mirror of
https://github.com/reactos/reactos.git
synced 2024-08-26 05:08:39 +00:00
![Stanislav Motylkov](/assets/img/avatar_default.png)
FAT16 filesystem was not recognized on some 4GB USB flash drives. This change makes them just work. References: - https://chat.reactos.org/reactos/pl/h83pm5ngkffk7qiyrmqnaaopxh - https://support.microsoft.com/en-us/help/140365/default-cluster-size-for-ntfs-fat-and-exfat CORE-16511
1467 lines
47 KiB
C
1467 lines
47 KiB
C
/*
|
|
* ReactOS kernel
|
|
* Copyright (C) 2002 ReactOS Team
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS kernel
|
|
* FILE: drivers/fs/vfat/fsctl.c
|
|
* PURPOSE: VFAT Filesystem
|
|
*/
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
#include "vfat.h"
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
extern VFAT_DISPATCH FatXDispatch;
|
|
extern VFAT_DISPATCH FatDispatch;
|
|
|
|
/* FUNCTIONS ****************************************************************/
|
|
|
|
#define CACHEPAGESIZE(pDeviceExt) ((pDeviceExt)->FatInfo.BytesPerCluster > PAGE_SIZE ? \
|
|
(pDeviceExt)->FatInfo.BytesPerCluster : PAGE_SIZE)
|
|
|
|
static
|
|
NTSTATUS
|
|
VfatHasFileSystem(
|
|
PDEVICE_OBJECT DeviceToMount,
|
|
PBOOLEAN RecognizedFS,
|
|
PFATINFO pFatInfo,
|
|
BOOLEAN Override)
|
|
{
|
|
NTSTATUS Status;
|
|
PARTITION_INFORMATION PartitionInfo;
|
|
DISK_GEOMETRY DiskGeometry;
|
|
FATINFO FatInfo;
|
|
ULONG Size;
|
|
ULONG Sectors;
|
|
LARGE_INTEGER Offset;
|
|
struct _BootSector* Boot;
|
|
struct _BootSectorFatX* BootFatX;
|
|
BOOLEAN PartitionInfoIsValid = FALSE;
|
|
|
|
DPRINT("VfatHasFileSystem\n");
|
|
|
|
*RecognizedFS = FALSE;
|
|
|
|
Size = sizeof(DISK_GEOMETRY);
|
|
Status = VfatBlockDeviceIoControl(DeviceToMount,
|
|
IOCTL_DISK_GET_DRIVE_GEOMETRY,
|
|
NULL,
|
|
0,
|
|
&DiskGeometry,
|
|
&Size,
|
|
Override);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("VfatBlockDeviceIoControl failed (%x)\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
FatInfo.FixedMedia = DiskGeometry.MediaType == FixedMedia ? TRUE : FALSE;
|
|
if (DiskGeometry.MediaType == FixedMedia || DiskGeometry.MediaType == RemovableMedia)
|
|
{
|
|
// We have found a hard disk
|
|
Size = sizeof(PARTITION_INFORMATION);
|
|
Status = VfatBlockDeviceIoControl(DeviceToMount,
|
|
IOCTL_DISK_GET_PARTITION_INFO,
|
|
NULL,
|
|
0,
|
|
&PartitionInfo,
|
|
&Size,
|
|
Override);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("VfatBlockDeviceIoControl failed (%x)\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
DPRINT("Partition Information:\n");
|
|
DPRINT("StartingOffset %I64x\n", PartitionInfo.StartingOffset.QuadPart / 512);
|
|
DPRINT("PartitionLength %I64x\n", PartitionInfo.PartitionLength.QuadPart / 512);
|
|
DPRINT("HiddenSectors %u\n", PartitionInfo.HiddenSectors);
|
|
DPRINT("PartitionNumber %u\n", PartitionInfo.PartitionNumber);
|
|
DPRINT("PartitionType %u\n", PartitionInfo.PartitionType);
|
|
DPRINT("BootIndicator %u\n", PartitionInfo.BootIndicator);
|
|
DPRINT("RecognizedPartition %u\n", PartitionInfo.RecognizedPartition);
|
|
DPRINT("RewritePartition %u\n", PartitionInfo.RewritePartition);
|
|
if (PartitionInfo.PartitionType)
|
|
{
|
|
if (PartitionInfo.PartitionType == PARTITION_FAT_12 ||
|
|
PartitionInfo.PartitionType == PARTITION_FAT_16 ||
|
|
PartitionInfo.PartitionType == PARTITION_HUGE ||
|
|
PartitionInfo.PartitionType == PARTITION_FAT32 ||
|
|
PartitionInfo.PartitionType == PARTITION_FAT32_XINT13 ||
|
|
PartitionInfo.PartitionType == PARTITION_XINT13)
|
|
{
|
|
PartitionInfoIsValid = TRUE;
|
|
*RecognizedFS = TRUE;
|
|
}
|
|
}
|
|
else if (DiskGeometry.MediaType == RemovableMedia &&
|
|
PartitionInfo.PartitionNumber > 0 &&
|
|
PartitionInfo.StartingOffset.QuadPart == 0 &&
|
|
PartitionInfo.PartitionLength.QuadPart > 0)
|
|
{
|
|
/* This is possible a removable media formated as super floppy */
|
|
PartitionInfoIsValid = TRUE;
|
|
*RecognizedFS = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*RecognizedFS = TRUE;
|
|
}
|
|
|
|
if (*RecognizedFS)
|
|
{
|
|
Boot = ExAllocatePoolWithTag(NonPagedPool, DiskGeometry.BytesPerSector, TAG_BUFFER);
|
|
if (Boot == NULL)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
Offset.QuadPart = 0;
|
|
|
|
/* Try to recognize FAT12/FAT16/FAT32 partitions */
|
|
Status = VfatReadDisk(DeviceToMount, &Offset, DiskGeometry.BytesPerSector, (PUCHAR) Boot, Override);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
if (Boot->Signatur1 != 0xaa55)
|
|
{
|
|
*RecognizedFS = FALSE;
|
|
}
|
|
|
|
if (*RecognizedFS &&
|
|
Boot->BytesPerSector != 512 &&
|
|
Boot->BytesPerSector != 1024 &&
|
|
Boot->BytesPerSector != 2048 &&
|
|
Boot->BytesPerSector != 4096)
|
|
{
|
|
DPRINT1("BytesPerSector %u\n", Boot->BytesPerSector);
|
|
*RecognizedFS = FALSE;
|
|
}
|
|
|
|
if (*RecognizedFS &&
|
|
Boot->FATCount != 1 &&
|
|
Boot->FATCount != 2)
|
|
{
|
|
DPRINT1("FATCount %u\n", Boot->FATCount);
|
|
*RecognizedFS = FALSE;
|
|
}
|
|
|
|
if (*RecognizedFS &&
|
|
Boot->Media != 0xf0 &&
|
|
Boot->Media != 0xf8 &&
|
|
Boot->Media != 0xf9 &&
|
|
Boot->Media != 0xfa &&
|
|
Boot->Media != 0xfb &&
|
|
Boot->Media != 0xfc &&
|
|
Boot->Media != 0xfd &&
|
|
Boot->Media != 0xfe &&
|
|
Boot->Media != 0xff)
|
|
{
|
|
DPRINT1("Media %02x\n", Boot->Media);
|
|
*RecognizedFS = FALSE;
|
|
}
|
|
|
|
if (*RecognizedFS &&
|
|
Boot->SectorsPerCluster != 1 &&
|
|
Boot->SectorsPerCluster != 2 &&
|
|
Boot->SectorsPerCluster != 4 &&
|
|
Boot->SectorsPerCluster != 8 &&
|
|
Boot->SectorsPerCluster != 16 &&
|
|
Boot->SectorsPerCluster != 32 &&
|
|
Boot->SectorsPerCluster != 64 &&
|
|
Boot->SectorsPerCluster != 128)
|
|
{
|
|
DPRINT1("SectorsPerCluster %02x\n", Boot->SectorsPerCluster);
|
|
*RecognizedFS = FALSE;
|
|
}
|
|
|
|
if (*RecognizedFS &&
|
|
Boot->BytesPerSector * Boot->SectorsPerCluster > 64 * 1024)
|
|
{
|
|
DPRINT1("ClusterSize %d\n", Boot->BytesPerSector * Boot->SectorsPerCluster);
|
|
*RecognizedFS = FALSE;
|
|
}
|
|
|
|
if (*RecognizedFS)
|
|
{
|
|
FatInfo.VolumeID = Boot->VolumeID;
|
|
FatInfo.FATStart = Boot->ReservedSectors;
|
|
FatInfo.FATCount = Boot->FATCount;
|
|
FatInfo.FATSectors = Boot->FATSectors ? Boot->FATSectors : ((struct _BootSector32*) Boot)->FATSectors32;
|
|
FatInfo.BytesPerSector = Boot->BytesPerSector;
|
|
FatInfo.SectorsPerCluster = Boot->SectorsPerCluster;
|
|
FatInfo.BytesPerCluster = FatInfo.BytesPerSector * FatInfo.SectorsPerCluster;
|
|
FatInfo.rootDirectorySectors = ((Boot->RootEntries * 32) + Boot->BytesPerSector - 1) / Boot->BytesPerSector;
|
|
FatInfo.rootStart = FatInfo.FATStart + FatInfo.FATCount * FatInfo.FATSectors;
|
|
FatInfo.dataStart = FatInfo.rootStart + FatInfo.rootDirectorySectors;
|
|
FatInfo.Sectors = Sectors = Boot->Sectors ? Boot->Sectors : Boot->SectorsHuge;
|
|
Sectors -= Boot->ReservedSectors + FatInfo.FATCount * FatInfo.FATSectors + FatInfo.rootDirectorySectors;
|
|
FatInfo.NumberOfClusters = Sectors / Boot->SectorsPerCluster;
|
|
if (FatInfo.NumberOfClusters < 4085)
|
|
{
|
|
DPRINT("FAT12\n");
|
|
FatInfo.FatType = FAT12;
|
|
FatInfo.RootCluster = (FatInfo.rootStart - 1) / FatInfo.SectorsPerCluster;
|
|
RtlCopyMemory(&FatInfo.VolumeLabel, &Boot->VolumeLabel, sizeof(FatInfo.VolumeLabel));
|
|
}
|
|
else if (FatInfo.NumberOfClusters >= 65525)
|
|
{
|
|
DPRINT("FAT32\n");
|
|
FatInfo.FatType = FAT32;
|
|
FatInfo.RootCluster = ((struct _BootSector32*) Boot)->RootCluster;
|
|
FatInfo.rootStart = FatInfo.dataStart + ((FatInfo.RootCluster - 2) * FatInfo.SectorsPerCluster);
|
|
FatInfo.VolumeID = ((struct _BootSector32*) Boot)->VolumeID;
|
|
FatInfo.FSInfoSector = ((struct _BootSector32*) Boot)->FSInfoSector;
|
|
RtlCopyMemory(&FatInfo.VolumeLabel, &((struct _BootSector32*)Boot)->VolumeLabel, sizeof(FatInfo.VolumeLabel));
|
|
}
|
|
else
|
|
{
|
|
DPRINT("FAT16\n");
|
|
FatInfo.FatType = FAT16;
|
|
FatInfo.RootCluster = FatInfo.rootStart / FatInfo.SectorsPerCluster;
|
|
RtlCopyMemory(&FatInfo.VolumeLabel, &Boot->VolumeLabel, sizeof(FatInfo.VolumeLabel));
|
|
}
|
|
|
|
if (PartitionInfoIsValid &&
|
|
FatInfo.Sectors > PartitionInfo.PartitionLength.QuadPart / FatInfo.BytesPerSector)
|
|
{
|
|
*RecognizedFS = FALSE;
|
|
}
|
|
|
|
if (pFatInfo && *RecognizedFS)
|
|
{
|
|
*pFatInfo = FatInfo;
|
|
}
|
|
}
|
|
}
|
|
|
|
ExFreePoolWithTag(Boot, TAG_BUFFER);
|
|
}
|
|
|
|
if (!*RecognizedFS && PartitionInfoIsValid)
|
|
{
|
|
BootFatX = ExAllocatePoolWithTag(NonPagedPool, sizeof(struct _BootSectorFatX), TAG_BUFFER);
|
|
if (BootFatX == NULL)
|
|
{
|
|
*RecognizedFS=FALSE;
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
Offset.QuadPart = 0;
|
|
|
|
/* Try to recognize FATX16/FATX32 partitions (Xbox) */
|
|
Status = VfatReadDisk(DeviceToMount, &Offset, sizeof(struct _BootSectorFatX), (PUCHAR) BootFatX, Override);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
*RecognizedFS = TRUE;
|
|
if (BootFatX->SysType[0] != 'F' ||
|
|
BootFatX->SysType[1] != 'A' ||
|
|
BootFatX->SysType[2] != 'T' ||
|
|
BootFatX->SysType[3] != 'X')
|
|
{
|
|
DPRINT1("SysType %02X%02X%02X%02X (%c%c%c%c)\n",
|
|
BootFatX->SysType[0], BootFatX->SysType[1], BootFatX->SysType[2], BootFatX->SysType[3],
|
|
isprint(BootFatX->SysType[0]) ? BootFatX->SysType[0] : '.',
|
|
isprint(BootFatX->SysType[1]) ? BootFatX->SysType[1] : '.',
|
|
isprint(BootFatX->SysType[2]) ? BootFatX->SysType[2] : '.',
|
|
isprint(BootFatX->SysType[3]) ? BootFatX->SysType[3] : '.');
|
|
|
|
*RecognizedFS = FALSE;
|
|
}
|
|
|
|
if (*RecognizedFS &&
|
|
BootFatX->SectorsPerCluster != 1 &&
|
|
BootFatX->SectorsPerCluster != 2 &&
|
|
BootFatX->SectorsPerCluster != 4 &&
|
|
BootFatX->SectorsPerCluster != 8 &&
|
|
BootFatX->SectorsPerCluster != 16 &&
|
|
BootFatX->SectorsPerCluster != 32 &&
|
|
BootFatX->SectorsPerCluster != 64 &&
|
|
BootFatX->SectorsPerCluster != 128)
|
|
{
|
|
DPRINT1("SectorsPerCluster %lu\n", BootFatX->SectorsPerCluster);
|
|
*RecognizedFS=FALSE;
|
|
}
|
|
|
|
if (*RecognizedFS)
|
|
{
|
|
FatInfo.BytesPerSector = DiskGeometry.BytesPerSector;
|
|
FatInfo.SectorsPerCluster = BootFatX->SectorsPerCluster;
|
|
FatInfo.rootDirectorySectors = BootFatX->SectorsPerCluster;
|
|
FatInfo.BytesPerCluster = BootFatX->SectorsPerCluster * DiskGeometry.BytesPerSector;
|
|
FatInfo.Sectors = (ULONG)(PartitionInfo.PartitionLength.QuadPart / DiskGeometry.BytesPerSector);
|
|
if (FatInfo.Sectors / FatInfo.SectorsPerCluster < 65525)
|
|
{
|
|
DPRINT("FATX16\n");
|
|
FatInfo.FatType = FATX16;
|
|
}
|
|
else
|
|
{
|
|
DPRINT("FATX32\n");
|
|
FatInfo.FatType = FATX32;
|
|
}
|
|
FatInfo.VolumeID = BootFatX->VolumeID;
|
|
FatInfo.FATStart = sizeof(struct _BootSectorFatX) / DiskGeometry.BytesPerSector;
|
|
FatInfo.FATCount = BootFatX->FATCount;
|
|
FatInfo.FATSectors =
|
|
ROUND_UP(FatInfo.Sectors / FatInfo.SectorsPerCluster * (FatInfo.FatType == FATX16 ? 2 : 4), 4096) /
|
|
FatInfo.BytesPerSector;
|
|
FatInfo.rootStart = FatInfo.FATStart + FatInfo.FATCount * FatInfo.FATSectors;
|
|
FatInfo.RootCluster = (FatInfo.rootStart - 1) / FatInfo.SectorsPerCluster;
|
|
FatInfo.dataStart = FatInfo.rootStart + FatInfo.rootDirectorySectors;
|
|
FatInfo.NumberOfClusters = (FatInfo.Sectors - FatInfo.dataStart) / FatInfo.SectorsPerCluster;
|
|
|
|
if (pFatInfo && *RecognizedFS)
|
|
{
|
|
*pFatInfo = FatInfo;
|
|
}
|
|
}
|
|
}
|
|
ExFreePoolWithTag(BootFatX, TAG_BUFFER);
|
|
}
|
|
|
|
DPRINT("VfatHasFileSystem done\n");
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* FUNCTION: Read the volume label
|
|
* WARNING: Read this comment carefully before using it (and using it wrong)
|
|
* Device parameter is expected to be the lower DO is start isn't 0
|
|
* otherwise, it is expected to be the VCB is start is 0
|
|
* Start parameter is expected to be, in bytes, the beginning of the root start.
|
|
* Set it to 0 if you wish to use the associated FCB with caching.
|
|
* In that specific case, Device parameter is expected to be the VCB!
|
|
* VolumeLabel parameter is expected to be a preallocated UNICODE_STRING (ie, with buffer)
|
|
* Its buffer has to be able to contain MAXIMUM_VOLUME_LABEL_LENGTH bytes
|
|
*/
|
|
static
|
|
NTSTATUS
|
|
ReadVolumeLabel(
|
|
PVOID Device,
|
|
ULONG Start,
|
|
BOOLEAN IsFatX,
|
|
PUNICODE_STRING VolumeLabel)
|
|
{
|
|
PDEVICE_EXTENSION DeviceExt;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PVOID Context = NULL;
|
|
ULONG DirIndex = 0;
|
|
PDIR_ENTRY Entry;
|
|
PVFATFCB pFcb;
|
|
LARGE_INTEGER FileOffset;
|
|
ULONG SizeDirEntry;
|
|
ULONG EntriesPerPage;
|
|
OEM_STRING StringO;
|
|
BOOLEAN NoCache = (Start != 0);
|
|
PVOID Buffer;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
if (IsFatX)
|
|
{
|
|
SizeDirEntry = sizeof(FATX_DIR_ENTRY);
|
|
EntriesPerPage = FATX_ENTRIES_PER_PAGE;
|
|
}
|
|
else
|
|
{
|
|
SizeDirEntry = sizeof(FAT_DIR_ENTRY);
|
|
EntriesPerPage = FAT_ENTRIES_PER_PAGE;
|
|
}
|
|
|
|
FileOffset.QuadPart = Start;
|
|
if (!NoCache)
|
|
{
|
|
DeviceExt = Device;
|
|
|
|
/* FIXME: Check we really have a VCB
|
|
ASSERT();
|
|
*/
|
|
|
|
ExAcquireResourceExclusiveLite(&DeviceExt->DirResource, TRUE);
|
|
pFcb = vfatOpenRootFCB(DeviceExt);
|
|
ExReleaseResourceLite(&DeviceExt->DirResource);
|
|
|
|
_SEH2_TRY
|
|
{
|
|
CcMapData(pFcb->FileObject, &FileOffset, SizeDirEntry, MAP_WAIT, &Context, (PVOID*)&Entry);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
else
|
|
{
|
|
DeviceObject = Device;
|
|
|
|
ASSERT(DeviceObject->Type == 3);
|
|
|
|
Buffer = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, TAG_DIRENT);
|
|
if (Buffer != NULL)
|
|
{
|
|
Status = VfatReadDisk(DeviceObject, &FileOffset, PAGE_SIZE, (PUCHAR)Buffer, TRUE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ExFreePoolWithTag(Buffer, TAG_DIRENT);
|
|
}
|
|
else
|
|
{
|
|
Entry = Buffer;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
while (TRUE)
|
|
{
|
|
if (ENTRY_VOLUME(IsFatX, Entry))
|
|
{
|
|
/* copy volume label */
|
|
if (IsFatX)
|
|
{
|
|
StringO.Buffer = (PCHAR)Entry->FatX.Filename;
|
|
StringO.MaximumLength = StringO.Length = Entry->FatX.FilenameLength;
|
|
RtlOemStringToUnicodeString(VolumeLabel, &StringO, FALSE);
|
|
}
|
|
else
|
|
{
|
|
vfat8Dot3ToString(&Entry->Fat, VolumeLabel);
|
|
}
|
|
break;
|
|
}
|
|
if (ENTRY_END(IsFatX, Entry))
|
|
{
|
|
break;
|
|
}
|
|
DirIndex++;
|
|
Entry = (PDIR_ENTRY)((ULONG_PTR)Entry + SizeDirEntry);
|
|
if ((DirIndex % EntriesPerPage) == 0)
|
|
{
|
|
FileOffset.u.LowPart += PAGE_SIZE;
|
|
|
|
if (!NoCache)
|
|
{
|
|
CcUnpinData(Context);
|
|
|
|
_SEH2_TRY
|
|
{
|
|
CcMapData(pFcb->FileObject, &FileOffset, SizeDirEntry, MAP_WAIT, &Context, (PVOID*)&Entry);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
Context = NULL;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Status = VfatReadDisk(DeviceObject, &FileOffset, PAGE_SIZE, (PUCHAR)Buffer, TRUE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
break;
|
|
}
|
|
Entry = Buffer;
|
|
}
|
|
}
|
|
}
|
|
if (Context)
|
|
{
|
|
CcUnpinData(Context);
|
|
}
|
|
else if (NoCache)
|
|
{
|
|
ExFreePoolWithTag(Buffer, TAG_DIRENT);
|
|
}
|
|
}
|
|
|
|
if (!NoCache)
|
|
{
|
|
ExAcquireResourceExclusiveLite(&DeviceExt->DirResource, TRUE);
|
|
vfatReleaseFCB(DeviceExt, pFcb);
|
|
ExReleaseResourceLite(&DeviceExt->DirResource);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
* FUNCTION: Mount the filesystem
|
|
*/
|
|
static
|
|
NTSTATUS
|
|
VfatMount(
|
|
PVFAT_IRP_CONTEXT IrpContext)
|
|
{
|
|
PDEVICE_OBJECT DeviceObject = NULL;
|
|
PDEVICE_EXTENSION DeviceExt = NULL;
|
|
BOOLEAN RecognizedFS;
|
|
NTSTATUS Status;
|
|
PVFATFCB Fcb = NULL;
|
|
PVFATFCB VolumeFcb = NULL;
|
|
PDEVICE_OBJECT DeviceToMount;
|
|
PVPB Vpb;
|
|
UNICODE_STRING NameU = RTL_CONSTANT_STRING(L"\\$$Fat$$");
|
|
UNICODE_STRING VolumeNameU = RTL_CONSTANT_STRING(L"\\$$Volume$$");
|
|
UNICODE_STRING VolumeLabelU;
|
|
ULONG HashTableSize;
|
|
ULONG i;
|
|
FATINFO FatInfo;
|
|
BOOLEAN Dirty;
|
|
|
|
DPRINT("VfatMount(IrpContext %p)\n", IrpContext);
|
|
|
|
ASSERT(IrpContext);
|
|
|
|
if (IrpContext->DeviceObject != VfatGlobalData->DeviceObject)
|
|
{
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
goto ByeBye;
|
|
}
|
|
|
|
DeviceToMount = IrpContext->Stack->Parameters.MountVolume.DeviceObject;
|
|
Vpb = IrpContext->Stack->Parameters.MountVolume.Vpb;
|
|
|
|
Status = VfatHasFileSystem(DeviceToMount, &RecognizedFS, &FatInfo, FALSE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto ByeBye;
|
|
}
|
|
|
|
if (RecognizedFS == FALSE)
|
|
{
|
|
DPRINT("VFAT: Unrecognized Volume\n");
|
|
Status = STATUS_UNRECOGNIZED_VOLUME;
|
|
goto ByeBye;
|
|
}
|
|
|
|
/* Use prime numbers for the table size */
|
|
if (FatInfo.FatType == FAT12)
|
|
{
|
|
HashTableSize = 4099; // 4096 = 4 * 1024
|
|
}
|
|
else if (FatInfo.FatType == FAT16 ||
|
|
FatInfo.FatType == FATX16)
|
|
{
|
|
HashTableSize = 16411; // 16384 = 16 * 1024
|
|
}
|
|
else
|
|
{
|
|
HashTableSize = 65537; // 65536 = 64 * 1024;
|
|
}
|
|
DPRINT("VFAT: Recognized volume\n");
|
|
Status = IoCreateDevice(VfatGlobalData->DriverObject,
|
|
ROUND_UP(sizeof (DEVICE_EXTENSION), sizeof(ULONG)) + sizeof(HASHENTRY*) * HashTableSize,
|
|
NULL,
|
|
FILE_DEVICE_DISK_FILE_SYSTEM,
|
|
DeviceToMount->Characteristics,
|
|
FALSE,
|
|
&DeviceObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto ByeBye;
|
|
}
|
|
|
|
DeviceExt = DeviceObject->DeviceExtension;
|
|
RtlZeroMemory(DeviceExt, ROUND_UP(sizeof(DEVICE_EXTENSION), sizeof(ULONG)) + sizeof(HASHENTRY*) * HashTableSize);
|
|
DeviceExt->FcbHashTable = (HASHENTRY**)((ULONG_PTR)DeviceExt + ROUND_UP(sizeof(DEVICE_EXTENSION), sizeof(ULONG)));
|
|
DeviceExt->HashTableSize = HashTableSize;
|
|
DeviceExt->VolumeDevice = DeviceObject;
|
|
|
|
/* use same vpb as device disk */
|
|
DeviceObject->Vpb = Vpb;
|
|
DeviceToMount->Vpb = Vpb;
|
|
|
|
RtlCopyMemory(&DeviceExt->FatInfo, &FatInfo, sizeof(FATINFO));
|
|
|
|
DPRINT("BytesPerSector: %u\n", DeviceExt->FatInfo.BytesPerSector);
|
|
DPRINT("SectorsPerCluster: %u\n", DeviceExt->FatInfo.SectorsPerCluster);
|
|
DPRINT("FATCount: %u\n", DeviceExt->FatInfo.FATCount);
|
|
DPRINT("FATSectors: %u\n", DeviceExt->FatInfo.FATSectors);
|
|
DPRINT("RootStart: %u\n", DeviceExt->FatInfo.rootStart);
|
|
DPRINT("DataStart: %u\n", DeviceExt->FatInfo.dataStart);
|
|
if (DeviceExt->FatInfo.FatType == FAT32)
|
|
{
|
|
DPRINT("RootCluster: %u\n", DeviceExt->FatInfo.RootCluster);
|
|
}
|
|
|
|
switch (DeviceExt->FatInfo.FatType)
|
|
{
|
|
case FAT12:
|
|
DeviceExt->GetNextCluster = FAT12GetNextCluster;
|
|
DeviceExt->FindAndMarkAvailableCluster = FAT12FindAndMarkAvailableCluster;
|
|
DeviceExt->WriteCluster = FAT12WriteCluster;
|
|
/* We don't define dirty bit functions here
|
|
* FAT12 doesn't have such bit and they won't get called
|
|
*/
|
|
break;
|
|
|
|
case FAT16:
|
|
case FATX16:
|
|
DeviceExt->GetNextCluster = FAT16GetNextCluster;
|
|
DeviceExt->FindAndMarkAvailableCluster = FAT16FindAndMarkAvailableCluster;
|
|
DeviceExt->WriteCluster = FAT16WriteCluster;
|
|
DeviceExt->GetDirtyStatus = FAT16GetDirtyStatus;
|
|
DeviceExt->SetDirtyStatus = FAT16SetDirtyStatus;
|
|
break;
|
|
|
|
case FAT32:
|
|
case FATX32:
|
|
DeviceExt->GetNextCluster = FAT32GetNextCluster;
|
|
DeviceExt->FindAndMarkAvailableCluster = FAT32FindAndMarkAvailableCluster;
|
|
DeviceExt->WriteCluster = FAT32WriteCluster;
|
|
DeviceExt->GetDirtyStatus = FAT32GetDirtyStatus;
|
|
DeviceExt->SetDirtyStatus = FAT32SetDirtyStatus;
|
|
break;
|
|
}
|
|
|
|
if (DeviceExt->FatInfo.FatType == FATX16 ||
|
|
DeviceExt->FatInfo.FatType == FATX32)
|
|
{
|
|
DeviceExt->Flags |= VCB_IS_FATX;
|
|
DeviceExt->BaseDateYear = 2000;
|
|
RtlCopyMemory(&DeviceExt->Dispatch, &FatXDispatch, sizeof(VFAT_DISPATCH));
|
|
}
|
|
else
|
|
{
|
|
DeviceExt->BaseDateYear = 1980;
|
|
RtlCopyMemory(&DeviceExt->Dispatch, &FatDispatch, sizeof(VFAT_DISPATCH));
|
|
}
|
|
|
|
DeviceExt->StorageDevice = DeviceToMount;
|
|
DeviceExt->StorageDevice->Vpb->DeviceObject = DeviceObject;
|
|
DeviceExt->StorageDevice->Vpb->RealDevice = DeviceExt->StorageDevice;
|
|
DeviceExt->StorageDevice->Vpb->Flags |= VPB_MOUNTED;
|
|
DeviceObject->StackSize = DeviceExt->StorageDevice->StackSize + 1;
|
|
DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
|
|
DPRINT("FsDeviceObject %p\n", DeviceObject);
|
|
|
|
/* Initialize this resource early ... it's used in VfatCleanup */
|
|
ExInitializeResourceLite(&DeviceExt->DirResource);
|
|
|
|
DeviceExt->IoVPB = DeviceObject->Vpb;
|
|
DeviceExt->SpareVPB = ExAllocatePoolWithTag(NonPagedPool, sizeof(VPB), TAG_VPB);
|
|
if (DeviceExt->SpareVPB == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ByeBye;
|
|
}
|
|
|
|
DeviceExt->Statistics = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(STATISTICS) * VfatGlobalData->NumberProcessors,
|
|
TAG_STATS);
|
|
if (DeviceExt->Statistics == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ByeBye;
|
|
}
|
|
|
|
RtlZeroMemory(DeviceExt->Statistics, sizeof(STATISTICS) * VfatGlobalData->NumberProcessors);
|
|
for (i = 0; i < VfatGlobalData->NumberProcessors; ++i)
|
|
{
|
|
DeviceExt->Statistics[i].Base.FileSystemType = FILESYSTEM_STATISTICS_TYPE_FAT;
|
|
DeviceExt->Statistics[i].Base.Version = 1;
|
|
DeviceExt->Statistics[i].Base.SizeOfCompleteStructure = sizeof(STATISTICS);
|
|
}
|
|
|
|
DeviceExt->FATFileObject = IoCreateStreamFileObject(NULL, DeviceExt->StorageDevice);
|
|
Fcb = vfatNewFCB(DeviceExt, &NameU);
|
|
if (Fcb == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ByeBye;
|
|
}
|
|
|
|
Status = vfatAttachFCBToFileObject(DeviceExt, Fcb, DeviceExt->FATFileObject);
|
|
if (!NT_SUCCESS(Status))
|
|
goto ByeBye;
|
|
|
|
DeviceExt->FATFileObject->PrivateCacheMap = NULL;
|
|
Fcb->FileObject = DeviceExt->FATFileObject;
|
|
|
|
Fcb->Flags = FCB_IS_FAT;
|
|
Fcb->RFCB.FileSize.QuadPart = DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector;
|
|
Fcb->RFCB.ValidDataLength = Fcb->RFCB.FileSize;
|
|
Fcb->RFCB.AllocationSize = Fcb->RFCB.FileSize;
|
|
|
|
_SEH2_TRY
|
|
{
|
|
CcInitializeCacheMap(DeviceExt->FATFileObject,
|
|
(PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
|
|
TRUE,
|
|
&VfatGlobalData->CacheMgrCallbacks,
|
|
Fcb);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
goto ByeBye;
|
|
}
|
|
_SEH2_END;
|
|
|
|
DeviceExt->LastAvailableCluster = 2;
|
|
CountAvailableClusters(DeviceExt, NULL);
|
|
ExInitializeResourceLite(&DeviceExt->FatResource);
|
|
|
|
InitializeListHead(&DeviceExt->FcbListHead);
|
|
|
|
VolumeFcb = vfatNewFCB(DeviceExt, &VolumeNameU);
|
|
if (VolumeFcb == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ByeBye;
|
|
}
|
|
|
|
VolumeFcb->Flags = FCB_IS_VOLUME;
|
|
VolumeFcb->RFCB.FileSize.QuadPart = (LONGLONG) DeviceExt->FatInfo.Sectors * DeviceExt->FatInfo.BytesPerSector;
|
|
VolumeFcb->RFCB.ValidDataLength = VolumeFcb->RFCB.FileSize;
|
|
VolumeFcb->RFCB.AllocationSize = VolumeFcb->RFCB.FileSize;
|
|
DeviceExt->VolumeFcb = VolumeFcb;
|
|
|
|
ExAcquireResourceExclusiveLite(&VfatGlobalData->VolumeListLock, TRUE);
|
|
InsertHeadList(&VfatGlobalData->VolumeListHead, &DeviceExt->VolumeListEntry);
|
|
ExReleaseResourceLite(&VfatGlobalData->VolumeListLock);
|
|
|
|
/* read serial number */
|
|
DeviceObject->Vpb->SerialNumber = DeviceExt->FatInfo.VolumeID;
|
|
|
|
/* read volume label */
|
|
VolumeLabelU.Buffer = DeviceObject->Vpb->VolumeLabel;
|
|
VolumeLabelU.Length = 0;
|
|
VolumeLabelU.MaximumLength = sizeof(DeviceObject->Vpb->VolumeLabel);
|
|
ReadVolumeLabel(DeviceExt, 0, vfatVolumeIsFatX(DeviceExt), &VolumeLabelU);
|
|
Vpb->VolumeLabelLength = VolumeLabelU.Length;
|
|
|
|
/* read dirty bit status */
|
|
Status = GetDirtyStatus(DeviceExt, &Dirty);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* The volume wasn't dirty, it was properly dismounted */
|
|
if (!Dirty)
|
|
{
|
|
/* Mark it dirty now! */
|
|
SetDirtyStatus(DeviceExt, TRUE);
|
|
VolumeFcb->Flags |= VCB_CLEAR_DIRTY;
|
|
}
|
|
else
|
|
{
|
|
DPRINT1("Mounting a dirty volume\n");
|
|
}
|
|
}
|
|
|
|
VolumeFcb->Flags |= VCB_IS_DIRTY;
|
|
if (BooleanFlagOn(Vpb->RealDevice->Flags, DO_SYSTEM_BOOT_PARTITION))
|
|
{
|
|
SetFlag(DeviceExt->Flags, VCB_IS_SYS_OR_HAS_PAGE);
|
|
}
|
|
|
|
/* Initialize the notify list and synchronization object */
|
|
InitializeListHead(&DeviceExt->NotifyList);
|
|
FsRtlNotifyInitializeSync(&DeviceExt->NotifySync);
|
|
|
|
/* The VCB is OK for usage */
|
|
SetFlag(DeviceExt->Flags, VCB_GOOD);
|
|
|
|
/* Send the mount notification */
|
|
FsRtlNotifyVolumeEvent(DeviceExt->FATFileObject, FSRTL_VOLUME_MOUNT);
|
|
|
|
DPRINT("Mount success\n");
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
ByeBye:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Cleanup */
|
|
if (DeviceExt && DeviceExt->FATFileObject)
|
|
{
|
|
LARGE_INTEGER Zero = {{0,0}};
|
|
PVFATCCB Ccb = (PVFATCCB)DeviceExt->FATFileObject->FsContext2;
|
|
|
|
CcUninitializeCacheMap(DeviceExt->FATFileObject,
|
|
&Zero,
|
|
NULL);
|
|
ObDereferenceObject(DeviceExt->FATFileObject);
|
|
if (Ccb)
|
|
vfatDestroyCCB(Ccb);
|
|
DeviceExt->FATFileObject = NULL;
|
|
}
|
|
if (Fcb)
|
|
vfatDestroyFCB(Fcb);
|
|
if (DeviceExt && DeviceExt->SpareVPB)
|
|
ExFreePoolWithTag(DeviceExt->SpareVPB, TAG_VPB);
|
|
if (DeviceExt && DeviceExt->Statistics)
|
|
ExFreePoolWithTag(DeviceExt->Statistics, TAG_STATS);
|
|
if (DeviceObject)
|
|
IoDeleteDevice(DeviceObject);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/*
|
|
* FUNCTION: Verify the filesystem
|
|
*/
|
|
static
|
|
NTSTATUS
|
|
VfatVerify(
|
|
PVFAT_IRP_CONTEXT IrpContext)
|
|
{
|
|
PDEVICE_OBJECT DeviceToVerify;
|
|
NTSTATUS Status;
|
|
FATINFO FatInfo;
|
|
BOOLEAN RecognizedFS;
|
|
PDEVICE_EXTENSION DeviceExt;
|
|
BOOLEAN AllowRaw;
|
|
PVPB Vpb;
|
|
ULONG ChangeCount, BufSize = sizeof(ChangeCount);
|
|
|
|
DPRINT("VfatVerify(IrpContext %p)\n", IrpContext);
|
|
|
|
DeviceToVerify = IrpContext->Stack->Parameters.VerifyVolume.DeviceObject;
|
|
DeviceExt = DeviceToVerify->DeviceExtension;
|
|
Vpb = IrpContext->Stack->Parameters.VerifyVolume.Vpb;
|
|
AllowRaw = BooleanFlagOn(IrpContext->Stack->Flags, SL_ALLOW_RAW_MOUNT);
|
|
|
|
if (!BooleanFlagOn(Vpb->RealDevice->Flags, DO_VERIFY_VOLUME))
|
|
{
|
|
DPRINT("Already verified\n");
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
Status = VfatBlockDeviceIoControl(DeviceExt->StorageDevice,
|
|
IOCTL_DISK_CHECK_VERIFY,
|
|
NULL,
|
|
0,
|
|
&ChangeCount,
|
|
&BufSize,
|
|
TRUE);
|
|
if (!NT_SUCCESS(Status) && Status != STATUS_VERIFY_REQUIRED)
|
|
{
|
|
DPRINT("VfatBlockDeviceIoControl() failed (Status %lx)\n", Status);
|
|
Status = (AllowRaw ? STATUS_WRONG_VOLUME : Status);
|
|
}
|
|
else
|
|
{
|
|
Status = VfatHasFileSystem(DeviceExt->StorageDevice, &RecognizedFS, &FatInfo, TRUE);
|
|
if (!NT_SUCCESS(Status) || RecognizedFS == FALSE)
|
|
{
|
|
if (NT_SUCCESS(Status) || AllowRaw)
|
|
{
|
|
Status = STATUS_WRONG_VOLUME;
|
|
}
|
|
}
|
|
else if (sizeof(FATINFO) == RtlCompareMemory(&FatInfo, &DeviceExt->FatInfo, sizeof(FATINFO)))
|
|
{
|
|
WCHAR BufferU[MAXIMUM_VOLUME_LABEL_LENGTH / sizeof(WCHAR)];
|
|
UNICODE_STRING VolumeLabelU;
|
|
UNICODE_STRING VpbLabelU;
|
|
|
|
VolumeLabelU.Buffer = BufferU;
|
|
VolumeLabelU.Length = 0;
|
|
VolumeLabelU.MaximumLength = sizeof(BufferU);
|
|
Status = ReadVolumeLabel(DeviceExt->StorageDevice, FatInfo.rootStart * FatInfo.BytesPerSector, (FatInfo.FatType >= FATX16), &VolumeLabelU);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (AllowRaw)
|
|
{
|
|
Status = STATUS_WRONG_VOLUME;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
VpbLabelU.Buffer = Vpb->VolumeLabel;
|
|
VpbLabelU.Length = Vpb->VolumeLabelLength;
|
|
VpbLabelU.MaximumLength = sizeof(Vpb->VolumeLabel);
|
|
|
|
if (RtlCompareUnicodeString(&VpbLabelU, &VolumeLabelU, FALSE) != 0)
|
|
{
|
|
Status = STATUS_WRONG_VOLUME;
|
|
}
|
|
else
|
|
{
|
|
DPRINT1("Same volume\n");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_WRONG_VOLUME;
|
|
}
|
|
}
|
|
|
|
Vpb->RealDevice->Flags &= ~DO_VERIFY_VOLUME;
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static
|
|
NTSTATUS
|
|
VfatGetVolumeBitmap(
|
|
PVFAT_IRP_CONTEXT IrpContext)
|
|
{
|
|
DPRINT("VfatGetVolumeBitmap (IrpContext %p)\n", IrpContext);
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
|
|
static
|
|
NTSTATUS
|
|
VfatGetRetrievalPointers(
|
|
PVFAT_IRP_CONTEXT IrpContext)
|
|
{
|
|
PIO_STACK_LOCATION Stack;
|
|
LARGE_INTEGER Vcn;
|
|
PRETRIEVAL_POINTERS_BUFFER RetrievalPointers;
|
|
PFILE_OBJECT FileObject;
|
|
ULONG MaxExtentCount;
|
|
PVFATFCB Fcb;
|
|
PDEVICE_EXTENSION DeviceExt;
|
|
ULONG FirstCluster;
|
|
ULONG CurrentCluster;
|
|
ULONG LastCluster;
|
|
NTSTATUS Status;
|
|
|
|
DPRINT("VfatGetRetrievalPointers(IrpContext %p)\n", IrpContext);
|
|
|
|
DeviceExt = IrpContext->DeviceExt;
|
|
FileObject = IrpContext->FileObject;
|
|
Stack = IrpContext->Stack;
|
|
if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(STARTING_VCN_INPUT_BUFFER) ||
|
|
Stack->Parameters.DeviceIoControl.Type3InputBuffer == NULL)
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (IrpContext->Irp->UserBuffer == NULL ||
|
|
Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(RETRIEVAL_POINTERS_BUFFER))
|
|
{
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
Fcb = FileObject->FsContext;
|
|
|
|
ExAcquireResourceSharedLite(&Fcb->MainResource, TRUE);
|
|
|
|
Vcn = ((PSTARTING_VCN_INPUT_BUFFER)Stack->Parameters.DeviceIoControl.Type3InputBuffer)->StartingVcn;
|
|
RetrievalPointers = IrpContext->Irp->UserBuffer;
|
|
|
|
MaxExtentCount = ((Stack->Parameters.DeviceIoControl.OutputBufferLength - sizeof(RetrievalPointers->ExtentCount) - sizeof(RetrievalPointers->StartingVcn)) / sizeof(RetrievalPointers->Extents[0]));
|
|
|
|
if (Vcn.QuadPart >= Fcb->RFCB.AllocationSize.QuadPart / DeviceExt->FatInfo.BytesPerCluster)
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto ByeBye;
|
|
}
|
|
|
|
CurrentCluster = FirstCluster = vfatDirEntryGetFirstCluster(DeviceExt, &Fcb->entry);
|
|
Status = OffsetToCluster(DeviceExt, FirstCluster,
|
|
Vcn.u.LowPart * DeviceExt->FatInfo.BytesPerCluster,
|
|
&CurrentCluster, FALSE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto ByeBye;
|
|
}
|
|
|
|
RetrievalPointers->StartingVcn = Vcn;
|
|
RetrievalPointers->ExtentCount = 0;
|
|
RetrievalPointers->Extents[0].Lcn.u.HighPart = 0;
|
|
RetrievalPointers->Extents[0].Lcn.u.LowPart = CurrentCluster - 2;
|
|
LastCluster = 0;
|
|
while (CurrentCluster != 0xffffffff && RetrievalPointers->ExtentCount < MaxExtentCount)
|
|
{
|
|
LastCluster = CurrentCluster;
|
|
Status = NextCluster(DeviceExt, CurrentCluster, &CurrentCluster, FALSE);
|
|
Vcn.QuadPart++;
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto ByeBye;
|
|
}
|
|
|
|
if (LastCluster + 1 != CurrentCluster)
|
|
{
|
|
RetrievalPointers->Extents[RetrievalPointers->ExtentCount].NextVcn = Vcn;
|
|
RetrievalPointers->ExtentCount++;
|
|
if (RetrievalPointers->ExtentCount < MaxExtentCount)
|
|
{
|
|
RetrievalPointers->Extents[RetrievalPointers->ExtentCount].Lcn.u.HighPart = 0;
|
|
RetrievalPointers->Extents[RetrievalPointers->ExtentCount].Lcn.u.LowPart = CurrentCluster - 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
IrpContext->Irp->IoStatus.Information = sizeof(RETRIEVAL_POINTERS_BUFFER) + (sizeof(RetrievalPointers->Extents[0]) * (RetrievalPointers->ExtentCount - 1));
|
|
Status = STATUS_SUCCESS;
|
|
|
|
ByeBye:
|
|
ExReleaseResourceLite(&Fcb->MainResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
static
|
|
NTSTATUS
|
|
VfatMoveFile(
|
|
PVFAT_IRP_CONTEXT IrpContext)
|
|
{
|
|
DPRINT("VfatMoveFile(IrpContext %p)\n", IrpContext);
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
static
|
|
NTSTATUS
|
|
VfatIsVolumeDirty(
|
|
PVFAT_IRP_CONTEXT IrpContext)
|
|
{
|
|
PULONG Flags;
|
|
|
|
DPRINT("VfatIsVolumeDirty(IrpContext %p)\n", IrpContext);
|
|
|
|
if (IrpContext->Stack->Parameters.FileSystemControl.OutputBufferLength != sizeof(ULONG))
|
|
return STATUS_INVALID_BUFFER_SIZE;
|
|
else if (!IrpContext->Irp->AssociatedIrp.SystemBuffer)
|
|
return STATUS_INVALID_USER_BUFFER;
|
|
|
|
Flags = (PULONG)IrpContext->Irp->AssociatedIrp.SystemBuffer;
|
|
*Flags = 0;
|
|
|
|
if (BooleanFlagOn(IrpContext->DeviceExt->VolumeFcb->Flags, VCB_IS_DIRTY) &&
|
|
!BooleanFlagOn(IrpContext->DeviceExt->VolumeFcb->Flags, VCB_CLEAR_DIRTY))
|
|
{
|
|
*Flags |= VOLUME_IS_DIRTY;
|
|
}
|
|
|
|
IrpContext->Irp->IoStatus.Information = sizeof(ULONG);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static
|
|
NTSTATUS
|
|
VfatMarkVolumeDirty(
|
|
PVFAT_IRP_CONTEXT IrpContext)
|
|
{
|
|
PDEVICE_EXTENSION DeviceExt;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
DPRINT("VfatMarkVolumeDirty(IrpContext %p)\n", IrpContext);
|
|
DeviceExt = IrpContext->DeviceExt;
|
|
|
|
if (!BooleanFlagOn(DeviceExt->VolumeFcb->Flags, VCB_IS_DIRTY))
|
|
{
|
|
Status = SetDirtyStatus(DeviceExt, TRUE);
|
|
}
|
|
|
|
DeviceExt->VolumeFcb->Flags &= ~VCB_CLEAR_DIRTY;
|
|
|
|
return Status;
|
|
}
|
|
|
|
static
|
|
NTSTATUS
|
|
VfatLockOrUnlockVolume(
|
|
PVFAT_IRP_CONTEXT IrpContext,
|
|
BOOLEAN Lock)
|
|
{
|
|
PFILE_OBJECT FileObject;
|
|
PDEVICE_EXTENSION DeviceExt;
|
|
PVFATFCB Fcb;
|
|
PVPB Vpb;
|
|
|
|
DPRINT("VfatLockOrUnlockVolume(%p, %d)\n", IrpContext, Lock);
|
|
|
|
DeviceExt = IrpContext->DeviceExt;
|
|
FileObject = IrpContext->FileObject;
|
|
Fcb = FileObject->FsContext;
|
|
Vpb = DeviceExt->FATFileObject->Vpb;
|
|
|
|
/* Only allow locking with the volume open */
|
|
if (!BooleanFlagOn(Fcb->Flags, FCB_IS_VOLUME))
|
|
{
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
/* Bail out if it's already in the demanded state */
|
|
if ((BooleanFlagOn(DeviceExt->Flags, VCB_VOLUME_LOCKED) && Lock) ||
|
|
(!BooleanFlagOn(DeviceExt->Flags, VCB_VOLUME_LOCKED) && !Lock))
|
|
{
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
/* Bail out if it's already in the demanded state */
|
|
if ((BooleanFlagOn(Vpb->Flags, VPB_LOCKED) && Lock) ||
|
|
(!BooleanFlagOn(Vpb->Flags, VPB_LOCKED) && !Lock))
|
|
{
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
if (Lock)
|
|
{
|
|
FsRtlNotifyVolumeEvent(IrpContext->Stack->FileObject, FSRTL_VOLUME_LOCK);
|
|
}
|
|
|
|
/* Deny locking if we're not alone */
|
|
if (Lock && DeviceExt->OpenHandleCount != 1)
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
|
|
#if 1
|
|
/* FIXME: Hack that allows locking the system volume on
|
|
* boot so that autochk can run properly
|
|
* That hack is, on purpose, really restrictive
|
|
* it will only allow locking with two directories
|
|
* open: current directory of smss and autochk.
|
|
*/
|
|
BOOLEAN ForceLock = TRUE;
|
|
ULONG HandleCount = 0;
|
|
|
|
/* Only allow boot volume */
|
|
if (BooleanFlagOn(DeviceExt->Flags, VCB_IS_SYS_OR_HAS_PAGE))
|
|
{
|
|
/* We'll browse all the FCB */
|
|
ListEntry = DeviceExt->FcbListHead.Flink;
|
|
while (ListEntry != &DeviceExt->FcbListHead)
|
|
{
|
|
Fcb = CONTAINING_RECORD(ListEntry, VFATFCB, FcbListEntry);
|
|
ListEntry = ListEntry->Flink;
|
|
|
|
/* If no handle: that FCB is no problem for locking
|
|
* so ignore it
|
|
*/
|
|
if (Fcb->OpenHandleCount == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/* Not a dir? We're no longer at boot */
|
|
if (!vfatFCBIsDirectory(Fcb))
|
|
{
|
|
ForceLock = FALSE;
|
|
break;
|
|
}
|
|
|
|
/* If we have cached initialized and several handles, we're
|
|
not in the boot case
|
|
*/
|
|
if (Fcb->FileObject != NULL && Fcb->OpenHandleCount > 1)
|
|
{
|
|
ForceLock = FALSE;
|
|
break;
|
|
}
|
|
|
|
/* Count the handles */
|
|
HandleCount += Fcb->OpenHandleCount;
|
|
/* More than two handles? Then, we're not booting anymore */
|
|
if (HandleCount > 2)
|
|
{
|
|
ForceLock = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ForceLock = FALSE;
|
|
}
|
|
|
|
/* Here comes the hack, ignore the failure! */
|
|
if (!ForceLock)
|
|
{
|
|
#endif
|
|
|
|
DPRINT1("Can't lock: %u opened\n", DeviceExt->OpenHandleCount);
|
|
|
|
ListEntry = DeviceExt->FcbListHead.Flink;
|
|
while (ListEntry != &DeviceExt->FcbListHead)
|
|
{
|
|
Fcb = CONTAINING_RECORD(ListEntry, VFATFCB, FcbListEntry);
|
|
ListEntry = ListEntry->Flink;
|
|
|
|
if (Fcb->OpenHandleCount > 0)
|
|
{
|
|
DPRINT1("Opened (%u - %u): %wZ\n", Fcb->OpenHandleCount, Fcb->RefCount, &Fcb->PathNameU);
|
|
}
|
|
}
|
|
|
|
FsRtlNotifyVolumeEvent(IrpContext->Stack->FileObject, FSRTL_VOLUME_LOCK_FAILED);
|
|
|
|
return STATUS_ACCESS_DENIED;
|
|
|
|
#if 1
|
|
/* End of the hack: be verbose about its usage,
|
|
* just in case we would mess up everything!
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
DPRINT1("HACK: Using lock-hack!\n");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Finally, proceed */
|
|
if (Lock)
|
|
{
|
|
/* Flush volume & files */
|
|
VfatFlushVolume(DeviceExt, DeviceExt->VolumeFcb);
|
|
|
|
/* The volume is now clean */
|
|
if (BooleanFlagOn(DeviceExt->VolumeFcb->Flags, VCB_CLEAR_DIRTY) &&
|
|
BooleanFlagOn(DeviceExt->VolumeFcb->Flags, VCB_IS_DIRTY))
|
|
{
|
|
/* Drop the dirty bit */
|
|
if (NT_SUCCESS(SetDirtyStatus(DeviceExt, FALSE)))
|
|
ClearFlag(DeviceExt->VolumeFcb->Flags, VCB_IS_DIRTY);
|
|
}
|
|
|
|
DeviceExt->Flags |= VCB_VOLUME_LOCKED;
|
|
Vpb->Flags |= VPB_LOCKED;
|
|
}
|
|
else
|
|
{
|
|
DeviceExt->Flags &= ~VCB_VOLUME_LOCKED;
|
|
Vpb->Flags &= ~VPB_LOCKED;
|
|
|
|
FsRtlNotifyVolumeEvent(IrpContext->Stack->FileObject, FSRTL_VOLUME_UNLOCK);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static
|
|
NTSTATUS
|
|
VfatDismountVolume(
|
|
PVFAT_IRP_CONTEXT IrpContext)
|
|
{
|
|
PDEVICE_EXTENSION DeviceExt;
|
|
PLIST_ENTRY NextEntry;
|
|
PVFATFCB Fcb;
|
|
PFILE_OBJECT FileObject;
|
|
|
|
DPRINT("VfatDismountVolume(%p)\n", IrpContext);
|
|
|
|
DeviceExt = IrpContext->DeviceExt;
|
|
FileObject = IrpContext->FileObject;
|
|
|
|
/* We HAVE to be locked. Windows also allows dismount with no lock
|
|
* but we're here mainly for 1st stage, so KISS
|
|
*/
|
|
if (!BooleanFlagOn(DeviceExt->Flags, VCB_VOLUME_LOCKED))
|
|
{
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
/* Deny dismount of boot volume */
|
|
if (BooleanFlagOn(DeviceExt->Flags, VCB_IS_SYS_OR_HAS_PAGE))
|
|
{
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
/* Race condition? */
|
|
if (BooleanFlagOn(DeviceExt->Flags, VCB_DISMOUNT_PENDING))
|
|
{
|
|
return STATUS_VOLUME_DISMOUNTED;
|
|
}
|
|
|
|
/* Notify we'll dismount. Pass that point there's no reason we fail */
|
|
FsRtlNotifyVolumeEvent(IrpContext->Stack->FileObject, FSRTL_VOLUME_DISMOUNT);
|
|
|
|
ExAcquireResourceExclusiveLite(&DeviceExt->FatResource, TRUE);
|
|
|
|
/* Flush volume & files */
|
|
VfatFlushVolume(DeviceExt, (PVFATFCB)FileObject->FsContext);
|
|
|
|
/* The volume is now clean */
|
|
if (BooleanFlagOn(DeviceExt->VolumeFcb->Flags, VCB_CLEAR_DIRTY) &&
|
|
BooleanFlagOn(DeviceExt->VolumeFcb->Flags, VCB_IS_DIRTY))
|
|
{
|
|
/* Drop the dirty bit */
|
|
if (NT_SUCCESS(SetDirtyStatus(DeviceExt, FALSE)))
|
|
DeviceExt->VolumeFcb->Flags &= ~VCB_IS_DIRTY;
|
|
}
|
|
|
|
/* Rebrowse the FCB in order to free them now */
|
|
while (!IsListEmpty(&DeviceExt->FcbListHead))
|
|
{
|
|
NextEntry = RemoveTailList(&DeviceExt->FcbListHead);
|
|
Fcb = CONTAINING_RECORD(NextEntry, VFATFCB, FcbListEntry);
|
|
|
|
if (Fcb == DeviceExt->RootFcb)
|
|
DeviceExt->RootFcb = NULL;
|
|
else if (Fcb == DeviceExt->VolumeFcb)
|
|
DeviceExt->VolumeFcb = NULL;
|
|
|
|
vfatDestroyFCB(Fcb);
|
|
}
|
|
|
|
/* We are uninitializing, the VCB cannot be used anymore */
|
|
ClearFlag(DeviceExt->Flags, VCB_GOOD);
|
|
|
|
/* Mark we're being dismounted */
|
|
DeviceExt->Flags |= VCB_DISMOUNT_PENDING;
|
|
#ifndef ENABLE_SWAPOUT
|
|
IrpContext->DeviceObject->Vpb->Flags &= ~VPB_MOUNTED;
|
|
#endif
|
|
|
|
ExReleaseResourceLite(&DeviceExt->FatResource);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static
|
|
NTSTATUS
|
|
VfatGetStatistics(
|
|
PVFAT_IRP_CONTEXT IrpContext)
|
|
{
|
|
PVOID Buffer;
|
|
ULONG Length;
|
|
NTSTATUS Status;
|
|
PDEVICE_EXTENSION DeviceExt;
|
|
|
|
DeviceExt = IrpContext->DeviceExt;
|
|
Length = IrpContext->Stack->Parameters.FileSystemControl.OutputBufferLength;
|
|
Buffer = IrpContext->Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
if (Length < sizeof(FILESYSTEM_STATISTICS))
|
|
{
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
if (Buffer == NULL)
|
|
{
|
|
return STATUS_INVALID_USER_BUFFER;
|
|
}
|
|
|
|
if (Length >= sizeof(STATISTICS) * VfatGlobalData->NumberProcessors)
|
|
{
|
|
Length = sizeof(STATISTICS) * VfatGlobalData->NumberProcessors;
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
|
|
RtlCopyMemory(Buffer, DeviceExt->Statistics, Length);
|
|
IrpContext->Irp->IoStatus.Information = Length;
|
|
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* FUNCTION: File system control
|
|
*/
|
|
NTSTATUS
|
|
VfatFileSystemControl(
|
|
PVFAT_IRP_CONTEXT IrpContext)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
DPRINT("VfatFileSystemControl(IrpContext %p)\n", IrpContext);
|
|
|
|
ASSERT(IrpContext);
|
|
ASSERT(IrpContext->Irp);
|
|
ASSERT(IrpContext->Stack);
|
|
|
|
IrpContext->Irp->IoStatus.Information = 0;
|
|
|
|
switch (IrpContext->MinorFunction)
|
|
{
|
|
case IRP_MN_KERNEL_CALL:
|
|
case IRP_MN_USER_FS_REQUEST:
|
|
switch(IrpContext->Stack->Parameters.DeviceIoControl.IoControlCode)
|
|
{
|
|
case FSCTL_GET_VOLUME_BITMAP:
|
|
Status = VfatGetVolumeBitmap(IrpContext);
|
|
break;
|
|
|
|
case FSCTL_GET_RETRIEVAL_POINTERS:
|
|
Status = VfatGetRetrievalPointers(IrpContext);
|
|
break;
|
|
|
|
case FSCTL_MOVE_FILE:
|
|
Status = VfatMoveFile(IrpContext);
|
|
break;
|
|
|
|
case FSCTL_IS_VOLUME_DIRTY:
|
|
Status = VfatIsVolumeDirty(IrpContext);
|
|
break;
|
|
|
|
case FSCTL_MARK_VOLUME_DIRTY:
|
|
Status = VfatMarkVolumeDirty(IrpContext);
|
|
break;
|
|
|
|
case FSCTL_LOCK_VOLUME:
|
|
Status = VfatLockOrUnlockVolume(IrpContext, TRUE);
|
|
break;
|
|
|
|
case FSCTL_UNLOCK_VOLUME:
|
|
Status = VfatLockOrUnlockVolume(IrpContext, FALSE);
|
|
break;
|
|
|
|
case FSCTL_DISMOUNT_VOLUME:
|
|
Status = VfatDismountVolume(IrpContext);
|
|
break;
|
|
|
|
case FSCTL_FILESYSTEM_GET_STATISTICS:
|
|
Status = VfatGetStatistics(IrpContext);
|
|
break;
|
|
|
|
default:
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
break;
|
|
|
|
case IRP_MN_MOUNT_VOLUME:
|
|
Status = VfatMount(IrpContext);
|
|
break;
|
|
|
|
case IRP_MN_VERIFY_VOLUME:
|
|
DPRINT("VFATFS: IRP_MN_VERIFY_VOLUME\n");
|
|
Status = VfatVerify(IrpContext);
|
|
break;
|
|
|
|
default:
|
|
DPRINT("VFAT FSC: MinorFunction %u\n", IrpContext->MinorFunction);
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
}
|
|
|
|
return Status;
|
|
}
|