1602 lines
46 KiB
C
1602 lines
46 KiB
C
/*++
|
||
|
||
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;
|
||
|
||
#ifdef LFS_CLUSTER_CHECK
|
||
LONG
|
||
NtfsFspCloseExceptionFilter (
|
||
IN PIRP_CONTEXT IrpContext OPTIONAL,
|
||
IN PEXCEPTION_POINTERS ExceptionPointer
|
||
);
|
||
#endif
|
||
|
||
//
|
||
// Local procedure prototypes
|
||
//
|
||
|
||
NTSTATUS
|
||
NtfsCommonClose (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PSCB Scb,
|
||
IN PFCB Fcb,
|
||
IN PVCB Vcb,
|
||
IN PCCB *Ccb,
|
||
IN TYPE_OF_OPEN TypeOfOpen,
|
||
IN BOOLEAN ReadOnly,
|
||
IN BOOLEAN CalledFromFsp
|
||
);
|
||
|
||
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;
|
||
BOOLEAN IsSystemFile;
|
||
BOOLEAN IsReadOnly;
|
||
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;
|
||
}
|
||
|
||
//
|
||
// If this is the log file object for the Vcb then clear the field in the Vcb and
|
||
// return. We don't need to synchronize here since there is only one file object
|
||
// and it is closed only once.
|
||
//
|
||
|
||
if (FileObject == Vcb->LogFileObject) {
|
||
|
||
//
|
||
// Clear the internal file name constant
|
||
//
|
||
|
||
NtfsClearInternalFilename( Vcb->LogFileObject );
|
||
|
||
Vcb->LogFileObject = NULL;
|
||
|
||
Status = STATUS_SUCCESS;
|
||
NtfsCompleteRequest( NULL, Irp, Status );
|
||
|
||
DebugTrace( -1, Dbg, ("NtfsFsdClose -> %08lx\n", Status) );
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Call the common Close routine
|
||
//
|
||
|
||
FsRtlEnterFileSystem();
|
||
|
||
//
|
||
// Remember if this Ccb has gone through close.
|
||
//
|
||
|
||
if (Ccb != NULL) {
|
||
|
||
//
|
||
// We are not synchronized with the file resources at this point.
|
||
// It is possible that NtfsUpdateFileDupInfo or the rename path may want to
|
||
// update the name in the CCB. Our intention here is to mark this CCB_FLAG_CLOSE
|
||
// so that these other operations know to skip this CCB. We need to deal with the
|
||
// race condition where these other operations don't see the CLOSE flag but
|
||
// then access the CCB name (which points back to the file object) after we
|
||
// return the file object to the object manager (but put the CCB on the delayed
|
||
// close queue).
|
||
//
|
||
// We will use the Fcb mutex to close the hole where DupInfo and rename need to look
|
||
// at a CCB that might be in the close path.
|
||
//
|
||
|
||
NtfsLockFcb( NULL, Fcb );
|
||
SetFlag( Ccb->Flags, CCB_FLAG_CLOSE );
|
||
|
||
//
|
||
// If we're protecting the name in dupinfo path - strip it from the fileobject and free it
|
||
// with the ccb
|
||
//
|
||
|
||
if (FlagOn( Ccb->Flags, CCB_FLAG_PROTECT_NAME )) {
|
||
|
||
SetFlag( Ccb->Flags, CCB_FLAG_ALLOCATED_FILE_NAME );
|
||
FileObject->FileName.Buffer = NULL;
|
||
FileObject->FileName.Length = FileObject->FileName.MaximumLength = 0;
|
||
}
|
||
|
||
NtfsUnlockFcb( NULL, Fcb );
|
||
ASSERT( FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE ));
|
||
}
|
||
|
||
ThreadTopLevelContext = NtfsInitializeTopLevelIrp( &TopLevelContext, FALSE, FALSE );
|
||
IsSystemFile = FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE) || (TypeOfOpen == StreamFileOpen);
|
||
IsReadOnly = (BOOLEAN)IsFileObjectReadOnly( FileObject );
|
||
|
||
do {
|
||
|
||
try {
|
||
|
||
//
|
||
// Jam Wait to FALSE when we create the IrpContext, to avoid
|
||
// deadlocks when coming in from cleanup.
|
||
//
|
||
|
||
if (IrpContext == NULL) {
|
||
|
||
//
|
||
// Allocate and initialize the Irp.
|
||
//
|
||
|
||
NtfsInitializeIrpContext( Irp, FALSE, &IrpContext );
|
||
|
||
//
|
||
// Set the level structure on the stack.
|
||
//
|
||
|
||
NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext );
|
||
|
||
//
|
||
// If this is a top level request and we are not in the
|
||
// system process, then we can wait. If it is a top level
|
||
// 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 (NtfsIsTopLevelRequest( IrpContext )) {
|
||
|
||
if (PsGetCurrentProcess() != NtfsData.OurProcess) {
|
||
|
||
SetFlag( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
|
||
|
||
//
|
||
// This close is within the system process. It could be
|
||
// the segment derefernce thread. We want to be careful
|
||
// about processing the close in this thread. If we
|
||
// process the close too slowly we can eventually
|
||
// cause a large backlog of file objects within
|
||
// MM. We will consider posting under the following conditions.
|
||
//
|
||
// - There are more that four times as many file objects as handles (AND)
|
||
// - The number of excess file objects (CloseCount - CleanupCount) is
|
||
// over our async post threshold for this size system.
|
||
// - we're the deref seg thread (identified by current priority being above real time )
|
||
//
|
||
|
||
} else {
|
||
|
||
NtfsAsyncPassCount += 1;
|
||
|
||
if ((KeQueryPriorityThread( PsGetCurrentThread() ) >= LOW_REALTIME_PRIORITY) ||
|
||
|
||
(FlagOn( NtfsAsyncPassCount, 3 ) &&
|
||
(Vcb->CleanupCount * 4 < Vcb->CloseCount) &&
|
||
(Vcb->CloseCount - Vcb->CleanupCount > NtfsAsyncPostThreshold + NtfsMaxDelayedCloseCount))) {
|
||
|
||
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. Also if there was any error in the top level post it to
|
||
// preserve stack
|
||
//
|
||
|
||
} else if (!NtfsIsExclusiveScb( Scb ) ||
|
||
(IrpContext->TopLevelIrpContext->ExceptionStatus != STATUS_SUCCESS )) {
|
||
|
||
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,
|
||
Scb,
|
||
Fcb,
|
||
Vcb,
|
||
&Ccb,
|
||
TypeOfOpen,
|
||
IsReadOnly,
|
||
FALSE );
|
||
}
|
||
|
||
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
|
||
// exception code.
|
||
//
|
||
|
||
if (IrpContext == NULL) {
|
||
|
||
//
|
||
// We could've hit insufficient resources in trying to allocate
|
||
// the IrpContext. Make sure we don't leave a reference
|
||
// hanging around in this case. ProcessException will complete
|
||
// the IRP for us.
|
||
//
|
||
|
||
PLCB Lcb;
|
||
|
||
ASSERT( GetExceptionCode() == STATUS_INSUFFICIENT_RESOURCES );
|
||
|
||
if (Ccb != NULL) {
|
||
|
||
Lcb = Ccb->Lcb;
|
||
NtfsUnlinkCcbFromLcb( NULL, Fcb, Ccb );
|
||
NtfsDeleteCcb( Fcb, &Ccb );
|
||
|
||
} else {
|
||
|
||
Lcb = NULL;
|
||
}
|
||
|
||
//
|
||
// This only decrements the close counts so it will not raise
|
||
//
|
||
|
||
NtfsDecrementCloseCounts( NULL,
|
||
Scb,
|
||
Lcb,
|
||
IsSystemFile,
|
||
IsReadOnly,
|
||
TRUE,
|
||
NULL );
|
||
|
||
}
|
||
|
||
Status = NtfsProcessException( IrpContext, Irp, GetExceptionCode() );
|
||
}
|
||
|
||
ASSERT( NT_SUCCESS( Status ) || (IrpContext == NULL) || IsListEmpty(&IrpContext->ExclusiveFcbList) );
|
||
|
||
} while (Status == STATUS_CANT_WAIT ||
|
||
Status == STATUS_LOG_FILE_FULL);
|
||
|
||
//
|
||
// Io believes that it needs to free the FileObject->FileName.Buffer ONLY
|
||
// if FileObject->FileName.Length != 0. Ntfs hides the attribute name
|
||
// between FileObject->FileName.Length and FileObject->Filename.MaximumLength
|
||
// and for a attribute-name-open relative to a file opened by Id, the Length
|
||
// field will be zero. This, alas, causes Io to leak names. So...
|
||
//
|
||
// If we have a buffer allocated, make sure that the length is not zero when
|
||
// Io gets to see it.
|
||
//
|
||
|
||
if (FileObject->FileName.Buffer != NULL) {
|
||
|
||
FileObject->FileName.Length = 1;
|
||
}
|
||
|
||
//
|
||
// Trigger an assert on any unexpected cases.
|
||
//
|
||
|
||
ASSERT( (Status == STATUS_SUCCESS) || (Status == STATUS_PENDING) ||
|
||
(Status == STATUS_INSUFFICIENT_RESOURCES) );
|
||
|
||
//
|
||
// Post the request to the close queue on PENDING.
|
||
//
|
||
|
||
if (Status == STATUS_PENDING) {
|
||
|
||
BOOLEAN DelayCloseQueue = FALSE;
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
//
|
||
// Restore the thread context pointer if associated with this IrpContext.
|
||
//
|
||
|
||
if (FlagOn( IrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL )) {
|
||
|
||
NtfsRestoreTopLevelIrp();
|
||
ClearFlag( IrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL );
|
||
}
|
||
|
||
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DONT_DELETE );
|
||
NtfsCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
|
||
Status = STATUS_SUCCESS;
|
||
|
||
IrpContext->OriginatingIrp = (PIRP) Scb;
|
||
IrpContext->Union.SubjectContext = (PSECURITY_SUBJECT_CONTEXT) Ccb;
|
||
IrpContext->TransactionId = (TRANSACTION_ID) TypeOfOpen;
|
||
|
||
//
|
||
// At this point the file is effectively readonly - by changing it
|
||
// here we remove a race with implict locking through volume opens and
|
||
// the async close queue. Note: we have NO synchroniation here other
|
||
// than the interlocked operation. The vcb will not go away until
|
||
// this close is done
|
||
//
|
||
|
||
if (Ccb != NULL) {
|
||
|
||
if (!IsFileObjectReadOnly( FileObject )) {
|
||
FileObject->WriteAccess = 0;
|
||
FileObject->DeleteAccess = 0;
|
||
InterlockedIncrement( &Vcb->ReadOnlyCloseCount );
|
||
}
|
||
SetFlag( IrpContext->State, IRP_CONTEXT_STATE_READ_ONLY_FO );
|
||
|
||
} else {
|
||
|
||
//
|
||
// System files should never be read-only. There will be
|
||
// a ccb for all user fileobjects. Internal fileobjects are
|
||
// also always marked as system
|
||
//
|
||
|
||
ASSERT( !IsFileObjectReadOnly( FileObject ));
|
||
}
|
||
|
||
//
|
||
// Decide which close queue this will go on.
|
||
//
|
||
|
||
if (FlagOn( Scb->ScbState, SCB_STATE_DELAY_CLOSE )) {
|
||
|
||
NtfsAcquireFsrtlHeader( Scb );
|
||
ClearFlag( Scb->ScbState, SCB_STATE_DELAY_CLOSE );
|
||
NtfsReleaseFsrtlHeader( Scb );
|
||
|
||
if (Scb->Fcb->DelayedCloseCount == 0) {
|
||
|
||
DelayCloseQueue = TRUE;
|
||
}
|
||
}
|
||
|
||
NtfsQueueClose( IrpContext, DelayCloseQueue );
|
||
|
||
//
|
||
// Succeed in all other cases.
|
||
//
|
||
|
||
} else {
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
|
||
NtfsCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
|
||
}
|
||
|
||
//
|
||
// INSUFFICIENT_RESOURCES is the only other status that
|
||
// we can hit at this point. We would've completed the IRP in
|
||
// the except clause above in this case, so don't try doing it again.
|
||
//
|
||
|
||
ASSERT( Status == STATUS_SUCCESS || Status == STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
ASSERT( IoGetTopLevelIrp() != (PIRP) &TopLevelContext );
|
||
|
||
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;
|
||
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
PVCB CurrentVcb = NULL;
|
||
|
||
BOOLEAN ThrottleCreate = FALSE;
|
||
ULONG ClosedCount = 0;
|
||
|
||
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;
|
||
ThrottleCreate = TRUE;
|
||
}
|
||
|
||
ThreadTopLevelContext = NtfsInitializeTopLevelIrp( &TopLevelContext, TRUE, FALSE );
|
||
ASSERT( ThreadTopLevelContext == &TopLevelContext );
|
||
|
||
//
|
||
// 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, ThrottleCreate )) {
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
|
||
//
|
||
// 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->State, IRP_CONTEXT_STATE_READ_ONLY_FO )) {
|
||
|
||
ReadOnly = TRUE;
|
||
ClearFlag( IrpContext->State, IRP_CONTEXT_STATE_READ_ONLY_FO );
|
||
|
||
} else {
|
||
|
||
ReadOnly = FALSE;
|
||
}
|
||
|
||
ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAGS_CLEAR_ON_POST );
|
||
SetFlag( IrpContext->State,
|
||
IRP_CONTEXT_STATE_IN_FSP | IRP_CONTEXT_STATE_WAIT );
|
||
|
||
//
|
||
// Loop for retryable errors.
|
||
//
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
do {
|
||
|
||
//
|
||
// Set the TopLevel structure.
|
||
//
|
||
|
||
NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext );
|
||
|
||
//
|
||
// Call the common Close routine.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Do logfile full checkpointing
|
||
//
|
||
|
||
if (Status == STATUS_LOG_FILE_FULL) {
|
||
NtfsCheckpointForLogFileFull( IrpContext );
|
||
}
|
||
|
||
CurrentVcb = IrpContext->Vcb;
|
||
|
||
Status = NtfsCommonClose( IrpContext,
|
||
Scb,
|
||
Scb->Fcb,
|
||
IrpContext->Vcb,
|
||
&Ccb,
|
||
TypeOfOpen,
|
||
ReadOnly,
|
||
TRUE );
|
||
|
||
ASSERT(Status == STATUS_SUCCESS);
|
||
|
||
#ifdef LFS_CLUSTER_CHECK
|
||
} except( NtfsFspCloseExceptionFilter( IrpContext, GetExceptionInformation() )) {
|
||
#else
|
||
} except( NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) {
|
||
#endif
|
||
|
||
Status = NtfsProcessException( IrpContext, NULL, GetExceptionCode() );
|
||
}
|
||
|
||
ASSERT( NT_SUCCESS(Status) || IsListEmpty(&IrpContext->ExclusiveFcbList) );
|
||
|
||
//
|
||
// If we got a log file full, and our caller may have something
|
||
// acquired, then clean up and raise again.
|
||
//
|
||
|
||
if (((Status == STATUS_LOG_FILE_FULL) ||
|
||
(Status == STATUS_CANT_WAIT)) &&
|
||
ARGUMENT_PRESENT( ThisVcb )) {
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DONT_DELETE );
|
||
NtfsCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );
|
||
|
||
//
|
||
// Restore the information on the file object being closed.
|
||
//
|
||
|
||
IrpContext->OriginatingIrp = (PIRP)Scb;
|
||
IrpContext->Union.SubjectContext = (PVOID)Ccb;
|
||
IrpContext->TransactionId = TypeOfOpen;
|
||
if (ReadOnly) {
|
||
SetFlag( IrpContext->State, IRP_CONTEXT_STATE_READ_ONLY_FO );
|
||
}
|
||
|
||
//
|
||
// Now queue the close as an async close and get out.
|
||
//
|
||
|
||
if (FlagOn( IrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL )) {
|
||
|
||
NtfsRestoreTopLevelIrp();
|
||
ClearFlag( IrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL );
|
||
}
|
||
|
||
NtfsQueueClose( IrpContext, FALSE );
|
||
|
||
FsRtlExitFileSystem();
|
||
ExRaiseStatus( Status );
|
||
}
|
||
|
||
} while ((Status == STATUS_LOG_FILE_FULL) || (Status == STATUS_CANT_WAIT));
|
||
|
||
//
|
||
// No more for us to do. Clean up the IrpContext in any case.
|
||
//
|
||
|
||
NtfsCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );
|
||
|
||
//
|
||
// If we were just throttling creates and we made our last pass
|
||
// then exit.
|
||
//
|
||
|
||
if (ThrottleCreate) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
ASSERT( IoGetTopLevelIrp() != (PIRP) &TopLevelContext );
|
||
|
||
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 = NULL;
|
||
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 {
|
||
|
||
NtfsInitializeIrpContext( NULL, TRUE, &NewIrpContext );
|
||
|
||
//
|
||
// 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;
|
||
|
||
//
|
||
// Now increment the close counts for this Scb.
|
||
//
|
||
|
||
NtfsIncrementCloseCounts( Scb, TRUE, FALSE );
|
||
|
||
//
|
||
// Move the Scb to the end of the Fcb queue. We don't want to
|
||
// keep other Scb's from being deleted because this one is on
|
||
// the delayed close queue.
|
||
//
|
||
|
||
if (Scb->FcbLinks.Flink != &Scb->Fcb->ScbQueue) {
|
||
|
||
NtfsLockFcb( IrpContext, Scb->Fcb );
|
||
RemoveEntryList( &Scb->FcbLinks );
|
||
InsertTailList( &Scb->Fcb->ScbQueue, &Scb->FcbLinks );
|
||
ASSERT( Scb->FcbLinks.Flink == &Scb->Fcb->ScbQueue );
|
||
NtfsUnlockFcb( IrpContext, Scb->Fcb );
|
||
}
|
||
|
||
//
|
||
// Now add this to the correct queue.
|
||
//
|
||
|
||
NtfsQueueClose( NewIrpContext, DelayClose );
|
||
|
||
} except( FsRtlIsNtstatusExpected( GetExceptionCode() ) ?
|
||
EXCEPTION_EXECUTE_HANDLER :
|
||
EXCEPTION_CONTINUE_SEARCH ) {
|
||
|
||
NtfsMinimumExceptionProcessing( IrpContext );
|
||
Result = FALSE;
|
||
}
|
||
|
||
return Result;
|
||
|
||
UNREFERENCED_PARAMETER( IrpContext );
|
||
}
|
||
|
||
|
||
//
|
||
// Internal support routine
|
||
//
|
||
|
||
NTSTATUS
|
||
NtfsCommonClose (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PSCB Scb,
|
||
IN PFCB Fcb,
|
||
IN PVCB Vcb,
|
||
IN PCCB *Ccb,
|
||
IN TYPE_OF_OPEN TypeOfOpen,
|
||
IN BOOLEAN ReadOnly,
|
||
IN BOOLEAN CalledFromFsp
|
||
)
|
||
|
||
/*++
|
||
|
||
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.
|
||
|
||
Arguments:
|
||
|
||
Scb - Scb for this stream.
|
||
|
||
Fcb - Fcb for this stream.
|
||
|
||
Vcb - Vcb for this volume.
|
||
|
||
Ccb - User's Ccb for user files.
|
||
|
||
TypeOfOpen - Indicates the type of open for this stream.
|
||
|
||
ReadOnly - Indicates if the file object was for read-only access.
|
||
|
||
CalledFromFsp - Indicates whether this function was called from NtfsFspClose.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The return status for the operation
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN ExclusiveVcb = FALSE;
|
||
BOOLEAN AcquiredFcb = FALSE;
|
||
|
||
BOOLEAN SystemFile;
|
||
BOOLEAN RemovedFcb = FALSE;
|
||
ULONG AcquireFlags = ACQUIRE_NO_DELETE_CHECK | ACQUIRE_HOLD_BITMAP;
|
||
BOOLEAN NeedVcbExclusive = FALSE;
|
||
BOOLEAN WriteFileSize;
|
||
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
PLCB Lcb;
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
|
||
PAGED_CODE();
|
||
|
||
ASSERT( FlagOn( IrpContext->TopLevelIrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL ));
|
||
|
||
//
|
||
// Get the current Irp stack location
|
||
//
|
||
|
||
DebugTrace( +1, Dbg, ("NtfsCommonClose\n") );
|
||
DebugTrace( 0, Dbg, ("IrpContext = %08lx\n", IrpContext) );
|
||
|
||
if (!FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT )) {
|
||
|
||
SetFlag( AcquireFlags, ACQUIRE_DONT_WAIT );
|
||
}
|
||
|
||
//
|
||
// Loop here to acquire both the Vcb and Fcb. We want to acquire
|
||
// the Vcb exclusively if the file has multiple links.
|
||
//
|
||
|
||
while (TRUE) {
|
||
|
||
WriteFileSize = FALSE;
|
||
|
||
//
|
||
// Perform an unsafe test and optimistically acquire Vcb.
|
||
//
|
||
|
||
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;
|
||
}
|
||
}
|
||
|
||
//
|
||
// 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, AcquireFlags )) {
|
||
|
||
//
|
||
// Always release the Vcb. This can only be from the Fsd thread.
|
||
//
|
||
|
||
NtfsReleaseVcb( IrpContext, Vcb );
|
||
return STATUS_PENDING;
|
||
}
|
||
AcquiredFcb = TRUE;
|
||
|
||
//
|
||
// Recheck scbstate now that we own the fcb exclusive to see if we need
|
||
// to write the filesize at this point
|
||
//
|
||
|
||
if ((!FlagOn( Scb->ScbState, SCB_STATE_VOLUME_DISMOUNTED )) &&
|
||
(!FlagOn( Vcb->VcbState, VCB_STATE_LOCKED )) &&
|
||
(FlagOn( Scb->ScbState, SCB_STATE_WRITE_FILESIZE_ON_CLOSE )) &&
|
||
(Fcb->LinkCount > 0) &&
|
||
(!FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED ))) {
|
||
|
||
WriteFileSize = TRUE;
|
||
NtfsReleaseFcb( IrpContext, Fcb );
|
||
AcquiredFcb = FALSE;
|
||
|
||
//
|
||
// NtfsAcquireWithPaging only gets the paging if the irpcontext
|
||
// flag is set. Also it assumes no delete check which we explictly
|
||
// want here anyway.
|
||
//
|
||
|
||
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ACQUIRE_PAGING );
|
||
if (!NtfsAcquireFcbWithPaging( IrpContext, Fcb, AcquireFlags )) {
|
||
|
||
NtfsReleaseVcb( IrpContext, Vcb );
|
||
return STATUS_PENDING;
|
||
}
|
||
AcquiredFcb = TRUE;
|
||
|
||
//
|
||
// Recapture whether we need to write file size since dropping
|
||
//
|
||
|
||
if ((!FlagOn( Scb->ScbState, SCB_STATE_WRITE_FILESIZE_ON_CLOSE )) ||
|
||
(Fcb->LinkCount == 0) ||
|
||
(FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED ))) {
|
||
|
||
WriteFileSize = FALSE;
|
||
}
|
||
}
|
||
|
||
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;
|
||
NtfsReleaseFcbWithPaging( IrpContext, Fcb );
|
||
NtfsReleaseVcb( IrpContext, Vcb );
|
||
AcquiredFcb = FALSE;
|
||
|
||
} else {
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Set the wait flag in the IrpContext so we can acquire any other files
|
||
// we encounter.
|
||
//
|
||
|
||
SetFlag( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
|
||
|
||
try {
|
||
|
||
//
|
||
// See if we possibly have to do any Usn processing
|
||
//
|
||
|
||
if (Fcb->FcbUsnRecord != NULL) {
|
||
|
||
//
|
||
// If the file has no more user handles, but there is a pending Usn
|
||
// update (this should normally only happen if a stream was mapped
|
||
// by the user), then scan the streams to see if there are any
|
||
// remaining datasections, and if not then post the close.
|
||
//
|
||
|
||
if ((Fcb->CleanupCount == 0) &&
|
||
(Fcb->FcbUsnRecord->UsnRecord.Reason != 0)) {
|
||
|
||
if (!FlagOn( Vcb->VcbState, VCB_STATE_LOCKED ) &&
|
||
!FlagOn( Scb->ScbState, SCB_STATE_VOLUME_DISMOUNTED ) &&
|
||
!FlagOn( IrpContext->State, IRP_CONTEXT_STATE_FAILED_CLOSE )) {
|
||
|
||
PSCB TempScb;
|
||
|
||
//
|
||
// Leave if there are any streams with user-mapped files.
|
||
//
|
||
|
||
TempScb = (PSCB)CONTAINING_RECORD( Fcb->ScbQueue.Flink,
|
||
SCB,
|
||
FcbLinks );
|
||
|
||
while (&TempScb->FcbLinks != &Fcb->ScbQueue) {
|
||
|
||
if ((TempScb->NonpagedScb->SegmentObject.DataSectionObject != NULL) &&
|
||
!MmCanFileBeTruncated( &TempScb->NonpagedScb->SegmentObject, &Li0)) {
|
||
goto NoPost;
|
||
}
|
||
|
||
TempScb = (PSCB)CONTAINING_RECORD( TempScb->FcbLinks.Flink,
|
||
SCB,
|
||
FcbLinks );
|
||
}
|
||
|
||
//
|
||
// If we are not supposed to wait, then we should force this request to
|
||
// be posted. All recursive closes will go here since they are async
|
||
//
|
||
|
||
if (FlagOn( AcquireFlags, ACQUIRE_DONT_WAIT )) {
|
||
Status = STATUS_PENDING;
|
||
leave;
|
||
}
|
||
|
||
//
|
||
// We cannot generate logfile fulls in a regular thread with a recursive close
|
||
// safely without deadlocking
|
||
//
|
||
|
||
ASSERT( NtfsIsTopLevelRequest( IrpContext ) ||
|
||
FlagOn( IrpContext->State, IRP_CONTEXT_STATE_IN_FSP ) );
|
||
|
||
//
|
||
// Protect the call to the Usn routines with a try-except. If we hit
|
||
// any non-fatal error then set the IrpContext flag which indicates
|
||
// not to bother with the Usn and force a retry.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Now try to actually post the change.
|
||
//
|
||
|
||
NtfsPostUsnChange( IrpContext, Fcb, USN_REASON_CLOSE );
|
||
|
||
//
|
||
// Now write the journal, checkpoint the transaction, and free the UsnJournal to
|
||
// reduce contention. We force the write now, because the Fcb may get deleted
|
||
// before we normally would write the changes when the transaction commits.
|
||
//
|
||
|
||
NtfsWriteUsnJournalChanges( IrpContext );
|
||
NtfsCheckpointCurrentTransaction( IrpContext );
|
||
|
||
} except( (!FsRtlIsNtstatusExpected( Status = GetExceptionCode() ) ||
|
||
(Status == STATUS_LOG_FILE_FULL) ||
|
||
(Status == STATUS_CANT_WAIT)) ?
|
||
EXCEPTION_CONTINUE_SEARCH :
|
||
EXCEPTION_EXECUTE_HANDLER ) {
|
||
|
||
//
|
||
// We got some sort of error processing the Usn journal. We can't
|
||
// handle it in the close path. Let's retry this request but don't
|
||
// try to do the Usn operation.
|
||
//
|
||
|
||
SetFlag( IrpContext->State, IRP_CONTEXT_STATE_FAILED_CLOSE );
|
||
IrpContext->ExceptionStatus = STATUS_SUCCESS;
|
||
NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
|
||
}
|
||
|
||
//
|
||
// Free any remaining resources before decrementing close counts below,
|
||
// except for our Fcb. This reduces contention via the Usn Journal and
|
||
// prevents deadlocks since the Usn Journal is acquired last.
|
||
//
|
||
|
||
ASSERT(Fcb->ExclusiveFcbLinks.Flink != NULL);
|
||
while (!IsListEmpty(&IrpContext->ExclusiveFcbList)) {
|
||
|
||
if (&Fcb->ExclusiveFcbLinks == IrpContext->ExclusiveFcbList.Flink) {
|
||
|
||
RemoveEntryList( &Fcb->ExclusiveFcbLinks );
|
||
Fcb->ExclusiveFcbLinks.Flink = NULL;
|
||
|
||
} else {
|
||
|
||
NtfsReleaseFcb( IrpContext,
|
||
(PFCB)CONTAINING_RECORD(IrpContext->ExclusiveFcbList.Flink,
|
||
FCB,
|
||
ExclusiveFcbLinks ));
|
||
}
|
||
}
|
||
ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_RELEASE_USN_JRNL |
|
||
IRP_CONTEXT_FLAG_RELEASE_MFT );
|
||
|
||
//
|
||
// Now reinsert our Fcb if we removed it from the list. Check the Flink
|
||
// field to know if this is the case. Otherwise a higher level IrpContext
|
||
// will own this.
|
||
//
|
||
|
||
if (Fcb->ExclusiveFcbLinks.Flink == NULL) {
|
||
|
||
InsertTailList( &IrpContext->ExclusiveFcbList, &Fcb->ExclusiveFcbLinks );
|
||
}
|
||
|
||
//
|
||
// Escape here if we are not posting the close due to a user-mapped file.
|
||
//
|
||
|
||
NoPost: NOTHING;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now rewrite the filesizes if we have to
|
||
//
|
||
|
||
if (WriteFileSize) {
|
||
|
||
ASSERT( IrpContext->CleanupStructure != NULL );
|
||
|
||
//
|
||
// If the call to write the file size or the commit produces a logfile full
|
||
// we must retry in the fsp thread to prevent deadlocking from
|
||
// a recursive caller's already owning the vcb and an attempt to
|
||
// checkpoint
|
||
//
|
||
|
||
try {
|
||
|
||
NtfsWriteFileSizes( IrpContext, Scb, &Scb->Header.ValidDataLength.QuadPart, TRUE, TRUE, FALSE );
|
||
NtfsCheckpointCurrentTransaction( IrpContext );
|
||
ClearFlag( Scb->ScbState, SCB_STATE_WRITE_FILESIZE_ON_CLOSE );
|
||
|
||
} except( (Status = GetExceptionCode()), (Status != STATUS_LOG_FILE_FULL || FlagOn( IrpContext->State, IRP_CONTEXT_STATE_IN_FSP )) ?
|
||
EXCEPTION_CONTINUE_SEARCH :
|
||
EXCEPTION_EXECUTE_HANDLER ) {
|
||
|
||
NtfsMinimumExceptionProcessing( IrpContext );
|
||
Status = STATUS_PENDING;
|
||
}
|
||
|
||
if (Status == STATUS_PENDING) {
|
||
leave;
|
||
}
|
||
|
||
} // endif writing filesize
|
||
|
||
//
|
||
// 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, Fcb, (*Ccb) );
|
||
NtfsDeleteCcb( Fcb, Ccb );
|
||
|
||
} else {
|
||
|
||
Lcb = NULL;
|
||
}
|
||
|
||
SystemFile = FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE) || (TypeOfOpen == StreamFileOpen);
|
||
NtfsDecrementCloseCounts( IrpContext,
|
||
Scb,
|
||
Lcb,
|
||
SystemFile,
|
||
ReadOnly,
|
||
FALSE,
|
||
&RemovedFcb );
|
||
|
||
//
|
||
// Now that we're holding the Vcb, and we're past the point where we might
|
||
// raise log file full, we can safely adjust this field.
|
||
//
|
||
|
||
if (CalledFromFsp) {
|
||
|
||
InterlockedDecrement( &Vcb->QueuedCloseCount );
|
||
}
|
||
|
||
//
|
||
// 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 ) {
|
||
|
||
NtfsMinimumExceptionProcessing( IrpContext );
|
||
if (IrpContext->TransactionId != 0) {
|
||
|
||
NtfsCleanupFailedTransaction( IrpContext );
|
||
}
|
||
}
|
||
}
|
||
|
||
} finally {
|
||
|
||
DebugUnwind( NtfsCommonClose );
|
||
|
||
//
|
||
// Manage fcb explictly because we recursively come into this path
|
||
// and its cleaner to release the fcb at the same level in which you acquire it
|
||
//
|
||
|
||
if (AcquiredFcb && !RemovedFcb) {
|
||
NtfsReleaseFcbWithPaging( IrpContext, Fcb );
|
||
}
|
||
|
||
if (ExclusiveVcb) {
|
||
NtfsReleaseVcbCheckDelete( IrpContext, Vcb, IRP_MJ_CLOSE, NULL );
|
||
} else {
|
||
NtfsReleaseVcb( IrpContext, Vcb );
|
||
}
|
||
|
||
DebugTrace( -1, Dbg, ("NtfsCommonClose -> returning\n") );
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Internal support routine, spinlock wrapper.
|
||
//
|
||
|
||
VOID
|
||
NtfsQueueClose (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN BOOLEAN DelayClose
|
||
)
|
||
{
|
||
KIRQL SavedIrql;
|
||
BOOLEAN StartWorker = FALSE;
|
||
|
||
InterlockedIncrement( &(IrpContext->Vcb->QueuedCloseCount) );
|
||
|
||
if (DelayClose) {
|
||
|
||
//
|
||
// Increment the delayed close count for the Fcb for this
|
||
// file.
|
||
//
|
||
|
||
InterlockedIncrement( &((PSCB) IrpContext->OriginatingIrp)->Fcb->DelayedCloseCount );
|
||
|
||
ASSERT( IsListEmpty( &IrpContext->ExclusiveFcbList ) );
|
||
ASSERT( IsListEmpty( &IrpContext->RecentlyDeallocatedQueue ) );
|
||
|
||
RtlZeroMemory( &IrpContext->WorkQueueItem, sizeof( WORK_QUEUE_ITEM ));
|
||
|
||
SavedIrql = KeAcquireQueuedSpinLock( LockQueueNtfsStructLock );
|
||
|
||
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 {
|
||
|
||
SavedIrql = KeAcquireQueuedSpinLock( LockQueueNtfsStructLock );
|
||
|
||
ASSERT( IsListEmpty( &IrpContext->ExclusiveFcbList ) );
|
||
ASSERT( IsListEmpty( &IrpContext->RecentlyDeallocatedQueue ) );
|
||
RtlZeroMemory( &IrpContext->WorkQueueItem, sizeof( WORK_QUEUE_ITEM ));
|
||
|
||
InsertTailList( &NtfsData.AsyncCloseList,
|
||
&IrpContext->WorkQueueItem.List );
|
||
|
||
NtfsData.AsyncCloseCount += 1;
|
||
|
||
if (!NtfsData.AsyncCloseActive) {
|
||
|
||
NtfsData.AsyncCloseActive = TRUE;
|
||
|
||
StartWorker = TRUE;
|
||
}
|
||
}
|
||
|
||
KeReleaseQueuedSpinLock( LockQueueNtfsStructLock, 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;
|
||
|
||
SavedIrql = KeAcquireQueuedSpinLock( LockQueueNtfsStructLock );
|
||
|
||
//
|
||
// 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 (DBG || defined( NTFS_FREE_ASSERTS ))
|
||
} else {
|
||
|
||
ASSERT( NtfsData.DelayedCloseCount <= NtfsMaxDelayedCloseCount );
|
||
#endif
|
||
}
|
||
}
|
||
|
||
//
|
||
// If this is the delayed close case then decrement the delayed close count
|
||
// on this Fcb.
|
||
//
|
||
|
||
if (FromDelayedClose) {
|
||
|
||
KeReleaseQueuedSpinLock( LockQueueNtfsStructLock, 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;
|
||
}
|
||
|
||
KeReleaseQueuedSpinLock( LockQueueNtfsStructLock, SavedIrql );
|
||
}
|
||
if (IrpContext != NULL) {
|
||
|
||
//
|
||
// Reset the shared fields
|
||
//
|
||
|
||
InitializeListHead( &IrpContext->RecentlyDeallocatedQueue );
|
||
InitializeListHead( &IrpContext->ExclusiveFcbList );
|
||
|
||
}
|
||
|
||
ASSERT( (Vcb == NULL) || NtfsIsExclusiveVcb( Vcb ) || (IrpContext == NULL) );
|
||
return IrpContext;
|
||
}
|
||
|
||
#ifdef LFS_CLUSTER_CHECK
|
||
LONG
|
||
NtfsFspCloseExceptionFilter (
|
||
IN PIRP_CONTEXT IrpContext OPTIONAL,
|
||
IN PEXCEPTION_POINTERS ExceptionPointer
|
||
)
|
||
|
||
{
|
||
NTSTATUS ExceptionCode = ExceptionPointer->ExceptionRecord->ExceptionCode;
|
||
|
||
ASSERT( (ExceptionCode == STATUS_SUCCESS) ||
|
||
(ExceptionCode == STATUS_LOG_FILE_FULL) ||
|
||
(ExceptionCode == STATUS_CANT_WAIT) );
|
||
|
||
return NtfsExceptionFilter( IrpContext, ExceptionPointer );
|
||
}
|
||
#endif
|