1 /*++ 2 3 Copyright (c) 1989-2000 Microsoft Corporation 4 5 Module Name: 6 7 PathSup.c 8 9 Abstract: 10 11 This module implements the Path Table support routines for Cdfs. 12 13 The path table on a CDROM is a condensed summary of the entire 14 directory structure. It is stored on a number of contiguous sectors 15 on the disk. Each directory on the disk has an entry in the path 16 table. The entries are aligned on USHORT boundaries and MAY span 17 sector boundaries. The entries are stored as a breadth-first search. 18 19 The first entry in the table contains the entry for the root. The 20 next entries will consist of the contents of the root directory. The 21 next entries will consist of the all the directories at the next level 22 of the tree. The children of a given directory will be grouped together. 23 24 The directories are assigned ordinal numbers based on their position in 25 the path table. The root dirctory is assigned ordinal value 1. 26 27 Path table sectors: 28 29 Ordinal 1 2 3 4 5 6 30 +-----------+ 31 | Spanning | 32 | Sectors | 33 +----------------------------+ +------------------------+ 34 | | | | | | | | | 35 DirName | \ | a | b |c| | c | d | e | 36 | | | | | | | | | 37 Parent #| 1 | 1 | 1 | | | 2 | 2 | 3 | 38 +----------------------------+ +------------------------+ 39 40 Directory Tree: 41 42 \ (root) 43 44 / \ 45 / \ 46 a b 47 48 / \ \ 49 / \ \ 50 c d e 51 52 Path Table Entries: 53 54 - Position scan at known offset in the path table. Path Entry at 55 this offset must exist and is known to be valid. Used when 56 scanning for the children of a given directory. 57 58 - Position scan at known offset in the path table. Path Entry is 59 known to start at this location but the bounds must be checked 60 for validity. 61 62 - Move to next path entry in the table. 63 64 - Update a common path entry structure with the details of the 65 on-disk structure. This is used to smooth out the differences 66 in the on-disk structures. 67 68 - Update the filename in the in-memory path entry with the bytes 69 off the disk. For Joliet disks we will have 70 to convert to little endian. We assume that directories 71 don't have version numbers. 72 73 74 --*/ 75 76 #include "cdprocs.h" 77 78 // 79 // The Bug check file id for this module 80 // 81 82 #define BugCheckFileId (CDFS_BUG_CHECK_PATHSUP) 83 84 // 85 // Local macros 86 // 87 88 // 89 // PRAW_PATH_ENTRY 90 // CdRawPathEntry ( 91 // _In_ PIRP_CONTEXT IrpContext, 92 // _In_ PPATH_ENUM_CONTEXT PathContext 93 // ); 94 // 95 96 #define CdRawPathEntry(IC, PC) \ 97 Add2Ptr( (PC)->Data, (PC)->DataOffset, PRAW_PATH_ENTRY ) 98 99 // 100 // Local support routines 101 // 102 103 VOID 104 CdMapPathTableBlock ( 105 _In_ PIRP_CONTEXT IrpContext, 106 _In_ PFCB Fcb, 107 _In_ LONGLONG BaseOffset, 108 _Inout_ PPATH_ENUM_CONTEXT PathContext 109 ); 110 111 _Success_(return != FALSE) 112 BOOLEAN 113 CdUpdatePathEntryFromRawPathEntry ( 114 _In_ PIRP_CONTEXT IrpContext, 115 _In_ ULONG Ordinal, 116 _In_ BOOLEAN VerifyBounds, 117 _In_ PPATH_ENUM_CONTEXT PathContext, 118 _Out_ PPATH_ENTRY PathEntry 119 ); 120 121 #ifdef ALLOC_PRAGMA 122 #pragma alloc_text(PAGE, CdFindPathEntry) 123 #pragma alloc_text(PAGE, CdLookupPathEntry) 124 #pragma alloc_text(PAGE, CdLookupNextPathEntry) 125 #pragma alloc_text(PAGE, CdMapPathTableBlock) 126 #pragma alloc_text(PAGE, CdUpdatePathEntryFromRawPathEntry) 127 #pragma alloc_text(PAGE, CdUpdatePathEntryName) 128 #endif 129 130 131 VOID 132 CdLookupPathEntry ( 133 _In_ PIRP_CONTEXT IrpContext, 134 _In_ ULONG PathEntryOffset, 135 _In_ ULONG Ordinal, 136 _In_ BOOLEAN VerifyBounds, 137 _Inout_ PCOMPOUND_PATH_ENTRY CompoundPathEntry 138 ) 139 140 /*++ 141 142 Routine Description: 143 144 This routine is called to initiate a walk through a path table. We are 145 looking for a path table entry at location PathEntryOffset. 146 147 Arguments: 148 149 PathEntryOffset - This is our target point in the Path Table. We know that 150 a path entry must begin at this point although we may have to verify 151 the bounds. 152 153 Ordinal - Ordinal number for the directory at the PathEntryOffset above. 154 155 VerifyBounds - Indicates whether we need to check the validity of 156 this entry. 157 158 CompoundPathEntry - PathEnumeration context and in-memory path entry. This 159 has been initialized outside of this call. 160 161 Return Value: 162 163 None. 164 165 --*/ 166 167 { 168 PPATH_ENUM_CONTEXT PathContext = &CompoundPathEntry->PathContext; 169 LONGLONG CurrentBaseOffset; 170 171 PAGED_CODE(); 172 173 // 174 // Compute the starting base and starting path table offset. 175 // 176 177 CurrentBaseOffset = SectorTruncate( PathEntryOffset ); 178 179 // 180 // Map the next block in the Path Table. 181 // 182 183 CdMapPathTableBlock( IrpContext, 184 IrpContext->Vcb->PathTableFcb, 185 CurrentBaseOffset, 186 PathContext ); 187 188 // 189 // Set up our current offset into the Path Context. 190 // 191 192 PathContext->DataOffset = PathEntryOffset - PathContext->BaseOffset; 193 194 // 195 // Update the in-memory structure for this path entry. 196 // 197 198 (VOID) CdUpdatePathEntryFromRawPathEntry( IrpContext, 199 Ordinal, 200 VerifyBounds, 201 &CompoundPathEntry->PathContext, 202 &CompoundPathEntry->PathEntry ); 203 } 204 205 206 BOOLEAN 207 CdLookupNextPathEntry ( 208 _In_ PIRP_CONTEXT IrpContext, 209 _Inout_ PPATH_ENUM_CONTEXT PathContext, 210 _Inout_ PPATH_ENTRY PathEntry 211 ) 212 213 /*++ 214 215 Routine Description: 216 217 This routine is called to move to the next path table entry. We know 218 the offset and the length of the current entry. We start by computing 219 the offset of the next entry and determine if it is contained in the 220 table. Then we check to see if we need to move to the next sector in 221 the path table. We always map two sectors at a time so we don't 222 have to deal with any path entries which span sectors. We move to 223 the next sector if we are in the second sector of the current mapped 224 data block. 225 226 We look up the next entry and update the path entry structure with 227 the values out of the raw sector but don't update the CdName structure. 228 229 Arguments: 230 231 PathContext - Enumeration context for this scan of the path table. 232 233 PathEntry - In-memory representation of the on-disk path table entry. 234 235 Return Value: 236 237 BOOLEAN - TRUE if another entry is found, FALSE otherwise. 238 This routine may raise on error. 239 240 --*/ 241 242 { 243 LONGLONG CurrentBaseOffset; 244 245 PAGED_CODE(); 246 247 // 248 // Get the offset of the next path entry within the current 249 // data block. 250 // 251 252 PathContext->DataOffset += PathEntry->PathEntryLength; 253 254 // 255 // If we are in the last data block then check if we are beyond the 256 // end of the file. 257 // 258 259 if (PathContext->LastDataBlock) { 260 261 if (PathContext->DataOffset >= PathContext->DataLength) { 262 263 return FALSE; 264 } 265 266 // 267 // If we are not in the last data block of the path table and 268 // this offset is in the second sector then move to the next 269 // data block. 270 // 271 272 } else if (PathContext->DataOffset >= SECTOR_SIZE) { 273 274 CurrentBaseOffset = PathContext->BaseOffset + SECTOR_SIZE; 275 276 CdMapPathTableBlock( IrpContext, 277 IrpContext->Vcb->PathTableFcb, 278 CurrentBaseOffset, 279 PathContext ); 280 281 // 282 // Set up our current offset into the Path Context. 283 // 284 285 PathContext->DataOffset -= SECTOR_SIZE; 286 } 287 288 // 289 // Now update the path entry with the values from the on-disk 290 // structure. 291 // 292 293 return CdUpdatePathEntryFromRawPathEntry( IrpContext, 294 PathEntry->Ordinal + 1, 295 TRUE, 296 PathContext, 297 PathEntry ); 298 } 299 300 _Success_(return != FALSE) 301 BOOLEAN 302 CdFindPathEntry ( 303 _In_ PIRP_CONTEXT IrpContext, 304 _In_ PFCB ParentFcb, 305 _In_ PCD_NAME DirName, 306 _In_ BOOLEAN IgnoreCase, 307 _Inout_ PCOMPOUND_PATH_ENTRY CompoundPathEntry 308 ) 309 310 /*++ 311 312 Routine Description: 313 314 This routine will walk through the path table looking for a matching entry for DirName 315 among the child directories of the ParentFcb. 316 317 Arguments: 318 319 ParentFcb - This is the directory we are examining. We know the ordinal and path table 320 offset for this directory in the path table. If this is the first scan for this 321 Fcb we will update the first child offset for this directory in the path table. 322 323 DirName - This is the name we are searching for. This name will not contain wildcard 324 characters. The name will also not have a version string. 325 326 IgnoreCase - Indicates if this search is exact or ignore case. 327 328 CompoundPathEntry - Complete path table enumeration structure. We will have initialized 329 it for the search on entry. This will be positioned at the matching name if found. 330 331 Return Value: 332 333 BOOLEAN - TRUE if matching entry found, FALSE otherwise. 334 335 --*/ 336 337 { 338 BOOLEAN Found = FALSE; 339 BOOLEAN UpdateChildOffset = TRUE; 340 341 ULONG StartingOffset; 342 ULONG StartingOrdinal; 343 344 PAGED_CODE(); 345 346 // 347 // Position ourselves at either the first child or at the directory itself. 348 // Lock the Fcb to get this value and remember whether to update with the first 349 // child. 350 // 351 352 StartingOffset = CdQueryFidPathTableOffset( ParentFcb->FileId ); 353 StartingOrdinal = ParentFcb->Ordinal; 354 355 // 356 // ISO 9660 9.4.4 restricts the backpointer from child to parent in a 357 // pathtable entry to 16bits. Although we internally store ordinals 358 // as 32bit values, it is impossible to search for the children of a 359 // directory whose ordinal value is greater than MAXUSHORT. Media that 360 // could induce such a search is illegal. 361 // 362 // Note that it is not illegal to have more than MAXUSHORT directories. 363 // 364 365 if (ParentFcb->Ordinal > MAXUSHORT) { 366 367 CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR ); 368 } 369 370 CdLockFcb( IrpContext, ParentFcb ); 371 372 if (ParentFcb->ChildPathTableOffset != 0) { 373 374 StartingOffset = ParentFcb->ChildPathTableOffset; 375 StartingOrdinal = ParentFcb->ChildOrdinal; 376 UpdateChildOffset = FALSE; 377 378 } else if (ParentFcb == ParentFcb->Vcb->RootIndexFcb) { 379 380 UpdateChildOffset = FALSE; 381 } 382 383 CdUnlockFcb( IrpContext, ParentFcb ); 384 385 CdLookupPathEntry( IrpContext, StartingOffset, StartingOrdinal, FALSE, CompoundPathEntry ); 386 387 // 388 // Loop until we find a match or are beyond the children for this directory. 389 // 390 391 do { 392 393 // 394 // If we are beyond this directory then return FALSE. 395 // 396 397 if (CompoundPathEntry->PathEntry.ParentOrdinal > ParentFcb->Ordinal) { 398 399 // 400 // Update the Fcb with the offsets for the children in the path table. 401 // 402 403 if (UpdateChildOffset) { 404 405 CdLockFcb( IrpContext, ParentFcb ); 406 407 ParentFcb->ChildPathTableOffset = StartingOffset; 408 ParentFcb->ChildOrdinal = StartingOrdinal; 409 410 CdUnlockFcb( IrpContext, ParentFcb ); 411 } 412 413 break; 414 } 415 416 // 417 // If we are within the children of this directory then check for a match. 418 // 419 420 if (CompoundPathEntry->PathEntry.ParentOrdinal == ParentFcb->Ordinal) { 421 422 // 423 // Update the child offset if not yet done. 424 // 425 426 if (UpdateChildOffset) { 427 428 CdLockFcb( IrpContext, ParentFcb ); 429 430 ParentFcb->ChildPathTableOffset = CompoundPathEntry->PathEntry.PathTableOffset; 431 ParentFcb->ChildOrdinal = CompoundPathEntry->PathEntry.Ordinal; 432 433 CdUnlockFcb( IrpContext, ParentFcb ); 434 435 UpdateChildOffset = FALSE; 436 } 437 438 // 439 // Update the name in the path entry. 440 // 441 442 CdUpdatePathEntryName( IrpContext, &CompoundPathEntry->PathEntry, IgnoreCase ); 443 444 // 445 // Now compare the names for an exact match. 446 // 447 448 if (CdIsNameInExpression( IrpContext, 449 &CompoundPathEntry->PathEntry.CdCaseDirName, 450 DirName, 451 0, 452 FALSE )) { 453 454 // 455 // Let our caller know we have a match. 456 // 457 458 Found = TRUE; 459 break; 460 } 461 } 462 463 // 464 // Go to the next entry in the path table. Remember the current position 465 // in the event we update the Fcb. 466 // 467 468 StartingOffset = CompoundPathEntry->PathEntry.PathTableOffset; 469 StartingOrdinal = CompoundPathEntry->PathEntry.Ordinal; 470 471 } while (CdLookupNextPathEntry( IrpContext, 472 &CompoundPathEntry->PathContext, 473 &CompoundPathEntry->PathEntry )); 474 475 return Found; 476 } 477 478 479 // 480 // Local support routine 481 // 482 483 VOID 484 CdMapPathTableBlock ( 485 _In_ PIRP_CONTEXT IrpContext, 486 _In_ PFCB Fcb, 487 _In_ LONGLONG BaseOffset, 488 _Inout_ PPATH_ENUM_CONTEXT PathContext 489 ) 490 491 /*++ 492 493 Routine Description: 494 495 This routine is called to map (or allocate and copy) the next 496 data block in the path table. We check if the next block will 497 span a view boundary and allocate an auxilary buffer in that case. 498 499 Arguments: 500 501 Fcb - This is the Fcb for the Path Table. 502 503 BaseOffset - Offset of the first sector to map. This will be on a 504 sector boundary. 505 506 PathContext - Enumeration context to update in this routine. 507 508 Return Value: 509 510 None. 511 512 --*/ 513 514 { 515 ULONG CurrentLength; 516 ULONG SectorSize; 517 ULONG DataOffset; 518 ULONG PassCount; 519 PVOID Sector; 520 521 PAGED_CODE(); 522 523 UNREFERENCED_PARAMETER( IrpContext ); 524 525 // 526 // Map the new block and set the enumeration context to this 527 // point. Allocate an auxilary buffer if necessary. 528 // 529 530 CurrentLength = 2 * SECTOR_SIZE; 531 532 if (CurrentLength >= (ULONG) (Fcb->FileSize.QuadPart - BaseOffset)) { 533 534 CurrentLength = (ULONG) (Fcb->FileSize.QuadPart - BaseOffset); 535 536 // 537 // We know this is the last data block for this 538 // path table. 539 // 540 541 PathContext->LastDataBlock = TRUE; 542 } 543 544 // 545 // Set context values. 546 // 547 548 PathContext->BaseOffset = (ULONG) BaseOffset; 549 PathContext->DataLength = CurrentLength; 550 551 // 552 // Drop the previous sector's mapping 553 // 554 555 CdUnpinData( IrpContext, &PathContext->Bcb ); 556 557 // 558 // Check if spanning a view section. The following must 559 // be true before we take this step. 560 // 561 // Data length is more than one sector. 562 // Starting offset must be one sector before the 563 // cache manager VACB boundary. 564 // 565 566 if ((CurrentLength > SECTOR_SIZE) && 567 (FlagOn( ((ULONG) BaseOffset), VACB_MAPPING_MASK ) == LAST_VACB_SECTOR_OFFSET )) { 568 569 // 570 // Map each sector individually and store into an auxilary 571 // buffer. 572 // 573 574 SectorSize = SECTOR_SIZE; 575 DataOffset = 0; 576 PassCount = 2; 577 578 PathContext->Data = FsRtlAllocatePoolWithTag( CdPagedPool, 579 CurrentLength, 580 TAG_SPANNING_PATH_TABLE ); 581 PathContext->AllocatedData = TRUE; 582 583 while (PassCount--) { 584 585 CcMapData( Fcb->FileObject, 586 (PLARGE_INTEGER) &BaseOffset, 587 SectorSize, 588 TRUE, 589 &PathContext->Bcb, 590 &Sector ); 591 592 RtlCopyMemory( Add2Ptr( PathContext->Data, DataOffset, PVOID ), 593 Sector, 594 SectorSize ); 595 596 CdUnpinData( IrpContext, &PathContext->Bcb ); 597 598 BaseOffset += SECTOR_SIZE; 599 SectorSize = CurrentLength - SECTOR_SIZE; 600 DataOffset = SECTOR_SIZE; 601 } 602 603 // 604 // Otherwise we can just map the data into the cache. 605 // 606 607 } else { 608 609 // 610 // There is a slight chance that we have allocated an 611 // auxilary buffer on the previous sector. 612 // 613 614 if (PathContext->AllocatedData) { 615 616 CdFreePool( &PathContext->Data ); 617 PathContext->AllocatedData = FALSE; 618 } 619 620 CcMapData( Fcb->FileObject, 621 (PLARGE_INTEGER) &BaseOffset, 622 CurrentLength, 623 TRUE, 624 &PathContext->Bcb, 625 &PathContext->Data ); 626 } 627 628 return; 629 } 630 631 632 // 633 // Local support routine 634 // 635 _Success_(return != FALSE) 636 BOOLEAN 637 CdUpdatePathEntryFromRawPathEntry ( 638 _In_ PIRP_CONTEXT IrpContext, 639 _In_ ULONG Ordinal, 640 _In_ BOOLEAN VerifyBounds, 641 _In_ PPATH_ENUM_CONTEXT PathContext, 642 _Out_ PPATH_ENTRY PathEntry 643 ) 644 645 /*++ 646 647 Routine Description: 648 649 This routine is called to update the in-memory Path Entry from the on-disk 650 path entry. We also do a careful check of the bounds if requested and we 651 are in the last data block of the path table. 652 653 Arguments: 654 655 Ordinal - Ordinal number for this directory. 656 657 VerifyBounds - Check that the current raw Path Entry actually fits 658 within the data block. 659 660 PathContext - Current path table enumeration context. 661 662 PathEntry - Pointer to the in-memory path entry structure. 663 664 Return Value: 665 666 TRUE if updated ok, 667 FALSE if we've hit the end of the pathtable - zero name length && PT size is a multiple 668 of blocksize. This is a workaround for some Video CDs. Win 9x works around this. 669 670 This routine may raise. 671 672 --*/ 673 674 { 675 PRAW_PATH_ENTRY RawPathEntry = CdRawPathEntry( IrpContext, PathContext ); 676 ULONG RemainingDataLength; 677 678 PAGED_CODE(); 679 680 // 681 // Check for a name length of zero. This is the first byte of the record, 682 // and there must be at least one byte remaining in the buffer else we 683 // wouldn't be here (caller would have spotted buffer end). 684 // 685 686 PathEntry->DirNameLen = CdRawPathIdLen( IrpContext, RawPathEntry ); 687 688 if (0 == PathEntry->DirNameLen) { 689 690 // 691 // If we are in the last block, and the path table size (ie last block) is a 692 // multiple of block size, then we will consider this the end of the path table 693 // rather than raising an error. Workaround for NTI Cd Maker video CDs which 694 // round path table length to blocksize multiple. In all other cases we consider 695 // a zero length name to be corruption. 696 // 697 698 if ( PathContext->LastDataBlock && 699 (0 == BlockOffset( IrpContext->Vcb, PathContext->DataLength))) { 700 701 return FALSE; 702 } 703 704 CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR ); 705 } 706 707 // 708 // Check if we should verify the path entry. If we are not in the last 709 // data block then there is nothing to check. 710 // 711 712 if (PathContext->LastDataBlock && VerifyBounds) { 713 714 // 715 // Quick check to see if the maximum size is still available. This 716 // will handle most cases and we don't need to access any of the 717 // fields. 718 // 719 720 RemainingDataLength = PathContext->DataLength - PathContext->DataOffset; 721 722 if (RemainingDataLength < sizeof( RAW_PATH_ENTRY )) { 723 724 // 725 // Make sure the remaining bytes hold the path table entries. 726 // Do the following checks. 727 // 728 // - A minimal path table entry will fit (and then check) 729 // - This path table entry (with dir name) will fit. 730 // 731 732 if ((RemainingDataLength < MIN_RAW_PATH_ENTRY_LEN) || 733 (RemainingDataLength < (ULONG) (CdRawPathIdLen( IrpContext, RawPathEntry ) + MIN_RAW_PATH_ENTRY_LEN - 1))) { 734 735 CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR ); 736 } 737 } 738 } 739 740 // 741 // The ordinal number of this directory is passed in. 742 // Compute the path table offset of this entry. 743 // 744 745 PathEntry->Ordinal = Ordinal; 746 PathEntry->PathTableOffset = PathContext->BaseOffset + PathContext->DataOffset; 747 748 // 749 // We know we can safely access all of the fields of the raw path table at 750 // this point. 751 752 // 753 // Bias the disk offset by the number of logical blocks 754 // 755 756 CopyUchar4( &PathEntry->DiskOffset, CdRawPathLoc( IrpContext, RawPathEntry )); 757 758 PathEntry->DiskOffset += CdRawPathXar( IrpContext, RawPathEntry ); 759 760 CopyUchar2( &PathEntry->ParentOrdinal, &RawPathEntry->ParentNum ); 761 762 PathEntry->PathEntryLength = PathEntry->DirNameLen + MIN_RAW_PATH_ENTRY_LEN - 1; 763 764 // 765 // Align the path entry length on a ushort boundary. 766 // 767 768 PathEntry->PathEntryLength = WordAlign( PathEntry->PathEntryLength ); 769 770 PathEntry->DirName = (PCHAR)RawPathEntry->DirId; 771 772 return TRUE; 773 } 774 775 776 // 777 // Local support routine 778 // 779 780 VOID 781 CdUpdatePathEntryName ( 782 _In_ PIRP_CONTEXT IrpContext, 783 _Inout_ PPATH_ENTRY PathEntry, 784 _In_ BOOLEAN IgnoreCase 785 ) 786 787 /*++ 788 789 Routine Description: 790 791 This routine will store the directory name into the CdName in the 792 path entry. If this is a Joliet name then we will make sure we have 793 an allocated buffer and need to convert from big endian to little 794 endian. We also correctly update the case name. If this operation is ignore 795 case then we need an auxilary buffer for the name. 796 797 For an Ansi disk we can use the name from the disk for the exact case. We only 798 need to allocate a buffer for the ignore case name. The on-disk representation of 799 a Unicode name is useless for us. In this case we will need a name buffer for 800 both names. We store a buffer in the PathEntry which can hold two 8.3 unicode 801 names. This means we will almost never need to allocate a buffer in the Ansi case 802 (we only need one buffer and already have 48 characters). 803 804 Arguments: 805 806 PathEntry - Pointer to a path entry structure. We have already updated 807 this path entry with the values from the raw path entry. 808 809 Return Value: 810 811 None. 812 813 --*/ 814 815 { 816 ULONG Length; 817 NTSTATUS Status; 818 819 PAGED_CODE(); 820 821 // 822 // Check if this is a self entry. We use a fixed string for this. 823 // 824 // Self-Entry - Length is 1, value is 0. 825 // 826 827 if ((*PathEntry->DirName == 0) && 828 (PathEntry->DirNameLen == 1)) { 829 830 // 831 // There should be no allocated buffers. 832 // 833 834 NT_ASSERT( !FlagOn( PathEntry->Flags, PATH_ENTRY_FLAG_ALLOC_BUFFER )); 835 836 // 837 // Now use one of the hard coded directory names. 838 // 839 840 PathEntry->CdDirName.FileName = CdUnicodeDirectoryNames[0]; 841 842 // 843 // Show that there is no version number. 844 // 845 846 PathEntry->CdDirName.VersionString.Length = 0; 847 848 // 849 // The case name is identical. 850 // 851 852 PathEntry->CdCaseDirName = PathEntry->CdDirName; 853 854 // 855 // Return now. 856 // 857 858 return; 859 } 860 861 // 862 // Compute how large a buffer we will need. If this is an ignore 863 // case operation then we will want a double size buffer. If the disk is not 864 // a Joliet disk then we might need two bytes for each byte in the name. 865 // 866 867 Length = PathEntry->DirNameLen; 868 869 if (IgnoreCase) { 870 871 Length *= 2; 872 } 873 874 if (!FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_JOLIET )) { 875 876 Length *= sizeof( WCHAR ); 877 } 878 879 // 880 // Now decide if we need to allocate a new buffer. We will if 881 // this name won't fit in the embedded name buffer and it is 882 // larger than the current allocated buffer. We always use the 883 // allocated buffer if present. 884 // 885 // If we haven't allocated a buffer then use the embedded buffer if the data 886 // will fit. This is the typical case. 887 // 888 889 if (!FlagOn( PathEntry->Flags, PATH_ENTRY_FLAG_ALLOC_BUFFER ) && 890 (Length <= sizeof( PathEntry->NameBuffer ))) { 891 892 PathEntry->CdDirName.FileName.MaximumLength = sizeof( PathEntry->NameBuffer ); 893 PathEntry->CdDirName.FileName.Buffer = PathEntry->NameBuffer; 894 895 } else { 896 897 // 898 // We need to use an allocated buffer. Check if the current buffer 899 // is large enough. 900 // 901 902 if (Length > PathEntry->CdDirName.FileName.MaximumLength) { 903 904 // 905 // Free any allocated buffer. 906 // 907 908 if (FlagOn( PathEntry->Flags, PATH_ENTRY_FLAG_ALLOC_BUFFER )) { 909 910 CdFreePool( &PathEntry->CdDirName.FileName.Buffer ); 911 ClearFlag( PathEntry->Flags, PATH_ENTRY_FLAG_ALLOC_BUFFER ); 912 } 913 914 PathEntry->CdDirName.FileName.Buffer = FsRtlAllocatePoolWithTag( CdPagedPool, 915 Length, 916 TAG_PATH_ENTRY_NAME ); 917 918 SetFlag( PathEntry->Flags, PATH_ENTRY_FLAG_ALLOC_BUFFER ); 919 920 PathEntry->CdDirName.FileName.MaximumLength = (USHORT) Length; 921 } 922 } 923 924 // 925 // We now have a buffer for the name. We need to either convert the on-disk bigendian 926 // to little endian or covert the name to Unicode. 927 // 928 929 if (!FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_JOLIET )) { 930 931 Status = RtlOemToUnicodeN( PathEntry->CdDirName.FileName.Buffer, 932 PathEntry->CdDirName.FileName.MaximumLength, 933 &Length, 934 PathEntry->DirName, 935 PathEntry->DirNameLen ); 936 937 NT_ASSERT( Status == STATUS_SUCCESS ); 938 __analysis_assert( Status == STATUS_SUCCESS ); 939 PathEntry->CdDirName.FileName.Length = (USHORT) Length; 940 941 } else { 942 943 // 944 // Convert this string to little endian. 945 // 946 947 CdConvertBigToLittleEndian( IrpContext, 948 PathEntry->DirName, 949 PathEntry->DirNameLen, 950 (PCHAR) PathEntry->CdDirName.FileName.Buffer ); 951 952 PathEntry->CdDirName.FileName.Length = (USHORT) PathEntry->DirNameLen; 953 } 954 955 // 956 // There is no version string. 957 // 958 959 PathEntry->CdDirName.VersionString.Length = 960 PathEntry->CdCaseDirName.VersionString.Length = 0; 961 962 // 963 // If the name string ends with a period then knock off the last 964 // character. 965 // 966 967 if (PathEntry->CdDirName.FileName.Buffer[(PathEntry->CdDirName.FileName.Length - sizeof( WCHAR )) / 2] == L'.') { 968 969 // 970 // Shrink the filename length. 971 // 972 973 PathEntry->CdDirName.FileName.Length -= sizeof( WCHAR ); 974 } 975 976 // 977 // Update the case name buffer if necessary. If this is an exact case 978 // operation then just copy the exact case string. 979 // 980 981 if (IgnoreCase) { 982 983 PathEntry->CdCaseDirName.FileName.Buffer = Add2Ptr( PathEntry->CdDirName.FileName.Buffer, 984 PathEntry->CdDirName.FileName.MaximumLength / 2, 985 PWCHAR); 986 987 PathEntry->CdCaseDirName.FileName.MaximumLength = PathEntry->CdDirName.FileName.MaximumLength / 2; 988 989 CdUpcaseName( IrpContext, 990 &PathEntry->CdDirName, 991 &PathEntry->CdCaseDirName ); 992 993 } else { 994 995 PathEntry->CdCaseDirName = PathEntry->CdDirName; 996 } 997 998 return; 999 } 1000 1001 1002