1 /*++ 2 3 Copyright (c) 1990-2000 Microsoft Corporation 4 5 Module Name: 6 7 AllocSup.c 8 9 Abstract: 10 11 This module implements the Allocation support routines for Cdfs. 12 13 The data structure used here is the CD_MCB. There is an entry in 14 the Mcb for each dirent for a file. The entry will map the offset 15 within some file to a starting disk offset and number of bytes. 16 The Mcb also contains the interleave information for an extent. 17 An interleave consists of a number of blocks with data and a 18 (possibly different) number of blocks to skip. Any number of 19 data/skip pairs may exist in an extent but the data and skip sizes 20 are the same throughout the extent. 21 22 We store the following information into an Mcb entry for an extent. 23 24 FileOffset Offset in file for start of extent 25 DiskOffset Offset on disk for start of extent 26 ByteCount Number of file bytes in extent, no skip bytes 27 DataBlockByteCount Number of bytes in each data block 28 TotalBlockByteCount Number of bytes is data block and skip block 29 30 The disk offset in the Mcb has already been biased by the size of 31 the Xar block if present. All of the byte count fields are aligned 32 on logical block boundaries. If this is a directory or path table 33 then the file offset has been biased to round the initial disk 34 offset down to a sector boundary. The biasing is done when loading 35 the values into an Mcb entry. 36 37 An XA file has a header prepended to the file and each sector is 2352 38 bytes. The allocation information ignores the header and only deals 39 with 2048 byte sectors. Callers into the allocation package have 40 adjusted the starting offset value to reflect 2048 sectors. On return 41 from this package the caller will have to convert from 2048 sector values 42 into raw XA sector values. 43 44 45 --*/ 46 47 #include "cdprocs.h" 48 49 // 50 // The Bug check file id for this module 51 // 52 53 #define BugCheckFileId (CDFS_BUG_CHECK_ALLOCSUP) 54 55 // 56 // Local support routines 57 // 58 59 ULONG 60 CdFindMcbEntry ( 61 _In_ PIRP_CONTEXT IrpContext, 62 _In_ PFCB Fcb, 63 _In_ LONGLONG FileOffset 64 ); 65 66 VOID 67 CdDiskOffsetFromMcbEntry ( 68 _In_ PIRP_CONTEXT IrpContext, 69 _In_ PCD_MCB_ENTRY McbEntry, 70 _In_ LONGLONG FileOffset, 71 _Out_ PLONGLONG DiskOffset, 72 _Out_ PULONG ByteCount 73 ); 74 75 #ifdef ALLOC_PRAGMA 76 #pragma alloc_text(PAGE, CdAddInitialAllocation) 77 #pragma alloc_text(PAGE, CdAddAllocationFromDirent) 78 #pragma alloc_text(PAGE, CdDiskOffsetFromMcbEntry) 79 #pragma alloc_text(PAGE, CdFindMcbEntry) 80 #pragma alloc_text(PAGE, CdInitializeMcb) 81 #pragma alloc_text(PAGE, CdLookupAllocation) 82 #pragma alloc_text(PAGE, CdTruncateAllocation) 83 #pragma alloc_text(PAGE, CdUninitializeMcb) 84 #endif 85 86 87 _Requires_lock_held_(_Global_critical_region_) 88 VOID 89 #ifdef _MSC_VER 90 // PREFast currently has no way to express the Fcb==Fcb->Vcb->VolumeDasdFcb early return 91 #pragma warning(suppress: 6001 6101) 92 #endif 93 CdLookupAllocation ( 94 _In_ PIRP_CONTEXT IrpContext, 95 _In_ PFCB Fcb, 96 _In_ LONGLONG FileOffset, 97 _Out_ PLONGLONG DiskOffset, 98 _Out_ PULONG ByteCount 99 ) 100 101 /*++ 102 103 Routine Description: 104 105 This routine looks through the mapping information for the file 106 to find the logical diskoffset and number of bytes at that offset. 107 We only deal with logical 2048 byte sectors here. 108 109 If the mapping isn't present we will look it up on disk now. 110 This routine assumes we are looking up a valid range in the file. This 111 routine raises if it can't find mapping for the file offset. 112 113 The Fcb may not be locked prior to calling this routine. We will always 114 acquire it here. 115 116 Arguments: 117 118 Fcb - Fcb representing this stream. 119 120 FileOffset - Lookup the allocation beginning at this point. 121 122 DiskOffset - Address to store the logical disk offset. 123 124 ByteCount - Address to store the number of contiguous bytes beginning 125 at DiskOffset above. 126 127 Return Value: 128 129 None. 130 131 --*/ 132 133 { 134 BOOLEAN FirstPass = TRUE; 135 ULONG McbEntryOffset; 136 PFCB ParentFcb = NULL; 137 BOOLEAN CleanupParent = FALSE; 138 139 BOOLEAN UnlockFcb = FALSE; 140 141 LONGLONG CurrentFileOffset; 142 ULONG CurrentMcbOffset; 143 PCD_MCB_ENTRY CurrentMcbEntry; 144 145 DIRENT_ENUM_CONTEXT DirContext = {0}; 146 DIRENT Dirent = {0}; 147 148 PAGED_CODE(); 149 150 ASSERT_IRP_CONTEXT( IrpContext ); 151 ASSERT_FCB( Fcb ); 152 153 // 154 // For DASD IO we already have clamped the read to the volume limits. 155 // We'll allow reading beyond those limits for extended DASD IO, so 156 // no MCB lookup here. 157 // 158 159 if (Fcb == Fcb->Vcb->VolumeDasdFcb) { 160 161 *DiskOffset = FileOffset; 162 return; 163 } 164 165 // 166 // Use a try finally to facilitate cleanup. 167 // 168 169 _SEH2_TRY { 170 171 // 172 // We use a loop to perform the lookup. If we don't find the mapping in the 173 // first pass then we look up all of the allocation and then look again. 174 175 while (TRUE) { 176 177 // 178 // 179 // Lookup the entry containing this file offset. 180 // 181 182 CdLockFcb( IrpContext, Fcb ); 183 UnlockFcb = TRUE; 184 185 McbEntryOffset = CdFindMcbEntry( IrpContext, Fcb, FileOffset ); 186 187 // 188 // If within the Mcb then we use the data out of this entry and are 189 // done. 190 // 191 192 if (McbEntryOffset < Fcb->Mcb.CurrentEntryCount) { 193 194 CdDiskOffsetFromMcbEntry( IrpContext, 195 Fcb->Mcb.McbArray + McbEntryOffset, 196 FileOffset, 197 DiskOffset, 198 ByteCount ); 199 200 break; 201 202 // 203 // If this is not the first pass then the disk is corrupt. 204 // 205 206 } else if (!FirstPass) { 207 208 CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR ); 209 } 210 211 CdUnlockFcb( IrpContext, Fcb ); 212 UnlockFcb = FALSE; 213 214 // 215 // Initialize the search dirent structures. 216 // 217 218 CdInitializeDirContext( IrpContext, &DirContext ); 219 CdInitializeDirent( IrpContext, &Dirent ); 220 221 // 222 // Otherwise we need to walk the dirents for this file until we find 223 // the one containing this entry. The parent Fcb should always be 224 // present. 225 // 226 227 ParentFcb = Fcb->ParentFcb; 228 CdAcquireFileShared( IrpContext, ParentFcb ); 229 CleanupParent = TRUE; 230 231 // 232 // Do an unsafe test to see if we need to create a file object. 233 // 234 235 CdVerifyOrCreateDirStreamFile( IrpContext, ParentFcb); 236 237 // 238 // Initialize the local variables to indicate the first dirent 239 // and lookup the first dirent. 240 // 241 242 CurrentFileOffset = 0; 243 CurrentMcbOffset = 0; 244 245 CdLookupDirent( IrpContext, 246 ParentFcb, 247 CdQueryFidDirentOffset( Fcb->FileId ), 248 &DirContext ); 249 250 // 251 // If we are adding allocation to the Mcb then add all of it. 252 // 253 254 while (TRUE ) { 255 256 // 257 // Update the dirent from the on-disk dirent. 258 // 259 260 CdUpdateDirentFromRawDirent( IrpContext, ParentFcb, &DirContext, &Dirent ); 261 262 // 263 // Add this dirent to the Mcb if not already present. 264 // 265 266 CdLockFcb( IrpContext, Fcb ); 267 UnlockFcb = TRUE; 268 269 if (CurrentMcbOffset >= Fcb->Mcb.CurrentEntryCount) { 270 271 CdAddAllocationFromDirent( IrpContext, Fcb, CurrentMcbOffset, CurrentFileOffset, &Dirent ); 272 } 273 274 CdUnlockFcb( IrpContext, Fcb ); 275 UnlockFcb = FALSE; 276 277 // 278 // If this is the last dirent for the file then exit. 279 // 280 281 if (!FlagOn( Dirent.DirentFlags, CD_ATTRIBUTE_MULTI )) { 282 283 break; 284 } 285 286 // 287 // If we couldn't find another entry then the directory is corrupt because 288 // the last dirent for a file doesn't exist. 289 // 290 291 if (!CdLookupNextDirent( IrpContext, ParentFcb, &DirContext, &DirContext )) { 292 293 CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR ); 294 } 295 296 // 297 // Update our loop variables. 298 // 299 300 CurrentMcbEntry = Fcb->Mcb.McbArray + CurrentMcbOffset; 301 CurrentFileOffset += CurrentMcbEntry->ByteCount; 302 CurrentMcbOffset += 1; 303 } 304 305 // 306 // All of the allocation is loaded. Go back and look up the mapping again. 307 // It better be there this time. 308 // 309 310 FirstPass = FALSE; 311 } 312 313 } _SEH2_FINALLY { 314 315 if (CleanupParent) { 316 317 // 318 // Release the parent and cleanup the dirent structures. 319 // 320 321 CdReleaseFile( IrpContext, ParentFcb ); 322 323 CdCleanupDirContext( IrpContext, &DirContext ); 324 CdCleanupDirent( IrpContext, &Dirent ); 325 } 326 327 if (UnlockFcb) { CdUnlockFcb( IrpContext, Fcb ); } 328 } _SEH2_END; 329 330 return; 331 } 332 333 334 VOID 335 CdAddAllocationFromDirent ( 336 _In_ PIRP_CONTEXT IrpContext, 337 _Inout_ PFCB Fcb, 338 _In_ ULONG McbEntryOffset, 339 _In_ LONGLONG StartingFileOffset, 340 _In_ PDIRENT Dirent 341 ) 342 343 /*++ 344 345 Routine Description: 346 347 This routine is called to add an entry into the Cd Mcb. We grow the Mcb 348 as necessary and update the new entry. 349 350 NOTE - The Fcb has already been locked prior to makeing this call. 351 352 Arguments: 353 354 Fcb - Fcb containing the Mcb to update. 355 356 McbEntryOffset - Offset into the Mcb array to add this data. 357 358 StartingFileOffset - Offset in bytes from the start of the file. 359 360 Dirent - Dirent containing the on-disk data for this entry. 361 362 Return Value: 363 364 None 365 366 --*/ 367 368 { 369 ULONG NewArraySize; 370 PVOID NewMcbArray; 371 PCD_MCB_ENTRY McbEntry; 372 373 PAGED_CODE(); 374 375 UNREFERENCED_PARAMETER( IrpContext ); 376 377 ASSERT_IRP_CONTEXT( IrpContext ); 378 ASSERT_FCB( Fcb ); 379 ASSERT_LOCKED_FCB( Fcb ); 380 381 // 382 // If we need to grow the Mcb then do it now. 383 // 384 385 if (McbEntryOffset >= Fcb->Mcb.MaximumEntryCount) { 386 387 // 388 // Allocate a new buffer and copy the old data over. 389 // 390 391 NewArraySize = Fcb->Mcb.MaximumEntryCount * 2 * sizeof( CD_MCB_ENTRY ); 392 393 NewMcbArray = FsRtlAllocatePoolWithTag( CdPagedPool, 394 NewArraySize, 395 TAG_MCB_ARRAY ); 396 397 RtlZeroMemory( NewMcbArray, NewArraySize ); 398 RtlCopyMemory( NewMcbArray, 399 Fcb->Mcb.McbArray, 400 Fcb->Mcb.MaximumEntryCount * sizeof( CD_MCB_ENTRY )); 401 402 // 403 // Deallocate the current array unless it is embedded in the Fcb. 404 // 405 406 if (Fcb->Mcb.MaximumEntryCount != 1) { 407 408 CdFreePool( &Fcb->Mcb.McbArray ); 409 } 410 411 // 412 // Now update the Mcb with the new array. 413 // 414 415 Fcb->Mcb.MaximumEntryCount *= 2; 416 Fcb->Mcb.McbArray = NewMcbArray; 417 } 418 419 // 420 // Update the new entry with the input data. 421 // 422 423 McbEntry = Fcb->Mcb.McbArray + McbEntryOffset; 424 425 // 426 // Start with the location and length on disk. 427 // 428 429 McbEntry->DiskOffset = LlBytesFromBlocks( Fcb->Vcb, Dirent->StartingOffset ); 430 McbEntry->ByteCount = Dirent->DataLength; 431 432 // 433 // Round the byte count up to a logical block boundary if this is 434 // the last extent. 435 // 436 437 if (!FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_MULTI )) { 438 439 McbEntry->ByteCount = BlockAlign( Fcb->Vcb, McbEntry->ByteCount ); 440 } 441 442 // 443 // The file offset is the logical position within this file. 444 // We know this is correct regardless of whether we bias the 445 // file size or disk offset. 446 // 447 448 McbEntry->FileOffset = StartingFileOffset; 449 450 // 451 // Convert the interleave information from logical blocks to 452 // bytes. 453 // 454 455 if (Dirent->FileUnitSize != 0) { 456 457 McbEntry->DataBlockByteCount = LlBytesFromBlocks( Fcb->Vcb, Dirent->FileUnitSize ); 458 McbEntry->TotalBlockByteCount = McbEntry->DataBlockByteCount + 459 LlBytesFromBlocks( Fcb->Vcb, Dirent->InterleaveGapSize ); 460 461 // 462 // If the file is not interleaved then the size of the data block 463 // and total block are the same as the byte count. 464 // 465 466 } else { 467 468 McbEntry->DataBlockByteCount = 469 McbEntry->TotalBlockByteCount = McbEntry->ByteCount; 470 } 471 472 // 473 // Update the number of entries in the Mcb. The Mcb is never sparse 474 // so whenever we add an entry it becomes the last entry in the Mcb. 475 // 476 477 Fcb->Mcb.CurrentEntryCount = McbEntryOffset + 1; 478 479 return; 480 } 481 482 483 VOID 484 CdAddInitialAllocation ( 485 _In_ PIRP_CONTEXT IrpContext, 486 _Inout_ PFCB Fcb, 487 _In_ ULONG StartingBlock, 488 _In_ LONGLONG DataLength 489 ) 490 491 /*++ 492 493 Routine Description: 494 495 This routine is called to set up the initial entry in an Mcb. 496 497 This routine handles the single initial entry for a directory file. We will 498 round the start block down to a sector boundary. Our caller has already 499 biased the DataLength with any adjustments. This is used for the case 500 where there is a single entry and we want to align the data on a sector 501 boundary. 502 503 Arguments: 504 505 Fcb - Fcb containing the Mcb to update. 506 507 StartingBlock - Starting logical block for this directory. This is 508 the start of the actual data. We will bias this by the sector 509 offset of the data. 510 511 DataLength - Length of the data. 512 513 Return Value: 514 515 None 516 517 --*/ 518 519 { 520 PCD_MCB_ENTRY McbEntry; 521 522 PAGED_CODE(); 523 524 UNREFERENCED_PARAMETER( IrpContext ); 525 526 ASSERT_IRP_CONTEXT( IrpContext ); 527 ASSERT_FCB( Fcb ); 528 ASSERT_LOCKED_FCB( Fcb ); 529 NT_ASSERT( 0 == Fcb->Mcb.CurrentEntryCount); 530 NT_ASSERT( CDFS_NTC_FCB_DATA != Fcb->NodeTypeCode); 531 532 // 533 // Update the new entry with the input data. 534 // 535 536 McbEntry = Fcb->Mcb.McbArray; 537 538 // 539 // Start with the location and length on disk. 540 // 541 542 McbEntry->DiskOffset = LlBytesFromBlocks( Fcb->Vcb, StartingBlock ); 543 McbEntry->DiskOffset -= Fcb->StreamOffset; 544 545 McbEntry->ByteCount = DataLength; 546 547 // 548 // The file offset is the logical position within this file. 549 // We know this is correct regardless of whether we bias the 550 // file size or disk offset. 551 // 552 553 McbEntry->FileOffset = 0; 554 555 // 556 // If the file is not interleaved then the size of the data block 557 // and total block are the same as the byte count. 558 // 559 560 McbEntry->DataBlockByteCount = 561 McbEntry->TotalBlockByteCount = McbEntry->ByteCount; 562 563 // 564 // Update the number of entries in the Mcb. The Mcb is never sparse 565 // so whenever we add an entry it becomes the last entry in the Mcb. 566 // 567 568 Fcb->Mcb.CurrentEntryCount = 1; 569 570 return; 571 } 572 573 574 VOID 575 CdTruncateAllocation ( 576 _In_ PIRP_CONTEXT IrpContext, 577 _Inout_ PFCB Fcb, 578 _In_ LONGLONG StartingFileOffset 579 ) 580 581 /*++ 582 583 Routine Description: 584 585 This routine truncates the Mcb for a file by eliminating all of the Mcb 586 entries from the entry which contains the given offset. 587 588 The Fcb should be locked when this routine is called. 589 590 Arguments: 591 592 Fcb - Fcb containing the Mcb to truncate. 593 594 StartingFileOffset - Offset in the file to truncate the Mcb from. 595 596 Return Value: 597 598 None 599 600 --*/ 601 602 { 603 ULONG McbEntryOffset; 604 605 PAGED_CODE(); 606 607 ASSERT_IRP_CONTEXT( IrpContext ); 608 ASSERT_FCB( Fcb ); 609 ASSERT_LOCKED_FCB( Fcb ); 610 611 // 612 // Find the entry containg this starting offset. 613 // 614 615 McbEntryOffset = CdFindMcbEntry( IrpContext, Fcb, StartingFileOffset ); 616 617 // 618 // Now set the current size of the mcb to this point. 619 // 620 621 Fcb->Mcb.CurrentEntryCount = McbEntryOffset; 622 623 return; 624 } 625 626 627 _At_(Fcb->NodeByteSize, _In_range_(>=, FIELD_OFFSET( FCB, FcbType ))) 628 VOID 629 CdInitializeMcb ( 630 _In_ PIRP_CONTEXT IrpContext, 631 _Inout_updates_bytes_(Fcb->NodeByteSize) PFCB Fcb 632 ) 633 634 /*++ 635 636 Routine Description: 637 638 This routine is called to initialize the Mcb in an Fcb. We initialize 639 this with an entry count of one and point to the entry in the Fcb 640 itself. 641 642 Fcb should be acquired exclusively when this is called. 643 644 Arguments: 645 646 Fcb - Fcb containing the Mcb to initialize. 647 648 Return Value: 649 650 None 651 652 --*/ 653 654 { 655 PAGED_CODE(); 656 657 UNREFERENCED_PARAMETER( IrpContext ); 658 659 ASSERT_IRP_CONTEXT( IrpContext ); 660 ASSERT_FCB( Fcb ); 661 662 // 663 // Set the entry counts to show there is one entry in the array and 664 // it is unused. 665 // 666 667 Fcb->Mcb.MaximumEntryCount = 1; 668 Fcb->Mcb.CurrentEntryCount = 0; 669 670 Fcb->Mcb.McbArray = &Fcb->McbEntry; 671 672 return; 673 } 674 675 676 _At_(Fcb->NodeByteSize, _In_range_(>=, FIELD_OFFSET( FCB, FcbType ))) 677 _When_(Fcb->NodeTypeCode == CDFS_NTC_FCB_PATH_TABLE, _At_(Fcb->NodeByteSize, _In_range_(==, SIZEOF_FCB_INDEX))) 678 _When_(Fcb->NodeTypeCode == CDFS_NTC_FCB_INDEX, _At_(Fcb->NodeByteSize, _In_range_(==, SIZEOF_FCB_INDEX))) 679 _When_(Fcb->NodeTypeCode == CDFS_NTC_FCB_DATA, _At_(Fcb->NodeByteSize, _In_range_(==, SIZEOF_FCB_DATA))) 680 VOID 681 CdUninitializeMcb ( 682 _In_ PIRP_CONTEXT IrpContext, 683 _Inout_updates_bytes_(Fcb->NodeByteSize) PFCB Fcb 684 ) 685 686 /*++ 687 688 Routine Description: 689 690 This routine is called to cleanup an Mcb in an Fcb. We look at the 691 maximum run count in the Fcb and if greater than one we will deallocate 692 the buffer. 693 694 Fcb should be acquired exclusively when this is called. 695 696 Arguments: 697 698 Fcb - Fcb containing the Mcb to uninitialize. 699 700 Return Value: 701 702 None 703 704 --*/ 705 706 { 707 PAGED_CODE(); 708 709 UNREFERENCED_PARAMETER( IrpContext ); 710 711 ASSERT_IRP_CONTEXT( IrpContext ); 712 ASSERT_FCB( Fcb ); 713 714 // 715 // If the count is greater than one then this is an allocated buffer. 716 // 717 718 if (Fcb->Mcb.MaximumEntryCount > 1) { 719 720 CdFreePool( &Fcb->Mcb.McbArray ); 721 } 722 723 return; 724 } 725 726 727 // 728 // Local suupport routine 729 // 730 731 ULONG 732 CdFindMcbEntry ( 733 _In_ PIRP_CONTEXT IrpContext, 734 _In_ PFCB Fcb, 735 _In_ LONGLONG FileOffset 736 ) 737 738 /*++ 739 740 Routine Description: 741 742 This routine is called to find the Mcb entry which contains the file 743 offset at the given point. If the file offset is not currently in the 744 Mcb then we return the offset of the entry to add. 745 746 Fcb should be locked when this is called. 747 748 Arguments: 749 750 Fcb - Fcb containing the Mcb to uninitialize. 751 752 FileOffset - Return the Mcb entry which contains this file offset. 753 754 Return Value: 755 756 ULONG - Offset in the Mcb of the entry for this offset. 757 758 --*/ 759 760 { 761 ULONG CurrentMcbOffset; 762 PCD_MCB_ENTRY CurrentMcbEntry; 763 764 PAGED_CODE(); 765 766 UNREFERENCED_PARAMETER( IrpContext ); 767 768 ASSERT_IRP_CONTEXT( IrpContext ); 769 ASSERT_FCB( Fcb ); 770 ASSERT_LOCKED_FCB( Fcb ); 771 772 // 773 // We expect a linear search will be sufficient here. 774 // 775 776 CurrentMcbOffset = 0; 777 CurrentMcbEntry = Fcb->Mcb.McbArray; 778 779 while (CurrentMcbOffset < Fcb->Mcb.CurrentEntryCount) { 780 781 // 782 // Check if the offset lies within the current Mcb position. 783 // 784 785 if (FileOffset < CurrentMcbEntry->FileOffset + CurrentMcbEntry->ByteCount) { 786 787 break; 788 } 789 790 // 791 // Move to the next entry. 792 // 793 794 CurrentMcbOffset += 1; 795 CurrentMcbEntry += 1; 796 } 797 798 // 799 // This is the offset containing this file offset (or the point 800 // where an entry should be added). 801 // 802 803 return CurrentMcbOffset; 804 } 805 806 807 // 808 // Local support routine 809 // 810 811 VOID 812 CdDiskOffsetFromMcbEntry ( 813 _In_ PIRP_CONTEXT IrpContext, 814 _In_ PCD_MCB_ENTRY McbEntry, 815 _In_ LONGLONG FileOffset, 816 _Out_ PLONGLONG DiskOffset, 817 _Out_ PULONG ByteCount 818 ) 819 820 /*++ 821 822 Routine Description: 823 824 This routine is called to return the diskoffset and length of the file 825 data which begins at offset 'FileOffset'. We have the Mcb entry which 826 contains the mapping and interleave information. 827 828 NOTE - This routine deals with data in 2048 byte logical sectors. If 829 this is an XA file then our caller has already converted from 830 'raw' file bytes to 'cooked' file bytes. 831 832 Arguments: 833 834 McbEntry - Entry in the Mcb containing the allocation information. 835 836 FileOffset - Starting Offset in the file to find the matching disk 837 offsets. 838 839 DiskOffset - Address to store the starting disk offset for this operation. 840 841 ByteCount - Address to store number of contiguous bytes starting at this 842 disk offset. 843 844 Return Value: 845 846 None 847 848 --*/ 849 850 { 851 LONGLONG ExtentOffset; 852 853 LONGLONG CurrentDiskOffset; 854 LONGLONG CurrentExtentOffset; 855 856 LONGLONG LocalByteCount; 857 858 PAGED_CODE(); 859 860 UNREFERENCED_PARAMETER( IrpContext ); 861 862 ASSERT_IRP_CONTEXT( IrpContext ); 863 864 // 865 // Extent offset is the difference between the file offset and the start 866 // of the extent. 867 // 868 869 ExtentOffset = FileOffset - McbEntry->FileOffset; 870 871 // 872 // Optimize the non-interleave case. 873 // 874 875 if (McbEntry->ByteCount == McbEntry->DataBlockByteCount) { 876 877 *DiskOffset = McbEntry->DiskOffset + ExtentOffset; 878 879 LocalByteCount = McbEntry->ByteCount - ExtentOffset; 880 881 } else { 882 883 // 884 // Walk though any interleave until we reach the current offset in 885 // this extent. 886 // 887 888 CurrentExtentOffset = McbEntry->DataBlockByteCount; 889 CurrentDiskOffset = McbEntry->DiskOffset; 890 891 while (CurrentExtentOffset <= ExtentOffset) { 892 893 CurrentDiskOffset += McbEntry->TotalBlockByteCount; 894 CurrentExtentOffset += McbEntry->DataBlockByteCount; 895 } 896 897 // 898 // We are now positioned at the data block containing the starting 899 // file offset we were given. The disk offset is the offset of 900 // the start of this block plus the extent offset into this block. 901 // The byte count is the data block byte count minus our offset into 902 // this block. 903 // 904 905 *DiskOffset = CurrentDiskOffset + (ExtentOffset + McbEntry->DataBlockByteCount - CurrentExtentOffset); 906 907 // 908 // Make sure we aren't past the end of the data length. This is possible 909 // if we only use part of the last data block on an interleaved file. 910 // 911 912 if (CurrentExtentOffset > McbEntry->ByteCount) { 913 914 CurrentExtentOffset = McbEntry->ByteCount; 915 } 916 917 LocalByteCount = CurrentExtentOffset - ExtentOffset; 918 } 919 920 // 921 // If the byte count exceeds our limit then cut it to fit in 32 bits. 922 // 923 924 if (LocalByteCount > MAXULONG) { 925 926 *ByteCount = MAXULONG; 927 928 } else { 929 930 *ByteCount = (ULONG) LocalByteCount; 931 } 932 933 return; 934 } 935 936