reactos/drivers/filesystems/fastfat/fsctl.c

1472 lines
48 KiB
C

/*
* ReactOS kernel
* Copyright (C) 2002 ReactOS Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: drivers/fs/vfat/fsctl.c
* PURPOSE: VFAT Filesystem
*/
/* INCLUDES *****************************************************************/
#include "vfat.h"
#define NDEBUG
#include <debug.h>
extern VFAT_DISPATCH FatXDispatch;
extern VFAT_DISPATCH FatDispatch;
/* FUNCTIONS ****************************************************************/
#define CACHEPAGESIZE(pDeviceExt) ((pDeviceExt)->FatInfo.BytesPerCluster > PAGE_SIZE ? \
(pDeviceExt)->FatInfo.BytesPerCluster : PAGE_SIZE)
static
NTSTATUS
VfatHasFileSystem(
PDEVICE_OBJECT DeviceToMount,
PBOOLEAN RecognizedFS,
PFATINFO pFatInfo,
BOOLEAN Override)
{
NTSTATUS Status;
PARTITION_INFORMATION PartitionInfo;
DISK_GEOMETRY DiskGeometry;
FATINFO FatInfo;
ULONG Size;
ULONG Sectors;
LARGE_INTEGER Offset;
struct _BootSector* Boot;
struct _BootSectorFatX* BootFatX;
BOOLEAN PartitionInfoIsValid = FALSE;
DPRINT("VfatHasFileSystem\n");
*RecognizedFS = FALSE;
Size = sizeof(DISK_GEOMETRY);
Status = VfatBlockDeviceIoControl(DeviceToMount,
IOCTL_DISK_GET_DRIVE_GEOMETRY,
NULL,
0,
&DiskGeometry,
&Size,
Override);
if (!NT_SUCCESS(Status))
{
DPRINT("VfatBlockDeviceIoControl failed (%x)\n", Status);
return Status;
}
FatInfo.FixedMedia = DiskGeometry.MediaType == FixedMedia ? TRUE : FALSE;
if (DiskGeometry.MediaType == FixedMedia || DiskGeometry.MediaType == RemovableMedia)
{
// We have found a hard disk
Size = sizeof(PARTITION_INFORMATION);
Status = VfatBlockDeviceIoControl(DeviceToMount,
IOCTL_DISK_GET_PARTITION_INFO,
NULL,
0,
&PartitionInfo,
&Size,
Override);
if (!NT_SUCCESS(Status))
{
DPRINT("VfatBlockDeviceIoControl failed (%x)\n", Status);
return Status;
}
DPRINT("Partition Information:\n");
DPRINT("StartingOffset %I64x\n", PartitionInfo.StartingOffset.QuadPart / 512);
DPRINT("PartitionLength %I64x\n", PartitionInfo.PartitionLength.QuadPart / 512);
DPRINT("HiddenSectors %u\n", PartitionInfo.HiddenSectors);
DPRINT("PartitionNumber %u\n", PartitionInfo.PartitionNumber);
DPRINT("PartitionType %u\n", PartitionInfo.PartitionType);
DPRINT("BootIndicator %u\n", PartitionInfo.BootIndicator);
DPRINT("RecognizedPartition %u\n", PartitionInfo.RecognizedPartition);
DPRINT("RewritePartition %u\n", PartitionInfo.RewritePartition);
if (PartitionInfo.PartitionType)
{
if (PartitionInfo.PartitionType == PARTITION_FAT_12 ||
PartitionInfo.PartitionType == PARTITION_FAT_16 ||
PartitionInfo.PartitionType == PARTITION_HUGE ||
PartitionInfo.PartitionType == PARTITION_FAT32 ||
PartitionInfo.PartitionType == PARTITION_FAT32_XINT13 ||
PartitionInfo.PartitionType == PARTITION_XINT13)
{
PartitionInfoIsValid = TRUE;
*RecognizedFS = TRUE;
}
}
else if (DiskGeometry.MediaType == RemovableMedia &&
PartitionInfo.PartitionNumber > 0 &&
PartitionInfo.StartingOffset.QuadPart == 0 &&
PartitionInfo.PartitionLength.QuadPart > 0)
{
/* This is possible a removable media formated as super floppy */
PartitionInfoIsValid = TRUE;
*RecognizedFS = TRUE;
}
}
else
{
*RecognizedFS = TRUE;
}
if (*RecognizedFS)
{
Boot = ExAllocatePoolWithTag(NonPagedPool, DiskGeometry.BytesPerSector, TAG_BUFFER);
if (Boot == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
Offset.QuadPart = 0;
/* Try to recognize FAT12/FAT16/FAT32 partitions */
Status = VfatReadDisk(DeviceToMount, &Offset, DiskGeometry.BytesPerSector, (PUCHAR) Boot, Override);
if (NT_SUCCESS(Status))
{
if (Boot->Signatur1 != 0xaa55)
{
*RecognizedFS = FALSE;
}
if (*RecognizedFS &&
Boot->BytesPerSector != 512 &&
Boot->BytesPerSector != 1024 &&
Boot->BytesPerSector != 2048 &&
Boot->BytesPerSector != 4096)
{
DPRINT1("BytesPerSector %u\n", Boot->BytesPerSector);
*RecognizedFS = FALSE;
}
if (*RecognizedFS &&
Boot->FATCount != 1 &&
Boot->FATCount != 2)
{
DPRINT1("FATCount %u\n", Boot->FATCount);
*RecognizedFS = FALSE;
}
if (*RecognizedFS &&
Boot->Media != 0xf0 &&
Boot->Media != 0xf8 &&
Boot->Media != 0xf9 &&
Boot->Media != 0xfa &&
Boot->Media != 0xfb &&
Boot->Media != 0xfc &&
Boot->Media != 0xfd &&
Boot->Media != 0xfe &&
Boot->Media != 0xff)
{
DPRINT1("Media %02x\n", Boot->Media);
*RecognizedFS = FALSE;
}
if (*RecognizedFS &&
Boot->SectorsPerCluster != 1 &&
Boot->SectorsPerCluster != 2 &&
Boot->SectorsPerCluster != 4 &&
Boot->SectorsPerCluster != 8 &&
Boot->SectorsPerCluster != 16 &&
Boot->SectorsPerCluster != 32 &&
Boot->SectorsPerCluster != 64 &&
Boot->SectorsPerCluster != 128)
{
DPRINT1("SectorsPerCluster %02x\n", Boot->SectorsPerCluster);
*RecognizedFS = FALSE;
}
if (*RecognizedFS &&
Boot->BytesPerSector * Boot->SectorsPerCluster > 64 * 1024)
{
DPRINT1("ClusterSize %d\n", Boot->BytesPerSector * Boot->SectorsPerCluster);
*RecognizedFS = FALSE;
}
if (*RecognizedFS)
{
FatInfo.VolumeID = Boot->VolumeID;
FatInfo.FATStart = Boot->ReservedSectors;
FatInfo.FATCount = Boot->FATCount;
FatInfo.FATSectors = Boot->FATSectors ? Boot->FATSectors : ((struct _BootSector32*) Boot)->FATSectors32;
FatInfo.BytesPerSector = Boot->BytesPerSector;
FatInfo.SectorsPerCluster = Boot->SectorsPerCluster;
FatInfo.BytesPerCluster = FatInfo.BytesPerSector * FatInfo.SectorsPerCluster;
FatInfo.rootDirectorySectors = ((Boot->RootEntries * 32) + Boot->BytesPerSector - 1) / Boot->BytesPerSector;
FatInfo.rootStart = FatInfo.FATStart + FatInfo.FATCount * FatInfo.FATSectors;
FatInfo.dataStart = FatInfo.rootStart + FatInfo.rootDirectorySectors;
FatInfo.Sectors = Sectors = Boot->Sectors ? Boot->Sectors : Boot->SectorsHuge;
Sectors -= Boot->ReservedSectors + FatInfo.FATCount * FatInfo.FATSectors + FatInfo.rootDirectorySectors;
FatInfo.NumberOfClusters = Sectors / Boot->SectorsPerCluster;
if (FatInfo.NumberOfClusters < 4085)
{
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)
{
DPRINT("FAT32\n");
FatInfo.FatType = FAT32;
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 &&
FatInfo.Sectors > PartitionInfo.PartitionLength.QuadPart / FatInfo.BytesPerSector)
{
*RecognizedFS = FALSE;
}
if (pFatInfo && *RecognizedFS)
{
*pFatInfo = FatInfo;
}
}
}
ExFreePoolWithTag(Boot, TAG_BUFFER);
}
if (!*RecognizedFS && PartitionInfoIsValid)
{
BootFatX = ExAllocatePoolWithTag(NonPagedPool, sizeof(struct _BootSectorFatX), TAG_BUFFER);
if (BootFatX == NULL)
{
*RecognizedFS=FALSE;
return STATUS_INSUFFICIENT_RESOURCES;
}
Offset.QuadPart = 0;
/* Try to recognize FATX16/FATX32 partitions (Xbox) */
Status = VfatReadDisk(DeviceToMount, &Offset, sizeof(struct _BootSectorFatX), (PUCHAR) BootFatX, Override);
if (NT_SUCCESS(Status))
{
*RecognizedFS = TRUE;
if (BootFatX->SysType[0] != 'F' ||
BootFatX->SysType[1] != 'A' ||
BootFatX->SysType[2] != 'T' ||
BootFatX->SysType[3] != 'X')
{
DPRINT1("SysType %02X%02X%02X%02X (%c%c%c%c)\n",
BootFatX->SysType[0], BootFatX->SysType[1], BootFatX->SysType[2], BootFatX->SysType[3],
isprint(BootFatX->SysType[0]) ? BootFatX->SysType[0] : '.',
isprint(BootFatX->SysType[1]) ? BootFatX->SysType[1] : '.',
isprint(BootFatX->SysType[2]) ? BootFatX->SysType[2] : '.',
isprint(BootFatX->SysType[3]) ? BootFatX->SysType[3] : '.');
*RecognizedFS = FALSE;
}
if (*RecognizedFS &&
BootFatX->SectorsPerCluster != 1 &&
BootFatX->SectorsPerCluster != 2 &&
BootFatX->SectorsPerCluster != 4 &&
BootFatX->SectorsPerCluster != 8 &&
BootFatX->SectorsPerCluster != 16 &&
BootFatX->SectorsPerCluster != 32 &&
BootFatX->SectorsPerCluster != 64 &&
BootFatX->SectorsPerCluster != 128)
{
DPRINT1("SectorsPerCluster %lu\n", BootFatX->SectorsPerCluster);
*RecognizedFS=FALSE;
}
if (*RecognizedFS)
{
FatInfo.BytesPerSector = DiskGeometry.BytesPerSector;
FatInfo.SectorsPerCluster = BootFatX->SectorsPerCluster;
FatInfo.rootDirectorySectors = BootFatX->SectorsPerCluster;
FatInfo.BytesPerCluster = BootFatX->SectorsPerCluster * DiskGeometry.BytesPerSector;
FatInfo.Sectors = (ULONG)(PartitionInfo.PartitionLength.QuadPart / DiskGeometry.BytesPerSector);
if (FatInfo.Sectors / FatInfo.SectorsPerCluster < 65525)
{
DPRINT("FATX16\n");
FatInfo.FatType = FATX16;
}
else
{
DPRINT("FATX32\n");
FatInfo.FatType = FATX32;
}
FatInfo.VolumeID = BootFatX->VolumeID;
FatInfo.FATStart = sizeof(struct _BootSectorFatX) / DiskGeometry.BytesPerSector;
FatInfo.FATCount = BootFatX->FATCount;
FatInfo.FATSectors =
ROUND_UP(FatInfo.Sectors / FatInfo.SectorsPerCluster * (FatInfo.FatType == FATX16 ? 2 : 4), 4096) /
FatInfo.BytesPerSector;
FatInfo.rootStart = FatInfo.FATStart + FatInfo.FATCount * FatInfo.FATSectors;
FatInfo.RootCluster = (FatInfo.rootStart - 1) / FatInfo.SectorsPerCluster;
FatInfo.dataStart = FatInfo.rootStart + FatInfo.rootDirectorySectors;
FatInfo.NumberOfClusters = (FatInfo.Sectors - FatInfo.dataStart) / FatInfo.SectorsPerCluster;
if (pFatInfo && *RecognizedFS)
{
*pFatInfo = FatInfo;
}
}
}
ExFreePoolWithTag(BootFatX, TAG_BUFFER);
}
DPRINT("VfatHasFileSystem done\n");
return Status;
}
/*
* 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
ReadVolumeLabel(
PVOID Device,
ULONG Start,
BOOLEAN IsFatX,
PUNICODE_STRING VolumeLabel)
{
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;
if (IsFatX)
{
SizeDirEntry = sizeof(FATX_DIR_ENTRY);
EntriesPerPage = FATX_ENTRIES_PER_PAGE;
}
else
{
SizeDirEntry = sizeof(FAT_DIR_ENTRY);
EntriesPerPage = FAT_ENTRIES_PER_PAGE;
}
FileOffset.QuadPart = Start;
if (!NoCache)
{
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_DIRENT);
if (Buffer != NULL)
{
Status = VfatReadDisk(DeviceObject, &FileOffset, PAGE_SIZE, (PUCHAR)Buffer, TRUE);
if (!NT_SUCCESS(Status))
{
ExFreePoolWithTag(Buffer, TAG_DIRENT);
}
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_DIRENT);
}
}
if (!NoCache)
{
ExAcquireResourceExclusiveLite(&DeviceExt->DirResource, TRUE);
vfatReleaseFCB(DeviceExt, pFcb);
ExReleaseResourceLite(&DeviceExt->DirResource);
}
return STATUS_SUCCESS;
}
/*
* FUNCTION: Mount the filesystem
*/
static
NTSTATUS
VfatMount(
PVFAT_IRP_CONTEXT IrpContext)
{
PDEVICE_OBJECT DeviceObject = NULL;
PDEVICE_EXTENSION DeviceExt = NULL;
BOOLEAN RecognizedFS;
NTSTATUS Status;
PVFATFCB Fcb = NULL;
PVFATFCB VolumeFcb = NULL;
PDEVICE_OBJECT DeviceToMount;
PVPB Vpb;
UNICODE_STRING NameU = RTL_CONSTANT_STRING(L"\\$$Fat$$");
UNICODE_STRING VolumeNameU = RTL_CONSTANT_STRING(L"\\$$Volume$$");
UNICODE_STRING VolumeLabelU;
ULONG HashTableSize;
ULONG i;
FATINFO FatInfo;
BOOLEAN Dirty;
DPRINT("VfatMount(IrpContext %p)\n", IrpContext);
ASSERT(IrpContext);
if (IrpContext->DeviceObject != VfatGlobalData->DeviceObject)
{
Status = STATUS_INVALID_DEVICE_REQUEST;
goto ByeBye;
}
DeviceToMount = IrpContext->Stack->Parameters.MountVolume.DeviceObject;
Vpb = IrpContext->Stack->Parameters.MountVolume.Vpb;
Status = VfatHasFileSystem(DeviceToMount, &RecognizedFS, &FatInfo, FALSE);
if (!NT_SUCCESS(Status))
{
goto ByeBye;
}
if (RecognizedFS == FALSE)
{
DPRINT("VFAT: Unrecognized Volume\n");
Status = STATUS_UNRECOGNIZED_VOLUME;
goto ByeBye;
}
/* Use prime numbers for the table size */
if (FatInfo.FatType == FAT12)
{
HashTableSize = 4099; // 4096 = 4 * 1024
}
else if (FatInfo.FatType == FAT16 ||
FatInfo.FatType == FATX16)
{
HashTableSize = 16411; // 16384 = 16 * 1024
}
else
{
HashTableSize = 65537; // 65536 = 64 * 1024;
}
DPRINT("VFAT: Recognized volume\n");
Status = IoCreateDevice(VfatGlobalData->DriverObject,
ROUND_UP(sizeof (DEVICE_EXTENSION), sizeof(ULONG)) + sizeof(HASHENTRY*) * HashTableSize,
NULL,
FILE_DEVICE_DISK_FILE_SYSTEM,
DeviceToMount->Characteristics,
FALSE,
&DeviceObject);
if (!NT_SUCCESS(Status))
{
goto ByeBye;
}
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;
KeInitializeSpinLock(&DeviceExt->OverflowQueueSpinLock);
InitializeListHead(&DeviceExt->OverflowQueue);
DeviceExt->OverflowQueueCount = 0;
DeviceExt->PostedRequestCount = 0;
/* use same vpb as device disk */
DeviceObject->Vpb = Vpb;
DeviceToMount->Vpb = Vpb;
RtlCopyMemory(&DeviceExt->FatInfo, &FatInfo, sizeof(FATINFO));
DPRINT("BytesPerSector: %u\n", DeviceExt->FatInfo.BytesPerSector);
DPRINT("SectorsPerCluster: %u\n", DeviceExt->FatInfo.SectorsPerCluster);
DPRINT("FATCount: %u\n", DeviceExt->FatInfo.FATCount);
DPRINT("FATSectors: %u\n", DeviceExt->FatInfo.FATSectors);
DPRINT("RootStart: %u\n", DeviceExt->FatInfo.rootStart);
DPRINT("DataStart: %u\n", DeviceExt->FatInfo.dataStart);
if (DeviceExt->FatInfo.FatType == FAT32)
{
DPRINT("RootCluster: %u\n", DeviceExt->FatInfo.RootCluster);
}
switch (DeviceExt->FatInfo.FatType)
{
case FAT12:
DeviceExt->GetNextCluster = FAT12GetNextCluster;
DeviceExt->FindAndMarkAvailableCluster = FAT12FindAndMarkAvailableCluster;
DeviceExt->WriteCluster = FAT12WriteCluster;
/* We don't define dirty bit functions here
* FAT12 doesn't have such bit and they won't get called
*/
break;
case FAT16:
case FATX16:
DeviceExt->GetNextCluster = FAT16GetNextCluster;
DeviceExt->FindAndMarkAvailableCluster = FAT16FindAndMarkAvailableCluster;
DeviceExt->WriteCluster = FAT16WriteCluster;
DeviceExt->GetDirtyStatus = FAT16GetDirtyStatus;
DeviceExt->SetDirtyStatus = FAT16SetDirtyStatus;
break;
case FAT32:
case FATX32:
DeviceExt->GetNextCluster = FAT32GetNextCluster;
DeviceExt->FindAndMarkAvailableCluster = FAT32FindAndMarkAvailableCluster;
DeviceExt->WriteCluster = FAT32WriteCluster;
DeviceExt->GetDirtyStatus = FAT32GetDirtyStatus;
DeviceExt->SetDirtyStatus = FAT32SetDirtyStatus;
break;
}
if (DeviceExt->FatInfo.FatType == FATX16 ||
DeviceExt->FatInfo.FatType == FATX32)
{
DeviceExt->Flags |= VCB_IS_FATX;
DeviceExt->BaseDateYear = 2000;
RtlCopyMemory(&DeviceExt->Dispatch, &FatXDispatch, sizeof(VFAT_DISPATCH));
}
else
{
DeviceExt->BaseDateYear = 1980;
RtlCopyMemory(&DeviceExt->Dispatch, &FatDispatch, sizeof(VFAT_DISPATCH));
}
DeviceExt->StorageDevice = DeviceToMount;
DeviceExt->StorageDevice->Vpb->DeviceObject = DeviceObject;
DeviceExt->StorageDevice->Vpb->RealDevice = DeviceExt->StorageDevice;
DeviceExt->StorageDevice->Vpb->Flags |= VPB_MOUNTED;
DeviceObject->StackSize = DeviceExt->StorageDevice->StackSize + 1;
DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
DPRINT("FsDeviceObject %p\n", DeviceObject);
/* Initialize this resource early ... it's used in VfatCleanup */
ExInitializeResourceLite(&DeviceExt->DirResource);
DeviceExt->IoVPB = DeviceObject->Vpb;
DeviceExt->SpareVPB = ExAllocatePoolWithTag(NonPagedPool, sizeof(VPB), TAG_VPB);
if (DeviceExt->SpareVPB == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto ByeBye;
}
DeviceExt->Statistics = ExAllocatePoolWithTag(NonPagedPool,
sizeof(STATISTICS) * VfatGlobalData->NumberProcessors,
TAG_STATS);
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)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto ByeBye;
}
Status = vfatAttachFCBToFileObject(DeviceExt, Fcb, DeviceExt->FATFileObject);
if (!NT_SUCCESS(Status))
goto ByeBye;
DeviceExt->FATFileObject->PrivateCacheMap = NULL;
Fcb->FileObject = DeviceExt->FATFileObject;
Fcb->Flags = FCB_IS_FAT;
Fcb->RFCB.FileSize.QuadPart = DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector;
Fcb->RFCB.ValidDataLength = Fcb->RFCB.FileSize;
Fcb->RFCB.AllocationSize = Fcb->RFCB.FileSize;
_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 = vfatNewFCB(DeviceExt, &VolumeNameU);
if (VolumeFcb == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto ByeBye;
}
VolumeFcb->Flags = FCB_IS_VOLUME;
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;
ExAcquireResourceExclusiveLite(&VfatGlobalData->VolumeListLock, TRUE);
InsertHeadList(&VfatGlobalData->VolumeListHead, &DeviceExt->VolumeListEntry);
ExReleaseResourceLite(&VfatGlobalData->VolumeListLock);
/* read serial number */
DeviceObject->Vpb->SerialNumber = DeviceExt->FatInfo.VolumeID;
/* read volume label */
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))
{
/* The volume wasn't dirty, it was properly dismounted */
if (!Dirty)
{
/* 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);
}
/* Initialize the notify list and synchronization object */
InitializeListHead(&DeviceExt->NotifyList);
FsRtlNotifyInitializeSync(&DeviceExt->NotifySync);
/* The VCB is OK for usage */
SetFlag(DeviceExt->Flags, VCB_GOOD);
/* Send the mount notification */
FsRtlNotifyVolumeEvent(DeviceExt->FATFileObject, FSRTL_VOLUME_MOUNT);
DPRINT("Mount success\n");
Status = STATUS_SUCCESS;
ByeBye:
if (!NT_SUCCESS(Status))
{
/* Cleanup */
if (DeviceExt && DeviceExt->FATFileObject)
{
LARGE_INTEGER Zero = {{0,0}};
PVFATCCB Ccb = (PVFATCCB)DeviceExt->FATFileObject->FsContext2;
CcUninitializeCacheMap(DeviceExt->FATFileObject,
&Zero,
NULL);
ObDereferenceObject(DeviceExt->FATFileObject);
if (Ccb)
vfatDestroyCCB(Ccb);
DeviceExt->FATFileObject = NULL;
}
if (Fcb)
vfatDestroyFCB(Fcb);
if (DeviceExt && DeviceExt->SpareVPB)
ExFreePoolWithTag(DeviceExt->SpareVPB, TAG_VPB);
if (DeviceExt && DeviceExt->Statistics)
ExFreePoolWithTag(DeviceExt->Statistics, TAG_STATS);
if (DeviceObject)
IoDeleteDevice(DeviceObject);
}
return Status;
}
/*
* FUNCTION: Verify the filesystem
*/
static
NTSTATUS
VfatVerify(
PVFAT_IRP_CONTEXT IrpContext)
{
PDEVICE_OBJECT DeviceToVerify;
NTSTATUS Status;
FATINFO FatInfo;
BOOLEAN RecognizedFS;
PDEVICE_EXTENSION DeviceExt;
BOOLEAN AllowRaw;
PVPB Vpb;
ULONG ChangeCount, BufSize = sizeof(ChangeCount);
DPRINT("VfatVerify(IrpContext %p)\n", IrpContext);
DeviceToVerify = IrpContext->Stack->Parameters.VerifyVolume.DeviceObject;
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,
&ChangeCount,
&BufSize,
TRUE);
if (!NT_SUCCESS(Status) && Status != STATUS_VERIFY_REQUIRED)
{
DPRINT("VfatBlockDeviceIoControl() failed (Status %lx)\n", Status);
Status = (AllowRaw ? STATUS_WRONG_VOLUME : Status);
}
else
{
Status = VfatHasFileSystem(DeviceExt->StorageDevice, &RecognizedFS, &FatInfo, TRUE);
if (!NT_SUCCESS(Status) || RecognizedFS == FALSE)
{
if (NT_SUCCESS(Status) || AllowRaw)
{
Status = STATUS_WRONG_VOLUME;
}
}
else if (sizeof(FATINFO) == RtlCompareMemory(&FatInfo, &DeviceExt->FatInfo, sizeof(FATINFO)))
{
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
{
Status = STATUS_WRONG_VOLUME;
}
}
Vpb->RealDevice->Flags &= ~DO_VERIFY_VOLUME;
return Status;
}
static
NTSTATUS
VfatGetVolumeBitmap(
PVFAT_IRP_CONTEXT IrpContext)
{
DPRINT("VfatGetVolumeBitmap (IrpContext %p)\n", IrpContext);
return STATUS_INVALID_DEVICE_REQUEST;
}
static
NTSTATUS
VfatGetRetrievalPointers(
PVFAT_IRP_CONTEXT IrpContext)
{
PIO_STACK_LOCATION Stack;
LARGE_INTEGER Vcn;
PRETRIEVAL_POINTERS_BUFFER RetrievalPointers;
PFILE_OBJECT FileObject;
ULONG MaxExtentCount;
PVFATFCB Fcb;
PDEVICE_EXTENSION DeviceExt;
ULONG FirstCluster;
ULONG CurrentCluster;
ULONG LastCluster;
NTSTATUS Status;
DPRINT("VfatGetRetrievalPointers(IrpContext %p)\n", IrpContext);
DeviceExt = IrpContext->DeviceExt;
FileObject = IrpContext->FileObject;
Stack = IrpContext->Stack;
if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(STARTING_VCN_INPUT_BUFFER) ||
Stack->Parameters.DeviceIoControl.Type3InputBuffer == NULL)
{
return STATUS_INVALID_PARAMETER;
}
if (IrpContext->Irp->UserBuffer == NULL ||
Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(RETRIEVAL_POINTERS_BUFFER))
{
return STATUS_BUFFER_TOO_SMALL;
}
Fcb = FileObject->FsContext;
ExAcquireResourceSharedLite(&Fcb->MainResource, TRUE);
Vcn = ((PSTARTING_VCN_INPUT_BUFFER)Stack->Parameters.DeviceIoControl.Type3InputBuffer)->StartingVcn;
RetrievalPointers = IrpContext->Irp->UserBuffer;
MaxExtentCount = ((Stack->Parameters.DeviceIoControl.OutputBufferLength - sizeof(RetrievalPointers->ExtentCount) - sizeof(RetrievalPointers->StartingVcn)) / sizeof(RetrievalPointers->Extents[0]));
if (Vcn.QuadPart >= Fcb->RFCB.AllocationSize.QuadPart / DeviceExt->FatInfo.BytesPerCluster)
{
Status = STATUS_INVALID_PARAMETER;
goto ByeBye;
}
CurrentCluster = FirstCluster = vfatDirEntryGetFirstCluster(DeviceExt, &Fcb->entry);
Status = OffsetToCluster(DeviceExt, FirstCluster,
Vcn.u.LowPart * DeviceExt->FatInfo.BytesPerCluster,
&CurrentCluster, FALSE);
if (!NT_SUCCESS(Status))
{
goto ByeBye;
}
RetrievalPointers->StartingVcn = Vcn;
RetrievalPointers->ExtentCount = 0;
RetrievalPointers->Extents[0].Lcn.u.HighPart = 0;
RetrievalPointers->Extents[0].Lcn.u.LowPart = CurrentCluster - 2;
LastCluster = 0;
while (CurrentCluster != 0xffffffff && RetrievalPointers->ExtentCount < MaxExtentCount)
{
LastCluster = CurrentCluster;
Status = NextCluster(DeviceExt, CurrentCluster, &CurrentCluster, FALSE);
Vcn.QuadPart++;
if (!NT_SUCCESS(Status))
{
goto ByeBye;
}
if (LastCluster + 1 != CurrentCluster)
{
RetrievalPointers->Extents[RetrievalPointers->ExtentCount].NextVcn = Vcn;
RetrievalPointers->ExtentCount++;
if (RetrievalPointers->ExtentCount < MaxExtentCount)
{
RetrievalPointers->Extents[RetrievalPointers->ExtentCount].Lcn.u.HighPart = 0;
RetrievalPointers->Extents[RetrievalPointers->ExtentCount].Lcn.u.LowPart = CurrentCluster - 2;
}
}
}
IrpContext->Irp->IoStatus.Information = sizeof(RETRIEVAL_POINTERS_BUFFER) + (sizeof(RetrievalPointers->Extents[0]) * (RetrievalPointers->ExtentCount - 1));
Status = STATUS_SUCCESS;
ByeBye:
ExReleaseResourceLite(&Fcb->MainResource);
return Status;
}
static
NTSTATUS
VfatMoveFile(
PVFAT_IRP_CONTEXT IrpContext)
{
DPRINT("VfatMoveFile(IrpContext %p)\n", IrpContext);
return STATUS_INVALID_DEVICE_REQUEST;
}
static
NTSTATUS
VfatIsVolumeDirty(
PVFAT_IRP_CONTEXT IrpContext)
{
PULONG Flags;
DPRINT("VfatIsVolumeDirty(IrpContext %p)\n", IrpContext);
if (IrpContext->Stack->Parameters.FileSystemControl.OutputBufferLength != sizeof(ULONG))
return STATUS_INVALID_BUFFER_SIZE;
else if (!IrpContext->Irp->AssociatedIrp.SystemBuffer)
return STATUS_INVALID_USER_BUFFER;
Flags = (PULONG)IrpContext->Irp->AssociatedIrp.SystemBuffer;
*Flags = 0;
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;
}
static
NTSTATUS
VfatMarkVolumeDirty(
PVFAT_IRP_CONTEXT IrpContext)
{
PDEVICE_EXTENSION DeviceExt;
NTSTATUS Status = STATUS_SUCCESS;
DPRINT("VfatMarkVolumeDirty(IrpContext %p)\n", IrpContext);
DeviceExt = IrpContext->DeviceExt;
if (!BooleanFlagOn(DeviceExt->VolumeFcb->Flags, VCB_IS_DIRTY))
{
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;
}
if (Lock)
{
FsRtlNotifyVolumeEvent(IrpContext->Stack->FileObject, FSRTL_VOLUME_LOCK);
}
/* 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);
}
}
FsRtlNotifyVolumeEvent(IrpContext->Stack->FileObject, FSRTL_VOLUME_LOCK_FAILED);
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 */
if (Lock)
{
/* Flush volume & files */
VfatFlushVolume(DeviceExt, DeviceExt->VolumeFcb);
/* The volume is now clean */
if (BooleanFlagOn(DeviceExt->VolumeFcb->Flags, VCB_CLEAR_DIRTY) &&
BooleanFlagOn(DeviceExt->VolumeFcb->Flags, VCB_IS_DIRTY))
{
/* Drop the dirty bit */
if (NT_SUCCESS(SetDirtyStatus(DeviceExt, FALSE)))
ClearFlag(DeviceExt->VolumeFcb->Flags, VCB_IS_DIRTY);
}
DeviceExt->Flags |= VCB_VOLUME_LOCKED;
Vpb->Flags |= VPB_LOCKED;
}
else
{
DeviceExt->Flags &= ~VCB_VOLUME_LOCKED;
Vpb->Flags &= ~VPB_LOCKED;
FsRtlNotifyVolumeEvent(IrpContext->Stack->FileObject, FSRTL_VOLUME_UNLOCK);
}
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);
/* Flush volume & files */
VfatFlushVolume(DeviceExt, (PVFATFCB)FileObject->FsContext);
/* The volume is now clean */
if (BooleanFlagOn(DeviceExt->VolumeFcb->Flags, VCB_CLEAR_DIRTY) &&
BooleanFlagOn(DeviceExt->VolumeFcb->Flags, VCB_IS_DIRTY))
{
/* Drop the dirty bit */
if (NT_SUCCESS(SetDirtyStatus(DeviceExt, FALSE)))
DeviceExt->VolumeFcb->Flags &= ~VCB_IS_DIRTY;
}
/* Rebrowse the FCB in order to free them now */
while (!IsListEmpty(&DeviceExt->FcbListHead))
{
NextEntry = RemoveTailList(&DeviceExt->FcbListHead);
Fcb = CONTAINING_RECORD(NextEntry, VFATFCB, FcbListEntry);
if (Fcb == DeviceExt->RootFcb)
DeviceExt->RootFcb = NULL;
else if (Fcb == DeviceExt->VolumeFcb)
DeviceExt->VolumeFcb = NULL;
vfatDestroyFCB(Fcb);
}
/* We are uninitializing, the VCB cannot be used anymore */
ClearFlag(DeviceExt->Flags, VCB_GOOD);
/* Mark we're being dismounted */
DeviceExt->Flags |= VCB_DISMOUNT_PENDING;
#ifndef ENABLE_SWAPOUT
IrpContext->DeviceObject->Vpb->Flags &= ~VPB_MOUNTED;
#endif
ExReleaseResourceLite(&DeviceExt->FatResource);
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;
}
/*
* FUNCTION: File system control
*/
NTSTATUS
VfatFileSystemControl(
PVFAT_IRP_CONTEXT IrpContext)
{
NTSTATUS Status;
DPRINT("VfatFileSystemControl(IrpContext %p)\n", IrpContext);
ASSERT(IrpContext);
ASSERT(IrpContext->Irp);
ASSERT(IrpContext->Stack);
IrpContext->Irp->IoStatus.Information = 0;
switch (IrpContext->MinorFunction)
{
case IRP_MN_KERNEL_CALL:
case IRP_MN_USER_FS_REQUEST:
switch(IrpContext->Stack->Parameters.DeviceIoControl.IoControlCode)
{
case FSCTL_GET_VOLUME_BITMAP:
Status = VfatGetVolumeBitmap(IrpContext);
break;
case FSCTL_GET_RETRIEVAL_POINTERS:
Status = VfatGetRetrievalPointers(IrpContext);
break;
case FSCTL_MOVE_FILE:
Status = VfatMoveFile(IrpContext);
break;
case FSCTL_IS_VOLUME_DIRTY:
Status = VfatIsVolumeDirty(IrpContext);
break;
case FSCTL_MARK_VOLUME_DIRTY:
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;
case IRP_MN_MOUNT_VOLUME:
Status = VfatMount(IrpContext);
break;
case IRP_MN_VERIFY_VOLUME:
DPRINT("VFATFS: IRP_MN_VERIFY_VOLUME\n");
Status = VfatVerify(IrpContext);
break;
default:
DPRINT("VFAT FSC: MinorFunction %u\n", IrpContext->MinorFunction);
Status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
return Status;
}