[NTFS] Apply fix from 52f0726: allow partial info copy on dir enumeration on first...
[reactos.git] / drivers / filesystems / ntfs / dirctl.c
index 28c1877..4ad76fa 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ReactOS kernel
- *  Copyright (C) 2002,2003 ReactOS Team
+ *  Copyright (C) 2002, 2003, 2014 ReactOS Team
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -20,7 +20,9 @@
  * PROJECT:          ReactOS kernel
  * FILE:             drivers/filesystem/ntfs/dirctl.c
  * PURPOSE:          NTFS filesystem driver
- * PROGRAMMER:       Eric Kohl
+ * PROGRAMMERS:      Eric Kohl
+ *                   Pierre Schweitzer (pierre@reactos.org)
+ *                   HervĂ© Poussineau (hpoussin@reactos.org)
  */
 
 /* INCLUDES *****************************************************************/
 
 /* FUNCTIONS ****************************************************************/
 
-#if 0
-static NTSTATUS
-CdfsGetEntryName(PDEVICE_EXTENSION DeviceExt,
-                PVOID *Context,
-                PVOID *Block,
-                PLARGE_INTEGER StreamOffset,
-                ULONG DirLength,
-                PVOID *Ptr,
-                PWSTR Name,
-                PULONG pIndex,
-                PULONG pIndex2)
-/*
- * FUNCTION: Retrieves the file name, be it in short or long file name format
- */
+ULONGLONG
+NtfsGetFileSize(PDEVICE_EXTENSION DeviceExt,
+                PFILE_RECORD_HEADER FileRecord,
+                PCWSTR Stream,
+                ULONG StreamLength,
+                PULONGLONG AllocatedSize)
 {
-  PDIR_RECORD Record;
-  NTSTATUS Status;
-  ULONG Index = 0;
-  ULONG Offset = 0;
-  ULONG BlockOffset = 0;
-
-  Record = (PDIR_RECORD)*Block;
-  while(Index < *pIndex)
-    {
-      BlockOffset += Record->RecordLength;
-      Offset += Record->RecordLength;
-
-      Record = (PDIR_RECORD)(*Block + BlockOffset);
-      if (BlockOffset >= BLOCKSIZE || Record->RecordLength == 0)
-       {
-         DPRINT("Map next sector\n");
-         CcUnpinData(*Context);
-         StreamOffset->QuadPart += BLOCKSIZE;
-         Offset = ROUND_UP(Offset, BLOCKSIZE);
-         BlockOffset = 0;
-
-         if (!CcMapData(DeviceExt->StreamFileObject,
-                        StreamOffset,
-                        BLOCKSIZE, TRUE,
-                        Context, Block))
-           {
-             DPRINT("CcMapData() failed\n");
-             return(STATUS_UNSUCCESSFUL);
-           }
-         Record = (PDIR_RECORD)(*Block + BlockOffset);
-       }
-
-      if (Offset >= DirLength)
-       return(STATUS_NO_MORE_ENTRIES);
-
-      Index++;
-    }
-
-  DPRINT("Index %lu  RecordLength %lu  Offset %lu\n",
-        Index, Record->RecordLength, Offset);
+    ULONGLONG Size = 0ULL;
+    ULONGLONG Allocated = 0ULL;
+    NTSTATUS Status;
+    PNTFS_ATTR_CONTEXT DataContext;
 
-  if (Record->FileIdLength == 1 && Record->FileId[0] == 0)
-    {
-      wcscpy(Name, L".");
-    }
-  else if (Record->FileIdLength == 1 && Record->FileId[0] == 1)
-    {
-      wcscpy(Name, L"..");
-    }
-  else
+    Status = FindAttribute(DeviceExt, FileRecord, AttributeData, Stream, StreamLength, &DataContext, NULL);
+    if (NT_SUCCESS(Status))
     {
-      if (DeviceExt->CdInfo.JolietLevel == 0)
-       {
-         ULONG i;
-
-         for (i = 0; i < Record->FileIdLength && Record->FileId[i] != ';'; i++)
-           Name[i] = (WCHAR)Record->FileId[i];
-         Name[i] = 0;
-       }
-      else
-       {
-         CdfsSwapString(Name, Record->FileId, Record->FileIdLength);
-       }
+        Size = AttributeDataLength(DataContext->pRecord);
+        Allocated = AttributeAllocatedLength(DataContext->pRecord);
+        ReleaseAttributeContext(DataContext);
     }
 
-  DPRINT("Name '%S'\n", Name);
+    if (AllocatedSize != NULL) *AllocatedSize = Allocated;
 
-  *Ptr = Record;
+    return Size;
+}
 
