xref: /reactos/drivers/filesystems/cdfs/close.c (revision 4561998a)
1 /*++
2 
3 Copyright (c) 1989-2000 Microsoft Corporation
4 
5 Module Name:
6 
7     Close.c
8 
9 Abstract:
10 
11     This module implements the File Close routine for Cdfs called by the
12     Fsd/Fsp dispatch routines.
13 
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.
21 
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.
27 
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.
33 
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.
41 
42 
43 --*/
44 
45 #include "cdprocs.h"
46 
47 //
48 //  The Bug check file id for this module
49 //
50 
51 #define BugCheckFileId                   (CDFS_BUG_CHECK_CLOSE)
52 
53 //
54 //  Local support routines
55 //
56 
57 _Requires_lock_held_(_Global_critical_region_)
58 BOOLEAN
59 CdCommonClosePrivate (
60     _In_ PIRP_CONTEXT IrpContext,
61     _In_ PVCB Vcb,
62     _In_ PFCB Fcb,
63     _In_ ULONG UserReference,
64     _In_ BOOLEAN FromFsd
65     );
66 
67 VOID
68 CdQueueClose (
69     _In_ PIRP_CONTEXT IrpContext,
70     _In_ PFCB Fcb,
71     _In_ ULONG UserReference,
72     _In_ BOOLEAN DelayedClose
73     );
74 
75 PIRP_CONTEXT
76 CdRemoveClose (
77     _In_opt_ PVCB Vcb
78     );
79 
80 //  Tell prefast this is a workitem routine
81 IO_WORKITEM_ROUTINE CdCloseWorker;
82 
83 VOID
84 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
85 CdCloseWorker (
86     _In_ PDEVICE_OBJECT DeviceObject,
87     _In_opt_ PVOID Context
88     );
89 
90 #ifdef ALLOC_PRAGMA
91 #pragma alloc_text(PAGE, CdFspClose)
92 #pragma alloc_text(PAGE, CdCommonClose)
93 #pragma alloc_text(PAGE, CdCommonClosePrivate)
94 #pragma alloc_text(PAGE, CdQueueClose)
95 #pragma alloc_text(PAGE, CdRemoveClose)
96 #pragma alloc_text(PAGE, CdCloseWorker)
97 #endif
98 
99 
100 VOID
101 CdFspClose (
102     _In_opt_ PVCB Vcb
103     )
104 
105 /*++
106 
107 Routine Description:
108 
109     This routine is called to process the close queues in the CdData.  If the
110     Vcb is passed then we want to remove all of the closes for this Vcb.
111     Otherwise we will do as many of the delayed closes as we need to do.
112 
113 Arguments:
114 
115     Vcb - If specified then we are looking for all of the closes for the
116         given Vcb.
117 
118 Return Value:
119 
120     None
121 
122 --*/
123 
124 {
125     PIRP_CONTEXT IrpContext;
126     IRP_CONTEXT StackIrpContext;
127 
128     THREAD_CONTEXT ThreadContext = {0};
129 
130     PFCB Fcb;
131     ULONG UserReference;
132 
133     ULONG VcbHoldCount = 0;
134     PVCB CurrentVcb = NULL;
135 
136     BOOLEAN PotentialVcbTeardown = FALSE;
137 
138     PAGED_CODE();
139 
140     FsRtlEnterFileSystem();
141 
142     //
143     //  Continue processing until there are no more closes to process.
144     //
145 
146     while ((IrpContext = CdRemoveClose( Vcb )) != NULL) {
147 
148         //
149         //  If we don't have an IrpContext then use the one on the stack.
150         //  Initialize it for this request.
151         //
152 
153         if (SafeNodeType( IrpContext ) != CDFS_NTC_IRP_CONTEXT ) {
154 
155             //
156             //  Update the local values from the IrpContextLite.
157             //
158 
159             Fcb = ((PIRP_CONTEXT_LITE) IrpContext)->Fcb;
160             UserReference = ((PIRP_CONTEXT_LITE) IrpContext)->UserReference;
161 
162             //
163             //  Update the stack irp context with the values from the
164             //  IrpContextLite.
165             //
166 
167             CdInitializeStackIrpContext( &StackIrpContext,
168                                          (PIRP_CONTEXT_LITE) IrpContext );
169 
170             //
171             //  Free the IrpContextLite.
172             //
173 
174             CdFreeIrpContextLite( *(PVOID*)&IrpContext ); /* ReactOS Change: GCC "error: invalid lvalue in unary '&'" */
175 
176             //
177             //  Remember we have the IrpContext from the stack.
178             //
179 
180             IrpContext = &StackIrpContext;
181 
182         //
183         //  Otherwise cleanup the existing IrpContext.
184         //
185 
186         } else {
187 
188             //
189             //  Remember the Fcb and user reference count.
190             //
191 
192             Fcb = (PFCB) IrpContext->Irp;
193             IrpContext->Irp = NULL;
194 
195             UserReference = (ULONG) IrpContext->ExceptionStatus;
196             IrpContext->ExceptionStatus = STATUS_SUCCESS;
197         }
198 
199         _Analysis_assume_(Fcb != NULL && Fcb->Vcb != NULL);
200 
201         //
202         //  We have an IrpContext.  Now we need to set the top level thread
203         //  context.
204         //
205 
206         SetFlag( IrpContext->Flags, IRP_CONTEXT_FSP_FLAGS );
207 
208         //
209         //  If we were given a Vcb then there is a request on top of this.
210         //
211 
212         if (ARGUMENT_PRESENT( Vcb )) {
213 
214             ClearFlag( IrpContext->Flags,
215                        IRP_CONTEXT_FLAG_TOP_LEVEL | IRP_CONTEXT_FLAG_TOP_LEVEL_CDFS );
216         }
217 
218         CdSetThreadContext( IrpContext, &ThreadContext );
219 
220         //
221         //  If we have hit the maximum number of requests to process without
222         //  releasing the Vcb then release the Vcb now.  If we are holding
223         //  a different Vcb to this one then release the previous Vcb.
224         //
225         //  In either case acquire the current Vcb.
226         //
227         //  We use the MinDelayedCloseCount from the CdData since it is
228         //  a convenient value based on the system size.  Only thing we are trying
229         //  to do here is prevent this routine starving other threads which
230         //  may need this Vcb exclusively.
231         //
232         //  Note that the check for potential teardown below is unsafe.  We'll
233         //  repeat later within the cddata lock.
234         //
235 
236         PotentialVcbTeardown = !ARGUMENT_PRESENT( Vcb ) &&
237                                (Fcb->Vcb->VcbCondition != VcbMounted) &&
238                                (Fcb->Vcb->VcbCondition != VcbMountInProgress) &&
239                                (Fcb->Vcb->VcbCleanup == 0);
240 
241         if (PotentialVcbTeardown ||
242             (VcbHoldCount > CdData.MinDelayedCloseCount) ||
243             (Fcb->Vcb != CurrentVcb)) {
244 
245             if (CurrentVcb != NULL) {
246 
247                 CdReleaseVcb( IrpContext, CurrentVcb );
248             }
249 
250             if (PotentialVcbTeardown) {
251 
252                 CdAcquireCdData( IrpContext );
253 
254                 //
255                 //  Repeat the checks with global lock held.  The volume could have
256                 //  been remounted while we didn't hold the lock.
257                 //
258 
259                 PotentialVcbTeardown = !ARGUMENT_PRESENT( Vcb ) &&
260                                        (Fcb->Vcb->VcbCondition != VcbMounted) &&
261                                        (Fcb->Vcb->VcbCondition != VcbMountInProgress) &&
262                                        (Fcb->Vcb->VcbCleanup == 0);
263 
264                 if (!PotentialVcbTeardown)  {
265 
266                     CdReleaseCdData( IrpContext);
267                 }
268             }
269 
270             CurrentVcb = Fcb->Vcb;
271 
272             _Analysis_assume_( CurrentVcb != NULL );
273 
274             CdAcquireVcbShared( IrpContext, CurrentVcb, FALSE );
275 
276             VcbHoldCount = 0;
277 
278         } else {
279 
280             VcbHoldCount += 1;
281         }
282 
283         //
284         //  Call our worker routine to perform the close operation.
285         //
286 
287         CdCommonClosePrivate( IrpContext, CurrentVcb, Fcb, UserReference, FALSE );
288 
289         //
290         //  If the reference count on this Vcb is below our residual reference
291         //  then check if we should dismount the volume.
292         //
293 
294         if (PotentialVcbTeardown) {
295 
296             CdReleaseVcb( IrpContext, CurrentVcb );
297             CdCheckForDismount( IrpContext, CurrentVcb, FALSE );
298 
299             CurrentVcb = NULL;
300 
301             CdReleaseCdData( IrpContext );
302             PotentialVcbTeardown = FALSE;
303         }
304 
305         //
306         //  Complete the current request to cleanup the IrpContext.
307         //
308 
309         CdCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );
310     }
311 
312     //
313     //  Release any Vcb we may still hold.
314     //
315 
316     if (CurrentVcb != NULL) {
317 
318         CdReleaseVcb( IrpContext, CurrentVcb );
319 
320     }
321 
322 #ifdef _MSC_VER
323 #pragma prefast(suppress:26165, "Esp:1153")
324 #endif
325     FsRtlExitFileSystem();
326 }
327 
328 _Requires_lock_held_(_Global_critical_region_)
329 NTSTATUS
330 CdCommonClose (
331     _Inout_ PIRP_CONTEXT IrpContext,
332     _Inout_ PIRP Irp
333     )
334 
335 /*++
336 
337 Routine Description:
338 
339     This routine is the Fsd entry for the close operation.  We decode the file
340     object to find the CDFS structures and type of open.  We call our internal
341     worker routine to perform the actual work.  If the work wasn't completed
342     then we post to one of our worker queues.  The Ccb isn't needed after this
343     point so we delete the Ccb and return STATUS_SUCCESS to our caller in all
344     cases.
345 
346 Arguments:
347 
348     Irp - Supplies the Irp to process
349 
350 Return Value:
351 
352     STATUS_SUCCESS
353 
354 --*/
355 
356 {
357     TYPE_OF_OPEN TypeOfOpen;
358 
359     PVCB Vcb;
360     PFCB Fcb;
361     PCCB Ccb;
362     ULONG UserReference = 0;
363 
364     BOOLEAN PotentialVcbTeardown = FALSE;
365 
366     PAGED_CODE();
367 
368     ASSERT_IRP_CONTEXT( IrpContext );
369     ASSERT_IRP( Irp );
370 
371     //
372     //  If we were called with our file system device object instead of a
373     //  volume device object, just complete this request with STATUS_SUCCESS.
374     //
375 
376     if (IrpContext->Vcb == NULL) {
377 
378         CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
379         return STATUS_SUCCESS;
380     }
381 
382     //
383     //  Decode the file object to get the type of open and Fcb/Ccb.
384     //
385 
386     TypeOfOpen = CdDecodeFileObject( IrpContext,
387                                      IoGetCurrentIrpStackLocation( Irp )->FileObject,
388                                      &Fcb,
389                                      &Ccb );
390 
391     //
392     //  No work to do for unopened file objects.
393     //
394 
395     if (TypeOfOpen == UnopenedFileObject) {
396 
397         CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
398 
399         return STATUS_SUCCESS;
400     }
401 
402     Vcb = Fcb->Vcb;
403 
404     //
405     //  Clean up any CCB associated with this open.
406     //
407 
408     if (Ccb != NULL) {
409 
410         UserReference = 1;
411 
412         //
413         //  We can always deallocate the Ccb if present.
414         //
415 
416         CdDeleteCcb( IrpContext, Ccb );
417     }
418 
419     //
420     //  If this is the last reference to a user file or directory on a
421     //  currently mounted volume, then post it to the delayed close queue.  Note
422     //  that the VcbCondition check is unsafe,  but it doesn't really matter -
423     //  we just might delay the volume teardown a little by posting this close.
424     //
425 
426     if ((Vcb->VcbCondition == VcbMounted) &&
427         (Fcb->FcbReference == 1) &&
428         ((TypeOfOpen == UserFileOpen) ||
429          (TypeOfOpen == UserDirectoryOpen))) {
430 
431         CdQueueClose( IrpContext, Fcb, UserReference, TRUE );
432         IrpContext = NULL;
433 
434     //
435     //  Otherwise try to process this close.  Post to the async close queue
436     //  if we can't acquire all of the resources.
437     //
438 
439     }
440     else {
441 
442         //
443         //  If we may be dismounting this volume then acquire the CdData
444         //  resource.
445         //
446         //  Since we now must make volumes go away as soon as reasonable after
447         //  the last user handles closes, key off of the cleanup count.  It is
448         //  OK to do this more than neccesary.  Since this Fcb could be holding
449         //  a number of other Fcbs (and thus their references), a simple check
450         //  on reference count is not appropriate.
451         //
452         //  Do an unsafe check first to avoid taking the (global) cddata lock in the
453         //  common case.
454         //
455 
456         if ((Vcb->VcbCleanup == 0) &&
457             (Vcb->VcbCondition != VcbMounted))  {
458 
459             //
460             //  Possible dismount.  Acquire CdData to synchronise with the remount path
461             //  before looking at the vcb condition again.
462             //
463 
464             CdAcquireCdData( IrpContext );
465 
466             if ((Vcb->VcbCleanup == 0) &&
467                 (Vcb->VcbCondition != VcbMounted) &&
468                 (Vcb->VcbCondition != VcbMountInProgress) &&
469                 FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TOP_LEVEL_CDFS ))  {
470 
471                 PotentialVcbTeardown = TRUE;
472             }
473             else {
474 
475                 //
476                 //  We can't dismount this volume now,  there are other references or
477                 //  it's just been remounted.
478                 //
479             }
480 
481             //
482             //  Drop the global lock if we don't need it anymore.
483             //
484 
485             if (!PotentialVcbTeardown) {
486 
487                 CdReleaseCdData( IrpContext );
488             }
489         }
490 
491         //
492         //  Call the worker routine to perform the actual work.  This routine
493         //  should never raise except for a fatal error.
494         //
495 
496         if (!CdCommonClosePrivate( IrpContext, Vcb, Fcb, UserReference, TRUE )) {
497 
498             //
499             //  If we didn't complete the request then post the request as needed.
500             //
501 
502             CdQueueClose( IrpContext, Fcb, UserReference, FALSE );
503             IrpContext = NULL;
504 
505         //
506         //  Check whether we should be dismounting the volume and then complete
507         //  the request.
508         //
509 
510         }
511         else if (PotentialVcbTeardown) {
512 
513             CdCheckForDismount( IrpContext, Vcb, FALSE );
514         }
515     }
516 
517     //
518     //  Always complete this request with STATUS_SUCCESS.
519     //
520 
521     CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
522 
523     if (PotentialVcbTeardown) {
524 
525         CdReleaseCdData( IrpContext );
526     }
527 
528     //
529     //  Always return STATUS_SUCCESS for closes.
530     //
531 
532     return STATUS_SUCCESS;
533 }
534 
535 
536 //
537 //  Local support routine
538 //
539 
540 _Requires_lock_held_(_Global_critical_region_)
541 BOOLEAN
542 CdCommonClosePrivate (
543     _In_ PIRP_CONTEXT IrpContext,
544     _In_ PVCB Vcb,
545     _In_ PFCB Fcb,
546     _In_ ULONG UserReference,
547     _In_ BOOLEAN FromFsd
548     )
549 
550 /*++
551 
552 Routine Description:
553 
554     This is the worker routine for the close operation.  We can be called in
555     an Fsd thread or from a worker Fsp thread.  If called from the Fsd thread
556     then we acquire the resources without waiting.  Otherwise we know it is
557     safe to wait.
558 
559     We check to see whether we should post this request to the delayed close
560     queue.  If we are to process the close here then we acquire the Vcb and
561     Fcb.  We will adjust the counts and call our teardown routine to see
562     if any of the structures should go away.
563 
564 Arguments:
565 
566     Vcb - Vcb for this volume.
567 
568     Fcb - Fcb for this request.
569 
570     UserReference - Number of user references for this file object.  This is
571         zero for an internal stream.
572 
573     FromFsd - This request was called from an Fsd thread.  Indicates whether
574         we should wait to acquire resources.
575 
576     DelayedClose - Address to store whether we should try to put this on
577         the delayed close queue.  Ignored if this routine can process this
578         close.
579 
580 Return Value:
581 
582     BOOLEAN - TRUE if this thread processed the close, FALSE otherwise.
583 
584 --*/
585 
586 {
587     BOOLEAN RemovedFcb;
588 
589     PAGED_CODE();
590 
591     ASSERT_IRP_CONTEXT( IrpContext );
592     ASSERT_FCB( Fcb );
593 
594     //
595     //  Try to acquire the Vcb and Fcb.  If we can't acquire them then return
596     //  and let our caller know he should post the request to the async
597     //  queue.
598     //
599 
600     if (CdAcquireVcbShared( IrpContext, Vcb, FromFsd )) {
601 
602         if (!CdAcquireFcbExclusive( IrpContext, Fcb, FromFsd )) {
603 
604             //
605             //  We couldn't get the Fcb.  Release the Vcb and let our caller
606             //  know to post this request.
607             //
608 
609             CdReleaseVcb( IrpContext, Vcb );
610             return FALSE;
611         }
612 
613     //
614     //  We didn't get the Vcb.  Let our caller know to post this request.
615     //
616 
617     } else {
618 
619         return FALSE;
620     }
621 
622     //
623     //  Lock the Vcb and decrement the reference counts.
624     //
625 
626     CdLockVcb( IrpContext, Vcb );
627     CdDecrementReferenceCounts( IrpContext, Fcb, 1, UserReference );
628     CdUnlockVcb( IrpContext, Vcb );
629 
630     //
631     //  Call our teardown routine to see if this object can go away.
632     //  If we don't remove the Fcb then release it.
633     //
634 
635     CdTeardownStructures( IrpContext, Fcb, &RemovedFcb );
636 
637     if (!RemovedFcb) {
638 
639         CdReleaseFcb( IrpContext, Fcb );
640     }
641     else {
642         _Analysis_assume_lock_not_held_(Fcb->FcbNonpaged->FcbResource);
643     }
644 
645     //
646     //  Release the Vcb and return to our caller.  Let him know we completed
647     //  this request.
648     //
649 
650     CdReleaseVcb( IrpContext, Vcb );
651 
652     return TRUE;
653 }
654 
655 VOID
656 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
657 CdCloseWorker (
658     _In_ PDEVICE_OBJECT DeviceObject,
659     _In_opt_ PVOID Context
660     )
661 /*++
662 
663 Routine Description:
664 
665     Worker routine to call CsFspClose.
666 
667 Arguments:
668 
669     DeviceObject - Filesystem registration device object
670 
671     Context - Callers context
672 
673 Return Value:
674 
675     None
676 
677 --*/
678 
679 {
680     PAGED_CODE();
681 
682     UNREFERENCED_PARAMETER( DeviceObject );
683     UNREFERENCED_PARAMETER( Context );
684 
685     CdFspClose (NULL);
686 }
687 
688 
689 VOID
690 CdQueueClose (
691     _In_ PIRP_CONTEXT IrpContext,
692     _In_ PFCB Fcb,
693     _In_ ULONG UserReference,
694     _In_ BOOLEAN DelayedClose
695     )
696 
697 /*++
698 
699 Routine Description:
700 
701     This routine is called to queue a request to either the async or delayed
702     close queue.  For the delayed queue we need to allocate a smaller
703     structure to contain the information about the file object.  We do
704     that so we don't put the larger IrpContext structures into this long
705     lived queue.  If we can allocate this structure then we put this
706     on the async queue instead.
707 
708 Arguments:
709 
710     Fcb - Fcb for this file object.
711 
712     UserReference - Number of user references for this file object.  This is
713         zero for an internal stream.
714 
715     DelayedClose - Indicates whether this should go on the async or delayed
716         close queue.
717 
718 Return Value:
719 
720     None
721 
722 --*/
723 
724 {
725     PIRP_CONTEXT_LITE IrpContextLite = NULL;
726     BOOLEAN StartWorker = FALSE;
727 
728     PAGED_CODE();
729 
730     ASSERT_IRP_CONTEXT( IrpContext );
731     ASSERT_FCB( Fcb );
732 
733     //
734     //  Start with the delayed queue request.  We can move this to the async
735     //  queue if there is an allocation failure.
736     //
737 
738     if (DelayedClose) {
739 
740         //
741         //  Try to allocate non-paged pool for the IRP_CONTEXT_LITE.
742         //
743 
744         IrpContextLite = CdCreateIrpContextLite( IrpContext );
745     }
746 
747     //
748     //  We want to clear the top level context in this thread if
749     //  necessary.  Call our cleanup routine to do the work.
750     //
751 
752     SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_MORE_PROCESSING );
753     CdCleanupIrpContext( IrpContext, TRUE );
754 
755     //
756     //  Synchronize with the CdData lock.
757     //
758 
759     CdLockCdData();
760 
761     //
762     //  If we have an IrpContext then put the request on the delayed close queue.
763     //
764 
765     if (IrpContextLite != NULL) {
766 
767         //
768         //  Initialize the IrpContextLite.
769         //
770 
771         IrpContextLite->NodeTypeCode = CDFS_NTC_IRP_CONTEXT_LITE;
772         IrpContextLite->NodeByteSize = sizeof( IRP_CONTEXT_LITE );
773         IrpContextLite->Fcb = Fcb;
774         IrpContextLite->UserReference = UserReference;
775         IrpContextLite->RealDevice = IrpContext->RealDevice;
776 
777         //
778         //  Add this to the delayed close list and increment
779         //  the count.
780         //
781 
782         InsertTailList( &CdData.DelayedCloseQueue,
783                         &IrpContextLite->DelayedCloseLinks );
784 
785         CdData.DelayedCloseCount += 1;
786 
787         //
788         //  If we are above our threshold then start the delayed
789         //  close operation.
790         //
791 
792         if (CdData.DelayedCloseCount > CdData.MaxDelayedCloseCount) {
793 
794             CdData.ReduceDelayedClose = TRUE;
795 
796             if (!CdData.FspCloseActive) {
797 
798                 CdData.FspCloseActive = TRUE;
799                 StartWorker = TRUE;
800             }
801         }
802 
803         //
804         //  Unlock the CdData.
805         //
806 
807         CdUnlockCdData();
808 
809         //
810         //  Cleanup the IrpContext.
811         //
812 
813         CdCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );
814 
815     //
816     //  Otherwise drop into the async case below.
817     //
818 
819     } else {
820 
821         //
822         //  Store the information about the file object into the IrpContext.
823         //
824 
825         IrpContext->Irp = (PIRP) Fcb;
826         IrpContext->ExceptionStatus = (NTSTATUS) UserReference;
827 
828         //
829         //  Add this to the async close list and increment the count.
830         //
831 
832         InsertTailList( &CdData.AsyncCloseQueue,
833                         &IrpContext->WorkQueueItem.List );
834 
835         CdData.AsyncCloseCount += 1;
836 
837         //
838         //  Remember to start the Fsp close thread if not currently started.
839         //
840 
841         if (!CdData.FspCloseActive) {
842 
843             CdData.FspCloseActive = TRUE;
844 
845             StartWorker = TRUE;
846         }
847 
848         //
849         //  Unlock the CdData.
850         //
851 
852         CdUnlockCdData();
853     }
854 
855     //
856     //  Start the FspClose thread if we need to.
857     //
858 
859     if (StartWorker) {
860 
861         IoQueueWorkItem( CdData.CloseItem, CdCloseWorker, CriticalWorkQueue, NULL );
862     }
863 
864     //
865     //  Return to our caller.
866     //
867 
868     return;
869 }
870 
871 
872 //
873 //  Local support routine
874 //
875 
876 PIRP_CONTEXT
877 CdRemoveClose (
878     _In_opt_ PVCB Vcb
879     )
880 
881 /*++
882 
883 Routine Description:
884 
885 Arguments:
886 
887     This routine is called to scan the async and delayed close queues looking
888     for a suitable entry.  If the Vcb is specified then we scan both queues
889     looking for an entry with the same Vcb.  Otherwise we will look in the
890     async queue first for any close item.  If none found there then we look
891     in the delayed close queue provided that we have triggered the delayed
892     close operation.
893 
894 Return Value:
895 
896     PIRP_CONTEXT - NULL if no work item found.  Otherwise it is the pointer to
897         either the IrpContext or IrpContextLite for this request.
898 
899 --*/
900 
901 {
902     PIRP_CONTEXT IrpContext = NULL;
903     PIRP_CONTEXT NextIrpContext;
904     PIRP_CONTEXT_LITE NextIrpContextLite;
905 
906     PLIST_ENTRY Entry;
907 
908     PAGED_CODE();
909 
910     ASSERT_OPTIONAL_VCB( Vcb );
911 
912     //
913     //  Lock the CdData to perform the scan.
914     //
915 
916     CdLockCdData();
917 
918     //
919     //  First check the list of async closes.
920     //
921 
922     Entry = CdData.AsyncCloseQueue.Flink;
923 
924     while (Entry != &CdData.AsyncCloseQueue) {
925 
926         //
927         //  Extract the IrpContext.
928         //
929 
930         NextIrpContext = CONTAINING_RECORD( Entry,
931                                             IRP_CONTEXT,
932                                             WorkQueueItem.List );
933 
934         //
935         //  If no Vcb was specified or this Vcb is for our volume
936         //  then perform the close.
937         //
938 
939         if (!ARGUMENT_PRESENT( Vcb ) || (NextIrpContext->Vcb == Vcb)) {
940 
941             RemoveEntryList( Entry );
942             CdData.AsyncCloseCount -= 1;
943 
944             IrpContext = NextIrpContext;
945             break;
946         }
947 
948         //
949         //  Move to the next entry.
950         //
951 
952         Entry = Entry->Flink;
953     }
954 
955     //
956     //  If we didn't find anything look through the delayed close
957     //  queue.
958     //
959     //  We will only check the delayed close queue if we were given
960     //  a Vcb or the delayed close operation is active.
961     //
962 
963     if ((IrpContext == NULL) &&
964         (ARGUMENT_PRESENT( Vcb ) ||
965          (CdData.ReduceDelayedClose &&
966           (CdData.DelayedCloseCount > CdData.MinDelayedCloseCount)))) {
967 
968         Entry = CdData.DelayedCloseQueue.Flink;
969 
970         while (Entry != &CdData.DelayedCloseQueue) {
971 
972             //
973             //  Extract the IrpContext.
974             //
975 
976             NextIrpContextLite = CONTAINING_RECORD( Entry,
977                                                     IRP_CONTEXT_LITE,
978                                                     DelayedCloseLinks );
979 
980             //
981             //  If no Vcb was specified or this Vcb is for our volume
982             //  then perform the close.
983             //
984 
985             if (!ARGUMENT_PRESENT( Vcb ) || (NextIrpContextLite->Fcb->Vcb == Vcb)) {
986 
987                 RemoveEntryList( Entry );
988                 CdData.DelayedCloseCount -= 1;
989 
990                 IrpContext = (PIRP_CONTEXT) NextIrpContextLite;
991                 break;
992             }
993 
994             //
995             //  Move to the next entry.
996             //
997 
998             Entry = Entry->Flink;
999         }
1000     }
1001 
1002     //
1003     //  If the Vcb wasn't specified and we couldn't find an entry
1004     //  then turn off the Fsp thread.
1005     //
1006 
1007     if (!ARGUMENT_PRESENT( Vcb ) && (IrpContext == NULL)) {
1008 
1009         CdData.FspCloseActive = FALSE;
1010         CdData.ReduceDelayedClose = FALSE;
1011     }
1012 
1013     //
1014     //  Unlock the CdData.
1015     //
1016 
1017     CdUnlockCdData();
1018 
1019     return IrpContext;
1020 }
1021 
1022 
1023 
1024