Rewritten the read and write functions.
authorHartmut Birr <osexpert@googlemail.com>
Wed, 10 Oct 2001 22:19:51 +0000 (22:19 +0000)
committerHartmut Birr <osexpert@googlemail.com>
Wed, 10 Oct 2001 22:19:51 +0000 (22:19 +0000)
Changed the cache access from vfat..Region to Cc...Data() and CcCopy...().

svn path=/trunk/; revision=2290

reactos/drivers/fs/vfat/rw.c

index 352042d..e5661e4 100644 (file)
@@ -1,5 +1,5 @@
 
-/* $Id: rw.c,v 1.31 2001/08/14 20:47:30 hbirr Exp $
+/* $Id: rw.c,v 1.32 2001/10/10 22:19:51 hbirr Exp $
  *
  * COPYRIGHT:        See COPYING in the top level directory
  * PROJECT:          ReactOS kernel
@@ -63,8 +63,8 @@ NextCluster(PDEVICE_EXTENSION DeviceExt,
 }
 
 NTSTATUS
-OffsetToCluster(PDEVICE_EXTENSION DeviceExt, 
-               ULONG FirstCluster, 
+OffsetToCluster(PDEVICE_EXTENSION DeviceExt,
+               ULONG FirstCluster,
                ULONG FileOffset,
                PULONG Cluster,
                BOOLEAN Extend)
@@ -78,7 +78,7 @@ OffsetToCluster(PDEVICE_EXTENSION DeviceExt,
   NTSTATUS Status;
 
   if (FirstCluster == 1)
-    {                          
+    {
       /* root of FAT16 or FAT12 */
       *Cluster = DeviceExt->rootStart + FileOffset
        / (DeviceExt->BytesPerCluster) * DeviceExt->Boot->SectorsPerCluster;
@@ -102,249 +102,39 @@ OffsetToCluster(PDEVICE_EXTENSION DeviceExt,
 }
 
 NTSTATUS
