1 /*++ 2 3 Copyright (c) 1989-2000 Microsoft Corporation 4 5 Module Name: 6 7 Flush.c 8 9 Abstract: 10 11 This module implements the File Flush buffers 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_FLUSH) 24 25 // 26 // The local debug trace level 27 // 28 29 #define Dbg (DEBUG_TRACE_FLUSH) 30 31 #ifdef ALLOC_PRAGMA 32 #pragma alloc_text(PAGE, FatCommonFlushBuffers) 33 #pragma alloc_text(PAGE, FatFlushDirectory) 34 #pragma alloc_text(PAGE, FatFlushFat) 35 #pragma alloc_text(PAGE, FatFlushFile) 36 #pragma alloc_text(PAGE, FatFlushVolume) 37 #pragma alloc_text(PAGE, FatFsdFlushBuffers) 38 #pragma alloc_text(PAGE, FatFlushDirentForFile) 39 #pragma alloc_text(PAGE, FatFlushFatEntries) 40 #pragma alloc_text(PAGE, FatHijackIrpAndFlushDevice) 41 #endif 42 43 // 44 // Local procedure prototypes 45 // 46 47 IO_COMPLETION_ROUTINE FatFlushCompletionRoutine; 48 49 NTSTATUS 50 NTAPI 51 FatFlushCompletionRoutine ( 52 _In_ PDEVICE_OBJECT DeviceObject, 53 _In_ PIRP Irp, 54 _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt 55 ); 56 57 IO_COMPLETION_ROUTINE FatHijackCompletionRoutine; 58 59 NTSTATUS 60 NTAPI 61 FatHijackCompletionRoutine ( 62 _In_ PDEVICE_OBJECT DeviceObject, 63 _In_ PIRP Irp, 64 _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt 65 ); 66 67 68 _Function_class_(IRP_MJ_FLUSH_BUFFERS) 69 _Function_class_(DRIVER_DISPATCH) 70 NTSTATUS 71 NTAPI 72 FatFsdFlushBuffers ( 73 _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, 74 _Inout_ PIRP Irp 75 ) 76 77 /*++ 78 79 Routine Description: 80 81 This routine implements the FSD part of Flush buffers. 82 83 Arguments: 84 85 VolumeDeviceObject - Supplies the volume device object where the 86 file being flushed exists 87 88 Irp - Supplies the Irp being processed 89 90 Return Value: 91 92 NTSTATUS - The FSD status for the IRP 93 94 --*/ 95 96 { 97 NTSTATUS Status; 98 PIRP_CONTEXT IrpContext = NULL; 99 100 BOOLEAN TopLevel; 101 102 PAGED_CODE(); 103 104 DebugTrace(+1, Dbg, "FatFsdFlushBuffers\n", 0); 105 106 // 107 // Call the common Cleanup routine, with blocking allowed if synchronous 108 // 109 110 FsRtlEnterFileSystem(); 111 112 TopLevel = FatIsIrpTopLevel( Irp ); 113 114 _SEH2_TRY { 115 116 IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) ); 117 118 Status = FatCommonFlushBuffers( IrpContext, Irp ); 119 120 } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) { 121 122 // 123 // We had some trouble trying to perform the requested 124 // operation, so we'll abort the I/O request with 125 // the error status that we get back from the 126 // execption code 127 // 128 129 Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() ); 130 } _SEH2_END; 131 132 if (TopLevel) { IoSetTopLevelIrp( NULL ); } 133 134 FsRtlExitFileSystem(); 135 136 // 137 // And return to our caller 138 // 139 140 DebugTrace(-1, Dbg, "FatFsdFlushBuffers -> %08lx\n", Status); 141 142 UNREFERENCED_PARAMETER( VolumeDeviceObject ); 143 144 return Status; 145 } 146 147 148 _Requires_lock_held_(_Global_critical_region_) 149 NTSTATUS 150 FatCommonFlushBuffers ( 151 IN PIRP_CONTEXT IrpContext, 152 IN PIRP Irp 153 ) 154 155 /*++ 156 157 Routine Description: 158 159 This is the common routine for flushing a buffer. 160 161 Arguments: 162 163 Irp - Supplies the Irp to process 164 165 Return Value: 166 167 NTSTATUS - The return status for the operation 168 169 --*/ 170 171 { 172 NTSTATUS Status; 173 174 PIO_STACK_LOCATION IrpSp; 175 176 PFILE_OBJECT FileObject; 177 178 TYPE_OF_OPEN TypeOfOpen; 179 PVCB Vcb; 180 PFCB Fcb; 181 PFCB NextFcb; 182 PCCB Ccb; 183 184 BOOLEAN VcbAcquired = FALSE; 185 BOOLEAN FcbAcquired = FALSE; 186 BOOLEAN FatFlushRequired = FALSE; 187 188 PAGED_CODE(); 189 190 IrpSp = IoGetCurrentIrpStackLocation( Irp ); 191 192 DebugTrace(+1, Dbg, "FatCommonFlushBuffers\n", 0); 193 DebugTrace( 0, Dbg, "Irp = %p\n", Irp); 194 DebugTrace( 0, Dbg, "->FileObject = %p\n", IrpSp->FileObject); 195 196 // 197 // Extract and decode the file object 198 // 199 200 FileObject = IrpSp->FileObject; 201 TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ); 202 203 // 204 // CcFlushCache is always synchronous, so if we can't wait enqueue 205 // the irp to the Fsp. 206 // 207 208 if ( !FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ) { 209 210 Status = FatFsdPostRequest( IrpContext, Irp ); 211 212 DebugTrace(-1, Dbg, "FatCommonFlushBuffers -> %08lx\n", Status ); 213 return Status; 214 } 215 216 Status = STATUS_SUCCESS; 217 218 _SEH2_TRY { 219 220 #if (NTDDI_VERSION >= NTDDI_WIN8) 221 222 if (FatDiskAccountingEnabled) { 223 224 PETHREAD OriginatingThread = NULL; 225 226 // 227 // Charge the flush to the originating thread. 228 // Try the Thread in Irp's tail first, if that is NULL, then charge 229 // the flush to current thread. 230 // 231 232 if ((Irp->Tail.Overlay.Thread != NULL) && 233 !IoIsSystemThread( Irp->Tail.Overlay.Thread )) { 234 235 OriginatingThread = Irp->Tail.Overlay.Thread; 236 237 } else { 238 239 OriginatingThread = PsGetCurrentThread(); 240 } 241 242 NT_ASSERT( OriginatingThread != NULL ); 243 244 PsUpdateDiskCounters( PsGetThreadProcess( OriginatingThread ), 245 0, 246 0, 247 0, 248 0, 249 1 ); 250 } 251 252 #endif 253 254 // 255 // Case on the type of open that we are trying to flush 256 // 257 258 switch (TypeOfOpen) { 259 260 case VirtualVolumeFile: 261 case EaFile: 262 case DirectoryFile: 263 DebugTrace(0, Dbg, "Flush that does nothing\n", 0); 264 break; 265 266 case UserFileOpen: 267 268 DebugTrace(0, Dbg, "Flush User File Open\n", 0); 269 270 (VOID)FatAcquireExclusiveFcb( IrpContext, Fcb ); 271 272 FcbAcquired = TRUE; 273 274 FatVerifyFcb( IrpContext, Fcb ); 275 276 // 277 // If the file is cached then flush its cache 278 // 279 280 Status = FatFlushFile( IrpContext, Fcb, Flush ); 281 282 // 283 // Also flush the file's dirent in the parent directory if the file 284 // flush worked. 285 // 286 287 if (NT_SUCCESS( Status )) { 288 289 // 290 // Insure that we get the filesize to disk correctly. This is 291 // benign if it was already good. 292 // 293 294 SetFlag(FileObject->Flags, FO_FILE_SIZE_CHANGED); 295 296 297 FatUpdateDirentFromFcb( IrpContext, FileObject, Fcb, Ccb ); 298 299 if (FlagOn(Fcb->FcbState, FCB_STATE_FLUSH_FAT)) { 300 301 FatFlushRequired = TRUE; 302 } 303 304 // 305 // Flush the parent Dcb's to get any dirent updates to disk. 306 // 307 308 NextFcb = Fcb->ParentDcb; 309 310 while (NextFcb != NULL) { 311 312 // 313 // Make sure the Fcb is OK. 314 // 315 316 _SEH2_TRY { 317 318 FatVerifyFcb( IrpContext, NextFcb ); 319 320 } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ? 321 EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { 322 323 FatResetExceptionState( IrpContext ); 324 } _SEH2_END; 325 326 if (NextFcb->FcbCondition == FcbGood) { 327 328 NTSTATUS LocalStatus; 329 330 LocalStatus = FatFlushFile( IrpContext, NextFcb, Flush ); 331 332 if (!NT_SUCCESS(LocalStatus)) { 333 334 Status = LocalStatus; 335 } 336 337 if (FlagOn(NextFcb->FcbState, FCB_STATE_FLUSH_FAT)) { 338 339 FatFlushRequired = TRUE; 340 } 341 } 342 343 NextFcb = NextFcb->ParentDcb; 344 } 345 346 // 347 // Flush the volume file to get any allocation information 348 // updates to disk. 349 // 350 351 if (FatFlushRequired) { 352 353 Status = FatFlushFat( IrpContext, Vcb ); 354 355 ClearFlag(Fcb->FcbState, FCB_STATE_FLUSH_FAT); 356 } 357 358 // 359 // Set the write through bit so that these modifications 360 // will be completed with the request. 361 // 362 363 SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH); 364 } 365 366 break; 367 368 case UserDirectoryOpen: 369 370 // 371 // If the user had opened the root directory then we'll 372 // oblige by flushing the volume. 373 // 374 375 if (NodeType(Fcb) != FAT_NTC_ROOT_DCB) { 376 377 DebugTrace(0, Dbg, "Flush a directory does nothing\n", 0); 378 break; 379 } 380 381 case UserVolumeOpen: 382 383 DebugTrace(0, Dbg, "Flush User Volume Open, or root dcb\n", 0); 384 385 // 386 // Acquire exclusive access to the Vcb. 387 // 388 389 { 390 BOOLEAN Finished; 391 #ifdef _MSC_VER 392 #pragma prefast( suppress:28931, "needed for debug build" ) 393 #endif 394 Finished = FatAcquireExclusiveVcb( IrpContext, Vcb ); 395 NT_ASSERT( Finished ); 396 } 397 398 VcbAcquired = TRUE; 399 400 // 401 // Mark the volume clean and then flush the volume file, 402 // and then all directories 403 // 404 405 Status = FatFlushVolume( IrpContext, Vcb, Flush ); 406 407 // 408 // If the volume was dirty, do the processing that the delayed 409 // callback would have done. 410 // 411 412 if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY)) { 413 414 // 415 // Cancel any pending clean volumes. 416 // 417 418 (VOID)KeCancelTimer( &Vcb->CleanVolumeTimer ); 419 (VOID)KeRemoveQueueDpc( &Vcb->CleanVolumeDpc ); 420 421 // 422 // The volume is now clean, note it. 423 // 424 425 if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY)) { 426 427 FatMarkVolume( IrpContext, Vcb, VolumeClean ); 428 ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY ); 429 } 430 431 // 432 // Unlock the volume if it is removable. 433 // 434 435 if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) && 436 !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE)) { 437 438 FatToggleMediaEjectDisable( IrpContext, Vcb, FALSE ); 439 } 440 } 441 442 break; 443 444 default: 445 446 #ifdef _MSC_VER 447 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) 448 #endif 449 FatBugCheck( TypeOfOpen, 0, 0 ); 450 } 451 452 FatUnpinRepinnedBcbs( IrpContext ); 453 454 } _SEH2_FINALLY { 455 456 DebugUnwind( FatCommonFlushBuffers ); 457 458 if (VcbAcquired) { FatReleaseVcb( IrpContext, Vcb ); } 459 460 if (FcbAcquired) { FatReleaseFcb( IrpContext, Fcb ); } 461 462 // 463 // If this is a normal termination then pass the request on 464 // to the target device object. 465 // 466 467 if (!_SEH2_AbnormalTermination()) { 468 469 #if (NTDDI_VERSION >= NTDDI_WIN8) 470 if ((IrpSp->MinorFunction != IRP_MN_FLUSH_DATA_ONLY) && 471 (IrpSp->MinorFunction != IRP_MN_FLUSH_NO_SYNC)) { 472 #endif 473 474 NTSTATUS DriverStatus; 475 476 // 477 // Get the next stack location, and copy over the stack location 478 // 479 480 IoCopyCurrentIrpStackLocationToNext(Irp); 481 482 // 483 // Set up the completion routine 484 // 485 486 IoSetCompletionRoutine( Irp, 487 FatFlushCompletionRoutine, 488 ULongToPtr( Status ), 489 TRUE, 490 TRUE, 491 TRUE ); 492 493 // 494 // Send the request. 495 // 496 497 DriverStatus = IoCallDriver(Vcb->TargetDeviceObject, Irp); 498 499 if ((DriverStatus == STATUS_PENDING) || 500 (!NT_SUCCESS(DriverStatus) && 501 (DriverStatus != STATUS_INVALID_DEVICE_REQUEST))) { 502 503 Status = DriverStatus; 504 } 505 506 Irp = NULL; 507 508 #if (NTDDI_VERSION >= NTDDI_WIN8) 509 } 510 #endif 511 512 // 513 // Complete the Irp if necessary and return to the caller. 514 // 515 516 FatCompleteRequest( IrpContext, Irp, Status ); 517 518 } 519 520 DebugTrace(-1, Dbg, "FatCommonFlushBuffers -> %08lx\n", Status); 521 } _SEH2_END; 522 523 return Status; 524 } 525 526 527 _Requires_lock_held_(_Global_critical_region_) 528 NTSTATUS 529 FatFlushDirectory ( 530 IN PIRP_CONTEXT IrpContext, 531 IN PDCB Dcb, 532 IN FAT_FLUSH_TYPE FlushType 533 ) 534 535 /*++ 536 537 Routine Description: 538 539 This routine non-recursively flushes a dcb tree. 540 541 Arguments: 542 543 Dcb - Supplies the Dcb being flushed 544 545 FlushType - Specifies the kind of flushing to perform 546 547 Return Value: 548 549 VOID 550 551 --*/ 552 553 { 554 PFCB Fcb; 555 PVCB Vcb; 556 PFCB NextFcb; 557 558 PDIRENT Dirent; 559 PBCB DirentBcb = NULL; 560 561 NTSTATUS Status; 562 NTSTATUS ReturnStatus = STATUS_SUCCESS; 563 564 BOOLEAN ClearWriteThroughOnExit = FALSE; 565 BOOLEAN ClearWaitOnExit = FALSE; 566 567 ULONG CorrectedFileSize = 0; 568 569 PAGED_CODE(); 570 571 NT_ASSERT( FatVcbAcquiredExclusive(IrpContext, Dcb->Vcb) ); 572 573 DebugTrace(+1, Dbg, "FatFlushDirectory, Dcb = %p\n", Dcb); 574 575 // 576 // First flush all the files, then the directories, to make sure all the 577 // file sizes and times get sets correctly on disk. 578 // 579 // We also have to check here if the "Ea Data. Sf" fcb really 580 // corressponds to an existing file. 581 // 582 583 if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH)) { 584 585 ClearWriteThroughOnExit = TRUE; 586 SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH); 587 } 588 589 if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) { 590 591 ClearWaitOnExit = TRUE; 592 SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); 593 } 594 595 Vcb = Dcb->Vcb; 596 Fcb = Dcb; 597 598 while (Fcb != NULL) { 599 600 NextFcb = FatGetNextFcbTopDown(IrpContext, Fcb, Dcb); 601 602 if ( (NodeType( Fcb ) == FAT_NTC_FCB) && 603 (Vcb->EaFcb != Fcb) && 604 !IsFileDeleted(IrpContext, Fcb)) { 605 606 (VOID)FatAcquireExclusiveFcb( IrpContext, Fcb ); 607 608 ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB ); 609 610 // 611 // Exception handler to catch and commute errors encountered 612 // doing the flush dance. We may encounter corruption, and 613 // should continue flushing the volume as much as possible. 614 // 615 616 _SEH2_TRY { 617 618 // 619 // Standard handler to release resources, etc. 620 // 621 622 _SEH2_TRY { 623 624 // 625 // Make sure the Fcb is OK. 626 // 627 628 _SEH2_TRY { 629 630 FatVerifyFcb( IrpContext, Fcb ); 631 632 } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ? 633 EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { 634 635 FatResetExceptionState( IrpContext ); 636 } _SEH2_END; 637 638 // 639 // If this Fcb is not good skip it. Note that a 'continue' 640 // here would be very expensive as we inside a try{} body. 641 // 642 643 if (Fcb->FcbCondition != FcbGood) { 644 645 try_leave( NOTHING); 646 } 647 648 // 649 // In case a handle was never closed and the FS and AS are more 650 // than a cluster different, do this truncate. 651 // 652 653 if ( FlagOn(Fcb->FcbState, FCB_STATE_TRUNCATE_ON_CLOSE) ) { 654 655 656 FatTruncateFileAllocation( IrpContext, 657 Fcb, 658 Fcb->Header.FileSize.LowPart ); 659 660 661 } 662 663 // 664 // Also compare the file's dirent in the parent directory 665 // with the size information in the Fcb and update 666 // it if neccessary. Note that we don't mark the Bcb dirty 667 // because we will be flushing the file object presently, and 668 // Mm knows what's really dirty. 669 // 670 671 FatGetDirentFromFcbOrDcb( IrpContext, 672 Fcb, 673 FALSE, 674 &Dirent, 675 &DirentBcb ); 676 677 678 CorrectedFileSize = Fcb->Header.FileSize.LowPart; 679 680 681 if (Dirent->FileSize != CorrectedFileSize) { 682 683 Dirent->FileSize = CorrectedFileSize; 684 685 686 } 687 688 // 689 // We must unpin the Bcb before the flush since we recursively tear up 690 // the tree if Mm decides that the data section is no longer referenced 691 // and the final close comes in for this file. If this parent has no 692 // more children as a result, we will try to initiate teardown on it 693 // and Cc will deadlock against the active count of this Bcb. 694 // 695 696 FatUnpinBcb( IrpContext, DirentBcb ); 697 698 // 699 // Now flush the file. Note that this may make the Fcb 700 // go away if Mm dereferences its file object. 701 // 702 703 Status = FatFlushFile( IrpContext, Fcb, FlushType ); 704 705 if (!NT_SUCCESS(Status)) { 706 707 ReturnStatus = Status; 708 } 709 710 } _SEH2_FINALLY { 711 712 FatUnpinBcb( IrpContext, DirentBcb ); 713 714 // 715 // Since we have the Vcb exclusive we know that if any closes 716 // come in it is because the CcPurgeCacheSection caused the 717 // Fcb to go away. 718 // 719 720 if ( !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB) ) { 721 722 FatReleaseFcb( (IRPCONTEXT), Fcb ); 723 } 724 } _SEH2_END; 725 } _SEH2_EXCEPT( (FsRtlIsNtstatusExpected( ReturnStatus = _SEH2_GetExceptionCode() ) != 0 ) ? 726 EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { 727 FatResetExceptionState( IrpContext ); 728 } _SEH2_END; 729 730 } 731 732 Fcb = NextFcb; 733 } 734 735 // 736 // OK, now flush the directories. 737 // 738 739 Fcb = Dcb; 740 741 while (Fcb != NULL) { 742 743 NextFcb = FatGetNextFcbTopDown(IrpContext, Fcb, Dcb); 744 745 if ( (NodeType( Fcb ) != FAT_NTC_FCB) && 746 !IsFileDeleted(IrpContext, Fcb) ) { 747 748 // 749 // Make sure the Fcb is OK. 750 // 751 752 _SEH2_TRY { 753 754 FatVerifyFcb( IrpContext, Fcb ); 755 756 } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ? 757 EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { 758 759 FatResetExceptionState( IrpContext ); 760 } _SEH2_END; 761 762 if (Fcb->FcbCondition == FcbGood) { 763 764 Status = FatFlushFile( IrpContext, Fcb, FlushType ); 765 766 if (!NT_SUCCESS(Status)) { 767 768 ReturnStatus = Status; 769 } 770 } 771 } 772 773 Fcb = NextFcb; 774 } 775 776 _SEH2_TRY { 777 778 FatUnpinRepinnedBcbs( IrpContext ); 779 780 } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) { 781 782 ReturnStatus = IrpContext->ExceptionStatus; 783 } _SEH2_END; 784 785 if (ClearWriteThroughOnExit) { 786 787 ClearFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH); 788 } 789 if (ClearWaitOnExit) { 790 791 ClearFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); 792 } 793 794 DebugTrace(-1, Dbg, "FatFlushDirectory -> 0x%08lx\n", ReturnStatus); 795 796 return ReturnStatus; 797 } 798 799 800 NTSTATUS 801 FatFlushFat ( 802 IN PIRP_CONTEXT IrpContext, 803 IN PVCB Vcb 804 ) 805 806 /*++ 807 808 Routine Description: 809 810 The function carefully flushes the entire FAT for a volume. It is 811 nessecary to dance around a bit because of complicated synchronization 812 reasons. 813 814 Arguments: 815 816 Vcb - Supplies the Vcb whose FAT is being flushed 817 818 Return Value: 819 820 VOID 821 822 --*/ 823 824 { 825 PBCB Bcb; 826 PVOID DontCare; 827 IO_STATUS_BLOCK Iosb; 828 LARGE_INTEGER Offset; 829 830 NTSTATUS ReturnStatus = STATUS_SUCCESS; 831 832 PAGED_CODE(); 833 834 // 835 // If this volume is write protected, no need to flush. 836 // 837 838 if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) { 839 840 return STATUS_SUCCESS; 841 } 842 843 // 844 // Make sure the Vcb is OK. 845 // 846 847 _SEH2_TRY { 848 849 FatVerifyVcb( IrpContext, Vcb ); 850 851 } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ? 852 EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { 853 854 FatResetExceptionState( IrpContext ); 855 } _SEH2_END; 856 857 if (Vcb->VcbCondition != VcbGood) { 858 859 return STATUS_FILE_INVALID; 860 } 861 862 // 863 // The only way we have to correctly synchronize things is to 864 // repin stuff, and then unpin repin it. 865 // 866 // With NT 5.0, we can use some new cache manager support to make 867 // this a lot more efficient (important for FAT32). Since we're 868 // only worried about ranges that are dirty - and since we're a 869 // modified-no-write stream - we can assume that if there is no 870 // BCB, there is no work to do in the range. I.e., the lazy writer 871 // beat us to it. 872 // 873 // This is much better than reading the entire FAT in and trying 874 // to punch it out (see the test in the write path to blow 875 // off writes that don't correspond to dirty ranges of the FAT). 876 // For FAT32, this would be a *lot* of reading. 877 // 878 879 if (Vcb->AllocationSupport.FatIndexBitSize != 12) { 880 881 // 882 // Walk through the Fat, one page at a time. 883 // 884 885 ULONG NumberOfPages; 886 ULONG Page; 887 888 NumberOfPages = ( FatReservedBytes(&Vcb->Bpb) + 889 FatBytesPerFat(&Vcb->Bpb) + 890 (PAGE_SIZE - 1) ) / PAGE_SIZE; 891 892 893 for ( Page = 0, Offset.QuadPart = 0; 894 Page < NumberOfPages; 895 Page++, Offset.LowPart += PAGE_SIZE ) { 896 897 _SEH2_TRY { 898 899 if (CcPinRead( Vcb->VirtualVolumeFile, 900 &Offset, 901 PAGE_SIZE, 902 PIN_WAIT | PIN_IF_BCB, 903 &Bcb, 904 &DontCare )) { 905 906 CcSetDirtyPinnedData( Bcb, NULL ); 907 CcRepinBcb( Bcb ); 908 CcUnpinData( Bcb ); 909 CcUnpinRepinnedBcb( Bcb, TRUE, &Iosb ); 910 911 if (!NT_SUCCESS(Iosb.Status)) { 912 913 ReturnStatus = Iosb.Status; 914 } 915 } 916 917 } _SEH2_EXCEPT(FatExceptionFilter(IrpContext, _SEH2_GetExceptionInformation())) { 918 919 ReturnStatus = IrpContext->ExceptionStatus; 920 continue; 921 } _SEH2_END; 922 } 923 924 } else { 925 926 // 927 // We read in the entire fat in the 12 bit case. 928 // 929 930 Offset.QuadPart = FatReservedBytes( &Vcb->Bpb ); 931 932 _SEH2_TRY { 933 934 if (CcPinRead( Vcb->VirtualVolumeFile, 935 &Offset, 936 FatBytesPerFat( &Vcb->Bpb ), 937 PIN_WAIT | PIN_IF_BCB, 938 &Bcb, 939 &DontCare )) { 940 941 CcSetDirtyPinnedData( Bcb, NULL ); 942 CcRepinBcb( Bcb ); 943 CcUnpinData( Bcb ); 944 CcUnpinRepinnedBcb( Bcb, TRUE, &Iosb ); 945 946 if (!NT_SUCCESS(Iosb.Status)) { 947 948 ReturnStatus = Iosb.Status; 949 } 950 } 951 952 } _SEH2_EXCEPT(FatExceptionFilter(IrpContext, _SEH2_GetExceptionInformation())) { 953 954 ReturnStatus = IrpContext->ExceptionStatus; 955 } _SEH2_END; 956 } 957 958 return ReturnStatus; 959 } 960 961 962 _Requires_lock_held_(_Global_critical_region_) 963 NTSTATUS 964 FatFlushVolume ( 965 IN PIRP_CONTEXT IrpContext, 966 IN PVCB Vcb, 967 IN FAT_FLUSH_TYPE FlushType 968 ) 969 970 /*++ 971 972 Routine Description: 973 974 The following routine is used to flush a volume to disk, including the 975 volume file, and ea file. 976 977 Arguments: 978 979 Vcb - Supplies the volume being flushed 980 981 FlushType - Specifies the kind of flushing to perform 982 983 Return Value: 984 985 NTSTATUS - The Status from the flush. 986 987 --*/ 988 989 { 990 NTSTATUS Status; 991 NTSTATUS ReturnStatus = STATUS_SUCCESS; 992 993 PAGED_CODE(); 994 995 // 996 // If this volume is write protected, no need to flush. 997 // 998 999 if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) { 1000 1001 return STATUS_SUCCESS; 1002 } 1003 1004 // 1005 // Flush all the files and directories. 1006 // 1007 1008 Status = FatFlushDirectory( IrpContext, Vcb->RootDcb, FlushType ); 1009 1010 if (!NT_SUCCESS(Status)) { 1011 1012 ReturnStatus = Status; 1013 } 1014 1015 // 1016 // Now Flush the FAT 1017 // 1018 1019 Status = FatFlushFat( IrpContext, Vcb ); 1020 1021 if (!NT_SUCCESS(Status)) { 1022 1023 ReturnStatus = Status; 1024 } 1025 1026 // 1027 // Unlock the volume if it is removable. 1028 // 1029 1030 if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) && 1031 !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE)) { 1032 1033 FatToggleMediaEjectDisable( IrpContext, Vcb, FALSE ); 1034 } 1035 1036 return ReturnStatus; 1037 } 1038 1039 1040 _Requires_lock_held_(_Global_critical_region_) 1041 NTSTATUS 1042 FatFlushFile ( 1043 IN PIRP_CONTEXT IrpContext, 1044 IN PFCB Fcb, 1045 IN FAT_FLUSH_TYPE FlushType 1046 ) 1047 1048 /*++ 1049 1050 Routine Description: 1051 1052 This routine simply flushes the data section on a file. 1053 1054 Arguments: 1055 1056 Fcb - Supplies the file being flushed 1057 1058 FlushType - Specifies the kind of flushing to perform 1059 1060 Return Value: 1061 1062 NTSTATUS - The Status from the flush. 1063 1064 --*/ 1065 1066 { 1067 IO_STATUS_BLOCK Iosb; 1068 PVCB Vcb = Fcb->Vcb; 1069 1070 PAGED_CODE(); 1071 1072 CcFlushCache( &Fcb->NonPaged->SectionObjectPointers, NULL, 0, &Iosb ); 1073 1074 1075 if ( !FlagOn( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB )) { 1076 1077 // 1078 // Grab and release PagingIo to serialize ourselves with the lazy writer. 1079 // This will work to ensure that all IO has completed on the cached 1080 // data. 1081 // 1082 // If we are to invalidate the file, now is the right time to do it. Do 1083 // it non-recursively so we don't thump children before their time. 1084 // 1085 1086 ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE); 1087 1088 if (FlushType == FlushAndInvalidate) { 1089 1090 FatMarkFcbCondition( IrpContext, Fcb, FcbBad, FALSE ); 1091 } 1092 1093 ExReleaseResourceLite( Fcb->Header.PagingIoResource ); 1094 } 1095 1096 return Iosb.Status; 1097 } 1098 1099 1100 NTSTATUS 1101 FatHijackIrpAndFlushDevice ( 1102 IN PIRP_CONTEXT IrpContext, 1103 IN PIRP Irp, 1104 IN PDEVICE_OBJECT TargetDeviceObject 1105 ) 1106 1107 /*++ 1108 1109 Routine Description: 1110 1111 This routine is called when we need to send a flush to a device but 1112 we don't have a flush Irp. What this routine does is make a copy 1113 of its current Irp stack location, but changes the Irp Major code 1114 to a IRP_MJ_FLUSH_BUFFERS amd then send it down, but cut it off at 1115 the knees in the completion routine, fix it up and return to the 1116 user as if nothing had happened. 1117 1118 Arguments: 1119 1120 Irp - The Irp to hijack 1121 1122 TargetDeviceObject - The device to send the request to. 1123 1124 Return Value: 1125 1126 NTSTATUS - The Status from the flush in case anybody cares. 1127 1128 --*/ 1129 1130 { 1131 KEVENT Event; 1132 NTSTATUS Status; 1133 PIO_STACK_LOCATION NextIrpSp; 1134 1135 PAGED_CODE(); 1136 1137 UNREFERENCED_PARAMETER( IrpContext ); 1138 1139 // 1140 // Get the next stack location, and copy over the stack location 1141 // 1142 1143 IoCopyCurrentIrpStackLocationToNext(Irp); 1144 1145 NextIrpSp = IoGetNextIrpStackLocation( Irp ); 1146 NextIrpSp->MajorFunction = IRP_MJ_FLUSH_BUFFERS; 1147 NextIrpSp->MinorFunction = 0; 1148 1149 // 1150 // Set up the completion routine 1151 // 1152 1153 KeInitializeEvent( &Event, NotificationEvent, FALSE ); 1154 1155 IoSetCompletionRoutine( Irp, 1156 FatHijackCompletionRoutine, 1157 &Event, 1158 TRUE, 1159 TRUE, 1160 TRUE ); 1161 1162 // 1163 // Send the request. 1164 // 1165 1166 Status = IoCallDriver( TargetDeviceObject, Irp ); 1167 1168 if (Status == STATUS_PENDING) { 1169 1170 KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL ); 1171 1172 Status = Irp->IoStatus.Status; 1173 } 1174 1175 // 1176 // If the driver doesn't support flushes, return SUCCESS. 1177 // 1178 1179 if (Status == STATUS_INVALID_DEVICE_REQUEST) { 1180 Status = STATUS_SUCCESS; 1181 } 1182 1183 Irp->IoStatus.Status = 0; 1184 Irp->IoStatus.Information = 0; 1185 1186 return Status; 1187 } 1188 1189 1190 VOID 1191 FatFlushFatEntries ( 1192 IN PIRP_CONTEXT IrpContext, 1193 IN PVCB Vcb, 1194 IN ULONG Cluster, 1195 IN ULONG Count 1196 ) 1197 1198 /*++ 1199 1200 Routine Description: 1201 1202 This macro flushes the FAT page(s) containing the passed in run. 1203 1204 Arguments: 1205 1206 Vcb - Supplies the volume being flushed 1207 1208 Cluster - The starting cluster 1209 1210 Count - The number of FAT entries in the run 1211 1212 Return Value: 1213 1214 VOID 1215 1216 --*/ 1217 1218 { 1219 ULONG ByteCount; 1220 LARGE_INTEGER FileOffset; 1221 1222 IO_STATUS_BLOCK Iosb; 1223 1224 PAGED_CODE(); 1225 1226 FileOffset.HighPart = 0; 1227 FileOffset.LowPart = FatReservedBytes( &Vcb->Bpb ); 1228 1229 if (Vcb->AllocationSupport.FatIndexBitSize == 12) { 1230 1231 FileOffset.LowPart += Cluster * 3 / 2; 1232 ByteCount = (Count * 3 / 2) + 1; 1233 1234 } else if (Vcb->AllocationSupport.FatIndexBitSize == 32) { 1235 1236 FileOffset.LowPart += Cluster * sizeof(ULONG); 1237 ByteCount = Count * sizeof(ULONG); 1238 1239 } else { 1240 1241 FileOffset.LowPart += Cluster * sizeof( USHORT ); 1242 ByteCount = Count * sizeof( USHORT ); 1243 1244 } 1245 1246 CcFlushCache( &Vcb->SectionObjectPointers, 1247 &FileOffset, 1248 ByteCount, 1249 &Iosb ); 1250 1251 if (NT_SUCCESS(Iosb.Status)) { 1252 Iosb.Status = FatHijackIrpAndFlushDevice( IrpContext, 1253 IrpContext->OriginatingIrp, 1254 Vcb->TargetDeviceObject ); 1255 } 1256 1257 if (!NT_SUCCESS(Iosb.Status)) { 1258 FatNormalizeAndRaiseStatus(IrpContext, Iosb.Status); 1259 } 1260 } 1261 1262 1263 VOID 1264 FatFlushDirentForFile ( 1265 IN PIRP_CONTEXT IrpContext, 1266 IN PFCB Fcb 1267 ) 1268 1269 /*++ 1270 1271 Routine Description: 1272 1273 This macro flushes the page containing a file's DIRENT in its parent. 1274 1275 Arguments: 1276 1277 Fcb - Supplies the file whose DIRENT is being flushed 1278 1279 Return Value: 1280 1281 VOID 1282 1283 --*/ 1284 1285 { 1286 LARGE_INTEGER FileOffset; 1287 IO_STATUS_BLOCK Iosb; 1288 1289 PAGED_CODE(); 1290 1291 FileOffset.QuadPart = Fcb->DirentOffsetWithinDirectory; 1292 1293 CcFlushCache( &Fcb->ParentDcb->NonPaged->SectionObjectPointers, 1294 &FileOffset, 1295 sizeof( DIRENT ), 1296 &Iosb ); 1297 1298 if (NT_SUCCESS(Iosb.Status)) { 1299 Iosb.Status = FatHijackIrpAndFlushDevice( IrpContext, 1300 IrpContext->OriginatingIrp, 1301 Fcb->Vcb->TargetDeviceObject ); 1302 } 1303 1304 if (!NT_SUCCESS(Iosb.Status)) { 1305 FatNormalizeAndRaiseStatus(IrpContext, Iosb.Status); 1306 } 1307 } 1308 1309 1310 // 1311 // Local support routine 1312 // 1313 1314 NTSTATUS 1315 NTAPI 1316 FatFlushCompletionRoutine ( 1317 IN PDEVICE_OBJECT DeviceObject, 1318 IN PIRP Irp, 1319 IN PVOID Contxt 1320 ) 1321 1322 { 1323 NTSTATUS Status = (NTSTATUS) (ULONG_PTR) Contxt; 1324 1325 if ( Irp->PendingReturned ) { 1326 1327 IoMarkIrpPending( Irp ); 1328 } 1329 1330 // 1331 // If the Irp got STATUS_INVALID_DEVICE_REQUEST, normalize it 1332 // to STATUS_SUCCESS. 1333 // 1334 1335 if (NT_SUCCESS(Irp->IoStatus.Status) || 1336 (Irp->IoStatus.Status == STATUS_INVALID_DEVICE_REQUEST)) { 1337 1338 Irp->IoStatus.Status = Status; 1339 } 1340 1341 UNREFERENCED_PARAMETER( DeviceObject ); 1342 UNREFERENCED_PARAMETER( Contxt ); 1343 1344 return STATUS_SUCCESS; 1345 } 1346 1347 // 1348 // Local support routine 1349 // 1350 1351 NTSTATUS 1352 NTAPI 1353 FatHijackCompletionRoutine ( 1354 _In_ PDEVICE_OBJECT DeviceObject, 1355 _In_ PIRP Irp, 1356 _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt 1357 ) 1358 1359 { 1360 // 1361 // Set the event so that our call will wake up. 1362 // 1363 1364 KeSetEvent( (PKEVENT)Contxt, 0, FALSE ); 1365 1366 UNREFERENCED_PARAMETER( DeviceObject ); 1367 UNREFERENCED_PARAMETER( Irp ); 1368 1369 return STATUS_MORE_PROCESSING_REQUIRED; 1370 } 1371 1372