Author: weiden Date: Fri Jul 13 23:43:09 2007 New Revision: 27634
URL: http://svn.reactos.org/svn/reactos?rev=27634&view=rev Log: Rewrite parts of FindFirstFileEx to: - Fix searches relative to the current directory using the features RtlDosPathNameToNtPathName_U provides instead of hacking around them - Enable searches for fake DOS devices. Some installers like NSIS use this feature to determine if a drive exists, this works now.
Modified: trunk/reactos/dll/win32/kernel32/file/find.c
Modified: trunk/reactos/dll/win32/kernel32/file/find.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/kernel32/file/fin... ============================================================================== --- trunk/reactos/dll/win32/kernel32/file/find.c (original) +++ trunk/reactos/dll/win32/kernel32/file/find.c Fri Jul 13 23:43:09 2007 @@ -21,6 +21,8 @@
#define FIND_DATA_SIZE (16*1024)
+#define FIND_DEVICE_HANDLE ((HANDLE)0x1) + typedef struct _KERNEL32_FIND_FILE_DATA { HANDLE DirectoryHandle; @@ -48,6 +50,61 @@
/* FUNCTIONS ****************************************************************/ + +HANDLE +InternalCopyDeviceFindDataW(LPWIN32_FIND_DATAW lpFindFileData, + LPCWSTR lpFileName, + ULONG DeviceNameInfo) +{ + UNICODE_STRING DeviceName; + + DeviceName.Length = DeviceName.MaximumLength = (USHORT)(DeviceNameInfo & 0xFFFF); + DeviceName.Buffer = (LPWSTR)((ULONG_PTR)lpFileName + (DeviceNameInfo >> 16)); + + /* Return the data */ + RtlZeroMemory(lpFindFileData, + sizeof(*lpFindFileData)); + lpFindFileData->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE; + RtlCopyMemory(lpFindFileData->cFileName, + DeviceName.Buffer, + DeviceName.Length); + + return FIND_DEVICE_HANDLE; +} + +HANDLE +InternalCopyDeviceFindDataA(LPWIN32_FIND_DATAA lpFindFileData, + PUNICODE_STRING FileName, + ULONG DeviceNameInfo) +{ + UNICODE_STRING DeviceName; + ANSI_STRING BufferA; + CHAR Buffer[MAX_PATH]; + + DeviceName.Length = DeviceName.MaximumLength = (USHORT)(DeviceNameInfo & 0xFFFF); + DeviceName.Buffer = (LPWSTR)((ULONG_PTR)FileName->Buffer + (DeviceNameInfo >> 16)); + + BufferA.MaximumLength = sizeof(Buffer) - sizeof(Buffer[0]); + BufferA.Buffer = Buffer; + if (bIsFileApiAnsi) + RtlUnicodeStringToAnsiString (&BufferA, &DeviceName, FALSE); + else + RtlUnicodeStringToOemString (&BufferA, &DeviceName, FALSE); + + /* NOTE: Free the string before we try to write the results to the caller, + this way we prevent a memory leak in case of a fault... */ + RtlFreeUnicodeString(FileName); + + /* Return the data */ + RtlZeroMemory(lpFindFileData, + sizeof(*lpFindFileData)); + lpFindFileData->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE; + RtlCopyMemory(lpFindFileData->cFileName, + BufferA.Buffer, + BufferA.Length); + + return FIND_DEVICE_HANDLE; +}
VOID InternalCopyFindDataW(LPWIN32_FIND_DATAW lpFindFileData, @@ -142,6 +199,12 @@ NTSTATUS Status;
DPRINT("InternalFindNextFile(%lx)\n", hFindFile); + + if (hFindFile == FIND_DEVICE_HANDLE) + { + SetLastError (ERROR_NO_MORE_FILES); + return FALSE; + }
IHeader = (PKERNEL32_FIND_DATA_HEADER)hFindFile; if (hFindFile == NULL || hFindFile == INVALID_HANDLE_VALUE || @@ -196,214 +259,185 @@ HANDLE STDCALL InternalFindFirstFile ( - LPCWSTR lpFileName, - BOOLEAN DirectoryOnly + LPCWSTR lpFileName, + BOOLEAN DirectoryOnly, + PULONG DeviceNameInfo ) { OBJECT_ATTRIBUTES ObjectAttributes; PKERNEL32_FIND_DATA_HEADER IHeader; PKERNEL32_FIND_FILE_DATA IData; IO_STATUS_BLOCK IoStatusBlock; - UNICODE_STRING NtPathU; - UNICODE_STRING PatternStr = RTL_CONSTANT_STRING(L"*"); + UNICODE_STRING NtPathU, FileName, PathFileName; NTSTATUS Status; - PWSTR e1, e2; - WCHAR CurrentDir[256]; - PWCHAR SlashlessFileName; - PWSTR SearchPath; - PWCHAR SearchPattern; - ULONG Length; + PWSTR NtPathBuffer; + BOOLEAN RemovedSlash = FALSE; BOOL bResult; + CURDIR DirInfo; + HANDLE hDirectory = NULL;
DPRINT("FindFirstFileW(lpFileName %S)\n", lpFileName);
- Length = wcslen(lpFileName); - if (L'\' == lpFileName[Length - 1]) - { - SlashlessFileName = RtlAllocateHeap(hProcessHeap, - 0, - Length * sizeof(WCHAR)); - if (NULL == SlashlessFileName) + *DeviceNameInfo = 0; + RtlZeroMemory(&PathFileName, + sizeof(PathFileName)); + RtlInitUnicodeString(&FileName, + lpFileName); + + bResult = RtlDosPathNameToNtPathName_U (lpFileName, + &NtPathU, + (PCWSTR *)((ULONG_PTR)&PathFileName.Buffer), + &DirInfo); + if (FALSE == bResult) + { + SetLastError(ERROR_PATH_NOT_FOUND); + return NULL; + } + + /* Save the buffer pointer for later, we need to free it! */ + NtPathBuffer = NtPathU.Buffer; + + /* If there is a file name/pattern then determine it's length */ + if (PathFileName.Buffer != NULL) + { + PathFileName.Length = NtPathU.Length - + (USHORT)((ULONG_PTR)PathFileName.Buffer - (ULONG_PTR)NtPathU.Buffer); + } + PathFileName.MaximumLength = PathFileName.Length; + + if (DirInfo.DosPath.Length != 0 && DirInfo.DosPath.Buffer != PathFileName.Buffer) + { + if (PathFileName.Buffer != NULL) { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return NULL; + /* This is a relative path to DirInfo.Handle, adjust NtPathU! */ + NtPathU.Length = NtPathU.MaximumLength = + (USHORT)((ULONG_PTR)PathFileName.Buffer - (ULONG_PTR)DirInfo.DosPath.Buffer); + NtPathU.Buffer = DirInfo.DosPath.Buffer; } - memcpy(SlashlessFileName, lpFileName, (Length - 1) * sizeof(WCHAR)); - SlashlessFileName[Length - 1] = L'\0'; - lpFileName = SlashlessFileName; } else { - SlashlessFileName = NULL; - } - - e1 = wcsrchr(lpFileName, L'/'); - e2 = wcsrchr(lpFileName, L'\'); - SearchPattern = max(e1, e2); - SearchPath = CurrentDir; - - if (NULL == SearchPattern) - { - CHECKPOINT; - SearchPattern = (PWCHAR)lpFileName; - Length = GetCurrentDirectoryW(sizeof(CurrentDir) / sizeof(WCHAR), SearchPath); - if (0 == Length) - { - if (NULL != SlashlessFileName) - { - RtlFreeHeap(hProcessHeap, - 0, - SlashlessFileName); - } - return NULL; - } - if (Length > sizeof(CurrentDir) / sizeof(WCHAR)) - { - SearchPath = RtlAllocateHeap(hProcessHeap, - HEAP_ZERO_MEMORY, - Length * sizeof(WCHAR)); - if (NULL == SearchPath) - { - if (NULL != SlashlessFileName) - { - RtlFreeHeap(hProcessHeap, - 0, - SlashlessFileName); - } - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return NULL; - } - GetCurrentDirectoryW(Length, SearchPath); - } - } - else - { - CHECKPOINT; - SearchPattern++; - Length = SearchPattern - lpFileName; - if (Length + 1 > sizeof(CurrentDir) / sizeof(WCHAR)) - { - SearchPath = RtlAllocateHeap(hProcessHeap, - HEAP_ZERO_MEMORY, - (Length + 1) * sizeof(WCHAR)); - if (NULL == SearchPath) - { - if (NULL != SlashlessFileName) - { - RtlFreeHeap(hProcessHeap, - 0, - SlashlessFileName); - } - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return NULL; - } - } - memcpy(SearchPath, lpFileName, Length * sizeof(WCHAR)); - SearchPath[Length] = 0; - } - - bResult = RtlDosPathNameToNtPathName_U (SearchPath, - &NtPathU, - NULL, - NULL); - if (SearchPath != CurrentDir) - { - RtlFreeHeap(hProcessHeap, - 0, - SearchPath); - } - if (FALSE == bResult) - { - if (NULL != SlashlessFileName) - { - RtlFreeHeap(hProcessHeap, - 0, - SlashlessFileName); - } - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return NULL; - } - - DPRINT("NtPathU '%S'\n", NtPathU.Buffer); - - IHeader = RtlAllocateHeap (hProcessHeap, - HEAP_ZERO_MEMORY, - sizeof(KERNEL32_FIND_DATA_HEADER) + - sizeof(KERNEL32_FIND_FILE_DATA) + FIND_DATA_SIZE); - if (NULL == IHeader) - { - RtlFreeHeap (hProcessHeap, - 0, - NtPathU.Buffer); - if (NULL != SlashlessFileName) - { - RtlFreeHeap(hProcessHeap, - 0, - SlashlessFileName); - } - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return NULL; - } - - IHeader->Type = FileFind; - IData = (PKERNEL32_FIND_FILE_DATA)(IHeader + 1); - - /* change pattern: "*.*" --> "*" */ - if (wcscmp (SearchPattern, L"*.*")) - { - RtlInitUnicodeString(&PatternStr, SearchPattern); - } - - DPRINT("NtPathU '%S' Pattern '%S'\n", - NtPathU.Buffer, PatternStr.Buffer); + /* This is an absolute path, NtPathU receives the full path */ + DirInfo.Handle = NULL; + if (PathFileName.Buffer != NULL) + { + NtPathU.Length = NtPathU.MaximumLength = + (USHORT)((ULONG_PTR)PathFileName.Buffer - (ULONG_PTR)NtPathU.Buffer); + } + } + + /* Remove a trailing backslash from the path, unless it's a DOS drive directly */ + if (NtPathU.Length > 3 * sizeof(WCHAR) && + NtPathU.Buffer[(NtPathU.Length / sizeof(WCHAR)) - 2] != L':' && + NtPathU.Buffer[(NtPathU.Length / sizeof(WCHAR)) - 1] != L'\') + { + NtPathU.Length -= sizeof(WCHAR); + RemovedSlash = TRUE; + } + + DPRINT("lpFileName: "%ws"\n", lpFileName); + DPRINT("NtPathU: "%wZ"\n", &NtPathU); + DPRINT("PathFileName: "%wZ"\n", &PathFileName); + DPRINT("RelativeTo: 0x%p\n", DirInfo.Handle);
InitializeObjectAttributes (&ObjectAttributes, &NtPathU, - 0, - NULL, + OBJ_CASE_INSENSITIVE, + DirInfo.Handle, NULL);
- Status = NtOpenFile (&IData->DirectoryHandle, + Status = NtOpenFile (&hDirectory, FILE_LIST_DIRECTORY, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_DIRECTORY_FILE);
+ if (!NT_SUCCESS(Status) && RemovedSlash) + { + /* Try again, this time with the trailing slash... */ + NtPathU.Length -= sizeof(WCHAR); + + Status = NtOpenFile (&hDirectory, + FILE_LIST_DIRECTORY, + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_READ|FILE_SHARE_WRITE, + FILE_DIRECTORY_FILE); + + NtPathU.Length -= sizeof(WCHAR); + } + + if (!NT_SUCCESS(Status)) + { + RtlFreeHeap (hProcessHeap, + 0, + NtPathBuffer); + + /* See if the application tries to look for a DOS device */ + *DeviceNameInfo = RtlIsDosDeviceName_U((PWSTR)((ULONG_PTR)lpFileName)); + if (*DeviceNameInfo != 0) + return FIND_DEVICE_HANDLE; + + SetLastErrorByStatus (Status); + return(NULL); + } + + if (PathFileName.Length == 0) + { + /* No file part?! */ + NtClose(hDirectory); + RtlFreeHeap (hProcessHeap, + 0, + NtPathBuffer); + SetLastError(ERROR_FILE_NOT_FOUND); + return NULL; + } + + IHeader = RtlAllocateHeap (hProcessHeap, + HEAP_ZERO_MEMORY, + sizeof(KERNEL32_FIND_DATA_HEADER) + + sizeof(KERNEL32_FIND_FILE_DATA) + FIND_DATA_SIZE); + if (NULL == IHeader) + { + RtlFreeHeap (hProcessHeap, + 0, + NtPathBuffer); + NtClose(hDirectory); + + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return NULL; + } + + IHeader->Type = FileFind; + IData = (PKERNEL32_FIND_FILE_DATA)(IHeader + 1); + IData->DirectoryHandle = hDirectory; + + /* change pattern: "*.*" --> "*" */ + if (PathFileName.Length == 6 && + RtlCompareMemory(PathFileName.Buffer, + L"*.*", + 6) == 6) + { + PathFileName.Length = 2; + } + + IData->pFileInfo = (PVOID)((ULONG_PTR)IData + sizeof(KERNEL32_FIND_FILE_DATA)); + IData->pFileInfo->FileIndex = 0; + IData->DirectoryOnly = DirectoryOnly; + + bResult = InternalFindNextFile((HANDLE)IHeader, &PathFileName); + RtlFreeHeap (hProcessHeap, 0, - NtPathU.Buffer); - - if (!NT_SUCCESS(Status)) - { - RtlFreeHeap (hProcessHeap, 0, IHeader); - if (NULL != SlashlessFileName) - { - RtlFreeHeap(hProcessHeap, - 0, - SlashlessFileName); - } - SetLastErrorByStatus (Status); - return(NULL); - } - IData->pFileInfo = (PVOID)((ULONG_PTR)IData + sizeof(KERNEL32_FIND_FILE_DATA)); - IData->pFileInfo->FileIndex = 0; - IData->DirectoryOnly = DirectoryOnly; - - bResult = InternalFindNextFile((HANDLE)IHeader, &PatternStr); - if (NULL != SlashlessFileName) - { - RtlFreeHeap(hProcessHeap, - 0, - SlashlessFileName); - } - - if (!bResult) - { - FindClose((HANDLE)IHeader); - return NULL; - } + NtPathBuffer); + + if (!bResult) + { + FindClose((HANDLE)IHeader); + return NULL; + }
return (HANDLE)IHeader; } @@ -423,6 +457,7 @@ PKERNEL32_FIND_FILE_DATA IData; UNICODE_STRING FileNameU; ANSI_STRING FileName; + ULONG DeviceNameInfo;
RtlInitAnsiString (&FileName, (LPSTR)lpFileName); @@ -437,15 +472,22 @@ &FileName, TRUE);
- IHeader = InternalFindFirstFile (FileNameU.Buffer, FALSE); - - RtlFreeUnicodeString (&FileNameU); + IHeader = InternalFindFirstFile (FileNameU.Buffer, FALSE, &DeviceNameInfo);
if (IHeader == NULL) { + RtlFreeUnicodeString (&FileNameU); DPRINT("Failing request\n"); return INVALID_HANDLE_VALUE; } + + if ((HANDLE)IHeader == FIND_DEVICE_HANDLE) + { + /* NOTE: FileNameU will be freed in InternalCopyDeviceFindDataA */ + return InternalCopyDeviceFindDataA(lpFindFileData, &FileNameU, DeviceNameInfo); + } + + RtlFreeUnicodeString (&FileNameU);
IData = (PKERNEL32_FIND_FILE_DATA)(IHeader + 1);
@@ -501,6 +543,9 @@ PKERNEL32_FIND_DATA_HEADER IHeader;
DPRINT("FindClose(hFindFile %x)\n",hFindFile); + + if (hFindFile == FIND_DEVICE_HANDLE) + return TRUE;
if (!hFindFile || hFindFile == INVALID_HANDLE_VALUE) { @@ -600,6 +645,7 @@ { PKERNEL32_FIND_DATA_HEADER IHeader; PKERNEL32_FIND_FILE_DATA IData; + ULONG DeviceNameInfo;
if (fInfoLevelId != FindExInfoStandard) { @@ -614,12 +660,15 @@ return INVALID_HANDLE_VALUE; }
- IHeader = InternalFindFirstFile (lpFileName, fSearchOp == FindExSearchLimitToDirectories ? TRUE : FALSE); - if (IHeader == NULL) - { - DPRINT("Failing request\n"); - return INVALID_HANDLE_VALUE; - } + IHeader = InternalFindFirstFile (lpFileName, fSearchOp == FindExSearchLimitToDirectories ? TRUE : FALSE, &DeviceNameInfo); + if (IHeader == NULL) + { + DPRINT("Failing request\n"); + return INVALID_HANDLE_VALUE; + } + + if ((HANDLE)IHeader == FIND_DEVICE_HANDLE) + return InternalCopyDeviceFindDataW((LPWIN32_FIND_DATAW)lpFindFileData, lpFileName, DeviceNameInfo);
IData = (PKERNEL32_FIND_FILE_DATA)(IHeader + 1);
@@ -650,6 +699,7 @@ PKERNEL32_FIND_FILE_DATA IData; UNICODE_STRING FileNameU; ANSI_STRING FileNameA; + ULONG DeviceNameInfo;
if (fInfoLevelId != FindExInfoStandard) { @@ -672,15 +722,22 @@ else RtlOemStringToUnicodeString (&FileNameU, &FileNameA, TRUE);
- IHeader = InternalFindFirstFile (FileNameU.Buffer, FALSE); - - RtlFreeUnicodeString (&FileNameU); + IHeader = InternalFindFirstFile (FileNameU.Buffer, FALSE, &DeviceNameInfo);
if (IHeader == NULL) { + RtlFreeUnicodeString (&FileNameU); DPRINT("Failing request\n"); return INVALID_HANDLE_VALUE; } + + if ((HANDLE)IHeader == FIND_DEVICE_HANDLE) + { + /* NOTE: FileNameU will be freed in InternalCopyDeviceFindDataA */ + return InternalCopyDeviceFindDataA(lpFindFileData, &FileNameU, DeviceNameInfo); + } + + RtlFreeUnicodeString (&FileNameU);
IData = (PKERNEL32_FIND_FILE_DATA)(IHeader + 1);