Disabled the cache access for the page file in VfatReadFile()/VfatWriteFile().
[reactos.git] / reactos / drivers / fs / vfat / rw.c
index c9418eb..bb08abb 100644 (file)
@@ -1,4 +1,5 @@
-/* $Id: rw.c,v 1.6 2000/06/17 22:04:08 phreak Exp $
+
+/* $Id: rw.c,v 1.40 2002/03/19 02:29:32 hbirr Exp $
  *
  * COPYRIGHT:        See COPYING in the top level directory
  * PROJECT:          ReactOS kernel
 
 #include <ddk/ntddk.h>
 #include <wchar.h>
-#include <internal/string.h>
-#include <ddk/cctypes.h>
+#include <ntos/minmax.h>
 
 #define NDEBUG
-#include <internal/debug.h>
+#include <debug.h>
 
 #include "vfat.h"
 
-/* FUNCTIONS ****************************************************************/
+/* GLOBALS *******************************************************************/
+
+#define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
+#define ROUND_DOWN(N, S) ((N) - ((N) % (S)))
+
+/* FUNCTIONS *****************************************************************/
+
+NTSTATUS
+NextCluster(PDEVICE_EXTENSION DeviceExt,
+           PVFATFCB Fcb,
+           ULONG FirstCluster,
+           PULONG CurrentCluster,
+           BOOLEAN Extend)
+     /*
+      * Return the next cluster in a FAT chain, possibly extending the chain if
+      * necessary
+      */
+{
+  if (Fcb != NULL && Fcb->Flags & FCB_IS_PAGE_FILE)
+    {
+      ULONG i;
+      PULONG FatChain;
+      NTSTATUS Status;
+      DPRINT("NextCluster(Fcb %x, FirstCluster %x, Extend %d)\n", Fcb, 
+            FirstCluster, Extend);
+      if (Fcb->FatChainSize == 0)
+       {
+         /* Paging file with zero length. */
+         *CurrentCluster = 0xffffffff;
+         if (Extend)
+           {
+             Fcb->FatChain = ExAllocatePool(NonPagedPool, sizeof(ULONG));
+             if (!Fcb->FatChain)
+               {
+                 return STATUS_NO_MEMORY;
+               }
+             Status = GetNextCluster(DeviceExt, 0, CurrentCluster, TRUE);
+             if (!NT_SUCCESS(Status))
+               {
+                 ExFreePool(Fcb->FatChain);
+                 return Status;
+               }
+             Fcb->FatChain[0] = *CurrentCluster;
+             Fcb->FatChainSize = 1;
+             return Status;
+           }
+         else
+           {
+             return STATUS_UNSUCCESSFUL;
+           }
+       }
+      else
+       {
+         for (i = 0; i < Fcb->FatChainSize; i++)
+           {
+             if (Fcb->FatChain[i] == *CurrentCluster)
+               break;
+           }
+         if (i >= Fcb->FatChainSize)
+           {
+             return STATUS_UNSUCCESSFUL;
+           }
+         if (i == Fcb->FatChainSize - 1)
+           {
+             if (Extend)
+               {
+                 FatChain = ExAllocatePool(NonPagedPool, 
+                                           (i + 2) * sizeof(ULONG));
+                 if (!FatChain)
+                   {
+                     *CurrentCluster = 0xffffffff;
+                     return STATUS_NO_MEMORY;
+                   }
+                 Status = GetNextCluster(DeviceExt, *CurrentCluster, 
+                                         CurrentCluster, TRUE);
+                 if (NT_SUCCESS(Status) && *CurrentCluster != 0xffffffff)
+                   {
+                     memcpy(FatChain, Fcb->FatChain, (i + 1) * sizeof(ULONG));
+                     FatChain[i + 1] = *CurrentCluster;
+                     ExFreePool(Fcb->FatChain);
+                     Fcb->FatChain = FatChain;
+                     Fcb->FatChainSize = i + 2;
+                   }
+                 else
+                   {
+                     ExFreePool(FatChain);
+                   }
+                 return Status;
+               }
+             else
+               {
+                 *CurrentCluster = 0xffffffff;
+                 return STATUS_SUCCESS;
+               }
+           }
+         *CurrentCluster = Fcb->FatChain[i + 1];
+         return STATUS_SUCCESS;
+       }
+    }
+  if (FirstCluster == 1)
+    {
+      (*CurrentCluster) += DeviceExt->FatInfo.SectorsPerCluster;
+      return(STATUS_SUCCESS);
+    }
+  else
+    {
+      /* 
+       * CN: FIXME: Real bug here or in dirwr, where CurrentCluster isn't 
+       * initialized when 0
+       */
+      if (FirstCluster == 0)
+       {
+         NTSTATUS Status;
+         
+         Status = GetNextCluster(DeviceExt, 0, CurrentCluster,
+                                 Extend);
+         return(Status);
+       }
+      else
+       {
+         NTSTATUS Status;
+         
+         Status = GetNextCluster(DeviceExt, (*CurrentCluster), CurrentCluster,
+                                 Extend);
+         return(Status);
+       }
+    }
+}
+
+NTSTATUS
+OffsetToCluster(PDEVICE_EXTENSION DeviceExt,
+               PVFATFCB Fcb,
+               ULONG FirstCluster,
+               ULONG FileOffset,
+               PULONG Cluster,
+               BOOLEAN Extend)
+     /*
+      * Return the cluster corresponding to an offset within a file,
+      * possibly extending the file if necessary
+      */
+{
+  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);
+  if (FirstCluster == 0)
+  {
+    DbgPrint("OffsetToCluster is called with FirstCluster = 0!\n");
+    KeBugCheck(0);
+  }
+
+  if (Fcb != NULL && Fcb->Flags & FCB_IS_PAGE_FILE)
+    {
+      ULONG NCluster;
+      ULONG Offset = FileOffset / DeviceExt->FatInfo.BytesPerCluster;
+      PULONG FatChain;
+      int i;
+      if (Fcb->FatChainSize == 0)
+      {
+        DbgPrint("OffsetToCluster is called with FirstCluster = %x"
+                 " and Fcb->FatChainSize = 0!\n", FirstCluster);
+        KeBugCheck(0);
+      }
+      if (Offset < Fcb->FatChainSize)
+      {
+        *Cluster = Fcb->FatChain[Offset];
+        return STATUS_SUCCESS;
+      }
+      else
+      {
+        if (!Extend)
+        {
+          *Cluster = 0xffffffff;
+          return STATUS_UNSUCCESSFUL;
+        }
+        else
+       {
+          FatChain = ExAllocatePool(NonPagedPool, (Offset + 1) * sizeof(ULONG));
+          if (!FatChain)
+          {
+            *Cluster = 0xffffffff;
+            return STATUS_UNSUCCESSFUL;
+          }
+                 
+          CurrentCluster = Fcb->FatChain[Fcb->FatChainSize - 1];
+          FatChain[Fcb->FatChainSize - 1] = CurrentCluster;
+          for (i = Fcb->FatChainSize; i < Offset + 1; i++)
+          {
+            Status = GetNextCluster(DeviceExt, CurrentCluster, &CurrentCluster, TRUE);
+            if (!NT_SUCCESS(Status) || CurrentCluster == 0xFFFFFFFF)
+            {
+              while (i >= Fcb->FatChainSize)
+              {
+                WriteCluster(DeviceExt, FatChain[i - 1], 0xFFFFFFFF);
+                i--;
+              }
+              *Cluster = 0xffffffff;
+              ExFreePool(FatChain);
+              if (!NT_SUCCESS(Status))
+                 return Status;
+              return STATUS_UNSUCCESSFUL;
+            }
+            FatChain[i] = CurrentCluster;
+          }
+          memcpy (FatChain, Fcb->FatChain, Fcb->FatChainSize * sizeof(ULONG));
+          ExFreePool(Fcb->FatChain);
+          Fcb->FatChain = FatChain;
+          Fcb->FatChainSize = Offset + 1;
+        }
+      }
+      *Cluster = CurrentCluster;
+      return(STATUS_SUCCESS);
+    }
+  if (FirstCluster == 1)
+    {
+      /* root of FAT16 or FAT12 */
+      *Cluster = DeviceExt->FatInfo.rootStart + FileOffset
+       / (DeviceExt->FatInfo.BytesPerCluster) * DeviceExt->FatInfo.SectorsPerCluster;
+      return(STATUS_SUCCESS);
+    }
+  else
+    {
+      CurrentCluster = FirstCluster;
+      for (i = 0; i < FileOffset / DeviceExt->FatInfo.BytesPerCluster; i++)
+       {
+         Status = GetNextCluster (DeviceExt, CurrentCluster, &CurrentCluster,
+                                  Extend);
+         if (!NT_SUCCESS(Status))
+           {
+             return(Status);
+           }
+       }
+      *Cluster = CurrentCluster;
+      return(STATUS_SUCCESS);
+    }
+}
+
+NTSTATUS
+VfatReadCluster(PDEVICE_EXTENSION DeviceExt,
+               PVFATFCB Fcb,
+               ULONG FirstCluster,
+               PULONG CurrentCluster,
+               PVOID Destination,
+               ULONG InternalOffset,
+               ULONG InternalLength)
+{
+  PVOID BaseAddress = NULL;
+  NTSTATUS Status;
 
-NTSTATUS FsdReadFile(PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject,
-                    PVOID Buffer, ULONG Length, ULONG ReadOffset,
-                    PULONG LengthRead)
+  if (InternalLength == DeviceExt->FatInfo.BytesPerCluster)
+  {
+    Status = VfatRawReadCluster(DeviceExt, FirstCluster,
+                                Destination, *CurrentCluster, 1);
+  }
+  else
+  {
+    BaseAddress = ExAllocatePool(NonPagedPool, DeviceExt->FatInfo.BytesPerCluster);
+    if (BaseAddress == NULL)
+    {
+      return(STATUS_NO_MEMORY);
+    }
+    Status = VfatRawReadCluster(DeviceExt, FirstCluster,
+                                BaseAddress, *CurrentCluster, 1);
+    memcpy(Destination, BaseAddress + InternalOffset, InternalLength);
+    ExFreePool(BaseAddress);
+  }
+  if (!NT_SUCCESS(Status))
+  {
+     return(Status);
+  }
+  Status = NextCluster(DeviceExt, Fcb, FirstCluster, CurrentCluster, FALSE);
+  return(Status);
+}
+
+NTSTATUS
+VfatReadFile (PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject,
+             PVOID Buffer, ULONG Length, ULONG ReadOffset,
+             PULONG LengthRead, ULONG NoCache)
 /*
  * FUNCTION: Reads data from a file
  */
 {
-   ULONG CurrentCluster;
-   ULONG FileOffset;
-   ULONG FirstCluster;
-   PVFATFCB  Fcb;
-   PVOID Temp;
-   ULONG TempLength;
-   
-   /* PRECONDITION */
-   assert(DeviceExt != NULL);
-   assert(DeviceExt->BytesPerCluster != 0);
-   assert(FileObject != NULL);
-   assert(FileObject->FsContext != NULL);
-
-   DPRINT("FsdReadFile(DeviceExt %x, FileObject %x, Buffer %x, "
-           "Length %d, ReadOffset %d)\n",DeviceExt,FileObject,Buffer,
-           Length,ReadOffset);
-   
-   Fcb = ((PVFATCCB)(FileObject->FsContext2))->pFcb;
-   if (DeviceExt->FatType == FAT32)
-       CurrentCluster = Fcb->entry.FirstCluster
-                +Fcb->entry.FirstClusterHigh*65536;
-   else
-       CurrentCluster = Fcb->entry.FirstCluster;
-   FirstCluster=CurrentCluster;
-   DPRINT("DeviceExt->BytesPerCluster %x\n",DeviceExt->BytesPerCluster);
+  ULONG CurrentCluster;
+  ULONG FirstCluster;
+  ULONG StartCluster;
+  ULONG ClusterCount;
+  PVFATFCB Fcb;
+  PVFATCCB Ccb;
+  NTSTATUS Status;
+  ULONG TempLength;
+  LARGE_INTEGER FileOffset;
+  IO_STATUS_BLOCK IoStatus;
+  ULONG BytesDone;
 
-   if (ReadOffset >= Fcb->entry.FileSize)
-     {
-       return(STATUS_END_OF_FILE);
-     }
-   if ((ReadOffset + Length) > Fcb->entry.FileSize)
-     {
-       Length = Fcb->entry.FileSize - ReadOffset;
-     }
-   *LengthRead = 0;
-   /* FIXME: optimize by remembering the last cluster read and using if possible */
-   Temp = ExAllocatePool(NonPagedPool,DeviceExt->BytesPerCluster);
-   if(!Temp) return STATUS_UNSUCCESSFUL;
-   if (FirstCluster==1)
-   {  //root of FAT16 or FAT12
-     CurrentCluster=DeviceExt->rootStart+ReadOffset
-              /(DeviceExt->BytesPerCluster)*DeviceExt->Boot->SectorsPerCluster;
-   }
-   else
-     for (FileOffset=0; FileOffset < ReadOffset / DeviceExt->BytesPerCluster
-           ; FileOffset++)
-     {
-       CurrentCluster = GetNextCluster(DeviceExt,CurrentCluster);
-     }
-   CHECKPOINT;
-   if ((ReadOffset % DeviceExt->BytesPerCluster)!=0)
-     {
-       if (FirstCluster == 1)
-         {
-            VFATReadSectors(DeviceExt->StorageDevice,
-                            CurrentCluster,
-                            DeviceExt->Boot->SectorsPerCluster,
-                            Temp);
-            CurrentCluster += DeviceExt->Boot->SectorsPerCluster;
-         }
-       else
-         {
-            VFATLoadCluster(DeviceExt,Temp,CurrentCluster);
-            CurrentCluster = GetNextCluster(DeviceExt, CurrentCluster);
-         }
-       TempLength = min(Length,DeviceExt->BytesPerCluster -
-                        (ReadOffset % DeviceExt->BytesPerCluster));
-       
-       memcpy(Buffer, Temp + ReadOffset % DeviceExt->BytesPerCluster,
-              TempLength);
-       
-       (*LengthRead) = (*LengthRead) + TempLength;
-       Length = Length - TempLength;
-       Buffer = Buffer + TempLength;        
-     }
-   CHECKPOINT;
-   while (Length >= DeviceExt->BytesPerCluster)
+  /* PRECONDITION */
+  assert (DeviceExt != NULL);
+  assert (DeviceExt->FatInfo.BytesPerCluster != 0);
+  assert (FileObject != NULL);
+  assert (FileObject->FsContext2 != NULL);
+
+  DPRINT("VfatReadFile(DeviceExt %x, FileObject %x, Buffer %x, "
+        "Length %d, ReadOffset 0x%x)\n", DeviceExt, FileObject, Buffer,
+        Length, ReadOffset);
+
+  *LengthRead = 0;
+
+  Ccb = (PVFATCCB)FileObject->FsContext2;
+  Fcb = Ccb->pFcb;
+
+  /* Is this a read of the FAT? */
+  if (Fcb->Flags & FCB_IS_FAT)
+    {
+      if (!NoCache)
+       {
+         DbgPrint ("Cached FAT read outside from VFATFS.SYS\n");
+         KeBugCheck (0);
+       }
+      if (ReadOffset >= Fcb->RFCB.FileSize.QuadPart || 
+         ReadOffset % BLOCKSIZE != 0 || Length % BLOCKSIZE != 0)
+       {
+         DbgPrint ("Start or end of FAT read is not on a sector boundary\n");
+         KeBugCheck (0);
+       }
+      if (ReadOffset + Length > Fcb->RFCB.FileSize.QuadPart)
+       {
+         Length = Fcb->RFCB.FileSize.QuadPart - ReadOffset;
+       }
+
+      Status = VfatReadSectors(DeviceExt->StorageDevice,
+                              DeviceExt->FatInfo.FATStart + ReadOffset / BLOCKSIZE, 
+                              Length / BLOCKSIZE, Buffer);
+      if (NT_SUCCESS(Status))
+       {
+         *LengthRead = Length;
+       }
+      else
+       {
+         DPRINT1("FAT reading failed, Status %x\n", Status);
+       }
+      return Status;
+    }
+
+  /*
+   * Find the first cluster
+   */
+  FirstCluster = CurrentCluster = 
+    vfatDirEntryGetFirstCluster (DeviceExt, &Fcb->entry);
+
+  /*
+   * Truncate the read if necessary
+   */
+  if (!(Fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY))
+    {
+      if (ReadOffset >= Fcb->entry.FileSize)
+       {
+         return (STATUS_END_OF_FILE);
+       }
+      if ((ReadOffset + Length) > Fcb->entry.FileSize)
+       {
+         Length = Fcb->entry.FileSize - ReadOffset;
+       }
+    }
+
+  if (FirstCluster == 1)
+    {
+      /* root directory of FAT12 or FAT16 */
+      if (ReadOffset + Length > DeviceExt->FatInfo.rootDirectorySectors * BLOCKSIZE)
+       {
+         Length = DeviceExt->FatInfo.rootDirectorySectors * BLOCKSIZE - ReadOffset;
+       }
+  }
+
+  /* Using the Cc-interface if possible. */
+  if (!NoCache && !(Fcb->Flags & FCB_IS_PAGE_FILE))
+    {
+      FileOffset.QuadPart = ReadOffset;
+      CcCopyRead(FileObject, &FileOffset, Length, TRUE, Buffer, &IoStatus);
+      *LengthRead = IoStatus.Information;
+      return IoStatus.Status;
+    }
+
+  /*
+   * Find the cluster to start the read from
+   */
+  if (Ccb->LastCluster > 0 && ReadOffset > Ccb->LastOffset)
+    {
+      CurrentCluster = Ccb->LastCluster;
+    }
+  Status = OffsetToCluster(DeviceExt,
+                          Fcb,
+                          FirstCluster,
+                          ROUND_DOWN(ReadOffset, DeviceExt->FatInfo.BytesPerCluster),
+                          &CurrentCluster,
+                          FALSE);
+  if (!NT_SUCCESS(Status))
+    {
+      return(Status);
+    }
+  /*
+   * If the read doesn't begin on a chunk boundary then we need special
+   * handling
+   */
+  if ((ReadOffset % DeviceExt->FatInfo.BytesPerCluster) != 0 )
+    {
+      TempLength = min (Length, DeviceExt->FatInfo.BytesPerCluster - 
+                       (ReadOffset % DeviceExt->FatInfo.BytesPerCluster));
+      Ccb->LastCluster = CurrentCluster;
+      Ccb->LastOffset = ROUND_DOWN(ReadOffset, DeviceExt->FatInfo.BytesPerCluster);
+      Status = VfatReadCluster(DeviceExt, Fcb, FirstCluster, &CurrentCluster, 
+                              Buffer, 
+                              ReadOffset % DeviceExt->FatInfo.BytesPerCluster, 
+                              TempLength);
+      if (NT_SUCCESS(Status))
+       {
+         (*LengthRead) = (*LengthRead) + TempLength;
+         Length = Length - TempLength;
+         Buffer = Buffer + TempLength;
+         ReadOffset = ReadOffset + TempLength;
+       }
+  }
+
+  while (Length >= DeviceExt->FatInfo.BytesPerCluster && 
+        CurrentCluster != 0xffffffff && NT_SUCCESS(Status))
+    {
+      StartCluster = CurrentCluster;
+      ClusterCount = 0;
+      BytesDone = 0;
+      /* Search for continous clusters. */
+      do
+       {
+         ClusterCount++;
+         BytesDone += DeviceExt->FatInfo.BytesPerCluster;
+         Status = NextCluster(DeviceExt, Fcb, FirstCluster, &CurrentCluster, 
+                              FALSE);
+       }
+      while (StartCluster + ClusterCount == CurrentCluster && 
+            NT_SUCCESS(Status) &&
+            Length - BytesDone >= DeviceExt->FatInfo.BytesPerCluster);
+
+      DPRINT("Count %d, Start %x Next %x\n", ClusterCount, StartCluster, 
+            CurrentCluster);
+      Ccb->LastCluster = StartCluster + (ClusterCount - 1);
+      Ccb->LastOffset = ReadOffset + 
+       (ClusterCount - 1) * DeviceExt->FatInfo.BytesPerCluster;
+      
+      Status = VfatRawReadCluster(DeviceExt, FirstCluster, Buffer, 
+                                 StartCluster, ClusterCount);
+      if (NT_SUCCESS(Status))
+       {
+         ClusterCount *=  DeviceExt->FatInfo.BytesPerCluster;
+         (*LengthRead) = (*LengthRead) + ClusterCount;
+         Buffer += ClusterCount;
+         Length -= ClusterCount;
+         ReadOffset += ClusterCount;
+       }
+    }
+  /*
+   * If the read doesn't end on a chunk boundary then we need special
+   * handling
+   */
+  if (Length > 0 && CurrentCluster != 0xffffffff && NT_SUCCESS(Status))
+    {
+      Ccb->LastCluster = CurrentCluster;
+      Ccb->LastOffset = ReadOffset + DeviceExt->FatInfo.BytesPerCluster;
+
+      Status = VfatReadCluster(DeviceExt, Fcb, FirstCluster, &CurrentCluster,
+                              Buffer, 0, Length);
+      if (NT_SUCCESS(Status))
+       {
+         (*LengthRead) = (*LengthRead) + Length;
+       }
+    }
+  return Status;
+}
+
+NTSTATUS
+VfatWriteCluster(PDEVICE_EXTENSION DeviceExt,
+                PVFATFCB Fcb,
+                ULONG StartOffset,
+                ULONG FirstCluster,
+                PULONG CurrentCluster,
+                PVOID Source,
+                ULONG InternalOffset,
+                ULONG InternalLength)
+{
+  PVOID BaseAddress;
+  NTSTATUS Status;
+
+  if (InternalLength != DeviceExt->FatInfo.BytesPerCluster)
+  {
+     BaseAddress = ExAllocatePool(NonPagedPool, DeviceExt->FatInfo.BytesPerCluster);
+     if (BaseAddress == NULL)
      {
-       if (FirstCluster == 1)
-         {
-            VFATReadSectors(DeviceExt->StorageDevice,
-                            CurrentCluster,
-                            DeviceExt->Boot->SectorsPerCluster,
-                            Buffer);
-            CurrentCluster += DeviceExt->Boot->SectorsPerCluster;
-         }
-       else
-         {
-            VFATLoadCluster(DeviceExt,Buffer,CurrentCluster);
-            CurrentCluster = GetNextCluster(DeviceExt, CurrentCluster);
-         }
-       if (CurrentCluster == 0xffffffff)
-         {
-            ExFreePool(Temp);
-            return(STATUS_SUCCESS);
-         }
-       
-       (*LengthRead) = (*LengthRead) + DeviceExt->BytesPerCluster;
-       Buffer = Buffer + DeviceExt->BytesPerCluster;
-       Length = Length - DeviceExt->BytesPerCluster;
+       return(STATUS_NO_MEMORY);
      }
-   CHECKPOINT;
-   if (Length > 0)
+  }
+  else
+    BaseAddress = Source;
+  if (InternalLength != DeviceExt->FatInfo.BytesPerCluster)
+  {
+    /*
+     * If the data in the cache isn't valid or we are bypassing the
+     * cache and not writing a cluster aligned, cluster sized region
+     * then read data in to base address
+     */
+     Status = VfatRawReadCluster(DeviceExt, FirstCluster, BaseAddress,
+                               *CurrentCluster, 1);
+     if (!NT_SUCCESS(Status))
      {
-       (*LengthRead) = (*LengthRead) + Length;
-       if (FirstCluster == 1)
-         {
-            VFATReadSectors(DeviceExt->StorageDevice,
-                            CurrentCluster,
-                            DeviceExt->Boot->SectorsPerCluster,
-                            Temp);
-            CurrentCluster += DeviceExt->Boot->SectorsPerCluster;
-         }
-       else
-         {
-            VFATLoadCluster(DeviceExt,Temp,CurrentCluster);
-            CurrentCluster = GetNextCluster(DeviceExt, CurrentCluster);
-         }
-       memcpy(Buffer, Temp, Length);
+        if (InternalLength != DeviceExt->FatInfo.BytesPerCluster)
+        {
+          ExFreePool(BaseAddress);
+        }
+        return(Status);
      }
-   ExFreePool(Temp);
-   return(STATUS_SUCCESS);
+     memcpy(BaseAddress + InternalOffset, Source, InternalLength);
+  }
+  /*
+   * Write the data back to disk
+   */
+  DPRINT("Writing 0x%x\n", *CurrentCluster);
+  Status = VfatRawWriteCluster(DeviceExt, FirstCluster, BaseAddress,
+                              *CurrentCluster, 1);
+  if (InternalLength != DeviceExt->FatInfo.BytesPerCluster)
+  {
+    ExFreePool(BaseAddress);
+  }
+  if (!NT_SUCCESS(Status))
+  {
+    return Status;
+  }
+  Status = NextCluster(DeviceExt, Fcb, FirstCluster, CurrentCluster, FALSE);
+  return(Status);
 }
 
