[FASTFAT] Implement support for stack overflow in read operations.
[reactos.git] / drivers / filesystems / fastfat / rw.c
index ab16183..76ffff9 100644 (file)
@@ -1,17 +1,20 @@
 /*
  * COPYRIGHT:        See COPYING in the top level directory
  * PROJECT:          ReactOS kernel
- * FILE:             drivers/fs/vfat/rw.c
+ * FILE:             drivers/filesystems/fastfat/rw.c
  * PURPOSE:          VFAT Filesystem
  * PROGRAMMER:       Jason Filby (jasonfilby@yahoo.com)
+ *                   Pierre Schweitzer (pierre@reactos.org)
  *
  */
 
 /* INCLUDES *****************************************************************/
 
-#define NDEBUG
 #include "vfat.h"
 
+#define NDEBUG
+#include <debug.h>
+
 /*
  * Uncomment to enable strict verification of cluster/offset pair
  * caching. If this option is enabled you lose all the benefits of
  */
 /* #define DEBUG_VERIFY_OFFSET_CACHING */
 
+/* Arbitrary, taken from MS FastFAT, should be
+ * refined given what we experience in common
+ * out of stack operations
+ */
+#define OVERFLOW_READ_THRESHHOLD 0xE00
+
 /* FUNCTIONS *****************************************************************/
 
+/*
+ * Return the next cluster in a FAT chain, possibly extending the chain if
+ * necessary
+ */
 NTSTATUS
-NextCluster(PDEVICE_EXTENSION DeviceExt,
-           ULONG FirstCluster,
-           PULONG CurrentCluster,
-           BOOLEAN Extend)
-     /*
-      * Return the next cluster in a FAT chain, possibly extending the chain if
-      * necessary
-      */
+NextCluster(
+    PDEVICE_EXTENSION DeviceExt,
+    ULONG FirstCluster,
+    PULONG CurrentCluster,
+    BOOLEAN Extend)
 {
- if (FirstCluster == 1)
   if (FirstCluster == 1)
     {
-      (*CurrentCluster) += DeviceExt->FatInfo.SectorsPerCluster;
-      return(STATUS_SUCCESS);
+        (*CurrentCluster) += DeviceExt->FatInfo.SectorsPerCluster;
+        return STATUS_SUCCESS;
     }
-  else
+    else
     {
-      if (Extend)
-        return GetNextClusterExtend(DeviceExt, (*CurrentCluster), CurrentCluster);
-      else
-        return GetNextCluster(DeviceExt, (*CurrentCluster), CurrentCluster);
+        if (Extend)
+            return GetNextClusterExtend(DeviceExt, (*CurrentCluster), CurrentCluster);
+        else
+            return GetNextCluster(DeviceExt, (*CurrentCluster), CurrentCluster);
     }
 }
 
 NTSTATUS
-OffsetToCluster(PDEVICE_EXTENSION DeviceExt,
-               ULONG FirstCluster,
-               ULONG FileOffset,
-               PULONG Cluster,
-               BOOLEAN Extend)
-     /*
-      * Return the cluster corresponding to an offset within a file,
-      * possibly extending the file if necessary
-      */
+OffsetToCluster(
+    PDEVICE_EXTENSION DeviceExt,
+    ULONG FirstCluster,
+    ULONG FileOffset,
+    PULONG Cluster,
+    BOOLEAN Extend)
 {
-  ULONG CurrentCluster;
-  ULONG i;
-  NTSTATUS Status;
+    ULONG CurrentCluster;
+    ULONG i;
+    NTSTATUS Status;
 /*
-  DPRINT("OffsetToCluster(DeviceExt %x, Fcb %x, FirstCluster %x,"
-         " FileOffset %x, Cluster %x, Extend %d)\n", DeviceExt,
-         Fcb, FirstCluster, FileOffset, Cluster, Extend);
+    DPRINT("OffsetToCluster(DeviceExt %x, Fcb %x, FirstCluster %x,"
+           " FileOffset %x, Cluster %x, Extend %d)\n", DeviceExt,
+           Fcb, FirstCluster, FileOffset, Cluster, Extend);
 */
-  if (FirstCluster == 0)
-  {
-    DbgPrint("OffsetToCluster is called with FirstCluster = 0!\n");
-    ASSERT(FALSE);
-  }
+    if (FirstCluster == 0)
+    {
+        DbgPrint("OffsetToCluster is called with FirstCluster = 0!\n");
+        ASSERT(FALSE);
+    }
 
-  if (FirstCluster == 1)
+    if (FirstCluster == 1)
     {
-      /* root of FAT16 or FAT12 */
-      *Cluster = DeviceExt->FatInfo.rootStart + FileOffset
-       / (DeviceExt->FatInfo.BytesPerCluster) * DeviceExt->FatInfo.SectorsPerCluster;
-      return(STATUS_SUCCESS);
+        /* root of FAT16 or FAT12 */
+        *Cluster = DeviceExt->FatInfo.rootStart + FileOffset
+            / (DeviceExt->FatInfo.BytesPerCluster) * DeviceExt->FatInfo.SectorsPerCluster;
+        return STATUS_SUCCESS;
     }
-  else
+    else
     {
-      CurrentCluster = FirstCluster;
-      if (Extend)
+        CurrentCluster = FirstCluster;
+        if (Extend)
         {
-          for (i = 0; i < FileOffset / DeviceExt->FatInfo.BytesPerCluster; i++)
+            for (i = 0; i < FileOffset / DeviceExt->FatInfo.BytesPerCluster; i++)
             {
-              Status = GetNextClusterExtend (DeviceExt, CurrentCluster, &CurrentCluster);
-              if (!NT_SUCCESS(Status))
-                return(Status);
-           }
-          *Cluster = CurrentCluster;
-       }
-     else
+                Status = GetNextClusterExtend (DeviceExt, CurrentCluster, &CurrentCluster);
+                if (!NT_SUCCESS(Status))
+                    return Status;
+            }
+            *Cluster = CurrentCluster;
+        }
+        else
         {
-          for (i = 0; i < FileOffset / DeviceExt->FatInfo.BytesPerCluster; i++)
+            for (i = 0; i < FileOffset / DeviceExt->FatInfo.BytesPerCluster; i++)
             {
-              Status = GetNextCluster (DeviceExt, CurrentCluster, &CurrentCluster);
-              if (!NT_SUCCESS(Status))
-                return(Status);
-           }
-          *Cluster = CurrentCluster;
+                Status = GetNextCluster (DeviceExt, CurrentCluster, &CurrentCluster);
+                if (!NT_SUCCESS(Status))
+                    return Status;
+            }
+            *Cluster = CurrentCluster;
        }
-     return(STATUS_SUCCESS);
+       return STATUS_SUCCESS;
    }
 }
 
-static NTSTATUS
-VfatReadFileData (PVFAT_IRP_CONTEXT IrpContext,
-                  ULONG Length,
-                 LARGE_INTEGER ReadOffset,
-                 PULONG LengthRead)
 /*
  * FUNCTION: Reads data from a file
  */
