[FASTFAT] Fix size checking in VfatGetFileNameInformation()
[reactos.git] / drivers / filesystems / fastfat / dir.c
index 9522cb1..1e657f1 100644 (file)
 
 /* INCLUDES *****************************************************************/
 
-#define NDEBUG
 #include "vfat.h"
 
+#define NDEBUG
+#include <debug.h>
+
 /* FUNCTIONS ****************************************************************/
 
 /* Function like DosDateTimeToFileTime */
 BOOLEAN
-FsdDosDateTimeToSystemTime(PDEVICE_EXTENSION DeviceExt,
-                           USHORT DosDate,
-                           USHORT DosTime,
-                           PLARGE_INTEGER SystemTime)
+FsdDosDateTimeToSystemTime(
+    PDEVICE_EXTENSION DeviceExt,
+    USHORT DosDate,
+    USHORT DosTime,
+    PLARGE_INTEGER SystemTime)
 {
     PDOSTIME pdtime = (PDOSTIME)&DosTime;
     PDOSDATE pddate = (PDOSDATE)&DosDate;
@@ -47,10 +50,11 @@ FsdDosDateTimeToSystemTime(PDEVICE_EXTENSION DeviceExt,
 
 /* Function like FileTimeToDosDateTime */
 BOOLEAN
-FsdSystemTimeToDosDateTime(PDEVICE_EXTENSION DeviceExt,
-                           PLARGE_INTEGER SystemTime,
-                           PUSHORT pDosDate,
-                           PUSHORT pDosTime)
+FsdSystemTimeToDosDateTime(
+    PDEVICE_EXTENSION DeviceExt,
+    PLARGE_INTEGER SystemTime,
+    PUSHORT pDosDate,
+    PUSHORT pDosTime)
 {
     PDOSTIME pdtime = (PDOSTIME)pDosTime;
     PDOSDATE pddate = (PDOSDATE)pDosDate;
@@ -82,298 +86,434 @@ FsdSystemTimeToDosDateTime(PDEVICE_EXTENSION DeviceExt,
 
 #define ULONG_ROUND_UP(x)   ROUND_UP((x), (sizeof(ULONG)))
 
-static NTSTATUS
-VfatGetFileNameInformation(PVFAT_DIRENTRY_CONTEXT DirContext,
-                           PFILE_NAMES_INFORMATION pInfo,
-                           ULONG BufferLength)
+static
+NTSTATUS
+VfatGetFileNameInformation(
+    PVFAT_DIRENTRY_CONTEXT DirContext,
+    PFILE_NAME_INFORMATION pInfo,
+    ULONG BufferLength,
+    PULONG Written,
+    BOOLEAN First)
 {
-    if ((sizeof(FILE_DIRECTORY_INFORMATION) + DirContext->LongNameU.Length) > BufferLength)
-        return STATUS_BUFFER_OVERFLOW;
+    NTSTATUS Status;
+    ULONG BytesToCopy = 0;
+
+    *Written = 0;
+    Status = STATUS_BUFFER_OVERFLOW;
+
+    if (FIELD_OFFSET(FILE_NAME_INFORMATION, FileName) > BufferLength)
+        return Status;
+
+    if (First || (BufferLength >= FIELD_OFFSET(FILE_NAME_INFORMATION, FileName) + DirContext->LongNameU.Length))
+    {
+        pInfo->FileNameLength = DirContext->LongNameU.Length;
 
-    pInfo->FileNameLength = DirContext->LongNameU.Length;
-    pInfo->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_DIRECTORY_INFORMATION) +
-                                            DirContext->LongNameU.Length);
+        *Written = FIELD_OFFSET(FILE_NAME_INFORMATION, FileName);
+        if (BufferLength > FIELD_OFFSET(FILE_NAME_INFORMATION, FileName))
+        {
+            BytesToCopy = min(DirContext->LongNameU.Length, BufferLength - FIELD_OFFSET(FILE_NAME_INFORMATION, FileName));
+            RtlCopyMemory(pInfo->FileName,
+                         DirContext->LongNameU.Buffer,
+                         BytesToCopy);
+            *Written += BytesToCopy;
 
-    RtlCopyMemory(pInfo->FileName,
-                  DirContext->LongNameU.Buffer,
-                  DirContext->LongNameU.Length);
+            if (BytesToCopy == DirContext->LongNameU.Length)
+            {
+                Status = STATUS_SUCCESS;
+            }
+        }
+    }
 
-    return STATUS_SUCCESS;
+    return Status;
 }
 
-static NTSTATUS
-VfatGetFileDirectoryInformation(PVFAT_DIRENTRY_CONTEXT DirContext,
-                                PDEVICE_EXTENSION DeviceExt,
-                                PFILE_DIRECTORY_INFORMATION pInfo,
-                                ULONG BufferLength)
+static
+NTSTATUS
+VfatGetFileNamesInformation(
+    PVFAT_DIRENTRY_CONTEXT DirContext,
+    PFILE_NAMES_INFORMATION pInfo,
+    ULONG BufferLength,
+    PULONG Written,
+    BOOLEAN First)
 {
-    if ((sizeof(FILE_DIRECTORY_INFORMATION) + DirContext->LongNameU.Length) > BufferLength)
-        return STATUS_BUFFER_OVERFLOW;
+    NTSTATUS Status;
+    ULONG BytesToCopy = 0;
 
-    pInfo->FileNameLength = DirContext->LongNameU.Length;
-    pInfo->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_DIRECTORY_INFORMATION) +
-                                            DirContext->LongNameU.Length);
-    /* pInfo->FileIndex = ; */
+    *Written = 0;
+    Status = STATUS_BUFFER_OVERFLOW;
 
-    RtlCopyMemory(pInfo->FileName,
-                  DirContext->LongNameU.Buffer,
-                  DirContext->LongNameU.Length);
+    if (FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName) > BufferLength)
+        return Status;
 
