diff options
Diffstat (limited to 'zen/FindFilePlus/find_file_plus.cpp')
-rw-r--r-- | zen/FindFilePlus/find_file_plus.cpp | 117 |
1 files changed, 83 insertions, 34 deletions
diff --git a/zen/FindFilePlus/find_file_plus.cpp b/zen/FindFilePlus/find_file_plus.cpp index ec56b0bc..fdabc46f 100644 --- a/zen/FindFilePlus/find_file_plus.cpp +++ b/zen/FindFilePlus/find_file_plus.cpp @@ -8,9 +8,7 @@ #include "init_dll_binding.h" //#include <windows.h> //these two don't play nice with each other #include "load_dll.h" -#include <zen/scope_guard.h> #include <new> -//#include <cstdio> using namespace dll; using namespace findplus; @@ -56,15 +54,18 @@ typedef struct _RTL_RELATIVE_NAME_U PVOID /*PRTLP_CURDIR_REF*/ CurDirRef; } RTL_RELATIVE_NAME_U, *PRTL_RELATIVE_NAME_U; -typedef BOOLEAN (NTAPI* RtlDosPathNameToNtPathName_UFunc)(PCWSTR, //__in dosFileName, - PUNICODE_STRING, //__out ntFileName, - PCWSTR*, //__out_optFilePart, - PRTL_RELATIVE_NAME_U); //__out_opt relativeName +typedef BOOLEAN (NTAPI* RtlDosPathNameToNtPathName_UFunc)(PCWSTR dosFileName, //__in + PUNICODE_STRING ntFileName, //__out + PCWSTR* filePart, //__out + PRTL_RELATIVE_NAME_U relativeName); //__out -typedef BOOLEAN (NTAPI* RtlDosPathNameToRelativeNtPathName_UFunc)(PCWSTR, //__in dosFileName, - PUNICODE_STRING, //__out ntFileName, - PCWSTR*, //__out_optFilePart, - PRTL_RELATIVE_NAME_U); //__out_opt relativeName +typedef BOOLEAN (NTAPI* RtlDosPathNameToRelativeNtPathName_UFunc)(PCWSTR dosFileName, //__in + PUNICODE_STRING ntFileName, //__out + PCWSTR* filePart, //__out + PRTL_RELATIVE_NAME_U relativeName); //__out + +typedef BOOLEAN (NTAPI* RtlCreateUnicodeStringFunc)(PUNICODE_STRING DestinationString, //_Out_ + PCWSTR SourceString); //_In_ typedef VOID (NTAPI* RtlFreeUnicodeStringFunc)(PUNICODE_STRING); //__inout unicodeString @@ -76,6 +77,7 @@ const SysDllFun<NtOpenFileFunc> ntOpenFile (L"ntdll.d const SysDllFun<NtCloseFunc> ntClose (L"ntdll.dll", "NtClose"); const SysDllFun<NtQueryDirectoryFileFunc> ntQueryDirectoryFile (L"ntdll.dll", "NtQueryDirectoryFile"); const SysDllFun<RtlNtStatusToDosErrorFunc> rtlNtStatusToDosError (L"ntdll.dll", "RtlNtStatusToDosError"); +const SysDllFun<RtlCreateUnicodeStringFunc> rtlCreateUnicodeString (L"ntdll.dll", "RtlCreateUnicodeString"); const SysDllFun<RtlFreeUnicodeStringFunc> rtlFreeUnicodeString (L"ntdll.dll", "RtlFreeUnicodeString"); const SysDllFun<RtlDosPathNameToNtPathName_UFunc> rtlDosPathNameToNtPathName_U(SysDllFun<RtlDosPathNameToRelativeNtPathName_UFunc>(L"ntdll.dll", "RtlDosPathNameToRelativeNtPathName_U") ? SysDllFun<RtlDosPathNameToRelativeNtPathName_UFunc>(L"ntdll.dll", "RtlDosPathNameToRelativeNtPathName_U") : //use the newer version if available @@ -93,11 +95,12 @@ bool findplus::initDllBinding() //evaluate in ::DllMain() when attaching process //http://msdn.microsoft.com/en-us/library/ff563638(v=VS.85).aspx //verify dynamic dll binding - return ntOpenFile && - ntClose && - ntQueryDirectoryFile && - rtlNtStatusToDosError && - rtlFreeUnicodeString && + return ntOpenFile && + ntClose && + ntQueryDirectoryFile && + rtlNtStatusToDosError && + rtlCreateUnicodeString && + rtlFreeUnicodeString && rtlDosPathNameToNtPathName_U; //this may become handy some time: nt status code STATUS_ORDINAL_NOT_FOUND maps to win32 code ERROR_INVALID_ORDINAL @@ -128,43 +131,89 @@ private: }; +//a simple scope guard without <utility>, <type_traits>, <cassert> dependencies: +template <typename F> +class ScopeGuardLean +{ +public: + explicit ScopeGuardLean(F fun) : dismissed_(false), fun_(fun) {} + ScopeGuardLean(ScopeGuardLean&& other) : dismissed_(other.dismissed_), fun_(std::move(other.fun_)) { other.dismiss(); } + + void dismiss() { dismissed_ = true; } + + ~ScopeGuardLean() + { + if (!dismissed_) + try { fun_(); } + catch (...) {} + } + +private: + ScopeGuardLean (const ScopeGuardLean&); // = delete + ScopeGuardLean& operator=(const ScopeGuardLean&); // + + bool dismissed_; + F fun_; +}; + +template <class F> inline +ScopeGuardLean<F> makeGuard(F fun) { return ScopeGuardLean<F>(fun); } + + FileSearcher::FileSearcher(const wchar_t* dirname) : + dirnameNt(), //[!] hDir(nullptr), nextEntryOffset(0) { - dirnameNt.Buffer = nullptr; - dirnameNt.Length = 0; - dirnameNt.MaximumLength = 0; - - zen::ScopeGuard guardConstructor = zen::makeGuard([&] { this->~FileSearcher(); }); + auto guardConstructor = makeGuard([&] { this->~FileSearcher(); }); //-------------------------------------------------------------------------------------------------------------- //convert dosFileName, e.g. C:\Users or \\?\C:\Users to ntFileName \??\C:\Users //in contrast to ::FindFirstFile() implementation we don't evaluate the relativeName, //however tests indicate ntFileName is *always* filled with an absolute name, even if dosFileName is relative + PCWSTR filePart = nullptr; + RTL_RELATIVE_NAME_U relativeName = {}; + //NOTE: RtlDosPathNameToNtPathName_U may be used on all XP/Win7/Win8 for compatibility // RtlDosPathNameToNtPathName_U: used by Windows XP available with OS version 3.51 (Windows NT) and higher // RtlDosPathNameToRelativeNtPathName_U: used by Win7/Win8 available with OS version 5.2 (Windows Server 2003) and higher - if (!rtlDosPathNameToNtPathName_U(dirname, //__in dosFileName, - &dirnameNt, //__out ntFileName, - nullptr, //__out_optFilePart, - nullptr)) //__out_opt relativeName - empty if dosFileName is absolute + if (!rtlDosPathNameToNtPathName_U(dirname, //__in dosFileName, + &dirnameNt, //__out ntFileName, + &filePart, //__out FilePart - points into ntFileName + &relativeName)) //__out relativeName - points into ntFileName; empty if dosFileName is absolute throw NtFileError(STATUS_OBJECT_PATH_NOT_FOUND); //translates to ERROR_PATH_NOT_FOUND, same behavior like ::FindFirstFileEx() + //note 1: internally it distinguishes between "quick path" == \\?\ and "slow path" handling! + //http://doxygen.reactos.org/d9/d6e/lib_2rtl_2path_8c_a11c87ad0f7752999b0b8972af6165d7a.html#a11c87ad0f7752999b0b8972af6165d7a + //note 2: without \\?\, i.e. slow path handling it calls RtlGetFullPathName_Ustr() which removes trailing spaces!!! + //http://doxygen.reactos.org/d9/d6e/lib_2rtl_2path_8c_a8624b864678ca64b031f5fc273e022af.html#a8624b864678ca64b031f5fc273e022af + //FindFirstFile() gets lucky because it passes "<dirname>\*" which never has trailing space chars! >:( OBJECT_ATTRIBUTES objAttr = {}; - InitializeObjectAttributes(&objAttr, //[out] POBJECT_ATTRIBUTES initializedAttributes, - &dirnameNt, //[in] PUNICODE_STRING objectName, - OBJ_CASE_INSENSITIVE, //[in] ULONG attributes, - nullptr, //[in] HANDLE rootDirectory, - nullptr); //[in, optional] PSECURITY_DESCRIPTOR securityDescriptor + if (relativeName.RelativeName.Length == 0) //absolute name + { + InitializeObjectAttributes(&objAttr, //[out] POBJECT_ATTRIBUTES initializedAttributes, + &dirnameNt, //[in] PUNICODE_STRING objectName, + OBJ_CASE_INSENSITIVE, //[in] ULONG attributes, + nullptr, //[in] HANDLE rootDirectory, + nullptr); //[in, optional] PSECURITY_DESCRIPTOR securityDescriptor + } + else //relative name (it seems we alternatively could also use dirnameNt here?) + { + InitializeObjectAttributes(&objAttr, //[out] POBJECT_ATTRIBUTES initializedAttributes, + &relativeName.RelativeName, //[in] PUNICODE_STRING objectName, + OBJ_CASE_INSENSITIVE, //[in] ULONG attributes, + relativeName.ContainingDirectory, //[in] HANDLE rootDirectory, + nullptr); //[in, optional] PSECURITY_DESCRIPTOR securityDescriptor + } + { IO_STATUS_BLOCK status = {}; - NTSTATUS rv = ntOpenFile(&hDir, //__out PHANDLE FileHandle, + NTSTATUS rv = ntOpenFile(&hDir, //__out PHANDLE FileHandle, FILE_LIST_DIRECTORY | SYNCHRONIZE, //__in ACCESS_MASK desiredAccess, - 100001 used by ::FindFirstFile() on all XP/Win7/Win8 - &objAttr, //__in POBJECT_ATTRIBUTES objectAttributes, - &status, //__out PIO_STATUS_BLOCK ioStatusBlock, + &objAttr, //__in POBJECT_ATTRIBUTES objectAttributes, + &status, //__out PIO_STATUS_BLOCK ioStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //__in ULONG shareAccess, - 7 on Win7/Win8, 3 on XP FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT); //__in ULONG openOptions - 4021 used on all XP/Win7/Win8 if (!NT_SUCCESS(rv)) @@ -184,7 +233,7 @@ FileSearcher::~FileSearcher() if (dirnameNt.Buffer) rtlFreeUnicodeString(&dirnameNt); //cleanup identical to ::FindFirstFile() - //note that most of this function seems inlined by the linker, so that its assembly looks equivalent to "RtlFreeHeap(GetProcessHeap(), 0, ntPathName.Buffer)" + //note that most of this function seems inlined by the linker, so that its assembly looks equivalent to "RtlFreeHeap(RtlGetProcessHeap(), 0, ntPathName.Buffer)" } @@ -364,7 +413,7 @@ FindHandle findplus::openDir(const wchar_t* dirname) setWin32Error(rtlNtStatusToDosError(e.ntError)); return nullptr; } - catch (const std::bad_alloc&) //not unlikely in this context! => handle! + catch (const std::bad_alloc&) //not unlikely in file sync context! => handle! { setWin32Error(rtlNtStatusToDosError(STATUS_NO_MEMORY)); return nullptr; |