+static
+NTSTATUS
+VfatReadFileData(
+    PVFAT_IRP_CONTEXT IrpContext,
+    ULONG Length,
+    LARGE_INTEGER ReadOffset,
+    PULONG LengthRead)
 {
-  ULONG CurrentCluster;
-  ULONG FirstCluster;
-  ULONG StartCluster;
-  ULONG ClusterCount;
-  LARGE_INTEGER StartOffset;
-  PDEVICE_EXTENSION DeviceExt;
-  BOOLEAN First = TRUE;
-  PVFATFCB Fcb;
-  NTSTATUS Status;
-  ULONG BytesDone;
-  ULONG BytesPerSector;
-  ULONG BytesPerCluster;
-  ULONG LastCluster;
-  ULONG LastOffset;
-
-  /* PRECONDITION */
-  ASSERT(IrpContext);
-  DeviceExt = IrpContext->DeviceExt;
-  ASSERT(DeviceExt);
-  ASSERT(DeviceExt->FatInfo.BytesPerCluster);
-  ASSERT(IrpContext->FileObject);
-  ASSERT(IrpContext->FileObject->FsContext2 != NULL);
-
-  DPRINT("VfatReadFileData(DeviceExt %p, FileObject %p, "
-        "Length %d, ReadOffset 0x%I64x)\n", DeviceExt,
-        IrpContext->FileObject, Length, ReadOffset.QuadPart);
-
-  *LengthRead = 0;
-
-  Fcb = IrpContext->FileObject->FsContext;
-  BytesPerSector = DeviceExt->FatInfo.BytesPerSector;
-  BytesPerCluster = DeviceExt->FatInfo.BytesPerCluster;
-
-  ASSERT(ReadOffset.QuadPart + Length <= ROUND_UP(Fcb->RFCB.FileSize.QuadPart, BytesPerSector));
-  ASSERT(ReadOffset.u.LowPart % BytesPerSector == 0);
-  ASSERT(Length % BytesPerSector == 0);
-
-  /* Is this a read of the FAT? */
-  if (Fcb->Flags & FCB_IS_FAT)
-  {
-    ReadOffset.QuadPart += DeviceExt->FatInfo.FATStart * BytesPerSector;
-    Status = VfatReadDiskPartial(IrpContext, &ReadOffset, Length, 0, TRUE);
-
-    if (NT_SUCCESS(Status))
-    {
-      *LengthRead = Length;
+    ULONG CurrentCluster;
+    ULONG FirstCluster;
+    ULONG StartCluster;
+    ULONG ClusterCount;
+    LARGE_INTEGER StartOffset;
+    PDEVICE_EXTENSION DeviceExt;
+    BOOLEAN First = TRUE;
+    PVFATFCB Fcb;
+    NTSTATUS Status;
+    ULONG BytesDone;
+    ULONG BytesPerSector;
+    ULONG BytesPerCluster;
+    ULONG LastCluster;
+    ULONG LastOffset;
+
+    /* PRECONDITION */
+    ASSERT(IrpContext);
+    DeviceExt = IrpContext->DeviceExt;
+    ASSERT(DeviceExt);
+    ASSERT(DeviceExt->FatInfo.BytesPerCluster);
+    ASSERT(IrpContext->FileObject);
+    ASSERT(IrpContext->FileObject->FsContext2 != NULL);
+
+    DPRINT("VfatReadFileData(DeviceExt %p, FileObject %p, "
+           "Length %u, ReadOffset 0x%I64x)\n", DeviceExt,
+           IrpContext->FileObject, Length, ReadOffset.QuadPart);
+
+    *LengthRead = 0;
+
+    Fcb = IrpContext->FileObject->FsContext;
+    BytesPerSector = DeviceExt->FatInfo.BytesPerSector;
+    BytesPerCluster = DeviceExt->FatInfo.BytesPerCluster;
+
+    ASSERT(ReadOffset.QuadPart + Length <= ROUND_UP_64(Fcb->RFCB.FileSize.QuadPart, BytesPerSector));
+    ASSERT(ReadOffset.u.LowPart % BytesPerSector == 0);
+    ASSERT(Length % BytesPerSector == 0);
+
+    /* Is this a read of the FAT? */
+    if (BooleanFlagOn(Fcb->Flags, FCB_IS_FAT))
+    {
+        ReadOffset.QuadPart += DeviceExt->FatInfo.FATStart * BytesPerSector;
+        Status = VfatReadDiskPartial(IrpContext, &ReadOffset, Length, 0, TRUE);
+
+        if (NT_SUCCESS(Status))
+        {
+            *LengthRead = Length;
+        }
+        else
+        {
+            DPRINT1("FAT reading failed, Status %x\n", Status);
+        }
+        return Status;
     }
-    else
+
+    /* Is this a read of the Volume ? */
+    if (BooleanFlagOn(Fcb->Flags, FCB_IS_VOLUME))
+    {
+        Status = VfatReadDiskPartial(IrpContext, &ReadOffset, Length, 0, TRUE);
+        if (NT_SUCCESS(Status))
+        {
+            *LengthRead = Length;
+        }
+        else
+        {
+            DPRINT1("Volume reading failed, Status %x\n", Status);
+        }
+        return Status;
+    }
+
+    /* Find the first cluster */
+    FirstCluster =
+    CurrentCluster = vfatDirEntryGetFirstCluster (DeviceExt, &Fcb->entry);
+
+    if (FirstCluster == 1)
     {
-      DPRINT1("FAT reading failed, Status %x\n", Status);
+        /* Directory of FAT12/16 needs a special handling */
+        if (ReadOffset.u.LowPart + Length > DeviceExt->FatInfo.rootDirectorySectors * BytesPerSector)
+        {
+            Length = DeviceExt->FatInfo.rootDirectorySectors * BytesPerSector - ReadOffset.u.LowPart;
+        }
+        ReadOffset.u.LowPart += DeviceExt->FatInfo.rootStart * BytesPerSector;
+
+        /* Fire up the read command */
+        Status = VfatReadDiskPartial (IrpContext, &ReadOffset, Length, 0, TRUE);
+        if (NT_SUCCESS(Status))
+        {
+            *LengthRead = Length;
+        }
+        return Status;
     }
-    return Status;
-  }
-  /* Is this a read of the Volume ? */
-  if (Fcb->Flags & FCB_IS_VOLUME)
-  {
-    Status = VfatReadDiskPartial(IrpContext, &ReadOffset, Length, 0, TRUE);
-    if (NT_SUCCESS(Status))
+
+    ExAcquireFastMutex(&Fcb->LastMutex);
+    LastCluster = Fcb->LastCluster;
+    LastOffset = Fcb->LastOffset;
+    ExReleaseFastMutex(&Fcb->LastMutex);
+
+    /* Find the cluster to start the read from */
+    if (LastCluster > 0 && ReadOffset.u.LowPart >= LastOffset)
     {
-      *LengthRead = Length;
+        Status = OffsetToCluster(DeviceExt, LastCluster,
+                                 ROUND_DOWN(ReadOffset.u.LowPart, BytesPerCluster) -
+                                 LastOffset,
+                                 &CurrentCluster, FALSE);
+#ifdef DEBUG_VERIFY_OFFSET_CACHING
+        /* DEBUG VERIFICATION */
+        {
+            ULONG CorrectCluster;
+            OffsetToCluster(DeviceExt, FirstCluster,
+                            ROUND_DOWN(ReadOffset.u.LowPart, BytesPerCluster),
+                            &CorrectCluster, FALSE);
+            if (CorrectCluster != CurrentCluster)
+                KeBugCheck(FAT_FILE_SYSTEM);
+        }
+#endif
     }
     else
     {
-      DPRINT1("Volume reading failed, Status %x\n", Status);
+        Status = OffsetToCluster(DeviceExt, FirstCluster,
+                                 ROUND_DOWN(ReadOffset.u.LowPart, BytesPerCluster),
+                                 &CurrentCluster, FALSE);
+    }
+
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
     }
-    return Status;
-  }
 
-  /*
-   * Find the first cluster
-   */
-  FirstCluster = CurrentCluster =
-    vfatDirEntryGetFirstCluster (DeviceExt, &Fcb->entry);
+    ExAcquireFastMutex(&Fcb->LastMutex);
+    Fcb->LastCluster = CurrentCluster;
+    Fcb->LastOffset = ROUND_DOWN (ReadOffset.u.LowPart, BytesPerCluster);
+    ExReleaseFastMutex(&Fcb->LastMutex);
 
-  if (FirstCluster == 1)
-  {
-    // Directory of FAT12/16 needs a special handling
-    if (ReadOffset.u.LowPart + Length > DeviceExt->FatInfo.rootDirectorySectors * BytesPerSector)
+    KeInitializeEvent(&IrpContext->Event, NotificationEvent, FALSE);
+    IrpContext->RefCount = 1;
+
+    while (Length > 0 && CurrentCluster != 0xffffffff)
     {
-      Length = DeviceExt->FatInfo.rootDirectorySectors * BytesPerSector - ReadOffset.u.LowPart;
+        StartCluster = CurrentCluster;
+        StartOffset.QuadPart = ClusterToSector(DeviceExt, StartCluster) * BytesPerSector;
+        BytesDone = 0;
+        ClusterCount = 0;
+
+        do
+        {
+            ClusterCount++;
+            if (First)
+            {
+                BytesDone =  min (Length, BytesPerCluster - (ReadOffset.u.LowPart % BytesPerCluster));
+                StartOffset.QuadPart += ReadOffset.u.LowPart % BytesPerCluster;
+                First = FALSE;
+            }
+            else
+            {
+                if (Length - BytesDone > BytesPerCluster)
+                {
+                    BytesDone += BytesPerCluster;
+                }
+                else
+                {
+                    BytesDone = Length;
+                }
+            }
+            Status = NextCluster(DeviceExt, FirstCluster, &CurrentCluster, FALSE);
+        }
+        while (StartCluster + ClusterCount == CurrentCluster && NT_SUCCESS(Status) && Length > BytesDone);
+        DPRINT("start %08x, next %08x, count %u\n",
+               StartCluster, CurrentCluster, ClusterCount);
+
+        ExAcquireFastMutex(&Fcb->LastMutex);
+        Fcb->LastCluster = StartCluster + (ClusterCount - 1);
+        Fcb->LastOffset = ROUND_DOWN(ReadOffset.u.LowPart, BytesPerCluster) + (ClusterCount - 1) * BytesPerCluster;
+        ExReleaseFastMutex(&Fcb->LastMutex);
+
+        /* Fire up the read command */
+        Status = VfatReadDiskPartial (IrpContext, &StartOffset, BytesDone, *LengthRead, FALSE);
+        if (!NT_SUCCESS(Status) && Status != STATUS_PENDING)
+        {
+            break;
+        }
+        *LengthRead += BytesDone;
+        Length -= BytesDone;
+        ReadOffset.u.LowPart += BytesDone;
     }
-    ReadOffset.u.LowPart += DeviceExt->FatInfo.rootStart * BytesPerSector;
 
-    // Fire up the read command
+    if (InterlockedDecrement((PLONG)&IrpContext->RefCount) != 0)
+    {
+        KeWaitForSingleObject(&IrpContext->Event, Executive, KernelMode, FALSE, NULL);
+    }
 
-    Status = VfatReadDiskPartial (IrpContext, &ReadOffset, Length, 0, TRUE);
-    if (NT_SUCCESS(Status))
+    if (NT_SUCCESS(Status) || Status == STATUS_PENDING)
     {
-      *LengthRead = Length;
+        if (Length > 0)
+        {
+            Status = STATUS_UNSUCCESSFUL;
+        }
+        else
+        {
+            Status = IrpContext->Irp->IoStatus.Status;
+        }
     }
+
     return Status;
-  }
-
-  ExAcquireFastMutex(&Fcb->LastMutex);
-  LastCluster = Fcb->LastCluster;
-  LastOffset = Fcb->LastOffset;
-  ExReleaseFastMutex(&Fcb->LastMutex);
-
-  /*
-   * Find the cluster to start the read from
-   */
-  if (LastCluster > 0 && ReadOffset.u.LowPart >= LastOffset)
-  {
-    Status = OffsetToCluster(DeviceExt, LastCluster,
-                             ROUND_DOWN(ReadOffset.u.LowPart, BytesPerCluster) -
-                             LastOffset,
-                             &CurrentCluster, FALSE);
-#ifdef DEBUG_VERIFY_OFFSET_CACHING
-    /* DEBUG VERIFICATION */
+}
+
+static
+NTSTATUS
+VfatWriteFileData(
+    PVFAT_IRP_CONTEXT IrpContext,
+    ULONG Length,
+    LARGE_INTEGER WriteOffset)
+{
+    PDEVICE_EXTENSION DeviceExt;
+    PVFATFCB Fcb;
+    ULONG Count;
+    ULONG FirstCluster;
+    ULONG CurrentCluster;
+    ULONG BytesDone;
+    ULONG StartCluster;
+    ULONG ClusterCount;
+    NTSTATUS Status = STATUS_SUCCESS;
+    BOOLEAN First = TRUE;
+    ULONG BytesPerSector;
+    ULONG BytesPerCluster;
+    LARGE_INTEGER StartOffset;
+    ULONG BufferOffset;
+    ULONG LastCluster;
+    ULONG LastOffset;
+
+    /* PRECONDITION */
+    ASSERT(IrpContext);
+    DeviceExt = IrpContext->DeviceExt;
+    ASSERT(DeviceExt);
+    ASSERT(DeviceExt->FatInfo.BytesPerCluster);
+    ASSERT(IrpContext->FileObject);
+    ASSERT(IrpContext->FileObject->FsContext2 != NULL);
+
+    Fcb = IrpContext->FileObject->FsContext;
+    BytesPerCluster = DeviceExt->FatInfo.BytesPerCluster;
+    BytesPerSector = DeviceExt->FatInfo.BytesPerSector;
+
+    DPRINT("VfatWriteFileData(DeviceExt %p, FileObject %p, "
+           "Length %u, WriteOffset 0x%I64x), '%wZ'\n", DeviceExt,
+           IrpContext->FileObject, Length, WriteOffset.QuadPart,
+           &Fcb->PathNameU);
+
+    ASSERT(WriteOffset.QuadPart + Length <= Fcb->RFCB.AllocationSize.QuadPart);
+    ASSERT(WriteOffset.u.LowPart % BytesPerSector == 0);
+    ASSERT(Length % BytesPerSector == 0);
+
+    /* Is this a write of the volume? */
+    if (BooleanFlagOn(Fcb->Flags, FCB_IS_VOLUME))
     {
-      ULONG CorrectCluster;
-      OffsetToCluster(DeviceExt, FirstCluster,
-                      ROUND_DOWN(ReadOffset.u.LowPart, BytesPerCluster),
-                      &CorrectCluster, FALSE);
-      if (CorrectCluster != CurrentCluster)
-        KeBugCheck(FAT_FILE_SYSTEM);
+        Status = VfatWriteDiskPartial(IrpContext, &WriteOffset, Length, 0, TRUE);
+        if (!NT_SUCCESS(Status))
+        {
+            DPRINT1("Volume writing failed, Status %x\n", Status);
+        }
+        return Status;
     }
+
+    /* Is this a write to the FAT? */
+    if (BooleanFlagOn(Fcb->Flags, FCB_IS_FAT))
+    {
+        WriteOffset.u.LowPart += DeviceExt->FatInfo.FATStart * BytesPerSector;
+        IrpContext->RefCount = 1;
+        for (Count = 0; Count < DeviceExt->FatInfo.FATCount; Count++)
+        {
+            Status = VfatWriteDiskPartial(IrpContext, &WriteOffset, Length, 0, FALSE);
+            if (!NT_SUCCESS(Status) && Status != STATUS_PENDING)
+            {
+                DPRINT1("FAT writing failed, Status %x\n", Status);
+                break;
+            }
+            WriteOffset.u.LowPart += Fcb->RFCB.FileSize.u.LowPart;
+        }
+
+        if (InterlockedDecrement((PLONG)&IrpContext->RefCount) != 0)
+        {
+            KeWaitForSingleObject(&IrpContext->Event, Executive, KernelMode, FALSE, NULL);
+        }
+
+        if (NT_SUCCESS(Status) || Status == STATUS_PENDING)
+        {
+            Status = IrpContext->Irp->IoStatus.Status;
+        }
+        return Status;
+    }
+
+    /*
+     * Find the first cluster
+     */
+    FirstCluster =
+    CurrentCluster = vfatDirEntryGetFirstCluster (DeviceExt, &Fcb->entry);
+
+    if (FirstCluster == 1)
+    {
+        ASSERT(WriteOffset.u.LowPart + Length <= DeviceExt->FatInfo.rootDirectorySectors * BytesPerSector);
+        // Directory of FAT12/16 needs a special handling
+        WriteOffset.u.LowPart += DeviceExt->FatInfo.rootStart * BytesPerSector;
+        // Fire up the write command
+        Status = VfatWriteDiskPartial (IrpContext, &WriteOffset, Length, 0, TRUE);
+        return Status;
+    }
+
+    ExAcquireFastMutex(&Fcb->LastMutex);
+    LastCluster = Fcb->LastCluster;
+    LastOffset = Fcb->LastOffset;
+    ExReleaseFastMutex(&Fcb->LastMutex);
+
+    /*
+     * Find the cluster to start the write from
+     */
+    if (LastCluster > 0 && WriteOffset.u.LowPart >= LastOffset)
+    {
+        Status = OffsetToCluster(DeviceExt, LastCluster,
+                                 ROUND_DOWN(WriteOffset.u.LowPart, BytesPerCluster) -
+                                 LastOffset,
+                                 &CurrentCluster, FALSE);
+#ifdef DEBUG_VERIFY_OFFSET_CACHING
+        /* DEBUG VERIFICATION */
+        {
+            ULONG CorrectCluster;
+            OffsetToCluster(DeviceExt, FirstCluster,
+                            ROUND_DOWN(WriteOffset.u.LowPart, BytesPerCluster),
+                            &CorrectCluster, FALSE);
+            if (CorrectCluster != CurrentCluster)
+                KeBugCheck(FAT_FILE_SYSTEM);
+        }
 #endif
-  }
-  else
-  {
-    Status = OffsetToCluster(DeviceExt, FirstCluster,
-                             ROUND_DOWN(ReadOffset.u.LowPart, BytesPerCluster),
-                             &CurrentCluster, FALSE);
-  }
-  if (!NT_SUCCESS(Status))
-  {
-    return(Status);
-  }
-
-  ExAcquireFastMutex(&Fcb->LastMutex);
-  Fcb->LastCluster = CurrentCluster;
-  Fcb->LastOffset = ROUND_DOWN (ReadOffset.u.LowPart, BytesPerCluster);
-  ExReleaseFastMutex(&Fcb->LastMutex);
-
-  KeInitializeEvent(&IrpContext->Event, NotificationEvent, FALSE);
-  IrpContext->RefCount = 1;
-
-  while (Length > 0 && CurrentCluster != 0xffffffff)
-  {
-    StartCluster = CurrentCluster;
-    StartOffset.QuadPart = ClusterToSector(DeviceExt, StartCluster) * BytesPerSector;
-    BytesDone = 0;
-    ClusterCount = 0;
-
-    do
-    {
-      ClusterCount++;
-      if (First)
-      {
-        BytesDone =  min (Length, BytesPerCluster - (ReadOffset.u.LowPart % BytesPerCluster));
-       StartOffset.QuadPart += ReadOffset.u.LowPart % BytesPerCluster;
-       First = FALSE;
-      }
-      else
-      {
-       if (Length - BytesDone > BytesPerCluster)
-       {
-         BytesDone += BytesPerCluster;
-       }
-       else
-       {
-         BytesDone = Length;
-       }
-      }
-      Status = NextCluster(DeviceExt, FirstCluster, &CurrentCluster, FALSE);
-    }
-    while (StartCluster + ClusterCount == CurrentCluster && NT_SUCCESS(Status) && Length > BytesDone);
-    DPRINT("start %08x, next %08x, count %d\n",
-           StartCluster, CurrentCluster, ClusterCount);
+    }
+    else
+    {
+        Status = OffsetToCluster(DeviceExt, FirstCluster,
+                                 ROUND_DOWN(WriteOffset.u.LowPart, BytesPerCluster),
+                                 &CurrentCluster, FALSE);
+    }
+
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
 
     ExAcquireFastMutex(&Fcb->LastMutex);
-    Fcb->LastCluster = StartCluster + (ClusterCount - 1);
-    Fcb->LastOffset = ROUND_DOWN(ReadOffset.u.LowPart, BytesPerCluster) + (ClusterCount - 1) * BytesPerCluster;
+    Fcb->LastCluster = CurrentCluster;
+    Fcb->LastOffset = ROUND_DOWN (WriteOffset.u.LowPart, BytesPerCluster);
     ExReleaseFastMutex(&Fcb->LastMutex);
 
-    // Fire up the read command
-    Status = VfatReadDiskPartial (IrpContext, &StartOffset, BytesDone, *LengthRead, FALSE);
-    if (!NT_SUCCESS(Status) && Status != STATUS_PENDING)
-      {
-        break;
-      }
-    *LengthRead += BytesDone;
-    Length -= BytesDone;
-    ReadOffset.u.LowPart += BytesDone;
-  }
-  if (0 != InterlockedDecrement((PLONG)&IrpContext->RefCount))
+    IrpContext->RefCount = 1;
+    BufferOffset = 0;
+
+    while (Length > 0 && CurrentCluster != 0xffffffff)
     {
-      KeWaitForSingleObject(&IrpContext->Event, Executive, KernelMode, FALSE, NULL);
+        StartCluster = CurrentCluster;
+        StartOffset.QuadPart = ClusterToSector(DeviceExt, StartCluster) * BytesPerSector;
+        BytesDone = 0;
+        ClusterCount = 0;
+
+        do
+        {
+            ClusterCount++;
+            if (First)
+            {
+                BytesDone =  min (Length, BytesPerCluster - (WriteOffset.u.LowPart % BytesPerCluster));
+                StartOffset.QuadPart += WriteOffset.u.LowPart % BytesPerCluster;
+                First = FALSE;
+            }
+            else
+            {
+                if (Length - BytesDone > BytesPerCluster)
+                {
+                    BytesDone += BytesPerCluster;
+                }
+                else
+                {
+                    BytesDone = Length;
+                }
+            }
+            Status = NextCluster(DeviceExt, FirstCluster, &CurrentCluster, FALSE);
+        }
+        while (StartCluster + ClusterCount == CurrentCluster && NT_SUCCESS(Status) && Length > BytesDone);
+        DPRINT("start %08x, next %08x, count %u\n",
+               StartCluster, CurrentCluster, ClusterCount);
+
+        ExAcquireFastMutex(&Fcb->LastMutex);
+        Fcb->LastCluster = StartCluster + (ClusterCount - 1);
+        Fcb->LastOffset = ROUND_DOWN(WriteOffset.u.LowPart, BytesPerCluster) + (ClusterCount - 1) * BytesPerCluster;
+        ExReleaseFastMutex(&Fcb->LastMutex);
+
+        // Fire up the write command
+        Status = VfatWriteDiskPartial (IrpContext, &StartOffset, BytesDone, BufferOffset, FALSE);
+        if (!NT_SUCCESS(Status) && Status != STATUS_PENDING)
+        {
+            break;
+        }
+        BufferOffset += BytesDone;
+        Length -= BytesDone;
+        WriteOffset.u.LowPart += BytesDone;
     }
-  if (NT_SUCCESS(Status) || Status == STATUS_PENDING)
+
+    if (InterlockedDecrement((PLONG)&IrpContext->RefCount) != 0)
     {
-      if (Length > 0)
+        KeWaitForSingleObject(&IrpContext->Event, Executive, KernelMode, FALSE, NULL);
+    }
+
+    if (NT_SUCCESS(Status) || Status == STATUS_PENDING)
+    {
+        if (Length > 0)
         {
-         Status = STATUS_UNSUCCESSFUL;
-       }
-      else
+            Status = STATUS_UNSUCCESSFUL;
+        }
+        else
         {
-          Status = IrpContext->Irp->IoStatus.Status;
-       }
+            Status = IrpContext->Irp->IoStatus.Status;
+        }
     }
