2019-05-31 04:03:46 +00:00
|
|
|
#include "localBackup.h"
|
|
|
|
|
2018-08-28 13:48:16 +00:00
|
|
|
#include <Common/Exception.h>
|
|
|
|
#include <string>
|
2020-03-19 16:37:55 +00:00
|
|
|
#include <cerrno>
|
2016-01-11 21:46:36 +00:00
|
|
|
|
|
|
|
namespace DB
|
|
|
|
{
|
2018-08-28 13:48:16 +00:00
|
|
|
|
2016-01-11 21:46:36 +00:00
|
|
|
namespace ErrorCodes
|
|
|
|
{
|
|
|
|
extern const int TOO_DEEP_RECURSION;
|
2016-06-28 20:50:37 +00:00
|
|
|
extern const int DIRECTORY_ALREADY_EXISTS;
|
2016-01-11 21:46:36 +00:00
|
|
|
}
|
2014-11-11 04:11:07 +00:00
|
|
|
|
2022-04-21 12:39:12 +00:00
|
|
|
namespace
|
|
|
|
{
|
2014-11-11 04:11:07 +00:00
|
|
|
|
2022-04-21 12:39:12 +00:00
|
|
|
void localBackupImpl(
|
|
|
|
const DiskPtr & disk, const String & source_path,
|
|
|
|
const String & destination_path, bool make_source_readonly, size_t level,
|
|
|
|
std::optional<size_t> max_level)
|
2014-11-11 04:11:07 +00:00
|
|
|
{
|
2018-06-07 04:18:22 +00:00
|
|
|
if (max_level && level > *max_level)
|
2018-05-21 13:49:54 +00:00
|
|
|
return;
|
|
|
|
|
2014-11-11 04:11:07 +00:00
|
|
|
if (level >= 1000)
|
2016-01-11 21:46:36 +00:00
|
|
|
throw DB::Exception("Too deep recursion", DB::ErrorCodes::TOO_DEEP_RECURSION);
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2020-03-19 16:37:55 +00:00
|
|
|
disk->createDirectories(destination_path);
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2020-03-19 16:37:55 +00:00
|
|
|
for (auto it = disk->iterateDirectory(source_path); it->isValid(); it->next())
|
2014-11-11 04:11:07 +00:00
|
|
|
{
|
2020-03-19 16:37:55 +00:00
|
|
|
auto source = it->path();
|
2021-05-08 10:59:55 +00:00
|
|
|
auto destination = fs::path(destination_path) / it->name();
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2020-03-19 16:37:55 +00:00
|
|
|
if (!disk->isDirectory(source))
|
2014-11-11 04:11:07 +00:00
|
|
|
{
|
2022-02-17 21:26:37 +00:00
|
|
|
if (make_source_readonly)
|
|
|
|
disk->setReadOnly(source);
|
2020-03-19 16:37:55 +00:00
|
|
|
disk->createHardLink(source, destination);
|
2014-11-11 04:11:07 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-02-17 21:26:37 +00:00
|
|
|
localBackupImpl(disk, source, destination, make_source_readonly, level + 1, max_level);
|
2014-11-11 04:11:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-22 18:20:31 +00:00
|
|
|
class CleanupOnFail
|
|
|
|
{
|
|
|
|
public:
|
2022-02-01 11:07:58 +00:00
|
|
|
explicit CleanupOnFail(std::function<void()> && cleaner_)
|
|
|
|
: cleaner(cleaner_)
|
|
|
|
{}
|
2021-10-22 18:20:31 +00:00
|
|
|
|
|
|
|
~CleanupOnFail()
|
|
|
|
{
|
|
|
|
if (!is_success)
|
2022-02-01 11:07:58 +00:00
|
|
|
{
|
|
|
|
/// We are trying to handle race condition here. So if we was not
|
|
|
|
/// able to backup directory try to remove garbage, but it's ok if
|
|
|
|
/// it doesn't exist.
|
|
|
|
try
|
|
|
|
{
|
|
|
|
cleaner();
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
tryLogCurrentException(__PRETTY_FUNCTION__);
|
|
|
|
}
|
|
|
|
}
|
2021-10-22 18:20:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void success()
|
|
|
|
{
|
|
|
|
is_success = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::function<void()> cleaner;
|
2022-02-01 11:07:58 +00:00
|
|
|
bool is_success{false};
|
2021-10-22 18:20:31 +00:00
|
|
|
};
|
2022-02-01 11:07:58 +00:00
|
|
|
}
|
2021-10-22 18:20:31 +00:00
|
|
|
|
2022-04-21 12:39:12 +00:00
|
|
|
void localBackup(
|
|
|
|
const DiskPtr & disk, const String & source_path,
|
|
|
|
const String & destination_path, bool make_source_readonly,
|
|
|
|
std::optional<size_t> max_level, bool copy_instead_of_hardlinks)
|
2014-11-11 04:11:07 +00:00
|
|
|
{
|
2020-03-19 16:37:55 +00:00
|
|
|
if (disk->exists(destination_path) && !disk->isDirectoryEmpty(destination_path))
|
2016-06-28 20:50:37 +00:00
|
|
|
{
|
2020-03-19 16:37:55 +00:00
|
|
|
throw DB::Exception("Directory " + fullPath(disk, destination_path) + " already exists and is not empty.", DB::ErrorCodes::DIRECTORY_ALREADY_EXISTS);
|
2016-06-28 20:50:37 +00:00
|
|
|
}
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2014-11-17 00:41:40 +00:00
|
|
|
size_t try_no = 0;
|
|
|
|
const size_t max_tries = 10;
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2022-02-01 11:07:58 +00:00
|
|
|
CleanupOnFail cleanup([disk, destination_path]() { disk->removeRecursive(destination_path); });
|
2021-10-22 18:20:31 +00:00
|
|
|
|
2017-03-25 20:12:56 +00:00
|
|
|
/** Files in the directory can be permanently added and deleted.
|
2017-03-26 01:28:07 +00:00
|
|
|
* If some file is deleted during an attempt to make a backup, then try again,
|
2022-02-01 11:07:58 +00:00
|
|
|
* because it's important to take into account any new files that might appear.
|
2014-11-17 00:41:40 +00:00
|
|
|
*/
|
2014-12-21 01:20:07 +00:00
|
|
|
while (true)
|
2014-11-17 00:41:40 +00:00
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2022-04-21 12:39:12 +00:00
|
|
|
if (copy_instead_of_hardlinks)
|
|
|
|
disk->copyDirectoryContent(source_path, disk, destination_path);
|
|
|
|
else
|
|
|
|
localBackupImpl(disk, source_path, destination_path, make_source_readonly, 0, max_level);
|
2014-11-17 00:41:40 +00:00
|
|
|
}
|
|
|
|
catch (const DB::ErrnoException & e)
|
|
|
|
{
|
|
|
|
if (e.getErrno() != ENOENT)
|
|
|
|
throw;
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2014-11-17 00:41:40 +00:00
|
|
|
++try_no;
|
|
|
|
if (try_no == max_tries)
|
|
|
|
throw;
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2014-11-17 00:41:40 +00:00
|
|
|
continue;
|
|
|
|
}
|
2021-05-07 21:53:44 +00:00
|
|
|
catch (const fs::filesystem_error & e)
|
2014-11-17 00:41:40 +00:00
|
|
|
{
|
2021-05-07 21:53:44 +00:00
|
|
|
if (e.code() == std::errc::no_such_file_or_directory)
|
|
|
|
{
|
|
|
|
++try_no;
|
|
|
|
if (try_no == max_tries)
|
|
|
|
throw;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
throw;
|
2014-11-17 00:41:40 +00:00
|
|
|
}
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2014-12-21 01:20:07 +00:00
|
|
|
break;
|
|
|
|
}
|
2021-10-22 18:20:31 +00:00
|
|
|
|
|
|
|
cleanup.success();
|
2014-11-11 04:11:07 +00:00
|
|
|
}
|
2018-08-28 13:48:16 +00:00
|
|
|
|
|
|
|
}
|