diff options
Diffstat (limited to 'libs/usvfs/src/usvfs_dll/hooks')
| -rw-r--r-- | libs/usvfs/src/usvfs_dll/hooks/file_information_utils.h | 263 | ||||
| -rw-r--r-- | libs/usvfs/src/usvfs_dll/hooks/kernel32.cpp | 1860 | ||||
| -rw-r--r-- | libs/usvfs/src/usvfs_dll/hooks/kernel32.h | 116 | ||||
| -rw-r--r-- | libs/usvfs/src/usvfs_dll/hooks/ntdll.cpp | 1520 | ||||
| -rw-r--r-- | libs/usvfs/src/usvfs_dll/hooks/ntdll.h | 57 | ||||
| -rw-r--r-- | libs/usvfs/src/usvfs_dll/hooks/sharedids.h | 9 |
6 files changed, 0 insertions, 3825 deletions
diff --git a/libs/usvfs/src/usvfs_dll/hooks/file_information_utils.h b/libs/usvfs/src/usvfs_dll/hooks/file_information_utils.h deleted file mode 100644 index dbd3597..0000000 --- a/libs/usvfs/src/usvfs_dll/hooks/file_information_utils.h +++ /dev/null @@ -1,263 +0,0 @@ -#pragma once - -/** - * This header defines function to deal with the various FileInformationClass - * enumeration. - */ - -#include <type_traits> - -#include <ntdll_declarations.h> - -#include <windows.h> - -namespace usvfs::details -{ - -#define DECLARE_HAS_FIELD(Field) \ - template <typename T> \ - constexpr auto HasFieldImpl##Field(int)->decltype(std::declval<T>().Field, void(), \ - std::true_type()) \ - { \ - return {}; \ - } \ - template <typename T> \ - constexpr auto HasFieldImpl##Field(...) \ - { \ - return std::false_type{}; \ - } \ - template <typename T> \ - constexpr auto HasField##Field() \ - { \ - return HasFieldImpl##Field<T>(0); \ - } - -DECLARE_HAS_FIELD(FileName) -DECLARE_HAS_FIELD(NextEntryOffset) -DECLARE_HAS_FIELD(ShortName) - -#undef DECLARE_HAS_FIELD - -template <FILE_INFORMATION_CLASS fileInformationClass, class FileInformationClass> -struct FileInformationClassUtilsImpl -{ - static void get_data(LPCVOID address, ULONG& offset, std::wstring& fileName) - { - const FileInformationClass* info = - reinterpret_cast<const FileInformationClass*>(address); - - // default value - offset = sizeof(FileInformationClass); - - if constexpr (HasFieldFileName<FileInformationClass>()) { - if constexpr (HasFieldNextEntryOffset<FileInformationClass>()) { - offset = info->NextEntryOffset; - } - fileName = std::wstring(info->FileName, info->FileNameLength / sizeof(WCHAR)); - } - } - - static void set_offset(LPVOID address, ULONG offset) - { - FileInformationClass* info = reinterpret_cast<FileInformationClass*>(address); - - if constexpr (HasFieldNextEntryOffset<FileInformationClass>()) { - info->NextEntryOffset = offset; - } - } - - static void set_filename(LPVOID address, const std::wstring& fileName) - { - FileInformationClass* info = reinterpret_cast<FileInformationClass*>(address); - - if constexpr (HasFieldFileName<FileInformationClass>()) { - memset(info->FileName, L'\0', info->FileNameLength); - info->FileNameLength = static_cast<ULONG>(fileName.length() * sizeof(WCHAR)); - - // doesn't need to be 0-terminated - memcpy(info->FileName, fileName.c_str(), info->FileNameLength); - - if constexpr (HasFieldShortName<FileInformationClass>()) { - if (info->ShortNameLength > 0) { - info->ShortNameLength = static_cast<CCHAR>( - GetShortPathNameW(fileName.c_str(), info->ShortName, 8)); - } - } - } - } -}; - -template <FILE_INFORMATION_CLASS fileInformationClass> -struct FileInformationClassUtils; - -template <> -struct FileInformationClassUtils<FileDirectoryInformation> - : FileInformationClassUtilsImpl<FileDirectoryInformation, - FILE_DIRECTORY_INFORMATION> -{}; -template <> -struct FileInformationClassUtils<FileFullDirectoryInformation> - : FileInformationClassUtilsImpl<FileFullDirectoryInformation, - FILE_FULL_DIR_INFORMATION> -{}; -template <> -struct FileInformationClassUtils<FileBothDirectoryInformation> - : FileInformationClassUtilsImpl<FileBothDirectoryInformation, - FILE_BOTH_DIR_INFORMATION> -{}; -template <> -struct FileInformationClassUtils<FileStandardInformation> - : FileInformationClassUtilsImpl<FileStandardInformation, FILE_STANDARD_INFORMATION> -{}; -template <> -struct FileInformationClassUtils<FileNameInformation> - : FileInformationClassUtilsImpl<FileNameInformation, FILE_NAME_INFORMATION> -{}; -template <> -struct FileInformationClassUtils<FileRenameInformation> - : FileInformationClassUtilsImpl<FileRenameInformation, FILE_RENAME_INFORMATION> -{}; -template <> -struct FileInformationClassUtils<FileNamesInformation> - : FileInformationClassUtilsImpl<FileNamesInformation, FILE_NAMES_INFORMATION> -{}; -template <> -struct FileInformationClassUtils<FileObjectIdInformation> - : FileInformationClassUtilsImpl<FileObjectIdInformation, FILE_OBJECTID_INFORMATION> -{}; -template <> -struct FileInformationClassUtils<FileReparsePointInformation> - : FileInformationClassUtilsImpl<FileReparsePointInformation, - FILE_REPARSE_POINT_INFORMATION> -{}; -template <> -struct FileInformationClassUtils<FileIdBothDirectoryInformation> - : FileInformationClassUtilsImpl<FileIdBothDirectoryInformation, - FILE_ID_BOTH_DIR_INFORMATION> -{}; -template <> -struct FileInformationClassUtils<FileIdFullDirectoryInformation> - : FileInformationClassUtilsImpl<FileIdFullDirectoryInformation, - FILE_ID_FULL_DIR_INFORMATION> -{}; -template <> -struct FileInformationClassUtils<FileNormalizedNameInformation> - : FileInformationClassUtilsImpl<FileNormalizedNameInformation, - FILE_NAME_INFORMATION> -{}; -template <> -struct FileInformationClassUtils<FileIdExtdDirectoryInformation> - : FileInformationClassUtilsImpl<FileIdExtdDirectoryInformation, - FILE_ID_EXTD_DIR_INFORMATION> -{}; -template <> -struct FileInformationClassUtils<FileIdExtdBothDirectoryInformation> - : FileInformationClassUtilsImpl<FileIdExtdBothDirectoryInformation, - FILE_ID_EXTD_BOTH_DIR_INFORMATION> -{}; -template <> -struct FileInformationClassUtils<FileId64ExtdDirectoryInformation> - : FileInformationClassUtilsImpl<FileId64ExtdDirectoryInformation, - FILE_ID_64_EXTD_DIR_INFORMATION> -{}; -template <> -struct FileInformationClassUtils<FileId64ExtdBothDirectoryInformation> - : FileInformationClassUtilsImpl<FileId64ExtdBothDirectoryInformation, - FILE_ID_64_EXTD_BOTH_DIR_INFORMATION> -{}; -template <> -struct FileInformationClassUtils<FileIdAllExtdDirectoryInformation> - : FileInformationClassUtilsImpl<FileIdAllExtdDirectoryInformation, - FILE_ID_ALL_EXTD_DIR_INFORMATION> -{}; -template <> -struct FileInformationClassUtils<FileIdAllExtdBothDirectoryInformation> - : FileInformationClassUtilsImpl<FileIdAllExtdBothDirectoryInformation, - FILE_ID_ALL_EXTD_BOTH_DIR_INFORMATION> -{}; - -// FILE_ALL_INFORMATION needs to be handled differently because it has a field that -// is itself a structure -template <> -struct FileInformationClassUtils<FileAllInformation> -{ - static void get_data(LPCVOID address, ULONG& offset, std::wstring& fileName) - { - FileInformationClassUtils<FileNameInformation>::get_data( - &reinterpret_cast<const FILE_ALL_INFORMATION*>(address)->NameInformation, - offset, fileName); - } - - static void set_offset(LPVOID address, ULONG offset) - { - // this is a no-op but it's consistent to do that everywhere - FileInformationClassUtils<FileNameInformation>::set_offset( - &reinterpret_cast<FILE_ALL_INFORMATION*>(address)->NameInformation, offset); - } - - static void set_filename(LPVOID address, const std::wstring& fileName) - { - FileInformationClassUtils<FileNameInformation>::set_filename( - &reinterpret_cast<FILE_ALL_INFORMATION*>(address)->NameInformation, fileName); - } -}; - -} // namespace usvfs::details - -#define _APP_FINFO_CASE(clazz, fn, ...) \ - case clazz: \ - return usvfs::details::FileInformationClassUtils<clazz>::fn(__VA_ARGS__); - -#define _APPLY_FILEINFO_FN(fn, ...) \ - _APP_FINFO_CASE(FileAllInformation, fn, __VA_ARGS__) \ - _APP_FINFO_CASE(FileDirectoryInformation, fn, __VA_ARGS__) \ - _APP_FINFO_CASE(FileFullDirectoryInformation, fn, __VA_ARGS__) \ - _APP_FINFO_CASE(FileBothDirectoryInformation, fn, __VA_ARGS__) \ - _APP_FINFO_CASE(FileStandardInformation, fn, __VA_ARGS__) \ - _APP_FINFO_CASE(FileNameInformation, fn, __VA_ARGS__) \ - _APP_FINFO_CASE(FileRenameInformation, fn, __VA_ARGS__) \ - _APP_FINFO_CASE(FileNamesInformation, fn, __VA_ARGS__) \ - _APP_FINFO_CASE(FileObjectIdInformation, fn, __VA_ARGS__) \ - _APP_FINFO_CASE(FileReparsePointInformation, fn, __VA_ARGS__) \ - _APP_FINFO_CASE(FileIdBothDirectoryInformation, fn, __VA_ARGS__) \ - _APP_FINFO_CASE(FileIdFullDirectoryInformation, fn, __VA_ARGS__) \ - _APP_FINFO_CASE(FileNormalizedNameInformation, fn, __VA_ARGS__) \ - _APP_FINFO_CASE(FileIdExtdDirectoryInformation, fn, __VA_ARGS__) \ - _APP_FINFO_CASE(FileIdExtdBothDirectoryInformation, fn, __VA_ARGS__) \ - _APP_FINFO_CASE(FileId64ExtdDirectoryInformation, fn, __VA_ARGS__) \ - _APP_FINFO_CASE(FileId64ExtdBothDirectoryInformation, fn, __VA_ARGS__) \ - _APP_FINFO_CASE(FileIdAllExtdDirectoryInformation, fn, __VA_ARGS__) \ - _APP_FINFO_CASE(FileIdAllExtdBothDirectoryInformation, fn, __VA_ARGS__) - -void GetFileInformationData(FILE_INFORMATION_CLASS fileInformationClass, - LPCVOID address, ULONG& offset, std::wstring& fileName) -{ - switch (fileInformationClass) { - _APPLY_FILEINFO_FN(get_data, address, offset, fileName); - default: - offset = ULONG_MAX; - } -} - -void SetFileInformationOffset(FILE_INFORMATION_CLASS fileInformationClass, - LPVOID address, ULONG offset) -{ - switch (fileInformationClass) { - _APPLY_FILEINFO_FN(set_offset, address, offset); - default: - break; - } -} - -void SetFileInformationFileName(FILE_INFORMATION_CLASS fileInformationClass, - LPVOID address, const std::wstring& fileName) -{ - switch (fileInformationClass) { - _APPLY_FILEINFO_FN(set_filename, address, fileName); - default: - break; - } -} - -#undef _APP_FINFO_CASE -#undef _APPLY_FILEINFO_FN diff --git a/libs/usvfs/src/usvfs_dll/hooks/kernel32.cpp b/libs/usvfs/src/usvfs_dll/hooks/kernel32.cpp deleted file mode 100644 index 6599eb0..0000000 --- a/libs/usvfs/src/usvfs_dll/hooks/kernel32.cpp +++ /dev/null @@ -1,1860 +0,0 @@ -#include "kernel32.h" -#include "sharedids.h" - -#include "../hookcallcontext.h" -#include "../hookcontext.h" -#include "../hookmanager.h" -#include "../maptracker.h" -#include <formatters.h> -#include <inject.h> -#include <loghelpers.h> -#include <stringcast.h> -#include <stringutils.h> -#include <usvfs.h> -#include <winapi.h> -#include <winbase.h> - -namespace ush = usvfs::shared; -using ush::CodePage; -using ush::string_cast; - -namespace usvfs -{ -MapTracker k32DeleteTracker; -MapTracker k32FakeDirTracker; -} // namespace usvfs - -class CurrentDirectoryTracker -{ -public: - using wstring = std::wstring; - - bool get(wstring& currentDir, const wchar_t* forRelativePath = nullptr) - { - int index = m_currentDrive; - if (forRelativePath && *forRelativePath && forRelativePath[1] == ':') - if (!getDriveIndex(forRelativePath, index)) - spdlog::get("usvfs")->warn( - "CurrentDirectoryTracker::get() invalid drive letter: {}, will use current " - "drive {}", - string_cast<std::string>(forRelativePath), - static_cast<char>('A' + index)); // prints '@' for m_currentDrive == -1 - if (index < 0) - return false; - - std::shared_lock<std::shared_mutex> lock(m_mutex); - if (m_perDrive[index].empty()) - return false; - else { - currentDir = m_perDrive[index]; - return true; - } - } - - bool set(const wstring& currentDir) - { - int index = -1; - bool good = !currentDir.empty() && getDriveIndex(currentDir.c_str(), index) && - currentDir[1] == ':'; - std::unique_lock<std::shared_mutex> lock(m_mutex); - m_currentDrive = good ? index : -1; - if (good) - m_perDrive[index] = currentDir; - return good; - } - -private: - static bool getDriveIndex(const wchar_t* path, int& index) - { - if (*path >= 'a' && *path <= 'z') - index = *path - 'a'; - else if (*path >= 'A' && *path <= 'Z') - index = *path - 'A'; - else - return false; - return true; - } - - mutable std::shared_mutex m_mutex; - wstring m_perDrive['z' - 'a' + 1]; - int m_currentDrive{-1}; -}; - -CurrentDirectoryTracker k32CurrentDirectoryTracker; - -// attempts to copy source to destination and return the error code -static inline DWORD copyFileDirect(LPCWSTR source, LPCWSTR destination, bool overwrite) -{ - usvfs::FunctionGroupLock lock(usvfs::MutExHookGroup::SHELL_FILEOP); - return CopyFileExW(source, destination, NULL, NULL, NULL, - overwrite ? 0 : COPY_FILE_FAIL_IF_EXISTS) - ? ERROR_SUCCESS - : GetLastError(); -} - -static inline WCHAR pathNameDriveLetter(LPCWSTR path) -{ - if (!path || !path[0]) - return 0; - if (path[1] == ':') - return path[0]; - // if path is not ?: or \* then we need to get absolute path: - std::wstring buf; - if (path[0] != '\\') { - buf = winapi::wide::getFullPathName(path).first; - path = buf.c_str(); - if (!path[0] || path[1] == ':') - return path[0]; - } - // check for \??\C: - if (path[1] && path[2] && path[3] && path[4] && path[0] == '\\' && path[3] == '\\' && - path[5] == ':') - return path[4]; - // give up - return 0; -} - -// returns false also in case we fail to determine the drive letter of the path -static inline bool pathsOnDifferentDrives(LPCWSTR path1, LPCWSTR path2) -{ - WCHAR drive1 = pathNameDriveLetter(path1); - WCHAR drive2 = pathNameDriveLetter(path2); - return drive1 && drive2 && towupper(drive1) != towupper(drive2); -} - -HMODULE WINAPI usvfs::hook_LoadLibraryExA(LPCSTR lpFileName, HANDLE hFile, - DWORD dwFlags) -{ - HMODULE res = nullptr; - - HOOK_START_GROUP(MutExHookGroup::LOAD_LIBRARY) - const std::wstring fileName = ush::string_cast<std::wstring>(lpFileName); - - PRE_REALCALL - res = LoadLibraryExW(fileName.c_str(), hFile, dwFlags); - POST_REALCALL - - HOOK_END - return res; -} - -HMODULE WINAPI usvfs::hook_LoadLibraryExW(LPCWSTR lpFileName, HANDLE hFile, - DWORD dwFlags) -{ - HMODULE res = nullptr; - - HOOK_START_GROUP(MutExHookGroup::LOAD_LIBRARY) - // Why is the usual if (!callContext.active()... check missing? - - RerouteW reroute = RerouteW::create(READ_CONTEXT(), callContext, lpFileName); - PRE_REALCALL - res = ::LoadLibraryExW(reroute.fileName(), hFile, dwFlags); - POST_REALCALL - - if (reroute.wasRerouted()) { - LOG_CALL() - .PARAM(lpFileName) - .PARAM(reroute.fileName()) - .PARAM(res) - .PARAM(callContext.lastError()); - } - - HOOK_END - - return res; -} - -/// determine name of the binary to run based on parameters for createprocess -std::wstring getBinaryName(LPCWSTR applicationName, LPCWSTR lpCommandLine) -{ - if (applicationName != nullptr) { - std::pair<std::wstring, std::wstring> fullPath = - winapi::wide::getFullPathName(applicationName); - return fullPath.second; - } else { - if (lpCommandLine[0] == '"') { - const wchar_t* endQuote = wcschr(lpCommandLine, '"'); - if (endQuote != nullptr) { - return std::wstring(lpCommandLine + 1, endQuote - 1); - } - } - - // according to the documentation, if the commandline is unquoted and has - // spaces, it will be interpreted in multiple ways, i.e. - // c:\program.exe files\sub dir\program name - // c:\program files\sub.exe dir\program name - // c:\program files\sub dir\program.exe name - // c:\program files\sub dir\program name.exe - LPCWSTR space = wcschr(lpCommandLine, L' '); - while (space != nullptr) { - std::wstring subString(lpCommandLine, space); - bool isDirectory = true; - if (winapi::ex::wide::fileExists(subString.c_str(), &isDirectory) && - !isDirectory) { - return subString; - } else { - space = wcschr(space + 1, L' '); - } - } - return std::wstring(lpCommandLine); - } -} - -BOOL(WINAPI* usvfs::CreateProcessInternalW)( - LPVOID token, LPCWSTR lpApplicationName, LPWSTR lpCommandLine, - LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, - BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, - LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, - LPPROCESS_INFORMATION lpProcessInformation, LPVOID newToken); - -BOOL WINAPI usvfs::hook_CreateProcessInternalW( - LPVOID token, LPCWSTR lpApplicationName, LPWSTR lpCommandLine, - LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, - BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, - LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, - LPPROCESS_INFORMATION lpProcessInformation, LPVOID newToken) -{ - BOOL res = FALSE; - - HOOK_START_GROUP(MutExHookGroup::CREATE_PROCESS) - if (!callContext.active()) { - res = CreateProcessInternalW( - token, lpApplicationName, lpCommandLine, lpProcessAttributes, - lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, - lpCurrentDirectory, lpStartupInfo, lpProcessInformation, newToken); - callContext.updateLastError(); - return res; - } - - // remember if the caller wanted the process to be suspended. If so, we - // don't resume when we're done - BOOL susp = dwCreationFlags & CREATE_SUSPENDED; - dwCreationFlags |= CREATE_SUSPENDED; - - RerouteW applicationReroute; - RerouteW cmdReroute; - LPWSTR cend = nullptr; - - std::wstring dllPath; - usvfsParameters callParameters; - - { // scope for context lock - auto context = READ_CONTEXT(); - - if (RerouteW::interestingPath(lpCommandLine)) { - // First "argument" in the commandline is the command, we need to identify it and - // reroute it: - if (*lpCommandLine == '"') { - // If the first argument is quoted we trust its is quoted correctly - for (cend = lpCommandLine; *cend && *cend != ' '; ++cend) - if (*cend == '"') { - int escaped = 0; - for (++cend; *cend && (*cend != '"' || escaped % 2 != 0); ++cend) - escaped = *cend == '\\' ? escaped + 1 : 0; - } - - if (*(cend - 1) == '"') - --cend; - auto old_cend = *cend; - *cend = 0; - cmdReroute = RerouteW::create(context, callContext, lpCommandLine + 1); - *cend = old_cend; - if (old_cend == '"') - ++cend; - } else { - // If the first argument we have no choice but to test all the options to quote - // the command as the real CreateProcess will do this: - cend = lpCommandLine; - while (true) { - while (*cend && *cend != ' ') - ++cend; - - auto old_cend = *cend; - *cend = 0; - cmdReroute = RerouteW::create(context, callContext, lpCommandLine); - *cend = old_cend; - if (cmdReroute.wasRerouted() || pathIsFile(cmdReroute.fileName())) - break; - - while (*cend == ' ') - ++cend; - - if (!*cend) { - // if we reached the end of the string we'll just use the whole commandline - // as is: - cend = nullptr; - break; - } - } - } - } - - applicationReroute = RerouteW::create(context, callContext, lpApplicationName); - - dllPath = context->dllPath(); - callParameters = context->callParameters(); - } - - std::wstring cmdline; - if (cend && cmdReroute.fileName()) { - auto fileName = cmdReroute.fileName(); - cmdline.reserve(wcslen(fileName) + wcslen(cend) + 2); - if (*fileName != '"') - cmdline += L"\""; - cmdline += fileName; - if (*fileName != '"') - cmdline += L"\""; - cmdline += cend; - } - - PRE_REALCALL - res = CreateProcessInternalW(token, applicationReroute.fileName(), - cmdline.empty() ? lpCommandLine : &cmdline[0], - lpProcessAttributes, lpThreadAttributes, bInheritHandles, - dwCreationFlags, lpEnvironment, lpCurrentDirectory, - lpStartupInfo, lpProcessInformation, newToken); - POST_REALCALL - - BOOL blacklisted = FALSE; - { // limit scope of context - auto context = READ_CONTEXT(); - blacklisted = context->executableBlacklisted(applicationReroute.fileName(), - cmdReroute.fileName()); - } - - if (res) { - if (!blacklisted) { - try { - injectProcess(dllPath, callParameters, *lpProcessInformation); - } catch (const std::exception& e) { - spdlog::get("hooks")->error("failed to inject into {0}: {1}", - lpApplicationName != nullptr - ? applicationReroute.fileName() - : static_cast<LPCWSTR>(lpCommandLine), - e.what()); - } - } - - // resume unless process is supposed to start suspended - if (!susp && (ResumeThread(lpProcessInformation->hThread) == (DWORD)-1)) { - spdlog::get("hooks")->error("failed to inject into spawned process"); - res = FALSE; - } - } - - LOG_CALL() - .PARAM(lpApplicationName) - .PARAM(applicationReroute.fileName()) - .PARAM(cmdReroute.fileName()) - .PARAM(res) - .PARAM(callContext.lastError()) - .PARAM(cmdline); - HOOK_END - - return res; -} - -BOOL WINAPI usvfs::hook_GetFileAttributesExA(LPCSTR lpFileName, - GET_FILEEX_INFO_LEVELS fInfoLevelId, - LPVOID lpFileInformation) -{ - BOOL res = FALSE; - - HOOK_START_GROUP(MutExHookGroup::FILE_ATTRIBUTES) - if (!callContext.active() || !RerouteW::interestingPath(lpFileName)) { - res = GetFileAttributesExA(lpFileName, fInfoLevelId, lpFileInformation); - callContext.updateLastError(); - return res; - } - HOOK_END - - HOOK_START - const std::wstring fileName = ush::string_cast<std::wstring>(lpFileName); - - PRE_REALCALL - res = GetFileAttributesExW(fileName.c_str(), fInfoLevelId, lpFileInformation); - POST_REALCALL - - HOOK_END - return res; -} - -BOOL WINAPI usvfs::hook_GetFileAttributesExW(LPCWSTR lpFileName, - GET_FILEEX_INFO_LEVELS fInfoLevelId, - LPVOID lpFileInformation) -{ - BOOL res = FALSE; - - HOOK_START_GROUP(MutExHookGroup::FILE_ATTRIBUTES) - if (!callContext.active() || !RerouteW::interestingPath(lpFileName)) { - res = GetFileAttributesExW(lpFileName, fInfoLevelId, lpFileInformation); - callContext.updateLastError(); - return res; - } - - fs::path canonicalFile = RerouteW::canonizePath(RerouteW::absolutePath(lpFileName)); - - RerouteW reroute = - RerouteW::create(READ_CONTEXT(), callContext, canonicalFile.c_str()); - - PRE_REALCALL - res = ::GetFileAttributesExW(reroute.fileName(), fInfoLevelId, lpFileInformation); - POST_REALCALL - - DWORD originalError = callContext.lastError(); - DWORD fixedError = originalError; - // In case the target does not exist the error value varies to differentiate if the - // parent folder exists (ERROR_FILE_NOT_FOUND) or not (ERROR_PATH_NOT_FOUND). - // If the original target's parent folder doesn't actually exist it may exist in the - // the virtualized sense, or if we rerouted the query the parent of the original path - // might exist while the parent of the rerouted path might not: - if (!res && fixedError == ERROR_PATH_NOT_FOUND) { - // first query original file parent (if we rerouted it): - fs::path originalParent = canonicalFile.parent_path(); - WIN32_FILE_ATTRIBUTE_DATA parentAttr; - if (reroute.wasRerouted() && - ::GetFileAttributesExW(originalParent.c_str(), GetFileExInfoStandard, - &parentAttr) && - (parentAttr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) - fixedError = ERROR_FILE_NOT_FOUND; - else { - // now query the rerouted path for parent (which can be different from the parent - // of the rerouted path) - RerouteW rerouteParent = - RerouteW::create(READ_CONTEXT(), callContext, originalParent.c_str()); - if (rerouteParent.wasRerouted() && - ::GetFileAttributesExW(rerouteParent.fileName(), GetFileExInfoStandard, - &parentAttr) && - (parentAttr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) - fixedError = ERROR_FILE_NOT_FOUND; - } - } - if (fixedError != originalError) - callContext.updateLastError(fixedError); - - if (reroute.wasRerouted() || fixedError != originalError) { - DWORD resAttrib; - if (res && fInfoLevelId == GetFileExInfoStandard && lpFileInformation) - resAttrib = reinterpret_cast<WIN32_FILE_ATTRIBUTE_DATA*>(lpFileInformation) - ->dwFileAttributes; - else - resAttrib = (DWORD)-1; - LOG_CALL() - .PARAM(lpFileName) - .PARAM(reroute.fileName()) - .PARAMHEX(fInfoLevelId) - .PARAMHEX(res) - .PARAMHEX(resAttrib) - .PARAM(originalError) - .PARAM(fixedError); - } - - HOOK_END - - return res; -} - -DWORD WINAPI usvfs::hook_GetFileAttributesA(LPCSTR lpFileName) -{ - BOOL res = FALSE; - - HOOK_START_GROUP(MutExHookGroup::FILE_ATTRIBUTES) - if (!callContext.active() || !RerouteW::interestingPath(lpFileName)) { - res = GetFileAttributesA(lpFileName); - callContext.updateLastError(); - return res; - } - HOOK_END - - HOOK_START - const std::wstring fileName = ush::string_cast<std::wstring>(lpFileName); - - PRE_REALCALL - res = GetFileAttributesW(fileName.c_str()); - POST_REALCALL - - HOOK_END - return res; -} - -DWORD WINAPI usvfs::hook_GetFileAttributesW(LPCWSTR lpFileName) -{ - DWORD res = 0UL; - - HOOK_START_GROUP(MutExHookGroup::FILE_ATTRIBUTES) - if (!callContext.active() || !RerouteW::interestingPath(lpFileName)) { - res = GetFileAttributesW(lpFileName); - callContext.updateLastError(); - return res; - } - - fs::path canonicalFile = RerouteW::canonizePath(RerouteW::absolutePath(lpFileName)); - - RerouteW reroute = - RerouteW::create(READ_CONTEXT(), callContext, canonicalFile.c_str()); - - if (reroute.wasRerouted()) - PRE_REALCALL - res = ::GetFileAttributesW(reroute.fileName()); - POST_REALCALL - - DWORD originalError = callContext.lastError(); - DWORD fixedError = originalError; - // In case the target does not exist the error value varies to differentiate if the - // parent folder exists (ERROR_FILE_NOT_FOUND) or not (ERROR_PATH_NOT_FOUND). - // If the original target's parent folder doesn't actually exist it may exist in the - // the virtualized sense, or if we rerouted the query the parent of the original path - // might exist while the parent of the rerouted path might not: - if (res == INVALID_FILE_ATTRIBUTES && fixedError == ERROR_PATH_NOT_FOUND) { - // first query original file parent (if we rerouted it): - fs::path originalParent = canonicalFile.parent_path(); - DWORD attr; - if (reroute.wasRerouted() && - (attr = ::GetFileAttributesW(originalParent.c_str())) != - INVALID_FILE_ATTRIBUTES && - (attr & FILE_ATTRIBUTE_DIRECTORY)) - fixedError = ERROR_FILE_NOT_FOUND; - else { - // now query the rerouted path for parent (which can be different from the parent - // of the rerouted path) - RerouteW rerouteParent = - RerouteW::create(READ_CONTEXT(), callContext, originalParent.c_str()); - if (rerouteParent.wasRerouted() && - (attr = ::GetFileAttributesW(rerouteParent.fileName())) != - INVALID_FILE_ATTRIBUTES && - (attr & FILE_ATTRIBUTE_DIRECTORY)) - fixedError = ERROR_FILE_NOT_FOUND; - } - } - if (fixedError != originalError) - callContext.updateLastError(fixedError); - - if (reroute.wasRerouted() || fixedError != originalError) { - LOG_CALL() - .PARAM(lpFileName) - .PARAM(reroute.fileName()) - .PARAMHEX(res) - .PARAM(originalError) - .PARAM(fixedError); - } - - HOOK_ENDP(lpFileName); - - return res; -} - -DWORD WINAPI usvfs::hook_SetFileAttributesW(LPCWSTR lpFileName, DWORD dwFileAttributes) -{ - DWORD res = 0UL; - - HOOK_START_GROUP(MutExHookGroup::FILE_ATTRIBUTES) - // Why is the usual if (!callContext.active()... check missing? - - RerouteW reroute = RerouteW::create(READ_CONTEXT(), callContext, lpFileName); - PRE_REALCALL - res = ::SetFileAttributesW(reroute.fileName(), dwFileAttributes); - POST_REALCALL - - if (reroute.wasRerouted()) { - LOG_CALL().PARAM(reroute.fileName()).PARAM(res).PARAM(callContext.lastError()); - } - - HOOK_END - - return res; -} - -BOOL WINAPI usvfs::hook_DeleteFileW(LPCWSTR lpFileName) -{ - BOOL res = FALSE; - - HOOK_START_GROUP(MutExHookGroup::DELETE_FILE) - // Why is the usual if (!callContext.active()... check missing? - - const std::wstring path = - RerouteW::canonizePath(RerouteW::absolutePath(lpFileName)).wstring(); - - RerouteW reroute = RerouteW::create(READ_CONTEXT(), callContext, path.c_str()); - - PRE_REALCALL - if (reroute.wasRerouted()) { - res = ::DeleteFileW(reroute.fileName()); - } else { - res = ::DeleteFileW(path.c_str()); - } - POST_REALCALL - - if (res) { - reroute.removeMapping(READ_CONTEXT()); - } - - if (reroute.wasRerouted()) - LOG_CALL() - .PARAM(lpFileName) - .PARAM(reroute.fileName()) - .PARAM(res) - .PARAM(callContext.lastError()); - - HOOK_END - - return res; -} - -BOOL rewriteChangedDrives(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName, - const usvfs::RerouteW& readReroute, - const usvfs::CreateRerouter& writeReroute) -{ - return ((readReroute.wasRerouted() || writeReroute.wasRerouted()) && - pathsOnDifferentDrives(readReroute.fileName(), writeReroute.fileName()) && - !pathsOnDifferentDrives(lpExistingFileName, lpNewFileName)); -} - -BOOL WINAPI usvfs::hook_MoveFileA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName) -{ - BOOL res = FALSE; - - HOOK_START_GROUP(MutExHookGroup::SHELL_FILEOP) - - if (!callContext.active()) { - res = MoveFileA(lpExistingFileName, lpNewFileName); - callContext.updateLastError(); - return res; - } - - HOOK_END - HOOK_START - - const auto& existingFileName = ush::string_cast<std::wstring>(lpExistingFileName); - const auto& newFileName = ush::string_cast<std::wstring>(lpNewFileName); - - PRE_REALCALL - res = MoveFileW(existingFileName.c_str(), newFileName.c_str()); - POST_REALCALL - - HOOK_END - - return res; -} - -BOOL WINAPI usvfs::hook_MoveFileW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName) -{ - BOOL res = FALSE; - - HOOK_START_GROUP(MutExHookGroup::SHELL_FILEOP) - if (!callContext.active()) { - res = MoveFileW(lpExistingFileName, lpNewFileName); - callContext.updateLastError(); - return res; - } - - RerouteW readReroute; - CreateRerouter writeReroute; - bool callOriginal = true; - DWORD newFlags = 0; - - { - auto context = READ_CONTEXT(); - readReroute = RerouteW::create(context, callContext, lpExistingFileName); - callOriginal = writeReroute.rerouteNew(context, callContext, lpNewFileName, false, - "hook_MoveFileW"); - } - - if (callOriginal) { - bool movedDrives = rewriteChangedDrives(lpExistingFileName, lpNewFileName, - readReroute, writeReroute); - if (movedDrives) - newFlags |= MOVEFILE_COPY_ALLOWED; - - bool isDirectory = pathIsDirectory(readReroute.fileName()); - - PRE_REALCALL - if (isDirectory && movedDrives) { - SHFILEOPSTRUCTW sf = {0}; - sf.wFunc = FO_MOVE; - sf.hwnd = 0; - sf.fFlags = FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR | FOF_NOERRORUI; - sf.pFrom = readReroute.fileName(); - sf.pTo = writeReroute.fileName(); - int shRes = ::SHFileOperationW(&sf); - switch (shRes) { - case 0x78: - callContext.updateLastError(ERROR_ACCESS_DENIED); - break; - case 0x7C: - callContext.updateLastError(ERROR_FILE_NOT_FOUND); - break; - case 0x7E: - case 0x80: - callContext.updateLastError(ERROR_FILE_EXISTS); - break; - default: - callContext.updateLastError(shRes); - } - res = shRes == 0; - } else if (newFlags) - res = ::MoveFileExW(readReroute.fileName(), writeReroute.fileName(), newFlags); - else - res = ::MoveFileW(readReroute.fileName(), writeReroute.fileName()); - POST_REALCALL - - if (res) - SetLastError(ERROR_SUCCESS); - - writeReroute.updateResult(callContext, res); - - if (res) { - readReroute.removeMapping( - READ_CONTEXT(), isDirectory); // Updating the rerouteCreate to check deleted - // file entries should make this okay - - if (writeReroute.newReroute()) { - if (isDirectory) - RerouteW::addDirectoryMapping(WRITE_CONTEXT(), fs::path(lpNewFileName), - fs::path(writeReroute.fileName())); - else - writeReroute.insertMapping(WRITE_CONTEXT()); - } - } - - if (readReroute.wasRerouted() || writeReroute.wasRerouted() || - writeReroute.changedError()) - LOG_CALL() - .PARAM(readReroute.fileName()) - .PARAM(writeReroute.fileName()) - .PARAMWRAP(newFlags) - .PARAM(res) - .PARAM(writeReroute.originalError()) - .PARAM(callContext.lastError()); - } - - HOOK_END - - return res; -} - -BOOL WINAPI usvfs::hook_MoveFileExA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName, - DWORD dwFlags) -{ - BOOL res = FALSE; - - HOOK_START_GROUP(MutExHookGroup::SHELL_FILEOP) - - if (!callContext.active()) { - res = MoveFileExA(lpExistingFileName, lpNewFileName, dwFlags); - callContext.updateLastError(); - return res; - } - - HOOK_END - HOOK_START - - const std::wstring existingFileName = - ush::string_cast<std::wstring>(lpExistingFileName); - - // careful: lpNewFileName can be null if dwFlags is - // MOVEFILE_DELAY_UNTIL_REBOOT, so don't blindly cast the string and make sure - // the null pointer is forwarded correctly - std::wstring newFileNameWstring; - const wchar_t* newFileName = nullptr; - - if (lpNewFileName) { - newFileNameWstring = ush::string_cast<std::wstring>(lpNewFileName); - newFileName = newFileNameWstring.c_str(); - } - - PRE_REALCALL - res = MoveFileExW(existingFileName.c_str(), newFileName, dwFlags); - POST_REALCALL - - HOOK_END - - return res; -} - -BOOL WINAPI usvfs::hook_MoveFileExW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName, - DWORD dwFlags) -{ - BOOL res = FALSE; - - HOOK_START_GROUP(MutExHookGroup::SHELL_FILEOP) - if (!callContext.active()) { - res = MoveFileExW(lpExistingFileName, lpNewFileName, dwFlags); - callContext.updateLastError(); - return res; - } - - RerouteW readReroute; - CreateRerouter writeReroute; - bool callOriginal = true; - DWORD newFlags = dwFlags; - - { - auto context = READ_CONTEXT(); - readReroute = RerouteW::create(context, callContext, lpExistingFileName); - callOriginal = writeReroute.rerouteNew(context, callContext, lpNewFileName, - newFlags & MOVEFILE_REPLACE_EXISTING, - "hook_MoveFileExW"); - } - - if (callOriginal) { - bool movedDrives = rewriteChangedDrives(lpExistingFileName, lpNewFileName, - readReroute, writeReroute); - - bool isDirectory = pathIsDirectory(readReroute.fileName()); - - PRE_REALCALL - if (isDirectory && movedDrives) { - SHFILEOPSTRUCTW sf = {0}; - sf.wFunc = FO_MOVE; - sf.hwnd = 0; - sf.fFlags = FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR | FOF_NOERRORUI; - sf.pFrom = readReroute.fileName(); - sf.pTo = writeReroute.fileName(); - int shRes = ::SHFileOperationW(&sf); - switch (shRes) { - case 0x78: - callContext.updateLastError(ERROR_ACCESS_DENIED); - break; - case 0x7C: - callContext.updateLastError(ERROR_FILE_NOT_FOUND); - break; - case 0x7E: - case 0x80: - callContext.updateLastError(ERROR_FILE_EXISTS); - break; - default: - callContext.updateLastError(shRes); - } - res = shRes == 0; - } else - res = ::MoveFileExW(readReroute.fileName(), writeReroute.fileName(), newFlags); - POST_REALCALL - - if (res) - SetLastError(ERROR_SUCCESS); - - writeReroute.updateResult(callContext, res); - - if (res) { - readReroute.removeMapping( - READ_CONTEXT(), isDirectory); // Updating the rerouteCreate to check deleted - // file entries should make this okay - - if (writeReroute.newReroute()) { - if (isDirectory) - RerouteW::addDirectoryMapping(WRITE_CONTEXT(), fs::path(lpNewFileName), - fs::path(writeReroute.fileName())); - else - writeReroute.insertMapping(WRITE_CONTEXT()); - } - } - - if (readReroute.wasRerouted() || writeReroute.wasRerouted() || - writeReroute.changedError()) - LOG_CALL() - .PARAM(readReroute.fileName()) - .PARAM(writeReroute.fileName()) - .PARAMWRAP(dwFlags) - .PARAMWRAP(newFlags) - .PARAM(res) - .PARAM(writeReroute.originalError()) - .PARAM(callContext.lastError()); - } - - HOOK_END - - return res; -} - -BOOL WINAPI usvfs::hook_MoveFileWithProgressA(LPCSTR lpExistingFileName, - LPCSTR lpNewFileName, - LPPROGRESS_ROUTINE lpProgressRoutine, - LPVOID lpData, DWORD dwFlags) -{ - BOOL res = FALSE; - - HOOK_START_GROUP(MutExHookGroup::SHELL_FILEOP) - - if (!callContext.active()) { - res = MoveFileWithProgressA(lpExistingFileName, lpNewFileName, lpProgressRoutine, - lpData, dwFlags); - callContext.updateLastError(); - return res; - } - - HOOK_END - HOOK_START - - const auto& existingFileName = ush::string_cast<std::wstring>(lpExistingFileName); - - // careful: lpNewFileName can be null if dwFlags is - // MOVEFILE_DELAY_UNTIL_REBOOT, so don't blindly cast the string and make sure - // the null pointer is forwarded correctly - std::wstring newFileNameWstring; - const wchar_t* newFileName = nullptr; - - if (lpNewFileName) { - newFileNameWstring = ush::string_cast<std::wstring>(lpNewFileName); - newFileName = newFileNameWstring.c_str(); - } - - PRE_REALCALL - res = MoveFileWithProgressW(existingFileName.c_str(), newFileName, lpProgressRoutine, - lpData, dwFlags); - POST_REALCALL - - HOOK_END - - return res; -} - -BOOL WINAPI usvfs::hook_MoveFileWithProgressW(LPCWSTR lpExistingFileName, - LPCWSTR lpNewFileName, - LPPROGRESS_ROUTINE lpProgressRoutine, - LPVOID lpData, DWORD dwFlags) -{ - - // TODO: Remove all redundant hooks to moveFile alternatives. - // it would appear that all other moveFile functions end up calling this one with no - // additional code. - BOOL res = FALSE; - - HOOK_START_GROUP(MutExHookGroup::SHELL_FILEOP) - if (!callContext.active()) { - res = MoveFileWithProgressW(lpExistingFileName, lpNewFileName, lpProgressRoutine, - lpData, dwFlags); - callContext.updateLastError(); - return res; - } - - RerouteW readReroute; - usvfs::CreateRerouter writeReroute; - bool callOriginal = true; - DWORD newFlags = dwFlags; - - { - auto context = READ_CONTEXT(); - readReroute = RerouteW::create(context, callContext, lpExistingFileName); - callOriginal = writeReroute.rerouteNew(context, callContext, lpNewFileName, - newFlags & MOVEFILE_REPLACE_EXISTING, - "hook_MoveFileWithProgressW"); - } - - if (callOriginal) { - bool movedDrives = rewriteChangedDrives(lpExistingFileName, lpNewFileName, - readReroute, writeReroute); - if (movedDrives) - newFlags |= MOVEFILE_COPY_ALLOWED; - - bool isDirectory = pathIsDirectory(readReroute.fileName()); - - PRE_REALCALL - if (isDirectory && movedDrives) { - SHFILEOPSTRUCTW sf = {0}; - sf.wFunc = FO_MOVE; - sf.hwnd = 0; - sf.fFlags = FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR | FOF_NOERRORUI; - sf.pFrom = readReroute.fileName(); - sf.pTo = writeReroute.fileName(); - int shRes = ::SHFileOperationW(&sf); - switch (shRes) { - case 0x78: - callContext.updateLastError(ERROR_ACCESS_DENIED); - break; - case 0x7C: - callContext.updateLastError(ERROR_FILE_NOT_FOUND); - break; - case 0x7E: - case 0x80: - callContext.updateLastError(ERROR_FILE_EXISTS); - break; - default: - callContext.updateLastError(shRes); - } - res = shRes == 0; - } else - res = ::MoveFileWithProgressW(readReroute.fileName(), writeReroute.fileName(), - lpProgressRoutine, lpData, newFlags); - POST_REALCALL - - if (res) - SetLastError(ERROR_SUCCESS); - - writeReroute.updateResult(callContext, res); - - if (res) { - // TODO: this call causes the node to be removed twice in case of - // MOVEFILE_COPY_ALLOWED as the deleteFile hook lower level already takes care of - // it, but deleteFile can't be disabled since we are relying on it in case of - // MOVEFILE_REPLACE_EXISTING for the destination file. - readReroute.removeMapping( - READ_CONTEXT(), - isDirectory); // Updating the rerouteCreate to check deleted file entries - // should make this okay (not related to comments above) - - if (writeReroute.newReroute()) { - if (isDirectory) - RerouteW::addDirectoryMapping(WRITE_CONTEXT(), fs::path(lpNewFileName), - fs::path(writeReroute.fileName())); - else - writeReroute.insertMapping(WRITE_CONTEXT()); - } - } - - if (readReroute.wasRerouted() || writeReroute.wasRerouted() || - writeReroute.changedError()) - LOG_CALL() - .PARAM(readReroute.fileName()) - .PARAM(writeReroute.fileName()) - .PARAMWRAP(dwFlags) - .PARAMWRAP(newFlags) - .PARAM(res) - .PARAM(writeReroute.originalError()) - .PARAM(callContext.lastError()); - } - - HOOK_END - - return res; -} - -BOOL WINAPI usvfs::hook_CopyFileExW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName, - LPPROGRESS_ROUTINE lpProgressRoutine, LPVOID lpData, - LPBOOL pbCancel, DWORD dwCopyFlags) -{ - BOOL res = FALSE; - - HOOK_START_GROUP(MutExHookGroup::SHELL_FILEOP) - if (!callContext.active()) { - res = CopyFileExW(lpExistingFileName, lpNewFileName, lpProgressRoutine, lpData, - pbCancel, dwCopyFlags); - callContext.updateLastError(); - return res; - } - - RerouteW readReroute; - usvfs::CreateRerouter writeReroute; - bool callOriginal = true; - - { - auto context = READ_CONTEXT(); - readReroute = RerouteW::create(context, callContext, lpExistingFileName); - callOriginal = writeReroute.rerouteNew( - context, callContext, lpNewFileName, - (dwCopyFlags & COPY_FILE_FAIL_IF_EXISTS) == 0, "hook_CopyFileExW"); - } - - if (callOriginal) { - PRE_REALCALL - res = ::CopyFileExW(readReroute.fileName(), writeReroute.fileName(), - lpProgressRoutine, lpData, pbCancel, dwCopyFlags); - POST_REALCALL - writeReroute.updateResult(callContext, res); - - if (res && writeReroute.newReroute()) - writeReroute.insertMapping(WRITE_CONTEXT()); - - if (readReroute.wasRerouted() || writeReroute.wasRerouted() || - writeReroute.changedError()) - LOG_CALL() - .PARAM(readReroute.fileName()) - .PARAM(writeReroute.fileName()) - .PARAM(res) - .PARAM(writeReroute.originalError()) - .PARAM(callContext.lastError()); - } - - HOOK_END - - return res; -} - -DWORD WINAPI usvfs::hook_GetCurrentDirectoryA(DWORD nBufferLength, LPSTR lpBuffer) -{ - DWORD res = 0; - - HOOK_START - - std::wstring buffer; - buffer.resize(nBufferLength); - - PRE_REALCALL - res = GetCurrentDirectoryW(nBufferLength, &buffer[0]); - POST_REALCALL - - if (res > 0) { - res = WideCharToMultiByte(CP_ACP, 0, buffer.c_str(), res + 1, lpBuffer, - nBufferLength, nullptr, nullptr); - } - - HOOK_END - - return res; -} - -DWORD WINAPI usvfs::hook_GetCurrentDirectoryW(DWORD nBufferLength, LPWSTR lpBuffer) -{ - DWORD res = FALSE; - - HOOK_START - - std::wstring actualCWD; - - if (!k32CurrentDirectoryTracker.get(actualCWD)) { - PRE_REALCALL - res = ::GetCurrentDirectoryW(nBufferLength, lpBuffer); - POST_REALCALL - } else { - ush::wcsncpy_sz(lpBuffer, &actualCWD[0], - std::min(static_cast<size_t>(nBufferLength), actualCWD.size() + 1)); - - // yupp, that's how GetCurrentDirectory actually works... - if (actualCWD.size() < nBufferLength) { - res = static_cast<DWORD>(actualCWD.size()); - } else { - res = static_cast<DWORD>(actualCWD.size() + 1); - } - } - - if (nBufferLength) - LOG_CALL() - .PARAM(std::wstring(lpBuffer, res)) - .PARAM(nBufferLength) - .PARAM(actualCWD.size()) - .PARAM(res) - .PARAM(callContext.lastError()); - - HOOK_END - - return res; -} - -BOOL WINAPI usvfs::hook_SetCurrentDirectoryA(LPCSTR lpPathName) -{ - return SetCurrentDirectoryW(ush::string_cast<std::wstring>(lpPathName).c_str()); -} - -BOOL WINAPI usvfs::hook_SetCurrentDirectoryW(LPCWSTR lpPathName) -{ - BOOL res = FALSE; - - HOOK_START - - const fs::path& realPath = RerouteW::canonizePath(RerouteW::absolutePath(lpPathName)); - const std::wstring& realPathStr = realPath.wstring(); - std::wstring finalRoute; - BOOL found = FALSE; - - if (fs::exists(realPath)) - finalRoute = realPathStr; - else { - WCHAR processDir[MAX_PATH]; - if (::GetModuleFileNameW(NULL, processDir, MAX_PATH) != 0 && - ::PathRemoveFileSpecW(processDir)) { - WCHAR processName[MAX_PATH]; - ::GetModuleFileNameW(NULL, processName, MAX_PATH); - fs::path routedName = realPath / processName; - RerouteW rerouteTest = - RerouteW::create(READ_CONTEXT(), callContext, routedName.wstring().c_str()); - if (rerouteTest.wasRerouted()) { - std::wstring reroutedPath = rerouteTest.fileName(); - if (routedName.wstring().find(processDir) != std::string::npos) { - fs::path finalPath(reroutedPath); - finalRoute = finalPath.parent_path().wstring(); - found = TRUE; - } - } - } - - if (!found) { - RerouteW reroute = - RerouteW::create(READ_CONTEXT(), callContext, realPathStr.c_str()); - finalRoute = reroute.fileName(); - } - } - - PRE_REALCALL - res = ::SetCurrentDirectoryW(finalRoute.c_str()); - POST_REALCALL - - if (res) - if (!k32CurrentDirectoryTracker.set(realPathStr)) - spdlog::get("usvfs")->warn("Updating actual current directory failed: {} ?!", - string_cast<std::string>(realPathStr)); - - LOG_CALL() - .PARAM(lpPathName) - .PARAM(realPathStr) - .PARAM(finalRoute) - .PARAM(res) - .PARAM(callContext.lastError()); - - HOOK_END - - return res; -} - -DLLEXPORT BOOL WINAPI usvfs::hook_CreateDirectoryW( - LPCWSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttributes) -{ - BOOL res = FALSE; - HOOK_START - - RerouteW reroute = RerouteW::createOrNew(READ_CONTEXT(), callContext, lpPathName); - - PRE_REALCALL - res = ::CreateDirectoryW(reroute.fileName(), lpSecurityAttributes); - POST_REALCALL - - if (res && reroute.newReroute()) - reroute.insertMapping(WRITE_CONTEXT(), true); - - if (reroute.wasRerouted()) - LOG_CALL() - .PARAM(lpPathName) - .PARAM(reroute.fileName()) - .PARAM(res) - .PARAM(callContext.lastError()); - - HOOK_END - - return res; -} - -DLLEXPORT BOOL WINAPI usvfs::hook_RemoveDirectoryW(LPCWSTR lpPathName) -{ - - BOOL res = FALSE; - - HOOK_START_GROUP(MutExHookGroup::DELETE_FILE) - // Why is the usual if (!callContext.active()... check missing? - - RerouteW reroute = RerouteW::create(READ_CONTEXT(), callContext, lpPathName); - - PRE_REALCALL - if (reroute.wasRerouted()) { - res = ::RemoveDirectoryW(reroute.fileName()); - } else { - res = ::RemoveDirectoryW(lpPathName); - } - POST_REALCALL - - if (res) { - reroute.removeMapping(READ_CONTEXT(), true); - } - - if (reroute.wasRerouted()) - LOG_CALL() - .PARAM(lpPathName) - .PARAM(reroute.fileName()) - .PARAM(res) - .PARAM(callContext.lastError()); - - HOOK_END - - return res; -} - -DWORD WINAPI usvfs::hook_GetFullPathNameA(LPCSTR lpFileName, DWORD nBufferLength, - LPSTR lpBuffer, LPSTR* lpFilePart) -{ - DWORD res = 0UL; - - HOOK_START_GROUP(MutExHookGroup::FULL_PATHNAME) - if (!callContext.active()) { - res = GetFullPathNameA(lpFileName, nBufferLength, lpBuffer, lpFilePart); - callContext.updateLastError(); - return res; - } - - std::string resolvedWithCMD; - - std::wstring actualCWD; - fs::path filePath = ush::string_cast<std::wstring>(lpFileName, CodePage::UTF8); - if (k32CurrentDirectoryTracker.get(actualCWD, filePath.wstring().c_str())) { - if (!filePath.is_absolute()) - resolvedWithCMD = ush::string_cast<std::string>( - (actualCWD / filePath.relative_path()).wstring()); - } - - PRE_REALCALL - res = - ::GetFullPathNameA(resolvedWithCMD.empty() ? lpFileName : resolvedWithCMD.c_str(), - nBufferLength, lpBuffer, lpFilePart); - POST_REALCALL - - if (false && nBufferLength) - LOG_CALL() - .PARAM(lpFileName) - .PARAM(resolvedWithCMD) - .PARAM(std::string(lpBuffer, res)) - .PARAM(nBufferLength) - .PARAM(res) - .PARAM(callContext.lastError()); - - HOOK_END - - return res; -} - -DWORD WINAPI usvfs::hook_GetFullPathNameW(LPCWSTR lpFileName, DWORD nBufferLength, - LPWSTR lpBuffer, LPWSTR* lpFilePart) -{ - DWORD res = 0UL; - - HOOK_START_GROUP(MutExHookGroup::FULL_PATHNAME) - if (!callContext.active()) { - res = GetFullPathNameW(lpFileName, nBufferLength, lpBuffer, lpFilePart); - callContext.updateLastError(); - return res; - } - - std::wstring resolvedWithCMD; - - std::wstring actualCWD; - if (k32CurrentDirectoryTracker.get(actualCWD, lpFileName)) { - fs::path filePath = lpFileName; - if (!filePath.is_absolute()) - resolvedWithCMD = (actualCWD / filePath.relative_path()).wstring(); - } - - PRE_REALCALL - res = - ::GetFullPathNameW(resolvedWithCMD.empty() ? lpFileName : resolvedWithCMD.c_str(), - nBufferLength, lpBuffer, lpFilePart); - POST_REALCALL - - if (false && nBufferLength) - LOG_CALL() - .PARAM(lpFileName) - .PARAM(resolvedWithCMD) - .PARAM(std::wstring(lpBuffer, res)) - .PARAM(nBufferLength) - .PARAM(res) - .PARAM(callContext.lastError()); - - HOOK_END - - return res; -} - -DWORD WINAPI usvfs::hook_GetModuleFileNameA(HMODULE hModule, LPSTR lpFilename, - DWORD nSize) -{ - DWORD res = 0UL; - - HOOK_START_GROUP(MutExHookGroup::ALL_GROUPS) - - PRE_REALCALL - res = ::GetModuleFileNameA(hModule, lpFilename, nSize); - POST_REALCALL - if ((res != 0) && callContext.active()) { - std::vector<char> buf; - // If GetModuleFileNameA failed because the buffer is not large enough this - // complicates matters because we are dealing with incomplete information - // (consider for example the case that we have a long real path which will - // be routed to a short virtual so the call should actually succeed in such - // a case). To solve this we simply use our own buffer to find the complete - // module path: - DWORD full_res = res; - size_t buf_size = nSize; - while (full_res == buf_size) { - buf_size = std::max(static_cast<size_t>(MAX_PATH), buf_size * 2); - buf.resize(buf_size); - full_res = ::GetModuleFileNameA(hModule, buf.data(), buf_size); - } - - RerouteW reroute = RerouteW::create( - READ_CONTEXT(), callContext, - ush::string_cast<std::wstring>(buf.empty() ? lpFilename : buf.data()).c_str(), - true); - if (reroute.wasRerouted()) { - DWORD reroutedSize = static_cast<DWORD>(wcslen(reroute.fileName())); - if (reroutedSize >= nSize) { - reroutedSize = nSize - 1; - callContext.updateLastError(ERROR_INSUFFICIENT_BUFFER); - res = nSize; - } else - res = reroutedSize; - memcpy(lpFilename, ush::string_cast<std::string>(reroute.fileName()).c_str(), - reroutedSize * sizeof(lpFilename[0])); - lpFilename[reroutedSize] = 0; - - LOG_CALL() - .PARAM(hModule) - .addParam("lpFilename", (res != 0UL) ? lpFilename : "<not set>") - .PARAM(nSize) - .PARAM(res) - .PARAM(callContext.lastError()); - } - } - HOOK_END - - return res; -} - -DWORD WINAPI usvfs::hook_GetModuleFileNameW(HMODULE hModule, LPWSTR lpFilename, - DWORD nSize) -{ - DWORD res = 0UL; - - HOOK_START_GROUP(MutExHookGroup::ALL_GROUPS) - - PRE_REALCALL - res = ::GetModuleFileNameW(hModule, lpFilename, nSize); - POST_REALCALL - if ((res != 0) && callContext.active()) { - std::vector<WCHAR> buf; - // If GetModuleFileNameW failed because the buffer is not large enough this - // complicates matters because we are dealing with incomplete information (consider - // for example the case that we have a long real path which will be routed to a - // short virtual so the call should actually succeed in such a case). To solve this - // we simply use our own buffer to find the complete module path: - DWORD full_res = res; - size_t buf_size = nSize; - while (full_res == buf_size) { - buf_size = std::max(static_cast<size_t>(MAX_PATH), buf_size * 2); - buf.resize(buf_size); - full_res = ::GetModuleFileNameW(hModule, buf.data(), buf_size); - } - - RerouteW reroute = RerouteW::create(READ_CONTEXT(), callContext, - buf.empty() ? lpFilename : buf.data(), true); - if (reroute.wasRerouted()) { - DWORD reroutedSize = static_cast<DWORD>(wcslen(reroute.fileName())); - if (reroutedSize >= nSize) { - reroutedSize = nSize - 1; - callContext.updateLastError(ERROR_INSUFFICIENT_BUFFER); - res = nSize; - } else - res = reroutedSize; - memcpy(lpFilename, reroute.fileName(), reroutedSize * sizeof(lpFilename[0])); - lpFilename[reroutedSize] = 0; - - LOG_CALL() - .PARAM(hModule) - .addParam("lpFilename", (res != 0UL) ? lpFilename : L"<not set>") - .PARAM(nSize) - .PARAM(res) - .PARAM(callContext.lastError()); - } - } - HOOK_END - - return res; -} - -HANDLE WINAPI usvfs::hook_FindFirstFileExW( - LPCWSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData, - FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags) -{ - HANDLE res = INVALID_HANDLE_VALUE; - - HOOK_START_GROUP(MutExHookGroup::SEARCH_FILES) - if (!callContext.active()) { - res = FindFirstFileExW(lpFileName, fInfoLevelId, lpFindFileData, fSearchOp, - lpSearchFilter, dwAdditionalFlags); - callContext.updateLastError(); - return res; - } - - // FindFirstFileEx() must fail early if the path ends with a slash - if (lpFileName) { - const auto len = wcslen(lpFileName); - if (len > 0) { - if (lpFileName[len - 1] == L'\\' || lpFileName[len - 1] == L'/') { - spdlog::get("usvfs")->warn( - "hook_FindFirstFileExW(): path '{}' ends with slash, always fails", - fs::path(lpFileName).string()); - return INVALID_HANDLE_VALUE; - } - } - } - - fs::path finalPath; - RerouteW reroute; - fs::path originalPath; - - bool usedRewrite = false; - - // We need to do some trickery here, since we only want to use the hooked - // NtQueryDirectoryFile for rerouted locations we need to check if the Directory path - // has been routed instead of the full path. - originalPath = RerouteW::canonizePath(RerouteW::absolutePath(lpFileName)); - PRE_REALCALL - res = ::FindFirstFileExW(originalPath.c_str(), fInfoLevelId, lpFindFileData, - fSearchOp, lpSearchFilter, dwAdditionalFlags); - POST_REALCALL - - if (res == INVALID_HANDLE_VALUE) { - fs::path searchPath = originalPath.filename(); - fs::path parentPath = originalPath.parent_path(); - std::wstring findPath = parentPath.wstring(); - while (findPath.find(L"*?<>\"", 0, 1) != std::wstring::npos) { - searchPath = parentPath.filename() / searchPath; - parentPath = parentPath.parent_path(); - findPath = parentPath.wstring(); - } - reroute = RerouteW::create(READ_CONTEXT(), callContext, parentPath.c_str()); - if (reroute.wasRerouted()) { - finalPath = reroute.fileName(); - finalPath /= searchPath.wstring(); - } - if (!finalPath.empty()) { - PRE_REALCALL - usedRewrite = true; - res = ::FindFirstFileExW(finalPath.c_str(), fInfoLevelId, lpFindFileData, - fSearchOp, lpSearchFilter, dwAdditionalFlags); - POST_REALCALL - } - } - - if (res != INVALID_HANDLE_VALUE) { - // store the original search path for use during iteration - WRITE_CONTEXT()->customData<SearchHandleMap>(SearchHandles)[res] = lpFileName; - } - - LOG_CALL() - .PARAM(lpFileName) - .PARAM(originalPath.c_str()) - .PARAM(res) - .PARAM(callContext.lastError()); - if (usedRewrite) - LOG_CALL() - .PARAM(lpFileName) - .PARAM(finalPath.c_str()) - .PARAM(res) - .PARAM(callContext.lastError()); - - HOOK_END - - return res; -} - -HRESULT(WINAPI* usvfs::CopyFile2)(PCWSTR pwszExistingFileName, PCWSTR pwszNewFileName, - COPYFILE2_EXTENDED_PARAMETERS* pExtendedParameters); - -HRESULT WINAPI usvfs::hook_CopyFile2(PCWSTR pwszExistingFileName, - PCWSTR pwszNewFileName, - COPYFILE2_EXTENDED_PARAMETERS* pExtendedParameters) -{ - HRESULT res = E_FAIL; - - typedef HRESULT(WINAPI * CopyFile2_t)(PCWSTR, PCWSTR, COPYFILE2_EXTENDED_PARAMETERS*); - - HOOK_START_GROUP(MutExHookGroup::SHELL_FILEOP) - if (!callContext.active()) { - res = CopyFile2(pwszExistingFileName, pwszNewFileName, pExtendedParameters); - callContext.updateLastError(); - return res; - } - - RerouteW readReroute; - CreateRerouter writeReroute; - bool callOriginal = true; - - { - auto context = READ_CONTEXT(); - readReroute = RerouteW::create(context, callContext, pwszExistingFileName); - callOriginal = writeReroute.rerouteNew( - context, callContext, pwszNewFileName, - pExtendedParameters && - (pExtendedParameters->dwCopyFlags & COPY_FILE_FAIL_IF_EXISTS) == 0, - "hook_CopyFile2"); - } - - if (callOriginal) { - PRE_REALCALL - res = - CopyFile2(readReroute.fileName(), writeReroute.fileName(), pExtendedParameters); - POST_REALCALL - writeReroute.updateResult(callContext, SUCCEEDED(res)); - - if (SUCCEEDED(res) && writeReroute.newReroute()) - writeReroute.insertMapping(WRITE_CONTEXT()); - - if (readReroute.wasRerouted() || writeReroute.wasRerouted() || - writeReroute.changedError()) - LOG_CALL() - .PARAM(readReroute.fileName()) - .PARAM(writeReroute.fileName()) - .PARAM(res) - .PARAM(writeReroute.originalError()) - .PARAM(callContext.lastError()); - } - - HOOK_END - - return res; -} - -DWORD WINAPI usvfs::hook_GetPrivateProfileStringA(LPCSTR lpAppName, LPCSTR lpKeyName, - LPCSTR lpDefault, - LPSTR lpReturnedString, DWORD nSize, - LPCSTR lpFileName) -{ - DWORD res = 0; - - HOOK_START_GROUP(MutExHookGroup::OPEN_FILE) - - if (!callContext.active() || !RerouteW::interestingPath(lpFileName)) { - res = ::GetPrivateProfileStringA(lpAppName, lpKeyName, lpDefault, lpReturnedString, - nSize, lpFileName); - callContext.updateLastError(); - return res; - } - - RerouteW reroute = RerouteW::create( - READ_CONTEXT(), callContext, ush::string_cast<std::wstring>(lpFileName).c_str()); - - PRE_REALCALL - res = ::GetPrivateProfileStringA( - lpAppName, lpKeyName, lpDefault, lpReturnedString, nSize, - ush::string_cast<std::string>(reroute.fileName()).c_str()); - POST_REALCALL - - if (reroute.wasRerouted()) { - LOG_CALL() - .PARAM(lpAppName) - .PARAM(lpKeyName) - .PARAM(lpFileName) - .PARAM(reroute.fileName()) - .PARAMHEX(res) - .PARAM(callContext.lastError()); - } - - HOOK_END - - return res; -} - -DWORD WINAPI usvfs::hook_GetPrivateProfileStringW(LPCWSTR lpAppName, LPCWSTR lpKeyName, - LPCWSTR lpDefault, - LPWSTR lpReturnedString, DWORD nSize, - LPCWSTR lpFileName) -{ - DWORD res = 0; - - HOOK_START_GROUP(MutExHookGroup::OPEN_FILE) - - if (!callContext.active() || !RerouteW::interestingPath(lpFileName)) { - res = ::GetPrivateProfileStringW(lpAppName, lpKeyName, lpDefault, lpReturnedString, - nSize, lpFileName); - callContext.updateLastError(); - return res; - } - - RerouteW reroute = RerouteW::create(READ_CONTEXT(), callContext, lpFileName); - - PRE_REALCALL - res = ::GetPrivateProfileStringW(lpAppName, lpKeyName, lpDefault, lpReturnedString, - nSize, reroute.fileName()); - POST_REALCALL - - if (reroute.wasRerouted()) { - LOG_CALL() - .PARAM(lpAppName) - .PARAM(lpKeyName) - .PARAM(lpFileName) - .PARAM(reroute.fileName()) - .PARAMHEX(res) - .PARAM(callContext.lastError()); - } - - HOOK_END - - return res; -} - -DWORD WINAPI usvfs::hook_GetPrivateProfileSectionA(LPCSTR lpAppName, - LPSTR lpReturnedString, DWORD nSize, - LPCSTR lpFileName) -{ - DWORD res = 0; - - HOOK_START_GROUP(MutExHookGroup::OPEN_FILE) - - if (!callContext.active() || !RerouteW::interestingPath(lpFileName)) { - res = ::GetPrivateProfileSectionA(lpAppName, lpReturnedString, nSize, lpFileName); - callContext.updateLastError(); - return res; - } - - RerouteW reroute = RerouteW::create( - READ_CONTEXT(), callContext, ush::string_cast<std::wstring>(lpFileName).c_str()); - - PRE_REALCALL - res = ::GetPrivateProfileSectionA( - lpAppName, lpReturnedString, nSize, - ush::string_cast<std::string>(reroute.fileName()).c_str()); - POST_REALCALL - - if (reroute.wasRerouted()) { - LOG_CALL() - .PARAM(lpAppName) - .PARAM(lpFileName) - .PARAM(reroute.fileName()) - .PARAMHEX(res) - .PARAM(callContext.lastError()); - } - - HOOK_END - - return res; -} - -DWORD WINAPI usvfs::hook_GetPrivateProfileSectionW(LPCWSTR lpAppName, - LPWSTR lpReturnedString, DWORD nSize, - LPCWSTR lpFileName) -{ - DWORD res = 0; - - HOOK_START_GROUP(MutExHookGroup::OPEN_FILE) - - if (!callContext.active() || !RerouteW::interestingPath(lpFileName)) { - res = ::GetPrivateProfileSectionW(lpAppName, lpReturnedString, nSize, lpFileName); - callContext.updateLastError(); - return res; - } - - RerouteW reroute = RerouteW::create(READ_CONTEXT(), callContext, lpFileName); - - PRE_REALCALL - res = ::GetPrivateProfileSectionW(lpAppName, lpReturnedString, nSize, - reroute.fileName()); - POST_REALCALL - - if (reroute.wasRerouted()) { - LOG_CALL() - .PARAM(lpAppName) - .PARAM(lpFileName) - .PARAM(reroute.fileName()) - .PARAMHEX(res) - .PARAM(callContext.lastError()); - } - - HOOK_END - - return res; -} - -BOOL WINAPI usvfs::hook_WritePrivateProfileStringA(LPCSTR lpAppName, LPCSTR lpKeyName, - LPCSTR lpString, LPCSTR lpFileName) -{ - BOOL res = false; - - HOOK_START_GROUP(MutExHookGroup::OPEN_FILE) - - if (!callContext.active() || !RerouteW::interestingPath(lpFileName)) { - res = ::WritePrivateProfileStringA(lpAppName, lpKeyName, lpString, lpFileName); - callContext.updateLastError(); - return res; - } - - CreateRerouter reroute; - bool callOriginal = reroute.rerouteNew( - READ_CONTEXT(), callContext, ush::string_cast<std::wstring>(lpFileName).c_str(), - true, "hook_WritePrivateProfileStringA"); - - if (callOriginal) { - PRE_REALCALL - res = ::WritePrivateProfileStringA( - lpAppName, lpKeyName, lpString, - ush::string_cast<std::string>(reroute.fileName()).c_str()); - POST_REALCALL - reroute.updateResult(callContext, res); - - if (res && reroute.newReroute()) - reroute.insertMapping(WRITE_CONTEXT()); - - if (reroute.wasRerouted() || reroute.changedError()) - LOG_CALL() - .PARAM(lpAppName) - .PARAM(lpKeyName) - .PARAM(lpFileName) - .PARAM(reroute.fileName()) - .PARAMHEX(res) - .PARAM(reroute.originalError()) - .PARAM(callContext.lastError()); - } - - HOOK_END - - return res; -} - -BOOL WINAPI usvfs::hook_WritePrivateProfileStringW(LPCWSTR lpAppName, LPCWSTR lpKeyName, - LPCWSTR lpString, LPCWSTR lpFileName) -{ - BOOL res = false; - - HOOK_START_GROUP(MutExHookGroup::OPEN_FILE) - - if (!callContext.active() || !RerouteW::interestingPath(lpFileName)) { - res = ::WritePrivateProfileStringW(lpAppName, lpKeyName, lpString, lpFileName); - callContext.updateLastError(); - return res; - } - - CreateRerouter reroute; - bool callOriginal = reroute.rerouteNew(READ_CONTEXT(), callContext, lpFileName, true, - "hook_WritePrivateProfileStringW"); - - if (callOriginal) { - PRE_REALCALL - res = ::WritePrivateProfileStringW(lpAppName, lpKeyName, lpString, - reroute.fileName()); - POST_REALCALL - reroute.updateResult(callContext, res); - - if (res && reroute.newReroute()) - reroute.insertMapping(WRITE_CONTEXT()); - - if (reroute.wasRerouted() || reroute.changedError()) - LOG_CALL() - .PARAM(lpAppName) - .PARAM(lpKeyName) - .PARAM(lpFileName) - .PARAM(reroute.fileName()) - .PARAMHEX(res) - .PARAM(reroute.originalError()) - .PARAM(callContext.lastError()); - } - - HOOK_END - - return res; -} - -VOID WINAPI usvfs::hook_ExitProcess(UINT exitCode) -{ - HOOK_START - - { - HookContext::Ptr context = WRITE_CONTEXT(); - - std::vector<std::future<int>>& delayed = context->delayed(); - - if (!delayed.empty()) { - // ensure all delayed tasks are completed before we exit the process - for (std::future<int>& delayedOp : delayed) { - delayedOp.get(); - } - delayed.clear(); - } - } - - // exitprocess doesn't return so logging the call after the real call doesn't - // make much sense. - // nor does any pre/post call macro - LOG_CALL().PARAM(exitCode); - - usvfsDisconnectVFS(); - - // HookManager::instance().removeHook("ExitProcess"); - // PRE_REALCALL - ::ExitProcess(exitCode); - // POST_REALCALL - - HOOK_END -} diff --git a/libs/usvfs/src/usvfs_dll/hooks/kernel32.h b/libs/usvfs/src/usvfs_dll/hooks/kernel32.h deleted file mode 100644 index 7087b2e..0000000 --- a/libs/usvfs/src/usvfs_dll/hooks/kernel32.h +++ /dev/null @@ -1,116 +0,0 @@ -#pragma once - -#include "ntdll_declarations.h" -#include <dllimport.h> - -namespace usvfs -{ - -DLLEXPORT BOOL WINAPI hook_GetFileAttributesExA(LPCSTR lpFileName, - GET_FILEEX_INFO_LEVELS fInfoLevelId, - LPVOID lpFileInformation); -DLLEXPORT BOOL WINAPI hook_GetFileAttributesExW(LPCWSTR lpFileName, - GET_FILEEX_INFO_LEVELS fInfoLevelId, - LPVOID lpFileInformation); -DLLEXPORT DWORD WINAPI hook_GetFileAttributesA(LPCSTR lpFileName); -DLLEXPORT DWORD WINAPI hook_GetFileAttributesW(LPCWSTR lpFileName); -DLLEXPORT DWORD WINAPI hook_SetFileAttributesW(LPCWSTR lpFileName, - DWORD dwFileAttributes); - -DLLEXPORT DWORD WINAPI hook_GetCurrentDirectoryA(DWORD nBufferLength, LPSTR lpBuffer); -DLLEXPORT DWORD WINAPI hook_GetCurrentDirectoryW(DWORD nBufferLength, LPWSTR lpBuffer); -DLLEXPORT BOOL WINAPI hook_SetCurrentDirectoryA(LPCSTR lpPathName); -DLLEXPORT BOOL WINAPI hook_SetCurrentDirectoryW(LPCWSTR lpPathName); - -DLLEXPORT DWORD WINAPI hook_GetFullPathNameA(LPCSTR lpFileName, DWORD nBufferLength, - LPSTR lpBuffer, LPSTR* lpFilePart); -DLLEXPORT DWORD WINAPI hook_GetFullPathNameW(LPCWSTR lpFileName, DWORD nBufferLength, - LPWSTR lpBuffer, LPWSTR* lpFilePart); - -DLLEXPORT BOOL WINAPI hook_CreateDirectoryW(LPCWSTR lpPathName, - LPSECURITY_ATTRIBUTES lpSecurityAttributes); -DLLEXPORT BOOL WINAPI hook_RemoveDirectoryW(LPCWSTR lpPathName); - -DLLEXPORT BOOL WINAPI hook_DeleteFileW(LPCWSTR lpFileName); - -DLLEXPORT BOOL WINAPI hook_MoveFileA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName); -DLLEXPORT BOOL WINAPI hook_MoveFileW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName); -DLLEXPORT BOOL WINAPI hook_MoveFileExA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName, - DWORD dwFlags); -DLLEXPORT BOOL WINAPI hook_MoveFileExW(LPCWSTR lpExistingFileName, - LPCWSTR lpNewFileName, DWORD dwFlags); -DLLEXPORT BOOL WINAPI hook_MoveFileWithProgressA(LPCSTR lpExistingFileName, - LPCSTR lpNewFileName, - LPPROGRESS_ROUTINE lpProgressRoutine, - LPVOID lpData, DWORD dwFlags); -DLLEXPORT BOOL WINAPI hook_MoveFileWithProgressW(LPCWSTR lpExistingFileName, - LPCWSTR lpNewFileName, - LPPROGRESS_ROUTINE lpProgressRoutine, - LPVOID lpData, DWORD dwFlags); -; - -DLLEXPORT BOOL WINAPI hook_CopyFileExW(LPCWSTR lpExistingFileName, - LPCWSTR lpNewFileName, - LPPROGRESS_ROUTINE lpProgressRoutine, - LPVOID lpData, LPBOOL pbCancel, - DWORD dwCopyFlags); - -extern HRESULT(WINAPI* CopyFile2)(PCWSTR pwszExistingFileName, PCWSTR pwszNewFileName, - COPYFILE2_EXTENDED_PARAMETERS* pExtendedParameters); -DLLEXPORT HRESULT WINAPI -hook_CopyFile2(PCWSTR pwszExistingFileName, PCWSTR pwszNewFileName, - COPYFILE2_EXTENDED_PARAMETERS* pExtendedParameters); - -DLLEXPORT HMODULE WINAPI hook_LoadLibraryExA(LPCSTR lpFileName, HANDLE hFile, - DWORD dwFlags); -DLLEXPORT HMODULE WINAPI hook_LoadLibraryExW(LPCWSTR lpFileName, HANDLE hFile, - DWORD dwFlags); - -extern BOOL(WINAPI* CreateProcessInternalW)( - LPVOID token, LPCWSTR lpApplicationName, LPWSTR lpCommandLine, - LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, - BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, - LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, - LPPROCESS_INFORMATION lpProcessInformation, LPVOID newToken); -DLLEXPORT BOOL WINAPI hook_CreateProcessInternalW( - LPVOID token, LPCWSTR lpApplicationName, LPWSTR lpCommandLine, - LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, - BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, - LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, - LPPROCESS_INFORMATION lpProcessInformation, LPVOID newToken); - -DLLEXPORT DWORD WINAPI hook_GetModuleFileNameA(HMODULE hModule, LPSTR lpFilename, - DWORD nSize); -DLLEXPORT DWORD WINAPI hook_GetModuleFileNameW(HMODULE hModule, LPWSTR lpFilename, - DWORD nSize); - -DLLEXPORT HANDLE WINAPI hook_FindFirstFileExW( - LPCWSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData, - FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags); - -DLLEXPORT DWORD WINAPI hook_GetPrivateProfileStringA(LPCSTR lpAppName, LPCSTR lpKeyName, - LPCSTR lpDefault, - LPSTR lpReturnedString, - DWORD nSize, LPCSTR lpFileName); -DLLEXPORT DWORD WINAPI hook_GetPrivateProfileStringW(LPCWSTR lpAppName, - LPCWSTR lpKeyName, - LPCWSTR lpDefault, - LPWSTR lpReturnedString, - DWORD nSize, LPCWSTR lpFileName); -DLLEXPORT DWORD WINAPI hook_GetPrivateProfileSectionA(LPCSTR lpAppName, - LPSTR lpReturnedString, - DWORD nSize, LPCSTR lpFileName); -DLLEXPORT DWORD WINAPI hook_GetPrivateProfileSectionW(LPCWSTR lpAppName, - LPWSTR lpReturnedString, - DWORD nSize, LPCWSTR lpFileName); -DLLEXPORT BOOL WINAPI hook_WritePrivateProfileStringA(LPCSTR lpAppName, - LPCSTR lpKeyName, LPCSTR lpString, - LPCSTR lpFileName); -DLLEXPORT BOOL WINAPI hook_WritePrivateProfileStringW(LPCWSTR lpAppName, - LPCWSTR lpKeyName, - LPCWSTR lpString, - LPCWSTR lpFileName); - -DLLEXPORT VOID WINAPI hook_ExitProcess(UINT exitCode); - -} // namespace usvfs diff --git a/libs/usvfs/src/usvfs_dll/hooks/ntdll.cpp b/libs/usvfs/src/usvfs_dll/hooks/ntdll.cpp deleted file mode 100644 index ad53418..0000000 --- a/libs/usvfs/src/usvfs_dll/hooks/ntdll.cpp +++ /dev/null @@ -1,1520 +0,0 @@ -#include "ntdll.h" - -#include <mutex> -#include <queue> -#include <set> - -#include <boost/filesystem.hpp> - -#include <addrtools.h> -#include <loghelpers.h> -#include <stringcast.h> -#include <stringutils.h> -#include <unicodestring.h> -#include <usvfs.h> - -#include "../hookcallcontext.h" -#include "../hookcontext.h" -#include "../maptracker.h" -#include "../stringcast_boost.h" - -#include "file_information_utils.h" -#include "sharedids.h" - -namespace ulog = usvfs::log; -namespace ush = usvfs::shared; -namespace bfs = boost::filesystem; - -using usvfs::UnicodeString; - -// flag definitions below are copied from winternl.h -#define FILE_SUPERSEDE 0x00000000 -#define FILE_OPEN 0x00000001 -#define FILE_CREATE 0x00000002 -#define FILE_OPEN_IF 0x00000003 -#define FILE_OVERWRITE 0x00000004 -#define FILE_OVERWRITE_IF 0x00000005 -#define FILE_MAXIMUM_DISPOSITION 0x00000005 - -template <typename T> -using unique_ptr_deleter = std::unique_ptr<T, void (*)(T*)>; - -class RedirectionInfo -{ -public: - UnicodeString path; - bool redirected; - - RedirectionInfo() {} - RedirectionInfo(UnicodeString path, bool redirected) - : path(path), redirected(redirected) - {} -}; - -class HandleTracker -{ -public: - using handle_type = HANDLE; - using info_type = UnicodeString; - - HandleTracker() { insert_current_directory(); } - - info_type lookup(handle_type handle) const - { - if (valid_handle(handle)) { - std::shared_lock<std::shared_mutex> lock(m_mutex); - auto find = m_map.find(handle); - if (find != m_map.end()) - return find->second; - } - return info_type(); - } - - void insert(handle_type handle, const info_type& info) - { - if (!valid_handle(handle)) - return; - std::unique_lock<std::shared_mutex> lock(m_mutex); - m_map[handle] = info; - } - - void erase(handle_type handle) - { - if (!valid_handle(handle)) - return; - std::unique_lock<std::shared_mutex> lock(m_mutex); - m_map.erase(handle); - } - -private: - static bool valid_handle(handle_type handle) - { - return handle && handle != INVALID_HANDLE_VALUE; - } - - void insert_current_directory() - { - ntdll_declarations_init(); - - UNICODE_STRING p{0}; - RTL_RELATIVE_NAME r{0}; - NTSTATUS status = - RtlDosPathNameToRelativeNtPathName_U_WithStatus(L"x", &p, nullptr, &r); - if (status >= 0) { - if (r.ContainingDirectory && p.Buffer) { - size_t len = p.Length / sizeof(WCHAR); - size_t trim = strlen("\\x") + (p.Buffer[len - 1] ? 0 : 1); - if (len > trim) - insert(r.ContainingDirectory, info_type(p.Buffer, len - trim)); - } - RtlReleaseRelativeName(&r); - if (p.Buffer) - HeapFree(GetProcessHeap(), 0, p.Buffer); - } - } - - mutable std::shared_mutex m_mutex; - std::unordered_map<handle_type, info_type> m_map; -}; - -HandleTracker ntdllHandleTracker; - -UnicodeString CreateUnicodeString(const OBJECT_ATTRIBUTES* objectAttributes) -{ - UnicodeString result = ntdllHandleTracker.lookup(objectAttributes->RootDirectory); - if (objectAttributes->ObjectName != nullptr) { - result.appendPath(objectAttributes->ObjectName); - } - return result; -} - -std::ostream& operator<<(std::ostream& os, const _UNICODE_STRING& str) -{ - try { - // TODO this does not correctly support surrogate pairs - // since the size used here is the number of 16-bit characters in the buffer - // whereas - // toNarrow expects the actual number of characters. - // It will always underestimate though, so worst case scenario we truncate - // the string - os << ush::string_cast<std::string>(str.Buffer, ush::CodePage::UTF8, - str.Length / sizeof(WCHAR)); - } catch (const std::exception& e) { - os << e.what(); - } - - return os; -} - -std::ostream& operator<<(std::ostream& os, POBJECT_ATTRIBUTES attr) -{ - return operator<<(os, *attr->ObjectName); -} - -RedirectionInfo applyReroute(const usvfs::HookContext::ConstPtr& context, - const usvfs::HookCallContext& callContext, - const UnicodeString& inPath) -{ - RedirectionInfo result; - result.path = inPath; - result.redirected = false; - - if (callContext.active()) { - // see if the file exists in the redirection tree - std::string lookupPath = ush::string_cast<std::string>( - static_cast<LPCWSTR>(result.path) + 4, ush::CodePage::UTF8); - auto node = context->redirectionTable()->findNode(lookupPath.c_str()); - // if so, replace the file name with the path to the mapped file - if ((node.get() != nullptr) && - (!node->data().linkTarget.empty() || node->isDirectory())) { - std::wstring reroutePath; - - if (node->data().linkTarget.length() > 0) { - reroutePath = ush::string_cast<std::wstring>(node->data().linkTarget.c_str(), - ush::CodePage::UTF8); - } else { - reroutePath = - ush::string_cast<std::wstring>(node->path().c_str(), ush::CodePage::UTF8); - } - if ((*reroutePath.rbegin() == L'\\') && (*lookupPath.rbegin() != '\\')) { - reroutePath.resize(reroutePath.size() - 1); - } - std::replace(reroutePath.begin(), reroutePath.end(), L'/', L'\\'); - if (reroutePath[1] == L'\\') - reroutePath[1] = L'?'; - result.path = LR"(\??\)" + reroutePath; - result.redirected = true; - } - } - return result; -} - -RedirectionInfo applyReroute(const usvfs::CreateRerouter& rerouter) -{ - RedirectionInfo result; - result.path = rerouter.fileName(); - result.redirected = rerouter.wasRerouted(); - - std::wstring reroutePath(static_cast<LPCWSTR>(result.path)); - std::replace(reroutePath.begin(), reroutePath.end(), L'/', L'\\'); - if (reroutePath[1] == L'\\') - reroutePath[1] = L'?'; - if (!((reroutePath[0] == L'\\') && (reroutePath[1] == L'?') && - (reroutePath[2] == L'?') && (reroutePath[3] == L'\\'))) { - result.path = LR"(\??\)" + reroutePath; - } - - return result; -} - -struct FindCreateTarget -{ - usvfs::RedirectionTree::NodePtrT target; - void operator()(usvfs::RedirectionTree::NodePtrT node) - { - if (node->hasFlag(usvfs::shared::FLAG_CREATETARGET)) { - target = node; - } - } -}; - -std::pair<UnicodeString, UnicodeString> -findCreateTarget(const usvfs::HookContext::ConstPtr& context, - const UnicodeString& inPath) -{ - std::pair<UnicodeString, UnicodeString> result; - result.first = inPath; - result.second = UnicodeString(); - - std::string lookupPath = ush::string_cast<std::string>( - static_cast<LPCWSTR>(result.first) + 4, ush::CodePage::UTF8); - FindCreateTarget visitor; - usvfs::RedirectionTree::VisitorFunction visitorWrapper = - [&](const usvfs::RedirectionTree::NodePtrT& node) { - visitor(node); - }; - context->redirectionTable()->visitPath(lookupPath, visitorWrapper); - if (visitor.target.get() != nullptr) { - bfs::path relativePath = - ush::make_relative(visitor.target->path(), bfs::path(lookupPath)); - - bfs::path target(visitor.target->data().linkTarget.c_str()); - target /= relativePath; - - result.second = UnicodeString(target.wstring().c_str()); - winapi::ex::wide::createPath(target.parent_path()); - } - return result; -} - -RedirectionInfo applyReroute(const usvfs::HookContext::ConstPtr& context, - const usvfs::HookCallContext& callContext, - POBJECT_ATTRIBUTES inAttributes) -{ - return applyReroute(context, callContext, CreateUnicodeString(inAttributes)); -} - -int NextDividableBy(int number, int divider) -{ - return static_cast<int>( - ceilf(static_cast<float>(number) / static_cast<float>(divider)) * divider); -} - -// Something is trying to create a variety of files starting with "\Device\", -// such as "\Device\DeviceApi\Dev\Query", "\Device\MMCSS\MmThread", -// "\Device\DeviceApi\CMNotify", etc. -// -// This used to create a bunch of spurious "Device" and "MMCSS" folders when -// using Explorer++. Some of these names seem to part of PnP, others from the -// "Multimedia Class Scheduler Service". -// -// There's basically zero information on this stuff online. -// -// Regardless, these files ended up being rerouted, so they became something -// like C:\game\Data\Somewhere\Device\MMCSS\MmThread, which ended up being -// rerouted to overwrite and created as a fake path -// "overwrite\Somewhere\Device\MMCSS" -// -// These paths are weird, they feel like pipes or something. It's not clear how -// they should be reliably recognized, so this is a hardcoded check for any -// path that starts with "\Device\". These paths will be forwarded directly -// to NtCreateFile/NtOpenFile -// -bool isDeviceFile(std::wstring_view name) -{ - static const std::wstring_view DevicePrefix(L"\\Device\\"); - - // starts with - if (name.size() < DevicePrefix.size()) { - return false; - } else { - return (_wcsnicmp(name.data(), DevicePrefix.data(), DevicePrefix.size()) == 0); - } -} - -NTSTATUS addNtSearchData(HANDLE hdl, PUNICODE_STRING FileName, - const std::wstring& fakeName, - FILE_INFORMATION_CLASS FileInformationClass, PVOID& buffer, - ULONG& bufferSize, std::set<std::wstring>& foundFiles, - HANDLE event, PIO_APC_ROUTINE apcRoutine, PVOID apcContext, - BOOLEAN returnSingleEntry) -{ - NTSTATUS res = STATUS_NO_SUCH_FILE; - if (hdl != INVALID_HANDLE_VALUE) { - PVOID lastValidRecord = nullptr; - PVOID bufferInit = buffer; - IO_STATUS_BLOCK status; - res = NtQueryDirectoryFile(hdl, event, apcRoutine, apcContext, &status, buffer, - bufferSize, FileInformationClass, returnSingleEntry, - FileName, FALSE); - - if ((res != STATUS_SUCCESS) || (status.Information <= 0)) { - bufferSize = 0UL; - } else { - ULONG totalOffset = 0; - PVOID lastSkipPos = nullptr; - - while (totalOffset < status.Information) { - ULONG offset; - std::wstring fileName; - GetFileInformationData(FileInformationClass, buffer, offset, fileName); - // in case this is a single-file search result and the specified - // filename differs from the file name found, replace it in the - // information structure - if ((totalOffset == 0) && (offset == 0) && (fakeName.length() > 0)) { - // if the fake name is larger than what is in the buffer and there is - // not enough room, that's a buffer overflow - if ((fakeName.length() > fileName.length()) && - ((fakeName.length() - fileName.length()) > - (bufferSize - status.Information))) { - res = STATUS_BUFFER_OVERFLOW; - break; - } - // WARNING for the case where the fake name is longer this needs to - // move back all further results and update the offset first - SetFileInformationFileName(FileInformationClass, buffer, fakeName); - fileName = fakeName; - } - bool add = true; - if (fileName.length() > 0) { - auto insertRes = foundFiles.insert(ush::to_upper(fileName)); - add = insertRes.second; // add only if we didn't find this file before - } - if (!add) { - if (lastSkipPos == nullptr) { - lastSkipPos = buffer; - } - } else { - if (lastSkipPos != nullptr) { - memmove(lastSkipPos, buffer, status.Information - totalOffset); - ULONG delta = static_cast<ULONG>(ush::AddrDiff(buffer, lastSkipPos)); - totalOffset -= delta; - - buffer = lastSkipPos; - lastSkipPos = nullptr; - } - lastValidRecord = buffer; - } - - if (offset == 0) { - offset = static_cast<ULONG>(status.Information) - totalOffset; - } - buffer = ush::AddrAdd(buffer, offset); - totalOffset += offset; - } - - if (lastSkipPos != nullptr) { - buffer = lastSkipPos; - bufferSize = static_cast<ULONG>(ush::AddrDiff(buffer, bufferInit)); - // null out the unused rest if there is some - memset(lastSkipPos, 0, status.Information - bufferSize); - } else { - bufferSize = static_cast<ULONG>(ush::AddrDiff(buffer, bufferInit)); - } - } - if (lastValidRecord != nullptr) { - SetFileInformationOffset(FileInformationClass, lastValidRecord, 0); - } - } - return res; -} - -DATA_ID(SearchInfo); - -struct Searches -{ - struct Info - { - struct VirtualMatch - { - // full path to where the file/directory actually is - std::wstring realPath; - // virtual filename (only filename since it has to be within the searched - // directory) - // this is left empty when a folder with all its content is mapped to the - // search directory - std::wstring virtualName; - }; - - Info() : currentSearchHandle(INVALID_HANDLE_VALUE) {} - std::set<std::wstring> foundFiles; - HANDLE currentSearchHandle; - std::queue<VirtualMatch> virtualMatches; - UnicodeString searchPattern; - bool regularComplete{false}; - }; - - Searches() = default; - - // must provide a special copy constructor because boost::mutex is - // non-copyable - Searches(const Searches& reference) : info(reference.info) {} - - Searches& operator=(const Searches&) = delete; - - std::recursive_mutex queryMutex; - - std::map<HANDLE, Info> info; -}; - -void gatherVirtualEntries(const UnicodeString& dirName, - const usvfs::RedirectionTreeContainer& redir, - PUNICODE_STRING FileName, Searches::Info& info) -{ - LPCWSTR dirNameW = static_cast<LPCWSTR>(dirName); - // fix directory name. I'd love to know why microsoft sometimes uses "\??\" vs - // "\\?\" - if ((wcsncmp(dirNameW, LR"(\\?\)", 4) == 0) || - (wcsncmp(dirNameW, LR"(\??\)", 4) == 0)) { - dirNameW += 4; - } - auto node = redir->findNode(boost::filesystem::path(dirNameW)); - if (node.get() != nullptr) { - std::string searchPattern = - FileName != nullptr - ? ush::string_cast<std::string>(FileName->Buffer, ush::CodePage::UTF8) - : "*.*"; - - boost::replace_all(searchPattern, "\"", "."); - - for (const auto& subNode : node->find(searchPattern)) { - if (((subNode->data().linkTarget.length() > 0) || subNode->isDirectory()) && - !subNode->hasFlag(usvfs::shared::FLAG_DUMMY)) { - std::wstring vName = - ush::string_cast<std::wstring>(subNode->name(), ush::CodePage::UTF8); - - Searches::Info::VirtualMatch m; - if (subNode->data().linkTarget.length() > 0) { - m = {ush::string_cast<std::wstring>(subNode->data().linkTarget.c_str(), - ush::CodePage::UTF8), - vName}; - } else { - m = {ush::string_cast<std::wstring>(subNode->path().c_str(), - ush::CodePage::UTF8), - vName}; - } - - info.virtualMatches.push(m); - info.foundFiles.insert(ush::to_upper(vName)); - } - } - } -} - -/** - * @brief insert a virtual entry into the search result - * @param FileInformation - * @param FileInformationClass - * @param info - * @param realPath path were the actual file resides - * @param virtualName virtual file name (without path). will often be the same - * as the name component of realpath - * @param ReturnSingleEntry - * @param dataRead - * @return true if a virtual result was added, false if the search handle in the - * info object yields no more results - */ -bool addVirtualSearchResult(PVOID& FileInformation, - FILE_INFORMATION_CLASS FileInformationClass, - Searches::Info& info, const std::wstring& realPath, - const std::wstring& virtualName, BOOLEAN ReturnSingleEntry, - ULONG& dataRead) -{ - // this opens a search in the real location, then copies the information about - // files we care about (the ones being mapped) to the result we intend to - // return - bfs::path fullPath(realPath); - if (fullPath.filename().wstring() == L".") { - fullPath = fullPath.parent_path(); - } - if (info.currentSearchHandle == INVALID_HANDLE_VALUE) { - std::wstring dirName = fullPath.parent_path().wstring(); - if (dirName.length() >= MAX_PATH && !ush::startswith(dirName.c_str(), LR"(\\?\)")) - dirName = LR"(\\?\)" + dirName; - info.currentSearchHandle = - CreateFileW(dirName.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, - nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); - } - std::wstring fileName = - ush::string_cast<std::wstring>(fullPath.filename().string(), ush::CodePage::UTF8); - NTSTATUS subRes = addNtSearchData( - info.currentSearchHandle, - (fileName != L".") ? static_cast<PUNICODE_STRING>(UnicodeString(fileName.c_str())) - : nullptr, - virtualName, FileInformationClass, FileInformation, dataRead, info.foundFiles, - nullptr, nullptr, nullptr, ReturnSingleEntry); - if (subRes == STATUS_SUCCESS) { - return true; - } else { - // STATUS_NO_MORE_FILES merely means the search ended, everything else is an - // error message. Either way, the search here is finished and we should - // resume in the next mapped directory - if (subRes != STATUS_NO_MORE_FILES) { - spdlog::get("hooks")->warn("error reported listing files in {0}: {1:x}", - fullPath.string(), static_cast<uint32_t>(subRes)); - } - return false; - } -} - -NTSTATUS WINAPI usvfs::hook_NtQueryDirectoryFile( - HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, - PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length, - FILE_INFORMATION_CLASS FileInformationClass, BOOLEAN ReturnSingleEntry, - PUNICODE_STRING FileName, BOOLEAN RestartScan) -{ - PreserveGetLastError ntFunctionsDoNotChangeGetLastError; - - // this is quite messy... - // first, this will gather the virtual locations mapping to the iterated one - // then we return results from the real location, skipping those that exist - // in the virtual locations, as those take precedence - // finally the virtual results are returned, adding each result to a skip - // list, so they don't get added twice - // - // if we don't add the regular files first, "." and ".." wouldn't be in the - // first search result of wildcard searches which may confuse the caller - NTSTATUS res = STATUS_NO_MORE_FILES; - HOOK_START_GROUP(MutExHookGroup::FIND_FILES) - if (!callContext.active()) { - return ::NtQueryDirectoryFile( - FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock, FileInformation, - Length, FileInformationClass, ReturnSingleEntry, FileName, RestartScan); - } - - // std::unique_lock<std::recursive_mutex> queryLock; - std::map<HANDLE, Searches::Info>::iterator infoIter; - bool firstSearch = false; - - { // scope to limit context lifetime - HookContext::ConstPtr context = READ_CONTEXT(); - Searches& activeSearches = context->customData<Searches>(SearchInfo); - // queryLock = std::unique_lock<std::recursive_mutex>(activeSearches.queryMutex); - - if (RestartScan) { - auto iter = activeSearches.info.find(FileHandle); - if (iter != activeSearches.info.end()) { - activeSearches.info.erase(iter); - } - } - - // see if we already have a running search - infoIter = activeSearches.info.find(FileHandle); - firstSearch = (infoIter == activeSearches.info.end()); - } - - if (firstSearch) { - HookContext::Ptr context = WRITE_CONTEXT(); - Searches& activeSearches = context->customData<Searches>(SearchInfo); - // tradeoff time: we store this search status even if no virtual results - // were found. This causes a little extra cost here and in NtClose every - // time a non-virtual dir is being searched. However if we don't, - // whenever NtQueryDirectoryFile is called another time on the same handle, - // this (expensive) block would be run again. - infoIter = - activeSearches.info.insert(std::make_pair(FileHandle, Searches::Info())).first; - infoIter->second.searchPattern.appendPath(FileName); - - SearchHandleMap& searchMap = context->customData<SearchHandleMap>(SearchHandles); - SearchHandleMap::iterator iter = searchMap.find(FileHandle); - - UnicodeString searchPath; - if (iter != searchMap.end()) { - searchPath = UnicodeString(iter->second.c_str()); - infoIter->second.currentSearchHandle = CreateFileW( - iter->second.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, - nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); - } else { - searchPath = ntdllHandleTracker.lookup(FileHandle); - } - gatherVirtualEntries(searchPath, context->redirectionTable(), FileName, - infoIter->second); - } - - ULONG dataRead = Length; - PVOID FileInformationCurrent = FileInformation; - - // add regular search results, skipping those files we have in a virtual - // location - bool moreRegular = !infoIter->second.regularComplete; - bool dataReturned = false; - while (moreRegular && !dataReturned) { - dataRead = Length; - - HANDLE handle = infoIter->second.currentSearchHandle; - if (handle == INVALID_HANDLE_VALUE) { - handle = FileHandle; - } - NTSTATUS subRes = addNtSearchData( - handle, FileName, L"", FileInformationClass, FileInformationCurrent, dataRead, - infoIter->second.foundFiles, Event, ApcRoutine, ApcContext, ReturnSingleEntry); - moreRegular = subRes == STATUS_SUCCESS; - if (moreRegular) { - dataReturned = dataRead != 0; - } else { - infoIter->second.regularComplete = true; - infoIter->second.foundFiles.clear(); - if (infoIter->second.currentSearchHandle != INVALID_HANDLE_VALUE) { - ::CloseHandle(infoIter->second.currentSearchHandle); - infoIter->second.currentSearchHandle = INVALID_HANDLE_VALUE; - } - } - } - if (!moreRegular) { - // add virtual results - while (!dataReturned && infoIter->second.virtualMatches.size() > 0) { - auto match = infoIter->second.virtualMatches.front(); - if (match.realPath.size() != 0) { - dataRead = Length; - if (addVirtualSearchResult(FileInformationCurrent, FileInformationClass, - infoIter->second, match.realPath, match.virtualName, - ReturnSingleEntry, dataRead)) { - // a positive result here means the call returned data and there may - // be further objects to be retrieved by repeating the call - dataReturned = true; - } else { - // proceed to next search handle - - // TODO: doesn't append search results from more than one redirection - // per call. This is bad for performance but otherwise we'd need to - // re-write the offsets between information objects - infoIter->second.virtualMatches.pop(); - CloseHandle(infoIter->second.currentSearchHandle); - infoIter->second.currentSearchHandle = INVALID_HANDLE_VALUE; - } - } - } - } - - if (!dataReturned) { - if (firstSearch) { - res = STATUS_NO_SUCH_FILE; - } else { - res = STATUS_NO_MORE_FILES; - } - } else { - res = STATUS_SUCCESS; - } - IoStatusBlock->Status = res; - IoStatusBlock->Information = dataRead; - - size_t numVirtualFiles = infoIter->second.virtualMatches.size(); - if ((numVirtualFiles > 0)) { - LOG_CALL() - .addParam("path", ntdllHandleTracker.lookup(FileHandle)) - .PARAM(FileInformationClass) - .PARAM(FileName) - .PARAM(numVirtualFiles) - .PARAMWRAP(res); - } - - HOOK_END - return res; -} - -NTSTATUS WINAPI usvfs::hook_NtQueryDirectoryFileEx( - HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, - PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length, - FILE_INFORMATION_CLASS FileInformationClass, ULONG QueryFlags, - PUNICODE_STRING FileName) -{ - PreserveGetLastError ntFunctionsDoNotChangeGetLastError; - NTSTATUS res = STATUS_NO_MORE_FILES; - - // this is quite messy... - // first, this will gather the virtual locations mapping to the iterated one - // then we return results from the real location, skipping those that exist - // in the virtual locations, as those take precedence - // finally the virtual results are returned, adding each result to a skip - // list, so they don't get added twice - // - // if we don't add the regular files first, "." and ".." wouldn't be in the - // first search result of wildcard searches which may confuse the caller - HOOK_START_GROUP(MutExHookGroup::FIND_FILES) - if (!callContext.active()) { - return ::NtQueryDirectoryFileEx(FileHandle, Event, ApcRoutine, ApcContext, - IoStatusBlock, FileInformation, Length, - FileInformationClass, QueryFlags, FileName); - } - - // std::unique_lock<std::recursive_mutex> queryLock; - std::map<HANDLE, Searches::Info>::iterator infoIter; - bool firstSearch = false; - - { // scope to limit context lifetime - HookContext::ConstPtr context = READ_CONTEXT(); - Searches& activeSearches = context->customData<Searches>(SearchInfo); - // queryLock = std::unique_lock<std::recursive_mutex>(activeSearches.queryMutex); - - if (QueryFlags & SL_RESTART_SCAN) { - auto iter = activeSearches.info.find(FileHandle); - if (iter != activeSearches.info.end()) { - activeSearches.info.erase(iter); - } - } - - // see if we already have a running search - infoIter = activeSearches.info.find(FileHandle); - firstSearch = (infoIter == activeSearches.info.end()); - } - - if (firstSearch) { - HookContext::Ptr context = WRITE_CONTEXT(); - Searches& activeSearches = context->customData<Searches>(SearchInfo); - // tradeoff time: we store this search status even if no virtual results - // were found. This causes a little extra cost here and in NtClose every - // time a non-virtual dir is being searched. However if we don't, - // whenever NtQueryDirectoryFile is called another time on the same handle, - // this (expensive) block would be run again. - infoIter = - activeSearches.info.insert(std::make_pair(FileHandle, Searches::Info())).first; - infoIter->second.searchPattern.appendPath(FileName); - - SearchHandleMap& searchMap = context->customData<SearchHandleMap>(SearchHandles); - SearchHandleMap::iterator iter = searchMap.find(FileHandle); - - UnicodeString searchPath; - if (iter != searchMap.end()) { - searchPath = UnicodeString(iter->second.c_str()); - infoIter->second.currentSearchHandle = CreateFileW( - iter->second.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, - nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); - } else { - searchPath = ntdllHandleTracker.lookup(FileHandle); - } - gatherVirtualEntries(searchPath, context->redirectionTable(), FileName, - infoIter->second); - } - - ULONG dataRead = Length; - PVOID FileInformationCurrent = FileInformation; - - // add regular search results, skipping those files we have in a virtual - // location - bool moreRegular = !infoIter->second.regularComplete; - bool dataReturned = false; - while (moreRegular && !dataReturned) { - dataRead = Length; - - HANDLE handle = infoIter->second.currentSearchHandle; - if (handle == INVALID_HANDLE_VALUE) { - handle = FileHandle; - } - NTSTATUS subRes = addNtSearchData(handle, FileName, L"", FileInformationClass, - FileInformationCurrent, dataRead, - infoIter->second.foundFiles, Event, ApcRoutine, - ApcContext, QueryFlags & SL_RETURN_SINGLE_ENTRY); - moreRegular = subRes == STATUS_SUCCESS; - if (moreRegular) { - dataReturned = dataRead != 0; - } else { - infoIter->second.regularComplete = true; - infoIter->second.foundFiles.clear(); - if (infoIter->second.currentSearchHandle != INVALID_HANDLE_VALUE) { - ::CloseHandle(infoIter->second.currentSearchHandle); - infoIter->second.currentSearchHandle = INVALID_HANDLE_VALUE; - } - } - } - if (!moreRegular) { - // add virtual results - while (!dataReturned && infoIter->second.virtualMatches.size() > 0) { - auto match = infoIter->second.virtualMatches.front(); - if (match.realPath.size() != 0) { - dataRead = Length; - if (addVirtualSearchResult(FileInformationCurrent, FileInformationClass, - infoIter->second, match.realPath, match.virtualName, - QueryFlags & SL_RETURN_SINGLE_ENTRY, dataRead)) { - // a positive result here means the call returned data and there may - // be further objects to be retrieved by repeating the call - dataReturned = true; - } else { - // proceed to next search handle - - // TODO: doesn't append search results from more than one redirection - // per call. This is bad for performance but otherwise we'd need to - // re-write the offsets between information objects - infoIter->second.virtualMatches.pop(); - CloseHandle(infoIter->second.currentSearchHandle); - infoIter->second.currentSearchHandle = INVALID_HANDLE_VALUE; - } - } - } - } - - if (!dataReturned) { - if (firstSearch) { - res = STATUS_NO_SUCH_FILE; - } else { - res = STATUS_NO_MORE_FILES; - } - } else { - res = STATUS_SUCCESS; - } - IoStatusBlock->Status = res; - IoStatusBlock->Information = dataRead; - - size_t numVirtualFiles = infoIter->second.virtualMatches.size(); - if ((numVirtualFiles > 0)) { - LOG_CALL() - .addParam("path", ntdllHandleTracker.lookup(FileHandle)) - .PARAM(FileInformationClass) - .PARAM(FileName) - .PARAM(QueryFlags) - .PARAM(numVirtualFiles) - .PARAMWRAP(res); - } - - HOOK_END - return res; -} - -DLLEXPORT NTSTATUS WINAPI usvfs::hook_NtQueryObject( - HANDLE Handle, OBJECT_INFORMATION_CLASS ObjectInformationClass, - PVOID ObjectInformation, ULONG ObjectInformationLength, PULONG ReturnLength) -{ - NTSTATUS res = STATUS_SUCCESS; - - HOOK_START_GROUP(MutExHookGroup::FILE_ATTRIBUTES) - if (!callContext.active()) { - return ::NtQueryObject(Handle, ObjectInformationClass, ObjectInformation, - ObjectInformationLength, ReturnLength); - } - - PRE_REALCALL - res = ::NtQueryObject(Handle, ObjectInformationClass, ObjectInformation, - ObjectInformationLength, ReturnLength); - POST_REALCALL - - // we handle both SUCCESS and BUFFER_OVERFLOW since the fixed name might be - // smaller than the original one - // - // we only handle STATUS_INFO_LENGTH_MISMATCH if ReturnLength is not NULL since - // this is only returned if the length is too small to hold the structure itself - // (regardless of the name), in which case, we need to compute our own ReturnLength - // - if ((res == STATUS_SUCCESS || res == STATUS_BUFFER_OVERFLOW || - (res == STATUS_INFO_LENGTH_MISMATCH && ReturnLength)) && - (ObjectInformationClass == ObjectNameInformation)) { - const auto trackerInfo = ntdllHandleTracker.lookup(Handle); - const auto redir = applyReroute(READ_CONTEXT(), callContext, trackerInfo); - - OBJECT_NAME_INFORMATION* info = - reinterpret_cast<OBJECT_NAME_INFORMATION*>(ObjectInformation); - - if (redir.redirected) { - // https://learn.microsoft.com/en-us/windows/win32/fileio/displaying-volume-paths - // - - // TODO: is that always true? - // path should start with \??\X: - we need to replace this by device name - // - WCHAR deviceName[MAX_PATH]; - std::wstring buffer(static_cast<LPCWSTR>(trackerInfo)); - buffer[6] = L'\0'; - - QueryDosDeviceW(buffer.data() + 4, deviceName, ARRAYSIZE(deviceName)); - - buffer = std::wstring(deviceName) + L'\\' + - std::wstring(buffer.data() + 7, buffer.size() - 7); - - // the name is put in the buffer AFTER the struct, so the required size if - // sizeof(OBJECT_NAME_INFORMATION) + the number of bytes for the name + 2 bytes - // for a wide null character - const auto requiredLength = - sizeof(OBJECT_NAME_INFORMATION) + buffer.size() * 2 + 2; - if (ObjectInformationLength < requiredLength) { - // if the status was info length mismatch, we keep it, we are just going to - // update *ReturnLength - if (res != STATUS_INFO_LENGTH_MISMATCH) { - res = STATUS_BUFFER_OVERFLOW; - } - - if (ReturnLength) { - *ReturnLength = static_cast<ULONG>(requiredLength); - } - } else { - // put the unicode buffer at the end of the object - const USHORT unicodeBufferLength = static_cast<USHORT>(std::min( - static_cast<unsigned long long>(std::numeric_limits<USHORT>::max()), - static_cast<unsigned long long>(ObjectInformationLength - - sizeof(OBJECT_NAME_INFORMATION)))); - LPWSTR unicodeBuffer = reinterpret_cast<LPWSTR>( - static_cast<LPSTR>(ObjectInformation) + sizeof(OBJECT_NAME_INFORMATION)); - - // copy the path into the buffer - wmemcpy_s(unicodeBuffer, unicodeBufferLength, buffer.data(), buffer.size()); - - // set the null character - unicodeBuffer[buffer.size()] = L'\0'; - - // update the actual unicode string - info->Name.Buffer = unicodeBuffer; - info->Name.Length = static_cast<USHORT>(buffer.size() * 2); - info->Name.MaximumLength = unicodeBufferLength; - - res = STATUS_SUCCESS; - } - } - - auto logger = LOG_CALL() - .PARAMWRAP(res) - .PARAM(ObjectInformationLength) - .addParam("return_length", ReturnLength ? *ReturnLength : -1) - .addParam("tracker_path", trackerInfo) - .PARAM(ObjectInformationClass) - .PARAM(redir.redirected) - .PARAM(redir.path); - - if (res == STATUS_SUCCESS) { - logger.addParam("name_info", info->Name); - } else { - logger.addParam("name_info", ""); - } - } - - HOOK_END - return res; -} - -DLLEXPORT NTSTATUS WINAPI usvfs::hook_NtQueryInformationFile( - HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, - ULONG Length, FILE_INFORMATION_CLASS FileInformationClass) -{ - NTSTATUS res = STATUS_SUCCESS; - - HOOK_START_GROUP(MutExHookGroup::FILE_ATTRIBUTES) - if (!callContext.active()) { - return ::NtQueryInformationFile(FileHandle, IoStatusBlock, FileInformation, Length, - FileInformationClass); - } - - PRE_REALCALL - res = ::NtQueryInformationFile(FileHandle, IoStatusBlock, FileInformation, Length, - FileInformationClass); - POST_REALCALL - - // we handle both SUCCESS and BUFFER_OVERFLOW since the fixed name might be - // smaller than the original one - // - // we do not handle STATUS_INFO_LENGTH_MISMATCH because this is only returned if - // the length is too small to old the structure itself (regardless of the name) - // - // TODO: currently, this does not handle STATUS_BUFFER_OVERLOW for ALL information - // because most of the structures would need to be manually filled, which is very - // complicated - this should not pose huge issue - // - if (((res == STATUS_SUCCESS || res == STATUS_BUFFER_OVERFLOW) && - (FileInformationClass == FileNameInformation || - FileInformationClass == FileNormalizedNameInformation)) || - (res == STATUS_SUCCESS && FileInformationClass == FileAllInformation)) { - - const auto trackerInfo = ntdllHandleTracker.lookup(FileHandle); - const auto redir = applyReroute(READ_CONTEXT(), callContext, trackerInfo); - - // TODO: difference between FileNameInformation and FileNormalizedNameInformation - - // maximum length in bytes - the required length is - // - for ALL, 100 + the number of bytes in the name (not account for null character) - // - for NAME, 4 + the number of bytes in the name (not accounting for null - // character) - // - // it is close to the sizeof the structure + the number of bytes in the name, except - // for the alignment that gives us a bit more space - // - ULONG prefixStructLength; - FILE_NAME_INFORMATION* info; - if (FileInformationClass == FileAllInformation) { - info = &reinterpret_cast<FILE_ALL_INFORMATION*>(FileInformation)->NameInformation; - prefixStructLength = sizeof(FILE_ALL_INFORMATION) - 4; - } else { - info = reinterpret_cast<FILE_NAME_INFORMATION*>(FileInformation); - prefixStructLength = sizeof(FILE_NAME_INFORMATION) - 4; - } - - if (redir.redirected) { - auto requiredLength = prefixStructLength + 2 * (trackerInfo.size() - 6); - if (Length < requiredLength) { - res = STATUS_BUFFER_OVERFLOW; - } else { - // strip the \??\X: prefix (X being the drive name) - LPCWSTR filenameFixed = static_cast<LPCWSTR>(trackerInfo) + 6; - - // not using SetInfoFilename because the length is not set and we do not need to - // 0-out the memory here - info->FileNameLength = static_cast<ULONG>((trackerInfo.size() - 6) * 2); - wmemcpy(info->FileName, filenameFixed, trackerInfo.size() - 6); - res = STATUS_SUCCESS; - - // update status block, Information is the number of bytes written, basically - // the required length - IoStatusBlock->Status = STATUS_SUCCESS; - IoStatusBlock->Information = static_cast<ULONG_PTR>(requiredLength); - } - } - - LOG_CALL() - .PARAMWRAP(res) - .addParam("tracker_path", trackerInfo) - .PARAM(FileInformationClass) - .PARAM(redir.redirected) - .PARAM(redir.path) - .addParam("name_info", res == STATUS_SUCCESS - ? std::wstring{info->FileName, - info->FileNameLength / sizeof(WCHAR)} - : std::wstring{}); - } - - HOOK_END - return res; -} - -unique_ptr_deleter<OBJECT_ATTRIBUTES> -makeObjectAttributes(RedirectionInfo& redirInfo, POBJECT_ATTRIBUTES attributeTemplate) -{ - if (redirInfo.redirected) { - unique_ptr_deleter<OBJECT_ATTRIBUTES> result(new OBJECT_ATTRIBUTES, - [](OBJECT_ATTRIBUTES* ptr) { - delete ptr; - }); - memcpy(result.get(), attributeTemplate, sizeof(OBJECT_ATTRIBUTES)); - result->RootDirectory = nullptr; - result->ObjectName = static_cast<PUNICODE_STRING>(redirInfo.path); - return result; - } else { - // just reuse the template with a dummy deleter - return unique_ptr_deleter<OBJECT_ATTRIBUTES>(attributeTemplate, - [](OBJECT_ATTRIBUTES*) {}); - } -} - -DLLEXPORT NTSTATUS WINAPI usvfs::hook_NtQueryInformationByName( - POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, - PVOID FileInformation, ULONG Length, FILE_INFORMATION_CLASS FileInformationClass) -{ - NTSTATUS res = STATUS_SUCCESS; - - HOOK_START_GROUP(MutExHookGroup::FILE_ATTRIBUTES) - - if (!callContext.active()) { - res = ::NtQueryInformationByName(ObjectAttributes, IoStatusBlock, FileInformation, - Length, FileInformationClass); - callContext.updateLastError(); - return res; - } - - RedirectionInfo redir = - applyReroute(READ_CONTEXT(), callContext, CreateUnicodeString(ObjectAttributes)); - - if (redir.redirected) { - auto newObjectAttributes = makeObjectAttributes(redir, ObjectAttributes); - - PRE_REALCALL - res = ::NtQueryInformationByName(newObjectAttributes.get(), IoStatusBlock, - FileInformation, Length, FileInformationClass); - POST_REALCALL - - LOG_CALL() - .PARAMWRAP(res) - .addParam("input_path", *ObjectAttributes->ObjectName) - .addParam("reroute_path", redir.path); - } else { - PRE_REALCALL - res = ::NtQueryInformationByName(ObjectAttributes, IoStatusBlock, FileInformation, - Length, FileInformationClass); - POST_REALCALL - } - - HOOK_END - return res; -} - -NTSTATUS ntdll_mess_NtOpenFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, - POBJECT_ATTRIBUTES ObjectAttributes, - PIO_STATUS_BLOCK IoStatusBlock, ULONG ShareAccess, - ULONG OpenOptions) -{ - using namespace usvfs; - - PreserveGetLastError ntFunctionsDoNotChangeGetLastError; - - NTSTATUS res = STATUS_NO_SUCH_FILE; - - HOOK_START_GROUP(MutExHookGroup::OPEN_FILE) - // Why is the usual if (!callContext.active()... check missing? - - bool storePath = false; - if (((OpenOptions & FILE_DIRECTORY_FILE) != 0UL) && - ((OpenOptions & FILE_OPEN_FOR_BACKUP_INTENT) != 0UL)) { - // this may be an attempt to open a directory handle for iterating. - // If so we need to treat it a little bit differently - /* usvfs::FunctionGroupLock lock(usvfs::MutExHookGroup::FILE_ATTRIBUTES); - FILE_BASIC_INFORMATION dummy; - storePath = FAILED(NtQueryAttributesFile(ObjectAttributes, &dummy));*/ - storePath = true; - } - - UnicodeString fullName = CreateUnicodeString(ObjectAttributes); - - if (isDeviceFile(static_cast<LPCWSTR>(fullName))) { - return ::NtOpenFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, - ShareAccess, OpenOptions); - } - - UnicodeString Path = ntdllHandleTracker.lookup(ObjectAttributes->RootDirectory); - - std::wstring checkpath = - ush::string_cast<std::wstring>(static_cast<LPCWSTR>(Path), ush::CodePage::UTF8); - - if ((fullName.size() == 0) || - (GetFileSize(ObjectAttributes->RootDirectory, nullptr) != INVALID_FILE_SIZE)) { - // //relative paths that we don't have permission over will fail here due that we - // can't get the filesize of the root directory - // //We should try again to see if it is a directory using another method - if ((fullName.size() == 0) || - (GetFileAttributesW((LPCWSTR)checkpath.c_str()) == INVALID_FILE_ATTRIBUTES)) { - return ::NtOpenFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, - ShareAccess, OpenOptions); - } - } - - try { - RedirectionInfo redir = applyReroute(READ_CONTEXT(), callContext, fullName); - unique_ptr_deleter<OBJECT_ATTRIBUTES> adjustedAttributes = - makeObjectAttributes(redir, ObjectAttributes); - - PRE_REALCALL - res = ::NtOpenFile(FileHandle, DesiredAccess, adjustedAttributes.get(), - IoStatusBlock, ShareAccess, OpenOptions); - POST_REALCALL - if (SUCCEEDED(res) && storePath) { - // store the original search path for use during iteration - READ_CONTEXT()->customData<SearchHandleMap>(SearchHandles)[*FileHandle] = - static_cast<LPCWSTR>(fullName); -#pragma message("need to clean up this handle in CloseHandle call") - } - - if (redir.redirected) { - LOG_CALL() - .addParam("source", ObjectAttributes) - .addParam("rerouted", adjustedAttributes.get()) - .PARAM(*FileHandle) - .PARAM(OpenOptions) - .PARAMWRAP(res); - } - } catch (const std::exception&) { - PRE_REALCALL - res = ::NtOpenFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, - ShareAccess, OpenOptions); - POST_REALCALL - } - HOOK_END - - return res; -} - -NTSTATUS WINAPI usvfs::hook_NtOpenFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, - POBJECT_ATTRIBUTES ObjectAttributes, - PIO_STATUS_BLOCK IoStatusBlock, - ULONG ShareAccess, ULONG OpenOptions) -{ - NTSTATUS res = ntdll_mess_NtOpenFile(FileHandle, DesiredAccess, ObjectAttributes, - IoStatusBlock, ShareAccess, OpenOptions); - if (res >= 0 && ObjectAttributes && FileHandle && - GetFileType(*FileHandle) == FILE_TYPE_DISK) - ntdllHandleTracker.insert(*FileHandle, CreateUnicodeString(ObjectAttributes)); - return res; -} - -NTSTATUS ntdll_mess_NtCreateFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, - POBJECT_ATTRIBUTES ObjectAttributes, - PIO_STATUS_BLOCK IoStatusBlock, - PLARGE_INTEGER AllocationSize, ULONG FileAttributes, - ULONG ShareAccess, ULONG CreateDisposition, - ULONG CreateOptions, PVOID EaBuffer, ULONG EaLength) -{ - using namespace usvfs; - - NTSTATUS res = STATUS_NO_SUCH_FILE; - - PreserveGetLastError ntFunctionsDoNotChangeGetLastError; - - HOOK_START_GROUP(MutExHookGroup::OPEN_FILE) - if (!callContext.active()) { - return ::NtCreateFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, - AllocationSize, FileAttributes, ShareAccess, - CreateDisposition, CreateOptions, EaBuffer, EaLength); - } - - UnicodeString inPath = CreateUnicodeString(ObjectAttributes); - LPCWSTR inPathW = static_cast<LPCWSTR>(inPath); - - if (inPath.size() == 0) { - spdlog::get("hooks")->info( - "failed to set from handle: {0}", - ush::string_cast<std::string>(ObjectAttributes->ObjectName->Buffer)); - return ::NtCreateFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, - AllocationSize, FileAttributes, ShareAccess, - CreateDisposition, CreateOptions, EaBuffer, EaLength); - } - - if (isDeviceFile(inPathW)) { - return ::NtCreateFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, - AllocationSize, FileAttributes, ShareAccess, - CreateDisposition, CreateOptions, EaBuffer, EaLength); - } - - DWORD convertedDisposition = OPEN_EXISTING; - switch (CreateDisposition) { - case FILE_SUPERSEDE: - convertedDisposition = CREATE_ALWAYS; - break; - case FILE_OPEN: - convertedDisposition = OPEN_EXISTING; - break; - case FILE_CREATE: - convertedDisposition = CREATE_NEW; - break; - case FILE_OPEN_IF: - convertedDisposition = OPEN_ALWAYS; - break; - case FILE_OVERWRITE: - convertedDisposition = TRUNCATE_EXISTING; - break; - case FILE_OVERWRITE_IF: - convertedDisposition = CREATE_ALWAYS; - break; - default: - spdlog::get("hooks")->error("invalid disposition: {0}", CreateDisposition); - break; - } - - DWORD convertedAccess = 0; - if ((DesiredAccess & FILE_GENERIC_READ) == FILE_GENERIC_READ) - convertedAccess |= GENERIC_READ; - if ((DesiredAccess & FILE_GENERIC_WRITE) == FILE_GENERIC_WRITE) - convertedAccess |= GENERIC_WRITE; - if ((DesiredAccess & FILE_GENERIC_EXECUTE) == FILE_GENERIC_EXECUTE) - convertedAccess |= GENERIC_EXECUTE; - if ((DesiredAccess & FILE_ALL_ACCESS) == FILE_ALL_ACCESS) - convertedAccess |= GENERIC_ALL; - - ULONG originalDisposition = CreateDisposition; - CreateRerouter rerouter; - if (rerouter.rerouteCreate( - READ_CONTEXT(), callContext, inPathW, convertedDisposition, convertedAccess, - (LPSECURITY_ATTRIBUTES)ObjectAttributes->SecurityDescriptor)) { - switch (convertedDisposition) { - case CREATE_NEW: - CreateDisposition = FILE_CREATE; - break; - case CREATE_ALWAYS: - if (CreateDisposition != FILE_SUPERSEDE) - CreateDisposition = FILE_OVERWRITE_IF; - break; - case OPEN_EXISTING: - CreateDisposition = FILE_OPEN; - break; - case OPEN_ALWAYS: - CreateDisposition = FILE_OPEN_IF; - break; - case TRUNCATE_EXISTING: - CreateDisposition = FILE_OVERWRITE; - break; - } - - RedirectionInfo redir = applyReroute(rerouter); - - unique_ptr_deleter<OBJECT_ATTRIBUTES> adjustedAttributes = - makeObjectAttributes(redir, ObjectAttributes); - - PRE_REALCALL - res = ::NtCreateFile(FileHandle, DesiredAccess, adjustedAttributes.get(), - IoStatusBlock, AllocationSize, FileAttributes, ShareAccess, - CreateDisposition, CreateOptions, EaBuffer, EaLength); - POST_REALCALL - rerouter.updateResult(callContext, res == STATUS_SUCCESS); - - if (res == STATUS_SUCCESS) { - if (rerouter.newReroute()) - rerouter.insertMapping(WRITE_CONTEXT()); - - if (rerouter.isDir() && rerouter.wasRerouted() && - ((FileAttributes & FILE_OPEN_FOR_BACKUP_INTENT) == - FILE_OPEN_FOR_BACKUP_INTENT)) { - // store the original search path for use during iteration - WRITE_CONTEXT()->customData<SearchHandleMap>(SearchHandles)[*FileHandle] = - inPathW; - } - } - - if (rerouter.wasRerouted() || rerouter.changedError() || - originalDisposition != CreateDisposition) { - LOG_CALL() - .PARAM(inPathW) - .PARAM(rerouter.fileName()) - .PARAMHEX(DesiredAccess) - .PARAMHEX(originalDisposition) - .PARAMHEX(CreateDisposition) - .PARAMHEX(FileAttributes) - .PARAMHEX(res) - .PARAMHEX(*FileHandle) - .PARAM(rerouter.originalError()) - .PARAM(rerouter.error()); - } - } else { - // make the original call to set up the proper errors and return statuses - PRE_REALCALL - res = ::NtCreateFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, - AllocationSize, FileAttributes, ShareAccess, CreateDisposition, - CreateOptions, EaBuffer, EaLength); - POST_REALCALL - } - - HOOK_END - - return res; -} - -NTSTATUS WINAPI usvfs::hook_NtCreateFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, - POBJECT_ATTRIBUTES ObjectAttributes, - PIO_STATUS_BLOCK IoStatusBlock, - PLARGE_INTEGER AllocationSize, - ULONG FileAttributes, ULONG ShareAccess, - ULONG CreateDisposition, ULONG CreateOptions, - PVOID EaBuffer, ULONG EaLength) -{ - NTSTATUS res = ntdll_mess_NtCreateFile(FileHandle, DesiredAccess, ObjectAttributes, - IoStatusBlock, AllocationSize, FileAttributes, - ShareAccess, CreateDisposition, CreateOptions, - EaBuffer, EaLength); - if (res >= 0 && ObjectAttributes && FileHandle && - GetFileType(*FileHandle) == FILE_TYPE_DISK) - ntdllHandleTracker.insert(*FileHandle, CreateUnicodeString(ObjectAttributes)); - return res; -} - -NTSTATUS WINAPI usvfs::hook_NtClose(HANDLE Handle) -{ - PreserveGetLastError ntFunctionsDoNotChangeGetLastError; - - NTSTATUS res = STATUS_NO_SUCH_FILE; - - HOOK_START_GROUP(MutExHookGroup::ALL_GROUPS) - bool log = false; - - if ((::GetFileType(Handle) == FILE_TYPE_DISK)) { - HookContext::Ptr context = WRITE_CONTEXT(); - - { // clean up search data associated with this handle part 1 - Searches& activeSearches = context->customData<Searches>(SearchInfo); - // std::lock_guard<std::recursive_mutex> lock(activeSearches.queryMutex); - auto iter = activeSearches.info.find(Handle); - if (iter != activeSearches.info.end()) { - if (iter->second.currentSearchHandle != INVALID_HANDLE_VALUE) { - ::CloseHandle(iter->second.currentSearchHandle); - } - - activeSearches.info.erase(iter); - log = true; - } - } - - { - SearchHandleMap& searchHandles = - context->customData<SearchHandleMap>(SearchHandles); - auto iter = searchHandles.find(Handle); - if (iter != searchHandles.end()) { - searchHandles.erase(iter); - log = true; - } - } - } - - if (GetFileType(Handle) == FILE_TYPE_DISK) - ntdllHandleTracker.erase(Handle); - - PRE_REALCALL - res = ::NtClose(Handle); - POST_REALCALL - - if (log) { - LOG_CALL().PARAM(Handle).PARAMWRAP(res); - } - - HOOK_END - - return res; -} - -NTSTATUS WINAPI usvfs::hook_NtQueryAttributesFile( - POBJECT_ATTRIBUTES ObjectAttributes, PFILE_BASIC_INFORMATION FileInformation) -{ - PreserveGetLastError ntFunctionsDoNotChangeGetLastError; - - NTSTATUS res = STATUS_SUCCESS; - - HOOK_START_GROUP(MutExHookGroup::FILE_ATTRIBUTES) - // Why is the usual if (!callContext.active()... check missing? - - UnicodeString inPath = CreateUnicodeString(ObjectAttributes); - - RedirectionInfo redir = applyReroute(READ_CONTEXT(), callContext, inPath); - unique_ptr_deleter<OBJECT_ATTRIBUTES> adjustedAttributes = - makeObjectAttributes(redir, ObjectAttributes); - - PRE_REALCALL - res = ::NtQueryAttributesFile(adjustedAttributes.get(), FileInformation); - POST_REALCALL - - if (redir.redirected) { - LOG_CALL() - .addParam("source", ObjectAttributes) - .addParam("rerouted", adjustedAttributes.get()) - .PARAMWRAP(res); - } - - HOOK_END - - return res; -} - -NTSTATUS WINAPI usvfs::hook_NtQueryFullAttributesFile( - POBJECT_ATTRIBUTES ObjectAttributes, PFILE_NETWORK_OPEN_INFORMATION FileInformation) -{ - PreserveGetLastError ntFunctionsDoNotChangeGetLastError; - - NTSTATUS res = STATUS_SUCCESS; - - HOOK_START_GROUP(MutExHookGroup::FILE_ATTRIBUTES) - - if (!callContext.active()) { - return ::NtQueryFullAttributesFile(ObjectAttributes, FileInformation); - } - - UnicodeString inPath; - try { - inPath = CreateUnicodeString(ObjectAttributes); - } catch (const std::exception&) { - return ::NtQueryFullAttributesFile(ObjectAttributes, FileInformation); - } - - RedirectionInfo redir = applyReroute(READ_CONTEXT(), callContext, inPath); - unique_ptr_deleter<OBJECT_ATTRIBUTES> adjustedAttributes = - makeObjectAttributes(redir, ObjectAttributes); - - PRE_REALCALL - res = ::NtQueryFullAttributesFile(adjustedAttributes.get(), FileInformation); - POST_REALCALL - - if (redir.redirected) { - LOG_CALL() - .addParam("source", ObjectAttributes) - .addParam("rerouted", adjustedAttributes.get()) - .PARAMWRAP(res); - } - - HOOK_END - - return res; -} - -NTSTATUS WINAPI usvfs::hook_NtTerminateProcess(HANDLE ProcessHandle, - NTSTATUS ExitStatus) -{ - // this hook is normally called when terminating another process, in which - // case there's nothing to do - // - // if the current process exits normally, the ExitProcess() hook is called - // and disconnects from the vfs; if the current process crashes, this hook - // is not called, the process just dies - // - // but a process can also terminate itself, bypassing ExitProcess(), and - // ending up here, in which case the vfs should be disconnected - // - // NtTerminateProcess() can be called two different ways to terminate the - // current process: - // - with a valid handle for the current process, or - // - with -1, because that's what GetCurrentProcess() returns - // - // it's unclear what a NULL handle represents, the behaviour is not - // documented anywhere, but looking at ReactOS, it's also interpreted as the - // current process - - NTSTATUS res = STATUS_SUCCESS; - - HOOK_START - - const bool isCurrentProcess = ProcessHandle == (HANDLE)-1 || ProcessHandle == 0 || - GetProcessId(ProcessHandle) == GetCurrentProcessId(); - - if (isCurrentProcess) { - usvfsDisconnectVFS(); - } - - res = ::NtTerminateProcess(ProcessHandle, ExitStatus); - - HOOK_END - - return res; -} diff --git a/libs/usvfs/src/usvfs_dll/hooks/ntdll.h b/libs/usvfs/src/usvfs_dll/hooks/ntdll.h deleted file mode 100644 index f3db4a6..0000000 --- a/libs/usvfs/src/usvfs_dll/hooks/ntdll.h +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#include <dllimport.h> -#include <loghelpers.h> -#include <ntdll_declarations.h> - -namespace usvfs -{ - -DLLEXPORT NTSTATUS WINAPI -hook_NtQueryFullAttributesFile(POBJECT_ATTRIBUTES ObjectAttributes, - PFILE_NETWORK_OPEN_INFORMATION FileInformation); - -DLLEXPORT NTSTATUS WINAPI hook_NtQueryAttributesFile( - POBJECT_ATTRIBUTES ObjectAttributes, PFILE_BASIC_INFORMATION FileInformation); - -DLLEXPORT NTSTATUS WINAPI hook_NtQueryDirectoryFile( - HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, - PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length, - FILE_INFORMATION_CLASS FileInformationClass, BOOLEAN ReturnSingleEntry, - PUNICODE_STRING FileName, BOOLEAN RestartScan); - -DLLEXPORT NTSTATUS WINAPI hook_NtQueryDirectoryFileEx( - HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, - PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length, - FILE_INFORMATION_CLASS FileInformationClass, ULONG QueryFlags, - PUNICODE_STRING FileName); - -DLLEXPORT NTSTATUS WINAPI hook_NtQueryObject( - HANDLE Handle, OBJECT_INFORMATION_CLASS ObjectInformationClass, - PVOID ObjectInformation, ULONG ObjectInformationLength, PULONG ReturnLength); - -DLLEXPORT NTSTATUS WINAPI hook_NtQueryInformationFile( - HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, - ULONG Length, FILE_INFORMATION_CLASS FileInformationClass); - -DLLEXPORT NTSTATUS WINAPI hook_NtQueryInformationByName( - POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, - PVOID FileInformation, ULONG Length, FILE_INFORMATION_CLASS FileInformationClass); - -DLLEXPORT NTSTATUS WINAPI hook_NtOpenFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, - POBJECT_ATTRIBUTES ObjectAttributes, - PIO_STATUS_BLOCK IoStatusBlock, - ULONG ShareAccess, ULONG OpenOptions); - -DLLEXPORT NTSTATUS WINAPI hook_NtCreateFile( - PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, - PIO_STATUS_BLOCK IoStatusBlock, PLARGE_INTEGER AllocationSize, ULONG FileAttributes, - ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer, - ULONG EaLength); - -DLLEXPORT NTSTATUS WINAPI hook_NtClose(HANDLE Handle); - -DLLEXPORT NTSTATUS WINAPI hook_NtTerminateProcess(HANDLE ProcessHandle, - NTSTATUS ExitStatus); - -} // namespace usvfs diff --git a/libs/usvfs/src/usvfs_dll/hooks/sharedids.h b/libs/usvfs/src/usvfs_dll/hooks/sharedids.h deleted file mode 100644 index 9a7531c..0000000 --- a/libs/usvfs/src/usvfs_dll/hooks/sharedids.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include "../hookcontext.h" - -typedef std::map<HANDLE, std::wstring> SearchHandleMap; - -// maps handles opened for searching to the original search path, which is -// necessary if the handle creation was rerouted -DATA_ID(SearchHandles); |
