2002-05-23 09:53:26 +00:00
|
|
|
/*
|
2022-09-24 13:24:08 +00:00
|
|
|
* PROJECT: VFAT Filesystem
|
|
|
|
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
|
|
|
* PURPOSE: Filesystem routines
|
|
|
|
* COPYRIGHT: Copyright 2002-2013 Eric Kohl <eric.kohl@reactos.org>
|
|
|
|
* Copyright 2008-2018 Pierre Schweitzer <pierre@reactos.org>
|
2002-03-18 22:37:13 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
|
|
|
|
#include "vfat.h"
|
|
|
|
|
2013-12-19 16:20:28 +00:00
|
|
|
#define NDEBUG
|
|
|
|
#include <debug.h>
|
|
|
|
|
2017-02-17 22:25:03 +00:00
|
|
|
extern VFAT_DISPATCH FatXDispatch;
|
|
|
|
extern VFAT_DISPATCH FatDispatch;
|
|
|
|
|
2002-03-18 22:37:13 +00:00
|
|
|
/* FUNCTIONS ****************************************************************/
|
|
|
|
|
2002-10-01 19:27:25 +00:00
|
|
|
#define CACHEPAGESIZE(pDeviceExt) ((pDeviceExt)->FatInfo.BytesPerCluster > PAGE_SIZE ? \
|
2007-10-19 07:50:59 +00:00
|
|
|
(pDeviceExt)->FatInfo.BytesPerCluster : PAGE_SIZE)
|
2002-03-18 22:37:13 +00:00
|
|
|
|
2013-12-09 10:35:15 +00:00
|
|
|
static
|
|
|
|
NTSTATUS
|
|
|
|
VfatHasFileSystem(
|
|
|
|
PDEVICE_OBJECT DeviceToMount,
|
|
|
|
PBOOLEAN RecognizedFS,
|
2017-02-05 17:07:31 +00:00
|
|
|
PFATINFO pFatInfo,
|
|
|
|
BOOLEAN Override)
|
2002-03-18 22:37:13 +00:00
|
|
|
{
|
2013-12-09 10:35:15 +00:00
|
|
|
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,
|
2017-02-05 17:07:31 +00:00
|
|
|
Override);
|
2013-12-09 10:35:15 +00:00
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
2016-11-13 15:31:39 +00:00
|
|
|
DPRINT("VfatBlockDeviceIoControl failed (%x)\n", Status);
|
2013-12-09 10:35:15 +00:00
|
|
|
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,
|
2017-02-05 17:07:31 +00:00
|
|
|
Override);
|
2013-12-09 10:35:15 +00:00
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
2016-11-13 15:31:39 +00:00
|
|
|
DPRINT("VfatBlockDeviceIoControl failed (%x)\n", Status);
|
2013-12-09 10:35:15 +00:00
|
|
|
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)
|
|
|
|
{
|
2014-10-29 22:51:16 +00:00
|
|
|
PartitionInfoIsValid = TRUE;
|
2013-12-09 10:35:15 +00:00
|
|
|
*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 */
|
2014-10-29 22:51:16 +00:00
|
|
|
PartitionInfoIsValid = TRUE;
|
2005-01-13 20:57:30 +00:00
|
|
|
*RecognizedFS = TRUE;
|
2013-12-09 10:35:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*RecognizedFS = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*RecognizedFS)
|
|
|
|
{
|
2018-08-21 06:36:51 +00:00
|
|
|
Boot = ExAllocatePoolWithTag(NonPagedPool, DiskGeometry.BytesPerSector, TAG_BUFFER);
|
2013-12-09 10:35:15 +00:00
|
|
|
if (Boot == NULL)
|
|
|
|
{
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
|
|
|
|
|
|
Offset.QuadPart = 0;
|
|
|
|
|
|
|
|
/* Try to recognize FAT12/FAT16/FAT32 partitions */
|
2017-02-05 17:07:31 +00:00
|
|
|
Status = VfatReadDisk(DeviceToMount, &Offset, DiskGeometry.BytesPerSector, (PUCHAR) Boot, Override);
|
2013-12-09 10:35:15 +00:00
|
|
|
if (NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
if (Boot->Signatur1 != 0xaa55)
|
|
|
|
{
|
|
|
|
*RecognizedFS = FALSE;
|
|
|
|
}
|
2004-05-02 20:16:46 +00:00
|
|
|
|
2013-12-09 10:35:15 +00:00
|
|
|
if (*RecognizedFS &&
|
|
|
|
Boot->BytesPerSector != 512 &&
|
|
|
|
Boot->BytesPerSector != 1024 &&
|
|
|
|
Boot->BytesPerSector != 2048 &&
|
|
|
|
Boot->BytesPerSector != 4096)
|
2005-01-25 21:11:46 +00:00
|
|
|
{
|
2013-12-09 10:35:15 +00:00
|
|
|
DPRINT1("BytesPerSector %u\n", Boot->BytesPerSector);
|
|
|
|
*RecognizedFS = FALSE;
|
2005-01-25 21:11:46 +00:00
|
|
|
}
|
2013-12-09 10:35:15 +00:00
|
|
|
|
|
|
|
if (*RecognizedFS &&
|
|
|
|
Boot->FATCount != 1 &&
|
|
|
|
Boot->FATCount != 2)
|
2005-01-25 21:11:46 +00:00
|
|
|
{
|
2013-12-09 10:35:15 +00:00
|
|
|
DPRINT1("FATCount %u\n", Boot->FATCount);
|
|
|
|
*RecognizedFS = FALSE;
|
2005-01-25 21:11:46 +00:00
|
|
|
}
|
2013-12-09 10:35:15 +00:00
|
|
|
|
|
|
|
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)
|
2005-01-25 21:11:46 +00:00
|
|
|
{
|
2013-12-09 10:35:15 +00:00
|
|
|
DPRINT1("Media %02x\n", Boot->Media);
|
|
|
|
*RecognizedFS = FALSE;
|
2005-01-25 21:11:46 +00:00
|
|
|
}
|
2013-12-09 10:35:15 +00:00
|
|
|
|
|
|
|
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)
|
2005-01-25 21:11:46 +00:00
|
|
|
{
|
2013-12-09 10:35:15 +00:00
|
|
|
DPRINT1("SectorsPerCluster %02x\n", Boot->SectorsPerCluster);
|
|
|
|
*RecognizedFS = FALSE;
|
2005-01-25 21:11:46 +00:00
|
|
|
}
|
2005-05-08 02:16:32 +00:00
|
|
|
|
2013-12-09 10:35:15 +00:00
|
|
|
if (*RecognizedFS &&
|
2020-07-22 16:50:24 +00:00
|
|
|
Boot->BytesPerSector * Boot->SectorsPerCluster > 64 * 1024)
|
2005-01-25 21:11:46 +00:00
|
|
|
{
|
2020-07-22 16:50:24 +00:00
|
|
|
DPRINT1("ClusterSize %d\n", Boot->BytesPerSector * Boot->SectorsPerCluster);
|
2013-12-09 10:35:15 +00:00
|
|
|
*RecognizedFS = FALSE;
|
2005-01-25 21:11:46 +00:00
|
|
|
}
|
|
|
|
|
2013-12-09 10:35:15 +00:00
|
|
|
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;
|
2017-02-06 19:43:48 +00:00
|
|
|
RtlCopyMemory(&FatInfo.VolumeLabel, &Boot->VolumeLabel, sizeof(FatInfo.VolumeLabel));
|
2013-12-09 10:35:15 +00:00
|
|
|
}
|
|
|
|
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;
|
2018-06-09 10:11:43 +00:00
|
|
|
FatInfo.FSInfoSector = ((struct _BootSector32*) Boot)->FSInfoSector;
|
2017-02-06 19:43:48 +00:00
|
|
|
RtlCopyMemory(&FatInfo.VolumeLabel, &((struct _BootSector32*)Boot)->VolumeLabel, sizeof(FatInfo.VolumeLabel));
|
2013-12-09 10:35:15 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DPRINT("FAT16\n");
|
|
|
|
FatInfo.FatType = FAT16;
|
|
|
|
FatInfo.RootCluster = FatInfo.rootStart / FatInfo.SectorsPerCluster;
|
2017-02-06 19:43:48 +00:00
|
|
|
RtlCopyMemory(&FatInfo.VolumeLabel, &Boot->VolumeLabel, sizeof(FatInfo.VolumeLabel));
|
2013-12-09 10:35:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (PartitionInfoIsValid &&
|
|
|
|
FatInfo.Sectors > PartitionInfo.PartitionLength.QuadPart / FatInfo.BytesPerSector)
|
|
|
|
{
|
|
|
|
*RecognizedFS = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pFatInfo && *RecognizedFS)
|
|
|
|
{
|
|
|
|
*pFatInfo = FatInfo;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2004-03-31 03:33:48 +00:00
|
|
|
|
2018-08-21 06:36:51 +00:00
|
|
|
ExFreePoolWithTag(Boot, TAG_BUFFER);
|
2013-12-09 10:35:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!*RecognizedFS && PartitionInfoIsValid)
|
|
|
|
{
|
2018-08-21 06:36:51 +00:00
|
|
|
BootFatX = ExAllocatePoolWithTag(NonPagedPool, sizeof(struct _BootSectorFatX), TAG_BUFFER);
|
2013-12-09 10:35:15 +00:00
|
|
|
if (BootFatX == NULL)
|
|
|
|
{
|
2004-12-05 16:31:51 +00:00
|
|
|
*RecognizedFS=FALSE;
|
2013-12-09 10:35:15 +00:00
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
|
|
|
|
|
|
Offset.QuadPart = 0;
|
|
|
|
|
|
|
|
/* Try to recognize FATX16/FATX32 partitions (Xbox) */
|
2017-02-05 17:14:42 +00:00
|
|
|
Status = VfatReadDisk(DeviceToMount, &Offset, sizeof(struct _BootSectorFatX), (PUCHAR) BootFatX, Override);
|
2013-12-09 10:35:15 +00:00
|
|
|
if (NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
*RecognizedFS = TRUE;
|
|
|
|
if (BootFatX->SysType[0] != 'F' ||
|
|
|
|
BootFatX->SysType[1] != 'A' ||
|
|
|
|
BootFatX->SysType[2] != 'T' ||
|
|
|
|
BootFatX->SysType[3] != 'X')
|
2004-12-05 16:31:51 +00:00
|
|
|
{
|
2018-10-13 17:57:24 +00:00
|
|
|
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;
|
2004-12-05 16:31:51 +00:00
|
|
|
}
|
2013-12-09 10:35:15 +00:00
|
|
|
|
|
|
|
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)
|
2004-12-05 16:31:51 +00:00
|
|
|
{
|
2013-12-09 10:35:15 +00:00
|
|
|
DPRINT1("SectorsPerCluster %lu\n", BootFatX->SectorsPerCluster);
|
|
|
|
*RecognizedFS=FALSE;
|
2004-12-05 16:31:51 +00:00
|
|
|
}
|
2013-12-09 10:35:15 +00:00
|
|
|
|
|
|
|
if (*RecognizedFS)
|
2004-12-05 16:31:51 +00:00
|
|
|
{
|
2013-12-09 10:35:15 +00:00
|
|
|
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;
|
|
|
|
}
|
2004-12-05 16:31:51 +00:00
|
|
|
}
|
2013-12-09 10:35:15 +00:00
|
|
|
}
|
2018-08-21 06:36:51 +00:00
|
|
|
ExFreePoolWithTag(BootFatX, TAG_BUFFER);
|
2013-12-09 10:35:15 +00:00
|
|
|
}
|
2005-05-08 02:16:32 +00:00
|
|
|
|
2013-12-09 10:35:15 +00:00
|
|
|
DPRINT("VfatHasFileSystem done\n");
|
|
|
|
return Status;
|
2002-03-18 22:37:13 +00:00
|
|
|
}
|
|
|
|
|
2017-02-18 18:35:48 +00:00
|
|
|
/*
|
|
|
|
* FUNCTION: Read the volume label
|
2017-02-18 21:24:31 +00:00
|
|
|
* 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)
|
2021-06-11 12:29:21 +00:00
|
|
|
* Its buffer has to be able to contain MAXIMUM_VOLUME_LABEL_LENGTH bytes
|
2017-02-18 18:35:48 +00:00
|
|
|
*/
|
|
|
|
static
|
|
|
|
NTSTATUS
|
|
|
|
ReadVolumeLabel(
|
2017-02-18 21:24:31 +00:00
|
|
|
PVOID Device,
|
|
|
|
ULONG Start,
|
|
|
|
BOOLEAN IsFatX,
|
|
|
|
PUNICODE_STRING VolumeLabel)
|
2017-02-18 18:35:48 +00:00
|
|
|
{
|
2017-02-18 21:24:31 +00:00
|
|
|
PDEVICE_EXTENSION DeviceExt;
|
|
|
|
PDEVICE_OBJECT DeviceObject;
|
2017-02-18 18:35:48 +00:00
|
|
|
PVOID Context = NULL;
|
|
|
|
ULONG DirIndex = 0;
|
|
|
|
PDIR_ENTRY Entry;
|
|
|
|
PVFATFCB pFcb;
|
|
|
|
LARGE_INTEGER FileOffset;
|
|
|
|
ULONG SizeDirEntry;
|
|
|
|
ULONG EntriesPerPage;
|
|
|
|
OEM_STRING StringO;
|
2017-02-18 21:24:31 +00:00
|
|
|
BOOLEAN NoCache = (Start != 0);
|
|
|
|
PVOID Buffer;
|
2017-02-18 18:35:48 +00:00
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
|
2017-02-18 21:24:31 +00:00
|
|
|
if (IsFatX)
|
2017-02-18 18:35:48 +00:00
|
|
|
{
|
|
|
|
SizeDirEntry = sizeof(FATX_DIR_ENTRY);
|
|
|
|
EntriesPerPage = FATX_ENTRIES_PER_PAGE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
SizeDirEntry = sizeof(FAT_DIR_ENTRY);
|
|
|
|
EntriesPerPage = FAT_ENTRIES_PER_PAGE;
|
|
|
|
}
|
|
|
|
|
2017-02-18 21:24:31 +00:00
|
|
|
FileOffset.QuadPart = Start;
|
|
|
|
if (!NoCache)
|
2017-02-18 18:35:48 +00:00
|
|
|
{
|
2017-02-18 21:24:31 +00:00
|
|
|
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;
|
2017-02-18 18:35:48 +00:00
|
|
|
}
|
2017-02-18 21:24:31 +00:00
|
|
|
else
|
2017-02-18 18:35:48 +00:00
|
|
|
{
|
2017-02-18 21:24:31 +00:00
|
|
|
DeviceObject = Device;
|
|
|
|
|
|
|
|
ASSERT(DeviceObject->Type == 3);
|
|
|
|
|
2018-08-21 06:36:51 +00:00
|
|
|
Buffer = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, TAG_DIRENT);
|
2017-02-18 21:24:31 +00:00
|
|
|
if (Buffer != NULL)
|
|
|
|
{
|
|
|
|
Status = VfatReadDisk(DeviceObject, &FileOffset, PAGE_SIZE, (PUCHAR)Buffer, TRUE);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
2018-08-21 06:36:51 +00:00
|
|
|
ExFreePoolWithTag(Buffer, TAG_DIRENT);
|
2017-03-12 18:25:21 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Entry = Buffer;
|
2017-02-18 21:24:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
2017-02-18 18:35:48 +00:00
|
|
|
}
|
2017-02-18 21:24:31 +00:00
|
|
|
|
2017-02-18 18:35:48 +00:00
|
|
|
if (NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
while (TRUE)
|
|
|
|
{
|
2017-02-18 21:37:56 +00:00
|
|
|
if (ENTRY_VOLUME(IsFatX, Entry))
|
2017-02-18 18:35:48 +00:00
|
|
|
{
|
|
|
|
/* copy volume label */
|
2017-02-18 21:24:31 +00:00
|
|
|
if (IsFatX)
|
2017-02-18 18:35:48 +00:00
|
|
|
{
|
|
|
|
StringO.Buffer = (PCHAR)Entry->FatX.Filename;
|
|
|
|
StringO.MaximumLength = StringO.Length = Entry->FatX.FilenameLength;
|
2017-02-18 21:24:31 +00:00
|
|
|
RtlOemStringToUnicodeString(VolumeLabel, &StringO, FALSE);
|
2017-02-18 18:35:48 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-02-18 21:24:31 +00:00
|
|
|
vfat8Dot3ToString(&Entry->Fat, VolumeLabel);
|
2017-02-18 18:35:48 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2017-02-18 21:37:56 +00:00
|
|
|
if (ENTRY_END(IsFatX, Entry))
|
2017-02-18 18:35:48 +00:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
DirIndex++;
|
|
|
|
Entry = (PDIR_ENTRY)((ULONG_PTR)Entry + SizeDirEntry);
|
|
|
|
if ((DirIndex % EntriesPerPage) == 0)
|
|
|
|
{
|
|
|
|
FileOffset.u.LowPart += PAGE_SIZE;
|
2017-02-18 21:24:31 +00:00
|
|
|
|
|
|
|
if (!NoCache)
|
2017-02-18 18:35:48 +00:00
|
|
|
{
|
2017-02-18 21:24:31 +00:00
|
|
|
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;
|
|
|
|
}
|
2017-02-18 18:35:48 +00:00
|
|
|
}
|
2017-02-18 21:24:31 +00:00
|
|
|
else
|
2017-02-18 18:35:48 +00:00
|
|
|
{
|
2017-02-18 21:24:31 +00:00
|
|
|
Status = VfatReadDisk(DeviceObject, &FileOffset, PAGE_SIZE, (PUCHAR)Buffer, TRUE);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
Entry = Buffer;
|
2017-02-18 18:35:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (Context)
|
|
|
|
{
|
|
|
|
CcUnpinData(Context);
|
|
|
|
}
|
2017-02-18 21:24:31 +00:00
|
|
|
else if (NoCache)
|
|
|
|
{
|
2018-08-21 06:36:51 +00:00
|
|
|
ExFreePoolWithTag(Buffer, TAG_DIRENT);
|
2017-02-18 21:24:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!NoCache)
|
|
|
|
{
|
|
|
|
ExAcquireResourceExclusiveLite(&DeviceExt->DirResource, TRUE);
|
|
|
|
vfatReleaseFCB(DeviceExt, pFcb);
|
|
|
|
ExReleaseResourceLite(&DeviceExt->DirResource);
|
2017-02-18 18:35:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2002-03-18 22:37:13 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* FUNCTION: Mount the filesystem
|
|
|
|
*/
|
2013-12-12 13:51:50 +00:00
|
|
|
static
|
|
|
|
NTSTATUS
|
|
|
|
VfatMount(
|
|
|
|
PVFAT_IRP_CONTEXT IrpContext)
|
2002-03-18 22:37:13 +00:00
|
|
|
{
|
2013-12-12 13:51:50 +00:00
|
|
|
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$$");
|
2017-02-18 21:24:31 +00:00
|
|
|
UNICODE_STRING VolumeLabelU;
|
2013-12-12 13:51:50 +00:00
|
|
|
ULONG HashTableSize;
|
2017-09-24 08:56:06 +00:00
|
|
|
ULONG i;
|
2013-12-12 13:51:50 +00:00
|
|
|
FATINFO FatInfo;
|
[FASTFAT] Completely rewrite support for dirty volumes.
Until now, our support for dirty volumes was totally broken
to a point where, on FAT32 volume, the dirty couldn't even
be written nor read from the disk.
This commit totally rewrites its handling, for both FAT16 and FAT32
so that it's now fully functionnal. Furthermore, it also gets
totally compatible with our vfatlib, and thus, autochk.
Now, on mount, FastFAT will check if the volume is dirty or not, and
autochk will be able to ask for a repair if dirty. vfatlib will
repair the volume and remove the dirty bit. So that, on next
reboot, the volume will be mounted clean.
As a reminder, the dirty bit is set immediately after mounting
the volume, so that, if you crash or have a powercut, autochk
will always attempt to repair your volume (with more or less,
that's FAT!).
If you want to experience without breaking your FAT volume,
just boot, open a cmd prompt and type: fsutil dirty set c:
and reboot!
CORE-13758
CORE-13760
CORE-13759
2018-05-18 21:00:13 +00:00
|
|
|
BOOLEAN Dirty;
|
2013-12-12 13:51:50 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2017-02-05 17:07:31 +00:00
|
|
|
Status = VfatHasFileSystem(DeviceToMount, &RecognizedFS, &FatInfo, FALSE);
|
2013-12-12 13:51:50 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-08-26 13:41:57 +00:00
|
|
|
DeviceExt = DeviceObject->DeviceExtension;
|
2013-12-12 13:51:50 +00:00
|
|
|
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;
|
2014-11-10 22:11:36 +00:00
|
|
|
DeviceExt->VolumeDevice = DeviceObject;
|
2013-12-12 13:51:50 +00:00
|
|
|
|
2020-10-18 13:23:52 +00:00
|
|
|
KeInitializeSpinLock(&DeviceExt->OverflowQueueSpinLock);
|
|
|
|
InitializeListHead(&DeviceExt->OverflowQueue);
|
|
|
|
DeviceExt->OverflowQueueCount = 0;
|
|
|
|
DeviceExt->PostedRequestCount = 0;
|
|
|
|
|
2013-12-12 13:51:50 +00:00
|
|
|
/* use same vpb as device disk */
|
|
|
|
DeviceObject->Vpb = Vpb;
|
|
|
|
DeviceToMount->Vpb = Vpb;
|
|
|
|
|
2017-02-06 18:14:01 +00:00
|
|
|
RtlCopyMemory(&DeviceExt->FatInfo, &FatInfo, sizeof(FATINFO));
|
2013-12-12 13:51:50 +00:00
|
|
|
|
|
|
|
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;
|
[FASTFAT] Completely rewrite support for dirty volumes.
Until now, our support for dirty volumes was totally broken
to a point where, on FAT32 volume, the dirty couldn't even
be written nor read from the disk.
This commit totally rewrites its handling, for both FAT16 and FAT32
so that it's now fully functionnal. Furthermore, it also gets
totally compatible with our vfatlib, and thus, autochk.
Now, on mount, FastFAT will check if the volume is dirty or not, and
autochk will be able to ask for a repair if dirty. vfatlib will
repair the volume and remove the dirty bit. So that, on next
reboot, the volume will be mounted clean.
As a reminder, the dirty bit is set immediately after mounting
the volume, so that, if you crash or have a powercut, autochk
will always attempt to repair your volume (with more or less,
that's FAT!).
If you want to experience without breaking your FAT volume,
just boot, open a cmd prompt and type: fsutil dirty set c:
and reboot!
CORE-13758
CORE-13760
CORE-13759
2018-05-18 21:00:13 +00:00
|
|
|
/* We don't define dirty bit functions here
|
|
|
|
* FAT12 doesn't have such bit and they won't get called
|
|
|
|
*/
|
2013-12-12 13:51:50 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case FAT16:
|
|
|
|
case FATX16:
|
|
|
|
DeviceExt->GetNextCluster = FAT16GetNextCluster;
|
|
|
|
DeviceExt->FindAndMarkAvailableCluster = FAT16FindAndMarkAvailableCluster;
|
|
|
|
DeviceExt->WriteCluster = FAT16WriteCluster;
|
[FASTFAT] Completely rewrite support for dirty volumes.
Until now, our support for dirty volumes was totally broken
to a point where, on FAT32 volume, the dirty couldn't even
be written nor read from the disk.
This commit totally rewrites its handling, for both FAT16 and FAT32
so that it's now fully functionnal. Furthermore, it also gets
totally compatible with our vfatlib, and thus, autochk.
Now, on mount, FastFAT will check if the volume is dirty or not, and
autochk will be able to ask for a repair if dirty. vfatlib will
repair the volume and remove the dirty bit. So that, on next
reboot, the volume will be mounted clean.
As a reminder, the dirty bit is set immediately after mounting
the volume, so that, if you crash or have a powercut, autochk
will always attempt to repair your volume (with more or less,
that's FAT!).
If you want to experience without breaking your FAT volume,
just boot, open a cmd prompt and type: fsutil dirty set c:
and reboot!
CORE-13758
CORE-13760
CORE-13759
2018-05-18 21:00:13 +00:00
|
|
|
DeviceExt->GetDirtyStatus = FAT16GetDirtyStatus;
|
|
|
|
DeviceExt->SetDirtyStatus = FAT16SetDirtyStatus;
|
2013-12-12 13:51:50 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case FAT32:
|
|
|
|
case FATX32:
|
|
|
|
DeviceExt->GetNextCluster = FAT32GetNextCluster;
|
|
|
|
DeviceExt->FindAndMarkAvailableCluster = FAT32FindAndMarkAvailableCluster;
|
|
|
|
DeviceExt->WriteCluster = FAT32WriteCluster;
|
[FASTFAT] Completely rewrite support for dirty volumes.
Until now, our support for dirty volumes was totally broken
to a point where, on FAT32 volume, the dirty couldn't even
be written nor read from the disk.
This commit totally rewrites its handling, for both FAT16 and FAT32
so that it's now fully functionnal. Furthermore, it also gets
totally compatible with our vfatlib, and thus, autochk.
Now, on mount, FastFAT will check if the volume is dirty or not, and
autochk will be able to ask for a repair if dirty. vfatlib will
repair the volume and remove the dirty bit. So that, on next
reboot, the volume will be mounted clean.
As a reminder, the dirty bit is set immediately after mounting
the volume, so that, if you crash or have a powercut, autochk
will always attempt to repair your volume (with more or less,
that's FAT!).
If you want to experience without breaking your FAT volume,
just boot, open a cmd prompt and type: fsutil dirty set c:
and reboot!
CORE-13758
CORE-13760
CORE-13759
2018-05-18 21:00:13 +00:00
|
|
|
DeviceExt->GetDirtyStatus = FAT32GetDirtyStatus;
|
|
|
|
DeviceExt->SetDirtyStatus = FAT32SetDirtyStatus;
|
2013-12-12 13:51:50 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (DeviceExt->FatInfo.FatType == FATX16 ||
|
|
|
|
DeviceExt->FatInfo.FatType == FATX32)
|
|
|
|
{
|
|
|
|
DeviceExt->Flags |= VCB_IS_FATX;
|
|
|
|
DeviceExt->BaseDateYear = 2000;
|
2017-02-17 22:25:03 +00:00
|
|
|
RtlCopyMemory(&DeviceExt->Dispatch, &FatXDispatch, sizeof(VFAT_DISPATCH));
|
2013-12-12 13:51:50 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DeviceExt->BaseDateYear = 1980;
|
2017-02-17 22:25:03 +00:00
|
|
|
RtlCopyMemory(&DeviceExt->Dispatch, &FatDispatch, sizeof(VFAT_DISPATCH));
|
2013-12-12 13:51:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2014-11-10 17:42:51 +00:00
|
|
|
DeviceExt->IoVPB = DeviceObject->Vpb;
|
2018-08-21 06:36:51 +00:00
|
|
|
DeviceExt->SpareVPB = ExAllocatePoolWithTag(NonPagedPool, sizeof(VPB), TAG_VPB);
|
2014-11-10 17:42:51 +00:00
|
|
|
if (DeviceExt->SpareVPB == NULL)
|
|
|
|
{
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
goto ByeBye;
|
|
|
|
}
|
|
|
|
|
2017-09-24 08:56:06 +00:00
|
|
|
DeviceExt->Statistics = ExAllocatePoolWithTag(NonPagedPool,
|
|
|
|
sizeof(STATISTICS) * VfatGlobalData->NumberProcessors,
|
2018-08-18 17:00:42 +00:00
|
|
|
TAG_STATS);
|
2017-09-24 08:56:06 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-12-12 13:51:50 +00:00
|
|
|
DeviceExt->FATFileObject = IoCreateStreamFileObject(NULL, DeviceExt->StorageDevice);
|
|
|
|
Fcb = vfatNewFCB(DeviceExt, &NameU);
|
|
|
|
if (Fcb == NULL)
|
|
|
|
{
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
goto ByeBye;
|
|
|
|
}
|
|
|
|
|
[FASTFAT] Improvements for volume dismount + minor bugfixing.
- Cache the RootFcb so that its cleanup can be handled separately
during dismounting.
- Force volume dismount at cleanup if the VCB_DISMOUNT_PENDING flag
is set.
- Actually dismount a volume if its VCB has been flagged as not good,
or if we force dismounting.
NOTE: In their *CheckForDismount() function, our 3rd-party FS drivers
as well as MS' fastfat, perform a comparison check of the current VCB's
VPB ReferenceCount with some sort of "dangling"/"residual" open count.
It seems to be related to the fact that the volume root directory as
well as auxiliary data stream(s) are still opened, and only these are
allowed to be opened at that moment. After analysis it appears that for
the ReactOS' fastfat, this number is equal to "3".
- On dismounting, cleanup and destroy the RootFcb, VolumeFcb and the
FATFileObject. Then cleanup the SpareVPB or the IoVPB members, and
finish by removing the dismounted volume from the VolumeListEntry
and cleaning up the notify synchronization object and the resources.
- During dismounting, and on shutdown, flush the volume before
resetting its dirty bit.
- On shutdown, after volume flushing, try to unmount it without forcing.
- Release the VCB resources only when we actually dismount the volume
in VfatCheckForDismount().
- Initialize first the notify list and the synchronization object,
before sending the FSRTL_VOLUME_MOUNT notification.
- If we failed at mounting a volume but its VCB's FATFileObject was
already initialized, first call CcUninitializeCacheMap() on it
before dereferencing it.
- Send FSRTL_VOLUME_LOCK, FSRTL_VOLUME_LOCK_FAILED and
FSRTL_VOLUME_UNLOCK notifications during volume locking (and failure)
and volume unlocking.
- Flush the volume before locking it, and clean its dirty bit if needed.
NOTE: In addition to checking for VCB_CLEAR_DIRTY, we also check for the
presence of the VCB_IS_DIRTY flag before cleaning up the dirty bit: this
allows us to not re-clean the bit if it has been previously cleaned.
This is needed for instance in this scenario:
- The volume is locked (it gets flushed and the dirty bit is possibly cleared);
- The volume then gets formatted with a completely different FS, that
possibly clears up the first sector (e.g. BTRFS ignores 1st sector);
- The volume is then dismounted: if we didn't check whether VCB_IS_DIRTY
was set prior to resetting it, we could attempt clearing it again! But
now that the volume's filesystem has been completely changed, we would
then try to modify the dirty bit on an erroneous position on disk!
That's why it should not be touched in this case during dismounting.
- The volume is unlocked (same comment as above), and later can be
detected as being BTRFS.
2018-11-11 16:17:48 +00:00
|
|
|
Status = vfatAttachFCBToFileObject(DeviceExt, Fcb, DeviceExt->FATFileObject);
|
|
|
|
if (!NT_SUCCESS(Status))
|
2013-12-12 13:51:50 +00:00
|
|
|
goto ByeBye;
|
|
|
|
|
|
|
|
DeviceExt->FATFileObject->PrivateCacheMap = NULL;
|
|
|
|
Fcb->FileObject = DeviceExt->FATFileObject;
|
|
|
|
|
[FASTFAT] Improvements for volume dismount + minor bugfixing.
- Cache the RootFcb so that its cleanup can be handled separately
during dismounting.
- Force volume dismount at cleanup if the VCB_DISMOUNT_PENDING flag
is set.
- Actually dismount a volume if its VCB has been flagged as not good,
or if we force dismounting.
NOTE: In their *CheckForDismount() function, our 3rd-party FS drivers
as well as MS' fastfat, perform a comparison check of the current VCB's
VPB ReferenceCount with some sort of "dangling"/"residual" open count.
It seems to be related to the fact that the volume root directory as
well as auxiliary data stream(s) are still opened, and only these are
allowed to be opened at that moment. After analysis it appears that for
the ReactOS' fastfat, this number is equal to "3".
- On dismounting, cleanup and destroy the RootFcb, VolumeFcb and the
FATFileObject. Then cleanup the SpareVPB or the IoVPB members, and
finish by removing the dismounted volume from the VolumeListEntry
and cleaning up the notify synchronization object and the resources.
- During dismounting, and on shutdown, flush the volume before
resetting its dirty bit.
- On shutdown, after volume flushing, try to unmount it without forcing.
- Release the VCB resources only when we actually dismount the volume
in VfatCheckForDismount().
- Initialize first the notify list and the synchronization object,
before sending the FSRTL_VOLUME_MOUNT notification.
- If we failed at mounting a volume but its VCB's FATFileObject was
already initialized, first call CcUninitializeCacheMap() on it
before dereferencing it.
- Send FSRTL_VOLUME_LOCK, FSRTL_VOLUME_LOCK_FAILED and
FSRTL_VOLUME_UNLOCK notifications during volume locking (and failure)
and volume unlocking.
- Flush the volume before locking it, and clean its dirty bit if needed.
NOTE: In addition to checking for VCB_CLEAR_DIRTY, we also check for the
presence of the VCB_IS_DIRTY flag before cleaning up the dirty bit: this
allows us to not re-clean the bit if it has been previously cleaned.
This is needed for instance in this scenario:
- The volume is locked (it gets flushed and the dirty bit is possibly cleared);
- The volume then gets formatted with a completely different FS, that
possibly clears up the first sector (e.g. BTRFS ignores 1st sector);
- The volume is then dismounted: if we didn't check whether VCB_IS_DIRTY
was set prior to resetting it, we could attempt clearing it again! But
now that the volume's filesystem has been completely changed, we would
then try to modify the dirty bit on an erroneous position on disk!
That's why it should not be touched in this case during dismounting.
- The volume is unlocked (same comment as above), and later can be
detected as being BTRFS.
2018-11-11 16:17:48 +00:00
|
|
|
Fcb->Flags = FCB_IS_FAT;
|
2013-12-12 13:51:50 +00:00
|
|
|
Fcb->RFCB.FileSize.QuadPart = DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector;
|
|
|
|
Fcb->RFCB.ValidDataLength = Fcb->RFCB.FileSize;
|
|
|
|
Fcb->RFCB.AllocationSize = Fcb->RFCB.FileSize;
|
|
|
|
|
2014-08-24 03:28:01 +00:00
|
|
|
_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;
|
2013-12-12 13:51:50 +00:00
|
|
|
|
|
|
|
DeviceExt->LastAvailableCluster = 2;
|
2018-06-09 16:21:32 +00:00
|
|
|
CountAvailableClusters(DeviceExt, NULL);
|
2013-12-12 13:51:50 +00:00
|
|
|
ExInitializeResourceLite(&DeviceExt->FatResource);
|
|
|
|
|
|
|
|
InitializeListHead(&DeviceExt->FcbListHead);
|
|
|
|
|
|
|
|
VolumeFcb = vfatNewFCB(DeviceExt, &VolumeNameU);
|
|
|
|
if (VolumeFcb == NULL)
|
|
|
|
{
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
goto ByeBye;
|
|
|
|
}
|
|
|
|
|
|
|
|
VolumeFcb->Flags = FCB_IS_VOLUME;
|
2016-06-11 09:32:00 +00:00
|
|
|
VolumeFcb->RFCB.FileSize.QuadPart = (LONGLONG) DeviceExt->FatInfo.Sectors * DeviceExt->FatInfo.BytesPerSector;
|
2013-12-12 13:51:50 +00:00
|
|
|
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 */
|
2017-02-18 21:24:31 +00:00
|
|
|
VolumeLabelU.Buffer = DeviceObject->Vpb->VolumeLabel;
|
|
|
|
VolumeLabelU.Length = 0;
|
|
|
|
VolumeLabelU.MaximumLength = sizeof(DeviceObject->Vpb->VolumeLabel);
|
|
|
|
ReadVolumeLabel(DeviceExt, 0, vfatVolumeIsFatX(DeviceExt), &VolumeLabelU);
|
|
|
|
Vpb->VolumeLabelLength = VolumeLabelU.Length;
|
2013-12-12 13:51:50 +00:00
|
|
|
|
[FASTFAT] Completely rewrite support for dirty volumes.
Until now, our support for dirty volumes was totally broken
to a point where, on FAT32 volume, the dirty couldn't even
be written nor read from the disk.
This commit totally rewrites its handling, for both FAT16 and FAT32
so that it's now fully functionnal. Furthermore, it also gets
totally compatible with our vfatlib, and thus, autochk.
Now, on mount, FastFAT will check if the volume is dirty or not, and
autochk will be able to ask for a repair if dirty. vfatlib will
repair the volume and remove the dirty bit. So that, on next
reboot, the volume will be mounted clean.
As a reminder, the dirty bit is set immediately after mounting
the volume, so that, if you crash or have a powercut, autochk
will always attempt to repair your volume (with more or less,
that's FAT!).
If you want to experience without breaking your FAT volume,
just boot, open a cmd prompt and type: fsutil dirty set c:
and reboot!
CORE-13758
CORE-13760
CORE-13759
2018-05-18 21:00:13 +00:00
|
|
|
/* read dirty bit status */
|
|
|
|
Status = GetDirtyStatus(DeviceExt, &Dirty);
|
2013-12-12 13:51:50 +00:00
|
|
|
if (NT_SUCCESS(Status))
|
|
|
|
{
|
[FASTFAT] Completely rewrite support for dirty volumes.
Until now, our support for dirty volumes was totally broken
to a point where, on FAT32 volume, the dirty couldn't even
be written nor read from the disk.
This commit totally rewrites its handling, for both FAT16 and FAT32
so that it's now fully functionnal. Furthermore, it also gets
totally compatible with our vfatlib, and thus, autochk.
Now, on mount, FastFAT will check if the volume is dirty or not, and
autochk will be able to ask for a repair if dirty. vfatlib will
repair the volume and remove the dirty bit. So that, on next
reboot, the volume will be mounted clean.
As a reminder, the dirty bit is set immediately after mounting
the volume, so that, if you crash or have a powercut, autochk
will always attempt to repair your volume (with more or less,
that's FAT!).
If you want to experience without breaking your FAT volume,
just boot, open a cmd prompt and type: fsutil dirty set c:
and reboot!
CORE-13758
CORE-13760
CORE-13759
2018-05-18 21:00:13 +00:00
|
|
|
/* The volume wasn't dirty, it was properly dismounted */
|
|
|
|
if (!Dirty)
|
2013-12-12 13:51:50 +00:00
|
|
|
{
|
[FASTFAT] Completely rewrite support for dirty volumes.
Until now, our support for dirty volumes was totally broken
to a point where, on FAT32 volume, the dirty couldn't even
be written nor read from the disk.
This commit totally rewrites its handling, for both FAT16 and FAT32
so that it's now fully functionnal. Furthermore, it also gets
totally compatible with our vfatlib, and thus, autochk.
Now, on mount, FastFAT will check if the volume is dirty or not, and
autochk will be able to ask for a repair if dirty. vfatlib will
repair the volume and remove the dirty bit. So that, on next
reboot, the volume will be mounted clean.
As a reminder, the dirty bit is set immediately after mounting
the volume, so that, if you crash or have a powercut, autochk
will always attempt to repair your volume (with more or less,
that's FAT!).
If you want to experience without breaking your FAT volume,
just boot, open a cmd prompt and type: fsutil dirty set c:
and reboot!
CORE-13758
CORE-13760
CORE-13759
2018-05-18 21:00:13 +00:00
|
|
|
/* Mark it dirty now! */
|
|
|
|
SetDirtyStatus(DeviceExt, TRUE);
|
2013-12-12 13:51:50 +00:00
|
|
|
VolumeFcb->Flags |= VCB_CLEAR_DIRTY;
|
|
|
|
}
|
[FASTFAT] Completely rewrite support for dirty volumes.
Until now, our support for dirty volumes was totally broken
to a point where, on FAT32 volume, the dirty couldn't even
be written nor read from the disk.
This commit totally rewrites its handling, for both FAT16 and FAT32
so that it's now fully functionnal. Furthermore, it also gets
totally compatible with our vfatlib, and thus, autochk.
Now, on mount, FastFAT will check if the volume is dirty or not, and
autochk will be able to ask for a repair if dirty. vfatlib will
repair the volume and remove the dirty bit. So that, on next
reboot, the volume will be mounted clean.
As a reminder, the dirty bit is set immediately after mounting
the volume, so that, if you crash or have a powercut, autochk
will always attempt to repair your volume (with more or less,
that's FAT!).
If you want to experience without breaking your FAT volume,
just boot, open a cmd prompt and type: fsutil dirty set c:
and reboot!
CORE-13758
CORE-13760
CORE-13759
2018-05-18 21:00:13 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
DPRINT1("Mounting a dirty volume\n");
|
|
|
|
}
|
2013-12-12 13:51:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
VolumeFcb->Flags |= VCB_IS_DIRTY;
|
2018-05-16 19:44:47 +00:00
|
|
|
if (BooleanFlagOn(Vpb->RealDevice->Flags, DO_SYSTEM_BOOT_PARTITION))
|
|
|
|
{
|
|
|
|
SetFlag(DeviceExt->Flags, VCB_IS_SYS_OR_HAS_PAGE);
|
|
|
|
}
|
2013-12-12 13:51:50 +00:00
|
|
|
|
[FASTFAT] Improvements for volume dismount + minor bugfixing.
- Cache the RootFcb so that its cleanup can be handled separately
during dismounting.
- Force volume dismount at cleanup if the VCB_DISMOUNT_PENDING flag
is set.
- Actually dismount a volume if its VCB has been flagged as not good,
or if we force dismounting.
NOTE: In their *CheckForDismount() function, our 3rd-party FS drivers
as well as MS' fastfat, perform a comparison check of the current VCB's
VPB ReferenceCount with some sort of "dangling"/"residual" open count.
It seems to be related to the fact that the volume root directory as
well as auxiliary data stream(s) are still opened, and only these are
allowed to be opened at that moment. After analysis it appears that for
the ReactOS' fastfat, this number is equal to "3".
- On dismounting, cleanup and destroy the RootFcb, VolumeFcb and the
FATFileObject. Then cleanup the SpareVPB or the IoVPB members, and
finish by removing the dismounted volume from the VolumeListEntry
and cleaning up the notify synchronization object and the resources.
- During dismounting, and on shutdown, flush the volume before
resetting its dirty bit.
- On shutdown, after volume flushing, try to unmount it without forcing.
- Release the VCB resources only when we actually dismount the volume
in VfatCheckForDismount().
- Initialize first the notify list and the synchronization object,
before sending the FSRTL_VOLUME_MOUNT notification.
- If we failed at mounting a volume but its VCB's FATFileObject was
already initialized, first call CcUninitializeCacheMap() on it
before dereferencing it.
- Send FSRTL_VOLUME_LOCK, FSRTL_VOLUME_LOCK_FAILED and
FSRTL_VOLUME_UNLOCK notifications during volume locking (and failure)
and volume unlocking.
- Flush the volume before locking it, and clean its dirty bit if needed.
NOTE: In addition to checking for VCB_CLEAR_DIRTY, we also check for the
presence of the VCB_IS_DIRTY flag before cleaning up the dirty bit: this
allows us to not re-clean the bit if it has been previously cleaned.
This is needed for instance in this scenario:
- The volume is locked (it gets flushed and the dirty bit is possibly cleared);
- The volume then gets formatted with a completely different FS, that
possibly clears up the first sector (e.g. BTRFS ignores 1st sector);
- The volume is then dismounted: if we didn't check whether VCB_IS_DIRTY
was set prior to resetting it, we could attempt clearing it again! But
now that the volume's filesystem has been completely changed, we would
then try to modify the dirty bit on an erroneous position on disk!
That's why it should not be touched in this case during dismounting.
- The volume is unlocked (same comment as above), and later can be
detected as being BTRFS.
2018-11-11 16:17:48 +00:00
|
|
|
/* Initialize the notify list and synchronization object */
|
2014-03-07 19:38:35 +00:00
|
|
|
InitializeListHead(&DeviceExt->NotifyList);
|
[FASTFAT] Improvements for volume dismount + minor bugfixing.
- Cache the RootFcb so that its cleanup can be handled separately
during dismounting.
- Force volume dismount at cleanup if the VCB_DISMOUNT_PENDING flag
is set.
- Actually dismount a volume if its VCB has been flagged as not good,
or if we force dismounting.
NOTE: In their *CheckForDismount() function, our 3rd-party FS drivers
as well as MS' fastfat, perform a comparison check of the current VCB's
VPB ReferenceCount with some sort of "dangling"/"residual" open count.
It seems to be related to the fact that the volume root directory as
well as auxiliary data stream(s) are still opened, and only these are
allowed to be opened at that moment. After analysis it appears that for
the ReactOS' fastfat, this number is equal to "3".
- On dismounting, cleanup and destroy the RootFcb, VolumeFcb and the
FATFileObject. Then cleanup the SpareVPB or the IoVPB members, and
finish by removing the dismounted volume from the VolumeListEntry
and cleaning up the notify synchronization object and the resources.
- During dismounting, and on shutdown, flush the volume before
resetting its dirty bit.
- On shutdown, after volume flushing, try to unmount it without forcing.
- Release the VCB resources only when we actually dismount the volume
in VfatCheckForDismount().
- Initialize first the notify list and the synchronization object,
before sending the FSRTL_VOLUME_MOUNT notification.
- If we failed at mounting a volume but its VCB's FATFileObject was
already initialized, first call CcUninitializeCacheMap() on it
before dereferencing it.
- Send FSRTL_VOLUME_LOCK, FSRTL_VOLUME_LOCK_FAILED and
FSRTL_VOLUME_UNLOCK notifications during volume locking (and failure)
and volume unlocking.
- Flush the volume before locking it, and clean its dirty bit if needed.
NOTE: In addition to checking for VCB_CLEAR_DIRTY, we also check for the
presence of the VCB_IS_DIRTY flag before cleaning up the dirty bit: this
allows us to not re-clean the bit if it has been previously cleaned.
This is needed for instance in this scenario:
- The volume is locked (it gets flushed and the dirty bit is possibly cleared);
- The volume then gets formatted with a completely different FS, that
possibly clears up the first sector (e.g. BTRFS ignores 1st sector);
- The volume is then dismounted: if we didn't check whether VCB_IS_DIRTY
was set prior to resetting it, we could attempt clearing it again! But
now that the volume's filesystem has been completely changed, we would
then try to modify the dirty bit on an erroneous position on disk!
That's why it should not be touched in this case during dismounting.
- The volume is unlocked (same comment as above), and later can be
detected as being BTRFS.
2018-11-11 16:17:48 +00:00
|
|
|
FsRtlNotifyInitializeSync(&DeviceExt->NotifySync);
|
|
|
|
|
|
|
|
/* The VCB is OK for usage */
|
|
|
|
SetFlag(DeviceExt->Flags, VCB_GOOD);
|
|
|
|
|
|
|
|
/* Send the mount notification */
|
|
|
|
FsRtlNotifyVolumeEvent(DeviceExt->FATFileObject, FSRTL_VOLUME_MOUNT);
|
2013-12-12 13:51:50 +00:00
|
|
|
|
2014-11-02 11:30:14 +00:00
|
|
|
DPRINT("Mount success\n");
|
2014-10-30 20:56:40 +00:00
|
|
|
|
2013-12-12 13:51:50 +00:00
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
|
2002-03-18 22:37:13 +00:00
|
|
|
ByeBye:
|
2013-12-12 13:51:50 +00:00
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
/* Cleanup */
|
|
|
|
if (DeviceExt && DeviceExt->FATFileObject)
|
[FASTFAT] Improvements for volume dismount + minor bugfixing.
- Cache the RootFcb so that its cleanup can be handled separately
during dismounting.
- Force volume dismount at cleanup if the VCB_DISMOUNT_PENDING flag
is set.
- Actually dismount a volume if its VCB has been flagged as not good,
or if we force dismounting.
NOTE: In their *CheckForDismount() function, our 3rd-party FS drivers
as well as MS' fastfat, perform a comparison check of the current VCB's
VPB ReferenceCount with some sort of "dangling"/"residual" open count.
It seems to be related to the fact that the volume root directory as
well as auxiliary data stream(s) are still opened, and only these are
allowed to be opened at that moment. After analysis it appears that for
the ReactOS' fastfat, this number is equal to "3".
- On dismounting, cleanup and destroy the RootFcb, VolumeFcb and the
FATFileObject. Then cleanup the SpareVPB or the IoVPB members, and
finish by removing the dismounted volume from the VolumeListEntry
and cleaning up the notify synchronization object and the resources.
- During dismounting, and on shutdown, flush the volume before
resetting its dirty bit.
- On shutdown, after volume flushing, try to unmount it without forcing.
- Release the VCB resources only when we actually dismount the volume
in VfatCheckForDismount().
- Initialize first the notify list and the synchronization object,
before sending the FSRTL_VOLUME_MOUNT notification.
- If we failed at mounting a volume but its VCB's FATFileObject was
already initialized, first call CcUninitializeCacheMap() on it
before dereferencing it.
- Send FSRTL_VOLUME_LOCK, FSRTL_VOLUME_LOCK_FAILED and
FSRTL_VOLUME_UNLOCK notifications during volume locking (and failure)
and volume unlocking.
- Flush the volume before locking it, and clean its dirty bit if needed.
NOTE: In addition to checking for VCB_CLEAR_DIRTY, we also check for the
presence of the VCB_IS_DIRTY flag before cleaning up the dirty bit: this
allows us to not re-clean the bit if it has been previously cleaned.
This is needed for instance in this scenario:
- The volume is locked (it gets flushed and the dirty bit is possibly cleared);
- The volume then gets formatted with a completely different FS, that
possibly clears up the first sector (e.g. BTRFS ignores 1st sector);
- The volume is then dismounted: if we didn't check whether VCB_IS_DIRTY
was set prior to resetting it, we could attempt clearing it again! But
now that the volume's filesystem has been completely changed, we would
then try to modify the dirty bit on an erroneous position on disk!
That's why it should not be touched in this case during dismounting.
- The volume is unlocked (same comment as above), and later can be
detected as being BTRFS.
2018-11-11 16:17:48 +00:00
|
|
|
{
|
|
|
|
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);
|
2014-11-10 17:42:51 +00:00
|
|
|
if (DeviceExt && DeviceExt->SpareVPB)
|
2018-08-21 06:36:51 +00:00
|
|
|
ExFreePoolWithTag(DeviceExt->SpareVPB, TAG_VPB);
|
2017-09-24 08:56:06 +00:00
|
|
|
if (DeviceExt && DeviceExt->Statistics)
|
2018-08-18 17:00:42 +00:00
|
|
|
ExFreePoolWithTag(DeviceExt->Statistics, TAG_STATS);
|
2013-12-12 13:51:50 +00:00
|
|
|
if (DeviceObject)
|
|
|
|
IoDeleteDevice(DeviceObject);
|
|
|
|
}
|
2002-03-18 22:37:13 +00:00
|
|
|
|
2013-12-12 13:51:50 +00:00
|
|
|
return Status;
|
2002-03-18 22:37:13 +00:00
|
|
|
}
|
|
|
|
|
2002-04-10 09:58:45 +00:00
|
|
|
|
|
|
|
/*
|
2003-06-24 21:34:41 +00:00
|
|
|
* FUNCTION: Verify the filesystem
|
2002-04-10 09:58:45 +00:00
|
|
|
*/
|
2013-12-12 13:51:50 +00:00
|
|
|
static
|
|
|
|
NTSTATUS
|
|
|
|
VfatVerify(
|
|
|
|
PVFAT_IRP_CONTEXT IrpContext)
|
2002-04-10 09:58:45 +00:00
|
|
|
{
|
2013-12-12 13:51:50 +00:00
|
|
|
PDEVICE_OBJECT DeviceToVerify;
|
2017-02-11 18:33:47 +00:00
|
|
|
NTSTATUS Status;
|
2013-12-12 13:51:50 +00:00
|
|
|
FATINFO FatInfo;
|
|
|
|
BOOLEAN RecognizedFS;
|
2017-02-05 17:07:31 +00:00
|
|
|
PDEVICE_EXTENSION DeviceExt;
|
2017-02-11 20:26:33 +00:00
|
|
|
BOOLEAN AllowRaw;
|
2017-02-12 19:31:31 +00:00
|
|
|
PVPB Vpb;
|
2017-02-13 21:17:26 +00:00
|
|
|
ULONG ChangeCount, BufSize = sizeof(ChangeCount);
|
2013-12-12 13:51:50 +00:00
|
|
|
|
|
|
|
DPRINT("VfatVerify(IrpContext %p)\n", IrpContext);
|
|
|
|
|
|
|
|
DeviceToVerify = IrpContext->Stack->Parameters.VerifyVolume.DeviceObject;
|
2017-02-05 17:07:31 +00:00
|
|
|
DeviceExt = DeviceToVerify->DeviceExtension;
|
2017-02-12 19:31:31 +00:00
|
|
|
Vpb = IrpContext->Stack->Parameters.VerifyVolume.Vpb;
|
2017-02-11 20:26:33 +00:00
|
|
|
AllowRaw = BooleanFlagOn(IrpContext->Stack->Flags, SL_ALLOW_RAW_MOUNT);
|
|
|
|
|
2017-02-12 19:31:31 +00:00
|
|
|
if (!BooleanFlagOn(Vpb->RealDevice->Flags, DO_VERIFY_VOLUME))
|
|
|
|
{
|
|
|
|
DPRINT("Already verified\n");
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2017-02-05 17:07:31 +00:00
|
|
|
Status = VfatBlockDeviceIoControl(DeviceExt->StorageDevice,
|
2013-12-12 13:51:50 +00:00
|
|
|
IOCTL_DISK_CHECK_VERIFY,
|
|
|
|
NULL,
|
|
|
|
0,
|
2017-02-13 21:17:26 +00:00
|
|
|
&ChangeCount,
|
|
|
|
&BufSize,
|
2013-12-12 13:51:50 +00:00
|
|
|
TRUE);
|
|
|
|
if (!NT_SUCCESS(Status) && Status != STATUS_VERIFY_REQUIRED)
|
|
|
|
{
|
|
|
|
DPRINT("VfatBlockDeviceIoControl() failed (Status %lx)\n", Status);
|
2017-02-11 20:26:33 +00:00
|
|
|
Status = (AllowRaw ? STATUS_WRONG_VOLUME : Status);
|
2013-12-12 13:51:50 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-02-05 17:07:31 +00:00
|
|
|
Status = VfatHasFileSystem(DeviceExt->StorageDevice, &RecognizedFS, &FatInfo, TRUE);
|
2013-12-12 13:51:50 +00:00
|
|
|
if (!NT_SUCCESS(Status) || RecognizedFS == FALSE)
|
2004-06-23 20:23:59 +00:00
|
|
|
{
|
2017-02-11 20:26:33 +00:00
|
|
|
if (NT_SUCCESS(Status) || AllowRaw)
|
2017-02-18 21:24:31 +00:00
|
|
|
{
|
2017-02-11 20:26:33 +00:00
|
|
|
Status = STATUS_WRONG_VOLUME;
|
2017-02-18 21:24:31 +00:00
|
|
|
}
|
2004-06-23 20:23:59 +00:00
|
|
|
}
|
2013-12-12 13:51:50 +00:00
|
|
|
else if (sizeof(FATINFO) == RtlCompareMemory(&FatInfo, &DeviceExt->FatInfo, sizeof(FATINFO)))
|
2004-06-23 20:23:59 +00:00
|
|
|
{
|
2017-02-18 21:24:31 +00:00
|
|
|
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))
|
2017-02-05 17:07:31 +00:00
|
|
|
{
|
2017-02-18 21:24:31 +00:00
|
|
|
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");
|
|
|
|
}
|
2017-02-05 17:07:31 +00:00
|
|
|
}
|
2004-06-23 20:23:59 +00:00
|
|
|
}
|
2013-12-12 13:51:50 +00:00
|
|
|
else
|
2007-10-19 07:50:59 +00:00
|
|
|
{
|
2013-12-12 13:51:50 +00:00
|
|
|
Status = STATUS_WRONG_VOLUME;
|
2004-06-23 20:23:59 +00:00
|
|
|
}
|
2013-12-12 13:51:50 +00:00
|
|
|
}
|
2005-05-08 02:16:32 +00:00
|
|
|
|
2017-02-12 19:31:31 +00:00
|
|
|
Vpb->RealDevice->Flags &= ~DO_VERIFY_VOLUME;
|
2017-02-05 17:07:31 +00:00
|
|
|
|
2013-12-12 13:51:50 +00:00
|
|
|
return Status;
|
2002-04-10 09:58:45 +00:00
|
|
|
}
|
|
|
|
|
2003-06-24 21:34:41 +00:00
|
|
|
|
2013-12-12 13:51:50 +00:00
|
|
|
static
|
|
|
|
NTSTATUS
|
|
|
|
VfatGetVolumeBitmap(
|
|
|
|
PVFAT_IRP_CONTEXT IrpContext)
|
2003-05-11 09:51:26 +00:00
|
|
|
{
|
2013-12-12 13:51:50 +00:00
|
|
|
DPRINT("VfatGetVolumeBitmap (IrpContext %p)\n", IrpContext);
|
|
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
2003-05-11 09:51:26 +00:00
|
|
|
}
|
|
|
|
|
2003-06-24 21:34:41 +00:00
|
|
|
|
2013-12-12 13:51:50 +00:00
|
|
|
static
|
|
|
|
NTSTATUS
|
|
|
|
VfatGetRetrievalPointers(
|
|
|
|
PVFAT_IRP_CONTEXT IrpContext)
|
2003-05-11 09:51:26 +00:00
|
|
|
{
|
2013-12-12 13:51:50 +00:00
|
|
|
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;
|
2003-05-11 09:51:26 +00:00
|
|
|
|
|
|
|
ByeBye:
|
2013-12-12 13:51:50 +00:00
|
|
|
ExReleaseResourceLite(&Fcb->MainResource);
|
2003-05-11 09:51:26 +00:00
|
|
|
|
2013-12-12 13:51:50 +00:00
|
|
|
return Status;
|
2003-05-11 09:51:26 +00:00
|
|
|
}
|
|
|
|
|
2013-12-12 13:51:50 +00:00
|
|
|
static
|
|
|
|
NTSTATUS
|
|
|
|
VfatMoveFile(
|
|
|
|
PVFAT_IRP_CONTEXT IrpContext)
|
2003-05-11 09:51:26 +00:00
|
|
|
{
|
2013-12-12 13:51:50 +00:00
|
|
|
DPRINT("VfatMoveFile(IrpContext %p)\n", IrpContext);
|
|
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
2003-05-11 09:51:26 +00:00
|
|
|
}
|
2002-04-10 09:58:45 +00:00
|
|
|
|
2013-12-12 13:51:50 +00:00
|
|
|
static
|
|
|
|
NTSTATUS
|
|
|
|
VfatIsVolumeDirty(
|
|
|
|
PVFAT_IRP_CONTEXT IrpContext)
|
2005-04-16 16:04:38 +00:00
|
|
|
{
|
2013-12-12 13:51:50 +00:00
|
|
|
PULONG Flags;
|
2005-05-08 02:16:32 +00:00
|
|
|
|
2013-12-12 13:51:50 +00:00
|
|
|
DPRINT("VfatIsVolumeDirty(IrpContext %p)\n", IrpContext);
|
2005-05-08 02:16:32 +00:00
|
|
|
|
2013-12-12 13:51:50 +00:00
|
|
|
if (IrpContext->Stack->Parameters.FileSystemControl.OutputBufferLength != sizeof(ULONG))
|
|
|
|
return STATUS_INVALID_BUFFER_SIZE;
|
|
|
|
else if (!IrpContext->Irp->AssociatedIrp.SystemBuffer)
|
|
|
|
return STATUS_INVALID_USER_BUFFER;
|
2005-05-08 02:16:32 +00:00
|
|
|
|
2013-12-12 13:51:50 +00:00
|
|
|
Flags = (PULONG)IrpContext->Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
*Flags = 0;
|
2005-05-08 02:16:32 +00:00
|
|
|
|
2017-02-17 17:58:18 +00:00
|
|
|
if (BooleanFlagOn(IrpContext->DeviceExt->VolumeFcb->Flags, VCB_IS_DIRTY) &&
|
|
|
|
!BooleanFlagOn(IrpContext->DeviceExt->VolumeFcb->Flags, VCB_CLEAR_DIRTY))
|
2013-12-12 13:51:50 +00:00
|
|
|
{
|
|
|
|
*Flags |= VOLUME_IS_DIRTY;
|
|
|
|
}
|
2005-05-08 02:16:32 +00:00
|
|
|
|
2017-09-05 19:08:01 +00:00
|
|
|
IrpContext->Irp->IoStatus.Information = sizeof(ULONG);
|
|
|
|
|
2013-12-12 13:51:50 +00:00
|
|
|
return STATUS_SUCCESS;
|
2005-04-16 16:04:38 +00:00
|
|
|
}
|
|
|
|
|
2013-12-12 13:51:50 +00:00
|
|
|
static
|
|
|
|
NTSTATUS
|
|
|
|
VfatMarkVolumeDirty(
|
|
|
|
PVFAT_IRP_CONTEXT IrpContext)
|
2005-04-16 16:04:38 +00:00
|
|
|
{
|
2013-12-12 13:51:50 +00:00
|
|
|
PDEVICE_EXTENSION DeviceExt;
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
|
|
|
|
DPRINT("VfatMarkVolumeDirty(IrpContext %p)\n", IrpContext);
|
|
|
|
DeviceExt = IrpContext->DeviceExt;
|
|
|
|
|
2017-02-17 17:58:18 +00:00
|
|
|
if (!BooleanFlagOn(DeviceExt->VolumeFcb->Flags, VCB_IS_DIRTY))
|
2013-12-12 13:51:50 +00:00
|
|
|
{
|
[FASTFAT] Completely rewrite support for dirty volumes.
Until now, our support for dirty volumes was totally broken
to a point where, on FAT32 volume, the dirty couldn't even
be written nor read from the disk.
This commit totally rewrites its handling, for both FAT16 and FAT32
so that it's now fully functionnal. Furthermore, it also gets
totally compatible with our vfatlib, and thus, autochk.
Now, on mount, FastFAT will check if the volume is dirty or not, and
autochk will be able to ask for a repair if dirty. vfatlib will
repair the volume and remove the dirty bit. So that, on next
reboot, the volume will be mounted clean.
As a reminder, the dirty bit is set immediately after mounting
the volume, so that, if you crash or have a powercut, autochk
will always attempt to repair your volume (with more or less,
that's FAT!).
If you want to experience without breaking your FAT volume,
just boot, open a cmd prompt and type: fsutil dirty set c:
and reboot!
CORE-13758
CORE-13760
CORE-13759
2018-05-18 21:00:13 +00:00
|
|
|
Status = SetDirtyStatus(DeviceExt, TRUE);
|
2013-12-12 13:51:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DeviceExt->VolumeFcb->Flags &= ~VCB_CLEAR_DIRTY;
|
|
|
|
|
|
|
|
return Status;
|
2005-04-16 16:04:38 +00:00
|
|
|
}
|
|
|
|
|
2014-10-30 20:56:40 +00:00
|
|
|
static
|
|
|
|
NTSTATUS
|
|
|
|
VfatLockOrUnlockVolume(
|
|
|
|
PVFAT_IRP_CONTEXT IrpContext,
|
|
|
|
BOOLEAN Lock)
|
|
|
|
{
|
|
|
|
PFILE_OBJECT FileObject;
|
|
|
|
PDEVICE_EXTENSION DeviceExt;
|
2014-11-07 15:38:31 +00:00
|
|
|
PVFATFCB Fcb;
|
2017-03-04 11:34:13 +00:00
|
|
|
PVPB Vpb;
|
2014-10-30 20:56:40 +00:00
|
|
|
|
2014-11-02 11:30:14 +00:00
|
|
|
DPRINT("VfatLockOrUnlockVolume(%p, %d)\n", IrpContext, Lock);
|
2014-10-30 20:56:40 +00:00
|
|
|
|
|
|
|
DeviceExt = IrpContext->DeviceExt;
|
|
|
|
FileObject = IrpContext->FileObject;
|
2014-11-07 15:38:31 +00:00
|
|
|
Fcb = FileObject->FsContext;
|
2017-03-04 11:34:13 +00:00
|
|
|
Vpb = DeviceExt->FATFileObject->Vpb;
|
2014-10-30 20:56:40 +00:00
|
|
|
|
|
|
|
/* Only allow locking with the volume open */
|
2017-02-17 17:58:18 +00:00
|
|
|
if (!BooleanFlagOn(Fcb->Flags, FCB_IS_VOLUME))
|
2014-10-30 20:56:40 +00:00
|
|
|
{
|
|
|
|
return STATUS_ACCESS_DENIED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Bail out if it's already in the demanded state */
|
2017-02-17 17:58:18 +00:00
|
|
|
if ((BooleanFlagOn(DeviceExt->Flags, VCB_VOLUME_LOCKED) && Lock) ||
|
|
|
|
(!BooleanFlagOn(DeviceExt->Flags, VCB_VOLUME_LOCKED) && !Lock))
|
2014-10-30 20:56:40 +00:00
|
|
|
{
|
|
|
|
return STATUS_ACCESS_DENIED;
|
|
|
|
}
|
|
|
|
|
2017-03-04 11:34:13 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
[FASTFAT] Improvements for volume dismount + minor bugfixing.
- Cache the RootFcb so that its cleanup can be handled separately
during dismounting.
- Force volume dismount at cleanup if the VCB_DISMOUNT_PENDING flag
is set.
- Actually dismount a volume if its VCB has been flagged as not good,
or if we force dismounting.
NOTE: In their *CheckForDismount() function, our 3rd-party FS drivers
as well as MS' fastfat, perform a comparison check of the current VCB's
VPB ReferenceCount with some sort of "dangling"/"residual" open count.
It seems to be related to the fact that the volume root directory as
well as auxiliary data stream(s) are still opened, and only these are
allowed to be opened at that moment. After analysis it appears that for
the ReactOS' fastfat, this number is equal to "3".
- On dismounting, cleanup and destroy the RootFcb, VolumeFcb and the
FATFileObject. Then cleanup the SpareVPB or the IoVPB members, and
finish by removing the dismounted volume from the VolumeListEntry
and cleaning up the notify synchronization object and the resources.
- During dismounting, and on shutdown, flush the volume before
resetting its dirty bit.
- On shutdown, after volume flushing, try to unmount it without forcing.
- Release the VCB resources only when we actually dismount the volume
in VfatCheckForDismount().
- Initialize first the notify list and the synchronization object,
before sending the FSRTL_VOLUME_MOUNT notification.
- If we failed at mounting a volume but its VCB's FATFileObject was
already initialized, first call CcUninitializeCacheMap() on it
before dereferencing it.
- Send FSRTL_VOLUME_LOCK, FSRTL_VOLUME_LOCK_FAILED and
FSRTL_VOLUME_UNLOCK notifications during volume locking (and failure)
and volume unlocking.
- Flush the volume before locking it, and clean its dirty bit if needed.
NOTE: In addition to checking for VCB_CLEAR_DIRTY, we also check for the
presence of the VCB_IS_DIRTY flag before cleaning up the dirty bit: this
allows us to not re-clean the bit if it has been previously cleaned.
This is needed for instance in this scenario:
- The volume is locked (it gets flushed and the dirty bit is possibly cleared);
- The volume then gets formatted with a completely different FS, that
possibly clears up the first sector (e.g. BTRFS ignores 1st sector);
- The volume is then dismounted: if we didn't check whether VCB_IS_DIRTY
was set prior to resetting it, we could attempt clearing it again! But
now that the volume's filesystem has been completely changed, we would
then try to modify the dirty bit on an erroneous position on disk!
That's why it should not be touched in this case during dismounting.
- The volume is unlocked (same comment as above), and later can be
detected as being BTRFS.
2018-11-11 16:17:48 +00:00
|
|
|
if (Lock)
|
|
|
|
{
|
|
|
|
FsRtlNotifyVolumeEvent(IrpContext->Stack->FileObject, FSRTL_VOLUME_LOCK);
|
|
|
|
}
|
|
|
|
|
2014-10-30 20:56:40 +00:00
|
|
|
/* Deny locking if we're not alone */
|
|
|
|
if (Lock && DeviceExt->OpenHandleCount != 1)
|
|
|
|
{
|
2017-12-17 17:19:03 +00:00
|
|
|
PLIST_ENTRY ListEntry;
|
|
|
|
|
2018-05-18 20:35:03 +00:00
|
|
|
#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
|
|
|
|
|
2017-12-17 17:19:03 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
[FASTFAT] Improvements for volume dismount + minor bugfixing.
- Cache the RootFcb so that its cleanup can be handled separately
during dismounting.
- Force volume dismount at cleanup if the VCB_DISMOUNT_PENDING flag
is set.
- Actually dismount a volume if its VCB has been flagged as not good,
or if we force dismounting.
NOTE: In their *CheckForDismount() function, our 3rd-party FS drivers
as well as MS' fastfat, perform a comparison check of the current VCB's
VPB ReferenceCount with some sort of "dangling"/"residual" open count.
It seems to be related to the fact that the volume root directory as
well as auxiliary data stream(s) are still opened, and only these are
allowed to be opened at that moment. After analysis it appears that for
the ReactOS' fastfat, this number is equal to "3".
- On dismounting, cleanup and destroy the RootFcb, VolumeFcb and the
FATFileObject. Then cleanup the SpareVPB or the IoVPB members, and
finish by removing the dismounted volume from the VolumeListEntry
and cleaning up the notify synchronization object and the resources.
- During dismounting, and on shutdown, flush the volume before
resetting its dirty bit.
- On shutdown, after volume flushing, try to unmount it without forcing.
- Release the VCB resources only when we actually dismount the volume
in VfatCheckForDismount().
- Initialize first the notify list and the synchronization object,
before sending the FSRTL_VOLUME_MOUNT notification.
- If we failed at mounting a volume but its VCB's FATFileObject was
already initialized, first call CcUninitializeCacheMap() on it
before dereferencing it.
- Send FSRTL_VOLUME_LOCK, FSRTL_VOLUME_LOCK_FAILED and
FSRTL_VOLUME_UNLOCK notifications during volume locking (and failure)
and volume unlocking.
- Flush the volume before locking it, and clean its dirty bit if needed.
NOTE: In addition to checking for VCB_CLEAR_DIRTY, we also check for the
presence of the VCB_IS_DIRTY flag before cleaning up the dirty bit: this
allows us to not re-clean the bit if it has been previously cleaned.
This is needed for instance in this scenario:
- The volume is locked (it gets flushed and the dirty bit is possibly cleared);
- The volume then gets formatted with a completely different FS, that
possibly clears up the first sector (e.g. BTRFS ignores 1st sector);
- The volume is then dismounted: if we didn't check whether VCB_IS_DIRTY
was set prior to resetting it, we could attempt clearing it again! But
now that the volume's filesystem has been completely changed, we would
then try to modify the dirty bit on an erroneous position on disk!
That's why it should not be touched in this case during dismounting.
- The volume is unlocked (same comment as above), and later can be
detected as being BTRFS.
2018-11-11 16:17:48 +00:00
|
|
|
FsRtlNotifyVolumeEvent(IrpContext->Stack->FileObject, FSRTL_VOLUME_LOCK_FAILED);
|
|
|
|
|
2014-10-30 20:56:40 +00:00
|
|
|
return STATUS_ACCESS_DENIED;
|
2018-05-18 20:35:03 +00:00
|
|
|
|
|
|
|
#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
|
2014-10-30 20:56:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Finally, proceed */
|
|
|
|
if (Lock)
|
|
|
|
{
|
[FASTFAT] Improvements for volume dismount + minor bugfixing.
- Cache the RootFcb so that its cleanup can be handled separately
during dismounting.
- Force volume dismount at cleanup if the VCB_DISMOUNT_PENDING flag
is set.
- Actually dismount a volume if its VCB has been flagged as not good,
or if we force dismounting.
NOTE: In their *CheckForDismount() function, our 3rd-party FS drivers
as well as MS' fastfat, perform a comparison check of the current VCB's
VPB ReferenceCount with some sort of "dangling"/"residual" open count.
It seems to be related to the fact that the volume root directory as
well as auxiliary data stream(s) are still opened, and only these are
allowed to be opened at that moment. After analysis it appears that for
the ReactOS' fastfat, this number is equal to "3".
- On dismounting, cleanup and destroy the RootFcb, VolumeFcb and the
FATFileObject. Then cleanup the SpareVPB or the IoVPB members, and
finish by removing the dismounted volume from the VolumeListEntry
and cleaning up the notify synchronization object and the resources.
- During dismounting, and on shutdown, flush the volume before
resetting its dirty bit.
- On shutdown, after volume flushing, try to unmount it without forcing.
- Release the VCB resources only when we actually dismount the volume
in VfatCheckForDismount().
- Initialize first the notify list and the synchronization object,
before sending the FSRTL_VOLUME_MOUNT notification.
- If we failed at mounting a volume but its VCB's FATFileObject was
already initialized, first call CcUninitializeCacheMap() on it
before dereferencing it.
- Send FSRTL_VOLUME_LOCK, FSRTL_VOLUME_LOCK_FAILED and
FSRTL_VOLUME_UNLOCK notifications during volume locking (and failure)
and volume unlocking.
- Flush the volume before locking it, and clean its dirty bit if needed.
NOTE: In addition to checking for VCB_CLEAR_DIRTY, we also check for the
presence of the VCB_IS_DIRTY flag before cleaning up the dirty bit: this
allows us to not re-clean the bit if it has been previously cleaned.
This is needed for instance in this scenario:
- The volume is locked (it gets flushed and the dirty bit is possibly cleared);
- The volume then gets formatted with a completely different FS, that
possibly clears up the first sector (e.g. BTRFS ignores 1st sector);
- The volume is then dismounted: if we didn't check whether VCB_IS_DIRTY
was set prior to resetting it, we could attempt clearing it again! But
now that the volume's filesystem has been completely changed, we would
then try to modify the dirty bit on an erroneous position on disk!
That's why it should not be touched in this case during dismounting.
- The volume is unlocked (same comment as above), and later can be
detected as being BTRFS.
2018-11-11 16:17:48 +00:00
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
|
2014-10-30 20:56:40 +00:00
|
|
|
DeviceExt->Flags |= VCB_VOLUME_LOCKED;
|
2017-03-04 11:34:13 +00:00
|
|
|
Vpb->Flags |= VPB_LOCKED;
|
2014-10-30 20:56:40 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DeviceExt->Flags &= ~VCB_VOLUME_LOCKED;
|
2017-03-04 11:34:13 +00:00
|
|
|
Vpb->Flags &= ~VPB_LOCKED;
|
[FASTFAT] Improvements for volume dismount + minor bugfixing.
- Cache the RootFcb so that its cleanup can be handled separately
during dismounting.
- Force volume dismount at cleanup if the VCB_DISMOUNT_PENDING flag
is set.
- Actually dismount a volume if its VCB has been flagged as not good,
or if we force dismounting.
NOTE: In their *CheckForDismount() function, our 3rd-party FS drivers
as well as MS' fastfat, perform a comparison check of the current VCB's
VPB ReferenceCount with some sort of "dangling"/"residual" open count.
It seems to be related to the fact that the volume root directory as
well as auxiliary data stream(s) are still opened, and only these are
allowed to be opened at that moment. After analysis it appears that for
the ReactOS' fastfat, this number is equal to "3".
- On dismounting, cleanup and destroy the RootFcb, VolumeFcb and the
FATFileObject. Then cleanup the SpareVPB or the IoVPB members, and
finish by removing the dismounted volume from the VolumeListEntry
and cleaning up the notify synchronization object and the resources.
- During dismounting, and on shutdown, flush the volume before
resetting its dirty bit.
- On shutdown, after volume flushing, try to unmount it without forcing.
- Release the VCB resources only when we actually dismount the volume
in VfatCheckForDismount().
- Initialize first the notify list and the synchronization object,
before sending the FSRTL_VOLUME_MOUNT notification.
- If we failed at mounting a volume but its VCB's FATFileObject was
already initialized, first call CcUninitializeCacheMap() on it
before dereferencing it.
- Send FSRTL_VOLUME_LOCK, FSRTL_VOLUME_LOCK_FAILED and
FSRTL_VOLUME_UNLOCK notifications during volume locking (and failure)
and volume unlocking.
- Flush the volume before locking it, and clean its dirty bit if needed.
NOTE: In addition to checking for VCB_CLEAR_DIRTY, we also check for the
presence of the VCB_IS_DIRTY flag before cleaning up the dirty bit: this
allows us to not re-clean the bit if it has been previously cleaned.
This is needed for instance in this scenario:
- The volume is locked (it gets flushed and the dirty bit is possibly cleared);
- The volume then gets formatted with a completely different FS, that
possibly clears up the first sector (e.g. BTRFS ignores 1st sector);
- The volume is then dismounted: if we didn't check whether VCB_IS_DIRTY
was set prior to resetting it, we could attempt clearing it again! But
now that the volume's filesystem has been completely changed, we would
then try to modify the dirty bit on an erroneous position on disk!
That's why it should not be touched in this case during dismounting.
- The volume is unlocked (same comment as above), and later can be
detected as being BTRFS.
2018-11-11 16:17:48 +00:00
|
|
|
|
|
|
|
FsRtlNotifyVolumeEvent(IrpContext->Stack->FileObject, FSRTL_VOLUME_UNLOCK);
|
2014-10-30 20:56:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
NTSTATUS
|
|
|
|
VfatDismountVolume(
|
|
|
|
PVFAT_IRP_CONTEXT IrpContext)
|
|
|
|
{
|
|
|
|
PDEVICE_EXTENSION DeviceExt;
|
2014-11-01 20:19:52 +00:00
|
|
|
PLIST_ENTRY NextEntry;
|
|
|
|
PVFATFCB Fcb;
|
2014-11-10 10:18:29 +00:00
|
|
|
PFILE_OBJECT FileObject;
|
2014-10-30 20:56:40 +00:00
|
|
|
|
2014-11-02 11:30:14 +00:00
|
|
|
DPRINT("VfatDismountVolume(%p)\n", IrpContext);
|
2014-10-30 20:56:40 +00:00
|
|
|
|
|
|
|
DeviceExt = IrpContext->DeviceExt;
|
2014-11-10 10:18:29 +00:00
|
|
|
FileObject = IrpContext->FileObject;
|
2014-10-30 20:56:40 +00:00
|
|
|
|
2014-11-01 20:19:52 +00:00
|
|
|
/* We HAVE to be locked. Windows also allows dismount with no lock
|
|
|
|
* but we're here mainly for 1st stage, so KISS
|
|
|
|
*/
|
2017-02-17 17:58:18 +00:00
|
|
|
if (!BooleanFlagOn(DeviceExt->Flags, VCB_VOLUME_LOCKED))
|
2014-10-30 20:56:40 +00:00
|
|
|
{
|
|
|
|
return STATUS_ACCESS_DENIED;
|
|
|
|
}
|
|
|
|
|
2018-05-16 19:44:47 +00:00
|
|
|
/* Deny dismount of boot volume */
|
|
|
|
if (BooleanFlagOn(DeviceExt->Flags, VCB_IS_SYS_OR_HAS_PAGE))
|
|
|
|
{
|
|
|
|
return STATUS_ACCESS_DENIED;
|
|
|
|
}
|
|
|
|
|
2014-11-01 20:19:52 +00:00
|
|
|
/* Race condition? */
|
2017-02-17 17:58:18 +00:00
|
|
|
if (BooleanFlagOn(DeviceExt->Flags, VCB_DISMOUNT_PENDING))
|
2014-11-01 20:19:52 +00:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
|
[FASTFAT] Improvements for volume dismount + minor bugfixing.
- Cache the RootFcb so that its cleanup can be handled separately
during dismounting.
- Force volume dismount at cleanup if the VCB_DISMOUNT_PENDING flag
is set.
- Actually dismount a volume if its VCB has been flagged as not good,
or if we force dismounting.
NOTE: In their *CheckForDismount() function, our 3rd-party FS drivers
as well as MS' fastfat, perform a comparison check of the current VCB's
VPB ReferenceCount with some sort of "dangling"/"residual" open count.
It seems to be related to the fact that the volume root directory as
well as auxiliary data stream(s) are still opened, and only these are
allowed to be opened at that moment. After analysis it appears that for
the ReactOS' fastfat, this number is equal to "3".
- On dismounting, cleanup and destroy the RootFcb, VolumeFcb and the
FATFileObject. Then cleanup the SpareVPB or the IoVPB members, and
finish by removing the dismounted volume from the VolumeListEntry
and cleaning up the notify synchronization object and the resources.
- During dismounting, and on shutdown, flush the volume before
resetting its dirty bit.
- On shutdown, after volume flushing, try to unmount it without forcing.
- Release the VCB resources only when we actually dismount the volume
in VfatCheckForDismount().
- Initialize first the notify list and the synchronization object,
before sending the FSRTL_VOLUME_MOUNT notification.
- If we failed at mounting a volume but its VCB's FATFileObject was
already initialized, first call CcUninitializeCacheMap() on it
before dereferencing it.
- Send FSRTL_VOLUME_LOCK, FSRTL_VOLUME_LOCK_FAILED and
FSRTL_VOLUME_UNLOCK notifications during volume locking (and failure)
and volume unlocking.
- Flush the volume before locking it, and clean its dirty bit if needed.
NOTE: In addition to checking for VCB_CLEAR_DIRTY, we also check for the
presence of the VCB_IS_DIRTY flag before cleaning up the dirty bit: this
allows us to not re-clean the bit if it has been previously cleaned.
This is needed for instance in this scenario:
- The volume is locked (it gets flushed and the dirty bit is possibly cleared);
- The volume then gets formatted with a completely different FS, that
possibly clears up the first sector (e.g. BTRFS ignores 1st sector);
- The volume is then dismounted: if we didn't check whether VCB_IS_DIRTY
was set prior to resetting it, we could attempt clearing it again! But
now that the volume's filesystem has been completely changed, we would
then try to modify the dirty bit on an erroneous position on disk!
That's why it should not be touched in this case during dismounting.
- The volume is unlocked (same comment as above), and later can be
detected as being BTRFS.
2018-11-11 16:17:48 +00:00
|
|
|
/* 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))
|
2014-11-27 06:16:21 +00:00
|
|
|
{
|
[FASTFAT] Completely rewrite support for dirty volumes.
Until now, our support for dirty volumes was totally broken
to a point where, on FAT32 volume, the dirty couldn't even
be written nor read from the disk.
This commit totally rewrites its handling, for both FAT16 and FAT32
so that it's now fully functionnal. Furthermore, it also gets
totally compatible with our vfatlib, and thus, autochk.
Now, on mount, FastFAT will check if the volume is dirty or not, and
autochk will be able to ask for a repair if dirty. vfatlib will
repair the volume and remove the dirty bit. So that, on next
reboot, the volume will be mounted clean.
As a reminder, the dirty bit is set immediately after mounting
the volume, so that, if you crash or have a powercut, autochk
will always attempt to repair your volume (with more or less,
that's FAT!).
If you want to experience without breaking your FAT volume,
just boot, open a cmd prompt and type: fsutil dirty set c:
and reboot!
CORE-13758
CORE-13760
CORE-13759
2018-05-18 21:00:13 +00:00
|
|
|
/* Drop the dirty bit */
|
|
|
|
if (NT_SUCCESS(SetDirtyStatus(DeviceExt, FALSE)))
|
|
|
|
DeviceExt->VolumeFcb->Flags &= ~VCB_IS_DIRTY;
|
2014-11-27 06:16:21 +00:00
|
|
|
}
|
|
|
|
|
2014-11-01 20:19:52 +00:00
|
|
|
/* Rebrowse the FCB in order to free them now */
|
|
|
|
while (!IsListEmpty(&DeviceExt->FcbListHead))
|
|
|
|
{
|
2017-12-17 17:21:51 +00:00
|
|
|
NextEntry = RemoveTailList(&DeviceExt->FcbListHead);
|
2014-11-01 20:19:52 +00:00
|
|
|
Fcb = CONTAINING_RECORD(NextEntry, VFATFCB, FcbListEntry);
|
[FASTFAT] Improvements for volume dismount + minor bugfixing.
- Cache the RootFcb so that its cleanup can be handled separately
during dismounting.
- Force volume dismount at cleanup if the VCB_DISMOUNT_PENDING flag
is set.
- Actually dismount a volume if its VCB has been flagged as not good,
or if we force dismounting.
NOTE: In their *CheckForDismount() function, our 3rd-party FS drivers
as well as MS' fastfat, perform a comparison check of the current VCB's
VPB ReferenceCount with some sort of "dangling"/"residual" open count.
It seems to be related to the fact that the volume root directory as
well as auxiliary data stream(s) are still opened, and only these are
allowed to be opened at that moment. After analysis it appears that for
the ReactOS' fastfat, this number is equal to "3".
- On dismounting, cleanup and destroy the RootFcb, VolumeFcb and the
FATFileObject. Then cleanup the SpareVPB or the IoVPB members, and
finish by removing the dismounted volume from the VolumeListEntry
and cleaning up the notify synchronization object and the resources.
- During dismounting, and on shutdown, flush the volume before
resetting its dirty bit.
- On shutdown, after volume flushing, try to unmount it without forcing.
- Release the VCB resources only when we actually dismount the volume
in VfatCheckForDismount().
- Initialize first the notify list and the synchronization object,
before sending the FSRTL_VOLUME_MOUNT notification.
- If we failed at mounting a volume but its VCB's FATFileObject was
already initialized, first call CcUninitializeCacheMap() on it
before dereferencing it.
- Send FSRTL_VOLUME_LOCK, FSRTL_VOLUME_LOCK_FAILED and
FSRTL_VOLUME_UNLOCK notifications during volume locking (and failure)
and volume unlocking.
- Flush the volume before locking it, and clean its dirty bit if needed.
NOTE: In addition to checking for VCB_CLEAR_DIRTY, we also check for the
presence of the VCB_IS_DIRTY flag before cleaning up the dirty bit: this
allows us to not re-clean the bit if it has been previously cleaned.
This is needed for instance in this scenario:
- The volume is locked (it gets flushed and the dirty bit is possibly cleared);
- The volume then gets formatted with a completely different FS, that
possibly clears up the first sector (e.g. BTRFS ignores 1st sector);
- The volume is then dismounted: if we didn't check whether VCB_IS_DIRTY
was set prior to resetting it, we could attempt clearing it again! But
now that the volume's filesystem has been completely changed, we would
then try to modify the dirty bit on an erroneous position on disk!
That's why it should not be touched in this case during dismounting.
- The volume is unlocked (same comment as above), and later can be
detected as being BTRFS.
2018-11-11 16:17:48 +00:00
|
|
|
|
|
|
|
if (Fcb == DeviceExt->RootFcb)
|
|
|
|
DeviceExt->RootFcb = NULL;
|
|
|
|
else if (Fcb == DeviceExt->VolumeFcb)
|
|
|
|
DeviceExt->VolumeFcb = NULL;
|
|
|
|
|
2014-11-01 20:19:52 +00:00
|
|
|
vfatDestroyFCB(Fcb);
|
|
|
|
}
|
|
|
|
|
[FASTFAT] Improvements for volume dismount + minor bugfixing.
- Cache the RootFcb so that its cleanup can be handled separately
during dismounting.
- Force volume dismount at cleanup if the VCB_DISMOUNT_PENDING flag
is set.
- Actually dismount a volume if its VCB has been flagged as not good,
or if we force dismounting.
NOTE: In their *CheckForDismount() function, our 3rd-party FS drivers
as well as MS' fastfat, perform a comparison check of the current VCB's
VPB ReferenceCount with some sort of "dangling"/"residual" open count.
It seems to be related to the fact that the volume root directory as
well as auxiliary data stream(s) are still opened, and only these are
allowed to be opened at that moment. After analysis it appears that for
the ReactOS' fastfat, this number is equal to "3".
- On dismounting, cleanup and destroy the RootFcb, VolumeFcb and the
FATFileObject. Then cleanup the SpareVPB or the IoVPB members, and
finish by removing the dismounted volume from the VolumeListEntry
and cleaning up the notify synchronization object and the resources.
- During dismounting, and on shutdown, flush the volume before
resetting its dirty bit.
- On shutdown, after volume flushing, try to unmount it without forcing.
- Release the VCB resources only when we actually dismount the volume
in VfatCheckForDismount().
- Initialize first the notify list and the synchronization object,
before sending the FSRTL_VOLUME_MOUNT notification.
- If we failed at mounting a volume but its VCB's FATFileObject was
already initialized, first call CcUninitializeCacheMap() on it
before dereferencing it.
- Send FSRTL_VOLUME_LOCK, FSRTL_VOLUME_LOCK_FAILED and
FSRTL_VOLUME_UNLOCK notifications during volume locking (and failure)
and volume unlocking.
- Flush the volume before locking it, and clean its dirty bit if needed.
NOTE: In addition to checking for VCB_CLEAR_DIRTY, we also check for the
presence of the VCB_IS_DIRTY flag before cleaning up the dirty bit: this
allows us to not re-clean the bit if it has been previously cleaned.
This is needed for instance in this scenario:
- The volume is locked (it gets flushed and the dirty bit is possibly cleared);
- The volume then gets formatted with a completely different FS, that
possibly clears up the first sector (e.g. BTRFS ignores 1st sector);
- The volume is then dismounted: if we didn't check whether VCB_IS_DIRTY
was set prior to resetting it, we could attempt clearing it again! But
now that the volume's filesystem has been completely changed, we would
then try to modify the dirty bit on an erroneous position on disk!
That's why it should not be touched in this case during dismounting.
- The volume is unlocked (same comment as above), and later can be
detected as being BTRFS.
2018-11-11 16:17:48 +00:00
|
|
|
/* We are uninitializing, the VCB cannot be used anymore */
|
|
|
|
ClearFlag(DeviceExt->Flags, VCB_GOOD);
|
|
|
|
|
2014-11-01 20:19:52 +00:00
|
|
|
/* Mark we're being dismounted */
|
|
|
|
DeviceExt->Flags |= VCB_DISMOUNT_PENDING;
|
2014-11-29 20:26:07 +00:00
|
|
|
#ifndef ENABLE_SWAPOUT
|
|
|
|
IrpContext->DeviceObject->Vpb->Flags &= ~VPB_MOUNTED;
|
|
|
|
#endif
|
2014-11-01 20:19:52 +00:00
|
|
|
|
|
|
|
ExReleaseResourceLite(&DeviceExt->FatResource);
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
2014-10-30 20:56:40 +00:00
|
|
|
}
|
|
|
|
|
2017-09-24 08:56:06 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2002-03-18 22:37:13 +00:00
|
|
|
/*
|
|
|
|
* FUNCTION: File system control
|
|
|
|
*/
|
2013-12-12 13:51:50 +00:00
|
|
|
NTSTATUS
|
|
|
|
VfatFileSystemControl(
|
|
|
|
PVFAT_IRP_CONTEXT IrpContext)
|
2002-03-18 22:37:13 +00:00
|
|
|
{
|
2013-12-12 13:51:50 +00:00
|
|
|
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;
|
2002-03-18 22:37:13 +00:00
|
|
|
|
2013-12-12 13:51:50 +00:00
|
|
|
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;
|
|
|
|
|
2014-10-30 20:56:40 +00:00
|
|
|
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;
|
|
|
|
|
2017-09-24 08:56:06 +00:00
|
|
|
case FSCTL_FILESYSTEM_GET_STATISTICS:
|
|
|
|
Status = VfatGetStatistics(IrpContext);
|
|
|
|
break;
|
|
|
|
|
2013-12-12 13:51:50 +00:00
|
|
|
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;
|
2002-03-18 22:37:13 +00:00
|
|
|
}
|