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