initial commit
This commit is contained in:
110
src/drives/directory_cache.cpp
Normal file
110
src/drives/directory_cache.cpp
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
Copyright <2018-2022> <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 "drives/directory_cache.hpp"
|
||||
#include "drives/directory_iterator.hpp"
|
||||
#include "events/events.hpp"
|
||||
#include "events/event_system.hpp"
|
||||
#include "types/repertory.hpp"
|
||||
|
||||
namespace repertory {
|
||||
bool directory_cache::execute_action(const std::string &api_path, const execute_callback &execute) {
|
||||
auto found = false;
|
||||
recur_mutex_lock directory_lock(directory_mutex_);
|
||||
if ((found = (directory_lookup_.find(api_path) != directory_lookup_.end()))) {
|
||||
execute(*directory_lookup_[api_path].iterator);
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
void directory_cache::refresh_thread() {
|
||||
while (not is_shutdown_) {
|
||||
unique_recur_mutex_lock directory_lock(directory_mutex_);
|
||||
auto lookup = directory_lookup_;
|
||||
directory_lock.unlock();
|
||||
|
||||
for (const auto &kv : lookup) {
|
||||
if (std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() -
|
||||
kv.second.last_update) >= 120s) {
|
||||
directory_lock.lock();
|
||||
directory_lookup_.erase(kv.first);
|
||||
directory_lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
if (not is_shutdown_) {
|
||||
unique_mutex_lock shutdown_lock(shutdown_mutex_);
|
||||
if (not is_shutdown_) {
|
||||
shutdown_notify_.wait_for(shutdown_lock, 15s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
directory_iterator *directory_cache::remove_directory(const std::string &api_path) {
|
||||
directory_iterator *ret = nullptr;
|
||||
|
||||
recur_mutex_lock directory_lock(directory_mutex_);
|
||||
if (directory_lookup_.find(api_path) != directory_lookup_.end()) {
|
||||
ret = directory_lookup_[api_path].iterator;
|
||||
directory_lookup_.erase(api_path);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void directory_cache::remove_directory(directory_iterator *iterator) {
|
||||
if (iterator) {
|
||||
recur_mutex_lock directory_lock(directory_mutex_);
|
||||
const auto it = std::find_if(
|
||||
directory_lookup_.begin(), directory_lookup_.end(),
|
||||
[&iterator](const auto &kv) -> bool { return kv.second.iterator == iterator; });
|
||||
if (it != directory_lookup_.end()) {
|
||||
directory_lookup_.erase(it->first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void directory_cache::set_directory(const std::string &api_path, directory_iterator *iterator) {
|
||||
recur_mutex_lock directory_lock(directory_mutex_);
|
||||
directory_lookup_[api_path] = {iterator};
|
||||
}
|
||||
|
||||
void directory_cache::start() {
|
||||
unique_mutex_lock shutdown_lock(shutdown_mutex_);
|
||||
if (is_shutdown_) {
|
||||
is_shutdown_ = false;
|
||||
|
||||
refresh_thread_ = std::make_unique<std::thread>([this]() { this->refresh_thread(); });
|
||||
}
|
||||
}
|
||||
|
||||
void directory_cache::stop() {
|
||||
unique_mutex_lock shutdown_lock(shutdown_mutex_);
|
||||
if (not is_shutdown_) {
|
||||
event_system::instance().raise<service_shutdown>("directory_cache");
|
||||
is_shutdown_ = true;
|
||||
shutdown_notify_.notify_all();
|
||||
shutdown_lock.unlock();
|
||||
|
||||
refresh_thread_->join();
|
||||
refresh_thread_.reset();
|
||||
}
|
||||
}
|
||||
} // namespace repertory
|
129
src/drives/directory_iterator.cpp
Normal file
129
src/drives/directory_iterator.cpp
Normal file
@ -0,0 +1,129 @@
|
||||
/*
|
||||
Copyright <2018-2022> <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 "drives/directory_iterator.hpp"
|
||||
#include "utils/path_utils.hpp"
|
||||
|
||||
namespace repertory {
|
||||
#ifndef _WIN32
|
||||
int directory_iterator::fill_buffer(const remote::file_offset &offset,
|
||||
fuse_fill_dir_t filler_function, void *buffer,
|
||||
populate_stat_callback populate_stat) {
|
||||
if (offset < items_.size()) {
|
||||
std::string item_name;
|
||||
struct stat st {};
|
||||
struct stat *pst = nullptr;
|
||||
switch (offset) {
|
||||
case 0: {
|
||||
item_name = ".";
|
||||
} break;
|
||||
|
||||
case 1: {
|
||||
item_name = "..";
|
||||
} break;
|
||||
|
||||
default: {
|
||||
const auto &item = items_[offset];
|
||||
item_name = utils::path::strip_to_file_name(item.api_path);
|
||||
populate_stat(item.api_path, item.size, item.meta, item.directory, &st);
|
||||
pst = &st;
|
||||
} break;
|
||||
}
|
||||
|
||||
if (filler_function(buffer, &item_name[0], pst, offset + 1) != 0) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
errno = 120;
|
||||
return -1;
|
||||
}
|
||||
#endif // !_WIN32
|
||||
|
||||
int directory_iterator::get(const std::size_t &offset, std::string &item) {
|
||||
if (offset < items_.size()) {
|
||||
item = items_[offset].api_path;
|
||||
return 0;
|
||||
}
|
||||
|
||||
errno = 120;
|
||||
return -1;
|
||||
}
|
||||
|
||||
api_error directory_iterator::get_directory_item(const std::size_t &offset, directory_item &di) {
|
||||
if (offset < items_.size()) {
|
||||
di = items_[offset];
|
||||
return api_error::success;
|
||||
}
|
||||
|
||||
return api_error::directory_end_of_files;
|
||||
}
|
||||
|
||||
api_error directory_iterator::get_directory_item(const std::string &api_path, directory_item &di) {
|
||||
auto iter = std::find_if(items_.begin(), items_.end(),
|
||||
[&](const auto &item) -> bool { return api_path == item.api_path; });
|
||||
if (iter != items_.end()) {
|
||||
di = *iter;
|
||||
return api_error::success;
|
||||
}
|
||||
|
||||
return api_error::item_not_found;
|
||||
}
|
||||
|
||||
int directory_iterator::get_json(const std::size_t &offset, json &item) {
|
||||
if (offset < items_.size()) {
|
||||
item = items_[offset].to_json();
|
||||
return 0;
|
||||
}
|
||||
|
||||
errno = 120;
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::size_t directory_iterator::get_next_directory_offset(const std::string &api_path) const {
|
||||
const auto it = std::find_if(items_.begin(), items_.end(), [&api_path](const auto &di) -> bool {
|
||||
return api_path == di.api_path;
|
||||
});
|
||||
|
||||
return (it == items_.end()) ? 0 : std::distance(items_.begin(), it) + std::size_t(1u);
|
||||
}
|
||||
|
||||
directory_iterator &directory_iterator::operator=(const directory_iterator &iterator) noexcept {
|
||||
if (this != &iterator) {
|
||||
items_ = iterator.items_;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
directory_iterator &directory_iterator::operator=(directory_iterator &&iterator) noexcept {
|
||||
if (this != &iterator) {
|
||||
items_ = std::move(iterator.items_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
directory_iterator &directory_iterator::operator=(directory_item_list list) noexcept {
|
||||
if (&items_ != &list) {
|
||||
items_ = std::move(list);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
} // namespace repertory
|
145
src/drives/eviction.cpp
Normal file
145
src/drives/eviction.cpp
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
Copyright <2018-2022> <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 "drives/eviction.hpp"
|
||||
#include "app_config.hpp"
|
||||
#include "drives/i_open_file_table.hpp"
|
||||
#include "providers/i_provider.hpp"
|
||||
#include "types/repertory.hpp"
|
||||
#include "utils/file_utils.hpp"
|
||||
#include "utils/global_data.hpp"
|
||||
|
||||
namespace repertory {
|
||||
void eviction::check_items_thread() {
|
||||
while (not stop_requested_) {
|
||||
auto should_evict = true;
|
||||
|
||||
// Handle maximum cache size eviction
|
||||
auto used_bytes = global_data::instance().get_used_cache_space();
|
||||
if (config_.get_enable_max_cache_size()) {
|
||||
should_evict = (used_bytes > config_.get_max_cache_size_bytes());
|
||||
}
|
||||
|
||||
// Evict all items if minimum redundancy eviction is enabled; otherwise, evict
|
||||
// until required space is reclaimed.
|
||||
if (should_evict) {
|
||||
// Remove cached source files that don't meet minimum requirements
|
||||
auto cached_files_list = get_filtered_cached_files();
|
||||
if (not cached_files_list.empty()) {
|
||||
while (not stop_requested_ && should_evict && not cached_files_list.empty()) {
|
||||
std::string api_path;
|
||||
if (provider_.get_api_path_from_source(cached_files_list.front(), api_path) ==
|
||||
api_error::success) {
|
||||
std::string pinned;
|
||||
provider_.get_item_meta(api_path, META_PINNED, pinned);
|
||||
|
||||
api_file file{};
|
||||
filesystem_item fsi{};
|
||||
if ((pinned.empty() || not utils::string::to_bool(pinned)) &&
|
||||
provider_.get_filesystem_item_and_file(api_path, file, fsi) == api_error::success) {
|
||||
// Only evict files that match expected size
|
||||
std::uint64_t file_size = 0u;
|
||||
utils::file::get_file_size(cached_files_list.front(), file_size);
|
||||
if (file_size == fsi.size) {
|
||||
// Ensure minimum file redundancy has been met or source path is not being
|
||||
// used for local recovery
|
||||
const auto different_source = file.source_path != fsi.source_path;
|
||||
if (file.recoverable &&
|
||||
((file.redundancy >= config_.get_minimum_redundancy()) || different_source)) {
|
||||
// Try to evict file
|
||||
if (oft_.evict_file(fsi.api_path) && config_.get_enable_max_cache_size()) {
|
||||
// Restrict number of items evicted if maximum cache size is enabled
|
||||
used_bytes -= file_size;
|
||||
should_evict = (used_bytes > config_.get_max_cache_size_bytes());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cached_files_list.pop_front();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (not stop_requested_) {
|
||||
unique_mutex_lock l(eviction_mutex_);
|
||||
if (not stop_requested_) {
|
||||
stop_notify_.wait_for(l, 30s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool eviction::check_minimum_requirements(const std::string &file_path) {
|
||||
auto ret = false;
|
||||
// Only evict cachedFileList that are > 0
|
||||
std::uint64_t file_size = 0u;
|
||||
utils::file::get_file_size(file_path, file_size);
|
||||
if (file_size) {
|
||||
// Check modified/accessed date/time
|
||||
std::uint64_t reference_time = 0u;
|
||||
if ((ret = config_.get_eviction_uses_accessed_time()
|
||||
? utils::file::get_accessed_time(file_path, reference_time)
|
||||
: utils::file::get_modified_time(file_path, reference_time))) {
|
||||
#ifdef _WIN32
|
||||
const auto now = std::chrono::system_clock::now();
|
||||
const auto delay = std::chrono::minutes(config_.get_eviction_delay_mins());
|
||||
ret = ((std::chrono::system_clock::from_time_t(reference_time) + delay) <= now);
|
||||
#else
|
||||
const auto now = utils::get_time_now();
|
||||
const auto delay = (config_.get_eviction_delay_mins() * 60L) * NANOS_PER_SECOND;
|
||||
ret = ((reference_time + delay) <= now);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::deque<std::string> eviction::get_filtered_cached_files() {
|
||||
auto list = utils::file::get_directory_files(config_.get_cache_directory(), true);
|
||||
list.erase(std::remove_if(list.begin(), list.end(),
|
||||
[this](const std::string &path) -> bool {
|
||||
return not this->check_minimum_requirements(path);
|
||||
}),
|
||||
list.end());
|
||||
return list;
|
||||
}
|
||||
|
||||
void eviction::start() {
|
||||
mutex_lock l(start_stop_mutex_);
|
||||
if (not eviction_thread_) {
|
||||
stop_requested_ = false;
|
||||
eviction_thread_ = std::make_unique<std::thread>([this] { this->check_items_thread(); });
|
||||
}
|
||||
}
|
||||
|
||||
void eviction::stop() {
|
||||
mutex_lock l(start_stop_mutex_);
|
||||
if (eviction_thread_) {
|
||||
event_system::instance().raise<service_shutdown>("eviction");
|
||||
stop_requested_ = true;
|
||||
{
|
||||
mutex_lock l2(eviction_mutex_);
|
||||
stop_notify_.notify_all();
|
||||
}
|
||||
eviction_thread_->join();
|
||||
eviction_thread_.reset();
|
||||
}
|
||||
}
|
||||
} // namespace repertory
|
844
src/drives/fuse/fuse_base.cpp
Normal file
844
src/drives/fuse/fuse_base.cpp
Normal file
@ -0,0 +1,844 @@
|
||||
/*
|
||||
Copyright <2018-2022> <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 _WIN32
|
||||
|
||||
#include "drives/fuse/fuse_base.hpp"
|
||||
#include "app_config.hpp"
|
||||
#include "drives/fuse/events.hpp"
|
||||
#include "events/events.hpp"
|
||||
#include "events/event_system.hpp"
|
||||
#include "providers/i_provider.hpp"
|
||||
#include "utils/path_utils.hpp"
|
||||
#include "utils/string_utils.hpp"
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
namespace repertory {
|
||||
fuse_base &fuse_base::instance() {
|
||||
return *reinterpret_cast<fuse_base *>(fuse_get_context()->private_data);
|
||||
}
|
||||
|
||||
fuse_base::fuse_base(app_config &config) : config_(config) {
|
||||
#ifdef __APPLE__
|
||||
fuse_ops_.chflags = fuse_base::chflags_;
|
||||
fuse_ops_.fsetattr_x = fuse_base::fsetattr_x_;
|
||||
fuse_ops_.getxtimes = fuse_base::getxtimes_;
|
||||
fuse_ops_.setattr_x = fuse_base::setattr_x_;
|
||||
fuse_ops_.setbkuptime = fuse_base::setbkuptime_;
|
||||
fuse_ops_.setchgtime = fuse_base::setchgtime_;
|
||||
fuse_ops_.setcrtime = fuse_base::setcrtime_;
|
||||
fuse_ops_.setvolname = fuse_base::setvolname_;
|
||||
fuse_ops_.statfs_x = fuse_base::statfs_x_;
|
||||
#endif // __APPLE__
|
||||
|
||||
E_SUBSCRIBE_EXACT(unmount_requested, [this](const unmount_requested &) {
|
||||
std::thread([this]() { this->shutdown(); }).detach();
|
||||
});
|
||||
}
|
||||
|
||||
fuse_base::~fuse_base() { E_CONSUMER_RELEASE(); }
|
||||
|
||||
int fuse_base::access_(const char *path, int mask) {
|
||||
return instance().instance().execute_callback(__FUNCTION__, path,
|
||||
[&](const std::string &api_path) -> api_error {
|
||||
return instance().access_impl(api_path, mask);
|
||||
});
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
int fuse_base::chflags_(const char *path, uint32_t flags) {
|
||||
return instance().instance().execute_callback(__FUNCTION__, path,
|
||||
[&](const std::string &api_path) -> api_error {
|
||||
return instance().chflags_impl(api_path, flags);
|
||||
});
|
||||
}
|
||||
#endif // __APPLE__
|
||||
|
||||
api_error fuse_base::check_access(const std::string &api_path, int mask) const {
|
||||
api_meta_map meta;
|
||||
const auto res = get_item_meta(api_path, meta);
|
||||
if (res != api_error::success) {
|
||||
return res;
|
||||
}
|
||||
|
||||
// Always allow root
|
||||
if (fuse_get_context()->uid != 0) {
|
||||
// Always allow forced user
|
||||
if (not forced_uid_.has_value() || (fuse_get_context()->uid != get_effective_uid())) {
|
||||
// Always allow if checking file exists
|
||||
if (F_OK != mask) {
|
||||
const auto effective_uid =
|
||||
(forced_uid_.has_value() ? forced_uid_.value() : get_uid_from_meta(meta));
|
||||
const auto effective_gid =
|
||||
(forced_gid_.has_value() ? forced_gid_.value() : get_gid_from_meta(meta));
|
||||
|
||||
// Create file mode
|
||||
mode_t effective_mode = forced_umask_.has_value()
|
||||
? ((S_IRWXU | S_IRWXG | S_IRWXO) & (~forced_umask_.value()))
|
||||
: get_mode_from_meta(meta);
|
||||
|
||||
// Create access mask
|
||||
mode_t active_mask = S_IRWXO;
|
||||
if (fuse_get_context()->uid == effective_uid) {
|
||||
active_mask |= S_IRWXU;
|
||||
}
|
||||
if (fuse_get_context()->gid == effective_gid) {
|
||||
active_mask |= S_IRWXG;
|
||||
}
|
||||
if (utils::is_uid_member_of_group(fuse_get_context()->uid, effective_gid)) {
|
||||
active_mask |= S_IRWXG;
|
||||
}
|
||||
|
||||
// Calculate effective file mode
|
||||
effective_mode &= active_mask;
|
||||
|
||||
// Check allow execute
|
||||
if ((mask & X_OK) == X_OK) {
|
||||
if ((effective_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) {
|
||||
return api_error::permission_denied;
|
||||
}
|
||||
}
|
||||
|
||||
// Check allow write
|
||||
if ((mask & W_OK) == W_OK) {
|
||||
if ((effective_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0) {
|
||||
return api_error::access_denied;
|
||||
}
|
||||
}
|
||||
|
||||
// Check allow read
|
||||
if ((mask & R_OK) == R_OK) {
|
||||
if ((effective_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
|
||||
return api_error::access_denied;
|
||||
}
|
||||
}
|
||||
|
||||
if (effective_mode == 0) {
|
||||
// Deny access if effective mode is 0
|
||||
return api_error::access_denied;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return api_error::success;
|
||||
}
|
||||
|
||||
api_error fuse_base::check_and_perform(const std::string &api_path, int parent_mask,
|
||||
const std::function<api_error(api_meta_map &meta)> &action) {
|
||||
api_meta_map meta;
|
||||
auto ret = get_item_meta(api_path, meta);
|
||||
if (ret != api_error::success) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = check_parent_access(api_path, parent_mask)) != api_error::success) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = check_owner(meta)) != api_error::success) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return action(meta);
|
||||
}
|
||||
|
||||
api_error fuse_base::check_open_flags(const int &flags, const int &mask,
|
||||
const api_error &fail_error) {
|
||||
return ((flags & mask) ? fail_error : api_error::success);
|
||||
}
|
||||
|
||||
api_error fuse_base::check_owner(const api_meta_map &meta) const {
|
||||
// Always allow root
|
||||
if ((fuse_get_context()->uid != 0) &&
|
||||
// Always allow forced UID
|
||||
(not forced_uid_.has_value() || (fuse_get_context()->uid != get_effective_uid())) &&
|
||||
// Check if current uid matches file uid
|
||||
(get_uid_from_meta(meta) != get_effective_uid())) {
|
||||
return api_error::permission_denied;
|
||||
}
|
||||
|
||||
return api_error::success;
|
||||
}
|
||||
|
||||
api_error fuse_base::check_parent_access(const std::string &api_path, int mask) const {
|
||||
auto ret = api_error::success;
|
||||
|
||||
// Ignore root
|
||||
if (api_path != "/") {
|
||||
if ((mask & X_OK) == X_OK) {
|
||||
for (auto parent = utils::path::get_parent_directory(api_path);
|
||||
(ret == api_error::success) && not parent.empty();
|
||||
parent = utils::path::get_parent_directory(parent)) {
|
||||
if (((ret = check_access(parent, X_OK)) == api_error::success) && (parent == "/")) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == api_error::success) {
|
||||
mask &= (~X_OK);
|
||||
if (mask != 0) {
|
||||
ret = check_access(utils::path::get_parent_directory(api_path), mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
api_error fuse_base::check_readable(const int &flags, const api_error &fail_error) {
|
||||
const auto mode = (flags & O_ACCMODE);
|
||||
return ((mode == O_WRONLY) ? fail_error : api_error::success);
|
||||
}
|
||||
|
||||
api_error fuse_base::check_writeable(const int &flags, const api_error &fail_error) {
|
||||
return ((flags & O_ACCMODE) ? api_error::success : fail_error);
|
||||
}
|
||||
|
||||
int fuse_base::chmod_(const char *path, mode_t mode) {
|
||||
return instance().execute_callback(__FUNCTION__, path,
|
||||
[&](const std::string &api_path) -> api_error {
|
||||
return instance().chmod_impl(api_path, mode);
|
||||
});
|
||||
}
|
||||
|
||||
int fuse_base::chown_(const char *path, uid_t uid, gid_t gid) {
|
||||
return instance().execute_callback(__FUNCTION__, path,
|
||||
[&](const std::string &api_path) -> api_error {
|
||||
return instance().chown_impl(api_path, uid, gid);
|
||||
});
|
||||
}
|
||||
|
||||
int fuse_base::create_(const char *path, mode_t mode, struct fuse_file_info *fi) {
|
||||
return instance().execute_callback(__FUNCTION__, path,
|
||||
[&](const std::string &api_path) -> api_error {
|
||||
return instance().create_impl(api_path, mode, fi);
|
||||
});
|
||||
}
|
||||
|
||||
void fuse_base::destroy_(void *ptr) {
|
||||
execute_void_callback(__FUNCTION__, [&]() { instance().destroy_impl(ptr); });
|
||||
}
|
||||
|
||||
void fuse_base::display_options(int argc, char *argv[]) {
|
||||
struct fuse_operations fuse_ops {};
|
||||
fuse_main(argc, argv, &fuse_ops, nullptr);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
void fuse_base::display_version_information(int argc, char *argv[]) {
|
||||
struct fuse_operations fuse_ops {};
|
||||
fuse_main(argc, argv, &fuse_ops, nullptr);
|
||||
}
|
||||
|
||||
int fuse_base::execute_callback(const std::string &function_name, const char *from, const char *to,
|
||||
const std::function<api_error(const std::string &from_api_file,
|
||||
const std::string &to_api_path)> &cb,
|
||||
const bool &disable_logging) {
|
||||
const auto from_api_file = utils::path::create_api_path(from ? from : "");
|
||||
const auto to_api_file = utils::path::create_api_path(to ? to : "");
|
||||
const auto res = utils::translate_api_error(cb(from_api_file, to_api_file));
|
||||
raise_fuse_event(function_name, "from|" + from_api_file + "|to|" + to_api_file, res,
|
||||
disable_logging);
|
||||
return res;
|
||||
}
|
||||
|
||||
int fuse_base::execute_callback(const std::string &function_name, const char *path,
|
||||
const std::function<api_error(const std::string &api_path)> &cb,
|
||||
const bool &disable_logging) {
|
||||
const auto api_path = utils::path::create_api_path(path ? path : "");
|
||||
const auto res = utils::translate_api_error(cb(api_path));
|
||||
raise_fuse_event(function_name, api_path, res, disable_logging);
|
||||
return res;
|
||||
}
|
||||
|
||||
void fuse_base::execute_void_callback(const std::string &function_name,
|
||||
const std::function<void()> &cb) {
|
||||
cb();
|
||||
|
||||
instance().raise_fuse_event(function_name, "", 0, false);
|
||||
}
|
||||
|
||||
void *fuse_base::execute_void_pointer_callback(const std::string &function_name,
|
||||
const std::function<void *()> &cb) {
|
||||
auto *ret = cb();
|
||||
|
||||
instance().raise_fuse_event(function_name, "", 0, false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int fuse_base::fallocate_(const char *path, int mode, off_t offset, off_t length,
|
||||
struct fuse_file_info *fi) {
|
||||
return instance().execute_callback(
|
||||
__FUNCTION__, path, [&](const std::string &api_path) -> api_error {
|
||||
return instance().fallocate_impl(api_path, mode, offset, length, fi);
|
||||
});
|
||||
}
|
||||
|
||||
int fuse_base::fgetattr_(const char *path, struct stat *st, struct fuse_file_info *fi) {
|
||||
return instance().execute_callback(__FUNCTION__, path,
|
||||
[&](const std::string &api_path) -> api_error {
|
||||
return instance().fgetattr_impl(api_path, st, fi);
|
||||
});
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
int fuse_base::fsetattr_x_(const char *path, struct setattr_x *attr, struct fuse_file_info *fi) {
|
||||
return instance().execute_callback(__FUNCTION__, path,
|
||||
[&](const std::string &api_path) -> api_error {
|
||||
return instance().fsetattr_x_impl(api_path, attr, fi);
|
||||
});
|
||||
}
|
||||
#endif // __APPLE__
|
||||
|
||||
int fuse_base::fsync_(const char *path, int datasync, struct fuse_file_info *fi) {
|
||||
return instance().execute_callback(__FUNCTION__, path,
|
||||
[&](const std::string &api_path) -> api_error {
|
||||
return instance().fsync_impl(api_path, datasync, fi);
|
||||
});
|
||||
}
|
||||
|
||||
int fuse_base::ftruncate_(const char *path, off_t size, struct fuse_file_info *fi) {
|
||||
return instance().execute_callback(__FUNCTION__, path,
|
||||
[&](const std::string &api_path) -> api_error {
|
||||
return instance().ftruncate_impl(api_path, size, fi);
|
||||
});
|
||||
}
|
||||
|
||||
uid_t fuse_base::get_effective_uid() const {
|
||||
return forced_uid_.has_value() ? forced_uid_.value() : fuse_get_context()->uid;
|
||||
}
|
||||
|
||||
uid_t fuse_base::get_effective_gid() const {
|
||||
return forced_gid_.has_value() ? forced_gid_.value() : fuse_get_context()->gid;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
__uint32_t fuse_base::get_flags_from_meta(const api_meta_map &meta) {
|
||||
return utils::string::to_uint32(meta.at(META_OSXFLAGS));
|
||||
}
|
||||
#endif // __APPLE__
|
||||
|
||||
gid_t fuse_base::get_gid_from_meta(const api_meta_map &meta) {
|
||||
return static_cast<gid_t>(utils::string::to_uint32(meta.at(META_GID)));
|
||||
}
|
||||
|
||||
mode_t fuse_base::get_mode_from_meta(const api_meta_map &meta) {
|
||||
return static_cast<mode_t>(utils::string::to_uint32(meta.at(META_MODE)));
|
||||
}
|
||||
|
||||
void fuse_base::get_timespec_from_meta(const api_meta_map &meta, const std::string &name,
|
||||
struct timespec &ts) {
|
||||
const auto t = utils::string::to_uint64(meta.at(name));
|
||||
ts.tv_nsec = t % NANOS_PER_SECOND;
|
||||
ts.tv_sec = t / NANOS_PER_SECOND;
|
||||
}
|
||||
|
||||
uid_t fuse_base::get_uid_from_meta(const api_meta_map &meta) {
|
||||
return static_cast<uid_t>(utils::string::to_uint32(meta.at(META_UID)));
|
||||
}
|
||||
|
||||
int fuse_base::getattr_(const char *path, struct stat *st) {
|
||||
return instance().execute_callback(__FUNCTION__, path,
|
||||
[&](const std::string &api_path) -> api_error {
|
||||
return instance().getattr_impl(api_path, st);
|
||||
});
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
int fuse_base::getxtimes_(const char *path, struct timespec *bkuptime, struct timespec *crtime) {
|
||||
return instance().execute_callback(__FUNCTION__, path,
|
||||
[&](const std::string &api_path) -> api_error {
|
||||
return instance().getxtimes_impl(api_path, bkuptime, crtime);
|
||||
});
|
||||
}
|
||||
#endif // __APPLE__
|
||||
|
||||
void *fuse_base::init_(struct fuse_conn_info *conn) {
|
||||
return execute_void_pointer_callback(__FUNCTION__,
|
||||
[&]() -> void * { return instance().init_impl(conn); });
|
||||
}
|
||||
|
||||
void *fuse_base::init_impl(struct fuse_conn_info *conn) {
|
||||
#ifdef __APPLE__
|
||||
conn->want |= FUSE_CAP_VOL_RENAME;
|
||||
conn->want |= FUSE_CAP_XTIMES;
|
||||
#endif // __APPLE__
|
||||
conn->want |= FUSE_CAP_BIG_WRITES;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
int fuse_base::mkdir_(const char *path, mode_t mode) {
|
||||
return instance().execute_callback(__FUNCTION__, path,
|
||||
[&](const std::string &api_path) -> api_error {
|
||||
return instance().mkdir_impl(api_path, mode);
|
||||
});
|
||||
}
|
||||
|
||||
int fuse_base::mount(std::vector<std::string> args) {
|
||||
auto ret = parse_args(args);
|
||||
if (ret == 0) {
|
||||
std::vector<const char *> fuse_argv(args.size());
|
||||
for (std::size_t i = 0u; i < args.size(); i++) {
|
||||
fuse_argv[i] = args[i].c_str();
|
||||
}
|
||||
|
||||
{
|
||||
struct fuse_args fa =
|
||||
FUSE_ARGS_INIT(static_cast<int>(fuse_argv.size()), (char **)&fuse_argv[0]);
|
||||
|
||||
char *mount_location = nullptr;
|
||||
fuse_parse_cmdline(&fa, &mount_location, nullptr, nullptr);
|
||||
if (mount_location) {
|
||||
mount_location_ = mount_location;
|
||||
free(mount_location);
|
||||
}
|
||||
}
|
||||
|
||||
notify_fuse_args_parsed(args);
|
||||
|
||||
umask(0);
|
||||
ret = fuse_main(static_cast<int>(fuse_argv.size()), (char **)&fuse_argv[0], &fuse_ops_, this);
|
||||
notify_fuse_main_exit(ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int fuse_base::open_(const char *path, struct fuse_file_info *fi) {
|
||||
return instance().execute_callback(
|
||||
__FUNCTION__, path,
|
||||
[&](const std::string &api_path) -> api_error { return instance().open_impl(api_path, fi); });
|
||||
}
|
||||
|
||||
int fuse_base::opendir_(const char *path, struct fuse_file_info *fi) {
|
||||
return instance().execute_callback(__FUNCTION__, path,
|
||||
[&](const std::string &api_path) -> api_error {
|
||||
return instance().opendir_impl(api_path, fi);
|
||||
});
|
||||
}
|
||||
|
||||
int fuse_base::read_(const char *path, char *buffer, size_t read_size, off_t read_offset,
|
||||
struct fuse_file_info *fi) {
|
||||
std::size_t bytes_read{};
|
||||
const auto res = instance().execute_callback(
|
||||
__FUNCTION__, path,
|
||||
[&](const std::string &api_path) -> api_error {
|
||||
return instance().read_impl(api_path, buffer, read_size, read_offset, fi, bytes_read);
|
||||
},
|
||||
true);
|
||||
return (res == 0) ? static_cast<int>(bytes_read) : res;
|
||||
}
|
||||
|
||||
int fuse_base::readdir_(const char *path, void *buf, fuse_fill_dir_t fuse_fill_dir, off_t offset,
|
||||
struct fuse_file_info *fi) {
|
||||
return instance().execute_callback(
|
||||
__FUNCTION__, path, [&](const std::string &api_path) -> api_error {
|
||||
return instance().readdir_impl(api_path, buf, fuse_fill_dir, offset, fi);
|
||||
});
|
||||
}
|
||||
|
||||
int fuse_base::release_(const char *path, struct fuse_file_info *fi) {
|
||||
return instance().execute_callback(__FUNCTION__, path,
|
||||
[&](const std::string &api_path) -> api_error {
|
||||
return instance().release_impl(api_path, fi);
|
||||
});
|
||||
}
|
||||
|
||||
int fuse_base::releasedir_(const char *path, struct fuse_file_info *fi) {
|
||||
return instance().execute_callback(__FUNCTION__, path,
|
||||
[&](const std::string &api_path) -> api_error {
|
||||
return instance().releasedir_impl(api_path, fi);
|
||||
});
|
||||
}
|
||||
|
||||
int fuse_base::rename_(const char *from, const char *to) {
|
||||
return instance().execute_callback(
|
||||
__FUNCTION__, from, to,
|
||||
[&](const std::string &from_api_file, const std::string &to_api_path) -> api_error {
|
||||
return instance().rename_impl(from_api_file, to_api_path);
|
||||
});
|
||||
}
|
||||
|
||||
int fuse_base::rmdir_(const char *path) {
|
||||
return instance().execute_callback(
|
||||
__FUNCTION__, path,
|
||||
[&](const std::string &api_path) -> api_error { return instance().rmdir_impl(api_path); });
|
||||
}
|
||||
|
||||
#ifdef HAS_SETXATTR
|
||||
#ifdef __APPLE__
|
||||
int fuse_base::getxattr_(const char *path, const char *name, char *value, size_t size,
|
||||
uint32_t position) {
|
||||
int attribute_size = 0;
|
||||
const auto res = instance().execute_callback(
|
||||
__FUNCTION__, path, [&](const std::string &api_path) -> api_error {
|
||||
return instance().getxattr_impl(api_path, name, value, size, position, attribute_size);
|
||||
});
|
||||
|
||||
return res == 0 ? attribute_size : res;
|
||||
}
|
||||
#else // __APPLE__
|
||||
int fuse_base::getxattr_(const char *path, const char *name, char *value, size_t size) {
|
||||
int attribute_size = 0;
|
||||
const auto res = instance().execute_callback(
|
||||
__FUNCTION__, path, [&](const std::string &api_path) -> api_error {
|
||||
return instance().getxattr_impl(api_path, name, value, size, attribute_size);
|
||||
});
|
||||
|
||||
return res == 0 ? attribute_size : res;
|
||||
}
|
||||
#endif // __APPLE__
|
||||
|
||||
int fuse_base::listxattr_(const char *path, char *buffer, size_t size) {
|
||||
int required_size = 0;
|
||||
bool return_size = false;
|
||||
|
||||
const auto res = instance().execute_callback(
|
||||
__FUNCTION__, path, [&](const std::string &api_path) -> api_error {
|
||||
return instance().listxattr_impl(api_path, buffer, size, required_size, return_size);
|
||||
});
|
||||
|
||||
return return_size ? required_size : res;
|
||||
}
|
||||
|
||||
int fuse_base::parse_args(std::vector<std::string> &args) {
|
||||
auto force_no_console = false;
|
||||
for (std::size_t i = 1; !force_no_console && (i < args.size()); i++) {
|
||||
if (args[i] == "-nc") {
|
||||
force_no_console = true;
|
||||
}
|
||||
}
|
||||
utils::remove_element_from(args, "-nc");
|
||||
|
||||
for (std::size_t i = 1; i < args.size(); i++) {
|
||||
if (args[i] == "-f") {
|
||||
console_enabled_ = not force_no_console;
|
||||
} else if (args[i].find("-o") == 0) {
|
||||
std::string options = "";
|
||||
if (args[i].size() == 2) {
|
||||
if ((i + 1) < args.size()) {
|
||||
options = args[++i];
|
||||
}
|
||||
} else {
|
||||
options = args[i].substr(2);
|
||||
}
|
||||
|
||||
const auto option_parts = utils::string::split(options, ',');
|
||||
for (const auto &option : option_parts) {
|
||||
if (option.find("gid") == 0) {
|
||||
const auto parts = utils::string::split(option, '=');
|
||||
if (parts.size() == 2) {
|
||||
auto gid = getgrnam(parts[1].c_str());
|
||||
if (not gid) {
|
||||
gid = getgrgid(utils::string::to_uint32(parts[1]));
|
||||
}
|
||||
if ((getgid() != 0) && (gid->gr_gid == 0)) {
|
||||
std::cerr << "'gid=0' requires running as root" << std::endl;
|
||||
return -1;
|
||||
} else {
|
||||
forced_gid_ = gid->gr_gid;
|
||||
}
|
||||
}
|
||||
} else if (option.find("noatime") == 0) {
|
||||
atime_enabled_ = false;
|
||||
} else if (option.find("uid") == 0) {
|
||||
const auto parts = utils::string::split(option, '=');
|
||||
if (parts.size() == 2) {
|
||||
auto uid = getpwnam(parts[1].c_str());
|
||||
if (not uid) {
|
||||
uid = getpwuid(utils::string::to_uint32(parts[1]));
|
||||
}
|
||||
if ((getuid() != 0) && (uid->pw_uid == 0)) {
|
||||
std::cerr << "'uid=0' requires running as root" << std::endl;
|
||||
return -1;
|
||||
} else {
|
||||
forced_uid_ = uid->pw_uid;
|
||||
}
|
||||
}
|
||||
} else if (option.find("umask") == 0) {
|
||||
const auto parts = utils::string::split(option, '=');
|
||||
if (parts.size() == 2) {
|
||||
static const auto match_number_regex = std::regex("[0-9]+");
|
||||
try {
|
||||
if (not std::regex_match(parts[1], match_number_regex)) {
|
||||
throw std::runtime_error("invalid syntax");
|
||||
} else {
|
||||
forced_umask_ = utils::string::to_uint32(parts[1]);
|
||||
}
|
||||
} catch (...) {
|
||||
std::cerr << ("'" + option + "' invalid syntax") << std::endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
api_error fuse_base::parse_xattr_parameters(const char *name, const uint32_t &position,
|
||||
std::string &attribute_name,
|
||||
const std::string &api_path) {
|
||||
#else
|
||||
api_error fuse_base::parse_xattr_parameters(const char *name, std::string &attribute_name,
|
||||
const std::string &api_path) {
|
||||
#endif
|
||||
auto res = api_path.empty() ? api_error::bad_address : api_error::success;
|
||||
if (res != api_error::success) {
|
||||
return res;
|
||||
}
|
||||
|
||||
if (not name) {
|
||||
return api_error::bad_address;
|
||||
}
|
||||
|
||||
attribute_name = std::string(name);
|
||||
#ifdef __APPLE__
|
||||
if (attribute_name == A_KAUTH_FILESEC_XATTR) {
|
||||
char new_name[MAXPATHLEN] = {0};
|
||||
memcpy(new_name, A_KAUTH_FILESEC_XATTR, sizeof(A_KAUTH_FILESEC_XATTR));
|
||||
memcpy(new_name, G_PREFIX, sizeof(G_PREFIX) - 1);
|
||||
attribute_name = new_name;
|
||||
} else if (attribute_name.empty() ||
|
||||
((attribute_name != XATTR_RESOURCEFORK_NAME) && (position != 0))) {
|
||||
return api_error::xattr_osx_invalid;
|
||||
}
|
||||
#endif
|
||||
|
||||
return api_error::success;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
api_error fuse_base::parse_xattr_parameters(const char *name, const char *value, size_t size,
|
||||
const uint32_t &position, std::string &attribute_name,
|
||||
const std::string &api_path) {
|
||||
auto res = parse_xattr_parameters(name, position, attribute_name, api_path);
|
||||
#else
|
||||
api_error fuse_base::parse_xattr_parameters(const char *name, const char *value, size_t size,
|
||||
std::string &attribute_name,
|
||||
const std::string &api_path) {
|
||||
auto res = parse_xattr_parameters(name, attribute_name, api_path);
|
||||
#endif
|
||||
if (res != api_error::success) {
|
||||
return res;
|
||||
}
|
||||
|
||||
return (value ? api_error::success : (size ? api_error::bad_address : api_error::success));
|
||||
}
|
||||
|
||||
void fuse_base::populate_stat(const std::string &api_path, const std::uint64_t &size_or_count,
|
||||
const api_meta_map &meta, const bool &directory, i_provider &provider,
|
||||
struct stat *st) {
|
||||
memset(st, 0, sizeof(struct stat));
|
||||
st->st_nlink =
|
||||
(directory ? 2 + (size_or_count ? size_or_count : provider.get_directory_item_count(api_path))
|
||||
: 1);
|
||||
if (directory) {
|
||||
st->st_blocks = 0;
|
||||
} else {
|
||||
st->st_size = size_or_count;
|
||||
static const auto blockSizeStat = static_cast<std::uint64_t>(512u);
|
||||
static const auto blockSize = static_cast<std::uint64_t>(4096u);
|
||||
const auto size =
|
||||
utils::divide_with_ceiling(static_cast<std::uint64_t>(st->st_size), blockSize) * blockSize;
|
||||
st->st_blocks =
|
||||
std::max(blockSize / blockSizeStat, utils::divide_with_ceiling(size, blockSizeStat));
|
||||
}
|
||||
st->st_gid = get_gid_from_meta(meta);
|
||||
st->st_mode = (directory ? S_IFDIR : S_IFREG) | get_mode_from_meta(meta);
|
||||
st->st_uid = get_uid_from_meta(meta);
|
||||
#ifdef __APPLE__
|
||||
st->st_blksize = 0;
|
||||
st->st_flags = get_flags_from_meta(meta);
|
||||
|
||||
set_timespec_from_meta(meta, META_MODIFIED, st->st_mtimespec);
|
||||
set_timespec_from_meta(meta, META_CREATION, st->st_birthtimespec);
|
||||
set_timespec_from_meta(meta, META_CHANGED, st->st_ctimespec);
|
||||
set_timespec_from_meta(meta, META_ACCESSED, st->st_atimespec);
|
||||
#else // __APPLE__
|
||||
st->st_blksize = 4096;
|
||||
|
||||
set_timespec_from_meta(meta, META_MODIFIED, st->st_mtim);
|
||||
set_timespec_from_meta(meta, META_CREATION, st->st_ctim);
|
||||
set_timespec_from_meta(meta, META_ACCESSED, st->st_atim);
|
||||
#endif // __APPLE__
|
||||
}
|
||||
|
||||
void fuse_base::raise_fuse_event(std::string function_name, const std::string &api_path,
|
||||
const int &ret, const bool &disable_logging) {
|
||||
if ((ret >= 0) && disable_logging) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (not config_.get_enable_drive_events()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (((config_.get_event_level() >= fuse_event::level) && (ret != 0)) ||
|
||||
(config_.get_event_level() >= event_level::verbose)) {
|
||||
event_system::instance().raise<fuse_event>(utils::string::right_trim(function_name, '_'),
|
||||
api_path, ret);
|
||||
}
|
||||
}
|
||||
|
||||
int fuse_base::removexattr_(const char *path, const char *name) {
|
||||
return instance().execute_callback(__FUNCTION__, path,
|
||||
[&](const std::string &api_path) -> api_error {
|
||||
return instance().removexattr_impl(api_path, name);
|
||||
});
|
||||
}
|
||||
|
||||
void fuse_base::set_timespec_from_meta(const api_meta_map &meta, const std::string &name,
|
||||
struct timespec &ts) {
|
||||
const auto t = utils::string::to_uint64(meta.at(name));
|
||||
ts.tv_nsec = t % NANOS_PER_SECOND;
|
||||
ts.tv_sec = t / NANOS_PER_SECOND;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
int fuse_base::setxattr_(const char *path, const char *name, const char *value, size_t size,
|
||||
int flags, uint32_t position) {
|
||||
const auto res = instance().execute_callback(
|
||||
__FUNCTION__, path, [&](const std::string &api_path) -> api_error {
|
||||
return instance().setxattr_impl(api_path, name, value, size, flags, position);
|
||||
});
|
||||
if (res != 0) {
|
||||
errno = std::abs(res);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
#else // __APPLE__
|
||||
int fuse_base::setxattr_(const char *path, const char *name, const char *value, size_t size,
|
||||
int flags) {
|
||||
const auto res = instance().execute_callback(
|
||||
__FUNCTION__, path, [&](const std::string &api_path) -> api_error {
|
||||
return instance().setxattr_impl(api_path, name, value, size, flags);
|
||||
});
|
||||
if (res != 0) {
|
||||
errno = std::abs(res);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
#endif // __APPLE__
|
||||
#endif // HAS_SETXATTR
|
||||
|
||||
int fuse_base::shutdown() {
|
||||
#if __APPLE__
|
||||
const auto unmount = "umount \"" + mount_location_ + "\" >/dev/null 2>&1";
|
||||
#else
|
||||
const auto unmount = "fusermount -u \"" + mount_location_ + "\" >/dev/null 2>&1";
|
||||
#endif
|
||||
|
||||
return system(unmount.c_str());
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
int fuse_base::setattr_x_(const char *path, struct setattr_x *attr) {
|
||||
return instance().execute_callback(__FUNCTION__, path,
|
||||
[&](const std::string &api_path) -> api_error {
|
||||
return instance().setattr_x_impl(api_path, attr);
|
||||
});
|
||||
}
|
||||
|
||||
int fuse_base::setbkuptime_(const char *path, const struct timespec *bkuptime) {
|
||||
return instance().execute_callback(__FUNCTION__, path,
|
||||
[&](const std::string &api_path) -> api_error {
|
||||
return instance().setbkuptime_impl(api_path, bkuptime);
|
||||
});
|
||||
}
|
||||
|
||||
int fuse_base::setchgtime_(const char *path, const struct timespec *chgtime) {
|
||||
return instance().execute_callback(__FUNCTION__, path,
|
||||
[&](const std::string &api_path) -> api_error {
|
||||
return instance().setchgtime_impl(api_path, chgtime);
|
||||
});
|
||||
}
|
||||
|
||||
int fuse_base::setcrtime_(const char *path, const struct timespec *crtime) {
|
||||
return instance().execute_callback(__FUNCTION__, path,
|
||||
[&](const std::string &api_path) -> api_error {
|
||||
return instance().setcrtime_impl(api_path, crtime);
|
||||
});
|
||||
}
|
||||
|
||||
int fuse_base::setvolname_(const char *volname) {
|
||||
return instance().execute_callback(__FUNCTION__, volname,
|
||||
[&](const std::string &api_path) -> api_error {
|
||||
return instance().setvolname_impl(volname);
|
||||
});
|
||||
}
|
||||
|
||||
int fuse_base::statfs_x_(const char *path, struct statfs *stbuf) {
|
||||
return instance().execute_callback(__FUNCTION__, path,
|
||||
[&](const std::string &api_path) -> api_error {
|
||||
return instance().statfs_x_impl(api_path, stbuf);
|
||||
});
|
||||
}
|
||||
#else // __APPLE__
|
||||
int fuse_base::statfs_(const char *path, struct statvfs *stbuf) {
|
||||
return instance().execute_callback(__FUNCTION__, path,
|
||||
[&](const std::string &api_path) -> api_error {
|
||||
return instance().statfs_impl(api_path, stbuf);
|
||||
});
|
||||
}
|
||||
#endif // __APPLE__
|
||||
|
||||
int fuse_base::truncate_(const char *path, off_t size) {
|
||||
return instance().execute_callback(__FUNCTION__, path,
|
||||
[&](const std::string &api_path) -> api_error {
|
||||
return instance().truncate_impl(api_path, size);
|
||||
});
|
||||
}
|
||||
|
||||
int fuse_base::unlink_(const char *path) {
|
||||
return instance().execute_callback(
|
||||
__FUNCTION__, path,
|
||||
[&](const std::string &api_path) -> api_error { return instance().unlink_impl(api_path); });
|
||||
}
|
||||
|
||||
int fuse_base::utimens_(const char *path, const struct timespec tv[2]) {
|
||||
return instance().execute_callback(__FUNCTION__, path,
|
||||
[&](const std::string &api_path) -> api_error {
|
||||
return instance().utimens_impl(api_path, tv);
|
||||
});
|
||||
}
|
||||
|
||||
int fuse_base::write_(const char *path, const char *buffer, size_t write_size, off_t write_offset,
|
||||
struct fuse_file_info *fi) {
|
||||
std::size_t bytes_written{};
|
||||
|
||||
const auto res = instance().execute_callback(
|
||||
__FUNCTION__, path,
|
||||
[&](const std::string &api_path) -> api_error {
|
||||
return instance().write_impl(api_path, buffer, write_size, write_offset, fi, bytes_written);
|
||||
},
|
||||
true);
|
||||
return (res == 0) ? static_cast<int>(bytes_written) : res;
|
||||
}
|
||||
} // namespace repertory
|
||||
|
||||
#endif // _WIN32
|
1182
src/drives/fuse/fuse_drive.cpp
Normal file
1182
src/drives/fuse/fuse_drive.cpp
Normal file
File diff suppressed because it is too large
Load Diff
716
src/drives/fuse/remotefuse/remote_client.cpp
Normal file
716
src/drives/fuse/remotefuse/remote_client.cpp
Normal file
@ -0,0 +1,716 @@
|
||||
/*
|
||||
Copyright <2018-2022> <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 "drives/fuse/remotefuse/remote_client.hpp"
|
||||
#include "comm/packet/packet.hpp"
|
||||
#include "app_config.hpp"
|
||||
#include "events/events.hpp"
|
||||
#include "utils/path_utils.hpp"
|
||||
|
||||
namespace repertory::remote_fuse {
|
||||
// clang-format off
|
||||
E_SIMPLE3(remote_fuse_client_event, debug, true,
|
||||
std::string, function, func, E_STRING,
|
||||
std::string, api_path, ap, E_STRING,
|
||||
int, result, res, E_FROM_INT32
|
||||
);
|
||||
// clang-format on
|
||||
|
||||
#define RAISE_REMOTE_CLIENT_FUSE_EVENT(func, file, ret) \
|
||||
if (config_.get_enable_drive_events() && \
|
||||
(((config_.get_event_level() >= remote_fuse_client_event::level) && (ret != 0)) || \
|
||||
(config_.get_event_level() >= event_level::verbose))) \
|
||||
event_system::instance().raise<remote_fuse_client_event>(func, file, ret)
|
||||
|
||||
remote_client::remote_client(const app_config &config)
|
||||
: config_(config),
|
||||
packet_client_(config.get_remote_host_name_or_ip(), config.get_remote_max_connections(),
|
||||
config.get_remote_port(), config.get_remote_receive_timeout_secs(),
|
||||
config.get_remote_send_timeout_secs(), config.get_remote_token()) {}
|
||||
|
||||
packet::error_type remote_client::fuse_access(const char *path, const std::int32_t &mask) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
request.encode(mask);
|
||||
|
||||
std::uint32_t service_flags = 0;
|
||||
const auto ret = packet_client_.send(__FUNCTION__, request, service_flags);
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::fuse_chflags(const char *path, const std::uint32_t &flags) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
request.encode(flags);
|
||||
|
||||
std::uint32_t service_flags = 0;
|
||||
const auto ret = packet_client_.send(__FUNCTION__, request, service_flags);
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::fuse_chmod(const char *path, const remote::file_mode &mode) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
request.encode(mode);
|
||||
|
||||
std::uint32_t service_flags = 0;
|
||||
const auto ret = packet_client_.send(__FUNCTION__, request, service_flags);
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::fuse_chown(const char *path, const remote::user_id &uid,
|
||||
const remote::group_id &gid) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
request.encode(uid);
|
||||
request.encode(gid);
|
||||
|
||||
std::uint32_t service_flags = 0;
|
||||
const auto ret = packet_client_.send(__FUNCTION__, request, service_flags);
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::fuse_destroy() {
|
||||
std::uint32_t service_flags = 0;
|
||||
const auto ret = packet_client_.send(__FUNCTION__, service_flags);
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, "", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*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;
|
||||
request.encode(path);
|
||||
request.encode(mode);
|
||||
request.encode(offset);
|
||||
request.encode(length);
|
||||
request.encode(handle);
|
||||
|
||||
std::uint32_t service_flags = 0;
|
||||
const auto ret = packetClient_.send(__FUNCTION__, request, service_flags);
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}*/
|
||||
|
||||
packet::error_type remote_client::fuse_fgetattr(const char *path, remote::stat &st, bool &directory,
|
||||
const remote::file_handle &handle) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
request.encode(handle);
|
||||
request.encode(uid_);
|
||||
request.encode(gid_);
|
||||
|
||||
packet response;
|
||||
std::uint32_t service_flags = 0;
|
||||
auto ret = packet_client_.send(__FUNCTION__, request, response, service_flags);
|
||||
if (ret == 0) {
|
||||
if ((ret = response.decode(st)) == 0) {
|
||||
std::uint8_t d = 0;
|
||||
if ((ret = response.decode(d)) == 0) {
|
||||
directory = static_cast<bool>(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::fuse_fsetattr_x(const char *path, const remote::setattr_x &attr,
|
||||
const remote::file_handle &handle) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
request.encode(attr);
|
||||
request.encode(handle);
|
||||
|
||||
std::uint32_t service_flags = 0;
|
||||
const auto ret = packet_client_.send(__FUNCTION__, request, service_flags);
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::fuse_fsync(const char *path, const std::int32_t &datasync,
|
||||
const remote::file_handle &handle) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
request.encode(datasync);
|
||||
request.encode(handle);
|
||||
|
||||
std::uint32_t service_flags = 0;
|
||||
const auto ret = packet_client_.send(__FUNCTION__, request, service_flags);
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::fuse_ftruncate(const char *path, const remote::file_offset &size,
|
||||
const remote::file_handle &handle) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
request.encode(size);
|
||||
request.encode(handle);
|
||||
|
||||
std::uint32_t service_flags = 0;
|
||||
const auto ret = packet_client_.send(__FUNCTION__, request, service_flags);
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::fuse_getattr(const char *path, remote::stat &st,
|
||||
bool &directory) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
request.encode(uid_);
|
||||
request.encode(gid_);
|
||||
|
||||
packet response;
|
||||
std::uint32_t service_flags = 0;
|
||||
auto ret = packet_client_.send(__FUNCTION__, request, response, service_flags);
|
||||
if (ret == 0) {
|
||||
if ((ret = response.decode(st)) == 0) {
|
||||
std::uint8_t d = 0;
|
||||
if ((ret = response.decode(d)) == 0) {
|
||||
directory = static_cast<bool>(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*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; if (size >
|
||||
std::numeric_limits<std::size_t>::max()) { ret = -ERANGE; } else { packet request;
|
||||
request.encode(path);
|
||||
request.encode(name);
|
||||
request.encode(size);
|
||||
|
||||
packet response;
|
||||
std::uint32_t service_flags = 0;
|
||||
if ((ret = packetClient_.send(__FUNCTION__, request, response, service_flags)) == 0) {
|
||||
remote::file_size size2;
|
||||
if ((ret = response.decode(size2)) == 0) {
|
||||
if (size2 > std::numeric_limits<std::size_t>::max()) {
|
||||
ret = -ERANGE;
|
||||
} else {
|
||||
memcpy(value, response.CurrentPointer(), static_cast<std::size_t>(size2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::fuse_getxattr_osx(const char *path, const char *name, char
|
||||
*value, const remote::file_size &size, const 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);
|
||||
request.encode(size);
|
||||
request.encode(position);
|
||||
|
||||
packet response;
|
||||
std::uint32_t service_flags = 0;
|
||||
if ((ret = packetClient_.send(__FUNCTION__, request, response, service_flags)) == 0) {
|
||||
remote::file_size size2;
|
||||
if ((ret = response.decode(size2)) == 0) {
|
||||
if (size2 > std::numeric_limits<std::size_t>::max()) {
|
||||
ret = -ERANGE;
|
||||
} else {
|
||||
memcpy(value, response.CurrentPointer(), static_cast<std::size_t>(size2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}*/
|
||||
|
||||
packet::error_type remote_client::fuse_getxtimes(const char *path, remote::file_time &bkuptime,
|
||||
remote::file_time &crtime) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
|
||||
packet response;
|
||||
std::uint32_t service_flags = 0;
|
||||
auto ret = packet_client_.send(__FUNCTION__, request, response, service_flags);
|
||||
if (ret == 0) {
|
||||
response.decode(bkuptime);
|
||||
response.decode(crtime);
|
||||
}
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::fuse_init() {
|
||||
std::uint32_t service_flags = 0;
|
||||
auto ret = packet_client_.send(__FUNCTION__, service_flags);
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, "", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*packet::error_type remote_client::fuse_listxattr(const char *path, char *buffer,
|
||||
const 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);
|
||||
|
||||
packet response;
|
||||
std::uint32_t service_flags = 0;
|
||||
if ((ret = packetClient_.send(__FUNCTION__, request, response, service_flags)) == 0) {
|
||||
remote::file_size size2;
|
||||
if ((ret = response.decode(size2)) == 0) {
|
||||
if (size2 > std::numeric_limits<std::size_t>::max()) {
|
||||
ret = -ERANGE;
|
||||
} else {
|
||||
memcpy(buffer, response.CurrentPointer(), static_cast<std::size_t>(size2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}*/
|
||||
|
||||
packet::error_type remote_client::fuse_mkdir(const char *path, const remote::file_mode &mode) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
request.encode(mode);
|
||||
|
||||
std::uint32_t service_flags = 0;
|
||||
const auto ret = packet_client_.send(__FUNCTION__, request, service_flags);
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::fuse_opendir(const char *path, remote::file_handle &handle) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
|
||||
packet response;
|
||||
std::uint32_t service_flags = 0;
|
||||
auto ret = packet_client_.send(__FUNCTION__, request, response, service_flags);
|
||||
if (ret == 0) {
|
||||
ret = response.decode(handle);
|
||||
}
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::fuse_create(const char *path, const remote::file_mode &mode,
|
||||
const remote::open_flags &flags,
|
||||
remote::file_handle &handle) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
request.encode(mode);
|
||||
request.encode(flags);
|
||||
|
||||
packet response;
|
||||
std::uint32_t service_flags = 0;
|
||||
auto ret = packet_client_.send(__FUNCTION__, request, response, service_flags);
|
||||
if (ret == 0) {
|
||||
ret = response.decode(handle);
|
||||
}
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::fuse_open(const char *path, const remote::open_flags &flags,
|
||||
remote::file_handle &handle) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
request.encode(flags);
|
||||
|
||||
packet response;
|
||||
std::uint32_t service_flags = 0;
|
||||
auto ret = packet_client_.send(__FUNCTION__, request, response, service_flags);
|
||||
if (ret == 0) {
|
||||
ret = response.decode(handle);
|
||||
}
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type 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) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
request.encode(read_size);
|
||||
request.encode(read_offset);
|
||||
request.encode(handle);
|
||||
|
||||
packet response;
|
||||
std::uint32_t service_flags = 0;
|
||||
auto ret = packet_client_.send(__FUNCTION__, request, response, service_flags);
|
||||
if (ret > 0) {
|
||||
memcpy(buffer, response.current_pointer(), ret);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::fuse_rename(const char *from, const char *to) {
|
||||
packet request;
|
||||
request.encode(from);
|
||||
request.encode(to);
|
||||
|
||||
std::uint32_t service_flags = 0;
|
||||
const auto ret = packet_client_.send(__FUNCTION__, request, service_flags);
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(
|
||||
__FUNCTION__, utils::path::create_api_path(from) + "|" + utils::path::create_api_path(to),
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type 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) {
|
||||
packet::error_type ret = 0;
|
||||
if (write_size > std::numeric_limits<std::size_t>::max()) {
|
||||
ret = -ERANGE;
|
||||
} else {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
request.encode(write_size);
|
||||
request.encode(buffer, static_cast<std::size_t>(write_size));
|
||||
request.encode(write_offset);
|
||||
request.encode(handle);
|
||||
|
||||
std::uint32_t service_flags = 0;
|
||||
ret = packet_client_.send(__FUNCTION__, request, service_flags);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type 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) {
|
||||
packet::error_type ret = 0;
|
||||
if (write_size > std::numeric_limits<std::size_t>::max()) {
|
||||
ret = -ERANGE;
|
||||
} else {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
request.encode(write_size);
|
||||
request.encode(buffer, static_cast<std::size_t>(write_size));
|
||||
request.encode(write_offset);
|
||||
request.encode(handle);
|
||||
|
||||
std::uint32_t service_flags = 0;
|
||||
ret = packet_client_.send(__FUNCTION__, request, service_flags);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::fuse_readdir(const char *path, const remote::file_offset &offset,
|
||||
const remote::file_handle &handle,
|
||||
std::string &item_path) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
request.encode(offset);
|
||||
request.encode(handle);
|
||||
|
||||
packet response;
|
||||
std::uint32_t service_flags = 0;
|
||||
auto ret = packet_client_.send(__FUNCTION__, request, response, service_flags);
|
||||
if (ret == 0) {
|
||||
DECODE_OR_IGNORE(&response, item_path);
|
||||
}
|
||||
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path),
|
||||
ret == -120 ? 0 : ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::fuse_release(const char *path,
|
||||
const remote::file_handle &handle) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
request.encode(handle);
|
||||
|
||||
std::uint32_t service_flags = 0;
|
||||
const auto ret = packet_client_.send(__FUNCTION__, request, service_flags);
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::fuse_releasedir(const char *path,
|
||||
const remote::file_handle &handle) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
request.encode(handle);
|
||||
|
||||
std::uint32_t service_flags = 0;
|
||||
const auto ret = packet_client_.send(__FUNCTION__, request, service_flags);
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*packet::error_type remote_client::fuse_removexattr(const char *path, const char *name)
|
||||
override { packet request; request.encode(path); request.Encode(name);
|
||||
|
||||
std::uint32_t service_flags = 0;
|
||||
const auto ret = packetClient_.send(__FUNCTION__, request, service_flags);
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}*/
|
||||
|
||||
packet::error_type remote_client::fuse_rmdir(const char *path) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
|
||||
std::uint32_t service_flags = 0;
|
||||
const auto ret = packet_client_.send(__FUNCTION__, request, service_flags);
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::fuse_setattr_x(const char *path, remote::setattr_x &attr) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
request.encode(attr);
|
||||
|
||||
std::uint32_t service_flags = 0;
|
||||
const auto ret = packet_client_.send(__FUNCTION__, request, service_flags);
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::fuse_setbkuptime(const char *path,
|
||||
const remote::file_time &bkuptime) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
request.encode(bkuptime);
|
||||
|
||||
std::uint32_t service_flags = 0;
|
||||
const auto ret = packet_client_.send(__FUNCTION__, request, service_flags);
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::fuse_setchgtime(const char *path,
|
||||
const remote::file_time &chgtime) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
request.encode(chgtime);
|
||||
|
||||
std::uint32_t service_flags = 0;
|
||||
const auto ret = packet_client_.send(__FUNCTION__, request, service_flags);
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::fuse_setcrtime(const char *path,
|
||||
const remote::file_time &crtime) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
request.encode(crtime);
|
||||
|
||||
std::uint32_t service_flags = 0;
|
||||
const auto ret = packet_client_.send(__FUNCTION__, request, service_flags);
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::fuse_setvolname(const char *volname) {
|
||||
packet request;
|
||||
request.encode(volname);
|
||||
|
||||
std::uint32_t service_flags = 0;
|
||||
const auto ret = packet_client_.send(__FUNCTION__, request, service_flags);
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, volname ? volname : "", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*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 &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);
|
||||
request.encode(value, static_cast<std::size_t>(size));
|
||||
request.encode(flags);
|
||||
|
||||
std::uint32_t service_flags = 0;
|
||||
ret = packetClient_.send(__FUNCTION__, request, service_flags);
|
||||
}
|
||||
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
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 &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); request.Encode(size); request.encode(value,
|
||||
static_cast<std::size_t>(size)); request.encode(flags); request.encode(position);
|
||||
|
||||
std::uint32_t service_flags = 0;
|
||||
ret = packetClient_.send(__FUNCTION__, request, service_flags);
|
||||
}
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}*/
|
||||
|
||||
packet::error_type remote_client::fuse_statfs(const char *path, const std::uint64_t &frsize,
|
||||
remote::statfs &st) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
request.encode(frsize);
|
||||
|
||||
packet response;
|
||||
std::uint32_t service_flags = 0;
|
||||
const auto ret = packet_client_.send(__FUNCTION__, request, response, service_flags);
|
||||
if (ret == 0) {
|
||||
response.decode(st);
|
||||
}
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::fuse_statfs_x(const char *path, const std::uint64_t &bsize,
|
||||
remote::statfs_x &st) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
request.encode(bsize);
|
||||
|
||||
packet response;
|
||||
std::uint32_t service_flags = 0;
|
||||
const auto ret = packet_client_.send(__FUNCTION__, request, response, service_flags);
|
||||
if (ret == 0) {
|
||||
response.decode(st);
|
||||
}
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::fuse_truncate(const char *path, const remote::file_offset &size) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
request.encode(size);
|
||||
|
||||
std::uint32_t service_flags = 0;
|
||||
const auto ret = packet_client_.send(__FUNCTION__, request, service_flags);
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::fuse_unlink(const char *path) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
|
||||
std::uint32_t service_flags = 0;
|
||||
const auto ret = packet_client_.send(__FUNCTION__, request, service_flags);
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::fuse_utimens(const char *path, const remote::file_time *tv,
|
||||
const std::uint64_t &op0, const std::uint64_t &op1) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
request.encode(&tv[0], sizeof(remote::file_time) * 2);
|
||||
request.encode(op0);
|
||||
request.encode(op1);
|
||||
|
||||
std::uint32_t service_flags = 0;
|
||||
const auto ret = packet_client_.send(__FUNCTION__, request, service_flags);
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, utils::path::create_api_path(path), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::json_create_directory_snapshot(const std::string &path,
|
||||
json &json_data) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
|
||||
packet response;
|
||||
std::uint32_t service_flags = 0;
|
||||
auto ret = packet_client_.send(__FUNCTION__, request, response, service_flags);
|
||||
if (ret == 0) {
|
||||
ret = packet::decode_json(response, json_data);
|
||||
}
|
||||
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, path, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::json_read_directory_snapshot(const std::string &path,
|
||||
const remote::file_handle &handle,
|
||||
const std::uint32_t &page,
|
||||
json &json_data) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
request.encode(handle);
|
||||
request.encode(page);
|
||||
|
||||
packet response;
|
||||
std::uint32_t service_flags = 0;
|
||||
auto ret = packet_client_.send(__FUNCTION__, request, response, service_flags);
|
||||
if (ret == 0) {
|
||||
ret = packet::decode_json(response, json_data);
|
||||
}
|
||||
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, path, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type
|
||||
remote_client::json_release_directory_snapshot(const std::string &path,
|
||||
const remote::file_handle &handle) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
request.encode(handle);
|
||||
|
||||
std::uint32_t service_flags = 0;
|
||||
const auto ret = packet_client_.send(__FUNCTION__, request, service_flags);
|
||||
|
||||
RAISE_REMOTE_CLIENT_FUSE_EVENT(__FUNCTION__, path, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void remote_client::set_fuse_uid_gid(const remote::user_id &uid, const remote::group_id &gid) {
|
||||
uid_ = uid;
|
||||
gid_ = gid;
|
||||
}
|
||||
} // namespace repertory::remote_fuse
|
617
src/drives/fuse/remotefuse/remote_fuse_drive.cpp
Normal file
617
src/drives/fuse/remotefuse/remote_fuse_drive.cpp
Normal file
@ -0,0 +1,617 @@
|
||||
/*
|
||||
Copyright <2018-2022> <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 _WIN32
|
||||
|
||||
#include "drives/fuse/remotefuse/remote_fuse_drive.hpp"
|
||||
#include "app_config.hpp"
|
||||
#include "drives/fuse/events.hpp"
|
||||
#include "events/consumers/console_consumer.hpp"
|
||||
#include "events/consumers/logging_consumer.hpp"
|
||||
#include "events/events.hpp"
|
||||
#include "events/event_system.hpp"
|
||||
#include "platform/platform.hpp"
|
||||
#include "rpc/server/server.hpp"
|
||||
#include "types/remote.hpp"
|
||||
#include "utils/file_utils.hpp"
|
||||
#include "utils/path_utils.hpp"
|
||||
|
||||
namespace repertory::remote_fuse {
|
||||
app_config *remote_fuse_drive::remote_fuse_impl::config_ = nullptr;
|
||||
lock_data *remote_fuse_drive::remote_fuse_impl::lock_ = nullptr;
|
||||
std::string *remote_fuse_drive::remote_fuse_impl::mount_location_ = nullptr;
|
||||
bool remote_fuse_drive::remote_fuse_impl::console_enabled_ = false;
|
||||
bool remote_fuse_drive::remote_fuse_impl::was_mounted_ = false;
|
||||
std::optional<gid_t> remote_fuse_drive::remote_fuse_impl::forced_gid_;
|
||||
std::optional<uid_t> remote_fuse_drive::remote_fuse_impl::forced_uid_;
|
||||
std::optional<mode_t> remote_fuse_drive::remote_fuse_impl::forced_umask_;
|
||||
remote_instance_factory *remote_fuse_drive::remote_fuse_impl::factory_ = nullptr;
|
||||
std::unique_ptr<logging_consumer> remote_fuse_drive::remote_fuse_impl::logging_consumer_;
|
||||
std::unique_ptr<console_consumer> remote_fuse_drive::remote_fuse_impl::console_consumer_;
|
||||
std::unique_ptr<i_remote_instance> remote_fuse_drive::remote_fuse_impl::remote_instance_;
|
||||
std::unique_ptr<server> remote_fuse_drive::remote_fuse_impl::server_;
|
||||
|
||||
void remote_fuse_drive::remote_fuse_impl::tear_down(const int &ret) {
|
||||
if (was_mounted_) {
|
||||
event_system::instance().raise<drive_mount_result>(std::to_string(ret));
|
||||
event_system::instance().stop();
|
||||
logging_consumer_.reset();
|
||||
console_consumer_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void remote_fuse_drive::remote_fuse_impl::populate_stat(const remote::stat &r,
|
||||
const bool &directory, struct stat &st) {
|
||||
memset(&st, 0, sizeof(struct stat));
|
||||
|
||||
#ifdef __APPLE__
|
||||
st.st_blksize = 0;
|
||||
|
||||
st.st_atimespec.tv_nsec = r.st_atimespec % NANOS_PER_SECOND;
|
||||
st.st_atimespec.tv_sec = r.st_atimespec / NANOS_PER_SECOND;
|
||||
|
||||
st.st_birthtimespec.tv_nsec = r.st_birthtimespec % NANOS_PER_SECOND;
|
||||
st.st_birthtimespec.tv_sec = r.st_birthtimespec / NANOS_PER_SECOND;
|
||||
|
||||
st.st_ctimespec.tv_nsec = r.st_ctimespec % NANOS_PER_SECOND;
|
||||
st.st_ctimespec.tv_sec = r.st_ctimespec / NANOS_PER_SECOND;
|
||||
|
||||
st.st_mtimespec.tv_nsec = r.st_mtimespec % NANOS_PER_SECOND;
|
||||
st.st_mtimespec.tv_sec = r.st_mtimespec / NANOS_PER_SECOND;
|
||||
|
||||
st.st_flags = r.st_flags;
|
||||
#else
|
||||
st.st_blksize = 4096;
|
||||
|
||||
st.st_atim.tv_nsec = r.st_atimespec % NANOS_PER_SECOND;
|
||||
st.st_atim.tv_sec = r.st_atimespec / NANOS_PER_SECOND;
|
||||
|
||||
st.st_ctim.tv_nsec = r.st_ctimespec % NANOS_PER_SECOND;
|
||||
st.st_ctim.tv_sec = r.st_ctimespec / NANOS_PER_SECOND;
|
||||
|
||||
st.st_mtim.tv_nsec = r.st_mtimespec % NANOS_PER_SECOND;
|
||||
st.st_mtim.tv_sec = r.st_mtimespec / NANOS_PER_SECOND;
|
||||
#endif
|
||||
if (not directory) {
|
||||
const auto blockSizeStat = static_cast<std::uint64_t>(512);
|
||||
const auto blockSize = static_cast<std::uint64_t>(4096);
|
||||
const auto size =
|
||||
utils::divide_with_ceiling(static_cast<std::uint64_t>(st.st_size), blockSize) * blockSize;
|
||||
st.st_blocks =
|
||||
std::max(blockSize / blockSizeStat, utils::divide_with_ceiling(size, blockSizeStat));
|
||||
}
|
||||
|
||||
st.st_gid = r.st_gid;
|
||||
st.st_mode = (directory ? S_IFDIR : S_IFREG) | r.st_mode;
|
||||
|
||||
st.st_nlink = r.st_nlink;
|
||||
st.st_size = r.st_size;
|
||||
st.st_uid = r.st_uid;
|
||||
}
|
||||
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_access(const char *path, int mask) {
|
||||
return remote_instance_->fuse_access(path, mask);
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_chflags(const char *path, uint32_t flags) {
|
||||
return remote_instance_->fuse_chflags(path, flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_chmod(const char *path, mode_t mode) {
|
||||
return remote_instance_->fuse_chmod(path, mode);
|
||||
}
|
||||
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_chown(const char *path, uid_t uid, gid_t gid) {
|
||||
return remote_instance_->fuse_chown(path, uid, gid);
|
||||
}
|
||||
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_create(const char *path, mode_t mode,
|
||||
struct fuse_file_info *fi) {
|
||||
return remote_instance_->fuse_create(path, mode, remote::open_flags(fi->flags), fi->fh);
|
||||
}
|
||||
|
||||
void remote_fuse_drive::remote_fuse_impl::repertory_destroy(void * /*ptr*/) {
|
||||
event_system::instance().raise<drive_unmount_pending>(*mount_location_);
|
||||
if (server_) {
|
||||
server_->stop();
|
||||
server_.reset();
|
||||
}
|
||||
|
||||
remote_instance_->fuse_destroy();
|
||||
remote_instance_.reset();
|
||||
|
||||
lock_->set_mount_state(false, "", -1);
|
||||
event_system::instance().raise<drive_mounted>(*mount_location_);
|
||||
}
|
||||
|
||||
/*int remote_fuse_drive::remote_fuse_impl::repertory_fallocate(const char *path, int mode, off_t
|
||||
offset, off_t length, struct fuse_file_info *fi) { return remote_instance_->fuse_fallocate(path,
|
||||
mode, offset, length, fi->fh);
|
||||
}*/
|
||||
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_fgetattr(const char *path, struct stat *st,
|
||||
struct fuse_file_info *fi) {
|
||||
remote::stat r{};
|
||||
|
||||
bool directory = false;
|
||||
auto ret = remote_instance_->fuse_fgetattr(path, r, directory, fi->fh);
|
||||
if (ret == 0) {
|
||||
populate_stat(r, directory, *st);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_fsetattr_x(const char *path,
|
||||
struct setattr_x *attr,
|
||||
struct fuse_file_info *fi) {
|
||||
remote::setattr_x attrRepertory{};
|
||||
attrRepertory.valid = attr->valid;
|
||||
attrRepertory.mode = attr->mode;
|
||||
attrRepertory.uid = attr->uid;
|
||||
attrRepertory.gid = attr->gid;
|
||||
attrRepertory.size = attr->size;
|
||||
attrRepertory.acctime = ((attr->acctime.tv_sec * NANOS_PER_SECOND) + attr->acctime.tv_nsec);
|
||||
attrRepertory.modtime = ((attr->modtime.tv_sec * NANOS_PER_SECOND) + attr->modtime.tv_nsec);
|
||||
attrRepertory.crtime = ((attr->crtime.tv_sec * NANOS_PER_SECOND) + attr->crtime.tv_nsec);
|
||||
attrRepertory.chgtime = ((attr->chgtime.tv_sec * NANOS_PER_SECOND) + attr->chgtime.tv_nsec);
|
||||
attrRepertory.bkuptime = ((attr->bkuptime.tv_sec * NANOS_PER_SECOND) + attr->bkuptime.tv_nsec);
|
||||
attrRepertory.flags = attr->flags;
|
||||
return remote_instance_->fuse_fsetattr_x(path, attrRepertory, fi->fh);
|
||||
}
|
||||
#endif
|
||||
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_fsync(const char *path, int datasync,
|
||||
struct fuse_file_info *fi) {
|
||||
return remote_instance_->fuse_fsync(path, datasync, fi->fh);
|
||||
}
|
||||
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_ftruncate(const char *path, off_t size,
|
||||
struct fuse_file_info *fi) {
|
||||
return remote_instance_->fuse_ftruncate(path, size, fi->fh);
|
||||
}
|
||||
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_getattr(const char *path, struct stat *st) {
|
||||
bool directory = false;
|
||||
remote::stat r{};
|
||||
const auto ret = remote_instance_->fuse_getattr(path, r, directory);
|
||||
if (ret == 0) {
|
||||
populate_stat(r, directory, *st);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_getxtimes(const char *path,
|
||||
struct timespec *bkuptime,
|
||||
struct timespec *crtime) {
|
||||
if (not(bkuptime && crtime)) {
|
||||
return -EFAULT;
|
||||
} else {
|
||||
remote::file_time repertory_bkuptime = 0;
|
||||
remote::file_time repertory_crtime = 0;
|
||||
const auto ret = remote_instance_->fuse_getxtimes(path, repertory_bkuptime, repertory_crtime);
|
||||
if (ret == 0) {
|
||||
bkuptime->tv_nsec = static_cast<long>(repertory_bkuptime % NANOS_PER_SECOND);
|
||||
bkuptime->tv_sec = repertory_bkuptime / NANOS_PER_SECOND;
|
||||
crtime->tv_nsec = static_cast<long>(repertory_crtime % NANOS_PER_SECOND);
|
||||
crtime->tv_sec = repertory_crtime / NANOS_PER_SECOND;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void *remote_fuse_drive::remote_fuse_impl::repertory_init(struct fuse_conn_info *conn) {
|
||||
utils::file::change_to_process_directory();
|
||||
#ifdef __APPLE__
|
||||
conn->want |= FUSE_CAP_VOL_RENAME;
|
||||
conn->want |= FUSE_CAP_XTIMES;
|
||||
#endif
|
||||
conn->want |= FUSE_CAP_BIG_WRITES;
|
||||
|
||||
if (console_enabled_) {
|
||||
console_consumer_ = std::make_unique<console_consumer>();
|
||||
}
|
||||
logging_consumer_ =
|
||||
std::make_unique<logging_consumer>(config_->get_log_directory(), config_->get_event_level());
|
||||
event_system::instance().start();
|
||||
|
||||
was_mounted_ = true;
|
||||
lock_->set_mount_state(true, *mount_location_, getpid());
|
||||
|
||||
remote_instance_ = (*factory_)();
|
||||
remote_instance_->set_fuse_uid_gid(getuid(), getgid());
|
||||
|
||||
if (remote_instance_->fuse_init() != 0) {
|
||||
event_system::instance().raise<repertory_exception>(__FUNCTION__,
|
||||
"Failed to connect to remote server");
|
||||
event_system::instance().raise<unmount_requested>();
|
||||
} else {
|
||||
server_ = std::make_unique<server>(*config_);
|
||||
server_->start();
|
||||
event_system::instance().raise<drive_mounted>(*mount_location_);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_mkdir(const char *path, mode_t mode) {
|
||||
return remote_instance_->fuse_mkdir(path, mode);
|
||||
}
|
||||
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_open(const char *path,
|
||||
struct fuse_file_info *fi) {
|
||||
return remote_instance_->fuse_open(path, remote::open_flags(fi->flags), fi->fh);
|
||||
}
|
||||
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_opendir(const char *path,
|
||||
struct fuse_file_info *fi) {
|
||||
return remote_instance_->fuse_opendir(path, fi->fh);
|
||||
}
|
||||
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_read(const char *path, char *buffer,
|
||||
size_t readSize, off_t readOffset,
|
||||
struct fuse_file_info *fi) {
|
||||
return remote_instance_->fuse_read(path, buffer, readSize, readOffset, fi->fh);
|
||||
}
|
||||
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_readdir(const char *path, void *buf,
|
||||
fuse_fill_dir_t fuseFillDir,
|
||||
off_t offset,
|
||||
struct fuse_file_info *fi) {
|
||||
std::string item_path;
|
||||
int ret = 0;
|
||||
while ((ret = remote_instance_->fuse_readdir(path, offset, fi->fh, item_path)) == 0) {
|
||||
if ((item_path != ".") && (item_path != "..")) {
|
||||
item_path = utils::path::strip_to_file_name(item_path);
|
||||
}
|
||||
if (fuseFillDir(buf, &item_path[0], nullptr, ++offset) != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == -120) {
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_release(const char *path,
|
||||
struct fuse_file_info *fi) {
|
||||
return remote_instance_->fuse_release(path, fi->fh);
|
||||
}
|
||||
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_releasedir(const char *path,
|
||||
struct fuse_file_info *fi) {
|
||||
return remote_instance_->fuse_releasedir(path, fi->fh);
|
||||
}
|
||||
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_rename(const char *from, const char *to) {
|
||||
return remote_instance_->fuse_rename(from, to);
|
||||
}
|
||||
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_rmdir(const char *path) {
|
||||
return remote_instance_->fuse_rmdir(path);
|
||||
}
|
||||
/*
|
||||
#ifdef HAS_SETXATTR
|
||||
#ifdef __APPLE__
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_getxattr(const char *path, const char *name,
|
||||
char *value, size_t size, uint32_t position) { return remote_instance_->fuse_getxattr_osx(path,
|
||||
name, value, size, position);
|
||||
}
|
||||
#else
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_getxattr(const char *path, const char *name,
|
||||
char *value, size_t size) { return remote_instance_->fuse_getxattr(path, name, value, size);
|
||||
}
|
||||
|
||||
#endif
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_listxattr(const char *path, char *buffer,
|
||||
size_t size) { return remote_instance_->fuse_listxattr(path, buffer, size);
|
||||
}
|
||||
|
||||
remote_fuse_drive::remote_fuse_impl::int repertory_removexattr(const char *path, const char
|
||||
*name) { return remote_instance_->fuse_removexattr(path, name);
|
||||
}
|
||||
#ifdef __APPLE__
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_setxattr(const char *path, const char *name,
|
||||
const char *value, size_t size, int flags, uint32_t position) { return
|
||||
remote_instance_->fuse_setxattr_osx(path, name, value, size, flags, position);
|
||||
}
|
||||
#else
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_setxattr(const char *path, const char *name,
|
||||
const char *value, size_t size, int flags) { return remote_instance_->fuse_setxattr(path, name,
|
||||
value, size, flags);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
*/
|
||||
#ifdef __APPLE__
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_setattr_x(const char *path,
|
||||
struct setattr_x *attr) {
|
||||
remote::setattr_x attributes{};
|
||||
attributes.valid = attr->valid;
|
||||
attributes.mode = attr->mode;
|
||||
attributes.uid = attr->uid;
|
||||
attributes.gid = attr->gid;
|
||||
attributes.size = attr->size;
|
||||
attributes.acctime = ((attr->acctime.tv_sec * NANOS_PER_SECOND) + attr->acctime.tv_nsec);
|
||||
attributes.modtime = ((attr->modtime.tv_sec * NANOS_PER_SECOND) + attr->modtime.tv_nsec);
|
||||
attributes.crtime = ((attr->crtime.tv_sec * NANOS_PER_SECOND) + attr->crtime.tv_nsec);
|
||||
attributes.chgtime = ((attr->chgtime.tv_sec * NANOS_PER_SECOND) + attr->chgtime.tv_nsec);
|
||||
attributes.bkuptime = ((attr->bkuptime.tv_sec * NANOS_PER_SECOND) + attr->bkuptime.tv_nsec);
|
||||
attributes.flags = attr->flags;
|
||||
return remote_instance_->fuse_setattr_x(path, attributes);
|
||||
}
|
||||
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_setbkuptime(const char *path,
|
||||
const struct timespec *bkuptime) {
|
||||
remote::file_time repertory_bkuptime =
|
||||
((bkuptime->tv_sec * NANOS_PER_SECOND) + bkuptime->tv_nsec);
|
||||
return remote_instance_->fuse_setbkuptime(path, repertory_bkuptime);
|
||||
}
|
||||
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_setchgtime(const char *path,
|
||||
const struct timespec *chgtime) {
|
||||
remote::file_time repertory_chgtime = ((chgtime->tv_sec * NANOS_PER_SECOND) + chgtime->tv_nsec);
|
||||
return remote_instance_->fuse_setchgtime(path, repertory_chgtime);
|
||||
}
|
||||
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_setcrtime(const char *path,
|
||||
const struct timespec *crtime) {
|
||||
remote::file_time repertory_crtime = ((crtime->tv_sec * NANOS_PER_SECOND) + crtime->tv_nsec);
|
||||
return remote_instance_->fuse_setcrtime(path, repertory_crtime);
|
||||
}
|
||||
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_setvolname(const char *volname) {
|
||||
return remote_instance_->fuse_setvolname(volname);
|
||||
}
|
||||
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_statfs_x(const char *path,
|
||||
struct statfs *stbuf) {
|
||||
auto ret = statfs(config_->get_data_directory().c_str(), stbuf);
|
||||
if (ret == 0) {
|
||||
remote::statfs_x r{};
|
||||
if ((ret = remote_instance_->fuse_statfs_x(path, stbuf->f_bsize, r)) == 0) {
|
||||
stbuf->f_blocks = r.f_blocks;
|
||||
stbuf->f_bavail = r.f_bavail;
|
||||
stbuf->f_bfree = r.f_bfree;
|
||||
stbuf->f_ffree = r.f_ffree;
|
||||
stbuf->f_files = r.f_files;
|
||||
stbuf->f_owner = getuid();
|
||||
strncpy(&stbuf->f_mntonname[0], mount_location_->c_str(), MNAMELEN);
|
||||
strncpy(&stbuf->f_mntfromname[0], &r.f_mntfromname[0], MNAMELEN);
|
||||
}
|
||||
} else {
|
||||
ret = -errno;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_statfs(const char *path, struct statvfs *stbuf) {
|
||||
auto ret = statvfs(&config_->get_data_directory()[0], stbuf);
|
||||
if (ret == 0) {
|
||||
remote::statfs r{};
|
||||
if ((ret = remote_instance_->fuse_statfs(path, stbuf->f_frsize, r)) == 0) {
|
||||
stbuf->f_blocks = r.f_blocks;
|
||||
stbuf->f_bavail = r.f_bavail;
|
||||
stbuf->f_bfree = r.f_bfree;
|
||||
stbuf->f_ffree = r.f_ffree;
|
||||
stbuf->f_favail = r.f_favail;
|
||||
stbuf->f_files = r.f_files;
|
||||
}
|
||||
} else {
|
||||
ret = -errno;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_truncate(const char *path, off_t size) {
|
||||
return remote_instance_->fuse_truncate(path, size);
|
||||
}
|
||||
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_unlink(const char *path) {
|
||||
return remote_instance_->fuse_unlink(path);
|
||||
}
|
||||
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_utimens(const char *path,
|
||||
const struct timespec tv[2]) {
|
||||
remote::file_time rtv[2] = {0};
|
||||
if (tv) {
|
||||
rtv[0] = tv[0].tv_nsec + (tv[0].tv_sec * NANOS_PER_SECOND);
|
||||
rtv[1] = tv[1].tv_nsec + (tv[1].tv_sec * NANOS_PER_SECOND);
|
||||
}
|
||||
return remote_instance_->fuse_utimens(path, rtv, tv ? tv[0].tv_nsec : 0, tv ? tv[1].tv_nsec : 0);
|
||||
}
|
||||
|
||||
int remote_fuse_drive::remote_fuse_impl::repertory_write(const char *path, const char *buffer,
|
||||
size_t writeSize, off_t writeOffset,
|
||||
struct fuse_file_info *fi) {
|
||||
return remote_instance_->fuse_write(path, buffer, writeSize, writeOffset, fi->fh);
|
||||
}
|
||||
|
||||
remote_fuse_drive::remote_fuse_drive(app_config &config, lock_data &lock,
|
||||
remote_instance_factory factory)
|
||||
: config_(config), lock_(lock), factory_(std::move(factory)) {
|
||||
E_SUBSCRIBE_EXACT(unmount_requested, [this](const unmount_requested &) {
|
||||
std::thread([this]() { remote_fuse_drive::shutdown(mount_location_); }).detach();
|
||||
});
|
||||
}
|
||||
|
||||
int remote_fuse_drive::mount(std::vector<std::string> drive_args) {
|
||||
remote_fuse_impl::lock_ = &lock_;
|
||||
remote_fuse_impl::config_ = &config_;
|
||||
remote_fuse_impl::mount_location_ = &mount_location_;
|
||||
remote_fuse_impl::factory_ = &factory_;
|
||||
|
||||
#ifdef __APPLE__
|
||||
fuse_ops_.chflags = remote_fuse_impl::repertory_chflags;
|
||||
fuse_ops_.fsetattr_x = remote_fuse_impl::repertory_fsetattr_x;
|
||||
fuse_ops_.getxtimes = remote_fuse_impl::repertory_getxtimes;
|
||||
fuse_ops_.setattr_x = remote_fuse_impl::repertory_setattr_x;
|
||||
fuse_ops_.setbkuptime = remote_fuse_impl::repertory_setbkuptime;
|
||||
fuse_ops_.setchgtime = remote_fuse_impl::repertory_setchgtime;
|
||||
fuse_ops_.setcrtime = remote_fuse_impl::repertory_setcrtime;
|
||||
fuse_ops_.setvolname = remote_fuse_impl::repertory_setvolname;
|
||||
fuse_ops_.statfs_x = remote_fuse_impl::repertory_statfs_x;
|
||||
#endif
|
||||
auto force_no_console = false;
|
||||
for (std::size_t i = 1u; !force_no_console && (i < drive_args.size()); i++) {
|
||||
if (drive_args[i] == "-nc") {
|
||||
force_no_console = true;
|
||||
}
|
||||
}
|
||||
utils::remove_element_from(drive_args, "-nc");
|
||||
|
||||
for (std::size_t i = 1u; i < drive_args.size(); i++) {
|
||||
if (drive_args[i] == "-f") {
|
||||
remote_fuse_impl::console_enabled_ = not force_no_console;
|
||||
} else if (drive_args[i].find("-o") == 0) {
|
||||
std::string options;
|
||||
if (drive_args[i].size() == 2u) {
|
||||
if ((i + 1) < drive_args.size()) {
|
||||
options = drive_args[++i];
|
||||
}
|
||||
} else {
|
||||
options = drive_args[i].substr(2);
|
||||
}
|
||||
|
||||
const auto option_list = utils::string::split(options, ',');
|
||||
for (const auto &option : option_list) {
|
||||
if (option.find("gid") == 0) {
|
||||
const auto parts = utils::string::split(option, '=');
|
||||
if (parts.size() == 2u) {
|
||||
auto gid = getgrnam(parts[1u].c_str());
|
||||
if (not gid) {
|
||||
gid = getgrgid(utils::string::to_uint32(parts[1]));
|
||||
}
|
||||
if ((getgid() != 0) && (gid->gr_gid == 0)) {
|
||||
std::cerr << "'gid=0' requires running as root" << std::endl;
|
||||
return -1;
|
||||
} else {
|
||||
remote_fuse_impl::forced_gid_ = gid->gr_gid;
|
||||
}
|
||||
}
|
||||
} else if (option.find("uid") == 0) {
|
||||
const auto parts = utils::string::split(option, '=');
|
||||
if (parts.size() == 2u) {
|
||||
auto uid = getpwnam(parts[1u].c_str());
|
||||
if (not uid) {
|
||||
uid = getpwuid(utils::string::to_uint32(parts[1]));
|
||||
}
|
||||
if ((getuid() != 0) && (uid->pw_uid == 0)) {
|
||||
std::cerr << "'uid=0' requires running as root" << std::endl;
|
||||
return -1;
|
||||
} else {
|
||||
remote_fuse_impl::forced_uid_ = uid->pw_uid;
|
||||
}
|
||||
}
|
||||
} else if (option.find("umask") == 0) {
|
||||
const auto parts = utils::string::split(option, '=');
|
||||
if (parts.size() == 2u) {
|
||||
static const auto numeric_regex = std::regex("[0-9]+");
|
||||
try {
|
||||
if (not std::regex_match(parts[1u], numeric_regex)) {
|
||||
throw std::runtime_error("invalid syntax");
|
||||
} else {
|
||||
remote_fuse_impl::forced_umask_ = utils::string::to_uint32(parts[1]);
|
||||
}
|
||||
} catch (...) {
|
||||
std::cerr << ("'" + option + "' invalid syntax") << std::endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<const char *> fuse_argv(drive_args.size());
|
||||
for (std::size_t i = 0u; i < drive_args.size(); i++) {
|
||||
fuse_argv[i] = drive_args[i].c_str();
|
||||
}
|
||||
|
||||
struct fuse_args args =
|
||||
FUSE_ARGS_INIT(static_cast<int>(fuse_argv.size()), (char **)&fuse_argv[0]);
|
||||
char *mount_location = nullptr;
|
||||
fuse_parse_cmdline(&args, &mount_location, nullptr, nullptr);
|
||||
if (mount_location) {
|
||||
mount_location_ = mount_location;
|
||||
free(mount_location);
|
||||
}
|
||||
|
||||
std::string args_string;
|
||||
for (const auto *arg : fuse_argv) {
|
||||
if (args_string.empty()) {
|
||||
args_string += arg;
|
||||
} else {
|
||||
args_string += (" " + std::string(arg));
|
||||
}
|
||||
}
|
||||
|
||||
event_system::instance().raise<fuse_args_parsed>(args_string);
|
||||
|
||||
umask(0);
|
||||
const auto ret = fuse_main(static_cast<int>(fuse_argv.size()), (char **)&fuse_argv[0], &fuse_ops_,
|
||||
&mount_location);
|
||||
remote_fuse_impl::tear_down(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void remote_fuse_drive::display_options(int argc, char *argv[]) {
|
||||
struct fuse_operations fuse_ops {};
|
||||
fuse_main(argc, argv, &fuse_ops, nullptr);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
void remote_fuse_drive::display_version_information(int argc, char *argv[]) {
|
||||
struct fuse_operations fuse_ops {};
|
||||
fuse_main(argc, argv, &fuse_ops, nullptr);
|
||||
}
|
||||
|
||||
void remote_fuse_drive::shutdown(std::string mount_location) {
|
||||
#if __APPLE__
|
||||
const auto unmount = "umount \"" + mount_location + "\" >/dev/null 2>&1";
|
||||
#else
|
||||
const auto unmount = "fusermount -u \"" + mount_location + "\" >/dev/null 2>&1";
|
||||
#endif
|
||||
int ret;
|
||||
for (std::uint8_t i = 0u; ((ret = system(unmount.c_str())) != 0) && (i < 10u); i++) {
|
||||
event_system::instance().raise<unmount_result>(mount_location, std::to_string(ret));
|
||||
if (i != 9u) {
|
||||
std::this_thread::sleep_for(1s);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
ret = kill(getpid(), SIGINT);
|
||||
}
|
||||
|
||||
event_system::instance().raise<unmount_result>(mount_location, std::to_string(ret));
|
||||
}
|
||||
} // namespace repertory::remote_fuse
|
||||
|
||||
#endif // _WIN32
|
51
src/drives/fuse/remotefuse/remote_fuse_drive2.cpp
Normal file
51
src/drives/fuse/remotefuse/remote_fuse_drive2.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
Copyright <2018-2022> <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 _WIN32
|
||||
#if 0
|
||||
|
||||
#include "drives/fuse/remotefuse/remote_fuse_drive2.hpp"
|
||||
#include "app_config.hpp"
|
||||
#include "events/consumers/console_consumer.hpp"
|
||||
#include "events/consumers/logging_consumer.hpp"
|
||||
#include "events/events.hpp"
|
||||
#include "events/event_system.hpp"
|
||||
#include "platform/platform.hpp"
|
||||
#include "rpc/server/server.hpp"
|
||||
#include "types/remote.hpp"
|
||||
#include "utils/file_utils.hpp"
|
||||
#include "utils/path_utils.hpp"
|
||||
|
||||
namespace repertory::remote_fuse {
|
||||
api_error remote_fuse_drive2::access_impl(std::string api_path, int mask) {
|
||||
return utils::to_api_error(remote_instance_->fuse_access(api_path.c_str(), mask));
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
api_error remote_fuse_drive2::chflags_impl(std::string api_path, uint32_t flags) {
|
||||
return utils::to_api_error(remote_instance_->fuse_chflags(path, flags));
|
||||
}
|
||||
#endif // __APPLE__
|
||||
|
||||
api_error remote_fuse_drive2::chmod_impl(std::string api_path, mode_t mode) {
|
||||
return utils::to_api_error(remote_instance_->fuse_chmod(path, mode));
|
||||
}
|
||||
} // namespace repertory::remote_fuse
|
||||
|
||||
#endif // 0
|
||||
#endif // _WIN32
|
1473
src/drives/fuse/remotefuse/remote_server.cpp
Normal file
1473
src/drives/fuse/remotefuse/remote_server.cpp
Normal file
File diff suppressed because it is too large
Load Diff
192
src/drives/remote/remote_open_file_table.cpp
Normal file
192
src/drives/remote/remote_open_file_table.cpp
Normal file
@ -0,0 +1,192 @@
|
||||
/*
|
||||
Copyright <2018-2022> <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 "drives/remote/remote_open_file_table.hpp"
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
namespace repertory {
|
||||
void remote_open_file_table::add_directory(const std::string &client_id, void *dir) {
|
||||
unique_mutex_lock directory_lock(directory_mutex_);
|
||||
auto &list = directory_lookup_[client_id];
|
||||
if (utils::collection_excludes(list, dir)) {
|
||||
directory_lookup_[client_id].emplace_back(dir);
|
||||
}
|
||||
}
|
||||
|
||||
void remote_open_file_table::close_all(const std::string &client_id) {
|
||||
std::vector<remote::file_handle> compatHandles;
|
||||
unique_mutex_lock compat_lock(compat_mutex_);
|
||||
for (auto &kv : compat_lookup_) {
|
||||
if (kv.second.client_id == client_id) {
|
||||
compatHandles.emplace_back(kv.first);
|
||||
}
|
||||
}
|
||||
compat_lock.unlock();
|
||||
|
||||
for (auto &handle : compatHandles) {
|
||||
#ifdef _WIN32
|
||||
_close(static_cast<int>(handle));
|
||||
#else
|
||||
close(static_cast<int>(handle));
|
||||
#endif
|
||||
remove_compat_open_info(handle);
|
||||
}
|
||||
|
||||
std::vector<OSHandle> handles;
|
||||
unique_mutex_lock file_lock(file_mutex_);
|
||||
for (auto &kv : file_lookup_) {
|
||||
if (kv.second.client_id == client_id) {
|
||||
handles.emplace_back(kv.first);
|
||||
}
|
||||
}
|
||||
file_lock.unlock();
|
||||
|
||||
for (auto &handle : handles) {
|
||||
#ifdef _WIN32
|
||||
::CloseHandle(handle);
|
||||
#else
|
||||
close(handle);
|
||||
#endif
|
||||
remove_open_info(handle);
|
||||
}
|
||||
|
||||
std::vector<void *> dirs;
|
||||
unique_mutex_lock directory_lock(directory_mutex_);
|
||||
for (auto &kv : directory_lookup_) {
|
||||
if (kv.first == client_id) {
|
||||
dirs.insert(dirs.end(), kv.second.begin(), kv.second.end());
|
||||
}
|
||||
}
|
||||
directory_lock.unlock();
|
||||
|
||||
for (auto *dir : dirs) {
|
||||
remove_directory(client_id, dir);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
bool remote_open_file_table::get_directory_buffer(const OSHandle &handle, PVOID *&buffer) {
|
||||
mutex_lock file_lock(file_mutex_);
|
||||
if (file_lookup_.find(handle) != file_lookup_.end()) {
|
||||
buffer = &file_lookup_[handle].directory_buffer;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool remote_open_file_table::get_open_info(const OSHandle &handle, open_info &oi) {
|
||||
mutex_lock file_lock(file_mutex_);
|
||||
if (file_lookup_.find(handle) != file_lookup_.end()) {
|
||||
oi = file_lookup_[handle];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string remote_open_file_table::get_open_file_path(const OSHandle &handle) {
|
||||
mutex_lock file_lock(file_mutex_);
|
||||
if (file_lookup_.find(handle) != file_lookup_.end()) {
|
||||
return file_lookup_[handle].path;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
bool remote_open_file_table::has_open_directory(const std::string &client_id, void *dir) {
|
||||
unique_mutex_lock directory_lock(directory_mutex_);
|
||||
auto &list = directory_lookup_[client_id];
|
||||
return (utils::collection_includes(list, dir));
|
||||
}
|
||||
|
||||
int remote_open_file_table::has_compat_open_info(const remote::file_handle &handle,
|
||||
const int &errorReturn) {
|
||||
mutex_lock compat_lock(compat_mutex_);
|
||||
const auto res = ((compat_lookup_.find(handle) == compat_lookup_.end()) ? -1 : 0);
|
||||
if (res == -1) {
|
||||
errno = errorReturn;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void remote_open_file_table::remove_compat_open_info(const remote::file_handle &handle) {
|
||||
mutex_lock compat_lock(compat_mutex_);
|
||||
if (compat_lookup_[handle].count > 0) {
|
||||
compat_lookup_[handle].count--;
|
||||
}
|
||||
if (not compat_lookup_[handle].count) {
|
||||
compat_lookup_.erase(handle);
|
||||
}
|
||||
}
|
||||
|
||||
bool remote_open_file_table::remove_directory(const std::string &client_id, void *dir) {
|
||||
unique_mutex_lock directory_lock(directory_mutex_);
|
||||
auto &list = directory_lookup_[client_id];
|
||||
if (utils::collection_includes(list, dir)) {
|
||||
utils::remove_element_from(list, dir);
|
||||
delete_open_directory(dir);
|
||||
if (directory_lookup_[client_id].empty()) {
|
||||
directory_lookup_.erase(client_id);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void remote_open_file_table::remove_open_info(const OSHandle &handle) {
|
||||
mutex_lock file_lock(file_mutex_);
|
||||
if (file_lookup_[handle].count > 0) {
|
||||
file_lookup_[handle].count--;
|
||||
}
|
||||
if (not file_lookup_[handle].count) {
|
||||
#ifdef _WIN32
|
||||
if (file_lookup_[handle].directory_buffer) {
|
||||
FspFileSystemDeleteDirectoryBuffer(&file_lookup_[handle].directory_buffer);
|
||||
}
|
||||
#endif
|
||||
file_lookup_.erase(handle);
|
||||
}
|
||||
}
|
||||
|
||||
void remote_open_file_table::set_compat_client_id(const remote::file_handle &handle,
|
||||
const std::string &client_id) {
|
||||
mutex_lock compat_lock(compat_mutex_);
|
||||
compat_lookup_[handle].client_id = client_id;
|
||||
}
|
||||
|
||||
void remote_open_file_table::set_client_id(const OSHandle &handle, const std::string &client_id) {
|
||||
mutex_lock file_lock(file_mutex_);
|
||||
file_lookup_[handle].client_id = client_id;
|
||||
}
|
||||
|
||||
void remote_open_file_table::set_compat_open_info(const remote::file_handle &handle) {
|
||||
mutex_lock compat_lock(compat_mutex_);
|
||||
if (compat_lookup_.find(handle) == compat_lookup_.end()) {
|
||||
compat_lookup_[handle] = {0, ""};
|
||||
}
|
||||
compat_lookup_[handle].count++;
|
||||
}
|
||||
|
||||
void remote_open_file_table::set_open_info(const OSHandle &handle, open_info oi) {
|
||||
mutex_lock file_lock(file_mutex_);
|
||||
if (file_lookup_.find(handle) == file_lookup_.end()) {
|
||||
file_lookup_[handle] = std::move(oi);
|
||||
}
|
||||
file_lookup_[handle].count++;
|
||||
}
|
||||
} // namespace repertory
|
462
src/drives/winfsp/remotewinfsp/remote_client.cpp
Normal file
462
src/drives/winfsp/remotewinfsp/remote_client.cpp
Normal file
@ -0,0 +1,462 @@
|
||||
/*
|
||||
Copyright <2018-2022> <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 "drives/winfsp/remotewinfsp/remote_client.hpp"
|
||||
#include "app_config.hpp"
|
||||
#include "drives/winfsp/remotewinfsp/i_remote_instance.hpp"
|
||||
#include "events/events.hpp"
|
||||
#include "events/event_system.hpp"
|
||||
#include "types/repertory.hpp"
|
||||
#include "utils/path_utils.hpp"
|
||||
|
||||
namespace repertory::remote_winfsp {
|
||||
#define RAISE_REMOTE_WINFSP_CLIENT_EVENT(func, file, ret) \
|
||||
if (config_.get_enable_drive_events() && \
|
||||
(((config_.get_event_level() >= remote_winfsp_client_event::level) && \
|
||||
(ret != STATUS_SUCCESS)) || \
|
||||
(config_.get_event_level() >= event_level::verbose))) \
|
||||
event_system::instance().raise<remote_winfsp_client_event>(func, file, ret)
|
||||
|
||||
// clang-format off
|
||||
E_SIMPLE3(remote_winfsp_client_event, debug, true,
|
||||
std::string, function, func, E_STRING,
|
||||
std::string, api_path, ap, E_STRING,
|
||||
packet::error_type, result, res, E_FROM_INT32
|
||||
);
|
||||
// clang-format on
|
||||
|
||||
remote_client::remote_client(const app_config &config)
|
||||
: config_(config),
|
||||
packet_client_(config.get_remote_host_name_or_ip(), config.get_remote_max_connections(),
|
||||
config.get_remote_port(), config.get_remote_receive_timeout_secs(),
|
||||
config.get_remote_send_timeout_secs(), config.get_remote_token()) {}
|
||||
|
||||
packet::error_type remote_client::winfsp_can_delete(PVOID file_desc, PWSTR file_name) {
|
||||
packet request;
|
||||
request.encode(file_desc);
|
||||
request.encode(file_name);
|
||||
|
||||
std::uint32_t service_flags = 0u;
|
||||
const auto ret = packet_client_.send(__FUNCTION__, request, service_flags);
|
||||
RAISE_REMOTE_WINFSP_CLIENT_EVENT(
|
||||
__FUNCTION__, utils::path::create_api_path(utils::string::to_utf8(file_name)), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::json_create_directory_snapshot(const std::string &path,
|
||||
json &json_data) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
|
||||
packet response;
|
||||
std::uint32_t service_flags = 0u;
|
||||
auto ret = packet_client_.send(__FUNCTION__, request, response, service_flags);
|
||||
if (ret == 0) {
|
||||
ret = packet::decode_json(response, json_data);
|
||||
}
|
||||
|
||||
RAISE_REMOTE_WINFSP_CLIENT_EVENT(__FUNCTION__, path, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::json_read_directory_snapshot(const std::string &path,
|
||||
const remote::file_handle &handle,
|
||||
const std::uint32_t &page,
|
||||
json &json_data) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
request.encode(handle);
|
||||
request.encode(page);
|
||||
|
||||
packet response;
|
||||
std::uint32_t service_flags = 0u;
|
||||
auto ret = packet_client_.send(__FUNCTION__, request, response, service_flags);
|
||||
if (ret == 0) {
|
||||
ret = packet::decode_json(response, json_data);
|
||||
}
|
||||
|
||||
RAISE_REMOTE_WINFSP_CLIENT_EVENT(__FUNCTION__, path, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type
|
||||
remote_client::json_release_directory_snapshot(const std::string &path,
|
||||
const remote::file_handle &handle) {
|
||||
packet request;
|
||||
request.encode(path);
|
||||
request.encode(handle);
|
||||
|
||||
std::uint32_t service_flags = 0u;
|
||||
const auto ret = packet_client_.send(__FUNCTION__, request, service_flags);
|
||||
|
||||
RAISE_REMOTE_WINFSP_CLIENT_EVENT(__FUNCTION__, path, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::winfsp_cleanup(PVOID file_desc, PWSTR file_name, UINT32 flags,
|
||||
BOOLEAN &was_closed) {
|
||||
const auto file_path = get_open_file_path(to_handle(file_desc));
|
||||
was_closed = 0;
|
||||
|
||||
packet request;
|
||||
request.encode(file_desc);
|
||||
request.encode(file_name);
|
||||
request.encode(flags);
|
||||
|
||||
packet response;
|
||||
std::uint32_t service_flags = 0u;
|
||||
auto ret = packet_client_.send(__FUNCTION__, request, response, service_flags);
|
||||
DECODE_OR_IGNORE(&response, was_closed);
|
||||
if (was_closed) {
|
||||
remove_open_info(to_handle(file_desc));
|
||||
}
|
||||
RAISE_REMOTE_WINFSP_CLIENT_EVENT(__FUNCTION__, file_path, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::winfsp_close(PVOID file_desc) {
|
||||
const auto file_path = get_open_file_path(to_handle(file_desc));
|
||||
|
||||
packet request;
|
||||
request.encode(file_desc);
|
||||
|
||||
std::uint32_t service_flags = 0u;
|
||||
const auto ret = packet_client_.send(__FUNCTION__, request, service_flags);
|
||||
if (ret == STATUS_SUCCESS) {
|
||||
remove_open_info(to_handle(file_desc));
|
||||
}
|
||||
RAISE_REMOTE_WINFSP_CLIENT_EVENT(__FUNCTION__, file_path, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::winfsp_create(PWSTR file_name, UINT32 create_options,
|
||||
UINT32 granted_access, UINT32 attributes,
|
||||
UINT64 allocation_size, PVOID *file_desc,
|
||||
remote::file_info *file_info,
|
||||
std::string &normalized_name, BOOLEAN &exists) {
|
||||
packet request;
|
||||
request.encode(file_name);
|
||||
request.encode(create_options);
|
||||
request.encode(granted_access);
|
||||
request.encode(attributes);
|
||||
request.encode(allocation_size);
|
||||
|
||||
packet response;
|
||||
std::uint32_t service_flags = 0u;
|
||||
auto ret = packet_client_.send(__FUNCTION__, request, response, service_flags);
|
||||
if (ret == STATUS_SUCCESS) {
|
||||
HANDLE handle;
|
||||
DECODE_OR_IGNORE(&response, handle);
|
||||
DECODE_OR_IGNORE(&response, *file_info);
|
||||
DECODE_OR_IGNORE(&response, normalized_name);
|
||||
DECODE_OR_IGNORE(&response, exists);
|
||||
if (ret == STATUS_SUCCESS) {
|
||||
*file_desc = reinterpret_cast<PVOID>(handle);
|
||||
set_open_info(to_handle(*file_desc),
|
||||
open_info{0, "", nullptr, utils::string::to_utf8(file_name)});
|
||||
#ifdef _WIN32
|
||||
if (exists) {
|
||||
::SetLastError(ERROR_ALREADY_EXISTS);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
RAISE_REMOTE_WINFSP_CLIENT_EVENT(__FUNCTION__, get_open_file_path(to_handle(*file_desc)), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::winfsp_flush(PVOID file_desc, remote::file_info *file_info) {
|
||||
packet request;
|
||||
request.encode(file_desc);
|
||||
|
||||
packet response;
|
||||
std::uint32_t service_flags = 0u;
|
||||
auto ret = packet_client_.send(__FUNCTION__, request, response, service_flags);
|
||||
DECODE_OR_IGNORE(&response, *file_info);
|
||||
|
||||
RAISE_REMOTE_WINFSP_CLIENT_EVENT(__FUNCTION__, get_open_file_path(to_handle(file_desc)), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::winfsp_get_dir_buffer(PVOID file_desc, PVOID *&ptr) {
|
||||
#ifdef _WIN32
|
||||
if (get_directory_buffer(reinterpret_cast<OSHandle>(file_desc), ptr)) {
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
return STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::winfsp_get_file_info(PVOID file_desc,
|
||||
remote::file_info *file_info) {
|
||||
packet request;
|
||||
request.encode(file_desc);
|
||||
|
||||
packet response;
|
||||
std::uint32_t service_flags = 0u;
|
||||
auto ret = packet_client_.send(__FUNCTION__, request, response, service_flags);
|
||||
DECODE_OR_IGNORE(&response, *file_info);
|
||||
|
||||
RAISE_REMOTE_WINFSP_CLIENT_EVENT(__FUNCTION__, get_open_file_path(to_handle(file_desc)), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::winfsp_get_security_by_name(PWSTR file_name, PUINT32 attributes,
|
||||
std::uint64_t *descriptor_size,
|
||||
std::wstring &string_descriptor) {
|
||||
packet request;
|
||||
request.encode(file_name);
|
||||
request.encode(static_cast<std::uint64_t>(descriptor_size ? *descriptor_size : 0));
|
||||
request.encode(static_cast<std::uint8_t>(attributes != nullptr));
|
||||
|
||||
packet response;
|
||||
std::uint32_t service_flags = 0u;
|
||||
auto ret = packet_client_.send(__FUNCTION__, request, response, service_flags);
|
||||
|
||||
string_descriptor.clear();
|
||||
DECODE_OR_IGNORE(&response, string_descriptor);
|
||||
if (string_descriptor.empty()) {
|
||||
string_descriptor = L"O:BAG:BAD:P(A;;FA;;;SY)(A;;FA;;;BA)(A;;FA;;;WD)";
|
||||
}
|
||||
|
||||
if (attributes) {
|
||||
DECODE_OR_IGNORE(&response, *attributes);
|
||||
}
|
||||
|
||||
RAISE_REMOTE_WINFSP_CLIENT_EVENT(__FUNCTION__, utils::string::to_utf8(file_name), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::winfsp_get_volume_info(UINT64 &total_size, UINT64 &free_size,
|
||||
std::string &volume_label) {
|
||||
packet request;
|
||||
packet response;
|
||||
std::uint32_t service_flags = 0u;
|
||||
auto ret = packet_client_.send(__FUNCTION__, request, response, service_flags);
|
||||
DECODE_OR_IGNORE(&response, total_size);
|
||||
DECODE_OR_IGNORE(&response, free_size);
|
||||
DECODE_OR_IGNORE(&response, volume_label);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::winfsp_mounted(const std::wstring &location) {
|
||||
packet request;
|
||||
request.encode(get_repertory_version());
|
||||
request.encode(location);
|
||||
|
||||
std::uint32_t service_flags = 0u;
|
||||
const auto ret = packet_client_.send(__FUNCTION__, request, service_flags);
|
||||
const auto mountLocation = utils::string::to_utf8(location);
|
||||
event_system::instance().raise<drive_mounted>(mountLocation);
|
||||
|
||||
RAISE_REMOTE_WINFSP_CLIENT_EVENT(__FUNCTION__, mountLocation, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::winfsp_open(PWSTR file_name, UINT32 create_options,
|
||||
UINT32 granted_access, PVOID *file_desc,
|
||||
remote::file_info *file_info,
|
||||
std::string &normalized_name) {
|
||||
packet request;
|
||||
request.encode(file_name);
|
||||
request.encode(create_options);
|
||||
request.encode(granted_access);
|
||||
|
||||
packet response;
|
||||
std::uint32_t service_flags = 0u;
|
||||
auto ret = packet_client_.send(__FUNCTION__, request, response, service_flags);
|
||||
if (ret == STATUS_SUCCESS) {
|
||||
HANDLE handle;
|
||||
DECODE_OR_IGNORE(&response, handle);
|
||||
DECODE_OR_IGNORE(&response, *file_info);
|
||||
DECODE_OR_IGNORE(&response, normalized_name);
|
||||
|
||||
if (ret == STATUS_SUCCESS) {
|
||||
*file_desc = reinterpret_cast<PVOID>(handle);
|
||||
set_open_info(to_handle(*file_desc),
|
||||
open_info{0, "", nullptr, utils::string::to_utf8(file_name)});
|
||||
}
|
||||
}
|
||||
|
||||
RAISE_REMOTE_WINFSP_CLIENT_EVENT(__FUNCTION__, get_open_file_path(to_handle(*file_desc)), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::winfsp_overwrite(PVOID file_desc, UINT32 attributes,
|
||||
BOOLEAN replace_attributes,
|
||||
UINT64 allocation_size,
|
||||
remote::file_info *file_info) {
|
||||
packet request;
|
||||
request.encode(file_desc);
|
||||
request.encode(attributes);
|
||||
request.encode(replace_attributes);
|
||||
request.encode(allocation_size);
|
||||
|
||||
packet response;
|
||||
std::uint32_t service_flags = 0u;
|
||||
auto ret = packet_client_.send(__FUNCTION__, request, response, service_flags);
|
||||
DECODE_OR_IGNORE(&response, *file_info);
|
||||
|
||||
RAISE_REMOTE_WINFSP_CLIENT_EVENT(__FUNCTION__, get_open_file_path(to_handle(file_desc)), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::winfsp_read(PVOID file_desc, PVOID buffer, UINT64 offset,
|
||||
UINT32 length, PUINT32 bytes_transferred) {
|
||||
packet request;
|
||||
request.encode(file_desc);
|
||||
request.encode(offset);
|
||||
request.encode(length);
|
||||
|
||||
packet response;
|
||||
std::uint32_t service_flags = 0u;
|
||||
auto ret = packet_client_.send(__FUNCTION__, request, response, service_flags);
|
||||
DECODE_OR_IGNORE(&response, *bytes_transferred);
|
||||
if (ret == STATUS_SUCCESS) {
|
||||
ret = response.decode(buffer, *bytes_transferred);
|
||||
#ifdef _WIN32
|
||||
if ((ret == STATUS_SUCCESS) && (not *bytes_transferred || (*bytes_transferred != length))) {
|
||||
::SetLastError(ERROR_HANDLE_EOF);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (ret != STATUS_SUCCESS) {
|
||||
RAISE_REMOTE_WINFSP_CLIENT_EVENT(__FUNCTION__, get_open_file_path(to_handle(file_desc)), ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::winfsp_read_directory(PVOID file_desc, PWSTR pattern,
|
||||
PWSTR marker, json &item_list) {
|
||||
packet request;
|
||||
request.encode(file_desc);
|
||||
request.encode(pattern);
|
||||
request.encode(marker);
|
||||
|
||||
packet response;
|
||||
std::uint32_t service_flags = 0u;
|
||||
auto ret = packet_client_.send(__FUNCTION__, request, response, service_flags);
|
||||
if (ret == STATUS_SUCCESS) {
|
||||
ret = packet::decode_json(response, item_list);
|
||||
}
|
||||
|
||||
RAISE_REMOTE_WINFSP_CLIENT_EVENT(__FUNCTION__, get_open_file_path(to_handle(file_desc)), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::winfsp_rename(PVOID file_desc, PWSTR file_name,
|
||||
PWSTR new_file_name, BOOLEAN replace_if_exists) {
|
||||
packet request;
|
||||
request.encode(file_desc);
|
||||
request.encode(file_name);
|
||||
request.encode(new_file_name);
|
||||
request.encode(replace_if_exists);
|
||||
|
||||
std::uint32_t service_flags = 0u;
|
||||
const auto ret = packet_client_.send(__FUNCTION__, request, service_flags);
|
||||
RAISE_REMOTE_WINFSP_CLIENT_EVENT(
|
||||
__FUNCTION__,
|
||||
utils::path::create_api_path(utils::string::to_utf8(file_name)) + "|" +
|
||||
utils::path::create_api_path(utils::string::to_utf8(new_file_name)),
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::winfsp_set_basic_info(PVOID file_desc, UINT32 attributes,
|
||||
UINT64 creation_time,
|
||||
UINT64 last_access_time,
|
||||
UINT64 last_write_time, UINT64 change_time,
|
||||
remote::file_info *file_info) {
|
||||
packet request;
|
||||
request.encode(file_desc);
|
||||
request.encode(attributes);
|
||||
request.encode(creation_time);
|
||||
request.encode(last_access_time);
|
||||
request.encode(last_write_time);
|
||||
request.encode(change_time);
|
||||
|
||||
packet response;
|
||||
std::uint32_t service_flags = 0u;
|
||||
auto ret = packet_client_.send(__FUNCTION__, request, response, service_flags);
|
||||
DECODE_OR_IGNORE(&response, *file_info);
|
||||
|
||||
RAISE_REMOTE_WINFSP_CLIENT_EVENT(__FUNCTION__, get_open_file_path(to_handle(file_desc)), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::winfsp_set_file_size(PVOID file_desc, UINT64 new_size,
|
||||
BOOLEAN set_allocation_size,
|
||||
remote::file_info *file_info) {
|
||||
packet request;
|
||||
request.encode(file_desc);
|
||||
request.encode(new_size);
|
||||
request.encode(set_allocation_size);
|
||||
|
||||
packet response;
|
||||
std::uint32_t service_flags = 0u;
|
||||
auto ret = packet_client_.send(__FUNCTION__, request, response, service_flags);
|
||||
DECODE_OR_IGNORE(&response, *file_info);
|
||||
|
||||
RAISE_REMOTE_WINFSP_CLIENT_EVENT(__FUNCTION__, get_open_file_path(to_handle(file_desc)), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::winfsp_unmounted(const std::wstring &location) {
|
||||
const auto mount_location = utils::string::to_utf8(location);
|
||||
event_system::instance().raise<drive_unmount_pending>(mount_location);
|
||||
packet request;
|
||||
request.encode(location);
|
||||
|
||||
std::uint32_t service_flags = 0u;
|
||||
const auto ret = packet_client_.send(__FUNCTION__, request, service_flags);
|
||||
event_system::instance().raise<drive_unmounted>(mount_location);
|
||||
|
||||
RAISE_REMOTE_WINFSP_CLIENT_EVENT(__FUNCTION__, mount_location, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
packet::error_type remote_client::winfsp_write(PVOID file_desc, PVOID buffer, UINT64 offset,
|
||||
UINT32 length, BOOLEAN write_to_end,
|
||||
BOOLEAN constrained_io, PUINT32 bytes_transferred,
|
||||
remote::file_info *file_info) {
|
||||
packet request;
|
||||
request.encode(file_desc);
|
||||
request.encode(length);
|
||||
request.encode(offset);
|
||||
request.encode(write_to_end);
|
||||
request.encode(constrained_io);
|
||||
if (length) {
|
||||
request.encode(buffer, length);
|
||||
}
|
||||
|
||||
packet response;
|
||||
std::uint32_t service_flags = 0u;
|
||||
auto ret = packet_client_.send(__FUNCTION__, request, response, service_flags);
|
||||
DECODE_OR_IGNORE(&response, *bytes_transferred);
|
||||
DECODE_OR_IGNORE(&response, *file_info);
|
||||
|
||||
if (ret != STATUS_SUCCESS) {
|
||||
RAISE_REMOTE_WINFSP_CLIENT_EVENT(__FUNCTION__, get_open_file_path(to_handle(file_desc)), ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
} // namespace repertory::remote_winfsp
|
1144
src/drives/winfsp/remotewinfsp/remote_server.cpp
Normal file
1144
src/drives/winfsp/remotewinfsp/remote_server.cpp
Normal file
File diff suppressed because it is too large
Load Diff
401
src/drives/winfsp/remotewinfsp/remote_winfsp_drive.cpp
Normal file
401
src/drives/winfsp/remotewinfsp/remote_winfsp_drive.cpp
Normal file
@ -0,0 +1,401 @@
|
||||
/*
|
||||
Copyright <2018-2022> <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.
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
#include "drives/winfsp/remotewinfsp/remote_winfsp_drive.hpp"
|
||||
#include "app_config.hpp"
|
||||
#include "events/consumers/console_consumer.hpp"
|
||||
#include "events/consumers/logging_consumer.hpp"
|
||||
#include "events/events.hpp"
|
||||
#include "platform/platform.hpp"
|
||||
#include "rpc/server/server.hpp"
|
||||
#include "utils/file_utils.hpp"
|
||||
#include "utils/path_utils.hpp"
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
namespace repertory::remote_winfsp {
|
||||
remote_winfsp_drive::winfsp_service::winfsp_service(lock_data &lock, remote_winfsp_drive &drive,
|
||||
std::vector<std::string> drive_args,
|
||||
app_config &config)
|
||||
: Service(&(L"RepertoryRemote_" + utils::string::from_utf8(lock.get_unique_id()))[0u]),
|
||||
config_(config),
|
||||
lock_(lock),
|
||||
drive_(drive),
|
||||
drive_args_(std::move(drive_args)),
|
||||
host_(drive) {}
|
||||
|
||||
NTSTATUS remote_winfsp_drive::winfsp_service::OnStart(ULONG, PWSTR *) {
|
||||
const auto mount_location =
|
||||
utils::path::absolute((drive_args_.size() > 1u) ? drive_args_[1u] : "");
|
||||
const auto drive_letter = ((mount_location.size() == 2u) ||
|
||||
((mount_location.size() == 3u) && (mount_location[2u] == '\\'))) &&
|
||||
(mount_location[1u] == ':');
|
||||
|
||||
auto ret = drive_letter ? STATUS_DEVICE_BUSY : STATUS_NOT_SUPPORTED;
|
||||
if ((drive_letter && not utils::file::is_directory(mount_location))) {
|
||||
auto unicode_mount_location = utils::string::from_utf8(mount_location);
|
||||
host_.SetFileSystemName(&unicode_mount_location[0u]);
|
||||
ret = host_.Mount(&unicode_mount_location[0u]);
|
||||
} else {
|
||||
std::cerr << (drive_letter ? "Mount location in use: " : "Mount location not supported: ")
|
||||
<< mount_location << std::endl;
|
||||
}
|
||||
|
||||
if (ret != STATUS_SUCCESS) {
|
||||
event_system::instance().raise<drive_mount_failed>(mount_location, std::to_string(ret));
|
||||
lock_.set_mount_state(false, "", -1);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
NTSTATUS remote_winfsp_drive::winfsp_service::OnStop() {
|
||||
host_.Unmount();
|
||||
lock_.set_mount_state(false, "", -1);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
remote_winfsp_drive::remote_winfsp_drive(app_config &config, lock_data &lockData,
|
||||
remote_instance_factory factory)
|
||||
: FileSystemBase(), config_(config), lock_(lockData), factory_(factory) {
|
||||
E_SUBSCRIBE_EXACT(unmount_requested, [this](const unmount_requested &e) {
|
||||
std::thread([this]() { this->shutdown(); }).detach();
|
||||
});
|
||||
}
|
||||
|
||||
NTSTATUS remote_winfsp_drive::CanDelete(PVOID file_node, PVOID file_desc, PWSTR file_name) {
|
||||
return remote_instance_->winfsp_can_delete(file_desc, file_name);
|
||||
}
|
||||
|
||||
VOID remote_winfsp_drive::Cleanup(PVOID file_node, PVOID file_desc, PWSTR file_name, ULONG flags) {
|
||||
BOOLEAN was_closed;
|
||||
remote_instance_->winfsp_cleanup(file_desc, file_name, flags, was_closed);
|
||||
}
|
||||
|
||||
VOID remote_winfsp_drive::Close(PVOID file_node, PVOID file_desc) {
|
||||
remote_instance_->winfsp_close(file_desc);
|
||||
}
|
||||
|
||||
NTSTATUS remote_winfsp_drive::Create(PWSTR file_name, UINT32 create_options, UINT32 granted_access,
|
||||
UINT32 attributes, PSECURITY_DESCRIPTOR descriptor,
|
||||
UINT64 allocation_size, PVOID *file_node, PVOID *file_desc,
|
||||
OpenFileInfo *ofi) {
|
||||
remote::file_info fi{};
|
||||
std::string normalized_name;
|
||||
BOOLEAN exists = 0;
|
||||
const auto ret =
|
||||
remote_instance_->winfsp_create(file_name, create_options, granted_access, attributes,
|
||||
allocation_size, file_desc, &fi, normalized_name, exists);
|
||||
if (ret == STATUS_SUCCESS) {
|
||||
SetFileInfo(ofi->FileInfo, fi);
|
||||
const auto filePath = utils::string::from_utf8(normalized_name);
|
||||
wcsncpy(ofi->NormalizedName, &filePath[0], wcslen(&filePath[0]));
|
||||
ofi->NormalizedNameSize = (UINT16)(wcslen(&filePath[0]) * sizeof(WCHAR));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
NTSTATUS remote_winfsp_drive::Flush(PVOID file_node, PVOID file_desc, FileInfo *file_info) {
|
||||
remote::file_info fi{};
|
||||
const auto ret = remote_instance_->winfsp_flush(file_desc, &fi);
|
||||
SetFileInfo(*file_info, fi);
|
||||
return ret;
|
||||
}
|
||||
|
||||
NTSTATUS remote_winfsp_drive::GetFileInfo(PVOID file_node, PVOID file_desc, FileInfo *file_info) {
|
||||
remote::file_info fi{};
|
||||
const auto ret = remote_instance_->winfsp_get_file_info(file_desc, &fi);
|
||||
SetFileInfo(*file_info, fi);
|
||||
return ret;
|
||||
}
|
||||
|
||||
NTSTATUS remote_winfsp_drive::GetSecurityByName(PWSTR file_name, PUINT32 attributes,
|
||||
PSECURITY_DESCRIPTOR descriptor,
|
||||
SIZE_T *descriptor_size) {
|
||||
std::wstring string_descriptor;
|
||||
std::uint64_t sds = descriptor_size ? *descriptor_size : 0;
|
||||
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;
|
||||
if (::ConvertStringSecurityDescriptorToSecurityDescriptorW(&string_descriptor[0u],
|
||||
SDDL_REVISION_1, &sd, &sz2)) {
|
||||
if (sz2 > *descriptor_size) {
|
||||
ret = STATUS_BUFFER_TOO_SMALL;
|
||||
} else {
|
||||
::CopyMemory(descriptor, sd, sz2);
|
||||
}
|
||||
*descriptor_size = sz2;
|
||||
::LocalFree(sd);
|
||||
} else {
|
||||
ret = FspNtStatusFromWin32(::GetLastError());
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
NTSTATUS remote_winfsp_drive::GetVolumeInfo(VolumeInfo *volume_info) {
|
||||
std::string volume_label;
|
||||
const auto ret = remote_instance_->winfsp_get_volume_info(volume_info->TotalSize,
|
||||
volume_info->FreeSize, volume_label);
|
||||
if (ret == STATUS_SUCCESS) {
|
||||
const auto byte_size = static_cast<UINT16>(volume_label.size() * sizeof(WCHAR));
|
||||
wcscpy_s(&volume_info->VolumeLabel[0u], 32, &utils::string::from_utf8(volume_label)[0u]);
|
||||
volume_info->VolumeLabelLength = std::min(static_cast<UINT16>(64u), byte_size);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
NTSTATUS remote_winfsp_drive::Init(PVOID host) {
|
||||
auto *file_system_host = reinterpret_cast<FileSystemHost *>(host);
|
||||
file_system_host->SetPrefix(
|
||||
&(L"\\repertory\\" + std::wstring(file_system_host->FileSystemName()).substr(0, 1))[0]);
|
||||
file_system_host->SetFileSystemName(REPERTORY_W);
|
||||
file_system_host->SetFlushAndPurgeOnCleanup(TRUE);
|
||||
file_system_host->SetReparsePoints(FALSE);
|
||||
file_system_host->SetReparsePointsAccessCheck(FALSE);
|
||||
file_system_host->SetSectorSize(WINFSP_ALLOCATION_UNIT);
|
||||
file_system_host->SetSectorsPerAllocationUnit(1);
|
||||
file_system_host->SetFileInfoTimeout(1000);
|
||||
file_system_host->SetCaseSensitiveSearch(FALSE);
|
||||
file_system_host->SetCasePreservedNames(TRUE);
|
||||
file_system_host->SetNamedStreams(FALSE);
|
||||
file_system_host->SetUnicodeOnDisk(TRUE);
|
||||
file_system_host->SetPersistentAcls(FALSE);
|
||||
file_system_host->SetPostCleanupWhenModifiedOnly(TRUE);
|
||||
file_system_host->SetPassQueryDirectoryPattern(FALSE);
|
||||
file_system_host->SetVolumeCreationTime(utils::get_file_time_now());
|
||||
file_system_host->SetVolumeSerialNumber(0);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int remote_winfsp_drive::mount(const std::vector<std::string> &drive_args) {
|
||||
std::vector<std::string> parsed_drive_args;
|
||||
|
||||
const auto force_no_console = utils::collection_includes(drive_args, "-nc");
|
||||
|
||||
auto enable_console = false;
|
||||
for (const auto &arg : drive_args) {
|
||||
if (arg == "-f") {
|
||||
if (not force_no_console) {
|
||||
enable_console = true;
|
||||
}
|
||||
} else if (arg != "-nc") {
|
||||
parsed_drive_args.emplace_back(arg);
|
||||
}
|
||||
}
|
||||
|
||||
logging_consumer l(config_.get_log_directory(), config_.get_event_level());
|
||||
std::unique_ptr<console_consumer> c;
|
||||
if (enable_console) {
|
||||
c = std::make_unique<console_consumer>();
|
||||
}
|
||||
event_system::instance().start();
|
||||
const auto ret = winfsp_service(lock_, *this, parsed_drive_args, config_).Run();
|
||||
event_system::instance().raise<drive_mount_result>(std::to_string(ret));
|
||||
event_system::instance().stop();
|
||||
c.reset();
|
||||
return static_cast<int>(ret);
|
||||
}
|
||||
|
||||
NTSTATUS remote_winfsp_drive::Mounted(PVOID host) {
|
||||
auto *file_system_host = reinterpret_cast<FileSystemHost *>(host);
|
||||
remote_instance_ = factory_();
|
||||
server_ = std::make_unique<server>(config_);
|
||||
server_->start();
|
||||
mount_location_ = utils::string::to_utf8(file_system_host->MountPoint());
|
||||
lock_.set_mount_state(true, mount_location_, ::GetCurrentProcessId());
|
||||
return remote_instance_->winfsp_mounted(file_system_host->MountPoint());
|
||||
}
|
||||
|
||||
NTSTATUS remote_winfsp_drive::Open(PWSTR file_name, UINT32 create_options, UINT32 granted_access,
|
||||
PVOID *file_node, PVOID *file_desc, OpenFileInfo *ofi) {
|
||||
remote::file_info fi{};
|
||||
std::string normalize_name;
|
||||
const auto ret = remote_instance_->winfsp_open(file_name, create_options, granted_access,
|
||||
file_desc, &fi, normalize_name);
|
||||
if (ret == STATUS_SUCCESS) {
|
||||
SetFileInfo(ofi->FileInfo, fi);
|
||||
const auto filePath = utils::string::from_utf8(normalize_name);
|
||||
wcsncpy(ofi->NormalizedName, &filePath[0], wcslen(&filePath[0]));
|
||||
ofi->NormalizedNameSize = (UINT16)(wcslen(&filePath[0]) * sizeof(WCHAR));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
NTSTATUS remote_winfsp_drive::Overwrite(PVOID file_node, PVOID file_desc, UINT32 attributes,
|
||||
BOOLEAN replace_attributes, UINT64 allocation_size,
|
||||
FileInfo *file_info) {
|
||||
remote::file_info fi{};
|
||||
const auto ret = remote_instance_->winfsp_overwrite(file_desc, attributes, replace_attributes,
|
||||
allocation_size, &fi);
|
||||
SetFileInfo(*file_info, fi);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void remote_winfsp_drive::PopulateFileInfo(const json &item, FSP_FSCTL_FILE_INFO &file_info) {
|
||||
const auto di = directory_item::from_json(item);
|
||||
file_info.FileSize = di.directory ? 0 : di.size;
|
||||
file_info.AllocationSize =
|
||||
utils::divide_with_ceiling(file_info.FileSize, WINFSP_ALLOCATION_UNIT) *
|
||||
WINFSP_ALLOCATION_UNIT;
|
||||
file_info.ChangeTime = utils::get_changed_time_from_meta(di.meta);
|
||||
file_info.CreationTime = utils::get_creation_time_from_meta(di.meta);
|
||||
file_info.FileAttributes = utils::get_attributes_from_meta(di.meta);
|
||||
file_info.HardLinks = 0;
|
||||
file_info.IndexNumber = 0;
|
||||
file_info.LastAccessTime = utils::get_accessed_time_from_meta(di.meta);
|
||||
file_info.LastWriteTime = utils::get_written_time_from_meta(di.meta);
|
||||
file_info.ReparseTag = 0;
|
||||
file_info.EaSize = 0;
|
||||
}
|
||||
|
||||
NTSTATUS remote_winfsp_drive::Read(PVOID file_node, PVOID file_desc, PVOID buffer, UINT64 offset,
|
||||
ULONG length, PULONG bytes_transferred) {
|
||||
return remote_instance_->winfsp_read(file_desc, buffer, offset, length,
|
||||
reinterpret_cast<PUINT32>(bytes_transferred));
|
||||
}
|
||||
|
||||
NTSTATUS remote_winfsp_drive::ReadDirectory(PVOID file_node, PVOID file_desc, PWSTR pattern,
|
||||
PWSTR marker, PVOID buffer, ULONG buffer_length,
|
||||
PULONG bytes_transferred) {
|
||||
json itemList;
|
||||
NTSTATUS ret = remote_instance_->winfsp_read_directory(file_desc, pattern, marker, itemList);
|
||||
if (ret == STATUS_SUCCESS) {
|
||||
PVOID *directory_buffer = nullptr;
|
||||
if ((ret = remote_instance_->winfsp_get_dir_buffer(file_desc, directory_buffer)) ==
|
||||
STATUS_SUCCESS) {
|
||||
if (FspFileSystemAcquireDirectoryBuffer(directory_buffer,
|
||||
static_cast<BOOLEAN>(nullptr == marker), &ret)) {
|
||||
auto item_found = false;
|
||||
for (const auto &item : itemList) {
|
||||
const auto item_path = item["path"].get<std::string>();
|
||||
const 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) +
|
||||
((MAX_PATH + 1) * sizeof(WCHAR))];
|
||||
FSP_FSCTL_DIR_INFO D;
|
||||
} directory_info_buffer;
|
||||
|
||||
auto *directory_info = &directory_info_buffer.D;
|
||||
::ZeroMemory(directory_info, sizeof(*directory_info));
|
||||
directory_info->Size = static_cast<UINT16>(
|
||||
FIELD_OFFSET(FSP_FSCTL_DIR_INFO, FileNameBuf) +
|
||||
(std::min((size_t)MAX_PATH, display_name.size()) * sizeof(WCHAR)));
|
||||
|
||||
if (not item["meta"].empty() || ((item_path != ".") && (item_path != ".."))) {
|
||||
PopulateFileInfo(item, directory_info->FileInfo);
|
||||
}
|
||||
if (ret == STATUS_SUCCESS) {
|
||||
::wcscpy_s(&directory_info->FileNameBuf[0], MAX_PATH, &display_name[0]);
|
||||
|
||||
FspFileSystemFillDirectoryBuffer(directory_buffer, directory_info, &ret);
|
||||
if (ret != STATUS_SUCCESS) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
item_found = display_name == std::wstring(marker);
|
||||
}
|
||||
}
|
||||
|
||||
FspFileSystemReleaseDirectoryBuffer(directory_buffer);
|
||||
}
|
||||
|
||||
if ((ret == STATUS_SUCCESS) || (ret == STATUS_NO_MORE_FILES)) {
|
||||
FspFileSystemReadDirectoryBuffer(directory_buffer, marker, buffer, buffer_length,
|
||||
reinterpret_cast<PULONG>(bytes_transferred));
|
||||
ret = STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
NTSTATUS remote_winfsp_drive::Rename(PVOID file_node, PVOID file_desc, PWSTR file_name,
|
||||
PWSTR new_file_name, BOOLEAN replace_if_exists) {
|
||||
return remote_instance_->winfsp_rename(file_desc, file_name, new_file_name, replace_if_exists);
|
||||
}
|
||||
|
||||
NTSTATUS remote_winfsp_drive::SetBasicInfo(PVOID file_node, PVOID file_desc, UINT32 attributes,
|
||||
UINT64 creation_time, UINT64 last_access_time,
|
||||
UINT64 last_write_time, UINT64 change_time,
|
||||
FileInfo *file_info) {
|
||||
remote::file_info fi{};
|
||||
const auto ret = remote_instance_->winfsp_set_basic_info(
|
||||
file_desc, attributes, creation_time, last_access_time, last_write_time, change_time, &fi);
|
||||
SetFileInfo(*file_info, fi);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void remote_winfsp_drive::SetFileInfo(FileInfo &dest, const remote::file_info &src) {
|
||||
dest.FileAttributes = src.FileAttributes;
|
||||
dest.ReparseTag = src.ReparseTag;
|
||||
dest.AllocationSize = src.AllocationSize;
|
||||
dest.FileSize = src.FileSize;
|
||||
dest.CreationTime = src.CreationTime;
|
||||
dest.LastAccessTime = src.LastAccessTime;
|
||||
dest.LastWriteTime = src.LastWriteTime;
|
||||
dest.ChangeTime = src.ChangeTime;
|
||||
dest.IndexNumber = src.IndexNumber;
|
||||
dest.HardLinks = src.HardLinks;
|
||||
dest.EaSize = src.EaSize;
|
||||
}
|
||||
|
||||
NTSTATUS remote_winfsp_drive::SetFileSize(PVOID file_node, PVOID file_desc, UINT64 new_size,
|
||||
BOOLEAN set_allocation_size, FileInfo *file_info) {
|
||||
remote::file_info fi{};
|
||||
const auto ret =
|
||||
remote_instance_->winfsp_set_file_size(file_desc, new_size, set_allocation_size, &fi);
|
||||
SetFileInfo(*file_info, fi);
|
||||
return ret;
|
||||
}
|
||||
|
||||
VOID remote_winfsp_drive::Unmounted(PVOID host) {
|
||||
server_->stop();
|
||||
server_.reset();
|
||||
auto *fileSystemHost = reinterpret_cast<FileSystemHost *>(host);
|
||||
lock_.set_mount_state(false, "", -1);
|
||||
remote_instance_->winfsp_unmounted(fileSystemHost->MountPoint());
|
||||
remote_instance_.reset();
|
||||
mount_location_ = "";
|
||||
}
|
||||
|
||||
NTSTATUS remote_winfsp_drive::Write(PVOID file_node, PVOID file_desc, PVOID buffer, UINT64 offset,
|
||||
ULONG length, BOOLEAN write_to_end, BOOLEAN constrained_io,
|
||||
PULONG bytes_transferred, FileInfo *file_info) {
|
||||
remote::file_info fi{};
|
||||
const auto ret = remote_instance_->winfsp_write(
|
||||
file_desc, buffer, offset, length, write_to_end, constrained_io,
|
||||
reinterpret_cast<PUINT32>(bytes_transferred), &fi);
|
||||
SetFileInfo(*file_info, fi);
|
||||
return ret;
|
||||
}
|
||||
} // namespace repertory::remote_winfsp
|
||||
|
||||
#endif // _WIN32
|
1039
src/drives/winfsp/winfsp_drive.cpp
Normal file
1039
src/drives/winfsp/winfsp_drive.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user