[FASTFAT]
[reactos.git] / reactos / drivers / filesystems / fastfat / fsctl.c
index 938467b..ff222f5 100644 (file)
@@ -40,7 +40,8 @@ NTSTATUS
 VfatHasFileSystem(
     PDEVICE_OBJECT DeviceToMount,
     PBOOLEAN RecognizedFS,
-    PFATINFO pFatInfo)
+    PFATINFO pFatInfo,
+    BOOLEAN Override)
 {
     NTSTATUS Status;
     PARTITION_INFORMATION PartitionInfo;
@@ -64,10 +65,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 +83,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 +108,7 @@ VfatHasFileSystem(
                 PartitionInfo.PartitionType == PARTITION_FAT32_XINT13 ||
                 PartitionInfo.PartitionType == PARTITION_XINT13)
             {
+                 PartitionInfoIsValid = TRUE;
                 *RecognizedFS = TRUE;
             }
         }
@@ -117,19 +118,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 +138,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 +220,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 +229,14 @@ VfatHasFileSystem(
                     FatInfo.RootCluster = ((struct _BootSector32*) Boot)->RootCluster;
                     FatInfo.rootStart = FatInfo.dataStart + ((FatInfo.RootCluster - 2) * FatInfo.SectorsPerCluster);
                     FatInfo.VolumeID = ((struct _BootSector32*) Boot)->VolumeID;
+                    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 +267,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;
@@ -340,30 +335,6 @@ VfatHasFileSystem(
     return Status;
 }
 
-/*
- * FUNCTION: Mounts the device
- */
-static
-NTSTATUS
-VfatMountDevice(
-    PDEVICE_EXTENSION DeviceExt,
-    PDEVICE_OBJECT DeviceToMount)
-{
-    NTSTATUS Status;
-    BOOLEAN RecognizedFS;
-
-    DPRINT("Mounting VFAT device...\n");
-
-    Status = VfatHasFileSystem(DeviceToMount, &RecognizedFS, &DeviceExt->FatInfo);
-    if (!NT_SUCCESS(Status))
-    {
-        return Status;
-    }
-    DPRINT("MountVfatdev %u, PAGE_SIZE = %d\n", DeviceExt->FatInfo.BytesPerCluster, PAGE_SIZE);
-
-    return STATUS_SUCCESS;
-}
-
 
 /*
  * FUNCTION: Mount the filesystem
@@ -401,7 +372,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 +399,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,
@@ -446,17 +416,13 @@ VfatMount(
     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);
@@ -520,6 +486,14 @@ 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->FATFileObject = IoCreateStreamFileObject(NULL, DeviceExt->StorageDevice);
     Fcb = vfatNewFCB(DeviceExt, &NameU);
     if (Fcb == NULL)
@@ -577,7 +551,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;
@@ -611,6 +585,8 @@ VfatMount(
     FsRtlNotifyInitializeSync(&DeviceExt->NotifySync);
     InitializeListHead(&DeviceExt->NotifyList);
 
+    DPRINT("Mount success\n");
+
     Status = STATUS_SUCCESS;
 
 ByeBye:
@@ -619,6 +595,8 @@ ByeBye:
         /* Cleanup */
         if (DeviceExt && DeviceExt->FATFileObject)
             ObDereferenceObject (DeviceExt->FATFileObject);
+        if (DeviceExt && DeviceExt->SpareVPB)
+            ExFreePoolWithTag(DeviceExt->SpareVPB, TAG_VFAT);
         if (Fcb)
             vfatDestroyFCB(Fcb);
         if (Ccb)
@@ -640,22 +618,22 @@ 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;
 
     DPRINT("VfatVerify(IrpContext %p)\n", IrpContext);
 
     DeviceToVerify = IrpContext->Stack->Parameters.VerifyVolume.DeviceObject;
-    Status = VfatBlockDeviceIoControl(DeviceToVerify,
+    DeviceExt = DeviceToVerify->DeviceExtension;
+    Status = VfatBlockDeviceIoControl(DeviceExt->StorageDevice,
                                       IOCTL_DISK_CHECK_VERIFY,
                                       NULL,
                                       0,
                                       NULL,
                                       0,
                                       TRUE);
-    DeviceToVerify->Flags &= ~DO_VERIFY_VOLUME;
     if (!NT_SUCCESS(Status) && Status != STATUS_VERIFY_REQUIRED)
     {
         DPRINT("VfatBlockDeviceIoControl() failed (Status %lx)\n", Status);
@@ -663,19 +641,25 @@ VfatVerify(
     }
     else
     {
-        Status = VfatHasFileSystem(DeviceToVerify, &RecognizedFS, &FatInfo);
+        Status = VfatHasFileSystem(DeviceExt->StorageDevice, &RecognizedFS, &FatInfo, TRUE);
         if (!NT_SUCCESS(Status) || RecognizedFS == FALSE)
         {
             Status = STATUS_WRONG_VOLUME;
         }
         else if (sizeof(FATINFO) == RtlCompareMemory(&FatInfo, &DeviceExt->FatInfo, sizeof(FATINFO)))
         {
+            DPRINT1("Same volume\n");
             /*
              * FIXME:
-             *   Preformated floppy disks have very often a serial number of 0000:0000.
+             *   Preformatted 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.
              */
+            /* HACK */
+            if (!FatInfo.FixedMedia && FatInfo.FatType >= FATX16)
+            {
+                Status = STATUS_WRONG_VOLUME;
+            }
         }
         else
         {
@@ -683,6 +667,8 @@ VfatVerify(
         }
     }
 
+    IrpContext->Stack->Parameters.VerifyVolume.Vpb->RealDevice->Flags &= ~DO_VERIFY_VOLUME;
+
     return Status;
 }
 
@@ -854,6 +840,129 @@ VfatMarkVolumeDirty(
     return Status;
 }
 
+static
+NTSTATUS
+VfatLockOrUnlockVolume(
+    PVFAT_IRP_CONTEXT IrpContext,
+    BOOLEAN Lock)
+{
+    PFILE_OBJECT FileObject;
+    PDEVICE_EXTENSION DeviceExt;
+    PVFATFCB Fcb;
+
+    DPRINT("VfatLockOrUnlockVolume(%p, %d)\n", IrpContext, Lock);
+
+    DeviceExt = IrpContext->DeviceExt;
+    FileObject = IrpContext->FileObject;
+    Fcb = FileObject->FsContext;
+
+    /* Only allow locking with the volume open */
+    if (!(Fcb->Flags & FCB_IS_VOLUME))
+    {
+        return STATUS_ACCESS_DENIED;
+    }
+
+    /* Bail out if it's already in the demanded state */
+    if (((DeviceExt->Flags & VCB_VOLUME_LOCKED) && Lock) ||
+        (!(DeviceExt->Flags & VCB_VOLUME_LOCKED) && !Lock))
+    {
+        return STATUS_ACCESS_DENIED;
+    }
+
+    /* Deny locking if we're not alone */
+    if (Lock && DeviceExt->OpenHandleCount != 1)
+    {
+        return STATUS_ACCESS_DENIED;
+    }
+
+    /* Finally, proceed */
+    if (Lock)
+    {
+        DeviceExt->Flags |= VCB_VOLUME_LOCKED;
+    }
+    else
+    {
+        DeviceExt->Flags &= ~VCB_VOLUME_LOCKED;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+static
+NTSTATUS
+VfatDismountVolume(
+    PVFAT_IRP_CONTEXT IrpContext)
+{
+    PDEVICE_EXTENSION DeviceExt;
+    PLIST_ENTRY NextEntry;
+    PVFATFCB Fcb;
+    PFILE_OBJECT FileObject;
+    ULONG eocMark;
+    NTSTATUS Status;
+
+    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 (!(DeviceExt->Flags & VCB_VOLUME_LOCKED))
+    {
+        return STATUS_ACCESS_DENIED;
+    }
+
+    /* Race condition? */
+    if (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);
+
+    if (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;
+        }
+    }
+
+    /* Flush volume & files */
+    VfatFlushVolume(DeviceExt, (PVFATFCB)FileObject->FsContext);
+
+    /* Rebrowse the FCB in order to free them now */
+    while (!IsListEmpty(&DeviceExt->FcbListHead))
+    {
+        NextEntry = RemoveHeadList(&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;
+}
+
 /*
  * FUNCTION: File system control
  */
@@ -897,6 +1006,18 @@ 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;
+
                 default:
                     Status = STATUS_INVALID_DEVICE_REQUEST;
             }
@@ -917,9 +1038,5 @@ VfatFileSystemControl(
             break;
     }
 
-    IrpContext->Irp->IoStatus.Status = Status;
-
-    IoCompleteRequest(IrpContext->Irp, IO_NO_INCREMENT);
-    VfatFreeIrpContext(IrpContext);
     return Status;
 }