3 Copyright (c) 1989-2000 Microsoft Corporation
11 This module implements the Fat Name support routines
18 #define Dbg (DEBUG_TRACE_NAMESUP)
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)
35 FatIsNameInExpression (
36 IN PIRP_CONTEXT IrpContext
,
37 IN OEM_STRING Expression
,
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.
51 Expression - Supplies the input expression to check against
52 The caller must have already upcased the Expression.
54 Name - Supplies the input name to check for. The caller must have
55 already upcased the name.
59 BOOLEAN - TRUE if Name is an element in the set of strings denoted
60 by the input Expression and FALSE otherwise.
68 // Call the appropriate FsRtl routine do to the real work
71 return FsRtlIsDbcsInExpression( &Expression
,
74 UNREFERENCED_PARAMETER( IrpContext
);
80 _In_ PIRP_CONTEXT IrpContext
,
81 _In_ OEM_STRING InputString
,
82 _Out_writes_bytes_(11) PFAT8DOT3 Output8dot3
89 Convert a string into fat 8.3 format. The string must not contain
94 InputString - Supplies the input string to convert
96 Output8dot3 - Receives the converted string, the memory must be supplied
111 DebugTrace(+1, Dbg
, "FatStringTo8dot3\n", 0);
112 DebugTrace( 0, Dbg
, "InputString = %Z\n", &InputString
);
114 NT_ASSERT( InputString
.Length
<= 12 );
117 // Make the output name all blanks
120 RtlFillMemory( Output8dot3
, 11, UCHAR_SP
);
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.
128 (i
< (ULONG
)InputString
.Length
) && (InputString
.Buffer
[i
] != '.') && (i
< 11);
131 (*Output8dot3
)[i
] = InputString
.Buffer
[i
];
135 // Check if we need to process an extension
138 if (i
< (ULONG
)InputString
.Length
) {
141 // Make sure we have a dot and then skip over it.
144 NT_ASSERT( (InputString
.Length
- i
) <= 4 );
145 NT_ASSERT( InputString
.Buffer
[i
] == '.' );
150 // Copy over the extension. Stop when we get to the
151 // end of the input string.
154 for (j
= 8; (i
< (ULONG
)InputString
.Length
); j
+= 1, i
+= 1) {
156 (*Output8dot3
)[j
] = InputString
.Buffer
[i
];
161 // Before we return check if we should translate the first character
165 if ((*Output8dot3
)[0] == 0xe5) {
167 (*Output8dot3
)[0] = FAT_DIRENT_REALLY_0E5
;
170 DebugTrace(-1, Dbg
, "FatStringTo8dot3 -> (VOID)\n", 0);
172 UNREFERENCED_PARAMETER( IrpContext
);
180 _In_ PIRP_CONTEXT IrpContext
,
182 _In_ BOOLEAN RestoreCase
,
183 _Out_ POEM_STRING OutputString
190 Convert fat 8.3 format into a string. The 8.3 name must be well formed.
194 Dirent - Supplies the input 8.3 name to convert
196 RestoreCase - If TRUE, then the magic reserved bits are used to restore
199 OutputString - Receives the converted name, the memory must be supplied
210 ULONG BaseLength
, ExtensionLength
;
214 DebugTrace(+1, Dbg
, "Fat8dot3ToString\n", 0);
217 // First, find the length of the base component.
220 for (BaseLength
= 8; BaseLength
> 0; BaseLength
-= 1) {
222 if (Dirent
->FileName
[BaseLength
- 1] != UCHAR_SP
) {
229 // Now find the length of the extension.
232 for (ExtensionLength
= 3; ExtensionLength
> 0; ExtensionLength
-= 1) {
234 if (Dirent
->FileName
[8 + ExtensionLength
- 1] != UCHAR_SP
) {
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.
245 if (BaseLength
!= 0) {
247 RtlCopyMemory( OutputString
->Buffer
, Dirent
->FileName
, BaseLength
);
249 if (OutputString
->Buffer
[0] == FAT_DIRENT_REALLY_0E5
) {
251 OutputString
->Buffer
[0] = 0xe5;
255 // Now if we are to restore case, look for A-Z
258 if (FatData
.ChicagoMode
&&
260 FlagOn(Dirent
->NtByte
, FAT_DIRENT_NT_BYTE_8_LOWER_CASE
)) {
262 for (StringIndex
= 0; StringIndex
< BaseLength
; StringIndex
+= 1) {
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.
271 if (FsRtlIsLeadDbcsCharacter(OutputString
->Buffer
[StringIndex
])) {
277 if ((OutputString
->Buffer
[StringIndex
] >= 'A') &&
278 (OutputString
->Buffer
[StringIndex
] <= 'Z')) {
280 OutputString
->Buffer
[StringIndex
] += 'a' - 'A';
287 // If there was an extension, copy that over. Else we now know the
288 // size of the string.
291 if (ExtensionLength
!= 0) {
299 OutputString
->Buffer
[BaseLength
++] = '.';
302 // Copy over the extension into the output buffer.
305 o
= (PUCHAR
)&OutputString
->Buffer
[BaseLength
];
306 d
= &Dirent
->FileName
[8];
308 switch (ExtensionLength
) {
318 // Set the output string length
321 OutputString
->Length
= (USHORT
)(BaseLength
+ ExtensionLength
);
324 // Now if we are to restore case, look for A-Z
327 if (FatData
.ChicagoMode
&&
329 FlagOn(Dirent
->NtByte
, FAT_DIRENT_NT_BYTE_3_LOWER_CASE
)) {
331 for (StringIndex
= BaseLength
;
332 StringIndex
< OutputString
->Length
;
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.
342 if (FsRtlIsLeadDbcsCharacter(OutputString
->Buffer
[StringIndex
])) {
348 if ((OutputString
->Buffer
[StringIndex
] >= 'A') &&
349 (OutputString
->Buffer
[StringIndex
] <= 'Z')) {
351 OutputString
->Buffer
[StringIndex
] += 'a' - 'A';
359 // Set the output string length
362 OutputString
->Length
= (USHORT
)BaseLength
;
366 // And return to our caller
369 DebugTrace(-1, Dbg
, "Fat8dot3ToString, OutputString = \"%Z\" -> VOID\n", OutputString
);
371 UNREFERENCED_PARAMETER( IrpContext
);
376 _Requires_lock_held_(_Global_critical_region_
)
378 FatGetUnicodeNameFromFcb (
379 IN PIRP_CONTEXT IrpContext
,
381 IN OUT PUNICODE_STRING Lfn
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.
394 Fcb - Supplies the Fcb to query.
396 Lfn - Supplies a string that already has enough storage for the
407 PBCB DirentBcb
= NULL
;
408 ULONG DirentByteOffset
;
414 NT_ASSERT((MAX_LFN_CHARACTERS
* sizeof( WCHAR
)) == Lfn
->MaximumLength
);
417 // We'll start by locating the dirent for the name.
420 FatStringTo8dot3( IrpContext
,
421 Fcb
->ShortName
.Name
.Oem
,
422 &LocalCcb
.OemQueryTemplate
.Constant
);
425 LocalCcb
.UnicodeQueryTemplate
.Length
= 0;
426 LocalCcb
.ContainsWildCards
= FALSE
;
428 FatLocateDirent( IrpContext
,
431 Fcb
->LfnOffsetWithinDirectory
,
435 (PVBO
)&DirentByteOffset
,
442 // If we didn't find the Dirent, something is terribly wrong.
445 if ((DirentBcb
== NULL
) ||
446 (DirentByteOffset
!= Fcb
->DirentOffsetWithinDirectory
)) {
448 FatRaiseStatus( IrpContext
, STATUS_FILE_INVALID
);
452 // Check for the easy case.
455 if (Lfn
->Length
== 0) {
458 OEM_STRING ShortName
;
459 UCHAR ShortNameBuffer
[12];
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 ...
467 NT_ASSERT( Fcb
->LfnOffsetWithinDirectory
== Fcb
->DirentOffsetWithinDirectory
);
469 if (Fcb
->LfnOffsetWithinDirectory
!= Fcb
->DirentOffsetWithinDirectory
) {
471 FatRaiseStatus( IrpContext
, STATUS_FILE_INVALID
);
475 // There is no LFN, so manufacture a UNICODE name.
478 ShortName
.Length
= 0;
479 ShortName
.MaximumLength
= 12;
480 ShortName
.Buffer
= (PCHAR
)ShortNameBuffer
;
482 Fat8dot3ToString( IrpContext
, Dirent
, TRUE
, &ShortName
);
485 // OK, now convert this string to UNICODE
489 #pragma prefast( suppress:28931, "needed for debug build" )
491 Status
= RtlOemStringToCountedUnicodeString( Lfn
,
495 NT_ASSERT( Status
== STATUS_SUCCESS
);
500 FatUnpinBcb( IrpContext
, DirentBcb
);
504 _Requires_lock_held_(_Global_critical_region_
)
506 FatSetFullFileNameInFcb (
507 IN PIRP_CONTEXT IrpContext
,
515 If the FullFileName field in the Fcb has not yet been filled in, we
520 Fcb - Supplies the file.
531 if (Fcb
->FullFileName
.Buffer
== NULL
) {
537 ULONG PathLength
= 0;
540 // We will assume we do this infrequently enough, that it's OK to
541 // to a pool allocation here.
545 Lfn
.MaximumLength
= MAX_LFN_CHARACTERS
* sizeof(WCHAR
);
546 Lfn
.Buffer
= FsRtlAllocatePoolWithTag( PagedPool
,
547 MAX_LFN_CHARACTERS
* sizeof(WCHAR
),
548 TAG_FILENAME_BUFFER
);
553 // First determine how big the name will be. If we find an
554 // ancestor with a FullFileName, our work is easier.
557 while (TmpFcb
!= Fcb
->Vcb
->RootDcb
) {
559 if ((TmpFcb
!= Fcb
) && (TmpFcb
->FullFileName
.Buffer
!= NULL
)) {
561 PathLength
+= TmpFcb
->FullFileName
.Length
;
563 Fcb
->FullFileName
.Buffer
= FsRtlAllocatePoolWithTag( PagedPool
,
565 TAG_FILENAME_BUFFER
);
567 RtlCopyMemory( Fcb
->FullFileName
.Buffer
,
568 TmpFcb
->FullFileName
.Buffer
,
569 TmpFcb
->FullFileName
.Length
);
574 PathLength
+= TmpFcb
->FinalNameLength
+ sizeof(WCHAR
);
576 TmpFcb
= TmpFcb
->ParentDcb
;
580 // If FullFileName.Buffer is still NULL, allocate it.
583 if (Fcb
->FullFileName
.Buffer
== NULL
) {
585 Fcb
->FullFileName
.Buffer
= FsRtlAllocatePoolWithTag( PagedPool
,
587 TAG_FILENAME_BUFFER
);
593 TmpBuffer
= Fcb
->FullFileName
.Buffer
+ PathLength
/ sizeof(WCHAR
);
595 Fcb
->FullFileName
.Length
=
596 Fcb
->FullFileName
.MaximumLength
= (USHORT
)PathLength
;
598 while (TmpFcb
!= StopFcb
) {
600 FatGetUnicodeNameFromFcb( IrpContext
,
604 TmpBuffer
-= Lfn
.Length
/ sizeof(WCHAR
);
606 RtlCopyMemory( TmpBuffer
, Lfn
.Buffer
, Lfn
.Length
);
612 TmpFcb
= TmpFcb
->ParentDcb
;
617 if (_SEH2_AbnormalTermination()) {
619 if (Fcb
->FullFileName
.Buffer
) {
621 ExFreePool( Fcb
->FullFileName
.Buffer
);
622 Fcb
->FullFileName
.Buffer
= NULL
;
626 ExFreePool( Lfn
.Buffer
);
632 FatUnicodeToUpcaseOem (
633 IN PIRP_CONTEXT IrpContext
,
634 IN POEM_STRING OemString
,
635 IN PUNICODE_STRING UnicodeString
642 This routine is our standard routine for trying to use stack space
643 if possible when calling RtlUpcaseUnicodeStringToCountedOemString().
645 If an unmappable character is encountered, we set the destination
650 OemString - Specifies the destination string. Space is already assumed to
651 be allocated. If there is not enough, then we allocate enough
654 UnicodeString - Specifies the source string.
667 Status
= RtlUpcaseUnicodeStringToCountedOemString( OemString
,
671 if (Status
== STATUS_BUFFER_OVERFLOW
) {
673 OemString
->Buffer
= NULL
;
674 OemString
->Length
= 0;
675 OemString
->MaximumLength
= 0;
677 Status
= RtlUpcaseUnicodeStringToCountedOemString( OemString
,
682 if (!NT_SUCCESS(Status
)) {
684 if (Status
== STATUS_UNMAPPABLE_CHARACTER
) {
686 OemString
->Length
= 0;
690 FatNormalizeAndRaiseStatus( IrpContext
, Status
);
698 _Requires_lock_held_(_Global_critical_region_
)
701 IN PIRP_CONTEXT IrpContext
,
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
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.
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.
726 OemName - Supplies the proposed short Oem name.
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.
732 UnicodeName - Provides the original final name.
734 SuggestedShortName - a first-try short name to try before auto-generation
737 AllLowerComponent - Returns whether this component was all lower case.
739 AllLowerExtension - Returns wheather the extension was all lower case.
741 CreateLfn - Tells the caller if we must create an LFN for the UnicodeName or
751 BOOLEAN GenerateShortName
;
756 // First see if we must generate a short name.
759 if ((OemName
->Length
== 0) ||
760 !FatIsNameShortOemValid( IrpContext
, *OemName
, FALSE
, FALSE
, FALSE
) ||
761 FatSpaceInName( IrpContext
, UnicodeName
)) {
763 WCHAR ShortNameBuffer
[12];
764 UNICODE_STRING ShortUnicodeName
;
765 GENERATE_NAME_CONTEXT Context
;
766 BOOLEAN TrySuggestedShortName
;
773 GenerateShortName
= TRUE
;
775 TrySuggestedShortName
= (SuggestedShortName
!= NULL
);
778 // Now generate a short name.
781 ShortUnicodeName
.Length
= 0;
782 ShortUnicodeName
.MaximumLength
= 12 * sizeof(WCHAR
);
783 ShortUnicodeName
.Buffer
= ShortNameBuffer
;
785 RtlZeroMemory( &Context
, sizeof( GENERATE_NAME_CONTEXT
) );
791 FatUnpinBcb( IrpContext
, Bcb
);
793 if (TrySuggestedShortName
) {
796 // Try our caller's candidate first. Note that this must have
797 // been uppercased previously.
800 ShortUnicodeName
.Length
= SuggestedShortName
->Length
;
801 ShortUnicodeName
.MaximumLength
= SuggestedShortName
->MaximumLength
;
802 ShortUnicodeName
.Buffer
= SuggestedShortName
->Buffer
;
804 TrySuggestedShortName
= FALSE
;
808 RtlGenerate8dot3Name( UnicodeName
, TRUE
, &Context
, &ShortUnicodeName
);
812 // We have a candidate, make sure it doesn't exist.
816 #pragma prefast( suppress:28931, "needed for debug build" )
818 Status
= RtlUnicodeStringToCountedOemString( ShortName
,
822 NT_ASSERT( Status
== STATUS_SUCCESS
);
824 FatLocateSimpleOemDirent( IrpContext
,
840 FatUnpinBcb( IrpContext
, Bcb
);
846 // Only do this copy if the two string are indeed different.
849 if (ShortName
!= OemName
) {
850 ShortName
->Length
= OemName
->Length
;
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
859 _Analysis_assume_(ShortName
->MaximumLength
<= OemName
->Length
);
861 RtlCopyMemory( ShortName
->Buffer
, OemName
->Buffer
, OemName
->Length
);
864 GenerateShortName
= FALSE
;
868 // Now see if the caller will have to use unicode string as an LFN
871 if (GenerateShortName
) {
874 *AllLowerComponent
= FALSE
;
875 *AllLowerExtension
= FALSE
;
879 FatEvaluateNameCase( IrpContext
,
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
903 This routine takes a UNICODE string and sees if it is eligible for
904 the special case optimization.
908 UnicodeName - Provides the original final name.
910 AllLowerComponent - Returns whether this compoent was all lower case.
912 AllLowerExtension - Returns wheather the extension was all lower case.
914 CreateLfn - Tells the call if we must create an LFN for the UnicodeName.
927 BOOLEAN ExtensionPresent
= FALSE
;
930 UNREFERENCED_PARAMETER( IrpContext
);
934 for (i
= 0; i
< UnicodeName
->Length
/ sizeof(WCHAR
); i
++) {
938 c
= UnicodeName
->Buffer
[i
];
940 if ((c
>= 'A') && (c
<= 'Z')) {
944 } else if ((c
>= 'a') && (c
<= 'z')) {
948 } else if ((c
>= 0x0080) && FatData
.CodePageInvariant
) {
954 // If we come to a period, figure out if the extension was
960 *CreateLfn
= (Lowers
!= 0) && (Uppers
!= 0);
962 *AllLowerComponent
= !(*CreateLfn
) && (Lowers
!= 0);
964 ExtensionPresent
= TRUE
;
967 // Now reset the uppers and lowers count.
975 // Now check again for creating an LFN.
978 *CreateLfn
= (*CreateLfn
||
979 (i
!= UnicodeName
->Length
/ sizeof(WCHAR
)) ||
980 ((Lowers
!= 0) && (Uppers
!= 0)));
983 // Now we know the final state of CreateLfn, update the two
984 // "AllLower" booleans.
987 if (ExtensionPresent
) {
989 *AllLowerComponent
= !(*CreateLfn
) && *AllLowerComponent
;
990 *AllLowerExtension
= !(*CreateLfn
) && (Lowers
!= 0);
994 *AllLowerComponent
= !(*CreateLfn
) && (Lowers
!= 0);
995 *AllLowerExtension
= FALSE
;
1004 IN PIRP_CONTEXT IrpContext
,
1005 IN PUNICODE_STRING UnicodeName
1010 Routine Description:
1012 This routine takes a UNICODE string and sees if it contains any spaces.
1016 UnicodeName - Provides the final name.
1020 BOOLEAN - TRUE if it does, FALSE if it doesn't.
1028 UNREFERENCED_PARAMETER( IrpContext
);
1030 for (i
=0; i
< UnicodeName
->Length
/sizeof(WCHAR
); i
++) {
1032 if (UnicodeName
->Buffer
[i
] == L
' ') {
1041 FatUnicodeRestoreShortNameCase(
1042 IN PUNICODE_STRING ShortNameWithCase
,
1043 IN BOOLEAN LowerCase8
,
1044 IN BOOLEAN LowerCase3
1049 Routine Description:
1051 Given an 8.3 filename in a UNICODE_STRING, fix the case of the
1052 name given the two 8do3 case flags.
1056 ShortNameWithCase - the UNICODE_STRING containing the short name.
1057 LowerCase8, LowerCase3 - the flag indicating whether to downcase the 8dot3 name component.
1066 UNICODE_STRING DownCaseSeg
;
1070 NT_ASSERT( ShortNameWithCase
->Length
<= 24 );
1073 // Have to repair the case of the short name
1076 for (i
= 0; i
< (ShortNameWithCase
->Length
/sizeof(WCHAR
)) &&
1077 ShortNameWithCase
->Buffer
[i
] != L
'.'; i
++);
1080 // Now pointing at the '.', or otherwise the end of name component
1085 DownCaseSeg
.Buffer
= ShortNameWithCase
->Buffer
;
1086 DownCaseSeg
.MaximumLength
= DownCaseSeg
.Length
= i
*sizeof(WCHAR
);
1088 RtlDowncaseUnicodeString(&DownCaseSeg
, &DownCaseSeg
, FALSE
);
1094 // Now pointing at first wchar of the extension.
1100 // It is not neccesarily the case that we can rely on the flag
1101 // indicating that we really have an extension.
1104 if ((i
*sizeof(WCHAR
)) < ShortNameWithCase
->Length
) {
1105 DownCaseSeg
.Buffer
= &ShortNameWithCase
->Buffer
[i
];
1106 DownCaseSeg
.MaximumLength
= DownCaseSeg
.Length
= ShortNameWithCase
->Length
- i
*sizeof(WCHAR
);
1108 RtlDowncaseUnicodeString(&DownCaseSeg
, &DownCaseSeg
, FALSE
);