From 3f4b88a2c62eb8cd5cb0502d7904cf7ad85c74a7 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Wed, 30 Jul 2025 15:10:25 -0500 Subject: [PATCH 1/2] [bug] UI is unable to launch repertory.exe on Windows when absolute path contains spaces #55 --- CHANGELOG.md | 5 ++ config.sh | 6 +- repertory/repertory/src/ui/handlers.cpp | 110 ++++++++++++++---------- 3 files changed, 74 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3674096..9ab724c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## v2.0.7-release + +### Issues +* \#55 [bug] UI is unable to launch `repertory.exe` on Windows when absolute path contains spaces + ## v2.0.6-release ### Issues diff --git a/config.sh b/config.sh index da28760a..1d60f196 100755 --- a/config.sh +++ b/config.sh @@ -10,9 +10,9 @@ PROJECT_DESC="Mount utility for Sia and S3" PROJECT_MAJOR_VERSION=2 PROJECT_MINOR_VERSION=0 -PROJECT_REVISION_VERSION=6 -PROJECT_RELEASE_NUM=1 -PROJECT_RELEASE_ITER=release +PROJECT_REVISION_VERSION=7 +PROJECT_RELEASE_NUM=0 +PROJECT_RELEASE_ITER=rc PROJECT_APP_LIST=(${PROJECT_NAME}) diff --git a/repertory/repertory/src/ui/handlers.cpp b/repertory/repertory/src/ui/handlers.cpp index b5e2edf7..47fe87d0 100644 --- a/repertory/repertory/src/ui/handlers.cpp +++ b/repertory/repertory/src/ui/handlers.cpp @@ -38,6 +38,7 @@ #include #include #include +namespace bp1 = boost::process::v1; namespace { [[nodiscard]] auto decrypt(std::string_view data, std::string_view password) @@ -113,6 +114,41 @@ handlers::handlers(mgmt_app_config *config, httplib::Server *server) server_(server) { REPERTORY_USES_FUNCTION_NAME(); +#if defined(_WIN32) + if (config_->get_hidden()) { + ::ShowWindow(::GetConsoleWindow(), SW_HIDE); + } +#endif // defined(_WIN32) + + 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()); +#else // error + build fails here +#endif + } + + std::uint16_t port{}; + if (not utils::get_next_available_port(config_->get_api_port(), port)) { + fmt::println("failed to detect if port is available|{}", + config_->get_api_port()); + return; + } + + if (port != config_->get_api_port()) { + fmt::println("failed to listen on port|{}|next available|{}", + config_->get_api_port(), port); + return; + } + server_->set_socket_options([](auto &&sock) { #if defined(_WIN32) int enable{1}; @@ -244,32 +280,6 @@ handlers::handlers(mgmt_app_config *config, httplib::Server *server) #endif // !defined(_WIN32) std::signal(SIGTERM, quit_handler); -#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()); -#else // error - build fails here -#endif - - std::uint16_t port{}; - if (not utils::get_next_available_port(config_->get_api_port(), port)) { - fmt::println("failed to detect if port is available|{}", - config_->get_api_port()); - return; - } - - if (port != config_->get_api_port()) { - fmt::println("failed to listen on port|{}|next available|{}", - config_->get_api_port(), port); - return; - } - event_system::instance().start(); nonce_thread_ = @@ -711,19 +721,37 @@ auto handlers::launch_process(provider_type prov, std::string_view name, recur_mutex_lock inst_lock(inst_mtx); if (background) { #if defined(_WIN32) - std::array path{}; - ::GetSystemDirectoryA(path.data(), path.size()); + args.insert(args.begin(), "--hidden"); - args.insert(args.begin(), utils::path::combine(path.data(), {"cmd.exe"})); - args.insert(std::next(args.begin()), "/c"); - args.insert(std::next(args.begin(), 2U), "start"); - args.insert(std::next(args.begin(), 3U), ""); - args.insert(std::next(args.begin(), 4U), "/MIN"); - args.insert(std::next(args.begin(), 5U), repertory_binary_); + auto cmdline = fmt::format("{}{}{}", '"', repertory_binary_, '"'); + for (const auto &arg : args) { + cmdline += " "; + if (arg.find_first_of(" \t") != std::string::npos) { + cmdline += fmt::format("{}{}{}", '"', arg, '"'); + } else { + cmdline += arg; + } + } + + STARTUPINFOA start_info{}; + start_info.cb = sizeof(start_info); + start_info.dwFlags = STARTF_USESHOWWINDOW; + start_info.wShowWindow = SW_SHOWMINNOACTIVE; + + PROCESS_INFORMATION proc_info{}; + auto result = ::CreateProcessA( + nullptr, &cmdline[0U], nullptr, nullptr, FALSE, + CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP, nullptr, + utils::path::get_parent_path(repertory_binary_).c_str(), &start_info, + &proc_info); + + if (result) { + ::CloseHandle(proc_info.hProcess); + ::CloseHandle(proc_info.hThread); + } #else // !defined(_WIN32) - args.insert(args.begin(), "-f"); args.insert(args.begin(), repertory_binary_); -#endif // defined(_WIN32) + args.insert(std::next(args.begin()), "-f"); std::vector exec_args; exec_args.reserve(args.size() + 1U); @@ -732,10 +760,6 @@ auto handlers::launch_process(provider_type prov, std::string_view name, } exec_args.push_back(nullptr); -#if defined(_WIN32) - _spawnv(_P_DETACH, exec_args.at(0U), - const_cast(exec_args.data())); -#else // !defined(_WIN32) auto pid = fork(); if (pid < 0) { exit(1); @@ -769,10 +793,8 @@ auto handlers::launch_process(provider_type prov, std::string_view name, return {}; } - boost::process::v1::ipstream out; - boost::process::v1::child proc(repertory_binary_, - boost::process::v1::args(args), - boost::process::v1::std_out > out); + bp1::ipstream out; + bp1::child proc(repertory_binary_, bp1::args(args), bp1::std_out > out); std::string data; std::string line; From 2d53671fdfb7e26e21edc3e5d61d13180fc5af84 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Wed, 30 Jul 2025 15:22:13 -0500 Subject: [PATCH 2/2] [bug] Directory entries . and .. are incorrectly being reported as files in Linux remote mounts #57 --- CHANGELOG.md | 1 + .../fuse/remotefuse/remote_fuse_drive.cpp | 50 +++++++++++++------ 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ab724c8..0243d81b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Issues * \#55 [bug] UI is unable to launch `repertory.exe` on Windows when absolute path contains spaces +* \#57 [bug] Directory entries . and .. are incorrectly being reported as files in Linux remote mounts ## v2.0.6-release 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 f86d991a..f2ae5aea 100644 --- a/repertory/librepertory/src/drives/fuse/remotefuse/remote_fuse_drive.cpp +++ b/repertory/librepertory/src/drives/fuse/remotefuse/remote_fuse_drive.cpp @@ -102,7 +102,7 @@ void remote_fuse_drive::destroy_impl(void *ptr) { } if (remote_instance_) { - const auto res = remote_instance_->fuse_destroy(); + auto res = remote_instance_->fuse_destroy(); if (res != 0) { utils::error::raise_error(function_name, "remote fuse_destroy() failed|err|" + @@ -128,8 +128,8 @@ auto remote_fuse_drive::fgetattr_impl(std::string api_path, remote::stat r_stat{}; auto directory = false; - const auto res = remote_instance_->fuse_fgetattr(api_path.c_str(), r_stat, - directory, f_info->fh); + auto res = remote_instance_->fuse_fgetattr(api_path.c_str(), r_stat, + directory, f_info->fh); if (res == 0) { populate_stat(r_stat, directory, *unix_st); } @@ -191,7 +191,7 @@ auto remote_fuse_drive::getattr_impl(std::string api_path, struct stat *unix_st) bool directory = false; remote::stat r_stat{}; - const auto res = + auto res = remote_instance_->fuse_getattr(api_path.c_str(), r_stat, directory); if (res == 0) { populate_stat(r_stat, directory, *unix_st); @@ -208,9 +208,9 @@ api_error remote_fuse_drive::getxtimes_impl(std::string api_path, return utils::to_api_error(-EFAULT); } - remote::file_time repertory_bkuptime = 0u; - remote::file_time repertory_crtime = 0u; - const auto res = remote_instance_->fuse_getxtimes( + remote::file_time repertory_bkuptime{0U}; + remote::file_time repertory_crtime{0U}; + auto res = remote_instance_->fuse_getxtimes( api_path.c_str(), repertory_bkuptime, repertory_crtime); if (res == 0) { bkuptime->tv_nsec = @@ -381,7 +381,7 @@ auto remote_fuse_drive::readdir_impl(std::string api_path, void *buf, fuse_fill_dir_t fuse_fill_dir, off_t offset, struct fuse_file_info *f_info, - fuse_readdir_flags /*flags*/) + fuse_readdir_flags /* flags */) -> api_error { #else // FUSE_USE_VERSION < 30 auto remote_fuse_drive::readdir_impl(std::string api_path, void *buf, @@ -390,20 +390,42 @@ auto remote_fuse_drive::readdir_impl(std::string api_path, void *buf, struct fuse_file_info *f_info) -> api_error { #endif // FUSE_USE_VERSION >= 30 + std::string item_path; - int res = 0; + int res{0}; while ((res = remote_instance_->fuse_readdir( api_path.c_str(), static_cast(offset), f_info->fh, item_path)) == 0) { - if ((item_path != ".") && (item_path != "..")) { + std::unique_ptr p_stat{nullptr}; + int stat_res{0}; + if ((item_path == ".") || (item_path == "..")) { + p_stat = std::make_unique(); + std::memset(p_stat.get(), 0, sizeof(struct stat)); + if (item_path == ".") { + stat_res = + stat(utils::path::combine(get_mount_location(), {api_path}).c_str(), + p_stat.get()); + } else { + stat_res = + stat(utils::path::get_parent_path( + utils::path::combine(get_mount_location(), {api_path})) + .c_str(), + p_stat.get()); + } + + if (stat_res != 0) { + res = stat_res; + break; + } + } else { item_path = utils::path::strip_to_file_name(item_path); } #if FUSE_USE_VERSION >= 30 - if (fuse_fill_dir(buf, item_path.c_str(), nullptr, ++offset, - static_cast(0)) != 0) { + if (fuse_fill_dir(buf, item_path.c_str(), p_stat.get(), ++offset, + FUSE_FILL_DIR_PLUS) != 0) { #else // FUSE_USE_VERSION < 30 - if (fuse_fill_dir(buf, item_path.c_str(), nullptr, ++offset) != 0) { + if (fuse_fill_dir(buf, item_path.c_str(), p_stat.get(), ++offset) != 0) { #endif // FUSE_USE_VERSION >= 30 break; } @@ -592,7 +614,7 @@ auto remote_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 { - const auto res = remote_instance_->fuse_write( + auto res = remote_instance_->fuse_write( api_path.c_str(), buffer, write_size, static_cast(write_offset), f_info->fh); if (res >= 0) {