winfsp unit tests and fixes-support remote mount

This commit is contained in:
Scott E. Graves 2024-11-05 12:46:13 -06:00
parent f5993d472c
commit cda89d0588
3 changed files with 201 additions and 184 deletions

View File

@ -986,16 +986,14 @@ auto remote_server::winfsp_create(PWSTR file_name, UINT32 create_options,
{utils::string::to_utf8(file_name)}))
.exists());
auto create_flags = FILE_FLAG_BACKUP_SEMANTICS;
auto create_flags{FILE_FLAG_BACKUP_SEMANTICS};
if ((create_options & FILE_DIRECTORY_FILE) != 0U) {
create_flags |= FILE_FLAG_POSIX_SEMANTICS;
attributes |= FILE_ATTRIBUTE_DIRECTORY;
} else {
attributes &= static_cast<UINT32>(
~(FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_NORMAL));
attributes |= FILE_ATTRIBUTE_ARCHIVE;
}
attributes |=
((create_options & FILE_DIRECTORY_FILE) == 0U ? FILE_ATTRIBUTE_ARCHIVE
: FILE_ATTRIBUTE_DIRECTORY);
auto *handle = ::CreateFileW(
file_path.c_str(), granted_access,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
@ -1161,34 +1159,23 @@ auto remote_server::winfsp_overwrite(PVOID file_desc, UINT32 attributes,
auto *handle = reinterpret_cast<HANDLE>(file_desc);
auto ret = has_open_info(handle, STATUS_INVALID_HANDLE);
if (ret == STATUS_SUCCESS) {
if (replace_attributes != 0U) {
if (attributes == 0U) {
attributes = FILE_ATTRIBUTE_ARCHIVE;
}
attributes |= FILE_ATTRIBUTE_ARCHIVE;
if (replace_attributes != 0U) {
FILE_BASIC_INFO basic_info{};
basic_info.FileAttributes = attributes;
if (::SetFileInformationByHandle(handle, FileBasicInfo, &basic_info,
sizeof(FILE_BASIC_INFO)) == 0) {
ret = FspNtStatusFromWin32(::GetLastError());
}
} else if (attributes != 0U) {
} else {
FILE_ATTRIBUTE_TAG_INFO tag_info{};
if (::GetFileInformationByHandleEx(
handle, FileAttributeTagInfo, &tag_info,
sizeof(FILE_ATTRIBUTE_TAG_INFO)) == 0) {
ret = FspNtStatusFromWin32(::GetLastError());
if (::GetFileInformationByHandleEx(handle, FileAttributeTagInfo,
&tag_info,
sizeof(FILE_ATTRIBUTE_TAG_INFO))) {
attributes |= tag_info.FileAttributes;
} else {
FILE_BASIC_INFO basic_info{};
basic_info.FileAttributes =
attributes | (tag_info.FileAttributes &
static_cast<UINT32>(~FILE_ATTRIBUTE_NORMAL));
if ((basic_info.FileAttributes != tag_info.FileAttributes)) {
if (::SetFileInformationByHandle(handle, FileBasicInfo, &basic_info,
sizeof(FILE_BASIC_INFO)) == 0) {
ret = FspNtStatusFromWin32(::GetLastError());
}
}
ret = FspNtStatusFromWin32(::GetLastError());
}
}

View File

@ -26,7 +26,7 @@
#include "test_common.hpp"
#include "app_config.hpp"
#include "comm/curl/curl_comm.hpp"
#include "drives/winfsp/remotewinfsp/remote_winfsp_drive.hpp"
#include "drives/winfsp/winfsp_drive.hpp"
#include "platform/platform.hpp"
#include "providers/i_provider.hpp"
@ -42,215 +42,242 @@ constexpr const auto SLEEP_SECONDS{1.5s};
} // namespace
namespace repertory {
struct local_s3 final {
static constexpr const provider_type type{provider_type::s3};
static constexpr const provider_type type2{provider_type::s3};
};
struct local_sia final {
static constexpr const provider_type type{provider_type::sia};
static constexpr const provider_type type2{provider_type::sia};
};
struct remote_s3 final {
static constexpr const provider_type type{provider_type::remote};
static constexpr const provider_type type2{provider_type::s3};
};
struct remote_sia final {
static constexpr const provider_type type{provider_type::remote};
static constexpr const provider_type type2{provider_type::sia};
};
template <typename provider_t> class winfsp_test : public ::testing::Test {
public:
static std::string cfg_directory;
static std::unique_ptr<curl_comm> comm;
static std::unique_ptr<app_config> config;
static std::filesystem::path current_directory;
static std::unique_ptr<winfsp_drive> drive;
static std::vector<std::string> drive_args;
static std::string mount_location;
static std::unique_ptr<i_provider> provider;
static std::string test_directory;
static provider_type current_provider;
static std::vector<std::string> drive_args;
static std::vector<std::string> drive_args2;
static std::string mount_location;
static std::string mount_location2;
protected:
static void SetUpTestCase() {
current_directory = std::filesystem::current_path();
test_directory = utils::path::combine(
test::get_test_output_dir(),
{
"winfsp_test",
app_config::get_provider_name(current_provider),
});
mount_location = "U:";
cfg_directory = utils::path::combine(test_directory, {"cfg"});
ASSERT_TRUE(utils::file::directory(cfg_directory).create_directory());
config = std::make_unique<app_config>(current_provider, cfg_directory);
switch (current_provider) {
case provider_type::s3: {
const auto mount_s3 = [&]() {
{
app_config src_cfg{
provider_type::s3,
utils::path::combine(test::get_test_config_dir(), {"s3"}),
};
config->set_enable_drive_events(true);
config->set_event_level(event_level::trace);
config->set_s3_config(src_cfg.get_s3_config());
auto test_directory = utils::path::combine(
test::get_test_output_dir(),
{
"winfsp_test",
app_config::get_provider_name(provider_type::s3),
});
mount_location = "U:";
auto cfg_directory = utils::path::combine(test_directory, {"cfg"});
ASSERT_TRUE(utils::file::directory(cfg_directory).create_directory());
auto config =
std::make_unique<app_config>(provider_type::s3, cfg_directory);
{
app_config src_cfg{
provider_type::s3,
utils::path::combine(test::get_test_config_dir(), {"s3"}),
};
config->set_enable_drive_events(true);
config->set_event_level(event_level::trace);
config->set_s3_config(src_cfg.get_s3_config());
config->set_enable_remote_mount(true);
config->set_remote_port(30000U);
}
drive_args = std::vector<std::string>({
"-dd",
config->get_data_directory(),
"-s3",
"-na",
"s3",
mount_location,
});
}
execute_mount(drive_args, mount_location);
};
const auto mount_sia = [&]() {
{
auto test_directory = utils::path::combine(
test::get_test_output_dir(),
{
"winfsp_test",
app_config::get_provider_name(provider_type::sia),
});
mount_location = "U:";
auto cfg_directory = utils::path::combine(test_directory, {"cfg"});
ASSERT_TRUE(utils::file::directory(cfg_directory).create_directory());
auto config =
std::make_unique<app_config>(provider_type::s3, cfg_directory);
{
app_config src_cfg{
provider_type::sia,
utils::path::combine(test::get_test_config_dir(), {"sia"}),
};
config->set_enable_drive_events(true);
config->set_event_level(event_level::trace);
config->set_host_config(src_cfg.get_host_config());
config->set_sia_config(src_cfg.get_sia_config());
config->set_enable_remote_mount(true);
config->set_remote_port(30000U);
}
drive_args = std::vector<std::string>({
"-dd",
config->get_data_directory(),
"-na",
"sia",
mount_location,
});
}
drive_args = std::vector<std::string>({
"-s3",
"-na",
"s3",
});
execute_mount(drive_args, mount_location);
};
const auto mount_remote = [&]() {
{
auto test_directory = utils::path::combine(
test::get_test_output_dir(),
{
"winfsp_test",
app_config::get_provider_name(provider_type::remote),
});
mount_location2 = mount_location;
mount_location = "V:";
auto cfg_directory = utils::path::combine(test_directory, {"cfg2"});
ASSERT_TRUE(utils::file::directory(cfg_directory).create_directory());
auto config =
std::make_unique<app_config>(provider_type::remote, cfg_directory);
config->set_enable_drive_events(true);
config->set_event_level(event_level::trace);
drive_args2 = std::vector<std::string>({
"-dd",
config->get_data_directory(),
"-rm",
"localhost:30000",
mount_location,
});
}
execute_mount(drive_args2, mount_location);
};
switch (provider_t::type) {
case provider_type::s3: {
mount_s3();
} break;
case provider_type::sia: {
{
app_config src_cfg{
provider_type::sia,
utils::path::combine(test::get_test_config_dir(), {"sia"}),
};
config->set_enable_drive_events(true);
config->set_event_level(event_level::trace);
config->set_host_config(src_cfg.get_host_config());
config->set_sia_config(src_cfg.get_sia_config());
mount_sia();
} break;
case provider_type::remote: {
switch (provider_t::type2) {
case provider_type::s3: {
mount_s3();
} break;
case provider_type::sia: {
mount_sia();
} break;
default:
throw std::runtime_error("remote provider type is not implemented");
return;
}
drive_args = std::vector<std::string>({
"-na",
"sia",
});
mount_remote();
} break;
// case 0U: {
// config =
// std::make_unique<app_config>(provider_type::encrypt,
// cfg_directory);
// {
// app_config src_cfg(
// provider_type::s3,
// utils::path::combine(test::get_test_input_dir(), {"encrypt"}));
// config->set_enable_drive_events(true);
// config->set_event_level(event_level::trace);
// config->set_s3_config(src_cfg.get_s3_config());
// }
// } break;
default:
throw std::runtime_error("provider type is not implemented");
return;
}
drive_args.push_back(mount_location);
execute_mount();
}
static void TearDownTestCase() {
execute_unmount();
if (provider_t::type == provider_type::remote) {
execute_unmount(drive_args2, mount_location);
execute_unmount(drive_args, mount_location2);
} else {
execute_unmount(drive_args, mount_location);
}
std::filesystem::current_path(current_directory);
}
public:
[[nodiscard]] static auto create_directory_and_test(std::string &dir_name)
-> std::string {
dir_name += std::to_string(++idx);
auto api_path = utils::path::create_api_path(dir_name);
auto dir_path = utils::path::combine(mount_location, {dir_name});
EXPECT_FALSE(::PathIsDirectoryA(dir_path.c_str()));
EXPECT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
EXPECT_TRUE(::PathIsDirectoryA(dir_path.c_str()));
return dir_path;
}
[[nodiscard]] static auto create_file_and_test(std::string &file_name)
-> std::string {
file_name += std::to_string(++idx);
auto api_path = utils::path::create_api_path(file_name);
auto file_path = utils::path::combine(mount_location, {file_name});
auto handle =
::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
EXPECT_NE(INVALID_HANDLE_VALUE, handle);
EXPECT_TRUE(::CloseHandle(handle));
EXPECT_TRUE(utils::file::file(file_path).exists());
auto opt_size = utils::file::file(file_path).size();
EXPECT_TRUE(opt_size.has_value());
EXPECT_EQ(0, opt_size.value());
std::string attr;
EXPECT_EQ(api_error::success,
provider->get_item_meta(api_path, META_ATTRIBUTES, attr));
EXPECT_EQ(FILE_ATTRIBUTE_ARCHIVE, utils::string::to_uint32(attr));
return file_path;
}
static void delete_directory_and_test(const std::string &dir_path) {
EXPECT_TRUE(::PathIsDirectoryA(dir_path.c_str()));
EXPECT_TRUE(::RemoveDirectoryA(dir_path.c_str()));
EXPECT_FALSE(::PathIsDirectoryA(dir_path.c_str()));
}
static void delete_file_and_test(std::string_view file_path) {
EXPECT_TRUE(utils::file::file(file_path).remove());
EXPECT_FALSE(utils::file::file(file_path).exists());
EXPECT_FALSE(utils::file::directory(file_path).exists());
EXPECT_FALSE(utils::file::file(file_path).exists());
}
static void execute_mount() {
auto mount_cmd = "start .\\repertory.exe -f -dd \"" +
config->get_data_directory() + "\"" + " " +
utils::string::join(drive_args, ' ');
static void execute_mount(auto args, auto location) {
auto mount_cmd =
"start .\\repertory.exe -f " + utils::string::join(args, ' ');
std::cout << "mount command: " << mount_cmd << std::endl;
ASSERT_EQ(0, system(mount_cmd.c_str()));
std::this_thread::sleep_for(5s);
ASSERT_TRUE(utils::file::directory{mount_location}.exists());
ASSERT_TRUE(utils::file::directory{location}.exists());
}
static void execute_unmount() {
static void execute_unmount(auto args, auto location) {
auto unmounted{false};
auto unmount_cmd = ".\\repertory.exe -dd \"" +
config->get_data_directory() + "\"" + " " +
utils::string::join(drive_args, ' ') + " -unmount";
for (int i = 0; not unmounted && (i < 50); i++) {
auto unmount_cmd =
".\\repertory.exe " + utils::string::join(args, ' ') + " -unmount";
for (int i = 0; not unmounted && (i < 6); i++) {
std::cout << "unmount command: " << unmount_cmd << std::endl;
system(unmount_cmd.c_str());
unmounted = not utils::file::directory{mount_location}.exists();
unmounted = not utils::file::directory{location}.exists();
if (not unmounted) {
std::this_thread::sleep_for(5s);
}
}
EXPECT_TRUE(unmounted);
ASSERT_TRUE(unmounted);
}
};
template <typename provider_t>
std::string winfsp_test<provider_t>::cfg_directory;
template <typename provider_t>
std::unique_ptr<curl_comm> winfsp_test<provider_t>::comm;
template <typename provider_t>
std::unique_ptr<app_config> winfsp_test<provider_t>::config;
template <typename provider_t>
std::filesystem::path winfsp_test<provider_t>::current_directory;
template <typename provider_t>
provider_type winfsp_test<provider_t>::current_provider{provider_t::type};
template <typename provider_t>
std::unique_ptr<winfsp_drive> winfsp_test<provider_t>::drive;
provider_type winfsp_test<provider_t>::current_provider{provider_t::type2};
template <typename provider_t>
std::vector<std::string> winfsp_test<provider_t>::drive_args;
template <typename provider_t>
std::vector<std::string> winfsp_test<provider_t>::drive_args2;
template <typename provider_t>
std::string winfsp_test<provider_t>::mount_location;
template <typename provider_t>
std::unique_ptr<i_provider> winfsp_test<provider_t>::provider;
template <typename provider_t>
std::string winfsp_test<provider_t>::test_directory;
using winfsp_provider_types = ::testing::Types<s3_provider>;
// using winfsp_provider_types = ::testing::Types<s3_provider, sia_provider>;
std::string winfsp_test<provider_t>::mount_location2;
// using winfsp_provider_types = ::testing::Types<local_s3, local_sia,
// remote_s3, remote_sia>;
using winfsp_provider_types = ::testing::Types<local_s3, remote_s3>;
} // namespace repertory
#endif // defined(_WIN32)

View File

@ -138,6 +138,9 @@ TYPED_TEST(winfsp_test, info_can_get_file_name_info) {
}
TYPED_TEST(winfsp_test, info_can_get_file_info) {
FILETIME file_time{};
::GetSystemTimeAsFileTime(&file_time);
auto time_low = ((PLARGE_INTEGER)&file_time)->QuadPart;
auto time_high = time_low + 10000 * 10000 /* 10 seconds */;
@ -153,14 +156,14 @@ TYPED_TEST(winfsp_test, info_can_get_file_info) {
BY_HANDLE_FILE_INFORMATION file_info{};
EXPECT_TRUE(::GetFileInformationByHandle(handle, &file_info));
EXPECT_LE(time_low, file_info.ftCreationTime.QuadPart);
EXPECT_GT(time_high, file_info.ftCreationTime.QuadPart);
EXPECT_LE(time_low, file_info.ftCreationTime.dwLowDateTime);
EXPECT_GT(time_high, file_info.ftCreationTime.dwHighDateTime);
EXPECT_LE(time_low, file_info.ftLastAccessTime.QuadPart);
EXPECT_GT(time_high, file_info.ftLastAccessTime.QuadPart);
EXPECT_LE(time_low, file_info.ftLastAccessTime.dwLowDateTime);
EXPECT_GT(time_high, file_info.ftLastAccessTime.dwHighDateTime);
EXPECT_LE(time_low, file_info.ftLastWriteTime.QuadPart);
EXPECT_GT(time_high, file_info.ftLastWriteTime.QuadPart);
EXPECT_LE(time_low, file_info.ftLastWriteTime.dwLowDateTime);
EXPECT_GT(time_high, file_info.ftLastWriteTime.dwHighDateTime);
EXPECT_EQ(0U, file_info.nFileSizeHigh);
EXPECT_EQ(0U, file_info.nFileSizeLow);
@ -169,7 +172,7 @@ TYPED_TEST(winfsp_test, info_can_get_file_info) {
EXPECT_EQ(FILE_ATTRIBUTE_ARCHIVE, file_info.dwFileAttributes);
EXPECT_EQ(0U, file_info.dwVolumeSerialNumber)
EXPECT_EQ(0U, file_info.dwVolumeSerialNumber);
::CloseHandle(handle);
}