[FASTFAT] Reduce the usage of the generic allocation tag
[reactos.git] / drivers / filesystems / fastfat / fat.c
index a079f25..8d4ecd9 100644 (file)
@@ -1,9 +1,10 @@
 /*
  * COPYRIGHT:        See COPYING in the top level directory
  * PROJECT:          ReactOS kernel
- * FILE:             drivers/fs/vfat/fat.c
- * PURPOSE:          VFAT Filesystem
+ * FILE:             drivers/filesystems/fastfat/fat.c
+ * PURPOSE:          FastFAT Filesystem
  * PROGRAMMER:       Jason Filby (jasonfilby@yahoo.com)
+ *                   Pierre Schweitzer (pierre@reactos.org)
  *
  */
 
 #define  CACHEPAGESIZE(pDeviceExt) ((pDeviceExt)->FatInfo.BytesPerCluster > PAGE_SIZE ? \
                   (pDeviceExt)->FatInfo.BytesPerCluster : PAGE_SIZE)
 
+/* FIXME: because volume is not cached, we have to perform direct IOs
+ * The day this is fixed, just comment out that line, and check
+ * it still works (and delete old code ;-))
+ */
+#define VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
+
 /* FUNCTIONS ****************************************************************/
 
 /*
@@ -31,6 +38,7 @@ FAT32GetNextCluster(
     ULONG CurrentCluster,
     PULONG NextCluster)
 {
+    NTSTATUS Status = STATUS_SUCCESS;
     PVOID BaseAddress;
     ULONG FATOffset;
     ULONG ChunkSize;
@@ -40,18 +48,30 @@ FAT32GetNextCluster(
     ChunkSize = CACHEPAGESIZE(DeviceExt);
     FATOffset = CurrentCluster * sizeof(ULONG);
     Offset.QuadPart = ROUND_DOWN(FATOffset, ChunkSize);
-    if (!CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, 1, &Context, &BaseAddress))
+    _SEH2_TRY
+    {
+        CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, MAP_WAIT, &Context, &BaseAddress);
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
     {
-        return STATUS_UNSUCCESSFUL;
+        _SEH2_YIELD(return _SEH2_GetExceptionCode());
     }
+    _SEH2_END;
 
     CurrentCluster = (*(PULONG)((char*)BaseAddress + (FATOffset % ChunkSize))) & 0x0fffffff;
     if (CurrentCluster >= 0xffffff8 && CurrentCluster <= 0xfffffff)
         CurrentCluster = 0xffffffff;
 
+    if (CurrentCluster == 0)
+    {
+        DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n");
+        Status = STATUS_FILE_CORRUPT_ERROR;
+        if (VfatGlobalData->Flags & VFAT_BREAK_ON_CORRUPTION)
+            ASSERT(CurrentCluster != 0);
+    }
     CcUnpinData(Context);
     *NextCluster = CurrentCluster;
-    return STATUS_SUCCESS;
+    return Status;
 }
 
 /*
@@ -63,6 +83,7 @@ FAT16GetNextCluster(
     ULONG CurrentCluster,
     PULONG NextCluster)
 {
+    NTSTATUS Status = STATUS_SUCCESS;
     PVOID BaseAddress;
     ULONG FATOffset;
     ULONG ChunkSize;
@@ -72,17 +93,31 @@ FAT16GetNextCluster(
     ChunkSize = CACHEPAGESIZE(DeviceExt);
     FATOffset = CurrentCluster * 2;
     Offset.QuadPart = ROUND_DOWN(FATOffset, ChunkSize);
-    if (!CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, MAP_WAIT, &Context, &BaseAddress))
+    _SEH2_TRY
+    {
+        CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, MAP_WAIT, &Context, &BaseAddress);
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
     {
-         return STATUS_UNSUCCESSFUL;
+        _SEH2_YIELD(return _SEH2_GetExceptionCode());
     }
+    _SEH2_END;
 
     CurrentCluster = *((PUSHORT)((char*)BaseAddress + (FATOffset % ChunkSize)));
     if (CurrentCluster >= 0xfff8 && CurrentCluster <= 0xffff)
         CurrentCluster = 0xffffffff;
+
+    if (CurrentCluster == 0)
+    {
+        DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n");
+        Status = STATUS_FILE_CORRUPT_ERROR;
+        if (VfatGlobalData->Flags & VFAT_BREAK_ON_CORRUPTION)
+            ASSERT(CurrentCluster != 0);
+    }
+
     CcUnpinData(Context);
     *NextCluster = CurrentCluster;
-    return STATUS_SUCCESS;
+    return Status;
 }
 
 /*
@@ -103,10 +138,15 @@ FAT12GetNextCluster(
     *NextCluster = 0;
 
     Offset.QuadPart = 0;
-    if (!CcMapData(DeviceExt->FATFileObject, &Offset, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector, 1, &Context, &BaseAddress))
+    _SEH2_TRY
     {
-        return STATUS_UNSUCCESSFUL;
+        CcMapData(DeviceExt->FATFileObject, &Offset, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector, MAP_WAIT, &Context, &BaseAddress);
     }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        _SEH2_YIELD(return _SEH2_GetExceptionCode());
+    }
+    _SEH2_END;
 
     CBlock = (PUSHORT)((char*)BaseAddress + (CurrentCluster * 12) / 8);
     if ((CurrentCluster % 2) == 0)
@@ -123,6 +163,7 @@ FAT12GetNextCluster(
         Entry = 0xffffffff;
 
 //    DPRINT("Returning %x\n",Entry);
+    ASSERT(Entry != 0);
     *NextCluster = Entry;
     CcUnpinData(Context);
 //    return Entry == 0xffffffff ? STATUS_END_OF_FILE : STATUS_SUCCESS;
@@ -157,11 +198,16 @@ FAT16FindAndMarkAvailableCluster(
         for (i = StartCluster; i < FatLength;)
         {
             Offset.QuadPart = ROUND_DOWN(i * 2, ChunkSize);
-            if (!CcPinRead(DeviceExt->FATFileObject, &Offset, ChunkSize, 1, &Context, &BaseAddress))
+            _SEH2_TRY
+            {
+                CcPinRead(DeviceExt->FATFileObject, &Offset, ChunkSize, PIN_WAIT, &Context, &BaseAddress);
+            }
+            _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
             {
-                DPRINT1("CcMapData(Offset %x, Length %u) failed\n", (ULONG)Offset.QuadPart, ChunkSize);
-                return STATUS_UNSUCCESSFUL;
+                DPRINT1("CcPinRead(Offset %x, Length %u) failed\n", (ULONG)Offset.QuadPart, ChunkSize);
+                _SEH2_YIELD(return _SEH2_GetExceptionCode());
             }
+            _SEH2_END;
 
             Block = (PUSHORT)((ULONG_PTR)BaseAddress + (i * 2) % ChunkSize);
             BlockEnd = (PUSHORT)((ULONG_PTR)BaseAddress + ChunkSize);
@@ -216,11 +262,16 @@ FAT12FindAndMarkAvailableCluster(
     *Cluster = 0;
     StartCluster = DeviceExt->LastAvailableCluster;
     Offset.QuadPart = 0;
-    if (!CcPinRead(DeviceExt->FATFileObject, &Offset, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector, 1, &Context, &BaseAddress))
+    _SEH2_TRY
     {
-        DPRINT1("CcMapData(Offset %x, Length %u) failed\n", (ULONG)Offset.QuadPart, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector);
-        return STATUS_UNSUCCESSFUL;
+        CcPinRead(DeviceExt->FATFileObject, &Offset, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector, PIN_WAIT, &Context, &BaseAddress);
     }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        DPRINT1("CcPinRead(Offset %x, Length %u) failed\n", (ULONG)Offset.QuadPart, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector);
+        _SEH2_YIELD(return _SEH2_GetExceptionCode());
+    }
+    _SEH2_END;
 
     for (j = 0; j < 2; j++)
     {
@@ -286,11 +337,16 @@ FAT32FindAndMarkAvailableCluster(
         for (i = StartCluster; i < FatLength;)
         {
             Offset.QuadPart = ROUND_DOWN(i * 4, ChunkSize);
-            if (!CcPinRead(DeviceExt->FATFileObject, &Offset, ChunkSize, 1, &Context, &BaseAddress))
+            _SEH2_TRY
+            {
+                CcPinRead(DeviceExt->FATFileObject, &Offset, ChunkSize, PIN_WAIT, &Context, &BaseAddress);
+            }
+            _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
             {
-                DPRINT1("CcMapData(Offset %x, Length %u) failed\n", (ULONG)Offset.QuadPart, ChunkSize);
-                return STATUS_UNSUCCESSFUL;
+                DPRINT1("CcPinRead(Offset %x, Length %u) failed\n", (ULONG)Offset.QuadPart, ChunkSize);
+                _SEH2_YIELD(return _SEH2_GetExceptionCode());
             }
+            _SEH2_END;
             Block = (PULONG)((ULONG_PTR)BaseAddress + (i * 4) % ChunkSize);
             BlockEnd = (PULONG)((ULONG_PTR)BaseAddress + ChunkSize);
 
@@ -339,10 +395,15 @@ FAT12CountAvailableClusters(
     PUSHORT CBlock;
 
     Offset.QuadPart = 0;
-    if (!CcMapData(DeviceExt->FATFileObject, &Offset, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector, 1, &Context, &BaseAddress))
+    _SEH2_TRY
     {
-        return STATUS_UNSUCCESSFUL;
+        CcMapData(DeviceExt->FATFileObject, &Offset, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector, MAP_WAIT, &Context, &BaseAddress);
     }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        _SEH2_YIELD(return _SEH2_GetExceptionCode());
+    }
+    _SEH2_END;
 
     numberofclusters = DeviceExt->FatInfo.NumberOfClusters + 2;
 
@@ -394,10 +455,15 @@ FAT16CountAvailableClusters(
     for (i = 2; i < FatLength; )
     {
         Offset.QuadPart = ROUND_DOWN(i * 2, ChunkSize);
-        if (!CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, 1, &Context, &BaseAddress))
+        _SEH2_TRY
+        {
+            CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, MAP_WAIT, &Context, &BaseAddress);
+        }
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
         {
-            return STATUS_UNSUCCESSFUL;
+            _SEH2_YIELD(return _SEH2_GetExceptionCode());
         }
+        _SEH2_END;
         Block = (PUSHORT)((ULONG_PTR)BaseAddress + (i * 2) % ChunkSize);
         BlockEnd = (PUSHORT)((ULONG_PTR)BaseAddress + ChunkSize);
 
@@ -444,11 +510,16 @@ FAT32CountAvailableClusters(
     for (i = 2; i < FatLength; )
     {
         Offset.QuadPart = ROUND_DOWN(i * 4, ChunkSize);
-        if (!CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, 1, &Context, &BaseAddress))
+        _SEH2_TRY
+        {
+            CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, MAP_WAIT, &Context, &BaseAddress);
+        }
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
         {
             DPRINT1("CcMapData(Offset %x, Length %u) failed\n", (ULONG)Offset.QuadPart, ChunkSize);
-            return STATUS_UNSUCCESSFUL;
+            _SEH2_YIELD(return _SEH2_GetExceptionCode());
         }
+        _SEH2_END;
         Block = (PULONG)((ULONG_PTR)BaseAddress + (i * 4) % ChunkSize);
         BlockEnd = (PULONG)((ULONG_PTR)BaseAddress + ChunkSize);
 
@@ -486,7 +557,10 @@ CountAvailableClusters(
         else
             Status = FAT32CountAvailableClusters(DeviceExt);
     }
-    Clusters->QuadPart = DeviceExt->AvailableClusters;
+    if (Clusters != NULL)
+    {
+        Clusters->QuadPart = DeviceExt->AvailableClusters;
+    }
     ExReleaseResourceLite (&DeviceExt->FatResource);
 
     return Status;
@@ -510,10 +584,15 @@ FAT12WriteCluster(
     LARGE_INTEGER Offset;
 
     Offset.QuadPart = 0;
-    if (!CcPinRead(DeviceExt->FATFileObject, &Offset, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector, 1, &Context, &BaseAddress))
+    _SEH2_TRY
     {
-        return STATUS_UNSUCCESSFUL;
+        CcPinRead(DeviceExt->FATFileObject, &Offset, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector, PIN_WAIT, &Context, &BaseAddress);
     }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        _SEH2_YIELD(return _SEH2_GetExceptionCode());
+    }
+    _SEH2_END;
     CBlock = (PUCHAR)BaseAddress;
 
     FATOffset = (ClusterToWrite * 12) / 8;
@@ -559,10 +638,15 @@ FAT16WriteCluster(
     ChunkSize = CACHEPAGESIZE(DeviceExt);
     FATOffset = ClusterToWrite * 2;
     Offset.QuadPart = ROUND_DOWN(FATOffset, ChunkSize);
-    if (!CcPinRead(DeviceExt->FATFileObject, &Offset, ChunkSize, 1, &Context, &BaseAddress))
+    _SEH2_TRY
+    {
+        CcPinRead(DeviceExt->FATFileObject, &Offset, ChunkSize, PIN_WAIT, &Context, &BaseAddress);
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
     {
-        return STATUS_UNSUCCESSFUL;
+        _SEH2_YIELD(return _SEH2_GetExceptionCode());
     }
+    _SEH2_END;
 
     DPRINT("Writing 0x%x for offset 0x%x 0x%x\n", NewValue, FATOffset,
            ClusterToWrite);
@@ -595,10 +679,15 @@ FAT32WriteCluster(
 
     FATOffset = (ClusterToWrite * 4);
     Offset.QuadPart = ROUND_DOWN(FATOffset, ChunkSize);
-    if (!CcPinRead(DeviceExt->FATFileObject, &Offset, ChunkSize, 1, &Context, &BaseAddress))
+    _SEH2_TRY
     {
-        return STATUS_UNSUCCESSFUL;
+        CcPinRead(DeviceExt->FATFileObject, &Offset, ChunkSize, PIN_WAIT, &Context, &BaseAddress);
     }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        _SEH2_YIELD(return _SEH2_GetExceptionCode());
+    }
+    _SEH2_END;
 
     DPRINT("Writing 0x%x for offset 0x%x 0x%x\n", NewValue, FATOffset,
            ClusterToWrite);
@@ -667,7 +756,12 @@ GetNextCluster(
            DeviceExt, CurrentCluster);
 
     if (CurrentCluster == 0)
-        return STATUS_INVALID_PARAMETER;
+    {
+        DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n");
+        if (VfatGlobalData->Flags & VFAT_BREAK_ON_CORRUPTION)
+            ASSERT(CurrentCluster != 0);
+        return STATUS_FILE_CORRUPT_ERROR;
+    }
 
     ExAcquireResourceSharedLite(&DeviceExt->FatResource, TRUE);
     Status = DeviceExt->GetNextCluster(DeviceExt, CurrentCluster, NextCluster);
@@ -734,4 +828,472 @@ GetNextClusterExtend(
     return Status;
 }
 
+/*
+ * FUNCTION: Retrieve the dirty status
+ */
+NTSTATUS
+GetDirtyStatus(
+    PDEVICE_EXTENSION DeviceExt,
+    PBOOLEAN DirtyStatus)
+{
+    NTSTATUS Status;
+
+    DPRINT("GetDirtyStatus(DeviceExt %p)\n", DeviceExt);
+
+    /* FAT12 has no dirty bit */
+    if (DeviceExt->FatInfo.FatType == FAT12)
+    {
+        *DirtyStatus = FALSE;
+        return STATUS_SUCCESS;
+    }
+
+    /* Not really in the FAT, but share the lock because
+     * we're really low-level and shouldn't happent that often
+     * And call the appropriate function
+     */
+    ExAcquireResourceSharedLite(&DeviceExt->FatResource, TRUE);
+    Status = DeviceExt->GetDirtyStatus(DeviceExt, DirtyStatus);
+    ExReleaseResourceLite(&DeviceExt->FatResource);
+
+    return Status;
+}
+
+NTSTATUS
+FAT16GetDirtyStatus(
+    PDEVICE_EXTENSION DeviceExt,
+    PBOOLEAN DirtyStatus)
+{
+    LARGE_INTEGER Offset;
+    ULONG Length;
+#ifdef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
+    NTSTATUS Status;
+#else
+    PVOID Context;
+#endif
+    struct _BootSector * Sector;
+
+    /* We'll read the bootsector at 0 */
+    Offset.QuadPart = 0;
+    Length = DeviceExt->FatInfo.BytesPerSector;
+#ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
+    /* Go through Cc for this */
+    _SEH2_TRY
+    {
+        CcPinRead(DeviceExt->VolumeFcb->FileObject, &Offset, Length, PIN_WAIT, &Context, (PVOID *)&Sector);
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        _SEH2_YIELD(return _SEH2_GetExceptionCode());
+    }
+    _SEH2_END;
+#else
+    /* No Cc, do it the old way:
+     * - Allocate a big enough buffer
+     * - And read the disk
+     */
+    Sector = ExAllocatePoolWithTag(NonPagedPool, Length, TAG_BUFFER);
+    if (Sector == NULL)
+    {
+        *DirtyStatus = TRUE;
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    Status = VfatReadDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
+    if  (!NT_SUCCESS(Status))
+    {
+        *DirtyStatus = TRUE;
+        ExFreePoolWithTag(Sector, TAG_BUFFER);
+        return Status;
+    }
+#endif
+
+    /* Make sure we have a boot sector...
+     * FIXME: This check is a bit lame and should be improved
+     */
+    if (Sector->Signatur1 != 0xaa55)
+    {
+        /* Set we are dirty so that we don't attempt anything */
+        *DirtyStatus = TRUE;
+#ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
+        CcUnpinData(Context);
+#else
+        ExFreePoolWithTag(Sector, TAG_BUFFER);
+#endif
+        return STATUS_DISK_CORRUPT_ERROR;
+    }
+
+    /* Return the status of the dirty bit */
+    if (Sector->Res1 & FAT_DIRTY_BIT)
+        *DirtyStatus = TRUE;
+    else
+        *DirtyStatus = FALSE;
+
+#ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
+    CcUnpinData(Context);
+#else
+    ExFreePoolWithTag(Sector, TAG_BUFFER);
+#endif
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+FAT32GetDirtyStatus(
+    PDEVICE_EXTENSION DeviceExt,
+    PBOOLEAN DirtyStatus)
+{
+    LARGE_INTEGER Offset;
+    ULONG Length;
+#ifdef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
+    NTSTATUS Status;
+#else
+    PVOID Context;
+#endif
+    struct _BootSector32 * Sector;
+
+    /* We'll read the bootsector at 0 */
+    Offset.QuadPart = 0;
+    Length = DeviceExt->FatInfo.BytesPerSector;
+#ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
+    /* Go through Cc for this */
+    _SEH2_TRY
+    {
+        CcPinRead(DeviceExt->VolumeFcb->FileObject, &Offset, Length, PIN_WAIT, &Context, (PVOID *)&Sector);
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        _SEH2_YIELD(return _SEH2_GetExceptionCode());
+    }
+    _SEH2_END;
+#else
+    /* No Cc, do it the old way:
+     * - Allocate a big enough buffer
+     * - And read the disk
+     */
+    Sector = ExAllocatePoolWithTag(NonPagedPool, Length, TAG_BUFFER);
+    if (Sector == NULL)
+    {
+        *DirtyStatus = TRUE;
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    Status = VfatReadDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
+    if  (!NT_SUCCESS(Status))
+    {
+        *DirtyStatus = TRUE;
+        ExFreePoolWithTag(Sector, TAG_BUFFER);
+        return Status;
+    }
+#endif
+
+    /* Make sure we have a boot sector...
+     * FIXME: This check is a bit lame and should be improved
+     */
+    if (Sector->Signature1 != 0xaa55)
+    {
+        /* Set we are dirty so that we don't attempt anything */
+        *DirtyStatus = TRUE;
+#ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
+        CcUnpinData(Context);
+#else
+        ExFreePoolWithTag(Sector, TAG_BUFFER);
+#endif
+        return STATUS_DISK_CORRUPT_ERROR;
+    }
+
+    /* Return the status of the dirty bit */
+    if (Sector->Res4 & FAT_DIRTY_BIT)
+        *DirtyStatus = TRUE;
+    else
+        *DirtyStatus = FALSE;
+
+#ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
+    CcUnpinData(Context);
+#else
+    ExFreePoolWithTag(Sector, TAG_BUFFER);
+#endif
+    return STATUS_SUCCESS;
+}
+
+/*
+ * FUNCTION: Set the dirty status
+ */
+NTSTATUS
+SetDirtyStatus(
+    PDEVICE_EXTENSION DeviceExt,
+    BOOLEAN DirtyStatus)
+{
+    NTSTATUS Status;
+
+    DPRINT("SetDirtyStatus(DeviceExt %p, DirtyStatus %d)\n", DeviceExt, DirtyStatus);
+
+    /* FAT12 has no dirty bit */
+    if (DeviceExt->FatInfo.FatType == FAT12)
+    {
+        return STATUS_SUCCESS;
+    }
+
+    /* Not really in the FAT, but share the lock because
+     * we're really low-level and shouldn't happent that often
+     * And call the appropriate function
+     * Acquire exclusive because we will modify ondisk value
+     */
+    ExAcquireResourceExclusiveLite(&DeviceExt->FatResource, TRUE);
+    Status = DeviceExt->SetDirtyStatus(DeviceExt, DirtyStatus);
+    ExReleaseResourceLite(&DeviceExt->FatResource);
+
+    return Status;
+}
+
+NTSTATUS
+FAT16SetDirtyStatus(
+    PDEVICE_EXTENSION DeviceExt,
+    BOOLEAN DirtyStatus)
+{
+    LARGE_INTEGER Offset;
+    ULONG Length;
+#ifdef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
+    NTSTATUS Status;
+#else
+    PVOID Context;
+#endif
+    struct _BootSector * Sector;
+
+    /* We'll read (and then write) the bootsector at 0 */
+    Offset.QuadPart = 0;
+    Length = DeviceExt->FatInfo.BytesPerSector;
+#ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
+    /* Go through Cc for this */
+    _SEH2_TRY
+    {
+        CcPinRead(DeviceExt->VolumeFcb->FileObject, &Offset, Length, PIN_WAIT, &Context, (PVOID *)&Sector);
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        _SEH2_YIELD(return _SEH2_GetExceptionCode());
+    }
+    _SEH2_END;
+#else
+    /* No Cc, do it the old way:
+     * - Allocate a big enough buffer
+     * - And read the disk
+     */
+    Sector = ExAllocatePoolWithTag(NonPagedPool, Length, TAG_BUFFER);
+    if (Sector == NULL)
+    {
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    Status = VfatReadDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
+    if  (!NT_SUCCESS(Status))
+    {
+        ExFreePoolWithTag(Sector, TAG_BUFFER);
+        return Status;
+    }
+#endif
+
+    /* Make sure we have a boot sector...
+     * FIXME: This check is a bit lame and should be improved
+     */
+    if (Sector->Signatur1 != 0xaa55)
+    {
+#ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
+        CcUnpinData(Context);
+#else
+        ExFreePoolWithTag(Sector, TAG_BUFFER);
+#endif
+        return STATUS_DISK_CORRUPT_ERROR;
+    }
+
+    /* Modify the dirty bit status according
+     * to caller needs
+     */
+    if (!DirtyStatus)
+    {
+        Sector->Res1 &= ~FAT_DIRTY_BIT;
+    }
+    else
+    {
+        Sector->Res1 |= FAT_DIRTY_BIT;
+    }
+
+#ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
+    /* Mark boot sector dirty so that it gets written to the disk */
+    CcSetDirtyPinnedData(Context, NULL);
+    CcUnpinData(Context);
+    return STATUS_SUCCESS;
+#else
+    /* Write back the boot sector to the disk */
+    Status = VfatWriteDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
+    ExFreePoolWithTag(Sector, TAG_BUFFER);
+    return Status;
+#endif
+}
+
+NTSTATUS
+FAT32SetDirtyStatus(
+    PDEVICE_EXTENSION DeviceExt,
+    BOOLEAN DirtyStatus)
+{
+    LARGE_INTEGER Offset;
+    ULONG Length;
+#ifdef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
+    NTSTATUS Status;
+#else
+    PVOID Context;
+#endif
+    struct _BootSector32 * Sector;
+
+    /* We'll read (and then write) the bootsector at 0 */
+    Offset.QuadPart = 0;
+    Length = DeviceExt->FatInfo.BytesPerSector;
+#ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
+    /* Go through Cc for this */
+    _SEH2_TRY
+    {
+        CcPinRead(DeviceExt->VolumeFcb->FileObject, &Offset, Length, PIN_WAIT, &Context, (PVOID *)&Sector);
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        _SEH2_YIELD(return _SEH2_GetExceptionCode());
+    }
+    _SEH2_END;
+#else
+    /* No Cc, do it the old way:
+     * - Allocate a big enough buffer
+     * - And read the disk
+     */
+    Sector = ExAllocatePoolWithTag(NonPagedPool, Length, TAG_BUFFER);
+    if (Sector == NULL)
+    {
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    Status = VfatReadDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
+    if  (!NT_SUCCESS(Status))
+    {
+        ExFreePoolWithTag(Sector, TAG_BUFFER);
+        return Status;
+    }
+#endif
+
+    /* Make sure we have a boot sector...
+     * FIXME: This check is a bit lame and should be improved
+     */
+    if (Sector->Signature1 != 0xaa55)
+    {
+        ASSERT(FALSE);
+#ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
+        CcUnpinData(Context);
+#else
+        ExFreePoolWithTag(Sector, TAG_BUFFER);
+#endif
+        return STATUS_DISK_CORRUPT_ERROR;
+    }
+
+    /* Modify the dirty bit status according
+     * to caller needs
+     */
+    if (!DirtyStatus)
+    {
+        Sector->Res4 &= ~FAT_DIRTY_BIT;
+    }
+    else
+    {
+        Sector->Res4 |= FAT_DIRTY_BIT;
+    }
+
+#ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
+    /* Mark boot sector dirty so that it gets written to the disk */
+    CcSetDirtyPinnedData(Context, NULL);
+    CcUnpinData(Context);
+    return STATUS_SUCCESS;
+#else
+    /* Write back the boot sector to the disk */
+    Status = VfatWriteDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
+    ExFreePoolWithTag(Sector, TAG_BUFFER);
+    return Status;
+#endif
+}
+
+NTSTATUS
+FAT32UpdateFreeClustersCount(
+    PDEVICE_EXTENSION DeviceExt)
+{
+    LARGE_INTEGER Offset;
+    ULONG Length;
+#ifdef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
+    NTSTATUS Status;
+#else
+    PVOID Context;
+#endif
+    struct _FsInfoSector * Sector;
+
+    if (!DeviceExt->AvailableClustersValid)
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* We'll read (and then write) the fsinfo sector */
+    Offset.QuadPart = DeviceExt->FatInfo.FSInfoSector * DeviceExt->FatInfo.BytesPerSector;
+    Length = DeviceExt->FatInfo.BytesPerSector;
+#ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
+    /* Go through Cc for this */
+    _SEH2_TRY
+    {
+        CcPinRead(DeviceExt->VolumeFcb->FileObject, &Offset, Length, PIN_WAIT, &Context, (PVOID *)&Sector);
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        _SEH2_YIELD(return _SEH2_GetExceptionCode());
+    }
+    _SEH2_END;
+#else
+    /* No Cc, do it the old way:
+     * - Allocate a big enough buffer
+     * - And read the disk
+     */
+    Sector = ExAllocatePoolWithTag(NonPagedPool, Length, TAG_BUFFER);
+    if (Sector == NULL)
+    {
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    Status = VfatReadDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
+    if  (!NT_SUCCESS(Status))
+    {
+        ExFreePoolWithTag(Sector, TAG_BUFFER);
+        return Status;
+    }
+#endif
+
+    /* Make sure we have a FSINFO sector */
+    if (Sector->ExtBootSignature2 != 0x41615252 ||
+        Sector->FSINFOSignature != 0x61417272 ||
+        Sector->Signatur2 != 0xaa550000)
+    {
+        ASSERT(FALSE);
+#ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
+        CcUnpinData(Context);
+#else
+        ExFreePoolWithTag(Sector, TAG_BUFFER);
+#endif
+        return STATUS_DISK_CORRUPT_ERROR;
+    }
+
+    /* Update the free clusters count */
+    Sector->FreeCluster = InterlockedCompareExchange((PLONG)&DeviceExt->AvailableClusters, 0, 0);
+
+#ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
+    /* Mark FSINFO sector dirty so that it gets written to the disk */
+    CcSetDirtyPinnedData(Context, NULL);
+    CcUnpinData(Context);
+    return STATUS_SUCCESS;
+#else
+    /* Write back the FSINFO sector to the disk */
+    Status = VfatWriteDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
+    ExFreePoolWithTag(Sector, TAG_BUFFER);
+    return Status;
+#endif
+}
+
 /* EOF */