-    if (DeviceExt->Flags & VCB_IS_FATX)
+    if (First || (BufferLength >= FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName) + DirContext->LongNameU.Length))
     {
-        FsdDosDateTimeToSystemTime(DeviceExt,
-                                   DirContext->DirEntry.FatX.CreationDate,
-                                   DirContext->DirEntry.FatX.CreationTime,
-                                   &pInfo->CreationTime);
-        FsdDosDateTimeToSystemTime(DeviceExt,
-                                   DirContext->DirEntry.FatX.AccessDate,
-                                   DirContext->DirEntry.FatX.AccessTime,
-                                   &pInfo->LastAccessTime);
-        FsdDosDateTimeToSystemTime(DeviceExt,
-                                   DirContext->DirEntry.FatX.UpdateDate,
-                                   DirContext->DirEntry.FatX.UpdateTime,
-                                   &pInfo->LastWriteTime);
-
-        pInfo->ChangeTime = pInfo->LastWriteTime;
-
-        if (DirContext->DirEntry.FatX.Attrib & FILE_ATTRIBUTE_DIRECTORY)
-        {
-            pInfo->EndOfFile.QuadPart = 0;
-            pInfo->AllocationSize.QuadPart = 0;
-        }
-        else
-        {
-            pInfo->EndOfFile.u.HighPart = 0;
-            pInfo->EndOfFile.u.LowPart = DirContext->DirEntry.FatX.FileSize;
-            /* Make allocsize a rounded up multiple of BytesPerCluster */
-            pInfo->AllocationSize.u.HighPart = 0;
-            pInfo->AllocationSize.u.LowPart = ROUND_UP(DirContext->DirEntry.FatX.FileSize,
-                                                       DeviceExt->FatInfo.BytesPerCluster);
-        }
-    
-        pInfo->FileAttributes = DirContext->DirEntry.FatX.Attrib & 0x3f;
-    }
-    else
-    {
-        FsdDosDateTimeToSystemTime(DeviceExt,
-                                   DirContext->DirEntry.Fat.CreationDate,
-                                   DirContext->DirEntry.Fat.CreationTime,
-                                   &pInfo->CreationTime);
-        FsdDosDateTimeToSystemTime(DeviceExt,
-                                   DirContext->DirEntry.Fat.AccessDate,
-                                   0,
-                                   &pInfo->LastAccessTime);
-        FsdDosDateTimeToSystemTime(DeviceExt,
-                                   DirContext->DirEntry.Fat.UpdateDate,
-                                   DirContext->DirEntry.Fat.UpdateTime,
-                                   &pInfo->LastWriteTime);
-
-        pInfo->ChangeTime = pInfo->LastWriteTime;
-
-        if (DirContext->DirEntry.Fat.Attrib & FILE_ATTRIBUTE_DIRECTORY)
-        {
-            pInfo->EndOfFile.QuadPart = 0;
-            pInfo->AllocationSize.QuadPart = 0;
-        }
-        else
+        pInfo->FileNameLength = DirContext->LongNameU.Length;
+
+        *Written = FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName);
+        pInfo->NextEntryOffset = 0;
+        if (BufferLength > FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName))
         {
-            pInfo->EndOfFile.u.HighPart = 0;
-            pInfo->EndOfFile.u.LowPart = DirContext->DirEntry.Fat.FileSize;
-            /* Make allocsize a rounded up multiple of BytesPerCluster */
-            pInfo->AllocationSize.u.HighPart = 0;
-            pInfo->AllocationSize.u.LowPart = ROUND_UP(DirContext->DirEntry.Fat.FileSize,
-                                                       DeviceExt->FatInfo.BytesPerCluster);
-        }
+            BytesToCopy = min(DirContext->LongNameU.Length, BufferLength - FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName));
+            RtlCopyMemory(pInfo->FileName,
+                         DirContext->LongNameU.Buffer,
+                         BytesToCopy);
+            *Written += BytesToCopy;
 
-        pInfo->FileAttributes = DirContext->DirEntry.Fat.Attrib & 0x3f;
+            if (BytesToCopy == DirContext->LongNameU.Length)
+            {
+                pInfo->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_NAMES_INFORMATION) +
+                                                        BytesToCopy);
+                Status = STATUS_SUCCESS;
+            }
+        }
     }
 
-    return STATUS_SUCCESS;
+    return Status;
 }
 
-static NTSTATUS
-VfatGetFileFullDirectoryInformation(PVFAT_DIRENTRY_CONTEXT DirContext,
-                                    PDEVICE_EXTENSION DeviceExt,
-                                    PFILE_FULL_DIR_INFORMATION pInfo,
-                                    ULONG BufferLength)
+static
+NTSTATUS
+VfatGetFileDirectoryInformation(
+    PVFAT_DIRENTRY_CONTEXT DirContext,
+    PDEVICE_EXTENSION DeviceExt,
+    PFILE_DIRECTORY_INFORMATION pInfo,
+    ULONG BufferLength,
+    PULONG Written,
+    BOOLEAN First)
 {
-    if ((sizeof(FILE_FULL_DIR_INFORMATION) + DirContext->LongNameU.Length) > BufferLength)
-        return STATUS_BUFFER_OVERFLOW;
+    NTSTATUS Status;
+    ULONG BytesToCopy = 0;
 
-    pInfo->FileNameLength = DirContext->LongNameU.Length;
-    pInfo->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_FULL_DIR_INFORMATION) +
-                                            DirContext->LongNameU.Length);
-    /* pInfo->FileIndex = ; */
-    /* pInfo->EaSize = ; */
+    *Written = 0;
+    Status = STATUS_BUFFER_OVERFLOW;
 
