From 4c3d75983705cd6b900d8f141ece73b63025777f Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 9 Nov 2024 14:11:44 -0600 Subject: [PATCH] winfsp unit tests and fixes --- .../src/winfsp_drive_rdrw_test.cpp | 334 +++++++++++++++++- 1 file changed, 331 insertions(+), 3 deletions(-) diff --git a/repertory/repertory_test/src/winfsp_drive_rdrw_test.cpp b/repertory/repertory_test/src/winfsp_drive_rdrw_test.cpp index 045fa36d..0d7e0b0c 100644 --- a/repertory/repertory_test/src/winfsp_drive_rdrw_test.cpp +++ b/repertory/repertory_test/src/winfsp_drive_rdrw_test.cpp @@ -30,7 +30,7 @@ namespace repertory { TYPED_TEST_CASE(winfsp_test, winfsp_provider_types); -static void test_file(auto mount_location, auto &&file_path, auto &&flags) { +static void test_file(auto &&mount_location, auto &&file_path, auto &&flags) { SYSTEM_INFO sys_info{}; ::GetSystemInfo(&sys_info); @@ -195,7 +195,7 @@ static void test_file(auto mount_location, auto &&file_path, auto &&flags) { EXPECT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError()); } -static void test_append_file(auto mount_location, auto &&file_path, +static void test_append_file(auto &&mount_location, auto &&file_path, auto &&flags, bool should_fail = false) { SYSTEM_INFO sys_info{}; ::GetSystemInfo(&sys_info); @@ -258,7 +258,7 @@ static void test_append_file(auto mount_location, auto &&file_path, EXPECT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError()); } -static void test_overlapped_file(auto mount_location, auto &&file_path, +static void test_overlapped_file(auto &&mount_location, auto &&file_path, auto &&flags) { SYSTEM_INFO sys_info{}; ::GetSystemInfo(&sys_info); @@ -440,6 +440,306 @@ static void test_overlapped_file(auto mount_location, auto &&file_path, EXPECT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError()); } +static void test_mixed_file(auto &&mount_location, auto &&file_path) { + SYSTEM_INFO sys_info{}; + ::GetSystemInfo(&sys_info); + + DWORD bytes_per_sector{}; + DWORD free_clusters{}; + DWORD sectors_per_cluster{}; + DWORD total_clusters{}; + EXPECT_TRUE(::GetDiskFreeSpaceA(mount_location.c_str(), §ors_per_cluster, + &bytes_per_sector, &free_clusters, + &total_clusters)); + const auto buffer_size = 16U * sys_info.dwPageSize; + auto write_buffer = utils::generate_secure_random(buffer_size); + + auto handle0 = CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, + CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr); + ASSERT_NE(INVALID_HANDLE_VALUE, handle0); + + auto handle1 = + ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING, nullptr); + ASSERT_NE(INVALID_HANDLE_VALUE, handle0); + + auto pointer = ::SetFilePointer(handle0, 0, 0, FILE_BEGIN); + EXPECT_EQ(0U, pointer); + + DWORD bytes_written{}; + EXPECT_TRUE(::WriteFile(handle0, write_buffer.data(), bytes_per_sector, + &bytes_written, nullptr)); + + EXPECT_EQ(bytes_per_sector, bytes_written); + EXPECT_EQ(pointer + bytes_per_sector, + ::SetFilePointer(handle0, 0, 0, FILE_CURRENT)); + + pointer = ::SetFilePointer(handle1, 2 * bytes_per_sector, 0, FILE_BEGIN); + EXPECT_EQ(2U * bytes_per_sector, pointer); + + EXPECT_TRUE(::WriteFile(handle1, write_buffer.data(), bytes_per_sector, + &bytes_written, nullptr)); + EXPECT_EQ(bytes_per_sector, bytes_written); + EXPECT_EQ(pointer + bytes_written, + ::SetFilePointer(handle1, 0, 0, FILE_CURRENT)); + + pointer = ::SetFilePointer(handle0, 0, 0, FILE_BEGIN); + EXPECT_EQ(0U, pointer); + + data_buffer read_buffer{}; + read_buffer.resize(buffer_size); + DWORD bytes_read{}; + EXPECT_TRUE(::ReadFile(handle0, read_buffer.data(), bytes_per_sector, + &bytes_read, nullptr)); + EXPECT_EQ(bytes_per_sector, bytes_read); + EXPECT_EQ(pointer + bytes_read, + ::SetFilePointer(handle0, 0, 0, FILE_CURRENT)); + EXPECT_EQ(0, + std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read)); + + read_buffer.clear(); + read_buffer.resize(buffer_size); + pointer = ::SetFilePointer(handle1, 0, 0, FILE_BEGIN); + EXPECT_EQ(0U, pointer); + EXPECT_TRUE(::ReadFile(handle1, read_buffer.data(), bytes_per_sector, + &bytes_read, nullptr)); + EXPECT_EQ(bytes_per_sector, bytes_read); + EXPECT_EQ(pointer + bytes_read, + ::SetFilePointer(handle1, 0, 0, FILE_CURRENT)); + EXPECT_EQ(0, + std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read)); + + EXPECT_TRUE(::CloseHandle(handle0)); + + EXPECT_TRUE(::CloseHandle(handle1)); + + EXPECT_TRUE(::DeleteFileA(file_path.c_str())); + + handle0 = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, + CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr); + EXPECT_NE(INVALID_HANDLE_VALUE, handle0); + + pointer = ::SetFilePointer(handle0, 0, 0, FILE_BEGIN); + EXPECT_EQ(0U, pointer); + EXPECT_TRUE(::WriteFile(handle0, write_buffer.data(), bytes_per_sector, + &bytes_written, nullptr)); + EXPECT_EQ(bytes_per_sector, bytes_written); + EXPECT_EQ(pointer + bytes_written, + ::SetFilePointer(handle0, 0, 0, FILE_CURRENT)); + + pointer = ::SetFilePointer(handle0, 2 * bytes_per_sector, 0, FILE_BEGIN); + EXPECT_EQ(2U * bytes_per_sector, pointer); + EXPECT_TRUE(::WriteFile(handle0, write_buffer.data(), bytes_per_sector, + &bytes_written, nullptr)); + EXPECT_EQ(bytes_per_sector, bytes_written); + EXPECT_EQ(pointer + bytes_written, + ::SetFilePointer(handle0, 0, 0, FILE_CURRENT)); + + pointer = ::SetFilePointer(handle0, 0, 0, FILE_BEGIN); + EXPECT_EQ(0U, pointer); + + read_buffer.clear(); + read_buffer.resize(buffer_size); + EXPECT_TRUE(::ReadFile(handle0, read_buffer.data(), bytes_per_sector, + &bytes_read, nullptr)); + EXPECT_EQ(bytes_per_sector, bytes_read); + EXPECT_EQ(pointer + bytes_read, + ::SetFilePointer(handle0, 0, 0, FILE_CURRENT)); + EXPECT_EQ(0, + std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read)); + + EXPECT_TRUE(::CloseHandle(handle0)); + + handle1 = + ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING, nullptr); + EXPECT_NE(INVALID_HANDLE_VALUE, handle0); + + pointer = ::SetFilePointer(handle1, 0, 0, FILE_BEGIN); + EXPECT_EQ(0U, pointer); + + read_buffer.clear(); + read_buffer.resize(buffer_size); + EXPECT_TRUE(::ReadFile(handle1, read_buffer.data(), bytes_per_sector, + &bytes_read, nullptr)); + EXPECT_EQ(bytes_per_sector, bytes_read); + EXPECT_EQ(pointer + bytes_read, + ::SetFilePointer(handle1, 0, 0, FILE_CURRENT)); + EXPECT_EQ(0, + std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read)); + + EXPECT_TRUE(::CloseHandle(handle1)); + + EXPECT_TRUE(::DeleteFileA(file_path.c_str())); +} + +// static void test_mmap_file(auto &&mount_location, auto &&file_path, +// auto &&flags) { +// SYSTEM_INFO sys_info{}; +// ::GetSystemInfo(&sys_info); +// +// DWORD bytes_per_sector{}; +// DWORD free_clusters{}; +// DWORD sectors_per_cluster{}; +// DWORD total_clusters{}; +// EXPECT_TRUE(::GetDiskFreeSpaceA(mount_location.c_str(), +// §ors_per_cluster, +// &bytes_per_sector, &free_clusters, +// &total_clusters)); +// const auto buffer_size = 16U * sys_info.dwPageSize; +// auto write_buffer = +// utils::generate_secure_random(buffer_size); +// +// handle1 = CreateFileW(FilePath, GENERIC_READ | GENERIC_WRITE, +// FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_ALWAYS, +// FILE_ATTRIBUTE_NORMAL | CreateFlags, 0); +// ASSERT(INVALID_HANDLE_VALUE != handle1); +// +// Mapping = CreateFileMappingW(handle1, 0, PAGE_READWRITE, 0, +// FileSize0 + FileSize1, 0); +// ASSERT(0 != Mapping); +// +// if (EarlyClose) { +// Success = CloseHandle(Handle); +// ASSERT(Success); +// } +// +// MappedView = MapViewOfFile(Mapping, FILE_MAP_ALL_ACCESS, 0, 0, 0); +// ASSERT(0 != MappedView); +// +// srand(seed); +// for (PUINT8 Bgn = MappedView + FileSize1 / 2, End = Bgn + FileSize0; +// End > Bgn; Bgn++) +// *Bgn = rand() & 0xff; +// +// Success = UnmapViewOfFile(MappedView); +// ASSERT(Success); +// +// MappedView = MapViewOfFile(Mapping, FILE_MAP_ALL_ACCESS, 0, 0, 0); +// ASSERT(0 != MappedView); +// +// srand(seed); +// for (PUINT8 Bgn = MappedView + FileSize1 / 2, End = Bgn + FileSize0; +// End > Bgn; Bgn++) +// ASSERT(*Bgn == (rand() & 0xff)); +// +// Success = UnmapViewOfFile(MappedView); +// ASSERT(Success); +// +// Success = CloseHandle(Mapping); +// ASSERT(Success); +// +// if (!EarlyClose) { +// Success = CloseHandle(Handle); +// ASSERT(Success); +// } +// +// handle1 = CreateFileW(FilePath, GENERIC_READ | GENERIC_WRITE, +// FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, +// FILE_ATTRIBUTE_NORMAL | CreateFlags, 0); +// ASSERT(INVALID_HANDLE_VALUE != handle1); +// +// Mapping = CreateFileMappingW(handle1, 0, PAGE_READWRITE, 0, +// FileSize0 + FileSize1, 0); +// ASSERT(0 != Mapping); +// +// if (EarlyClose) { +// Success = CloseHandle(Handle); +// ASSERT(Success); +// } +// +// MappedView = MapViewOfFile(Mapping, FILE_MAP_ALL_ACCESS, 0, 0, 0); +// ASSERT(0 != MappedView); +// +// srand(seed); +// for (PUINT8 Bgn = MappedView + FileSize1 / 2, End = Bgn + FileSize0; +// End > Bgn; Bgn++) +// ASSERT(*Bgn == (rand() & 0xff)); +// +// if (!EarlyClose) { +// pointer = SetFilePointer(handle1, FileSize0 / 2, 0, FILE_BEGIN); +// ASSERT(FileSize0 / 2 == pointer); +// Success = +// WriteFile(handle1, Buffer[0], bytes_per_sector, &BytesTransferred, 0); +// ASSERT(Success); +// ASSERT(bytes_per_sector == BytesTransferred); +// ASSERT(pointer + BytesTransferred == +// SetFilePointer(handle1, 0, 0, FILE_CURRENT)); +// } +// +// Success = UnmapViewOfFile(MappedView); +// ASSERT(Success); +// +// Success = CloseHandle(Mapping); +// ASSERT(Success); +// +// if (!EarlyClose) { +// Success = CloseHandle(Handle); +// ASSERT(Success); +// } +// +// handle1 = CreateFileW(FilePath, GENERIC_READ | GENERIC_WRITE, +// FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, +// FILE_ATTRIBUTE_NORMAL | CreateFlags, 0); +// ASSERT(INVALID_HANDLE_VALUE != handle1); +// +// Mapping = CreateFileMappingW(handle1, 0, PAGE_READWRITE, 0, +// FileSize0 + FileSize1, 0); +// ASSERT(0 != Mapping); +// +// if (EarlyClose) { +// Success = CloseHandle(Handle); +// ASSERT(Success); +// } +// +// MappedView = MapViewOfFile(Mapping, FILE_MAP_ALL_ACCESS, 0, 0, 0); +// ASSERT(0 != MappedView); +// +// if (!EarlyClose) { +// pointer = SetFilePointer(handle1, FileSize0 / 2, 0, FILE_BEGIN); +// ASSERT(FileSize0 / 2 == pointer); +// memset(Buffer[1], 0, bytes_per_sector); +// Success = ReadFile(handle1, Buffer[1], bytes_per_sector, &BytesTransferred, +// 0); ASSERT(Success); ASSERT(bytes_per_sector == BytesTransferred); +// ASSERT(pointer + BytesTransferred == +// SetFilePointer(handle1, 0, 0, FILE_CURRENT)); +// ASSERT(0 == memcmp(Buffer[0], Buffer[1], BytesTransferred)); +// } +// +// srand(seed); +// for (PUINT8 Bgn = MappedView + FileSize1 / 2, +// End = MappedView + FileSize0 / 2; +// End > Bgn; Bgn++) +// ASSERT(*Bgn == (rand() & 0xff)); +// if (!EarlyClose) +// ASSERT(0 == memcmp(Buffer[0], MappedView + FileSize0 / 2, +// bytes_per_sector)); +// for (size_t i = 0; bytes_per_sector > i; i++) +// rand(); +// for (PUINT8 Bgn = MappedView + FileSize0 / 2 + bytes_per_sector, +// End = MappedView + FileSize1 / 2 + FileSize0; +// End > Bgn; Bgn++) +// ASSERT(*Bgn == (rand() & 0xff)); +// +// Success = UnmapViewOfFile(MappedView); +// ASSERT(Success); +// +// Success = CloseHandle(Mapping); +// ASSERT(Success); +// +// if (!EarlyClose) { +// Success = CloseHandle(Handle); +// ASSERT(Success); +// } +// +// Success = DeleteFileW(FilePath); +// ASSERT(Success); +// } + TYPED_TEST(winfsp_test, rdrw_can_read_and_write_file_no_flags) { auto file_path{ utils::path::combine(this->mount_location, {"test_file_5"}), @@ -504,6 +804,34 @@ TYPED_TEST(winfsp_test, rdrw_can_read_and_write_file_overlapped_write_through) { test_overlapped_file(this->mount_location, file_path, FILE_FLAG_WRITE_THROUGH); } + +// TYPED_TEST(winfsp_test, rdrw_can_read_and_write_mmap_file_no_flags) { +// auto file_path{ +// utils::path::combine(this->mount_location, {"test_file_5"}), +// }; +// test_mmap_file(this->mount_location, file_path, 0U); +// } +// +// TYPED_TEST(winfsp_test, rdrw_can_read_and_write_mmap_file_no_buffering) { +// auto file_path{ +// utils::path::combine(this->mount_location, {"test_file_5"}), +// }; +// test_mmap_file(this->mount_location, file_path, FILE_FLAG_NO_BUFFERING); +// } +// +// TYPED_TEST(winfsp_test, rdrw_can_read_and_write_file_mmap_write_through) { +// auto file_path{ +// utils::path::combine(this->mount_location, {"test_file_5"}), +// }; +// test_mmap_file(this->mount_location, file_path, FILE_FLAG_WRITE_THROUGH); +// } + +TYPED_TEST(winfsp_test, rdrw_can_read_and_write_mixed_file) { + auto file_path{ + utils::path::combine(this->mount_location, {"test_file_5"}), + }; + test_mixed_file(this->mount_location, file_path); +} } // namespace repertory #endif // defined(_WIN32)