-  return Status;
+    return Status;
 }
 
-static NTSTATUS
-VfatWriteFileData(PVFAT_IRP_CONTEXT IrpContext,
-                 ULONG Length,
-                 LARGE_INTEGER WriteOffset)
+NTSTATUS
+VfatCommonRead(
+    PVFAT_IRP_CONTEXT IrpContext)
 {
-   PDEVICE_EXTENSION DeviceExt;
-   PVFATFCB Fcb;
-   ULONG Count;
-   ULONG FirstCluster;
-   ULONG CurrentCluster;
-   ULONG BytesDone;
-   ULONG StartCluster;
-   ULONG ClusterCount;
-   NTSTATUS Status = STATUS_SUCCESS;
-   BOOLEAN First = TRUE;
-   ULONG BytesPerSector;
-   ULONG BytesPerCluster;
-   LARGE_INTEGER StartOffset;
-   ULONG BufferOffset;
-   ULONG LastCluster;
-   ULONG LastOffset;
-
-   /* PRECONDITION */
-   ASSERT(IrpContext);
-   DeviceExt = IrpContext->DeviceExt;
-   ASSERT(DeviceExt);
-   ASSERT(DeviceExt->FatInfo.BytesPerCluster);
-   ASSERT(IrpContext->FileObject);
-   ASSERT(IrpContext->FileObject->FsContext2 != NULL);
-
-   Fcb = IrpContext->FileObject->FsContext;
-   BytesPerCluster = DeviceExt->FatInfo.BytesPerCluster;
-   BytesPerSector = DeviceExt->FatInfo.BytesPerSector;
-
-   DPRINT("VfatWriteFileData(DeviceExt %p, FileObject %p, "
-         "Length %d, WriteOffset 0x%I64x), '%wZ'\n", DeviceExt,
-         IrpContext->FileObject, Length, WriteOffset.QuadPart,
-         &Fcb->PathNameU);
-
-   ASSERT(WriteOffset.QuadPart + Length <= Fcb->RFCB.AllocationSize.QuadPart);
-   ASSERT(WriteOffset.u.LowPart % BytesPerSector == 0);
-   ASSERT(Length % BytesPerSector == 0);
-
-   // Is this a write of the volume ?
-   if (Fcb->Flags & FCB_IS_VOLUME)
-   {
-      Status = VfatWriteDiskPartial(IrpContext, &WriteOffset, Length, 0, TRUE);
-      if (!NT_SUCCESS(Status))
-      {
-         DPRINT1("Volume writing failed, Status %x\n", Status);
-      }
-      return Status;
-   }
+    PVFATFCB Fcb;
+    PVOID Buffer;
+    NTSTATUS Status;
+    ULONG Length = 0;
+    ULONG BytesPerSector;
+    LARGE_INTEGER ByteOffset;
+    ULONG ReturnedLength = 0;
+    BOOLEAN PagingIo, CanWait, IsVolume, NoCache;
+
+    PagingIo = BooleanFlagOn(IrpContext->Irp->Flags, IRP_PAGING_IO);
+    CanWait = BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT);
+    NoCache = BooleanFlagOn(IrpContext->Irp->Flags, IRP_NOCACHE);
+    Fcb = IrpContext->FileObject->FsContext;
+    IsVolume = BooleanFlagOn(Fcb->Flags, FCB_IS_VOLUME);
+
+    ByteOffset = IrpContext->Stack->Parameters.Read.ByteOffset;
+    Length = IrpContext->Stack->Parameters.Read.Length;
+    BytesPerSector = IrpContext->DeviceExt->FatInfo.BytesPerSector;
+
+    if (!PagingIo &&
+        FsRtlAreThereCurrentFileLocks(&Fcb->FileLock))
+    {
+        if (!FsRtlCheckLockForReadAccess(&Fcb->FileLock, IrpContext->Irp))
+        {
+            return STATUS_FILE_LOCK_CONFLICT;
+        }
+    }
 