-    RtlCopyMemory(pInfo->FileName,
-                  DirContext->LongNameU.Buffer,
-                  DirContext->LongNameU.Length);
+    if (FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName) > BufferLength)
+        return Status;
 
-    if (DeviceExt->Flags & VCB_IS_FATX)
+    if (First || (BufferLength >= FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName) + DirContext->LongNameU.Length))
     {
-        FsdDosDateTimeToSystemTime(DeviceExt,
-                                   DirContext->DirEntry.FatX.CreationDate,
-                                   DirContext->DirEntry.FatX.CreationTime,
-                                   &pInfo->CreationTime);
-        FsdDosDateTimeToSystemTime(DeviceExt,
-                                   DirContext->DirEntry.FatX.AccessDate,
-                                   DirContext->DirEntry.FatX.AccessTime,
-                                   &pInfo->LastAccessTime);
-        FsdDosDateTimeToSystemTime(DeviceExt,
-                                   DirContext->DirEntry.FatX.UpdateDate,
-                                   DirContext->DirEntry.FatX.UpdateTime,
-                                   &pInfo->LastWriteTime);
-
-        pInfo->ChangeTime = pInfo->LastWriteTime;
-        pInfo->EndOfFile.u.HighPart = 0;
-        pInfo->EndOfFile.u.LowPart = DirContext->DirEntry.FatX.FileSize;
-        /* Make allocsize a rounded up multiple of BytesPerCluster */
-        pInfo->AllocationSize.u.HighPart = 0;
-        pInfo->AllocationSize.u.LowPart = ROUND_UP(DirContext->DirEntry.FatX.FileSize,
-                                                   DeviceExt->FatInfo.BytesPerCluster);
-        pInfo->FileAttributes = DirContext->DirEntry.FatX.Attrib & 0x3f;
-    }
-    else
-    {
-        FsdDosDateTimeToSystemTime(DeviceExt,
-                                   DirContext->DirEntry.Fat.CreationDate,
-                                   DirContext->DirEntry.Fat.CreationTime,
-                                   &pInfo->CreationTime);
-        FsdDosDateTimeToSystemTime(DeviceExt,
-                                   DirContext->DirEntry.Fat.AccessDate,
-                                   0,
-                                   &pInfo->LastAccessTime);
-        FsdDosDateTimeToSystemTime(DeviceExt,
-                                   DirContext->DirEntry.Fat.UpdateDate,
-                                   DirContext->DirEntry.Fat.UpdateTime,
-                                   &pInfo->LastWriteTime);
-
-        pInfo->ChangeTime = pInfo->LastWriteTime;
-        pInfo->EndOfFile.u.HighPart = 0;
-        pInfo->EndOfFile.u.LowPart = DirContext->DirEntry.Fat.FileSize;
-        /* Make allocsize a rounded up multiple of BytesPerCluster */
-        pInfo->AllocationSize.u.HighPart = 0;
-        pInfo->AllocationSize.u.LowPart = ROUND_UP(DirContext->DirEntry.Fat.FileSize,
-                                                   DeviceExt->FatInfo.BytesPerCluster);
-        pInfo->FileAttributes = DirContext->DirEntry.Fat.Attrib & 0x3f;
+        pInfo->FileNameLength = DirContext->LongNameU.Length;
+        /* pInfo->FileIndex = ; */
+
+        *Written = FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName);
+        pInfo->NextEntryOffset = 0;
+        if (BufferLength > FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName))
+        {
+            BytesToCopy = min(DirContext->LongNameU.Length, BufferLength - FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName));
+            RtlCopyMemory(pInfo->FileName,
+                         DirContext->LongNameU.Buffer,
+                         BytesToCopy);
+            *Written += BytesToCopy;
+
+            if (BytesToCopy == DirContext->LongNameU.Length)
+            {
+                pInfo->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_DIRECTORY_INFORMATION) +
+                                                        BytesToCopy);
+                Status = STATUS_SUCCESS;
+            }
+        }
+
+
+
+        if (vfatVolumeIsFatX(DeviceExt))
+        {
+            FsdDosDateTimeToSystemTime(DeviceExt,
+                                       DirContext->DirEntry.FatX.CreationDate,
+                                       DirContext->DirEntry.FatX.CreationTime,
+                                       &pInfo->CreationTime);
+            FsdDosDateTimeToSystemTime(DeviceExt,
+                                       DirContext->DirEntry.FatX.AccessDate,
+                                       DirContext->DirEntry.FatX.AccessTime,
+                                       &pInfo->LastAccessTime);
+            FsdDosDateTimeToSystemTime(DeviceExt,
+                                       DirContext->DirEntry.FatX.UpdateDate,
+                                       DirContext->DirEntry.FatX.UpdateTime,
+                                       &pInfo->LastWriteTime);
+
+            pInfo->ChangeTime = pInfo->LastWriteTime;
+
+            if (BooleanFlagOn(DirContext->DirEntry.FatX.Attrib, FILE_ATTRIBUTE_DIRECTORY))
+            {
+                pInfo->EndOfFile.QuadPart = 0;
+                pInfo->AllocationSize.QuadPart = 0;
+            }
+            else
+            {
+                pInfo->EndOfFile.u.HighPart = 0;
+                pInfo->EndOfFile.u.LowPart = DirContext->DirEntry.FatX.FileSize;
+                /* Make allocsize a rounded up multiple of BytesPerCluster */
+                pInfo->AllocationSize.u.HighPart = 0;
+                pInfo->AllocationSize.u.LowPart = ROUND_UP(DirContext->DirEntry.FatX.FileSize,
+                                                           DeviceExt->FatInfo.BytesPerCluster);
+            }
+
+            pInfo->FileAttributes = DirContext->DirEntry.FatX.Attrib & 0x3f;
+        }
+        else
+        {
+            FsdDosDateTimeToSystemTime(DeviceExt,
+                                       DirContext->DirEntry.Fat.CreationDate,
+                                       DirContext->DirEntry.Fat.CreationTime,
+                                       &pInfo->CreationTime);
+            FsdDosDateTimeToSystemTime(DeviceExt,
+                                       DirContext->DirEntry.Fat.AccessDate,
+                                       0,
+                                       &pInfo->LastAccessTime);
+            FsdDosDateTimeToSystemTime(DeviceExt,
+                                       DirContext->DirEntry.Fat.UpdateDate,
+                                       DirContext->DirEntry.Fat.UpdateTime,
+                                       &pInfo->LastWriteTime);
+
+            pInfo->ChangeTime = pInfo->LastWriteTime;
+
+            if (BooleanFlagOn(DirContext->DirEntry.Fat.Attrib, FILE_ATTRIBUTE_DIRECTORY))
+            {
+                pInfo->EndOfFile.QuadPart = 0;
+                pInfo->AllocationSize.QuadPart = 0;
+            }
+            else
+            {
+                pInfo->EndOfFile.u.HighPart = 0;
+                pInfo->EndOfFile.u.LowPart = DirContext->DirEntry.Fat.FileSize;
+                /* Make allocsize a rounded up multiple of BytesPerCluster */
+                pInfo->AllocationSize.u.HighPart = 0;
+                pInfo->AllocationSize.u.LowPart = ROUND_UP(DirContext->DirEntry.Fat.FileSize,
+                                                           DeviceExt->FatInfo.BytesPerCluster);
+            }
+
+            pInfo->FileAttributes = DirContext->DirEntry.Fat.Attrib & 0x3f;
+        }
     }
 
