[KERNEL32]
[reactos.git] / reactos / dll / win32 / kernel32 / client / file / find.c
index 48bac71..3d1903d 100644 (file)
-/* $Id: find.c 53068 2011-08-04 22:18:01Z ion $
- *
+/*
  * COPYRIGHT:       See COPYING in the top level directory
  * PROJECT:         ReactOS system libraries
- * FILE:            lib/kernel32/file/find.c
+ * FILE:            dll/win32/kernel32/client/file/find.c
  * PURPOSE:         Find functions
- * PROGRAMMER:      Ariadne ( ariadne@xs4all.nl)
+ * PROGRAMMERS:     Ariadne (ariadne@xs4all.nl)
  *                  Pierre Schweitzer (pierre.schweitzer@reactos.org)
- * UPDATE HISTORY:
- *                  Created 01/11/98
+ *                  Hermes BELUSCA - MAITO (hermes.belusca@sfr.fr)
  */
 
-/* INCLUDES *****************************************************************/
+/* INCLUDES *******************************************************************/
 
 #include <k32.h>
 #define NDEBUG
 #include <debug.h>
 DEBUG_CHANNEL(kernel32file);
 
-/* TYPES ********************************************************************/
 
-#define FIND_DATA_SIZE 0x4000
+/* TYPES **********************************************************************/
 
-#define FIND_DEVICE_HANDLE ((HANDLE)0x1)
+#define FIND_DATA_SIZE      0x4000
+#define FIND_DEVICE_HANDLE  ((HANDLE)0x1)
 
-typedef struct _KERNEL32_FIND_FILE_DATA
+typedef enum _FIND_DATA_TYPE
 {
-   HANDLE DirectoryHandle;
-   RTL_CRITICAL_SECTION Lock;
-   PFILE_BOTH_DIR_INFORMATION pFileInfo;
-   BOOLEAN DirectoryOnly;
-   BOOLEAN HasMoreData;
-   BOOLEAN HasData;
-   BOOLEAN LockInitialized;
-} KERNEL32_FIND_FILE_DATA, *PKERNEL32_FIND_FILE_DATA;
-
-typedef struct _KERNEL32_FIND_STREAM_DATA
+    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
 {
-    STREAM_INFO_LEVELS InfoLevel;
-    PFILE_STREAM_INFORMATION pFileStreamInfo;
-    PFILE_STREAM_INFORMATION pCurrent;
-} KERNEL32_FIND_STREAM_DATA, *PKERNEL32_FIND_STREAM_DATA;
+    PVOID DirInfo;
+    PFILE_FULL_DIR_INFORMATION FullDirInfo;
+    PFILE_BOTH_DIR_INFORMATION BothDirInfo;
+} DIR_INFORMATION;
 
-typedef enum _KERNEL32_FIND_DATA_TYPE
+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 FildNextFile 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
 {
-    FileFind,
-    StreamFind
-} KERNEL32_FIND_DATA_TYPE;
+    STREAM_INFO_LEVELS InfoLevel;
+    PFILE_STREAM_INFORMATION FileStreamInfo;
+    PFILE_STREAM_INFORMATION CurrentInfo;
+} FIND_STREAM_DATA, *PFIND_STREAM_DATA;
 
-typedef struct _KERNEL32_FIND_DATA_HEADER
+typedef struct _FIND_DATA_HANDLE
 {
-    KERNEL32_FIND_DATA_TYPE Type;
-} KERNEL32_FIND_DATA_HEADER, *PKERNEL32_FIND_DATA_HEADER;
+    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;
 
 
-/* FUNCTIONS ****************************************************************/
+/* PRIVATE FUNCTIONS **********************************************************/
 
 static VOID
-InternalCopyDeviceFindDataW(LPWIN32_FIND_DATAW lpFindFileData,
-                            LPCWSTR lpFileName,
-                            ULONG DeviceNameInfo)
+CopyDeviceFindData(OUT LPWIN32_FIND_DATAW lpFindFileData,
+                   IN LPCWSTR lpFileName,
+                   IN ULONG DeviceNameInfo)
 {
     UNICODE_STRING DeviceName;
 
-    DeviceName.Length = DeviceName.MaximumLength = (USHORT)(DeviceNameInfo & 0xFFFF);
-    DeviceName.Buffer = (LPWSTR)((ULONG_PTR)lpFileName + (DeviceNameInfo >> 16));
+    _SEH2_TRY
+    {
+        /* DeviceNameInfo == { USHORT Offset; USHORT Length } */
+        DeviceName.Length = DeviceName.MaximumLength = (USHORT)(DeviceNameInfo & 0xFFFF);
+        DeviceName.Buffer = (LPWSTR)((ULONG_PTR)lpFileName + ((DeviceNameInfo >> 16) & 0xFFFF));
+
+        /* Return the data */
+        RtlZeroMemory(lpFindFileData, sizeof(*lpFindFileData));
+        lpFindFileData->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE;
+        RtlCopyMemory(lpFindFileData->cFileName,
+                      DeviceName.Buffer,
+                      DeviceName.Length);
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+    }
+    _SEH2_END;
 
-    /* Return the data */
-    RtlZeroMemory(lpFindFileData,
-                  sizeof(*lpFindFileData));
-    lpFindFileData->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE;
-    RtlCopyMemory(lpFindFileData->cFileName,
-                  DeviceName.Buffer,
-                  DeviceName.Length);
+    return;
 }
 
 static VOID
