Skip to content

Commit

Permalink
Implement migrate-long-name
Browse files Browse the repository at this point in the history
  • Loading branch information
netheril96 committed Apr 6, 2024
1 parent 0c15430 commit 3f8683b
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 40 deletions.
91 changes: 83 additions & 8 deletions sources/commands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -818,7 +818,7 @@ class MountCommand : public CommandBase
ERROR_LOG(
"Config file %s does not exist. Perhaps you forget to run `create` command "
"first?",
single_pass_holder_.get_real_config_path_for_reading().c_str());
single_pass_holder_.get_real_config_path_for_reading());
return 19;
}
throw;
Expand Down Expand Up @@ -1034,13 +1034,10 @@ class InfoCommand : public CommandBase
int execute() override
{
auto real_config_path = single_pass_holder_.get_real_config_path_for_reading();
auto params
= decrypt(OSService::get_default()
.open_file_stream(
single_pass_holder_.get_real_config_path_for_reading(), O_RDONLY, 0)
->as_string(),
{single_pass_holder_.password.data(), single_pass_holder_.password.size()},
maybe_open_key_stream(single_pass_holder_.keyfile.getValue()).get());
auto params = decrypt(
OSService::get_default().open_file_stream(real_config_path, O_RDONLY, 0)->as_string(),
{single_pass_holder_.password.data(), single_pass_holder_.password.size()},
maybe_open_key_stream(single_pass_holder_.keyfile.getValue()).get());
if (!unmask.getValue())
{
if (params.has_lite_format_params())
Expand Down Expand Up @@ -1070,6 +1067,83 @@ class InfoCommand : public CommandBase
}
};

class MigrateLongNameCommand : public CommandBase
{
private:
SinglePasswordHolder single_pass_holder_{cmdline()};
Argon2idArgsHolder argon2{cmdline()};

static constexpr size_t kDefaultLongNameThreshold = 128;

public:
const char* long_name() const noexcept override { return "migrate-long-name"; }
char short_name() const noexcept override { return 0; }
const char* help_message() const noexcept override
{
return "Migrate a lite format repository without long name support.";
}
void parse_cmdline(int argc, const char* const* argv) override
{
CommandBase::parse_cmdline(argc, argv);
single_pass_holder_.get_password(false);
}

int execute() override
{
auto real_config_path = single_pass_holder_.get_real_config_path_for_reading();
auto params = decrypt(
OSService::get_default().open_file_stream(real_config_path, O_RDONLY, 0)->as_string(),
{single_pass_holder_.password.data(), single_pass_holder_.password.size()},
maybe_open_key_stream(single_pass_holder_.keyfile.getValue()).get());
if (!params.has_lite_format_params())
{
throw_runtime_error("This command is only available for lite format repositories.");
}
if (params.lite_format_params().long_name_threshold() > 0)
{
WARN_LOG("Already supports long name.");
return 0;
}
size_t max_filename_length = 0;
OSService::get_default().recursive_traverse(
single_pass_holder_.data_dir.getValue(),
[&](const std::string& dir, const std::string& name, int type)
{
if (type == S_IFLNK)
{
throw_runtime_error("Cannot migrate when symbolic links are present.");
}
max_filename_length = std::max(max_filename_length, name.size());
});
size_t threshold;
if (max_filename_length < (kDefaultLongNameThreshold + 16) * 8 / 5)
{
threshold = kDefaultLongNameThreshold;
}
else
{
threshold = max_filename_length * 5 / 8 - 16;
}
params.mutable_lite_format_params()->set_long_name_threshold(threshold);
auto encrypted_data
= encrypt(params,
argon2.to_params(),
{single_pass_holder_.password.data(), single_pass_holder_.password.size()},
maybe_open_key_stream(single_pass_holder_.keyfile.getValue()).get())
.SerializeAsString();
auto tmp_path = absl::StrCat(real_config_path, ".tmp");
auto stream = OSService::get_default().open_file_stream(
tmp_path, O_WRONLY | O_CREAT | O_EXCL, 0644);
DEFER(if (has_uncaught_exceptions()) {
OSService::get_default().remove_file_nothrow(tmp_path);
});
stream->write(encrypted_data.data(), 0, encrypted_data.size());
stream.reset();
OSService::get_default().rename(tmp_path, real_config_path);
return 0;
}
};

