1151 lines
41 KiB
C++
1151 lines
41 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.
|
|
*/
|
|
#ifdef _WIN32
|
|
|
|
#include "drives/winfsp/winfsp_drive.hpp"
|
|
|
|
#include "app_config.hpp"
|
|
#include "drives/directory_iterator.hpp"
|
|
#include "events/consumers/console_consumer.hpp"
|
|
#include "events/consumers/logging_consumer.hpp"
|
|
#include "events/events.hpp"
|
|
#include "platform/platform.hpp"
|
|
#include "providers/i_provider.hpp"
|
|
#include "types/repertory.hpp"
|
|
#include "types/startup_exception.hpp"
|
|
#include "utils/error_utils.hpp"
|
|
#include "utils/file_utils.hpp"
|
|
#include "utils/polling.hpp"
|
|
#include "utils/string_utils.hpp"
|
|
#include "utils/utils.hpp"
|
|
|
|
namespace repertory {
|
|
// clang-format off
|
|
E_SIMPLE3(winfsp_event, debug, true,
|
|
std::string, function, func, E_STRING,
|
|
std::string, api_path, ap, E_STRING,
|
|
NTSTATUS, result, res, E_FROM_INT32
|
|
);
|
|
// clang-format on
|
|
|
|
#define RAISE_WINFSP_EVENT(func, file, ret) \
|
|
if (config_.get_enable_drive_events() && \
|
|
(((config_.get_event_level() >= winfsp_event::level) && \
|
|
(ret != STATUS_SUCCESS)) || \
|
|
(config_.get_event_level() >= event_level::verbose))) \
|
|
event_system::instance().raise<winfsp_event>(func, file, ret)
|
|
|
|
winfsp_drive::winfsp_service::winfsp_service(
|
|
lock_data &lock, winfsp_drive &drive, std::vector<std::string> drive_args,
|
|
app_config &config)
|
|
: Service((PWSTR)L"Repertory"),
|
|
lock_(lock),
|
|
drive_(drive),
|
|
drive_args_(std::move(drive_args)),
|
|
host_(drive),
|
|
config_(config) {}
|
|
|
|
auto winfsp_drive::winfsp_service::OnStart(ULONG, PWSTR *) -> NTSTATUS {
|
|
const auto mount_location = utils::string::to_lower(
|
|
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]);
|
|
if (config_.get_enable_mount_manager()) {
|
|
unicode_mount_location =
|
|
std::wstring(L"\\\\.\\") + unicode_mount_location[0U] + L":";
|
|
}
|
|
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) {
|
|
if (not lock_.set_mount_state(false, "", -1)) {
|
|
utils::error::raise_error(__FUNCTION__, ret, "failed to set mount state");
|
|
}
|
|
event_system::instance().raise<drive_mount_failed>(mount_location,
|
|
std::to_string(ret));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
auto winfsp_drive::winfsp_service::OnStop() -> NTSTATUS {
|
|
host_.Unmount();
|
|
|
|
if (not lock_.set_mount_state(false, "", -1)) {
|
|
utils::error::raise_error(__FUNCTION__, "failed to set mount state");
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
winfsp_drive::winfsp_drive(app_config &config, lock_data &lock,
|
|
i_provider &provider)
|
|
: FileSystemBase(), provider_(provider), config_(config), lock_(lock) {
|
|
E_SUBSCRIBE_EXACT(unmount_requested, [this](const unmount_requested & /*e*/) {
|
|
std::thread([this]() { this->shutdown(); }).detach();
|
|
});
|
|
}
|
|
|
|
void winfsp_drive::shutdown() { ::GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); }
|
|
|
|
auto winfsp_drive::CanDelete(PVOID /*file_node*/, PVOID file_desc,
|
|
PWSTR /*file_name*/) -> NTSTATUS {
|
|
std::string api_path;
|
|
auto error = api_error::invalid_handle;
|
|
auto handle =
|
|
static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(file_desc));
|
|
if (handle) {
|
|
std::shared_ptr<i_open_file> f;
|
|
if (fm_->get_open_file(handle, false, f)) {
|
|
api_path = f->get_api_path();
|
|
if (f->is_directory()) {
|
|
error = provider_.get_directory_item_count(api_path)
|
|
? api_error::directory_not_empty
|
|
: api_error::success;
|
|
} else {
|
|
error = fm_->is_processing(api_path) ? api_error::file_in_use
|
|
: api_error::success;
|
|
}
|
|
}
|
|
}
|
|
|
|
auto ret = utils::from_api_error(error);
|
|
RAISE_WINFSP_EVENT(__FUNCTION__, api_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
VOID winfsp_drive::Cleanup(PVOID file_node, PVOID file_desc,
|
|
PWSTR /*file_name*/, ULONG flags) {
|
|
std::string api_path;
|
|
auto handle =
|
|
static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(file_desc));
|
|
if (handle) {
|
|
std::shared_ptr<i_open_file> f;
|
|
if (fm_->get_open_file(handle, false, f)) {
|
|
api_path = f->get_api_path();
|
|
|
|
const auto directory = f->is_directory();
|
|
if (flags & FspCleanupDelete) {
|
|
auto *directory_buffer = f->get_open_data(handle).directory_buffer;
|
|
f.reset();
|
|
|
|
if (directory_buffer) {
|
|
FspFileSystemDeleteDirectoryBuffer(&directory_buffer);
|
|
}
|
|
|
|
if (directory) {
|
|
auto res = provider_.remove_directory(api_path);
|
|
if (res != api_error::success) {
|
|
utils::error::raise_api_path_error(__FUNCTION__, api_path, res,
|
|
"failed to remove directory");
|
|
}
|
|
} else {
|
|
auto res = fm_->remove_file(api_path);
|
|
if (res != api_error::success) {
|
|
utils::error::raise_api_path_error(__FUNCTION__, api_path, res,
|
|
"failed to remove file");
|
|
}
|
|
}
|
|
} else {
|
|
if ((flags & FspCleanupSetArchiveBit) && not directory) {
|
|
api_meta_map meta;
|
|
if (provider_.get_item_meta(api_path, meta) == api_error::success) {
|
|
auto res = provider_.set_item_meta(
|
|
api_path, META_ATTRIBUTES,
|
|
std::to_string(utils::get_attributes_from_meta(meta) |
|
|
FILE_ATTRIBUTE_ARCHIVE));
|
|
if (res != api_error::success) {
|
|
utils::error::raise_api_path_error(
|
|
__FUNCTION__, api_path, res, "failed to set meta attributes");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (flags & (FspCleanupSetLastAccessTime | FspCleanupSetLastWriteTime |
|
|
FspCleanupSetChangeTime)) {
|
|
const auto now = utils::get_file_time_now();
|
|
if (flags & FspCleanupSetLastAccessTime) {
|
|
auto res = provider_.set_item_meta(api_path, META_ACCESSED,
|
|
std::to_string(now));
|
|
if (res != api_error::success) {
|
|
utils::error::raise_api_path_error(
|
|
__FUNCTION__, api_path, res,
|
|
"failed to set meta accessed time");
|
|
}
|
|
}
|
|
|
|
if (flags & FspCleanupSetLastWriteTime) {
|
|
auto res = provider_.set_item_meta(api_path, META_WRITTEN,
|
|
std::to_string(now));
|
|
if (res != api_error::success) {
|
|
utils::error::raise_api_path_error(
|
|
__FUNCTION__, api_path, res,
|
|
"failed to set meta written time");
|
|
}
|
|
}
|
|
|
|
if (flags & FspCleanupSetChangeTime) {
|
|
auto res = provider_.set_item_meta(
|
|
api_path, {{META_CHANGED, std::to_string(now)},
|
|
{META_MODIFIED, std::to_string(now)}});
|
|
if (res != api_error::success) {
|
|
utils::error::raise_api_path_error(
|
|
__FUNCTION__, api_path, res,
|
|
"failed to set meta modified time");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (flags & FspCleanupSetAllocationSize) {
|
|
UINT64 allocation_size =
|
|
utils::divide_with_ceiling(f->get_file_size(),
|
|
WINFSP_ALLOCATION_UNIT) *
|
|
WINFSP_ALLOCATION_UNIT;
|
|
SetFileSize(file_node, file_desc, allocation_size, TRUE, nullptr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
RAISE_WINFSP_EVENT(__FUNCTION__, api_path, 0);
|
|
}
|
|
|
|
VOID winfsp_drive::Close(PVOID /*file_node*/, PVOID file_desc) {
|
|
std::string api_path;
|
|
auto handle =
|
|
static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(file_desc));
|
|
if (handle) {
|
|
PVOID directory_buffer = nullptr;
|
|
|
|
std::shared_ptr<i_open_file> f;
|
|
if (fm_->get_open_file(handle, false, f)) {
|
|
api_path = f->get_api_path();
|
|
directory_buffer = f->get_open_data(handle).directory_buffer;
|
|
f.reset();
|
|
}
|
|
fm_->close(handle);
|
|
if (directory_buffer) {
|
|
FspFileSystemDeleteDirectoryBuffer(&directory_buffer);
|
|
}
|
|
}
|
|
RAISE_WINFSP_EVENT(__FUNCTION__, api_path, 0);
|
|
}
|
|
|
|
auto 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) -> NTSTATUS {
|
|
// TODO Need to revisit this
|
|
// (ConvertSecurityDescriptorToStringSecurityDescriptor/ConvertStringSecurityDescriptorToSecurityDescriptor)
|
|
auto error = api_error::error;
|
|
|
|
if (create_options & FILE_DIRECTORY_FILE) {
|
|
attributes |= FILE_ATTRIBUTE_DIRECTORY;
|
|
} else {
|
|
attributes &= ~FILE_ATTRIBUTE_DIRECTORY;
|
|
}
|
|
|
|
if (not attributes) {
|
|
attributes = FILE_ATTRIBUTE_NORMAL;
|
|
}
|
|
|
|
const auto now = utils::get_file_time_now();
|
|
auto meta = create_meta_attributes(
|
|
now, attributes, now, now, attributes & FILE_ATTRIBUTE_DIRECTORY, 0U, "",
|
|
0U, now, 0U, 0U, 0U,
|
|
(attributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
? ""
|
|
: utils::path::combine(config_.get_cache_directory(),
|
|
{utils::create_uuid_string()}),
|
|
0U, now);
|
|
|
|
const auto api_path =
|
|
utils::path::create_api_path(utils::string::to_utf8(file_name));
|
|
|
|
open_file_data ofd{};
|
|
std::uint64_t handle{};
|
|
std::shared_ptr<i_open_file> f;
|
|
if (attributes & FILE_ATTRIBUTE_DIRECTORY) {
|
|
error = provider_.create_directory(api_path, meta);
|
|
if (error == api_error::success) {
|
|
error = fm_->open(api_path, true, ofd, handle, f);
|
|
}
|
|
} else {
|
|
error = fm_->create(api_path, meta, ofd, handle, f);
|
|
}
|
|
|
|
if (error == api_error::success) {
|
|
populate_file_info(api_path, 0, meta, *ofi);
|
|
*file_desc = reinterpret_cast<PVOID>(handle);
|
|
}
|
|
|
|
auto ret = utils::from_api_error(error);
|
|
RAISE_WINFSP_EVENT(__FUNCTION__, api_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto winfsp_drive::Flush(PVOID /*file_node*/, PVOID file_desc,
|
|
FileInfo *file_info) -> NTSTATUS {
|
|
std::string api_path;
|
|
auto error = api_error::success;
|
|
auto handle =
|
|
static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(file_desc));
|
|
if (handle) {
|
|
error = api_error::invalid_handle;
|
|
std::shared_ptr<i_open_file> f;
|
|
if (fm_->get_open_file(handle, false, f)) {
|
|
api_path = f->get_api_path();
|
|
error = f->native_operation([&](native_handle handle) {
|
|
if (not ::FlushFileBuffers(handle)) {
|
|
return api_error::os_error;
|
|
}
|
|
|
|
return api_error::success;
|
|
});
|
|
|
|
// Populate file information
|
|
api_meta_map meta;
|
|
if (provider_.get_item_meta(api_path, meta) == api_error::success) {
|
|
populate_file_info(f->get_file_size(), meta, *file_info);
|
|
}
|
|
}
|
|
}
|
|
|
|
auto ret = utils::from_api_error(error);
|
|
RAISE_WINFSP_EVENT(__FUNCTION__, api_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto winfsp_drive::get_directory_item_count(const std::string &api_path) const
|
|
-> std::uint64_t {
|
|
return provider_.get_directory_item_count(api_path);
|
|
}
|
|
|
|
auto winfsp_drive::get_directory_items(const std::string &api_path) const
|
|
-> directory_item_list {
|
|
directory_item_list list;
|
|
auto res = provider_.get_directory_items(api_path, list);
|
|
if (res != api_error::success) {
|
|
utils::error::raise_api_path_error(__FUNCTION__, api_path, res,
|
|
"failed to get directory list");
|
|
}
|
|
return list;
|
|
}
|
|
|
|
auto winfsp_drive::GetFileInfo(PVOID /*file_node*/, PVOID file_desc,
|
|
FileInfo *file_info) -> NTSTATUS {
|
|
std::string api_path;
|
|
auto error = api_error::invalid_handle;
|
|
auto handle =
|
|
static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(file_desc));
|
|
if (handle) {
|
|
std::shared_ptr<i_open_file> f;
|
|
if (fm_->get_open_file(handle, false, f)) {
|
|
api_path = f->get_api_path();
|
|
api_meta_map meta;
|
|
if ((error = provider_.get_item_meta(api_path, meta)) ==
|
|
api_error::success) {
|
|
populate_file_info(f->get_file_size(), meta, *file_info);
|
|
}
|
|
}
|
|
}
|
|
|
|
auto ret = utils::from_api_error(error);
|
|
RAISE_WINFSP_EVENT(__FUNCTION__, api_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto winfsp_drive::get_file_size(const std::string &api_path) const
|
|
-> std::uint64_t {
|
|
std::uint64_t file_size{};
|
|
auto res = provider_.get_file_size(api_path, file_size);
|
|
if (res != api_error::success) {
|
|
utils::error::raise_api_path_error(__FUNCTION__, api_path, res,
|
|
"failed to get file size");
|
|
}
|
|
|
|
return file_size;
|
|
}
|
|
|
|
auto winfsp_drive::get_item_meta(const std::string &api_path,
|
|
api_meta_map &meta) const -> api_error {
|
|
return provider_.get_item_meta(api_path, meta);
|
|
}
|
|
|
|
auto winfsp_drive::get_item_meta(const std::string &api_path,
|
|
const std::string &name,
|
|
std::string &value) const -> api_error {
|
|
api_meta_map meta{};
|
|
auto ret = provider_.get_item_meta(api_path, meta);
|
|
if (ret == api_error::success) {
|
|
value = meta[name];
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
auto winfsp_drive::get_security_by_name(PWSTR file_name, PUINT32 attributes,
|
|
PSECURITY_DESCRIPTOR descriptor,
|
|
std::uint64_t *descriptor_size)
|
|
-> NTSTATUS {
|
|
const auto api_path =
|
|
utils::path::create_api_path(utils::string::to_utf8(file_name));
|
|
|
|
api_meta_map meta{};
|
|
auto error = provider_.get_item_meta(api_path, meta);
|
|
if (error == api_error::success) {
|
|
if (attributes) {
|
|
*attributes = utils::get_attributes_from_meta(meta);
|
|
}
|
|
|
|
if (descriptor_size) {
|
|
ULONG sz{};
|
|
PSECURITY_DESCRIPTOR sd{};
|
|
|
|
if (::ConvertStringSecurityDescriptorToSecurityDescriptor(
|
|
"O:BAG:BAD:P(A;;FA;;;SY)(A;;FA;;;BA)(A;;FA;;;WD)",
|
|
SDDL_REVISION_1, &sd, &sz)) {
|
|
if (sz > *descriptor_size) {
|
|
error = api_error::buffer_too_small;
|
|
} else {
|
|
::CopyMemory(descriptor, sd, sz);
|
|
}
|
|
*descriptor_size = sz;
|
|
::LocalFree(sd);
|
|
} else {
|
|
error = api_error::os_error;
|
|
}
|
|
}
|
|
}
|
|
|
|
return utils::from_api_error(error);
|
|
}
|
|
|
|
auto winfsp_drive::GetSecurityByName(PWSTR file_name, PUINT32 attributes,
|
|
PSECURITY_DESCRIPTOR descriptor,
|
|
SIZE_T *descriptor_size) -> NTSTATUS {
|
|
const auto api_path =
|
|
utils::path::create_api_path(utils::string::to_utf8(file_name));
|
|
std::uint64_t sds = descriptor_size ? *descriptor_size : 0U;
|
|
auto ret = get_security_by_name(file_name, attributes, descriptor,
|
|
sds > 0U ? &sds : nullptr);
|
|
if (sds) {
|
|
*descriptor_size = static_cast<SIZE_T>(sds);
|
|
}
|
|
RAISE_WINFSP_EVENT(__FUNCTION__, api_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto winfsp_drive::get_total_drive_space() const -> std::uint64_t {
|
|
return provider_.get_total_drive_space();
|
|
}
|
|
|
|
auto winfsp_drive::get_total_item_count() const -> std::uint64_t {
|
|
return provider_.get_total_item_count();
|
|
}
|
|
|
|
auto winfsp_drive::get_used_drive_space() const -> std::uint64_t {
|
|
return provider_.get_used_drive_space();
|
|
}
|
|
|
|
void winfsp_drive::get_volume_info(UINT64 &total_size, UINT64 &free_size,
|
|
std::string &volume_label) const {
|
|
const auto total_bytes = provider_.get_total_drive_space();
|
|
const auto total_used = provider_.get_used_drive_space();
|
|
free_size = total_bytes - total_used;
|
|
total_size = total_bytes;
|
|
volume_label = utils::create_volume_label(config_.get_provider_type());
|
|
}
|
|
|
|
auto winfsp_drive::GetVolumeInfo(VolumeInfo *volume_info) -> NTSTATUS {
|
|
const std::wstring volume_label = utils::string::from_utf8(
|
|
utils::create_volume_label(config_.get_provider_type()));
|
|
const auto total_bytes = provider_.get_total_drive_space();
|
|
const auto total_used = provider_.get_used_drive_space();
|
|
volume_info->FreeSize = total_bytes - total_used;
|
|
volume_info->TotalSize = total_bytes;
|
|
wcscpy_s(&volume_info->VolumeLabel[0U], 32, &volume_label[0U]);
|
|
volume_info->VolumeLabelLength =
|
|
std::min(static_cast<UINT16>(64),
|
|
static_cast<UINT16>(volume_label.size() * sizeof(WCHAR)));
|
|
|
|
RAISE_WINFSP_EVENT(__FUNCTION__, utils::string::to_utf8(volume_label), 0);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
auto winfsp_drive::Init(PVOID host) -> NTSTATUS {
|
|
auto *file_system_host = reinterpret_cast<FileSystemHost *>(host);
|
|
if (not config_.get_enable_mount_manager()) {
|
|
file_system_host->SetPrefix(
|
|
&(L"\\repertory\\" +
|
|
std::wstring(file_system_host->FileSystemName()).substr(0, 1))[0u]);
|
|
}
|
|
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(TRUE);
|
|
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;
|
|
}
|
|
|
|
auto winfsp_drive::mount(const std::vector<std::string> &drive_args) -> int {
|
|
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();
|
|
auto svc = winfsp_service(lock_, *this, parsed_drive_args, config_);
|
|
auto ret = svc.Run();
|
|
|
|
event_system::instance().raise<drive_mount_result>(std::to_string(ret));
|
|
event_system::instance().stop();
|
|
c.reset();
|
|
return static_cast<int>(ret);
|
|
}
|
|
|
|
auto winfsp_drive::Mounted(PVOID host) -> NTSTATUS {
|
|
auto ret = STATUS_SUCCESS;
|
|
utils::file::change_to_process_directory();
|
|
|
|
auto *file_system_host = reinterpret_cast<FileSystemHost *>(host);
|
|
polling::instance().start(&config_);
|
|
fm_ = std::make_unique<file_manager>(config_, provider_);
|
|
server_ = std::make_unique<full_server>(config_, provider_, *fm_);
|
|
if (not provider_.is_direct_only()) {
|
|
eviction_ = std::make_unique<eviction>(provider_, config_, *fm_);
|
|
}
|
|
|
|
try {
|
|
server_->start();
|
|
if (not provider_.start(
|
|
[this](bool directory, api_file &file) -> api_error {
|
|
return provider_meta_handler(provider_, directory, file);
|
|
},
|
|
fm_.get())) {
|
|
throw startup_exception("provider is offline");
|
|
}
|
|
|
|
fm_->start();
|
|
if (eviction_) {
|
|
eviction_->start();
|
|
}
|
|
|
|
const auto mount_location =
|
|
parse_mount_location(file_system_host->MountPoint());
|
|
if (config_.get_enable_remote_mount()) {
|
|
remote_server_ = std::make_unique<remote_winfsp::remote_server>(
|
|
config_, *this, mount_location);
|
|
}
|
|
|
|
if (not lock_.set_mount_state(true, mount_location,
|
|
::GetCurrentProcessId())) {
|
|
utils::error::raise_error(__FUNCTION__, "failed to set mount state");
|
|
}
|
|
|
|
event_system::instance().raise<drive_mounted>(mount_location);
|
|
} catch (const std::exception &e) {
|
|
utils::error::raise_error(__FUNCTION__, e, "exception occurred");
|
|
if (remote_server_) {
|
|
remote_server_.reset();
|
|
}
|
|
server_->stop();
|
|
polling::instance().stop();
|
|
if (eviction_) {
|
|
eviction_->stop();
|
|
}
|
|
fm_->stop();
|
|
provider_.stop();
|
|
ret = STATUS_INTERNAL_ERROR;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
auto winfsp_drive::Open(PWSTR file_name, UINT32 create_options,
|
|
UINT32 granted_access, PVOID * /*file_node*/,
|
|
PVOID *file_desc, OpenFileInfo *ofi) -> NTSTATUS {
|
|
const auto api_path =
|
|
utils::path::create_api_path(utils::string::to_utf8(file_name));
|
|
|
|
bool directory{};
|
|
auto error = provider_.is_directory(api_path, directory);
|
|
if (error == api_error::success) {
|
|
error = api_error::directory_not_found;
|
|
if ((create_options & FILE_DIRECTORY_FILE) && not directory) {
|
|
} else {
|
|
error = api_error::success;
|
|
if (not directory &&
|
|
(((granted_access & FILE_WRITE_DATA) == FILE_WRITE_DATA) ||
|
|
((granted_access & GENERIC_WRITE) == GENERIC_WRITE))) {
|
|
error = provider_.is_file_writeable(api_path)
|
|
? api_error::success
|
|
: api_error::permission_denied;
|
|
}
|
|
|
|
if (error == api_error::success) {
|
|
open_file_data ofd{};
|
|
std::uint64_t handle{};
|
|
std::shared_ptr<i_open_file> f;
|
|
error = fm_->open(api_path, directory, ofd, handle, f);
|
|
if (error == api_error::success) {
|
|
api_meta_map meta;
|
|
if ((error = provider_.get_item_meta(api_path, meta)) ==
|
|
api_error::success) {
|
|
populate_file_info(f->get_api_path(), f->get_file_size(), meta,
|
|
*ofi);
|
|
*file_desc = reinterpret_cast<PVOID>(handle);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
auto ret = utils::from_api_error(error);
|
|
RAISE_WINFSP_EVENT(__FUNCTION__, api_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto winfsp_drive::Overwrite(PVOID /*file_node*/, PVOID file_desc,
|
|
UINT32 attributes, BOOLEAN replace_attributes,
|
|
UINT64 /*allocation_size*/, FileInfo *file_info)
|
|
-> NTSTATUS {
|
|
std::string api_path;
|
|
auto error = api_error::invalid_handle;
|
|
auto handle =
|
|
static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(file_desc));
|
|
if (handle) {
|
|
std::shared_ptr<i_open_file> f;
|
|
if (fm_->get_open_file(handle, true, f)) {
|
|
api_path = f->get_api_path();
|
|
api_meta_map meta{};
|
|
if ((error = provider_.get_item_meta(api_path, meta)) ==
|
|
api_error::success) {
|
|
error = api_error::file_in_use;
|
|
if (not fm_->is_processing(api_path)) {
|
|
if ((error = f->resize(0)) == api_error::success) {
|
|
filesystem_item existing_fsi{};
|
|
if ((error = provider_.get_filesystem_item(
|
|
api_path, false, existing_fsi)) == api_error::success) {
|
|
// Handle replace attributes
|
|
if (replace_attributes) {
|
|
if (not attributes) {
|
|
attributes = FILE_ATTRIBUTE_NORMAL;
|
|
}
|
|
meta[META_ATTRIBUTES] = std::to_string(attributes);
|
|
error = provider_.set_item_meta(api_path, meta);
|
|
} else if (attributes) { // Handle merge attributes
|
|
const auto current_attributes =
|
|
utils::get_attributes_from_meta(meta);
|
|
const auto merged_attributes = attributes | current_attributes;
|
|
if (merged_attributes != current_attributes) {
|
|
meta[META_ATTRIBUTES] = std::to_string(merged_attributes);
|
|
error = provider_.set_item_meta(api_path, meta);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Populate file information
|
|
populate_file_info(f->get_file_size(), meta, *file_info);
|
|
}
|
|
}
|
|
}
|
|
|
|
auto ret = utils::from_api_error(error);
|
|
RAISE_WINFSP_EVENT(__FUNCTION__, api_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto winfsp_drive::parse_mount_location(const std::wstring &mount_location)
|
|
-> std::string {
|
|
return utils::string::to_utf8((mount_location.substr(0, 4) == L"\\\\.\\")
|
|
? mount_location.substr(4)
|
|
: mount_location);
|
|
}
|
|
|
|
void winfsp_drive::populate_file_info(const std::string &api_path,
|
|
std::uint64_t file_size,
|
|
const api_meta_map &meta,
|
|
FSP_FSCTL_OPEN_FILE_INFO &ofi) {
|
|
const auto file_path = utils::string::from_utf8(
|
|
utils::string::replace_copy(api_path, '/', '\\'));
|
|
|
|
wcscpy_s(ofi.NormalizedName, ofi.NormalizedNameSize / sizeof(WCHAR),
|
|
&file_path[0U]);
|
|
ofi.NormalizedNameSize = (UINT16)(wcslen(&file_path[0U]) * sizeof(WCHAR));
|
|
populate_file_info(file_size, meta, ofi.FileInfo);
|
|
}
|
|
|
|
auto winfsp_drive::populate_file_info(const std::string &api_path,
|
|
remote::file_info &file_info)
|
|
-> api_error {
|
|
api_meta_map meta{};
|
|
auto ret = provider_.get_item_meta(api_path, meta);
|
|
if (ret == api_error::success) {
|
|
FSP_FSCTL_FILE_INFO fi{};
|
|
populate_file_info(utils::string::to_uint64(meta[META_SIZE]), meta, fi);
|
|
set_file_info(file_info, fi);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void winfsp_drive::populate_file_info(std::uint64_t file_size,
|
|
api_meta_map meta,
|
|
FSP_FSCTL_FILE_INFO &file_info) {
|
|
file_info.FileSize = file_size;
|
|
file_info.AllocationSize =
|
|
utils::divide_with_ceiling(file_size ? file_size : WINFSP_ALLOCATION_UNIT,
|
|
WINFSP_ALLOCATION_UNIT) *
|
|
WINFSP_ALLOCATION_UNIT;
|
|
file_info.ChangeTime = utils::get_changed_time_from_meta(meta);
|
|
file_info.CreationTime = utils::get_creation_time_from_meta(meta);
|
|
file_info.FileAttributes = utils::get_attributes_from_meta(meta);
|
|
file_info.HardLinks = 0;
|
|
file_info.IndexNumber = 0;
|
|
file_info.LastAccessTime = utils::get_accessed_time_from_meta(meta);
|
|
file_info.LastWriteTime = utils::get_written_time_from_meta(meta);
|
|
file_info.ReparseTag = 0;
|
|
file_info.EaSize = 0;
|
|
}
|
|
|
|
auto winfsp_drive::Read(PVOID /*file_node*/, PVOID file_desc, PVOID buffer,
|
|
UINT64 offset, ULONG length, PULONG bytes_transferred)
|
|
-> NTSTATUS {
|
|
*bytes_transferred = 0U;
|
|
|
|
std::string api_path;
|
|
auto error = api_error::invalid_handle;
|
|
auto handle =
|
|
static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(file_desc));
|
|
if (handle) {
|
|
if (length > 0U) {
|
|
std::shared_ptr<i_open_file> f;
|
|
if (fm_->get_open_file(handle, false, f)) {
|
|
api_path = f->get_api_path();
|
|
data_buffer data;
|
|
if ((error = f->read(length, offset, data)) == api_error::success) {
|
|
*bytes_transferred = static_cast<ULONG>(data.size());
|
|
if ((length > 0) && (data.size() != length)) {
|
|
::SetLastError(ERROR_HANDLE_EOF);
|
|
}
|
|
|
|
if (not data.empty()) {
|
|
::CopyMemory(buffer, &data[0U], data.size());
|
|
data.clear();
|
|
auto res = provider_.set_item_meta(
|
|
api_path, META_ACCESSED,
|
|
std::to_string(utils::get_file_time_now()));
|
|
if (res != api_error::success) {
|
|
utils::error::raise_api_path_error(
|
|
__FUNCTION__, api_path, res,
|
|
"failed to set meta accessed time");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
error = api_error::success;
|
|
}
|
|
}
|
|
|
|
auto ret = utils::from_api_error(error);
|
|
if (ret != STATUS_SUCCESS) {
|
|
RAISE_WINFSP_EVENT(__FUNCTION__, api_path, ret);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
auto winfsp_drive::ReadDirectory(PVOID /*file_node*/, PVOID file_desc,
|
|
PWSTR /*pattern*/, PWSTR marker, PVOID buffer,
|
|
ULONG buffer_length, PULONG bytes_transferred)
|
|
-> NTSTATUS {
|
|
std::string api_path;
|
|
auto error = api_error::invalid_handle;
|
|
auto handle =
|
|
static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(file_desc));
|
|
if (handle) {
|
|
std::shared_ptr<i_open_file> f;
|
|
if (fm_->get_open_file(handle, false, f)) {
|
|
api_path = f->get_api_path();
|
|
bool exists{};
|
|
error = provider_.is_directory(api_path, exists);
|
|
if (error == api_error::success) {
|
|
if (exists) {
|
|
directory_item_list list{};
|
|
if ((error = provider_.get_directory_items(api_path, list)) ==
|
|
api_error::success) {
|
|
directory_iterator iterator(std::move(list));
|
|
auto status_result = STATUS_SUCCESS;
|
|
auto *directory_buffer = f->get_open_data(handle).directory_buffer;
|
|
if (FspFileSystemAcquireDirectoryBuffer(
|
|
&directory_buffer, static_cast<BOOLEAN>(nullptr == marker),
|
|
&status_result)) {
|
|
directory_item di{};
|
|
auto offset =
|
|
marker
|
|
? iterator.get_next_directory_offset(
|
|
utils::path::create_api_path(utils::path::combine(
|
|
api_path, {utils::string::to_utf8(marker)})))
|
|
: 0U;
|
|
while ((error = iterator.get_directory_item(offset++, di)) ==
|
|
api_error::success) {
|
|
if (utils::path::is_ads_file_path(di.api_path) ||
|
|
di.api_path == "." || di.api_path == "..") {
|
|
continue;
|
|
}
|
|
|
|
if (di.meta.empty()) {
|
|
utils::error::raise_api_path_error(__FUNCTION__, di.api_path,
|
|
api_error::error,
|
|
"item meta is empty");
|
|
continue;
|
|
}
|
|
|
|
const auto display_name = utils::string::from_utf8(
|
|
utils::path::strip_to_file_name(di.api_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)));
|
|
|
|
populate_file_info(di.size, di.meta, directory_info->FileInfo);
|
|
::wcscpy_s(&directory_info->FileNameBuf[0U], MAX_PATH,
|
|
&display_name[0U]);
|
|
|
|
FspFileSystemFillDirectoryBuffer(
|
|
&directory_buffer, directory_info, &status_result);
|
|
}
|
|
|
|
FspFileSystemReleaseDirectoryBuffer(&directory_buffer);
|
|
}
|
|
|
|
if (status_result == STATUS_SUCCESS) {
|
|
FspFileSystemReadDirectoryBuffer(&directory_buffer, marker,
|
|
buffer, buffer_length,
|
|
bytes_transferred);
|
|
if (error == api_error::directory_end_of_files) {
|
|
error = api_error::success;
|
|
}
|
|
} else {
|
|
RAISE_WINFSP_EVENT(__FUNCTION__, api_path, status_result);
|
|
return status_result;
|
|
}
|
|
}
|
|
} else {
|
|
error = api_error::directory_not_found;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
auto ret = utils::from_api_error(error);
|
|
RAISE_WINFSP_EVENT(__FUNCTION__, api_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto winfsp_drive::Rename(PVOID /*file_node*/, PVOID /*file_desc*/,
|
|
PWSTR file_name, PWSTR new_file_name,
|
|
BOOLEAN replace_if_exists) -> NTSTATUS {
|
|
const auto from_api_path =
|
|
utils::path::create_api_path(utils::string::to_utf8(file_name));
|
|
const auto to_api_path =
|
|
utils::path::create_api_path(utils::string::to_utf8(new_file_name));
|
|
|
|
auto error = api_error::item_not_found;
|
|
bool exists{};
|
|
error = provider_.is_file(from_api_path, exists);
|
|
if (error == api_error::success) {
|
|
if (exists) {
|
|
error = fm_->rename_file(from_api_path, to_api_path, replace_if_exists);
|
|
} else {
|
|
error = provider_.is_directory(from_api_path, exists);
|
|
if (error == api_error::success && exists)
|
|
error = fm_->rename_directory(from_api_path, to_api_path);
|
|
}
|
|
}
|
|
|
|
auto ret = utils::from_api_error(error);
|
|
RAISE_WINFSP_EVENT(__FUNCTION__, from_api_path + "|" + to_api_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
auto 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)
|
|
-> NTSTATUS {
|
|
std::string api_path;
|
|
auto error = api_error::invalid_handle;
|
|
auto handle =
|
|
static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(file_desc));
|
|
if (handle) {
|
|
std::shared_ptr<i_open_file> f;
|
|
if (fm_->get_open_file(handle, false, f)) {
|
|
api_path = f->get_api_path();
|
|
if (attributes == INVALID_FILE_ATTRIBUTES) {
|
|
attributes = 0;
|
|
} else if (attributes == 0) {
|
|
attributes = f->is_directory() ? FILE_ATTRIBUTE_DIRECTORY
|
|
: FILE_ATTRIBUTE_NORMAL;
|
|
}
|
|
|
|
api_meta_map meta;
|
|
if (attributes) {
|
|
if ((attributes & FILE_ATTRIBUTE_NORMAL) &&
|
|
(attributes != FILE_ATTRIBUTE_NORMAL)) {
|
|
attributes &= ~(FILE_ATTRIBUTE_NORMAL);
|
|
}
|
|
meta[META_ATTRIBUTES] = std::to_string(attributes);
|
|
}
|
|
if (creation_time && (creation_time != 0xFFFFFFFFFFFFFFFF)) {
|
|
meta[META_CREATION] = std::to_string(creation_time);
|
|
}
|
|
if (last_access_time && (last_access_time != 0xFFFFFFFFFFFFFFFF)) {
|
|
meta[META_ACCESSED] = std::to_string(last_access_time);
|
|
}
|
|
if (last_write_time && (last_write_time != 0xFFFFFFFFFFFFFFFF)) {
|
|
meta[META_WRITTEN] = std::to_string(last_write_time);
|
|
}
|
|
if (change_time && (change_time != 0xFFFFFFFFFFFFFFFF)) {
|
|
meta[META_CHANGED] = std::to_string(change_time);
|
|
meta[META_MODIFIED] = std::to_string(change_time);
|
|
}
|
|
|
|
error = provider_.set_item_meta(api_path, meta);
|
|
|
|
// Populate file information
|
|
if (provider_.get_item_meta(api_path, meta) == api_error::success) {
|
|
populate_file_info(utils::string::to_uint64(meta[META_SIZE]), meta,
|
|
*file_info);
|
|
}
|
|
}
|
|
}
|
|
|
|
auto ret = utils::from_api_error(error);
|
|
RAISE_WINFSP_EVENT(__FUNCTION__, api_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
void winfsp_drive::set_file_info(remote::file_info &dest,
|
|
const FSP_FSCTL_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;
|
|
}
|
|
|
|
auto winfsp_drive::SetFileSize(PVOID /*file_node*/, PVOID file_desc,
|
|
UINT64 new_size, BOOLEAN set_allocation_size,
|
|
FileInfo *file_info) -> NTSTATUS {
|
|
std::string api_path;
|
|
auto error = api_error::invalid_handle;
|
|
auto handle =
|
|
static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(file_desc));
|
|
if (handle) {
|
|
std::shared_ptr<i_open_file> f;
|
|
if (fm_->get_open_file(handle, true, f)) {
|
|
api_path = f->get_api_path();
|
|
i_open_file::native_operation_callback allocator;
|
|
if (set_allocation_size) {
|
|
allocator = [&](native_handle handle) -> api_error {
|
|
std::string meta_allocation_size;
|
|
utils::calculate_allocation_size(false, 0, new_size,
|
|
meta_allocation_size);
|
|
|
|
FILE_ALLOCATION_INFO allocation_info{};
|
|
allocation_info.AllocationSize.QuadPart = new_size;
|
|
return ::SetFileInformationByHandle(handle, FileAllocationInfo,
|
|
&allocation_info,
|
|
sizeof(allocation_info))
|
|
? api_error::success
|
|
: api_error::os_error;
|
|
};
|
|
} else {
|
|
allocator = [&](native_handle handle) -> api_error {
|
|
FILE_END_OF_FILE_INFO end_of_file_info{};
|
|
end_of_file_info.EndOfFile.QuadPart = new_size;
|
|
return ::SetFileInformationByHandle(handle, FileEndOfFileInfo,
|
|
&end_of_file_info,
|
|
sizeof(end_of_file_info))
|
|
? api_error::success
|
|
: api_error::os_error;
|
|
};
|
|
}
|
|
|
|
error = f->native_operation(new_size, allocator);
|
|
if (file_info) {
|
|
// Populate file information
|
|
api_meta_map meta;
|
|
if (provider_.get_item_meta(api_path, meta) == api_error::success) {
|
|
populate_file_info(f->get_file_size(), meta, *file_info);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
auto ret = utils::from_api_error(error);
|
|
RAISE_WINFSP_EVENT(__FUNCTION__, api_path, ret);
|
|
return ret;
|
|
}
|
|
|
|
VOID winfsp_drive::Unmounted(PVOID host) {
|
|
auto *file_system_host = reinterpret_cast<FileSystemHost *>(host);
|
|
const auto mount_location =
|
|
parse_mount_location(file_system_host->MountPoint());
|
|
event_system::instance().raise<drive_unmount_pending>(mount_location);
|
|
if (remote_server_) {
|
|
remote_server_.reset();
|
|
}
|
|
server_->stop();
|
|
polling::instance().stop();
|
|
if (eviction_) {
|
|
eviction_->stop();
|
|
}
|
|
fm_->stop();
|
|
provider_.stop();
|
|
server_.reset();
|
|
fm_.reset();
|
|
eviction_.reset();
|
|
|
|
if (not lock_.set_mount_state(false, "", -1)) {
|
|
utils::error::raise_error(__FUNCTION__, "failed to set mount state");
|
|
}
|
|
|
|
event_system::instance().raise<drive_unmounted>(mount_location);
|
|
config_.save();
|
|
}
|
|
|
|
auto 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) -> NTSTATUS {
|
|
*bytes_transferred = 0;
|
|
|
|
std::string api_path;
|
|
auto error = api_error::invalid_handle;
|
|
auto handle =
|
|
static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(file_desc));
|
|
if (handle) {
|
|
std::shared_ptr<i_open_file> f;
|
|
if (fm_->get_open_file(handle, true, f)) {
|
|
api_path = f->get_api_path();
|
|
if (write_to_end) {
|
|
offset = f->get_file_size();
|
|
}
|
|
|
|
auto should_write = true;
|
|
if (constrained_io) {
|
|
if (offset >= f->get_file_size()) {
|
|
error = api_error::success;
|
|
should_write = false;
|
|
} else if (offset + length > f->get_file_size()) {
|
|
length = static_cast<ULONG>(f->get_file_size() - offset);
|
|
}
|
|
}
|
|
|
|
if (should_write) {
|
|
if (length > 0U) {
|
|
std::size_t bytes_written{};
|
|
data_buffer data(length);
|
|
std::memcpy(&data[0U], buffer, length);
|
|
if ((error = f->write(offset, data, bytes_written)) ==
|
|
api_error::success) {
|
|
*bytes_transferred = static_cast<ULONG>(bytes_written);
|
|
|
|
// Populate file information
|
|
api_meta_map meta;
|
|
if (provider_.get_item_meta(api_path, meta) == api_error::success) {
|
|
populate_file_info(f->get_file_size(), meta, *file_info);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
error = api_error::success;
|
|
}
|
|
}
|
|
}
|
|
|
|
auto ret = utils::from_api_error(error);
|
|
if (ret != STATUS_SUCCESS) {
|
|
RAISE_WINFSP_EVENT(__FUNCTION__, api_path, ret);
|
|
}
|
|
return ret;
|
|
}
|
|
} // namespace repertory
|
|
|
|
#endif // _WIN32
|