-   // Is this a write to the FAT ?
-   if (Fcb->Flags & FCB_IS_FAT)
-   {
-      WriteOffset.u.LowPart += DeviceExt->FatInfo.FATStart * BytesPerSector;
-      IrpContext->RefCount = 1;
-      for (Count = 0; Count < DeviceExt->FatInfo.FATCount; Count++)
-      {
-         Status = VfatWriteDiskPartial(IrpContext, &WriteOffset, Length, 0, FALSE);
-         if (!NT_SUCCESS(Status) && Status != STATUS_PENDING)
-         {
-            DPRINT1("FAT writing failed, Status %x\n", Status);
-           break;
-         }
-        WriteOffset.u.LowPart += Fcb->RFCB.FileSize.u.LowPart;
-      }
-      if (0 != InterlockedDecrement((PLONG)&IrpContext->RefCount))
+    Buffer = VfatGetUserBuffer(IrpContext->Irp, PagingIo);
+
+    if (!PagingIo && !NoCache && !IsVolume)
+    {
+        // cached read
+        Status = STATUS_SUCCESS;
+        if (ByteOffset.u.LowPart + Length > Fcb->RFCB.FileSize.u.LowPart)
         {
-         KeWaitForSingleObject(&IrpContext->Event, Executive, KernelMode, FALSE, NULL);
-       }
-      if (NT_SUCCESS(Status) || Status == STATUS_PENDING)
+            Length = Fcb->RFCB.FileSize.u.LowPart - ByteOffset.u.LowPart;
+            Status = /*STATUS_END_OF_FILE*/STATUS_SUCCESS;
+        }
+
+        vfatAddToStat(IrpContext->DeviceExt, Base.UserFileReads, 1);
+        vfatAddToStat(IrpContext->DeviceExt, Base.UserFileReadBytes, Length);
+
+        _SEH2_TRY
         {
-         Status = IrpContext->Irp->IoStatus.Status;
-       }
-      return Status;
-   }
+            if (IrpContext->FileObject->PrivateCacheMap == NULL)
+            {
+                CcInitializeCacheMap(IrpContext->FileObject,
+                                     (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
+                                     FALSE,
+                                     &(VfatGlobalData->CacheMgrCallbacks),
+                                     Fcb);
+            }
+
+            if (!CcCopyRead(IrpContext->FileObject,
+                            &ByteOffset,
+                            Length,
+                            CanWait,
+                            Buffer,
+                            &IrpContext->Irp->IoStatus))
+            {
+                ASSERT(!CanWait);
+                Status = STATUS_PENDING;
+                goto ByeBye;
+            }
+        }
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+        {
+            Status = _SEH2_GetExceptionCode();
+            goto ByeBye;
+        }
+        _SEH2_END;
 
-   /*
-    * Find the first cluster
-    */
-   FirstCluster = CurrentCluster =
-      vfatDirEntryGetFirstCluster (DeviceExt, &Fcb->entry);
-
-   if (FirstCluster == 1)
-   {
-      ASSERT(WriteOffset.u.LowPart + Length <= DeviceExt->FatInfo.rootDirectorySectors * BytesPerSector);
-      // Directory of FAT12/16 needs a special handling
-      WriteOffset.u.LowPart += DeviceExt->FatInfo.rootStart * BytesPerSector;
-      // Fire up the write command
-      Status = VfatWriteDiskPartial (IrpContext, &WriteOffset, Length, 0, TRUE);
-      return Status;
-   }
+        if (!NT_SUCCESS(IrpContext->Irp->IoStatus.Status))
+        {
+            Status = IrpContext->Irp->IoStatus.Status;
+        }
+    }
+    else
+    {
+        // non cached read
+        Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoWriteAccess);
+        if (!NT_SUCCESS(Status))
+        {
+            goto ByeBye;
+        }
 
-   ExAcquireFastMutex(&Fcb->LastMutex);
-   LastCluster = Fcb->LastCluster;
-   LastOffset = Fcb->LastOffset;
-   ExReleaseFastMutex(&Fcb->LastMutex);
-
-   /*
-    * Find the cluster to start the write from
-    */
-   if (LastCluster > 0 && WriteOffset.u.LowPart >= LastOffset)
-   {
-      Status = OffsetToCluster(DeviceExt, LastCluster,
-                               ROUND_DOWN(WriteOffset.u.LowPart, BytesPerCluster) -
-                               LastOffset,
-                               &CurrentCluster, FALSE);
-#ifdef DEBUG_VERIFY_OFFSET_CACHING
-      /* DEBUG VERIFICATION */
-      {
-         ULONG CorrectCluster;
-         OffsetToCluster(DeviceExt, FirstCluster,
-                         ROUND_DOWN(WriteOffset.u.LowPart, BytesPerCluster),
-                         &CorrectCluster, FALSE);
-         if (CorrectCluster != CurrentCluster)
-            KeBugCheck(FAT_FILE_SYSTEM);
-      }
-#endif
-   }
-   else
-   {
-      Status = OffsetToCluster(DeviceExt, FirstCluster,
-                               ROUND_DOWN(WriteOffset.u.LowPart, BytesPerCluster),
-                               &CurrentCluster, FALSE);
-   }
+        if (ByteOffset.QuadPart + Length > ROUND_UP_64(Fcb->RFCB.FileSize.QuadPart, BytesPerSector))
+        {
+            Length = (ULONG)(ROUND_UP_64(Fcb->RFCB.FileSize.QuadPart, BytesPerSector) - ByteOffset.QuadPart);
+        }
 
-   if (!NT_SUCCESS(Status))
-   {
-      return(Status);
-   }
+        if (!IsVolume)
+        {
+            vfatAddToStat(IrpContext->DeviceExt, Fat.NonCachedReads, 1);
+            vfatAddToStat(IrpContext->DeviceExt, Fat.NonCachedReadBytes, Length);
+        }
+        else
+        {
+            vfatAddToStat(IrpContext->DeviceExt, Base.MetaDataReads, 1);
+            vfatAddToStat(IrpContext->DeviceExt, Base.MetaDataReadBytes, Length);
+        }
 
