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:
2025-09-25 17:06:22 -05:00
119 changed files with 5256 additions and 2933 deletions

View File

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

View File

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

View File

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

View 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_

View File

@@ -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());

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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*/)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -41,6 +41,9 @@ create_rocksdb(const app_config &cfg, const std::string &name,
[[nodiscard]] auto create_volume_label(provider_type prov) -> std::string;
[[nodiscard]] auto get_attributes_from_meta(const api_meta_map &meta) -> DWORD;
[[nodiscard]] auto get_version_number(std::string_view version)
-> std::uint32_t;
} // namespace utils
} // namespace repertory

View File

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

View File

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

View 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

View File

@@ -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");
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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();

View File

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

View File

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

View File

@@ -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());

View File

@@ -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();

View File

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

View File

@@ -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();

View File

@@ -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();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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");
}
}

View 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();

View File

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

View File

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

View File

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

View File

@@ -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"},

View File

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

View File

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

View File

@@ -19,8 +19,6 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "rocksdb/table.h"
#include "utils/utils.hpp"
#include "app_config.hpp"
@@ -90,4 +88,13 @@ auto create_volume_label(provider_type prov) -> std::string {
auto get_attributes_from_meta(const api_meta_map &meta) -> DWORD {
return static_cast<DWORD>(utils::string::to_uint32(meta.at(META_ATTRIBUTES)));
}
auto get_version_number(std::string_view version) -> std::uint32_t {
auto parts = utils::string::split(version, '-', false);
parts = utils::string::split(parts.at(0U), '.', false);
return (utils::string::to_uint32(parts.at(0U)) << 24U) |
(utils::string::to_uint32(parts.at(1U)) << 16U) |
(utils::string::to_uint32(parts.at(2U)) << 8U);
}
} // namespace repertory::utils

View File

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

View File

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

View File

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

View 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_

View File

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

View File

@@ -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();

View File

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

View 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

View File

@@ -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();

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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();

View File

@@ -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());

View File

@@ -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();

View File

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

View File

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

View File

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

View File

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

View 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)

View 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)

View File

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

View 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)

View File

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

View 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)

View File

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

View File

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

View 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)

View File

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