-    return STATUS_SUCCESS;
+    return Status;
 }
 
-static NTSTATUS
-VfatGetFileBothInformation(PVFAT_DIRENTRY_CONTEXT DirContext,
-                           PDEVICE_EXTENSION DeviceExt,
-                           PFILE_BOTH_DIR_INFORMATION pInfo,
-                           ULONG BufferLength)
+static
+NTSTATUS
+VfatGetFileFullDirectoryInformation(
+    PVFAT_DIRENTRY_CONTEXT DirContext,
+    PDEVICE_EXTENSION DeviceExt,
+    PFILE_FULL_DIR_INFORMATION pInfo,
+    ULONG BufferLength,
+    PULONG Written,
+    BOOLEAN First)
 {
-    if ((sizeof(FILE_BOTH_DIR_INFORMATION) + DirContext->LongNameU.Length) > BufferLength)
-        return STATUS_BUFFER_OVERFLOW;
+    NTSTATUS Status;
+    ULONG BytesToCopy = 0;
+
+    *Written = 0;
+    Status = STATUS_BUFFER_OVERFLOW;
 
-    pInfo->EaSize = 0;
+    if (FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName) > BufferLength)
+        return Status;
 
-    if (DeviceExt->Flags & VCB_IS_FATX)
+    if (First || (BufferLength >= FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName) + DirContext->LongNameU.Length))
     {
         pInfo->FileNameLength = DirContext->LongNameU.Length;
-
-        RtlCopyMemory(pInfo->FileName,
-                      DirContext->LongNameU.Buffer,
-                      DirContext->LongNameU.Length);
-
-        pInfo->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_BOTH_DIR_INFORMATION) +
-                                                DirContext->LongNameU.Length);
-        pInfo->ShortName[0] = 0;
-        pInfo->ShortNameLength = 0;
         /* pInfo->FileIndex = ; */
+        pInfo->EaSize = 0;
 
-        FsdDosDateTimeToSystemTime(DeviceExt,
-                                   DirContext->DirEntry.FatX.CreationDate,
-                                   DirContext->DirEntry.FatX.CreationTime,
-                                   &pInfo->CreationTime);
-        FsdDosDateTimeToSystemTime(DeviceExt,
-                                   DirContext->DirEntry.FatX.AccessDate,
-                                   DirContext->DirEntry.FatX.AccessTime,
-                                   &pInfo->LastAccessTime);
-        FsdDosDateTimeToSystemTime(DeviceExt,
-                                   DirContext->DirEntry.FatX.UpdateDate,
-                                   DirContext->DirEntry.FatX.UpdateTime,
-                                   &pInfo->LastWriteTime);
-
-        pInfo->ChangeTime = pInfo->LastWriteTime;
-
-        if (DirContext->DirEntry.FatX.Attrib & FILE_ATTRIBUTE_DIRECTORY)
+        *Written = FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName);
+        pInfo->NextEntryOffset = 0;
+        if (BufferLength > FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName))
         {
-            pInfo->EndOfFile.QuadPart = 0;
-            pInfo->AllocationSize.QuadPart = 0;
+            BytesToCopy = min(DirContext->LongNameU.Length, BufferLength - FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName));
+            RtlCopyMemory(pInfo->FileName,
+                         DirContext->LongNameU.Buffer,
+                         BytesToCopy);
+            *Written += BytesToCopy;
+
+            if (BytesToCopy == DirContext->LongNameU.Length)
+            {
+                pInfo->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_FULL_DIR_INFORMATION) +
+                                                        BytesToCopy);
+                Status = STATUS_SUCCESS;
+            }
         }