-   ExAcquireFastMutex(&Fcb->LastMutex);
-   Fcb->LastCluster = CurrentCluster;
-   Fcb->LastOffset = ROUND_DOWN (WriteOffset.u.LowPart, BytesPerCluster);
-   ExReleaseFastMutex(&Fcb->LastMutex);
-
-   IrpContext->RefCount = 1;
-   BufferOffset = 0;
-
-   while (Length > 0 && CurrentCluster != 0xffffffff)
-   {
-      StartCluster = CurrentCluster;
-      StartOffset.QuadPart = ClusterToSector(DeviceExt, StartCluster) * BytesPerSector;
-      BytesDone = 0;
-      ClusterCount = 0;
-
-      do
-      {
-         ClusterCount++;
-         if (First)
-         {
-            BytesDone =  min (Length, BytesPerCluster - (WriteOffset.u.LowPart % BytesPerCluster));
-           StartOffset.QuadPart += WriteOffset.u.LowPart % BytesPerCluster;
-           First = FALSE;
-         }
-         else
-         {
-           if (Length - BytesDone > BytesPerCluster)
-           {
-              BytesDone += BytesPerCluster;
-           }
-           else
-           {
-              BytesDone = Length;
-           }
-         }
-         Status = NextCluster(DeviceExt, FirstCluster, &CurrentCluster, FALSE);
-      }
-      while (StartCluster + ClusterCount == CurrentCluster && NT_SUCCESS(Status) && Length > BytesDone);
-      DPRINT("start %08x, next %08x, count %d\n",
-             StartCluster, CurrentCluster, ClusterCount);
-
-      ExAcquireFastMutex(&Fcb->LastMutex);
-      Fcb->LastCluster = StartCluster + (ClusterCount - 1);
-      Fcb->LastOffset = ROUND_DOWN(WriteOffset.u.LowPart, BytesPerCluster) + (ClusterCount - 1) * BytesPerCluster;
-      ExReleaseFastMutex(&Fcb->LastMutex);
-
-      // Fire up the write command
-      Status = VfatWriteDiskPartial (IrpContext, &StartOffset, BytesDone, BufferOffset, FALSE);
-      if (!NT_SUCCESS(Status) && Status != STATUS_PENDING)
+        Status = VfatReadFileData(IrpContext, Length, ByteOffset, &ReturnedLength);
+        if (NT_SUCCESS(Status))
         {
-         break;
-       }
-      BufferOffset += BytesDone;
-      Length -= BytesDone;
-      WriteOffset.u.LowPart += BytesDone;
-   }
-   if (0 != InterlockedDecrement((PLONG)&IrpContext->RefCount))
-     {
-       KeWaitForSingleObject(&IrpContext->Event, Executive, KernelMode, FALSE, NULL);
-     }
-   if (NT_SUCCESS(Status) || Status == STATUS_PENDING)
-     {
-       if (Length > 0)
-         {
-          Status = STATUS_UNSUCCESSFUL;
-        }
-       else
-         {
-          Status = IrpContext->Irp->IoStatus.Status;
-        }
-     }
-   return Status;
+            IrpContext->Irp->IoStatus.Information = ReturnedLength;
+        }
+    }
+
+ByeBye:
+    return Status;
+}
+
+VOID
+NTAPI
+VfatStackOverflowRead(
+    PVOID Context,
+    IN PKEVENT Event)
+{
+    PVFAT_IRP_CONTEXT IrpContext;
+
+    IrpContext = Context;
+    /* In a separate thread, we can wait and resources got locked */
+    SetFlag(IrpContext->Flags, IRPCONTEXT_CANWAIT);
+
+    /* Perform the read operation */
+    DPRINT1("Performing posted read\n");
+    VfatCommonRead(IrpContext);
+
+    KeSetEvent(Event, 0, FALSE);
+}
+
+VOID
+VfatPostRead(
+    PVFAT_IRP_CONTEXT IrpContext,
+    PERESOURCE Lock,
+    BOOLEAN PagingIo)
+{
+    KEVENT Event;
+
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+    ExAcquireResourceSharedLite(Lock, TRUE);
+
+    /* If paging IO, call the non failing but blocking routine */
+    if (PagingIo)
+    {
+        FsRtlPostPagingFileStackOverflow(IrpContext, &Event, VfatStackOverflowRead);
+    }
+    else
+    {
+        FsRtlPostStackOverflow(IrpContext, &Event, VfatStackOverflowRead);
+    }
+
+    /* Wait till it's done */
+    KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
 }
 
 NTSTATUS
-VfatRead(PVFAT_IRP_CONTEXT IrpContext)
+VfatRead(
+    PVFAT_IRP_CONTEXT IrpContext)
 {
-   NTSTATUS Status;
-   PVFATFCB Fcb;
-   ULONG Length = 0;
-   ULONG ReturnedLength = 0;
-   PERESOURCE Resource = NULL;
-   LARGE_INTEGER ByteOffset;
-   PVOID Buffer;
-   ULONG BytesPerSector;
-
-   ASSERT(IrpContext);
-
-   DPRINT("VfatRead(IrpContext %p)\n", IrpContext);
-
-   ASSERT(IrpContext->DeviceObject);
-
-   // This request is not allowed on the main device object
-   if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
-   {
-      DPRINT("VfatRead is called with the main device object.\n");
-      Status = STATUS_INVALID_DEVICE_REQUEST;
-      goto ByeBye;
-   }
+    NTSTATUS Status;
+    PVFATFCB Fcb;
+    ULONG Length = 0;
+    PERESOURCE Resource = NULL;
+    LARGE_INTEGER ByteOffset;
+    ULONG BytesPerSector;
+    BOOLEAN PagingIo, CanWait, IsVolume, NoCache;
 
-   ASSERT(IrpContext->DeviceExt);
-   ASSERT(IrpContext->FileObject);
-   Fcb = IrpContext->FileObject->FsContext;
-   ASSERT(Fcb);
-
-   if (Fcb->Flags & FCB_IS_PAGE_FILE)
-   {
-      PFATINFO FatInfo = &IrpContext->DeviceExt->FatInfo;
-      IrpContext->Stack->Parameters.Read.ByteOffset.QuadPart += FatInfo->dataStart * FatInfo->BytesPerSector;
-      IoSkipCurrentIrpStackLocation(IrpContext->Irp);
-      DPRINT("Read from page file, disk offset %I64x\n", IrpContext->Stack->Parameters.Read.ByteOffset.QuadPart);
-      Status = IoCallDriver(IrpContext->DeviceExt->StorageDevice, IrpContext->Irp);
-      VfatFreeIrpContext(IrpContext);
-      return Status;
-   }
+    ASSERT(IrpContext);
 
-   DPRINT("<%wZ>\n", &Fcb->PathNameU);
+    DPRINT("VfatRead(IrpContext %p)\n", IrpContext);
 
-   ByteOffset = IrpContext->Stack->Parameters.Read.ByteOffset;
-   Length = IrpContext->Stack->Parameters.Read.Length;
-   BytesPerSector = IrpContext->DeviceExt->FatInfo.BytesPerSector;
+    ASSERT(IrpContext->DeviceObject);
 
-   /* fail if file is a directory and no paged read */
-   if (*Fcb->Attributes & FILE_ATTRIBUTE_DIRECTORY && !(IrpContext->Irp->Flags & IRP_PAGING_IO))
-   {
-      Status = STATUS_INVALID_PARAMETER;
-      goto ByeBye;
-   }
+    PagingIo = BooleanFlagOn(IrpContext->Irp->Flags, IRP_PAGING_IO);
+    CanWait = BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT);
+    NoCache = BooleanFlagOn(IrpContext->Irp->Flags, IRP_NOCACHE);
 
+    // This request is not allowed on the main device object
+    if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
+    {
+        DPRINT("VfatRead is called with the main device object.\n");
+        Status = STATUS_INVALID_DEVICE_REQUEST;
+        goto ByeBye;
+    }
 
-   DPRINT("'%wZ', Offset: %d, Length %d\n", &Fcb->PathNameU, ByteOffset.u.LowPart, Length);
+    ASSERT(IrpContext->DeviceExt);
+    ASSERT(IrpContext->FileObject);
+    Fcb = IrpContext->FileObject->FsContext;
+    ASSERT(Fcb);
 
-   if (ByteOffset.u.HighPart && !(Fcb->Flags & FCB_IS_VOLUME))
-   {
-      Status = STATUS_INVALID_PARAMETER;
-      goto ByeBye;
-   }
-   if (ByteOffset.QuadPart >= Fcb->RFCB.FileSize.QuadPart)
-   {
-      IrpContext->Irp->IoStatus.Information = 0;
-      Status = STATUS_END_OF_FILE;
-      goto ByeBye;
-   }
-   if (IrpContext->Irp->Flags & (IRP_PAGING_IO | IRP_NOCACHE) || (Fcb->Flags & FCB_IS_VOLUME))
-   {
-      if (ByteOffset.u.LowPart % BytesPerSector != 0 || Length % BytesPerSector != 0)
-      {
-         DPRINT("%d %d\n", ByteOffset.u.LowPart, Length);
-         // non cached read must be sector aligned
-         Status = STATUS_INVALID_PARAMETER;
-         goto ByeBye;
-      }
-   }
-   if (Length == 0)
-   {
-      IrpContext->Irp->IoStatus.Information = 0;
-      Status = STATUS_SUCCESS;
-      goto ByeBye;
-   }
+    IsVolume = BooleanFlagOn(Fcb->Flags, FCB_IS_VOLUME);
 
-   if (Fcb->Flags & FCB_IS_VOLUME)
-   {
-      Resource = &IrpContext->DeviceExt->DirResource;
-   }
-   else if (IrpContext->Irp->Flags & IRP_PAGING_IO)
-   {
-      Resource = &Fcb->PagingIoResource;
-   }
-   else
-   {
-      Resource = &Fcb->MainResource;
-   }
-   if (!ExAcquireResourceSharedLite(Resource,
-                                    IrpContext->Flags & IRPCONTEXT_CANWAIT ? TRUE : FALSE))
-   {
-      Resource = NULL;
-      Status = STATUS_PENDING;
-      goto ByeBye;
-   }
+    if (BooleanFlagOn(Fcb->Flags, FCB_IS_PAGE_FILE))
+    {
+        PFATINFO FatInfo = &IrpContext->DeviceExt->FatInfo;
+        IrpContext->Stack->Parameters.Read.ByteOffset.QuadPart += FatInfo->dataStart * FatInfo->BytesPerSector;
+        IoSkipCurrentIrpStackLocation(IrpContext->Irp);
+        IrpContext->Flags &= ~IRPCONTEXT_COMPLETE;
+        DPRINT("Read from page file, disk offset %I64x\n", IrpContext->Stack->Parameters.Read.ByteOffset.QuadPart);
+        Status = IoCallDriver(IrpContext->DeviceExt->StorageDevice, IrpContext->Irp);
+        return Status;
+    }
 
-   if (!(IrpContext->Irp->Flags & IRP_PAGING_IO) &&
-      FsRtlAreThereCurrentFileLocks(&Fcb->FileLock))
-   {
-      if (!FsRtlCheckLockForReadAccess(&Fcb->FileLock, IrpContext->Irp))
-      {
-         Status = STATUS_FILE_LOCK_CONFLICT;
-         goto ByeBye;
-      }
-   }
+    DPRINT("<%wZ>\n", &Fcb->PathNameU);
+
+    ByteOffset = IrpContext->Stack->Parameters.Read.ByteOffset;
+    Length = IrpContext->Stack->Parameters.Read.Length;
+    BytesPerSector = IrpContext->DeviceExt->FatInfo.BytesPerSector;
+
+    /* fail if file is a directory and no paged read */
+    if (vfatFCBIsDirectory(Fcb) && !PagingIo)
+    {
+        Status = STATUS_INVALID_PARAMETER;
+        goto ByeBye;
+    }
 
