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