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