2020-09-30 17:17:25 +02:00

582 lines
15 KiB
C

/*++
Copyright (c) 2000-2001 Microsoft Corporation
Module Name:
mountvol.c
Abstract:
This module implements routines related to the mounting of a FAT volume.
--*/
#include "fatx.h"
NTSTATUS
FatxCreateVolumeDevice(
IN PDEVICE_OBJECT TargetDeviceObject,
IN PDISK_GEOMETRY DiskGeometry,
IN PPARTITION_INFORMATION PartitionInformation,
OUT PDEVICE_OBJECT *ReturnedVolumeDeviceObject
)
/*++
Routine Description:
This routine creates an instance of a volume device object.
Arguments:
TargetDeviceObject - Specifies the device object that the volume object
sends I/O requests to.
DiskGeometry - Specifies the disk geometry of the target device.
PartitionInformation - Specifies the partition information for the target
device.
ReturnedVolumeDeviceObject - Specifies the buffer to receive the created
device object.
Return Value:
Status of operation.
--*/
{
NTSTATUS status;
PDEVICE_OBJECT VolumeDeviceObject;
PFAT_VOLUME_EXTENSION VolumeExtension;
//
// Create the volume's device object.
//
status = IoCreateDevice(&FatxDriverObject, sizeof(FAT_VOLUME_EXTENSION),
NULL, FILE_DEVICE_DISK_FILE_SYSTEM, FALSE, &VolumeDeviceObject);
if (!NT_SUCCESS(status)) {
return status;
}
//
// Increase the volume's IRP stack size for requests that we pass down to
// the target device object.
//
VolumeDeviceObject->StackSize = (UCHAR)(VolumeDeviceObject->StackSize +
TargetDeviceObject->StackSize);
//
// Ensure that the alignment for this device matches the requirements of the
// target device.
//
if (VolumeDeviceObject->AlignmentRequirement < TargetDeviceObject->AlignmentRequirement) {
VolumeDeviceObject->AlignmentRequirement = TargetDeviceObject->AlignmentRequirement;
}
//
// If the target device object supports scatter gather I/O, then indicate
// that we can as well.
//
if (FatxIsFlagSet(TargetDeviceObject->Flags, DO_SCATTER_GATHER_IO)) {
VolumeDeviceObject->Flags |= DO_SCATTER_GATHER_IO;
}
//
// Set the size of a sector for this device.
//
VolumeDeviceObject->SectorSize = (USHORT)DiskGeometry->BytesPerSector;
//
// Increment the reference count on the target device object since we'll be
// holding a pointer to it.
//
ObReferenceObject(TargetDeviceObject);
//
// Initialize the volume's device extension data.
//
VolumeExtension = (PFAT_VOLUME_EXTENSION)VolumeDeviceObject->DeviceExtension;
VolumeExtension->TargetDeviceObject = TargetDeviceObject;
VolumeExtension->PartitionLength = PartitionInformation->PartitionLength;
VolumeExtension->SectorSize = DiskGeometry->BytesPerSector;
VolumeExtension->SectorShift = RtlFindFirstSetRightMember(DiskGeometry->BytesPerSector);
//
// Initialize the volume's instance lock.
//
ExInitializeReadWriteLock(&VolumeExtension->VolumeMutex);
//
// Initialize the volume's cluster cache lock.
//
RtlInitializeCriticalSection(&VolumeExtension->ClusterCacheMutex);
//
// Initialize the file control block for the physical volume.
//
VolumeExtension->VolumeFcb.Flags = FAT_FCB_VOLUME;
VolumeExtension->VolumeFcb.ReferenceCount = 1;
//
// The device has finished initializing and is ready to accept requests.
//
VolumeDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
//
// Return the device object to the caller.
//
*ReturnedVolumeDeviceObject = VolumeDeviceObject;
return STATUS_SUCCESS;
}
VOID
FatxDeleteVolumeDevice(
IN PDEVICE_OBJECT VolumeDeviceObject
)
/*++
Routine Description:
This routine deletes an instance of a volume device object.
Arguments:
VolumeDeviceObject - Specifies the volume device object to delete.
Return Value:
Status of operation.
--*/
{
PFAT_VOLUME_EXTENSION VolumeExtension;
VolumeExtension = (PFAT_VOLUME_EXTENSION)VolumeDeviceObject->DeviceExtension;
//
// There shouldn't be any open files for this volume.
//
ASSERT(VolumeExtension->FileObjectCount == 0);
//
// Delete the volume's cluster cache lock.
//
RtlDeleteCriticalSection(&VolumeExtension->ClusterCacheMutex);
//
// Delete any file control blocks created by the volume.
//
if (VolumeExtension->RootDirectoryFcb != NULL) {
FatxDereferenceFcb(VolumeExtension->RootDirectoryFcb);
}
//
// Invalidate any file system cache buffers for this device.
//
FscInvalidateDevice(&VolumeExtension->CacheExtension);
//
// Release the reference on the target device object.
//
if (VolumeExtension->TargetDeviceObject != NULL) {
ObDereferenceObject(VolumeExtension->TargetDeviceObject);
}
//
// Delete the device object.
//
IoDeleteDevice(VolumeDeviceObject);
}
NTSTATUS
FatxProcessBootSector(
IN PDEVICE_OBJECT VolumeDeviceObject,
IN PIRP Irp,
IN PLARGE_INTEGER PartitionLength
)
/*++
Routine Description:
This routine reads the boot sector for the supplied volume device object,
validates its structure, and caches any interesting information in the
device extension.
Arguments:
VolumeDeviceObject - Specifies the volume device object to mount.
Irp - Specifies the packet that describes the I/O request.
PartitionLength - Specifies the length of the partition in bytes.
Return Value:
Status of operation.
--*/
{
NTSTATUS status;
PFAT_VOLUME_EXTENSION VolumeExtension;
PVOID CacheBuffer;
ULONG StructureAlignment;
ULONG NumberOfReservedBytes;
PFAT_VOLUME_METADATA VolumeMetadata;
ULONG RootDirectoryFirstCluster;
ULONG NumberOfClustersLimit;
ULONG NumberOfBytesPerFat;
ULONGLONG NumberOfUnreservedBytes;
ULONGLONG NumberOfFileAreaBytes;
VolumeExtension = (PFAT_VOLUME_EXTENSION)VolumeDeviceObject->DeviceExtension;
CacheBuffer = NULL;
//
// Compute the alignment between the various file system structures.
// Everything should be page aligned in order to maximum file system cache
// efficiency.
//
StructureAlignment = ROUND_TO_PAGES(VolumeExtension->SectorSize);
//
// Compute the number of reserved bytes for the volume.
//
// We only need to reserve enough space for the volume metadata block.
//
NumberOfReservedBytes = PAGE_SIZE;
if (NumberOfReservedBytes < StructureAlignment) {
NumberOfReservedBytes = StructureAlignment;
}
//
// Verify that the partition has enough space to contain the reserved bytes.
//
if ((ULONGLONG)NumberOfReservedBytes >= (ULONGLONG)PartitionLength->QuadPart) {
status = STATUS_UNRECOGNIZED_VOLUME;
goto CleanupAndExit;
}
//
// Map the volume metadata block into the cache.
//
status = FscMapBuffer(&VolumeExtension->CacheExtension, Irp, 0, FALSE,
&CacheBuffer);
if (!NT_SUCCESS(status)) {
status = STATUS_UNRECOGNIZED_VOLUME;
goto CleanupAndExit;
}
VolumeMetadata = (PFAT_VOLUME_METADATA)CacheBuffer;
//
// Verify that the signature is valid.
//
if (VolumeMetadata->Signature != FAT_VOLUME_SIGNATURE) {
FatxDbgPrint(("FATX: found unrecognized signature\n"));
status = STATUS_UNRECOGNIZED_VOLUME;
goto CleanupAndExit;
}
//
// Verify that the sectors per cluster looks reasonable.
//
switch (VolumeMetadata->SectorsPerCluster) {
case 1:
case 2:
case 4:
case 8:
case 16:
case 32:
case 64:
case 128:
break;
default:
FatxDbgPrint(("FATX: found invalid sectors per cluster\n"));
status = STATUS_UNRECOGNIZED_VOLUME;
goto CleanupAndExit;
}
VolumeExtension->BytesPerCluster = VolumeMetadata->SectorsPerCluster <<
VolumeExtension->SectorShift;
VolumeExtension->ClusterShift =
RtlFindFirstSetRightMember(VolumeExtension->BytesPerCluster);
//
// Copy the serial number from to the volume extension.
//
VolumeExtension->SerialNumber = VolumeMetadata->SerialNumber;
//
// Remember the root directory cluster and unmap the volume metadata block.
//
RootDirectoryFirstCluster = VolumeMetadata->RootDirFirstCluster;
FscUnmapBuffer(CacheBuffer);
CacheBuffer = NULL;
//
// Verify that the number of bytes per cluster is greater than or equal to
// the structure alignment requirement.
//
if (VolumeExtension->BytesPerCluster < StructureAlignment) {
FatxDbgPrint(("FATX: found too small of cluster size\n"));
status = STATUS_UNRECOGNIZED_VOLUME;
goto CleanupAndExit;
}
//
// For volumes with more than 64K of clusters, we use 32-bit file allocation
// table entries. Otherwise, we use 16-bit file allocation table entries.
// This is not FAT16 as defined by DOS; we still have a FAT32 style BIOS
// parameter block, but the FAT is in a "compressed" format.
//
// Note that we base the test on the limit of the number of clusters for the
// entire partition. The same test is performed in the FAT format code.
//
// The below number isn't exactly 64K, because we need to avoid the magic
// cluster numbers that mark free or end of file clusters.
//
NumberOfClustersLimit = (ULONG)((ULONGLONG)PartitionLength->QuadPart >>
VolumeExtension->ClusterShift) + FAT_RESERVED_FAT_ENTRIES;
if (NumberOfClustersLimit < FAT_CLUSTER16_RESERVED) {
VolumeExtension->Flags |= FAT_VOLUME_FAT16X;
VolumeExtension->FatEntryShift = 1;
NumberOfBytesPerFat = NumberOfClustersLimit * sizeof(USHORT);
} else {
VolumeExtension->FatEntryShift = 2;
NumberOfBytesPerFat = NumberOfClustersLimit * sizeof(ULONG);
}
NumberOfBytesPerFat = (NumberOfBytesPerFat + (StructureAlignment - 1)) &
~(StructureAlignment - 1);
//
// Compute the number of bytes that haven't been reserved above.
//
NumberOfUnreservedBytes = (ULONGLONG)PartitionLength->QuadPart -
NumberOfReservedBytes;
//
// Verify that the partition has enough space to contain the FAT.
//
if ((ULONGLONG)NumberOfBytesPerFat >= NumberOfUnreservedBytes) {
FatxDbgPrint(("FATX: volume too small to hold the FAT\n"));
status = STATUS_UNRECOGNIZED_VOLUME;
goto CleanupAndExit;
}
//
// Compute the number of clusters available on the volume.
//
NumberOfFileAreaBytes = NumberOfUnreservedBytes - NumberOfBytesPerFat;
VolumeExtension->NumberOfClusters = (ULONG)(NumberOfFileAreaBytes >>
VolumeExtension->ClusterShift);
//
// Compute the physical byte offsets of the FAT and the first cluster.
//
VolumeExtension->FatByteOffset = NumberOfReservedBytes;
VolumeExtension->FileAreaByteOffset = VolumeExtension->FatByteOffset +
NumberOfBytesPerFat;
//
// When dealing with the file system cache, other parts of the file system
// assume that the file allocation table or file area start on a page
// aligned byte offset. Also, the number of bytes per cluster must be a
// multiple of the page size.
//
ASSERT(BYTE_OFFSET(VolumeExtension->FatByteOffset) == 0);
ASSERT(BYTE_OFFSET(VolumeExtension->FileAreaByteOffset) == 0);
ASSERT(BYTE_OFFSET(VolumeExtension->BytesPerCluster) == 0);
//
// Create the file control block for the root directory.
//
status = FatxCreateFcb(NULL, RootDirectoryFirstCluster, NULL, 0,
&VolumeExtension->RootDirectoryFcb);
if (!NT_SUCCESS(status)) {
goto CleanupAndExit;
}
status = STATUS_SUCCESS;
CleanupAndExit:
if (CacheBuffer != NULL) {
FscUnmapBuffer(CacheBuffer);
}
return status;
}
NTSTATUS
FatxMountVolume(
IN PDEVICE_OBJECT TargetDeviceObject
)
/*++
Routine Description:
This routine is called by the I/O manager to attempt to mount this file
system.
Arguments:
TargetDeviceObject - Specifies the device object to attempt to mount.
Return Value:
Status of operation.
--*/
{
NTSTATUS status;
PIRP Irp;
PDEVICE_OBJECT VolumeDeviceObject;
PFAT_VOLUME_EXTENSION VolumeExtension;
DISK_GEOMETRY DiskGeometry;
PARTITION_INFORMATION PartitionInformation;
Irp = NULL;
VolumeDeviceObject = NULL;
FatxAcquireGlobalMutexExclusive();
//
// Get the drive geometry from the device.
//
status = IoSynchronousDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY,
TargetDeviceObject, NULL, 0, &DiskGeometry, sizeof(DISK_GEOMETRY), NULL,
FALSE);
if (!NT_SUCCESS(status)) {
goto CleanupAndExit;
}
//
// Get the partition information from the device.
//
status = IoSynchronousDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO,
TargetDeviceObject, NULL, 0, &PartitionInformation,
sizeof(PARTITION_INFORMATION), NULL, FALSE);
if (!NT_SUCCESS(status)) {
goto CleanupAndExit;
}
//
// Create an I/O request packet in order to submit requests to the target
// device.
//
Irp = IoAllocateIrp(TargetDeviceObject->StackSize);
if (Irp == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto CleanupAndExit;
}
//
// Create the volume's device object.
//
status = FatxCreateVolumeDevice(TargetDeviceObject, &DiskGeometry,
&PartitionInformation, &VolumeDeviceObject);
if (!NT_SUCCESS(status)) {
goto CleanupAndExit;
}
VolumeExtension = (PFAT_VOLUME_EXTENSION)VolumeDeviceObject->DeviceExtension;
//
// Process the boot sector for the volume.
//
status = FatxProcessBootSector(VolumeDeviceObject, Irp,
&PartitionInformation.PartitionLength);
if (!NT_SUCCESS(status)) {
goto CleanupAndExit;
}
//
// Initialize the allocation support for the volume.
//
status = FatxInitializeAllocationSupport(VolumeExtension, Irp);
if (!NT_SUCCESS(status)) {
goto CleanupAndExit;
}
//
// Connect the volume device object to the target device object and return
// success.
//
TargetDeviceObject->MountedOrSelfDevice = VolumeDeviceObject;
VolumeDeviceObject = NULL;
status = STATUS_SUCCESS;
CleanupAndExit:
if (VolumeDeviceObject != NULL) {
FatxDeleteVolumeDevice(VolumeDeviceObject);
}
if (Irp != NULL) {
IoFreeIrp(Irp);
}
FatxReleaseGlobalMutex();
return status;
}