mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1187 lines
29 KiB
1187 lines
29 KiB
/*++
|
|
|
|
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;
|
|
}
|