xref: /reactos/drivers/filesystems/fastfat/close.c (revision d82185f1)
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 Fat called by the
12     dispatch driver.
13 
14 
15 --*/
16 
17 #include "fatprocs.h"
18 
19 //
20 //  The Bug check file id for this module
21 //
22 
23 #define BugCheckFileId                   (FAT_BUG_CHECK_CLOSE)
24 
25 //
26 //  The local debug trace level
27 //
28 
29 #define Dbg                              (DEBUG_TRACE_CLOSE)
30 
31 ULONG FatMaxDelayedCloseCount;
32 
33 
34 #define FatAcquireCloseMutex() {                        \
35     NT_ASSERT(KeAreApcsDisabled());                     \
36     ExAcquireFastMutexUnsafe( &FatCloseQueueMutex );    \
37 }
38 
39 #define FatReleaseCloseMutex() {                        \
40     NT_ASSERT(KeAreApcsDisabled());                     \
41     ExReleaseFastMutexUnsafe( &FatCloseQueueMutex );    \
42 }
43 
44 //
45 //  Local procedure prototypes
46 //
47 
48 _Requires_lock_held_(_Global_critical_region_)
49 VOID
50 FatQueueClose (
51     IN PCLOSE_CONTEXT CloseContext,
52     IN BOOLEAN DelayClose
53     );
54 
55 _Requires_lock_held_(_Global_critical_region_)
56 PCLOSE_CONTEXT
57 FatRemoveClose (
58     PVCB Vcb OPTIONAL,
59     PVCB LastVcbHint OPTIONAL
60     );
61 
62 IO_WORKITEM_ROUTINE FatCloseWorker;
63 
64 VOID
65 NTAPI
66 FatCloseWorker (
67     _In_        PDEVICE_OBJECT DeviceObject,
68     _In_opt_    PVOID Context
69     );
70 
71 #ifdef ALLOC_PRAGMA
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)
77 #endif
78 
79 
80 _Function_class_(IRP_MJ_CLOSE)
81 _Function_class_(DRIVER_DISPATCH)
82 NTSTATUS
83 NTAPI
84 FatFsdClose (
85     _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
86     _Inout_ PIRP Irp
87     )
88 
89 /*++
90 
91 Routine Description:
92 
93     This routine implements the FSD part of Close.
94 
95 Arguments:
96 
97     VolumeDeviceObject - Supplies the volume device object where the
98         file exists
99 
100     Irp - Supplies the Irp being processed
101 
102 Return Value:
103 
104     NTSTATUS - The FSD status for the IRP
105 
106 --*/
107 
108 {
109     NTSTATUS Status = STATUS_SUCCESS;
110     PIO_STACK_LOCATION IrpSp;
111     PFILE_OBJECT FileObject;
112 
113     PVCB Vcb;
114     PFCB Fcb;
115     PCCB Ccb;
116     TYPE_OF_OPEN TypeOfOpen;
117 
118     BOOLEAN TopLevel;
119     BOOLEAN VcbDeleted = FALSE;
120 
121     PAGED_CODE();
122 
123     //
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
126     //
127 
128     if (FatDeviceIsFatFsdo( VolumeDeviceObject))  {
129 
130         Irp->IoStatus.Status = STATUS_SUCCESS;
131         Irp->IoStatus.Information = FILE_OPENED;
132 
133         IoCompleteRequest( Irp, IO_DISK_INCREMENT );
134 
135         return STATUS_SUCCESS;
136     }
137 
138     DebugTrace(+1, Dbg, "FatFsdClose\n", 0);
139 
140     //
141     //  Call the common Close routine
142     //
143 
144     FsRtlEnterFileSystem();
145 
146     TopLevel = FatIsIrpTopLevel( Irp );
147 
148     //
149     //  Get a pointer to the current stack location and the file object
150     //
151 
152     IrpSp = IoGetCurrentIrpStackLocation( Irp );
153 
154     FileObject = IrpSp->FileObject;
155 
156     //
157     //  Decode the file object and set the read-only bit in the Ccb.
158     //
159 
160     TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb );
161 
162     if (Ccb && IsFileObjectReadOnly(FileObject)) {
163 
164         SetFlag( Ccb->Flags, CCB_FLAG_READ_ONLY );
165     }
166 
167     _SEH2_TRY {
168 
169         PCLOSE_CONTEXT CloseContext = NULL;
170 
171         //
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.
175         //
176 
177         BOOLEAN Wait = TopLevel && (PsGetCurrentProcess() != FatData.OurProcess);
178 
179 #if (NTDDI_VERSION >= NTDDI_WIN8)
180 
181         //
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.
185         //
186 
187         if ((Fcb != NULL) &&
188             !FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE ) &&
189             FatIsFileOplockable( Fcb )) {
190 
191             //
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.
195             //
196 
197             FsRtlCheckOplockEx( FatGetFcbOplock(Fcb),
198                                 Irp,
199                                 0,
200                                 NULL,
201                                 NULL,
202                                 NULL );
203         }
204 #endif
205 
206         //
207         //  Metadata streams have had close contexts preallocated. Pull one out now, while we're
208         //  guaranteed the VCB exists.
209         //
210 
211         if ( (TypeOfOpen == VirtualVolumeFile) || (TypeOfOpen == DirectoryFile) || (TypeOfOpen == EaFile)
212             ) {
213 
214             CloseContext = FatAllocateCloseContext( Vcb );
215             NT_ASSERT( CloseContext != NULL );
216             CloseContext->Free = TRUE;
217 
218         }
219 
220         //
221         //  Call the common Close routine if we are not delaying this close.
222         //
223 
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)) {
229 
230             //
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.
233             //
234 
235             //
236             //  Metadata streams have had close contexts preallocated. If we have a user open,
237             //  pull the close context out of the Ccb.
238             //
239 
240             if( CloseContext == NULL ) {
241 
242                 //
243                 //  Free up any query template strings before using the close context fields,
244                 //  which overlap (union)
245                 //
246 
247                 FatDeallocateCcbStrings( Ccb );
248 
249                 CloseContext = &Ccb->CloseContext;
250                 CloseContext->Free = FALSE;
251 
252                 SetFlag( Ccb->Flags, CCB_FLAG_CLOSE_CONTEXT );
253             }
254 
255             //
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.
260             //
261 
262             CloseContext->Vcb = Vcb;
263             CloseContext->Fcb = Fcb;
264             CloseContext->TypeOfOpen = TypeOfOpen;
265 
266             //
267             //  Send it off, either to an ExWorkerThread or to the async
268             //  close list.
269             //
270 
271             FatQueueClose( CloseContext,
272                            (BOOLEAN)(Fcb && FlagOn(Fcb->FcbState, FCB_STATE_DELAY_CLOSE)));
273 
274         } else {
275 
276             //
277             //  The close proceeded synchronously, so for the metadata objects we
278             //  can now drop the close context we preallocated.
279             //
280 
281             if ((TypeOfOpen == VirtualVolumeFile) ||
282                 (TypeOfOpen == DirectoryFile) ||
283                 (TypeOfOpen == EaFile)
284                 ) {
285 
286                 if (CloseContext != NULL) {
287 
288                     ExFreePool( CloseContext );
289 
290                 }
291             }
292         }
293 
294         FatCompleteRequest( FatNull, Irp, Status );
295 
296     }
297     _SEH2_EXCEPT(FatExceptionFilter( NULL, _SEH2_GetExceptionInformation() )) {
298 
299         //
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.
303         //
304 
305         Status = FatProcessException( NULL, Irp, _SEH2_GetExceptionCode() );
306     } _SEH2_END;
307 
308     if (TopLevel) { IoSetTopLevelIrp( NULL ); }
309 
310     FsRtlExitFileSystem();
311 
312     //
313     //  And return to our caller
314     //
315 
316     DebugTrace(-1, Dbg, "FatFsdClose -> %08lx\n", Status);
317 
318     UNREFERENCED_PARAMETER( VolumeDeviceObject );
319 
320     return Status;
321 }
322 
323 VOID
324 NTAPI
325 FatCloseWorker (
326     _In_        PDEVICE_OBJECT DeviceObject,
327     _In_opt_    PVOID Context
328     )
329 /*++
330 
331 Routine Description:
332 
333     This routine is a shim between the IO worker package and FatFspClose.
334 
335 Arguments:
336 
337     DeviceObject - Registration device object, unused
338     Context - Context value, unused
339 
340 Return Value:
341 
342     None.
343 
344 --*/
345 {
346     PAGED_CODE();
347 
348     UNREFERENCED_PARAMETER( DeviceObject );
349 
350     FsRtlEnterFileSystem();
351 
352     FatFspClose (Context);
353 
354     FsRtlExitFileSystem();
355 }
356 
357 
358 _Requires_lock_held_(_Global_critical_region_)
359 VOID
360 FatFspClose (
361     IN PVCB Vcb OPTIONAL
362     )
363 
364 /*++
365 
366 Routine Description:
367 
368     This routine implements the FSP part of Close.
369 
370 Arguments:
371 
372     Vcb - If present, tells us to only close file objects opened on the
373         specified volume.
374 
375 Return Value:
376 
377     None.
378 
379 --*/
380 
381 {
382     PCLOSE_CONTEXT CloseContext;
383     PVCB CurrentVcb = NULL;
384     PVCB LastVcb = NULL;
385     BOOLEAN FreeContext = FALSE;
386     BOOLEAN TopLevel = FALSE;
387 
388     ULONG LoopsWithVcbHeld = 0;
389 
390     PAGED_CODE();
391 
392     DebugTrace(+1, Dbg, "FatFspClose\n", 0);
393 
394     //
395     //  Set the top level IRP for the true FSP operation.
396     //
397 
398     if (!ARGUMENT_PRESENT( Vcb )) {
399 
400         IoSetTopLevelIrp( (PIRP)FSRTL_FSP_TOP_LEVEL_IRP );
401         TopLevel = TRUE;
402     }
403 
404     while ((CloseContext = FatRemoveClose(Vcb, LastVcb)) != NULL) {
405 
406         //
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.
409         //
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.
413         //
414 
415         if (!ARGUMENT_PRESENT(Vcb)) {
416 
417             if (!FatData.ShutdownStarted) {
418 
419                 if (CloseContext->Vcb != CurrentVcb) {
420 
421                     LoopsWithVcbHeld = 0;
422 
423                     //
424                     //  Release a previously held Vcb, if any.
425                     //
426 
427                     if (CurrentVcb != NULL) {
428 
429                         ExReleaseResourceLite( &CurrentVcb->Resource);
430                     }
431 
432                     //
433                     //  Get the new Vcb.
434                     //
435 
436                     CurrentVcb = CloseContext->Vcb;
437                     (VOID)ExAcquireResourceExclusiveLite( &CurrentVcb->Resource, TRUE );
438 
439                 } else {
440 
441                     //
442                     //  Share the resource occasionally if we seem to be finding a lot
443                     //  of closes for a single volume.
444                     //
445 
446                     if (++LoopsWithVcbHeld >= 20) {
447 
448                         if (ExGetSharedWaiterCount( &CurrentVcb->Resource ) +
449                             ExGetExclusiveWaiterCount( &CurrentVcb->Resource )) {
450 
451                             ExReleaseResourceLite( &CurrentVcb->Resource);
452                             (VOID)ExAcquireResourceExclusiveLite( &CurrentVcb->Resource, TRUE );
453                         }
454 
455                         LoopsWithVcbHeld = 0;
456                     }
457                 }
458 
459                 //
460                 //  Now check the Open count.  We may be about to delete this volume!
461                 //
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
467                 //  own later.
468                 //
469 
470                 if (CurrentVcb->OpenFileCount <= 1) {
471                     ExReleaseResourceLite( &CurrentVcb->Resource);
472                     CurrentVcb = NULL;
473                 }
474             //
475             //  If shutdown has started while processing our list, drop the
476             //  current Vcb resource.
477             //
478 
479             } else if (CurrentVcb != NULL) {
480 
481                 ExReleaseResourceLite( &CurrentVcb->Resource);
482                 CurrentVcb = NULL;
483             }
484         }
485 
486         LastVcb = CurrentVcb;
487 
488         //
489         //  Call the common Close routine.  Protected in a try {} except {}
490         //
491 
492         _SEH2_TRY {
493 
494             //
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.
498             //
499 
500             FreeContext = CloseContext->Free;
501 
502             (VOID)FatCommonClose( CloseContext->Vcb,
503                                   CloseContext->Fcb,
504                                   (FreeContext ? NULL :
505                                                  CONTAINING_RECORD( CloseContext, CCB, CloseContext)),
506                                   CloseContext->TypeOfOpen,
507                                   TRUE,
508                                   TopLevel,
509                                   NULL );
510 
511         } _SEH2_EXCEPT(FatExceptionFilter( NULL, _SEH2_GetExceptionInformation() )) {
512 
513             //
514             //  Ignore anything we expect.
515             //
516 
517             NOTHING;
518         } _SEH2_END;
519 
520         //
521         //  Drop the context if it came from pool.
522         //
523 
524         if (FreeContext) {
525 
526             ExFreePool( CloseContext );
527         }
528     }
529 
530     //
531     //  Release a previously held Vcb, if any.
532     //
533 
534     if (CurrentVcb != NULL) {
535 
536         ExReleaseResourceLite( &CurrentVcb->Resource);
537     }
538 
539     //
540     //  Clean up the top level IRP hint if we owned it.
541     //
542 
543     if (!ARGUMENT_PRESENT( Vcb )) {
544 
545         IoSetTopLevelIrp( NULL );
546     }
547 
548     //
549     //  And return to our caller
550     //
551 
552     DebugTrace(-1, Dbg, "FatFspClose -> NULL\n", 0);
553 }
554 
555 
556 _Requires_lock_held_(_Global_critical_region_)
557 VOID
558 FatQueueClose (
559     IN PCLOSE_CONTEXT CloseContext,
560     IN BOOLEAN DelayClose
561     )
562 
563 /*++
564 
565 Routine Description:
566 
567     Enqueue a deferred close to one of the two delayed close queues.
568 
569 Arguments:
570 
571     CloseContext - a close context to enqueue for the delayed close thread.
572 
573     DelayClose - whether this should go on the delayed close queue (unreferenced
574         objects).
575 
576 Return Value:
577 
578     None.
579 
580 --*/
581 
582 {
583     BOOLEAN StartWorker = FALSE;
584 
585     FatAcquireCloseMutex();
586 
587     if (DelayClose) {
588 
589         InsertTailList( &FatData.DelayedCloseList,
590                         &CloseContext->GlobalLinks );
591         InsertTailList( &CloseContext->Vcb->DelayedCloseList,
592                         &CloseContext->VcbLinks );
593 
594         FatData.DelayedCloseCount += 1;
595 
596         if ((FatData.DelayedCloseCount > FatMaxDelayedCloseCount) &&
597             !FatData.AsyncCloseActive) {
598 
599             FatData.AsyncCloseActive = TRUE;
600             StartWorker = TRUE;
601         }
602 
603     } else {
604 
605         InsertTailList( &FatData.AsyncCloseList,
606                         &CloseContext->GlobalLinks );
607         InsertTailList( &CloseContext->Vcb->AsyncCloseList,
608                         &CloseContext->VcbLinks );
609 
610         FatData.AsyncCloseCount += 1;
611 
612         if (!FatData.AsyncCloseActive) {
613 
614             FatData.AsyncCloseActive = TRUE;
615             StartWorker = TRUE;
616         }
617     }
618 
619     FatReleaseCloseMutex();
620 
621     if (StartWorker) {
622 
623         IoQueueWorkItem( FatData.FatCloseItem, FatCloseWorker, CriticalWorkQueue, NULL );
624     }
625 }
626 
627 
628 _Requires_lock_held_(_Global_critical_region_)
629 PCLOSE_CONTEXT
630 FatRemoveClose (
631     PVCB Vcb OPTIONAL,
632     PVCB LastVcbHint OPTIONAL
633     )
634 
635 /*++
636 
637 Routine Description:
638 
639     Dequeue a deferred close from one of the two delayed close queues.
640 
641 Arguments:
642 
643     Vcb - if specified, only returns close for this volume.
644 
645     LastVcbHint - if specified and other starvation avoidance is required by
646         the system condition, will attempt to return closes for this volume.
647 
648 Return Value:
649 
650     A close to perform.
651 
652 --*/
653 
654 {
655     PLIST_ENTRY Entry;
656     PCLOSE_CONTEXT CloseContext;
657     BOOLEAN WorkerThread;
658 
659     PAGED_CODE();
660 
661     FatAcquireCloseMutex();
662 
663     //
664     //  Remember if this is the worker thread, so we can pull down the active
665     //  flag should we run everything out.
666     //
667 
668     WorkerThread = (Vcb == NULL);
669 
670     //
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
676     //  exclusively).
677     //
678     //  Only do this in the delayed close thread.  We will know this is the
679     //  case by seeing a NULL mandatory Vcb.
680     //
681 
682     if (Vcb == NULL && LastVcbHint != NULL) {
683 
684         //
685         //  Flip over to aggressive at twice the legal limit, and flip it
686         //  off at the legal limit.
687         //
688 
689         if (!FatData.HighAsync && FatData.AsyncCloseCount > FatMaxDelayedCloseCount*2) {
690 
691             FatData.HighAsync = TRUE;
692 
693         } else if (FatData.HighAsync && FatData.AsyncCloseCount < FatMaxDelayedCloseCount) {
694 
695             FatData.HighAsync = FALSE;
696         }
697 
698         if (!FatData.HighDelayed && FatData.DelayedCloseCount > FatMaxDelayedCloseCount*2) {
699 
700             FatData.HighDelayed = TRUE;
701 
702         } else if (FatData.HighDelayed && FatData.DelayedCloseCount < FatMaxDelayedCloseCount) {
703 
704             FatData.HighDelayed = FALSE;
705         }
706 
707         if (FatData.HighAsync || FatData.HighDelayed) {
708 
709             Vcb = LastVcbHint;
710         }
711     }
712 
713     //
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
716     //  under pressure.
717     //
718 
719     if (Vcb == NULL) {
720 
721         AnyClose:
722 
723         //
724         //  First check the list of async closes.
725         //
726 
727         if (!IsListEmpty( &FatData.AsyncCloseList )) {
728 
729             Entry = RemoveHeadList( &FatData.AsyncCloseList );
730             FatData.AsyncCloseCount -= 1;
731 
732             CloseContext = CONTAINING_RECORD( Entry,
733                                               CLOSE_CONTEXT,
734                                               GlobalLinks );
735 
736             RemoveEntryList( &CloseContext->VcbLinks );
737 
738         //
739         //  Do any delayed closes over half the limit, unless shutdown has
740         //  started (then kill them all).
741         //
742 
743         } else if (!IsListEmpty( &FatData.DelayedCloseList ) &&
744                    (FatData.DelayedCloseCount > FatMaxDelayedCloseCount/2 ||
745                     FatData.ShutdownStarted)) {
746 
747             Entry = RemoveHeadList( &FatData.DelayedCloseList );
748             FatData.DelayedCloseCount -= 1;
749 
750             CloseContext = CONTAINING_RECORD( Entry,
751                                               CLOSE_CONTEXT,
752                                               GlobalLinks );
753 
754             RemoveEntryList( &CloseContext->VcbLinks );
755 
756         //
757         //  There are no more closes to perform; show that we are done.
758         //
759 
760         } else {
761 
762             CloseContext = NULL;
763 
764             if (WorkerThread) {
765 
766                 FatData.AsyncCloseActive = FALSE;
767             }
768         }
769 
770     //
771     //  We're running down a specific volume.
772     //
773 
774     } else {
775 
776 
777         //
778         //  First check the list of async closes.
779         //
780 
781         if (!IsListEmpty( &Vcb->AsyncCloseList )) {
782 
783             Entry = RemoveHeadList( &Vcb->AsyncCloseList );
784             FatData.AsyncCloseCount -= 1;
785 
786             CloseContext = CONTAINING_RECORD( Entry,
787                                               CLOSE_CONTEXT,
788                                               VcbLinks );
789 
790             RemoveEntryList( &CloseContext->GlobalLinks );
791 
792         //
793         //  Do any delayed closes.
794         //
795 
796         } else if (!IsListEmpty( &Vcb->DelayedCloseList )) {
797 
798             Entry = RemoveHeadList( &Vcb->DelayedCloseList );
799             FatData.DelayedCloseCount -= 1;
800 
801             CloseContext = CONTAINING_RECORD( Entry,
802                                               CLOSE_CONTEXT,
803                                               VcbLinks );
804 
805             RemoveEntryList( &CloseContext->GlobalLinks );
806 
807         //
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.
810         //
811 
812         } else if (LastVcbHint) {
813 
814             goto AnyClose;
815 
816         //
817         //  There are no more closes to perform; show that we are done.
818         //
819 
820         } else {
821 
822             CloseContext = NULL;
823         }
824     }
825 
826     FatReleaseCloseMutex();
827 
828     return CloseContext;
829 }
830 
831 
832 _Requires_lock_held_(_Global_critical_region_)
833 NTSTATUS
834 FatCommonClose (
835     IN PVCB Vcb,
836     IN PFCB Fcb,
837     IN PCCB Ccb,
838     IN TYPE_OF_OPEN TypeOfOpen,
839     IN BOOLEAN Wait,
840     IN BOOLEAN TopLevel,
841     OUT PBOOLEAN VcbDeleted OPTIONAL
842     )
843 
844 /*++
845 
846 Routine Description:
847 
848     This is the common routine for closing a file/directory called by both
849     the fsd and fsp threads.
850 
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.
854 
855     The function of close is to completely tear down and remove the fcb/dcb/ccb
856     structures associated with the file object.
857 
858 Arguments:
859 
860     Fcb - Supplies the file to process.
861 
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.
864 
865     TopLevel - If this is TRUE this is a top level request.
866 
867     VcbDeleted - Returns whether the VCB was deleted by this call.
868 
869 Return Value:
870 
871     NTSTATUS - The return status for the operation
872 
873 --*/
874 
875 {
876     NTSTATUS Status = STATUS_SUCCESS;
877     PDCB ParentDcb;
878     BOOLEAN RecursiveClose;
879     BOOLEAN LocalVcbDeleted;
880     IRP_CONTEXT IrpContext;
881 
882     PAGED_CODE();
883 
884     DebugTrace(+1, Dbg, "FatCommonClose...\n", 0);
885 
886     //
887     //  Initialize the callers variable, if needed.
888     //
889 
890     LocalVcbDeleted = FALSE;
891 
892     if (ARGUMENT_PRESENT( VcbDeleted )) {
893 
894         *VcbDeleted = LocalVcbDeleted;
895     }
896 
897     //
898     //  Special case the unopened file object
899     //
900 
901     if (TypeOfOpen == UnopenedFileObject) {
902 
903         DebugTrace(0, Dbg, "Close unopened file object\n", 0);
904 
905         Status = STATUS_SUCCESS;
906 
907         DebugTrace(-1, Dbg, "FatCommonClose -> %08lx\n", Status);
908         return Status;
909     }
910 
911     //
912     //  Set up our stack IrpContext.
913     //
914 
915     RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT) );
916 
917     IrpContext.NodeTypeCode = FAT_NTC_IRP_CONTEXT;
918     IrpContext.NodeByteSize = sizeof( IrpContext );
919     IrpContext.MajorFunction = IRP_MJ_CLOSE;
920     IrpContext.Vcb = Vcb;
921 
922     if (Wait) {
923 
924         SetFlag( IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT );
925     }
926 
927     //
928     //  Acquire exclusive access to the Vcb and enqueue the irp if we didn't
929     //  get access.
930     //
931 
932 #ifdef _MSC_VER
933 #pragma prefast( suppress: 28137, "prefast wants Wait to be a constant, but that's not possible for fastfat" )
934 #endif
935     if (!ExAcquireResourceExclusiveLite( &Vcb->Resource, Wait )) {
936 
937         return STATUS_PENDING;
938     }
939 
940     //
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.
944     //
945 
946     if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS) &&
947         Vcb->EaFcb != Fcb) {
948 
949         ExReleaseResourceLite( &Vcb->Resource );
950 
951         return STATUS_PENDING;
952     }
953 
954     //
955     //  Setting the following flag prevents recursive closes of directory file
956     //  objects, which are handled in a special case loop.
957     //
958 
959     if ( FlagOn(Vcb->VcbState, VCB_STATE_FLAG_CLOSE_IN_PROGRESS) ) {
960 
961         RecursiveClose = TRUE;
962 
963     } else {
964 
965 
966         SetFlag(Vcb->VcbState, VCB_STATE_FLAG_CLOSE_IN_PROGRESS);
967         RecursiveClose = FALSE;
968 
969         //
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.
973         //
974 
975         Vcb->OpenFileCount += 1;
976     }
977 
978     _SEH2_TRY {
979 
980         //
981         //  Case on the type of open that we are trying to close.
982         //
983 
984         switch (TypeOfOpen) {
985 
986         case VirtualVolumeFile:
987 
988             DebugTrace(0, Dbg, "Close VirtualVolumeFile\n", 0);
989 
990             //
991             //  Remove this internal, residual open from the count.
992             //
993 
994             InterlockedDecrement( (LONG*)&(Vcb->InternalOpenCount) );
995             InterlockedDecrement( (LONG*)&(Vcb->ResidualOpenCount) );
996 
997             try_return( Status = STATUS_SUCCESS );
998             break;
999 
1000         case UserVolumeOpen:
1001 
1002             DebugTrace(0, Dbg, "Close UserVolumeOpen\n", 0);
1003 
1004             Vcb->DirectAccessOpenCount -= 1;
1005             Vcb->OpenFileCount -= 1;
1006             if (FlagOn(Ccb->Flags, CCB_FLAG_READ_ONLY)) { Vcb->ReadOnlyCount -= 1; }
1007 
1008             FatDeleteCcb( &IrpContext, &Ccb );
1009 
1010             try_return( Status = STATUS_SUCCESS );
1011             break;
1012 
1013         case EaFile:
1014 
1015             DebugTrace(0, Dbg, "Close EaFile\n", 0);
1016 
1017             //
1018             //  Remove this internal, residual open from the count.
1019             //
1020 
1021             InterlockedDecrement( (LONG*)&(Vcb->InternalOpenCount) );
1022             InterlockedDecrement( (LONG*)&(Vcb->ResidualOpenCount) );
1023 
1024             try_return( Status = STATUS_SUCCESS );
1025             break;
1026 
1027         case DirectoryFile:
1028 
1029             DebugTrace(0, Dbg, "Close DirectoryFile\n", 0);
1030 
1031             InterlockedDecrement( (LONG*)&Fcb->Specific.Dcb.DirectoryFileOpenCount );
1032 
1033             //
1034             //  Remove this internal open from the count.
1035             //
1036 
1037             InterlockedDecrement( (LONG*)&(Vcb->InternalOpenCount) );
1038 
1039             //
1040             //  If this is the root directory, it is a residual open
1041             //  as well.
1042             //
1043 
1044             if (NodeType( Fcb ) == FAT_NTC_ROOT_DCB) {
1045 
1046                 InterlockedDecrement( (LONG*)&(Vcb->ResidualOpenCount) );
1047             }
1048 
1049             //
1050             //  If this is a recursive close, just return here.
1051             //
1052 
1053             if ( RecursiveClose ) {
1054 
1055                 try_return( Status = STATUS_SUCCESS );
1056 
1057             } else {
1058 
1059                 break;
1060             }
1061 
1062 
1063         case UserDirectoryOpen:
1064         case UserFileOpen:
1065 
1066             DebugTrace(0, Dbg, "Close UserFileOpen/UserDirectoryOpen\n", 0);
1067 
1068             //
1069             //  Uninitialize the cache map if we no longer need to use it
1070             //
1071 
1072             if ((NodeType(Fcb) == FAT_NTC_DCB) &&
1073                 IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue) &&
1074                 (Fcb->OpenCount == 1) &&
1075                 (Fcb->Specific.Dcb.DirectoryFile != NULL)) {
1076 
1077                 PFILE_OBJECT DirectoryFileObject = Fcb->Specific.Dcb.DirectoryFile;
1078 
1079                 DebugTrace(0, Dbg, "Uninitialize the stream file object\n", 0);
1080 
1081                 CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL );
1082 
1083                 //
1084                 //  Dereference the directory file.  This may cause a close
1085                 //  Irp to be processed, so we need to do this before we destroy
1086                 //  the Fcb.
1087                 //
1088 
1089                 Fcb->Specific.Dcb.DirectoryFile = NULL;
1090                 ObDereferenceObject( DirectoryFileObject );
1091             }
1092 
1093             Fcb->OpenCount -= 1;
1094             Vcb->OpenFileCount -= 1;
1095             if (FlagOn(Ccb->Flags, CCB_FLAG_READ_ONLY)) { Vcb->ReadOnlyCount -= 1; }
1096 
1097             FatDeleteCcb( &IrpContext, &Ccb );
1098 
1099             break;
1100 
1101         default:
1102 
1103 #ifdef _MSC_VER
1104 #pragma prefast( suppress: 28159, "if the type of open is unknown, we seriously messed up." )
1105 #endif
1106             FatBugCheck( TypeOfOpen, 0, 0 );
1107         }
1108 
1109         //
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.
1115         //
1116 
1117         if (((NodeType(Fcb) == FAT_NTC_FCB) &&
1118              (Fcb->OpenCount == 0))
1119 
1120                 ||
1121 
1122              ((NodeType(Fcb) == FAT_NTC_DCB) &&
1123               (IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue)) &&
1124               (Fcb->OpenCount == 0) &&
1125               (Fcb->Specific.Dcb.DirectoryFileOpenCount == 0))) {
1126 
1127             ParentDcb = Fcb->ParentDcb;
1128 
1129             SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB );
1130 
1131             FatDeleteFcb( &IrpContext, &Fcb );
1132 
1133             //
1134             //  Uninitialize our parent's cache map if we no longer need
1135             //  to use it.
1136             //
1137 
1138             while ((NodeType(ParentDcb) == FAT_NTC_DCB) &&
1139                    IsListEmpty(&ParentDcb->Specific.Dcb.ParentDcbQueue) &&
1140                    (ParentDcb->OpenCount == 0) &&
1141                    (ParentDcb->Specific.Dcb.DirectoryFile != NULL)) {
1142 
1143                 PFILE_OBJECT DirectoryFileObject;
1144 
1145                 DirectoryFileObject = ParentDcb->Specific.Dcb.DirectoryFile;
1146 
1147                 DebugTrace(0, Dbg, "Uninitialize our parent Stream Cache Map\n", 0);
1148 
1149                 CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL );
1150 
1151                 ParentDcb->Specific.Dcb.DirectoryFile = NULL;
1152 
1153                 ObDereferenceObject( DirectoryFileObject );
1154 
1155                 //
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
1159                 //  and stop here..
1160                 //
1161 
1162                 if ( ParentDcb->Specific.Dcb.DirectoryFileOpenCount == 0) {
1163 
1164                     PDCB CurrentDcb;
1165 
1166                     CurrentDcb = ParentDcb;
1167                     ParentDcb = CurrentDcb->ParentDcb;
1168 
1169                     SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB );
1170 
1171                     FatDeleteFcb( &IrpContext, &CurrentDcb );
1172 
1173                 } else {
1174 
1175                     break;
1176                 }
1177             }
1178         }
1179 
1180         Status = STATUS_SUCCESS;
1181 
1182     try_exit: NOTHING;
1183     } _SEH2_FINALLY {
1184 
1185         DebugUnwind( FatCommonClose );
1186 
1187         //
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.
1191         //
1192 
1193         if (!RecursiveClose) {
1194 
1195             //
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
1201             //  to be top level.
1202             //
1203 
1204             if (Vcb->OpenFileCount == 1 &&
1205                 Vcb->VcbCondition != VcbGood &&
1206                 !FlagOn( Vcb->VcbState, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS ) &&
1207                 ARGUMENT_PRESENT( VcbDeleted ) &&
1208                 TopLevel) {
1209 
1210                 //
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.
1216                 //
1217 
1218                 FatReleaseVcb( &IrpContext,
1219                                Vcb );
1220 
1221                 SetFlag( IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT );
1222 
1223 #ifdef _MSC_VER
1224 #pragma prefast( suppress: 28137, "prefast wants the wait parameter in this macro expansion to be a constant, unfortunately this is not possible" )
1225 #endif
1226                 FatAcquireExclusiveGlobal( &IrpContext );
1227 
1228                 FatAcquireExclusiveVcb( &IrpContext,
1229                                         Vcb );
1230 
1231                 //
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.
1237                 //
1238 
1239                 Vcb->OpenFileCount -= 1;
1240 
1241                 LocalVcbDeleted = FatCheckForDismount( &IrpContext,
1242                                                        Vcb,
1243                                                        FALSE );
1244 
1245                 FatReleaseGlobal( &IrpContext );
1246 
1247                 //
1248                 //  Let the caller know what happened, if they want this information.
1249                 //
1250 
1251                 if (ARGUMENT_PRESENT( VcbDeleted )) {
1252 
1253                     *VcbDeleted = LocalVcbDeleted;
1254                 }
1255 
1256             } else {
1257 
1258                 //
1259                 //  The volume cannot go away now.  Just remove our extra reference.
1260                 //
1261 
1262                 Vcb->OpenFileCount -= 1;
1263             }
1264 
1265             //
1266             //  If the VCB is still around, clear our recursion flag.
1267             //
1268 
1269             if (!LocalVcbDeleted) {
1270 
1271                 ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_CLOSE_IN_PROGRESS );
1272             }
1273         }
1274 
1275         //
1276         //  Only release the VCB if it did not go away.
1277         //
1278 
1279         if (!LocalVcbDeleted) {
1280 
1281             FatReleaseVcb( &IrpContext, Vcb );
1282         }
1283 
1284         DebugTrace(-1, Dbg, "FatCommonClose -> %08lx\n", Status);
1285     } _SEH2_END;
1286 
1287     return Status;
1288 }
1289 
1290