class DocCommand : public CommandBase
{
private:
Expand Down Expand Up @@ -1212,6 +1286,7 @@ int commands_main(int argc, const char* const* argv)
make_unique<ChangePasswordCommand>(),
make_unique<VersionCommand>(),
make_unique<InfoCommand>(),
make_unique<MigrateLongNameCommand>(),
make_unique<DocCommand>()};

const char* const program_name = argv[0];
Expand Down
5 changes: 1 addition & 4 deletions sources/common_platform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,11 @@ void OSService::recursive_traverse(const std::string& dir,
{
if (name == "." || name == "..")
continue;
callback(dir, name, S_IFMT & st.st_mode);
if ((S_IFMT & st.st_mode) == S_IFDIR)
{
recursive_traverse(StrCat(dir, "/", name), callback);
}
else
{
callback(dir, name);
}
}
}

Expand Down
56 changes: 29 additions & 27 deletions sources/myutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,38 +19,40 @@ static void find_ids_helper(const std::string& current_dir,
{
id_type id;
std::string hex(id_type::size() * 2, 0);
OSService::recursive_traverse_callback callback
= [&id, &result, &hex](std::string_view dir, std::string_view name) -> bool
auto callback = [&id, &result, &hex](std::string_view dir, std::string_view name, int type)
{
if (name == "." || name == "..")
return true;
if (absl::EndsWithIgnoreCase(name, ".meta"))
if (type != S_IFREG)
{
std::string total_name
= absl::StrCat(dir, "/", name.substr(0, name.size() - strlen(".meta")));
hex.assign(hex.size(), 0);
ptrdiff_t i = hex.size() - 1, j = total_name.size() - 1;
while (i >= 0 && j >= 0)
return;
}
if (!absl::EndsWithIgnoreCase(name, ".meta"))
{
return;
}

std::string total_name
= absl::StrCat(dir, "/", name.substr(0, name.size() - strlen(".meta")));
hex.assign(hex.size(), 0);
ptrdiff_t i = hex.size() - 1, j = total_name.size() - 1;
while (i >= 0 && j >= 0)
{
char namechar = total_name[j];
if ((namechar >= '0' && namechar <= '9') || (namechar >= 'a' && namechar <= 'f'))
{
hex[i] = namechar;
--i;
}
else if (namechar != '/' && namechar != '\\')
{
char namechar = total_name[j];
if ((namechar >= '0' && namechar <= '9') || (namechar >= 'a' && namechar <= 'f'))
{
hex[i] = namechar;
--i;
}
else if (namechar != '/' && namechar != '\\')
{
throw_runtime_error(absl::StrFormat(
"File \"%s\" has extension .meta, but not a valid securefs "
"meta filename. Please cleanup the underlying storage first.",
total_name));
}
--j;
throw_runtime_error(
absl::StrFormat("File \"%s\" has extension .meta, but not a valid securefs "
"meta filename. Please cleanup the underlying storage first.",
total_name));
}
parse_hex(hex, id.data(), id.size());
result.insert(id);
--j;
}
return true;
parse_hex(hex, id.data(), id.size());
result.insert(id);
};

OSService::get_default().recursive_traverse(current_dir, callback);
Expand Down
4 changes: 3 additions & 1 deletion sources/platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "streams.h"

#include <absl/base/thread_annotations.h>
#include <absl/functional/function_ref.h>
#include <absl/synchronization/mutex.h>
#include <uni_algo/conv.h>

Expand Down Expand Up @@ -205,7 +206,8 @@ class OSService
ssize_t readlink(const std::string& path, char* output, size_t size) const;
void symlink(const std::string& source, const std::string& dest) const;

typedef std::function<void(const std::string&, const std::string&)> recursive_traverse_callback;
using recursive_traverse_callback
= absl::FunctionRef<void(const std::string&, const std::string&, int)>;
void recursive_traverse(const std::string& dir,
const recursive_traverse_callback& callback) const;

Expand Down

0 comments on commit 3f8683b

Please sign in to comment.