• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2   Functions for manipulating file names.
3 
4 Copyright (c) 2005 - 2015, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials are licensed and made available
6 under the terms and conditions of the BSD License which accompanies this
7 distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9 
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13 **/
14 
15 #include "Fat.h"
16 
17 /**
18 
19   This function checks whether the input FileName is a valid 8.3 short name.
20   If the input FileName is a valid 8.3, the output is the 8.3 short name;
21   otherwise, the output is the base tag of 8.3 short name.
22 
23   @param  FileName              - The input unicode filename.
24   @param  File8Dot3Name         - The output ascii 8.3 short name or base tag of 8.3 short name.
25 
26   @retval TRUE                  - The input unicode filename is a valid 8.3 short name.
27   @retval FALSE                 - The input unicode filename is not a valid 8.3 short name.
28 
29 **/
30 BOOLEAN
FatCheckIs8Dot3Name(IN CHAR16 * FileName,OUT CHAR8 * File8Dot3Name)31 FatCheckIs8Dot3Name (
32   IN  CHAR16    *FileName,
33   OUT CHAR8     *File8Dot3Name
34   )
35 {
36   BOOLEAN PossibleShortName;
37   CHAR16  *TempName;
38   CHAR16  *ExtendName;
39   CHAR16  *SeparateDot;
40   UINTN   MainNameLen;
41   UINTN   ExtendNameLen;
42 
43   PossibleShortName = TRUE;
44   SeparateDot       = NULL;
45   SetMem (File8Dot3Name, FAT_NAME_LEN, ' ');
46   for (TempName = FileName; *TempName != '\0'; TempName++) {
47     if (*TempName == L'.') {
48       SeparateDot = TempName;
49     }
50   }
51 
52   if (SeparateDot == NULL) {
53     //
54     // Extended filename is not detected
55     //
56     MainNameLen   = TempName - FileName;
57     ExtendName    = TempName;
58     ExtendNameLen = 0;
59   } else {
60     //
61     // Extended filename is detected
62     //
63     MainNameLen   = SeparateDot - FileName;
64     ExtendName    = SeparateDot + 1;
65     ExtendNameLen = TempName - ExtendName;
66   }
67   //
68   // We scan the filename for the second time
69   // to check if there exists any extra blanks and dots
70   //
71   while (--TempName >= FileName) {
72     if ((*TempName == L'.' || *TempName == L' ') && (TempName != SeparateDot)) {
73       //
74       // There exist extra blanks and dots
75       //
76       PossibleShortName = FALSE;
77     }
78   }
79 
80   if (MainNameLen == 0) {
81     PossibleShortName = FALSE;
82   }
83 
84   if (MainNameLen > FAT_MAIN_NAME_LEN) {
85     PossibleShortName = FALSE;
86     MainNameLen       = FAT_MAIN_NAME_LEN;
87   }
88 
89   if (ExtendNameLen > FAT_EXTEND_NAME_LEN) {
90     PossibleShortName = FALSE;
91     ExtendNameLen     = FAT_EXTEND_NAME_LEN;
92   }
93 
94   if (FatStrToFat (FileName, MainNameLen, File8Dot3Name)) {
95     PossibleShortName = FALSE;
96   }
97 
98   if (FatStrToFat (ExtendName, ExtendNameLen, File8Dot3Name + FAT_MAIN_NAME_LEN)) {
99     PossibleShortName = FALSE;
100   }
101 
102   return PossibleShortName;
103 }
104 
105 /**
106 
107   Trim the trailing blanks of fat name.
108 
109   @param  Name                  - The Char8 string needs to be trimed.
110   @param  Len                   - The length of the fat name.
111 
112   The real length of the fat name after the trailing blanks are trimmed.
113 
114 **/
115 STATIC
116 UINTN
FatTrimAsciiTrailingBlanks(IN CHAR8 * Name,IN UINTN Len)117 FatTrimAsciiTrailingBlanks (
118   IN CHAR8        *Name,
119   IN UINTN        Len
120   )
121 {
122   while (Len > 0 && Name[Len - 1] == ' ') {
123     Len--;
124   }
125 
126   return Len;
127 }
128 
129 /**
130 
131   Convert the ascii fat name to the unicode string and strip trailing spaces,
132   and if necessary, convert the unicode string to lower case.
133 
134   @param  FatName               - The Char8 string needs to be converted.
135   @param  Len                   - The length of the fat name.
136   @param  LowerCase             - Indicate whether to convert the string to lower case.
137   @param  Str                   - The result of the convertion.
138 
139 **/
140 VOID
FatNameToStr(IN CHAR8 * FatName,IN UINTN Len,IN UINTN LowerCase,OUT CHAR16 * Str)141 FatNameToStr (
142   IN  CHAR8            *FatName,
143   IN  UINTN            Len,
144   IN  UINTN            LowerCase,
145   OUT CHAR16           *Str
146   )
147 {
148   //
149   // First, trim the trailing blanks
150   //
151   Len = FatTrimAsciiTrailingBlanks (FatName, Len);
152   //
153   // Convert fat string to unicode string
154   //
155   FatFatToStr (Len, FatName, Str);
156 
157   //
158   // If the name is to be lower cased, do it now
159   //
160   if (LowerCase != 0) {
161     FatStrLwr (Str);
162   }
163 }
164 
165 /**
166 
167   This function generates 8Dot3 name from user specified name for a newly created file.
168 
169   @param  Parent                - The parent directory.
170   @param  DirEnt                - The directory entry whose 8Dot3Name needs to be generated.
171 
172 **/
173 VOID
FatCreate8Dot3Name(IN FAT_OFILE * Parent,IN FAT_DIRENT * DirEnt)174 FatCreate8Dot3Name (
175   IN FAT_OFILE    *Parent,
176   IN FAT_DIRENT   *DirEnt
177   )
178 {
179   CHAR8 *ShortName;
180   CHAR8 *ShortNameChar;
181   UINTN BaseTagLen;
182   UINTN Index;
183   UINTN Retry;
184   UINT8 Segment;
185   union {
186     UINT32  Crc;
187     struct HEX_DATA {
188       UINT8 Segment : HASH_VALUE_TAG_LEN;
189     } Hex[HASH_VALUE_TAG_LEN];
190   } HashValue;
191   //
192   // Make sure the whole directory has been loaded
193   //
194   ASSERT (Parent->ODir->EndOfDir);
195   ShortName = DirEnt->Entry.FileName;
196 
197   //
198   // Trim trailing blanks of 8.3 name
199   //
200   BaseTagLen = FatTrimAsciiTrailingBlanks (ShortName, FAT_MAIN_NAME_LEN);
201   if (BaseTagLen > SPEC_BASE_TAG_LEN) {
202     BaseTagLen = SPEC_BASE_TAG_LEN;
203   }
204   //
205   // We first use the algorithm described by spec.
206   //
207   ShortNameChar     = ShortName + BaseTagLen;
208   *ShortNameChar++  = '~';
209   *ShortNameChar    = '1';
210   Retry = 0;
211   while (*FatShortNameHashSearch (Parent->ODir, ShortName) != NULL) {
212     *ShortNameChar = (CHAR8)(*ShortNameChar + 1);
213     if (++Retry == MAX_SPEC_RETRY) {
214       //
215       // We use new algorithm to generate 8.3 name
216       //
217       ASSERT (DirEnt->FileString != NULL);
218       gBS->CalculateCrc32 (DirEnt->FileString, StrSize (DirEnt->FileString), &HashValue.Crc);
219 
220       if (BaseTagLen > HASH_BASE_TAG_LEN) {
221         BaseTagLen = HASH_BASE_TAG_LEN;
222       }
223 
224       ShortNameChar = ShortName + BaseTagLen;
225       for (Index = 0; Index < HASH_VALUE_TAG_LEN; Index++) {
226         Segment = HashValue.Hex[Index].Segment;
227         if (Segment > 9) {
228           *ShortNameChar++ = (CHAR8)(Segment - 10 + 'A');
229         } else {
230           *ShortNameChar++ = (CHAR8)(Segment + '0');
231         }
232       }
233 
234       *ShortNameChar++  = '~';
235       *ShortNameChar    = '1';
236     }
237   }
238 }
239 
240 /**
241 
242   Check the string is lower case or upper case
243   and it is used by fatname to dir entry count
244 
245   @param Str                   - The string which needs to be checked.
246   @param InCaseFlag            - The input case flag which is returned when the string is lower case.
247 
248   @retval OutCaseFlag           - The output case flag.
249 
250 **/
251 STATIC
252 UINT8
FatCheckNameCase(IN CHAR16 * Str,IN UINT8 InCaseFlag)253 FatCheckNameCase (
254   IN CHAR16           *Str,
255   IN UINT8            InCaseFlag
256   )
257 {
258   CHAR16  Buffer[FAT_MAIN_NAME_LEN + 1 + FAT_EXTEND_NAME_LEN + 1];
259   UINT8   OutCaseFlag;
260 
261   //
262   // Assume the case of input string is mixed
263   //
264   OutCaseFlag = FAT_CASE_MIXED;
265   //
266   // Lower case a copy of the string, if it matches the
267   // original then the string is lower case
268   //
269   StrCpyS (Buffer, ARRAY_SIZE (Buffer), Str);
270   FatStrLwr (Buffer);
271   if (StrCmp (Str, Buffer) == 0) {
272     OutCaseFlag = InCaseFlag;
273   }
274   //
275   // Upper case a copy of the string, if it matches the
276   // original then the string is upper case
277   //
278   StrCpyS (Buffer, ARRAY_SIZE (Buffer), Str);
279   FatStrUpr (Buffer);
280   if (StrCmp (Str, Buffer) == 0) {
281     OutCaseFlag = 0;
282   }
283 
284   return OutCaseFlag;
285 }
286 
287 /**
288 
289   Set the caseflag value for the directory entry.
290 
291   @param DirEnt                - The logical directory entry whose caseflag value is to be set.
292 
293 **/
294 VOID
FatSetCaseFlag(IN FAT_DIRENT * DirEnt)295 FatSetCaseFlag (
296   IN FAT_DIRENT   *DirEnt
297   )
298 {
299   CHAR16  LfnBuffer[FAT_MAIN_NAME_LEN + 1 + FAT_EXTEND_NAME_LEN + 1];
300   CHAR16  *TempCharPtr;
301   CHAR16  *ExtendName;
302   CHAR16  *FileNameCharPtr;
303   UINT8   CaseFlag;
304 
305   ExtendName      = NULL;
306   TempCharPtr     = LfnBuffer;
307   FileNameCharPtr = DirEnt->FileString;
308   ASSERT (StrSize (DirEnt->FileString) <= sizeof (LfnBuffer));
309   while ((*TempCharPtr = *FileNameCharPtr) != 0) {
310     if (*TempCharPtr == L'.') {
311       ExtendName = TempCharPtr;
312     }
313 
314     TempCharPtr++;
315     FileNameCharPtr++;
316   }
317 
318   CaseFlag = 0;
319   if (ExtendName != NULL) {
320     *ExtendName = 0;
321     ExtendName++;
322     CaseFlag = (UINT8)(CaseFlag | FatCheckNameCase (ExtendName, FAT_CASE_EXT_LOWER));
323   }
324 
325   CaseFlag = (UINT8)(CaseFlag | FatCheckNameCase (LfnBuffer, FAT_CASE_NAME_LOWER));
326   if ((CaseFlag & FAT_CASE_MIXED) == 0) {
327     //
328     // We just need one directory entry to store this file name entry
329     //
330     DirEnt->Entry.CaseFlag = CaseFlag;
331   } else {
332     //
333     // We need one extra directory entry to store the mixed case entry
334     //
335     DirEnt->Entry.CaseFlag = 0;
336     DirEnt->EntryCount++;
337   }
338 }
339 
340 /**
341 
342   Convert the 8.3 ASCII fat name to cased Unicode string according to case flag.
343 
344   @param  DirEnt                - The corresponding directory entry.
345   @param  FileString            - The output Unicode file name.
346   @param  FileStringMax           The max length of FileString.
347 
348 **/
349 VOID
FatGetFileNameViaCaseFlag(IN FAT_DIRENT * DirEnt,IN OUT CHAR16 * FileString,IN UINTN FileStringMax)350 FatGetFileNameViaCaseFlag (
351   IN     FAT_DIRENT   *DirEnt,
352   IN OUT CHAR16       *FileString,
353   IN     UINTN        FileStringMax
354   )
355 {
356   UINT8   CaseFlag;
357   CHAR8   *File8Dot3Name;
358   CHAR16  TempExt[1 + FAT_EXTEND_NAME_LEN + 1];
359   //
360   // Store file extension like ".txt"
361   //
362   CaseFlag      = DirEnt->Entry.CaseFlag;
363   File8Dot3Name = DirEnt->Entry.FileName;
364 
365   FatNameToStr (File8Dot3Name, FAT_MAIN_NAME_LEN, CaseFlag & FAT_CASE_NAME_LOWER, FileString);
366   FatNameToStr (File8Dot3Name + FAT_MAIN_NAME_LEN, FAT_EXTEND_NAME_LEN, CaseFlag & FAT_CASE_EXT_LOWER, &TempExt[1]);
367   if (TempExt[1] != 0) {
368     TempExt[0] = L'.';
369     StrCatS (FileString, FileStringMax, TempExt);
370   }
371 }
372 
373 /**
374 
375   Get the Check sum for a short name.
376 
377   @param  ShortNameString       - The short name for a file.
378 
379   @retval Sum                   - UINT8 checksum.
380 
381 **/
382 UINT8
FatCheckSum(IN CHAR8 * ShortNameString)383 FatCheckSum (
384   IN CHAR8  *ShortNameString
385   )
386 {
387   UINTN ShortNameLen;
388   UINT8 Sum;
389   Sum = 0;
390   for (ShortNameLen = FAT_NAME_LEN; ShortNameLen != 0; ShortNameLen--) {
391     Sum = (UINT8)((((Sum & 1) != 0) ? 0x80 : 0) + (Sum >> 1) + *ShortNameString++);
392   }
393 
394   return Sum;
395 }
396 
397 /**
398 
399   Takes Path as input, returns the next name component
400   in Name, and returns the position after Name (e.g., the
401   start of the next name component)
402 
403   @param  Path                  - The path of one file.
404   @param  Name                  - The next name component in Path.
405 
406   The position after Name in the Path
407 
408 **/
409 CHAR16 *
FatGetNextNameComponent(IN CHAR16 * Path,OUT CHAR16 * Name)410 FatGetNextNameComponent (
411   IN  CHAR16      *Path,
412   OUT CHAR16      *Name
413   )
414 {
415   while (*Path != 0 && *Path != PATH_NAME_SEPARATOR) {
416     *Name++ = *Path++;
417   }
418   *Name = 0;
419   //
420   // Get off of trailing path name separator
421   //
422   while (*Path == PATH_NAME_SEPARATOR) {
423     Path++;
424   }
425 
426   return Path;
427 }
428 
429 /**
430 
431   Check whether the IFileName is valid long file name. If the IFileName is a valid
432   long file name, then we trim the possible leading blanks and leading/trailing dots.
433   the trimmed filename is stored in OutputFileName
434 
435   @param  InputFileName         - The input file name.
436   @param  OutputFileName        - The output file name.
437 
438   @retval TRUE                  - The InputFileName is a valid long file name.
439   @retval FALSE                 - The InputFileName is not a valid long file name.
440 
441 **/
442 BOOLEAN
FatFileNameIsValid(IN CHAR16 * InputFileName,OUT CHAR16 * OutputFileName)443 FatFileNameIsValid (
444   IN  CHAR16  *InputFileName,
445   OUT CHAR16  *OutputFileName
446   )
447 {
448   CHAR16  *TempNamePointer;
449   CHAR16  TempChar;
450   //
451   // Trim Leading blanks
452   //
453   while (*InputFileName == L' ') {
454     InputFileName++;
455   }
456 
457   TempNamePointer = OutputFileName;
458   while (*InputFileName != 0) {
459     *TempNamePointer++ = *InputFileName++;
460   }
461   //
462   // Trim Trailing blanks and dots
463   //
464   while (TempNamePointer > OutputFileName) {
465     TempChar = *(TempNamePointer - 1);
466     if (TempChar != L' ' && TempChar != L'.') {
467       break;
468     }
469 
470     TempNamePointer--;
471   }
472 
473   *TempNamePointer = 0;
474 
475   //
476   // Per FAT Spec the file name should meet the following criteria:
477   //   C1. Length (FileLongName) <= 255
478   //   C2. Length (X:FileFullPath<NUL>) <= 260
479   // Here we check C1.
480   //
481   if (TempNamePointer - OutputFileName > EFI_FILE_STRING_LENGTH) {
482     return FALSE;
483   }
484   //
485   // See if there is any illegal characters within the name
486   //
487   do {
488     if (*OutputFileName < 0x20 ||
489         *OutputFileName == '\"' ||
490         *OutputFileName == '*' ||
491         *OutputFileName == '/' ||
492         *OutputFileName == ':' ||
493         *OutputFileName == '<' ||
494         *OutputFileName == '>' ||
495         *OutputFileName == '?' ||
496         *OutputFileName == '\\' ||
497         *OutputFileName == '|'
498         ) {
499       return FALSE;
500     }
501 
502     OutputFileName++;
503   } while (*OutputFileName != 0);
504   return TRUE;
505 }
506