Some checks failed
BlockStorage/repertory_osx/pipeline/head This commit looks good
BlockStorage/repertory_windows/pipeline/head This commit looks good
BlockStorage/repertory/pipeline/head There was a failure building this commit
BlockStorage/repertory_linux_builds/pipeline/head This commit looks good
BlockStorage/repertory_osx_builds/pipeline/head There was a failure building this commit
### Issues * \#1 \[bug\] Unable to mount S3 due to 'item_not_found' exception * \#2 Require bucket name for S3 mounts * \#3 \[bug\] File size is not being updated in S3 mount * \#4 Upgrade to libfuse-3.x.x * \#5 Switch to renterd for Sia support * \#6 Switch to cpp-httplib to further reduce dependencies * \#7 Remove global_data and calculate used disk space per provider * \#8 Switch to libcurl for S3 mount support ### Changes from v1.x.x * Added read-only encrypt provider * Pass-through mount point that transparently encrypts source data using `XChaCha20-Poly1305` * Added S3 encryption support via `XChaCha20-Poly1305` * Added replay protection to remote mounts * Added support base64 writes in remote FUSE * Created static linked Linux binaries for `amd64` and `aarch64` using `musl-libc` * Removed legacy Sia renter support * Removed Skynet support * Fixed multiple remote mount WinFSP API issues on \*NIX servers * Implemented chunked read and write * Writes for non-cached files are performed in chunks of 8Mib * Removed `repertory-ui` support * Removed `FreeBSD` support * Switched to `libsodium` over `CryptoPP` * Switched to `XChaCha20-Poly1305` for remote mounts * Updated `GoogleTest` to v1.14.0 * Updated `JSON for Modern C++` to v3.11.2 * Updated `OpenSSL` to v1.1.1w * Updated `RocksDB` to v8.5.3 * Updated `WinFSP` to 2023 * Updated `boost` to v1.78.0 * Updated `cURL` to v8.3.0 * Updated `zlib` to v1.3 * Use `upload_manager` for all providers * Adds a delay to uploads to prevent excessive API calls * Supports re-upload after mount restart for incomplete uploads * NOTE: Uploads for all providers are full file (no resume support) * Multipart upload support is planned for S3 Reviewed-on: #9
1278 lines
47 KiB
C++
1278 lines
47 KiB
C++
/*
|
|
Copyright <2018-2023> <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.
|
|
*/
|
|
// NOTE: Most of the WinFSP pass-through code has been modified from:
|
|
// https://github.com/billziss-gh/winfsp/blob/master/tst/passthrough-cpp/passthrough-cpp.cpp
|
|
#ifdef _WIN32
|
|
|
|
#include "drives/winfsp/remotewinfsp/remote_server.hpp"
|
|
|
|
#include "app_config.hpp"
|
|
#include "comm/packet/client_pool.hpp"
|
|
#include "comm/packet/packet_server.hpp"
|
|
#include "drives/directory_iterator.hpp"
|
|
#include "drives/fuse/remotefuse/i_remote_instance.hpp"
|
|
#include "drives/remote/remote_open_file_table.hpp"
|
|
#include "drives/winfsp/remotewinfsp/i_remote_instance.hpp"
|
|
#include "events/event_system.hpp"
|
|
#include "events/events.hpp"
|
|
#include "types/remote.hpp"
|
|
#include "types/repertory.hpp"
|
|
#include "utils/file_utils.hpp"
|
|
#include "utils/path_utils.hpp"
|
|
|
|
#ifndef _SH_DENYNO
|
|
#define _SH_DENYRW 0x10 // deny read/write mode
|
|
#define _SH_DENYWR 0x20 // deny write mode
|
|
#define _SH_DENYRD 0x30 // deny read mode
|
|
#define _SH_DENYNO 0x40 // deny none mode
|
|
#define _SH_SECURE 0x80 // secure mode
|
|
#endif
|
|
|
|
namespace repertory::remote_winfsp {
|
|
#define RAISE_REMOTE_WINFSP_SERVER_EVENT(func, file, ret) \
|
|
if (config_.get_enable_drive_events() && \
|
|
(((config_.get_event_level() >= remote_winfsp_server_event::level) && \
|
|
(ret != STATUS_SUCCESS)) || \
|
|
(config_.get_event_level() >= event_level::verbose))) \
|
|
event_system::instance().raise<remote_winfsp_server_event>(func, file, ret)
|
|
|
|
// clang-format off
|
|
E_SIMPLE3(remote_winfsp_server_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
|
|
|
|
auto remote_server::construct_path(std::string path) -> std::string {
|
|
path = utils::path::combine(mount_location_, {path});
|
|
if (path == mount_location_) {
|
|
path += '\\';
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
auto remote_server::populate_file_info(const std::string &api_path,
|
|
remote::file_info &file_info)
|
|
-> packet::error_type {
|
|
return (drive_.populate_file_info(api_path, file_info) == api_error::success)
|
|
? STATUS_SUCCESS
|
|
: STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
|
|
void remote_server::populate_stat(const char *path, bool directory,
|
|
remote::stat &st, const struct _stat64 &st1) {
|
|
st.st_nlink = static_cast<remote::file_nlink>(
|
|
directory ? 2 + drive_.get_directory_item_count(
|
|
utils::path::create_api_path(path))
|
|
: 1);
|
|
st.st_atimespec = utils::time64_to_unix_time(st1.st_atime);
|
|
st.st_birthtimespec = utils::time64_to_unix_time(st1.st_ctime);
|
|
st.st_ctimespec = utils::time64_to_unix_time(st1.st_ctime);
|
|
st.st_mtimespec = utils::time64_to_unix_time(st1.st_mtime);
|
|
st.st_size = st1.st_size;
|
|
st.st_mode = st1.st_mode;
|
|
}
|
|
|
|
// FUSE Layer
|
|
auto remote_server::fuse_access(const char *path, const std::int32_t &mask)
|
|
-> packet::error_type {
|
|
const auto file_path = construct_path(path);
|
|
const auto windows_mask = utils::unix_access_mask_to_windows(mask);
|
|
const auto res = _access(file_path.c_str(), windows_mask);
|
|
const auto ret = ((res < 0) ? -errno : 0);
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::fuse_chflags(const char *path, std::uint32_t /*flags*/)
|
|
-> packet::error_type {
|
|
const auto file_path = construct_path(path);
|
|
const auto ret = STATUS_NOT_IMPLEMENTED;
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::fuse_chmod(const char *path,
|
|
const remote::file_mode & /*mode*/)
|
|
-> packet::error_type {
|
|
const auto file_path = construct_path(path);
|
|
const auto ret = STATUS_NOT_IMPLEMENTED;
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::fuse_chown(const char *path,
|
|
const remote::user_id & /*uid*/,
|
|
const remote::group_id & /*gid*/)
|
|
-> packet::error_type {
|
|
const auto file_path = construct_path(path);
|
|
const auto ret = STATUS_NOT_IMPLEMENTED;
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::fuse_destroy() -> packet::error_type {
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, "", 0);
|
|
return 0;
|
|
}
|
|
|
|
/*packet::error_type remote_server::fuse_fallocate(const char *path, const
|
|
std::int32_t &mode, const remote::file_offset &offset, const remote::file_offset
|
|
&length, const remote::file_handle &handle) { const auto file_path =
|
|
construct_path(path); auto res = HasOpenFileCompatInfo(handle, EBADF); if (res
|
|
== 0) { res = _chsize_s(static_cast<int>(handle), offset + length);
|
|
}
|
|
|
|
const auto ret = ((res < 0) ? -errno : 0);
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
return ret;
|
|
}*/
|
|
|
|
auto remote_server::fuse_fgetattr(const char *path, remote::stat &st,
|
|
bool &directory,
|
|
const remote::file_handle &handle)
|
|
-> packet::error_type {
|
|
const auto file_path = construct_path(path);
|
|
memset(&st, 0, sizeof(remote::stat));
|
|
|
|
auto res = has_compat_open_info(handle, EBADF);
|
|
if (res == 0) {
|
|
directory = utils::file::is_directory(file_path);
|
|
struct _stat64 st1 {};
|
|
if ((res = _fstat64(static_cast<int>(handle), &st1)) == 0) {
|
|
populate_stat(path, directory, st, st1);
|
|
}
|
|
}
|
|
|
|
const auto ret = ((res < 0) ? -errno : 0);
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::fuse_fsetattr_x(const char *path,
|
|
const remote::setattr_x & /*attr*/,
|
|
const remote::file_handle & /*handle*/)
|
|
-> packet::error_type {
|
|
const auto file_path = construct_path(path);
|
|
const auto ret = STATUS_NOT_IMPLEMENTED;
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::fuse_fsync(const char *path,
|
|
const std::int32_t & /*datasync*/,
|
|
const remote::file_handle &handle)
|
|
-> packet::error_type {
|
|
const auto file_path = construct_path(path);
|
|
|
|
auto res = has_compat_open_info(handle, EBADF);
|
|
if (res == 0) {
|
|
res = -1;
|
|
errno = EBADF;
|
|
auto os_handle =
|
|
reinterpret_cast<HANDLE>(_get_osfhandle(static_cast<int>(handle)));
|
|
if (os_handle != INVALID_HANDLE_VALUE) {
|
|
errno = EFAULT;
|
|
if (::FlushFileBuffers(os_handle)) {
|
|
res = 0;
|
|
errno = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
const auto ret = ((res < 0) ? -errno : 0);
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::fuse_ftruncate(const char *path,
|
|
const remote::file_offset &size,
|
|
const remote::file_handle &handle)
|
|
-> packet::error_type {
|
|
const auto file_path = construct_path(path);
|
|
|
|
auto res = has_compat_open_info(handle, EBADF);
|
|
if (res == 0) {
|
|
res = -1;
|
|
errno = EBADF;
|
|
auto os_handle =
|
|
reinterpret_cast<HANDLE>(_get_osfhandle(static_cast<int>(handle)));
|
|
if (os_handle != INVALID_HANDLE_VALUE) {
|
|
errno = EFAULT;
|
|
FILE_END_OF_FILE_INFO fi{};
|
|
fi.EndOfFile.QuadPart = size;
|
|
if (::SetFileInformationByHandle(os_handle, FileEndOfFileInfo, &fi,
|
|
sizeof(FILE_END_OF_FILE_INFO))) {
|
|
res = 0;
|
|
errno = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
const auto ret = ((res < 0) ? -errno : 0);
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::fuse_getattr(const char *path, remote::stat &st,
|
|
bool &directory) -> packet::error_type {
|
|
const auto file_path = construct_path(path);
|
|
memset(&st, 0, sizeof(remote::stat));
|
|
|
|
directory = utils::file::is_directory(file_path);
|
|
|
|
struct _stat64 st1 {};
|
|
const auto res = _stat64(file_path.c_str(), &st1);
|
|
if (res == 0) {
|
|
populate_stat(path, directory, st, st1);
|
|
}
|
|
|
|
const auto ret = ((res < 0) ? -errno : 0);
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
/*packet::error_type remote_server::fuse_getxattr(const char *path, const char
|
|
*name, char *value, const remote::file_size &size) { const auto file_path =
|
|
construct_path(path); const auto ret = STATUS_NOT_IMPLEMENTED;
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret); return ret;
|
|
}
|
|
|
|
packet::error_type remote_server::fuse_getxattr_osx(const char *path, const char
|
|
*name, char *value, const remote::file_size &size, std::uint32_t position) {
|
|
const auto file_path = construct_path(path); const auto ret =
|
|
STATUS_NOT_IMPLEMENTED; RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__,
|
|
file_path, ret); return ret;
|
|
}*/
|
|
|
|
auto remote_server::fuse_getxtimes(const char *path,
|
|
remote::file_time & /*bkuptime*/,
|
|
remote::file_time & /*crtime*/)
|
|
-> packet::error_type {
|
|
const auto file_path = construct_path(path);
|
|
const auto ret = STATUS_NOT_IMPLEMENTED;
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::fuse_init() -> packet::error_type {
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, "", 0);
|
|
return 0;
|
|
}
|
|
|
|
/*packet::error_type remote_server::fuse_listxattr(const char *path, char
|
|
*buffer, const remote::file_size &size) { const auto file_path =
|
|
construct_path(path); const auto ret = STATUS_NOT_IMPLEMENTED;
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
return ret;
|
|
}*/
|
|
|
|
auto remote_server::fuse_mkdir(const char *path,
|
|
const remote::file_mode & /*mode*/)
|
|
-> packet::error_type {
|
|
const auto file_path = construct_path(path);
|
|
const auto res = _mkdir(file_path.c_str());
|
|
const auto ret = ((res < 0) ? -errno : 0);
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::fuse_opendir(const char *path, remote::file_handle &handle)
|
|
-> packet::error_type {
|
|
const auto file_path = construct_path(path);
|
|
const auto unicode_file_path = utils::string::from_utf8(file_path);
|
|
auto res = -1;
|
|
errno = ENOENT;
|
|
|
|
if (::PathIsDirectoryW(unicode_file_path.c_str())) {
|
|
auto list = drive_.get_directory_items(utils::path::create_api_path(path));
|
|
handle = static_cast<remote::file_handle>(reinterpret_cast<std::uintptr_t>(
|
|
new directory_iterator(std::move(list))));
|
|
res = 0;
|
|
errno = 0;
|
|
}
|
|
|
|
const auto ret = ((res < 0) ? -errno : 0);
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::fuse_create(const char *path, const remote::file_mode &mode,
|
|
const remote::open_flags &flags,
|
|
remote::file_handle &handle)
|
|
-> packet::error_type {
|
|
auto ret = -1;
|
|
|
|
const auto file_path = construct_path(path);
|
|
const auto directory_op = ((flags & remote::open_flags::directory) ==
|
|
remote::open_flags::directory);
|
|
std::int32_t perms = 0;
|
|
const auto open_flags =
|
|
utils::unix_open_flags_to_flags_and_perms(mode, flags, perms);
|
|
const auto create_op = (open_flags & _O_CREAT);
|
|
|
|
if (create_op && directory_op) {
|
|
ret = -EINVAL;
|
|
} else if (directory_op) {
|
|
ret = -EACCES;
|
|
} else {
|
|
int fd = -1;
|
|
const auto res =
|
|
_sopen_s(&fd, file_path.c_str(), open_flags, _SH_DENYNO, perms);
|
|
if (res == 0) {
|
|
handle = fd;
|
|
ret = 0;
|
|
set_compat_open_info(handle, file_path);
|
|
} else {
|
|
ret = -errno;
|
|
}
|
|
}
|
|
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::fuse_open(const char *path, const remote::open_flags &flags,
|
|
remote::file_handle &handle)
|
|
-> packet::error_type {
|
|
const auto file_path = construct_path(path);
|
|
auto res = -1;
|
|
|
|
if ((flags & remote::open_flags::directory) ==
|
|
remote::open_flags::directory) {
|
|
errno = EACCES;
|
|
} else {
|
|
std::int32_t perms = 0;
|
|
const auto open_flags =
|
|
utils::unix_open_flags_to_flags_and_perms(0, flags, perms) &
|
|
(~_O_CREAT);
|
|
int fd = -1;
|
|
res = _sopen_s(&fd, file_path.c_str(), open_flags, _SH_DENYNO, 0);
|
|
if (res == 0) {
|
|
handle = fd;
|
|
set_compat_open_info(handle, file_path);
|
|
} else {
|
|
res = -1;
|
|
}
|
|
}
|
|
|
|
const auto ret = ((res < 0) ? -errno : 0);
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::fuse_read(const char *path, char *buffer,
|
|
const remote::file_size &readSize,
|
|
const remote::file_offset &readOffset,
|
|
const remote::file_handle &handle)
|
|
-> packet::error_type {
|
|
const auto file_path = construct_path(path);
|
|
auto &b = *reinterpret_cast<data_buffer *>(buffer);
|
|
auto res = 0;
|
|
if (readSize > std::numeric_limits<std::size_t>::max()) {
|
|
res = -1;
|
|
errno = ERANGE;
|
|
} else if ((res = has_compat_open_info(handle, EBADF)) == 0) {
|
|
res = -1;
|
|
errno = EBADF;
|
|
auto os_handle =
|
|
reinterpret_cast<HANDLE>(_get_osfhandle(static_cast<int>(handle)));
|
|
if (os_handle != INVALID_HANDLE_VALUE) {
|
|
errno = EFAULT;
|
|
|
|
auto file = native_file::attach(os_handle);
|
|
std::uint64_t file_size = 0u;
|
|
if (file->get_file_size(file_size)) {
|
|
b.resize(utils::calculate_read_size(
|
|
file_size, static_cast<std::size_t>(readSize), readOffset));
|
|
if (b.size() > 0) {
|
|
std::size_t bytes_read = 0u;
|
|
if (file->read_bytes(&b[0u], b.size(), readOffset, bytes_read)) {
|
|
res = 0;
|
|
errno = 0;
|
|
}
|
|
} else {
|
|
res = 0;
|
|
errno = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const auto ret = ((res < 0) ? -errno : static_cast<int>(b.size()));
|
|
if (ret < 0) {
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::fuse_rename(const char *from, const char *to)
|
|
-> packet::error_type {
|
|
const auto from_path = utils::path::combine(mount_location_, {from});
|
|
const auto to_path = utils::path::combine(mount_location_, {to});
|
|
const auto res = rename(from_path.c_str(), to_path.c_str());
|
|
|
|
const auto ret = ((res < 0) ? -errno : 0);
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, from + std::string("|") + to,
|
|
ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::fuse_write(const char *path, const char *buffer,
|
|
const remote::file_size &write_size,
|
|
const remote::file_offset &write_offset,
|
|
const remote::file_handle &handle)
|
|
-> packet::error_type {
|
|
const auto file_path = construct_path(path);
|
|
std::size_t bytes_written = 0u;
|
|
auto res = 0;
|
|
if (write_size > std::numeric_limits<std::size_t>::max()) {
|
|
res = -1;
|
|
errno = ERANGE;
|
|
} else {
|
|
if ((res = has_compat_open_info(handle, EBADF)) == 0) {
|
|
res = -1;
|
|
errno = EBADF;
|
|
auto os_handle =
|
|
reinterpret_cast<HANDLE>(_get_osfhandle(static_cast<int>(handle)));
|
|
if (os_handle != INVALID_HANDLE_VALUE) {
|
|
errno = EFAULT;
|
|
if ((write_size == 0) ||
|
|
native_file::attach(os_handle)->write_bytes(
|
|
buffer, static_cast<std::size_t>(write_size), write_offset,
|
|
bytes_written)) {
|
|
res = 0;
|
|
errno = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const auto ret = ((res < 0) ? -errno : static_cast<int>(bytes_written));
|
|
if (ret < 0) {
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::fuse_write_base64(
|
|
const char * /*path*/, const char * /*buffer*/,
|
|
const remote::file_size & /*write_size*/,
|
|
const remote::file_offset & /*write_offset*/,
|
|
const remote::file_handle & /*handle*/) -> packet::error_type {
|
|
// DOES NOTHING
|
|
return 0;
|
|
}
|
|
|
|
auto remote_server::fuse_readdir(const char *path,
|
|
const remote::file_offset &offset,
|
|
const remote::file_handle &handle,
|
|
std::string &itemPath) -> packet::error_type {
|
|
const auto file_path = construct_path(path);
|
|
auto res = 0;
|
|
if (offset > std::numeric_limits<std::size_t>::max()) {
|
|
errno = ERANGE;
|
|
res = -1;
|
|
} else {
|
|
auto *iterator = reinterpret_cast<directory_iterator *>(handle);
|
|
if (iterator) {
|
|
res = iterator->get(static_cast<std::size_t>(offset), itemPath);
|
|
} else {
|
|
res = -1;
|
|
errno = EFAULT;
|
|
}
|
|
}
|
|
|
|
const auto ret = ((res < 0) ? -errno : 0);
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::fuse_release(const char *path,
|
|
const remote::file_handle &handle)
|
|
-> packet::error_type {
|
|
const auto file_path = construct_path(path);
|
|
|
|
remove_compat_open_info(handle);
|
|
const auto res = _close(static_cast<int>(handle));
|
|
|
|
const auto ret = ((res < 0) ? -errno : 0);
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::fuse_releasedir(const char *path,
|
|
const remote::file_handle & /*handle*/)
|
|
-> packet::error_type {
|
|
const auto file_path = construct_path(path);
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*packet::error_type remote_server::fuse_removexattr(const char *path, const
|
|
char *name) { const auto file_path = construct_path(path); const auto ret =
|
|
STATUS_NOT_IMPLEMENTED; RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__,
|
|
file_path, ret); return ret;
|
|
}*/
|
|
|
|
auto remote_server::fuse_rmdir(const char *path) -> packet::error_type {
|
|
const auto file_path = construct_path(path);
|
|
const auto res = _rmdir(file_path.c_str());
|
|
const auto ret = ((res < 0) ? -errno : 0);
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::fuse_setattr_x(const char *path,
|
|
remote::setattr_x & /*attr*/)
|
|
-> packet::error_type {
|
|
const auto file_path = construct_path(path);
|
|
const auto ret = STATUS_NOT_IMPLEMENTED;
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::fuse_setbkuptime(const char *path,
|
|
const remote::file_time & /*bkuptime*/)
|
|
-> packet::error_type {
|
|
const auto file_path = construct_path(path);
|
|
const auto ret = STATUS_NOT_IMPLEMENTED;
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::fuse_setchgtime(const char *path,
|
|
const remote::file_time & /*chgtime*/)
|
|
-> packet::error_type {
|
|
const auto file_path = construct_path(path);
|
|
const auto ret = STATUS_NOT_IMPLEMENTED;
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::fuse_setcrtime(const char *path,
|
|
const remote::file_time & /*crtime*/)
|
|
-> packet::error_type {
|
|
const auto file_path = construct_path(path);
|
|
const auto ret = STATUS_NOT_IMPLEMENTED;
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::fuse_setvolname(const char *volname) -> packet::error_type {
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, volname, 0);
|
|
return 0;
|
|
}
|
|
|
|
/*packet::error_type remote_server::fuse_setxattr(const char *path, const char
|
|
*name, const char *value, const remote::file_size &size, const std::int32_t
|
|
&flags) { const auto file_path = construct_path(path); const auto ret =
|
|
STATUS_NOT_IMPLEMENTED; RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__,
|
|
file_path, ret); return ret;
|
|
}
|
|
|
|
packet::error_type remote_server::fuse_setxattr_osx(const char *path, const char
|
|
*name, const char *value, const remote::file_size &size, const std::int32_t
|
|
&flags, const std::uint32_t &position) { const auto file_path =
|
|
construct_path(path); const auto ret = STATUS_NOT_IMPLEMENTED;
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
return ret;
|
|
}*/
|
|
|
|
auto remote_server::fuse_statfs(const char *path, std::uint64_t frsize,
|
|
remote::statfs &st) -> packet::error_type {
|
|
const auto file_path = construct_path(path);
|
|
|
|
const auto total_bytes = drive_.get_total_drive_space();
|
|
const auto total_used = drive_.get_used_drive_space();
|
|
const auto used_blocks = utils::divide_with_ceiling(
|
|
total_used, static_cast<std::uint64_t>(frsize));
|
|
st.f_files = 4294967295;
|
|
st.f_blocks = utils::divide_with_ceiling(total_bytes,
|
|
static_cast<std::uint64_t>(frsize));
|
|
st.f_bavail = st.f_bfree = st.f_blocks ? (st.f_blocks - used_blocks) : 0;
|
|
st.f_ffree = st.f_favail = st.f_files - drive_.get_total_item_count();
|
|
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, 0);
|
|
return 0;
|
|
}
|
|
|
|
auto remote_server::fuse_statfs_x(const char *path, std::uint64_t bsize,
|
|
remote::statfs_x &st) -> packet::error_type {
|
|
const auto file_path = construct_path(path);
|
|
|
|
const auto total_bytes = drive_.get_total_drive_space();
|
|
const auto total_used = drive_.get_used_drive_space();
|
|
const auto used_blocks =
|
|
utils::divide_with_ceiling(total_used, static_cast<std::uint64_t>(bsize));
|
|
st.f_blocks = utils::divide_with_ceiling(total_bytes,
|
|
static_cast<std::uint64_t>(bsize));
|
|
st.f_bavail = st.f_bfree = st.f_blocks ? (st.f_blocks - used_blocks) : 0;
|
|
st.f_files = 4294967295;
|
|
st.f_ffree = st.f_favail = st.f_files - drive_.get_total_item_count();
|
|
strncpy(&st.f_mntfromname[0u],
|
|
(utils::create_volume_label(config_.get_provider_type())).c_str(),
|
|
1024);
|
|
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, 0);
|
|
return 0;
|
|
}
|
|
|
|
auto remote_server::fuse_truncate(const char *path,
|
|
const remote::file_offset &size)
|
|
-> packet::error_type {
|
|
const auto file_path = construct_path(path);
|
|
const auto unicode_file_path = utils::string::from_utf8(file_path);
|
|
auto res = -1;
|
|
errno = ENOENT;
|
|
|
|
const auto flags_and_attributes =
|
|
FILE_FLAG_BACKUP_SEMANTICS |
|
|
(::PathIsDirectoryW(unicode_file_path.c_str())
|
|
? FILE_ATTRIBUTE_DIRECTORY | FILE_FLAG_POSIX_SEMANTICS
|
|
: 0);
|
|
const auto os_handle = ::CreateFileW(
|
|
unicode_file_path.c_str(), FILE_GENERIC_READ | FILE_GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
|
|
OPEN_EXISTING, flags_and_attributes, nullptr);
|
|
if (os_handle != INVALID_HANDLE_VALUE) {
|
|
errno = EFAULT;
|
|
FILE_END_OF_FILE_INFO fi{};
|
|
fi.EndOfFile.QuadPart = size;
|
|
if (::SetFileInformationByHandle(os_handle, FileEndOfFileInfo, &fi,
|
|
sizeof(FILE_END_OF_FILE_INFO))) {
|
|
res = 0;
|
|
errno = 0;
|
|
}
|
|
::CloseHandle(os_handle);
|
|
}
|
|
|
|
const auto ret = ((res < 0) ? -errno : 0);
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::fuse_unlink(const char *path) -> packet::error_type {
|
|
const auto file_path = construct_path(path);
|
|
const auto res = _unlink(file_path.c_str());
|
|
const auto ret = ((res < 0) ? -errno : 0);
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::fuse_utimens(const char *path, const remote::file_time *tv,
|
|
std::uint64_t op0, std::uint64_t op1)
|
|
-> packet::error_type {
|
|
const auto file_path = construct_path(path);
|
|
const auto unicode_file_path = utils::string::from_utf8(file_path);
|
|
|
|
auto res = -1;
|
|
errno = ENOENT;
|
|
|
|
const auto flags_and_attributes =
|
|
FILE_FLAG_BACKUP_SEMANTICS |
|
|
(::PathIsDirectoryW(unicode_file_path.c_str())
|
|
? FILE_ATTRIBUTE_DIRECTORY | FILE_FLAG_POSIX_SEMANTICS
|
|
: 0);
|
|
const auto os_handle =
|
|
::CreateFileW(unicode_file_path.c_str(), FILE_WRITE_ATTRIBUTES,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
nullptr, OPEN_EXISTING, flags_and_attributes, nullptr);
|
|
if (os_handle != INVALID_HANDLE_VALUE) {
|
|
FILETIME access_time{};
|
|
FILETIME write_time{};
|
|
FILETIME *access_time_ptr = nullptr;
|
|
FILETIME *write_time_ptr = nullptr;
|
|
|
|
if (not tv[0u] || (op0 == UTIME_NOW)) {
|
|
const auto now = utils::get_file_time_now();
|
|
access_time.dwHighDateTime = ((now >> 32u) & 0xFFFFFFFF);
|
|
access_time.dwLowDateTime = now & 0xFFFFFFFF;
|
|
access_time_ptr = &access_time;
|
|
} else if (op0 != UTIME_OMIT) {
|
|
utils::unix_time_to_filetime(tv[0u], access_time);
|
|
access_time_ptr = &access_time;
|
|
}
|
|
|
|
if (not tv[1] || (op1 == UTIME_NOW)) {
|
|
const auto now = utils::get_file_time_now();
|
|
write_time.dwHighDateTime = ((now >> 32u) & 0xFFFFFFFF);
|
|
write_time.dwLowDateTime = now & 0xFFFFFFFF;
|
|
write_time_ptr = &write_time;
|
|
} else if (op1 != UTIME_OMIT) {
|
|
utils::unix_time_to_filetime(tv[1], write_time);
|
|
write_time_ptr = &write_time;
|
|
}
|
|
|
|
errno = EFAULT;
|
|
if (::SetFileTime(os_handle, nullptr, access_time_ptr, write_time_ptr)) {
|
|
res = 0;
|
|
errno = 0;
|
|
}
|
|
::CloseHandle(os_handle);
|
|
}
|
|
|
|
const auto ret = ((res < 0) ? -errno : 0);
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
// JSON Layer
|
|
auto remote_server::json_create_directory_snapshot(const std::string &path,
|
|
json &json_data)
|
|
-> packet::error_type {
|
|
const auto file_path = construct_path(path);
|
|
|
|
auto res = -1;
|
|
errno = ENOENT;
|
|
|
|
if (utils::file::is_directory(file_path)) {
|
|
auto list = drive_.get_directory_items(utils::path::create_api_path(path));
|
|
auto *iterator = new directory_iterator(std::move(list));
|
|
json_data["path"] = path;
|
|
json_data["handle"] = static_cast<remote::file_handle>(
|
|
reinterpret_cast<std::uintptr_t>(iterator));
|
|
json_data["page_count"] = utils::divide_with_ceiling(
|
|
iterator->get_count(), REPERTORY_DIRECTORY_PAGE_SIZE);
|
|
res = 0;
|
|
errno = 0;
|
|
}
|
|
|
|
const auto ret = ((res < 0) ? -errno : 0);
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::json_read_directory_snapshot(
|
|
const std::string &path, const remote::file_handle &handle,
|
|
std::uint32_t page, json &json_data) -> packet::error_type {
|
|
const auto file_path = construct_path(path);
|
|
auto *iterator = reinterpret_cast<directory_iterator *>(handle);
|
|
std::size_t offset = 0u;
|
|
int res;
|
|
json item_json;
|
|
while ((json_data["directory_list"].size() < REPERTORY_DIRECTORY_PAGE_SIZE) &&
|
|
(res = iterator->get_json(
|
|
(page * REPERTORY_DIRECTORY_PAGE_SIZE) + offset++, item_json)) ==
|
|
0) {
|
|
json_data["directory_list"].emplace_back(item_json);
|
|
}
|
|
json_data["handle"] = static_cast<remote::file_handle>(
|
|
reinterpret_cast<std::uintptr_t>(iterator));
|
|
json_data["path"] = path;
|
|
json_data["page"] = page;
|
|
json_data["page_count"] = utils::divide_with_ceiling(
|
|
iterator->get_count(), REPERTORY_DIRECTORY_PAGE_SIZE);
|
|
const auto ret = ((res < 0) ? -errno : 0);
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::json_release_directory_snapshot(
|
|
const std::string &path, const remote::file_handle & /*handle*/)
|
|
-> packet::error_type {
|
|
const auto file_path = construct_path(path);
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// WinFSP Layer
|
|
auto remote_server::winfsp_can_delete(PVOID file_desc, PWSTR /*file_name*/)
|
|
-> packet::error_type {
|
|
const auto handle = reinterpret_cast<HANDLE>(file_desc);
|
|
|
|
auto ret = has_open_info(handle, STATUS_INVALID_HANDLE);
|
|
if (ret == STATUS_SUCCESS) {
|
|
FILE_DISPOSITION_INFO dispositionInfo = {TRUE};
|
|
ret = ::SetFileInformationByHandle(handle, FileDispositionInfo,
|
|
&dispositionInfo,
|
|
sizeof(FILE_DISPOSITION_INFO))
|
|
? STATUS_SUCCESS
|
|
: FspNtStatusFromWin32(::GetLastError());
|
|
}
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, get_open_file_path(file_desc),
|
|
ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::winfsp_cleanup(PVOID file_desc, PWSTR /*file_name*/,
|
|
UINT32 flags, BOOLEAN &was_closed)
|
|
-> packet::error_type {
|
|
const auto file_path = get_open_file_path(file_desc);
|
|
was_closed = FALSE;
|
|
const auto handle = reinterpret_cast<HANDLE>(file_desc);
|
|
const auto ret = has_open_info(handle, STATUS_INVALID_HANDLE);
|
|
if (ret == STATUS_SUCCESS) {
|
|
if (flags & FileSystemBase::CleanupDelete) {
|
|
::CloseHandle(handle);
|
|
remove_open_info(file_desc);
|
|
was_closed = TRUE;
|
|
}
|
|
}
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::winfsp_close(PVOID file_desc) -> packet::error_type {
|
|
const auto file_path = get_open_file_path(file_desc);
|
|
|
|
const auto handle = reinterpret_cast<HANDLE>(file_desc);
|
|
if (has_open_info(handle, STATUS_INVALID_HANDLE) == STATUS_SUCCESS) {
|
|
::CloseHandle(handle);
|
|
remove_open_info(file_desc);
|
|
}
|
|
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, file_path, STATUS_SUCCESS);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
auto remote_server::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::error_type {
|
|
const auto file_path = utils::string::from_utf8(utils::path::combine(
|
|
mount_location_, {utils::string::to_utf8(file_name)}));
|
|
exists = utils::file::is_file(utils::path::combine(
|
|
mount_location_, {utils::string::to_utf8(file_name)}));
|
|
|
|
auto create_flags = FILE_FLAG_BACKUP_SEMANTICS;
|
|
if (create_options & FILE_DIRECTORY_FILE) {
|
|
create_flags |= FILE_FLAG_POSIX_SEMANTICS;
|
|
attributes |= FILE_ATTRIBUTE_DIRECTORY;
|
|
} else {
|
|
attributes &= ~FILE_ATTRIBUTE_DIRECTORY;
|
|
}
|
|
|
|
if (not attributes) {
|
|
attributes = FILE_ATTRIBUTE_NORMAL;
|
|
}
|
|
|
|
const auto handle =
|
|
::CreateFileW(file_path.c_str(), granted_access,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
nullptr, CREATE_NEW, create_flags | attributes, nullptr);
|
|
if (handle != INVALID_HANDLE_VALUE) {
|
|
*file_desc = reinterpret_cast<PVOID>(handle);
|
|
normalized_name = utils::string::to_utf8(file_name);
|
|
set_open_info(*file_desc,
|
|
open_info{0, "", nullptr, utils::string::to_utf8(file_path)});
|
|
}
|
|
|
|
const auto ret =
|
|
(handle == INVALID_HANDLE_VALUE)
|
|
? FspNtStatusFromWin32(::GetLastError())
|
|
: populate_file_info(construct_api_path(get_open_file_path(handle)),
|
|
*file_info);
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__,
|
|
utils::string::to_utf8(file_path), ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::winfsp_flush(PVOID file_desc, remote::file_info *file_info)
|
|
-> packet::error_type {
|
|
const auto handle = reinterpret_cast<HANDLE>(file_desc);
|
|
auto ret = has_open_info(handle, STATUS_INVALID_HANDLE);
|
|
if (ret == STATUS_SUCCESS) {
|
|
if ((ret = ::FlushFileBuffers(handle)
|
|
? ret
|
|
: FspNtStatusFromWin32(::GetLastError())) ==
|
|
STATUS_SUCCESS) {
|
|
ret = populate_file_info(construct_api_path(get_open_file_path(handle)),
|
|
*file_info);
|
|
}
|
|
}
|
|
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, get_open_file_path(file_desc),
|
|
ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::winfsp_get_file_info(PVOID file_desc,
|
|
remote::file_info *file_info)
|
|
-> packet::error_type {
|
|
const auto handle = reinterpret_cast<HANDLE>(file_desc);
|
|
auto ret = has_open_info(handle, STATUS_INVALID_HANDLE);
|
|
if (ret == STATUS_SUCCESS) {
|
|
ret = populate_file_info(construct_api_path(get_open_file_path(handle)),
|
|
*file_info);
|
|
}
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, get_open_file_path(file_desc),
|
|
ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::winfsp_get_security_by_name(PWSTR file_name,
|
|
PUINT32 attributes,
|
|
std::uint64_t *descriptor_size,
|
|
std::wstring &string_descriptor)
|
|
-> packet::error_type {
|
|
const auto file_path = utils::string::from_utf8(utils::path::combine(
|
|
mount_location_, {utils::string::to_utf8(file_name)}));
|
|
|
|
auto ret = STATUS_BUFFER_OVERFLOW;
|
|
if (not descriptor_size ||
|
|
(*descriptor_size <= std::numeric_limits<SIZE_T>::max())) {
|
|
auto *descriptor = descriptor_size
|
|
? static_cast<PSECURITY_DESCRIPTOR>(::LocalAlloc(
|
|
LPTR, static_cast<SIZE_T>(*descriptor_size)))
|
|
: nullptr;
|
|
if (((ret = drive_.get_security_by_name(file_name, attributes, descriptor,
|
|
descriptor_size)) ==
|
|
STATUS_SUCCESS) &&
|
|
descriptor_size) {
|
|
ULONG str_size = 0u;
|
|
const auto security_info =
|
|
OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
|
|
DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION;
|
|
LPWSTR str;
|
|
if (::ConvertSecurityDescriptorToStringSecurityDescriptorW(
|
|
descriptor, SDDL_REVISION_1, security_info, &str, &str_size)) {
|
|
string_descriptor = std::wstring(str, wcslen(str));
|
|
::LocalFree(str);
|
|
} else {
|
|
ret = FspNtStatusFromWin32(::GetLastError());
|
|
}
|
|
}
|
|
::LocalFree(descriptor);
|
|
}
|
|
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__,
|
|
utils::string::to_utf8(file_path), ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::winfsp_get_volume_info(UINT64 &total_size,
|
|
UINT64 &free_size,
|
|
std::string &volume_label)
|
|
-> packet::error_type {
|
|
drive_.get_volume_info(total_size, free_size, volume_label);
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, volume_label, STATUS_SUCCESS);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
auto remote_server::winfsp_mounted(const std::wstring &location)
|
|
-> packet::error_type {
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(
|
|
__FUNCTION__, utils::string::to_utf8(location), STATUS_SUCCESS);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
auto remote_server::winfsp_open(PWSTR file_name, UINT32 create_options,
|
|
UINT32 granted_access, PVOID *file_desc,
|
|
remote::file_info *file_info,
|
|
std::string &normalized_name)
|
|
-> packet::error_type {
|
|
const auto file_path = utils::string::from_utf8(utils::path::combine(
|
|
mount_location_, {utils::string::to_utf8(file_name)}));
|
|
|
|
auto create_flags = FILE_FLAG_BACKUP_SEMANTICS;
|
|
if (create_options & FILE_DELETE_ON_CLOSE) {
|
|
create_flags |= FILE_FLAG_DELETE_ON_CLOSE;
|
|
}
|
|
|
|
const auto handle =
|
|
::CreateFileW(file_path.c_str(), granted_access,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
nullptr, OPEN_EXISTING, create_flags, nullptr);
|
|
if (handle != INVALID_HANDLE_VALUE) {
|
|
*file_desc = reinterpret_cast<PVOID>(handle);
|
|
normalized_name = utils::string::to_utf8(file_name);
|
|
set_open_info(*file_desc,
|
|
open_info{0, "", nullptr, utils::string::to_utf8(file_path)});
|
|
}
|
|
|
|
const auto ret =
|
|
(handle == INVALID_HANDLE_VALUE)
|
|
? FspNtStatusFromWin32(::GetLastError())
|
|
: populate_file_info(construct_api_path(get_open_file_path(handle)),
|
|
*file_info);
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__,
|
|
utils::string::to_utf8(file_path), ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::winfsp_overwrite(PVOID file_desc, UINT32 attributes,
|
|
BOOLEAN replace_attributes,
|
|
UINT64 /*allocation_size*/,
|
|
remote::file_info *file_info)
|
|
-> packet::error_type {
|
|
const auto handle = reinterpret_cast<HANDLE>(file_desc);
|
|
auto ret = has_open_info(handle, STATUS_INVALID_HANDLE);
|
|
if (ret == STATUS_SUCCESS) {
|
|
if (replace_attributes) {
|
|
if (not attributes) {
|
|
attributes = FILE_ATTRIBUTE_NORMAL;
|
|
}
|
|
FILE_BASIC_INFO basic_info{};
|
|
basic_info.FileAttributes = attributes;
|
|
if (not ::SetFileInformationByHandle(handle, FileBasicInfo, &basic_info,
|
|
sizeof(FILE_BASIC_INFO))) {
|
|
ret = FspNtStatusFromWin32(::GetLastError());
|
|
}
|
|
} else if (attributes) {
|
|
FILE_ATTRIBUTE_TAG_INFO tag_info{};
|
|
if (not ::GetFileInformationByHandleEx(handle, FileAttributeTagInfo,
|
|
&tag_info,
|
|
sizeof(FILE_ATTRIBUTE_TAG_INFO))) {
|
|
ret = FspNtStatusFromWin32(::GetLastError());
|
|
} else {
|
|
FILE_BASIC_INFO basic_info{};
|
|
basic_info.FileAttributes =
|
|
attributes | (tag_info.FileAttributes & ~(FILE_ATTRIBUTE_NORMAL));
|
|
if ((basic_info.FileAttributes != tag_info.FileAttributes)) {
|
|
if (not ::SetFileInformationByHandle(handle, FileBasicInfo,
|
|
&basic_info,
|
|
sizeof(FILE_BASIC_INFO))) {
|
|
ret = FspNtStatusFromWin32(::GetLastError());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ret == STATUS_SUCCESS) {
|
|
FILE_ALLOCATION_INFO allocationInfo{};
|
|
ret =
|
|
::SetFileInformationByHandle(handle, FileAllocationInfo,
|
|
&allocationInfo,
|
|
sizeof(FILE_ALLOCATION_INFO))
|
|
? populate_file_info(
|
|
construct_api_path(get_open_file_path(handle)), *file_info)
|
|
: FspNtStatusFromWin32(::GetLastError());
|
|
}
|
|
}
|
|
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, get_open_file_path(file_desc),
|
|
ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::winfsp_read(PVOID file_desc, PVOID buffer, UINT64 offset,
|
|
UINT32 length, PUINT32 bytes_transferred)
|
|
-> packet::error_type {
|
|
*bytes_transferred = 0u;
|
|
|
|
const auto handle = reinterpret_cast<HANDLE>(file_desc);
|
|
auto ret = has_open_info(handle, STATUS_INVALID_HANDLE);
|
|
if ((ret == STATUS_SUCCESS) && length) {
|
|
OVERLAPPED overlapped{};
|
|
overlapped.Offset = static_cast<DWORD>(offset);
|
|
overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
|
|
|
|
ret = ::ReadFile(handle, buffer, length,
|
|
reinterpret_cast<LPDWORD>(bytes_transferred), &overlapped)
|
|
? STATUS_SUCCESS
|
|
: FspNtStatusFromWin32(::GetLastError());
|
|
}
|
|
if (ret != STATUS_SUCCESS) {
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__,
|
|
get_open_file_path(file_desc), ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::winfsp_read_directory(PVOID file_desc, PWSTR /*pattern*/,
|
|
PWSTR marker, json &item_list)
|
|
-> packet::error_type {
|
|
auto ret = STATUS_INVALID_HANDLE;
|
|
item_list.clear();
|
|
|
|
open_info ofi{};
|
|
if (get_open_info(file_desc, ofi)) {
|
|
const auto api_path =
|
|
utils::path::create_api_path(ofi.path.substr(mount_location_.size()));
|
|
auto items = drive_.get_directory_items(api_path);
|
|
directory_iterator iterator(std::move(items));
|
|
auto offset = marker
|
|
? iterator.get_next_directory_offset(
|
|
utils::path::create_api_path(utils::path::combine(
|
|
api_path, {utils::string::to_utf8(marker)})))
|
|
: 0;
|
|
json item;
|
|
while (iterator.get_json(offset++, item) == 0) {
|
|
item_list.emplace_back(item);
|
|
}
|
|
ret = STATUS_SUCCESS;
|
|
}
|
|
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, get_open_file_path(file_desc),
|
|
ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::winfsp_rename(PVOID /*file_desc*/, PWSTR file_name,
|
|
PWSTR new_file_name,
|
|
BOOLEAN replace_if_exists)
|
|
-> packet::error_type {
|
|
const auto from_path = utils::string::from_utf8(utils::path::combine(
|
|
mount_location_, {utils::string::to_utf8(file_name)}));
|
|
const auto to_path = utils::string::from_utf8(utils::path::combine(
|
|
mount_location_, {utils::string::to_utf8(new_file_name)}));
|
|
const auto ret =
|
|
::MoveFileExW(from_path.c_str(), to_path.c_str(),
|
|
replace_if_exists ? MOVEFILE_REPLACE_EXISTING : 0)
|
|
? STATUS_SUCCESS
|
|
: FspNtStatusFromWin32(::GetLastError());
|
|
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(
|
|
__FUNCTION__, utils::string::to_utf8(from_path + L"|" + to_path), ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::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::error_type {
|
|
const auto handle = reinterpret_cast<HANDLE>(file_desc);
|
|
auto ret = has_open_info(handle, STATUS_INVALID_HANDLE);
|
|
if (ret == STATUS_SUCCESS) {
|
|
if (attributes == INVALID_FILE_ATTRIBUTES) {
|
|
attributes = 0;
|
|
} else if (not attributes) {
|
|
attributes = FILE_ATTRIBUTE_NORMAL;
|
|
}
|
|
|
|
FILE_BASIC_INFO basic_info{};
|
|
basic_info.FileAttributes = attributes;
|
|
basic_info.CreationTime.QuadPart = creation_time;
|
|
basic_info.LastAccessTime.QuadPart = last_access_time;
|
|
basic_info.LastWriteTime.QuadPart = last_write_time;
|
|
basic_info.ChangeTime.QuadPart = change_time;
|
|
ret = ::SetFileInformationByHandle(handle, FileBasicInfo, &basic_info,
|
|
sizeof(FILE_BASIC_INFO))
|
|
? populate_file_info(
|
|
construct_api_path(get_open_file_path(handle)), *file_info)
|
|
: FspNtStatusFromWin32(::GetLastError());
|
|
}
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, get_open_file_path(file_desc),
|
|
ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::winfsp_set_file_size(PVOID file_desc, UINT64 new_size,
|
|
BOOLEAN set_allocation_size,
|
|
remote::file_info *file_info)
|
|
-> packet::error_type {
|
|
const auto handle = reinterpret_cast<HANDLE>(file_desc);
|
|
auto ret = has_open_info(handle, STATUS_INVALID_HANDLE);
|
|
if (ret == STATUS_SUCCESS) {
|
|
if (set_allocation_size) {
|
|
FILE_ALLOCATION_INFO allocation_info{};
|
|
allocation_info.AllocationSize.QuadPart = new_size;
|
|
if (not ::SetFileInformationByHandle(handle, FileAllocationInfo,
|
|
&allocation_info,
|
|
sizeof(FILE_ALLOCATION_INFO))) {
|
|
ret = FspNtStatusFromWin32(::GetLastError());
|
|
}
|
|
} else {
|
|
FILE_END_OF_FILE_INFO end_of_file_info{};
|
|
end_of_file_info.EndOfFile.QuadPart = new_size;
|
|
if (not ::SetFileInformationByHandle(handle, FileEndOfFileInfo,
|
|
&end_of_file_info,
|
|
sizeof(FILE_END_OF_FILE_INFO))) {
|
|
ret = FspNtStatusFromWin32(::GetLastError());
|
|
}
|
|
}
|
|
|
|
ret = (ret == STATUS_SUCCESS)
|
|
? populate_file_info(
|
|
construct_api_path(get_open_file_path(handle)), *file_info)
|
|
: ret;
|
|
}
|
|
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__, get_open_file_path(file_desc),
|
|
ret);
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::winfsp_unmounted(const std::wstring &location)
|
|
-> packet::error_type {
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(
|
|
__FUNCTION__, utils::string::to_utf8(location), STATUS_SUCCESS);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
auto remote_server::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::error_type {
|
|
const auto handle = reinterpret_cast<HANDLE>(file_desc);
|
|
*bytes_transferred = 0;
|
|
auto ret = has_open_info(handle, STATUS_INVALID_HANDLE);
|
|
if (ret == STATUS_SUCCESS) {
|
|
if (length) {
|
|
auto should_write = true;
|
|
|
|
LARGE_INTEGER file_size{};
|
|
OVERLAPPED overlapped{};
|
|
if (constrained_io) {
|
|
if (not ::GetFileSizeEx(handle, &file_size)) {
|
|
ret = FspNtStatusFromWin32(::GetLastError());
|
|
} else if (offset >= static_cast<UINT64>(file_size.QuadPart)) {
|
|
ret = STATUS_SUCCESS;
|
|
should_write = false;
|
|
} else if (offset + length > static_cast<UINT64>(file_size.QuadPart)) {
|
|
length = static_cast<UINT32>(static_cast<UINT64>(file_size.QuadPart) -
|
|
offset);
|
|
}
|
|
}
|
|
|
|
if (should_write && (ret == STATUS_SUCCESS)) {
|
|
overlapped.Offset = static_cast<DWORD>(offset);
|
|
overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32u);
|
|
ret = ::WriteFile(handle, buffer, length,
|
|
reinterpret_cast<LPDWORD>(bytes_transferred),
|
|
&overlapped)
|
|
? populate_file_info(
|
|
construct_api_path(get_open_file_path(handle)),
|
|
*file_info)
|
|
: FspNtStatusFromWin32(::GetLastError());
|
|
}
|
|
} else {
|
|
ret = populate_file_info(construct_api_path(get_open_file_path(handle)),
|
|
*file_info);
|
|
}
|
|
}
|
|
|
|
if (ret != STATUS_SUCCESS) {
|
|
RAISE_REMOTE_WINFSP_SERVER_EVENT(__FUNCTION__,
|
|
get_open_file_path(file_desc), ret);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
auto remote_server::winfsp_get_dir_buffer(PVOID /*file_desc*/, PVOID *& /*ptr*/)
|
|
-> packet::error_type {
|
|
return STATUS_INVALID_HANDLE;
|
|
}
|
|
} // namespace repertory::remote_winfsp
|
|
|
|
#endif // _WIN32
|