-  *pIndex = Index;
 
-  return(STATUS_SUCCESS);
-}
-#endif
+#define ULONG_ROUND_UP(x)   ROUND_UP((x), (sizeof(ULONG)))
 
 
 static NTSTATUS
-NtfsGetNameInformation(PDEVICE_EXTENSION DeviceExt,
-                       PFILE_RECORD_HEADER FileRecord,
-                       PNTFS_ATTR_CONTEXT DataContext,
-                       PFILE_NAMES_INFORMATION Info,
-                       ULONG BufferLength)
+NtfsGetNamesInformation(PDEVICE_EXTENSION DeviceExt,
+                        PFILE_RECORD_HEADER FileRecord,
+                        ULONGLONG MFTIndex,
+                        PFILE_NAMES_INFORMATION Info,
+                        ULONG BufferLength,
+                        PULONG Written,
+                        BOOLEAN First)
 {
     ULONG Length;
+    NTSTATUS Status;
+    ULONG BytesToCopy = 0;
     PFILENAME_ATTRIBUTE FileName;
 
-    DPRINT("NtfsGetNameInformation() called\n");
+    DPRINT("NtfsGetNamesInformation() called\n");
 
-    FileName = GetFileNameFromRecord(FileRecord);
-    ASSERT(FileName != NULL);
+    *Written = 0;
+    Status = STATUS_BUFFER_OVERFLOW;
+    if (FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName) > BufferLength)
+    {
+        return Status;
+    }
+
+    FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
+    if (FileName == NULL)
+    {
+        DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
+        NtfsDumpFileAttributes(DeviceExt, FileRecord);
+        return STATUS_OBJECT_NAME_NOT_FOUND;
+    }
 
     Length = FileName->NameLength * sizeof (WCHAR);
-    if ((sizeof(FILE_NAMES_INFORMATION) + Length) > BufferLength)
-        return(STATUS_BUFFER_OVERFLOW);
+    if (First || (BufferLength >= FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName) + Length))
+    {
+        Info->FileNameLength = Length;
 
-    Info->FileNameLength = Length;
-    Info->NextEntryOffset =
-        ROUND_UP(sizeof(FILE_NAMES_INFORMATION) + Length, sizeof(ULONG));
-    RtlCopyMemory(Info->FileName, FileName->Name, Length);
+        *Written = FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName);
+        Info->NextEntryOffset = 0;
+        if (BufferLength > FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName))
+        {
+            BytesToCopy = min(Length, BufferLength - FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName));
+            RtlCopyMemory(Info->FileName, FileName->Name, BytesToCopy);
+            *Written += BytesToCopy;
 
-    return(STATUS_SUCCESS);
+            if (BytesToCopy == Length)
+            {
+                Info->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_NAMES_INFORMATION) +
+                                                       BytesToCopy);
+                Status = STATUS_SUCCESS;
+            }
+        }
+    }
+
+    return Status;
 }
 
 
 static NTSTATUS
 NtfsGetDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
                             PFILE_RECORD_HEADER FileRecord,
-                            PNTFS_ATTR_CONTEXT DataContext,
+                            ULONGLONG MFTIndex,
                             PFILE_DIRECTORY_INFORMATION Info,
