/*++ Copyright (c) 1991 Microsoft Corporation Module Name: Close.c Abstract: This module implements the File Close routine for Ntfs called by the dispatch driver. Author: Your Name [Email] dd-Mon-Year Revision History: --*/ #include "NtfsProc.h" // // The local debug trace level // #define Dbg (DEBUG_TRACE_CLOSE) ULONG NtfsAsyncPassCount = 0; // // Local procedure prototypes // NTSTATUS NtfsCommonClose ( IN PIRP_CONTEXT IrpContext, IN PFILE_OBJECT FileObject OPTIONAL, IN PSCB Scb, IN PFCB Fcb, IN PVCB Vcb, IN PCCB Ccb, IN PBOOLEAN AcquiredVcb OPTIONAL, IN PBOOLEAN ExclusiveVcb OPTIONAL, IN TYPE_OF_OPEN TypeOfOpen, IN BOOLEAN ReadOnly ); VOID NtfsQueueClose ( IN PIRP_CONTEXT IrpContext, IN BOOLEAN DelayClose ); PIRP_CONTEXT NtfsRemoveClose ( IN PVCB Vcb OPTIONAL, IN BOOLEAN ThrottleCreate ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, NtfsCommonClose) #pragma alloc_text(PAGE, NtfsFsdClose) #pragma alloc_text(PAGE, NtfsFspClose) #endif NTSTATUS NtfsFsdClose ( 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 --*/ { TOP_LEVEL_CONTEXT TopLevelContext; PTOP_LEVEL_CONTEXT ThreadTopLevelContext; NTSTATUS Status = STATUS_SUCCESS; PIRP_CONTEXT IrpContext = NULL; PFILE_OBJECT FileObject; TYPE_OF_OPEN TypeOfOpen; PVCB Vcb; PFCB Fcb; PSCB Scb; PCCB Ccb; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); PAGED_CODE(); ASSERT_IRP( Irp ); // // If we were called with our file system device object instead of a // volume device object, just complete this request with STATUS_SUCCESS // if (VolumeDeviceObject->DeviceObject.Size == (USHORT)sizeof(DEVICE_OBJECT)) { Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = FILE_OPENED; IoCompleteRequest( Irp, IO_DISK_INCREMENT ); return STATUS_SUCCESS; } DebugTrace( +1, Dbg, ("NtfsFsdClose\n") ); // // Extract and decode the file object, we are willing to handle the unmounted // file object. // FileObject = IrpSp->FileObject; TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, FALSE ); // // Special case the unopened file object // if (TypeOfOpen == UnopenedFileObject) { DebugTrace( 0, Dbg, ("Close unopened file object\n") ); Status = STATUS_SUCCESS; NtfsCompleteRequest( NULL, &Irp, Status ); DebugTrace( -1, Dbg, ("NtfsFsdClose -> %08lx\n", Status) ); return Status; } // // Remember if this Ccb has gone through close. // if (Ccb != NULL) { SetFlag( Ccb->Flags, CCB_FLAG_CLOSE ); } // // Call the common Close routine // FsRtlEnterFileSystem(); ThreadTopLevelContext = NtfsSetTopLevelIrp( &TopLevelContext, FALSE, FALSE ); do { try { // // Jam Wait to FALSE when we create the IrpContext, to avoid // deadlocks when coming in from cleanup. // if (IrpContext == NULL) { IrpContext = NtfsCreateIrpContext( Irp, FALSE ); NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext ); // // If this is a top level Ntfs request and we are not in the // system process, then we can wait. If it is a top level // Ntfs request and we are in the system process then we would // rather not block this thread at all. If the number of pending // async closes is not too large we will post this immediately. // if (NtfsIsTopLevelNtfs( IrpContext )) { if (PsGetCurrentProcess() != NtfsData.OurProcess) { SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); // // This close is coming from a system thread. It could // be the segment dereference thread trying to make pages // available. Rather than try to do all of the work here // we will post 3 out of 4 closes to our async close // workqueue so that there will be two threads doing // the work. // } else { NtfsAsyncPassCount += 1; if (FlagOn( NtfsAsyncPassCount, 3 )) { Status = STATUS_PENDING; break; } } // // This is a recursive Ntfs call. Post this unless we already // own this file. Otherwise we could deadlock walking // up the tree. // } else if (!NtfsIsExclusiveScb( Scb )) { Status = STATUS_PENDING; break; } } else if (Status == STATUS_LOG_FILE_FULL) { NtfsCheckpointForLogFileFull( IrpContext ); } // // If this Scb should go on the delayed close queue then // status is STATUS_PENDING; // if (FlagOn( Scb->ScbState, SCB_STATE_DELAY_CLOSE ) && (Scb->Fcb->DelayedCloseCount == 0)) { Status = STATUS_PENDING; } else { Status = NtfsCommonClose( IrpContext, FileObject, Scb, Fcb, Vcb, Ccb, NULL, NULL, TypeOfOpen, (BOOLEAN)IsFileObjectReadOnly(FileObject) ); } 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); // // If this is a normal termination then complete the request. // if (Status == STATUS_SUCCESS) { NtfsCompleteRequest( &IrpContext, &Irp, STATUS_SUCCESS ); } else if (Status == STATUS_PENDING) { // // If the status is can't wait, then let's get the information we // need into the IrpContext, complete the request, // and post the IrpContext. // IrpContext->OriginatingIrp = (PIRP) Scb; IrpContext->Union.SubjectContext = (PSECURITY_SUBJECT_CONTEXT) Ccb; IrpContext->TransactionId = (TRANSACTION_ID) TypeOfOpen; if (IsFileObjectReadOnly( FileObject )) { SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_READ_ONLY_FO ); } NtfsCompleteRequest( NULL, &Irp, STATUS_SUCCESS ); if (FlagOn( Scb->ScbState, SCB_STATE_DELAY_CLOSE ) && (Scb->Fcb->DelayedCloseCount == 0)) { NtfsQueueClose( IrpContext, TRUE ); } else { NtfsQueueClose( IrpContext, FALSE ); } } if (ThreadTopLevelContext == &TopLevelContext) { NtfsRestoreTopLevelIrp( ThreadTopLevelContext ); } FsRtlExitFileSystem(); // // And return to our caller // DebugTrace( -1, Dbg, ("NtfsFsdClose -> %08lx\n", Status) ); return Status; } VOID NtfsFspClose ( IN PVCB ThisVcb OPTIONAL ) /*++ Routine Description: This routine implements the FSP part of Close. Arguments: ThisVcb - If specified then we want to remove all closes for a given Vcb. Otherwise this routine will close all of the async closes and as many of the delayed closes as possible. Return Value: None. --*/ { PIRP_CONTEXT IrpContext; TOP_LEVEL_CONTEXT TopLevelContext; PTOP_LEVEL_CONTEXT ThreadTopLevelContext; TYPE_OF_OPEN TypeOfOpen; PSCB Scb; PCCB Ccb; BOOLEAN ReadOnly; BOOLEAN AcquiredVcb = FALSE; PBOOLEAN AcquiredVcbPtr; BOOLEAN ExclusiveVcb = FALSE; PVCB CurrentVcb = NULL; ULONG VcbHoldCount = 0; BOOLEAN SinglePass = FALSE; DebugTrace( +1, Dbg, ("NtfsFspClose\n") ); PAGED_CODE(); FsRtlEnterFileSystem(); // // Occasionally we are called from some other routine to try to // reduce the backlog of closes. This is indicated by a pointer // value of 1. // if (ThisVcb == (PVCB) 1) { ThisVcb = NULL; SinglePass = TRUE; } // // If we were passed a Vcb then we don't need CommonClose to hold the // Vcb open. // if (ARGUMENT_PRESENT( ThisVcb )) { AcquiredVcbPtr = NULL; } else { AcquiredVcbPtr = &AcquiredVcb; } // // Extract and decode the file object, we are willing to handle the unmounted // file object. Note we normally get here via an IrpContext which really // just points to a file object. We should never see an Irp, unless it can // happen for verify or some other reason. // while (IrpContext = NtfsRemoveClose( ThisVcb, SinglePass )) { ASSERT_IRP_CONTEXT( IrpContext ); ThreadTopLevelContext = NtfsSetTopLevelIrp( &TopLevelContext, TRUE, FALSE ); ASSERT( ThreadTopLevelContext == &TopLevelContext ); NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext ); // // Recover the information about the file object being closed from // the data stored in the IrpContext. The following fields are // used for this. // // OriginatingIrp - Contains the Scb // SubjectContext - Contains the Ccb // TransactionId - Contains the TypeOfOpen // Flags - Has bit for read-only file. // Scb = (PSCB) IrpContext->OriginatingIrp; IrpContext->OriginatingIrp = NULL; Ccb = (PCCB) IrpContext->Union.SubjectContext; IrpContext->Union.SubjectContext = NULL; TypeOfOpen = (TYPE_OF_OPEN) IrpContext->TransactionId; IrpContext->TransactionId = 0; if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_READ_ONLY_FO )) { ReadOnly = TRUE; ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_READ_ONLY_FO ); } else { ReadOnly = FALSE; } ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAGS_CLEAR_ON_POST ); SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_IN_FSP | IRP_CONTEXT_FLAG_WAIT ); // // Call the common Close routine. // try { // // Release the previous Vcb if held. // if (AcquiredVcb && IrpContext->Vcb != CurrentVcb) { ExReleaseResource( &CurrentVcb->Resource ); AcquiredVcb = FALSE; VcbHoldCount = 0; } CurrentVcb = IrpContext->Vcb; (VOID)NtfsCommonClose( IrpContext, NULL, Scb, Scb->Fcb, IrpContext->Vcb, Ccb, AcquiredVcbPtr, &ExclusiveVcb, TypeOfOpen, ReadOnly ); // // If we are currently holding the Vcb exclusive then convert // to shared but only there was no input Vcb. Otherwise we // might change how a top level request is holding the Vcb. // if (AcquiredVcb) { // // We must periodically release this resource in case there // is an exclusive waiter. // if (VcbHoldCount > NtfsMinDelayedCloseCount) { ExReleaseResource( &CurrentVcb->Resource ); AcquiredVcb = FALSE; VcbHoldCount = 0; } else if (ExclusiveVcb) { ExConvertExclusiveToShared( &CurrentVcb->Resource ); ExclusiveVcb = FALSE; } VcbHoldCount += 1; } else { VcbHoldCount = 0; } } except( NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) { NOTHING; } // // Now just "complete" the IrpContext. // NtfsCompleteRequest( &IrpContext, NULL, STATUS_SUCCESS ); NtfsRestoreTopLevelIrp( ThreadTopLevelContext ); // // If we were just to do a single pass and we don't currently // hold the Vcb then exit. // if (SinglePass && !AcquiredVcb) { break; } } // // Release the previously held Vcb, if any. // if (AcquiredVcb) { ExReleaseResource( &CurrentVcb->Resource ); } FsRtlExitFileSystem(); // // And return to our caller // DebugTrace( -1, Dbg, ("NtfsFspClose -> NULL\n") ); return; } BOOLEAN NtfsAddScbToFspClose ( IN PIRP_CONTEXT IrpContext, IN PSCB Scb, IN BOOLEAN DelayClose ) /*++ Routine Description: This routine is called to add an entry for the current Scb onto one of the Fsp close queues. This is used when we want to guarantee that a teardown will be called on an Scb or Fcb when the current operation can't begin the operation. Arguments: Scb - Scb to add to the queue. DelayClose - Indicates which queue this should go into. Return Value: BOOLEAN - Indicates whether or not the SCB was added to the delayed close queue --*/ { PIRP_CONTEXT NewIrpContext; BOOLEAN Result = TRUE; PAGED_CODE(); // // Use a try-except to catch any allocation failures. The only valid // error here is an allocation failure for the new irp context. // try { NewIrpContext = NtfsCreateIrpContext( NULL, TRUE ); // // Set the necessary fields to post this to the workqueue. // NewIrpContext->Vcb = Scb->Vcb; NewIrpContext->MajorFunction = IRP_MJ_CLOSE; NewIrpContext->OriginatingIrp = (PIRP) Scb; NewIrpContext->TransactionId = (TRANSACTION_ID) StreamFileOpen; SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_READ_ONLY_FO ); // // Now increment the close counts for this Scb. // NtfsIncrementCloseCounts( Scb, TRUE, FALSE ); // // Now add this to the correct queue. // NtfsQueueClose( NewIrpContext, DelayClose ); } except( FsRtlIsNtstatusExpected( GetExceptionCode() ) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { Result = FALSE; } return Result; } // // Internal support routine // NTSTATUS NtfsCommonClose ( IN PIRP_CONTEXT IrpContext, IN PFILE_OBJECT FileObject OPTIONAL, IN PSCB Scb, IN PFCB Fcb, IN PVCB Vcb, IN PCCB Ccb, IN PBOOLEAN AcquiredVcb OPTIONAL, IN PBOOLEAN ExclusiveVcb OPTIONAL, IN TYPE_OF_OPEN TypeOfOpen, IN BOOLEAN ReadOnly ) /*++ Routine Description: This is the common routine for Close called by both the fsd and fsp threads. Key for this routine is how to acquire the Vcb and whether to leave the Vcb acquired on exit. ExclusiveVcb - Acquire the Vcb exclusively if the file being closed has multiple links. Also if the volume is not mounted or we are closing one of the system streams used only by the filesystem or we are performing system shutdown. AcquiredVcb - Release the Vcb in this routine if the AcquiredVcb pointer was not supplied. Also if the volume is not mounted or we are closing a system file. Arguments: FileObject - This is the file object for this open. Won't be specified if this call is from the Fsp path. Scb - Scb for this stream. Fcb - Fcb for this stream. Vcb - Vcb for this volume. Ccb - User's Ccb for user files. AcquiredVcb - If specified and TRUE then our caller has already acquired the Vcb. If specified and FALSE then our caller hasn't acquired the Vcb but would like to have it held on exit from this routine. Look at the ExclusiveVcb boolean to determine how it was acquired. Set to FALSE if this routine will free the Vcb. ExclusiveVcb - If AcquiredVcb is TRUE then this boolean will indicate how our caller has acquired this Vcb. Updated on exit if we leave the Vcb held. TypeOfOpen - Indicates the type of open for this stream. ReadOnly - Indicates if the file object was for read-only access. Return Value: NTSTATUS - The return status for the operation --*/ { BOOLEAN ReleaseVcb; BOOLEAN LocalAcquiredVcb; BOOLEAN LocalExclusiveVcb; BOOLEAN SystemFile; BOOLEAN RemovedFcb = FALSE; BOOLEAN DontWait; BOOLEAN NeedVcbExclusive = FALSE; PLCB Lcb; ASSERT_IRP_CONTEXT( IrpContext ); PAGED_CODE(); // // Get the current Irp stack location // DebugTrace( +1, Dbg, ("NtfsCommonClose\n") ); DebugTrace( 0, Dbg, ("IrpContext = %08lx\n", IrpContext) ); if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) { DontWait = FALSE; } else { DontWait = TRUE; } // // Look at the input parameters to determine if we should hold the Vcb on // exit. // if (ARGUMENT_PRESENT( AcquiredVcb )) { // // If the volume isn't mounted or this is a system file then // acquire the Vcb exclusively and release on exit. // if ((FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED | VCB_STATE_FLAG_SHUTDOWN | VCB_STATE_PERFORMED_DISMOUNT ) == VCB_STATE_VOLUME_MOUNTED) && (NtfsSegmentNumber( &Fcb->FileReference ) >= FIRST_USER_FILE_NUMBER || NtfsSegmentNumber( &Fcb->FileReference ) == ROOT_FILE_NAME_INDEX_NUMBER || NtfsSegmentNumber( &Fcb->FileReference ) == VOLUME_DASD_NUMBER)) { ReleaseVcb = FALSE; } else { // // We want to release the Vcb but also want to acquire it exclusively. // ReleaseVcb = TRUE; NeedVcbExclusive = TRUE; if (!(*ExclusiveVcb) && *AcquiredVcb) { NtfsReleaseVcb( IrpContext, Vcb ); *AcquiredVcb = FALSE; } } } else { ReleaseVcb = TRUE; AcquiredVcb = &LocalAcquiredVcb; ExclusiveVcb = &LocalExclusiveVcb; *AcquiredVcb = FALSE; } // // Loop here to acquire both the Vcb and Fcb. We want to acquire // the Vcb exclusively if the file has multiple links. // while (TRUE) { // // If we haven't acquired the Vcb then perform an unsafe test and // optimistically acquire it. // if (!(*AcquiredVcb)) { if (NeedVcbExclusive || (Fcb->LcbQueue.Flink != Fcb->LcbQueue.Blink) || FlagOn( Vcb->VcbState, VCB_STATE_PERFORMED_DISMOUNT )) { if (!NtfsAcquireExclusiveVcb( IrpContext, Vcb, FALSE )) { return STATUS_PENDING; } *ExclusiveVcb = TRUE; } else { if (!NtfsAcquireSharedVcb( IrpContext, Vcb, FALSE )) { return STATUS_PENDING; } *ExclusiveVcb = FALSE; } *AcquiredVcb = TRUE; } // // Now try to acquire the Fcb. If we are unable to acquire it then // release the Vcb and return. This can only be from the Fsd path // since otherwise Wait will be TRUE. // if (!NtfsAcquireExclusiveFcb( IrpContext, Fcb, NULL, TRUE, DontWait )) { // // Always release the Vcb. This can only be from the Fsd thread. // NtfsReleaseVcb( IrpContext, Vcb ); *AcquiredVcb = FALSE; return STATUS_PENDING; } if (*ExclusiveVcb) { break; } // // Otherwise we need to confirm that our unsafe test above was correct. // if ((Fcb->LcbQueue.Flink != Fcb->LcbQueue.Blink) || FlagOn( Vcb->VcbState, VCB_STATE_PERFORMED_DISMOUNT )) { NeedVcbExclusive = TRUE; NtfsReleaseFcb( IrpContext, Fcb ); NtfsReleaseVcb( IrpContext, Vcb ); *AcquiredVcb = FALSE; } else { break; } } // // Set the wait flag in the IrpContext so we can acquire any other files // we encounter. // SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); try { // // We take the same action for all open files. We // delete the Ccb if present, and we decrement the close // file counts. // if (Ccb != NULL) { Lcb = Ccb->Lcb; NtfsUnlinkCcbFromLcb( IrpContext, Ccb ); NtfsDeleteCcb( IrpContext, Fcb, &Ccb ); } else { Lcb = NULL; } SystemFile = FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE) || (TypeOfOpen == StreamFileOpen); NtfsDecrementCloseCounts( IrpContext, Scb, Lcb, SystemFile, ReadOnly, FALSE ); // // If we had to write a log record for close, it can only be for duplicate // information. We will commit that transaction here and remove // the entry from the transaction table. We do it here so we won't // fail inside the 'except' of a 'try-except'. // if (IrpContext->TransactionId != 0) { try { NtfsCommitCurrentTransaction( IrpContext ); } except( EXCEPTION_EXECUTE_HANDLER ) { if (IrpContext->TransactionId != 0) { // // We couldn't write the commit record, we clean up as // best we can. // NtfsAcquireExclusiveRestartTable( &Vcb->TransactionTable, TRUE ); NtfsFreeRestartTableIndex( &Vcb->TransactionTable, IrpContext->TransactionId ); NtfsReleaseRestartTable( &Vcb->TransactionTable ); IrpContext->TransactionId = 0; } } } } finally { DebugUnwind( NtfsCommonClose ); if (ReleaseVcb) { NtfsReleaseVcbCheckDelete( IrpContext, Vcb, IRP_MJ_CLOSE, FileObject ); *AcquiredVcb = FALSE; } DebugTrace( -1, Dbg, ("NtfsCommonClose -> returning\n") ); } return STATUS_SUCCESS; } // // Internal support routine, spinlock wrapper. // VOID NtfsQueueClose ( IN PIRP_CONTEXT IrpContext, IN BOOLEAN DelayClose ) { KIRQL SavedIrql; BOOLEAN StartWorker = FALSE; if (DelayClose) { // // Increment the delayed close count for the Fcb for this // file. // InterlockedIncrement( &((PSCB) IrpContext->OriginatingIrp)->Fcb->DelayedCloseCount ); KeAcquireSpinLock( &NtfsData.StrucSupSpinLock, &SavedIrql ); InsertTailList( &NtfsData.DelayedCloseList, &IrpContext->WorkQueueItem.List ); NtfsData.DelayedCloseCount += 1; if (NtfsData.DelayedCloseCount > NtfsMaxDelayedCloseCount) { NtfsData.ReduceDelayedClose = TRUE; if (!NtfsData.AsyncCloseActive) { NtfsData.AsyncCloseActive = TRUE; StartWorker = TRUE; } } } else { KeAcquireSpinLock( &NtfsData.StrucSupSpinLock, &SavedIrql ); InsertTailList( &NtfsData.AsyncCloseList, &IrpContext->WorkQueueItem.List ); NtfsData.AsyncCloseCount += 1; if (!NtfsData.AsyncCloseActive) { NtfsData.AsyncCloseActive = TRUE; StartWorker = TRUE; } } KeReleaseSpinLock( &NtfsData.StrucSupSpinLock, SavedIrql ); if (StartWorker) { ExQueueWorkItem( &NtfsData.NtfsCloseItem, CriticalWorkQueue ); } } // // Internal support routine, spinlock wrapper. // PIRP_CONTEXT NtfsRemoveClose ( IN PVCB Vcb OPTIONAL, IN BOOLEAN ThrottleCreate ) { PLIST_ENTRY Entry; KIRQL SavedIrql; PIRP_CONTEXT IrpContext = NULL; BOOLEAN FromDelayedClose = FALSE; ASSERT( Vcb == NULL || NtfsIsExclusiveVcb( Vcb )); KeAcquireSpinLock( &NtfsData.StrucSupSpinLock, &SavedIrql ); // // First check the list of async closes. // if (!IsListEmpty( &NtfsData.AsyncCloseList )) { Entry = NtfsData.AsyncCloseList.Flink; while (Entry != &NtfsData.AsyncCloseList) { // // Extract the IrpContext. // IrpContext = CONTAINING_RECORD( Entry, IRP_CONTEXT, WorkQueueItem.List ); // // If no Vcb was specified or this Vcb is for our volume // then perform the close. // if (!ARGUMENT_PRESENT( Vcb ) || IrpContext->Vcb == Vcb) { RemoveEntryList( Entry ); NtfsData.AsyncCloseCount -= 1; break; } else { IrpContext = NULL; Entry = Entry->Flink; } } } // // If we didn't find anything look through the delayed close // queue. // if (IrpContext == NULL) { // // Now check our delayed close list. // if (ARGUMENT_PRESENT( Vcb )) { Entry = NtfsData.DelayedCloseList.Flink; IrpContext = NULL; // // If we were given a Vcb, only do the closes for this volume. // while (Entry != &NtfsData.DelayedCloseList) { // // Extract the IrpContext. // IrpContext = CONTAINING_RECORD( Entry, IRP_CONTEXT, WorkQueueItem.List ); // // Is this close on our volume? // if (IrpContext->Vcb == Vcb) { RemoveEntryList( Entry ); NtfsData.DelayedCloseCount -= 1; FromDelayedClose = TRUE; break; } else { IrpContext = NULL; Entry = Entry->Flink; } } // // Check if need to reduce the delayed close count. // } else if (NtfsData.ReduceDelayedClose) { if (NtfsData.DelayedCloseCount > NtfsMinDelayedCloseCount) { // // Do any closes over the limit. // Entry = RemoveHeadList( &NtfsData.DelayedCloseList ); NtfsData.DelayedCloseCount -= 1; // // Extract the IrpContext. // IrpContext = CONTAINING_RECORD( Entry, IRP_CONTEXT, WorkQueueItem.List ); FromDelayedClose = TRUE; } else { NtfsData.ReduceDelayedClose = FALSE; } } } // // If this is the delayed close case then decrement the delayed close count // on this Fcb. // if (FromDelayedClose) { KeReleaseSpinLock( &NtfsData.StrucSupSpinLock, SavedIrql ); InterlockedDecrement( &((PSCB) IrpContext->OriginatingIrp)->Fcb->DelayedCloseCount ); // // If we are returning NULL, show that we are done. // } else { if (!ARGUMENT_PRESENT( Vcb ) && (IrpContext == NULL) && !ThrottleCreate) { NtfsData.AsyncCloseActive = FALSE; } KeReleaseSpinLock( &NtfsData.StrucSupSpinLock, SavedIrql ); } return IrpContext; }