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; BOOLEAN VcbDeleted; #ifdef __ND_FAT__ if ((PVOID)FatControlDeviceObject == VolumeDeviceObject) { Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = FILE_OPENED; IoCompleteRequest( Irp, IO_DISK_INCREMENT ); return STATUS_SUCCESS; } #endif // // 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); #ifdef __ND_FAT__ if (Ccb) { SetFlag( Ccb->NdFatFlags, ND_FAT_CCB_FLAG_CLOSE ); ((PCCB)(FileObject->FsContext2))->FileObject = NULL; FileObject->FsContext = NULL; FileObject->FsContext2 = NULL; } #endif // // 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, &VcbDeleted ) == 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; }
NTSTATUS FatFsdClose ( _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, _Inout_ 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; BOOLEAN VcbDeleted = FALSE; PAGED_CODE(); // // 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 = NULL; // // 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); #if (NTDDI_VERSION >= NTDDI_WIN8) // // To catch the odd case where a close comes in without a preceding cleanup, // call the oplock package to get rid of any oplock state. This can only // be safely done in the FSD path. // if ((Fcb != NULL) && !FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE ) && FatIsFileOplockable( Fcb )) { // // This is equivalent to handling cleanup, and it always cleans up any // oplock immediately. Also, we don't need any locking of the FCB here; // the oplock's own lock will be sufficient for this purpose. // FsRtlCheckOplockEx( FatGetFcbOplock(Fcb), Irp, 0, NULL, NULL, NULL ); } #endif // // 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, TopLevel, &VcbDeleted ) == STATUS_PENDING)) { // // Metadata streams have had close contexts preallocated. // if (TypeOfOpen == VirtualVolumeFile) { NT_ASSERT( Vcb->CloseContext != NULL ); CloseContext = Vcb->CloseContext; Vcb->CloseContext = NULL; CloseContext->Free = TRUE; } else if ((TypeOfOpen == DirectoryFile) || (TypeOfOpen == EaFile)) { CloseContext = FatAllocateCloseContext( Vcb); NT_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)) { if (TypeOfOpen == VirtualVolumeFile) { // // If the VCB was deleted during the close, the close context for this // open has already been freed. // if (!VcbDeleted) { CloseContext = Vcb->CloseContext; Vcb->CloseContext = NULL; NT_ASSERT( CloseContext != NULL ); } else { CloseContext = NULL; } } else { CloseContext = FatAllocateCloseContext( (VcbDeleted ? NULL : Vcb) ); NT_ASSERT( CloseContext != NULL ); } if (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 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)) { #ifdef __ND_FAT_SECONDARY__ NTSTATUS Status; #endif // // 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; #ifdef __ND_FAT_SECONDARY__ if (CloseContext->Fcb && FlagOn(CloseContext->Fcb->NdFatFlags, ND_FAT_FCB_FLAG_SECONDARY)) { PVOLUME_DEVICE_OBJECT VolumeDeviceObject = (CONTAINING_RECORD(CloseContext->Vcb, VOLUME_DEVICE_OBJECT, Vcb)); BOOLEAN secondaryResourceAcquired = FALSE; BOOLEAN secondaryRecoveryResourceAcquired = FALSE; ASSERT( FreeContext == FALSE ); Status = STATUS_SUCCESS; while (TRUE) { ASSERT( secondaryRecoveryResourceAcquired == FALSE ); ASSERT( secondaryResourceAcquired == FALSE ); if (FlagOn(VolumeDeviceObject->Secondary->Thread.Flags, SECONDARY_THREAD_FLAG_REMOTE_DISCONNECTED)) { if (FlagOn(VolumeDeviceObject->Vcb.NdFatFlags, ND_FAT_VCB_FLAG_TRY_CLOSE_FILES)) { Status = STATUS_CANT_WAIT; break; } secondaryRecoveryResourceAcquired = SecondaryAcquireResourceExclusiveLite( NULL, &VolumeDeviceObject->Secondary->RecoveryResource, TRUE ); if (!FlagOn(VolumeDeviceObject->Secondary->Thread.Flags, SECONDARY_THREAD_FLAG_REMOTE_DISCONNECTED) ) { SecondaryReleaseResourceLite( NULL, &VolumeDeviceObject->Secondary->RecoveryResource ); secondaryRecoveryResourceAcquired = FALSE; continue; } secondaryResourceAcquired = SecondaryAcquireResourceExclusiveLite( NULL, &VolumeDeviceObject->Secondary->Resource, TRUE ); try { SessionRecovery( VolumeDeviceObject->Secondary, NULL ); } finally { SecondaryReleaseResourceLite( NULL, &VolumeDeviceObject->Secondary->Resource ); secondaryResourceAcquired = FALSE; SecondaryReleaseResourceLite( NULL, &VolumeDeviceObject->Secondary->RecoveryResource ); secondaryRecoveryResourceAcquired = FALSE; } continue; } secondaryResourceAcquired = SecondaryAcquireResourceSharedLite( NULL, &VolumeDeviceObject->Secondary->Resource, TRUE ); if (secondaryResourceAcquired == FALSE) { ASSERT( FlagOn(VolumeDeviceObject->Secondary->Thread.Flags, SECONDARY_THREAD_FLAG_REMOTE_DISCONNECTED) || FlagOn(VolumeDeviceObject->Secondary->Flags, SECONDARY_FLAG_RECONNECTING) ); continue; } break; } if (Status == STATUS_SUCCESS) { try { Status = FatCommonClose( CloseContext->Vcb, CloseContext->Fcb, (FreeContext ? NULL : CONTAINING_RECORD( CloseContext, CCB, CloseContext)), CloseContext->TypeOfOpen, TRUE, NULL ); } finally { ASSERT( ExIsResourceAcquiredSharedLite(&VolumeDeviceObject->Secondary->Resource) ); SecondaryReleaseResourceLite( NULL, &VolumeDeviceObject->Secondary->Resource ); } } } else Status = FatCommonClose( CloseContext->Vcb, CloseContext->Fcb, (FreeContext ? NULL : CONTAINING_RECORD( CloseContext, CCB, CloseContext)), CloseContext->TypeOfOpen, TRUE, NULL ); #else (VOID)FatCommonClose( CloseContext->Vcb, CloseContext->Fcb, (FreeContext ? NULL : CONTAINING_RECORD( CloseContext, CCB, CloseContext)), CloseContext->TypeOfOpen, TRUE, NULL ); #endif } except(FatExceptionFilter( NULL, GetExceptionInformation() )) { // // Ignore anything we expect. // NOTHING; } // // Drop the context if it came from pool. // #ifdef __ND_FAT_SECONDARY__ if (Status == STATUS_CANT_WAIT) { ASSERT( FreeContext == FALSE ); ASSERT( CloseContext->Fcb && FlagOn(CloseContext->Fcb->FcbState, FCB_STATE_DELAY_CLOSE) ); FatQueueClose( CloseContext, (BOOLEAN)(CloseContext->Fcb && FlagOn(CloseContext->Fcb->FcbState, FCB_STATE_DELAY_CLOSE)) ); break; } if (FreeContext) { ExFreePool( CloseContext ); } #else if (FreeContext) { ExFreePool( CloseContext ); } #endif }