From 06f169293d739aafff2495cf95372fabf76c9d3c Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 22 Sep 2025 10:36:36 -0500 Subject: [PATCH] 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();