-NTSTATUS FsdWriteFile(PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject,
-                     PVOID Buffer, ULONG Length, ULONG WriteOffset)
+NTSTATUS
+VfatWriteFile (PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject,
+              PVOID Buffer, ULONG Length, ULONG WriteOffset,
+              BOOLEAN NoCache, BOOLEAN PageIo)
 /*
  * FUNCTION: Writes data to file
  */
 {
-   ULONG CurrentCluster;
-   ULONG FileOffset;
-   ULONG FirstCluster;
-   PVFATFCB  Fcb;
-   PVFATCCB pCcb;
-   PVOID Temp;
-   ULONG TempLength,Length2=Length;
-   LARGE_INTEGER SystemTime, LocalTime;
-   
-   DPRINT1("FsdWriteFile(FileObject %x, Buffer %x, Length %x, "
-          "WriteOffset %x\n", FileObject, Buffer, Length, WriteOffset);
-   
-   /* Locate the first cluster of the file */
-   assert(FileObject);
-   pCcb=(PVFATCCB)(FileObject->FsContext2);
-   assert(pCcb);
-   Fcb = pCcb->pFcb;
-   assert(Fcb);
-   if (DeviceExt->FatType == FAT32)
-     {
-       CurrentCluster = 
-         Fcb->entry.FirstCluster+Fcb->entry.FirstClusterHigh*65536;
-     }
-   else
-     {
-       CurrentCluster = Fcb->entry.FirstCluster;
-     }
-   FirstCluster=CurrentCluster;
-   
-   /* Allocate a buffer to hold 1 cluster of data */
-   Temp = ExAllocatePool(NonPagedPool, DeviceExt->BytesPerCluster);
-   assert(Temp);
-
-   /* Find the cluster according to the offset in the file */
-   if (CurrentCluster==1)
-     {  
-       CurrentCluster=DeviceExt->rootStart+WriteOffset
-          / DeviceExt->BytesPerCluster*DeviceExt->Boot->SectorsPerCluster;
-     }
-   else
-     {
-       if (CurrentCluster==0)
-         {
-            /*
-             * File of size zero
-             */
-            CurrentCluster=GetNextWriteCluster(DeviceExt,0);
-            if (DeviceExt->FatType == FAT32)
-              {
-                 Fcb->entry.FirstClusterHigh = CurrentCluster>>16;
-                 Fcb->entry.FirstCluster = CurrentCluster;
-              }
-            else
-              Fcb->entry.FirstCluster=CurrentCluster;
-         }
-       else
-         {
-            for (FileOffset=0; 
-                 FileOffset < WriteOffset / DeviceExt->BytesPerCluster; 
-                 FileOffset++)
-              {
-                 CurrentCluster = GetNextCluster(DeviceExt,CurrentCluster);
-              }
-         }
-       CHECKPOINT;     
-     }
-   
-   /*
-    * If the offset in the cluster doesn't fall on the cluster boundary 
-    * then we have to write only from the specified offset
-    */
-   
-   if ((WriteOffset % DeviceExt->BytesPerCluster)!=0)
-     {
-       CHECKPOINT;
-       TempLength = min(Length,DeviceExt->BytesPerCluster -
-                        (WriteOffset % DeviceExt->BytesPerCluster));
-       /* Read in the existing cluster data */
-       if (FirstCluster==1)
-         {
-                 VFATReadSectors(DeviceExt->StorageDevice,
-                                 CurrentCluster,
-                                 DeviceExt->Boot->SectorsPerCluster,
-                                 Temp);
-         }
-       else
-         {
-            VFATLoadCluster(DeviceExt,Temp,CurrentCluster);
-         }
-
-       /* Overwrite the last parts of the data as necessary */
-       memcpy(Temp + (WriteOffset % DeviceExt->BytesPerCluster), 
-              Buffer,
-              TempLength);
-       
-       /* Write the cluster back */
-       if (FirstCluster==1)
-         {
-            VFATWriteSectors(DeviceExt->StorageDevice,
-                             CurrentCluster,
-                             DeviceExt->Boot->SectorsPerCluster,
-                             Temp);
-            CurrentCluster += DeviceExt->Boot->SectorsPerCluster;
-         }
-       else
-         {
-            VFATWriteCluster(DeviceExt,Temp,CurrentCluster);
-            CurrentCluster = GetNextCluster(DeviceExt, CurrentCluster);
-         }
-       Length2 -= TempLength;
-       Buffer = Buffer + TempLength;
-     }
-   CHECKPOINT;
-   
-   /* Write the buffer in chunks of 1 cluster */
-   
-   while (Length2 >= DeviceExt->BytesPerCluster)
-     {
-       CHECKPOINT;
-       if (CurrentCluster == 0)
-         {
-            ExFreePool(Temp);
-            return(STATUS_UNSUCCESSFUL);
-         }
-       if (FirstCluster==1)
-         {
-            VFATWriteSectors(DeviceExt->StorageDevice,
-                             CurrentCluster,
-                             DeviceExt->Boot->SectorsPerCluster,
-                             Buffer);
-            CurrentCluster += DeviceExt->Boot->SectorsPerCluster;
-         }
-       else
-         {
-            VFATWriteCluster(DeviceExt,Buffer,CurrentCluster);
-            CurrentCluster = GetNextCluster(DeviceExt, CurrentCluster);
-         }
-       Buffer = Buffer + DeviceExt->BytesPerCluster;
-       Length2 -= DeviceExt->BytesPerCluster;
-     }
-   CHECKPOINT;
-   
-   /* Write the remainder */
-   
-   if (Length2 > 0)
-     {
-       CHECKPOINT;
-       if (CurrentCluster == 0)
-         {
-            ExFreePool(Temp);
-            return(STATUS_UNSUCCESSFUL);
-         }
-       CHECKPOINT;
-       /* Read in the existing cluster data */
-       if (FirstCluster==1)
-         {
-            VFATReadSectors(DeviceExt->StorageDevice,
-                            CurrentCluster,
-                            DeviceExt->Boot->SectorsPerCluster,
-                            Temp);
-         }
-       else
-         {
-            VFATLoadCluster(DeviceExt,Temp,CurrentCluster);
-            CHECKPOINT;
-            memcpy(Temp, Buffer, Length2);
-            CHECKPOINT;
-            if (FirstCluster==1)
-              {
-                 VFATWriteSectors(DeviceExt->StorageDevice,
-                                  CurrentCluster,
-                                  DeviceExt->Boot->SectorsPerCluster,
-                                  Temp);
-              }
-            else
-              {
-                 VFATWriteCluster(DeviceExt,Temp,CurrentCluster);
-              }
-         }
-       CHECKPOINT;
-     }
-   
-
-   /* set dates and times */
-   KeQuerySystemTime (&SystemTime);
-   ExSystemTimeToLocalTime (&SystemTime,
-                            &LocalTime);
-   FsdFileTimeToDosDateTime ((TIME*)&LocalTime,
-                             &Fcb->entry.UpdateDate,
-                             &Fcb->entry.UpdateTime);
-   Fcb->entry.AccessDate = Fcb->entry.UpdateDate;
-
-   if (Fcb->entry.FileSize < WriteOffset+Length)
-     {
-       Fcb->entry.FileSize = WriteOffset+Length;
-       /*
-        * update entry in directory
-        */
-       updEntry(DeviceExt,FileObject);
-     }
-   
-   ExFreePool(Temp);
-   return (STATUS_SUCCESS);
+  ULONG CurrentCluster;
+  ULONG FirstCluster;
+  ULONG StartCluster;
+  ULONG Count;
+  PVFATFCB Fcb;
+  PVFATCCB pCcb;
+  ULONG TempLength;
+  LARGE_INTEGER SystemTime, LocalTime;
+  NTSTATUS Status;
+  BOOLEAN Extend;
+  LARGE_INTEGER FileOffset;
+
+  DPRINT ("VfatWriteFile(FileObject %x, Buffer %x, Length %x, "
+             "WriteOffset %x\n", FileObject, Buffer, Length, WriteOffset);
+
+  assert (FileObject);
+  pCcb = (PVFATCCB) (FileObject->FsContext2);
+  assert (pCcb);
+  Fcb = pCcb->pFcb;
+  assert (Fcb);
+
+//  DPRINT1("%S\n", Fcb->PathName);
+
+  if (Length == 0)
+  {
+    return STATUS_SUCCESS;
+  }
+
+  // Is this a write to the FAT ?
+  if (Fcb->Flags & FCB_IS_FAT)
+  {
+    if (!NoCache && !PageIo)
+    {
+      DbgPrint ("Cached FAT write outside from VFATFS.SYS\n");
+      KeBugCheck (0);
+    }
+    if (WriteOffset >= Fcb->RFCB.FileSize.QuadPart || WriteOffset % BLOCKSIZE != 0 || Length % BLOCKSIZE != 0)
+    {
+      DbgPrint ("Start or end of FAT write is not on a sector boundary\n");
+      KeBugCheck (0);
+    }
+    if (WriteOffset + Length > (ULONG)Fcb->RFCB.FileSize.QuadPart)
+    {
+      Length = (ULONG)Fcb->RFCB.FileSize.QuadPart - WriteOffset;
+    }
+
+    for (Count = 0; Count < DeviceExt->FatInfo.FATCount; Count++)
+    {
+      Status = VfatWriteSectors(DeviceExt->StorageDevice,
+                 DeviceExt->FatInfo.FATStart + (Count * (ULONG)Fcb->RFCB.FileSize.QuadPart + WriteOffset) / BLOCKSIZE,
+                 Length / BLOCKSIZE, Buffer);
+      if (!NT_SUCCESS(Status))
+      {
+        DPRINT1("FAT writing failed, Status %x\n", Status);
+      }
+    }
+    return Status;
+  }
+
+  /* Locate the first cluster of the file */
+  FirstCluster = CurrentCluster = vfatDirEntryGetFirstCluster (DeviceExt, &Fcb->entry);
+
+  if (PageIo)
+  {
+    if (FirstCluster == 0)
+    {
+      return STATUS_UNSUCCESSFUL;
+    }
+  }
+  else
+  {
+    if (FirstCluster == 1)
+    {
+      // root directory of FAT12 od FAT16
+      if (WriteOffset + Length > DeviceExt->FatInfo.rootDirectorySectors * BLOCKSIZE)
+      {
+        DPRINT("Writing over the end of the root directory on FAT12/16\n");
+        return STATUS_END_OF_FILE;
+      }
+    }
+
+    Status = vfatExtendSpace(DeviceExt, FileObject, WriteOffset + Length);
+    if (!NT_SUCCESS (Status))
+    {
+      return Status;
+    }
+  }
+
+  if (NoCache || PageIo || Fcb->Flags & FCB_IS_PAGE_FILE)
+  {
+
+    FirstCluster = CurrentCluster = vfatDirEntryGetFirstCluster (DeviceExt, &Fcb->entry);
+    if (pCcb->LastCluster > 0 && WriteOffset > pCcb->LastOffset)
+    {
+      CurrentCluster = pCcb->LastCluster;
+    }
+    Status = OffsetToCluster(DeviceExt,
+                            Fcb,
+                          FirstCluster,
+                          ROUND_DOWN(WriteOffset, DeviceExt->FatInfo.BytesPerCluster),
+                          &CurrentCluster,
+                          FALSE);
+    if (!NT_SUCCESS(Status) || CurrentCluster == 0xffffffff)
+    {
+      DPRINT1("????\n");
+      return(Status);
+    }
+    pCcb->LastCluster = CurrentCluster;
+    pCcb->LastOffset = ROUND_DOWN(WriteOffset, DeviceExt->FatInfo.BytesPerCluster);
+
+    /*
+     * If the offset in the cluster doesn't fall on the cluster boundary
+     * then we have to write only from the specified offset
+     */
+    Status = STATUS_SUCCESS;
+    if ((WriteOffset % DeviceExt->FatInfo.BytesPerCluster) != 0)
+    {
+      TempLength = min (Length, DeviceExt->FatInfo.BytesPerCluster 
+             - (WriteOffset % DeviceExt->FatInfo.BytesPerCluster));
+      Status = VfatWriteCluster(DeviceExt,
+                               Fcb,
+                             ROUND_DOWN(WriteOffset, DeviceExt->FatInfo.BytesPerCluster),
+                             FirstCluster,
+                             &CurrentCluster,
+                             Buffer,
+                             WriteOffset % DeviceExt->FatInfo.BytesPerCluster,
+                             TempLength);
+      if (NT_SUCCESS(Status))
+      {
+        Buffer = Buffer + TempLength;
+        Length = Length - TempLength;
+        WriteOffset = WriteOffset + TempLength;
+      }
+    }
+
+    while (Length >= DeviceExt->FatInfo.BytesPerCluster && 
+           CurrentCluster != 0xffffffff && NT_SUCCESS(Status))
+    {
+      StartCluster = CurrentCluster;
+      Count = 0;
+      // search for continous clusters
+      do
+      {
+        Count++;
+        Status = NextCluster(DeviceExt, Fcb, FirstCluster, &CurrentCluster, FALSE);
+      }
+      while (StartCluster + Count == CurrentCluster && NT_SUCCESS(Status) &&
+        Length - Count * DeviceExt->FatInfo.BytesPerCluster >= DeviceExt->FatInfo.BytesPerCluster);
+
+      pCcb->LastCluster = StartCluster + (Count - 1);
+      pCcb->LastOffset = WriteOffset + (Count - 1) * DeviceExt->FatInfo.BytesPerCluster;
+
+      Status = VfatRawWriteCluster(DeviceExt, FirstCluster, Buffer, StartCluster, Count);
+      if (NT_SUCCESS(Status))
+      {
+        Count *=  DeviceExt->FatInfo.BytesPerCluster;
+        Buffer += Count;
+        Length -= Count;
+        WriteOffset += Count;
+      }
+    }
+
+    /* Write the remainder */
+    if (Length > 0 && CurrentCluster != 0xffffffff && NT_SUCCESS(Status))
+    {
+      Status = VfatWriteCluster(DeviceExt,
+                               Fcb,
+                            WriteOffset,
+                            FirstCluster,
+                            &CurrentCluster,
+                            Buffer,
+                            0,
+                        Length);
+      if (NT_SUCCESS(Status))
+      {
+        Length = 0;
+      }
+    }
+    if (NT_SUCCESS(Status) && Length)
+    {
+      if (WriteOffset < Fcb->RFCB.AllocationSize.QuadPart)
+      {
+        DPRINT1("%d %d\n", WriteOffset, (ULONG)Fcb->RFCB.AllocationSize.QuadPart);
+        Status = STATUS_DISK_FULL; // ???????????
+      }
+    }
+  }
+  else
+  {
+    // using the Cc-interface if possible
+    FileOffset.QuadPart = WriteOffset;
+    if(CcCopyWrite(FileObject, &FileOffset, Length, TRUE, Buffer))
+    {
+      Status = STATUS_SUCCESS;
+    }
+    else
+    {
+      Status = STATUS_UNSUCCESSFUL;
+    }
+  }
+
+
+  if (!PageIo)
+  {
+    if(!(Fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY))
+    {
+      /* set dates and times */
+      KeQuerySystemTime (&SystemTime);
+      ExSystemTimeToLocalTime (&SystemTime, &LocalTime);
+      FsdFileTimeToDosDateTime ((TIME*)&LocalTime,
+                       &Fcb->entry.UpdateDate,
+                       &Fcb->entry.UpdateTime);
+      Fcb->entry.AccessDate = Fcb->entry.UpdateDate;
+      // update dates/times and length
+      updEntry (DeviceExt, FileObject);
+    }
+  }
+
+  return Status;
 }
 
