Browse Source

Truncate should make it to the file system.

Awhile back we changed writes to hit the file system to avoid
overcommitting storage space. Truncate should get the same treatement.
This required teaching FAT how to grow a file, which it sort of already
had been doing in the old version of write (where it might get surprised
by a new large file size).

This was found by an assert in the shared memory object code fired during
the mmaptest.
Evan Green 8 years ago
parent
commit
2ba428c02c
8 changed files with 309 additions and 238 deletions
  1. 43 126
      drivers/fat/fatfs.c
  2. 27 2
      include/minoca/kernel/io.h
  3. 36 21
      include/minoca/lib/fat/fat.h
  4. 16 4
      kernel/io/cachedio.c
  5. 37 41
      kernel/io/fileobj.c
  6. 8 3
      kernel/io/iop.h
  7. 10 11
      kernel/io/shmemobj.c
  8. 132 30
      lib/fatlib/fat.c

+ 43 - 126
drivers/fat/fatfs.c

@@ -245,6 +245,7 @@ KSTATUS
 FatpTruncateFile (
     PFATFS_VOLUME Volume,
     PFILE_PROPERTIES FileProperties,
+    ULONGLONG NewSize,
     PVOID FileToken
     );
 
@@ -967,11 +968,6 @@ Return Value:
 {
 
     UINTN BytesCompleted;
-    UINTN BytesCompletedThisRound;
-    UINTN BytesToCompleteThisRound;
-    ULONG ClusterByteOffset;
-    ULONG ClusterSize;
-    ULONGLONG CurrentOffset;
     PFATFS_DIRECTORY_OBJECT DirectoryObject;
     PIRP DiskIrp;
     ULONG ElementsRead;
@@ -984,8 +980,6 @@ Return Value:
     ULONGLONG IoOffset;
     KSTATUS Status;
     PFATFS_TRANSFER Transfer;
-    UINTN ZeroBufferSize;
-    PIO_BUFFER ZeroIoBuffer;
 
     ASSERT(Irp->Direction == IrpDown);
     ASSERT(Irp->MajorCode == IrpMajorIo);
@@ -1017,7 +1011,6 @@ Return Value:
 
     IoBuffer = Irp->U.ReadWrite.IoBuffer;
     IoOffset = Irp->U.ReadWrite.IoOffset;
-    ZeroIoBuffer = NULL;
 
     //
     // All requests must supply an I/O buffer.
@@ -1079,6 +1072,26 @@ Return Value:
         goto DispatchIoEnd;
     }
 
+    //
+    // If the seek didn't get all the way to the desired offset, write some
+    // zeroes.
+    //
+
+    if ((Irp->MinorCode == IrpMinorIoWrite) && (FileProperties != NULL)) {
+        READ_INT64_SYNC(&(FileProperties->FileSize), &FileSize);
+        if (FileSize < IoOffset) {
+            Status = FatTruncate(FatVolume->VolumeToken,
+                                 FatFile->FileToken,
+                                 FileProperties->FileId,
+                                 FileSize,
+                                 IoOffset);
+
+            if (!KSUCCESS(Status)) {
+                goto DispatchIoEnd;
+            }
+        }
+    }
+
     //
     // Seek to the desired offset within the file. If the seek reaches the end
     // of file and this is not a write operation, fail.
@@ -1092,10 +1105,7 @@ Return Value:
                          IoOffset,
                          &FatSeekInformation);
 
