Merge branch 'v2.1.0-rc-develop' of https://git.fifthgrid.com/blockstorage/repertory into v2.1.0-rc-develop
This commit is contained in:
@@ -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
|
||||
|
11
CHANGELOG.md
11
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
|
||||
|
||||
<!-- markdownlint-disable-next-line -->
|
||||
|
@@ -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
|
||||
)
|
||||
|
68
repertory/librepertory/include/comm/packet/common.hpp
Normal file
68
repertory/librepertory/include/comm/packet/common.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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<boost::asio::ip::tcp>::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_
|
@@ -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<std::uint32_t>(buffer_.size());
|
||||
|
@@ -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<std::shared_ptr<client>> clients_;
|
||||
std::vector<std::thread> service_threads_;
|
||||
|
||||
private:
|
||||
static void close(client &cli);
|
||||
@@ -67,23 +68,35 @@ private:
|
||||
|
||||
[[nodiscard]] auto get_client() -> std::shared_ptr<client>;
|
||||
|
||||
[[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<client> &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
|
||||
|
||||
|
@@ -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<std::thread> server_thread_;
|
||||
std::vector<std::thread> 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<connection> conn) const -> bool;
|
||||
|
||||
void initialize(const uint16_t &port, uint8_t pool_size);
|
||||
|
||||
void listen_for_connection(tcp::acceptor &acceptor);
|
||||
|
@@ -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"};
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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__)
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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 =
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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 <typename error_type>
|
||||
@@ -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);
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -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<repertory::packet_client_timeout> {
|
||||
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<std::string>(value.event_name);
|
||||
data.at("function_name").get_to<std::string>(value.function_name);
|
||||
data.at("msg").get_to<std::string>(value.msg);
|
||||
}
|
||||
};
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
|
@@ -67,17 +67,18 @@ private:
|
||||
std::unordered_map<std::string, std::shared_ptr<i_closeable_open_file>>
|
||||
open_file_lookup_;
|
||||
stop_type stop_requested_{false};
|
||||
std::unordered_map<std::uint64_t, std::shared_ptr<i_closeable_open_file>>
|
||||
unlinked_file_lookup_;
|
||||
std::unordered_map<std::string, std::unique_ptr<upload>> upload_lookup_;
|
||||
mutable std::mutex upload_mtx_;
|
||||
std::condition_variable upload_notify_;
|
||||
std::unique_ptr<std::thread> 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<i_closeable_open_file>;
|
||||
|
||||
[[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<i_open_file> &file) -> bool;
|
||||
|
||||
[[nodiscard]] auto get_open_file(std::uint64_t handle, bool write_supported,
|
||||
std::shared_ptr<i_open_file> &file) -> bool;
|
||||
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
|
@@ -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*/)
|
||||
|
@@ -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;
|
||||
|
@@ -34,33 +34,37 @@ private:
|
||||
std::atomic<std::uint32_t> 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
|
||||
|
||||
|
@@ -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<repertory::remote::remote_config> {
|
||||
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<repertory::remote::remote_config> {
|
||||
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);
|
||||
|
@@ -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"};
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -45,9 +45,8 @@ inline const std::array<std::string, 4U> 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)
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
206
repertory/librepertory/src/comm/packet/common.cpp
Normal file
206
repertory/librepertory/src/comm/packet/common.cpp
Normal file
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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<std::uint8_t, 1> 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 <class op_t>
|
||||
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<repertory::packet_client_timeout>(
|
||||
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<boost::asio::ip::tcp>::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<std::uint8_t *>(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<std::uint8_t *>(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
|
@@ -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<std::uint32_t>(buffer_.size()));
|
||||
if (include_size) {
|
||||
encode_top(static_cast<std::uint32_t>(buffer_.size()));
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::raise_error(function_name, e, "exception occurred");
|
||||
}
|
||||
|
@@ -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 <utils/config.hpp>
|
||||
|
||||
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<packet_client::client> {
|
||||
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<client>(io_context_);
|
||||
connect(*cli);
|
||||
auto cli = std::make_shared<client>(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<client> &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<client> &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<std::uint32_t>(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<std::uint32_t *>(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<packet_client_timeout>(
|
||||
"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<std::uint32_t>(bytes_written);
|
||||
}
|
||||
request_timeout.disable();
|
||||
|
||||
timeout response_timeout(
|
||||
[method, current_client]() {
|
||||
event_system::instance().raise<packet_client_timeout>(
|
||||
"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);
|
||||
}
|
||||
}
|
||||
|
@@ -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<service_stop_begin>(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<service_stop_end>(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<connection> 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<std::uint32_t>(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<packet_server::connection>(io_context_, acceptor);
|
||||
acceptor.async_accept(conn->socket, [this, conn](auto &&err) {
|
||||
on_accept(conn, std::forward<decltype(err)>(err));
|
||||
});
|
||||
acceptor.async_accept(conn->socket,
|
||||
[this, conn](auto &&err) { on_accept(conn, err); });
|
||||
}
|
||||
|
||||
void packet_server::on_accept(std::shared_ptr<connection> conn,
|
||||
@@ -121,15 +219,22 @@ void packet_server::on_accept(std::shared_ptr<connection> 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<connection> conn) {
|
||||
@@ -139,16 +244,17 @@ void packet_server::read_header(std::shared_ptr<connection> 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<std::uint32_t *>(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<connection> 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<connection> 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
|
||||
|
@@ -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) {
|
||||
|
@@ -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) {
|
||||
|
@@ -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)
|
||||
|
@@ -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<api_error(std::string from_api_file,
|
||||
std::string to_api_path)> &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<api_error(std::string api_path)> &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();
|
||||
|
||||
|
@@ -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<std::uint64_t>(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<std::uint64_t>(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<i_open_file> 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<std::uint64_t>(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<i_open_file> open_file;
|
||||
if (not fm_->get_open_file(f_info->fh, true, open_file)) {
|
||||
return api_error::item_not_found;
|
||||
|
@@ -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<bool>(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<bool>(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<bool>(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<bool>(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<std::size_t>::max()) { ret = -ERANGE; } else {
|
||||
packet request; request.encode(path); request.encode(name);
|
||||
request.encode(size);
|
||||
@@ -226,7 +227,7 @@ response.CurrentPointer(), static_cast<std::size_t>(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<std::size_t>::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<std::size_t>::max()) { ret = -ERANGE; } else { packet
|
||||
request; request.encode(path); request.encode(size);
|
||||
|
||||
@@ -297,7 +298,7 @@ static_cast<std::size_t>(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<std::size_t>::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<std::size_t>::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;
|
||||
}
|
||||
|
@@ -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<std::uint64_t>(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());
|
||||
|
@@ -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<remote::file_handle>(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<native_handle>(handle), EBADF);
|
||||
if (res == 0) {
|
||||
directory = utils::file::directory(file_path).exists();
|
||||
auto res = -1;
|
||||
auto file_path = get_open_file_path(static_cast<native_handle>(handle));
|
||||
if (file_path.empty()) {
|
||||
errno = EBADF;
|
||||
} else {
|
||||
struct stat64 u_stat{};
|
||||
res = fstat64(static_cast<native_handle>(handle), &u_stat);
|
||||
if (res == -1 && errno == ESTALE) {
|
||||
std::uint64_t internal_handle{};
|
||||
res = ioctl(static_cast<native_handle>(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<native_handle>(handle), EBADF);
|
||||
if (res == 0) {
|
||||
#if defined(__APPLE__)
|
||||
res = datasync ? fcntl(static_cast<native_handle>(handle), F_FULLFSYNC)
|
||||
: fsync(static_cast<native_handle>(handle));
|
||||
auto res = datasync ? fcntl(static_cast<native_handle>(handle), F_FULLFSYNC)
|
||||
: fsync(static_cast<native_handle>(handle));
|
||||
#else // !defined(__APPLE__)
|
||||
res = datasync ? fdatasync(static_cast<native_handle>(handle))
|
||||
: fsync(static_cast<native_handle>(handle));
|
||||
auto res = datasync ? fdatasync(static_cast<native_handle>(handle))
|
||||
: fsync(static_cast<native_handle>(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<native_handle>(handle), EBADF);
|
||||
if (res == 0) {
|
||||
res =
|
||||
ftruncate(static_cast<native_handle>(handle), static_cast<off_t>(size));
|
||||
}
|
||||
auto res =
|
||||
ftruncate(static_cast<native_handle>(handle), static_cast<off_t>(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<remote::file_handle>(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<data_buffer *>(buffer);
|
||||
|
||||
ssize_t bytes_read{has_open_info(static_cast<native_handle>(handle), EBADF)};
|
||||
if (bytes_read == 0) {
|
||||
data.resize(read_size);
|
||||
bytes_read = pread64(static_cast<native_handle>(handle), data.data(),
|
||||
read_size, static_cast<off_t>(read_offset));
|
||||
}
|
||||
data.resize(read_size);
|
||||
auto bytes_read = pread64(static_cast<native_handle>(handle), data.data(),
|
||||
read_size, static_cast<off_t>(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<native_handle>(handle), EBADF);
|
||||
if (res == 0) {
|
||||
res = close(static_cast<native_handle>(handle));
|
||||
remove_open_info(static_cast<native_handle>(handle));
|
||||
}
|
||||
auto res = close(static_cast<native_handle>(handle));
|
||||
remove_open_info(static_cast<native_handle>(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<time_t>(op);
|
||||
std::array<struct timespec, 2U> 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<time_t>(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<native_handle>(handle), EBADF)};
|
||||
if (bytes_written == 0) {
|
||||
bytes_written = pwrite64(static_cast<native_handle>(handle), buffer,
|
||||
write_size, static_cast<off_t>(write_offset));
|
||||
}
|
||||
auto bytes_written = pwrite64(static_cast<native_handle>(handle), buffer,
|
||||
write_size, static_cast<off_t>(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<packet::error_type>(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();
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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();
|
||||
|
||||
|
@@ -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<int>(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();
|
||||
|
||||
|
@@ -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) {
|
||||
|
@@ -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<i_open_file> 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<i_closeable_open_file> {
|
||||
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<i_open_file> &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<i_open_file> &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<i_closeable_open_file> 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<file_upload_retry>(
|
||||
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<file_upload_retry>(
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@@ -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},
|
||||
|
@@ -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<filesystem_item_opened>(
|
||||
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;
|
||||
}
|
||||
|
||||
|
@@ -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<filesystem_item_added>(
|
||||
|
@@ -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<filesystem_item_added>(
|
||||
|
@@ -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_item> 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");
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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<DWORD>(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<std::string> {
|
||||
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();
|
||||
|
@@ -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) {
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -210,9 +210,11 @@ static const std::unordered_map<api_error, std::string> 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"},
|
||||
|
@@ -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<repertory_exception>(
|
||||
function, static_cast<std::string>(msg) + "|ap|" +
|
||||
static_cast<std::string>(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<repertory_exception>(
|
||||
|
@@ -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) {
|
||||
|
@@ -19,8 +19,6 @@
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "rocksdb/table.h"
|
||||
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
#include "app_config.hpp"
|
||||
@@ -90,4 +88,13 @@ auto create_volume_label(provider_type prov) -> std::string {
|
||||
auto get_attributes_from_meta(const api_meta_map &meta) -> DWORD {
|
||||
return static_cast<DWORD>(utils::string::to_uint32(meta.at(META_ATTRIBUTES)));
|
||||
}
|
||||
|
||||
auto get_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
|
||||
|
@@ -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;
|
||||
|
@@ -55,8 +55,6 @@ struct option_hasher {
|
||||
|
||||
inline const std::unordered_map<utils::cli::option, action, option_hasher>
|
||||
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,
|
||||
|
@@ -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<const char *> /* args */,
|
||||
const std::string &data_directory,
|
||||
provider_type prov,
|
||||
const std::string & /*unique_id*/,
|
||||
std::string /*user*/,
|
||||
std::string /*password*/) -> exit_code {
|
||||
[[nodiscard]] inline auto
|
||||
check_version(const std::string &data_directory, provider_type prov,
|
||||
const std::string &remote_host, std::uint16_t remote_port)
|
||||
-> exit_code {
|
||||
if (prov == provider_type::remote) {
|
||||
app_config config(prov, data_directory);
|
||||
auto remote_cfg = config.get_remote_config();
|
||||
remote_cfg.host_name_or_ip = remote_host;
|
||||
remote_cfg.api_port = remote_port;
|
||||
config.set_remote_config(remote_cfg);
|
||||
packet_client client(config.get_remote_config());
|
||||
|
||||
std::uint32_t min_version{};
|
||||
auto client_version = utils::get_version_number(project_get_version());
|
||||
auto 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;
|
||||
}
|
||||
|
||||
|
29
repertory/repertory/include/ui/ui_main.hpp
Normal file
29
repertory/repertory/include/ui/ui_main.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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<const char *> &args) -> int;
|
||||
} // namespace repertory::ui
|
||||
|
||||
#endif // REPERTORY_INCLUDE_UI_MAIN_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,
|
||||
|
@@ -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<repertory_drive>(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<std::int32_t>(exit_code::ui_failed);
|
||||
} else {
|
||||
const auto run_ui = [](auto *server) {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
static std::atomic<ui::ui_server *> 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<std::int32_t>(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<std::int32_t>(res));
|
||||
}
|
||||
|
||||
ret = ((res == exit_code::mount_result) ? mount_result
|
||||
: static_cast<std::int32_t>(res));
|
||||
}
|
||||
|
||||
repertory::project_cleanup();
|
||||
|
@@ -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 {
|
||||
|
110
repertory/repertory/src/ui/ui_main.cpp
Normal file
110
repertory/repertory/src/ui/ui_main.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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<const char *> &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<std::int32_t>(exit_code::ui_failed);
|
||||
}
|
||||
|
||||
const auto run_ui = [&]() -> int {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
ui_server server(&config);
|
||||
|
||||
static std::atomic<ui_server *> 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
|
@@ -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<const char *>(&enable), sizeof(enable));
|
||||
#else // !defined(_WIN32)!
|
||||
int one = 1;
|
||||
int one{1};
|
||||
::setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
|
||||
reinterpret_cast<const char *>(&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<std::thread>([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();
|
||||
|
@@ -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<std::size_t> 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<std::string> 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 <typename provider_t> class drive_fixture : public ::testing::Test {
|
||||
public:
|
||||
#if !defined(_WIN32)
|
||||
static std::unique_ptr<app_config> config;
|
||||
static std::unique_ptr<app_config> config2;
|
||||
static std::unique_ptr<i_meta_db> meta;
|
||||
#endif // !defined(_WIN32)
|
||||
static std::filesystem::path current_directory;
|
||||
@@ -202,21 +200,24 @@ public:
|
||||
static std::vector<std::string> 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<app_config>(provider_type::remote, cfg_dir2);
|
||||
cfg2->set_enable_drive_events(true);
|
||||
cfg2->set_event_level(event_level::trace);
|
||||
config2 = std::make_unique<app_config>(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<size_t>(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<char, 4096U> 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::string> {
|
||||
std::set<std::string> 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 <typename provider_t>
|
||||
std::unique_ptr<app_config> drive_fixture<provider_t>::config;
|
||||
|
||||
template <typename provider_t>
|
||||
std::unique_ptr<app_config> drive_fixture<provider_t>::config2;
|
||||
|
||||
template <typename provider_t>
|
||||
std::unique_ptr<i_meta_db> drive_fixture<provider_t>::meta{};
|
||||
#endif // !defined(_WIN32)
|
||||
|
||||
template <typename provider_t>
|
||||
std::filesystem::path drive_fixture<provider_t>::current_directory;
|
||||
|
||||
template <typename provider_t>
|
||||
provider_type drive_fixture<provider_t>::current_provider{provider_t::type2};
|
||||
|
||||
template <typename provider_t>
|
||||
std::vector<std::string> drive_fixture<provider_t>::drive_args;
|
||||
|
||||
template <typename provider_t>
|
||||
std::vector<std::string> drive_fixture<provider_t>::drive_args2;
|
||||
|
||||
template <typename provider_t>
|
||||
std::string drive_fixture<provider_t>::mount_location;
|
||||
|
||||
template <typename provider_t>
|
||||
std::string drive_fixture<provider_t>::mount_location2;
|
||||
|
||||
template <typename provider_t>
|
||||
provider_type drive_fixture<provider_t>::mount_provider{provider_t::type};
|
||||
|
||||
using platform_provider_types =
|
||||
::testing::Types<local_s3_no_encryption, local_s3_encryption,
|
||||
local_s3_legacy_encryption, remote_s3_no_encryption,
|
||||
remote_s3_encryption, remote_s3_legacy_encryption,
|
||||
local_sia, remote_sia>;
|
||||
#if defined(_WIN32)
|
||||
using winfsp_test = drive_fixture;
|
||||
#else
|
||||
using fuse_test = drive_fixture;
|
||||
#endif
|
||||
template <typename provider_t> using winfsp_test = drive_fixture<provider_t>;
|
||||
#else // !defined(_WIN32)
|
||||
template <typename provider_t> using fuse_test = drive_fixture<provider_t>;
|
||||
#endif // defined(_WIN32)
|
||||
} // namespace repertory
|
||||
|
||||
#endif // REPERTORY_TEST_INCLUDE_FIXTURES_PLATFORM_FIXTURE_HPP
|
||||
#endif // REPERTORY_TEST_INCLUDE_FIXTURES_DRIVE_FIXTURE_HPP
|
||||
|
@@ -1,513 +0,0 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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<std::size_t> 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 <typename provider_t> class fuse_test : public ::testing::Test {
|
||||
public:
|
||||
static std::unique_ptr<app_config> config;
|
||||
static std::filesystem::path current_directory;
|
||||
static provider_type current_provider;
|
||||
static std::vector<std::string> drive_args;
|
||||
static std::vector<std::string> drive_args2;
|
||||
static std::unique_ptr<i_meta_db> 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<app_config>(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<std::string>({
|
||||
"-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<app_config>(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<std::string>({
|
||||
"-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<app_config>(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<std::string>({
|
||||
"-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 <typename provider_t>
|
||||
std::unique_ptr<app_config> fuse_test<provider_t>::config;
|
||||
|
||||
template <typename provider_t>
|
||||
std::filesystem::path fuse_test<provider_t>::current_directory;
|
||||
|
||||
template <typename provider_t>
|
||||
provider_type fuse_test<provider_t>::current_provider{provider_t::type2};
|
||||
|
||||
template <typename provider_t>
|
||||
std::vector<std::string> fuse_test<provider_t>::drive_args;
|
||||
|
||||
template <typename provider_t>
|
||||
std::vector<std::string> fuse_test<provider_t>::drive_args2;
|
||||
|
||||
template <typename provider_t>
|
||||
std::unique_ptr<i_meta_db> fuse_test<provider_t>::meta{};
|
||||
|
||||
template <typename provider_t>
|
||||
std::string fuse_test<provider_t>::mount_location;
|
||||
|
||||
template <typename provider_t>
|
||||
std::string fuse_test<provider_t>::mount_location2;
|
||||
|
||||
using fuse_provider_types =
|
||||
::testing::Types<local_s3_no_encryption, local_s3_encryption,
|
||||
local_s3_legacy_encryption, remote_s3_no_encryption,
|
||||
remote_s3_encryption, remote_s3_legacy_encryption,
|
||||
local_sia, remote_sia>;
|
||||
// using fuse_provider_types =
|
||||
// ::testing::Types<local_s3, remote_s3, local_sia, remote_sia,
|
||||
// remote_linux_to_winfsp>;
|
||||
} // namespace repertory
|
||||
|
||||
#endif // !defined(_WIN32)
|
||||
#endif // REPERTORY_TEST_INCLUDE_FIXTURES_FUSE_FIXTURE_HPP
|
338
repertory/repertory_test/include/fixtures/providers_fixture.hpp
Normal file
338
repertory/repertory_test/include/fixtures/providers_fixture.hpp
Normal file
@@ -0,0 +1,338 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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<i_http_comm> & /* comm */,
|
||||
std::unique_ptr<app_config> &config,
|
||||
std::unique_ptr<i_provider> &provider) {
|
||||
auto config_path =
|
||||
utils::path::combine(test::get_test_output_dir(), {
|
||||
"provider",
|
||||
"encrypt",
|
||||
});
|
||||
|
||||
config = std::make_unique<app_config>(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<encrypt_provider>(*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<i_http_comm> &comm,
|
||||
std::unique_ptr<app_config> &config,
|
||||
std::unique_ptr<i_provider> &provider) {
|
||||
auto config_path =
|
||||
utils::path::combine(test::get_test_output_dir(), {
|
||||
"provider",
|
||||
"s3",
|
||||
});
|
||||
|
||||
config = std::make_unique<app_config>(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<curl_comm>(config->get_s3_config());
|
||||
|
||||
provider = std::make_unique<s3_provider>(*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<i_http_comm> &comm,
|
||||
std::unique_ptr<app_config> &config,
|
||||
std::unique_ptr<i_provider> &provider) {
|
||||
auto config_path =
|
||||
utils::path::combine(test::get_test_output_dir(), {
|
||||
"provider",
|
||||
"s3",
|
||||
});
|
||||
|
||||
config = std::make_unique<app_config>(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<curl_comm>(config->get_s3_config());
|
||||
|
||||
provider = std::make_unique<s3_provider>(*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<i_http_comm> &comm,
|
||||
std::unique_ptr<app_config> &config,
|
||||
std::unique_ptr<i_provider> &provider) {
|
||||
auto config_path =
|
||||
utils::path::combine(test::get_test_output_dir(), {
|
||||
"provider",
|
||||
"sia",
|
||||
});
|
||||
|
||||
config = std::make_unique<app_config>(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<curl_comm>(config->get_host_config());
|
||||
|
||||
provider = std::make_unique<sia_provider>(*config, *comm);
|
||||
|
||||
EXPECT_EQ(type, provider->get_provider_type());
|
||||
EXPECT_FALSE(provider->is_read_only());
|
||||
EXPECT_TRUE(provider->is_rename_supported());
|
||||
}
|
||||
};
|
||||
|
||||
template <typename provider_t> class providers_test : public ::testing::Test {
|
||||
public:
|
||||
static std::unique_ptr<i_http_comm> comm;
|
||||
static std::unique_ptr<app_config> config;
|
||||
static console_consumer consumer;
|
||||
static std::unique_ptr<file_manager> mgr;
|
||||
static std::unique_ptr<i_provider> provider;
|
||||
|
||||
protected:
|
||||
static void SetUpTestCase() {
|
||||
event_system::instance().start();
|
||||
|
||||
provider_t::setup(comm, config, provider);
|
||||
mgr = std::make_unique<file_manager>(*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<std::string, 2>{".", ".."};
|
||||
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<gid_t>(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<uid_t>(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<gid_t>(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<uid_t>(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 <typename provider_t>
|
||||
std::unique_ptr<i_http_comm> providers_test<provider_t>::comm;
|
||||
|
||||
template <typename provider_t>
|
||||
std::unique_ptr<app_config> providers_test<provider_t>::config;
|
||||
|
||||
template <typename provider_t>
|
||||
console_consumer providers_test<provider_t>::consumer;
|
||||
|
||||
template <typename provider_t>
|
||||
std::unique_ptr<file_manager> providers_test<provider_t>::mgr;
|
||||
|
||||
template <typename provider_t>
|
||||
std::unique_ptr<i_provider> providers_test<provider_t>::provider;
|
||||
|
||||
using provider_types =
|
||||
::testing::Types<encrypt_provider_type, s3_provider_encrypted_type,
|
||||
s3_provider_unencrypted_type, sia_provider_type>;
|
||||
} // namespace repertory
|
||||
|
||||
#endif // REPERTORY_TEST_INCLUDE_FIXTURES_PROVIDERS_FIXTURE_HPP
|
@@ -1,361 +0,0 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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<std::size_t> 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 <typename provider_t> class winfsp_test : public ::testing::Test {
|
||||
public:
|
||||
static std::filesystem::path current_directory;
|
||||
static provider_type current_provider;
|
||||
static std::vector<std::string> drive_args;
|
||||
static std::vector<std::string> 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<app_config>(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<std::string>({
|
||||
"-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<app_config>(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<std::string>({
|
||||
"-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<app_config>(provider_type::remote, cfg_directory);
|
||||
config->set_enable_drive_events(true);
|
||||
config->set_event_level(event_level::trace);
|
||||
|
||||
drive_args2 = std::vector<std::string>({
|
||||
"-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 <typename provider_t>
|
||||
std::filesystem::path winfsp_test<provider_t>::current_directory;
|
||||
|
||||
template <typename provider_t>
|
||||
provider_type winfsp_test<provider_t>::current_provider{provider_t::type2};
|
||||
|
||||
template <typename provider_t>
|
||||
std::vector<std::string> winfsp_test<provider_t>::drive_args;
|
||||
|
||||
template <typename provider_t>
|
||||
std::vector<std::string> winfsp_test<provider_t>::drive_args2;
|
||||
|
||||
template <typename provider_t>
|
||||
std::string winfsp_test<provider_t>::mount_location;
|
||||
|
||||
template <typename provider_t>
|
||||
std::string winfsp_test<provider_t>::mount_location2;
|
||||
|
||||
using winfsp_provider_types =
|
||||
::testing::Types<local_s3_no_encryption, local_s3_encryption,
|
||||
local_s3_legacy_encryption, remote_s3_no_encryption,
|
||||
remote_s3_encryption, remote_s3_legacy_encryption,
|
||||
local_sia, remote_sia, remote_winfsp_to_linux>;
|
||||
} // namespace repertory
|
||||
|
||||
#endif // defined(_WIN32)
|
||||
#endif // REPERTORY_TEST_INCLUDE_FIXTURES_WINFSP_FIXTURE_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),
|
||||
|
@@ -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_; }
|
||||
|
@@ -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);
|
||||
|
@@ -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();
|
||||
|
@@ -92,6 +92,8 @@ std::atomic<std::size_t> 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<std::string>()));
|
||||
|
||||
event_consumer consumer(service_start_begin::name, [](const i_event &evt) {
|
||||
const auto &evt2 = dynamic_cast<const service_start_begin &>(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<std::string>()));
|
||||
|
||||
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<std::string>()));
|
||||
|
||||
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<std::string>()));
|
||||
|
||||
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<std::string>()));
|
||||
|
||||
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<std::string>()));
|
||||
|
||||
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<std::string>()));
|
||||
|
||||
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<std::string>()));
|
||||
|
||||
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<std::string>()));
|
||||
|
||||
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<std::string>()));
|
||||
|
||||
polling::instance().start(cfg.get());
|
||||
|
||||
|
@@ -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();
|
||||
|
@@ -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(
|
||||
|
@@ -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"};
|
||||
|
@@ -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) {
|
||||
|
@@ -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"};
|
||||
|
118
repertory/repertory_test/src/fuse_drive_directory_test.cpp
Normal file
118
repertory/repertory_test/src/fuse_drive_directory_test.cpp
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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)
|
261
repertory/repertory_test/src/fuse_drive_fallocate_test.cpp
Normal file
261
repertory/repertory_test/src/fuse_drive_fallocate_test.cpp
Normal file
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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)
|
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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)
|
64
repertory/repertory_test/src/fuse_drive_misc_test.cpp
Normal file
64
repertory/repertory_test/src/fuse_drive_misc_test.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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)
|
@@ -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"};
|
||||
|
391
repertory/repertory_test/src/fuse_drive_rename_test.cpp
Normal file
391
repertory/repertory_test/src/fuse_drive_rename_test.cpp
Normal file
@@ -0,0 +1,391 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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)
|
@@ -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
|
||||
|
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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)
|
116
repertory/repertory_test/src/fuse_drive_unlink_test.cpp
Normal file
116
repertory/repertory_test/src/fuse_drive_unlink_test.cpp
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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)
|
@@ -0,0 +1,333 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
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<long long>(st_unix.st_atimespec.tv_sec) *
|
||||
static_cast<long long>(NANOS_PER_SECOND) +
|
||||
static_cast<long long>(st_unix.st_atimespec.tv_nsec);
|
||||
mt_ns = static_cast<long long>(st_unix.st_mtimespec.tv_sec) *
|
||||
static_cast<long long>(NANOS_PER_SECOND) +
|
||||
static_cast<long long>(st_unix.st_mtimespec.tv_nsec);
|
||||
#else // !defined(__APPLE__)
|
||||
at_ns = static_cast<long long>(st_unix.st_atim.tv_sec) *
|
||||
static_cast<long long>(NANOS_PER_SECOND) +
|
||||
static_cast<long long>(st_unix.st_atim.tv_nsec);
|
||||
mt_ns = static_cast<long long>(st_unix.st_mtim.tv_sec) *
|
||||
static_cast<long long>(NANOS_PER_SECOND) +
|
||||
static_cast<long long>(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<long long>(spec.tv_sec) *
|
||||
static_cast<long long>(NANOS_PER_SECOND) +
|
||||
static_cast<long long>(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<long long>(NANOS_PER_SECOND);
|
||||
constexpr long long NOW_TOL_NS = 5LL * static_cast<long long>(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<long long>(NANOS_PER_SECOND) + 111111111LL;
|
||||
auto target_mt =
|
||||
now - 1800LL * static_cast<long long>(NANOS_PER_SECOND) + 222222222LL;
|
||||
|
||||
auto spec = std::array<timespec, 2U>{
|
||||
ts_make(static_cast<time_t>(target_at /
|
||||
static_cast<long long>(NANOS_PER_SECOND)),
|
||||
static_cast<long>(target_at %
|
||||
static_cast<long long>(NANOS_PER_SECOND))),
|
||||
ts_make(static_cast<time_t>(target_mt /
|
||||
static_cast<long long>(NANOS_PER_SECOND)),
|
||||
static_cast<long>(target_mt %
|
||||
static_cast<long long>(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<long long>(NANOS_PER_SECOND);
|
||||
|
||||
auto spec = std::array<timespec, 2U>{
|
||||
ts_make(static_cast<time_t>(target_at /
|
||||
static_cast<long long>(NANOS_PER_SECOND)),
|
||||
static_cast<long>(target_at %
|
||||
static_cast<long long>(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<long long>(NANOS_PER_SECOND);
|
||||
|
||||
auto spec = std::array<timespec, 2U>{
|
||||
ts_make(0, UTIME_OMIT),
|
||||
ts_make(static_cast<time_t>(target_mt /
|
||||
static_cast<long long>(NANOS_PER_SECOND)),
|
||||
static_cast<long>(target_mt %
|
||||
static_cast<long long>(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<timespec, 2U>{
|
||||
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<timespec, 2U>{
|
||||
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<timespec, 2U>{
|
||||
ts_make(0, static_cast<long long>(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<long long>(NANOS_PER_SECOND) + 333333333LL;
|
||||
auto target_mt =
|
||||
now - 600LL * static_cast<long long>(NANOS_PER_SECOND) + 444444444LL;
|
||||
|
||||
auto spec = std::array<timespec, 2U>{
|
||||
ts_make(static_cast<time_t>(target_at /
|
||||
static_cast<long long>(NANOS_PER_SECOND)),
|
||||
static_cast<long>(target_at %
|
||||
static_cast<long long>(NANOS_PER_SECOND))),
|
||||
ts_make(static_cast<time_t>(target_mt /
|
||||
static_cast<long long>(NANOS_PER_SECOND)),
|
||||
static_cast<long>(target_mt %
|
||||
static_cast<long long>(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<long long>(NANOS_PER_SECOND);
|
||||
|
||||
auto spec = std::array<timespec, 2U>{
|
||||
ts_make(0, UTIME_OMIT),
|
||||
ts_make(static_cast<time_t>(target_mt /
|
||||
static_cast<long long>(NANOS_PER_SECOND)),
|
||||
static_cast<long>(target_mt %
|
||||
static_cast<long long>(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<timespec, 2U>{
|
||||
ts_make(0, 0),
|
||||
ts_make(0, static_cast<long long>(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)
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user