summaryrefslogtreecommitdiff
path: root/zen/FindFilePlus
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 17:17:51 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 17:17:51 +0200
commit237aedc590b58c0e69d7dfcac92b5f767b7c004a (patch)
tree83f361a82ba483f2daf83b677e8685cd953812d9 /zen/FindFilePlus
parent4.5 (diff)
downloadFreeFileSync-237aedc590b58c0e69d7dfcac92b5f767b7c004a.tar.gz
FreeFileSync-237aedc590b58c0e69d7dfcac92b5f767b7c004a.tar.bz2
FreeFileSync-237aedc590b58c0e69d7dfcac92b5f767b7c004a.zip
4.6
Diffstat (limited to 'zen/FindFilePlus')
-rw-r--r--zen/FindFilePlus/find_file_plus.cpp127
-rw-r--r--zen/FindFilePlus/find_file_plus.h15
2 files changed, 61 insertions, 81 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;
}
diff --git a/zen/FindFilePlus/find_file_plus.h b/zen/FindFilePlus/find_file_plus.h
index 33e9a178..2ef5affe 100644
--- a/zen/FindFilePlus/find_file_plus.h
+++ b/zen/FindFilePlus/find_file_plus.h
@@ -38,7 +38,7 @@ struct FileInformation
DWORD fileAttributes;
DWORD shortNameLength;
WCHAR shortName[MAX_PATH + 1]; //shortName is 0-terminated
-}; //no need for #pragma pack -> all members already starting at 4 byte boundary!
+}; //no need for #pragma pack -> all members are perfectly 4, 8 byte aligned!
class FileSearcher;
typedef FileSearcher* FindHandle;
@@ -49,6 +49,19 @@ FindHandle openDir(const wchar_t* dirname); //returns NULL on error, call ::GetL
DLL_FUNCTION_DECLARATION
bool readDir(FindHandle hnd, FileInformation& output); //returns false on error or if there are no more files; ::GetLastError() returns ERROR_NO_MORE_FILES in this case
+/*
+warning:; this may also return file system implementation dependent error codes like ERROR_NOT_SUPPORTED, ERROR_INVALID_LEVEL,
+ect. if "FileIdBothDirectoryInformation" is not supported! We need a fallback:
+
+ sometimes it's *not* sufficient to use fallback for NtQueryDirectoryFile() alone, we need to reset "hDir", since it may be f$ck$d $p by some poor file system layer implementation:
+ - 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. Fallback to FileBothDirectoryInformation will return STATUS_NO_MORE_FILES, even if there *are* more files
+ - NtQueryDirectoryFile() may *not* respect "restartScan" for some weird Win2000 file system drivers, so we cannot rely on this as a replacement for a "hDir" reset
+ - Windows 7 Remote Desktop sharing does not work unless "hDir" is reset!
+ => let's assume worst case in general and do a reset!
+ perf note: implementing this reset at a folder level is possible, but a huge perf-killer (additional open/close handle), therefore fallback must apply to a complete folder (sub-)tree!
+ => caller needs to handle this and implement FindFirstFile()/FindNextFile() fallback!
+*/
DLL_FUNCTION_DECLARATION
void closeDir(FindHandle hnd);
bgstack15