added check version support to remote mounts

This commit is contained in:
2025-09-22 10:36:36 -05:00
parent 63701e105a
commit 06f169293d
8 changed files with 127 additions and 41 deletions

View File

@@ -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

View File

@@ -68,7 +68,8 @@ private:
[[nodiscard]] auto get_client() -> std::shared_ptr<client>;
[[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<client> &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;

View File

@@ -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

View File

@@ -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<packet_client::client> {
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;

View File

@@ -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<connection> 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};

View File

@@ -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<DWORD>(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

View File

@@ -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<const char *> /* 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;
}

View File

@@ -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<std::int32_t>(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<std::int32_t>(res));
}
ret = ((res == exit_code::mount_result) ? mount_result
: static_cast<std::int32_t>(res));
}
repertory::project_cleanup();