-NTSTATUS FsdWrite(PDEVICE_OBJECT DeviceObject, PIRP Irp)
-/*
- * FUNCTION: Write to a file
- */
+NTSTATUS vfatExtendSpace (PDEVICE_EXTENSION pDeviceExt, PFILE_OBJECT pFileObject, ULONG NewSize)
 {
-   ULONG Length;
-   PVOID Buffer;
-   ULONG Offset;
-   PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
-   PFILE_OBJECT FileObject = Stack->FileObject;
-   PDEVICE_EXTENSION DeviceExt = DeviceObject->DeviceExtension;
-   NTSTATUS Status;
-   
-   DPRINT("FsdWrite(DeviceObject %x Irp %x)\n",DeviceObject,Irp);
+  ULONG FirstCluster;
+  ULONG CurrentCluster;
+  ULONG NewCluster;
+  NTSTATUS Status;
+  PVFATFCB pFcb;
+
 
-   Length = Stack->Parameters.Write.Length;
-   Buffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
-   Offset = Stack->Parameters.Write.ByteOffset.u.LowPart;
+  pFcb = ((PVFATCCB) (pFileObject->FsContext2))->pFcb;
 
-   Status = FsdWriteFile(DeviceExt,FileObject,Buffer,Length,Offset);
+  DPRINT ("New Size %d,  AllocationSize %d,  BytesPerCluster %d\n",  NewSize,
+    (ULONG)pFcb->RFCB.AllocationSize.QuadPart, pDeviceExt->FatInfo.BytesPerCluster);
 
-   Irp->IoStatus.Status = Status;
-   Irp->IoStatus.Information = Length;
-   IoCompleteRequest(Irp,IO_NO_INCREMENT);
+  FirstCluster = CurrentCluster = vfatDirEntryGetFirstCluster (pDeviceExt, &pFcb->entry);
 
-   return(Status);
+  if (NewSize > pFcb->RFCB.AllocationSize.QuadPart || FirstCluster==0)
+  {
+    // size on disk must be extended
+    if (FirstCluster == 0)
+    {
+      // file of size zero
+      Status = NextCluster (pDeviceExt, pFcb, FirstCluster, &CurrentCluster, TRUE);
+      if (!NT_SUCCESS(Status))
+      {
+        DPRINT1("NextCluster failed, Status %x\n", Status);
+        return Status;
+      }
+      NewCluster = FirstCluster = CurrentCluster;
+    }
+    else
+    {
+      Status = OffsetToCluster(pDeviceExt, pFcb, FirstCluster,
+                 pFcb->RFCB.AllocationSize.QuadPart - pDeviceExt->FatInfo.BytesPerCluster,
+                 &CurrentCluster, FALSE);
+      if (!NT_SUCCESS(Status))
+      {
+        DPRINT1("OffsetToCluster failed, Status %x\n", Status);
+        return Status;
+      }
+      if (CurrentCluster == 0xffffffff)
+      {
+        DPRINT1("Not enough disk space.\n");
+        return STATUS_DISK_FULL;
+      }
+      // CurrentCluster zeigt jetzt auf den letzten Cluster in der Kette
+      NewCluster = CurrentCluster;
+      Status = NextCluster(pDeviceExt, pFcb, FirstCluster, &NewCluster, FALSE);
+      if (NewCluster != 0xffffffff)
+      {
+        DPRINT1("Difference between size from direntry and the FAT.\n");
+      }
+    }
+
+    Status = OffsetToCluster(pDeviceExt, pFcb, FirstCluster,
+               ROUND_DOWN(NewSize-1, pDeviceExt->FatInfo.BytesPerCluster),
+               &NewCluster, TRUE);
+    if (!NT_SUCCESS(Status) || NewCluster == 0xffffffff)
+    {
+      DPRINT1("Not enough free space on disk\n");
+      if (pFcb->RFCB.AllocationSize.QuadPart > 0)
+      {
+        NewCluster = CurrentCluster;
+        // FIXME: check status
+        NextCluster(pDeviceExt, pFcb, FirstCluster, &NewCluster, FALSE);
+        WriteCluster(pDeviceExt, CurrentCluster, 0xffffffff);
+      }
+      // free the allocated space
+      while (NewCluster != 0xffffffff)
+      {
+        CurrentCluster = NewCluster;
+        // FIXME: check status
+        NextCluster (pDeviceExt, pFcb, FirstCluster, &NewCluster, FALSE);
+        WriteCluster (pDeviceExt, CurrentCluster, 0);
+      }
+      return STATUS_DISK_FULL;
+    }
+    if (pFcb->RFCB.AllocationSize.QuadPart == 0)
+    {
+      pFcb->entry.FirstCluster = FirstCluster;
+      if(pDeviceExt->FatInfo.FatType == FAT32)
+        pFcb->entry.FirstClusterHigh = FirstCluster >> 16;
+    }
+    pFcb->RFCB.AllocationSize.QuadPart = ROUND_UP(NewSize, pDeviceExt->FatInfo.BytesPerCluster);
+    if (pFcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY)
+    {
+      pFcb->RFCB.FileSize.QuadPart = pFcb->RFCB.AllocationSize.QuadPart;
+      pFcb->RFCB.ValidDataLength.QuadPart = pFcb->RFCB.AllocationSize.QuadPart;
+    }
+    else
+    {
+      pFcb->entry.FileSize = NewSize;
+      pFcb->RFCB.FileSize.QuadPart = NewSize;
+      pFcb->RFCB.ValidDataLength.QuadPart = NewSize;
+    }
+    CcSetFileSizes(pFileObject, (PCC_FILE_SIZES)&pFcb->RFCB.AllocationSize);
+  }
+  else
+  {
+    if (NewSize > pFcb->RFCB.FileSize.QuadPart)
+    {
+      // size on disk must not be extended
+      if (!(pFcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY))
+      {
+        pFcb->entry.FileSize = NewSize;
+        pFcb->RFCB.FileSize.QuadPart = NewSize;
+        CcSetFileSizes(pFileObject, (PCC_FILE_SIZES)&pFcb->RFCB.AllocationSize);
+      }
+    }
+    else
+    {
+      // nothing to do
+    }
+  }
+  return STATUS_SUCCESS;
 }
 
