/*++ Copyright (c) 2000-2001 Microsoft Corporation Module Name: fsctrl.c Abstract: This module implements routines related to handling IRP_MJ_FILE_SYSTEM_CONTROL. --*/ #include "fatx.h" VOID FatxSignalDismountUnblockEvent( IN PFAT_VOLUME_EXTENSION VolumeExtension ) /*++ Routine Description: This routine signals the volume's dismount unblock event when the volume's dismount block count is negative. Arguments: VolumeExtension - Specifies the volume to signal the dismount unblock event for. Return Value: None. --*/ { KIRQL OldIrql; OldIrql = KeRaiseIrqlToDpcLevel(); if (VolumeExtension->DismountUnblockEvent != NULL) { KeSetEvent(VolumeExtension->DismountUnblockEvent, 0, FALSE); } KeLowerIrql(OldIrql); } NTSTATUS FatxDismountVolume( IN PDEVICE_OBJECT DeviceObject ) /*++ Routine Description: This routine is indirectly called by the I/O manager to handle FSCTL_DISMOUNT_VOLUME requests. Arguments: DeviceObject - Specifies the device object that the I/O request is for. Return Value: Status of operation. --*/ { PFAT_VOLUME_EXTENSION VolumeExtension; KEVENT DismountUnblockEvent; KIRQL OldIrql; ULONG FileObjectCount; VolumeExtension = (PFAT_VOLUME_EXTENSION)DeviceObject->DeviceExtension; FatxAcquireVolumeMutexExclusive(VolumeExtension); // // Check if the volume is already marked for dismount. If not, mark it for // dismount. // if (FatxIsFlagSet(VolumeExtension->Flags, FAT_VOLUME_DISMOUNTED)) { FatxReleaseVolumeMutex(VolumeExtension); return STATUS_VOLUME_DISMOUNTED; } VolumeExtension->Flags |= FAT_VOLUME_DISMOUNTED; // // Synchronize the dismount operation with any read and write operations // already in progress. When all read and write operations have completed, // the DismountBlockCount will be zero. If the DismountBlockCount is not // zero at this point, then we need to block. Whenever a read or write // operation completes, the DismountBlockCount is decremented and if the // count is negative, then that indicates that this routine is blocked // waiting for the operation to complete. // // A read or write operation can complete from a DPC, so we need to // synchronize access to the DismountBlockCount and DismountUnblockEvent at // DISPATCH_LEVEL. // KeInitializeEvent(&DismountUnblockEvent, SynchronizationEvent, FALSE); OldIrql = KeRaiseIrqlToDpcLevel(); ASSERT(VolumeExtension->DismountUnblockEvent == NULL); VolumeExtension->DismountUnblockEvent = &DismountUnblockEvent; if (VolumeExtension->DismountBlockCount > 0) { // // Decrement the dismount count so that the lowest possible state is // negative one, which FatxDecrementDismountBlockCount uses as the // trigger to know that we need to be signaled. // VolumeExtension->DismountBlockCount--; // // Wait for the unblock event to be signaled outside of the raised IRQL // and ownership of the volume's mutex. We don't have to worry about // another thread attempting to dismount this volume because the above // code has already marked the volume as dismounted, so another dismount // request will fail. // KeLowerIrql(OldIrql); FatxReleaseVolumeMutex(VolumeExtension); KeWaitForSingleObject(&DismountUnblockEvent, Executive, KernelMode, FALSE, NULL); FatxAcquireVolumeMutexExclusive(VolumeExtension); OldIrql = KeRaiseIrqlToDpcLevel(); // // Boost the lowest possible state back up to zero so that read and // write operations to the dismounted volume don't keep calling // FatxSignalDismountUnblockEvent. // VolumeExtension->DismountBlockCount++; } ASSERT(VolumeExtension->DismountUnblockEvent == &DismountUnblockEvent); VolumeExtension->DismountUnblockEvent = NULL; KeLowerIrql(OldIrql); // // Invalidate any file system cache buffers for this device. // FatxDeleteVolumeDevice will also invalidate the cache, but we might as // well release as many cache pages as we can now. // FscInvalidateDevice(&VolumeExtension->CacheExtension); // // Synchronize access to the MountedOrSelfDevice with the I/O manager by // raising to DISPATCH_LEVEL. // // Clear out the target device object's MountedOrSelfDevice field. That // will cause future accesses to the target device object to mount a new // file system device object. // OldIrql = KeRaiseIrqlToDpcLevel(); VolumeExtension->TargetDeviceObject->MountedOrSelfDevice = NULL; KeLowerIrql(OldIrql); // // Release the reference on the target device object. // ObDereferenceObject(VolumeExtension->TargetDeviceObject); VolumeExtension->TargetDeviceObject = NULL; // // Take a snapshot of the file object count, release the volume mutex, and // delete the volume device if the file object count is zero. This won't // normally happen since there's probably an open handle in order to make // this dismount call, but it could happen if an IRP was submitted directly // to the device object. // // We'll print out a debug message if the file object count is greater than // two. XUnmountMU dismounts a volume with two file objects outstanding, so // anything beyond that represents some user file that hasn't been closed. // FileObjectCount = VolumeExtension->FileObjectCount; FatxReleaseVolumeMutex(VolumeExtension); if (FileObjectCount == 0) { FatxDeleteVolumeDevice(DeviceObject); } else if (FileObjectCount > 2) { FatxDbgPrint(("FATX: dismounting volume %p with %d open file handles\n", VolumeExtension, FileObjectCount)); } return STATUS_SUCCESS; } NTSTATUS FatxReadWriteVolumeMetadata( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is indirectly called by the I/O manager to handle FSCTL_READ_VOLUME_METADATA and FSCTL_WRITE_VOLUME_METADATA requests. Arguments: DeviceObject - Specifies the device object that the I/O request is for. Irp - Specifies the packet that describes the I/O request. Return Value: Status of operation. --*/ { NTSTATUS status; PFAT_VOLUME_EXTENSION VolumeExtension; PIO_STACK_LOCATION IrpSp; PFSCTL_VOLUME_METADATA FsctlVolumeMetadata; BOOLEAN WritingMetadata; PVOID CacheBuffer; VolumeExtension = (PFAT_VOLUME_EXTENSION)DeviceObject->DeviceExtension; IrpSp = IoGetCurrentIrpStackLocation(Irp); FatxAcquireVolumeMutexExclusive(VolumeExtension); // // Verify that the input buffer is large enough. // if (IrpSp->Parameters.FileSystemControl.InputBufferLength < sizeof(FSCTL_VOLUME_METADATA)) { status = STATUS_INVALID_PARAMETER; goto CleanupAndExit; } // // Verify that the starting byte offset and transfer length are valid. The // volume metadata sector is at least 4096 bytes in length. // FsctlVolumeMetadata = (PFSCTL_VOLUME_METADATA)IrpSp->Parameters.FileSystemControl.InputBuffer; if ((FsctlVolumeMetadata->ByteOffset >= PAGE_SIZE) || (FsctlVolumeMetadata->TransferLength > PAGE_SIZE) || ((FsctlVolumeMetadata->ByteOffset + FsctlVolumeMetadata->TransferLength) > PAGE_SIZE)) { status = STATUS_INVALID_PARAMETER; goto CleanupAndExit; } // // Map the volume metadata block into the file system cache. // WritingMetadata = (BOOLEAN)(IrpSp->Parameters.FileSystemControl.FsControlCode == FSCTL_WRITE_VOLUME_METADATA); status = FscMapBuffer(&VolumeExtension->CacheExtension, Irp, 0, WritingMetadata, &CacheBuffer); if (!NT_SUCCESS(status)) { goto CleanupAndExit; } if (WritingMetadata) { // // Copy from the transfer buffer to the volume metadata block and write // out the changes. // RtlCopyMemory((PUCHAR)CacheBuffer + FsctlVolumeMetadata->ByteOffset, FsctlVolumeMetadata->TransferBuffer, FsctlVolumeMetadata->TransferLength); status = FscWriteBuffer(&VolumeExtension->CacheExtension, Irp, 0, PAGE_SIZE, CacheBuffer); } else { // // Copy from the volume metadata block to the transfer buffer. // RtlCopyMemory(FsctlVolumeMetadata->TransferBuffer, (PUCHAR)CacheBuffer + FsctlVolumeMetadata->ByteOffset, FsctlVolumeMetadata->TransferLength); FscUnmapBuffer(CacheBuffer); status = STATUS_SUCCESS; } CleanupAndExit: FatxReleaseVolumeMutex(VolumeExtension); return status; } NTSTATUS FatxFsdFileSystemControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is called by the I/O manager to handle IRP_MJ_FILE_SYSTEM_CONTROL requests. Arguments: DeviceObject - Specifies the device object that the I/O request is for. Irp - Specifies the packet that describes the I/O request. Return Value: Status of operation. --*/ { NTSTATUS status; PIO_STACK_LOCATION IrpSp; IrpSp = IoGetCurrentIrpStackLocation(Irp); switch (IrpSp->Parameters.FileSystemControl.FsControlCode) { case FSCTL_DISMOUNT_VOLUME: status = FatxDismountVolume(DeviceObject); break; case FSCTL_READ_VOLUME_METADATA: case FSCTL_WRITE_VOLUME_METADATA: status = FatxReadWriteVolumeMetadata(DeviceObject, Irp); break; default: status = STATUS_INVALID_DEVICE_REQUEST; break; } Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; }