Compare commits
54 Commits
v2.0.5-rc
...
8b4724a9c1
Author | SHA1 | Date | |
---|---|---|---|
8b4724a9c1 | |||
6a0d50bc66 | |||
69910bef4c | |||
8c298c84c5 | |||
47dea2cc38 | |||
d8b476e80a | |||
c86c6e2ec6 | |||
c397497eb7 | |||
f8803dfbf0 | |||
5d5cacc482 | |||
3ce4210d56 | |||
d109344544 | |||
ff746a7bec | |||
a613ec77ff | |||
00d3dd95a8 | |||
28d1789f04 | |||
0603463885 | |||
88398485e1 | |||
908e75c696 | |||
dab8c61f87 | |||
e0cf58b01e | |||
281d3758e0 | |||
dfa170022a | |||
52c2780283 | |||
c2dbfc970a | |||
6f9b1f8f08 | |||
078d603be9 | |||
983e47103b | |||
8d2024d34b | |||
4f2ee2ad99 | |||
533938bcef | |||
98edf33be4 | |||
a080c9ff86 | |||
e6cdcd74a1 | |||
573ae549be | |||
fca149f998 | |||
76e375c488 | |||
3f9322f659 | |||
c286d496c3 | |||
56ba0fcb83 | |||
dcafb104ea | |||
ab757dfd36 | |||
eec2d2e9a9 | |||
8c1c91e02b | |||
bb85015733 | |||
883de836c6 | |||
bf2bdd1b5d | |||
f1e82d8f9f | |||
f5c4aebdac | |||
f2f9e8fd15 | |||
2a673915af | |||
df3db38ae7 | |||
b0b69c6dd4 | |||
11b118a30f |
19
CHANGELOG.md
19
CHANGELOG.md
@@ -1,7 +1,25 @@
|
||||
# Changelog
|
||||
|
||||
## v2.0.6-release
|
||||
|
||||
### Issues
|
||||
|
||||
* ~~\#12 [Unit Test] Complete all providers unit tests~~
|
||||
* ~~\#21 [Unit Test] Complete WinFSP unit tests~~
|
||||
* ~~\#22 [Unit Test] Complete FUSE unit tests~~
|
||||
* ~~\#33 Complete initial v2.0 documentation~~
|
||||
* \#42 [bug] Remote mount directory listing on Windows connected to Linux is failing
|
||||
* \#43 [bug] Directories are not importing properly for Sia
|
||||
* \#44 [bug] Windows-to-Linux remote mount ignores `CREATE_NEW`
|
||||
* \#45 [bug] Windows-to-Linux remote mount is not handling attempts to remove a non-empty directory properly
|
||||
|
||||
### Changes from v2.0.5-rc
|
||||
|
||||
* Drive letters in UI should always be lowercase
|
||||
|
||||
## v2.0.5-rc
|
||||
|
||||
<!-- markdownlint-disable-next-line -->
|
||||
### Issues
|
||||
|
||||
* \#39 Create management portal in Flutter
|
||||
@@ -69,6 +87,7 @@
|
||||
|
||||
## v2.0.2-rc
|
||||
|
||||
<!-- markdownlint-disable-next-line -->
|
||||
### BREAKING CHANGES
|
||||
|
||||
* Refactored `config.json` - will need to verify configuration settings prior to mounting
|
||||
|
@@ -10,7 +10,7 @@ PROJECT_DESC="Mount utility for Sia and S3"
|
||||
|
||||
PROJECT_MAJOR_VERSION=2
|
||||
PROJECT_MINOR_VERSION=0
|
||||
PROJECT_REVISION_VERSION=5
|
||||
PROJECT_REVISION_VERSION=6
|
||||
PROJECT_RELEASE_NUM=0
|
||||
PROJECT_RELEASE_ITER=rc
|
||||
|
||||
|
@@ -225,8 +225,7 @@ public:
|
||||
}
|
||||
|
||||
if (curl_code != CURLE_OK) {
|
||||
event_system::instance().raise<curl_error>(curl_code, function_name,
|
||||
url);
|
||||
event_system::instance().raise<curl_error>(curl_code, function_name, url);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@@ -54,12 +54,13 @@ REPERTORY_IGNORE_WARNINGS_DISABLE()
|
||||
using namespace std::chrono_literals;
|
||||
using json = nlohmann::json;
|
||||
|
||||
inline constexpr const std::string_view REPERTORY = "repertory";
|
||||
inline constexpr const std::wstring_view REPERTORY_W = L"repertory";
|
||||
inline constexpr const std::string_view REPERTORY{"repertory"};
|
||||
inline constexpr const std::string_view REPERTORY_DATA_NAME{"repertory2"};
|
||||
inline constexpr const std::wstring_view REPERTORY_W{L"repertory"};
|
||||
|
||||
inline constexpr const std::uint64_t REPERTORY_CONFIG_VERSION = 2ULL;
|
||||
inline constexpr const std::string_view REPERTORY_DATA_NAME = "repertory2";
|
||||
inline constexpr const std::string_view REPERTORY_MIN_REMOTE_VERSION = "2.0.0";
|
||||
inline constexpr const std::uint64_t REPERTORY_CONFIG_VERSION{2ULL};
|
||||
inline constexpr const std::string_view REPERTORY_MIN_REMOTE_VERSION{"2.0.0"};
|
||||
inline constexpr const std::string_view RENTERD_MIN_VERSION{"2.0.0"};
|
||||
|
||||
#define REPERTORY_INVALID_HANDLE INVALID_HANDLE_VALUE
|
||||
|
||||
@@ -221,7 +222,6 @@ using WCHAR = wchar_t;
|
||||
|
||||
#define MAX_PATH 260
|
||||
|
||||
#define STATUS_SUCCESS std::uint32_t{0U}
|
||||
#define STATUS_ACCESS_DENIED std::uint32_t{0xC0000022L}
|
||||
#define STATUS_DEVICE_BUSY std::uint32_t{0x80000011L}
|
||||
#define STATUS_DEVICE_INSUFFICIENT_RESOURCES std::uint32_t{0xC0000468L}
|
||||
@@ -234,11 +234,13 @@ using WCHAR = wchar_t;
|
||||
#define STATUS_INVALID_HANDLE std::uint32_t{0xC0000006L}
|
||||
#define STATUS_INVALID_IMAGE_FORMAT std::uint32_t{0xC000007BL}
|
||||
#define STATUS_INVALID_PARAMETER std::uint32_t{0xC000000DL}
|
||||
#define STATUS_NO_MEMORY std::uint32_t{0xC0000017L}
|
||||
#define STATUS_NOT_IMPLEMENTED std::uint32_t{0xC0000002L}
|
||||
#define STATUS_NO_MEMORY std::uint32_t{0xC0000017L}
|
||||
#define STATUS_OBJECT_NAME_COLLISION std::uint32_t{0xC0000035L}
|
||||
#define STATUS_OBJECT_NAME_EXISTS std::uint32_t{0x40000000L}
|
||||
#define STATUS_OBJECT_NAME_NOT_FOUND std::uint32_t{0xC0000034L}
|
||||
#define STATUS_OBJECT_PATH_INVALID std::uint32_t{0xC0000039L}
|
||||
#define STATUS_SUCCESS std::uint32_t{0U}
|
||||
#define STATUS_UNEXPECTED_IO_ERROR std::uint32_t{0xC00000E9L}
|
||||
|
||||
#define CONVERT_STATUS_NOT_IMPLEMENTED(e) \
|
||||
|
@@ -59,7 +59,8 @@ private:
|
||||
|
||||
static void populate_stat(const struct stat64 &unix_st, remote::stat &r_stat);
|
||||
|
||||
[[nodiscard]] auto update_to_windows_format(json &item) -> json &;
|
||||
[[nodiscard]] auto update_to_windows_format(const std::string &root_api_path,
|
||||
json &item) -> json &;
|
||||
|
||||
public:
|
||||
// FUSE Layer
|
||||
|
@@ -143,14 +143,17 @@ public:
|
||||
allocation_size, &file_desc, &file_info,
|
||||
normalized_name, exists);
|
||||
if (ret == STATUS_SUCCESS) {
|
||||
if (exists == 0U) {
|
||||
#if defined(_WIN32)
|
||||
this->set_client_id(file_desc, client_id);
|
||||
this->set_client_id(file_desc, client_id);
|
||||
#else // !defined(_WIN32)
|
||||
this->set_client_id(
|
||||
static_cast<native_handle>(
|
||||
reinterpret_cast<std::uintptr_t>(file_desc)),
|
||||
client_id);
|
||||
this->set_client_id(
|
||||
static_cast<native_handle>(
|
||||
reinterpret_cast<std::uintptr_t>(file_desc)),
|
||||
client_id);
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
response.encode(file_desc);
|
||||
response.encode(file_info);
|
||||
response.encode(normalized_name);
|
||||
@@ -589,8 +592,9 @@ public:
|
||||
DECODE_OR_RETURN(request, flags);
|
||||
|
||||
remote::file_handle handle{};
|
||||
if ((ret = this->fuse_create(path.data(), mode, flags, handle)) >=
|
||||
0) {
|
||||
|
||||
ret = this->fuse_create(path.data(), mode, flags, handle);
|
||||
if (ret >= 0) {
|
||||
#if defined(_WIN32)
|
||||
this->set_compat_client_id(handle, client_id);
|
||||
#else // !defined(_WIN32)
|
||||
@@ -846,7 +850,8 @@ public:
|
||||
DECODE_OR_RETURN(request, flags);
|
||||
|
||||
remote::file_handle handle;
|
||||
if ((ret = this->fuse_open(path.c_str(), flags, handle)) >= 0) {
|
||||
ret = this->fuse_open(path.c_str(), flags, handle);
|
||||
if (ret >= 0) {
|
||||
#if defined(_WIN32)
|
||||
this->set_compat_client_id(handle, client_id);
|
||||
#else // !defined(_WIN32)
|
||||
@@ -867,7 +872,8 @@ public:
|
||||
DECODE_OR_RETURN(request, path);
|
||||
|
||||
remote::file_handle handle{0};
|
||||
if ((ret = this->fuse_opendir(path.c_str(), handle)) >= 0) {
|
||||
ret = this->fuse_opendir(path.c_str(), handle);
|
||||
if (ret >= 0) {
|
||||
this->add_directory(client_id, handle);
|
||||
response.encode(handle);
|
||||
}
|
||||
|
@@ -49,16 +49,28 @@ private:
|
||||
sia_config sia_config_;
|
||||
|
||||
private:
|
||||
[[nodiscard]] auto create_directory_key(const std::string &api_path) const
|
||||
-> repertory::api_error;
|
||||
|
||||
[[nodiscard]] auto ensure_directory_exists(const std::string &api_path) const
|
||||
-> api_error;
|
||||
|
||||
[[nodiscard]] auto get_object_info(const std::string &api_path,
|
||||
json &object_info) const -> api_error;
|
||||
|
||||
[[nodiscard]] auto get_object_list(const std::string &api_path,
|
||||
nlohmann::json &object_list) const -> bool;
|
||||
[[nodiscard]] auto
|
||||
get_object_list(const std::string &api_path, nlohmann::json &object_list,
|
||||
std::optional<std::string> marker = std::nullopt) const
|
||||
-> bool;
|
||||
|
||||
[[nodiscard]] auto get_sia_config() const -> const auto & {
|
||||
return sia_config_;
|
||||
}
|
||||
|
||||
void iterate_objects(
|
||||
const std::string &api_path, const json &object_list,
|
||||
std::function<void(const std::string &, bool, json)> handle_entry) const;
|
||||
|
||||
protected:
|
||||
[[nodiscard]] auto create_directory_impl(const std::string &api_path,
|
||||
api_meta_map &meta)
|
||||
|
@@ -374,13 +374,13 @@ auto fuse_base::init_impl(struct fuse_conn_info *conn) -> void * {
|
||||
if (not utils::file::change_to_process_directory()) {
|
||||
utils::error::raise_error(function_name,
|
||||
"failed to change to process directory");
|
||||
event_system::instance().raise<unmount_requested>();
|
||||
event_system::instance().raise<unmount_requested>(function_name);
|
||||
return this;
|
||||
}
|
||||
|
||||
if (not console_enabled_ && not repertory::project_initialize()) {
|
||||
utils::error::raise_error(function_name, "failed to initialize repertory");
|
||||
event_system::instance().raise<unmount_requested>();
|
||||
event_system::instance().raise<unmount_requested>(function_name);
|
||||
}
|
||||
|
||||
return this;
|
||||
|
@@ -85,8 +85,8 @@ auto fuse_drive::chown_impl(std::string api_path, uid_t uid, gid_t gid,
|
||||
struct fuse_file_info * /*file_info*/)
|
||||
-> api_error {
|
||||
#else
|
||||
auto fuse_drive::chown_impl(std::string api_path, uid_t uid,
|
||||
gid_t gid) -> api_error {
|
||||
auto fuse_drive::chown_impl(std::string api_path, uid_t uid, gid_t gid)
|
||||
-> api_error {
|
||||
#endif
|
||||
return check_and_perform(
|
||||
api_path, X_OK, [&](api_meta_map &meta) -> api_error {
|
||||
@@ -464,7 +464,7 @@ auto fuse_drive::get_file_size(const std::string &api_path) const
|
||||
|
||||
std::uint64_t file_size{};
|
||||
auto res = provider_.get_file_size(api_path, file_size);
|
||||
if (res == api_error::success) {
|
||||
if (res != api_error::success) {
|
||||
utils::error::raise_api_path_error(function_name, api_path, res,
|
||||
"failed to get file size from provider");
|
||||
}
|
||||
@@ -494,8 +494,8 @@ auto fuse_drive::getattr_impl(std::string api_path, struct stat *unix_st,
|
||||
struct fuse_file_info * /*file_info*/)
|
||||
-> api_error {
|
||||
#else
|
||||
auto fuse_drive::getattr_impl(std::string api_path,
|
||||
struct stat *unix_st) -> api_error {
|
||||
auto fuse_drive::getattr_impl(std::string api_path, struct stat *unix_st)
|
||||
-> api_error {
|
||||
#endif
|
||||
auto parent = utils::path::get_parent_api_path(api_path);
|
||||
|
||||
@@ -561,8 +561,8 @@ auto fuse_drive::getxtimes_impl(std::string api_path, struct timespec *bkuptime,
|
||||
#endif // __APPLE__
|
||||
|
||||
#if FUSE_USE_VERSION >= 30
|
||||
auto fuse_drive::init_impl(struct fuse_conn_info *conn,
|
||||
struct fuse_config *cfg) -> void * {
|
||||
auto fuse_drive::init_impl(struct fuse_conn_info *conn, struct fuse_config *cfg)
|
||||
-> void * {
|
||||
#else
|
||||
void *fuse_drive::init_impl(struct fuse_conn_info *conn) {
|
||||
#endif
|
||||
@@ -804,8 +804,9 @@ auto fuse_drive::release_impl(std::string /*api_path*/,
|
||||
return api_error::success;
|
||||
}
|
||||
|
||||
auto fuse_drive::releasedir_impl(
|
||||
std::string /*api_path*/, struct fuse_file_info *file_info) -> api_error {
|
||||
auto fuse_drive::releasedir_impl(std::string /*api_path*/,
|
||||
struct fuse_file_info *file_info)
|
||||
-> api_error {
|
||||
auto iter = directory_cache_->get_directory(file_info->fh);
|
||||
if (iter == nullptr) {
|
||||
return api_error::invalid_handle;
|
||||
@@ -823,8 +824,8 @@ auto fuse_drive::rename_directory(const std::string &from_api_path,
|
||||
}
|
||||
|
||||
auto fuse_drive::rename_file(const std::string &from_api_path,
|
||||
const std::string &to_api_path,
|
||||
bool overwrite) -> int {
|
||||
const std::string &to_api_path, bool overwrite)
|
||||
-> int {
|
||||
auto res = fm_->rename_file(from_api_path, to_api_path, overwrite);
|
||||
errno = std::abs(utils::from_api_error(res));
|
||||
return (res == api_error::success) ? 0 : -1;
|
||||
@@ -834,8 +835,8 @@ auto fuse_drive::rename_file(const std::string &from_api_path,
|
||||
auto fuse_drive::rename_impl(std::string from_api_path, std::string to_api_path,
|
||||
unsigned int /*flags*/) -> api_error {
|
||||
#else
|
||||
auto fuse_drive::rename_impl(std::string from_api_path,
|
||||
std::string to_api_path) -> api_error {
|
||||
auto fuse_drive::rename_impl(std::string from_api_path, std::string to_api_path)
|
||||
-> api_error {
|
||||
#endif
|
||||
auto res = check_parent_access(to_api_path, W_OK | X_OK);
|
||||
if (res != api_error::success) {
|
||||
@@ -937,15 +938,15 @@ auto fuse_drive::getxattr_impl(std::string api_path, const char *name,
|
||||
}
|
||||
#else // __APPLE__
|
||||
auto fuse_drive::getxattr_impl(std::string api_path, const char *name,
|
||||
char *value, size_t size,
|
||||
int &attribute_size) -> api_error {
|
||||
char *value, size_t size, int &attribute_size)
|
||||
-> api_error {
|
||||
return getxattr_common(api_path, name, value, size, attribute_size, nullptr);
|
||||
}
|
||||
#endif // __APPLE__
|
||||
|
||||
auto fuse_drive::listxattr_impl(std::string api_path, char *buffer, size_t size,
|
||||
int &required_size,
|
||||
bool &return_size) -> api_error {
|
||||
int &required_size, bool &return_size)
|
||||
-> api_error {
|
||||
auto check_size = (size == 0);
|
||||
|
||||
auto res = check_parent_access(api_path, X_OK);
|
||||
@@ -985,8 +986,8 @@ auto fuse_drive::listxattr_impl(std::string api_path, char *buffer, size_t size,
|
||||
return res;
|
||||
}
|
||||
|
||||
auto fuse_drive::removexattr_impl(std::string api_path,
|
||||
const char *name) -> api_error {
|
||||
auto fuse_drive::removexattr_impl(std::string api_path, const char *name)
|
||||
-> api_error {
|
||||
std::string attribute_name;
|
||||
#if defined(__APPLE__)
|
||||
auto res = parse_xattr_parameters(name, 0, attribute_name, api_path);
|
||||
@@ -1014,8 +1015,8 @@ auto fuse_drive::setxattr_impl(std::string api_path, const char *name,
|
||||
uint32_t position) -> api_error {
|
||||
#else // __APPLE__
|
||||
auto fuse_drive::setxattr_impl(std::string api_path, const char *name,
|
||||
const char *value, size_t size,
|
||||
int flags) -> api_error {
|
||||
const char *value, size_t size, int flags)
|
||||
-> api_error {
|
||||
#endif
|
||||
std::string attribute_name;
|
||||
#if defined(__APPLE__)
|
||||
@@ -1093,8 +1094,8 @@ void fuse_drive::set_item_meta(const std::string &api_path,
|
||||
}
|
||||
|
||||
#if defined(__APPLE__)
|
||||
auto fuse_drive::setattr_x_impl(std::string api_path,
|
||||
struct setattr_x *attr) -> api_error {
|
||||
auto fuse_drive::setattr_x_impl(std::string api_path, struct setattr_x *attr)
|
||||
-> api_error {
|
||||
bool exists{};
|
||||
auto res = provider_.is_file(api_path, exists);
|
||||
if (res != api_error::success) {
|
||||
@@ -1148,7 +1149,7 @@ auto fuse_drive::setattr_x_impl(std::string api_path,
|
||||
ts[0].tv_sec = attr->acctime.tv_sec;
|
||||
ts[0].tv_nsec = attr->acctime.tv_nsec;
|
||||
} else {
|
||||
struct timeval tv {};
|
||||
struct timeval tv{};
|
||||
gettimeofday(&tv, NULL);
|
||||
ts[0].tv_sec = tv.tv_sec;
|
||||
ts[0].tv_nsec = tv.tv_usec * 1000;
|
||||
@@ -1193,8 +1194,9 @@ auto fuse_drive::setattr_x_impl(std::string api_path,
|
||||
return api_error::success;
|
||||
}
|
||||
|
||||
auto fuse_drive::setbkuptime_impl(
|
||||
std::string api_path, const struct timespec *bkuptime) -> api_error {
|
||||
auto fuse_drive::setbkuptime_impl(std::string api_path,
|
||||
const struct timespec *bkuptime)
|
||||
-> api_error {
|
||||
return check_and_perform(
|
||||
api_path, X_OK, [&](api_meta_map &meta) -> api_error {
|
||||
auto nanos = bkuptime->tv_nsec +
|
||||
@@ -1230,8 +1232,8 @@ auto fuse_drive::setvolname_impl(const char * /*volname*/) -> api_error {
|
||||
return api_error::success;
|
||||
}
|
||||
|
||||
auto fuse_drive::statfs_x_impl(std::string /*api_path*/,
|
||||
struct statfs *stbuf) -> api_error {
|
||||
auto fuse_drive::statfs_x_impl(std::string /*api_path*/, struct statfs *stbuf)
|
||||
-> api_error {
|
||||
if (statfs(&config_.get_cache_directory()[0], stbuf) != 0) {
|
||||
return api_error::os_error;
|
||||
}
|
||||
@@ -1256,8 +1258,8 @@ auto fuse_drive::statfs_x_impl(std::string /*api_path*/,
|
||||
return api_error::success;
|
||||
}
|
||||
#else // __APPLE__
|
||||
auto fuse_drive::statfs_impl(std::string /*api_path*/,
|
||||
struct statvfs *stbuf) -> api_error {
|
||||
auto fuse_drive::statfs_impl(std::string /*api_path*/, struct statvfs *stbuf)
|
||||
-> api_error {
|
||||
if (statvfs(config_.get_cache_directory().data(), stbuf) != 0) {
|
||||
return api_error::os_error;
|
||||
}
|
||||
@@ -1341,8 +1343,8 @@ auto fuse_drive::utimens_impl(std::string api_path, const struct timespec tv[2],
|
||||
struct fuse_file_info * /*file_info*/)
|
||||
-> api_error {
|
||||
#else
|
||||
auto fuse_drive::utimens_impl(std::string api_path,
|
||||
const struct timespec tv[2]) -> api_error {
|
||||
auto fuse_drive::utimens_impl(std::string api_path, const struct timespec tv[2])
|
||||
-> api_error {
|
||||
#endif
|
||||
api_meta_map meta;
|
||||
auto res = provider_.get_item_meta(api_path, meta);
|
||||
|
@@ -259,7 +259,7 @@ auto remote_fuse_drive::init_impl(struct fuse_conn_info *conn) -> void * {
|
||||
if (remote_instance_->fuse_init() != 0) {
|
||||
utils::error::raise_error(function_name,
|
||||
"failed to connect to remote server");
|
||||
event_system::instance().raise<unmount_requested>();
|
||||
event_system::instance().raise<unmount_requested>(function_name);
|
||||
} else {
|
||||
server_ = std::make_shared<server>(config_);
|
||||
server_->start();
|
||||
|
@@ -83,17 +83,17 @@ auto remote_server::populate_file_info(const std::string &api_path,
|
||||
remote::file_info &file_info)
|
||||
-> packet::error_type {
|
||||
std::string meta_attributes;
|
||||
auto directory = utils::file::directory(construct_path(api_path)).exists();
|
||||
|
||||
auto error = drive_.get_item_meta(api_path, META_ATTRIBUTES, meta_attributes);
|
||||
if (error == api_error::success) {
|
||||
if (meta_attributes.empty()) {
|
||||
meta_attributes =
|
||||
utils::file::directory(construct_path(api_path)).exists()
|
||||
? std::to_string(FILE_ATTRIBUTE_DIRECTORY)
|
||||
: std::to_string(FILE_ATTRIBUTE_ARCHIVE);
|
||||
meta_attributes = directory ? std::to_string(FILE_ATTRIBUTE_DIRECTORY)
|
||||
: std::to_string(FILE_ATTRIBUTE_ARCHIVE);
|
||||
drive_.set_item_meta(api_path, META_ATTRIBUTES, meta_attributes);
|
||||
}
|
||||
const auto attributes = utils::string::to_uint32(meta_attributes);
|
||||
const auto file_size = drive_.get_file_size(api_path);
|
||||
auto attributes = utils::string::to_uint32(meta_attributes);
|
||||
auto file_size = directory ? 0U : drive_.get_file_size(api_path);
|
||||
populate_file_info(api_path, file_size, attributes, file_info);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
@@ -1016,8 +1016,8 @@ auto remote_server::winfsp_can_delete(PVOID file_desc, PWSTR file_name)
|
||||
-> packet::error_type {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
const auto relative_path = utils::string::to_utf8(file_name);
|
||||
const auto file_path = construct_path(relative_path);
|
||||
auto relative_path = utils::string::to_utf8(file_name);
|
||||
auto file_path = construct_path(relative_path);
|
||||
auto ret = static_cast<packet::error_type>(
|
||||
has_open_info(static_cast<native_handle>(
|
||||
reinterpret_cast<remote::file_handle>(file_desc)),
|
||||
@@ -1026,13 +1026,12 @@ auto remote_server::winfsp_can_delete(PVOID file_desc, PWSTR file_name)
|
||||
ret = static_cast<packet::error_type>(
|
||||
utils::file::directory(file_path).exists()
|
||||
? drive_.get_directory_item_count(
|
||||
utils::path::create_api_path(relative_path))
|
||||
? STATUS_DIRECTORY_NOT_EMPTY
|
||||
: STATUS_SUCCESS
|
||||
utils::path::create_api_path(relative_path)) == 0U
|
||||
? STATUS_SUCCESS
|
||||
: STATUS_DIRECTORY_NOT_EMPTY
|
||||
|
||||
: STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
RAISE_REMOTE_FUSE_SERVER_EVENT(function_name, file_path, ret);
|
||||
return ret;
|
||||
}
|
||||
@@ -1042,17 +1041,20 @@ auto remote_server::winfsp_cleanup(PVOID /*file_desc*/, PWSTR file_name,
|
||||
-> packet::error_type {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
const auto relative_path = utils::string::to_utf8(file_name);
|
||||
const auto file_path = construct_path(relative_path);
|
||||
auto relative_path = utils::string::to_utf8(file_name);
|
||||
auto file_path = construct_path(relative_path);
|
||||
was_deleted = 0U;
|
||||
|
||||
const auto directory = utils::file::directory(file_path).exists();
|
||||
auto directory = utils::file::directory(file_path).exists();
|
||||
if (flags & FileSystemBase::FspCleanupDelete) {
|
||||
remove_all(file_path);
|
||||
was_deleted = 1U;
|
||||
|
||||
if (directory) {
|
||||
rmdir(file_path.c_str());
|
||||
if (drive_.get_directory_item_count(
|
||||
utils::path::create_api_path(relative_path)) == 0U) {
|
||||
rmdir(file_path.c_str());
|
||||
}
|
||||
} else {
|
||||
unlink(file_path.c_str());
|
||||
}
|
||||
@@ -1120,49 +1122,55 @@ auto remote_server::winfsp_create(PWSTR file_name, UINT32 create_options,
|
||||
-> packet::error_type {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
const auto relative_path = utils::string::to_utf8(file_name);
|
||||
const auto file_path = construct_path(relative_path);
|
||||
exists = utils::file::file(file_path).exists();
|
||||
|
||||
if ((create_options & FILE_DIRECTORY_FILE) != 0U) {
|
||||
attributes |= FILE_ATTRIBUTE_DIRECTORY;
|
||||
} else {
|
||||
attributes &= static_cast<UINT32>(~FILE_ATTRIBUTE_DIRECTORY);
|
||||
attributes |= FILE_ATTRIBUTE_ARCHIVE;
|
||||
}
|
||||
|
||||
remote::file_mode mode{0U};
|
||||
std::uint32_t flags{0U};
|
||||
utils::windows_create_to_unix(create_options, granted_access, flags, mode);
|
||||
|
||||
int res = 0;
|
||||
if ((create_options & FILE_DIRECTORY_FILE) != 0U) {
|
||||
res = mkdir(file_path.c_str(), mode);
|
||||
if (res >= 0) {
|
||||
res = open(file_path.c_str(), static_cast<int>(flags));
|
||||
auto relative_path = utils::string::to_utf8(file_name);
|
||||
auto file_path = construct_path(relative_path);
|
||||
exists = utils::file::file{file_path}.exists() ||
|
||||
utils::file::directory{file_path}.exists()
|
||||
? 1
|
||||
: 0;
|
||||
auto ret{static_cast<packet::error_type>(STATUS_SUCCESS)};
|
||||
if (exists == 0U) {
|
||||
if ((create_options & FILE_DIRECTORY_FILE) != 0U) {
|
||||
attributes |= FILE_ATTRIBUTE_DIRECTORY;
|
||||
} else {
|
||||
attributes &= static_cast<UINT32>(~FILE_ATTRIBUTE_DIRECTORY);
|
||||
attributes |= FILE_ATTRIBUTE_ARCHIVE;
|
||||
}
|
||||
} else {
|
||||
res = open(file_path.c_str(), static_cast<int>(flags), mode);
|
||||
|
||||
remote::file_mode mode{0U};
|
||||
std::uint32_t flags{0U};
|
||||
utils::windows_create_to_unix(create_options, granted_access, flags, mode);
|
||||
|
||||
auto res{0};
|
||||
if ((create_options & FILE_DIRECTORY_FILE) != 0U) {
|
||||
res = mkdir(file_path.c_str(), mode);
|
||||
if (res >= 0) {
|
||||
res = open(file_path.c_str(), static_cast<int>(flags));
|
||||
}
|
||||
} else {
|
||||
res = open(file_path.c_str(), static_cast<int>(flags), mode);
|
||||
}
|
||||
|
||||
if (res >= 0) {
|
||||
*file_desc = reinterpret_cast<PVOID>(res);
|
||||
drive_.set_item_meta(construct_api_path(file_path), META_ATTRIBUTES,
|
||||
std::to_string(attributes));
|
||||
set_open_info(res, open_info{
|
||||
"",
|
||||
nullptr,
|
||||
{},
|
||||
file_path,
|
||||
});
|
||||
|
||||
const auto api_path = utils::path::create_api_path(relative_path);
|
||||
normalized_name = utils::string::replace_copy(api_path, '/', '\\');
|
||||
populate_file_info(api_path, 0, attributes, *file_info);
|
||||
}
|
||||
|
||||
ret = static_cast<packet::error_type>(
|
||||
utils::unix_error_to_windows((res < 0) ? errno : 0));
|
||||
}
|
||||
|
||||
if (res >= 0) {
|
||||
*file_desc = reinterpret_cast<PVOID>(res);
|
||||
drive_.set_item_meta(construct_api_path(file_path), META_ATTRIBUTES,
|
||||
std::to_string(attributes));
|
||||
set_open_info(res, open_info{
|
||||
"",
|
||||
nullptr,
|
||||
{},
|
||||
file_path,
|
||||
});
|
||||
|
||||
const auto api_path = utils::path::create_api_path(relative_path);
|
||||
normalized_name = utils::string::replace_copy(api_path, '/', '\\');
|
||||
populate_file_info(api_path, 0, attributes, *file_info);
|
||||
}
|
||||
|
||||
auto ret = static_cast<packet::error_type>(
|
||||
utils::unix_error_to_windows((res < 0) ? errno : 0));
|
||||
RAISE_REMOTE_FUSE_SERVER_EVENT(function_name, file_path, ret);
|
||||
return ret;
|
||||
}
|
||||
@@ -1261,9 +1269,9 @@ auto remote_server::winfsp_open(PWSTR file_name, UINT32 create_options,
|
||||
-> packet::error_type {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
const auto relative_path = utils::string::to_utf8(file_name);
|
||||
const auto file_path = construct_path(relative_path);
|
||||
const auto directory = utils::file::directory(file_path).exists();
|
||||
auto relative_path = utils::string::to_utf8(file_name);
|
||||
auto file_path = construct_path(relative_path);
|
||||
auto directory = utils::file::directory(file_path).exists();
|
||||
if (directory) {
|
||||
create_options |= FILE_DIRECTORY_FILE;
|
||||
}
|
||||
@@ -1283,7 +1291,7 @@ auto remote_server::winfsp_open(PWSTR file_name, UINT32 create_options,
|
||||
file_path,
|
||||
});
|
||||
|
||||
const auto api_path = utils::path::create_api_path(relative_path);
|
||||
auto api_path = utils::path::create_api_path(relative_path);
|
||||
normalized_name = utils::string::replace_copy(api_path, '/', '\\');
|
||||
res = populate_file_info(api_path, *file_info);
|
||||
if (res != STATUS_SUCCESS) {
|
||||
@@ -1390,11 +1398,11 @@ auto remote_server::winfsp_read_directory(PVOID file_desc, PWSTR /*pattern*/,
|
||||
|
||||
item_list.clear();
|
||||
|
||||
const auto handle = reinterpret_cast<remote::file_handle>(file_desc);
|
||||
auto handle = reinterpret_cast<remote::file_handle>(file_desc);
|
||||
auto ret = static_cast<packet::error_type>(
|
||||
has_open_info(static_cast<native_handle>(handle), STATUS_INVALID_HANDLE));
|
||||
if (ret == STATUS_SUCCESS) {
|
||||
const auto api_path = construct_api_path(
|
||||
auto api_path = construct_api_path(
|
||||
get_open_file_path(static_cast<native_handle>(handle)));
|
||||
directory_iterator iterator(drive_.get_directory_items(api_path));
|
||||
auto offset = marker == nullptr
|
||||
@@ -1405,7 +1413,7 @@ auto remote_server::winfsp_read_directory(PVOID file_desc, PWSTR /*pattern*/,
|
||||
json item;
|
||||
while (iterator.get_json(offset++, item) == 0) {
|
||||
try {
|
||||
item_list.emplace_back(update_to_windows_format(item));
|
||||
item_list.emplace_back(update_to_windows_format(api_path, item));
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::raise_error(function_name, e, "exception occurred");
|
||||
}
|
||||
@@ -1425,25 +1433,32 @@ auto remote_server::winfsp_rename(PVOID /*file_desc*/, PWSTR file_name,
|
||||
-> packet::error_type {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
const auto relative_path = utils::string::to_utf8(file_name);
|
||||
const auto file_path = construct_path(relative_path);
|
||||
const auto new_relative_path = utils::string::to_utf8(new_file_name);
|
||||
const auto new_file_path = construct_path(new_relative_path);
|
||||
auto relative_path = utils::string::to_utf8(file_name);
|
||||
auto file_path = construct_path(relative_path);
|
||||
auto new_relative_path = utils::string::to_utf8(new_file_name);
|
||||
auto new_file_path = construct_path(new_relative_path);
|
||||
|
||||
auto res = -1;
|
||||
packet::error_type ret{};
|
||||
auto res{-1};
|
||||
errno = ENOENT;
|
||||
if (utils::file::file(file_path).exists()) {
|
||||
res = drive_.rename_file(construct_api_path(file_path),
|
||||
construct_api_path(new_file_path),
|
||||
replace_if_exists != 0U);
|
||||
ret = ((res < 0) ? static_cast<packet::error_type>(
|
||||
utils::unix_error_to_windows(errno))
|
||||
: 0);
|
||||
} else if (utils::file::directory(file_path).exists()) {
|
||||
res = drive_.rename_directory(construct_api_path(file_path),
|
||||
construct_api_path(new_file_path));
|
||||
ret =
|
||||
((res < 0) ? errno == EISDIR
|
||||
? static_cast<packet::error_type>(STATUS_ACCESS_DENIED)
|
||||
: static_cast<packet::error_type>(
|
||||
utils::unix_error_to_windows(errno))
|
||||
: 0);
|
||||
}
|
||||
|
||||
auto ret = ((res < 0) ? static_cast<packet::error_type>(
|
||||
utils::unix_error_to_windows(errno))
|
||||
: 0);
|
||||
RAISE_REMOTE_FUSE_SERVER_EVENT(function_name, file_path + "|" + new_file_path,
|
||||
ret);
|
||||
return ret;
|
||||
@@ -1679,30 +1694,53 @@ auto remote_server::json_release_directory_snapshot(
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto remote_server::update_to_windows_format(json &item) -> json & {
|
||||
const auto api_path = item["path"].get<std::string>();
|
||||
item["meta"][META_ACCESSED] = std::to_string(
|
||||
utils::string::to_uint64(empty_as_zero(item["meta"][META_ACCESSED])));
|
||||
item["meta"][META_CREATION] = std::to_string(
|
||||
utils::string::to_uint64(empty_as_zero(item["meta"][META_CREATION])));
|
||||
item["meta"][META_MODIFIED] = std::to_string(
|
||||
utils::string::to_uint64(empty_as_zero(item["meta"][META_MODIFIED])));
|
||||
auto remote_server::update_to_windows_format(const std::string &root_api_path,
|
||||
json &item) -> json & {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
if (item["meta"][META_WRITTEN].empty() ||
|
||||
(item["meta"][META_WRITTEN].get<std::string>() == "0") ||
|
||||
(item["meta"][META_WRITTEN].get<std::string>() ==
|
||||
std::to_string(utils::time::WIN32_TIME_CONVERSION))) {
|
||||
drive_.set_item_meta(api_path, META_WRITTEN,
|
||||
item["meta"][META_MODIFIED].get<std::string>());
|
||||
item["meta"][META_WRITTEN] = item["meta"][META_MODIFIED];
|
||||
auto api_path = item[JSON_API_PATH].get<std::string>();
|
||||
if (api_path == "." || api_path == "..") {
|
||||
auto orig_api_path{api_path};
|
||||
|
||||
api_path = api_path == "."
|
||||
? root_api_path
|
||||
: utils::path::get_parent_api_path(root_api_path);
|
||||
|
||||
api_meta_map meta;
|
||||
auto res = drive_.get_item_meta(api_path, meta);
|
||||
if (res != api_error::success) {
|
||||
utils::error::raise_api_path_error(
|
||||
function_name, api_path, res,
|
||||
fmt::format("failed to get '{}' meta", orig_api_path));
|
||||
return item;
|
||||
}
|
||||
|
||||
item[JSON_META] = meta;
|
||||
}
|
||||
|
||||
if (item["meta"][META_ATTRIBUTES].empty()) {
|
||||
item["meta"][META_ATTRIBUTES] =
|
||||
item["directory"].get<bool>() ? std::to_string(FILE_ATTRIBUTE_DIRECTORY)
|
||||
: std::to_string(FILE_ATTRIBUTE_ARCHIVE);
|
||||
item[JSON_META][META_ACCESSED] = std::to_string(
|
||||
utils::string::to_uint64(empty_as_zero(item[JSON_META][META_ACCESSED])));
|
||||
item[JSON_META][META_CREATION] = std::to_string(
|
||||
utils::string::to_uint64(empty_as_zero(item[JSON_META][META_CREATION])));
|
||||
item[JSON_META][META_MODIFIED] = std::to_string(
|
||||
utils::string::to_uint64(empty_as_zero(item[JSON_META][META_MODIFIED])));
|
||||
|
||||
if (item[JSON_META][META_WRITTEN].empty() ||
|
||||
(item[JSON_META][META_WRITTEN].get<std::string>() == "0") ||
|
||||
(item[JSON_META][META_WRITTEN].get<std::string>() ==
|
||||
std::to_string(utils::time::WIN32_TIME_CONVERSION))) {
|
||||
drive_.set_item_meta(api_path, META_WRITTEN,
|
||||
item[JSON_META][META_MODIFIED].get<std::string>());
|
||||
item[JSON_META][META_WRITTEN] = item[JSON_META][META_MODIFIED];
|
||||
}
|
||||
|
||||
if (item[JSON_META][META_ATTRIBUTES].empty()) {
|
||||
item[JSON_META][META_ATTRIBUTES] =
|
||||
item[JSON_DIRECTORY].get<bool>()
|
||||
? std::to_string(FILE_ATTRIBUTE_DIRECTORY)
|
||||
: std::to_string(FILE_ATTRIBUTE_ARCHIVE);
|
||||
drive_.set_item_meta(api_path, META_ATTRIBUTES,
|
||||
item["meta"][META_ATTRIBUTES].get<std::string>());
|
||||
item[JSON_META][META_ATTRIBUTES].get<std::string>());
|
||||
}
|
||||
|
||||
return item;
|
||||
|
@@ -188,7 +188,7 @@ auto remote_client::winfsp_create(PWSTR file_name, UINT32 create_options,
|
||||
DECODE_OR_IGNORE(&response, normalized_name);
|
||||
DECODE_OR_IGNORE(&response, exists);
|
||||
|
||||
if (ret == STATUS_SUCCESS) {
|
||||
if (exists == 0U) {
|
||||
*file_desc = reinterpret_cast<PVOID>(handle);
|
||||
set_open_info(to_handle(*file_desc),
|
||||
open_info{
|
||||
@@ -197,12 +197,12 @@ auto remote_client::winfsp_create(PWSTR file_name, UINT32 create_options,
|
||||
{},
|
||||
utils::string::to_utf8(file_name),
|
||||
});
|
||||
#if defined(_WIN32)
|
||||
if (exists) {
|
||||
::SetLastError(ERROR_ALREADY_EXISTS);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#if defined(_WIN32)
|
||||
else {
|
||||
ret = STATUS_OBJECT_NAME_COLLISION;
|
||||
}
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@@ -1061,7 +1061,7 @@ auto remote_server::winfsp_get_security_by_name(PWSTR file_name,
|
||||
auto file_path = utils::string::from_utf8(utils::path::combine(
|
||||
mount_location_, {utils::string::to_utf8(file_name)}));
|
||||
|
||||
auto ret = STATUS_BUFFER_OVERFLOW;
|
||||
auto ret{STATUS_BUFFER_OVERFLOW};
|
||||
if ((descriptor_size == nullptr) ||
|
||||
(*descriptor_size <= std::numeric_limits<SIZE_T>::max())) {
|
||||
auto *descriptor = descriptor_size == nullptr
|
||||
|
@@ -131,14 +131,14 @@ auto remote_winfsp_drive::Create(PWSTR file_name, UINT32 create_options,
|
||||
UINT64 allocation_size, PVOID * /*file_node*/,
|
||||
PVOID *file_desc, OpenFileInfo *ofi)
|
||||
-> NTSTATUS {
|
||||
remote::file_info fi{};
|
||||
remote::file_info f_info{};
|
||||
std::string normalized_name;
|
||||
BOOLEAN exists = 0;
|
||||
BOOLEAN exists{0};
|
||||
auto ret = remote_instance_->winfsp_create(
|
||||
file_name, create_options, granted_access, attributes, allocation_size,
|
||||
file_desc, &fi, normalized_name, exists);
|
||||
file_desc, &f_info, normalized_name, exists);
|
||||
if (ret == STATUS_SUCCESS) {
|
||||
set_file_info(ofi->FileInfo, fi);
|
||||
set_file_info(ofi->FileInfo, f_info);
|
||||
auto file_path = utils::string::from_utf8(normalized_name);
|
||||
wcsncpy(ofi->NormalizedName, file_path.data(), wcslen(file_path.c_str()));
|
||||
ofi->NormalizedNameSize =
|
||||
@@ -168,24 +168,32 @@ auto remote_winfsp_drive::GetSecurityByName(PWSTR file_name, PUINT32 attributes,
|
||||
PSECURITY_DESCRIPTOR descriptor,
|
||||
SIZE_T *descriptor_size)
|
||||
-> NTSTATUS {
|
||||
std::uint64_t sds{
|
||||
(descriptor_size == nullptr) ? 0U : *descriptor_size,
|
||||
};
|
||||
|
||||
std::wstring string_descriptor;
|
||||
std::uint64_t sds = (descriptor_size == nullptr) ? 0 : *descriptor_size;
|
||||
auto ret = remote_instance_->winfsp_get_security_by_name(
|
||||
file_name, attributes, descriptor_size ? &sds : nullptr,
|
||||
string_descriptor);
|
||||
*descriptor_size = static_cast<SIZE_T>(sds);
|
||||
if ((ret == STATUS_SUCCESS) && *descriptor_size) {
|
||||
PSECURITY_DESCRIPTOR sd{nullptr};
|
||||
ULONG sz2{0U};
|
||||
auto ret{
|
||||
remote_instance_->winfsp_get_security_by_name(
|
||||
file_name, attributes, descriptor_size ? &sds : nullptr,
|
||||
string_descriptor),
|
||||
};
|
||||
|
||||
if ((ret == STATUS_SUCCESS) && (descriptor_size != nullptr)) {
|
||||
*descriptor_size = static_cast<SIZE_T>(sds);
|
||||
|
||||
PSECURITY_DESCRIPTOR desc{nullptr};
|
||||
ULONG size{0U};
|
||||
if (::ConvertStringSecurityDescriptorToSecurityDescriptorW(
|
||||
string_descriptor.data(), SDDL_REVISION_1, &sd, &sz2)) {
|
||||
if (sz2 > *descriptor_size) {
|
||||
string_descriptor.data(), SDDL_REVISION_1, &desc, &size)) {
|
||||
if (size > *descriptor_size) {
|
||||
ret = STATUS_BUFFER_TOO_SMALL;
|
||||
} else {
|
||||
::CopyMemory(descriptor, sd, sz2);
|
||||
::CopyMemory(descriptor, desc, size);
|
||||
}
|
||||
*descriptor_size = sz2;
|
||||
::LocalFree(sd);
|
||||
|
||||
*descriptor_size = size;
|
||||
::LocalFree(desc);
|
||||
} else {
|
||||
ret = FspNtStatusFromWin32(::GetLastError());
|
||||
}
|
||||
@@ -361,11 +369,10 @@ auto remote_winfsp_drive::ReadDirectory(PVOID /*file_node*/, PVOID file_desc,
|
||||
&ret)) {
|
||||
auto item_found = false;
|
||||
for (const auto &item : item_list) {
|
||||
auto item_path = item["path"].get<std::string>();
|
||||
auto item_path = item[JSON_API_PATH].get<std::string>();
|
||||
auto display_name = utils::string::from_utf8(
|
||||
utils::path::strip_to_file_name(item_path));
|
||||
if (not marker || (marker && item_found)) {
|
||||
// if (not utils::path::is_ads_file_path(item_path)) {
|
||||
union {
|
||||
UINT8 B[FIELD_OFFSET(FSP_FSCTL_DIR_INFO, FileNameBuf) +
|
||||
((repertory::max_path_length + 1U) * sizeof(WCHAR))];
|
||||
@@ -380,10 +387,8 @@ auto remote_winfsp_drive::ReadDirectory(PVOID /*file_node*/, PVOID file_desc,
|
||||
display_name.size()) *
|
||||
sizeof(WCHAR)));
|
||||
|
||||
if (not item["meta"].empty() ||
|
||||
((item_path != ".") && (item_path != ".."))) {
|
||||
populate_file_info(item, directory_info->FileInfo);
|
||||
}
|
||||
populate_file_info(item, directory_info->FileInfo);
|
||||
|
||||
if (ret == STATUS_SUCCESS) {
|
||||
::wcscpy_s(&directory_info->FileNameBuf[0],
|
||||
repertory::max_path_length, &display_name[0]);
|
||||
@@ -394,7 +399,6 @@ auto remote_winfsp_drive::ReadDirectory(PVOID /*file_node*/, PVOID file_desc,
|
||||
break;
|
||||
}
|
||||
}
|
||||
// }
|
||||
} else {
|
||||
item_found = display_name == std::wstring(marker);
|
||||
}
|
||||
@@ -418,8 +422,13 @@ auto remote_winfsp_drive::ReadDirectory(PVOID /*file_node*/, PVOID file_desc,
|
||||
auto remote_winfsp_drive::Rename(PVOID /*file_node*/, PVOID file_desc,
|
||||
PWSTR file_name, PWSTR new_file_name,
|
||||
BOOLEAN replace_if_exists) -> NTSTATUS {
|
||||
return remote_instance_->winfsp_rename(file_desc, file_name, new_file_name,
|
||||
replace_if_exists);
|
||||
auto res = remote_instance_->winfsp_rename(file_desc, file_name,
|
||||
new_file_name, replace_if_exists);
|
||||
if (res == STATUS_OBJECT_NAME_EXISTS) {
|
||||
return FspNtStatusFromWin32(ERROR_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
auto remote_winfsp_drive::SetBasicInfo(PVOID /*file_node*/, PVOID file_desc,
|
||||
@@ -428,11 +437,11 @@ auto remote_winfsp_drive::SetBasicInfo(PVOID /*file_node*/, PVOID file_desc,
|
||||
UINT64 last_write_time,
|
||||
UINT64 change_time, FileInfo *file_info)
|
||||
-> NTSTATUS {
|
||||
remote::file_info fi{};
|
||||
remote::file_info f_info{};
|
||||
auto ret = remote_instance_->winfsp_set_basic_info(
|
||||
file_desc, attributes, creation_time, last_access_time, last_write_time,
|
||||
change_time, &fi);
|
||||
set_file_info(*file_info, fi);
|
||||
change_time, &f_info);
|
||||
set_file_info(*file_info, f_info);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -473,7 +482,6 @@ VOID remote_winfsp_drive::Unmounted(PVOID host) {
|
||||
}
|
||||
remote_instance_->winfsp_unmounted(file_system_host->MountPoint());
|
||||
remote_instance_.reset();
|
||||
mount_location_ = "";
|
||||
}
|
||||
|
||||
auto remote_winfsp_drive::Write(PVOID /*file_node*/, PVOID file_desc,
|
||||
|
@@ -67,7 +67,7 @@ auto sia_provider::check_version(std::string &required_version,
|
||||
std::string &returned_version) const -> bool {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
required_version = "2.0.0";
|
||||
required_version = RENTERD_MIN_VERSION;
|
||||
|
||||
try {
|
||||
curl::requests::http_get get{};
|
||||
@@ -149,12 +149,58 @@ auto sia_provider::create_directory_impl(const std::string &api_path,
|
||||
return api_error::success;
|
||||
}
|
||||
|
||||
auto sia_provider::create_directory_key(const std::string &api_path) const
|
||||
-> api_error {
|
||||
auto parent_api_path = utils::path::get_parent_api_path(api_path);
|
||||
|
||||
json object_list;
|
||||
if (not get_object_list(parent_api_path, object_list)) {
|
||||
return api_error::comm_error;
|
||||
}
|
||||
|
||||
if (not object_list.contains("objects")) {
|
||||
return api_error::directory_not_found;
|
||||
}
|
||||
|
||||
const auto &list = object_list.at("objects");
|
||||
if (std::ranges::find_if(list, [&api_path](auto &&entry) -> bool {
|
||||
return entry.at("key").template get<std::string>() == api_path + '/';
|
||||
}) == list.end()) {
|
||||
return api_error::directory_not_found;
|
||||
}
|
||||
|
||||
api_meta_map meta;
|
||||
return const_cast<sia_provider *>(this)->create_directory_impl(api_path,
|
||||
meta);
|
||||
}
|
||||
|
||||
auto sia_provider::ensure_directory_exists(const std::string &api_path) const
|
||||
-> api_error {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
bool exists{};
|
||||
auto res{is_directory(api_path, exists)};
|
||||
if (res != api_error::success) {
|
||||
utils::error::raise_api_path_error(function_name, api_path, res,
|
||||
"failed detect existing directory");
|
||||
return res;
|
||||
}
|
||||
|
||||
if (not exists) {
|
||||
utils::error::raise_api_path_error(function_name, api_path, res,
|
||||
"directory not found");
|
||||
return api_error::directory_not_found;
|
||||
}
|
||||
|
||||
return api_error::success;
|
||||
}
|
||||
|
||||
auto sia_provider::get_directory_item_count(const std::string &api_path) const
|
||||
-> std::uint64_t {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
json object_list{};
|
||||
json object_list;
|
||||
if (not get_object_list(api_path, object_list)) {
|
||||
return 0U;
|
||||
}
|
||||
@@ -192,41 +238,25 @@ auto sia_provider::get_directory_items_impl(const std::string &api_path,
|
||||
-> api_error {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
json object_list{};
|
||||
json object_list;
|
||||
if (not get_object_list(api_path, object_list)) {
|
||||
return api_error::comm_error;
|
||||
}
|
||||
|
||||
if (object_list.contains("objects")) {
|
||||
for (const auto &entry : object_list.at("objects")) {
|
||||
try {
|
||||
auto name{entry.at("key").get<std::string>()};
|
||||
auto entry_api_path{utils::path::create_api_path(name)};
|
||||
|
||||
auto directory{utils::string::ends_with(name, "/")};
|
||||
if (directory && (entry_api_path == api_path)) {
|
||||
continue;
|
||||
iterate_objects(
|
||||
api_path, object_list,
|
||||
[&](auto &&entry_api_path, auto &&directory, auto &&entry) {
|
||||
api_meta_map meta;
|
||||
auto res{get_item_meta(entry_api_path, meta)};
|
||||
if (res != api_error::success) {
|
||||
utils::error::raise_api_path_error(function_name, entry_api_path, res,
|
||||
"failed to get item meta");
|
||||
return;
|
||||
}
|
||||
|
||||
api_file file{};
|
||||
api_meta_map meta{};
|
||||
if (get_item_meta(entry_api_path, meta) == api_error::item_not_found) {
|
||||
file = create_api_file(entry_api_path, "",
|
||||
directory ? 0U
|
||||
: entry["size"].get<std::uint64_t>(),
|
||||
get_last_modified(entry));
|
||||
get_api_item_added()(directory, file);
|
||||
auto res{get_item_meta(entry_api_path, meta)};
|
||||
if (res != api_error::success) {
|
||||
utils::error::raise_api_path_error(function_name, entry_api_path,
|
||||
res, "failed to get item meta");
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
file = create_api_file(
|
||||
entry_api_path,
|
||||
directory ? 0U : entry["size"].get<std::uint64_t>(), meta);
|
||||
}
|
||||
auto file = create_api_file(
|
||||
entry_api_path,
|
||||
directory ? 0U : entry["size"].template get<std::uint64_t>(), meta);
|
||||
|
||||
directory_item dir_item{};
|
||||
dir_item.api_parent = file.api_parent;
|
||||
@@ -235,13 +265,7 @@ auto sia_provider::get_directory_items_impl(const std::string &api_path,
|
||||
dir_item.meta = meta;
|
||||
dir_item.size = file.file_size;
|
||||
list.emplace_back(std::move(dir_item));
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::raise_api_path_error(function_name, api_path, e,
|
||||
"failed to process entry|" +
|
||||
entry.dump());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return api_error::success;
|
||||
}
|
||||
@@ -290,73 +314,45 @@ auto sia_provider::get_file(const std::string &api_path, api_file &file) const
|
||||
return api_error::error;
|
||||
}
|
||||
|
||||
auto sia_provider::get_file_list(api_file_list &list,
|
||||
std::string & /* marker */) const
|
||||
auto sia_provider::get_file_list(api_file_list &list, std::string &marker) const
|
||||
-> api_error {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
using dir_func = std::function<api_error(std::string api_path)>;
|
||||
const dir_func get_files_in_dir = [&](std::string api_path) -> api_error {
|
||||
try {
|
||||
nlohmann::json object_list{};
|
||||
if (not get_object_list(api_path, object_list)) {
|
||||
return api_error::comm_error;
|
||||
}
|
||||
|
||||
if (object_list.contains("objects")) {
|
||||
for (const auto &entry : object_list.at("objects")) {
|
||||
auto name{entry.at("key").get<std::string>()};
|
||||
auto entry_api_path{utils::path::create_api_path(name)};
|
||||
|
||||
if (utils::string::ends_with(name, "/")) {
|
||||
if (entry_api_path == utils::path::create_api_path(api_path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
api_meta_map meta{};
|
||||
if (get_item_meta(entry_api_path, meta) ==
|
||||
api_error::item_not_found) {
|
||||
auto dir{
|
||||
create_api_file(entry_api_path, "", 0U,
|
||||
get_last_modified(entry)),
|
||||
};
|
||||
get_api_item_added()(true, dir);
|
||||
}
|
||||
|
||||
auto res{get_files_in_dir(entry_api_path)};
|
||||
if (res != api_error::success) {
|
||||
return res;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
api_file file{};
|
||||
api_meta_map meta{};
|
||||
if (get_item_meta(entry_api_path, meta) ==
|
||||
api_error::item_not_found) {
|
||||
file = create_api_file(entry_api_path, "",
|
||||
entry["size"].get<std::uint64_t>(),
|
||||
get_last_modified(entry));
|
||||
get_api_item_added()(false, file);
|
||||
} else {
|
||||
file = create_api_file(entry_api_path,
|
||||
entry["size"].get<std::uint64_t>(), meta);
|
||||
}
|
||||
|
||||
list.emplace_back(std::move(file));
|
||||
}
|
||||
}
|
||||
|
||||
return api_error::success;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::raise_api_path_error(function_name, api_path, e,
|
||||
"failed to process directory");
|
||||
try {
|
||||
json object_list;
|
||||
if (not get_object_list("", object_list, marker)) {
|
||||
return api_error::comm_error;
|
||||
}
|
||||
|
||||
return api_error::error;
|
||||
};
|
||||
iterate_objects("/", object_list,
|
||||
[&](auto &&entry_api_path, auto &&directory, auto &&entry) {
|
||||
if (directory) {
|
||||
return;
|
||||
}
|
||||
|
||||
return get_files_in_dir("");
|
||||
api_meta_map meta;
|
||||
auto res{get_item_meta(entry_api_path, meta)};
|
||||
if (res != api_error::success) {
|
||||
utils::error::raise_api_path_error(
|
||||
function_name, entry_api_path, res,
|
||||
"failed to get item meta");
|
||||
return;
|
||||
}
|
||||
|
||||
list.emplace_back(create_api_file(
|
||||
entry_api_path,
|
||||
entry["size"].template get<std::uint64_t>(), meta));
|
||||
});
|
||||
|
||||
marker = object_list.at("nextMarker").get<std::string>();
|
||||
return object_list.at("hasMore").get<bool>() ? api_error::more_data
|
||||
: api_error::success;
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::raise_api_path_error(function_name, "/", e,
|
||||
"failed to process directory");
|
||||
}
|
||||
|
||||
return api_error::error;
|
||||
}
|
||||
|
||||
auto sia_provider::get_object_info(const std::string &api_path,
|
||||
@@ -408,7 +404,9 @@ auto sia_provider::get_object_info(const std::string &api_path,
|
||||
}
|
||||
|
||||
auto sia_provider::get_object_list(const std::string &api_path,
|
||||
nlohmann::json &object_list) const -> bool {
|
||||
nlohmann::json &object_list,
|
||||
std::optional<std::string> marker) const
|
||||
-> bool {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
try {
|
||||
@@ -416,7 +414,12 @@ auto sia_provider::get_object_list(const std::string &api_path,
|
||||
get.allow_timeout = true;
|
||||
get.path = "/api/bus/objects" + api_path + "/";
|
||||
get.query["bucket"] = get_bucket(get_sia_config());
|
||||
get.query["delimiter"] = "/";
|
||||
if (marker.has_value()) {
|
||||
get.query["limit"] = "1000";
|
||||
get.query["marker"] = marker.value();
|
||||
} else {
|
||||
get.query["delimiter"] = "/";
|
||||
}
|
||||
|
||||
std::string error_data;
|
||||
get.response_handler = [&error_data, &object_list](auto &&data,
|
||||
@@ -513,6 +516,14 @@ auto sia_provider::is_directory(const std::string &api_path, bool &exists) const
|
||||
json file_data{};
|
||||
auto res{get_object_info(api_path + '/', file_data)};
|
||||
if (res == api_error::item_not_found) {
|
||||
if (create_directory_key(api_path) == api_error::success) {
|
||||
exists = true;
|
||||
return api_error::success;
|
||||
}
|
||||
}
|
||||
|
||||
if (res == api_error::directory_not_found ||
|
||||
res == api_error::item_not_found) {
|
||||
return api_error::success;
|
||||
}
|
||||
|
||||
@@ -609,6 +620,52 @@ auto sia_provider::is_online() const -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
void sia_provider::iterate_objects(
|
||||
const std::string &api_path, const json &object_list,
|
||||
std::function<void(const std::string &, bool, json)> handle_entry) const {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
if (not object_list.contains("objects")) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto &entry : object_list.at("objects")) {
|
||||
try {
|
||||
auto name{entry.at("key").get<std::string>()};
|
||||
|
||||
auto directory{utils::string::ends_with(name, "/")};
|
||||
auto entry_api_path{utils::path::create_api_path(name)};
|
||||
|
||||
{
|
||||
api_meta_map meta{};
|
||||
if (get_item_meta(entry_api_path, meta) == api_error::item_not_found) {
|
||||
auto file = create_api_file(
|
||||
entry_api_path, "",
|
||||
directory ? 0U : entry["size"].get<std::uint64_t>(),
|
||||
get_last_modified(entry));
|
||||
if (directory) {
|
||||
auto res{ensure_directory_exists(entry_api_path)};
|
||||
if (res != api_error::success) {
|
||||
utils::error::raise_api_path_error(
|
||||
function_name, entry_api_path, res,
|
||||
"failed detect existing directory");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
get_api_item_added()(directory, file);
|
||||
}
|
||||
}
|
||||
|
||||
handle_entry(entry_api_path, directory, entry);
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::raise_api_path_error(
|
||||
function_name, api_path, e,
|
||||
fmt::format("failed to process entry|{}", entry.dump()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto sia_provider::read_file_bytes(const std::string &api_path,
|
||||
std::size_t size, std::uint64_t offset,
|
||||
data_buffer &buffer,
|
||||
@@ -636,7 +693,6 @@ auto sia_provider::read_file_bytes(const std::string &api_path,
|
||||
++idx) {
|
||||
long response_code{};
|
||||
const auto notify_retry = [&]() {
|
||||
fmt::println("{}", std::string(buffer.begin(), buffer.end()));
|
||||
if (response_code == 0) {
|
||||
utils::error::raise_api_path_error(
|
||||
function_name, api_path, api_error::comm_error,
|
||||
|
@@ -1,187 +1,189 @@
|
||||
/*
|
||||
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 "rpc/server/server.hpp"
|
||||
|
||||
#include "app_config.hpp"
|
||||
#include "events/event_system.hpp"
|
||||
#include "events/types/service_start_begin.hpp"
|
||||
#include "events/types/service_start_end.hpp"
|
||||
#include "events/types/service_stop_begin.hpp"
|
||||
#include "events/types/service_stop_end.hpp"
|
||||
#include "events/types/unmount_requested.hpp"
|
||||
#include "rpc/common.hpp"
|
||||
#include "utils/error_utils.hpp"
|
||||
|
||||
namespace repertory {
|
||||
server::server(app_config &config) : config_(config) {}
|
||||
|
||||
void server::handle_get_config(const httplib::Request & /*req*/,
|
||||
httplib::Response &res) {
|
||||
auto data = config_.get_json();
|
||||
clean_json_config(config_.get_provider_type(), data);
|
||||
res.set_content(data.dump(), "application/json");
|
||||
res.status = http_error_codes::ok;
|
||||
}
|
||||
|
||||
void server::handle_get_config_value_by_name(const httplib::Request &req,
|
||||
httplib::Response &res) {
|
||||
auto name = req.get_param_value("name");
|
||||
auto data = json({{
|
||||
"value",
|
||||
clean_json_value(name, config_.get_value_by_name(name)),
|
||||
}});
|
||||
res.set_content(data.dump(), "application/json");
|
||||
res.status = http_error_codes::ok;
|
||||
}
|
||||
|
||||
void server::handle_set_config_value_by_name(const httplib::Request &req,
|
||||
httplib::Response &res) {
|
||||
auto name = req.get_param_value("name");
|
||||
auto value = req.get_param_value("value");
|
||||
|
||||
json data = {{
|
||||
"value",
|
||||
clean_json_value(name, config_.set_value_by_name(name, value)),
|
||||
}};
|
||||
res.set_content(data.dump(), "application/json");
|
||||
res.status = http_error_codes::ok;
|
||||
}
|
||||
|
||||
void server::handle_unmount(const httplib::Request & /*req*/,
|
||||
httplib::Response &res) {
|
||||
event_system::instance().raise<unmount_requested>();
|
||||
res.status = http_error_codes::ok;
|
||||
}
|
||||
|
||||
void server::initialize(httplib::Server &inst) {
|
||||
inst.Get("/api/v1/" + rpc_method::get_config, [this](auto &&req, auto &&res) {
|
||||
handle_get_config(std::forward<decltype(req)>(req),
|
||||
std::forward<decltype(res)>(res));
|
||||
});
|
||||
|
||||
inst.Get("/api/v1/" + rpc_method::get_config_value_by_name,
|
||||
[this](auto &&req, auto &&res) {
|
||||
handle_get_config_value_by_name(std::forward<decltype(req)>(req),
|
||||
std::forward<decltype(res)>(res));
|
||||
});
|
||||
|
||||
inst.Post("/api/v1/" + rpc_method::set_config_value_by_name,
|
||||
[this](auto &&req, auto &&res) {
|
||||
handle_set_config_value_by_name(std::forward<decltype(req)>(req),
|
||||
std::forward<decltype(res)>(res));
|
||||
});
|
||||
|
||||
inst.Post("/api/v1/" + rpc_method::unmount, [this](auto &&req, auto &&res) {
|
||||
handle_unmount(std::forward<decltype(req)>(req),
|
||||
std::forward<decltype(res)>(res));
|
||||
});
|
||||
}
|
||||
|
||||
void server::start() {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
mutex_lock lock(start_stop_mutex_);
|
||||
if (server_thread_) {
|
||||
return;
|
||||
}
|
||||
|
||||
event_system::instance().raise<service_start_begin>(function_name, "server");
|
||||
|
||||
server_ = std::make_unique<httplib::Server>();
|
||||
|
||||
server_->set_exception_handler([](const httplib::Request &req,
|
||||
httplib::Response &res,
|
||||
std::exception_ptr ptr) {
|
||||
json data = {{"path", req.path}};
|
||||
|
||||
try {
|
||||
std::rethrow_exception(ptr);
|
||||
} catch (std::exception &e) {
|
||||
data["error"] = (e.what() == nullptr) ? "unknown error" : e.what();
|
||||
utils::error::raise_error(function_name, e,
|
||||
"failed request: " + req.path);
|
||||
} catch (...) {
|
||||
data["error"] = "unknown error";
|
||||
utils::error::raise_error(function_name, "unknown error",
|
||||
"failed request: " + req.path);
|
||||
}
|
||||
|
||||
res.set_content(data.dump(), "application/json");
|
||||
res.status = http_error_codes::internal_error;
|
||||
});
|
||||
|
||||
server_->set_pre_routing_handler(
|
||||
[this](auto &&req, auto &&res) -> httplib::Server::HandlerResponse {
|
||||
if (rpc::check_authorization(config_, req)) {
|
||||
return httplib::Server::HandlerResponse::Unhandled;
|
||||
}
|
||||
|
||||
res.status = http_error_codes::unauthorized;
|
||||
return httplib::Server::HandlerResponse::Handled;
|
||||
});
|
||||
|
||||
initialize(*server_);
|
||||
|
||||
server_thread_ = std::make_unique<std::thread>([this]() {
|
||||
server_->set_socket_options([](auto &&sock) {
|
||||
#if defined(_WIN32)
|
||||
int enable{1};
|
||||
setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
|
||||
reinterpret_cast<const char *>(&enable), sizeof(enable));
|
||||
#else // !defined(_WIN32)
|
||||
linger opt{1, 0};
|
||||
setsockopt(sock, SOL_SOCKET, SO_LINGER,
|
||||
reinterpret_cast<const char *>(&opt), sizeof(opt));
|
||||
#endif // defined(_WIN32)
|
||||
});
|
||||
|
||||
server_->listen("127.0.0.1", config_.get_api_port());
|
||||
});
|
||||
event_system::instance().raise<service_start_end>(function_name, "server");
|
||||
}
|
||||
|
||||
void server::stop() {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
unique_mutex_lock lock(start_stop_mutex_);
|
||||
if (not server_thread_) {
|
||||
return;
|
||||
}
|
||||
|
||||
event_system::instance().raise<service_stop_begin>(function_name, "server");
|
||||
|
||||
server_->stop();
|
||||
|
||||
std::unique_ptr<std::thread> thread{nullptr};
|
||||
std::swap(thread, server_thread_);
|
||||
lock.unlock();
|
||||
|
||||
thread->join();
|
||||
thread.reset();
|
||||
|
||||
lock.lock();
|
||||
server_.reset();
|
||||
lock.unlock();
|
||||
|
||||
event_system::instance().raise<service_stop_end>(function_name, "server");
|
||||
}
|
||||
} // namespace repertory
|
||||
/*
|
||||
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 "rpc/server/server.hpp"
|
||||
|
||||
#include "app_config.hpp"
|
||||
#include "events/event_system.hpp"
|
||||
#include "events/types/service_start_begin.hpp"
|
||||
#include "events/types/service_start_end.hpp"
|
||||
#include "events/types/service_stop_begin.hpp"
|
||||
#include "events/types/service_stop_end.hpp"
|
||||
#include "events/types/unmount_requested.hpp"
|
||||
#include "rpc/common.hpp"
|
||||
#include "utils/error_utils.hpp"
|
||||
|
||||
namespace repertory {
|
||||
server::server(app_config &config) : config_(config) {}
|
||||
|
||||
void server::handle_get_config(const httplib::Request & /*req*/,
|
||||
httplib::Response &res) {
|
||||
auto data = config_.get_json();
|
||||
clean_json_config(config_.get_provider_type(), data);
|
||||
res.set_content(data.dump(), "application/json");
|
||||
res.status = http_error_codes::ok;
|
||||
}
|
||||
|
||||
void server::handle_get_config_value_by_name(const httplib::Request &req,
|
||||
httplib::Response &res) {
|
||||
auto name = req.get_param_value("name");
|
||||
auto data = json({{
|
||||
"value",
|
||||
clean_json_value(name, config_.get_value_by_name(name)),
|
||||
}});
|
||||
res.set_content(data.dump(), "application/json");
|
||||
res.status = http_error_codes::ok;
|
||||
}
|
||||
|
||||
void server::handle_set_config_value_by_name(const httplib::Request &req,
|
||||
httplib::Response &res) {
|
||||
auto name = req.get_param_value("name");
|
||||
auto value = req.get_param_value("value");
|
||||
|
||||
json data = {{
|
||||
"value",
|
||||
clean_json_value(name, config_.set_value_by_name(name, value)),
|
||||
}};
|
||||
res.set_content(data.dump(), "application/json");
|
||||
res.status = http_error_codes::ok;
|
||||
}
|
||||
|
||||
void server::handle_unmount(const httplib::Request & /*req*/,
|
||||
httplib::Response &res) {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
event_system::instance().raise<unmount_requested>(function_name);
|
||||
res.status = http_error_codes::ok;
|
||||
}
|
||||
|
||||
void server::initialize(httplib::Server &inst) {
|
||||
inst.Get("/api/v1/" + rpc_method::get_config, [this](auto &&req, auto &&res) {
|
||||
handle_get_config(std::forward<decltype(req)>(req),
|
||||
std::forward<decltype(res)>(res));
|
||||
});
|
||||
|
||||
inst.Get("/api/v1/" + rpc_method::get_config_value_by_name,
|
||||
[this](auto &&req, auto &&res) {
|
||||
handle_get_config_value_by_name(std::forward<decltype(req)>(req),
|
||||
std::forward<decltype(res)>(res));
|
||||
});
|
||||
|
||||
inst.Post("/api/v1/" + rpc_method::set_config_value_by_name,
|
||||
[this](auto &&req, auto &&res) {
|
||||
handle_set_config_value_by_name(std::forward<decltype(req)>(req),
|
||||
std::forward<decltype(res)>(res));
|
||||
});
|
||||
|
||||
inst.Post("/api/v1/" + rpc_method::unmount, [this](auto &&req, auto &&res) {
|
||||
handle_unmount(std::forward<decltype(req)>(req),
|
||||
std::forward<decltype(res)>(res));
|
||||
});
|
||||
}
|
||||
|
||||
void server::start() {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
mutex_lock lock(start_stop_mutex_);
|
||||
if (server_thread_) {
|
||||
return;
|
||||
}
|
||||
|
||||
event_system::instance().raise<service_start_begin>(function_name, "server");
|
||||
|
||||
server_ = std::make_unique<httplib::Server>();
|
||||
|
||||
server_->set_exception_handler([](const httplib::Request &req,
|
||||
httplib::Response &res,
|
||||
std::exception_ptr ptr) {
|
||||
json data = {{"path", req.path}};
|
||||
|
||||
try {
|
||||
std::rethrow_exception(ptr);
|
||||
} catch (std::exception &e) {
|
||||
data["error"] = (e.what() == nullptr) ? "unknown error" : e.what();
|
||||
utils::error::raise_error(function_name, e,
|
||||
"failed request: " + req.path);
|
||||
} catch (...) {
|
||||
data["error"] = "unknown error";
|
||||
utils::error::raise_error(function_name, "unknown error",
|
||||
"failed request: " + req.path);
|
||||
}
|
||||
|
||||
res.set_content(data.dump(), "application/json");
|
||||
res.status = http_error_codes::internal_error;
|
||||
});
|
||||
|
||||
server_->set_pre_routing_handler(
|
||||
[this](auto &&req, auto &&res) -> httplib::Server::HandlerResponse {
|
||||
if (rpc::check_authorization(config_, req)) {
|
||||
return httplib::Server::HandlerResponse::Unhandled;
|
||||
}
|
||||
|
||||
res.status = http_error_codes::unauthorized;
|
||||
return httplib::Server::HandlerResponse::Handled;
|
||||
});
|
||||
|
||||
initialize(*server_);
|
||||
|
||||
server_thread_ = std::make_unique<std::thread>([this]() {
|
||||
server_->set_socket_options([](auto &&sock) {
|
||||
#if defined(_WIN32)
|
||||
int enable{1};
|
||||
setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
|
||||
reinterpret_cast<const char *>(&enable), sizeof(enable));
|
||||
#else // !defined(_WIN32)
|
||||
linger opt{1, 0};
|
||||
setsockopt(sock, SOL_SOCKET, SO_LINGER,
|
||||
reinterpret_cast<const char *>(&opt), sizeof(opt));
|
||||
#endif // defined(_WIN32)
|
||||
});
|
||||
|
||||
server_->listen("127.0.0.1", config_.get_api_port());
|
||||
});
|
||||
event_system::instance().raise<service_start_end>(function_name, "server");
|
||||
}
|
||||
|
||||
void server::stop() {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
unique_mutex_lock lock(start_stop_mutex_);
|
||||
if (not server_thread_) {
|
||||
return;
|
||||
}
|
||||
|
||||
event_system::instance().raise<service_stop_begin>(function_name, "server");
|
||||
|
||||
server_->stop();
|
||||
|
||||
std::unique_ptr<std::thread> thread{nullptr};
|
||||
std::swap(thread, server_thread_);
|
||||
lock.unlock();
|
||||
|
||||
thread->join();
|
||||
thread.reset();
|
||||
|
||||
lock.lock();
|
||||
server_.reset();
|
||||
lock.unlock();
|
||||
|
||||
event_system::instance().raise<service_stop_end>(function_name, "server");
|
||||
}
|
||||
} // namespace repertory
|
||||
|
@@ -23,6 +23,8 @@
|
||||
#include "backward.hpp"
|
||||
#endif // defined(PROJECT_ENABLE_BACKWARD_CPP)
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "cli/actions.hpp"
|
||||
#include "initialize.hpp"
|
||||
#include "types/repertory.hpp"
|
||||
@@ -44,7 +46,7 @@ auto main(int argc, char **argv) -> int {
|
||||
std::vector<const char *> args;
|
||||
{
|
||||
auto args_span = std::span(argv, static_cast<std::size_t>(argc));
|
||||
std::copy(args_span.begin(), args_span.end(), std::back_inserter(args));
|
||||
std::ranges::copy(args_span, std::back_inserter(args));
|
||||
}
|
||||
|
||||
if (argc == 1) {
|
||||
|
@@ -326,9 +326,9 @@ void handlers::handle_put_mount_location(const httplib::Request &req,
|
||||
void handlers::handle_get_available_locations(httplib::Response &res) {
|
||||
#if defined(_WIN32)
|
||||
constexpr const std::array<std::string_view, 26U> letters{
|
||||
"A:", "B:", "C:", "D:", "E:", "F:", "G:", "H:", "I:",
|
||||
"J:", "K:", "L:", "M:", "N:", "O:", "P:", "Q:", "R:",
|
||||
"S:", "T:", "U:", "V:", "W:", "X:", "Y:", "Z:",
|
||||
"a:", "b:", "c:", "d:", "e:", "f:", "g:", "h:", "i:",
|
||||
"j:", "k:", "l:", "m:", "n:", "o:", "p:", "q:", "r:",
|
||||
"s:", "t:", "u:", "v:", "w:", "x:", "y:", "z:",
|
||||
};
|
||||
|
||||
auto available = std::accumulate(
|
||||
|
@@ -54,21 +54,31 @@ namespace repertory {
|
||||
struct local_s3 final {
|
||||
static constexpr const provider_type type{provider_type::s3};
|
||||
static constexpr const provider_type type2{provider_type::s3};
|
||||
static constexpr const std::uint16_t port{0U};
|
||||
};
|
||||
|
||||
struct local_sia final {
|
||||
static constexpr const provider_type type{provider_type::sia};
|
||||
static constexpr const provider_type type2{provider_type::sia};
|
||||
static constexpr const std::uint16_t port{0U};
|
||||
};
|
||||
|
||||
struct remote_s3 final {
|
||||
static constexpr const provider_type type{provider_type::remote};
|
||||
static constexpr const provider_type type2{provider_type::s3};
|
||||
static constexpr const std::uint16_t port{0U};
|
||||
};
|
||||
|
||||
struct remote_sia final {
|
||||
static constexpr const provider_type type{provider_type::remote};
|
||||
static constexpr const provider_type type2{provider_type::sia};
|
||||
static constexpr const std::uint16_t port{0U};
|
||||
};
|
||||
|
||||
struct remote_linux_to_winfsp final {
|
||||
static constexpr const provider_type type{provider_type::remote};
|
||||
static constexpr const provider_type type2{provider_type::unknown};
|
||||
static constexpr const std::uint16_t port{40001U};
|
||||
};
|
||||
|
||||
template <typename provider_t> class fuse_test : public ::testing::Test {
|
||||
@@ -113,7 +123,7 @@ protected:
|
||||
|
||||
auto r_cfg = config->get_remote_mount();
|
||||
r_cfg.enable = true;
|
||||
r_cfg.api_port = 30000U;
|
||||
r_cfg.api_port = 40000U;
|
||||
config->set_remote_mount(r_cfg);
|
||||
}
|
||||
|
||||
@@ -160,7 +170,7 @@ protected:
|
||||
|
||||
auto r_cfg = config->get_remote_mount();
|
||||
r_cfg.enable = true;
|
||||
r_cfg.api_port = 30000U;
|
||||
r_cfg.api_port = 40000U;
|
||||
config->set_remote_mount(r_cfg);
|
||||
}
|
||||
|
||||
@@ -178,7 +188,7 @@ protected:
|
||||
execute_mount(drive_args, mount_location);
|
||||
};
|
||||
|
||||
const auto mount_remote = [&]() {
|
||||
const auto mount_remote = [&](std::uint16_t port = 40000U) {
|
||||
{
|
||||
mount_location2 = mount_location;
|
||||
|
||||
@@ -187,7 +197,8 @@ protected:
|
||||
{
|
||||
"fuse_test",
|
||||
app_config::get_provider_name(provider_t::type) + '_' +
|
||||
app_config::get_provider_name(provider_t::type2),
|
||||
app_config::get_provider_name(provider_t::type2) + '_' +
|
||||
std::to_string(port),
|
||||
});
|
||||
|
||||
mount_location = utils::path::combine(test_directory, {"mount"});
|
||||
@@ -206,7 +217,7 @@ protected:
|
||||
"-dd",
|
||||
config2->get_data_directory(),
|
||||
"-rm",
|
||||
"localhost:30000",
|
||||
fmt::format("localhost:{}", port),
|
||||
mount_location,
|
||||
});
|
||||
}
|
||||
@@ -233,6 +244,10 @@ protected:
|
||||
mount_sia();
|
||||
} break;
|
||||
|
||||
case provider_type::unknown:
|
||||
mount_remote(provider_t::port);
|
||||
return;
|
||||
|
||||
default:
|
||||
throw std::runtime_error("remote provider type is not implemented");
|
||||
return;
|
||||
@@ -410,9 +425,15 @@ 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, remote_s3>;
|
||||
#if defined(__linux__)
|
||||
using fuse_provider_types =
|
||||
::testing::Types<local_s3, remote_s3, local_sia, remote_sia>;
|
||||
// using fuse_provider_types =
|
||||
// ::testing::Types<local_s3, remote_s3, local_sia, remote_sia,
|
||||
// remote_linux_to_winfsp>;
|
||||
#else // !defined(__linux__)
|
||||
build fails here
|
||||
#endif // defined(_WIN32)
|
||||
} // namespace repertory
|
||||
|
||||
#endif // !defined(_WIN32)
|
||||
|
@@ -45,21 +45,31 @@ namespace repertory {
|
||||
struct local_s3 final {
|
||||
static constexpr const provider_type type{provider_type::s3};
|
||||
static constexpr const provider_type type2{provider_type::s3};
|
||||
static constexpr const std::uint16_t port{0U};
|
||||
};
|
||||
|
||||
struct local_sia final {
|
||||
static constexpr const provider_type type{provider_type::sia};
|
||||
static constexpr const provider_type type2{provider_type::sia};
|
||||
static constexpr const std::uint16_t port{0U};
|
||||
};
|
||||
|
||||
struct remote_s3 final {
|
||||
static constexpr const provider_type type{provider_type::remote};
|
||||
static constexpr const provider_type type2{provider_type::s3};
|
||||
static constexpr const std::uint16_t port{0U};
|
||||
};
|
||||
|
||||
struct remote_sia final {
|
||||
static constexpr const provider_type type{provider_type::remote};
|
||||
static constexpr const provider_type type2{provider_type::sia};
|
||||
static constexpr const std::uint16_t port{0U};
|
||||
};
|
||||
|
||||
struct remote_winfsp_to_linux final {
|
||||
static constexpr const provider_type type{provider_type::remote};
|
||||
static constexpr const provider_type type2{provider_type::unknown};
|
||||
static constexpr const std::uint16_t port{40001U};
|
||||
};
|
||||
|
||||
template <typename provider_t> class winfsp_test : public ::testing::Test {
|
||||
@@ -102,7 +112,7 @@ protected:
|
||||
|
||||
auto r_cfg = config->get_remote_mount();
|
||||
r_cfg.enable = true;
|
||||
r_cfg.api_port = 30000U;
|
||||
r_cfg.api_port = 40000U;
|
||||
config->set_remote_mount(r_cfg);
|
||||
}
|
||||
|
||||
@@ -144,7 +154,7 @@ protected:
|
||||
|
||||
auto r_cfg = config->get_remote_mount();
|
||||
r_cfg.enable = true;
|
||||
r_cfg.api_port = 30000U;
|
||||
r_cfg.api_port = 40000U;
|
||||
config->set_remote_mount(r_cfg);
|
||||
}
|
||||
|
||||
@@ -160,13 +170,15 @@ protected:
|
||||
execute_mount(drive_args, mount_location);
|
||||
};
|
||||
|
||||
const auto mount_remote = [&]() {
|
||||
const auto mount_remote = [&](std::uint16_t port = 40000U) {
|
||||
{
|
||||
auto test_directory = utils::path::combine(
|
||||
test::get_test_output_dir(),
|
||||
{
|
||||
"winfsp_test",
|
||||
app_config::get_provider_name(provider_type::remote),
|
||||
app_config::get_provider_name(provider_t::type) + '_' +
|
||||
app_config::get_provider_name(provider_t::type2) + '_' +
|
||||
std::to_string(port),
|
||||
});
|
||||
|
||||
mount_location2 = mount_location;
|
||||
@@ -184,7 +196,7 @@ protected:
|
||||
"-dd",
|
||||
config->get_data_directory(),
|
||||
"-rm",
|
||||
"localhost:30000",
|
||||
fmt::format("localhost:{}", port),
|
||||
mount_location,
|
||||
});
|
||||
}
|
||||
@@ -211,6 +223,10 @@ protected:
|
||||
mount_sia();
|
||||
} break;
|
||||
|
||||
case provider_type::unknown:
|
||||
mount_remote(provider_t::port);
|
||||
return;
|
||||
|
||||
default:
|
||||
throw std::runtime_error("remote provider type is not implemented");
|
||||
return;
|
||||
@@ -228,7 +244,9 @@ protected:
|
||||
static void TearDownTestCase() {
|
||||
if (provider_t::type == provider_type::remote) {
|
||||
execute_unmount(drive_args2, mount_location);
|
||||
execute_unmount(drive_args, mount_location2);
|
||||
if (provider_t::type2 != provider_type::unknown) {
|
||||
execute_unmount(drive_args, mount_location2);
|
||||
}
|
||||
} else {
|
||||
execute_unmount(drive_args, mount_location);
|
||||
}
|
||||
@@ -245,6 +263,7 @@ protected:
|
||||
}
|
||||
|
||||
static void execute_unmount(auto args, auto location) {
|
||||
std::this_thread::sleep_for(10s);
|
||||
auto unmounted{false};
|
||||
|
||||
auto unmount_cmd =
|
||||
@@ -282,7 +301,8 @@ std::string winfsp_test<provider_t>::mount_location2;
|
||||
|
||||
// using winfsp_provider_types = ::testing::Types<local_s3, remote_s3,
|
||||
// local_sia, remote_sia>;
|
||||
using winfsp_provider_types = ::testing::Types<local_s3, remote_s3>;
|
||||
// using winfsp_provider_types = ::testing::Types<local_s3, remote_s3>;
|
||||
using winfsp_provider_types = ::testing::Types<remote_winfsp_to_linux>;
|
||||
} // namespace repertory
|
||||
|
||||
#endif // defined(_WIN32)
|
||||
|
@@ -73,6 +73,9 @@ TYPED_TEST(winfsp_test, rename_fails_if_dest_exists_and_replace_is_false) {
|
||||
auto file_path2{
|
||||
utils::path::combine(dir_path, {"test_file2_4"}),
|
||||
};
|
||||
auto file_path3{
|
||||
utils::path::combine(dir_path, {"test_file_5"}),
|
||||
};
|
||||
|
||||
ASSERT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
|
||||
|
||||
@@ -84,9 +87,16 @@ TYPED_TEST(winfsp_test, rename_fails_if_dest_exists_and_replace_is_false) {
|
||||
|
||||
EXPECT_TRUE(::MoveFileExA(file_path.c_str(), file_path2.c_str(), 0));
|
||||
|
||||
EXPECT_FALSE(::MoveFileExA(file_path.c_str(), file_path2.c_str(), 0));
|
||||
handle = ::CreateFileA(file_path3.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
::CloseHandle(handle);
|
||||
|
||||
EXPECT_FALSE(::MoveFileExA(file_path3.c_str(), file_path2.c_str(), 0));
|
||||
EXPECT_EQ(ERROR_ALREADY_EXISTS, ::GetLastError());
|
||||
|
||||
EXPECT_TRUE(::DeleteFileA(file_path3.c_str()));
|
||||
EXPECT_TRUE(::DeleteFileA(file_path2.c_str()));
|
||||
EXPECT_TRUE(::RemoveDirectoryA(dir_path.c_str()));
|
||||
}
|
||||
@@ -105,6 +115,9 @@ TYPED_TEST(winfsp_test, rename_succeeds_if_dest_exists_and_replace_is_true) {
|
||||
auto file_path2{
|
||||
utils::path::combine(dir_path, {"test_file2_4"}),
|
||||
};
|
||||
auto file_path3{
|
||||
utils::path::combine(dir_path, {"test_file_5"}),
|
||||
};
|
||||
|
||||
ASSERT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
|
||||
|
||||
@@ -116,13 +129,13 @@ TYPED_TEST(winfsp_test, rename_succeeds_if_dest_exists_and_replace_is_true) {
|
||||
|
||||
EXPECT_TRUE(::MoveFileExA(file_path.c_str(), file_path2.c_str(), 0));
|
||||
|
||||
handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
handle = ::CreateFileA(file_path3.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
::CloseHandle(handle);
|
||||
|
||||
EXPECT_TRUE(::MoveFileExA(file_path.c_str(), file_path2.c_str(),
|
||||
EXPECT_TRUE(::MoveFileExA(file_path3.c_str(), file_path2.c_str(),
|
||||
MOVEFILE_REPLACE_EXISTING));
|
||||
|
||||
EXPECT_TRUE(::DeleteFileA(file_path2.c_str()));
|
||||
|
Reference in New Issue
Block a user