|
|
/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
FsCtrl.c
Abstract:
This module implements the File System Control routines for Raw called by the dispatch driver.
Author:
David Goebel [DavidGoe] 18-Mar-91
Revision History:
--*/
#include "RawProcs.h"
//
// Local procedure prototypes
//
NTSTATUS RawMountVolume ( IN PIO_STACK_LOCATION IrpSp );
NTSTATUS RawVerifyVolume ( IN PIO_STACK_LOCATION IrpSp, IN PVCB Vcb );
NTSTATUS RawUserFsCtrl ( IN PIO_STACK_LOCATION IrpSp, IN PVCB Vcb );
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, RawMountVolume)
#pragma alloc_text(PAGE, RawUserFsCtrl)
#pragma alloc_text(PAGE, RawFileSystemControl)
#endif
NTSTATUS RawFileSystemControl ( IN PVCB Vcb, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp )
/*++
Routine Description:
This routine implements the FileSystem control operations
Arguments:
Vcb - Supplies the volume being queried.
Irp - Supplies the Irp being processed.
IrpSp - Supplies parameters describing the FileSystem control operation.
Return Value:
NTSTATUS - The status for the IRP
--*/
{ NTSTATUS Status;
PAGED_CODE();
//
// We know this is a file system control so we'll case on the
// minor function, and call an internal worker routine.
//
switch (IrpSp->MinorFunction) {
case IRP_MN_USER_FS_REQUEST:
Status = RawUserFsCtrl( IrpSp, Vcb ); break;
case IRP_MN_MOUNT_VOLUME:
Status = RawMountVolume( IrpSp ); break;
case IRP_MN_VERIFY_VOLUME:
Status = RawVerifyVolume( IrpSp, Vcb ); break;
default:
Status = STATUS_INVALID_DEVICE_REQUEST; break; }
RawCompleteRequest( Irp, Status );
return Status; }
//
// Local Support Routine
//
NTSTATUS RawMountVolume ( IN PIO_STACK_LOCATION IrpSp )
/*++
Routine Description:
This routine performs the mount volume operation.
Arguments:
IrpSp - Supplies the IrpSp parameters to process
Return Value:
NTSTATUS - The return status for the operation
--*/
{ NTSTATUS Status; PDEVICE_OBJECT DeviceObjectWeTalkTo;
PVOLUME_DEVICE_OBJECT VolumeDeviceObject;
PAGED_CODE();
//
// Save some references to make our life a little easier
//
DeviceObjectWeTalkTo = IrpSp->Parameters.MountVolume.DeviceObject;
//
// A mount operation has been requested. Create a
// new device object to represent this volume.
//
Status = IoCreateDevice( IrpSp->DeviceObject->DriverObject, sizeof(VOLUME_DEVICE_OBJECT) - sizeof(DEVICE_OBJECT), NULL, FILE_DEVICE_DISK_FILE_SYSTEM, 0, FALSE, (PDEVICE_OBJECT *)&VolumeDeviceObject );
if ( !NT_SUCCESS( Status ) ) {
return Status; }
//
// Our alignment requirement is the larger of the processor alignment requirement
// already in the volume device object and that in the DeviceObjectWeTalkTo
//
if (DeviceObjectWeTalkTo->AlignmentRequirement > VolumeDeviceObject->DeviceObject.AlignmentRequirement) {
VolumeDeviceObject->DeviceObject.AlignmentRequirement = DeviceObjectWeTalkTo->AlignmentRequirement; }
//
// Set sector size to the same value as the DeviceObjectWeTalkTo.
//
VolumeDeviceObject->DeviceObject.SectorSize = DeviceObjectWeTalkTo->SectorSize;
VolumeDeviceObject->DeviceObject.Flags |= DO_DIRECT_IO;
//
// Initialize the Vcb for this volume
//
Status = RawInitializeVcb( &VolumeDeviceObject->Vcb, IrpSp->Parameters.MountVolume.DeviceObject, IrpSp->Parameters.MountVolume.Vpb );
if ( !NT_SUCCESS( Status ) ) {
//
// Unlike the other points of teardown we do not need to deref the target device
// a iosubsys will automatically do that for a failed mount
//
IoDeleteDevice( (PDEVICE_OBJECT)VolumeDeviceObject ); return Status; }
//
// Finally, make it look as if the volume has been
// mounted. This includes storing the
// address of this file system's device object (the one
// that was created to handle this volume) in the VPB so
// all requests are directed to this file system from
// now until the volume is initialized with a real file
// structure.
//
VolumeDeviceObject->Vcb.Vpb->DeviceObject = (PDEVICE_OBJECT)VolumeDeviceObject; VolumeDeviceObject->Vcb.Vpb->SerialNumber = 0xFFFFFFFF; VolumeDeviceObject->Vcb.Vpb->VolumeLabelLength = 0;
VolumeDeviceObject->DeviceObject.Flags &= ~DO_DEVICE_INITIALIZING; VolumeDeviceObject->DeviceObject.StackSize = (UCHAR) (DeviceObjectWeTalkTo->StackSize + 1);
{ PFILE_OBJECT VolumeFileObject = NULL;
//
// We need a file object to do the notification.
//
try { VolumeFileObject = IoCreateStreamFileObjectLite( NULL, &VolumeDeviceObject->DeviceObject ); } except (EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); }
if (!NT_SUCCESS(Status)) { IoDeleteDevice( (PDEVICE_OBJECT)VolumeDeviceObject ); return Status; }
//
// We need to bump the count up 2 now so that the close we do in a few lines
// doesn't make the Vcb go away now.
//
VolumeDeviceObject->Vcb.OpenCount += 2; FsRtlNotifyVolumeEvent( VolumeFileObject, FSRTL_VOLUME_MOUNT ); ObDereferenceObject( VolumeFileObject );
//
// Okay, the close is over, now we can safely decrement the open count again
// (back to 0) so the Vcb can go away when we're really done with it.
//
VolumeDeviceObject->Vcb.OpenCount -= 2; } return Status; }
//
// Local Support Routine
//
NTSTATUS RawVerifyVolume ( IN PIO_STACK_LOCATION IrpSp, IN PVCB Vcb )
/*++
Routine Description:
This routine verifies a volume.
Arguments:
IrpSp - Supplies the IrpSp parameters to process
Return Value:
NTSTATUS - The return status for the operation
--*/
{ NTSTATUS Status; BOOLEAN DeleteVolume = FALSE; KIRQL Irql; PVPB vpb; BOOLEAN Mounted;
//
// If the volume is somehow stale, dismount. We must synchronize
// our inspection of the close count so we don't rip the volume up
// while racing with a close, for instance. The VPB refcount drops
// *before* the close comes into the filesystem.
//
//
// By this time its possible that the volume has been dismounted by
// RawClose. So check if its mounted. If so, take a reference on the VPB
// The reference on the VPB will prevent close from deleting the device.
//
IoAcquireVpbSpinLock(&Irql);
Mounted = FALSE; vpb = IrpSp->Parameters.VerifyVolume.Vpb; if (vpb->Flags & VPB_MOUNTED) { vpb->ReferenceCount++; Mounted = TRUE; }
IoReleaseVpbSpinLock(Irql);
if (!Mounted) { return STATUS_WRONG_VOLUME; }
Status = KeWaitForSingleObject( &Vcb->Mutex, Executive, KernelMode, FALSE, (PLARGE_INTEGER) NULL ); ASSERT( NT_SUCCESS( Status ) );
//
// Since we ignore all verify errors from the disk driver itself,
// this request must have originated from a file system, thus
// since we weren't the originators, we're going to say this isn't
// our volume, and if the open count is zero, dismount the volume.
//
IoAcquireVpbSpinLock(&Irql); vpb->ReferenceCount--; IoReleaseVpbSpinLock(Irql);
Vcb->Vpb->RealDevice->Flags &= ~DO_VERIFY_VOLUME;
if (Vcb->OpenCount == 0) {
DeleteVolume = RawCheckForDismount( Vcb, FALSE ); }
if (!DeleteVolume) { (VOID)KeReleaseMutex( &Vcb->Mutex, FALSE ); }
return STATUS_WRONG_VOLUME; }
//
// Local Support Routine
//
NTSTATUS RawUserFsCtrl ( IN PIO_STACK_LOCATION IrpSp, IN PVCB Vcb )
/*++
Routine Description:
This is the common routine for implementing the user's requests made through NtFsControlFile.
Arguments:
IrpSp - Supplies the IrpSp parameters to process
Vcb - Supplies the volume we are working on.
Return Value:
NTSTATUS - The return status for the operation
--*/
{ NTSTATUS Status; ULONG FsControlCode; PFILE_OBJECT FileObject;
PAGED_CODE();
FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode; FileObject = IrpSp->FileObject;
//
// Do pre-notification before entering the volume mutex so that we
// can be reentered by good threads cleaning up their resources.
//
switch (FsControlCode) { case FSCTL_LOCK_VOLUME: FsRtlNotifyVolumeEvent( FileObject, FSRTL_VOLUME_LOCK ); break;
case FSCTL_DISMOUNT_VOLUME:
FsRtlNotifyVolumeEvent( FileObject, FSRTL_VOLUME_DISMOUNT ); break;
default: break; } Status = KeWaitForSingleObject( &Vcb->Mutex, Executive, KernelMode, FALSE, (PLARGE_INTEGER) NULL ); ASSERT( NT_SUCCESS( Status ) );
switch ( FsControlCode ) {
case FSCTL_REQUEST_OPLOCK_LEVEL_1: case FSCTL_REQUEST_OPLOCK_LEVEL_2: case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE: case FSCTL_OPLOCK_BREAK_NOTIFY:
Status = STATUS_NOT_IMPLEMENTED; break;
case FSCTL_LOCK_VOLUME:
if ( !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_LOCKED) && (Vcb->OpenCount == 1) ) {
Vcb->VcbState |= VCB_STATE_FLAG_LOCKED;
Status = STATUS_SUCCESS;
} else {
Status = STATUS_ACCESS_DENIED; }
break;
case FSCTL_UNLOCK_VOLUME:
if ( !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_LOCKED) ) {
Status = STATUS_NOT_LOCKED;
} else {
Vcb->VcbState &= ~VCB_STATE_FLAG_LOCKED;
Status = STATUS_SUCCESS; }
break;
case FSCTL_DISMOUNT_VOLUME:
//
// Right now the logic in cleanup.c assumes that there can
// only be one handle on the volume if locked. The code
// there needs to be fixed if forced dismounts are allowed.
//
if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_LOCKED)) {
Vcb->VcbState |= VCB_STATE_FLAG_DISMOUNTED; Status = STATUS_SUCCESS;
} else {
Status = STATUS_ACCESS_DENIED; }
break;
default:
Status = STATUS_INVALID_PARAMETER; break; }
(VOID)KeReleaseMutex( &Vcb->Mutex, FALSE );
//
// Now perform post-notification as required.
//
if (NT_SUCCESS( Status )) { switch ( FsControlCode ) { case FSCTL_UNLOCK_VOLUME:
FsRtlNotifyVolumeEvent( FileObject, FSRTL_VOLUME_UNLOCK ); break; default: break; } } else { switch ( FsControlCode ) { case FSCTL_LOCK_VOLUME: FsRtlNotifyVolumeEvent( FileObject, FSRTL_VOLUME_LOCK_FAILED ); break;
case FSCTL_DISMOUNT_VOLUME:
FsRtlNotifyVolumeEvent( FileObject, FSRTL_VOLUME_DISMOUNT_FAILED ); break;
default: break; } }
return Status; }
|