-NTSTATUS FsdRead(PDEVICE_OBJECT DeviceObject, PIRP Irp)
-/*
- * FUNCTION: Read from a file
- */
+NTSTATUS 
+VfatRead(PVFAT_IRP_CONTEXT IrpContext)
+{
+  PVFATFCB Fcb;
+  PVFATCCB Ccb;
+  NTSTATUS Status = STATUS_SUCCESS;
+  ULONG ReadLength;
+  ULONG ReturnedReadLength = 0;
+  LARGE_INTEGER ReadOffset;
+  PVOID Buffer;
+  
+  DPRINT ("VfatRead(IrpContext %x)\n", IrpContext);
+  assert (IrpContext);
+  Ccb = (PVFATCCB) IrpContext->FileObject->FsContext2;
+  assert (Ccb);
+  Fcb = Ccb->pFcb;
+  assert (Fcb);
+  
+  if (IrpContext->Irp->Flags & IRP_PAGING_IO)
+    {
+      if (!ExAcquireResourceSharedLite(&Fcb->PagingIoResource, 
+                                      IrpContext->Flags & IRPCONTEXT_CANWAIT))
+      {
+       return VfatQueueRequest (IrpContext);
+      }
+    }
+  else
+    {
+      if (!ExAcquireResourceSharedLite(&Fcb->MainResource, 
+                                      IrpContext->Flags & IRPCONTEXT_CANWAIT))
+       {
+         return VfatQueueRequest (IrpContext);
+       }
+   }
+  
+  ReadLength = IrpContext->Stack->Parameters.Read.Length;
+  ReadOffset = IrpContext->Stack->Parameters.Read.ByteOffset;
+  Buffer = MmGetSystemAddressForMdl (IrpContext->Irp->MdlAddress);
+  
+  /* fail if file is a directory and no paged read */
+  if (Fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY && 
+      !(IrpContext->Irp->Flags & IRP_PAGING_IO))
+   {
+     Status = STATUS_FILE_IS_A_DIRECTORY;
+   }
+  else
+    {
+      BOOLEAN NoCache;
+      NoCache = IrpContext->FileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING
+       || IrpContext->Irp->Flags & IRP_PAGING_IO;
+      Status = VfatReadFile (IrpContext->DeviceExt, IrpContext->FileObject,
+                            Buffer, ReadLength, ReadOffset.u.LowPart, 
+                            &ReturnedReadLength,
+                            NoCache);
+    }
+
+  if (IrpContext->Irp->Flags & IRP_PAGING_IO)
+    {
+      ExReleaseResourceLite(&Fcb->PagingIoResource);
+    }
+  else
+    {
+      ExReleaseResourceLite(&Fcb->MainResource);
+    }
+  
+  if (NT_SUCCESS(Status))
+   {
+     if (IrpContext->FileObject->Flags & FO_SYNCHRONOUS_IO && 
+        !(IrpContext->Irp->Flags & IRP_PAGING_IO))
+      {
+       IrpContext->FileObject->CurrentByteOffset.QuadPart = 
+         ReadOffset.QuadPart + ReturnedReadLength;
+      }
+     IrpContext->Irp->IoStatus.Information = ReturnedReadLength;
+   }
+  else
+    {
+      IrpContext->Irp->IoStatus.Information = 0;
+    }
+
+  IrpContext->Irp->IoStatus.Status = Status;
+  IoCompleteRequest (IrpContext->Irp, IO_NO_INCREMENT);
+  VfatFreeIrpContext (IrpContext);
+  
+  return Status;
+}
+
+NTSTATUS VfatWrite(PVFAT_IRP_CONTEXT IrpContext)
 {
-   ULONG Length;
-   PVOID Buffer;
-   ULONG Offset;
-   PIO_STACK_LOCATION Stack;
-   PFILE_OBJECT FileObject;
-   PDEVICE_EXTENSION DeviceExt;
-   NTSTATUS Status;
-   ULONG LengthRead;
    PVFATFCB Fcb;
+   PVFATCCB Ccb;
+   NTSTATUS Status = STATUS_SUCCESS;
+   ULONG WriteLength;
+   LARGE_INTEGER WriteOffset;
+   PVOID Buffer;
 
-   DPRINT("FsdRead(DeviceObject %x, Irp %x)\n",DeviceObject,Irp);
+   DPRINT ("VfatWrite(), %S\n", ((PVFATCCB) IrpContext->FileObject->FsContext2)->pFcb->FileName);
+   assert (IrpContext);
+   Ccb = (PVFATCCB) IrpContext->FileObject->FsContext2;
+   assert (Ccb);
+   Fcb = Ccb->pFcb;
+   assert (Fcb);
 
-   /* Precondition / Initialization */
-   assert(Irp != NULL);
-   Stack = IoGetCurrentIrpStackLocation(Irp);
-   assert(Stack != NULL);
-   FileObject = Stack->FileObject;
-   assert(FileObject != NULL);
-   DeviceExt = DeviceObject->DeviceExtension;
-   assert(DeviceExt != NULL);
+   if (IrpContext->Irp->Flags & IRP_PAGING_IO)
+   {
+      if (!ExAcquireResourceExclusiveLite(&Fcb->PagingIoResource, IrpContext->Flags & IRPCONTEXT_CANWAIT))
+      {
+         return VfatQueueRequest (IrpContext);
+      }
+   }
+   else
+   {
+      if (!ExAcquireResourceExclusiveLite(&Fcb->MainResource, IrpContext->Flags & IRPCONTEXT_CANWAIT))
+      {
+         return VfatQueueRequest (IrpContext);
+      }
+   }
 
-   Length = Stack->Parameters.Read.Length;
-   Buffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
-   Offset = Stack->Parameters.Read.ByteOffset.u.LowPart;
+   WriteLength = IrpContext->Stack->Parameters.Write.Length;
+   WriteOffset = IrpContext->Stack->Parameters.Write.ByteOffset;
+   Buffer = MmGetSystemAddressForMdl (IrpContext->Irp->MdlAddress);
 
-   /* fail if file is a directory */
-   Fcb = ((PVFATCCB)(FileObject->FsContext2))->pFcb;
-   if (Fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY)
-     {
-       Status = STATUS_FILE_IS_A_DIRECTORY;
-     }
+   /* fail if file is a directory and no paged read */
+   if (Fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY && !(IrpContext->Irp->Flags & IRP_PAGING_IO))
+   {
+      Status = STATUS_FILE_IS_A_DIRECTORY;
+   }
    else
-     {
-       Status = FsdReadFile(DeviceExt,
-                            FileObject,
-                            Buffer,
-                            Length,
-                            Offset,
-                            &LengthRead);
-    }
+   {
+      Status = VfatWriteFile (IrpContext->DeviceExt, IrpContext->FileObject,
+                 Buffer, WriteLength, WriteOffset.u.LowPart,
+                 IrpContext->FileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING,
+                  IrpContext->Irp->Flags & IRP_PAGING_IO);
+   }
 
-   Irp->IoStatus.Status = Status;
-   Irp->IoStatus.Information = LengthRead;
-   IoCompleteRequest(Irp,IO_NO_INCREMENT);
+   if (IrpContext->Irp->Flags & IRP_PAGING_IO)
+   {
+      ExReleaseResourceLite(&Fcb->PagingIoResource);
+   }
+   else
+   {
+      ExReleaseResourceLite(&Fcb->MainResource);
+   }
+
+   if (NT_SUCCESS(Status))
+   {
+      if (IrpContext->FileObject->Flags & FO_SYNCHRONOUS_IO && !(IrpContext->Irp->Flags & IRP_PAGING_IO))
+      {
+         IrpContext->FileObject->CurrentByteOffset.QuadPart = WriteOffset.QuadPart + WriteLength;
+      }
+      IrpContext->Irp->IoStatus.Information = WriteLength;
+   }
+   else
+   {
+      IrpContext->Irp->IoStatus.Information = 0;
+   }
 
-   return(Status);
+   IrpContext->Irp->IoStatus.Status = Status;
+   IoCompleteRequest (IrpContext->Irp, IO_NO_INCREMENT);
+   VfatFreeIrpContext (IrpContext);
+   return Status;
 }