-   Buffer = VfatGetUserBuffer(IrpContext->Irp);
-   if (!Buffer)
-     {
-       Status = STATUS_INVALID_USER_BUFFER;
+    DPRINT("'%wZ', Offset: %u, Length %u\n", &Fcb->PathNameU, ByteOffset.u.LowPart, Length);
+
+    if (ByteOffset.u.HighPart && !IsVolume)
+    {
+       Status = STATUS_INVALID_PARAMETER;
        goto ByeBye;
-     }
-
-   if (!(IrpContext->Irp->Flags & (IRP_NOCACHE|IRP_PAGING_IO)) &&
-     !(Fcb->Flags & (FCB_IS_PAGE_FILE|FCB_IS_VOLUME)))
-   {
-      // cached read
-      Status = STATUS_SUCCESS;
-      if (ByteOffset.u.LowPart + Length > Fcb->RFCB.FileSize.u.LowPart)
-      {
-         Length = Fcb->RFCB.FileSize.u.LowPart - ByteOffset.u.LowPart;
-         Status = /*STATUS_END_OF_FILE*/STATUS_SUCCESS;
-      }
-
-      if (IrpContext->FileObject->PrivateCacheMap == NULL)
-      {
-        CcInitializeCacheMap(IrpContext->FileObject,
-                             (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
-                             FALSE,
-                             &(VfatGlobalData->CacheMgrCallbacks),
-                             Fcb);
-      }
-      if (!CcCopyRead(IrpContext->FileObject, &ByteOffset, Length,
-                      (BOOLEAN)(IrpContext->Flags & IRPCONTEXT_CANWAIT), Buffer,
-                      &IrpContext->Irp->IoStatus))
-      {
-         Status = STATUS_PENDING;
-         goto ByeBye;
-      }
-      if (!NT_SUCCESS(IrpContext->Irp->IoStatus.Status))
-      {
-        Status = IrpContext->Irp->IoStatus.Status;
-      }
-   }
-   else
-   {
-      // non cached read
-      if (ByteOffset.QuadPart + Length > ROUND_UP(Fcb->RFCB.FileSize.QuadPart, BytesPerSector))
-      {
-         Length = (ULONG)(ROUND_UP(Fcb->RFCB.FileSize.QuadPart, BytesPerSector) - ByteOffset.QuadPart);
-      }
-
-      Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoWriteAccess);
-      if (!NT_SUCCESS(Status))
+    }
+
+    if (Length == 0)
+    {
+        IrpContext->Irp->IoStatus.Information = 0;
+        Status = STATUS_SUCCESS;
+        goto ByeBye;
+    }
+
+    if (ByteOffset.QuadPart >= Fcb->RFCB.FileSize.QuadPart)
+    {
+       IrpContext->Irp->IoStatus.Information = 0;
+       Status = STATUS_END_OF_FILE;
+       goto ByeBye;
+    }
+
+    if (NoCache || PagingIo || IsVolume)
+    {
+        if (ByteOffset.u.LowPart % BytesPerSector != 0 || Length % BytesPerSector != 0)
         {
-          goto ByeBye;
+            DPRINT("%u %u\n", ByteOffset.u.LowPart, Length);
+            // non cached read must be sector aligned
+            Status = STATUS_INVALID_PARAMETER;
+            goto ByeBye;
         }
+    }
 
-      Status = VfatReadFileData(IrpContext, Length, ByteOffset, &ReturnedLength);
-      if (NT_SUCCESS(Status))
-      {
-         IrpContext->Irp->IoStatus.Information = ReturnedLength;
-      }
-   }
+    if (IsVolume)
+    {
+        Resource = &IrpContext->DeviceExt->DirResource;
+    }
+    else if (PagingIo)
+    {
+        Resource = &Fcb->PagingIoResource;
+    }
+    else
+    {
+        Resource = &Fcb->MainResource;
+    }
+
+    /* Are we out of stack for the rest of the operation? */
+    if (IoGetRemainingStackSize() < OVERFLOW_READ_THRESHHOLD)
+    {
+        /* Lock the buffer */
+        Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoWriteAccess);
+        if (!NT_SUCCESS(Status))
+        {
+            return Status;
+        }
+
+        /* And post the read to the overflow thread */
+        VfatPostRead(IrpContext, Resource, PagingIo);
+
+        /* Return the appropriate status */
+        return IrpContext->Irp->IoStatus.Status;
+    }
+
+    if (!ExAcquireResourceSharedLite(Resource, CanWait))
+    {
+        Resource = NULL;
+        Status = STATUS_PENDING;
+        goto ByeBye;
+    }
+
+    Status = VfatCommonRead(IrpContext);
 
 ByeBye:
-   if (Resource)
-   {
-      ExReleaseResourceLite(Resource);
-   }
+    if (Resource)
+    {
+        ExReleaseResourceLite(Resource);
+    }
 
-   if (Status == STATUS_PENDING)
-   {
-      Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoWriteAccess);
-      if (NT_SUCCESS(Status))
-      {
-         Status = VfatQueueRequest(IrpContext);
-      }
-      else
-      {
-         IrpContext->Irp->IoStatus.Status = Status;
-         IoCompleteRequest(IrpContext->Irp, IO_NO_INCREMENT);
-         VfatFreeIrpContext(IrpContext);
-      }
-   }
-   else
-   {
-      IrpContext->Irp->IoStatus.Status = Status;
-      if (IrpContext->FileObject->Flags & FO_SYNCHRONOUS_IO &&
-          !(IrpContext->Irp->Flags & IRP_PAGING_IO) &&
-          (NT_SUCCESS(Status) || Status==STATUS_END_OF_FILE))
-      {
-         IrpContext->FileObject->CurrentByteOffset.QuadPart =
-           ByteOffset.QuadPart + IrpContext->Irp->IoStatus.Information;
-      }
-
-      IoCompleteRequest(IrpContext->Irp,
-                        (CCHAR)(NT_SUCCESS(Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT));
-      VfatFreeIrpContext(IrpContext);
-   }
-   DPRINT("%x\n", Status);
-   return Status;
+    if (Status == STATUS_PENDING)
+    {
+        Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoWriteAccess);
+        if (NT_SUCCESS(Status))
+        {
+            Status = VfatMarkIrpContextForQueue(IrpContext);
+        }
+    }
+    else
+    {
+        IrpContext->Irp->IoStatus.Status = Status;
+        if (BooleanFlagOn(IrpContext->FileObject->Flags, FO_SYNCHRONOUS_IO) &&
+            !PagingIo &&
+            (NT_SUCCESS(Status) || Status == STATUS_END_OF_FILE))
+        {
+            IrpContext->FileObject->CurrentByteOffset.QuadPart =
+                ByteOffset.QuadPart + IrpContext->Irp->IoStatus.Information;
+        }
+
+        if (NT_SUCCESS(Status))
+            IrpContext->PriorityBoost = IO_DISK_INCREMENT;
+    }
+    DPRINT("%x\n", Status);
+    return Status;
 }
 
-NTSTATUS VfatWrite (PVFAT_IRP_CONTEXT IrpContext)
+NTSTATUS
+VfatWrite(
+    PVFAT_IRP_CONTEXT IrpContext)
 {
-   PVFATFCB Fcb;
-   PERESOURCE Resource = NULL;
-   LARGE_INTEGER ByteOffset;
-   LARGE_INTEGER OldFileSize;
-   NTSTATUS Status = STATUS_SUCCESS;
-   ULONG Length = 0;
-   PVOID Buffer;
-   ULONG BytesPerSector;
-
-   ASSERT(IrpContext);
-
-   DPRINT("VfatWrite(IrpContext %p)\n", IrpContext);
-
-   ASSERT(IrpContext->DeviceObject);
-
-   // This request is not allowed on the main device object
-   if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
-   {
-      DPRINT("VfatWrite is called with the main device object.\n");
-      Status = STATUS_INVALID_DEVICE_REQUEST;
-      goto ByeBye;
-   }
+    PVFATFCB Fcb;
+    PERESOURCE Resource = NULL;
+    LARGE_INTEGER ByteOffset;
+    LARGE_INTEGER OldFileSize;
+    NTSTATUS Status = STATUS_SUCCESS;
+    ULONG Length = 0;
+    PVOID Buffer;
+    ULONG BytesPerSector;
+    BOOLEAN PagingIo, CanWait, IsVolume, IsFAT, NoCache;
 
-   ASSERT(IrpContext->DeviceExt);
-   ASSERT(IrpContext->FileObject);
-   Fcb = IrpContext->FileObject->FsContext;
-   ASSERT(Fcb);
-
-   if (Fcb->Flags & FCB_IS_PAGE_FILE)
-   {
-       PFATINFO FatInfo = &IrpContext->DeviceExt->FatInfo;
-       IrpContext->Stack->Parameters.Write.ByteOffset.QuadPart += FatInfo->dataStart * FatInfo->BytesPerSector;
-       IoSkipCurrentIrpStackLocation(IrpContext->Irp);
-       DPRINT("Write to page file, disk offset %I64x\n", IrpContext->Stack->Parameters.Write.ByteOffset.QuadPart);
-       Status = IoCallDriver(IrpContext->DeviceExt->StorageDevice, IrpContext->Irp);
-       VfatFreeIrpContext(IrpContext);
-       return Status;
-   }
+    ASSERT(IrpContext);
 
-   DPRINT("<%wZ>\n", &Fcb->PathNameU);
+    DPRINT("VfatWrite(IrpContext %p)\n", IrpContext);
 
-  /* fail if file is a directory and no paged read */
-   if (*Fcb->Attributes & FILE_ATTRIBUTE_DIRECTORY && !(IrpContext->Irp->Flags & IRP_PAGING_IO))
-   {
-      Status = STATUS_INVALID_PARAMETER;
-      goto ByeBye;
-   }
+    ASSERT(IrpContext->DeviceObject);
 
-   ByteOffset = IrpContext->Stack->Parameters.Write.ByteOffset;
-   if (ByteOffset.u.LowPart == FILE_WRITE_TO_END_OF_FILE &&
-       ByteOffset.u.HighPart == -1)
-   {
-      ByteOffset.QuadPart = Fcb->RFCB.FileSize.QuadPart;
-   }
-   Length = IrpContext->Stack->Parameters.Write.Length;
-   BytesPerSector = IrpContext->DeviceExt->FatInfo.BytesPerSector;
+    PagingIo = BooleanFlagOn(IrpContext->Irp->Flags, IRP_PAGING_IO);
+    CanWait = BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT);
+    NoCache = BooleanFlagOn(IrpContext->Irp->Flags, IRP_NOCACHE);
 