-                            ULONG BufferLength)
+                            ULONG BufferLength,
+                            PULONG Written,
+                            BOOLEAN First)
 {
     ULONG Length;
+    NTSTATUS Status;
+    ULONG BytesToCopy = 0;
     PFILENAME_ATTRIBUTE FileName;
+    PSTANDARD_INFORMATION StdInfo;
 
     DPRINT("NtfsGetDirectoryInformation() called\n");
 
-    FileName = GetFileNameFromRecord(FileRecord);
-    ASSERT(FileName != NULL);
+    *Written = 0;
+    Status = STATUS_BUFFER_OVERFLOW;
+    if (FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName) > BufferLength)
+    {
+        return Status;
+    }
+
+    FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
+    if (FileName == NULL)
+    {
+        DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
+        NtfsDumpFileAttributes(DeviceExt, FileRecord);
+        return STATUS_OBJECT_NAME_NOT_FOUND;
+    }
+
+    StdInfo = GetStandardInformationFromRecord(DeviceExt, FileRecord);
+    ASSERT(StdInfo != NULL);
 
     Length = FileName->NameLength * sizeof (WCHAR);
-    if ((sizeof(FILE_DIRECTORY_INFORMATION) + Length) > BufferLength)
-        return(STATUS_BUFFER_OVERFLOW);
+    if (First || (BufferLength >= FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName) + Length))
+    {
+        Info->FileNameLength = Length;
+
+        *Written = FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName);
+        Info->NextEntryOffset = 0;
+        if (BufferLength > FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName))
+        {
+            BytesToCopy = min(Length, BufferLength - FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName));
+            RtlCopyMemory(Info->FileName, FileName->Name, BytesToCopy);
+            *Written += BytesToCopy;
 
-    Info->FileNameLength = Length;
-    Info->NextEntryOffset =
-        ROUND_UP(sizeof(FILE_DIRECTORY_INFORMATION) + Length, sizeof(ULONG));
-    RtlCopyMemory(Info->FileName, FileName->Name, Length);
+            if (BytesToCopy == Length)
+            {
+                Info->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_DIRECTORY_INFORMATION) +
+                                                       BytesToCopy);
+                Status = STATUS_SUCCESS;
+            }
+        }
 
-    /* Convert file times */
-    NtfsDateTimeToFileTime(FileName->CreationTime, &Info->CreationTime);
-    NtfsDateTimeToFileTime(FileName->LastAccessTime, &Info->LastAccessTime);
-    NtfsDateTimeToFileTime(FileName->LastWriteTime, &Info->LastWriteTime);
-    NtfsDateTimeToFileTime(FileName->ChangeTime, &Info->ChangeTime);
+        Info->CreationTime.QuadPart = FileName->CreationTime;
+        Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
+        Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
+        Info->ChangeTime.QuadPart = FileName->ChangeTime;
 
-    /* Convert file flags */
-    NtfsFileFlagsToAttributes(FileName->FileAttributes, &Info->FileAttributes);
+        /* Convert file flags */
+        NtfsFileFlagsToAttributes(FileName->FileAttributes | StdInfo->FileAttribute, &Info->FileAttributes);
 
-    Info->EndOfFile.QuadPart = FileName->DataSize;
-    Info->AllocationSize.QuadPart = FileName->AllocatedSize;
+        Info->EndOfFile.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, (PULONGLONG)&Info->AllocationSize.QuadPart);
 
-//  Info->FileIndex=;
+        Info->FileIndex = MFTIndex;
+    }
 
-    return STATUS_SUCCESS;
+    return Status;
 }
 
 
 static NTSTATUS
 NtfsGetFullDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
                                 PFILE_RECORD_HEADER FileRecord,
-                                PNTFS_ATTR_CONTEXT DataContext,
+                                ULONGLONG MFTIndex,
                                 PFILE_FULL_DIRECTORY_INFORMATION Info,
