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