-    if (!KSUCCESS(Status) &&
-        ((Irp->MinorCode != IrpMinorIoWrite) ||
-         (Status != STATUS_END_OF_FILE))) {
-
+    if (!KSUCCESS(Status)) {
         if (Status == STATUS_OUT_OF_BOUNDS) {
             Status = STATUS_END_OF_FILE;
         }
@@ -1103,104 +1113,6 @@ Return Value:
         goto DispatchIoEnd;
     }
 
-    //
-    // If the seek didn't get all the way to the desired offset, write some
-    // zeroes.
-    //
-
-    ClusterSize = FatGetFileBlockSize(FatFile->FileToken);
-    if (Irp->MinorCode == IrpMinorIoWrite) {
-        CurrentOffset = FatSeekInformation.FileByteOffset;
-
-        //
-        // If the current offset is beyond the file size, then the seek
-        // above went to the end of the cluster, but some of that cluster
-        // needs to be zero'd out. Seek back. Make no assumptions about the
-        // system having already zero'd the uninitialized portion of a cluster.
-        // The FAT partition could have come from another system and all bets
-        // are off.
-        //
-
-        if (FileProperties != NULL) {
-            READ_INT64_SYNC(&(FileProperties->FileSize), &FileSize);
-            if (CurrentOffset > FileSize) {
-                Status = FatFileSeek(FatFile->FileToken,
-                                     DiskIrp,
-                                     Irp->U.ReadWrite.IoFlags,
-                                     SeekCommandFromBeginning,
-                                     FileSize,
-                                     &FatSeekInformation);
-
-                if (!KSUCCESS(Status)) {
-                    goto DispatchIoEnd;
-                }
-
-                ASSERT(FileSize == FatSeekInformation.FileByteOffset);
-
-                CurrentOffset = FileSize;
-            }
-        }
-
-        if (CurrentOffset != IoOffset) {
-            ZeroBufferSize = IoOffset - CurrentOffset;
-            if (ZeroBufferSize > FAT_ZERO_BUFFER_SIZE) {
-                ZeroBufferSize = FAT_ZERO_BUFFER_SIZE;
-            }
-
-            ZeroIoBuffer = MmAllocatePagedIoBuffer(ZeroBufferSize, 0);
-            if (ZeroIoBuffer == NULL) {
-                Status = STATUS_INSUFFICIENT_RESOURCES;
-                goto DispatchIoEnd;
-            }
-
-            MmZeroIoBuffer(ZeroIoBuffer, 0, ZeroBufferSize);
-
-            //
-            // Zero out the file block by block.
-            //
-
-            while (CurrentOffset != IoOffset) {
-
-                //
-                // If the offset is not aligned, just write the rest of the
-                // block.
-                //
-
-                BytesToCompleteThisRound = ZeroBufferSize;
-                if (IS_ALIGNED(CurrentOffset, ClusterSize) == FALSE) {
-                    ClusterByteOffset = FatSeekInformation.ClusterByteOffset;
-
-                    ASSERT(ClusterByteOffset < ClusterSize);
-
-                    BytesToCompleteThisRound = ClusterSize - ClusterByteOffset;
-                }
-
-                if ((IoOffset - CurrentOffset) < BytesToCompleteThisRound) {
-                    BytesToCompleteThisRound = IoOffset - CurrentOffset;
-                }
-
-                Status = FatWriteFile(FatFile->FileToken,
-                                      &FatSeekInformation,
-                                      (PFAT_IO_BUFFER)ZeroIoBuffer,
-                                      BytesToCompleteThisRound,
-                                      Irp->U.ReadWrite.IoFlags,
-                                      DiskIrp,
-                                      &BytesCompletedThisRound);
-
-                if (!KSUCCESS(Status)) {
-                    goto DispatchIoEnd;
-                }
-
-                ASSERT(BytesCompletedThisRound != 0);
-
-                CurrentOffset += BytesCompletedThisRound;
-            }
-
-            MmFreeIoBuffer(ZeroIoBuffer);
-            ZeroIoBuffer = NULL;
-        }
-    }
-
     //
     // Read or write the requested region of the file.
     //
@@ -1233,10 +1145,6 @@ Return Value:
     Irp->U.ReadWrite.NewIoOffset = IoOffset + BytesCompleted;
 
 DispatchIoEnd:
-    if (ZeroIoBuffer != NULL) {
-        MmFreeIoBuffer(ZeroIoBuffer);
-    }
-
     IoCompleteIrp(FatDriver, Irp, Status);
     return;
 }
