3 Copyright (c) 1989-2000 Microsoft Corporation
11 This module implements the File Close routine for Cdfs called by the
12 Fsd/Fsp dispatch routines.
14 The close operation interacts with both the async and delayed close queues
15 in the CdData structure. Since close may be called recursively we may
16 violate the locking order in acquiring the Vcb or Fcb. In this case
17 we may move the request to the async close queue. If this is the last
18 reference on the Fcb and there is a chance the user may reopen this
19 file again soon we would like to defer the close. In this case we
20 may move the request to the async close queue.
22 Once we are past the decode file operation there is no need for the
23 file object. If we are moving the request to either of the work
24 queues then we remember all of the information from the file object and
25 complete the request with STATUS_SUCCESS. The Io system can then
26 reuse the file object and we can complete the request when convenient.
28 The async close queue consists of requests which we would like to
29 complete as soon as possible. They are queued using the original
30 IrpContext where some of the fields have been overwritten with
31 information from the file object. We will extract this information,
32 cleanup the IrpContext and then call the close worker routine.
34 The delayed close queue consists of requests which we would like to
35 defer the close for. We keep size of this list within a range
36 determined by the size of the system. We let it grow to some maximum
37 value and then shrink to some minimum value. We allocate a small
38 structure which contains the key information from the file object
39 and use this information along with an IrpContext on the stack
40 to complete the request.
48 // The Bug check file id for this module
51 #define BugCheckFileId (CDFS_BUG_CHECK_CLOSE)
54 // Local support routines
58 CdCommonClosePrivate (
59 IN PIRP_CONTEXT IrpContext
,
62 IN ULONG UserReference
,
68 IN PIRP_CONTEXT IrpContext
,
70 IN ULONG UserReference
,
71 IN BOOLEAN DelayedClose
80 NTAPI
/* ReactOS Change: GCC Does not support STDCALL by default */
82 IN PDEVICE_OBJECT DeviceObject
,
87 #pragma alloc_text(PAGE, CdCommonClose)
88 #pragma alloc_text(PAGE, CdCommonClosePrivate)
89 #pragma alloc_text(PAGE, CdQueueClose)
90 #pragma alloc_text(PAGE, CdRemoveClose)
91 #pragma alloc_text(PAGE, CdCloseWorker)
104 This routine is called to process the close queues in the CdData. If the
105 Vcb is passed then we want to remove all of the closes for this Vcb.
106 Otherwise we will do as many of the delayed closes as we need to do.
110 Vcb - If specified then we are looking for all of the closes for the
120 PIRP_CONTEXT IrpContext
;
121 IRP_CONTEXT StackIrpContext
;
123 THREAD_CONTEXT ThreadContext
;
128 ULONG VcbHoldCount
= 0;
129 PVCB CurrentVcb
= NULL
;
131 BOOLEAN PotentialVcbTeardown
= FALSE
;
135 FsRtlEnterFileSystem();
138 // Continue processing until there are no more closes to process.
140 /* ReactOS Change: "GCC suggest parentheses around assignment used as truth value" */
141 while ((IrpContext
= CdRemoveClose( Vcb
))) {
144 // If we don't have an IrpContext then use the one on the stack.
145 // Initialize it for this request.
148 if (SafeNodeType( IrpContext
) != CDFS_NTC_IRP_CONTEXT
) {
151 // Update the local values from the IrpContextLite.
154 Fcb
= ((PIRP_CONTEXT_LITE
) IrpContext
)->Fcb
;
155 UserReference
= ((PIRP_CONTEXT_LITE
) IrpContext
)->UserReference
;
158 // Update the stack irp context with the values from the
162 CdInitializeStackIrpContext( &StackIrpContext
,
163 (PIRP_CONTEXT_LITE
) IrpContext
);
166 // Free the IrpContextLite.
169 CdFreeIrpContextLite( IrpContext
); /* ReactOS Change: GCC "error: invalid lvalue in unary '&'" */
172 // Remember we have the IrpContext from the stack.
175 IrpContext
= &StackIrpContext
;
178 // Otherwise cleanup the existing IrpContext.
184 // Remember the Fcb and user reference count.
187 Fcb
= (PFCB
) IrpContext
->Irp
;
188 IrpContext
->Irp
= NULL
;
190 UserReference
= (ULONG
) IrpContext
->ExceptionStatus
;
191 IrpContext
->ExceptionStatus
= STATUS_SUCCESS
;
195 // We have an IrpContext. Now we need to set the top level thread
199 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FSP_FLAGS
);
202 // If we were given a Vcb then there is a request on top of this.
205 if (ARGUMENT_PRESENT( Vcb
)) {
207 ClearFlag( IrpContext
->Flags
,
208 IRP_CONTEXT_FLAG_TOP_LEVEL
| IRP_CONTEXT_FLAG_TOP_LEVEL_CDFS
);
211 CdSetThreadContext( IrpContext
, &ThreadContext
);
214 // If we have hit the maximum number of requests to process without
215 // releasing the Vcb then release the Vcb now. If we are holding
216 // a different Vcb to this one then release the previous Vcb.
218 // In either case acquire the current Vcb.
220 // We use the MinDelayedCloseCount from the CdData since it is
221 // a convenient value based on the system size. Only thing we are trying
222 // to do here is prevent this routine starving other threads which
223 // may need this Vcb exclusively.
225 // Note that the check for potential teardown below is unsafe. We'll
226 // repeat later within the cddata lock.
229 PotentialVcbTeardown
= !ARGUMENT_PRESENT( Vcb
) &&
230 (Fcb
->Vcb
->VcbCondition
!= VcbMounted
) &&
231 (Fcb
->Vcb
->VcbCondition
!= VcbMountInProgress
) &&
232 (Fcb
->Vcb
->VcbCleanup
== 0);
234 if (PotentialVcbTeardown
||
235 (VcbHoldCount
> CdData
.MinDelayedCloseCount
) ||
236 (Fcb
->Vcb
!= CurrentVcb
)) {
238 if (CurrentVcb
!= NULL
) {
240 CdReleaseVcb( IrpContext
, CurrentVcb
);
243 if (PotentialVcbTeardown
) {
245 CdAcquireCdData( IrpContext
);
248 // Repeat the checks with global lock held. The volume could have
249 // been remounted while we didn't hold the lock.
252 PotentialVcbTeardown
= !ARGUMENT_PRESENT( Vcb
) &&
253 (Fcb
->Vcb
->VcbCondition
!= VcbMounted
) &&
254 (Fcb
->Vcb
->VcbCondition
!= VcbMountInProgress
) &&
255 (Fcb
->Vcb
->VcbCleanup
== 0);
257 if (!PotentialVcbTeardown
) {
259 CdReleaseCdData( IrpContext
);
263 CurrentVcb
= Fcb
->Vcb
;
264 CdAcquireVcbShared( IrpContext
, CurrentVcb
, FALSE
);
274 // Call our worker routine to perform the close operation.
277 CdCommonClosePrivate( IrpContext
, CurrentVcb
, Fcb
, UserReference
, FALSE
);
280 // If the reference count on this Vcb is below our residual reference
281 // then check if we should dismount the volume.
284 if (PotentialVcbTeardown
) {
286 CdReleaseVcb( IrpContext
, CurrentVcb
);
287 CdCheckForDismount( IrpContext
, CurrentVcb
, FALSE
);
291 CdReleaseCdData( IrpContext
);
292 PotentialVcbTeardown
= FALSE
;
296 // Complete the current request to cleanup the IrpContext.
299 CdCompleteRequest( IrpContext
, NULL
, STATUS_SUCCESS
);
303 // Release any Vcb we may still hold.
306 if (CurrentVcb
!= NULL
) {
308 CdReleaseVcb( IrpContext
, CurrentVcb
);
312 FsRtlExitFileSystem();
318 IN PIRP_CONTEXT IrpContext
,
326 This routine is the Fsd entry for the close operation. We decode the file
327 object to find the CDFS structures and type of open. We call our internal
328 worker routine to perform the actual work. If the work wasn't completed
329 then we post to one of our worker queues. The Ccb isn't needed after this
330 point so we delete the Ccb and return STATUS_SUCCESS to our caller in all
335 Irp - Supplies the Irp to process
344 TYPE_OF_OPEN TypeOfOpen
;
349 ULONG UserReference
= 0;
351 BOOLEAN PotentialVcbTeardown
= FALSE
;
352 BOOLEAN ForceDismount
= FALSE
;
356 ASSERT_IRP_CONTEXT( IrpContext
);
360 // If we were called with our file system device object instead of a
361 // volume device object, just complete this request with STATUS_SUCCESS.
364 if (IrpContext
->Vcb
== NULL
) {
366 CdCompleteRequest( IrpContext
, Irp
, STATUS_SUCCESS
);
367 return STATUS_SUCCESS
;
371 // Decode the file object to get the type of open and Fcb/Ccb.
374 TypeOfOpen
= CdDecodeFileObject( IrpContext
,
375 IoGetCurrentIrpStackLocation( Irp
)->FileObject
,
380 // No work to do for unopened file objects.
383 if (TypeOfOpen
== UnopenedFileObject
) {
385 CdCompleteRequest( IrpContext
, Irp
, STATUS_SUCCESS
);
387 return STATUS_SUCCESS
;
393 // Clean up any CCB associated with this open.
401 // Was a FSCTL_DISMOUNT issued on this handle? If so, we need to
402 // force a dismount of the volume now.
405 ForceDismount
= BooleanFlagOn( Ccb
->Flags
, CCB_FLAG_DISMOUNT_ON_CLOSE
);
408 // We can always deallocate the Ccb if present.
411 CdDeleteCcb( IrpContext
, Ccb
);
415 // If this is the last reference to a user file or directory on a
416 // currently mounted volume, then post it to the delayed close queue. Note
417 // that the VcbCondition check is unsafe, but it doesn't really matter -
418 // we just might delay the volume teardown a little by posting this close.
421 if ((Vcb
->VcbCondition
== VcbMounted
) &&
422 (Fcb
->FcbReference
== 1) &&
423 ((TypeOfOpen
== UserFileOpen
) ||
424 (TypeOfOpen
== UserDirectoryOpen
))) {
426 CdQueueClose( IrpContext
, Fcb
, UserReference
, TRUE
);
430 // Otherwise try to process this close. Post to the async close queue
431 // if we can't acquire all of the resources.
437 // If we may be dismounting this volume then acquire the CdData
440 // Since we now must make volumes go away as soon as reasonable after
441 // the last user handles closes, key off of the cleanup count. It is
442 // OK to do this more than necessary. Since this Fcb could be holding
443 // a number of other Fcbs (and thus their references), a simple check
444 // on reference count is not appropriate.
446 // Do an unsafe check first to avoid taking the (global) cddata lock in the
450 if (((Vcb
->VcbCleanup
== 0) || ForceDismount
) &&
451 (Vcb
->VcbCondition
!= VcbMounted
)) {
454 // Possible. Acquire CdData to synchronise with the remount path, and
455 // then repeat the tests.
457 // Note that we must send the notification outside of any locks, since
458 // the worker that processes the notify could also be calling into our
459 // pnp path which wants both CdData and VcbResource. For a force dismount
460 // the volume will be marked invalid (no going back), so we will definitely
461 // go ahead and dismount below.
467 // Send notification.
470 FsRtlNotifyVolumeEvent( IoGetCurrentIrpStackLocation( Irp
)->FileObject
,
471 FSRTL_VOLUME_DISMOUNT
);
474 CdAcquireCdData( IrpContext
);
476 if (((Vcb
->VcbCleanup
== 0) || ForceDismount
) &&
477 (Vcb
->VcbCondition
!= VcbMounted
) &&
478 (Vcb
->VcbCondition
!= VcbMountInProgress
) &&
479 FlagOn( IrpContext
->Flags
, IRP_CONTEXT_FLAG_TOP_LEVEL_CDFS
)) {
481 PotentialVcbTeardown
= TRUE
;
486 // We can't dismount this volume now, there are other references or
487 // it's just been remounted.
490 CdReleaseCdData( IrpContext
);
497 // Physically disconnect this Vcb from the device so a new mount can
498 // occur. Vcb deletion cannot happen at this time since there is
499 // a handle on it associated with this very request, but we'll call
500 // check for dismount again later anyway.
503 CdCheckForDismount( IrpContext
, Vcb
, TRUE
);
507 // Call the worker routine to perform the actual work. This routine
508 // should never raise except for a fatal error.
511 if (!CdCommonClosePrivate( IrpContext
, Vcb
, Fcb
, UserReference
, TRUE
)) {
514 // If we didn't complete the request then post the request as needed.
517 CdQueueClose( IrpContext
, Fcb
, UserReference
, FALSE
);
521 // Check whether we should be dismounting the volume and then complete
525 } else if (PotentialVcbTeardown
) {
527 CdCheckForDismount( IrpContext
, Vcb
, FALSE
);
532 // Always complete this request with STATUS_SUCCESS.
535 CdCompleteRequest( IrpContext
, Irp
, STATUS_SUCCESS
);
537 if (PotentialVcbTeardown
) {
539 CdReleaseCdData( IrpContext
);
543 // Always return STATUS_SUCCESS for closes.
546 return STATUS_SUCCESS
;
551 // Local support routine
555 CdCommonClosePrivate (
556 IN PIRP_CONTEXT IrpContext
,
559 IN ULONG UserReference
,
567 This is the worker routine for the close operation. We can be called in
568 an Fsd thread or from a worker Fsp thread. If called from the Fsd thread
569 then we acquire the resources without waiting. Otherwise we know it is
572 We check to see whether we should post this request to the delayed close
573 queue. If we are to process the close here then we acquire the Vcb and
574 Fcb. We will adjust the counts and call our teardown routine to see
575 if any of the structures should go away.
579 Vcb - Vcb for this volume.
581 Fcb - Fcb for this request.
583 UserReference - Number of user references for this file object. This is
584 zero for an internal stream.
586 FromFsd - This request was called from an Fsd thread. Indicates whether
587 we should wait to acquire resources.
589 DelayedClose - Address to store whether we should try to put this on
590 the delayed close queue. Ignored if this routine can process this
595 BOOLEAN - TRUE if this thread processed the close, FALSE otherwise.
604 ASSERT_IRP_CONTEXT( IrpContext
);
608 // Try to acquire the Vcb and Fcb. If we can't acquire them then return
609 // and let our caller know he should post the request to the async
613 if (CdAcquireVcbShared( IrpContext
, Vcb
, FromFsd
)) {
615 if (!CdAcquireFcbExclusive( IrpContext
, Fcb
, FromFsd
)) {
618 // We couldn't get the Fcb. Release the Vcb and let our caller
619 // know to post this request.
622 CdReleaseVcb( IrpContext
, Vcb
);
627 // We didn't get the Vcb. Let our caller know to post this request.
636 // Lock the Vcb and decrement the reference counts.
639 CdLockVcb( IrpContext
, Vcb
);
640 CdDecrementReferenceCounts( IrpContext
, Fcb
, 1, UserReference
);
641 CdUnlockVcb( IrpContext
, Vcb
);
644 // Call our teardown routine to see if this object can go away.
645 // If we don't remove the Fcb then release it.
648 CdTeardownStructures( IrpContext
, Fcb
, &RemovedFcb
);
652 CdReleaseFcb( IrpContext
, Fcb
);
656 // Release the Vcb and return to our caller. Let him know we completed
660 CdReleaseVcb( IrpContext
, Vcb
);
666 NTAPI
/* ReactOS Change: GCC Does not support STDCALL by default */
668 IN PDEVICE_OBJECT DeviceObject
,
675 Worker routine to call CsFspClose.
679 DeviceObject - Filesystem registration device object
681 Context - Callers context
696 IN PIRP_CONTEXT IrpContext
,
698 IN ULONG UserReference
,
699 IN BOOLEAN DelayedClose
706 This routine is called to queue a request to either the async or delayed
707 close queue. For the delayed queue we need to allocate a smaller
708 structure to contain the information about the file object. We do
709 that so we don't put the larger IrpContext structures into this long
710 lived queue. If we can allocate this structure then we put this
711 on the async queue instead.
715 Fcb - Fcb for this file object.
717 UserReference - Number of user references for this file object. This is
718 zero for an internal stream.
720 DelayedClose - Indicates whether this should go on the async or delayed
730 PIRP_CONTEXT_LITE IrpContextLite
= NULL
;
731 BOOLEAN StartWorker
= FALSE
;
735 ASSERT_IRP_CONTEXT( IrpContext
);
739 // Start with the delayed queue request. We can move this to the async
740 // queue if there is an allocation failure.
746 // Try to allocate non-paged pool for the IRP_CONTEXT_LITE.
749 IrpContextLite
= CdCreateIrpContextLite( IrpContext
);
753 // We want to clear the top level context in this thread if
754 // necessary. Call our cleanup routine to do the work.
757 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_MORE_PROCESSING
);
758 CdCleanupIrpContext( IrpContext
, TRUE
);
761 // Synchronize with the CdData lock.
767 // If we have an IrpContext then put the request on the delayed close queue.
770 if (IrpContextLite
!= NULL
) {
773 // Initialize the IrpContextLite.
776 IrpContextLite
->NodeTypeCode
= CDFS_NTC_IRP_CONTEXT_LITE
;
777 IrpContextLite
->NodeByteSize
= sizeof( IRP_CONTEXT_LITE
);
778 IrpContextLite
->Fcb
= Fcb
;
779 IrpContextLite
->UserReference
= UserReference
;
780 IrpContextLite
->RealDevice
= IrpContext
->RealDevice
;
783 // Add this to the delayed close list and increment
787 InsertTailList( &CdData
.DelayedCloseQueue
,
788 &IrpContextLite
->DelayedCloseLinks
);
790 CdData
.DelayedCloseCount
+= 1;
793 // If we are above our threshold then start the delayed
797 if (CdData
.DelayedCloseCount
> CdData
.MaxDelayedCloseCount
) {
799 CdData
.ReduceDelayedClose
= TRUE
;
801 if (!CdData
.FspCloseActive
) {
803 CdData
.FspCloseActive
= TRUE
;
809 // Unlock the CdData.
815 // Cleanup the IrpContext.
818 CdCompleteRequest( IrpContext
, NULL
, STATUS_SUCCESS
);
821 // Otherwise drop into the async case below.
827 // Store the information about the file object into the IrpContext.
830 IrpContext
->Irp
= (PIRP
) Fcb
;
831 IrpContext
->ExceptionStatus
= (NTSTATUS
) UserReference
;
834 // Add this to the async close list and increment the count.
837 InsertTailList( &CdData
.AsyncCloseQueue
,
838 &IrpContext
->WorkQueueItem
.List
);
840 CdData
.AsyncCloseCount
+= 1;
843 // Remember to start the Fsp close thread if not currently started.
846 if (!CdData
.FspCloseActive
) {
848 CdData
.FspCloseActive
= TRUE
;
854 // Unlock the CdData.
861 // Start the FspClose thread if we need to.
866 IoQueueWorkItem( CdData
.CloseItem
, CdCloseWorker
, CriticalWorkQueue
, NULL
);
870 // Return to our caller.
878 // Local support routine
892 This routine is called to scan the async and delayed close queues looking
893 for a suitable entry. If the Vcb is specified then we scan both queues
894 looking for an entry with the same Vcb. Otherwise we will look in the
895 async queue first for any close item. If none found there then we look
896 in the delayed close queue provided that we have triggered the delayed
901 PIRP_CONTEXT - NULL if no work item found. Otherwise it is the pointer to
902 either the IrpContext or IrpContextLite for this request.
907 PIRP_CONTEXT IrpContext
= NULL
;
908 PIRP_CONTEXT NextIrpContext
;
909 PIRP_CONTEXT_LITE NextIrpContextLite
;
915 ASSERT_OPTIONAL_VCB( Vcb
);
918 // Lock the CdData to perform the scan.
924 // First check the list of async closes.
927 Entry
= CdData
.AsyncCloseQueue
.Flink
;
929 while (Entry
!= &CdData
.AsyncCloseQueue
) {
932 // Extract the IrpContext.
935 NextIrpContext
= CONTAINING_RECORD( Entry
,
937 WorkQueueItem
.List
);
940 // If no Vcb was specified or this Vcb is for our volume
941 // then perform the close.
944 if (!ARGUMENT_PRESENT( Vcb
) || (NextIrpContext
->Vcb
== Vcb
)) {
946 RemoveEntryList( Entry
);
947 CdData
.AsyncCloseCount
-= 1;
949 IrpContext
= NextIrpContext
;
954 // Move to the next entry.
957 Entry
= Entry
->Flink
;
961 // If we didn't find anything look through the delayed close
964 // We will only check the delayed close queue if we were given
965 // a Vcb or the delayed close operation is active.
968 if ((IrpContext
== NULL
) &&
969 (ARGUMENT_PRESENT( Vcb
) ||
970 (CdData
.ReduceDelayedClose
&&
971 (CdData
.DelayedCloseCount
> CdData
.MinDelayedCloseCount
)))) {
973 Entry
= CdData
.DelayedCloseQueue
.Flink
;
975 while (Entry
!= &CdData
.DelayedCloseQueue
) {
978 // Extract the IrpContext.
981 NextIrpContextLite
= CONTAINING_RECORD( Entry
,
986 // If no Vcb was specified or this Vcb is for our volume
987 // then perform the close.
990 if (!ARGUMENT_PRESENT( Vcb
) || (NextIrpContextLite
->Fcb
->Vcb
== Vcb
)) {
992 RemoveEntryList( Entry
);
993 CdData
.DelayedCloseCount
-= 1;
995 IrpContext
= (PIRP_CONTEXT
) NextIrpContextLite
;
1000 // Move to the next entry.
1003 Entry
= Entry
->Flink
;
1008 // If the Vcb wasn't specified and we couldn't find an entry
1009 // then turn off the Fsp thread.
1012 if (!ARGUMENT_PRESENT( Vcb
) && (IrpContext
== NULL
)) {
1014 CdData
.FspCloseActive
= FALSE
;
1015 CdData
.ReduceDelayedClose
= FALSE
;
1019 // Unlock the CdData.