1 /*++ 2 3 Copyright (c) 1989-2000 Microsoft Corporation 4 5 Module Name: 6 7 DirSup.c 8 9 Abstract: 10 11 This module implements the dirent support routines for Cdfs. 12 13 Directories on a CD consist of a number of contiguous sectors on 14 the disk. File descriptors consist of one or more directory entries 15 (dirents) within a directory. Files may contain version numbers. If 16 present all like-named files will be ordered contiguously in the 17 directory by decreasing version numbers. We will only return the 18 first of these on a directory query unless the user explicitly 19 asks for version numbers. Finally dirents will not span sector 20 boundaries. Unused bytes at the end of a sector will be zero 21 filled. 22 23 Directory sector: Offset 24 2048 25 +---------------------------------------------------------------+ 26 | | | | | | | 27 | foo;4 | foo;4 | foo;3 | hat | zebra | Zero| 28 | | | | | | Fill| 29 | | final | single | | | | 30 | | extent | extent | | | | 31 +---------------------------------------------------------------+ 32 33 Dirent operations: 34 35 - Position scan at known offset in directory. Dirent at this 36 offset must exist and is valid. Used when scanning a directory 37 from the beginning when the self entry is known to be valid. 38 Used when positioning at the first dirent for an open 39 file to scan the allocation information. Used when resuming 40 a directory enumeration from a valid directory entry. 41 42 - Position scan at known offset in directory. Dirent is known to 43 start at this position but must be checked for validity. 44 Used to read the self-directory entry. 45 46 - Move to the next dirent within a directory. 47 48 - Given a known starting dirent, collect all the dirents for 49 that file. Scan will finish positioned at the last dirent 50 for the file. We will accumulate the extent lengths to 51 find the size of the file. 52 53 - Given a known starting dirent, position the scan for the first 54 dirent of the following file. Used when not interested in 55 all of the details for the current file and are looking for 56 the next file. 57 58 - Update a common dirent structure with the details of the on-disk 59 structure. This is used to smooth out the differences 60 61 - Build the filename (name and version strings) out of the stream 62 of bytes in the file name on disk. For Joliet disks we will have 63 to convert to little endian. 64 65 66 --*/ 67 68 #include "cdprocs.h" 69 70 // 71 // The Bug check file id for this module 72 // 73 74 #define BugCheckFileId (CDFS_BUG_CHECK_DIRSUP) 75 76 // 77 // Local macros 78 // 79 80 // 81 // PRAW_DIRENT 82 // CdRawDirent ( 83 // _In_ PIRP_CONTEXT IrpContext, 84 // _In_ PDIR_ENUM_CONTEXT DirContext 85 // ); 86 // 87 88 #define CdRawDirent(IC,DC) \ 89 Add2Ptr( (DC)->Sector, (DC)->SectorOffset, PRAW_DIRENT ) 90 91 // 92 // Local support routines 93 // 94 95 ULONG 96 CdCheckRawDirentBounds ( 97 _In_ PIRP_CONTEXT IrpContext, 98 _In_ PDIRENT_ENUM_CONTEXT DirContext 99 ); 100 101 XA_EXTENT_TYPE 102 CdCheckForXAExtent ( 103 _In_ PIRP_CONTEXT IrpContext, 104 _In_ PRAW_DIRENT RawDirent, 105 _Inout_ PDIRENT Dirent 106 ); 107 108 #ifdef ALLOC_PRAGMA 109 #pragma alloc_text(PAGE, CdCheckForXAExtent) 110 #pragma alloc_text(PAGE, CdCheckRawDirentBounds) 111 #pragma alloc_text(PAGE, CdCleanupFileContext) 112 #pragma alloc_text(PAGE, CdFindFile) 113 #pragma alloc_text(PAGE, CdFindDirectory) 114 #pragma alloc_text(PAGE, CdFindFileByShortName) 115 #pragma alloc_text(PAGE, CdLookupDirent) 116 #pragma alloc_text(PAGE, CdLookupLastFileDirent) 117 #pragma alloc_text(PAGE, CdLookupNextDirent) 118 #pragma alloc_text(PAGE, CdLookupNextInitialFileDirent) 119 #pragma alloc_text(PAGE, CdUpdateDirentFromRawDirent) 120 #pragma alloc_text(PAGE, CdUpdateDirentName) 121 #endif 122 123 124 VOID 125 CdLookupDirent ( 126 _In_ PIRP_CONTEXT IrpContext, 127 _In_ PFCB Fcb, 128 _In_ ULONG DirentOffset, 129 _Out_ PDIRENT_ENUM_CONTEXT DirContext 130 ) 131 132 /*++ 133 134 Routine Description: 135 136 This routine is called to initiate a walk through a directory. We will 137 position ourselves in the directory at offset DirentOffset. We know that 138 a dirent begins at this boundary but may have to verify the dirent bounds. 139 We will call this routine when looking up the first entry of a known 140 file or verifying the self entry of a directory. 141 142 Arguments: 143 144 Fcb - Fcb for the directory being traversed. 145 146 DirentOffset - This is our target point in the directory. We will map the 147 page containing this entry and possibly verify the dirent bounds at 148 this location. 149 150 DirContext - This is the dirent context for this scan. We update it with 151 the location of the dirent we found. This structure has been initialized 152 outside of this call. 153 154 Return Value: 155 156 None. 157 158 --*/ 159 160 { 161 LONGLONG BaseOffset; 162 163 PAGED_CODE(); 164 165 // 166 // Initialize the offset of the first dirent we want to map. 167 // 168 169 DirContext->BaseOffset = SectorTruncate( DirentOffset ); 170 BaseOffset = DirContext->BaseOffset; 171 172 DirContext->DataLength = SECTOR_SIZE; 173 174 DirContext->SectorOffset = SectorOffset( DirentOffset ); 175 176 // 177 // Truncate the data length if we are at the end of the file. 178 // 179 180 if (DirContext->DataLength > (Fcb->FileSize.QuadPart - BaseOffset)) { 181 182 DirContext->DataLength = (ULONG) (Fcb->FileSize.QuadPart - BaseOffset); 183 } 184 185 // 186 // Now map the data at this offset. 187 // 188 189 CcMapData( Fcb->FileObject, 190 (PLARGE_INTEGER) &BaseOffset, 191 DirContext->DataLength, 192 TRUE, 193 &DirContext->Bcb, 194 &DirContext->Sector ); 195 196 // 197 // Verify the dirent bounds. 198 // 199 200 DirContext->NextDirentOffset = CdCheckRawDirentBounds( IrpContext, 201 DirContext ); 202 203 return; 204 } 205 206 207 BOOLEAN 208 CdLookupNextDirent ( 209 _In_ PIRP_CONTEXT IrpContext, 210 _In_ PFCB Fcb, 211 _In_ PDIRENT_ENUM_CONTEXT CurrentDirContext, 212 _Inout_ PDIRENT_ENUM_CONTEXT NextDirContext 213 ) 214 215 /*++ 216 217 Routine Description: 218 219 This routine is called to find the next dirent in the directory. The 220 current position is given and we look for the next. We leave the context 221 for the starting position untouched and update the context for the 222 dirent we found. The target context may already be initialized so we 223 may already have the sector in memory. 224 225 This routine will position the enumeration context for the next dirent and 226 verify the dirent bounds. 227 228 NOTE - This routine can be called with CurrentDirContext and NextDirContext 229 pointing to the same enumeration context. 230 231 Arguments: 232 233 Fcb - Fcb for the directory being traversed. 234 235 CurrentDirContext - This is the dirent context for this scan. We update 236 it with the location of the dirent we found. This is currently 237 pointing to a dirent location. The dirent bounds at this location 238 have already been verified. 239 240 NextDirContext - This is the dirent context to update with the dirent we 241 find. This may already point to a dirent so we need to check if 242 we are in the same sector and unmap any buffer as necessary. 243 244 This dirent is left in an indeterminant state if we don't find a dirent. 245 246 Return Value: 247 248 BOOLEAN - TRUE if we find a location for the next dirent, FALSE otherwise. 249 This routine can cause a raise if the directory is corrupt. 250 251 --*/ 252 253 { 254 LONGLONG CurrentBaseOffset = CurrentDirContext->BaseOffset; 255 ULONG TempUlong; 256 257 BOOLEAN FoundDirent = FALSE; 258 259 PAGED_CODE(); 260 261 // 262 // Check if a different sector is mapped. If so then move our target 263 // enumeration context to the same sector. 264 // 265 266 if ((CurrentDirContext->BaseOffset != NextDirContext->BaseOffset) || 267 (NextDirContext->Bcb == NULL)) { 268 269 // 270 // Unpin the current target Bcb and map the next sector. 271 // 272 273 CdUnpinData( IrpContext, &NextDirContext->Bcb ); 274 275 CcMapData( Fcb->FileObject, 276 (PLARGE_INTEGER) &CurrentBaseOffset, 277 CurrentDirContext->DataLength, 278 TRUE, 279 &NextDirContext->Bcb, 280 &NextDirContext->Sector ); 281 282 // 283 // Copy the data length and sector offset. 284 // 285 286 NextDirContext->DataLength = CurrentDirContext->DataLength; 287 NextDirContext->BaseOffset = CurrentDirContext->BaseOffset; 288 } 289 290 // 291 // Now move to the same offset in the sector. 292 // 293 294 NextDirContext->SectorOffset = CurrentDirContext->SectorOffset; 295 296 // 297 // If the value is zero then unmap the current sector and set up 298 // the base offset to the beginning of the next sector. 299 // 300 301 if (CurrentDirContext->NextDirentOffset == 0) { 302 303 CurrentBaseOffset = NextDirContext->BaseOffset + NextDirContext->DataLength; 304 305 // 306 // Unmap the current sector. We test the value of the Bcb in the 307 // loop below to see if we need to read in another sector. 308 // 309 310 CdUnpinData( IrpContext, &NextDirContext->Bcb ); 311 312 // 313 // There is another possible dirent in the current sector. Update the 314 // enumeration context to reflect this. 315 // 316 317 } else { 318 319 NextDirContext->SectorOffset += CurrentDirContext->NextDirentOffset; 320 } 321 322 // 323 // Now loop until we find the next possible dirent or walk off the directory. 324 // 325 326 while (TRUE) { 327 328 // 329 // If we don't currently have a sector mapped then map the 330 // directory at the current offset. 331 // 332 333 if (NextDirContext->Bcb == NULL) { 334 335 TempUlong = SECTOR_SIZE; 336 337 if (TempUlong > (ULONG) (Fcb->FileSize.QuadPart - CurrentBaseOffset)) { 338 339 TempUlong = (ULONG) (Fcb->FileSize.QuadPart - CurrentBaseOffset); 340 341 // 342 // If the length is zero then there is no dirent. 343 // 344 345 if (TempUlong == 0) { 346 347 break; 348 } 349 } 350 351 CcMapData( Fcb->FileObject, 352 (PLARGE_INTEGER) &CurrentBaseOffset, 353 TempUlong, 354 TRUE, 355 &NextDirContext->Bcb, 356 &NextDirContext->Sector ); 357 358 NextDirContext->BaseOffset = (ULONG) CurrentBaseOffset; 359 NextDirContext->SectorOffset = 0; 360 NextDirContext->DataLength = TempUlong; 361 } 362 363 // 364 // The CDFS spec allows for sectors in a directory to contain all zeroes. 365 // In this case we need to move to the next sector. So look at the 366 // current potential dirent for a zero length. Move to the next 367 // dirent if length is zero. 368 // 369 370 if (*((PCHAR) CdRawDirent( IrpContext, NextDirContext )) != 0) { 371 372 FoundDirent = TRUE; 373 break; 374 } 375 376 CurrentBaseOffset = NextDirContext->BaseOffset + NextDirContext->DataLength; 377 CdUnpinData( IrpContext, &NextDirContext->Bcb ); 378 } 379 380 // 381 // Check the dirent bounds if we found a dirent. 382 // 383 384 if (FoundDirent) { 385 386 NextDirContext->NextDirentOffset = CdCheckRawDirentBounds( IrpContext, 387 NextDirContext ); 388 } 389 390 return FoundDirent; 391 } 392 393 394 _At_(Dirent->CdTime, _Post_notnull_) 395 VOID 396 CdUpdateDirentFromRawDirent ( 397 _In_ PIRP_CONTEXT IrpContext, 398 _In_ PFCB Fcb, 399 _In_ PDIRENT_ENUM_CONTEXT DirContext, 400 _Inout_ PDIRENT Dirent 401 ) 402 403 /*++ 404 405 Routine Description: 406 407 This routine is called to safely copy the data from the dirent on disk 408 to the in-memory dirent. The fields on disk are unaligned so we 409 need to safely copy them to our structure. 410 411 Arguments: 412 413 Fcb - Fcb for the directory being scanned. 414 415 DirContext - Enumeration context for the raw disk dirent. 416 417 Dirent - In-memory dirent to update. 418 419 Return Value: 420 421 None. 422 423 --*/ 424 425 { 426 PRAW_DIRENT RawDirent = CdRawDirent( IrpContext, DirContext ); 427 428 PAGED_CODE(); 429 430 UNREFERENCED_PARAMETER( Fcb ); 431 432 // 433 // Clear all of the current state flags except the flag indicating that 434 // we allocated a name string. 435 // 436 437 ClearFlag( Dirent->Flags, DIRENT_FLAG_NOT_PERSISTENT ); 438 439 // 440 // The dirent offset is the sum of the start of the sector and the 441 // sector offset. 442 // 443 444 Dirent->DirentOffset = DirContext->BaseOffset + DirContext->SectorOffset; 445 446 // 447 // Copy the dirent length from the raw dirent. 448 // 449 450 Dirent->DirentLength = RawDirent->DirLen; 451 452 // 453 // The starting offset on disk is computed by finding the starting 454 // logical block and stepping over the Xar block. 455 // 456 457 CopyUchar4( &Dirent->StartingOffset, RawDirent->FileLoc ); 458 459 Dirent->StartingOffset += RawDirent->XarLen; 460 461 // 462 // Do a safe copy to get the data length. 463 // 464 465 CopyUchar4( &Dirent->DataLength, RawDirent->DataLen ); 466 467 // 468 // Save a pointer to the time stamps. 469 // 470 471 Dirent->CdTime = (PCHAR)RawDirent->RecordTime; 472 473 // 474 // Copy the dirent flags. 475 // 476 477 Dirent->DirentFlags = CdRawDirentFlags( IrpContext, RawDirent ); 478 479 // 480 // For both the file unit and interleave skip we want to take the 481 // logical block count. 482 // 483 484 Dirent->FileUnitSize = 485 Dirent->InterleaveGapSize = 0; 486 487 if (RawDirent->IntLeaveSize != 0) { 488 489 Dirent->FileUnitSize = RawDirent->IntLeaveSize; 490 Dirent->InterleaveGapSize = RawDirent->IntLeaveSkip; 491 } 492 493 // 494 // Get the name length and remember a pointer to the start of the 495 // name string. We don't do any processing on the name at this 496 // point. 497 // 498 // Check that the name length is non-zero. 499 // 500 501 if (RawDirent->FileIdLen == 0) { 502 503 CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); 504 } 505 506 Dirent->FileNameLen = RawDirent->FileIdLen; 507 Dirent->FileName = (PCHAR)RawDirent->FileId; 508 509 // 510 // If there are any remaining bytes at the end of the dirent then 511 // there may be a system use area. We protect ourselves from 512 // disks which don't pad the dirent entries correctly by using 513 // a fudge factor of one. All system use areas must have a length 514 // greater than one. Don't bother with the system use area 515 // if this is a directory. 516 // 517 518 Dirent->XAAttributes = 0; 519 Dirent->XAFileNumber = 0; 520 Dirent->ExtentType = Form1Data; 521 Dirent->SystemUseOffset = 0; 522 523 if (!FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_DIRECTORY ) && 524 (Dirent->DirentLength > ((FIELD_OFFSET( RAW_DIRENT, FileId ) + Dirent->FileNameLen) + 1))) { 525 526 Dirent->SystemUseOffset = WordAlign( FIELD_OFFSET( RAW_DIRENT, FileId ) + Dirent->FileNameLen ); 527 } 528 529 return; 530 } 531 532 533 VOID 534 CdUpdateDirentName ( 535 _In_ PIRP_CONTEXT IrpContext, 536 _Inout_ PDIRENT Dirent, 537 _In_ ULONG IgnoreCase 538 ) 539 540 /*++ 541 542 Routine Description: 543 544 This routine is called to update the name in the dirent with the name 545 from the disk. We will look for the special case of the self and 546 parent entries and also construct the Unicode name for the Joliet disk 547 in order to work around the BigEndian on-disk structure. 548 549 Arguments: 550 551 Dirent - Pointer to the in-memory dirent structure. 552 553 IgnoreCase - TRUE if we should build the upcased version. Otherwise we 554 use the exact case name. 555 556 Return Value: 557 558 None. 559 560 --*/ 561 562 { 563 UCHAR DirectoryValue; 564 ULONG Length; 565 566 NTSTATUS Status; 567 568 PAGED_CODE(); 569 570 // 571 // Check if this is a self or parent entry. There is no version number 572 // in these cases. We use a fixed string for these. 573 // 574 // Self-Entry - Length is 1, value is 0. 575 // Parent-Entry - Length is 1, value is 1. 576 // 577 578 if ((Dirent->FileNameLen == 1) && 579 FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_DIRECTORY )) { 580 581 DirectoryValue = *((PCHAR) Dirent->FileName); 582 583 if ((DirectoryValue == 0) || (DirectoryValue == 1)) { 584 585 // 586 // We should not have allocated a name by the time we see these cases. 587 // If we have, this means that the image is in violation of ISO 9660 7.6.2, 588 // which states that the ./.. entries must be the first two in the directory. 589 // 590 591 if (FlagOn( Dirent->Flags, DIRENT_FLAG_ALLOC_BUFFER )) { 592 593 CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); 594 } 595 596 // 597 // Now use one of the hard coded directory names. 598 // 599 600 Dirent->CdFileName.FileName = CdUnicodeDirectoryNames[DirectoryValue]; 601 602 // 603 // Show that there is no version number. 604 // 605 606 Dirent->CdFileName.VersionString.Length = 0; 607 608 // 609 // The case name is the same as the exact name. 610 // 611 612 Dirent->CdCaseFileName = Dirent->CdFileName; 613 614 // 615 // Mark this as a constant value entry. 616 // 617 618 SetFlag( Dirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY ); 619 620 // 621 // Return now. 622 // 623 624 return; 625 } 626 } 627 628 // 629 // Mark this as a non-constant value entry. 630 // 631 632 ClearFlag( Dirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY ); 633 634 // 635 // Compute how large a buffer we will need. If this is an ignore 636 // case operation then we will want a double size buffer. If the disk is not 637 // a Joliet disk then we might need two bytes for each byte in the name. 638 // 639 640 Length = Dirent->FileNameLen; 641 642 if (IgnoreCase) { 643 644 Length *= 2; 645 } 646 647 if (!FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_JOLIET )) { 648 649 Length *= sizeof( WCHAR ); 650 } 651 652 // 653 // Now decide if we need to allocate a new buffer. We will if 654 // this name won't fit in the embedded name buffer and it is 655 // larger than the current allocated buffer. We always use the 656 // allocated buffer if present. 657 // 658 // If we haven't allocated a buffer then use the embedded buffer if the data 659 // will fit. This is the typical case. 660 // 661 662 if (!FlagOn( Dirent->Flags, DIRENT_FLAG_ALLOC_BUFFER ) && 663 (Length <= sizeof( Dirent->NameBuffer ))) { 664 665 Dirent->CdFileName.FileName.MaximumLength = sizeof( Dirent->NameBuffer ); 666 Dirent->CdFileName.FileName.Buffer = Dirent->NameBuffer; 667 668 } else { 669 670 // 671 // We need to use an allocated buffer. Check if the current buffer 672 // is large enough. 673 // 674 675 if (Length > Dirent->CdFileName.FileName.MaximumLength) { 676 677 // 678 // Free any allocated buffer. 679 // 680 681 if (FlagOn( Dirent->Flags, DIRENT_FLAG_ALLOC_BUFFER )) { 682 683 CdFreePool( &Dirent->CdFileName.FileName.Buffer ); 684 ClearFlag( Dirent->Flags, DIRENT_FLAG_ALLOC_BUFFER ); 685 } 686 687 Dirent->CdFileName.FileName.Buffer = FsRtlAllocatePoolWithTag( CdPagedPool, 688 Length, 689 TAG_DIRENT_NAME ); 690 691 SetFlag( Dirent->Flags, DIRENT_FLAG_ALLOC_BUFFER ); 692 693 Dirent->CdFileName.FileName.MaximumLength = (USHORT) Length; 694 } 695 } 696 697 // 698 // We now have a buffer for the name. We need to either convert the on-disk bigendian 699 // to little endian or covert the name to Unicode. 700 // 701 702 if (!FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_JOLIET )) { 703 704 Status = RtlOemToUnicodeN( Dirent->CdFileName.FileName.Buffer, 705 Dirent->CdFileName.FileName.MaximumLength, 706 &Length, 707 Dirent->FileName, 708 Dirent->FileNameLen ); 709 710 __analysis_assert( Status == STATUS_SUCCESS ); 711 NT_ASSERT( Status == STATUS_SUCCESS ); 712 Dirent->CdFileName.FileName.Length = (USHORT) Length; 713 714 } else { 715 716 // 717 // Convert this string to little endian. 718 // 719 720 CdConvertBigToLittleEndian( IrpContext, 721 Dirent->FileName, 722 Dirent->FileNameLen, 723 (PCHAR) Dirent->CdFileName.FileName.Buffer ); 724 725 Dirent->CdFileName.FileName.Length = (USHORT) Dirent->FileNameLen; 726 } 727 728 // 729 // Split the name into name and version strings. 730 // 731 732 CdConvertNameToCdName( IrpContext, 733 &Dirent->CdFileName ); 734 735 // 736 // The name length better be non-zero. 737 // 738 739 if (Dirent->CdFileName.FileName.Length == 0) { 740 741 CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); 742 } 743 744 // 745 // If the filename ends with a period then back up one character. 746 // 747 748 if (Dirent->CdFileName.FileName.Buffer[(Dirent->CdFileName.FileName.Length - sizeof( WCHAR )) / 2] == L'.') { 749 750 // 751 // Slide the version string down. 752 // 753 754 if (Dirent->CdFileName.VersionString.Length != 0) { 755 756 PWCHAR NewVersion; 757 758 // 759 // Start from the position currently containing the separator. 760 // 761 762 NewVersion = Add2Ptr( Dirent->CdFileName.FileName.Buffer, 763 Dirent->CdFileName.FileName.Length, 764 PWCHAR ); 765 766 // 767 // Now overwrite the period. 768 // 769 770 RtlMoveMemory( NewVersion - 1, 771 NewVersion, 772 Dirent->CdFileName.VersionString.Length + sizeof( WCHAR )); 773 774 // 775 // Now point to the new version string. 776 // 777 778 Dirent->CdFileName.VersionString.Buffer = NewVersion; 779 } 780 781 // 782 // Shrink the filename length. 783 // 784 785 Dirent->CdFileName.FileName.Length -= sizeof( WCHAR ); 786 } 787 788 if (!CdIsLegalName( IrpContext, &Dirent->CdFileName.FileName )) { 789 790 CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); 791 } 792 793 // 794 // If this an exact case operation then use the filename exactly. 795 // 796 797 if (!IgnoreCase) { 798 799 Dirent->CdCaseFileName = Dirent->CdFileName; 800 801 // 802 // Otherwise perform our upcase operation. We already have guaranteed the buffers are 803 // there. 804 // 805 806 } else { 807 808 Dirent->CdCaseFileName.FileName.Buffer = Add2Ptr( Dirent->CdFileName.FileName.Buffer, 809 Dirent->CdFileName.FileName.MaximumLength / 2, 810 PWCHAR); 811 812 Dirent->CdCaseFileName.FileName.MaximumLength = Dirent->CdFileName.FileName.MaximumLength / 2; 813 814 CdUpcaseName( IrpContext, 815 &Dirent->CdFileName, 816 &Dirent->CdCaseFileName ); 817 } 818 819 return; 820 } 821 822 823 _Success_(return != FALSE) BOOLEAN 824 CdFindFile ( 825 _In_ PIRP_CONTEXT IrpContext, 826 _In_ PFCB Fcb, 827 _In_ PCD_NAME Name, 828 _In_ BOOLEAN IgnoreCase, 829 _Inout_ PFILE_ENUM_CONTEXT FileContext, 830 _Out_ PCD_NAME *MatchingName 831 ) 832 833 /*++ 834 835 Routine Description: 836 837 This routine is called to search a dirctory for a file matching the input 838 name. This name has been upcased at this point if this a case-insensitive 839 search. The name has been separated into separate name and version strings. 840 We look for an exact match in the name and only consider the version if 841 there is a version specified in the search name. 842 843 Arguments: 844 845 Fcb - Fcb for the directory being scanned. 846 847 Name - Name to search for. 848 849 IgnoreCase - Indicates the case of the search. 850 851 FileContext - File context to use for the search. This has already been 852 initialized. 853 854 MatchingName - Pointer to buffer containing matching name. We need this 855 in case we don't match the name in the directory but match the 856 short name instead. 857 858 Return Value: 859 860 BOOLEAN - TRUE if matching entry is found, FALSE otherwise. 861 862 --*/ 863 864 { 865 PDIRENT Dirent; 866 ULONG ShortNameDirentOffset; 867 868 BOOLEAN Found = FALSE; 869 870 PAGED_CODE(); 871 872 // 873 // Make sure there is a stream file for this Fcb. 874 // 875 876 CdVerifyOrCreateDirStreamFile( IrpContext, Fcb); 877 878 // 879 // Check to see whether we need to check for a possible short name. 880 // 881 882 ShortNameDirentOffset = CdShortNameDirentOffset( IrpContext, &Name->FileName ); 883 884 // 885 // Position ourselves at the first entry. 886 // 887 888 CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, Fcb->StreamOffset ); 889 890 // 891 // Loop while there are more entries in this directory. 892 // 893 894 do { 895 896 Dirent = &FileContext->InitialDirent->Dirent; 897 898 // 899 // We only consider files which don't have the associated bit set. 900 // We also only look for files. All directories would already 901 // have been found. 902 // 903 904 if (!FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_ASSOC | CD_ATTRIBUTE_DIRECTORY )) { 905 906 // 907 // Update the name in the current dirent. 908 // 909 910 CdUpdateDirentName( IrpContext, Dirent, IgnoreCase ); 911 912 // 913 // Don't bother with constant entries. 914 // 915 916 if (FlagOn( Dirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY )) { 917 918 continue; 919 } 920 921 // 922 // Now check whether we have a name match. 923 // We exit the loop if we have a match. 924 // 925 926 if (CdIsNameInExpression( IrpContext, 927 &Dirent->CdCaseFileName, 928 Name, 929 0, 930 TRUE )) { 931 932 *MatchingName = &Dirent->CdCaseFileName; 933 Found = TRUE; 934 break; 935 } 936 937 // 938 // The names didn't match. If the input name is a possible short 939 // name and we are at the correct offset in the directory then 940 // check if the short names match. 941 // 942 943 if (((Dirent->DirentOffset >> SHORT_NAME_SHIFT) == ShortNameDirentOffset) && 944 (Name->VersionString.Length == 0) && 945 !CdIs8dot3Name( IrpContext, 946 Dirent->CdFileName.FileName )) { 947 948 // 949 // Create the short name and check for a match. 950 // 951 952 CdGenerate8dot3Name( IrpContext, 953 &Dirent->CdCaseFileName.FileName, 954 Dirent->DirentOffset, 955 FileContext->ShortName.FileName.Buffer, 956 &FileContext->ShortName.FileName.Length ); 957 958 // 959 // Now check whether we have a name match. 960 // We exit the loop if we have a match. 961 // 962 963 if (CdIsNameInExpression( IrpContext, 964 &FileContext->ShortName, 965 Name, 966 0, 967 FALSE )) { 968 969 *MatchingName = &FileContext->ShortName, 970 Found = TRUE; 971 break; 972 } 973 } 974 } 975 976 // 977 // Go to the next initial dirent for a file. 978 // 979 980 } while (CdLookupNextInitialFileDirent( IrpContext, Fcb, FileContext )); 981 982 // 983 // If we find the file then collect all of the dirents. 984 // 985 986 if (Found) { 987 988 CdLookupLastFileDirent( IrpContext, Fcb, FileContext ); 989 990 } 991 992 return Found; 993 } 994 995 996 BOOLEAN 997 CdFindDirectory ( 998 _In_ PIRP_CONTEXT IrpContext, 999 _In_ PFCB Fcb, 1000 _In_ PCD_NAME Name, 1001 _In_ BOOLEAN IgnoreCase, 1002 _Inout_ PFILE_ENUM_CONTEXT FileContext 1003 ) 1004 1005 /*++ 1006 1007 Routine Description: 1008 1009 This routine is called to search a dirctory for a directory matching the input 1010 name. This name has been upcased at this point if this a case-insensitive 1011 search. We look for an exact match in the name and do not look for shortname 1012 equivalents. 1013 1014 Arguments: 1015 1016 Fcb - Fcb for the directory being scanned. 1017 1018 Name - Name to search for. 1019 1020 IgnoreCase - Indicates the case of the search. 1021 1022 FileContext - File context to use for the search. This has already been 1023 initialized. 1024 1025 Return Value: 1026 1027 BOOLEAN - TRUE if matching entry is found, FALSE otherwise. 1028 1029 --*/ 1030 1031 { 1032 PDIRENT Dirent; 1033 1034 BOOLEAN Found = FALSE; 1035 1036 PAGED_CODE(); 1037 1038 // 1039 // Make sure there is a stream file for this Fcb. 1040 // 1041 1042 CdVerifyOrCreateDirStreamFile( IrpContext, Fcb); 1043 1044 // 1045 // Position ourselves at the first entry. 1046 // 1047 1048 CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, Fcb->StreamOffset ); 1049 1050 // 1051 // Loop while there are more entries in this directory. 1052 // 1053 1054 do { 1055 1056 Dirent = &FileContext->InitialDirent->Dirent; 1057 1058 // 1059 // We only look for directories. Directories cannot have the 1060 // associated bit set. 1061 // 1062 1063 if (FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_DIRECTORY )) { 1064 1065 // 1066 // Update the name in the current dirent. 1067 // 1068 1069 CdUpdateDirentName( IrpContext, Dirent, IgnoreCase ); 1070 1071 // 1072 // Don't bother with constant entries. 1073 // 1074 1075 if (FlagOn( Dirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY )) { 1076 1077 continue; 1078 } 1079 1080 // 1081 // Now check whether we have a name match. 1082 // We exit the loop if we have a match. 1083 // 1084 1085 if (CdIsNameInExpression( IrpContext, 1086 &Dirent->CdCaseFileName, 1087 Name, 1088 0, 1089 TRUE )) { 1090 1091 Found = TRUE; 1092 break; 1093 } 1094 } 1095 1096 // 1097 // Go to the next initial dirent. 1098 // 1099 1100 } while (CdLookupNextInitialFileDirent( IrpContext, Fcb, FileContext )); 1101 1102 return Found; 1103 } 1104 1105 1106 _At_(FileContext->ShortName.FileName.MaximumLength, _In_range_(>=, BYTE_COUNT_8_DOT_3)) 1107 BOOLEAN 1108 CdFindFileByShortName ( 1109 _In_ PIRP_CONTEXT IrpContext, 1110 _In_ PFCB Fcb, 1111 _In_ PCD_NAME Name, 1112 _In_ BOOLEAN IgnoreCase, 1113 _In_ ULONG ShortNameDirentOffset, 1114 _Inout_ PFILE_ENUM_CONTEXT FileContext 1115 ) 1116 1117 /*++ 1118 1119 Routine Description: 1120 1121 This routine is called to find the file name entry whose short name 1122 is defined by the input DirentOffset. The dirent offset here is 1123 multiplied by 32 and we look for the dirent begins in this 32 byte offset in 1124 directory. The minimum dirent length is 34 so we are guaranteed that only 1125 one dirent can begin in each 32 byte block in the directory. 1126 1127 Arguments: 1128 1129 Fcb - Fcb for the directory being scanned. 1130 1131 Name - Name we are trying to match. We know this contains the tilde 1132 character followed by decimal characters. 1133 1134 IgnoreCase - Indicates whether we need to upcase the long name and 1135 generated short name. 1136 1137 ShortNameDirentOffset - This is the shifted value for the offset of the 1138 name in the directory. 1139 1140 FileContext - This is the initialized file context to use for the search. 1141 1142 Return Value: 1143 1144 BOOLEAN - TRUE if a matching name was found, FALSE otherwise. 1145 1146 --*/ 1147 1148 { 1149 BOOLEAN Found = FALSE; 1150 PDIRENT Dirent; 1151 1152 ULONG ThisShortNameDirentOffset; 1153 1154 PAGED_CODE(); 1155 1156 // 1157 // Make sure there is a stream file for this Fcb. 1158 // 1159 1160 CdVerifyOrCreateDirStreamFile( IrpContext, Fcb); 1161 1162 // 1163 // Position ourselves at the start of the directory and update 1164 // 1165 // 1166 1167 CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, Fcb->StreamOffset ); 1168 1169 // 1170 // Loop until we have found the entry or are beyond this dirent. 1171 // 1172 1173 do { 1174 1175 // 1176 // Compute the short name dirent offset for the current dirent. 1177 // 1178 1179 Dirent = &FileContext->InitialDirent->Dirent; 1180 ThisShortNameDirentOffset = Dirent->DirentOffset >> SHORT_NAME_SHIFT; 1181 1182 // 1183 // If beyond the target then exit. 1184 // 1185 1186 if (ThisShortNameDirentOffset > ShortNameDirentOffset) { 1187 1188 break; 1189 } 1190 1191 // 1192 // If equal to the target then check if we have a name match. 1193 // We will either match or fail here. 1194 // 1195 1196 if (ThisShortNameDirentOffset == ShortNameDirentOffset) { 1197 1198 // 1199 // If this is an associated file then get out. 1200 // 1201 1202 if (FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_ASSOC )) { 1203 1204 break; 1205 } 1206 1207 // 1208 // Update the name in the dirent and check if it is not 1209 // an 8.3 name. 1210 // 1211 1212 CdUpdateDirentName( IrpContext, Dirent, IgnoreCase ); 1213 1214 if (CdIs8dot3Name( IrpContext, 1215 Dirent->CdFileName.FileName )) { 1216 1217 break; 1218 } 1219 1220 // 1221 // Generate the 8.3 name see if it matches our input name. 1222 // 1223 1224 CdGenerate8dot3Name( IrpContext, 1225 &Dirent->CdCaseFileName.FileName, 1226 Dirent->DirentOffset, 1227 FileContext->ShortName.FileName.Buffer, 1228 &FileContext->ShortName.FileName.Length ); 1229 1230 // 1231 // Check if this name matches. 1232 // 1233 1234 if (CdIsNameInExpression( IrpContext, 1235 Name, 1236 &FileContext->ShortName, 1237 0, 1238 FALSE )) { 1239 1240 // 1241 // Let our caller know we found an entry. 1242 // 1243 1244 Found = TRUE; 1245 } 1246 1247 // 1248 // Break out of the loop. 1249 // 1250 1251 break; 1252 } 1253 1254 // 1255 // Continue until there are no more entries. 1256 // 1257 1258 } while (CdLookupNextInitialFileDirent( IrpContext, Fcb, FileContext )); 1259 1260 // 1261 // If we find the file then collect all of the dirents. 1262 // 1263 1264 if (Found) { 1265 1266 CdLookupLastFileDirent( IrpContext, Fcb, FileContext ); 1267 1268 } 1269 1270 return Found; 1271 } 1272 1273 1274 BOOLEAN 1275 CdLookupNextInitialFileDirent ( 1276 _In_ PIRP_CONTEXT IrpContext, 1277 _In_ PFCB Fcb, 1278 _Inout_ PFILE_ENUM_CONTEXT FileContext 1279 ) 1280 1281 /*++ 1282 1283 Routine Description: 1284 1285 This routine is called to walk through the directory until we find the 1286 first possible dirent for file. We are positioned at some point described 1287 by the FileContext. We will walk through any remaing dirents for the 1288 current file until we find the first dirent for some subsequent file. 1289 1290 We can be called when we have found just one dirent for a file or all 1291 of them. We first check the CurrentDirContext. In the typical 1292 single-extent case this is unused. Then we look to the InitialDirContext 1293 which must be initialized. 1294 1295 This routine will save the initial DirContext to the PriorDirContext and 1296 clean up any existing DirContext for the Prior or Current positions in 1297 the enumeration context. 1298 1299 Arguments: 1300 1301 Fcb - This is the directory to scan. 1302 1303 FileContext - This is the file enumeration context. It is currently pointing 1304 at some file in the directory. 1305 1306 Return Value: 1307 1308 --*/ 1309 1310 { 1311 PRAW_DIRENT RawDirent; 1312 1313 PDIRENT_ENUM_CONTEXT CurrentDirContext; 1314 PDIRENT_ENUM_CONTEXT TargetDirContext; 1315 PCOMPOUND_DIRENT TempDirent; 1316 1317 BOOLEAN FoundDirent = FALSE; 1318 BOOLEAN FoundLastDirent; 1319 1320 PAGED_CODE(); 1321 1322 // 1323 // Start by saving the initial dirent of the current file as the 1324 // previous file. 1325 // 1326 1327 TempDirent = FileContext->PriorDirent; 1328 FileContext->PriorDirent = FileContext->InitialDirent; 1329 FileContext->InitialDirent = TempDirent; 1330 1331 // 1332 // We will use the initial dirent of the prior file unless the 1333 // previous search returned multiple extents. 1334 // 1335 1336 CurrentDirContext = &FileContext->PriorDirent->DirContext; 1337 1338 if (FlagOn( FileContext->Flags, FILE_CONTEXT_MULTIPLE_DIRENTS )) { 1339 1340 CurrentDirContext = &FileContext->CurrentDirent->DirContext; 1341 } 1342 1343 // 1344 // Clear all of the flags and file size for the next file. 1345 // 1346 1347 FileContext->Flags = 0; 1348 FileContext->FileSize = 0; 1349 1350 FileContext->ShortName.FileName.Length = 0; 1351 1352 // 1353 // We always want to store the result into the updated initial dirent 1354 // context. 1355 // 1356 1357 TargetDirContext = &FileContext->InitialDirent->DirContext; 1358 1359 // 1360 // Loop until we find the first dirent after the last dirent of the 1361 // current file. We may not be at the last dirent for the current file yet 1362 // so we may walk forward looking for the last and then find the 1363 // initial dirent for the next file after that. 1364 // 1365 1366 while (TRUE) { 1367 1368 // 1369 // Remember if the last dirent we visited was the last dirent for 1370 // a file. 1371 // 1372 1373 RawDirent = CdRawDirent( IrpContext, CurrentDirContext ); 1374 1375 FoundLastDirent = !FlagOn( CdRawDirentFlags( IrpContext, RawDirent ), CD_ATTRIBUTE_MULTI ); 1376 1377 // 1378 // Try to find another dirent. 1379 // 1380 1381 FoundDirent = CdLookupNextDirent( IrpContext, 1382 Fcb, 1383 CurrentDirContext, 1384 TargetDirContext ); 1385 1386 // 1387 // Exit the loop if no entry found. 1388 // 1389 1390 if (!FoundDirent) { 1391 1392 break; 1393 1394 } 1395 1396 // 1397 // Update the in-memory dirent. 1398 // 1399 1400 CdUpdateDirentFromRawDirent( IrpContext, 1401 Fcb, 1402 TargetDirContext, 1403 &FileContext->InitialDirent->Dirent ); 1404 1405 // 1406 // Exit the loop if we had the end for the previous file. 1407 // 1408 1409 if (FoundLastDirent) { 1410 1411 break; 1412 } 1413 1414 // 1415 // Always use a single dirent from this point on. 1416 // 1417 1418 CurrentDirContext = TargetDirContext; 1419 } 1420 1421 return FoundDirent; 1422 } 1423 1424 1425 VOID 1426 CdLookupLastFileDirent ( 1427 _In_ PIRP_CONTEXT IrpContext, 1428 _In_ PFCB Fcb, 1429 _In_ PFILE_ENUM_CONTEXT FileContext 1430 ) 1431 1432 /*++ 1433 1434 Routine Description: 1435 1436 This routine is called when we've found the matching initial dirent for 1437 a file. Now we want to find all of the dirents for a file as well as 1438 compute the running total for the file size. 1439 1440 We also go out to the system use area and check whether this is an 1441 XA sector. In that case we will compute the real file size. 1442 1443 The dirent in the initial compound dirent has been updated from the 1444 raw dirent when this routine is called. 1445 1446 Arguments: 1447 1448 Fcb - Directory containing the entries for the file. 1449 1450 FileContext - Enumeration context for this search. It currently points 1451 to the first dirent of the file and the in-memory dirent has been 1452 updated. 1453 1454 Return Value: 1455 1456 None. This routine may raise STATUS_FILE_CORRUPT. 1457 1458 --*/ 1459 1460 { 1461 XA_EXTENT_TYPE ExtentType = Form1Data; 1462 PCOMPOUND_DIRENT CurrentCompoundDirent; 1463 PDIRENT CurrentDirent = NULL; 1464 1465 BOOLEAN FirstPass = TRUE; 1466 BOOLEAN FoundDirent; 1467 1468 PAGED_CODE(); 1469 1470 // 1471 // The current dirent to look at is the initial dirent for the file. 1472 // 1473 1474 CurrentCompoundDirent = FileContext->InitialDirent; 1475 1476 // 1477 // Loop until we reach the last dirent for the file. 1478 // 1479 1480 while (TRUE) { 1481 1482 CurrentDirent = &CurrentCompoundDirent->Dirent; 1483 1484 // 1485 // Check if this extent has XA sectors. 1486 // 1487 1488 if ((CurrentDirent->SystemUseOffset != 0) && 1489 FlagOn( Fcb->Vcb->VcbState, VCB_STATE_CDXA ) && 1490 CdCheckForXAExtent( IrpContext, 1491 CdRawDirent( IrpContext, &CurrentCompoundDirent->DirContext ), 1492 CurrentDirent )) { 1493 1494 // 1495 // Any previous dirent must describe XA sectors as well. 1496 // 1497 1498 if (!FirstPass && (ExtentType != CurrentDirent->ExtentType)) { 1499 1500 CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); 1501 } 1502 1503 // 1504 // If there are XA sectors then the data on the disk must 1505 // be correctly aligned on sectors and be an integral number of 1506 // sectors. Only an issue if the logical block size is not 1507 // 2048. 1508 // 1509 1510 if (Fcb->Vcb->BlockSize != SECTOR_SIZE) { 1511 1512 // 1513 // We will do the following checks. 1514 // 1515 // Data must start on a sector boundary. 1516 // Data length must be integral number of sectors. 1517 // 1518 1519 if ((SectorBlockOffset( Fcb->Vcb, CurrentDirent->StartingOffset ) != 0) || 1520 (SectorBlockOffset( Fcb->Vcb, CurrentDirent->DataLength ) != 0)) { 1521 1522 CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); 1523 } 1524 1525 // 1526 // If interleaved then both the file unit and interleave 1527 // gap must be integral number of sectors. 1528 // 1529 1530 if ((CurrentDirent->FileUnitSize != 0) && 1531 ((SectorBlockOffset( Fcb->Vcb, CurrentDirent->FileUnitSize ) != 0) || 1532 (SectorBlockOffset( Fcb->Vcb, CurrentDirent->InterleaveGapSize ) != 0))) { 1533 1534 CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); 1535 } 1536 } 1537 1538 // 1539 // If this is the first dirent then add the bytes for the RIFF 1540 // header. 1541 // 1542 1543 if (FirstPass) { 1544 1545 FileContext->FileSize = sizeof( RIFF_HEADER ); 1546 } 1547 1548 // 1549 // Add the size of the mode2-form2 sector for each sector 1550 // we have here. 1551 // 1552 1553 FileContext->FileSize += Int32x32To64( CurrentDirent->DataLength >> SECTOR_SHIFT, 1554 XA_SECTOR_SIZE); 1555 1556 } else { 1557 1558 // 1559 // This extent does not have XA sectors. Any previous dirent 1560 // better not have XA sectors. 1561 // 1562 1563 if (!FirstPass && (ExtentType != CurrentDirent->ExtentType)) { 1564 1565 CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); 1566 } 1567 1568 // 1569 // Add these bytes to the file size. 1570 // 1571 1572 FileContext->FileSize += CurrentDirent->DataLength; 1573 } 1574 1575 // 1576 // If we are at the last dirent then exit. 1577 // 1578 1579 if (!FlagOn( CurrentDirent->DirentFlags, CD_ATTRIBUTE_MULTI )) { 1580 1581 break; 1582 } 1583 1584 // 1585 // Remember the extent type of the current extent. 1586 // 1587 1588 ExtentType = CurrentDirent->ExtentType; 1589 1590 // 1591 // Look for the next dirent of the file. 1592 // 1593 1594 FoundDirent = CdLookupNextDirent( IrpContext, 1595 Fcb, 1596 &CurrentCompoundDirent->DirContext, 1597 &FileContext->CurrentDirent->DirContext ); 1598 1599 // 1600 // If we didn't find the entry then this is a corrupt directory. 1601 // 1602 1603 if (!FoundDirent) { 1604 1605 CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); 1606 } 1607 1608 // 1609 // Remember the dirent we just found. 1610 // 1611 1612 CurrentCompoundDirent = FileContext->CurrentDirent; 1613 FirstPass = FALSE; 1614 1615 // 1616 // Look up all of the dirent information for the given dirent. 1617 // 1618 1619 CdUpdateDirentFromRawDirent( IrpContext, 1620 Fcb, 1621 &CurrentCompoundDirent->DirContext, 1622 &CurrentCompoundDirent->Dirent ); 1623 1624 // 1625 // Set flag to show there were multiple extents. 1626 // 1627 1628 SetFlag( FileContext->Flags, FILE_CONTEXT_MULTIPLE_DIRENTS ); 1629 } 1630 1631 return; 1632 } 1633 1634 1635 VOID 1636 CdCleanupFileContext ( 1637 _In_ PIRP_CONTEXT IrpContext, 1638 _In_ PFILE_ENUM_CONTEXT FileContext 1639 ) 1640 1641 /*++ 1642 1643 Routine Description: 1644 1645 This routine is called to cleanup the enumeration context for a file 1646 search in a directory. We will unpin any remaining Bcbs and free 1647 any allocated buffers. 1648 1649 Arguments: 1650 1651 FileContext - Enumeration context for the file search. 1652 1653 Return Value: 1654 1655 None. 1656 1657 --*/ 1658 1659 { 1660 PCOMPOUND_DIRENT CurrentCompoundDirent; 1661 ULONG Count = 2; 1662 1663 PAGED_CODE(); 1664 1665 UNREFERENCED_PARAMETER( IrpContext ); 1666 1667 // 1668 // Cleanup the individual compound dirents. 1669 // 1670 1671 do { 1672 1673 CurrentCompoundDirent = &FileContext->Dirents[ Count ]; 1674 CdCleanupDirContext( IrpContext, &CurrentCompoundDirent->DirContext ); 1675 CdCleanupDirent( IrpContext, &CurrentCompoundDirent->Dirent ); 1676 1677 } while (Count--); 1678 1679 return; 1680 } 1681 1682 1683 // 1684 // Local support routine 1685 // 1686 1687 ULONG 1688 CdCheckRawDirentBounds ( 1689 _In_ PIRP_CONTEXT IrpContext, 1690 _In_ PDIRENT_ENUM_CONTEXT DirContext 1691 ) 1692 1693 /*++ 1694 1695 Routine Description: 1696 1697 This routine takes a Dirent enumeration context and computes the offset 1698 to the next dirent. A non-zero value indicates the offset within this 1699 sector. A zero value indicates to move to the next sector. If the 1700 current dirent does not fit within the sector then we will raise 1701 STATUS_CORRUPT. 1702 1703 Arguments: 1704 1705 DirContext - Enumeration context indicating the current position in 1706 the sector. 1707 1708 Return Value: 1709 1710 ULONG - Offset to the next dirent in this sector or zero if the 1711 next dirent is in the next sector. 1712 1713 This routine will raise on a dirent which does not fit into the 1714 described data buffer. 1715 1716 --*/ 1717 1718 { 1719 ULONG NextDirentOffset; 1720 PRAW_DIRENT RawDirent; 1721 1722 PAGED_CODE(); 1723 1724 UNREFERENCED_PARAMETER( IrpContext ); 1725 1726 // 1727 // We should always have at least a byte still available in the 1728 // current buffer. 1729 // 1730 1731 NT_ASSERT( (DirContext->DataLength - DirContext->SectorOffset) >= 1 ); 1732 1733 // 1734 // Get a pointer to the current dirent. 1735 // 1736 1737 RawDirent = CdRawDirent( IrpContext, DirContext ); 1738 1739 // 1740 // If the dirent length is non-zero then look at the current dirent. 1741 // 1742 1743 if (RawDirent->DirLen != 0) { 1744 1745 // 1746 // Check the following bound for the dirent length. 1747 // 1748 // - Fits in the available bytes in the sector. 1749 // - Is at least the minimal dirent size. 1750 // - Is large enough to hold the file name. 1751 // 1752 1753 if ((RawDirent->DirLen > (DirContext->DataLength - DirContext->SectorOffset)) || 1754 (RawDirent->DirLen < MIN_RAW_DIRENT_LEN) || 1755 (RawDirent->DirLen < (MIN_RAW_DIRENT_LEN - 1 + RawDirent->FileIdLen))) { 1756 1757 CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); 1758 } 1759 1760 // 1761 // Copy the dirent length field. 1762 // 1763 1764 NextDirentOffset = RawDirent->DirLen; 1765 1766 // 1767 // If we are exactly at the next sector then tell our caller by 1768 // returning zero. 1769 // 1770 1771 if (NextDirentOffset == (DirContext->DataLength - DirContext->SectorOffset)) { 1772 1773 NextDirentOffset = 0; 1774 } 1775 1776 } else { 1777 1778 NextDirentOffset = 0; 1779 } 1780 1781 return NextDirentOffset; 1782 } 1783 1784 1785 // 1786 // Local support routine 1787 // 1788 1789 XA_EXTENT_TYPE 1790 CdCheckForXAExtent ( 1791 _In_ PIRP_CONTEXT IrpContext, 1792 _In_ PRAW_DIRENT RawDirent, 1793 _Inout_ PDIRENT Dirent 1794 ) 1795 1796 /*++ 1797 1798 Routine Description: 1799 1800 This routine is called to scan through the system use area to test if 1801 the current dirent has the XA bit set. The bit in the in-memory 1802 dirent will be set as appropriate. 1803 1804 Arguments: 1805 1806 RawDirent - Pointer to the on-disk dirent. 1807 1808 Dirent - Pointer to the in-memory dirent. We will update this with the 1809 appropriate XA flag. 1810 1811 Return Value: 1812 1813 XA_EXTENT_TYPE - Type of physical extent for this on disk dirent. 1814 1815 --*/ 1816 1817 { 1818 XA_EXTENT_TYPE ExtentType = Form1Data; 1819 PSYSTEM_USE_XA SystemUseArea; 1820 1821 PAGED_CODE(); 1822 1823 UNREFERENCED_PARAMETER( IrpContext ); 1824 1825 // 1826 // Check if there is enough space for the XA system use area. 1827 // 1828 1829 if (Dirent->DirentLength - Dirent->SystemUseOffset >= sizeof( SYSTEM_USE_XA )) { 1830 1831 SystemUseArea = Add2Ptr( RawDirent, Dirent->SystemUseOffset, PSYSTEM_USE_XA ); 1832 1833 // 1834 // Check for a valid signature. 1835 // 1836 1837 if (SystemUseArea->Signature == SYSTEM_XA_SIGNATURE) { 1838 1839 // 1840 // Check for an audio track. 1841 // 1842 1843 if (FlagOn( SystemUseArea->Attributes, SYSTEM_USE_XA_DA )) { 1844 1845 ExtentType = CDAudio; 1846 1847 } else if (FlagOn( SystemUseArea->Attributes, SYSTEM_USE_XA_FORM2 )) { 1848 1849 // 1850 // Check for XA data. Note that a number of discs (video CDs) 1851 // have files marked as type XA Mode 2 Form 1 (2048 bytes of 1852 // user data), but actually record these sectors as Mode2 Form 2 1853 // (2352). We will fail to read these files, since for M2F1, 1854 // a normal read CD command is issued (as per SCSI specs). 1855 // 1856 1857 ExtentType = Mode2Form2Data; 1858 } 1859 1860 Dirent->XAAttributes = SystemUseArea->Attributes; 1861 Dirent->XAFileNumber = SystemUseArea->FileNumber; 1862 } 1863 } 1864 1865 Dirent->ExtentType = ExtentType; 1866 return ExtentType; 1867 } 1868 1869 1870 1871