-VfatReadBigCluster(PDEVICE_EXTENSION DeviceExt,
-               PVFATFCB Fcb,
-               ULONG StartOffset,
+VfatReadCluster(PDEVICE_EXTENSION DeviceExt,
                ULONG FirstCluster,
                PULONG CurrentCluster,
                PVOID Destination,
-               ULONG NoCache,
                ULONG InternalOffset,
                ULONG InternalLength)
 {
-  BOOLEAN Valid;
   PVOID BaseAddress = NULL;
-  PCACHE_SEGMENT CacheSeg = NULL;
   NTSTATUS Status;
 
-  /*
-   * In this case the size of a cache segment is the same as a cluster
-   */
-
-  if (!NoCache)
+  if (InternalLength == DeviceExt->BytesPerCluster)
   {
-     Status = CcRosRequestCacheSegment(Fcb->RFCB.Bcb,
-                                      StartOffset,
-                                      &BaseAddress,
-                                      &Valid,
-                                      &CacheSeg);
-    if (!NT_SUCCESS(Status))
-    {
-        return(Status);
-    }
+    Status = VfatRawReadCluster(DeviceExt, FirstCluster,
+                                Destination, *CurrentCluster, 1);
   }
   else
   {
-    Valid = FALSE;
-    if (InternalLength == DeviceExt->BytesPerCluster)
+    BaseAddress = ExAllocatePool(NonPagedPool, DeviceExt->BytesPerCluster);
+    if (BaseAddress == NULL)
     {
-      BaseAddress = Destination;
+      return(STATUS_NO_MEMORY);
     }
-    else
-    {
-      BaseAddress = ExAllocatePool(NonPagedPool, DeviceExt->BytesPerCluster);
-      if (BaseAddress == NULL)
-      {
-        return(STATUS_NO_MEMORY);
-      }
-    }
-  }
-
-  if (!Valid)
-  {
-    /*
-     * If necessary read the cluster from the disk
-     */
-    Status = VfatRawReadCluster(DeviceExt, FirstCluster, BaseAddress,
-                               *CurrentCluster);
-    if (!NT_SUCCESS(Status))
-    { 
-       if (!NoCache)
-       {
-         CcRosReleaseCacheSegment(Fcb->RFCB.Bcb, CacheSeg, FALSE);
-       }
-       else if (InternalLength != DeviceExt->BytesPerCluster)
-       {
-         ExFreePool(BaseAddress);
-       }
-       return(Status);
-    }
-  }
-  /*
-   * Copy the data from the cache to the caller
-   */
-  if (InternalLength != DeviceExt->BytesPerCluster || !NoCache)
-  {
+    Status = VfatRawReadCluster(DeviceExt, FirstCluster,
+                                BaseAddress, *CurrentCluster, 1);
     memcpy(Destination, BaseAddress + InternalOffset, InternalLength);
-  }
-  if (!NoCache)
-  {
-    CcRosReleaseCacheSegment(Fcb->RFCB.Bcb, CacheSeg, TRUE);
-  }
-  else if (InternalLength != DeviceExt->BytesPerCluster)
-  {
     ExFreePool(BaseAddress);
   }
-
-  Status = NextCluster(DeviceExt, FirstCluster, CurrentCluster, FALSE);
   if (!NT_SUCCESS(Status))
   {
-    return(Status);
-  }
-  return(STATUS_SUCCESS);
-}
-
-NTSTATUS
-VfatReadSmallCluster(PDEVICE_EXTENSION DeviceExt,
-                    PVFATFCB Fcb,
-                    ULONG StartOffset,
-                    ULONG FirstCluster,
-                    PULONG CurrentCluster,
-                    PVOID Destination,
-                    ULONG NoCache,
-                    ULONG InternalOffset,
-                    ULONG InternalLength)
-{
-  BOOLEAN Valid;
-  PVOID BaseAddress = NULL;
-  PCACHE_SEGMENT CacheSeg = NULL;
-  NTSTATUS Status;
-  ULONG i;
-
-  /*
-   * Otherwise we read a page of clusters together
-   */
-  if (!NoCache)
-  {
-    Status = CcRosRequestCacheSegment(Fcb->RFCB.Bcb,
-                                     StartOffset,
-                                     &BaseAddress,
-                                     &Valid,
-                                     &CacheSeg);
-    if (!NT_SUCCESS(Status))
-    {
-      return(Status);
-    }
-  }
-  else
-  {
-    Valid = FALSE;
-    if (InternalLength == PAGESIZE)
-    {
-      BaseAddress = Destination;
-    }
-    else
-    {
-       BaseAddress = ExAllocatePool(NonPagedPool, PAGESIZE);
-       if (BaseAddress == NULL)
-       {
-         return(STATUS_NO_MEMORY);
-       }
-    }
-  }
-
-  /*
-   * If necessary read all the data for the page, unfortunately the
-   * file length may not be page aligned in which case the page will
-   * only be partially filled.
-   * FIXME: So zero fill the rest?
-   */
-  if (!Valid)
-  {
-    for (i = 0; i < (PAGESIZE / DeviceExt->BytesPerCluster); i++)
-    {
-      Status = VfatRawReadCluster(DeviceExt,
-                                 FirstCluster,
-                                 BaseAddress +
-                                 (i * DeviceExt->BytesPerCluster),
-                                 *CurrentCluster);
-      if (!NT_SUCCESS(Status))
-      {
-        if (!NoCache)
-        {
-          CcRosReleaseCacheSegment(Fcb->RFCB.Bcb, CacheSeg, FALSE);
-        }
-       else if (InternalLength != PAGESIZE)
-        {
-         ExFreePool(BaseAddress);
-        }
-       return(Status);
-      }
-      Status = NextCluster(DeviceExt, FirstCluster, CurrentCluster, FALSE);
-      if ((*CurrentCluster) == 0xFFFFFFFF)
-      {
-       break;
-      }
-    }
-  }
-  else
-  {
-    /*
-     * Otherwise just move onto the next cluster
-     */
-    for (i = 0; i < (PAGESIZE / DeviceExt->BytesPerCluster); i++)
-    {
-      NextCluster(DeviceExt, FirstCluster, CurrentCluster, FALSE);
-      if ((*CurrentCluster) == 0xFFFFFFFF)
-      {
-        break;
-      }
-    }
-  }
-  /*
-   * Copy the data from the cache to the caller
-   */
-  if (InternalLength != PAGESIZE || !NoCache)
-  {
-    memcpy(Destination, BaseAddress + InternalOffset, InternalLength);
-  }
-  if (!NoCache)
-  {
-    CcRosReleaseCacheSegment(Fcb->RFCB.Bcb, CacheSeg,
-                             Valid || InternalLength == PAGESIZE ? TRUE : FALSE);
-  }
-  else if (InternalLength != PAGESIZE)
-  {
-    ExFreePool(BaseAddress);
-  }
-  return(STATUS_SUCCESS);
-}
-
-NTSTATUS
-VfatReadCluster(PDEVICE_EXTENSION DeviceExt,
-               PVFATFCB Fcb,
-               ULONG StartOffset,
-               ULONG FirstCluster,
-               PULONG CurrentCluster,
-               PVOID Destination,
-               ULONG NoCache,
-               ULONG InternalOffset,
-               ULONG InternalLength)
-{
-  if (DeviceExt->BytesPerCluster >= PAGESIZE)
-  {
-    return(VfatReadBigCluster(DeviceExt,
-                              Fcb,
-                             StartOffset,
-                             FirstCluster,
-                             CurrentCluster,
-                             Destination,
-                             NoCache,
-                             InternalOffset,
-                             InternalLength));
-  }
-  else
-  {
-    return(VfatReadSmallCluster(DeviceExt,
-                               Fcb,
-                               StartOffset,
-                               FirstCluster,
-                               CurrentCluster,
-                               Destination,
-                               NoCache,
-                               InternalOffset,
-                               InternalLength));
+     return(Status);
   }
+  Status = NextCluster(DeviceExt, FirstCluster, CurrentCluster, FALSE);
+  return(Status);
 }
 
 NTSTATUS
@@ -357,10 +147,14 @@ VfatReadFile (PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject,
 {
   ULONG CurrentCluster;
   ULONG FirstCluster;
+  ULONG StartCluster;
+  ULONG ClusterCount;
   PVFATFCB Fcb;
-  ULONG ChunkSize;
+  PVFATCCB Ccb;
   NTSTATUS Status;
   ULONG TempLength;
+  LARGE_INTEGER FileOffset;
+  IO_STATUS_BLOCK IoStatus;
 
   /* PRECONDITION */
   assert (DeviceExt != NULL);
@@ -369,23 +163,50 @@ VfatReadFile (PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject,
   assert (FileObject->FsContext2 != NULL);
 
   DPRINT("VfatReadFile(DeviceExt %x, FileObject %x, Buffer %x, "
-        "Length %d, ReadOffset 0x%x)\n", DeviceExt, FileObject, Buffer,
-        Length, ReadOffset);
+            "Length %d, ReadOffset 0x%x)\n", DeviceExt, FileObject, Buffer,
+            Length, ReadOffset);
 
   *LengthRead = 0;
 
-  Fcb = ((PVFATCCB)FileObject->FsContext2)->pFcb;
+  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->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
    */
-  if (DeviceExt->FatType == FAT32)
-    CurrentCluster = Fcb->entry.FirstCluster
-      + Fcb->entry.FirstClusterHigh * 65536;
-  else
-    CurrentCluster = Fcb->entry.FirstCluster;
-  FirstCluster = CurrentCluster;
-  DPRINT( "FirstCluster = %x\n", FirstCluster );
+  FirstCluster = CurrentCluster = vfatDirEntryGetFirstCluster (DeviceExt, &Fcb->entry);
+
   /*
    * Truncate the read if necessary
    */
@@ -410,43 +231,42 @@ VfatReadFile (PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject,
     }
   }
 
-  /*
-   * Select an appropiate size for reads
-   */
-  if (DeviceExt->BytesPerCluster >= PAGESIZE)
-  {
-    ChunkSize = DeviceExt->BytesPerCluster;
-  }
-  else
+  // using the Cc-interface if possible
+  if (!NoCache)
   {
-    ChunkSize = PAGESIZE;
+    FileOffset.QuadPart = ReadOffset;
+    CcCopyRead(FileObject, &FileOffset, Length, TRUE, Buffer, &IoStatus);
+    *LengthRead = IoStatus.Information;
+    return IoStatus.Status;
   }
 
   /*
    * Find the cluster to start the read from
-   * FIXME: Optimize by remembering the last cluster read and using if
-   * possible.
    */
+  if (Ccb->LastCluster > 0 && ReadOffset > Ccb->LastOffset)
+  {
+    CurrentCluster = Ccb->LastCluster;
+  }
   Status = OffsetToCluster(DeviceExt,
                           FirstCluster,
-                          ROUND_DOWN(ReadOffset, ChunkSize),
+                          ROUND_DOWN(ReadOffset, DeviceExt->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 % ChunkSize) != 0 )
+  if ((ReadOffset % DeviceExt->BytesPerCluster) != 0 )
   {
-    TempLength = min (Length, ChunkSize - (ReadOffset % ChunkSize));
-    Status = VfatReadCluster(DeviceExt, Fcb, ROUND_DOWN(ReadOffset, ChunkSize),
-                            FirstCluster, &CurrentCluster, Buffer, NoCache,
-                            ReadOffset % ChunkSize, TempLength);
+    TempLength = min (Length, DeviceExt->BytesPerCluster - (ReadOffset % DeviceExt->BytesPerCluster));
+    Ccb->LastCluster = CurrentCluster;
+    Ccb->LastOffset = ROUND_DOWN(ReadOffset, DeviceExt->BytesPerCluster);
+    Status = VfatReadCluster(DeviceExt, FirstCluster, &CurrentCluster, Buffer,
+               ReadOffset % DeviceExt->BytesPerCluster, TempLength);
     if (NT_SUCCESS(Status))
     {
       (*LengthRead) = (*LengthRead) + TempLength;
@@ -456,24 +276,43 @@ VfatReadFile (PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject,
     }
   }
 
-  while (Length >= ChunkSize && CurrentCluster != 0xffffffff && NT_SUCCESS(Status))
+  while (Length >= DeviceExt->BytesPerCluster && CurrentCluster != 0xffffffff && NT_SUCCESS(Status))
   {
-    Status = VfatReadCluster(DeviceExt, Fcb, ReadOffset,
-                            FirstCluster, &CurrentCluster, Buffer, NoCache, 0,
-                            ChunkSize);
+    StartCluster = CurrentCluster;
+    ClusterCount = 0;
+    // search for continous clusters
+    do
+    {
+      ClusterCount++;
+      Status = NextCluster(DeviceExt, FirstCluster, &CurrentCluster, FALSE);
+    }
+    while (StartCluster + ClusterCount == CurrentCluster && NT_SUCCESS(Status) &&
+       Length - ClusterCount * DeviceExt->BytesPerCluster >= DeviceExt->BytesPerCluster);
+    DPRINT("Count %d, Start %x Next %x\n", ClusterCount, StartCluster, CurrentCluster);
+    Ccb->LastCluster = StartCluster + (ClusterCount - 1);
+    Ccb->LastOffset = ReadOffset + (ClusterCount - 1) * DeviceExt->BytesPerCluster;
+
+    Status = VfatRawReadCluster(DeviceExt, FirstCluster, Buffer, StartCluster, ClusterCount);
     if (NT_SUCCESS(Status))
     {
-      (*LengthRead) = (*LengthRead) + ChunkSize;
-      Buffer = Buffer + ChunkSize;
-      Length = Length - ChunkSize;
-      ReadOffset = ReadOffset + ChunkSize;
+      ClusterCount *=  DeviceExt->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))
   {
-    Status = VfatReadCluster(DeviceExt, Fcb, ReadOffset,
-                            FirstCluster, &CurrentCluster, Buffer, NoCache, 0,
-                            Length);
+    Ccb->LastCluster = CurrentCluster;
+    Ccb->LastOffset = ReadOffset + DeviceExt->BytesPerCluster;
+
+    Status = VfatReadCluster(DeviceExt, FirstCluster, &CurrentCluster,
+                             Buffer, 0, Length);
     if (NT_SUCCESS(Status))
     {
       (*LengthRead) = (*LengthRead) + Length;
@@ -483,512 +322,286 @@ VfatReadFile (PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject,
 }
 
 NTSTATUS
-VfatWriteBigCluster(PDEVICE_EXTENSION DeviceExt,
-                   PVFATFCB Fcb,
+VfatWriteCluster(PDEVICE_EXTENSION DeviceExt,
                    ULONG StartOffset,
                    ULONG FirstCluster,
                    PULONG CurrentCluster,
                    PVOID Source,
-                   ULONG NoCache,
                    ULONG InternalOffset,
-                   ULONG InternalLength,
-                   BOOLEAN Extend)
+                   ULONG InternalLength)
 {
-  BOOLEAN Valid;
   PVOID BaseAddress;
-  PCACHE_SEGMENT CacheSeg;
   NTSTATUS Status;
 
-  /*
-   * In this case the size of a cache segment is the same as a cluster
-   */
-  if (!NoCache)
+  if (InternalLength != DeviceExt->BytesPerCluster)
   {
-    Status = CcRosRequestCacheSegment(Fcb->RFCB.Bcb,
-                                     StartOffset,
-                                     &BaseAddress,
-                                     &Valid,
-                                     &CacheSeg);
-    if (!NT_SUCCESS(Status))
-    {
-      return(Status);
-    }
+     BaseAddress = ExAllocatePool(NonPagedPool, DeviceExt->BytesPerCluster);
+     if (BaseAddress == NULL)
+     {
+       return(STATUS_NO_MEMORY);
+     }
   }
   else
-  {
-    Valid = FALSE;
-    /*
-     * If we are bypassing the cache and not writing starting on
-     * cluster boundary then allocate a temporary buffer
-     */
-    if (InternalLength != DeviceExt->BytesPerCluster)
-    {
-      BaseAddress = ExAllocatePool(NonPagedPool,
-                                  DeviceExt->BytesPerCluster);
-      if (BaseAddress == NULL)
-      {
-        return(STATUS_NO_MEMORY);
-      }
-    }
-  }
-  if (!Valid && InternalLength != DeviceExt->BytesPerCluster)
+    BaseAddress = Source;
+  if (InternalLength != DeviceExt->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);
-    if (!NT_SUCCESS(Status))
-    {
-      if (!NoCache)
-      {
-        CcRosReleaseCacheSegment(Fcb->RFCB.Bcb, CacheSeg, FALSE);
-      }
-      else if (InternalLength != DeviceExt->BytesPerCluster)
-      {
-        ExFreePool(BaseAddress);
-      }
-      return(Status);
-    }
-  }
-  if (!NoCache || InternalLength != DeviceExt->BytesPerCluster)
-  {
-    /*
-     * If we are writing into the cache or we are writing from a
-     * temporary buffer then copy the data over
-     */
-    DPRINT("InternalOffset 0x%x InternalLength 0x%x BA %x\n",
-           InternalOffset, InternalLength, BaseAddress);
-    memcpy(BaseAddress + InternalOffset, Source, InternalLength);
+     Status = VfatRawReadCluster(DeviceExt, FirstCluster, BaseAddress,
+                               *CurrentCluster, 1);
+     if (!NT_SUCCESS(Status))
+     {
+        if (InternalLength != DeviceExt->BytesPerCluster)
+        {
+          ExFreePool(BaseAddress);
+        }
+        return(Status);
+     }
+     memcpy(BaseAddress + InternalOffset, Source, InternalLength);
   }
   /*
    * Write the data back to disk
    */
   DPRINT("Writing 0x%x\n", *CurrentCluster);
   Status = VfatRawWriteCluster(DeviceExt, FirstCluster, BaseAddress,
-                              *CurrentCluster);
-  if (!NoCache)
-  {
-    CcRosReleaseCacheSegment(Fcb->RFCB.Bcb, CacheSeg, TRUE);
-  }
-  else if (InternalLength != DeviceExt->BytesPerCluster)
+                              *CurrentCluster, 1);
+  if (InternalLength != DeviceExt->BytesPerCluster)
   {
     ExFreePool(BaseAddress);
   }
-
-  Status = NextCluster(DeviceExt, FirstCluster, CurrentCluster, Extend);
   if (!NT_SUCCESS(Status))
   {
-    return(Status);
-  }
-  return(STATUS_SUCCESS);
-}
-
-NTSTATUS
-VfatWriteSmallCluster(PDEVICE_EXTENSION DeviceExt,
-                     PVFATFCB Fcb,
-                     ULONG StartOffset,
-                     ULONG FirstCluster,
-                     PULONG CurrentCluster,
-                     PVOID Source,
-                     ULONG NoCache,
-                     ULONG InternalOffset,
-                     ULONG InternalLength,
-                     BOOLEAN Extend)
-{
-  BOOLEAN Valid;
-  PVOID BaseAddress;
-  PCACHE_SEGMENT CacheSeg;
-  NTSTATUS Status;
-  ULONG NCluster;
-  ULONG i;
-
-  /*
-   * Otherwise we read a page of clusters together
-   */
-
-  if (!NoCache)
-  {
-    Status = CcRosRequestCacheSegment(Fcb->RFCB.Bcb,
-                                      StartOffset,
-                                      &BaseAddress,
-                                      &Valid,
-                                      &CacheSeg);
-    if (!NT_SUCCESS(Status))
-    {
-      return(Status);
-    }
-  }
-  else
-  {
-    Valid = FALSE;
-    if (InternalLength != PAGESIZE)
-    {
-      BaseAddress = ExAllocatePool(NonPagedPool, PAGESIZE);
-      if (BaseAddress == NULL)
-      {
-        return(STATUS_NO_MEMORY);
-      }
-    }
-    else
-    {
-      BaseAddress = Source;
-    }
-  }
-
-  /*
-   * If necessary read all the data for the page, unfortunately the
-   * file length may not be page aligned in which case the page will
-   * only be partially filled.
-   * FIXME: So zero fill the rest?
-   */
-  if (!Valid || InternalLength != PAGESIZE)
-  {
-    NCluster = *CurrentCluster;
-
-    for (i = 0; i < (PAGESIZE / DeviceExt->BytesPerCluster); i++)
-    {
-      Status = VfatRawReadCluster(DeviceExt,
-                                 FirstCluster,
-                                 BaseAddress +
-                                 (i * DeviceExt->BytesPerCluster),
-                                 NCluster);
-      if (!NT_SUCCESS(Status))
-      {
-        if (!NoCache)
-        {
-         CcRosReleaseCacheSegment(Fcb->RFCB.Bcb, CacheSeg, FALSE);
-        }
-       else if (InternalLength != PAGESIZE)
-        {
-         ExFreePool(BaseAddress);
-        }
-       return(Status);
-      }
-      Status = NextCluster(DeviceExt, FirstCluster, &NCluster, Extend);
-      if (NCluster == 0xFFFFFFFF)
-      {
-        break;
-      }
-    }
-  }
-  else
-  {
-    /*
-     * Otherwise just move onto the next cluster
-     */
-    NCluster = *CurrentCluster;
-    for (i = 0; i < (PAGESIZE / DeviceExt->BytesPerCluster); i++)
-    {
-      NextCluster(DeviceExt, FirstCluster, &NCluster, Extend);
-      if (NCluster == 0xFFFFFFFF)
-      {
-        break;
-      }
-    }
-  }
-
-  if (!NoCache || InternalLength != PAGESIZE)
-  {
-    /*
-     * Copy the caller's data if we are using the cache or writing
-     * from temporary buffer
-     */
-    memcpy(BaseAddress + InternalOffset, Source, InternalLength);
-  }
-
-  /*
-   * Write the data to the disk
-   */
-  NCluster = *CurrentCluster;
-
-  for (i = 0; i < (PAGESIZE / DeviceExt->BytesPerCluster); i++)
-  {
-    Status = VfatRawWriteCluster(DeviceExt,
-                                FirstCluster,
-                                BaseAddress +
-                                (i * DeviceExt->BytesPerCluster),
-                                NCluster);
-    if (!NT_SUCCESS(Status))
-    {
-      if (!NoCache)
-      {
-        CcRosReleaseCacheSegment(Fcb->RFCB.Bcb, CacheSeg, FALSE);
-      }
-      else if (InternalLength != PAGESIZE)
-      {
-       ExFreePool(BaseAddress);
-      }
-      return(Status);
-    }
-    // FIXME: Overwriting the variable Extend isn't correct,
-    //        but when VFatWriteFile writes the last cache segment,
-    //        than is Extend set to FALSE. We must extend,
-    //        when the data needs a next cluster.
-    if (i < (PAGESIZE / DeviceExt->BytesPerCluster)-1 &&
-      InternalOffset + InternalLength > (i+1) * DeviceExt->BytesPerCluster)
-    {
-      Extend = TRUE;
-    }
-    else
-    {
-      Extend = FALSE;
-    }
-    Status = NextCluster(DeviceExt, FirstCluster, &NCluster, Extend);
-    if (NCluster == 0xFFFFFFFF)
-    {
-      break;
-    }
-  }
-  *CurrentCluster = NCluster;
-
-  if (!NoCache)
-  {
-    //
-    CcRosReleaseCacheSegment(Fcb->RFCB.Bcb, CacheSeg,
-                             Valid || InternalLength == PAGESIZE ? TRUE : FALSE);
-  }
-  else if (InternalLength != PAGESIZE)
-  {
-    ExFreePool(BaseAddress);
-  }
-  return(STATUS_SUCCESS);
-}
-
-NTSTATUS
-VfatWriteCluster(PDEVICE_EXTENSION DeviceExt,
-                PVFATFCB Fcb,
-                ULONG StartOffset,
-                ULONG FirstCluster,
-                PULONG CurrentCluster,
-                PVOID Source,
-                ULONG NoCache,
-                ULONG InternalOffset,
-                ULONG InternalLength,
-                BOOLEAN Extend)
-{
-
-
-  if (DeviceExt->BytesPerCluster >= PAGESIZE)
-  {
-    return(VfatWriteBigCluster(DeviceExt,
-                              Fcb,
-                              StartOffset,
-                              FirstCluster,
-                              CurrentCluster,
-                              Source,
-                              NoCache,
-                              InternalOffset,
-                              InternalLength,
-                              Extend));
-  }
-  else
-  {
-    return(VfatWriteSmallCluster(DeviceExt,
-                                Fcb,
-                                StartOffset,
-                                FirstCluster,
-                                CurrentCluster,
-                                Source,
-                                NoCache,
-                                InternalOffset,
-                                InternalLength,
-                                Extend));
+    return Status;
   }
+  Status = NextCluster(DeviceExt, FirstCluster, CurrentCluster, FALSE);
+  return(Status);
 }
 
 NTSTATUS
 VfatWriteFile (PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject,
               PVOID Buffer, ULONG Length, ULONG WriteOffset,
-              ULONG NoCache)
+              BOOLEAN NoCache, BOOLEAN PageIo)
 /*
  * FUNCTION: Writes data to file
  */
 {
   ULONG CurrentCluster;
   ULONG FirstCluster;
+  ULONG StartCluster;
+  ULONG Count;
   PVFATFCB Fcb;
   PVFATCCB pCcb;
   ULONG TempLength;
   LARGE_INTEGER SystemTime, LocalTime;
-  ULONG ChunkSize;
   NTSTATUS Status;
   BOOLEAN Extend;
+  LARGE_INTEGER FileOffset;
 
   DPRINT ("VfatWriteFile(FileObject %x, Buffer %x, Length %x, "
-          "WriteOffset %x\n", FileObject, Buffer, Length, WriteOffset);
+             "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->BytesPerCluster >= PAGESIZE)
+//  DPRINT1("%S\n", Fcb->PathName);
+
+  if (Length == 0)
   {
-    ChunkSize = DeviceExt->BytesPerCluster;
+    return STATUS_SUCCESS;
   }
-  else
+
+  // Is this a write to the FAT ?
+  if (Fcb->Flags & FCB_IS_FAT)
   {
-    ChunkSize = PAGESIZE;
+    if (!NoCache)
+    {
+      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->Boot->FATCount; Count++)
+    {
+      Status = VfatWriteSectors(DeviceExt->StorageDevice,
+                 DeviceExt->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;
   }
 
-  if (DeviceExt->FatType == FAT32)
+  /* Locate the first cluster of the file */
+  FirstCluster = CurrentCluster = vfatDirEntryGetFirstCluster (DeviceExt, &Fcb->entry);
+
+  if (PageIo)
   {
-    CurrentCluster =
-      Fcb->entry.FirstCluster + Fcb->entry.FirstClusterHigh * 65536;
+    if (FirstCluster == 0)
+    {
+      return STATUS_UNSUCCESSFUL;
+    }
   }
   else
   {
-    CurrentCluster = Fcb->entry.FirstCluster;
-  }
-  FirstCluster = CurrentCluster;
-  
-  /* Find the cluster according to the offset in the file */
-  if (CurrentCluster == 0)
-  {
-    /*
-     * File of size zero
-     */
-    Status = NextCluster (DeviceExt, FirstCluster, &CurrentCluster, 
-                         TRUE);
-    if (DeviceExt->FatType == FAT32)
+    if (FirstCluster == 1)
     {
-      Fcb->entry.FirstClusterHigh = CurrentCluster >> 16;
-      Fcb->entry.FirstCluster = CurrentCluster;
+      // root directory of FAT12 od FAT16
+      if (WriteOffset + Length > DeviceExt->rootDirectorySectors * BLOCKSIZE)
+      {
+        DPRINT("Writing over the end of the root directory on FAT12/16\n");
+        return STATUS_END_OF_FILE;
+      }
     }
-    else
+
+    Status = vfatExtendSpace(DeviceExt, FileObject, WriteOffset + Length);
+    if (!NT_SUCCESS (Status))
     {
-      Fcb->entry.FirstCluster = CurrentCluster;
+      return Status;
     }
-    FirstCluster = CurrentCluster;
   }
-  Status = OffsetToCluster(DeviceExt,
-                          FirstCluster,
-                          ROUND_DOWN(WriteOffset, ChunkSize),
-                          &CurrentCluster,
-                          TRUE);
 
-  if (WriteOffset + Length > Fcb->entry.FileSize &&
-      !(Fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY))
+  if (NoCache || PageIo)
   {
-    Fcb->entry.FileSize = WriteOffset + Length;
-  }
 
-  if (FirstCluster == 1)
-  {
-    // root directory of FAT12 od FAT16
-    if (WriteOffset + Length > DeviceExt->rootDirectorySectors * BLOCKSIZE)
-      return STATUS_END_OF_FILE;
-  }
-
-  /*
-   * 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 % ChunkSize) != 0)
-  {
-    TempLength = min (Length, ChunkSize - (WriteOffset % ChunkSize));
-    if ((Length - TempLength) > 0)
+    FirstCluster = CurrentCluster = vfatDirEntryGetFirstCluster (DeviceExt, &Fcb->entry);
+    if (pCcb->LastCluster > 0 && WriteOffset > pCcb->LastOffset)
     {
-      Extend = TRUE;
+      CurrentCluster = pCcb->LastCluster;
     }
-    else
+    Status = OffsetToCluster(DeviceExt,
+                          FirstCluster,
+                          ROUND_DOWN(WriteOffset, DeviceExt->BytesPerCluster),
+                          &CurrentCluster,
+                          FALSE);
+    if (!NT_SUCCESS(Status) || CurrentCluster == 0xffffffff)
     {
-      Extend = FALSE;
+      DPRINT1("????\n");
+      return(Status);
     }
-    Status = VfatWriteCluster(DeviceExt,
-                             Fcb,
-                             ROUND_DOWN(WriteOffset, ChunkSize),
+    pCcb->LastCluster = CurrentCluster;
+    pCcb->LastOffset = ROUND_DOWN(WriteOffset, DeviceExt->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->BytesPerCluster) != 0)
+    {
+      TempLength = min (Length, DeviceExt->BytesPerCluster - (WriteOffset % DeviceExt->BytesPerCluster));
+      Status = VfatWriteCluster(DeviceExt,
+                             ROUND_DOWN(WriteOffset, DeviceExt->BytesPerCluster),
                              FirstCluster,
                              &CurrentCluster,
                              Buffer,
-                             NoCache,
-                             WriteOffset % ChunkSize,
-                             TempLength,
-                             Extend);
-    if (NT_SUCCESS(Status))
-    {
-      Buffer = Buffer + TempLength;
-      Length = Length - TempLength;
-      WriteOffset = WriteOffset + TempLength;
+                             WriteOffset % DeviceExt->BytesPerCluster,
+                             TempLength);
+      if (NT_SUCCESS(Status))
+      {
+        Buffer = Buffer + TempLength;
+        Length = Length - TempLength;
+        WriteOffset = WriteOffset + TempLength;
+      }
     }
-  }
 
-  while (Length >= ChunkSize && CurrentCluster != 0xffffffff && NT_SUCCESS(Status))
-  {
-    if ((Length - ChunkSize) > 0)
+    while (Length >= DeviceExt->BytesPerCluster && CurrentCluster != 0xffffffff && NT_SUCCESS(Status))
     {
-      Extend = TRUE;
+      StartCluster = CurrentCluster;
+      Count = 0;
+      // search for continous clusters
+      do
+      {
+        Count++;
+        Status = NextCluster(DeviceExt, FirstCluster, &CurrentCluster, FALSE);
+      }
+      while (StartCluster + Count == CurrentCluster && NT_SUCCESS(Status) &&
+        Length - Count * DeviceExt->BytesPerCluster >= DeviceExt->BytesPerCluster);
+
+      pCcb->LastCluster = StartCluster + (Count - 1);
+      pCcb->LastOffset = WriteOffset + (Count - 1) * DeviceExt->BytesPerCluster;
+
+      Status = VfatRawWriteCluster(DeviceExt, FirstCluster, Buffer, StartCluster, Count);
+      if (NT_SUCCESS(Status))
+      {
+        Count *=  DeviceExt->BytesPerCluster;
+        Buffer += Count;
+        Length -= Count;
+        WriteOffset += Count;
+      }
     }
-    else
+
+    /* Write the remainder */
+    if (Length > 0 && CurrentCluster != 0xffffffff && NT_SUCCESS(Status))
     {
-      Extend = FALSE;
+      Status = VfatWriteCluster(DeviceExt,
+                            WriteOffset,
+                            FirstCluster,
+                            &CurrentCluster,
+                            Buffer,
+                            0,
+                        Length);
+      if (NT_SUCCESS(Status))
+      {
+        Length = 0;
+      }
     }
-    Status = VfatWriteCluster(DeviceExt,
-                             Fcb,
-                             ROUND_DOWN(WriteOffset, ChunkSize),
-                             FirstCluster,
-                             &CurrentCluster,
-                             Buffer,
-                             NoCache,
-                             0,
-                             ChunkSize,
-                             Extend);
-    if (!NT_SUCCESS(Status))
+    if (NT_SUCCESS(Status) && Length)
     {
-      break;
+      if (WriteOffset < Fcb->RFCB.AllocationSize.QuadPart)
+      {
+        DPRINT1("%d %d\n", WriteOffset, (ULONG)Fcb->RFCB.AllocationSize.QuadPart);
+        Status = STATUS_DISK_FULL; // ???????????
+      }
     }
-    Buffer = Buffer + ChunkSize;
-    Length = Length - ChunkSize;
-    WriteOffset = WriteOffset + ChunkSize;
   }
-
-  /* Write the remainder */
-  if (Length > 0 && CurrentCluster != 0xffffffff && NT_SUCCESS(Status))
+  else
   {
-    Status = VfatWriteCluster(DeviceExt,
-                             Fcb,
-                             ROUND_DOWN(WriteOffset, ChunkSize),
-                             FirstCluster,
-                             &CurrentCluster,
-                             Buffer,
-                             NoCache,
-                             0,
-                             Length,
-                             FALSE);
-    if (NT_SUCCESS(Status))
+    // using the Cc-interface if possible
+    FileOffset.QuadPart = WriteOffset;
+    if(CcCopyWrite(FileObject, &FileOffset, Length, TRUE, Buffer))
     {
-      Length = 0;
+      Status = STATUS_SUCCESS;
+    }
+    else
+    {
+      Status = STATUS_UNSUCCESSFUL;
     }
   }
 
 
-  /* set dates and times */
-  if (!(Fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY))
+  if (!PageIo)
   {
-    KeQuerySystemTime (&SystemTime);
-    ExSystemTimeToLocalTime (&SystemTime, &LocalTime);
-    FsdFileTimeToDosDateTime ((TIME*)&LocalTime,
-                               &Fcb->entry.UpdateDate,
-                               &Fcb->entry.UpdateTime);
-    Fcb->entry.AccessDate = Fcb->entry.UpdateDate;
+    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);
   }
 
-  if (!NT_SUCCESS(Status))
-  {
-    return Status;
-  }
-  if (Length > 0)
-  {
-    return STATUS_DISK_FULL;
-  }
-  return (STATUS_SUCCESS);
+  return Status;
 }
 
 NTSTATUS STDCALL
@@ -1023,7 +636,12 @@ VfatWrite (PDEVICE_OBJECT DeviceObject, PIRP Irp)
   }
 
   Status = VfatWriteFile (DeviceExt, FileObject, Buffer, Length, Offset,
-                         NoCache);
+                         NoCache, Irp->Flags & IRP_PAGING_IO ? TRUE : FALSE);
+
+  if (!(Irp->Flags & IRP_PAGING_IO) && NT_SUCCESS(Status))
+  {
+    FileObject->CurrentByteOffset.QuadPart = Offset + Length;
+  }
 
   Irp->IoStatus.Status = Status;
   Irp->IoStatus.Information = Length;
