722 lines
21 KiB
C
722 lines
21 KiB
C
/*++
|
||
|
||
Copyright (c) 1991 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
Pnp.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the Pnp routines for Ntfs called by the
|
||
dispatch driver.
|
||
|
||
Author:
|
||
|
||
Gary Kimura [GaryKi] 29-Aug-1991
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "NtfsProc.h"
|
||
|
||
//
|
||
// The Bug check file id for this module
|
||
//
|
||
|
||
#define BugCheckFileId (NTFS_BUG_CHECK_PNP)
|
||
|
||
//
|
||
// The local debug trace level
|
||
//
|
||
|
||
#define Dbg (DEBUG_TRACE_PNP)
|
||
|
||
//
|
||
// Local procedure prototypes
|
||
//
|
||
|
||
NTSTATUS
|
||
NtfsCommonPnp (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP *Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
NtfsPnpCompletionRoutine (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PNTFS_COMPLETION_CONTEXT CompletionContext
|
||
);
|
||
|
||
VOID
|
||
NtfsPerformSurpriseRemoval(
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVCB Vcb
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, NtfsCommonPnp)
|
||
#pragma alloc_text(PAGE, NtfsFsdPnp)
|
||
#pragma alloc_text(PAGE, NtfsPerformSurpriseRemoval)
|
||
#endif
|
||
|
||
NTSTATUS
|
||
NtfsFsdPnp (
|
||
IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine implements the FSD entry point for plug and play (Pnp).
|
||
|
||
Arguments:
|
||
|
||
VolumeDeviceObject - Supplies the volume device object where the
|
||
file exists
|
||
|
||
Irp - Supplies the Irp being processed
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The FSD status for the IRP
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
TOP_LEVEL_CONTEXT TopLevelContext;
|
||
PTOP_LEVEL_CONTEXT ThreadTopLevelContext;
|
||
|
||
PIRP_CONTEXT IrpContext = NULL;
|
||
|
||
ASSERT_IRP( Irp );
|
||
|
||
UNREFERENCED_PARAMETER( VolumeDeviceObject );
|
||
|
||
#ifdef NTFSPNPDBG
|
||
if (NtfsDebugTraceLevel != 0) SetFlag( NtfsDebugTraceLevel, DEBUG_TRACE_PNP );
|
||
#endif
|
||
|
||
DebugTrace( +1, Dbg, ("NtfsFsdPnp\n") );
|
||
|
||
//
|
||
// Call the common Pnp routine
|
||
//
|
||
|
||
FsRtlEnterFileSystem();
|
||
|
||
switch( IoGetCurrentIrpStackLocation( Irp )->MinorFunction ) {
|
||
|
||
case IRP_MN_QUERY_REMOVE_DEVICE:
|
||
case IRP_MN_REMOVE_DEVICE:
|
||
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
||
case IRP_MN_SURPRISE_REMOVAL:
|
||
|
||
ThreadTopLevelContext = NtfsInitializeTopLevelIrp( &TopLevelContext, FALSE, FALSE );
|
||
break;
|
||
|
||
default:
|
||
|
||
ThreadTopLevelContext = NtfsInitializeTopLevelIrp( &TopLevelContext, TRUE, TRUE );
|
||
break;
|
||
}
|
||
|
||
do {
|
||
|
||
try {
|
||
|
||
//
|
||
// We are either initiating this request or retrying it.
|
||
//
|
||
|
||
if (IrpContext == NULL) {
|
||
|
||
//
|
||
// Allocate and initialize the Irp.
|
||
//
|
||
|
||
NtfsInitializeIrpContext( Irp, TRUE, &IrpContext );
|
||
|
||
//
|
||
// Initialize the thread top level structure, if needed.
|
||
//
|
||
|
||
NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext );
|
||
|
||
} else if (Status == STATUS_LOG_FILE_FULL) {
|
||
|
||
NtfsCheckpointForLogFileFull( IrpContext );
|
||
}
|
||
|
||
Status = NtfsCommonPnp( IrpContext, &Irp );
|
||
break;
|
||
|
||
} except(NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) {
|
||
|
||
//
|
||
// We had some trouble trying to perform the requested
|
||
// operation, so we'll abort the I/O request with
|
||
// the error status that we get back from the
|
||
// execption code
|
||
//
|
||
|
||
Status = NtfsProcessException( IrpContext, Irp, GetExceptionCode() );
|
||
}
|
||
|
||
} while (Status == STATUS_CANT_WAIT ||
|
||
Status == STATUS_LOG_FILE_FULL);
|
||
|
||
ASSERT( IoGetTopLevelIrp() != (PIRP) &TopLevelContext );
|
||
FsRtlExitFileSystem();
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
DebugTrace( -1, Dbg, ("NtfsFsdPnp -> %08lx\n", Status) );
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NtfsCommonPnp (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP *Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the common routine for PnP called by the fsd thread.
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the Irp to process. WARNING! THIS IRP HAS NO
|
||
FILE OBJECT IN OUR IRP STACK LOCATION!!!
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The return status for the operation
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
NTSTATUS FlushStatus;
|
||
PIO_STACK_LOCATION IrpSp;
|
||
NTFS_COMPLETION_CONTEXT CompletionContext;
|
||
PVOLUME_DEVICE_OBJECT OurDeviceObject;
|
||
|
||
PVCB Vcb;
|
||
BOOLEAN VcbAcquired = FALSE;
|
||
BOOLEAN CheckpointAcquired = FALSE;
|
||
BOOLEAN DecrementCloseCount = FALSE;
|
||
|
||
#ifdef SYSCACHE_DEBUG
|
||
ULONG SystemHandleCount = 0;
|
||
#endif
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
ASSERT_IRP( *Irp );
|
||
ASSERT( FlagOn( IrpContext->TopLevelIrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL ));
|
||
|
||
//
|
||
// Get the current Irp stack location.
|
||
//
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( *Irp );
|
||
|
||
//
|
||
// Find our Vcb. This is tricky since we have no file object in the Irp.
|
||
//
|
||
|
||
OurDeviceObject = (PVOLUME_DEVICE_OBJECT) IrpSp->DeviceObject;
|
||
|
||
//
|
||
// Make sure this device object really is big enough to be a volume device
|
||
// object. If it isn't, we need to get out before we try to reference some
|
||
// field that takes us past the end of an ordinary device object. Then we
|
||
// check if it is actually one of ours, just to be perfectly paranoid.
|
||
//
|
||
|
||
if (OurDeviceObject->DeviceObject.Size != sizeof(VOLUME_DEVICE_OBJECT) ||
|
||
NodeType(&OurDeviceObject->Vcb) != NTFS_NTC_VCB) {
|
||
|
||
NtfsCompleteRequest( IrpContext, *Irp, STATUS_INVALID_PARAMETER );
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
Vcb = &OurDeviceObject->Vcb;
|
||
KeInitializeEvent( &CompletionContext.Event, NotificationEvent, FALSE );
|
||
|
||
//
|
||
// Anyone who is flushing the volume or setting Vcb bits needs to get the
|
||
// vcb exclusively.
|
||
//
|
||
|
||
switch ( IrpSp->MinorFunction ) {
|
||
|
||
case IRP_MN_QUERY_REMOVE_DEVICE:
|
||
case IRP_MN_SURPRISE_REMOVAL:
|
||
|
||
//
|
||
// Lock volume / dismount synchs with checkpoint - we need to do this first before
|
||
// acquiring the vcb to preserve locking order since we're going to do a lock in
|
||
// the query remove case and a dismount in the surprise removal
|
||
//
|
||
|
||
NtfsAcquireCheckpointSynchronization( IrpContext, Vcb );
|
||
CheckpointAcquired = TRUE;
|
||
|
||
// fall through
|
||
|
||
case IRP_MN_REMOVE_DEVICE:
|
||
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
||
|
||
NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE );
|
||
VcbAcquired = TRUE;
|
||
break;
|
||
}
|
||
|
||
try {
|
||
|
||
switch ( IrpSp->MinorFunction ) {
|
||
|
||
case IRP_MN_QUERY_REMOVE_DEVICE:
|
||
|
||
DebugTrace( 0, Dbg, ("IRP_MN_QUERY_REMOVE_DEVICE\n") );
|
||
|
||
if (!FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
|
||
|
||
Status = STATUS_VOLUME_DISMOUNTED;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// If we already know we don't want to dismount this volume, don't bother
|
||
// flushing now. If there's a nonzero cleanup count, flushing won't get
|
||
// the close count down to zero, so we might as well get out now.
|
||
//
|
||
|
||
#ifdef SYSCACHE_DEBUG
|
||
if (Vcb->SyscacheScb != NULL) {
|
||
SystemHandleCount = Vcb->SyscacheScb->CleanupCount;
|
||
}
|
||
if ((Vcb->CleanupCount > SystemHandleCount) ||
|
||
#else
|
||
if ((Vcb->CleanupCount > 0) ||
|
||
#endif
|
||
FlagOn(Vcb->VcbState, VCB_STATE_DISALLOW_DISMOUNT)) {
|
||
|
||
DebugTrace( 0, Dbg, ("IRP_MN_QUERY_REMOVE_DEVICE --> cleanup count still %x \n", Vcb->CleanupCount) );
|
||
|
||
//
|
||
// We don't want the device to get removed or stopped if this volume has files
|
||
// open. We'll fail this query, and we won't bother calling the driver(s) below us.
|
||
//
|
||
|
||
Status = STATUS_UNSUCCESSFUL;
|
||
|
||
} else {
|
||
|
||
//
|
||
// We might dismount this volume soon, so let's try to flush and purge
|
||
// everything we can right now.
|
||
//
|
||
|
||
FlushStatus = NtfsFlushVolume( IrpContext,
|
||
Vcb,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE,
|
||
FALSE );
|
||
|
||
//
|
||
// We need to make sure the cache manager is done with any lazy writes
|
||
// that might be keeping the close count up. Since Cc might need to
|
||
// close some streams, we need to release the vcb. We'd hate to have
|
||
// the Vcb go away, so we'll bias the close count temporarily.
|
||
//
|
||
|
||
Vcb->CloseCount += 1;
|
||
DecrementCloseCount = TRUE;
|
||
|
||
NtfsReleaseVcb( IrpContext, Vcb );
|
||
CcWaitForCurrentLazyWriterActivity();
|
||
NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE );
|
||
|
||
Vcb->CloseCount -= 1;
|
||
DecrementCloseCount = FALSE;
|
||
|
||
//
|
||
// Since we dropped the Vcb, we need to redo any tests we've done.
|
||
//
|
||
|
||
if (!FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
|
||
|
||
Status = STATUS_VOLUME_DISMOUNTED;
|
||
break;
|
||
}
|
||
|
||
|
||
#ifdef SYSCACHE_DEBUG
|
||
if (Vcb->SyscacheScb != NULL) {
|
||
SystemHandleCount = Vcb->SyscacheScb->CleanupCount;
|
||
}
|
||
|
||
if ((Vcb->CleanupCount > SystemHandleCount) ||
|
||
#else
|
||
if ((Vcb->CleanupCount > 0) ||
|
||
#endif
|
||
FlagOn(Vcb->VcbState, VCB_STATE_DISALLOW_DISMOUNT)) {
|
||
|
||
Status = STATUS_UNSUCCESSFUL;
|
||
break;
|
||
}
|
||
|
||
if ((Vcb->CloseCount - (Vcb->SystemFileCloseCount + Vcb->QueuedCloseCount)) > 0) {
|
||
|
||
DebugTrace( 0, Dbg, ("IRP_MN_QUERY_REMOVE_DEVICE --> %x user files still open \n", (Vcb->CloseCount - Vcb->SystemFileCloseCount)) );
|
||
|
||
//
|
||
// We don't want the device to get removed or stopped if this volume has files
|
||
// open. We'll fail this query, and we won't bother calling the driver(s) below us.
|
||
//
|
||
|
||
Status = STATUS_UNSUCCESSFUL;
|
||
|
||
} else {
|
||
|
||
//
|
||
// We've already done all we can to clear up any open files, so there's
|
||
// no point in retrying if this lock volume fails. We'll just tell
|
||
// NtfsLockVolumeInternal we're already retrying.
|
||
//
|
||
|
||
ULONG Retrying = 1;
|
||
|
||
DebugTrace( 0, Dbg, ("IRP_MN_QUERY_REMOVE_DEVICE --> No user files, Locking volume \n") );
|
||
|
||
Status = NtfsLockVolumeInternal( IrpContext,
|
||
Vcb,
|
||
((PFILE_OBJECT) 1),
|
||
&Retrying );
|
||
|
||
//
|
||
// Remember not to send any irps to the target device now.
|
||
//
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
ASSERT_EXCLUSIVE_RESOURCE( &Vcb->Resource );
|
||
SetFlag( Vcb->VcbState, VCB_STATE_TARGET_DEVICE_STOPPED );
|
||
}
|
||
}
|
||
}
|
||
|
||
break;
|
||
|
||
case IRP_MN_REMOVE_DEVICE:
|
||
|
||
DebugTrace( 0, Dbg, ("IRP_MN_REMOVE_DEVICE\n") );
|
||
|
||
//
|
||
// If remove_device is preceded by query_remove, we treat this just
|
||
// like a cancel_remove and unlock the volume and pass the irp to
|
||
// the driver(s) below the filesystem.
|
||
//
|
||
|
||
if (FlagOn( Vcb->VcbState, VCB_STATE_EXPLICIT_LOCK )) {
|
||
|
||
DebugTrace( 0, Dbg, ("IRP_MN_REMOVE_DEVICE --> Volume locked \n") );
|
||
Status = NtfsUnlockVolumeInternal( IrpContext, Vcb );
|
||
|
||
} else {
|
||
|
||
//
|
||
// The only other possibility is for remove_device to be prededed
|
||
// by surprise_remove, in which case we treat this as a failed verify.
|
||
//
|
||
|
||
// **** TODO **** ADD CODE TO TREAT THIS LIKE A FAILED VERIFY
|
||
|
||
DebugTrace( 0, Dbg, ("IRP_MN_REMOVE_DEVICE --> Volume _not_ locked \n") );
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
|
||
break;
|
||
|
||
case IRP_MN_SURPRISE_REMOVAL:
|
||
|
||
DebugTrace( 0, Dbg, ("IRP_MN_SURPRISE_REMOVAL\n") );
|
||
|
||
//
|
||
// For surprise removal, we call the driver(s) below us first, then do
|
||
// our processing. Let us also remember that we can't send any more
|
||
// IRPs to the target device.
|
||
//
|
||
|
||
SetFlag( Vcb->VcbState, VCB_STATE_TARGET_DEVICE_STOPPED );
|
||
|
||
Status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
||
|
||
Status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
default:
|
||
|
||
DebugTrace( 0, Dbg, ("Some other PnP IRP_MN_ %x\n", IrpSp->MinorFunction) );
|
||
Status = STATUS_SUCCESS;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// We only pass this irp down if we didn't have some reason to fail it ourselves.
|
||
// We want to keep the IrpContext around for our own cleanup.
|
||
//
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
|
||
NtfsCompleteRequest( NULL, *Irp, Status );
|
||
try_return( NOTHING );
|
||
}
|
||
|
||
//
|
||
// Get the next stack location, and copy over the stack location
|
||
//
|
||
|
||
IoCopyCurrentIrpStackLocationToNext( *Irp );
|
||
|
||
//
|
||
// Set up the completion routine
|
||
//
|
||
|
||
CompletionContext.IrpContext = IrpContext;
|
||
IoSetCompletionRoutine( *Irp,
|
||
NtfsPnpCompletionRoutine,
|
||
&CompletionContext,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE );
|
||
|
||
//
|
||
// Send the request to the driver(s) below us. - We don't own it anymore
|
||
// so null it out
|
||
//
|
||
|
||
Status = IoCallDriver( Vcb->TargetDeviceObject, *Irp );
|
||
*Irp = NULL;
|
||
|
||
//
|
||
// Wait for the driver to definitely complete
|
||
//
|
||
|
||
if (Status == STATUS_PENDING) {
|
||
|
||
KeWaitForSingleObject( &CompletionContext.Event,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL );
|
||
KeClearEvent( &CompletionContext.Event );
|
||
}
|
||
|
||
//
|
||
// Post processing - these are items that need to be done after the lower
|
||
// storage stack has processed the request.
|
||
//
|
||
|
||
switch (IrpContext->MinorFunction) {
|
||
|
||
case IRP_MN_SURPRISE_REMOVAL:
|
||
|
||
//
|
||
// Start the tear-down process irrespective of the status
|
||
// the driver below us sent back. There's no turning back here.
|
||
//
|
||
|
||
if (FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
|
||
NtfsPerformSurpriseRemoval( IrpContext, Vcb );
|
||
}
|
||
break;
|
||
|
||
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
||
|
||
//
|
||
// Since we cancelled and have told the driver we can now safely unlock
|
||
// the volume and send ioctls to the drive (unlock media)
|
||
//
|
||
|
||
ClearFlag( Vcb->VcbState, VCB_STATE_TARGET_DEVICE_STOPPED );
|
||
|
||
if (FlagOn( Vcb->VcbState, VCB_STATE_EXPLICIT_LOCK )) {
|
||
|
||
DebugTrace( 0, Dbg, ("IRP_MN_CANCEL_REMOVE_DEVICE --> Volume locked \n") );
|
||
NtfsUnlockVolumeInternal( IrpContext, Vcb );
|
||
|
||
}
|
||
break;
|
||
}
|
||
|
||
try_exit: NOTHING;
|
||
} finally {
|
||
|
||
if (DecrementCloseCount) {
|
||
|
||
if (!VcbAcquired) {
|
||
|
||
NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE );
|
||
VcbAcquired = TRUE;
|
||
}
|
||
|
||
Vcb->CloseCount -= 1;
|
||
}
|
||
|
||
if (VcbAcquired) {
|
||
|
||
//
|
||
// All 4 paths query / remove / surprise remove / cancel remove
|
||
// come through here. For the 3 except query we want the vcb to go away
|
||
// if possible. In the query remove path - dismount won't be complete
|
||
// even if the close count is 0 (since the dismount is incomplete)
|
||
// so this will only release
|
||
//
|
||
|
||
NtfsReleaseVcbCheckDelete( IrpContext, Vcb, IrpContext->MajorFunction, NULL );
|
||
}
|
||
|
||
if (CheckpointAcquired) {
|
||
NtfsReleaseCheckpointSynchronization( IrpContext, Vcb );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Cleanup our IrpContext; The underlying driver completed the Irp.
|
||
//
|
||
|
||
DebugTrace( -1, Dbg, ("NtfsCommonPnp -> %08lx\n", Status ) );
|
||
NtfsCompleteRequest( IrpContext, NULL, Status );
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
NTSTATUS
|
||
NtfsPnpCompletionRoutine (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PNTFS_COMPLETION_CONTEXT CompletionContext
|
||
)
|
||
{
|
||
PIO_STACK_LOCATION IrpSp;
|
||
PIRP_CONTEXT IrpContext;
|
||
PVOLUME_DEVICE_OBJECT OurDeviceObject;
|
||
|
||
PVCB Vcb;
|
||
BOOLEAN VcbAcquired = FALSE;
|
||
|
||
ASSERT_IRP( Irp );
|
||
|
||
IrpContext = CompletionContext->IrpContext;
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
|
||
//
|
||
// Get the current Irp stack location.
|
||
//
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
//
|
||
// Find our Vcb. This is tricky since we have no file object in the Irp.
|
||
//
|
||
|
||
OurDeviceObject = (PVOLUME_DEVICE_OBJECT) DeviceObject;
|
||
|
||
//
|
||
// Make sure this device object really is big enough to be a volume device
|
||
// object. If it isn't, we need to get out before we try to reference some
|
||
// field that takes us past the end of an ordinary device object. Then we
|
||
// check if it is actually one of ours, just to be perfectly paranoid.
|
||
//
|
||
|
||
if (OurDeviceObject->DeviceObject.Size != sizeof(VOLUME_DEVICE_OBJECT) ||
|
||
NodeType(&OurDeviceObject->Vcb) != NTFS_NTC_VCB) {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
Vcb = &OurDeviceObject->Vcb;
|
||
|
||
KeSetEvent( &CompletionContext->Event, 0, FALSE );
|
||
|
||
//
|
||
// Propagate the Irp pending state.
|
||
//
|
||
|
||
if (Irp->PendingReturned) {
|
||
|
||
IoMarkIrpPending( Irp );
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Local utility routine
|
||
//
|
||
|
||
VOID
|
||
NtfsPerformSurpriseRemoval (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVCB Vcb
|
||
)
|
||
|
||
/*++
|
||
|
||
Performs further processing on SURPRISE_REMOVAL notifications.
|
||
|
||
--*/
|
||
|
||
{
|
||
ASSERT(ExIsResourceAcquiredExclusiveLite( &Vcb->Resource ));
|
||
|
||
//
|
||
// Flush and purge and mark all files as dismounted.
|
||
// Since there may be outstanding handles, we could still see any
|
||
// operation (read, write, set info, etc.) happen for files on the
|
||
// volume after surprise_remove. Since all the files will be marked
|
||
// for dismount, we will fail these operations gracefully. All
|
||
// operations besides cleanup & close on the volume will fail from
|
||
// this time on.
|
||
//
|
||
|
||
if (!FlagOn( Vcb->VcbState, VCB_STATE_DISALLOW_DISMOUNT )) {
|
||
|
||
(VOID)NtfsFlushVolume( IrpContext,
|
||
Vcb,
|
||
FALSE,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE );
|
||
|
||
NtfsPerformDismountOnVcb( IrpContext, Vcb, TRUE, NULL );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|