-InternalCopyFindDataW(LPWIN32_FIND_DATAW            lpFindFileData,
-                      PFILE_BOTH_DIR_INFORMATION    lpFileInfo)
+CopyFindData(OUT LPWIN32_FIND_DATAW lpFindFileData,
+             IN FINDEX_INFO_LEVELS fInfoLevelId,
+             IN DIR_INFORMATION DirInfo)
 {
-    lpFindFileData->dwFileAttributes = lpFileInfo->FileAttributes;
+#define ULARGE_INTEGER_2_FILETIME(ft, ul) \
+do { \
+    (ft).dwHighDateTime = (ul).u.HighPart; \
+    (ft).dwLowDateTime  = (ul).u.LowPart ; \
+} while(0)
 
-    lpFindFileData->ftCreationTime.dwHighDateTime = lpFileInfo->CreationTime.u.HighPart;
-    lpFindFileData->ftCreationTime.dwLowDateTime = lpFileInfo->CreationTime.u.LowPart;
-
-    lpFindFileData->ftLastAccessTime.dwHighDateTime = lpFileInfo->LastAccessTime.u.HighPart;
-    lpFindFileData->ftLastAccessTime.dwLowDateTime = lpFileInfo->LastAccessTime.u.LowPart;
-
-    lpFindFileData->ftLastWriteTime.dwHighDateTime = lpFileInfo->LastWriteTime.u.HighPart;
-    lpFindFileData->ftLastWriteTime.dwLowDateTime = lpFileInfo->LastWriteTime.u.LowPart;
-
-    lpFindFileData->nFileSizeHigh = lpFileInfo->EndOfFile.u.HighPart;
-    lpFindFileData->nFileSizeLow = lpFileInfo->EndOfFile.u.LowPart;
+    _SEH2_TRY
+    {
+        RtlZeroMemory(lpFindFileData, sizeof(*lpFindFileData));
 
-    memcpy (lpFindFileData->cFileName, lpFileInfo->FileName, lpFileInfo->FileNameLength);
-    lpFindFileData->cFileName[lpFileInfo->FileNameLength / sizeof(WCHAR)] = 0;
+        lpFindFileData->dwFileAttributes = DirInfo.FullDirInfo->FileAttributes;
 
-    memcpy (lpFindFileData->cAlternateFileName, lpFileInfo->ShortName, lpFileInfo->ShortNameLength);
-    lpFindFileData->cAlternateFileName[lpFileInfo->ShortNameLength / sizeof(WCHAR)] = 0;
-}
+        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;
 
-/*
- * @implemented
- */
-BOOL
-WINAPI
-InternalFindNextFile (
-    HANDLE     hFindFile,
-    PUNICODE_STRING SearchPattern,
-    PVOID lpFindFileData
-    )
-{
-    PKERNEL32_FIND_DATA_HEADER IHeader;
-    PKERNEL32_FIND_FILE_DATA IData;
-    IO_STATUS_BLOCK IoStatusBlock;
-    BOOLEAN Locked = FALSE;
-    PFILE_BOTH_DIR_INFORMATION Buffer, FoundFile = NULL;
-    NTSTATUS Status = STATUS_SUCCESS;
+        /* 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;
 
-    TRACE("InternalFindNextFile(%lx, %wZ)\n", hFindFile, SearchPattern);
+        /* Unused dwReserved1 field */
+        lpFindFileData->dwReserved1 = 0;
 
-    if (hFindFile != FIND_DEVICE_HANDLE)
-    {
-        IHeader = (PKERNEL32_FIND_DATA_HEADER)hFindFile;
-        if (hFindFile == NULL || hFindFile == INVALID_HANDLE_VALUE ||
-            IHeader->Type != FileFind)
+        if (fInfoLevelId == FindExInfoStandard)
         {
-            SetLastError (ERROR_INVALID_HANDLE);
-            return FALSE;
+            RtlCopyMemory(lpFindFileData->cFileName,
+                          DirInfo.BothDirInfo->FileName,
+                          DirInfo.BothDirInfo->FileNameLength);
+            lpFindFileData->cFileName[DirInfo.BothDirInfo->FileNameLength / sizeof(WCHAR)] = UNICODE_NULL;
+
+            RtlCopyMemory(lpFindFileData->cAlternateFileName,
+                          DirInfo.BothDirInfo->ShortName,
+                          DirInfo.BothDirInfo->ShortNameLength);
+            lpFindFileData->cAlternateFileName[DirInfo.BothDirInfo->ShortNameLength / sizeof(WCHAR)] = UNICODE_NULL;
         }
+        else if (fInfoLevelId == FindExInfoBasic)
+        {
+            RtlCopyMemory(lpFindFileData->cFileName,
+                          DirInfo.FullDirInfo->FileName,
+                          DirInfo.FullDirInfo->FileNameLength);
+            lpFindFileData->cFileName[DirInfo.FullDirInfo->FileNameLength / sizeof(WCHAR)] = UNICODE_NULL;
 
-        IData = (PKERNEL32_FIND_FILE_DATA)(IHeader + 1);
-        Buffer = (PFILE_BOTH_DIR_INFORMATION)((ULONG_PTR)IData + sizeof(KERNEL32_FIND_FILE_DATA));
-
-        if (SearchPattern == NULL)
+            lpFindFileData->cAlternateFileName[0] = UNICODE_NULL;
+        }
+        else
         {
-            RtlEnterCriticalSection(&IData->Lock);
-            Locked = TRUE;
+            /* Invalid InfoLevelId */
+            ASSERT(FALSE);
         }
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+    }
+    _SEH2_END;
 
-        do
-        {
-            if (IData->HasData)
-            {
-                if (!IData->DirectoryOnly || (IData->pFileInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY))
-                {
-                    FoundFile = IData->pFileInfo;
-                }
+    return;
+}
 
-                if (IData->pFileInfo->NextEntryOffset != 0)
-                {
-                    ULONG_PTR BufferEnd;
-
-                    IData->pFileInfo = (PFILE_BOTH_DIR_INFORMATION)((ULONG_PTR)IData->pFileInfo + IData->pFileInfo->NextEntryOffset);
-
-                    /* Be paranoid and make sure that the next entry is completely there */
-                    BufferEnd = (ULONG_PTR)Buffer + FIND_DATA_SIZE;
-                    if (BufferEnd < (ULONG_PTR)IData->pFileInfo ||
-                        BufferEnd < (ULONG_PTR)&IData->pFileInfo->FileNameLength + sizeof(IData->pFileInfo->FileNameLength) ||
-                        BufferEnd <= (ULONG_PTR)&IData->pFileInfo->FileName[IData->pFileInfo->FileNameLength])
-                    {
-                        goto NeedMoreData;
-                    }
-                }
-                else
-                {
-NeedMoreData:
-                    IData->HasData = FALSE;
+static VOID
+CopyStreamData(IN OUT PFIND_STREAM_DATA FindStreamData,
+               OUT PWIN32_FIND_STREAM_DATA lpFindStreamData)
+{
+    _SEH2_TRY
+    {
+        ASSERT(FindStreamData->CurrentInfo);
 
-                    if (!IData->HasMoreData)
-                        break;
-                }
-            }
-            else
+        switch (FindStreamData->InfoLevel)
+        {
+            case FindStreamInfoStandard:
             {
-                IData->pFileInfo = Buffer;
-                IData->pFileInfo->NextEntryOffset = 0;
-                Status = NtQueryDirectoryFile (IData->DirectoryHandle,
-                                               NULL,
-                                               NULL,
-                                               NULL,
-                                               &IoStatusBlock,
-                                               (PVOID)IData->pFileInfo,
-                                               FIND_DATA_SIZE,
-                                               FileBothDirectoryInformation,
-                                               FALSE,
-                                               SearchPattern,
-                                               SearchPattern != NULL);
+                ULONG StreamNameLen = min(FindStreamData->CurrentInfo->StreamNameLength,
+                                          sizeof(lpFindStreamData->cStreamName) - sizeof(WCHAR));
 
-                if (Status == STATUS_BUFFER_OVERFLOW)
-                {
-                    IData->HasMoreData = TRUE;
-                    Status = STATUS_SUCCESS;
-                }
-                else
-                {
-                    if (!NT_SUCCESS(Status))
-                        break;
+                RtlZeroMemory(lpFindStreamData, sizeof(*lpFindStreamData));
 
-                    IData->HasMoreData = FALSE;
-                }
+                lpFindStreamData->StreamSize.QuadPart = FindStreamData->CurrentInfo->StreamSize.QuadPart;
+                RtlCopyMemory(lpFindStreamData->cStreamName,
+                              FindStreamData->CurrentInfo->StreamName,
+                              StreamNameLen);
+                lpFindStreamData->cStreamName[StreamNameLen / sizeof(WCHAR)] = UNICODE_NULL;
 
-                IData->HasData = TRUE;
-                SearchPattern = NULL;
+                break;
             }
 
-        } while (FoundFile == NULL);
-
-        if (FoundFile != NULL)
-        {
-            _SEH2_TRY
-            {
-                InternalCopyFindDataW(lpFindFileData,
-                                      FoundFile);
-            }
-            _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+            default:
             {
+                /* Invalid InfoLevel */
+                ASSERT(FALSE);
+                break;
             }
-            _SEH2_END;
         }
-
-        if (Locked)
-            RtlLeaveCriticalSection(&IData->Lock);
     }
-
-    if (!NT_SUCCESS(Status))
-    {
-        BaseSetLastNTError (Status);
-        return FALSE;
-    }
-    else if (FoundFile == NULL)
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
     {
-        SetLastError (ERROR_NO_MORE_FILES);
-        return FALSE;
     }
+    _SEH2_END;
 
-    return TRUE;
+    return;
 }
 
 
-/*
- * @implemented
- */
-HANDLE
-WINAPI
-InternalFindFirstFile (
-    LPCWSTR    lpFileName,
-    BOOLEAN DirectoryOnly,
-    PVOID lpFindFileData
-       )
-{
-       OBJECT_ATTRIBUTES ObjectAttributes;
-       PKERNEL32_FIND_DATA_HEADER IHeader;
-       PKERNEL32_FIND_FILE_DATA IData;
-       IO_STATUS_BLOCK IoStatusBlock;
-       UNICODE_STRING NtPathU, FileName, PathFileName;
-       NTSTATUS Status;
-       PWSTR NtPathBuffer;
-       BOOLEAN RemovedLastChar = FALSE;
-       BOOL bResult;
-       RTL_RELATIVE_NAME_U DirInfo;
-       ULONG DeviceNameInfo;
-       HANDLE hDirectory = NULL;
-
-       TRACE("FindFirstFileW(lpFileName %S)\n",
-              lpFileName);
-
-       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 INVALID_HANDLE_VALUE;
-       }
-
-       /* 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.RelativeName.Length != 0 && DirInfo.RelativeName.Buffer != PathFileName.Buffer)
-       {
-           if (PathFileName.Buffer != NULL)
-           {
-               /* This is a relative path to DirInfo.ContainingDirectory, adjust NtPathU! */
-               NtPathU.Length = NtPathU.MaximumLength =
-                   (USHORT)((ULONG_PTR)PathFileName.Buffer - (ULONG_PTR)DirInfo.RelativeName.Buffer);
-               NtPathU.Buffer = DirInfo.RelativeName.Buffer;
-           }
-       }
-       else
-       {
-           /* This is an absolute path, NtPathU receives the full path */
-           DirInfo.ContainingDirectory = NULL;
-           if (PathFileName.Buffer != NULL)
-           {
-               NtPathU.Length = NtPathU.MaximumLength =
-                   (USHORT)((ULONG_PTR)PathFileName.Buffer - (ULONG_PTR)NtPathU.Buffer);
-           }
-       }
-
-       /* Remove the last character of the path (Unless the path is a drive and
-          ends with ":\"). If the caller however supplies a path to a device, such
-          as "C:\NUL" then the last character gets cut off, which later results in
-          NtOpenFile to return STATUS_OBJECT_NAME_NOT_FOUND, which in turn triggers
-          a fake DOS device check with RtlIsDosDeviceName_U. However, if there is a
-          real device with a name eg. "NU" in the system, FindFirstFile will succeed,
-          rendering the fake DOS device check useless... Why would they invent such a
-          stupid and broken behavior?! */
-       if (NtPathU.Length >= 2 * sizeof(WCHAR) &&
-           NtPathU.Buffer[(NtPathU.Length / sizeof(WCHAR)) - 1] != L'\\' &&
-           NtPathU.Buffer[(NtPathU.Length / sizeof(WCHAR)) - 2] != L':')
-       {
-           NtPathU.Length -= sizeof(WCHAR);
-           RemovedLastChar = TRUE;
-       }
-
-       TRACE("lpFileName: \"%ws\"\n", lpFileName);
-       TRACE("NtPathU: \"%wZ\"\n", &NtPathU);
-       TRACE("PathFileName: \"%wZ\"\n", &PathFileName);
-       TRACE("RelativeTo: 0x%p\n", DirInfo.ContainingDirectory);
-
-       InitializeObjectAttributes (&ObjectAttributes,
-                                   &NtPathU,
-                                   OBJ_CASE_INSENSITIVE,
-                                   DirInfo.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 (Status == STATUS_NOT_A_DIRECTORY && RemovedLastChar)
-       {
-           /* Try again, this time with the last character ... */
-           NtPathU.Length += sizeof(WCHAR);
-
-           Status = NtOpenFile (&hDirectory,
-                                FILE_LIST_DIRECTORY | SYNCHRONIZE,
-                                &ObjectAttributes,
-                                &IoStatusBlock,
-                                FILE_SHARE_READ|FILE_SHARE_WRITE,
-                                FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
-
-           NtPathU.Length += sizeof(WCHAR);
-       }
-
-       if (!NT_SUCCESS(Status))
-       {
-          RtlFreeHeap (RtlGetProcessHeap(),
-                       0,
-                       NtPathBuffer);
-
-          /* See if the application tries to look for a DOS device */
-          DeviceNameInfo = RtlIsDosDeviceName_U((PWSTR)((ULONG_PTR)lpFileName));
-          if (DeviceNameInfo != 0)
-          {
-              InternalCopyDeviceFindDataW(lpFindFileData,
-                                          lpFileName,
-                                          DeviceNameInfo);
-
-              return FIND_DEVICE_HANDLE;
-          }
-
-          BaseSetLastNTError (Status);
-          return INVALID_HANDLE_VALUE;
-       }
-
-       if (PathFileName.Length == 0)
-       {
-           /* No file part?! */
-           NtClose(hDirectory);
-           RtlFreeHeap (RtlGetProcessHeap(),
-                        0,
-                        NtPathBuffer);
-           SetLastError(ERROR_FILE_NOT_FOUND);
-           return INVALID_HANDLE_VALUE;
-       }
-
-       IHeader = RtlAllocateHeap (RtlGetProcessHeap(),
-                                  HEAP_ZERO_MEMORY,
-                                      sizeof(KERNEL32_FIND_DATA_HEADER) +
-                                      sizeof(KERNEL32_FIND_FILE_DATA) + FIND_DATA_SIZE);
-       if (NULL == IHeader)
-       {
-           RtlFreeHeap (RtlGetProcessHeap(),
-                        0,
-                        NtPathBuffer);
-           NtClose(hDirectory);
-
-           SetLastError(ERROR_NOT_ENOUGH_MEMORY);
-           return INVALID_HANDLE_VALUE;
-       }
-
-       IHeader->Type = FileFind;
-       IData = (PKERNEL32_FIND_FILE_DATA)(IHeader + 1);
-       IData->DirectoryHandle = hDirectory;
-       IData->HasMoreData = TRUE;
-
-       /* 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,
-                                      lpFindFileData);
-
-       RtlFreeHeap (RtlGetProcessHeap(),
-                    0,
-                    NtPathBuffer);
-
-       if (!bResult)
-       {
-           FindClose((HANDLE)IHeader);
-           return INVALID_HANDLE_VALUE;
-       }
-
-       RtlInitializeCriticalSection(&IData->Lock);
-       IData->LockInitialized = TRUE;
-
-       return (HANDLE)IHeader;
-}
-
+/* PUBLIC FUNCTIONS ***********************************************************/
 
 /*
  * @implemented
@@ -464,27 +268,23 @@ FindFirstFileA(IN LPCSTR lpFileName,
     WIN32_FIND_DATAW FindFileDataW;
 
     lpFileNameW = Basep8BitStringToStaticUnicodeString(lpFileName);
-    if (!lpFileNameW)
-    {
-        return INVALID_HANDLE_VALUE;
-    }
+    if (!lpFileNameW) return INVALID_HANDLE_VALUE;
 
     hSearch = FindFirstFileExW(lpFileNameW->Buffer,
                                FindExInfoStandard,
                                &FindFileDataW,
                                FindExSearchNameMatch,
                                NULL, 0);
-    if (hSearch == INVALID_HANDLE_VALUE)
-    {
-        return INVALID_HANDLE_VALUE;
-    }
+    if (hSearch == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE;
 
-    memcpy(lpFindFileData, &FindFileDataW, FIELD_OFFSET(WIN32_FIND_DATA, cFileName));
+    RtlCopyMemory(lpFindFileData,
+                  &FindFileDataW,
+                  FIELD_OFFSET(WIN32_FIND_DATAA, cFileName));
 
     RtlInitUnicodeString(&UTF8, FindFileDataW.cFileName);
     Ansi.Buffer = lpFindFileData->cFileName;
     Ansi.Length = 0;
-    Ansi.MaximumLength = MAX_PATH;
+    Ansi.MaximumLength = sizeof(lpFindFileData->cFileName);
     Status = BasepUnicodeStringTo8BitString(&Ansi, &UTF8, FALSE);
     if (!NT_SUCCESS(Status))
     {
@@ -496,7 +296,7 @@ FindFirstFileA(IN LPCSTR lpFileName,
     RtlInitUnicodeString(&UTF8, FindFileDataW.cAlternateFileName);
     Ansi.Buffer = lpFindFileData->cAlternateFileName;
     Ansi.Length = 0;
-    Ansi.MaximumLength = 14;
+    Ansi.MaximumLength = sizeof(lpFindFileData->cAlternateFileName);
     Status = BasepUnicodeStringTo8BitString(&Ansi, &UTF8, FALSE);
     if (!NT_SUCCESS(Status))
     {
@@ -509,6 +309,22 @@ FindFirstFileA(IN LPCSTR lpFileName,
 }
 
 
+/*
+ * @implemented
+ */
+HANDLE
+WINAPI
+FindFirstFileW(IN LPCWSTR lpFileName,
+               OUT LPWIN32_FIND_DATAW lpFindFileData)
+{
+    return FindFirstFileExW(lpFileName,
+                            FindExInfoStandard,
+                            lpFindFileData,
+                            FindExSearchNameMatch,
+                            NULL, 0);
+}
+
+
 /*
  * @implemented
  */
@@ -523,16 +339,16 @@ FindNextFileA(IN HANDLE hFindFile,
     WIN32_FIND_DATAW FindFileDataW;
 
     if (!FindNextFileW(hFindFile, &FindFileDataW))
-    {
         return FALSE;
-    }
 
-    memcpy(lpFindFileData, &FindFileDataW, FIELD_OFFSET(WIN32_FIND_DATA, cFileName));
+    RtlCopyMemory(lpFindFileData,
+                  &FindFileDataW,
+                  FIELD_OFFSET(WIN32_FIND_DATAA, cFileName));
 
     RtlInitUnicodeString(&UTF8, FindFileDataW.cFileName);
     Ansi.Buffer = lpFindFileData->cFileName;
     Ansi.Length = 0;
-    Ansi.MaximumLength = MAX_PATH;
+    Ansi.MaximumLength = sizeof(lpFindFileData->cFileName);
     Status = BasepUnicodeStringTo8BitString(&Ansi, &UTF8, FALSE);
     if (!NT_SUCCESS(Status))
     {
@@ -543,7 +359,7 @@ FindNextFileA(IN HANDLE hFindFile,
     RtlInitUnicodeString(&UTF8, FindFileDataW.cAlternateFileName);
     Ansi.Buffer = lpFindFileData->cAlternateFileName;
     Ansi.Length = 0;
-    Ansi.MaximumLength = 14;
+    Ansi.MaximumLength = sizeof(lpFindFileData->cAlternateFileName);
     Status = BasepUnicodeStringTo8BitString(&Ansi, &UTF8, FALSE);
     if (!NT_SUCCESS(Status))
     {
@@ -560,123 +376,187 @@ FindNextFileA(IN HANDLE hFindFile,
  */
 BOOL
 WINAPI
-FindClose (
-       HANDLE  hFindFile
-       )
+FindNextFileW(IN HANDLE hFindFile,
+              OUT LPWIN32_FIND_DATAW lpFindFileData)
 {
-       PKERNEL32_FIND_DATA_HEADER IHeader;
-
-       TRACE("FindClose(hFindFile %x)\n",hFindFile);
-
-       if (hFindFile == FIND_DEVICE_HANDLE)
-               return TRUE;
-
-       if (!hFindFile || hFindFile == INVALID_HANDLE_VALUE)
-       {
-               SetLastError (ERROR_INVALID_HANDLE);
-               return FALSE;
-       }
-
-       IHeader = (PKERNEL32_FIND_DATA_HEADER)hFindFile;
-
-       switch (IHeader->Type)
-       {
-               case FileFind:
-               {
-                       PKERNEL32_FIND_FILE_DATA IData = (PKERNEL32_FIND_FILE_DATA)(IHeader + 1);
-                       CloseHandle (IData->DirectoryHandle);
-                       if (IData->LockInitialized)
-                               RtlDeleteCriticalSection(&IData->Lock);
-                       IData->LockInitialized = FALSE;
-                       break;
-               }
-
-               case StreamFind:
-               {
-                       PKERNEL32_FIND_STREAM_DATA IData = (PKERNEL32_FIND_STREAM_DATA)(IHeader + 1);
-                       if (IData->pFileStreamInfo != NULL)
-                       {
-                               RtlFreeHeap (RtlGetProcessHeap(), 0, IData->pFileStreamInfo);
-                       }
-                       break;
-               }
-
-               default:
-                       SetLastError (ERROR_INVALID_HANDLE);
-                       return FALSE;
-       }
-
-       RtlFreeHeap (RtlGetProcessHeap(), 0, IHeader);
-
-       return TRUE;
-}
+    NTSTATUS Status = STATUS_SUCCESS;
+    DIR_INFORMATION FoundFile = {NULL};
 
+    TRACE("FindNextFileW(%lx, 0x%p)\n", hFindFile, lpFindFileData);
 
-/*
- * @implemented
- */
-HANDLE
-WINAPI
-FindFirstFileW(IN LPCWSTR lpFileName,
-               OUT LPWIN32_FIND_DATAW lpFindFileData)
-{
-    return FindFirstFileExW(lpFileName,
-                            FindExInfoStandard,
-                            lpFindFileData,
-                            FindExSearchNameMatch,
-                            NULL,
-                            0);
+    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
-FindNextFileW(IN HANDLE hFindFile,
-              OUT LPWIN32_FIND_DATAW lpFindFileData)
+FindClose(HANDLE hFindFile)
 {
-       return InternalFindNextFile(hFindFile,
-                                   NULL,
-                                   lpFindFileData);
-}
+    TRACE("FindClose(hFindFile %x)\n", hFindFile);
 
+    if (hFindFile == FIND_DEVICE_HANDLE)
+        return TRUE;
 
-/*
- * @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)
-{
-    if (fInfoLevelId != FindExInfoStandard)
+    if (!hFindFile || hFindFile == INVALID_HANDLE_VALUE)
     {
-        SetLastError(ERROR_INVALID_PARAMETER);
-        return INVALID_HANDLE_VALUE;
+        SetLastError(ERROR_INVALID_HANDLE);
+        return FALSE;
     }
 
-    if (fSearchOp == FindExSearchNameMatch || fSearchOp == FindExSearchLimitToDirectories)
+    /* Protect with SEH against closing attempts on invalid handles. */
+    _SEH2_TRY
     {
-        if (lpSearchFilter)
+        PFIND_DATA_HANDLE FindDataHandle = (PFIND_DATA_HANDLE)hFindFile;
+
+        switch (FindDataHandle->Type)
         {
-            SetLastError(ERROR_INVALID_PARAMETER);
-            return INVALID_HANDLE_VALUE;
+            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);
+            }
         }
 
-        return InternalFindFirstFile (lpFileName,
-                                      fSearchOp == FindExSearchLimitToDirectories,
-                                      lpFindFileData);
+        RtlFreeHeap(RtlGetProcessHeap(), 0, FindDataHandle);
+        _SEH2_YIELD(return TRUE);
     }
-
-    SetLastError(ERROR_INVALID_PARAMETER);
-    return INVALID_HANDLE_VALUE;
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        BaseSetLastNTError(_SEH2_GetExceptionCode());
+        _SEH2_YIELD(return FALSE);
+    }
+    _SEH2_END;
 }
 
+
 /*
  * @unimplemented
  */
@@ -695,30 +575,37 @@ FindFirstFileExA(IN LPCSTR lpFileName,
     UNICODE_STRING UTF8;
     PUNICODE_STRING lpFileNameW;
     WIN32_FIND_DATAW FindFileDataW;
+    LPWIN32_FIND_DATAA lpFindFileDataA = (LPWIN32_FIND_DATAA)lpFindFileData;
 
-    lpFileNameW = Basep8BitStringToStaticUnicodeString(lpFileName);
-    if (!lpFileNameW)
+    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;
-    }
+    if (hSearch == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE;
 
-    memcpy(lpFindFileData, &FindFileDataW, FIELD_OFFSET(WIN32_FIND_DATA, cFileName));
+    RtlCopyMemory(lpFindFileDataA,
+                  &FindFileDataW,
+                  FIELD_OFFSET(WIN32_FIND_DATAA, cFileName));
 
     RtlInitUnicodeString(&UTF8, FindFileDataW.cFileName);
-    Ansi.Buffer = ((LPWIN32_FIND_DATAA)lpFindFileData)->cFileName;
+    Ansi.Buffer = lpFindFileDataA->cFileName;
     Ansi.Length = 0;
-    Ansi.MaximumLength = MAX_PATH;
+    Ansi.MaximumLength = sizeof(lpFindFileDataA->cFileName);
     Status = BasepUnicodeStringTo8BitString(&Ansi, &UTF8, FALSE);
     if (!NT_SUCCESS(Status))
     {
@@ -727,50 +614,276 @@ FindFirstFileExA(IN LPCSTR lpFileName,
         return INVALID_HANDLE_VALUE;
     }
 
-    RtlInitUnicodeString(&UTF8, FindFileDataW.cAlternateFileName);
-    Ansi.Buffer = ((LPWIN32_FIND_DATAA)lpFindFileData)->cAlternateFileName;
-    Ansi.Length = 0;
-    Ansi.MaximumLength = 14;
-    Status = BasepUnicodeStringTo8BitString(&Ansi, &UTF8, FALSE);
-    if (!NT_SUCCESS(Status))
+    if (fInfoLevelId != FindExInfoBasic)
     {
-        FindClose(hSearch);
-        BaseSetLastNTError(Status);
-        return INVALID_HANDLE_VALUE;
+        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;
 }
 
 
-static VOID
-InternalCopyStreamInfo(IN OUT PKERNEL32_FIND_STREAM_DATA IData,
-                       OUT LPVOID lpFindStreamData)
+/*
+ * @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)
 {
-    ASSERT(IData->pCurrent);
+    TRACE("FindFirstFileExW(lpFileName %S)\n", lpFileName);
 
-    switch (IData->InfoLevel)
+    if ((fInfoLevelId != FindExInfoStandard && fInfoLevelId != FindExInfoBasic) ||
+        fSearchOp == FindExSearchLimitToDevices ||
+        dwAdditionalFlags & ~FIND_FIRST_EX_CASE_SENSITIVE /* only supported flag for now */)
     {
-        case FindStreamInfoStandard:
+        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;
+        PWSTR NtPathBuffer;
+        RTL_RELATIVE_NAME_U RelativePath;
+        ULONG DeviceNameInfo = 0;
+
+        NTSTATUS Status;
+        OBJECT_ATTRIBUTES ObjectAttributes;
+        IO_STATUS_BLOCK IoStatusBlock;
+        HANDLE hDirectory = NULL;
+
+        /*
+         * May represent many FILE_BOTH_DIR_INFORMATION
+         * or many FILE_FULL_DIR_INFORMATION structures.
+         */
+        BYTE DirectoryInfo[FIND_DATA_SIZE];
+        DIR_INFORMATION DirInfo = {&DirectoryInfo};
+
+        /* The search filter is always unused */
+        if (lpSearchFilter)
         {
-            ULONG StreamNameLen;
-            WIN32_FIND_STREAM_DATA *StreamData = (WIN32_FIND_STREAM_DATA*)lpFindStreamData;
-
-            StreamNameLen = IData->pCurrent->StreamNameLength;
-            if (StreamNameLen > sizeof(StreamData->cStreamName) - sizeof(WCHAR))
-                StreamNameLen = sizeof(StreamData->cStreamName) - sizeof(WCHAR);
-
-            StreamData->StreamSize.QuadPart = IData->pCurrent->StreamSize.QuadPart;
-            RtlCopyMemory(StreamData->cStreamName,
-                          IData->pCurrent->StreamName,
-                          StreamNameLen);
-            StreamData->cStreamName[StreamNameLen / sizeof(WCHAR)] = L'\0';
-            break;
+            SetLastError(ERROR_INVALID_PARAMETER);
+            return INVALID_HANDLE_VALUE;
         }
 
-        default:
-            ASSERT(FALSE);
-            break;
+        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;
+        }
+
+        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;
     }
 }
 
@@ -785,11 +898,11 @@ FindFirstStreamW(IN LPCWSTR lpFileName,
                  OUT LPVOID lpFindStreamData,
                  IN DWORD dwFlags)
 {
-    PKERNEL32_FIND_DATA_HEADER IHeader = NULL;
-    PKERNEL32_FIND_STREAM_DATA IData = NULL;
+    PFIND_DATA_HANDLE FindDataHandle = NULL;
+    PFIND_STREAM_DATA FindStreamData;
     OBJECT_ATTRIBUTES ObjectAttributes;
     IO_STATUS_BLOCK IoStatusBlock;
-    UNICODE_STRING NtPathU;
+    UNICODE_STRING NtFilePath;
     HANDLE FileHandle = NULL;
     NTSTATUS Status;
     ULONG BufferSize = 0;
@@ -801,19 +914,18 @@ FindFirstStreamW(IN LPCWSTR lpFileName,
         return INVALID_HANDLE_VALUE;
     }
 
-    /* validate & translate the filename */
+    /* Validate and translate the filename */
     if (!RtlDosPathNameToNtPathName_U(lpFileName,
-                                      &NtPathU,
-                                      NULL,
-                                      NULL))
+                                      &NtFilePath,
+                                      NULL, NULL))
     {
         SetLastError(ERROR_PATH_NOT_FOUND);
         return INVALID_HANDLE_VALUE;
     }
 
-    /* open the file */
+    /* Open the file */
     InitializeObjectAttributes(&ObjectAttributes,
-                               &NtPathU,
+                               &NtFilePath,
                                OBJ_CASE_INSENSITIVE,
                                NULL,
                                NULL);
@@ -822,47 +934,52 @@ FindFirstStreamW(IN LPCWSTR lpFileName,
                           0,
                           &ObjectAttributes,
                           &IoStatusBlock,
-                          NULL,
-                          0,
+                          NULL, 0,
                           FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
                           FILE_OPEN,
-                          0,
-                          NULL,
-                          0);
-    if (!NT_SUCCESS(Status))
+                          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;
     }
 
-    /* create the search context */
-    IHeader = RtlAllocateHeap(RtlGetProcessHeap(),
-                              0,
-                              sizeof(KERNEL32_FIND_DATA_HEADER) +
-                                  sizeof(KERNEL32_FIND_STREAM_DATA));
-    if (IHeader == NULL)
+    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))
     {
-        Status = STATUS_NO_MEMORY;
+        RtlFreeHeap(RtlGetProcessHeap(), 0, FindDataHandle);
         goto Cleanup;
     }
 
