1 /*++ 2 3 Copyright (c) 1989-2000 Microsoft Corporation 4 5 Module Name: 6 7 NameSup.c 8 9 Abstract: 10 11 This module implements the Fat Name support routines 12 13 14 --*/ 15 16 #include "fatprocs.h" 17 18 #define Dbg (DEBUG_TRACE_NAMESUP) 19 20 #ifdef ALLOC_PRAGMA 21 #pragma alloc_text(PAGE, Fat8dot3ToString) 22 #pragma alloc_text(PAGE, FatIsNameInExpression) 23 #pragma alloc_text(PAGE, FatStringTo8dot3) 24 #pragma alloc_text(PAGE, FatSetFullFileNameInFcb) 25 #pragma alloc_text(PAGE, FatGetUnicodeNameFromFcb) 26 #pragma alloc_text(PAGE, FatUnicodeToUpcaseOem) 27 #pragma alloc_text(PAGE, FatSelectNames) 28 #pragma alloc_text(PAGE, FatEvaluateNameCase) 29 #pragma alloc_text(PAGE, FatSpaceInName) 30 #pragma alloc_text(PAGE, FatUnicodeRestoreShortNameCase) 31 #endif 32 33 34 BOOLEAN 35 FatIsNameInExpression ( 36 IN PIRP_CONTEXT IrpContext, 37 IN OEM_STRING Expression, 38 IN OEM_STRING Name 39 ) 40 41 /*++ 42 43 Routine Description: 44 45 This routine compare a name and an expression and tells the caller if 46 the name is equal to or not equal to the expression. The input name 47 cannot contain wildcards, while the expression may contain wildcards. 48 49 Arguments: 50 51 Expression - Supplies the input expression to check against 52 The caller must have already upcased the Expression. 53 54 Name - Supplies the input name to check for. The caller must have 55 already upcased the name. 56 57 Return Value: 58 59 BOOLEAN - TRUE if Name is an element in the set of strings denoted 60 by the input Expression and FALSE otherwise. 61 62 --*/ 63 64 { 65 PAGED_CODE(); 66 67 // 68 // Call the appropriate FsRtl routine do to the real work 69 // 70 71 return FsRtlIsDbcsInExpression( &Expression, 72 &Name ); 73 74 UNREFERENCED_PARAMETER( IrpContext ); 75 } 76 77 78 VOID 79 FatStringTo8dot3 ( 80 _In_ PIRP_CONTEXT IrpContext, 81 _In_ OEM_STRING InputString, 82 _Out_writes_bytes_(11) PFAT8DOT3 Output8dot3 83 ) 84 85 /*++ 86 87 Routine Description: 88 89 Convert a string into fat 8.3 format. The string must not contain 90 any wildcards. 91 92 Arguments: 93 94 InputString - Supplies the input string to convert 95 96 Output8dot3 - Receives the converted string, the memory must be supplied 97 by the caller. 98 99 Return Value: 100 101 None. 102 103 --*/ 104 105 { 106 ULONG i; 107 ULONG j; 108 109 PAGED_CODE(); 110 111 DebugTrace(+1, Dbg, "FatStringTo8dot3\n", 0); 112 DebugTrace( 0, Dbg, "InputString = %Z\n", &InputString); 113 114 NT_ASSERT( InputString.Length <= 12 ); 115 116 // 117 // Make the output name all blanks 118 // 119 120 RtlFillMemory( Output8dot3, 11, UCHAR_SP ); 121 122 // 123 // Copy over the first part of the file name. Stop when we get to 124 // the end of the input string or a dot. 125 // 126 127 for (i = 0; 128 (i < (ULONG)InputString.Length) && (InputString.Buffer[i] != '.') && (i < 11); 129 i += 1) { 130 131 (*Output8dot3)[i] = InputString.Buffer[i]; 132 } 133 134 // 135 // Check if we need to process an extension 136 // 137 138 if (i < (ULONG)InputString.Length) { 139 140 // 141 // Make sure we have a dot and then skip over it. 142 // 143 144 NT_ASSERT( (InputString.Length - i) <= 4 ); 145 NT_ASSERT( InputString.Buffer[i] == '.' ); 146 147 i += 1; 148 149 // 150 // Copy over the extension. Stop when we get to the 151 // end of the input string. 152 // 153 154 for (j = 8; (i < (ULONG)InputString.Length); j += 1, i += 1) { 155 156 (*Output8dot3)[j] = InputString.Buffer[i]; 157 } 158 } 159 160 // 161 // Before we return check if we should translate the first character 162 // from 0xe5 to 0x5. 163 // 164 165 if ((*Output8dot3)[0] == 0xe5) { 166 167 (*Output8dot3)[0] = FAT_DIRENT_REALLY_0E5; 168 } 169 170 DebugTrace(-1, Dbg, "FatStringTo8dot3 -> (VOID)\n", 0); 171 172 UNREFERENCED_PARAMETER( IrpContext ); 173 174 return; 175 } 176 177 178 VOID 179 Fat8dot3ToString ( 180 _In_ PIRP_CONTEXT IrpContext, 181 _In_ PDIRENT Dirent, 182 _In_ BOOLEAN RestoreCase, 183 _Out_ POEM_STRING OutputString 184 ) 185 186 /*++ 187 188 Routine Description: 189 190 Convert fat 8.3 format into a string. The 8.3 name must be well formed. 191 192 Arguments: 193 194 Dirent - Supplies the input 8.3 name to convert 195 196 RestoreCase - If TRUE, then the magic reserved bits are used to restore 197 the original case. 198 199 OutputString - Receives the converted name, the memory must be supplied 200 by the caller. 201 202 Return Value: 203 204 None 205 206 --*/ 207 208 { 209 ULONG StringIndex; 210 ULONG BaseLength, ExtensionLength; 211 212 PAGED_CODE(); 213 214 DebugTrace(+1, Dbg, "Fat8dot3ToString\n", 0); 215 216 // 217 // First, find the length of the base component. 218 // 219 220 for (BaseLength = 8; BaseLength > 0; BaseLength -= 1) { 221 222 if (Dirent->FileName[BaseLength - 1] != UCHAR_SP) { 223 224 break; 225 } 226 } 227 228 // 229 // Now find the length of the extension. 230 // 231 232 for (ExtensionLength = 3; ExtensionLength > 0; ExtensionLength -= 1) { 233 234 if (Dirent->FileName[8 + ExtensionLength - 1] != UCHAR_SP) { 235 236 break; 237 } 238 } 239 240 // 241 // If there was a base part, copy it and check the case. Don't forget 242 // if the first character needs to be changed from 0x05 to 0xe5. 243 // 244 245 if (BaseLength != 0) { 246 247 RtlCopyMemory( OutputString->Buffer, Dirent->FileName, BaseLength ); 248 249 if (OutputString->Buffer[0] == FAT_DIRENT_REALLY_0E5) { 250 251 OutputString->Buffer[0] = 0xe5; 252 } 253 254 // 255 // Now if we are to restore case, look for A-Z 256 // 257 258 if (FatData.ChicagoMode && 259 RestoreCase && 260 FlagOn(Dirent->NtByte, FAT_DIRENT_NT_BYTE_8_LOWER_CASE)) { 261 262 for (StringIndex = 0; StringIndex < BaseLength; StringIndex += 1) { 263 264 // 265 // Depending on whether the media was built in a system that was 266 // running with "code page invariance" (see FatEvaluateNameCase), 267 // there could be double-byte OEM characters lying in wait here. 268 // Gotta skip them. 269 // 270 271 if (FsRtlIsLeadDbcsCharacter(OutputString->Buffer[StringIndex])) { 272 273 StringIndex += 1; 274 continue; 275 } 276 277 if ((OutputString->Buffer[StringIndex] >= 'A') && 278 (OutputString->Buffer[StringIndex] <= 'Z')) { 279 280 OutputString->Buffer[StringIndex] += 'a' - 'A'; 281 } 282 } 283 } 284 } 285 286 // 287 // If there was an extension, copy that over. Else we now know the 288 // size of the string. 289 // 290 291 if (ExtensionLength != 0) { 292 293 PUCHAR o, d; 294 295 // 296 // Now add the dot 297 // 298 299 OutputString->Buffer[BaseLength++] = '.'; 300 301 // 302 // Copy over the extension into the output buffer. 303 // 304 305 o = (PUCHAR)&OutputString->Buffer[BaseLength]; 306 d = &Dirent->FileName[8]; 307 308 switch (ExtensionLength) { 309 case 3: 310 *o++ = *d++; 311 case 2: 312 *o++ = *d++; 313 case 1: 314 *o++ = *d++; 315 } 316 317 // 318 // Set the output string length 319 // 320 321 OutputString->Length = (USHORT)(BaseLength + ExtensionLength); 322 323 // 324 // Now if we are to restore case, look for A-Z 325 // 326 327 if (FatData.ChicagoMode && 328 RestoreCase && 329 FlagOn(Dirent->NtByte, FAT_DIRENT_NT_BYTE_3_LOWER_CASE)) { 330 331 for (StringIndex = BaseLength; 332 StringIndex < OutputString->Length; 333 StringIndex++ ) { 334 335 // 336 // Depending on whether the media was built in a system that was 337 // running with "code page invariance" (see FatEvaluateNameCase), 338 // there could be double-byte OEM characters lying in wait here. 339 // Gotta skip them. 340 // 341 342 if (FsRtlIsLeadDbcsCharacter(OutputString->Buffer[StringIndex])) { 343 344 StringIndex += 1; 345 continue; 346 } 347 348 if ((OutputString->Buffer[StringIndex] >= 'A') && 349 (OutputString->Buffer[StringIndex] <= 'Z')) { 350 351 OutputString->Buffer[StringIndex] += 'a' - 'A'; 352 } 353 } 354 } 355 356 } else { 357 358 // 359 // Set the output string length 360 // 361 362 OutputString->Length = (USHORT)BaseLength; 363 } 364 365 // 366 // And return to our caller 367 // 368 369 DebugTrace(-1, Dbg, "Fat8dot3ToString, OutputString = \"%Z\" -> VOID\n", OutputString); 370 371 UNREFERENCED_PARAMETER( IrpContext ); 372 373 return; 374 } 375 376 _Requires_lock_held_(_Global_critical_region_) 377 VOID 378 FatGetUnicodeNameFromFcb ( 379 IN PIRP_CONTEXT IrpContext, 380 IN PFCB Fcb, 381 IN OUT PUNICODE_STRING Lfn 382 ) 383 384 /*++ 385 386 Routine Description: 387 388 This routine will return the unicode name for a given Fcb. If the 389 file has an LFN, it will return this. Otherwise it will return 390 the UNICODE conversion of the Oem name, properly cased. 391 392 Arguments: 393 394 Fcb - Supplies the Fcb to query. 395 396 Lfn - Supplies a string that already has enough storage for the 397 full unicode name. 398 399 Return Value: 400 401 None 402 403 --*/ 404 405 { 406 PDIRENT Dirent; 407 PBCB DirentBcb = NULL; 408 ULONG DirentByteOffset; 409 410 CCB LocalCcb; 411 412 PAGED_CODE(); 413 414 NT_ASSERT((MAX_LFN_CHARACTERS * sizeof( WCHAR)) == Lfn->MaximumLength); 415 416 // 417 // We'll start by locating the dirent for the name. 418 // 419 420 FatStringTo8dot3( IrpContext, 421 Fcb->ShortName.Name.Oem, 422 &LocalCcb.OemQueryTemplate.Constant ); 423 424 LocalCcb.Flags = 0; 425 LocalCcb.UnicodeQueryTemplate.Length = 0; 426 LocalCcb.ContainsWildCards = FALSE; 427 428 FatLocateDirent( IrpContext, 429 Fcb->ParentDcb, 430 &LocalCcb, 431 Fcb->LfnOffsetWithinDirectory, 432 NULL, 433 &Dirent, 434 &DirentBcb, 435 (PVBO)&DirentByteOffset, 436 NULL, 437 Lfn, 438 NULL ); 439 _SEH2_TRY { 440 441 // 442 // If we didn't find the Dirent, something is terribly wrong. 443 // 444 445 if ((DirentBcb == NULL) || 446 (DirentByteOffset != Fcb->DirentOffsetWithinDirectory)) { 447 448 FatRaiseStatus( IrpContext, STATUS_FILE_INVALID ); 449 } 450 451 // 452 // Check for the easy case. 453 // 454 455 if (Lfn->Length == 0) { 456 457 NTSTATUS Status; 458 OEM_STRING ShortName; 459 UCHAR ShortNameBuffer[12]; 460 461 // 462 // If we thought that there was an LFN here and didn't find one, 463 // we're as dead. This shouldn't happen in normal operation, but 464 // if someone scrambles a directory by hand ... 465 // 466 467 NT_ASSERT( Fcb->LfnOffsetWithinDirectory == Fcb->DirentOffsetWithinDirectory ); 468 469 if (Fcb->LfnOffsetWithinDirectory != Fcb->DirentOffsetWithinDirectory) { 470 471 FatRaiseStatus( IrpContext, STATUS_FILE_INVALID ); 472 } 473 474 // 475 // There is no LFN, so manufacture a UNICODE name. 476 // 477 478 ShortName.Length = 0; 479 ShortName.MaximumLength = 12; 480 ShortName.Buffer = (PCHAR)ShortNameBuffer; 481 482 Fat8dot3ToString( IrpContext, Dirent, TRUE, &ShortName ); 483 484 // 485 // OK, now convert this string to UNICODE 486 // 487 488 #ifdef _MSC_VER 489 #pragma prefast( suppress:28931, "needed for debug build" ) 490 #endif 491 Status = RtlOemStringToCountedUnicodeString( Lfn, 492 &ShortName, 493 FALSE ); 494 495 NT_ASSERT( Status == STATUS_SUCCESS ); 496 } 497 498 } _SEH2_FINALLY { 499 500 FatUnpinBcb( IrpContext, DirentBcb ); 501 } _SEH2_END; 502 } 503 504 _Requires_lock_held_(_Global_critical_region_) 505 VOID 506 FatSetFullFileNameInFcb ( 507 IN PIRP_CONTEXT IrpContext, 508 IN PFCB Fcb 509 ) 510 511 /*++ 512 513 Routine Description: 514 515 If the FullFileName field in the Fcb has not yet been filled in, we 516 proceed to do so. 517 518 Arguments: 519 520 Fcb - Supplies the file. 521 522 Return Value: 523 524 None 525 526 --*/ 527 528 { 529 PAGED_CODE(); 530 531 if (Fcb->FullFileName.Buffer == NULL) { 532 533 UNICODE_STRING Lfn; 534 PFCB TmpFcb = Fcb; 535 PFCB StopFcb; 536 PWCHAR TmpBuffer; 537 ULONG PathLength = 0; 538 539 // 540 // We will assume we do this infrequently enough, that it's OK to 541 // to a pool allocation here. 542 // 543 544 Lfn.Length = 0; 545 Lfn.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR); 546 Lfn.Buffer = FsRtlAllocatePoolWithTag( PagedPool, 547 MAX_LFN_CHARACTERS * sizeof(WCHAR), 548 TAG_FILENAME_BUFFER ); 549 550 _SEH2_TRY { 551 552 // 553 // First determine how big the name will be. If we find an 554 // ancestor with a FullFileName, our work is easier. 555 // 556 557 while (TmpFcb != Fcb->Vcb->RootDcb) { 558 559 if ((TmpFcb != Fcb) && (TmpFcb->FullFileName.Buffer != NULL)) { 560 561 PathLength += TmpFcb->FullFileName.Length; 562 563 Fcb->FullFileName.Buffer = FsRtlAllocatePoolWithTag( PagedPool, 564 PathLength, 565 TAG_FILENAME_BUFFER ); 566 567 RtlCopyMemory( Fcb->FullFileName.Buffer, 568 TmpFcb->FullFileName.Buffer, 569 TmpFcb->FullFileName.Length ); 570 571 break; 572 } 573 574 PathLength += TmpFcb->FinalNameLength + sizeof(WCHAR); 575 576 TmpFcb = TmpFcb->ParentDcb; 577 } 578 579 // 580 // If FullFileName.Buffer is still NULL, allocate it. 581 // 582 583 if (Fcb->FullFileName.Buffer == NULL) { 584 585 Fcb->FullFileName.Buffer = FsRtlAllocatePoolWithTag( PagedPool, 586 PathLength, 587 TAG_FILENAME_BUFFER ); 588 } 589 590 StopFcb = TmpFcb; 591 592 TmpFcb = Fcb; 593 TmpBuffer = Fcb->FullFileName.Buffer + PathLength / sizeof(WCHAR); 594 595 Fcb->FullFileName.Length = 596 Fcb->FullFileName.MaximumLength = (USHORT)PathLength; 597 598 while (TmpFcb != StopFcb) { 599 600 FatGetUnicodeNameFromFcb( IrpContext, 601 TmpFcb, 602 &Lfn ); 603 604 TmpBuffer -= Lfn.Length / sizeof(WCHAR); 605 606 RtlCopyMemory( TmpBuffer, Lfn.Buffer, Lfn.Length ); 607 608 TmpBuffer -= 1; 609 610 *TmpBuffer = L'\\'; 611 612 TmpFcb = TmpFcb->ParentDcb; 613 } 614 615 } _SEH2_FINALLY { 616 617 if (_SEH2_AbnormalTermination()) { 618 619 if (Fcb->FullFileName.Buffer) { 620 621 ExFreePool( Fcb->FullFileName.Buffer ); 622 Fcb->FullFileName.Buffer = NULL; 623 } 624 } 625 626 ExFreePool( Lfn.Buffer ); 627 } _SEH2_END; 628 } 629 } 630 631 VOID 632 FatUnicodeToUpcaseOem ( 633 IN PIRP_CONTEXT IrpContext, 634 IN POEM_STRING OemString, 635 IN PUNICODE_STRING UnicodeString 636 ) 637 638 /*++ 639 640 Routine Description: 641 642 This routine is our standard routine for trying to use stack space 643 if possible when calling RtlUpcaseUnicodeStringToCountedOemString(). 644 645 If an unmappable character is encountered, we set the destination 646 length to 0. 647 648 Arguments: 649 650 OemString - Specifies the destination string. Space is already assumed to 651 be allocated. If there is not enough, then we allocate enough 652 space. 653 654 UnicodeString - Specifies the source string. 655 656 Return Value: 657 658 None. 659 660 --*/ 661 662 { 663 NTSTATUS Status; 664 665 PAGED_CODE(); 666 667 Status = RtlUpcaseUnicodeStringToCountedOemString( OemString, 668 UnicodeString, 669 FALSE ); 670 671 if (Status == STATUS_BUFFER_OVERFLOW) { 672 673 OemString->Buffer = NULL; 674 OemString->Length = 0; 675 OemString->MaximumLength = 0; 676 677 Status = RtlUpcaseUnicodeStringToCountedOemString( OemString, 678 UnicodeString, 679 TRUE ); 680 } 681 682 if (!NT_SUCCESS(Status)) { 683 684 if (Status == STATUS_UNMAPPABLE_CHARACTER) { 685 686 OemString->Length = 0; 687 688 } else { 689 690 FatNormalizeAndRaiseStatus( IrpContext, Status ); 691 } 692 } 693 694 return; 695 } 696 697 698 _Requires_lock_held_(_Global_critical_region_) 699 VOID 700 FatSelectNames ( 701 IN PIRP_CONTEXT IrpContext, 702 IN PDCB Parent, 703 IN POEM_STRING OemName, 704 IN PUNICODE_STRING UnicodeName, 705 IN OUT POEM_STRING ShortName, 706 IN PUNICODE_STRING SuggestedShortName OPTIONAL, 707 IN OUT BOOLEAN *AllLowerComponent, 708 IN OUT BOOLEAN *AllLowerExtension, 709 IN OUT BOOLEAN *CreateLfn 710 ) 711 712 /*++ 713 714 Routine Description: 715 716 This routine takes the original UNICODE string that the user specified, 717 and the upcased Oem equivalent. This routine then decides if the OemName 718 is acceptable for dirent, or whether a short name has to be manufactured. 719 720 Two values are returned to the caller. One tells the caller if the name 721 happens to be all lower case < 0x80. In this special case we don't 722 have to create an Lfn. Also we tell the caller if it must create an LFN. 723 724 Arguments: 725 726 OemName - Supplies the proposed short Oem name. 727 728 ShortName - If this OemName is OK for storeage in a dirent it is copied to 729 this string, otherwise this string is filled with a name that is OK. 730 If OemName and ShortName are the same string, no copy is done. 731 732 UnicodeName - Provides the original final name. 733 734 SuggestedShortName - a first-try short name to try before auto-generation 735 is used 736 737 AllLowerComponent - Returns whether this component was all lower case. 738 739 AllLowerExtension - Returns wheather the extension was all lower case. 740 741 CreateLfn - Tells the caller if we must create an LFN for the UnicodeName or 742 SuggestedLongName 743 744 Return Value: 745 746 None. 747 748 --*/ 749 750 { 751 BOOLEAN GenerateShortName; 752 753 PAGED_CODE(); 754 755 // 756 // First see if we must generate a short name. 757 // 758 759 if ((OemName->Length == 0) || 760 !FatIsNameShortOemValid( IrpContext, *OemName, FALSE, FALSE, FALSE ) || 761 FatSpaceInName( IrpContext, UnicodeName )) { 762 763 WCHAR ShortNameBuffer[12]; 764 UNICODE_STRING ShortUnicodeName; 765 GENERATE_NAME_CONTEXT Context; 766 BOOLEAN TrySuggestedShortName; 767 768 PDIRENT Dirent; 769 PBCB Bcb = NULL; 770 ULONG ByteOffset; 771 NTSTATUS Status; 772 773 GenerateShortName = TRUE; 774 775 TrySuggestedShortName = (SuggestedShortName != NULL); 776 777 // 778 // Now generate a short name. 779 // 780 781 ShortUnicodeName.Length = 0; 782 ShortUnicodeName.MaximumLength = 12 * sizeof(WCHAR); 783 ShortUnicodeName.Buffer = ShortNameBuffer; 784 785 RtlZeroMemory( &Context, sizeof( GENERATE_NAME_CONTEXT ) ); 786 787 _SEH2_TRY { 788 789 while ( TRUE ) { 790 791 FatUnpinBcb( IrpContext, Bcb ); 792 793 if (TrySuggestedShortName) { 794 795 // 796 // Try our caller's candidate first. Note that this must have 797 // been uppercased previously. 798 // 799 800 ShortUnicodeName.Length = SuggestedShortName->Length; 801 ShortUnicodeName.MaximumLength = SuggestedShortName->MaximumLength; 802 ShortUnicodeName.Buffer = SuggestedShortName->Buffer; 803 804 TrySuggestedShortName = FALSE; 805 806 } else { 807 808 RtlGenerate8dot3Name( UnicodeName, TRUE, &Context, &ShortUnicodeName ); 809 } 810 811 // 812 // We have a candidate, make sure it doesn't exist. 813 // 814 815 #ifdef _MSC_VER 816 #pragma prefast( suppress:28931, "needed for debug build" ) 817 #endif 818 Status = RtlUnicodeStringToCountedOemString( ShortName, 819 &ShortUnicodeName, 820 FALSE ); 821 822 NT_ASSERT( Status == STATUS_SUCCESS ); 823 824 FatLocateSimpleOemDirent( IrpContext, 825 Parent, 826 ShortName, 827 &Dirent, 828 &Bcb, 829 (PVBO)&ByteOffset ); 830 831 if (Bcb == NULL) { 832 833 _SEH2_LEAVE; 834 835 } 836 } 837 838 } _SEH2_FINALLY { 839 840 FatUnpinBcb( IrpContext, Bcb ); 841 } _SEH2_END; 842 843 } else { 844 845 // 846 // Only do this copy if the two string are indeed different. 847 // 848 849 if (ShortName != OemName) { 850 ShortName->Length = OemName->Length; 851 852 // 853 // If FsRtlIsFatDbcsLegal() on OemName fails, we will not 854 // be in this code path, so we infer that ShortName's 855 // buffer is big enough to hold the full FAT file name in 856 // OemName. 857 // 858 859 _Analysis_assume_(ShortName->MaximumLength <= OemName->Length); 860 861 RtlCopyMemory( ShortName->Buffer, OemName->Buffer, OemName->Length ); 862 } 863 864 GenerateShortName = FALSE; 865 } 866 867 // 868 // Now see if the caller will have to use unicode string as an LFN 869 // 870 871 if (GenerateShortName) { 872 873 *CreateLfn = TRUE; 874 *AllLowerComponent = FALSE; 875 *AllLowerExtension = FALSE; 876 877 } else { 878 879 FatEvaluateNameCase( IrpContext, 880 UnicodeName, 881 AllLowerComponent, 882 AllLowerExtension, 883 CreateLfn ); 884 } 885 886 return; 887 } 888 889 890 VOID 891 FatEvaluateNameCase ( 892 IN PIRP_CONTEXT IrpContext, 893 IN PUNICODE_STRING UnicodeName, 894 IN OUT BOOLEAN *AllLowerComponent, 895 IN OUT BOOLEAN *AllLowerExtension, 896 IN OUT BOOLEAN *CreateLfn 897 ) 898 899 /*++ 900 901 Routine Description: 902 903 This routine takes a UNICODE string and sees if it is eligible for 904 the special case optimization. 905 906 Arguments: 907 908 UnicodeName - Provides the original final name. 909 910 AllLowerComponent - Returns whether this compoent was all lower case. 911 912 AllLowerExtension - Returns wheather the extension was all lower case. 913 914 CreateLfn - Tells the call if we must create an LFN for the UnicodeName. 915 916 Return Value: 917 918 None. 919 920 --*/ 921 922 { 923 ULONG i; 924 UCHAR Uppers = 0; 925 UCHAR Lowers = 0; 926 927 BOOLEAN ExtensionPresent = FALSE; 928 929 PAGED_CODE(); 930 UNREFERENCED_PARAMETER( IrpContext ); 931 932 *CreateLfn = FALSE; 933 934 for (i = 0; i < UnicodeName->Length / sizeof(WCHAR); i++) { 935 936 WCHAR c; 937 938 c = UnicodeName->Buffer[i]; 939 940 if ((c >= 'A') && (c <= 'Z')) { 941 942 Uppers += 1; 943 944 } else if ((c >= 'a') && (c <= 'z')) { 945 946 Lowers += 1; 947 948 } else if ((c >= 0x0080) && FatData.CodePageInvariant) { 949 950 break; 951 } 952 953 // 954 // If we come to a period, figure out if the extension was 955 // all one case. 956 // 957 958 if (c == L'.') { 959 960 *CreateLfn = (Lowers != 0) && (Uppers != 0); 961 962 *AllLowerComponent = !(*CreateLfn) && (Lowers != 0); 963 964 ExtensionPresent = TRUE; 965 966 // 967 // Now reset the uppers and lowers count. 968 // 969 970 Uppers = Lowers = 0; 971 } 972 } 973 974 // 975 // Now check again for creating an LFN. 976 // 977 978 *CreateLfn = (*CreateLfn || 979 (i != UnicodeName->Length / sizeof(WCHAR)) || 980 ((Lowers != 0) && (Uppers != 0))); 981 982 // 983 // Now we know the final state of CreateLfn, update the two 984 // "AllLower" booleans. 985 // 986 987 if (ExtensionPresent) { 988 989 *AllLowerComponent = !(*CreateLfn) && *AllLowerComponent; 990 *AllLowerExtension = !(*CreateLfn) && (Lowers != 0); 991 992 } else { 993 994 *AllLowerComponent = !(*CreateLfn) && (Lowers != 0); 995 *AllLowerExtension = FALSE; 996 } 997 998 return; 999 } 1000 1001 1002 BOOLEAN 1003 FatSpaceInName ( 1004 IN PIRP_CONTEXT IrpContext, 1005 IN PUNICODE_STRING UnicodeName 1006 ) 1007 1008 /*++ 1009 1010 Routine Description: 1011 1012 This routine takes a UNICODE string and sees if it contains any spaces. 1013 1014 Arguments: 1015 1016 UnicodeName - Provides the final name. 1017 1018 Return Value: 1019 1020 BOOLEAN - TRUE if it does, FALSE if it doesn't. 1021 1022 --*/ 1023 1024 { 1025 ULONG i; 1026 1027 PAGED_CODE(); 1028 UNREFERENCED_PARAMETER( IrpContext ); 1029 1030 for (i=0; i < UnicodeName->Length/sizeof(WCHAR); i++) { 1031 1032 if (UnicodeName->Buffer[i] == L' ') { 1033 return TRUE; 1034 } 1035 } 1036 1037 return FALSE; 1038 } 1039 1040 VOID 1041 FatUnicodeRestoreShortNameCase( 1042 IN PUNICODE_STRING ShortNameWithCase, 1043 IN BOOLEAN LowerCase8, 1044 IN BOOLEAN LowerCase3 1045 ) 1046 1047 /*++ 1048 1049 Routine Description: 1050 1051 Given an 8.3 filename in a UNICODE_STRING, fix the case of the 1052 name given the two 8do3 case flags. 1053 1054 Arguments: 1055 1056 ShortNameWithCase - the UNICODE_STRING containing the short name. 1057 LowerCase8, LowerCase3 - the flag indicating whether to downcase the 8dot3 name component. 1058 1059 Return Value: 1060 1061 None. 1062 1063 --*/ 1064 { 1065 USHORT i; 1066 UNICODE_STRING DownCaseSeg; 1067 1068 PAGED_CODE(); 1069 1070 NT_ASSERT( ShortNameWithCase->Length <= 24 ); 1071 1072 // 1073 // Have to repair the case of the short name 1074 // 1075 1076 for (i = 0; i < (ShortNameWithCase->Length/sizeof(WCHAR)) && 1077 ShortNameWithCase->Buffer[i] != L'.'; i++); 1078 1079 // 1080 // Now pointing at the '.', or otherwise the end of name component 1081 // 1082 1083 if (LowerCase8) { 1084 1085 DownCaseSeg.Buffer = ShortNameWithCase->Buffer; 1086 DownCaseSeg.MaximumLength = DownCaseSeg.Length = i*sizeof(WCHAR); 1087 1088 RtlDowncaseUnicodeString(&DownCaseSeg, &DownCaseSeg, FALSE); 1089 } 1090 1091 i++; 1092 1093 // 1094 // Now pointing at first wchar of the extension. 1095 // 1096 1097 if (LowerCase3) { 1098 1099 // 1100 // It is not neccesarily the case that we can rely on the flag 1101 // indicating that we really have an extension. 1102 // 1103 1104 if ((i*sizeof(WCHAR)) < ShortNameWithCase->Length) { 1105 DownCaseSeg.Buffer = &ShortNameWithCase->Buffer[i]; 1106 DownCaseSeg.MaximumLength = DownCaseSeg.Length = ShortNameWithCase->Length - i*sizeof(WCHAR); 1107 1108 RtlDowncaseUnicodeString(&DownCaseSeg, &DownCaseSeg, FALSE); 1109 } 1110 } 1111 1112 } 1113 1114 1115