diff options
Diffstat (limited to 'zen/FindFilePlus/find_file_plus.cpp')
-rw-r--r-- | zen/FindFilePlus/find_file_plus.cpp | 127 |
1 files changed, 47 insertions, 80 deletions
diff --git a/zen/FindFilePlus/find_file_plus.cpp b/zen/FindFilePlus/find_file_plus.cpp index e613177b..876d0c0f 100644 --- a/zen/FindFilePlus/find_file_plus.cpp +++ b/zen/FindFilePlus/find_file_plus.cpp @@ -110,16 +110,12 @@ public: FileSearcher(const wchar_t* dirname); //throw FileError ~FileSearcher(); - void readDir(FileInformation& output) { (this->*readDirFun)(output); } //throw FileError - - bool tryFallbackToDefaultQuery(); //call if readDir() throws STATUS_NOT_SUPPORTED or similar + void readDir(FileInformation& output); //throw FileError private: template <class QueryPolicy> void readDirImpl(FileInformation& output); //throw FileError - //handle fallback, if retrieving file id is not supported by file system layer - void (FileSearcher::*readDirFun)(FileInformation& output); - UNICODE_STRING ntPathName; //it seems hDir implicitly keeps a reference to this, at least ::FindFirstFile() does no cleanup before ::FindClose()! + UNICODE_STRING dirnameNt; //it seems hDir implicitly keeps a reference to this, at least ::FindFirstFile() does no cleanup before ::FindClose()! HANDLE hDir; ULONG nextEntryOffset; //!= 0 if entry is waiting in buffer @@ -132,47 +128,13 @@ private: }; -namespace -{ -/* -Common C-style policy handling directory traversal: - -struct QueryPolicy -{ - typedef ... RawFileInfo; - static const FILE_INFORMATION_CLASS fileInformationClass = ...; - static void extractFileId(const RawFileInfo& rawInfo, FileInformation& fileInfo); -}; -*/ - -struct DirQueryDefault -{ - typedef FILE_BOTH_DIR_INFORMATION RawFileInfo; - static const FILE_INFORMATION_CLASS fileInformationClass = FileBothDirectoryInformation; - static void extractFileId(const RawFileInfo& rawInfo, FileInformation& fileInfo) { fileInfo.fileId.QuadPart = 0; } -}; - -struct DirQueryFileId -{ - typedef FILE_ID_BOTH_DIR_INFORMATION RawFileInfo; - static const FILE_INFORMATION_CLASS fileInformationClass = FileIdBothDirectoryInformation; - static void extractFileId(const RawFileInfo& rawInfo, FileInformation& fileInfo) - { - fileInfo.fileId.QuadPart = rawInfo.FileId.QuadPart; //may be 0 even in this context, e.g. for mapped FTP drive! - static_assert(sizeof(fileInfo.fileId) == sizeof(rawInfo.FileId), "dang!"); - } -}; -} - - FileSearcher::FileSearcher(const wchar_t* dirname) : hDir(NULL), - nextEntryOffset(0), - readDirFun(&FileSearcher::readDirImpl<DirQueryFileId>) //start optimistically + nextEntryOffset(0) { - ntPathName.Buffer = NULL; - ntPathName.Length = 0; - ntPathName.MaximumLength = 0; + dirnameNt.Buffer = NULL; + dirnameNt.Length = 0; + dirnameNt.MaximumLength = 0; zen::ScopeGuard guardConstructor = zen::makeGuard([&]() { this->~FileSearcher(); }); //-------------------------------------------------------------------------------------------------------------- @@ -184,19 +146,19 @@ FileSearcher::FileSearcher(const wchar_t* dirname) : //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, - &ntPathName, //__out ntFileName, - NULL, //__out_optFilePart, - NULL)) //__out_opt relativeName - empty if dosFileName is absolute + if (!rtlDosPathNameToNtPathName_U(dirname, //__in dosFileName, + &dirnameNt, //__out ntFileName, + NULL, //__out_optFilePart, + NULL)) //__out_opt relativeName - empty if dosFileName is absolute throw NtFileError(STATUS_OBJECT_PATH_NOT_FOUND); //translates to ERROR_PATH_NOT_FOUND, same behavior like ::FindFirstFileEx() + OBJECT_ATTRIBUTES objAttr = {}; InitializeObjectAttributes(&objAttr, //[out] POBJECT_ATTRIBUTES initializedAttributes, - &ntPathName, //[in] PUNICODE_STRING objectName, + &dirnameNt, //[in] PUNICODE_STRING objectName, OBJ_CASE_INSENSITIVE, //[in] ULONG attributes, NULL, //[in] HANDLE rootDirectory, NULL); //[in, optional] PSECURITY_DESCRIPTOR securityDescriptor - { IO_STATUS_BLOCK status = {}; NTSTATUS rv = ntOpenFile(&hDir, //__out PHANDLE FileHandle, @@ -220,28 +182,49 @@ FileSearcher::~FileSearcher() if (hDir) ntClose(hDir); - if (ntPathName.Buffer) - rtlFreeUnicodeString(&ntPathName); //cleanup identical to ::FindFirstFile() + 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)" } -inline -bool FileSearcher::tryFallbackToDefaultQuery() +namespace { - if (readDirFun == &FileSearcher::readDirImpl<DirQueryDefault>) - return false; //already default +/* +Common C-style policy handling directory traversal: - //Note: NtQueryDirectoryFile() may not respect "restartScan" for some weird Win2000 file system drivers, so we won't bother - //Samba before v3.0.22 (Mar 30, 2006) seems to have a bug which sucessfully returns 128 elements via NtQueryDirectoryFile() - //and FileIdBothDirectoryInformation, then fails with STATUS_INVALID_LEVEL. - //Although traversal is NOT finished yet, it will further return STATUS_NO_MORE_FILES, even if falling back to FileBothDirectoryInformation!!! +struct QueryPolicy +{ + typedef ... RawFileInfo; + static const FILE_INFORMATION_CLASS fileInformationClass = ...; + static void extractFileId(const RawFileInfo& rawInfo, FileInformation& fileInfo); +}; +*/ - readDirFun = &FileSearcher::readDirImpl<DirQueryDefault>; - return true; +struct DirQueryDefault //as implemented in Win32 FindFirstFile()/FindNextFile() +{ + typedef FILE_BOTH_DIR_INFORMATION RawFileInfo; + static const FILE_INFORMATION_CLASS fileInformationClass = FileBothDirectoryInformation; + static void extractFileId(const RawFileInfo& rawInfo, FileInformation& fileInfo) { fileInfo.fileId.QuadPart = 0; } +}; + +struct DirQueryFileId +{ + typedef FILE_ID_BOTH_DIR_INFORMATION RawFileInfo; + static const FILE_INFORMATION_CLASS fileInformationClass = FileIdBothDirectoryInformation; + static void extractFileId(const RawFileInfo& rawInfo, FileInformation& fileInfo) + { + fileInfo.fileId.QuadPart = rawInfo.FileId.QuadPart; //may be 0 even in this context, e.g. for mapped FTP drive! + static_assert(sizeof(fileInfo.fileId) == sizeof(rawInfo.FileId), "dang!"); + } +}; } +inline +void FileSearcher::readDir(FileInformation& output) { readDirImpl<DirQueryFileId>(output); } //throw FileError + + template <class QueryPolicy> void FileSearcher::readDirImpl(FileInformation& output) //throw FileError { @@ -283,9 +266,9 @@ void FileSearcher::readDirImpl(FileInformation& output) //throw FileError if (!NT_SUCCESS(rv)) { if (rv == STATUS_NO_SUCH_FILE) //harmonize ntQueryDirectoryFile() error handling! failure to find a file on first call returns STATUS_NO_SUCH_FILE, - rv = STATUS_NO_MORE_FILES; //while on subsequent accesses return STATUS_NO_MORE_FILES + rv = STATUS_NO_MORE_FILES; //while on subsequent accesses returns STATUS_NO_MORE_FILES //note: not all directories contain "., .." entries! E.g. a drive's root directory or NetDrive + ftp.gnu.org\CRYPTO.README" - //-> addon: this is NOT a directory, it looks like on in NetDrive, but it's a file in Opera + //-> addon: this is NOT a directory, it looks like one in NetDrive, but it's a file in Opera throw NtFileError(rv); //throws STATUS_NO_MORE_FILES when finished } @@ -353,22 +336,6 @@ bool findplus::readDir(FindHandle hnd, FileInformation& output) } catch (const NtFileError& e) { - /* - fallback to default directory query method, if FileIdBothDirectoryInformation is not properly implemented - this is required for NetDrive mounted Webdav, e.g. www.box.net and NT4, 2000 remote drives, et al. - */ - if (e.ntError != STATUS_NO_MORE_FILES) - if (e.ntError == STATUS_INVALID_LEVEL || - e.ntError == STATUS_NOT_SUPPORTED || - e.ntError == STATUS_INVALID_PARAMETER || - e.ntError == STATUS_INVALID_NETWORK_RESPONSE || - e.ntError == STATUS_INVALID_INFO_CLASS || - e.ntError == STATUS_ACCESS_VIOLATION) //FileIdBothDirectoryInformation on XP accessing UDF - { - if (hnd->tryFallbackToDefaultQuery()) - return readDir(hnd, output); //implementation of tryFallbackToDefaultQuery() promises, that we don't land in an endless recursion here! - } - setWin32Error(rtlNtStatusToDosError(e.ntError)); return false; } |