-   if (ByteOffset.u.HighPart && !(Fcb->Flags & FCB_IS_VOLUME))
-   {
-      Status = STATUS_INVALID_PARAMETER;
-      goto ByeBye;
-   }
+    // This request is not allowed on the main device object
+    if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
+    {
+        DPRINT("VfatWrite is called with the main device object.\n");
+        Status = STATUS_INVALID_DEVICE_REQUEST;
+        goto ByeBye;
+    }
 
-   if (Fcb->Flags & (FCB_IS_FAT | FCB_IS_VOLUME) ||
-       1 == vfatDirEntryGetFirstCluster (IrpContext->DeviceExt, &Fcb->entry))
-   {
-      if (ByteOffset.QuadPart + Length > Fcb->RFCB.FileSize.QuadPart)
-      {
-         // we can't extend the FAT, the volume or the root on FAT12/FAT16
-         Status = STATUS_END_OF_FILE;
-         goto ByeBye;
-      }
-   }
+    ASSERT(IrpContext->DeviceExt);
+    ASSERT(IrpContext->FileObject);
+    Fcb = IrpContext->FileObject->FsContext;
+    ASSERT(Fcb);
 
-   if (IrpContext->Irp->Flags & (IRP_PAGING_IO|IRP_NOCACHE) || (Fcb->Flags & FCB_IS_VOLUME))
-   {
-      if (ByteOffset.u.LowPart % BytesPerSector != 0 || Length % BytesPerSector != 0)
-      {
-         // non cached write must be sector aligned
-         Status = STATUS_INVALID_PARAMETER;
-         goto ByeBye;
-      }
-   }
+    IsVolume = BooleanFlagOn(Fcb->Flags, FCB_IS_VOLUME);
+    IsFAT = BooleanFlagOn(Fcb->Flags, FCB_IS_FAT);
 
-   if (Length == 0)
-   {
-      /* FIXME:
-       *   Update last write time
-       */
-      IrpContext->Irp->IoStatus.Information = 0;
-      Status = STATUS_SUCCESS;
-      goto ByeBye;
-   }
+    if (BooleanFlagOn(Fcb->Flags, FCB_IS_PAGE_FILE))
+    {
+        PFATINFO FatInfo = &IrpContext->DeviceExt->FatInfo;
+        IrpContext->Stack->Parameters.Write.ByteOffset.QuadPart += FatInfo->dataStart * FatInfo->BytesPerSector;
+        IoSkipCurrentIrpStackLocation(IrpContext->Irp);
+        IrpContext->Flags &= ~IRPCONTEXT_COMPLETE;
+        DPRINT("Write to page file, disk offset %I64x\n", IrpContext->Stack->Parameters.Write.ByteOffset.QuadPart);
+        Status = IoCallDriver(IrpContext->DeviceExt->StorageDevice, IrpContext->Irp);
+        return Status;
+    }
 
-   if (IrpContext->Irp->Flags & IRP_PAGING_IO)
-   {
-      if (ByteOffset.u.LowPart + Length > Fcb->RFCB.AllocationSize.u.LowPart)
-      {
-         Status = STATUS_INVALID_PARAMETER;
-         goto ByeBye;
-      }
-      if (ByteOffset.u.LowPart + Length > ROUND_UP(Fcb->RFCB.AllocationSize.u.LowPart, BytesPerSector))
-      {
-         Length =  ROUND_UP(Fcb->RFCB.FileSize.u.LowPart, BytesPerSector) - ByteOffset.u.LowPart;
-      }
-   }
+    DPRINT("<%wZ>\n", &Fcb->PathNameU);
 
-   if (Fcb->Flags & FCB_IS_VOLUME)
-   {
-      Resource = &IrpContext->DeviceExt->DirResource;
-   }
-   else if (IrpContext->Irp->Flags & IRP_PAGING_IO)
-   {
-      Resource = &Fcb->PagingIoResource;
-   }
-   else
-   {
-      Resource = &Fcb->MainResource;
-   }
+    /* fail if file is a directory and no paged read */
+    if (vfatFCBIsDirectory(Fcb) && !PagingIo)
+    {
+        Status = STATUS_INVALID_PARAMETER;
+        goto ByeBye;
+    }
 
-   if (Fcb->Flags & FCB_IS_PAGE_FILE)
-   {
-      if (!ExAcquireResourceSharedLite(Resource,
-                                       (BOOLEAN)(IrpContext->Flags & IRPCONTEXT_CANWAIT)))
-      {
-         Resource = NULL;
-         Status = STATUS_PENDING;
-         goto ByeBye;
-      }
-   }
-   else
-   {
-      if (!ExAcquireResourceExclusiveLite(Resource,
-                                          (BOOLEAN)(IrpContext->Flags & IRPCONTEXT_CANWAIT)))
-      {
-         Resource = NULL;
-         Status = STATUS_PENDING;
-         goto ByeBye;
-      }
-   }
+    ByteOffset = IrpContext->Stack->Parameters.Write.ByteOffset;
+    if (ByteOffset.u.LowPart == FILE_WRITE_TO_END_OF_FILE &&
+        ByteOffset.u.HighPart == -1)
+    {
+        ByteOffset.QuadPart = Fcb->RFCB.FileSize.QuadPart;
+    }
+    Length = IrpContext->Stack->Parameters.Write.Length;
+    BytesPerSector = IrpContext->DeviceExt->FatInfo.BytesPerSector;
 
-   if (!(IrpContext->Irp->Flags & IRP_PAGING_IO) &&
-      FsRtlAreThereCurrentFileLocks(&Fcb->FileLock))
-   {
-      if (!FsRtlCheckLockForWriteAccess(&Fcb->FileLock, IrpContext->Irp))
-      {
-         Status = STATUS_FILE_LOCK_CONFLICT;
-         goto ByeBye;
-       }
+    if (ByteOffset.u.HighPart && !IsVolume)
+    {
+        Status = STATUS_INVALID_PARAMETER;
+        goto ByeBye;
+    }
+
+    if (IsFAT || IsVolume ||
+        vfatDirEntryGetFirstCluster(IrpContext->DeviceExt, &Fcb->entry) == 1)
+    {
+        if (ByteOffset.QuadPart + Length > Fcb->RFCB.FileSize.QuadPart)
+        {
+            // we can't extend the FAT, the volume or the root on FAT12/FAT16
+            Status = STATUS_END_OF_FILE;
+            goto ByeBye;
+        }
+    }
+
+    if (PagingIo || NoCache || IsVolume)
+    {
+        if (ByteOffset.u.LowPart % BytesPerSector != 0 || Length % BytesPerSector != 0)
+        {
+            // non cached write must be sector aligned
+            Status = STATUS_INVALID_PARAMETER;
+            goto ByeBye;
+        }
+    }
+
+    OldFileSize = Fcb->RFCB.FileSize;
+
+    if (Length == 0)
+    {
+        /* Update last write time */
+        IrpContext->Irp->IoStatus.Information = 0;
+        Status = STATUS_SUCCESS;
+        goto Metadata;
+    }
+
+    if (PagingIo)
+    {
+        if (ByteOffset.u.LowPart + Length > Fcb->RFCB.AllocationSize.u.LowPart)
+        {
+            Status = STATUS_INVALID_PARAMETER;
+            goto ByeBye;
+        }
+
+        if (ByteOffset.u.LowPart + Length > ROUND_UP(Fcb->RFCB.AllocationSize.u.LowPart, BytesPerSector))
+        {
+            Length =  ROUND_UP(Fcb->RFCB.FileSize.u.LowPart, BytesPerSector) - ByteOffset.u.LowPart;
+        }
     }
 
-   if (!(IrpContext->Flags & IRPCONTEXT_CANWAIT) && !(Fcb->Flags & FCB_IS_VOLUME))
-   {
-      if (ByteOffset.u.LowPart + Length > Fcb->RFCB.AllocationSize.u.LowPart)
-      {
+    if (!NoCache && !CcCanIWrite(IrpContext->FileObject, Length, CanWait,
+                                 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_DEFERRED_WRITE)))
+    {
+        BOOLEAN Retrying;
+
+        Retrying = BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_DEFERRED_WRITE);
+        SetFlag(IrpContext->Flags, IRPCONTEXT_DEFERRED_WRITE);
+
         Status = STATUS_PENDING;
+        CcDeferWrite(IrpContext->FileObject, VfatHandleDeferredWrite,
+                     IrpContext, NULL, Length, Retrying);
+
+        DPRINT1("Dererring write!\n");
+
         goto ByeBye;
-      }
-   }
+    }
 
-   OldFileSize = Fcb->RFCB.FileSize;
+    if (IsVolume)
+    {
+        Resource = &IrpContext->DeviceExt->DirResource;
+    }
+    else if (PagingIo)
+    {
+        Resource = &Fcb->PagingIoResource;
+    }
+    else
+    {
+        Resource = &Fcb->MainResource;
+    }
 
-   Buffer = VfatGetUserBuffer(IrpContext->Irp);
-   if (!Buffer)
-     {
-       Status = STATUS_INVALID_USER_BUFFER;
-       goto ByeBye;
-     }
-
-
-   if (!(Fcb->Flags & (FCB_IS_FAT|FCB_IS_VOLUME)) &&
-       !(IrpContext->Irp->Flags & IRP_PAGING_IO) &&
-       ByteOffset.u.LowPart + Length > Fcb->RFCB.FileSize.u.LowPart)
-   {
-      LARGE_INTEGER AllocationSize;
-      AllocationSize.QuadPart = ByteOffset.u.LowPart + Length;
-      Status = VfatSetAllocationSizeInformation(IrpContext->FileObject, Fcb,
-                                               IrpContext->DeviceExt, &AllocationSize);
-      if (!NT_SUCCESS (Status))
-      {
-         goto ByeBye;
-      }
-   }
+    if (PagingIo)
+    {
+        if (!ExAcquireResourceSharedLite(Resource, CanWait))
+        {
+            Resource = NULL;
+            Status = STATUS_PENDING;
+            goto ByeBye;
+        }
+    }
+    else
+    {
+        if (!ExAcquireResourceExclusiveLite(Resource, CanWait))
+        {
+            Resource = NULL;
+            Status = STATUS_PENDING;
+            goto ByeBye;
+        }
+    }
 