-                                ULONG BufferLength)
+                                ULONG BufferLength,
+                                PULONG Written,
+                                BOOLEAN First)
 {
     ULONG Length;
+    NTSTATUS Status;
+    ULONG BytesToCopy = 0;
     PFILENAME_ATTRIBUTE FileName;
+    PSTANDARD_INFORMATION StdInfo;
 
     DPRINT("NtfsGetFullDirectoryInformation() called\n");
 
-    FileName = GetFileNameFromRecord(FileRecord);
-    ASSERT(FileName != NULL);
+    *Written = 0;
+    Status = STATUS_BUFFER_OVERFLOW;
+    if (FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName) > BufferLength)
+    {
+        return Status;
+    }
+
+    FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
+    if (FileName == NULL)
+    {
+        DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
+        NtfsDumpFileAttributes(DeviceExt, FileRecord);
+        return STATUS_OBJECT_NAME_NOT_FOUND;
+    }
+
+    StdInfo = GetStandardInformationFromRecord(DeviceExt, FileRecord);
+    ASSERT(StdInfo != NULL);
 
     Length = FileName->NameLength * sizeof (WCHAR);
-    if ((sizeof(FILE_FULL_DIRECTORY_INFORMATION) + Length) > BufferLength)
-        return(STATUS_BUFFER_OVERFLOW);
+    if (First || (BufferLength >= FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName) + Length))
+    {
+        Info->FileNameLength = Length;
+
+        *Written = FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName);
+        Info->NextEntryOffset = 0;
+        if (BufferLength > FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName))
+        {
+            BytesToCopy = min(Length, BufferLength - FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName));
+            RtlCopyMemory(Info->FileName, FileName->Name, BytesToCopy);
+            *Written += BytesToCopy;
 
-    Info->FileNameLength = Length;
-    Info->NextEntryOffset =
-        ROUND_UP(sizeof(FILE_FULL_DIRECTORY_INFORMATION) + Length, sizeof(ULONG));
-    RtlCopyMemory(Info->FileName, FileName->Name, Length);
+            if (BytesToCopy == Length)
+            {
+                Info->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_FULL_DIR_INFORMATION) +
+                                                       BytesToCopy);
+                Status = STATUS_SUCCESS;
+            }
+        }
 
-    /* Convert file times */
-    NtfsDateTimeToFileTime(FileName->CreationTime, &Info->CreationTime);
-    NtfsDateTimeToFileTime(FileName->LastAccessTime, &Info->LastAccessTime);
-    NtfsDateTimeToFileTime(FileName->LastWriteTime, &Info->LastWriteTime);
-    NtfsDateTimeToFileTime(FileName->ChangeTime, &Info->ChangeTime);
+        Info->CreationTime.QuadPart = FileName->CreationTime;
+        Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
+        Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
+        Info->ChangeTime.QuadPart = FileName->ChangeTime;
 
-    /* Convert file flags */
-    NtfsFileFlagsToAttributes(FileName->FileAttributes, &Info->FileAttributes);
+        /* Convert file flags */
+        NtfsFileFlagsToAttributes(FileName->FileAttributes | StdInfo->FileAttribute, &Info->FileAttributes);
 
-    Info->EndOfFile.QuadPart = FileName->DataSize;
-    Info->AllocationSize.QuadPart = FileName->AllocatedSize;
+        Info->EndOfFile.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, (PULONGLONG)&Info->AllocationSize.QuadPart);
 
-//  Info->FileIndex=;
-    Info->EaSize = 0;
+        Info->FileIndex = MFTIndex;
+        Info->EaSize = 0;
+    }
 
-    return STATUS_SUCCESS;
+    return Status;
 }
 
 
 static NTSTATUS
 NtfsGetBothDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
                                 PFILE_RECORD_HEADER FileRecord,
-                                PNTFS_ATTR_CONTEXT DataContext,
+                                ULONGLONG MFTIndex,
                                 PFILE_BOTH_DIR_INFORMATION Info,
