/* * 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 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_VFAT); 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 > 32 * 1024) { DPRINT1("ClusterSize %dx\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; 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; } } } ExFreePool(Boot); } if (!*RecognizedFS && PartitionInfoIsValid) { BootFatX = ExAllocatePoolWithTag(NonPagedPool, sizeof(struct _BootSectorFatX), TAG_VFAT); 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 %c%c%c%c\n", BootFatX->SysType[0], BootFatX->SysType[1], BootFatX->SysType[2], 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; } } } ExFreePool(BootFatX); } 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_VFAT); if (Buffer != NULL) { Status = VfatReadDisk(DeviceObject, &FileOffset, PAGE_SIZE, (PUCHAR)Buffer, TRUE); if (!NT_SUCCESS(Status)) { ExFreePoolWithTag(Buffer, TAG_VFAT); } else { Entry = Buffer; } } else { Status = STATUS_INSUFFICIENT_RESOURCES; } } if (NT_SUCCESS(Status)) { while (TRUE) { if (ENTRY_VOLUME(IsFatX, Entry)) { /* copy volume label */ if (IsFatX) { StringO.Buffer = (PCHAR)Entry->FatX.Filename; StringO.MaximumLength = StringO.Length = Entry->FatX.FilenameLength; RtlOemStringToUnicodeString(VolumeLabel, &StringO, FALSE); } else { vfat8Dot3ToString(&Entry->Fat, VolumeLabel); } break; } if (ENTRY_END(IsFatX, Entry)) { break; } DirIndex++; Entry = (PDIR_ENTRY)((ULONG_PTR)Entry + SizeDirEntry); if ((DirIndex % EntriesPerPage) == 0) { FileOffset.u.LowPart += PAGE_SIZE; if (!NoCache) { CcUnpinData(Context); _SEH2_TRY { CcMapData(pFcb->FileObject, &FileOffset, SizeDirEntry, MAP_WAIT, &Context, (PVOID*)&Entry); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; if (!NT_SUCCESS(Status)) { Context = NULL; break; } } else { Status = VfatReadDisk(DeviceObject, &FileOffset, PAGE_SIZE, (PUCHAR)Buffer, TRUE); if (!NT_SUCCESS(Status)) { break; } Entry = Buffer; } } } if (Context) { CcUnpinData(Context); } else if (NoCache) { ExFreePoolWithTag(Buffer, TAG_VFAT); } } if (!NoCache) { ExAcquireResourceExclusiveLite(&DeviceExt->DirResource, TRUE); vfatReleaseFCB(DeviceExt, pFcb); ExReleaseResourceLite(&DeviceExt->DirResource); } 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; PVFATCCB Ccb = 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 eocMark; ULONG i; FATINFO FatInfo; 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; DeviceExt->CleanShutBitMask = 0; break; case FAT16: case FATX16: DeviceExt->GetNextCluster = FAT16GetNextCluster; DeviceExt->FindAndMarkAvailableCluster = FAT16FindAndMarkAvailableCluster; DeviceExt->WriteCluster = FAT16WriteCluster; DeviceExt->CleanShutBitMask = 0x8000; break; case FAT32: case FATX32: DeviceExt->GetNextCluster = FAT32GetNextCluster; DeviceExt->FindAndMarkAvailableCluster = FAT32FindAndMarkAvailableCluster; DeviceExt->WriteCluster = FAT32WriteCluster; DeviceExt->CleanShutBitMask = 0x80000000; 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_VFAT); if (DeviceExt->SpareVPB == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto ByeBye; } DeviceExt->Statistics = ExAllocatePoolWithTag(NonPagedPool, sizeof(STATISTICS) * VfatGlobalData->NumberProcessors, TAG_VFAT); if (DeviceExt->Statistics == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto ByeBye; } RtlZeroMemory(DeviceExt->Statistics, sizeof(STATISTICS) * VfatGlobalData->NumberProcessors); for (i = 0; i < VfatGlobalData->NumberProcessors; ++i) { DeviceExt->Statistics[i].Base.FileSystemType = FILESYSTEM_STATISTICS_TYPE_FAT; DeviceExt->Statistics[i].Base.Version = 1; DeviceExt->Statistics[i].Base.SizeOfCompleteStructure = sizeof(STATISTICS); } DeviceExt->FATFileObject = IoCreateStreamFileObject(NULL, DeviceExt->StorageDevice); Fcb = vfatNewFCB(DeviceExt, &NameU); if (Fcb == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto ByeBye; } Ccb = ExAllocateFromNPagedLookasideList(&VfatGlobalData->CcbLookasideList); if (Ccb == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto ByeBye; } RtlZeroMemory(Ccb, sizeof (VFATCCB)); DeviceExt->FATFileObject->FsContext = Fcb; DeviceExt->FATFileObject->FsContext2 = Ccb; DeviceExt->FATFileObject->SectionObjectPointer = &Fcb->SectionObjectPointers; DeviceExt->FATFileObject->PrivateCacheMap = NULL; DeviceExt->FATFileObject->Vpb = DeviceObject->Vpb; 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; 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 clean shutdown bit status */ Status = GetNextCluster(DeviceExt, 1, &eocMark); if (NT_SUCCESS(Status)) { if (eocMark & DeviceExt->CleanShutBitMask) { /* unset clean shutdown bit */ eocMark &= ~DeviceExt->CleanShutBitMask; WriteCluster(DeviceExt, 1, eocMark); VolumeFcb->Flags |= VCB_CLEAR_DIRTY; } } VolumeFcb->Flags |= VCB_IS_DIRTY; if (BooleanFlagOn(Vpb->RealDevice->Flags, DO_SYSTEM_BOOT_PARTITION)) { SetFlag(DeviceExt->Flags, VCB_IS_SYS_OR_HAS_PAGE); } FsRtlNotifyVolumeEvent(DeviceExt->FATFileObject, FSRTL_VOLUME_MOUNT); FsRtlNotifyInitializeSync(&DeviceExt->NotifySync); InitializeListHead(&DeviceExt->NotifyList); DPRINT("Mount success\n"); Status = STATUS_SUCCESS; ByeBye: if (!NT_SUCCESS(Status)) { /* Cleanup */ if (DeviceExt && DeviceExt->FATFileObject) ObDereferenceObject (DeviceExt->FATFileObject); if (DeviceExt && DeviceExt->SpareVPB) ExFreePoolWithTag(DeviceExt->SpareVPB, TAG_VFAT); if (DeviceExt && DeviceExt->Statistics) ExFreePoolWithTag(DeviceExt->Statistics, TAG_VFAT); if (Fcb) vfatDestroyFCB(Fcb); if (Ccb) vfatDestroyCCB(Ccb); 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) { ULONG eocMark; 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 = GetNextCluster(DeviceExt, 1, &eocMark); if (NT_SUCCESS(Status)) { /* unset clean shutdown bit */ eocMark &= ~DeviceExt->CleanShutBitMask; Status = WriteCluster(DeviceExt, 1, eocMark); } } DeviceExt->VolumeFcb->Flags &= ~VCB_CLEAR_DIRTY; return Status; } static NTSTATUS VfatLockOrUnlockVolume( PVFAT_IRP_CONTEXT IrpContext, BOOLEAN Lock) { PFILE_OBJECT FileObject; PDEVICE_EXTENSION DeviceExt; PVFATFCB Fcb; PVPB Vpb; DPRINT("VfatLockOrUnlockVolume(%p, %d)\n", IrpContext, Lock); DeviceExt = IrpContext->DeviceExt; FileObject = IrpContext->FileObject; Fcb = FileObject->FsContext; Vpb = DeviceExt->FATFileObject->Vpb; /* Only allow locking with the volume open */ if (!BooleanFlagOn(Fcb->Flags, FCB_IS_VOLUME)) { return STATUS_ACCESS_DENIED; } /* Bail out if it's already in the demanded state */ if ((BooleanFlagOn(DeviceExt->Flags, VCB_VOLUME_LOCKED) && Lock) || (!BooleanFlagOn(DeviceExt->Flags, VCB_VOLUME_LOCKED) && !Lock)) { return STATUS_ACCESS_DENIED; } /* Bail out if it's already in the demanded state */ if ((BooleanFlagOn(Vpb->Flags, VPB_LOCKED) && Lock) || (!BooleanFlagOn(Vpb->Flags, VPB_LOCKED) && !Lock)) { return STATUS_ACCESS_DENIED; } /* Deny locking if we're not alone */ if (Lock && DeviceExt->OpenHandleCount != 1) { PLIST_ENTRY ListEntry; DPRINT1("Can't lock: %u opened\n", DeviceExt->OpenHandleCount); ListEntry = DeviceExt->FcbListHead.Flink; while (ListEntry != &DeviceExt->FcbListHead) { Fcb = CONTAINING_RECORD(ListEntry, VFATFCB, FcbListEntry); ListEntry = ListEntry->Flink; if (Fcb->OpenHandleCount > 0) { DPRINT1("Opened (%u - %u): %wZ\n", Fcb->OpenHandleCount, Fcb->RefCount, &Fcb->PathNameU); } } return STATUS_ACCESS_DENIED; } /* Finally, proceed */ if (Lock) { DeviceExt->Flags |= VCB_VOLUME_LOCKED; Vpb->Flags |= VPB_LOCKED; } else { DeviceExt->Flags &= ~VCB_VOLUME_LOCKED; Vpb->Flags &= ~VPB_LOCKED; } return STATUS_SUCCESS; } static NTSTATUS VfatDismountVolume( PVFAT_IRP_CONTEXT IrpContext) { PDEVICE_EXTENSION DeviceExt; PLIST_ENTRY NextEntry; PVFATFCB Fcb; PFILE_OBJECT FileObject; ULONG eocMark; NTSTATUS Status; 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); if (BooleanFlagOn(DeviceExt->VolumeFcb->Flags, VCB_CLEAR_DIRTY)) { /* Set clean shutdown bit */ Status = GetNextCluster(DeviceExt, 1, &eocMark); if (NT_SUCCESS(Status)) { eocMark |= DeviceExt->CleanShutBitMask; if (NT_SUCCESS(WriteCluster(DeviceExt, 1, eocMark))) DeviceExt->VolumeFcb->Flags &= ~VCB_IS_DIRTY; } } /* Flush volume & files */ VfatFlushVolume(DeviceExt, (PVFATFCB)FileObject->FsContext); /* Rebrowse the FCB in order to free them now */ while (!IsListEmpty(&DeviceExt->FcbListHead)) { NextEntry = RemoveTailList(&DeviceExt->FcbListHead); Fcb = CONTAINING_RECORD(NextEntry, VFATFCB, FcbListEntry); vfatDestroyFCB(Fcb); } /* Mark we're being dismounted */ DeviceExt->Flags |= VCB_DISMOUNT_PENDING; #ifndef ENABLE_SWAPOUT IrpContext->DeviceObject->Vpb->Flags &= ~VPB_MOUNTED; #endif ExReleaseResourceLite(&DeviceExt->FatResource); /* Release a few resources and quit, we're done */ ExDeleteResourceLite(&DeviceExt->DirResource); ExDeleteResourceLite(&DeviceExt->FatResource); ObDereferenceObject(DeviceExt->FATFileObject); return STATUS_SUCCESS; } static NTSTATUS VfatGetStatistics( PVFAT_IRP_CONTEXT IrpContext) { PVOID Buffer; ULONG Length; NTSTATUS Status; PDEVICE_EXTENSION DeviceExt; DeviceExt = IrpContext->DeviceExt; Length = IrpContext->Stack->Parameters.FileSystemControl.OutputBufferLength; Buffer = IrpContext->Irp->AssociatedIrp.SystemBuffer; if (Length < sizeof(FILESYSTEM_STATISTICS)) { return STATUS_BUFFER_TOO_SMALL; } if (Buffer == NULL) { return STATUS_INVALID_USER_BUFFER; } if (Length >= sizeof(STATISTICS) * VfatGlobalData->NumberProcessors) { Length = sizeof(STATISTICS) * VfatGlobalData->NumberProcessors; Status = STATUS_SUCCESS; } else { Status = STATUS_BUFFER_OVERFLOW; } RtlCopyMemory(Buffer, DeviceExt->Statistics, Length); IrpContext->Irp->IoStatus.Information = Length; return Status; } /* * 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; }