-   if (!(IrpContext->Irp->Flags & (IRP_NOCACHE|IRP_PAGING_IO)) &&
-      !(Fcb->Flags & (FCB_IS_PAGE_FILE|FCB_IS_VOLUME)))
-   {
-      // cached write
-
-      if (IrpContext->FileObject->PrivateCacheMap == NULL)
-      {
-         CcInitializeCacheMap(IrpContext->FileObject,
-                              (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
-                              FALSE,
-                              &VfatGlobalData->CacheMgrCallbacks,
-                              Fcb);
-      }
-      if (ByteOffset.QuadPart > OldFileSize.QuadPart)
-      {
-         CcZeroData(IrpContext->FileObject, &OldFileSize, &ByteOffset, TRUE);
-      }
-      if (CcCopyWrite(IrpContext->FileObject, &ByteOffset, Length,
-                      1 /*IrpContext->Flags & IRPCONTEXT_CANWAIT*/, Buffer))
-      {
-        IrpContext->Irp->IoStatus.Information = Length;
-         Status = STATUS_SUCCESS;
-      }
-      else
-      {
-         Status = STATUS_UNSUCCESSFUL;
-      }
-   }
-   else
-   {
-      // non cached write
+    if (!PagingIo &&
+        FsRtlAreThereCurrentFileLocks(&Fcb->FileLock))
+    {
+        if (!FsRtlCheckLockForWriteAccess(&Fcb->FileLock, IrpContext->Irp))
+        {
+            Status = STATUS_FILE_LOCK_CONFLICT;
+            goto ByeBye;
+        }
+    }
 
-      if (ByteOffset.QuadPart > OldFileSize.QuadPart)
-      {
-         CcZeroData(IrpContext->FileObject, &OldFileSize, &ByteOffset, TRUE);
-      }
+    if (!CanWait && !IsVolume)
+    {
+        if (ByteOffset.u.LowPart + Length > Fcb->RFCB.AllocationSize.u.LowPart)
+        {
+            Status = STATUS_PENDING;
+            goto ByeBye;
+        }
+    }
+
+    Buffer = VfatGetUserBuffer(IrpContext->Irp, PagingIo);
 
-      Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoReadAccess);
-      if (!NT_SUCCESS(Status))
+    if (!IsFAT && !IsVolume && !PagingIo &&
+        ByteOffset.u.LowPart + Length > Fcb->RFCB.FileSize.u.LowPart)
+    {
+        LARGE_INTEGER AllocationSize;
+        AllocationSize.QuadPart = ByteOffset.u.LowPart + Length;
+        Status = VfatSetAllocationSizeInformation(IrpContext->FileObject, Fcb,
+                                                  IrpContext->DeviceExt, &AllocationSize);
+        if (!NT_SUCCESS (Status))
         {
-         goto ByeBye;
-       }
-
-      Status = VfatWriteFileData(IrpContext, Length, ByteOffset);
-      if (NT_SUCCESS(Status))
-      {
-         IrpContext->Irp->IoStatus.Information = Length;
-      }
-   }
+            goto ByeBye;
+        }
+    }
 
-   if (!(IrpContext->Irp->Flags & IRP_PAGING_IO) &&
-      !(Fcb->Flags & (FCB_IS_FAT|FCB_IS_VOLUME)))
-   {
-      if(!(*Fcb->Attributes & FILE_ATTRIBUTE_DIRECTORY))
-      {
-         LARGE_INTEGER SystemTime;
-         // set dates and times
-         KeQuerySystemTime (&SystemTime);
-         if (Fcb->Flags & FCB_IS_FATX_ENTRY)
-         {
-            FsdSystemTimeToDosDateTime (IrpContext->DeviceExt,
-                                     &SystemTime, &Fcb->entry.FatX.UpdateDate,
-                                     &Fcb->entry.FatX.UpdateTime);
-            Fcb->entry.FatX.AccessDate = Fcb->entry.FatX.UpdateDate;
-            Fcb->entry.FatX.AccessTime = Fcb->entry.FatX.UpdateTime;
-         }
-         else
-         {
-            FsdSystemTimeToDosDateTime (IrpContext->DeviceExt,
-                                     &SystemTime, &Fcb->entry.Fat.UpdateDate,
-                                     &Fcb->entry.Fat.UpdateTime);
-            Fcb->entry.Fat.AccessDate = Fcb->entry.Fat.UpdateDate;
-         }
-         /* set date and times to dirty */
-        Fcb->Flags |= FCB_IS_DIRTY;
-      }
-   }
+    if (!NoCache && !PagingIo && !IsVolume)
+    {
+        // cached write
 
-ByeBye:
-   if (Resource)
-   {
-      ExReleaseResourceLite(Resource);
-   }
+        vfatAddToStat(IrpContext->DeviceExt, Base.UserFileWrites, 1);
+        vfatAddToStat(IrpContext->DeviceExt, Base.UserFileWriteBytes, Length);
 
-   if (Status == STATUS_PENDING)
-   {
-      Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoReadAccess);
-      if (NT_SUCCESS(Status))
-      {
-         Status = VfatQueueRequest(IrpContext);
-      }
-      else
-      {
-         IrpContext->Irp->IoStatus.Status = Status;
-         IoCompleteRequest(IrpContext->Irp, IO_NO_INCREMENT);
-         VfatFreeIrpContext(IrpContext);
-      }
-   }
-   else
-   {
-      IrpContext->Irp->IoStatus.Status = Status;
-      if (IrpContext->FileObject->Flags & FO_SYNCHRONOUS_IO &&
-          !(IrpContext->Irp->Flags & IRP_PAGING_IO) && NT_SUCCESS(Status))
-      {
-         IrpContext->FileObject->CurrentByteOffset.QuadPart =
-           ByteOffset.QuadPart + IrpContext->Irp->IoStatus.Information;
-      }
-
-      IoCompleteRequest(IrpContext->Irp,
-                        (CCHAR)(NT_SUCCESS(Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT));
-      VfatFreeIrpContext(IrpContext);
-   }
-   DPRINT("%x\n", Status);
-   return Status;
-}
+        _SEH2_TRY
+        {
+            if (IrpContext->FileObject->PrivateCacheMap == NULL)
+            {
+                CcInitializeCacheMap(IrpContext->FileObject,
+                                     (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
+                                     FALSE,
+                                     &VfatGlobalData->CacheMgrCallbacks,
+                                     Fcb);
+            }
+
+            if (ByteOffset.QuadPart > OldFileSize.QuadPart)
+            {
+                CcZeroData(IrpContext->FileObject, &OldFileSize, &ByteOffset, TRUE);
+            }
+
+            if (CcCopyWrite(IrpContext->FileObject,
+                            &ByteOffset,
+                            Length,
+                            TRUE /*CanWait*/,
+                            Buffer))
+            {
+                IrpContext->Irp->IoStatus.Information = Length;
+                Status = STATUS_SUCCESS;
+            }
+            else
+            {
+                ASSERT(FALSE /*!CanWait*/);
+                Status = STATUS_UNSUCCESSFUL;
+            }
+        }
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+        {
+            Status = _SEH2_GetExceptionCode();
+        }
+        _SEH2_END;
+    }
+    else
+    {
+        // non cached write
+        Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoReadAccess);
+        if (!NT_SUCCESS(Status))
+        {
+            Status = STATUS_INVALID_USER_BUFFER;
+            goto ByeBye;
+        }
+
+        if (ByteOffset.QuadPart > OldFileSize.QuadPart)
+        {
+            CcZeroData(IrpContext->FileObject, &OldFileSize, &ByteOffset, TRUE);
+        }
+
+        if (!IsVolume)
+        {
+            vfatAddToStat(IrpContext->DeviceExt, Fat.NonCachedWrites, 1);
+            vfatAddToStat(IrpContext->DeviceExt, Fat.NonCachedWriteBytes, Length);
+        }
+        else
+        {
+            vfatAddToStat(IrpContext->DeviceExt, Base.MetaDataWrites, 1);
+            vfatAddToStat(IrpContext->DeviceExt, Base.MetaDataWriteBytes, Length);
+        }
 
+        Status = VfatWriteFileData(IrpContext, Length, ByteOffset);
+        if (NT_SUCCESS(Status))
+        {
+            IrpContext->Irp->IoStatus.Information = Length;
+        }
+    }
 
+Metadata:
+    if (!PagingIo && !IsFAT && !IsVolume)
+    {
+        if(!vfatFCBIsDirectory(Fcb))
+        {
+            LARGE_INTEGER SystemTime;
+            ULONG Filter;
+
+            // set dates and times
+            KeQuerySystemTime (&SystemTime);
+            if (vfatVolumeIsFatX(IrpContext->DeviceExt))
+            {
+                FsdSystemTimeToDosDateTime(IrpContext->DeviceExt,
+                                           &SystemTime, &Fcb->entry.FatX.UpdateDate,
+                                           &Fcb->entry.FatX.UpdateTime);
+                Fcb->entry.FatX.AccessDate = Fcb->entry.FatX.UpdateDate;
+                Fcb->entry.FatX.AccessTime = Fcb->entry.FatX.UpdateTime;
+            }
+            else
+            {
+                FsdSystemTimeToDosDateTime(IrpContext->DeviceExt,
+                                           &SystemTime, &Fcb->entry.Fat.UpdateDate,
+                                           &Fcb->entry.Fat.UpdateTime);
+                Fcb->entry.Fat.AccessDate = Fcb->entry.Fat.UpdateDate;
+            }
+            /* set date and times to dirty */
+            Fcb->Flags |= FCB_IS_DIRTY;
+
+            /* Time to notify the OS */
+            Filter = FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
+            if (ByteOffset.QuadPart != OldFileSize.QuadPart) Filter |= FILE_NOTIFY_CHANGE_SIZE;
+
+            vfatReportChange(IrpContext->DeviceExt, Fcb, Filter, FILE_ACTION_MODIFIED);
+        }
+    }
+
+ByeBye:
+    if (Resource)
+    {
+        ExReleaseResourceLite(Resource);
+    }
+
+    if (Status == STATUS_PENDING)
+    {
+        Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoReadAccess);
+        if (NT_SUCCESS(Status))
+        {
+            Status = VfatMarkIrpContextForQueue(IrpContext);
+        }
+    }
+    else
+    {
+        IrpContext->Irp->IoStatus.Status = Status;
+        if (BooleanFlagOn(IrpContext->FileObject->Flags, FO_SYNCHRONOUS_IO) &&
+            !PagingIo && NT_SUCCESS(Status))
+        {
+            IrpContext->FileObject->CurrentByteOffset.QuadPart =
+                ByteOffset.QuadPart + IrpContext->Irp->IoStatus.Information;
+        }
+
+        if (NT_SUCCESS(Status))
+            IrpContext->PriorityBoost = IO_DISK_INCREMENT;
+    }
+    DPRINT("%x\n", Status);
+    return Status;
+}