@@ -1074,17 +692,22 @@ VfatRead (PDEVICE_OBJECT DeviceObject, PIRP Irp)
     NoCache = FALSE;
   }
 
-  /* fail if file is a directory */
   Fcb = ((PVFATCCB) (FileObject->FsContext2))->pFcb;
-  if (Fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY)
+  /* fail if file is a directory and no paged read */
+  if (Fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY && !(Irp->Flags & IRP_PAGING_IO))
   {
     Status = STATUS_FILE_IS_A_DIRECTORY;
   }
   else
   {
-    Status = VfatReadFile (DeviceExt,
-                          FileObject, Buffer, Length, Offset, &LengthRead,
-                          NoCache);
+    Status = VfatReadFile (DeviceExt, FileObject, Buffer, Length,
+               Offset, &LengthRead, NoCache);
+  }
+
+  if (!(Irp->Flags & IRP_PAGING_IO))
+  {
+    // update the file pointer
+    FileObject->CurrentByteOffset.QuadPart = Offset + LengthRead;
   }
 
   Irp->IoStatus.Status = Status;
@@ -1093,3 +716,120 @@ VfatRead (PDEVICE_OBJECT DeviceObject, PIRP Irp)
 
   return (Status);
 }
+
+NTSTATUS vfatExtendSpace (PDEVICE_EXTENSION pDeviceExt, PFILE_OBJECT pFileObject, ULONG NewSize)
+{
+  ULONG FirstCluster;
+  ULONG CurrentCluster;
+  ULONG NewCluster;
+  NTSTATUS Status;
+  PVFATFCB pFcb;
+
+
+  pFcb = ((PVFATCCB) (pFileObject->FsContext2))->pFcb;
+
+  DPRINT ("New Size %d,  AllocationSize %d,  BytesPerCluster %d\n",  NewSize,
+    (ULONG)pFcb->RFCB.AllocationSize.QuadPart, pDeviceExt->BytesPerCluster);
+
+  FirstCluster = CurrentCluster = vfatDirEntryGetFirstCluster (pDeviceExt, &pFcb->entry);
+
+  if (NewSize > pFcb->RFCB.AllocationSize.QuadPart || FirstCluster==0)
+  {
+    // size on disk must be extended
+    if (FirstCluster == 0)
+    {
+      // file of size zero
+      Status = NextCluster (pDeviceExt, FirstCluster, &CurrentCluster, TRUE);
+      if (!NT_SUCCESS(Status))
+      {
+        DPRINT1("NextCluster failed, Status %x\n", Status);
+        return Status;
+      }
+      NewCluster = FirstCluster = CurrentCluster;
+    }
+    else
+    {
+      Status = OffsetToCluster(pDeviceExt, FirstCluster,
+                 pFcb->RFCB.AllocationSize.QuadPart - pDeviceExt->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, FirstCluster, &NewCluster, FALSE);
+      if (NewCluster != 0xffffffff)
+      {
+        DPRINT1("Difference between size from direntry and the FAT.\n");
+      }
+    }
+
+    Status = OffsetToCluster(pDeviceExt, FirstCluster,
+               ROUND_DOWN(NewSize-1, pDeviceExt->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, FirstCluster, &NewCluster, FALSE);
+        WriteCluster(pDeviceExt, CurrentCluster, 0xffffffff);
+      }
+      // free the allocated space
+      while (NewCluster != 0xffffffff)
+      {
+        CurrentCluster = NewCluster;
+        // FIXME: check status
+        NextCluster (pDeviceExt, FirstCluster, &NewCluster, FALSE);
+        WriteCluster (pDeviceExt, CurrentCluster, 0);
+      }
+      return STATUS_DISK_FULL;
+    }
+    if (pFcb->RFCB.AllocationSize.QuadPart == 0)
+    {
+      pFcb->entry.FirstCluster = FirstCluster;
+      if(pDeviceExt->FatType == FAT32)
+        pFcb->entry.FirstClusterHigh = FirstCluster >> 16;
+    }
+    pFcb->RFCB.AllocationSize.QuadPart = ROUND_UP(NewSize, pDeviceExt->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;
+}