-        else
+
+        if (vfatVolumeIsFatX(DeviceExt))
         {
+            FsdDosDateTimeToSystemTime(DeviceExt,
+                                       DirContext->DirEntry.FatX.CreationDate,
+                                       DirContext->DirEntry.FatX.CreationTime,
+                                       &pInfo->CreationTime);
+            FsdDosDateTimeToSystemTime(DeviceExt,
+                                       DirContext->DirEntry.FatX.AccessDate,
+                                       DirContext->DirEntry.FatX.AccessTime,
+                                       &pInfo->LastAccessTime);
+            FsdDosDateTimeToSystemTime(DeviceExt,
+                                       DirContext->DirEntry.FatX.UpdateDate,
+                                       DirContext->DirEntry.FatX.UpdateTime,
+                                       &pInfo->LastWriteTime);
+
+            pInfo->ChangeTime = pInfo->LastWriteTime;
             pInfo->EndOfFile.u.HighPart = 0;
             pInfo->EndOfFile.u.LowPart = DirContext->DirEntry.FatX.FileSize;
             /* Make allocsize a rounded up multiple of BytesPerCluster */
             pInfo->AllocationSize.u.HighPart = 0;
             pInfo->AllocationSize.u.LowPart = ROUND_UP(DirContext->DirEntry.FatX.FileSize,
                                                        DeviceExt->FatInfo.BytesPerCluster);
+            pInfo->FileAttributes = DirContext->DirEntry.FatX.Attrib & 0x3f;
+        }
+        else
+        {
+            FsdDosDateTimeToSystemTime(DeviceExt,
+                                       DirContext->DirEntry.Fat.CreationDate,
+                                       DirContext->DirEntry.Fat.CreationTime,
+                                       &pInfo->CreationTime);
+            FsdDosDateTimeToSystemTime(DeviceExt,
+                                       DirContext->DirEntry.Fat.AccessDate,
+                                       0,
+                                       &pInfo->LastAccessTime);
+            FsdDosDateTimeToSystemTime(DeviceExt,
+                                       DirContext->DirEntry.Fat.UpdateDate,
+                                       DirContext->DirEntry.Fat.UpdateTime,
+                                       &pInfo->LastWriteTime);
+
+            pInfo->ChangeTime = pInfo->LastWriteTime;
+            pInfo->EndOfFile.u.HighPart = 0;
+            pInfo->EndOfFile.u.LowPart = DirContext->DirEntry.Fat.FileSize;
+            /* Make allocsize a rounded up multiple of BytesPerCluster */
+            pInfo->AllocationSize.u.HighPart = 0;
+            pInfo->AllocationSize.u.LowPart = ROUND_UP(DirContext->DirEntry.Fat.FileSize,
+                                                       DeviceExt->FatInfo.BytesPerCluster);
+            pInfo->FileAttributes = DirContext->DirEntry.Fat.Attrib & 0x3f;
         }
-
-        pInfo->FileAttributes = DirContext->DirEntry.FatX.Attrib & 0x3f;
     }
-    else
-    {
-        pInfo->FileNameLength = DirContext->LongNameU.Length;
-        pInfo->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_BOTH_DIR_INFORMATION) +
-                                                DirContext->LongNameU.Length);
 
-        RtlCopyMemory(pInfo->ShortName,
-                      DirContext->ShortNameU.Buffer,
-                      DirContext->ShortNameU.Length);
+    return Status;
+}
 
-        pInfo->ShortNameLength = (CCHAR)DirContext->ShortNameU.Length;
+static
+NTSTATUS
+VfatGetFileBothInformation(
+    PVFAT_DIRENTRY_CONTEXT DirContext,
+    PDEVICE_EXTENSION DeviceExt,
+    PFILE_BOTH_DIR_INFORMATION pInfo,
+    ULONG BufferLength,
+    PULONG Written,
+    BOOLEAN First)
+{
+    NTSTATUS Status;
+    ULONG BytesToCopy = 0;
 
-        RtlCopyMemory(pInfo->FileName,
-                      DirContext->LongNameU.Buffer,
-                      DirContext->LongNameU.Length);
+    *Written = 0;
+    Status = STATUS_BUFFER_OVERFLOW;
 
-        /* pInfo->FileIndex = ; */
+    if (FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName) > BufferLength)
+        return Status;
+
+    if (First || (BufferLength >= FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName) + DirContext->LongNameU.Length))
+    {
+        pInfo->FileNameLength = DirContext->LongNameU.Length;
+        pInfo->EaSize = 0;
+
+        *Written = FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName);
+        pInfo->NextEntryOffset = 0;
+        if (BufferLength > FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName))
+        {
+            BytesToCopy = min(DirContext->LongNameU.Length, BufferLength - FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName));
+            RtlCopyMemory(pInfo->FileName,
+                         DirContext->LongNameU.Buffer,
+                         BytesToCopy);
+            *Written += BytesToCopy;
+
+            if (BytesToCopy == DirContext->LongNameU.Length)
+            {
+                pInfo->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_BOTH_DIR_INFORMATION) +
+                                                        BytesToCopy);
+                Status = STATUS_SUCCESS;
+            }
+        }
 
