diff --git a/.cspell/words.txt b/.cspell/words.txt index 7ca74458..05e23079 100644 --- a/.cspell/words.txt +++ b/.cspell/words.txt @@ -5,6 +5,7 @@ _sh_denyrd _sh_denyrw _spawnv aarch64 +abcdefgh advapi32 armv8 autogen @@ -18,6 +19,7 @@ cflags chrono clsid cmake_current_source_dir +cmdc coinit_apartmentthreaded comdlg32 conin$ @@ -103,6 +105,8 @@ endforeach endfunction eventlib expect_streq +expect_strne +falloc_fl_keep_size fallocate fallocate_impl fext @@ -159,6 +163,7 @@ libuuid libuuid_include_dirs libvlc linkflags +llabsll localappdata lpbyte lpthread @@ -193,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 4fae90bb..939466dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,12 @@ ### BREAKING CHANGES * Remote mounts must be upgraded to v2.1.0+ to support new authentication scheme + * Protocol handshake added for DoS protection ### 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 @@ -20,6 +23,14 @@ * \#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 + +* 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 +* Implemented POSIX-compliant `unlink()` with FUSE `hard_remove` + * Open handles remain valid after `unlink()` + ## v2.0.7-release 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/librepertory/include/comm/packet/common.hpp b/repertory/librepertory/include/comm/packet/common.hpp new file mode 100644 index 00000000..744758da --- /dev/null +++ b/repertory/librepertory/include/comm/packet/common.hpp @@ -0,0 +1,68 @@ +/* + 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 { +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 { + 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_); + + ~non_blocking_guard(); + +private: + bool non_blocking; + boost::asio::ip::tcp::socket &sock; +}; + +[[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_client.hpp b/repertory/librepertory/include/comm/packet/packet_client.hpp index ff47896d..353684b7 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_; @@ -57,6 +57,7 @@ private: resolve_results_; std::mutex clients_mutex_; std::vector> clients_; + std::vector service_threads_; private: static void close(client &cli); @@ -67,23 +68,35 @@ private: [[nodiscard]] auto get_client() -> std::shared_ptr; + [[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, + [[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: - [[nodiscard]] auto send(std::string_view method, - std::uint32_t &service_flags) -> packet::error_type; + [[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; [[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/comm/packet/packet_server.hpp b/repertory/librepertory/include/comm/packet/packet_server.hpp index e81fb1ae..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_; @@ -77,6 +80,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/include/common.hpp b/repertory/librepertory/include/common.hpp index 8c7549da..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{2ULL}; +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/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/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/file_manager/file_manager.hpp b/repertory/librepertory/include/file_manager/file_manager.hpp index 5322e516..498eaf17 100644 --- a/repertory/librepertory/include/file_manager/file_manager.hpp +++ b/repertory/librepertory/include/file_manager/file_manager.hpp @@ -67,17 +67,18 @@ private: 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_; 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 + [[nodiscard]] auto get_open_file_by_handle(std::uint64_t handle, + bool &is_unlinked) 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 ee480707..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; @@ -113,6 +117,10 @@ public: 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_base.hpp b/repertory/librepertory/include/file_manager/open_file_base.hpp index ab527159..5cb866d9 100644 --- a/repertory/librepertory/include/file_manager/open_file_base.hpp +++ b/repertory/librepertory/include/file_manager/open_file_base.hpp @@ -119,6 +119,8 @@ private: }; bool modified_{false}; bool removed_{false}; + bool unlinked_{false}; + api_meta_map unlinked_meta_; private: void file_io_thread(); @@ -164,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; @@ -202,12 +204,16 @@ 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 { 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 +221,10 @@ public: void remove_all() override; 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/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/include/providers/base_provider.hpp b/repertory/librepertory/include/providers/base_provider.hpp index c54fb5d8..93726a98 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 @@ -190,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 77ddc722..30b9bf84 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 @@ -183,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; } @@ -210,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/providers/i_provider.hpp b/repertory/librepertory/include/providers/i_provider.hpp index bd538c0b..3c9587cc 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 @@ -114,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/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/include/types/remote.hpp b/repertory/librepertory/include/types/remote.hpp index 0bc92b40..69bb5f6a 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{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_receive_timeout_ms{120U * 1000U}; -inline constexpr auto default_remote_send_timeout_ms{30U * 1000U}; +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 { 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.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; @@ -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 dfa2770b..44558232 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, @@ -405,6 +407,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/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/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/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/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/app_config.cpp b/repertory/librepertory/src/app_config.cpp index 01a7e1da..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) { @@ -1097,6 +1110,16 @@ auto app_config::load() -> bool { } } + 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; + 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/common.cpp b/repertory/librepertory/src/comm/packet/common.cpp new file mode 100644 index 00000000..1a4263b7 --- /dev/null +++ b/repertory/librepertory/src/comm/packet/common.cpp @@ -0,0 +1,206 @@ +/* + 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 { +non_blocking_guard::non_blocking_guard(boost::asio::ip::tcp::socket &sock_) + : non_blocking(sock_.non_blocking()), sock(sock_) { + 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; + } + + 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 b71ba98e..56efa216 100644 --- a/repertory/librepertory/src/comm/packet/packet_client.cpp +++ b/repertory/librepertory/src/comm/packet/packet_client.cpp @@ -21,33 +21,49 @@ */ #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" #include "utils/common.hpp" #include "utils/error_utils.hpp" -#include "utils/timeout.hpp" +#include "utils/utils.hpp" #include "version.hpp" +#include + +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) { + REPERTORY_USES_FUNCTION_NAME(); + try { cli.socket.shutdown(boost::asio::socket_base::shutdown_both); 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"); } } @@ -63,48 +79,145 @@ void packet_client::close_all() { unique_id_ = utils::create_uuid_string(); } -void packet_client::connect(client &cli) { +auto packet_client::check_version(std::uint32_t client_version, + std::uint32_t &min_version) -> api_error { REPERTORY_USES_FUNCTION_NAME(); try { - resolve(); - boost::asio::connect(cli.socket, resolve_results_); + 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, ctx, min_version)) { + return api_error::comm_error; + } + + if ((min_version & 0xFFU) != 0U) { + min_version = 0U; + return api_error::incompatible_version; + } + + 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(); + + connect_with_deadline(io_context_, 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)); + + std::uint32_t min_version{}; + if (not handshake(cli, io_context_, min_version)) { + 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 (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; +} + +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 { + min_version = 0U; + + data_buffer buffer; + { + packet tmp; + tmp.encode(utils::get_version_number(REPERTORY_MIN_REMOTE_VERSION)); + tmp.encode(utils::generate_random_string(packet_nonce_size)); + tmp.to_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); + if (res != 0) { + throw std::runtime_error("failed to decode server version"); + } + + response.encrypt(cfg_.encryption_token, false); + response.to_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) { + utils::error::raise_error(function_name, e, "handlshake failed"); + } + + return false; } 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; } @@ -116,33 +229,28 @@ 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)); - 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(ctx, 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); + 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_buffer(); + read_exact_with_deadline(ctx, 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 +289,21 @@ 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_read_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 +318,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_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 d714e7da..5157c18e 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" @@ -30,10 +31,13 @@ #include "platform/platform.hpp" #include "types/repertory.hpp" #include "utils/error_utils.hpp" +#include "utils/timeout.hpp" +#include "utils/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) @@ -54,14 +58,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"); } @@ -70,13 +76,106 @@ 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; } } +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(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}; + + 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"); + } + + 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 bytes_written = boost::asio::write( + conn->socket, boost::asio::buffer(boost::asio::buffer(buffer))); + write_timeout.disable(); + + if (bytes_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::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 client version"); + } + + 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(); @@ -95,7 +194,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(); }); } @@ -108,9 +207,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, @@ -121,15 +219,22 @@ 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)); + return; + } - conn->generate_nonce(); + 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)); + + boost::asio::dispatch(conn->socket.get_executor(), [this, conn]() { + if (not handshake(conn)) { + conn->socket.close(); + return; + } packet response; send_response(conn, 0, response); - } + }); } void packet_server::read_header(std::shared_ptr conn) { @@ -139,16 +244,17 @@ 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()); - } else { - auto to_read = - *reinterpret_cast(conn->buffer.data()); - boost::endian::big_to_native_inplace(to_read); - read_packet(conn, to_read); + return; } + + 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); }); } @@ -182,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(); @@ -258,15 +364,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 diff --git a/repertory/librepertory/src/db/impl/rdb_meta_db.cpp b/repertory/librepertory/src/db/impl/rdb_meta_db.cpp index 538f3d90..663abba0 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" @@ -318,9 +319,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; @@ -343,10 +345,14 @@ auto rdb_meta_db::remove_api_path( 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 - return api_error::success; + 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; } json json_data; @@ -417,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 3bf829a7..44b674aa 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" @@ -305,10 +306,14 @@ 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 { - if (key == META_DIRECTORY || key == META_PINNED || key == META_SIZE || - key == META_SOURCE) { - // TODO log warning for unsupported attributes - return api_error::success; + 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; } api_meta_map meta{}; @@ -342,9 +347,13 @@ 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{}; - 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 && res != api_error::item_not_found) { + 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/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 ccda30cb..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_; @@ -102,7 +103,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 +113,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); }); @@ -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 4f213070..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" @@ -160,8 +161,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; } @@ -337,6 +337,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)) { @@ -369,10 +370,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, @@ -382,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; } @@ -495,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 { @@ -634,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); } @@ -1301,9 +1357,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; } @@ -1404,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 7b765afa..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; @@ -267,12 +267,13 @@ 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); } } 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; @@ -285,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/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/file_manager/file_manager.cpp b/repertory/librepertory/src/file_manager/file_manager.cpp index 1d62c060..cc615183 100644 --- a/repertory/librepertory/src/file_manager/file_manager.cpp +++ b/repertory/librepertory/src/file_manager/file_manager.cpp @@ -77,33 +77,39 @@ 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_); - auto closeable_file = get_open_file_by_handle(handle); + bool is_unlinked{}; + auto closeable_file = get_open_file_by_handle(handle, is_unlinked); 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 (is_unlinked) { + unlinked_file_lookup_.erase(handle); } - auto closeable_file = file_iter->second; - open_file_lookup_.erase(api_path); + if (not is_unlinked || closeable_file->get_open_file_count() != 0U) { + return; + } 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() { @@ -158,18 +164,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 +192,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) { @@ -292,8 +311,17 @@ 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_unlinked) const -> std::shared_ptr { + REPERTORY_USES_FUNCTION_NAME(); + + if (unlinked_file_lookup_.contains(handle)) { + is_unlinked = true; + return unlinked_file_lookup_.at(handle); + } + + is_unlinked = false; auto file_iter = std::ranges::find_if(open_file_lookup_, [&handle](auto &&item) -> bool { return item.second->has_handle(handle); @@ -309,14 +337,28 @@ 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; + } + + 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_unlinked{}; + auto file_ptr = get_open_file_by_handle(handle, is_unlinked); if (not file_ptr) { return false; } @@ -329,7 +371,15 @@ 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_unlinked); + if (is_unlinked) { + writeable_file->set_unlinked_meta(file_ptr->get_unlinked_meta()); + 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; + } file = writeable_file; return true; } @@ -404,7 +454,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; } @@ -415,7 +465,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; @@ -473,7 +523,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; }; @@ -602,14 +652,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()) { + if (provider_.is_read_only() || is_unlinked) { return; } @@ -646,7 +698,13 @@ auto file_manager::remove_file(const std::string &api_path) -> api_error { return res; } - auto allocated = close_all(api_path); + 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_); remove_upload(api_path, true); @@ -654,15 +712,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()) { + remove_source_and_shrink_cache(api_path, fsi.source_path, fsi.size, true); + return api_error::success; + } + + auto closed_file = file_iter->second; + open_file_lookup_.erase(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) { + 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, + closed_file->get_source_path(), res, + "failed to shrink cache"); + } + return api_error::success; } @@ -948,7 +1030,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()) { @@ -1068,7 +1150,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_unlinked()) { return; } @@ -1139,7 +1221,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); } } @@ -1194,7 +1276,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 bb48ef1b..beaea559 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,17 @@ auto open_file::native_operation( set_file_size(new_file_size); auto now = std::to_string(utils::time::get_time_now()); + + if (is_unlinked()) { + 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( get_api_path(), { {META_CHANGED, now}, @@ -653,6 +668,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); @@ -766,6 +788,17 @@ auto open_file::write(std::uint64_t write_offset, const data_buffer &data, } auto now = std::to_string(utils::time::get_time_now()); + if (is_unlinked()) { + 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; + } + 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 babfe479..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); @@ -244,6 +249,16 @@ 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; +} + +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_; @@ -293,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 { @@ -308,6 +328,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(); @@ -317,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/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/base_provider.cpp b/repertory/librepertory/src/providers/base_provider.cpp index 90546250..e9d0e98a 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); } @@ -502,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(); @@ -831,6 +787,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; @@ -838,7 +796,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 +806,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 +816,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"); } } diff --git a/repertory/librepertory/src/providers/encrypt/encrypt_provider.cpp b/repertory/librepertory/src/providers/encrypt/encrypt_provider.cpp index 18fe0e7c..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, @@ -474,49 +468,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 {}; } @@ -641,11 +595,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(); } @@ -951,6 +900,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/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/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); 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/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/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( 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/utils.cpp b/repertory/librepertory/src/utils/utils.cpp index 0b51cadd..8aed1dbf 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_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/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.iss.in b/repertory/repertory.iss.in similarity index 100% rename from repertory.iss.in rename to repertory/repertory.iss.in diff --git a/repertory/repertory/include/cli/actions.hpp b/repertory/repertory/include/cli/actions.hpp index 5597f09c..79f22a5e 100644 --- a/repertory/repertory/include/cli/actions.hpp +++ b/repertory/repertory/include/cli/actions.hpp @@ -55,8 +55,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, diff --git a/repertory/repertory/include/cli/check_version.hpp b/repertory/repertory/include/cli/check_version.hpp index e10564f6..bf83efcb 100644 --- a/repertory/repertory/include/cli/check_version.hpp +++ b/repertory/repertory/include/cli/check_version.hpp @@ -23,17 +23,44 @@ #define REPERTORY_INCLUDE_CLI_CHECK_VERSION_HPP_ #include "cli/common.hpp" +#include "comm/packet/packet_client.hpp" +#include "utils/utils.hpp" +#include "version.hpp" 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 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 { + fmt::println("1\nFailed:\n\tRequired: {}\n\tActual: {}", min_version, + client_version); + } + + 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) { - 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/include/ui/ui_main.hpp b/repertory/repertory/include/ui/ui_main.hpp new file mode 100644 index 00000000..efc68203 --- /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/include/ui/ui_server.hpp b/repertory/repertory/include/ui/ui_server.hpp index 8da23394..61a994f8 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}; @@ -74,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 +139,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/main.cpp b/repertory/repertory/main.cpp index c113065c..e851b70a 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); @@ -191,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()) @@ -223,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(cli::actions::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(); 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..92301fd2 --- /dev/null +++ b/repertory/repertory/src/ui/ui_main.cpp @@ -0,0 +1,110 @@ +/* + 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 "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 { + 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 = [&]() -> int { + REPERTORY_USES_FUNCTION_NAME(); + + ui_server server(&config); + + 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"); + } + + repertory::project_cleanup(); + }; + + 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"); + } + + quit_handler(SIGTERM); + return 0; + }; + +#if defined(_WIN32) + return run_ui(); +#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; + } + + return run_ui(); + }); +#endif // defined(_WIN32) +} +} // namespace repertory::ui diff --git a/repertory/repertory/src/ui/ui_server.cpp b/repertory/repertory/src/ui/ui_server.cpp index 2cffbaa5..0df992ec 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" @@ -123,11 +121,22 @@ 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) { + 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__) @@ -136,11 +145,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 @@ -265,15 +274,66 @@ 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(); +} + +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) { + 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 { @@ -812,6 +872,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); @@ -872,127 +955,57 @@ 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 - } - }; - - auto should_launch{true}; - lock_data ui_lock(provider_type::unknown, "ui"); auto res = ui_lock.grab_lock(1U); - if (res == lock_result::locked) { - 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) { - 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 (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(); - - server_.listen_after_bind(); - } 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; - } - } - - notify_and_unlock(nonce_lock); - if (not should_launch) { + if (res != lock_result::success) { + notify_and_unlock(nonce_lock); + open_ui(); return; } - launch_ui(); + 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) { + 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)", + "host", + host, + "port", + std::to_string(desired_port), + })); + notify_and_unlock(nonce_lock); + return; + } + + auto_start_mounts(); + open_ui(); + + notify_and_unlock(nonce_lock); + + server_.listen_after_bind(); } void ui_server::stop() { @@ -1005,6 +1018,7 @@ void ui_server::stop() { stop_requested = true; notify_and_unlock(nonce_lock); + server_.stop(); nonce_thread_->join(); nonce_lock.lock(); diff --git a/repertory/repertory_test/include/fixtures/drive_fixture.hpp b/repertory/repertory_test/include/fixtures/drive_fixture.hpp index 9fd72885..f795dc0f 100644 --- a/repertory/repertory_test/include/fixtures/drive_fixture.hpp +++ b/repertory/repertory_test/include/fixtures/drive_fixture.hpp @@ -19,36 +19,31 @@ 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" #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" #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 -#include "comm/curl/curl_comm.hpp" +#else // !defined(_WIN32) #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) -#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}; @@ -134,7 +129,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; @@ -142,11 +138,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) } @@ -165,7 +162,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"); @@ -194,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; @@ -202,21 +200,24 @@ 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() { 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)}); - ASSERT_TRUE(utils::file::directory(cfg).create_directory()); + const std::string &name) -> std::string { + auto cfg = utils::path::combine(root, {name}); + EXPECT_TRUE(utils::file::directory(cfg).create_directory()); return cfg; }; @@ -232,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) { @@ -248,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) { @@ -338,15 +339,23 @@ 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", - 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); }; @@ -392,6 +401,7 @@ protected: #if !defined(_WIN32) meta.reset(); config.reset(); + config2.reset(); #endif // !defined(_WIN32) std::filesystem::current_path(current_directory); } @@ -463,7 +473,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,46 +499,123 @@ 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); } + + 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; + } + + 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) }; #if !defined(_WIN32) template std::unique_ptr drive_fixture::config; + +template +std::unique_ptr drive_fixture::config2; + 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; +template +provider_type drive_fixture::mount_provider{provider_t::type}; + using platform_provider_types = ::testing::Types; #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_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/providers_fixture.hpp b/repertory/repertory_test/include/fixtures/providers_fixture.hpp new file mode 100644 index 00000000..82c9b763 --- /dev/null +++ b/repertory/repertory_test/include/fixtures/providers_fixture.hpp @@ -0,0 +1,338 @@ +/* + 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 "comm/i_http_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/file.hpp" +#include "utils/path.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 */, + 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 console_consumer consumer; + static std::unique_ptr mgr; + static std::unique_ptr provider; + +protected: + static void SetUpTestCase() { + event_system::instance().start(); + + provider_t::setup(comm, config, provider); + mgr = std::make_unique(*config, *provider); + + EXPECT_TRUE(provider->start( + [](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(); + } + +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 +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; + +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/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/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/include/mocks/mock_provider.hpp b/repertory/repertory_test/include/mocks/mock_provider.hpp index d4b88d4b..563e45ce 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)); @@ -117,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/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); 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_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/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..e067e661 100644 --- a/repertory/repertory_test/src/fuse_drive_access_test.cpp +++ b/repertory/repertory_test/src/fuse_drive_access_test.cpp @@ -21,10 +21,10 @@ */ #if !defined(_WIN32) -#include "fixtures/fuse_fixture.hpp" +#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 @@ -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, 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 c06ac728..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_CASE(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 3990b4ca..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_CASE(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 89acd5f3..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_CASE(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_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 new file mode 100644 index 00000000..321d1791 --- /dev/null +++ b/repertory/repertory_test/src/fuse_drive_fallocate_test.cpp @@ -0,0 +1,261 @@ +/* + 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); + if (res == EOPNOTSUPP) { + ::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(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); + } + 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__) + 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)); +#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) diff --git a/repertory/repertory_test/src/fuse_drive_getattr_fgetattr_test.cpp b/repertory/repertory_test/src/fuse_drive_getattr_fgetattr_test.cpp new file mode 100644 index 00000000..1bf50c4e --- /dev/null +++ b/repertory/repertory_test/src/fuse_drive_getattr_fgetattr_test.cpp @@ -0,0 +1,147 @@ +/* + 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, 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 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)); + EXPECT_EQ(ENOENT, errno); +} + +TYPED_TEST(fuse_test, fgetattr_on_open_file_reflects_size_growth) { + std::string name{"fgetattr"}; + auto src = this->create_file_and_test(name); + this->overwrite_text(src, "ABC"); + + auto desc = ::open(src.c_str(), O_RDWR | O_APPEND); + ASSERT_NE(desc, -1); + + std::string_view more{"DEF"}; + ASSERT_EQ(3, ::write(desc, more.data(), more.size())); + + struct stat st_unix{}; + errno = 0; + ASSERT_EQ(0, ::fstat(desc, &st_unix)); + EXPECT_TRUE(S_ISREG(st_unix.st_mode)); + EXPECT_EQ(6, st_unix.st_size); + + ::close(desc); + this->unlink_file_and_test(src); +} + +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) + 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(desc, &st_unix)); + EXPECT_TRUE(S_ISDIR(st_unix.st_mode)); + + ::close(desc); + this->rmdir_and_test(dir); +} + +TYPED_TEST(fuse_test, fgetattr_on_closed_fd_sets_ebadf) { + std::string name{"fgetattr"}; + auto src = this->create_file_and_test(name); + this->overwrite_text(src, "X"); + + 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(desc, &st_unix)); + EXPECT_EQ(EBADF, errno); + + this->unlink_file_and_test(src); +} + +TYPED_TEST(fuse_test, getattr_reflects_changes_after_write_and_chmod) { + std::string name{"getattr"}; + auto src = this->create_file_and_test(name); + this->overwrite_text(src, "HI"); // 2 bytes + + 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())); + ASSERT_EQ(0, ::fsync(desc)); + ASSERT_EQ(0, ::close(desc)); + + ASSERT_EQ(0, ::chmod(src.c_str(), 0644)); + + 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(6, st_unix.st_size); + + this->unlink_file_and_test(src); +} +} // namespace repertory + +#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 new file mode 100644 index 00000000..15290035 --- /dev/null +++ b/repertory/repertory_test/src/fuse_drive_misc_test.cpp @@ -0,0 +1,64 @@ +/* + 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->config2->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); + fmt::println("client|{}|server|{}", 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->config2->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); + fmt::println("client|{}|server|{}", client_version, min_version); + EXPECT_EQ(api_error::incompatible_version, res); +} +} // namespace repertory + +#endif // !defined(_WIN32) diff --git a/repertory/repertory_test/src/fuse_drive_rdrw_test.cpp b/repertory/repertory_test/src/fuse_drive_rdrw_test.cpp index 9b7bbc63..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_CASE(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 new file mode 100644 index 00000000..9be3db5d --- /dev/null +++ b/repertory/repertory_test/src/fuse_drive_rename_test.cpp @@ -0,0 +1,391 @@ +/* + 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, rename_can_rename_a_file) { + if (this->current_provider != provider_type::sia) { + // TODO finish test + GTEST_SKIP(); + 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); + + 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, rename_can_rename_a_directory) { + if (this->current_provider != provider_type::sia) { + // TODO finish test + GTEST_SKIP(); + return; + } + + std::string src_dir_name{"rename_test"}; + auto src_dir = this->create_directory_and_test(src_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())); + + errno = 0; + EXPECT_EQ(-1, ::access(src_dir.c_str(), F_OK)); + EXPECT_EQ(ENOENT, errno); + + 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_can_overwrite_existing_file) { + if (this->current_provider != provider_type::sia) { + // TODO finish test + GTEST_SKIP(); + 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); + + this->overwrite_text(src, "SRC"); + this->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", this->slurp(dst)); + + this->unlink_file_and_test(dst); +} + +TYPED_TEST(fuse_test, rename_can_rename_file_into_different_directory) { + if (this->current_provider != provider_type::sia) { + // TODO finish test + GTEST_SKIP(); + return; + } + + 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"}); + + this->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", this->slurp(dst)); + + this->unlink_file_and_test(dst); + this->rmdir_and_test(dir1); + this->rmdir_and_test(dir2); +} + +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); + this->overwrite_text(src, "CMDC"); + + errno = 0; + EXPECT_EQ(0, ::rename(src.c_str(), src.c_str())); + EXPECT_EQ("CMDC", this->slurp(src)); + + this->unlink_file_and_test(src); +} + +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; + } + + std::string src_file_name{"rename"}; + auto src = this->create_file_path(src_file_name); + + 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 + GTEST_SKIP(); + 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_fails_if_destination_is_directory) { + 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); + + std::string dir_name{"dir"}; + auto dest_dir = this->create_directory_and_test(dir_name); + + errno = 0; + 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(dest_dir); +} + +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; + } + + 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); + + 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(src_dir); + this->rmdir_and_test(dest_dir); +} + +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; + } + + 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); + + 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(src_dir); + this->rmdir_and_test(dest_dir); +} + +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; + } + + 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); + + this->overwrite_text(src, "NEW"); + this->overwrite_text(dst, "OLD"); + + ASSERT_EQ(0, ::chmod(dst.c_str(), 0444)); + + errno = 0; + 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, res); + + EXPECT_EQ("NEW", this->slurp(dst)); + + ASSERT_EQ(0, ::chmod(dst.c_str(), 0644)); + this->unlink_file_and_test(dst); +} + +TYPED_TEST(fuse_test, rename_file_retains_open_file_descriptor) { + if (this->current_provider != provider_type::sia) { + // TODO finish test + GTEST_SKIP(); + 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); + + this->overwrite_text(src, "HELLO"); + + int desc = ::open(src.c_str(), O_RDWR); + ASSERT_NE(desc, -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(desc, 0, SEEK_END)); + this->write_all(desc, " WORLD"); + ::close(desc); + + EXPECT_EQ("HELLO WORLD", this->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"); + + 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) diff --git a/repertory/repertory_test/src/fuse_drive_test_legacy.cpp b/repertory/repertory_test/src/fuse_drive_test_legacy.cpp index 41ba37cd..1405c343 100644 --- a/repertory/repertory_test/src/fuse_drive_test_legacy.cpp +++ b/repertory/repertory_test/src/fuse_drive_test_legacy.cpp @@ -1,68 +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)); -// -// 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) { @@ -118,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; @@ -302,110 +204,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 diff --git a/repertory/repertory_test/src/fuse_drive_truncate_ftruncate_test.cpp b/repertory/repertory_test/src/fuse_drive_truncate_ftruncate_test.cpp new file mode 100644 index 00000000..3a7bcdff --- /dev/null +++ b/repertory/repertory_test/src/fuse_drive_truncate_ftruncate_test.cpp @@ -0,0 +1,200 @@ +/* + 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_fails_if_file_is_read_only) { + std::string name{"trunc_ro"}; + auto src = this->create_file_and_test(name); + this->overwrite_text(src, "DATA"); + + ASSERT_EQ(0, ::chmod(src.c_str(), 0444)); + + errno = 0; + int res = ::truncate(src.c_str(), 2); + if (res == -1 && errno == EROFS) { + ASSERT_EQ(0, ::chmod(src.c_str(), 0644)); + this->unlink_file_and_test(src); + return; + } + + EXPECT_EQ(-1, res); + EXPECT_TRUE(errno == EACCES || errno == EPERM); + + ASSERT_EQ(0, ::chmod(src.c_str(), 0644)); + this->unlink_file_and_test(src); +} + +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"); + + auto desc = ::open(src.c_str(), O_RDWR); + ASSERT_NE(desc, -1); + + errno = 0; + ASSERT_EQ(0, ::ftruncate(desc, 4)); + ::close(desc); + + EXPECT_EQ(4, this->stat_size(src)); + EXPECT_EQ("HELL", this->slurp(src)); + + this->unlink_file_and_test(src); +} + +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"); + + auto desc = ::open(src.c_str(), O_RDWR); + ASSERT_NE(desc, -1); + + errno = 0; + ASSERT_EQ(0, ::ftruncate(desc, 6)); + ::close(desc); + + EXPECT_EQ(6, this->stat_size(src)); + + 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(src); +} + +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"); + + auto desc = ::open(src.c_str(), O_RDONLY); + ASSERT_NE(desc, -1); + + errno = 0; + EXPECT_EQ(-1, ::ftruncate(desc, 1)); + EXPECT_TRUE(errno == EBADF || errno == EINVAL); + + ::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; + + std::string name{"ftrunc_ro_mount"}; + std::string p = this->create_file_and_test(name); + this->overwrite_text(p, "X"); + + 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(desc, 0); + if (res == -1 && errno == EROFS) { + ::close(desc); + this->unlink_file_and_test(p); + GTEST_SKIP() << "read-only mount; ftruncate returns EROFS"; + } + + ASSERT_EQ(0, res) << std::strerror(errno); + ::close(desc); + + this->unlink_file_and_test(p); +} +*/ +} // namespace repertory + +#endif // !defined(_WIN32) 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..6818b543 --- /dev/null +++ b/repertory/repertory_test/src/fuse_drive_unlink_test.cpp @@ -0,0 +1,116 @@ +/* + 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); + + { + auto desc = ::open(path.c_str(), O_RDWR); + 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())); + + 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)); + 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) 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..063ef0ad --- /dev/null +++ b/repertory/repertory_test/src/fuse_drive_utimens_futimens_test.cpp @@ -0,0 +1,333 @@ +/* + 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" + +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)); + +#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__) +} + +[[nodiscard]] auto ts_make(time_t sec, long long nsec) -> timespec { + return timespec{ + .tv_sec = sec, + .tv_nsec = nsec, + }; +} + +[[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 { + timespec spec{}; +#if defined(CLOCK_REALTIME) + clock_gettime(CLOCK_REALTIME, &spec); +#else // defined(CLOCK_REALTIME) + timeval val{}; + gettimeofday(&val, nullptr); + spec.tv_sec = val.tv_sec; + spec.tv_nsec = val.tv_usec * 1000; +#endif // !defined(CLOCK_REALTIME) + return to_ns(spec); +} + +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; + + 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(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.data(), 0)); + + long long at1{}; + long long mt1{}; + get_times_ns(src, at1, mt1); + + 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); +} + +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); + + 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), + }; + + errno = 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(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); +} + +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); + + 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))), + }; + + errno = 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(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); +} + +TYPED_TEST(fuse_test, utimens_set_now_for_both) { + std::string name{"utimens"}; + auto src = this->create_file_and_test(name); + + 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.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(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 file_name{"utimens"}; + auto missing = this->create_file_path(file_name); + + auto spec = std::array{ + ts_make(123, 0), + ts_make(456, 0), + }; + + errno = 0; + EXPECT_EQ(-1, ::utimensat(AT_FDCWD, missing.c_str(), spec.data(), 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); + + auto spec = std::array{ + ts_make(0, static_cast(NANOS_PER_SECOND)), + ts_make(0, 0), + }; + + errno = 0; + EXPECT_EQ(-1, ::utimensat(AT_FDCWD, src.c_str(), spec.data(), 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; + + 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(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.data())); + ::close(desc); + + long long access_time{}; + long long modified_time{}; + get_times_ns(src, access_time, modified_time); + 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); +} + +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); + + 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))), + }; + + errno = 0; + 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(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); +} + +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); + + auto spec = std::array{ + ts_make(0, 0), + ts_make(0, static_cast(NANOS_PER_SECOND)), + }; + + errno = 0; + EXPECT_EQ(-1, ::futimens(desc, spec.data())); + EXPECT_EQ(EINVAL, errno); + + ::close(desc); + this->unlink_file_and_test(src); +} +} // namespace repertory + +#endif // !defined(_WIN32) 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); 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/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); diff --git a/repertory/repertory_test/src/providers_test.cpp b/repertory/repertory_test/src/providers_test.cpp index 947fcb6c..a3a545d6 100644 --- a/repertory/repertory_test/src/providers_test.cpp +++ b/repertory/repertory_test/src/providers_test.cpp @@ -21,234 +21,251 @@ */ #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 "fixtures/providers_fixture.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 { -#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, '/'); - } -}; -} // 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_SUITE(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) { + 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); + } + 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())); + this->create_file(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; + } + + this->create_directory("/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())); + this->create_file(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; + } + + this->create_file("/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; + } + + this->create_directory("/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"); + this->create_file("/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"); + this->create_directory("/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) { @@ -262,158 +279,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"); + this->create_directory("/clone"); + this->create_directory("/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"); + this->create_directory("/clone"); + this->create_file("/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; + } + + this->create_file("/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"); + this->create_directory("/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); + this->decrypt_parts(file_name); EXPECT_STREQ("test.txt", file_name.c_str()); return; } - create_file(provider, "/pt01.txt"); + this->create_file("/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"}); @@ -421,61 +395,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)); - check_forced_dirs(list); + EXPECT_EQ(api_error::success, + this->provider->get_directory_items("/", 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(cfg, dir_item.api_parent); - decrypt_parts(cfg, dir_item.api_path); + this->decrypt_parts(dir_item.api_parent); + this->decrypt_parts(dir_item.api_path); } auto dir = @@ -500,21 +437,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)); - check_forced_dirs(list); + EXPECT_EQ(api_error::success, + this->provider->get_directory_items(api_path, 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(cfg, dir_item.api_parent); - decrypt_parts(cfg, dir_item.api_path); + this->decrypt_parts(dir_item.api_parent); + this->decrypt_parts(dir_item.api_path); } auto file2 = @@ -532,14 +470,14 @@ 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"); + 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, provider.get_directory_items("/", list)); - check_forced_dirs(list); + EXPECT_EQ(api_error::success, this->provider->get_directory_items("/", list)); + this->check_forced_dirs(list); EXPECT_GE(list.size(), std::size_t(6U)); auto iter = std::ranges::find_if( @@ -570,68 +508,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"); + this->create_file("/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; + } + + 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)); + this->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)); + 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()); @@ -644,10 +602,10 @@ static void get_file(const app_config &cfg, i_provider &provider) { return; } - create_file(provider, "/pt01.txt"); + this->create_file("/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()); @@ -660,253 +618,675 @@ 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"); + this->create_directory("/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")); } +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()); -static void get_file_list(const app_config &cfg, i_provider &provider) { - 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::string api_path{}; + EXPECT_EQ(api_error::success, + this->provider->get_api_path_from_source(source_path, api_path)); - 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 run_tests(const app_config &cfg, i_provider &provider) { - get_file_list(cfg, provider); - ASSERT_FALSE(::testing::Test::HasFailure()); - - 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); - - // TODO need to test read when file size changes for encrypt provider - /* get_file_list(provider); - get_file_size(provider); - get_filesystem_item(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); - get_total_drive_space(provider); - get_total_item_count(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); - remove_item_meta(provider); - rename_file(provider); - set_item_meta(provider); - set_item_meta2(provider); - upload_file(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(); + std::uint64_t 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), + size); + return; } - event_system::instance().stop(); + auto &file = test::create_random_file(128U); + auto api_path = + fmt::format("/{}", utils::path::strip_to_file_name(file.get_path())); + this->create_file(api_path); + + stop_type stop_requested{false}; + auto res = + this->provider->upload_file(api_path, file.get_path(), stop_requested); + ASSERT_EQ(api_error::success, res); + + std::uint64_t size{}; + res = this->provider->get_file_size(api_path, size); + EXPECT_EQ(api_error::success, res); + EXPECT_EQ(*file.size(), size); + + res = this->provider->remove_file(api_path); + EXPECT_EQ(api_error::success, res); } -TEST(providers_test, s3_provider) { - auto config_path = - utils::path::combine(test::get_test_output_dir(), {"provider", "s3"}); +TYPED_TEST(providers_test, get_file_size_fails_if_path_not_found) { + std::uint64_t size{}; + auto res = this->provider->get_file_size("/cow/moose/doge/chicken", size); - console_consumer consumer{}; - event_system::instance().start(); + EXPECT_EQ(api_error::item_not_found, res); + EXPECT_EQ(0U, size); +} - { - app_config cfg(provider_type::s3, config_path); - { - 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()); +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, + 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 = 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 = this->provider->get_file_size(api_path, size); + ASSERT_EQ(api_error::success, res); + EXPECT_EQ(size, fsi.size); + + return; + } + + auto &file = test::create_random_file(128U); + auto api_path = + fmt::format("/{}", utils::path::strip_to_file_name(file.get_path())); + this->create_file(api_path); + + stop_type stop_requested{false}; + auto res = + this->provider->upload_file(api_path, file.get_path(), stop_requested); + ASSERT_EQ(api_error::success, res); + EXPECT_EQ(api_error::success, + this->provider->set_item_meta(api_path, META_SIZE, + std::to_string(*file.size()))); + + filesystem_item 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 = this->provider->remove_file(api_path); + EXPECT_EQ(api_error::success, res); +} + +TYPED_TEST(providers_test, get_filesystem_item_root_is_directory) { + filesystem_item 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); +} + +TYPED_TEST(providers_test, get_filesystem_item_fails_if_file_is_not_found) { + filesystem_item fsi{}; + auto res = this->provider->get_filesystem_item("/cow/moose/doge/chicken", + false, fsi); + EXPECT_EQ(api_error::item_not_found, res); +} + +TYPED_TEST(providers_test, + get_filesystem_item_fails_if_directory_is_not_found) { + filesystem_item fsi{}; + auto res = + this->provider->get_filesystem_item("/cow/moose/doge/chicken", true, fsi); + EXPECT_EQ(api_error::directory_not_found, res); +} + +TYPED_TEST(providers_test, get_filesystem_item_from_source_path) { + std::string api_path; + std::string source_path; + std::uint64_t size{}; + 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()); + + size = utils::encryption::encrypting_reader::calculate_encrypted_size( + src_size.value(), 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(); + this->create_file(api_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 = + this->provider->upload_file(api_path, source_path, stop_requested); + ASSERT_EQ(api_error::success, res); + } + + filesystem_item 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 (this->provider->get_provider_type() != provider_type::encrypt) { + EXPECT_EQ(api_error::success, this->provider->remove_file(api_path)); + } +} +TYPED_TEST(providers_test, + get_filesystem_item_from_source_path_fails_if_file_is_not_found) { + filesystem_item fsi{}; + auto res = this->provider->get_filesystem_item_from_source_path( + "/cow/moose/doge/chicken", fsi); + EXPECT_EQ(api_error::item_not_found, res); +} + +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); +} + +TYPED_TEST(providers_test, remove_file_fails_if_item_is_directory) { + if (this->provider->is_read_only()) { + EXPECT_EQ(api_error::not_implemented, + this->provider->remove_file("/dir01")); + return; + } + + 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")); +} + +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; + } + + 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")); +} + +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; + } + + 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")); + EXPECT_EQ(api_error::success, + 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 = this->provider->get_pinned_files(); + EXPECT_EQ(std::size_t(2U), pinned.size()); + + 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")); + EXPECT_EQ(api_error::success, this->provider->remove_file("/nopin01.txt")); +} + +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; + } + + 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, + this->provider->set_item_meta("/pin02.txt", META_PINNED, "true")); + + auto pinned = this->provider->get_pinned_files(); + EXPECT_EQ(std::size_t(2U), pinned.size()); + + EXPECT_EQ(api_error::success, + 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(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")); + pinned = this->provider->get_pinned_files(); + EXPECT_TRUE(pinned.empty()); + + EXPECT_EQ(api_error::success, this->provider->remove_file("/pin01.txt")); + EXPECT_EQ(api_error::success, this->provider->remove_file("/pin02.txt")); +} + +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; + } + + 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")); + EXPECT_EQ(api_error::success, this->provider->set_item_meta( + "/pin_delete.txt", META_PINNED, "true")); + EXPECT_EQ(api_error::success, + this->provider->set_item_meta("/nopin.txt", META_PINNED, "false")); + + auto pinned = this->provider->get_pinned_files(); + EXPECT_EQ(std::size_t(2U), pinned.size()); + + 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(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")); +} + +TYPED_TEST(providers_test, get_total_item_count) { + if (this->provider->get_provider_type() == provider_type::encrypt) { + // TODO revisit + /* std::uint64_t count{this->provider->get_total_item_count()}; + EXPECT_EQ(3U, count); */ + return; + } + + std::uint64_t before{this->provider->get_total_item_count()}; + + 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); + + 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{this->provider->get_total_item_count()}; + EXPECT_EQ(before, after); +} + +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, 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, + this->provider->get_file_size(file.api_path, size)); + sum_sizes += size; } - 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(); + std::uint64_t used{this->provider->get_used_drive_space()}; + EXPECT_EQ(sum_sizes, used); */ + return; } - event_system::instance().stop(); + std::uint64_t before{this->provider->get_used_drive_space()}; + + 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())); + + this->create_file(api_path1); + this->create_file(api_path2); + + stop_type stop_requested{false}; + 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, + this->provider->set_item_meta(api_path1, META_SIZE, + std::to_string(*file1.size()))); + EXPECT_EQ(api_error::success, + this->provider->set_item_meta(api_path2, META_SIZE, + std::to_string(*file2.size()))); + + std::uint64_t mid{this->provider->get_used_drive_space()}; + EXPECT_EQ(before + *file1.size() + *file2.size(), mid); + + 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{this->provider->get_used_drive_space()}; + EXPECT_EQ(before, after); } -TEST(providers_test, sia_provider) { - auto config_path = - utils::path::combine(test::get_test_output_dir(), {"sia", "provider"}); +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); + } +} - 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(); +TYPED_TEST(providers_test, remove_item_meta) { + std::string api_path{"/rim_custom_ok.txt"}; + if (this->provider->get_provider_type() == provider_type::encrypt) { + EXPECT_EQ(api_error::success, + this->provider->remove_item_meta(api_path, "user.custom")); + return; } - event_system::instance().stop(); + this->create_file(api_path); + + EXPECT_EQ(api_error::success, + this->provider->set_item_meta(api_path, "user.custom", "abc123")); + + api_meta_map 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, + this->provider->remove_item_meta(api_path, "user.custom")); + + api_meta_map 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, this->provider->remove_file(api_path)); +} + +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 = this->provider->remove_item_meta("/cow_moose_doge_chicken", + "user.custom"); + EXPECT_EQ(api_error::item_not_found, res); +} + +TYPED_TEST(providers_test, remove_item_meta_restricted_names_fail) { + std::string api_path; + 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, + this->provider->get_api_path_from_source(source_path, api_path)); + } else { + api_path = "/rim_restricted.txt"; + this->create_file(api_path); + } + + for (const auto &key : META_USED_NAMES) { + 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, + this->provider->get_item_meta(api_path, meta)); + EXPECT_TRUE(meta.contains(std::string{key})); + } + + if (this->provider->get_provider_type() != provider_type::encrypt) { + EXPECT_EQ(api_error::success, this->provider->remove_file(api_path)); + } +} + +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"}; + this->create_file(src); + + std::string src_meta_size{}; + std::string src_meta_source{}; + EXPECT_EQ(api_error::success, + this->provider->get_item_meta(src, META_SIZE, src_meta_size)); + EXPECT_EQ(api_error::success, + this->provider->get_item_meta(src, META_SOURCE, src_meta_source)); + + EXPECT_EQ(api_error::success, this->provider->rename_file(src, dst)); + + bool exists{}; + EXPECT_EQ(api_error::success, this->provider->is_file(src, exists)); + EXPECT_FALSE(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, + this->provider->get_item_meta(dst, META_SIZE, dst_meta_size)); + EXPECT_EQ(api_error::success, + 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, this->provider->remove_file(dst)); +} + +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 = this->provider->rename_file("/rn_missing.txt", "/rn_any.txt"); + EXPECT_EQ(api_error::item_not_found, res); +} + +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) { + 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"); + EXPECT_EQ(api_error::not_implemented, res); + + if (this->provider->get_provider_type() != provider_type::encrypt) { + EXPECT_EQ(api_error::success, + this->provider->remove_file("/rn_src_conflict.txt")); + EXPECT_EQ(api_error::success, + this->provider->remove_file("/rn_dst_conflict.txt")); + } + return; + } + + std::string src{"/rn_src_conflict.txt"}; + std::string dst{"/rn_dst_conflict.txt"}; + this->create_file(src); + this->create_file(dst); + + auto res = this->provider->rename_file(src, dst); + EXPECT_EQ(api_error::item_exists, res); + + bool exists{}; + EXPECT_EQ(api_error::success, this->provider->is_file(src, exists)); + EXPECT_TRUE(exists); + EXPECT_EQ(api_error::success, this->provider->is_file(dst, exists)); + EXPECT_TRUE(exists); + + EXPECT_EQ(api_error::success, this->provider->remove_file(src)); + EXPECT_EQ(api_error::success, this->provider->remove_file(dst)); +} + +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) { + 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"); + EXPECT_EQ(api_error::not_implemented, res); + + if (this->provider->get_provider_type() != provider_type::encrypt) { + EXPECT_EQ(api_error::success, + this->provider->remove_file("/rn_src_conflict.txt")); + EXPECT_EQ(api_error::success, + this->provider->remove_directory("/rn_dst_conflict")); + } + return; + } + + std::string src{"/rn_src_conflict.txt"}; + std::string dst{"/rn_dst_conflict"}; + this->create_file(src); + this->create_directory(dst); + + auto res = this->provider->rename_file(src, dst); + EXPECT_EQ(api_error::directory_exists, res); + + bool exists{}; + EXPECT_EQ(api_error::success, this->provider->is_file(src, exists)); + EXPECT_TRUE(exists); + EXPECT_EQ(api_error::success, this->provider->is_directory(dst, exists)); + EXPECT_TRUE(exists); + + 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_is_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_NE(res, api_error::success); +} + +TYPED_TEST(providers_test, + file_is_not_a_directory_and_a_directory_is_not_a_file) { + 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..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_CASE(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 09454eb3..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_CASE(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 0c9befdb..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_CASE(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 37aec63a..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_CASE(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 cdade53a..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_CASE(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 98a03513..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_CASE(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 5cd67322..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_CASE(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 49d4a255..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_CASE(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 f01bf844..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_CASE(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; 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 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); } 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, 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; 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(