1 /*++ 2 3 Copyright (c) 1989-2000 Microsoft Corporation 4 5 Module Name: 6 7 Cleanup.c 8 9 Abstract: 10 11 This module implements the File Cleanup 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_CLEANUP) 24 25 // 26 // The local debug trace level 27 // 28 29 #define Dbg (DEBUG_TRACE_CLEANUP) 30 31 // 32 // The following little routine exists solely because it need a spin lock. 33 // 34 35 VOID 36 FatAutoUnlock ( 37 IN PIRP_CONTEXT IrpContext, 38 IN PVCB Vcb 39 ); 40 41 #ifdef ALLOC_PRAGMA 42 #pragma alloc_text(PAGE, FatCommonCleanup) 43 #pragma alloc_text(PAGE, FatFsdCleanup) 44 #endif 45 46 47 _Function_class_(IRP_MJ_CLEANUP) 48 _Function_class_(DRIVER_DISPATCH) 49 NTSTATUS 50 NTAPI 51 FatFsdCleanup ( 52 _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, 53 _Inout_ PIRP Irp 54 ) 55 56 /*++ 57 58 Routine Description: 59 60 This routine implements the FSD part of closing down a handle to a 61 file object. 62 63 Arguments: 64 65 VolumeDeviceObject - Supplies the volume device object where the 66 file being Cleanup exists 67 68 Irp - Supplies the Irp being processed 69 70 Return Value: 71 72 NTSTATUS - The FSD status for the IRP 73 74 --*/ 75 76 { 77 NTSTATUS Status; 78 PIRP_CONTEXT IrpContext = NULL; 79 80 BOOLEAN TopLevel; 81 82 PAGED_CODE(); 83 84 // 85 // If we were called with our file system device object instead of a 86 // volume device object, just complete this request with STATUS_SUCCESS 87 // 88 89 if ( FatDeviceIsFatFsdo( VolumeDeviceObject)) { 90 91 Irp->IoStatus.Status = STATUS_SUCCESS; 92 Irp->IoStatus.Information = FILE_OPENED; 93 94 IoCompleteRequest( Irp, IO_DISK_INCREMENT ); 95 96 return STATUS_SUCCESS; 97 } 98 99 DebugTrace(+1, Dbg, "FatFsdCleanup\n", 0); 100 101 // 102 // Call the common Cleanup routine, with blocking allowed. 103 // 104 105 FsRtlEnterFileSystem(); 106 107 TopLevel = FatIsIrpTopLevel( Irp ); 108 109 _SEH2_TRY { 110 111 IrpContext = FatCreateIrpContext( Irp, TRUE ); 112 113 Status = FatCommonCleanup( IrpContext, Irp ); 114 115 } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) { 116 117 // 118 // We had some trouble trying to perform the requested 119 // operation, so we'll abort the I/O request with 120 // the error status that we get back from the 121 // execption code 122 // 123 124 Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() ); 125 } _SEH2_END; 126 127 if (TopLevel) { IoSetTopLevelIrp( NULL ); } 128 129 FsRtlExitFileSystem(); 130 131 // 132 // And return to our caller 133 // 134 135 DebugTrace(-1, Dbg, "FatFsdCleanup -> %08lx\n", Status); 136 137 UNREFERENCED_PARAMETER( VolumeDeviceObject ); 138 139 return Status; 140 } 141 142 143 _Requires_lock_held_(_Global_critical_region_) 144 NTSTATUS 145 FatCommonCleanup ( 146 IN PIRP_CONTEXT IrpContext, 147 IN PIRP Irp 148 ) 149 150 /*++ 151 152 Routine Description: 153 154 This is the common routine for cleanup of a file/directory called by both 155 the fsd and fsp threads. 156 157 Cleanup is invoked whenever the last handle to a file object is closed. 158 This is different than the Close operation which is invoked when the last 159 reference to a file object is deleted. 160 161 The function of cleanup is to essentially "cleanup" the file/directory 162 after a user is done with it. The Fcb/Dcb remains around (because MM 163 still has the file object referenced) but is now available for another 164 user to open (i.e., as far as the user is concerned the is now closed). 165 166 See close for a more complete description of what close does. 167 168 Arguments: 169 170 Irp - Supplies the Irp to process 171 172 Return Value: 173 174 NTSTATUS - The return status for the operation 175 176 --*/ 177 178 { 179 NTSTATUS Status = STATUS_SUCCESS; 180 181 PIO_STACK_LOCATION IrpSp; 182 183 PFILE_OBJECT FileObject; 184 185 TYPE_OF_OPEN TypeOfOpen; 186 PVCB Vcb; 187 PFCB Fcb; 188 PCCB Ccb; 189 190 BOOLEAN SendUnlockNotification = FALSE; 191 192 PSHARE_ACCESS ShareAccess; 193 194 PLARGE_INTEGER TruncateSize = NULL; 195 LARGE_INTEGER LocalTruncateSize; 196 197 BOOLEAN AcquiredVcb = FALSE; 198 BOOLEAN AcquiredFcb = FALSE; 199 200 #if (NTDDI_VERSION >= NTDDI_WIN8) 201 BOOLEAN ProcessingDeleteOnClose = FALSE; 202 #endif 203 204 PAGED_CODE(); 205 206 IrpSp = IoGetCurrentIrpStackLocation( Irp ); 207 208 DebugTrace(+1, Dbg, "FatCommonCleanup\n", 0); 209 DebugTrace( 0, Dbg, "Irp = %p\n", Irp); 210 DebugTrace( 0, Dbg, "->FileObject = %p\n", IrpSp->FileObject); 211 212 // 213 // Extract and decode the file object 214 // 215 216 FileObject = IrpSp->FileObject; 217 TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ); 218 219 // 220 // Special case the unopened file object. This will occur only when 221 // we are initializing Vcb and IoCreateStreamFileObject is being 222 // called. 223 // 224 225 if (TypeOfOpen == UnopenedFileObject) { 226 227 DebugTrace(0, Dbg, "Unopened File Object\n", 0); 228 229 FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); 230 231 DebugTrace(-1, Dbg, "FatCommonCleanup -> STATUS_SUCCESS\n", 0); 232 return STATUS_SUCCESS; 233 } 234 235 // 236 // If this is not our first time through (for whatever reason) 237 // only see if we have to flush the file. 238 // 239 240 if (FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE )) { 241 242 if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH) && 243 FlagOn(FileObject->Flags, FO_FILE_MODIFIED) && 244 !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED) && 245 (TypeOfOpen == UserFileOpen)) { 246 247 // 248 // Flush the file. 249 // 250 251 Status = FatFlushFile( IrpContext, Fcb, Flush ); 252 253 if (!NT_SUCCESS(Status)) { 254 255 FatNormalizeAndRaiseStatus( IrpContext, Status ); 256 } 257 } 258 259 FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); 260 261 DebugTrace(-1, Dbg, "FatCommonCleanup -> STATUS_SUCCESS\n", 0); 262 return STATUS_SUCCESS; 263 } 264 265 // 266 // If we call change the allocation or call CcUninitialize, 267 // we have to take the Fcb exclusive 268 // 269 270 if ((TypeOfOpen == UserFileOpen) || (TypeOfOpen == UserDirectoryOpen)) { 271 272 NT_ASSERT( Fcb != NULL ); 273 274 (VOID)FatAcquireExclusiveFcb( IrpContext, Fcb ); 275 276 AcquiredFcb = TRUE; 277 278 // 279 // Do a check here if this was a DELETE_ON_CLOSE FileObject, and 280 // set the Fcb flag appropriately. 281 // 282 283 if (FlagOn(Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE)) { 284 285 NT_ASSERT( NodeType(Fcb) != FAT_NTC_ROOT_DCB ); 286 287 // 288 // Transfer the delete-on-close state to the FCB. We do this rather 289 // than leave the CCB_FLAG_DELETE_ON_CLOSE flag set so that if we 290 // end up breaking an oplock and come in again we won't try to break 291 // the oplock again (and again, and again...). 292 // 293 294 SetFlag( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE ); 295 ClearFlag( Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE ); 296 297 #if (NTDDI_VERSION >= NTDDI_WIN8) 298 ProcessingDeleteOnClose = TRUE; 299 #endif 300 301 // 302 // Report this to the dir notify package for a directory. 303 // 304 305 if (TypeOfOpen == UserDirectoryOpen) { 306 307 #ifdef _MSC_VER 308 #pragma prefast( suppress:6309, "FullDirectoryName may be NULL if NotifyIrp is also NULL. this indicates the object is being deleted." ) 309 #endif 310 FsRtlNotifyFullChangeDirectory( Vcb->NotifySync, 311 &Vcb->DirNotifyList, 312 FileObject->FsContext, 313 NULL, 314 FALSE, 315 FALSE, 316 0, 317 NULL, 318 NULL, 319 NULL ); 320 } 321 } 322 323 // 324 // Now if we may delete the file, drop the Fcb and acquire the Vcb 325 // first. Note that while we own the Fcb exclusive, a file cannot 326 // become DELETE_ON_CLOSE and cannot be opened via CommonCreate. 327 // 328 329 if ((Fcb->UncleanCount == 1) && 330 FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE) && 331 (Fcb->FcbCondition != FcbBad) && 332 !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) { 333 334 FatReleaseFcb( IrpContext, Fcb ); 335 AcquiredFcb = FALSE; 336 337 (VOID)FatAcquireExclusiveVcb( IrpContext, Vcb ); 338 AcquiredVcb = TRUE; 339 340 (VOID)FatAcquireExclusiveFcb( IrpContext, Fcb ); 341 AcquiredFcb = TRUE; 342 } 343 } 344 345 // 346 // For user DASD cleanups, grab the Vcb exclusive. 347 // 348 349 if (TypeOfOpen == UserVolumeOpen) { 350 351 (VOID)FatAcquireExclusiveVcb( IrpContext, Vcb ); 352 AcquiredVcb = TRUE; 353 } 354 355 // 356 // Complete any Notify Irps on this file handle. 357 // 358 359 if (TypeOfOpen == UserDirectoryOpen) { 360 361 FsRtlNotifyCleanup( Vcb->NotifySync, 362 &Vcb->DirNotifyList, 363 Ccb ); 364 } 365 366 // 367 // Determine the Fcb state, Good or Bad, for better or for worse. 368 // 369 // We can only read the volume file if VcbCondition is good. 370 // 371 372 if ( Fcb != NULL) { 373 374 // 375 // Stop any raises from FatVerifyFcb, unless it is REAL bad. 376 // 377 378 _SEH2_TRY { 379 380 _SEH2_TRY { 381 382 FatVerifyFcb( IrpContext, Fcb ); 383 384 } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ? 385 EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { 386 387 FatResetExceptionState( IrpContext ); 388 } _SEH2_END; 389 390 } _SEH2_FINALLY { 391 392 if ( _SEH2_AbnormalTermination() ) { 393 394 // 395 // We will be raising out of here. 396 // 397 398 if (AcquiredFcb) { FatReleaseFcb( IrpContext, Fcb ); } 399 if (AcquiredVcb) { FatReleaseVcb( IrpContext, Vcb ); } 400 } 401 } _SEH2_END; 402 } 403 404 _SEH2_TRY { 405 406 #if (NTDDI_VERSION >= NTDDI_WIN8) 407 408 // 409 // See if this is a delete-on-close handle on a file or empty directory. 410 // If so we may need to break an oplock. We do this in the try block 411 // so that resources will be properly released. 412 // 413 414 if (ProcessingDeleteOnClose && 415 FatIsFileOplockable( Fcb ) && 416 ((NodeType( Fcb ) != FAT_NTC_DCB) || 417 FatIsDirectoryEmpty( IrpContext, Fcb ))) { 418 419 Status = FsRtlCheckOplockEx( FatGetFcbOplock(Fcb), 420 Irp, 421 OPLOCK_FLAG_CLOSING_DELETE_ON_CLOSE, 422 IrpContext, 423 FatOplockComplete, 424 FatPrePostIrp ); 425 426 if (Status != STATUS_SUCCESS) { 427 428 if (Status == STATUS_PENDING) { 429 430 SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_CLEANUP_BREAKING_OPLOCK ); 431 try_return( Status ); 432 433 } else { 434 435 FatNormalizeAndRaiseStatus( IrpContext, Status ); 436 } 437 } 438 } 439 #endif 440 441 // 442 // Case on the type of open that we are trying to cleanup. 443 // For all cases we need to set the share access to point to the 444 // share access variable (if there is one). After the switch 445 // we then remove the share access and complete the Irp. 446 // In the case of UserFileOpen we actually have a lot more work 447 // to do and we have the FsdLockControl complete the Irp for us. 448 // 449 450 switch (TypeOfOpen) { 451 452 case DirectoryFile: 453 case VirtualVolumeFile: 454 455 DebugTrace(0, Dbg, "Cleanup VirtualVolumeFile/DirectoryFile\n", 0); 456 457 ShareAccess = NULL; 458 459 break; 460 461 462 case UserVolumeOpen: 463 464 DebugTrace(0, Dbg, "Cleanup UserVolumeOpen\n", 0); 465 466 if (FlagOn( Ccb->Flags, CCB_FLAG_COMPLETE_DISMOUNT )) { 467 468 FatCheckForDismount( IrpContext, Vcb, TRUE ); 469 470 // 471 // If this handle had write access, and actually wrote something, 472 // flush the device buffers, and then set the verify bit now 473 // just to be safe (in case there is no dismount). 474 // 475 476 } else if (FileObject->WriteAccess && 477 FlagOn(FileObject->Flags, FO_FILE_MODIFIED)) { 478 479 (VOID)FatHijackIrpAndFlushDevice( IrpContext, 480 Irp, 481 Vcb->TargetDeviceObject ); 482 483 SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME); 484 } 485 486 // 487 // If the volume is locked by this file object then release 488 // the volume and send notification. 489 // 490 491 if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_LOCKED) && 492 (Vcb->FileObjectWithVcbLocked == FileObject)) { 493 494 FatAutoUnlock( IrpContext, Vcb ); 495 SendUnlockNotification = TRUE; 496 } 497 498 ShareAccess = &Vcb->ShareAccess; 499 500 break; 501 502 case EaFile: 503 504 DebugTrace(0, Dbg, "Cleanup EaFileObject\n", 0); 505 506 ShareAccess = NULL; 507 508 break; 509 510 case UserDirectoryOpen: 511 512 DebugTrace(0, Dbg, "Cleanup UserDirectoryOpen\n", 0); 513 514 ShareAccess = &Fcb->ShareAccess; 515 516 // 517 // Determine here if we should try do delayed close. 518 // 519 520 if ((Fcb->UncleanCount == 1) && 521 (Fcb->OpenCount == 1) && 522 (Fcb->Specific.Dcb.DirectoryFileOpenCount == 0) && 523 !FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE) && 524 Fcb->FcbCondition == FcbGood) { 525 526 // 527 // Delay our close. 528 // 529 530 SetFlag( Fcb->FcbState, FCB_STATE_DELAY_CLOSE ); 531 } 532 533 // 534 // Clear the deny defrag bit, if the handle we're cleaning up was the one that set it. 535 // 536 537 if( FlagOn(Fcb->FcbState, FCB_STATE_DENY_DEFRAG) && FlagOn(Ccb->Flags, CCB_FLAG_DENY_DEFRAG) ) { 538 539 ClearFlag(Ccb->Flags, CCB_FLAG_DENY_DEFRAG); 540 ClearFlag(Fcb->FcbState, FCB_STATE_DENY_DEFRAG ); 541 } 542 543 if ((VcbGood == Vcb->VcbCondition) && 544 !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_SHUTDOWN)) { 545 546 FatUpdateDirentFromFcb( IrpContext, FileObject, Fcb, Ccb ); 547 548 // 549 // If the directory has a unclean count of 1 then we know 550 // that this is the last handle for the file object. If 551 // we are supposed to delete it, do so. 552 // 553 554 if ((Fcb->UncleanCount == 1) && 555 (NodeType(Fcb) == FAT_NTC_DCB) && 556 (FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE)) && 557 (Fcb->FcbCondition == FcbGood) && 558 !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) { 559 560 if (!FatIsDirectoryEmpty(IrpContext, Fcb)) { 561 562 // 563 // If there are files in the directory at this point, 564 // forget that we were trying to delete it. 565 // 566 567 ClearFlag( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE ); 568 569 } else { 570 571 #if (NTDDI_VERSION >= NTDDI_WIN8) 572 NTSTATUS BreakStatus; 573 #endif 574 575 // 576 // Even if something goes wrong, we cannot turn back! 577 // 578 579 _SEH2_TRY { 580 581 DELETE_CONTEXT DeleteContext; 582 583 584 // 585 // Before truncating file allocation remember this 586 // info for FatDeleteDirent. 587 // 588 589 DeleteContext.FileSize = Fcb->Header.FileSize.LowPart; 590 DeleteContext.FirstClusterOfFile = Fcb->FirstClusterOfFile; 591 592 // 593 // Synchronize here with paging IO 594 // 595 596 (VOID)ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, 597 TRUE ); 598 599 Fcb->Header.FileSize.LowPart = 0; 600 601 ExReleaseResourceLite( Fcb->Header.PagingIoResource ); 602 603 // 604 // Truncate the file allocation down to zero 605 // 606 607 DebugTrace(0, Dbg, "Delete File allocation\n", 0); 608 609 FatTruncateFileAllocation( IrpContext, Fcb, 0 ); 610 611 if (Fcb->Header.AllocationSize.LowPart == 0) { 612 613 // 614 // Tunnel and remove the dirent for the directory 615 // 616 617 DebugTrace(0, Dbg, "Delete the directory dirent\n", 0); 618 619 FatTunnelFcbOrDcb( Fcb, NULL ); 620 621 FatDeleteDirent( IrpContext, Fcb, &DeleteContext, TRUE ); 622 623 // 624 // Report that we have removed an entry. 625 // 626 627 FatNotifyReportChange( IrpContext, 628 Vcb, 629 Fcb, 630 FILE_NOTIFY_CHANGE_DIR_NAME, 631 FILE_ACTION_REMOVED ); 632 } 633 634 } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ? 635 EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { 636 637 FatResetExceptionState( IrpContext ); 638 } _SEH2_END; 639 640 // 641 // Remove the entry from the name table. 642 // This will ensure that 643 // we will not collide with the Dcb if the user wants 644 // to recreate the same file over again before we 645 // get a close irp. 646 // 647 648 FatRemoveNames( IrpContext, Fcb ); 649 650 #if (NTDDI_VERSION >= NTDDI_WIN8) 651 // 652 // We've removed the names so break any parent directory oplock. 653 // Directory oplock breaks are always advisory, so we will never 654 // block/get STATUS_PENDING here. 655 // 656 657 BreakStatus = FsRtlCheckOplockEx( FatGetFcbOplock(Fcb->ParentDcb), 658 Irp, 659 (OPLOCK_FLAG_PARENT_OBJECT | 660 OPLOCK_FLAG_REMOVING_FILE_OR_LINK), 661 NULL, 662 NULL, 663 NULL ); 664 665 ASSERT( BreakStatus != STATUS_PENDING ); 666 #endif 667 } 668 } 669 } 670 671 // 672 // Decrement the unclean count. 673 // 674 675 NT_ASSERT( Fcb->UncleanCount != 0 ); 676 Fcb->UncleanCount -= 1; 677 678 break; 679 680 case UserFileOpen: 681 682 DebugTrace(0, Dbg, "Cleanup UserFileOpen\n", 0); 683 684 ShareAccess = &Fcb->ShareAccess; 685 686 // 687 // Determine here if we should do a delayed close. 688 // 689 690 if ((FileObject->SectionObjectPointer->DataSectionObject == NULL) && 691 (FileObject->SectionObjectPointer->ImageSectionObject == NULL) && 692 (Fcb->UncleanCount == 1) && 693 (Fcb->OpenCount == 1) && 694 !FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE) && 695 !FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE) && 696 Fcb->FcbCondition == FcbGood) { 697 698 // 699 // Delay our close. 700 // 701 702 SetFlag( Fcb->FcbState, FCB_STATE_DELAY_CLOSE ); 703 } 704 705 // 706 // Clear the deny defrag bit, if the handle we're cleaning up was the one that set it. 707 // 708 709 if( FlagOn(Fcb->FcbState, FCB_STATE_DENY_DEFRAG) && FlagOn(Ccb->Flags, CCB_FLAG_DENY_DEFRAG) ) { 710 711 ClearFlag(Ccb->Flags, CCB_FLAG_DENY_DEFRAG); 712 ClearFlag(Fcb->FcbState, FCB_STATE_DENY_DEFRAG ); 713 } 714 715 // 716 // Unlock all outstanding file locks. 717 // 718 719 (VOID) FsRtlFastUnlockAll( &Fcb->Specific.Fcb.FileLock, 720 FileObject, 721 IoGetRequestorProcess( Irp ), 722 NULL ); 723 724 725 726 // 727 // We can proceed with on-disk updates only if the volume is mounted 728 // and we can still write to it if it hasn't been shutdown. Remember that 729 // we toss all sections in the failed-verify and dismount cases. 730 // 731 732 if ((Vcb->VcbCondition == VcbGood) && 733 !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_SHUTDOWN)) { 734 735 if (Fcb->FcbCondition == FcbGood) { 736 737 738 FatUpdateDirentFromFcb( IrpContext, FileObject, Fcb, Ccb ); 739 740 } 741 742 // 743 // If the file has a unclean count of 1 then we know 744 // that this is the last handle for the file object. 745 // 746 747 if ( (Fcb->UncleanCount == 1) && (Fcb->FcbCondition == FcbGood) ) { 748 749 DELETE_CONTEXT DeleteContext; 750 751 // 752 // Check if we should be deleting the file. The 753 // delete operation really deletes the file but 754 // keeps the Fcb around for close to do away with. 755 // 756 757 if (FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE) && 758 !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) { 759 760 // 761 // Before truncating file allocation remember this 762 // info for FatDeleteDirent. 763 // 764 765 DeleteContext.FileSize = Fcb->Header.FileSize.LowPart; 766 DeleteContext.FirstClusterOfFile = Fcb->FirstClusterOfFile; 767 768 DebugTrace(0, Dbg, "Delete File allocation\n", 0); 769 770 // 771 // Synchronize here with paging IO 772 // 773 774 (VOID)ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, 775 TRUE ); 776 777 Fcb->Header.FileSize.LowPart = 0; 778 Fcb->Header.ValidDataLength.LowPart = 0; 779 Fcb->ValidDataToDisk = 0; 780 781 ExReleaseResourceLite( Fcb->Header.PagingIoResource ); 782 783 _SEH2_TRY { 784 785 FatSetFileSizeInDirent( IrpContext, Fcb, NULL ); 786 787 } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ? 788 EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { 789 790 FatResetExceptionState( IrpContext ); 791 } _SEH2_END; 792 793 Fcb->FcbState |= FCB_STATE_TRUNCATE_ON_CLOSE; 794 795 } else { 796 797 // 798 // We must zero between ValidDataLength and FileSize 799 // 800 801 if (!FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE) && 802 (Fcb->Header.ValidDataLength.LowPart < Fcb->Header.FileSize.LowPart)) { 803 804 ULONG ValidDataLength; 805 806 ValidDataLength = Fcb->Header.ValidDataLength.LowPart; 807 808 if (ValidDataLength < Fcb->ValidDataToDisk) { 809 ValidDataLength = Fcb->ValidDataToDisk; 810 } 811 812 // 813 // Recheck, VDD can be >= FS 814 // 815 816 if (ValidDataLength < Fcb->Header.FileSize.LowPart) { 817 818 _SEH2_TRY { 819 820 (VOID)FatZeroData( IrpContext, 821 Vcb, 822 FileObject, 823 ValidDataLength, 824 Fcb->Header.FileSize.LowPart - 825 ValidDataLength ); 826 827 // 828 // Since we just zeroed this, we can now bump 829 // up VDL in the Fcb. 830 // 831 832 Fcb->ValidDataToDisk = 833 Fcb->Header.ValidDataLength.LowPart = 834 Fcb->Header.FileSize.LowPart; 835 836 // 837 // We inform Cc of the motion so that the cache map is updated. 838 // This prevents optimized zero-page faults in case the cache 839 // structures are re-used for another handle before they are torn 840 // down by our soon-to-occur uninitialize. If they were, a noncached 841 // producer could write into the region we just zeroed and Cc would 842 // be none the wiser, then our async cached reader comes in and takes 843 // the optimized path, and we get bad (zero) data. 844 // 845 // If this was memory mapped, we don't have to (can't) tell Cc, it'll 846 // figure it out when a cached handle is opened. 847 // 848 849 if (CcIsFileCached( FileObject )) { 850 CcSetFileSizes( FileObject, (PCC_FILE_SIZES)&Fcb->Header.AllocationSize ); 851 } 852 853 } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ? 854 EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { 855 856 FatResetExceptionState( IrpContext ); 857 } _SEH2_END; 858 } 859 } 860 } 861 862 // 863 // See if we are supposed to truncate the file on the last 864 // close. If we cannot wait we'll ship this off to the fsp 865 // 866 867 _SEH2_TRY { 868 869 if (FlagOn(Fcb->FcbState, FCB_STATE_TRUNCATE_ON_CLOSE)) { 870 871 DebugTrace(0, Dbg, "truncate file allocation\n", 0); 872 873 if (Vcb->VcbCondition == VcbGood) { 874 875 876 FatTruncateFileAllocation( IrpContext, 877 Fcb, 878 Fcb->Header.FileSize.LowPart ); 879 880 881 } 882 883 // 884 // We also have to get rid of the Cache Map because 885 // this is the only way we have of trashing the 886 // truncated pages. 887 // 888 889 LocalTruncateSize = Fcb->Header.FileSize; 890 TruncateSize = &LocalTruncateSize; 891 892 // 893 // Mark the Fcb as having now been truncated, just incase 894 // we have to reship this off to the fsp. 895 // 896 897 Fcb->FcbState &= ~FCB_STATE_TRUNCATE_ON_CLOSE; 898 } 899 900 // 901 // Now check again if we are to delete the file and if 902 // so then we remove the file from the disk. 903 // 904 905 if (FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE) && 906 Fcb->Header.AllocationSize.LowPart == 0) { 907 908 DebugTrace(0, Dbg, "Delete File\n", 0); 909 910 // 911 // Now tunnel and delete the dirent 912 // 913 914 FatTunnelFcbOrDcb( Fcb, Ccb ); 915 916 FatDeleteDirent( IrpContext, Fcb, &DeleteContext, TRUE ); 917 918 // 919 // Report that we have removed an entry. 920 // 921 922 FatNotifyReportChange( IrpContext, 923 Vcb, 924 Fcb, 925 FILE_NOTIFY_CHANGE_FILE_NAME, 926 FILE_ACTION_REMOVED ); 927 } 928 929 } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ? 930 EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { 931 932 FatResetExceptionState( IrpContext ); 933 } _SEH2_END; 934 935 if (FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE)) { 936 937 #if (NTDDI_VERSION >= NTDDI_WIN8) 938 NTSTATUS BreakStatus; 939 #endif 940 // 941 // Remove the entry from the splay table. This will 942 // ensure that we will not collide with the Fcb if the 943 // user wants to recreate the same file over again 944 // before we get a close irp. 945 // 946 // Note that we remove the name even if we couldn't 947 // truncate the allocation and remove the dirent above. 948 // 949 950 FatRemoveNames( IrpContext, Fcb ); 951 952 #if (NTDDI_VERSION >= NTDDI_WIN8) 953 // 954 // We've removed the names so break any parent directory oplock. 955 // Directory oplock breaks are always advisory, so we will never 956 // block/get STATUS_PENDING here. 957 // 958 959 BreakStatus = FsRtlCheckOplockEx( FatGetFcbOplock(Fcb->ParentDcb), 960 Irp, 961 (OPLOCK_FLAG_PARENT_OBJECT | 962 OPLOCK_FLAG_REMOVING_FILE_OR_LINK), 963 NULL, 964 NULL, 965 NULL ); 966 967 ASSERT( BreakStatus != STATUS_PENDING ); 968 #endif 969 } 970 } 971 } 972 973 // 974 // We've just finished everything associated with an unclean 975 // fcb so now decrement the unclean count before releasing 976 // the resource. 977 // 978 979 NT_ASSERT( Fcb->UncleanCount != 0 ); 980 Fcb->UncleanCount -= 1; 981 if (!FlagOn( FileObject->Flags, FO_CACHE_SUPPORTED )) { 982 NT_ASSERT( Fcb->NonCachedUncleanCount != 0 ); 983 Fcb->NonCachedUncleanCount -= 1; 984 } 985 986 // 987 // If this was the last cached open, and there are open 988 // non-cached handles, attempt a flush and purge operation 989 // to avoid cache coherency overhead from these non-cached 990 // handles later. We ignore any I/O errors from the flush. 991 // 992 993 if (FlagOn( FileObject->Flags, FO_CACHE_SUPPORTED ) && 994 (Fcb->NonCachedUncleanCount != 0) && 995 (Fcb->NonCachedUncleanCount == Fcb->UncleanCount) && 996 (Fcb->NonPaged->SectionObjectPointers.DataSectionObject != NULL)) { 997 998 CcFlushCache( &Fcb->NonPaged->SectionObjectPointers, NULL, 0, NULL ); 999 1000 // 1001 // Grab and release PagingIo to serialize ourselves with the lazy writer. 1002 // This will work to ensure that all IO has completed on the cached 1003 // data and we will succesfully tear away the cache section. 1004 // 1005 1006 ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE); 1007 ExReleaseResourceLite( Fcb->Header.PagingIoResource ); 1008 1009 CcPurgeCacheSection( &Fcb->NonPaged->SectionObjectPointers, 1010 NULL, 1011 0, 1012 FALSE ); 1013 } 1014 1015 // 1016 // If the file is invalid, hint to the cache that we should throw everything out. 1017 // 1018 1019 if ( Fcb->FcbCondition == FcbBad ) { 1020 1021 TruncateSize = &FatLargeZero; 1022 } 1023 1024 // 1025 // Cleanup the cache map 1026 // 1027 1028 CcUninitializeCacheMap( FileObject, TruncateSize, NULL ); 1029 1030 break; 1031 1032 default: 1033 1034 #ifdef _MSC_VER 1035 #pragma prefast( suppress: 28159, "if the type of open is unknown then things are very bad." ) 1036 #endif 1037 FatBugCheck( TypeOfOpen, 0, 0 ); 1038 } 1039 1040 // 1041 // We must clean up the share access at this time, since we may not 1042 // get a Close call for awhile if the file was mapped through this 1043 // File Object. 1044 // 1045 1046 if (ShareAccess != NULL) { 1047 1048 DebugTrace(0, Dbg, "Cleanup the Share access\n", 0); 1049 IoRemoveShareAccess( FileObject, ShareAccess ); 1050 } 1051 1052 if ((TypeOfOpen == UserFileOpen) 1053 #if (NTDDI_VERSION >= NTDDI_WIN8) 1054 || 1055 (TypeOfOpen == UserDirectoryOpen) 1056 #endif 1057 ) { 1058 1059 // 1060 // Coordinate the cleanup operation with the oplock state. 1061 // Cleanup operations can always cleanup immediately. 1062 // 1063 1064 FsRtlCheckOplock( FatGetFcbOplock(Fcb), 1065 Irp, 1066 IrpContext, 1067 NULL, 1068 NULL ); 1069 1070 Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb ); 1071 } 1072 1073 // 1074 // First set the FO_CLEANUP_COMPLETE flag. 1075 // 1076 1077 SetFlag( FileObject->Flags, FO_CLEANUP_COMPLETE ); 1078 1079 Status = STATUS_SUCCESS; 1080 1081 // 1082 // Now unpin any repinned Bcbs. 1083 // 1084 1085 FatUnpinRepinnedBcbs( IrpContext ); 1086 1087 // 1088 // If this was deferred flush media, flush the volume. 1089 // We used to do this in lieu of write through for all removable 1090 // media. 1091 // 1092 1093 if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH) && 1094 !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) { 1095 1096 // 1097 // Flush the file. 1098 // 1099 1100 if ((TypeOfOpen == UserFileOpen) && 1101 FlagOn(FileObject->Flags, FO_FILE_MODIFIED)) { 1102 1103 Status = FatFlushFile( IrpContext, Fcb, Flush ); 1104 } 1105 1106 // 1107 // If that worked ok, then see if we should flush the FAT as well. 1108 // 1109 1110 if (NT_SUCCESS(Status) && Fcb && !FatIsFat12( Vcb) && 1111 FlagOn( Fcb->FcbState, FCB_STATE_FLUSH_FAT)) { 1112 1113 Status = FatFlushFat( IrpContext, Vcb); 1114 1115 // 1116 // Also flush the parent directory. 1117 // 1118 1119 if (NT_SUCCESS(Status) && (Fcb->ParentDcb != NULL)) { 1120 1121 Status = FatFlushFile( IrpContext, Fcb->ParentDcb, Flush ); 1122 } 1123 } 1124 1125 if (!NT_SUCCESS(Status)) { 1126 1127 FatNormalizeAndRaiseStatus( IrpContext, Status ); 1128 } 1129 } 1130 1131 #if (NTDDI_VERSION >= NTDDI_WIN8) 1132 1133 try_exit: NOTHING; 1134 1135 #endif 1136 1137 } _SEH2_FINALLY { 1138 1139 DebugUnwind( FatCommonCleanup ); 1140 1141 if (AcquiredFcb) { FatReleaseFcb( IrpContext, Fcb ); } 1142 if (AcquiredVcb) { FatReleaseVcb( IrpContext, Vcb ); } 1143 1144 if (SendUnlockNotification) { 1145 1146 FsRtlNotifyVolumeEvent( FileObject, FSRTL_VOLUME_UNLOCK ); 1147 } 1148 1149 // 1150 // If this is a normal termination then complete the request 1151 // 1152 1153 if (!_SEH2_AbnormalTermination() && 1154 (Status != STATUS_PENDING)) { 1155 1156 FatCompleteRequest( IrpContext, Irp, Status ); 1157 } 1158 1159 DebugTrace(-1, Dbg, "FatCommonCleanup -> %08lx\n", Status); 1160 } _SEH2_END; 1161 1162 return Status; 1163 } 1164 1165 VOID 1166 FatAutoUnlock ( 1167 IN PIRP_CONTEXT IrpContext, 1168 IN PVCB Vcb 1169 ) 1170 { 1171 KIRQL SavedIrql; 1172 1173 // 1174 // Unlock the volume. 1175 // 1176 1177 UNREFERENCED_PARAMETER( IrpContext ); 1178 1179 IoAcquireVpbSpinLock( &SavedIrql ); 1180 1181 ClearFlag( Vcb->Vpb->Flags, (VPB_LOCKED | VPB_DIRECT_WRITES_ALLOWED) ); 1182 1183 Vcb->VcbState &= ~VCB_STATE_FLAG_LOCKED; 1184 Vcb->FileObjectWithVcbLocked = NULL; 1185 1186 IoReleaseVpbSpinLock( SavedIrql ); 1187 } 1188 1189 1190