|
|
/*++
Copyright (c) 1989-2000 Microsoft Corporation
Module Name:
Close.c
Abstract:
This module implements the File Close routine for Fat called by the dispatch driver.
// @@BEGIN_DDKSPLIT
Author:
Gary Kimura [GaryKi] 28-Dec-1989
Revision History:
// @@END_DDKSPLIT
--*/
#include "FatProcs.h"
//
// The Bug check file id for this module
//
#define BugCheckFileId (FAT_BUG_CHECK_CLOSE)
//
// The local debug trace level
//
#define Dbg (DEBUG_TRACE_CLOSE)
ULONG FatMaxDelayedCloseCount;
#define FatAcquireCloseMutex() { \
ASSERT(KeAreApcsDisabled()); \ ExAcquireFastMutexUnsafe( &FatCloseQueueMutex ); \ }
#define FatReleaseCloseMutex() { \
ASSERT(KeAreApcsDisabled()); \ ExReleaseFastMutexUnsafe( &FatCloseQueueMutex ); \ }
//
// Local procedure prototypes
//
VOID FatQueueClose ( IN PCLOSE_CONTEXT CloseContext, IN BOOLEAN DelayClose );
PCLOSE_CONTEXT FatRemoveClose ( PVCB Vcb OPTIONAL, PVCB LastVcbHint OPTIONAL );
VOID FatCloseWorker ( IN PDEVICE_OBJECT DeviceObject, IN PVOID Context );
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, FatFsdClose)
#pragma alloc_text(PAGE, FatFspClose)
#pragma alloc_text(PAGE, FatCommonClose)
#pragma alloc_text(PAGE, FatCloseWorker)
#endif
NTSTATUS FatFsdClose ( IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, IN PIRP Irp )
/*++
Routine Description:
This routine implements the FSD part of Close.
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; PIO_STACK_LOCATION IrpSp; PFILE_OBJECT FileObject;
PVCB Vcb; PFCB Fcb; PCCB Ccb; TYPE_OF_OPEN TypeOfOpen;
BOOLEAN TopLevel;
//
// If we were called with our file system device object instead of a
// volume device object, just complete this request with STATUS_SUCCESS
//
if (FatDeviceIsFatFsdo( VolumeDeviceObject)) {
Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = FILE_OPENED;
IoCompleteRequest( Irp, IO_DISK_INCREMENT );
return STATUS_SUCCESS; }
DebugTrace(+1, Dbg, "FatFsdClose\n", 0);
//
// Call the common Close routine
//
FsRtlEnterFileSystem();
TopLevel = FatIsIrpTopLevel( Irp );
//
// Get a pointer to the current stack location and the file object
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
FileObject = IrpSp->FileObject;
//
// Decode the file object and set the read-only bit in the Ccb.
//
TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb );
if (Ccb && IsFileObjectReadOnly(FileObject)) {
SetFlag( Ccb->Flags, CCB_FLAG_READ_ONLY ); }
try {
PCLOSE_CONTEXT CloseContext;
//
// If we are top level, WAIT can be TRUE, otherwise make it FALSE
// to avoid deadlocks, unless this is a top
// level request not originating from the system process.
//
BOOLEAN Wait = TopLevel && (PsGetCurrentProcess() != FatData.OurProcess);
//
// Call the common Close routine if we are not delaying this close.
//
if ((((TypeOfOpen == UserFileOpen) || (TypeOfOpen == UserDirectoryOpen)) && FlagOn(Fcb->FcbState, FCB_STATE_DELAY_CLOSE) && !FatData.ShutdownStarted) || (FatCommonClose(Vcb, Fcb, Ccb, TypeOfOpen, Wait, NULL) == STATUS_PENDING)) {
//
// Metadata streams have had close contexts preallocated.
//
if (TypeOfOpen == VirtualVolumeFile || TypeOfOpen == DirectoryFile || TypeOfOpen == EaFile) {
CloseContext = FatAllocateCloseContext(); ASSERT( CloseContext != NULL ); CloseContext->Free = TRUE;
} else {
//
// Free up any query template strings before using the close context fields,
// which overlap (union)
//
FatDeallocateCcbStrings( Ccb);
CloseContext = &Ccb->CloseContext; CloseContext->Free = FALSE; SetFlag( Ccb->Flags, CCB_FLAG_CLOSE_CONTEXT ); }
//
// If the status is pending, then let's get the information we
// need into the close context we already have bagged, complete
// the request, and post it. It is important we allocate nothing
// in the close path.
//
CloseContext->Vcb = Vcb; CloseContext->Fcb = Fcb; CloseContext->TypeOfOpen = TypeOfOpen;
//
// Send it off, either to an ExWorkerThread or to the async
// close list.
//
FatQueueClose( CloseContext, (BOOLEAN)(Fcb && FlagOn(Fcb->FcbState, FCB_STATE_DELAY_CLOSE))); } else { //
// The close proceeded synchronously, so for the metadata objects we
// can now drop the close context we preallocated.
//
if (TypeOfOpen == VirtualVolumeFile || TypeOfOpen == DirectoryFile || TypeOfOpen == EaFile) {
CloseContext = FatAllocateCloseContext(); ASSERT( CloseContext != NULL );
ExFreePool( CloseContext ); }
}
FatCompleteRequest( FatNull, Irp, Status );
} except(FatExceptionFilter( NULL, 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 = FatProcessException( NULL, Irp, GetExceptionCode() ); }
if (TopLevel) { IoSetTopLevelIrp( NULL ); }
FsRtlExitFileSystem();
//
// And return to our caller
//
DebugTrace(-1, Dbg, "FatFsdClose -> %08lx\n", Status);
UNREFERENCED_PARAMETER( VolumeDeviceObject );
return Status; }
VOID FatCloseWorker ( IN PDEVICE_OBJECT DeviceObject, IN PVOID Context ) /*++
Routine Description:
This routine is a shim between the IO worker package and FatFspClose.
Arguments:
DeviceObject - Registration device object, unused Context - Context value, unused
Return Value:
None.
--*/ { FsRtlEnterFileSystem(); FatFspClose (Context); FsRtlExitFileSystem(); }
VOID FatFspClose ( IN PVCB Vcb OPTIONAL )
/*++
Routine Description:
This routine implements the FSP part of Close.
Arguments:
Vcb - If present, tells us to only close file objects opened on the specified volume.
Return Value:
None.
--*/
{ PCLOSE_CONTEXT CloseContext; PVCB CurrentVcb = NULL; PVCB LastVcb = NULL; BOOLEAN FreeContext;
ULONG LoopsWithVcbHeld; DebugTrace(+1, Dbg, "FatFspClose\n", 0);
//
// Set the top level IRP for the true FSP operation.
//
if (!ARGUMENT_PRESENT( Vcb )) { IoSetTopLevelIrp( (PIRP)FSRTL_FSP_TOP_LEVEL_IRP ); } while (CloseContext = FatRemoveClose(Vcb, LastVcb)) {
//
// If we are in the FSP (i.e. Vcb == NULL), then try to keep ahead of
// creates by doing several closes with one acquisition of the Vcb.
//
// Note that we cannot be holding the Vcb on entry to FatCommonClose
// if this is last close as we will try to acquire FatData, and
// worse the volume (and therefore the Vcb) may go away.
//
if (!ARGUMENT_PRESENT(Vcb)) { if (!FatData.ShutdownStarted) {
if (CloseContext->Vcb != CurrentVcb) {
LoopsWithVcbHeld = 0;
//
// Release a previously held Vcb, if any.
//
if (CurrentVcb != NULL) {
ExReleaseResourceLite( &CurrentVcb->Resource); }
//
// Get the new Vcb.
//
CurrentVcb = CloseContext->Vcb; (VOID)ExAcquireResourceExclusiveLite( &CurrentVcb->Resource, TRUE );
} else {
//
// Share the resource occasionally if we seem to be finding a lot
// of closes for a single volume.
//
if (++LoopsWithVcbHeld >= 20) {
if (ExGetSharedWaiterCount( &CurrentVcb->Resource ) + ExGetExclusiveWaiterCount( &CurrentVcb->Resource )) {
ExReleaseResourceLite( &CurrentVcb->Resource); (VOID)ExAcquireResourceExclusiveLite( &CurrentVcb->Resource, TRUE ); }
LoopsWithVcbHeld = 0; } }
//
// Now check the Open count. We may be about to delete this volume!
//
// The test below must be <= 1 because there could still be outstanding
// stream references on this VCB that are not counted in the OpenFileCount.
// For example if there are no open files OpenFileCount could be zero and we would
// not release the resource here. The call to FatCommonClose() below may cause
// the VCB to be torn down and we will try to release memory we don't
// own later.
//
if (CurrentVcb->OpenFileCount <= 1) {
ExReleaseResourceLite( &CurrentVcb->Resource); CurrentVcb = NULL; } //
// If shutdown has started while processing our list, drop the
// current Vcb resource.
//
} else if (CurrentVcb != NULL) {
ExReleaseResourceLite( &CurrentVcb->Resource); CurrentVcb = NULL; } }
LastVcb = CurrentVcb;
//
// Call the common Close routine. Protected in a try {} except {}
//
try {
//
// The close context either is in the CCB, automatically freed,
// or was from pool for a metadata fileobject, CCB is NULL, and
// we'll need to free it.
//
FreeContext = CloseContext->Free;
(VOID)FatCommonClose( CloseContext->Vcb, CloseContext->Fcb, (FreeContext ? NULL : CONTAINING_RECORD( CloseContext, CCB, CloseContext)), CloseContext->TypeOfOpen, TRUE, NULL );
} except(FatExceptionFilter( NULL, GetExceptionInformation() )) {
//
// Ignore anything we expect.
//
NOTHING; }
//
// Drop the context if it came from pool.
//
if (FreeContext) {
ExFreePool( CloseContext ); } }
//
// Release a previously held Vcb, if any.
//
if (CurrentVcb != NULL) {
ExReleaseResourceLite( &CurrentVcb->Resource); }
//
// Clean up the top level IRP hint if we owned it.
//
if (!ARGUMENT_PRESENT( Vcb )) { IoSetTopLevelIrp( NULL ); } //
// And return to our caller
//
DebugTrace(-1, Dbg, "FatFspClose -> NULL\n", 0);
return; }
VOID FatQueueClose ( IN PCLOSE_CONTEXT CloseContext, IN BOOLEAN DelayClose )
/*++
Routine Description:
Enqueue a deferred close to one of the two delayed close queues.
Arguments:
CloseContext - a close context to enqueue for the delayed close thread. DelayClose - whether this should go on the delayed close queue (unreferenced objects).
Return Value:
None.
--*/
{ BOOLEAN StartWorker = FALSE;
FatAcquireCloseMutex();
if (DelayClose) {
InsertTailList( &FatData.DelayedCloseList, &CloseContext->GlobalLinks ); InsertTailList( &CloseContext->Vcb->DelayedCloseList, &CloseContext->VcbLinks );
FatData.DelayedCloseCount += 1;
if ((FatData.DelayedCloseCount > FatMaxDelayedCloseCount) && !FatData.AsyncCloseActive) {
FatData.AsyncCloseActive = TRUE; StartWorker = TRUE; }
} else {
InsertTailList( &FatData.AsyncCloseList, &CloseContext->GlobalLinks ); InsertTailList( &CloseContext->Vcb->AsyncCloseList, &CloseContext->VcbLinks );
FatData.AsyncCloseCount += 1;
if (!FatData.AsyncCloseActive) {
FatData.AsyncCloseActive = TRUE; StartWorker = TRUE; } }
FatReleaseCloseMutex();
if (StartWorker) {
IoQueueWorkItem( FatData.FatCloseItem, FatCloseWorker, CriticalWorkQueue, NULL ); } }
PCLOSE_CONTEXT FatRemoveClose ( PVCB Vcb OPTIONAL, PVCB LastVcbHint OPTIONAL )
/*++
Routine Description:
Dequeue a deferred close from one of the two delayed close queues.
Arguments:
Vcb - if specified, only returns close for this volume. LastVcbHint - if specified and other starvation avoidance is required by the system condition, will attempt to return closes for this volume.
Return Value:
A close to perform.
--*/
{ PLIST_ENTRY Entry; PCLOSE_CONTEXT CloseContext; BOOLEAN WorkerThread;
FatAcquireCloseMutex();
//
// Remember if this is the worker thread, so we can pull down the active
// flag should we run everything out.
//
WorkerThread = (Vcb == NULL);
//
// If the queues are above the limits by a significant amount, we have
// to try hard to pull them down. To do this, we will aggresively try
// to find closes for the last volume the caller looked at. This will
// make sure we fully utilize the acquisition of the volume, which can
// be a hugely expensive resource to get (create/close/cleanup use it
// exclusively).
//
// Only do this in the delayed close thread. We will know this is the
// case by seeing a NULL mandatory Vcb.
//
if (Vcb == NULL && LastVcbHint != NULL) {
//
// Flip over to aggressive at twice the legal limit, and flip it
// off at the legal limit.
//
if (!FatData.HighAsync && FatData.AsyncCloseCount > FatMaxDelayedCloseCount*2) {
FatData.HighAsync = TRUE; } else if (FatData.HighAsync && FatData.AsyncCloseCount < FatMaxDelayedCloseCount) {
FatData.HighAsync = FALSE; } if (!FatData.HighDelayed && FatData.DelayedCloseCount > FatMaxDelayedCloseCount*2) {
FatData.HighDelayed = TRUE; } else if (FatData.HighDelayed && FatData.DelayedCloseCount < FatMaxDelayedCloseCount) {
FatData.HighDelayed = FALSE; }
if (FatData.HighAsync || FatData.HighDelayed) {
Vcb = LastVcbHint; } } //
// Do the case when we don't care about which Vcb the close is on.
// This is the case when we are in an ExWorkerThread and aren't
// under pressure.
//
if (Vcb == NULL) {
AnyClose:
//
// First check the list of async closes.
//
if (!IsListEmpty( &FatData.AsyncCloseList )) {
Entry = RemoveHeadList( &FatData.AsyncCloseList ); FatData.AsyncCloseCount -= 1;
CloseContext = CONTAINING_RECORD( Entry, CLOSE_CONTEXT, GlobalLinks );
RemoveEntryList( &CloseContext->VcbLinks );
//
// Do any delayed closes over half the limit, unless shutdown has
// started (then kill them all).
//
} else if (!IsListEmpty( &FatData.DelayedCloseList ) && (FatData.DelayedCloseCount > FatMaxDelayedCloseCount/2 || FatData.ShutdownStarted)) {
Entry = RemoveHeadList( &FatData.DelayedCloseList ); FatData.DelayedCloseCount -= 1;
CloseContext = CONTAINING_RECORD( Entry, CLOSE_CONTEXT, GlobalLinks );
RemoveEntryList( &CloseContext->VcbLinks );
//
// There are no more closes to perform; show that we are done.
//
} else {
CloseContext = NULL;
if (WorkerThread) { FatData.AsyncCloseActive = FALSE; } }
//
// We're running down a specific volume.
//
} else {
//
// First check the list of async closes.
//
if (!IsListEmpty( &Vcb->AsyncCloseList )) {
Entry = RemoveHeadList( &Vcb->AsyncCloseList ); FatData.AsyncCloseCount -= 1;
CloseContext = CONTAINING_RECORD( Entry, CLOSE_CONTEXT, VcbLinks );
RemoveEntryList( &CloseContext->GlobalLinks );
//
// Do any delayed closes.
//
} else if (!IsListEmpty( &Vcb->DelayedCloseList )) {
Entry = RemoveHeadList( &Vcb->DelayedCloseList ); FatData.DelayedCloseCount -= 1;
CloseContext = CONTAINING_RECORD( Entry, CLOSE_CONTEXT, VcbLinks ); RemoveEntryList( &CloseContext->GlobalLinks ); //
// If we were trying to run down the queues but didn't find anything for this
// volume, flip over to accept anything and try again.
//
} else if (LastVcbHint) {
goto AnyClose; //
// There are no more closes to perform; show that we are done.
//
} else {
CloseContext = NULL; } }
FatReleaseCloseMutex();
return CloseContext; }
NTSTATUS FatCommonClose ( IN PVCB Vcb, IN PFCB Fcb, IN PCCB Ccb, IN TYPE_OF_OPEN TypeOfOpen, IN BOOLEAN Wait, IN PVOLUME_DEVICE_OBJECT *VolDo OPTIONAL )
/*++
Routine Description:
This is the common routine for closing a file/directory called by both the fsd and fsp threads.
Close is invoked whenever the last reference to a file object is deleted. Cleanup is invoked when the last handle to a file object is closed, and is called before close.
The function of close is to completely tear down and remove the fcb/dcb/ccb structures associated with the file object.
Arguments:
Fcb - Supplies the file to process.
Wait - If this is TRUE we are allowed to block for the Vcb, if FALSE then we must try to acquire the Vcb anyway.
VolDo - This is really gross. If we are really in the Fsp, and a volume goes away. We need some way to NULL out the VolDo variable in FspDispatch().
Return Value:
NTSTATUS - The return status for the operation
--*/
{ NTSTATUS Status; PDCB ParentDcb; BOOLEAN RecursiveClose; IRP_CONTEXT IrpContext;
DebugTrace(+1, Dbg, "FatCommonClose...\n", 0);
//
// Special case the unopened file object
//
if (TypeOfOpen == UnopenedFileObject) {
DebugTrace(0, Dbg, "Close unopened file object\n", 0);
Status = STATUS_SUCCESS;
DebugTrace(-1, Dbg, "FatCommonClose -> %08lx\n", Status); return Status; }
//
// Set up our stack IrpContext.
//
RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT) );
if (Wait) {
SetFlag( IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT ); }
//
// Acquire exclusive access to the Vcb and enqueue the irp if we didn't
// get access.
//
if (!ExAcquireResourceExclusiveLite( &Vcb->Resource, Wait )) {
return STATUS_PENDING; }
//
// The following test makes sure that we don't blow away an Fcb if we
// are trying to do a Supersede/Overwrite open above us. This test
// does not apply for the EA file.
//
if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS) && Vcb->EaFcb != Fcb) {
ExReleaseResourceLite( &Vcb->Resource );
return STATUS_PENDING; }
//
// Setting the following flag prevents recursive closes of directory file
// objects, which are handled in a special case loop.
//
if ( FlagOn(Vcb->VcbState, VCB_STATE_FLAG_CLOSE_IN_PROGRESS) ) {
RecursiveClose = TRUE;
} else {
SetFlag(Vcb->VcbState, VCB_STATE_FLAG_CLOSE_IN_PROGRESS);
RecursiveClose = FALSE; }
//
// Synchronize here with other closes regarding volume deletion. Note
// that the Vcb->OpenFileCount can be safely incremented here without
// FatData synchronization for the following reasons:
//
// This counter only becomes relevant when (holding a spinlock):
//
// A: The Vcb->OpenFileCount is zero, and
// B: The Vpb->Refcount is the residual (2/3 for close/verify)
//
// For A to be true, there can be no more pending closes at this point
// in the close code. For B to be true, in close, there cannot be
// a create in process, and thus no verify in process.
//
// Also we only increment the count if this is a top level close.
//
if ( !RecursiveClose ) {
Vcb->OpenFileCount += 1; }
try {
//
// Case on the type of open that we are trying to close.
//
switch (TypeOfOpen) {
case VirtualVolumeFile:
DebugTrace(0, Dbg, "Close VirtualVolumeFile\n", 0);
try_return( Status = STATUS_SUCCESS ); break;
case UserVolumeOpen:
DebugTrace(0, Dbg, "Close UserVolumeOpen\n", 0);
Vcb->DirectAccessOpenCount -= 1; Vcb->OpenFileCount -= 1; if (FlagOn(Ccb->Flags, CCB_FLAG_READ_ONLY)) { Vcb->ReadOnlyCount -= 1; }
FatDeleteCcb( &IrpContext, Ccb );
try_return( Status = STATUS_SUCCESS ); break;
case EaFile:
DebugTrace(0, Dbg, "Close EaFile\n", 0);
try_return( Status = STATUS_SUCCESS ); break;
case DirectoryFile:
DebugTrace(0, Dbg, "Close DirectoryFile\n", 0);
InterlockedDecrement( &Fcb->Specific.Dcb.DirectoryFileOpenCount );
//
// If this is a recursive close, just return here.
//
if ( RecursiveClose ) {
try_return( Status = STATUS_SUCCESS );
} else {
break; }
case UserDirectoryOpen: case UserFileOpen:
DebugTrace(0, Dbg, "Close UserFileOpen/UserDirectoryOpen\n", 0);
//
// Uninitialize the cache map if we no longer need to use it
//
if ((NodeType(Fcb) == FAT_NTC_DCB) && IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue) && (Fcb->OpenCount == 1) && (Fcb->Specific.Dcb.DirectoryFile != NULL)) {
PFILE_OBJECT DirectoryFileObject = Fcb->Specific.Dcb.DirectoryFile;
DebugTrace(0, Dbg, "Uninitialize the stream file object\n", 0);
CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL );
//
// Dereference the directory file. This may cause a close
// Irp to be processed, so we need to do this before we destory
// the Fcb.
//
Fcb->Specific.Dcb.DirectoryFile = NULL; ObDereferenceObject( DirectoryFileObject ); }
Fcb->OpenCount -= 1; Vcb->OpenFileCount -= 1; if (FlagOn(Ccb->Flags, CCB_FLAG_READ_ONLY)) { Vcb->ReadOnlyCount -= 1; }
FatDeleteCcb( &IrpContext, Ccb );
break;
default:
FatBugCheck( TypeOfOpen, 0, 0 ); }
//
// At this point we've cleaned up any on-disk structure that needs
// to be done, and we can now update the in-memory structures.
// Now if this is an unreferenced FCB or if it is
// an unreferenced DCB (not the root) then we can remove
// the fcb and set our ParentDcb to non null.
//
if (((NodeType(Fcb) == FAT_NTC_FCB) && (Fcb->OpenCount == 0))
||
((NodeType(Fcb) == FAT_NTC_DCB) && (IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue)) && (Fcb->OpenCount == 0) && (Fcb->Specific.Dcb.DirectoryFileOpenCount == 0))) {
ParentDcb = Fcb->ParentDcb;
SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB );
FatDeleteFcb( &IrpContext, Fcb );
//
// Uninitialize our parent's cache map if we no longer need
// to use it.
//
while ((NodeType(ParentDcb) == FAT_NTC_DCB) && IsListEmpty(&ParentDcb->Specific.Dcb.ParentDcbQueue) && (ParentDcb->OpenCount == 0) && (ParentDcb->Specific.Dcb.DirectoryFile != NULL)) {
PFILE_OBJECT DirectoryFileObject;
DirectoryFileObject = ParentDcb->Specific.Dcb.DirectoryFile;
DebugTrace(0, Dbg, "Uninitialize our parent Stream Cache Map\n", 0);
CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL );
ParentDcb->Specific.Dcb.DirectoryFile = NULL;
ObDereferenceObject( DirectoryFileObject );
//
// Now, if the ObDereferenceObject() caused the final close
// to come in, then blow away the Fcb and continue up,
// otherwise wait for Mm to to dereference its file objects
// and stop here..
//
if ( ParentDcb->Specific.Dcb.DirectoryFileOpenCount == 0) {
PDCB CurrentDcb;
CurrentDcb = ParentDcb; ParentDcb = CurrentDcb->ParentDcb;
SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB );
FatDeleteFcb( &IrpContext, CurrentDcb );
} else {
break; } } }
Status = STATUS_SUCCESS;
try_exit: NOTHING; } finally {
DebugUnwind( FatCommonClose );
if ( !RecursiveClose ) {
ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_CLOSE_IN_PROGRESS ); }
//
// Check if we should delete the volume. Unfortunately, to correctly
// synchronize with verify, we can only unsafely check our own
// transition. This results in a little bit of extra overhead in the
// 1 -> 0 OpenFileCount transition.
//
// 2 is the residual Vpb->RefCount on a volume to be freed.
//
//
// Here is the deal with releasing the Vcb. We must be holding the
// Vcb when decrementing the Vcb->OpenFileCount. If we don't this
// could cause the decrement to mal-function on an MP system. But we
// want to be holding the Global resource exclusive when decrement
// the count so that nobody else will try to dismount the volume.
// However, because of locking rules, the Global resource must be
// acquired first, which is why we do what we do below.
//
if ( !RecursiveClose ) {
if ( Vcb->OpenFileCount == 1 ) {
PVPB Vpb = Vcb->Vpb;
SetFlag( IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT );
FatReleaseVcb( &IrpContext, Vcb );
(VOID)FatAcquireExclusiveGlobal( &IrpContext ); (VOID)FatAcquireExclusiveVcb( &IrpContext, Vcb );
Vcb->OpenFileCount -= 1;
FatReleaseVcb( &IrpContext, Vcb );
//
// We can now "safely" check OpenFileCount and VcbCondition.
// If they are OK, we will proceed to checking the
// Vpb Ref Count in FatCheckForDismount.
//
if ( (Vcb->OpenFileCount == 0) && ((Vcb->VcbCondition == VcbNotMounted) || (Vcb->VcbCondition == VcbBad) || FlagOn( Vcb->VcbState, VCB_STATE_FLAG_SHUTDOWN )) && FatCheckForDismount( &IrpContext, Vcb, FALSE ) ) {
//
// If this is not the Vpb "attached" to the device, free it.
//
if ((Vpb->RealDevice->Vpb != Vpb) && !FlagOn( Vpb->Flags, VPB_PERSISTENT)) {
ExFreePool( Vpb ); }
if (ARGUMENT_PRESENT(VolDo)) {
*VolDo = NULL; } }
FatReleaseGlobal( &IrpContext );
} else {
Vcb->OpenFileCount -= 1;
FatReleaseVcb( &IrpContext, Vcb ); }
} else {
FatReleaseVcb( &IrpContext, Vcb ); }
DebugTrace(-1, Dbg, "FatCommonClose -> %08lx\n", Status); }
return Status; }
|