3 Copyright (c) 1989-2000 Microsoft Corporation
11 This module implements the File Close routine for Fat called by the
20 // The Bug check file id for this module
23 #define BugCheckFileId (FAT_BUG_CHECK_CLOSE)
26 // The local debug trace level
29 #define Dbg (DEBUG_TRACE_CLOSE)
31 ULONG FatMaxDelayedCloseCount
;
34 #define FatAcquireCloseMutex() { \
35 NT_ASSERT(KeAreApcsDisabled()); \
36 ExAcquireFastMutexUnsafe( &FatCloseQueueMutex ); \
39 #define FatReleaseCloseMutex() { \
40 NT_ASSERT(KeAreApcsDisabled()); \
41 ExReleaseFastMutexUnsafe( &FatCloseQueueMutex ); \
45 // Local procedure prototypes
48 _Requires_lock_held_(_Global_critical_region_
)
51 IN PCLOSE_CONTEXT CloseContext
,
55 _Requires_lock_held_(_Global_critical_region_
)
59 PVCB LastVcbHint OPTIONAL
62 IO_WORKITEM_ROUTINE FatCloseWorker
;
67 _In_ PDEVICE_OBJECT DeviceObject
,
68 _In_opt_ PVOID Context
72 #pragma alloc_text(PAGE, FatFsdClose)
73 #pragma alloc_text(PAGE, FatFspClose)
74 #pragma alloc_text(PAGE, FatRemoveClose)
75 #pragma alloc_text(PAGE, FatCommonClose)
76 #pragma alloc_text(PAGE, FatCloseWorker)
80 _Function_class_(IRP_MJ_CLOSE
)
81 _Function_class_(DRIVER_DISPATCH
)
85 _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject
,
93 This routine implements the FSD part of Close.
97 VolumeDeviceObject - Supplies the volume device object where the
100 Irp - Supplies the Irp being processed
104 NTSTATUS - The FSD status for the IRP
109 NTSTATUS Status
= STATUS_SUCCESS
;
110 PIO_STACK_LOCATION IrpSp
;
111 PFILE_OBJECT FileObject
;
116 TYPE_OF_OPEN TypeOfOpen
;
119 BOOLEAN VcbDeleted
= FALSE
;
124 // If we were called with our file system device object instead of a
125 // volume device object, just complete this request with STATUS_SUCCESS
128 if (FatDeviceIsFatFsdo( VolumeDeviceObject
)) {
130 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
131 Irp
->IoStatus
.Information
= FILE_OPENED
;
133 IoCompleteRequest( Irp
, IO_DISK_INCREMENT
);
135 return STATUS_SUCCESS
;
138 DebugTrace(+1, Dbg
, "FatFsdClose\n", 0);
141 // Call the common Close routine
144 FsRtlEnterFileSystem();
146 TopLevel
= FatIsIrpTopLevel( Irp
);
149 // Get a pointer to the current stack location and the file object
152 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
154 FileObject
= IrpSp
->FileObject
;
157 // Decode the file object and set the read-only bit in the Ccb.
160 TypeOfOpen
= FatDecodeFileObject( FileObject
, &Vcb
, &Fcb
, &Ccb
);
162 if (Ccb
&& IsFileObjectReadOnly(FileObject
)) {
164 SetFlag( Ccb
->Flags
, CCB_FLAG_READ_ONLY
);
169 PCLOSE_CONTEXT CloseContext
= NULL
;
172 // If we are top level, WAIT can be TRUE, otherwise make it FALSE
173 // to avoid deadlocks, unless this is a top
174 // level request not originating from the system process.
177 BOOLEAN Wait
= TopLevel
&& (PsGetCurrentProcess() != FatData
.OurProcess
);
179 #if (NTDDI_VERSION >= NTDDI_WIN8)
182 // To catch the odd case where a close comes in without a preceding cleanup,
183 // call the oplock package to get rid of any oplock state. This can only
184 // be safely done in the FSD path.
188 !FlagOn( FileObject
->Flags
, FO_CLEANUP_COMPLETE
) &&
189 FatIsFileOplockable( Fcb
)) {
192 // This is equivalent to handling cleanup, and it always cleans up any
193 // oplock immediately. Also, we don't need any locking of the FCB here;
194 // the oplock's own lock will be sufficient for this purpose.
197 FsRtlCheckOplockEx( FatGetFcbOplock(Fcb
),
207 // Metadata streams have had close contexts preallocated. Pull one out now, while we're
208 // guaranteed the VCB exists.
211 if ( (TypeOfOpen
== VirtualVolumeFile
) || (TypeOfOpen
== DirectoryFile
) || (TypeOfOpen
== EaFile
)
214 CloseContext
= FatAllocateCloseContext( Vcb
);
215 NT_ASSERT( CloseContext
!= NULL
);
216 CloseContext
->Free
= TRUE
;
221 // Call the common Close routine if we are not delaying this close.
224 if ((((TypeOfOpen
== UserFileOpen
) ||
225 (TypeOfOpen
== UserDirectoryOpen
)) &&
226 FlagOn(Fcb
->FcbState
, FCB_STATE_DELAY_CLOSE
) &&
227 !FatData
.ShutdownStarted
) ||
228 (FatCommonClose( Vcb
, Fcb
, Ccb
, TypeOfOpen
, Wait
, TopLevel
, &VcbDeleted
) == STATUS_PENDING
)) {
231 // Ship it off to the delayed close queue if we tried to close, and got STATUS_PENDING, or
232 // if the user open told us to delay the close.
236 // Metadata streams have had close contexts preallocated. If we have a user open,
237 // pull the close context out of the Ccb.
240 if( CloseContext
== NULL
) {
243 // Free up any query template strings before using the close context fields,
244 // which overlap (union)
247 FatDeallocateCcbStrings( Ccb
);
249 CloseContext
= &Ccb
->CloseContext
;
250 CloseContext
->Free
= FALSE
;
252 SetFlag( Ccb
->Flags
, CCB_FLAG_CLOSE_CONTEXT
);
256 // If the status is pending, then let's get the information we
257 // need into the close context we already have bagged, complete
258 // the request, and post it. It is important we allocate nothing
259 // in the close path.
262 CloseContext
->Vcb
= Vcb
;
263 CloseContext
->Fcb
= Fcb
;
264 CloseContext
->TypeOfOpen
= TypeOfOpen
;
267 // Send it off, either to an ExWorkerThread or to the async
271 FatQueueClose( CloseContext
,
272 (BOOLEAN
)(Fcb
&& FlagOn(Fcb
->FcbState
, FCB_STATE_DELAY_CLOSE
)));
277 // The close proceeded synchronously, so for the metadata objects we
278 // can now drop the close context we preallocated.
281 if ((TypeOfOpen
== VirtualVolumeFile
) ||
282 (TypeOfOpen
== DirectoryFile
) ||
283 (TypeOfOpen
== EaFile
)
286 if (CloseContext
!= NULL
) {
288 ExFreePool( CloseContext
);
294 FatCompleteRequest( FatNull
, Irp
, Status
);
297 _SEH2_EXCEPT(FatExceptionFilter( NULL
, _SEH2_GetExceptionInformation() )) {
300 // We had some trouble trying to perform the requested
301 // operation, so we'll abort the I/O request with the
302 // error status that we get back from the exception code.
305 Status
= FatProcessException( NULL
, Irp
, _SEH2_GetExceptionCode() );
308 if (TopLevel
) { IoSetTopLevelIrp( NULL
); }
310 FsRtlExitFileSystem();
313 // And return to our caller
316 DebugTrace(-1, Dbg
, "FatFsdClose -> %08lx\n", Status
);
318 UNREFERENCED_PARAMETER( VolumeDeviceObject
);
326 _In_ PDEVICE_OBJECT DeviceObject
,
327 _In_opt_ PVOID Context
333 This routine is a shim between the IO worker package and FatFspClose.
337 DeviceObject - Registration device object, unused
338 Context - Context value, unused
348 UNREFERENCED_PARAMETER( DeviceObject
);
350 FsRtlEnterFileSystem();
352 FatFspClose (Context
);
354 FsRtlExitFileSystem();
358 _Requires_lock_held_(_Global_critical_region_
)
368 This routine implements the FSP part of Close.
372 Vcb - If present, tells us to only close file objects opened on the
382 PCLOSE_CONTEXT CloseContext
;
383 PVCB CurrentVcb
= NULL
;
385 BOOLEAN FreeContext
= FALSE
;
386 BOOLEAN TopLevel
= FALSE
;
388 ULONG LoopsWithVcbHeld
= 0;
392 DebugTrace(+1, Dbg
, "FatFspClose\n", 0);
395 // Set the top level IRP for the true FSP operation.
398 if (!ARGUMENT_PRESENT( Vcb
)) {
400 IoSetTopLevelIrp( (PIRP
)FSRTL_FSP_TOP_LEVEL_IRP
);
404 while ((CloseContext
= FatRemoveClose(Vcb
, LastVcb
)) != NULL
) {
407 // If we are in the FSP (i.e. Vcb == NULL), then try to keep ahead of
408 // creates by doing several closes with one acquisition of the Vcb.
410 // Note that we cannot be holding the Vcb on entry to FatCommonClose
411 // if this is last close as we will try to acquire FatData, and
412 // worse the volume (and therefore the Vcb) may go away.
415 if (!ARGUMENT_PRESENT(Vcb
)) {
417 if (!FatData
.ShutdownStarted
) {
419 if (CloseContext
->Vcb
!= CurrentVcb
) {
421 LoopsWithVcbHeld
= 0;
424 // Release a previously held Vcb, if any.
427 if (CurrentVcb
!= NULL
) {
429 ExReleaseResourceLite( &CurrentVcb
->Resource
);
436 CurrentVcb
= CloseContext
->Vcb
;
437 (VOID
)ExAcquireResourceExclusiveLite( &CurrentVcb
->Resource
, TRUE
);
442 // Share the resource occasionally if we seem to be finding a lot
443 // of closes for a single volume.
446 if (++LoopsWithVcbHeld
>= 20) {
448 if (ExGetSharedWaiterCount( &CurrentVcb
->Resource
) +
449 ExGetExclusiveWaiterCount( &CurrentVcb
->Resource
)) {
451 ExReleaseResourceLite( &CurrentVcb
->Resource
);
452 (VOID
)ExAcquireResourceExclusiveLite( &CurrentVcb
->Resource
, TRUE
);
455 LoopsWithVcbHeld
= 0;
460 // Now check the Open count. We may be about to delete this volume!
462 // The test below must be <= 1 because there could still be outstanding
463 // stream references on this VCB that are not counted in the OpenFileCount.
464 // For example if there are no open files OpenFileCount could be zero and we would
465 // not release the resource here. The call to FatCommonClose() below may cause
466 // the VCB to be torn down and we will try to release memory we don't
470 if (CurrentVcb
->OpenFileCount
<= 1) {
471 ExReleaseResourceLite( &CurrentVcb
->Resource
);
475 // If shutdown has started while processing our list, drop the
476 // current Vcb resource.
479 } else if (CurrentVcb
!= NULL
) {
481 ExReleaseResourceLite( &CurrentVcb
->Resource
);
486 LastVcb
= CurrentVcb
;
489 // Call the common Close routine. Protected in a try {} except {}
495 // The close context either is in the CCB, automatically freed,
496 // or was from pool for a metadata fileobject, CCB is NULL, and
497 // we'll need to free it.
500 FreeContext
= CloseContext
->Free
;
502 (VOID
)FatCommonClose( CloseContext
->Vcb
,
504 (FreeContext
? NULL
:
505 CONTAINING_RECORD( CloseContext
, CCB
, CloseContext
)),
506 CloseContext
->TypeOfOpen
,
511 } _SEH2_EXCEPT(FatExceptionFilter( NULL
, _SEH2_GetExceptionInformation() )) {
514 // Ignore anything we expect.
521 // Drop the context if it came from pool.
526 ExFreePool( CloseContext
);
531 // Release a previously held Vcb, if any.
534 if (CurrentVcb
!= NULL
) {
536 ExReleaseResourceLite( &CurrentVcb
->Resource
);
540 // Clean up the top level IRP hint if we owned it.
543 if (!ARGUMENT_PRESENT( Vcb
)) {
545 IoSetTopLevelIrp( NULL
);
549 // And return to our caller
552 DebugTrace(-1, Dbg
, "FatFspClose -> NULL\n", 0);
556 _Requires_lock_held_(_Global_critical_region_
)
559 IN PCLOSE_CONTEXT CloseContext
,
560 IN BOOLEAN DelayClose
567 Enqueue a deferred close to one of the two delayed close queues.
571 CloseContext - a close context to enqueue for the delayed close thread.
573 DelayClose - whether this should go on the delayed close queue (unreferenced
583 BOOLEAN StartWorker
= FALSE
;
585 FatAcquireCloseMutex();
589 InsertTailList( &FatData
.DelayedCloseList
,
590 &CloseContext
->GlobalLinks
);
591 InsertTailList( &CloseContext
->Vcb
->DelayedCloseList
,
592 &CloseContext
->VcbLinks
);
594 FatData
.DelayedCloseCount
+= 1;
596 if ((FatData
.DelayedCloseCount
> FatMaxDelayedCloseCount
) &&
597 !FatData
.AsyncCloseActive
) {
599 FatData
.AsyncCloseActive
= TRUE
;
605 InsertTailList( &FatData
.AsyncCloseList
,
606 &CloseContext
->GlobalLinks
);
607 InsertTailList( &CloseContext
->Vcb
->AsyncCloseList
,
608 &CloseContext
->VcbLinks
);
610 FatData
.AsyncCloseCount
+= 1;
612 if (!FatData
.AsyncCloseActive
) {
614 FatData
.AsyncCloseActive
= TRUE
;
619 FatReleaseCloseMutex();
623 IoQueueWorkItem( FatData
.FatCloseItem
, FatCloseWorker
, CriticalWorkQueue
, NULL
);
628 _Requires_lock_held_(_Global_critical_region_
)
632 PVCB LastVcbHint OPTIONAL
639 Dequeue a deferred close from one of the two delayed close queues.
643 Vcb - if specified, only returns close for this volume.
645 LastVcbHint - if specified and other starvation avoidance is required by
646 the system condition, will attempt to return closes for this volume.
656 PCLOSE_CONTEXT CloseContext
;
657 BOOLEAN WorkerThread
;
661 FatAcquireCloseMutex();
664 // Remember if this is the worker thread, so we can pull down the active
665 // flag should we run everything out.
668 WorkerThread
= (Vcb
== NULL
);
671 // If the queues are above the limits by a significant amount, we have
672 // to try hard to pull them down. To do this, we will aggressively try
673 // to find closes for the last volume the caller looked at. This will
674 // make sure we fully utilize the acquisition of the volume, which can
675 // be a hugely expensive resource to get (create/close/cleanup use it
678 // Only do this in the delayed close thread. We will know this is the
679 // case by seeing a NULL mandatory Vcb.
682 if (Vcb
== NULL
&& LastVcbHint
!= NULL
) {
685 // Flip over to aggressive at twice the legal limit, and flip it
686 // off at the legal limit.
689 if (!FatData
.HighAsync
&& FatData
.AsyncCloseCount
> FatMaxDelayedCloseCount
*2) {
691 FatData
.HighAsync
= TRUE
;
693 } else if (FatData
.HighAsync
&& FatData
.AsyncCloseCount
< FatMaxDelayedCloseCount
) {
695 FatData
.HighAsync
= FALSE
;
698 if (!FatData
.HighDelayed
&& FatData
.DelayedCloseCount
> FatMaxDelayedCloseCount
*2) {
700 FatData
.HighDelayed
= TRUE
;
702 } else if (FatData
.HighDelayed
&& FatData
.DelayedCloseCount
< FatMaxDelayedCloseCount
) {
704 FatData
.HighDelayed
= FALSE
;
707 if (FatData
.HighAsync
|| FatData
.HighDelayed
) {
714 // Do the case when we don't care about which Vcb the close is on.
715 // This is the case when we are in an ExWorkerThread and aren't
724 // First check the list of async closes.
727 if (!IsListEmpty( &FatData
.AsyncCloseList
)) {
729 Entry
= RemoveHeadList( &FatData
.AsyncCloseList
);
730 FatData
.AsyncCloseCount
-= 1;
732 CloseContext
= CONTAINING_RECORD( Entry
,
736 RemoveEntryList( &CloseContext
->VcbLinks
);
739 // Do any delayed closes over half the limit, unless shutdown has
740 // started (then kill them all).
743 } else if (!IsListEmpty( &FatData
.DelayedCloseList
) &&
744 (FatData
.DelayedCloseCount
> FatMaxDelayedCloseCount
/2 ||
745 FatData
.ShutdownStarted
)) {
747 Entry
= RemoveHeadList( &FatData
.DelayedCloseList
);
748 FatData
.DelayedCloseCount
-= 1;
750 CloseContext
= CONTAINING_RECORD( Entry
,
754 RemoveEntryList( &CloseContext
->VcbLinks
);
757 // There are no more closes to perform; show that we are done.
766 FatData
.AsyncCloseActive
= FALSE
;
771 // We're running down a specific volume.
778 // First check the list of async closes.
781 if (!IsListEmpty( &Vcb
->AsyncCloseList
)) {
783 Entry
= RemoveHeadList( &Vcb
->AsyncCloseList
);
784 FatData
.AsyncCloseCount
-= 1;
786 CloseContext
= CONTAINING_RECORD( Entry
,
790 RemoveEntryList( &CloseContext
->GlobalLinks
);
793 // Do any delayed closes.
796 } else if (!IsListEmpty( &Vcb
->DelayedCloseList
)) {
798 Entry
= RemoveHeadList( &Vcb
->DelayedCloseList
);
799 FatData
.DelayedCloseCount
-= 1;
801 CloseContext
= CONTAINING_RECORD( Entry
,
805 RemoveEntryList( &CloseContext
->GlobalLinks
);
808 // If we were trying to run down the queues but didn't find anything for this
809 // volume, flip over to accept anything and try again.
812 } else if (LastVcbHint
) {
817 // There are no more closes to perform; show that we are done.
826 FatReleaseCloseMutex();
832 _Requires_lock_held_(_Global_critical_region_
)
838 IN TYPE_OF_OPEN TypeOfOpen
,
841 OUT PBOOLEAN VcbDeleted OPTIONAL
848 This is the common routine for closing a file/directory called by both
849 the fsd and fsp threads.
851 Close is invoked whenever the last reference to a file object is deleted.
852 Cleanup is invoked when the last handle to a file object is closed, and
853 is called before close.
855 The function of close is to completely tear down and remove the fcb/dcb/ccb
856 structures associated with the file object.
860 Fcb - Supplies the file to process.
862 Wait - If this is TRUE we are allowed to block for the Vcb, if FALSE
863 then we must try to acquire the Vcb anyway.
865 TopLevel - If this is TRUE this is a top level request.
867 VcbDeleted - Returns whether the VCB was deleted by this call.
871 NTSTATUS - The return status for the operation
876 NTSTATUS Status
= STATUS_SUCCESS
;
878 BOOLEAN RecursiveClose
;
879 BOOLEAN LocalVcbDeleted
;
880 IRP_CONTEXT IrpContext
;
884 DebugTrace(+1, Dbg
, "FatCommonClose...\n", 0);
887 // Initialize the callers variable, if needed.
890 LocalVcbDeleted
= FALSE
;
892 if (ARGUMENT_PRESENT( VcbDeleted
)) {
894 *VcbDeleted
= LocalVcbDeleted
;
898 // Special case the unopened file object
901 if (TypeOfOpen
== UnopenedFileObject
) {
903 DebugTrace(0, Dbg
, "Close unopened file object\n", 0);
905 Status
= STATUS_SUCCESS
;
907 DebugTrace(-1, Dbg
, "FatCommonClose -> %08lx\n", Status
);
912 // Set up our stack IrpContext.
915 RtlZeroMemory( &IrpContext
, sizeof(IRP_CONTEXT
) );
917 IrpContext
.NodeTypeCode
= FAT_NTC_IRP_CONTEXT
;
918 IrpContext
.NodeByteSize
= sizeof( IrpContext
);
919 IrpContext
.MajorFunction
= IRP_MJ_CLOSE
;
920 IrpContext
.Vcb
= Vcb
;
924 SetFlag( IrpContext
.Flags
, IRP_CONTEXT_FLAG_WAIT
);
928 // Acquire exclusive access to the Vcb and enqueue the irp if we didn't
933 #pragma prefast( suppress: 28137, "prefast wants Wait to be a constant, but that's not possible for fastfat" )
935 if (!ExAcquireResourceExclusiveLite( &Vcb
->Resource
, Wait
)) {
937 return STATUS_PENDING
;
941 // The following test makes sure that we don't blow away an Fcb if we
942 // are trying to do a Supersede/Overwrite open above us. This test
943 // does not apply for the EA file.
946 if (FlagOn(Vcb
->VcbState
, VCB_STATE_FLAG_CREATE_IN_PROGRESS
) &&
949 ExReleaseResourceLite( &Vcb
->Resource
);
951 return STATUS_PENDING
;
955 // Setting the following flag prevents recursive closes of directory file
956 // objects, which are handled in a special case loop.
959 if ( FlagOn(Vcb
->VcbState
, VCB_STATE_FLAG_CLOSE_IN_PROGRESS
) ) {
961 RecursiveClose
= TRUE
;
966 SetFlag(Vcb
->VcbState
, VCB_STATE_FLAG_CLOSE_IN_PROGRESS
);
967 RecursiveClose
= FALSE
;
970 // Since we are at the top of the close chain, we need to add
971 // a reference to the VCB. This will keep it from going away
972 // on us until we are ready to check for a dismount below.
975 Vcb
->OpenFileCount
+= 1;
981 // Case on the type of open that we are trying to close.
984 switch (TypeOfOpen
) {
986 case VirtualVolumeFile
:
988 DebugTrace(0, Dbg
, "Close VirtualVolumeFile\n", 0);
991 // Remove this internal, residual open from the count.
994 InterlockedDecrement( (LONG
*)&(Vcb
->InternalOpenCount
) );
995 InterlockedDecrement( (LONG
*)&(Vcb
->ResidualOpenCount
) );
997 try_return( Status
= STATUS_SUCCESS
);
1000 case UserVolumeOpen
:
1002 DebugTrace(0, Dbg
, "Close UserVolumeOpen\n", 0);
1004 Vcb
->DirectAccessOpenCount
-= 1;
1005 Vcb
->OpenFileCount
-= 1;
1006 if (FlagOn(Ccb
->Flags
, CCB_FLAG_READ_ONLY
)) { Vcb
->ReadOnlyCount
-= 1; }
1008 FatDeleteCcb( &IrpContext
, &Ccb
);
1010 try_return( Status
= STATUS_SUCCESS
);
1015 DebugTrace(0, Dbg
, "Close EaFile\n", 0);
1018 // Remove this internal, residual open from the count.
1021 InterlockedDecrement( (LONG
*)&(Vcb
->InternalOpenCount
) );
1022 InterlockedDecrement( (LONG
*)&(Vcb
->ResidualOpenCount
) );
1024 try_return( Status
= STATUS_SUCCESS
);
1029 DebugTrace(0, Dbg
, "Close DirectoryFile\n", 0);
1031 InterlockedDecrement( (LONG
*)&Fcb
->Specific
.Dcb
.DirectoryFileOpenCount
);
1034 // Remove this internal open from the count.
1037 InterlockedDecrement( (LONG
*)&(Vcb
->InternalOpenCount
) );
1040 // If this is the root directory, it is a residual open
1044 if (NodeType( Fcb
) == FAT_NTC_ROOT_DCB
) {
1046 InterlockedDecrement( (LONG
*)&(Vcb
->ResidualOpenCount
) );
1050 // If this is a recursive close, just return here.
1053 if ( RecursiveClose
) {
1055 try_return( Status
= STATUS_SUCCESS
);
1063 case UserDirectoryOpen
:
1066 DebugTrace(0, Dbg
, "Close UserFileOpen/UserDirectoryOpen\n", 0);
1069 // Uninitialize the cache map if we no longer need to use it
1072 if ((NodeType(Fcb
) == FAT_NTC_DCB
) &&
1073 IsListEmpty(&Fcb
->Specific
.Dcb
.ParentDcbQueue
) &&
1074 (Fcb
->OpenCount
== 1) &&
1075 (Fcb
->Specific
.Dcb
.DirectoryFile
!= NULL
)) {
1077 PFILE_OBJECT DirectoryFileObject
= Fcb
->Specific
.Dcb
.DirectoryFile
;
1079 DebugTrace(0, Dbg
, "Uninitialize the stream file object\n", 0);
1081 CcUninitializeCacheMap( DirectoryFileObject
, NULL
, NULL
);
1084 // Dereference the directory file. This may cause a close
1085 // Irp to be processed, so we need to do this before we destroy
1089 Fcb
->Specific
.Dcb
.DirectoryFile
= NULL
;
1090 ObDereferenceObject( DirectoryFileObject
);
1093 Fcb
->OpenCount
-= 1;
1094 Vcb
->OpenFileCount
-= 1;
1095 if (FlagOn(Ccb
->Flags
, CCB_FLAG_READ_ONLY
)) { Vcb
->ReadOnlyCount
-= 1; }
1097 FatDeleteCcb( &IrpContext
, &Ccb
);
1104 #pragma prefast( suppress: 28159, "if the type of open is unknown, we seriously messed up." )
1106 FatBugCheck( TypeOfOpen
, 0, 0 );
1110 // At this point we've cleaned up any on-disk structure that needs
1111 // to be done, and we can now update the in-memory structures.
1112 // Now if this is an unreferenced FCB or if it is
1113 // an unreferenced DCB (not the root) then we can remove
1114 // the fcb and set our ParentDcb to non null.
1117 if (((NodeType(Fcb
) == FAT_NTC_FCB
) &&
1118 (Fcb
->OpenCount
== 0))
1122 ((NodeType(Fcb
) == FAT_NTC_DCB
) &&
1123 (IsListEmpty(&Fcb
->Specific
.Dcb
.ParentDcbQueue
)) &&
1124 (Fcb
->OpenCount
== 0) &&
1125 (Fcb
->Specific
.Dcb
.DirectoryFileOpenCount
== 0))) {
1127 ParentDcb
= Fcb
->ParentDcb
;
1129 SetFlag( Vcb
->VcbState
, VCB_STATE_FLAG_DELETED_FCB
);
1131 FatDeleteFcb( &IrpContext
, &Fcb
);
1134 // Uninitialize our parent's cache map if we no longer need
1138 while ((NodeType(ParentDcb
) == FAT_NTC_DCB
) &&
1139 IsListEmpty(&ParentDcb
->Specific
.Dcb
.ParentDcbQueue
) &&
1140 (ParentDcb
->OpenCount
== 0) &&
1141 (ParentDcb
->Specific
.Dcb
.DirectoryFile
!= NULL
)) {
1143 PFILE_OBJECT DirectoryFileObject
;
1145 DirectoryFileObject
= ParentDcb
->Specific
.Dcb
.DirectoryFile
;
1147 DebugTrace(0, Dbg
, "Uninitialize our parent Stream Cache Map\n", 0);
1149 CcUninitializeCacheMap( DirectoryFileObject
, NULL
, NULL
);
1151 ParentDcb
->Specific
.Dcb
.DirectoryFile
= NULL
;
1153 ObDereferenceObject( DirectoryFileObject
);
1156 // Now, if the ObDereferenceObject() caused the final close
1157 // to come in, then blow away the Fcb and continue up,
1158 // otherwise wait for Mm to to dereference its file objects
1162 if ( ParentDcb
->Specific
.Dcb
.DirectoryFileOpenCount
== 0) {
1166 CurrentDcb
= ParentDcb
;
1167 ParentDcb
= CurrentDcb
->ParentDcb
;
1169 SetFlag( Vcb
->VcbState
, VCB_STATE_FLAG_DELETED_FCB
);
1171 FatDeleteFcb( &IrpContext
, &CurrentDcb
);
1180 Status
= STATUS_SUCCESS
;
1185 DebugUnwind( FatCommonClose
);
1188 // We are done processing the close. If we are the top of the close
1189 // chain, see if the VCB can go away. We have biased the open count by
1190 // one, so we need to take that into account.
1193 if (!RecursiveClose
) {
1196 // See if there is only one open left. If so, it is ours. We only want
1197 // to check for a dismount if a dismount is not already in progress.
1198 // We also only do this if the Vcb condition is not VcbGood and the
1199 // caller can handle the VCB going away. This is determined by whether
1200 // they passed in the VcbDeleted argument. This request also needs
1204 if (Vcb
->OpenFileCount
== 1 &&
1205 Vcb
->VcbCondition
!= VcbGood
&&
1206 !FlagOn( Vcb
->VcbState
, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS
) &&
1207 ARGUMENT_PRESENT( VcbDeleted
) &&
1211 // We need the global lock, which must be acquired before the
1212 // VCB. Since we already have the VCB, we have to drop and
1213 // reacquire here. Note that we always want to wait from this
1214 // point on. Note that the VCB cannot go away, since we have
1215 // biased the open file count.
1218 FatReleaseVcb( &IrpContext
,
1221 SetFlag( IrpContext
.Flags
, IRP_CONTEXT_FLAG_WAIT
);
1224 #pragma prefast( suppress: 28137, "prefast wants the wait parameter in this macro expansion to be a constant, unfortunately this is not possible" )
1226 FatAcquireExclusiveGlobal( &IrpContext
);
1228 FatAcquireExclusiveVcb( &IrpContext
,
1232 // We have our locks in the correct order. Remove our
1233 // extra open and check for a dismount. Note that if
1234 // something changed while we dropped the lock, it will
1235 // not matter, since the dismount code does the correct
1236 // checks to make sure the volume can really go away.
1239 Vcb
->OpenFileCount
-= 1;
1241 LocalVcbDeleted
= FatCheckForDismount( &IrpContext
,
1245 FatReleaseGlobal( &IrpContext
);
1248 // Let the caller know what happened, if they want this information.
1251 if (ARGUMENT_PRESENT( VcbDeleted
)) {
1253 *VcbDeleted
= LocalVcbDeleted
;
1259 // The volume cannot go away now. Just remove our extra reference.
1262 Vcb
->OpenFileCount
-= 1;
1266 // If the VCB is still around, clear our recursion flag.
1269 if (!LocalVcbDeleted
) {
1271 ClearFlag( Vcb
->VcbState
, VCB_STATE_FLAG_CLOSE_IN_PROGRESS
);
1276 // Only release the VCB if it did not go away.
1279 if (!LocalVcbDeleted
) {
1281 FatReleaseVcb( &IrpContext
, Vcb
);
1284 DebugTrace(-1, Dbg
, "FatCommonClose -> %08lx\n", Status
);