-                                ULONG BufferLength)
+                                ULONG BufferLength,
+                                PULONG Written,
+                                BOOLEAN First)
 {
     ULONG Length;
-    PFILENAME_ATTRIBUTE FileName;
+    NTSTATUS Status;
+    ULONG BytesToCopy = 0;
+    PFILENAME_ATTRIBUTE FileName, ShortFileName;
+    PSTANDARD_INFORMATION StdInfo;
 
     DPRINT("NtfsGetBothDirectoryInformation() called\n");
 
-    FileName = GetFileNameFromRecord(FileRecord);
-    ASSERT(FileName != NULL);
+    *Written = 0;
+    Status = STATUS_BUFFER_OVERFLOW;
+    if (FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName) > BufferLength)
+    {
+        return Status;
+    }
+
+    FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
+    if (FileName == NULL)
+    {
+        DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
+        NtfsDumpFileAttributes(DeviceExt, FileRecord);
+        return STATUS_OBJECT_NAME_NOT_FOUND;
+    }
+    ShortFileName = GetFileNameFromRecord(DeviceExt, FileRecord, NTFS_FILE_NAME_DOS);
+
+    StdInfo = GetStandardInformationFromRecord(DeviceExt, FileRecord);
+    ASSERT(StdInfo != NULL);
 
     Length = FileName->NameLength * sizeof (WCHAR);
-    if ((sizeof(FILE_BOTH_DIR_INFORMATION) + Length) > BufferLength)
-        return(STATUS_BUFFER_OVERFLOW);
+    if (First || (BufferLength >= FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName) + Length))
+    {
+        Info->FileNameLength = Length;
+
+        *Written = FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName);
+        Info->NextEntryOffset = 0;
+        if (BufferLength > FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName))
+        {
+            BytesToCopy = min(Length, BufferLength - FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName));
+            RtlCopyMemory(Info->FileName, FileName->Name, BytesToCopy);
+            *Written += BytesToCopy;
 
-    Info->FileNameLength = Length;
-    Info->NextEntryOffset =
-        ROUND_UP(sizeof(FILE_BOTH_DIR_INFORMATION) + Length, sizeof(ULONG));
-    RtlCopyMemory(Info->FileName, FileName->Name, Length);
+            if (BytesToCopy == Length)
+            {
+                Info->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_BOTH_DIR_INFORMATION) +
+                                                       BytesToCopy);
+                Status = STATUS_SUCCESS;
+            }
+        }
 
-    /* Convert file times */
-    NtfsDateTimeToFileTime(FileName->CreationTime, &Info->CreationTime);
-    NtfsDateTimeToFileTime(FileName->LastAccessTime, &Info->LastAccessTime);
-    NtfsDateTimeToFileTime(FileName->LastWriteTime, &Info->LastWriteTime);
-    NtfsDateTimeToFileTime(FileName->ChangeTime, &Info->ChangeTime);
+        if (ShortFileName)
+        {
+            /* Should we upcase the filename? */
+            ASSERT(ShortFileName->NameLength <= ARRAYSIZE(Info->ShortName));
+            Info->ShortNameLength = ShortFileName->NameLength * sizeof(WCHAR);
+            RtlCopyMemory(Info->ShortName, ShortFileName->Name, Info->ShortNameLength);
+        }
+        else
+        {
+            Info->ShortName[0] = 0;
+            Info->ShortNameLength = 0;
+        }
 
-    /* Convert file flags */
-    NtfsFileFlagsToAttributes(FileName->FileAttributes, &Info->FileAttributes);
+        Info->CreationTime.QuadPart = FileName->CreationTime;
+        Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
+        Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
+        Info->ChangeTime.QuadPart = FileName->ChangeTime;
 
-    Info->EndOfFile.QuadPart = FileName->DataSize;
-    Info->AllocationSize.QuadPart = FileName->AllocatedSize;
+        /* Convert file flags */
+        NtfsFileFlagsToAttributes(FileName->FileAttributes | StdInfo->FileAttribute, &Info->FileAttributes);
 
-//  Info->FileIndex=;
-    Info->EaSize = 0;
+        Info->EndOfFile.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, (PULONGLONG)&Info->AllocationSize.QuadPart);
 
-    Info->ShortName[0] = 0;
-    Info->ShortNameLength = 0;
+        Info->FileIndex = MFTIndex;
+        Info->EaSize = 0;
+    }
 
-    return STATUS_SUCCESS;
+    return Status;
 }
 
 
