#define NDEBUG
#include <debug.h>
+extern VFAT_DISPATCH FatXDispatch;
+extern VFAT_DISPATCH FatDispatch;
+
/* FUNCTIONS ****************************************************************/
#define CACHEPAGESIZE(pDeviceExt) ((pDeviceExt)->FatInfo.BytesPerCluster > PAGE_SIZE ? \
VfatHasFileSystem(
PDEVICE_OBJECT DeviceToMount,
PBOOLEAN RecognizedFS,
- PFATINFO pFatInfo)
+ PFATINFO pFatInfo,
+ BOOLEAN Override)
{
NTSTATUS Status;
PARTITION_INFORMATION PartitionInfo;
0,
&DiskGeometry,
&Size,
- FALSE);
+ Override);
if (!NT_SUCCESS(Status))
{
- DPRINT("VfatBlockDeviceIoControl faild (%x)\n", Status);
+ DPRINT("VfatBlockDeviceIoControl failed (%x)\n", Status);
return Status;
}
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);
PartitionInfo.PartitionType == PARTITION_FAT32_XINT13 ||
PartitionInfo.PartitionType == PARTITION_XINT13)
{
+ PartitionInfoIsValid = TRUE;
*RecognizedFS = TRUE;
}
}
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;
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)
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)
{
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 &&
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;
}
/*
- * 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;
}
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);
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;
{
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,
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);
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:
DeviceExt->GetNextCluster = FAT16GetNextCluster;
DeviceExt->FindAndMarkAvailableCluster = FAT16FindAndMarkAvailableCluster;
DeviceExt->WriteCluster = FAT16WriteCluster;
- DeviceExt->CleanShutBitMask = 0x8000;
+ DeviceExt->GetDirtyStatus = FAT16GetDirtyStatus;
+ DeviceExt->SetDirtyStatus = FAT16SetDirtyStatus;
break;
case FAT32:
DeviceExt->GetNextCluster = FAT32GetNextCluster;
DeviceExt->FindAndMarkAvailableCluster = FAT32FindAndMarkAvailableCluster;
DeviceExt->WriteCluster = FAT32WriteCluster;
- DeviceExt->CleanShutBitMask = 0x80000000;
+ DeviceExt->GetDirtyStatus = FAT32GetDirtyStatus;
+ DeviceExt->SetDirtyStatus = FAT32SetDirtyStatus;
break;
}
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;
/* 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)
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);
}
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;
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:
/* 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)
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
{
}
}
+ Vpb->RealDevice->Flags &= ~DO_VERIFY_VOLUME;
+
return Status;
}
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;
}
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;
}
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;
}
break;
}
- IrpContext->Irp->IoStatus.Status = Status;
-
- IoCompleteRequest(IrpContext->Irp, IO_NO_INCREMENT);
- VfatFreeIrpContext(IrpContext);
return Status;
}