1 | #include "localBackup.h" |
2 | |
3 | #include <Common/createHardLink.h> |
4 | #include <Common/Exception.h> |
5 | #include <Poco/DirectoryIterator.h> |
6 | #include <Poco/Path.h> |
7 | #include <Poco/File.h> |
8 | #include <string> |
9 | #include <iostream> |
10 | #include <errno.h> |
11 | |
12 | |
13 | namespace DB |
14 | { |
15 | |
16 | namespace ErrorCodes |
17 | { |
18 | extern const int TOO_DEEP_RECURSION; |
19 | extern const int DIRECTORY_ALREADY_EXISTS; |
20 | } |
21 | |
22 | |
23 | static void localBackupImpl(const Poco::Path & source_path, const Poco::Path & destination_path, size_t level, |
24 | std::optional<size_t> max_level) |
25 | { |
26 | if (max_level && level > *max_level) |
27 | return; |
28 | |
29 | if (level >= 1000) |
30 | throw DB::Exception("Too deep recursion" , DB::ErrorCodes::TOO_DEEP_RECURSION); |
31 | |
32 | Poco::File(destination_path).createDirectories(); |
33 | |
34 | Poco::DirectoryIterator dir_end; |
35 | for (Poco::DirectoryIterator dir_it(source_path); dir_it != dir_end; ++dir_it) |
36 | { |
37 | Poco::Path source = dir_it.path(); |
38 | Poco::Path destination = destination_path; |
39 | destination.append(dir_it.name()); |
40 | |
41 | if (!dir_it->isDirectory()) |
42 | { |
43 | dir_it->setReadOnly(); |
44 | |
45 | createHardLink(source.toString(), destination.toString()); |
46 | } |
47 | else |
48 | { |
49 | localBackupImpl(source, destination, level + 1, max_level); |
50 | } |
51 | } |
52 | } |
53 | |
54 | void localBackup(const Poco::Path & source_path, const Poco::Path & destination_path, std::optional<size_t> max_level) |
55 | { |
56 | if (Poco::File(destination_path).exists() |
57 | && Poco::DirectoryIterator(destination_path) != Poco::DirectoryIterator()) |
58 | { |
59 | throw DB::Exception("Directory " + destination_path.toString() + " already exists and is not empty." , DB::ErrorCodes::DIRECTORY_ALREADY_EXISTS); |
60 | } |
61 | |
62 | size_t try_no = 0; |
63 | const size_t max_tries = 10; |
64 | |
65 | /** Files in the directory can be permanently added and deleted. |
66 | * If some file is deleted during an attempt to make a backup, then try again, |
67 | * because it's important to take into account any new files that might appear. |
68 | */ |
69 | while (true) |
70 | { |
71 | try |
72 | { |
73 | localBackupImpl(source_path, destination_path, 0, max_level); |
74 | } |
75 | catch (const DB::ErrnoException & e) |
76 | { |
77 | if (e.getErrno() != ENOENT) |
78 | throw; |
79 | |
80 | ++try_no; |
81 | if (try_no == max_tries) |
82 | throw; |
83 | |
84 | continue; |
85 | } |
86 | catch (const Poco::FileNotFoundException &) |
87 | { |
88 | ++try_no; |
89 | if (try_no == max_tries) |
90 | throw; |
91 | |
92 | continue; |
93 | } |
94 | |
95 | break; |
96 | } |
97 | } |
98 | |
99 | } |
100 | |