reactos/dll/win32/kernel32/client/file/find.c

1155 lines
37 KiB
C
Raw Normal View History

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS system libraries
* FILE: dll/win32/kernel32/client/file/find.c
* PURPOSE: Find functions
* PROGRAMMERS: Ariadne (ariadne@xs4all.nl)
* Pierre Schweitzer (pierre.schweitzer@reactos.org)
* Hermes Belusca-Maito
*/
/* INCLUDES *******************************************************************/
2003-01-15 Casper S. Hornstrup <chorns@users.sourceforge.net> * lib/kernel32/k32.h: New file. * lib/kernel32/makefile (TARGET_CFLAGS): Add -I./. (TARGET_PCH): Set to k32.h. * lib/kernel32/except/except.c: Use <k32.h>. * lib/kernel32/file/backup.c: Ditto. * lib/kernel32/file/cnotify.c: Ditto. * lib/kernel32/file/copy.c: Ditto. * lib/kernel32/file/create.c: Ditto. * lib/kernel32/file/curdir.c: Ditto. * lib/kernel32/file/delete.c: Ditto. * lib/kernel32/file/deviceio.c: Ditto. * lib/kernel32/file/dir.c: Ditto. * lib/kernel32/file/dosdev.c: Ditto. * lib/kernel32/file/file.c: Ditto. * lib/kernel32/file/find.c: Ditto. * lib/kernel32/file/iocompl.c: Ditto. * lib/kernel32/file/lfile.c: Ditto. * lib/kernel32/file/lock.c: Ditto. * lib/kernel32/file/mailslot.c: Ditto. * lib/kernel32/file/move.c: Ditto. * lib/kernel32/file/npipe.c: Ditto. * lib/kernel32/file/pipe.c: Ditto. * lib/kernel32/file/rw.c: Ditto. * lib/kernel32/file/tape.c: Ditto. * lib/kernel32/file/volume.c: Ditto. * lib/kernel32/mem/global.c: Ditto. * lib/kernel32/mem/heap.c: Ditto. * lib/kernel32/mem/isbad.c: Ditto. * lib/kernel32/mem/local.c: Ditto. * lib/kernel32/mem/procmem.c: Ditto. * lib/kernel32/mem/section.c: Ditto. * lib/kernel32/mem/virtual.c: Ditto. * lib/kernel32/misc/atom.c: Ditto. * lib/kernel32/misc/comm.c: Ditto. * lib/kernel32/misc/console.c: Ditto. * lib/kernel32/misc/debug.c: Ditto. * lib/kernel32/misc/dllmain.c: Ditto. * lib/kernel32/misc/env.c: Ditto. * lib/kernel32/misc/error.c: Ditto. * lib/kernel32/misc/handle.c: Ditto. * lib/kernel32/misc/ldr.c: Ditto. * lib/kernel32/misc/profile.c: Ditto. * lib/kernel32/misc/res.c: Ditto. * lib/kernel32/misc/stubs.c: Ditto. * lib/kernel32/misc/sysinfo.c: Ditto. * lib/kernel32/misc/time.c: Ditto. * lib/kernel32/process/cmdline.c: Ditto. * lib/kernel32/process/create.c: Ditto. * lib/kernel32/process/proc.c: Ditto. * lib/kernel32/process/session.c: Ditto. * lib/kernel32/string/lstring.c: Ditto. * lib/kernel32/synch/critical.c: Ditto. * lib/kernel32/synch/event.c: Ditto. * lib/kernel32/synch/intrlck.c: Ditto. * lib/kernel32/synch/mutex.c: Ditto. * lib/kernel32/synch/sem.c: Ditto. * lib/kernel32/synch/timer.c: Ditto. * lib/kernel32/synch/wait.c: Ditto. * lib/kernel32/thread/fiber.c: Ditto. * lib/kernel32/thread/thread.c: Ditto. * lib/kernel32/thread/tls.c: Ditto. svn path=/trunk/; revision=4009
2003-01-15 21:24:36 +00:00
#include <k32.h>
#include <ntstrsafe.h>
[KERNEL32]: While working on the CMAKE branch, Amine and myself discovered a rather serious issue in kernel32 (and perhaps other libraries as well). Unlike rbuild, CMake does not allow you to export non-existant DLL functions (try it: add "poopyhead" in kernel32's exports under RBuild, and will it export "poopyhead", God knowing what that will actually link to). As an additional feature on top of the "allow non-existing functions to be exported" "feature", because rbuild generates and links STDCALL function names without the proper decoration (vs. enforcing decoration at linking, but only removing it at export-time), this allows the definition (as an export) of a STDCALL function that is completely different from the actual function itself. For example, the 5-parameter Foo function is normally Foo@20, while the 3-parameter Foo function woudl be Foo@12. Linking one against the other would fail (say, 2 parameters were added to Foo in a newer version). However, under RBUILD, both of these would appear as "Foo", and the linker/compiler would happilly connect the caller of Foo@3 (that has pushed 3 parameters) to the receiving side of Foo@5 (that is about to pop 5 parameters). Even -if- decorations WERE to be applied, Foo@12 would STILL succeed, because of the first feature, which would enable the export of Foo@12 even though no such function exist. In a further, bizare, twist of fate, the behavior of this RBUILD "feature", when the target function is not found, is to link the exported DLL TO ITSELF. Therefore, one can see how, previously to this patch, kernel32.dll would import a dozen functions from itself (all the non-existing functions). To really seal the deal, the behavior of exported functions used by kernel32, but that are actually forwarded to another DLL deserves a special mention. GetLastError, for example, merely forwards to RtlGetLastWin32Error, so it is normal behavior to use a #define in the C code so that all internal calls to the function are routed correctly. This did not happen, so instead, kernel32 tried importing/linking/exporting GetLastError, but this symbol is not found in the binary, because it is only a forwarder. This caused kernel32 to import from itself (the behavior when an exported symbol is not found). When importing from itself, the loader would now find the _forwarded_ for GetLastError, and correctly link with ntdll. What should be a one-liner of assembly (inline TEB access) thus became a triple-call indirection (GetLastError@0->StubLastError@0->__impGetLastError@0->__impRtlGetLastWin32Error->RtlGetLastWin32Error. While analyzing these issues, we also realized a strange macro SetLastErrorByStatus that manually tried to perform what there already exists a function for: RtlSetLastNtStatusFromWin32Error. And, in an exciting coda, we also realized that our Server 2003 Kernel32 exports more than a dozen Windows 95 APIs, through an auto-stub generation mechanism within winebuild, that gets linked as an object behind the scenes. [KERNEL32]: Removed all Win95 exports, cleaned up exports. [KERNEL32]: Fixed up set/get error macros by making them inline and/or calling the correct ntdll function. [KERNEL32]: Removed bizare calls to Wine-internal/specific APIs from our core Win32 DLL. [KERNEL32]: Wrote stubs for all functions which should be exported, and set the correct number of parameters for them. [KERNEL32]: Kernel32 is smaller, loads faster, does not export Windows 95 functions, does not export non-existing functions, and does not import from itself anymore. Note: This is one of the many failings of RBUILD the CMAKE system has helped us discover. I believe these issues are serious enough to warrant an immediate sync with trunk, but rest assured, there are many more completely broken, infinitely-regressing things that we discovered while switching to CMAKE. svn path=/trunk/; revision=48475
2010-08-07 05:02:58 +00:00
#define NDEBUG
#include <debug.h>
DEBUG_CHANNEL(kernel32file);
/* TYPES **********************************************************************/
#define FIND_DATA_SIZE 0x4000
#define FIND_DEVICE_HANDLE ((HANDLE)0x1)
typedef enum _FIND_DATA_TYPE
{
FindFile = 1,
FindStream = 2
} FIND_DATA_TYPE;
/*
* FILE_FULL_DIR_INFORMATION and FILE_BOTH_DIR_INFORMATION structures layout.
*
*
* struct FILE_FULL_DIR_INFORMATION | struct FILE_BOTH_DIR_INFORMATION
* ------------------------------------+---------------------------------------
* ULONG NextEntryOffset; | ULONG NextEntryOffset;
* ULONG FileIndex; | ULONG FileIndex;
* LARGE_INTEGER CreationTime; | LARGE_INTEGER CreationTime;
* LARGE_INTEGER LastAccessTime; | LARGE_INTEGER LastAccessTime;
* LARGE_INTEGER LastWriteTime; | LARGE_INTEGER LastWriteTime;
* LARGE_INTEGER ChangeTime; | LARGE_INTEGER ChangeTime;
* LARGE_INTEGER EndOfFile; | LARGE_INTEGER EndOfFile;
* LARGE_INTEGER AllocationSize; | LARGE_INTEGER AllocationSize;
* ULONG FileAttributes; | ULONG FileAttributes;
* ULONG FileNameLength; | ULONG FileNameLength;
* ULONG EaSize; | ULONG EaSize;
* ------------------------------------+---------------------------------------
* WCHAR FileName[1]; | CCHAR ShortNameLength;
* | WCHAR ShortName[12];
* | WCHAR FileName[1];
*
* Therefore we can use pointers to FILE_FULL_DIR_INFORMATION when one doesn't
* want to refer to the ShortName* fields and FileName (useful for implementing
* the FindExInfoBasic functionality for FindFirstFileEx), however a cast to
* FILE_BOTH_DIR_INFORMATION is required when one wants to use FileName and
* ShortName* fields (needed for the FindExInfoStandard functionality).
*
*/
typedef union _DIR_INFORMATION
{
PVOID DirInfo;
PFILE_FULL_DIR_INFORMATION FullDirInfo;
PFILE_BOTH_DIR_INFORMATION BothDirInfo;
} DIR_INFORMATION;
typedef struct _FIND_FILE_DATA
{
HANDLE Handle;
FINDEX_INFO_LEVELS InfoLevel;
FINDEX_SEARCH_OPS SearchOp;
/*
* For handling STATUS_BUFFER_OVERFLOW errors emitted by
* NtQueryDirectoryFile in the FindNextFile function.
*/
BOOLEAN HasMoreData;
/*
* "Pointer" to the next file info structure in the buffer.
* The type is defined by the 'InfoLevel' parameter.
*/
DIR_INFORMATION NextDirInfo;
BYTE Buffer[FIND_DATA_SIZE];
} FIND_FILE_DATA, *PFIND_FILE_DATA;
typedef struct _FIND_STREAM_DATA
{
STREAM_INFO_LEVELS InfoLevel;
PFILE_STREAM_INFORMATION FileStreamInfo;
PFILE_STREAM_INFORMATION CurrentInfo;
} FIND_STREAM_DATA, *PFIND_STREAM_DATA;
typedef struct _FIND_DATA_HANDLE
{
FIND_DATA_TYPE Type;
RTL_CRITICAL_SECTION Lock;
/*
* Pointer to the following finding data, located at
* (this + 1). The type is defined by the 'Type' parameter.
*/
union
{
PFIND_FILE_DATA FindFileData;
PFIND_STREAM_DATA FindStreamData;
} u;
} FIND_DATA_HANDLE, *PFIND_DATA_HANDLE;
/* PRIVATE FUNCTIONS **********************************************************/
static VOID
CopyDeviceFindData(OUT LPWIN32_FIND_DATAW lpFindFileData,
IN LPCWSTR lpFileName,
IN ULONG DeviceNameInfo)
{
LPCWSTR DeviceName;
SIZE_T Length;
_SEH2_TRY
{
/* DeviceNameInfo == { USHORT Offset; USHORT Length } */
Length = (SIZE_T)(DeviceNameInfo & 0xFFFF);
DeviceName = (LPCWSTR)((ULONG_PTR)lpFileName + ((DeviceNameInfo >> 16) & 0xFFFF));
/* Return the data */
RtlZeroMemory(lpFindFileData, sizeof(*lpFindFileData));
lpFindFileData->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE;
RtlStringCbCopyNW(lpFindFileData->cFileName,
sizeof(lpFindFileData->cFileName),
DeviceName, Length);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
}
_SEH2_END;
return;
}
static VOID
CopyFindData(OUT LPWIN32_FIND_DATAW lpFindFileData,
IN FINDEX_INFO_LEVELS fInfoLevelId,
IN DIR_INFORMATION DirInfo)
{
#define ULARGE_INTEGER_2_FILETIME(ft, ul) \
do { \
(ft).dwHighDateTime = (ul).u.HighPart; \
(ft).dwLowDateTime = (ul).u.LowPart ; \
} while(0)
_SEH2_TRY
{
RtlZeroMemory(lpFindFileData, sizeof(*lpFindFileData));
lpFindFileData->dwFileAttributes = DirInfo.FullDirInfo->FileAttributes;
ULARGE_INTEGER_2_FILETIME(lpFindFileData->ftCreationTime, DirInfo.FullDirInfo->CreationTime);
ULARGE_INTEGER_2_FILETIME(lpFindFileData->ftLastAccessTime, DirInfo.FullDirInfo->LastAccessTime);
ULARGE_INTEGER_2_FILETIME(lpFindFileData->ftLastWriteTime, DirInfo.FullDirInfo->LastWriteTime);
lpFindFileData->nFileSizeHigh = DirInfo.FullDirInfo->EndOfFile.u.HighPart;
lpFindFileData->nFileSizeLow = DirInfo.FullDirInfo->EndOfFile.u.LowPart;
/* dwReserved0 contains the NTFS reparse point tag, if any. */
if (DirInfo.FullDirInfo->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
lpFindFileData->dwReserved0 = DirInfo.FullDirInfo->EaSize;
else
lpFindFileData->dwReserved0 = 0;
/* Unused dwReserved1 field */
lpFindFileData->dwReserved1 = 0;
if (fInfoLevelId == FindExInfoStandard)
{
RtlStringCbCopyNW(lpFindFileData->cFileName,
sizeof(lpFindFileData->cFileName),
DirInfo.BothDirInfo->FileName,
DirInfo.BothDirInfo->FileNameLength);
RtlStringCbCopyNW(lpFindFileData->cAlternateFileName,
sizeof(lpFindFileData->cAlternateFileName),
DirInfo.BothDirInfo->ShortName,
DirInfo.BothDirInfo->ShortNameLength);
}
else if (fInfoLevelId == FindExInfoBasic)
{
RtlStringCbCopyNW(lpFindFileData->cFileName,
sizeof(lpFindFileData->cFileName),
DirInfo.FullDirInfo->FileName,
DirInfo.FullDirInfo->FileNameLength);
lpFindFileData->cAlternateFileName[0] = UNICODE_NULL;
}
else
{
/* Invalid InfoLevelId */
ASSERT(FALSE);
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
}
_SEH2_END;
return;
}
static VOID
CopyStreamData(IN OUT PFIND_STREAM_DATA FindStreamData,
OUT PWIN32_FIND_STREAM_DATA lpFindStreamData)
{
_SEH2_TRY
{
ASSERT(FindStreamData->CurrentInfo);
switch (FindStreamData->InfoLevel)
{
case FindStreamInfoStandard:
{
ULONG StreamNameLen = min(FindStreamData->CurrentInfo->StreamNameLength,
sizeof(lpFindStreamData->cStreamName) - sizeof(WCHAR));
RtlZeroMemory(lpFindStreamData, sizeof(*lpFindStreamData));
lpFindStreamData->StreamSize.QuadPart = FindStreamData->CurrentInfo->StreamSize.QuadPart;
RtlCopyMemory(lpFindStreamData->cStreamName,
FindStreamData->CurrentInfo->StreamName,
StreamNameLen);
lpFindStreamData->cStreamName[StreamNameLen / sizeof(WCHAR)] = UNICODE_NULL;
break;
}
default:
{
/* Invalid InfoLevel */
ASSERT(FALSE);
break;
}
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
}
_SEH2_END;
return;
}
/* PUBLIC FUNCTIONS ***********************************************************/
2003-07-10 Casper S. Hornstrup <chorns@users.sourceforge.net> * lib/kernel32/debug/break.c: Add @implemented and @unimplemented to APIs. * lib/kernel32/debug/debugger.c: Ditto. * lib/kernel32/debug/output.c: Ditto. * lib/kernel32/except/except.c: Ditto. * lib/kernel32/file/backup.c: Ditto. * lib/kernel32/file/cnotify.c: Ditto. * lib/kernel32/file/copy.c: Ditto. * lib/kernel32/file/create.c: Ditto. * lib/kernel32/file/curdir.c: Ditto. * lib/kernel32/file/delete.c: Ditto. * lib/kernel32/file/deviceio.c: Ditto. * lib/kernel32/file/dir.c: Ditto. * lib/kernel32/file/dosdev.c: Ditto. * lib/kernel32/file/file.c: Ditto. * lib/kernel32/file/find.c: Ditto. * lib/kernel32/file/iocompl.c: Ditto. * lib/kernel32/file/lfile.c: Ditto. * lib/kernel32/file/lock.c: Ditto. * lib/kernel32/file/mailslot.c: Ditto. * lib/kernel32/file/move.c: Ditto. * lib/kernel32/file/npipe.c: Ditto. * lib/kernel32/file/pipe.c: Ditto. * lib/kernel32/file/rw.c: Ditto. * lib/kernel32/file/tape.c: Ditto. * lib/kernel32/file/volume.c: Ditto. * lib/kernel32/mem/global.c: Ditto. * lib/kernel32/mem/heap.c: Ditto. * lib/kernel32/mem/isbad.c: Ditto. * lib/kernel32/mem/local.c: Ditto. * lib/kernel32/mem/procmem.c: Ditto. * lib/kernel32/mem/section.c: Ditto. * lib/kernel32/mem/virtual.c: Ditto. * lib/kernel32/misc/atom.c: Ditto. * lib/kernel32/misc/comm.c: Ditto. * lib/kernel32/misc/computername.c: Ditto. * lib/kernel32/misc/console.c: Ditto. * lib/kernel32/misc/env.c: Ditto. * lib/kernel32/misc/error.c: Ditto. * lib/kernel32/misc/errormsg.c: Ditto. * lib/kernel32/misc/handle.c: Ditto. * lib/kernel32/misc/ldr.c: Ditto. * lib/kernel32/misc/mbchars.c: Ditto. * lib/kernel32/misc/muldiv.c: Ditto. * lib/kernel32/misc/perfcnt.c: Ditto. * lib/kernel32/misc/profile.c: Ditto. * lib/kernel32/misc/res.c: Ditto. * lib/kernel32/misc/stubs.c: Ditto. * lib/kernel32/misc/sysinfo.c: Ditto. * lib/kernel32/misc/time.c: Ditto. * lib/kernel32/misc/toolhelp.c: Ditto. * lib/kernel32/process/cmdline.c: Ditto. * lib/kernel32/process/create.c: Ditto. * lib/kernel32/process/proc.c: Ditto. * lib/kernel32/process/session.c: Ditto. * lib/kernel32/string/lstring.c: Ditto. * lib/kernel32/synch/critical.c: Ditto. * lib/kernel32/synch/event.c: Ditto. * lib/kernel32/synch/intrlck.c: Ditto. * lib/kernel32/synch/mutex.c: Ditto. * lib/kernel32/synch/sem.c: Ditto. * lib/kernel32/synch/timer.c: Ditto. * lib/kernel32/synch/wait.c: Ditto. * lib/kernel32/thread/fiber.c: Ditto. * lib/kernel32/thread/fls.c: Ditto. * lib/kernel32/thread/thread.c: Ditto. * lib/kernel32/thread/tls.c: Ditto. svn path=/trunk/; revision=5045
2003-07-10 18:50:51 +00:00
/*
* @implemented
2003-07-10 Casper S. Hornstrup <chorns@users.sourceforge.net> * lib/kernel32/debug/break.c: Add @implemented and @unimplemented to APIs. * lib/kernel32/debug/debugger.c: Ditto. * lib/kernel32/debug/output.c: Ditto. * lib/kernel32/except/except.c: Ditto. * lib/kernel32/file/backup.c: Ditto. * lib/kernel32/file/cnotify.c: Ditto. * lib/kernel32/file/copy.c: Ditto. * lib/kernel32/file/create.c: Ditto. * lib/kernel32/file/curdir.c: Ditto. * lib/kernel32/file/delete.c: Ditto. * lib/kernel32/file/deviceio.c: Ditto. * lib/kernel32/file/dir.c: Ditto. * lib/kernel32/file/dosdev.c: Ditto. * lib/kernel32/file/file.c: Ditto. * lib/kernel32/file/find.c: Ditto. * lib/kernel32/file/iocompl.c: Ditto. * lib/kernel32/file/lfile.c: Ditto. * lib/kernel32/file/lock.c: Ditto. * lib/kernel32/file/mailslot.c: Ditto. * lib/kernel32/file/move.c: Ditto. * lib/kernel32/file/npipe.c: Ditto. * lib/kernel32/file/pipe.c: Ditto. * lib/kernel32/file/rw.c: Ditto. * lib/kernel32/file/tape.c: Ditto. * lib/kernel32/file/volume.c: Ditto. * lib/kernel32/mem/global.c: Ditto. * lib/kernel32/mem/heap.c: Ditto. * lib/kernel32/mem/isbad.c: Ditto. * lib/kernel32/mem/local.c: Ditto. * lib/kernel32/mem/procmem.c: Ditto. * lib/kernel32/mem/section.c: Ditto. * lib/kernel32/mem/virtual.c: Ditto. * lib/kernel32/misc/atom.c: Ditto. * lib/kernel32/misc/comm.c: Ditto. * lib/kernel32/misc/computername.c: Ditto. * lib/kernel32/misc/console.c: Ditto. * lib/kernel32/misc/env.c: Ditto. * lib/kernel32/misc/error.c: Ditto. * lib/kernel32/misc/errormsg.c: Ditto. * lib/kernel32/misc/handle.c: Ditto. * lib/kernel32/misc/ldr.c: Ditto. * lib/kernel32/misc/mbchars.c: Ditto. * lib/kernel32/misc/muldiv.c: Ditto. * lib/kernel32/misc/perfcnt.c: Ditto. * lib/kernel32/misc/profile.c: Ditto. * lib/kernel32/misc/res.c: Ditto. * lib/kernel32/misc/stubs.c: Ditto. * lib/kernel32/misc/sysinfo.c: Ditto. * lib/kernel32/misc/time.c: Ditto. * lib/kernel32/misc/toolhelp.c: Ditto. * lib/kernel32/process/cmdline.c: Ditto. * lib/kernel32/process/create.c: Ditto. * lib/kernel32/process/proc.c: Ditto. * lib/kernel32/process/session.c: Ditto. * lib/kernel32/string/lstring.c: Ditto. * lib/kernel32/synch/critical.c: Ditto. * lib/kernel32/synch/event.c: Ditto. * lib/kernel32/synch/intrlck.c: Ditto. * lib/kernel32/synch/mutex.c: Ditto. * lib/kernel32/synch/sem.c: Ditto. * lib/kernel32/synch/timer.c: Ditto. * lib/kernel32/synch/wait.c: Ditto. * lib/kernel32/thread/fiber.c: Ditto. * lib/kernel32/thread/fls.c: Ditto. * lib/kernel32/thread/thread.c: Ditto. * lib/kernel32/thread/tls.c: Ditto. svn path=/trunk/; revision=5045
2003-07-10 18:50:51 +00:00
*/
HANDLE
WINAPI
FindFirstFileA(IN LPCSTR lpFileName,
OUT LPWIN32_FIND_DATAA lpFindFileData)
{
HANDLE hSearch;
NTSTATUS Status;
ANSI_STRING Ansi;
UNICODE_STRING UTF8;
PUNICODE_STRING lpFileNameW;
WIN32_FIND_DATAW FindFileDataW;
lpFileNameW = Basep8BitStringToStaticUnicodeString(lpFileName);
if (!lpFileNameW) return INVALID_HANDLE_VALUE;
hSearch = FindFirstFileExW(lpFileNameW->Buffer,
FindExInfoStandard,
&FindFileDataW,
FindExSearchNameMatch,
NULL, 0);
if (hSearch == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE;
RtlCopyMemory(lpFindFileData,
&FindFileDataW,
FIELD_OFFSET(WIN32_FIND_DATAA, cFileName));
RtlInitUnicodeString(&UTF8, FindFileDataW.cFileName);
Ansi.Buffer = lpFindFileData->cFileName;
Ansi.Length = 0;
Ansi.MaximumLength = sizeof(lpFindFileData->cFileName);
Status = BasepUnicodeStringTo8BitString(&Ansi, &UTF8, FALSE);
if (!NT_SUCCESS(Status))
{
FindClose(hSearch);
BaseSetLastNTError(Status);
return INVALID_HANDLE_VALUE;
}
RtlInitUnicodeString(&UTF8, FindFileDataW.cAlternateFileName);
Ansi.Buffer = lpFindFileData->cAlternateFileName;
Ansi.Length = 0;
Ansi.MaximumLength = sizeof(lpFindFileData->cAlternateFileName);
Status = BasepUnicodeStringTo8BitString(&Ansi, &UTF8, FALSE);
if (!NT_SUCCESS(Status))
{
FindClose(hSearch);
BaseSetLastNTError(Status);
return INVALID_HANDLE_VALUE;
}
return hSearch;
}
/*
* @implemented
*/
HANDLE
WINAPI
FindFirstFileW(IN LPCWSTR lpFileName,
OUT LPWIN32_FIND_DATAW lpFindFileData)
{
return FindFirstFileExW(lpFileName,
FindExInfoStandard,
lpFindFileData,
FindExSearchNameMatch,
NULL, 0);
}
/*
* @implemented
*/
BOOL
WINAPI
FindNextFileA(IN HANDLE hFindFile,
OUT LPWIN32_FIND_DATAA lpFindFileData)
{
NTSTATUS Status;
ANSI_STRING Ansi;
UNICODE_STRING UTF8;
WIN32_FIND_DATAW FindFileDataW;
if (!FindNextFileW(hFindFile, &FindFileDataW))
return FALSE;
RtlCopyMemory(lpFindFileData,
&FindFileDataW,
FIELD_OFFSET(WIN32_FIND_DATAA, cFileName));
RtlInitUnicodeString(&UTF8, FindFileDataW.cFileName);
Ansi.Buffer = lpFindFileData->cFileName;
Ansi.Length = 0;
Ansi.MaximumLength = sizeof(lpFindFileData->cFileName);
Status = BasepUnicodeStringTo8BitString(&Ansi, &UTF8, FALSE);
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(Status);
return FALSE;
}
RtlInitUnicodeString(&UTF8, FindFileDataW.cAlternateFileName);
Ansi.Buffer = lpFindFileData->cAlternateFileName;
Ansi.Length = 0;
Ansi.MaximumLength = sizeof(lpFindFileData->cAlternateFileName);
Status = BasepUnicodeStringTo8BitString(&Ansi, &UTF8, FALSE);
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(Status);
return FALSE;
}
return TRUE;
}
/*
* @implemented
*/
BOOL
WINAPI
FindNextFileW(IN HANDLE hFindFile,
OUT LPWIN32_FIND_DATAW lpFindFileData)
{
NTSTATUS Status = STATUS_SUCCESS;
DIR_INFORMATION FoundFile = {NULL};
TRACE("FindNextFileW(%p, 0x%p)\n", hFindFile, lpFindFileData);
if (hFindFile != FIND_DEVICE_HANDLE)
{
PFIND_DATA_HANDLE FindDataHandle = (PFIND_DATA_HANDLE)hFindFile;
PFIND_FILE_DATA FindFileData;
FINDEX_INFO_LEVELS InfoLevel;
IO_STATUS_BLOCK IoStatusBlock;
DIR_INFORMATION DirInfo = {NULL}, NextDirInfo = {NULL};
if (hFindFile == NULL || hFindFile == INVALID_HANDLE_VALUE ||
FindDataHandle->Type != FindFile)
{
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
RtlEnterCriticalSection(&FindDataHandle->Lock);
FindFileData = FindDataHandle->u.FindFileData;
InfoLevel = FindFileData->InfoLevel;
do
{
if (FindFileData->NextDirInfo.DirInfo == NULL)
{
Status = NtQueryDirectoryFile(FindFileData->Handle,
NULL, NULL, NULL,
&IoStatusBlock,
&FindFileData->Buffer,
sizeof(FindFileData->Buffer),
(InfoLevel == FindExInfoStandard
? FileBothDirectoryInformation
: FileFullDirectoryInformation),
FALSE,
NULL, /* Use the file pattern from the first call */
FALSE);
if (Status == STATUS_BUFFER_OVERFLOW)
{
FindFileData->HasMoreData = TRUE;
Status = STATUS_SUCCESS;
}
else
{
if (!NT_SUCCESS(Status)) break;
FindFileData->HasMoreData = FALSE;
}
FindFileData->NextDirInfo.DirInfo = &FindFileData->Buffer;
}
DirInfo = FindFileData->NextDirInfo;
if (DirInfo.FullDirInfo->NextEntryOffset != 0)
{
ULONG_PTR BufferEnd = (ULONG_PTR)&FindFileData->Buffer + sizeof(FindFileData->Buffer);
PWSTR pFileName;
NextDirInfo.DirInfo = FindFileData->NextDirInfo.DirInfo =
(PVOID)((ULONG_PTR)DirInfo.DirInfo + DirInfo.FullDirInfo->NextEntryOffset);
pFileName = (InfoLevel == FindExInfoStandard
? NextDirInfo.BothDirInfo->FileName
: NextDirInfo.FullDirInfo->FileName);
/* Be paranoid and make sure that the next entry is completely there */
if (BufferEnd < (ULONG_PTR)NextDirInfo.DirInfo ||
BufferEnd < (ULONG_PTR)&NextDirInfo.FullDirInfo->FileNameLength + sizeof(NextDirInfo.FullDirInfo->FileNameLength) ||
BufferEnd <= (ULONG_PTR)((ULONG_PTR)pFileName + NextDirInfo.FullDirInfo->FileNameLength))
{
FindFileData->NextDirInfo.DirInfo = NULL;
}
}
else
{
FindFileData->NextDirInfo.DirInfo = NULL;
}
if ((FindFileData->SearchOp != FindExSearchLimitToDirectories) ||
(DirInfo.FullDirInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
FoundFile = DirInfo;
}
} while ( FoundFile.DirInfo == NULL && (FindFileData->NextDirInfo.DirInfo || FindFileData->HasMoreData) );
if (FoundFile.DirInfo != NULL)
{
/* Return the information */
CopyFindData(lpFindFileData, InfoLevel, FoundFile);
}
RtlLeaveCriticalSection(&FindDataHandle->Lock);
}
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(Status);
return FALSE;
}
else if (FoundFile.DirInfo == NULL)
{
SetLastError(ERROR_NO_MORE_FILES);
return FALSE;
}
return TRUE;
}
/*
* @implemented
*/
BOOL
WINAPI
FindClose(HANDLE hFindFile)
{
TRACE("FindClose(hFindFile %p)\n", hFindFile);
if (hFindFile == FIND_DEVICE_HANDLE)
return TRUE;
if (!hFindFile || hFindFile == INVALID_HANDLE_VALUE)
{
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
/* Protect with SEH against closing attempts on invalid handles. */
_SEH2_TRY
{
PFIND_DATA_HANDLE FindDataHandle = (PFIND_DATA_HANDLE)hFindFile;
switch (FindDataHandle->Type)
{
case FindFile:
{
RtlEnterCriticalSection(&FindDataHandle->Lock);
NtClose(FindDataHandle->u.FindFileData->Handle);
RtlLeaveCriticalSection(&FindDataHandle->Lock);
RtlDeleteCriticalSection(&FindDataHandle->Lock);
break;
}
case FindStream:
{
RtlEnterCriticalSection(&FindDataHandle->Lock);
if (FindDataHandle->u.FindStreamData->FileStreamInfo != NULL)
{
RtlFreeHeap(RtlGetProcessHeap(), 0,
FindDataHandle->u.FindStreamData->FileStreamInfo);
}
RtlLeaveCriticalSection(&FindDataHandle->Lock);
RtlDeleteCriticalSection(&FindDataHandle->Lock);
break;
}
default:
{
SetLastError(ERROR_INVALID_HANDLE);
_SEH2_YIELD(return FALSE);
}
}
RtlFreeHeap(RtlGetProcessHeap(), 0, FindDataHandle);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
BaseSetLastNTError(_SEH2_GetExceptionCode());
_SEH2_YIELD(return FALSE);
}
_SEH2_END;
return TRUE;
}
/*
* @unimplemented
*/
HANDLE
WINAPI
FindFirstFileExA(IN LPCSTR lpFileName,
IN FINDEX_INFO_LEVELS fInfoLevelId,
OUT LPVOID lpFindFileData,
IN FINDEX_SEARCH_OPS fSearchOp,
LPVOID lpSearchFilter,
IN DWORD dwAdditionalFlags)
{
HANDLE hSearch;
NTSTATUS Status;
ANSI_STRING Ansi;
UNICODE_STRING UTF8;
PUNICODE_STRING lpFileNameW;
WIN32_FIND_DATAW FindFileDataW;
LPWIN32_FIND_DATAA lpFindFileDataA = (LPWIN32_FIND_DATAA)lpFindFileData;
if ((fInfoLevelId != FindExInfoStandard && fInfoLevelId != FindExInfoBasic) ||
fSearchOp == FindExSearchLimitToDevices ||
dwAdditionalFlags & ~FIND_FIRST_EX_CASE_SENSITIVE /* only supported flag for now */)
{
SetLastError(fSearchOp == FindExSearchLimitToDevices
? ERROR_NOT_SUPPORTED
: ERROR_INVALID_PARAMETER);
return INVALID_HANDLE_VALUE;
}
lpFileNameW = Basep8BitStringToStaticUnicodeString(lpFileName);
if (!lpFileNameW) return INVALID_HANDLE_VALUE;
hSearch = FindFirstFileExW(lpFileNameW->Buffer,
fInfoLevelId,
&FindFileDataW,
fSearchOp,
lpSearchFilter,
dwAdditionalFlags);
if (hSearch == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE;
RtlCopyMemory(lpFindFileDataA,
&FindFileDataW,
FIELD_OFFSET(WIN32_FIND_DATAA, cFileName));
RtlInitUnicodeString(&UTF8, FindFileDataW.cFileName);
Ansi.Buffer = lpFindFileDataA->cFileName;
Ansi.Length = 0;
Ansi.MaximumLength = sizeof(lpFindFileDataA->cFileName);
Status = BasepUnicodeStringTo8BitString(&Ansi, &UTF8, FALSE);
if (!NT_SUCCESS(Status))
{
FindClose(hSearch);
BaseSetLastNTError(Status);
return INVALID_HANDLE_VALUE;
}
if (fInfoLevelId != FindExInfoBasic)
{
RtlInitUnicodeString(&UTF8, FindFileDataW.cAlternateFileName);
Ansi.Buffer = lpFindFileDataA->cAlternateFileName;
Ansi.Length = 0;
Ansi.MaximumLength = sizeof(lpFindFileDataA->cAlternateFileName);
Status = BasepUnicodeStringTo8BitString(&Ansi, &UTF8, FALSE);
if (!NT_SUCCESS(Status))
{
FindClose(hSearch);
BaseSetLastNTError(Status);
return INVALID_HANDLE_VALUE;
}
}
else
{
lpFindFileDataA->cAlternateFileName[0] = ANSI_NULL;
}
return hSearch;
}
/*
* @unimplemented
*/
HANDLE
WINAPI
FindFirstFileExW(IN LPCWSTR lpFileName,
IN FINDEX_INFO_LEVELS fInfoLevelId,
OUT LPVOID lpFindFileData,
IN FINDEX_SEARCH_OPS fSearchOp,
LPVOID lpSearchFilter,
IN DWORD dwAdditionalFlags)
{
TRACE("FindFirstFileExW(lpFileName %S)\n", lpFileName);
if ((fInfoLevelId != FindExInfoStandard && fInfoLevelId != FindExInfoBasic) ||
fSearchOp == FindExSearchLimitToDevices ||
dwAdditionalFlags & ~FIND_FIRST_EX_CASE_SENSITIVE /* only supported flag for now */)
{
SetLastError(fSearchOp == FindExSearchLimitToDevices
? ERROR_NOT_SUPPORTED
: ERROR_INVALID_PARAMETER);
return INVALID_HANDLE_VALUE;
}
if (fSearchOp == FindExSearchNameMatch ||
fSearchOp == FindExSearchLimitToDirectories)
{
LPWIN32_FIND_DATAW Win32FindData = (LPWIN32_FIND_DATAW)lpFindFileData;
PFIND_DATA_HANDLE FindDataHandle;
PFIND_FILE_DATA FindFileData;
UNICODE_STRING NtPath, FilePattern, FileName;
PWSTR NtPathBuffer;
RTL_RELATIVE_NAME_U RelativePath;
ULONG DeviceNameInfo = 0;
NTSTATUS Status;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
HANDLE hDirectory = NULL;
BOOLEAN HadADot = FALSE;
/*
* May represent many FILE_BOTH_DIR_INFORMATION
* or many FILE_FULL_DIR_INFORMATION structures.
* NOTE: NtQueryDirectoryFile requires the buffer to be ULONG-aligned
*/
DECLSPEC_ALIGN(4) BYTE DirectoryInfo[FIND_DATA_SIZE];
DIR_INFORMATION DirInfo = { .DirInfo = &DirectoryInfo };
/* The search filter is always unused */
if (lpSearchFilter)
{
SetLastError(ERROR_INVALID_PARAMETER);
return INVALID_HANDLE_VALUE;
}
RtlInitUnicodeString(&FileName, lpFileName);
if (FileName.Length != 0 && FileName.Buffer[FileName.Length / sizeof(WCHAR) - 1] == L'.')
{
HadADot = TRUE;
}
if (!RtlDosPathNameToNtPathName_U(lpFileName,
&NtPath,
(PCWSTR*)&FilePattern.Buffer,
&RelativePath))
{
SetLastError(ERROR_PATH_NOT_FOUND);
return INVALID_HANDLE_VALUE;
}
DPRINT("lpFileName = '%S'\n", lpFileName);
DPRINT("FilePattern.Buffer = '%S'\n", FilePattern.Buffer);
DPRINT("RelativePath.RelativeName = '%wZ'\n", &RelativePath.RelativeName);
DPRINT("NtPath.Buffer = '%S'\n", NtPath.Buffer);
DPRINT("NtPath - Before = '%wZ'\n", &NtPath);
/* Save the buffer pointer for later, we need to free it! */
NtPathBuffer = NtPath.Buffer;
/*
* Contrary to what Windows does, check NOW whether or not
* lpFileName is a DOS driver. Therefore we don't have to
* write broken code to check that.
*/
if (!FilePattern.Buffer || !*FilePattern.Buffer)
{
/* No file pattern specified, or DOS device */
DeviceNameInfo = RtlIsDosDeviceName_U(lpFileName);
if (DeviceNameInfo != 0)
{
RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathBuffer);
/* OK, it's really a DOS device */
CopyDeviceFindData(Win32FindData, lpFileName, DeviceNameInfo);
return FIND_DEVICE_HANDLE;
}
}
/* A file pattern was specified, or it was not a DOS device */
/* If there is a file pattern then determine its length */
if (FilePattern.Buffer != NULL)
{
FilePattern.Length = NtPath.Length -
(USHORT)((ULONG_PTR)FilePattern.Buffer - (ULONG_PTR)NtPath.Buffer);
}
else
{
FilePattern.Length = 0;
}
FilePattern.MaximumLength = FilePattern.Length;
if (RelativePath.RelativeName.Length != 0 &&
RelativePath.RelativeName.Buffer != FilePattern.Buffer)
{
if (FilePattern.Buffer != NULL)
{
/* This is a relative path to RelativePath.ContainingDirectory, adjust NtPath! */
NtPath.Length = NtPath.MaximumLength =
(USHORT)((ULONG_PTR)FilePattern.Buffer - (ULONG_PTR)RelativePath.RelativeName.Buffer);
NtPath.Buffer = RelativePath.RelativeName.Buffer;
}
}
else
{
/* This is an absolute path, NtPath receives the full path */
RelativePath.ContainingDirectory = NULL;
if (FilePattern.Buffer != NULL)
{
NtPath.Length = NtPath.MaximumLength =
(USHORT)((ULONG_PTR)FilePattern.Buffer - (ULONG_PTR)NtPath.Buffer);
}
}
DPRINT("NtPath - After = '%wZ'\n", &NtPath);
DPRINT("FilePattern = '%wZ'\n", &FilePattern);
DPRINT("RelativeTo = 0x%p\n", RelativePath.ContainingDirectory);
InitializeObjectAttributes(&ObjectAttributes,
&NtPath,
(dwAdditionalFlags & FIND_FIRST_EX_CASE_SENSITIVE) ? 0 : OBJ_CASE_INSENSITIVE,
RelativePath.ContainingDirectory,
NULL);
Status = NtOpenFile(&hDirectory,
FILE_LIST_DIRECTORY | SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
if (!NT_SUCCESS(Status))
{
RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathBuffer);
/* Adjust the last error codes */
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
Status = STATUS_OBJECT_PATH_NOT_FOUND;
else if (Status == STATUS_OBJECT_TYPE_MISMATCH)
Status = STATUS_OBJECT_PATH_NOT_FOUND;
BaseSetLastNTError(Status);
return INVALID_HANDLE_VALUE;
}
/*
* Fail if there is not any file pattern,
* since we are not looking for a device.
*/
if (FilePattern.Length == 0)
{
NtClose(hDirectory);
RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathBuffer);
SetLastError(ERROR_FILE_NOT_FOUND);
return INVALID_HANDLE_VALUE;
}
/* Change pattern: "*.*" --> "*" */
if (FilePattern.Length == 6 &&
RtlCompareMemory(FilePattern.Buffer, L"*.*", 6) == 6)
{
FilePattern.Length = 2;
}
else
{
/* Translate wildcard from "real" world to DOS world for lower interpretation */
USHORT PatternIndex = 0;
while (PatternIndex < FilePattern.Length / sizeof(WCHAR))
{
if (PatternIndex > 0)
{
if (FilePattern.Buffer[PatternIndex] == L'.' &&
FilePattern.Buffer[PatternIndex - 1] == L'*')
{
FilePattern.Buffer[PatternIndex - 1] = L'<';
}
}
if (FilePattern.Buffer[PatternIndex] == L'?')
{
FilePattern.Buffer[PatternIndex] = L'>';
if (PatternIndex > 0)
{
if (FilePattern.Buffer[PatternIndex - 1] == L'.')
{
FilePattern.Buffer[PatternIndex - 1] = L'\"';
}
}
}
else if (FilePattern.Buffer[PatternIndex] == L'*')
{
if (PatternIndex > 0)
{
if (FilePattern.Buffer[PatternIndex - 1] == L'.')
{
FilePattern.Buffer[PatternIndex - 1] = L'\"';
}
}
}
PatternIndex++;
}
/* Handle partial wc if our last dot was eaten */
if (HadADot)
{
if (FilePattern.Buffer[FilePattern.Length / sizeof(WCHAR) - 1] == L'*')
{
FilePattern.Buffer[FilePattern.Length / sizeof(WCHAR) - 1] = L'<';
}
}
}
Status = NtQueryDirectoryFile(hDirectory,
NULL, NULL, NULL,
&IoStatusBlock,
DirInfo.DirInfo, // == &DirectoryInfo
sizeof(DirectoryInfo),
(fInfoLevelId == FindExInfoStandard
? FileBothDirectoryInformation
: FileFullDirectoryInformation),
TRUE, /* Return a single entry */
&FilePattern,
TRUE);
RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathBuffer);
if (!NT_SUCCESS(Status))
{
NtClose(hDirectory);
BaseSetLastNTError(Status);
return INVALID_HANDLE_VALUE;
}
ASSERT(DirInfo.FullDirInfo->NextEntryOffset == 0);
/* Return the information */
CopyFindData(Win32FindData, fInfoLevelId, DirInfo);
/*
* Initialization of the search handle.
*/
FindDataHandle = RtlAllocateHeap(RtlGetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(FIND_DATA_HANDLE) +
sizeof(FIND_FILE_DATA));
if (!FindDataHandle)
{
NtClose(hDirectory);
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return INVALID_HANDLE_VALUE;
}
FindDataHandle->Type = FindFile;
FindDataHandle->u.FindFileData = (PFIND_FILE_DATA)(FindDataHandle + 1);
FindFileData = FindDataHandle->u.FindFileData;
FindFileData->Handle = hDirectory;
FindFileData->InfoLevel = fInfoLevelId;
FindFileData->SearchOp = fSearchOp;
FindFileData->HasMoreData = FALSE;
FindFileData->NextDirInfo.DirInfo = NULL;
/* The critical section must always be initialized */
Status = RtlInitializeCriticalSection(&FindDataHandle->Lock);
if (!NT_SUCCESS(Status))
{
NtClose(hDirectory);
RtlFreeHeap(RtlGetProcessHeap(), 0, FindDataHandle);
BaseSetLastNTError(Status);
return INVALID_HANDLE_VALUE;
}
return (HANDLE)FindDataHandle;
}
else
{
SetLastError(ERROR_NOT_SUPPORTED);
return INVALID_HANDLE_VALUE;
}
}
/*
* @implemented
*/
HANDLE
WINAPI
FindFirstStreamW(IN LPCWSTR lpFileName,
IN STREAM_INFO_LEVELS InfoLevel,
OUT LPVOID lpFindStreamData,
IN DWORD dwFlags)
{
PFIND_DATA_HANDLE FindDataHandle = NULL;
PFIND_STREAM_DATA FindStreamData;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
UNICODE_STRING NtFilePath;
HANDLE FileHandle = NULL;
NTSTATUS Status;
ULONG BufferSize = 0;
if (dwFlags != 0 || InfoLevel != FindStreamInfoStandard ||
lpFindStreamData == NULL)
{
SetLastError(ERROR_INVALID_PARAMETER);
return INVALID_HANDLE_VALUE;
}
/* Validate and translate the filename */
if (!RtlDosPathNameToNtPathName_U(lpFileName,
&NtFilePath,
NULL, NULL))
{
SetLastError(ERROR_PATH_NOT_FOUND);
return INVALID_HANDLE_VALUE;
}
/* Open the file */
InitializeObjectAttributes(&ObjectAttributes,
&NtFilePath,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = NtCreateFile(&FileHandle,
0,
&ObjectAttributes,
&IoStatusBlock,
NULL, 0,
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN,
0, NULL, 0);
if (!NT_SUCCESS(Status)) goto Cleanup;
/*
* Initialization of the search handle.
*/
FindDataHandle = RtlAllocateHeap(RtlGetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(FIND_DATA_HANDLE) +
sizeof(FIND_STREAM_DATA));
if (!FindDataHandle)
{
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
FindDataHandle->Type = FindStream;
FindDataHandle->u.FindStreamData = (PFIND_STREAM_DATA)(FindDataHandle + 1);
FindStreamData = FindDataHandle->u.FindStreamData;
FindStreamData->InfoLevel = InfoLevel;
FindStreamData->FileStreamInfo = NULL;
FindStreamData->CurrentInfo = NULL;
/* The critical section must always be initialized */
Status = RtlInitializeCriticalSection(&FindDataHandle->Lock);
if (!NT_SUCCESS(Status))
{
RtlFreeHeap(RtlGetProcessHeap(), 0, FindDataHandle);
goto Cleanup;
}
/* Capture all information about the streams */
do
{
BufferSize += 0x1000;
if (FindStreamData->FileStreamInfo == NULL)
{
FindStreamData->FileStreamInfo = RtlAllocateHeap(RtlGetProcessHeap(),
HEAP_ZERO_MEMORY,
BufferSize);
if (FindStreamData->FileStreamInfo == NULL)
{
Status = STATUS_NO_MEMORY;
break;
}
}
else
{
PFILE_STREAM_INFORMATION pfsi;
pfsi = RtlReAllocateHeap(RtlGetProcessHeap(),
0, // HEAP_ZERO_MEMORY,
FindStreamData->FileStreamInfo,
BufferSize);
if (pfsi == NULL)
{
Status = STATUS_NO_MEMORY;
break;
}
FindStreamData->FileStreamInfo = pfsi;
}
Status = NtQueryInformationFile(FileHandle,
&IoStatusBlock,
FindStreamData->FileStreamInfo,
BufferSize,
FileStreamInformation);
} while (Status == STATUS_BUFFER_TOO_SMALL);
if (NT_SUCCESS(Status))
{
/* Select the first stream and return the information */
FindStreamData->CurrentInfo = FindStreamData->FileStreamInfo;
CopyStreamData(FindStreamData, lpFindStreamData);
/* All done */
Status = STATUS_SUCCESS;
}
else
{
if (FindStreamData->FileStreamInfo)
{
RtlFreeHeap(RtlGetProcessHeap(), 0, FindStreamData->FileStreamInfo);
}
RtlFreeHeap(RtlGetProcessHeap(), 0, FindDataHandle);
}
Cleanup:
if (FileHandle) NtClose(FileHandle);
RtlFreeHeap(RtlGetProcessHeap(), 0, NtFilePath.Buffer);
if (NT_SUCCESS(Status))
{
return (HANDLE)FindDataHandle;
}
else
{
BaseSetLastNTError(Status);
return INVALID_HANDLE_VALUE;
}
}
/*
* @implemented
*/
BOOL
WINAPI
FindNextStreamW(IN HANDLE hFindStream,
OUT LPVOID lpFindStreamData)
{
PFIND_DATA_HANDLE FindDataHandle = (PFIND_DATA_HANDLE)hFindStream;
PFIND_STREAM_DATA FindStreamData;
if (hFindStream == NULL || hFindStream == INVALID_HANDLE_VALUE ||
FindDataHandle->Type != FindStream)
{
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
RtlEnterCriticalSection(&FindDataHandle->Lock);
FindStreamData = FindDataHandle->u.FindStreamData;
/* Select next stream if possible */
if (FindStreamData->CurrentInfo->NextEntryOffset != 0)
{
FindStreamData->CurrentInfo = (PFILE_STREAM_INFORMATION)((ULONG_PTR)FindStreamData->CurrentInfo +
FindStreamData->CurrentInfo->NextEntryOffset);
/* Return the information */
CopyStreamData(FindStreamData, lpFindStreamData);
RtlLeaveCriticalSection(&FindDataHandle->Lock);
return TRUE;
}
else
{
RtlLeaveCriticalSection(&FindDataHandle->Lock);
SetLastError(ERROR_HANDLE_EOF);
return FALSE;
}
}
/* EOF */