582 lines
15 KiB
C
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;
|
|
}
|