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