From a54f9284556b612796b7d7d47954feadbf3ecc89 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 15 Sep 2025 12:43:58 -0500 Subject: [PATCH 001/136] win32 fixes/refactor ui main --- repertory/repertory/include/ui/ui_main.hpp | 29 ++ repertory/repertory/main.cpp | 82 +----- .../repertory/src/ui/mgmt_app_config.cpp | 2 +- repertory/repertory/src/ui/ui_main.cpp | 259 ++++++++++++++++++ repertory/repertory/src/ui/ui_server.cpp | 3 +- 5 files changed, 293 insertions(+), 82 deletions(-) create mode 100644 repertory/repertory/include/ui/ui_main.hpp create mode 100644 repertory/repertory/src/ui/ui_main.cpp diff --git a/repertory/repertory/include/ui/ui_main.hpp b/repertory/repertory/include/ui/ui_main.hpp new file mode 100644 index 00000000..2ad0605f --- /dev/null +++ b/repertory/repertory/include/ui/ui_main.hpp @@ -0,0 +1,29 @@ +/* + Copyright <2018-2025> + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ +#ifndef REPERTORY_INCLUDE_UI_MAIN_HPP_ +#define REPERTORY_INCLUDE_UI_MAIN_HPP_ + +namespace repertory::ui { +[[nodiscard]] auto ui_main(const std::vector &args) -> int +} // namespace repertory::ui + +#endif // REPERTORY_INCLUDE_UI_MAIN_HPP_ diff --git a/repertory/repertory/main.cpp b/repertory/repertory/main.cpp index c113065c..59337fe9 100644 --- a/repertory/repertory/main.cpp +++ b/repertory/repertory/main.cpp @@ -26,10 +26,8 @@ #include "cli/actions.hpp" #include "initialize.hpp" #include "types/repertory.hpp" -#include "ui/mgmt_app_config.hpp" -#include "ui/ui_server.hpp" +#include "ui/ui_main.hpp" #include "utils/cli_utils.hpp" -#include "utils/error_utils.hpp" #include "utils/polling.hpp" using namespace repertory; @@ -66,83 +64,7 @@ auto main(int argc, char **argv) -> int { utils::cli::options::version_option)) { cli::actions::version(args); } else if (utils::cli::has_option(args, utils::cli::options::ui_option)) { - ui::mgmt_app_config config{ - utils::cli::has_option(args, utils::cli::options::hidden_option), - utils::cli::has_option(args, utils::cli::options::launch_only_option), - }; - - std::string data; - auto res = utils::cli::parse_string_option( - args, utils::cli::options::ui_port_option, data); - if (res == exit_code::success && not data.empty()) { - config.set_api_port(utils::string::to_uint16(data)); - } - - if (not utils::file::change_to_process_directory()) { - ret = static_cast(exit_code::ui_failed); - } else { - const auto run_ui = [](auto *server) { - REPERTORY_USES_FUNCTION_NAME(); - - static std::atomic active_server{server}; - static const auto quit_handler = [](int /* sig */) { - REPERTORY_USES_FUNCTION_NAME(); - - auto *ptr = active_server.exchange(nullptr); - if (ptr == nullptr) { - return; - } - - try { - ptr->stop(); - } catch (const std::exception &ex) { - utils::error::raise_error(function_name, ex, "failed to stop ui"); - } - }; - - std::signal(SIGINT, quit_handler); -#if !defined(_WIN32) - std::signal(SIGQUIT, quit_handler); -#endif // !defined(_WIN32) - std::signal(SIGTERM, quit_handler); - - try { - server->start(); - } catch (const std::exception &ex) { - utils::error::raise_error(function_name, ex, "failed to start ui"); - } - - try { - server->stop(); - } catch (const std::exception &ex) { - utils::error::raise_error(function_name, ex, "failed to stop ui"); - } - - repertory::project_cleanup(); - }; - -#if defined(_WIN32) - ui::server server(&config); - run_ui(&server); -#else // !defined(_WIN32) - repertory::project_cleanup(); - - ret = utils::create_daemon([&]() -> int { - if (not repertory::project_initialize()) { - repertory::project_cleanup(); - return -1; - } - - if (not utils::file::change_to_process_directory()) { - return -1; - } - - ui::ui_server server(&config); - run_ui(&server); - return 0; - }); -#endif // defined(_WIN32) - } + ret = ui::ui_main(args); } else { auto prov = utils::cli::get_provider_type_from_args(args); diff --git a/repertory/repertory/src/ui/mgmt_app_config.cpp b/repertory/repertory/src/ui/mgmt_app_config.cpp index a1facdac..5a4f4f58 100644 --- a/repertory/repertory/src/ui/mgmt_app_config.cpp +++ b/repertory/repertory/src/ui/mgmt_app_config.cpp @@ -299,7 +299,7 @@ void mgmt_app_config::set_auto_start(bool auto_start) { function_name, utils::get_last_error_code(), "failed to create auto-start entry|name|repertory"); } - } else if (utils::remove_shortcut(REPERTORY_W)) { + } else if (utils::remove_shortcut(std::wstring{REPERTORY_W})) { utils::error::handle_info(function_name, "removed auto-start entry|name|repertory"); } else { diff --git a/repertory/repertory/src/ui/ui_main.cpp b/repertory/repertory/src/ui/ui_main.cpp new file mode 100644 index 00000000..f5dea6f8 --- /dev/null +++ b/repertory/repertory/src/ui/ui_main.cpp @@ -0,0 +1,259 @@ +/* + Copyright <2018-2025> + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ +#include "ui/ui_main.hpp" + +#include "initialize.hpp" +#include "types/repertory.hpp" +#include "ui/mgmt_app_config.hpp" +#include "ui/ui_server.hpp" +#include "utils/cli_utils.hpp" +#include "utils/error_utils.hpp" + +namespace repertory::ui { +[[nodiscard]] auto ui_main(const std::vector &args) -> int { + mgmt_app_config config{ + utils::cli::has_option(args, utils::cli::options::hidden_option), + utils::cli::has_option(args, utils::cli::options::launch_only_option), + }; + + std::string data; + auto res = utils::cli::parse_string_option( + args, utils::cli::options::ui_port_option, data); + if (res == exit_code::success && not data.empty()) { + config.set_api_port(utils::string::to_uint16(data)); + } + + if (not utils::file::change_to_process_directory()) { + return static_cast(exit_code::ui_failed); + } + + const auto run_ui = [](auto *server) { + REPERTORY_USES_FUNCTION_NAME(); + + static std::atomic active_server{server}; + static const auto quit_handler = [](int /* sig */) { + REPERTORY_USES_FUNCTION_NAME(); + + auto *ptr = active_server.exchange(nullptr); + if (ptr == nullptr) { + return; + } + + try { + ptr->stop(); + } catch (const std::exception &ex) { + utils::error::raise_error(function_name, ex, "failed to stop ui"); + } + }; + + std::signal(SIGINT, quit_handler); +#if !defined(_WIN32) + std::signal(SIGQUIT, quit_handler); +#endif // !defined(_WIN32) + std::signal(SIGTERM, quit_handler); + + try { + server->start(); + } catch (const std::exception &ex) { + utils::error::raise_error(function_name, ex, "failed to start ui"); + } + + try { + server->stop(); + } catch (const std::exception &ex) { + utils::error::raise_error(function_name, ex, "failed to stop ui"); + } + + repertory::project_cleanup(); + }; + +#if defined(_WIN32) + server server(&config); + run_ui(&server); +#else // !defined(_WIN32) + repertory::project_cleanup(); + + return utils::create_daemon([&]() -> int { + if (not repertory::project_initialize()) { + repertory::project_cleanup(); + return -1; + } + + if (not utils::file::change_to_process_directory()) { + return -1; + } + + ui_server server(&config); + run_ui(&server); + return 0; + }); +#endif // defined(_WIN32) +} +} // namespace repertory + +using namespace repertory; + +auto main(int argc, char **argv) -> int { +#if defined(PROJECT_ENABLE_BACKWARD_CPP) + static backward::SignalHandling sh; +#endif // defined(PROJECT_ENABLE_BACKWARD_CPP) + + if (not repertory::project_initialize()) { + repertory::project_cleanup(); + return -1; + } + + std::vector args; + { + auto args_span = std::span(argv, static_cast(argc)); + std::ranges::copy(args_span, std::back_inserter(args)); + } + + int ret{0}; + + if (argc == 1) { +#if defined(__APPLE__) + args.push_back("-ui"); +#else // !defined(__APPLE__) + args.push_back("-h"); +#endif // defined(__APPLE__) + } + + if (utils::cli::has_option(args, utils::cli::options::help_option)) { + cli::actions::help(args); + } else if (utils::cli::has_option(args, + utils::cli::options::version_option)) { + cli::actions::version(args); + } else if (utils::cli::has_option(args, utils::cli::options::ui_option)) { + ret = ui_main(args); + } else { + auto prov = utils::cli::get_provider_type_from_args(args); + + std::string data_directory; + auto res = utils::cli::parse_string_option( + args, utils::cli::options::data_directory_option, data_directory); + + std::string password; + res = (res == exit_code::success) + ? utils::cli::parse_string_option( + args, utils::cli::options::password_option, password) + : res; + + std::string user; + res = (res == exit_code::success) + ? utils::cli::parse_string_option( + args, utils::cli::options::user_option, user) + : res; + + std::string remote_host; + std::uint16_t remote_port{}; + std::string unique_id; + if (res == exit_code::success) { + if (prov == provider_type::remote) { + std::string data; + res = utils::cli::parse_string_option( + args, utils::cli::options::remote_mount_option, data); + if (res == exit_code::success) { + const auto parts = utils::string::split(data, ':', false); + if (parts.size() != 2) { + std::cerr << "Invalid syntax for host/port '-rm " + "host:port,--remote_mount host:port'" + << std::endl; + res = exit_code::invalid_syntax; + } else { + unique_id = parts.at(0U) + ':' + parts.at(1U); + remote_host = parts.at(0U); + try { + remote_port = utils::string::to_uint16(parts.at(1U)); + data_directory = + data_directory.empty() + ? utils::path::combine( + app_config::default_data_directory(prov), + { + utils::string::replace_copy(unique_id, ':', + '_'), + }) + : utils::path::absolute(data_directory); + } catch (const std::exception &e) { + std::cerr << (e.what() == nullptr ? "Unable to parse port" + : e.what()) + << std::endl; + res = exit_code::invalid_syntax; + } + } + } + } else { + std::string data; + res = utils::cli::parse_string_option( + args, utils::cli::options::name_option, data); + if (res == exit_code::success) { + unique_id = utils::string::trim(data); + if (unique_id.empty()) { + std::cerr << "Configuration name for '" + << app_config::get_provider_display_name(prov) + << "' was not provided" << std::endl; + res = exit_code::invalid_syntax; + } + } + + if (res == exit_code::success) { + data_directory = + data_directory.empty() + ? utils::path::combine( + app_config::default_data_directory(prov), {unique_id}) + : utils::path::absolute(data_directory); + } + } + } + + int mount_result{}; + if (res == exit_code::success) { + res = exit_code::option_not_found; + for (std::size_t idx = 0U; + (res == exit_code::option_not_found) && + (idx < utils::cli::options::option_list.size()); + idx++) { + try { + res = cli::actions::perform_action( + utils::cli::options::option_list[idx], args, data_directory, prov, + unique_id, user, password); + } catch (const std::exception &ex) { + res = exit_code::exception; + } catch (...) { + res = exit_code::exception; + } + } + + if (res == exit_code::option_not_found) { + res = cli::actions::mount(args, data_directory, mount_result, prov, + remote_host, remote_port, unique_id); + } + } + + ret = ((res == exit_code::mount_result) ? mount_result + : static_cast(res)); + } + + repertory::project_cleanup(); + return ret; +} +} diff --git a/repertory/repertory/src/ui/ui_server.cpp b/repertory/repertory/src/ui/ui_server.cpp index 1ca6cf7f..bcf54dba 100644 --- a/repertory/repertory/src/ui/ui_server.cpp +++ b/repertory/repertory/src/ui/ui_server.cpp @@ -123,7 +123,8 @@ ui_server::ui_server(mgmt_app_config *config) "logs", })), #if defined(_WIN32) - repertory_binary_(utils::path::combine(".", {REPERTORY ".exe"})) + repertory_binary_( + utils::path::combine(".", {std::string{REPERTORY} + ".exe"})) #else // !defined(_WIN32) repertory_binary_(utils::path::combine(".", {REPERTORY})) #endif // defined(_WIN32) From c82ec168a18f6aeacfb855d1c1f2bf118d64ab3f Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 15 Sep 2025 12:48:41 -0500 Subject: [PATCH 002/136] ui main fixes --- repertory/repertory/include/ui/ui_main.hpp | 2 +- repertory/repertory/src/ui/ui_main.cpp | 154 +-------------------- 2 files changed, 6 insertions(+), 150 deletions(-) diff --git a/repertory/repertory/include/ui/ui_main.hpp b/repertory/repertory/include/ui/ui_main.hpp index 2ad0605f..efc68203 100644 --- a/repertory/repertory/include/ui/ui_main.hpp +++ b/repertory/repertory/include/ui/ui_main.hpp @@ -23,7 +23,7 @@ #define REPERTORY_INCLUDE_UI_MAIN_HPP_ namespace repertory::ui { -[[nodiscard]] auto ui_main(const std::vector &args) -> int +[[nodiscard]] auto ui_main(const std::vector &args) -> int; } // namespace repertory::ui #endif // REPERTORY_INCLUDE_UI_MAIN_HPP_ diff --git a/repertory/repertory/src/ui/ui_main.cpp b/repertory/repertory/src/ui/ui_main.cpp index f5dea6f8..ac463064 100644 --- a/repertory/repertory/src/ui/ui_main.cpp +++ b/repertory/repertory/src/ui/ui_main.cpp @@ -21,12 +21,15 @@ */ #include "ui/ui_main.hpp" +#include "cli/actions.hpp" #include "initialize.hpp" #include "types/repertory.hpp" #include "ui/mgmt_app_config.hpp" #include "ui/ui_server.hpp" #include "utils/cli_utils.hpp" #include "utils/error_utils.hpp" +#include "utils/file.hpp" +#include "utils/path.hpp" namespace repertory::ui { [[nodiscard]] auto ui_main(const std::vector &args) -> int { @@ -89,6 +92,7 @@ namespace repertory::ui { #if defined(_WIN32) server server(&config); run_ui(&server); + return 0; #else // !defined(_WIN32) repertory::project_cleanup(); @@ -108,152 +112,4 @@ namespace repertory::ui { }); #endif // defined(_WIN32) } -} // namespace repertory - -using namespace repertory; - -auto main(int argc, char **argv) -> int { -#if defined(PROJECT_ENABLE_BACKWARD_CPP) - static backward::SignalHandling sh; -#endif // defined(PROJECT_ENABLE_BACKWARD_CPP) - - if (not repertory::project_initialize()) { - repertory::project_cleanup(); - return -1; - } - - std::vector args; - { - auto args_span = std::span(argv, static_cast(argc)); - std::ranges::copy(args_span, std::back_inserter(args)); - } - - int ret{0}; - - if (argc == 1) { -#if defined(__APPLE__) - args.push_back("-ui"); -#else // !defined(__APPLE__) - args.push_back("-h"); -#endif // defined(__APPLE__) - } - - if (utils::cli::has_option(args, utils::cli::options::help_option)) { - cli::actions::help(args); - } else if (utils::cli::has_option(args, - utils::cli::options::version_option)) { - cli::actions::version(args); - } else if (utils::cli::has_option(args, utils::cli::options::ui_option)) { - ret = ui_main(args); - } else { - auto prov = utils::cli::get_provider_type_from_args(args); - - std::string data_directory; - auto res = utils::cli::parse_string_option( - args, utils::cli::options::data_directory_option, data_directory); - - std::string password; - res = (res == exit_code::success) - ? utils::cli::parse_string_option( - args, utils::cli::options::password_option, password) - : res; - - std::string user; - res = (res == exit_code::success) - ? utils::cli::parse_string_option( - args, utils::cli::options::user_option, user) - : res; - - std::string remote_host; - std::uint16_t remote_port{}; - std::string unique_id; - if (res == exit_code::success) { - if (prov == provider_type::remote) { - std::string data; - res = utils::cli::parse_string_option( - args, utils::cli::options::remote_mount_option, data); - if (res == exit_code::success) { - const auto parts = utils::string::split(data, ':', false); - if (parts.size() != 2) { - std::cerr << "Invalid syntax for host/port '-rm " - "host:port,--remote_mount host:port'" - << std::endl; - res = exit_code::invalid_syntax; - } else { - unique_id = parts.at(0U) + ':' + parts.at(1U); - remote_host = parts.at(0U); - try { - remote_port = utils::string::to_uint16(parts.at(1U)); - data_directory = - data_directory.empty() - ? utils::path::combine( - app_config::default_data_directory(prov), - { - utils::string::replace_copy(unique_id, ':', - '_'), - }) - : utils::path::absolute(data_directory); - } catch (const std::exception &e) { - std::cerr << (e.what() == nullptr ? "Unable to parse port" - : e.what()) - << std::endl; - res = exit_code::invalid_syntax; - } - } - } - } else { - std::string data; - res = utils::cli::parse_string_option( - args, utils::cli::options::name_option, data); - if (res == exit_code::success) { - unique_id = utils::string::trim(data); - if (unique_id.empty()) { - std::cerr << "Configuration name for '" - << app_config::get_provider_display_name(prov) - << "' was not provided" << std::endl; - res = exit_code::invalid_syntax; - } - } - - if (res == exit_code::success) { - data_directory = - data_directory.empty() - ? utils::path::combine( - app_config::default_data_directory(prov), {unique_id}) - : utils::path::absolute(data_directory); - } - } - } - - int mount_result{}; - if (res == exit_code::success) { - res = exit_code::option_not_found; - for (std::size_t idx = 0U; - (res == exit_code::option_not_found) && - (idx < utils::cli::options::option_list.size()); - idx++) { - try { - res = cli::actions::perform_action( - utils::cli::options::option_list[idx], args, data_directory, prov, - unique_id, user, password); - } catch (const std::exception &ex) { - res = exit_code::exception; - } catch (...) { - res = exit_code::exception; - } - } - - if (res == exit_code::option_not_found) { - res = cli::actions::mount(args, data_directory, mount_result, prov, - remote_host, remote_port, unique_id); - } - } - - ret = ((res == exit_code::mount_result) ? mount_result - : static_cast(res)); - } - - repertory::project_cleanup(); - return ret; -} -} +} // namespace repertory::ui From f2f28cea598650221ea4481087b7a8a5799f1143 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 15 Sep 2025 12:52:02 -0500 Subject: [PATCH 003/136] fix server --- repertory/repertory/src/ui/ui_main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repertory/repertory/src/ui/ui_main.cpp b/repertory/repertory/src/ui/ui_main.cpp index ac463064..8a3c88c3 100644 --- a/repertory/repertory/src/ui/ui_main.cpp +++ b/repertory/repertory/src/ui/ui_main.cpp @@ -90,7 +90,7 @@ namespace repertory::ui { }; #if defined(_WIN32) - server server(&config); + ui_server server(&config); run_ui(&server); return 0; #else // !defined(_WIN32) From 6b2bf3ab674c01b9968806fc5170c6b7e720028e Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 15 Sep 2025 12:58:46 -0500 Subject: [PATCH 004/136] refactor --- repertory/repertory/src/ui/ui_main.cpp | 27 +++++++++++--------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/repertory/repertory/src/ui/ui_main.cpp b/repertory/repertory/src/ui/ui_main.cpp index 8a3c88c3..92301fd2 100644 --- a/repertory/repertory/src/ui/ui_main.cpp +++ b/repertory/repertory/src/ui/ui_main.cpp @@ -49,10 +49,12 @@ namespace repertory::ui { return static_cast(exit_code::ui_failed); } - const auto run_ui = [](auto *server) { + const auto run_ui = [&]() -> int { REPERTORY_USES_FUNCTION_NAME(); - static std::atomic active_server{server}; + ui_server server(&config); + + static std::atomic active_server{&server}; static const auto quit_handler = [](int /* sig */) { REPERTORY_USES_FUNCTION_NAME(); @@ -66,6 +68,8 @@ namespace repertory::ui { } catch (const std::exception &ex) { utils::error::raise_error(function_name, ex, "failed to stop ui"); } + + repertory::project_cleanup(); }; std::signal(SIGINT, quit_handler); @@ -75,24 +79,17 @@ namespace repertory::ui { std::signal(SIGTERM, quit_handler); try { - server->start(); + server.start(); } catch (const std::exception &ex) { utils::error::raise_error(function_name, ex, "failed to start ui"); } - try { - server->stop(); - } catch (const std::exception &ex) { - utils::error::raise_error(function_name, ex, "failed to stop ui"); - } - - repertory::project_cleanup(); + quit_handler(SIGTERM); + return 0; }; #if defined(_WIN32) - ui_server server(&config); - run_ui(&server); - return 0; + return run_ui(); #else // !defined(_WIN32) repertory::project_cleanup(); @@ -106,9 +103,7 @@ namespace repertory::ui { return -1; } - ui_server server(&config); - run_ui(&server); - return 0; + return run_ui(); }); #endif // defined(_WIN32) } From 290bdb5fda74b869a64bbfd1c5885ca9a64f2da8 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 15 Sep 2025 13:34:52 -0500 Subject: [PATCH 005/136] refactor ui server --- repertory/repertory/include/ui/ui_server.hpp | 3 +++ repertory/repertory/src/ui/ui_server.cpp | 26 +++++++++++++------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/repertory/repertory/include/ui/ui_server.hpp b/repertory/repertory/include/ui/ui_server.hpp index 0f11cd05..33967e64 100644 --- a/repertory/repertory/include/ui/ui_server.hpp +++ b/repertory/repertory/include/ui/ui_server.hpp @@ -24,12 +24,15 @@ #include "events/consumers/console_consumer.hpp" #include "events/consumers/logging_consumer.hpp" +#include "events/event_system.hpp" #include "utils/common.hpp" namespace repertory::ui { class mgmt_app_config; class ui_server final { + E_CONSUMER(); + private: static constexpr auto nonce_length{128U}; static constexpr auto nonce_timeout{15U}; diff --git a/repertory/repertory/src/ui/ui_server.cpp b/repertory/repertory/src/ui/ui_server.cpp index bcf54dba..1fea462c 100644 --- a/repertory/repertory/src/ui/ui_server.cpp +++ b/repertory/repertory/src/ui/ui_server.cpp @@ -129,6 +129,16 @@ ui_server::ui_server(mgmt_app_config *config) repertory_binary_(utils::path::combine(".", {REPERTORY})) #endif // defined(_WIN32) { + E_SUBSCRIBE(event_level_changed, [this](auto &&event) { + config_->set_event_level(event.new_level); + }); + +#if defined(_WIN32) + if (config_->get_hidden()) { + ::ShowWindow(::GetConsoleWindow(), SW_HIDE); + } +#endif // defined(_WIN32) + #if defined(__APPLE__) server_.set_mount_point("/ui", "../Resources/web"); #else // !defined(__APPLE__) @@ -137,11 +147,11 @@ ui_server::ui_server(mgmt_app_config *config) server_.set_socket_options([](auto &&sock) { #if defined(_WIN32) - BOOL enable = TRUE; + BOOL enable{TRUE}; ::setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, reinterpret_cast(&enable), sizeof(enable)); #else // !defined(_WIN32)! - int one = 1; + int one{1}; ::setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&one), sizeof(one)); #ifdef SO_REUSEPORT @@ -262,15 +272,13 @@ ui_server::ui_server(mgmt_app_config *config) server_.Put("/api/v1/settings", [this](auto &&req, auto &&res) { handle_put_settings(req, res); }); - -#if defined(_WIN32) - if (config_->get_hidden()) { - ::ShowWindow(::GetConsoleWindow(), SW_HIDE); - } -#endif // defined(_WIN32) } -ui_server::~ui_server() { stop(); } +ui_server::~ui_server() { + stop(); + + E_CONSUMER_RELEASE(); +} auto ui_server::data_directory_exists(provider_type prov, std::string_view name) const -> bool { From a43a778b462ab5ad7430934f0055c3bf4f13076d Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 15 Sep 2025 13:39:13 -0500 Subject: [PATCH 006/136] fix --- repertory/repertory/src/ui/ui_server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repertory/repertory/src/ui/ui_server.cpp b/repertory/repertory/src/ui/ui_server.cpp index 1fea462c..12cdd7e7 100644 --- a/repertory/repertory/src/ui/ui_server.cpp +++ b/repertory/repertory/src/ui/ui_server.cpp @@ -888,7 +888,7 @@ void ui_server::start() { lock_data ui_lock(provider_type::unknown, "ui"); auto res = ui_lock.grab_lock(1U); - if (res == lock_result::locked) { + if (res == lock_result::success) { auto deadline{std::chrono::steady_clock::now() + 30s}; std::string host{"127.0.0.1"}; auto desired_port{config_->get_api_port()}; From a241cd16ef13b02b9cc1d9bd49019496afcc9b8d Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 15 Sep 2025 13:40:44 -0500 Subject: [PATCH 007/136] fix --- repertory/repertory/src/ui/ui_server.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/repertory/repertory/src/ui/ui_server.cpp b/repertory/repertory/src/ui/ui_server.cpp index 12cdd7e7..f7318789 100644 --- a/repertory/repertory/src/ui/ui_server.cpp +++ b/repertory/repertory/src/ui/ui_server.cpp @@ -22,9 +22,7 @@ #include "ui/ui_server.hpp" #include "app_config.hpp" -#include "events/consumers/console_consumer.hpp" -#include "events/consumers/logging_consumer.hpp" -#include "events/event_system.hpp" +#include "events/types/event_level_changed.hpp" #include "platform/platform.hpp" #include "types/repertory.hpp" #include "ui/mgmt_app_config.hpp" From 94abee27f912ea21b78602f232b09261b13eb789 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 15 Sep 2025 13:55:51 -0500 Subject: [PATCH 008/136] fix --- repertory/repertory/src/ui/ui_server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repertory/repertory/src/ui/ui_server.cpp b/repertory/repertory/src/ui/ui_server.cpp index f7318789..2b918bb5 100644 --- a/repertory/repertory/src/ui/ui_server.cpp +++ b/repertory/repertory/src/ui/ui_server.cpp @@ -892,7 +892,7 @@ void ui_server::start() { auto desired_port{config_->get_api_port()}; auto success{false}; - while (not success && std::chrono::steady_clock::now() >= deadline) { + while (not success && std::chrono::steady_clock::now() < deadline) { success = server_.bind_to_port(host, desired_port); if (success) { break; From 20d6eee1f88ed5048288c403b4c3beccdc00843b Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 15 Sep 2025 14:27:48 -0500 Subject: [PATCH 009/136] fix launch --- repertory/repertory/src/ui/ui_server.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/repertory/repertory/src/ui/ui_server.cpp b/repertory/repertory/src/ui/ui_server.cpp index 2b918bb5..b3c34215 100644 --- a/repertory/repertory/src/ui/ui_server.cpp +++ b/repertory/repertory/src/ui/ui_server.cpp @@ -961,6 +961,9 @@ void ui_server::start() { } }).join(); + launch_ui(); + + notify_and_unlock(nonce_lock); server_.listen_after_bind(); } else { utils::error::raise_error(function_name, From 91cac73e40671d181553f335e8772f0164a8ed16 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 15 Sep 2025 14:28:35 -0500 Subject: [PATCH 010/136] refactor --- repertory/repertory/src/ui/ui_server.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/repertory/repertory/src/ui/ui_server.cpp b/repertory/repertory/src/ui/ui_server.cpp index b3c34215..a8d98bd1 100644 --- a/repertory/repertory/src/ui/ui_server.cpp +++ b/repertory/repertory/src/ui/ui_server.cpp @@ -961,9 +961,13 @@ void ui_server::start() { } }).join(); - launch_ui(); - notify_and_unlock(nonce_lock); + + if (should_launch) { + launch_ui(); + should_launch = false; + } + server_.listen_after_bind(); } else { utils::error::raise_error(function_name, From 3be1cec3934585aea73fb7ca0117a9f1833d0cdc Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 15 Sep 2025 14:29:44 -0500 Subject: [PATCH 011/136] fix --- repertory/repertory/src/ui/ui_server.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/repertory/repertory/src/ui/ui_server.cpp b/repertory/repertory/src/ui/ui_server.cpp index a8d98bd1..fc75e95d 100644 --- a/repertory/repertory/src/ui/ui_server.cpp +++ b/repertory/repertory/src/ui/ui_server.cpp @@ -969,6 +969,7 @@ void ui_server::start() { } server_.listen_after_bind(); + return; } else { utils::error::raise_error(function_name, utils::error::create_error_message({ From 643b5fbe7ea54df91cc530f1e8b2cd5dd3a21537 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 15 Sep 2025 14:30:30 -0500 Subject: [PATCH 012/136] fix unlock --- repertory/repertory/src/ui/ui_server.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/repertory/repertory/src/ui/ui_server.cpp b/repertory/repertory/src/ui/ui_server.cpp index fc75e95d..0b60483c 100644 --- a/repertory/repertory/src/ui/ui_server.cpp +++ b/repertory/repertory/src/ui/ui_server.cpp @@ -970,17 +970,17 @@ void ui_server::start() { server_.listen_after_bind(); return; - } else { - utils::error::raise_error(function_name, - utils::error::create_error_message({ - "bind timeout (port in use)", - "host", - host, - "port", - std::to_string(desired_port), - })); - should_launch = false; } + + utils::error::raise_error(function_name, + utils::error::create_error_message({ + "bind timeout (port in use)", + "host", + host, + "port", + std::to_string(desired_port), + })); + should_launch = false; } notify_and_unlock(nonce_lock); From 8f2968d6951b49ff5d4fe3c73d377b5db0893885 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 15 Sep 2025 14:38:30 -0500 Subject: [PATCH 013/136] refactor --- repertory/repertory/src/ui/ui_server.cpp | 167 +++++++++++------------ 1 file changed, 79 insertions(+), 88 deletions(-) diff --git a/repertory/repertory/src/ui/ui_server.cpp b/repertory/repertory/src/ui/ui_server.cpp index 0b60483c..34249fd1 100644 --- a/repertory/repertory/src/ui/ui_server.cpp +++ b/repertory/repertory/src/ui/ui_server.cpp @@ -882,96 +882,39 @@ void ui_server::start() { } }; - auto should_launch{true}; - lock_data ui_lock(provider_type::unknown, "ui"); auto res = ui_lock.grab_lock(1U); - if (res == lock_result::success) { - auto deadline{std::chrono::steady_clock::now() + 30s}; - std::string host{"127.0.0.1"}; - auto desired_port{config_->get_api_port()}; + if (res != lock_result::success) { + notify_and_unlock(nonce_lock); + launch_ui(); + return; + } - auto success{false}; - while (not success && std::chrono::steady_clock::now() < deadline) { - success = server_.bind_to_port(host, desired_port); - if (success) { - break; - } - - utils::error::raise_error(function_name, - utils::error::create_error_message({ - "failed to bind", - "host", - host, - "port", - std::to_string(desired_port), - "error", - std::to_string(get_last_net_error()), - })); - std::this_thread::sleep_for(250ms); - } + auto deadline{std::chrono::steady_clock::now() + 30s}; + std::string host{"127.0.0.1"}; + auto desired_port{config_->get_api_port()}; + auto success{false}; + while (not success && std::chrono::steady_clock::now() < deadline) { + success = server_.bind_to_port(host, desired_port); if (success) { - std::thread([this]() { - for (const auto &[prov, names] : config_->get_auto_start_list()) { - for (const auto &name : names) { - try { - auto location = config_->get_mount_location(prov, name); - if (location.empty()) { - utils::error::raise_error(function_name, - utils::error::create_error_message({ - "failed to auto-mount", - "provider", - provider_type_to_string(prov), - "name", - name, - "location is empty", - })); - } else if (not mount(prov, name, location)) { - utils::error::raise_error(function_name, - utils::error::create_error_message({ - "failed to auto-mount", - "provider", - provider_type_to_string(prov), - "name", - name, - "mount failed", - })); - } - } catch (const std::exception &e) { - utils::error::raise_error(function_name, e, - utils::error::create_error_message({ - "failed to auto-mount", - "provider", - provider_type_to_string(prov), - "name", - name, - })); - } catch (...) { - utils::error::raise_error(function_name, "unknown error", - utils::error::create_error_message({ - "failed to auto-mount", - "provider", - provider_type_to_string(prov), - "name", - name, - })); - } - } - } - }).join(); - - notify_and_unlock(nonce_lock); - - if (should_launch) { - launch_ui(); - should_launch = false; - } - - server_.listen_after_bind(); - return; + break; } + utils::error::raise_error(function_name, + utils::error::create_error_message({ + "failed to bind", + "host", + host, + "port", + std::to_string(desired_port), + "error", + std::to_string(get_last_net_error()), + })); + std::this_thread::sleep_for(250ms); + } + + if (not success) { utils::error::raise_error(function_name, utils::error::create_error_message({ "bind timeout (port in use)", @@ -980,15 +923,63 @@ void ui_server::start() { "port", std::to_string(desired_port), })); - should_launch = false; - } - - notify_and_unlock(nonce_lock); - if (not should_launch) { + notify_and_unlock(nonce_lock); return; } + notify_and_unlock(nonce_lock); + + std::thread([this]() { + for (const auto &[prov, names] : config_->get_auto_start_list()) { + for (const auto &name : names) { + try { + auto location = config_->get_mount_location(prov, name); + if (location.empty()) { + utils::error::raise_error(function_name, + utils::error::create_error_message({ + "failed to auto-mount", + "provider", + provider_type_to_string(prov), + "name", + name, + "location is empty", + })); + } else if (not mount(prov, name, location)) { + utils::error::raise_error(function_name, + utils::error::create_error_message({ + "failed to auto-mount", + "provider", + provider_type_to_string(prov), + "name", + name, + "mount failed", + })); + } + } catch (const std::exception &e) { + utils::error::raise_error(function_name, e, + utils::error::create_error_message({ + "failed to auto-mount", + "provider", + provider_type_to_string(prov), + "name", + name, + })); + } catch (...) { + utils::error::raise_error(function_name, "unknown error", + utils::error::create_error_message({ + "failed to auto-mount", + "provider", + provider_type_to_string(prov), + "name", + name, + })); + } + } + } + }).join(); + launch_ui(); + server_.listen_after_bind(); } void ui_server::stop() { From e57f950f823f241f760becd6257198dfa58016e0 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 15 Sep 2025 14:46:10 -0500 Subject: [PATCH 014/136] refactor --- repertory/repertory/include/ui/ui_server.hpp | 4 + repertory/repertory/src/ui/ui_server.cpp | 151 ++++++++++--------- 2 files changed, 82 insertions(+), 73 deletions(-) diff --git a/repertory/repertory/include/ui/ui_server.hpp b/repertory/repertory/include/ui/ui_server.hpp index 33967e64..ea2964c7 100644 --- a/repertory/repertory/include/ui/ui_server.hpp +++ b/repertory/repertory/include/ui/ui_server.hpp @@ -77,6 +77,8 @@ private: mutable std::mutex test_mtx_; private: + void auto_start_mounts(); + [[nodiscard]] auto data_directory_exists(provider_type prov, std::string_view name) const -> bool; @@ -134,6 +136,8 @@ private: void notify_and_unlock(unique_mutex_lock &nonce_lock); + void open_ui() const; + void removed_expired_nonces(); void set_key_value(provider_type prov, std::string_view name, diff --git a/repertory/repertory/src/ui/ui_server.cpp b/repertory/repertory/src/ui/ui_server.cpp index 34249fd1..7bd282a9 100644 --- a/repertory/repertory/src/ui/ui_server.cpp +++ b/repertory/repertory/src/ui/ui_server.cpp @@ -278,6 +278,57 @@ ui_server::~ui_server() { E_CONSUMER_RELEASE(); } +void ui_server::auto_start_mounts() const { + std::thread([this]() { + for (const auto &[prov, names] : config_->get_auto_start_list()) { + for (const auto &name : names) { + try { + auto location = config_->get_mount_location(prov, name); + if (location.empty()) { + utils::error::raise_error(function_name, + utils::error::create_error_message({ + "failed to auto-mount", + "provider", + provider_type_to_string(prov), + "name", + name, + "location is empty", + })); + } else if (not mount(prov, name, location)) { + utils::error::raise_error(function_name, + utils::error::create_error_message({ + "failed to auto-mount", + "provider", + provider_type_to_string(prov), + "name", + name, + "mount failed", + })); + } + } catch (const std::exception &e) { + utils::error::raise_error(function_name, e, + utils::error::create_error_message({ + "failed to auto-mount", + "provider", + provider_type_to_string(prov), + "name", + name, + })); + } catch (...) { + utils::error::raise_error(function_name, "unknown error", + utils::error::create_error_message({ + "failed to auto-mount", + "provider", + provider_type_to_string(prov), + "name", + name, + })); + } + } + } + }).join(); +} + auto ui_server::data_directory_exists(provider_type prov, std::string_view name) const -> bool { auto data_dir = utils::path::combine(app_config::get_root_data_directory(), @@ -800,6 +851,29 @@ void ui_server::notify_and_unlock(unique_mutex_lock &nonce_lock) { nonce_lock.unlock(); } +void ui_server::open_ui() const { + if (config_->get_launch_only()) { + return; + } + +#if defined(_WIN32) + system(fmt::format( + R"(start "Repertory Management Portal" "http://127.0.0.1:{}/ui")", + config_->get_api_port()) + .c_str()); +#elif defined(__linux__) + system(fmt::format(R"(xdg-open "http://127.0.0.1:{}/ui")", + config_->get_api_port()) + .c_str()); +#elif defined(__APPLE__) + system( + fmt::format(R"(open "http://127.0.0.1:{}/ui")", config_->get_api_port()) + .c_str()); +#else + build fails here +#endif +} + void ui_server::removed_expired_nonces() { unique_mutex_lock nonce_lock(nonce_mtx_); notify_and_unlock(nonce_lock); @@ -860,33 +934,11 @@ void ui_server::start() { nonce_thread_ = std::make_unique([this]() { removed_expired_nonces(); }); - const auto launch_ui = [this]() { - if (not config_->get_launch_only()) { -#if defined(_WIN32) - system( - fmt::format( - R"(start "Repertory Management Portal" "http://127.0.0.1:{}/ui")", - config_->get_api_port()) - .c_str()); -#elif defined(__linux__) - system(fmt::format(R"(xdg-open "http://127.0.0.1:{}/ui")", - config_->get_api_port()) - .c_str()); -#elif defined(__APPLE__) - system(fmt::format(R"(open "http://127.0.0.1:{}/ui")", - config_->get_api_port()) - .c_str()); -#else - build fails here -#endif - } - }; - lock_data ui_lock(provider_type::unknown, "ui"); auto res = ui_lock.grab_lock(1U); if (res != lock_result::success) { notify_and_unlock(nonce_lock); - launch_ui(); + open_ui(); return; } @@ -927,58 +979,11 @@ void ui_server::start() { return; } + auto_start_mounts(); + open_ui(); + notify_and_unlock(nonce_lock); - std::thread([this]() { - for (const auto &[prov, names] : config_->get_auto_start_list()) { - for (const auto &name : names) { - try { - auto location = config_->get_mount_location(prov, name); - if (location.empty()) { - utils::error::raise_error(function_name, - utils::error::create_error_message({ - "failed to auto-mount", - "provider", - provider_type_to_string(prov), - "name", - name, - "location is empty", - })); - } else if (not mount(prov, name, location)) { - utils::error::raise_error(function_name, - utils::error::create_error_message({ - "failed to auto-mount", - "provider", - provider_type_to_string(prov), - "name", - name, - "mount failed", - })); - } - } catch (const std::exception &e) { - utils::error::raise_error(function_name, e, - utils::error::create_error_message({ - "failed to auto-mount", - "provider", - provider_type_to_string(prov), - "name", - name, - })); - } catch (...) { - utils::error::raise_error(function_name, "unknown error", - utils::error::create_error_message({ - "failed to auto-mount", - "provider", - provider_type_to_string(prov), - "name", - name, - })); - } - } - } - }).join(); - - launch_ui(); server_.listen_after_bind(); } From bbd0445a44f2e8014af649458a30a0787cbc74bf Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 15 Sep 2025 14:49:03 -0500 Subject: [PATCH 015/136] fix --- repertory/repertory/src/ui/ui_server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repertory/repertory/src/ui/ui_server.cpp b/repertory/repertory/src/ui/ui_server.cpp index 7bd282a9..da703af4 100644 --- a/repertory/repertory/src/ui/ui_server.cpp +++ b/repertory/repertory/src/ui/ui_server.cpp @@ -278,7 +278,7 @@ ui_server::~ui_server() { E_CONSUMER_RELEASE(); } -void ui_server::auto_start_mounts() const { +void ui_server::auto_start_mounts() { std::thread([this]() { for (const auto &[prov, names] : config_->get_auto_start_list()) { for (const auto &name : names) { From 5583d3f493501131437493e10d82f6505344a9cc Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 15 Sep 2025 14:53:13 -0500 Subject: [PATCH 016/136] fix --- repertory/repertory/src/ui/ui_server.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/repertory/repertory/src/ui/ui_server.cpp b/repertory/repertory/src/ui/ui_server.cpp index da703af4..2dc2460b 100644 --- a/repertory/repertory/src/ui/ui_server.cpp +++ b/repertory/repertory/src/ui/ui_server.cpp @@ -279,6 +279,8 @@ ui_server::~ui_server() { } void ui_server::auto_start_mounts() { + REPERTORY_USES_FUNCTION_NAME(); + std::thread([this]() { for (const auto &[prov, names] : config_->get_auto_start_list()) { for (const auto &name : names) { From cbfa64556a9644a48f64acaa3a83532be18b26ab Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Tue, 16 Sep 2025 06:56:12 -0500 Subject: [PATCH 017/136] Pinning a file should automatically initiate a download to cache #38 --- .../librepertory/src/db/impl/rdb_meta_db.cpp | 12 ++++++--- .../src/db/impl/sqlite_meta_db.cpp | 11 +++++--- .../src/file_manager/file_manager.cpp | 25 ++++++++++++++----- .../src/providers/base_provider.cpp | 7 ++++-- 4 files changed, 40 insertions(+), 15 deletions(-) diff --git a/repertory/librepertory/src/db/impl/rdb_meta_db.cpp b/repertory/librepertory/src/db/impl/rdb_meta_db.cpp index 538f3d90..7598ccf1 100644 --- a/repertory/librepertory/src/db/impl/rdb_meta_db.cpp +++ b/repertory/librepertory/src/db/impl/rdb_meta_db.cpp @@ -318,9 +318,10 @@ void rdb_meta_db::remove_api_path(const std::string &api_path) { } } -auto rdb_meta_db::remove_api_path( - const std::string &api_path, const std::string &source_path, - rocksdb::Transaction *txn) -> rocksdb::Status { +auto rdb_meta_db::remove_api_path(const std::string &api_path, + const std::string &source_path, + rocksdb::Transaction *txn) + -> rocksdb::Status { auto txn_res = txn->Delete(pinned_family_, api_path); if (not txn_res.ok()) { return txn_res; @@ -345,7 +346,10 @@ auto rdb_meta_db::remove_item_meta(const std::string &api_path, const std::string &key) -> api_error { if (key == META_DIRECTORY || key == META_PINNED || key == META_SIZE || key == META_SOURCE) { - // TODO log warning for unsupported attributes + utils::error::raise_api_path_error( + function_name, api_path, + fmt::format("failed to remove item meta-key is restricted|key|{}", + key)); return api_error::success; } diff --git a/repertory/librepertory/src/db/impl/sqlite_meta_db.cpp b/repertory/librepertory/src/db/impl/sqlite_meta_db.cpp index 3bf829a7..aa292fe7 100644 --- a/repertory/librepertory/src/db/impl/sqlite_meta_db.cpp +++ b/repertory/librepertory/src/db/impl/sqlite_meta_db.cpp @@ -307,7 +307,10 @@ auto sqlite_meta_db::remove_item_meta(const std::string &api_path, const std::string &key) -> api_error { if (key == META_DIRECTORY || key == META_PINNED || key == META_SIZE || key == META_SOURCE) { - // TODO log warning for unsupported attributes + utils::error::raise_api_path_error( + function_name, api_path, + fmt::format("failed to remove item meta-key is restricted|key|{}", + key)); return api_error::success; } @@ -343,8 +346,10 @@ auto sqlite_meta_db::set_item_meta(const std::string &api_path, auto sqlite_meta_db::set_item_meta(const std::string &api_path, const api_meta_map &meta) -> api_error { api_meta_map existing_meta{}; - if (get_item_meta(api_path, existing_meta) != api_error::success) { - // TODO handle error + auto res = get_item_meta(api_path, existing_meta); + if (res != api_error::success) { + utils::error::raise_api_path_error(function_name, api_path, res, + "failed to get item meta"); } for (const auto &item : meta) { diff --git a/repertory/librepertory/src/file_manager/file_manager.cpp b/repertory/librepertory/src/file_manager/file_manager.cpp index 1d62c060..ab4c5b18 100644 --- a/repertory/librepertory/src/file_manager/file_manager.cpp +++ b/repertory/librepertory/src/file_manager/file_manager.cpp @@ -158,18 +158,27 @@ auto file_manager::download_pinned_file(const std::string &api_path) -> bool { try { if (provider_.is_read_only()) { - // TODO handle error + utils::error::raise_api_path_error( + function_name, api_path, + fmt::format( + "failed to download pinned file-provider is " + "read-only|type|{}", + app_config::get_provider_name(provider_.get_provider_type()))); return false; } std::string str_pinned; auto res = provider_.get_item_meta(api_path, META_PINNED, str_pinned); if (res != api_error::success) { - // TODO handle error + utils::error::raise_api_path_error( + function_name, api_path, res, + "failed to get item meta|key|META_PINNED"); return false; } if (not utils::string::to_bool(str_pinned)) { - // TODO handle error + utils::error::raise_api_path_error( + function_name, api_path, + "failed to download pinned file-file is not pinned"); return false; } @@ -177,17 +186,21 @@ auto file_manager::download_pinned_file(const std::string &api_path) -> bool { std::shared_ptr open_file; res = open(api_path, false, {}, handle, open_file); if (res != api_error::success) { - // TODO handle error + utils::error::raise_api_path_error( + function_name, api_path, res, + "failed to download pinned file-file open failed"); return false; } auto ret = get_open_file(handle, true, open_file); if (ret) { open_file->force_download(); + } else { + utils::error::raise_api_path_error( + function_name, api_path, + "failed to download pinned file-file open as writeable failed"); } - // TODO handle error (ret == false) - close(handle); return ret; } catch (const std::exception &ex) { diff --git a/repertory/librepertory/src/providers/base_provider.cpp b/repertory/librepertory/src/providers/base_provider.cpp index 90546250..a207ff6e 100644 --- a/repertory/librepertory/src/providers/base_provider.cpp +++ b/repertory/librepertory/src/providers/base_provider.cpp @@ -838,7 +838,8 @@ auto base_provider::set_item_meta(const std::string &api_path, if (key == META_PINNED && utils::string::to_bool(value)) { if (fm_ == nullptr || not fm_->download_pinned_file(api_path)) { - // TODO handle error + utils::error::raise_api_path_error(function_name, api_path, + "failed to pin file"); } } @@ -847,6 +848,7 @@ auto base_provider::set_item_meta(const std::string &api_path, auto base_provider::set_item_meta(const std::string &api_path, api_meta_map meta) -> api_error { + REPERTORY_USES_FUNCTION_NAME(); auto ret = meta_db_->set_item_meta(api_path, meta); if (ret != api_error::success) { @@ -856,7 +858,8 @@ auto base_provider::set_item_meta(const std::string &api_path, if (meta.contains(META_PINNED) && utils::string::to_bool(meta.at(META_PINNED))) { if (fm_ == nullptr || not fm_->download_pinned_file(api_path)) { - // TODO handle error + utils::error::raise_api_path_error(function_name, api_path, + "failed to pin file"); } } From 06485e03076ccfb94949744a55765e1bf761d7e3 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Tue, 16 Sep 2025 07:02:33 -0500 Subject: [PATCH 018/136] fixes --- repertory/librepertory/include/utils/error_utils.hpp | 3 +++ repertory/librepertory/src/db/impl/rdb_meta_db.cpp | 2 ++ repertory/librepertory/src/db/impl/sqlite_meta_db.cpp | 2 ++ repertory/librepertory/src/utils/error_utils.cpp | 7 +++++++ 4 files changed, 14 insertions(+) diff --git a/repertory/librepertory/include/utils/error_utils.hpp b/repertory/librepertory/include/utils/error_utils.hpp index 4c030e17..8999d4f5 100644 --- a/repertory/librepertory/include/utils/error_utils.hpp +++ b/repertory/librepertory/include/utils/error_utils.hpp @@ -50,6 +50,9 @@ void raise_error(std::string_view function, std::int64_t err, void raise_error(std::string_view function, const std::exception &exception, std::string_view file_path, std::string_view msg); +void raise_api_path_error(std::string_view function, std::string_view api_path, + std::string_view msg); + void raise_api_path_error(std::string_view function, std::string_view api_path, api_error err, std::string_view msg); diff --git a/repertory/librepertory/src/db/impl/rdb_meta_db.cpp b/repertory/librepertory/src/db/impl/rdb_meta_db.cpp index 7598ccf1..e6706a44 100644 --- a/repertory/librepertory/src/db/impl/rdb_meta_db.cpp +++ b/repertory/librepertory/src/db/impl/rdb_meta_db.cpp @@ -344,6 +344,8 @@ auto rdb_meta_db::remove_api_path(const std::string &api_path, auto rdb_meta_db::remove_item_meta(const std::string &api_path, const std::string &key) -> api_error { + REPERTORY_USES_FUNCTION_NAME(); + if (key == META_DIRECTORY || key == META_PINNED || key == META_SIZE || key == META_SOURCE) { utils::error::raise_api_path_error( diff --git a/repertory/librepertory/src/db/impl/sqlite_meta_db.cpp b/repertory/librepertory/src/db/impl/sqlite_meta_db.cpp index aa292fe7..99b5a385 100644 --- a/repertory/librepertory/src/db/impl/sqlite_meta_db.cpp +++ b/repertory/librepertory/src/db/impl/sqlite_meta_db.cpp @@ -305,6 +305,8 @@ void sqlite_meta_db::remove_api_path(const std::string &api_path) { auto sqlite_meta_db::remove_item_meta(const std::string &api_path, const std::string &key) -> api_error { + REPERTORY_USES_FUNCTION_NAME(); + if (key == META_DIRECTORY || key == META_PINNED || key == META_SIZE || key == META_SOURCE) { utils::error::raise_api_path_error( diff --git a/repertory/librepertory/src/utils/error_utils.cpp b/repertory/librepertory/src/utils/error_utils.cpp index 4a5e9ff2..ce128f7e 100644 --- a/repertory/librepertory/src/utils/error_utils.cpp +++ b/repertory/librepertory/src/utils/error_utils.cpp @@ -146,6 +146,13 @@ void raise_error(std::string_view function, const std::exception &exception, (exception.what() == nullptr ? "unknown error" : exception.what())); } +void raise_api_path_error(std::string_view function, std::string_view api_path, + std::string_view msg) { + event_system::instance().raise( + function, static_cast(msg) + "|ap|" + + static_cast(api_path)); +} + void raise_api_path_error(std::string_view function, std::string_view api_path, api_error err, std::string_view msg) { event_system::instance().raise( From 9f956aedf38d04ea63bea4586f1a496a20441671 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Tue, 16 Sep 2025 07:06:09 -0500 Subject: [PATCH 019/136] fix --- repertory/librepertory/src/db/impl/rdb_meta_db.cpp | 5 ++++- repertory/librepertory/src/db/impl/sqlite_meta_db.cpp | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/repertory/librepertory/src/db/impl/rdb_meta_db.cpp b/repertory/librepertory/src/db/impl/rdb_meta_db.cpp index e6706a44..446ce4e4 100644 --- a/repertory/librepertory/src/db/impl/rdb_meta_db.cpp +++ b/repertory/librepertory/src/db/impl/rdb_meta_db.cpp @@ -423,10 +423,13 @@ auto rdb_meta_db::set_item_meta(const std::string &api_path, auto rdb_meta_db::set_item_meta(const std::string &api_path, const api_meta_map &meta) -> api_error { + REPERTORY_USES_FUNCTION_NAME(); + json json_data; auto res = get_item_meta_json(api_path, json_data); if (res != api_error::success && res != api_error::item_not_found) { - return res; + utils::error::raise_api_path_error(function_name, api_path, res, + "failed to get item meta"); } for (const auto &data : meta) { diff --git a/repertory/librepertory/src/db/impl/sqlite_meta_db.cpp b/repertory/librepertory/src/db/impl/sqlite_meta_db.cpp index 99b5a385..2dc57e7a 100644 --- a/repertory/librepertory/src/db/impl/sqlite_meta_db.cpp +++ b/repertory/librepertory/src/db/impl/sqlite_meta_db.cpp @@ -347,9 +347,11 @@ auto sqlite_meta_db::set_item_meta(const std::string &api_path, auto sqlite_meta_db::set_item_meta(const std::string &api_path, const api_meta_map &meta) -> api_error { + REPERTORY_USES_FUNCTION_NAME(); + api_meta_map existing_meta{}; auto res = get_item_meta(api_path, existing_meta); - if (res != api_error::success) { + if (res != api_error::success && res != api_error::item_not_found) { utils::error::raise_api_path_error(function_name, api_path, res, "failed to get item meta"); } From e511cd747a17662111f7fc916b21f431875990af Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Tue, 16 Sep 2025 07:08:39 -0500 Subject: [PATCH 020/136] fix --- repertory/librepertory/src/providers/base_provider.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/repertory/librepertory/src/providers/base_provider.cpp b/repertory/librepertory/src/providers/base_provider.cpp index a207ff6e..ab48c934 100644 --- a/repertory/librepertory/src/providers/base_provider.cpp +++ b/repertory/librepertory/src/providers/base_provider.cpp @@ -831,6 +831,8 @@ void base_provider::remove_unmatched_source_files(stop_type &stop_requested) { auto base_provider::set_item_meta(const std::string &api_path, const std::string &key, const std::string &value) -> api_error { + REPERTORY_USES_FUNCTION_NAME(); + auto ret = meta_db_->set_item_meta(api_path, key, value); if (ret != api_error::success) { return ret; From 66b3a0595de0ce1d4fa2d5b77711ab2e8d6f119f Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Tue, 16 Sep 2025 10:25:54 -0500 Subject: [PATCH 021/136] [unit test] Complete all providers unit tests #12 --- .../repertory_test/src/providers_test.cpp | 186 +++++++++++++++++- 1 file changed, 182 insertions(+), 4 deletions(-) diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index 947fcb6c..ae79b494 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -698,6 +698,10 @@ static void get_file_fails_if_item_is_directory(const app_config &cfg, } static void get_file_list(const app_config &cfg, i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + api_file_list list{}; std::string marker; EXPECT_EQ(api_error::success, provider.get_file_list(list, marker)); @@ -724,6 +728,128 @@ static void get_file_list(const app_config &cfg, i_provider &provider) { } } +static void get_file_size(i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + + if (provider.get_provider_type() == provider_type::encrypt) { + api_file_list list{}; + std::string marker; + auto res = provider.get_file_list(list, marker); + ASSERT_EQ(res, api_error::success); + + ASSERT_FALSE(list.empty()); + + std::uint64_t size{}; + res = provider.get_file_size(list.front().api_path, size); + EXPECT_EQ(api_error::success, res); + EXPECT_EQ(utils::encryption::encrypting_reader::calculate_encrypted_size( + 7U, true), + size); + return; + } + + auto &file = test::create_random_file(128U); + auto api_path = + fmt::format("/{}", utils::path::strip_to_file_name(file.get_path())); + + stop_type stop_requested{false}; + auto res = provider.upload_file(api_path, file.get_path(), stop_requested); + ASSERT_EQ(api_error::success, res); + + std::uint64_t size{}; + res = provider.get_file_size(api_path, size); + EXPECT_EQ(api_error::success, res); + EXPECT_EQ(*file.size(), size); + + res = provider.remove_file(api_path); + EXPECT_EQ(api_error::success, res); +} + +static void get_file_size_fails_if_path_not_found(i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + + std::uint64_t size{}; + auto res = provider.get_file_size("/cow/moose/dog/chicken", size); + + EXPECT_EQ(api_error::item_not_found, res); + EXPECT_EQ(0U, size); +} + +static void get_filesystem_item(i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + + if (provider.get_provider_type() == provider_type::encrypt) { + api_file_list list{}; + std::string marker; + auto res = provider.get_file_list(list, marker); + ASSERT_EQ(api_error::success, res); + ASSERT_FALSE(list.empty()); + + auto api_path = list.front().api_path; + + filesystem_item fsi{}; + res = provider.get_filesystem_item(api_path, fsi); + EXPECT_EQ(api_error::success, res); + + EXPECT_FALSE(fsi.is_directory); + EXPECT_EQ(api_path, fsi.api_path); + + std::uint64_t size{}; + res = provider.get_file_size(api_path, size); + ASSERT_EQ(api_error::success, res); + EXPECT_EQ(size, fsi.file_size); + + return; + } + + auto &src = test::create_random_file(128U); + auto api_path = + fmt::format("/{}", utils::path::strip_to_file_name(src.get_path())); + + stop_type stop_requested{false}; + auto res = provider.upload_file(api_path, src.get_path(), stop_requested); + ASSERT_EQ(api_error::success, res); + + filesystem_item fsi{}; + res = provider.get_filesystem_item(api_path, fsi); + EXPECT_EQ(api_error::success, res); + EXPECT_EQ(api_path, fsi.api_path); + EXPECT_FALSE(fsi.is_directory); + EXPECT_EQ(*src.size(), fsi.file_size); + + res = provider.remove_file(api_path); + EXPECT_EQ(api_error::success, res); +} + +static void get_filesystem_item_root_is_directory(i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + + filesystem_item fsi{}; + auto res = provider.get_filesystem_item("/", fsi); + + EXPECT_EQ(api_error::success, res); + EXPECT_TRUE(fsi.is_directory); + EXPECT_EQ("/", fsi.api_path); +} + +static void get_filesystem_item_fails_if_path_not_found(i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + + filesystem_item fsi{}; + auto res = provider.get_filesystem_item("/cow/moose/dog/chicken", fsi); + EXPECT_EQ(api_error::item_not_found, res); +} + static void run_tests(const app_config &cfg, i_provider &provider) { get_file_list(cfg, provider); ASSERT_FALSE(::testing::Test::HasFailure()); @@ -755,10 +881,15 @@ static void run_tests(const app_config &cfg, i_provider &provider) { get_file_fails_if_file_not_found(provider); get_file_fails_if_item_is_directory(cfg, provider); - // TODO need to test read when file size changes for encrypt provider - /* get_file_list(provider); get_file_size(provider); + get_file_size_fails_if_path_not_found(provider); + get_filesystem_item(provider); + get_filesystem_item_root_is_directory(provider); + get_filesystem_item_fails_if_path_not_found(provider); + + // TODO need to test read when file size changes for encrypt provider + /* get_filesystem_item_and_file(provider); get_filesystem_item_from_source_path(provider); get_item_meta(provider); @@ -825,7 +956,7 @@ TEST(providers_test, encrypt_provider) { event_system::instance().stop(); } -TEST(providers_test, s3_provider) { +TEST(providers_test, s3_provider_unencrypted) { auto config_path = utils::path::combine(test::get_test_output_dir(), {"provider", "s3"}); @@ -838,7 +969,54 @@ TEST(providers_test, s3_provider) { app_config src_cfg( provider_type::s3, utils::path::combine(test::get_test_config_dir(), {"s3"})); - cfg.set_s3_config(src_cfg.get_s3_config()); + auto s3_cfg = src_cfg.get_s3_config(); + s3_cfg.encryption_token = ""; + cfg.set_s3_config(s3_cfg); + } + + curl_comm comm{cfg.get_s3_config()}; + + s3_provider provider{cfg, comm}; + file_manager mgr(cfg, provider); + + EXPECT_TRUE(provider.start( + [&provider](bool directory, api_file &file) -> api_error { + return provider_meta_handler(provider, directory, file); + }, + &mgr)); + + mgr.start(); + + EXPECT_EQ(provider_type::s3, provider.get_provider_type()); + EXPECT_FALSE(provider.is_read_only()); + EXPECT_TRUE(provider.is_online()); + EXPECT_FALSE(provider.is_rename_supported()); + + run_tests(cfg, provider); + + provider.stop(); + mgr.stop(); + } + + event_system::instance().stop(); +} + +TEST(providers_test, s3_provider_encrypted) { + auto config_path = + utils::path::combine(test::get_test_output_dir(), {"provider", "s3"}); + + console_consumer consumer{}; + event_system::instance().start(); + + { + app_config cfg(provider_type::s3, config_path); + { + app_config src_cfg( + provider_type::s3, + utils::path::combine(test::get_test_config_dir(), {"s3"})); + auto s3_cfg = src_cfg.get_s3_config(); + s3_cfg.encryption_token = "cow_moose_doge_chicken"; + cfg.set_s3_config(s3_cfg); } curl_comm comm{cfg.get_s3_config()}; From 816d242234f4bb9dc3b29fea3bea9321cddb3c6e Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Tue, 16 Sep 2025 10:36:23 -0500 Subject: [PATCH 022/136] [unit test] Complete all providers unit tests #12 --- .../repertory_test/src/providers_test.cpp | 28 +++++++++++++------ support/include/utils/encrypting_reader.hpp | 4 +++ support/src/utils/encrypting_reader.cpp | 11 ++++++-- 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index ae79b494..270b1066 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -794,10 +794,10 @@ static void get_filesystem_item(i_provider &provider) { auto api_path = list.front().api_path; filesystem_item fsi{}; - res = provider.get_filesystem_item(api_path, fsi); + res = provider.get_filesystem_item(api_path, false, fsi); EXPECT_EQ(api_error::success, res); - EXPECT_FALSE(fsi.is_directory); + EXPECT_FALSE(fsi.directory); EXPECT_EQ(api_path, fsi.api_path); std::uint64_t size{}; @@ -817,10 +817,10 @@ static void get_filesystem_item(i_provider &provider) { ASSERT_EQ(api_error::success, res); filesystem_item fsi{}; - res = provider.get_filesystem_item(api_path, fsi); + res = provider.get_filesystem_item(api_path, false, fsi); EXPECT_EQ(api_error::success, res); EXPECT_EQ(api_path, fsi.api_path); - EXPECT_FALSE(fsi.is_directory); + EXPECT_FALSE(fsi.directory); EXPECT_EQ(*src.size(), fsi.file_size); res = provider.remove_file(api_path); @@ -833,23 +833,35 @@ static void get_filesystem_item_root_is_directory(i_provider &provider) { __FUNCTION__); filesystem_item fsi{}; - auto res = provider.get_filesystem_item("/", fsi); + auto res = provider.get_filesystem_item("/", true, fsi); EXPECT_EQ(api_error::success, res); - EXPECT_TRUE(fsi.is_directory); + EXPECT_TRUE(fsi.directory); EXPECT_EQ("/", fsi.api_path); } -static void get_filesystem_item_fails_if_path_not_found(i_provider &provider) { +static void +get_filesystem_item_fails_if_file_is_not_found(i_provider &provider) { fmt::println("testing|{}|{}", app_config::get_provider_name(provider.get_provider_type()), __FUNCTION__); filesystem_item fsi{}; - auto res = provider.get_filesystem_item("/cow/moose/dog/chicken", fsi); + auto res = provider.get_filesystem_item("/cow/moose/dog/chicken", false, fsi); EXPECT_EQ(api_error::item_not_found, res); } +static void +get_filesystem_item_fails_if_directory_is_not_found(i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + + filesystem_item fsi{}; + auto res = provider.get_filesystem_item("/cow/moose/dog/chicken", true, fsi); + EXPECT_EQ(api_error::directory_not_found, res); +} + static void run_tests(const app_config &cfg, i_provider &provider) { get_file_list(cfg, provider); ASSERT_FALSE(::testing::Test::HasFailure()); diff --git a/support/include/utils/encrypting_reader.hpp b/support/include/utils/encrypting_reader.hpp index d98a138b..4fb56961 100644 --- a/support/include/utils/encrypting_reader.hpp +++ b/support/include/utils/encrypting_reader.hpp @@ -174,6 +174,10 @@ public: calculate_encrypted_size(std::string_view source_path, bool uses_kdf) -> std::uint64_t; + [[nodiscard]] static auto calculate_encrypted_size(std::uint64_t size, + bool uses_kdf) + -> std::uint64_t; + [[nodiscard]] auto create_iostream() const -> std::shared_ptr; [[nodiscard]] static constexpr auto get_encrypted_chunk_size() diff --git a/support/src/utils/encrypting_reader.cpp b/support/src/utils/encrypting_reader.cpp index 0132c865..78dd10fb 100644 --- a/support/src/utils/encrypting_reader.cpp +++ b/support/src/utils/encrypting_reader.cpp @@ -430,11 +430,16 @@ auto encrypting_reader::calculate_encrypted_size(std::string_view source_path, source_path, }); } - auto file_size{opt_size.value()}; + return calculate_encrypted_size(opt_size.value(), uses_kdf); +} + +auto encrypting_reader::calculate_encrypted_size(std::uint64_t size, + bool uses_kdf) + -> std::uint64_t { auto total_chunks = utils::divide_with_ceiling( - file_size, static_cast(data_chunk_size_)); - return file_size + (total_chunks * encryption_header_size) + + size, static_cast(data_chunk_size_)); + return size + (total_chunks * encryption_header_size) + (uses_kdf ? kdf_config::size() : 0U); } From 72495c891be6fdf6b1bde5ca7e62071435bb8b25 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Tue, 16 Sep 2025 10:42:00 -0500 Subject: [PATCH 023/136] [unit test] Complete all providers unit tests #12 --- repertory/repertory_test/src/providers_test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index 270b1066..86999602 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -803,7 +803,7 @@ static void get_filesystem_item(i_provider &provider) { std::uint64_t size{}; res = provider.get_file_size(api_path, size); ASSERT_EQ(api_error::success, res); - EXPECT_EQ(size, fsi.file_size); + EXPECT_EQ(size, fsi.size); return; } @@ -821,7 +821,7 @@ static void get_filesystem_item(i_provider &provider) { EXPECT_EQ(api_error::success, res); EXPECT_EQ(api_path, fsi.api_path); EXPECT_FALSE(fsi.directory); - EXPECT_EQ(*src.size(), fsi.file_size); + EXPECT_EQ(*src.size(), fsi.size); res = provider.remove_file(api_path); EXPECT_EQ(api_error::success, res); From a1a9c916a1f5b04837ebe23f73bdb01a1b186e22 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Tue, 16 Sep 2025 10:48:10 -0500 Subject: [PATCH 024/136] [unit test] Complete all providers unit tests #12 --- repertory/repertory_test/src/providers_test.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index 86999602..038575e6 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -898,7 +898,8 @@ static void run_tests(const app_config &cfg, i_provider &provider) { get_filesystem_item(provider); get_filesystem_item_root_is_directory(provider); - get_filesystem_item_fails_if_path_not_found(provider); + get_filesystem_item_fails_if_file_is_not_found(provider); + get_filesystem_item_fails_if_directory_is_not_found(provider); // TODO need to test read when file size changes for encrypt provider /* From 865d9f5ba67ca78cd022a01cb3382687097a6dd7 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Tue, 16 Sep 2025 13:03:09 -0500 Subject: [PATCH 025/136] [unit test] Complete all providers unit tests #12 --- .../include/providers/base_provider.hpp | 5 -- .../providers/encrypt/encrypt_provider.hpp | 5 -- .../include/providers/i_provider.hpp | 4 -- .../src/providers/base_provider.cpp | 33 ----------- .../providers/encrypt/encrypt_provider.cpp | 40 ------------- .../include/mocks/mock_provider.hpp | 5 -- .../repertory_test/src/providers_test.cpp | 59 ++++++++++++++++++- 7 files changed, 56 insertions(+), 95 deletions(-) diff --git a/repertory/librepertory/include/providers/base_provider.hpp b/repertory/librepertory/include/providers/base_provider.hpp index c54fb5d8..ec63dc8c 100644 --- a/repertory/librepertory/include/providers/base_provider.hpp +++ b/repertory/librepertory/include/providers/base_provider.hpp @@ -164,11 +164,6 @@ public: filesystem_item &fsi) const -> api_error override; - [[nodiscard]] auto get_filesystem_item_and_file(const std::string &api_path, - api_file &f, - filesystem_item &fsi) const - -> api_error override; - [[nodiscard]] auto get_filesystem_item_from_source_path(const std::string &source_path, filesystem_item &fsi) const diff --git a/repertory/librepertory/include/providers/encrypt/encrypt_provider.hpp b/repertory/librepertory/include/providers/encrypt/encrypt_provider.hpp index 77ddc722..6808c2da 100644 --- a/repertory/librepertory/include/providers/encrypt/encrypt_provider.hpp +++ b/repertory/librepertory/include/providers/encrypt/encrypt_provider.hpp @@ -145,11 +145,6 @@ public: filesystem_item &fsi) const -> api_error override; - [[nodiscard]] auto get_filesystem_item_and_file(const std::string &api_path, - api_file &file, - filesystem_item &fsi) const - -> api_error override; - [[nodiscard]] auto get_filesystem_item_from_source_path(const std::string &source_path, filesystem_item &fsi) const diff --git a/repertory/librepertory/include/providers/i_provider.hpp b/repertory/librepertory/include/providers/i_provider.hpp index bd538c0b..91e35997 100644 --- a/repertory/librepertory/include/providers/i_provider.hpp +++ b/repertory/librepertory/include/providers/i_provider.hpp @@ -79,10 +79,6 @@ public: filesystem_item &fsi) const -> api_error = 0; - [[nodiscard]] virtual auto - get_filesystem_item_and_file(const std::string &api_path, api_file &file, - filesystem_item &fsi) const -> api_error = 0; - [[nodiscard]] virtual auto get_filesystem_item_from_source_path(const std::string &source_path, filesystem_item &fsi) const diff --git a/repertory/librepertory/src/providers/base_provider.cpp b/repertory/librepertory/src/providers/base_provider.cpp index ab48c934..648f8820 100644 --- a/repertory/librepertory/src/providers/base_provider.cpp +++ b/repertory/librepertory/src/providers/base_provider.cpp @@ -435,30 +435,6 @@ auto base_provider::get_filesystem_item(const std::string &api_path, return api_error::success; } -auto base_provider::get_filesystem_item_and_file(const std::string &api_path, - api_file &file, - filesystem_item &fsi) const - -> api_error { - auto res = get_file(api_path, file); - if (res != api_error::success) { - return res; - } - - api_meta_map meta{}; - res = get_item_meta(api_path, meta); - if (res != api_error::success) { - return res; - } - - fsi.api_parent = utils::path::get_parent_api_path(api_path); - fsi.api_path = api_path; - fsi.directory = false; - fsi.size = utils::string::to_uint64(meta[META_SIZE]); - fsi.source_path = meta[META_SOURCE]; - - return api_error::success; -} - auto base_provider::get_filesystem_item_from_source_path( const std::string &source_path, filesystem_item &fsi) const -> api_error { std::string api_path{}; @@ -467,15 +443,6 @@ auto base_provider::get_filesystem_item_from_source_path( return res; } - bool exists{}; - res = is_directory(api_path, exists); - if (res != api_error::success) { - return res; - } - if (exists) { - return api_error::directory_exists; - } - return get_filesystem_item(api_path, false, fsi); } diff --git a/repertory/librepertory/src/providers/encrypt/encrypt_provider.cpp b/repertory/librepertory/src/providers/encrypt/encrypt_provider.cpp index 18fe0e7c..2947bcdb 100644 --- a/repertory/librepertory/src/providers/encrypt/encrypt_provider.cpp +++ b/repertory/librepertory/src/providers/encrypt/encrypt_provider.cpp @@ -474,49 +474,9 @@ auto encrypt_provider::get_filesystem_item_from_source_path( return res; } - bool exists{}; - res = is_directory(api_path, exists); - if (res != api_error::success) { - return res; - } - if (exists) { - return api_error::directory_exists; - } - return get_filesystem_item(api_path, false, fsi); } -auto encrypt_provider::get_filesystem_item_and_file(const std::string &api_path, - api_file &file, - filesystem_item &fsi) const - -> api_error { - REPERTORY_USES_FUNCTION_NAME(); - - try { - bool exists{}; - auto res{is_directory(api_path, exists)}; - if (res != api_error::success) { - return res; - } - if (exists) { - return api_error::directory_exists; - } - - auto ret{get_filesystem_item(api_path, exists, fsi)}; - if (ret != api_error::success) { - return ret; - } - - file = create_api_file(api_path, false, fsi.source_path); - return api_error::success; - } catch (const std::exception &ex) { - utils::error::raise_error(function_name, ex, api_path, - "failed to get filesystem_item and file"); - } - - return api_error::error; -} - auto encrypt_provider::get_pinned_files() const -> std::vector { return {}; } diff --git a/repertory/repertory_test/include/mocks/mock_provider.hpp b/repertory/repertory_test/include/mocks/mock_provider.hpp index d4b88d4b..99c294bb 100644 --- a/repertory/repertory_test/include/mocks/mock_provider.hpp +++ b/repertory/repertory_test/include/mocks/mock_provider.hpp @@ -80,11 +80,6 @@ public: filesystem_item &fsi), (const, override)); - MOCK_METHOD(api_error, get_filesystem_item_and_file, - (const std::string &api_path, api_file &file, - filesystem_item &fsi), - (const, override)); - MOCK_METHOD(api_error, get_filesystem_item_from_source_path, (const std::string &source_path, filesystem_item &fsi), (const, override)); diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index 038575e6..5a5e9e1b 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -745,7 +745,7 @@ static void get_file_size(i_provider &provider) { res = provider.get_file_size(list.front().api_path, size); EXPECT_EQ(api_error::success, res); EXPECT_EQ(utils::encryption::encrypting_reader::calculate_encrypted_size( - 7U, true), + 8U, true), size); return; } @@ -753,6 +753,7 @@ static void get_file_size(i_provider &provider) { auto &file = test::create_random_file(128U); auto api_path = fmt::format("/{}", utils::path::strip_to_file_name(file.get_path())); + create_file(provider, api_path); stop_type stop_requested{false}; auto res = provider.upload_file(api_path, file.get_path(), stop_requested); @@ -811,6 +812,7 @@ static void get_filesystem_item(i_provider &provider) { auto &src = test::create_random_file(128U); auto api_path = fmt::format("/{}", utils::path::strip_to_file_name(src.get_path())); + create_file(provider, api_path); stop_type stop_requested{false}; auto res = provider.upload_file(api_path, src.get_path(), stop_requested); @@ -862,6 +864,56 @@ get_filesystem_item_fails_if_directory_is_not_found(i_provider &provider) { EXPECT_EQ(api_error::directory_not_found, res); } +static void get_filesystem_item_from_source_path(const app_config &cfg, + i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + + std::string api_path; + std::string source_path; + std::uint64_t size{}; + if (provider.get_provider_type() == provider_type::encrypt) { + source_path = + utils::path::combine(cfg.get_encrypt_config().path, "/test.txt"); + size = utils::encryption::encrypting_reader::calculate_encrypted_size(8U, + true); + } else { + size = 128U; + auto &file = test::create_random_file(size); + api_path = + fmt::format("/{}", utils::path::strip_to_file_name(file.get_path())); + create_file(provider, api_path); + + source_path = file.get_path(); + stop_type stop_requested{false}; + auto res = provider.upload_file(api_path, source_path, stop_requested); + ASSERT_EQ(api_error::success, res); + } + + filesystem_item fsi{}; + auto res = provider.get_filesystem_item_from_source_path(source_path, fsi); + EXPECT_EQ(api_error::success, res); + EXPECT_FALSE(fsi.directory); + EXPECT_EQ(size, fsi.size); + + if (provider.get_provider_type() != provider_type::encrypt) { + EXPECT_EQ(api_error::success, provider.remove_file(api_path)); + } +} + +static void get_filesystem_item_from_source_path_fails_if_file_is_not_found( + const app_config &cfg, i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + + filesystem_item fsi{}; + auto res = provider.get_filesystem_item_from_source_path( + "/cow/moose/doge/chicken", fsi); + EXPECT_EQ(api_error::item_not_found, res); +} + static void run_tests(const app_config &cfg, i_provider &provider) { get_file_list(cfg, provider); ASSERT_FALSE(::testing::Test::HasFailure()); @@ -901,10 +953,11 @@ static void run_tests(const app_config &cfg, i_provider &provider) { get_filesystem_item_fails_if_file_is_not_found(provider); get_filesystem_item_fails_if_directory_is_not_found(provider); + get_filesystem_item_from_source_path(cfg, provider); + get_filesystem_item_from_source_path_fails_if_file_is_not_found(cfg, + provider); // TODO need to test read when file size changes for encrypt provider /* - get_filesystem_item_and_file(provider); - get_filesystem_item_from_source_path(provider); get_item_meta(provider); get_item_meta2(provider); get_pinned_files(provider); From 8f3e06cd1726ef31d45023cf74616e8bc27c94ba Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Tue, 16 Sep 2025 13:23:36 -0500 Subject: [PATCH 026/136] [unit test] Complete all providers unit tests #12 --- repertory/repertory_test/src/providers_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index 5a5e9e1b..dcf16735 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -875,7 +875,7 @@ static void get_filesystem_item_from_source_path(const app_config &cfg, std::uint64_t size{}; if (provider.get_provider_type() == provider_type::encrypt) { source_path = - utils::path::combine(cfg.get_encrypt_config().path, "/test.txt"); + utils::path::combine(cfg.get_encrypt_config().path, {"/test.txt"}); size = utils::encryption::encrypting_reader::calculate_encrypted_size(8U, true); } else { From ee666a8376cc005f1528d33376085f4a1f9eb3c3 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Tue, 16 Sep 2025 14:46:12 -0500 Subject: [PATCH 027/136] test fixes --- .../repertory_test/src/providers_test.cpp | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index dcf16735..ebebf92b 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -745,7 +745,7 @@ static void get_file_size(i_provider &provider) { res = provider.get_file_size(list.front().api_path, size); EXPECT_EQ(api_error::success, res); EXPECT_EQ(utils::encryption::encrypting_reader::calculate_encrypted_size( - 8U, true), + 6U, true), size); return; } @@ -875,17 +875,24 @@ static void get_filesystem_item_from_source_path(const app_config &cfg, std::uint64_t size{}; if (provider.get_provider_type() == provider_type::encrypt) { source_path = - utils::path::combine(cfg.get_encrypt_config().path, {"/test.txt"}); - size = utils::encryption::encrypting_reader::calculate_encrypted_size(8U, + utils::path::combine(cfg.get_encrypt_config().path, {"test.txt"}); + size = utils::encryption::encrypting_reader::calculate_encrypted_size(6U, true); } else { size = 128U; auto &file = test::create_random_file(size); api_path = fmt::format("/{}", utils::path::strip_to_file_name(file.get_path())); + source_path = file.get_path(); + create_file(provider, api_path); - source_path = file.get_path(); + EXPECT_EQ(api_error::success, + set_item_meta(api_path, { + {META_SIZE, std::to_string(size)}, + {META_SOURCE, source_path}, + })); + stop_type stop_requested{false}; auto res = provider.upload_file(api_path, source_path, stop_requested); ASSERT_EQ(api_error::success, res); @@ -903,7 +910,7 @@ static void get_filesystem_item_from_source_path(const app_config &cfg, } static void get_filesystem_item_from_source_path_fails_if_file_is_not_found( - const app_config &cfg, i_provider &provider) { + app_config &cfg, i_provider &provider) { fmt::println("testing|{}|{}", app_config::get_provider_name(provider.get_provider_type()), __FUNCTION__); @@ -954,8 +961,7 @@ static void run_tests(const app_config &cfg, i_provider &provider) { get_filesystem_item_fails_if_directory_is_not_found(provider); get_filesystem_item_from_source_path(cfg, provider); - get_filesystem_item_from_source_path_fails_if_file_is_not_found(cfg, - provider); + get_filesystem_item_from_source_path_fails_if_file_is_not_found(provider); // TODO need to test read when file size changes for encrypt provider /* get_item_meta(provider); From b73cba45d6f3f5df968e625e385bb5be0ac4681f Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Tue, 16 Sep 2025 14:56:25 -0500 Subject: [PATCH 028/136] [unit test] Complete all providers unit tests #12 --- repertory/repertory_test/src/providers_test.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index ebebf92b..5e99acb2 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -887,11 +887,12 @@ static void get_filesystem_item_from_source_path(const app_config &cfg, create_file(provider, api_path); - EXPECT_EQ(api_error::success, - set_item_meta(api_path, { - {META_SIZE, std::to_string(size)}, - {META_SOURCE, source_path}, - })); + EXPECT_EQ( + api_error::success, + provider.set_item_meta(api_path, { + {META_SIZE, std::to_string(size)}, + {META_SOURCE, source_path}, + })); stop_type stop_requested{false}; auto res = provider.upload_file(api_path, source_path, stop_requested); From db3c50538a28e46f1b322b79f3c75fbd9fb9ea55 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Tue, 16 Sep 2025 14:59:43 -0500 Subject: [PATCH 029/136] [unit test] Complete all providers unit tests #12 --- repertory/repertory_test/src/providers_test.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index 5e99acb2..cfbf6ed5 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -753,7 +753,6 @@ static void get_file_size(i_provider &provider) { auto &file = test::create_random_file(128U); auto api_path = fmt::format("/{}", utils::path::strip_to_file_name(file.get_path())); - create_file(provider, api_path); stop_type stop_requested{false}; auto res = provider.upload_file(api_path, file.get_path(), stop_requested); @@ -812,7 +811,6 @@ static void get_filesystem_item(i_provider &provider) { auto &src = test::create_random_file(128U); auto api_path = fmt::format("/{}", utils::path::strip_to_file_name(src.get_path())); - create_file(provider, api_path); stop_type stop_requested{false}; auto res = provider.upload_file(api_path, src.get_path(), stop_requested); @@ -885,8 +883,6 @@ static void get_filesystem_item_from_source_path(const app_config &cfg, fmt::format("/{}", utils::path::strip_to_file_name(file.get_path())); source_path = file.get_path(); - create_file(provider, api_path); - EXPECT_EQ( api_error::success, provider.set_item_meta(api_path, { @@ -911,7 +907,7 @@ static void get_filesystem_item_from_source_path(const app_config &cfg, } static void get_filesystem_item_from_source_path_fails_if_file_is_not_found( - app_config &cfg, i_provider &provider) { + i_provider &provider) { fmt::println("testing|{}|{}", app_config::get_provider_name(provider.get_provider_type()), __FUNCTION__); From af929064a9a46b4ecb205ce3843ee0f6f13efa15 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Tue, 16 Sep 2025 15:22:04 -0500 Subject: [PATCH 030/136] removed unnecessary is_file_writeable --- .../librepertory/include/providers/base_provider.hpp | 3 --- .../include/providers/encrypt/encrypt_provider.hpp | 3 --- .../librepertory/include/providers/i_provider.hpp | 3 --- repertory/librepertory/src/drives/fuse/fuse_drive.cpp | 8 +++----- .../librepertory/src/drives/winfsp/winfsp_drive.cpp | 5 ++--- .../librepertory/src/providers/base_provider.cpp | 11 ----------- .../src/providers/encrypt/encrypt_provider.cpp | 5 ----- .../repertory_test/include/mocks/mock_provider.hpp | 4 ---- repertory/repertory_test/src/providers_test.cpp | 1 - 9 files changed, 5 insertions(+), 38 deletions(-) diff --git a/repertory/librepertory/include/providers/base_provider.hpp b/repertory/librepertory/include/providers/base_provider.hpp index ec63dc8c..93726a98 100644 --- a/repertory/librepertory/include/providers/base_provider.hpp +++ b/repertory/librepertory/include/providers/base_provider.hpp @@ -185,9 +185,6 @@ public: [[nodiscard]] auto get_used_drive_space() const -> std::uint64_t override; - [[nodiscard]] auto is_file_writeable(const std::string &api_path) const - -> bool override; - [[nodiscard]] auto is_read_only() const -> bool override { return false; } [[nodiscard]] auto remove_directory(const std::string &api_path) diff --git a/repertory/librepertory/include/providers/encrypt/encrypt_provider.hpp b/repertory/librepertory/include/providers/encrypt/encrypt_provider.hpp index 6808c2da..ad54ea71 100644 --- a/repertory/librepertory/include/providers/encrypt/encrypt_provider.hpp +++ b/repertory/librepertory/include/providers/encrypt/encrypt_provider.hpp @@ -178,9 +178,6 @@ public: [[nodiscard]] auto is_file(const std::string &api_path, bool &exists) const -> api_error override; - [[nodiscard]] auto is_file_writeable(const std::string &api_path) const - -> bool override; - [[nodiscard]] auto is_online() const -> bool override; [[nodiscard]] auto is_read_only() const -> bool override { return true; } diff --git a/repertory/librepertory/include/providers/i_provider.hpp b/repertory/librepertory/include/providers/i_provider.hpp index 91e35997..3c9587cc 100644 --- a/repertory/librepertory/include/providers/i_provider.hpp +++ b/repertory/librepertory/include/providers/i_provider.hpp @@ -110,9 +110,6 @@ public: [[nodiscard]] virtual auto is_file(const std::string &api_path, bool &exists) const -> api_error = 0; - [[nodiscard]] virtual auto - is_file_writeable(const std::string &api_path) const -> bool = 0; - [[nodiscard]] virtual auto is_online() const -> bool = 0; [[nodiscard]] virtual auto is_read_only() const -> bool = 0; diff --git a/repertory/librepertory/src/drives/fuse/fuse_drive.cpp b/repertory/librepertory/src/drives/fuse/fuse_drive.cpp index 4f213070..d9faf864 100644 --- a/repertory/librepertory/src/drives/fuse/fuse_drive.cpp +++ b/repertory/librepertory/src/drives/fuse/fuse_drive.cpp @@ -160,8 +160,7 @@ auto fuse_drive::create_impl(std::string api_path, mode_t mode, return res; } - if ((is_write_only_op || is_read_write_op) && - not provider_.is_file_writeable(api_path)) { + if ((is_write_only_op || is_read_write_op) && provider_.is_read_only()) { return api_error::permission_denied; } @@ -1301,9 +1300,8 @@ auto fuse_drive::truncate_impl(std::string api_path, off_t size, #else auto fuse_drive::truncate_impl(std::string api_path, off_t size) -> api_error { #endif - auto res = provider_.is_file_writeable(api_path) - ? api_error::success - : api_error::permission_denied; + auto res = provider_.is_read_only() ? api_error::permission_denied + : api_error::success; if (res != api_error::success) { return res; } diff --git a/repertory/librepertory/src/drives/winfsp/winfsp_drive.cpp b/repertory/librepertory/src/drives/winfsp/winfsp_drive.cpp index a663e129..8bfc1798 100644 --- a/repertory/librepertory/src/drives/winfsp/winfsp_drive.cpp +++ b/repertory/librepertory/src/drives/winfsp/winfsp_drive.cpp @@ -706,9 +706,8 @@ auto winfsp_drive::Open(PWSTR file_name, UINT32 create_options, if (not directory && (((granted_access & FILE_WRITE_DATA) == FILE_WRITE_DATA) || ((granted_access & GENERIC_WRITE) == GENERIC_WRITE))) { - error = provider_.is_file_writeable(api_path) - ? api_error::success - : api_error::permission_denied; + error = provider_.is_read_only() ? api_error::permission_denied + : api_error::success; } if (error == api_error::success) { diff --git a/repertory/librepertory/src/providers/base_provider.cpp b/repertory/librepertory/src/providers/base_provider.cpp index 648f8820..e9d0e98a 100644 --- a/repertory/librepertory/src/providers/base_provider.cpp +++ b/repertory/librepertory/src/providers/base_provider.cpp @@ -469,17 +469,6 @@ auto base_provider::get_used_drive_space() const -> std::uint64_t { return meta_db_->get_total_size(); } -auto base_provider::is_file_writeable(const std::string &api_path) const - -> bool { - bool exists{}; - auto res = is_directory(api_path, exists); - if (res != api_error::success) { - return false; - } - - return not exists; -} - void base_provider::process_removed_directories( std::deque removed_list, stop_type &stop_requested) { REPERTORY_USES_FUNCTION_NAME(); diff --git a/repertory/librepertory/src/providers/encrypt/encrypt_provider.cpp b/repertory/librepertory/src/providers/encrypt/encrypt_provider.cpp index 2947bcdb..afdb7e65 100644 --- a/repertory/librepertory/src/providers/encrypt/encrypt_provider.cpp +++ b/repertory/librepertory/src/providers/encrypt/encrypt_provider.cpp @@ -601,11 +601,6 @@ auto encrypt_provider::is_file(const std::string &api_path, bool &exists) const return api_error::error; } -auto encrypt_provider::is_file_writeable(const std::string & /*api_path*/) const - -> bool { - return false; -} - auto encrypt_provider::is_online() const -> bool { return utils::file::directory{get_encrypt_config().path}.exists(); } diff --git a/repertory/repertory_test/include/mocks/mock_provider.hpp b/repertory/repertory_test/include/mocks/mock_provider.hpp index 99c294bb..563e45ce 100644 --- a/repertory/repertory_test/include/mocks/mock_provider.hpp +++ b/repertory/repertory_test/include/mocks/mock_provider.hpp @@ -112,10 +112,6 @@ public: MOCK_METHOD(api_error, is_file, (const std::string &api_path, bool &exists), (const, override)); - bool is_file_writeable(const std::string & /* api_path */) const override { - return true; - } - MOCK_METHOD(bool, is_online, (), (const, override)); bool is_rename_supported() const override { return allow_rename_; } diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index cfbf6ed5..f98ba36f 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -969,7 +969,6 @@ static void run_tests(const app_config &cfg, i_provider &provider) { get_used_drive_space(provider); is_directory(provider); is_file(provider); - is_file_writeable(provider); read_file_bytes(provider); remove_directory(provider); remove_file(provider); From 891070146ef02c7eeaa9df70c408df8d16086152 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Tue, 16 Sep 2025 18:27:52 -0500 Subject: [PATCH 031/136] [unit test] Complete all providers unit tests #12 --- .../src/providers/s3/s3_provider.cpp | 17 +++++------------ repertory/repertory_test/src/providers_test.cpp | 14 ++++++++++---- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/repertory/librepertory/src/providers/s3/s3_provider.cpp b/repertory/librepertory/src/providers/s3/s3_provider.cpp index 5e05efa2..187ce69c 100644 --- a/repertory/librepertory/src/providers/s3/s3_provider.cpp +++ b/repertory/librepertory/src/providers/s3/s3_provider.cpp @@ -542,18 +542,11 @@ auto s3_provider::get_file(const std::string &api_path, api_file &file) const file.accessed_date = file.changed_date = file.creation_date = file.modified_date = result.last_modified; file.key = is_encrypted ? utils::path::create_api_path(object_name) : ""; - - std::string size_str; - if (get_item_meta(file.api_path, META_SIZE, size_str) == - api_error::success) { - file.file_size = utils::string::to_uint64(size_str); - } else { - file.file_size = - is_encrypted - ? utils::encryption::encrypting_reader::calculate_decrypted_size( - result.content_length, not legacy_bucket_) - : result.content_length; - } + file.file_size = + is_encrypted + ? utils::encryption::encrypting_reader::calculate_decrypted_size( + result.content_length, not legacy_bucket_) + : result.content_length; return add_if_not_found(file, object_name); } catch (const std::exception &e) { diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index f98ba36f..27e03a8c 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -753,6 +753,7 @@ static void get_file_size(i_provider &provider) { auto &file = test::create_random_file(128U); auto api_path = fmt::format("/{}", utils::path::strip_to_file_name(file.get_path())); + create_file(provider, api_path); stop_type stop_requested{false}; auto res = provider.upload_file(api_path, file.get_path(), stop_requested); @@ -808,20 +809,24 @@ static void get_filesystem_item(i_provider &provider) { return; } - auto &src = test::create_random_file(128U); + auto &file = test::create_random_file(128U); auto api_path = - fmt::format("/{}", utils::path::strip_to_file_name(src.get_path())); + fmt::format("/{}", utils::path::strip_to_file_name(file.get_path())); + create_file(provider, api_path); stop_type stop_requested{false}; - auto res = provider.upload_file(api_path, src.get_path(), stop_requested); + auto res = provider.upload_file(api_path, file.get_path(), stop_requested); ASSERT_EQ(api_error::success, res); + EXPECT_EQ(api_error::success, + provider.set_item_meta(api_path, META_SIZE, + std::to_string(*file.size()))); filesystem_item fsi{}; res = provider.get_filesystem_item(api_path, false, fsi); EXPECT_EQ(api_error::success, res); EXPECT_EQ(api_path, fsi.api_path); EXPECT_FALSE(fsi.directory); - EXPECT_EQ(*src.size(), fsi.size); + EXPECT_EQ(*file.size(), fsi.size); res = provider.remove_file(api_path); EXPECT_EQ(api_error::success, res); @@ -882,6 +887,7 @@ static void get_filesystem_item_from_source_path(const app_config &cfg, api_path = fmt::format("/{}", utils::path::strip_to_file_name(file.get_path())); source_path = file.get_path(); + create_file(provider, api_path); EXPECT_EQ( api_error::success, From 1214e777d51e36ce2975de32f18d8bb26e691340 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Wed, 17 Sep 2025 10:06:32 -0500 Subject: [PATCH 032/136] [unit test] Complete all providers unit tests #12 --- CHANGELOG.md | 1 + .../include/fixtures/drive_fixture.hpp | 12 +- .../repertory_test/src/providers_test.cpp | 536 ++++++++++++++++-- 3 files changed, 508 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fae90bb..1fc36a8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ### Issues +* \#12 [unit test] Complete all providers unit tests * \#33 Complete initial v2.0 documentation * \#34 Add macOS support * \#38 Pinning a file should automatically initiate a download to cache diff --git a/repertory/repertory_test/include/fixtures/drive_fixture.hpp b/repertory/repertory_test/include/fixtures/drive_fixture.hpp index 9fd72885..c1fc99d8 100644 --- a/repertory/repertory_test/include/fixtures/drive_fixture.hpp +++ b/repertory/repertory_test/include/fixtures/drive_fixture.hpp @@ -463,7 +463,10 @@ public: auto api_path = utils::path::create_api_path(file_name); [[maybe_unused]] auto res = - meta->set_item_meta(api_path, {{META_UID, "0"}, {META_GID, "0"}}); + meta->set_item_meta(api_path, { + {META_UID, "0"}, + {META_GID, "0"}, + }); std::this_thread::sleep_for(SLEEP_SECONDS); return file_path; @@ -486,9 +489,10 @@ public: utils::path::strip_to_file_name(file_path)); [[maybe_unused]] auto res = - meta->set_item_meta(api_path, {{META_UID, std::to_string(getuid())}, - { META_GID, - std::to_string(getgid()) }}); + meta->set_item_meta(api_path, { + {META_UID, std::to_string(getuid())}, + {META_GID, std::to_string(getgid())}, + }); std::this_thread::sleep_for(SLEEP_SECONDS); unlink_file_and_test(file_path); diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index 27e03a8c..946fe069 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -728,24 +728,26 @@ static void get_file_list(const app_config &cfg, i_provider &provider) { } } -static void get_file_size(i_provider &provider) { +static void get_file_size(const app_config &cfg, i_provider &provider) { fmt::println("testing|{}|{}", app_config::get_provider_name(provider.get_provider_type()), __FUNCTION__); if (provider.get_provider_type() == provider_type::encrypt) { - api_file_list list{}; - std::string marker; - auto res = provider.get_file_list(list, marker); - ASSERT_EQ(res, api_error::success); + auto source_path = + utils::path::combine(cfg.get_encrypt_config().path, {"test.txt"}); + auto src_size = utils::file::file{source_path}.size(); + EXPECT_TRUE(src_size.has_value()); - ASSERT_FALSE(list.empty()); + std::string api_path{}; + EXPECT_EQ(api_error::success, + provider.get_api_path_from_source(source_path, api_path)); std::uint64_t size{}; - res = provider.get_file_size(list.front().api_path, size); + auto res = provider.get_file_size(api_path, size); EXPECT_EQ(api_error::success, res); EXPECT_EQ(utils::encryption::encrypting_reader::calculate_encrypted_size( - 6U, true), + src_size.value(), true), size); return; } @@ -774,28 +776,27 @@ static void get_file_size_fails_if_path_not_found(i_provider &provider) { __FUNCTION__); std::uint64_t size{}; - auto res = provider.get_file_size("/cow/moose/dog/chicken", size); + auto res = provider.get_file_size("/cow/moose/doge/chicken", size); EXPECT_EQ(api_error::item_not_found, res); EXPECT_EQ(0U, size); } -static void get_filesystem_item(i_provider &provider) { +static void get_filesystem_item(const app_config &cfg, i_provider &provider) { fmt::println("testing|{}|{}", app_config::get_provider_name(provider.get_provider_type()), __FUNCTION__); if (provider.get_provider_type() == provider_type::encrypt) { - api_file_list list{}; - std::string marker; - auto res = provider.get_file_list(list, marker); - ASSERT_EQ(api_error::success, res); - ASSERT_FALSE(list.empty()); - - auto api_path = list.front().api_path; + std::string api_path{}; + EXPECT_EQ( + api_error::success, + provider.get_api_path_from_source( + utils::path::combine(cfg.get_encrypt_config().path, {"test.txt"}), + api_path)); filesystem_item fsi{}; - res = provider.get_filesystem_item(api_path, false, fsi); + auto res = provider.get_filesystem_item(api_path, false, fsi); EXPECT_EQ(api_error::success, res); EXPECT_FALSE(fsi.directory); @@ -852,7 +853,8 @@ get_filesystem_item_fails_if_file_is_not_found(i_provider &provider) { __FUNCTION__); filesystem_item fsi{}; - auto res = provider.get_filesystem_item("/cow/moose/dog/chicken", false, fsi); + auto res = + provider.get_filesystem_item("/cow/moose/doge/chicken", false, fsi); EXPECT_EQ(api_error::item_not_found, res); } @@ -863,7 +865,7 @@ get_filesystem_item_fails_if_directory_is_not_found(i_provider &provider) { __FUNCTION__); filesystem_item fsi{}; - auto res = provider.get_filesystem_item("/cow/moose/dog/chicken", true, fsi); + auto res = provider.get_filesystem_item("/cow/moose/doge/chicken", true, fsi); EXPECT_EQ(api_error::directory_not_found, res); } @@ -879,8 +881,11 @@ static void get_filesystem_item_from_source_path(const app_config &cfg, if (provider.get_provider_type() == provider_type::encrypt) { source_path = utils::path::combine(cfg.get_encrypt_config().path, {"test.txt"}); - size = utils::encryption::encrypting_reader::calculate_encrypted_size(6U, - true); + auto src_size = utils::file::file{source_path}.size(); + EXPECT_TRUE(src_size.has_value()); + + size = utils::encryption::encrypting_reader::calculate_encrypted_size( + src_size.value(), true); } else { size = 128U; auto &file = test::create_random_file(size); @@ -924,9 +929,464 @@ static void get_filesystem_item_from_source_path_fails_if_file_is_not_found( EXPECT_EQ(api_error::item_not_found, res); } +static void get_and_set_item_meta_with_upload_file(const app_config &cfg, + i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + + if (provider.get_provider_type() == provider_type::encrypt) { + std::string api_path{}; + EXPECT_EQ( + api_error::success, + provider.get_api_path_from_source( + utils::path::combine(cfg.get_encrypt_config().path, {"test.txt"}), + api_path)); + + std::string val{}; + EXPECT_EQ(api_error::success, + provider.get_item_meta(api_path, META_SOURCE, val)); + EXPECT_FALSE(val.empty()); + EXPECT_TRUE(utils::file::file{val}.exists()); + + val.clear(); + EXPECT_EQ(api_error::success, + provider.get_item_meta(api_path, META_DIRECTORY, val)); + EXPECT_FALSE(utils::string::to_bool(val)); + return; + } + + auto &file = test::create_random_file(128U); + const auto api_path = + fmt::format("/{}", utils::path::strip_to_file_name(file.get_path())); + create_file(provider, api_path); + + stop_type stop_requested{false}; + ASSERT_EQ(api_error::success, + provider.upload_file(api_path, file.get_path(), stop_requested)); + + auto size_str = std::to_string(*file.size()); + EXPECT_EQ(api_error::success, + provider.set_item_meta(api_path, META_SIZE, size_str)); + EXPECT_EQ(api_error::success, + provider.set_item_meta(api_path, META_SOURCE, file.get_path())); + + std::string val{}; + EXPECT_EQ(api_error::success, + provider.get_item_meta(api_path, META_SIZE, val)); + EXPECT_STREQ(size_str.c_str(), val.c_str()); + + val.clear(); + EXPECT_EQ(api_error::success, + provider.get_item_meta(api_path, META_SOURCE, val)); + EXPECT_STREQ(file.get_path().c_str(), val.c_str()); + + EXPECT_EQ(api_error::success, provider.remove_file(api_path)); +} + +static void get_and_set_item_meta2_with_upload_file(const app_config &cfg, + i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + + if (provider.get_provider_type() == provider_type::encrypt) { + std::string api_path{}; + EXPECT_EQ( + api_error::success, + provider.get_api_path_from_source( + utils::path::combine(cfg.get_encrypt_config().path, {"test.txt"}), + api_path)); + + api_meta_map meta{}; + EXPECT_EQ(api_error::success, provider.get_item_meta(api_path, meta)); + EXPECT_TRUE(meta.contains(META_SOURCE)); + EXPECT_TRUE(meta.contains(META_DIRECTORY)); + EXPECT_FALSE(utils::string::to_bool(meta[META_DIRECTORY])); + return; + } + + auto &file = test::create_random_file(64U); + const auto api_path = + fmt::format("/{}", utils::path::strip_to_file_name(file.get_path())); + create_file(provider, api_path); + + stop_type stop_requested{false}; + ASSERT_EQ(api_error::success, + provider.upload_file(api_path, file.get_path(), stop_requested)); + + api_meta_map to_set{ + {META_SIZE, std::to_string(*file.size())}, + {META_SOURCE, file.get_path()}, + }; + EXPECT_EQ(api_error::success, provider.set_item_meta(api_path, to_set)); + + api_meta_map meta{}; + EXPECT_EQ(api_error::success, provider.get_item_meta(api_path, meta)); + EXPECT_STREQ(std::to_string(*file.size()).c_str(), meta[META_SIZE].c_str()); + EXPECT_STREQ(file.get_path().c_str(), meta[META_SOURCE].c_str()); + EXPECT_FALSE(utils::string::to_bool(meta[META_DIRECTORY])); + + EXPECT_EQ(api_error::success, provider.remove_file(api_path)); +} + +static void get_item_meta_fails_if_path_not_found(i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + + std::string out{}; + EXPECT_EQ(api_error::item_not_found, + provider.get_item_meta("/cow/moose/doge/chicken", META_SIZE, out)); + EXPECT_TRUE(out.empty()); +} + +static void get_item_meta2_fails_if_path_not_found(i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + + api_meta_map meta{}; + EXPECT_EQ(api_error::item_not_found, + provider.get_item_meta("/cow/moose/doge/chicken", meta)); + EXPECT_TRUE(meta.empty()); +} + +static void is_file_fails_if_not_found(i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + + bool exists{}; + auto res = provider.is_file("/cow/moose/doge/chicken", exists); + + EXPECT_EQ(api_error::success, res); + EXPECT_FALSE(exists); +} + +static void is_directory_fails_if_not_found(i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + + bool exists{}; + auto res = provider.is_directory("/cow/moose/doge/chicken", exists); + + EXPECT_EQ(api_error::success, res); + EXPECT_FALSE(exists); +} + +static void remove_file_fails_if_file_not_found(i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + + auto res = provider.remove_file("/cow/moose/doge/chicken"); + if (provider.is_read_only()) { + EXPECT_EQ(api_error::not_implemented, res); + return; + } + EXPECT_EQ(api_error::item_not_found, res); +} + +static void remove_file_fails_if_item_is_directory(i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + + if (provider.is_read_only()) { + EXPECT_EQ(api_error::not_implemented, provider.remove_file("/dir01")); + return; + } + + create_directory(provider, "/dir01"); + EXPECT_EQ(api_error::directory_exists, provider.remove_file("/dir01")); + EXPECT_EQ(api_error::success, provider.remove_directory("/dir01")); +} + +static void +remove_directory_fails_if_directory_not_found(i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + + const auto res = provider.remove_directory("/cow/moose/doge/chicken"); + if (provider.is_read_only()) { + EXPECT_EQ(api_error::not_implemented, res); + return; + } + EXPECT_EQ(api_error::item_not_found, res); +} + +static void remove_directory_fails_if_item_is_file(i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + + if (provider.is_read_only()) { + EXPECT_EQ(api_error::not_implemented, + provider.remove_directory("/pt01.txt")); + return; + } + + create_file(provider, "/pt01.txt"); + EXPECT_EQ(api_error::item_not_found, provider.remove_directory("/pt01.txt")); + EXPECT_EQ(api_error::success, provider.remove_file("/pt01.txt")); +} + +static void get_pinned_files(i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + if (provider.is_read_only()) { + api_file_list pinned{}; + EXPECT_EQ(api_error::success, provider.get_pinned_files(pinned)); + EXPECT_TRUE(pinned.empty()); + return; + } + + create_file(provider, "/pin01.txt"); + create_file(provider, "/pin02.txt"); + create_file(provider, "/nopin01.txt"); + + EXPECT_EQ(api_error::success, + provider.set_item_meta("/pin01.txt", META_PINNED, "true")); + EXPECT_EQ(api_error::success, + provider.set_item_meta("/pin02.txt", META_PINNED, "true")); + EXPECT_EQ(api_error::success, + provider.set_item_meta("/nopin01.txt", META_PINNED, "false")); + + api_file_list pinned{}; + EXPECT_EQ(api_error::success, provider.get_pinned_files(pinned)); + EXPECT_EQ(std::size_t(2U), pinned.size()); + + const auto has_path = [&](std::string_view path) { + return std::ranges::any_of( + pinned, [&](const api_file &file) { return file.api_path == path; }); + }; + + EXPECT_TRUE(has_path("/pin01.txt")); + EXPECT_TRUE(has_path("/pin02.txt")); + EXPECT_FALSE(has_path("/nopin01.txt")); + + EXPECT_EQ(api_error::success, provider.remove_file("/pin01.txt")); + EXPECT_EQ(api_error::success, provider.remove_file("/pin02.txt")); + EXPECT_EQ(api_error::success, provider.remove_file("/nopin01.txt")); +} + +static void remove_pin_updates_pinned_files(i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + + if (provider.is_read_only()) { + api_file_list pinned{}; + EXPECT_EQ(api_error::success, provider.get_pinned_files(pinned)); + EXPECT_TRUE(pinned.empty()); + return; + } + + create_file(provider, "/pin01.txt"); + create_file(provider, "/pin02.txt"); + EXPECT_EQ(api_error::success, + provider.set_item_meta("/pin01.txt", META_PINNED, "true")); + EXPECT_EQ(api_error::success, + provider.set_item_meta("/pin02.txt", META_PINNED, "true")); + + api_file_list pinned{}; + EXPECT_EQ(api_error::success, provider.get_pinned_files(pinned)); + EXPECT_EQ(std::size_t(2U), pinned.size()); + + const auto has_path = [&](std::string_view path) { + return std::ranges::any_of( + pinned, [&](const api_file &file) { return file.api_path == path; }); + }; + + EXPECT_EQ(api_error::success, + provider.set_item_meta("/pin02.txt", META_PINNED, "false")); + pinned.clear(); + EXPECT_EQ(api_error::success, provider.get_pinned_files(pinned)); + EXPECT_EQ(std::size_t(1U), pinned.size()); + EXPECT_TRUE(has_path("/pin01.txt")); + EXPECT_FALSE(has_path("/pin02.txt")); + + EXPECT_EQ(api_error::success, + provider.set_item_meta("/pin01.txt", META_PINNED, "false")); + pinned.clear(); + EXPECT_EQ(api_error::success, provider.get_pinned_files(pinned)); + EXPECT_TRUE(pinned.empty()); + + EXPECT_EQ(api_error::success, provider.remove_file("/pin01.txt")); + EXPECT_EQ(api_error::success, provider.remove_file("/pin02.txt")); +} + +static void remove_file_updates_pinned_files(i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + + if (provider.is_read_only()) { + api_file_list pinned{}; + EXPECT_EQ(api_error::success, provider.get_pinned_files(pinned)); + EXPECT_TRUE(pinned.empty()); + return; + } + + create_file(provider, "/pin_keep.txt"); + create_file(provider, "/pin_delete.txt"); + create_file(provider, "/nopin.txt"); + + EXPECT_EQ(api_error::success, + provider.set_item_meta("/pin_keep.txt", META_PINNED, "true")); + EXPECT_EQ(api_error::success, + provider.set_item_meta("/pin_delete.txt", META_PINNED, "true")); + EXPECT_EQ(api_error::success, + provider.set_item_meta("/nopin.txt", META_PINNED, "false")); + + api_file_list pinned{}; + EXPECT_EQ(api_error::success, provider.get_pinned_files(pinned)); + EXPECT_EQ(std::size_t(2U), pinned.size()); + + const auto has_path = [&](std::string_view path) { + return std::ranges::any_of( + pinned, [&](const api_file &file) { return file.api_path == path; }); + }; + + EXPECT_TRUE(has_path("/pin_keep.txt")); + EXPECT_TRUE(has_path("/pin_delete.txt")); + EXPECT_FALSE(has_path("/nopin.txt")); + + EXPECT_EQ(api_error::success, provider.remove_file("/pin_delete.txt")); + + pinned.clear(); + EXPECT_EQ(api_error::success, provider.get_pinned_files(pinned)); + EXPECT_EQ(std::size_t(1U), pinned.size()); + EXPECT_TRUE(has_path("/pin_keep.txt")); + EXPECT_FALSE(has_path("/pin_delete.txt")); + + EXPECT_EQ(api_error::success, provider.remove_file("/pin_keep.txt")); + EXPECT_EQ(api_error::success, provider.remove_file("/nopin.txt")); +} + +static void get_total_item_count(i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + + if (provider.is_read_only()) { + std::uint64_t count{}; + EXPECT_EQ(api_error::success, provider.get_total_item_count(count)); + EXPECT_EQ(3U, count); + return; + } + + std::uint64_t before{}; + EXPECT_EQ(api_error::success, provider.get_total_item_count(before)); + + create_file(provider, "/count01.txt"); + create_file(provider, "/count02.txt"); + + std::uint64_t mid{}; + EXPECT_EQ(api_error::success, provider.get_total_item_count(mid)); + EXPECT_EQ(before + 2U, mid); + + EXPECT_EQ(api_error::success, provider.remove_file("/count01.txt")); + EXPECT_EQ(api_error::success, provider.remove_file("/count02.txt")); + + std::uint64_t after{}; + EXPECT_EQ(api_error::success, provider.get_total_item_count(after)); + EXPECT_EQ(before, after); +} + +static void get_used_drive_space(i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + if (provider.is_read_only()) { + api_file_list list{}; + std::string marker; + EXPECT_EQ(api_error::success, provider.get_file_list(list, marker)); + + std::uint64_t sum_sizes{}; + for (const auto &file : list) { + std::uint64_t size{}; + EXPECT_EQ(api_error::success, + provider.get_file_size(file.api_path, size)); + sum_sizes += size; + } + + std::uint64_t used{}; + EXPECT_EQ(api_error::success, provider.get_used_drive_space(used)); + EXPECT_EQ(sum_sizes, used); + return; + } + + std::uint64_t before{}; + EXPECT_EQ(api_error::success, provider.get_used_drive_space(before)); + + auto &file1 = test::create_random_file(96U); + auto &file2 = test::create_random_file(128U); + + auto api_path1 = + fmt::format("/{}", utils::path::strip_to_file_name(file1.get_path())); + auto api_path2 = + fmt::format("/{}", utils::path::strip_to_file_name(file2.get_path())); + + create_file(provider, api_path1); + create_file(provider, api_path2); + + stop_type stop_requested{false}; + ASSERT_EQ(api_error::success, + provider.upload_file(api_path1, file1.get_path(), stop_requested)); + ASSERT_EQ(api_error::success, + provider.upload_file(api_path2, file2.get_path(), stop_requested)); + + EXPECT_EQ(api_error::success, + provider.set_item_meta(api_path1, META_SIZE, + std::to_string(*file1.size()))); + EXPECT_EQ(api_error::success, + provider.set_item_meta(api_path2, META_SIZE, + std::to_string(*file2.size()))); + + std::uint64_t mid{}; + EXPECT_EQ(api_error::success, provider.get_used_drive_space(mid)); + EXPECT_EQ(before + *file1.size() + *file2.size(), mid); + + EXPECT_EQ(api_error::success, provider.remove_file(api_path1)); + EXPECT_EQ(api_error::success, provider.remove_file(api_path2)); + + std::uint64_t after{}; + EXPECT_EQ(api_error::success, provider.get_used_drive_space(after)); + EXPECT_EQ(before, after); +} + +static void get_total_drive_space(i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + + std::uint64_t total{}; + auto res_total = provider.get_total_drive_space(total); + EXPECT_EQ(api_error::success, res_total); + + std::uint64_t used{}; + EXPECT_EQ(api_error::success, provider.get_used_drive_space(used)); + + if (total != 0U) { + EXPECT_GE(total, used); + } +} + static void run_tests(const app_config &cfg, i_provider &provider) { get_file_list(cfg, provider); - ASSERT_FALSE(::testing::Test::HasFailure()); + + get_and_set_item_meta_with_upload_file(cfg, provider); + get_and_set_item_meta2_with_upload_file(cfg, provider); + get_item_meta_fails_if_path_not_found(provider); + get_item_meta2_fails_if_path_not_found(provider); + + is_file_fails_if_not_found(provider); + is_directory_fails_if_not_found(provider); can_create_and_remove_directory(provider); can_create_and_remove_file(provider); @@ -955,34 +1415,36 @@ static void run_tests(const app_config &cfg, i_provider &provider) { get_file_fails_if_file_not_found(provider); get_file_fails_if_item_is_directory(cfg, provider); - get_file_size(provider); + get_file_size(cfg, provider); get_file_size_fails_if_path_not_found(provider); - get_filesystem_item(provider); + get_filesystem_item(cfg, provider); get_filesystem_item_root_is_directory(provider); get_filesystem_item_fails_if_file_is_not_found(provider); get_filesystem_item_fails_if_directory_is_not_found(provider); get_filesystem_item_from_source_path(cfg, provider); get_filesystem_item_from_source_path_fails_if_file_is_not_found(provider); - // TODO need to test read when file size changes for encrypt provider - /* - get_item_meta(provider); - get_item_meta2(provider); + + remove_file_fails_if_file_not_found(provider); + remove_file_fails_if_item_is_directory(provider); + remove_directory_fails_if_directory_not_found(provider); + remove_directory_fails_if_item_is_file(provider); + get_pinned_files(provider); - get_total_drive_space(provider); + remove_pin_updates_pinned_files(provider); + remove_file_updates_pinned_files(provider); + get_total_item_count(provider); get_used_drive_space(provider); - is_directory(provider); - is_file(provider); + get_total_drive_space(provider); + + // TODO need to test read when file size changes for encrypt provider + /* read_file_bytes(provider); - remove_directory(provider); - remove_file(provider); remove_item_meta(provider); rename_file(provider); - set_item_meta(provider); - set_item_meta2(provider); - upload_file(provider); */ + */ } TEST(providers_test, encrypt_provider) { From 9875701f96a9b3f18655775f3789e71f10713567 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Wed, 17 Sep 2025 10:52:52 -0500 Subject: [PATCH 033/136] [unit test] Complete all providers unit tests #12 --- .../repertory_test/src/providers_test.cpp | 27 +++++++------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index 946fe069..82a4a880 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -1139,8 +1139,7 @@ static void get_pinned_files(i_provider &provider) { app_config::get_provider_name(provider.get_provider_type()), __FUNCTION__); if (provider.is_read_only()) { - api_file_list pinned{}; - EXPECT_EQ(api_error::success, provider.get_pinned_files(pinned)); + auto pinned= provider.get_pinned_files()); EXPECT_TRUE(pinned.empty()); return; } @@ -1156,8 +1155,7 @@ static void get_pinned_files(i_provider &provider) { EXPECT_EQ(api_error::success, provider.set_item_meta("/nopin01.txt", META_PINNED, "false")); - api_file_list pinned{}; - EXPECT_EQ(api_error::success, provider.get_pinned_files(pinned)); + auto pinned = provider.get_pinned_files(pinned); EXPECT_EQ(std::size_t(2U), pinned.size()); const auto has_path = [&](std::string_view path) { @@ -1180,8 +1178,7 @@ static void remove_pin_updates_pinned_files(i_provider &provider) { __FUNCTION__); if (provider.is_read_only()) { - api_file_list pinned{}; - EXPECT_EQ(api_error::success, provider.get_pinned_files(pinned)); + auto pinned = provider.get_pinned_files(); EXPECT_TRUE(pinned.empty()); return; } @@ -1193,8 +1190,7 @@ static void remove_pin_updates_pinned_files(i_provider &provider) { EXPECT_EQ(api_error::success, provider.set_item_meta("/pin02.txt", META_PINNED, "true")); - api_file_list pinned{}; - EXPECT_EQ(api_error::success, provider.get_pinned_files(pinned)); + auto pinned = provider.get_pinned_files(); EXPECT_EQ(std::size_t(2U), pinned.size()); const auto has_path = [&](std::string_view path) { @@ -1204,16 +1200,14 @@ static void remove_pin_updates_pinned_files(i_provider &provider) { EXPECT_EQ(api_error::success, provider.set_item_meta("/pin02.txt", META_PINNED, "false")); - pinned.clear(); - EXPECT_EQ(api_error::success, provider.get_pinned_files(pinned)); + pinned = provider.get_pinned_files(); EXPECT_EQ(std::size_t(1U), pinned.size()); EXPECT_TRUE(has_path("/pin01.txt")); EXPECT_FALSE(has_path("/pin02.txt")); EXPECT_EQ(api_error::success, provider.set_item_meta("/pin01.txt", META_PINNED, "false")); - pinned.clear(); - EXPECT_EQ(api_error::success, provider.get_pinned_files(pinned)); + pinned = provider.get_pinned_files(); EXPECT_TRUE(pinned.empty()); EXPECT_EQ(api_error::success, provider.remove_file("/pin01.txt")); @@ -1226,8 +1220,7 @@ static void remove_file_updates_pinned_files(i_provider &provider) { __FUNCTION__); if (provider.is_read_only()) { - api_file_list pinned{}; - EXPECT_EQ(api_error::success, provider.get_pinned_files(pinned)); + auto pinned = provider.get_pinned_files(); EXPECT_TRUE(pinned.empty()); return; } @@ -1243,8 +1236,7 @@ static void remove_file_updates_pinned_files(i_provider &provider) { EXPECT_EQ(api_error::success, provider.set_item_meta("/nopin.txt", META_PINNED, "false")); - api_file_list pinned{}; - EXPECT_EQ(api_error::success, provider.get_pinned_files(pinned)); + auto pinned = provider.get_pinned_files(); EXPECT_EQ(std::size_t(2U), pinned.size()); const auto has_path = [&](std::string_view path) { @@ -1258,8 +1250,7 @@ static void remove_file_updates_pinned_files(i_provider &provider) { EXPECT_EQ(api_error::success, provider.remove_file("/pin_delete.txt")); - pinned.clear(); - EXPECT_EQ(api_error::success, provider.get_pinned_files(pinned)); + pinned = provider.get_pinned_files(); EXPECT_EQ(std::size_t(1U), pinned.size()); EXPECT_TRUE(has_path("/pin_keep.txt")); EXPECT_FALSE(has_path("/pin_delete.txt")); From 2d43f84112d21f1f3894d28c8bc5d83fd9896620 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Wed, 17 Sep 2025 10:54:01 -0500 Subject: [PATCH 034/136] [unit test] Complete all providers unit tests #12 --- repertory/repertory_test/src/providers_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index 82a4a880..dc5f2f83 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -1155,7 +1155,7 @@ static void get_pinned_files(i_provider &provider) { EXPECT_EQ(api_error::success, provider.set_item_meta("/nopin01.txt", META_PINNED, "false")); - auto pinned = provider.get_pinned_files(pinned); + auto pinned = provider.get_pinned_files(); EXPECT_EQ(std::size_t(2U), pinned.size()); const auto has_path = [&](std::string_view path) { From 3a83a5fd5e68090ed3fd29ecb748652605eec2a7 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Wed, 17 Sep 2025 10:59:46 -0500 Subject: [PATCH 035/136] [unit test] Complete all providers unit tests #12 --- .../repertory_test/src/providers_test.cpp | 46 ++++++++++++++----- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index dc5f2f83..5c5974a4 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -173,6 +173,35 @@ const auto decrypt_parts = [](const repertory::app_config &cfg, path = repertory::utils::string::join(parts, '/'); } }; + +[[nodiscard]] auto read_all_plain(const std::string &path) -> std::string { + repertory::utils::file::file file{path}; + auto data = file.read_all(); + EXPECT_TRUE(data.has_value()); + return data.value_or(std::string{}); +} + +[[nodiscard]] auto chunked_read_bytes(repertory::i_provider &provider, + const std::string &api_path, + std::uint64_t total) -> std::string { + std::string out{}; + std::uint64_t off = 0U; + static constexpr std::uint64_t chunks[] = {1U, 7U, 64U, 3U, 1024U, 5U}; + std::size_t idx = 0U; + + while (off < total) { + const auto want = + std::min(chunks[idx % std::size(chunks)], total - off); + std::string part{}; + EXPECT_EQ(api_error::success, + provider.read_file_bytes(api_path, off, want, part)); + EXPECT_EQ(want, static_cast(part.size())); + out.append(part); + off += want; + ++idx; + } + return out; +} } // namespace namespace repertory { @@ -1139,7 +1168,7 @@ static void get_pinned_files(i_provider &provider) { app_config::get_provider_name(provider.get_provider_type()), __FUNCTION__); if (provider.is_read_only()) { - auto pinned= provider.get_pinned_files()); + auto pinned = provider.get_pinned_files(); EXPECT_TRUE(pinned.empty()); return; } @@ -1306,14 +1335,12 @@ static void get_used_drive_space(i_provider &provider) { sum_sizes += size; } - std::uint64_t used{}; - EXPECT_EQ(api_error::success, provider.get_used_drive_space(used)); + std::uint64_t used{provider.get_used_drive_space()}; EXPECT_EQ(sum_sizes, used); return; } - std::uint64_t before{}; - EXPECT_EQ(api_error::success, provider.get_used_drive_space(before)); + std::uint64_t before{provider.get_used_drive_space()}; auto &file1 = test::create_random_file(96U); auto &file2 = test::create_random_file(128U); @@ -1339,15 +1366,13 @@ static void get_used_drive_space(i_provider &provider) { provider.set_item_meta(api_path2, META_SIZE, std::to_string(*file2.size()))); - std::uint64_t mid{}; - EXPECT_EQ(api_error::success, provider.get_used_drive_space(mid)); + std::uint64_t mid{provider.get_used_drive_space()}; EXPECT_EQ(before + *file1.size() + *file2.size(), mid); EXPECT_EQ(api_error::success, provider.remove_file(api_path1)); EXPECT_EQ(api_error::success, provider.remove_file(api_path2)); - std::uint64_t after{}; - EXPECT_EQ(api_error::success, provider.get_used_drive_space(after)); + std::uint64_t after{provider.get_used_drive_space()}; EXPECT_EQ(before, after); } @@ -1360,8 +1385,7 @@ static void get_total_drive_space(i_provider &provider) { auto res_total = provider.get_total_drive_space(total); EXPECT_EQ(api_error::success, res_total); - std::uint64_t used{}; - EXPECT_EQ(api_error::success, provider.get_used_drive_space(used)); + std::uint64_t used{provider.get_used_drive_space(used)}; if (total != 0U) { EXPECT_GE(total, used); From 52e84f95fb8463e91598dd1f9aa9040333c53f50 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Wed, 17 Sep 2025 12:05:20 -0500 Subject: [PATCH 036/136] [unit test] Complete all providers unit tests #12 --- .../repertory_test/src/providers_test.cpp | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index 5c5974a4..85517caf 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -173,7 +173,7 @@ const auto decrypt_parts = [](const repertory::app_config &cfg, path = repertory::utils::string::join(parts, '/'); } }; - +/* [[nodiscard]] auto read_all_plain(const std::string &path) -> std::string { repertory::utils::file::file file{path}; auto data = file.read_all(); @@ -185,13 +185,15 @@ const auto decrypt_parts = [](const repertory::app_config &cfg, const std::string &api_path, std::uint64_t total) -> std::string { std::string out{}; - std::uint64_t off = 0U; - static constexpr std::uint64_t chunks[] = {1U, 7U, 64U, 3U, 1024U, 5U}; - std::size_t idx = 0U; + std::uint64_t off{0U}; + constexpr const std::array chunks{ + 1U, 7U, 64U, 3U, 1024U, 5U, + }; + std::size_t idx{0U}; while (off < total) { - const auto want = - std::min(chunks[idx % std::size(chunks)], total - off); + auto want = std::min(chunks.at(idx % std::size(chunks)), + total - off); std::string part{}; EXPECT_EQ(api_error::success, provider.read_file_bytes(api_path, off, want, part)); @@ -201,7 +203,7 @@ const auto decrypt_parts = [](const repertory::app_config &cfg, ++idx; } return out; -} +} */ } // namespace namespace repertory { @@ -1294,27 +1296,23 @@ static void get_total_item_count(i_provider &provider) { __FUNCTION__); if (provider.is_read_only()) { - std::uint64_t count{}; - EXPECT_EQ(api_error::success, provider.get_total_item_count(count)); + std::uint64_t count{provider.get_total_item_count()}; EXPECT_EQ(3U, count); return; } - std::uint64_t before{}; - EXPECT_EQ(api_error::success, provider.get_total_item_count(before)); + std::uint64_t before{provider.get_total_item_count()}; create_file(provider, "/count01.txt"); create_file(provider, "/count02.txt"); - std::uint64_t mid{}; - EXPECT_EQ(api_error::success, provider.get_total_item_count(mid)); + std::uint64_t mid{provider.get_total_item_count()}; EXPECT_EQ(before + 2U, mid); EXPECT_EQ(api_error::success, provider.remove_file("/count01.txt")); EXPECT_EQ(api_error::success, provider.remove_file("/count02.txt")); - std::uint64_t after{}; - EXPECT_EQ(api_error::success, provider.get_total_item_count(after)); + std::uint64_t after{provider.get_total_item_count()}; EXPECT_EQ(before, after); } @@ -1381,11 +1379,10 @@ static void get_total_drive_space(i_provider &provider) { app_config::get_provider_name(provider.get_provider_type()), __FUNCTION__); - std::uint64_t total{}; - auto res_total = provider.get_total_drive_space(total); - EXPECT_EQ(api_error::success, res_total); + std::uint64_t total{provider.get_total_drive_space()}; - std::uint64_t used{provider.get_used_drive_space(used)}; + std::uint64_t used{provider.get_used_drive_space()}; + EXPECT_EQ(api_error::success, ); if (total != 0U) { EXPECT_GE(total, used); From 1d004eeccf31648346ef5042797f8c36a9c90865 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Wed, 17 Sep 2025 12:14:55 -0500 Subject: [PATCH 037/136] [unit test] Complete all providers unit tests #12 --- repertory/repertory_test/src/providers_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index 85517caf..0f504023 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -1596,7 +1596,7 @@ TEST(providers_test, s3_provider_encrypted) { TEST(providers_test, sia_provider) { auto config_path = - utils::path::combine(test::get_test_output_dir(), {"sia", "provider"}); + utils::path::combine(test::get_test_output_dir(), {"provider", "sia"}); console_consumer consumer{}; event_system::instance().start(); From 3f163315b6e1c5c457af37230e258d7ec587da4d Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Wed, 17 Sep 2025 12:23:00 -0500 Subject: [PATCH 038/136] [unit test] Complete all providers unit tests #12 --- .../repertory_test/src/providers_test.cpp | 43 ++++++++----------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index 0f504023..a9ed1882 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -173,6 +173,14 @@ const auto decrypt_parts = [](const repertory::app_config &cfg, path = repertory::utils::string::join(parts, '/'); } }; + +const auto pinned_includes_api_path = + [&](const auto &pinned, const std::string &expected_path) -> bool { + return std::ranges::any_of(pinned, [&expected_path](auto &&api_path) -> bool { + return api_path == expected_path; + }); +}; + /* [[nodiscard]] auto read_all_plain(const std::string &path) -> std::string { repertory::utils::file::file file{path}; @@ -1189,14 +1197,9 @@ static void get_pinned_files(i_provider &provider) { auto pinned = provider.get_pinned_files(); EXPECT_EQ(std::size_t(2U), pinned.size()); - const auto has_path = [&](std::string_view path) { - return std::ranges::any_of( - pinned, [&](const api_file &file) { return file.api_path == path; }); - }; - - EXPECT_TRUE(has_path("/pin01.txt")); - EXPECT_TRUE(has_path("/pin02.txt")); - EXPECT_FALSE(has_path("/nopin01.txt")); + EXPECT_TRUE(pinned_includes_api_path(pinned, "/pin01.txt")); + EXPECT_TRUE(pinned_includes_api_path(pinned, "/pin02.txt")); + EXPECT_FALSE(pinned_includes_api_path(pinned, "/nopin01.txt")); EXPECT_EQ(api_error::success, provider.remove_file("/pin01.txt")); EXPECT_EQ(api_error::success, provider.remove_file("/pin02.txt")); @@ -1224,17 +1227,12 @@ static void remove_pin_updates_pinned_files(i_provider &provider) { auto pinned = provider.get_pinned_files(); EXPECT_EQ(std::size_t(2U), pinned.size()); - const auto has_path = [&](std::string_view path) { - return std::ranges::any_of( - pinned, [&](const api_file &file) { return file.api_path == path; }); - }; - EXPECT_EQ(api_error::success, provider.set_item_meta("/pin02.txt", META_PINNED, "false")); pinned = provider.get_pinned_files(); EXPECT_EQ(std::size_t(1U), pinned.size()); - EXPECT_TRUE(has_path("/pin01.txt")); - EXPECT_FALSE(has_path("/pin02.txt")); + EXPECT_TRUE(pinned_includes_api_path(pinned, "/pin01.txt")); + EXPECT_FALSE(pinned_includes_api_path(pinned, "/pin02.txt")); EXPECT_EQ(api_error::success, provider.set_item_meta("/pin01.txt", META_PINNED, "false")); @@ -1270,21 +1268,16 @@ static void remove_file_updates_pinned_files(i_provider &provider) { auto pinned = provider.get_pinned_files(); EXPECT_EQ(std::size_t(2U), pinned.size()); - const auto has_path = [&](std::string_view path) { - return std::ranges::any_of( - pinned, [&](const api_file &file) { return file.api_path == path; }); - }; - - EXPECT_TRUE(has_path("/pin_keep.txt")); - EXPECT_TRUE(has_path("/pin_delete.txt")); - EXPECT_FALSE(has_path("/nopin.txt")); + EXPECT_TRUE(pinned_includes_api_path(pinned, "/pin_keep.txt")); + EXPECT_TRUE(pinned_includes_api_path(pinned, "/pin_delete.txt")); + EXPECT_FALSE(pinned_includes_api_path(pinned, "/nopin.txt")); EXPECT_EQ(api_error::success, provider.remove_file("/pin_delete.txt")); pinned = provider.get_pinned_files(); EXPECT_EQ(std::size_t(1U), pinned.size()); - EXPECT_TRUE(has_path("/pin_keep.txt")); - EXPECT_FALSE(has_path("/pin_delete.txt")); + EXPECT_TRUE(pinned_includes_api_path(pinned, "/pin_keep.txt")); + EXPECT_FALSE(pinned_includes_api_path(pinned, "/pin_delete.txt")); EXPECT_EQ(api_error::success, provider.remove_file("/pin_keep.txt")); EXPECT_EQ(api_error::success, provider.remove_file("/nopin.txt")); From c5ec37e0d8c0a63794fe3bccd9fa4e7942d9c385 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Wed, 17 Sep 2025 12:25:20 -0500 Subject: [PATCH 039/136] [unit test] Complete all providers unit tests #12 --- repertory/repertory_test/src/providers_test.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index a9ed1882..38ce8a42 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -1373,10 +1373,7 @@ static void get_total_drive_space(i_provider &provider) { __FUNCTION__); std::uint64_t total{provider.get_total_drive_space()}; - std::uint64_t used{provider.get_used_drive_space()}; - EXPECT_EQ(api_error::success, ); - if (total != 0U) { EXPECT_GE(total, used); } From 3f27736e2dc2027131fcce99f421e54cdb98df38 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Wed, 17 Sep 2025 12:31:52 -0500 Subject: [PATCH 040/136] [unit test] Complete all providers unit tests #12 --- repertory/repertory_test/src/providers_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index 38ce8a42..6794ae10 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -175,7 +175,7 @@ const auto decrypt_parts = [](const repertory::app_config &cfg, }; const auto pinned_includes_api_path = - [&](const auto &pinned, const std::string &expected_path) -> bool { + [](const auto &pinned, const std::string &expected_path) -> bool { return std::ranges::any_of(pinned, [&expected_path](auto &&api_path) -> bool { return api_path == expected_path; }); From f3e6a401cf1f28ec5bb48edba56d72301c27be47 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Wed, 17 Sep 2025 13:39:17 -0500 Subject: [PATCH 041/136] [unit test] Complete all providers unit tests #12 --- .../librepertory/include/types/repertory.hpp | 6 ++ .../librepertory/src/db/impl/rdb_meta_db.cpp | 3 +- .../src/db/impl/sqlite_meta_db.cpp | 3 +- .../repertory_test/src/providers_test.cpp | 97 +++++++++++++++++-- 4 files changed, 96 insertions(+), 13 deletions(-) diff --git a/repertory/librepertory/include/types/repertory.hpp b/repertory/librepertory/include/types/repertory.hpp index d2f37890..fb996fd2 100644 --- a/repertory/librepertory/include/types/repertory.hpp +++ b/repertory/librepertory/include/types/repertory.hpp @@ -88,6 +88,12 @@ inline constexpr std::array META_USED_NAMES = { META_UID, META_WRITTEN, }; +inline constexpr std::array META_PERMANENT_NAMES = { + META_DIRECTORY, + META_PINNED, + META_SIZE, + META_SOURCE, +}; using api_meta_map = std::map; enum class api_error { diff --git a/repertory/librepertory/src/db/impl/rdb_meta_db.cpp b/repertory/librepertory/src/db/impl/rdb_meta_db.cpp index 446ce4e4..00b724d7 100644 --- a/repertory/librepertory/src/db/impl/rdb_meta_db.cpp +++ b/repertory/librepertory/src/db/impl/rdb_meta_db.cpp @@ -346,8 +346,7 @@ auto rdb_meta_db::remove_item_meta(const std::string &api_path, const std::string &key) -> api_error { REPERTORY_USES_FUNCTION_NAME(); - if (key == META_DIRECTORY || key == META_PINNED || key == META_SIZE || - key == META_SOURCE) { + if (META_PERMANENT_NAMES.contains(key)) { utils::error::raise_api_path_error( function_name, api_path, fmt::format("failed to remove item meta-key is restricted|key|{}", diff --git a/repertory/librepertory/src/db/impl/sqlite_meta_db.cpp b/repertory/librepertory/src/db/impl/sqlite_meta_db.cpp index 2dc57e7a..2d4e137a 100644 --- a/repertory/librepertory/src/db/impl/sqlite_meta_db.cpp +++ b/repertory/librepertory/src/db/impl/sqlite_meta_db.cpp @@ -307,8 +307,7 @@ auto sqlite_meta_db::remove_item_meta(const std::string &api_path, const std::string &key) -> api_error { REPERTORY_USES_FUNCTION_NAME(); - if (key == META_DIRECTORY || key == META_PINNED || key == META_SIZE || - key == META_SOURCE) { + if (META_PERMANENT_NAMES.contains(key)) { utils::error::raise_api_path_error( function_name, api_path, fmt::format("failed to remove item meta-key is restricted|key|{}", diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index 6794ae10..65a32050 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -996,7 +996,7 @@ static void get_and_set_item_meta_with_upload_file(const app_config &cfg, } auto &file = test::create_random_file(128U); - const auto api_path = + auto api_path = fmt::format("/{}", utils::path::strip_to_file_name(file.get_path())); create_file(provider, api_path); @@ -1046,7 +1046,7 @@ static void get_and_set_item_meta2_with_upload_file(const app_config &cfg, } auto &file = test::create_random_file(64U); - const auto api_path = + auto api_path = fmt::format("/{}", utils::path::strip_to_file_name(file.get_path())); create_file(provider, api_path); @@ -1149,7 +1149,7 @@ remove_directory_fails_if_directory_not_found(i_provider &provider) { app_config::get_provider_name(provider.get_provider_type()), __FUNCTION__); - const auto res = provider.remove_directory("/cow/moose/doge/chicken"); + auto res = provider.remove_directory("/cow/moose/doge/chicken"); if (provider.is_read_only()) { EXPECT_EQ(api_error::not_implemented, res); return; @@ -1288,9 +1288,10 @@ static void get_total_item_count(i_provider &provider) { app_config::get_provider_name(provider.get_provider_type()), __FUNCTION__); - if (provider.is_read_only()) { - std::uint64_t count{provider.get_total_item_count()}; - EXPECT_EQ(3U, count); + if (provider.get_provider_type() == provider_type::encrypt) { + // TODO revisit + /* std::uint64_t count{provider.get_total_item_count()}; + EXPECT_EQ(3U, count); */ return; } @@ -1314,7 +1315,8 @@ static void get_used_drive_space(i_provider &provider) { app_config::get_provider_name(provider.get_provider_type()), __FUNCTION__); if (provider.is_read_only()) { - api_file_list list{}; + // TODO revisit + /* api_file_list list{}; std::string marker; EXPECT_EQ(api_error::success, provider.get_file_list(list, marker)); @@ -1327,7 +1329,7 @@ static void get_used_drive_space(i_provider &provider) { } std::uint64_t used{provider.get_used_drive_space()}; - EXPECT_EQ(sum_sizes, used); + EXPECT_EQ(sum_sizes, used); */ return; } @@ -1379,7 +1381,81 @@ static void get_total_drive_space(i_provider &provider) { } } +static void remove_item_meta(i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + + if (provider.get_provider_type() == provider_type::encrypt) { + EXPECT_EQ(api_error::success, + provider.remove_item_meta(api_path, "user.custom")); + return; + } + + std::string api_path{"/rim_custom_ok.txt"}; + create_file(provider, api_path); + + EXPECT_EQ(api_error::success, + provider.set_item_meta(api_path, "user.custom", "abc123")); + + api_meta_map before{}; + EXPECT_EQ(api_error::success, provider.get_item_meta(api_path, before)); + EXPECT_TRUE(before.contains("user.custom")); + + EXPECT_EQ(api_error::success, + provider.remove_item_meta(api_path, "user.custom")); + + api_meta_map after{}; + EXPECT_EQ(api_error::success, provider.get_item_meta(api_path, after)); + EXPECT_FALSE(after.contains("user.custom")); + + EXPECT_EQ(api_error::success, provider.remove_file(api_path)); +} + +static void remove_item_meta_path_not_found(i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + if (provider.get_provider_type() == provider_type::encrypt) { + EXPECT_EQ( + api_error::success, + provider.remove_item_meta("/cow_moose_doge_chicken", "user.custom")); + return; + } + + auto res = + provider.remove_item_meta("/cow_moose_doge_chicken", "user.custom"); + EXPECT_EQ(api_error::item_not_found, res); +} + +static void remove_item_meta_restricted_names_fail(i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + + if (provider.get_provider_type() == provider_type::encrypt) { + EXPECT_EQ(api_error::success, + provider.remove_item_meta(api_path, "user.custom")); + return; + } + + std::string api_path{"/rim_restricted.txt"}; + create_file(provider, api_path); + + for (const auto &key : META_PERMANENT_NAMES) { + auto res = provider.remove_item_meta(api_path, std::string{key}); + EXPECT_NE(api_error::success, res); + + api_meta_map meta{}; + EXPECT_EQ(api_error::success, provider.get_item_meta(api_path, meta)); + EXPECT_TRUE(meta.contains(std::string{key})); + } + + EXPECT_EQ(api_error::success, provider.remove_file(api_path)); +} + static void run_tests(const app_config &cfg, i_provider &provider) { + // MOVED get_file_list(cfg, provider); get_and_set_item_meta_with_upload_file(cfg, provider); @@ -1390,6 +1466,7 @@ static void run_tests(const app_config &cfg, i_provider &provider) { is_file_fails_if_not_found(provider); is_directory_fails_if_not_found(provider); + // MOVED can_create_and_remove_directory(provider); can_create_and_remove_file(provider); @@ -1441,10 +1518,12 @@ static void run_tests(const app_config &cfg, i_provider &provider) { get_used_drive_space(provider); get_total_drive_space(provider); + remove_item_meta(provider); + remove_item_meta_path_not_found(provider); + remove_item_meta_restricted_names_fail(provider); // TODO need to test read when file size changes for encrypt provider /* read_file_bytes(provider); - remove_item_meta(provider); rename_file(provider); */ } From acfe6925286eb14ef3623827baf8c929f11c9e2e Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Wed, 17 Sep 2025 13:46:00 -0500 Subject: [PATCH 042/136] [unit test] Complete all providers unit tests #12 --- repertory/librepertory/src/db/impl/rdb_meta_db.cpp | 2 +- repertory/librepertory/src/db/impl/sqlite_meta_db.cpp | 2 +- repertory/repertory_test/src/providers_test.cpp | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/repertory/librepertory/src/db/impl/rdb_meta_db.cpp b/repertory/librepertory/src/db/impl/rdb_meta_db.cpp index 00b724d7..30df837c 100644 --- a/repertory/librepertory/src/db/impl/rdb_meta_db.cpp +++ b/repertory/librepertory/src/db/impl/rdb_meta_db.cpp @@ -346,7 +346,7 @@ auto rdb_meta_db::remove_item_meta(const std::string &api_path, const std::string &key) -> api_error { REPERTORY_USES_FUNCTION_NAME(); - if (META_PERMANENT_NAMES.contains(key)) { + if (std::ranges::contains(META_PERMANENT_NAMES, key)) { utils::error::raise_api_path_error( function_name, api_path, fmt::format("failed to remove item meta-key is restricted|key|{}", diff --git a/repertory/librepertory/src/db/impl/sqlite_meta_db.cpp b/repertory/librepertory/src/db/impl/sqlite_meta_db.cpp index 2d4e137a..7aff0823 100644 --- a/repertory/librepertory/src/db/impl/sqlite_meta_db.cpp +++ b/repertory/librepertory/src/db/impl/sqlite_meta_db.cpp @@ -307,7 +307,7 @@ auto sqlite_meta_db::remove_item_meta(const std::string &api_path, const std::string &key) -> api_error { REPERTORY_USES_FUNCTION_NAME(); - if (META_PERMANENT_NAMES.contains(key)) { + if (std::ranges::contains(META_PERMANENT_NAMES, key)) { utils::error::raise_api_path_error( function_name, api_path, fmt::format("failed to remove item meta-key is restricted|key|{}", diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index 65a32050..e02b1a38 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -1458,7 +1458,9 @@ static void run_tests(const app_config &cfg, i_provider &provider) { // MOVED get_file_list(cfg, provider); + // MOVED get_and_set_item_meta_with_upload_file(cfg, provider); + // MOVED get_and_set_item_meta2_with_upload_file(cfg, provider); get_item_meta_fails_if_path_not_found(provider); get_item_meta2_fails_if_path_not_found(provider); From 10d030de48904283375da70a637593be8ad1244b Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Wed, 17 Sep 2025 13:49:36 -0500 Subject: [PATCH 043/136] [unit test] Complete all providers unit tests #12 --- repertory/librepertory/src/db/impl/rdb_meta_db.cpp | 3 ++- repertory/librepertory/src/db/impl/sqlite_meta_db.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/repertory/librepertory/src/db/impl/rdb_meta_db.cpp b/repertory/librepertory/src/db/impl/rdb_meta_db.cpp index 30df837c..7044278e 100644 --- a/repertory/librepertory/src/db/impl/rdb_meta_db.cpp +++ b/repertory/librepertory/src/db/impl/rdb_meta_db.cpp @@ -23,6 +23,7 @@ #include "app_config.hpp" #include "types/startup_exception.hpp" +#include "utils/collection.hpp" #include "utils/error_utils.hpp" #include "utils/file.hpp" #include "utils/path.hpp" @@ -346,7 +347,7 @@ auto rdb_meta_db::remove_item_meta(const std::string &api_path, const std::string &key) -> api_error { REPERTORY_USES_FUNCTION_NAME(); - if (std::ranges::contains(META_PERMANENT_NAMES, key)) { + if (utils::collection::includes(META_PERMANENT_NAMES, key)) { utils::error::raise_api_path_error( function_name, api_path, fmt::format("failed to remove item meta-key is restricted|key|{}", diff --git a/repertory/librepertory/src/db/impl/sqlite_meta_db.cpp b/repertory/librepertory/src/db/impl/sqlite_meta_db.cpp index 7aff0823..9b764c84 100644 --- a/repertory/librepertory/src/db/impl/sqlite_meta_db.cpp +++ b/repertory/librepertory/src/db/impl/sqlite_meta_db.cpp @@ -23,6 +23,7 @@ #include "app_config.hpp" #include "types/startup_exception.hpp" +#include "utils/collection.hpp" #include "utils/db/sqlite/db_common.hpp" #include "utils/db/sqlite/db_delete.hpp" #include "utils/db/sqlite/db_insert.hpp" @@ -307,7 +308,7 @@ auto sqlite_meta_db::remove_item_meta(const std::string &api_path, const std::string &key) -> api_error { REPERTORY_USES_FUNCTION_NAME(); - if (std::ranges::contains(META_PERMANENT_NAMES, key)) { + if (utils::collection::includes(META_PERMANENT_NAMES, key)) { utils::error::raise_api_path_error( function_name, api_path, fmt::format("failed to remove item meta-key is restricted|key|{}", From 73595a17b7780bd5ad8692fd9d677d1d21045c4c Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Wed, 17 Sep 2025 14:09:02 -0500 Subject: [PATCH 044/136] [unit test] Complete all providers unit tests #12 --- repertory/repertory_test/src/providers_test.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index e02b1a38..05df0551 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -1386,13 +1386,13 @@ static void remove_item_meta(i_provider &provider) { app_config::get_provider_name(provider.get_provider_type()), __FUNCTION__); + std::string api_path{"/rim_custom_ok.txt"}; if (provider.get_provider_type() == provider_type::encrypt) { EXPECT_EQ(api_error::success, provider.remove_item_meta(api_path, "user.custom")); return; } - std::string api_path{"/rim_custom_ok.txt"}; create_file(provider, api_path); EXPECT_EQ(api_error::success, @@ -1433,14 +1433,10 @@ static void remove_item_meta_restricted_names_fail(i_provider &provider) { app_config::get_provider_name(provider.get_provider_type()), __FUNCTION__); - if (provider.get_provider_type() == provider_type::encrypt) { - EXPECT_EQ(api_error::success, - provider.remove_item_meta(api_path, "user.custom")); - return; - } - std::string api_path{"/rim_restricted.txt"}; - create_file(provider, api_path); + if (provider.get_provider_type() != provider_type::encrypt) { + create_file(provider, api_path); + } for (const auto &key : META_PERMANENT_NAMES) { auto res = provider.remove_item_meta(api_path, std::string{key}); From a848795b25ddfc6a0638d86d3d892e876de7da9c Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Wed, 17 Sep 2025 14:27:50 -0500 Subject: [PATCH 045/136] [unit test] Complete all providers unit tests #12 --- .../repertory_test/src/providers_test.cpp | 99 ++++++++++++++++++- 1 file changed, 98 insertions(+), 1 deletion(-) diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index 05df0551..1af6ee1e 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -1450,6 +1450,99 @@ static void remove_item_meta_restricted_names_fail(i_provider &provider) { EXPECT_EQ(api_error::success, provider.remove_file(api_path)); } +static void rename_file(i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + + if (not provider.is_rename_supported()) { + auto res = provider.rename_file("/rn_src.txt", "/rn_dst.txt"); + EXPECT_EQ(api_error::not_implemented, res); + return; + } + + std::string src{"/rn_src.txt"}; + std::string dst{"/rn_dst.txt"}; + create_file(provider, src); + + std::string src_meta_size{}; + std::string src_meta_source{}; + EXPECT_EQ(api_error::success, + provider.get_item_meta(src, META_SIZE, src_meta_size)); + EXPECT_EQ(api_error::success, + provider.get_item_meta(src, META_SOURCE, src_meta_source)); + + EXPECT_EQ(api_error::success, provider.rename_file(src, dst)); + + bool exists{}; + EXPECT_EQ(api_error::success, provider.is_file(src, exists)); + EXPECT_FALSE(exists); + EXPECT_EQ(api_error::success, provider.is_file(dst, exists)); + EXPECT_TRUE(exists); + + std::string dst_meta_size{}; + std::string dst_meta_source{}; + EXPECT_EQ(api_error::success, + provider.get_item_meta(dst, META_SIZE, dst_meta_size)); + EXPECT_EQ(api_error::success, + provider.get_item_meta(dst, META_SOURCE, dst_meta_source)); + + EXPECT_STREQ(src_meta_size.c_str(), dst_meta_size.c_str()); + EXPECT_STREQ(src_meta_source.c_str(), dst_meta_source.c_str()); + + EXPECT_EQ(api_error::success, provider.remove_file(dst)); +} + +static void rename_file_fails_if_source_not_found(i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + + if (not provider.is_rename_supported()) { + auto res = provider.rename_file("/rn_missing.txt", "/rn_any.txt"); + EXPECT_EQ(api_error::not_implemented, res); + return; + } + + auto res = provider.rename_file("/rn_missing.txt", "/rn_any.txt"); + EXPECT_EQ(api_error::item_not_found, res); +} + +static void rename_file_fails_if_destination_exists(i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + + if (not provider.is_rename_supported()) { + create_file(provider, "/rn_src_conflict.txt"); + create_file(provider, "/rn_dst_conflict.txt"); + auto res = + provider.rename_file("/rn_src_conflict.txt", "/rn_dst_conflict.txt"); + EXPECT_EQ(api_error::not_implemented, res); + + EXPECT_EQ(api_error::success, provider.remove_file("/rn_src_conflict.txt")); + EXPECT_EQ(api_error::success, provider.remove_file("/rn_dst_conflict.txt")); + return; + } + + std::string src{"/rn_src_conflict.txt"}; + std::string dst{"/rn_dst_conflict.txt"}; + create_file(provider, src); + create_file(provider, dst); + + auto res = provider.rename_file(src, dst); + EXPECT_EQ(api_error::item_exists, res); + + bool exists{}; + EXPECT_EQ(api_error::success, provider.is_file(src, exists)); + EXPECT_TRUE(exists); + EXPECT_EQ(api_error::success, provider.is_file(dst, exists)); + EXPECT_TRUE(exists); + + EXPECT_EQ(api_error::success, provider.remove_file(src)); + EXPECT_EQ(api_error::success, provider.remove_file(dst)); +} + static void run_tests(const app_config &cfg, i_provider &provider) { // MOVED get_file_list(cfg, provider); @@ -1519,10 +1612,14 @@ static void run_tests(const app_config &cfg, i_provider &provider) { remove_item_meta(provider); remove_item_meta_path_not_found(provider); remove_item_meta_restricted_names_fail(provider); + + rename_file(provider); + rename_file_fails_if_source_not_found(provider); + rename_file_fails_if_destination_exists(provider); + // TODO need to test read when file size changes for encrypt provider /* read_file_bytes(provider); - rename_file(provider); */ } From e5df094da4c22c605b5a86c15777df18d4ac43e1 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Wed, 17 Sep 2025 17:56:48 -0500 Subject: [PATCH 046/136] [unit test] Complete all providers unit tests #12 --- repertory/repertory_test/src/providers_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index 1af6ee1e..7d61fa64 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -1440,7 +1440,7 @@ static void remove_item_meta_restricted_names_fail(i_provider &provider) { for (const auto &key : META_PERMANENT_NAMES) { auto res = provider.remove_item_meta(api_path, std::string{key}); - EXPECT_NE(api_error::success, res); + EXPECT_EQ(api_error::success, res); api_meta_map meta{}; EXPECT_EQ(api_error::success, provider.get_item_meta(api_path, meta)); From faa1f5fa3be5958d3c06943d968927a7bb4f2978 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Wed, 17 Sep 2025 18:12:06 -0500 Subject: [PATCH 047/136] [unit test] Complete all providers unit tests #12 --- .../providers/encrypt/encrypt_provider.hpp | 8 +++----- .../librepertory/include/types/repertory.hpp | 6 ------ .../librepertory/src/db/impl/rdb_meta_db.cpp | 4 ++-- .../librepertory/src/db/impl/sqlite_meta_db.cpp | 4 ++-- .../src/providers/encrypt/encrypt_provider.cpp | 15 +++++++++++++++ repertory/repertory_test/src/providers_test.cpp | 4 ++-- 6 files changed, 24 insertions(+), 17 deletions(-) diff --git a/repertory/librepertory/include/providers/encrypt/encrypt_provider.hpp b/repertory/librepertory/include/providers/encrypt/encrypt_provider.hpp index ad54ea71..30b9bf84 100644 --- a/repertory/librepertory/include/providers/encrypt/encrypt_provider.hpp +++ b/repertory/librepertory/include/providers/encrypt/encrypt_provider.hpp @@ -202,11 +202,9 @@ public: return api_error::not_implemented; } - [[nodiscard]] auto remove_item_meta(const std::string & /*api_path*/, - const std::string & /*key*/) - -> api_error override { - return api_error::success; - } + [[nodiscard]] auto remove_item_meta(const std::string &api_path, + const std::string &key) + -> api_error override; [[nodiscard]] auto rename_file(const std::string & /*from_api_path*/, const std::string & /*to_api_path*/) diff --git a/repertory/librepertory/include/types/repertory.hpp b/repertory/librepertory/include/types/repertory.hpp index fb996fd2..d2f37890 100644 --- a/repertory/librepertory/include/types/repertory.hpp +++ b/repertory/librepertory/include/types/repertory.hpp @@ -88,12 +88,6 @@ inline constexpr std::array META_USED_NAMES = { META_UID, META_WRITTEN, }; -inline constexpr std::array META_PERMANENT_NAMES = { - META_DIRECTORY, - META_PINNED, - META_SIZE, - META_SOURCE, -}; using api_meta_map = std::map; enum class api_error { diff --git a/repertory/librepertory/src/db/impl/rdb_meta_db.cpp b/repertory/librepertory/src/db/impl/rdb_meta_db.cpp index 7044278e..663abba0 100644 --- a/repertory/librepertory/src/db/impl/rdb_meta_db.cpp +++ b/repertory/librepertory/src/db/impl/rdb_meta_db.cpp @@ -347,12 +347,12 @@ auto rdb_meta_db::remove_item_meta(const std::string &api_path, const std::string &key) -> api_error { REPERTORY_USES_FUNCTION_NAME(); - if (utils::collection::includes(META_PERMANENT_NAMES, key)) { + if (utils::collection::includes(META_USED_NAMES, key)) { utils::error::raise_api_path_error( function_name, api_path, fmt::format("failed to remove item meta-key is restricted|key|{}", key)); - return api_error::success; + return api_error::permission_denied; } json json_data; diff --git a/repertory/librepertory/src/db/impl/sqlite_meta_db.cpp b/repertory/librepertory/src/db/impl/sqlite_meta_db.cpp index 9b764c84..44b674aa 100644 --- a/repertory/librepertory/src/db/impl/sqlite_meta_db.cpp +++ b/repertory/librepertory/src/db/impl/sqlite_meta_db.cpp @@ -308,12 +308,12 @@ auto sqlite_meta_db::remove_item_meta(const std::string &api_path, const std::string &key) -> api_error { REPERTORY_USES_FUNCTION_NAME(); - if (utils::collection::includes(META_PERMANENT_NAMES, key)) { + if (utils::collection::includes(META_USED_NAMES, key)) { utils::error::raise_api_path_error( function_name, api_path, fmt::format("failed to remove item meta-key is restricted|key|{}", key)); - return api_error::success; + return api_error::permission_denied; } api_meta_map meta{}; diff --git a/repertory/librepertory/src/providers/encrypt/encrypt_provider.cpp b/repertory/librepertory/src/providers/encrypt/encrypt_provider.cpp index afdb7e65..7b5cf60d 100644 --- a/repertory/librepertory/src/providers/encrypt/encrypt_provider.cpp +++ b/repertory/librepertory/src/providers/encrypt/encrypt_provider.cpp @@ -906,6 +906,21 @@ void encrypt_provider::remove_expired_files() { } } +auto encrypt_provider::remove_item_meta(const std::string &api_path, + const std::string &key) -> api_error { + REPERTORY_USES_FUNCTION_NAME(); + + if (utils::collection::includes(META_USED_NAMES, key)) { + utils::error::raise_api_path_error( + function_name, api_path, + fmt::format("failed to remove item meta-key is restricted|key|{}", + key)); + return api_error::permission_denied; + } + + return api_error::success; +} + auto encrypt_provider::start(api_item_added_callback /*api_item_added*/, i_file_manager * /*mgr*/) -> bool { REPERTORY_USES_FUNCTION_NAME(); diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index 7d61fa64..5fddf92f 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -1438,9 +1438,9 @@ static void remove_item_meta_restricted_names_fail(i_provider &provider) { create_file(provider, api_path); } - for (const auto &key : META_PERMANENT_NAMES) { + for (const auto &key : META_USED_NAMES) { auto res = provider.remove_item_meta(api_path, std::string{key}); - EXPECT_EQ(api_error::success, res); + EXPECT_EQ(api_error::permission_denied, res); api_meta_map meta{}; EXPECT_EQ(api_error::success, provider.get_item_meta(api_path, meta)); From 4c1e9360fbdb19a37c5909e3e861735efda198be Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Wed, 17 Sep 2025 18:20:55 -0500 Subject: [PATCH 048/136] [unit test] Complete all providers unit tests #12 --- repertory/repertory_test/src/providers_test.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index 5fddf92f..3e2a27b1 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -1428,13 +1428,20 @@ static void remove_item_meta_path_not_found(i_provider &provider) { EXPECT_EQ(api_error::item_not_found, res); } -static void remove_item_meta_restricted_names_fail(i_provider &provider) { +static void remove_item_meta_restricted_names_fail(const app_config &cfg, + i_provider &provider) { fmt::println("testing|{}|{}", app_config::get_provider_name(provider.get_provider_type()), __FUNCTION__); - std::string api_path{"/rim_restricted.txt"}; - if (provider.get_provider_type() != provider_type::encrypt) { + std::string api_path; + if (provider.get_provider_type() == provider_type::encrypt) { + auto source_path = + utils::path::combine(cfg.get_encrypt_config().path, {"test.txt"}); + EXPECT_EQ(api_error::success, + provider.get_api_path_from_source(source_path, api_path)); + } else { + api_path = "/rim_restricted.txt"; create_file(provider, api_path); } @@ -1611,7 +1618,7 @@ static void run_tests(const app_config &cfg, i_provider &provider) { remove_item_meta(provider); remove_item_meta_path_not_found(provider); - remove_item_meta_restricted_names_fail(provider); + remove_item_meta_restricted_names_fail(cfg, provider); rename_file(provider); rename_file_fails_if_source_not_found(provider); From d9e70ad81d20d4d3a48c4fecbcb61170507abd0d Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Wed, 17 Sep 2025 18:21:30 -0500 Subject: [PATCH 049/136] [unit test] Complete all providers unit tests #12 --- repertory/repertory_test/src/providers_test.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index 3e2a27b1..7d7fd350 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -1454,7 +1454,9 @@ static void remove_item_meta_restricted_names_fail(const app_config &cfg, EXPECT_TRUE(meta.contains(std::string{key})); } - EXPECT_EQ(api_error::success, provider.remove_file(api_path)); + if (provider.get_provider_type() != provider_type::encrypt) { + EXPECT_EQ(api_error::success, provider.remove_file(api_path)); + } } static void rename_file(i_provider &provider) { From 3cc25bc482337fdae5973443502cd6574c0111ba Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Wed, 17 Sep 2025 18:26:46 -0500 Subject: [PATCH 050/136] [unit test] Complete all providers unit tests #12 --- repertory/repertory_test/src/providers_test.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index 7d7fd350..1cede56b 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -1523,14 +1523,20 @@ static void rename_file_fails_if_destination_exists(i_provider &provider) { __FUNCTION__); if (not provider.is_rename_supported()) { - create_file(provider, "/rn_src_conflict.txt"); - create_file(provider, "/rn_dst_conflict.txt"); + if (provider.get_provider_type() != provider_type::encrypt) { + create_file(provider, "/rn_src_conflict.txt"); + create_file(provider, "/rn_dst_conflict.txt"); + } auto res = provider.rename_file("/rn_src_conflict.txt", "/rn_dst_conflict.txt"); EXPECT_EQ(api_error::not_implemented, res); - EXPECT_EQ(api_error::success, provider.remove_file("/rn_src_conflict.txt")); - EXPECT_EQ(api_error::success, provider.remove_file("/rn_dst_conflict.txt")); + if (provider.get_provider_type() != provider_type::encrypt) { + EXPECT_EQ(api_error::success, + provider.remove_file("/rn_src_conflict.txt")); + EXPECT_EQ(api_error::success, + provider.remove_file("/rn_dst_conflict.txt")); + } return; } From 17bc1b41c6d118e08056081d9e7d54307e6f531d Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Wed, 17 Sep 2025 19:02:46 -0500 Subject: [PATCH 051/136] [unit test] Complete all providers unit tests #12 --- .../include/platform/unix_platform.hpp | 3 +++ .../include/platform/win32_platform.hpp | 3 +++ .../src/platform/unix_platform.cpp | 15 ++++++++----- .../src/platform/win32_platform.cpp | 15 ++++++++----- .../providers/encrypt/encrypt_provider.cpp | 22 +++++++------------ .../src/providers/sia/sia_provider.cpp | 19 +++++++++++++++- 6 files changed, 52 insertions(+), 25 deletions(-) diff --git a/repertory/librepertory/include/platform/unix_platform.hpp b/repertory/librepertory/include/platform/unix_platform.hpp index fbb9ceab..d97da6c8 100644 --- a/repertory/librepertory/include/platform/unix_platform.hpp +++ b/repertory/librepertory/include/platform/unix_platform.hpp @@ -79,6 +79,9 @@ public: std::uint32_t osx_flags, std::uint64_t size, const std::string &source_path, std::uint32_t uid, std::uint64_t written_date) -> api_meta_map; +[[nodiscard]] auto provider_meta_creator(bool directory, const api_file &file) + -> api_meta_map; + [[nodiscard]] auto provider_meta_handler(i_provider &provider, bool directory, const api_file &file) -> api_error; } // namespace repertory diff --git a/repertory/librepertory/include/platform/win32_platform.hpp b/repertory/librepertory/include/platform/win32_platform.hpp index 628a0d06..ee3da061 100644 --- a/repertory/librepertory/include/platform/win32_platform.hpp +++ b/repertory/librepertory/include/platform/win32_platform.hpp @@ -66,6 +66,9 @@ public: std::uint32_t osx_flags, std::uint64_t size, const std::string &source_path, std::uint32_t uid, std::uint64_t written_date) -> api_meta_map; +[[nodiscard]] auto provider_meta_creator(bool directory, const api_file &file) + -> api_meta_map; + [[nodiscard]] auto provider_meta_handler(i_provider &provider, bool directory, const api_file &file) -> api_error; } // namespace repertory diff --git a/repertory/librepertory/src/platform/unix_platform.cpp b/repertory/librepertory/src/platform/unix_platform.cpp index d3aac226..7486e1fb 100644 --- a/repertory/librepertory/src/platform/unix_platform.cpp +++ b/repertory/librepertory/src/platform/unix_platform.cpp @@ -235,11 +235,9 @@ auto create_meta_attributes( }; } -auto provider_meta_handler(i_provider &provider, bool directory, - const api_file &file) -> api_error { - REPERTORY_USES_FUNCTION_NAME(); - - auto meta = create_meta_attributes( +auto provider_meta_creator(bool directory, const api_file &file) + -> api_meta_map { + return create_meta_attributes( file.accessed_date, directory ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_ARCHIVE, file.changed_date, file.creation_date, directory, getgid(), file.key, @@ -247,6 +245,13 @@ auto provider_meta_handler(i_provider &provider, bool directory, : S_IFREG | S_IRUSR | S_IWUSR, file.modified_date, 0U, 0U, file.file_size, file.source_path, getuid(), file.modified_date); +} + +auto provider_meta_handler(i_provider &provider, bool directory, + const api_file &file) -> api_error { + REPERTORY_USES_FUNCTION_NAME(); + + auto meta = provider_meta_creator(directory, file); auto res = provider.set_item_meta(file.api_path, meta); if (res == api_error::success) { event_system::instance().raise( diff --git a/repertory/librepertory/src/platform/win32_platform.cpp b/repertory/librepertory/src/platform/win32_platform.cpp index 7821f051..17250082 100644 --- a/repertory/librepertory/src/platform/win32_platform.cpp +++ b/repertory/librepertory/src/platform/win32_platform.cpp @@ -217,16 +217,21 @@ auto create_meta_attributes( }; } -auto provider_meta_handler(i_provider &provider, bool directory, - const api_file &file) -> api_error { - REPERTORY_USES_FUNCTION_NAME(); - - const auto meta = create_meta_attributes( +auto provider_meta_creator(bool directory, const api_file &file) + -> api_meta_map { + return create_meta_attributes( file.accessed_date, directory ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_ARCHIVE, file.changed_date, file.creation_date, directory, 0u, file.key, directory ? S_IFDIR : S_IFREG, file.modified_date, 0u, 0u, file.file_size, file.source_path, 0u, file.modified_date); +} + +auto provider_meta_handler(i_provider &provider, bool directory, + const api_file &file) -> api_error { + REPERTORY_USES_FUNCTION_NAME(); + + auto meta = provider_meta_creator(directory, file); auto res = provider.set_item_meta(file.api_path, meta); if (res == api_error::success) { event_system::instance().raise( diff --git a/repertory/librepertory/src/providers/encrypt/encrypt_provider.cpp b/repertory/librepertory/src/providers/encrypt/encrypt_provider.cpp index 7b5cf60d..6caf5198 100644 --- a/repertory/librepertory/src/providers/encrypt/encrypt_provider.cpp +++ b/repertory/librepertory/src/providers/encrypt/encrypt_provider.cpp @@ -31,6 +31,7 @@ #include "events/types/service_start_end.hpp" #include "events/types/service_stop_begin.hpp" #include "events/types/service_stop_end.hpp" +#include "platform/platform.hpp" #include "types/repertory.hpp" #include "types/startup_exception.hpp" #include "utils/base64.hpp" @@ -83,29 +84,22 @@ void encrypt_provider::create_item_meta(api_meta_map &meta, bool directory, stat(file.source_path.c_str(), &buf); #endif // defined(_WIN32) - meta[META_ACCESSED] = std::to_string(file.accessed_date); + meta = provider_meta_creator(directory, file); + #if defined(_WIN32) meta[META_ATTRIBUTES] = std::to_string(::GetFileAttributesA(file.source_path.c_str()) & ~static_cast(FILE_ATTRIBUTE_REPARSE_POINT)); - #endif // defined(_WIN32) -#if defined(__APPLE__) - meta[META_BACKUP]; -#endif // defined(__APPLE__) - meta[META_CHANGED] = std::to_string(file.changed_date); - meta[META_CREATION] = std::to_string(file.creation_date); - meta[META_DIRECTORY] = utils::string::from_bool(directory); - meta[META_GID] = std::to_string(buf.st_gid); + meta[META_MODE] = std::to_string(buf.st_mode); - meta[META_MODIFIED] = std::to_string(file.modified_date); + meta[META_GID] = std::to_string(buf.st_gid); + meta[META_UID] = std::to_string(buf.st_uid); + #if defined(__APPLE__) + meta[META_BACKUP] = std::to_string(buf.st_bkuptime); meta[META_OSXFLAGS] = std::to_string(buf.st_flags); #endif // defined(__APPLE__) - meta[META_SIZE] = std::to_string(file.file_size); - meta[META_SOURCE] = file.source_path; - meta[META_UID] = std::to_string(buf.st_uid); - meta[META_WRITTEN] = std::to_string(file.modified_date); } auto encrypt_provider::create_directory(const std::string &api_path, diff --git a/repertory/librepertory/src/providers/sia/sia_provider.cpp b/repertory/librepertory/src/providers/sia/sia_provider.cpp index c940815b..3909c255 100644 --- a/repertory/librepertory/src/providers/sia/sia_provider.cpp +++ b/repertory/librepertory/src/providers/sia/sia_provider.cpp @@ -809,6 +809,23 @@ auto sia_provider::rename_file(const std::string &from_api_path, REPERTORY_USES_FUNCTION_NAME(); try { + bool exists{}; + auto res = is_file(to_api_path, exists); + if (res != api_error::success) { + return res; + } + if (exists) { + return api_error::item_exists; + } + + res = is_directory(to_api_path, exists); + if (res != api_error::success) { + return res; + } + if (exists) { + return api_error::directory_exists; + } + curl::requests::http_post post{}; post.json = nlohmann::json({ {"bucket", get_sia_config().bucket}, @@ -842,7 +859,7 @@ auto sia_provider::rename_file(const std::string &from_api_path, function_name, fmt::format("{}|{}", from_api_path, to_api_path), response_code, fmt::format("failed to rename file file|response|{}", error_data)); - return api_error::comm_error; + return api_error::item_not_found; } return get_db().rename_item_meta(from_api_path, to_api_path); From 3d798ca17e3fe1f2f760ac1094625b23cf524b6d Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Wed, 17 Sep 2025 19:06:30 -0500 Subject: [PATCH 052/136] [unit test] Complete all providers unit tests #12 --- .../repertory_test/src/providers_test.cpp | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index 1cede56b..bcd6afb6 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -1558,6 +1558,47 @@ static void rename_file_fails_if_destination_exists(i_provider &provider) { EXPECT_EQ(api_error::success, provider.remove_file(dst)); } +static void +rename_file_fails_if_destination_is_directory(i_provider &provider) { + fmt::println("testing|{}|{}", + app_config::get_provider_name(provider.get_provider_type()), + __FUNCTION__); + + if (not provider.is_rename_supported()) { + if (provider.get_provider_type() != provider_type::encrypt) { + create_file(provider, "/rn_src_conflict.txt"); + create_directory(provider, "/rn_dst_conflict"); + } + auto res = provider.rename_file("/rn_src_conflict.txt", "/rn_dst_conflict"); + EXPECT_EQ(api_error::not_implemented, res); + + if (provider.get_provider_type() != provider_type::encrypt) { + EXPECT_EQ(api_error::success, + provider.remove_file("/rn_src_conflict.txt")); + EXPECT_EQ(api_error::success, + provider.remove_directory("/rn_dst_conflict")); + } + return; + } + + std::string src{"/rn_src_conflict.txt"}; + std::string dst{"/rn_dst_conflict"}; + create_file(provider, src); + create_directory(provider, dst); + + auto res = provider.rename_file(src, dst); + EXPECT_EQ(api_error::directory_exists, res); + + bool exists{}; + EXPECT_EQ(api_error::success, provider.is_file(src, exists)); + EXPECT_TRUE(exists); + EXPECT_EQ(api_error::success, provider.is_directory(dst, exists)); + EXPECT_TRUE(exists); + + EXPECT_EQ(api_error::success, provider.remove_file(src)); + EXPECT_EQ(api_error::success, provider.remove_directory(dst)); +} + static void run_tests(const app_config &cfg, i_provider &provider) { // MOVED get_file_list(cfg, provider); @@ -1631,6 +1672,7 @@ static void run_tests(const app_config &cfg, i_provider &provider) { rename_file(provider); rename_file_fails_if_source_not_found(provider); rename_file_fails_if_destination_exists(provider); + rename_file_fails_if_destination_is_directory(provider); // TODO need to test read when file size changes for encrypt provider /* From b0fab0752713bf661a78dc43cbe060479156dd83 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Thu, 18 Sep 2025 14:52:43 -0500 Subject: [PATCH 053/136] [unit test] Complete all providers unit tests #12 --- .cspell/words.txt | 1 + .../include/fixtures/providers_fixture.hpp | 221 +++ .../repertory_test/src/providers_test.cpp | 1676 ++++++----------- 3 files changed, 825 insertions(+), 1073 deletions(-) create mode 100644 repertory/repertory_test/include/fixtures/providers_fixture.hpp diff --git a/.cspell/words.txt b/.cspell/words.txt index 7ca74458..2d991326 100644 --- a/.cspell/words.txt +++ b/.cspell/words.txt @@ -103,6 +103,7 @@ endforeach endfunction eventlib expect_streq +expect_strne fallocate fallocate_impl fext diff --git a/repertory/repertory_test/include/fixtures/providers_fixture.hpp b/repertory/repertory_test/include/fixtures/providers_fixture.hpp new file mode 100644 index 00000000..7d57ba15 --- /dev/null +++ b/repertory/repertory_test/include/fixtures/providers_fixture.hpp @@ -0,0 +1,221 @@ +/* + Copyright <2018-2025> + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ +#ifndef REPERTORY_TEST_INCLUDE_FIXTURES_PROVIDERS_FIXTURE_HPP +#define REPERTORY_TEST_INCLUDE_FIXTURES_PROVIDERS_FIXTURE_HPP + +#include "test_common.hpp" + +#include "comm/curl/curl_comm.hpp" +#include "events/event_system.hpp" +#include "file_manager/file_manager.hpp" +#include "fixtures/providers_fixture.hpp" +#include "platform/platform.hpp" +#include "providers/encrypt/encrypt_provider.hpp" +#include "providers/i_provider.hpp" +#include "providers/s3/s3_provider.hpp" +#include "providers/sia/sia_provider.hpp" +#include "utils/collection.hpp" +#include "utils/file.hpp" +#include "utils/path.hpp" +#include "utils/string.hpp" +#include "utils/time.hpp" +#include "utils/utils.hpp" + +namespace repertory { +struct encrypt_provider_type final { + static constexpr provider_type type{provider_type::encrypt}; + + static void setup(std::unique_ptr &comm, + std::unique_ptr &config, + std::unique_ptr &provider) { + auto config_path = + utils::path::combine(test::get_test_output_dir(), { + "provider", + "encrypt", + }); + + config = std::make_unique(type, config_path); + + auto encrypt_path = + utils::path::combine(test::get_test_input_dir(), {"encrypt"}); + + EXPECT_STREQ( + encrypt_path.c_str(), + config->set_value_by_name("EncryptConfig.Path", encrypt_path).c_str()); + EXPECT_STREQ( + "test_token", + config->set_value_by_name("EncryptConfig.EncryptionToken", "test_token") + .c_str()); + + provider = std::make_unique(*config); + EXPECT_TRUE(provider->is_read_only()); + EXPECT_FALSE(provider->is_rename_supported()); + EXPECT_EQ(type, provider->get_provider_type()); + } +}; + +struct s3_provider_encrypted_type final { + static constexpr provider_type type{provider_type::s3}; + + static void setup(std::unique_ptr &comm, + std::unique_ptr &config, + std::unique_ptr &provider) { + auto config_path = + utils::path::combine(test::get_test_output_dir(), { + "provider", + "s3", + }); + + config = std::make_unique(type, config_path); + { + app_config src_cfg( + type, utils::path::combine(test::get_test_config_dir(), {"s3"})); + auto s3_cfg = src_cfg.get_s3_config(); + s3_cfg.encryption_token = "cow_moose_doge_chicken"; + config->set_s3_config(s3_cfg); + } + + comm = std::make_unique(config->get_s3_config()); + + provider = std::make_unique(*config, *comm); + EXPECT_EQ(type, provider->get_provider_type()); + EXPECT_FALSE(provider->is_read_only()); + EXPECT_FALSE(provider->is_rename_supported()); + } +}; + +struct s3_provider_unencrypted_type final { + static constexpr provider_type type{provider_type::s3}; + + static void setup(std::unique_ptr &comm, + std::unique_ptr &config, + std::unique_ptr &provider) { + auto config_path = + utils::path::combine(test::get_test_output_dir(), { + "provider", + "s3", + }); + + config = std::make_unique(type, config_path); + { + app_config src_cfg( + type, utils::path::combine(test::get_test_config_dir(), {"s3"})); + auto s3_cfg = src_cfg.get_s3_config(); + s3_cfg.encryption_token = ""; + config->set_s3_config(s3_cfg); + } + + comm = std::make_unique(config->get_s3_config()); + + provider = std::make_unique(*config, *comm); + EXPECT_EQ(type, provider->get_provider_type()); + EXPECT_FALSE(provider->is_read_only()); + EXPECT_FALSE(provider->is_rename_supported()); + } +}; + +struct sia_provider_type final { + static constexpr provider_type type{provider_type::sia}; + + static void setup(std::unique_ptr &comm, + std::unique_ptr &config, + std::unique_ptr &provider) { + auto config_path = + utils::path::combine(test::get_test_output_dir(), { + "provider", + "sia", + }); + + config = std::make_unique(type, config_path); + { + app_config src_cfg( + provider_type::sia, + utils::path::combine(test::get_test_config_dir(), {"sia"})); + config->set_host_config(src_cfg.get_host_config()); + config->set_sia_config(src_cfg.get_sia_config()); + } + + comm = std::make_unique(config->get_host_config()); + + provider = std::make_unique(*config, *comm); + + EXPECT_EQ(type, provider->get_provider_type()); + EXPECT_FALSE(provider->is_read_only()); + EXPECT_TRUE(provider->is_rename_supported()); + } +}; + +template class providers_test : public ::testing::Test { +public: + static std::unique_ptr comm; + static std::unique_ptr config; + static std::unique_ptr mgr; + static std::unique_ptr provider; + static console_consumer consumer{}; + +protected: + static void SetUpTestCase() { + event_system::instance().start(); + + provider_t::setup(comm, config, provider); + mgr = std::make_unique(*config, *provider); + + EXPECT_TRUE(provider->start( + [&provider](bool directory, api_file &file) -> api_error { + return provider_meta_handler(*provider, directory, file); + }, + mgr.get())); + + mgr->start(); + EXPECT_TRUE(provider->is_online()); + } + + static void TearDownTestCase() { + provider->stop(); + mgr->stop(); + + mgr.reset(); + provider.reset(); + comm.reset(); + + event_system::instance().stop(); + } +}; + +template +std::unique_ptr providers_test::comm; + +template +std::unique_ptr providers_test::config; + +template +std::unique_ptr providers_test::mgr; + +template +std::unique_ptr providers_test::provider; + +using provider_types = + ::testing::Types; +} // namespace repertory + +#endif // REPERTORY_TEST_INCLUDE_FIXTURES_PROVIDERS_FIXTURE_HPP diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index bcd6afb6..036e5bbb 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -21,20 +21,7 @@ */ #include "test_common.hpp" -#include "comm/curl/curl_comm.hpp" -#include "events/event_system.hpp" -#include "file_manager/file_manager.hpp" -#include "platform/platform.hpp" -#include "providers/encrypt/encrypt_provider.hpp" -#include "providers/i_provider.hpp" -#include "providers/s3/s3_provider.hpp" -#include "providers/sia/sia_provider.hpp" -#include "utils/collection.hpp" -#include "utils/file.hpp" -#include "utils/path.hpp" -#include "utils/string.hpp" -#include "utils/time.hpp" -#include "utils/utils.hpp" +#include "fixtures/providers_fixture.hpp" namespace { #if defined(_WIN32) @@ -181,113 +168,248 @@ const auto pinned_includes_api_path = }); }; -/* -[[nodiscard]] auto read_all_plain(const std::string &path) -> std::string { - repertory::utils::file::file file{path}; - auto data = file.read_all(); - EXPECT_TRUE(data.has_value()); - return data.value_or(std::string{}); -} - -[[nodiscard]] auto chunked_read_bytes(repertory::i_provider &provider, - const std::string &api_path, - std::uint64_t total) -> std::string { - std::string out{}; - std::uint64_t off{0U}; - constexpr const std::array chunks{ - 1U, 7U, 64U, 3U, 1024U, 5U, - }; - std::size_t idx{0U}; - - while (off < total) { - auto want = std::min(chunks.at(idx % std::size(chunks)), - total - off); - std::string part{}; - EXPECT_EQ(api_error::success, - provider.read_file_bytes(api_path, off, want, part)); - EXPECT_EQ(want, static_cast(part.size())); - out.append(part); - off += want; - ++idx; - } - return out; -} */ } // namespace namespace repertory { -static void can_create_and_remove_directory(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - if (provider.is_read_only()) { - api_meta_map meta{}; - EXPECT_EQ(api_error::not_implemented, - provider.create_directory("/moose", meta)); +TYPED_TEST_CASE(providers_test, provider_types); - EXPECT_EQ(api_error::not_implemented, provider.remove_directory("/moose")); +TYPED_TEST(providers_test, get_file_list) { + api_file_list list{}; + std::string marker; + EXPECT_EQ(api_error::success, this->provider->get_file_list(list, marker)); + if (this->provider->get_provider_type() == provider_type::encrypt) { + EXPECT_EQ(std::size_t(2U), list.size()); + + std::vector expected_parents{ + {"/"}, + {"/sub10"}, + }; + std::vector expected_paths{ + {"/test.txt"}, + {"/sub10/moose.txt"}, + }; + + for (auto &file : list) { + decrypt_parts(*this->config, file.api_parent); + decrypt_parts(*this->config, file.api_path); + utils::collection::remove_element(expected_parents, file.api_parent); + utils::collection::remove_element(expected_paths, file.api_path); + } + EXPECT_TRUE(expected_parents.empty()); + EXPECT_TRUE(expected_paths.empty()); + } +} + +TYPED_TEST(providers_test, get_and_set_item_meta_with_upload_file) { + if (this->provider->get_provider_type() == provider_type::encrypt) { + std::string api_path{}; + EXPECT_EQ(api_error::success, + this->provider->get_api_path_from_source( + utils::path::combine(this->config->get_encrypt_config().path, + {"test.txt"}), + api_path)); + + std::string val{}; + EXPECT_EQ(api_error::success, + this->provider->get_item_meta(api_path, META_SOURCE, val)); + EXPECT_FALSE(val.empty()); + EXPECT_TRUE(utils::file::file{val}.exists()); + + val.clear(); + EXPECT_EQ(api_error::success, + this->provider->get_item_meta(api_path, META_DIRECTORY, val)); + EXPECT_FALSE(utils::string::to_bool(val)); return; } - create_directory(provider, "/pt01"); - EXPECT_EQ(api_error::success, provider.remove_directory("/pt01")); + auto &file = test::create_random_file(128U); + auto api_path = + fmt::format("/{}", utils::path::strip_to_file_name(file.get_path())); + create_file(*this->provider, api_path); + + stop_type stop_requested{false}; + ASSERT_EQ(api_error::success, this->provider->upload_file( + api_path, file.get_path(), stop_requested)); + + auto size_str = std::to_string(*file.size()); + EXPECT_EQ(api_error::success, + this->provider->set_item_meta(api_path, META_SIZE, size_str)); + EXPECT_EQ(api_error::success, this->provider->set_item_meta( + api_path, META_SOURCE, file.get_path())); + + std::string val{}; + EXPECT_EQ(api_error::success, + this->provider->get_item_meta(api_path, META_SIZE, val)); + EXPECT_STREQ(size_str.c_str(), val.c_str()); + + val.clear(); + EXPECT_EQ(api_error::success, + this->provider->get_item_meta(api_path, META_SOURCE, val)); + EXPECT_STREQ(file.get_path().c_str(), val.c_str()); + + EXPECT_EQ(api_error::success, this->provider->remove_file(api_path)); +} + +TYPED_TEST(providers_test, can_create_and_remove_directory) { + if (this->provider->is_read_only()) { + api_meta_map meta{}; + EXPECT_EQ(api_error::not_implemented, + this->provider->create_directory("/moose", meta)); + + EXPECT_EQ(api_error::not_implemented, + this->provider->remove_directory("/moose")); + return; + } + + create_directory(*this->provider, "/pt01"); + EXPECT_EQ(api_error::success, this->provider->remove_directory("/pt01")); bool exists{}; - EXPECT_EQ(api_error::success, provider.is_directory("/pt01", exists)); + EXPECT_EQ(api_error::success, this->provider->is_directory("/pt01", exists)); EXPECT_FALSE(exists); } -static void create_directory_fails_if_already_exists(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - if (provider.is_read_only()) { +TYPED_TEST(providers_test, get_and_set_item_meta2_with_upload_file) { + if (this->provider->get_provider_type() == provider_type::encrypt) { + std::string api_path{}; + EXPECT_EQ(api_error::success, + this->provider->get_api_path_from_source( + utils::path::combine(this->config->get_encrypt_config().path, + {"test.txt"}), + api_path)); + + api_meta_map meta{}; + EXPECT_EQ(api_error::success, + this->provider->get_item_meta(api_path, meta)); + EXPECT_TRUE(meta.contains(META_SOURCE)); + EXPECT_TRUE(meta.contains(META_DIRECTORY)); + EXPECT_FALSE(utils::string::to_bool(meta[META_DIRECTORY])); return; } - create_directory(provider, "/pt01"); + auto &file = test::create_random_file(64U); + auto api_path = + fmt::format("/{}", utils::path::strip_to_file_name(file.get_path())); + create_file(*this->provider, api_path); + + stop_type stop_requested{false}; + ASSERT_EQ(api_error::success, this->provider->upload_file( + api_path, file.get_path(), stop_requested)); + + api_meta_map to_set{ + {META_SIZE, std::to_string(*file.size())}, + {META_SOURCE, file.get_path()}, + }; + EXPECT_EQ(api_error::success, + this->provider->set_item_meta(api_path, to_set)); + + api_meta_map meta{}; + EXPECT_EQ(api_error::success, this->provider->get_item_meta(api_path, meta)); + EXPECT_STREQ(std::to_string(*file.size()).c_str(), meta[META_SIZE].c_str()); + EXPECT_STREQ(file.get_path().c_str(), meta[META_SOURCE].c_str()); + EXPECT_FALSE(utils::string::to_bool(meta[META_DIRECTORY])); + + EXPECT_EQ(api_error::success, this->provider->remove_file(api_path)); +} + +TYPED_TEST(providers_test, get_item_meta_fails_if_path_not_found) { + std::string val{}; + EXPECT_EQ( + api_error::item_not_found, + this->provider->get_item_meta("/cow/moose/doge/chicken", META_SIZE, val)); + EXPECT_TRUE(val.empty()); +} + +TYPED_TEST(providers_test, get_item_meta2_fails_if_path_not_found) { + api_meta_map meta{}; + EXPECT_EQ(api_error::item_not_found, + this->provider->get_item_meta("/cow/moose/doge/chicken", meta)); + EXPECT_TRUE(meta.empty()); +} + +TYPED_TEST(providers_test, is_file_fails_if_not_found) { + bool exists{}; + auto res = this->provider->is_file("/cow/moose/doge/chicken", exists); + + EXPECT_EQ(api_error::success, res); + EXPECT_FALSE(exists); +} + +TYPED_TEST(providers_test, is_directory_fails_if_not_found) { + bool exists{}; + auto res = this->provider->is_directory("/cow/moose/doge/chicken", exists); + + EXPECT_EQ(api_error::success, res); + EXPECT_FALSE(exists); +} + +TYPED_TEST(providers_test, can_create_and_remove_file) { + if (this->provider->is_read_only()) { + api_meta_map meta{}; + EXPECT_EQ(api_error::not_implemented, + this->provider->create_file("/moose.txt", meta)); + return; + } + + create_file(*this->provider, "/pt01.txt"); + + bool exists{}; + EXPECT_EQ(api_error::success, this->provider->is_file("/pt01.txt", exists)); + EXPECT_TRUE(exists); + + EXPECT_EQ(api_error::success, this->provider->remove_file("/pt01.txt")); + + EXPECT_EQ(api_error::success, this->provider->is_file("/pt01.txt", exists)); + EXPECT_FALSE(exists); +} + +TYPED_TEST(providers_test, create_directory_fails_if_already_exists) { + if (this->provider->is_read_only()) { + return; + } + + create_directory(*this->provider, "/pt01"); api_meta_map meta{}; EXPECT_EQ(api_error::directory_exists, - provider.create_directory("/pt01", meta)); - EXPECT_EQ(api_error::success, provider.remove_directory("/pt01")); + this->provider->create_directory("/pt01", meta)); + EXPECT_EQ(api_error::success, this->provider->remove_directory("/pt01")); } -static void -create_directory_fails_if_file_already_exists(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - if (provider.is_read_only()) { +TYPED_TEST(providers_test, create_directory_fails_if_file_already_exists) { + if (this->provider->is_read_only()) { return; } - create_file(provider, "/pt01"); + create_file(*this->provider, "/pt01"); api_meta_map meta{}; - EXPECT_EQ(api_error::item_exists, provider.create_directory("/pt01", meta)); + EXPECT_EQ(api_error::item_exists, + this->provider->create_directory("/pt01", meta)); - EXPECT_EQ(api_error::success, provider.remove_file("/pt01")); + EXPECT_EQ(api_error::success, this->provider->remove_file("/pt01")); } -static void create_directory_clone_source_meta(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - if (provider.is_read_only()) { - EXPECT_EQ(api_error::not_implemented, - provider.create_directory_clone_source_meta("/moose", "/moose")); +TYPED_TEST(providers_test, create_directory_clone_source_meta) { + if (this->provider->is_read_only()) { + EXPECT_EQ( + api_error::not_implemented, + this->provider->create_directory_clone_source_meta("/moose", "/moose")); return; } - create_directory(provider, "/clone"); + create_directory(*this->provider, "/clone"); api_meta_map meta_orig{}; - EXPECT_EQ(api_error::success, provider.get_item_meta("/clone", meta_orig)); - EXPECT_EQ(api_error::success, - provider.create_directory_clone_source_meta("/clone", "/clone2")); + this->provider->get_item_meta("/clone", meta_orig)); + + EXPECT_EQ( + api_error::success, + this->provider->create_directory_clone_source_meta("/clone", "/clone2")); api_meta_map meta_clone{}; - EXPECT_EQ(api_error::success, provider.get_item_meta("/clone2", meta_clone)); + EXPECT_EQ(api_error::success, + this->provider->get_item_meta("/clone2", meta_clone)); EXPECT_EQ(meta_orig.size(), meta_clone.size()); for (const auto &item : meta_orig) { @@ -301,158 +423,115 @@ static void create_directory_clone_source_meta(i_provider &provider) { EXPECT_STREQ(item.second.c_str(), meta_clone[item.first].c_str()); } - EXPECT_EQ(api_error::success, provider.remove_directory("/clone")); - EXPECT_EQ(api_error::success, provider.remove_directory("/clone2")); + EXPECT_EQ(api_error::success, this->provider->remove_directory("/clone")); + EXPECT_EQ(api_error::success, this->provider->remove_directory("/clone2")); } -static void create_directory_clone_source_meta_fails_if_already_exists( - i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - if (provider.is_read_only()) { +TYPED_TEST(providers_test, + create_directory_clone_source_meta_fails_if_already_exists) { + if (this->provider->is_read_only()) { return; } - create_directory(provider, "/clone"); - create_directory(provider, "/clone2"); + create_directory(*this->provider, "/clone"); + create_directory(*this->provider, "/clone2"); - EXPECT_EQ(api_error::directory_exists, - provider.create_directory_clone_source_meta("/clone", "/clone2")); + EXPECT_EQ( + api_error::directory_exists, + this->provider->create_directory_clone_source_meta("/clone", "/clone2")); - EXPECT_EQ(api_error::success, provider.remove_directory("/clone")); - EXPECT_EQ(api_error::success, provider.remove_directory("/clone2")); + EXPECT_EQ(api_error::success, this->provider->remove_directory("/clone")); + EXPECT_EQ(api_error::success, this->provider->remove_directory("/clone2")); } -static void create_directory_clone_source_meta_fails_if_directory_not_found( - i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - if (provider.is_read_only()) { +TYPED_TEST(providers_test, + create_directory_clone_source_meta_fails_if_directory_not_found) { + if (this->provider->is_read_only()) { return; } - EXPECT_EQ(api_error::directory_not_found, - provider.create_directory_clone_source_meta("/clone", "/clone2")); + EXPECT_EQ( + api_error::directory_not_found, + this->provider->create_directory_clone_source_meta("/clone", "/clone2")); } -static void create_directory_clone_source_meta_fails_if_file_already_exists( - i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - if (provider.is_read_only()) { +TYPED_TEST(providers_test, + create_directory_clone_source_meta_fails_if_file_already_exists) { + if (this->provider->is_read_only()) { return; } - create_directory(provider, "/clone"); - create_file(provider, "/clone2"); + create_directory(*this->provider, "/clone"); + create_file(*this->provider, "/clone2"); + EXPECT_EQ( + api_error::item_exists, + this->provider->create_directory_clone_source_meta("/clone", "/clone2")); + + EXPECT_EQ(api_error::success, this->provider->remove_directory("/clone")); + EXPECT_EQ(api_error::success, this->provider->remove_file("/clone2")); +} + +TYPED_TEST(providers_test, create_file_fails_if_already_exists) { + if (this->provider->is_read_only()) { + return; + } + + create_file(*this->provider, "/pt01.txt"); + + api_meta_map meta{}; EXPECT_EQ(api_error::item_exists, - provider.create_directory_clone_source_meta("/clone", "/clone2")); + this->provider->create_file("/pt01.txt", meta)); - EXPECT_EQ(api_error::success, provider.remove_directory("/clone")); - EXPECT_EQ(api_error::success, provider.remove_file("/clone2")); + EXPECT_EQ(api_error::success, this->provider->remove_file("/pt01.txt")); } -static void can_create_and_remove_file(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - if (provider.is_read_only()) { - api_meta_map meta{}; - EXPECT_EQ(api_error::not_implemented, - provider.create_file("/moose.txt", meta)); +TYPED_TEST(providers_test, create_file_fails_if_directory_already_exists) { + if (this->provider->is_read_only()) { return; } - create_file(provider, "/pt01.txt"); - - bool exists{}; - EXPECT_EQ(api_error::success, provider.is_file("/pt01.txt", exists)); - EXPECT_TRUE(exists); - - EXPECT_EQ(api_error::success, provider.remove_file("/pt01.txt")); - - EXPECT_EQ(api_error::success, provider.is_file("/pt01.txt", exists)); - EXPECT_FALSE(exists); -} - -static void create_file_fails_if_already_exists(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - if (provider.is_read_only()) { - return; - } - - create_file(provider, "/pt01.txt"); + create_directory(*this->provider, "/pt01"); api_meta_map meta{}; - EXPECT_EQ(api_error::item_exists, provider.create_file("/pt01.txt", meta)); + EXPECT_EQ(api_error::directory_exists, + this->provider->create_file("/pt01", meta)); - EXPECT_EQ(api_error::success, provider.remove_file("/pt01.txt")); + EXPECT_EQ(api_error::success, this->provider->remove_directory("/pt01")); } -static void -create_file_fails_if_directory_already_exists(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - if (provider.is_read_only()) { - return; - } - - create_directory(provider, "/pt01"); - - api_meta_map meta{}; - EXPECT_EQ(api_error::directory_exists, provider.create_file("/pt01", meta)); - - EXPECT_EQ(api_error::success, provider.remove_directory("/pt01")); -} - -static void get_api_path_from_source(const app_config &cfg, - i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - if (provider.get_provider_type() == provider_type::encrypt) { +TYPED_TEST(providers_test, get_api_path_from_source) { + if (this->provider->get_provider_type() == provider_type::encrypt) { auto source_path = utils::path::combine("./test_input/encrypt", {"test.txt"}); std::string api_path{}; EXPECT_EQ(api_error::success, - provider.get_api_path_from_source(source_path, api_path)); + this->provider->get_api_path_from_source(source_path, api_path)); std::string file_name{api_path.substr(1U)}; - decrypt_parts(cfg, file_name); + decrypt_parts(*this->config, file_name); EXPECT_STREQ("test.txt", file_name.c_str()); return; } - create_file(provider, "/pt01.txt"); + create_file(*this->provider, "/pt01.txt"); filesystem_item fsi{}; EXPECT_EQ(api_error::success, - provider.get_filesystem_item("/pt01.txt", false, fsi)); + this->provider->get_filesystem_item("/pt01.txt", false, fsi)); std::string api_path{}; - EXPECT_EQ(api_error::success, - provider.get_api_path_from_source(fsi.source_path, api_path)); + EXPECT_EQ(api_error::success, this->provider->get_api_path_from_source( + fsi.source_path, api_path)); EXPECT_STREQ("/pt01.txt", api_path.c_str()); - EXPECT_EQ(api_error::success, provider.remove_file("/pt01.txt")); + EXPECT_EQ(api_error::success, this->provider->remove_file("/pt01.txt")); } -static void -get_api_path_from_source_fails_if_file_not_found(const app_config &cfg, - i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); +TYPED_TEST(providers_test, get_api_path_from_source_fails_if_file_not_found) { std::string source_path{}; - if (provider.get_provider_type() == provider_type::encrypt) { - source_path = utils::path::combine(cfg.get_encrypt_config().path, + if (this->provider->get_provider_type() == provider_type::encrypt) { + source_path = utils::path::combine(this->config->get_encrypt_config().path, {"test_not_found.txt"}); } else { source_path = utils::path::combine("./", {"test_not_found.txt"}); @@ -460,61 +539,24 @@ get_api_path_from_source_fails_if_file_not_found(const app_config &cfg, std::string api_path{}; EXPECT_EQ(api_error::item_not_found, - provider.get_api_path_from_source(source_path, api_path)); + this->provider->get_api_path_from_source(source_path, api_path)); EXPECT_TRUE(api_path.empty()); } -static void get_directory_item_count(const app_config & /* cfg */, - i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - if (provider.get_provider_type() == provider_type::encrypt) { - EXPECT_EQ(std::size_t(2U), provider.get_directory_item_count("/")); - EXPECT_EQ(std::size_t(0U), provider.get_directory_item_count("/not_found")); - - auto source_path = - utils::path::combine(test::get_test_input_dir(), {"encrypt", "sub10"}); - - std::string api_path{}; - EXPECT_EQ(api_error::success, - provider.get_api_path_from_source(source_path, api_path)); - EXPECT_EQ(std::size_t(1U), provider.get_directory_item_count(api_path)); - return; - } - - create_file(provider, "/pt01.txt"); - create_file(provider, "/pt02.txt"); - create_directory(provider, "/dir01"); - create_directory(provider, "/dir02"); - - directory_item_list list{}; - EXPECT_EQ(api_error::success, provider.get_directory_items("/", list)); - check_forced_dirs(list); - EXPECT_GE(list.size(), std::size_t(6U)); - - EXPECT_EQ(api_error::success, provider.remove_file("/pt01.txt")); - EXPECT_EQ(api_error::success, provider.remove_file("/pt02.txt")); - EXPECT_EQ(api_error::success, provider.remove_directory("/dir01")); - EXPECT_EQ(api_error::success, provider.remove_directory("/dir02")); -} - -static void get_directory_items(const app_config &cfg, i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - if (provider.get_provider_type() == provider_type::encrypt) { +TYPED_TEST(providers_test, get_directory_items) { + if (this->provider->get_provider_type() == provider_type::encrypt) { directory_item_list list{}; - EXPECT_EQ(api_error::success, provider.get_directory_items("/", list)); + EXPECT_EQ(api_error::success, + this->provider->get_directory_items("/", list)); check_forced_dirs(list); EXPECT_EQ(std::size_t(4U), list.size()); directory_item_list list_decrypted{list.begin() + 2U, list.end()}; for (auto &dir_item : list_decrypted) { - decrypt_parts(cfg, dir_item.api_parent); - decrypt_parts(cfg, dir_item.api_path); + decrypt_parts(*this->config, dir_item.api_parent); + decrypt_parts(*this->config, dir_item.api_path); } auto dir = @@ -539,21 +581,22 @@ static void get_directory_items(const app_config &cfg, i_provider &provider) { EXPECT_EQ(std::size_t(82U), file->size); #endif - auto source_path = - utils::path::combine(cfg.get_encrypt_config().path, {"sub10"}); + auto source_path = utils::path::combine( + this->config->get_encrypt_config().path, {"sub10"}); std::string api_path{}; EXPECT_EQ(api_error::success, - provider.get_api_path_from_source(source_path, api_path)); + this->provider->get_api_path_from_source(source_path, api_path)); list.clear(); - EXPECT_EQ(api_error::success, provider.get_directory_items(api_path, list)); + EXPECT_EQ(api_error::success, + this->provider->get_directory_items(api_path, list)); check_forced_dirs(list); EXPECT_EQ(std::size_t(3U), list.size()); directory_item_list list_decrypted2{list.begin() + 2U, list.end()}; for (auto &dir_item : list_decrypted2) { - decrypt_parts(cfg, dir_item.api_parent); - decrypt_parts(cfg, dir_item.api_path); + decrypt_parts(*this->config, dir_item.api_parent); + decrypt_parts(*this->config, dir_item.api_path); } auto file2 = @@ -571,13 +614,13 @@ static void get_directory_items(const app_config &cfg, i_provider &provider) { return; } - create_file(provider, "/pt01.txt"); - create_file(provider, "/pt02.txt"); - create_directory(provider, "/dir01"); - create_directory(provider, "/dir02"); + create_file(*this->provider, "/pt01.txt"); + create_file(*this->provider, "/pt02.txt"); + create_directory(*this->provider, "/dir01"); + create_directory(*this->provider, "/dir02"); directory_item_list list{}; - EXPECT_EQ(api_error::success, provider.get_directory_items("/", list)); + EXPECT_EQ(api_error::success, this->provider->get_directory_items("/", list)); check_forced_dirs(list); EXPECT_GE(list.size(), std::size_t(6U)); @@ -609,68 +652,88 @@ static void get_directory_items(const app_config &cfg, i_provider &provider) { EXPECT_TRUE((*iter).directory); EXPECT_EQ(std::uint64_t{0U}, (*iter).size); - EXPECT_EQ(api_error::success, provider.remove_file("/pt01.txt")); - EXPECT_EQ(api_error::success, provider.remove_file("/pt02.txt")); - EXPECT_EQ(api_error::success, provider.remove_directory("/dir01")); - EXPECT_EQ(api_error::success, provider.remove_directory("/dir02")); + EXPECT_EQ(api_error::success, this->provider->remove_file("/pt01.txt")); + EXPECT_EQ(api_error::success, this->provider->remove_file("/pt02.txt")); + EXPECT_EQ(api_error::success, this->provider->remove_directory("/dir01")); + EXPECT_EQ(api_error::success, this->provider->remove_directory("/dir02")); } - -static void -get_directory_items_fails_if_directory_not_found(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); +TYPED_TEST(providers_test, get_directory_items_fails_if_directory_not_found) { directory_item_list list{}; EXPECT_EQ(api_error::directory_not_found, - provider.get_directory_items("/not_found", list)); + this->provider->get_directory_items("/not_found", list)); EXPECT_TRUE(list.empty()); } -static void get_directory_items_fails_if_item_is_file(const app_config &cfg, - i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - if (provider.get_provider_type() == provider_type::encrypt) { - auto source_path = - utils::path::combine(cfg.get_encrypt_config().path, {"test.txt"}); +TYPED_TEST(providers_test, get_directory_items_fails_if_item_is_file) { + if (this->provider->get_provider_type() == provider_type::encrypt) { + auto source_path = utils::path::combine( + this->config->get_encrypt_config().path, {"test.txt"}); std::string api_path{}; EXPECT_EQ(api_error::success, - provider.get_api_path_from_source(source_path, api_path)); + this->provider->get_api_path_from_source(source_path, api_path)); directory_item_list list{}; EXPECT_EQ(api_error::item_exists, - provider.get_directory_items(api_path, list)); + this->provider->get_directory_items(api_path, list)); EXPECT_TRUE(list.empty()); return; } - create_file(provider, "/pt01.txt"); + create_file(*this->provider, "/pt01.txt"); directory_item_list list{}; EXPECT_EQ(api_error::item_exists, - provider.get_directory_items("/pt01.txt", list)); + this->provider->get_directory_items("/pt01.txt", list)); - EXPECT_EQ(api_error::success, provider.remove_file("/pt01.txt")); + EXPECT_EQ(api_error::success, this->provider->remove_file("/pt01.txt")); } +TYPED_TEST(providers_test, get_directory_item_count) { + if (this->provider->get_provider_type() == provider_type::encrypt) { + EXPECT_EQ(std::size_t(2U), this->provider->get_directory_item_count("/")); + EXPECT_EQ(std::size_t(0U), + this->provider->get_directory_item_count("/not_found")); -static void get_file(const app_config &cfg, i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - if (provider.get_provider_type() == provider_type::encrypt) { auto source_path = - utils::path::combine(cfg.get_encrypt_config().path, {"test.txt"}); + utils::path::combine(test::get_test_input_dir(), {"encrypt", "sub10"}); std::string api_path{}; EXPECT_EQ(api_error::success, - provider.get_api_path_from_source(source_path, api_path)); + this->provider->get_api_path_from_source(source_path, api_path)); + EXPECT_EQ(std::size_t(1U), + this->provider->get_directory_item_count(api_path)); + return; + } + + create_file(*this->provider, "/pt01.txt"); + create_file(*this->provider, "/pt02.txt"); + create_directory(*this->provider, "/dir01"); + create_directory(*this->provider, "/dir02"); + + directory_item_list list{}; + EXPECT_EQ(api_error::success, this->provider->get_directory_items("/", list)); + check_forced_dirs(list); + EXPECT_GE(list.size(), std::size_t(6U)); + + EXPECT_EQ(api_error::success, this->provider->remove_file("/pt01.txt")); + EXPECT_EQ(api_error::success, this->provider->remove_file("/pt02.txt")); + EXPECT_EQ(api_error::success, this->provider->remove_directory("/dir01")); + EXPECT_EQ(api_error::success, this->provider->remove_directory("/dir02")); +} + +TYPED_TEST(providers_test, get_file) { + if (this->provider->get_provider_type() == provider_type::encrypt) { + auto source_path = utils::path::combine( + this->config->get_encrypt_config().path, {"test.txt"}); + + std::string api_path{}; + EXPECT_EQ(api_error::success, + this->provider->get_api_path_from_source(source_path, api_path)); api_file file{}; - EXPECT_EQ(api_error::success, provider.get_file(api_path, file)); - decrypt_parts(cfg, file.api_path); - decrypt_parts(cfg, file.api_parent); + EXPECT_EQ(api_error::success, this->provider->get_file(api_path, file)); + decrypt_parts(*this->config, file.api_path); + decrypt_parts(*this->config, file.api_parent); EXPECT_STREQ("/test.txt", file.api_path.c_str()); EXPECT_STREQ("/", file.api_parent.c_str()); @@ -683,10 +746,10 @@ static void get_file(const app_config &cfg, i_provider &provider) { return; } - create_file(provider, "/pt01.txt"); + create_file(*this->provider, "/pt01.txt"); api_file file{}; - EXPECT_EQ(api_error::success, provider.get_file("/pt01.txt", file)); + EXPECT_EQ(api_error::success, this->provider->get_file("/pt01.txt", file)); EXPECT_STREQ("/pt01.txt", file.api_path.c_str()); EXPECT_STREQ("/", file.api_parent.c_str()); @@ -699,91 +762,50 @@ static void get_file(const app_config &cfg, i_provider &provider) { EXPECT_LT(utils::time::get_time_now() - (utils::time::NANOS_PER_SECOND * 5U), file.modified_date); - EXPECT_EQ(api_error::success, provider.remove_file("/pt01.txt")); + EXPECT_EQ(api_error::success, this->provider->remove_file("/pt01.txt")); } - -static void get_file_fails_if_file_not_found(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); +TYPED_TEST(providers_test, get_file_fails_if_file_not_found) { api_file file{}; - EXPECT_EQ(api_error::item_not_found, provider.get_file("/not_found", file)); + EXPECT_EQ(api_error::item_not_found, + this->provider->get_file("/not_found", file)); } -static void get_file_fails_if_item_is_directory(const app_config &cfg, - i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - if (provider.get_provider_type() == provider_type::encrypt) { - auto source_path = - utils::path::combine(cfg.get_encrypt_config().path, {"sub10"}); +TYPED_TEST(providers_test, get_file_fails_if_item_is_directory) { + if (this->provider->get_provider_type() == provider_type::encrypt) { + auto source_path = utils::path::combine( + this->config->get_encrypt_config().path, {"sub10"}); std::string api_path{}; EXPECT_EQ(api_error::success, - provider.get_api_path_from_source(source_path, api_path)); + this->provider->get_api_path_from_source(source_path, api_path)); api_file file{}; - EXPECT_EQ(api_error::directory_exists, provider.get_file(api_path, file)); + EXPECT_EQ(api_error::directory_exists, + this->provider->get_file(api_path, file)); return; } - create_directory(provider, "/dir01"); + create_directory(*this->provider, "/dir01"); api_file file{}; - EXPECT_EQ(api_error::directory_exists, provider.get_file("/dir01", file)); + EXPECT_EQ(api_error::directory_exists, + this->provider->get_file("/dir01", file)); - EXPECT_EQ(api_error::success, provider.remove_directory("/dir01")); + EXPECT_EQ(api_error::success, this->provider->remove_directory("/dir01")); } - -static void get_file_list(const app_config &cfg, i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - - api_file_list list{}; - std::string marker; - EXPECT_EQ(api_error::success, provider.get_file_list(list, marker)); - if (provider.get_provider_type() == provider_type::encrypt) { - EXPECT_EQ(std::size_t(2U), list.size()); - - std::vector expected_parents{ - {"/"}, - {"/sub10"}, - }; - std::vector expected_paths{ - {"/test.txt"}, - {"/sub10/moose.txt"}, - }; - - for (auto &file : list) { - decrypt_parts(cfg, file.api_parent); - decrypt_parts(cfg, file.api_path); - utils::collection::remove_element(expected_parents, file.api_parent); - utils::collection::remove_element(expected_paths, file.api_path); - } - EXPECT_TRUE(expected_parents.empty()); - EXPECT_TRUE(expected_paths.empty()); - } -} - -static void get_file_size(const app_config &cfg, i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - - if (provider.get_provider_type() == provider_type::encrypt) { - auto source_path = - utils::path::combine(cfg.get_encrypt_config().path, {"test.txt"}); +TYPED_TEST(providers_test, get_file_size) { + if (this->provider->get_provider_type() == provider_type::encrypt) { + auto source_path = utils::path::combine( + this->config->get_encrypt_config().path, {"test.txt"}); auto src_size = utils::file::file{source_path}.size(); EXPECT_TRUE(src_size.has_value()); std::string api_path{}; EXPECT_EQ(api_error::success, - provider.get_api_path_from_source(source_path, api_path)); + this->provider->get_api_path_from_source(source_path, api_path)); std::uint64_t size{}; - auto res = provider.get_file_size(api_path, size); + auto res = this->provider->get_file_size(api_path, size); EXPECT_EQ(api_error::success, res); EXPECT_EQ(utils::encryption::encrypting_reader::calculate_encrypted_size( src_size.value(), true), @@ -794,55 +816,48 @@ static void get_file_size(const app_config &cfg, i_provider &provider) { auto &file = test::create_random_file(128U); auto api_path = fmt::format("/{}", utils::path::strip_to_file_name(file.get_path())); - create_file(provider, api_path); + create_file(*this->provider, api_path); stop_type stop_requested{false}; - auto res = provider.upload_file(api_path, file.get_path(), stop_requested); + auto res = + this->provider->upload_file(api_path, file.get_path(), stop_requested); ASSERT_EQ(api_error::success, res); std::uint64_t size{}; - res = provider.get_file_size(api_path, size); + res = this->provider->get_file_size(api_path, size); EXPECT_EQ(api_error::success, res); EXPECT_EQ(*file.size(), size); - res = provider.remove_file(api_path); + res = this->provider->remove_file(api_path); EXPECT_EQ(api_error::success, res); } -static void get_file_size_fails_if_path_not_found(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - +TYPED_TEST(providers_test, get_file_size_fails_if_path_not_found) { std::uint64_t size{}; - auto res = provider.get_file_size("/cow/moose/doge/chicken", size); + auto res = this->provider->get_file_size("/cow/moose/doge/chicken", size); EXPECT_EQ(api_error::item_not_found, res); EXPECT_EQ(0U, size); } -static void get_filesystem_item(const app_config &cfg, i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - - if (provider.get_provider_type() == provider_type::encrypt) { +TYPED_TEST(providers_test, get_filesystem_item) { + if (this->provider->get_provider_type() == provider_type::encrypt) { std::string api_path{}; - EXPECT_EQ( - api_error::success, - provider.get_api_path_from_source( - utils::path::combine(cfg.get_encrypt_config().path, {"test.txt"}), - api_path)); + EXPECT_EQ(api_error::success, + this->provider->get_api_path_from_source( + utils::path::combine(this->config->get_encrypt_config().path, + {"test.txt"}), + api_path)); filesystem_item fsi{}; - auto res = provider.get_filesystem_item(api_path, false, fsi); + auto res = this->provider->get_filesystem_item(api_path, false, fsi); EXPECT_EQ(api_error::success, res); EXPECT_FALSE(fsi.directory); EXPECT_EQ(api_path, fsi.api_path); std::uint64_t size{}; - res = provider.get_file_size(api_path, size); + res = this->provider->get_file_size(api_path, size); ASSERT_EQ(api_error::success, res); EXPECT_EQ(size, fsi.size); @@ -852,74 +867,58 @@ static void get_filesystem_item(const app_config &cfg, i_provider &provider) { auto &file = test::create_random_file(128U); auto api_path = fmt::format("/{}", utils::path::strip_to_file_name(file.get_path())); - create_file(provider, api_path); + create_file(*this->provider, api_path); stop_type stop_requested{false}; - auto res = provider.upload_file(api_path, file.get_path(), stop_requested); + auto res = + this->provider->upload_file(api_path, file.get_path(), stop_requested); ASSERT_EQ(api_error::success, res); EXPECT_EQ(api_error::success, - provider.set_item_meta(api_path, META_SIZE, - std::to_string(*file.size()))); + this->provider->set_item_meta(api_path, META_SIZE, + std::to_string(*file.size()))); filesystem_item fsi{}; - res = provider.get_filesystem_item(api_path, false, fsi); + res = this->provider->get_filesystem_item(api_path, false, fsi); EXPECT_EQ(api_error::success, res); EXPECT_EQ(api_path, fsi.api_path); EXPECT_FALSE(fsi.directory); EXPECT_EQ(*file.size(), fsi.size); - res = provider.remove_file(api_path); + res = this->provider->remove_file(api_path); EXPECT_EQ(api_error::success, res); } -static void get_filesystem_item_root_is_directory(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - +TYPED_TEST(providers_test, get_filesystem_item_root_is_directory) { filesystem_item fsi{}; - auto res = provider.get_filesystem_item("/", true, fsi); + auto res = this->provider->get_filesystem_item("/", true, fsi); EXPECT_EQ(api_error::success, res); EXPECT_TRUE(fsi.directory); EXPECT_EQ("/", fsi.api_path); } -static void -get_filesystem_item_fails_if_file_is_not_found(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - +TYPED_TEST(providers_test, get_filesystem_item_fails_if_file_is_not_found) { filesystem_item fsi{}; - auto res = - provider.get_filesystem_item("/cow/moose/doge/chicken", false, fsi); + auto res = this->provider->get_filesystem_item("/cow/moose/doge/chicken", + false, fsi); EXPECT_EQ(api_error::item_not_found, res); } -static void -get_filesystem_item_fails_if_directory_is_not_found(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - +TYPED_TEST(providers_test, + get_filesystem_item_fails_if_directory_is_not_found) { filesystem_item fsi{}; - auto res = provider.get_filesystem_item("/cow/moose/doge/chicken", true, fsi); + auto res = + this->provider->get_filesystem_item("/cow/moose/doge/chicken", true, fsi); EXPECT_EQ(api_error::directory_not_found, res); } -static void get_filesystem_item_from_source_path(const app_config &cfg, - i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - +TYPED_TEST(providers_test, get_filesystem_item_from_source_path) { std::string api_path; std::string source_path; std::uint64_t size{}; - if (provider.get_provider_type() == provider_type::encrypt) { - source_path = - utils::path::combine(cfg.get_encrypt_config().path, {"test.txt"}); + if (this->provider->get_provider_type() == provider_type::encrypt) { + source_path = utils::path::combine(this->config->get_encrypt_config().path, + {"test.txt"}); auto src_size = utils::file::file{source_path}.size(); EXPECT_TRUE(src_size.has_value()); @@ -931,409 +930,226 @@ static void get_filesystem_item_from_source_path(const app_config &cfg, api_path = fmt::format("/{}", utils::path::strip_to_file_name(file.get_path())); source_path = file.get_path(); - create_file(provider, api_path); + create_file(*this->provider, api_path); - EXPECT_EQ( - api_error::success, - provider.set_item_meta(api_path, { - {META_SIZE, std::to_string(size)}, - {META_SOURCE, source_path}, - })); + EXPECT_EQ(api_error::success, + this->provider->set_item_meta( + api_path, { + {META_SIZE, std::to_string(size)}, + {META_SOURCE, source_path}, + })); stop_type stop_requested{false}; - auto res = provider.upload_file(api_path, source_path, stop_requested); + auto res = + this->provider->upload_file(api_path, source_path, stop_requested); ASSERT_EQ(api_error::success, res); } filesystem_item fsi{}; - auto res = provider.get_filesystem_item_from_source_path(source_path, fsi); + auto res = + this->provider->get_filesystem_item_from_source_path(source_path, fsi); EXPECT_EQ(api_error::success, res); EXPECT_FALSE(fsi.directory); EXPECT_EQ(size, fsi.size); - if (provider.get_provider_type() != provider_type::encrypt) { - EXPECT_EQ(api_error::success, provider.remove_file(api_path)); + if (this->provider->get_provider_type() != provider_type::encrypt) { + EXPECT_EQ(api_error::success, this->provider->remove_file(api_path)); } } - -static void get_filesystem_item_from_source_path_fails_if_file_is_not_found( - i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - +TYPED_TEST(providers_test, + get_filesystem_item_from_source_path_fails_if_file_is_not_found) { filesystem_item fsi{}; - auto res = provider.get_filesystem_item_from_source_path( + auto res = this->provider->get_filesystem_item_from_source_path( "/cow/moose/doge/chicken", fsi); EXPECT_EQ(api_error::item_not_found, res); } -static void get_and_set_item_meta_with_upload_file(const app_config &cfg, - i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - - if (provider.get_provider_type() == provider_type::encrypt) { - std::string api_path{}; - EXPECT_EQ( - api_error::success, - provider.get_api_path_from_source( - utils::path::combine(cfg.get_encrypt_config().path, {"test.txt"}), - api_path)); - - std::string val{}; - EXPECT_EQ(api_error::success, - provider.get_item_meta(api_path, META_SOURCE, val)); - EXPECT_FALSE(val.empty()); - EXPECT_TRUE(utils::file::file{val}.exists()); - - val.clear(); - EXPECT_EQ(api_error::success, - provider.get_item_meta(api_path, META_DIRECTORY, val)); - EXPECT_FALSE(utils::string::to_bool(val)); - return; - } - - auto &file = test::create_random_file(128U); - auto api_path = - fmt::format("/{}", utils::path::strip_to_file_name(file.get_path())); - create_file(provider, api_path); - - stop_type stop_requested{false}; - ASSERT_EQ(api_error::success, - provider.upload_file(api_path, file.get_path(), stop_requested)); - - auto size_str = std::to_string(*file.size()); - EXPECT_EQ(api_error::success, - provider.set_item_meta(api_path, META_SIZE, size_str)); - EXPECT_EQ(api_error::success, - provider.set_item_meta(api_path, META_SOURCE, file.get_path())); - - std::string val{}; - EXPECT_EQ(api_error::success, - provider.get_item_meta(api_path, META_SIZE, val)); - EXPECT_STREQ(size_str.c_str(), val.c_str()); - - val.clear(); - EXPECT_EQ(api_error::success, - provider.get_item_meta(api_path, META_SOURCE, val)); - EXPECT_STREQ(file.get_path().c_str(), val.c_str()); - - EXPECT_EQ(api_error::success, provider.remove_file(api_path)); -} - -static void get_and_set_item_meta2_with_upload_file(const app_config &cfg, - i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - - if (provider.get_provider_type() == provider_type::encrypt) { - std::string api_path{}; - EXPECT_EQ( - api_error::success, - provider.get_api_path_from_source( - utils::path::combine(cfg.get_encrypt_config().path, {"test.txt"}), - api_path)); - - api_meta_map meta{}; - EXPECT_EQ(api_error::success, provider.get_item_meta(api_path, meta)); - EXPECT_TRUE(meta.contains(META_SOURCE)); - EXPECT_TRUE(meta.contains(META_DIRECTORY)); - EXPECT_FALSE(utils::string::to_bool(meta[META_DIRECTORY])); - return; - } - - auto &file = test::create_random_file(64U); - auto api_path = - fmt::format("/{}", utils::path::strip_to_file_name(file.get_path())); - create_file(provider, api_path); - - stop_type stop_requested{false}; - ASSERT_EQ(api_error::success, - provider.upload_file(api_path, file.get_path(), stop_requested)); - - api_meta_map to_set{ - {META_SIZE, std::to_string(*file.size())}, - {META_SOURCE, file.get_path()}, - }; - EXPECT_EQ(api_error::success, provider.set_item_meta(api_path, to_set)); - - api_meta_map meta{}; - EXPECT_EQ(api_error::success, provider.get_item_meta(api_path, meta)); - EXPECT_STREQ(std::to_string(*file.size()).c_str(), meta[META_SIZE].c_str()); - EXPECT_STREQ(file.get_path().c_str(), meta[META_SOURCE].c_str()); - EXPECT_FALSE(utils::string::to_bool(meta[META_DIRECTORY])); - - EXPECT_EQ(api_error::success, provider.remove_file(api_path)); -} - -static void get_item_meta_fails_if_path_not_found(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - - std::string out{}; - EXPECT_EQ(api_error::item_not_found, - provider.get_item_meta("/cow/moose/doge/chicken", META_SIZE, out)); - EXPECT_TRUE(out.empty()); -} - -static void get_item_meta2_fails_if_path_not_found(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - - api_meta_map meta{}; - EXPECT_EQ(api_error::item_not_found, - provider.get_item_meta("/cow/moose/doge/chicken", meta)); - EXPECT_TRUE(meta.empty()); -} - -static void is_file_fails_if_not_found(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - - bool exists{}; - auto res = provider.is_file("/cow/moose/doge/chicken", exists); - - EXPECT_EQ(api_error::success, res); - EXPECT_FALSE(exists); -} - -static void is_directory_fails_if_not_found(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - - bool exists{}; - auto res = provider.is_directory("/cow/moose/doge/chicken", exists); - - EXPECT_EQ(api_error::success, res); - EXPECT_FALSE(exists); -} - -static void remove_file_fails_if_file_not_found(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - - auto res = provider.remove_file("/cow/moose/doge/chicken"); - if (provider.is_read_only()) { +TYPED_TEST(providers_test, remove_file_fails_if_file_not_found) { + auto res = this->provider->remove_file("/cow/moose/doge/chicken"); + if (this->provider->is_read_only()) { EXPECT_EQ(api_error::not_implemented, res); return; } EXPECT_EQ(api_error::item_not_found, res); } -static void remove_file_fails_if_item_is_directory(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - - if (provider.is_read_only()) { - EXPECT_EQ(api_error::not_implemented, provider.remove_file("/dir01")); - return; - } - - create_directory(provider, "/dir01"); - EXPECT_EQ(api_error::directory_exists, provider.remove_file("/dir01")); - EXPECT_EQ(api_error::success, provider.remove_directory("/dir01")); -} - -static void -remove_directory_fails_if_directory_not_found(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - - auto res = provider.remove_directory("/cow/moose/doge/chicken"); - if (provider.is_read_only()) { - EXPECT_EQ(api_error::not_implemented, res); - return; - } - EXPECT_EQ(api_error::item_not_found, res); -} - -static void remove_directory_fails_if_item_is_file(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - - if (provider.is_read_only()) { +TYPED_TEST(providers_test, remove_file_fails_if_item_is_directory) { + if (this->provider->is_read_only()) { EXPECT_EQ(api_error::not_implemented, - provider.remove_directory("/pt01.txt")); + this->provider->remove_file("/dir01")); return; } - create_file(provider, "/pt01.txt"); - EXPECT_EQ(api_error::item_not_found, provider.remove_directory("/pt01.txt")); - EXPECT_EQ(api_error::success, provider.remove_file("/pt01.txt")); + create_directory(*this->provider, "/dir01"); + EXPECT_EQ(api_error::directory_exists, this->provider->remove_file("/dir01")); + EXPECT_EQ(api_error::success, this->provider->remove_directory("/dir01")); } -static void get_pinned_files(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - if (provider.is_read_only()) { - auto pinned = provider.get_pinned_files(); +TYPED_TEST(providers_test, remove_directory_fails_if_item_is_file) { + if (this->provider->is_read_only()) { + EXPECT_EQ(api_error::not_implemented, + this->provider->remove_directory("/pt01.txt")); + return; + } + + create_file(*this->provider, "/pt01.txt"); + EXPECT_EQ(api_error::item_not_found, + this->provider->remove_directory("/pt01.txt")); + EXPECT_EQ(api_error::success, this->provider->remove_file("/pt01.txt")); +} + +TYPED_TEST(providers_test, remove_directory_fails_if_directory_not_found) { + auto res = this->provider->remove_directory("/cow/moose/doge/chicken"); + if (this->provider->is_read_only()) { + EXPECT_EQ(api_error::not_implemented, res); + return; + } + EXPECT_EQ(api_error::item_not_found, res); +} + +TYPED_TEST(providers_test, get_pinned_files) { + if (this->provider->is_read_only()) { + auto pinned = this->provider->get_pinned_files(); EXPECT_TRUE(pinned.empty()); return; } - create_file(provider, "/pin01.txt"); - create_file(provider, "/pin02.txt"); - create_file(provider, "/nopin01.txt"); + create_file(*this->provider, "/pin01.txt"); + create_file(*this->provider, "/pin02.txt"); + create_file(*this->provider, "/nopin01.txt"); EXPECT_EQ(api_error::success, - provider.set_item_meta("/pin01.txt", META_PINNED, "true")); + this->provider->set_item_meta("/pin01.txt", META_PINNED, "true")); EXPECT_EQ(api_error::success, - provider.set_item_meta("/pin02.txt", META_PINNED, "true")); - EXPECT_EQ(api_error::success, - provider.set_item_meta("/nopin01.txt", META_PINNED, "false")); + this->provider->set_item_meta("/pin02.txt", META_PINNED, "true")); + EXPECT_EQ(api_error::success, this->provider->set_item_meta( + "/nopin01.txt", META_PINNED, "false")); - auto pinned = provider.get_pinned_files(); + auto pinned = this->provider->get_pinned_files(); EXPECT_EQ(std::size_t(2U), pinned.size()); EXPECT_TRUE(pinned_includes_api_path(pinned, "/pin01.txt")); EXPECT_TRUE(pinned_includes_api_path(pinned, "/pin02.txt")); EXPECT_FALSE(pinned_includes_api_path(pinned, "/nopin01.txt")); - EXPECT_EQ(api_error::success, provider.remove_file("/pin01.txt")); - EXPECT_EQ(api_error::success, provider.remove_file("/pin02.txt")); - EXPECT_EQ(api_error::success, provider.remove_file("/nopin01.txt")); + EXPECT_EQ(api_error::success, this->provider->remove_file("/pin01.txt")); + EXPECT_EQ(api_error::success, this->provider->remove_file("/pin02.txt")); + EXPECT_EQ(api_error::success, this->provider->remove_file("/nopin01.txt")); } -static void remove_pin_updates_pinned_files(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - - if (provider.is_read_only()) { - auto pinned = provider.get_pinned_files(); +TYPED_TEST(providers_test, remove_pin_updates_pinned_files) { + if (this->provider->is_read_only()) { + auto pinned = this->provider->get_pinned_files(); EXPECT_TRUE(pinned.empty()); return; } - create_file(provider, "/pin01.txt"); - create_file(provider, "/pin02.txt"); + create_file(*this->provider, "/pin01.txt"); + create_file(*this->provider, "/pin02.txt"); EXPECT_EQ(api_error::success, - provider.set_item_meta("/pin01.txt", META_PINNED, "true")); + this->provider->set_item_meta("/pin01.txt", META_PINNED, "true")); EXPECT_EQ(api_error::success, - provider.set_item_meta("/pin02.txt", META_PINNED, "true")); + this->provider->set_item_meta("/pin02.txt", META_PINNED, "true")); - auto pinned = provider.get_pinned_files(); + auto pinned = this->provider->get_pinned_files(); EXPECT_EQ(std::size_t(2U), pinned.size()); EXPECT_EQ(api_error::success, - provider.set_item_meta("/pin02.txt", META_PINNED, "false")); - pinned = provider.get_pinned_files(); + this->provider->set_item_meta("/pin02.txt", META_PINNED, "false")); + pinned = this->provider->get_pinned_files(); EXPECT_EQ(std::size_t(1U), pinned.size()); EXPECT_TRUE(pinned_includes_api_path(pinned, "/pin01.txt")); EXPECT_FALSE(pinned_includes_api_path(pinned, "/pin02.txt")); EXPECT_EQ(api_error::success, - provider.set_item_meta("/pin01.txt", META_PINNED, "false")); - pinned = provider.get_pinned_files(); + this->provider->set_item_meta("/pin01.txt", META_PINNED, "false")); + pinned = this->provider->get_pinned_files(); EXPECT_TRUE(pinned.empty()); - EXPECT_EQ(api_error::success, provider.remove_file("/pin01.txt")); - EXPECT_EQ(api_error::success, provider.remove_file("/pin02.txt")); + EXPECT_EQ(api_error::success, this->provider->remove_file("/pin01.txt")); + EXPECT_EQ(api_error::success, this->provider->remove_file("/pin02.txt")); } -static void remove_file_updates_pinned_files(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - - if (provider.is_read_only()) { - auto pinned = provider.get_pinned_files(); +TYPED_TEST(providers_test, remove_file_updates_pinned_files) { + if (this->provider->is_read_only()) { + auto pinned = this->provider->get_pinned_files(); EXPECT_TRUE(pinned.empty()); return; } - create_file(provider, "/pin_keep.txt"); - create_file(provider, "/pin_delete.txt"); - create_file(provider, "/nopin.txt"); + create_file(*this->provider, "/pin_keep.txt"); + create_file(*this->provider, "/pin_delete.txt"); + create_file(*this->provider, "/nopin.txt"); + EXPECT_EQ(api_error::success, this->provider->set_item_meta( + "/pin_keep.txt", META_PINNED, "true")); + EXPECT_EQ(api_error::success, this->provider->set_item_meta( + "/pin_delete.txt", META_PINNED, "true")); EXPECT_EQ(api_error::success, - provider.set_item_meta("/pin_keep.txt", META_PINNED, "true")); - EXPECT_EQ(api_error::success, - provider.set_item_meta("/pin_delete.txt", META_PINNED, "true")); - EXPECT_EQ(api_error::success, - provider.set_item_meta("/nopin.txt", META_PINNED, "false")); + this->provider->set_item_meta("/nopin.txt", META_PINNED, "false")); - auto pinned = provider.get_pinned_files(); + auto pinned = this->provider->get_pinned_files(); EXPECT_EQ(std::size_t(2U), pinned.size()); EXPECT_TRUE(pinned_includes_api_path(pinned, "/pin_keep.txt")); EXPECT_TRUE(pinned_includes_api_path(pinned, "/pin_delete.txt")); EXPECT_FALSE(pinned_includes_api_path(pinned, "/nopin.txt")); - EXPECT_EQ(api_error::success, provider.remove_file("/pin_delete.txt")); + EXPECT_EQ(api_error::success, this->provider->remove_file("/pin_delete.txt")); - pinned = provider.get_pinned_files(); + pinned = this->provider->get_pinned_files(); EXPECT_EQ(std::size_t(1U), pinned.size()); EXPECT_TRUE(pinned_includes_api_path(pinned, "/pin_keep.txt")); EXPECT_FALSE(pinned_includes_api_path(pinned, "/pin_delete.txt")); - EXPECT_EQ(api_error::success, provider.remove_file("/pin_keep.txt")); - EXPECT_EQ(api_error::success, provider.remove_file("/nopin.txt")); + EXPECT_EQ(api_error::success, this->provider->remove_file("/pin_keep.txt")); + EXPECT_EQ(api_error::success, this->provider->remove_file("/nopin.txt")); } -static void get_total_item_count(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - - if (provider.get_provider_type() == provider_type::encrypt) { +TYPED_TEST(providers_test, get_total_item_count) { + if (this->provider->get_provider_type() == provider_type::encrypt) { // TODO revisit - /* std::uint64_t count{provider.get_total_item_count()}; + /* std::uint64_t count{this->provider->get_total_item_count()}; EXPECT_EQ(3U, count); */ return; } - std::uint64_t before{provider.get_total_item_count()}; + std::uint64_t before{this->provider->get_total_item_count()}; - create_file(provider, "/count01.txt"); - create_file(provider, "/count02.txt"); + create_file(*this->provider, "/count01.txt"); + create_file(*this->provider, "/count02.txt"); - std::uint64_t mid{provider.get_total_item_count()}; + std::uint64_t mid{this->provider->get_total_item_count()}; EXPECT_EQ(before + 2U, mid); - EXPECT_EQ(api_error::success, provider.remove_file("/count01.txt")); - EXPECT_EQ(api_error::success, provider.remove_file("/count02.txt")); + EXPECT_EQ(api_error::success, this->provider->remove_file("/count01.txt")); + EXPECT_EQ(api_error::success, this->provider->remove_file("/count02.txt")); - std::uint64_t after{provider.get_total_item_count()}; + std::uint64_t after{this->provider->get_total_item_count()}; EXPECT_EQ(before, after); } -static void get_used_drive_space(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - if (provider.is_read_only()) { +TYPED_TEST(providers_test, get_used_drive_space) { + if (this->provider->is_read_only()) { // TODO revisit /* api_file_list list{}; std::string marker; - EXPECT_EQ(api_error::success, provider.get_file_list(list, marker)); + EXPECT_EQ(api_error::success, this->provider->get_file_list(list, marker)); std::uint64_t sum_sizes{}; for (const auto &file : list) { std::uint64_t size{}; EXPECT_EQ(api_error::success, - provider.get_file_size(file.api_path, size)); + this->provider->get_file_size(file.api_path, size)); sum_sizes += size; } - std::uint64_t used{provider.get_used_drive_space()}; + std::uint64_t used{this->provider->get_used_drive_space()}; EXPECT_EQ(sum_sizes, used); */ return; } - std::uint64_t before{provider.get_used_drive_space()}; + std::uint64_t before{this->provider->get_used_drive_space()}; auto &file1 = test::create_random_file(96U); auto &file2 = test::create_random_file(128U); @@ -1343,517 +1159,231 @@ static void get_used_drive_space(i_provider &provider) { auto api_path2 = fmt::format("/{}", utils::path::strip_to_file_name(file2.get_path())); - create_file(provider, api_path1); - create_file(provider, api_path2); + create_file(*this->provider, api_path1); + create_file(*this->provider, api_path2); stop_type stop_requested{false}; - ASSERT_EQ(api_error::success, - provider.upload_file(api_path1, file1.get_path(), stop_requested)); - ASSERT_EQ(api_error::success, - provider.upload_file(api_path2, file2.get_path(), stop_requested)); + ASSERT_EQ( + api_error::success, + this->provider->upload_file(api_path1, file1.get_path(), stop_requested)); + ASSERT_EQ( + api_error::success, + this->provider->upload_file(api_path2, file2.get_path(), stop_requested)); EXPECT_EQ(api_error::success, - provider.set_item_meta(api_path1, META_SIZE, - std::to_string(*file1.size()))); + this->provider->set_item_meta(api_path1, META_SIZE, + std::to_string(*file1.size()))); EXPECT_EQ(api_error::success, - provider.set_item_meta(api_path2, META_SIZE, - std::to_string(*file2.size()))); + this->provider->set_item_meta(api_path2, META_SIZE, + std::to_string(*file2.size()))); - std::uint64_t mid{provider.get_used_drive_space()}; + std::uint64_t mid{this->provider->get_used_drive_space()}; EXPECT_EQ(before + *file1.size() + *file2.size(), mid); - EXPECT_EQ(api_error::success, provider.remove_file(api_path1)); - EXPECT_EQ(api_error::success, provider.remove_file(api_path2)); + EXPECT_EQ(api_error::success, this->provider->remove_file(api_path1)); + EXPECT_EQ(api_error::success, this->provider->remove_file(api_path2)); - std::uint64_t after{provider.get_used_drive_space()}; + std::uint64_t after{this->provider->get_used_drive_space()}; EXPECT_EQ(before, after); } -static void get_total_drive_space(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - - std::uint64_t total{provider.get_total_drive_space()}; - std::uint64_t used{provider.get_used_drive_space()}; +TYPED_TEST(providers_test, get_total_drive_space) { + std::uint64_t total{this->provider->get_total_drive_space()}; + std::uint64_t used{this->provider->get_used_drive_space()}; if (total != 0U) { EXPECT_GE(total, used); } } -static void remove_item_meta(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - +TYPED_TEST(providers_test, remove_item_meta) { std::string api_path{"/rim_custom_ok.txt"}; - if (provider.get_provider_type() == provider_type::encrypt) { + if (this->provider->get_provider_type() == provider_type::encrypt) { EXPECT_EQ(api_error::success, - provider.remove_item_meta(api_path, "user.custom")); + this->provider->remove_item_meta(api_path, "user.custom")); return; } - create_file(provider, api_path); + create_file(*this->provider, api_path); EXPECT_EQ(api_error::success, - provider.set_item_meta(api_path, "user.custom", "abc123")); + this->provider->set_item_meta(api_path, "user.custom", "abc123")); api_meta_map before{}; - EXPECT_EQ(api_error::success, provider.get_item_meta(api_path, before)); + EXPECT_EQ(api_error::success, + this->provider->get_item_meta(api_path, before)); EXPECT_TRUE(before.contains("user.custom")); EXPECT_EQ(api_error::success, - provider.remove_item_meta(api_path, "user.custom")); + this->provider->remove_item_meta(api_path, "user.custom")); api_meta_map after{}; - EXPECT_EQ(api_error::success, provider.get_item_meta(api_path, after)); + EXPECT_EQ(api_error::success, this->provider->get_item_meta(api_path, after)); EXPECT_FALSE(after.contains("user.custom")); - EXPECT_EQ(api_error::success, provider.remove_file(api_path)); + EXPECT_EQ(api_error::success, this->provider->remove_file(api_path)); } -static void remove_item_meta_path_not_found(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - if (provider.get_provider_type() == provider_type::encrypt) { - EXPECT_EQ( - api_error::success, - provider.remove_item_meta("/cow_moose_doge_chicken", "user.custom")); +TYPED_TEST(providers_test, remove_item_meta_path_not_found) { + if (this->provider->get_provider_type() == provider_type::encrypt) { + EXPECT_EQ(api_error::success, + this->provider->remove_item_meta("/cow_moose_doge_chicken", + "user.custom")); return; } - auto res = - provider.remove_item_meta("/cow_moose_doge_chicken", "user.custom"); + auto res = this->provider->remove_item_meta("/cow_moose_doge_chicken", + "user.custom"); EXPECT_EQ(api_error::item_not_found, res); } -static void remove_item_meta_restricted_names_fail(const app_config &cfg, - i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - +TYPED_TEST(providers_test, remove_item_meta_restricted_names_fail) { std::string api_path; - if (provider.get_provider_type() == provider_type::encrypt) { - auto source_path = - utils::path::combine(cfg.get_encrypt_config().path, {"test.txt"}); + if (this->provider->get_provider_type() == provider_type::encrypt) { + auto source_path = utils::path::combine( + this->config->get_encrypt_config().path, {"test.txt"}); EXPECT_EQ(api_error::success, - provider.get_api_path_from_source(source_path, api_path)); + this->provider->get_api_path_from_source(source_path, api_path)); } else { api_path = "/rim_restricted.txt"; - create_file(provider, api_path); + create_file(*this->provider, api_path); } for (const auto &key : META_USED_NAMES) { - auto res = provider.remove_item_meta(api_path, std::string{key}); + auto res = this->provider->remove_item_meta(api_path, std::string{key}); EXPECT_EQ(api_error::permission_denied, res); api_meta_map meta{}; - EXPECT_EQ(api_error::success, provider.get_item_meta(api_path, meta)); + EXPECT_EQ(api_error::success, + this->provider->get_item_meta(api_path, meta)); EXPECT_TRUE(meta.contains(std::string{key})); } - if (provider.get_provider_type() != provider_type::encrypt) { - EXPECT_EQ(api_error::success, provider.remove_file(api_path)); + if (this->provider->get_provider_type() != provider_type::encrypt) { + EXPECT_EQ(api_error::success, this->provider->remove_file(api_path)); } } -static void rename_file(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - - if (not provider.is_rename_supported()) { - auto res = provider.rename_file("/rn_src.txt", "/rn_dst.txt"); +TYPED_TEST(providers_test, rename_file) { + if (not this->provider->is_rename_supported()) { + auto res = this->provider->rename_file("/rn_src.txt", "/rn_dst.txt"); EXPECT_EQ(api_error::not_implemented, res); return; } std::string src{"/rn_src.txt"}; std::string dst{"/rn_dst.txt"}; - create_file(provider, src); + create_file(*this->provider, src); std::string src_meta_size{}; std::string src_meta_source{}; EXPECT_EQ(api_error::success, - provider.get_item_meta(src, META_SIZE, src_meta_size)); + this->provider->get_item_meta(src, META_SIZE, src_meta_size)); EXPECT_EQ(api_error::success, - provider.get_item_meta(src, META_SOURCE, src_meta_source)); + this->provider->get_item_meta(src, META_SOURCE, src_meta_source)); - EXPECT_EQ(api_error::success, provider.rename_file(src, dst)); + EXPECT_EQ(api_error::success, this->provider->rename_file(src, dst)); bool exists{}; - EXPECT_EQ(api_error::success, provider.is_file(src, exists)); + EXPECT_EQ(api_error::success, this->provider->is_file(src, exists)); EXPECT_FALSE(exists); - EXPECT_EQ(api_error::success, provider.is_file(dst, exists)); + EXPECT_EQ(api_error::success, this->provider->is_file(dst, exists)); EXPECT_TRUE(exists); std::string dst_meta_size{}; std::string dst_meta_source{}; EXPECT_EQ(api_error::success, - provider.get_item_meta(dst, META_SIZE, dst_meta_size)); + this->provider->get_item_meta(dst, META_SIZE, dst_meta_size)); EXPECT_EQ(api_error::success, - provider.get_item_meta(dst, META_SOURCE, dst_meta_source)); + this->provider->get_item_meta(dst, META_SOURCE, dst_meta_source)); EXPECT_STREQ(src_meta_size.c_str(), dst_meta_size.c_str()); EXPECT_STREQ(src_meta_source.c_str(), dst_meta_source.c_str()); - EXPECT_EQ(api_error::success, provider.remove_file(dst)); + EXPECT_EQ(api_error::success, this->provider->remove_file(dst)); } -static void rename_file_fails_if_source_not_found(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - - if (not provider.is_rename_supported()) { - auto res = provider.rename_file("/rn_missing.txt", "/rn_any.txt"); +TYPED_TEST(providers_test, rename_file_fails_if_source_not_found) { + if (not this->provider->is_rename_supported()) { + auto res = this->provider->rename_file("/rn_missing.txt", "/rn_any.txt"); EXPECT_EQ(api_error::not_implemented, res); return; } - auto res = provider.rename_file("/rn_missing.txt", "/rn_any.txt"); + auto res = this->provider->rename_file("/rn_missing.txt", "/rn_any.txt"); EXPECT_EQ(api_error::item_not_found, res); } -static void rename_file_fails_if_destination_exists(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - - if (not provider.is_rename_supported()) { - if (provider.get_provider_type() != provider_type::encrypt) { - create_file(provider, "/rn_src_conflict.txt"); - create_file(provider, "/rn_dst_conflict.txt"); +TYPED_TEST(providers_test, rename_file_fails_if_destination_exists) { + if (not this->provider->is_rename_supported()) { + if (this->provider->get_provider_type() != provider_type::encrypt) { + create_file(*this->provider, "/rn_src_conflict.txt"); + create_file(*this->provider, "/rn_dst_conflict.txt"); } - auto res = - provider.rename_file("/rn_src_conflict.txt", "/rn_dst_conflict.txt"); + auto res = this->provider->rename_file("/rn_src_conflict.txt", + "/rn_dst_conflict.txt"); EXPECT_EQ(api_error::not_implemented, res); - if (provider.get_provider_type() != provider_type::encrypt) { + if (this->provider->get_provider_type() != provider_type::encrypt) { EXPECT_EQ(api_error::success, - provider.remove_file("/rn_src_conflict.txt")); + this->provider->remove_file("/rn_src_conflict.txt")); EXPECT_EQ(api_error::success, - provider.remove_file("/rn_dst_conflict.txt")); + this->provider->remove_file("/rn_dst_conflict.txt")); } return; } std::string src{"/rn_src_conflict.txt"}; std::string dst{"/rn_dst_conflict.txt"}; - create_file(provider, src); - create_file(provider, dst); + create_file(*this->provider, src); + create_file(*this->provider, dst); - auto res = provider.rename_file(src, dst); + auto res = this->provider->rename_file(src, dst); EXPECT_EQ(api_error::item_exists, res); bool exists{}; - EXPECT_EQ(api_error::success, provider.is_file(src, exists)); + EXPECT_EQ(api_error::success, this->provider->is_file(src, exists)); EXPECT_TRUE(exists); - EXPECT_EQ(api_error::success, provider.is_file(dst, exists)); + EXPECT_EQ(api_error::success, this->provider->is_file(dst, exists)); EXPECT_TRUE(exists); - EXPECT_EQ(api_error::success, provider.remove_file(src)); - EXPECT_EQ(api_error::success, provider.remove_file(dst)); + EXPECT_EQ(api_error::success, this->provider->remove_file(src)); + EXPECT_EQ(api_error::success, this->provider->remove_file(dst)); } -static void -rename_file_fails_if_destination_is_directory(i_provider &provider) { - fmt::println("testing|{}|{}", - app_config::get_provider_name(provider.get_provider_type()), - __FUNCTION__); - - if (not provider.is_rename_supported()) { - if (provider.get_provider_type() != provider_type::encrypt) { - create_file(provider, "/rn_src_conflict.txt"); - create_directory(provider, "/rn_dst_conflict"); +TYPED_TEST(providers_test, rename_file_fails_if_destination_is_directory) { + if (not this->provider->is_rename_supported()) { + if (this->provider->get_provider_type() != provider_type::encrypt) { + create_file(*this->provider, "/rn_src_conflict.txt"); + create_directory(*this->provider, "/rn_dst_conflict"); } - auto res = provider.rename_file("/rn_src_conflict.txt", "/rn_dst_conflict"); + auto res = + this->provider->rename_file("/rn_src_conflict.txt", "/rn_dst_conflict"); EXPECT_EQ(api_error::not_implemented, res); - if (provider.get_provider_type() != provider_type::encrypt) { + if (this->provider->get_provider_type() != provider_type::encrypt) { EXPECT_EQ(api_error::success, - provider.remove_file("/rn_src_conflict.txt")); + this->provider->remove_file("/rn_src_conflict.txt")); EXPECT_EQ(api_error::success, - provider.remove_directory("/rn_dst_conflict")); + this->provider->remove_directory("/rn_dst_conflict")); } return; } std::string src{"/rn_src_conflict.txt"}; std::string dst{"/rn_dst_conflict"}; - create_file(provider, src); - create_directory(provider, dst); + create_file(*this->provider, src); + create_directory(*this->provider, dst); - auto res = provider.rename_file(src, dst); + auto res = this->provider->rename_file(src, dst); EXPECT_EQ(api_error::directory_exists, res); bool exists{}; - EXPECT_EQ(api_error::success, provider.is_file(src, exists)); + EXPECT_EQ(api_error::success, this->provider->is_file(src, exists)); EXPECT_TRUE(exists); - EXPECT_EQ(api_error::success, provider.is_directory(dst, exists)); + EXPECT_EQ(api_error::success, this->provider->is_directory(dst, exists)); EXPECT_TRUE(exists); - EXPECT_EQ(api_error::success, provider.remove_file(src)); - EXPECT_EQ(api_error::success, provider.remove_directory(dst)); -} - -static void run_tests(const app_config &cfg, i_provider &provider) { - // MOVED - get_file_list(cfg, provider); - - // MOVED - get_and_set_item_meta_with_upload_file(cfg, provider); - // MOVED - get_and_set_item_meta2_with_upload_file(cfg, provider); - get_item_meta_fails_if_path_not_found(provider); - get_item_meta2_fails_if_path_not_found(provider); - - is_file_fails_if_not_found(provider); - is_directory_fails_if_not_found(provider); - - // MOVED - can_create_and_remove_directory(provider); - can_create_and_remove_file(provider); - - create_directory_fails_if_already_exists(provider); - create_directory_fails_if_file_already_exists(provider); - - create_directory_clone_source_meta(provider); - create_directory_clone_source_meta_fails_if_already_exists(provider); - create_directory_clone_source_meta_fails_if_directory_not_found(provider); - create_directory_clone_source_meta_fails_if_file_already_exists(provider); - - create_file_fails_if_already_exists(provider); - create_file_fails_if_directory_already_exists(provider); - - get_api_path_from_source(cfg, provider); - get_api_path_from_source_fails_if_file_not_found(cfg, provider); - - get_directory_items(cfg, provider); - get_directory_items_fails_if_directory_not_found(provider); - get_directory_items_fails_if_item_is_file(cfg, provider); - - get_directory_item_count(cfg, provider); - - get_file(cfg, provider); - get_file_fails_if_file_not_found(provider); - get_file_fails_if_item_is_directory(cfg, provider); - - get_file_size(cfg, provider); - get_file_size_fails_if_path_not_found(provider); - - get_filesystem_item(cfg, provider); - get_filesystem_item_root_is_directory(provider); - get_filesystem_item_fails_if_file_is_not_found(provider); - get_filesystem_item_fails_if_directory_is_not_found(provider); - - get_filesystem_item_from_source_path(cfg, provider); - get_filesystem_item_from_source_path_fails_if_file_is_not_found(provider); - - remove_file_fails_if_file_not_found(provider); - remove_file_fails_if_item_is_directory(provider); - remove_directory_fails_if_directory_not_found(provider); - remove_directory_fails_if_item_is_file(provider); - - get_pinned_files(provider); - remove_pin_updates_pinned_files(provider); - remove_file_updates_pinned_files(provider); - - get_total_item_count(provider); - get_used_drive_space(provider); - get_total_drive_space(provider); - - remove_item_meta(provider); - remove_item_meta_path_not_found(provider); - remove_item_meta_restricted_names_fail(cfg, provider); - - rename_file(provider); - rename_file_fails_if_source_not_found(provider); - rename_file_fails_if_destination_exists(provider); - rename_file_fails_if_destination_is_directory(provider); - - // TODO need to test read when file size changes for encrypt provider - /* - read_file_bytes(provider); - */ -} - -TEST(providers_test, encrypt_provider) { - auto config_path = utils::path::combine(test::get_test_output_dir(), - {"provider", "encrypt"}); - console_consumer consumer{}; - event_system::instance().start(); - - { - app_config cfg(provider_type::encrypt, config_path); - - auto encrypt_path = - utils::path::combine(test::get_test_input_dir(), {"encrypt"}); - - EXPECT_STREQ( - encrypt_path.c_str(), - cfg.set_value_by_name("EncryptConfig.Path", encrypt_path).c_str()); - EXPECT_STREQ( - "test_token", - cfg.set_value_by_name("EncryptConfig.EncryptionToken", "test_token") - .c_str()); - - encrypt_provider provider{cfg}; - file_manager mgr(cfg, provider); - - EXPECT_TRUE(provider.start( - [&provider](bool directory, api_file &file) -> api_error { - return provider_meta_handler(provider, directory, file); - }, - &mgr)); - - mgr.start(); - - EXPECT_EQ(provider_type::encrypt, provider.get_provider_type()); - EXPECT_TRUE(provider.is_read_only()); - EXPECT_TRUE(provider.is_online()); - EXPECT_FALSE(provider.is_rename_supported()); - - run_tests(cfg, provider); - - provider.stop(); - mgr.stop(); - } - - event_system::instance().stop(); -} - -TEST(providers_test, s3_provider_unencrypted) { - auto config_path = - utils::path::combine(test::get_test_output_dir(), {"provider", "s3"}); - - console_consumer consumer{}; - event_system::instance().start(); - - { - app_config cfg(provider_type::s3, config_path); - { - app_config src_cfg( - provider_type::s3, - utils::path::combine(test::get_test_config_dir(), {"s3"})); - auto s3_cfg = src_cfg.get_s3_config(); - s3_cfg.encryption_token = ""; - cfg.set_s3_config(s3_cfg); - } - - curl_comm comm{cfg.get_s3_config()}; - - s3_provider provider{cfg, comm}; - file_manager mgr(cfg, provider); - - EXPECT_TRUE(provider.start( - [&provider](bool directory, api_file &file) -> api_error { - return provider_meta_handler(provider, directory, file); - }, - &mgr)); - - mgr.start(); - - EXPECT_EQ(provider_type::s3, provider.get_provider_type()); - EXPECT_FALSE(provider.is_read_only()); - EXPECT_TRUE(provider.is_online()); - EXPECT_FALSE(provider.is_rename_supported()); - - run_tests(cfg, provider); - - provider.stop(); - mgr.stop(); - } - - event_system::instance().stop(); -} - -TEST(providers_test, s3_provider_encrypted) { - auto config_path = - utils::path::combine(test::get_test_output_dir(), {"provider", "s3"}); - - console_consumer consumer{}; - event_system::instance().start(); - - { - app_config cfg(provider_type::s3, config_path); - { - app_config src_cfg( - provider_type::s3, - utils::path::combine(test::get_test_config_dir(), {"s3"})); - auto s3_cfg = src_cfg.get_s3_config(); - s3_cfg.encryption_token = "cow_moose_doge_chicken"; - cfg.set_s3_config(s3_cfg); - } - - curl_comm comm{cfg.get_s3_config()}; - - s3_provider provider{cfg, comm}; - file_manager mgr(cfg, provider); - - EXPECT_TRUE(provider.start( - [&provider](bool directory, api_file &file) -> api_error { - return provider_meta_handler(provider, directory, file); - }, - &mgr)); - - mgr.start(); - - EXPECT_EQ(provider_type::s3, provider.get_provider_type()); - EXPECT_FALSE(provider.is_read_only()); - EXPECT_TRUE(provider.is_online()); - EXPECT_FALSE(provider.is_rename_supported()); - - run_tests(cfg, provider); - - provider.stop(); - mgr.stop(); - } - - event_system::instance().stop(); -} - -TEST(providers_test, sia_provider) { - auto config_path = - utils::path::combine(test::get_test_output_dir(), {"provider", "sia"}); - - console_consumer consumer{}; - event_system::instance().start(); - - { - app_config cfg(provider_type::sia, config_path); - { - app_config src_cfg( - provider_type::sia, - utils::path::combine(test::get_test_config_dir(), {"sia"})); - cfg.set_host_config(src_cfg.get_host_config()); - cfg.set_sia_config(src_cfg.get_sia_config()); - } - - curl_comm comm{cfg.get_host_config()}; - - sia_provider provider{cfg, comm}; - file_manager mgr(cfg, provider); - EXPECT_TRUE(provider.start( - [&provider](bool directory, api_file &file) -> api_error { - return provider_meta_handler(provider, directory, file); - }, - &mgr)); - - mgr.start(); - EXPECT_EQ(provider_type::sia, provider.get_provider_type()); - EXPECT_FALSE(provider.is_read_only()); - EXPECT_TRUE(provider.is_online()); - EXPECT_TRUE(provider.is_rename_supported()); - - run_tests(cfg, provider); - - provider.stop(); - mgr.stop(); - } - - event_system::instance().stop(); + EXPECT_EQ(api_error::success, this->provider->remove_file(src)); + EXPECT_EQ(api_error::success, this->provider->remove_directory(dst)); } } // namespace repertory From 26830963c4b44497ce7f878cd3ec5e88eff65324 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Thu, 18 Sep 2025 15:06:19 -0500 Subject: [PATCH 054/136] [unit test] Complete all providers unit tests #12 --- repertory/repertory_test/include/fixtures/providers_fixture.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/repertory/repertory_test/include/fixtures/providers_fixture.hpp b/repertory/repertory_test/include/fixtures/providers_fixture.hpp index 7d57ba15..ed1cd7fc 100644 --- a/repertory/repertory_test/include/fixtures/providers_fixture.hpp +++ b/repertory/repertory_test/include/fixtures/providers_fixture.hpp @@ -25,6 +25,7 @@ #include "test_common.hpp" #include "comm/curl/curl_comm.hpp" +#include "comm/i_http_com.hpp" #include "events/event_system.hpp" #include "file_manager/file_manager.hpp" #include "fixtures/providers_fixture.hpp" From 0809e6129063c5c1f75773cb4ef6c444d6cc2f1e Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Thu, 18 Sep 2025 15:09:34 -0500 Subject: [PATCH 055/136] fix --- repertory/repertory_test/include/fixtures/providers_fixture.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repertory/repertory_test/include/fixtures/providers_fixture.hpp b/repertory/repertory_test/include/fixtures/providers_fixture.hpp index ed1cd7fc..ca30b0ad 100644 --- a/repertory/repertory_test/include/fixtures/providers_fixture.hpp +++ b/repertory/repertory_test/include/fixtures/providers_fixture.hpp @@ -25,7 +25,7 @@ #include "test_common.hpp" #include "comm/curl/curl_comm.hpp" -#include "comm/i_http_com.hpp" +#include "comm/i_http_comm.hpp" #include "events/event_system.hpp" #include "file_manager/file_manager.hpp" #include "fixtures/providers_fixture.hpp" From 769f1621ca65277473e51c77a079567b5124cb92 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Thu, 18 Sep 2025 15:12:49 -0500 Subject: [PATCH 056/136] fix --- .../include/fixtures/providers_fixture.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/repertory/repertory_test/include/fixtures/providers_fixture.hpp b/repertory/repertory_test/include/fixtures/providers_fixture.hpp index ca30b0ad..78a1b3fc 100644 --- a/repertory/repertory_test/include/fixtures/providers_fixture.hpp +++ b/repertory/repertory_test/include/fixtures/providers_fixture.hpp @@ -45,7 +45,7 @@ namespace repertory { struct encrypt_provider_type final { static constexpr provider_type type{provider_type::encrypt}; - static void setup(std::unique_ptr &comm, + static void setup(std::unique_ptr &comm, std::unique_ptr &config, std::unique_ptr &provider) { auto config_path = @@ -77,7 +77,7 @@ struct encrypt_provider_type final { struct s3_provider_encrypted_type final { static constexpr provider_type type{provider_type::s3}; - static void setup(std::unique_ptr &comm, + static void setup(std::unique_ptr &comm, std::unique_ptr &config, std::unique_ptr &provider) { auto config_path = @@ -107,7 +107,7 @@ struct s3_provider_encrypted_type final { struct s3_provider_unencrypted_type final { static constexpr provider_type type{provider_type::s3}; - static void setup(std::unique_ptr &comm, + static void setup(std::unique_ptr &comm, std::unique_ptr &config, std::unique_ptr &provider) { auto config_path = @@ -137,7 +137,7 @@ struct s3_provider_unencrypted_type final { struct sia_provider_type final { static constexpr provider_type type{provider_type::sia}; - static void setup(std::unique_ptr &comm, + static void setup(std::unique_ptr &comm, std::unique_ptr &config, std::unique_ptr &provider) { auto config_path = @@ -167,7 +167,7 @@ struct sia_provider_type final { template class providers_test : public ::testing::Test { public: - static std::unique_ptr comm; + static std::unique_ptr comm; static std::unique_ptr config; static std::unique_ptr mgr; static std::unique_ptr provider; @@ -203,7 +203,7 @@ protected: }; template -std::unique_ptr providers_test::comm; +std::unique_ptr providers_test::comm; template std::unique_ptr providers_test::config; From 2d6f6a5dc7ecd7243a94f4b873434238bd45d186 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Thu, 18 Sep 2025 15:13:55 -0500 Subject: [PATCH 057/136] fix --- .../repertory_test/include/fixtures/providers_fixture.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/repertory/repertory_test/include/fixtures/providers_fixture.hpp b/repertory/repertory_test/include/fixtures/providers_fixture.hpp index 78a1b3fc..db973863 100644 --- a/repertory/repertory_test/include/fixtures/providers_fixture.hpp +++ b/repertory/repertory_test/include/fixtures/providers_fixture.hpp @@ -169,9 +169,9 @@ template class providers_test : public ::testing::Test { public: static std::unique_ptr comm; static std::unique_ptr config; + static console_consumer consumer; static std::unique_ptr mgr; static std::unique_ptr provider; - static console_consumer consumer{}; protected: static void SetUpTestCase() { @@ -208,6 +208,9 @@ std::unique_ptr providers_test::comm; template std::unique_ptr providers_test::config; +template +console_consumer providers_test::consumer; + template std::unique_ptr providers_test::mgr; From 6948f2730976dd956e8bb14e16b5e3b9e53e19ec Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Thu, 18 Sep 2025 15:18:26 -0500 Subject: [PATCH 058/136] refactor --- repertory/repertory_test/include/fixtures/providers_fixture.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repertory/repertory_test/include/fixtures/providers_fixture.hpp b/repertory/repertory_test/include/fixtures/providers_fixture.hpp index db973863..371c78e5 100644 --- a/repertory/repertory_test/include/fixtures/providers_fixture.hpp +++ b/repertory/repertory_test/include/fixtures/providers_fixture.hpp @@ -181,7 +181,7 @@ protected: mgr = std::make_unique(*config, *provider); EXPECT_TRUE(provider->start( - [&provider](bool directory, api_file &file) -> api_error { + [](bool directory, api_file &file) -> api_error { return provider_meta_handler(*provider, directory, file); }, mgr.get())); From 37c036a2d78937423b61bc784fbe901a1d25b250 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Thu, 18 Sep 2025 17:27:56 -0500 Subject: [PATCH 059/136] [unit test] Complete all providers unit tests #12 --- .../include/fixtures/providers_fixture.hpp | 123 +++++- repertory/repertory_test/src/file_db_test.cpp | 2 +- .../repertory_test/src/file_mgr_db_test.cpp | 2 +- .../src/fuse_drive_access_test.cpp | 2 +- .../src/fuse_drive_chmod_test.cpp | 2 +- .../src/fuse_drive_chown_test.cpp | 2 +- .../src/fuse_drive_create_and_open_test.cpp | 2 +- .../src/fuse_drive_rdrw_test.cpp | 2 +- repertory/repertory_test/src/meta_db_test.cpp | 2 +- .../repertory_test/src/providers_test.cpp | 350 +++++++----------- .../src/winfsp_drive_create_attr_test.cpp | 2 +- .../src/winfsp_drive_create_nl_test.cpp | 2 +- .../src/winfsp_drive_create_test.cpp | 2 +- .../src/winfsp_drive_delete_test.cpp | 2 +- .../src/winfsp_drive_info_test.cpp | 2 +- .../src/winfsp_drive_rdrw_test.cpp | 2 +- .../src/winfsp_drive_rename_test.cpp | 2 +- .../repertory_test/src/winfsp_drive_test.cpp | 2 +- .../src/winfsp_drive_volume_test.cpp | 2 +- 19 files changed, 261 insertions(+), 246 deletions(-) diff --git a/repertory/repertory_test/include/fixtures/providers_fixture.hpp b/repertory/repertory_test/include/fixtures/providers_fixture.hpp index 371c78e5..82c9b763 100644 --- a/repertory/repertory_test/include/fixtures/providers_fixture.hpp +++ b/repertory/repertory_test/include/fixtures/providers_fixture.hpp @@ -28,24 +28,29 @@ #include "comm/i_http_comm.hpp" #include "events/event_system.hpp" #include "file_manager/file_manager.hpp" -#include "fixtures/providers_fixture.hpp" #include "platform/platform.hpp" #include "providers/encrypt/encrypt_provider.hpp" #include "providers/i_provider.hpp" #include "providers/s3/s3_provider.hpp" #include "providers/sia/sia_provider.hpp" -#include "utils/collection.hpp" #include "utils/file.hpp" #include "utils/path.hpp" -#include "utils/string.hpp" -#include "utils/time.hpp" #include "utils/utils.hpp" +#if defined(_WIN32) +namespace { +using gid_t = std::uint32_t; +using uid_t = std::uint32_t; +static constexpr auto getgid() -> gid_t { return 0U; } +static constexpr auto getuid() -> uid_t { return 0U; } +} // namespace +#endif // defined(_WIN32) + namespace repertory { struct encrypt_provider_type final { static constexpr provider_type type{provider_type::encrypt}; - static void setup(std::unique_ptr &comm, + static void setup(std::unique_ptr & /* comm */, std::unique_ptr &config, std::unique_ptr &provider) { auto config_path = @@ -200,6 +205,114 @@ protected: event_system::instance().stop(); } + +protected: + static void check_forced_dirs(const directory_item_list &list) { + static auto forced_dirs = std::array{".", ".."}; + for (std::size_t i = 0U; i < forced_dirs.size(); ++i) { + const auto &item = list.at(i); + EXPECT_TRUE(item.directory); + EXPECT_STREQ(forced_dirs.at(i).c_str(), item.api_path.c_str()); + EXPECT_STREQ("", item.api_parent.c_str()); + EXPECT_EQ(std::size_t(0U), item.size); + } + } + + static void create_directory(const std::string &api_path) { + auto date = utils::time::get_time_now(); + auto meta = create_meta_attributes( + date, 1U, date + 1U, date + 2U, true, getgid(), "", 0700, date + 3U, 2U, + 3U, 0U, api_path + "_src", getuid(), date + 4U); + EXPECT_EQ(api_error::success, provider->create_directory(api_path, meta)); + + bool exists{}; + EXPECT_EQ(api_error::success, provider->is_directory(api_path, exists)); + EXPECT_TRUE(exists); + + api_meta_map meta2{}; + EXPECT_EQ(api_error::success, provider->get_item_meta(api_path, meta2)); + + EXPECT_EQ(date, utils::string::to_uint64(meta2[META_ACCESSED])); + EXPECT_EQ(1U, utils::string::to_uint64(meta2[META_ATTRIBUTES])); + EXPECT_EQ(date + 1U, utils::string::to_uint64(meta2[META_CHANGED])); + EXPECT_EQ(date + 2U, utils::string::to_uint64(meta2[META_CREATION])); + EXPECT_TRUE(utils::string::to_bool(meta2.at(META_DIRECTORY))); + EXPECT_EQ(getgid(), + static_cast(utils::string::to_uint32(meta2[META_GID]))); + EXPECT_EQ(std::uint32_t(0700), utils::string::to_uint32(meta2[META_MODE])); + EXPECT_EQ(date + 3U, utils::string::to_uint64(meta2[META_MODIFIED])); + EXPECT_EQ(2U, utils::string::to_uint64(meta2[META_BACKUP])); + EXPECT_EQ(3U, utils::string::to_uint64(meta2[META_OSXFLAGS])); + EXPECT_FALSE(utils::string::to_bool(meta2[META_PINNED])); + EXPECT_EQ(std::uint64_t(0U), utils::string::to_uint64(meta2[META_SIZE])); + EXPECT_EQ(getuid(), + static_cast(utils::string::to_uint32(meta2[META_UID]))); + EXPECT_EQ(date + 4U, utils::string::to_uint64(meta2[META_WRITTEN])); + } + + static void create_file(const std::string &api_path) { + auto source_path = test::generate_test_file_name("providers_test"); + + auto date = utils::time::get_time_now(); + auto meta = create_meta_attributes(date, 1U, date + 1U, date + 2U, false, + getgid(), "", 0700, date + 3U, 2U, 3U, + 0U, source_path, getuid(), date + 4U); + EXPECT_EQ(api_error::success, provider->create_file(api_path, meta)); + + bool exists{}; + EXPECT_EQ(api_error::success, provider->is_file(api_path, exists)); + EXPECT_TRUE(exists); + + EXPECT_TRUE(utils::file::file{source_path}.remove()); + + api_meta_map meta2{}; + EXPECT_EQ(api_error::success, provider->get_item_meta(api_path, meta2)); + + EXPECT_EQ(date, utils::string::to_uint64(meta2[META_ACCESSED])); + EXPECT_EQ(1U, utils::string::to_uint64(meta2[META_ATTRIBUTES])); + EXPECT_EQ(date + 1U, utils::string::to_uint64(meta2[META_CHANGED])); + EXPECT_EQ(date + 2U, utils::string::to_uint64(meta2[META_CREATION])); + EXPECT_FALSE(utils::string::to_bool(meta2.at(META_DIRECTORY))); + EXPECT_EQ(getgid(), + static_cast(utils::string::to_uint32(meta2[META_GID]))); + EXPECT_EQ(std::uint32_t(0700), utils::string::to_uint32(meta2[META_MODE])); + EXPECT_EQ(date + 3U, utils::string::to_uint64(meta2[META_MODIFIED])); + EXPECT_EQ(2U, utils::string::to_uint64(meta2[META_BACKUP])); + EXPECT_EQ(3U, utils::string::to_uint64(meta2[META_OSXFLAGS])); + EXPECT_FALSE(utils::string::to_bool(meta2[META_PINNED])); + EXPECT_EQ(std::uint64_t(0U), utils::string::to_uint64(meta2[META_SIZE])); + EXPECT_STREQ(source_path.c_str(), meta2[META_SOURCE].c_str()); + EXPECT_EQ(getuid(), + static_cast(utils::string::to_uint32(meta2[META_UID]))); + EXPECT_EQ(date + 4U, utils::string::to_uint64(meta2[META_WRITTEN])); + } + + static void decrypt_parts(std::string &path) { + if (path != "/" && path != "." && path != "..") { + utils::hash::hash_256_t key{}; + EXPECT_TRUE(utils::encryption::recreate_key_argon2id( + config->get_encrypt_config().encryption_token, + config->get_encrypt_config().kdf_cfg, key)); + auto parts = utils::string::split(path, '/', false); + for (auto &part : parts) { + if (part.empty()) { + continue; + } + + EXPECT_TRUE(utils::encryption::decrypt_file_name(key, part)); + } + path = utils::string::join(parts, '/'); + } + } + + [[nodiscard]] static auto + pinned_includes_api_path(const auto &pinned, const std::string &expected_path) + -> bool { + return std::ranges::any_of(pinned, + [&expected_path](auto &&api_path) -> bool { + return api_path == expected_path; + }); + }; }; template diff --git a/repertory/repertory_test/src/file_db_test.cpp b/repertory/repertory_test/src/file_db_test.cpp index 010a185b..75b3d12e 100644 --- a/repertory/repertory_test/src/file_db_test.cpp +++ b/repertory/repertory_test/src/file_db_test.cpp @@ -27,7 +27,7 @@ // } // namespace // // namespace repertory { -// TYPED_TEST_CASE(file_db_test, file_db_types); +// TYPED_TEST_SUITE(file_db_test, file_db_types); // // TYPED_TEST(file_db_test, can_add_and_remove_directory) { // this->file_db->clear(); diff --git a/repertory/repertory_test/src/file_mgr_db_test.cpp b/repertory/repertory_test/src/file_mgr_db_test.cpp index 57e41f58..a891d2af 100644 --- a/repertory/repertory_test/src/file_mgr_db_test.cpp +++ b/repertory/repertory_test/src/file_mgr_db_test.cpp @@ -24,7 +24,7 @@ #include "utils/time.hpp" namespace repertory { -TYPED_TEST_CASE(file_mgr_db_test, file_mgr_db_types); +TYPED_TEST_SUITE(file_mgr_db_test, file_mgr_db_types); TYPED_TEST(file_mgr_db_test, can_add_and_remove_resume) { this->file_mgr_db->clear(); diff --git a/repertory/repertory_test/src/fuse_drive_access_test.cpp b/repertory/repertory_test/src/fuse_drive_access_test.cpp index c141d5b5..b4cde5e3 100644 --- a/repertory/repertory_test/src/fuse_drive_access_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_access_test.cpp @@ -71,7 +71,7 @@ void perform_access_test(auto &&permutation, auto &&item_path) { } // namespace namespace repertory { -TYPED_TEST_CASE(fuse_test, fuse_provider_types); +TYPED_TEST_SUITE(fuse_test, fuse_provider_types); TYPED_TEST(fuse_test, access_can_check_if_item_does_not_exist) { EXPECT_EQ( diff --git a/repertory/repertory_test/src/fuse_drive_chmod_test.cpp b/repertory/repertory_test/src/fuse_drive_chmod_test.cpp index c06ac728..f06569b2 100644 --- a/repertory/repertory_test/src/fuse_drive_chmod_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_chmod_test.cpp @@ -24,7 +24,7 @@ #include "fixtures/fuse_fixture.hpp" namespace repertory { -TYPED_TEST_CASE(fuse_test, fuse_provider_types); +TYPED_TEST_SUITE(fuse_test, fuse_provider_types); TYPED_TEST(fuse_test, chmod_can_not_chmod_set_sticky_if_not_root) { std::string file_name{"chmod_test"}; diff --git a/repertory/repertory_test/src/fuse_drive_chown_test.cpp b/repertory/repertory_test/src/fuse_drive_chown_test.cpp index 3990b4ca..abf6fad0 100644 --- a/repertory/repertory_test/src/fuse_drive_chown_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_chown_test.cpp @@ -24,7 +24,7 @@ #include "fixtures/fuse_fixture.hpp" namespace repertory { -TYPED_TEST_CASE(fuse_test, fuse_provider_types); +TYPED_TEST_SUITE(fuse_test, fuse_provider_types); TYPED_TEST(fuse_test, chown_can_chown_group_if_owner_and_a_member_of_the_group) { diff --git a/repertory/repertory_test/src/fuse_drive_create_and_open_test.cpp b/repertory/repertory_test/src/fuse_drive_create_and_open_test.cpp index 89acd5f3..b5691615 100644 --- a/repertory/repertory_test/src/fuse_drive_create_and_open_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_create_and_open_test.cpp @@ -23,7 +23,7 @@ #include "fixtures/fuse_fixture.hpp" namespace repertory { -TYPED_TEST_CASE(fuse_test, fuse_provider_types); +TYPED_TEST_SUITE(fuse_test, fuse_provider_types); TYPED_TEST(fuse_test, create_can_create_and_remove_directory) { std::string dir_name{"create_test"}; diff --git a/repertory/repertory_test/src/fuse_drive_rdrw_test.cpp b/repertory/repertory_test/src/fuse_drive_rdrw_test.cpp index 9b7bbc63..9405e39d 100644 --- a/repertory/repertory_test/src/fuse_drive_rdrw_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_rdrw_test.cpp @@ -24,7 +24,7 @@ #include "fixtures/fuse_fixture.hpp" namespace repertory { -TYPED_TEST_CASE(fuse_test, fuse_provider_types); +TYPED_TEST_SUITE(fuse_test, fuse_provider_types); TYPED_TEST(fuse_test, rdrw_can_read_and_write_file) { std::string file_name{"create_test"}; diff --git a/repertory/repertory_test/src/meta_db_test.cpp b/repertory/repertory_test/src/meta_db_test.cpp index 8e79fee2..0c1f32ad 100644 --- a/repertory/repertory_test/src/meta_db_test.cpp +++ b/repertory/repertory_test/src/meta_db_test.cpp @@ -30,7 +30,7 @@ namespace { } // namespace namespace repertory { -TYPED_TEST_CASE(meta_db_test, meta_db_types); +TYPED_TEST_SUITE(meta_db_test, meta_db_types); TYPED_TEST(meta_db_test, can_get_api_path_from_source_path) { auto test_file = create_test_file(); diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index 036e5bbb..fe66c98b 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -22,156 +22,12 @@ #include "test_common.hpp" #include "fixtures/providers_fixture.hpp" - -namespace { -#if defined(_WIN32) -using gid_t = std::uint32_t; -using uid_t = std::uint32_t; -static constexpr auto getgid() -> gid_t { return 0U; } -static constexpr auto getuid() -> uid_t { return 0U; } -#endif // defined(_WIN32) - -const auto check_forced_dirs = [](const repertory::directory_item_list &list) { - static auto forced_dirs = std::array{".", ".."}; - for (std::size_t i = 0U; i < forced_dirs.size(); ++i) { - const auto &item = list.at(i); - EXPECT_TRUE(item.directory); - EXPECT_STREQ(forced_dirs.at(i).c_str(), item.api_path.c_str()); - EXPECT_STREQ("", item.api_parent.c_str()); - EXPECT_EQ(std::size_t(0U), item.size); - } -}; - -const auto create_directory = [](repertory::i_provider &provider, - const std::string &api_path) { - auto date = repertory::utils::time::get_time_now(); - auto meta = repertory::create_meta_attributes( - date, 1U, date + 1U, date + 2U, true, getgid(), "", 0700, date + 3U, 2U, - 3U, 0U, api_path + "_src", getuid(), date + 4U); - EXPECT_EQ(repertory::api_error::success, - provider.create_directory(api_path, meta)); - - bool exists{}; - EXPECT_EQ(repertory::api_error::success, - provider.is_directory(api_path, exists)); - EXPECT_TRUE(exists); - - repertory::api_meta_map meta2{}; - EXPECT_EQ(repertory::api_error::success, - provider.get_item_meta(api_path, meta2)); - - EXPECT_EQ(date, repertory::utils::string::to_uint64( - meta2[repertory::META_ACCESSED])); - EXPECT_EQ(1U, repertory::utils::string::to_uint64( - meta2[repertory::META_ATTRIBUTES])); - EXPECT_EQ(date + 1U, repertory::utils::string::to_uint64( - meta2[repertory::META_CHANGED])); - EXPECT_EQ(date + 2U, repertory::utils::string::to_uint64( - meta2[repertory::META_CREATION])); - EXPECT_TRUE( - repertory::utils::string::to_bool(meta2.at(repertory::META_DIRECTORY))); - EXPECT_EQ(getgid(), static_cast(repertory::utils::string::to_uint32( - meta2[repertory::META_GID]))); - EXPECT_EQ(std::uint32_t(0700), - repertory::utils::string::to_uint32(meta2[repertory::META_MODE])); - EXPECT_EQ(date + 3U, repertory::utils::string::to_uint64( - meta2[repertory::META_MODIFIED])); - EXPECT_EQ(2U, - repertory::utils::string::to_uint64(meta2[repertory::META_BACKUP])); - EXPECT_EQ( - 3U, repertory::utils::string::to_uint64(meta2[repertory::META_OSXFLAGS])); - EXPECT_FALSE( - repertory::utils::string::to_bool(meta2[repertory::META_PINNED])); - EXPECT_EQ(std::uint64_t(0U), - repertory::utils::string::to_uint64(meta2[repertory::META_SIZE])); - EXPECT_EQ(getuid(), static_cast(repertory::utils::string::to_uint32( - meta2[repertory::META_UID]))); - EXPECT_EQ(date + 4U, repertory::utils::string::to_uint64( - meta2[repertory::META_WRITTEN])); -}; - -const auto create_file = [](repertory::i_provider &provider, - const std::string &api_path) { - auto source_path = repertory::test::generate_test_file_name("providers_test"); - - auto date = repertory::utils::time::get_time_now(); - auto meta = repertory::create_meta_attributes( - date, 1U, date + 1U, date + 2U, false, getgid(), "", 0700, date + 3U, 2U, - 3U, 0U, source_path, getuid(), date + 4U); - EXPECT_EQ(repertory::api_error::success, - provider.create_file(api_path, meta)); - - bool exists{}; - EXPECT_EQ(repertory::api_error::success, provider.is_file(api_path, exists)); - EXPECT_TRUE(exists); - - EXPECT_TRUE(repertory::utils::file::file{source_path}.remove()); - - repertory::api_meta_map meta2{}; - EXPECT_EQ(repertory::api_error::success, - provider.get_item_meta(api_path, meta2)); - - EXPECT_EQ(date, repertory::utils::string::to_uint64( - meta2[repertory::META_ACCESSED])); - EXPECT_EQ(1U, repertory::utils::string::to_uint64( - meta2[repertory::META_ATTRIBUTES])); - EXPECT_EQ(date + 1U, repertory::utils::string::to_uint64( - meta2[repertory::META_CHANGED])); - EXPECT_EQ(date + 2U, repertory::utils::string::to_uint64( - meta2[repertory::META_CREATION])); - EXPECT_FALSE( - repertory::utils::string::to_bool(meta2.at(repertory::META_DIRECTORY))); - EXPECT_EQ(getgid(), static_cast(repertory::utils::string::to_uint32( - meta2[repertory::META_GID]))); - EXPECT_EQ(std::uint32_t(0700), - repertory::utils::string::to_uint32(meta2[repertory::META_MODE])); - EXPECT_EQ(date + 3U, repertory::utils::string::to_uint64( - meta2[repertory::META_MODIFIED])); - EXPECT_EQ(2U, - repertory::utils::string::to_uint64(meta2[repertory::META_BACKUP])); - EXPECT_EQ( - 3U, repertory::utils::string::to_uint64(meta2[repertory::META_OSXFLAGS])); - EXPECT_FALSE( - repertory::utils::string::to_bool(meta2[repertory::META_PINNED])); - EXPECT_EQ(std::uint64_t(0U), - repertory::utils::string::to_uint64(meta2[repertory::META_SIZE])); - EXPECT_STREQ(source_path.c_str(), meta2[repertory::META_SOURCE].c_str()); - EXPECT_EQ(getuid(), static_cast(repertory::utils::string::to_uint32( - meta2[repertory::META_UID]))); - EXPECT_EQ(date + 4U, repertory::utils::string::to_uint64( - meta2[repertory::META_WRITTEN])); -}; - -const auto decrypt_parts = [](const repertory::app_config &cfg, - std::string &path) { - if (path != "/" && path != "." && path != "..") { - repertory::utils::hash::hash_256_t key{}; - EXPECT_TRUE(repertory::utils::encryption::recreate_key_argon2id( - cfg.get_encrypt_config().encryption_token, - cfg.get_encrypt_config().kdf_cfg, key)); - auto parts = repertory::utils::string::split(path, '/', false); - for (auto &part : parts) { - if (part.empty()) { - continue; - } - - EXPECT_TRUE(repertory::utils::encryption::decrypt_file_name(key, part)); - } - path = repertory::utils::string::join(parts, '/'); - } -}; - -const auto pinned_includes_api_path = - [](const auto &pinned, const std::string &expected_path) -> bool { - return std::ranges::any_of(pinned, [&expected_path](auto &&api_path) -> bool { - return api_path == expected_path; - }); -}; - -} // namespace +#include "utils/collection.hpp" +#include "utils/string.hpp" +#include "utils/time.hpp" namespace repertory { -TYPED_TEST_CASE(providers_test, provider_types); +TYPED_TEST_SUITE(providers_test, provider_types); TYPED_TEST(providers_test, get_file_list) { api_file_list list{}; @@ -190,8 +46,8 @@ TYPED_TEST(providers_test, get_file_list) { }; for (auto &file : list) { - decrypt_parts(*this->config, file.api_parent); - decrypt_parts(*this->config, file.api_path); + this->decrypt_parts(file.api_parent); + this->decrypt_parts(file.api_path); utils::collection::remove_element(expected_parents, file.api_parent); utils::collection::remove_element(expected_paths, file.api_path); } @@ -225,7 +81,7 @@ TYPED_TEST(providers_test, get_and_set_item_meta_with_upload_file) { auto &file = test::create_random_file(128U); auto api_path = fmt::format("/{}", utils::path::strip_to_file_name(file.get_path())); - create_file(*this->provider, api_path); + this->create_file(api_path); stop_type stop_requested{false}; ASSERT_EQ(api_error::success, this->provider->upload_file( @@ -261,7 +117,7 @@ TYPED_TEST(providers_test, can_create_and_remove_directory) { return; } - create_directory(*this->provider, "/pt01"); + this->create_directory("/pt01"); EXPECT_EQ(api_error::success, this->provider->remove_directory("/pt01")); bool exists{}; @@ -290,7 +146,7 @@ TYPED_TEST(providers_test, get_and_set_item_meta2_with_upload_file) { auto &file = test::create_random_file(64U); auto api_path = fmt::format("/{}", utils::path::strip_to_file_name(file.get_path())); - create_file(*this->provider, api_path); + this->create_file(api_path); stop_type stop_requested{false}; ASSERT_EQ(api_error::success, this->provider->upload_file( @@ -351,7 +207,7 @@ TYPED_TEST(providers_test, can_create_and_remove_file) { return; } - create_file(*this->provider, "/pt01.txt"); + this->create_file("/pt01.txt"); bool exists{}; EXPECT_EQ(api_error::success, this->provider->is_file("/pt01.txt", exists)); @@ -368,7 +224,7 @@ TYPED_TEST(providers_test, create_directory_fails_if_already_exists) { return; } - create_directory(*this->provider, "/pt01"); + this->create_directory("/pt01"); api_meta_map meta{}; EXPECT_EQ(api_error::directory_exists, @@ -381,7 +237,7 @@ TYPED_TEST(providers_test, create_directory_fails_if_file_already_exists) { return; } - create_file(*this->provider, "/pt01"); + this->create_file("/pt01"); api_meta_map meta{}; EXPECT_EQ(api_error::item_exists, @@ -397,7 +253,7 @@ TYPED_TEST(providers_test, create_directory_clone_source_meta) { this->provider->create_directory_clone_source_meta("/moose", "/moose")); return; } - create_directory(*this->provider, "/clone"); + this->create_directory("/clone"); api_meta_map meta_orig{}; EXPECT_EQ(api_error::success, @@ -432,8 +288,8 @@ TYPED_TEST(providers_test, if (this->provider->is_read_only()) { return; } - create_directory(*this->provider, "/clone"); - create_directory(*this->provider, "/clone2"); + this->create_directory("/clone"); + this->create_directory("/clone2"); EXPECT_EQ( api_error::directory_exists, @@ -460,8 +316,8 @@ TYPED_TEST(providers_test, return; } - create_directory(*this->provider, "/clone"); - create_file(*this->provider, "/clone2"); + this->create_directory("/clone"); + this->create_file("/clone2"); EXPECT_EQ( api_error::item_exists, @@ -476,7 +332,7 @@ TYPED_TEST(providers_test, create_file_fails_if_already_exists) { return; } - create_file(*this->provider, "/pt01.txt"); + this->create_file("/pt01.txt"); api_meta_map meta{}; EXPECT_EQ(api_error::item_exists, @@ -490,7 +346,7 @@ TYPED_TEST(providers_test, create_file_fails_if_directory_already_exists) { return; } - create_directory(*this->provider, "/pt01"); + this->create_directory("/pt01"); api_meta_map meta{}; EXPECT_EQ(api_error::directory_exists, @@ -509,12 +365,12 @@ TYPED_TEST(providers_test, get_api_path_from_source) { this->provider->get_api_path_from_source(source_path, api_path)); std::string file_name{api_path.substr(1U)}; - decrypt_parts(*this->config, file_name); + this->decrypt_parts(file_name); EXPECT_STREQ("test.txt", file_name.c_str()); return; } - create_file(*this->provider, "/pt01.txt"); + this->create_file("/pt01.txt"); filesystem_item fsi{}; EXPECT_EQ(api_error::success, @@ -549,14 +405,14 @@ TYPED_TEST(providers_test, get_directory_items) { directory_item_list list{}; EXPECT_EQ(api_error::success, this->provider->get_directory_items("/", list)); - check_forced_dirs(list); + this->check_forced_dirs(list); EXPECT_EQ(std::size_t(4U), list.size()); directory_item_list list_decrypted{list.begin() + 2U, list.end()}; for (auto &dir_item : list_decrypted) { - decrypt_parts(*this->config, dir_item.api_parent); - decrypt_parts(*this->config, dir_item.api_path); + this->decrypt_parts(dir_item.api_parent); + this->decrypt_parts(dir_item.api_path); } auto dir = @@ -590,13 +446,13 @@ TYPED_TEST(providers_test, get_directory_items) { list.clear(); EXPECT_EQ(api_error::success, this->provider->get_directory_items(api_path, list)); - check_forced_dirs(list); + this->check_forced_dirs(list); EXPECT_EQ(std::size_t(3U), list.size()); directory_item_list list_decrypted2{list.begin() + 2U, list.end()}; for (auto &dir_item : list_decrypted2) { - decrypt_parts(*this->config, dir_item.api_parent); - decrypt_parts(*this->config, dir_item.api_path); + this->decrypt_parts(dir_item.api_parent); + this->decrypt_parts(dir_item.api_path); } auto file2 = @@ -614,14 +470,14 @@ TYPED_TEST(providers_test, get_directory_items) { return; } - create_file(*this->provider, "/pt01.txt"); - create_file(*this->provider, "/pt02.txt"); - create_directory(*this->provider, "/dir01"); - create_directory(*this->provider, "/dir02"); + this->create_file("/pt01.txt"); + this->create_file("/pt02.txt"); + this->create_directory("/dir01"); + this->create_directory("/dir02"); directory_item_list list{}; EXPECT_EQ(api_error::success, this->provider->get_directory_items("/", list)); - check_forced_dirs(list); + this->check_forced_dirs(list); EXPECT_GE(list.size(), std::size_t(6U)); auto iter = std::ranges::find_if( @@ -680,7 +536,7 @@ TYPED_TEST(providers_test, get_directory_items_fails_if_item_is_file) { return; } - create_file(*this->provider, "/pt01.txt"); + this->create_file("/pt01.txt"); directory_item_list list{}; EXPECT_EQ(api_error::item_exists, @@ -705,14 +561,14 @@ TYPED_TEST(providers_test, get_directory_item_count) { return; } - create_file(*this->provider, "/pt01.txt"); - create_file(*this->provider, "/pt02.txt"); - create_directory(*this->provider, "/dir01"); - create_directory(*this->provider, "/dir02"); + this->create_file("/pt01.txt"); + this->create_file("/pt02.txt"); + this->create_directory("/dir01"); + this->create_directory("/dir02"); directory_item_list list{}; EXPECT_EQ(api_error::success, this->provider->get_directory_items("/", list)); - check_forced_dirs(list); + this->check_forced_dirs(list); EXPECT_GE(list.size(), std::size_t(6U)); EXPECT_EQ(api_error::success, this->provider->remove_file("/pt01.txt")); @@ -732,8 +588,8 @@ TYPED_TEST(providers_test, get_file) { api_file file{}; EXPECT_EQ(api_error::success, this->provider->get_file(api_path, file)); - decrypt_parts(*this->config, file.api_path); - decrypt_parts(*this->config, file.api_parent); + this->decrypt_parts(file.api_path); + this->decrypt_parts(file.api_parent); EXPECT_STREQ("/test.txt", file.api_path.c_str()); EXPECT_STREQ("/", file.api_parent.c_str()); @@ -746,7 +602,7 @@ TYPED_TEST(providers_test, get_file) { return; } - create_file(*this->provider, "/pt01.txt"); + this->create_file("/pt01.txt"); api_file file{}; EXPECT_EQ(api_error::success, this->provider->get_file("/pt01.txt", file)); @@ -785,7 +641,7 @@ TYPED_TEST(providers_test, get_file_fails_if_item_is_directory) { return; } - create_directory(*this->provider, "/dir01"); + this->create_directory("/dir01"); api_file file{}; EXPECT_EQ(api_error::directory_exists, @@ -816,7 +672,7 @@ TYPED_TEST(providers_test, get_file_size) { auto &file = test::create_random_file(128U); auto api_path = fmt::format("/{}", utils::path::strip_to_file_name(file.get_path())); - create_file(*this->provider, api_path); + this->create_file(api_path); stop_type stop_requested{false}; auto res = @@ -867,7 +723,7 @@ TYPED_TEST(providers_test, get_filesystem_item) { auto &file = test::create_random_file(128U); auto api_path = fmt::format("/{}", utils::path::strip_to_file_name(file.get_path())); - create_file(*this->provider, api_path); + this->create_file(api_path); stop_type stop_requested{false}; auto res = @@ -930,7 +786,7 @@ TYPED_TEST(providers_test, get_filesystem_item_from_source_path) { api_path = fmt::format("/{}", utils::path::strip_to_file_name(file.get_path())); source_path = file.get_path(); - create_file(*this->provider, api_path); + this->create_file(api_path); EXPECT_EQ(api_error::success, this->provider->set_item_meta( @@ -980,7 +836,7 @@ TYPED_TEST(providers_test, remove_file_fails_if_item_is_directory) { return; } - create_directory(*this->provider, "/dir01"); + this->create_directory("/dir01"); EXPECT_EQ(api_error::directory_exists, this->provider->remove_file("/dir01")); EXPECT_EQ(api_error::success, this->provider->remove_directory("/dir01")); } @@ -992,7 +848,7 @@ TYPED_TEST(providers_test, remove_directory_fails_if_item_is_file) { return; } - create_file(*this->provider, "/pt01.txt"); + this->create_file("/pt01.txt"); EXPECT_EQ(api_error::item_not_found, this->provider->remove_directory("/pt01.txt")); EXPECT_EQ(api_error::success, this->provider->remove_file("/pt01.txt")); @@ -1014,9 +870,9 @@ TYPED_TEST(providers_test, get_pinned_files) { return; } - create_file(*this->provider, "/pin01.txt"); - create_file(*this->provider, "/pin02.txt"); - create_file(*this->provider, "/nopin01.txt"); + this->create_file("/pin01.txt"); + this->create_file("/pin02.txt"); + this->create_file("/nopin01.txt"); EXPECT_EQ(api_error::success, this->provider->set_item_meta("/pin01.txt", META_PINNED, "true")); @@ -1028,9 +884,9 @@ TYPED_TEST(providers_test, get_pinned_files) { auto pinned = this->provider->get_pinned_files(); EXPECT_EQ(std::size_t(2U), pinned.size()); - EXPECT_TRUE(pinned_includes_api_path(pinned, "/pin01.txt")); - EXPECT_TRUE(pinned_includes_api_path(pinned, "/pin02.txt")); - EXPECT_FALSE(pinned_includes_api_path(pinned, "/nopin01.txt")); + EXPECT_TRUE(this->pinned_includes_api_path(pinned, "/pin01.txt")); + EXPECT_TRUE(this->pinned_includes_api_path(pinned, "/pin02.txt")); + EXPECT_FALSE(this->pinned_includes_api_path(pinned, "/nopin01.txt")); EXPECT_EQ(api_error::success, this->provider->remove_file("/pin01.txt")); EXPECT_EQ(api_error::success, this->provider->remove_file("/pin02.txt")); @@ -1044,8 +900,8 @@ TYPED_TEST(providers_test, remove_pin_updates_pinned_files) { return; } - create_file(*this->provider, "/pin01.txt"); - create_file(*this->provider, "/pin02.txt"); + this->create_file("/pin01.txt"); + this->create_file("/pin02.txt"); EXPECT_EQ(api_error::success, this->provider->set_item_meta("/pin01.txt", META_PINNED, "true")); EXPECT_EQ(api_error::success, @@ -1058,8 +914,8 @@ TYPED_TEST(providers_test, remove_pin_updates_pinned_files) { this->provider->set_item_meta("/pin02.txt", META_PINNED, "false")); pinned = this->provider->get_pinned_files(); EXPECT_EQ(std::size_t(1U), pinned.size()); - EXPECT_TRUE(pinned_includes_api_path(pinned, "/pin01.txt")); - EXPECT_FALSE(pinned_includes_api_path(pinned, "/pin02.txt")); + EXPECT_TRUE(this->pinned_includes_api_path(pinned, "/pin01.txt")); + EXPECT_FALSE(this->pinned_includes_api_path(pinned, "/pin02.txt")); EXPECT_EQ(api_error::success, this->provider->set_item_meta("/pin01.txt", META_PINNED, "false")); @@ -1077,9 +933,9 @@ TYPED_TEST(providers_test, remove_file_updates_pinned_files) { return; } - create_file(*this->provider, "/pin_keep.txt"); - create_file(*this->provider, "/pin_delete.txt"); - create_file(*this->provider, "/nopin.txt"); + this->create_file("/pin_keep.txt"); + this->create_file("/pin_delete.txt"); + this->create_file("/nopin.txt"); EXPECT_EQ(api_error::success, this->provider->set_item_meta( "/pin_keep.txt", META_PINNED, "true")); @@ -1091,16 +947,16 @@ TYPED_TEST(providers_test, remove_file_updates_pinned_files) { auto pinned = this->provider->get_pinned_files(); EXPECT_EQ(std::size_t(2U), pinned.size()); - EXPECT_TRUE(pinned_includes_api_path(pinned, "/pin_keep.txt")); - EXPECT_TRUE(pinned_includes_api_path(pinned, "/pin_delete.txt")); - EXPECT_FALSE(pinned_includes_api_path(pinned, "/nopin.txt")); + EXPECT_TRUE(this->pinned_includes_api_path(pinned, "/pin_keep.txt")); + EXPECT_TRUE(this->pinned_includes_api_path(pinned, "/pin_delete.txt")); + EXPECT_FALSE(this->pinned_includes_api_path(pinned, "/nopin.txt")); EXPECT_EQ(api_error::success, this->provider->remove_file("/pin_delete.txt")); pinned = this->provider->get_pinned_files(); EXPECT_EQ(std::size_t(1U), pinned.size()); - EXPECT_TRUE(pinned_includes_api_path(pinned, "/pin_keep.txt")); - EXPECT_FALSE(pinned_includes_api_path(pinned, "/pin_delete.txt")); + EXPECT_TRUE(this->pinned_includes_api_path(pinned, "/pin_keep.txt")); + EXPECT_FALSE(this->pinned_includes_api_path(pinned, "/pin_delete.txt")); EXPECT_EQ(api_error::success, this->provider->remove_file("/pin_keep.txt")); EXPECT_EQ(api_error::success, this->provider->remove_file("/nopin.txt")); @@ -1116,8 +972,8 @@ TYPED_TEST(providers_test, get_total_item_count) { std::uint64_t before{this->provider->get_total_item_count()}; - create_file(*this->provider, "/count01.txt"); - create_file(*this->provider, "/count02.txt"); + this->create_file("/count01.txt"); + this->create_file("/count02.txt"); std::uint64_t mid{this->provider->get_total_item_count()}; EXPECT_EQ(before + 2U, mid); @@ -1159,8 +1015,8 @@ TYPED_TEST(providers_test, get_used_drive_space) { auto api_path2 = fmt::format("/{}", utils::path::strip_to_file_name(file2.get_path())); - create_file(*this->provider, api_path1); - create_file(*this->provider, api_path2); + this->create_file(api_path1); + this->create_file(api_path2); stop_type stop_requested{false}; ASSERT_EQ( @@ -1203,7 +1059,7 @@ TYPED_TEST(providers_test, remove_item_meta) { return; } - create_file(*this->provider, api_path); + this->create_file(api_path); EXPECT_EQ(api_error::success, this->provider->set_item_meta(api_path, "user.custom", "abc123")); @@ -1245,7 +1101,7 @@ TYPED_TEST(providers_test, remove_item_meta_restricted_names_fail) { this->provider->get_api_path_from_source(source_path, api_path)); } else { api_path = "/rim_restricted.txt"; - create_file(*this->provider, api_path); + this->create_file(api_path); } for (const auto &key : META_USED_NAMES) { @@ -1272,7 +1128,7 @@ TYPED_TEST(providers_test, rename_file) { std::string src{"/rn_src.txt"}; std::string dst{"/rn_dst.txt"}; - create_file(*this->provider, src); + this->create_file(src); std::string src_meta_size{}; std::string src_meta_source{}; @@ -1316,8 +1172,8 @@ TYPED_TEST(providers_test, rename_file_fails_if_source_not_found) { TYPED_TEST(providers_test, rename_file_fails_if_destination_exists) { if (not this->provider->is_rename_supported()) { if (this->provider->get_provider_type() != provider_type::encrypt) { - create_file(*this->provider, "/rn_src_conflict.txt"); - create_file(*this->provider, "/rn_dst_conflict.txt"); + this->create_file("/rn_src_conflict.txt"); + this->create_file("/rn_dst_conflict.txt"); } auto res = this->provider->rename_file("/rn_src_conflict.txt", "/rn_dst_conflict.txt"); @@ -1334,8 +1190,8 @@ TYPED_TEST(providers_test, rename_file_fails_if_destination_exists) { std::string src{"/rn_src_conflict.txt"}; std::string dst{"/rn_dst_conflict.txt"}; - create_file(*this->provider, src); - create_file(*this->provider, dst); + this->create_file(src); + this->create_file(dst); auto res = this->provider->rename_file(src, dst); EXPECT_EQ(api_error::item_exists, res); @@ -1353,8 +1209,8 @@ TYPED_TEST(providers_test, rename_file_fails_if_destination_exists) { TYPED_TEST(providers_test, rename_file_fails_if_destination_is_directory) { if (not this->provider->is_rename_supported()) { if (this->provider->get_provider_type() != provider_type::encrypt) { - create_file(*this->provider, "/rn_src_conflict.txt"); - create_directory(*this->provider, "/rn_dst_conflict"); + this->create_file("/rn_src_conflict.txt"); + this->create_directory("/rn_dst_conflict"); } auto res = this->provider->rename_file("/rn_src_conflict.txt", "/rn_dst_conflict"); @@ -1371,8 +1227,8 @@ TYPED_TEST(providers_test, rename_file_fails_if_destination_is_directory) { std::string src{"/rn_src_conflict.txt"}; std::string dst{"/rn_dst_conflict"}; - create_file(*this->provider, src); - create_directory(*this->provider, dst); + this->create_file(src); + this->create_directory(dst); auto res = this->provider->rename_file(src, dst); EXPECT_EQ(api_error::directory_exists, res); @@ -1386,4 +1242,50 @@ TYPED_TEST(providers_test, rename_file_fails_if_destination_is_directory) { EXPECT_EQ(api_error::success, this->provider->remove_file(src)); EXPECT_EQ(api_error::success, this->provider->remove_directory(dst)); } + +TYPED_TEST(providers_test, upload_file_not_implemented_on_read_only) { + if (not this->provider->is_read_only()) { + return; + } + + auto &file = test::create_random_file(16U); + stop_type stop_requested{false}; + auto res = this->provider->upload_file("/ro_upload.txt", file.get_path(), + stop_requested); + EXPECT_EQ(api_error::not_implemented, res); +} + +TYPED_TEST(providers_test, upload_file_fails_if_source_not_found) { + if (this->provider->is_read_only()) { + return; + } + + stop_type stop_requested{false}; + auto res = this->provider->upload_file( + "/no_src_upload.txt", "/path/does/not/exist.bin", stop_requested); + EXPECT_EQ(api_error::item_not_found, res); +} + +TYPED_TEST(providers_test, is_file_is_directory_cross_checks) { + if (this->provider->is_read_only()) { + return; + } + + std::string file_api_path{"/xf_file.txt"}; + std::string dir_api_path{"/xd_dir"}; + + this->create_file(file_api_path); + this->create_directory(dir_api_path); + + bool exists{}; + EXPECT_EQ(api_error::success, + this->provider->is_directory(file_api_path, exists)); + EXPECT_FALSE(exists); + + EXPECT_EQ(api_error::success, this->provider->is_file(dir_api_path, exists)); + EXPECT_FALSE(exists); + + EXPECT_EQ(api_error::success, this->provider->remove_file(file_api_path)); + EXPECT_EQ(api_error::success, this->provider->remove_directory(dir_api_path)); +} } // namespace repertory diff --git a/repertory/repertory_test/src/winfsp_drive_create_attr_test.cpp b/repertory/repertory_test/src/winfsp_drive_create_attr_test.cpp index ac59d424..46f81075 100644 --- a/repertory/repertory_test/src/winfsp_drive_create_attr_test.cpp +++ b/repertory/repertory_test/src/winfsp_drive_create_attr_test.cpp @@ -28,7 +28,7 @@ #include "fixtures/winfsp_fixture.hpp" namespace repertory { -TYPED_TEST_CASE(winfsp_test, winfsp_provider_types); +TYPED_TEST_SUITE(winfsp_test, winfsp_provider_types); TYPED_TEST(winfsp_test, cr8_attr_can_create_new_file_with_normal_attribute) { auto file_path{ diff --git a/repertory/repertory_test/src/winfsp_drive_create_nl_test.cpp b/repertory/repertory_test/src/winfsp_drive_create_nl_test.cpp index 09454eb3..4a780337 100644 --- a/repertory/repertory_test/src/winfsp_drive_create_nl_test.cpp +++ b/repertory/repertory_test/src/winfsp_drive_create_nl_test.cpp @@ -28,7 +28,7 @@ #include "fixtures/winfsp_fixture.hpp" namespace repertory { -TYPED_TEST_CASE(winfsp_test, winfsp_provider_types); +TYPED_TEST_SUITE(winfsp_test, winfsp_provider_types); TYPED_TEST(winfsp_test, cr8_nl_can_create_file_of_max_component_length) { if (this->current_provider == provider_type::s3) { diff --git a/repertory/repertory_test/src/winfsp_drive_create_test.cpp b/repertory/repertory_test/src/winfsp_drive_create_test.cpp index 0c9befdb..609cb969 100644 --- a/repertory/repertory_test/src/winfsp_drive_create_test.cpp +++ b/repertory/repertory_test/src/winfsp_drive_create_test.cpp @@ -28,7 +28,7 @@ #include "fixtures/winfsp_fixture.hpp" namespace repertory { -TYPED_TEST_CASE(winfsp_test, winfsp_provider_types); +TYPED_TEST_SUITE(winfsp_test, winfsp_provider_types); TYPED_TEST(winfsp_test, cr8_file_can_create_file) { auto file_path{ diff --git a/repertory/repertory_test/src/winfsp_drive_delete_test.cpp b/repertory/repertory_test/src/winfsp_drive_delete_test.cpp index 37aec63a..49fa2495 100644 --- a/repertory/repertory_test/src/winfsp_drive_delete_test.cpp +++ b/repertory/repertory_test/src/winfsp_drive_delete_test.cpp @@ -28,7 +28,7 @@ #include "fixtures/winfsp_fixture.hpp" namespace repertory { -TYPED_TEST_CASE(winfsp_test, winfsp_provider_types); +TYPED_TEST_SUITE(winfsp_test, winfsp_provider_types); TYPED_TEST(winfsp_test, delete_directory_fails_if_directory_not_empty) { auto dir_path{ diff --git a/repertory/repertory_test/src/winfsp_drive_info_test.cpp b/repertory/repertory_test/src/winfsp_drive_info_test.cpp index cdade53a..df610b7e 100644 --- a/repertory/repertory_test/src/winfsp_drive_info_test.cpp +++ b/repertory/repertory_test/src/winfsp_drive_info_test.cpp @@ -28,7 +28,7 @@ #include "fixtures/winfsp_fixture.hpp" namespace repertory { -TYPED_TEST_CASE(winfsp_test, winfsp_provider_types); +TYPED_TEST_SUITE(winfsp_test, winfsp_provider_types); TYPED_TEST(winfsp_test, info_can_get_tag_info) { auto file_path{ diff --git a/repertory/repertory_test/src/winfsp_drive_rdrw_test.cpp b/repertory/repertory_test/src/winfsp_drive_rdrw_test.cpp index 98a03513..983cd65e 100644 --- a/repertory/repertory_test/src/winfsp_drive_rdrw_test.cpp +++ b/repertory/repertory_test/src/winfsp_drive_rdrw_test.cpp @@ -28,7 +28,7 @@ #include "fixtures/winfsp_fixture.hpp" namespace repertory { -TYPED_TEST_CASE(winfsp_test, winfsp_provider_types); +TYPED_TEST_SUITE(winfsp_test, winfsp_provider_types); static void test_file(auto &&mount_location, auto &&file_path, auto &&flags) { SYSTEM_INFO sys_info{}; diff --git a/repertory/repertory_test/src/winfsp_drive_rename_test.cpp b/repertory/repertory_test/src/winfsp_drive_rename_test.cpp index 5cd67322..1a6356ee 100644 --- a/repertory/repertory_test/src/winfsp_drive_rename_test.cpp +++ b/repertory/repertory_test/src/winfsp_drive_rename_test.cpp @@ -28,7 +28,7 @@ #include "fixtures/winfsp_fixture.hpp" namespace repertory { -TYPED_TEST_CASE(winfsp_test, winfsp_provider_types); +TYPED_TEST_SUITE(winfsp_test, winfsp_provider_types); TYPED_TEST(winfsp_test, rename_can_rename_file_if_dest_does_not_exist) { if (this->current_provider == provider_type::s3) { diff --git a/repertory/repertory_test/src/winfsp_drive_test.cpp b/repertory/repertory_test/src/winfsp_drive_test.cpp index 49d4a255..4b72ffd9 100644 --- a/repertory/repertory_test/src/winfsp_drive_test.cpp +++ b/repertory/repertory_test/src/winfsp_drive_test.cpp @@ -49,7 +49,7 @@ #include "fixtures/winfsp_fixture.hpp" namespace repertory { -TYPED_TEST_CASE(winfsp_test, winfsp_provider_types); +TYPED_TEST_SUITE(winfsp_test, winfsp_provider_types); TYPED_TEST(winfsp_test, can_set_current_directory_to_mount_location) { EXPECT_TRUE(::SetCurrentDirectoryA(this->mount_location.c_str())); diff --git a/repertory/repertory_test/src/winfsp_drive_volume_test.cpp b/repertory/repertory_test/src/winfsp_drive_volume_test.cpp index f01bf844..e695ea87 100644 --- a/repertory/repertory_test/src/winfsp_drive_volume_test.cpp +++ b/repertory/repertory_test/src/winfsp_drive_volume_test.cpp @@ -28,7 +28,7 @@ #include "fixtures/winfsp_fixture.hpp" namespace repertory { -TYPED_TEST_CASE(winfsp_test, winfsp_provider_types); +TYPED_TEST_SUITE(winfsp_test, winfsp_provider_types); TYPED_TEST(winfsp_test, volume_can_get_volume_info) { std::string volume_label; From 1fbdf19269de961bb3535e8c2ae40f0947209c4b Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Thu, 18 Sep 2025 17:33:26 -0500 Subject: [PATCH 060/136] [unit test] Complete all providers unit tests #12 --- repertory/librepertory/src/providers/s3/s3_provider.cpp | 2 ++ repertory/librepertory/src/providers/sia/sia_provider.cpp | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/repertory/librepertory/src/providers/s3/s3_provider.cpp b/repertory/librepertory/src/providers/s3/s3_provider.cpp index 187ce69c..82703539 100644 --- a/repertory/librepertory/src/providers/s3/s3_provider.cpp +++ b/repertory/librepertory/src/providers/s3/s3_provider.cpp @@ -1204,6 +1204,8 @@ auto s3_provider::upload_file_impl(const std::string &api_path, return api_error::comm_error; } file_size = opt_size.value(); + } else { + return api_error::item_not_found; } const auto &cfg{get_s3_config()}; diff --git a/repertory/librepertory/src/providers/sia/sia_provider.cpp b/repertory/librepertory/src/providers/sia/sia_provider.cpp index 3909c255..87eae75d 100644 --- a/repertory/librepertory/src/providers/sia/sia_provider.cpp +++ b/repertory/librepertory/src/providers/sia/sia_provider.cpp @@ -900,6 +900,10 @@ auto sia_provider::upload_file_impl(const std::string &api_path, stop_type &stop_requested) -> api_error { REPERTORY_USES_FUNCTION_NAME(); + if (not utils::file::file{source_path}.exists()) { + return api_error::item_not_found; + } + curl::requests::http_put_file put_file{}; put_file.path = "/api/worker/object" + api_path; put_file.query["bucket"] = get_sia_config().bucket; From 2cae947d228dbd6ddf441ed87a722fd98ccaa04e Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Thu, 18 Sep 2025 17:53:36 -0500 Subject: [PATCH 061/136] [unit test] Complete all providers unit tests #12 --- repertory/librepertory/src/providers/s3/s3_provider.cpp | 2 -- repertory/librepertory/src/providers/sia/sia_provider.cpp | 4 ---- repertory/repertory_test/src/providers_test.cpp | 5 +++-- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/repertory/librepertory/src/providers/s3/s3_provider.cpp b/repertory/librepertory/src/providers/s3/s3_provider.cpp index 82703539..187ce69c 100644 --- a/repertory/librepertory/src/providers/s3/s3_provider.cpp +++ b/repertory/librepertory/src/providers/s3/s3_provider.cpp @@ -1204,8 +1204,6 @@ auto s3_provider::upload_file_impl(const std::string &api_path, return api_error::comm_error; } file_size = opt_size.value(); - } else { - return api_error::item_not_found; } const auto &cfg{get_s3_config()}; diff --git a/repertory/librepertory/src/providers/sia/sia_provider.cpp b/repertory/librepertory/src/providers/sia/sia_provider.cpp index 87eae75d..3909c255 100644 --- a/repertory/librepertory/src/providers/sia/sia_provider.cpp +++ b/repertory/librepertory/src/providers/sia/sia_provider.cpp @@ -900,10 +900,6 @@ auto sia_provider::upload_file_impl(const std::string &api_path, stop_type &stop_requested) -> api_error { REPERTORY_USES_FUNCTION_NAME(); - if (not utils::file::file{source_path}.exists()) { - return api_error::item_not_found; - } - curl::requests::http_put_file put_file{}; put_file.path = "/api/worker/object" + api_path; put_file.query["bucket"] = get_sia_config().bucket; diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index fe66c98b..5a5f9e89 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -1263,10 +1263,11 @@ TYPED_TEST(providers_test, upload_file_fails_if_source_not_found) { stop_type stop_requested{false}; auto res = this->provider->upload_file( "/no_src_upload.txt", "/path/does/not/exist.bin", stop_requested); - EXPECT_EQ(api_error::item_not_found, res); + EXPECT_NE(res, api_error::success); } -TYPED_TEST(providers_test, is_file_is_directory_cross_checks) { +TYPED_TEST(providers_test, + file_is_not_a_directory_and_a_directory_is_not_a_file) { if (this->provider->is_read_only()) { return; } From 9c79f151399ee0562749331f4d9c8c7e170ed2ab Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Thu, 18 Sep 2025 17:54:02 -0500 Subject: [PATCH 062/136] [unit test] Complete all providers unit tests #12 --- repertory/repertory_test/src/providers_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index 5a5f9e89..a3a545d6 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -1255,7 +1255,7 @@ TYPED_TEST(providers_test, upload_file_not_implemented_on_read_only) { EXPECT_EQ(api_error::not_implemented, res); } -TYPED_TEST(providers_test, upload_file_fails_if_source_not_found) { +TYPED_TEST(providers_test, upload_file_fails_if_source_is_not_found) { if (this->provider->is_read_only()) { return; } From 3cd22af906ad6592f89813847bd14a79aa7ded64 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Thu, 18 Sep 2025 18:12:27 -0500 Subject: [PATCH 063/136] setup rename tests --- .../src/fuse_drive_rename_test.cpp | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 repertory/repertory_test/src/fuse_drive_rename_test.cpp diff --git a/repertory/repertory_test/src/fuse_drive_rename_test.cpp b/repertory/repertory_test/src/fuse_drive_rename_test.cpp new file mode 100644 index 00000000..8d696306 --- /dev/null +++ b/repertory/repertory_test/src/fuse_drive_rename_test.cpp @@ -0,0 +1,34 @@ +/* + Copyright <2018-2025> + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ +#if !defined(_WIN32) + +#include "fixtures/fuse_fixture.hpp" + +namespace repertory { +TYPED_TEST_SUITE(fuse_test, fuse_provider_types); + +TYPED_TEST(fuse_test, can_rename_a_file) {} + +TYPED_TEST(fuse_test, can_rename_a_directory) {} +} // namespace repertory + +#endif // !defined(_WIN32) From 17b7d011db48d3dc30c0f83b78d6a5dc6da28a70 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Fri, 19 Sep 2025 07:02:34 -0500 Subject: [PATCH 064/136] begin switch to common drive fixture --- .../include/fixtures/drive_fixture.hpp | 22 +- .../include/fixtures/fuse_fixture.hpp | 513 ------------------ .../include/fixtures/winfsp_fixture.hpp | 361 ------------ .../src/fuse_drive_access_test.cpp | 4 +- .../src/fuse_drive_chmod_test.cpp | 4 +- .../src/fuse_drive_chown_test.cpp | 4 +- .../src/fuse_drive_create_and_open_test.cpp | 5 +- .../src/fuse_drive_rdrw_test.cpp | 4 +- .../src/fuse_drive_rename_test.cpp | 4 +- .../src/fuse_drive_test_legacy.cpp | 40 -- .../src/winfsp_drive_create_attr_test.cpp | 4 +- .../src/winfsp_drive_create_nl_test.cpp | 4 +- .../src/winfsp_drive_create_test.cpp | 4 +- .../src/winfsp_drive_delete_test.cpp | 4 +- .../src/winfsp_drive_info_test.cpp | 4 +- .../src/winfsp_drive_rdrw_test.cpp | 4 +- .../src/winfsp_drive_rename_test.cpp | 4 +- .../repertory_test/src/winfsp_drive_test.cpp | 4 +- .../src/winfsp_drive_volume_test.cpp | 4 +- 19 files changed, 46 insertions(+), 951 deletions(-) delete mode 100644 repertory/repertory_test/include/fixtures/fuse_fixture.hpp delete mode 100644 repertory/repertory_test/include/fixtures/winfsp_fixture.hpp diff --git a/repertory/repertory_test/include/fixtures/drive_fixture.hpp b/repertory/repertory_test/include/fixtures/drive_fixture.hpp index c1fc99d8..59bb979a 100644 --- a/repertory/repertory_test/include/fixtures/drive_fixture.hpp +++ b/repertory/repertory_test/include/fixtures/drive_fixture.hpp @@ -19,8 +19,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#ifndef REPERTORY_TEST_INCLUDE_FIXTURES_PLATFORM_FIXTURE_HPP -#define REPERTORY_TEST_INCLUDE_FIXTURES_PLATFORM_FIXTURE_HPP +#ifndef REPERTORY_TEST_INCLUDE_FIXTURES_DRIVE_FIXTURE_HPP +#define REPERTORY_TEST_INCLUDE_FIXTURES_DRIVE_FIXTURE_HPP #include "test_common.hpp" @@ -37,7 +37,7 @@ #include "providers/i_provider.hpp" #include "providers/s3/s3_provider.hpp" #include "providers/sia/sia_provider.hpp" -#else +#else // !defined(_WIN32) #include "comm/curl/curl_comm.hpp" #include "db/i_meta_db.hpp" #include "db/meta_db.hpp" @@ -45,10 +45,12 @@ #include "providers/encrypt/encrypt_provider.hpp" #include "providers/s3/s3_provider.hpp" #include "providers/sia/sia_provider.hpp" + #if !defined(ACCESSPERMS) -#define ACCESSPERMS (S_IRWXU | S_IRWXG | S_IRWXO) /* 0777 */ -#endif -#endif +// 0777 +#define ACCESSPERMS (S_IRWXU | S_IRWXG | S_IRWXO) +#endif // !defined(ACCESSPERMS) +#endif // defined(_WIN32) namespace { std::atomic provider_idx{0U}; @@ -503,20 +505,26 @@ public: #if !defined(_WIN32) template std::unique_ptr drive_fixture::config; + template std::unique_ptr drive_fixture::meta{}; #endif // !defined(_WIN32) template std::filesystem::path drive_fixture::current_directory; + template provider_type drive_fixture::current_provider{provider_t::type2}; + template std::vector drive_fixture::drive_args; + template std::vector drive_fixture::drive_args2; + template std::string drive_fixture::mount_location; + template std::string drive_fixture::mount_location2; @@ -532,4 +540,4 @@ using fuse_test = drive_fixture; #endif } // namespace repertory -#endif // REPERTORY_TEST_INCLUDE_FIXTURES_PLATFORM_FIXTURE_HPP +#endif // REPERTORY_TEST_INCLUDE_FIXTURES_DRIVE_FIXTURE_HPP diff --git a/repertory/repertory_test/include/fixtures/fuse_fixture.hpp b/repertory/repertory_test/include/fixtures/fuse_fixture.hpp deleted file mode 100644 index 7b62352a..00000000 --- a/repertory/repertory_test/include/fixtures/fuse_fixture.hpp +++ /dev/null @@ -1,513 +0,0 @@ -/* - Copyright <2018-2025> - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ -#ifndef REPERTORY_TEST_INCLUDE_FIXTURES_FUSE_FIXTURE_HPP -#define REPERTORY_TEST_INCLUDE_FIXTURES_FUSE_FIXTURE_HPP -#if !defined(_WIN32) - -#include "test_common.hpp" - -#include "app_config.hpp" -#include "comm/curl/curl_comm.hpp" -#include "db/i_meta_db.hpp" -#include "db/meta_db.hpp" -#include "drives/fuse/fuse_drive.hpp" -#include "platform/platform.hpp" -#include "providers/encrypt/encrypt_provider.hpp" -#include "providers/s3/s3_provider.hpp" -#include "providers/sia/sia_provider.hpp" -#include "types/repertory.hpp" -#include "utils/event_capture.hpp" -#include "utils/file_utils.hpp" -#include "utils/path.hpp" -#include "utils/utils.hpp" - -#if !defined(ACCESSPERMS) -#define ACCESSPERMS (S_IRWXU | S_IRWXG | S_IRWXO) /* 0777 */ -#endif - -namespace { -std::atomic provider_idx{0U}; - -constexpr auto SLEEP_SECONDS{1.5s}; -} // namespace - -namespace repertory { -struct local_s3_no_encryption final { - static constexpr provider_type type{provider_type::s3}; - static constexpr provider_type type2{provider_type::s3}; - static constexpr std::uint16_t remote_port{41000U}; - static constexpr bool force_legacy_encryption{false}; - static constexpr std::string_view encryption_token{""}; -}; - -struct local_s3_encryption final { - static constexpr provider_type type{provider_type::s3}; - static constexpr provider_type type2{provider_type::s3}; - static constexpr std::uint16_t remote_port{41000U}; - static constexpr bool force_legacy_encryption{false}; - static constexpr std::string_view encryption_token{"encryption_token"}; -}; - -struct local_s3_legacy_encryption final { - static constexpr provider_type type{provider_type::s3}; - static constexpr provider_type type2{provider_type::s3}; - static constexpr std::uint16_t remote_port{41000U}; - static constexpr bool force_legacy_encryption{true}; - static constexpr std::string_view encryption_token{"encryption_token"}; -}; - -struct remote_s3_no_encryption final { - static constexpr provider_type type{provider_type::remote}; - static constexpr provider_type type2{provider_type::s3}; - static constexpr std::uint16_t remote_port{41000U}; - static constexpr bool force_legacy_encryption{false}; - static constexpr std::string_view encryption_token{""}; -}; - -struct remote_s3_encryption final { - static constexpr provider_type type{provider_type::remote}; - static constexpr provider_type type2{provider_type::s3}; - static constexpr std::uint16_t remote_port{41000U}; - static constexpr bool force_legacy_encryption{false}; - static constexpr std::string_view encryption_token{"encryption_token"}; -}; - -struct remote_s3_legacy_encryption final { - static constexpr provider_type type{provider_type::remote}; - static constexpr provider_type type2{provider_type::s3}; - static constexpr std::uint16_t remote_port{41000U}; - static constexpr bool force_legacy_encryption{true}; - static constexpr std::string_view encryption_token{"encryption_token"}; -}; - -struct local_sia final { - static constexpr provider_type type{provider_type::sia}; - static constexpr provider_type type2{provider_type::sia}; - static constexpr std::uint16_t remote_port{41001U}; - static constexpr bool force_legacy_encryption{false}; - static constexpr std::string_view encryption_token{""}; -}; - -struct remote_sia final { - static constexpr provider_type type{provider_type::remote}; - static constexpr provider_type type2{provider_type::sia}; - static constexpr std::uint16_t remote_port{41001U}; - static constexpr bool force_legacy_encryption{false}; - static constexpr std::string_view encryption_token{""}; -}; - -struct remote_linux_to_winfsp final { - static constexpr provider_type type{provider_type::remote}; - static constexpr provider_type type2{provider_type::unknown}; - static constexpr std::uint16_t remote_port{41002U}; - static constexpr bool force_legacy_encryption{false}; - static constexpr std::string_view encryption_token{""}; -}; - -template class fuse_test : public ::testing::Test { -public: - static std::unique_ptr config; - static std::filesystem::path current_directory; - static provider_type current_provider; - static std::vector drive_args; - static std::vector drive_args2; - static std::unique_ptr meta; - static std::string mount_location; - static std::string mount_location2; - -protected: - static void SetUpTestCase() { - current_directory = std::filesystem::current_path(); - - const auto mount_s3 = [&](bool as_remote) { - if (::testing::Test::HasFatalFailure()) { - return; - } - - { - auto test_directory = utils::path::combine( - test::get_test_output_dir(), - { - "fuse_test", - fmt::format("{}_{}", - app_config::get_provider_name(current_provider), - as_remote), - }); - - mount_location = utils::path::combine(test_directory, {"mount"}); - ASSERT_TRUE(utils::file::directory(mount_location).create_directory()); - - auto cfg_directory = utils::path::combine(test_directory, {"cfg"}); - ASSERT_TRUE(utils::file::directory(cfg_directory).create_directory()); - - config = std::make_unique(current_provider, cfg_directory); - { - app_config src_cfg{ - provider_type::s3, - utils::path::combine(test::get_test_config_dir(), {"s3"}), - }; - - auto cfg = src_cfg.get_s3_config(); - cfg.force_legacy_encryption = provider_t::force_legacy_encryption; - cfg.encryption_token = provider_t::encryption_token; - config->set_enable_drive_events(true); - config->set_event_level(event_level::trace); - config->set_s3_config(cfg); - - auto r_cfg = config->get_remote_mount(); - r_cfg.enable = true; - r_cfg.api_port = provider_t::remote_port; - config->set_remote_mount(r_cfg); - } - - drive_args = std::vector({ - "-dd", - config->get_data_directory(), - "-s3", - "-na", - "s3", - }); - } - - config->set_database_type(database_type::sqlite); - meta = create_meta_db(*config); - execute_mount(drive_args, mount_location); - }; - - const auto mount_sia = [&](bool as_remote) { - if (::testing::Test::HasFatalFailure()) { - return; - } - - { - auto test_directory = utils::path::combine( - test::get_test_output_dir(), - { - "fuse_test", - fmt::format("{}_{}", - app_config::get_provider_name(current_provider), - as_remote), - }); - - mount_location = utils::path::combine(test_directory, {"mount"}); - ASSERT_TRUE(utils::file::directory(mount_location).create_directory()); - - auto cfg_directory = utils::path::combine(test_directory, {"cfg"}); - ASSERT_TRUE(utils::file::directory(cfg_directory).create_directory()); - - config = std::make_unique(current_provider, cfg_directory); - { - app_config src_cfg{ - provider_type::sia, - utils::path::combine(test::get_test_config_dir(), {"sia"}), - }; - config->set_enable_drive_events(true); - config->set_event_level(event_level::trace); - config->set_host_config(src_cfg.get_host_config()); - config->set_sia_config(src_cfg.get_sia_config()); - - auto r_cfg = config->get_remote_mount(); - r_cfg.enable = true; - r_cfg.api_port = provider_t::remote_port; - config->set_remote_mount(r_cfg); - } - - drive_args = std::vector({ - "-dd", - config->get_data_directory(), - "-na", - "sia", - }); - } - - config->set_database_type(database_type::sqlite); - meta = create_meta_db(*config); - execute_mount(drive_args, mount_location); - }; - - const auto mount_remote = [&]() { - if (::testing::Test::HasFatalFailure()) { - return; - } - - { - mount_location2 = mount_location; - auto test_directory = utils::path::combine( - test::get_test_output_dir(), - { - "fuse_test", - fmt::format("{}_{}_{}", - app_config::get_provider_name(provider_t::type), - app_config::get_provider_name(provider_t::type2), - provider_t::remote_port), - }); - - mount_location = utils::path::combine(test_directory, {"mount"}); - ASSERT_TRUE(utils::file::directory(mount_location).create_directory()); - - auto cfg_directory = utils::path::combine(test_directory, {"cfg"}); - ASSERT_TRUE(utils::file::directory(cfg_directory).create_directory()); - - auto config2 = - std::make_unique(provider_type::remote, cfg_directory); - config2->set_enable_drive_events(true); - config2->set_event_level(event_level::trace); - config2->set_database_type(database_type::sqlite); - - drive_args2 = std::vector({ - "-dd", - config2->get_data_directory(), - "-rm", - fmt::format("localhost:{}", provider_t::remote_port), - }); - } - - execute_mount(drive_args2, mount_location); - }; - - switch (provider_t::type) { - case provider_type::s3: { - mount_s3(false); - } break; - - case provider_type::sia: { - mount_sia(false); - } break; - - case provider_type::remote: { - switch (provider_t::type2) { - case provider_type::s3: { - mount_s3(true); - } break; - - case provider_type::sia: { - mount_sia(true); - } break; - - case provider_type::unknown: - mount_remote(); - return; - - default: - throw std::runtime_error("remote provider type is not implemented"); - return; - } - - mount_remote(); - } break; - - default: - throw std::runtime_error("provider type is not implemented"); - return; - } - } - - static void TearDownTestCase() { - if (provider_t::type == provider_type::remote) { - execute_unmount(drive_args2); - execute_unmount(drive_args); - } else { - execute_unmount(drive_args); - } - - meta.reset(); - config.reset(); - - std::filesystem::current_path(current_directory); - } - -public: - static auto create_file_path(std::string &file_name) { - file_name += std::to_string(++provider_idx); - auto file_path = utils::path::combine(mount_location, {file_name}); - return file_path; - } - - static auto create_file_and_test(std::string &file_name, mode_t perms) - -> std::string { - file_name += std::to_string(++provider_idx); - auto file_path = utils::path::combine(mount_location, {file_name}); - - auto handle = open(file_path.c_str(), O_CREAT | O_EXCL | O_RDWR, perms); - EXPECT_LE(1, handle); - - auto opt_size = utils::file::file{file_path}.size(); - EXPECT_TRUE(opt_size.has_value()); - if (opt_size.has_value()) { - EXPECT_EQ(0U, opt_size.value()); - } - - EXPECT_EQ(0, close(handle)); - - EXPECT_TRUE(utils::file::file(file_path).exists()); - EXPECT_FALSE(utils::file::directory(file_path).exists()); - - struct stat64 u_stat{}; - EXPECT_EQ(0, stat64(file_path.c_str(), &u_stat)); - EXPECT_EQ(getgid(), u_stat.st_gid); - EXPECT_EQ(getuid(), u_stat.st_uid); - - return file_path; - } - - static auto create_file_and_test(std::string &file_name) -> std::string { - return create_file_and_test(file_name, ACCESSPERMS); - } - - static auto create_directory_and_test(std::string &dir_name, mode_t perms) - -> std::string { - dir_name += std::to_string(++provider_idx); - - auto dir_path = utils::path::combine(mount_location, {dir_name}); - mkdir(dir_path.c_str(), perms); - - EXPECT_TRUE(utils::file::directory(dir_path).exists()); - EXPECT_EQ(0U, utils::file::directory(dir_path).count(false)); - EXPECT_EQ(0U, utils::file::directory(dir_path).count(true)); - EXPECT_FALSE(utils::file::file(dir_path).exists()); - - struct stat64 u_stat{}; - EXPECT_EQ(0, stat64(dir_path.c_str(), &u_stat)); - EXPECT_EQ(getgid(), u_stat.st_gid); - EXPECT_EQ(getuid(), u_stat.st_uid); - - return dir_path; - } - - static auto create_directory_and_test(std::string &dir_name) -> std::string { - return create_directory_and_test(dir_name, ACCESSPERMS); - } - - static auto create_root_file(std::string &file_name) -> std::string { - auto file_path = create_file_and_test(file_name); - auto api_path = utils::path::create_api_path(file_name); - - [[maybe_unused]] auto res = - meta->set_item_meta(api_path, { - {META_UID, "0"}, - {META_GID, "0"}, - }); - std::this_thread::sleep_for(SLEEP_SECONDS); - - return file_path; - } - - static void execute_mount(auto args, auto location) { - EXPECT_TRUE(utils::file::change_to_process_directory()); - - args.emplace_back(location); -#if defined(__APPLE__) - auto mount_cmd = "./repertory.app/Contents/MacOS/repertory " + - utils::string::join(args, ' '); -#else // !defined(__APPLE__) - auto mount_cmd = "./repertory " + utils::string::join(args, ' '); -#endif // defined(__APPLE__) - - std::cout << "mount command: " << mount_cmd << std::endl; - - ASSERT_EQ(0, system(mount_cmd.c_str())); - std::this_thread::sleep_for(5s); - ASSERT_TRUE(utils::file::directory{location}.exists()); - } - - static void execute_unmount(auto args) { - EXPECT_TRUE(utils::file::change_to_process_directory()); - - args.emplace_back("-unmount"); -#if defined(__APPLE__) - auto unmount_cmd = "./repertory.app/Contents/MacOS/repertory " + - utils::string::join(args, ' '); -#else // !defined(__APPLE__) - auto unmount_cmd = "./repertory " + utils::string::join(args, ' '); -#endif // defined(__APPLE__) - - std::cout << "unmount command: " << unmount_cmd << std::endl; - - auto res = system(unmount_cmd.c_str()); - EXPECT_EQ(0, res); - } - - static void rmdir_and_test(std::string_view dir_path) { - EXPECT_TRUE(utils::file::directory(dir_path).remove()); - EXPECT_FALSE(utils::file::directory(dir_path).exists()); - - EXPECT_FALSE(utils::file::file(dir_path).exists()); - } - - static void unlink_file_and_test(std::string_view file_path) { - EXPECT_TRUE(utils::file::file(file_path).remove()); - EXPECT_FALSE(utils::file::file(file_path).exists()); - - EXPECT_FALSE(utils::file::directory(file_path).exists()); - } - - static void unlink_root_file(const std::string &file_path) { - auto api_path = utils::path::create_api_path( - utils::path::strip_to_file_name(file_path)); - - [[maybe_unused]] auto res = - meta->set_item_meta(api_path, { - {META_UID, std::to_string(getuid())}, - {META_GID, std::to_string(getgid())}, - }); - std::this_thread::sleep_for(SLEEP_SECONDS); - - unlink_file_and_test(file_path); - } -}; - -template -std::unique_ptr fuse_test::config; - -template -std::filesystem::path fuse_test::current_directory; - -template -provider_type fuse_test::current_provider{provider_t::type2}; - -template -std::vector fuse_test::drive_args; - -template -std::vector fuse_test::drive_args2; - -template -std::unique_ptr fuse_test::meta{}; - -template -std::string fuse_test::mount_location; - -template -std::string fuse_test::mount_location2; - -using fuse_provider_types = - ::testing::Types; -// using fuse_provider_types = -// ::testing::Types; -} // namespace repertory - -#endif // !defined(_WIN32) -#endif // REPERTORY_TEST_INCLUDE_FIXTURES_FUSE_FIXTURE_HPP diff --git a/repertory/repertory_test/include/fixtures/winfsp_fixture.hpp b/repertory/repertory_test/include/fixtures/winfsp_fixture.hpp deleted file mode 100644 index 141c86c9..00000000 --- a/repertory/repertory_test/include/fixtures/winfsp_fixture.hpp +++ /dev/null @@ -1,361 +0,0 @@ -/* - Copyright <2018-2025> - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ -#ifndef REPERTORY_TEST_INCLUDE_FIXTURES_WINFSP_FIXTURE_HPP -#define REPERTORY_TEST_INCLUDE_FIXTURES_WINFSP_FIXTURE_HPP -#if defined(_WIN32) - -#include "test_common.hpp" - -#include "app_config.hpp" -#include "drives/winfsp/remotewinfsp/remote_winfsp_drive.hpp" -#include "drives/winfsp/winfsp_drive.hpp" -#include "platform/platform.hpp" -#include "providers/i_provider.hpp" -#include "providers/s3/s3_provider.hpp" -#include "providers/sia/sia_provider.hpp" -#include "types/repertory.hpp" -#include "utils/file_utils.hpp" -#include "utils/path.hpp" - -namespace { -std::atomic idx{0U}; -constexpr auto SLEEP_SECONDS{1.5s}; -} // namespace - -namespace repertory { -struct local_s3_no_encryption final { - static constexpr provider_type type{provider_type::s3}; - static constexpr provider_type type2{provider_type::s3}; - static constexpr std::uint16_t remote_port{41000U}; - static constexpr bool force_legacy_encryption{false}; - static constexpr std::string_view encryption_token{""}; -}; - -struct local_s3_encryption final { - static constexpr provider_type type{provider_type::s3}; - static constexpr provider_type type2{provider_type::s3}; - static constexpr std::uint16_t remote_port{41000U}; - static constexpr bool force_legacy_encryption{false}; - static constexpr std::string_view encryption_token{"encryption_token"}; -}; - -struct local_s3_legacy_encryption final { - static constexpr provider_type type{provider_type::s3}; - static constexpr provider_type type2{provider_type::s3}; - static constexpr std::uint16_t remote_port{41000U}; - static constexpr bool force_legacy_encryption{true}; - static constexpr std::string_view encryption_token{"encryption_token"}; -}; - -struct remote_s3_no_encryption final { - static constexpr provider_type type{provider_type::remote}; - static constexpr provider_type type2{provider_type::s3}; - static constexpr std::uint16_t remote_port{41000U}; - static constexpr bool force_legacy_encryption{false}; - static constexpr std::string_view encryption_token{""}; -}; - -struct remote_s3_encryption final { - static constexpr provider_type type{provider_type::remote}; - static constexpr provider_type type2{provider_type::s3}; - static constexpr std::uint16_t remote_port{41000U}; - static constexpr bool force_legacy_encryption{false}; - static constexpr std::string_view encryption_token{"encryption_token"}; -}; - -struct remote_s3_legacy_encryption final { - static constexpr provider_type type{provider_type::remote}; - static constexpr provider_type type2{provider_type::s3}; - static constexpr std::uint16_t remote_port{41000U}; - static constexpr bool force_legacy_encryption{true}; - static constexpr std::string_view encryption_token{"encryption_token"}; -}; - -struct local_sia final { - static constexpr provider_type type{provider_type::sia}; - static constexpr provider_type type2{provider_type::sia}; - static constexpr std::uint16_t remote_port{41001U}; - static constexpr bool force_legacy_encryption{false}; - static constexpr std::string_view encryption_token{""}; -}; - -struct remote_sia final { - static constexpr provider_type type{provider_type::remote}; - static constexpr provider_type type2{provider_type::sia}; - static constexpr std::uint16_t remote_port{41001U}; - static constexpr bool force_legacy_encryption{false}; - static constexpr std::string_view encryption_token{""}; -}; - -struct remote_winfsp_to_linux final { - static constexpr provider_type type{provider_type::remote}; - static constexpr provider_type type2{provider_type::unknown}; - static constexpr std::uint16_t remote_port{41002U}; - static constexpr bool force_legacy_encryption{false}; - static constexpr std::string_view encryption_token{""}; -}; - -template class winfsp_test : public ::testing::Test { -public: - static std::filesystem::path current_directory; - static provider_type current_provider; - static std::vector drive_args; - static std::vector drive_args2; - static std::string mount_location; - static std::string mount_location2; - -protected: - static void SetUpTestCase() { - current_directory = std::filesystem::current_path(); - - mount_location = utils::string::to_lower(std::string{"U:"}); - - const auto mount_s3 = [&](bool as_remote) { - { - auto test_directory = utils::path::combine( - test::get_test_output_dir(), - { - "winfsp_test", - fmt::format("{}_{}", - app_config::get_provider_name(current_provider), - as_remote), - }); - - auto cfg_directory = utils::path::combine(test_directory, {"cfg"}); - ASSERT_TRUE(utils::file::directory(cfg_directory).create_directory()); - - auto config = - std::make_unique(provider_type::s3, cfg_directory); - { - app_config src_cfg{ - provider_type::s3, - utils::path::combine(test::get_test_config_dir(), {"s3"}), - }; - - auto cfg = src_cfg.get_s3_config(); - cfg.force_legacy_encryption = provider_t::force_legacy_encryption; - cfg.encryption_token = provider_t::encryption_token; - config->set_enable_drive_events(true); - config->set_event_level(event_level::trace); - config->set_s3_config(cfg); - - auto r_cfg = config->get_remote_mount(); - r_cfg.enable = true; - r_cfg.api_port = provider_t::remote_port; - config->set_remote_mount(r_cfg); - } - - drive_args = std::vector({ - "-dd", - config->get_data_directory(), - "-s3", - "-na", - "s3", - mount_location, - }); - } - execute_mount(drive_args, mount_location); - }; - - const auto mount_sia = [&](bool as_remote) { - { - auto test_directory = utils::path::combine( - test::get_test_output_dir(), - { - "winfsp_test", - fmt::format("{}_{}", - app_config::get_provider_name(current_provider), - as_remote), - }); - - auto cfg_directory = utils::path::combine(test_directory, {"cfg"}); - ASSERT_TRUE(utils::file::directory(cfg_directory).create_directory()); - - auto config = - std::make_unique(provider_type::sia, cfg_directory); - { - app_config src_cfg{ - provider_type::sia, - utils::path::combine(test::get_test_config_dir(), {"sia"}), - }; - config->set_enable_drive_events(true); - config->set_event_level(event_level::trace); - config->set_host_config(src_cfg.get_host_config()); - config->set_sia_config(src_cfg.get_sia_config()); - - auto r_cfg = config->get_remote_mount(); - r_cfg.enable = true; - r_cfg.api_port = provider_t::remote_port; - config->set_remote_mount(r_cfg); - } - - drive_args = std::vector({ - "-dd", - config->get_data_directory(), - "-na", - "sia", - mount_location, - }); - } - - execute_mount(drive_args, mount_location); - }; - - const auto mount_remote = [&]() { - { - auto test_directory = utils::path::combine( - test::get_test_output_dir(), - { - "winfsp_test", - fmt::format("{}_{}_{}", - app_config::get_provider_name(provider_t::type), - app_config::get_provider_name(provider_t::type2), - provider_t::remote_port), - }); - - mount_location2 = mount_location; - mount_location = utils::string::to_lower(std::string{"V:"}); - - auto cfg_directory = utils::path::combine(test_directory, {"cfg2"}); - ASSERT_TRUE(utils::file::directory(cfg_directory).create_directory()); - - auto config = - std::make_unique(provider_type::remote, cfg_directory); - config->set_enable_drive_events(true); - config->set_event_level(event_level::trace); - - drive_args2 = std::vector({ - "-dd", - config->get_data_directory(), - "-rm", - fmt::format("localhost:{}", provider_t::remote_port), - mount_location, - }); - } - - execute_mount(drive_args2, mount_location); - }; - - switch (provider_t::type) { - case provider_type::s3: { - mount_s3(false); - } break; - - case provider_type::sia: { - mount_sia(false); - } break; - - case provider_type::remote: { - switch (provider_t::type2) { - case provider_type::s3: { - mount_s3(true); - } break; - - case provider_type::sia: { - mount_sia(true); - } break; - - case provider_type::unknown: - mount_remote(); - return; - - default: - throw std::runtime_error("remote provider type is not implemented"); - return; - } - - mount_remote(); - } break; - - default: - throw std::runtime_error("provider type is not implemented"); - return; - } - } - - static void TearDownTestCase() { - if (provider_t::type == provider_type::remote) { - execute_unmount(drive_args2, mount_location); - if (provider_t::type2 != provider_type::unknown) { - execute_unmount(drive_args, mount_location2); - } - } else { - execute_unmount(drive_args, mount_location); - } - std::filesystem::current_path(current_directory); - } - - static void execute_mount(auto args, auto location) { - auto mount_cmd = - "start .\\repertory.exe -f " + utils::string::join(args, ' '); - std::cout << "mount command: " << mount_cmd << std::endl; - ASSERT_EQ(0, system(mount_cmd.c_str())); - std::this_thread::sleep_for(5s); - ASSERT_TRUE(utils::file::directory{location}.exists()); - } - - static void execute_unmount(auto args, auto location) { - std::this_thread::sleep_for(10s); - auto unmounted{false}; - - auto unmount_cmd = - ".\\repertory.exe " + utils::string::join(args, ' ') + " -unmount"; - for (int i = 0; not unmounted && (i < 6); i++) { - std::cout << "unmount command: " << unmount_cmd << std::endl; - system(unmount_cmd.c_str()); - unmounted = not utils::file::directory{location}.exists(); - if (not unmounted) { - std::this_thread::sleep_for(5s); - } - } - - ASSERT_TRUE(unmounted); - } -}; - -template -std::filesystem::path winfsp_test::current_directory; - -template -provider_type winfsp_test::current_provider{provider_t::type2}; - -template -std::vector winfsp_test::drive_args; - -template -std::vector winfsp_test::drive_args2; - -template -std::string winfsp_test::mount_location; - -template -std::string winfsp_test::mount_location2; - -using winfsp_provider_types = - ::testing::Types; -} // namespace repertory - -#endif // defined(_WIN32) -#endif // REPERTORY_TEST_INCLUDE_FIXTURES_WINFSP_FIXTURE_HPP diff --git a/repertory/repertory_test/src/fuse_drive_access_test.cpp b/repertory/repertory_test/src/fuse_drive_access_test.cpp index b4cde5e3..65761203 100644 --- a/repertory/repertory_test/src/fuse_drive_access_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_access_test.cpp @@ -21,7 +21,7 @@ */ #if !defined(_WIN32) -#include "fixtures/fuse_fixture.hpp" +#include "fixtures/drive_fixture.hpp" namespace { const auto access_permutations = { @@ -71,7 +71,7 @@ void perform_access_test(auto &&permutation, auto &&item_path) { } // namespace namespace repertory { -TYPED_TEST_SUITE(fuse_test, fuse_provider_types); +TYPED_TEST_SUITE(fuse_test, platform_provider_types); TYPED_TEST(fuse_test, access_can_check_if_item_does_not_exist) { EXPECT_EQ( diff --git a/repertory/repertory_test/src/fuse_drive_chmod_test.cpp b/repertory/repertory_test/src/fuse_drive_chmod_test.cpp index f06569b2..aa143a62 100644 --- a/repertory/repertory_test/src/fuse_drive_chmod_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_chmod_test.cpp @@ -21,10 +21,10 @@ */ #if !defined(_WIN32) -#include "fixtures/fuse_fixture.hpp" +#include "fixtures/drive_fixture.hpp" namespace repertory { -TYPED_TEST_SUITE(fuse_test, fuse_provider_types); +TYPED_TEST_SUITE(fuse_test, platform_provider_types); TYPED_TEST(fuse_test, chmod_can_not_chmod_set_sticky_if_not_root) { std::string file_name{"chmod_test"}; diff --git a/repertory/repertory_test/src/fuse_drive_chown_test.cpp b/repertory/repertory_test/src/fuse_drive_chown_test.cpp index abf6fad0..0c2aac85 100644 --- a/repertory/repertory_test/src/fuse_drive_chown_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_chown_test.cpp @@ -21,10 +21,10 @@ */ #if !defined(_WIN32) -#include "fixtures/fuse_fixture.hpp" +#include "fixtures/drive_fixture.hpp" namespace repertory { -TYPED_TEST_SUITE(fuse_test, fuse_provider_types); +TYPED_TEST_SUITE(fuse_test, platform_provider_types); TYPED_TEST(fuse_test, chown_can_chown_group_if_owner_and_a_member_of_the_group) { diff --git a/repertory/repertory_test/src/fuse_drive_create_and_open_test.cpp b/repertory/repertory_test/src/fuse_drive_create_and_open_test.cpp index b5691615..02b349c8 100644 --- a/repertory/repertory_test/src/fuse_drive_create_and_open_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_create_and_open_test.cpp @@ -21,9 +21,10 @@ */ #if !defined(_WIN32) -#include "fixtures/fuse_fixture.hpp" +#include "fixtures/drive_fixture.hpp" + namespace repertory { -TYPED_TEST_SUITE(fuse_test, fuse_provider_types); +TYPED_TEST_SUITE(fuse_test, platform_provider_types); TYPED_TEST(fuse_test, create_can_create_and_remove_directory) { std::string dir_name{"create_test"}; diff --git a/repertory/repertory_test/src/fuse_drive_rdrw_test.cpp b/repertory/repertory_test/src/fuse_drive_rdrw_test.cpp index 9405e39d..8a295845 100644 --- a/repertory/repertory_test/src/fuse_drive_rdrw_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_rdrw_test.cpp @@ -21,10 +21,10 @@ */ #if !defined(_WIN32) -#include "fixtures/fuse_fixture.hpp" +#include "fixtures/drive_fixture.hpp" namespace repertory { -TYPED_TEST_SUITE(fuse_test, fuse_provider_types); +TYPED_TEST_SUITE(fuse_test, platform_provider_types); TYPED_TEST(fuse_test, rdrw_can_read_and_write_file) { std::string file_name{"create_test"}; diff --git a/repertory/repertory_test/src/fuse_drive_rename_test.cpp b/repertory/repertory_test/src/fuse_drive_rename_test.cpp index 8d696306..0b654c7c 100644 --- a/repertory/repertory_test/src/fuse_drive_rename_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_rename_test.cpp @@ -21,10 +21,10 @@ */ #if !defined(_WIN32) -#include "fixtures/fuse_fixture.hpp" +#include "fixtures/drive_fixture.hpp" namespace repertory { -TYPED_TEST_SUITE(fuse_test, fuse_provider_types); +TYPED_TEST_SUITE(fuse_test, platform_provider_types); TYPED_TEST(fuse_test, can_rename_a_file) {} diff --git a/repertory/repertory_test/src/fuse_drive_test_legacy.cpp b/repertory/repertory_test/src/fuse_drive_test_legacy.cpp index 41ba37cd..1a7b1a33 100644 --- a/repertory/repertory_test/src/fuse_drive_test_legacy.cpp +++ b/repertory/repertory_test/src/fuse_drive_test_legacy.cpp @@ -1,43 +1,3 @@ -// static void test_rename_file(const std::string &from_file_path, -// const std::string &to_file_path, -// bool is_rename_supported) { -// std::cout << __FUNCTION__ << std::endl; -// auto fd = open(from_file_path.c_str(), O_RDWR, S_IRUSR | S_IWUSR | -// S_IRGRP); EXPECT_LE(1, fd); close(fd); -// -// std::this_thread::sleep_for(SLEEP_SECONDS); -// -// if (is_rename_supported) { -// EXPECT_EQ(0, rename(from_file_path.c_str(), to_file_path.c_str())); -// EXPECT_FALSE(utils::file::is_file(from_file_path)); -// EXPECT_TRUE(utils::file::is_file(to_file_path)); -// } else { -// EXPECT_EQ(-1, rename(from_file_path.c_str(), to_file_path.c_str())); -// EXPECT_TRUE(utils::file::is_file(from_file_path)); -// EXPECT_FALSE(utils::file::is_file(to_file_path)); -// } -// } -// -// static void test_rename_directory(const std::string &from_dir_path, -// const std::string &to_dir_path, -// bool is_rename_supported) { -// std::cout << __FUNCTION__ << std::endl; -// EXPECT_EQ(0, mkdir(from_dir_path.c_str(), -// S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP)); -// std::this_thread::sleep_for(SLEEP_SECONDS); -// -// EXPECT_TRUE(utils::file::is_directory(from_dir_path)); -// if (is_rename_supported) { -// EXPECT_EQ(0, rename(from_dir_path.c_str(), to_dir_path.c_str())); -// EXPECT_FALSE(utils::file::is_directory(from_dir_path)); -// EXPECT_TRUE(utils::file::is_directory(to_dir_path)); -// } else { -// EXPECT_EQ(-1, rename(from_dir_path.c_str(), to_dir_path.c_str())); -// EXPECT_TRUE(utils::file::is_directory(from_dir_path)); -// EXPECT_FALSE(utils::file::is_directory(to_dir_path)); -// } -// } -// // static void test_truncate(const std::string &file_path) { // std::cout << __FUNCTION__ << std::endl; // EXPECT_EQ(0, truncate(file_path.c_str(), 10u)); diff --git a/repertory/repertory_test/src/winfsp_drive_create_attr_test.cpp b/repertory/repertory_test/src/winfsp_drive_create_attr_test.cpp index 46f81075..53c98890 100644 --- a/repertory/repertory_test/src/winfsp_drive_create_attr_test.cpp +++ b/repertory/repertory_test/src/winfsp_drive_create_attr_test.cpp @@ -25,10 +25,10 @@ // Implemented test cases based on WinFsp tests: // https://github.com/winfsp/winfsp/blob/v2.0/tst/winfsp-tests // -#include "fixtures/winfsp_fixture.hpp" +#include "fixtures/drive_fixture.hpp" namespace repertory { -TYPED_TEST_SUITE(winfsp_test, winfsp_provider_types); +TYPED_TEST_SUITE(winfsp_test, platform_provider_types); TYPED_TEST(winfsp_test, cr8_attr_can_create_new_file_with_normal_attribute) { auto file_path{ diff --git a/repertory/repertory_test/src/winfsp_drive_create_nl_test.cpp b/repertory/repertory_test/src/winfsp_drive_create_nl_test.cpp index 4a780337..9ed6e779 100644 --- a/repertory/repertory_test/src/winfsp_drive_create_nl_test.cpp +++ b/repertory/repertory_test/src/winfsp_drive_create_nl_test.cpp @@ -25,10 +25,10 @@ // Implemented test cases based on WinFsp tests: // https://github.com/winfsp/winfsp/blob/v2.0/tst/winfsp-tests // -#include "fixtures/winfsp_fixture.hpp" +#include "fixtures/drive_fixture.hpp" namespace repertory { -TYPED_TEST_SUITE(winfsp_test, winfsp_provider_types); +TYPED_TEST_SUITE(winfsp_test, platform_provider_types); TYPED_TEST(winfsp_test, cr8_nl_can_create_file_of_max_component_length) { if (this->current_provider == provider_type::s3) { diff --git a/repertory/repertory_test/src/winfsp_drive_create_test.cpp b/repertory/repertory_test/src/winfsp_drive_create_test.cpp index 609cb969..0d88caef 100644 --- a/repertory/repertory_test/src/winfsp_drive_create_test.cpp +++ b/repertory/repertory_test/src/winfsp_drive_create_test.cpp @@ -25,10 +25,10 @@ // Implemented test cases based on WinFsp tests: // https://github.com/winfsp/winfsp/blob/v2.0/tst/winfsp-tests // -#include "fixtures/winfsp_fixture.hpp" +#include "fixtures/drive_fixture.hpp" namespace repertory { -TYPED_TEST_SUITE(winfsp_test, winfsp_provider_types); +TYPED_TEST_SUITE(winfsp_test, platform_provider_types); TYPED_TEST(winfsp_test, cr8_file_can_create_file) { auto file_path{ diff --git a/repertory/repertory_test/src/winfsp_drive_delete_test.cpp b/repertory/repertory_test/src/winfsp_drive_delete_test.cpp index 49fa2495..bcf6d242 100644 --- a/repertory/repertory_test/src/winfsp_drive_delete_test.cpp +++ b/repertory/repertory_test/src/winfsp_drive_delete_test.cpp @@ -25,10 +25,10 @@ // Implemented test cases based on WinFsp tests: // https://github.com/winfsp/winfsp/blob/v2.0/tst/winfsp-tests // -#include "fixtures/winfsp_fixture.hpp" +#include "fixtures/drive_fixture.hpp" namespace repertory { -TYPED_TEST_SUITE(winfsp_test, winfsp_provider_types); +TYPED_TEST_SUITE(winfsp_test, platform_provider_types); TYPED_TEST(winfsp_test, delete_directory_fails_if_directory_not_empty) { auto dir_path{ diff --git a/repertory/repertory_test/src/winfsp_drive_info_test.cpp b/repertory/repertory_test/src/winfsp_drive_info_test.cpp index df610b7e..c7f9b658 100644 --- a/repertory/repertory_test/src/winfsp_drive_info_test.cpp +++ b/repertory/repertory_test/src/winfsp_drive_info_test.cpp @@ -25,10 +25,10 @@ // Implemented test cases based on WinFsp tests: // https://github.com/winfsp/winfsp/blob/v2.0/tst/winfsp-tests // -#include "fixtures/winfsp_fixture.hpp" +#include "fixtures/drive_fixture.hpp" namespace repertory { -TYPED_TEST_SUITE(winfsp_test, winfsp_provider_types); +TYPED_TEST_SUITE(winfsp_test, platform_provider_types); TYPED_TEST(winfsp_test, info_can_get_tag_info) { auto file_path{ diff --git a/repertory/repertory_test/src/winfsp_drive_rdrw_test.cpp b/repertory/repertory_test/src/winfsp_drive_rdrw_test.cpp index 983cd65e..b0f2524b 100644 --- a/repertory/repertory_test/src/winfsp_drive_rdrw_test.cpp +++ b/repertory/repertory_test/src/winfsp_drive_rdrw_test.cpp @@ -25,10 +25,10 @@ // Implemented test cases based on WinFsp tests: // https://github.com/winfsp/winfsp/blob/v2.0/tst/winfsp-tests // -#include "fixtures/winfsp_fixture.hpp" +#include "fixtures/drive_fixture.hpp" namespace repertory { -TYPED_TEST_SUITE(winfsp_test, winfsp_provider_types); +TYPED_TEST_SUITE(winfsp_test, platform_provider_types); static void test_file(auto &&mount_location, auto &&file_path, auto &&flags) { SYSTEM_INFO sys_info{}; diff --git a/repertory/repertory_test/src/winfsp_drive_rename_test.cpp b/repertory/repertory_test/src/winfsp_drive_rename_test.cpp index 1a6356ee..c983de87 100644 --- a/repertory/repertory_test/src/winfsp_drive_rename_test.cpp +++ b/repertory/repertory_test/src/winfsp_drive_rename_test.cpp @@ -25,10 +25,10 @@ // Implemented test cases based on WinFsp tests: // https://github.com/winfsp/winfsp/blob/v2.0/tst/winfsp-tests // -#include "fixtures/winfsp_fixture.hpp" +#include "fixtures/drive_fixture.hpp" namespace repertory { -TYPED_TEST_SUITE(winfsp_test, winfsp_provider_types); +TYPED_TEST_SUITE(winfsp_test, platform_provider_types); TYPED_TEST(winfsp_test, rename_can_rename_file_if_dest_does_not_exist) { if (this->current_provider == provider_type::s3) { diff --git a/repertory/repertory_test/src/winfsp_drive_test.cpp b/repertory/repertory_test/src/winfsp_drive_test.cpp index 4b72ffd9..1061b3a7 100644 --- a/repertory/repertory_test/src/winfsp_drive_test.cpp +++ b/repertory/repertory_test/src/winfsp_drive_test.cpp @@ -46,10 +46,10 @@ // Implemented test cases based on WinFsp tests: // https://github.com/winfsp/winfsp/blob/v2.0/tst/winfsp-tests // -#include "fixtures/winfsp_fixture.hpp" +#include "fixtures/drive_fixture.hpp" namespace repertory { -TYPED_TEST_SUITE(winfsp_test, winfsp_provider_types); +TYPED_TEST_SUITE(winfsp_test, platform_provider_types); TYPED_TEST(winfsp_test, can_set_current_directory_to_mount_location) { EXPECT_TRUE(::SetCurrentDirectoryA(this->mount_location.c_str())); diff --git a/repertory/repertory_test/src/winfsp_drive_volume_test.cpp b/repertory/repertory_test/src/winfsp_drive_volume_test.cpp index e695ea87..1a99e75e 100644 --- a/repertory/repertory_test/src/winfsp_drive_volume_test.cpp +++ b/repertory/repertory_test/src/winfsp_drive_volume_test.cpp @@ -25,10 +25,10 @@ // Implemented test cases based on WinFsp tests: // https://github.com/winfsp/winfsp/blob/v2.0/tst/winfsp-tests // -#include "fixtures/winfsp_fixture.hpp" +#include "fixtures/drive_fixture.hpp" namespace repertory { -TYPED_TEST_SUITE(winfsp_test, winfsp_provider_types); +TYPED_TEST_SUITE(winfsp_test, platform_provider_types); TYPED_TEST(winfsp_test, volume_can_get_volume_info) { std::string volume_label; From 5aa0558a807d46b4b69982e87e64d548fa69889d Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Fri, 19 Sep 2025 07:09:54 -0500 Subject: [PATCH 065/136] fix using --- .../repertory_test/include/fixtures/drive_fixture.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/repertory/repertory_test/include/fixtures/drive_fixture.hpp b/repertory/repertory_test/include/fixtures/drive_fixture.hpp index 59bb979a..c6700671 100644 --- a/repertory/repertory_test/include/fixtures/drive_fixture.hpp +++ b/repertory/repertory_test/include/fixtures/drive_fixture.hpp @@ -534,10 +534,10 @@ using platform_provider_types = remote_s3_encryption, remote_s3_legacy_encryption, local_sia, remote_sia>; #if defined(_WIN32) -using winfsp_test = drive_fixture; -#else -using fuse_test = drive_fixture; -#endif +template using winfsp_test = drive_fixture; +#else // !defined(_WIN32) +template using fuse_test = drive_fixture; +#endif // defined(_WIN32) } // namespace repertory #endif // REPERTORY_TEST_INCLUDE_FIXTURES_DRIVE_FIXTURE_HPP From cd82118a20ff1540fe943d89d64dd7b2abbf0791 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Fri, 19 Sep 2025 07:15:19 -0500 Subject: [PATCH 066/136] fix return types --- .../include/fixtures/drive_fixture.hpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/repertory/repertory_test/include/fixtures/drive_fixture.hpp b/repertory/repertory_test/include/fixtures/drive_fixture.hpp index c6700671..dd7ad9c0 100644 --- a/repertory/repertory_test/include/fixtures/drive_fixture.hpp +++ b/repertory/repertory_test/include/fixtures/drive_fixture.hpp @@ -209,15 +209,17 @@ protected: static void SetUpTestCase() { current_directory = std::filesystem::current_path(); - const auto make_test_dir = [](std::string_view suite, - std::string_view sub) { - return utils::path::combine(test::get_test_output_dir(), - {std::string(suite), std::string(sub)}); + const auto make_test_dir = [](const std::string &suite, + const std::string &sub) -> std::string { + return utils::path::combine(test::get_test_output_dir(), { + suite, + sub, + }); }; const auto make_cfg_dir = [](const std::string &root, - std::string_view name) { - auto cfg = utils::path::combine(root, {std::string(name)}); + const std::string &name) -> std::string { + auto cfg = utils::path::combine(root, {name}); ASSERT_TRUE(utils::file::directory(cfg).create_directory()); return cfg; }; From c4ce61ba200d1c19ec3fca1d8c0dcbd2e2e08cfd Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Fri, 19 Sep 2025 08:47:45 -0500 Subject: [PATCH 067/136] fix asserts --- .../repertory_test/include/fixtures/drive_fixture.hpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/repertory/repertory_test/include/fixtures/drive_fixture.hpp b/repertory/repertory_test/include/fixtures/drive_fixture.hpp index dd7ad9c0..d4ab7fab 100644 --- a/repertory/repertory_test/include/fixtures/drive_fixture.hpp +++ b/repertory/repertory_test/include/fixtures/drive_fixture.hpp @@ -144,11 +144,12 @@ struct platform_ops { return ".\\repertory.exe " + args_joined; #else // !defined(_WIN32) #if defined(__APPLE__) - constexpr const char *kBin = "./repertory.app/Contents/MacOS/repertory "; + constexpr std::string_view repertory_bin = + "./repertory.app/Contents/MacOS/repertory "; #else // !defined(__APPLE__) - constexpr const char *kBin = "./repertory "; + constexpr std::string_view repertory_bin = "./repertory "; #endif // defined(__APPLE__) - return std::string(kBin) + args_joined; + return std::string(repertory_bin) + args_joined; #endif // defined(_WIN32) } From 7116ea84fd4cdbdc8f4d48948d5f21714bb4082b Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Fri, 19 Sep 2025 09:00:26 -0500 Subject: [PATCH 068/136] test fixes --- .../include/fixtures/drive_fixture.hpp | 34 ++++++++----------- .../src/fuse_drive_access_test.cpp | 2 +- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/repertory/repertory_test/include/fixtures/drive_fixture.hpp b/repertory/repertory_test/include/fixtures/drive_fixture.hpp index d4ab7fab..f3e2da83 100644 --- a/repertory/repertory_test/include/fixtures/drive_fixture.hpp +++ b/repertory/repertory_test/include/fixtures/drive_fixture.hpp @@ -27,24 +27,19 @@ #include "app_config.hpp" #include "platform/platform.hpp" #include "types/repertory.hpp" -#include "utils/file_utils.hpp" +#include "utils/file.hpp" #include "utils/path.hpp" #include "utils/utils.hpp" +#include "providers/s3/s3_provider.hpp" +#include "providers/sia/sia_provider.hpp" #if defined(_WIN32) #include "drives/winfsp/remotewinfsp/remote_winfsp_drive.hpp" #include "drives/winfsp/winfsp_drive.hpp" -#include "providers/i_provider.hpp" -#include "providers/s3/s3_provider.hpp" -#include "providers/sia/sia_provider.hpp" #else // !defined(_WIN32) -#include "comm/curl/curl_comm.hpp" #include "db/i_meta_db.hpp" #include "db/meta_db.hpp" #include "drives/fuse/fuse_drive.hpp" -#include "providers/encrypt/encrypt_provider.hpp" -#include "providers/s3/s3_provider.hpp" -#include "providers/sia/sia_provider.hpp" #if !defined(ACCESSPERMS) // 0777 @@ -136,7 +131,8 @@ struct platform_ops { #endif // !defined(_WIN32) } - static std::string build_cmd(const std::string &args_joined, bool is_mount) { + static auto build_cmd(const std::string &args_joined, + [[maybe_unused]] bool is_mount) -> std::string { #if defined(_WIN32) if (is_mount) { return "start .\\repertory.exe -f " + args_joined; @@ -168,7 +164,7 @@ struct platform_ops { } static void execute_unmount(std::vector args_without_unmount, - const std::string &location) { + [[maybe_unused]] const std::string &location) { ensure_process_cwd(); args_without_unmount.emplace_back("-unmount"); @@ -221,7 +217,7 @@ protected: const auto make_cfg_dir = [](const std::string &root, const std::string &name) -> std::string { auto cfg = utils::path::combine(root, {name}); - ASSERT_TRUE(utils::file::directory(cfg).create_directory()); + EXPECT_TRUE(utils::file::directory(cfg).create_directory()); return cfg; }; @@ -237,10 +233,10 @@ protected: cfg_obj.set_event_level(event_level::trace); cfg_obj.set_s3_config(cfg); - auto r = cfg_obj.get_remote_mount(); - r.enable = true; - r.api_port = provider_t::remote_port; - cfg_obj.set_remote_mount(r); + auto remote_cfg = cfg_obj.get_remote_mount(); + remote_cfg.enable = true; + remote_cfg.api_port = provider_t::remote_port; + cfg_obj.set_remote_mount(remote_cfg); }; const auto configure_sia = [](app_config &cfg_obj) { @@ -253,10 +249,10 @@ protected: cfg_obj.set_host_config(src_cfg.get_host_config()); cfg_obj.set_sia_config(src_cfg.get_sia_config()); - auto r = cfg_obj.get_remote_mount(); - r.enable = true; - r.api_port = provider_t::remote_port; - cfg_obj.set_remote_mount(r); + auto remote_cfg = cfg_obj.get_remote_mount(); + remote_cfg.enable = true; + remote_cfg.api_port = provider_t::remote_port; + cfg_obj.set_remote_mount(remote_cfg); }; const auto mount_local_s3 = [&](bool as_remote) { diff --git a/repertory/repertory_test/src/fuse_drive_access_test.cpp b/repertory/repertory_test/src/fuse_drive_access_test.cpp index 65761203..e067e661 100644 --- a/repertory/repertory_test/src/fuse_drive_access_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_access_test.cpp @@ -24,7 +24,7 @@ #include "fixtures/drive_fixture.hpp" namespace { -const auto access_permutations = { +constexpr const auto access_permutations = { // clang-format off std::make_tuple(0000, R_OK, -1, EACCES), // No permissions, R_OK std::make_tuple(0000, W_OK, -1, EACCES), // No permissions, W_OK From d219b961a50c6a31dee9e6e39752340acbd18edb Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Fri, 19 Sep 2025 11:02:07 -0500 Subject: [PATCH 069/136] [unit test] Complete FUSE unit tests #22 --- .cspell/words.txt | 1 + CHANGELOG.md | 1 + .../src/fuse_drive_rename_test.cpp | 286 +++++++++++++++++- 3 files changed, 286 insertions(+), 2 deletions(-) diff --git a/.cspell/words.txt b/.cspell/words.txt index 2d991326..691b7d2b 100644 --- a/.cspell/words.txt +++ b/.cspell/words.txt @@ -18,6 +18,7 @@ cflags chrono clsid cmake_current_source_dir +cmdc coinit_apartmentthreaded comdlg32 conin$ diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fc36a8b..ff90c326 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Issues * \#12 [unit test] Complete all providers unit tests +* \#22 [unit test] Complete FUSE unit tests * \#33 Complete initial v2.0 documentation * \#34 Add macOS support * \#38 Pinning a file should automatically initiate a download to cache diff --git a/repertory/repertory_test/src/fuse_drive_rename_test.cpp b/repertory/repertory_test/src/fuse_drive_rename_test.cpp index 0b654c7c..fdc48c1b 100644 --- a/repertory/repertory_test/src/fuse_drive_rename_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_rename_test.cpp @@ -23,12 +23,294 @@ #include "fixtures/drive_fixture.hpp" +namespace { +void overwrite_text(const std::string &path, const char *s) { + int fd = ::open(path.c_str(), O_WRONLY | O_TRUNC); + ASSERT_NE(fd, -1); + write_all(fd, s, std::strlen(s)); + ::close(fd); +} + +void write_all(int fd, const char *s, size_t n) { + size_t off = 0; + while (off < n) { + ssize_t w = ::write(fd, s + off, n - off); + ASSERT_NE(w, -1); + off += static_cast(w); + } +} + +[[nodiscard]] auto slurp(const std::string &path) -> std::string { + int fd = ::open(path.c_str(), O_RDONLY); + if (fd == -1) + return {}; + std::string out; + char buf[4096]; + for (;;) { + ssize_t r = ::read(fd, buf, sizeof(buf)); + if (r == 0) + break; + if (r == -1) { + if (errno == EINTR) + continue; + break; + } + out.append(buf, buf + r); + } + + ::close(fd); + return out; +} +} // namespace + namespace repertory { TYPED_TEST_SUITE(fuse_test, platform_provider_types); -TYPED_TEST(fuse_test, can_rename_a_file) {} +TYPED_TEST(fuse_test, can_rename_a_file) { + std::string src_file_name{"rename_test"}; + auto src = this->create_file_and_test(src_file_name); -TYPED_TEST(fuse_test, can_rename_a_directory) {} + std::string dest_file_name{"rename_test_2"}; + auto dst = this->create_file_and_test(dest_file_name); + + errno = 0; + ASSERT_EQ(0, ::rename(src.c_str(), dst.c_str())); + + errno = 0; + EXPECT_EQ(-1, ::access(src.c_str(), F_OK)); + EXPECT_EQ(ENOENT, errno); + + struct stat st_unix{}; + ASSERT_EQ(0, ::stat(dst.c_str(), &st_unix)); + EXPECT_TRUE(S_ISREG(st_unix.st_mode)); + + this->unlink_file_and_test(dst); +} + +TYPED_TEST(fuse_test, can_rename_a_directory) { + std::string src_dir_name{"rename_test"}; + auto src_dir = this->create_directory_and_test(src_dir_name); + + std::string dest_dir_name{"rename_test_2"}; + auto dst_dir = this->create_directory_and_test(dest_dir_name); + + errno = 0; + ASSERT_EQ(0, ::rename(src_dir.c_str(), dst_dir.c_str())); + + errno = 0; + EXPECT_EQ(-1, ::access(src_dir.c_str(), F_OK)); + EXPECT_EQ(ENOENT, errno); + + struct stat st{}; + ASSERT_EQ(0, ::stat(dst_dir.c_str(), &st)); + EXPECT_TRUE(S_ISDIR(st.st_mode)); + + this->rmdir_and_test(dst_dir); +} + +/* TYPED_TEST(fuse_test, rename_file_overwrite_existing) { + std::string src_name{"rename_overwrite_src.txt"}; + std::string dst_name{"rename_overwrite_dst.txt"}; + auto src = this->create_file_and_test(src_name); + auto dst = this->create_file_and_test(dst_name); + + overwrite_text(src, "SRC"); + overwrite_text(dst, "DST"); + + errno = 0; + ASSERT_EQ(0, ::rename(src.c_str(), dst.c_str())); + + errno = 0; + EXPECT_EQ(-1, ::access(src.c_str(), F_OK)); + EXPECT_EQ(ENOENT, errno); + + EXPECT_EQ("SRC", slurp(dst)); + + this->unlink_file_and_test(dst); +} + +TYPED_TEST(fuse_test, rename_file_cross_directory) { + auto dir1 = this->create_directory_and_test("dir_1"); + auto dir2 = this->create_directory_and_test("dir_2"); + + auto src = this->create_file_and_test("dir_1/file.txt"); + std::string dst = utils::path::combine(dir2, {"moved.txt"}); + + overwrite_text(src, "CMDC"); + + errno = 0; + ASSERT_EQ(0, ::rename(src.c_str(), dst.c_str())); + + errno = 0; + EXPECT_EQ(-1, ::access(src.c_str(), F_OK)); + EXPECT_EQ(ENOENT, errno); + EXPECT_EQ("CMDC", slurp(dst)); + + this->unlink_file_and_test(dst); + this->rmdir_and_test(dir1); + this->rmdir_and_test(dir2); +} + +TYPED_TEST(fuse_test, rename_file_same_path) { + auto src = this->create_file_and_test("rn_same.txt"); + overwrite_text(src, "CMDC"); + + errno = 0; + EXPECT_EQ(0, ::rename(src.c_str(), src.c_str())); + EXPECT_EQ("CMDC", slurp(src)); + + this->unlink_file_and_test(src); +} + +TYPED_TEST(fuse_test, rename_file_source_missing_sets_errno) { + auto dst = this->create_file_and_test("rn_any_dest.txt"); + auto missing = this->mount_location() + "/rn_missing_src.txt"; + + errno = 0; + EXPECT_EQ(-1, ::rename(missing.c_str(), dst.c_str())); + EXPECT_EQ(ENOENT, errno); + + this->unlink_file_and_test(dst); +} + +TYPED_TEST(fuse_test, rename_file_destination_parent_missing_sets_errno) { + auto src = this->create_file_and_test("rn_good_src.txt"); + auto dst = this->mount_location() + "/rn_missing_parent/rn_dest.txt"; + + errno = 0; + EXPECT_EQ(-1, ::rename(src.c_str(), dst.c_str())); + EXPECT_EQ(ENOENT, errno); + + this->unlink_file_and_test(src); +} + +TYPED_TEST(fuse_test, rename_file_into_existing_directory_sets_eisdir) { + auto src = this->create_file_and_test("rn_file.txt"); + auto dstdir = this->create_directory_and_test("rn_dest_dir"); + + errno = 0; + EXPECT_EQ(-1, ::rename(src.c_str(), dstdir.c_str())); + EXPECT_EQ(EISDIR, errno); + + this->unlink_file_and_test(src); + this->rmdir_and_test(dstdir); +} + +TYPED_TEST(fuse_test, rename_file_no_write_on_source_parent_sets_eacces) { + auto srcdir = this->create_directory_and_test("rn_srcp"); + auto dstdir = this->create_directory_and_test("rn_dstp"); + auto src = this->create_file_and_test("rn_srcp/a.txt"); + auto dst = dstdir + "/b.txt"; + + ASSERT_EQ(0, ::chmod(srcdir.c_str(), 0555)); + + errno = 0; + EXPECT_EQ(-1, ::rename(src.c_str(), dst.c_str())); + EXPECT_EQ(EACCES, errno); + + ASSERT_EQ(0, ::chmod(srcdir.c_str(), 0755)); + + this->unlink_file_and_test(src); + this->rmdir_and_test(srcdir); + this->rmdir_and_test(dstdir); +} + +TYPED_TEST(fuse_test, rename_file_no_write_on_destination_parent_sets_eacces) { + auto srcdir = this->create_directory_and_test("rn_srcp2"); + auto dstdir = this->create_directory_and_test("rn_dstp2"); + auto src = this->create_file_and_test("rn_srcp2/a.txt"); + auto dst = dstdir + "/b.txt"; + + ASSERT_EQ(0, ::chmod(dstdir.c_str(), 0555)); + + errno = 0; + EXPECT_EQ(-1, ::rename(src.c_str(), dst.c_str())); + EXPECT_EQ(EACCES, errno); + + ASSERT_EQ(0, ::chmod(dstdir.c_str(), 0755)); + + this->unlink_file_and_test(src); + this->rmdir_and_test(srcdir); + this->rmdir_and_test(dstdir); +} + +TYPED_TEST(fuse_test, rename_file_overwrite_readonly_destination_succeeds) { + auto src = this->create_file_and_test("rn_src_ro.txt"); + auto dst = this->create_file_and_test("rn_dst_ro.txt"); + + overwrite_text(src, "NEW"); + overwrite_text(dst, "OLD"); + + ASSERT_EQ(0, ::chmod(dst.c_str(), 0444)); + + errno = 0; + int rn = ::rename(src.c_str(), dst.c_str()); + if (rn == -1 && errno == EROFS) { + this->unlink_file_and_test(src); + ASSERT_EQ(0, ::chmod(dst.c_str(), 0644)); + this->unlink_file_and_test(dst); + GTEST_SKIP(); + } + ASSERT_EQ(0, rn); + + EXPECT_EQ("NEW", slurp(dst)); + + ASSERT_EQ(0, ::chmod(dst.c_str(), 0644)); + this->unlink_file_and_test(dst); +} + +TYPED_TEST(fuse_test, rename_file_on_readonly_mount_sets_erofs_or_skip) { + auto src = this->create_file_and_test("rn_ro_src.txt"); + auto dst = this->mount_location() + "/rn_ro_dst.txt"; + + if (::rename(src.c_str(), src.c_str()) == 0) { + this->unlink_file_and_test(src); + GTEST_SKIP(); + } + + errno = 0; + EXPECT_EQ(-1, ::rename(src.c_str(), dst.c_str())); + EXPECT_EQ(EROFS, errno); + + this->unlink_file_and_test(src); +} + +TYPED_TEST(fuse_test, rename_file_open_fd_remains_valid) { + auto src = this->create_file_and_test("rn_fd_src.txt"); + auto dst = this->mount_location() + "/rn_fd_dst.txt"; + + overwrite_text(src, "HELLO"); + + int fd = ::open(src.c_str(), O_RDWR); + ASSERT_NE(fd, -1); + + errno = 0; + ASSERT_EQ(0, ::rename(src.c_str(), dst.c_str())); + + errno = 0; + EXPECT_EQ(-1, ::access(src.c_str(), F_OK)); + EXPECT_EQ(ENOENT, errno); + + ASSERT_NE(-1, ::lseek(fd, 0, SEEK_END)); + write_all(fd, " WORLD", 6); + ::close(fd); + + EXPECT_EQ("HELLO WORLD", slurp(dst)); + this->unlink_file_and_test(dst); +} + +TYPED_TEST(fuse_test, rename_file_destination_component_name_too_long) { + auto src = this->create_file_and_test("rn_ntl_src.txt"); + + std::string longname(300, 'a'); + std::string dst = this->mount_location() + "/" + longname; + + errno = 0; + EXPECT_EQ(-1, ::rename(src.c_str(), dst.c_str())); + EXPECT_TRUE(errno == ENAMETOOLONG || errno == EINVAL); + + this->unlink_file_and_test(src); +} */ } // namespace repertory #endif // !defined(_WIN32) From 7c6a8cad7b460f603f734406b3293f1b4c32d2f2 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Fri, 19 Sep 2025 11:09:23 -0500 Subject: [PATCH 070/136] cleanup legacy tests --- .../src/fuse_drive_test_legacy.cpp | 107 ------------------ 1 file changed, 107 deletions(-) diff --git a/repertory/repertory_test/src/fuse_drive_test_legacy.cpp b/repertory/repertory_test/src/fuse_drive_test_legacy.cpp index 1a7b1a33..f4041442 100644 --- a/repertory/repertory_test/src/fuse_drive_test_legacy.cpp +++ b/repertory/repertory_test/src/fuse_drive_test_legacy.cpp @@ -262,110 +262,3 @@ // EXPECT_EQ(ENODATA, errno); // } // #endif -// -// // file_path = create_file_and_test(mount_location, -// // "write_read_test"); -// // test_write_and_read(utils::path::create_api_path("write_read_test"), -// // file_path); -// // unlink_file_and_test(file_path); -// // -// // file_path = -// // create_file_and_test(mount_location, "from_rename_file_test"); -// // auto to_file_path = -// // utils::path::combine(mount_location, {"to_rename_file_test"}); -// // test_rename_file(file_path, to_file_path, -// // provider_ptr->is_rename_supported()); -// // EXPECT_TRUE(utils::file::file(file_path).remove()); -// // EXPECT_TRUE(utils::file::file(to_file_path).remove()); -// // -// // file_path = -// // utils::path::combine(mount_location, -// {"from_rename_dir_test"}); -// // to_file_path = -// // utils::path::combine(mount_location, {"to_rename_dir_test"}); -// // test_rename_directory(file_path, to_file_path, -// // provider_ptr->is_rename_supported()); -// // EXPECT_TRUE(utils::file::retry_delete_directory(file_path.c_str())); -// // EXPECT_TRUE(utils::file::retry_delete_directory(to_file_path.c_str())); -// // -// // file_path = create_file_and_test(mount_location, -// // "truncate_file_test"); test_truncate(file_path); -// // unlink_file_and_test(file_path); -// // -// // file_path = create_file_and_test(mount_location, -// // "ftruncate_file_test"); test_ftruncate(file_path); -// // unlink_file_and_test(file_path); -// // -// // #if !defined(__APPLE__) -// // file_path = create_file_and_test(mount_location, -// // "fallocate_file_test"); -// // test_fallocate(utils::path::create_api_path("fallocate_file_test"), -// // file_path); -// // unlink_file_and_test(file_path); -// // #endif -// // -// // file_path = create_file_and_test(mount_location, -// // "write_fails_ro_test"); test_write_operations_fail_if_read_only( -// // utils::path::create_api_path("write_fails_ro_test"), -// // file_path); -// // unlink_file_and_test(file_path); -// // -// // file_path = create_file_and_test(mount_location, "getattr.txt"); -// // test_file_getattr(utils::path::create_api_path("getattr.txt"), -// // file_path); -// // unlink_file_and_test(file_path); -// // -// // file_path = utils::path::combine(mount_location, {"getattr_dir"}); -// // test_directory_getattr(utils::path::create_api_path("getattr_dir"), -// // file_path); -// // rmdir_and_test(file_path); -// // -// // #if !__APPLE__ && HAS_SETXATTR -// // file_path = -// // create_file_and_test(mount_location, -// // "xattr_invalid_names_test"); -// // test_xattr_invalid_parameters(file_path); -// // unlink_file_and_test(file_path); -// // -// // file_path = -// // create_file_and_test(mount_location, "xattr_create_get_test"); -// // test_xattr_create_and_get(file_path); -// // unlink_file_and_test(file_path); -// // -// // file_path = -// // create_file_and_test(mount_location, "xattr_listxattr_test"); -// // test_xattr_listxattr(file_path); -// // unlink_file_and_test(file_path); -// // -// // file_path = create_file_and_test(mount_location, -// // "xattr_replace_test"); test_xattr_replace(file_path); -// // unlink_file_and_test(file_path); -// // -// // file_path = -// // create_file_and_test(mount_location, -// // "xattr_default_create_test"); -// // test_xattr_default_create(file_path); -// // unlink_file_and_test(file_path); -// // -// // file_path = -// // create_file_and_test(mount_location, -// // "xattr_default_replace_test"); -// // test_xattr_default_replace(file_path); -// // unlink_file_and_test(file_path); -// // -// // file_path = create_file_and_test(mount_location, -// // "xattr_create_fails_exists_test"); -// // test_xattr_create_fails_if_exists(file_path); -// // unlink_file_and_test(file_path); -// // -// // file_path = create_file_and_test(mount_location, -// // "xattr_create_fails_not_exists_test"); -// // test_xattr_create_fails_if_not_exists(file_path); -// // unlink_file_and_test(file_path); -// // -// // file_path = -// // create_file_and_test(mount_location, -// "xattr_removexattr_test"); -// // test_xattr_removexattr(file_path); -// // unlink_file_and_test(file_path); -// // #endif From 740cb467df752048e9fb93799b017a8d2f3c37c6 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Fri, 19 Sep 2025 11:55:29 -0500 Subject: [PATCH 071/136] cleanup --- CMakeLists.txt | 4 ++-- repertory.iss.in => repertory/repertory.iss.in | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename repertory.iss.in => repertory/repertory.iss.in (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a002a3f..f10a09cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -120,9 +120,9 @@ if(PROJECT_BUILD) @ONLY ) - if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}.iss.in") + if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}/${PROJECT_NAME}.iss.in") configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}.iss.in + ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}/${PROJECT_NAME}.iss.in ${PROJECT_DIST_DIR}/../${PROJECT_NAME}.iss @ONLY ) diff --git a/repertory.iss.in b/repertory/repertory.iss.in similarity index 100% rename from repertory.iss.in rename to repertory/repertory.iss.in From 415b28c7fe6cda4c574386ab9884b795f56394f8 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Fri, 19 Sep 2025 18:03:06 -0500 Subject: [PATCH 072/136] [unit test] Complete FUSE unit tests #22 --- .../include/fixtures/drive_fixture.hpp | 2 - .../src/fuse_drive_rename_test.cpp | 101 +++++++++++------- 2 files changed, 63 insertions(+), 40 deletions(-) diff --git a/repertory/repertory_test/include/fixtures/drive_fixture.hpp b/repertory/repertory_test/include/fixtures/drive_fixture.hpp index f3e2da83..4b48063b 100644 --- a/repertory/repertory_test/include/fixtures/drive_fixture.hpp +++ b/repertory/repertory_test/include/fixtures/drive_fixture.hpp @@ -31,8 +31,6 @@ #include "utils/path.hpp" #include "utils/utils.hpp" -#include "providers/s3/s3_provider.hpp" -#include "providers/sia/sia_provider.hpp" #if defined(_WIN32) #include "drives/winfsp/remotewinfsp/remote_winfsp_drive.hpp" #include "drives/winfsp/winfsp_drive.hpp" diff --git a/repertory/repertory_test/src/fuse_drive_rename_test.cpp b/repertory/repertory_test/src/fuse_drive_rename_test.cpp index fdc48c1b..6eaca63e 100644 --- a/repertory/repertory_test/src/fuse_drive_rename_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_rename_test.cpp @@ -24,41 +24,49 @@ #include "fixtures/drive_fixture.hpp" namespace { -void overwrite_text(const std::string &path, const char *s) { - int fd = ::open(path.c_str(), O_WRONLY | O_TRUNC); - ASSERT_NE(fd, -1); - write_all(fd, s, std::strlen(s)); - ::close(fd); +void overwrite_text(const std::string &path, const std::string &data); +void write_all(int desc, const std::string &data); +[[nodiscard]] auto slurp(const std::string &path) -> std::string; + +void overwrite_text(const std::string &path, const std::string &data) { + int desc = ::open(path.c_str(), O_WRONLY | O_TRUNC); + ASSERT_NE(desc, -1); + write_all(desc, data); + ::close(desc); } -void write_all(int fd, const char *s, size_t n) { - size_t off = 0; - while (off < n) { - ssize_t w = ::write(fd, s + off, n - off); - ASSERT_NE(w, -1); - off += static_cast(w); +void write_all(int desc, const std::string &data) { + std::size_t off{0U}; + while (off < data.size()) { + auto written = ::write(desc, &data.at(off), data.length() - off); + ASSERT_NE(written, -1); + off += static_cast(written); } } -[[nodiscard]] auto slurp(const std::string &path) -> std::string { - int fd = ::open(path.c_str(), O_RDONLY); - if (fd == -1) +auto slurp(const std::string &path) -> std::string { + int desc = ::open(path.c_str(), O_RDONLY); + if (desc == -1) { return {}; + } std::string out; - char buf[4096]; + std::array buf{}; for (;;) { - ssize_t r = ::read(fd, buf, sizeof(buf)); - if (r == 0) - break; - if (r == -1) { - if (errno == EINTR) - continue; + auto bytes_read = ::read(desc, buf.data(), buf.size()); + if (bytes_read == 0) { break; } - out.append(buf, buf + r); + if (bytes_read == -1) { + if (errno == EINTR) { + continue; + } + break; + } + + out.append(buf.begin(), std::next(buf.begin(), bytes_read)); } - ::close(fd); + ::close(desc); return out; } } // namespace @@ -66,7 +74,12 @@ void write_all(int fd, const char *s, size_t n) { namespace repertory { TYPED_TEST_SUITE(fuse_test, platform_provider_types); -TYPED_TEST(fuse_test, can_rename_a_file) { +TYPED_TEST(fuse_test, rename_can_rename_a_file) { + if (this->current_provider != provider_type::sia) { + // TODO finish test + return; + } + std::string src_file_name{"rename_test"}; auto src = this->create_file_and_test(src_file_name); @@ -87,12 +100,17 @@ TYPED_TEST(fuse_test, can_rename_a_file) { this->unlink_file_and_test(dst); } -TYPED_TEST(fuse_test, can_rename_a_directory) { +TYPED_TEST(fuse_test, rename_can_rename_a_directory) { + if (this->current_provider != provider_type::sia) { + // TODO finish test + return; + } + std::string src_dir_name{"rename_test"}; auto src_dir = this->create_directory_and_test(src_dir_name); - std::string dest_dir_name{"rename_test_2"}; - auto dst_dir = this->create_directory_and_test(dest_dir_name); + auto dst_dir = utils::path::combine(utils::path::get_parent_path(src_dir), + {"rename_test_2"}); errno = 0; ASSERT_EQ(0, ::rename(src_dir.c_str(), dst_dir.c_str())); @@ -101,17 +119,23 @@ TYPED_TEST(fuse_test, can_rename_a_directory) { EXPECT_EQ(-1, ::access(src_dir.c_str(), F_OK)); EXPECT_EQ(ENOENT, errno); - struct stat st{}; - ASSERT_EQ(0, ::stat(dst_dir.c_str(), &st)); - EXPECT_TRUE(S_ISDIR(st.st_mode)); + struct stat st_unix{}; + ASSERT_EQ(0, ::stat(dst_dir.c_str(), &st_unix)); + EXPECT_TRUE(S_ISDIR(st_unix.st_mode)); this->rmdir_and_test(dst_dir); } -/* TYPED_TEST(fuse_test, rename_file_overwrite_existing) { - std::string src_name{"rename_overwrite_src.txt"}; - std::string dst_name{"rename_overwrite_dst.txt"}; +TYPED_TEST(fuse_test, rename_can_overwrite_existing_file) { + if (this->current_provider != provider_type::sia) { + // TODO finish test + return; + } + + std::string src_name{"rename.txt"}; auto src = this->create_file_and_test(src_name); + + std::string dst_name{"rename2.txt"}; auto dst = this->create_file_and_test(dst_name); overwrite_text(src, "SRC"); @@ -129,6 +153,7 @@ TYPED_TEST(fuse_test, can_rename_a_directory) { this->unlink_file_and_test(dst); } +/* TYPED_TEST(fuse_test, rename_file_cross_directory) { auto dir1 = this->create_directory_and_test("dir_1"); auto dir2 = this->create_directory_and_test("dir_2"); @@ -281,8 +306,8 @@ TYPED_TEST(fuse_test, rename_file_open_fd_remains_valid) { overwrite_text(src, "HELLO"); - int fd = ::open(src.c_str(), O_RDWR); - ASSERT_NE(fd, -1); + int desc = ::open(src.c_str(), O_RDWR); + ASSERT_NE(desc, -1); errno = 0; ASSERT_EQ(0, ::rename(src.c_str(), dst.c_str())); @@ -291,9 +316,9 @@ TYPED_TEST(fuse_test, rename_file_open_fd_remains_valid) { EXPECT_EQ(-1, ::access(src.c_str(), F_OK)); EXPECT_EQ(ENOENT, errno); - ASSERT_NE(-1, ::lseek(fd, 0, SEEK_END)); - write_all(fd, " WORLD", 6); - ::close(fd); + ASSERT_NE(-1, ::lseek(desc, 0, SEEK_END)); + write_all(desc, " WORLD", 6); + ::close(desc); EXPECT_EQ("HELLO WORLD", slurp(dst)); this->unlink_file_and_test(dst); From 6b1891741b25581462c1c81e2c1782717da62a68 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 20 Sep 2025 08:12:42 -0500 Subject: [PATCH 073/136] [unit test] Complete FUSE unit tests #22 --- .../include/rpc/client/client.hpp | 28 ++- .../librepertory/src/rpc/client/client.cpp | 26 ++- .../src/fuse_drive_rename_test.cpp | 216 ++++++++++++------ 3 files changed, 181 insertions(+), 89 deletions(-) diff --git a/repertory/librepertory/include/rpc/client/client.hpp b/repertory/librepertory/include/rpc/client/client.hpp index a9cea483..bdb1e8f4 100644 --- a/repertory/librepertory/include/rpc/client/client.hpp +++ b/repertory/librepertory/include/rpc/client/client.hpp @@ -34,33 +34,37 @@ private: std::atomic request_id_{0U}; public: - [[nodiscard]] auto get_drive_information() -> rpc_response; + [[nodiscard]] auto get_drive_information() const -> rpc_response; - [[nodiscard]] auto get_config() -> rpc_response; + [[nodiscard]] auto get_config() const -> rpc_response; - [[nodiscard]] auto get_config_value_by_name(const std::string &name) + [[nodiscard]] auto get_config_value_by_name(const std::string &name) const -> rpc_response; - [[nodiscard]] auto get_directory_items(const std::string &api_path) + [[nodiscard]] auto get_directory_items(const std::string &api_path) const -> rpc_response; - [[nodiscard]] auto get_item_info(const std::string &api_path) -> rpc_response; + [[nodiscard]] auto get_item_info(const std::string &api_path) const + -> rpc_response; - [[nodiscard]] auto get_open_files() -> rpc_response; + [[nodiscard]] auto get_open_files() const -> rpc_response; - [[nodiscard]] auto get_pinned_files() -> rpc_response; + [[nodiscard]] auto get_pinned_files() const -> rpc_response; - [[nodiscard]] auto pin_file(const std::string &api_path) -> rpc_response; + [[nodiscard]] auto pin_file(const std::string &api_path) const + -> rpc_response; - [[nodiscard]] auto pinned_status(const std::string &api_path) -> rpc_response; + [[nodiscard]] auto pinned_status(const std::string &api_path) const + -> rpc_response; [[nodiscard]] auto set_config_value_by_name(const std::string &name, - const std::string &value) + const std::string &value) const -> rpc_response; - [[nodiscard]] auto unmount() -> rpc_response; + [[nodiscard]] auto unmount() const -> rpc_response; - [[nodiscard]] auto unpin_file(const std::string &api_path) -> rpc_response; + [[nodiscard]] auto unpin_file(const std::string &api_path) const + -> rpc_response; }; } // namespace repertory diff --git a/repertory/librepertory/src/rpc/client/client.cpp b/repertory/librepertory/src/rpc/client/client.cpp index e010346d..7e0d8120 100644 --- a/repertory/librepertory/src/rpc/client/client.cpp +++ b/repertory/librepertory/src/rpc/client/client.cpp @@ -27,7 +27,7 @@ namespace repertory { client::client(rpc_host_info host_info) : host_info_(std::move(host_info)) {} -auto client::get_drive_information() -> rpc_response { +auto client::get_drive_information() const -> rpc_response { auto base_url = "http://" + host_info_.host + ":" + std::to_string(host_info_.port); @@ -55,7 +55,7 @@ auto client::get_drive_information() -> rpc_response { }; } -auto client::get_config() -> rpc_response { +auto client::get_config() const -> rpc_response { auto base_url = "http://" + host_info_.host + ":" + std::to_string(host_info_.port); @@ -83,7 +83,8 @@ auto client::get_config() -> rpc_response { }; } -auto client::get_config_value_by_name(const std::string &name) -> rpc_response { +auto client::get_config_value_by_name(const std::string &name) const + -> rpc_response { auto base_url = "http://" + host_info_.host + ":" + std::to_string(host_info_.port); @@ -113,7 +114,8 @@ auto client::get_config_value_by_name(const std::string &name) -> rpc_response { }; } -auto client::get_directory_items(const std::string &api_path) -> rpc_response { +auto client::get_directory_items(const std::string &api_path) const + -> rpc_response { auto base_url = "http://" + host_info_.host + ":" + std::to_string(host_info_.port); @@ -142,7 +144,7 @@ auto client::get_directory_items(const std::string &api_path) -> rpc_response { }; } -auto client::get_item_info(const std::string &api_path) -> rpc_response { +auto client::get_item_info(const std::string &api_path) const -> rpc_response { auto base_url = "http://" + host_info_.host + ":" + std::to_string(host_info_.port); @@ -171,7 +173,7 @@ auto client::get_item_info(const std::string &api_path) -> rpc_response { }; } -auto client::get_open_files() -> rpc_response { +auto client::get_open_files() const -> rpc_response { auto base_url = "http://" + host_info_.host + ":" + std::to_string(host_info_.port); @@ -199,7 +201,7 @@ auto client::get_open_files() -> rpc_response { }; } -auto client::get_pinned_files() -> rpc_response { +auto client::get_pinned_files() const -> rpc_response { auto base_url = "http://" + host_info_.host + ":" + std::to_string(host_info_.port); @@ -227,7 +229,7 @@ auto client::get_pinned_files() -> rpc_response { }; } -auto client::pin_file(const std::string &api_path) -> rpc_response { +auto client::pin_file(const std::string &api_path) const -> rpc_response { auto base_url = "http://" + host_info_.host + ":" + std::to_string(host_info_.port); @@ -256,7 +258,7 @@ auto client::pin_file(const std::string &api_path) -> rpc_response { }; } -auto client::pinned_status(const std::string &api_path) -> rpc_response { +auto client::pinned_status(const std::string &api_path) const -> rpc_response { auto base_url = "http://" + host_info_.host + ":" + std::to_string(host_info_.port); @@ -286,7 +288,7 @@ auto client::pinned_status(const std::string &api_path) -> rpc_response { } auto client::set_config_value_by_name(const std::string &name, - const std::string &value) + const std::string &value) const -> rpc_response { auto base_url = "http://" + host_info_.host + ":" + std::to_string(host_info_.port); @@ -321,7 +323,7 @@ auto client::set_config_value_by_name(const std::string &name, }; } -auto client::unmount() -> rpc_response { +auto client::unmount() const -> rpc_response { auto base_url = "http://" + host_info_.host + ":" + std::to_string(host_info_.port); @@ -349,7 +351,7 @@ auto client::unmount() -> rpc_response { }; } -auto client::unpin_file(const std::string &api_path) -> rpc_response { +auto client::unpin_file(const std::string &api_path) const -> rpc_response { auto base_url = "http://" + host_info_.host + ":" + std::to_string(host_info_.port); diff --git a/repertory/repertory_test/src/fuse_drive_rename_test.cpp b/repertory/repertory_test/src/fuse_drive_rename_test.cpp index 6eaca63e..14d4d80d 100644 --- a/repertory/repertory_test/src/fuse_drive_rename_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_rename_test.cpp @@ -153,12 +153,20 @@ TYPED_TEST(fuse_test, rename_can_overwrite_existing_file) { this->unlink_file_and_test(dst); } -/* -TYPED_TEST(fuse_test, rename_file_cross_directory) { - auto dir1 = this->create_directory_and_test("dir_1"); - auto dir2 = this->create_directory_and_test("dir_2"); +TYPED_TEST(fuse_test, rename_can_rename_file_into_different_directory) { + if (this->current_provider != provider_type::sia) { + // TODO finish test + return; + } - auto src = this->create_file_and_test("dir_1/file.txt"); + std::string dir_name_1{"dir_1"}; + auto dir1 = this->create_directory_and_test(dir_name_1); + + std::string dir_name_2{"dir_2"}; + auto dir2 = this->create_directory_and_test(dir_name_2); + + std::string file_name{dir_name_1 + "/rename"}; + auto src = this->create_file_and_test(file_name); std::string dst = utils::path::combine(dir2, {"moved.txt"}); overwrite_text(src, "CMDC"); @@ -176,8 +184,14 @@ TYPED_TEST(fuse_test, rename_file_cross_directory) { this->rmdir_and_test(dir2); } -TYPED_TEST(fuse_test, rename_file_same_path) { - auto src = this->create_file_and_test("rn_same.txt"); +TYPED_TEST(fuse_test, rename_can_rename_file_to_same_path) { + if (this->current_provider != provider_type::sia) { + // TODO finish test + return; + } + + std::string file_name{"rename"}; + auto src = this->create_file_and_test(file_name); overwrite_text(src, "CMDC"); errno = 0; @@ -187,81 +201,143 @@ TYPED_TEST(fuse_test, rename_file_same_path) { this->unlink_file_and_test(src); } -TYPED_TEST(fuse_test, rename_file_source_missing_sets_errno) { - auto dst = this->create_file_and_test("rn_any_dest.txt"); - auto missing = this->mount_location() + "/rn_missing_src.txt"; +TYPED_TEST(fuse_test, rename_file_fails_if_source_file_does_not_exist) { + if (this->current_provider != provider_type::sia) { + // TODO finish test + return; + } - errno = 0; - EXPECT_EQ(-1, ::rename(missing.c_str(), dst.c_str())); - EXPECT_EQ(ENOENT, errno); + std::string src_file_name{"rename"}; + auto src = this->create_file_path(src_file_name); - this->unlink_file_and_test(dst); -} - -TYPED_TEST(fuse_test, rename_file_destination_parent_missing_sets_errno) { - auto src = this->create_file_and_test("rn_good_src.txt"); - auto dst = this->mount_location() + "/rn_missing_parent/rn_dest.txt"; + std::string dst_file_name{"rename_2"}; + auto dst = this->create_file_path(dst_file_name); errno = 0; EXPECT_EQ(-1, ::rename(src.c_str(), dst.c_str())); EXPECT_EQ(ENOENT, errno); + EXPECT_FALSE(utils::file::file{src}.exists()); + EXPECT_FALSE(utils::file::file{dst}.exists()); +} + +TYPED_TEST(fuse_test, + rename_file_fails_if_destination_directory_does_not_exist) { + if (this->current_provider != provider_type::sia) { + // TODO finish test + return; + } + + std::string file_name{"rename"}; + auto src = this->create_file_and_test(file_name); + + std::string dst_file_name{"cow_moose_doge_chicken/rename_2"}; + auto dst = this->create_file_path(dst_file_name); + + errno = 0; + EXPECT_EQ(-1, ::rename(src.c_str(), dst.c_str())); + EXPECT_EQ(ENOENT, errno); + + EXPECT_FALSE(utils::file::file{dst}.exists()); + this->unlink_file_and_test(src); } -TYPED_TEST(fuse_test, rename_file_into_existing_directory_sets_eisdir) { - auto src = this->create_file_and_test("rn_file.txt"); - auto dstdir = this->create_directory_and_test("rn_dest_dir"); +TYPED_TEST(fuse_test, rename_file_fails_if_destination_is_directory) { + if (this->current_provider != provider_type::sia) { + // TODO finish test + return; + } + + std::string file_name{"rename"}; + auto src = this->create_file_and_test(file_name); + + std::string dir_name{"dir"}; + auto dest_dir = this->create_directory_and_test(dir_name); errno = 0; - EXPECT_EQ(-1, ::rename(src.c_str(), dstdir.c_str())); + EXPECT_EQ(-1, ::rename(src.c_str(), dest_dir.c_str())); EXPECT_EQ(EISDIR, errno); + EXPECT_TRUE(utils::file::directory{dest_dir}.exists()); + this->unlink_file_and_test(src); - this->rmdir_and_test(dstdir); + this->rmdir_and_test(dest_dir); } -TYPED_TEST(fuse_test, rename_file_no_write_on_source_parent_sets_eacces) { - auto srcdir = this->create_directory_and_test("rn_srcp"); - auto dstdir = this->create_directory_and_test("rn_dstp"); - auto src = this->create_file_and_test("rn_srcp/a.txt"); - auto dst = dstdir + "/b.txt"; +TYPED_TEST(fuse_test, rename_file_fails_if_source_directory_is_read_only) { + if (this->current_provider != provider_type::sia) { + // TODO finish test + return; + } - ASSERT_EQ(0, ::chmod(srcdir.c_str(), 0555)); + std::string src_dir_name("dir_1"); + auto src_dir = this->create_directory_and_test(src_dir_name); + + std::string dest_dir_name("dir_2"); + auto dest_dir = this->create_directory_and_test(dest_dir_name); + + std::string file_name{src_dir_name + "/rename"}; + auto src = this->create_file_and_test(file_name); + auto dst = dest_dir + "/dest"; + + ASSERT_EQ(0, ::chmod(src_dir.c_str(), 0555)); errno = 0; EXPECT_EQ(-1, ::rename(src.c_str(), dst.c_str())); EXPECT_EQ(EACCES, errno); - ASSERT_EQ(0, ::chmod(srcdir.c_str(), 0755)); + EXPECT_FALSE(utils::file::file{dst}.exists()); + + ASSERT_EQ(0, ::chmod(src_dir.c_str(), 0755)); this->unlink_file_and_test(src); - this->rmdir_and_test(srcdir); - this->rmdir_and_test(dstdir); + this->rmdir_and_test(src_dir); + this->rmdir_and_test(dest_dir); } -TYPED_TEST(fuse_test, rename_file_no_write_on_destination_parent_sets_eacces) { - auto srcdir = this->create_directory_and_test("rn_srcp2"); - auto dstdir = this->create_directory_and_test("rn_dstp2"); - auto src = this->create_file_and_test("rn_srcp2/a.txt"); - auto dst = dstdir + "/b.txt"; +TYPED_TEST(fuse_test, rename_file_fails_if_destination_directory_is_read_only) { + if (this->current_provider != provider_type::sia) { + // TODO finish test + return; + } - ASSERT_EQ(0, ::chmod(dstdir.c_str(), 0555)); + std::string src_dir_name("dir_1"); + auto src_dir = this->create_directory_and_test(src_dir_name); + + std::string dest_dir_name("dir_2"); + auto dest_dir = this->create_directory_and_test(dest_dir_name); + + std::string file_name{src_dir_name + "/rename"}; + auto src = this->create_file_and_test(file_name); + auto dst = dest_dir + "/dest"; + + ASSERT_EQ(0, ::chmod(dest_dir.c_str(), 0555)); errno = 0; EXPECT_EQ(-1, ::rename(src.c_str(), dst.c_str())); EXPECT_EQ(EACCES, errno); - ASSERT_EQ(0, ::chmod(dstdir.c_str(), 0755)); + EXPECT_FALSE(utils::file::file{dst}.exists()); + + ASSERT_EQ(0, ::chmod(dest_dir.c_str(), 0755)); this->unlink_file_and_test(src); - this->rmdir_and_test(srcdir); - this->rmdir_and_test(dstdir); + this->rmdir_and_test(src_dir); + this->rmdir_and_test(dest_dir); } -TYPED_TEST(fuse_test, rename_file_overwrite_readonly_destination_succeeds) { - auto src = this->create_file_and_test("rn_src_ro.txt"); - auto dst = this->create_file_and_test("rn_dst_ro.txt"); +TYPED_TEST(fuse_test, rename_file_succeeds_if_destination_file_is_read_only) { + if (this->current_provider != provider_type::sia) { + // TODO finish test + return; + } + + std::string src_file_name{"rename_test"}; + auto src = this->create_file_and_test(src_file_name); + + std::string dest_file_name{"rename_test_2"}; + auto dst = this->create_file_and_test(dest_file_name); overwrite_text(src, "NEW"); overwrite_text(dst, "OLD"); @@ -269,14 +345,14 @@ TYPED_TEST(fuse_test, rename_file_overwrite_readonly_destination_succeeds) { ASSERT_EQ(0, ::chmod(dst.c_str(), 0444)); errno = 0; - int rn = ::rename(src.c_str(), dst.c_str()); - if (rn == -1 && errno == EROFS) { + auto res = ::rename(src.c_str(), dst.c_str()); + if (res == -1 && errno == EROFS) { this->unlink_file_and_test(src); ASSERT_EQ(0, ::chmod(dst.c_str(), 0644)); this->unlink_file_and_test(dst); GTEST_SKIP(); } - ASSERT_EQ(0, rn); + ASSERT_EQ(0, res); EXPECT_EQ("NEW", slurp(dst)); @@ -284,25 +360,17 @@ TYPED_TEST(fuse_test, rename_file_overwrite_readonly_destination_succeeds) { this->unlink_file_and_test(dst); } -TYPED_TEST(fuse_test, rename_file_on_readonly_mount_sets_erofs_or_skip) { - auto src = this->create_file_and_test("rn_ro_src.txt"); - auto dst = this->mount_location() + "/rn_ro_dst.txt"; - - if (::rename(src.c_str(), src.c_str()) == 0) { - this->unlink_file_and_test(src); - GTEST_SKIP(); +TYPED_TEST(fuse_test, rename_file_retains_open_file_descriptor) { + if (this->current_provider != provider_type::sia) { + // TODO finish test + return; } - errno = 0; - EXPECT_EQ(-1, ::rename(src.c_str(), dst.c_str())); - EXPECT_EQ(EROFS, errno); + std::string src_file_name{"rename_test"}; + auto src = this->create_file_and_test(src_file_name); - this->unlink_file_and_test(src); -} - -TYPED_TEST(fuse_test, rename_file_open_fd_remains_valid) { - auto src = this->create_file_and_test("rn_fd_src.txt"); - auto dst = this->mount_location() + "/rn_fd_dst.txt"; + std::string dest_file_name{"rename_test_2"}; + auto dst = this->create_file_and_test(dest_file_name); overwrite_text(src, "HELLO"); @@ -317,13 +385,31 @@ TYPED_TEST(fuse_test, rename_file_open_fd_remains_valid) { EXPECT_EQ(ENOENT, errno); ASSERT_NE(-1, ::lseek(desc, 0, SEEK_END)); - write_all(desc, " WORLD", 6); + write_all(desc, " WORLD"); ::close(desc); EXPECT_EQ("HELLO WORLD", slurp(dst)); this->unlink_file_and_test(dst); } +// TODO revisit tests +/* +TYPED_TEST(fuse_test, rename_file_on_readonly_mount_sets_erofs_or_skip) { + auto src = this->create_file_and_test("rn_ro_src.txt"); + auto dst = this->mount_location() + "/rn_ro_dst.txt"; + + if (::rename(src.c_str(), src.c_str()) == 0) { + this->unlink_file_and_test(src); + GTEST_SKIP(); + } + + errno = 0; + EXPECT_EQ(-1, ::rename(src.c_str(), dst.c_str())); + EXPECT_EQ(EROFS, errno); + + this->unlink_file_and_test(src); +} + TYPED_TEST(fuse_test, rename_file_destination_component_name_too_long) { auto src = this->create_file_and_test("rn_ntl_src.txt"); From f7d4fe00bac55862df2d4ab22d429e158e74191e Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 20 Sep 2025 08:43:35 -0500 Subject: [PATCH 074/136] [unit test] Complete FUSE unit tests #22 --- .cspell/words.txt | 1 + .../include/fixtures/drive_fixture.hpp | 50 ++++ .../src/fuse_drive_rename_test.cpp | 86 ++----- .../src/fuse_drive_test_legacy.cpp | 25 -- .../src/fuse_drive_truncate_test.cpp | 214 ++++++++++++++++++ 5 files changed, 290 insertions(+), 86 deletions(-) create mode 100644 repertory/repertory_test/src/fuse_drive_truncate_test.cpp diff --git a/.cspell/words.txt b/.cspell/words.txt index 691b7d2b..68b1a0e2 100644 --- a/.cspell/words.txt +++ b/.cspell/words.txt @@ -5,6 +5,7 @@ _sh_denyrd _sh_denyrw _spawnv aarch64 +abcdefgh advapi32 armv8 autogen diff --git a/repertory/repertory_test/include/fixtures/drive_fixture.hpp b/repertory/repertory_test/include/fixtures/drive_fixture.hpp index 4b48063b..5b1e08f7 100644 --- a/repertory/repertory_test/include/fixtures/drive_fixture.hpp +++ b/repertory/repertory_test/include/fixtures/drive_fixture.hpp @@ -496,6 +496,56 @@ public: unlink_file_and_test(file_path); } + + static void overwrite_text(const std::string &path, const std::string &data) { + int desc = ::open(path.c_str(), O_WRONLY | O_TRUNC); + ASSERT_NE(desc, -1); + write_all(desc, data); + ::close(desc); + } + + static void write_all(int desc, const std::string &data) { + std::size_t off{0U}; + while (off < data.size()) { + auto written = ::write(desc, &data.at(off), data.length() - off); + ASSERT_NE(written, -1); + off += static_cast(written); + } + } + + static auto slurp(const std::string &path) -> std::string { + int desc = ::open(path.c_str(), O_RDONLY); + if (desc == -1) { + return {}; + } + std::string out; + std::array buf{}; + for (;;) { + auto bytes_read = ::read(desc, buf.data(), buf.size()); + if (bytes_read == 0) { + break; + } + if (bytes_read == -1) { + if (errno == EINTR) { + continue; + } + break; + } + + out.append(buf.begin(), std::next(buf.begin(), bytes_read)); + } + + ::close(desc); + return out; + } + + [[nodiscard]] static auto stat_size(const std::string &path) -> off_t { + struct stat st_unix{}; + int res = ::stat(path.c_str(), &st_unix); + EXPECT_EQ(0, res) << "stat(" << path + << ") failed: " << std::strerror(errno); + return st_unix.st_size; + } #endif // !defined(_WIN32) }; diff --git a/repertory/repertory_test/src/fuse_drive_rename_test.cpp b/repertory/repertory_test/src/fuse_drive_rename_test.cpp index 14d4d80d..9be3db5d 100644 --- a/repertory/repertory_test/src/fuse_drive_rename_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_rename_test.cpp @@ -23,60 +23,13 @@ #include "fixtures/drive_fixture.hpp" -namespace { -void overwrite_text(const std::string &path, const std::string &data); -void write_all(int desc, const std::string &data); -[[nodiscard]] auto slurp(const std::string &path) -> std::string; - -void overwrite_text(const std::string &path, const std::string &data) { - int desc = ::open(path.c_str(), O_WRONLY | O_TRUNC); - ASSERT_NE(desc, -1); - write_all(desc, data); - ::close(desc); -} - -void write_all(int desc, const std::string &data) { - std::size_t off{0U}; - while (off < data.size()) { - auto written = ::write(desc, &data.at(off), data.length() - off); - ASSERT_NE(written, -1); - off += static_cast(written); - } -} - -auto slurp(const std::string &path) -> std::string { - int desc = ::open(path.c_str(), O_RDONLY); - if (desc == -1) { - return {}; - } - std::string out; - std::array buf{}; - for (;;) { - auto bytes_read = ::read(desc, buf.data(), buf.size()); - if (bytes_read == 0) { - break; - } - if (bytes_read == -1) { - if (errno == EINTR) { - continue; - } - break; - } - - out.append(buf.begin(), std::next(buf.begin(), bytes_read)); - } - - ::close(desc); - return out; -} -} // namespace - namespace repertory { TYPED_TEST_SUITE(fuse_test, platform_provider_types); TYPED_TEST(fuse_test, rename_can_rename_a_file) { if (this->current_provider != provider_type::sia) { // TODO finish test + GTEST_SKIP(); return; } @@ -103,6 +56,7 @@ TYPED_TEST(fuse_test, rename_can_rename_a_file) { TYPED_TEST(fuse_test, rename_can_rename_a_directory) { if (this->current_provider != provider_type::sia) { // TODO finish test + GTEST_SKIP(); return; } @@ -129,6 +83,7 @@ TYPED_TEST(fuse_test, rename_can_rename_a_directory) { TYPED_TEST(fuse_test, rename_can_overwrite_existing_file) { if (this->current_provider != provider_type::sia) { // TODO finish test + GTEST_SKIP(); return; } @@ -138,8 +93,8 @@ TYPED_TEST(fuse_test, rename_can_overwrite_existing_file) { std::string dst_name{"rename2.txt"}; auto dst = this->create_file_and_test(dst_name); - overwrite_text(src, "SRC"); - overwrite_text(dst, "DST"); + this->overwrite_text(src, "SRC"); + this->overwrite_text(dst, "DST"); errno = 0; ASSERT_EQ(0, ::rename(src.c_str(), dst.c_str())); @@ -148,7 +103,7 @@ TYPED_TEST(fuse_test, rename_can_overwrite_existing_file) { EXPECT_EQ(-1, ::access(src.c_str(), F_OK)); EXPECT_EQ(ENOENT, errno); - EXPECT_EQ("SRC", slurp(dst)); + EXPECT_EQ("SRC", this->slurp(dst)); this->unlink_file_and_test(dst); } @@ -156,6 +111,7 @@ TYPED_TEST(fuse_test, rename_can_overwrite_existing_file) { TYPED_TEST(fuse_test, rename_can_rename_file_into_different_directory) { if (this->current_provider != provider_type::sia) { // TODO finish test + GTEST_SKIP(); return; } @@ -169,7 +125,7 @@ TYPED_TEST(fuse_test, rename_can_rename_file_into_different_directory) { auto src = this->create_file_and_test(file_name); std::string dst = utils::path::combine(dir2, {"moved.txt"}); - overwrite_text(src, "CMDC"); + this->overwrite_text(src, "CMDC"); errno = 0; ASSERT_EQ(0, ::rename(src.c_str(), dst.c_str())); @@ -177,7 +133,7 @@ TYPED_TEST(fuse_test, rename_can_rename_file_into_different_directory) { errno = 0; EXPECT_EQ(-1, ::access(src.c_str(), F_OK)); EXPECT_EQ(ENOENT, errno); - EXPECT_EQ("CMDC", slurp(dst)); + EXPECT_EQ("CMDC", this->slurp(dst)); this->unlink_file_and_test(dst); this->rmdir_and_test(dir1); @@ -187,16 +143,17 @@ TYPED_TEST(fuse_test, rename_can_rename_file_into_different_directory) { TYPED_TEST(fuse_test, rename_can_rename_file_to_same_path) { if (this->current_provider != provider_type::sia) { // TODO finish test + GTEST_SKIP(); return; } std::string file_name{"rename"}; auto src = this->create_file_and_test(file_name); - overwrite_text(src, "CMDC"); + this->overwrite_text(src, "CMDC"); errno = 0; EXPECT_EQ(0, ::rename(src.c_str(), src.c_str())); - EXPECT_EQ("CMDC", slurp(src)); + EXPECT_EQ("CMDC", this->slurp(src)); this->unlink_file_and_test(src); } @@ -204,6 +161,7 @@ TYPED_TEST(fuse_test, rename_can_rename_file_to_same_path) { TYPED_TEST(fuse_test, rename_file_fails_if_source_file_does_not_exist) { if (this->current_provider != provider_type::sia) { // TODO finish test + GTEST_SKIP(); return; } @@ -225,6 +183,7 @@ TYPED_TEST(fuse_test, rename_file_fails_if_destination_directory_does_not_exist) { if (this->current_provider != provider_type::sia) { // TODO finish test + GTEST_SKIP(); return; } @@ -246,6 +205,7 @@ TYPED_TEST(fuse_test, TYPED_TEST(fuse_test, rename_file_fails_if_destination_is_directory) { if (this->current_provider != provider_type::sia) { // TODO finish test + GTEST_SKIP(); return; } @@ -268,6 +228,7 @@ TYPED_TEST(fuse_test, rename_file_fails_if_destination_is_directory) { TYPED_TEST(fuse_test, rename_file_fails_if_source_directory_is_read_only) { if (this->current_provider != provider_type::sia) { // TODO finish test + GTEST_SKIP(); return; } @@ -299,6 +260,7 @@ TYPED_TEST(fuse_test, rename_file_fails_if_source_directory_is_read_only) { TYPED_TEST(fuse_test, rename_file_fails_if_destination_directory_is_read_only) { if (this->current_provider != provider_type::sia) { // TODO finish test + GTEST_SKIP(); return; } @@ -330,6 +292,7 @@ TYPED_TEST(fuse_test, rename_file_fails_if_destination_directory_is_read_only) { TYPED_TEST(fuse_test, rename_file_succeeds_if_destination_file_is_read_only) { if (this->current_provider != provider_type::sia) { // TODO finish test + GTEST_SKIP(); return; } @@ -339,8 +302,8 @@ TYPED_TEST(fuse_test, rename_file_succeeds_if_destination_file_is_read_only) { std::string dest_file_name{"rename_test_2"}; auto dst = this->create_file_and_test(dest_file_name); - overwrite_text(src, "NEW"); - overwrite_text(dst, "OLD"); + this->overwrite_text(src, "NEW"); + this->overwrite_text(dst, "OLD"); ASSERT_EQ(0, ::chmod(dst.c_str(), 0444)); @@ -354,7 +317,7 @@ TYPED_TEST(fuse_test, rename_file_succeeds_if_destination_file_is_read_only) { } ASSERT_EQ(0, res); - EXPECT_EQ("NEW", slurp(dst)); + EXPECT_EQ("NEW", this->slurp(dst)); ASSERT_EQ(0, ::chmod(dst.c_str(), 0644)); this->unlink_file_and_test(dst); @@ -363,6 +326,7 @@ TYPED_TEST(fuse_test, rename_file_succeeds_if_destination_file_is_read_only) { TYPED_TEST(fuse_test, rename_file_retains_open_file_descriptor) { if (this->current_provider != provider_type::sia) { // TODO finish test + GTEST_SKIP(); return; } @@ -372,7 +336,7 @@ TYPED_TEST(fuse_test, rename_file_retains_open_file_descriptor) { std::string dest_file_name{"rename_test_2"}; auto dst = this->create_file_and_test(dest_file_name); - overwrite_text(src, "HELLO"); + this->overwrite_text(src, "HELLO"); int desc = ::open(src.c_str(), O_RDWR); ASSERT_NE(desc, -1); @@ -385,10 +349,10 @@ TYPED_TEST(fuse_test, rename_file_retains_open_file_descriptor) { EXPECT_EQ(ENOENT, errno); ASSERT_NE(-1, ::lseek(desc, 0, SEEK_END)); - write_all(desc, " WORLD"); + this->write_all(desc, " WORLD"); ::close(desc); - EXPECT_EQ("HELLO WORLD", slurp(dst)); + EXPECT_EQ("HELLO WORLD", this->slurp(dst)); this->unlink_file_and_test(dst); } diff --git a/repertory/repertory_test/src/fuse_drive_test_legacy.cpp b/repertory/repertory_test/src/fuse_drive_test_legacy.cpp index f4041442..4e9c46f9 100644 --- a/repertory/repertory_test/src/fuse_drive_test_legacy.cpp +++ b/repertory/repertory_test/src/fuse_drive_test_legacy.cpp @@ -1,28 +1,3 @@ -// static void test_truncate(const std::string &file_path) { -// std::cout << __FUNCTION__ << std::endl; -// EXPECT_EQ(0, truncate(file_path.c_str(), 10u)); -// -// std::uint64_t file_size{}; -// EXPECT_TRUE(utils::file::get_file_size(file_path, file_size)); -// -// EXPECT_EQ(std::uint64_t(10u), file_size); -// } -// -// static void test_ftruncate(const std::string &file_path) { -// std::cout << __FUNCTION__ << std::endl; -// auto fd = open(file_path.c_str(), O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP); -// EXPECT_LE(1, fd); -// -// EXPECT_EQ(0, ftruncate(fd, 10u)); -// -// std::uint64_t file_size{}; -// EXPECT_TRUE(utils::file::get_file_size(file_path, file_size)); -// -// EXPECT_EQ(std::uint64_t(10u), file_size); -// -// close(fd); -// } -// // #if !defined(__APPLE__) // static void test_fallocate(const std::string & /* api_path */, // const std::string &file_path) { diff --git a/repertory/repertory_test/src/fuse_drive_truncate_test.cpp b/repertory/repertory_test/src/fuse_drive_truncate_test.cpp new file mode 100644 index 00000000..126fbb2d --- /dev/null +++ b/repertory/repertory_test/src/fuse_drive_truncate_test.cpp @@ -0,0 +1,214 @@ +/* + Copyright <2018-2025> + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ +#if !defined(_WIN32) + +#include "fixtures/drive_fixture.hpp" + +namespace repertory { +TYPED_TEST_SUITE(fuse_test, platform_provider_types); + +TYPED_TEST(fuse_test, truncate_can_shrink_file) { + std::string file_name{"truncate"}; + auto src = this->create_file_and_test(file_name); + this->overwrite_text(src, "ABCDEFGH"); + + errno = 0; + ASSERT_EQ(0, ::truncate(src.c_str(), 3)); + EXPECT_EQ(3, this->stat_size(src)); + EXPECT_EQ("ABC", this->slurp(src)); + + this->unlink_file_and_test(src); +} + +TYPED_TEST(fuse_test, truncate_expand_file_is_zero_filled) { + std::string name{"truncate"}; + auto src = this->create_file_and_test(name); + this->overwrite_text(src, "XYZ"); + + errno = 0; + ASSERT_EQ(0, ::truncate(src.c_str(), 10)); + EXPECT_EQ(10, this->stat_size(src)); + + auto data = this->slurp(src); + ASSERT_EQ(10U, data.size()); + EXPECT_EQ('X', data.at(0U)); + EXPECT_EQ('Y', data.at(1U)); + EXPECT_EQ('Z', data.at(2U)); + for (std::size_t idx = 3; idx < data.size(); ++idx) { + EXPECT_EQ('\0', data[idx]); + } + + this->unlink_file_and_test(src); +} + +TYPED_TEST(fuse_test, truncate_fails_if_source_does_not_exist) { + std::string name{"truncate"}; + auto src = this->create_file_path(name); + + errno = 0; + EXPECT_EQ(-1, ::truncate(src.c_str(), 1)); + EXPECT_EQ(ENOENT, errno); + + EXPECT_FALSE(utils::file::file{src}.exists()); +} + +TYPED_TEST(fuse_test, truncate_fails_if_path_is_directory) { + std::string name{"truncate"}; + auto src = this->create_directory_and_test(name); + + errno = 0; + auto res = ::truncate(src.c_str(), 0); + EXPECT_EQ(-1, res); + EXPECT_TRUE(errno == EISDIR || errno == EPERM || errno == EACCES || + errno == EINVAL); + + this->rmdir_and_test(src); +} + +/* +TYPED_TEST(fuse_test, truncate_readonly_file_eacces_or_eperm_or_erofs_skip) { + if (this->current_provider != provider_type::sia) + return; + + std::string name{"trunc_ro"}; + std::string p = this->create_file_and_test(name); + this->overwrite_text(p, "DATA"); + + ASSERT_EQ(0, ::chmod(p.c_str(), 0444)) << std::strerror(errno); + + errno = 0; + int res = ::truncate(p.c_str(), 2); + if (res == -1 && errno == EROFS) { + ASSERT_EQ(0, ::chmod(p.c_str(), 0644)); + this->unlink_file_and_test(p); + GTEST_SKIP() << "read-only mount; truncate returns EROFS"; + } + EXPECT_EQ(-1, res); + EXPECT_TRUE(errno == EACCES || errno == EPERM) + << "errno=" << errno << " " << std::strerror(errno); + + ASSERT_EQ(0, ::chmod(p.c_str(), 0644)); + this->unlink_file_and_test(p); +} + +// ========== ftruncate(2) ========== + +TYPED_TEST(fuse_test, ftruncate_shrink_open_fd) { + if (this->current_provider != provider_type::sia) + return; + + std::string name{"ftrunc_shrink"}; + std::string p = this->create_file_and_test(name); + this->overwrite_text(p, "HELLOWORLD"); // 10 bytes + + int fd = ::open(p.c_str(), O_RDWR); + ASSERT_NE(fd, -1) << "open failed: " << std::strerror(errno); + + errno = 0; + ASSERT_EQ(0, ::ftruncate(fd, 4)) << std::strerror(errno); + ::close(fd); + + EXPECT_EQ(4, this->stat_size(p)); + EXPECT_EQ("HELL", this->slurp(p)); + + this->unlink_file_and_test(p); +} + +TYPED_TEST(fuse_test, ftruncate_expand_open_fd_zero_fills) { + if (this->current_provider != provider_type::sia) + return; + + std::string name{"ftrunc_expand"}; + std::string p = this->create_file_and_test(name); + this->overwrite_text(p, "AA"); // 2 bytes + + int fd = ::open(p.c_str(), O_RDWR); + ASSERT_NE(fd, -1); + + errno = 0; + ASSERT_EQ(0, ::ftruncate(fd, 6)) << std::strerror(errno); + ::close(fd); + + EXPECT_EQ(6, this->stat_size(p)); + + auto s = this->slurp(p); + ASSERT_EQ(6u, s.size()); + EXPECT_EQ('A', s[0]); + EXPECT_EQ('A', s[1]); + for (size_t i = 2; i < s.size(); ++i) { + EXPECT_EQ('\0', s[i]) << "byte " << i << " not zero"; + } + + this->unlink_file_and_test(p); +} + +TYPED_TEST(fuse_test, ftruncate_readonly_fd_ebadf) { + if (this->current_provider != provider_type::sia) + return; + + std::string name{"ftrunc_rofd"}; + std::string p = this->create_file_and_test(name); + this->overwrite_text(p, "RW"); + + int fd = ::open(p.c_str(), O_RDONLY); + ASSERT_NE(fd, -1); + + errno = 0; + EXPECT_EQ(-1, ::ftruncate(fd, 1)); + EXPECT_EQ(EBADF, errno); + + ::close(fd); + this->unlink_file_and_test(p); +} + +TYPED_TEST(fuse_test, ftruncate_on_ro_mount_erofs_or_skip) { + if (this->current_provider != provider_type::sia) + return; + + std::string name{"ftrunc_ro_mount"}; + std::string p = this->create_file_and_test(name); + this->overwrite_text(p, "X"); + + int fd = ::open(p.c_str(), O_RDWR); + if (fd == -1) { + this->unlink_file_and_test(p); + GTEST_SKIP() << "cannot open O_RDWR; probable RO mount"; + } + + errno = 0; + int res = ::ftruncate(fd, 0); + if (res == -1 && errno == EROFS) { + ::close(fd); + this->unlink_file_and_test(p); + GTEST_SKIP() << "read-only mount; ftruncate returns EROFS"; + } + + ASSERT_EQ(0, res) << std::strerror(errno); + ::close(fd); + + this->unlink_file_and_test(p); +} +*/ + +} // namespace repertory + +#endif // !defined(_WIN32) From 29648af9ba2313f9017eea50f40558939c390418 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 20 Sep 2025 08:52:30 -0500 Subject: [PATCH 075/136] [unit test] Complete FUSE unit tests #22 --- ...=> fuse_drive_truncate_ftruncate_test.cpp} | 125 ++++++++---------- 1 file changed, 56 insertions(+), 69 deletions(-) rename repertory/repertory_test/src/{fuse_drive_truncate_test.cpp => fuse_drive_truncate_ftruncate_test.cpp} (60%) diff --git a/repertory/repertory_test/src/fuse_drive_truncate_test.cpp b/repertory/repertory_test/src/fuse_drive_truncate_ftruncate_test.cpp similarity index 60% rename from repertory/repertory_test/src/fuse_drive_truncate_test.cpp rename to repertory/repertory_test/src/fuse_drive_truncate_ftruncate_test.cpp index 126fbb2d..de91c855 100644 --- a/repertory/repertory_test/src/fuse_drive_truncate_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_truncate_ftruncate_test.cpp @@ -84,102 +84,90 @@ TYPED_TEST(fuse_test, truncate_fails_if_path_is_directory) { this->rmdir_and_test(src); } -/* -TYPED_TEST(fuse_test, truncate_readonly_file_eacces_or_eperm_or_erofs_skip) { - if (this->current_provider != provider_type::sia) - return; - +TYPED_TEST(fuse_test, truncate_fails_if_file_is_read_only) { std::string name{"trunc_ro"}; - std::string p = this->create_file_and_test(name); - this->overwrite_text(p, "DATA"); + auto src = this->create_file_and_test(name); + this->overwrite_text(src, "DATA"); - ASSERT_EQ(0, ::chmod(p.c_str(), 0444)) << std::strerror(errno); + ASSERT_EQ(0, ::chmod(src.c_str(), 0444)); errno = 0; - int res = ::truncate(p.c_str(), 2); + int res = ::truncate(src.c_str(), 2); if (res == -1 && errno == EROFS) { - ASSERT_EQ(0, ::chmod(p.c_str(), 0644)); - this->unlink_file_and_test(p); - GTEST_SKIP() << "read-only mount; truncate returns EROFS"; + ASSERT_EQ(0, ::chmod(src.c_str(), 0644)); + this->unlink_file_and_test(src); + GTEST_SKIP(); + return; } + EXPECT_EQ(-1, res); - EXPECT_TRUE(errno == EACCES || errno == EPERM) - << "errno=" << errno << " " << std::strerror(errno); + EXPECT_TRUE(errno == EACCES || errno == EPERM); - ASSERT_EQ(0, ::chmod(p.c_str(), 0644)); - this->unlink_file_and_test(p); + ASSERT_EQ(0, ::chmod(src.c_str(), 0644)); + this->unlink_file_and_test(src); } -// ========== ftruncate(2) ========== +TYPED_TEST(fuse_test, ftruncate_can_shrink_file) { + std::string name{"ftruncate"}; + auto src = this->create_file_and_test(name); + this->overwrite_text(src, "HELLOWORLD"); -TYPED_TEST(fuse_test, ftruncate_shrink_open_fd) { - if (this->current_provider != provider_type::sia) - return; - - std::string name{"ftrunc_shrink"}; - std::string p = this->create_file_and_test(name); - this->overwrite_text(p, "HELLOWORLD"); // 10 bytes - - int fd = ::open(p.c_str(), O_RDWR); - ASSERT_NE(fd, -1) << "open failed: " << std::strerror(errno); + auto desc = ::open(src.c_str(), O_RDWR); + ASSERT_NE(desc, -1); errno = 0; - ASSERT_EQ(0, ::ftruncate(fd, 4)) << std::strerror(errno); - ::close(fd); + ASSERT_EQ(0, ::ftruncate(desc, 4)); + ::close(desc); - EXPECT_EQ(4, this->stat_size(p)); - EXPECT_EQ("HELL", this->slurp(p)); + EXPECT_EQ(4, this->stat_size(src)); + EXPECT_EQ("HELL", this->slurp(src)); - this->unlink_file_and_test(p); + this->unlink_file_and_test(src); } -TYPED_TEST(fuse_test, ftruncate_expand_open_fd_zero_fills) { - if (this->current_provider != provider_type::sia) - return; +TYPED_TEST(fuse_test, ftruncate_expand_file_is_zero_filled) { + std::string name{"ftruncate"}; + auto src = this->create_file_and_test(name); + this->overwrite_text(src, "AA"); - std::string name{"ftrunc_expand"}; - std::string p = this->create_file_and_test(name); - this->overwrite_text(p, "AA"); // 2 bytes - - int fd = ::open(p.c_str(), O_RDWR); - ASSERT_NE(fd, -1); + auto desc = ::open(src.c_str(), O_RDWR); + ASSERT_NE(desc, -1); errno = 0; - ASSERT_EQ(0, ::ftruncate(fd, 6)) << std::strerror(errno); - ::close(fd); + ASSERT_EQ(0, ::ftruncate(desc, 6)); + ::close(desc); - EXPECT_EQ(6, this->stat_size(p)); + EXPECT_EQ(6, this->stat_size(src)); - auto s = this->slurp(p); - ASSERT_EQ(6u, s.size()); - EXPECT_EQ('A', s[0]); - EXPECT_EQ('A', s[1]); - for (size_t i = 2; i < s.size(); ++i) { - EXPECT_EQ('\0', s[i]) << "byte " << i << " not zero"; + auto data = this->slurp(src); + ASSERT_EQ(6U, data.size()); + EXPECT_EQ('A', data.at(0U)); + EXPECT_EQ('A', data.at(1U)); + for (std::size_t idx = 2; idx < data.size(); ++idx) { + EXPECT_EQ('\0', data[idx]); } - this->unlink_file_and_test(p); + this->unlink_file_and_test(src); } -TYPED_TEST(fuse_test, ftruncate_readonly_fd_ebadf) { - if (this->current_provider != provider_type::sia) - return; +TYPED_TEST(fuse_test, ftruncate_fails_if_file_is_read_only) { + std::string name{"ftruncate"}; + auto src = this->create_file_and_test(name); + this->overwrite_text(src, "RW"); - std::string name{"ftrunc_rofd"}; - std::string p = this->create_file_and_test(name); - this->overwrite_text(p, "RW"); - - int fd = ::open(p.c_str(), O_RDONLY); - ASSERT_NE(fd, -1); + auto desc = ::open(src.c_str(), O_RDONLY); + ASSERT_NE(desc, -1); errno = 0; - EXPECT_EQ(-1, ::ftruncate(fd, 1)); + EXPECT_EQ(-1, ::ftruncate(desc, 1)); EXPECT_EQ(EBADF, errno); - ::close(fd); - this->unlink_file_and_test(p); + ::close(desc); + this->unlink_file_and_test(src); } +/* +// TODO revisit tests TYPED_TEST(fuse_test, ftruncate_on_ro_mount_erofs_or_skip) { if (this->current_provider != provider_type::sia) return; @@ -188,27 +176,26 @@ TYPED_TEST(fuse_test, ftruncate_on_ro_mount_erofs_or_skip) { std::string p = this->create_file_and_test(name); this->overwrite_text(p, "X"); - int fd = ::open(p.c_str(), O_RDWR); - if (fd == -1) { + int desc = ::open(p.c_str(), O_RDWR); + if (desc == -1) { this->unlink_file_and_test(p); GTEST_SKIP() << "cannot open O_RDWR; probable RO mount"; } errno = 0; - int res = ::ftruncate(fd, 0); + int res = ::ftruncate(desc, 0); if (res == -1 && errno == EROFS) { - ::close(fd); + ::close(desc); this->unlink_file_and_test(p); GTEST_SKIP() << "read-only mount; ftruncate returns EROFS"; } ASSERT_EQ(0, res) << std::strerror(errno); - ::close(fd); + ::close(desc); this->unlink_file_and_test(p); } */ - } // namespace repertory #endif // !defined(_WIN32) From a837ee6bc9d221c12e33a0f8f53cf110b6a6daeb Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 20 Sep 2025 08:53:16 -0500 Subject: [PATCH 076/136] [unit test] Complete FUSE unit tests #22 --- .../repertory_test/src/fuse_drive_truncate_ftruncate_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repertory/repertory_test/src/fuse_drive_truncate_ftruncate_test.cpp b/repertory/repertory_test/src/fuse_drive_truncate_ftruncate_test.cpp index de91c855..a0b302e0 100644 --- a/repertory/repertory_test/src/fuse_drive_truncate_ftruncate_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_truncate_ftruncate_test.cpp @@ -166,8 +166,8 @@ TYPED_TEST(fuse_test, ftruncate_fails_if_file_is_read_only) { this->unlink_file_and_test(src); } -/* // TODO revisit tests +/* TYPED_TEST(fuse_test, ftruncate_on_ro_mount_erofs_or_skip) { if (this->current_provider != provider_type::sia) return; From 0158b88807ca05de64e2f47aba5287435db97001 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 20 Sep 2025 08:54:18 -0500 Subject: [PATCH 077/136] [unit test] Complete FUSE unit tests #22 --- .../repertory_test/src/fuse_drive_truncate_ftruncate_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repertory/repertory_test/src/fuse_drive_truncate_ftruncate_test.cpp b/repertory/repertory_test/src/fuse_drive_truncate_ftruncate_test.cpp index a0b302e0..f7d834ea 100644 --- a/repertory/repertory_test/src/fuse_drive_truncate_ftruncate_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_truncate_ftruncate_test.cpp @@ -160,7 +160,7 @@ TYPED_TEST(fuse_test, ftruncate_fails_if_file_is_read_only) { errno = 0; EXPECT_EQ(-1, ::ftruncate(desc, 1)); - EXPECT_EQ(EBADF, errno); + EXPECT_TRUE(errno == EBADF || errno == EINVAL); ::close(desc); this->unlink_file_and_test(src); From e9df198cbd5d18caa1483a2376a712bceb738d7c Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 20 Sep 2025 09:10:09 -0500 Subject: [PATCH 078/136] [unit test] Complete FUSE unit tests #22 --- .../include/fixtures/drive_fixture.hpp | 4 + .../src/fuse_drive_getattr_fgetattr_test.cpp | 151 ++++++++++++++++++ .../src/fuse_drive_test_legacy.cpp | 33 ---- 3 files changed, 155 insertions(+), 33 deletions(-) create mode 100644 repertory/repertory_test/src/fuse_drive_getattr_fgetattr_test.cpp diff --git a/repertory/repertory_test/include/fixtures/drive_fixture.hpp b/repertory/repertory_test/include/fixtures/drive_fixture.hpp index 5b1e08f7..9d82ad2c 100644 --- a/repertory/repertory_test/include/fixtures/drive_fixture.hpp +++ b/repertory/repertory_test/include/fixtures/drive_fixture.hpp @@ -199,6 +199,7 @@ public: static std::vector drive_args2; static std::string mount_location; static std::string mount_location2; + static provider_type mount_provider; protected: static void SetUpTestCase() { @@ -575,6 +576,9 @@ std::string drive_fixture::mount_location; template std::string drive_fixture::mount_location2; +template +provider_type drive_fixture::mount_provider{provider_t::type}; + using platform_provider_types = ::testing::Types + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ +#if !defined(_WIN32) + +#include "fixtures/drive_fixture.hpp" + +namespace repertory { +TYPED_TEST_SUITE(fuse_test, platform_provider_types); + +TYPED_TEST(fuse_test, getattr_regular_file_reports_type_and_size) { + std::string name{"getattr"}; + auto src = this->create_file_and_test(name); + this->overwrite_text(src, "HELLO"); + + struct stat st_unix{}; + errno = 0; + ASSERT_EQ(0, ::stat(src.c_str(), &st_unix)); + + EXPECT_TRUE(S_ISREG(st_unix.st_mode)); + EXPECT_EQ(5, st_unix.st_size); + + this->unlink_file_and_test(src); +} + +TYPED_TEST(fuse_test, getattr_directory_reports_type) { + std::string dir_name{"getattr_dir"}; + auto dir = this->create_directory_and_test(dir_name); + + struct stat st_unix{}; + errno = 0; + ASSERT_EQ(0, ::stat(dir.c_str(), &st_unix)); + EXPECT_TRUE(S_ISDIR(st_unix.st_mode)); + + this->rmdir_and_test(dir); +} + +/* +TYPED_TEST(fuse_test, getattr_missing_path_sets_enoent) { + std::string missing = this->mount_location + "/ga_missing.txt"; + struct stat st_unix{}; + errno = 0; + EXPECT_EQ(-1, ::stat(missing.c_str(), &st_unix)); + EXPECT_EQ(ENOENT, errno); +} + +TYPED_TEST(fuse_test, fgetattr_on_open_file_reflects_size_growth) { + std::string name{"fga_grow"}; + std::string p = this->create_file_and_test(name); + this->overwrite_text(p, "ABC"); // 3 bytes + + int fd = ::open(p.c_str(), O_RDWR); + ASSERT_NE(fd, -1) << "open failed: " << std::strerror(errno); + + // append via fd; size should grow + const char *more = "DEF"; + ASSERT_EQ(3, ::write(fd, more, 3)) << std::strerror(errno); + + struct stat st_unix{}; + errno = 0; + ASSERT_EQ(0, ::fstat(fd, &st_unix)) << std::strerror(errno); + EXPECT_TRUE(S_ISREG(st_unix.st_mode)); + EXPECT_EQ(6, st_unix.st_size); + + ::close(fd); + this->unlink_file_and_test(p); +} + +TYPED_TEST(fuse_test, fgetattr_on_directory_fd_reports_type) { + std::string dname{"fga_dir"}; + std::string d = this->create_directory_and_test(dname); + +#if defined(O_DIRECTORY) + int fd = ::open(d.c_str(), O_RDONLY | O_DIRECTORY); +#else + int fd = ::open(d.c_str(), O_RDONLY); +#endif + ASSERT_NE(fd, -1) << "open dir failed: " << std::strerror(errno); + + struct stat st_unix{}; + errno = 0; + ASSERT_EQ(0, ::fstat(fd, &st_unix)) << std::strerror(errno); + EXPECT_TRUE(S_ISDIR(st_unix.st_mode)); + + ::close(fd); + this->rmdir_and_test(d); +} + +TYPED_TEST(fuse_test, fgetattr_on_closed_fd_sets_ebadf) { + std::string name{"fga_ebadf"}; + std::string p = this->create_file_and_test(name); + this->overwrite_text(p, "X"); + + int fd = ::open(p.c_str(), O_RDONLY); + ASSERT_NE(fd, -1); + ASSERT_EQ(0, ::close(fd)); + + struct stat st_unix{}; + errno = 0; + EXPECT_EQ(-1, ::fstat(fd, &st_unix)); + EXPECT_EQ(EBADF, errno); + + this->unlink_file_and_test(p); +} + +TYPED_TEST(fuse_test, getattr_reflects_changes_after_write_and_chmod) { + std::string name{"ga_mutate"}; + std::string p = this->create_file_and_test(name); + this->overwrite_text(p, "HI"); // 2 bytes + + // Write more and sync; then stat should reflect new size. + int fd = ::open(p.c_str(), O_RDWR); + ASSERT_NE(fd, -1); + const char *more = "JKLMN"; + ASSERT_EQ(5, ::write(fd, more, 5)); + ASSERT_EQ(0, ::fsync(fd)); + ASSERT_EQ(0, ::close(fd)); + + // chmod shouldn’t change type; size should now be 7. + ASSERT_EQ(0, ::chmod(p.c_str(), 0644)) << std::strerror(errno); + + struct stat st_unix{}; + errno = 0; + ASSERT_EQ(0, ::stat(p.c_str(), &st_unix)) << std::strerror(errno); + EXPECT_TRUE(S_ISREG(st_unix.st_mode)); + EXPECT_EQ(7, st_unix.st_size); // "HIJKLMN" + + this->unlink_file_and_test(p); +} +*/ +} // namespace repertory + +#endif // !defined(_WIN32) diff --git a/repertory/repertory_test/src/fuse_drive_test_legacy.cpp b/repertory/repertory_test/src/fuse_drive_test_legacy.cpp index 4e9c46f9..1405c343 100644 --- a/repertory/repertory_test/src/fuse_drive_test_legacy.cpp +++ b/repertory/repertory_test/src/fuse_drive_test_legacy.cpp @@ -53,39 +53,6 @@ // EXPECT_FALSE(S_ISREG(unix_st.st_mode)); // } // -// static void -// test_write_operations_fail_if_read_only(const std::string & /* api_path */, -// const std::string &file_path) { -// std::cout << __FUNCTION__ << std::endl; -// auto fd = -// open(file_path.c_str(), O_CREAT | O_RDONLY, S_IRUSR | S_IWUSR | -// S_IRGRP); -// EXPECT_LE(1, fd); -// -// std::string data = "TestData"; -// EXPECT_EQ(-1, write(fd, data.data(), data.size())); -// -// EXPECT_EQ(-1, ftruncate(fd, 9u)); -// -// #if !defined(__APPLE__) -// EXPECT_EQ(-1, fallocate(fd, 0, 0, 16)); -// #endif -// -// EXPECT_EQ(0, close(fd)); -// -// std::this_thread::sleep_for(SLEEP_SECONDS); -// -// std::uint64_t file_size{}; -// EXPECT_TRUE(utils::file::get_file_size(file_path, file_size)); -// EXPECT_EQ(std::size_t(0u), file_size); -// -// // filesystem_item fsi{}; -// // EXPECT_EQ(api_error::success, -// // provider.get_filesystem_item(api_path, false, fsi)); -// // EXPECT_TRUE(utils::file::get_file_size(fsi.source_path, file_size)); -// // EXPECT_EQ(std::size_t(0u), file_size); -// } -// // #if !__APPLE__ && HAS_SETXATTR // static void test_xattr_invalid_parameters(const std::string &file_path) { // std::cout << __FUNCTION__ << std::endl; From 38e928811e688f0f53caaf377c8b4f813e0dd21b Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 20 Sep 2025 10:27:12 -0500 Subject: [PATCH 079/136] [unit test] Complete FUSE unit tests #22 --- .../src/fuse_drive_getattr_fgetattr_test.cpp | 93 +++++++++---------- 1 file changed, 44 insertions(+), 49 deletions(-) diff --git a/repertory/repertory_test/src/fuse_drive_getattr_fgetattr_test.cpp b/repertory/repertory_test/src/fuse_drive_getattr_fgetattr_test.cpp index 647297a4..e7c2c632 100644 --- a/repertory/repertory_test/src/fuse_drive_getattr_fgetattr_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_getattr_fgetattr_test.cpp @@ -53,99 +53,94 @@ TYPED_TEST(fuse_test, getattr_directory_reports_type) { this->rmdir_and_test(dir); } -/* TYPED_TEST(fuse_test, getattr_missing_path_sets_enoent) { - std::string missing = this->mount_location + "/ga_missing.txt"; + auto src = this->create_file_path("getattr"); struct stat st_unix{}; errno = 0; - EXPECT_EQ(-1, ::stat(missing.c_str(), &st_unix)); + EXPECT_EQ(-1, ::stat(src.c_str(), &st_unix)); EXPECT_EQ(ENOENT, errno); } TYPED_TEST(fuse_test, fgetattr_on_open_file_reflects_size_growth) { - std::string name{"fga_grow"}; - std::string p = this->create_file_and_test(name); - this->overwrite_text(p, "ABC"); // 3 bytes + std::string name{"fgetattr"}; + auto src = this->create_file_and_test(name); + this->overwrite_text(src, "ABC"); - int fd = ::open(p.c_str(), O_RDWR); - ASSERT_NE(fd, -1) << "open failed: " << std::strerror(errno); + auto desc = ::open(src.c_str(), O_RDWR); + ASSERT_NE(desc, -1); - // append via fd; size should grow - const char *more = "DEF"; - ASSERT_EQ(3, ::write(fd, more, 3)) << std::strerror(errno); + std::string_view more{"DEF"}; + ASSERT_EQ(3, ::write(desc, more.data(), more.size())); struct stat st_unix{}; errno = 0; - ASSERT_EQ(0, ::fstat(fd, &st_unix)) << std::strerror(errno); + ASSERT_EQ(0, ::fstat(desc, &st_unix)); EXPECT_TRUE(S_ISREG(st_unix.st_mode)); EXPECT_EQ(6, st_unix.st_size); - ::close(fd); - this->unlink_file_and_test(p); + ::close(desc); + this->unlink_file_and_test(src); } -TYPED_TEST(fuse_test, fgetattr_on_directory_fd_reports_type) { - std::string dname{"fga_dir"}; - std::string d = this->create_directory_and_test(dname); +TYPED_TEST(fuse_test, fgetattr_directory_reports_type) { + std::string dir_name{"dir"}; + auto dir = this->create_directory_and_test(dir_name); #if defined(O_DIRECTORY) - int fd = ::open(d.c_str(), O_RDONLY | O_DIRECTORY); -#else - int fd = ::open(d.c_str(), O_RDONLY); -#endif - ASSERT_NE(fd, -1) << "open dir failed: " << std::strerror(errno); + auto desc = ::open(dir.c_str(), O_RDONLY | O_DIRECTORY); +#else // !defined(O_DIRECTORY) + auto desc = ::open(d.c_str(), O_RDONLY); +#endif // defined(O_DIRECTORY) + ASSERT_NE(desc, -1); struct stat st_unix{}; errno = 0; - ASSERT_EQ(0, ::fstat(fd, &st_unix)) << std::strerror(errno); + ASSERT_EQ(0, ::fstat(desc, &st_unix)); EXPECT_TRUE(S_ISDIR(st_unix.st_mode)); - ::close(fd); - this->rmdir_and_test(d); + ::close(desc); + this->rmdir_and_test(dir); } TYPED_TEST(fuse_test, fgetattr_on_closed_fd_sets_ebadf) { - std::string name{"fga_ebadf"}; - std::string p = this->create_file_and_test(name); - this->overwrite_text(p, "X"); + std::string name{"fgetattr"}; + auto src = this->create_file_and_test(name); + this->overwrite_text(src, "X"); - int fd = ::open(p.c_str(), O_RDONLY); - ASSERT_NE(fd, -1); - ASSERT_EQ(0, ::close(fd)); + auto desc = ::open(src.c_str(), O_RDONLY); + ASSERT_NE(desc, -1); + ASSERT_EQ(0, ::close(desc)); struct stat st_unix{}; errno = 0; - EXPECT_EQ(-1, ::fstat(fd, &st_unix)); + EXPECT_EQ(-1, ::fstat(desc, &st_unix)); EXPECT_EQ(EBADF, errno); - this->unlink_file_and_test(p); + this->unlink_file_and_test(src); } TYPED_TEST(fuse_test, getattr_reflects_changes_after_write_and_chmod) { - std::string name{"ga_mutate"}; - std::string p = this->create_file_and_test(name); - this->overwrite_text(p, "HI"); // 2 bytes + std::string name{"getattr"}; + auto src = this->create_file_and_test(name); + this->overwrite_text(src, "HI"); // 2 bytes - // Write more and sync; then stat should reflect new size. - int fd = ::open(p.c_str(), O_RDWR); - ASSERT_NE(fd, -1); - const char *more = "JKLMN"; - ASSERT_EQ(5, ::write(fd, more, 5)); - ASSERT_EQ(0, ::fsync(fd)); - ASSERT_EQ(0, ::close(fd)); + auto desc = ::open(src.c_str(), O_RDWR); + ASSERT_NE(desc, -1); + std::string_view more{"CMDC"}; + ASSERT_EQ(4, ::write(desc, more.data(), more.size())); + ASSERT_EQ(0, ::fsync(desc)); + ASSERT_EQ(0, ::close(desc)); - // chmod shouldn’t change type; size should now be 7. - ASSERT_EQ(0, ::chmod(p.c_str(), 0644)) << std::strerror(errno); + ASSERT_EQ(0, ::chmod(src.c_str(), 0644)); struct stat st_unix{}; errno = 0; - ASSERT_EQ(0, ::stat(p.c_str(), &st_unix)) << std::strerror(errno); + ASSERT_EQ(0, ::stat(src.c_str(), &st_unix)); EXPECT_TRUE(S_ISREG(st_unix.st_mode)); - EXPECT_EQ(7, st_unix.st_size); // "HIJKLMN" + EXPECT_EQ(7, st_unix.st_size); - this->unlink_file_and_test(p); + this->unlink_file_and_test(src); } -*/ } // namespace repertory #endif // !defined(_WIN32) From cfe06ff304134f6bda4a0520fa4a27dbf8606030 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 20 Sep 2025 10:28:00 -0500 Subject: [PATCH 080/136] [unit test] Complete FUSE unit tests #22 --- .../repertory_test/src/fuse_drive_getattr_fgetattr_test.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/repertory/repertory_test/src/fuse_drive_getattr_fgetattr_test.cpp b/repertory/repertory_test/src/fuse_drive_getattr_fgetattr_test.cpp index e7c2c632..58a115b2 100644 --- a/repertory/repertory_test/src/fuse_drive_getattr_fgetattr_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_getattr_fgetattr_test.cpp @@ -54,7 +54,8 @@ TYPED_TEST(fuse_test, getattr_directory_reports_type) { } TYPED_TEST(fuse_test, getattr_missing_path_sets_enoent) { - auto src = this->create_file_path("getattr"); + std::string file_name{"getattr"}; + auto src = this->create_file_path(file_name); struct stat st_unix{}; errno = 0; EXPECT_EQ(-1, ::stat(src.c_str(), &st_unix)); From 5f4a2703dc805ea0f1008731f235ddca6b74f8cb Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 20 Sep 2025 10:32:42 -0500 Subject: [PATCH 081/136] [unit test] Complete FUSE unit tests #22 --- .../repertory_test/src/fuse_drive_getattr_fgetattr_test.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/repertory/repertory_test/src/fuse_drive_getattr_fgetattr_test.cpp b/repertory/repertory_test/src/fuse_drive_getattr_fgetattr_test.cpp index 58a115b2..1bf50c4e 100644 --- a/repertory/repertory_test/src/fuse_drive_getattr_fgetattr_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_getattr_fgetattr_test.cpp @@ -67,7 +67,7 @@ TYPED_TEST(fuse_test, fgetattr_on_open_file_reflects_size_growth) { auto src = this->create_file_and_test(name); this->overwrite_text(src, "ABC"); - auto desc = ::open(src.c_str(), O_RDWR); + auto desc = ::open(src.c_str(), O_RDWR | O_APPEND); ASSERT_NE(desc, -1); std::string_view more{"DEF"}; @@ -125,7 +125,7 @@ TYPED_TEST(fuse_test, getattr_reflects_changes_after_write_and_chmod) { auto src = this->create_file_and_test(name); this->overwrite_text(src, "HI"); // 2 bytes - auto desc = ::open(src.c_str(), O_RDWR); + auto desc = ::open(src.c_str(), O_RDWR | O_APPEND); ASSERT_NE(desc, -1); std::string_view more{"CMDC"}; ASSERT_EQ(4, ::write(desc, more.data(), more.size())); @@ -138,7 +138,7 @@ TYPED_TEST(fuse_test, getattr_reflects_changes_after_write_and_chmod) { errno = 0; ASSERT_EQ(0, ::stat(src.c_str(), &st_unix)); EXPECT_TRUE(S_ISREG(st_unix.st_mode)); - EXPECT_EQ(7, st_unix.st_size); + EXPECT_EQ(6, st_unix.st_size); this->unlink_file_and_test(src); } From 8df54749a5d1a0bd702346b5c1ef2888fa4244d6 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 20 Sep 2025 12:30:29 -0500 Subject: [PATCH 082/136] [unit test] Complete FUSE unit tests #22 --- .cspell/words.txt | 1 + CHANGELOG.md | 4 + .../src/drives/fuse/fuse_base.cpp | 4 +- .../src/drives/fuse/fuse_drive.cpp | 8 +- .../src/fuse_drive_fallocate_test.cpp | 245 ++++++++++++++++++ 5 files changed, 258 insertions(+), 4 deletions(-) create mode 100644 repertory/repertory_test/src/fuse_drive_fallocate_test.cpp diff --git a/.cspell/words.txt b/.cspell/words.txt index 68b1a0e2..2a38d841 100644 --- a/.cspell/words.txt +++ b/.cspell/words.txt @@ -106,6 +106,7 @@ endfunction eventlib expect_streq expect_strne +falloc_fl_keep_size fallocate fallocate_impl fext diff --git a/CHANGELOG.md b/CHANGELOG.md index ff90c326..a6959c9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,10 @@ * \#60 Implement secure key via KDF for transparent data encryption/decryption * \#61 [ui] UI theme should match repertory blue +### Changes from v2.0.7-release + +* Fixed handling of `FALLOC_FL_KEEP_SIZE` on Linux + ## v2.0.7-release diff --git a/repertory/librepertory/src/drives/fuse/fuse_base.cpp b/repertory/librepertory/src/drives/fuse/fuse_base.cpp index ccda30cb..5f80b8d6 100644 --- a/repertory/librepertory/src/drives/fuse/fuse_base.cpp +++ b/repertory/librepertory/src/drives/fuse/fuse_base.cpp @@ -102,7 +102,7 @@ fuse_base::~fuse_base() { E_CONSUMER_RELEASE(); } auto fuse_base::access_(const char *path, int mask) -> int { REPERTORY_USES_FUNCTION_NAME(); - return instance().instance().execute_callback( + return instance().execute_callback( function_name, path, [&](std::string api_path) -> api_error { return instance().access_impl(std::move(api_path), mask); }); @@ -112,7 +112,7 @@ auto fuse_base::access_(const char *path, int mask) -> int { auto fuse_base::chflags_(const char *path, uint32_t flags) -> int { REPERTORY_USES_FUNCTION_NAME(); - return instance().instance().execute_callback( + return instance().execute_callback( function_name, path, [&](std::string api_path) -> api_error { return instance().chflags_impl(std::move(api_path), flags); }); diff --git a/repertory/librepertory/src/drives/fuse/fuse_drive.cpp b/repertory/librepertory/src/drives/fuse/fuse_drive.cpp index d9faf864..002a4102 100644 --- a/repertory/librepertory/src/drives/fuse/fuse_drive.cpp +++ b/repertory/librepertory/src/drives/fuse/fuse_drive.cpp @@ -336,6 +336,7 @@ auto fuse_drive::fallocate_impl(std::string /*api_path*/, int mode, i_open_file::native_operation_callback allocator; + auto new_file_size = static_cast(offset + length); #if defined(__APPLE__) fstore_t fstore = {0}; if (not(mode & PREALLOCATE)) { @@ -368,10 +369,13 @@ auto fuse_drive::fallocate_impl(std::string /*api_path*/, int mode, return (fallocate(handle, mode, offset, length) == -1) ? api_error::os_error : api_error::success; }; + + if ((mode & FALLOC_FL_KEEP_SIZE) == FALLOC_FL_KEEP_SIZE) { + new_file_size = open_file->get_file_size(); + } #endif // __APPLE__ - return open_file->native_operation( - static_cast(offset + length), allocator); + return open_file->native_operation(new_file_size, allocator); } auto fuse_drive::fgetattr_impl(std::string api_path, struct stat *u_stat, diff --git a/repertory/repertory_test/src/fuse_drive_fallocate_test.cpp b/repertory/repertory_test/src/fuse_drive_fallocate_test.cpp new file mode 100644 index 00000000..ae71858c --- /dev/null +++ b/repertory/repertory_test/src/fuse_drive_fallocate_test.cpp @@ -0,0 +1,245 @@ +/* + Copyright <2018-2025> + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ +#if !defined(_WIN32) + +#include "fixtures/drive_fixture.hpp" + +namespace repertory { +TYPED_TEST_SUITE(fuse_test, platform_provider_types); + +TYPED_TEST(fuse_test, fallocate_basic_preallocation_platform_semantics) { + std::string name{"fallocate"}; + auto src = this->create_file_and_test(name); + + auto desc = ::open(src.c_str(), O_RDWR); + ASSERT_NE(desc, -1); + + constexpr off_t off = 0; + constexpr off_t len = 64 * 1024; + +#if defined(__APPLE__) + fstore_t store{}; + store.fst_flags = F_ALLOCATECONTIG; + store.fst_posmode = F_PEOFPOSMODE; + store.fst_offset = 0; + store.fst_length = len; + store.fst_bytesalloc = 0; + + auto res = ::fcntl(desc, F_PREALLOCATE, &store); + if (res == -1) { + store.fst_flags = F_ALLOCATEALL; + res = ::fcntl(desc, F_PREALLOCATE, &store); + } + EXPECT_EQ(0, res); + + struct stat st_unix{}; + EXPECT_EQ(0, ::fstat(desc, &st_unix)); + EXPECT_TRUE(S_ISREG(st_unix.st_mode)); + EXPECT_EQ(0, st_unix.st_size); +#else // !defined(__APPLE__) + auto res = ::posix_fallocate(desc, off, len); + EXPECT_EQ(0, res); + + struct stat st_unix{}; + EXPECT_EQ(0, ::fstat(desc, &st_unix)); + EXPECT_TRUE(S_ISREG(st_unix.st_mode)); + EXPECT_EQ(off + len, st_unix.st_size); +#endif // defined(__APPLE__) + + ::close(desc); + this->unlink_file_and_test(src); +} + +TYPED_TEST(fuse_test, fallocate_then_ftruncate_makes_size_visible) { + std::string name{"fallocate"}; + auto src = this->create_file_and_test(name); + + auto desc = ::open(src.c_str(), O_RDWR); + ASSERT_NE(desc, -1); + + constexpr off_t len = 128 * 1024; + +#if defined(__APPLE__) + fstore_t store{}; + store.fst_flags = F_ALLOCATECONTIG; + store.fst_posmode = F_PEOFPOSMODE; + store.fst_offset = 0; + store.fst_length = len; + store.fst_bytesalloc = 0; + auto res = ::fcntl(desc, F_PREALLOCATE, &store); + if (res == -1) { + store.fst_flags = F_ALLOCATEALL; + res = ::fcntl(desc, F_PREALLOCATE, &store); + } + EXPECT_EQ(0, res); + EXPECT_EQ(0, ::ftruncate(desc, len)); +#else // !defined(__APPLE__) + auto res = ::posix_fallocate(desc, 0, len); + EXPECT_EQ(0, res); + EXPECT_EQ(0, ::ftruncate(desc, len / 2)); + EXPECT_EQ(0, ::ftruncate(desc, len)); +#endif // defined(__APPLE__) + + struct stat st_unix{}; + EXPECT_EQ(0, ::fstat(desc, &st_unix)); + EXPECT_TRUE(S_ISREG(st_unix.st_mode)); + EXPECT_EQ(len, st_unix.st_size); + + ::close(desc); + this->unlink_file_and_test(src); +} + +#if !defined(__APPLE__) +TYPED_TEST(fuse_test, + fallocate_does_not_change_size_when_keep_size_is_specified) { + std::string name{"fallocate"}; + auto src = this->create_file_and_test(name); + + auto desc = ::open(src.c_str(), O_RDWR); + ASSERT_NE(desc, -1); + + EXPECT_EQ(0, ::ftruncate(desc, 4096)); + + constexpr off_t len = 64 * 1024; + errno = 0; + auto res = ::fallocate(desc, FALLOC_FL_KEEP_SIZE, 0, len); + if (res == -1 && + (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)) { + ::close(desc); + this->unlink_file_and_test(src); + return; + } + EXPECT_EQ(0, res); + + struct stat st_unix{}; + EXPECT_EQ(0, ::fstat(desc, &st_unix)); + EXPECT_TRUE(S_ISREG(st_unix.st_mode)); + EXPECT_EQ(4096, st_unix.st_size); + + ::close(desc); + this->unlink_file_and_test(src); +} + +TYPED_TEST( + fuse_test, + fallocate_does_not_change_size_when_keep_size_and_punch_hole_are_specified) { + std::string name{"fallocate"}; + auto src = this->create_file_and_test(name); + + auto desc = ::open(src.c_str(), O_RDWR); + ASSERT_NE(desc, -1); + + constexpr off_t size = 64 * 1024; + EXPECT_EQ(0, ::ftruncate(desc, size)); + + constexpr off_t hole_off = 24 * 1024; + constexpr off_t hole_len = 8 * 1024; + + errno = 0; + auto res = ::fallocate(desc, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, + hole_off, hole_len); + if (res == -1 && + (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)) { + ::close(desc); + this->unlink_file_and_test(src); + return; + } + EXPECT_EQ(0, res) << "errno: " << errno; + + struct stat st_unix{}; + EXPECT_EQ(0, ::fstat(desc, &st_unix)); + EXPECT_EQ(size, st_unix.st_size); + + ::close(desc); + this->unlink_file_and_test(src); +} +#endif // !defined(__APPLE__) + +TYPED_TEST(fuse_test, fallocate_can_handle_invalid_arguments) { + std::string name{"fallocate"}; + auto src = this->create_file_and_test(name); + + auto desc = ::open(src.c_str(), O_RDWR); + ASSERT_NE(desc, -1); + +#if defined(__APPLE__) + fstore_t store{}; + store.fst_flags = F_ALLOCATEALL; + store.fst_posmode = F_PEOFPOSMODE; + store.fst_offset = 0; + store.fst_length = 0; + errno = 0; + auto res = ::fcntl(desc, F_PREALLOCATE, &store); + if (res == 0) { + ::close(desc); + this->unlink_file_and_test(src); + return; + } + + EXPECT_EQ(-1, res); + EXPECT_TRUE(errno == EINVAL || errno == EOPNOTSUPP || errno == ENOSYS); +#else // !defined(__APPLE__) + auto ret1 = ::posix_fallocate(desc, -1, 4096); + EXPECT_EQ(EINVAL, ret1); + + auto ret2 = ::posix_fallocate(desc, 0, -4096); + EXPECT_EQ(EINVAL, ret2); +#endif // defined(__APPLE__) + + ::close(desc); + this->unlink_file_and_test(src); +} + +TYPED_TEST(fuse_test, fallocate_fails_on_directory) { + std::string dir_name{"dir"}; + auto dir = this->create_directory_and_test(dir_name); + +#if defined(__APPLE__) + auto desc = ::open(dir.c_str(), O_RDONLY); + EXPECT_NE(desc, -1); + + fstore_t store{}; + store.fst_flags = F_ALLOCATEALL; + store.fst_posmode = F_PEOFPOSMODE; + store.fst_offset = 0; + store.fst_length = 4096; + + errno = 0; + auto res = ::fcntl(desc, F_PREALLOCATE, &store); + EXPECT_EQ(-1, res); + EXPECT_TRUE(errno == EISDIR || errno == EBADF || errno == EOPNOTSUPP || + errno == ENOTTY || errno == ENOSYS); + ::close(desc); +#else // !defined(__APPLE__) + auto desc = ::open(dir.c_str(), O_RDONLY | O_DIRECTORY); + EXPECT_NE(desc, -1); + + auto ret = ::posix_fallocate(desc, 0, 4096); + EXPECT_NE(0, ret); + ::close(desc); +#endif // defined(__APPLE__) + + this->rmdir_and_test(dir); +} +} // namespace repertory + +#endif // !defined(_WIN32) From 1bc57976c900e6c792146178fe39eba5a169da47 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 20 Sep 2025 12:54:39 -0500 Subject: [PATCH 083/136] [unit test] Complete FUSE unit tests #22 --- .cspell/words.txt | 1 + .../src/fuse_drive_utimens_futimens_test.cpp | 320 ++++++++++++++++++ 2 files changed, 321 insertions(+) create mode 100644 repertory/repertory_test/src/fuse_drive_utimens_futimens_test.cpp diff --git a/.cspell/words.txt b/.cspell/words.txt index 2a38d841..232718e4 100644 --- a/.cspell/words.txt +++ b/.cspell/words.txt @@ -163,6 +163,7 @@ libuuid libuuid_include_dirs libvlc linkflags +llabsll localappdata lpbyte lpthread diff --git a/repertory/repertory_test/src/fuse_drive_utimens_futimens_test.cpp b/repertory/repertory_test/src/fuse_drive_utimens_futimens_test.cpp new file mode 100644 index 00000000..30346577 --- /dev/null +++ b/repertory/repertory_test/src/fuse_drive_utimens_futimens_test.cpp @@ -0,0 +1,320 @@ +/* + Copyright <2018-2025> + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ +#if !defined(_WIN32) + +#include "fixtures/drive_fixture.hpp" + +namespace { +using repertory::utils::time::NANOS_PER_SECOND; + +void get_times_ns(const std::string &path, long long &at_ns, long long &mt_ns) { + struct stat st_unix{}; + ASSERT_EQ(0, ::stat(path.c_str(), &st_unix)); + +#if defined(__APPLE__) + at_ns = static_cast(st_unix.st_atimespec.tv_sec) * + static_cast(NANOS_PER_SECOND) + + static_cast(st_unix.st_atimespec.tv_nsec); + mt_ns = static_cast(st_unix.st_mtimespec.tv_sec) * + static_cast(NANOS_PER_SECOND) + + static_cast(st_unix.st_mtimespec.tv_nsec); +#else // !defined(__APPLE__) + at_ns = static_cast(st_unix.st_atim.tv_sec) * + static_cast(NANOS_PER_SECOND) + + static_cast(st_unix.st_atim.tv_nsec); + mt_ns = static_cast(st_unix.st_mtim.tv_sec) * + static_cast(NANOS_PER_SECOND) + + static_cast(st_unix.st_mtim.tv_nsec); +#endif // defined(__APPLE__) +} + +auto ts_make(time_t sec, long nsec) -> struct timespec { + struct timespec spec{}; + spec.tv_sec = sec; + spec.tv_nsec = nsec; + return spec; +} + +[[nodiscard]] auto +to_ns(const struct timespec &spec) -> long long { + return static_cast(spec.tv_sec) * + static_cast(NANOS_PER_SECOND) + + static_cast(spec.tv_nsec); +} + +[[nodiscard]] auto now_ns() -> long long { + struct timespec spec{}; +#if defined(CLOCK_REALTIME) + clock_gettime(CLOCK_REALTIME, &spec); +#else + struct timeval val{}; + gettimeofday(&val, nullptr); + spec.tv_sec = val.tv_sec; + spec.tv_nsec = val.tv_usec * 1000; +#endif + return to_ns(spec); +} + +[[nodiscard]] auto llabsll(long long value) -> long long { + return (value < 0) ? -value : value; +} + +constexpr long long GRANULAR_TOL_NS = + 1LL * static_cast(NANOS_PER_SECOND); +constexpr long long NOW_TOL_NS = 5LL * static_cast(NANOS_PER_SECOND); +} // namespace + +namespace repertory { +using std::strerror; + +TYPED_TEST_SUITE(fuse_test, platform_provider_types); + +TYPED_TEST(fuse_test, utimens_set_both_times_specific_values) { + std::string name{"utimens"}; + auto src = this->create_file_and_test(name); + + long long at0{}; + long long mt0{}; + get_times_ns(src, at0, mt0); + + auto now = now_ns(); + auto target_at = + now - 3600LL * static_cast(NANOS_PER_SECOND) + 111111111LL; + auto target_mt = + now - 1800LL * static_cast(NANOS_PER_SECOND) + 222222222LL; + + struct timespec spec[2]{ + ts_make(static_cast(target_at / + static_cast(NANOS_PER_SECOND)), + static_cast(target_at % + static_cast(NANOS_PER_SECOND))), + ts_make(static_cast(target_mt / + static_cast(NANOS_PER_SECOND)), + static_cast(target_mt % + static_cast(NANOS_PER_SECOND)))}; + + errno = 0; + ASSERT_EQ(0, ::utimensat(AT_FDCWD, src.c_str(), spec, 0)); + + long long at1{}; + long long mt1{}; + get_times_ns(src, at1, mt1); + + EXPECT_LE(llabsll(at1 - target_at), GRANULAR_TOL_NS); + EXPECT_LE(llabsll(mt1 - target_mt), GRANULAR_TOL_NS); + + this->unlink_file_and_test(src); +} + +TYPED_TEST(fuse_test, utimens_set_atime_only_omit_mtime) { + std::string name{"utimens"}; + auto src = this->create_file_and_test(name); + + long long at_before{}; + long long mt_before{}; + get_times_ns(src, at_before, mt_before); + + long long target_at = + now_ns() - 10LL * static_cast(NANOS_PER_SECOND); + + struct timespec spec[2]{ + ts_make(static_cast(target_at / + static_cast(NANOS_PER_SECOND)), + static_cast(target_at % + static_cast(NANOS_PER_SECOND))), + ts_make(0, UTIME_OMIT)}; + + errno = 0; + ASSERT_EQ(0, ::utimensat(AT_FDCWD, src.c_str(), spec, 0)); + + long long at_after{}; + long long mt_after{}; + get_times_ns(src, at_after, mt_after); + + EXPECT_LE(llabsll(at_after - target_at), GRANULAR_TOL_NS); + EXPECT_LE(llabsll(mt_after - mt_before), GRANULAR_TOL_NS); + + this->unlink_file_and_test(src); +} + +TYPED_TEST(fuse_test, utimens_set_mtime_only_omit_atime) { + std::string name{"utimens"}; + auto src = this->create_file_and_test(name); + + long long at_before{}; + long long mt_before{}; + get_times_ns(src, at_before, mt_before); + + auto target_mt = now_ns() - 30LL * static_cast(NANOS_PER_SECOND); + + struct timespec spec[2]{ + ts_make(0, UTIME_OMIT), + ts_make(static_cast(target_mt / + static_cast(NANOS_PER_SECOND)), + static_cast(target_mt % + static_cast(NANOS_PER_SECOND)))}; + + errno = 0; + ASSERT_EQ(0, ::utimensat(AT_FDCWD, src.c_str(), spec, 0)); + + long long at_after{}; + long long mt_after{}; + get_times_ns(src, at_after, mt_after); + + EXPECT_LE(llabsll(mt_after - target_mt), GRANULAR_TOL_NS); + EXPECT_LE(llabsll(at_after - at_before), GRANULAR_TOL_NS); + + this->unlink_file_and_test(src); +} + +TYPED_TEST(fuse_test, utimens_set_now_for_both) { + std::string name{"utimens"}; + auto src = this->create_file_and_test(name); + + struct timespec spec[2]{ts_make(0, UTIME_NOW), ts_make(0, UTIME_NOW)}; + + errno = 0; + ASSERT_EQ(0, ::utimensat(AT_FDCWD, src.c_str(), spec, 0)); + + auto now_after = now_ns(); + long long access_time{}; + long long modified_time{}; + get_times_ns(src, access_time, modified_time); + + EXPECT_LE(llabsll(access_time - now_after), NOW_TOL_NS); + EXPECT_LE(llabsll(modified_time - now_after), NOW_TOL_NS); + + this->unlink_file_and_test(src); +} + +TYPED_TEST(fuse_test, utimens_nonexistent_path_returns_enoent) { + std::string missing = + this->mount_location + "/utimens_missing_" + std::to_string(::getpid()); + + struct timespec spec[2]{ts_make(123, 0), ts_make(456, 0)}; + errno = 0; + EXPECT_EQ(-1, ::utimensat(AT_FDCWD, missing.c_str(), spec, 0)); + EXPECT_EQ(ENOENT, errno); +} + +TYPED_TEST(fuse_test, utimens_invalid_nsec_returns_einval) { + std::string name{"utimens"}; + auto src = this->create_file_and_test(name); + + struct timespec spec[2]{ts_make(0, 1000000000L), ts_make(0, 0)}; + + errno = 0; + EXPECT_EQ(-1, ::utimensat(AT_FDCWD, src.c_str(), spec, 0)); + EXPECT_EQ(EINVAL, errno); + + this->unlink_file_and_test(src); +} + +TYPED_TEST(fuse_test, futimens_set_both_times_specific_values) { + std::string name{"futimens"}; + auto src = this->create_file_and_test(name); + + auto desc = ::open(src.c_str(), O_RDWR); + ASSERT_NE(desc, -1); + + auto now = now_ns(); + auto target_at = + now - 7200LL * static_cast(NANOS_PER_SECOND) + 333333333LL; + auto target_mt = + now - 600LL * static_cast(NANOS_PER_SECOND) + 444444444LL; + + struct timespec spec[2]{ + ts_make(static_cast(target_at / + static_cast(NANOS_PER_SECOND)), + static_cast(target_at % + static_cast(NANOS_PER_SECOND))), + ts_make(static_cast(target_mt / + static_cast(NANOS_PER_SECOND)), + static_cast(target_mt % + static_cast(NANOS_PER_SECOND)))}; + + errno = 0; + ASSERT_EQ(0, ::futimens(desc, spec)); + ::close(desc); + + long long access_time{}; + long long modified_time{}; + get_times_ns(src, access_time, modified_time); + EXPECT_LE(llabsll(access_time - target_at), GRANULAR_TOL_NS); + EXPECT_LE(llabsll(modified_time - target_mt), GRANULAR_TOL_NS); + + this->unlink_file_and_test(src); +} + +TYPED_TEST(fuse_test, futimens_set_mtime_only_omit_atime) { + std::string name{"futimens"}; + auto src = this->create_file_and_test(name); + + long long at_before{}; + long long mt_before{}; + get_times_ns(src, at_before, mt_before); + + auto desc = ::open(src.c_str(), O_RDWR); + ASSERT_NE(desc, -1); + + auto target_mt = now_ns() - 20LL * static_cast(NANOS_PER_SECOND); + + struct timespec spec[2]{ + ts_make(0, UTIME_OMIT), + ts_make(static_cast(target_mt / + static_cast(NANOS_PER_SECOND)), + static_cast(target_mt % + static_cast(NANOS_PER_SECOND)))}; + + errno = 0; + ASSERT_EQ(0, ::futimens(desc, spec)); + ::close(desc); + + long long at_after{}; + long long mt_after{}; + get_times_ns(src, at_after, mt_after); + + EXPECT_LE(llabsll(mt_after - target_mt), GRANULAR_TOL_NS); + EXPECT_LE(llabsll(at_after - at_before), GRANULAR_TOL_NS); + + this->unlink_file_and_test(src); +} + +TYPED_TEST(fuse_test, futimens_invalid_nsec_returns_einval) { + std::string name{"futimens"}; + auto src = this->create_file_and_test(name); + + auto desc = ::open(src.c_str(), O_RDWR); + ASSERT_NE(desc, -1); + + struct timespec spec[2]{ts_make(0, 0), ts_make(0, 1000000000L)}; + + errno = 0; + EXPECT_EQ(-1, ::futimens(desc, spec)); + EXPECT_EQ(EINVAL, errno); + + ::close(desc); + this->unlink_file_and_test(src); +} +} // namespace repertory + +#endif // !defined(_WIN32) From 0c7532e28b71bd0fd7211a1e6df5b8633cf6f560 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 20 Sep 2025 16:34:02 -0500 Subject: [PATCH 084/136] [unit test] Complete FUSE unit tests #22 --- .../src/fuse_drive_fallocate_test.cpp | 11 ++ .../fuse_drive_truncate_ftruncate_test.cpp | 1 - .../src/fuse_drive_utimens_futimens_test.cpp | 117 ++++++++++-------- .../encryption_read_encrypted_range_test.cpp | 6 +- 4 files changed, 78 insertions(+), 57 deletions(-) diff --git a/repertory/repertory_test/src/fuse_drive_fallocate_test.cpp b/repertory/repertory_test/src/fuse_drive_fallocate_test.cpp index ae71858c..4a54025b 100644 --- a/repertory/repertory_test/src/fuse_drive_fallocate_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_fallocate_test.cpp @@ -57,6 +57,11 @@ TYPED_TEST(fuse_test, fallocate_basic_preallocation_platform_semantics) { EXPECT_EQ(0, st_unix.st_size); #else // !defined(__APPLE__) auto res = ::posix_fallocate(desc, off, len); + if (res == EOPNOTSUPP) { + ::close(desc); + this->unlink_file_and_test(src); + return; + } EXPECT_EQ(0, res); struct stat st_unix{}; @@ -90,6 +95,12 @@ TYPED_TEST(fuse_test, fallocate_then_ftruncate_makes_size_visible) { store.fst_flags = F_ALLOCATEALL; res = ::fcntl(desc, F_PREALLOCATE, &store); } + if (res == EOPNOTSUPP) { + ::close(desc); + this->unlink_file_and_test(src); + return; + } + EXPECT_EQ(0, res); EXPECT_EQ(0, ::ftruncate(desc, len)); #else // !defined(__APPLE__) diff --git a/repertory/repertory_test/src/fuse_drive_truncate_ftruncate_test.cpp b/repertory/repertory_test/src/fuse_drive_truncate_ftruncate_test.cpp index f7d834ea..3a7bcdff 100644 --- a/repertory/repertory_test/src/fuse_drive_truncate_ftruncate_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_truncate_ftruncate_test.cpp @@ -96,7 +96,6 @@ TYPED_TEST(fuse_test, truncate_fails_if_file_is_read_only) { if (res == -1 && errno == EROFS) { ASSERT_EQ(0, ::chmod(src.c_str(), 0644)); this->unlink_file_and_test(src); - GTEST_SKIP(); return; } diff --git a/repertory/repertory_test/src/fuse_drive_utimens_futimens_test.cpp b/repertory/repertory_test/src/fuse_drive_utimens_futimens_test.cpp index 30346577..20c37a9e 100644 --- a/repertory/repertory_test/src/fuse_drive_utimens_futimens_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_utimens_futimens_test.cpp @@ -47,37 +47,32 @@ void get_times_ns(const std::string &path, long long &at_ns, long long &mt_ns) { #endif // defined(__APPLE__) } -auto ts_make(time_t sec, long nsec) -> struct timespec { - struct timespec spec{}; - spec.tv_sec = sec; - spec.tv_nsec = nsec; - return spec; +[[nodiscard]] auto ts_make(time_t sec, long nsec) -> timespec { + return timespec{ + .tv_sec = sec, + .tv_nsec = nsec, + }; } -[[nodiscard]] auto -to_ns(const struct timespec &spec) -> long long { +[[nodiscard]] auto to_ns(const timespec &spec) -> long long { return static_cast(spec.tv_sec) * static_cast(NANOS_PER_SECOND) + static_cast(spec.tv_nsec); } [[nodiscard]] auto now_ns() -> long long { - struct timespec spec{}; + timespec spec{}; #if defined(CLOCK_REALTIME) clock_gettime(CLOCK_REALTIME, &spec); -#else - struct timeval val{}; +#else // defined(CLOCK_REALTIME) + timeval val{}; gettimeofday(&val, nullptr); spec.tv_sec = val.tv_sec; spec.tv_nsec = val.tv_usec * 1000; -#endif +#endif // !defined(CLOCK_REALTIME) return to_ns(spec); } -[[nodiscard]] auto llabsll(long long value) -> long long { - return (value < 0) ? -value : value; -} - constexpr long long GRANULAR_TOL_NS = 1LL * static_cast(NANOS_PER_SECOND); constexpr long long NOW_TOL_NS = 5LL * static_cast(NANOS_PER_SECOND); @@ -102,7 +97,7 @@ TYPED_TEST(fuse_test, utimens_set_both_times_specific_values) { auto target_mt = now - 1800LL * static_cast(NANOS_PER_SECOND) + 222222222LL; - struct timespec spec[2]{ + auto spec = std::array{ ts_make(static_cast(target_at / static_cast(NANOS_PER_SECOND)), static_cast(target_at % @@ -110,17 +105,18 @@ TYPED_TEST(fuse_test, utimens_set_both_times_specific_values) { ts_make(static_cast(target_mt / static_cast(NANOS_PER_SECOND)), static_cast(target_mt % - static_cast(NANOS_PER_SECOND)))}; + static_cast(NANOS_PER_SECOND))), + }; errno = 0; - ASSERT_EQ(0, ::utimensat(AT_FDCWD, src.c_str(), spec, 0)); + ASSERT_EQ(0, ::utimensat(AT_FDCWD, src.c_str(), spec.data(), 0)); long long at1{}; long long mt1{}; get_times_ns(src, at1, mt1); - EXPECT_LE(llabsll(at1 - target_at), GRANULAR_TOL_NS); - EXPECT_LE(llabsll(mt1 - target_mt), GRANULAR_TOL_NS); + EXPECT_LE(std::abs(at1 - target_at), GRANULAR_TOL_NS); + EXPECT_LE(std::abs(mt1 - target_mt), GRANULAR_TOL_NS); this->unlink_file_and_test(src); } @@ -136,22 +132,23 @@ TYPED_TEST(fuse_test, utimens_set_atime_only_omit_mtime) { long long target_at = now_ns() - 10LL * static_cast(NANOS_PER_SECOND); - struct timespec spec[2]{ + auto spec = std::array{ ts_make(static_cast(target_at / static_cast(NANOS_PER_SECOND)), static_cast(target_at % static_cast(NANOS_PER_SECOND))), - ts_make(0, UTIME_OMIT)}; + ts_make(0, UTIME_OMIT), + }; errno = 0; - ASSERT_EQ(0, ::utimensat(AT_FDCWD, src.c_str(), spec, 0)); + ASSERT_EQ(0, ::utimensat(AT_FDCWD, src.c_str(), spec.data(), 0)); long long at_after{}; long long mt_after{}; get_times_ns(src, at_after, mt_after); - EXPECT_LE(llabsll(at_after - target_at), GRANULAR_TOL_NS); - EXPECT_LE(llabsll(mt_after - mt_before), GRANULAR_TOL_NS); + EXPECT_LE(std::abs(at_after - target_at), GRANULAR_TOL_NS); + EXPECT_LE(std::abs(mt_after - mt_before), GRANULAR_TOL_NS); this->unlink_file_and_test(src); } @@ -166,22 +163,23 @@ TYPED_TEST(fuse_test, utimens_set_mtime_only_omit_atime) { auto target_mt = now_ns() - 30LL * static_cast(NANOS_PER_SECOND); - struct timespec spec[2]{ + auto spec = std::array{ ts_make(0, UTIME_OMIT), ts_make(static_cast(target_mt / static_cast(NANOS_PER_SECOND)), static_cast(target_mt % - static_cast(NANOS_PER_SECOND)))}; + static_cast(NANOS_PER_SECOND))), + }; errno = 0; - ASSERT_EQ(0, ::utimensat(AT_FDCWD, src.c_str(), spec, 0)); + ASSERT_EQ(0, ::utimensat(AT_FDCWD, src.c_str(), spec.data(), 0)); long long at_after{}; long long mt_after{}; get_times_ns(src, at_after, mt_after); - EXPECT_LE(llabsll(mt_after - target_mt), GRANULAR_TOL_NS); - EXPECT_LE(llabsll(at_after - at_before), GRANULAR_TOL_NS); + EXPECT_LE(std::abs(mt_after - target_mt), GRANULAR_TOL_NS); + EXPECT_LE(std::abs(at_after - at_before), GRANULAR_TOL_NS); this->unlink_file_and_test(src); } @@ -190,29 +188,36 @@ TYPED_TEST(fuse_test, utimens_set_now_for_both) { std::string name{"utimens"}; auto src = this->create_file_and_test(name); - struct timespec spec[2]{ts_make(0, UTIME_NOW), ts_make(0, UTIME_NOW)}; + auto spec = std::array{ + ts_make(0, UTIME_NOW), + ts_make(0, UTIME_NOW), + }; errno = 0; - ASSERT_EQ(0, ::utimensat(AT_FDCWD, src.c_str(), spec, 0)); + ASSERT_EQ(0, ::utimensat(AT_FDCWD, src.c_str(), spec.data(), 0)); auto now_after = now_ns(); long long access_time{}; long long modified_time{}; get_times_ns(src, access_time, modified_time); - EXPECT_LE(llabsll(access_time - now_after), NOW_TOL_NS); - EXPECT_LE(llabsll(modified_time - now_after), NOW_TOL_NS); + EXPECT_LE(std::abs(access_time - now_after), NOW_TOL_NS); + EXPECT_LE(std::abs(modified_time - now_after), NOW_TOL_NS); this->unlink_file_and_test(src); } TYPED_TEST(fuse_test, utimens_nonexistent_path_returns_enoent) { - std::string missing = - this->mount_location + "/utimens_missing_" + std::to_string(::getpid()); + std::string file_name{"utimens"}; + auto missing = this->create_file_path(file_name); + + auto spec = std::array{ + ts_make(123, 0), + ts_make(456, 0), + }; - struct timespec spec[2]{ts_make(123, 0), ts_make(456, 0)}; errno = 0; - EXPECT_EQ(-1, ::utimensat(AT_FDCWD, missing.c_str(), spec, 0)); + EXPECT_EQ(-1, ::utimensat(AT_FDCWD, missing.c_str(), spec.data(), 0)); EXPECT_EQ(ENOENT, errno); } @@ -220,10 +225,13 @@ TYPED_TEST(fuse_test, utimens_invalid_nsec_returns_einval) { std::string name{"utimens"}; auto src = this->create_file_and_test(name); - struct timespec spec[2]{ts_make(0, 1000000000L), ts_make(0, 0)}; + auto spec = std::array{ + ts_make(0, 1000000000L), + ts_make(0, 0), + }; errno = 0; - EXPECT_EQ(-1, ::utimensat(AT_FDCWD, src.c_str(), spec, 0)); + EXPECT_EQ(-1, ::utimensat(AT_FDCWD, src.c_str(), spec.data(), 0)); EXPECT_EQ(EINVAL, errno); this->unlink_file_and_test(src); @@ -242,7 +250,7 @@ TYPED_TEST(fuse_test, futimens_set_both_times_specific_values) { auto target_mt = now - 600LL * static_cast(NANOS_PER_SECOND) + 444444444LL; - struct timespec spec[2]{ + auto spec = std::array{ ts_make(static_cast(target_at / static_cast(NANOS_PER_SECOND)), static_cast(target_at % @@ -250,17 +258,18 @@ TYPED_TEST(fuse_test, futimens_set_both_times_specific_values) { ts_make(static_cast(target_mt / static_cast(NANOS_PER_SECOND)), static_cast(target_mt % - static_cast(NANOS_PER_SECOND)))}; + static_cast(NANOS_PER_SECOND))), + }; errno = 0; - ASSERT_EQ(0, ::futimens(desc, spec)); + ASSERT_EQ(0, ::futimens(desc, spec.data())); ::close(desc); long long access_time{}; long long modified_time{}; get_times_ns(src, access_time, modified_time); - EXPECT_LE(llabsll(access_time - target_at), GRANULAR_TOL_NS); - EXPECT_LE(llabsll(modified_time - target_mt), GRANULAR_TOL_NS); + EXPECT_LE(std::abs(access_time - target_at), GRANULAR_TOL_NS); + EXPECT_LE(std::abs(modified_time - target_mt), GRANULAR_TOL_NS); this->unlink_file_and_test(src); } @@ -278,23 +287,24 @@ TYPED_TEST(fuse_test, futimens_set_mtime_only_omit_atime) { auto target_mt = now_ns() - 20LL * static_cast(NANOS_PER_SECOND); - struct timespec spec[2]{ + auto spec = std::array{ ts_make(0, UTIME_OMIT), ts_make(static_cast(target_mt / static_cast(NANOS_PER_SECOND)), static_cast(target_mt % - static_cast(NANOS_PER_SECOND)))}; + static_cast(NANOS_PER_SECOND))), + }; errno = 0; - ASSERT_EQ(0, ::futimens(desc, spec)); + ASSERT_EQ(0, ::futimens(desc, spec.data())); ::close(desc); long long at_after{}; long long mt_after{}; get_times_ns(src, at_after, mt_after); - EXPECT_LE(llabsll(mt_after - target_mt), GRANULAR_TOL_NS); - EXPECT_LE(llabsll(at_after - at_before), GRANULAR_TOL_NS); + EXPECT_LE(std::abs(mt_after - target_mt), GRANULAR_TOL_NS); + EXPECT_LE(std::abs(at_after - at_before), GRANULAR_TOL_NS); this->unlink_file_and_test(src); } @@ -306,10 +316,13 @@ TYPED_TEST(fuse_test, futimens_invalid_nsec_returns_einval) { auto desc = ::open(src.c_str(), O_RDWR); ASSERT_NE(desc, -1); - struct timespec spec[2]{ts_make(0, 0), ts_make(0, 1000000000L)}; + auto spec = std::array{ + ts_make(0, 0), + ts_make(0, 1000000000L), + }; errno = 0; - EXPECT_EQ(-1, ::futimens(desc, spec)); + EXPECT_EQ(-1, ::futimens(desc, spec.data())); EXPECT_EQ(EINVAL, errno); ::close(desc); diff --git a/support/test/src/utils/encryption_read_encrypted_range_test.cpp b/support/test/src/utils/encryption_read_encrypted_range_test.cpp index 56e35603..1536365f 100644 --- a/support/test/src/utils/encryption_read_encrypted_range_test.cpp +++ b/support/test/src/utils/encryption_read_encrypted_range_test.cpp @@ -336,11 +336,9 @@ TEST_P(utils_encryption_read_encrypted_range_fixture, single_byte_read) { TEST_P(utils_encryption_read_encrypted_range_fixture, begin_at_exact_chunk_boundary) { - if (chunk == 0U) { - GTEST_SKIP() << "chunk size is zero (unexpected)"; - } + ASSERT_NE(chunk, 0U); - std::uint64_t begin = static_cast(chunk); + auto begin = static_cast(chunk); std::uint64_t end = begin + 1024U - 1U; if (end >= plain_sz) end = static_cast(plain_sz) - 1U; From 52e673a9eee38ace5a49ebc0e23d8742388bac82 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 20 Sep 2025 16:39:18 -0500 Subject: [PATCH 085/136] refactor tests --- .../src/fuse_drive_utimens_futimens_test.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/repertory/repertory_test/src/fuse_drive_utimens_futimens_test.cpp b/repertory/repertory_test/src/fuse_drive_utimens_futimens_test.cpp index 20c37a9e..063ef0ad 100644 --- a/repertory/repertory_test/src/fuse_drive_utimens_futimens_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_utimens_futimens_test.cpp @@ -23,9 +23,9 @@ #include "fixtures/drive_fixture.hpp" -namespace { using repertory::utils::time::NANOS_PER_SECOND; +namespace { void get_times_ns(const std::string &path, long long &at_ns, long long &mt_ns) { struct stat st_unix{}; ASSERT_EQ(0, ::stat(path.c_str(), &st_unix)); @@ -47,7 +47,7 @@ void get_times_ns(const std::string &path, long long &at_ns, long long &mt_ns) { #endif // defined(__APPLE__) } -[[nodiscard]] auto ts_make(time_t sec, long nsec) -> timespec { +[[nodiscard]] auto ts_make(time_t sec, long long nsec) -> timespec { return timespec{ .tv_sec = sec, .tv_nsec = nsec, @@ -226,7 +226,7 @@ TYPED_TEST(fuse_test, utimens_invalid_nsec_returns_einval) { auto src = this->create_file_and_test(name); auto spec = std::array{ - ts_make(0, 1000000000L), + ts_make(0, static_cast(NANOS_PER_SECOND)), ts_make(0, 0), }; @@ -318,7 +318,7 @@ TYPED_TEST(fuse_test, futimens_invalid_nsec_returns_einval) { auto spec = std::array{ ts_make(0, 0), - ts_make(0, 1000000000L), + ts_make(0, static_cast(NANOS_PER_SECOND)), }; errno = 0; From d4aba9305110585d446bf3bc2379d4e90284c52a Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 20 Sep 2025 17:26:14 -0500 Subject: [PATCH 086/136] [unit test] Complete FUSE unit tests #22 --- .../include/fixtures/drive_fixture.hpp | 14 +++ .../src/fuse_drive_directory_test.cpp | 118 ++++++++++++++++++ .../src/fuse_drive_fallocate_test.cpp | 5 + 3 files changed, 137 insertions(+) create mode 100644 repertory/repertory_test/src/fuse_drive_directory_test.cpp diff --git a/repertory/repertory_test/include/fixtures/drive_fixture.hpp b/repertory/repertory_test/include/fixtures/drive_fixture.hpp index 9d82ad2c..9aecd61d 100644 --- a/repertory/repertory_test/include/fixtures/drive_fixture.hpp +++ b/repertory/repertory_test/include/fixtures/drive_fixture.hpp @@ -547,6 +547,20 @@ public: << ") failed: " << std::strerror(errno); return st_unix.st_size; } + + static auto read_dirnames(DIR *dir) -> std::set { + std::set names; + while (auto *entry = ::readdir(dir)) { + const auto *name = entry->d_name; + if (std::strcmp(name, ".") == 0 || std::strcmp(name, "..") == 0) { + continue; + } + names.emplace(name); + } + + return names; + } + #endif // !defined(_WIN32) }; diff --git a/repertory/repertory_test/src/fuse_drive_directory_test.cpp b/repertory/repertory_test/src/fuse_drive_directory_test.cpp new file mode 100644 index 00000000..88e2cdeb --- /dev/null +++ b/repertory/repertory_test/src/fuse_drive_directory_test.cpp @@ -0,0 +1,118 @@ +/* + Copyright <2018-2025> + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ +#if !defined(_WIN32) + +#include "fixtures/drive_fixture.hpp" + +namespace repertory { +TYPED_TEST_SUITE(fuse_test, platform_provider_types); + +TYPED_TEST(fuse_test, directory_can_read_empty_directory) { + std::string dir_name{"directory"}; + auto dir = this->create_directory_and_test(dir_name); + + auto *dir_ptr = ::opendir(dir.c_str()); + ASSERT_NE(dir_ptr, nullptr); + + auto names = this->read_dirnames(dir_ptr); + EXPECT_TRUE(names.empty()); + + EXPECT_EQ(0, ::closedir(dir_ptr)); + this->rmdir_and_test(dir); +} + +TYPED_TEST(fuse_test, directory_can_read_populated_directory) { + std::string dir_name{"directory"}; + auto dir = this->create_directory_and_test(dir_name); + + auto file_name_1{dir_name + "/file_a"}; + auto src_1 = this->create_file_and_test(file_name_1); + + auto file_name_2{dir_name + "/file_b"}; + auto src_2 = this->create_file_and_test(file_name_2); + + auto sub_dir_name{dir_name + "/subdir_a"}; + auto sub_dir = this->create_directory_and_test(sub_dir_name); + + auto *dir_ptr = ::opendir(dir.c_str()); + ASSERT_NE(dir_ptr, nullptr); + + auto names = this->read_dirnames(dir_ptr); + + EXPECT_TRUE(names.contains(utils::path::strip_to_file_name(src_1))); + EXPECT_TRUE(names.contains(utils::path::strip_to_file_name(src_2))); + EXPECT_TRUE(names.contains(utils::path::strip_to_file_name(sub_dir))); + + ::rewinddir(dir_ptr); + auto names2 = this->read_dirnames(dir_ptr); + EXPECT_EQ(names, names2); + + EXPECT_EQ(0, ::closedir(dir_ptr)); + + this->unlink_file_and_test(src_1); + this->unlink_file_and_test(src_2); + this->rmdir_and_test(sub_dir); + this->rmdir_and_test(dir); +} + +TYPED_TEST(fuse_test, directory_opendir_fails_for_file) { + std::string file_name{"directory"}; + auto src = this->create_file_and_test(file_name); + + errno = 0; + auto *dir_ptr = ::opendir(src.c_str()); + EXPECT_EQ(dir_ptr, nullptr); + EXPECT_EQ(errno, ENOTDIR); + + this->unlink_file_and_test(src); +} + +TYPED_TEST(fuse_test, directory_opendir_fails_if_directory_does_not_exist) { + std::string file_name{"directory"}; + auto dir = this->create_file_path(file_name); + + errno = 0; + auto *dir_ptr = ::opendir(dir.c_str()); + EXPECT_EQ(dir_ptr, nullptr); + EXPECT_EQ(errno, ENOENT); +} + +TYPED_TEST(fuse_test, directory_can_opendir_after_closedir) { + std::string dir_name{"directory"}; + auto dir = this->create_directory_and_test(dir_name); + + auto *dir_ptr = ::opendir(dir.c_str()); + ASSERT_NE(dir_ptr, nullptr); + + (void)this->read_dirnames(dir_ptr); + + EXPECT_EQ(0, ::closedir(dir_ptr)); + + dir_ptr = ::opendir(dir.c_str()); + ASSERT_NE(dir_ptr, nullptr); + EXPECT_EQ(0, ::closedir(dir_ptr)); + + this->rmdir_and_test(dir); +} +} // namespace repertory + +#endif // !defined(_WIN32) diff --git a/repertory/repertory_test/src/fuse_drive_fallocate_test.cpp b/repertory/repertory_test/src/fuse_drive_fallocate_test.cpp index 4a54025b..321d1791 100644 --- a/repertory/repertory_test/src/fuse_drive_fallocate_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_fallocate_test.cpp @@ -105,6 +105,11 @@ TYPED_TEST(fuse_test, fallocate_then_ftruncate_makes_size_visible) { EXPECT_EQ(0, ::ftruncate(desc, len)); #else // !defined(__APPLE__) auto res = ::posix_fallocate(desc, 0, len); + if (res == EOPNOTSUPP) { + ::close(desc); + this->unlink_file_and_test(src); + return; + } EXPECT_EQ(0, res); EXPECT_EQ(0, ::ftruncate(desc, len / 2)); EXPECT_EQ(0, ::ftruncate(desc, len)); From 75093c2b815c3da991ddee8f50ed74e10aff5b34 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 20 Sep 2025 20:03:56 -0500 Subject: [PATCH 087/136] fix intermintent hang on remote server disconnect --- .../include/comm/packet/packet_client.hpp | 14 +- repertory/librepertory/include/common.hpp | 2 +- .../events/types/packet_client_timeout.hpp | 14 +- .../librepertory/include/types/remote.hpp | 15 +- .../librepertory/include/types/repertory.hpp | 1 + repertory/librepertory/src/app_config.cpp | 10 + .../src/comm/packet/packet_client.cpp | 286 ++++++++++++++---- 7 files changed, 258 insertions(+), 84 deletions(-) diff --git a/repertory/librepertory/include/comm/packet/packet_client.hpp b/repertory/librepertory/include/comm/packet/packet_client.hpp index ff47896d..8099794e 100644 --- a/repertory/librepertory/include/comm/packet/packet_client.hpp +++ b/repertory/librepertory/include/comm/packet/packet_client.hpp @@ -47,7 +47,7 @@ public: auto operator=(packet_client &&) -> packet_client & = delete; private: - boost::asio::io_context io_context_; + mutable boost::asio::io_context io_context_; remote::remote_config cfg_; std::string unique_id_; @@ -69,21 +69,21 @@ private: void put_client(std::shared_ptr &cli); - [[nodiscard]] auto read_packet(client &cli, - packet &response) const -> packet::error_type; + [[nodiscard]] auto read_packet(client &cli, packet &response) const + -> packet::error_type; void resolve(); public: - [[nodiscard]] auto send(std::string_view method, - std::uint32_t &service_flags) -> packet::error_type; + [[nodiscard]] auto send(std::string_view method, std::uint32_t &service_flags) + -> packet::error_type; [[nodiscard]] auto send(std::string_view method, packet &request, std::uint32_t &service_flags) -> packet::error_type; [[nodiscard]] auto send(std::string_view method, packet &request, - packet &response, - std::uint32_t &service_flags) -> packet::error_type; + packet &response, std::uint32_t &service_flags) + -> packet::error_type; }; } // namespace repertory diff --git a/repertory/librepertory/include/common.hpp b/repertory/librepertory/include/common.hpp index 8c7549da..5991e7ac 100644 --- a/repertory/librepertory/include/common.hpp +++ b/repertory/librepertory/include/common.hpp @@ -58,7 +58,7 @@ inline constexpr std::string_view REPERTORY{"repertory"}; inline constexpr std::string_view REPERTORY_DATA_NAME{"repertory2"}; inline constexpr std::wstring_view REPERTORY_W{L"repertory"}; -inline constexpr std::uint64_t REPERTORY_CONFIG_VERSION{2ULL}; +inline constexpr std::uint64_t REPERTORY_CONFIG_VERSION{3ULL}; inline constexpr std::string_view REPERTORY_MIN_REMOTE_VERSION{"2.1.0"}; inline constexpr std::string_view RENTERD_MIN_VERSION{"2.0.0"}; diff --git a/repertory/librepertory/include/events/types/packet_client_timeout.hpp b/repertory/librepertory/include/events/types/packet_client_timeout.hpp index 1c36b242..ff58753b 100644 --- a/repertory/librepertory/include/events/types/packet_client_timeout.hpp +++ b/repertory/librepertory/include/events/types/packet_client_timeout.hpp @@ -28,11 +28,10 @@ namespace repertory { struct packet_client_timeout final : public i_event { packet_client_timeout() = default; - packet_client_timeout(std::string event_name_, - std::string_view function_name_, std::string msg_) - : event_name(std::move(event_name_)), - function_name(std::string(function_name_)), - msg(std::move(msg_)) {} + packet_client_timeout(std::string_view event_name_, + std::string_view function_name_) + : event_name(std::string(event_name_)), + function_name(std::string(function_name_)) {} static constexpr event_level level{event_level::warn}; static constexpr std::string_view name{"packet_client_timeout"}; @@ -50,8 +49,7 @@ struct packet_client_timeout final : public i_event { } [[nodiscard]] auto get_single_line() const -> std::string override { - return fmt::format("{}|func|{}|event|{}|msg|{}", name, function_name, - event_name, msg); + return fmt::format("{}|func|{}|event|{}", name, function_name, event_name); } }; } // namespace repertory @@ -62,14 +60,12 @@ template <> struct adl_serializer { const repertory::packet_client_timeout &value) { data["event_name"] = value.event_name; data["function_name"] = value.function_name; - data["msg"] = value.msg; } static void from_json(const json &data, repertory::packet_client_timeout &value) { data.at("event_name").get_to(value.event_name); data.at("function_name").get_to(value.function_name); - data.at("msg").get_to(value.msg); } }; NLOHMANN_JSON_NAMESPACE_END diff --git a/repertory/librepertory/include/types/remote.hpp b/repertory/librepertory/include/types/remote.hpp index 0bc92b40..9cbd3fd8 100644 --- a/repertory/librepertory/include/types/remote.hpp +++ b/repertory/librepertory/include/types/remote.hpp @@ -33,24 +33,27 @@ inline constexpr auto PACKET_SERVICE_FLAGS{PACKET_SERVICE_WINFSP}; inline constexpr auto PACKET_SERVICE_FLAGS{PACKET_SERVICE_FUSE}; #endif // defined(_WIN32) -inline constexpr auto default_remote_directory_page_size{std::size_t(100U)}; inline constexpr auto default_remote_client_pool_size{20U}; +inline constexpr auto default_remote_conn_timeout_ms{500U}; +inline constexpr auto default_remote_directory_page_size{std::size_t(100U)}; inline constexpr auto default_remote_max_connections{20U}; -inline constexpr auto default_remote_receive_timeout_ms{120U * 1000U}; -inline constexpr auto default_remote_send_timeout_ms{30U * 1000U}; +inline constexpr auto default_remote_recv_timeout_ms{500U}; +inline constexpr auto default_remote_send_timeout_ms{250U}; namespace repertory::remote { struct remote_config final { std::uint16_t api_port{}; + std::uint32_t conn_timeout_ms{default_remote_conn_timeout_ms}; std::string encryption_token; std::string host_name_or_ip; std::uint8_t max_connections{default_remote_max_connections}; - std::uint32_t recv_timeout_ms{default_remote_receive_timeout_ms}; + std::uint32_t recv_timeout_ms{default_remote_recv_timeout_ms}; std::uint32_t send_timeout_ms{default_remote_send_timeout_ms}; auto operator==(const remote_config &cfg) const noexcept -> bool { if (&cfg != this) { return api_port == cfg.api_port && + conn_timeout_ms == cfg.conn_timeout_ms && encryption_token == cfg.encryption_token && host_name_or_ip == cfg.host_name_or_ip && max_connections == cfg.max_connections && @@ -228,6 +231,7 @@ template <> struct adl_serializer { static void to_json(json &data, const repertory::remote::remote_config &value) { data[repertory::JSON_API_PORT] = value.api_port; + data[repertory::JSON_CONNECT_TIMEOUT_MS] = value.api_port; data[repertory::JSON_ENCRYPTION_TOKEN] = value.encryption_token; data[repertory::JSON_HOST_NAME_OR_IP] = value.host_name_or_ip; data[repertory::JSON_MAX_CONNECTIONS] = value.max_connections; @@ -238,6 +242,9 @@ template <> struct adl_serializer { static void from_json(const json &data, repertory::remote::remote_config &value) { data.at(repertory::JSON_API_PORT).get_to(value.api_port); + if (data.contains(repertory::JSON_CONNECT_TIMEOUT_MS)) { + data.at(repertory::JSON_CONNECT_TIMEOUT_MS).get_to(value.conn_timeout_ms); + } data.at(repertory::JSON_ENCRYPTION_TOKEN).get_to(value.encryption_token); data.at(repertory::JSON_HOST_NAME_OR_IP).get_to(value.host_name_or_ip); data.at(repertory::JSON_MAX_CONNECTIONS).get_to(value.max_connections); diff --git a/repertory/librepertory/include/types/repertory.hpp b/repertory/librepertory/include/types/repertory.hpp index d2f37890..26cf8d54 100644 --- a/repertory/librepertory/include/types/repertory.hpp +++ b/repertory/librepertory/include/types/repertory.hpp @@ -404,6 +404,7 @@ inline constexpr auto JSON_API_USER{"ApiUser"}; inline constexpr auto JSON_AUTO_START{"AutoStart"}; inline constexpr auto JSON_BUCKET{"Bucket"}; inline constexpr auto JSON_CLIENT_POOL_SIZE{"ClientPoolSize"}; +inline constexpr auto JSON_CONNECT_TIMEOUT_MS{"ConnectTimeoutMs"}; inline constexpr auto JSON_DATABASE_TYPE{"DatabaseType"}; inline constexpr auto JSON_DIRECTORY{"Directory"}; inline constexpr auto JSON_DOWNLOAD_TIMEOUT_SECS{"DownloadTimeoutSeconds"}; diff --git a/repertory/librepertory/src/app_config.cpp b/repertory/librepertory/src/app_config.cpp index 01a7e1da..04e4de2e 100644 --- a/repertory/librepertory/src/app_config.cpp +++ b/repertory/librepertory/src/app_config.cpp @@ -1097,6 +1097,16 @@ auto app_config::load() -> bool { } } + if (version_ == 3U) { + if (json_document.contains(JSON_REMOTE_CONFIG)) { + auto cfg = get_remote_config(); + cfg.conn_timeout_ms = default_remote_conn_timeout_ms; + cfg.recv_timeout_ms = default_remote_recv_timeout_ms; + cfg.send_timeout_ms = default_remote_send_timeout_ms; + set_remote_config(cfg); + } + } + found = false; } diff --git a/repertory/librepertory/src/comm/packet/packet_client.cpp b/repertory/librepertory/src/comm/packet/packet_client.cpp index b71ba98e..bb24cc31 100644 --- a/repertory/librepertory/src/comm/packet/packet_client.cpp +++ b/repertory/librepertory/src/comm/packet/packet_client.cpp @@ -28,9 +28,196 @@ #include "utils/collection.hpp" #include "utils/common.hpp" #include "utils/error_utils.hpp" -#include "utils/timeout.hpp" #include "version.hpp" +namespace { +namespace net = boost::asio; + +constexpr std::uint8_t max_attempts{3U}; + +struct non_blocking_guard final { + net::ip::tcp::socket &sock; + bool non_blocking{}; + + non_blocking_guard(const non_blocking_guard &) = delete; + non_blocking_guard(non_blocking_guard &&) = delete; + + auto operator=(const non_blocking_guard &) -> non_blocking_guard & = delete; + auto operator=(non_blocking_guard &&) -> non_blocking_guard & = delete; + + explicit non_blocking_guard(net::ip::tcp::socket &sock_) + : sock(sock_), non_blocking(sock_.non_blocking()) { + boost::system::error_code err; + [[maybe_unused]] auto ret = sock_.non_blocking(true, err); + } + + ~non_blocking_guard() { + if (not sock.is_open()) { + return; + } + + boost::system::error_code err; + [[maybe_unused]] auto ret = sock.non_blocking(non_blocking, err); + } +}; + +[[nodiscard]] auto is_socket_still_alive(boost::asio::ip::tcp::socket &sock) + -> bool { + if (not sock.is_open()) { + return false; + } + + non_blocking_guard guard{sock}; + + boost::system::error_code err{}; + std::array tmp{}; + auto available = sock.receive(boost::asio::buffer(tmp), + boost::asio::socket_base::message_peek, err); + + if (err == boost::asio::error::would_block || + err == boost::asio::error::try_again) { + return true; + } + + if (err == boost::asio::error::eof || + err == boost::asio::error::connection_reset || + err == boost::asio::error::operation_aborted || + err == boost::asio::error::not_connected || + err == boost::asio::error::bad_descriptor || + err == boost::asio::error::network_down) { + return false; + } + + if (not err && available == 0) { + return false; + } + + if (not err && available > 0) { + return true; + } + + return false; +} + +template +void run_with_deadline(net::io_context &io_ctx, op_t &&operation, + cancel_t &&cancel_op, std::chrono::milliseconds deadline, + std::string_view timeout_event_tag, + std::string_view op_name, + std::string_view function_name) { + deadline = std::max(deadline / max_attempts, 250ms); + + boost::system::error_code err{}; + bool done = false; + bool timed_out = false; + net::steady_timer timer{io_ctx}; + timer.expires_after(deadline); + timer.async_wait([&](const boost::system::error_code &err_) { + if (not err_) { + timed_out = true; + std::forward(cancel_op)(); + } + }); + + std::forward(operation)([&](const boost::system::error_code &err_) { + err = err_; + done = true; + }); + + io_ctx.restart(); + while (not done && not timed_out) { + io_ctx.run_one(); + } + timer.cancel(); + + if (timed_out) { + repertory::event_system::instance().raise( + timeout_event_tag, function_name); + throw std::runtime_error(std::string(op_name) + " timed-out"); + } + + if (err) { + throw std::runtime_error(std::string(op_name) + " failed|" + err.message()); + } +} + +void connect_with_deadline(net::io_context &io_ctx, + boost::asio::ip::tcp::socket &sock, + const auto &endpoints, + std::chrono::milliseconds deadline) { + REPERTORY_USES_FUNCTION_NAME(); + + run_with_deadline( + io_ctx, + [&](auto &&handler) { + net::async_connect(sock, endpoints, + [handler = std::forward(handler)]( + const boost::system::error_code &err, + const auto &) { handler(err); }); + }, + [&]() { sock.cancel(); }, deadline, "connect", "connect", function_name); +} + +void read_exact_with_deadline(net::io_context &io_ctx, + boost::asio::ip::tcp::socket &sock, auto buf, + std::chrono::milliseconds deadline) { + REPERTORY_USES_FUNCTION_NAME(); + + auto *base = static_cast(const_cast(buf.data())); + std::size_t total = buf.size(); + std::size_t offset = 0U; + + while (offset < total) { + std::size_t bytes_read = 0U; + + run_with_deadline( + io_ctx, + [&](auto &&handler) { + sock.async_read_some( + net::buffer(base + offset, total - offset), + [&, handler = std::forward(handler)]( + const boost::system::error_code &err, std::size_t count) { + bytes_read = count; + handler(err); + }); + }, + [&]() { sock.cancel(); }, deadline, "response", "read", function_name); + + offset += bytes_read; + } +} + +void write_all_with_deadline(net::io_context &io_ctx, + boost::asio::ip::tcp::socket &sock, auto buf, + std::chrono::milliseconds deadline) { + REPERTORY_USES_FUNCTION_NAME(); + + auto *base = static_cast(const_cast(buf.data())); + std::size_t total = buf.size(); + std::size_t offset = 0U; + + while (offset < total) { + std::size_t bytes_written = 0U; + + run_with_deadline( + io_ctx, + // start one chunk write + [&](auto &&handler) { + sock.async_write_some( + net::buffer(base + offset, total - offset), + [&, handler = std::forward(handler)]( + const boost::system::error_code &err, std::size_t count) { + bytes_written = count; + handler(err); + }); + }, + [&]() { sock.cancel(); }, deadline, "request", "write", function_name); + + offset += bytes_written; + } +} +} // namespace + namespace repertory { packet_client::packet_client(remote::remote_config cfg) : cfg_(std::move(cfg)), unique_id_(utils::create_uuid_string()) {} @@ -64,11 +251,12 @@ void packet_client::close_all() { } void packet_client::connect(client &cli) { - REPERTORY_USES_FUNCTION_NAME(); - try { resolve(); - boost::asio::connect(cli.socket, resolve_results_); + + connect_with_deadline(io_context_, cli.socket, resolve_results_, + std::chrono::milliseconds(cfg_.send_timeout_ms)); + cli.socket.set_option(boost::asio::ip::tcp::no_delay(true)); cli.socket.set_option(boost::asio::socket_base::linger(false, 0)); cli.socket.set_option(boost::asio::socket_base::keep_alive(true)); @@ -78,33 +266,42 @@ void packet_client::connect(client &cli) { if (res != 0) { throw std::runtime_error(std::to_string(res)); } - } catch (const std::exception &e) { - utils::error::raise_error(function_name, e, "connection handshake failed"); + } catch (...) { + close(cli); resolve_results_ = {}; + throw; } } auto packet_client::get_client() -> std::shared_ptr { + REPERTORY_USES_FUNCTION_NAME(); + unique_mutex_lock clients_lock(clients_mutex_); if (not allow_connections_) { return nullptr; } - if (clients_.empty()) { - clients_lock.unlock(); + try { + if (clients_.empty()) { + clients_lock.unlock(); - auto cli = std::make_shared(io_context_); - connect(*cli); + auto cli = std::make_shared(io_context_); + connect(*cli); + return cli; + } + + auto cli = clients_.at(0U); + utils::collection::remove_element(clients_, cli); return cli; + } catch (const std::exception &e) { + utils::error::raise_error(function_name, e, "connection handshake failed"); } - auto cli = clients_.at(0U); - utils::collection::remove_element(clients_, cli); - return cli; + return nullptr; } void packet_client::put_client(std::shared_ptr &cli) { - if (not cli->socket.is_open()) { + if (not cli || not is_socket_still_alive(cli->socket)) { return; } @@ -117,32 +314,21 @@ void packet_client::put_client(std::shared_ptr &cli) { auto packet_client::read_packet(client &cli, packet &response) const -> packet::error_type { data_buffer buffer(sizeof(std::uint32_t)); - const auto read_buffer = [&]() { - std::uint32_t offset{}; - while (offset < buffer.size()) { - auto bytes_read = boost::asio::read( - cli.socket, - boost::asio::buffer(&buffer.at(offset), buffer.size() - offset)); - if (bytes_read <= 0) { - throw std::runtime_error("read failed|" + std::to_string(bytes_read)); - } - offset += static_cast(bytes_read); - } - }; - read_buffer(); + read_exact_with_deadline(io_context_, cli.socket, boost::asio::buffer(buffer), + std::chrono::milliseconds(cfg_.recv_timeout_ms)); auto size = boost::endian::big_to_native( *reinterpret_cast(buffer.data())); buffer.resize(size); - read_buffer(); + read_exact_with_deadline(io_context_, cli.socket, boost::asio::buffer(buffer), + std::chrono::milliseconds(cfg_.recv_timeout_ms)); response = std::move(buffer); auto ret = response.decrypt(cfg_.encryption_token); if (ret == 0) { ret = response.decode(cli.nonce); } - return ret; } @@ -181,47 +367,20 @@ auto packet_client::send(std::string_view method, packet &request, request.encode_top(PACKET_SERVICE_FLAGS); request.encode_top(std::string{project_get_version()}); - static constexpr std::uint8_t max_attempts{5U}; - for (std::uint8_t i = 1U; - allow_connections_ && not success && (i <= max_attempts); i++) { + for (std::uint8_t retry = 1U; + allow_connections_ && not success && (retry <= max_attempts); ++retry) { auto current_client = get_client(); if (current_client) { try { request.encode_top(current_client->nonce); request.encrypt(cfg_.encryption_token); - timeout request_timeout( - [method, current_client]() { - event_system::instance().raise( - "request", function_name, std::string{method}); - packet_client::close(*current_client); - }, + write_all_with_deadline( + io_context_, current_client->socket, + boost::asio::buffer(&request[0], request.get_size()), std::chrono::milliseconds(cfg_.send_timeout_ms)); - std::uint32_t offset{}; - while (offset < request.get_size()) { - auto bytes_written = boost::asio::write( - current_client->socket, - boost::asio::buffer(&request[offset], - request.get_size() - offset)); - if (bytes_written <= 0) { - throw std::runtime_error("write failed|" + - std::to_string(bytes_written)); - } - offset += static_cast(bytes_written); - } - request_timeout.disable(); - - timeout response_timeout( - [method, current_client]() { - event_system::instance().raise( - "response", function_name, std::string{method}); - packet_client::close(*current_client); - }, - std::chrono::milliseconds(cfg_.recv_timeout_ms)); - ret = read_packet(*current_client, response); - response_timeout.disable(); if (ret == 0) { ret = response.decode(service_flags); if (ret == 0) { @@ -236,8 +395,9 @@ auto packet_client::send(std::string_view method, packet &request, } } catch (const std::exception &e) { utils::error::raise_error(function_name, e, "send failed"); - close_all(); - if (allow_connections_ && (i < max_attempts)) { + + close(*current_client); + if (allow_connections_ && (retry < max_attempts)) { std::this_thread::sleep_for(1s); } } From cef993cf06f316b242b8456927bc10a902b53efd Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 20 Sep 2025 20:10:09 -0500 Subject: [PATCH 088/136] updated changelog.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6959c9a..1369c448 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ ### Changes from v2.0.7-release * Fixed handling of `FALLOC_FL_KEEP_SIZE` on Linux +* Fixed intermittent client hang on remote mount server disconnect ## v2.0.7-release From 8c31589e5d21a68b9b17cd1ef22fabb7168ac1bb Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 20 Sep 2025 20:51:46 -0500 Subject: [PATCH 089/136] fix intermintent hang on remote server disconnect --- repertory/librepertory/include/common.hpp | 2 +- .../librepertory/include/types/remote.hpp | 6 ++--- repertory/librepertory/src/app_config.cpp | 2 +- .../src/comm/packet/packet_client.cpp | 26 +++++++++++++------ 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/repertory/librepertory/include/common.hpp b/repertory/librepertory/include/common.hpp index 5991e7ac..0f58e0ee 100644 --- a/repertory/librepertory/include/common.hpp +++ b/repertory/librepertory/include/common.hpp @@ -58,7 +58,7 @@ inline constexpr std::string_view REPERTORY{"repertory"}; inline constexpr std::string_view REPERTORY_DATA_NAME{"repertory2"}; inline constexpr std::wstring_view REPERTORY_W{L"repertory"}; -inline constexpr std::uint64_t REPERTORY_CONFIG_VERSION{3ULL}; +inline constexpr std::uint64_t REPERTORY_CONFIG_VERSION{4ULL}; inline constexpr std::string_view REPERTORY_MIN_REMOTE_VERSION{"2.1.0"}; inline constexpr std::string_view RENTERD_MIN_VERSION{"2.0.0"}; diff --git a/repertory/librepertory/include/types/remote.hpp b/repertory/librepertory/include/types/remote.hpp index 9cbd3fd8..e9da87c6 100644 --- a/repertory/librepertory/include/types/remote.hpp +++ b/repertory/librepertory/include/types/remote.hpp @@ -34,11 +34,11 @@ inline constexpr auto PACKET_SERVICE_FLAGS{PACKET_SERVICE_FUSE}; #endif // defined(_WIN32) inline constexpr auto default_remote_client_pool_size{20U}; -inline constexpr auto default_remote_conn_timeout_ms{500U}; +inline constexpr auto default_remote_conn_timeout_ms{3000U}; inline constexpr auto default_remote_directory_page_size{std::size_t(100U)}; inline constexpr auto default_remote_max_connections{20U}; -inline constexpr auto default_remote_recv_timeout_ms{500U}; -inline constexpr auto default_remote_send_timeout_ms{250U}; +inline constexpr auto default_remote_recv_timeout_ms{3000U}; +inline constexpr auto default_remote_send_timeout_ms{1500U}; namespace repertory::remote { struct remote_config final { diff --git a/repertory/librepertory/src/app_config.cpp b/repertory/librepertory/src/app_config.cpp index 04e4de2e..8f10b73e 100644 --- a/repertory/librepertory/src/app_config.cpp +++ b/repertory/librepertory/src/app_config.cpp @@ -1097,7 +1097,7 @@ auto app_config::load() -> bool { } } - if (version_ == 3U) { + if (version_ == 3U || version_ == 4U) { if (json_document.contains(JSON_REMOTE_CONFIG)) { auto cfg = get_remote_config(); cfg.conn_timeout_ms = default_remote_conn_timeout_ms; diff --git a/repertory/librepertory/src/comm/packet/packet_client.cpp b/repertory/librepertory/src/comm/packet/packet_client.cpp index bb24cc31..e3ad26ac 100644 --- a/repertory/librepertory/src/comm/packet/packet_client.cpp +++ b/repertory/librepertory/src/comm/packet/packet_client.cpp @@ -105,15 +105,16 @@ void run_with_deadline(net::io_context &io_ctx, op_t &&operation, std::string_view timeout_event_tag, std::string_view op_name, std::string_view function_name) { - deadline = std::max(deadline / max_attempts, 250ms); + deadline = std::max(deadline, std::chrono::milliseconds{250}); boost::system::error_code err{}; bool done = false; bool timed_out = false; + net::steady_timer timer{io_ctx}; timer.expires_after(deadline); timer.async_wait([&](const boost::system::error_code &err_) { - if (not err_) { + if (not err_ && not done) { timed_out = true; std::forward(cancel_op)(); } @@ -125,14 +126,14 @@ void run_with_deadline(net::io_context &io_ctx, op_t &&operation, }); io_ctx.restart(); - while (not done && not timed_out) { + while (not done) { io_ctx.run_one(); } timer.cancel(); if (timed_out) { repertory::event_system::instance().raise( - timeout_event_tag, function_name); + std::string(timeout_event_tag), std::string(function_name)); throw std::runtime_error(std::string(op_name) + " timed-out"); } @@ -183,6 +184,10 @@ void read_exact_with_deadline(net::io_context &io_ctx, }, [&]() { sock.cancel(); }, deadline, "response", "read", function_name); + if (bytes_read == 0U) { + throw std::runtime_error("0 bytes read"); + } + offset += bytes_read; } } @@ -201,7 +206,6 @@ void write_all_with_deadline(net::io_context &io_ctx, run_with_deadline( io_ctx, - // start one chunk write [&](auto &&handler) { sock.async_write_some( net::buffer(base + offset, total - offset), @@ -213,6 +217,10 @@ void write_all_with_deadline(net::io_context &io_ctx, }, [&]() { sock.cancel(); }, deadline, "request", "write", function_name); + if (bytes_written == 0U) { + throw std::runtime_error("0 bytes written"); + } + offset += bytes_written; } } @@ -255,7 +263,7 @@ void packet_client::connect(client &cli) { resolve(); connect_with_deadline(io_context_, cli.socket, resolve_results_, - std::chrono::milliseconds(cfg_.send_timeout_ms)); + std::chrono::milliseconds(cfg_.conn_timeout_ms)); cli.socket.set_option(boost::asio::ip::tcp::no_delay(true)); cli.socket.set_option(boost::asio::socket_base::linger(false, 0)); @@ -317,8 +325,10 @@ auto packet_client::read_packet(client &cli, packet &response) const read_exact_with_deadline(io_context_, cli.socket, boost::asio::buffer(buffer), std::chrono::milliseconds(cfg_.recv_timeout_ms)); - auto size = boost::endian::big_to_native( - *reinterpret_cast(buffer.data())); + std::uint32_t size_be = 0U; + std::memcpy(&size_be, buffer.data(), sizeof(size_be)); + const std::uint32_t size = boost::endian::big_to_native(size_be); + buffer.resize(size); read_exact_with_deadline(io_context_, cli.socket, boost::asio::buffer(buffer), From 5cfa87df5d9a8179b1ea424875e47c0888fcbb32 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 20 Sep 2025 20:53:17 -0500 Subject: [PATCH 090/136] fix intermintent hang on remote server disconnect --- repertory/librepertory/src/comm/packet/packet_client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repertory/librepertory/src/comm/packet/packet_client.cpp b/repertory/librepertory/src/comm/packet/packet_client.cpp index e3ad26ac..3a565929 100644 --- a/repertory/librepertory/src/comm/packet/packet_client.cpp +++ b/repertory/librepertory/src/comm/packet/packet_client.cpp @@ -33,7 +33,7 @@ namespace { namespace net = boost::asio; -constexpr std::uint8_t max_attempts{3U}; +constexpr std::uint8_t max_attempts{5U}; struct non_blocking_guard final { net::ip::tcp::socket &sock; From bee018a56342d1695c6d377b25f98d7d0f0fc33c Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 20 Sep 2025 20:55:54 -0500 Subject: [PATCH 091/136] stop server --- repertory/repertory/src/ui/ui_server.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/repertory/repertory/src/ui/ui_server.cpp b/repertory/repertory/src/ui/ui_server.cpp index 2dc2460b..5f1b0c90 100644 --- a/repertory/repertory/src/ui/ui_server.cpp +++ b/repertory/repertory/src/ui/ui_server.cpp @@ -999,6 +999,7 @@ void ui_server::stop() { stop_requested = true; notify_and_unlock(nonce_lock); + server_.stop(); nonce_thread_->join(); nonce_lock.lock(); From 8e3c7d4eeb5170de1a2097231132ed5140d75efe Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 20 Sep 2025 21:06:00 -0500 Subject: [PATCH 092/136] fix intermintent hang on remote server disconnect --- repertory/librepertory/include/common.hpp | 2 +- repertory/librepertory/include/types/remote.hpp | 6 +++--- repertory/librepertory/src/app_config.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/repertory/librepertory/include/common.hpp b/repertory/librepertory/include/common.hpp index 0f58e0ee..2ba70703 100644 --- a/repertory/librepertory/include/common.hpp +++ b/repertory/librepertory/include/common.hpp @@ -58,7 +58,7 @@ inline constexpr std::string_view REPERTORY{"repertory"}; inline constexpr std::string_view REPERTORY_DATA_NAME{"repertory2"}; inline constexpr std::wstring_view REPERTORY_W{L"repertory"}; -inline constexpr std::uint64_t REPERTORY_CONFIG_VERSION{4ULL}; +inline constexpr std::uint64_t REPERTORY_CONFIG_VERSION{5ULL}; inline constexpr std::string_view REPERTORY_MIN_REMOTE_VERSION{"2.1.0"}; inline constexpr std::string_view RENTERD_MIN_VERSION{"2.0.0"}; diff --git a/repertory/librepertory/include/types/remote.hpp b/repertory/librepertory/include/types/remote.hpp index e9da87c6..69bb5f6a 100644 --- a/repertory/librepertory/include/types/remote.hpp +++ b/repertory/librepertory/include/types/remote.hpp @@ -37,8 +37,8 @@ inline constexpr auto default_remote_client_pool_size{20U}; inline constexpr auto default_remote_conn_timeout_ms{3000U}; inline constexpr auto default_remote_directory_page_size{std::size_t(100U)}; inline constexpr auto default_remote_max_connections{20U}; -inline constexpr auto default_remote_recv_timeout_ms{3000U}; -inline constexpr auto default_remote_send_timeout_ms{1500U}; +inline constexpr auto default_remote_recv_timeout_ms{6000U}; +inline constexpr auto default_remote_send_timeout_ms{6000U}; namespace repertory::remote { struct remote_config final { @@ -231,7 +231,7 @@ template <> struct adl_serializer { static void to_json(json &data, const repertory::remote::remote_config &value) { data[repertory::JSON_API_PORT] = value.api_port; - data[repertory::JSON_CONNECT_TIMEOUT_MS] = value.api_port; + data[repertory::JSON_CONNECT_TIMEOUT_MS] = value.conn_timeout_ms; data[repertory::JSON_ENCRYPTION_TOKEN] = value.encryption_token; data[repertory::JSON_HOST_NAME_OR_IP] = value.host_name_or_ip; data[repertory::JSON_MAX_CONNECTIONS] = value.max_connections; diff --git a/repertory/librepertory/src/app_config.cpp b/repertory/librepertory/src/app_config.cpp index 8f10b73e..4dbf201b 100644 --- a/repertory/librepertory/src/app_config.cpp +++ b/repertory/librepertory/src/app_config.cpp @@ -1097,7 +1097,7 @@ auto app_config::load() -> bool { } } - if (version_ == 3U || version_ == 4U) { + if (version_ >= 3U && version_ <= 5U) { if (json_document.contains(JSON_REMOTE_CONFIG)) { auto cfg = get_remote_config(); cfg.conn_timeout_ms = default_remote_conn_timeout_ms; From 41ecd3339c5f0b5ee2ac2e3926ec8383e9da8dc5 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 20 Sep 2025 21:13:02 -0500 Subject: [PATCH 093/136] fix intermintent hang on remote server disconnect --- .../repertory_test/src/app_config_test.cpp | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/repertory/repertory_test/src/app_config_test.cpp b/repertory/repertory_test/src/app_config_test.cpp index f491f2f8..699b14c5 100644 --- a/repertory/repertory_test/src/app_config_test.cpp +++ b/repertory/repertory_test/src/app_config_test.cpp @@ -484,14 +484,16 @@ static void common_tests(app_config &config, provider_type prov) { remote_cfg1.max_connections = 4U; remote_cfg1.recv_timeout_ms = 5U; remote_cfg1.send_timeout_ms = 6U; + remote_cfg1.conn_timeout_ms = 7U; remote::remote_config remote_cfg2{}; - remote_cfg1.api_port = 6U; - remote_cfg1.encryption_token = "5"; - remote_cfg1.host_name_or_ip = "4"; - remote_cfg1.max_connections = 3U; - remote_cfg1.recv_timeout_ms = 2U; - remote_cfg1.send_timeout_ms = 1U; + remote_cfg1.api_port = 7U; + remote_cfg1.encryption_token = "6"; + remote_cfg1.host_name_or_ip = "6"; + remote_cfg1.max_connections = 4U; + remote_cfg1.recv_timeout_ms = 3U; + remote_cfg1.send_timeout_ms = 2U; + remote_cfg1.conn_timeout_ms = 1U; ASSERT_NE(remote_cfg1, remote_cfg2); @@ -506,6 +508,7 @@ static void common_tests(app_config &config, provider_type prov) { remote_cfg1.max_connections = 10U; remote_cfg1.recv_timeout_ms = 11U; remote_cfg1.send_timeout_ms = 12U; + remote_cfg1.conn_timeout_ms = 13U; auto value = cfg.set_value_by_name( fmt::format("{}.{}", JSON_REMOTE_CONFIG, JSON_API_PORT), @@ -513,6 +516,12 @@ static void common_tests(app_config &config, provider_type prov) { EXPECT_STREQ(std::to_string(remote_cfg3.api_port).c_str(), value.c_str()); + value = cfg.set_value_by_name( + fmt::format("{}.{}", JSON_REMOTE_CONFIG, JSON_CONNECT_TIMEOUT_MS), + std::to_string(remote_cfg3.conn_timeout_ms)); + EXPECT_STREQ(std::to_string(remote_cfg3.conn_timeout_ms).c_str(), + value.c_str()); + value = cfg.set_value_by_name( fmt::format("{}.{}", JSON_REMOTE_CONFIG, JSON_ENCRYPTION_TOKEN), remote_cfg3.encryption_token); From 8aa7cf40d365dd4e6f67d4b45c722238e57ff5e2 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 20 Sep 2025 21:15:09 -0500 Subject: [PATCH 094/136] fix intermintent hang on remote server disconnect --- repertory/librepertory/src/app_config.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/repertory/librepertory/src/app_config.cpp b/repertory/librepertory/src/app_config.cpp index 4dbf201b..5359be69 100644 --- a/repertory/librepertory/src/app_config.cpp +++ b/repertory/librepertory/src/app_config.cpp @@ -191,6 +191,10 @@ app_config::app_config(provider_type prov, std::string_view data_directory) }}, {fmt::format("{}.{}", JSON_REMOTE_CONFIG, JSON_API_PORT), [this]() { return std::to_string(get_remote_config().api_port); }}, + {fmt::format("{}.{}", JSON_REMOTE_CONFIG, JSON_CONNECT_TIMEOUT_MS), + [this]() { + return std::to_string(get_remote_config().conn_timeout_ms); + }}, {fmt::format("{}.{}", JSON_REMOTE_CONFIG, JSON_ENCRYPTION_TOKEN), [this]() { return get_remote_config().encryption_token; }}, {fmt::format("{}.{}", JSON_REMOTE_CONFIG, JSON_HOST_NAME_OR_IP), @@ -484,6 +488,15 @@ app_config::app_config(provider_type prov, std::string_view data_directory) return std::to_string(get_remote_config().api_port); }, }, + { + fmt::format("{}.{}", JSON_REMOTE_CONFIG, JSON_CONNECT_TIMEOUT_MS), + [this](const std::string &value) { + auto cfg = get_remote_config(); + cfg.conn_timeout_ms = utils::string::to_uint32(value); + set_remote_config(cfg); + return std::to_string(get_remote_config().conn_timeout_ms); + }, + }, { fmt::format("{}.{}", JSON_REMOTE_CONFIG, JSON_ENCRYPTION_TOKEN), [this](const std::string &value) { From 4f5a347d2c6f755d40b5322cfac9e89b02021248 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 20 Sep 2025 21:18:02 -0500 Subject: [PATCH 095/136] fix intermintent hang on remote server disconnect --- repertory/repertory_test/src/json_serialize_test.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/repertory/repertory_test/src/json_serialize_test.cpp b/repertory/repertory_test/src/json_serialize_test.cpp index 70a56549..c240b214 100644 --- a/repertory/repertory_test/src/json_serialize_test.cpp +++ b/repertory/repertory_test/src/json_serialize_test.cpp @@ -115,6 +115,7 @@ TEST(json_serialize_test, can_handle_host_config) { TEST(json_serialize_test, can_handle_remote_config) { remote::remote_config cfg{ .api_port = 1024U, + .conn_timeout_ms = 22U, .encryption_token = "token", .host_name_or_ip = "host", .max_connections = 11U, @@ -124,6 +125,7 @@ TEST(json_serialize_test, can_handle_remote_config) { json data(cfg); EXPECT_EQ(1024U, data.at(JSON_API_PORT).get()); + EXPECT_EQ(22U, data.at(JSON_CONNECT_TIMEOUT_MS).get()); EXPECT_STREQ("token", data.at(JSON_ENCRYPTION_TOKEN).get().c_str()); EXPECT_STREQ("host", @@ -135,6 +137,7 @@ TEST(json_serialize_test, can_handle_remote_config) { { auto cfg2 = data.get(); EXPECT_EQ(cfg2.api_port, cfg.api_port); + EXPECT_EQ(cfg2.conn_timeout_ms, cfg.conn_timeout_ms); EXPECT_STREQ(cfg2.encryption_token.c_str(), cfg.encryption_token.c_str()); EXPECT_STREQ(cfg2.host_name_or_ip.c_str(), cfg.host_name_or_ip.c_str()); EXPECT_EQ(cfg2.max_connections, cfg.max_connections); From b339ee435cb5c90c3cb7f50926749659459c7726 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 20 Sep 2025 21:30:08 -0500 Subject: [PATCH 096/136] added connect timeout --- web/repertory/lib/widgets/mount_settings.dart | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/web/repertory/lib/widgets/mount_settings.dart b/web/repertory/lib/widgets/mount_settings.dart index 816211db..3bd9f4e3 100644 --- a/web/repertory/lib/widgets/mount_settings.dart +++ b/web/repertory/lib/widgets/mount_settings.dart @@ -923,6 +923,23 @@ class _MountSettingsWidgetState extends State { ); } break; + case 'ConnectTimeoutMs': + { + createIntSetting( + context, + remoteConfigSettings, + widget.settings[key], + subKey, + subValue, + true, + widget.showAdvanced, + widget, + setState, + description: getSettingDescription('$key.$subKey'), + validators: getSettingValidators('$key.$subKey'), + ); + } + break; case 'EncryptionToken': { createPasswordSetting( From b0e74a30a230cd6c3736891ce3fb3d7f94678d0a Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 20 Sep 2025 23:30:18 -0500 Subject: [PATCH 097/136] refactor --- .../src/comm/packet/packet_client.cpp | 49 ++++++++++--------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/repertory/librepertory/src/comm/packet/packet_client.cpp b/repertory/librepertory/src/comm/packet/packet_client.cpp index 3a565929..b8a49a00 100644 --- a/repertory/librepertory/src/comm/packet/packet_client.cpp +++ b/repertory/librepertory/src/comm/packet/packet_client.cpp @@ -107,20 +107,21 @@ void run_with_deadline(net::io_context &io_ctx, op_t &&operation, std::string_view function_name) { deadline = std::max(deadline, std::chrono::milliseconds{250}); - boost::system::error_code err{}; bool done = false; bool timed_out = false; net::steady_timer timer{io_ctx}; timer.expires_after(deadline); - timer.async_wait([&](const boost::system::error_code &err_) { - if (not err_ && not done) { - timed_out = true; - std::forward(cancel_op)(); - } - }); + timer.async_wait( + [&cancel_op, &done, &timed_out](const boost::system::error_code &err_) { + if (not err_ && not done) { + timed_out = true; + cancel_op(); + } + }); - std::forward(operation)([&](const boost::system::error_code &err_) { + boost::system::error_code err{}; + operation([&done, &err](const boost::system::error_code &err_) { err = err_; done = true; }); @@ -150,21 +151,22 @@ void connect_with_deadline(net::io_context &io_ctx, run_with_deadline( io_ctx, - [&](auto &&handler) { + [&sock, &endpoints](auto &&handler) { net::async_connect(sock, endpoints, - [handler = std::forward(handler)]( - const boost::system::error_code &err, - const auto &) { handler(err); }); + [handler](auto &&err, auto &&) { handler(err); }); }, - [&]() { sock.cancel(); }, deadline, "connect", "connect", function_name); + [&sock]() { sock.cancel(); }, deadline, "connect", "connect", + function_name); } void read_exact_with_deadline(net::io_context &io_ctx, - boost::asio::ip::tcp::socket &sock, auto buf, + boost::asio::ip::tcp::socket &sock, + boost::asio::mutable_buffer buf, std::chrono::milliseconds deadline) { REPERTORY_USES_FUNCTION_NAME(); - auto *base = static_cast(const_cast(buf.data())); + auto *base = static_cast(buf.data()); + std::size_t total = buf.size(); std::size_t offset = 0U; @@ -176,13 +178,13 @@ void read_exact_with_deadline(net::io_context &io_ctx, [&](auto &&handler) { sock.async_read_some( net::buffer(base + offset, total - offset), - [&, handler = std::forward(handler)]( - const boost::system::error_code &err, std::size_t count) { + [&bytes_read, handler](auto &&err, auto &&count) { bytes_read = count; handler(err); }); }, - [&]() { sock.cancel(); }, deadline, "response", "read", function_name); + [&sock]() { sock.cancel(); }, deadline, "response", "read", + function_name); if (bytes_read == 0U) { throw std::runtime_error("0 bytes read"); @@ -193,11 +195,12 @@ void read_exact_with_deadline(net::io_context &io_ctx, } void write_all_with_deadline(net::io_context &io_ctx, - boost::asio::ip::tcp::socket &sock, auto buf, + boost::asio::ip::tcp::socket &sock, + boost::asio::mutable_buffer buf, std::chrono::milliseconds deadline) { REPERTORY_USES_FUNCTION_NAME(); - auto *base = static_cast(const_cast(buf.data())); + auto *base = static_cast(buf.data()); std::size_t total = buf.size(); std::size_t offset = 0U; @@ -209,13 +212,13 @@ void write_all_with_deadline(net::io_context &io_ctx, [&](auto &&handler) { sock.async_write_some( net::buffer(base + offset, total - offset), - [&, handler = std::forward(handler)]( - const boost::system::error_code &err, std::size_t count) { + [&bytes_written, handler](auto &&err, auto &&count) { bytes_written = count; handler(err); }); }, - [&]() { sock.cancel(); }, deadline, "request", "write", function_name); + [&sock]() { sock.cancel(); }, deadline, "request", "write", + function_name); if (bytes_written == 0U) { throw std::runtime_error("0 bytes written"); From 660cbb44e61baf41ace628e65472c7fbdf7d6bc5 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 20 Sep 2025 23:31:07 -0500 Subject: [PATCH 098/136] refactor --- repertory/librepertory/src/comm/packet/packet_client.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/repertory/librepertory/src/comm/packet/packet_client.cpp b/repertory/librepertory/src/comm/packet/packet_client.cpp index b8a49a00..58e26b54 100644 --- a/repertory/librepertory/src/comm/packet/packet_client.cpp +++ b/repertory/librepertory/src/comm/packet/packet_client.cpp @@ -31,12 +31,10 @@ #include "version.hpp" namespace { -namespace net = boost::asio; - constexpr std::uint8_t max_attempts{5U}; struct non_blocking_guard final { - net::ip::tcp::socket &sock; + boost::asio::ip::tcp::socket &sock; bool non_blocking{}; non_blocking_guard(const non_blocking_guard &) = delete; @@ -45,7 +43,7 @@ struct non_blocking_guard final { auto operator=(const non_blocking_guard &) -> non_blocking_guard & = delete; auto operator=(non_blocking_guard &&) -> non_blocking_guard & = delete; - explicit non_blocking_guard(net::ip::tcp::socket &sock_) + explicit non_blocking_guard(boost::asio::ip::tcp::socket &sock_) : sock(sock_), non_blocking(sock_.non_blocking()) { boost::system::error_code err; [[maybe_unused]] auto ret = sock_.non_blocking(true, err); From 1bdb8cb3816e193c076eab7423f267b2aeb3c883 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 20 Sep 2025 23:32:35 -0500 Subject: [PATCH 099/136] refactor --- .../src/comm/packet/packet_client.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/repertory/librepertory/src/comm/packet/packet_client.cpp b/repertory/librepertory/src/comm/packet/packet_client.cpp index 58e26b54..c7396882 100644 --- a/repertory/librepertory/src/comm/packet/packet_client.cpp +++ b/repertory/librepertory/src/comm/packet/packet_client.cpp @@ -98,7 +98,7 @@ struct non_blocking_guard final { } template -void run_with_deadline(net::io_context &io_ctx, op_t &&operation, +void run_with_deadline(boost::asio::io_context &io_ctx, op_t &&operation, cancel_t &&cancel_op, std::chrono::milliseconds deadline, std::string_view timeout_event_tag, std::string_view op_name, @@ -108,7 +108,7 @@ void run_with_deadline(net::io_context &io_ctx, op_t &&operation, bool done = false; bool timed_out = false; - net::steady_timer timer{io_ctx}; + boost::asio::steady_timer timer{io_ctx}; timer.expires_after(deadline); timer.async_wait( [&cancel_op, &done, &timed_out](const boost::system::error_code &err_) { @@ -141,7 +141,7 @@ void run_with_deadline(net::io_context &io_ctx, op_t &&operation, } } -void connect_with_deadline(net::io_context &io_ctx, +void connect_with_deadline(boost::asio::io_context &io_ctx, boost::asio::ip::tcp::socket &sock, const auto &endpoints, std::chrono::milliseconds deadline) { @@ -150,14 +150,14 @@ void connect_with_deadline(net::io_context &io_ctx, run_with_deadline( io_ctx, [&sock, &endpoints](auto &&handler) { - net::async_connect(sock, endpoints, - [handler](auto &&err, auto &&) { handler(err); }); + boost::asio::async_connect( + sock, endpoints, [handler](auto &&err, auto &&) { handler(err); }); }, [&sock]() { sock.cancel(); }, deadline, "connect", "connect", function_name); } -void read_exact_with_deadline(net::io_context &io_ctx, +void read_exact_with_deadline(boost::asio::io_context &io_ctx, boost::asio::ip::tcp::socket &sock, boost::asio::mutable_buffer buf, std::chrono::milliseconds deadline) { @@ -175,7 +175,7 @@ void read_exact_with_deadline(net::io_context &io_ctx, io_ctx, [&](auto &&handler) { sock.async_read_some( - net::buffer(base + offset, total - offset), + boost::asio::buffer(base + offset, total - offset), [&bytes_read, handler](auto &&err, auto &&count) { bytes_read = count; handler(err); @@ -192,7 +192,7 @@ void read_exact_with_deadline(net::io_context &io_ctx, } } -void write_all_with_deadline(net::io_context &io_ctx, +void write_all_with_deadline(boost::asio::io_context &io_ctx, boost::asio::ip::tcp::socket &sock, boost::asio::mutable_buffer buf, std::chrono::milliseconds deadline) { @@ -209,7 +209,7 @@ void write_all_with_deadline(net::io_context &io_ctx, io_ctx, [&](auto &&handler) { sock.async_write_some( - net::buffer(base + offset, total - offset), + boost::asio::buffer(base + offset, total - offset), [&bytes_written, handler](auto &&err, auto &&count) { bytes_written = count; handler(err); From 40b02dce5a25862bd128456b154648ba05fbb03a Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 20 Sep 2025 23:33:45 -0500 Subject: [PATCH 100/136] refactor --- repertory/librepertory/src/comm/packet/packet_client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repertory/librepertory/src/comm/packet/packet_client.cpp b/repertory/librepertory/src/comm/packet/packet_client.cpp index c7396882..0ba32b29 100644 --- a/repertory/librepertory/src/comm/packet/packet_client.cpp +++ b/repertory/librepertory/src/comm/packet/packet_client.cpp @@ -119,7 +119,7 @@ void run_with_deadline(boost::asio::io_context &io_ctx, op_t &&operation, }); boost::system::error_code err{}; - operation([&done, &err](const boost::system::error_code &err_) { + operation([&done, &err](auto &&err_) { err = err_; done = true; }); From 8c838380db2af51619ca230d8c7e799567bdd896 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 20 Sep 2025 23:34:22 -0500 Subject: [PATCH 101/136] refactor --- .../librepertory/src/comm/packet/packet_client.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/repertory/librepertory/src/comm/packet/packet_client.cpp b/repertory/librepertory/src/comm/packet/packet_client.cpp index 0ba32b29..f0cf3432 100644 --- a/repertory/librepertory/src/comm/packet/packet_client.cpp +++ b/repertory/librepertory/src/comm/packet/packet_client.cpp @@ -110,13 +110,12 @@ void run_with_deadline(boost::asio::io_context &io_ctx, op_t &&operation, boost::asio::steady_timer timer{io_ctx}; timer.expires_after(deadline); - timer.async_wait( - [&cancel_op, &done, &timed_out](const boost::system::error_code &err_) { - if (not err_ && not done) { - timed_out = true; - cancel_op(); - } - }); + timer.async_wait([&cancel_op, &done, &timed_out](auto &&err_) { + if (not err_ && not done) { + timed_out = true; + cancel_op(); + } + }); boost::system::error_code err{}; operation([&done, &err](auto &&err_) { From f070aa6964eb7f5581dca44125ce961e794d9760 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 20 Sep 2025 23:39:49 -0500 Subject: [PATCH 102/136] refactor --- repertory/librepertory/src/comm/packet/packet_server.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/repertory/librepertory/src/comm/packet/packet_server.cpp b/repertory/librepertory/src/comm/packet/packet_server.cpp index d714e7da..cc005ced 100644 --- a/repertory/librepertory/src/comm/packet/packet_server.cpp +++ b/repertory/librepertory/src/comm/packet/packet_server.cpp @@ -54,14 +54,16 @@ packet_server::~packet_server() { event_system::instance().raise(function_name, "packet_server"); - std::thread([this]() { - for (std::size_t i = 0U; i < service_threads_.size(); i++) { + std::thread stop_all([this]() { + for (std::size_t idx = 0U; idx < service_threads_.size(); ++idx) { io_context_.stop(); } - }).detach(); + }); server_thread_->join(); server_thread_.reset(); + stop_all.join(); + event_system::instance().raise(function_name, "packet_server"); } From 79e9e9e3126bbcec5807f210dd7eca3b9f883837 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 20 Sep 2025 23:41:38 -0500 Subject: [PATCH 103/136] refactor --- repertory/librepertory/src/comm/packet/packet_server.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/repertory/librepertory/src/comm/packet/packet_server.cpp b/repertory/librepertory/src/comm/packet/packet_server.cpp index cc005ced..b1ad54e7 100644 --- a/repertory/librepertory/src/comm/packet/packet_server.cpp +++ b/repertory/librepertory/src/comm/packet/packet_server.cpp @@ -72,10 +72,10 @@ void packet_server::add_client(connection &conn, const std::string &client_id) { conn.client_id = client_id; recur_mutex_lock connection_lock(connection_mutex_); - if (connection_lookup_.find(client_id) == connection_lookup_.end()) { - connection_lookup_[client_id] = 1U; - } else { + if (connection_lookup_.contains(client_id)) { connection_lookup_.at(client_id)++; + } else { + connection_lookup_[client_id] = 1U; } } @@ -97,7 +97,7 @@ void packet_server::initialize(const uint16_t &port, uint8_t pool_size) { } listen_for_connection(acceptor); - for (std::uint8_t i = 0U; i < pool_size; i++) { + for (std::uint8_t idx = 0U; idx < pool_size; ++idx) { service_threads_.emplace_back([this]() { io_context_.run(); }); } From b68a392d229e0eccbed332cde2971741cc2cf8c3 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sun, 21 Sep 2025 09:45:30 -0500 Subject: [PATCH 104/136] added handshake for dos protection --- .../include/comm/packet/packet_client.hpp | 2 + .../include/comm/packet/packet_server.hpp | 2 + .../src/comm/packet/packet_client.cpp | 89 ++++++++++++++---- .../src/comm/packet/packet_server.cpp | 91 ++++++++++++++++--- 4 files changed, 151 insertions(+), 33 deletions(-) diff --git a/repertory/librepertory/include/comm/packet/packet_client.hpp b/repertory/librepertory/include/comm/packet/packet_client.hpp index 8099794e..3389ea56 100644 --- a/repertory/librepertory/include/comm/packet/packet_client.hpp +++ b/repertory/librepertory/include/comm/packet/packet_client.hpp @@ -67,6 +67,8 @@ private: [[nodiscard]] auto get_client() -> std::shared_ptr; + [[nodiscard]] auto handshake(client &cli) const -> bool; + void put_client(std::shared_ptr &cli); [[nodiscard]] auto read_packet(client &cli, packet &response) const diff --git a/repertory/librepertory/include/comm/packet/packet_server.hpp b/repertory/librepertory/include/comm/packet/packet_server.hpp index e81fb1ae..ff7a4a12 100644 --- a/repertory/librepertory/include/comm/packet/packet_server.hpp +++ b/repertory/librepertory/include/comm/packet/packet_server.hpp @@ -77,6 +77,8 @@ private: private: void add_client(connection &conn, const std::string &client_id); + [[nodiscard]] auto handshake(std::shared_ptr conn) const -> bool; + void initialize(const uint16_t &port, uint8_t pool_size); void listen_for_connection(tcp::acceptor &acceptor); diff --git a/repertory/librepertory/src/comm/packet/packet_client.cpp b/repertory/librepertory/src/comm/packet/packet_client.cpp index f0cf3432..24ee277a 100644 --- a/repertory/librepertory/src/comm/packet/packet_client.cpp +++ b/repertory/librepertory/src/comm/packet/packet_client.cpp @@ -97,11 +97,11 @@ struct non_blocking_guard final { return false; } -template -void run_with_deadline(boost::asio::io_context &io_ctx, op_t &&operation, - cancel_t &&cancel_op, std::chrono::milliseconds deadline, - std::string_view timeout_event_tag, - std::string_view op_name, +template +void run_with_deadline(boost::asio::io_context &io_ctx, + boost::asio::ip::tcp::socket &sock, op_t &&operation, + std::chrono::milliseconds deadline, + std::string_view event_name, std::string_view function_name) { deadline = std::max(deadline, std::chrono::milliseconds{250}); @@ -110,10 +110,10 @@ void run_with_deadline(boost::asio::io_context &io_ctx, op_t &&operation, boost::asio::steady_timer timer{io_ctx}; timer.expires_after(deadline); - timer.async_wait([&cancel_op, &done, &timed_out](auto &&err_) { + timer.async_wait([&done, &sock, &timed_out](auto &&err_) { if (not err_ && not done) { timed_out = true; - cancel_op(); + sock.cancel(); } }); @@ -131,12 +131,13 @@ void run_with_deadline(boost::asio::io_context &io_ctx, op_t &&operation, if (timed_out) { repertory::event_system::instance().raise( - std::string(timeout_event_tag), std::string(function_name)); - throw std::runtime_error(std::string(op_name) + " timed-out"); + std::string(event_name), std::string(function_name)); + throw std::runtime_error(std::string(event_name) + " timed-out"); } if (err) { - throw std::runtime_error(std::string(op_name) + " failed|" + err.message()); + throw std::runtime_error(std::string(event_name) + " failed|err|" + + err.message()); } } @@ -147,13 +148,12 @@ void connect_with_deadline(boost::asio::io_context &io_ctx, REPERTORY_USES_FUNCTION_NAME(); run_with_deadline( - io_ctx, + io_ctx, sock, [&sock, &endpoints](auto &&handler) { boost::asio::async_connect( sock, endpoints, [handler](auto &&err, auto &&) { handler(err); }); }, - [&sock]() { sock.cancel(); }, deadline, "connect", "connect", - function_name); + deadline, "connect", function_name); } void read_exact_with_deadline(boost::asio::io_context &io_ctx, @@ -171,7 +171,7 @@ void read_exact_with_deadline(boost::asio::io_context &io_ctx, std::size_t bytes_read = 0U; run_with_deadline( - io_ctx, + io_ctx, sock, [&](auto &&handler) { sock.async_read_some( boost::asio::buffer(base + offset, total - offset), @@ -180,8 +180,7 @@ void read_exact_with_deadline(boost::asio::io_context &io_ctx, handler(err); }); }, - [&sock]() { sock.cancel(); }, deadline, "response", "read", - function_name); + deadline, "read", function_name); if (bytes_read == 0U) { throw std::runtime_error("0 bytes read"); @@ -205,7 +204,7 @@ void write_all_with_deadline(boost::asio::io_context &io_ctx, std::size_t bytes_written = 0U; run_with_deadline( - io_ctx, + io_ctx, sock, [&](auto &&handler) { sock.async_write_some( boost::asio::buffer(base + offset, total - offset), @@ -214,8 +213,7 @@ void write_all_with_deadline(boost::asio::io_context &io_ctx, handler(err); }); }, - [&sock]() { sock.cancel(); }, deadline, "request", "write", - function_name); + deadline, "write", function_name); if (bytes_written == 0U) { throw std::runtime_error("0 bytes written"); @@ -269,10 +267,15 @@ void packet_client::connect(client &cli) { cli.socket.set_option(boost::asio::socket_base::linger(false, 0)); cli.socket.set_option(boost::asio::socket_base::keep_alive(true)); + if (not handshake(cli)) { + close(cli); + return; + } + packet response; auto res = read_packet(cli, response); if (res != 0) { - throw std::runtime_error(std::to_string(res)); + throw std::runtime_error(fmt::format("read packet failed|err|{}", res)); } } catch (...) { close(cli); @@ -308,6 +311,52 @@ auto packet_client::get_client() -> std::shared_ptr { return nullptr; } +auto packet_client::handshake(client &cli) const -> bool { + REPERTORY_USES_FUNCTION_NAME(); + + try { + data_buffer buffer; + { + packet tmp; + tmp.encode_top(cli.nonce); + tmp.to_buffer(buffer); + } + auto to_read{buffer.size()}; + + std::uint32_t total_read{}; + while ((total_read < to_read) && cli.socket.is_open()) { + auto bytes_read = boost::asio::read( + cli.socket, + boost::asio::buffer(&buffer[total_read], buffer.size() - total_read)); + if (bytes_read <= 0) { + throw std::runtime_error("0 bytes read"); + } + + total_read += static_cast(bytes_read); + } + + if (total_read == to_read) { + packet response(buffer); + response.encrypt(cfg_.encryption_token); + response.to_buffer(buffer); + + auto written = boost::asio::write( + cli.socket, boost::asio::buffer(boost::asio::buffer(buffer))); + if (written == to_read) { + return true; + } + + throw std::runtime_error("failed to send handshake"); + } + + throw std::runtime_error("failed to read handshake"); + } catch (const std::exception &e) { + repertory::utils::error::raise_error(function_name, e, "handlshake failed"); + } + + return false; +} + void packet_client::put_client(std::shared_ptr &cli) { if (not cli || not is_socket_still_alive(cli->socket)) { return; diff --git a/repertory/librepertory/src/comm/packet/packet_server.cpp b/repertory/librepertory/src/comm/packet/packet_server.cpp index b1ad54e7..5badcab0 100644 --- a/repertory/librepertory/src/comm/packet/packet_server.cpp +++ b/repertory/librepertory/src/comm/packet/packet_server.cpp @@ -79,6 +79,67 @@ void packet_server::add_client(connection &conn, const std::string &client_id) { } } +auto packet_server::handshake(std::shared_ptr conn) const -> bool { + REPERTORY_USES_FUNCTION_NAME(); + + try { + conn->generate_nonce(); + + data_buffer buffer; + packet request; + request.encode_top(conn->nonce); + request.to_buffer(buffer); + auto to_read{buffer.size()}; + + auto written = boost::asio::write( + conn->socket, boost::asio::buffer(boost::asio::buffer(buffer))); + if (written == to_read) { + conn->buffer.resize(to_read); + + std::uint32_t total_read{}; + while ((total_read < to_read) && conn->socket.is_open()) { + auto bytes_read = boost::asio::read( + conn->socket, + boost::asio::buffer(&conn->buffer[total_read], + conn->buffer.size() - total_read)); + if (bytes_read <= 0) { + throw std::runtime_error("0 bytes read"); + } + + total_read += static_cast(bytes_read); + } + + if (total_read == to_read) { + packet response(conn->buffer); + if (response.decrypt(encryption_token_) == 0) { + std::string nonce; + if (response.decode(nonce) == 0) { + if (nonce == conn->nonce) { + conn->generate_nonce(); + return true; + } + + throw std::runtime_error("invalid nonce"); + } + + throw std::runtime_error("invalid nonce"); + } + + throw std::runtime_error("decryption failed"); + } + + throw std::runtime_error("invalid handshake"); + } + + throw std::runtime_error("failed to send handshake"); + } catch (const std::exception &e) { + repertory::utils::error::raise_error(function_name, e, "handlshake failed"); + } + + conn->socket.close(); + return false; +} + void packet_server::initialize(const uint16_t &port, uint8_t pool_size) { REPERTORY_USES_FUNCTION_NAME(); @@ -123,15 +184,19 @@ void packet_server::on_accept(std::shared_ptr conn, if (err) { utils::error::raise_error(function_name, err.message()); std::this_thread::sleep_for(1s); - } else { - conn->socket.set_option(boost::asio::ip::tcp::no_delay(true)); - conn->socket.set_option(boost::asio::socket_base::linger(false, 0)); - - conn->generate_nonce(); - - packet response; - send_response(conn, 0, response); + return; } + + conn->socket.set_option(boost::asio::ip::tcp::no_delay(true)); + conn->socket.set_option(boost::asio::socket_base::linger(false, 0)); + + if (not handshake(conn)) { + conn->socket.close(); + return; + } + + packet response; + send_response(conn, 0, response); } void packet_server::read_header(std::shared_ptr conn) { @@ -145,12 +210,12 @@ void packet_server::read_header(std::shared_ptr conn) { if (err) { remove_client(*conn); repertory::utils::error::raise_error(function_name, err.message()); - } else { - auto to_read = - *reinterpret_cast(conn->buffer.data()); - boost::endian::big_to_native_inplace(to_read); - read_packet(conn, to_read); + return; } + + auto to_read = *reinterpret_cast(conn->buffer.data()); + boost::endian::big_to_native_inplace(to_read); + read_packet(conn, to_read); }); } From ee03167e43b942ca6daf076aac007b2ba134028c Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sun, 21 Sep 2025 10:32:53 -0500 Subject: [PATCH 105/136] added handshake for dos protection --- CHANGELOG.md | 1 + .../include/comm/packet/common.hpp | 77 ++++++ .../include/comm/packet/packet.hpp | 2 +- .../include/comm/packet/packet_server.hpp | 7 +- .../librepertory/src/comm/packet/common.cpp | 191 ++++++++++++++ .../librepertory/src/comm/packet/packet.cpp | 6 +- .../src/comm/packet/packet_client.cpp | 241 ++---------------- .../src/comm/packet/packet_server.cpp | 70 +++-- 8 files changed, 325 insertions(+), 270 deletions(-) create mode 100644 repertory/librepertory/include/comm/packet/common.hpp create mode 100644 repertory/librepertory/src/comm/packet/common.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 1369c448..83d8987b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### BREAKING CHANGES * Remote mounts must be upgraded to v2.1.0+ to support new authentication scheme + * Protocol handshake added for DoS protection ### Issues diff --git a/repertory/librepertory/include/comm/packet/common.hpp b/repertory/librepertory/include/comm/packet/common.hpp new file mode 100644 index 00000000..5638ab58 --- /dev/null +++ b/repertory/librepertory/include/comm/packet/common.hpp @@ -0,0 +1,77 @@ +/* + Copyright <2018-2025> + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ +#ifndef REPERTORY_INCLUDE_COMM_PACKET_COMMON_HPP_ +#define REPERTORY_INCLUDE_COMM_PACKET_COMMON_HPP_ + +#include "utils/common.hpp" + +namespace repertory::comm { +constexpr const std::uint8_t max_read_attempts{5U}; +constexpr const std::uint16_t packet_nonce_size{256U}; + +struct non_blocking_guard final { + boost::asio::ip::tcp::socket &sock; + bool non_blocking{}; + + non_blocking_guard(const non_blocking_guard &) = delete; + non_blocking_guard(non_blocking_guard &&) = delete; + + auto operator=(const non_blocking_guard &) -> non_blocking_guard & = delete; + auto operator=(non_blocking_guard &&) -> non_blocking_guard & = delete; + + explicit non_blocking_guard(boost::asio::ip::tcp::socket &sock_) + : sock(sock_), non_blocking(sock_.non_blocking()) { + boost::system::error_code err; + [[maybe_unused]] auto ret = sock_.non_blocking(true, err); + } + + ~non_blocking_guard() { + if (not sock.is_open()) { + return; + } + + boost::system::error_code err; + [[maybe_unused]] auto ret = sock.non_blocking(non_blocking, err); + } +}; + +[[nodiscard]] auto is_socket_still_alive(boost::asio::ip::tcp::socket &sock) + -> bool; + +void connect_with_deadline( + boost::asio::io_context &io_ctx, boost::asio::ip::tcp::socket &sock, + boost::asio::ip::basic_resolver::results_type + &endpoints, + std::chrono::milliseconds deadline); + +void read_exact_with_deadline(boost::asio::io_context &io_ctx, + boost::asio::ip::tcp::socket &sock, + boost::asio::mutable_buffer buf, + std::chrono::milliseconds deadline); + +void write_all_with_deadline(boost::asio::io_context &io_ctx, + boost::asio::ip::tcp::socket &sock, + boost::asio::mutable_buffer buf, + std::chrono::milliseconds deadline); +} // namespace repertory::comm + +#endif // REPERTORY_INCLUDE_COMM_PACKET_COMMON_HPP_ diff --git a/repertory/librepertory/include/comm/packet/packet.hpp b/repertory/librepertory/include/comm/packet/packet.hpp index 61b5bcbe..32e0ac50 100644 --- a/repertory/librepertory/include/comm/packet/packet.hpp +++ b/repertory/librepertory/include/comm/packet/packet.hpp @@ -200,7 +200,7 @@ public: void encode_top(remote::file_info val); - void encrypt(std::string_view token); + void encrypt(std::string_view token, bool include_size = true); [[nodiscard]] auto get_size() const -> std::uint32_t { return static_cast(buffer_.size()); diff --git a/repertory/librepertory/include/comm/packet/packet_server.hpp b/repertory/librepertory/include/comm/packet/packet_server.hpp index ff7a4a12..cb33c4a1 100644 --- a/repertory/librepertory/include/comm/packet/packet_server.hpp +++ b/repertory/librepertory/include/comm/packet/packet_server.hpp @@ -23,6 +23,7 @@ #define REPERTORY_INCLUDE_COMM_PACKET_PACKET_SERVER_HPP_ #include "comm/packet/client_pool.hpp" +#include "comm/packet/common.hpp" #include "utils/common.hpp" using namespace boost::asio; @@ -61,14 +62,16 @@ private: std::string client_id; std::string nonce; - void generate_nonce() { nonce = utils::generate_random_string(256U); } + void generate_nonce() { + nonce = utils::generate_random_string(comm::packet_nonce_size); + } }; private: std::string encryption_token_; closed_callback closed_; message_handler_callback message_handler_; - io_context io_context_; + mutable io_context io_context_; std::unique_ptr server_thread_; std::vector service_threads_; std::recursive_mutex connection_mutex_; diff --git a/repertory/librepertory/src/comm/packet/common.cpp b/repertory/librepertory/src/comm/packet/common.cpp new file mode 100644 index 00000000..07f5549b --- /dev/null +++ b/repertory/librepertory/src/comm/packet/common.cpp @@ -0,0 +1,191 @@ +/* + Copyright <2018-2025> + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ +#include "comm/packet/common.hpp" + +#include "events/event_system.hpp" +#include "events/types/packet_client_timeout.hpp" + +namespace repertory::comm { +auto is_socket_still_alive(boost::asio::ip::tcp::socket &sock) -> bool { + if (not sock.is_open()) { + return false; + } + + non_blocking_guard guard{sock}; + + boost::system::error_code err{}; + std::array tmp{}; + auto available = sock.receive(boost::asio::buffer(tmp), + boost::asio::socket_base::message_peek, err); + + if (err == boost::asio::error::would_block || + err == boost::asio::error::try_again) { + return true; + } + + if (err == boost::asio::error::eof || + err == boost::asio::error::connection_reset || + err == boost::asio::error::operation_aborted || + err == boost::asio::error::not_connected || + err == boost::asio::error::bad_descriptor || + err == boost::asio::error::network_down) { + return false; + } + + if (not err && available == 0) { + return false; + } + + if (not err && available > 0) { + return true; + } + + return false; +} + +template +void run_with_deadline(boost::asio::io_context &io_ctx, + boost::asio::ip::tcp::socket &sock, op_t &&operation, + std::chrono::milliseconds deadline, + std::string_view event_name, + std::string_view function_name) { + deadline = std::max(deadline, std::chrono::milliseconds{250}); + + bool done = false; + bool timed_out = false; + + boost::asio::steady_timer timer{io_ctx}; + timer.expires_after(deadline); + timer.async_wait([&done, &sock, &timed_out](auto &&err_) { + if (not err_ && not done) { + timed_out = true; + sock.cancel(); + } + }); + + boost::system::error_code err{}; + operation([&done, &err](auto &&err_) { + err = err_; + done = true; + }); + + io_ctx.restart(); + while (not done) { + io_ctx.run_one(); + } + timer.cancel(); + + if (timed_out) { + repertory::event_system::instance().raise( + std::string(event_name), std::string(function_name)); + throw std::runtime_error(std::string(event_name) + " timed-out"); + } + + if (err) { + throw std::runtime_error(std::string(event_name) + " failed|err|" + + err.message()); + } +} + +void connect_with_deadline( + boost::asio::io_context &io_ctx, boost::asio::ip::tcp::socket &sock, + boost::asio::ip::basic_resolver::results_type + &endpoints, + std::chrono::milliseconds deadline) { + REPERTORY_USES_FUNCTION_NAME(); + + run_with_deadline( + io_ctx, sock, + [&sock, &endpoints](auto &&handler) { + boost::asio::async_connect( + sock, endpoints, [handler](auto &&err, auto &&) { handler(err); }); + }, + deadline, "connect", function_name); +} + +void read_exact_with_deadline(boost::asio::io_context &io_ctx, + boost::asio::ip::tcp::socket &sock, + boost::asio::mutable_buffer buf, + std::chrono::milliseconds deadline) { + REPERTORY_USES_FUNCTION_NAME(); + + auto *base = static_cast(buf.data()); + + std::size_t total = buf.size(); + std::size_t offset = 0U; + + while (offset < total) { + std::size_t bytes_read = 0U; + + run_with_deadline( + io_ctx, sock, + [&](auto &&handler) { + sock.async_read_some( + boost::asio::buffer(base + offset, total - offset), + [&bytes_read, handler](auto &&err, auto &&count) { + bytes_read = count; + handler(err); + }); + }, + deadline, "read", function_name); + + if (bytes_read == 0U) { + throw std::runtime_error("0 bytes read"); + } + + offset += bytes_read; + } +} + +void write_all_with_deadline(boost::asio::io_context &io_ctx, + boost::asio::ip::tcp::socket &sock, + boost::asio::mutable_buffer buf, + std::chrono::milliseconds deadline) { + REPERTORY_USES_FUNCTION_NAME(); + + auto *base = static_cast(buf.data()); + std::size_t total = buf.size(); + std::size_t offset = 0U; + + while (offset < total) { + std::size_t bytes_written = 0U; + + run_with_deadline( + io_ctx, sock, + [&](auto &&handler) { + sock.async_write_some( + boost::asio::buffer(base + offset, total - offset), + [&bytes_written, handler](auto &&err, auto &&count) { + bytes_written = count; + handler(err); + }); + }, + deadline, "write", function_name); + + if (bytes_written == 0U) { + throw std::runtime_error("0 bytes written"); + } + + offset += bytes_written; + } +} +} // namespace repertory::comm diff --git a/repertory/librepertory/src/comm/packet/packet.cpp b/repertory/librepertory/src/comm/packet/packet.cpp index 4ff12365..ad458184 100644 --- a/repertory/librepertory/src/comm/packet/packet.cpp +++ b/repertory/librepertory/src/comm/packet/packet.cpp @@ -518,14 +518,16 @@ void packet::encode_top(remote::file_info val) { encode_top(&val, sizeof(val), true); } -void packet::encrypt(std::string_view token) { +void packet::encrypt(std::string_view token, bool include_size) { REPERTORY_USES_FUNCTION_NAME(); try { data_buffer result; utils::encryption::encrypt_data(token, buffer_, result); buffer_ = std::move(result); - encode_top(static_cast(buffer_.size())); + if (include_size) { + encode_top(static_cast(buffer_.size())); + } } catch (const std::exception &e) { utils::error::raise_error(function_name, e, "exception occurred"); } diff --git a/repertory/librepertory/src/comm/packet/packet_client.cpp b/repertory/librepertory/src/comm/packet/packet_client.cpp index 24ee277a..412cc4e0 100644 --- a/repertory/librepertory/src/comm/packet/packet_client.cpp +++ b/repertory/librepertory/src/comm/packet/packet_client.cpp @@ -21,8 +21,7 @@ */ #include "comm/packet/packet_client.hpp" -#include "events/event_system.hpp" -#include "events/types/packet_client_timeout.hpp" +#include "comm/packet/common.hpp" #include "platform/platform.hpp" #include "types/repertory.hpp" #include "utils/collection.hpp" @@ -30,199 +29,7 @@ #include "utils/error_utils.hpp" #include "version.hpp" -namespace { -constexpr std::uint8_t max_attempts{5U}; - -struct non_blocking_guard final { - boost::asio::ip::tcp::socket &sock; - bool non_blocking{}; - - non_blocking_guard(const non_blocking_guard &) = delete; - non_blocking_guard(non_blocking_guard &&) = delete; - - auto operator=(const non_blocking_guard &) -> non_blocking_guard & = delete; - auto operator=(non_blocking_guard &&) -> non_blocking_guard & = delete; - - explicit non_blocking_guard(boost::asio::ip::tcp::socket &sock_) - : sock(sock_), non_blocking(sock_.non_blocking()) { - boost::system::error_code err; - [[maybe_unused]] auto ret = sock_.non_blocking(true, err); - } - - ~non_blocking_guard() { - if (not sock.is_open()) { - return; - } - - boost::system::error_code err; - [[maybe_unused]] auto ret = sock.non_blocking(non_blocking, err); - } -}; - -[[nodiscard]] auto is_socket_still_alive(boost::asio::ip::tcp::socket &sock) - -> bool { - if (not sock.is_open()) { - return false; - } - - non_blocking_guard guard{sock}; - - boost::system::error_code err{}; - std::array tmp{}; - auto available = sock.receive(boost::asio::buffer(tmp), - boost::asio::socket_base::message_peek, err); - - if (err == boost::asio::error::would_block || - err == boost::asio::error::try_again) { - return true; - } - - if (err == boost::asio::error::eof || - err == boost::asio::error::connection_reset || - err == boost::asio::error::operation_aborted || - err == boost::asio::error::not_connected || - err == boost::asio::error::bad_descriptor || - err == boost::asio::error::network_down) { - return false; - } - - if (not err && available == 0) { - return false; - } - - if (not err && available > 0) { - return true; - } - - return false; -} - -template -void run_with_deadline(boost::asio::io_context &io_ctx, - boost::asio::ip::tcp::socket &sock, op_t &&operation, - std::chrono::milliseconds deadline, - std::string_view event_name, - std::string_view function_name) { - deadline = std::max(deadline, std::chrono::milliseconds{250}); - - bool done = false; - bool timed_out = false; - - boost::asio::steady_timer timer{io_ctx}; - timer.expires_after(deadline); - timer.async_wait([&done, &sock, &timed_out](auto &&err_) { - if (not err_ && not done) { - timed_out = true; - sock.cancel(); - } - }); - - boost::system::error_code err{}; - operation([&done, &err](auto &&err_) { - err = err_; - done = true; - }); - - io_ctx.restart(); - while (not done) { - io_ctx.run_one(); - } - timer.cancel(); - - if (timed_out) { - repertory::event_system::instance().raise( - std::string(event_name), std::string(function_name)); - throw std::runtime_error(std::string(event_name) + " timed-out"); - } - - if (err) { - throw std::runtime_error(std::string(event_name) + " failed|err|" + - err.message()); - } -} - -void connect_with_deadline(boost::asio::io_context &io_ctx, - boost::asio::ip::tcp::socket &sock, - const auto &endpoints, - std::chrono::milliseconds deadline) { - REPERTORY_USES_FUNCTION_NAME(); - - run_with_deadline( - io_ctx, sock, - [&sock, &endpoints](auto &&handler) { - boost::asio::async_connect( - sock, endpoints, [handler](auto &&err, auto &&) { handler(err); }); - }, - deadline, "connect", function_name); -} - -void read_exact_with_deadline(boost::asio::io_context &io_ctx, - boost::asio::ip::tcp::socket &sock, - boost::asio::mutable_buffer buf, - std::chrono::milliseconds deadline) { - REPERTORY_USES_FUNCTION_NAME(); - - auto *base = static_cast(buf.data()); - - std::size_t total = buf.size(); - std::size_t offset = 0U; - - while (offset < total) { - std::size_t bytes_read = 0U; - - run_with_deadline( - io_ctx, sock, - [&](auto &&handler) { - sock.async_read_some( - boost::asio::buffer(base + offset, total - offset), - [&bytes_read, handler](auto &&err, auto &&count) { - bytes_read = count; - handler(err); - }); - }, - deadline, "read", function_name); - - if (bytes_read == 0U) { - throw std::runtime_error("0 bytes read"); - } - - offset += bytes_read; - } -} - -void write_all_with_deadline(boost::asio::io_context &io_ctx, - boost::asio::ip::tcp::socket &sock, - boost::asio::mutable_buffer buf, - std::chrono::milliseconds deadline) { - REPERTORY_USES_FUNCTION_NAME(); - - auto *base = static_cast(buf.data()); - std::size_t total = buf.size(); - std::size_t offset = 0U; - - while (offset < total) { - std::size_t bytes_written = 0U; - - run_with_deadline( - io_ctx, sock, - [&](auto &&handler) { - sock.async_write_some( - boost::asio::buffer(base + offset, total - offset), - [&bytes_written, handler](auto &&err, auto &&count) { - bytes_written = count; - handler(err); - }); - }, - deadline, "write", function_name); - - if (bytes_written == 0U) { - throw std::runtime_error("0 bytes written"); - } - - offset += bytes_written; - } -} -} // namespace +using namespace repertory::comm; namespace repertory { packet_client::packet_client(remote::remote_config cfg) @@ -318,38 +125,21 @@ auto packet_client::handshake(client &cli) const -> bool { data_buffer buffer; { packet tmp; - tmp.encode_top(cli.nonce); + tmp.encode_top(utils::generate_random_string(packet_nonce_size)); tmp.to_buffer(buffer); } - auto to_read{buffer.size()}; - std::uint32_t total_read{}; - while ((total_read < to_read) && cli.socket.is_open()) { - auto bytes_read = boost::asio::read( - cli.socket, - boost::asio::buffer(&buffer[total_read], buffer.size() - total_read)); - if (bytes_read <= 0) { - throw std::runtime_error("0 bytes read"); - } + read_exact_with_deadline(io_context_, cli.socket, + boost::asio::buffer(buffer), + std::chrono::milliseconds(cfg_.recv_timeout_ms)); + packet response(buffer); + response.encrypt(cfg_.encryption_token, false); + response.to_buffer(buffer); - total_read += static_cast(bytes_read); - } - - if (total_read == to_read) { - packet response(buffer); - response.encrypt(cfg_.encryption_token); - response.to_buffer(buffer); - - auto written = boost::asio::write( - cli.socket, boost::asio::buffer(boost::asio::buffer(buffer))); - if (written == to_read) { - return true; - } - - throw std::runtime_error("failed to send handshake"); - } - - throw std::runtime_error("failed to read handshake"); + write_all_with_deadline(io_context_, cli.socket, + boost::asio::buffer(buffer), + std::chrono::milliseconds(cfg_.send_timeout_ms)); + return true; } catch (const std::exception &e) { repertory::utils::error::raise_error(function_name, e, "handlshake failed"); } @@ -427,7 +217,8 @@ auto packet_client::send(std::string_view method, packet &request, request.encode_top(std::string{project_get_version()}); for (std::uint8_t retry = 1U; - allow_connections_ && not success && (retry <= max_attempts); ++retry) { + allow_connections_ && not success && (retry <= max_read_attempts); + ++retry) { auto current_client = get_client(); if (current_client) { try { @@ -456,7 +247,7 @@ auto packet_client::send(std::string_view method, packet &request, utils::error::raise_error(function_name, e, "send failed"); close(*current_client); - if (allow_connections_ && (retry < max_attempts)) { + if (allow_connections_ && (retry < max_read_attempts)) { std::this_thread::sleep_for(1s); } } diff --git a/repertory/librepertory/src/comm/packet/packet_server.cpp b/repertory/librepertory/src/comm/packet/packet_server.cpp index 5badcab0..8ecb0a53 100644 --- a/repertory/librepertory/src/comm/packet/packet_server.cpp +++ b/repertory/librepertory/src/comm/packet/packet_server.cpp @@ -21,6 +21,7 @@ */ #include "comm/packet/packet_server.hpp" +#include "comm/packet/common.hpp" #include "comm/packet/packet.hpp" #include "events/event_system.hpp" #include "events/types/service_start_begin.hpp" @@ -31,9 +32,10 @@ #include "types/repertory.hpp" #include "utils/error_utils.hpp" -namespace repertory { +using namespace repertory::comm; using std::thread; +namespace repertory { packet_server::packet_server(std::uint16_t port, std::string token, std::uint8_t pool_size, closed_callback closed, message_handler_callback message_handler) @@ -89,49 +91,30 @@ auto packet_server::handshake(std::shared_ptr conn) const -> bool { packet request; request.encode_top(conn->nonce); request.to_buffer(buffer); - auto to_read{buffer.size()}; + auto to_read{buffer.size() + utils::encryption::encryption_header_size}; - auto written = boost::asio::write( - conn->socket, boost::asio::buffer(boost::asio::buffer(buffer))); - if (written == to_read) { - conn->buffer.resize(to_read); + write_all_with_deadline(io_context_, conn->socket, + boost::asio::buffer(buffer), + std::chrono::milliseconds(3000U)); - std::uint32_t total_read{}; - while ((total_read < to_read) && conn->socket.is_open()) { - auto bytes_read = boost::asio::read( - conn->socket, - boost::asio::buffer(&conn->buffer[total_read], - conn->buffer.size() - total_read)); - if (bytes_read <= 0) { - throw std::runtime_error("0 bytes read"); + conn->buffer.resize(to_read); + read_exact_with_deadline(io_context_, conn->socket, + boost::asio::buffer(conn->buffer), + std::chrono::milliseconds(3000U)); + packet response(conn->buffer); + if (response.decrypt(encryption_token_) == 0) { + std::string nonce; + if (response.decode(nonce) == 0) { + if (nonce == conn->nonce) { + conn->generate_nonce(); + return true; } - - total_read += static_cast(bytes_read); } - if (total_read == to_read) { - packet response(conn->buffer); - if (response.decrypt(encryption_token_) == 0) { - std::string nonce; - if (response.decode(nonce) == 0) { - if (nonce == conn->nonce) { - conn->generate_nonce(); - return true; - } - - throw std::runtime_error("invalid nonce"); - } - - throw std::runtime_error("invalid nonce"); - } - - throw std::runtime_error("decryption failed"); - } - - throw std::runtime_error("invalid handshake"); + throw std::runtime_error("invalid nonce"); } - throw std::runtime_error("failed to send handshake"); + throw std::runtime_error("decryption failed"); } catch (const std::exception &e) { repertory::utils::error::raise_error(function_name, e, "handlshake failed"); } @@ -169,10 +152,16 @@ void packet_server::initialize(const uint16_t &port, uint8_t pool_size) { } void packet_server::listen_for_connection(tcp::acceptor &acceptor) { + REPERTORY_USES_FUNCTION_NAME(); + auto conn = std::make_shared(io_context_, acceptor); acceptor.async_accept(conn->socket, [this, conn](auto &&err) { - on_accept(conn, std::forward(err)); + try { + on_accept(conn, std::forward(err)); + } catch (const std::exception &e) { + utils::error::raise_error(function_name, e, "exception occurred"); + } }); } @@ -331,9 +320,10 @@ void packet_server::send_response(std::shared_ptr conn, if (err) { remove_client(*conn); utils::error::raise_error(function_name, err.message()); - } else { - read_header(conn); + return; } + + read_header(conn); }); } } // namespace repertory From 9c8e96bc5e904afdddfed389a45f5a41532cf5bb Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sun, 21 Sep 2025 11:03:43 -0500 Subject: [PATCH 106/136] added handshake for dos protection --- .../include/comm/packet/common.hpp | 25 ++--- .../librepertory/src/comm/packet/common.cpp | 15 +++ .../src/comm/packet/packet_server.cpp | 96 +++++++++++++------ 3 files changed, 91 insertions(+), 45 deletions(-) diff --git a/repertory/librepertory/include/comm/packet/common.hpp b/repertory/librepertory/include/comm/packet/common.hpp index 5638ab58..744758da 100644 --- a/repertory/librepertory/include/comm/packet/common.hpp +++ b/repertory/librepertory/include/comm/packet/common.hpp @@ -25,33 +25,24 @@ #include "utils/common.hpp" namespace repertory::comm { -constexpr const std::uint8_t max_read_attempts{5U}; -constexpr const std::uint16_t packet_nonce_size{256U}; +inline constexpr const std::uint8_t max_read_attempts{5U}; +inline constexpr const std::uint16_t packet_nonce_size{256U}; +inline constexpr const std::uint16_t server_handshake_timeout_ms{3000U}; struct non_blocking_guard final { - boost::asio::ip::tcp::socket &sock; - bool non_blocking{}; - non_blocking_guard(const non_blocking_guard &) = delete; non_blocking_guard(non_blocking_guard &&) = delete; auto operator=(const non_blocking_guard &) -> non_blocking_guard & = delete; auto operator=(non_blocking_guard &&) -> non_blocking_guard & = delete; - explicit non_blocking_guard(boost::asio::ip::tcp::socket &sock_) - : sock(sock_), non_blocking(sock_.non_blocking()) { - boost::system::error_code err; - [[maybe_unused]] auto ret = sock_.non_blocking(true, err); - } + explicit non_blocking_guard(boost::asio::ip::tcp::socket &sock_); - ~non_blocking_guard() { - if (not sock.is_open()) { - return; - } + ~non_blocking_guard(); - boost::system::error_code err; - [[maybe_unused]] auto ret = sock.non_blocking(non_blocking, err); - } +private: + bool non_blocking; + boost::asio::ip::tcp::socket &sock; }; [[nodiscard]] auto is_socket_still_alive(boost::asio::ip::tcp::socket &sock) diff --git a/repertory/librepertory/src/comm/packet/common.cpp b/repertory/librepertory/src/comm/packet/common.cpp index 07f5549b..a1e4481c 100644 --- a/repertory/librepertory/src/comm/packet/common.cpp +++ b/repertory/librepertory/src/comm/packet/common.cpp @@ -25,6 +25,21 @@ #include "events/types/packet_client_timeout.hpp" namespace repertory::comm { +non_blocking_guard::non_blocking_guard(boost::asio::ip::tcp::socket &sock_) + : sock(sock_), non_blocking(sock_.non_blocking()) { + boost::system::error_code err; + [[maybe_unused]] auto ret = sock_.non_blocking(true, err); +} + +non_blocking_guard::~non_blocking_guard() { + if (not sock.is_open()) { + return; + } + + boost::system::error_code err; + [[maybe_unused]] auto ret = sock.non_blocking(non_blocking, err); +} + auto is_socket_still_alive(boost::asio::ip::tcp::socket &sock) -> bool { if (not sock.is_open()) { return false; diff --git a/repertory/librepertory/src/comm/packet/packet_server.cpp b/repertory/librepertory/src/comm/packet/packet_server.cpp index 8ecb0a53..5e97d695 100644 --- a/repertory/librepertory/src/comm/packet/packet_server.cpp +++ b/repertory/librepertory/src/comm/packet/packet_server.cpp @@ -31,11 +31,13 @@ #include "platform/platform.hpp" #include "types/repertory.hpp" #include "utils/error_utils.hpp" +#include "utils/timeout.hpp" using namespace repertory::comm; using std::thread; namespace repertory { + packet_server::packet_server(std::uint16_t port, std::string token, std::uint8_t pool_size, closed_callback closed, message_handler_callback message_handler) @@ -93,28 +95,73 @@ auto packet_server::handshake(std::shared_ptr conn) const -> bool { request.to_buffer(buffer); auto to_read{buffer.size() + utils::encryption::encryption_header_size}; - write_all_with_deadline(io_context_, conn->socket, - boost::asio::buffer(buffer), - std::chrono::milliseconds(3000U)); - - conn->buffer.resize(to_read); - read_exact_with_deadline(io_context_, conn->socket, - boost::asio::buffer(conn->buffer), - std::chrono::milliseconds(3000U)); - packet response(conn->buffer); - if (response.decrypt(encryption_token_) == 0) { - std::string nonce; - if (response.decode(nonce) == 0) { - if (nonce == conn->nonce) { - conn->generate_nonce(); - return true; - } + const auto timeout_handler = [&conn]() { + try { + boost::system::error_code err{}; + [[maybe_unused]] auto ret = conn->socket.cancel(err); + } catch (const std::exception &e) { + repertory::utils::error::raise_error(function_name, e, + "exception occurred"); } - throw std::runtime_error("invalid nonce"); + try { + conn->socket.close(); + } catch (const std::exception &e) { + repertory::utils::error::raise_error(function_name, e, + "exception occurred"); + } + }; + + timeout write_timeout(timeout_handler, std::chrono::milliseconds( + server_handshake_timeout_ms)); + + auto written = boost::asio::write( + conn->socket, boost::asio::buffer(boost::asio::buffer(buffer))); + write_timeout.disable(); + + if (written == buffer.size()) { + conn->buffer.resize(to_read); + + timeout read_timeout(timeout_handler, std::chrono::milliseconds( + server_handshake_timeout_ms)); + + std::uint32_t total_read{}; + while ((total_read < to_read) && conn->socket.is_open()) { + auto bytes_read = boost::asio::read( + conn->socket, + boost::asio::buffer(&conn->buffer[total_read], + conn->buffer.size() - total_read)); + if (bytes_read <= 0) { + throw std::runtime_error("0 bytes read"); + } + + total_read += static_cast(bytes_read); + } + read_timeout.disable(); + + if (total_read == to_read) { + packet response(conn->buffer); + if (response.decrypt(encryption_token_) == 0) { + std::string nonce; + if (response.decode(nonce) == 0) { + if (nonce == conn->nonce) { + conn->generate_nonce(); + return true; + } + + throw std::runtime_error("invalid nonce"); + } + + throw std::runtime_error("invalid nonce"); + } + + throw std::runtime_error("decryption failed"); + } + + throw std::runtime_error("invalid handshake"); } - throw std::runtime_error("decryption failed"); + throw std::runtime_error("failed to send handshake"); } catch (const std::exception &e) { repertory::utils::error::raise_error(function_name, e, "handlshake failed"); } @@ -152,16 +199,10 @@ void packet_server::initialize(const uint16_t &port, uint8_t pool_size) { } void packet_server::listen_for_connection(tcp::acceptor &acceptor) { - REPERTORY_USES_FUNCTION_NAME(); - auto conn = std::make_shared(io_context_, acceptor); acceptor.async_accept(conn->socket, [this, conn](auto &&err) { - try { - on_accept(conn, std::forward(err)); - } catch (const std::exception &e) { - utils::error::raise_error(function_name, e, "exception occurred"); - } + on_accept(conn, std::forward(err)); }); } @@ -320,10 +361,9 @@ void packet_server::send_response(std::shared_ptr conn, if (err) { remove_client(*conn); utils::error::raise_error(function_name, err.message()); - return; + } else { + read_header(conn); } - - read_header(conn); }); } } // namespace repertory From beff363507bad9ef8ab34c80929bba0abb927842 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sun, 21 Sep 2025 11:05:34 -0500 Subject: [PATCH 107/136] added handshake for dos protection --- repertory/librepertory/src/comm/packet/common.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repertory/librepertory/src/comm/packet/common.cpp b/repertory/librepertory/src/comm/packet/common.cpp index a1e4481c..1a4263b7 100644 --- a/repertory/librepertory/src/comm/packet/common.cpp +++ b/repertory/librepertory/src/comm/packet/common.cpp @@ -26,7 +26,7 @@ namespace repertory::comm { non_blocking_guard::non_blocking_guard(boost::asio::ip::tcp::socket &sock_) - : sock(sock_), non_blocking(sock_.non_blocking()) { + : non_blocking(sock_.non_blocking()), sock(sock_) { boost::system::error_code err; [[maybe_unused]] auto ret = sock_.non_blocking(true, err); } From 067a1d26b3690385da05f48ca8ceb48ad4c4a311 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sun, 21 Sep 2025 11:13:52 -0500 Subject: [PATCH 108/136] added handshake for dos protection --- .../include/comm/packet/packet_client.hpp | 1 + .../src/comm/packet/packet_client.cpp | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/repertory/librepertory/include/comm/packet/packet_client.hpp b/repertory/librepertory/include/comm/packet/packet_client.hpp index 3389ea56..a48060fc 100644 --- a/repertory/librepertory/include/comm/packet/packet_client.hpp +++ b/repertory/librepertory/include/comm/packet/packet_client.hpp @@ -57,6 +57,7 @@ private: resolve_results_; std::mutex clients_mutex_; std::vector> clients_; + std::vector service_threads_; private: static void close(client &cli); diff --git a/repertory/librepertory/src/comm/packet/packet_client.cpp b/repertory/librepertory/src/comm/packet/packet_client.cpp index 412cc4e0..8ea562c0 100644 --- a/repertory/librepertory/src/comm/packet/packet_client.cpp +++ b/repertory/librepertory/src/comm/packet/packet_client.cpp @@ -33,12 +33,23 @@ using namespace repertory::comm; namespace repertory { packet_client::packet_client(remote::remote_config cfg) - : cfg_(std::move(cfg)), unique_id_(utils::create_uuid_string()) {} + : cfg_(std::move(cfg)), unique_id_(utils::create_uuid_string()) { + for (std::uint8_t idx = 0U; idx < cfg.max_connections; ++idx) { + service_threads_.emplace_back([this]() { io_context_.run(); }); + } +} packet_client::~packet_client() { allow_connections_ = false; close_all(); - io_context_.stop(); + + for (std::size_t idx = 0U; idx < service_threads_.size(); ++idx) { + io_context_.stop(); + } + + for (auto &thread : service_threads_) { + thread.join(); + } } void packet_client::close(client &cli) { @@ -58,7 +69,9 @@ void packet_client::close_all() { } clients_.clear(); - io_context_.restart(); + for (std::size_t idx = 0U; idx < service_threads_.size(); ++idx) { + io_context_.restart(); + } resolve_results_ = {}; unique_id_ = utils::create_uuid_string(); } From 450e0a9603c689bde92cdd7ce0f8ad9f24fc1b37 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sun, 21 Sep 2025 15:39:23 -0500 Subject: [PATCH 109/136] refactor --- .../src/comm/packet/packet_server.cpp | 45 ++++++++++--------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/repertory/librepertory/src/comm/packet/packet_server.cpp b/repertory/librepertory/src/comm/packet/packet_server.cpp index 5e97d695..0f5f3dbb 100644 --- a/repertory/librepertory/src/comm/packet/packet_server.cpp +++ b/repertory/librepertory/src/comm/packet/packet_server.cpp @@ -201,9 +201,8 @@ void packet_server::initialize(const uint16_t &port, uint8_t pool_size) { void packet_server::listen_for_connection(tcp::acceptor &acceptor) { auto conn = std::make_shared(io_context_, acceptor); - acceptor.async_accept(conn->socket, [this, conn](auto &&err) { - on_accept(conn, std::forward(err)); - }); + acceptor.async_accept(conn->socket, + [this, conn](auto &&err) { on_accept(conn, err); }); } void packet_server::on_accept(std::shared_ptr conn, @@ -219,14 +218,17 @@ void packet_server::on_accept(std::shared_ptr conn, conn->socket.set_option(boost::asio::ip::tcp::no_delay(true)); conn->socket.set_option(boost::asio::socket_base::linger(false, 0)); + conn->socket.set_option(boost::asio::socket_base::keep_alive(true)); - if (not handshake(conn)) { - conn->socket.close(); - return; - } + boost::asio::dispatch(conn->socket.get_executor(), [this, conn]() { + if (not handshake(conn)) { + conn->socket.close(); + return; + } - packet response; - send_response(conn, 0, response); + packet response; + send_response(conn, 0, response); + }); } void packet_server::read_header(std::shared_ptr conn) { @@ -236,14 +238,15 @@ void packet_server::read_header(std::shared_ptr conn) { boost::asio::async_read( conn->socket, boost::asio::buffer(conn->buffer.data(), conn->buffer.size()), - [this, conn](boost::system::error_code err, std::size_t) { + [this, conn](auto &&err, auto &&) { if (err) { remove_client(*conn); repertory::utils::error::raise_error(function_name, err.message()); return; } - auto to_read = *reinterpret_cast(conn->buffer.data()); + std::uint32_t to_read{}; + std::memcpy(&to_read, conn->buffer.data(), sizeof(to_read)); boost::endian::big_to_native_inplace(to_read); read_packet(conn, to_read); }); @@ -355,15 +358,15 @@ void packet_server::send_response(std::shared_ptr conn, response.encrypt(encryption_token_); response.to_buffer(conn->buffer); - boost::asio::async_write( - conn->socket, boost::asio::buffer(conn->buffer), - [this, conn](boost::system::error_code err, std::size_t /*length*/) { - if (err) { - remove_client(*conn); - utils::error::raise_error(function_name, err.message()); - } else { - read_header(conn); - } - }); + boost::asio::async_write(conn->socket, boost::asio::buffer(conn->buffer), + [this, conn](auto &&err, auto &&) { + if (err) { + remove_client(*conn); + utils::error::raise_error(function_name, + err.message()); + } else { + read_header(conn); + } + }); } } // namespace repertory From 1c180d3de5b807d3f0f5aa1bd9ac1bd3dcda0100 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sun, 21 Sep 2025 15:40:53 -0500 Subject: [PATCH 110/136] refactor --- repertory/librepertory/src/comm/packet/packet_client.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/repertory/librepertory/src/comm/packet/packet_client.cpp b/repertory/librepertory/src/comm/packet/packet_client.cpp index 8ea562c0..1a6d5229 100644 --- a/repertory/librepertory/src/comm/packet/packet_client.cpp +++ b/repertory/librepertory/src/comm/packet/packet_client.cpp @@ -177,11 +177,10 @@ auto packet_client::read_packet(client &cli, packet &response) const read_exact_with_deadline(io_context_, cli.socket, boost::asio::buffer(buffer), std::chrono::milliseconds(cfg_.recv_timeout_ms)); - std::uint32_t size_be = 0U; - std::memcpy(&size_be, buffer.data(), sizeof(size_be)); - const std::uint32_t size = boost::endian::big_to_native(size_be); - - buffer.resize(size); + std::uint32_t to_read{}; + std::memcpy(&to_read, buffer.data(), sizeof(to_read)); + boost::endian::big_to_native_inplace(to_read); + buffer.resize(to_read); read_exact_with_deadline(io_context_, cli.socket, boost::asio::buffer(buffer), std::chrono::milliseconds(cfg_.recv_timeout_ms)); From 018bbe626e1d6bb93ac376aed6139edffd124a88 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sun, 21 Sep 2025 15:42:31 -0500 Subject: [PATCH 111/136] refactor --- repertory/librepertory/src/comm/packet/packet_server.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/repertory/librepertory/src/comm/packet/packet_server.cpp b/repertory/librepertory/src/comm/packet/packet_server.cpp index 0f5f3dbb..a35a6169 100644 --- a/repertory/librepertory/src/comm/packet/packet_server.cpp +++ b/repertory/librepertory/src/comm/packet/packet_server.cpp @@ -115,11 +115,11 @@ auto packet_server::handshake(std::shared_ptr conn) const -> bool { timeout write_timeout(timeout_handler, std::chrono::milliseconds( server_handshake_timeout_ms)); - auto written = boost::asio::write( + auto bytes_written = boost::asio::write( conn->socket, boost::asio::buffer(boost::asio::buffer(buffer))); write_timeout.disable(); - if (written == buffer.size()) { + if (bytes_written == buffer.size()) { conn->buffer.resize(to_read); timeout read_timeout(timeout_handler, std::chrono::milliseconds( From 44bd4fce208789b8e009ccf8c6ec7cb742feef89 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sun, 21 Sep 2025 15:50:44 -0500 Subject: [PATCH 112/136] refactor --- repertory/librepertory/src/comm/packet/packet_server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repertory/librepertory/src/comm/packet/packet_server.cpp b/repertory/librepertory/src/comm/packet/packet_server.cpp index a35a6169..dd86cf00 100644 --- a/repertory/librepertory/src/comm/packet/packet_server.cpp +++ b/repertory/librepertory/src/comm/packet/packet_server.cpp @@ -131,7 +131,7 @@ auto packet_server::handshake(std::shared_ptr conn) const -> bool { conn->socket, boost::asio::buffer(&conn->buffer[total_read], conn->buffer.size() - total_read)); - if (bytes_read <= 0) { + if (bytes_read == 0) { throw std::runtime_error("0 bytes read"); } From ac6a435c320a89c84f8590da915fd5afbd55ebae Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sun, 21 Sep 2025 16:32:22 -0500 Subject: [PATCH 113/136] address ub --- support/src/utils/encryption.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/support/src/utils/encryption.cpp b/support/src/utils/encryption.cpp index 33f0a91e..80ee8c0b 100644 --- a/support/src/utils/encryption.cpp +++ b/support/src/utils/encryption.cpp @@ -61,7 +61,9 @@ auto kdf_config::generate_checksum() const -> std::uint64_t { tmp.checksum = 0; auto hash = utils::hash::create_hash_blake2b_64(tmp.to_header()); - return *reinterpret_cast(hash.data()); + std::uint64_t ret{}; + std::memcpy(&ret, hash.data(), hash.size()); + return ret; } auto kdf_config::from_header(data_cspan data, kdf_config &cfg, From 63701e105a2cc779114f56b4a2ca46abc5bcdb38 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 22 Sep 2025 07:38:07 -0500 Subject: [PATCH 114/136] fix namespace comment --- repertory/version.cpp.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repertory/version.cpp.in b/repertory/version.cpp.in index 98b141aa..85604429 100644 --- a/repertory/version.cpp.in +++ b/repertory/version.cpp.in @@ -11,4 +11,4 @@ namespace repertory { auto project_get_git_rev() -> std::string_view { return git_rev; } auto project_get_version() -> std::string_view { return version; } -} // namespace %PROJECT_NAME % +} // namespace repertory From 06f169293d739aafff2495cf95372fabf76c9d3c Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 22 Sep 2025 10:36:36 -0500 Subject: [PATCH 115/136] added check version support to remote mounts --- CHANGELOG.md | 1 + .../include/comm/packet/packet_client.hpp | 6 ++- .../librepertory/include/utils/utils.hpp | 3 ++ .../src/comm/packet/packet_client.cpp | 51 ++++++++++++++++--- .../src/comm/packet/packet_server.cpp | 5 +- repertory/librepertory/src/utils/utils.cpp | 11 +++- .../repertory/include/cli/check_version.hpp | 40 ++++++++++++--- repertory/repertory/main.cpp | 51 +++++++++++-------- 8 files changed, 127 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83d8987b..27b40244 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ ### Changes from v2.0.7-release +* Added check version support to remote mounts * Fixed handling of `FALLOC_FL_KEEP_SIZE` on Linux * Fixed intermittent client hang on remote mount server disconnect diff --git a/repertory/librepertory/include/comm/packet/packet_client.hpp b/repertory/librepertory/include/comm/packet/packet_client.hpp index a48060fc..c501f797 100644 --- a/repertory/librepertory/include/comm/packet/packet_client.hpp +++ b/repertory/librepertory/include/comm/packet/packet_client.hpp @@ -68,7 +68,8 @@ private: [[nodiscard]] auto get_client() -> std::shared_ptr; - [[nodiscard]] auto handshake(client &cli) const -> bool; + [[nodiscard]] auto handshake(client &cli, std::uint32_t &min_version) const + -> bool; void put_client(std::shared_ptr &cli); @@ -78,6 +79,9 @@ private: void resolve(); public: + [[nodiscard]] auto check_version(std::uint32_t client_version, + std::uint32_t &min_version) -> api_error; + [[nodiscard]] auto send(std::string_view method, std::uint32_t &service_flags) -> packet::error_type; diff --git a/repertory/librepertory/include/utils/utils.hpp b/repertory/librepertory/include/utils/utils.hpp index b630d09c..90aec22e 100644 --- a/repertory/librepertory/include/utils/utils.hpp +++ b/repertory/librepertory/include/utils/utils.hpp @@ -41,6 +41,9 @@ create_rocksdb(const app_config &cfg, const std::string &name, [[nodiscard]] auto create_volume_label(provider_type prov) -> std::string; [[nodiscard]] auto get_attributes_from_meta(const api_meta_map &meta) -> DWORD; + +[[nodiscard]] auto get_version_number(std::string_view version) + -> std::uint32_t; } // namespace utils } // namespace repertory diff --git a/repertory/librepertory/src/comm/packet/packet_client.cpp b/repertory/librepertory/src/comm/packet/packet_client.cpp index 1a6d5229..31531519 100644 --- a/repertory/librepertory/src/comm/packet/packet_client.cpp +++ b/repertory/librepertory/src/comm/packet/packet_client.cpp @@ -27,7 +27,6 @@ #include "utils/collection.hpp" #include "utils/common.hpp" #include "utils/error_utils.hpp" -#include "version.hpp" using namespace repertory::comm; @@ -58,7 +57,8 @@ void packet_client::close(client &cli) { boost::system::error_code err; [[maybe_unused]] auto res = cli.socket.close(err); - } catch (...) { + } catch (const std::exception &e) { + utils::error::raise_error(function_name, e, "connection handshake failed"); } } @@ -76,6 +76,39 @@ void packet_client::close_all() { unique_id_ = utils::create_uuid_string(); } +auto packet_client::check_version(std::uint32_t client_version, + std::uint32_t &min_version) -> api_error { + try { + min_version = 0U; + + boost::asio::io_context ctx{}; + auto resolve_results = tcp::resolver(ctx).resolve( + cfg_.host_name_or_ip, std::to_string(cfg_.api_port)); + + client cli(ctx); + connect_with_deadline(ctx, cli.socket, resolve_results, + std::chrono::milliseconds(cfg_.conn_timeout_ms)); + + cli.socket.set_option(boost::asio::ip::tcp::no_delay(true)); + cli.socket.set_option(boost::asio::socket_base::linger(false, 0)); + cli.socket.set_option(boost::asio::socket_base::keep_alive(true)); + + if (not handshake(cli, min_version)) { + return api_error::comm_error; + } + + if (client_version < min_version) { + return api_error::incompatible_version; + } + + return api_error::success; + } catch (const std::exception &e) { + utils::error::raise_error(function_name, e, "connection handshake failed"); + } + + return api_error::error; +} + void packet_client::connect(client &cli) { try { resolve(); @@ -87,7 +120,8 @@ void packet_client::connect(client &cli) { cli.socket.set_option(boost::asio::socket_base::linger(false, 0)); cli.socket.set_option(boost::asio::socket_base::keep_alive(true)); - if (not handshake(cli)) { + std::uint32_t min_version{}; + if (not handshake(cli, min_version)) { close(cli); return; } @@ -131,14 +165,18 @@ auto packet_client::get_client() -> std::shared_ptr { return nullptr; } -auto packet_client::handshake(client &cli) const -> bool { +auto packet_client::handshake(client &cli, std::uint32_t &min_version) const + -> bool { REPERTORY_USES_FUNCTION_NAME(); try { + min_version = 0U; + data_buffer buffer; { packet tmp; - tmp.encode_top(utils::generate_random_string(packet_nonce_size)); + tmp.encode(utils::get_version_number(REPERTORY_MIN_REMOTE_VERSION)); + tmp.encode(utils::generate_random_string(packet_nonce_size)); tmp.to_buffer(buffer); } @@ -146,6 +184,7 @@ auto packet_client::handshake(client &cli) const -> bool { boost::asio::buffer(buffer), std::chrono::milliseconds(cfg_.recv_timeout_ms)); packet response(buffer); + response.decode(min_version); response.encrypt(cfg_.encryption_token, false); response.to_buffer(buffer); @@ -154,7 +193,7 @@ auto packet_client::handshake(client &cli) const -> bool { std::chrono::milliseconds(cfg_.send_timeout_ms)); return true; } catch (const std::exception &e) { - repertory::utils::error::raise_error(function_name, e, "handlshake failed"); + utils::error::raise_error(function_name, e, "handlshake failed"); } return false; diff --git a/repertory/librepertory/src/comm/packet/packet_server.cpp b/repertory/librepertory/src/comm/packet/packet_server.cpp index dd86cf00..fa7018ea 100644 --- a/repertory/librepertory/src/comm/packet/packet_server.cpp +++ b/repertory/librepertory/src/comm/packet/packet_server.cpp @@ -32,12 +32,12 @@ #include "types/repertory.hpp" #include "utils/error_utils.hpp" #include "utils/timeout.hpp" +#include "utils/utils.hpp" using namespace repertory::comm; using std::thread; namespace repertory { - packet_server::packet_server(std::uint16_t port, std::string token, std::uint8_t pool_size, closed_callback closed, message_handler_callback message_handler) @@ -91,7 +91,8 @@ auto packet_server::handshake(std::shared_ptr conn) const -> bool { data_buffer buffer; packet request; - request.encode_top(conn->nonce); + request.encode(utils::get_version_number(REPERTORY_MIN_REMOTE_VERSION)); + request.encode(conn->nonce); request.to_buffer(buffer); auto to_read{buffer.size() + utils::encryption::encryption_header_size}; diff --git a/repertory/librepertory/src/utils/utils.cpp b/repertory/librepertory/src/utils/utils.cpp index 0b51cadd..4f4d474d 100644 --- a/repertory/librepertory/src/utils/utils.cpp +++ b/repertory/librepertory/src/utils/utils.cpp @@ -19,8 +19,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "rocksdb/table.h" - #include "utils/utils.hpp" #include "app_config.hpp" @@ -90,4 +88,13 @@ auto create_volume_label(provider_type prov) -> std::string { auto get_attributes_from_meta(const api_meta_map &meta) -> DWORD { return static_cast(utils::string::to_uint32(meta.at(META_ATTRIBUTES))); } + +auto get_repertory_version_number(std::string_view version) -> std::uint32_t { + auto parts = utils::string::split(version, '-', false); + parts = utils::string::split(parts.at(0U), '.', false); + + return (utils::string::to_uint32(parts.at(0U)) << 24U) | + (utils::string::to_uint32(parts.at(1U)) << 16U) | + (utils::string::to_uint32(parts.at(2U)) << 8U); +} } // namespace repertory::utils diff --git a/repertory/repertory/include/cli/check_version.hpp b/repertory/repertory/include/cli/check_version.hpp index e10564f6..8d18be58 100644 --- a/repertory/repertory/include/cli/check_version.hpp +++ b/repertory/repertory/include/cli/check_version.hpp @@ -23,17 +23,41 @@ #define REPERTORY_INCLUDE_CLI_CHECK_VERSION_HPP_ #include "cli/common.hpp" +#include "comm/packet/packet_client.hpp" +#include "utils/utils.hpp" +#include "version.cpp" namespace repertory::cli::actions { -[[nodiscard]] inline auto check_version(std::vector /* args */, - const std::string &data_directory, - provider_type prov, - const std::string & /*unique_id*/, - std::string /*user*/, - std::string /*password*/) -> exit_code { +[[nodiscard]] inline auto +check_version(const std::string &data_directory, provider_type prov, + const std::string &remote_host, std::uint16_t remote_port) + -> exit_code { + if (prov == provider_type::remote) { + app_config config(prov, data_directory); + auto remote_cfg = config.get_remote_config(); + remote_cfg.host_name_or_ip = remote_host; + remote_cfg.api_port = remote_port; + config.set_remote_config(remote_cfg); + packet_client client(config.get_remote_config()); + + std::uint32_t min_version{}; + auto client_version = utils::get_version_number(project_get_version()); + auto ret = client.check_version(client_version, min_version); + if (ret == api_error::success) { + fmt::println("0\nSuccess:\n\tRequired: {}\n\tActual: {}", min_version, + client_version); + } else { + fmt::println("1\nFailed:\n\tRequired: {}\n\tActual: {}", min_version, + client_version); + } + + return ret; + } + if (prov != provider_type::sia) { - fmt::println("Success:\n\tNo specific version is required for {} providers", - app_config::get_provider_display_name(prov)); + fmt::println( + "0\nSuccess:\n\tNo specific version is required for {} providers", + app_config::get_provider_display_name(prov)); return exit_code::success; } diff --git a/repertory/repertory/main.cpp b/repertory/repertory/main.cpp index 59337fe9..ac835fdc 100644 --- a/repertory/repertory/main.cpp +++ b/repertory/repertory/main.cpp @@ -113,6 +113,7 @@ auto main(int argc, char **argv) -> int { '_'), }) : utils::path::absolute(data_directory); + } catch (const std::exception &e) { std::cerr << (e.what() == nullptr ? "Unable to parse port" : e.what()) @@ -145,32 +146,38 @@ auto main(int argc, char **argv) -> int { } } - int mount_result{}; - if (res == exit_code::success) { - res = exit_code::option_not_found; - for (std::size_t idx = 0U; - (res == exit_code::option_not_found) && - (idx < utils::cli::options::option_list.size()); - idx++) { - try { - res = cli::actions::perform_action( - utils::cli::options::option_list[idx], args, data_directory, prov, - unique_id, user, password); - } catch (const std::exception &ex) { - res = exit_code::exception; - } catch (...) { - res = exit_code::exception; + if (utils::cli::has_option(args, + utils::cli::options::check_version_option)) { + ret = static_cast(utils::cli::check_version( + data_directory, prov, remote_host, remote_port)); + } else { + int mount_result{}; + if (res == exit_code::success) { + res = exit_code::option_not_found; + for (std::size_t idx = 0U; + (res == exit_code::option_not_found) && + (idx < utils::cli::options::option_list.size()); + idx++) { + try { + res = cli::actions::perform_action( + utils::cli::options::option_list[idx], args, data_directory, + prov, unique_id, user, password); + } catch (const std::exception &ex) { + res = exit_code::exception; + } catch (...) { + res = exit_code::exception; + } + } + + if (res == exit_code::option_not_found) { + res = cli::actions::mount(args, data_directory, mount_result, prov, + remote_host, remote_port, unique_id); } } - if (res == exit_code::option_not_found) { - res = cli::actions::mount(args, data_directory, mount_result, prov, - remote_host, remote_port, unique_id); - } + ret = ((res == exit_code::mount_result) ? mount_result + : static_cast(res)); } - - ret = ((res == exit_code::mount_result) ? mount_result - : static_cast(res)); } repertory::project_cleanup(); From 1a61ec693e28a14a52dc7fe9926bfafb1b72bf99 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 22 Sep 2025 10:43:41 -0500 Subject: [PATCH 116/136] added check version support to remote mounts --- .../src/comm/packet/packet_client.cpp | 17 +++++++++++++---- .../repertory/include/cli/check_version.hpp | 2 +- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/repertory/librepertory/src/comm/packet/packet_client.cpp b/repertory/librepertory/src/comm/packet/packet_client.cpp index 31531519..b1a19cae 100644 --- a/repertory/librepertory/src/comm/packet/packet_client.cpp +++ b/repertory/librepertory/src/comm/packet/packet_client.cpp @@ -27,6 +27,9 @@ #include "utils/collection.hpp" #include "utils/common.hpp" #include "utils/error_utils.hpp" +#include "utils/utils.hpp" +#include "version.hpp" +#include using namespace repertory::comm; @@ -52,6 +55,8 @@ packet_client::~packet_client() { } void packet_client::close(client &cli) { + REPERTORY_USES_FUNCTION_NAME(); + try { cli.socket.shutdown(boost::asio::socket_base::shutdown_both); @@ -69,15 +74,15 @@ void packet_client::close_all() { } clients_.clear(); - for (std::size_t idx = 0U; idx < service_threads_.size(); ++idx) { - io_context_.restart(); - } + io_context_.restart(); resolve_results_ = {}; unique_id_ = utils::create_uuid_string(); } auto packet_client::check_version(std::uint32_t client_version, std::uint32_t &min_version) -> api_error { + REPERTORY_USES_FUNCTION_NAME(); + try { min_version = 0U; @@ -184,7 +189,11 @@ auto packet_client::handshake(client &cli, std::uint32_t &min_version) const boost::asio::buffer(buffer), std::chrono::milliseconds(cfg_.recv_timeout_ms)); packet response(buffer); - response.decode(min_version); + auto res = response.decode(min_version); + if (res != 0) { + throw std::runtime_error("failed to decode server version"); + } + response.encrypt(cfg_.encryption_token, false); response.to_buffer(buffer); diff --git a/repertory/repertory/include/cli/check_version.hpp b/repertory/repertory/include/cli/check_version.hpp index 8d18be58..c1b18a51 100644 --- a/repertory/repertory/include/cli/check_version.hpp +++ b/repertory/repertory/include/cli/check_version.hpp @@ -25,7 +25,7 @@ #include "cli/common.hpp" #include "comm/packet/packet_client.hpp" #include "utils/utils.hpp" -#include "version.cpp" +#include "version.hpp" namespace repertory::cli::actions { [[nodiscard]] inline auto From 3476724454974d78b8d078ab6faec900197d44d7 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 22 Sep 2025 10:46:37 -0500 Subject: [PATCH 117/136] added check version support to remote mounts --- repertory/repertory/include/cli/check_version.hpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/repertory/repertory/include/cli/check_version.hpp b/repertory/repertory/include/cli/check_version.hpp index c1b18a51..bf83efcb 100644 --- a/repertory/repertory/include/cli/check_version.hpp +++ b/repertory/repertory/include/cli/check_version.hpp @@ -42,8 +42,8 @@ check_version(const std::string &data_directory, provider_type prov, std::uint32_t min_version{}; auto client_version = utils::get_version_number(project_get_version()); - auto ret = client.check_version(client_version, min_version); - if (ret == api_error::success) { + auto res = client.check_version(client_version, min_version); + if (res == api_error::success) { fmt::println("0\nSuccess:\n\tRequired: {}\n\tActual: {}", min_version, client_version); } else { @@ -51,7 +51,10 @@ check_version(const std::string &data_directory, provider_type prov, client_version); } - return ret; + return res == api_error::success ? exit_code::success + : res == api_error::incompatible_version + ? exit_code::incompatible_version + : exit_code::communication_error; } if (prov != provider_type::sia) { From efc8955944fa9a571975444e74947ca932f2763f Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 22 Sep 2025 10:49:02 -0500 Subject: [PATCH 118/136] added check version support to remote mounts --- repertory/repertory/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repertory/repertory/main.cpp b/repertory/repertory/main.cpp index ac835fdc..a90e7ae0 100644 --- a/repertory/repertory/main.cpp +++ b/repertory/repertory/main.cpp @@ -148,7 +148,7 @@ auto main(int argc, char **argv) -> int { if (utils::cli::has_option(args, utils::cli::options::check_version_option)) { - ret = static_cast(utils::cli::check_version( + ret = static_cast(cli::actions::check_version( data_directory, prov, remote_host, remote_port)); } else { int mount_result{}; From 37b014e9b47d437abdfb30fb242bdb9ecf02a638 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 22 Sep 2025 10:50:04 -0500 Subject: [PATCH 119/136] added check version support to remote mounts --- repertory/repertory/include/cli/actions.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/repertory/repertory/include/cli/actions.hpp b/repertory/repertory/include/cli/actions.hpp index 5597f09c..cbf72df8 100644 --- a/repertory/repertory/include/cli/actions.hpp +++ b/repertory/repertory/include/cli/actions.hpp @@ -22,7 +22,6 @@ #ifndef REPERTORY_INCLUDE_CLI_ACTIONS_HPP_ #define REPERTORY_INCLUDE_CLI_ACTIONS_HPP_ -#include "cli/check_version.hpp" #include "cli/display_config.hpp" #include "cli/drive_information.hpp" #include "cli/get.hpp" @@ -55,8 +54,6 @@ struct option_hasher { inline const std::unordered_map option_actions = { - {utils::cli::options::check_version_option, - cli::actions::check_version}, {utils::cli::options::display_config_option, cli::actions::display_config}, {utils::cli::options::drive_information_option, From 0cd8d59fdac2b2729e779b0a1439c6ab4c6007c4 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 22 Sep 2025 10:51:10 -0500 Subject: [PATCH 120/136] added check version support to remote mounts --- repertory/repertory/include/cli/actions.hpp | 1 + repertory/repertory/main.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/repertory/repertory/include/cli/actions.hpp b/repertory/repertory/include/cli/actions.hpp index cbf72df8..79f22a5e 100644 --- a/repertory/repertory/include/cli/actions.hpp +++ b/repertory/repertory/include/cli/actions.hpp @@ -22,6 +22,7 @@ #ifndef REPERTORY_INCLUDE_CLI_ACTIONS_HPP_ #define REPERTORY_INCLUDE_CLI_ACTIONS_HPP_ +#include "cli/check_version.hpp" #include "cli/display_config.hpp" #include "cli/drive_information.hpp" #include "cli/get.hpp" diff --git a/repertory/repertory/main.cpp b/repertory/repertory/main.cpp index a90e7ae0..e851b70a 100644 --- a/repertory/repertory/main.cpp +++ b/repertory/repertory/main.cpp @@ -157,7 +157,7 @@ auto main(int argc, char **argv) -> int { for (std::size_t idx = 0U; (res == exit_code::option_not_found) && (idx < utils::cli::options::option_list.size()); - idx++) { + ++idx) { try { res = cli::actions::perform_action( utils::cli::options::option_list[idx], args, data_directory, From 87c539628d5eebc452c7693d1308e92d55fc6e0d Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 22 Sep 2025 11:28:09 -0500 Subject: [PATCH 121/136] added check version support to remote mounts --- repertory/librepertory/src/utils/utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repertory/librepertory/src/utils/utils.cpp b/repertory/librepertory/src/utils/utils.cpp index 4f4d474d..8aed1dbf 100644 --- a/repertory/librepertory/src/utils/utils.cpp +++ b/repertory/librepertory/src/utils/utils.cpp @@ -89,7 +89,7 @@ auto get_attributes_from_meta(const api_meta_map &meta) -> DWORD { return static_cast(utils::string::to_uint32(meta.at(META_ATTRIBUTES))); } -auto get_repertory_version_number(std::string_view version) -> std::uint32_t { +auto get_version_number(std::string_view version) -> std::uint32_t { auto parts = utils::string::split(version, '-', false); parts = utils::string::split(parts.at(0U), '.', false); From 23f869f4646fdc65acfbf7acff9f38dae3be317f Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 22 Sep 2025 11:35:18 -0500 Subject: [PATCH 122/136] added check version support to remote mounts --- .../src/comm/packet/packet_server.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/repertory/librepertory/src/comm/packet/packet_server.cpp b/repertory/librepertory/src/comm/packet/packet_server.cpp index fa7018ea..5157c18e 100644 --- a/repertory/librepertory/src/comm/packet/packet_server.cpp +++ b/repertory/librepertory/src/comm/packet/packet_server.cpp @@ -143,17 +143,22 @@ auto packet_server::handshake(std::shared_ptr conn) const -> bool { if (total_read == to_read) { packet response(conn->buffer); if (response.decrypt(encryption_token_) == 0) { - std::string nonce; - if (response.decode(nonce) == 0) { - if (nonce == conn->nonce) { - conn->generate_nonce(); - return true; + std::uint32_t client_version{}; + if (response.decode(client_version) == 0) { + std::string nonce; + if (response.decode(nonce) == 0) { + if (nonce == conn->nonce) { + conn->generate_nonce(); + return true; + } + + throw std::runtime_error("nonce mismatch"); } throw std::runtime_error("invalid nonce"); } - throw std::runtime_error("invalid nonce"); + throw std::runtime_error("invalid client version"); } throw std::runtime_error("decryption failed"); @@ -283,7 +288,7 @@ void packet_server::read_packet(std::shared_ptr conn, ret = request->decode(nonce); if (ret == 0) { if (nonce != conn->nonce) { - throw std::runtime_error("invalid nonce"); + throw std::runtime_error("nonce mismatch"); } conn->generate_nonce(); From ec4d3022accd50d80b3ce643915fe9fe945d9378 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 22 Sep 2025 12:02:54 -0500 Subject: [PATCH 123/136] added check version support to remote mounts --- .../include/comm/packet/packet_client.hpp | 6 ++++ .../src/comm/packet/packet_client.cpp | 30 ++++++++++++++----- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/repertory/librepertory/include/comm/packet/packet_client.hpp b/repertory/librepertory/include/comm/packet/packet_client.hpp index c501f797..353684b7 100644 --- a/repertory/librepertory/include/comm/packet/packet_client.hpp +++ b/repertory/librepertory/include/comm/packet/packet_client.hpp @@ -71,11 +71,17 @@ private: [[nodiscard]] auto handshake(client &cli, std::uint32_t &min_version) const -> bool; + [[nodiscard]] auto handshake(client &cli, boost::asio::io_context &ctx, + std::uint32_t &min_version) const -> bool; + void put_client(std::shared_ptr &cli); [[nodiscard]] auto read_packet(client &cli, packet &response) const -> packet::error_type; + [[nodiscard]] auto read_packet(client &cli, boost::asio::io_context &ctx, + packet &response) const -> packet::error_type; + void resolve(); public: diff --git a/repertory/librepertory/src/comm/packet/packet_client.cpp b/repertory/librepertory/src/comm/packet/packet_client.cpp index b1a19cae..acf78e32 100644 --- a/repertory/librepertory/src/comm/packet/packet_client.cpp +++ b/repertory/librepertory/src/comm/packet/packet_client.cpp @@ -98,10 +98,16 @@ auto packet_client::check_version(std::uint32_t client_version, cli.socket.set_option(boost::asio::socket_base::linger(false, 0)); cli.socket.set_option(boost::asio::socket_base::keep_alive(true)); - if (not handshake(cli, min_version)) { + if (not handshake(cli, ctx, min_version)) { return api_error::comm_error; } + std::this_thread::sleep_for(2s); + if (cli.socket.available() != 0U) { + min_version = 0U; + return api_error::incompatible_version; + } + if (client_version < min_version) { return api_error::incompatible_version; } @@ -126,7 +132,7 @@ void packet_client::connect(client &cli) { cli.socket.set_option(boost::asio::socket_base::keep_alive(true)); std::uint32_t min_version{}; - if (not handshake(cli, min_version)) { + if (not handshake(cli, io_context_, min_version)) { close(cli); return; } @@ -172,6 +178,11 @@ auto packet_client::get_client() -> std::shared_ptr { auto packet_client::handshake(client &cli, std::uint32_t &min_version) const -> bool { + return handshake(cli, io_context_, min_version); +} + +auto packet_client::handshake(client &cli, boost::asio::io_context &ctx, + std::uint32_t &min_version) const -> bool { REPERTORY_USES_FUNCTION_NAME(); try { @@ -185,8 +196,7 @@ auto packet_client::handshake(client &cli, std::uint32_t &min_version) const tmp.to_buffer(buffer); } - read_exact_with_deadline(io_context_, cli.socket, - boost::asio::buffer(buffer), + read_exact_with_deadline(ctx, cli.socket, boost::asio::buffer(buffer), std::chrono::milliseconds(cfg_.recv_timeout_ms)); packet response(buffer); auto res = response.decode(min_version); @@ -197,8 +207,7 @@ auto packet_client::handshake(client &cli, std::uint32_t &min_version) const response.encrypt(cfg_.encryption_token, false); response.to_buffer(buffer); - write_all_with_deadline(io_context_, cli.socket, - boost::asio::buffer(buffer), + write_all_with_deadline(ctx, cli.socket, boost::asio::buffer(buffer), std::chrono::milliseconds(cfg_.send_timeout_ms)); return true; } catch (const std::exception &e) { @@ -221,8 +230,13 @@ void packet_client::put_client(std::shared_ptr &cli) { auto packet_client::read_packet(client &cli, packet &response) const -> packet::error_type { + return read_packet(cli, io_context_, response); +} + +auto packet_client::read_packet(client &cli, boost::asio::io_context &ctx, + packet &response) const -> packet::error_type { data_buffer buffer(sizeof(std::uint32_t)); - read_exact_with_deadline(io_context_, cli.socket, boost::asio::buffer(buffer), + read_exact_with_deadline(ctx, cli.socket, boost::asio::buffer(buffer), std::chrono::milliseconds(cfg_.recv_timeout_ms)); std::uint32_t to_read{}; @@ -230,7 +244,7 @@ auto packet_client::read_packet(client &cli, packet &response) const boost::endian::big_to_native_inplace(to_read); buffer.resize(to_read); - read_exact_with_deadline(io_context_, cli.socket, boost::asio::buffer(buffer), + read_exact_with_deadline(ctx, cli.socket, boost::asio::buffer(buffer), std::chrono::milliseconds(cfg_.recv_timeout_ms)); response = std::move(buffer); From 29a803e46edb73a7622ab8d314341e5c60341a6e Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 22 Sep 2025 13:43:55 -0500 Subject: [PATCH 124/136] added check_version unit test --- .../src/fuse_drive_misc_test.cpp | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 repertory/repertory_test/src/fuse_drive_misc_test.cpp diff --git a/repertory/repertory_test/src/fuse_drive_misc_test.cpp b/repertory/repertory_test/src/fuse_drive_misc_test.cpp new file mode 100644 index 00000000..9a2c66c0 --- /dev/null +++ b/repertory/repertory_test/src/fuse_drive_misc_test.cpp @@ -0,0 +1,62 @@ +/* + Copyright <2018-2025> + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ +#if !defined(_WIN32) + +#include "fixtures/drive_fixture.hpp" + +#include "comm/packet/packet_client.hpp" +#include "utils/utils.hpp" +#include "version.hpp" + +namespace repertory { +TYPED_TEST_SUITE(fuse_test, platform_provider_types); + +TYPED_TEST(fuse_test, misc_can_check_remote_version) { + if (this->mount_provider != provider_type::remote) { + GTEST_SKIP(); + return; + } + + packet_client client(this->config->get_remote_config()); + + std::uint32_t min_version{}; + auto client_version = utils::get_version_number(project_get_version()); + auto res = client.check_version(client_version, min_version); + EXPECT_EQ(api_error::success, res); +} + +TYPED_TEST(fuse_test, misc_can_fail_invalid_remote_version) { + if (this->mount_provider != provider_type::remote) { + GTEST_SKIP(); + return; + } + + packet_client client(this->config->get_remote_config()); + + std::uint32_t min_version{}; + auto client_version = utils::get_version_number("2.0.7-release"); + auto res = client.check_version(client_version, min_version); + EXPECT_EQ(api_error::incompatible_version, res); +} +} // namespace repertory + +#endif // !defined(_WIN32) From 2db7449370baca8ac0fcfc4e293c0b3ec4d01041 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 22 Sep 2025 13:54:31 -0500 Subject: [PATCH 125/136] fix test --- .../include/fixtures/drive_fixture.hpp | 14 +++++++++----- .../repertory_test/src/fuse_drive_misc_test.cpp | 4 ++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/repertory/repertory_test/include/fixtures/drive_fixture.hpp b/repertory/repertory_test/include/fixtures/drive_fixture.hpp index 9aecd61d..e8f60c4a 100644 --- a/repertory/repertory_test/include/fixtures/drive_fixture.hpp +++ b/repertory/repertory_test/include/fixtures/drive_fixture.hpp @@ -191,6 +191,7 @@ template class drive_fixture : public ::testing::Test { public: #if !defined(_WIN32) static std::unique_ptr config; + static std::unique_ptr config2; static std::unique_ptr meta; #endif // !defined(_WIN32) static std::filesystem::path current_directory; @@ -338,14 +339,14 @@ protected: #endif // defined(_WIN32) auto cfg_dir2 = make_cfg_dir(test_dir, "cfg2"); - auto cfg2 = std::make_unique(provider_type::remote, cfg_dir2); - cfg2->set_enable_drive_events(true); - cfg2->set_event_level(event_level::trace); + config2 = std::make_unique(provider_type::remote, cfg_dir2); + config2->set_enable_drive_events(true); + config2->set_event_level(event_level::trace); #if !defined(_WIN32) - cfg2->set_database_type(database_type::sqlite); + config2->set_database_type(database_type::sqlite); #endif // !defined(_WIN32) - drive_args2 = {"-dd", cfg2->get_data_directory(), "-rm", + drive_args2 = {"-dd", config2->get_data_directory(), "-rm", fmt::format("localhost:{}", provider_t::remote_port)}; platform_ops::execute_mount(drive_args2, mount_location); @@ -568,6 +569,9 @@ public: template std::unique_ptr drive_fixture::config; +template +std::unique_ptr drive_fixture::config2; + template std::unique_ptr drive_fixture::meta{}; #endif // !defined(_WIN32) diff --git a/repertory/repertory_test/src/fuse_drive_misc_test.cpp b/repertory/repertory_test/src/fuse_drive_misc_test.cpp index 9a2c66c0..c43da2c7 100644 --- a/repertory/repertory_test/src/fuse_drive_misc_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_misc_test.cpp @@ -36,7 +36,7 @@ TYPED_TEST(fuse_test, misc_can_check_remote_version) { return; } - packet_client client(this->config->get_remote_config()); + packet_client client(this->config2->get_remote_config()); std::uint32_t min_version{}; auto client_version = utils::get_version_number(project_get_version()); @@ -50,7 +50,7 @@ TYPED_TEST(fuse_test, misc_can_fail_invalid_remote_version) { return; } - packet_client client(this->config->get_remote_config()); + packet_client client(this->config2->get_remote_config()); std::uint32_t min_version{}; auto client_version = utils::get_version_number("2.0.7-release"); From 7d6356cbdca18e2d53c4fdb2c564732ade0417b0 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 22 Sep 2025 13:55:40 -0500 Subject: [PATCH 126/136] cleanup --- repertory/repertory_test/include/fixtures/drive_fixture.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/repertory/repertory_test/include/fixtures/drive_fixture.hpp b/repertory/repertory_test/include/fixtures/drive_fixture.hpp index e8f60c4a..3ba8402f 100644 --- a/repertory/repertory_test/include/fixtures/drive_fixture.hpp +++ b/repertory/repertory_test/include/fixtures/drive_fixture.hpp @@ -393,6 +393,7 @@ protected: #if !defined(_WIN32) meta.reset(); config.reset(); + config2.reset(); #endif // !defined(_WIN32) std::filesystem::current_path(current_directory); } From 1f775dd6f761f51e3633aa68ee37a49e8e57e014 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 22 Sep 2025 14:05:27 -0500 Subject: [PATCH 127/136] update remote config --- .../include/fixtures/drive_fixture.hpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/repertory/repertory_test/include/fixtures/drive_fixture.hpp b/repertory/repertory_test/include/fixtures/drive_fixture.hpp index 3ba8402f..f795dc0f 100644 --- a/repertory/repertory_test/include/fixtures/drive_fixture.hpp +++ b/repertory/repertory_test/include/fixtures/drive_fixture.hpp @@ -346,8 +346,16 @@ protected: config2->set_database_type(database_type::sqlite); #endif // !defined(_WIN32) - drive_args2 = {"-dd", config2->get_data_directory(), "-rm", - fmt::format("localhost:{}", provider_t::remote_port)}; + auto rem_cfg = config2->get_remote_config(); + rem_cfg.host_name_or_ip = "localhost"; + rem_cfg.api_port = provider_t::remote_port; + config2->set_remote_config(rem_cfg); + drive_args2 = { + "-dd", + config2->get_data_directory(), + "-rm", + fmt::format("localhost:{}", provider_t::remote_port), + }; platform_ops::execute_mount(drive_args2, mount_location); }; From d9af430f13efd31aa91049c602e628515da4d9de Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 22 Sep 2025 14:21:37 -0500 Subject: [PATCH 128/136] fix --- repertory/librepertory/src/comm/packet/packet_client.cpp | 6 ------ repertory/repertory_test/src/fuse_drive_misc_test.cpp | 1 + 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/repertory/librepertory/src/comm/packet/packet_client.cpp b/repertory/librepertory/src/comm/packet/packet_client.cpp index acf78e32..0c6d35c6 100644 --- a/repertory/librepertory/src/comm/packet/packet_client.cpp +++ b/repertory/librepertory/src/comm/packet/packet_client.cpp @@ -102,12 +102,6 @@ auto packet_client::check_version(std::uint32_t client_version, return api_error::comm_error; } - std::this_thread::sleep_for(2s); - if (cli.socket.available() != 0U) { - min_version = 0U; - return api_error::incompatible_version; - } - if (client_version < min_version) { return api_error::incompatible_version; } diff --git a/repertory/repertory_test/src/fuse_drive_misc_test.cpp b/repertory/repertory_test/src/fuse_drive_misc_test.cpp index c43da2c7..28de15b8 100644 --- a/repertory/repertory_test/src/fuse_drive_misc_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_misc_test.cpp @@ -41,6 +41,7 @@ TYPED_TEST(fuse_test, misc_can_check_remote_version) { std::uint32_t min_version{}; auto client_version = utils::get_version_number(project_get_version()); auto res = client.check_version(client_version, min_version); + fmt::println("client|{}|server|{}", client_version, min_version); EXPECT_EQ(api_error::success, res); } From 06c50dbcf81f5469f77cbf6581e0cf23bc4f064b Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 22 Sep 2025 14:25:51 -0500 Subject: [PATCH 129/136] fix --- repertory/librepertory/src/comm/packet/packet_client.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/repertory/librepertory/src/comm/packet/packet_client.cpp b/repertory/librepertory/src/comm/packet/packet_client.cpp index 0c6d35c6..feebd480 100644 --- a/repertory/librepertory/src/comm/packet/packet_client.cpp +++ b/repertory/librepertory/src/comm/packet/packet_client.cpp @@ -102,6 +102,10 @@ auto packet_client::check_version(std::uint32_t client_version, return api_error::comm_error; } + if ((min_version & 0xFFU) != 0U) { + return api_error::incompatible_version; + } + if (client_version < min_version) { return api_error::incompatible_version; } From ea510e9cdc2347d38e03d89415eead4e0e37e469 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 22 Sep 2025 14:26:20 -0500 Subject: [PATCH 130/136] reset min_version --- repertory/librepertory/src/comm/packet/packet_client.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/repertory/librepertory/src/comm/packet/packet_client.cpp b/repertory/librepertory/src/comm/packet/packet_client.cpp index feebd480..56efa216 100644 --- a/repertory/librepertory/src/comm/packet/packet_client.cpp +++ b/repertory/librepertory/src/comm/packet/packet_client.cpp @@ -103,6 +103,7 @@ auto packet_client::check_version(std::uint32_t client_version, } if ((min_version & 0xFFU) != 0U) { + min_version = 0U; return api_error::incompatible_version; } From d51bb9ab42f68fd79e9f0fb268173d761c54ebc8 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 22 Sep 2025 14:29:21 -0500 Subject: [PATCH 131/136] added println --- repertory/repertory_test/src/fuse_drive_misc_test.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/repertory/repertory_test/src/fuse_drive_misc_test.cpp b/repertory/repertory_test/src/fuse_drive_misc_test.cpp index 28de15b8..15290035 100644 --- a/repertory/repertory_test/src/fuse_drive_misc_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_misc_test.cpp @@ -56,6 +56,7 @@ TYPED_TEST(fuse_test, misc_can_fail_invalid_remote_version) { std::uint32_t min_version{}; auto client_version = utils::get_version_number("2.0.7-release"); auto res = client.check_version(client_version, min_version); + fmt::println("client|{}|server|{}", client_version, min_version); EXPECT_EQ(api_error::incompatible_version, res); } } // namespace repertory From 6b592f885967151187d3aeb94fcdcb090691ee69 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Tue, 23 Sep 2025 07:12:47 -0500 Subject: [PATCH 132/136] [unit test] Complete FUSE unit tests #22 --- .../src/fuse_drive_unlink_test.cpp | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 repertory/repertory_test/src/fuse_drive_unlink_test.cpp diff --git a/repertory/repertory_test/src/fuse_drive_unlink_test.cpp b/repertory/repertory_test/src/fuse_drive_unlink_test.cpp new file mode 100644 index 00000000..24d3b0e2 --- /dev/null +++ b/repertory/repertory_test/src/fuse_drive_unlink_test.cpp @@ -0,0 +1,113 @@ +/* + Copyright <2018-2025> + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ +#if !defined(_WIN32) + +#include "fixtures/drive_fixture.hpp" + +namespace repertory { +TYPED_TEST_SUITE(fuse_test, platform_provider_types); + +TYPED_TEST(fuse_test, unlink_can_remove_file) { + std::string name{"unlink"}; + auto path = this->create_file_and_test(name); + + this->unlink_file_and_test(path); + + struct stat st{}; + EXPECT_EQ(-1, ::stat(path.c_str(), &st)); + EXPECT_EQ(ENOENT, errno); +} + +TYPED_TEST(fuse_test, unlink_open_file_leaves_handle_intact) { + std::string name{"unlink"}; + auto path = this->create_file_and_test(name, 0644); + + { + auto desc = ::open(path.c_str(), O_WRONLY); + ASSERT_NE(desc, -1); + this->write_all(desc, "HELLO"); + ::close(desc); + } + + auto desc = ::open(path.c_str(), O_RDWR); + ASSERT_NE(desc, -1); + + ASSERT_EQ(0, ::unlink(path.c_str())); + + ASSERT_NE(-1, ::lseek(desc, 0, SEEK_END)); + this->write_all(desc, " WORLD"); + + ASSERT_NE(-1, ::lseek(desc, 0, SEEK_SET)); + std::string out; + char buf[4096]; + for (;;) { + ssize_t r = ::read(desc, buf, sizeof(buf)); + ASSERT_NE(r, -1); + if (r == 0) + break; + out.append(buf, buf + r); + } + ::close(desc); + + EXPECT_EQ("HELLO WORLD", out); +} + +TYPED_TEST(fuse_test, unlink_fails_if_file_is_not_found) { + std::string name{"unlink"}; + auto missing = this->create_file_path(name); + + errno = 0; + EXPECT_EQ(-1, ::unlink(missing.c_str())); + EXPECT_EQ(ENOENT, errno); +} + +TYPED_TEST(fuse_test, unlink_directory_fails) { + std::string name{"unlink"}; + auto dir = this->create_directory_and_test(name); + + errno = 0; + EXPECT_EQ(-1, ::unlink(dir.c_str())); + EXPECT_EQ(EISDIR, errno); + + this->rmdir_and_test(dir); +} + +// TODO revisit +// TYPED_TEST(fuse_test, unlink_on_readonly_mount_erofs_or_skip) { +// std::string name{"unlink"}; +// auto path = this->create_file_path(name); +// +// auto desc = ::open(path.c_str(), O_CREAT | O_WRONLY, 0644); +// if (desc == -1) { +// if (errno == EROFS) { +// GTEST_SKIP(); unlink would return EROFS"; +// } +// FAIL(); +// } +// ::close(desc); +// +// ASSERT_EQ(0, ::unlink(path.c_str())); +// GTEST_SKIP(); RO-specific unlink test skipped"; +// } +} // namespace repertory + +#endif // !defined(_WIN32) From ed01f3de122d22212483261b685945f572c85f75 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Tue, 23 Sep 2025 10:59:34 -0500 Subject: [PATCH 133/136] fix unlink handling --- CHANGELOG.md | 1 + .../include/file_manager/file_manager.hpp | 4 +- .../include/file_manager/i_open_file.hpp | 4 + .../include/file_manager/open_file.hpp | 4 + .../include/file_manager/open_file_base.hpp | 7 ++ .../drives/remote/remote_open_file_table.cpp | 1 + .../src/file_manager/file_manager.cpp | 75 ++++++++++++++----- .../src/file_manager/open_file.cpp | 19 +++++ .../src/file_manager/open_file_base.cpp | 10 +++ 9 files changed, 103 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27b40244..a1fba25d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ * Added check version support to remote mounts * Fixed handling of `FALLOC_FL_KEEP_SIZE` on Linux * Fixed intermittent client hang on remote mount server disconnect +* Fixed `unlink()` return `EBADF` if open file handles exist when removed ## v2.0.7-release diff --git a/repertory/librepertory/include/file_manager/file_manager.hpp b/repertory/librepertory/include/file_manager/file_manager.hpp index 5322e516..0b32d626 100644 --- a/repertory/librepertory/include/file_manager/file_manager.hpp +++ b/repertory/librepertory/include/file_manager/file_manager.hpp @@ -61,6 +61,8 @@ private: i_provider &provider_; private: + std::unordered_map> + closed_file_lookup_; std::unique_ptr mgr_db_; std::atomic next_handle_{0U}; mutable std::recursive_mutex open_file_mtx_; @@ -73,8 +75,6 @@ private: std::unique_ptr upload_thread_; private: - [[nodiscard]] auto close_all(const std::string &api_path) -> bool; - void close_timed_out_files(); [[nodiscard]] auto get_open_file_by_handle(std::uint64_t handle) const diff --git a/repertory/librepertory/include/file_manager/i_open_file.hpp b/repertory/librepertory/include/file_manager/i_open_file.hpp index ee480707..48f41281 100644 --- a/repertory/librepertory/include/file_manager/i_open_file.hpp +++ b/repertory/librepertory/include/file_manager/i_open_file.hpp @@ -110,9 +110,13 @@ public: [[nodiscard]] virtual auto is_modified() const -> bool = 0; + [[nodiscard]] virtual auto is_unlinked() const -> bool = 0; + virtual void remove(std::uint64_t handle) = 0; virtual void remove_all() = 0; + + virtual void set_unlinked(bool value) = 0; }; } // namespace repertory diff --git a/repertory/librepertory/include/file_manager/open_file.hpp b/repertory/librepertory/include/file_manager/open_file.hpp index 9e2492c0..b21dc102 100644 --- a/repertory/librepertory/include/file_manager/open_file.hpp +++ b/repertory/librepertory/include/file_manager/open_file.hpp @@ -111,6 +111,8 @@ public: [[nodiscard]] auto is_complete() const -> bool override; + [[nodiscard]] auto is_removed() const -> bool override; + [[nodiscard]] auto is_write_supported() const -> bool override { return true; } @@ -131,6 +133,8 @@ public: [[nodiscard]] auto resize(std::uint64_t new_file_size) -> api_error override; + void set_removed(bool value) override; + [[nodiscard]] auto write(std::uint64_t write_offset, const data_buffer &data, std::size_t &bytes_written) -> api_error override; }; diff --git a/repertory/librepertory/include/file_manager/open_file_base.hpp b/repertory/librepertory/include/file_manager/open_file_base.hpp index ab527159..addab380 100644 --- a/repertory/librepertory/include/file_manager/open_file_base.hpp +++ b/repertory/librepertory/include/file_manager/open_file_base.hpp @@ -119,6 +119,9 @@ private: }; bool modified_{false}; bool removed_{false}; +#if !defined(_WIN32) + bool unlinked_{false}; +#endif // !defined(_WIN32) private: void file_io_thread(); @@ -208,6 +211,8 @@ public: return fsi_.directory; } + [[nodiscard]] auto is_unlinked() const -> bool override; + [[nodiscard]] auto is_modified() const -> bool override; void remove(std::uint64_t handle) override; @@ -215,6 +220,8 @@ public: void remove_all() override; void set_api_path(const std::string &api_path) override; + + void set_unlinked(bool value) override; }; } // namespace repertory diff --git a/repertory/librepertory/src/drives/remote/remote_open_file_table.cpp b/repertory/librepertory/src/drives/remote/remote_open_file_table.cpp index 7b765afa..83312e3f 100644 --- a/repertory/librepertory/src/drives/remote/remote_open_file_table.cpp +++ b/repertory/librepertory/src/drives/remote/remote_open_file_table.cpp @@ -267,6 +267,7 @@ void remote_open_file_table::remove_and_close_all(const native_handle &handle) { #else // !defined(_WIN32) close(open_handle); #endif // defined(_WIN32) + remove_open_info(open_handle); } } diff --git a/repertory/librepertory/src/file_manager/file_manager.cpp b/repertory/librepertory/src/file_manager/file_manager.cpp index ab4c5b18..410d1810 100644 --- a/repertory/librepertory/src/file_manager/file_manager.cpp +++ b/repertory/librepertory/src/file_manager/file_manager.cpp @@ -78,32 +78,34 @@ file_manager::~file_manager() { void file_manager::close(std::uint64_t handle) { unique_recur_mutex_lock file_lock(open_file_mtx_); - auto closeable_file = get_open_file_by_handle(handle); + bool is_closed{false}; + auto closeable_file = get_open_file_by_handle(handle, is_closed); if (not closeable_file) { return; } file_lock.unlock(); closeable_file->remove(handle); -} -auto file_manager::close_all(const std::string &api_path) -> bool { - REPERTORY_USES_FUNCTION_NAME(); - - unique_recur_mutex_lock file_lock(open_file_mtx_); - auto file_iter = open_file_lookup_.find(api_path); - if (file_iter == open_file_lookup_.end()) { - return false; + file_lock.lock(); + if (not(is_closed && closeable_file->get_open_file_count() == 0U)) { + return; } - auto closeable_file = file_iter->second; - open_file_lookup_.erase(api_path); + closed_file_lookup_.erase(closeable_file->get_api_path()); file_lock.unlock(); - closeable_file->remove_all(); closeable_file->close(); - return closeable_file->get_allocated(); + auto file = utils::file::file{closeable_file->get_source_path()}; + if (file.remove()) { + return; + } + + utils::error::raise_api_path_error( + function_name, closeable_file->get_api_path(), + closeable_file->get_source_path(), utils::get_last_error_code(), + "failed to delete source"); } void file_manager::close_timed_out_files() { @@ -307,6 +309,15 @@ auto file_manager::get_next_handle() -> std::uint64_t { auto file_manager::get_open_file_by_handle(std::uint64_t handle) const -> std::shared_ptr { + { + auto file_iter = std::ranges::find_if( + closed_file_lookup_, [&handle](auto &&item) -> bool { + return item.second->has_handle(handle); + }); + return (file_iter == closed_file_lookup_.end()) ? nullptr + : file_iter->second; + } + auto file_iter = std::ranges::find_if(open_file_lookup_, [&handle](auto &&item) -> bool { return item.second->has_handle(handle); @@ -622,7 +633,7 @@ void file_manager::queue_upload(const std::string &api_path, const std::string &source_path, bool no_lock) { REPERTORY_USES_FUNCTION_NAME(); - if (provider_.is_read_only()) { + if (provider_.is_read_only() || file.is_removed()) { return; } @@ -659,7 +670,7 @@ auto file_manager::remove_file(const std::string &api_path) -> api_error { return res; } - auto allocated = close_all(api_path); + unique_recur_mutex_lock open_lock(open_file_mtx_); unique_mutex_lock upload_lock(upload_mtx_); remove_upload(api_path, true); @@ -667,15 +678,39 @@ auto file_manager::remove_file(const std::string &api_path) -> api_error { upload_notify_.notify_all(); upload_lock.unlock(); - recur_mutex_lock open_lock(open_file_mtx_); - res = provider_.remove_file(api_path); if (res != api_error::success) { return res; } - remove_source_and_shrink_cache(api_path, fsi.source_path, fsi.size, - allocated); + auto file_iter = open_file_lookup_.find(api_path); + if (file_iter == open_file_lookup_.end()) { + return api_error::success; + } + + if (closed_file_lookup_.contains(api_path)) { + auto closed_file = closed_file_lookup_.at(api_path).second; + closed_file_lookup_[api_path] = file_iter.second; + + for (auto &[handle, ofd] : closed_file->get_open_data()) { + closed_file_lookup_.at(api_path).add(handle, ofd); + } + } else { + closed_file_lookup_[api_path] = file_iter.second; + } + + open_file_lookup_.erase(api_path); + + auto closed_file = closed_file_lookup_.at(api_path).second; + closed_file->set_unlinked(true); + open_lock.unlock(); + + res = cache_size_mgr::instance().shrink(closed_file->get_file_size()); + if (res != api_error::success) { + utils::error::raise_api_path_error(function_name, api_path, source_path, + res, "failed to shrink cache"); + } + return api_error::success; } @@ -1081,7 +1116,7 @@ void file_manager::stop() { void file_manager::store_resume(const i_open_file &file) { REPERTORY_USES_FUNCTION_NAME(); - if (provider_.is_read_only()) { + if (provider_.is_read_only() || file.is_removed()) { return; } diff --git a/repertory/librepertory/src/file_manager/open_file.cpp b/repertory/librepertory/src/file_manager/open_file.cpp index bb48ef1b..93312a47 100644 --- a/repertory/librepertory/src/file_manager/open_file.cpp +++ b/repertory/librepertory/src/file_manager/open_file.cpp @@ -236,6 +236,10 @@ auto open_file::close() -> bool { nf_->close(); + if (is_unlinked()) { + return true; + } + if (is_modified()) { if (err == api_error::success) { mgr_.queue_upload(*this); @@ -534,6 +538,10 @@ auto open_file::native_operation( set_file_size(new_file_size); auto now = std::to_string(utils::time::get_time_now()); + if (is_unlinked()) { + return api_error::success: + } + res = get_provider().set_item_meta( get_api_path(), { {META_CHANGED, now}, @@ -653,6 +661,13 @@ auto open_file::resize(std::uint64_t new_file_size) -> api_error { } void open_file::set_modified() { + if (is_unlinked()) { + if (not is_modified()) { + open_file_base::set_modified(true); + } + return; + } + if (not is_modified()) { open_file_base::set_modified(true); mgr_.store_resume(*this); @@ -765,6 +780,10 @@ auto open_file::write(std::uint64_t write_offset, const data_buffer &data, return set_api_error(res); } + if (is_unlinked()) { + return api_error::success: + } + auto now = std::to_string(utils::time::get_time_now()); res = get_provider().set_item_meta(get_api_path(), { {META_CHANGED, now}, diff --git a/repertory/librepertory/src/file_manager/open_file_base.cpp b/repertory/librepertory/src/file_manager/open_file_base.cpp index babfe479..9ffba852 100644 --- a/repertory/librepertory/src/file_manager/open_file_base.cpp +++ b/repertory/librepertory/src/file_manager/open_file_base.cpp @@ -244,6 +244,11 @@ void open_file_base::set_source_path(std::string source_path) { fsi_.source_path = std::move(source_path); } +void open_file_base::set_unlinked(bool value) { + recur_mutex_lock file_lock(file_mtx_); + unlinked_ = value; +} + auto open_file_base::get_filesystem_item() const -> filesystem_item { recur_mutex_lock file_lock(file_mtx_); return fsi_; @@ -308,6 +313,11 @@ auto open_file_base::is_removed() const -> bool { return removed_; } +auto open_file_base::is_unlinked() const -> bool { + recur_mutex_lock file_lock(file_mtx_); + return unlinked_; +} + void open_file_base::notify_io() { mutex_lock io_lock(io_thread_mtx_); io_thread_notify_.notify_all(); From b58314c6d30f9ade0d49b2796eee1661375ffedc Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Thu, 25 Sep 2025 11:23:48 -0500 Subject: [PATCH 134/136] implemented posix-compliant unlink() with fuse hard_remove --- .cspell/words.txt | 1 + CHANGELOG.md | 3 +- .../include/drives/directory_iterator.hpp | 2 +- .../include/drives/fuse/fuse_base.hpp | 15 +- .../include/drives/fuse/fuse_drive.hpp | 8 + .../include/drives/fuse/i_fuse_drive.hpp | 6 + .../fuse/remotefuse/i_remote_instance.hpp | 214 +++++++++--------- .../drives/fuse/remotefuse/remote_client.hpp | 103 ++++----- .../drives/fuse/remotefuse/remote_server.hpp | 105 ++++----- .../include/drives/remote/i_remote_json.hpp | 4 +- .../drives/remote/remote_open_file_table.hpp | 8 +- .../winfsp/remotewinfsp/remote_client.hpp | 4 +- .../winfsp/remotewinfsp/remote_server.hpp | 98 ++++---- .../include/file_manager/file_manager.hpp | 8 +- .../include/file_manager/i_open_file.hpp | 14 +- .../include/file_manager/open_file.hpp | 4 - .../include/file_manager/open_file_base.hpp | 9 +- .../librepertory/include/types/repertory.hpp | 2 + .../include/utils/unix/unix_utils.hpp | 5 +- .../include/utils/windows/windows_utils.hpp | 2 +- .../src/drives/directory_iterator.cpp | 2 +- .../src/drives/fuse/fuse_base.cpp | 19 +- .../src/drives/fuse/fuse_drive.cpp | 63 +++++- .../drives/fuse/remotefuse/remote_client.cpp | 116 +++++----- .../fuse/remotefuse/remote_fuse_drive.cpp | 11 +- .../drives/fuse/remotefuse/remote_server.cpp | 200 ++++++++-------- .../drives/remote/remote_open_file_table.cpp | 8 +- .../winfsp/remotewinfsp/remote_client.cpp | 4 +- .../winfsp/remotewinfsp/remote_server.cpp | 90 ++++---- .../src/file_manager/file_manager.cpp | 97 ++++++-- .../src/file_manager/open_file.cpp | 20 +- .../src/file_manager/open_file_base.cpp | 21 +- .../librepertory/src/types/repertory.cpp | 2 + .../src/utils/unix/unix_utils.cpp | 15 +- .../src/utils/windows/windows_utils.cpp | 2 +- .../include/mocks/mock_open_file.hpp | 11 +- .../repertory_test/src/file_manager_test.cpp | 66 ++++++ .../src/fuse_drive_unlink_test.cpp | 9 +- .../repertory_test/src/open_file_test.cpp | 8 +- 39 files changed, 814 insertions(+), 565 deletions(-) diff --git a/.cspell/words.txt b/.cspell/words.txt index 232718e4..05e23079 100644 --- a/.cspell/words.txt +++ b/.cspell/words.txt @@ -198,6 +198,7 @@ pistream pkgconfig plarge_integer plex +posix println project_enable_fontconfig project_enable_gtkmm diff --git a/CHANGELOG.md b/CHANGELOG.md index a1fba25d..939466dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,7 +28,8 @@ * Added check version support to remote mounts * Fixed handling of `FALLOC_FL_KEEP_SIZE` on Linux * Fixed intermittent client hang on remote mount server disconnect -* Fixed `unlink()` return `EBADF` if open file handles exist when removed +* Implemented POSIX-compliant `unlink()` with FUSE `hard_remove` + * Open handles remain valid after `unlink()` ## v2.0.7-release diff --git a/repertory/librepertory/include/drives/directory_iterator.hpp b/repertory/librepertory/include/drives/directory_iterator.hpp index 372ef86c..d8a0874d 100644 --- a/repertory/librepertory/include/drives/directory_iterator.hpp +++ b/repertory/librepertory/include/drives/directory_iterator.hpp @@ -47,7 +47,7 @@ private: public: #if !defined(_WIN32) - [[nodiscard]] auto fill_buffer(const remote::file_offset &offset, + [[nodiscard]] auto fill_buffer(remote::file_offset offset, fuse_fill_dir_t filler_function, void *buffer, populate_stat_callback populate_stat) -> int; #endif // !defined(_WIN32) diff --git a/repertory/librepertory/include/drives/fuse/fuse_base.hpp b/repertory/librepertory/include/drives/fuse/fuse_base.hpp index b8404ac2..0ef7c20b 100644 --- a/repertory/librepertory/include/drives/fuse/fuse_base.hpp +++ b/repertory/librepertory/include/drives/fuse/fuse_base.hpp @@ -159,6 +159,10 @@ private: [[nodiscard]] static auto init_(struct fuse_conn_info *conn) -> void *; #endif // FUSE_USE_VERSION >= 30 + [[nodiscard]] static auto ioctl_(const char *path, int cmd, void *arg, + struct fuse_file_info *f_info, + unsigned int flags, void *data) -> int; + [[nodiscard]] static auto mkdir_(const char *path, mode_t mode) -> int; [[nodiscard]] static auto open_(const char *path, @@ -394,6 +398,13 @@ protected: virtual auto init_impl(struct fuse_conn_info *conn) -> void *; #endif // FUSE_USE_VERSION >= 30 + [[nodiscard]] virtual auto ioctl_impl(std::string /*api_path*/, int /* cmd */, + void * /* arg */, + struct fuse_file_info * /*f_info*/) + -> api_error { + return api_error::no_tty; + } + [[nodiscard]] virtual auto mkdir_impl(std::string /*api_path*/, mode_t /*mode*/) -> api_error { return api_error::not_implemented; @@ -471,14 +482,14 @@ protected: getxattr_impl(std::string /*api_path*/, const char * /*name*/, char * /*value*/, size_t /*size*/, uint32_t /*position*/, int & /*attribute_size*/) -> api_error { - return api_error::not_implemented; + return api_error::xattr_not_found; } #else // !defined(__APPLE__) [[nodiscard]] virtual auto getxattr_impl(std::string /*api_path*/, const char * /*name*/, char * /*value*/, size_t /*size*/, int & /*attribute_size*/) -> api_error { - return api_error::not_implemented; + return api_error::xattr_not_found; } #endif // defined(__APPLE__) diff --git a/repertory/librepertory/include/drives/fuse/fuse_drive.hpp b/repertory/librepertory/include/drives/fuse/fuse_drive.hpp index dfba150e..4c8b3e91 100644 --- a/repertory/librepertory/include/drives/fuse/fuse_drive.hpp +++ b/repertory/librepertory/include/drives/fuse/fuse_drive.hpp @@ -150,6 +150,10 @@ protected: auto init_impl(struct fuse_conn_info *conn) -> void * override; #endif // FUSE_USE_VERSION >= 30 + [[nodiscard]] auto ioctl_impl(std::string api_path, int cmd, void *arg, + struct fuse_file_info *f_info) + -> api_error override; + [[nodiscard]] auto mkdir_impl(std::string api_path, mode_t mode) -> api_error override; @@ -307,6 +311,10 @@ public: std::string &value) const -> api_error override; + [[nodiscard]] auto get_item_stat(std::uint64_t handle, + struct stat64 *u_stat) const + -> api_error override; + [[nodiscard]] auto get_total_drive_space() const -> std::uint64_t override; [[nodiscard]] auto get_total_item_count() const -> std::uint64_t override; diff --git a/repertory/librepertory/include/drives/fuse/i_fuse_drive.hpp b/repertory/librepertory/include/drives/fuse/i_fuse_drive.hpp index e70624f7..d51143b7 100644 --- a/repertory/librepertory/include/drives/fuse/i_fuse_drive.hpp +++ b/repertory/librepertory/include/drives/fuse/i_fuse_drive.hpp @@ -26,6 +26,8 @@ #include "types/repertory.hpp" namespace repertory { +inline constexpr const int repertory_ioctl_fd_command = 0x102010; + class i_fuse_drive { INTERFACE_SETUP(i_fuse_drive); @@ -57,6 +59,10 @@ public: std::string &value) const -> api_error = 0; + [[nodiscard]] virtual auto get_item_stat(std::uint64_t handle, + struct stat64 *u_stat) const + -> api_error = 0; + [[nodiscard]] virtual auto get_total_drive_space() const -> std::uint64_t = 0; [[nodiscard]] virtual auto get_total_item_count() const -> std::uint64_t = 0; diff --git a/repertory/librepertory/include/drives/fuse/remotefuse/i_remote_instance.hpp b/repertory/librepertory/include/drives/fuse/remotefuse/i_remote_instance.hpp index 4999da86..68bbcd1d 100644 --- a/repertory/librepertory/include/drives/fuse/remotefuse/i_remote_instance.hpp +++ b/repertory/librepertory/include/drives/fuse/remotefuse/i_remote_instance.hpp @@ -30,166 +30,170 @@ class i_remote_instance : public virtual i_remote_json { INTERFACE_SETUP(i_remote_instance); public: - [[nodiscard]] virtual auto - fuse_access(const char *path, - const std::int32_t &mask) -> packet::error_type = 0; + [[nodiscard]] virtual auto fuse_access(const char *path, std::int32_t mask) + -> packet::error_type = 0; + + [[nodiscard]] virtual auto fuse_chflags(const char *path, std::uint32_t flags) + -> packet::error_type = 0; + + [[nodiscard]] virtual auto fuse_chmod(const char *path, + remote::file_mode mode) + -> packet::error_type = 0; + + [[nodiscard]] virtual auto fuse_chown(const char *path, remote::user_id uid, + remote::group_id gid) + -> packet::error_type = 0; [[nodiscard]] virtual auto - fuse_chflags(const char *path, std::uint32_t flags) -> packet::error_type = 0; + fuse_create(const char *path, remote::file_mode mode, + const remote::open_flags &flags, remote::file_handle &handle) + -> packet::error_type = 0; - [[nodiscard]] virtual auto - fuse_chmod(const char *path, - const remote::file_mode &mode) -> packet::error_type = 0; - - [[nodiscard]] virtual auto - fuse_chown(const char *path, const remote::user_id &uid, - const remote::group_id &gid) -> packet::error_type = 0; - - [[nodiscard]] virtual auto - fuse_create(const char *path, const remote::file_mode &mode, - const remote::open_flags &flags, - remote::file_handle &handle) -> packet::error_type = 0; [[nodiscard]] virtual auto fuse_destroy() -> packet::error_type = 0; /*[[nodiscard]] virtual packet::error_type fuse_fallocate(const char *path, - const std::int32_t &mode, const remote::file_offset &offset, const - remote::file_offset &length, const remote::file_offset &length, const - remote::file_handle &handle) = 0;*/ + std::int32_t mode, remote::file_offset offset, const + remote::file_offset length, remote::file_offset length, const + remote::file_handle handle) = 0;*/ [[nodiscard]] virtual auto fuse_fgetattr(const char *path, remote::stat &r_stat, bool &directory, - const remote::file_handle &handle) -> packet::error_type = 0; + remote::file_handle handle) -> packet::error_type = 0; - [[nodiscard]] virtual auto - fuse_fsetattr_x(const char *path, const remote::setattr_x &attr, - const remote::file_handle &handle) -> packet::error_type = 0; + [[nodiscard]] virtual auto fuse_fsetattr_x(const char *path, + const remote::setattr_x &attr, + remote::file_handle handle) + -> packet::error_type = 0; - [[nodiscard]] virtual auto - fuse_fsync(const char *path, const std::int32_t &datasync, - const remote::file_handle &handle) -> packet::error_type = 0; + [[nodiscard]] virtual auto fuse_fsync(const char *path, std::int32_t datasync, + remote::file_handle handle) + -> packet::error_type = 0; - [[nodiscard]] virtual auto - fuse_ftruncate(const char *path, const remote::file_offset &size, - const remote::file_handle &handle) -> packet::error_type = 0; + [[nodiscard]] virtual auto fuse_ftruncate(const char *path, + remote::file_offset size, + remote::file_handle handle) + -> packet::error_type = 0; - [[nodiscard]] virtual auto - fuse_getattr(const char *path, remote::stat &r_stat, - bool &directory) -> packet::error_type = 0; + [[nodiscard]] virtual auto fuse_getattr(const char *path, + remote::stat &r_stat, bool &directory) + -> packet::error_type = 0; /*[[nodiscard]] virtual packet::error_type fuse_getxattr(const char *path, - const char *name, char *value, const remote::file_size &size) = 0; + const char *name, char *value, remote::file_size size) = 0; [[nodiscard]] virtual packet::error_type fuse_getxattrOSX(const char *path, - const char *name, char *value, const remote::file_size &size, std::uint32_t + const char *name, char *value, remote::file_size size, std::uint32_t position) = 0;*/ - [[nodiscard]] virtual auto - fuse_getxtimes(const char *path, remote::file_time &bkuptime, - remote::file_time &crtime) -> packet::error_type = 0; + [[nodiscard]] virtual auto fuse_getxtimes(const char *path, + remote::file_time &bkuptime, + remote::file_time &crtime) + -> packet::error_type = 0; [[nodiscard]] virtual auto fuse_init() -> packet::error_type = 0; [[nodiscard]] /*virtual packet::error_type fuse_listxattr(const char *path, - char *buffer, const remote::file_size &size) = 0;*/ + char *buffer, remote::file_size size) = 0;*/ [[nodiscard]] virtual auto - fuse_mkdir(const char *path, - const remote::file_mode &mode) -> packet::error_type = 0; + fuse_mkdir(const char *path, remote::file_mode mode) + -> packet::error_type = 0; + + [[nodiscard]] virtual auto fuse_open(const char *path, + const remote::open_flags &flags, + remote::file_handle &handle) + -> packet::error_type = 0; + + [[nodiscard]] virtual auto fuse_opendir(const char *path, + remote::file_handle &handle) + -> packet::error_type = 0; [[nodiscard]] virtual auto - fuse_open(const char *path, const remote::open_flags &flags, - remote::file_handle &handle) -> packet::error_type = 0; + fuse_read(const char *path, char *buffer, remote::file_size read_size, + remote::file_offset read_offset, remote::file_handle handle) + -> packet::error_type = 0; [[nodiscard]] virtual auto - fuse_opendir(const char *path, - remote::file_handle &handle) -> packet::error_type = 0; + fuse_readdir(const char *path, remote::file_offset offset, + remote::file_handle handle, std::string &item_path) + -> packet::error_type = 0; - [[nodiscard]] virtual auto - fuse_read(const char *path, char *buffer, const remote::file_size &read_size, - const remote::file_offset &read_offset, - const remote::file_handle &handle) -> packet::error_type = 0; + [[nodiscard]] virtual auto fuse_release(const char *path, + remote::file_handle handle) + -> packet::error_type = 0; - [[nodiscard]] virtual auto - fuse_readdir(const char *path, const remote::file_offset &offset, - const remote::file_handle &handle, - std::string &item_path) -> packet::error_type = 0; - - [[nodiscard]] virtual auto - fuse_release(const char *path, - const remote::file_handle &handle) -> packet::error_type = 0; - - [[nodiscard]] virtual auto - fuse_releasedir(const char *path, - const remote::file_handle &handle) -> packet::error_type = 0; + [[nodiscard]] virtual auto fuse_releasedir(const char *path, + remote::file_handle handle) + -> packet::error_type = 0; //[[nodiscard]] virtual packet::error_type fuse_removexattr(const char *path, // const char *name) = // 0; - [[nodiscard]] virtual auto - fuse_rename(const char *from, const char *to) -> packet::error_type = 0; + [[nodiscard]] virtual auto fuse_rename(const char *from, const char *to) + -> packet::error_type = 0; - [[nodiscard]] virtual auto - fuse_rmdir(const char *path) -> packet::error_type = 0; + [[nodiscard]] virtual auto fuse_rmdir(const char *path) + -> packet::error_type = 0; - [[nodiscard]] virtual auto - fuse_setattr_x(const char *path, - remote::setattr_x &attr) -> packet::error_type = 0; + [[nodiscard]] virtual auto fuse_setattr_x(const char *path, + remote::setattr_x &attr) + -> packet::error_type = 0; - [[nodiscard]] virtual auto - fuse_setbkuptime(const char *path, - const remote::file_time &bkuptime) -> packet::error_type = 0; + [[nodiscard]] virtual auto fuse_setbkuptime(const char *path, + remote::file_time bkuptime) + -> packet::error_type = 0; - [[nodiscard]] virtual auto - fuse_setchgtime(const char *path, - const remote::file_time &chgtime) -> packet::error_type = 0; + [[nodiscard]] virtual auto fuse_setchgtime(const char *path, + remote::file_time chgtime) + -> packet::error_type = 0; - [[nodiscard]] virtual auto - fuse_setcrtime(const char *path, - const remote::file_time &crtime) -> packet::error_type = 0; + [[nodiscard]] virtual auto fuse_setcrtime(const char *path, + remote::file_time crtime) + -> packet::error_type = 0; - [[nodiscard]] virtual auto - fuse_setvolname(const char *volname) -> packet::error_type = 0; + [[nodiscard]] virtual auto fuse_setvolname(const char *volname) + -> packet::error_type = 0; /*[[nodiscard]] virtual packet::error_type fuse_setxattr(const char *path, - const char *name, const char *value, const remote::file_size &size, const + const char *name, const char *value, remote::file_size size, const std::int32_t &flags) = 0; [[nodiscard]] virtual packet::error_type fuse_setxattr_osx(const char *path, - const char *name, const char *value, const remote::file_size &size, const + const char *name, const char *value, remote::file_size size, const std::int32_t &flags, std::uint32_t position) = 0;*/ - [[nodiscard]] virtual auto - fuse_statfs(const char *path, std::uint64_t frsize, - remote::statfs &r_stat) -> packet::error_type = 0; + [[nodiscard]] virtual auto fuse_statfs(const char *path, std::uint64_t frsize, + remote::statfs &r_stat) + -> packet::error_type = 0; [[nodiscard]] virtual auto - fuse_statfs_x(const char *path, std::uint64_t bsize, - remote::statfs_x &r_stat) -> packet::error_type = 0; + fuse_statfs_x(const char *path, std::uint64_t bsize, remote::statfs_x &r_stat) + -> packet::error_type = 0; + + [[nodiscard]] virtual auto fuse_truncate(const char *path, + remote::file_offset size) + -> packet::error_type = 0; + + [[nodiscard]] virtual auto fuse_unlink(const char *path) + -> packet::error_type = 0; + + [[nodiscard]] virtual auto fuse_utimens(const char *path, + const remote::file_time *tv, + std::uint64_t op0, std::uint64_t op1) + -> packet::error_type = 0; [[nodiscard]] virtual auto - fuse_truncate(const char *path, - const remote::file_offset &size) -> packet::error_type = 0; + fuse_write(const char *path, const char *buffer, remote::file_size writeSize, + remote::file_offset writeOffset, remote::file_handle handle) + -> packet::error_type = 0; [[nodiscard]] virtual auto - fuse_unlink(const char *path) -> packet::error_type = 0; + fuse_write_base64(const char *path, const char *buffer, + remote::file_size writeSize, + remote::file_offset writeOffset, remote::file_handle handle) + -> packet::error_type = 0; - [[nodiscard]] virtual auto - fuse_utimens(const char *path, const remote::file_time *tv, std::uint64_t op0, - std::uint64_t op1) -> packet::error_type = 0; - - [[nodiscard]] virtual auto - fuse_write(const char *path, const char *buffer, - const remote::file_size &writeSize, - const remote::file_offset &writeOffset, - const remote::file_handle &handle) -> packet::error_type = 0; - - [[nodiscard]] virtual auto fuse_write_base64( - const char *path, const char *buffer, const remote::file_size &writeSize, - const remote::file_offset &writeOffset, - const remote::file_handle &handle) -> packet::error_type = 0; - - virtual void set_fuse_uid_gid(const remote::user_id &uid, - const remote::group_id &gid) = 0; + virtual void set_fuse_uid_gid(remote::user_id uid, remote::group_id gid) = 0; }; using remote_instance_factory = diff --git a/repertory/librepertory/include/drives/fuse/remotefuse/remote_client.hpp b/repertory/librepertory/include/drives/fuse/remotefuse/remote_client.hpp index 92577046..4ce8e184 100644 --- a/repertory/librepertory/include/drives/fuse/remotefuse/remote_client.hpp +++ b/repertory/librepertory/include/drives/fuse/remotefuse/remote_client.hpp @@ -43,43 +43,41 @@ private: public: [[nodiscard]] auto check() -> packet::error_type; - [[nodiscard]] auto fuse_access(const char *path, const std::int32_t &mask) + [[nodiscard]] auto fuse_access(const char *path, std::int32_t mask) -> packet::error_type override; [[nodiscard]] auto fuse_chflags(const char *path, std::uint32_t flags) -> packet::error_type override; - [[nodiscard]] auto fuse_chmod(const char *path, const remote::file_mode &mode) + [[nodiscard]] auto fuse_chmod(const char *path, remote::file_mode mode) -> packet::error_type override; - [[nodiscard]] auto fuse_chown(const char *path, const remote::user_id &uid, - const remote::group_id &gid) + [[nodiscard]] auto fuse_chown(const char *path, remote::user_id uid, + remote::group_id gid) -> packet::error_type override; [[nodiscard]] auto fuse_destroy() -> packet::error_type override; [[nodiscard]] /*packet::error_type fuse_fallocate(const char *path, const - std::int32_t &mode, const remote::file_offset &offset, const - remote::file_offset &length, const remote::file_handle + std::int32_t &mode, remote::file_offset offset, const + remote::file_offset length, const remote::file_handle &handle) override ;*/ [[nodiscard]] auto fuse_fgetattr(const char *path, remote::stat &r_stat, bool &directory, - const remote::file_handle &handle) - -> packet::error_type override; + remote::file_handle handle) -> packet::error_type override; [[nodiscard]] auto fuse_fsetattr_x(const char *path, const remote::setattr_x &attr, - const remote::file_handle &handle) + remote::file_handle handle) -> packet::error_type override; - [[nodiscard]] auto fuse_fsync(const char *path, const std::int32_t &datasync, - const remote::file_handle &handle) + [[nodiscard]] auto fuse_fsync(const char *path, std::int32_t datasync, + remote::file_handle handle) -> packet::error_type override; - [[nodiscard]] auto fuse_ftruncate(const char *path, - const remote::file_offset &size, - const remote::file_handle &handle) + [[nodiscard]] auto fuse_ftruncate(const char *path, remote::file_offset size, + remote::file_handle handle) -> packet::error_type override; [[nodiscard]] auto fuse_getattr(const char *path, remote::stat &r_stat, @@ -87,10 +85,10 @@ public: -> packet::error_type override; /*[[nodiscard]] packet::error_type fuse_getxattr(const char *path, const char - *name, char *value, const remote::file_size &size) override ; + *name, char *value, remote::file_size size) override ; [[nodiscard]] packet::error_type fuse_getxattrOSX(const char *path, const char - *name, char *value, const remote::file_size &size, std::uint32_t position) + *name, char *value, remote::file_size size, std::uint32_t position) override ;*/ [[nodiscard]] auto fuse_getxtimes(const char *path, @@ -101,17 +99,17 @@ public: [[nodiscard]] auto fuse_init() -> packet::error_type override; /*[[nodiscard]] packet::error_type fuse_listxattr(const char *path, char - *buffer, const remote::file_size &size) override ;*/ + *buffer, remote::file_size size) override ;*/ - [[nodiscard]] auto fuse_mkdir(const char *path, const remote::file_mode &mode) + [[nodiscard]] auto fuse_mkdir(const char *path, remote::file_mode mode) -> packet::error_type override; [[nodiscard]] auto fuse_opendir(const char *path, remote::file_handle &handle) -> packet::error_type override; - [[nodiscard]] auto - fuse_create(const char *path, const remote::file_mode &mode, - const remote::open_flags &flags, remote::file_handle &handle) + [[nodiscard]] auto fuse_create(const char *path, remote::file_mode mode, + const remote::open_flags &flags, + remote::file_handle &handle) -> packet::error_type override; [[nodiscard]] auto fuse_open(const char *path, @@ -119,26 +117,24 @@ public: remote::file_handle &handle) -> packet::error_type override; - [[nodiscard]] auto fuse_read(const char *path, char *buffer, - const remote::file_size &read_size, - const remote::file_offset &read_offset, - const remote::file_handle &handle) + [[nodiscard]] auto + fuse_read(const char *path, char *buffer, remote::file_size read_size, + remote::file_offset read_offset, remote::file_handle handle) -> packet::error_type override; [[nodiscard]] auto fuse_rename(const char *from, const char *to) -> packet::error_type override; - [[nodiscard]] auto - fuse_readdir(const char *path, const remote::file_offset &offset, - const remote::file_handle &handle, std::string &item_path) + [[nodiscard]] auto fuse_readdir(const char *path, remote::file_offset offset, + remote::file_handle handle, + std::string &item_path) -> packet::error_type override; - [[nodiscard]] auto fuse_release(const char *path, - const remote::file_handle &handle) + [[nodiscard]] auto fuse_release(const char *path, remote::file_handle handle) -> packet::error_type override; [[nodiscard]] auto fuse_releasedir(const char *path, - const remote::file_handle &handle) + remote::file_handle handle) -> packet::error_type override; /*[[nodiscard]] packet::error_type fuse_removexattr(const char *path, const @@ -152,26 +148,25 @@ public: -> packet::error_type override; [[nodiscard]] auto fuse_setbkuptime(const char *path, - const remote::file_time &bkuptime) + remote::file_time bkuptime) -> packet::error_type override; [[nodiscard]] auto fuse_setchgtime(const char *path, - const remote::file_time &chgtime) + remote::file_time chgtime) -> packet::error_type override; - [[nodiscard]] auto fuse_setcrtime(const char *path, - const remote::file_time &crtime) + [[nodiscard]] auto fuse_setcrtime(const char *path, remote::file_time crtime) -> packet::error_type override; [[nodiscard]] auto fuse_setvolname(const char *volname) -> packet::error_type override; [[nodiscard]] /*packet::error_type fuse_setxattr(const char *path, const char - *name, const char *value, const remote::file_size &size, const std::int32_t + *name, const char *value, remote::file_size size, const std::int32_t &flags) override ; [[nodiscard]] packet::error_type fuse_setxattr_osx(const char *path, const - char *name, const char *value, const remote::file_size &size, const + char *name, const char *value, remote::file_size size, const std::int32_t &flags, std::uint32_t position) override ;*/ [[nodiscard]] auto @@ -182,8 +177,7 @@ public: remote::statfs_x &r_stat) -> packet::error_type override; - [[nodiscard]] auto fuse_truncate(const char *path, - const remote::file_offset &size) + [[nodiscard]] auto fuse_truncate(const char *path, remote::file_offset size) -> packet::error_type override; [[nodiscard]] auto fuse_unlink(const char *path) @@ -193,33 +187,32 @@ public: std::uint64_t op0, std::uint64_t op1) -> packet::error_type override; - [[nodiscard]] auto fuse_write(const char *path, const char *buffer, - const remote::file_size &write_size, - const remote::file_offset &write_offset, - const remote::file_handle &handle) + [[nodiscard]] auto + fuse_write(const char *path, const char *buffer, remote::file_size write_size, + remote::file_offset write_offset, remote::file_handle handle) -> packet::error_type override; [[nodiscard]] auto fuse_write_base64(const char *path, const char *buffer, - const remote::file_size &write_size, - const remote::file_offset &write_offset, - const remote::file_handle &handle) + remote::file_size write_size, + remote::file_offset write_offset, + remote::file_handle handle) -> packet::error_type override; [[nodiscard]] auto json_create_directory_snapshot(const std::string &path, json &json_data) -> packet::error_type override; - [[nodiscard]] auto json_read_directory_snapshot( - const std::string &path, const remote::file_handle &handle, - std::uint32_t page, json &json_data) -> packet::error_type override; - - [[nodiscard]] auto - json_release_directory_snapshot(const std::string &path, - const remote::file_handle &handle) + [[nodiscard]] auto json_read_directory_snapshot(const std::string &path, + remote::file_handle handle, + std::uint32_t page, + json &json_data) -> packet::error_type override; - void set_fuse_uid_gid(const remote::user_id &uid, - const remote::group_id &gid) override; + [[nodiscard]] auto json_release_directory_snapshot(const std::string &path, + remote::file_handle handle) + -> packet::error_type override; + + void set_fuse_uid_gid(remote::user_id uid, remote::group_id gid) override; }; } // namespace remote_fuse } // namespace repertory diff --git a/repertory/librepertory/include/drives/fuse/remotefuse/remote_server.hpp b/repertory/librepertory/include/drives/fuse/remotefuse/remote_server.hpp index d5e73e14..048203bb 100644 --- a/repertory/librepertory/include/drives/fuse/remotefuse/remote_server.hpp +++ b/repertory/librepertory/include/drives/fuse/remotefuse/remote_server.hpp @@ -53,8 +53,8 @@ private: remote::file_info &r_info) -> packet::error_type; - void populate_file_info(const std::string &api_path, const UINT64 &file_size, - const UINT32 &attributes, remote::file_info &r_info); + void populate_file_info(const std::string &api_path, UINT64 file_size, + UINT32 attributes, remote::file_info &r_info); static void populate_stat(const struct stat64 &u_stat, remote::stat &r_stat); @@ -63,48 +63,46 @@ private: public: // FUSE Layer - [[nodiscard]] auto fuse_access(const char *path, const std::int32_t &mask) + [[nodiscard]] auto fuse_access(const char *path, std::int32_t mask) -> packet::error_type override; [[nodiscard]] auto fuse_chflags(const char *path, std::uint32_t flags) -> packet::error_type override; - [[nodiscard]] auto fuse_chmod(const char *path, const remote::file_mode &mode) + [[nodiscard]] auto fuse_chmod(const char *path, remote::file_mode mode) -> packet::error_type override; - [[nodiscard]] auto fuse_chown(const char *path, const remote::user_id &uid, - const remote::group_id &gid) + [[nodiscard]] auto fuse_chown(const char *path, remote::user_id uid, + remote::group_id gid) -> packet::error_type override; - [[nodiscard]] auto - fuse_create(const char *path, const remote::file_mode &mode, - const remote::open_flags &flags, remote::file_handle &handle) + [[nodiscard]] auto fuse_create(const char *path, remote::file_mode mode, + const remote::open_flags &flags, + remote::file_handle &handle) -> packet::error_type override; [[nodiscard]] auto fuse_destroy() -> packet::error_type override; /*[[nodiscard]] packet::error_type fuse_fallocate(const char *path, const - std::int32_t &mode, const remote::file_offset &offset, const - remote::file_offset &length, const remote::file_handle &handle) override + std::int32_t &mode, remote::file_offset offset, const + remote::file_offset length, remote::file_handle handle) override ;*/ [[nodiscard]] auto fuse_fgetattr(const char *path, remote::stat &r_stat, - bool &directory, - const remote::file_handle &handle) + bool &directory, remote::file_handle handle) -> packet::error_type override; [[nodiscard]] auto fuse_fsetattr_x(const char *path, const remote::setattr_x &attr, - const remote::file_handle &handle) + remote::file_handle handle) -> packet::error_type override; - [[nodiscard]] auto fuse_fsync(const char *path, const std::int32_t &datasync, - const remote::file_handle &handle) + [[nodiscard]] auto fuse_fsync(const char *path, std::int32_t datasync, + remote::file_handle handle) -> packet::error_type override; - [[nodiscard]] auto fuse_ftruncate(const char *path, - const remote::file_offset &size, - const remote::file_handle &handle) + [[nodiscard]] auto fuse_ftruncate(const char *path, remote::file_offset size, + remote::file_handle handle) -> packet::error_type override; [[nodiscard]] auto fuse_getattr(const char *path, remote::stat &r_stat, @@ -112,10 +110,10 @@ public: -> packet::error_type override; /*[[nodiscard]] packet::error_type fuse_getxattr(const char *path, const char - *name, char *value, const remote::file_size &size) override ; + *name, char *value, remote::file_size size) override ; [[nodiscard]] packet::error_type fuse_getxattrOSX(const char *path, const char - *name, char *value, const remote::file_size &size, std::uint32_t position) + *name, char *value, remote::file_size size, std::uint32_t position) override ;*/ [[nodiscard]] auto fuse_getxtimes(const char *path, @@ -126,10 +124,10 @@ public: [[nodiscard]] auto fuse_init() -> packet::error_type override; [[nodiscard]] /*packet::error_type fuse_listxattr(const char *path, char - *buffer, const remote::file_size &size) override ;*/ + *buffer, remote::file_size size) override ;*/ [[nodiscard]] auto - fuse_mkdir(const char *path, const remote::file_mode &mode) + fuse_mkdir(const char *path, remote::file_mode mode) -> packet::error_type override; [[nodiscard]] auto fuse_open(const char *path, @@ -140,26 +138,24 @@ public: [[nodiscard]] auto fuse_opendir(const char *path, remote::file_handle &handle) -> packet::error_type override; - [[nodiscard]] auto fuse_read(const char *path, char *buffer, - const remote::file_size &read_size, - const remote::file_offset &read_offset, - const remote::file_handle &handle) + [[nodiscard]] auto + fuse_read(const char *path, char *buffer, remote::file_size read_size, + remote::file_offset read_offset, remote::file_handle handle) -> packet::error_type override; [[nodiscard]] auto fuse_rename(const char *from, const char *to) -> packet::error_type override; - [[nodiscard]] auto - fuse_readdir(const char *path, const remote::file_offset &offset, - const remote::file_handle &handle, std::string &item_path) + [[nodiscard]] auto fuse_readdir(const char *path, remote::file_offset offset, + remote::file_handle handle, + std::string &item_path) -> packet::error_type override; - [[nodiscard]] auto fuse_release(const char *path, - const remote::file_handle &handle) + [[nodiscard]] auto fuse_release(const char *path, remote::file_handle handle) -> packet::error_type override; [[nodiscard]] auto fuse_releasedir(const char *path, - const remote::file_handle &handle) + remote::file_handle handle) -> packet::error_type override; /*[[nodiscard]] packet::error_type fuse_removexattr(const char *path, const @@ -173,26 +169,25 @@ public: -> packet::error_type override; [[nodiscard]] auto fuse_setbkuptime(const char *path, - const remote::file_time &bkuptime) + remote::file_time bkuptime) -> packet::error_type override; [[nodiscard]] auto fuse_setchgtime(const char *path, - const remote::file_time &chgtime) + remote::file_time chgtime) -> packet::error_type override; - [[nodiscard]] auto fuse_setcrtime(const char *path, - const remote::file_time &crtime) + [[nodiscard]] auto fuse_setcrtime(const char *path, remote::file_time crtime) -> packet::error_type override; [[nodiscard]] auto fuse_setvolname(const char *volname) -> packet::error_type override; /*[[nodiscard]] packet::error_type fuse_setxattr(const char *path, const char - *name, const char *value, const remote::file_size &size, const std::int32_t + *name, const char *value, remote::file_size size, const std::int32_t &flags) override ; [[nodiscard]] packet::error_type fuse_setxattr_osx(const char *path, const - char *name, const char *value, const remote::file_size &size, const + char *name, const char *value, remote::file_size size, const std::int32_t &flags, std::uint32_t position) override ;*/ [[nodiscard]] auto fuse_statfs(const char *path, std::uint64_t frsize, @@ -203,8 +198,7 @@ public: remote::statfs_x &r_stat) -> packet::error_type override; - [[nodiscard]] auto fuse_truncate(const char *path, - const remote::file_offset &size) + [[nodiscard]] auto fuse_truncate(const char *path, remote::file_offset size) -> packet::error_type override; [[nodiscard]] auto fuse_unlink(const char *path) @@ -214,20 +208,18 @@ public: std::uint64_t op0, std::uint64_t op1) -> packet::error_type override; - [[nodiscard]] auto fuse_write(const char *path, const char *buffer, - const remote::file_size &write_size, - const remote::file_offset &write_offset, - const remote::file_handle &handle) + [[nodiscard]] auto + fuse_write(const char *path, const char *buffer, remote::file_size write_size, + remote::file_offset write_offset, remote::file_handle handle) -> packet::error_type override; [[nodiscard]] auto fuse_write_base64(const char *path, const char *buffer, - const remote::file_size &write_size, - const remote::file_offset &write_offset, - const remote::file_handle &handle) + remote::file_size write_size, + remote::file_offset write_offset, + remote::file_handle handle) -> packet::error_type override; - void set_fuse_uid_gid(const remote::user_id &, - const remote::group_id &) override {} + void set_fuse_uid_gid(remote::user_id, remote::group_id) override {} // JSON Layer [[nodiscard]] auto winfsp_get_dir_buffer(PVOID /*file_desc*/, @@ -240,13 +232,14 @@ public: json &json_data) -> packet::error_type override; - [[nodiscard]] auto json_read_directory_snapshot( - const std::string &path, const remote::file_handle &handle, - std::uint32_t page, json &json_data) -> packet::error_type override; + [[nodiscard]] auto json_read_directory_snapshot(const std::string &path, + remote::file_handle handle, + std::uint32_t page, + json &json_data) + -> packet::error_type override; - [[nodiscard]] auto - json_release_directory_snapshot(const std::string &path, - const remote::file_handle &handle) + [[nodiscard]] auto json_release_directory_snapshot(const std::string &path, + remote::file_handle handle) -> packet::error_type override; // WinFSP Layer diff --git a/repertory/librepertory/include/drives/remote/i_remote_json.hpp b/repertory/librepertory/include/drives/remote/i_remote_json.hpp index 74067975..48265c13 100644 --- a/repertory/librepertory/include/drives/remote/i_remote_json.hpp +++ b/repertory/librepertory/include/drives/remote/i_remote_json.hpp @@ -34,12 +34,12 @@ public: json &json_data) -> packet::error_type = 0; [[nodiscard]] virtual auto json_read_directory_snapshot( - const std::string &path, const remote::file_handle &handle, + const std::string &path, remote::file_handle handle, std::uint32_t page, json &json_data) -> packet::error_type = 0; [[nodiscard]] virtual auto json_release_directory_snapshot( const std::string &path, - const remote::file_handle &handle) -> packet::error_type = 0; + remote::file_handle handle) -> packet::error_type = 0; }; } // namespace repertory diff --git a/repertory/librepertory/include/drives/remote/remote_open_file_table.hpp b/repertory/librepertory/include/drives/remote/remote_open_file_table.hpp index f21bf480..826cde0c 100644 --- a/repertory/librepertory/include/drives/remote/remote_open_file_table.hpp +++ b/repertory/librepertory/include/drives/remote/remote_open_file_table.hpp @@ -80,7 +80,7 @@ protected: [[nodiscard]] auto has_open_directory(const std::string &client_id, std::uint64_t handle) -> bool; - [[nodiscard]] auto has_compat_open_info(const remote::file_handle &handle, + [[nodiscard]] auto has_compat_open_info(remote::file_handle handle, int error_return) -> int; template @@ -95,7 +95,7 @@ protected: void remove_and_close_all(const native_handle &handle); - void remove_compat_open_info(const remote::file_handle &handle); + void remove_compat_open_info(remote::file_handle handle); auto remove_directory(const std::string &client_id, std::uint64_t handle) -> bool; @@ -104,10 +104,10 @@ protected: void set_client_id(const native_handle &handle, const std::string &client_id); - void set_compat_client_id(const remote::file_handle &handle, + void set_compat_client_id(remote::file_handle handle, const std::string &client_id); - void set_compat_open_info(const remote::file_handle &handle, + void set_compat_open_info(remote::file_handle handle, const std::string &file_path); void set_open_info(const native_handle &handle, open_info op_info); diff --git a/repertory/librepertory/include/drives/winfsp/remotewinfsp/remote_client.hpp b/repertory/librepertory/include/drives/winfsp/remotewinfsp/remote_client.hpp index 226c52bb..36879427 100644 --- a/repertory/librepertory/include/drives/winfsp/remotewinfsp/remote_client.hpp +++ b/repertory/librepertory/include/drives/winfsp/remotewinfsp/remote_client.hpp @@ -55,12 +55,12 @@ public: -> packet::error_type override; [[nodiscard]] auto json_read_directory_snapshot( - const std::string &path, const remote::file_handle &handle, + const std::string &path, remote::file_handle handle, std::uint32_t page, json &json_data) -> packet::error_type override; [[nodiscard]] auto json_release_directory_snapshot(const std::string &path, - const remote::file_handle &handle) + remote::file_handle handle) -> packet::error_type override; [[nodiscard]] auto winfsp_can_delete(PVOID file_desc, PWSTR file_name) diff --git a/repertory/librepertory/include/drives/winfsp/remotewinfsp/remote_server.hpp b/repertory/librepertory/include/drives/winfsp/remotewinfsp/remote_server.hpp index d7c662fc..3644f46c 100644 --- a/repertory/librepertory/include/drives/winfsp/remotewinfsp/remote_server.hpp +++ b/repertory/librepertory/include/drives/winfsp/remotewinfsp/remote_server.hpp @@ -58,42 +58,41 @@ private: public: // FUSE Layer - [[nodiscard]] auto fuse_access(const char *path, const std::int32_t &mask) + [[nodiscard]] auto fuse_access(const char *path, std::int32_t mask) -> packet::error_type override; [[nodiscard]] auto fuse_chflags(const char *path, std::uint32_t flags) -> packet::error_type override; - [[nodiscard]] auto fuse_chmod(const char *path, const remote::file_mode &mode) + [[nodiscard]] auto fuse_chmod(const char *path, remote::file_mode mode) -> packet::error_type override; - [[nodiscard]] auto fuse_chown(const char *path, const remote::user_id &uid, - const remote::group_id &gid) + [[nodiscard]] auto fuse_chown(const char *path, remote::user_id uid, + remote::group_id gid) -> packet::error_type override; [[nodiscard]] auto fuse_destroy() -> packet::error_type override; /*packet::error_type fuse_fallocate(const char *path, const std::int32_t - &mode, const remote::file_offset &offset, const remote::file_offset - &length, const remote::file_handle &handle) override ;*/ + &mode, remote::file_offset offset, const remote::file_offset + &length, remote::file_handle handle) override ;*/ [[nodiscard]] auto fuse_fgetattr(const char *path, remote::stat &r_stat, - bool &directory, - const remote::file_handle &handle) + bool &directory, remote::file_handle handle) -> packet::error_type override; [[nodiscard]] auto fuse_fsetattr_x(const char *path, const remote::setattr_x &attr, - const remote::file_handle &handle) + remote::file_handle handle) -> packet::error_type override; - [[nodiscard]] auto fuse_fsync(const char *path, const std::int32_t &datasync, - const remote::file_handle &handle) + [[nodiscard]] auto fuse_fsync(const char *path, std::int32_t datasync, + remote::file_handle handle) -> packet::error_type override; [[nodiscard]] auto fuse_ftruncate(const char *path, - const remote::file_offset &size, - const remote::file_handle &handle) + remote::file_offset size, + remote::file_handle handle) -> packet::error_type override; [[nodiscard]] auto fuse_getattr(const char *path, remote::stat &r_stat, @@ -101,10 +100,10 @@ public: -> packet::error_type override; /*packet::error_type fuse_getxattr(const char *path, const char *name, char - *value, const remote::file_size &size) override ; + *value, remote::file_size size) override ; packet::error_type fuse_getxattrOSX(const char *path, const char *name, char - *value, const remote::file_size &size, std::uint32_t position) override ;*/ + *value, remote::file_size size, std::uint32_t position) override ;*/ [[nodiscard]] auto fuse_getxtimes(const char *path, remote::file_time &bkuptime, @@ -114,57 +113,55 @@ public: [[nodiscard]] auto fuse_init() -> packet::error_type override; /*packet::error_type fuse_listxattr(const char *path, char *buffer, - const remote::file_size &size) override + remote::file_size size) override ;*/ - [[nodiscard]] auto fuse_mkdir(const char *path, const remote::file_mode &mode) + [[nodiscard]] auto fuse_mkdir(const char *path, remote::file_mode mode) -> packet::error_type override; - [[nodiscard]] auto fuse_opendir(const char *path, remote::file_handle &handle) + [[nodiscard]] auto fuse_opendir(const char *path, remote::file_handle handle) -> packet::error_type override; [[nodiscard]] auto - fuse_create(const char *path, const remote::file_mode &mode, - const remote::open_flags &flags, remote::file_handle &handle) + fuse_create(const char *path, remote::file_mode mode, + const remote::open_flags &flags, remote::file_handle handle) -> packet::error_type override; [[nodiscard]] auto fuse_open(const char *path, const remote::open_flags &flags, - remote::file_handle &handle) + remote::file_handle handle) -> packet::error_type override; - [[nodiscard]] auto fuse_read(const char *path, char *buffer, - const remote::file_size &read_size, - const remote::file_offset &read_offset, - const remote::file_handle &handle) + [[nodiscard]] auto + fuse_read(const char *path, char *buffer, remote::file_size read_size, + remote::file_offset read_offset, remote::file_handle handle) -> packet::error_type override; [[nodiscard]] auto fuse_rename(const char *from, const char *to) -> packet::error_type override; [[nodiscard]] auto fuse_write(const char *path, const char *buffer, - const remote::file_size &write_size, - const remote::file_offset &write_offset, - const remote::file_handle &handle) + remote::file_size write_size, + remote::file_offset write_offset, + remote::file_handle handle) -> packet::error_type override; [[nodiscard]] auto fuse_write_base64(const char *path, const char *buffer, - const remote::file_size &write_size, - const remote::file_offset &write_offset, - const remote::file_handle &handle) + remote::file_size write_size, + remote::file_offset write_offset, + remote::file_handle handle) -> packet::error_type override; [[nodiscard]] auto - fuse_readdir(const char *path, const remote::file_offset &offset, - const remote::file_handle &handle, std::string &item_path) + fuse_readdir(const char *path, remote::file_offset offset, + remote::file_handle handle, std::string &item_path) -> packet::error_type override; - [[nodiscard]] auto fuse_release(const char *path, - const remote::file_handle &handle) + [[nodiscard]] auto fuse_release(const char *path, remote::file_handle handle) -> packet::error_type override; [[nodiscard]] auto fuse_releasedir(const char *path, - const remote::file_handle &handle) + remote::file_handle handle) -> packet::error_type override; /*packet::error_type fuse_removexattr(const char *path, const char *name) @@ -177,26 +174,26 @@ public: -> packet::error_type override; [[nodiscard]] auto fuse_setbkuptime(const char *path, - const remote::file_time &bkuptime) + remote::file_time bkuptime) -> packet::error_type override; [[nodiscard]] auto fuse_setchgtime(const char *path, - const remote::file_time &chgtime) + remote::file_time chgtime) -> packet::error_type override; [[nodiscard]] auto fuse_setcrtime(const char *path, - const remote::file_time &crtime) + remote::file_time crtime) -> packet::error_type override; [[nodiscard]] auto fuse_setvolname(const char *volname) -> packet::error_type override; /*packet::error_type fuse_setxattr(const char *path, const char *name, const - char *value, const remote::file_size &size, const std::int32_t &flags) + char *value, remote::file_size size, std::int32_t flags) override ; packet::error_type fuse_setxattr_osx(const char *path, const char *name, const - char *value, const remote::file_size &size, const std::int32_t &flags, + char *value, remote::file_size size, std::int32_t flags, std::uint32_t position) override ;*/ [[nodiscard]] auto fuse_statfs(const char *path, std::uint64_t frsize, @@ -208,7 +205,7 @@ public: -> packet::error_type override; [[nodiscard]] auto fuse_truncate(const char *path, - const remote::file_offset &size) + remote::file_offset size) -> packet::error_type override; [[nodiscard]] auto fuse_unlink(const char *path) @@ -218,21 +215,22 @@ public: std::uint64_t op0, std::uint64_t op1) -> packet::error_type override; - void set_fuse_uid_gid(const remote::user_id & /* uid */, - const remote::group_id & /* gid */) override {} + void set_fuse_uid_gid(remote::user_id /* uid */, + remote::group_id /* gid */) override {} // JSON Layer [[nodiscard]] auto json_create_directory_snapshot(const std::string &path, json &json_data) -> packet::error_type override; - [[nodiscard]] auto json_read_directory_snapshot( - const std::string &path, const remote::file_handle &handle, - std::uint32_t page, json &json_data) -> packet::error_type override; + [[nodiscard]] auto json_read_directory_snapshot(const std::string &path, + remote::file_handle handle, + std::uint32_t page, + json &json_data) + -> packet::error_type override; - [[nodiscard]] auto - json_release_directory_snapshot(const std::string &path, - const remote::file_handle &handle) + [[nodiscard]] auto json_release_directory_snapshot(const std::string &path, + remote::file_handle handle) -> packet::error_type override; // WinFSP Layer diff --git a/repertory/librepertory/include/file_manager/file_manager.hpp b/repertory/librepertory/include/file_manager/file_manager.hpp index 0b32d626..975972b9 100644 --- a/repertory/librepertory/include/file_manager/file_manager.hpp +++ b/repertory/librepertory/include/file_manager/file_manager.hpp @@ -77,7 +77,8 @@ private: private: void close_timed_out_files(); - [[nodiscard]] auto get_open_file_by_handle(std::uint64_t handle) const + [[nodiscard]] auto get_open_file_by_handle(std::uint64_t handle, + bool &is_closed) const -> std::shared_ptr; [[nodiscard]] auto get_open_file_count(const std::string &api_path) const @@ -92,7 +93,7 @@ private: -> api_error; void queue_upload(const std::string &api_path, const std::string &source_path, - bool no_lock); + bool is_unlinked, bool no_lock); void remove_resume(const std::string &api_path, const std::string &source_path, bool no_lock); @@ -145,6 +146,9 @@ public: [[nodiscard]] auto get_directory_items(const std::string &api_path) const -> directory_item_list override; + [[nodiscard]] auto get_open_file(const std::string &api_path, + std::shared_ptr &file) -> bool; + [[nodiscard]] auto get_open_file(std::uint64_t handle, bool write_supported, std::shared_ptr &file) -> bool; diff --git a/repertory/librepertory/include/file_manager/i_open_file.hpp b/repertory/librepertory/include/file_manager/i_open_file.hpp index 48f41281..6d6bdef7 100644 --- a/repertory/librepertory/include/file_manager/i_open_file.hpp +++ b/repertory/librepertory/include/file_manager/i_open_file.hpp @@ -64,13 +64,17 @@ public: [[nodiscard]] virtual auto get_source_path() const -> std::string = 0; + [[nodiscard]] virtual auto get_unlinked_meta() const -> api_meta_map = 0; + + [[nodiscard]] virtual auto has_handle(std::uint64_t handle) const -> bool = 0; + [[nodiscard]] virtual auto is_complete() const -> bool = 0; [[nodiscard]] virtual auto is_directory() const -> bool = 0; - [[nodiscard]] virtual auto is_write_supported() const -> bool = 0; + [[nodiscard]] virtual auto is_unlinked() const -> bool = 0; - [[nodiscard]] virtual auto has_handle(std::uint64_t handle) const -> bool = 0; + [[nodiscard]] virtual auto is_write_supported() const -> bool = 0; [[nodiscard]] virtual auto native_operation(native_operation_callback callback) -> api_error = 0; @@ -97,7 +101,7 @@ class i_closeable_open_file : public i_open_file { INTERFACE_SETUP(i_closeable_open_file); public: - virtual void add(std::uint64_t handle, open_file_data ofd) = 0; + virtual void add(std::uint64_t handle, open_file_data ofd, bool notify) = 0; [[nodiscard]] virtual auto can_close() const -> bool = 0; @@ -110,13 +114,13 @@ public: [[nodiscard]] virtual auto is_modified() const -> bool = 0; - [[nodiscard]] virtual auto is_unlinked() const -> bool = 0; - virtual void remove(std::uint64_t handle) = 0; virtual void remove_all() = 0; virtual void set_unlinked(bool value) = 0; + + virtual void set_unlinked_meta(api_meta_map meta) = 0; }; } // namespace repertory diff --git a/repertory/librepertory/include/file_manager/open_file.hpp b/repertory/librepertory/include/file_manager/open_file.hpp index b21dc102..9e2492c0 100644 --- a/repertory/librepertory/include/file_manager/open_file.hpp +++ b/repertory/librepertory/include/file_manager/open_file.hpp @@ -111,8 +111,6 @@ public: [[nodiscard]] auto is_complete() const -> bool override; - [[nodiscard]] auto is_removed() const -> bool override; - [[nodiscard]] auto is_write_supported() const -> bool override { return true; } @@ -133,8 +131,6 @@ public: [[nodiscard]] auto resize(std::uint64_t new_file_size) -> api_error override; - void set_removed(bool value) override; - [[nodiscard]] auto write(std::uint64_t write_offset, const data_buffer &data, std::size_t &bytes_written) -> api_error override; }; diff --git a/repertory/librepertory/include/file_manager/open_file_base.hpp b/repertory/librepertory/include/file_manager/open_file_base.hpp index addab380..5cb866d9 100644 --- a/repertory/librepertory/include/file_manager/open_file_base.hpp +++ b/repertory/librepertory/include/file_manager/open_file_base.hpp @@ -119,9 +119,8 @@ private: }; bool modified_{false}; bool removed_{false}; -#if !defined(_WIN32) bool unlinked_{false}; -#endif // !defined(_WIN32) + api_meta_map unlinked_meta_; private: void file_io_thread(); @@ -167,7 +166,7 @@ protected: void wait_for_io(stop_type_callback stop_requested_cb); public: - void add(std::uint64_t handle, open_file_data ofd) override; + void add(std::uint64_t handle, open_file_data ofd, bool notify) override; [[nodiscard]] auto can_close() const -> bool override; @@ -205,6 +204,8 @@ public: [[nodiscard]] auto get_source_path() const -> std::string override; + [[nodiscard]] auto get_unlinked_meta() const -> api_meta_map override; + [[nodiscard]] auto has_handle(std::uint64_t handle) const -> bool override; [[nodiscard]] auto is_directory() const -> bool override { @@ -222,6 +223,8 @@ public: void set_api_path(const std::string &api_path) override; void set_unlinked(bool value) override; + + void set_unlinked_meta(api_meta_map meta) override; }; } // namespace repertory diff --git a/repertory/librepertory/include/types/repertory.hpp b/repertory/librepertory/include/types/repertory.hpp index 26cf8d54..1d9cfaaf 100644 --- a/repertory/librepertory/include/types/repertory.hpp +++ b/repertory/librepertory/include/types/repertory.hpp @@ -125,9 +125,11 @@ enum class api_error { no_disk_space, not_implemented, not_supported, + no_tty, os_error, out_of_memory, permission_denied, + stale_descriptor, upload_failed, xattr_buffer_small, xattr_exists, diff --git a/repertory/librepertory/include/utils/unix/unix_utils.hpp b/repertory/librepertory/include/utils/unix/unix_utils.hpp index e95ddb65..7ab195e9 100644 --- a/repertory/librepertory/include/utils/unix/unix_utils.hpp +++ b/repertory/librepertory/include/utils/unix/unix_utils.hpp @@ -45,9 +45,8 @@ inline const std::array attribute_namespaces = { [[nodiscard]] auto unix_error_to_windows(int err) -> std::uint32_t; -void windows_create_to_unix(const UINT32 &create_options, - const UINT32 &granted_access, std::uint32_t &flags, - remote::file_mode &mode); +void windows_create_to_unix(UINT32 create_options, UINT32 granted_access, + std::uint32_t &flags, remote::file_mode &mode); } // namespace repertory::utils #endif // !defined(_WIN32) diff --git a/repertory/librepertory/include/utils/windows/windows_utils.hpp b/repertory/librepertory/include/utils/windows/windows_utils.hpp index 4d4f9769..b12395dc 100644 --- a/repertory/librepertory/include/utils/windows/windows_utils.hpp +++ b/repertory/librepertory/include/utils/windows/windows_utils.hpp @@ -45,7 +45,7 @@ namespace repertory::utils { [[nodiscard]] auto unix_access_mask_to_windows(std::int32_t mask) -> int; [[nodiscard]] auto -unix_open_flags_to_flags_and_perms(const remote::file_mode &mode, +unix_open_flags_to_flags_and_perms(remote::file_mode mode, const remote::open_flags &flags, std::int32_t &perms) -> int; } // namespace repertory::utils diff --git a/repertory/librepertory/src/drives/directory_iterator.cpp b/repertory/librepertory/src/drives/directory_iterator.cpp index 92efb8eb..63aaa51c 100644 --- a/repertory/librepertory/src/drives/directory_iterator.cpp +++ b/repertory/librepertory/src/drives/directory_iterator.cpp @@ -26,7 +26,7 @@ namespace repertory { #if !defined(_WIN32) -auto directory_iterator::fill_buffer(const remote::file_offset &offset, +auto directory_iterator::fill_buffer(remote::file_offset offset, fuse_fill_dir_t filler_function, void *buffer, populate_stat_callback populate_stat) diff --git a/repertory/librepertory/src/drives/fuse/fuse_base.cpp b/repertory/librepertory/src/drives/fuse/fuse_base.cpp index 5f80b8d6..453fa5c1 100644 --- a/repertory/librepertory/src/drives/fuse/fuse_base.cpp +++ b/repertory/librepertory/src/drives/fuse/fuse_base.cpp @@ -52,6 +52,7 @@ fuse_base::fuse_base(app_config &config) : config_(config) { fuse_ops_.fsync = fuse_base::fsync_; fuse_ops_.getattr = fuse_base::getattr_; fuse_ops_.init = fuse_base::init_; + fuse_ops_.ioctl = fuse_base::ioctl_; fuse_ops_.mkdir = fuse_base::mkdir_; fuse_ops_.open = fuse_base::open_; fuse_ops_.opendir = fuse_base::opendir_; @@ -211,8 +212,9 @@ auto fuse_base::execute_callback( const std::function &cb, bool disable_logging) -> int { - auto from_api_file = utils::path::create_api_path(from ? from : ""); - auto to_api_file = utils::path::create_api_path(to ? to : ""); + auto from_api_file = + utils::path::create_api_path(from == nullptr ? "" : from); + auto to_api_file = utils::path::create_api_path(to == nullptr ? "" : to); auto res = utils::from_api_error(cb(from_api_file, to_api_file)); raise_fuse_event(function_name, "from|" + from_api_file + "|to|" + to_api_file, res, @@ -224,7 +226,7 @@ auto fuse_base::execute_callback( std::string_view function_name, const char *path, const std::function &cb, bool disable_logging) -> int { - auto api_path = utils::path::create_api_path(path ? path : ""); + auto api_path = utils::path::create_api_path(path == nullptr ? "" : path); auto res = utils::from_api_error(cb(api_path)); raise_fuse_event(function_name, api_path, res, disable_logging); return res; @@ -389,6 +391,17 @@ auto fuse_base::init_impl(struct fuse_conn_info *conn) -> void * { return this; } +auto fuse_base::ioctl_(const char *path, int cmd, void *arg, + struct fuse_file_info *f_info, unsigned int /* flags */, + void * /* data */) -> int { + REPERTORY_USES_FUNCTION_NAME(); + + return instance().execute_callback( + function_name, path, [&](std::string api_path) -> api_error { + return instance().ioctl_impl(std::move(api_path), cmd, arg, f_info); + }); +} + auto fuse_base::mkdir_(const char *path, mode_t mode) -> int { REPERTORY_USES_FUNCTION_NAME(); diff --git a/repertory/librepertory/src/drives/fuse/fuse_drive.cpp b/repertory/librepertory/src/drives/fuse/fuse_drive.cpp index 002a4102..45eed6b8 100644 --- a/repertory/librepertory/src/drives/fuse/fuse_drive.cpp +++ b/repertory/librepertory/src/drives/fuse/fuse_drive.cpp @@ -44,6 +44,7 @@ #include "utils/base64.hpp" #include "utils/collection.hpp" #include "utils/common.hpp" +#include "utils/config.hpp" #include "utils/error_utils.hpp" #include "utils/polling.hpp" #include "utils/time.hpp" @@ -385,14 +386,25 @@ auto fuse_drive::fgetattr_impl(std::string api_path, struct stat *u_stat, return api_error::invalid_handle; } + auto is_unlinked{ + not open_file->is_directory() && open_file->is_unlinked(), + }; + api_meta_map meta{}; - auto res = provider_.get_item_meta(api_path, meta); - if (res != api_error::success) { - return res; + if (is_unlinked) { + meta = open_file->get_unlinked_meta(); + } else { + auto res = provider_.get_item_meta(api_path, meta); + if (res != api_error::success) { + return res; + } } fuse_drive_base::populate_stat(api_path, open_file->get_file_size(), meta, open_file->is_directory(), provider_, u_stat); + if (is_unlinked) { + u_stat->st_nlink = 0; + } return api_error::success; } @@ -498,9 +510,36 @@ auto fuse_drive::get_item_meta(const std::string &api_path, return ret; } +auto fuse_drive::get_item_stat(std::uint64_t handle, + struct stat64 *u_stat) const -> api_error { + std::shared_ptr open_file; + if (not fm_->get_open_file(handle, false, open_file)) { + return api_error::invalid_handle; + } + + api_meta_map meta{}; + if (open_file->is_unlinked()) { + meta = open_file->get_unlinked_meta(); + } else { + auto ret = provider_.get_item_meta(open_file->get_api_path(), meta); + if (ret != api_error::success) { + return ret; + } + } + + fuse_drive_base::populate_stat(open_file->get_api_path(), + open_file->get_file_size(), meta, + open_file->is_directory(), provider_, u_stat); + return api_error::success; +} + #if FUSE_USE_VERSION >= 30 auto fuse_drive::getattr_impl(std::string api_path, struct stat *u_stat, - struct fuse_file_info * /*f_info*/) -> api_error { + struct fuse_file_info *f_info) -> api_error { + if (f_info != nullptr && f_info->fh != 0 && + f_info->fh != static_cast(REPERTORY_INVALID_HANDLE)) { + return fgetattr_impl(api_path, u_stat, f_info); + } #else auto fuse_drive::getattr_impl(std::string api_path, struct stat *u_stat) -> api_error { @@ -637,6 +676,20 @@ auto fuse_drive::init_impl(struct fuse_conn_info *conn) -> void * { return ret; } +auto fuse_drive::ioctl_impl(std::string /* api_path */, int cmd, void *arg, + struct fuse_file_info *f_info) -> api_error { + if (cmd == repertory_ioctl_fd_command) { + if (arg == nullptr) { + return api_error::invalid_operation; + } + + std::memcpy(arg, &f_info->fh, sizeof(f_info->fh)); + return api_error::success; + } + + return api_error::no_tty; +} + auto fuse_drive::is_processing(const std::string &api_path) const -> bool { return fm_->is_processing(api_path); } @@ -1406,6 +1459,8 @@ auto fuse_drive::write_impl(std::string /*api_path*/ const char *buffer, size_t write_size, off_t write_offset, struct fuse_file_info *f_info, std::size_t &bytes_written) -> api_error { + REPERTORY_USES_FUNCTION_NAME(); + std::shared_ptr open_file; if (not fm_->get_open_file(f_info->fh, true, open_file)) { return api_error::item_not_found; diff --git a/repertory/librepertory/src/drives/fuse/remotefuse/remote_client.cpp b/repertory/librepertory/src/drives/fuse/remotefuse/remote_client.cpp index f86d7f6f..11a0267e 100644 --- a/repertory/librepertory/src/drives/fuse/remotefuse/remote_client.cpp +++ b/repertory/librepertory/src/drives/fuse/remotefuse/remote_client.cpp @@ -37,7 +37,7 @@ auto remote_client::check() -> packet::error_type { return packet_client_.send(function_name, request, service_flags); } -auto remote_client::fuse_access(const char *path, const std::int32_t &mask) +auto remote_client::fuse_access(const char *path, std::int32_t mask) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -61,7 +61,7 @@ auto remote_client::fuse_chflags(const char *path, std::uint32_t flags) return packet_client_.send(function_name, request, service_flags); } -auto remote_client::fuse_chmod(const char *path, const remote::file_mode &mode) +auto remote_client::fuse_chmod(const char *path, remote::file_mode mode) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -73,9 +73,8 @@ auto remote_client::fuse_chmod(const char *path, const remote::file_mode &mode) return packet_client_.send(function_name, request, service_flags); } -auto remote_client::fuse_chown(const char *path, const remote::user_id &uid, - const remote::group_id &gid) - -> packet::error_type { +auto remote_client::fuse_chown(const char *path, remote::user_id uid, + remote::group_id gid) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); packet request; @@ -95,8 +94,8 @@ auto remote_client::fuse_destroy() -> packet::error_type { } /*packet::error_type remote_client::fuse_fallocate(const char *path, const -std::int32_t &mode, const remote::file_offset &offset, const remote::file_offset -&length, const remote::file_handle &handle) { packet request; +std::int32_t &mode, remote::file_offset offset, const remote::file_offset +&length, remote::file_handle handle) { packet request; request.encode(path); request.encode(mode); request.encode(offset); @@ -108,8 +107,7 @@ std::int32_t &mode, const remote::file_offset &offset, const remote::file_offset }*/ auto remote_client::fuse_fgetattr(const char *path, remote::stat &r_stat, - bool &directory, - const remote::file_handle &handle) + bool &directory, remote::file_handle handle) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -124,10 +122,12 @@ auto remote_client::fuse_fgetattr(const char *path, remote::stat &r_stat, auto ret = packet_client_.send(function_name, request, response, service_flags); if (ret == 0) { - if ((ret = response.decode(r_stat)) == 0) { - std::uint8_t d{}; - if ((ret = response.decode(d)) == 0) { - directory = static_cast(d); + ret = response.decode(r_stat); + if (ret == 0) { + std::uint8_t is_dir{}; + ret = response.decode(is_dir); + if (ret == 0) { + directory = static_cast(is_dir); } } } @@ -137,7 +137,7 @@ auto remote_client::fuse_fgetattr(const char *path, remote::stat &r_stat, auto remote_client::fuse_fsetattr_x(const char *path, const remote::setattr_x &attr, - const remote::file_handle &handle) + remote::file_handle handle) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -150,8 +150,8 @@ auto remote_client::fuse_fsetattr_x(const char *path, return packet_client_.send(function_name, request, service_flags); } -auto remote_client::fuse_fsync(const char *path, const std::int32_t &datasync, - const remote::file_handle &handle) +auto remote_client::fuse_fsync(const char *path, std::int32_t datasync, + remote::file_handle handle) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -164,9 +164,8 @@ auto remote_client::fuse_fsync(const char *path, const std::int32_t &datasync, return packet_client_.send(function_name, request, service_flags); } -auto remote_client::fuse_ftruncate(const char *path, - const remote::file_offset &size, - const remote::file_handle &handle) +auto remote_client::fuse_ftruncate(const char *path, remote::file_offset size, + remote::file_handle handle) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -193,10 +192,12 @@ auto remote_client::fuse_getattr(const char *path, remote::stat &r_stat, auto ret = packet_client_.send(function_name, request, response, service_flags); if (ret == 0) { - if ((ret = response.decode(r_stat)) == 0) { - std::uint8_t d = 0; - if ((ret = response.decode(d)) == 0) { - directory = static_cast(d); + ret = response.decode(r_stat); + if (ret == 0) { + std::uint8_t is_dir{}; + ret = response.decode(is_dir); + if (ret == 0) { + directory = static_cast(is_dir); } } } @@ -205,7 +206,7 @@ auto remote_client::fuse_getattr(const char *path, remote::stat &r_stat, } /*packet::error_type remote_client::fuse_getxattr(const char *path, const char -*name, char *value, const remote::file_size &size) { packet::error_type ret = 0; +*name, char *value, remote::file_size size) { packet::error_type ret = 0; if (size > std::numeric_limits::max()) { ret = -ERANGE; } else { packet request; request.encode(path); request.encode(name); request.encode(size); @@ -226,7 +227,7 @@ response.CurrentPointer(), static_cast(size2)); } packet::error_type remote_client::fuse_getxattr_osx(const char *path, const char -*name, char *value, const remote::file_size &size, std::uint32_t position) { +*name, char *value, remote::file_size size, std::uint32_t position) { packet::error_type ret = 0; if (size > std::numeric_limits::max()) { ret = -ERANGE; } else { packet request; request.encode(path); request.encode(name); @@ -277,7 +278,7 @@ auto remote_client::fuse_init() -> packet::error_type { } /*packet::error_type remote_client::fuse_listxattr(const char *path, char -*buffer, const remote::file_size &size) { packet::error_type ret = 0; if (size > +*buffer, remote::file_size size) { packet::error_type ret = 0; if (size > std::numeric_limits::max()) { ret = -ERANGE; } else { packet request; request.encode(path); request.encode(size); @@ -297,7 +298,7 @@ static_cast(size2)); return ret; }*/ -auto remote_client::fuse_mkdir(const char *path, const remote::file_mode &mode) +auto remote_client::fuse_mkdir(const char *path, remote::file_mode mode) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -327,7 +328,7 @@ auto remote_client::fuse_opendir(const char *path, remote::file_handle &handle) return ret; } -auto remote_client::fuse_create(const char *path, const remote::file_mode &mode, +auto remote_client::fuse_create(const char *path, remote::file_mode mode, const remote::open_flags &flags, remote::file_handle &handle) -> packet::error_type { @@ -370,9 +371,9 @@ auto remote_client::fuse_open(const char *path, const remote::open_flags &flags, } auto remote_client::fuse_read(const char *path, char *buffer, - const remote::file_size &read_size, - const remote::file_offset &read_offset, - const remote::file_handle &handle) + remote::file_size read_size, + remote::file_offset read_offset, + remote::file_handle handle) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -406,9 +407,9 @@ auto remote_client::fuse_rename(const char *from, const char *to) } auto remote_client::fuse_write(const char *path, const char *buffer, - const remote::file_size &write_size, - const remote::file_offset &write_offset, - const remote::file_handle &handle) + remote::file_size write_size, + remote::file_offset write_offset, + remote::file_handle handle) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -428,9 +429,9 @@ auto remote_client::fuse_write(const char *path, const char *buffer, } auto remote_client::fuse_write_base64(const char *path, const char *buffer, - const remote::file_size &write_size, - const remote::file_offset &write_offset, - const remote::file_handle &handle) + remote::file_size write_size, + remote::file_offset write_offset, + remote::file_handle handle) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -449,9 +450,8 @@ auto remote_client::fuse_write_base64(const char *path, const char *buffer, return packet_client_.send(function_name, request, service_flags); } -auto remote_client::fuse_readdir(const char *path, - const remote::file_offset &offset, - const remote::file_handle &handle, +auto remote_client::fuse_readdir(const char *path, remote::file_offset offset, + remote::file_handle handle, std::string &item_path) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -471,8 +471,7 @@ auto remote_client::fuse_readdir(const char *path, return ret; } -auto remote_client::fuse_release(const char *path, - const remote::file_handle &handle) +auto remote_client::fuse_release(const char *path, remote::file_handle handle) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -485,7 +484,7 @@ auto remote_client::fuse_release(const char *path, } auto remote_client::fuse_releasedir(const char *path, - const remote::file_handle &handle) + remote::file_handle handle) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -528,7 +527,7 @@ auto remote_client::fuse_setattr_x(const char *path, remote::setattr_x &attr) } auto remote_client::fuse_setbkuptime(const char *path, - const remote::file_time &bkuptime) + remote::file_time bkuptime) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -540,8 +539,7 @@ auto remote_client::fuse_setbkuptime(const char *path, return packet_client_.send(function_name, request, service_flags); } -auto remote_client::fuse_setchgtime(const char *path, - const remote::file_time &chgtime) +auto remote_client::fuse_setchgtime(const char *path, remote::file_time chgtime) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -553,8 +551,7 @@ auto remote_client::fuse_setchgtime(const char *path, return packet_client_.send(function_name, request, service_flags); } -auto remote_client::fuse_setcrtime(const char *path, - const remote::file_time &crtime) +auto remote_client::fuse_setcrtime(const char *path, remote::file_time crtime) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -577,7 +574,7 @@ auto remote_client::fuse_setvolname(const char *volname) -> packet::error_type { } /*packet::error_type remote_client::fuse_setxattr(const char *path, const char -*name, const char *value, const remote::file_size &size, const std::int32_t +*name, const char *value, remote::file_size size, const std::int32_t &flags) { packet::error_type ret = 0; if (size > std::numeric_limits::max()) { ret = -ERANGE; } else { packet request; request.encode(path); request.encode(name); request.encode(size); @@ -592,7 +589,7 @@ request; request.encode(path); request.encode(name); request.encode(size); } packet::error_type remote_client::fuse_setxattr_osx(const char *path, const char -*name, const char *value, const remote::file_size &size, const std::int32_t +*name, const char *value, remote::file_size size, const std::int32_t &flags, const std::uint32_t &position) override { packet::error_type ret = 0; if (size > std::numeric_limits::max()) { ret = -ERANGE; } else { packet request; request.encode(path); request.Encode(name); @@ -645,8 +642,7 @@ auto remote_client::fuse_statfs_x(const char *path, std::uint64_t bsize, return ret; } -auto remote_client::fuse_truncate(const char *path, - const remote::file_offset &size) +auto remote_client::fuse_truncate(const char *path, remote::file_offset size) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -702,9 +698,11 @@ auto remote_client::json_create_directory_snapshot(const std::string &path, return ret; } -auto remote_client::json_read_directory_snapshot( - const std::string &path, const remote::file_handle &handle, - std::uint32_t page, json &json_data) -> packet::error_type { +auto remote_client::json_read_directory_snapshot(const std::string &path, + remote::file_handle handle, + std::uint32_t page, + json &json_data) + -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); packet request; @@ -723,8 +721,8 @@ auto remote_client::json_read_directory_snapshot( return ret; } -auto remote_client::json_release_directory_snapshot( - const std::string &path, const remote::file_handle &handle) +auto remote_client::json_release_directory_snapshot(const std::string &path, + remote::file_handle handle) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -736,8 +734,8 @@ auto remote_client::json_release_directory_snapshot( return packet_client_.send(function_name, request, service_flags); } -void remote_client::set_fuse_uid_gid(const remote::user_id &uid, - const remote::group_id &gid) { +void remote_client::set_fuse_uid_gid(remote::user_id uid, + remote::group_id gid) { uid_ = uid; gid_ = gid; } diff --git a/repertory/librepertory/src/drives/fuse/remotefuse/remote_fuse_drive.cpp b/repertory/librepertory/src/drives/fuse/remotefuse/remote_fuse_drive.cpp index d3ce5e2b..e783d229 100644 --- a/repertory/librepertory/src/drives/fuse/remotefuse/remote_fuse_drive.cpp +++ b/repertory/librepertory/src/drives/fuse/remotefuse/remote_fuse_drive.cpp @@ -171,7 +171,6 @@ auto remote_fuse_drive::fsetattr_x_impl(std::string api_path, auto remote_fuse_drive::fsync_impl(std::string api_path, int datasync, struct fuse_file_info *f_info) -> api_error { - return utils::to_api_error( remote_instance_->fuse_fsync(api_path.c_str(), datasync, f_info->fh)); } @@ -187,8 +186,12 @@ auto remote_fuse_drive::ftruncate_impl(std::string api_path, off_t size, #if FUSE_USE_VERSION >= 30 auto remote_fuse_drive::getattr_impl(std::string api_path, struct stat *u_stat, - struct fuse_file_info * /*f_info*/) + struct fuse_file_info *f_info) -> api_error { + if (f_info != nullptr && f_info->fh != 0 && + f_info->fh != static_cast(REPERTORY_INVALID_HANDLE)) { + return fgetattr_impl(api_path, u_stat, f_info); + } #else // FUSE_USE_VERSION < 30 auto remote_fuse_drive::getattr_impl(std::string api_path, struct stat *u_stat) -> api_error { @@ -407,7 +410,7 @@ auto remote_fuse_drive::readdir_impl(std::string api_path, void *buf, std::memset(p_stat.get(), 0, sizeof(struct stat)); if (item_path == ".") { #if FUSE_USE_VERSION >= 30 - stat_res = getattr_impl(api_path, p_stat.get(), f_info); + stat_res = getattr_impl(api_path, p_stat.get(), nullptr); #else // FUSE_USE_VERSION < 30 stat_res = getattr_impl(api_path, p_stat.get()); #endif // FUSE_USE_VERSION >= 30 @@ -422,7 +425,7 @@ auto remote_fuse_drive::readdir_impl(std::string api_path, void *buf, } else { #if FUSE_USE_VERSION >= 30 stat_res = getattr_impl(utils::path::get_parent_api_path(api_path), - p_stat.get(), f_info); + p_stat.get(), nullptr); #else // FUSE_USE_VERSION < 30 stat_res = getattr_impl(utils::path::get_parent_api_path(api_path), p_stat.get()); diff --git a/repertory/librepertory/src/drives/fuse/remotefuse/remote_server.cpp b/repertory/librepertory/src/drives/fuse/remotefuse/remote_server.cpp index 102bb1e4..db6d6331 100644 --- a/repertory/librepertory/src/drives/fuse/remotefuse/remote_server.cpp +++ b/repertory/librepertory/src/drives/fuse/remotefuse/remote_server.cpp @@ -105,8 +105,7 @@ auto remote_server::populate_file_info(const std::string &api_path, } void remote_server::populate_file_info(const std::string &api_path, - const UINT64 &file_size, - const UINT32 &attributes, + UINT64 file_size, UINT32 attributes, remote::file_info &r_info) { REPERTORY_USES_FUNCTION_NAME(); @@ -188,7 +187,7 @@ void remote_server::populate_stat(const struct stat64 &u_stat, r_stat.st_uid = u_stat.st_uid; } -auto remote_server::fuse_access(const char *path, const std::int32_t &mask) +auto remote_server::fuse_access(const char *path, std::int32_t mask) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -221,7 +220,7 @@ auto remote_server::fuse_chflags(const char *path, std::uint32_t flags) return ret; } -auto remote_server::fuse_chmod(const char *path, const remote::file_mode &mode) +auto remote_server::fuse_chmod(const char *path, remote::file_mode mode) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -232,9 +231,8 @@ auto remote_server::fuse_chmod(const char *path, const remote::file_mode &mode) return ret; } -auto remote_server::fuse_chown(const char *path, const remote::user_id &uid, - const remote::group_id &gid) - -> packet::error_type { +auto remote_server::fuse_chown(const char *path, remote::user_id uid, + remote::group_id gid) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); auto file_path = construct_path(path); @@ -244,7 +242,7 @@ auto remote_server::fuse_chown(const char *path, const remote::user_id &uid, return ret; } -auto remote_server::fuse_create(const char *path, const remote::file_mode &mode, +auto remote_server::fuse_create(const char *path, remote::file_mode mode, const remote::open_flags &flags, remote::file_handle &handle) -> packet::error_type { @@ -256,10 +254,10 @@ auto remote_server::fuse_create(const char *path, const remote::file_mode &mode, if (res >= 0) { handle = static_cast(res); set_open_info(res, open_info{ - "", - nullptr, - {}, - file_path, + .client_id = "", + .directory_buffer = nullptr, + .handles = {}, + .path = file_path, }); } @@ -276,8 +274,8 @@ auto remote_server::fuse_destroy() -> packet::error_type { } /*packet::error_type remote_server::fuse_fallocate(const char *path, const -std::int32_t &mode, const remote::file_offset &offset, const remote::file_offset -&length, const remote::file_handle &handle) { const auto file_path = +std::int32_t &mode, remote::file_offset offset, const remote::file_offset +&length, remote::file_handle handle) { const auto file_path = ConstructPath(path); auto ret = HasOpenFileInfo(handle, -EBADF); if (ret == 0) { #if defined(__APPLE__) ret = STATUS_NOT_IMPLEMENTED; @@ -313,22 +311,35 @@ length); ret = ((res < 0) ? -errno : 0); #endif return ret; }*/ -auto remote_server::fuse_fgetattr(const char *path, remote::stat &r_stat, - bool &directory, - const remote::file_handle &handle) +auto remote_server::fuse_fgetattr(const char * /* path */, remote::stat &r_stat, + bool &directory, remote::file_handle handle) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); r_stat = {}; - auto file_path = construct_path(path); - - auto res = has_open_info(static_cast(handle), EBADF); - if (res == 0) { - directory = utils::file::directory(file_path).exists(); + auto res = -1; + auto file_path = get_open_file_path(static_cast(handle)); + if (file_path.empty()) { + errno = EBADF; + } else { struct stat64 u_stat{}; res = fstat64(static_cast(handle), &u_stat); + if (res == -1 && errno == ESTALE) { + std::uint64_t internal_handle{}; + res = ioctl(static_cast(handle), + repertory_ioctl_fd_command, &internal_handle); + if (res == 0) { + auto err = drive_.get_item_stat(internal_handle, &u_stat); + if (err != api_error::success) { + res = -1; + errno = std::abs(utils::from_api_error(err)); + } + } + } + if (res == 0) { + directory = S_ISDIR(u_stat.st_mode); populate_stat(u_stat, r_stat); } } @@ -340,7 +351,7 @@ auto remote_server::fuse_fgetattr(const char *path, remote::stat &r_stat, auto remote_server::fuse_fsetattr_x(const char *path, const remote::setattr_x &attr, - const remote::file_handle &handle) + remote::file_handle handle) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -434,42 +445,34 @@ auto remote_server::fuse_fsetattr_x(const char *path, return ret; } -auto remote_server::fuse_fsync(const char *path, const std::int32_t &datasync, - const remote::file_handle &handle) +auto remote_server::fuse_fsync(const char *path, std::int32_t datasync, + remote::file_handle handle) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); auto file_path = construct_path(path); - auto res = has_open_info(static_cast(handle), EBADF); - if (res == 0) { #if defined(__APPLE__) - res = datasync ? fcntl(static_cast(handle), F_FULLFSYNC) - : fsync(static_cast(handle)); + auto res = datasync ? fcntl(static_cast(handle), F_FULLFSYNC) + : fsync(static_cast(handle)); #else // !defined(__APPLE__) - res = datasync ? fdatasync(static_cast(handle)) - : fsync(static_cast(handle)); + auto res = datasync ? fdatasync(static_cast(handle)) + : fsync(static_cast(handle)); #endif // defined(__APPLE__) - } auto ret = ((res < 0) ? -errno : 0); RAISE_REMOTE_FUSE_SERVER_EVENT(function_name, file_path, ret); return ret; } -auto remote_server::fuse_ftruncate(const char *path, - const remote::file_offset &size, - const remote::file_handle &handle) +auto remote_server::fuse_ftruncate(const char *path, remote::file_offset size, + remote::file_handle handle) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); auto file_path = construct_path(path); - - auto res = has_open_info(static_cast(handle), EBADF); - if (res == 0) { - res = - ftruncate(static_cast(handle), static_cast(size)); - } + auto res = + ftruncate(static_cast(handle), static_cast(size)); auto ret = ((res < 0) ? -errno : 0); RAISE_REMOTE_FUSE_SERVER_EVENT(function_name, file_path, ret); @@ -482,15 +485,13 @@ auto remote_server::fuse_getattr(const char *path, remote::stat &r_stat, auto api_path = utils::path::create_api_path(path); auto file_path = construct_path(api_path); - auto parent_api_path = utils::path::get_parent_api_path(api_path); r_stat = {}; - directory = utils::file::directory(file_path).exists(); - struct stat64 u_stat{}; auto res = stat64(file_path.c_str(), &u_stat); if (res == 0) { + directory = S_ISDIR(u_stat.st_mode); populate_stat(u_stat, r_stat); } @@ -500,7 +501,7 @@ auto remote_server::fuse_getattr(const char *path, remote::stat &r_stat, } /*packet::error_type remote_server::fuse_getxattr(const char *path, const char -*name, char *value, const remote::file_size &size) { const auto api_path = +*name, char *value, remote::file_size size) { const auto api_path = utils::path::create_api_path(path); const auto file_path = ConstructPath(api_path); const auto parentApiPath = utils::path::get_parent_api_path(api_path); @@ -542,7 +543,7 @@ filePath, ret); return ret; } packet::error_type remote_server::fuse_getxattrOSX(const char *path, const char -*name, char *value, const remote::file_size &size, std::uint32_t position) { +*name, char *value, remote::file_size size, std::uint32_t position) { const auto file_path = ConstructPath(path); #if defined(__APPLE__) && defined(HAS_SETXATTR) // TODO: CheckParentAccess(api_path, X_OK) @@ -594,7 +595,7 @@ auto remote_server::fuse_init() -> packet::error_type { } /*packet::error_type remote_server::fuse_listxattr(const char *path, char -*buffer, const remote::file_size &size) { const auto file_path = +*buffer, remote::file_size size) { const auto file_path = ConstructPath(path); #if defined(HAS_SETXATTR) #if defined(__APPLE__) const auto res = listxattr(file_path.c_str(), buffer, size, FSOPT_NOFOLLOW); #else const auto res = listxattr(file_path.c_str(), buffer, size); #endif auto ret = ((res < @@ -603,7 +604,7 @@ auto res = listxattr(file_path.c_str(), buffer, size); #endif auto ret = ((res < return ret; }*/ -auto remote_server::fuse_mkdir(const char *path, const remote::file_mode &mode) +auto remote_server::fuse_mkdir(const char *path, remote::file_mode mode) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -625,12 +626,13 @@ auto remote_server::fuse_open(const char *path, const remote::open_flags &flags, if (res >= 0) { handle = static_cast(res); set_open_info(res, open_info{ - "", - nullptr, - {}, - file_path, + .client_id = "", + .directory_buffer = nullptr, + .handles = {}, + .path = file_path, }); } + auto ret = ((res < 0) ? -errno : 0); RAISE_REMOTE_FUSE_SERVER_EVENT(function_name, file_path, ret); return ret; @@ -662,21 +664,18 @@ auto remote_server::fuse_opendir(const char *path, remote::file_handle &handle) } auto remote_server::fuse_read(const char *path, char *buffer, - const remote::file_size &read_size, - const remote::file_offset &read_offset, - const remote::file_handle &handle) + remote::file_size read_size, + remote::file_offset read_offset, + remote::file_handle handle) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); auto file_path = construct_path(path); auto &data = *reinterpret_cast(buffer); - ssize_t bytes_read{has_open_info(static_cast(handle), EBADF)}; - if (bytes_read == 0) { - data.resize(read_size); - bytes_read = pread64(static_cast(handle), data.data(), - read_size, static_cast(read_offset)); - } + data.resize(read_size); + auto bytes_read = pread64(static_cast(handle), data.data(), + read_size, static_cast(read_offset)); auto ret = ((bytes_read < 0) ? -errno : bytes_read); if (ret < 0) { @@ -699,9 +698,8 @@ auto remote_server::fuse_rename(const char *from, const char *to) return ret; } -auto remote_server::fuse_readdir(const char *path, - const remote::file_offset &offset, - const remote::file_handle &handle, +auto remote_server::fuse_readdir(const char *path, remote::file_offset offset, + remote::file_handle handle, std::string &item_path) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -725,19 +723,15 @@ auto remote_server::fuse_readdir(const char *path, return ret; } -auto remote_server::fuse_release(const char *path, - const remote::file_handle &handle) +auto remote_server::fuse_release(const char *path, remote::file_handle handle) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); packet::error_type ret = 0; auto file_path = construct_path(path); - auto res = has_open_info(static_cast(handle), EBADF); - if (res == 0) { - res = close(static_cast(handle)); - remove_open_info(static_cast(handle)); - } + auto res = close(static_cast(handle)); + remove_open_info(static_cast(handle)); ret = ((res < 0) ? -errno : 0); RAISE_REMOTE_FUSE_SERVER_EVENT(function_name, file_path, ret); @@ -745,7 +739,7 @@ auto remote_server::fuse_release(const char *path, } auto remote_server::fuse_releasedir(const char *path, - const remote::file_handle &handle) + remote::file_handle handle) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -792,7 +786,7 @@ auto remote_server::fuse_setattr_x(const char *path, remote::setattr_x &attr) } auto remote_server::fuse_setbkuptime(const char *path, - const remote::file_time &bkuptime) + remote::file_time bkuptime) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -812,8 +806,7 @@ auto remote_server::fuse_setbkuptime(const char *path, return ret; } -auto remote_server::fuse_setchgtime(const char *path, - const remote::file_time &chgtime) +auto remote_server::fuse_setchgtime(const char *path, remote::file_time chgtime) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -833,8 +826,7 @@ auto remote_server::fuse_setchgtime(const char *path, return ret; } -auto remote_server::fuse_setcrtime(const char *path, - const remote::file_time &crtime) +auto remote_server::fuse_setcrtime(const char *path, remote::file_time crtime) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -862,7 +854,7 @@ auto remote_server::fuse_setvolname(const char *volname) -> packet::error_type { } /*packet::error_type remote_server::fuse_setxattr(const char *path, const char -*name, const char *value, const remote::file_size &size, const std::int32_t +*name, const char *value, remote::file_size size, const std::int32_t &flags) { const auto file_path = ConstructPath(path); #if defined(__APPLE__{} || !defined(HAS_SETXATTR) auto ret = STATUS_NOT_IMPLEMENTED; #else const auto res = setxattr(file_path.c_str(), name, value, size, flags); auto ret = ((res < 0) ? @@ -871,7 +863,7 @@ ret); return ret; } packet::error_type remote_server::fuse_setxattrOSX(const char *path, const char -*name, const char *value, const remote::file_size &size, const std::int32_t +*name, const char *value, remote::file_size size, const std::int32_t &flags, const std::uint32_t &position) { const auto file_path = ConstructPath(path); #if defined(__APPLE__) && defined(HAS_SETXATTR) const auto res = setxattr(file_path.c_str(), name, value, size, position, flags); auto ret @@ -926,8 +918,7 @@ auto remote_server::fuse_statfs_x(const char *path, std::uint64_t bsize, return 0; } -auto remote_server::fuse_truncate(const char *path, - const remote::file_offset &size) +auto remote_server::fuse_truncate(const char *path, remote::file_offset size) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -955,10 +946,10 @@ auto remote_server::fuse_utimens(const char *path, const remote::file_time *tv, auto file_path = construct_path(path); - struct timespec tv2[2] = {{0, 0}}; - const auto process_timespec = [](auto op, const auto &src, auto &dst) { - if ((op == UTIME_NOW) || (op == UTIME_OMIT)) { - dst.tv_nsec = static_cast(op); + std::array tv2{}; + const auto process_timespec = [](auto cur_op, const auto &src, auto &dst) { + if ((cur_op == UTIME_NOW) || (cur_op == UTIME_OMIT)) { + dst.tv_nsec = static_cast(cur_op); dst.tv_sec = 0; return; } @@ -970,27 +961,23 @@ auto remote_server::fuse_utimens(const char *path, const remote::file_time *tv, process_timespec(op0, tv[0U], tv2[0U]); process_timespec(op1, tv[1U], tv2[1U]); - auto res = utimensat(0, file_path.c_str(), &tv2[0U], AT_SYMLINK_NOFOLLOW); + auto res = utimensat(0, file_path.c_str(), tv2.data(), AT_SYMLINK_NOFOLLOW); auto ret = ((res < 0) ? -errno : 0); RAISE_REMOTE_FUSE_SERVER_EVENT(function_name, file_path, ret); return ret; } auto remote_server::fuse_write(const char *path, const char *buffer, - const remote::file_size &write_size, - const remote::file_offset &write_offset, - const remote::file_handle &handle) + remote::file_size write_size, + remote::file_offset write_offset, + remote::file_handle handle) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); auto file_path = construct_path(path); - ssize_t bytes_written{ - has_open_info(static_cast(handle), EBADF)}; - if (bytes_written == 0) { - bytes_written = pwrite64(static_cast(handle), buffer, - write_size, static_cast(write_offset)); - } + auto bytes_written = pwrite64(static_cast(handle), buffer, + write_size, static_cast(write_offset)); auto ret = ((bytes_written < 0) ? -errno : bytes_written); if (ret < 0) { @@ -1000,11 +987,12 @@ auto remote_server::fuse_write(const char *path, const char *buffer, return static_cast(ret); } -auto remote_server::fuse_write_base64( - const char * /*path*/, const char * /*buffer*/, - const remote::file_size & /*write_size*/, - const remote::file_offset & /*write_offset*/, - const remote::file_handle & /*handle*/) -> packet::error_type { +auto remote_server::fuse_write_base64(const char * /*path*/, + const char * /*buffer*/, + remote::file_size /*write_size*/, + remote::file_offset /*write_offset*/, + remote::file_handle /*handle*/) + -> packet::error_type { // DOES NOTHING return 0; } @@ -1662,9 +1650,11 @@ auto remote_server::json_create_directory_snapshot(const std::string &path, return ret; } -auto remote_server::json_read_directory_snapshot( - const std::string &path, const remote::file_handle &handle, - std::uint32_t page, json &json_data) -> packet::error_type { +auto remote_server::json_read_directory_snapshot(const std::string &path, + remote::file_handle handle, + std::uint32_t page, + json &json_data) + -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); int res{-EBADF}; @@ -1693,8 +1683,8 @@ auto remote_server::json_read_directory_snapshot( return ret; } -auto remote_server::json_release_directory_snapshot( - const std::string &path, const remote::file_handle &handle) +auto remote_server::json_release_directory_snapshot(const std::string &path, + remote::file_handle handle) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); diff --git a/repertory/librepertory/src/drives/remote/remote_open_file_table.cpp b/repertory/librepertory/src/drives/remote/remote_open_file_table.cpp index 83312e3f..9dd5ef94 100644 --- a/repertory/librepertory/src/drives/remote/remote_open_file_table.cpp +++ b/repertory/librepertory/src/drives/remote/remote_open_file_table.cpp @@ -148,7 +148,7 @@ auto remote_open_file_table::has_open_directory(const std::string &client_id, } auto remote_open_file_table::has_compat_open_info( - const remote::file_handle &handle, int error_return) -> int { + remote::file_handle handle, int error_return) -> int { recur_mutex_lock compat_lock(file_mutex_); auto res = compat_handle_lookup_.contains(handle) ? 0 : -1; if (res == -1) { @@ -194,7 +194,7 @@ void remote_open_file_table::remove_all(const std::string &file_path) { } void remote_open_file_table::remove_compat_open_info( - const remote::file_handle &handle) { + remote::file_handle handle) { recur_mutex_lock compat_lock(file_mutex_); if (not compat_handle_lookup_.contains(handle)) { return; @@ -273,7 +273,7 @@ void remote_open_file_table::remove_and_close_all(const native_handle &handle) { } void remote_open_file_table::set_compat_client_id( - const remote::file_handle &handle, const std::string &client_id) { + remote::file_handle handle, const std::string &client_id) { recur_mutex_lock compat_lock(file_mutex_); compat_file_lookup_.at(compat_handle_lookup_.at(handle))->client_id = client_id; @@ -286,7 +286,7 @@ void remote_open_file_table::set_client_id(const native_handle &handle, } void remote_open_file_table::set_compat_open_info( - const remote::file_handle &handle, const std::string &file_path) { + remote::file_handle handle, const std::string &file_path) { recur_mutex_lock compat_lock(file_mutex_); if (compat_handle_lookup_.contains(handle)) { return; diff --git a/repertory/librepertory/src/drives/winfsp/remotewinfsp/remote_client.cpp b/repertory/librepertory/src/drives/winfsp/remotewinfsp/remote_client.cpp index e4a3dbd2..f9051899 100644 --- a/repertory/librepertory/src/drives/winfsp/remotewinfsp/remote_client.cpp +++ b/repertory/librepertory/src/drives/winfsp/remotewinfsp/remote_client.cpp @@ -74,7 +74,7 @@ auto remote_client::json_create_directory_snapshot(const std::string &path, } auto remote_client::json_read_directory_snapshot( - const std::string &path, const remote::file_handle &handle, + const std::string &path, remote::file_handle handle, std::uint32_t page, json &json_data) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -96,7 +96,7 @@ auto remote_client::json_read_directory_snapshot( } auto remote_client::json_release_directory_snapshot( - const std::string &path, const remote::file_handle &handle) + const std::string &path, remote::file_handle handle) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); diff --git a/repertory/librepertory/src/drives/winfsp/remotewinfsp/remote_server.cpp b/repertory/librepertory/src/drives/winfsp/remotewinfsp/remote_server.cpp index da718eba..916d8ea5 100644 --- a/repertory/librepertory/src/drives/winfsp/remotewinfsp/remote_server.cpp +++ b/repertory/librepertory/src/drives/winfsp/remotewinfsp/remote_server.cpp @@ -97,7 +97,7 @@ void remote_server::populate_stat(const char *path, bool directory, } // FUSE Layer -auto remote_server::fuse_access(const char *path, const std::int32_t &mask) +auto remote_server::fuse_access(const char *path, std::int32_t mask) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -132,7 +132,7 @@ auto remote_server::fuse_chflags(const char *path, std::uint32_t /*flags*/) } auto remote_server::fuse_chmod(const char *path, - const remote::file_mode & /*mode*/) + remote::file_mode /*mode*/) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -143,8 +143,8 @@ auto remote_server::fuse_chmod(const char *path, } auto remote_server::fuse_chown(const char *path, - const remote::user_id & /*uid*/, - const remote::group_id & /*gid*/) + remote::user_id /*uid*/, + remote::group_id /*gid*/) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -162,8 +162,8 @@ auto remote_server::fuse_destroy() -> packet::error_type { } /*packet::error_type remote_server::fuse_fallocate(const char *path, const -std::int32_t &mode, const remote::file_offset &offset, const remote::file_offset -&length, const remote::file_handle &handle) { auto file_path = +std::int32_t &mode, remote::file_offset offset, const remote::file_offset +&length, remote::file_handle handle) { auto file_path = construct_path(path); auto res = HasOpenFileCompatInfo(handle, EBADF); if (res == 0) { res = _chsize_s(static_cast(handle), offset + length); } @@ -174,8 +174,7 @@ construct_path(path); auto res = HasOpenFileCompatInfo(handle, EBADF); if (res }*/ auto remote_server::fuse_fgetattr(const char *path, remote::stat &r_stat, - bool &directory, - const remote::file_handle &handle) + bool &directory, remote::file_handle handle) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -202,7 +201,7 @@ auto remote_server::fuse_fgetattr(const char *path, remote::stat &r_stat, auto remote_server::fuse_fsetattr_x(const char *path, const remote::setattr_x & /*attr*/, - const remote::file_handle & /*handle*/) + remote::file_handle /*handle*/) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -213,8 +212,8 @@ auto remote_server::fuse_fsetattr_x(const char *path, } auto remote_server::fuse_fsync(const char *path, - const std::int32_t & /*datasync*/, - const remote::file_handle &handle) + std::int32_t /*datasync*/, + remote::file_handle handle) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -243,8 +242,8 @@ auto remote_server::fuse_fsync(const char *path, } auto remote_server::fuse_ftruncate(const char *path, - const remote::file_offset &size, - const remote::file_handle &handle) + remote::file_offset size, + remote::file_handle handle) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -299,13 +298,13 @@ auto remote_server::fuse_getattr(const char *path, remote::stat &r_stat, } /*packet::error_type remote_server::fuse_getxattr(const char *path, const char -*name, char *value, const remote::file_size &size) { auto file_path = +*name, char *value, remote::file_size size) { auto file_path = construct_path(path); auto ret = STATUS_NOT_IMPLEMENTED; RAISE_REMOTE_WINFSP_SERVER_EVENT(function_name, file_path, ret); return ret; } packet::error_type remote_server::fuse_getxattr_osx(const char *path, const char -*name, char *value, const remote::file_size &size, std::uint32_t position) { +*name, char *value, remote::file_size size, std::uint32_t position) { auto file_path = construct_path(path); auto ret = STATUS_NOT_IMPLEMENTED; RAISE_REMOTE_WINFSP_SERVER_EVENT(function_name, file_path, ret); return ret; @@ -331,14 +330,14 @@ auto remote_server::fuse_init() -> packet::error_type { } /*packet::error_type remote_server::fuse_listxattr(const char *path, char -*buffer, const remote::file_size &size) { auto file_path = +*buffer, remote::file_size size) { auto file_path = construct_path(path); auto ret = STATUS_NOT_IMPLEMENTED; RAISE_REMOTE_WINFSP_SERVER_EVENT(function_name, file_path, ret); return ret; }*/ auto remote_server::fuse_mkdir(const char *path, - const remote::file_mode & /*mode*/) + remote::file_mode /*mode*/) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -351,7 +350,7 @@ auto remote_server::fuse_mkdir(const char *path, return ret; } -auto remote_server::fuse_opendir(const char *path, remote::file_handle &handle) +auto remote_server::fuse_opendir(const char *path, remote::file_handle handle) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -376,9 +375,9 @@ auto remote_server::fuse_opendir(const char *path, remote::file_handle &handle) return ret; } -auto remote_server::fuse_create(const char *path, const remote::file_mode &mode, +auto remote_server::fuse_create(const char *path, remote::file_mode mode, const remote::open_flags &flags, - remote::file_handle &handle) + remote::file_handle handle) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -415,7 +414,7 @@ auto remote_server::fuse_create(const char *path, const remote::file_mode &mode, } auto remote_server::fuse_open(const char *path, const remote::open_flags &flags, - remote::file_handle &handle) + remote::file_handle handle) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -446,9 +445,9 @@ auto remote_server::fuse_open(const char *path, const remote::open_flags &flags, } auto remote_server::fuse_read(const char *path, char *buffer, - const remote::file_size &read_size, - const remote::file_offset &read_offset, - const remote::file_handle &handle) + remote::file_size read_size, + remote::file_offset read_offset, + remote::file_handle handle) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -497,9 +496,9 @@ auto remote_server::fuse_rename(const char *from, const char *to) } auto remote_server::fuse_write(const char *path, const char *buffer, - const remote::file_size &write_size, - const remote::file_offset &write_offset, - const remote::file_handle &handle) + remote::file_size write_size, + remote::file_offset write_offset, + remote::file_handle handle) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -534,16 +533,16 @@ auto remote_server::fuse_write(const char *path, const char *buffer, auto remote_server::fuse_write_base64( const char * /*path*/, const char * /*buffer*/, - const remote::file_size & /*write_size*/, - const remote::file_offset & /*write_offset*/, - const remote::file_handle & /*handle*/) -> packet::error_type { + remote::file_size /*write_size*/, + remote::file_offset /*write_offset*/, + remote::file_handle /*handle*/) -> packet::error_type { // DOES NOTHING return 0; } auto remote_server::fuse_readdir(const char *path, - const remote::file_offset &offset, - const remote::file_handle &handle, + remote::file_offset offset, + remote::file_handle handle, std::string &item_path) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -567,8 +566,7 @@ auto remote_server::fuse_readdir(const char *path, return ret; } -auto remote_server::fuse_release(const char *path, - const remote::file_handle &handle) +auto remote_server::fuse_release(const char *path, remote::file_handle handle) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -585,7 +583,7 @@ auto remote_server::fuse_release(const char *path, } auto remote_server::fuse_releasedir(const char *path, - const remote::file_handle & /*handle*/) + remote::file_handle /*handle*/) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -625,7 +623,7 @@ auto remote_server::fuse_setattr_x(const char *path, } auto remote_server::fuse_setbkuptime(const char *path, - const remote::file_time & /*bkuptime*/) + remote::file_time /*bkuptime*/) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -636,7 +634,7 @@ auto remote_server::fuse_setbkuptime(const char *path, } auto remote_server::fuse_setchgtime(const char *path, - const remote::file_time & /*chgtime*/) + remote::file_time /*chgtime*/) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -647,7 +645,7 @@ auto remote_server::fuse_setchgtime(const char *path, } auto remote_server::fuse_setcrtime(const char *path, - const remote::file_time & /*crtime*/) + remote::file_time /*crtime*/) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -665,14 +663,14 @@ auto remote_server::fuse_setvolname(const char *volname) -> packet::error_type { } /*packet::error_type remote_server::fuse_setxattr(const char *path, const char -*name, const char *value, const remote::file_size &size, const std::int32_t +*name, const char *value, remote::file_size size, const std::int32_t &flags) { auto file_path = construct_path(path); auto ret = STATUS_NOT_IMPLEMENTED; RAISE_REMOTE_WINFSP_SERVER_EVENT(function_name, file_path, ret); return ret; } packet::error_type remote_server::fuse_setxattr_osx(const char *path, const char -*name, const char *value, const remote::file_size &size, const std::int32_t +*name, const char *value, remote::file_size size, const std::int32_t &flags, const std::uint32_t &position) { auto file_path = construct_path(path); auto ret = STATUS_NOT_IMPLEMENTED; RAISE_REMOTE_WINFSP_SERVER_EVENT(function_name, file_path, ret); @@ -728,7 +726,7 @@ auto remote_server::fuse_statfs_x(const char *path, std::uint64_t bsize, } auto remote_server::fuse_truncate(const char *path, - const remote::file_offset &size) + remote::file_offset size) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); @@ -865,9 +863,11 @@ auto remote_server::json_create_directory_snapshot(const std::string &path, return ret; } -auto remote_server::json_read_directory_snapshot( - const std::string &path, const remote::file_handle &handle, - std::uint32_t page, json &json_data) -> packet::error_type { +auto remote_server::json_read_directory_snapshot(const std::string &path, + remote::file_handle handle, + std::uint32_t page, + json &json_data) + -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); auto file_path = construct_path(path); @@ -897,7 +897,7 @@ auto remote_server::json_read_directory_snapshot( } auto remote_server::json_release_directory_snapshot( - const std::string &path, const remote::file_handle & /*handle*/) + const std::string &path, remote::file_handle /*handle*/) -> packet::error_type { REPERTORY_USES_FUNCTION_NAME(); diff --git a/repertory/librepertory/src/file_manager/file_manager.cpp b/repertory/librepertory/src/file_manager/file_manager.cpp index 410d1810..7d154ebc 100644 --- a/repertory/librepertory/src/file_manager/file_manager.cpp +++ b/repertory/librepertory/src/file_manager/file_manager.cpp @@ -77,8 +77,10 @@ file_manager::~file_manager() { } void file_manager::close(std::uint64_t handle) { + REPERTORY_USES_FUNCTION_NAME(); + unique_recur_mutex_lock file_lock(open_file_mtx_); - bool is_closed{false}; + bool is_closed{}; auto closeable_file = get_open_file_by_handle(handle, is_closed); if (not closeable_file) { return; @@ -88,7 +90,7 @@ void file_manager::close(std::uint64_t handle) { closeable_file->remove(handle); file_lock.lock(); - if (not(is_closed && closeable_file->get_open_file_count() == 0U)) { + if (not is_closed || closeable_file->get_open_file_count() != 0U) { return; } @@ -307,17 +309,23 @@ auto file_manager::get_next_handle() -> std::uint64_t { return next_handle_; } -auto file_manager::get_open_file_by_handle(std::uint64_t handle) const +auto file_manager::get_open_file_by_handle(std::uint64_t handle, + bool &is_closed) const -> std::shared_ptr { + REPERTORY_USES_FUNCTION_NAME(); + { auto file_iter = std::ranges::find_if( closed_file_lookup_, [&handle](auto &&item) -> bool { return item.second->has_handle(handle); }); - return (file_iter == closed_file_lookup_.end()) ? nullptr - : file_iter->second; + if (file_iter != closed_file_lookup_.end()) { + is_closed = true; + return file_iter->second; + } } + is_closed = false; auto file_iter = std::ranges::find_if(open_file_lookup_, [&handle](auto &&item) -> bool { return item.second->has_handle(handle); @@ -333,14 +341,33 @@ auto file_manager::get_open_file_count(const std::string &api_path) const : file_iter->second->get_open_file_count(); } +auto file_manager::get_open_file(const std::string &api_path, + std::shared_ptr &file) -> bool { + unique_recur_mutex_lock open_lock(open_file_mtx_); + if (open_file_lookup_.contains(api_path)) { + file = open_file_lookup_.at(api_path); + return true; + } + + if (closed_file_lookup_.contains(api_path)) { + file = closed_file_lookup_.at(api_path); + return true; + } + + return false; +} + auto file_manager::get_open_file(std::uint64_t handle, bool write_supported, std::shared_ptr &file) -> bool { + REPERTORY_USES_FUNCTION_NAME(); + if (write_supported && provider_.is_read_only()) { return false; } unique_recur_mutex_lock open_lock(open_file_mtx_); - auto file_ptr = get_open_file_by_handle(handle); + bool is_closed{}; + auto file_ptr = get_open_file_by_handle(handle, is_closed); if (not file_ptr) { return false; } @@ -353,7 +380,13 @@ auto file_manager::get_open_file(std::uint64_t handle, bool write_supported, : 0U, file_ptr->get_filesystem_item(), file_ptr->get_open_data(), provider_, *this); - open_file_lookup_[file_ptr->get_api_path()] = writeable_file; + writeable_file->set_unlinked(is_closed); + if (is_closed) { + writeable_file->set_unlinked_meta(file_ptr->get_unlinked_meta()); + closed_file_lookup_[file_ptr->get_api_path()] = writeable_file; + } else { + open_file_lookup_[file_ptr->get_api_path()] = writeable_file; + } file = writeable_file; return true; } @@ -428,7 +461,7 @@ auto file_manager::handle_file_rename(const std::string &from_api_path, auto ret = provider_.rename_file(from_api_path, to_api_path); if (ret != api_error::success) { - queue_upload(from_api_path, source_path, false); + queue_upload(from_api_path, source_path, false, false); return ret; } @@ -439,7 +472,7 @@ auto file_manager::handle_file_rename(const std::string &from_api_path, : provider_.set_item_meta(to_api_path, META_SOURCE, source_path); if (should_upload) { - queue_upload(to_api_path, source_path, false); + queue_upload(to_api_path, source_path, false, false); } return ret; @@ -497,7 +530,7 @@ auto file_manager::open(const std::string &api_path, bool directory, const auto create_and_add_handle = [&](std::shared_ptr cur_file) { handle = get_next_handle(); - cur_file->add(handle, ofd); + cur_file->add(handle, ofd, true); file = cur_file; }; @@ -626,14 +659,16 @@ auto file_manager::open(const std::string &api_path, bool directory, } void file_manager::queue_upload(const i_open_file &file) { - queue_upload(file.get_api_path(), file.get_source_path(), false); + queue_upload(file.get_api_path(), file.get_source_path(), file.is_unlinked(), + false); } void file_manager::queue_upload(const std::string &api_path, - const std::string &source_path, bool no_lock) { + const std::string &source_path, + bool is_unlinked, bool no_lock) { REPERTORY_USES_FUNCTION_NAME(); - if (provider_.is_read_only() || file.is_removed()) { + if (provider_.is_read_only() || is_unlinked) { return; } @@ -670,6 +705,12 @@ auto file_manager::remove_file(const std::string &api_path) -> api_error { return res; } + api_meta_map meta{}; + res = provider_.get_item_meta(api_path, meta); + if (res != api_error::success) { + return res; + } + unique_recur_mutex_lock open_lock(open_file_mtx_); unique_mutex_lock upload_lock(upload_mtx_); @@ -685,30 +726,38 @@ auto file_manager::remove_file(const std::string &api_path) -> api_error { auto file_iter = open_file_lookup_.find(api_path); if (file_iter == open_file_lookup_.end()) { + remove_source_and_shrink_cache(api_path, fsi.source_path, fsi.size, true); return api_error::success; } if (closed_file_lookup_.contains(api_path)) { - auto closed_file = closed_file_lookup_.at(api_path).second; - closed_file_lookup_[api_path] = file_iter.second; + auto closed_file = closed_file_lookup_.at(api_path); + closed_file_lookup_[api_path] = file_iter->second; for (auto &[handle, ofd] : closed_file->get_open_data()) { - closed_file_lookup_.at(api_path).add(handle, ofd); + closed_file_lookup_.at(api_path)->add(handle, ofd, false); } } else { - closed_file_lookup_[api_path] = file_iter.second; + closed_file_lookup_[api_path] = file_iter->second; } open_file_lookup_.erase(api_path); - auto closed_file = closed_file_lookup_.at(api_path).second; + auto closed_file = closed_file_lookup_.at(api_path); + auto allocated = closed_file->get_allocated(); closed_file->set_unlinked(true); + closed_file->set_unlinked_meta(meta); open_lock.unlock(); + if (not allocated) { + return api_error::success; + } + res = cache_size_mgr::instance().shrink(closed_file->get_file_size()); if (res != api_error::success) { - utils::error::raise_api_path_error(function_name, api_path, source_path, - res, "failed to shrink cache"); + utils::error::raise_api_path_error(function_name, api_path, + closed_file->get_source_path(), res, + "failed to shrink cache"); } return api_error::success; @@ -996,7 +1045,7 @@ void file_manager::start() { } for (const auto &entry : mgr_db_->get_upload_active_list()) { - queue_upload(entry.api_path, entry.source_path, false); + queue_upload(entry.api_path, entry.source_path, false, false); } for (const auto &entry : get_stored_downloads()) { @@ -1116,7 +1165,7 @@ void file_manager::stop() { void file_manager::store_resume(const i_open_file &file) { REPERTORY_USES_FUNCTION_NAME(); - if (provider_.is_read_only() || file.is_removed()) { + if (provider_.is_read_only() || file.is_unlinked()) { return; } @@ -1187,7 +1236,7 @@ void file_manager::upload_completed(const file_upload_completed &evt) { event_system::instance().raise( evt.api_path, evt.error, function_name, evt.source_path); - queue_upload(evt.api_path, evt.source_path, true); + queue_upload(evt.api_path, evt.source_path, false, true); upload_notify_.wait_for(upload_lock, queue_wait_secs); } } @@ -1242,7 +1291,7 @@ void file_manager::upload_handler() { default: { event_system::instance().raise( entry->api_path, res, function_name, entry->source_path); - queue_upload(entry->api_path, entry->source_path, true); + queue_upload(entry->api_path, entry->source_path, false, true); } break; } } diff --git a/repertory/librepertory/src/file_manager/open_file.cpp b/repertory/librepertory/src/file_manager/open_file.cpp index 93312a47..beaea559 100644 --- a/repertory/librepertory/src/file_manager/open_file.cpp +++ b/repertory/librepertory/src/file_manager/open_file.cpp @@ -538,8 +538,15 @@ auto open_file::native_operation( set_file_size(new_file_size); auto now = std::to_string(utils::time::get_time_now()); + if (is_unlinked()) { - return api_error::success: + auto meta = get_unlinked_meta(); + meta[META_CHANGED] = now; + meta[META_MODIFIED] = now; + meta[META_SIZE] = std::to_string(new_file_size); + meta[META_WRITTEN] = now; + set_unlinked_meta(meta); + return api_error::success; } res = get_provider().set_item_meta( @@ -780,11 +787,18 @@ auto open_file::write(std::uint64_t write_offset, const data_buffer &data, return set_api_error(res); } + auto now = std::to_string(utils::time::get_time_now()); if (is_unlinked()) { - return api_error::success: + auto meta = get_unlinked_meta(); + meta[META_CHANGED] = now; + meta[META_MODIFIED] = now; + meta[META_WRITTEN] = now; + set_unlinked_meta(meta); + set_modified(); + + return api_error::success; } - auto now = std::to_string(utils::time::get_time_now()); res = get_provider().set_item_meta(get_api_path(), { {META_CHANGED, now}, {META_MODIFIED, now}, diff --git a/repertory/librepertory/src/file_manager/open_file_base.cpp b/repertory/librepertory/src/file_manager/open_file_base.cpp index 9ffba852..7aa90fd2 100644 --- a/repertory/librepertory/src/file_manager/open_file_base.cpp +++ b/repertory/librepertory/src/file_manager/open_file_base.cpp @@ -92,11 +92,16 @@ open_file_base::open_file_base( } } -void open_file_base::add(std::uint64_t handle, open_file_data ofd) { +void open_file_base::add(std::uint64_t handle, open_file_data ofd, + bool notify) { REPERTORY_USES_FUNCTION_NAME(); recur_mutex_lock file_lock(file_mtx_); open_data_[handle] = ofd; + if (not notify) { + return; + } + if (open_data_.size() == 1U) { event_system::instance().raise( fsi_.api_path, fsi_.directory, function_name, fsi_.source_path); @@ -249,6 +254,11 @@ void open_file_base::set_unlinked(bool value) { unlinked_ = value; } +void open_file_base::set_unlinked_meta(api_meta_map meta) { + recur_mutex_lock file_lock(file_mtx_); + unlinked_meta_ = std::move(meta); +} + auto open_file_base::get_filesystem_item() const -> filesystem_item { recur_mutex_lock file_lock(file_mtx_); return fsi_; @@ -298,9 +308,14 @@ auto open_file_base::get_source_path() const -> std::string { return fsi_.source_path; } +auto open_file_base::get_unlinked_meta() const -> api_meta_map { + recur_mutex_lock file_lock(file_mtx_); + return unlinked_meta_; +} + auto open_file_base::has_handle(std::uint64_t handle) const -> bool { recur_mutex_lock file_lock(file_mtx_); - return open_data_.find(handle) != open_data_.end(); + return open_data_.contains(handle); } auto open_file_base::is_modified() const -> bool { @@ -327,7 +342,7 @@ void open_file_base::remove(std::uint64_t handle) { REPERTORY_USES_FUNCTION_NAME(); recur_mutex_lock file_lock(file_mtx_); - if (open_data_.find(handle) == open_data_.end()) { + if (not open_data_.contains(handle)) { return; } diff --git a/repertory/librepertory/src/types/repertory.cpp b/repertory/librepertory/src/types/repertory.cpp index 676cc803..895529c4 100644 --- a/repertory/librepertory/src/types/repertory.cpp +++ b/repertory/librepertory/src/types/repertory.cpp @@ -210,9 +210,11 @@ static const std::unordered_map LOOKUP = { {api_error::no_disk_space, "no_disk_space"}, {api_error::not_implemented, "not_implemented"}, {api_error::not_supported, "not_supported"}, + {api_error::no_tty, "no_tty"}, {api_error::os_error, "os_error"}, {api_error::out_of_memory, "out_of_memory"}, {api_error::permission_denied, "permission_denied"}, + {api_error::stale_descriptor, "stale_descriptor"}, {api_error::upload_failed, "upload_failed"}, {api_error::xattr_buffer_small, "xattr_buffer_small"}, {api_error::xattr_exists, "xattr_exists"}, diff --git a/repertory/librepertory/src/utils/unix/unix_utils.cpp b/repertory/librepertory/src/utils/unix/unix_utils.cpp index 1b7f8b57..16230dfe 100644 --- a/repertory/librepertory/src/utils/unix/unix_utils.cpp +++ b/repertory/librepertory/src/utils/unix/unix_utils.cpp @@ -76,6 +76,10 @@ auto from_api_error(api_error err) -> int { return -ENOTSUP; case api_error::not_implemented: return -ENOSYS; + case api_error::no_tty: + return -ENOTTY; + case api_error::stale_descriptor: + return -ESTALE; case api_error::upload_failed: return -ENETDOWN; case api_error::xattr_buffer_small: @@ -144,6 +148,10 @@ auto to_api_error(int err) -> api_error { return api_error::not_supported; case ENOSYS: return api_error::not_implemented; + case ESTALE: + return api_error::stale_descriptor; + case ENOTTY: + return api_error::no_tty; case ENETDOWN: return api_error::upload_failed; case ERANGE: @@ -176,6 +184,8 @@ auto unix_error_to_windows(int err) -> std::uint32_t { case EACCES: case EPERM: return STATUS_ACCESS_DENIED; + case ESTALE: + return STATUS_INVALID_HANDLE; case EBADF: return STATUS_INVALID_HANDLE; case EBUSY: @@ -209,9 +219,8 @@ auto unix_error_to_windows(int err) -> std::uint32_t { } } -void windows_create_to_unix(const UINT32 &create_options, - const UINT32 &granted_access, std::uint32_t &flags, - remote::file_mode &mode) { +void windows_create_to_unix(UINT32 create_options, UINT32 granted_access, + std::uint32_t &flags, remote::file_mode &mode) { mode = S_IRUSR | S_IWUSR; flags = O_CREAT | O_RDWR; if ((create_options & FILE_DIRECTORY_FILE) != 0U) { diff --git a/repertory/librepertory/src/utils/windows/windows_utils.cpp b/repertory/librepertory/src/utils/windows/windows_utils.cpp index 673ee58d..6f533d62 100644 --- a/repertory/librepertory/src/utils/windows/windows_utils.cpp +++ b/repertory/librepertory/src/utils/windows/windows_utils.cpp @@ -119,7 +119,7 @@ auto unix_access_mask_to_windows(std::int32_t mask) -> int { return mask & 6; } -auto unix_open_flags_to_flags_and_perms(const remote::file_mode & /*mode*/, +auto unix_open_flags_to_flags_and_perms(remote::file_mode /*mode*/, const remote::open_flags &flags, std::int32_t &perms) -> int { auto ret = _O_BINARY | _O_RANDOM; diff --git a/repertory/repertory_test/include/mocks/mock_open_file.hpp b/repertory/repertory_test/include/mocks/mock_open_file.hpp index dba74fa7..f65d78be 100644 --- a/repertory/repertory_test/include/mocks/mock_open_file.hpp +++ b/repertory/repertory_test/include/mocks/mock_open_file.hpp @@ -29,7 +29,8 @@ namespace repertory { class mock_open_file : public virtual i_closeable_open_file { public: - MOCK_METHOD(void, add, (std::uint64_t handle, open_file_data ofd), + MOCK_METHOD(void, add, + (std::uint64_t handle, open_file_data ofd, bool notify), (override)); MOCK_METHOD(bool, can_close, (), (const, override)); @@ -70,6 +71,8 @@ public: MOCK_METHOD(std::string, get_source_path, (), (const, override)); + MOCK_METHOD(api_meta_map, get_unlinked_meta, (), (const, override)); + MOCK_METHOD(bool, has_handle, (std::uint64_t handle), (const, override)); MOCK_METHOD(bool, is_complete, (), (const, override)); @@ -78,6 +81,8 @@ public: MOCK_METHOD(bool, is_modified, (), (const, override)); + MOCK_METHOD(bool, is_unlinked, (), (const, override)); + MOCK_METHOD(bool, is_write_supported, (), (const, override)); MOCK_METHOD(api_error, native_operation, (native_operation_callback callback), @@ -100,6 +105,10 @@ public: MOCK_METHOD(void, set_api_path, (const std::string &api_path), (override)); + MOCK_METHOD(void, set_unlinked, (bool value), (override)); + + MOCK_METHOD(void, set_unlinked_meta, (api_meta_map meta), (override)); + MOCK_METHOD(api_error, write, (std::uint64_t write_offset, const data_buffer &data, std::size_t &bytes_written), diff --git a/repertory/repertory_test/src/file_manager_test.cpp b/repertory/repertory_test/src/file_manager_test.cpp index eb0f7024..0fc252e4 100644 --- a/repertory/repertory_test/src/file_manager_test.cpp +++ b/repertory/repertory_test/src/file_manager_test.cpp @@ -92,6 +92,8 @@ std::atomic file_manager_test::inst{0U}; TEST_F(file_manager_test, can_start_and_stop) { EXPECT_CALL(mp, is_read_only()).WillRepeatedly(Return(false)); + EXPECT_CALL(mp, get_pinned_files()) + .WillOnce(Return(std::vector())); event_consumer consumer(service_start_begin::name, [](const i_event &evt) { const auto &evt2 = dynamic_cast(evt); @@ -128,6 +130,8 @@ TEST_F(file_manager_test, can_create_and_close_file) { cfg->set_enable_download_timeout(true); EXPECT_CALL(mp, is_read_only()).WillRepeatedly(Return(false)); + EXPECT_CALL(mp, get_pinned_files()) + .WillOnce(Return(std::vector())); polling::instance().start(cfg.get()); @@ -233,6 +237,8 @@ TEST_F(file_manager_test, can_open_and_close_file) { cfg->set_enable_download_timeout(true); EXPECT_CALL(mp, is_read_only()).WillRepeatedly(Return(false)); + EXPECT_CALL(mp, get_pinned_files()) + .WillOnce(Return(std::vector())); polling::instance().start(cfg.get()); @@ -335,6 +341,8 @@ TEST_F(file_manager_test, can_open_and_close_file) { TEST_F(file_manager_test, can_open_and_close_multiple_handles_for_same_file) { EXPECT_CALL(mp, is_read_only()).WillRepeatedly(Return(false)); + EXPECT_CALL(mp, get_pinned_files()) + .WillOnce(Return(std::vector())); polling::instance().start(cfg.get()); @@ -398,6 +406,9 @@ TEST_F(file_manager_test, can_open_and_close_multiple_handles_for_same_file) { TEST_F(file_manager_test, download_is_stored_after_write_if_partially_downloaded) { EXPECT_CALL(mp, is_read_only()).WillRepeatedly(Return(false)); + EXPECT_CALL(mp, get_pinned_files()) + .Times(2) + .WillRepeatedly(Return(std::vector())); file_manager mgr(*cfg, mp); mgr.start(); @@ -549,6 +560,8 @@ TEST_F(file_manager_test, upload_occurs_after_write_if_fully_downloaded) { cfg->set_enable_download_timeout(true); EXPECT_CALL(mp, is_read_only()).WillRepeatedly(Return(false)); + EXPECT_CALL(mp, get_pinned_files()) + .WillOnce(Return(std::vector())); polling::instance().start(cfg.get()); @@ -664,6 +677,8 @@ TEST_F(file_manager_test, upload_occurs_after_write_if_fully_downloaded) { TEST_F(file_manager_test, can_evict_file) { EXPECT_CALL(mp, is_read_only()).WillRepeatedly(Return(false)); + EXPECT_CALL(mp, get_pinned_files()) + .WillOnce(Return(std::vector())); file_manager mgr(*cfg, mp); mgr.start(); @@ -855,6 +870,8 @@ TEST_F(file_manager_test, evict_file_fails_if_source_path_is_empty) { TEST_F(file_manager_test, evict_file_fails_if_file_is_uploading) { EXPECT_CALL(mp, is_read_only()).WillRepeatedly(Return(false)); + EXPECT_CALL(mp, get_pinned_files()) + .WillOnce(Return(std::vector())); file_manager mgr(*cfg, mp); mgr.start(); @@ -946,6 +963,7 @@ TEST_F(file_manager_test, evict_file_fails_if_file_is_in_upload_queue) { file_manager mgr(*cfg, mp); mock_open_file open_file{}; + EXPECT_CALL(open_file, is_unlinked).WillRepeatedly(Return(false)); EXPECT_CALL(open_file, is_directory).WillRepeatedly(Return(false)); EXPECT_CALL(open_file, get_api_path) .WillRepeatedly(Return("/test_evict.txt")); @@ -981,6 +999,7 @@ TEST_F(file_manager_test, evict_file_fails_if_file_is_modified) { EXPECT_CALL(*file, get_source_path).WillRepeatedly(Return("/test_evict.src")); EXPECT_CALL(*file, is_directory).WillOnce(Return(false)); EXPECT_CALL(*file, is_modified).WillRepeatedly(Return(true)); + EXPECT_CALL(*file, is_unlinked).WillRepeatedly(Return(false)); EXPECT_CALL(*file, is_write_supported).WillRepeatedly(Return(true)); std::uint64_t handle{}; @@ -1392,6 +1411,26 @@ TEST_F(file_manager_test, can_remove_file) { } EXPECT_TRUE(utils::file::file("./test_remove.txt").exists()); + api_file api_f{ + .api_path = "/test_remove.txt", + .api_parent = "/", + .accessed_date = 0, + .changed_date = 0, + .creation_date = 0, + .file_size = 0, + .key = "", + .modified_date = 0, + .source_path = "", + }; + + EXPECT_CALL(mp, get_item_meta(_, _)) + .WillOnce([&api_f](const std::string &api_path, + api_meta_map &meta) -> api_error { + EXPECT_STREQ("/test_remove.txt", api_path.c_str()); + meta = provider_meta_creator(false, api_f); + return api_error::success; + }); + EXPECT_CALL(mp, get_filesystem_item) .WillOnce([](const std::string &api_path, bool directory, filesystem_item &fsi) -> api_error { @@ -1423,6 +1462,7 @@ TEST_F(file_manager_test, can_queue_and_remove_upload) { file_manager mgr(*cfg, mp); mock_open_file file{}; + EXPECT_CALL(file, is_unlinked).WillOnce(Return(false)); EXPECT_CALL(file, get_api_path).WillOnce(Return("/test_queue.txt")); EXPECT_CALL(file, get_source_path).WillOnce(Return("/test_queue.src")); @@ -1443,6 +1483,8 @@ TEST_F(file_manager_test, file_is_closed_after_download_timeout) { polling::instance().start(cfg.get()); EXPECT_CALL(mp, is_read_only()).WillRepeatedly(Return(false)); + EXPECT_CALL(mp, get_pinned_files()) + .WillOnce(Return(std::vector())); file_manager mgr(*cfg, mp); mgr.start(); @@ -1547,6 +1589,7 @@ TEST_F(file_manager_test, remove_file_fails_if_provider_remove_file_fails) { file_manager mgr(*cfg, mp); + EXPECT_CALL(mp, get_item_meta(_, _)).WillOnce(Return(api_error::success)); EXPECT_CALL(mp, get_filesystem_item) .WillOnce([](const std::string &api_path, const bool &directory, filesystem_item &fsi) -> api_error { @@ -1564,11 +1607,34 @@ TEST_F(file_manager_test, remove_file_fails_if_provider_remove_file_fails) { EXPECT_EQ(api_error::item_not_found, mgr.remove_file("/test_remove.txt")); } +TEST_F(file_manager_test, remove_file_fails_if_get_item_meta_fails) { + EXPECT_CALL(mp, is_read_only()).WillRepeatedly(Return(false)); + + file_manager mgr(*cfg, mp); + + EXPECT_CALL(mp, get_item_meta(_, _)).WillOnce(Return(api_error::error)); + EXPECT_CALL(mp, get_filesystem_item) + .WillOnce([](const std::string &api_path, const bool &directory, + filesystem_item &fsi) -> api_error { + EXPECT_STREQ("/test_remove.txt", api_path.c_str()); + EXPECT_FALSE(directory); + fsi.api_path = api_path; + fsi.api_parent = utils::path::get_parent_api_path(api_path); + fsi.directory = directory; + fsi.size = 0U; + return api_error::success; + }); + + EXPECT_EQ(api_error::error, mgr.remove_file("/test_remove.txt")); +} + TEST_F(file_manager_test, resize_greater_than_chunk_size_sets_new_chunks_to_read) { cfg->set_enable_download_timeout(true); EXPECT_CALL(mp, is_read_only()).WillRepeatedly(Return(false)); + EXPECT_CALL(mp, get_pinned_files()) + .WillOnce(Return(std::vector())); polling::instance().start(cfg.get()); diff --git a/repertory/repertory_test/src/fuse_drive_unlink_test.cpp b/repertory/repertory_test/src/fuse_drive_unlink_test.cpp index 24d3b0e2..6818b543 100644 --- a/repertory/repertory_test/src/fuse_drive_unlink_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_unlink_test.cpp @@ -39,10 +39,10 @@ TYPED_TEST(fuse_test, unlink_can_remove_file) { TYPED_TEST(fuse_test, unlink_open_file_leaves_handle_intact) { std::string name{"unlink"}; - auto path = this->create_file_and_test(name, 0644); + auto path = this->create_file_and_test(name); { - auto desc = ::open(path.c_str(), O_WRONLY); + auto desc = ::open(path.c_str(), O_RDWR); ASSERT_NE(desc, -1); this->write_all(desc, "HELLO"); ::close(desc); @@ -53,7 +53,10 @@ TYPED_TEST(fuse_test, unlink_open_file_leaves_handle_intact) { ASSERT_EQ(0, ::unlink(path.c_str())); - ASSERT_NE(-1, ::lseek(desc, 0, SEEK_END)); + auto res = ::lseek(desc, 0, SEEK_END); + fmt::println("lseek|{}|{}", res, errno); + + ASSERT_NE(-1, res); this->write_all(desc, " WORLD"); ASSERT_NE(-1, ::lseek(desc, 0, SEEK_SET)); diff --git a/repertory/repertory_test/src/open_file_test.cpp b/repertory/repertory_test/src/open_file_test.cpp index 49ec415c..80343dc2 100644 --- a/repertory/repertory_test/src/open_file_test.cpp +++ b/repertory/repertory_test/src/open_file_test.cpp @@ -637,10 +637,10 @@ TEST_F(open_file_test, can_add_handle) { open_file o(test_chunk_size, 0U, fsi, provider, upload_mgr); #if defined(_WIN32) - o.add(1u, {}); + o.add(1u, {}, true); EXPECT_EQ(nullptr, o.get_open_data(1u).directory_buffer); #else - o.add(1u, O_RDWR | O_SYNC); + o.add(1u, O_RDWR | O_SYNC, true); EXPECT_EQ(O_RDWR | O_SYNC, o.get_open_data(1u)); #endif @@ -698,9 +698,9 @@ TEST_F(open_file_test, can_remove_handle) { open_file o(test_chunk_size, 0U, fsi, provider, upload_mgr); #if defined(_WIN32) - o.add(1u, {}); + o.add(1u, {}, true); #else - o.add(1u, O_RDWR | O_SYNC); + o.add(1u, O_RDWR | O_SYNC, true); #endif o.remove(1u); From 94a04cd718d08688d1c046c57e7aac0b6007ab51 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Thu, 25 Sep 2025 13:52:44 -0500 Subject: [PATCH 135/136] refactor unlink'd behavior to always work with handles --- .../include/file_manager/file_manager.hpp | 6 +- .../src/file_manager/file_manager.cpp | 61 +++++++------------ 2 files changed, 26 insertions(+), 41 deletions(-) diff --git a/repertory/librepertory/include/file_manager/file_manager.hpp b/repertory/librepertory/include/file_manager/file_manager.hpp index 975972b9..498eaf17 100644 --- a/repertory/librepertory/include/file_manager/file_manager.hpp +++ b/repertory/librepertory/include/file_manager/file_manager.hpp @@ -61,14 +61,14 @@ private: i_provider &provider_; private: - std::unordered_map> - closed_file_lookup_; std::unique_ptr mgr_db_; std::atomic next_handle_{0U}; mutable std::recursive_mutex open_file_mtx_; std::unordered_map> open_file_lookup_; stop_type stop_requested_{false}; + std::unordered_map> + unlinked_file_lookup_; std::unordered_map> upload_lookup_; mutable std::mutex upload_mtx_; std::condition_variable upload_notify_; @@ -78,7 +78,7 @@ private: void close_timed_out_files(); [[nodiscard]] auto get_open_file_by_handle(std::uint64_t handle, - bool &is_closed) const + bool &is_unlinked) const -> std::shared_ptr; [[nodiscard]] auto get_open_file_count(const std::string &api_path) const diff --git a/repertory/librepertory/src/file_manager/file_manager.cpp b/repertory/librepertory/src/file_manager/file_manager.cpp index 7d154ebc..b5b3bfae 100644 --- a/repertory/librepertory/src/file_manager/file_manager.cpp +++ b/repertory/librepertory/src/file_manager/file_manager.cpp @@ -80,8 +80,8 @@ void file_manager::close(std::uint64_t handle) { REPERTORY_USES_FUNCTION_NAME(); unique_recur_mutex_lock file_lock(open_file_mtx_); - bool is_closed{}; - auto closeable_file = get_open_file_by_handle(handle, is_closed); + bool is_unlinked{}; + auto closeable_file = get_open_file_by_handle(handle, is_unlinked); if (not closeable_file) { return; } @@ -90,11 +90,13 @@ void file_manager::close(std::uint64_t handle) { closeable_file->remove(handle); file_lock.lock(); - if (not is_closed || closeable_file->get_open_file_count() != 0U) { - return; + if (is_unlinked) { + unlinked_file_lookup_.erase(handle); } - closed_file_lookup_.erase(closeable_file->get_api_path()); + if (not is_unlinked || closeable_file->get_open_file_count() != 0U) { + return; + } file_lock.unlock(); closeable_file->close(); @@ -310,22 +312,16 @@ auto file_manager::get_next_handle() -> std::uint64_t { } auto file_manager::get_open_file_by_handle(std::uint64_t handle, - bool &is_closed) const + bool &is_unlinked) const -> std::shared_ptr { REPERTORY_USES_FUNCTION_NAME(); - { - auto file_iter = std::ranges::find_if( - closed_file_lookup_, [&handle](auto &&item) -> bool { - return item.second->has_handle(handle); - }); - if (file_iter != closed_file_lookup_.end()) { - is_closed = true; - return file_iter->second; - } + if (unlinked_file_lookup_.contains(handle)) { + is_unlinked = true; + unlinked_file_lookup_.at(handle); } - is_closed = false; + is_unlinked = false; auto file_iter = std::ranges::find_if(open_file_lookup_, [&handle](auto &&item) -> bool { return item.second->has_handle(handle); @@ -349,11 +345,6 @@ auto file_manager::get_open_file(const std::string &api_path, return true; } - if (closed_file_lookup_.contains(api_path)) { - file = closed_file_lookup_.at(api_path); - return true; - } - return false; } @@ -366,8 +357,8 @@ auto file_manager::get_open_file(std::uint64_t handle, bool write_supported, } unique_recur_mutex_lock open_lock(open_file_mtx_); - bool is_closed{}; - auto file_ptr = get_open_file_by_handle(handle, is_closed); + bool is_unlinked{}; + auto file_ptr = get_open_file_by_handle(handle, is_unlinked); if (not file_ptr) { return false; } @@ -380,10 +371,12 @@ auto file_manager::get_open_file(std::uint64_t handle, bool write_supported, : 0U, file_ptr->get_filesystem_item(), file_ptr->get_open_data(), provider_, *this); - writeable_file->set_unlinked(is_closed); - if (is_closed) { + writeable_file->set_unlinked(is_unlinked); + if (is_unlinked) { writeable_file->set_unlinked_meta(file_ptr->get_unlinked_meta()); - closed_file_lookup_[file_ptr->get_api_path()] = writeable_file; + for (const auto &[handle, ofd] : writeable_file->get_open_data()) { + unlinked_file_lookup_[handle] = writeable_file; + } } else { open_file_lookup_[file_ptr->get_api_path()] = writeable_file; } @@ -730,23 +723,15 @@ auto file_manager::remove_file(const std::string &api_path) -> api_error { return api_error::success; } - if (closed_file_lookup_.contains(api_path)) { - auto closed_file = closed_file_lookup_.at(api_path); - closed_file_lookup_[api_path] = file_iter->second; - - for (auto &[handle, ofd] : closed_file->get_open_data()) { - closed_file_lookup_.at(api_path)->add(handle, ofd, false); - } - } else { - closed_file_lookup_[api_path] = file_iter->second; - } - + auto closed_file = file_iter->second; open_file_lookup_.erase(api_path); - auto closed_file = closed_file_lookup_.at(api_path); auto allocated = closed_file->get_allocated(); closed_file->set_unlinked(true); closed_file->set_unlinked_meta(meta); + for (const auto &[handle, ofd] : closed_file->get_open_data()) { + unlinked_file_lookup_[handle] = closed_file; + } open_lock.unlock(); if (not allocated) { From 144708a6043929b7fe5f2bc9e523b7b72c976019 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Thu, 25 Sep 2025 14:46:15 -0500 Subject: [PATCH 136/136] fix missing return --- repertory/librepertory/src/file_manager/file_manager.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/repertory/librepertory/src/file_manager/file_manager.cpp b/repertory/librepertory/src/file_manager/file_manager.cpp index b5b3bfae..cc615183 100644 --- a/repertory/librepertory/src/file_manager/file_manager.cpp +++ b/repertory/librepertory/src/file_manager/file_manager.cpp @@ -318,7 +318,7 @@ auto file_manager::get_open_file_by_handle(std::uint64_t handle, if (unlinked_file_lookup_.contains(handle)) { is_unlinked = true; - unlinked_file_lookup_.at(handle); + return unlinked_file_lookup_.at(handle); } is_unlinked = false; @@ -374,8 +374,8 @@ auto file_manager::get_open_file(std::uint64_t handle, bool write_supported, writeable_file->set_unlinked(is_unlinked); if (is_unlinked) { writeable_file->set_unlinked_meta(file_ptr->get_unlinked_meta()); - for (const auto &[handle, ofd] : writeable_file->get_open_data()) { - unlinked_file_lookup_[handle] = writeable_file; + for (const auto &[sub_handle, ofd] : writeable_file->get_open_data()) { + unlinked_file_lookup_[sub_handle] = writeable_file; } } else { open_file_lookup_[file_ptr->get_api_path()] = writeable_file;