@@ -1352,6 +1260,7 @@ Return Value:
     PSYSTEM_CONTROL_LOOKUP Lookup;
     PSYSTEM_CONTROL_RENAME Rename;
     KSTATUS Status;
+    PSYSTEM_CONTROL_TRUNCATE Truncate;
     PSYSTEM_CONTROL_UNLINK Unlink;
     PFATFS_VOLUME Volume;
 
@@ -1602,14 +1511,15 @@ Return Value:
     //
 
     case IrpMinorSystemControlTruncate:
-        FileOperation = (PSYSTEM_CONTROL_FILE_OPERATION)Context;
+        Truncate = (PSYSTEM_CONTROL_TRUNCATE)Context;
 
-        ASSERT(FileOperation->FileProperties->Type == IoObjectRegularFile);
-        ASSERT(FileOperation->DeviceContext != NULL);
+        ASSERT(Truncate->FileProperties->Type == IoObjectRegularFile);
+        ASSERT(Truncate->DeviceContext != NULL);
 
-        File = (PFATFS_FILE)(FileOperation->DeviceContext);
+        File = (PFATFS_FILE)(Truncate->DeviceContext);
         Status = FatpTruncateFile(Volume,
-                                  FileOperation->FileProperties,
+                                  Truncate->FileProperties,
+                                  Truncate->NewSize,
                                   File->FileToken);
 
         IoCompleteIrp(FatDriver, Irp, Status);
@@ -1833,6 +1743,7 @@ KSTATUS
 FatpTruncateFile (
     PFATFS_VOLUME Volume,
     PFILE_PROPERTIES FileProperties,
+    ULONGLONG NewSize,
     PVOID FileToken
     )
 
@@ -1849,6 +1760,8 @@ Arguments:
     FileProperties - Supplies a pointer to the properties of the file being
         destroyed.
 
+    NewSize - Supplies the new file size to set.
+
     FileToken - Supplies an optional open file token.
 
 Return Value:
@@ -1859,15 +1772,19 @@ Return Value:
 
 {
 
-    ULONGLONG FileSize;
+    ULONGLONG OldSize;
     KSTATUS Status;
 
-    READ_INT64_SYNC(&(FileProperties->FileSize), &FileSize);
-    Status = FatDeleteFileBlocks(Volume->VolumeToken,
-                                 FileToken,
-                                 FileProperties->FileId,
-                                 FileSize,
-                                 TRUE);
+    READ_INT64_SYNC(&(FileProperties->FileSize), &OldSize);
+    Status = FatTruncate(Volume->VolumeToken,
+                         FileToken,
+                         FileProperties->FileId,
+                         OldSize,
+                         NewSize);
+
+    if (KSUCCESS(Status)) {
+        WRITE_INT64_SYNC(&(FileProperties->FileSize), NewSize);
+    }
 
     return Status;
 }

+ 27 - 2
include/minoca/kernel/io.h

