1 /*++ 2 3 Copyright (c) 1991-2000 Microsoft Corporation 4 5 Module Name: 6 7 NameSup.c 8 9 Abstract: 10 11 This module implements the Cdfs Name support routines 12 13 14 --*/ 15 16 #include "cdprocs.h" 17 18 // 19 // The Bug check file id for this module 20 // 21 22 #define BugCheckFileId (CDFS_BUG_CHECK_NAMESUP) 23 24 #ifdef ALLOC_PRAGMA 25 #pragma alloc_text(PAGE, CdConvertBigToLittleEndian) 26 #pragma alloc_text(PAGE, CdConvertNameToCdName) 27 #pragma alloc_text(PAGE, CdDissectName) 28 #pragma alloc_text(PAGE, CdGenerate8dot3Name) 29 #pragma alloc_text(PAGE, CdFullCompareNames) 30 #pragma alloc_text(PAGE, CdIsLegalName) 31 #pragma alloc_text(PAGE, CdIs8dot3Name) 32 #pragma alloc_text(PAGE, CdIsNameInExpression) 33 #pragma alloc_text(PAGE, CdShortNameDirentOffset) 34 #pragma alloc_text(PAGE, CdUpcaseName) 35 #endif 36 37 38 _Post_satisfies_(_Old_(CdName->FileName.Length) >= 39 CdName->FileName.Length + CdName->VersionString.Length) 40 VOID 41 CdConvertNameToCdName ( 42 _In_ PIRP_CONTEXT IrpContext, 43 _Inout_ PCD_NAME CdName 44 ) 45 46 /*++ 47 48 Routine Description: 49 50 This routine is called to convert a string of bytes into a CdName. 51 52 The full name is already in the CdName structure in the FileName field. 53 We split this into the filename and version strings. 54 55 Arguments: 56 57 CdName - Pointer to CdName structure to update. 58 59 Return Value: 60 61 None. 62 63 --*/ 64 65 { 66 ULONG NameLength = 0; 67 PWCHAR CurrentCharacter = CdName->FileName.Buffer; 68 69 PAGED_CODE(); 70 71 UNREFERENCED_PARAMETER( IrpContext ); 72 73 // 74 // Look for a separator character. 75 // 76 77 while ((NameLength < CdName->FileName.Length) && 78 (*CurrentCharacter != L';')) { 79 80 CurrentCharacter += 1; 81 NameLength += 2; 82 } 83 84 // 85 // If there is at least one more character after a possible separator then it 86 // and all following characters are part of the version string. 87 // 88 89 CdName->VersionString.Length = 0; 90 if (NameLength + sizeof( WCHAR ) < CdName->FileName.Length) { 91 92 CdName->VersionString.MaximumLength = 93 CdName->VersionString.Length = (USHORT) (CdName->FileName.Length - NameLength - sizeof( WCHAR )); 94 CdName->VersionString.Buffer = Add2Ptr( CdName->FileName.Buffer, 95 NameLength + sizeof( WCHAR ), 96 PWCHAR ); 97 } 98 99 // 100 // Now update the filename portion of the name. 101 // 102 103 CdName->FileName.Length = (USHORT) NameLength; 104 105 return; 106 } 107 108 109 VOID 110 CdConvertBigToLittleEndian ( 111 _In_ PIRP_CONTEXT IrpContext, 112 _In_reads_bytes_(ByteCount) PCHAR BigEndian, 113 _In_ ULONG ByteCount, 114 _Out_writes_bytes_(ByteCount) PCHAR LittleEndian 115 ) 116 117 /*++ 118 119 Routine Description: 120 121 This routine is called to convert a unicode string in big endian to 122 little endian. We start by copying all of the source bytes except 123 the first. This will put the low order bytes in the correct position. 124 We then copy each high order byte in its correct position. 125 126 Arguments: 127 128 BigEndian - Pointer to the string of big endian characters. 129 130 ByteCount - Number of unicode characters in this string. 131 132 LittleEndian - Pointer to array to store the little endian characters. 133 134 Return Value: 135 136 None. 137 138 --*/ 139 140 { 141 ULONG RemainingByteCount = ByteCount; 142 143 PCHAR Source = BigEndian; 144 PCHAR Destination = LittleEndian; 145 146 PAGED_CODE(); 147 148 // 149 // If the byte count isn't an even number then the disk is corrupt. 150 // 151 152 if (FlagOn( ByteCount, 1 )) { 153 154 CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR ); 155 } 156 157 // 158 // Start by copy the low-order bytes into the correct position. Do 159 // this by skipping the first byte in the BigEndian string. 160 // 161 162 RtlCopyMemory( Destination, 163 Source + 1, 164 RemainingByteCount - 1 ); 165 166 // 167 // Now move the high-order bytes into position. 168 // 169 170 Destination += 1; 171 172 while (RemainingByteCount != 0) { 173 174 #ifdef _MSC_VER 175 #pragma prefast(push) 176 #pragma prefast(suppress:26014, "RemainingByteCount is even") 177 #endif 178 *Destination = *Source; 179 #ifdef _MSC_VER 180 #pragma prefast(pop) 181 #endif 182 183 Source += 2; 184 Destination += 2; 185 186 RemainingByteCount -= 2; 187 } 188 189 return; 190 } 191 192 193 VOID 194 CdUpcaseName ( 195 _In_ PIRP_CONTEXT IrpContext, 196 _In_ PCD_NAME Name, 197 _Inout_ PCD_NAME UpcaseName 198 ) 199 200 /*++ 201 202 Routine Description: 203 204 This routine is called to upcase a CdName structure. We will do both 205 the filename and version strings. 206 207 Arguments: 208 209 Name - This is the mixed case version of the name. 210 211 UpcaseName - This is the destination for the upcase operation. 212 213 Return Value: 214 215 None. This routine will raise all errors. 216 217 --*/ 218 219 { 220 NTSTATUS Status; 221 222 PAGED_CODE(); 223 224 UNREFERENCED_PARAMETER( IrpContext ); 225 226 // 227 // If the name structures are different then initialize the different components. 228 // 229 230 if (Name != UpcaseName) { 231 232 // 233 // Initialize the version string portion of the name. 234 // 235 236 UpcaseName->VersionString.Length = 0; 237 238 if (Name->VersionString.Length != 0) { 239 240 UpcaseName->VersionString.MaximumLength = 241 UpcaseName->VersionString.Length = Name->VersionString.Length; 242 243 // 244 // Initially set the buffer to point to where we need to insert 245 // the separator. 246 // 247 248 UpcaseName->VersionString.Buffer = Add2Ptr( UpcaseName->FileName.Buffer, 249 Name->FileName.Length, 250 PWCHAR ); 251 252 // 253 // We are currently pointing to the location to store the separator. 254 // Store the ';' and then move to the next character to 255 // copy the data. 256 // 257 258 #ifdef _MSC_VER 259 #pragma prefast( suppress:26015, "CD_NAME structures have two UNICODE_STRING structures pointing to the same allocation. there is no way to tell prefast this is the case and that the allocation is always big enough."); 260 #endif 261 *(UpcaseName->VersionString.Buffer) = L';'; 262 263 UpcaseName->VersionString.Buffer += 1; 264 } 265 } 266 267 // 268 // Upcase the string using the correct upcase routine. 269 // 270 271 Status = RtlUpcaseUnicodeString( &UpcaseName->FileName, 272 &Name->FileName, 273 FALSE ); 274 275 // 276 // This should never fail. 277 // 278 279 NT_ASSERT( Status == STATUS_SUCCESS ); 280 __analysis_assert( Status == STATUS_SUCCESS ); 281 282 if (Name->VersionString.Length != 0) { 283 284 Status = RtlUpcaseUnicodeString( &UpcaseName->VersionString, 285 &Name->VersionString, 286 FALSE ); 287 288 // 289 // This should never fail. 290 // 291 292 NT_ASSERT( Status == STATUS_SUCCESS ); 293 __analysis_assert( Status == STATUS_SUCCESS ); 294 } 295 296 return; 297 } 298 299 300 VOID 301 CdDissectName ( 302 _In_ PIRP_CONTEXT IrpContext, 303 _Inout_ PUNICODE_STRING RemainingName, 304 _Out_ PUNICODE_STRING FinalName 305 ) 306 307 /*++ 308 309 Routine Description: 310 311 This routine is called to strip off leading components of the name strings. We search 312 for either the end of the string or separating characters. The input remaining 313 name strings should have neither a trailing or leading backslash. 314 315 Arguments: 316 317 RemainingName - Remaining name. 318 319 FinalName - Location to store next component of name. 320 321 Return Value: 322 323 None. 324 325 --*/ 326 327 { 328 ULONG NameLength; 329 PWCHAR NextWchar; 330 331 PAGED_CODE(); 332 333 UNREFERENCED_PARAMETER( IrpContext ); 334 335 // 336 // Find the offset of the next component separators. 337 // 338 339 for (NameLength = 0, NextWchar = RemainingName->Buffer; 340 (NameLength < RemainingName->Length) && (*NextWchar != L'\\'); 341 NameLength += sizeof( WCHAR) , NextWchar += 1); 342 343 // 344 // Adjust all the strings by this amount. 345 // 346 347 FinalName->Buffer = RemainingName->Buffer; 348 349 FinalName->MaximumLength = FinalName->Length = (USHORT) NameLength; 350 351 // 352 // If this is the last component then set the RemainingName lengths to zero. 353 // 354 355 if (NameLength == RemainingName->Length) { 356 357 RemainingName->Length = 0; 358 359 // 360 // Otherwise we adjust the string by this amount plus the separating character. 361 // 362 363 } else { 364 365 RemainingName->MaximumLength -= (USHORT) (NameLength + sizeof( WCHAR )); 366 RemainingName->Length -= (USHORT) (NameLength + sizeof( WCHAR )); 367 RemainingName->Buffer = Add2Ptr( RemainingName->Buffer, 368 NameLength + sizeof( WCHAR ), 369 PWCHAR ); 370 } 371 372 return; 373 } 374 375 376 BOOLEAN 377 CdIsLegalName ( 378 _In_ PIRP_CONTEXT IrpContext, 379 _In_ PUNICODE_STRING FileName 380 ) 381 382 /*++ 383 384 Routine Description: 385 386 This routine checks if the name is a legal ISO 9660 name. 387 388 Arguments: 389 390 FileName - String of bytes containing the name. 391 392 Return Value: 393 394 BOOLEAN - TRUE if this name is a legal, FALSE otherwise. 395 396 --*/ 397 398 { 399 PWCHAR Wchar; 400 401 PAGED_CODE(); 402 403 UNREFERENCED_PARAMETER( IrpContext ); 404 405 // 406 // Check if name corresponds to a legal file name. 407 // 408 409 for (Wchar = FileName->Buffer; 410 Wchar < Add2Ptr( FileName->Buffer, FileName->Length, PWCHAR ); 411 Wchar++) { 412 413 if ((*Wchar < 0xff) && 414 !FsRtlIsAnsiCharacterLegalHpfs( *Wchar, FALSE ) && 415 (*Wchar != L'"') && 416 (*Wchar != L'<') && 417 (*Wchar != L'>') && 418 (*Wchar != L'|')) { 419 420 return FALSE; 421 } 422 } 423 424 return TRUE; 425 } 426 427 428 BOOLEAN 429 CdIs8dot3Name ( 430 _In_ PIRP_CONTEXT IrpContext, 431 _In_ UNICODE_STRING FileName 432 ) 433 434 /*++ 435 436 Routine Description: 437 438 This routine checks if the name follows the 8.3 name conventions. We check for 439 the name length and whether the characters are valid. 440 441 Arguments: 442 443 FileName - String of bytes containing the name. 444 445 Return Value: 446 447 BOOLEAN - TRUE if this name is a legal 8.3 name, FALSE otherwise. 448 449 --*/ 450 451 { 452 CHAR DbcsNameBuffer[ BYTE_COUNT_8_DOT_3 ]; 453 STRING DbcsName = {0}; 454 455 PWCHAR NextWchar; 456 ULONG Count; 457 458 ULONG DotCount = 0; 459 BOOLEAN LastCharDot = FALSE; 460 461 PAGED_CODE(); 462 463 UNREFERENCED_PARAMETER( IrpContext ); 464 465 // 466 // The length must be less than 24 bytes. 467 // 468 469 NT_ASSERT( FileName.Length != 0 ); 470 if (FileName.Length > BYTE_COUNT_8_DOT_3) { 471 472 return FALSE; 473 } 474 475 // 476 // Walk though and check for a space character. 477 // 478 479 NextWchar = FileName.Buffer; 480 Count = 0; 481 482 do { 483 484 // 485 // No spaces allowed. 486 // 487 488 if (*NextWchar == L' ') { return FALSE; } 489 490 if (*NextWchar == L'.') { 491 492 // 493 // Not an 8.3 name if more than 1 dot or more than 8 characters 494 // remaining. (It is legal for the dot to be in the ninth 495 // position) 496 // 497 498 if ((DotCount > 0) || 499 (Count > 8 * sizeof( WCHAR ))) { 500 501 return FALSE; 502 } 503 504 DotCount += 1; 505 LastCharDot = TRUE; 506 507 } else { 508 509 LastCharDot = FALSE; 510 } 511 512 Count += 2; 513 NextWchar += 1; 514 515 } while (Count < FileName.Length); 516 517 // 518 // Go ahead and truncate the dot if at the end. 519 // 520 521 if (LastCharDot) { 522 523 FileName.Length -= sizeof( WCHAR ); 524 } 525 526 // 527 // Create an Oem name to use to check for a valid short name. 528 // 529 530 DbcsName.MaximumLength = BYTE_COUNT_8_DOT_3; 531 DbcsName.Buffer = DbcsNameBuffer; 532 533 if (!NT_SUCCESS( RtlUnicodeStringToCountedOemString( &DbcsName, 534 &FileName, 535 FALSE ))) { 536 537 return FALSE; 538 } 539 540 // 541 // We have now initialized the Oem string. Call the FsRtl package to check for a 542 // valid FAT name. 543 // 544 545 return FsRtlIsFatDbcsLegal( DbcsName, FALSE, FALSE, FALSE ); 546 } 547 548 549 VOID 550 CdGenerate8dot3Name ( 551 _In_ PIRP_CONTEXT IrpContext, 552 _In_ PUNICODE_STRING FileName, 553 _In_ ULONG DirentOffset, 554 _Out_writes_bytes_to_(BYTE_COUNT_8_DOT_3, *ShortByteCount) PWCHAR ShortFileName, 555 _Out_ PUSHORT ShortByteCount 556 ) 557 558 /*++ 559 560 Routine Description: 561 562 This routine is called to generate a short name from the given long name. We will 563 generate a short name from the given long name. 564 565 We go through the following steps to make this conversion. 566 567 1 - Generate the generic short name. This will also be in unicode format. 568 569 2 - Build the string representation of the dirent offset. 570 571 3 - Build the biased short name string by combining the generic short name with 572 the dirent offset string. 573 574 4 - Copy the final unicode string back to our caller's buffer. 575 576 Arguments: 577 578 FileName - String of bytes containing the name. 579 580 DirentOffset - Offset in the directory for this filename. We incorporate the offset into 581 the short name by dividing this by 32 and prepending a tilde character to the 582 digit character. We then append this to the base of the generated short name. 583 584 ShortFileName - Pointer to the buffer to store the short name into. 585 586 ShortByteCount - Address to store the number of bytes in the short name. 587 588 Return Value: 589 590 None. 591 592 --*/ 593 594 { 595 NTSTATUS Status; 596 597 UNICODE_STRING ShortName; 598 UNICODE_STRING BiasedShortName; 599 WCHAR ShortNameBuffer[ BYTE_COUNT_8_DOT_3 / sizeof( WCHAR ) ] = {0}; 600 WCHAR BiasedShortNameBuffer[ BYTE_COUNT_8_DOT_3 / sizeof( WCHAR ) ]; 601 602 GENERATE_NAME_CONTEXT NameContext; 603 604 ULONG BiasedDirentOffset; 605 606 ULONG MaximumBaseBytes; 607 ULONG BaseNameOffset; 608 609 PWCHAR NextWchar; 610 WCHAR ThisWchar; 611 USHORT Length; 612 613 BOOLEAN FoundTilde = FALSE; 614 615 OEM_STRING OemName = {0}; 616 USHORT OemNameOffset = 0; 617 BOOLEAN OverflowBuffer = FALSE; 618 619 PAGED_CODE(); 620 621 // 622 // Initialize the short string to use the input buffer. 623 // 624 625 ShortName.Buffer = ShortNameBuffer; 626 ShortName.MaximumLength = BYTE_COUNT_8_DOT_3; 627 628 // 629 // Initialize the name context. 630 // 631 632 RtlZeroMemory( &NameContext, sizeof( GENERATE_NAME_CONTEXT )); 633 634 // 635 // We now have the unicode name for the input string. Go ahead and generate 636 // the short name. 637 // 638 639 RtlGenerate8dot3Name( FileName, TRUE, &NameContext, &ShortName ); 640 641 // 642 // We now have the generic short name. We want incorporate the dirent offset 643 // into the name in order to reduce the chance of name conflicts. We will use 644 // a tilde character followed by a character representation of the dirent offset. 645 // This will be the hexadecimal representation of the dirent offset in the directory. 646 // It is actuall this offset divided by 32 since we don't need the full 647 // granularity. 648 // 649 650 BiasedDirentOffset = DirentOffset >> SHORT_NAME_SHIFT; 651 652 // 653 // Point to a local buffer to store the offset string. We start 654 // at the end of the buffer and work backwards. 655 // 656 657 NextWchar = Add2Ptr( BiasedShortNameBuffer, 658 BYTE_COUNT_8_DOT_3, 659 PWCHAR ); 660 661 BiasedShortName.MaximumLength = BYTE_COUNT_8_DOT_3; 662 663 // 664 // Generate an OEM version of the string so that we can check for double 665 // byte characters. 666 // 667 668 Status = RtlUnicodeStringToOemString(&OemName, &ShortName, TRUE); 669 670 // 671 // If this failed, bail out. Don't expect any problems other than no mem. 672 // 673 674 if (!NT_SUCCESS( Status)) { 675 676 NT_ASSERT( STATUS_INSUFFICIENT_RESOURCES == Status); 677 CdRaiseStatus( IrpContext, Status); 678 } 679 680 Length = 0; 681 682 // 683 // Now add the characters for the dirent offset. We need to start 684 // from the least significant digit and work backwards. 685 // 686 687 do { 688 689 NextWchar -= 1; 690 691 ThisWchar = (WCHAR) (BiasedDirentOffset & 0x0000000f); 692 693 // 694 // Store in the next character. Bias against either '0' or 'A' 695 // 696 697 if (ThisWchar <= 9) { 698 699 *NextWchar = ThisWchar + L'0'; 700 701 } else { 702 703 *NextWchar = ThisWchar + L'A' - 0xA; 704 } 705 706 Length += sizeof( WCHAR ); 707 708 // 709 // Shift out the low 4 bits of the offset. 710 // 711 712 BiasedDirentOffset >>= 4; 713 714 } while (BiasedDirentOffset != 0); 715 716 // 717 // Now store in the tilde character. 718 // 719 720 NextWchar -= 1; 721 *NextWchar = L'~'; 722 Length += sizeof( WCHAR ); 723 724 // 725 // Set the length of this string. 726 // 727 728 BiasedShortName.Length = Length; 729 BiasedShortName.Buffer = NextWchar; 730 731 // 732 // Figure out the maximum number of characters we can copy of the base 733 // name. We subract the number of characters in the dirent string from 8. 734 // We will copy this many characters or stop when we reach a '.' character 735 // or a '~' character in the name. 736 // 737 738 MaximumBaseBytes = 16 - Length; 739 740 BaseNameOffset = 0; 741 742 // 743 // Keep copying from the base name until we hit a '.', '~' or the end of 744 // the short name. 745 // 746 747 NextWchar = ShortFileName; 748 Length = 0; 749 750 while ((BaseNameOffset < ShortName.Length) && 751 (ShortName.Buffer[BaseNameOffset / 2] != L'.')) { 752 753 // 754 // Remember if we found a tilde character in the short name, 755 // so we don't copy it or anything following it. 756 // 757 758 if (ShortName.Buffer[BaseNameOffset / 2] == L'~') { 759 760 FoundTilde = TRUE; 761 } 762 763 // 764 // We need to consider the DBCS code page, because Unicode characters 765 // may use 2 bytes as DBCS characters. 766 // 767 768 #ifdef _MSC_VER 769 #pragma prefast(push) 770 #pragma prefast(suppress:26014, "OemNameOffset <= BaseNameOffset throughout this loop; OemName buffer previously allocated based on ShortName's length.") 771 #endif 772 if (FsRtlIsLeadDbcsCharacter(OemName.Buffer[OemNameOffset])) { 773 #ifdef _MSC_VER 774 #pragma prefast(pop) 775 #endif 776 777 OemNameOffset += 2; 778 779 if ((OemNameOffset + (BiasedShortName.Length / sizeof(WCHAR))) > 8) { 780 781 OverflowBuffer = TRUE; 782 } 783 } 784 else { 785 786 OemNameOffset++; 787 } 788 789 // 790 // Only copy the bytes if we still have space for the dirent string. 791 // 792 793 if (!FoundTilde && !OverflowBuffer && (BaseNameOffset < MaximumBaseBytes)) { 794 795 *NextWchar = ShortName.Buffer[BaseNameOffset / 2]; 796 Length += sizeof( WCHAR ); 797 NextWchar += 1; 798 } 799 800 BaseNameOffset += 2; 801 } 802 803 RtlFreeOemString(&OemName); 804 805 // 806 // Now copy the dirent string into the biased name buffer. 807 // 808 809 #ifdef _MSC_VER 810 #pragma prefast(push) 811 #endif 812 RtlCopyMemory( NextWchar, 813 BiasedShortName.Buffer, 814 BiasedShortName.Length ); 815 #ifdef _MSC_VER 816 #pragma prefast(pop) 817 #endif 818 819 Length += BiasedShortName.Length; 820 NextWchar += (BiasedShortName.Length / sizeof( WCHAR )); 821 822 // 823 // Now copy any remaining bytes over to the biased short name. 824 // 825 826 if (BaseNameOffset != ShortName.Length) { 827 828 RtlCopyMemory( NextWchar, 829 &ShortName.Buffer[BaseNameOffset / 2], 830 ShortName.Length - BaseNameOffset ); 831 832 Length += (ShortName.Length - (USHORT) BaseNameOffset); 833 } 834 835 // 836 // The final short name is stored in the user's buffer. 837 // 838 839 *ShortByteCount = Length; 840 } 841 842 843 BOOLEAN 844 CdIsNameInExpression ( 845 _In_ PIRP_CONTEXT IrpContext, 846 _In_ PCD_NAME CurrentName, 847 _In_ PCD_NAME SearchExpression, 848 _In_ ULONG WildcardFlags, 849 _In_ BOOLEAN CheckVersion 850 ) 851 852 /*++ 853 854 Routine Description: 855 856 This routine will compare two CdName strings. We assume that if this 857 is to be a case-insensitive search then they are already upcased. 858 859 We compare the filename portions of the name and if they match we 860 compare the version strings if requested. 861 862 Arguments: 863 864 CurrentName - Filename from the disk. 865 866 SearchExpression - Filename expression to use for match. 867 868 WildcardFlags - Flags field which indicates which parts of the 869 search expression might have wildcards. These flags are the 870 same as in the Ccb flags field. 871 872 CheckVersion - Indicates whether we should check both the name and the 873 version strings or just the name. 874 875 Return Value: 876 877 BOOLEAN - TRUE if the expressions match, FALSE otherwise. 878 879 --*/ 880 881 { 882 BOOLEAN Match = TRUE; 883 PAGED_CODE(); 884 885 UNREFERENCED_PARAMETER( IrpContext ); 886 887 // 888 // If there are wildcards in the expression then we call the 889 // appropriate FsRtlRoutine. 890 // 891 892 if (FlagOn( WildcardFlags, CCB_FLAG_ENUM_NAME_EXP_HAS_WILD )) { 893 894 Match = FsRtlIsNameInExpression( &SearchExpression->FileName, 895 &CurrentName->FileName, 896 FALSE, 897 NULL ); 898 899 // 900 // Otherwise do a direct memory comparison for the name string. 901 // 902 903 } else { 904 905 if ((CurrentName->FileName.Length != SearchExpression->FileName.Length) || 906 (!RtlEqualMemory( CurrentName->FileName.Buffer, 907 SearchExpression->FileName.Buffer, 908 CurrentName->FileName.Length ))) { 909 910 Match = FALSE; 911 } 912 } 913 914 // 915 // Check the version numbers if requested by the user and we have a 916 // match on the name and the version number is present. 917 // 918 919 if (Match && CheckVersion && SearchExpression->VersionString.Length && 920 !FlagOn( WildcardFlags, CCB_FLAG_ENUM_VERSION_MATCH_ALL )) { 921 922 // 923 // If there are wildcards in the expression then call the 924 // appropriate search expression. 925 // 926 927 if (FlagOn( WildcardFlags, CCB_FLAG_ENUM_VERSION_EXP_HAS_WILD )) { 928 929 Match = FsRtlIsNameInExpression( &SearchExpression->VersionString, 930 &CurrentName->VersionString, 931 FALSE, 932 NULL ); 933 934 // 935 // Otherwise do a direct memory comparison for the name string. 936 // 937 938 } else { 939 940 if ((CurrentName->VersionString.Length != SearchExpression->VersionString.Length) || 941 (!RtlEqualMemory( CurrentName->VersionString.Buffer, 942 SearchExpression->VersionString.Buffer, 943 CurrentName->VersionString.Length ))) { 944 945 Match = FALSE; 946 } 947 } 948 } 949 950 return Match; 951 } 952 953 954 ULONG 955 CdShortNameDirentOffset ( 956 _In_ PIRP_CONTEXT IrpContext, 957 _In_ PUNICODE_STRING Name 958 ) 959 960 /*++ 961 962 Routine Description: 963 964 This routine is called to examine a name to see if the dirent offset string is contained. 965 This consists of a tilde character followed by the offset represented as a hexadecimal 966 characters. We don't do any other checks to see if this is a short name. We 967 catch that later outside this routine. 968 969 Arguments: 970 971 Name - This is the CdName to examine. 972 973 Return Value: 974 975 ULONG - MAXULONG if there is no valid dirent offset string embedded, otherwise the 976 convert the value to numeric form. 977 978 --*/ 979 980 { 981 ULONG ResultOffset = MAXULONG; 982 ULONG RemainingByteCount = Name->Length; 983 984 BOOLEAN FoundTilde = FALSE; 985 986 PWCHAR NextWchar; 987 988 PAGED_CODE(); 989 990 UNREFERENCED_PARAMETER( IrpContext ); 991 992 // 993 // Walk through the name until we either reach the end of the name 994 // or find a tilde character. 995 // 996 997 for (NextWchar = Name->Buffer; 998 RemainingByteCount != 0; 999 NextWchar += 1, RemainingByteCount -= sizeof( WCHAR )) { 1000 1001 // 1002 // Check if this is a dot. Stop constructing any string if 1003 // we found a dot. 1004 // 1005 1006 if (*NextWchar == L'.') { 1007 1008 break; 1009 } 1010 1011 // 1012 // If we already found a tilde then check this character as a 1013 // valid character. It must be a digit or A to F. 1014 // 1015 1016 if (FoundTilde) { 1017 1018 if ((*NextWchar < L'0') || 1019 (*NextWchar > L'F') || 1020 ((*NextWchar > L'9') && (*NextWchar < 'A'))) { 1021 1022 ResultOffset = MAXULONG; 1023 break; 1024 } 1025 1026 // 1027 // Shift the result by 4 bits and add in this new character. 1028 // 1029 1030 ResultOffset <<= 4; 1031 1032 if (*NextWchar < L'A') { 1033 1034 ResultOffset += *NextWchar - L'0'; 1035 1036 } else { 1037 1038 ResultOffset += (*NextWchar - L'A') + 10; 1039 } 1040 1041 continue; 1042 } 1043 1044 // 1045 // If this is a tilde then start building the dirent string. 1046 // 1047 1048 if (*NextWchar == L'~') { 1049 1050 FoundTilde = TRUE; 1051 ResultOffset = 0; 1052 } 1053 } 1054 1055 return ResultOffset; 1056 } 1057 1058 1059 // 1060 // Local support routine 1061 // 1062 1063 FSRTL_COMPARISON_RESULT 1064 CdFullCompareNames ( 1065 _In_ PIRP_CONTEXT IrpContext, 1066 _In_ PUNICODE_STRING NameA, 1067 _In_ PUNICODE_STRING NameB 1068 ) 1069 1070 /*++ 1071 1072 Routine Description: 1073 1074 This function compares two names as fast as possible. Note that since 1075 this comparison is case sensitive we can do a direct memory comparison. 1076 1077 Arguments: 1078 1079 NameA & NameB - The names to compare. 1080 1081 Return Value: 1082 1083 COMPARISON - returns 1084 1085 LessThan if NameA < NameB lexicalgraphically, 1086 GreaterThan if NameA > NameB lexicalgraphically, 1087 EqualTo if NameA is equal to NameB 1088 1089 --*/ 1090 1091 { 1092 SIZE_T i; 1093 ULONG MinLength = NameA->Length; 1094 FSRTL_COMPARISON_RESULT Result = LessThan; 1095 1096 PAGED_CODE(); 1097 1098 UNREFERENCED_PARAMETER( IrpContext ); 1099 1100 // 1101 // Figure out the minimum of the two lengths 1102 // 1103 1104 if (NameA->Length > NameB->Length) { 1105 1106 MinLength = NameB->Length; 1107 Result = GreaterThan; 1108 1109 } else if (NameA->Length == NameB->Length) { 1110 1111 Result = EqualTo; 1112 } 1113 1114 // 1115 // Loop through looking at all of the characters in both strings 1116 // testing for equalilty, less than, and greater than 1117 // 1118 1119 i = RtlCompareMemory( NameA->Buffer, NameB->Buffer, MinLength ); 1120 1121 if (i < MinLength) { 1122 1123 // 1124 // We know the offset of the first character which is different. 1125 // 1126 1127 return ((NameA->Buffer[ i / 2 ] < NameB->Buffer[ i / 2 ]) ? 1128 LessThan : 1129 GreaterThan); 1130 } 1131 1132 // 1133 // The names match up to the length of the shorter string. 1134 // The shorter string lexically appears first. 1135 // 1136 1137 return Result; 1138 } 1139 1140 1141 1142