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 trimmed.
|
---|
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 conversion.
|
---|
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 | }
|
---|