From e6918187568dbd01842d8d1d2c808ce16a894239 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 21 Apr 2024 13:54:28 +0200 Subject: Adding upstream version 18.2.2. Signed-off-by: Daniel Baumann --- src/test/dokan/CMakeLists.txt | 13 + src/test/dokan/dokan.cc | 771 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 784 insertions(+) create mode 100644 src/test/dokan/CMakeLists.txt create mode 100644 src/test/dokan/dokan.cc (limited to 'src/test/dokan') diff --git a/src/test/dokan/CMakeLists.txt b/src/test/dokan/CMakeLists.txt new file mode 100644 index 000000000..71029d386 --- /dev/null +++ b/src/test/dokan/CMakeLists.txt @@ -0,0 +1,13 @@ +if(WITH_DOKAN) + add_executable(ceph_test_dokan + dokan.cc + ) + target_link_libraries(ceph_test_dokan + ceph-common + ${UNITTEST_LIBS} + ${EXTRALIBS} + ${CMAKE_DL_LIBS} + ) + install(TARGETS ceph_test_dokan + DESTINATION ${CMAKE_INSTALL_BINDIR}) +endif(WITH_DOKAN) diff --git a/src/test/dokan/dokan.cc b/src/test/dokan/dokan.cc new file mode 100644 index 000000000..1a1d39580 --- /dev/null +++ b/src/test/dokan/dokan.cc @@ -0,0 +1,771 @@ +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2022 Cloudbase Solutions + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" + +#include "common/SubProcess.h" +#include "common/run_cmd.h" +#include "include/uuid.h" + +#define DEFAULT_MOUNTPOINT "X:\\" +#define MOUNT_POLL_ATTEMPT 10 +#define MOUNT_POLL_INTERVAL_MS 1000 +#define TEST_VOL_SERIAL "1234567890" +#define MByte 1048576 + +namespace fs = std::filesystem; +using namespace std::chrono_literals; + +std::string get_uuid() { + uuid_d suffix; + suffix.generate_random(); + + return suffix.to_string(); +} + +bool move_eof(HANDLE handle, LARGE_INTEGER offset) { + + // Move file pointer to FILE_BEGIN + offset + if (!SetFilePointerEx(handle, offset, NULL, FILE_BEGIN)) { + std::cerr << "Setting file pointer failed. err: " + << GetLastError() << std::endl; + return false; + } + + if (!SetEndOfFile(handle)) { + std::cerr << "Setting EOF failed. err: " << GetLastError() << std::endl; + return false; + } + + return true; +} + +void write_file(std::string file_path, std::string data) { + std::ofstream file; + file.open(file_path); + + ASSERT_TRUE(file.is_open()) + << "Failed to open file: " << file_path; + file << data; + file.flush(); + + file.close(); +} + +void expect_write_failure(std::string file_path) { + std::ofstream file; + file.open(file_path); + + ASSERT_FALSE(file.is_open()); +} + +std::string read_file(std::string file_path) { + std::ifstream file; + file.open(file_path); + std::string content((std::istreambuf_iterator(file)), + std::istreambuf_iterator()); + file.close(); + + return content; +} + +void check_write_file(std::string file_path, std::string data) { + write_file(file_path, data); + ASSERT_EQ(read_file(file_path), data); +} + +int wait_for_mount(std::string mount_path) { + std::cerr << "Waiting for mount: " << mount_path << std::endl; + + int attempts = 0; + do { + attempts++; + if (attempts < MOUNT_POLL_ATTEMPT) + Sleep(MOUNT_POLL_INTERVAL_MS); + } while (!fs::exists(mount_path) + && attempts < MOUNT_POLL_ATTEMPT); + + if (!fs::exists(mount_path)) { + std::cerr << "Timed out waiting for ceph-dokan mount: " + << mount_path << std::endl; + return -ETIMEDOUT; + } + + std::cerr << "Successfully mounted: " << mount_path << std::endl; + + return 0; +} + +void map_dokan(SubProcess** mount, const char* mountpoint) { + SubProcess* new_mount = new SubProcess("ceph-dokan"); + + new_mount->add_cmd_args("map", "--win-vol-name", "TestCeph", + "--win-vol-serial", TEST_VOL_SERIAL, + "-l", mountpoint, NULL); + + *mount = new_mount; + ASSERT_EQ(new_mount->spawn(), 0); + ASSERT_EQ(wait_for_mount(mountpoint), 0); +} + +void map_dokan_read_only( + SubProcess** mount, + const char* mountpoint +) { + SubProcess* new_mount = new SubProcess("ceph-dokan"); + new_mount->add_cmd_args("map", "--win-vol-name", "TestCeph", + "--win-vol-serial", TEST_VOL_SERIAL, + "--read-only", "-l", mountpoint, NULL); + + *mount = new_mount; + ASSERT_EQ(new_mount->spawn(), 0); + ASSERT_EQ(wait_for_mount(mountpoint), 0); + std::cerr << mountpoint << " mounted in read-only mode" + << std::endl; +} + +void map_dokan_with_maxpath( + SubProcess** mount, + const char* mountpoint, + uint64_t max_path_len) +{ + SubProcess* new_mount = new SubProcess("ceph-dokan"); + new_mount->add_cmd_args("map", "--debug", "--dokan-stderr", + "--win-vol-name", "TestCeph", + "--win-vol-serial", TEST_VOL_SERIAL, + "--max-path-len", + (std::to_string(max_path_len)).c_str(), + "-l", mountpoint, NULL); + + *mount = new_mount; + ASSERT_EQ(new_mount->spawn(), 0); + if (256 <= max_path_len && max_path_len <= 4096) { + ASSERT_EQ(wait_for_mount(mountpoint), 0); + } else { + ASSERT_NE(wait_for_mount(mountpoint), 0); + } +} + +void unmap_dokan(SubProcess* mount, const char* mountpoint) { + std::string ret = run_cmd("ceph-dokan", "unmap", "-l", + mountpoint, (char*)NULL); + + ASSERT_EQ(ret, "") << "Failed unmapping: " << mountpoint; + std::cerr<< "Unmounted: " << mountpoint << std::endl; + + ASSERT_EQ(mount->join(), 0); +} + +int get_volume_max_path(std::string mountpoint){ + char volume_name[MAX_PATH + 1] = { 0 }; + char file_system_name[MAX_PATH + 1] = { 0 }; + DWORD serial_number = 0; + DWORD max_component_len = 0; + DWORD file_system_flags = 0; + if (GetVolumeInformation( + mountpoint.c_str(), + volume_name, + sizeof(volume_name), + &serial_number, + &max_component_len, + &file_system_flags, + file_system_name, + sizeof(file_system_name)) != TRUE) { + std::cerr << "GetVolumeInformation() failed, error: " + << GetLastError() << std::endl; + } + + return max_component_len; +} + +static SubProcess* shared_mount = nullptr; + +class DokanTests : public testing::Test +{ +protected: + + static void SetUpTestSuite() { + map_dokan(&shared_mount, DEFAULT_MOUNTPOINT); + } + + static void TearDownTestSuite() { + if (shared_mount) { + unmap_dokan(shared_mount, DEFAULT_MOUNTPOINT); + } + shared_mount = nullptr; + } +}; + +TEST_F(DokanTests, test_mount) { + std::string mountpoint = "Y:\\"; + SubProcess* mount = nullptr; + map_dokan(&mount, mountpoint.c_str()); + unmap_dokan(mount, mountpoint.c_str()); +} + +TEST_F(DokanTests, test_mount_read_only) { + std::string mountpoint = "Z:\\"; + std::string data = "abc123"; + std::string success_file_path = "ro_success_" + get_uuid(); + std::string failed_file_path = "ro_fail_" + get_uuid(); + + SubProcess* mount = nullptr; + map_dokan(&mount, mountpoint.c_str()); + + check_write_file(mountpoint + success_file_path, data); + ASSERT_TRUE(fs::exists(mountpoint + success_file_path)); + + unmap_dokan(mount, mountpoint.c_str()); + + mount = nullptr; + map_dokan_read_only(&mount, mountpoint.c_str()); + + expect_write_failure(mountpoint + failed_file_path); + ASSERT_FALSE(fs::exists(mountpoint + failed_file_path)); + + ASSERT_TRUE(fs::exists(mountpoint + success_file_path)); + ASSERT_EQ(read_file(mountpoint + success_file_path), data); + + std::string exception_msg( + "filesystem error: cannot remove: No such device [" + + mountpoint + success_file_path + "]"); + EXPECT_THROW({ + try { + fs::remove(mountpoint + success_file_path); + } catch(const fs::filesystem_error &e) { + EXPECT_STREQ(e.what(), exception_msg.c_str()); + throw; + } + }, fs::filesystem_error); + unmap_dokan(mount, mountpoint.c_str()); + + map_dokan(&mount, mountpoint.c_str()); + ASSERT_TRUE(fs::exists(mountpoint + success_file_path)); + ASSERT_TRUE(fs::remove(mountpoint + success_file_path)); + unmap_dokan(mount, mountpoint.c_str()); +} + +TEST_F(DokanTests, test_delete_on_close) { + std::string file_path = DEFAULT_MOUNTPOINT"file_" + get_uuid(); + HANDLE hFile = CreateFile( + file_path.c_str(), + GENERIC_WRITE, // open for writing + 0, // sharing mode, none in this case + 0, // use default security descriptor + CREATE_NEW, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, + 0); + + ASSERT_NE(hFile, INVALID_HANDLE_VALUE) + << "Could not open file: " + << DEFAULT_MOUNTPOINT"test_create.txt " + << "err: " << GetLastError() << std::endl; + + ASSERT_NE(CloseHandle(hFile), 0); + + // FILE_FLAG_DELETE_ON_CLOSE is used + ASSERT_FALSE(fs::exists(file_path)); +} + +TEST_F(DokanTests, test_io) { + std::string data = "abcdef"; + std::string file_path = "test_io_" + get_uuid(); + + std::string mountpoint = "I:\\"; + SubProcess* mount = nullptr; + map_dokan(&mount, mountpoint.c_str()); + + check_write_file(mountpoint + file_path, data); + ASSERT_TRUE(fs::exists(mountpoint + file_path)); + unmap_dokan(mount, mountpoint.c_str()); + + mountpoint = "O:\\"; + mount = nullptr; + map_dokan(&mount, mountpoint.c_str()); + + ASSERT_TRUE(fs::exists(mountpoint + file_path)); + EXPECT_EQ(data, read_file(mountpoint + file_path)); + ASSERT_TRUE(fs::remove((mountpoint + file_path).c_str())); + ASSERT_FALSE(fs::exists(mountpoint + file_path)); + unmap_dokan(mount, mountpoint.c_str()); +} + +TEST_F(DokanTests, test_subfolders) { + std::string base_dir_path = DEFAULT_MOUNTPOINT"base_dir_" + + get_uuid() + "\\"; + std::string sub_dir_path = base_dir_path + + "test_sub_dir" + get_uuid(); + std::string base_dir_file = base_dir_path + + "file_" + get_uuid(); + std::string sub_dir_file = sub_dir_path + + "file_" + get_uuid(); + + std::string data = "abc"; + + ASSERT_EQ(fs::create_directory(base_dir_path), true); + ASSERT_TRUE(fs::exists(base_dir_path)); + + ASSERT_EQ(fs::create_directory(sub_dir_path), true); + ASSERT_TRUE(fs::exists(sub_dir_path)); + + check_write_file(base_dir_file, data); + ASSERT_TRUE(fs::exists(base_dir_file)); + + check_write_file(sub_dir_file, data); + ASSERT_TRUE(fs::exists(sub_dir_file)); + + ASSERT_TRUE(fs::remove((sub_dir_file).c_str())) + << "Failed to remove file: " << sub_dir_file; + ASSERT_FALSE(fs::exists(sub_dir_file)); + + // Remove empty dir + ASSERT_TRUE(fs::remove((sub_dir_path).c_str())) + << "Failed to remove directory: " << sub_dir_path; + ASSERT_FALSE(fs::exists(sub_dir_file)); + + ASSERT_NE(fs::remove_all((base_dir_path).c_str()), 0) + << "Failed to remove directory: " << base_dir_path; + ASSERT_FALSE(fs::exists(sub_dir_file)); +} + +TEST_F(DokanTests, test_find_files) { + std::string basedir_path = "X:/find_" + get_uuid(); + std::string subdir_path = basedir_path + "/dir_" + get_uuid(); + std::string file1_path = basedir_path + "/file1_" + get_uuid(); + std::string file2_path = subdir_path + "/file2_" + get_uuid(); + + ASSERT_TRUE( + fs::create_directories(subdir_path) + ); + + std::ofstream{file1_path}; + std::ofstream{file2_path}; + + std::vector paths; + + for (const auto & entry : + fs::recursive_directory_iterator(basedir_path) + ) { + paths.push_back(entry.path().generic_string()); + } + + ASSERT_NE(std::find(begin(paths), end(paths), subdir_path), end(paths)); + ASSERT_NE(std::find(begin(paths), end(paths), file1_path), end(paths)); + ASSERT_NE(std::find(begin(paths), end(paths), file2_path), end(paths)); + + // clean-up + ASSERT_NE(fs::remove_all(basedir_path), 0); +} + +TEST_F(DokanTests, test_move_file) { + std::string dir1_path = DEFAULT_MOUNTPOINT + "test_mv_1_" + get_uuid() + "\\"; + std::string dir2_path = DEFAULT_MOUNTPOINT + "test_mv_2_" + get_uuid() + "\\"; + std::string file_name = "mv_file_" + get_uuid(); + std::string data = "abcd"; + + ASSERT_TRUE(fs::create_directory(dir1_path)); + ASSERT_TRUE(fs::create_directory(dir2_path)); + + check_write_file(dir1_path + file_name, data); + + fs::rename(dir1_path + file_name, dir2_path + file_name); + + ASSERT_TRUE(fs::exists(dir2_path + file_name)); + ASSERT_FALSE(fs::exists(dir1_path + file_name)); + + ASSERT_EQ(data, read_file(dir2_path + file_name)); + + // clean-up + ASSERT_NE(fs::remove_all(dir1_path),0); + ASSERT_NE(fs::remove_all(dir2_path),0); +} + +TEST_F(DokanTests, test_max_path) { + std::string mountpoint = "P:\\"; + std::string extended_mountpoint = "\\\\?\\" + mountpoint; + SubProcess* mount = nullptr; + char dir[200] = { 0 }; + char file[200] = { 0 }; + std::string data = "abcd1234"; + + memset(dir, 'd', sizeof(dir) - 1); + memset(file, 'f', sizeof(file) - 1); + + uint64_t max_path_len = 4096; + + map_dokan_with_maxpath(&mount, + mountpoint.c_str(), + max_path_len); + EXPECT_EQ(get_volume_max_path(extended_mountpoint), + max_path_len); + + std::string long_dir_path = extended_mountpoint; + + std::string dir_names[15]; + + for (int i = 0; i < 15; i++) { + std::string crt_dir = std::string(dir) + "_" + + get_uuid() + "\\"; + long_dir_path.append(crt_dir); + int stat = _mkdir(long_dir_path.c_str()); + ASSERT_EQ(stat, 0) << "Error creating directory " << i + << ": " << GetLastError() << std::endl; + dir_names[i] = crt_dir; + } + std::string file_path = long_dir_path + "\\" + std::string(file) + + "_" + get_uuid(); + + check_write_file(file_path, data); + + // clean-up + // fs::remove is unable to handle long Windows paths + EXPECT_NE(DeleteFileA(file_path.c_str()), 0); + + for (int i = 14; i >= 0; i--) { + std::string remove_dir = extended_mountpoint; + for (int j = 0; j <= i; j++) { + remove_dir.append(dir_names[j]); + } + + EXPECT_NE(RemoveDirectoryA(remove_dir.c_str()), 0); + } + + unmap_dokan(mount, mountpoint.c_str()); + + // value exceeds 32767, so a failure is expected + max_path_len = 32770; + map_dokan_with_maxpath(&mount, + mountpoint.c_str(), + max_path_len); + ASSERT_FALSE(fs::exists(mountpoint)); + + // value is below 256, so a failure is expected + max_path_len = 150; + map_dokan_with_maxpath(&mount, + mountpoint.c_str(), + max_path_len); + ASSERT_FALSE(fs::exists(mountpoint)); + + // default value + map_dokan(&mount, mountpoint.c_str()); + EXPECT_EQ(get_volume_max_path(mountpoint.c_str()), 256); + + unmap_dokan(mount, mountpoint.c_str()); +} + +TEST_F(DokanTests, test_set_eof) { + std::string file_path = DEFAULT_MOUNTPOINT"test_eof_" + + get_uuid(); + HANDLE hFile = CreateFile( + file_path.c_str(), + GENERIC_WRITE, // open for writing + 0, // sharing mode, none in this case + 0, // use default security descriptor + CREATE_NEW, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, + 0); + + ASSERT_NE(hFile, INVALID_HANDLE_VALUE) + << "Could not open file: " + << DEFAULT_MOUNTPOINT"test_create.txt " + << "err: " << GetLastError() << std::endl; + + LARGE_INTEGER offset; + offset.QuadPart = 2 * MByte; // 2MB + + LARGE_INTEGER file_size; + + ASSERT_TRUE(move_eof(hFile, offset)); + ASSERT_NE(GetFileSizeEx(hFile, &file_size), 0); + EXPECT_EQ(file_size.QuadPart, offset.QuadPart); + + offset.QuadPart = MByte; // 1MB + + ASSERT_TRUE(move_eof(hFile, offset)); + ASSERT_NE(GetFileSizeEx(hFile, &file_size), 0); + EXPECT_EQ(file_size.QuadPart, offset.QuadPart); + + ASSERT_NE(CloseHandle(hFile), 0); + + // FILE_FLAG_DELETE_ON_CLOSE is used + ASSERT_FALSE(fs::exists(file_path)); +} + +TEST_F(DokanTests, test_set_alloc_size) { + std::string file_path = DEFAULT_MOUNTPOINT"test_alloc_size_" + + get_uuid(); + HANDLE hFile = CreateFile( + file_path.c_str(), + GENERIC_WRITE, // open for writing + 0, // sharing mode, none in this case + 0, // use default security descriptor + CREATE_NEW, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, + 0); + + ASSERT_NE(hFile, INVALID_HANDLE_VALUE) + << "Could not open file: " + << DEFAULT_MOUNTPOINT"test_create.txt " + << "err: " << GetLastError() << std::endl; + + LARGE_INTEGER li; + li.QuadPart = MByte; + FILE_ALLOCATION_INFO fai; + fai.AllocationSize = li; + + ASSERT_NE(SetFileInformationByHandle( + hFile, + FileAllocationInfo, + &fai, + sizeof(FILE_ALLOCATION_INFO) + ),0) << "Error: " << GetLastError(); + + LARGE_INTEGER offset; + offset.QuadPart = 2 * MByte; + LARGE_INTEGER file_size; + + ASSERT_TRUE(move_eof(hFile, offset)); + ASSERT_NE(GetFileSizeEx(hFile, &file_size), 0); + EXPECT_EQ(file_size.QuadPart, offset.QuadPart); + + offset.QuadPart = MByte; + + ASSERT_TRUE(move_eof(hFile, offset)); + ASSERT_NE(GetFileSizeEx(hFile, &file_size), 0); + EXPECT_EQ(file_size.QuadPart, offset.QuadPart); + + ASSERT_NE(CloseHandle(hFile), 0); + + // FILE_FLAG_DELETE_ON_CLOSE is used + ASSERT_FALSE(fs::exists(file_path)); +} + +TEST_F(DokanTests, test_file_type) { + std::string test_dir = DEFAULT_MOUNTPOINT"test_info_" + + get_uuid() + "\\"; + std::string file_path = test_dir + "file_" + + get_uuid(); + std::string dir_path = test_dir + "dir_" + + get_uuid() + "\\"; + + ASSERT_TRUE(fs::create_directory(test_dir)); + + std::ofstream{file_path}; + ASSERT_TRUE(fs::create_directory(dir_path)); + + ASSERT_TRUE(fs::is_regular_file(fs::status(file_path))); + ASSERT_TRUE(fs::is_directory(fs::status(dir_path))); + + // clean-up + fs::remove_all(test_dir); + +} + +TEST_F(DokanTests, test_volume_info) { + char volume_name[MAX_PATH + 1] = { 0 }; + char file_system_name[MAX_PATH + 1] = { 0 }; + DWORD serial_number = 0; + DWORD max_component_len = 0; + DWORD file_system_flags = 0; + + ASSERT_EQ( + GetVolumeInformation( + DEFAULT_MOUNTPOINT, + volume_name, + sizeof(volume_name), + &serial_number, + &max_component_len, + &file_system_flags, + file_system_name, + sizeof(file_system_name)),TRUE) + << "GetVolumeInformation() failed, error: " + << GetLastError() << std::endl; + + ASSERT_STREQ(volume_name, "TestCeph") + << "Received: " << volume_name << std::endl; + ASSERT_STREQ(file_system_name, "Ceph") + << "Received: " << file_system_name << std::endl; + ASSERT_EQ(max_component_len, 256); + ASSERT_EQ(serial_number, std::stoi(TEST_VOL_SERIAL)) + << "Received: " << serial_number << std::endl; + + // Consider adding specific flags + // and check for them + // ASSERT_EQ(file_system_flags, 271); +} + +TEST_F(DokanTests, test_get_free_space) { + std::error_code ec; + const std::filesystem::space_info si = + std::filesystem::space(DEFAULT_MOUNTPOINT, ec); + ASSERT_EQ(ec.value(), 0); + + ASSERT_NE(static_cast(si.capacity), 0); + ASSERT_NE(static_cast(si.free), 0); + ASSERT_NE(static_cast(si.available), 0); +} + +TEST_F(DokanTests, test_file_timestamp) { + std::string file1 = DEFAULT_MOUNTPOINT"test_time1_" + + get_uuid(); + std::string file2 = DEFAULT_MOUNTPOINT"test_time2_" + + get_uuid(); + std::string file3 = DEFAULT_MOUNTPOINT"test_time3_" + + get_uuid(); + + std::ofstream{file1}; + Sleep(1000); + std::ofstream{file2}; + Sleep(1000); + std::ofstream{file3}; + + int64_t file1_creation = fs::last_write_time(file1) + .time_since_epoch().count(); + int64_t file2_creation = fs::last_write_time(file2) + .time_since_epoch().count(); + int64_t file3_creation = fs::last_write_time(file3) + .time_since_epoch().count(); + + EXPECT_LT(file1_creation, file2_creation); + EXPECT_LT(file2_creation, file3_creation); + + // add 1h to file 1 creation time + fs::file_time_type file1_time = fs::last_write_time(file1); + + fs::last_write_time(file1, file1_time + 1h); + + int64_t file1_new_time = fs::last_write_time(file1) + .time_since_epoch().count(); + + EXPECT_EQ((file1_time + 1h).time_since_epoch().count(), + file1_new_time); + EXPECT_GT(file1_new_time, file2_creation); + EXPECT_GT(file1_new_time, file3_creation); + + ASSERT_TRUE(fs::remove(file1)); + ASSERT_TRUE(fs::remove(file2)); + ASSERT_TRUE(fs::remove(file3)); +} + +TEST_F(DokanTests, test_delete_disposition) { + std::string file_path = DEFAULT_MOUNTPOINT"test_disp_" + + get_uuid(); + HANDLE hFile = CreateFile(file_path.c_str(), + GENERIC_ALL, // required for delete + 0, // exclusive access + NULL, + CREATE_ALWAYS, + 0, + NULL); + + ASSERT_NE(hFile, INVALID_HANDLE_VALUE) + << "Could not open file: " << file_path + << "err: " << GetLastError() << std::endl; + + FILE_DISPOSITION_INFO fdi; + fdi.DeleteFile = TRUE; // marking for deletion + + ASSERT_NE( + SetFileInformationByHandle( + hFile, + FileDispositionInfo, + &fdi, + sizeof(FILE_DISPOSITION_INFO)), 0); + + ASSERT_NE(CloseHandle(hFile), 0); + ASSERT_FALSE(fs::exists(file_path)); +} + +bool check_create_disposition(std::string path, DWORD disposition) { + HANDLE hFile = CreateFile(path.c_str(), + GENERIC_WRITE, + 0, // exclusive access + NULL, + disposition, + 0, + NULL); + + if(hFile == INVALID_HANDLE_VALUE) { + return false; + } + + if(CloseHandle(hFile) == 0) { + return false; + } + + return true; +} + +TEST_F(DokanTests, test_create_dispositions) { + std::string file_path = DEFAULT_MOUNTPOINT"test_create_" + + get_uuid(); + std::string non_existant_file = DEFAULT_MOUNTPOINT + "test_create_" + get_uuid(); + + EXPECT_TRUE( + check_create_disposition(file_path, CREATE_NEW)); + + // CREATE_ALWAYS with existing file + EXPECT_TRUE( + check_create_disposition(file_path, CREATE_ALWAYS)); + EXPECT_EQ(GetLastError(), ERROR_ALREADY_EXISTS); + + // CREATE_NEW with existing file + EXPECT_FALSE( + check_create_disposition(file_path, CREATE_NEW)); + EXPECT_EQ(GetLastError(), ERROR_FILE_EXISTS); + + // OPEN_EXISTING with existing file + EXPECT_TRUE( + check_create_disposition(file_path, OPEN_EXISTING)); + + ASSERT_FALSE(fs::exists(non_existant_file)); + // OPEN_EXISTING with non-existant file + EXPECT_FALSE( + check_create_disposition(non_existant_file, OPEN_EXISTING)); + EXPECT_EQ(GetLastError(), ERROR_FILE_NOT_FOUND); + + // OPEN_ALWAYS with existing file + EXPECT_TRUE( + check_create_disposition(file_path, OPEN_ALWAYS)); + EXPECT_EQ(GetLastError(), ERROR_ALREADY_EXISTS); + + ASSERT_FALSE(fs::exists(non_existant_file)); + // OPEN_ALWAYS with non-existant file + EXPECT_TRUE( + check_create_disposition(non_existant_file, OPEN_ALWAYS)); + EXPECT_EQ(GetLastError(), 0); + + ASSERT_TRUE(fs::remove(non_existant_file)); + + // TRUNCATE_EXISTING with existing file + EXPECT_TRUE( + check_create_disposition(file_path, TRUNCATE_EXISTING)); + + // TRUNCATE_EXISTING with non-existant file + EXPECT_FALSE( + check_create_disposition(non_existant_file, + TRUNCATE_EXISTING)); + EXPECT_EQ(GetLastError(), ERROR_FILE_NOT_FOUND); + + // clean-up + ASSERT_TRUE(fs::remove(file_path)); +} -- cgit v1.2.3