1 /*++ 2 3 Copyright (c) 1990-2000 Microsoft Corporation 4 5 Module Name: 6 7 EaSup.c 8 9 Abstract: 10 11 This module implements the cluster operations on the EA file for Fat. 12 13 14 --*/ 15 16 #include "fatprocs.h" 17 18 // 19 // Local debug trace level 20 // 21 22 #define Dbg (DEBUG_TRACE_EA) 23 24 #ifdef ALLOC_PRAGMA 25 #pragma alloc_text(PAGE, FatAddEaSet) 26 #pragma alloc_text(PAGE, FatAppendPackedEa) 27 #pragma alloc_text(PAGE, FatCreateEa) 28 #pragma alloc_text(PAGE, FatDeleteEa) 29 #pragma alloc_text(PAGE, FatDeleteEaSet) 30 #pragma alloc_text(PAGE, FatDeletePackedEa) 31 #pragma alloc_text(PAGE, FatGetEaFile) 32 #pragma alloc_text(PAGE, FatGetEaLength) 33 #pragma alloc_text(PAGE, FatGetNeedEaCount) 34 #pragma alloc_text(PAGE, FatIsEaNameValid) 35 #pragma alloc_text(PAGE, FatLocateEaByName) 36 #pragma alloc_text(PAGE, FatLocateNextEa) 37 #pragma alloc_text(PAGE, FatReadEaSet) 38 #pragma alloc_text(PAGE, FatPinEaRange) 39 #pragma alloc_text(PAGE, FatMarkEaRangeDirty) 40 #pragma alloc_text(PAGE, FatUnpinEaRange) 41 #endif 42 43 44 // 45 // Any access to the Ea file must recognize when a section boundary is being 46 // crossed. 47 // 48 49 #define EA_SECTION_SIZE (0x00040000) 50 51 52 _Requires_lock_held_(_Global_critical_region_) 53 VOID 54 FatGetEaLength ( 55 IN PIRP_CONTEXT IrpContext, 56 IN PVCB Vcb, 57 IN PDIRENT Dirent, 58 OUT PULONG EaLength 59 ) 60 61 /*++ 62 63 Routine Description: 64 65 This routine looks up the Ea length for the Eas of the file. This 66 length is the length of the packed eas, including the 4 bytes which 67 contain the Ea length. 68 69 This routine pins down the Ea set for the desired file and copies 70 this field from the Ea set header. 71 72 Arguments: 73 74 Vcb - Vcb for the volume containing the Eas. 75 76 Dirent - Supplies a pointer to the dirent for the file in question. 77 78 EaLength - Supplies the address to store the length of the Eas. 79 80 Return Value: 81 82 None 83 84 --*/ 85 86 { 87 PBCB EaBcb = NULL; 88 BOOLEAN LockedEaFcb = FALSE; 89 EA_RANGE EaSetRange; 90 91 PAGED_CODE(); 92 93 DebugTrace(+1, Dbg, "FatGetEaLength ...\n", 0); 94 95 // 96 // If this is Fat32 volume, or if the handle is 0 then the Ea length is 0. 97 // 98 99 if (FatIsFat32( Vcb ) || 100 Dirent->ExtendedAttributes == 0) { 101 102 *EaLength = 0; 103 DebugTrace(-1, Dbg, "FatGetEaLength -> %08lx\n", TRUE); 104 return; 105 } 106 107 RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE )); 108 109 // 110 // Use a try to facilitate cleanup. 111 // 112 113 _SEH2_TRY { 114 115 PDIRENT EaDirent; 116 OEM_STRING ThisFilename; 117 UCHAR Buffer[12]; 118 PEA_SET_HEADER EaSetHeader; 119 120 // 121 // Initial the local values. 122 // 123 124 EaBcb = NULL; 125 LockedEaFcb = FALSE; 126 127 // 128 // Try to get the Ea file object. Return FALSE on failure. 129 // 130 131 FatGetEaFile( IrpContext, 132 Vcb, 133 &EaDirent, 134 &EaBcb, 135 FALSE, 136 FALSE ); 137 138 LockedEaFcb = TRUE; 139 140 // 141 // If we didn't get the file because it doesn't exist, then the 142 // disk is corrupted. 143 // 144 145 if (Vcb->VirtualEaFile == NULL) { 146 147 DebugTrace(0, Dbg, "FatGetEaLength: Ea file doesn't exist\n", 0); 148 FatRaiseStatus( IrpContext, STATUS_NO_EAS_ON_FILE ); 149 } 150 151 // 152 // Try to pin down the Ea set header for the index in the 153 // dirent. If the operation doesn't complete, return FALSE 154 // from this routine. 155 // 156 157 ThisFilename.Buffer = (PCHAR)Buffer; 158 Fat8dot3ToString( IrpContext, Dirent, FALSE, &ThisFilename ); 159 160 FatReadEaSet( IrpContext, 161 Vcb, 162 Dirent->ExtendedAttributes, 163 &ThisFilename, 164 FALSE, 165 &EaSetRange ); 166 167 EaSetHeader = (PEA_SET_HEADER) EaSetRange.Data; 168 169 // 170 // We now have the Ea set header for this file. We simply copy 171 // the Ea length field. 172 // 173 174 CopyUchar4( EaLength, EaSetHeader->cbList ); 175 DebugTrace(0, Dbg, "FatGetEaLength: Length of Ea is -> %08lx\n", 176 *EaLength); 177 178 } _SEH2_FINALLY { 179 180 DebugUnwind( FatGetEaLength ); 181 182 // 183 // Unpin the EaDirent and the EaSetHeader if pinned. 184 // 185 186 FatUnpinBcb( IrpContext, EaBcb ); 187 188 FatUnpinEaRange( IrpContext, &EaSetRange ); 189 190 // 191 // Release the Fcb for the Ea file if locked. 192 // 193 194 if (LockedEaFcb) { 195 196 FatReleaseFcb( IrpContext, Vcb->EaFcb ); 197 } 198 199 DebugTrace(-1, Dbg, "FatGetEaLength: Ea length -> %08lx\n", *EaLength); 200 } _SEH2_END; 201 202 return; 203 } 204 205 206 _Requires_lock_held_(_Global_critical_region_) 207 VOID 208 FatGetNeedEaCount ( 209 IN PIRP_CONTEXT IrpContext, 210 IN PVCB Vcb, 211 IN PDIRENT Dirent, 212 OUT PULONG NeedEaCount 213 ) 214 215 /*++ 216 217 Routine Description: 218 219 This routine looks up the Need Ea count for the file. The value is the 220 in the ea header for the file. 221 222 Arguments: 223 224 Vcb - Vcb for the volume containing the Eas. 225 226 Dirent - Supplies a pointer to the dirent for the file in question. 227 228 NeedEaCount - Supplies the address to store the Need Ea count. 229 230 Return Value: 231 232 None 233 234 --*/ 235 236 { 237 PBCB EaBcb = NULL; 238 BOOLEAN LockedEaFcb = FALSE; 239 EA_RANGE EaSetRange; 240 241 PAGED_CODE(); 242 243 DebugTrace(+1, Dbg, "FatGetNeedEaCount ...\n", 0); 244 245 // 246 // If the handle is 0 then the Need Ea count is 0. 247 // 248 249 if (Dirent->ExtendedAttributes == 0) { 250 251 *NeedEaCount = 0; 252 DebugTrace(-1, Dbg, "FatGetNeedEaCount -> %08lx\n", TRUE); 253 return; 254 } 255 256 RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE )); 257 258 // 259 // Use a try to facilitate cleanup. 260 // 261 262 _SEH2_TRY { 263 264 PDIRENT EaDirent; 265 OEM_STRING ThisFilename; 266 UCHAR Buffer[12]; 267 PEA_SET_HEADER EaSetHeader; 268 269 // 270 // Initial the local values. 271 // 272 273 EaBcb = NULL; 274 LockedEaFcb = FALSE; 275 276 // 277 // Try to get the Ea file object. Return FALSE on failure. 278 // 279 280 FatGetEaFile( IrpContext, 281 Vcb, 282 &EaDirent, 283 &EaBcb, 284 FALSE, 285 FALSE ); 286 287 LockedEaFcb = TRUE; 288 289 // 290 // If we didn't get the file because it doesn't exist, then the 291 // disk is corrupted. 292 // 293 294 if (Vcb->VirtualEaFile == NULL) { 295 296 DebugTrace(0, Dbg, "FatGetNeedEaCount: Ea file doesn't exist\n", 0); 297 FatRaiseStatus( IrpContext, STATUS_NO_EAS_ON_FILE ); 298 } 299 300 // 301 // Try to pin down the Ea set header for the index in the 302 // dirent. If the operation doesn't complete, return FALSE 303 // from this routine. 304 // 305 306 ThisFilename.Buffer = (PCHAR)Buffer; 307 Fat8dot3ToString( IrpContext, Dirent, FALSE, &ThisFilename ); 308 309 FatReadEaSet( IrpContext, 310 Vcb, 311 Dirent->ExtendedAttributes, 312 &ThisFilename, 313 FALSE, 314 &EaSetRange ); 315 316 EaSetHeader = (PEA_SET_HEADER) EaSetRange.Data; 317 318 // 319 // We now have the Ea set header for this file. We simply copy 320 // the Need Ea field. 321 // 322 323 *NeedEaCount = EaSetHeader->NeedEaCount; 324 325 } _SEH2_FINALLY { 326 327 DebugUnwind( FatGetNeedEaCount ); 328 329 // 330 // Unpin the EaDirent and the EaSetHeader if pinned. 331 // 332 333 FatUnpinBcb( IrpContext, EaBcb ); 334 335 FatUnpinEaRange( IrpContext, &EaSetRange ); 336 337 // 338 // Release the Fcb for the Ea file if locked. 339 // 340 341 if (LockedEaFcb) { 342 343 FatReleaseFcb( IrpContext, Vcb->EaFcb ); 344 } 345 346 DebugTrace(-1, Dbg, "FatGetNeedEaCount: NeedEaCount -> %08lx\n", *NeedEaCount); 347 } _SEH2_END; 348 349 return; 350 } 351 352 353 _Requires_lock_held_(_Global_critical_region_) 354 VOID 355 FatCreateEa ( 356 IN PIRP_CONTEXT IrpContext, 357 IN PVCB Vcb, 358 IN PUCHAR Buffer, 359 IN ULONG Length, 360 IN POEM_STRING FileName, 361 OUT PUSHORT EaHandle 362 ) 363 364 /*++ 365 366 Routine Description: 367 368 This routine adds an entire ea set to the Ea file. The owning file 369 is specified in 'FileName'. This is used to replace the Ea set attached 370 to an existing file during a supersede operation. 371 372 NOTE: This routine may block, it should not be called unless the 373 thread is waitable. 374 375 Arguments: 376 377 Vcb - Supplies the Vcb for the volume. 378 379 Buffer - Buffer with the Ea list to add. 380 381 Length - Length of the buffer. 382 383 FileName - The Ea's will be attached to this file. 384 385 EaHandle - The new ea handle will be assigned to this address. 386 387 Return Value: 388 389 None 390 391 --*/ 392 393 { 394 PBCB EaBcb; 395 BOOLEAN LockedEaFcb; 396 397 PEA_SET_HEADER EaSetHeader = NULL; 398 EA_RANGE EaSetRange; 399 400 PAGED_CODE(); 401 402 DebugTrace(+1, Dbg, "FatCreateEa...\n", 0); 403 404 EaBcb = NULL; 405 LockedEaFcb = FALSE; 406 407 RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE )); 408 409 // 410 // Use 'try' to facilitate cleanup. 411 // 412 413 _SEH2_TRY { 414 415 PDIRENT EaDirent; 416 417 ULONG PackedEasLength; 418 ULONG AllocationLength; 419 ULONG BytesPerCluster; 420 421 PFILE_FULL_EA_INFORMATION FullEa; 422 423 // 424 // We will allocate a buffer and copy the Ea list from the user's 425 // buffer to a FAT packed Ea list. Initial allocation is one 426 // cluster, our starting offset into the packed Ea list is 0. 427 // 428 429 PackedEasLength = 0; 430 431 BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster; 432 433 AllocationLength = (PackedEasLength 434 + SIZE_OF_EA_SET_HEADER 435 + BytesPerCluster - 1) 436 & ~(BytesPerCluster - 1); 437 438 // 439 // Allocate the memory and store the file name into it. 440 // 441 442 EaSetHeader = FsRtlAllocatePoolWithTag( PagedPool, 443 AllocationLength, 444 TAG_EA_SET_HEADER ); 445 446 RtlZeroMemory( EaSetHeader, AllocationLength ); 447 448 RtlCopyMemory( EaSetHeader->OwnerFileName, 449 FileName->Buffer, 450 FileName->Length ); 451 452 AllocationLength -= SIZE_OF_EA_SET_HEADER; 453 454 // 455 // Loop through the user's Ea list. Catch any error for invalid 456 // name or non-existent Ea value. 457 // 458 459 for ( FullEa = (PFILE_FULL_EA_INFORMATION) Buffer; 460 FullEa < (PFILE_FULL_EA_INFORMATION) &Buffer[Length]; 461 FullEa = (PFILE_FULL_EA_INFORMATION) (FullEa->NextEntryOffset == 0 ? 462 &Buffer[Length] : 463 (PUCHAR) FullEa + FullEa->NextEntryOffset)) { 464 465 OEM_STRING EaName; 466 ULONG EaOffset; 467 468 EaName.Length = FullEa->EaNameLength; 469 EaName.Buffer = &FullEa->EaName[0]; 470 471 // 472 // Make sure the ea name is valid 473 // 474 475 if (!FatIsEaNameValid( IrpContext, EaName )) { 476 477 DebugTrace(0, Dbg, 478 "FatCreateEa: Invalid Ea Name -> %Z\n", 479 EaName); 480 481 IrpContext->OriginatingIrp->IoStatus.Information = (PUCHAR)FullEa - Buffer; 482 IrpContext->OriginatingIrp->IoStatus.Status = STATUS_INVALID_EA_NAME; 483 FatRaiseStatus( IrpContext, STATUS_INVALID_EA_NAME ); 484 } 485 486 // 487 // Check that no invalid ea flags are set. 488 // 489 490 // 491 // TEMPCODE We are returning STATUS_INVALID_EA_NAME 492 // until a more appropriate error code exists. 493 // 494 495 if (FullEa->Flags != 0 496 && FullEa->Flags != FILE_NEED_EA) { 497 498 IrpContext->OriginatingIrp->IoStatus.Information = (PUCHAR)FullEa - Buffer; 499 IrpContext->OriginatingIrp->IoStatus.Status = STATUS_INVALID_EA_NAME; 500 FatRaiseStatus( IrpContext, STATUS_INVALID_EA_NAME ); 501 } 502 503 // 504 // If this is a duplicate name then delete the current ea 505 // value. 506 // 507 508 if (FatLocateEaByName( IrpContext, 509 (PPACKED_EA) EaSetHeader->PackedEas, 510 PackedEasLength, 511 &EaName, 512 &EaOffset )) { 513 514 DebugTrace(0, Dbg, "FatCreateEa: Duplicate name found\n", 0); 515 516 FatDeletePackedEa( IrpContext, 517 EaSetHeader, 518 &PackedEasLength, 519 EaOffset ); 520 } 521 522 // 523 // We ignore this value if the eavalue length is zero. 524 // 525 526 if (FullEa->EaValueLength == 0) { 527 528 DebugTrace(0, Dbg, 529 "FatCreateEa: Empty ea\n", 530 0); 531 532 continue; 533 } 534 535 FatAppendPackedEa( IrpContext, 536 &EaSetHeader, 537 &PackedEasLength, 538 &AllocationLength, 539 FullEa, 540 BytesPerCluster ); 541 } 542 543 // 544 // If the resulting length isn't zero, then allocate a FAT cluster 545 // to store the data. 546 // 547 548 if (PackedEasLength != 0) { 549 550 PEA_SET_HEADER NewEaSetHeader; 551 552 // 553 // If the packed eas length (plus 4 bytes) is greater 554 // than the maximum allowed ea size, we return an error. 555 // 556 557 if (PackedEasLength + 4 > MAXIMUM_EA_SIZE) { 558 559 DebugTrace( 0, Dbg, "Ea length is greater than maximum\n", 0 ); 560 561 FatRaiseStatus( IrpContext, STATUS_EA_TOO_LARGE ); 562 } 563 564 // 565 // Get the Ea file. 566 // 567 568 FatGetEaFile( IrpContext, 569 Vcb, 570 &EaDirent, 571 &EaBcb, 572 TRUE, 573 TRUE ); 574 575 LockedEaFcb = TRUE; 576 577 FatAddEaSet( IrpContext, 578 Vcb, 579 PackedEasLength + SIZE_OF_EA_SET_HEADER, 580 EaBcb, 581 EaDirent, 582 EaHandle, 583 &EaSetRange ); 584 585 NewEaSetHeader = (PEA_SET_HEADER) EaSetRange.Data; 586 587 // 588 // Store the length of the new Ea's into the NewEaSetHeader. 589 // This is the PackedEasLength + 4. 590 // 591 592 PackedEasLength += 4; 593 594 CopyU4char( EaSetHeader->cbList, &PackedEasLength ); 595 596 // 597 // Copy all but the first four bytes of EaSetHeader into 598 // the new ea. The signature and index fields have 599 // already been filled in. 600 // 601 602 RtlCopyMemory( &NewEaSetHeader->NeedEaCount, 603 &EaSetHeader->NeedEaCount, 604 PackedEasLength + SIZE_OF_EA_SET_HEADER - 8 ); 605 606 FatMarkEaRangeDirty( IrpContext, Vcb->VirtualEaFile, &EaSetRange ); 607 FatUnpinEaRange( IrpContext, &EaSetRange ); 608 609 CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL ); 610 611 // 612 // There was no data added to the Ea file. Return a handle 613 // of 0. 614 // 615 616 } else { 617 618 *EaHandle = 0; 619 } 620 621 } _SEH2_FINALLY { 622 623 DebugUnwind( FatCreateEa ); 624 625 // 626 // Deallocate the EaSetHeader if present. 627 // 628 629 if (EaSetHeader) { 630 631 ExFreePool( EaSetHeader ); 632 } 633 634 // 635 // Release the EaFcb if held. 636 // 637 638 if (LockedEaFcb) { 639 640 FatReleaseFcb( IrpContext, Vcb->EaFcb ); 641 } 642 643 // 644 // Unpin the dirents for the EaFcb and EaSetFcb if necessary. 645 // 646 647 FatUnpinBcb( IrpContext, EaBcb ); 648 FatUnpinEaRange( IrpContext, &EaSetRange ); 649 650 DebugTrace(-1, Dbg, "FatCreateEa -> Exit\n", 0); 651 } _SEH2_END; 652 653 return; 654 } 655 656 _Requires_lock_held_(_Global_critical_region_) 657 VOID 658 FatDeleteEa ( 659 IN PIRP_CONTEXT IrpContext, 660 IN PVCB Vcb, 661 IN USHORT EaHandle, 662 IN POEM_STRING FileName 663 ) 664 665 /*++ 666 667 Routine Description: 668 669 This routine is called to remove an entire ea set. Most of the work 670 is done in the call to 'FatDeleteEaSet'. This routine opens the 671 Ea file and then calls the support routine. 672 673 NOTE: This routine may block, it should not be called unless the 674 thread is waitable. 675 676 Arguments: 677 678 Vcb - Vcb for the volume 679 680 EaHandle - The handle for the Ea's to remove. This handle will be 681 verified during this operation. 682 683 FileName - The name of the file whose Ea's are being removed. This 684 name is compared against the Ea owner's name in the Ea set. 685 686 Return Value: 687 688 None. 689 690 --*/ 691 692 { 693 PBCB EaBcb; 694 BOOLEAN LockedEaFcb; 695 696 PAGED_CODE(); 697 698 DebugTrace(+1, Dbg, "FatDeleteEa...\n", 0); 699 700 // 701 // Initialize local values. 702 // 703 704 EaBcb = NULL; 705 LockedEaFcb = FALSE; 706 707 // 708 // Use a try statement to facilitate cleanup. 709 // 710 711 _SEH2_TRY { 712 713 PDIRENT EaDirent; 714 715 // 716 // Get the Ea stream file. If the file doesn't exist on the disk 717 // then the disk has been corrupted. 718 // 719 720 FatGetEaFile( IrpContext, 721 Vcb, 722 &EaDirent, 723 &EaBcb, 724 FALSE, 725 TRUE ); 726 727 LockedEaFcb = TRUE; 728 729 // 730 // If we didn't get the Ea file, then the disk is corrupt. 731 // 732 733 if ( EaBcb == NULL ) { 734 735 736 DebugTrace(0, Dbg, 737 "FatDeleteEa: No Ea file exists\n", 738 0); 739 740 FatRaiseStatus( IrpContext, STATUS_NO_EAS_ON_FILE ); 741 } 742 743 // 744 // We now have everything we need to delete the ea set. Call the 745 // support routine to do this. 746 // 747 748 FatDeleteEaSet( IrpContext, 749 Vcb, 750 EaBcb, 751 EaDirent, 752 EaHandle, 753 FileName ); 754 755 CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL ); 756 757 } _SEH2_FINALLY { 758 759 DebugUnwind( FatDeleteEa ); 760 761 // 762 // Release the EaFcb if held. 763 // 764 765 if (LockedEaFcb) { 766 767 FatReleaseFcb( IrpContext, Vcb->EaFcb ); 768 } 769 770 // 771 // Unpin the dirent for the Ea file if pinned. 772 // 773 774 FatUnpinBcb( IrpContext, EaBcb ); 775 776 DebugTrace(-1, Dbg, "FatDeleteEa -> Exit\n", 0); 777 } _SEH2_END; 778 779 return; 780 } 781 782 783 _Requires_lock_held_(_Global_critical_region_) 784 VOID 785 FatGetEaFile ( 786 IN PIRP_CONTEXT IrpContext, 787 IN OUT PVCB Vcb, 788 OUT PDIRENT *EaDirent, 789 OUT PBCB *EaBcb, 790 IN BOOLEAN CreateFile, 791 IN BOOLEAN ExclusiveFcb 792 ) 793 794 /*++ 795 796 Routine Description: 797 798 This routine is used to completely initialize the Vcb and 799 the Ea file for the Vcb. 800 801 If the Vcb doesn't have the Ea file object, then we first try to 802 lookup the Ea data file in the root directory and if that fails 803 we try to create the file. The 'CreateFile' flag is used to check 804 whether it is necessary to create the Ea file. 805 806 This routine will lock down the Fcb for exclusive or shared access before 807 performing any operations. If the operation does not complete due 808 to blocking, exclusive or shared access will be given up before returning. 809 810 If we are creating the Ea file and marking sections of it dirty, 811 we can't use the repin feature through the cache map. In that case 812 we use a local IrpContext and then unpin all of the Bcb's before 813 continuing. 814 815 Note: If this routine will be creating the Ea file, we are guaranteed 816 to be waitable. 817 818 Arguments: 819 820 Vcb - Vcb for the volume 821 822 EaDirent - Location to store the address of the pinned dirent for the 823 Ea file. 824 825 EaBcb - Location to store the address of the Bcb for the pinned dirent. 826 827 CreateFile - Boolean indicating whether we should create the Ea file 828 on the disk. 829 830 ExclusiveFcb - Indicates whether shared or exclusive access is desired 831 for the EaFcb. 832 833 Return Value: 834 835 None. 836 837 --*/ 838 839 { 840 PFILE_OBJECT EaStreamFile = NULL; 841 EA_RANGE EaFileRange; 842 843 BOOLEAN UnwindLockedEaFcb = FALSE; 844 BOOLEAN UnwindLockedRootDcb = FALSE; 845 BOOLEAN UnwindAllocatedDiskSpace = FALSE; 846 BOOLEAN UnwindEaDirentCreated = FALSE; 847 BOOLEAN UnwindUpdatedSizes = FALSE; 848 849 PAGED_CODE(); 850 851 DebugTrace(+1, Dbg, "FatGetEaFile ...\n", 0); 852 853 RtlZeroMemory( &EaFileRange, sizeof( EA_RANGE )); 854 855 // 856 // Use a try to facilitate cleanup 857 // 858 859 _SEH2_TRY { 860 861 OEM_STRING EaFileName; 862 LARGE_INTEGER SectionSize; 863 864 // 865 // Check if the Vcb already has the file object. If it doesn't, then 866 // we need to search the root directory for the Ea data file. 867 // 868 869 if (Vcb->VirtualEaFile == NULL) { 870 871 // 872 // Always lock the Ea file exclusively if we have to create the file. 873 // 874 875 if ( !FatAcquireExclusiveFcb( IrpContext, Vcb->EaFcb )) { 876 877 DebugTrace(0, Dbg, "FatGetEaFile: Can't grab exclusive\n", 0); 878 FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); 879 } 880 881 UnwindLockedEaFcb = TRUE; 882 883 // 884 // Otherwise we acquire the Fcb as the caller requested. 885 // 886 887 } else { 888 889 if ((ExclusiveFcb && !FatAcquireExclusiveFcb( IrpContext, Vcb->EaFcb )) 890 || (!ExclusiveFcb && !FatAcquireSharedFcb( IrpContext, Vcb->EaFcb))) { 891 892 DebugTrace(0, Dbg, "FatGetEaFile: Can't grab EaFcb\n", 0); 893 894 FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); 895 } 896 897 UnwindLockedEaFcb = TRUE; 898 899 // 900 // If the file now does not exist we need to release the Fcb and 901 // reacquire exclusive if we acquired shared. 902 // 903 904 if ((Vcb->VirtualEaFile == NULL) && !ExclusiveFcb) { 905 906 FatReleaseFcb( IrpContext, Vcb->EaFcb ); 907 UnwindLockedEaFcb = FALSE; 908 909 if (!FatAcquireExclusiveFcb( IrpContext, Vcb->EaFcb )) { 910 911 DebugTrace(0, Dbg, "FatGetEaFile: Can't grab EaFcb\n", 0); 912 913 FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); 914 } 915 916 UnwindLockedEaFcb = TRUE; 917 } 918 } 919 920 // 921 // If the file object is now there we only need to get the 922 // dirent for the Ea file. 923 // 924 925 if (Vcb->VirtualEaFile != NULL) { 926 927 FatVerifyFcb( IrpContext, Vcb->EaFcb ); 928 929 FatGetDirentFromFcbOrDcb( IrpContext, 930 Vcb->EaFcb, 931 FALSE, 932 EaDirent, 933 EaBcb ); 934 935 try_return( NOTHING ); 936 937 } else { 938 939 VBO ByteOffset = 0; 940 941 // 942 // Always mark the ea fcb as good. 943 // 944 945 Vcb->EaFcb->FcbCondition = FcbGood; 946 947 // 948 // We try to lookup the dirent for the Ea Fcb. 949 // 950 951 EaFileName.Buffer = "EA DATA. SF"; 952 EaFileName.Length = 11; 953 EaFileName.MaximumLength = 12; 954 955 // 956 // Now pick up the root directory to be synchronized with 957 // deletion/creation of entries. If we may create the file, 958 // get it exclusive right now. 959 // 960 // Again, note how we are relying on bottom-up lockorder. We 961 // already got the EaFcb. 962 // 963 964 if (CreateFile) { 965 ExAcquireResourceExclusiveLite( Vcb->RootDcb->Header.Resource, TRUE ); 966 } else { 967 ExAcquireResourceSharedLite( Vcb->RootDcb->Header.Resource, TRUE ); 968 } 969 UnwindLockedRootDcb = TRUE; 970 971 FatLocateSimpleOemDirent( IrpContext, 972 Vcb->EaFcb->ParentDcb, 973 &EaFileName, 974 EaDirent, 975 EaBcb, 976 &ByteOffset ); 977 978 // 979 // If the file exists, we need to create the virtual file 980 // object for it. 981 // 982 983 if (*EaDirent != NULL) { 984 985 // 986 // Since we may be modifying the dirent, pin the data now. 987 // 988 989 FatPinMappedData( IrpContext, 990 Vcb->EaFcb->ParentDcb, 991 ByteOffset, 992 sizeof(DIRENT), 993 EaBcb ); 994 995 // 996 // Update the Fcb with information on the file size 997 // and disk location. Also increment the open/unclean 998 // counts in the EaFcb and the open count in the 999 // Vcb. 1000 // 1001 1002 Vcb->EaFcb->FirstClusterOfFile = (*EaDirent)->FirstClusterOfFile; 1003 Vcb->EaFcb->DirentOffsetWithinDirectory = ByteOffset; 1004 1005 // 1006 // Find the allocation size. The purpose here is 1007 // really to completely fill in the Mcb for the 1008 // file. 1009 // 1010 1011 Vcb->EaFcb->Header.AllocationSize.QuadPart = FCB_LOOKUP_ALLOCATIONSIZE_HINT; 1012 1013 FatLookupFileAllocationSize( IrpContext, Vcb->EaFcb ); 1014 1015 // 1016 // Start by computing the section size for the cache 1017 // manager. 1018 // 1019 1020 SectionSize.QuadPart = (*EaDirent)->FileSize; 1021 Vcb->EaFcb->Header.AllocationSize = SectionSize; 1022 Vcb->EaFcb->Header.FileSize = SectionSize; 1023 1024 // 1025 // Create and initialize the file object for the 1026 // Ea virtual file. 1027 // 1028 1029 EaStreamFile = FatOpenEaFile( IrpContext, Vcb->EaFcb ); 1030 1031 Vcb->VirtualEaFile = EaStreamFile; 1032 1033 // 1034 // Else there was no dirent. If we were instructed to 1035 // create the file object, we will try to create the dirent, 1036 // allocate disk space, initialize the Ea file header and 1037 // return this information to the user. 1038 // 1039 1040 } else if (CreateFile) { 1041 1042 ULONG BytesPerCluster; 1043 ULONG OffsetTableSize; 1044 ULONG AllocationSize; 1045 PEA_FILE_HEADER FileHeader; 1046 USHORT AllocatedClusters; 1047 PUSHORT CurrentIndex; 1048 ULONG Index; 1049 NTSTATUS Status = STATUS_SUCCESS; 1050 1051 DebugTrace(0, Dbg, "FatGetEaFile: Creating local IrpContext\n", 0); 1052 1053 BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster; 1054 1055 AllocationSize = (((ULONG) sizeof( EA_FILE_HEADER ) << 1) + BytesPerCluster - 1) 1056 & ~(BytesPerCluster - 1); 1057 1058 AllocatedClusters = (USHORT) (AllocationSize 1059 >> Vcb->AllocationSupport.LogOfBytesPerCluster); 1060 1061 OffsetTableSize = AllocationSize - sizeof( EA_FILE_HEADER ); 1062 1063 // 1064 // Allocate disk space, the space allocated is 1024 bytes 1065 // rounded up to the nearest cluster size. 1066 // 1067 1068 FatAllocateDiskSpace( IrpContext, 1069 Vcb, 1070 0, 1071 &AllocationSize, 1072 FALSE, 1073 &Vcb->EaFcb->Mcb ); 1074 1075 UnwindAllocatedDiskSpace = TRUE; 1076 1077 // 1078 // Allocate and initialize a dirent in the root directory 1079 // to describe this new file. 1080 // 1081 1082 Vcb->EaFcb->DirentOffsetWithinDirectory = 1083 FatCreateNewDirent( IrpContext, 1084 Vcb->EaFcb->ParentDcb, 1085 1, 1086 FALSE ); 1087 1088 FatPrepareWriteDirectoryFile( IrpContext, 1089 Vcb->EaFcb->ParentDcb, 1090 Vcb->EaFcb->DirentOffsetWithinDirectory, 1091 sizeof(DIRENT), 1092 EaBcb, 1093 #ifndef __REACTOS__ 1094 EaDirent, 1095 #else 1096 (PVOID *)EaDirent, 1097 #endif 1098 FALSE, 1099 TRUE, 1100 &Status ); 1101 1102 NT_ASSERT( NT_SUCCESS( Status )); 1103 1104 UnwindEaDirentCreated = TRUE; 1105 1106 FatConstructDirent( IrpContext, 1107 *EaDirent, 1108 &EaFileName, 1109 FALSE, 1110 FALSE, 1111 NULL, 1112 FAT_DIRENT_ATTR_READ_ONLY 1113 | FAT_DIRENT_ATTR_HIDDEN 1114 | FAT_DIRENT_ATTR_SYSTEM 1115 | FAT_DIRENT_ATTR_ARCHIVE, 1116 TRUE, 1117 NULL ); 1118 1119 (*EaDirent)->FileSize = AllocationSize; 1120 1121 // 1122 // Initialize the Fcb for this file and initialize the 1123 // cache map as well. 1124 // 1125 1126 // 1127 // Start by computing the section size for the cache 1128 // manager. 1129 // 1130 1131 SectionSize.QuadPart = (*EaDirent)->FileSize; 1132 Vcb->EaFcb->Header.AllocationSize = SectionSize; 1133 Vcb->EaFcb->Header.FileSize = SectionSize; 1134 UnwindUpdatedSizes = TRUE; 1135 1136 // 1137 // Create and initialize the file object for the 1138 // Ea virtual file. 1139 // 1140 1141 EaStreamFile = FatOpenEaFile( IrpContext, Vcb->EaFcb ); 1142 1143 // 1144 // Update the Fcb with information on the file size 1145 // and disk location. Also increment the open/unclean 1146 // counts in the EaFcb and the open count in the 1147 // Vcb. 1148 // 1149 1150 { 1151 LBO FirstLboOfFile; 1152 1153 FatLookupMcbEntry( Vcb, &Vcb->EaFcb->Mcb, 1154 0, 1155 &FirstLboOfFile, 1156 NULL, 1157 NULL ); 1158 1159 // 1160 // The discerning reader will note that this doesn't take 1161 // FAT32 into account, which is of course intentional. 1162 // 1163 1164 (*EaDirent)->FirstClusterOfFile = 1165 (USHORT) FatGetIndexFromLbo( Vcb, FirstLboOfFile ); 1166 } 1167 1168 Vcb->EaFcb->FirstClusterOfFile = (*EaDirent)->FirstClusterOfFile; 1169 1170 // 1171 // Initialize the Ea file header and mark the Bcb as dirty. 1172 // 1173 1174 FatPinEaRange( IrpContext, 1175 EaStreamFile, 1176 Vcb->EaFcb, 1177 &EaFileRange, 1178 0, 1179 AllocationSize, 1180 STATUS_DATA_ERROR ); 1181 1182 FileHeader = (PEA_FILE_HEADER) EaFileRange.Data; 1183 1184 RtlZeroMemory( FileHeader, AllocationSize ); 1185 FileHeader->Signature = EA_FILE_SIGNATURE; 1186 1187 for (Index = MAX_EA_BASE_INDEX, CurrentIndex = FileHeader->EaBaseTable; 1188 Index; 1189 Index--, CurrentIndex++) { 1190 1191 *CurrentIndex = AllocatedClusters; 1192 } 1193 1194 // 1195 // Initialize the offset table with the offset set to 1196 // after the just allocated clusters. 1197 // 1198 1199 for (Index = OffsetTableSize >> 1, 1200 CurrentIndex = (PUSHORT) ((PUCHAR) FileHeader + sizeof( EA_FILE_HEADER )); 1201 Index; 1202 Index--, CurrentIndex++) { 1203 1204 *CurrentIndex = UNUSED_EA_HANDLE; 1205 } 1206 1207 // 1208 // Unpin the file header and offset table. 1209 // 1210 1211 FatMarkEaRangeDirty( IrpContext, EaStreamFile, &EaFileRange ); 1212 FatUnpinEaRange( IrpContext, &EaFileRange ); 1213 1214 CcFlushCache( EaStreamFile->SectionObjectPointer, NULL, 0, NULL ); 1215 1216 // 1217 // Return the Ea file object to the user. 1218 // 1219 1220 Vcb->VirtualEaFile = EaStreamFile; 1221 } 1222 } 1223 try_exit: NOTHING; 1224 } _SEH2_FINALLY { 1225 1226 DebugUnwind( FatGetEaFile ); 1227 1228 // 1229 // If this is abnormal termination and disk space has been 1230 // allocated. We deallocate it now. 1231 // 1232 1233 if (_SEH2_AbnormalTermination()) { 1234 1235 // 1236 // Deallocate the Ea file 1237 // 1238 1239 if (UnwindAllocatedDiskSpace) { 1240 1241 FatDeallocateDiskSpace( IrpContext, 1242 Vcb, 1243 &Vcb->EaFcb->Mcb, 1244 FALSE ); 1245 } 1246 1247 // 1248 // Delete the dirent for the Ea file, if created. 1249 // 1250 1251 if (UnwindEaDirentCreated) { 1252 1253 if (UnwindUpdatedSizes) { 1254 1255 Vcb->EaFcb->Header.AllocationSize.QuadPart = 0; 1256 Vcb->EaFcb->Header.FileSize.QuadPart = 0; 1257 } 1258 1259 FatUnpinBcb( IrpContext, *EaBcb ); 1260 FatDeleteDirent( IrpContext, Vcb->EaFcb, NULL, TRUE ); 1261 } 1262 1263 // 1264 // Release the EA Fcb if held 1265 // 1266 1267 if (UnwindLockedEaFcb) { 1268 1269 FatReleaseFcb( IrpContext, Vcb->EaFcb ); 1270 } 1271 1272 // 1273 // Dereference the Ea stream file if created. 1274 // 1275 1276 if (EaStreamFile != NULL) { 1277 1278 ObDereferenceObject( EaStreamFile ); 1279 } 1280 } 1281 1282 // 1283 // Always release the root Dcb (our caller releases the EA Fcb if we 1284 // do not raise). 1285 // 1286 1287 if (UnwindLockedRootDcb) { 1288 1289 FatReleaseFcb( IrpContext, Vcb->RootDcb ); 1290 } 1291 1292 // 1293 // If the Ea file header is locked down. We unpin it now. 1294 // 1295 1296 FatUnpinEaRange( IrpContext, &EaFileRange ); 1297 1298 DebugTrace(-1, Dbg, "FatGetEaFile: Exit\n", 0); 1299 } _SEH2_END; 1300 1301 return; 1302 } 1303 1304 1305 VOID 1306 FatReadEaSet ( 1307 IN PIRP_CONTEXT IrpContext, 1308 IN PVCB Vcb, 1309 IN USHORT EaHandle, 1310 IN POEM_STRING FileName, 1311 IN BOOLEAN ReturnEntireSet, 1312 OUT PEA_RANGE EaSetRange 1313 ) 1314 1315 /*++ 1316 1317 Routine Description: 1318 1319 This routine pins the Ea set for the given ea handle within the 1320 Ea stream file. The EaHandle, after first comparing against valid 1321 index values, is used to compute the cluster offset for this 1322 this Ea set. The Ea set is then verified as belonging to this 1323 index and lying within the Ea data file. 1324 1325 The caller of this function will have verified that the Ea file 1326 exists and that the Vcb field points to an initialized cache file. 1327 The caller will already have gained exclusive access to the 1328 EaFcb. 1329 1330 Arguments: 1331 1332 Vcb - Supplies the Vcb for the volume. 1333 1334 EaHandle - Supplies the handle for the Ea's to read. 1335 1336 FileName - Name of the file whose Ea's are being read. 1337 1338 ReturnEntireSet - Indicates if the caller needs the entire set 1339 as opposed to just the header. 1340 1341 EaSetRange - Pointer to the EaRange structure which will describe the Ea 1342 on return. 1343 1344 Return Value: 1345 1346 None 1347 1348 --*/ 1349 1350 { 1351 ULONG BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster; 1352 1353 ULONG EaOffsetVbo; 1354 EA_RANGE EaOffsetRange; 1355 USHORT EaOffsetCluster; 1356 1357 EA_RANGE EaHeaderRange; 1358 PEA_FILE_HEADER EaHeader; 1359 1360 ULONG EaSetVbo; 1361 PEA_SET_HEADER EaSet; 1362 1363 ULONG CbList; 1364 1365 PAGED_CODE(); 1366 1367 UNREFERENCED_PARAMETER( FileName ); 1368 1369 DebugTrace(+1, Dbg, "FatReadEaSet\n", 0); 1370 DebugTrace( 0, Dbg, " Vcb = %p\n", Vcb); 1371 1372 // 1373 // Verify that the Ea index has a legal value. Raise status 1374 // STATUS_NONEXISTENT_EA_ENTRY if illegal. 1375 // 1376 1377 if (EaHandle < MIN_EA_HANDLE 1378 || EaHandle > MAX_EA_HANDLE) { 1379 1380 DebugTrace(-1, Dbg, "FatReadEaSet: Illegal handle value\n", 0); 1381 FatRaiseStatus( IrpContext, STATUS_NONEXISTENT_EA_ENTRY ); 1382 } 1383 1384 // 1385 // Verify that the virtual Ea file is large enough for us to read 1386 // the EaOffet table for this index. 1387 // 1388 1389 EaOffsetVbo = sizeof( EA_FILE_HEADER ) + (((ULONGLONG)EaHandle >> 7) << 8); 1390 1391 // 1392 // Zero the Ea range structures. 1393 // 1394 1395 RtlZeroMemory( &EaHeaderRange, sizeof( EA_RANGE )); 1396 RtlZeroMemory( &EaOffsetRange, sizeof( EA_RANGE )); 1397 1398 // 1399 // Use a try statement to clean up on exit. 1400 // 1401 1402 _SEH2_TRY { 1403 1404 // 1405 // Pin down the EA file header. 1406 // 1407 1408 FatPinEaRange( IrpContext, 1409 Vcb->VirtualEaFile, 1410 Vcb->EaFcb, 1411 &EaHeaderRange, 1412 0, 1413 sizeof( EA_FILE_HEADER ), 1414 STATUS_NONEXISTENT_EA_ENTRY ); 1415 1416 EaHeader = (PEA_FILE_HEADER) EaHeaderRange.Data; 1417 1418 // 1419 // Pin down the Ea offset table for the particular index. 1420 // 1421 1422 FatPinEaRange( IrpContext, 1423 Vcb->VirtualEaFile, 1424 Vcb->EaFcb, 1425 &EaOffsetRange, 1426 EaOffsetVbo, 1427 sizeof( EA_OFF_TABLE ), 1428 STATUS_NONEXISTENT_EA_ENTRY ); 1429 1430 // 1431 // Check if the specifific handle is currently being used. 1432 // 1433 1434 EaOffsetCluster = *((PUSHORT) EaOffsetRange.Data 1435 + (EaHandle & (MAX_EA_OFFSET_INDEX - 1))); 1436 1437 if (EaOffsetCluster == UNUSED_EA_HANDLE) { 1438 1439 DebugTrace(0, Dbg, "FatReadEaSet: Ea handle is unused\n", 0); 1440 FatRaiseStatus( IrpContext, STATUS_NONEXISTENT_EA_ENTRY ); 1441 } 1442 1443 // 1444 // Compute the file offset for the Ea data. 1445 // 1446 1447 EaSetVbo = (EaHeader->EaBaseTable[EaHandle >> 7] + EaOffsetCluster) 1448 << Vcb->AllocationSupport.LogOfBytesPerCluster; 1449 1450 // 1451 // Unpin the file header and offset table. 1452 // 1453 1454 FatUnpinEaRange( IrpContext, &EaHeaderRange ); 1455 FatUnpinEaRange( IrpContext, &EaOffsetRange ); 1456 1457 // 1458 // Pin the ea set. 1459 // 1460 1461 FatPinEaRange( IrpContext, 1462 Vcb->VirtualEaFile, 1463 Vcb->EaFcb, 1464 EaSetRange, 1465 EaSetVbo, 1466 BytesPerCluster, 1467 STATUS_DATA_ERROR ); 1468 1469 // 1470 // Verify that the Ea set is valid and belongs to this index. 1471 // Raise STATUS_DATA_ERROR if there is a data conflict. 1472 // 1473 1474 EaSet = (PEA_SET_HEADER) EaSetRange->Data; 1475 1476 if (EaSet->Signature != EA_SET_SIGNATURE 1477 || EaSet->OwnEaHandle != EaHandle ) { 1478 1479 DebugTrace(0, Dbg, "FatReadEaSet: Ea set header is corrupt\n", 0); 1480 FatRaiseStatus( IrpContext, STATUS_DATA_ERROR ); 1481 } 1482 1483 // 1484 // At this point we have pinned a single cluster of Ea data. If 1485 // this represents the entire Ea data for the Ea index, we are 1486 // done. Otherwise we need to check on the entire size of 1487 // of the Ea set header and whether it is contained in the allocated 1488 // size of the Ea virtual file. At that point we can unpin 1489 // the partial Ea set header and repin the entire header. 1490 // 1491 1492 CbList = GetcbList( EaSet ); 1493 1494 if (ReturnEntireSet 1495 && CbList > BytesPerCluster ) { 1496 1497 // 1498 // Round up to the cluster size. 1499 // 1500 1501 CbList = (CbList + EA_CBLIST_OFFSET + BytesPerCluster - 1) 1502 & ~(BytesPerCluster - 1); 1503 1504 FatUnpinEaRange( IrpContext, EaSetRange ); 1505 1506 RtlZeroMemory( EaSetRange, sizeof( EA_RANGE )); 1507 1508 FatPinEaRange( IrpContext, 1509 Vcb->VirtualEaFile, 1510 Vcb->EaFcb, 1511 EaSetRange, 1512 EaSetVbo, 1513 CbList, 1514 STATUS_DATA_ERROR ); 1515 } 1516 1517 } _SEH2_FINALLY { 1518 1519 DebugUnwind( FatReadEaSet ); 1520 1521 // 1522 // Unpin the Ea base and offset tables if locked down. 1523 // 1524 1525 FatUnpinEaRange( IrpContext, &EaHeaderRange ); 1526 FatUnpinEaRange( IrpContext, &EaOffsetRange ); 1527 1528 DebugTrace(-1, Dbg, "FatReadEaSet: Exit\n", 0); 1529 } _SEH2_END; 1530 1531 return; 1532 } 1533 1534 1535 _Requires_lock_held_(_Global_critical_region_) 1536 VOID 1537 FatDeleteEaSet ( 1538 IN PIRP_CONTEXT IrpContext, 1539 IN PVCB Vcb, 1540 IN PBCB EaBcb, 1541 OUT PDIRENT EaDirent, 1542 IN USHORT EaHandle, 1543 IN POEM_STRING FileName 1544 ) 1545 1546 /*++ 1547 1548 Routine Description: 1549 1550 This routines clips the Ea set for a particular index out of the 1551 Ea file for a volume. The index is verified as belonging to a valid 1552 handle. The clusters are removed and the Ea stream file along with 1553 the Ea base and offset files are updated. 1554 1555 The caller of this function will have verified that the Ea file 1556 exists and that the Vcb field points to an initialized cache file. 1557 The caller will already have gained exclusive access to the 1558 EaFcb. 1559 1560 Arguments: 1561 1562 Vcb - Supplies the Vcb for the volume. 1563 1564 VirtualEeFile - Pointer to the file object for the virtual Ea file. 1565 1566 EaFcb - Supplies the pointer to the Fcb for the Ea file. 1567 1568 EaBcb - Supplies a pointer to the Bcb for the Ea dirent. 1569 1570 EaDirent - Supplies a pointer to the dirent for the Ea file. 1571 1572 EaHandle - Supplies the handle for the Ea's to read. 1573 1574 FileName - Name of the file whose Ea's are being read. 1575 1576 Return Value: 1577 1578 None. 1579 1580 --*/ 1581 1582 { 1583 ULONG BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster; 1584 ULONG CbList; 1585 LARGE_INTEGER FileOffset; 1586 1587 LARGE_MCB DataMcb; 1588 BOOLEAN UnwindInitializeDataMcb = FALSE; 1589 BOOLEAN UnwindSplitData = FALSE; 1590 1591 LARGE_MCB TailMcb; 1592 BOOLEAN UnwindInitializeTailMcb = FALSE; 1593 BOOLEAN UnwindSplitTail = FALSE; 1594 BOOLEAN UnwindMergeTail = FALSE; 1595 1596 BOOLEAN UnwindModifiedEaHeader = FALSE; 1597 BOOLEAN UnwindCacheValues = FALSE; 1598 ULONG UnwindPrevFileSize = 0; 1599 1600 ULONG EaOffsetVbo; 1601 USHORT EaOffsetIndex; 1602 EA_RANGE EaOffsetRange; 1603 USHORT EaOffsetCluster; 1604 1605 PFILE_OBJECT VirtualEaFile = Vcb->VirtualEaFile; 1606 PFCB EaFcb = Vcb->EaFcb; 1607 1608 EA_RANGE EaHeaderRange; 1609 PEA_FILE_HEADER EaHeader; 1610 USHORT EaHeaderBaseIndex; 1611 1612 ULONG EaSetVbo = 0; 1613 ULONG EaSetLength; 1614 EA_RANGE EaSetRange; 1615 PEA_SET_HEADER EaSet; 1616 USHORT EaSetClusterCount; 1617 1618 PAGED_CODE(); 1619 1620 UNREFERENCED_PARAMETER( FileName ); 1621 1622 // 1623 // Verify that the Ea index has a legal value. Raise status 1624 // STATUS_INVALID_HANDLE if illegal. 1625 // 1626 1627 if (EaHandle < MIN_EA_HANDLE 1628 || EaHandle > MAX_EA_HANDLE) { 1629 1630 DebugTrace(-1, Dbg, "FatDeleteEaSet: Illegal handle value\n", 0); 1631 FatRaiseStatus( IrpContext, STATUS_NONEXISTENT_EA_ENTRY ); 1632 } 1633 1634 // 1635 // Verify that the virtual Ea file is large enough for us to read 1636 // the EaOffet table for this index. 1637 // 1638 1639 EaOffsetVbo = sizeof( EA_FILE_HEADER ) + (((ULONGLONG)EaHandle >> 7) << 8); 1640 1641 // 1642 // Zero the Ea range structures. 1643 // 1644 1645 RtlZeroMemory( &EaHeaderRange, sizeof( EA_RANGE )); 1646 RtlZeroMemory( &EaOffsetRange, sizeof( EA_RANGE )); 1647 RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE )); 1648 1649 // 1650 // Use a try to facilitate cleanup. 1651 // 1652 1653 _SEH2_TRY { 1654 1655 // 1656 // Pin down the EA file header. 1657 // 1658 1659 FatPinEaRange( IrpContext, 1660 VirtualEaFile, 1661 EaFcb, 1662 &EaHeaderRange, 1663 0, 1664 sizeof( EA_FILE_HEADER ), 1665 STATUS_NONEXISTENT_EA_ENTRY ); 1666 1667 EaHeader = (PEA_FILE_HEADER) EaHeaderRange.Data; 1668 1669 // 1670 // Pin down the Ea offset table for the particular index. 1671 // 1672 1673 FatPinEaRange( IrpContext, 1674 VirtualEaFile, 1675 EaFcb, 1676 &EaOffsetRange, 1677 EaOffsetVbo, 1678 sizeof( EA_OFF_TABLE ), 1679 STATUS_NONEXISTENT_EA_ENTRY ); 1680 1681 // 1682 // Check if the specifific handle is currently being used. 1683 // 1684 1685 EaOffsetIndex = EaHandle & (MAX_EA_OFFSET_INDEX - 1); 1686 EaOffsetCluster = *((PUSHORT) EaOffsetRange.Data + EaOffsetIndex); 1687 1688 if (EaOffsetCluster == UNUSED_EA_HANDLE) { 1689 1690 DebugTrace(0, Dbg, "FatReadEaSet: Ea handle is unused\n", 0); 1691 FatRaiseStatus( IrpContext, STATUS_NONEXISTENT_EA_ENTRY ); 1692 } 1693 1694 // 1695 // Compute the file offset for the Ea data. 1696 // 1697 1698 EaHeaderBaseIndex = EaHandle >> 7; 1699 EaSetVbo = (EaHeader->EaBaseTable[EaHeaderBaseIndex] + EaOffsetCluster) 1700 << Vcb->AllocationSupport.LogOfBytesPerCluster; 1701 1702 // 1703 // Unpin the file header and offset table. 1704 // 1705 1706 FatUnpinEaRange( IrpContext, &EaHeaderRange ); 1707 FatUnpinEaRange( IrpContext, &EaOffsetRange ); 1708 1709 // 1710 // Try to pin the requested Ea set. 1711 // 1712 1713 FatPinEaRange( IrpContext, 1714 VirtualEaFile, 1715 EaFcb, 1716 &EaSetRange, 1717 EaSetVbo, 1718 BytesPerCluster, 1719 STATUS_DATA_ERROR ); 1720 1721 EaSet = (PEA_SET_HEADER) EaSetRange.Data; 1722 1723 if (EaSet->Signature != EA_SET_SIGNATURE 1724 || EaSet->OwnEaHandle != EaHandle ) { 1725 1726 DebugTrace(0, Dbg, "FatReadEaSet: Ea set header is corrupt\n", 0); 1727 FatRaiseStatus( IrpContext, STATUS_DATA_ERROR ); 1728 } 1729 1730 // 1731 // At this point we have pinned a single cluster of Ea data. If 1732 // this represents the entire Ea data for the Ea index, we know 1733 // the number of clusters to remove. Otherwise we need to check 1734 // on the entire size of the Ea set header and whether it is 1735 // contained in the allocated size of the Ea virtual file. At 1736 // that point we unpin the partial Ea set header and remember the 1737 // starting cluster offset and number of clusters in both cluster 1738 // and Vbo formats. 1739 // 1740 // At that point the following variables have the described 1741 // values. 1742 // 1743 // EaSetVbo - Vbo to start splice at. 1744 // EaSetLength - Number of bytes to splice. 1745 // EaSetClusterCount - Number of clusters to splice. 1746 // 1747 1748 CbList = GetcbList( EaSet ); 1749 1750 EaSetClusterCount = (USHORT) ((CbList + EA_CBLIST_OFFSET + BytesPerCluster - 1) 1751 >> Vcb->AllocationSupport.LogOfBytesPerCluster); 1752 1753 EaSetLength = EaSetClusterCount << Vcb->AllocationSupport.LogOfBytesPerCluster; 1754 1755 if (EaSetLength > BytesPerCluster) { 1756 1757 if (EaFcb->Header.FileSize.LowPart - EaSetVbo < EaSetLength) { 1758 1759 DebugTrace(0, Dbg, "FatDeleteEaSet: Full Ea set not contained in file\n", 0); 1760 1761 FatRaiseStatus( IrpContext, STATUS_DATA_ERROR ); 1762 } 1763 } 1764 1765 FatUnpinEaRange( IrpContext, &EaSetRange ); 1766 1767 // 1768 // Update the cache manager for this file. This is done by 1769 // truncating to the point where the data was spliced and 1770 // reinitializing with the modified size of the file. 1771 // 1772 // NOTE: Even if the all the EA's are removed the Ea file will 1773 // always exist and the header area will never shrink. 1774 // 1775 1776 FileOffset.LowPart = EaSetVbo; 1777 FileOffset.HighPart = 0; 1778 1779 // 1780 // Round the cache map down to a system page boundary. 1781 // 1782 1783 FileOffset.LowPart &= ~(PAGE_SIZE - 1); 1784 1785 // 1786 // Make sure all the data gets out to the disk. 1787 // 1788 1789 { 1790 IO_STATUS_BLOCK Iosb; 1791 ULONG PurgeCount = 5; 1792 1793 while (--PurgeCount) { 1794 1795 Iosb.Status = STATUS_SUCCESS; 1796 1797 CcFlushCache( VirtualEaFile->SectionObjectPointer, 1798 NULL, 1799 0, 1800 &Iosb ); 1801 1802 NT_ASSERT( Iosb.Status == STATUS_SUCCESS ); 1803 1804 // 1805 // We do not have to worry about a lazy writer firing in parallel 1806 // with our CcFlushCache since we have the EaFcb exclusive. Thus 1807 // we know all data is out. 1808 // 1809 1810 // 1811 // We throw the unwanted pages out of the cache and then 1812 // truncate the Ea File for the new size. 1813 // 1814 1815 if (CcPurgeCacheSection( VirtualEaFile->SectionObjectPointer, 1816 &FileOffset, 1817 0, 1818 FALSE )) { 1819 1820 break; 1821 } 1822 } 1823 1824 if (!PurgeCount) { 1825 1826 FatRaiseStatus( IrpContext, STATUS_UNABLE_TO_DELETE_SECTION ); 1827 } 1828 } 1829 1830 FileOffset.LowPart = EaFcb->Header.FileSize.LowPart - EaSetLength; 1831 1832 // 1833 // Perform the splice operation on the FAT chain. This is done 1834 // by splitting the target clusters out and merging the remaining 1835 // clusters around them. We can ignore the return value from 1836 // the merge and splice functions because we are guaranteed 1837 // to be able to block. 1838 // 1839 1840 { 1841 FsRtlInitializeLargeMcb( &DataMcb, PagedPool ); 1842 1843 UnwindInitializeDataMcb = TRUE; 1844 1845 FatSplitAllocation( IrpContext, 1846 Vcb, 1847 &EaFcb->Mcb, 1848 EaSetVbo, 1849 &DataMcb ); 1850 1851 UnwindSplitData = TRUE; 1852 1853 if (EaSetLength + EaSetVbo != EaFcb->Header.FileSize.LowPart) { 1854 1855 FsRtlInitializeLargeMcb( &TailMcb, PagedPool ); 1856 1857 UnwindInitializeTailMcb = TRUE; 1858 1859 FatSplitAllocation( IrpContext, 1860 Vcb, 1861 &DataMcb, 1862 EaSetLength, 1863 &TailMcb ); 1864 1865 UnwindSplitTail = TRUE; 1866 1867 FatMergeAllocation( IrpContext, 1868 Vcb, 1869 &EaFcb->Mcb, 1870 &TailMcb ); 1871 1872 UnwindMergeTail = TRUE; 1873 } 1874 } 1875 1876 // 1877 // Update the Fcb for the Ea file 1878 // 1879 1880 UnwindPrevFileSize = EaFcb->Header.FileSize.LowPart; 1881 1882 (VOID)ExAcquireResourceExclusiveLite( EaFcb->Header.PagingIoResource, 1883 TRUE ); 1884 1885 EaFcb->Header.FileSize.LowPart = EaFcb->Header.FileSize.LowPart - EaSetLength; 1886 EaFcb->Header.AllocationSize = EaFcb->Header.FileSize; 1887 1888 1889 CcSetFileSizes( VirtualEaFile, 1890 (PCC_FILE_SIZES)&EaFcb->Header.AllocationSize ); 1891 1892 ExReleaseResourceLite( EaFcb->Header.PagingIoResource ); 1893 1894 UnwindCacheValues = TRUE; 1895 1896 EaDirent->FileSize = EaFcb->Header.FileSize.LowPart; 1897 1898 FatSetDirtyBcb( IrpContext, EaBcb, Vcb, TRUE ); 1899 1900 // 1901 // Update the Ea base and offset tables. For the Ea base table, 1902 // all subsequent index values must be decremented by the number 1903 // of clusters removed. 1904 // 1905 // For the entries in the relevant Ea offset table, all entries 1906 // after this index must also be decreased by the number of 1907 // clusters removed. 1908 // 1909 1910 // 1911 // Pin down the EA file header. 1912 // 1913 1914 RtlZeroMemory( &EaHeaderRange, 1915 sizeof( EA_RANGE )); 1916 1917 FatPinEaRange( IrpContext, 1918 VirtualEaFile, 1919 EaFcb, 1920 &EaHeaderRange, 1921 0, 1922 sizeof( EA_FILE_HEADER ), 1923 STATUS_NONEXISTENT_EA_ENTRY ); 1924 1925 EaHeader = (PEA_FILE_HEADER) EaHeaderRange.Data; 1926 1927 // 1928 // Pin down the Ea offset table for the particular index. 1929 // 1930 1931 RtlZeroMemory( &EaOffsetRange, 1932 sizeof( EA_RANGE )); 1933 1934 FatPinEaRange( IrpContext, 1935 VirtualEaFile, 1936 EaFcb, 1937 &EaOffsetRange, 1938 EaOffsetVbo, 1939 sizeof( EA_OFF_TABLE ), 1940 STATUS_NONEXISTENT_EA_ENTRY ); 1941 1942 { 1943 ULONG Count; 1944 PUSHORT NextEaIndex; 1945 1946 Count = MAX_EA_BASE_INDEX - EaHeaderBaseIndex - 1; 1947 1948 NextEaIndex = &EaHeader->EaBaseTable[EaHeaderBaseIndex + 1]; 1949 1950 while (Count--) { 1951 1952 *(NextEaIndex++) -= EaSetClusterCount; 1953 } 1954 1955 FatMarkEaRangeDirty( IrpContext, VirtualEaFile, &EaHeaderRange ); 1956 1957 Count = MAX_EA_OFFSET_INDEX - EaOffsetIndex - 1; 1958 NextEaIndex = (PUSHORT) EaOffsetRange.Data + EaOffsetIndex; 1959 1960 *(NextEaIndex++) = UNUSED_EA_HANDLE; 1961 1962 while (Count--) { 1963 1964 if (*NextEaIndex != UNUSED_EA_HANDLE) { 1965 1966 *NextEaIndex -= EaSetClusterCount; 1967 } 1968 1969 NextEaIndex++; 1970 } 1971 1972 FatMarkEaRangeDirty( IrpContext, VirtualEaFile, &EaOffsetRange ); 1973 } 1974 1975 UnwindModifiedEaHeader = TRUE; 1976 1977 // 1978 // Deallocate the ea set removed 1979 // 1980 1981 FatDeallocateDiskSpace( IrpContext, 1982 Vcb, 1983 &DataMcb, 1984 FALSE ); 1985 1986 } _SEH2_FINALLY { 1987 1988 DebugUnwind( FatDeleteEaSet ); 1989 1990 // 1991 // Restore file if abnormal termination. 1992 // 1993 // If we have modified the ea file header we ignore this 1994 // error. Otherwise we walk through the state variables. 1995 // 1996 1997 if (_SEH2_AbnormalTermination() 1998 && !UnwindModifiedEaHeader) { 1999 2000 // 2001 // If we modified the Ea dirent or Fcb, recover the previous 2002 // values. 2003 // 2004 2005 if (UnwindPrevFileSize) { 2006 2007 EaFcb->Header.FileSize.LowPart = UnwindPrevFileSize; 2008 EaFcb->Header.AllocationSize.LowPart = UnwindPrevFileSize; 2009 EaDirent->FileSize = UnwindPrevFileSize; 2010 2011 if (UnwindCacheValues) { 2012 2013 CcSetFileSizes( VirtualEaFile, 2014 (PCC_FILE_SIZES)&EaFcb->Header.AllocationSize ); 2015 } 2016 } 2017 2018 // 2019 // If we merged the tail with the 2020 // ea file header. We split it out 2021 // again. 2022 // 2023 2024 if (UnwindMergeTail) { 2025 2026 FatSplitAllocation( IrpContext, 2027 Vcb, 2028 &EaFcb->Mcb, 2029 EaSetVbo, 2030 &TailMcb ); 2031 } 2032 2033 // 2034 // If we split the tail off we merge the tail back 2035 // with the ea data to remove. 2036 // 2037 2038 if (UnwindSplitTail) { 2039 2040 FatMergeAllocation( IrpContext, 2041 Vcb, 2042 &DataMcb, 2043 &TailMcb ); 2044 } 2045 2046 // 2047 // If the ea set has been split out, we merge that 2048 // cluster string back in the file. Otherwise we 2049 // simply uninitialize the local Mcb. 2050 // 2051 2052 if (UnwindSplitData) { 2053 2054 FatMergeAllocation( IrpContext, 2055 Vcb, 2056 &EaFcb->Mcb, 2057 &DataMcb ); 2058 } 2059 } 2060 2061 // 2062 // Unpin any Bcb's still active. 2063 // 2064 2065 FatUnpinEaRange( IrpContext, &EaHeaderRange ); 2066 FatUnpinEaRange( IrpContext, &EaOffsetRange ); 2067 FatUnpinEaRange( IrpContext, &EaSetRange ); 2068 2069 // 2070 // Uninitialize any initialized Mcbs 2071 // 2072 2073 if (UnwindInitializeDataMcb) { 2074 2075 FsRtlUninitializeLargeMcb( &DataMcb ); 2076 } 2077 2078 if (UnwindInitializeTailMcb) { 2079 2080 FsRtlUninitializeLargeMcb( &TailMcb ); 2081 } 2082 2083 DebugTrace(-1, Dbg, "FatDeleteEaSet -> Exit\n", 0); 2084 } _SEH2_END; 2085 2086 return; 2087 } 2088 2089 2090 2091 _Requires_lock_held_(_Global_critical_region_) 2092 VOID 2093 FatAddEaSet ( 2094 IN PIRP_CONTEXT IrpContext, 2095 IN PVCB Vcb, 2096 IN ULONG EaSetLength, 2097 IN PBCB EaBcb, 2098 OUT PDIRENT EaDirent, 2099 OUT PUSHORT EaHandle, 2100 OUT PEA_RANGE EaSetRange 2101 ) 2102 2103 /*++ 2104 2105 Routine Description: 2106 2107 This routine will add the necessary clusters to support a new 2108 Ea set of the given size. This is done by splicing a chain of 2109 clusters into the existing Ea file. An Ea index is assigned to 2110 this new chain and the Ea base and offset tables are updated to 2111 include this new handle. This routine also pins the added 2112 clusters and returns their address and a Bcb. 2113 2114 The caller of this function will have verified that the Ea file 2115 exists and that the Vcb field points to an initialized cache file. 2116 The caller will already have gained exclusive access to the 2117 EaFcb. 2118 2119 Arguments: 2120 2121 Vcb - Supplies the Vcb to fill in. 2122 2123 EaSetLength - The number of bytes needed to contain the Ea set. This 2124 routine will round this up the next cluster size. 2125 2126 EaBcb - Supplies a pointer to the Bcb for the Ea dirent. 2127 2128 EaDirent - Supplies a pointer to the dirent for the Ea file. 2129 2130 EaHandle - Supplies the address to store the ea index generated here. 2131 2132 EaSetRange - This is the structure that describes new range in the Ea file. 2133 2134 Return Value: 2135 2136 None. 2137 2138 --*/ 2139 2140 { 2141 ULONG BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster; 2142 2143 EA_RANGE EaHeaderRange; 2144 USHORT EaHeaderIndex; 2145 PEA_FILE_HEADER EaHeader; 2146 2147 EA_RANGE EaOffsetRange; 2148 ULONG EaNewOffsetVbo = 0; 2149 USHORT EaOffsetIndex; 2150 ULONG EaOffsetTableSize; 2151 PUSHORT EaOffsetTable; 2152 2153 ULONG EaSetClusterOffset; 2154 ULONG EaSetVbo = 0; 2155 USHORT EaSetClusterCount; 2156 PEA_SET_HEADER EaSet; 2157 2158 PFILE_OBJECT VirtualEaFile = Vcb->VirtualEaFile; 2159 PFCB EaFcb = Vcb->EaFcb; 2160 2161 LARGE_MCB EaSetMcb; 2162 BOOLEAN UnwindInitializedEaSetMcb = FALSE; 2163 BOOLEAN UnwindAllocatedNewAllocation = FALSE; 2164 BOOLEAN UnwindMergedNewEaSet = FALSE; 2165 2166 LARGE_MCB EaOffsetMcb; 2167 BOOLEAN UnwindInitializedOffsetMcb = FALSE; 2168 BOOLEAN UnwindSplitNewAllocation = FALSE; 2169 BOOLEAN UnwindMergedNewOffset = FALSE; 2170 2171 LARGE_MCB EaTailMcb; 2172 BOOLEAN UnwindInitializedTailMcb = FALSE; 2173 BOOLEAN UnwindSplitTail = FALSE; 2174 BOOLEAN UnwindMergedTail = FALSE; 2175 2176 LARGE_MCB EaInitialEaMcb; 2177 BOOLEAN UnwindInitializedInitialEaMcb = FALSE; 2178 BOOLEAN UnwindSplitInitialEa = FALSE; 2179 BOOLEAN UnwindMergedInitialEa = FALSE; 2180 2181 USHORT NewEaIndex; 2182 PUSHORT NextEaOffset; 2183 2184 ULONG NewAllocation; 2185 LARGE_INTEGER FileOffset; 2186 ULONG Count; 2187 2188 ULONG UnwindPrevFileSize = 0; 2189 BOOLEAN UnwindCacheValues = FALSE; 2190 2191 BOOLEAN TailExists = FALSE; 2192 BOOLEAN AddedOffsetTableCluster = FALSE; 2193 BOOLEAN UnwindPurgeCacheMap = FALSE; 2194 2195 PAGED_CODE(); 2196 2197 DebugTrace(+1, Dbg, "FatAddEaSet\n", 0); 2198 DebugTrace( 0, Dbg, " Vcb = %p\n", Vcb); 2199 DebugTrace( 0, Dbg, " EaSetLength = %ul\n", EaSetLength ); 2200 2201 // 2202 // Zero the Ea range structures. 2203 // 2204 2205 RtlZeroMemory( &EaHeaderRange, sizeof( EA_RANGE )); 2206 RtlZeroMemory( &EaOffsetRange, sizeof( EA_RANGE )); 2207 2208 // 2209 // Use a try statement to facilitate cleanup. 2210 // 2211 2212 _SEH2_TRY { 2213 2214 // 2215 // Pin down the file header. 2216 // 2217 2218 FatPinEaRange( IrpContext, 2219 VirtualEaFile, 2220 EaFcb, 2221 &EaHeaderRange, 2222 0, 2223 sizeof( EA_FILE_HEADER ), 2224 STATUS_DATA_ERROR ); 2225 2226 EaHeader = (PEA_FILE_HEADER) EaHeaderRange.Data; 2227 2228 // 2229 // Compute the size of the offset table. 2230 // 2231 2232 EaNewOffsetVbo = EaHeader->EaBaseTable[0] << Vcb->AllocationSupport.LogOfBytesPerCluster; 2233 EaOffsetTableSize = EaNewOffsetVbo - sizeof( EA_FILE_HEADER ); 2234 2235 // 2236 // Pin down the entire offset table. 2237 // 2238 2239 FatPinEaRange( IrpContext, 2240 VirtualEaFile, 2241 EaFcb, 2242 &EaOffsetRange, 2243 sizeof( EA_FILE_HEADER ), 2244 EaOffsetTableSize, 2245 STATUS_DATA_ERROR ); 2246 2247 // 2248 // We now look for a valid handle out of the existing offset table. 2249 // We start at the last entry and walk backwards. We stop at the 2250 // first unused handle which is preceded by a used handle (or handle 2251 // 1). 2252 // 2253 // As we walk backwards, we need to remember the file offset of the 2254 // cluster which will follow the clusters we add. We initially 2255 // remember the end of the file. If the end of the offset table 2256 // consists of a string of used handles, we remember the offset of 2257 // the handle prior to the transition from used to unused handles. 2258 // 2259 2260 EaSetClusterOffset = EaFcb->Header.FileSize.LowPart 2261 >> Vcb->AllocationSupport.LogOfBytesPerCluster; 2262 2263 NewEaIndex = (USHORT) ((EaOffsetTableSize >> 1) - 1); 2264 2265 NextEaOffset = (PUSHORT) EaOffsetRange.Data + NewEaIndex; 2266 2267 // 2268 // Walk through the used handles at the end of the offset table. 2269 // 2270 2271 if (*NextEaOffset != UNUSED_EA_HANDLE) { 2272 2273 while (NewEaIndex != 0) { 2274 2275 if (*(NextEaOffset - 1) == UNUSED_EA_HANDLE) { 2276 2277 // 2278 // If the handle is 1, we take no action. Otherwise 2279 // we save the cluster offset of the current handle 2280 // knowing we will use a previous handle and insert 2281 // a chain of clusters. 2282 // 2283 2284 if (NewEaIndex != 1) { 2285 2286 EaSetClusterOffset = *NextEaOffset 2287 + EaHeader->EaBaseTable[NewEaIndex >> 7]; 2288 2289 TailExists = TRUE; 2290 } 2291 2292 NewEaIndex--; 2293 NextEaOffset--; 2294 2295 break; 2296 } 2297 2298 NewEaIndex--; 2299 NextEaOffset--; 2300 } 2301 } 2302 2303 // 2304 // Walk through looking for the first unused handle in a string 2305 // of unused handles. 2306 // 2307 2308 while (NewEaIndex) { 2309 2310 if (*(NextEaOffset - 1) != UNUSED_EA_HANDLE) { 2311 2312 break; 2313 } 2314 2315 NextEaOffset--; 2316 NewEaIndex--; 2317 } 2318 2319 // 2320 // If the handle is zero, we do a special test to see if handle 1 2321 // is available. Otherwise we will use the first handle of a new 2322 // cluster. A non-zero handle now indicates that a handle was found 2323 // in an existing offset table cluster. 2324 // 2325 2326 if (NewEaIndex == 0) { 2327 2328 if (*(NextEaOffset + 1) == UNUSED_EA_HANDLE) { 2329 2330 NewEaIndex = 1; 2331 2332 } else { 2333 2334 NewEaIndex = (USHORT) EaOffsetTableSize >> 1; 2335 AddedOffsetTableCluster = TRUE; 2336 } 2337 } 2338 2339 // 2340 // If the Ea index is outside the legal range then raise an 2341 // exception. 2342 // 2343 2344 if (NewEaIndex > MAX_EA_HANDLE) { 2345 2346 DebugTrace(-1, Dbg, 2347 "FatAddEaSet: Illegal handle value for new handle\n", 0); 2348 2349 FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); 2350 } 2351 2352 // 2353 // Compute the base and offset indexes. 2354 // 2355 2356 EaHeaderIndex = NewEaIndex >> 7; 2357 EaOffsetIndex = NewEaIndex & (MAX_EA_OFFSET_INDEX - 1); 2358 2359 // 2360 // Compute the byte offset of the new ea data in the file. 2361 // 2362 2363 EaSetVbo = EaSetClusterOffset << Vcb->AllocationSupport.LogOfBytesPerCluster; 2364 2365 // 2366 // Allocate all the required disk space together to insure this 2367 // operation is atomic. We don't want to allocate one block 2368 // of disk space and then fail on a second allocation. 2369 // 2370 2371 EaSetLength = (EaSetLength + BytesPerCluster - 1) 2372 & ~(BytesPerCluster - 1); 2373 2374 NewAllocation = EaSetLength 2375 + (AddedOffsetTableCluster ? BytesPerCluster : 0); 2376 2377 // 2378 // Verify that adding these clusters will not grow the Ea file 2379 // beyond its legal value. The maximum number of clusters is 2380 // 2^16 since the Ea sets are referenced by a 16 bit cluster 2381 // offset value. 2382 // 2383 2384 if ((ULONG) ((0x0000FFFF << Vcb->AllocationSupport.LogOfBytesPerCluster) 2385 - EaFcb->Header.FileSize.LowPart) 2386 < NewAllocation) { 2387 2388 DebugTrace(-1, Dbg, 2389 "FatAddEaSet: New Ea file size is too large\n", 0); 2390 2391 FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); 2392 } 2393 2394 FsRtlInitializeLargeMcb( &EaSetMcb, PagedPool ); 2395 2396 UnwindInitializedEaSetMcb = TRUE; 2397 2398 FatAllocateDiskSpace( IrpContext, 2399 Vcb, 2400 0, 2401 &NewAllocation, 2402 FALSE, 2403 &EaSetMcb ); 2404 2405 UnwindAllocatedNewAllocation = TRUE; 2406 2407 EaSetClusterCount = (USHORT) (EaSetLength >> Vcb->AllocationSupport.LogOfBytesPerCluster); 2408 2409 if (AddedOffsetTableCluster) { 2410 2411 FsRtlInitializeLargeMcb( &EaOffsetMcb, PagedPool ); 2412 2413 UnwindInitializedOffsetMcb = TRUE; 2414 2415 FatSplitAllocation( IrpContext, 2416 Vcb, 2417 &EaSetMcb, 2418 EaSetLength, 2419 &EaOffsetMcb ); 2420 2421 UnwindSplitNewAllocation = TRUE; 2422 } 2423 2424 FatUnpinEaRange( IrpContext, &EaHeaderRange ); 2425 FatUnpinEaRange( IrpContext, &EaOffsetRange ); 2426 2427 if (AddedOffsetTableCluster) { 2428 2429 FileOffset.LowPart = EaNewOffsetVbo; 2430 2431 } else { 2432 2433 FileOffset.LowPart = EaSetVbo; 2434 } 2435 2436 FileOffset.HighPart = 0; 2437 2438 // 2439 // Round the cache map down to a system page boundary. 2440 // 2441 2442 FileOffset.LowPart &= ~(PAGE_SIZE - 1); 2443 2444 { 2445 IO_STATUS_BLOCK Iosb; 2446 ULONG PurgeCount = 5; 2447 2448 while (--PurgeCount) { 2449 2450 Iosb.Status = STATUS_SUCCESS; 2451 2452 CcFlushCache( VirtualEaFile->SectionObjectPointer, 2453 NULL, 2454 0, 2455 &Iosb ); 2456 2457 NT_ASSERT( Iosb.Status == STATUS_SUCCESS ); 2458 2459 // 2460 // We do not have to worry about a lazy writer firing in parallel 2461 // with our CcFlushCache since we have the EaFcb exclusive. Thus 2462 // we know all data is out. 2463 // 2464 2465 // 2466 // We throw the unwanted pages out of the cache and then 2467 // truncate the Ea File for the new size. 2468 // 2469 // 2470 2471 if (CcPurgeCacheSection( VirtualEaFile->SectionObjectPointer, 2472 &FileOffset, 2473 0, 2474 FALSE )) { 2475 2476 break; 2477 } 2478 } 2479 2480 if (!PurgeCount) { 2481 2482 FatRaiseStatus( IrpContext, STATUS_UNABLE_TO_DELETE_SECTION ); 2483 } 2484 } 2485 2486 UnwindPurgeCacheMap = TRUE; 2487 2488 FileOffset.LowPart = EaFcb->Header.FileSize.LowPart + NewAllocation; 2489 2490 // 2491 // If there is a tail to the file, then we initialize an Mcb 2492 // for the file section and split the tail from the file. 2493 // 2494 2495 if (TailExists) { 2496 2497 FsRtlInitializeLargeMcb( &EaTailMcb, PagedPool ); 2498 2499 UnwindInitializedTailMcb = TRUE; 2500 2501 FatSplitAllocation( IrpContext, 2502 Vcb, 2503 &EaFcb->Mcb, 2504 EaSetVbo, 2505 &EaTailMcb ); 2506 2507 UnwindSplitTail = TRUE; 2508 } 2509 2510 // 2511 // If there is an initial section of ea data, we initialize an 2512 // Mcb for that section. 2513 // 2514 2515 if (AddedOffsetTableCluster 2516 && EaSetVbo != EaNewOffsetVbo) { 2517 2518 FsRtlInitializeLargeMcb( &EaInitialEaMcb, PagedPool ); 2519 2520 UnwindInitializedInitialEaMcb = TRUE; 2521 2522 FatSplitAllocation( IrpContext, 2523 Vcb, 2524 &EaFcb->Mcb, 2525 EaNewOffsetVbo, 2526 &EaInitialEaMcb ); 2527 2528 UnwindSplitInitialEa = TRUE; 2529 } 2530 2531 // 2532 // We have now split the new file allocation into the new 2533 // ea set and possibly a new offset table. 2534 // 2535 // We have also split the existing file data into a file 2536 // header, an initial section of ea data and the tail of the 2537 // file. These last 2 may not exist. 2538 // 2539 // Each section is described by an Mcb. 2540 // 2541 2542 // 2543 // Merge the new offset information if it exists. 2544 // 2545 2546 if (AddedOffsetTableCluster) { 2547 2548 FatMergeAllocation( IrpContext, 2549 Vcb, 2550 &EaFcb->Mcb, 2551 &EaOffsetMcb ); 2552 2553 FsRtlUninitializeLargeMcb( &EaOffsetMcb ); 2554 FsRtlInitializeLargeMcb( &EaOffsetMcb, PagedPool ); 2555 2556 UnwindMergedNewOffset = TRUE; 2557 } 2558 2559 // 2560 // Merge the existing initial ea data if it exists. 2561 // 2562 2563 if (UnwindInitializedInitialEaMcb) { 2564 2565 FatMergeAllocation( IrpContext, 2566 Vcb, 2567 &EaFcb->Mcb, 2568 &EaInitialEaMcb ); 2569 2570 FsRtlUninitializeLargeMcb( &EaInitialEaMcb ); 2571 FsRtlInitializeLargeMcb( &EaInitialEaMcb, PagedPool ); 2572 2573 UnwindMergedInitialEa = TRUE; 2574 } 2575 2576 // 2577 // We modify the offset of the new ea set by one cluster if 2578 // we added one to the offset table. 2579 // 2580 2581 if (AddedOffsetTableCluster) { 2582 2583 EaSetClusterOffset += 1; 2584 EaSetVbo += BytesPerCluster; 2585 } 2586 2587 // 2588 // Merge the new ea set. 2589 // 2590 2591 FatMergeAllocation( IrpContext, 2592 Vcb, 2593 &EaFcb->Mcb, 2594 &EaSetMcb ); 2595 2596 FsRtlUninitializeLargeMcb( &EaSetMcb ); 2597 FsRtlInitializeLargeMcb( &EaSetMcb, PagedPool ); 2598 2599 UnwindMergedNewEaSet = TRUE; 2600 2601 // 2602 // Merge the tail if it exists. 2603 // 2604 2605 if (UnwindInitializedTailMcb) { 2606 2607 FatMergeAllocation( IrpContext, 2608 Vcb, 2609 &EaFcb->Mcb, 2610 &EaTailMcb ); 2611 2612 FsRtlUninitializeLargeMcb( &EaTailMcb ); 2613 FsRtlInitializeLargeMcb( &EaTailMcb, PagedPool ); 2614 2615 UnwindMergedTail = TRUE; 2616 } 2617 2618 // 2619 // If we added a new cluster for the offset table, we need to 2620 // lock the entire cluster down and initialize all the handles to 2621 // the unused state except the first one. 2622 // 2623 2624 // 2625 // Update the Fcb information. 2626 // 2627 2628 UnwindPrevFileSize = EaFcb->Header.FileSize.LowPart; 2629 2630 EaFcb->Header.FileSize.LowPart += NewAllocation; 2631 EaFcb->Header.AllocationSize = EaFcb->Header.FileSize; 2632 EaDirent->FileSize = EaFcb->Header.FileSize.LowPart; 2633 2634 FatSetDirtyBcb( IrpContext, EaBcb, Vcb, TRUE ); 2635 2636 // 2637 // Let Mm and Cc know the new file sizes. 2638 // 2639 2640 CcSetFileSizes( VirtualEaFile, 2641 (PCC_FILE_SIZES)&EaFcb->Header.AllocationSize ); 2642 2643 UnwindCacheValues = TRUE; 2644 2645 // 2646 // Pin down the file header. 2647 // 2648 2649 RtlZeroMemory( &EaHeaderRange, sizeof( EA_RANGE )); 2650 2651 FatPinEaRange( IrpContext, 2652 VirtualEaFile, 2653 EaFcb, 2654 &EaHeaderRange, 2655 0, 2656 sizeof( EA_FILE_HEADER ), 2657 STATUS_DATA_ERROR ); 2658 2659 EaHeader = (PEA_FILE_HEADER) EaHeaderRange.Data; 2660 2661 // 2662 // Pin down the entire offset table. 2663 // 2664 2665 2666 RtlZeroMemory( &EaOffsetRange, sizeof( EA_RANGE )); 2667 2668 FatPinEaRange( IrpContext, 2669 VirtualEaFile, 2670 EaFcb, 2671 &EaOffsetRange, 2672 sizeof( EA_FILE_HEADER ) + (((ULONGLONG)NewEaIndex >> 7) << 8), 2673 sizeof( EA_OFF_TABLE ), 2674 STATUS_DATA_ERROR ); 2675 2676 EaOffsetTable = (PUSHORT) EaOffsetRange.Data; 2677 2678 // 2679 // Pin the Ea set header for the added clusters and initialize 2680 // the fields of interest. These are the signature field, the 2681 // owning handle field, the need Ea field and the cbList field. 2682 // Also mark the data as dirty. 2683 // 2684 2685 // 2686 // Pin the ea set. 2687 // 2688 2689 FatPinEaRange( IrpContext, 2690 VirtualEaFile, 2691 EaFcb, 2692 EaSetRange, 2693 EaSetVbo, 2694 EaSetLength, 2695 STATUS_DATA_ERROR ); 2696 2697 EaSet = (PEA_SET_HEADER) EaSetRange->Data; 2698 2699 EaSet->Signature = EA_SET_SIGNATURE; 2700 EaSet->OwnEaHandle = NewEaIndex; 2701 2702 FatMarkEaRangeDirty( IrpContext, VirtualEaFile, EaSetRange ); 2703 2704 // 2705 // Update the Ea base and offset tables. For the Ea base table, 2706 // all subsequent index values must be incremented by the number 2707 // of clusters added. 2708 // 2709 // For the entries in the relevant Ea offset table, all entries 2710 // after this index must also be increased by the number of 2711 // clusters added. 2712 // 2713 // If we added another cluster to the offset table, then we increment 2714 // all the base table values by 1. 2715 // 2716 2717 Count = MAX_EA_BASE_INDEX - EaHeaderIndex - 1; 2718 2719 NextEaOffset = &EaHeader->EaBaseTable[EaHeaderIndex + 1]; 2720 2721 while (Count--) { 2722 2723 *(NextEaOffset++) += EaSetClusterCount; 2724 } 2725 2726 if (AddedOffsetTableCluster) { 2727 2728 Count = MAX_EA_BASE_INDEX; 2729 2730 NextEaOffset = &EaHeader->EaBaseTable[0]; 2731 2732 while (Count--) { 2733 2734 *(NextEaOffset++) += 1; 2735 } 2736 } 2737 2738 FatMarkEaRangeDirty( IrpContext, VirtualEaFile, &EaHeaderRange ); 2739 2740 // 2741 // If we added an offset table cluster, we need to initialize 2742 // the handles to unused. 2743 // 2744 2745 if (AddedOffsetTableCluster) { 2746 2747 Count = (BytesPerCluster >> 1) - 1; 2748 NextEaOffset = EaOffsetTable; 2749 2750 *NextEaOffset++ = 0; 2751 2752 while (Count--) { 2753 2754 *NextEaOffset++ = UNUSED_EA_HANDLE; 2755 } 2756 } 2757 2758 // 2759 // We need to compute the offset of the added Ea set clusters 2760 // from their base. 2761 // 2762 2763 NextEaOffset = EaOffsetTable + EaOffsetIndex; 2764 2765 *NextEaOffset++ = (USHORT) (EaSetClusterOffset 2766 - EaHeader->EaBaseTable[EaHeaderIndex]); 2767 2768 Count = MAX_EA_OFFSET_INDEX - EaOffsetIndex - 1; 2769 2770 while (Count--) { 2771 2772 if (*NextEaOffset != UNUSED_EA_HANDLE) { 2773 2774 *NextEaOffset += EaSetClusterCount; 2775 } 2776 2777 NextEaOffset++; 2778 } 2779 2780 FatMarkEaRangeDirty( IrpContext, VirtualEaFile, &EaOffsetRange ); 2781 2782 // 2783 // Update the callers parameters. 2784 // 2785 2786 *EaHandle = NewEaIndex; 2787 2788 DebugTrace(0, Dbg, "FatAddEaSet: Return values\n", 0); 2789 2790 DebugTrace(0, Dbg, "FatAddEaSet: New Handle -> %x\n", 2791 *EaHandle); 2792 2793 } _SEH2_FINALLY { 2794 2795 DebugUnwind( FatAddEaSet ); 2796 2797 // 2798 // Handle cleanup for abnormal termination only if we allocated 2799 // disk space for the new ea set. 2800 // 2801 2802 if (_SEH2_AbnormalTermination() && UnwindAllocatedNewAllocation) { 2803 2804 // 2805 // If we modified the Ea dirent or Fcb, recover the previous 2806 // values. Even though we are decreasing FileSize here, we 2807 // don't need to synchronize to synchronize with paging Io 2808 // because there was no dirty data generated in the new allocation. 2809 // 2810 2811 if (UnwindPrevFileSize) { 2812 2813 EaFcb->Header.FileSize.LowPart = UnwindPrevFileSize; 2814 EaFcb->Header.AllocationSize.LowPart = UnwindPrevFileSize; 2815 EaDirent->FileSize = UnwindPrevFileSize; 2816 2817 if (UnwindCacheValues) { 2818 2819 CcSetFileSizes( VirtualEaFile, 2820 (PCC_FILE_SIZES)&EaFcb->Header.AllocationSize ); 2821 } 2822 } 2823 2824 // 2825 // If we merged the tail then split it off. 2826 // 2827 2828 if (UnwindMergedTail) { 2829 2830 VBO NewTailPosition; 2831 2832 NewTailPosition = EaSetVbo + EaSetLength; 2833 2834 FatSplitAllocation( IrpContext, 2835 Vcb, 2836 &EaFcb->Mcb, 2837 NewTailPosition, 2838 &EaTailMcb ); 2839 } 2840 2841 // 2842 // If we merged the new ea data then split it out. 2843 // 2844 2845 if (UnwindMergedNewEaSet) { 2846 2847 FatSplitAllocation( IrpContext, 2848 Vcb, 2849 &EaFcb->Mcb, 2850 EaSetVbo, 2851 &EaSetMcb ); 2852 } 2853 2854 // 2855 // If we merged the initial ea data then split it out. 2856 // 2857 2858 if (UnwindMergedInitialEa) { 2859 2860 FatSplitAllocation( IrpContext, 2861 Vcb, 2862 &EaFcb->Mcb, 2863 EaNewOffsetVbo + BytesPerCluster, 2864 &EaInitialEaMcb ); 2865 } 2866 2867 // 2868 // If we added a new offset cluster, then split it out. 2869 // 2870 2871 if (UnwindMergedNewOffset) { 2872 2873 FatSplitAllocation( IrpContext, 2874 Vcb, 2875 &EaFcb->Mcb, 2876 EaNewOffsetVbo, 2877 &EaOffsetMcb ); 2878 } 2879 2880 // 2881 // If there is an initial ea section prior to the new section, merge 2882 // it with the rest of the file. 2883 // 2884 2885 if (UnwindSplitInitialEa) { 2886 2887 FatMergeAllocation( IrpContext, Vcb, &EaFcb->Mcb, &EaInitialEaMcb ); 2888 } 2889 2890 // 2891 // If there is a file tail split off, merge it with the 2892 // rest of the file. 2893 // 2894 2895 if (UnwindSplitTail) { 2896 2897 FatMergeAllocation( IrpContext, Vcb, &EaFcb->Mcb, &EaTailMcb ); 2898 } 2899 2900 // 2901 // If we modified the cache initialization for the ea file, 2902 // then throw away the ea file object. 2903 // 2904 2905 if (UnwindPurgeCacheMap) { 2906 2907 Vcb->VirtualEaFile = NULL; 2908 ObDereferenceObject( VirtualEaFile ); 2909 } 2910 2911 // 2912 // If we split the allocation, then deallocate the block for 2913 // the new offset information. 2914 // 2915 2916 if (UnwindSplitNewAllocation) { 2917 2918 FatDeallocateDiskSpace( IrpContext, Vcb, &EaOffsetMcb, FALSE ); 2919 } 2920 2921 // 2922 // Deallocate the disk space. 2923 // 2924 2925 FatDeallocateDiskSpace( IrpContext, Vcb, &EaSetMcb, FALSE ); 2926 } 2927 2928 // 2929 // Unpin the Ea ranges. 2930 // 2931 2932 FatUnpinEaRange( IrpContext, &EaHeaderRange ); 2933 FatUnpinEaRange( IrpContext, &EaOffsetRange ); 2934 2935 // 2936 // Uninitialize any local Mcbs 2937 // 2938 2939 if (UnwindInitializedEaSetMcb) { 2940 2941 FsRtlUninitializeLargeMcb( &EaSetMcb ); 2942 } 2943 2944 if (UnwindInitializedOffsetMcb) { 2945 2946 FsRtlUninitializeLargeMcb( &EaOffsetMcb ); 2947 } 2948 2949 if (UnwindInitializedTailMcb) { 2950 2951 FsRtlUninitializeLargeMcb( &EaTailMcb ); 2952 } 2953 2954 if (UnwindInitializedInitialEaMcb) { 2955 2956 FsRtlUninitializeLargeMcb( &EaInitialEaMcb ); 2957 } 2958 2959 DebugTrace(-1, Dbg, "FatAddEaSet -> Exit\n", 0); 2960 } _SEH2_END; 2961 2962 return; 2963 } 2964 2965 2966 VOID 2967 FatAppendPackedEa ( 2968 IN PIRP_CONTEXT IrpContext, 2969 IN OUT PEA_SET_HEADER *EaSetHeader, 2970 IN OUT PULONG PackedEasLength, 2971 IN OUT PULONG AllocationLength, 2972 IN PFILE_FULL_EA_INFORMATION FullEa, 2973 IN ULONG BytesPerCluster 2974 ) 2975 2976 /*++ 2977 2978 Routine Description: 2979 2980 This routine appends a new packed ea onto an existing packed ea list, 2981 it also will allocate/dealloate pool as necessary to hold the ea list. 2982 2983 Arguments: 2984 2985 EaSetHeader - Supplies the address to store the pointer to pool memory 2986 which contains the Ea list for a file. 2987 2988 PackedEasLength - Supplies the length of the actual Ea data. The 2989 new Ea data will be appended at this point. 2990 2991 AllocationLength - Supplies the allocated length available for Ea 2992 data. 2993 2994 FullEa - Supplies a pointer to the new full ea that is to be appended 2995 (in packed form) to the packed ea list. 2996 2997 BytesPerCluster - Number of bytes per cluster on this volume. 2998 2999 NOTE: The EaSetHeader refers to the entire block of Ea data for a 3000 file. This includes the Ea's and their values as well as the 3001 header information. The PackedEasLength and AllocationLength 3002 parameters refer to the name/value pairs only. 3003 3004 Return Value: 3005 3006 None. 3007 3008 --*/ 3009 3010 { 3011 ULONG PackedEaSize; 3012 PPACKED_EA ThisPackedEa; 3013 OEM_STRING EaName; 3014 3015 PAGED_CODE(); 3016 3017 DebugTrace(+1, Dbg, "FatAppendPackedEa...\n", 0); 3018 3019 // 3020 // As a quick check see if the computed packed ea size plus the 3021 // current packed ea list size will overflow the buffer. Full Ea and 3022 // packed Ea only differ by 4 in their size 3023 // 3024 3025 PackedEaSize = SizeOfFullEa( FullEa ) - 4; 3026 3027 if ( PackedEaSize + *PackedEasLength > *AllocationLength ) { 3028 3029 // 3030 // We will overflow our current work buffer so allocate a larger 3031 // one and copy over the current buffer 3032 // 3033 3034 PVOID Temp; 3035 ULONG NewAllocationSize; 3036 ULONG OldAllocationSize; 3037 3038 DebugTrace(0, Dbg, "Allocate a new ea list buffer\n", 0); 3039 3040 // 3041 // Compute a new size and allocate space. Always increase the 3042 // allocation in cluster increments. 3043 // 3044 3045 NewAllocationSize = (SIZE_OF_EA_SET_HEADER 3046 + PackedEaSize 3047 + *PackedEasLength 3048 + BytesPerCluster - 1) 3049 & ~(BytesPerCluster - 1); 3050 3051 Temp = FsRtlAllocatePoolWithTag( PagedPool, 3052 NewAllocationSize, 3053 TAG_EA_SET_HEADER ); 3054 3055 // 3056 // Move over the existing ea list, and deallocate the old one 3057 // 3058 3059 RtlCopyMemory( Temp, 3060 *EaSetHeader, 3061 OldAllocationSize = *AllocationLength 3062 + SIZE_OF_EA_SET_HEADER ); 3063 3064 ExFreePool( *EaSetHeader ); 3065 3066 // 3067 // Set up so we will use the new packed ea list 3068 // 3069 3070 *EaSetHeader = Temp; 3071 3072 // 3073 // Zero out the added memory. 3074 // 3075 3076 RtlZeroMemory( &(*EaSetHeader)->PackedEas[*AllocationLength], 3077 NewAllocationSize - OldAllocationSize ); 3078 3079 *AllocationLength = NewAllocationSize - SIZE_OF_EA_SET_HEADER; 3080 } 3081 3082 // 3083 // Determine if we need to increment our need ea changes count 3084 // 3085 3086 if ( FlagOn(FullEa->Flags, FILE_NEED_EA )) { 3087 3088 // 3089 // The NeedEaCount field is long aligned so we will write 3090 // directly to it. 3091 // 3092 3093 (*EaSetHeader)->NeedEaCount++; 3094 } 3095 3096 // 3097 // Now copy over the ea, full ea's and packed ea are identical except 3098 // that full ea also have a next ea offset that we skip over 3099 // 3100 // Before: 3101 // UsedSize Allocated 3102 // | | 3103 // V V 3104 // +xxxxxxxx+-----------------------------+ 3105 // 3106 // After: 3107 // UsedSize Allocated 3108 // | | 3109 // V V 3110 // +xxxxxxxx+yyyyyyyyyyyyyyyy+------------+ 3111 // 3112 3113 ThisPackedEa = (PPACKED_EA) (RtlOffsetToPointer( (*EaSetHeader)->PackedEas, 3114 *PackedEasLength )); 3115 3116 RtlCopyMemory( ThisPackedEa, 3117 (PUCHAR) FullEa + 4, 3118 PackedEaSize ); 3119 3120 // 3121 // Now convert the name to uppercase. 3122 // 3123 3124 EaName.MaximumLength = EaName.Length = FullEa->EaNameLength; 3125 EaName.Buffer = ThisPackedEa->EaName; 3126 3127 FatUpcaseEaName( IrpContext, &EaName, &EaName ); 3128 3129 // 3130 // Increment the used size in the packed ea list structure 3131 // 3132 3133 *PackedEasLength += PackedEaSize; 3134 3135 // 3136 // And return to our caller 3137 // 3138 3139 DebugTrace(-1, Dbg, "FatAppendPackedEa -> VOID\n", 0); 3140 3141 UNREFERENCED_PARAMETER( IrpContext ); 3142 3143 return; 3144 } 3145 3146 3147 VOID 3148 FatDeletePackedEa ( 3149 IN PIRP_CONTEXT IrpContext, 3150 IN OUT PEA_SET_HEADER EaSetHeader, 3151 IN OUT PULONG PackedEasLength, 3152 IN ULONG Offset 3153 ) 3154 3155 /*++ 3156 3157 Routine Description: 3158 3159 This routine deletes an individual packed ea from the supplied 3160 packed ea list. 3161 3162 Arguments: 3163 3164 EaSetHeader - Supplies the address to store the pointer to pool memory 3165 which contains the Ea list for a file. 3166 3167 PackedEasLength - Supplies the length of the actual Ea data. The 3168 new Ea data will be appended at this point. 3169 3170 Offset - Supplies the offset to the individual ea in the list to delete 3171 3172 NOTE: The EaSetHeader refers to the entire block of Ea data for a 3173 file. This includes the Ea's and their values as well as the 3174 header information. The PackedEasLength parameter refer to the 3175 name/value pairs only. 3176 3177 Return Value: 3178 3179 None. 3180 3181 --*/ 3182 3183 { 3184 PPACKED_EA PackedEa; 3185 ULONG PackedEaSize; 3186 3187 PAGED_CODE(); 3188 3189 DebugTrace(+1, Dbg, "FatDeletePackedEa, Offset = %08lx\n", Offset); 3190 3191 // 3192 // Get a reference to the packed ea and figure out its size 3193 // 3194 3195 PackedEa = (PPACKED_EA) (&EaSetHeader->PackedEas[Offset]); 3196 3197 SizeOfPackedEa( PackedEa, &PackedEaSize ); 3198 3199 // 3200 // Determine if we need to decrement our need ea changes count 3201 // 3202 3203 if (FlagOn(PackedEa->Flags, EA_NEED_EA_FLAG)) { 3204 3205 EaSetHeader->NeedEaCount--; 3206 } 3207 3208 // 3209 // Shrink the ea list over the deleted ea. The amount to copy is the 3210 // total size of the ea list minus the offset to the end of the ea 3211 // we're deleting. 3212 // 3213 // Before: 3214 // Offset Offset+PackedEaSize UsedSize Allocated 3215 // | | | | 3216 // V V V V 3217 // +xxxxxxxx+yyyyyyyyyyyyyyyy+zzzzzzzzzzzzzzzzzz+------------+ 3218 // 3219 // After 3220 // Offset UsedSize Allocated 3221 // | | | 3222 // V V V 3223 // +xxxxxxxx+zzzzzzzzzzzzzzzzzz+-----------------------------+ 3224 // 3225 3226 RtlCopyMemory( PackedEa, 3227 (PUCHAR) PackedEa + PackedEaSize, 3228 *PackedEasLength - (Offset + PackedEaSize) ); 3229 3230 // 3231 // And zero out the remaing part of the ea list, to make things 3232 // nice and more robust 3233 // 3234 3235 RtlZeroMemory( &EaSetHeader->PackedEas[*PackedEasLength - PackedEaSize], 3236 PackedEaSize ); 3237 3238 // 3239 // Decrement the used size by the amount we just removed 3240 // 3241 3242 *PackedEasLength -= PackedEaSize; 3243 3244 // 3245 // And return to our caller 3246 // 3247 3248 DebugTrace(-1, Dbg, "FatDeletePackedEa -> VOID\n", 0); 3249 3250 UNREFERENCED_PARAMETER( IrpContext ); 3251 3252 return; 3253 } 3254 3255 3256 ULONG 3257 FatLocateNextEa ( 3258 IN PIRP_CONTEXT IrpContext, 3259 IN PPACKED_EA FirstPackedEa, 3260 IN ULONG PackedEasLength, 3261 IN ULONG PreviousOffset 3262 ) 3263 3264 /*++ 3265 3266 Routine Description: 3267 3268 This routine locates the offset for the next individual packed ea 3269 inside of a packed ea list, given the offset to a previous Ea. 3270 Instead of returing boolean to indicate if we've found the next one 3271 we let the return offset be so large that it overuns the used size 3272 of the packed ea list, and that way it's an easy construct to use 3273 in a for loop. 3274 3275 Arguments: 3276 3277 FirstPackedEa - Supplies a pointer to the packed ea list structure 3278 3279 PackedEasLength - Supplies the length of the packed ea list 3280 3281 PreviousOffset - Supplies the offset to a individual packed ea in the 3282 list 3283 3284 Return Value: 3285 3286 ULONG - The offset to the next ea in the list or 0xffffffff of one 3287 does not exist. 3288 3289 --*/ 3290 3291 { 3292 PPACKED_EA PackedEa; 3293 ULONG PackedEaSize; 3294 ULONG Offset; 3295 3296 PAGED_CODE(); 3297 3298 DebugTrace(+1, Dbg, "FatLocateNextEa, PreviousOffset = %08lx\n", 3299 PreviousOffset); 3300 3301 // 3302 // Make sure the previous offset is within the used size range 3303 // 3304 3305 if ( PreviousOffset >= PackedEasLength ) { 3306 3307 DebugTrace(-1, Dbg, "FatLocateNextEa -> 0xffffffff\n", 0); 3308 return 0xffffffff; 3309 } 3310 3311 // 3312 // Get a reference to the previous packed ea, and compute its size 3313 // 3314 3315 PackedEa = (PPACKED_EA) ((PUCHAR) FirstPackedEa + PreviousOffset ); 3316 SizeOfPackedEa( PackedEa, &PackedEaSize ); 3317 3318 // 3319 // Compute to the next ea 3320 // 3321 3322 Offset = PreviousOffset + PackedEaSize; 3323 3324 // 3325 // Now, if the new offset is beyond the ea size then we know 3326 // that there isn't one so, we return an offset of 0xffffffff. 3327 // otherwise we'll leave the new offset alone. 3328 // 3329 3330 if ( Offset >= PackedEasLength ) { 3331 3332 Offset = 0xffffffff; 3333 } 3334 3335 DebugTrace(-1, Dbg, "FatLocateNextEa -> %08lx\n", Offset); 3336 3337 UNREFERENCED_PARAMETER( IrpContext ); 3338 3339 return Offset; 3340 } 3341 3342 3343 BOOLEAN 3344 FatLocateEaByName ( 3345 IN PIRP_CONTEXT IrpContext, 3346 IN PPACKED_EA FirstPackedEa, 3347 IN ULONG PackedEasLength, 3348 IN POEM_STRING EaName, 3349 OUT PULONG Offset 3350 ) 3351 3352 /*++ 3353 3354 Routine Description: 3355 3356 This routine locates the offset for the next individual packed ea 3357 inside of a packed ea list, given the name of the ea to locate 3358 3359 Arguments: 3360 3361 FirstPackedEa - Supplies a pointer to the packed ea list structure 3362 3363 PackedEasLength - Supplies the length of the packed ea list 3364 3365 EaName - Supplies the name of the ea search for 3366 3367 Offset - Receives the offset to the located individual ea in the list 3368 if one exists. 3369 3370 Return Value: 3371 3372 BOOLEAN - TRUE if the named packed ea exists in the list and FALSE 3373 otherwise. 3374 3375 --*/ 3376 3377 { 3378 PPACKED_EA PackedEa; 3379 OEM_STRING Name; 3380 3381 PAGED_CODE(); 3382 3383 DebugTrace(+1, Dbg, "FatLocateEaByName, EaName = %Z\n", EaName); 3384 3385 // 3386 // For each packed ea in the list check its name against the 3387 // ea name we're searching for 3388 // 3389 3390 for ( *Offset = 0; 3391 *Offset < PackedEasLength; 3392 *Offset = FatLocateNextEa( IrpContext, 3393 FirstPackedEa, 3394 PackedEasLength, 3395 *Offset )) { 3396 3397 // 3398 // Reference the packed ea and get a string to its name 3399 // 3400 3401 PackedEa = (PPACKED_EA) ((PUCHAR) FirstPackedEa + *Offset); 3402 3403 Name.Buffer = &PackedEa->EaName[0]; 3404 Name.Length = PackedEa->EaNameLength; 3405 Name.MaximumLength = PackedEa->EaNameLength; 3406 3407 // 3408 // Compare the two strings, if they are equal then we've 3409 // found the caller's ea 3410 // 3411 3412 if ( RtlCompareString( EaName, &Name, TRUE ) == 0 ) { 3413 3414 DebugTrace(-1, Dbg, "FatLocateEaByName -> TRUE, *Offset = %08lx\n", *Offset); 3415 return TRUE; 3416 } 3417 } 3418 3419 // 3420 // We've exhausted the ea list without finding a match so return false 3421 // 3422 3423 DebugTrace(-1, Dbg, "FatLocateEaByName -> FALSE\n", 0); 3424 return FALSE; 3425 } 3426 3427 3428 BOOLEAN 3429 FatIsEaNameValid ( 3430 IN PIRP_CONTEXT IrpContext, 3431 IN OEM_STRING Name 3432 ) 3433 3434 /*++ 3435 3436 Routine Description: 3437 3438 This routine simple returns whether the specified file names conforms 3439 to the file system specific rules for legal Ea names. 3440 3441 For Ea names, the following rules apply: 3442 3443 A. An Ea name may not contain any of the following characters: 3444 3445 0x0000 - 0x001F \ / : * ? " < > | , + = [ ] ; 3446 3447 Arguments: 3448 3449 Name - Supllies the name to check. 3450 3451 Return Value: 3452 3453 BOOLEAN - TRUE if the name is legal, FALSE otherwise. 3454 3455 --*/ 3456 3457 { 3458 ULONG Index; 3459 3460 UCHAR Char; 3461 3462 PAGED_CODE(); 3463 3464 UNREFERENCED_PARAMETER( IrpContext ); 3465 3466 // 3467 // Empty names are not valid. 3468 // 3469 3470 if ( Name.Length == 0 ) { return FALSE; } 3471 3472 // 3473 // At this point we should only have a single name, which can't have 3474 // more than 254 characters 3475 // 3476 3477 if ( Name.Length > 254 ) { return FALSE; } 3478 3479 for ( Index = 0; Index < (ULONG)Name.Length; Index += 1 ) { 3480 3481 Char = Name.Buffer[ Index ]; 3482 3483 // 3484 // Skip over and Dbcs chacters 3485 // 3486 3487 if ( FsRtlIsLeadDbcsCharacter( Char ) ) { 3488 3489 NT_ASSERT( Index != (ULONG)(Name.Length - 1) ); 3490 3491 Index += 1; 3492 3493 continue; 3494 } 3495 3496 // 3497 // Make sure this character is legal, and if a wild card, that 3498 // wild cards are permissible. 3499 // 3500 3501 if ( !FsRtlIsAnsiCharacterLegalFat(Char, FALSE) ) { 3502 3503 return FALSE; 3504 } 3505 } 3506 3507 return TRUE; 3508 } 3509 3510 3511 VOID 3512 FatPinEaRange ( 3513 IN PIRP_CONTEXT IrpContext, 3514 IN PFILE_OBJECT VirtualEaFile, 3515 IN PFCB EaFcb, 3516 IN OUT PEA_RANGE EaRange, 3517 IN ULONG StartingVbo, 3518 IN ULONG Length, 3519 IN NTSTATUS ErrorStatus 3520 ) 3521 3522 /*++ 3523 3524 Routine Description: 3525 3526 This routine is called to pin a range within the Ea file. It will follow all the 3527 rules required by the cache manager so that we don't have overlapping pin operations. 3528 If the range being pinned spans a section then the desired data will be copied into 3529 an auxilary buffer. FatMarkEaRangeDirty will know whether to copy the data back 3530 into the cache or whether to simply mark the pinned data dirty. 3531 3532 Arguments: 3533 3534 VirtualEaFile - This is the stream file for the Ea file. 3535 3536 EaFcb - This is the Fcb for the Ea file. 3537 3538 EaRange - This is the Ea range structure for this request. 3539 3540 StartingVbo - This is the starting offset in the Ea file to read from. 3541 3542 Length - This is the length of the read. 3543 3544 ErrorStatus - This is the error status to use if we are reading outside 3545 of the file. 3546 3547 Return Value: 3548 3549 None. 3550 3551 --*/ 3552 3553 { 3554 LARGE_INTEGER LargeVbo; 3555 ULONG ByteCount; 3556 PBCB *NextBcb; 3557 PVOID Buffer; 3558 PCHAR DestinationBuffer = NULL; 3559 BOOLEAN FirstPage = TRUE; 3560 3561 PAGED_CODE(); 3562 3563 // 3564 // Verify that the entire read is contained within the Ea file. 3565 // 3566 3567 if (Length == 0 3568 || StartingVbo >= EaFcb->Header.AllocationSize.LowPart 3569 || (EaFcb->Header.AllocationSize.LowPart - StartingVbo) < Length) { 3570 3571 FatRaiseStatus( IrpContext, ErrorStatus ); 3572 } 3573 3574 // 3575 // If the read will span a section, the system addresses may not be contiguous. 3576 // Allocate a separate buffer in this case. 3577 // 3578 3579 if (((StartingVbo & (EA_SECTION_SIZE - 1)) + Length) > EA_SECTION_SIZE) { 3580 3581 EaRange->Data = FsRtlAllocatePoolWithTag( PagedPool, 3582 Length, 3583 TAG_EA_DATA ); 3584 EaRange->AuxilaryBuffer = TRUE; 3585 3586 DestinationBuffer = EaRange->Data; 3587 3588 } else { 3589 3590 // 3591 // PREfix correctly notes that if we don't decide here to have an aux buffer 3592 // and the flag is up in the EaRange, we'll party on random memory since 3593 // DestinationBuffer won't be set; however, this will never happen due to 3594 // initialization of ea ranges and the cleanup in UnpinEaRange. 3595 // 3596 3597 NT_ASSERT( EaRange->AuxilaryBuffer == FALSE ); 3598 } 3599 3600 3601 // 3602 // If the read will require more pages than our structure will hold then 3603 // allocate an auxilary buffer. We have to figure the number of pages 3604 // being requested so we have to include the page offset of the first page of 3605 // the request. 3606 // 3607 3608 EaRange->BcbChainLength = (USHORT) (((StartingVbo & (PAGE_SIZE - 1)) + Length + PAGE_SIZE - 1) / PAGE_SIZE); 3609 3610 if (EaRange->BcbChainLength > EA_BCB_ARRAY_SIZE) { 3611 3612 EaRange->BcbChain = FsRtlAllocatePoolWithTag( PagedPool, 3613 sizeof( PBCB ) * EaRange->BcbChainLength, 3614 TAG_BCB ); 3615 3616 RtlZeroMemory( EaRange->BcbChain, sizeof( PBCB ) * EaRange->BcbChainLength ); 3617 3618 } else { 3619 3620 EaRange->BcbChain = (PBCB *) &EaRange->BcbArray; 3621 } 3622 3623 // 3624 // Store the byte range data in the Ea Range structure. 3625 // 3626 3627 EaRange->StartingVbo = StartingVbo; 3628 EaRange->Length = Length; 3629 3630 // 3631 // Compute the initial pin length. 3632 // 3633 3634 ByteCount = PAGE_SIZE - (StartingVbo & (PAGE_SIZE - 1)); 3635 3636 // 3637 // For each page in the range; pin the page and update the Bcb count, copy to 3638 // the auxiliary buffer. 3639 // 3640 3641 NextBcb = EaRange->BcbChain; 3642 3643 while (Length != 0) { 3644 3645 // 3646 // Pin the page and remember the data start. 3647 // 3648 3649 LargeVbo.QuadPart = StartingVbo; 3650 3651 if (ByteCount > Length) { 3652 3653 ByteCount = Length; 3654 } 3655 3656 if (!CcPinRead( VirtualEaFile, 3657 &LargeVbo, 3658 ByteCount, 3659 BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT), 3660 NextBcb, 3661 &Buffer )) { 3662 3663 // 3664 // Could not read the data without waiting (cache miss). 3665 // 3666 3667 FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); 3668 } 3669 3670 // 3671 // Increment the Bcb pointer and copy to the auxilary buffer if necessary. 3672 // 3673 3674 NextBcb += 1; 3675 3676 if (EaRange->AuxilaryBuffer == TRUE) { 3677 3678 RtlCopyMemory( DestinationBuffer, 3679 Buffer, 3680 ByteCount ); 3681 3682 DestinationBuffer = (PCHAR) Add2Ptr( DestinationBuffer, ByteCount ); 3683 } 3684 3685 StartingVbo += ByteCount; 3686 Length -= ByteCount; 3687 3688 // 3689 // If this is the first page then update the Ea Range structure. 3690 // 3691 3692 if (FirstPage) { 3693 3694 FirstPage = FALSE; 3695 ByteCount = PAGE_SIZE; 3696 3697 if (EaRange->AuxilaryBuffer == FALSE) { 3698 3699 EaRange->Data = Buffer; 3700 } 3701 } 3702 } 3703 3704 return; 3705 } 3706 3707 3708 VOID 3709 FatMarkEaRangeDirty ( 3710 IN PIRP_CONTEXT IrpContext, 3711 IN PFILE_OBJECT EaFileObject, 3712 IN OUT PEA_RANGE EaRange 3713 ) 3714 3715 /*++ 3716 3717 Routine Description: 3718 3719 This routine is called to mark a range of the Ea file as dirty. If the modified 3720 data is sitting in an auxilary buffer then we will copy it back into the cache. 3721 In any case we will go through the list of Bcb's and mark them dirty. 3722 3723 Arguments: 3724 3725 EaFileObject - This is the file object for the Ea file. 3726 3727 EaRange - This is the Ea range structure for this request. 3728 3729 Return Value: 3730 3731 None. 3732 3733 --*/ 3734 3735 { 3736 PBCB *NextBcb; 3737 ULONG BcbCount; 3738 3739 PAGED_CODE(); 3740 3741 UNREFERENCED_PARAMETER( IrpContext ); 3742 3743 // 3744 // If there is an auxilary buffer we need to copy the data back into the cache. 3745 // 3746 3747 if (EaRange->AuxilaryBuffer == TRUE) { 3748 3749 LARGE_INTEGER LargeVbo; 3750 3751 LargeVbo.QuadPart = EaRange->StartingVbo; 3752 3753 CcCopyWrite( EaFileObject, 3754 &LargeVbo, 3755 EaRange->Length, 3756 TRUE, 3757 EaRange->Data ); 3758 } 3759 3760 // 3761 // Now walk through the Bcb chain and mark everything dirty. 3762 // 3763 3764 BcbCount = EaRange->BcbChainLength; 3765 NextBcb = EaRange->BcbChain; 3766 3767 while (BcbCount--) { 3768 3769 if (*NextBcb != NULL) { 3770 3771 CcSetDirtyPinnedData( *NextBcb, NULL ); 3772 } 3773 3774 NextBcb += 1; 3775 } 3776 3777 return; 3778 } 3779 3780 3781 VOID 3782 FatUnpinEaRange ( 3783 IN PIRP_CONTEXT IrpContext, 3784 IN OUT PEA_RANGE EaRange 3785 ) 3786 3787 /*++ 3788 3789 Routine Description: 3790 3791 This routine is called to unpin a range in the Ea file. Any structures allocated 3792 will be deallocated here. 3793 3794 Arguments: 3795 3796 EaRange - This is the Ea range structure for this request. 3797 3798 Return Value: 3799 3800 None. 3801 3802 --*/ 3803 3804 { 3805 PBCB *NextBcb; 3806 ULONG BcbCount; 3807 3808 PAGED_CODE(); 3809 3810 UNREFERENCED_PARAMETER( IrpContext ); 3811 3812 // 3813 // If we allocated a auxilary buffer, deallocate it here. 3814 // 3815 3816 if (EaRange->AuxilaryBuffer == TRUE) { 3817 3818 ExFreePool( EaRange->Data ); 3819 EaRange->AuxilaryBuffer = FALSE; 3820 } 3821 3822 // 3823 // Walk through the Bcb chain and unpin the data. 3824 // 3825 3826 if (EaRange->BcbChain != NULL) { 3827 3828 BcbCount = EaRange->BcbChainLength; 3829 NextBcb = EaRange->BcbChain; 3830 3831 while (BcbCount--) { 3832 3833 if (*NextBcb != NULL) { 3834 3835 CcUnpinData( *NextBcb ); 3836 *NextBcb = NULL; 3837 } 3838 3839 NextBcb += 1; 3840 } 3841 3842 // 3843 // If we allocated a Bcb chain, deallocate it here. 3844 // 3845 3846 if (EaRange->BcbChain != &EaRange->BcbArray[0]) { 3847 3848 ExFreePool( EaRange->BcbChain ); 3849 } 3850 3851 EaRange->BcbChain = NULL; 3852 } 3853 3854 return; 3855 } 3856 3857