mirror of
https://github.com/reactos/reactos.git
synced 2024-12-29 10:35:28 +00:00
303f17f884
This avoids blocking all Ex worker threads in fastfat, thereby making Cc unable to issue the lazy writes that would unblock those workers. This is more or less directly taken from fastfat_new.
1471 lines
48 KiB
C
1471 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;
|
|
}
|