-        FsdDosDateTimeToSystemTime(DeviceExt,
-                                   DirContext->DirEntry.Fat.CreationDate,
-                                   DirContext->DirEntry.Fat.CreationTime,
-                                   &pInfo->CreationTime);
-        FsdDosDateTimeToSystemTime(DeviceExt,
-                                   DirContext->DirEntry.Fat.AccessDate,
-                                   0,
-                                   &pInfo->LastAccessTime);
-        FsdDosDateTimeToSystemTime(DeviceExt,
-                                   DirContext->DirEntry.Fat.UpdateDate,
-                                   DirContext->DirEntry.Fat.UpdateTime,
-                                   &pInfo->LastWriteTime);
-
-        pInfo->ChangeTime = pInfo->LastWriteTime;
-
-        if (DirContext->DirEntry.Fat.Attrib & FILE_ATTRIBUTE_DIRECTORY)
+        if (vfatVolumeIsFatX(DeviceExt))
         {
-            pInfo->EndOfFile.QuadPart = 0;
-            pInfo->AllocationSize.QuadPart = 0;
+            pInfo->ShortName[0] = 0;
+            pInfo->ShortNameLength = 0;
+            /* pInfo->FileIndex = ; */
+
+            FsdDosDateTimeToSystemTime(DeviceExt,
+                                       DirContext->DirEntry.FatX.CreationDate,
+                                       DirContext->DirEntry.FatX.CreationTime,
+                                       &pInfo->CreationTime);
+            FsdDosDateTimeToSystemTime(DeviceExt,
+                                       DirContext->DirEntry.FatX.AccessDate,
+                                       DirContext->DirEntry.FatX.AccessTime,
+                                       &pInfo->LastAccessTime);
+            FsdDosDateTimeToSystemTime(DeviceExt,
+                                       DirContext->DirEntry.FatX.UpdateDate,
+                                       DirContext->DirEntry.FatX.UpdateTime,
+                                       &pInfo->LastWriteTime);
+
+            pInfo->ChangeTime = pInfo->LastWriteTime;
+
+            if (BooleanFlagOn(DirContext->DirEntry.FatX.Attrib, FILE_ATTRIBUTE_DIRECTORY))
+            {
+                pInfo->EndOfFile.QuadPart = 0;
+                pInfo->AllocationSize.QuadPart = 0;
+            }
+            else
+            {
+                pInfo->EndOfFile.u.HighPart = 0;
+                pInfo->EndOfFile.u.LowPart = DirContext->DirEntry.FatX.FileSize;
+                /* Make allocsize a rounded up multiple of BytesPerCluster */
+                pInfo->AllocationSize.u.HighPart = 0;
+                pInfo->AllocationSize.u.LowPart = ROUND_UP(DirContext->DirEntry.FatX.FileSize,
+                                                           DeviceExt->FatInfo.BytesPerCluster);
+            }
+
+            pInfo->FileAttributes = DirContext->DirEntry.FatX.Attrib & 0x3f;
         }
         else
         {
-            pInfo->EndOfFile.u.HighPart = 0;
-            pInfo->EndOfFile.u.LowPart = DirContext->DirEntry.Fat.FileSize;
-            /* Make allocsize a rounded up multiple of BytesPerCluster */
-            pInfo->AllocationSize.u.HighPart = 0;
-            pInfo->AllocationSize.u.LowPart = ROUND_UP(DirContext->DirEntry.Fat.FileSize, DeviceExt->FatInfo.BytesPerCluster);
-        }
+            pInfo->ShortNameLength = (CCHAR)DirContext->ShortNameU.Length;
+
+            RtlCopyMemory(pInfo->FileName,
+                          DirContext->LongNameU.Buffer,
+                          DirContext->LongNameU.Length);
+
+            /* pInfo->FileIndex = ; */
+
+            FsdDosDateTimeToSystemTime(DeviceExt,
+                                       DirContext->DirEntry.Fat.CreationDate,
+                                       DirContext->DirEntry.Fat.CreationTime,
+                                       &pInfo->CreationTime);
+            FsdDosDateTimeToSystemTime(DeviceExt,
+                                       DirContext->DirEntry.Fat.AccessDate,
+                                       0,
+                                       &pInfo->LastAccessTime);
+            FsdDosDateTimeToSystemTime(DeviceExt,
+                                       DirContext->DirEntry.Fat.UpdateDate,
+                                       DirContext->DirEntry.Fat.UpdateTime,
+                                       &pInfo->LastWriteTime);
+
+            pInfo->ChangeTime = pInfo->LastWriteTime;
+
+            if (BooleanFlagOn(DirContext->DirEntry.Fat.Attrib, FILE_ATTRIBUTE_DIRECTORY))
+            {
+                pInfo->EndOfFile.QuadPart = 0;
+                pInfo->AllocationSize.QuadPart = 0;
+            }
+            else
+            {
+                pInfo->EndOfFile.u.HighPart = 0;
+                pInfo->EndOfFile.u.LowPart = DirContext->DirEntry.Fat.FileSize;
+                /* Make allocsize a rounded up multiple of BytesPerCluster */
+                pInfo->AllocationSize.u.HighPart = 0;
+                pInfo->AllocationSize.u.LowPart = ROUND_UP(DirContext->DirEntry.Fat.FileSize, DeviceExt->FatInfo.BytesPerCluster);
+            }
 