@@ -301,9 +374,9 @@ NtfsQueryDirectory(PNTFS_IRP_CONTEXT IrpContext)
     PFILE_OBJECT FileObject;
     NTSTATUS Status = STATUS_SUCCESS;
     PFILE_RECORD_HEADER FileRecord;
-    PNTFS_ATTR_CONTEXT DataContext;
-    ULONGLONG MFTRecord;
+    ULONGLONG MFTRecord, OldMFTRecord = 0;
     UNICODE_STRING Pattern;
+    ULONG Written;
 
     DPRINT1("NtfsQueryDirectory() called\n");
 
@@ -324,21 +397,35 @@ NtfsQueryDirectory(PNTFS_IRP_CONTEXT IrpContext)
     FileInformationClass = Stack->Parameters.QueryDirectory.FileInformationClass;
     FileIndex = Stack->Parameters.QueryDirectory.FileIndex;
 
+    if (NtfsFCBIsCompressed(Fcb))
+    {
+        DPRINT1("Compressed directory!\n");
+        UNIMPLEMENTED;
+        return STATUS_NOT_IMPLEMENTED;
+    }
+
+    if (!ExAcquireResourceSharedLite(&Fcb->MainResource,
+                                     BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
+    {
+        return STATUS_PENDING;
+    }
+
     if (SearchPattern != NULL)
     {
         if (!Ccb->DirectorySearchPattern)
         {
             First = TRUE;
-            Ccb->DirectorySearchPattern =
-                ExAllocatePoolWithTag(NonPagedPool, SearchPattern->Length + sizeof(WCHAR), TAG_NTFS);
+            Pattern.Length = 0;
+            Pattern.MaximumLength = SearchPattern->Length + sizeof(WCHAR);
+            Ccb->DirectorySearchPattern = Pattern.Buffer =
+                ExAllocatePoolWithTag(NonPagedPool, Pattern.MaximumLength, TAG_NTFS);
             if (!Ccb->DirectorySearchPattern)
             {
+                ExReleaseResourceLite(&Fcb->MainResource);
                 return STATUS_INSUFFICIENT_RESOURCES;
             }
 
-            memcpy(Ccb->DirectorySearchPattern,
-                   SearchPattern->Buffer,
-                   SearchPattern->Length);
+            memcpy(Ccb->DirectorySearchPattern, SearchPattern->Buffer, SearchPattern->Length);
             Ccb->DirectorySearchPattern[SearchPattern->Length / sizeof(WCHAR)] = 0;
         }
     }
@@ -348,6 +435,7 @@ NtfsQueryDirectory(PNTFS_IRP_CONTEXT IrpContext)
         Ccb->DirectorySearchPattern = ExAllocatePoolWithTag(NonPagedPool, 2 * sizeof(WCHAR), TAG_NTFS);
         if (!Ccb->DirectorySearchPattern)
         {
+            ExReleaseResourceLite(&Fcb->MainResource);
             return STATUS_INSUFFICIENT_RESOURCES;
         }
 
@@ -356,184 +444,178 @@ NtfsQueryDirectory(PNTFS_IRP_CONTEXT IrpContext)
     }
 
     RtlInitUnicodeString(&Pattern, Ccb->DirectorySearchPattern);
-
-    DPRINT1("Search pattern '%S'\n", Ccb->DirectorySearchPattern);
-    DPRINT1("In: '%S'\n", Fcb->PathName);
+    DPRINT("Search pattern '%S'\n", Ccb->DirectorySearchPattern);
+    DPRINT("In: '%S'\n", Fcb->PathName);
 
     /* Determine directory index */
     if (Stack->Flags & SL_INDEX_SPECIFIED)
     {
-        Ccb->Entry = Ccb->CurrentByteOffset.u.LowPart;
+        Ccb->Entry = FileIndex;
     }
     else if (First || (Stack->Flags & SL_RESTART_SCAN))
     {
         Ccb->Entry = 0;
     }
 
-    /* Determine Buffer for result */
-    if (Irp->MdlAddress)
-    {
-        Buffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
-    }
-    else
-    {
-        Buffer = Irp->UserBuffer;
-    }
+    /* Get Buffer for result */
+    Buffer = NtfsGetUserBuffer(Irp, FALSE);
 
     DPRINT("Buffer=%p tofind=%S\n", Buffer, Ccb->DirectorySearchPattern);
 
+    if (!ExAcquireResourceExclusiveLite(&DeviceExtension->DirResource,
+                                        BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
+    {
+        ExReleaseResourceLite(&Fcb->MainResource);
+        return STATUS_PENDING;
+    }
+
+    Written = 0;
     while (Status == STATUS_SUCCESS && BufferLength > 0)
     {
         Status = NtfsFindFileAt(DeviceExtension,
                                 &Pattern,
                                 &Ccb->Entry,
                                 &FileRecord,
-                                &DataContext,
                                 &MFTRecord,
-                                Fcb->MFTIndex);
-      //DPRINT("Found %S, Status=%x, entry %x\n", TempFcb.ObjectName, Status, Ccb->Entry);
+                                Fcb->MFTIndex,
+                                BooleanFlagOn(Stack->Flags, SL_CASE_SENSITIVE));
 
         if (NT_SUCCESS(Status))
         {
+            /* HACK: files with both a short name and a long name are present twice in the index.
+             * Ignore the second entry, if it is immediately following the first one.
+             */
+            if (MFTRecord == OldMFTRecord)
+            {
+                DPRINT1("Ignoring duplicate MFT entry 0x%x\n", MFTRecord);
+                Ccb->Entry++;
+                ExFreeToNPagedLookasideList(&DeviceExtension->FileRecLookasideList, FileRecord);
+                continue;
+            }
+            OldMFTRecord = MFTRecord;
+
             switch (FileInformationClass)
             {
-                case FileNameInformation:
-                    Status = NtfsGetNameInformation(DeviceExtension,
-                                                    FileRecord,
-                                                    DataContext,
-                                                    (PFILE_NAMES_INFORMATION)Buffer,
-                                                    BufferLength);
+                case FileNamesInformation:
+                    Status = NtfsGetNamesInformation(DeviceExtension,
+                                                     FileRecord,
+                                                     MFTRecord,
+                                                     (PFILE_NAMES_INFORMATION)Buffer,
+                                                     BufferLength,
+                                                     &Written,
+                                                     Buffer0 == NULL);
                     break;
 
                 case FileDirectoryInformation:
                     Status = NtfsGetDirectoryInformation(DeviceExtension,
                                                          FileRecord,
-                                                         DataContext,
+                                                         MFTRecord,
                                                          (PFILE_DIRECTORY_INFORMATION)Buffer,
-                                                         BufferLength);
+                                                         BufferLength,
+                                                         &Written,
+                                                         Buffer0 == NULL);
                     break;
 
                 case FileFullDirectoryInformation:
                     Status = NtfsGetFullDirectoryInformation(DeviceExtension,
                                                              FileRecord,
-                                                             DataContext,
+                                                             MFTRecord,
                                                              (PFILE_FULL_DIRECTORY_INFORMATION)Buffer,
-                                                             BufferLength);
+                                                             BufferLength,
+                                                             &Written,
+                                                             Buffer0 == NULL);
                     break;
 
                 case FileBothDirectoryInformation:
                     Status = NtfsGetBothDirectoryInformation(DeviceExtension,
                                                              FileRecord,
-                                                             DataContext,
+                                                             MFTRecord,
                                                              (PFILE_BOTH_DIR_INFORMATION)Buffer,
-                                                             BufferLength);
+                                                             BufferLength,
+                                                             &Written,
+                                                             Buffer0 == NULL);
                     break;
 
                 default:
                     Status = STATUS_INVALID_INFO_CLASS;
             }
 
-            if (Status == STATUS_BUFFER_OVERFLOW)
+            if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_INVALID_INFO_CLASS)
             {
-                if (Buffer0)
-                {
-                    Buffer0->NextEntryOffset = 0;
-                }
                 break;
             }
         }
         else
         {
-            if (Buffer0)
-            {
-                Buffer0->NextEntryOffset = 0;
-            }
-
-            if (First)
-            {
-                Status = STATUS_NO_SUCH_FILE;
-            }
-            else
-            {
-                Status = STATUS_NO_MORE_FILES;
-            }
+            Status = (First ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES);
             break;
         }
 
         Buffer0 = (PFILE_NAMES_INFORMATION)Buffer;
         Buffer0->FileIndex = FileIndex++;
         Ccb->Entry++;
