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.

svn path=/trunk/; revision=27634
This commit is contained in:
Thomas Bluemel 2007-07-13 19:43:09 +00:00
parent 5bce20b1be
commit 532f7fecc5

View file

@ -21,6 +21,8 @@
#define FIND_DATA_SIZE (16*1024)
#define FIND_DEVICE_HANDLE ((HANDLE)0x1)
typedef struct _KERNEL32_FIND_FILE_DATA
{
HANDLE DirectoryHandle;
@ -49,6 +51,61 @@ typedef struct _KERNEL32_FIND_DATA_HEADER
/* 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,
PFILE_BOTH_DIR_INFORMATION lpFileInfo)
@ -143,6 +200,12 @@ InternalFindNextFile (
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 ||
IHeader->Type != FileFind)
@ -197,135 +260,140 @@ HANDLE
STDCALL
InternalFindFirstFile (
LPCWSTR lpFileName,
BOOLEAN DirectoryOnly
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)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return NULL;
}
memcpy(SlashlessFileName, lpFileName, (Length - 1) * sizeof(WCHAR));
SlashlessFileName[Length - 1] = L'\0';
lpFileName = SlashlessFileName;
}
else
{
SlashlessFileName = NULL;
}
*DeviceNameInfo = 0;
RtlZeroMemory(&PathFileName,
sizeof(PathFileName));
RtlInitUnicodeString(&FileName,
lpFileName);
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,
bResult = RtlDosPathNameToNtPathName_U (lpFileName,
&NtPathU,
NULL,
NULL);
if (SearchPath != CurrentDir)
{
RtlFreeHeap(hProcessHeap,
0,
SearchPath);
}
(PCWSTR *)((ULONG_PTR)&PathFileName.Buffer),
&DirInfo);
if (FALSE == bResult)
{
if (NULL != SlashlessFileName)
{
RtlFreeHeap(hProcessHeap,
0,
SlashlessFileName);
}
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
SetLastError(ERROR_PATH_NOT_FOUND);
return NULL;
}
DPRINT("NtPathU \'%S\'\n", NtPathU.Buffer);
/* 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)
{
/* 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;
}
}
else
{
/* 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,
OBJ_CASE_INSENSITIVE,
DirInfo.Handle,
NULL);
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,
@ -335,69 +403,35 @@ InternalFindFirstFile (
{
RtlFreeHeap (hProcessHeap,
0,
NtPathU.Buffer);
if (NULL != SlashlessFileName)
{
RtlFreeHeap(hProcessHeap,
0,
SlashlessFileName);
}
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 (wcscmp (SearchPattern, L"*.*"))
if (PathFileName.Length == 6 &&
RtlCompareMemory(PathFileName.Buffer,
L"*.*",
6) == 6)
{
RtlInitUnicodeString(&PatternStr, SearchPattern);
PathFileName.Length = 2;
}
DPRINT("NtPathU \'%S\' Pattern \'%S\'\n",
NtPathU.Buffer, PatternStr.Buffer);
InitializeObjectAttributes (&ObjectAttributes,
&NtPathU,
0,
NULL,
NULL);
Status = NtOpenFile (&IData->DirectoryHandle,
FILE_LIST_DIRECTORY,
&ObjectAttributes,
&IoStatusBlock,
FILE_SHARE_READ|FILE_SHARE_WRITE,
FILE_DIRECTORY_FILE);
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,
bResult = InternalFindNextFile((HANDLE)IHeader, &PathFileName);
RtlFreeHeap (hProcessHeap,
0,
SlashlessFileName);
}
NtPathBuffer);
if (!bResult)
{
@ -423,6 +457,7 @@ FindFirstFileA (
PKERNEL32_FIND_FILE_DATA IData;
UNICODE_STRING FileNameU;
ANSI_STRING FileName;
ULONG DeviceNameInfo;
RtlInitAnsiString (&FileName,
(LPSTR)lpFileName);
@ -437,16 +472,23 @@ FindFirstFileA (
&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);
DPRINT("IData->pFileInfo->FileNameLength %d\n",
@ -502,6 +544,9 @@ FindClose (
DPRINT("FindClose(hFindFile %x)\n",hFindFile);
if (hFindFile == FIND_DEVICE_HANDLE)
return TRUE;
if (!hFindFile || hFindFile == INVALID_HANDLE_VALUE)
{
SetLastError (ERROR_INVALID_HANDLE);
@ -600,6 +645,7 @@ FindFirstFileExW (LPCWSTR lpFileName,
{
PKERNEL32_FIND_DATA_HEADER IHeader;
PKERNEL32_FIND_FILE_DATA IData;
ULONG DeviceNameInfo;
if (fInfoLevelId != FindExInfoStandard)
{
@ -614,13 +660,16 @@ FindFirstFileExW (LPCWSTR lpFileName,
return INVALID_HANDLE_VALUE;
}
IHeader = InternalFindFirstFile (lpFileName, fSearchOp == FindExSearchLimitToDirectories ? TRUE : FALSE);
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);
/* copy data into WIN32_FIND_DATA structure */
@ -650,6 +699,7 @@ FindFirstFileExA (
PKERNEL32_FIND_FILE_DATA IData;
UNICODE_STRING FileNameU;
ANSI_STRING FileNameA;
ULONG DeviceNameInfo;
if (fInfoLevelId != FindExInfoStandard)
{
@ -672,16 +722,23 @@ FindFirstFileExA (
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);
/* copy data into WIN32_FIND_DATA structure */