1 /*++ 2 3 Copyright (c) 1989-2000 Microsoft Corporation 4 5 Module Name: 6 7 StrucSup.c 8 9 Abstract: 10 11 This module implements the Fat in-memory data structure manipulation 12 routines 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_STRUCSUP) 24 25 // 26 // The debug trace level 27 // 28 29 #define Dbg (DEBUG_TRACE_STRUCSUP) 30 31 #define FillMemory(BUF,SIZ,MASK) { \ 32 ULONG i; \ 33 for (i = 0; i < (((SIZ)/4) - 1); i += 2) { \ 34 ((PULONG)(BUF))[i] = (MASK); \ 35 ((PULONG)(BUF))[i+1] = (ULONG)PsGetCurrentThread(); \ 36 } \ 37 } 38 39 #define IRP_CONTEXT_HEADER (sizeof( IRP_CONTEXT ) * 0x10000 + FAT_NTC_IRP_CONTEXT) 40 41 // 42 // Local macros. 43 // 44 // Define our lookaside list allocators. For the time being, and perhaps 45 // permanently, the paged structures don't come off of lookasides. This 46 // is due to complications with clean unload as FAT can be in the paging 47 // path, making it really hard to find the right time to empty them. 48 // 49 // Fortunately, the hit rates on the Fcb/Ccb lists weren't stunning. 50 // 51 52 #define FAT_FILL_FREE 0 53 54 #ifdef __REACTOS__ 55 static 56 #endif 57 INLINE 58 PCCB 59 FatAllocateCcb ( 60 ) 61 { 62 return (PCCB) FsRtlAllocatePoolWithTag( PagedPool, sizeof(CCB), TAG_CCB ); 63 } 64 65 #ifdef __REACTOS__ 66 static 67 #endif 68 INLINE 69 VOID 70 FatFreeCcb ( 71 IN PCCB Ccb 72 ) 73 { 74 #if FAT_FILL_FREE 75 RtlFillMemoryUlong(Ccb, sizeof(CCB), FAT_FILL_FREE); 76 #endif 77 78 ExFreePool( Ccb ); 79 } 80 81 #ifdef __REACTOS__ 82 static 83 #endif 84 INLINE 85 PFCB 86 FatAllocateFcb ( 87 ) 88 { 89 return (PFCB) FsRtlAllocatePoolWithTag( PagedPool, sizeof(FCB), TAG_FCB ); 90 } 91 92 #ifdef __REACTOS__ 93 static 94 #endif 95 INLINE 96 VOID 97 FatFreeFcb ( 98 IN PFCB Fcb 99 ) 100 { 101 #if FAT_FILL_FREE 102 RtlFillMemoryUlong(Fcb, sizeof(FCB), FAT_FILL_FREE); 103 #endif 104 105 ExFreePool( Fcb ); 106 } 107 108 #ifdef __REACTOS__ 109 static 110 #endif 111 INLINE 112 PNON_PAGED_FCB 113 FatAllocateNonPagedFcb ( 114 ) 115 { 116 return (PNON_PAGED_FCB) ExAllocateFromNPagedLookasideList( &FatNonPagedFcbLookasideList ); 117 } 118 119 #ifdef __REACTOS__ 120 static 121 #endif 122 INLINE 123 VOID 124 FatFreeNonPagedFcb ( 125 PNON_PAGED_FCB NonPagedFcb 126 ) 127 { 128 #if FAT_FILL_FREE 129 RtlFillMemoryUlong(NonPagedFcb, sizeof(NON_PAGED_FCB), FAT_FILL_FREE); 130 #endif 131 132 ExFreeToNPagedLookasideList( &FatNonPagedFcbLookasideList, (PVOID) NonPagedFcb ); 133 } 134 135 #ifdef __REACTOS__ 136 static 137 #endif 138 INLINE 139 PERESOURCE 140 FatAllocateResource ( 141 ) 142 { 143 PERESOURCE Resource; 144 145 Resource = (PERESOURCE) ExAllocateFromNPagedLookasideList( &FatEResourceLookasideList ); 146 147 ExInitializeResourceLite( Resource ); 148 149 return Resource; 150 } 151 152 #ifdef __REACTOS__ 153 static 154 #endif 155 INLINE 156 VOID 157 FatFreeResource ( 158 IN PERESOURCE Resource 159 ) 160 { 161 ExDeleteResourceLite( Resource ); 162 163 #if FAT_FILL_FREE 164 RtlFillMemoryUlong(Resource, sizeof(ERESOURCE), FAT_FILL_FREE); 165 #endif 166 167 ExFreeToNPagedLookasideList( &FatEResourceLookasideList, (PVOID) Resource ); 168 } 169 170 #ifdef __REACTOS__ 171 static 172 #endif 173 INLINE 174 PIRP_CONTEXT 175 FatAllocateIrpContext ( 176 ) 177 { 178 return (PIRP_CONTEXT) ExAllocateFromNPagedLookasideList( &FatIrpContextLookasideList ); 179 } 180 181 #ifdef __REACTOS__ 182 static 183 #endif 184 INLINE 185 VOID 186 FatFreeIrpContext ( 187 IN PIRP_CONTEXT IrpContext 188 ) 189 { 190 #if FAT_FILL_FREE 191 RtlFillMemoryUlong(IrpContext, sizeof(IRP_CONTEXT), FAT_FILL_FREE); 192 #endif 193 194 ExFreeToNPagedLookasideList( &FatIrpContextLookasideList, (PVOID) IrpContext ); 195 } 196 197 #ifdef ALLOC_PRAGMA 198 #pragma alloc_text(PAGE, FatInitializeVcb) 199 #pragma alloc_text(PAGE, FatTearDownVcb) 200 #pragma alloc_text(PAGE, FatDeleteVcb) 201 #pragma alloc_text(PAGE, FatCreateRootDcb) 202 #pragma alloc_text(PAGE, FatCreateFcb) 203 #pragma alloc_text(PAGE, FatCreateDcb) 204 #pragma alloc_text(PAGE, FatDeleteFcb) 205 #pragma alloc_text(PAGE, FatCreateCcb) 206 #pragma alloc_text(PAGE, FatDeallocateCcbStrings) 207 #pragma alloc_text(PAGE, FatDeleteCcb) 208 #pragma alloc_text(PAGE, FatGetNextFcbTopDown) 209 #pragma alloc_text(PAGE, FatGetNextFcbBottomUp) 210 #pragma alloc_text(PAGE, FatConstructNamesInFcb) 211 #pragma alloc_text(PAGE, FatCheckFreeDirentBitmap) 212 #pragma alloc_text(PAGE, FatCreateIrpContext) 213 #pragma alloc_text(PAGE, FatDeleteIrpContext_Real) 214 #pragma alloc_text(PAGE, FatIsHandleCountZero) 215 #pragma alloc_text(PAGE, FatAllocateCloseContext) 216 #pragma alloc_text(PAGE, FatPreallocateCloseContext) 217 #pragma alloc_text(PAGE, FatEnsureStringBufferEnough) 218 #pragma alloc_text(PAGE, FatFreeStringBuffer) 219 #pragma alloc_text(PAGE, FatScanForDataTrack) 220 #endif 221 222 223 _Requires_lock_held_(_Global_critical_region_) 224 VOID 225 FatInitializeVcb ( 226 IN PIRP_CONTEXT IrpContext, 227 IN OUT PVCB Vcb, 228 IN PDEVICE_OBJECT TargetDeviceObject, 229 IN PVPB Vpb, 230 IN PDEVICE_OBJECT FsDeviceObject 231 ) 232 233 /*++ 234 235 Routine Description: 236 237 This routine initializes and inserts a new Vcb record into the in-memory 238 data structure. The Vcb record "hangs" off the end of the Volume device 239 object and must be allocated by our caller. 240 241 Arguments: 242 243 Vcb - Supplies the address of the Vcb record being initialized. 244 245 TargetDeviceObject - Supplies the address of the target device object to 246 associate with the Vcb record. 247 248 Vpb - Supplies the address of the Vpb to associate with the Vcb record. 249 250 FsDeviceObject - The filesystem device object that the mount was directed 251 too. 252 253 Return Value: 254 255 None. 256 257 --*/ 258 259 { 260 CC_FILE_SIZES FileSizes; 261 PDEVICE_OBJECT RealDevice; 262 ULONG i; 263 264 STORAGE_HOTPLUG_INFO HotplugInfo; 265 STORAGE_DEVICE_NUMBER StorDeviceNumber; 266 NTSTATUS Status; 267 268 // 269 // The following variables are used for abnormal unwind 270 // 271 272 PLIST_ENTRY UnwindEntryList = NULL; 273 PERESOURCE UnwindResource = NULL; 274 PERESOURCE UnwindResource2 = NULL; 275 PFILE_OBJECT UnwindFileObject = NULL; 276 PFILE_OBJECT UnwindCacheMap = NULL; 277 BOOLEAN UnwindWeAllocatedMcb = FALSE; 278 PFILE_SYSTEM_STATISTICS UnwindStatistics = NULL; 279 BOOLEAN UnwindWeAllocatedBadBlockMap = FALSE; 280 BOOLEAN CloseContextAllocated = FALSE; 281 282 PAGED_CODE(); 283 UNREFERENCED_PARAMETER( FsDeviceObject ); 284 285 DebugTrace(+1, Dbg, "FatInitializeVcb, Vcb = %p\n", Vcb); 286 287 _SEH2_TRY { 288 289 // 290 // We start by first zeroing out all of the VCB, this will guarantee 291 // that any stale data is wiped clean 292 // 293 294 RtlZeroMemory( Vcb, sizeof(VCB) ); 295 296 // 297 // Set the proper node type code and node byte size 298 // 299 300 Vcb->VolumeFileHeader.NodeTypeCode = FAT_NTC_VCB; 301 Vcb->VolumeFileHeader.NodeByteSize = sizeof(VCB); 302 303 // 304 // Initialize the tunneling cache 305 // 306 307 FsRtlInitializeTunnelCache(&Vcb->Tunnel); 308 309 // 310 // Insert this Vcb record on the FatData.VcbQueue 311 // 312 313 NT_ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); 314 315 316 #ifdef _MSC_VER 317 #pragma prefast( push ) 318 #pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" ) 319 #pragma prefast( disable: 28193, "this will always wait" ) 320 #endif 321 322 (VOID)FatAcquireExclusiveGlobal( IrpContext ); 323 324 #ifdef _MSC_VER 325 #pragma prefast( pop ) 326 #endif 327 328 InsertTailList( &FatData.VcbQueue, &Vcb->VcbLinks ); 329 FatReleaseGlobal( IrpContext ); 330 UnwindEntryList = &Vcb->VcbLinks; 331 332 // 333 // Set the Target Device Object, Vpb, and Vcb State fields 334 // 335 336 337 ObReferenceObject( TargetDeviceObject ); 338 Vcb->TargetDeviceObject = TargetDeviceObject; 339 Vcb->Vpb = Vpb; 340 341 Vcb->CurrentDevice = Vpb->RealDevice; 342 343 // 344 // Set the removable media and defflush flags based on the storage 345 // inquiry and the old characteristic bits. 346 // 347 348 Status = FatPerformDevIoCtrl( IrpContext, 349 IOCTL_STORAGE_GET_HOTPLUG_INFO, 350 TargetDeviceObject, 351 NULL, 352 0, 353 &HotplugInfo, 354 sizeof(HotplugInfo), 355 FALSE, 356 TRUE, 357 NULL ); 358 359 if (NT_SUCCESS( Status )) { 360 361 if (HotplugInfo.MediaRemovable) { 362 363 SetFlag( Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA ); 364 } 365 366 // 367 // If the media or device is hot-pluggable, then set this flag. 368 // 369 370 if (HotplugInfo.MediaHotplug || HotplugInfo.DeviceHotplug) { 371 372 SetFlag( Vcb->VcbState, VCB_STATE_FLAG_HOTPLUGGABLE ); 373 } 374 375 if (!HotplugInfo.WriteCacheEnableOverride) { 376 377 // 378 // If the device or media is hotplug and the override is not 379 // set, force defflush behavior for the device. 380 // 381 382 if (HotplugInfo.MediaHotplug || HotplugInfo.DeviceHotplug) { 383 384 NT_ASSERT( FlagOn( Vcb->VcbState, VCB_STATE_FLAG_HOTPLUGGABLE )); 385 386 SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH ); 387 388 // 389 // Now, for removables that claim to be lockable, lob a lock 390 // request and see if it works. There can unfortunately be 391 // transient, media dependent reasons that it can fail. If 392 // it does not, we must force defflush on. 393 // 394 395 } else if (HotplugInfo.MediaRemovable && 396 !HotplugInfo.MediaHotplug) { 397 398 Status = FatToggleMediaEjectDisable( IrpContext, Vcb, TRUE ); 399 400 if (!NT_SUCCESS( Status )) { 401 402 SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH ); 403 404 } 405 406 (VOID)FatToggleMediaEjectDisable( IrpContext, Vcb, FALSE ); 407 } 408 } 409 } 410 411 if (FlagOn(Vpb->RealDevice->Characteristics, FILE_REMOVABLE_MEDIA)) { 412 413 SetFlag( Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA ); 414 } 415 416 // 417 // Make sure we turn on deferred flushing for floppies like we always 418 // have. 419 // 420 421 if (FlagOn(Vpb->RealDevice->Characteristics, FILE_FLOPPY_DISKETTE)) { 422 423 SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH ); 424 } 425 426 // 427 // Query the storage device number. 428 // 429 430 Status = FatPerformDevIoCtrl( IrpContext, 431 IOCTL_STORAGE_GET_DEVICE_NUMBER, 432 TargetDeviceObject, 433 NULL, 434 0, 435 &StorDeviceNumber, 436 sizeof(StorDeviceNumber), 437 FALSE, 438 TRUE, 439 NULL ); 440 441 if (NT_SUCCESS( Status )) { 442 443 Vcb->DeviceNumber = StorDeviceNumber.DeviceNumber; 444 445 } else { 446 447 Vcb->DeviceNumber = (ULONG)(-1); 448 } 449 450 FatSetVcbCondition( Vcb, VcbGood); 451 452 // 453 // Initialize the resource variable for the Vcb 454 // 455 456 ExInitializeResourceLite( &Vcb->Resource ); 457 UnwindResource = &Vcb->Resource; 458 459 ExInitializeResourceLite( &Vcb->ChangeBitMapResource ); 460 UnwindResource2 = &Vcb->ChangeBitMapResource; 461 462 // 463 // Initialize the free cluster bitmap mutex. 464 // 465 466 ExInitializeFastMutex( &Vcb->FreeClusterBitMapMutex ); 467 468 // 469 // Create the special file object for the virtual volume file with a close 470 // context, its pointers back to the Vcb and the section object pointer. 471 // 472 // We don't have to unwind the close context. That will happen in the close 473 // path automatically. 474 // 475 476 RealDevice = Vcb->CurrentDevice; 477 478 FatPreallocateCloseContext(Vcb); 479 CloseContextAllocated = TRUE; 480 481 Vcb->VirtualVolumeFile = UnwindFileObject = IoCreateStreamFileObject( NULL, RealDevice ); 482 483 FatSetFileObject( Vcb->VirtualVolumeFile, 484 VirtualVolumeFile, 485 Vcb, 486 NULL ); 487 488 // 489 // Remember this internal, residual open. 490 // 491 492 InterlockedIncrement( (LONG*)&(Vcb->InternalOpenCount) ); 493 InterlockedIncrement( (LONG*)&(Vcb->ResidualOpenCount) ); 494 495 Vcb->VirtualVolumeFile->SectionObjectPointer = &Vcb->SectionObjectPointers; 496 497 Vcb->VirtualVolumeFile->ReadAccess = TRUE; 498 Vcb->VirtualVolumeFile->WriteAccess = TRUE; 499 Vcb->VirtualVolumeFile->DeleteAccess = TRUE; 500 501 // 502 // Initialize the notify structures. 503 // 504 505 InitializeListHead( &Vcb->DirNotifyList ); 506 507 FsRtlNotifyInitializeSync( &Vcb->NotifySync ); 508 509 // 510 // Initialize the Cache Map for the volume file. The size is 511 // initially set to that of our first read. It will be extended 512 // when we know how big the Fat is. 513 // 514 515 FileSizes.AllocationSize.QuadPart = 516 FileSizes.FileSize.QuadPart = sizeof(PACKED_BOOT_SECTOR); 517 FileSizes.ValidDataLength = FatMaxLarge; 518 519 FatInitializeCacheMap( Vcb->VirtualVolumeFile, 520 &FileSizes, 521 TRUE, 522 &FatData.CacheManagerNoOpCallbacks, 523 Vcb ); 524 525 UnwindCacheMap = Vcb->VirtualVolumeFile; 526 527 // 528 // Initialize the structure that will keep track of dirty fat sectors. 529 // The largest possible Mcb structures are less than 1K, so we use 530 // non paged pool. 531 // 532 533 FsRtlInitializeLargeMcb( &Vcb->DirtyFatMcb, PagedPool ); 534 535 UnwindWeAllocatedMcb = TRUE; 536 537 // 538 // Initialize the structure that will keep track of bad clusters on the volume. 539 // 540 // It will be empty until it is populated by FSCTL_GET_RETRIEVAL_POINTERS with a volume handle. 541 // 542 543 FsRtlInitializeLargeMcb( &Vcb->BadBlockMcb, PagedPool ); 544 UnwindWeAllocatedBadBlockMap = TRUE; 545 546 // 547 // Set the cluster index hint to the first valid cluster of a fat: 2 548 // 549 550 Vcb->ClusterHint = 2; 551 552 // 553 // Initialize the directory stream file object creation event. 554 // This event is also "borrowed" for async non-cached writes. 555 // 556 557 ExInitializeFastMutex( &Vcb->DirectoryFileCreationMutex ); 558 559 // 560 // Initialize the clean volume callback Timer and DPC. 561 // 562 563 KeInitializeTimer( &Vcb->CleanVolumeTimer ); 564 565 KeInitializeDpc( &Vcb->CleanVolumeDpc, FatCleanVolumeDpc, Vcb ); 566 567 // 568 // Initialize the performance counters. 569 // 570 571 Vcb->Statistics = FsRtlAllocatePoolWithTag( NonPagedPoolNx, 572 sizeof(FILE_SYSTEM_STATISTICS) * FatData.NumberProcessors, 573 TAG_VCB_STATS ); 574 UnwindStatistics = Vcb->Statistics; 575 576 RtlZeroMemory( Vcb->Statistics, sizeof(FILE_SYSTEM_STATISTICS) * FatData.NumberProcessors ); 577 578 for (i = 0; i < FatData.NumberProcessors; i += 1) { 579 Vcb->Statistics[i].Common.FileSystemType = FILESYSTEM_STATISTICS_TYPE_FAT; 580 Vcb->Statistics[i].Common.Version = 1; 581 Vcb->Statistics[i].Common.SizeOfCompleteStructure = 582 sizeof(FILE_SYSTEM_STATISTICS); 583 } 584 585 // 586 // Pick up a VPB right now so we know we can pull this filesystem stack off 587 // of the storage stack on demand. 588 // 589 590 Vcb->SwapVpb = FsRtlAllocatePoolWithTag( NonPagedPoolNx, 591 sizeof( VPB ), 592 TAG_VPB ); 593 594 RtlZeroMemory( Vcb->SwapVpb, sizeof( VPB ) ); 595 596 // 597 // Initialize the close queue listheads. 598 // 599 600 InitializeListHead( &Vcb->AsyncCloseList ); 601 InitializeListHead( &Vcb->DelayedCloseList ); 602 603 // 604 // Initialize the Advanced FCB Header 605 // 606 607 ExInitializeFastMutex( &Vcb->AdvancedFcbHeaderMutex ); 608 FsRtlSetupAdvancedHeader( &Vcb->VolumeFileHeader, 609 &Vcb->AdvancedFcbHeaderMutex ); 610 611 612 // 613 // With the Vcb now set up, set the IrpContext Vcb field. 614 // 615 616 IrpContext->Vcb = Vcb; 617 618 } _SEH2_FINALLY { 619 620 DebugUnwind( FatInitializeVcb ); 621 622 // 623 // If this is an abnormal termination then undo our work 624 // 625 626 if (_SEH2_AbnormalTermination()) { 627 628 if (UnwindCacheMap != NULL) { FatSyncUninitializeCacheMap( IrpContext, UnwindCacheMap ); } 629 if (UnwindFileObject != NULL) { ObDereferenceObject( UnwindFileObject ); } 630 if (UnwindResource != NULL) { FatDeleteResource( UnwindResource ); } 631 if (UnwindResource2 != NULL) { FatDeleteResource( UnwindResource2 ); } 632 if (UnwindWeAllocatedMcb) { FsRtlUninitializeLargeMcb( &Vcb->DirtyFatMcb ); } 633 if (UnwindWeAllocatedBadBlockMap) { FsRtlUninitializeLargeMcb(&Vcb->BadBlockMcb ); } 634 if (UnwindEntryList != NULL) { 635 #ifdef _MSC_VER 636 #pragma prefast( suppress: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" ) 637 #endif 638 (VOID)FatAcquireExclusiveGlobal( IrpContext ); 639 RemoveEntryList( UnwindEntryList ); 640 FatReleaseGlobal( IrpContext ); 641 } 642 if (UnwindStatistics != NULL) { ExFreePool( UnwindStatistics ); } 643 644 // 645 // Cleanup the close context we preallocated above. 646 // 647 648 if (CloseContextAllocated && (Vcb->VirtualVolumeFile == NULL)) { 649 650 // 651 // FatAllocateCloseContext does not allocate memory, it 652 // pulls a close context off the preallocated slist queue. 653 // 654 // Doing this here is necessary to balance out the one we 655 // preallocated for the Vcb earlier in this function, but 656 // only if we failed to create the virtual volume file. 657 // 658 // If VirtualVolumeFile is not NULL, then this CloseContext 659 // will get cleaned up when the close comes in for it during 660 // Vcb teardown. 661 // 662 663 PCLOSE_CONTEXT CloseContext = FatAllocateCloseContext(Vcb); 664 665 ExFreePool( CloseContext ); 666 CloseContextAllocated = FALSE; 667 } 668 } 669 670 DebugTrace(-1, Dbg, "FatInitializeVcb -> VOID\n", 0); 671 } _SEH2_END; 672 673 // 674 // and return to our caller 675 // 676 677 UNREFERENCED_PARAMETER( IrpContext ); 678 679 return; 680 } 681 682 683 VOID 684 FatTearDownVcb ( 685 IN PIRP_CONTEXT IrpContext, 686 IN PVCB Vcb 687 ) 688 689 /*++ 690 691 Routine Description: 692 693 This routine tries to remove all internal opens from the volume. 694 695 Arguments: 696 697 IrpContext - Supplies the context for the overall request. 698 699 Vcb - Supplies the Vcb to be torn down. 700 701 Return Value: 702 703 None 704 705 --*/ 706 707 { 708 PFILE_OBJECT DirectoryFileObject; 709 710 711 PAGED_CODE(); 712 713 // 714 // Get rid of the virtual volume file, if we need to. 715 // 716 717 if (Vcb->VirtualVolumeFile != NULL) { 718 719 // 720 // Uninitialize the cache 721 // 722 723 CcUninitializeCacheMap( Vcb->VirtualVolumeFile, 724 &FatLargeZero, 725 NULL ); 726 727 FsRtlTeardownPerStreamContexts( &Vcb->VolumeFileHeader ); 728 729 ObDereferenceObject( Vcb->VirtualVolumeFile ); 730 731 Vcb->VirtualVolumeFile = NULL; 732 } 733 734 // 735 // Close down the EA file. 736 // 737 738 FatCloseEaFile( IrpContext, Vcb, FALSE ); 739 740 // 741 // Close down the root directory stream.. 742 // 743 744 if (Vcb->RootDcb != NULL) { 745 746 DirectoryFileObject = Vcb->RootDcb->Specific.Dcb.DirectoryFile; 747 748 if (DirectoryFileObject != NULL) { 749 750 // 751 // Tear down this directory file. 752 // 753 754 CcUninitializeCacheMap( DirectoryFileObject, 755 &FatLargeZero, 756 NULL ); 757 758 Vcb->RootDcb->Specific.Dcb.DirectoryFile = NULL; 759 ObDereferenceObject( DirectoryFileObject ); 760 } 761 } 762 763 // 764 // The VCB can no longer be used. 765 // 766 767 FatSetVcbCondition( Vcb, VcbBad ); 768 } 769 770 771 VOID 772 FatDeleteVcb ( 773 IN PIRP_CONTEXT IrpContext, 774 IN PVCB Vcb 775 ) 776 777 /*++ 778 779 Routine Description: 780 781 This routine removes the Vcb record from Fat's in-memory data 782 structures. It also will remove all associated underlings 783 (i.e., FCB records). 784 785 Arguments: 786 787 Vcb - Supplies the Vcb to be removed 788 789 Return Value: 790 791 None 792 793 --*/ 794 795 { 796 PFCB Fcb; 797 798 PAGED_CODE(); 799 800 DebugTrace(+1, Dbg, "FatDeleteVcb, Vcb = %p\n", Vcb); 801 802 // 803 // If the IrpContext points to the VCB being deleted NULL out the stail 804 // pointer. 805 // 806 807 if (IrpContext->Vcb == Vcb) { 808 809 IrpContext->Vcb = NULL; 810 811 } 812 813 // 814 // Chuck the backpocket Vpb we kept just in case. 815 // 816 817 if (Vcb->SwapVpb) { 818 819 ExFreePool( Vcb->SwapVpb ); 820 821 } 822 823 // 824 // Free the VPB, if we need to. 825 // 826 827 if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VPB_MUST_BE_FREED )) { 828 829 // 830 // We swapped the VPB, so we need to free the main one. 831 // 832 833 ExFreePool( Vcb->Vpb ); 834 } 835 836 if (Vcb->VolumeGuidPath.Buffer) { 837 ExFreePool( Vcb->VolumeGuidPath.Buffer ); 838 Vcb->VolumeGuidPath.Buffer = NULL; 839 } 840 841 // 842 // Remove this record from the global list of all Vcb records. 843 // Note that the global lock must already be held when calling 844 // this function. 845 // 846 847 RemoveEntryList( &(Vcb->VcbLinks) ); 848 849 // 850 // Make sure the direct access open count is zero, and the open file count 851 // is also zero. 852 // 853 854 if ((Vcb->DirectAccessOpenCount != 0) || (Vcb->OpenFileCount != 0)) { 855 856 #ifdef _MSC_VER 857 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) 858 #endif 859 FatBugCheck( 0, 0, 0 ); 860 } 861 862 // 863 // Remove the EaFcb and dereference the Fcb for the Ea file if it 864 // exists. 865 // 866 867 if (Vcb->EaFcb != NULL) { 868 869 Vcb->EaFcb->OpenCount = 0; 870 FatDeleteFcb( IrpContext, &Vcb->EaFcb ); 871 } 872 873 // 874 // Remove the Root Dcb 875 // 876 877 if (Vcb->RootDcb != NULL) { 878 879 // 880 // Rundown stale child Fcbs that may be hanging around. Yes, this 881 // can happen. No, the create path isn't perfectly defensive about 882 // tearing down branches built up on creates that don't wind up 883 // succeeding. Normal system operation usually winds up having 884 // cleaned them out through re-visiting, but ... 885 // 886 // Just pick off Fcbs from the bottom of the tree until we run out. 887 // Then we delete the root Dcb. 888 // 889 890 while( (Fcb = FatGetNextFcbBottomUp( IrpContext, NULL, Vcb->RootDcb )) != Vcb->RootDcb ) { 891 892 FatDeleteFcb( IrpContext, &Fcb ); 893 } 894 895 FatDeleteFcb( IrpContext, &Vcb->RootDcb ); 896 } 897 898 // 899 // Uninitialize the notify sychronization object. 900 // 901 902 FsRtlNotifyUninitializeSync( &Vcb->NotifySync ); 903 904 // 905 // Uninitialize the resource variable for the Vcb 906 // 907 908 FatDeleteResource( &Vcb->Resource ); 909 FatDeleteResource( &Vcb->ChangeBitMapResource ); 910 911 // 912 // If allocation support has been setup, free it. 913 // 914 915 if (Vcb->FreeClusterBitMap.Buffer != NULL) { 916 917 FatTearDownAllocationSupport( IrpContext, Vcb ); 918 } 919 920 // 921 // UnInitialize the Mcb structure that kept track of dirty fat sectors. 922 // 923 924 FsRtlUninitializeLargeMcb( &Vcb->DirtyFatMcb ); 925 926 // 927 // Uninitialize the Mcb structure that kept track of bad sectors. 928 // 929 930 FsRtlUninitializeLargeMcb( &Vcb->BadBlockMcb ); 931 932 // 933 // Free the pool for the stached copy of the boot sector 934 // 935 936 if ( Vcb->First0x24BytesOfBootSector ) { 937 938 ExFreePool( Vcb->First0x24BytesOfBootSector ); 939 Vcb->First0x24BytesOfBootSector = NULL; 940 } 941 942 // 943 // Cancel the CleanVolume Timer and Dpc 944 // 945 946 (VOID)KeCancelTimer( &Vcb->CleanVolumeTimer ); 947 948 (VOID)KeRemoveQueueDpc( &Vcb->CleanVolumeDpc ); 949 950 // 951 // Free the performance counters memory 952 // 953 954 ExFreePool( Vcb->Statistics ); 955 956 // 957 // Clean out the tunneling cache 958 // 959 960 FsRtlDeleteTunnelCache(&Vcb->Tunnel); 961 962 // 963 // Dereference the target device object. 964 // 965 966 ObDereferenceObject( Vcb->TargetDeviceObject ); 967 968 // 969 // We better have used all the close contexts we allocated. There could be 970 // one remaining if we're doing teardown due to a final close coming in on 971 // a directory file stream object. It will be freed on the way back up. 972 // 973 974 NT_ASSERT( Vcb->CloseContextCount <= 1); 975 976 // 977 // And zero out the Vcb, this will help ensure that any stale data is 978 // wiped clean 979 // 980 981 RtlZeroMemory( Vcb, sizeof(VCB) ); 982 983 // 984 // return and tell the caller 985 // 986 987 DebugTrace(-1, Dbg, "FatDeleteVcb -> VOID\n", 0); 988 989 return; 990 } 991 992 993 _Requires_lock_held_(_Global_critical_region_) 994 VOID 995 FatCreateRootDcb ( 996 IN PIRP_CONTEXT IrpContext, 997 IN PVCB Vcb 998 ) 999 1000 /*++ 1001 1002 Routine Description: 1003 1004 This routine allocates, initializes, and inserts a new root DCB record 1005 into the in memory data structure. 1006 1007 Arguments: 1008 1009 Vcb - Supplies the Vcb to associate the new DCB under 1010 1011 Return Value: 1012 1013 None. The Vcb is modified in-place. 1014 1015 --*/ 1016 1017 { 1018 PDCB Dcb = NULL; 1019 1020 // 1021 // The following variables are used for abnormal unwind 1022 // 1023 1024 PVOID UnwindStorage[2] = { NULL, NULL }; 1025 PERESOURCE UnwindResource = NULL; 1026 PERESOURCE UnwindResource2 = NULL; 1027 PLARGE_MCB UnwindMcb = NULL; 1028 PFILE_OBJECT UnwindFileObject = NULL; 1029 1030 PAGED_CODE(); 1031 1032 DebugTrace(+1, Dbg, "FatCreateRootDcb, Vcb = %p\n", Vcb); 1033 1034 _SEH2_TRY { 1035 1036 // 1037 // Make sure we don't already have a root dcb for this vcb 1038 // 1039 1040 if (Vcb->RootDcb != NULL) { 1041 1042 DebugDump("Error trying to create multiple root dcbs\n", 0, Vcb); 1043 #ifdef _MSC_VER 1044 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) 1045 #endif 1046 FatBugCheck( 0, 0, 0 ); 1047 } 1048 1049 // 1050 // Allocate a new DCB and zero it out, we use Dcb locally so we don't 1051 // have to continually reference through the Vcb 1052 // 1053 1054 UnwindStorage[0] = Dcb = Vcb->RootDcb = FsRtlAllocatePoolWithTag( NonPagedPoolNx, 1055 sizeof(DCB), 1056 TAG_FCB ); 1057 1058 RtlZeroMemory( Dcb, sizeof(DCB)); 1059 1060 UnwindStorage[1] = 1061 Dcb->NonPaged = FatAllocateNonPagedFcb(); 1062 1063 RtlZeroMemory( Dcb->NonPaged, sizeof( NON_PAGED_FCB ) ); 1064 1065 // 1066 // Set the proper node type code, node byte size, and call backs 1067 // 1068 1069 Dcb->Header.NodeTypeCode = FAT_NTC_ROOT_DCB; 1070 Dcb->Header.NodeByteSize = sizeof(DCB); 1071 1072 Dcb->FcbCondition = FcbGood; 1073 1074 // 1075 // The parent Dcb, initial state, open count, dirent location 1076 // information, and directory change count fields are already zero so 1077 // we can skip setting them 1078 // 1079 1080 // 1081 // Initialize the resource variable 1082 // 1083 1084 UnwindResource = 1085 Dcb->Header.Resource = FatAllocateResource(); 1086 1087 // 1088 // Initialize the PagingIo Resource. We no longer use the FsRtl common 1089 // shared pool because this led to a) deadlocks due to cases where files 1090 // and their parent directories shared a resource and b) there is no way 1091 // to anticipate inter-driver induced deadlock via recursive operation. 1092 // 1093 1094 UnwindResource2 = 1095 Dcb->Header.PagingIoResource = FatAllocateResource(); 1096 1097 // 1098 // The root Dcb has an empty parent dcb links field 1099 // 1100 1101 InitializeListHead( &Dcb->ParentDcbLinks ); 1102 1103 // 1104 // Set the Vcb 1105 // 1106 1107 Dcb->Vcb = Vcb; 1108 1109 // 1110 // initialize the parent dcb queue. 1111 // 1112 1113 InitializeListHead( &Dcb->Specific.Dcb.ParentDcbQueue ); 1114 1115 // 1116 // Set the full file name up. 1117 // 1118 1119 Dcb->FullFileName.Buffer = L"\\"; 1120 Dcb->FullFileName.Length = (USHORT)2; 1121 Dcb->FullFileName.MaximumLength = (USHORT)4; 1122 1123 Dcb->ShortName.Name.Oem.Buffer = "\\"; 1124 Dcb->ShortName.Name.Oem.Length = (USHORT)1; 1125 Dcb->ShortName.Name.Oem.MaximumLength = (USHORT)2; 1126 1127 // 1128 // Construct a lie about file properties since we don't 1129 // have a proper "." entry to look at. 1130 // 1131 1132 Dcb->DirentFatFlags = FILE_ATTRIBUTE_DIRECTORY; 1133 1134 // 1135 // Initialize Advanced FCB Header fields 1136 // 1137 1138 ExInitializeFastMutex( &Dcb->NonPaged->AdvancedFcbHeaderMutex ); 1139 FsRtlSetupAdvancedHeader( &Dcb->Header, 1140 &Dcb->NonPaged->AdvancedFcbHeaderMutex ); 1141 1142 // 1143 // Initialize the Mcb, and setup its mapping. Note that the root 1144 // directory is a fixed size so we can set it everything up now. 1145 // 1146 1147 FsRtlInitializeLargeMcb( &Dcb->Mcb, NonPagedPoolNx ); 1148 UnwindMcb = &Dcb->Mcb; 1149 1150 if (FatIsFat32(Vcb)) { 1151 1152 // 1153 // The first cluster of fat32 roots comes from the BPB 1154 // 1155 1156 Dcb->FirstClusterOfFile = Vcb->Bpb.RootDirFirstCluster; 1157 1158 } else { 1159 1160 FatAddMcbEntry( Vcb, &Dcb->Mcb, 1161 0, 1162 FatRootDirectoryLbo( &Vcb->Bpb ), 1163 FatRootDirectorySize( &Vcb->Bpb )); 1164 } 1165 1166 if (FatIsFat32(Vcb)) { 1167 1168 // 1169 // Find the size of the fat32 root. As a side-effect, this will create 1170 // MCBs for the entire root. In the process of doing this, we may 1171 // discover that the FAT chain is bogus and raise corruption. 1172 // 1173 1174 Dcb->Header.AllocationSize.LowPart = 0xFFFFFFFF; 1175 FatLookupFileAllocationSize( IrpContext, Dcb); 1176 1177 Dcb->Header.FileSize.QuadPart = 1178 Dcb->Header.AllocationSize.QuadPart; 1179 } else { 1180 1181 // 1182 // set the allocation size to real size of the root directory 1183 // 1184 1185 Dcb->Header.FileSize.QuadPart = 1186 Dcb->Header.AllocationSize.QuadPart = FatRootDirectorySize( &Vcb->Bpb ); 1187 1188 } 1189 1190 // 1191 // Set our two create dirent aids to represent that we have yet to 1192 // enumerate the directory for never used or deleted dirents. 1193 // 1194 1195 Dcb->Specific.Dcb.UnusedDirentVbo = 0xffffffff; 1196 Dcb->Specific.Dcb.DeletedDirentHint = 0xffffffff; 1197 1198 // 1199 // Setup the free dirent bitmap buffer. 1200 // 1201 1202 RtlInitializeBitMap( &Dcb->Specific.Dcb.FreeDirentBitmap, 1203 NULL, 1204 0 ); 1205 1206 FatCheckFreeDirentBitmap( IrpContext, Dcb ); 1207 1208 #if (NTDDI_VERSION >= NTDDI_WIN8) 1209 // 1210 // Initialize the oplock structure. 1211 // 1212 1213 FsRtlInitializeOplock( FatGetFcbOplock(Dcb) ); 1214 #endif 1215 1216 } _SEH2_FINALLY { 1217 1218 DebugUnwind( FatCreateRootDcb ); 1219 1220 // 1221 // If this is an abnormal termination then undo our work 1222 // 1223 1224 if (_SEH2_AbnormalTermination()) { 1225 1226 ULONG i; 1227 1228 if (UnwindFileObject != NULL) { ObDereferenceObject( UnwindFileObject ); } 1229 if (UnwindMcb != NULL) { FsRtlUninitializeLargeMcb( UnwindMcb ); } 1230 if (UnwindResource != NULL) { FatFreeResource( UnwindResource ); } 1231 if (UnwindResource2 != NULL) { FatFreeResource( UnwindResource2 ); } 1232 1233 for (i = 0; i < sizeof(UnwindStorage)/sizeof(PVOID); i += 1) { 1234 if (UnwindStorage[i] != NULL) { ExFreePool( UnwindStorage[i] ); } 1235 } 1236 1237 // 1238 // Re-zero the entry in the Vcb. 1239 // 1240 1241 Vcb->RootDcb = NULL; 1242 } 1243 1244 DebugTrace(-1, Dbg, "FatCreateRootDcb -> %p\n", Dcb); 1245 } _SEH2_END; 1246 1247 return; 1248 } 1249 1250 1251 PFCB 1252 FatCreateFcb ( 1253 IN PIRP_CONTEXT IrpContext, 1254 IN PVCB Vcb, 1255 IN PDCB ParentDcb, 1256 IN ULONG LfnOffsetWithinDirectory, 1257 IN ULONG DirentOffsetWithinDirectory, 1258 IN PDIRENT Dirent, 1259 IN PUNICODE_STRING Lfn OPTIONAL, 1260 IN PUNICODE_STRING OrigLfn OPTIONAL, 1261 IN BOOLEAN IsPagingFile, 1262 IN BOOLEAN SingleResource 1263 ) 1264 1265 /*++ 1266 1267 Routine Description: 1268 1269 This routine allocates, initializes, and inserts a new Fcb record into 1270 the in-memory data structures. 1271 1272 Arguments: 1273 1274 Vcb - Supplies the Vcb to associate the new FCB under. 1275 1276 ParentDcb - Supplies the parent dcb that the new FCB is under. 1277 1278 LfnOffsetWithinDirectory - Supplies the offset of the LFN. If there is 1279 no LFN associated with this file then this value is same as 1280 DirentOffsetWithinDirectory. 1281 1282 DirentOffsetWithinDirectory - Supplies the offset, in bytes from the 1283 start of the directory file where the dirent for the fcb is located 1284 1285 Dirent - Supplies the dirent for the fcb being created 1286 1287 Lfn - Supplies a long UNICODE name associated with this file. 1288 1289 IsPagingFile - Indicates if we are creating an FCB for a paging file 1290 or some other type of file. 1291 1292 SingleResource - Indicates if this Fcb should share a single resource 1293 as both main and paging. 1294 1295 Return Value: 1296 1297 PFCB - Returns a pointer to the newly allocated FCB 1298 1299 --*/ 1300 1301 { 1302 PFCB Fcb = NULL; 1303 POOL_TYPE PoolType; 1304 1305 // 1306 // The following variables are used for abnormal unwind 1307 // 1308 1309 PVOID UnwindStorage[2] = { NULL, NULL }; 1310 PERESOURCE UnwindResource = NULL; 1311 PERESOURCE UnwindResource2 = NULL; 1312 PLIST_ENTRY UnwindEntryList = NULL; 1313 PLARGE_MCB UnwindMcb = NULL; 1314 PFILE_LOCK UnwindFileLock = NULL; 1315 POPLOCK UnwindOplock = NULL; 1316 1317 PAGED_CODE(); 1318 1319 UNREFERENCED_PARAMETER( OrigLfn ); 1320 1321 DebugTrace(+1, Dbg, "FatCreateFcb\n", 0); 1322 1323 _SEH2_TRY { 1324 1325 // 1326 // Determine the pool type we should be using for the fcb and the 1327 // mcb structure 1328 // 1329 1330 if (IsPagingFile) { 1331 1332 PoolType = NonPagedPoolNx; 1333 Fcb = UnwindStorage[0] = FsRtlAllocatePoolWithTag( NonPagedPoolNx, 1334 sizeof(FCB), 1335 TAG_FCB ); 1336 } else { 1337 1338 PoolType = PagedPool; 1339 Fcb = UnwindStorage[0] = FatAllocateFcb(); 1340 1341 } 1342 1343 // 1344 // ... and zero it out 1345 // 1346 1347 RtlZeroMemory( Fcb, sizeof(FCB) ); 1348 1349 UnwindStorage[1] = 1350 Fcb->NonPaged = FatAllocateNonPagedFcb(); 1351 1352 RtlZeroMemory( Fcb->NonPaged, sizeof( NON_PAGED_FCB ) ); 1353 1354 // 1355 // Set the proper node type code, node byte size, and call backs 1356 // 1357 1358 Fcb->Header.NodeTypeCode = FAT_NTC_FCB; 1359 Fcb->Header.NodeByteSize = sizeof(FCB); 1360 1361 Fcb->FcbCondition = FcbGood; 1362 1363 // 1364 // Check to see if we need to set the Fcb state to indicate that this 1365 // is a paging/system file. This will prevent it from being opened 1366 // again. 1367 // 1368 1369 if (IsPagingFile) { 1370 1371 SetFlag( Fcb->FcbState, FCB_STATE_PAGING_FILE | FCB_STATE_SYSTEM_FILE ); 1372 } 1373 1374 // 1375 // The initial state, open count, and segment objects fields are already 1376 // zero so we can skip setting them 1377 // 1378 1379 // 1380 // Initialize the resource variable 1381 // 1382 1383 1384 UnwindResource = 1385 Fcb->Header.Resource = FatAllocateResource(); 1386 1387 // 1388 // Initialize the PagingIo Resource. We no longer use the FsRtl common 1389 // shared pool because this led to a) deadlocks due to cases where files 1390 // and their parent directories shared a resource and b) there is no way 1391 // to anticipate inter-driver induced deadlock via recursive operation. 1392 // 1393 1394 if (SingleResource) { 1395 1396 Fcb->Header.PagingIoResource = Fcb->Header.Resource; 1397 1398 } else { 1399 1400 UnwindResource2 = 1401 Fcb->Header.PagingIoResource = FatAllocateResource(); 1402 } 1403 1404 // 1405 // Insert this fcb into our parent dcb's queue. 1406 // 1407 // There is a deep reason why this goes on the tail, to allow us 1408 // to easily enumerate all child directories before child files. 1409 // This is important to let us maintain whole-volume lockorder 1410 // via BottomUp enumeration. 1411 // 1412 1413 InsertTailList( &ParentDcb->Specific.Dcb.ParentDcbQueue, 1414 &Fcb->ParentDcbLinks ); 1415 UnwindEntryList = &Fcb->ParentDcbLinks; 1416 1417 // 1418 // Point back to our parent dcb 1419 // 1420 1421 Fcb->ParentDcb = ParentDcb; 1422 1423 // 1424 // Set the Vcb 1425 // 1426 1427 Fcb->Vcb = Vcb; 1428 1429 // 1430 // Set the dirent offset within the directory 1431 // 1432 1433 Fcb->LfnOffsetWithinDirectory = LfnOffsetWithinDirectory; 1434 Fcb->DirentOffsetWithinDirectory = DirentOffsetWithinDirectory; 1435 1436 // 1437 // Set the DirentFatFlags and LastWriteTime 1438 // 1439 1440 Fcb->DirentFatFlags = Dirent->Attributes; 1441 1442 Fcb->LastWriteTime = FatFatTimeToNtTime( IrpContext, 1443 Dirent->LastWriteTime, 1444 0 ); 1445 1446 // 1447 // These fields are only non-zero when in Chicago mode. 1448 // 1449 1450 if (FatData.ChicagoMode) { 1451 1452 LARGE_INTEGER FatSystemJanOne1980 = {0}; 1453 1454 // 1455 // If either date is possibly zero, get the system 1456 // version of 1/1/80. 1457 // 1458 1459 if ((((PUSHORT)Dirent)[9] & ((PUSHORT)Dirent)[8]) == 0) { 1460 1461 ExLocalTimeToSystemTime( &FatJanOne1980, 1462 &FatSystemJanOne1980 ); 1463 } 1464 1465 // 1466 // Only do the really hard work if this field is non-zero. 1467 // 1468 1469 if (((PUSHORT)Dirent)[9] != 0) { 1470 1471 Fcb->LastAccessTime = 1472 FatFatDateToNtTime( IrpContext, 1473 Dirent->LastAccessDate ); 1474 1475 } else { 1476 1477 Fcb->LastAccessTime = FatSystemJanOne1980; 1478 } 1479 1480 // 1481 // Only do the really hard work if this field is non-zero. 1482 // 1483 1484 if (((PUSHORT)Dirent)[8] != 0) { 1485 1486 Fcb->CreationTime = 1487 FatFatTimeToNtTime( IrpContext, 1488 Dirent->CreationTime, 1489 Dirent->CreationMSec ); 1490 1491 } else { 1492 1493 Fcb->CreationTime = FatSystemJanOne1980; 1494 } 1495 } 1496 1497 // 1498 // Initialize Advanced FCB Header fields 1499 // 1500 1501 ExInitializeFastMutex( &Fcb->NonPaged->AdvancedFcbHeaderMutex ); 1502 FsRtlSetupAdvancedHeader( &Fcb->Header, 1503 &Fcb->NonPaged->AdvancedFcbHeaderMutex ); 1504 1505 // 1506 // To make FAT match the present functionality of NTFS, disable 1507 // stream contexts on paging files 1508 // 1509 1510 if (IsPagingFile) { 1511 1512 SetFlag( Fcb->Header.Flags2, FSRTL_FLAG2_IS_PAGING_FILE ); 1513 ClearFlag( Fcb->Header.Flags2, FSRTL_FLAG2_SUPPORTS_FILTER_CONTEXTS ); 1514 } 1515 1516 // 1517 // Initialize the Mcb 1518 // 1519 1520 FsRtlInitializeLargeMcb( &Fcb->Mcb, PoolType ); 1521 UnwindMcb = &Fcb->Mcb; 1522 1523 // 1524 // Set the file size, valid data length, first cluster of file, 1525 // and allocation size based on the information stored in the dirent 1526 // 1527 1528 Fcb->Header.FileSize.LowPart = Dirent->FileSize; 1529 1530 Fcb->Header.ValidDataLength.LowPart = Dirent->FileSize; 1531 1532 Fcb->ValidDataToDisk = Dirent->FileSize; 1533 1534 Fcb->FirstClusterOfFile = (ULONG)Dirent->FirstClusterOfFile; 1535 1536 if ( FatIsFat32(Vcb) ) { 1537 1538 Fcb->FirstClusterOfFile += Dirent->FirstClusterOfFileHi << 16; 1539 } 1540 1541 if ( Fcb->FirstClusterOfFile == 0 ) { 1542 1543 Fcb->Header.AllocationSize.QuadPart = 0; 1544 1545 } else { 1546 1547 Fcb->Header.AllocationSize.QuadPart = FCB_LOOKUP_ALLOCATIONSIZE_HINT; 1548 } 1549 1550 1551 // 1552 // Initialize the Fcb's file lock record 1553 // 1554 1555 FsRtlInitializeFileLock( &Fcb->Specific.Fcb.FileLock, NULL, NULL ); 1556 UnwindFileLock = &Fcb->Specific.Fcb.FileLock; 1557 1558 // 1559 // Initialize the oplock structure. 1560 // 1561 1562 FsRtlInitializeOplock( FatGetFcbOplock(Fcb) ); 1563 UnwindOplock = FatGetFcbOplock(Fcb); 1564 1565 // 1566 // Indicate that Fast I/O is possible 1567 // 1568 1569 Fcb->Header.IsFastIoPossible = TRUE; 1570 1571 // 1572 // Set the file names. This must be the last thing we do. 1573 // 1574 1575 FatConstructNamesInFcb( IrpContext, 1576 Fcb, 1577 Dirent, 1578 Lfn ); 1579 1580 // 1581 // Drop the shortname hint so prefix searches can figure out 1582 // what they found 1583 // 1584 1585 Fcb->ShortName.FileNameDos = TRUE; 1586 1587 } _SEH2_FINALLY { 1588 1589 DebugUnwind( FatCreateFcb ); 1590 1591 // 1592 // If this is an abnormal termination then undo our work 1593 // 1594 1595 if (_SEH2_AbnormalTermination()) { 1596 1597 ULONG i; 1598 1599 if (UnwindOplock != NULL) { FsRtlUninitializeOplock( UnwindOplock ); } 1600 if (UnwindFileLock != NULL) { FsRtlUninitializeFileLock( UnwindFileLock ); } 1601 if (UnwindMcb != NULL) { FsRtlUninitializeLargeMcb( UnwindMcb ); } 1602 if (UnwindEntryList != NULL) { RemoveEntryList( UnwindEntryList ); } 1603 if (UnwindResource != NULL) { FatFreeResource( UnwindResource ); } 1604 if (UnwindResource2 != NULL) { FatFreeResource( UnwindResource2 ); } 1605 1606 for (i = 0; i < sizeof(UnwindStorage)/sizeof(PVOID); i += 1) { 1607 if (UnwindStorage[i] != NULL) { ExFreePool( UnwindStorage[i] ); } 1608 } 1609 } 1610 1611 DebugTrace(-1, Dbg, "FatCreateFcb -> %p\n", Fcb); 1612 } _SEH2_END; 1613 1614 // 1615 // return and tell the caller 1616 // 1617 1618 return Fcb; 1619 } 1620 1621 1622 PDCB 1623 FatCreateDcb ( 1624 IN PIRP_CONTEXT IrpContext, 1625 IN PVCB Vcb, 1626 IN PDCB ParentDcb, 1627 IN ULONG LfnOffsetWithinDirectory, 1628 IN ULONG DirentOffsetWithinDirectory, 1629 IN PDIRENT Dirent, 1630 IN PUNICODE_STRING Lfn OPTIONAL 1631 ) 1632 1633 /*++ 1634 1635 Routine Description: 1636 1637 This routine allocates, initializes, and inserts a new Dcb record into 1638 the in memory data structures. 1639 1640 Arguments: 1641 1642 Vcb - Supplies the Vcb to associate the new DCB under. 1643 1644 ParentDcb - Supplies the parent dcb that the new DCB is under. 1645 1646 LfnOffsetWithinDirectory - Supplies the offset of the LFN. If there is 1647 no LFN associated with this file then this value is same as 1648 DirentOffsetWithinDirectory. 1649 1650 DirentOffsetWithinDirectory - Supplies the offset, in bytes from the 1651 start of the directory file where the dirent for the fcb is located 1652 1653 Dirent - Supplies the dirent for the dcb being created 1654 1655 FileName - Supplies the file name of the file relative to the directory 1656 it's in (e.g., the file \config.sys is called "CONFIG.SYS" without 1657 the preceding backslash). 1658 1659 Lfn - Supplies a long UNICODE name associated with this directory. 1660 1661 Return Value: 1662 1663 PDCB - Returns a pointer to the newly allocated DCB 1664 1665 --*/ 1666 1667 { 1668 PDCB Dcb = NULL; 1669 1670 // 1671 // The following variables are used for abnormal unwind 1672 // 1673 1674 PVOID UnwindStorage[2] = { NULL, NULL }; 1675 PERESOURCE UnwindResource = NULL; 1676 PERESOURCE UnwindResource2 = NULL; 1677 PLIST_ENTRY UnwindEntryList = NULL; 1678 PLARGE_MCB UnwindMcb = NULL; 1679 POPLOCK UnwindOplock = NULL; 1680 1681 PAGED_CODE(); 1682 1683 DebugTrace(+1, Dbg, "FatCreateDcb\n", 0); 1684 1685 _SEH2_TRY { 1686 1687 // 1688 // assert that the only time we are called is if wait is true 1689 // 1690 1691 NT_ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); 1692 1693 // 1694 // Allocate a new DCB, and zero it out 1695 // 1696 1697 UnwindStorage[0] = Dcb = FatAllocateFcb(); 1698 1699 RtlZeroMemory( Dcb, sizeof(DCB) ); 1700 1701 UnwindStorage[1] = 1702 Dcb->NonPaged = FatAllocateNonPagedFcb(); 1703 1704 RtlZeroMemory( Dcb->NonPaged, sizeof( NON_PAGED_FCB ) ); 1705 1706 // 1707 // Set the proper node type code, node byte size and call backs 1708 // 1709 1710 Dcb->Header.NodeTypeCode = FAT_NTC_DCB; 1711 Dcb->Header.NodeByteSize = sizeof(DCB); 1712 1713 Dcb->FcbCondition = FcbGood; 1714 1715 // 1716 // The initial state, open count, and directory change count fields are 1717 // already zero so we can skip setting them 1718 // 1719 1720 // 1721 // Initialize the resource variable 1722 // 1723 1724 1725 UnwindResource = 1726 Dcb->Header.Resource = FatAllocateResource(); 1727 1728 // 1729 // Initialize the PagingIo Resource. We no longer use the FsRtl common 1730 // shared pool because this led to a) deadlocks due to cases where files 1731 // and their parent directories shared a resource and b) there is no way 1732 // to anticipate inter-driver induced deadlock via recursive operation. 1733 // 1734 1735 UnwindResource2 = 1736 Dcb->Header.PagingIoResource = FatAllocateResource(); 1737 1738 // 1739 // Insert this Dcb into our parent dcb's queue 1740 // 1741 // There is a deep reason why this goes on the head, to allow us 1742 // to easily enumerate all child directories before child files. 1743 // This is important to let us maintain whole-volume lockorder 1744 // via BottomUp enumeration. 1745 // 1746 1747 InsertHeadList( &ParentDcb->Specific.Dcb.ParentDcbQueue, 1748 &Dcb->ParentDcbLinks ); 1749 UnwindEntryList = &Dcb->ParentDcbLinks; 1750 1751 // 1752 // Point back to our parent dcb 1753 // 1754 1755 Dcb->ParentDcb = ParentDcb; 1756 1757 // 1758 // Set the Vcb 1759 // 1760 1761 Dcb->Vcb = Vcb; 1762 1763 // 1764 // Set the dirent offset within the directory 1765 // 1766 1767 Dcb->LfnOffsetWithinDirectory = LfnOffsetWithinDirectory; 1768 Dcb->DirentOffsetWithinDirectory = DirentOffsetWithinDirectory; 1769 1770 // 1771 // Set the DirentFatFlags and LastWriteTime 1772 // 1773 1774 Dcb->DirentFatFlags = Dirent->Attributes; 1775 1776 Dcb->LastWriteTime = FatFatTimeToNtTime( IrpContext, 1777 Dirent->LastWriteTime, 1778 0 ); 1779 1780 // 1781 // These fields are only non-zero when in Chicago mode. 1782 // 1783 1784 if (FatData.ChicagoMode) { 1785 1786 LARGE_INTEGER FatSystemJanOne1980 = {0}; 1787 1788 // 1789 // If either date is possibly zero, get the system 1790 // version of 1/1/80. 1791 // 1792 1793 if ((((PUSHORT)Dirent)[9] & ((PUSHORT)Dirent)[8]) == 0) { 1794 1795 ExLocalTimeToSystemTime( &FatJanOne1980, 1796 &FatSystemJanOne1980 ); 1797 } 1798 1799 // 1800 // Only do the really hard work if this field is non-zero. 1801 // 1802 1803 if (((PUSHORT)Dirent)[9] != 0) { 1804 1805 Dcb->LastAccessTime = 1806 FatFatDateToNtTime( IrpContext, 1807 Dirent->LastAccessDate ); 1808 1809 } else { 1810 1811 Dcb->LastAccessTime = FatSystemJanOne1980; 1812 } 1813 1814 // 1815 // Only do the really hard work if this field is non-zero. 1816 // 1817 1818 if (((PUSHORT)Dirent)[8] != 0) { 1819 1820 Dcb->CreationTime = 1821 FatFatTimeToNtTime( IrpContext, 1822 Dirent->CreationTime, 1823 Dirent->CreationMSec ); 1824 1825 } else { 1826 1827 Dcb->CreationTime = FatSystemJanOne1980; 1828 } 1829 } 1830 1831 // 1832 // Initialize Advanced FCB Header fields 1833 // 1834 1835 ExInitializeFastMutex( &Dcb->NonPaged->AdvancedFcbHeaderMutex ); 1836 FsRtlSetupAdvancedHeader( &Dcb->Header, 1837 &Dcb->NonPaged->AdvancedFcbHeaderMutex ); 1838 1839 // 1840 // Initialize the Mcb 1841 // 1842 1843 FsRtlInitializeLargeMcb( &Dcb->Mcb, PagedPool ); 1844 UnwindMcb = &Dcb->Mcb; 1845 1846 // 1847 // Set the file size, first cluster of file, and allocation size 1848 // based on the information stored in the dirent 1849 // 1850 1851 Dcb->FirstClusterOfFile = (ULONG)Dirent->FirstClusterOfFile; 1852 1853 if ( FatIsFat32(Dcb->Vcb) ) { 1854 1855 Dcb->FirstClusterOfFile += Dirent->FirstClusterOfFileHi << 16; 1856 } 1857 1858 if ( Dcb->FirstClusterOfFile == 0 ) { 1859 1860 Dcb->Header.AllocationSize.QuadPart = 0; 1861 1862 } else { 1863 1864 Dcb->Header.AllocationSize.QuadPart = FCB_LOOKUP_ALLOCATIONSIZE_HINT; 1865 } 1866 1867 1868 // initialize the notify queues, and the parent dcb queue. 1869 // 1870 1871 InitializeListHead( &Dcb->Specific.Dcb.ParentDcbQueue ); 1872 1873 // 1874 // Setup the free dirent bitmap buffer. Since we don't know the 1875 // size of the directory, leave it zero for now. 1876 // 1877 1878 RtlInitializeBitMap( &Dcb->Specific.Dcb.FreeDirentBitmap, 1879 NULL, 1880 0 ); 1881 1882 // 1883 // Set our two create dirent aids to represent that we have yet to 1884 // enumerate the directory for never used or deleted dirents. 1885 // 1886 1887 Dcb->Specific.Dcb.UnusedDirentVbo = 0xffffffff; 1888 Dcb->Specific.Dcb.DeletedDirentHint = 0xffffffff; 1889 1890 #if (NTDDI_VERSION >= NTDDI_WIN8) 1891 // 1892 // Initialize the oplock structure. 1893 // 1894 1895 FsRtlInitializeOplock( FatGetFcbOplock(Dcb) ); 1896 UnwindOplock = FatGetFcbOplock(Dcb); 1897 #endif 1898 1899 // 1900 // Postpone initializing the cache map until we need to do a read/write 1901 // of the directory file. 1902 1903 1904 // 1905 // set the file names. This must be the last thing we do. 1906 // 1907 1908 FatConstructNamesInFcb( IrpContext, 1909 Dcb, 1910 Dirent, 1911 Lfn ); 1912 1913 Dcb->ShortName.FileNameDos = TRUE; 1914 1915 } _SEH2_FINALLY { 1916 1917 DebugUnwind( FatCreateDcb ); 1918 1919 // 1920 // If this is an abnormal termination then undo our work 1921 // 1922 1923 if (_SEH2_AbnormalTermination()) { 1924 1925 ULONG i; 1926 1927 if (UnwindOplock != NULL) { FsRtlUninitializeOplock( UnwindOplock ); } 1928 if (UnwindMcb != NULL) { FsRtlUninitializeLargeMcb( UnwindMcb ); } 1929 if (UnwindEntryList != NULL) { RemoveEntryList( UnwindEntryList ); } 1930 if (UnwindResource != NULL) { FatFreeResource( UnwindResource ); } 1931 if (UnwindResource2 != NULL) { FatFreeResource( UnwindResource2 ); } 1932 1933 for (i = 0; i < sizeof(UnwindStorage)/sizeof(PVOID); i += 1) { 1934 if (UnwindStorage[i] != NULL) { ExFreePool( UnwindStorage[i] ); } 1935 } 1936 } 1937 1938 DebugTrace(-1, Dbg, "FatCreateDcb -> %p\n", Dcb); 1939 } _SEH2_END; 1940 1941 // 1942 // return and tell the caller 1943 // 1944 1945 DebugTrace(-1, Dbg, "FatCreateDcb -> %p\n", Dcb); 1946 1947 return Dcb; 1948 } 1949 1950 1951 VOID 1952 FatDeleteFcb ( 1953 IN PIRP_CONTEXT IrpContext, 1954 IN PFCB *FcbPtr 1955 ) 1956 1957 /*++ 1958 1959 Routine Description: 1960 1961 This routine deallocates and removes an FCB, DCB, or ROOT DCB record 1962 from Fat's in-memory data structures. It also will remove all 1963 associated underlings (i.e., Notify irps, and child FCB/DCB records). 1964 1965 Arguments: 1966 1967 Fcb - Supplies the FCB/DCB/ROOT DCB to be removed 1968 1969 Return Value: 1970 1971 None 1972 1973 --*/ 1974 1975 { 1976 PFCB Fcb = *FcbPtr; 1977 1978 PAGED_CODE(); 1979 1980 DebugTrace(+1, Dbg, "FatDeleteFcb, Fcb = %p\n", Fcb); 1981 1982 // 1983 // We can only delete this record if the open count is zero. 1984 // 1985 1986 if (Fcb->OpenCount != 0) { 1987 1988 DebugDump("Error deleting Fcb, Still Open\n", 0, Fcb); 1989 #ifdef _MSC_VER 1990 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) 1991 #endif 1992 FatBugCheck( 0, 0, 0 ); 1993 } 1994 1995 // 1996 // Better be an FCB/DCB. 1997 // 1998 1999 if ((Fcb->Header.NodeTypeCode != FAT_NTC_DCB) && 2000 (Fcb->Header.NodeTypeCode != FAT_NTC_ROOT_DCB) && 2001 (Fcb->Header.NodeTypeCode != FAT_NTC_FCB)) { 2002 2003 #ifdef _MSC_VER 2004 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) 2005 #endif 2006 FatBugCheck( 0, 0, 0 ); 2007 } 2008 2009 // 2010 // If this is a DCB then remove every Notify record from the two 2011 // notify queues 2012 // 2013 2014 if ((Fcb->Header.NodeTypeCode == FAT_NTC_DCB) || 2015 (Fcb->Header.NodeTypeCode == FAT_NTC_ROOT_DCB)) { 2016 2017 // 2018 // If we allocated a free dirent bitmap buffer, free it. 2019 // 2020 2021 if ((Fcb->Specific.Dcb.FreeDirentBitmap.Buffer != NULL) && 2022 (Fcb->Specific.Dcb.FreeDirentBitmap.Buffer != 2023 &Fcb->Specific.Dcb.FreeDirentBitmapBuffer[0])) { 2024 2025 ExFreePool(Fcb->Specific.Dcb.FreeDirentBitmap.Buffer); 2026 } 2027 2028 #if (NTDDI_VERSION >= NTDDI_WIN8) 2029 // 2030 // Uninitialize the oplock. 2031 // 2032 2033 FsRtlUninitializeOplock( FatGetFcbOplock(Fcb) ); 2034 #endif 2035 2036 NT_ASSERT( Fcb->Specific.Dcb.DirectoryFileOpenCount == 0 ); 2037 NT_ASSERT( IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue) ); 2038 NT_ASSERT( NULL == Fcb->Specific.Dcb.DirectoryFile); 2039 2040 } else { 2041 2042 // 2043 // Uninitialize the byte range file locks and opportunistic locks 2044 // 2045 2046 FsRtlUninitializeFileLock( &Fcb->Specific.Fcb.FileLock ); 2047 FsRtlUninitializeOplock( FatGetFcbOplock(Fcb) ); 2048 } 2049 2050 2051 // 2052 // Release any Filter Context structures associated with this FCB 2053 // 2054 2055 FsRtlTeardownPerStreamContexts( &Fcb->Header ); 2056 2057 // 2058 // Uninitialize the Mcb 2059 // 2060 2061 FsRtlUninitializeLargeMcb( &Fcb->Mcb ); 2062 2063 // 2064 // If this is not the root dcb then we need to remove ourselves from 2065 // our parents Dcb queue 2066 // 2067 2068 if (Fcb->Header.NodeTypeCode != FAT_NTC_ROOT_DCB) { 2069 2070 RemoveEntryList( &(Fcb->ParentDcbLinks) ); 2071 } 2072 2073 // 2074 // Remove the entry from the splay table if there is still is one. 2075 // 2076 2077 if (FlagOn( Fcb->FcbState, FCB_STATE_NAMES_IN_SPLAY_TREE )) { 2078 2079 FatRemoveNames( IrpContext, Fcb ); 2080 } 2081 2082 // 2083 // Free the file name pool if allocated. 2084 // 2085 2086 if (Fcb->Header.NodeTypeCode != FAT_NTC_ROOT_DCB) { 2087 2088 // 2089 // If we blew up at inconvenient times, the shortname 2090 // could be null even though you will *never* see this 2091 // normally. Rename is a good example of this case. 2092 // 2093 2094 if (Fcb->ShortName.Name.Oem.Buffer) { 2095 2096 ExFreePool( Fcb->ShortName.Name.Oem.Buffer ); 2097 } 2098 2099 if (Fcb->FullFileName.Buffer) { 2100 2101 ExFreePool( Fcb->FullFileName.Buffer ); 2102 } 2103 } 2104 2105 if (Fcb->ExactCaseLongName.Buffer) { 2106 2107 ExFreePool(Fcb->ExactCaseLongName.Buffer); 2108 } 2109 2110 #ifdef SYSCACHE_COMPILE 2111 2112 if (Fcb->WriteMask) { 2113 2114 ExFreePool( Fcb->WriteMask ); 2115 } 2116 2117 #endif 2118 2119 // 2120 // Finally deallocate the Fcb and non-paged fcb records 2121 // 2122 2123 FatFreeResource( Fcb->Header.Resource ); 2124 2125 if (Fcb->Header.PagingIoResource != Fcb->Header.Resource) { 2126 2127 FatFreeResource( Fcb->Header.PagingIoResource ); 2128 } 2129 2130 // 2131 // If an Event was allocated, get rid of it. 2132 // 2133 2134 if (Fcb->NonPaged->OutstandingAsyncEvent) { 2135 2136 ExFreePool( Fcb->NonPaged->OutstandingAsyncEvent ); 2137 } 2138 2139 FatFreeNonPagedFcb( Fcb->NonPaged ); 2140 2141 Fcb->Header.NodeTypeCode = NTC_UNDEFINED; 2142 2143 FatFreeFcb( Fcb ); 2144 *FcbPtr = NULL; 2145 2146 // 2147 // and return to our caller 2148 // 2149 2150 DebugTrace(-1, Dbg, "FatDeleteFcb -> VOID\n", 0); 2151 } 2152 2153 2154 PCCB 2155 FatCreateCcb ( 2156 IN PIRP_CONTEXT IrpContext 2157 ) 2158 2159 /*++ 2160 2161 Routine Description: 2162 2163 This routine creates a new CCB record 2164 2165 Arguments: 2166 2167 Return Value: 2168 2169 CCB - returns a pointer to the newly allocate CCB 2170 2171 --*/ 2172 2173 { 2174 PCCB Ccb; 2175 2176 PAGED_CODE(); 2177 2178 DebugTrace(+1, Dbg, "FatCreateCcb\n", 0); 2179 2180 // 2181 // Allocate a new CCB Record 2182 // 2183 2184 Ccb = FatAllocateCcb(); 2185 2186 RtlZeroMemory( Ccb, sizeof(CCB) ); 2187 2188 // 2189 // Set the proper node type code and node byte size 2190 // 2191 2192 Ccb->NodeTypeCode = FAT_NTC_CCB; 2193 Ccb->NodeByteSize = sizeof(CCB); 2194 2195 // 2196 // return and tell the caller 2197 // 2198 2199 DebugTrace(-1, Dbg, "FatCreateCcb -> %p\n", Ccb); 2200 2201 UNREFERENCED_PARAMETER( IrpContext ); 2202 2203 return Ccb; 2204 } 2205 2206 2207 2208 VOID 2209 FatDeallocateCcbStrings( 2210 IN PCCB Ccb 2211 ) 2212 /*++ 2213 2214 Routine Description: 2215 2216 This routine deallocates CCB query templates 2217 2218 Arguments: 2219 2220 Ccb - Supplies the CCB 2221 2222 Return Value: 2223 2224 None 2225 2226 --*/ 2227 { 2228 PAGED_CODE(); 2229 2230 // 2231 // If we allocated query template buffers, deallocate them now. 2232 // 2233 2234 if (FlagOn(Ccb->Flags, CCB_FLAG_FREE_UNICODE)) { 2235 2236 NT_ASSERT( Ccb->UnicodeQueryTemplate.Buffer); 2237 NT_ASSERT( !FlagOn( Ccb->Flags, CCB_FLAG_CLOSE_CONTEXT)); 2238 RtlFreeUnicodeString( &Ccb->UnicodeQueryTemplate ); 2239 } 2240 2241 if (FlagOn(Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT)) { 2242 2243 NT_ASSERT( Ccb->OemQueryTemplate.Wild.Buffer ); 2244 NT_ASSERT( !FlagOn( Ccb->Flags, CCB_FLAG_CLOSE_CONTEXT)); 2245 RtlFreeOemString( &Ccb->OemQueryTemplate.Wild ); 2246 } 2247 2248 ClearFlag( Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT | CCB_FLAG_FREE_UNICODE); 2249 } 2250 2251 2252 2253 VOID 2254 FatDeleteCcb ( 2255 IN PIRP_CONTEXT IrpContext, 2256 IN PCCB *Ccb 2257 ) 2258 2259 /*++ 2260 2261 Routine Description: 2262 2263 This routine deallocates and removes the specified CCB record 2264 from the Fat in memory data structures 2265 2266 Arguments: 2267 2268 Ccb - Supplies the CCB to remove 2269 2270 Return Value: 2271 2272 None 2273 2274 --*/ 2275 2276 { 2277 PAGED_CODE(); 2278 2279 DebugTrace(+1, Dbg, "FatDeleteCcb, Ccb = %p\n", *Ccb); 2280 2281 FatDeallocateCcbStrings( *Ccb); 2282 2283 // 2284 // Deallocate the Ccb record 2285 // 2286 2287 FatFreeCcb( *Ccb ); 2288 *Ccb = NULL; 2289 2290 // 2291 // return and tell the caller 2292 // 2293 2294 DebugTrace(-1, Dbg, "FatDeleteCcb -> VOID\n", 0); 2295 2296 UNREFERENCED_PARAMETER( IrpContext ); 2297 } 2298 2299 2300 PIRP_CONTEXT 2301 FatCreateIrpContext ( 2302 IN PIRP Irp, 2303 IN BOOLEAN Wait 2304 ) 2305 2306 /*++ 2307 2308 Routine Description: 2309 2310 This routine creates a new IRP_CONTEXT record 2311 2312 Arguments: 2313 2314 Irp - Supplies the originating Irp. 2315 2316 Wait - Supplies the wait value to store in the context 2317 2318 Return Value: 2319 2320 PIRP_CONTEXT - returns a pointer to the newly allocate IRP_CONTEXT Record 2321 2322 --*/ 2323 2324 { 2325 PIRP_CONTEXT IrpContext; 2326 PIO_STACK_LOCATION IrpSp; 2327 2328 PAGED_CODE(); 2329 2330 DebugTrace(+1, Dbg, "FatCreateIrpContext\n", 0); 2331 2332 IrpSp = IoGetCurrentIrpStackLocation( Irp ); 2333 2334 // 2335 // The only operations a filesystem device object should ever receive 2336 // are create/teardown of fsdo handles and operations which do not 2337 // occur in the context of fileobjects (i.e., mount). 2338 // 2339 2340 if (FatDeviceIsFatFsdo( IrpSp->DeviceObject)) { 2341 2342 if (IrpSp->FileObject != NULL && 2343 IrpSp->MajorFunction != IRP_MJ_CREATE && 2344 IrpSp->MajorFunction != IRP_MJ_CLEANUP && 2345 IrpSp->MajorFunction != IRP_MJ_CLOSE) { 2346 2347 ExRaiseStatus( STATUS_INVALID_DEVICE_REQUEST ); 2348 } 2349 2350 NT_ASSERT( IrpSp->FileObject != NULL || 2351 2352 (IrpSp->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL && 2353 IrpSp->MinorFunction == IRP_MN_USER_FS_REQUEST && 2354 IrpSp->Parameters.FileSystemControl.FsControlCode == FSCTL_INVALIDATE_VOLUMES) || 2355 2356 (IrpSp->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL && 2357 IrpSp->MinorFunction == IRP_MN_MOUNT_VOLUME ) || 2358 2359 IrpSp->MajorFunction == IRP_MJ_SHUTDOWN ); 2360 } 2361 2362 // 2363 // Attemtp to allocate from the region first and failing that allocate 2364 // from pool. 2365 // 2366 2367 DebugDoit( FatFsdEntryCount += 1); 2368 2369 IrpContext = FatAllocateIrpContext(); 2370 2371 // 2372 // Zero out the irp context. 2373 // 2374 2375 RtlZeroMemory( IrpContext, sizeof(IRP_CONTEXT) ); 2376 2377 // 2378 // Set the proper node type code and node byte size 2379 // 2380 2381 IrpContext->NodeTypeCode = FAT_NTC_IRP_CONTEXT; 2382 IrpContext->NodeByteSize = sizeof(IRP_CONTEXT); 2383 2384 // 2385 // Set the originating Irp field 2386 // 2387 2388 IrpContext->OriginatingIrp = Irp; 2389 2390 // 2391 // Major/Minor Function codes 2392 // 2393 2394 IrpContext->MajorFunction = IrpSp->MajorFunction; 2395 IrpContext->MinorFunction = IrpSp->MinorFunction; 2396 2397 // 2398 // Copy RealDevice for workque algorithms, and also set Write Through 2399 // and Removable Media if there is a file object. Only file system 2400 // control Irps won't have a file object, and they should all have 2401 // a Vpb as the first IrpSp location. 2402 // 2403 2404 if (IrpSp->FileObject != NULL) { 2405 2406 PFILE_OBJECT FileObject = IrpSp->FileObject; 2407 2408 IrpContext->RealDevice = FileObject->DeviceObject; 2409 IrpContext->Vcb = &((PVOLUME_DEVICE_OBJECT)(IrpSp->DeviceObject))->Vcb; 2410 2411 // 2412 // See if the request is Write Through. Look for both FileObjects opened 2413 // as write through, and non-cached requests with the SL_WRITE_THROUGH flag set. 2414 // 2415 // The latter can only originate from kernel components. (Note - NtWriteFile() 2416 // does redundantly set the SL_W_T flag for all requests it issues on write 2417 // through file objects) 2418 // 2419 2420 if (IsFileWriteThrough( FileObject, IrpContext->Vcb ) || 2421 ( (IrpSp->MajorFunction == IRP_MJ_WRITE) && 2422 BooleanFlagOn( Irp->Flags, IRP_NOCACHE) && 2423 BooleanFlagOn( IrpSp->Flags, SL_WRITE_THROUGH))) { 2424 2425 SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH); 2426 } 2427 } else if (IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) { 2428 2429 IrpContext->RealDevice = IrpSp->Parameters.MountVolume.Vpb->RealDevice; 2430 } 2431 2432 // 2433 // Set the wait parameter 2434 // 2435 2436 if (Wait) { SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); } 2437 2438 // 2439 // Set the recursive file system call parameter. We set it true if 2440 // the TopLevelIrp field in the thread local storage is not the current 2441 // irp, otherwise we leave it as FALSE. 2442 // 2443 2444 if ( IoGetTopLevelIrp() != Irp) { 2445 2446 SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_RECURSIVE_CALL); 2447 } 2448 2449 // 2450 // return and tell the caller 2451 // 2452 2453 DebugTrace(-1, Dbg, "FatCreateIrpContext -> %p\n", IrpContext); 2454 2455 return IrpContext; 2456 } 2457 2458 2459 2460 VOID 2461 FatDeleteIrpContext_Real ( 2462 IN PIRP_CONTEXT IrpContext 2463 ) 2464 2465 /*++ 2466 2467 Routine Description: 2468 2469 This routine deallocates and removes the specified IRP_CONTEXT record 2470 from the Fat in memory data structures. It should only be called 2471 by FatCompleteRequest. 2472 2473 Arguments: 2474 2475 IrpContext - Supplies the IRP_CONTEXT to remove 2476 2477 Return Value: 2478 2479 None 2480 2481 --*/ 2482 2483 { 2484 PAGED_CODE(); 2485 2486 DebugTrace(+1, Dbg, "FatDeleteIrpContext, IrpContext = %p\n", IrpContext); 2487 2488 NT_ASSERT( IrpContext->NodeTypeCode == FAT_NTC_IRP_CONTEXT ); 2489 NT_ASSERT( IrpContext->PinCount == 0 ); 2490 2491 2492 // 2493 // If there is a FatIoContext that was allocated, free it. 2494 // 2495 2496 if (IrpContext->FatIoContext != NULL) { 2497 2498 if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_STACK_IO_CONTEXT)) { 2499 2500 if (IrpContext->FatIoContext->ZeroMdl) { 2501 IoFreeMdl( IrpContext->FatIoContext->ZeroMdl ); 2502 } 2503 2504 ExFreePool( IrpContext->FatIoContext ); 2505 } 2506 } 2507 2508 // 2509 // Drop the IrpContext. 2510 // 2511 2512 FatFreeIrpContext( IrpContext ); 2513 2514 // 2515 // return and tell the caller 2516 // 2517 2518 DebugTrace(-1, Dbg, "FatDeleteIrpContext -> VOID\n", 0); 2519 2520 return; 2521 } 2522 2523 2524 PFCB 2525 FatGetNextFcbBottomUp ( 2526 IN PIRP_CONTEXT IrpContext, 2527 IN PFCB Fcb OPTIONAL, 2528 IN PFCB TerminationFcb 2529 ) 2530 2531 /*++ 2532 2533 Routine Description: 2534 2535 This routine is used to iterate through Fcbs in a tree. In order to match 2536 the lockorder for getting multiple Fcbs (so this can be used for acquiring 2537 all Fcbs), this version does a bottom-up enumeration. 2538 2539 This is different than the old one, now called TopDown. The problem with 2540 lockorder was very well hidden. 2541 2542 The transition rule is still pretty simple: 2543 2544 A) If you have an adjacent sibling, go to it 2545 1) Descend to its leftmost child 2546 B) Else go to your parent 2547 2548 If this routine is called with in invalid TerminationFcb it will fail, 2549 badly. 2550 2551 The TerminationFcb is the last Fcb returned in the enumeration. 2552 2553 This method is incompatible with the possibility that ancestors may vanish 2554 based on operations done on the last returned node. For instance, 2555 FatPurgeReferencedFileObjects cannot use BottomUp enumeration. 2556 2557 Arguments: 2558 2559 Fcb - Supplies the current Fcb. This is NULL if enumeration is starting. 2560 2561 TerminationFcb - The root Fcb of the tree in which the enumeration starts 2562 and at which it inclusively stops. 2563 2564 Return Value: 2565 2566 The next Fcb in the enumeration, or NULL if Fcb was the final one. 2567 2568 --*/ 2569 2570 { 2571 PFCB NextFcb; 2572 2573 PAGED_CODE(); 2574 UNREFERENCED_PARAMETER( IrpContext ); 2575 2576 NT_ASSERT( FatVcbAcquiredExclusive( IrpContext, TerminationFcb->Vcb ) || 2577 FlagOn( TerminationFcb->Vcb->VcbState, VCB_STATE_FLAG_LOCKED ) ); 2578 2579 // 2580 // Do we need to begin the enumeration? 2581 // 2582 2583 if (Fcb != NULL) { 2584 2585 // 2586 // Did we complete? 2587 // 2588 2589 if (Fcb == TerminationFcb) { 2590 2591 return NULL; 2592 } 2593 2594 // 2595 // Do we have a sibling to return? 2596 // 2597 2598 NextFcb = FatGetNextSibling( Fcb ); 2599 2600 // 2601 // If not, return our parent. We are done with this branch. 2602 // 2603 2604 if (NextFcb == NULL) { 2605 2606 return Fcb->ParentDcb; 2607 } 2608 2609 } else { 2610 2611 NextFcb = TerminationFcb; 2612 } 2613 2614 // 2615 // Decend to its furthest child (if it exists) and return it. 2616 // 2617 2618 for (; 2619 NodeType( NextFcb ) != FAT_NTC_FCB && FatGetFirstChild( NextFcb ) != NULL; 2620 NextFcb = FatGetFirstChild( NextFcb )) { 2621 } 2622 2623 return NextFcb; 2624 } 2625 2626 PFCB 2627 FatGetNextFcbTopDown ( 2628 IN PIRP_CONTEXT IrpContext, 2629 IN PFCB Fcb, 2630 IN PFCB TerminationFcb 2631 ) 2632 2633 /*++ 2634 2635 Routine Description: 2636 2637 This routine is used to iterate through Fcbs in a tree, from the top down. 2638 2639 The rule is very simple: 2640 2641 A) If you have a child, go to it, else 2642 B) If you have an older sibling, go to it, else 2643 C) Go to your parent's older sibling. 2644 2645 If this routine is called with in invalid TerminationFcb it will fail, 2646 badly. 2647 2648 The Termination Fcb is never returned. If it is the root of the tree you 2649 are traversing, visit it first. 2650 2651 This routine never returns direct ancestors of Fcb, and thus is useful when 2652 making Fcb's go away (which may tear up the tree). 2653 2654 Arguments: 2655 2656 Fcb - Supplies the current Fcb 2657 2658 TerminationFcb - The Fcb at which the enumeration should (non-inclusivly) 2659 stop. Assumed to be a directory. 2660 2661 Return Value: 2662 2663 The next Fcb in the enumeration, or NULL if Fcb was the final one. 2664 2665 --*/ 2666 2667 { 2668 PFCB Sibling; 2669 2670 PAGED_CODE(); 2671 UNREFERENCED_PARAMETER( IrpContext ); 2672 2673 NT_ASSERT( FatVcbAcquiredExclusive( IrpContext, Fcb->Vcb ) || 2674 FlagOn( Fcb->Vcb->VcbState, VCB_STATE_FLAG_LOCKED ) ); 2675 2676 // 2677 // If this was a directory (ie. not a file), get the child. If 2678 // there aren't any children and this is our termination Fcb, 2679 // return NULL. 2680 // 2681 2682 if ( ((NodeType(Fcb) == FAT_NTC_DCB) || 2683 (NodeType(Fcb) == FAT_NTC_ROOT_DCB)) && 2684 !IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue) ) { 2685 2686 return FatGetFirstChild( Fcb ); 2687 } 2688 2689 // 2690 // Were we only meant to do one iteration? 2691 // 2692 2693 if ( Fcb == TerminationFcb ) { 2694 2695 return NULL; 2696 } 2697 2698 Sibling = FatGetNextSibling(Fcb); 2699 2700 while (TRUE) { 2701 2702 // 2703 // Do we still have an "older" sibling in this directory who is 2704 // not the termination Fcb? 2705 // 2706 2707 if ( Sibling != NULL ) { 2708 2709 return (Sibling != TerminationFcb) ? Sibling : NULL; 2710 } 2711 2712 // 2713 // OK, let's move on to out parent and see if he is the termination 2714 // node or has any older siblings. 2715 // 2716 2717 if ( Fcb->ParentDcb == TerminationFcb ) { 2718 2719 return NULL; 2720 } 2721 2722 Fcb = Fcb->ParentDcb; 2723 2724 Sibling = FatGetNextSibling(Fcb); 2725 } 2726 } 2727 2728 2729 BOOLEAN 2730 FatSwapVpb ( 2731 IN PIRP_CONTEXT IrpContext, 2732 PVCB Vcb 2733 ) 2734 2735 /*++ 2736 2737 Routine Description: 2738 2739 This routine swaps the VPB for this VCB if it has not been done already. 2740 This means the device object will get our spare VPB and we will cleanup 2741 the one used while the volume was mounted. 2742 2743 Arguments: 2744 2745 IrpContext - Supplies the context for the overall request. 2746 2747 Vcb - Supplies the VCB to swap the VPB on. 2748 2749 Return Value: 2750 2751 TRUE - If the VPB was actually swapped. 2752 2753 FALSE - If the VPB was already swapped. 2754 2755 --*/ 2756 2757 { 2758 BOOLEAN Result = FALSE; 2759 PVPB OldVpb; 2760 PIO_STACK_LOCATION IrpSp; 2761 2762 2763 // 2764 // Make sure we have not already swapped it. 2765 // 2766 2767 OldVpb = Vcb->Vpb; 2768 2769 #ifdef _MSC_VER 2770 #pragma prefast( push ) 2771 #pragma prefast( disable: 28175, "touching Vpb is ok for a filesystem" ) 2772 #endif 2773 2774 if (!FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VPB_MUST_BE_FREED ) && OldVpb->RealDevice->Vpb == OldVpb) { 2775 2776 // 2777 // If not the final reference and we are forcing the disconnect, 2778 // then swap out the Vpb. We must preserve the REMOVE_PENDING flag 2779 // so that the device is not remounted in the middle of a PnP remove 2780 // operation. 2781 // 2782 2783 NT_ASSERT( Vcb->SwapVpb != NULL ); 2784 2785 Vcb->SwapVpb->Type = IO_TYPE_VPB; 2786 Vcb->SwapVpb->Size = sizeof( VPB ); 2787 Vcb->SwapVpb->RealDevice = OldVpb->RealDevice; 2788 2789 Vcb->SwapVpb->RealDevice->Vpb = Vcb->SwapVpb; 2790 2791 Vcb->SwapVpb->Flags = FlagOn( OldVpb->Flags, VPB_REMOVE_PENDING ); 2792 2793 // 2794 // If we are working on a mount request, we need to make sure we update 2795 // the VPB in the IRP, since the one it points to may no longer be valid. 2796 // 2797 2798 if (IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL && IrpContext->MinorFunction == IRP_MN_MOUNT_VOLUME) { 2799 2800 // 2801 // Get the IRP stack. 2802 // 2803 2804 IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp ); 2805 2806 NT_ASSERT( IrpSp->FileObject == NULL ); 2807 2808 // 2809 // Check the VPB in the IRP to see if it is the one we are swapping. 2810 // 2811 2812 if (IrpSp->Parameters.MountVolume.Vpb == OldVpb) { 2813 2814 // 2815 // Change the IRP to point to the swap VPB. 2816 // 2817 2818 IrpSp->Parameters.MountVolume.Vpb = Vcb->SwapVpb; 2819 } 2820 } 2821 2822 #ifdef _MSC_VER 2823 #pragma prefast( pop ) 2824 #endif 2825 2826 // 2827 // We place the volume in the Bad state (as opposed to NotMounted) so 2828 // that it is not eligible for a remount. Also indicate we used up 2829 // the swap. 2830 // 2831 2832 Vcb->SwapVpb = NULL; 2833 FatSetVcbCondition( Vcb, VcbBad); 2834 SetFlag( Vcb->VcbState, VCB_STATE_FLAG_VPB_MUST_BE_FREED ); 2835 2836 Result = TRUE; 2837 } 2838 2839 return Result; 2840 } 2841 2842 2843 _Requires_lock_held_(_Global_critical_region_) 2844 BOOLEAN 2845 FatCheckForDismount ( 2846 IN PIRP_CONTEXT IrpContext, 2847 PVCB Vcb, 2848 IN BOOLEAN Force 2849 ) 2850 2851 /*++ 2852 2853 Routine Description: 2854 2855 This routine determines if a volume is ready for deletion. It 2856 correctly synchronizes with creates en-route to the file system. 2857 2858 Arguments: 2859 2860 Vcb - Supplies the volume to examine 2861 2862 Force - Specifies whether we want this Vcb forcibly disconnected 2863 from the driver stack if it will not be deleted (a new vpb will 2864 be installed if neccesary). Caller is responsible for making 2865 sure that the volume has been marked in such a way that attempts 2866 to operate through the realdevice are blocked (i.e., move the vcb 2867 out of the mounted state). 2868 2869 Return Value: 2870 2871 BOOLEAN - TRUE if the volume was deleted, FALSE otherwise. 2872 2873 --*/ 2874 2875 { 2876 KIRQL SavedIrql; 2877 BOOLEAN VcbDeleted = FALSE; 2878 2879 2880 // 2881 // If the VCB condition is good and we are not forcing, just return. 2882 // 2883 2884 if (Vcb->VcbCondition == VcbGood && !Force) { 2885 2886 return FALSE; 2887 } 2888 2889 // 2890 // Now check for a zero Vpb count on an unmounted volume. These 2891 // volumes will be deleted as they now have no file objects and 2892 // there are no creates en route to this volume. 2893 // 2894 2895 IoAcquireVpbSpinLock( &SavedIrql ); 2896 2897 if (Vcb->Vpb->ReferenceCount == Vcb->ResidualOpenCount && Vcb->OpenFileCount == 0) { 2898 2899 PVPB Vpb = Vcb->Vpb; 2900 2901 #if DBG 2902 UNICODE_STRING VolumeLabel; 2903 2904 // 2905 // Setup the VolumeLabel string 2906 // 2907 2908 VolumeLabel.Length = Vcb->Vpb->VolumeLabelLength; 2909 VolumeLabel.MaximumLength = MAXIMUM_VOLUME_LABEL_LENGTH; 2910 VolumeLabel.Buffer = &Vcb->Vpb->VolumeLabel[0]; 2911 2912 KdPrintEx((DPFLTR_FASTFAT_ID, 2913 DPFLTR_INFO_LEVEL, 2914 "FASTFAT: Dismounting Volume %Z\n", 2915 &VolumeLabel)); 2916 #endif // DBG 2917 2918 // 2919 // Swap this VCB's VPB. 2920 // 2921 2922 FatSwapVpb( IrpContext, 2923 Vcb ); 2924 2925 // 2926 // Clear the VPB_MOUNTED bit. New opens should not come in due 2927 // to the swapped VPB, but having the flag cleared helps debugging. 2928 // Note that we must leave the Vpb->DeviceObject field set until 2929 // after the FatTearDownVcb call as closes will have to make their 2930 // way back to us. 2931 // 2932 2933 ClearFlag( Vpb->Flags, VPB_MOUNTED ); 2934 2935 // 2936 // If this Vpb was locked, clear this flag now. 2937 // 2938 2939 ClearFlag( Vpb->Flags, (VPB_LOCKED | VPB_DIRECT_WRITES_ALLOWED) ); 2940 2941 IoReleaseVpbSpinLock( SavedIrql ); 2942 2943 // 2944 // We are going to attempt the dismount, so mark the VCB as having 2945 // a dismount in progress. 2946 // 2947 2948 NT_ASSERT( !FlagOn( Vcb->VcbState, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS ) ); 2949 SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS ); 2950 2951 // 2952 // Close down our internal opens. 2953 // 2954 2955 FatTearDownVcb( IrpContext, 2956 Vcb ); 2957 2958 // 2959 // Process any delayed closes. 2960 // 2961 2962 FatFspClose( Vcb ); 2963 2964 // 2965 // Grab the VPB lock again so that we can recheck our counts. 2966 // 2967 2968 IoAcquireVpbSpinLock( &SavedIrql ); 2969 2970 // 2971 // See if we can delete this VCB. 2972 // 2973 2974 if (Vcb->Vpb->ReferenceCount == 0 && Vcb->InternalOpenCount == 0) { 2975 2976 Vpb->DeviceObject = NULL; 2977 2978 IoReleaseVpbSpinLock( SavedIrql ); 2979 2980 FatDeleteVcb( IrpContext, Vcb ); 2981 2982 IoDeleteDevice( (PDEVICE_OBJECT) 2983 CONTAINING_RECORD( Vcb, 2984 VOLUME_DEVICE_OBJECT, 2985 Vcb ) ); 2986 2987 VcbDeleted = TRUE; 2988 2989 } else { 2990 2991 IoReleaseVpbSpinLock( SavedIrql ); 2992 2993 NT_ASSERT( FlagOn( Vcb->VcbState, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS ) ); 2994 ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS ); 2995 } 2996 2997 } else if (Force) { 2998 2999 // 3000 // The requester is forcing the issue. We need to swap the VPB with our spare. 3001 // 3002 3003 FatSwapVpb( IrpContext, 3004 Vcb ); 3005 3006 IoReleaseVpbSpinLock( SavedIrql ); 3007 3008 } else { 3009 3010 // 3011 // Just drop the Vpb spinlock. 3012 // 3013 3014 IoReleaseVpbSpinLock( SavedIrql ); 3015 } 3016 3017 return VcbDeleted; 3018 } 3019 3020 3021 VOID 3022 FatConstructNamesInFcb ( 3023 IN PIRP_CONTEXT IrpContext, 3024 PFCB Fcb, 3025 PDIRENT Dirent, 3026 PUNICODE_STRING Lfn OPTIONAL 3027 ) 3028 3029 /*++ 3030 3031 Routine Description: 3032 3033 This routine places the short name in the dirent in the first set of 3034 STRINGs in the Fcb. If a long file name (Lfn) was specified, then 3035 we must decide whether we will store its Oem equivolent in the same 3036 prefix table as the short name, or rather just save the upcased 3037 version of the UNICODE string in the FCB. 3038 3039 For looking up Fcbs, the first approach will be faster, so we want to 3040 do this as much as possible. Here are the rules that I have thought 3041 through extensively to determine when it is safe to store only Oem 3042 version of the UNICODE name. 3043 3044 - If the UNICODE name contains no extended characters (>0x80), use Oem. 3045 3046 - Let U be the upcased version of the UNICODE name. 3047 Let Up(x) be the function that upcases a UNICODE string. 3048 Let Down(x) be the function that upcases a UNICODE string. 3049 Let OemToUni(x) be the function that converts an Oem string to Unicode. 3050 Let UniToOem(x) be the function that converts a Unicode string to Oem. 3051 Let BestOemFit(x) be the function that creates the Best uppercase Oem 3052 fit for the UNICODE string x. 3053 3054 BestOemFit(x) = UniToOem(Up(OemToUni(UniToOem(x)))) <1> 3055 3056 if (BestOemFit(U) == BestOemFit(Down(U)) <2> 3057 3058 then I know that there exists no UNICODE string Y such that: 3059 3060 Up(Y) == Up(U) <3> 3061 3062 AND 3063 3064 BestOemFit(U) != BestOemFit(Y) <4> 3065 3066 Consider string U as a collection of one character strings. The 3067 conjecture is clearly true for each sub-string, thus it is true 3068 for the entire string. 3069 3070 Equation <1> is what we use to convert an incoming unicode name in 3071 FatCommonCreate() to Oem. The double conversion is done to provide 3072 better visual best fitting for characters in the Ansi code page but 3073 not in the Oem code page. A single Nls routine is provided to do 3074 this conversion efficiently. 3075 3076 The idea is that with U, I only have to worry about a case varient Y 3077 matching it in a unicode compare, and I have shown that any case varient 3078 of U (the set Y defined in equation <3>), when filtered through <1> 3079 (as in create), will match the Oem string defined in <1>. 3080 3081 Thus I do not have to worry about another UNICODE string missing in 3082 the prefix lookup, but matching when comparing LFNs in the directory. 3083 3084 Arguments: 3085 3086 Fcb - The Fcb we are supposed to fill in. Note that ParentDcb must 3087 already be filled in. 3088 3089 Dirent - The gives up the short name. 3090 3091 Lfn - If provided, this gives us the long name. 3092 3093 Return Value: 3094 3095 None 3096 3097 --*/ 3098 3099 { 3100 #ifndef __REACTOS__ 3101 NTSTATUS Status; 3102 #endif 3103 ULONG i; 3104 3105 #ifndef __REACTOS__ 3106 OEM_STRING OemA; 3107 OEM_STRING OemB; 3108 #endif 3109 UNICODE_STRING Unicode; 3110 POEM_STRING ShortName; 3111 POEM_STRING LongOemName; 3112 PUNICODE_STRING LongUniName; 3113 3114 PAGED_CODE(); 3115 3116 ShortName = &Fcb->ShortName.Name.Oem; 3117 3118 NT_ASSERT( ShortName->Buffer == NULL ); 3119 3120 _SEH2_TRY { 3121 3122 // 3123 // First do the short name. 3124 // 3125 3126 // 3127 // Copy over the case flags for the short name of the file 3128 // 3129 3130 if (FlagOn(Dirent->NtByte, FAT_DIRENT_NT_BYTE_8_LOWER_CASE)) { 3131 3132 SetFlag(Fcb->FcbState, FCB_STATE_8_LOWER_CASE); 3133 3134 } else { 3135 3136 ClearFlag(Fcb->FcbState, FCB_STATE_8_LOWER_CASE); 3137 } 3138 3139 if (FlagOn(Dirent->NtByte, FAT_DIRENT_NT_BYTE_3_LOWER_CASE)) { 3140 3141 SetFlag(Fcb->FcbState, FCB_STATE_3_LOWER_CASE); 3142 3143 } else { 3144 3145 ClearFlag(Fcb->FcbState, FCB_STATE_3_LOWER_CASE); 3146 } 3147 3148 ShortName->MaximumLength = 16; 3149 ShortName->Buffer = FsRtlAllocatePoolWithTag( PagedPool, 3150 16, 3151 TAG_FILENAME_BUFFER ); 3152 3153 Fat8dot3ToString( IrpContext, Dirent, FALSE, ShortName ); 3154 3155 Fcb->ShortName.FileNameDos = TRUE; 3156 3157 // 3158 // If no Lfn was specified, we are done. In either case, set the 3159 // final name length. 3160 // 3161 3162 NT_ASSERT( Fcb->ExactCaseLongName.Buffer == NULL ); 3163 3164 if (!ARGUMENT_PRESENT(Lfn) || (Lfn->Length == 0)) { 3165 3166 Fcb->FinalNameLength = (USHORT) RtlOemStringToCountedUnicodeSize( ShortName ); 3167 Fcb->ExactCaseLongName.Length = Fcb->ExactCaseLongName.MaximumLength = 0; 3168 3169 try_return( NOTHING ); 3170 } 3171 3172 // 3173 // If we already set up the full filename, we could be in trouble. If the fast 3174 // path for doing it already fired, FatSetFullFileNameInFcb, it will have missed 3175 // this and could have built the full filename out of the shortname of the file. 3176 // 3177 // At that point, disaster could be inevitable since the final name length will not 3178 // match. We use this to tell the notify package what to do - FatNotifyReportChange. 3179 // 3180 3181 NT_ASSERT( Fcb->FullFileName.Buffer == NULL ); 3182 3183 // 3184 // We know now we have an Lfn, save away a copy. 3185 // 3186 3187 Fcb->FinalNameLength = Lfn->Length; 3188 3189 Fcb->ExactCaseLongName.Length = Fcb->ExactCaseLongName.MaximumLength = Lfn->Length; 3190 Fcb->ExactCaseLongName.Buffer = FsRtlAllocatePoolWithTag( PagedPool, 3191 Lfn->Length, 3192 TAG_FILENAME_BUFFER ); 3193 RtlCopyMemory(Fcb->ExactCaseLongName.Buffer, Lfn->Buffer, Lfn->Length); 3194 3195 // 3196 // First check for no extended characters. 3197 // 3198 3199 for (i=0; i < Lfn->Length/sizeof(WCHAR); i++) { 3200 3201 if (Lfn->Buffer[i] >= 0x80) { 3202 3203 break; 3204 } 3205 } 3206 3207 if (i == Lfn->Length/sizeof(WCHAR)) { 3208 3209 // 3210 // Cool, I can go with the Oem, upcase it fast by hand. 3211 // 3212 3213 LongOemName = &Fcb->LongName.Oem.Name.Oem; 3214 3215 3216 LongOemName->Buffer = FsRtlAllocatePoolWithTag( PagedPool, 3217 Lfn->Length/sizeof(WCHAR), 3218 TAG_FILENAME_BUFFER ); 3219 LongOemName->Length = 3220 LongOemName->MaximumLength = Lfn->Length/sizeof(WCHAR); 3221 3222 for (i=0; i < Lfn->Length/sizeof(WCHAR); i++) { 3223 3224 WCHAR c; 3225 3226 c = Lfn->Buffer[i]; 3227 3228 #ifdef _MSC_VER 3229 #pragma warning( push ) 3230 #pragma warning( disable:4244 ) 3231 #endif 3232 LongOemName->Buffer[i] = c < 'a' ? 3233 (UCHAR)c : 3234 c <= 'z' ? 3235 c - (UCHAR)('a'-'A') : 3236 (UCHAR)c; 3237 #ifdef _MSC_VER 3238 #pragma warning( pop ) 3239 #endif 3240 } 3241 3242 // 3243 // If this name happens to be exactly the same as the short 3244 // name, don't add it to the splay table. 3245 // 3246 3247 if (FatAreNamesEqual(IrpContext, *ShortName, *LongOemName) || 3248 (FatFindFcb( IrpContext, 3249 &Fcb->ParentDcb->Specific.Dcb.RootOemNode, 3250 LongOemName, 3251 NULL) != NULL)) { 3252 3253 ExFreePool( LongOemName->Buffer ); 3254 3255 LongOemName->Buffer = NULL; 3256 LongOemName->Length = 3257 LongOemName->MaximumLength = 0; 3258 3259 } else { 3260 3261 SetFlag( Fcb->FcbState, FCB_STATE_HAS_OEM_LONG_NAME ); 3262 } 3263 3264 try_return( NOTHING ); 3265 } 3266 3267 // 3268 // Now we have the fun part. Make a copy of the Lfn. 3269 // 3270 3271 #ifndef __REACTOS__ 3272 OemA.Buffer = NULL; 3273 OemB.Buffer = NULL; 3274 #endif 3275 Unicode.Buffer = NULL; 3276 3277 Unicode.Length = 3278 Unicode.MaximumLength = Lfn->Length; 3279 Unicode.Buffer = FsRtlAllocatePoolWithTag( PagedPool, 3280 Lfn->Length, 3281 TAG_FILENAME_BUFFER ); 3282 3283 RtlCopyMemory( Unicode.Buffer, Lfn->Buffer, Lfn->Length ); 3284 3285 #ifndef __REACTOS__ 3286 Status = STATUS_SUCCESS; 3287 #endif 3288 3289 #if TRUE 3290 // 3291 // Unfortunately, this next block of code breaks down when you have 3292 // two long Unicode filenames that both map to the same Oem (and are, 3293 // well, long, i.e. are not the short names). In this case, with one 3294 // in the prefix table first, the other will hit the common Oem 3295 // representation. This leads to several forms of user astonishment. 3296 // 3297 // It isn't worth it, or probably even possible, to try to figure out 3298 // when this is really safe to go through. Simply omit the attempt. 3299 // 3300 // Ex: ANSI 0x82 and 0x84 in the 1252 ANSI->UNI and 437 UNI->OEM codepages. 3301 // 3302 // 0x82 => 0x201a => 0x2c 3303 // 0x84 => 0x201e => 0x2c 3304 // 3305 // 0x2c is comma, so is FAT Oem illegal and forces shortname generation. 3306 // Since it is otherwise well-formed by the rules articulated previously, 3307 // we would have put 0x2c in the Oem prefix tree. In terms of the 3308 // argument given above, even though there exist no Y and U s.t. 3309 // 3310 // Up(Y) == Up(U) && BestOemFit(U) != BestOemFit(Y) 3311 // 3312 // there most certainly exist Y and U s.t. 3313 // 3314 // Up(Y) != Up(U) && BestOemFit(U) == BestOemFit(Y) 3315 // 3316 // and that is enough to keep us from doing this. Note that the < 0x80 3317 // case is OK since we know that the mapping in the OEM codepages are 3318 // the identity in that range. 3319 // 3320 // We still need to monocase it, though. Do this through a full down/up 3321 // transition. 3322 // 3323 3324 (VOID)RtlDowncaseUnicodeString( &Unicode, &Unicode, FALSE ); 3325 (VOID)RtlUpcaseUnicodeString( &Unicode, &Unicode, FALSE ); 3326 #else 3327 // 3328 // Downcase and convert to upcased Oem. Only continue if we can 3329 // convert without error. Any error other than UNMAPPABLE_CHAR 3330 // is a fatal error and we raise. 3331 // 3332 // Note that even if the conversion fails, we must leave Unicode 3333 // in an upcased state. 3334 // 3335 // NB: The Rtl doesn't NULL .Buffer on error. 3336 // 3337 3338 (VOID)RtlDowncaseUnicodeString( &Unicode, &Unicode, FALSE ); 3339 Status = RtlUpcaseUnicodeStringToCountedOemString( &OemA, &Unicode, TRUE ); 3340 (VOID)RtlUpcaseUnicodeString( &Unicode, &Unicode, FALSE ); 3341 3342 if (!NT_SUCCESS(Status)) { 3343 3344 if (Status != STATUS_UNMAPPABLE_CHARACTER) { 3345 3346 NT_ASSERT( Status == STATUS_NO_MEMORY ); 3347 ExFreePool(Unicode.Buffer); 3348 FatNormalizeAndRaiseStatus( IrpContext, Status ); 3349 } 3350 3351 } else { 3352 3353 // 3354 // The same as above except upcase. 3355 // 3356 3357 Status = RtlUpcaseUnicodeStringToCountedOemString( &OemB, &Unicode, TRUE ); 3358 3359 if (!NT_SUCCESS(Status)) { 3360 3361 RtlFreeOemString( &OemA ); 3362 3363 if (Status != STATUS_UNMAPPABLE_CHARACTER) { 3364 3365 NT_ASSERT( Status == STATUS_NO_MEMORY ); 3366 ExFreePool(Unicode.Buffer); 3367 FatNormalizeAndRaiseStatus( IrpContext, Status ); 3368 } 3369 } 3370 } 3371 3372 // 3373 // If the final OemNames are equal, I can use save only the Oem 3374 // name. If the name did not map, then I have to go with the UNICODE 3375 // name because I could get a case varient that didn't convert 3376 // in create, but did match the LFN. 3377 // 3378 3379 if (NT_SUCCESS(Status) && FatAreNamesEqual( IrpContext, OemA, OemB )) { 3380 3381 // 3382 // Cool, I can go with the Oem. If we didn't convert correctly, 3383 // get a fresh convert from the original LFN. 3384 // 3385 3386 ExFreePool(Unicode.Buffer); 3387 3388 RtlFreeOemString( &OemB ); 3389 3390 Fcb->LongName.Oem.Name.Oem = OemA; 3391 3392 // 3393 // If this name happens to be exactly the same as the short 3394 // name, or a similar short name already exists don't add it 3395 // to the splay table (note the final condition implies a 3396 // corrupt disk. 3397 // 3398 3399 if (FatAreNamesEqual(IrpContext, *ShortName, OemA) || 3400 (FatFindFcb( IrpContext, 3401 &Fcb->ParentDcb->Specific.Dcb.RootOemNode, 3402 &OemA, 3403 NULL) != NULL)) { 3404 3405 RtlFreeOemString( &OemA ); 3406 3407 } else { 3408 3409 SetFlag( Fcb->FcbState, FCB_STATE_HAS_OEM_LONG_NAME ); 3410 } 3411 3412 try_return( NOTHING ); 3413 } 3414 3415 // 3416 // The long name must be left in UNICODE. Free the two Oem strings 3417 // if we got here just because they weren't equal. 3418 // 3419 3420 if (NT_SUCCESS(Status)) { 3421 3422 RtlFreeOemString( &OemA ); 3423 RtlFreeOemString( &OemB ); 3424 } 3425 #endif 3426 3427 LongUniName = &Fcb->LongName.Unicode.Name.Unicode; 3428 3429 LongUniName->Length = 3430 LongUniName->MaximumLength = Unicode.Length; 3431 LongUniName->Buffer = Unicode.Buffer; 3432 3433 SetFlag(Fcb->FcbState, FCB_STATE_HAS_UNICODE_LONG_NAME); 3434 3435 try_exit: NOTHING; 3436 } _SEH2_FINALLY { 3437 3438 if (_SEH2_AbnormalTermination()) { 3439 3440 if (ShortName->Buffer != NULL) { 3441 3442 ExFreePool( ShortName->Buffer ); 3443 ShortName->Buffer = NULL; 3444 } 3445 3446 } else { 3447 3448 // 3449 // Creating all the names worked, so add all the names 3450 // to the splay tree. 3451 // 3452 3453 FatInsertName( IrpContext, 3454 &Fcb->ParentDcb->Specific.Dcb.RootOemNode, 3455 &Fcb->ShortName ); 3456 3457 Fcb->ShortName.Fcb = Fcb; 3458 3459 if (FlagOn(Fcb->FcbState, FCB_STATE_HAS_OEM_LONG_NAME)) { 3460 3461 FatInsertName( IrpContext, 3462 &Fcb->ParentDcb->Specific.Dcb.RootOemNode, 3463 &Fcb->LongName.Oem ); 3464 3465 Fcb->LongName.Oem.Fcb = Fcb; 3466 } 3467 3468 if (FlagOn(Fcb->FcbState, FCB_STATE_HAS_UNICODE_LONG_NAME)) { 3469 3470 FatInsertName( IrpContext, 3471 &Fcb->ParentDcb->Specific.Dcb.RootUnicodeNode, 3472 &Fcb->LongName.Unicode ); 3473 3474 Fcb->LongName.Unicode.Fcb = Fcb; 3475 } 3476 3477 SetFlag(Fcb->FcbState, FCB_STATE_NAMES_IN_SPLAY_TREE); 3478 } 3479 } _SEH2_END; 3480 3481 return; 3482 } 3483 3484 3485 _Requires_lock_held_(_Global_critical_region_) 3486 VOID 3487 FatCheckFreeDirentBitmap ( 3488 IN PIRP_CONTEXT IrpContext, 3489 IN PDCB Dcb 3490 ) 3491 3492 /*++ 3493 3494 Routine Description: 3495 3496 This routine checks if the size of the free dirent bitmap is 3497 sufficient to for the current directory size. It is called 3498 whenever we grow a directory. 3499 3500 Arguments: 3501 3502 Dcb - Supplies the directory in question. 3503 3504 Return Value: 3505 3506 None 3507 3508 --*/ 3509 3510 { 3511 ULONG OldNumberOfDirents; 3512 ULONG NewNumberOfDirents; 3513 3514 PAGED_CODE(); 3515 UNREFERENCED_PARAMETER( IrpContext ); 3516 3517 // 3518 // Setup the Bitmap buffer if it is not big enough already 3519 // 3520 3521 NT_ASSERT( Dcb->Header.AllocationSize.QuadPart != FCB_LOOKUP_ALLOCATIONSIZE_HINT ); 3522 3523 OldNumberOfDirents = Dcb->Specific.Dcb.FreeDirentBitmap.SizeOfBitMap; 3524 NewNumberOfDirents = Dcb->Header.AllocationSize.LowPart / sizeof(DIRENT); 3525 3526 // 3527 // Do the usual unsync/sync check. 3528 // 3529 3530 if (NewNumberOfDirents > OldNumberOfDirents) { 3531 3532 FatAcquireDirectoryFileMutex( Dcb->Vcb ); 3533 3534 _SEH2_TRY { 3535 3536 PULONG OldBitmapBuffer; 3537 PULONG BitmapBuffer; 3538 3539 ULONG BytesInBitmapBuffer; 3540 ULONG BytesInOldBitmapBuffer; 3541 3542 OldNumberOfDirents = Dcb->Specific.Dcb.FreeDirentBitmap.SizeOfBitMap; 3543 NewNumberOfDirents = Dcb->Header.AllocationSize.LowPart / sizeof(DIRENT); 3544 3545 if (NewNumberOfDirents > OldNumberOfDirents) { 3546 3547 // 3548 // Remember the old bitmap 3549 // 3550 3551 OldBitmapBuffer = Dcb->Specific.Dcb.FreeDirentBitmap.Buffer; 3552 3553 // 3554 // Now make a new bitmap bufffer 3555 // 3556 3557 BytesInBitmapBuffer = NewNumberOfDirents / 8; 3558 3559 BytesInOldBitmapBuffer = OldNumberOfDirents / 8; 3560 3561 if (DCB_UNION_SLACK_SPACE >= BytesInBitmapBuffer) { 3562 3563 BitmapBuffer = &Dcb->Specific.Dcb.FreeDirentBitmapBuffer[0]; 3564 3565 } else { 3566 3567 BitmapBuffer = FsRtlAllocatePoolWithTag( PagedPool, 3568 BytesInBitmapBuffer, 3569 TAG_DIRENT_BITMAP ); 3570 } 3571 3572 // 3573 // Copy the old buffer to the new buffer, free the old one, and zero 3574 // the rest of the new one. Only do the first two steps though if 3575 // we moved out of the initial buffer. 3576 // 3577 3578 if ((OldNumberOfDirents != 0) && 3579 (BitmapBuffer != &Dcb->Specific.Dcb.FreeDirentBitmapBuffer[0])) { 3580 3581 RtlCopyMemory( BitmapBuffer, 3582 OldBitmapBuffer, 3583 BytesInOldBitmapBuffer ); 3584 3585 if (OldBitmapBuffer != &Dcb->Specific.Dcb.FreeDirentBitmapBuffer[0]) { 3586 3587 ExFreePool( OldBitmapBuffer ); 3588 } 3589 } 3590 3591 NT_ASSERT( BytesInBitmapBuffer > BytesInOldBitmapBuffer ); 3592 3593 RtlZeroMemory( (PUCHAR)BitmapBuffer + BytesInOldBitmapBuffer, 3594 BytesInBitmapBuffer - BytesInOldBitmapBuffer ); 3595 3596 // 3597 // Now initialize the new bitmap. 3598 // 3599 3600 RtlInitializeBitMap( &Dcb->Specific.Dcb.FreeDirentBitmap, 3601 BitmapBuffer, 3602 NewNumberOfDirents ); 3603 } 3604 3605 } _SEH2_FINALLY { 3606 3607 FatReleaseDirectoryFileMutex( Dcb->Vcb ); 3608 } _SEH2_END; 3609 } 3610 } 3611 3612 3613 BOOLEAN 3614 FatIsHandleCountZero ( 3615 IN PIRP_CONTEXT IrpContext, 3616 IN PVCB Vcb 3617 ) 3618 3619 /*++ 3620 3621 Routine Description: 3622 3623 This routine decides if the handle count on the volume is zero. 3624 3625 Arguments: 3626 3627 Vcb - The volume in question 3628 3629 Return Value: 3630 3631 BOOLEAN - TRUE if there are no open handles on the volume, FALSE 3632 otherwise. 3633 3634 --*/ 3635 3636 { 3637 PFCB Fcb; 3638 3639 PAGED_CODE(); 3640 3641 Fcb = Vcb->RootDcb; 3642 3643 while (Fcb != NULL) { 3644 3645 if (Fcb->UncleanCount != 0) { 3646 3647 return FALSE; 3648 } 3649 3650 Fcb = FatGetNextFcbTopDown(IrpContext, Fcb, Vcb->RootDcb); 3651 } 3652 3653 return TRUE; 3654 } 3655 3656 3657 PCLOSE_CONTEXT 3658 3659 FatAllocateCloseContext( 3660 OPTIONAL PVCB Vcb 3661 ) 3662 /*++ 3663 3664 Routine Description: 3665 3666 This routine preallocates a close context, presumeably on behalf 3667 of a fileobject which does not have a structure we can embed one 3668 in. 3669 3670 Arguments: 3671 3672 None. 3673 3674 Return Value: 3675 3676 None. 3677 3678 --*/ 3679 { 3680 PAGED_CODE(); 3681 UNREFERENCED_PARAMETER( Vcb ); 3682 3683 #if DBG 3684 if (ARGUMENT_PRESENT(Vcb)) { 3685 3686 NT_ASSERT( 0 != Vcb->CloseContextCount); 3687 InterlockedDecrement( (LONG*)&Vcb->CloseContextCount); 3688 } 3689 #endif 3690 return (PCLOSE_CONTEXT)ExInterlockedPopEntrySList( &FatCloseContextSList, 3691 &FatData.GeneralSpinLock ); 3692 } 3693 3694 3695 VOID 3696 FatPreallocateCloseContext ( 3697 PVCB Vcb 3698 ) 3699 3700 /*++ 3701 3702 Routine Description: 3703 3704 This routine preallocates a close context, presumeably on behalf 3705 of a fileobject which does not have a structure we can embed one 3706 in. 3707 3708 Arguments: 3709 3710 None. 3711 3712 Return Value: 3713 3714 None. 3715 3716 --*/ 3717 3718 { 3719 PCLOSE_CONTEXT CloseContext; 3720 3721 PAGED_CODE(); 3722 3723 UNREFERENCED_PARAMETER( Vcb ); 3724 3725 CloseContext = FsRtlAllocatePoolWithTag( PagedPool, 3726 sizeof(CLOSE_CONTEXT), 3727 TAG_FAT_CLOSE_CONTEXT ); 3728 3729 ExInterlockedPushEntrySList( &FatCloseContextSList, 3730 (PSLIST_ENTRY) CloseContext, 3731 &FatData.GeneralSpinLock ); 3732 3733 DbgDoit( InterlockedIncrement( (LONG*)&Vcb->CloseContextCount)); 3734 } 3735 3736 3737 3738 VOID 3739 FatEnsureStringBufferEnough ( 3740 _Inout_ PVOID String, 3741 _In_ USHORT DesiredBufferSize 3742 ) 3743 3744 /*++ 3745 3746 Routine Description: 3747 3748 Ensures that a string string (STRING, UNICODE_STRING, ANSI_STRING, OEM_STRING) 3749 has a buffer >= DesiredBufferSize, allocating from pool if neccessary. Any 3750 existing pool buffer will be freed if a new one is allocated. 3751 3752 NOTE: No copy of old buffer contents is performed on reallocation. 3753 3754 Will raise on allocation failure. 3755 3756 Arguments: 3757 3758 String - pointer to string structure 3759 3760 DesiredBufferSize - (bytes) minimum required buffer size 3761 3762 --*/ 3763 3764 { 3765 PSTRING LocalString = String; 3766 3767 PAGED_CODE(); 3768 3769 if (LocalString->MaximumLength < DesiredBufferSize) { 3770 3771 FatFreeStringBuffer( LocalString); 3772 3773 LocalString->Buffer = FsRtlAllocatePoolWithTag( PagedPool, 3774 DesiredBufferSize, 3775 TAG_DYNAMIC_NAME_BUFFER); 3776 NT_ASSERT( LocalString->Buffer); 3777 3778 LocalString->MaximumLength = DesiredBufferSize; 3779 } 3780 } 3781 3782 3783 VOID 3784 FatFreeStringBuffer ( 3785 _Inout_ PVOID String 3786 ) 3787 3788 /*++ 3789 3790 Routine Description: 3791 3792 Frees the buffer of an string (STRING, UNICODE_STRING, ANSI_STRING, OEM_STRING) 3793 structure if it is not within the current thread's stack limits. 3794 3795 Regardless of action performed, on exit String->Buffer will be set to NULL and 3796 String->MaximumLength to zero. 3797 3798 Arguments: 3799 3800 String - pointer to string structure 3801 3802 --*/ 3803 3804 { 3805 ULONG_PTR High, Low; 3806 PSTRING LocalString = String; 3807 3808 PAGED_CODE(); 3809 3810 if (NULL != LocalString->Buffer) { 3811 3812 IoGetStackLimits( &Low, &High ); 3813 3814 if (((ULONG_PTR)(LocalString->Buffer) < Low) || 3815 ((ULONG_PTR)(LocalString->Buffer) > High)) { 3816 3817 ExFreePool( LocalString->Buffer); 3818 } 3819 3820 LocalString->Buffer = NULL; 3821 } 3822 3823 LocalString->MaximumLength = LocalString->Length = 0; 3824 } 3825 3826 3827 BOOLEAN 3828 FatScanForDataTrack( 3829 IN PIRP_CONTEXT IrpContext, 3830 IN PDEVICE_OBJECT TargetDeviceObject 3831 ) 3832 3833 /*++ 3834 3835 Routine Description: 3836 3837 This routine is called to verify and process the TOC for this disk. 3838 3839 FAT queries for the TOC to avoid trying to mount on CD-DA/CD-E media, Doing data reads on 3840 audio/leadin of that media sends a lot of drives into what could charitably be called 3841 "conniptions" which take a couple seconds to clear and would also convince FAT that the 3842 device was busted, and fail the mount (not letting CDFS get its crack). 3843 3844 There is special handling of PD media. These things fail the TOC read, but return 3845 a special error code so FAT knows to continue to try the mount anyway. 3846 3847 Arguments: 3848 3849 TargetDeviceObject - Device object to send TOC request to. 3850 3851 Return Value: 3852 3853 BOOLEAN - TRUE if we found a TOC with a single data track. 3854 3855 --*/ 3856 3857 { 3858 NTSTATUS Status; 3859 IO_STATUS_BLOCK Iosb; 3860 3861 ULONG LocalTrackCount; 3862 ULONG LocalTocLength; 3863 3864 PCDROM_TOC CdromToc; 3865 BOOLEAN Result = FALSE; 3866 3867 PAGED_CODE(); 3868 3869 CdromToc = FsRtlAllocatePoolWithTag( PagedPool, 3870 sizeof( CDROM_TOC ), 3871 TAG_IO_BUFFER ); 3872 3873 RtlZeroMemory( CdromToc, sizeof( CDROM_TOC )); 3874 3875 _SEH2_TRY { 3876 3877 // 3878 // Go ahead and read the table of contents 3879 // 3880 3881 Status = FatPerformDevIoCtrl( IrpContext, 3882 IOCTL_CDROM_READ_TOC, 3883 TargetDeviceObject, 3884 NULL, 3885 0, 3886 CdromToc, 3887 sizeof( CDROM_TOC ), 3888 FALSE, 3889 TRUE, 3890 &Iosb ); 3891 3892 // 3893 // Nothing to process if this request fails. 3894 // 3895 3896 if (Status != STATUS_SUCCESS) { 3897 3898 // 3899 // If we get the special error indicating a failed TOC read on PD media just 3900 // plow ahead with the mount (see comments above). 3901 // 3902 3903 if ((Status == STATUS_IO_DEVICE_ERROR) || (Status == STATUS_INVALID_DEVICE_REQUEST)) { 3904 3905 Result = TRUE; 3906 3907 } 3908 3909 try_leave( NOTHING ); 3910 } 3911 3912 // 3913 // Get the number of tracks and stated size of this structure. 3914 // 3915 3916 LocalTrackCount = CdromToc->LastTrack - CdromToc->FirstTrack + 1; 3917 LocalTocLength = PtrOffset( CdromToc, &CdromToc->TrackData[LocalTrackCount + 1] ); 3918 3919 // 3920 // Get out if there is an immediate problem with the TOC, or more than 3921 // one track. 3922 // 3923 3924 if ((LocalTocLength > Iosb.Information) || 3925 (CdromToc->FirstTrack > CdromToc->LastTrack) || 3926 (LocalTrackCount != 1)) { 3927 3928 try_leave( NOTHING); 3929 } 3930 3931 // 3932 // Is it a data track? DVD-RAM reports single, data, track. 3933 // 3934 3935 Result = BooleanFlagOn( CdromToc->TrackData[ 0].Control, 0x04 ); 3936 } 3937 _SEH2_FINALLY { 3938 3939 ExFreePool( CdromToc); 3940 } _SEH2_END; 3941 3942 return Result; 3943 } 3944 3945 3946