@@ -2158,8 +2158,8 @@ typedef struct _SYSTEM_CONTROL_LOOKUP {
 
 Structure Description:
 
-    This structure defines the information sent to a file system for the
-    following requests: write file properties, truncate, and delete.
+    This structure defines the information sent to a file system for
+    flushing file properties and unlink.
 
 Members:
 
@@ -2313,6 +2313,31 @@ typedef struct _SYSTEM_CONTROL_RENAME {
 
 /*++
 
+Structure Description:
+
+    This structure defines the information sent to a file system for a truncate
+    operation.
+
+Members:
+
+    File - Stores a pointer to the properties of the target file.
+
+    DeviceContext - Stores a pointer to the open device context for the file if
+        there is one. This is filled in for some operations (like truncate),
+        but not all.
+
+    NewSize - Stores the new size to truncate the file to.
+
+--*/
+
+typedef struct _SYSTEM_CONTROL_TRUNCATE {
+    PFILE_PROPERTIES FileProperties;
+    PVOID DeviceContext;
+    ULONGLONG NewSize;
+} SYSTEM_CONTROL_TRUNCATE, *PSYSTEM_CONTROL_TRUNCATE;
+
+/*++
+
 Structure Description:
 
     This structure defines a device information result returned as an array

+ 36 - 21
include/minoca/lib/fat/fat.h

@@ -635,6 +635,42 @@ Return Value:
 
 --*/
 
+KSTATUS
+FatTruncate (
+    PVOID Volume,
+    PVOID FileToken,
+    FILE_ID FileId,
+    ULONGLONG OldSize,
+    ULONGLONG NewSize
+    );
+
+/*++
+
+Routine Description:
+
+    This routine truncates a file to the given file size. This can be used to
+    both shrink and grow the file.
+
+Arguments:
+
+    Volume - Supplies a pointer to the volume.
+
+    FileToken - Supplies the file context of the file to operate on.
+
+    FileId - Supplies the file ID of the file to operate on.
+
+    OldSize - Supplies the original size of the file.
+
+    NewSize - Supplies the new size to make the file. If smaller, then
+        unused clusters will be freed. If larger, then the file will be
+        zeroed to make room.
+
+Return Value:
+
+    Status code.
+
+--*/
+
 KSTATUS
 FatFileSeek (
     PVOID FileToken,
@@ -683,27 +719,6 @@ Return Value:
 
 --*/
 
-ULONG
-FatGetFileBlockSize (
-    PVOID FileToken
-    );
-
-/*++
-
-Routine Description:
-
-    This routine returns the block size for the FAT file.
-
-Arguments:
-
-    FileToken - Supplies the opaque token returned when the file was opened.
-
-Return Value:
-
-    Returns the size of a block for the file.
-
---*/
-
 KSTATUS
 FatWriteFileProperties (
     PVOID Volume,

+ 16 - 4
kernel/io/cachedio.c

@@ -1216,7 +1216,10 @@ Return Value:
         MissContext.TimeoutInMilliseconds = TimeoutInMilliseconds;
         MissContext.Write = TRUE;
         Status = IopHandleCacheReadMiss(FileObject, &MissContext);
-        if (!KSUCCESS(Status) && (Status != STATUS_END_OF_FILE)) {
+        if ((!KSUCCESS(Status)) &&
+            ((Status != STATUS_END_OF_FILE) ||
+             (MissContext.BytesCompleted == 0))) {
+
             goto HandleCacheWriteMissEnd;
         }
 
@@ -1645,7 +1648,10 @@ Return Value:
     ReadIoContext.TimeoutInMilliseconds = IoContext->TimeoutInMilliseconds;
     ReadIoContext.Write = FALSE;
     Status = IopPerformNonCachedRead(FileObject, &ReadIoContext, NULL);
-    if (!KSUCCESS(Status) && (Status != STATUS_END_OF_FILE)) {
+    if ((!KSUCCESS(Status)) &&
+        ((Status != STATUS_END_OF_FILE) ||
+         (ReadIoContext.BytesCompleted == 0))) {
+
         goto HandleDefaultCacheReadMissEnd;
     }
 
@@ -1954,7 +1960,10 @@ Return Value:
     //
 
     Status = IopSendIoReadIrp(Device, &Parameters);
-    if ((!KSUCCESS(Status)) && (Status != STATUS_END_OF_FILE)) {
+    if ((!KSUCCESS(Status)) &&
+        ((Status != STATUS_END_OF_FILE) ||
+         (Parameters.IoBytesCompleted == 0))) {
+
         goto PerformDefaultNonCachedReadEnd;
     }
 
@@ -2328,7 +2337,10 @@ Return Value:
     Parameters.NewIoOffset = Parameters.IoOffset;
     Parameters.IoBuffer = AlignedIoBuffer;
     Status = IopSendIoReadIrp(Device, &Parameters);
-    if (!KSUCCESS(Status) && (Status != STATUS_END_OF_FILE)) {
+    if ((!KSUCCESS(Status)) &&
+        ((Status != STATUS_END_OF_FILE) ||
+         (Parameters.IoBytesCompleted == 0))) {
+
         goto PerformDefaultPartialWriteEnd;
     }
 

+ 37 - 41
kernel/io/fileobj.c

@@ -1947,6 +1947,7 @@ Return Value:
     IO_OFFSET EvictionOffset;
     ULONGLONG FileSize;
     ULONG PageSize;
+    SYSTEM_CONTROL_TRUNCATE Request;
     KSTATUS Status;
     IO_OFFSET UnmapOffset;
     ULONGLONG UnmapSize;
@@ -1963,46 +1964,52 @@ Return Value:
         goto ModifyFileObjectSizeEnd;
     }
 
+    BlockSize = FileObject->Properties.BlockSize;
+
     //
-    // If the new size is less than the current size, then work needs to be
-    // done to make sure the system isn't using any of the truncated data.
+    // TODO: Block size should be managed by the file system.
     //
 
-    if (NewFileSize < FileSize) {
-        WRITE_INT64_SYNC(&(FileObject->Properties.FileSize), NewFileSize);
-        BlockSize = FileObject->Properties.BlockSize;
-        FileObject->Properties.BlockCount =
-                           ALIGN_RANGE_UP(NewFileSize, BlockSize) / BlockSize;
+    FileObject->Properties.BlockCount =
+                       ALIGN_RANGE_UP(NewFileSize, BlockSize) / BlockSize;
 
-        IopMarkFileObjectPropertiesDirty(FileObject);
+    //
+    // If this is a shared memory object, then handle that separately.
+    //
 
-        //
-        // If this is a shared memory object, then handle that separately.
-        //
+    if (FileObject->Properties.Type == IoObjectSharedMemoryObject) {
+        Status = IopTruncateSharedMemoryObject(FileObject, NewFileSize);
 
-        if (FileObject->Properties.Type == IoObjectSharedMemoryObject) {
-            Status = IopTruncateSharedMemoryObject(FileObject);
+    //
+    // Otherwise call the driver to truncate the file or device. The
+    // driver will check the file size and truncate the file down to
+    // the new size.
+    //
 
-        //
-        // Otherwise call the driver to truncate the file or device. The
-        // driver will check the file size and truncate the file down to
-        // the new size.
-        //
+    } else {
+        if (DeviceContext == NULL) {
+            DeviceContext = FileObject->DeviceContext;
+        }
 
-        } else {
-            if (DeviceContext == NULL) {
-                DeviceContext = FileObject->DeviceContext;
-            }
+        Request.FileProperties = &(FileObject->Properties);
+        Request.DeviceContext = DeviceContext;
+        Request.NewSize = NewFileSize;
+        Status = IopSendSystemControlIrp(FileObject->Device,
+                                         IrpMinorSystemControlTruncate,
+                                         &Request);
+    }
 
-            Status = IopSendFileOperationIrp(IrpMinorSystemControlTruncate,
-                                             FileObject,
-                                             DeviceContext,
-                                             0);
-        }
+    IopMarkFileObjectPropertiesDirty(FileObject);
+    if (!KSUCCESS(Status)) {
+        goto ModifyFileObjectSizeEnd;
+    }
 
-        if (!KSUCCESS(Status)) {
-            goto ModifyFileObjectSizeEnd;
-        }
+    //
+    // If the new size is less than the current size, then work needs to be
+    // done to make sure the system isn't using any of the truncated data.
+    //
+
+    if (NewFileSize < FileSize) {
 
         //
         // Unmap all image sections that might have mapped portions of this
@@ -2032,17 +2039,6 @@ Return Value:
 
             IopEvictPageCacheEntries(FileObject, EvictionOffset, EvictionFlags);
         }
-
-        Status = STATUS_SUCCESS;
-
-    //
-    // Otherwise just update the file object's file size to allow reads beyond
-    // the old file size.
-    //
-
-    } else {
-        IopUpdateFileObjectFileSize(FileObject, NewFileSize);
-        Status = STATUS_SUCCESS;
     }
 
 ModifyFileObjectSizeEnd:

+ 8 - 3
kernel/io/iop.h

@@ -3684,18 +3684,23 @@ Return Value:
 
 KSTATUS
 IopTruncateSharedMemoryObject (
-    PFILE_OBJECT FileObject
+    PFILE_OBJECT FileObject,
+    ULONGLONG NewSize
     );
 
 /*++
 
 Routine Description:
 
-    This routine truncates a shared memory object.
+    This routine truncates a shared memory object. It assumes that the file's
+    lock is held exclusively.
 
 Arguments:
 
-    FileObject - Supplies a pointer to a shared memory object.
+    FileObject - Supplies a pointer to the file object that owns the shared
+        memory object.
+
+    NewSize - Supplies the new size to set.
 
 Return Value:
 

+ 10 - 11
kernel/io/shmemobj.c

@@ -614,7 +614,8 @@ CreateSharedMemoryObjectEnd:
 
 KSTATUS
 IopTruncateSharedMemoryObject (
-    PFILE_OBJECT FileObject
+    PFILE_OBJECT FileObject,
+    ULONGLONG NewSize
     )
 
 /*++
@@ -629,6 +630,8 @@ Arguments:
     FileObject - Supplies a pointer to the file object that owns the shared
         memory object.
 
+    NewSize - Supplies the new size to set.
+
 Return Value:
 
     Status code.
@@ -639,8 +642,6 @@ Return Value:
 
     PIO_HANDLE BackingImage;
     PFILE_OBJECT BackingImageObject;
-    ULONGLONG BackingImageSize;
-    ULONGLONG FileSize;
     PSHARED_MEMORY_OBJECT SharedMemoryObject;
     KSTATUS Status;
 
@@ -662,15 +663,13 @@ Return Value:
 
     BackingImage = SharedMemoryObject->BackingImage;
     BackingImageObject = BackingImage->FileObject;
-    READ_INT64_SYNC(&(FileObject->Properties.FileSize), &FileSize);
-    READ_INT64_SYNC(&(BackingImageObject->Properties.FileSize),
-                    &BackingImageSize);
+    Status = IopModifyFileObjectSize(BackingImageObject,
+                                     BackingImage->DeviceContext,
+                                     NewSize);
 
-    ASSERT(FileSize < BackingImageSize);
-
-    Status =  IopModifyFileObjectSize(BackingImageObject,
-                                      BackingImage->DeviceContext,
-                                      FileSize);
+    if (KSUCCESS(Status)) {
+        WRITE_INT64_SYNC(&(FileObject->Properties.FileSize), NewSize);
+    }
 
     return Status;
 }

+ 132 - 30
lib/fatlib/fat.c

@@ -2595,6 +2595,131 @@ RenameEnd:
     return Status;
 }
 
+KSTATUS
+FatTruncate (
+    PVOID Volume,
+    PVOID FileToken,
+    FILE_ID FileId,
+    ULONGLONG OldSize,
+    ULONGLONG NewSize
+    )
+
+/*++
+
+Routine Description:
+
+    This routine truncates a file to the given file size. This can be used to
+    both shrink and grow the file.
+
+Arguments:
+
+    Volume - Supplies a pointer to the volume.
+
+    FileToken - Supplies the file context of the file to operate on.
+
+    FileId - Supplies the file ID of the file to operate on.
+
+    OldSize - Supplies the original size of the file.
+
+    NewSize - Supplies the new size to make the file. If smaller, then
+        unused clusters will be freed. If larger, then the file will be
+        zeroed to make room.
+
+Return Value:
+
+    Status code.
+
+--*/
+
+{
+
+    UINTN BytesThisRound;
+    UINTN BytesWritten;
+    ULONG ClusterSize;
+    PFAT_VOLUME FatVolume;
+    FAT_SEEK_INFORMATION Seek;
+    KSTATUS Status;
+    PFAT_IO_BUFFER ZeroBuffer;
+
+    FatVolume = Volume;
+    ClusterSize = FatVolume->ClusterSize;
+    ZeroBuffer = NULL;
+    if (NewSize < OldSize) {
+        return FatDeleteFileBlocks(Volume, FileToken, FileId, NewSize, TRUE);
+    }
+
+    //
+    // Create a cluster sized buffer full of zeros.
+    //
+
+    ZeroBuffer = FatAllocateIoBuffer(FatVolume->Device.DeviceToken,
+                                     ClusterSize);
+
+    if (ZeroBuffer == NULL) {
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto TruncateEnd;
+    }
+
+    Status = FatZeroIoBuffer(ZeroBuffer, 0, ClusterSize);
+    if (!KSUCCESS(Status)) {
+        goto TruncateEnd;
+    }
+
+    //
+    // It's time to grow the file. Seek to the old end of the file.
+    //
+
+    RtlZeroMemory(&Seek, sizeof(FAT_SEEK_INFORMATION));
+    Status = FatFileSeek(FileToken,
+                         NULL,
+                         0,
+                         SeekCommandFromBeginning,
+                         OldSize,
+                         &Seek);
+
+    if (!KSUCCESS(Status)) {
+        goto TruncateEnd;
+    }
+
+    while (OldSize < NewSize) {
+        if (!IS_ALIGNED(OldSize, ClusterSize)) {
+            BytesThisRound = ClusterSize - REMAINDER(OldSize, ClusterSize);
+
+        } else {
+            BytesThisRound = ClusterSize;
+        }
+
+        if (OldSize + BytesThisRound > NewSize) {
+            BytesThisRound = NewSize - OldSize;
+        }
+
+        Status = FatWriteFile(FileToken,
+                              &Seek,
+                              ZeroBuffer,
+                              BytesThisRound,
+                              0,
+                              NULL,
+                              &BytesWritten);
+
+        if (!KSUCCESS(Status)) {
+            goto TruncateEnd;
+        }
+
+        ASSERT(BytesWritten != 0);
+
+        OldSize += BytesWritten;
+    }
+
+    Status = STATUS_SUCCESS;
+
+TruncateEnd:
+    if (ZeroBuffer != NULL) {
+        FatFreeIoBuffer(ZeroBuffer);
+    }
+
+    return Status;
+}
+
 KSTATUS
 FatFileSeek (
     PVOID FileToken,
@@ -2904,7 +3029,13 @@ Return Value:
         if ((CurrentCluster < FAT_CLUSTER_BEGIN) ||
             (CurrentCluster >= ClusterBad)) {
 
-            Status = STATUS_END_OF_FILE;
+            if (CurrentOffset == ClusterAlignedDestination) {
+                Status = STATUS_SUCCESS;
+
+            } else {
+                Status = STATUS_END_OF_FILE;
+            }
+
             DiskByteOffset = FAT_CLUSTER_TO_BYTE(Volume, PreviousCluster);
 
             //
@@ -2959,35 +3090,6 @@ FatFileSeekEnd:
     return Status;
 }
 
-ULONG
-FatGetFileBlockSize (
-    PVOID FileToken
-    )
-
-/*++
-
-Routine Description:
-
-    This routine returns the block size for the FAT file.
-
-Arguments:
-
-    FileToken - Supplies the opaque token returned when the file was opened.
-
-Return Value:
-
-    Returns the size of a block for the file.
-
---*/
-
-{
-
-    PFAT_FILE FatFile;
-
-    FatFile = (PFAT_FILE)FileToken;
-    return FatFile->Volume->ClusterSize;
-}
-
 KSTATUS
 FatWriteFileProperties (
     PVOID Volume,