+        BufferLength -= Buffer0->NextEntryOffset;
+
+        ExFreeToNPagedLookasideList(&DeviceExtension->FileRecLookasideList, FileRecord);
 
         if (Stack->Flags & SL_RETURN_SINGLE_ENTRY)
         {
             break;
         }
-        BufferLength -= Buffer0->NextEntryOffset;
+
         Buffer += Buffer0->NextEntryOffset;
-        ExFreePoolWithTag(FileRecord, TAG_NTFS);
     }
 
     if (Buffer0)
     {
         Buffer0->NextEntryOffset = 0;
+        Status = STATUS_SUCCESS;
+        IrpContext->Irp->IoStatus.Information = Stack->Parameters.QueryDirectory.Length - BufferLength;
     }
-
-    if (FileIndex > 0)
+    else
     {
-        Status = STATUS_SUCCESS;
+        ASSERT(Status != STATUS_SUCCESS || BufferLength == 0);
+        ASSERT(Written <= Stack->Parameters.QueryDirectory.Length);
+        IrpContext->Irp->IoStatus.Information = Written;
     }
 
+    ExReleaseResourceLite(&DeviceExtension->DirResource);
+    ExReleaseResourceLite(&Fcb->MainResource);
+
     return Status;
 }
 
 
 NTSTATUS