-    IHeader->Type = StreamFind;
-    IData = (PKERNEL32_FIND_STREAM_DATA)(IHeader + 1);
-
-    /* capture all information about the streams */
-    IData->InfoLevel = InfoLevel;
-    IData->pCurrent = NULL;
-    IData->pFileStreamInfo = NULL;
-
+    /* Capture all information about the streams */
     do
     {
         BufferSize += 0x1000;
 
-        if (IData->pFileStreamInfo == NULL)
+        if (FindStreamData->FileStreamInfo == NULL)
         {
-            IData->pFileStreamInfo = RtlAllocateHeap(RtlGetProcessHeap(),
-                                                     0,
-                                                     BufferSize);
-            if (IData->pFileStreamInfo == NULL)
+            FindStreamData->FileStreamInfo = RtlAllocateHeap(RtlGetProcessHeap(),
+                                                              HEAP_ZERO_MEMORY,
+                                                              BufferSize);
+            if (FindStreamData->FileStreamInfo == NULL)
             {
                 Status = STATUS_NO_MEMORY;
                 break;
@@ -873,8 +990,8 @@ FindFirstStreamW(IN LPCWSTR lpFileName,
             PFILE_STREAM_INFORMATION pfsi;
 
             pfsi = RtlReAllocateHeap(RtlGetProcessHeap(),
-                                     0,
-                                     IData->pFileStreamInfo,
+                                     0, // HEAP_ZERO_MEMORY,
+                                     FindStreamData->FileStreamInfo,
                                      BufferSize);
             if (pfsi == NULL)
             {
@@ -882,12 +999,12 @@ FindFirstStreamW(IN LPCWSTR lpFileName,
                 break;
             }
 
-            IData->pFileStreamInfo = pfsi;
+            FindStreamData->FileStreamInfo = pfsi;
         }
 
         Status = NtQueryInformationFile(FileHandle,
                                         &IoStatusBlock,
-                                        IData->pFileStreamInfo,
+                                        FindStreamData->FileStreamInfo,
                                         BufferSize,
                                         FileStreamInformation);
 
@@ -895,49 +1012,37 @@ FindFirstStreamW(IN LPCWSTR lpFileName,
 
     if (NT_SUCCESS(Status))
     {
-        NtClose(FileHandle);
-        FileHandle = NULL;
-
-        /* select the first stream and return the information */
-        IData->pCurrent = IData->pFileStreamInfo;
-        InternalCopyStreamInfo(IData,
-                               lpFindStreamData);
+        /* Select the first stream and return the information */
+        FindStreamData->CurrentInfo = FindStreamData->FileStreamInfo;
+        CopyStreamData(FindStreamData, lpFindStreamData);
 
-        /* all done */
+        /* All done */
         Status = STATUS_SUCCESS;
     }
-
-Cleanup:
-    if (FileHandle != NULL)
+    else
     {
-        NtClose(FileHandle);
-    }
+        if (FindStreamData->FileStreamInfo)
+        {
+            RtlFreeHeap(RtlGetProcessHeap(), 0, FindStreamData->FileStreamInfo);
+        }
 
-    RtlFreeHeap(RtlGetProcessHeap(),
-                0,
-                NtPathU.Buffer);
+        RtlFreeHeap(RtlGetProcessHeap(), 0, FindDataHandle);
+    }
 
-    if (!NT_SUCCESS(Status))
-    {
-        if (IHeader != NULL)
-        {
-            if (IData->pFileStreamInfo != NULL)
-            {
-                RtlFreeHeap(RtlGetProcessHeap(),
-                            0,
-                            IData->pFileStreamInfo);
-            }
+Cleanup:
+    if (FileHandle) NtClose(FileHandle);
 
-            RtlFreeHeap(RtlGetProcessHeap(),
-                        0,
-                        IHeader);
-        }
+    RtlFreeHeap(RtlGetProcessHeap(), 0, NtFilePath.Buffer);
 
+    if (NT_SUCCESS(Status))
+    {
+        return (HANDLE)FindDataHandle;
+    }
+    else
+    {
         BaseSetLastNTError(Status);
         return INVALID_HANDLE_VALUE;
     }
-
-    return (HANDLE)IHeader;
 }
 
 
@@ -949,37 +1054,39 @@ WINAPI
 FindNextStreamW(IN HANDLE hFindStream,
                 OUT LPVOID lpFindStreamData)
 {
-    PKERNEL32_FIND_DATA_HEADER IHeader;
-    PKERNEL32_FIND_STREAM_DATA IData;
+    PFIND_DATA_HANDLE FindDataHandle = (PFIND_DATA_HANDLE)hFindStream;
+    PFIND_STREAM_DATA FindStreamData;
 
-    IHeader = (PKERNEL32_FIND_DATA_HEADER)hFindStream;
     if (hFindStream == NULL || hFindStream == INVALID_HANDLE_VALUE ||
-        IHeader->Type != StreamFind)
+        FindDataHandle->Type != FindStream)
     {
-        SetLastError (ERROR_INVALID_HANDLE);
+        SetLastError(ERROR_INVALID_HANDLE);
         return FALSE;
     }
 
-    IData = (PKERNEL32_FIND_STREAM_DATA)(IHeader + 1);
+    RtlEnterCriticalSection(&FindDataHandle->Lock);
 
-    /* select next stream if possible */
-    if (IData->pCurrent->NextEntryOffset != 0)
+    FindStreamData = FindDataHandle->u.FindStreamData;
+
+    /* Select next stream if possible */
+    if (FindStreamData->CurrentInfo->NextEntryOffset != 0)
     {
-        IData->pCurrent = (PFILE_STREAM_INFORMATION)((ULONG_PTR)IData->pFileStreamInfo +
-                                                     IData->pCurrent->NextEntryOffset);
+        FindStreamData->CurrentInfo = (PFILE_STREAM_INFORMATION)((ULONG_PTR)FindStreamData->FileStreamInfo +
+                                                                 FindStreamData->CurrentInfo->NextEntryOffset);
+
+        /* Return the information */
+        CopyStreamData(FindStreamData, lpFindStreamData);
+
+        RtlLeaveCriticalSection(&FindDataHandle->Lock);
+        return TRUE;
     }
     else
     {
+        RtlLeaveCriticalSection(&FindDataHandle->Lock);
+
         SetLastError(ERROR_HANDLE_EOF);
         return FALSE;
     }
-
-    /* return the information */
-    InternalCopyStreamInfo(IData,
-                           lpFindStreamData);
-
-    return TRUE;
 }
 
-
 /* EOF */