[SHELL32] Remove 2 redundant initializations
[reactos.git] / drivers / filesystems / fastfat / close.c
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