-NTAPI
-NtfsFsdDirectoryControl(PDEVICE_OBJECT DeviceObject,
-                        PIRP Irp)
+NtfsDirectoryControl(PNTFS_IRP_CONTEXT IrpContext)
 {
-    PNTFS_IRP_CONTEXT IrpContext = NULL;
     NTSTATUS Status = STATUS_UNSUCCESSFUL;
 
     DPRINT1("NtfsDirectoryControl() called\n");
 
-    FsRtlEnterFileSystem();
-    ASSERT(DeviceObject);
-    ASSERT(Irp);
-
-    NtfsIsIrpTopLevel(Irp);
-
-    IrpContext = NtfsAllocateIrpContext(DeviceObject, Irp);
-    if (IrpContext)
+    switch (IrpContext->MinorFunction)
     {
-        switch (IrpContext->MinorFunction)
-        {
-            case IRP_MN_QUERY_DIRECTORY:
-                Status = NtfsQueryDirectory(IrpContext);
-                break;
+        case IRP_MN_QUERY_DIRECTORY:
+            Status = NtfsQueryDirectory(IrpContext);
+            break;
 
-            case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
-                DPRINT1("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
-                Status = STATUS_NOT_IMPLEMENTED;
-                break;
+        case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
+            DPRINT1("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
+            Status = STATUS_NOT_IMPLEMENTED;
+            break;
 
-            default:
-                Status = STATUS_INVALID_DEVICE_REQUEST;
-                break;
-        }
+        default:
+            Status = STATUS_INVALID_DEVICE_REQUEST;
+            break;
     }
-    else
-        Status = STATUS_INSUFFICIENT_RESOURCES;
 
-    Irp->IoStatus.Status = Status;
-    Irp->IoStatus.Information = 0;
-    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+    if (Status == STATUS_PENDING && IrpContext->Flags & IRPCONTEXT_COMPLETE)
+    {
+        return NtfsMarkIrpContextForQueue(IrpContext);
+    }
 
-    if (IrpContext)
-        ExFreePoolWithTag(IrpContext, 'PRIN');
+    IrpContext->Irp->IoStatus.Information = 0;
 
-    IoSetTopLevelIrp(NULL);
-    FsRtlExitFileSystem();
     return Status;
 }