[FASTFAT] Don't leak statistics on dismount
[reactos.git] / drivers / filesystems / fastfat / fsctl.c
index abd7726..b70a84c 100644 (file)
@@ -232,6 +232,7 @@ VfatHasFileSystem(
                     FatInfo.RootCluster = ((struct _BootSector32*) Boot)->RootCluster;
                     FatInfo.rootStart = FatInfo.dataStart + ((FatInfo.RootCluster - 2) * FatInfo.SectorsPerCluster);
                     FatInfo.VolumeID = ((struct _BootSector32*) Boot)->VolumeID;
+                    FatInfo.FSInfoSector = ((struct _BootSector32*) Boot)->FSInfoSector;
                     RtlCopyMemory(&FatInfo.VolumeLabel, &((struct _BootSector32*)Boot)->VolumeLabel, sizeof(FatInfo.VolumeLabel));
                 }
                 else
@@ -531,9 +532,9 @@ VfatMount(
     UNICODE_STRING VolumeNameU = RTL_CONSTANT_STRING(L"\\$$Volume$$");
     UNICODE_STRING VolumeLabelU;
     ULONG HashTableSize;
-    ULONG eocMark;
     ULONG i;
     FATINFO FatInfo;
+    BOOLEAN Dirty;
 
     DPRINT("VfatMount(IrpContext %p)\n", IrpContext);
 
@@ -617,7 +618,9 @@ VfatMount(
             DeviceExt->GetNextCluster = FAT12GetNextCluster;
             DeviceExt->FindAndMarkAvailableCluster = FAT12FindAndMarkAvailableCluster;
             DeviceExt->WriteCluster = FAT12WriteCluster;
-            DeviceExt->CleanShutBitMask = 0;
+            /* We don't define dirty bit functions here
+             * FAT12 doesn't have such bit and they won't get called
+             */
             break;
 
         case FAT16:
@@ -625,7 +628,8 @@ VfatMount(
             DeviceExt->GetNextCluster = FAT16GetNextCluster;
             DeviceExt->FindAndMarkAvailableCluster = FAT16FindAndMarkAvailableCluster;
             DeviceExt->WriteCluster = FAT16WriteCluster;
-            DeviceExt->CleanShutBitMask = 0x8000;
+            DeviceExt->GetDirtyStatus = FAT16GetDirtyStatus;
+            DeviceExt->SetDirtyStatus = FAT16SetDirtyStatus;
             break;
 
         case FAT32:
@@ -633,7 +637,8 @@ VfatMount(
             DeviceExt->GetNextCluster = FAT32GetNextCluster;
             DeviceExt->FindAndMarkAvailableCluster = FAT32FindAndMarkAvailableCluster;
             DeviceExt->WriteCluster = FAT32WriteCluster;
-            DeviceExt->CleanShutBitMask = 0x80000000;
+            DeviceExt->GetDirtyStatus = FAT32GetDirtyStatus;
+            DeviceExt->SetDirtyStatus = FAT32SetDirtyStatus;
             break;
     }
 
@@ -672,7 +677,7 @@ VfatMount(
 
     DeviceExt->Statistics = ExAllocatePoolWithTag(NonPagedPool,
                                                   sizeof(STATISTICS) * VfatGlobalData->NumberProcessors,
-                                                  TAG_VFAT);
+                                                  TAG_STATS);
     if (DeviceExt->Statistics == NULL)
     {
         Status = STATUS_INSUFFICIENT_RESOURCES;
@@ -732,6 +737,7 @@ VfatMount(
     _SEH2_END;
 
     DeviceExt->LastAvailableCluster = 2;
+    CountAvailableClusters(DeviceExt, NULL);
     ExInitializeResourceLite(&DeviceExt->FatResource);
 
     InitializeListHead(&DeviceExt->FcbListHead);
@@ -763,20 +769,28 @@ VfatMount(
     ReadVolumeLabel(DeviceExt, 0, vfatVolumeIsFatX(DeviceExt), &VolumeLabelU);
     Vpb->VolumeLabelLength = VolumeLabelU.Length;
 
-    /* read clean shutdown bit status */
-    Status = GetNextCluster(DeviceExt, 1, &eocMark);
+    /* read dirty bit status */
+    Status = GetDirtyStatus(DeviceExt, &Dirty);
     if (NT_SUCCESS(Status))
     {
-        if (eocMark & DeviceExt->CleanShutBitMask)
+        /* The volume wasn't dirty, it was properly dismounted */
+        if (!Dirty)
         {
-            /* unset clean shutdown bit */
-            eocMark &= ~DeviceExt->CleanShutBitMask;
-            WriteCluster(DeviceExt, 1, eocMark);
+            /* Mark it dirty now! */
+            SetDirtyStatus(DeviceExt, TRUE);
             VolumeFcb->Flags |= VCB_CLEAR_DIRTY;
         }
+        else
+        {
+            DPRINT1("Mounting a dirty volume\n");
+        }
     }
 
     VolumeFcb->Flags |= VCB_IS_DIRTY;
+    if (BooleanFlagOn(Vpb->RealDevice->Flags, DO_SYSTEM_BOOT_PARTITION))
+    {
+        SetFlag(DeviceExt->Flags, VCB_IS_SYS_OR_HAS_PAGE);
+    }
 
     FsRtlNotifyVolumeEvent(DeviceExt->FATFileObject, FSRTL_VOLUME_MOUNT);
     FsRtlNotifyInitializeSync(&DeviceExt->NotifySync);
@@ -795,7 +809,7 @@ ByeBye:
         if (DeviceExt && DeviceExt->SpareVPB)
             ExFreePoolWithTag(DeviceExt->SpareVPB, TAG_VFAT);
         if (DeviceExt && DeviceExt->Statistics)
-            ExFreePoolWithTag(DeviceExt->Statistics, TAG_VFAT);
+            ExFreePoolWithTag(DeviceExt->Statistics, TAG_STATS);
         if (Fcb)
             vfatDestroyFCB(Fcb);
         if (Ccb)
@@ -1051,7 +1065,6 @@ NTSTATUS
 VfatMarkVolumeDirty(
     PVFAT_IRP_CONTEXT IrpContext)
 {
-    ULONG eocMark;
     PDEVICE_EXTENSION DeviceExt;
     NTSTATUS Status = STATUS_SUCCESS;
 
@@ -1060,13 +1073,7 @@ VfatMarkVolumeDirty(
 
     if (!BooleanFlagOn(DeviceExt->VolumeFcb->Flags, VCB_IS_DIRTY))
     {
-        Status = GetNextCluster(DeviceExt, 1, &eocMark);
-        if (NT_SUCCESS(Status))
-        {
-            /* unset clean shutdown bit */
-            eocMark &= ~DeviceExt->CleanShutBitMask;
-            Status = WriteCluster(DeviceExt, 1, eocMark);
-        }
+        Status = SetDirtyStatus(DeviceExt, TRUE);
     }
 
     DeviceExt->VolumeFcb->Flags &= ~VCB_CLEAR_DIRTY;
@@ -1117,6 +1124,70 @@ VfatLockOrUnlockVolume(
     {
         PLIST_ENTRY ListEntry;
 
+#if 1
+        /* FIXME: Hack that allows locking the system volume on
+         * boot so that autochk can run properly
+         * That hack is, on purpose, really restrictive
+         * it will only allow locking with two directories
+         * open: current directory of smss and autochk.
+         */
+        BOOLEAN ForceLock = TRUE;
+        ULONG HandleCount = 0;
+
+        /* Only allow boot volume */
+        if (BooleanFlagOn(DeviceExt->Flags, VCB_IS_SYS_OR_HAS_PAGE))
+        {
+            /* We'll browse all the FCB */
+            ListEntry = DeviceExt->FcbListHead.Flink;
+            while (ListEntry != &DeviceExt->FcbListHead)
+            {
+                Fcb = CONTAINING_RECORD(ListEntry, VFATFCB, FcbListEntry);
+                ListEntry = ListEntry->Flink;
+
+                /* If no handle: that FCB is no problem for locking
+                 * so ignore it
+                 */
+                if (Fcb->OpenHandleCount == 0)
+                {
+                    continue;
+                }
+
+                /* Not a dir? We're no longer at boot */
+                if (!vfatFCBIsDirectory(Fcb))
+                {
+                    ForceLock = FALSE;
+                    break;
+                }
+
+                /* If we have cached initialized and several handles, we're
+                   not in the boot case
+                 */
+                if (Fcb->FileObject != NULL && Fcb->OpenHandleCount > 1)
+                {
+                    ForceLock = FALSE;
+                    break;
+                }
+
+                /* Count the handles */
+                HandleCount += Fcb->OpenHandleCount;
+                /* More than two handles? Then, we're not booting anymore */
+                if (HandleCount > 2)
+                {
+                    ForceLock = FALSE;
+                    break;
+                }
+            }
+        }
+        else
+        {
+            ForceLock = FALSE;
+        }
+
+        /* Here comes the hack, ignore the failure! */
+        if (!ForceLock)
+        {
+#endif
+
         DPRINT1("Can't lock: %u opened\n", DeviceExt->OpenHandleCount);
 
         ListEntry = DeviceExt->FcbListHead.Flink;
@@ -1132,6 +1203,17 @@ VfatLockOrUnlockVolume(
         }
 
         return STATUS_ACCESS_DENIED;
+
+#if 1
+        /* End of the hack: be verbose about its usage,
+         * just in case we would mess up everything!
+         */
+        }
+        else
+        {
+            DPRINT1("HACK: Using lock-hack!\n");
+        }
+#endif
     }
 
     /* Finally, proceed */
@@ -1158,8 +1240,6 @@ VfatDismountVolume(
     PLIST_ENTRY NextEntry;
     PVFATFCB Fcb;
     PFILE_OBJECT FileObject;
-    ULONG eocMark;
-    NTSTATUS Status;
 
     DPRINT("VfatDismountVolume(%p)\n", IrpContext);
 
@@ -1174,6 +1254,12 @@ VfatDismountVolume(
         return STATUS_ACCESS_DENIED;
     }
 
+    /* Deny dismount of boot volume */
+    if (BooleanFlagOn(DeviceExt->Flags, VCB_IS_SYS_OR_HAS_PAGE))
+    {
+        return STATUS_ACCESS_DENIED;
+    }
+
     /* Race condition? */
     if (BooleanFlagOn(DeviceExt->Flags, VCB_DISMOUNT_PENDING))
     {
@@ -1185,16 +1271,12 @@ VfatDismountVolume(
 
     ExAcquireResourceExclusiveLite(&DeviceExt->FatResource, TRUE);
 
+    /* We're performing a clean shutdown */
     if (BooleanFlagOn(DeviceExt->VolumeFcb->Flags, VCB_CLEAR_DIRTY))
     {
-        /* Set clean shutdown bit */
-        Status = GetNextCluster(DeviceExt, 1, &eocMark);
-        if (NT_SUCCESS(Status))
-        {
-            eocMark |= DeviceExt->CleanShutBitMask;
-            if (NT_SUCCESS(WriteCluster(DeviceExt, 1, eocMark)))
-                DeviceExt->VolumeFcb->Flags &= ~VCB_IS_DIRTY;
-        }
+        /* Drop the dirty bit */
+        if (NT_SUCCESS(SetDirtyStatus(DeviceExt, FALSE)))
+            DeviceExt->VolumeFcb->Flags &= ~VCB_IS_DIRTY;
     }
 
     /* Flush volume & files */
@@ -1217,6 +1299,7 @@ VfatDismountVolume(
     ExReleaseResourceLite(&DeviceExt->FatResource);
 
     /* Release a few resources and quit, we're done */
+    ExFreePoolWithTag(DeviceExt->Statistics, TAG_STATS);
     ExDeleteResourceLite(&DeviceExt->DirResource);
     ExDeleteResourceLite(&DeviceExt->FatResource);
     ObDereferenceObject(DeviceExt->FATFileObject);