[FASTFAT] Use the FastFAT mechanism for counting clusters already implemented
[reactos.git] / drivers / filesystems / fastfat / fsctl.c
index e196282..b849bf4 100644 (file)
@@ -30,6 +30,9 @@
 #define NDEBUG
 #include <debug.h>
 
+extern VFAT_DISPATCH FatXDispatch;
+extern VFAT_DISPATCH FatDispatch;
+
 /* FUNCTIONS ****************************************************************/
 
 #define  CACHEPAGESIZE(pDeviceExt) ((pDeviceExt)->FatInfo.BytesPerCluster > PAGE_SIZE ? \
@@ -40,7 +43,8 @@ NTSTATUS
 VfatHasFileSystem(
     PDEVICE_OBJECT DeviceToMount,
     PBOOLEAN RecognizedFS,
-    PFATINFO pFatInfo)
+    PFATINFO pFatInfo,
+    BOOLEAN Override)
 {
     NTSTATUS Status;
     PARTITION_INFORMATION PartitionInfo;
@@ -64,10 +68,10 @@ VfatHasFileSystem(
                                       0,
                                       &DiskGeometry,
                                       &Size,
-                                      FALSE);
+                                      Override);
     if (!NT_SUCCESS(Status))
     {
-        DPRINT("VfatBlockDeviceIoControl faild (%x)\n", Status);
+        DPRINT("VfatBlockDeviceIoControl failed (%x)\n", Status);
         return Status;
     }
 
@@ -82,14 +86,13 @@ VfatHasFileSystem(
                                           0,
                                           &PartitionInfo,
                                           &Size,
-                                          FALSE);
+                                          Override);
         if (!NT_SUCCESS(Status))
         {
-            DPRINT("VfatBlockDeviceIoControl faild (%x)\n", Status);
+            DPRINT("VfatBlockDeviceIoControl failed (%x)\n", Status);
             return Status;
         }
 
-        PartitionInfoIsValid = TRUE;
         DPRINT("Partition Information:\n");
         DPRINT("StartingOffset      %I64x\n", PartitionInfo.StartingOffset.QuadPart  / 512);
         DPRINT("PartitionLength     %I64x\n", PartitionInfo.PartitionLength.QuadPart / 512);
@@ -108,6 +111,7 @@ VfatHasFileSystem(
                 PartitionInfo.PartitionType == PARTITION_FAT32_XINT13 ||
                 PartitionInfo.PartitionType == PARTITION_XINT13)
             {
+                 PartitionInfoIsValid = TRUE;
                 *RecognizedFS = TRUE;
             }
         }
@@ -117,19 +121,10 @@ VfatHasFileSystem(
                  PartitionInfo.PartitionLength.QuadPart > 0)
         {
             /* This is possible a removable media formated as super floppy */
+            PartitionInfoIsValid = TRUE;
             *RecognizedFS = TRUE;
         }
     }