-        pInfo->FileAttributes = DirContext->DirEntry.Fat.Attrib & 0x3f;
+            pInfo->FileAttributes = DirContext->DirEntry.Fat.Attrib & 0x3f;
+        }
     }
 
-    return STATUS_SUCCESS;
+    return Status;
 }
 
-static NTSTATUS
-DoQuery(PVFAT_IRP_CONTEXT IrpContext)
+static
+NTSTATUS
+DoQuery(
+    PVFAT_IRP_CONTEXT IrpContext)
 {
     NTSTATUS Status = STATUS_SUCCESS;
     LONG BufferLength = 0;
@@ -388,6 +528,7 @@ DoQuery(PVFAT_IRP_CONTEXT IrpContext)
     VFAT_DIRENTRY_CONTEXT DirContext;
     WCHAR LongNameBuffer[LONGNAME_MAX_LENGTH + 1];
     WCHAR ShortNameBuffer[13];
+    ULONG Written;
 
     PIO_STACK_LOCATION Stack = IrpContext->Stack;
 
@@ -405,11 +546,22 @@ DoQuery(PVFAT_IRP_CONTEXT IrpContext)
         ProbeForWrite(IrpContext->Irp->UserBuffer, BufferLength, 1);
     }
 #endif
-    Buffer = VfatGetUserBuffer(IrpContext->Irp);
+    Buffer = VfatGetUserBuffer(IrpContext->Irp, FALSE);
+
+    if (!ExAcquireResourceExclusiveLite(&IrpContext->DeviceExt->DirResource,
+                                        BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
+    {
+        Status = VfatLockUserBuffer(IrpContext->Irp, BufferLength, IoWriteAccess);
+        if (NT_SUCCESS(Status))
+            Status = STATUS_PENDING;
+
+        return Status;
+    }
 
     if (!ExAcquireResourceSharedLite(&pFcb->MainResource,
-                                     (BOOLEAN)(IrpContext->Flags & IRPCONTEXT_CANWAIT)))
+                                     BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
     {
+        ExReleaseResourceLite(&IrpContext->DeviceExt->DirResource);
         Status = VfatLockUserBuffer(IrpContext->Irp, BufferLength, IoWriteAccess);
         if (NT_SUCCESS(Status))
             Status = STATUS_PENDING;
@@ -433,7 +585,7 @@ DoQuery(PVFAT_IRP_CONTEXT IrpContext)
      * -> The pattern length is not null
      * -> The pattern buffer is not null
      * Otherwise, we'll fall later and allocate a match all (*) pattern
-     */ 
+     */
     if (pSearchPattern &&
         pSearchPattern->Length != 0 && pSearchPattern->Buffer != NULL)
     {
@@ -447,6 +599,7 @@ DoQuery(PVFAT_IRP_CONTEXT IrpContext)
             if (!pCcb->SearchPattern.Buffer)
             {
                 ExReleaseResourceLite(&pFcb->MainResource);
+                ExReleaseResourceLite(&IrpContext->DeviceExt->DirResource);
                 return STATUS_INSUFFICIENT_RESOURCES;
             }
             RtlCopyUnicodeString(&pCcb->SearchPattern, pSearchPattern);
@@ -463,6 +616,7 @@ DoQuery(PVFAT_IRP_CONTEXT IrpContext)
         if (!pCcb->SearchPattern.Buffer)
         {
             ExReleaseResourceLite(&pFcb->MainResource);
+            ExReleaseResourceLite(&IrpContext->DeviceExt->DirResource);
             return STATUS_INSUFFICIENT_RESOURCES;
         }
         pCcb->SearchPattern.Buffer[0] = L'*';
@@ -470,11 +624,11 @@ DoQuery(PVFAT_IRP_CONTEXT IrpContext)
         pCcb->SearchPattern.Length = sizeof(WCHAR);
     }
 
-    if (IrpContext->Stack->Flags & SL_INDEX_SPECIFIED)
+    if (BooleanFlagOn(IrpContext->Stack->Flags, SL_INDEX_SPECIFIED))
     {
         DirContext.DirIndex = pCcb->Entry = Stack->Parameters.QueryDirectory.FileIndex;
     }
-    else if (FirstQuery || (IrpContext->Stack->Flags & SL_RESTART_SCAN))
+    else if (FirstQuery || BooleanFlagOn(IrpContext->Stack->Flags, SL_RESTART_SCAN))
     {
         DirContext.DirIndex = pCcb->Entry = 0;
     }
@@ -490,6 +644,7 @@ DoQuery(PVFAT_IRP_CONTEXT IrpContext)
     DirContext.ShortNameU.Buffer = ShortNameBuffer;
     DirContext.ShortNameU.MaximumLength = sizeof(ShortNameBuffer);
 
+    Written = 0;
     while ((Status == STATUS_SUCCESS) && (BufferLength > 0))
     {
         Status = FindFile(IrpContext->DeviceExt,
@@ -508,31 +663,47 @@ DoQuery(PVFAT_IRP_CONTEXT IrpContext)
             {
                 case FileNameInformation:
                     Status = VfatGetFileNameInformation(&DirContext,
-                                                        (PFILE_NAMES_INFORMATION)Buffer,
-                                                        BufferLength);
+                                                        (PFILE_NAME_INFORMATION)Buffer,
+                                                        BufferLength,
+                                                        &Written,
+                                                        Buffer0 == NULL);
                     break;
 
                 case FileDirectoryInformation:
                     Status = VfatGetFileDirectoryInformation(&DirContext,
                                                              IrpContext->DeviceExt,
                                                              (PFILE_DIRECTORY_INFORMATION)Buffer,
-                                                             BufferLength);
+                                                             BufferLength,
+                                                             &Written,
+                                                             Buffer0 == NULL);
                     break;
 
                 case FileFullDirectoryInformation:
                     Status = VfatGetFileFullDirectoryInformation(&DirContext,
                                                                  IrpContext->DeviceExt,
                                                                  (PFILE_FULL_DIR_INFORMATION)Buffer,
-                                                                 BufferLength);
+                                                                 BufferLength,
+                                                                 &Written,
+                                                                 Buffer0 == NULL);
                     break;
 
                 case FileBothDirectoryInformation:
                     Status = VfatGetFileBothInformation(&DirContext,
                                                         IrpContext->DeviceExt,
                                                         (PFILE_BOTH_DIR_INFORMATION)Buffer,
-                                                        BufferLength);
+                                                        BufferLength,
+                                                        &Written,
+                                                        Buffer0 == NULL);
                     break;
 
+                case FileNamesInformation:
+                    Status = VfatGetFileNamesInformation(&DirContext,
+                                                         (PFILE_NAMES_INFORMATION)Buffer,
+                                                         BufferLength,
+                                                         &Written,
+                                                         Buffer0 == NULL);
+                     break;
+
                 default:
                     Status = STATUS_INVALID_INFO_CLASS;
                     break;
@@ -552,7 +723,7 @@ DoQuery(PVFAT_IRP_CONTEXT IrpContext)
         pCcb->Entry = ++DirContext.DirIndex;
         BufferLength -= Buffer0->NextEntryOffset;
 
-        if (IrpContext->Stack->Flags & SL_RETURN_SINGLE_ENTRY)
+        if (BooleanFlagOn(IrpContext->Stack->Flags, SL_RETURN_SINGLE_ENTRY))
             break;
 
         Buffer += Buffer0->NextEntryOffset;
@@ -564,18 +735,51 @@ DoQuery(PVFAT_IRP_CONTEXT IrpContext)
         Status = STATUS_SUCCESS;
         IrpContext->Irp->IoStatus.Information = Stack->Parameters.QueryDirectory.Length - BufferLength;
     }
+    else
+    {
+        ASSERT(Status != STATUS_SUCCESS || BufferLength == 0);
+        ASSERT(Written <= Stack->Parameters.QueryDirectory.Length);
+        IrpContext->Irp->IoStatus.Information = Written;
+    }
 
     ExReleaseResourceLite(&pFcb->MainResource);
+    ExReleaseResourceLite(&IrpContext->DeviceExt->DirResource);
 
     return Status;
 }
 
