diff --git a/test/tvfs_test/main.cpp b/test/tvfs_test/main.cpp index 553a251..b0cc453 100644 --- a/test/tvfs_test/main.cpp +++ b/test/tvfs_test/main.cpp @@ -25,48 +25,44 @@ along with usvfs. If not, see . #include -#pragma warning (push, 3) -#include +#pragma warning(push, 3) #include +#include #include -#pragma warning (pop) - +#pragma warning(pop) #include -#include #include +#include -#include #include +#include #include -#include -#include #include #include -#include #include - - +#include +#include +#include namespace spd = spdlog; namespace ush = usvfs::shared; - -// name of a file to be created in the virtual fs. Shouldn't exist on disc but the directory must exist -static LPCSTR VIRTUAL_FILEA = "C:/np.exe"; +// name of a file to be created in the virtual fs. Shouldn't exist on disc but the +// directory must exist +static LPCSTR VIRTUAL_FILEA = "C:/np.exe"; static LPCWSTR VIRTUAL_FILEW = L"C:/np.exe"; // a real file on disc that has to exist -static LPCSTR REAL_FILEA = "C:/windows/notepad.exe"; +static LPCSTR REAL_FILEA = "C:/windows/notepad.exe"; static LPCWSTR REAL_FILEW = L"C:/windows/notepad.exe"; -static LPCSTR REAL_DIRA = "C:/windows/Logs"; +static LPCSTR REAL_DIRA = "C:/windows/Logs"; static LPCWSTR REAL_DIRW = L"C:/windows/Logs"; - static std::shared_ptr logger() { std::shared_ptr result = spdlog::get("test"); @@ -76,9 +72,10 @@ static std::shared_ptr logger() return result; } -auto defaultUsvfsParams(const char* instanceName = "usvfs_test") { - std::unique_ptr parameters{ - usvfsCreateParameters(), &usvfsFreeParameters }; +auto defaultUsvfsParams(const char* instanceName = "usvfs_test") +{ + std::unique_ptr parameters{ + usvfsCreateParameters(), &usvfsFreeParameters}; usvfsSetInstanceName(parameters.get(), instanceName); usvfsSetDebugMode(parameters.get(), true); @@ -92,13 +89,15 @@ auto defaultUsvfsParams(const char* instanceName = "usvfs_test") { class USVFSTest : public testing::Test { public: - void SetUp() { + void SetUp() + { SHMLogger::create("usvfs"); // need to initialize logging in the context of the dll usvfsInitLogging(); } - void TearDown() { + void TearDown() + { std::array buffer; while (SHMLogger::instance().tryGet(buffer.data(), buffer.size())) { std::cout << buffer.data() << std::endl; @@ -112,19 +111,22 @@ class USVFSTest : public testing::Test class USVFSTestWithReroute : public testing::Test { public: - void SetUp() { + void SetUp() + { SHMLogger::create("usvfs"); // need to initialize logging in the context of the dll usvfsInitLogging(); auto params = defaultUsvfsParams(); m_Context.reset(usvfsCreateHookContext(*params, ::GetModuleHandle(nullptr))); - usvfs::RedirectionTreeContainer &tree = m_Context->redirectionTable(); - tree.addFile(ush::string_cast(VIRTUAL_FILEW, ush::CodePage::UTF8).c_str() - , usvfs::RedirectionDataLocal(REAL_FILEA)); + usvfs::RedirectionTreeContainer& tree = m_Context->redirectionTable(); + tree.addFile( + ush::string_cast(VIRTUAL_FILEW, ush::CodePage::UTF8).c_str(), + usvfs::RedirectionDataLocal(REAL_FILEA)); } - void TearDown() { + void TearDown() + { std::array buffer; while (SHMLogger::instance().tryGet(buffer.data(), buffer.size())) { std::cout << buffer.data() << std::endl; @@ -132,6 +134,7 @@ class USVFSTestWithReroute : public testing::Test m_Context.reset(); SHMLogger::free(); } + private: std::unique_ptr m_Context; }; @@ -139,13 +142,15 @@ class USVFSTestWithReroute : public testing::Test class USVFSTestAuto : public testing::Test { public: - void SetUp() { + void SetUp() + { auto params = defaultUsvfsParams(); usvfsConnectVFS(params.get()); SHMLogger::create("usvfs"); } - void TearDown() { + void TearDown() + { usvfsDisconnectVFS(); std::array buffer; @@ -158,21 +163,28 @@ class USVFSTestAuto : public testing::Test private: }; - TEST_F(USVFSTest, CanResizeRedirectiontree) { using usvfs::shared::MissingThrow; ASSERT_NO_THROW({ - usvfs::RedirectionTreeContainer container("treetest_shm", 1024); - for (char i = 'a'; i <= 'z'; ++i) { - for (char j = 'a'; j <= 'z'; ++j) { - std::string name = std::string(R"(C:\temp\)") + i + j; - container.addFile(name, usvfs::RedirectionDataLocal("gaga"), false); - } + usvfs::RedirectionTreeContainer container("treetest_shm", 1024); + for (char i = 'a'; i <= 'z'; ++i) { + for (char j = 'a'; j <= 'z'; ++j) { + std::string name = std::string(R"(C:\temp\)") + i + j; + container.addFile(name, usvfs::RedirectionDataLocal("gaga"), false); } + } - ASSERT_EQ("gaga", container->node("C:")->node("temp")->node("aa", MissingThrow)->data().linkTarget); - ASSERT_EQ("gaga", container->node("C:")->node("temp")->node("az", MissingThrow)->data().linkTarget); + ASSERT_EQ("gaga", container->node("C:") + ->node("temp") + ->node("aa", MissingThrow) + ->data() + .linkTarget); + ASSERT_EQ("gaga", container->node("C:") + ->node("temp") + ->node("az", MissingThrow) + ->data() + .linkTarget); }); } @@ -181,15 +193,11 @@ TEST_F(USVFSTest, CreateFileHookReportsCorrectErrorOnMissingFile) { ASSERT_NO_THROW({ USVFSParameters params; - USVFSInitParameters(¶ms, "usvfs_test", true, LogLevel::Debug, CrashDumpsType::None, ""); - std::unique_ptr ctx(CreateHookContext(params, ::GetModuleHandle(nullptr))); - HANDLE res = usvfs::hook_CreateFileW(VIRTUAL_FILEW - , GENERIC_READ - , FILE_SHARE_READ | FILE_SHARE_WRITE - , nullptr - , OPEN_EXISTING - , FILE_ATTRIBUTE_NORMAL - , nullptr); + USVFSInitParameters(¶ms, "usvfs_test", true, LogLevel::Debug, +CrashDumpsType::None, ""); std::unique_ptr +ctx(CreateHookContext(params, ::GetModuleHandle(nullptr))); HANDLE res = +usvfs::hook_CreateFileW(VIRTUAL_FILEW , GENERIC_READ , FILE_SHARE_READ | +FILE_SHARE_WRITE , nullptr , OPEN_EXISTING , FILE_ATTRIBUTE_NORMAL , nullptr); ASSERT_EQ(INVALID_HANDLE_VALUE, res); ASSERT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError()); @@ -211,20 +219,20 @@ TEST_F(USVFSTestWithReroute, CreateFileHookRedirectsFile) } */ - TEST_F(USVFSTest, GetFileAttributesHookReportsCorrectErrorOnMissingFile) { ASSERT_NO_THROW({ try { - auto params = defaultUsvfsParams(); - std::unique_ptr ctx(usvfsCreateHookContext(*params, ::GetModuleHandle(nullptr))); - DWORD res = usvfs::hook_GetFileAttributesW(VIRTUAL_FILEW); + auto params = defaultUsvfsParams(); + std::unique_ptr ctx( + usvfsCreateHookContext(*params, ::GetModuleHandle(nullptr))); + DWORD res = usvfs::hook_GetFileAttributesW(VIRTUAL_FILEW); - ASSERT_EQ(INVALID_FILE_ATTRIBUTES, res); - ASSERT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError()); + ASSERT_EQ(INVALID_FILE_ATTRIBUTES, res); + ASSERT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError()); } catch (const std::exception& e) { - logger()->error("Exception: {}", e.what()); - throw; + logger()->error("Exception: {}", e.what()); + throw; } }); } @@ -232,21 +240,24 @@ TEST_F(USVFSTest, GetFileAttributesHookReportsCorrectErrorOnMissingFile) TEST_F(USVFSTest, GetFileAttributesHookRedirectsFile) { auto params = defaultUsvfsParams(); - std::unique_ptr ctx(usvfsCreateHookContext(*params, ::GetModuleHandle(nullptr))); - usvfs::RedirectionTreeContainer &tree = ctx->redirectionTable(); + std::unique_ptr ctx( + usvfsCreateHookContext(*params, ::GetModuleHandle(nullptr))); + usvfs::RedirectionTreeContainer& tree = ctx->redirectionTable(); - tree.addFile(ush::string_cast(VIRTUAL_FILEW, ush::CodePage::UTF8).c_str() - , usvfs::RedirectionDataLocal(REAL_FILEA)); + tree.addFile( + ush::string_cast(VIRTUAL_FILEW, ush::CodePage::UTF8).c_str(), + usvfs::RedirectionDataLocal(REAL_FILEA)); - ASSERT_EQ(::GetFileAttributesW(REAL_FILEW) - , usvfs::hook_GetFileAttributesW(VIRTUAL_FILEW)); + ASSERT_EQ(::GetFileAttributesW(REAL_FILEW), + usvfs::hook_GetFileAttributesW(VIRTUAL_FILEW)); } /* TEST_F(USVFSTest, GetFullPathNameOnRegularCurrentDirectory) { USVFSParameters params; - USVFSInitParameters(¶ms, "usvfs_test", true, LogLevel::Debug, CrashDumpsType::None, ""); - std::unique_ptr ctx(CreateHookContext(params, ::GetModuleHandle(nullptr))); + USVFSInitParameters(¶ms, "usvfs_test", true, LogLevel::Debug, +CrashDumpsType::None, ""); std::unique_ptr +ctx(CreateHookContext(params, ::GetModuleHandle(nullptr))); std::wstring expected = winapi::wide::getCurrentDirectory() + L"\\filename.txt"; @@ -254,7 +265,8 @@ TEST_F(USVFSTest, GetFullPathNameOnRegularCurrentDirectory) std::unique_ptr buffer(new wchar_t[bufferLength]); LPWSTR filePart = nullptr; - DWORD res = usvfs::hook_GetFullPathNameW(L"filename.txt", bufferLength, buffer.get(), &filePart); + DWORD res = usvfs::hook_GetFullPathNameW(L"filename.txt", bufferLength, buffer.get(), +&filePart); ASSERT_NE(0UL, res); ASSERT_EQ(expected, std::wstring(buffer.get())); @@ -263,37 +275,37 @@ TEST_F(USVFSTest, GetFullPathNameOnRegularCurrentDirectory) // small wrapper to call usvfs::hook_NtOpenFile with a path // // at some point in time, changes were made to USVFS such that calling a hooked -// function from a handle obtained from a non-hooked function would not work anymore, +// function from a handle obtained from a non-hooked function would not work anymore, // meaning that function such as CreateFileW that have no hook equivalent cannot // be used to test hook functions -// +// // this function is useful to simulate a CreateFileW by internally using the hook // version of NtOpenFile // -HANDLE hooked_NtOpenFile(LPCWSTR path, ACCESS_MASK accessMask, ULONG shareAccess, ULONG openOptions) +HANDLE hooked_NtOpenFile(LPCWSTR path, ACCESS_MASK accessMask, ULONG shareAccess, + ULONG openOptions) { constexpr size_t BUFFER_SIZE = 2048; IO_STATUS_BLOCK statusBlock; OBJECT_ATTRIBUTES attributes; - attributes.SecurityDescriptor = 0; + attributes.SecurityDescriptor = 0; attributes.SecurityQualityOfService = 0; - attributes.RootDirectory = 0; - attributes.Attributes = 0; - attributes.Length = sizeof(OBJECT_ATTRIBUTES); + attributes.RootDirectory = 0; + attributes.Attributes = 0; + attributes.Length = sizeof(OBJECT_ATTRIBUTES); WCHAR stringBuffer[BUFFER_SIZE]; - UNICODE_STRING string; + UNICODE_STRING string; string.Buffer = stringBuffer; lstrcpyW(stringBuffer, L"\\??\\"); lstrcatW(stringBuffer, path); - string.Length = lstrlenW(stringBuffer) * 2; - string.MaximumLength = BUFFER_SIZE; + string.Length = lstrlenW(stringBuffer) * 2; + string.MaximumLength = BUFFER_SIZE; attributes.ObjectName = &string; - + HANDLE ret = INVALID_HANDLE_VALUE; - if (usvfs::hook_NtOpenFile(&ret, accessMask, &attributes, - &statusBlock, shareAccess, openOptions) != STATUS_SUCCESS) - { + if (usvfs::hook_NtOpenFile(&ret, accessMask, &attributes, &statusBlock, shareAccess, + openOptions) != STATUS_SUCCESS) { return INVALID_HANDLE_VALUE; } @@ -303,29 +315,19 @@ HANDLE hooked_NtOpenFile(LPCWSTR path, ACCESS_MASK accessMask, ULONG shareAccess TEST_F(USVFSTest, NtQueryDirectoryFileRegularFile) { auto params = defaultUsvfsParams(); - std::unique_ptr ctx(usvfsCreateHookContext(*params, ::GetModuleHandle(nullptr))); + std::unique_ptr ctx( + usvfsCreateHookContext(*params, ::GetModuleHandle(nullptr))); - HANDLE hdl = hooked_NtOpenFile( - L"C:\\" - , FILE_GENERIC_READ - , FILE_SHARE_READ | FILE_SHARE_WRITE - , FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT); + HANDLE hdl = + hooked_NtOpenFile(L"C:\\", FILE_GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT); ASSERT_NE(INVALID_HANDLE_VALUE, hdl); IO_STATUS_BLOCK status; char buffer[1024]; - usvfs::hook_NtQueryDirectoryFile(hdl - , nullptr - , nullptr - , nullptr - , &status - , buffer - , 1024 - , FileDirectoryInformation - , TRUE - , nullptr - , TRUE); + usvfs::hook_NtQueryDirectoryFile(hdl, nullptr, nullptr, nullptr, &status, buffer, + 1024, FileDirectoryInformation, TRUE, nullptr, TRUE); ASSERT_EQ(STATUS_SUCCESS, status.Status); @@ -335,16 +337,15 @@ TEST_F(USVFSTest, NtQueryDirectoryFileRegularFile) TEST_F(USVFSTest, NtQueryDirectoryFileFindsVirtualFile) { auto params = defaultUsvfsParams(); - std::unique_ptr ctx(usvfsCreateHookContext(*params, ::GetModuleHandle(nullptr))); - usvfs::RedirectionTreeContainer &tree = ctx->redirectionTable(); + std::unique_ptr ctx( + usvfsCreateHookContext(*params, ::GetModuleHandle(nullptr))); + usvfs::RedirectionTreeContainer& tree = ctx->redirectionTable(); tree.addFile(L"C:\\np.exe", usvfs::RedirectionDataLocal(REAL_FILEA)); - HANDLE hdl = hooked_NtOpenFile( - L"C:\\" - , FILE_GENERIC_READ - , FILE_SHARE_READ | FILE_SHARE_WRITE - , FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT); + HANDLE hdl = + hooked_NtOpenFile(L"C:\\", FILE_GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT); ASSERT_NE(INVALID_HANDLE_VALUE, hdl); IO_STATUS_BLOCK status; @@ -352,19 +353,12 @@ TEST_F(USVFSTest, NtQueryDirectoryFileFindsVirtualFile) usvfs::UnicodeString fileName(L"np.exe"); - usvfs::hook_NtQueryDirectoryFile(hdl - , nullptr - , nullptr - , nullptr - , &status - , buffer - , 1024 - , FileDirectoryInformation - , TRUE - , static_cast(fileName) - , TRUE); - - FILE_DIRECTORY_INFORMATION *info = reinterpret_cast(buffer); + usvfs::hook_NtQueryDirectoryFile(hdl, nullptr, nullptr, nullptr, &status, buffer, + 1024, FileDirectoryInformation, TRUE, + static_cast(fileName), TRUE); + + FILE_DIRECTORY_INFORMATION* info = + reinterpret_cast(buffer); ASSERT_EQ(STATUS_SUCCESS, status.Status); ASSERT_EQ(0, wcscmp(info->FileName, L"np.exe")); @@ -373,6 +367,27 @@ TEST_F(USVFSTest, NtQueryDirectoryFileFindsVirtualFile) TEST_F(USVFSTest, NtQueryObjectVirtualFile) { + std::wstring c_drive_device; + { + // find the device path for C: + HANDLE hdl = ::CreateFileW( + L"C:\\", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, nullptr); + ASSERT_NE(INVALID_HANDLE_VALUE, hdl); + + char buffer[2048]; + ASSERT_EQ(STATUS_SUCCESS, ::NtQueryObject(hdl, ObjectNameInformation, buffer, + sizeof(buffer), nullptr)); + + OBJECT_NAME_INFORMATION* information = + reinterpret_cast(buffer); + + c_drive_device = + std::wstring(information->Name.Buffer, information->Name.Length / 2); + + ::CloseHandle(hdl); + } + auto params = defaultUsvfsParams(); std::unique_ptr ctx( usvfsCreateHookContext(*params, ::GetModuleHandle(nullptr))); @@ -380,10 +395,9 @@ TEST_F(USVFSTest, NtQueryObjectVirtualFile) tree.addFile(L"C:\\np.exe", usvfs::RedirectionDataLocal(REAL_FILEA)); - HANDLE hdl = hooked_NtOpenFile(L"C:\\np.exe" - , FILE_GENERIC_READ - , FILE_SHARE_READ | FILE_SHARE_WRITE - , FILE_NON_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT); + HANDLE hdl = hooked_NtOpenFile(L"C:\\np.exe", FILE_GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_NON_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT); ASSERT_NE(INVALID_HANDLE_VALUE, hdl) << "last error=" << ::GetLastError(); { @@ -416,23 +430,26 @@ TEST_F(USVFSTest, NtQueryObjectVirtualFile) sizeof(buffer), nullptr); ASSERT_EQ(STATUS_SUCCESS, res); - OBJECT_NAME_INFORMATION *information = reinterpret_cast(buffer); - ASSERT_EQ(L"\\Device\\HarddiskVolume3\\np.exe", std::wstring(information->Name.Buffer, information->Name.Length / sizeof(wchar_t))); + OBJECT_NAME_INFORMATION* information = + reinterpret_cast(buffer); + ASSERT_EQ(c_drive_device + L"np.exe", + std::wstring(information->Name.Buffer, + information->Name.Length / sizeof(wchar_t))); } usvfs::hook_NtClose(hdl); } - TEST_F(USVFSTestAuto, CannotCreateLinkToFileInNonexistantDirectory) { - ASSERT_EQ(FALSE, usvfsVirtualLinkFile(REAL_FILEW, L"c:/this_directory_shouldnt_exist/np.exe", FALSE)); + ASSERT_EQ(FALSE, usvfsVirtualLinkFile( + REAL_FILEW, L"c:/this_directory_shouldnt_exist/np.exe", FALSE)); } TEST_F(USVFSTestAuto, CanCreateMultipleLinks) { - static LPCWSTR outFile = LR"(C:\np.exe)"; - static LPCWSTR outDir = LR"(C:\logs)"; + static LPCWSTR outFile = LR"(C:\np.exe)"; + static LPCWSTR outDir = LR"(C:\logs)"; static LPCWSTR outDirCanonizeTest = LR"(C:\.\not/../logs\.\a\.\b\.\c\..\.\..\.\..\)"; ASSERT_EQ(TRUE, usvfsVirtualLinkFile(REAL_FILEW, outFile, 0)); ASSERT_EQ(TRUE, usvfsVirtualLinkDirectoryStatic(REAL_DIRW, outDir, 0)); @@ -441,22 +458,25 @@ TEST_F(USVFSTestAuto, CanCreateMultipleLinks) ASSERT_NE(INVALID_FILE_ATTRIBUTES, usvfs::hook_GetFileAttributesW(outFile)); ASSERT_NE(INVALID_FILE_ATTRIBUTES, usvfs::hook_GetFileAttributesW(outDir)); ASSERT_EQ(0UL, usvfs::hook_GetFileAttributesW(outFile) & FILE_ATTRIBUTE_DIRECTORY); - ASSERT_NE(0UL, usvfs::hook_GetFileAttributesW(outDir) & FILE_ATTRIBUTE_DIRECTORY); - ASSERT_NE(0UL, usvfs::hook_GetFileAttributesW(outDirCanonizeTest) & FILE_ATTRIBUTE_DIRECTORY); + ASSERT_NE(0UL, usvfs::hook_GetFileAttributesW(outDir) & FILE_ATTRIBUTE_DIRECTORY); + ASSERT_NE(0UL, usvfs::hook_GetFileAttributesW(outDirCanonizeTest) & + FILE_ATTRIBUTE_DIRECTORY); } -int main(int argc, char **argv) { +int main(int argc, char** argv) +{ using namespace test; auto dllPath = path_of_usvfs_lib(platform_dependant_executable("usvfs", "dll")); ScopedLoadLibrary loadDll(dllPath.c_str()); if (!loadDll) { - std::wcerr << L"failed to load usvfs dll: " << dllPath.c_str() << L", " << GetLastError() << std::endl; + std::wcerr << L"failed to load usvfs dll: " << dllPath.c_str() << L", " + << GetLastError() << std::endl; return 1; } - // note: this makes the logger available only to functions statically linked to the test binary, not those - // called in the dll + // note: this makes the logger available only to functions statically linked to the + // test binary, not those called in the dll auto logger = spdlog::stdout_logger_mt("usvfs"); logger->set_level(spdlog::level::warn); testing::InitGoogleTest(&argc, argv);