-    else if (DiskGeometry.MediaType == Unknown)
-    {
-        /*
-         * Floppy disk driver can return Unknown as media type if it
-         * doesn't know yet what floppy in the drive really is. This is
-         * perfectly correct to do under Windows.
-         */
-        *RecognizedFS = TRUE;
-        DiskGeometry.BytesPerSector = 512;
-    }
     else
     {
         *RecognizedFS = TRUE;
@@ -146,7 +141,7 @@ VfatHasFileSystem(
         Offset.QuadPart = 0;
 
         /* Try to recognize FAT12/FAT16/FAT32 partitions */
-        Status = VfatReadDisk(DeviceToMount, &Offset, DiskGeometry.BytesPerSector, (PUCHAR) Boot, FALSE);
+        Status = VfatReadDisk(DeviceToMount, &Offset, DiskGeometry.BytesPerSector, (PUCHAR) Boot, Override);
         if (NT_SUCCESS(Status))
         {
             if (Boot->Signatur1 != 0xaa55)
@@ -228,6 +223,7 @@ VfatHasFileSystem(
                     DPRINT("FAT12\n");
                     FatInfo.FatType = FAT12;
                     FatInfo.RootCluster = (FatInfo.rootStart - 1) / FatInfo.SectorsPerCluster;
+                    RtlCopyMemory(&FatInfo.VolumeLabel, &Boot->VolumeLabel, sizeof(FatInfo.VolumeLabel));
                 }
                 else if (FatInfo.NumberOfClusters >= 65525)
                 {
@@ -236,12 +232,15 @@ 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
                 {
                     DPRINT("FAT16\n");
                     FatInfo.FatType = FAT16;
                     FatInfo.RootCluster = FatInfo.rootStart / FatInfo.SectorsPerCluster;
+                    RtlCopyMemory(&FatInfo.VolumeLabel, &Boot->VolumeLabel, sizeof(FatInfo.VolumeLabel));
                 }
 
                 if (PartitionInfoIsValid &&
@@ -272,7 +271,7 @@ VfatHasFileSystem(
         Offset.QuadPart = 0;
 
         /* Try to recognize FATX16/FATX32 partitions (Xbox) */
-        Status = VfatReadDisk(DeviceToMount, &Offset, sizeof(struct _BootSectorFatX), (PUCHAR) BootFatX, FALSE);
+        Status = VfatReadDisk(DeviceToMount, &Offset, sizeof(struct _BootSectorFatX), (PUCHAR) BootFatX, Override);
         if (NT_SUCCESS(Status))
         {
             *RecognizedFS = TRUE;
@@ -341,25 +340,172 @@ VfatHasFileSystem(
 }
 
 /*
- * FUNCTION: Mounts the device
+ * FUNCTION: Read the volume label
+ * WARNING: Read this comment carefully before using it (and using it wrong)
+ * Device parameter is expected to be the lower DO is start isn't 0
+ *                  otherwise, it is expected to be the VCB is start is 0
+ * Start parameter is expected to be, in bytes, the beginning of the root start.
+ *                 Set it to 0 if you wish to use the associated FCB with caching.
+ *                 In that specific case, Device parameter is expected to be the VCB!
+ * VolumeLabel parameter is expected to be a preallocated UNICODE_STRING (ie, with buffer)
+ *                       Its buffer has to be able to contain MAXIMUM_VOLUME_LABEL_LENGTH bytes 
  */
 static
 NTSTATUS
-VfatMountDevice(
-    PDEVICE_EXTENSION DeviceExt,
-    PDEVICE_OBJECT DeviceToMount)
+ReadVolumeLabel(
+    PVOID Device,
+    ULONG Start,
+    BOOLEAN IsFatX,
+    PUNICODE_STRING VolumeLabel)
 {
-    NTSTATUS Status;
-    BOOLEAN RecognizedFS;
+    PDEVICE_EXTENSION DeviceExt;
+    PDEVICE_OBJECT DeviceObject;
+    PVOID Context = NULL;
+    ULONG DirIndex = 0;
+    PDIR_ENTRY Entry;
+    PVFATFCB pFcb;
+    LARGE_INTEGER FileOffset;
+    ULONG SizeDirEntry;
+    ULONG EntriesPerPage;
+    OEM_STRING StringO;
+    BOOLEAN NoCache = (Start != 0);
+    PVOID Buffer;
+    NTSTATUS Status = STATUS_SUCCESS;
 
-    DPRINT("Mounting VFAT device...\n");
+    if (IsFatX)
+    {
+        SizeDirEntry = sizeof(FATX_DIR_ENTRY);
+        EntriesPerPage = FATX_ENTRIES_PER_PAGE;
+    }
+    else
+    {
+        SizeDirEntry = sizeof(FAT_DIR_ENTRY);
+        EntriesPerPage = FAT_ENTRIES_PER_PAGE;
+    }
 
-    Status = VfatHasFileSystem(DeviceToMount, &RecognizedFS, &DeviceExt->FatInfo);
-    if (!NT_SUCCESS(Status))
+    FileOffset.QuadPart = Start;
+    if (!NoCache)
     {
-        return Status;
+        DeviceExt = Device;
+
+        /* FIXME: Check we really have a VCB
+        ASSERT();
+        */
+
+        ExAcquireResourceExclusiveLite(&DeviceExt->DirResource, TRUE);
+        pFcb = vfatOpenRootFCB(DeviceExt);
+        ExReleaseResourceLite(&DeviceExt->DirResource);
+
+        _SEH2_TRY
+        {
+            CcMapData(pFcb->FileObject, &FileOffset, SizeDirEntry, MAP_WAIT, &Context, (PVOID*)&Entry);
+        }
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+        {
+            Status = _SEH2_GetExceptionCode();
+        }
+        _SEH2_END;
+    }
+    else
+    {
+        DeviceObject = Device;
+
+        ASSERT(DeviceObject->Type == 3);
+
+        Buffer = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, TAG_VFAT);
+        if (Buffer != NULL)
+        {
+            Status = VfatReadDisk(DeviceObject, &FileOffset, PAGE_SIZE, (PUCHAR)Buffer, TRUE);
+            if (!NT_SUCCESS(Status))
+            {
+                ExFreePoolWithTag(Buffer, TAG_VFAT);
+            }
+            else
+            {
+                Entry = Buffer;
+            }
+        }
+        else
+        {
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+        }
+    }
+
+    if (NT_SUCCESS(Status))
+    {
+        while (TRUE)
+        {
+            if (ENTRY_VOLUME(IsFatX, Entry))
+            {
+                /* copy volume label */
+                if (IsFatX)
+                {
+                    StringO.Buffer = (PCHAR)Entry->FatX.Filename;
+                    StringO.MaximumLength = StringO.Length = Entry->FatX.FilenameLength;
+                    RtlOemStringToUnicodeString(VolumeLabel, &StringO, FALSE);
+                }
+                else
+                {
+                    vfat8Dot3ToString(&Entry->Fat, VolumeLabel);
+                }
+                break;
+            }
+            if (ENTRY_END(IsFatX, Entry))
+            {
+                break;
+            }
+            DirIndex++;
+            Entry = (PDIR_ENTRY)((ULONG_PTR)Entry + SizeDirEntry);
+            if ((DirIndex % EntriesPerPage) == 0)
+            {
+                FileOffset.u.LowPart += PAGE_SIZE;
+
+                if (!NoCache)
+                {
+                    CcUnpinData(Context);
+
+                    _SEH2_TRY
+                    {
+                        CcMapData(pFcb->FileObject, &FileOffset, SizeDirEntry, MAP_WAIT, &Context, (PVOID*)&Entry);
+                    }
+                    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+                    {
+                        Status = _SEH2_GetExceptionCode();
+                    }
+                    _SEH2_END;
+                    if (!NT_SUCCESS(Status))
+                    {
+                        Context = NULL;
+                        break;
+                    }
+                }
+                else
+                {
+                    Status = VfatReadDisk(DeviceObject, &FileOffset, PAGE_SIZE, (PUCHAR)Buffer, TRUE);
+                    if (!NT_SUCCESS(Status))
+                    {
+                        break;
+                    }
+                    Entry = Buffer;
+                }
+            }
+        }
+        if (Context)
+        {
+            CcUnpinData(Context);
+        }
+        else if (NoCache)
+        {
+            ExFreePoolWithTag(Buffer, TAG_VFAT);
+        }
+    }
+
+    if (!NoCache)
+    {
+        ExAcquireResourceExclusiveLite(&DeviceExt->DirResource, TRUE);
+        vfatReleaseFCB(DeviceExt, pFcb);
+        ExReleaseResourceLite(&DeviceExt->DirResource);
     }
-    DPRINT("MountVfatdev %u, PAGE_SIZE = %d\n", DeviceExt->FatInfo.BytesPerCluster, PAGE_SIZE);
 
     return STATUS_SUCCESS;
 }
@@ -384,9 +530,11 @@ VfatMount(
     PVPB Vpb;
     UNICODE_STRING NameU = RTL_CONSTANT_STRING(L"\\$$Fat$$");
     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);
 
@@ -401,7 +549,7 @@ VfatMount(
     DeviceToMount = IrpContext->Stack->Parameters.MountVolume.DeviceObject;
     Vpb = IrpContext->Stack->Parameters.MountVolume.Vpb;
 
-    Status = VfatHasFileSystem(DeviceToMount, &RecognizedFS, &FatInfo);
+    Status = VfatHasFileSystem(DeviceToMount, &RecognizedFS, &FatInfo, FALSE);
     if (!NT_SUCCESS(Status))
     {
         goto ByeBye;
@@ -428,7 +576,6 @@ VfatMount(
     {
         HashTableSize = 65537; // 65536 = 64 * 1024;
     }
-    HashTableSize = FCB_HASH_TABLE_SIZE;
     DPRINT("VFAT: Recognized volume\n");
     Status = IoCreateDevice(VfatGlobalData->DriverObject,
                             ROUND_UP(sizeof (DEVICE_EXTENSION), sizeof(ULONG)) + sizeof(HASHENTRY*) * HashTableSize,
@@ -442,22 +589,17 @@ VfatMount(
         goto ByeBye;
     }
 
-    DeviceObject->Flags = DeviceObject->Flags | DO_DIRECT_IO;
-    DeviceExt = (PVOID) DeviceObject->DeviceExtension;
+    DeviceExt = DeviceObject->DeviceExtension;
     RtlZeroMemory(DeviceExt, ROUND_UP(sizeof(DEVICE_EXTENSION), sizeof(ULONG)) + sizeof(HASHENTRY*) * HashTableSize);
     DeviceExt->FcbHashTable = (HASHENTRY**)((ULONG_PTR)DeviceExt + ROUND_UP(sizeof(DEVICE_EXTENSION), sizeof(ULONG)));
     DeviceExt->HashTableSize = HashTableSize;
+    DeviceExt->VolumeDevice = DeviceObject;
 
     /* use same vpb as device disk */
     DeviceObject->Vpb = Vpb;
     DeviceToMount->Vpb = Vpb;
 
-    Status = VfatMountDevice(DeviceExt, DeviceToMount);
-    if (!NT_SUCCESS(Status))
-    {
-        /* FIXME: delete device object */
-        goto ByeBye;
-    }
+    RtlCopyMemory(&DeviceExt->FatInfo, &FatInfo, sizeof(FATINFO));
 
     DPRINT("BytesPerSector:     %u\n", DeviceExt->FatInfo.BytesPerSector);
     DPRINT("SectorsPerCluster:  %u\n", DeviceExt->FatInfo.SectorsPerCluster);
@@ -476,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:
@@ -484,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:
@@ -492,7 +637,8 @@ VfatMount(
             DeviceExt->GetNextCluster = FAT32GetNextCluster;
             DeviceExt->FindAndMarkAvailableCluster = FAT32FindAndMarkAvailableCluster;
             DeviceExt->WriteCluster = FAT32WriteCluster;
-            DeviceExt->CleanShutBitMask = 0x80000000;
+            DeviceExt->GetDirtyStatus = FAT32GetDirtyStatus;
+            DeviceExt->SetDirtyStatus = FAT32SetDirtyStatus;
             break;
     }
 
@@ -500,13 +646,13 @@ VfatMount(
         DeviceExt->FatInfo.FatType == FATX32)
     {
         DeviceExt->Flags |= VCB_IS_FATX;
-        DeviceExt->GetNextDirEntry = FATXGetNextDirEntry;
         DeviceExt->BaseDateYear = 2000;
+        RtlCopyMemory(&DeviceExt->Dispatch, &FatXDispatch, sizeof(VFAT_DISPATCH));
     }
     else
     {
-        DeviceExt->GetNextDirEntry = FATGetNextDirEntry;
         DeviceExt->BaseDateYear = 1980;
+        RtlCopyMemory(&DeviceExt->Dispatch, &FatDispatch, sizeof(VFAT_DISPATCH));
     }
 
     DeviceExt->StorageDevice = DeviceToMount;
@@ -521,6 +667,31 @@ VfatMount(
     /* Initialize this resource early ... it's used in VfatCleanup */
     ExInitializeResourceLite(&DeviceExt->DirResource);
 
+    DeviceExt->IoVPB = DeviceObject->Vpb;
+    DeviceExt->SpareVPB = ExAllocatePoolWithTag(NonPagedPool, sizeof(VPB), TAG_VFAT);
+    if (DeviceExt->SpareVPB == NULL)
+    {
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto ByeBye;
+    }
+
+    DeviceExt->Statistics = ExAllocatePoolWithTag(NonPagedPool,
+                                                  sizeof(STATISTICS) * VfatGlobalData->NumberProcessors,
+                                                  TAG_VFAT);
+    if (DeviceExt->Statistics == NULL)
+    {
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto ByeBye;
+    }
+
+    RtlZeroMemory(DeviceExt->Statistics, sizeof(STATISTICS) * VfatGlobalData->NumberProcessors);
+    for (i = 0; i < VfatGlobalData->NumberProcessors; ++i)
+    {
+        DeviceExt->Statistics[i].Base.FileSystemType = FILESYSTEM_STATISTICS_TYPE_FAT;
+        DeviceExt->Statistics[i].Base.Version = 1;
+        DeviceExt->Statistics[i].Base.SizeOfCompleteStructure = sizeof(STATISTICS);
+    }
+
     DeviceExt->FATFileObject = IoCreateStreamFileObject(NULL, DeviceExt->StorageDevice);
     Fcb = vfatNewFCB(DeviceExt, &NameU);
     if (Fcb == NULL)
@@ -550,13 +721,23 @@ VfatMount(
     Fcb->RFCB.ValidDataLength = Fcb->RFCB.FileSize;
     Fcb->RFCB.AllocationSize = Fcb->RFCB.FileSize;
 
-    CcInitializeCacheMap(DeviceExt->FATFileObject,
-                         (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
-                         TRUE,
-                         &VfatGlobalData->CacheMgrCallbacks,
-                         Fcb);
+    _SEH2_TRY
+    {
+        CcInitializeCacheMap(DeviceExt->FATFileObject,
+                             (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
+                             TRUE,
+                             &VfatGlobalData->CacheMgrCallbacks,
+                             Fcb);
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        Status = _SEH2_GetExceptionCode();
+        goto ByeBye;
+    }
+    _SEH2_END;
 
     DeviceExt->LastAvailableCluster = 2;
+    CountAvailableClusters(DeviceExt, NULL);
     ExInitializeResourceLite(&DeviceExt->FatResource);
 
     InitializeListHead(&DeviceExt->FcbListHead);
@@ -569,7 +750,7 @@ VfatMount(
     }
 
     VolumeFcb->Flags = FCB_IS_VOLUME;
-    VolumeFcb->RFCB.FileSize.QuadPart = DeviceExt->FatInfo.Sectors * DeviceExt->FatInfo.BytesPerSector;
+    VolumeFcb->RFCB.FileSize.QuadPart = (LONGLONG) DeviceExt->FatInfo.Sectors * DeviceExt->FatInfo.BytesPerSector;
     VolumeFcb->RFCB.ValidDataLength = VolumeFcb->RFCB.FileSize;
     VolumeFcb->RFCB.AllocationSize = VolumeFcb->RFCB.FileSize;
     DeviceExt->VolumeFcb = VolumeFcb;
@@ -582,27 +763,41 @@ VfatMount(
     DeviceObject->Vpb->SerialNumber = DeviceExt->FatInfo.VolumeID;
 
     /* read volume label */
-    ReadVolumeLabel(DeviceExt,  DeviceObject->Vpb);
-
-    /* read clean shutdown bit status */
-    Status = GetNextCluster(DeviceExt, 1, &eocMark);
+    VolumeLabelU.Buffer = DeviceObject->Vpb->VolumeLabel;
+    VolumeLabelU.Length = 0;
+    VolumeLabelU.MaximumLength = sizeof(DeviceObject->Vpb->VolumeLabel);
+    ReadVolumeLabel(DeviceExt, 0, vfatVolumeIsFatX(DeviceExt), &VolumeLabelU);
+    Vpb->VolumeLabelLength = VolumeLabelU.Length;
+
+    /* 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);
     InitializeListHead(&DeviceExt->NotifyList);
 
+    DPRINT("Mount success\n");
+
     Status = STATUS_SUCCESS;
 
 ByeBye:
@@ -611,6 +806,10 @@ ByeBye:
         /* Cleanup */
         if (DeviceExt && DeviceExt->FATFileObject)
             ObDereferenceObject (DeviceExt->FATFileObject);
+        if (DeviceExt && DeviceExt->SpareVPB)
+            ExFreePoolWithTag(DeviceExt->SpareVPB, TAG_VFAT);
+        if (DeviceExt && DeviceExt->Statistics)
+            ExFreePoolWithTag(DeviceExt->Statistics, TAG_VFAT);
         if (Fcb)
             vfatDestroyFCB(Fcb);
         if (Ccb)
@@ -632,42 +831,81 @@ VfatVerify(
     PVFAT_IRP_CONTEXT IrpContext)
 {
     PDEVICE_OBJECT DeviceToVerify;
-    NTSTATUS Status = STATUS_SUCCESS;
+    NTSTATUS Status;
     FATINFO FatInfo;
     BOOLEAN RecognizedFS;
-    PDEVICE_EXTENSION DeviceExt = IrpContext->DeviceExt;
+    PDEVICE_EXTENSION DeviceExt;
+    BOOLEAN AllowRaw;
+    PVPB Vpb;
+    ULONG ChangeCount, BufSize = sizeof(ChangeCount);
 
     DPRINT("VfatVerify(IrpContext %p)\n", IrpContext);
 
     DeviceToVerify = IrpContext->Stack->Parameters.VerifyVolume.DeviceObject;
-    Status = VfatBlockDeviceIoControl(DeviceToVerify,
+    DeviceExt = DeviceToVerify->DeviceExtension;
+    Vpb = IrpContext->Stack->Parameters.VerifyVolume.Vpb;
+    AllowRaw = BooleanFlagOn(IrpContext->Stack->Flags, SL_ALLOW_RAW_MOUNT);
+
+    if (!BooleanFlagOn(Vpb->RealDevice->Flags, DO_VERIFY_VOLUME))
+    {
+        DPRINT("Already verified\n");
+        return STATUS_SUCCESS;
+    }
+
+    Status = VfatBlockDeviceIoControl(DeviceExt->StorageDevice,
                                       IOCTL_DISK_CHECK_VERIFY,
                                       NULL,
                                       0,
-                                      NULL,
-                                      0,
+                                      &ChangeCount,
+                                      &BufSize,
                                       TRUE);
-    DeviceToVerify->Flags &= ~DO_VERIFY_VOLUME;
     if (!NT_SUCCESS(Status) && Status != STATUS_VERIFY_REQUIRED)
     {
         DPRINT("VfatBlockDeviceIoControl() failed (Status %lx)\n", Status);
-        Status = STATUS_WRONG_VOLUME;
+        Status = (AllowRaw ? STATUS_WRONG_VOLUME : Status);
     }
     else
     {
-        Status = VfatHasFileSystem(DeviceToVerify, &RecognizedFS, &FatInfo);
+        Status = VfatHasFileSystem(DeviceExt->StorageDevice, &RecognizedFS, &FatInfo, TRUE);
         if (!NT_SUCCESS(Status) || RecognizedFS == FALSE)
         {
-            Status = STATUS_WRONG_VOLUME;
+            if (NT_SUCCESS(Status) || AllowRaw)
+            {
+                Status = STATUS_WRONG_VOLUME;
+            }
         }
         else if (sizeof(FATINFO) == RtlCompareMemory(&FatInfo, &DeviceExt->FatInfo, sizeof(FATINFO)))
         {
-            /*
-             * FIXME:
-             *   Preformated floppy disks have very often a serial number of 0000:0000.
-             *   We should calculate a crc sum over the sectors from the root directory as secondary volume number.
-             *   Each write to the root directory must update this crc sum.
-             */
+            WCHAR BufferU[MAXIMUM_VOLUME_LABEL_LENGTH / sizeof(WCHAR)];
+            UNICODE_STRING VolumeLabelU;
+            UNICODE_STRING VpbLabelU;
+
+            VolumeLabelU.Buffer = BufferU;
+            VolumeLabelU.Length = 0;
+            VolumeLabelU.MaximumLength = sizeof(BufferU);
+            Status = ReadVolumeLabel(DeviceExt->StorageDevice, FatInfo.rootStart * FatInfo.BytesPerSector, (FatInfo.FatType >= FATX16), &VolumeLabelU);
+            if (!NT_SUCCESS(Status))
+            {
+                if (AllowRaw)
+                {
+                    Status = STATUS_WRONG_VOLUME;
+                }
+            }
+            else
+            {
+                VpbLabelU.Buffer = Vpb->VolumeLabel;
+                VpbLabelU.Length = Vpb->VolumeLabelLength;
+                VpbLabelU.MaximumLength = sizeof(Vpb->VolumeLabel);
+
+                if (RtlCompareUnicodeString(&VpbLabelU, &VolumeLabelU, FALSE) != 0)
+                {
+                    Status = STATUS_WRONG_VOLUME;
+                }
+                else
+                {
+                    DPRINT1("Same volume\n");
+                }
+            }
         }
         else
         {
@@ -675,6 +913,8 @@ VfatVerify(
         }
     }
 
+    Vpb->RealDevice->Flags &= ~DO_VERIFY_VOLUME;
+
     return Status;
 }
 
@@ -809,12 +1049,14 @@ VfatIsVolumeDirty(
     Flags = (PULONG)IrpContext->Irp->AssociatedIrp.SystemBuffer;
     *Flags = 0;
 
-    if ((IrpContext->DeviceExt->VolumeFcb->Flags & VCB_IS_DIRTY) &&
-        !(IrpContext->DeviceExt->VolumeFcb->Flags & VCB_CLEAR_DIRTY))
+    if (BooleanFlagOn(IrpContext->DeviceExt->VolumeFcb->Flags, VCB_IS_DIRTY) &&
+        !BooleanFlagOn(IrpContext->DeviceExt->VolumeFcb->Flags, VCB_CLEAR_DIRTY))
     {
         *Flags |= VOLUME_IS_DIRTY;
     }
 
+    IrpContext->Irp->IoStatus.Information = sizeof(ULONG);
+
     return STATUS_SUCCESS;
 }
 
@@ -823,25 +1065,283 @@ NTSTATUS
 VfatMarkVolumeDirty(
     PVFAT_IRP_CONTEXT IrpContext)
 {
-    ULONG eocMark;
     PDEVICE_EXTENSION DeviceExt;
     NTSTATUS Status = STATUS_SUCCESS;
 
     DPRINT("VfatMarkVolumeDirty(IrpContext %p)\n", IrpContext);
     DeviceExt = IrpContext->DeviceExt;
 
-    if (!(DeviceExt->VolumeFcb->Flags & VCB_IS_DIRTY))
+    if (!BooleanFlagOn(DeviceExt->VolumeFcb->Flags, VCB_IS_DIRTY))
     {
-        Status = GetNextCluster(DeviceExt, 1, &eocMark);
-        if (NT_SUCCESS(Status))
+        Status = SetDirtyStatus(DeviceExt, TRUE);
+    }
+
+    DeviceExt->VolumeFcb->Flags &= ~VCB_CLEAR_DIRTY;
+
+    return Status;
+}
+
+static
+NTSTATUS
+VfatLockOrUnlockVolume(
+    PVFAT_IRP_CONTEXT IrpContext,
+    BOOLEAN Lock)
+{
+    PFILE_OBJECT FileObject;
+    PDEVICE_EXTENSION DeviceExt;
+    PVFATFCB Fcb;
+    PVPB Vpb;
+
+    DPRINT("VfatLockOrUnlockVolume(%p, %d)\n", IrpContext, Lock);
+
+    DeviceExt = IrpContext->DeviceExt;
+    FileObject = IrpContext->FileObject;
+    Fcb = FileObject->FsContext;
+    Vpb = DeviceExt->FATFileObject->Vpb;
+
+    /* Only allow locking with the volume open */
+    if (!BooleanFlagOn(Fcb->Flags, FCB_IS_VOLUME))
+    {
+        return STATUS_ACCESS_DENIED;
+    }
+
+    /* Bail out if it's already in the demanded state */
+    if ((BooleanFlagOn(DeviceExt->Flags, VCB_VOLUME_LOCKED) && Lock) ||
+        (!BooleanFlagOn(DeviceExt->Flags, VCB_VOLUME_LOCKED) && !Lock))
+    {
+        return STATUS_ACCESS_DENIED;
+    }
+
+    /* Bail out if it's already in the demanded state */
+    if ((BooleanFlagOn(Vpb->Flags, VPB_LOCKED) && Lock) ||
+        (!BooleanFlagOn(Vpb->Flags, VPB_LOCKED) && !Lock))
+    {
+        return STATUS_ACCESS_DENIED;
+    }
+
+    /* Deny locking if we're not alone */
+    if (Lock && DeviceExt->OpenHandleCount != 1)
+    {
+        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;
+        while (ListEntry != &DeviceExt->FcbListHead)
+        {
+            Fcb = CONTAINING_RECORD(ListEntry, VFATFCB, FcbListEntry);
+            ListEntry = ListEntry->Flink;
+
+            if (Fcb->OpenHandleCount  > 0)
+            {
+                DPRINT1("Opened (%u - %u): %wZ\n", Fcb->OpenHandleCount, Fcb->RefCount, &Fcb->PathNameU);
+            }
+        }
+
+        return STATUS_ACCESS_DENIED;
+
+#if 1
+        /* End of the hack: be verbose about its usage,
+         * just in case we would mess up everything!
+         */
+        }
+        else
         {
-            /* unset clean shutdown bit */
-            eocMark &= ~DeviceExt->CleanShutBitMask;
-            Status = WriteCluster(DeviceExt, 1, eocMark);
+            DPRINT1("HACK: Using lock-hack!\n");
         }
+#endif
     }
 
-    DeviceExt->VolumeFcb->Flags &= ~VCB_CLEAR_DIRTY;
+    /* Finally, proceed */
+    if (Lock)
+    {
+        DeviceExt->Flags |= VCB_VOLUME_LOCKED;
+        Vpb->Flags |= VPB_LOCKED;
+    }
+    else
+    {
+        DeviceExt->Flags &= ~VCB_VOLUME_LOCKED;
+        Vpb->Flags &= ~VPB_LOCKED;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+static
+NTSTATUS
+VfatDismountVolume(
+    PVFAT_IRP_CONTEXT IrpContext)
+{
+    PDEVICE_EXTENSION DeviceExt;
+    PLIST_ENTRY NextEntry;
+    PVFATFCB Fcb;
+    PFILE_OBJECT FileObject;
+
+    DPRINT("VfatDismountVolume(%p)\n", IrpContext);
+
+    DeviceExt = IrpContext->DeviceExt;
+    FileObject = IrpContext->FileObject;
+
+    /* We HAVE to be locked. Windows also allows dismount with no lock
+     * but we're here mainly for 1st stage, so KISS
+     */
+    if (!BooleanFlagOn(DeviceExt->Flags, VCB_VOLUME_LOCKED))
+    {
+        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))
+    {
+        return STATUS_VOLUME_DISMOUNTED;
+    }
+
+    /* Notify we'll dismount. Pass that point there's no reason we fail */
+    FsRtlNotifyVolumeEvent(IrpContext->Stack->FileObject, FSRTL_VOLUME_DISMOUNT);
+
+    ExAcquireResourceExclusiveLite(&DeviceExt->FatResource, TRUE);
+
+    /* We're performing a clean shutdown */
+    if (BooleanFlagOn(DeviceExt->VolumeFcb->Flags, VCB_CLEAR_DIRTY))
+    {
+        /* Drop the dirty bit */
+        if (NT_SUCCESS(SetDirtyStatus(DeviceExt, FALSE)))
+            DeviceExt->VolumeFcb->Flags &= ~VCB_IS_DIRTY;
+    }
+
+    /* Flush volume & files */
+    VfatFlushVolume(DeviceExt, (PVFATFCB)FileObject->FsContext);
+
+    /* Rebrowse the FCB in order to free them now */
+    while (!IsListEmpty(&DeviceExt->FcbListHead))
+    {
+        NextEntry = RemoveTailList(&DeviceExt->FcbListHead);
+        Fcb = CONTAINING_RECORD(NextEntry, VFATFCB, FcbListEntry);
+        vfatDestroyFCB(Fcb);
+    }
+
+    /* Mark we're being dismounted */
+    DeviceExt->Flags |= VCB_DISMOUNT_PENDING;
+#ifndef ENABLE_SWAPOUT
+    IrpContext->DeviceObject->Vpb->Flags &= ~VPB_MOUNTED;
+#endif
+
+    ExReleaseResourceLite(&DeviceExt->FatResource);
+
+    /* Release a few resources and quit, we're done */
+    ExDeleteResourceLite(&DeviceExt->DirResource);
+    ExDeleteResourceLite(&DeviceExt->FatResource);
+    ObDereferenceObject(DeviceExt->FATFileObject);
+
+    return STATUS_SUCCESS;
+}
+
+static
+NTSTATUS
+VfatGetStatistics(
+    PVFAT_IRP_CONTEXT IrpContext)
+{
+    PVOID Buffer;
+    ULONG Length;
+    NTSTATUS Status;
+    PDEVICE_EXTENSION DeviceExt;
+
+    DeviceExt = IrpContext->DeviceExt;
+    Length = IrpContext->Stack->Parameters.FileSystemControl.OutputBufferLength;
+    Buffer = IrpContext->Irp->AssociatedIrp.SystemBuffer;
+
+    if (Length < sizeof(FILESYSTEM_STATISTICS))
+    {
+        return STATUS_BUFFER_TOO_SMALL;
+    }
+
+    if (Buffer == NULL)
+    {
+        return STATUS_INVALID_USER_BUFFER;
+    }
+
+    if (Length >= sizeof(STATISTICS) * VfatGlobalData->NumberProcessors)
+    {
+        Length = sizeof(STATISTICS) * VfatGlobalData->NumberProcessors;
+        Status = STATUS_SUCCESS;
+    }
+    else
+    {
+        Status = STATUS_BUFFER_OVERFLOW;
+    }
+
+    RtlCopyMemory(Buffer, DeviceExt->Statistics, Length);
+    IrpContext->Irp->IoStatus.Information = Length;
 
     return Status;
 }
@@ -889,6 +1389,22 @@ VfatFileSystemControl(
                     Status = VfatMarkVolumeDirty(IrpContext);
                     break;
 
+                case FSCTL_LOCK_VOLUME:
+                    Status = VfatLockOrUnlockVolume(IrpContext, TRUE);
+                    break;
+
+                case FSCTL_UNLOCK_VOLUME:
+                    Status = VfatLockOrUnlockVolume(IrpContext, FALSE);
+                    break;
+
+                case FSCTL_DISMOUNT_VOLUME:
+                    Status = VfatDismountVolume(IrpContext);
+                    break;
+
+                case FSCTL_FILESYSTEM_GET_STATISTICS:
+                    Status = VfatGetStatistics(IrpContext);
+                    break;
+
                 default:
                     Status = STATUS_INVALID_DEVICE_REQUEST;
             }
@@ -909,9 +1425,5 @@ VfatFileSystemControl(
             break;
     }
 
-    IrpContext->Irp->IoStatus.Status = Status;
-
-    IoCompleteRequest(IrpContext->Irp, IO_NO_INCREMENT);
-    VfatFreeIrpContext(IrpContext);
     return Status;
 }