+NTSTATUS VfatNotifyChangeDirectory(PVFAT_IRP_CONTEXT IrpContext)
+{
+    PVCB pVcb;
+    PVFATFCB pFcb;
+    PIO_STACK_LOCATION Stack;
+    Stack = IrpContext->Stack;
+    pVcb = IrpContext->DeviceExt;
+    pFcb = (PVFATFCB) IrpContext->FileObject->FsContext;
+
+    FsRtlNotifyFullChangeDirectory(pVcb->NotifySync,
+                                   &(pVcb->NotifyList),
+                                   IrpContext->FileObject->FsContext2,
+                                   (PSTRING)&(pFcb->PathNameU),
+                                   BooleanFlagOn(Stack->Flags, SL_WATCH_TREE),
+                                   FALSE,
+                                   Stack->Parameters.NotifyDirectory.CompletionFilter,
+                                   IrpContext->Irp,
+                                   NULL,
+                                   NULL);
+
+    /* We won't handle IRP completion */
+    IrpContext->Flags &= ~IRPCONTEXT_COMPLETE;
+
+    return STATUS_PENDING;
+}
 
 /*
  * FUNCTION: directory control : read/write directory informations
  */
 NTSTATUS
-VfatDirectoryControl(PVFAT_IRP_CONTEXT IrpContext)
+VfatDirectoryControl(
+    PVFAT_IRP_CONTEXT IrpContext)
 {
     NTSTATUS Status = STATUS_SUCCESS;
 
@@ -588,8 +792,7 @@ VfatDirectoryControl(PVFAT_IRP_CONTEXT IrpContext)
             break;
 
         case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
-            DPRINT("VFAT, dir : change\n");
-            Status = STATUS_NOT_IMPLEMENTED;
+            Status = VfatNotifyChangeDirectory(IrpContext);
             break;
 
         default:
@@ -600,15 +803,9 @@ VfatDirectoryControl(PVFAT_IRP_CONTEXT IrpContext)
             break;
     }
 
-    if (Status == STATUS_PENDING)
-    {
-        Status = VfatQueueRequest(IrpContext);
-    }
-    else
+    if (Status == STATUS_PENDING && BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_COMPLETE))
     {
-        IrpContext->Irp->IoStatus.Status = Status;
-        IoCompleteRequest (IrpContext->Irp, IO_NO_INCREMENT);
-        VfatFreeIrpContext(IrpContext);
+        return VfatMarkIrpContextForQueue(IrpContext);
     }
 
     return Status;