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
13namespace DB
14{
15
16namespace ErrorCodes
17{
18 extern const int TOO_DEEP_RECURSION;
19 extern const int DIRECTORY_ALREADY_EXISTS;
20}
21
22
23static 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
54void 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