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 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 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 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 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 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 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 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 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 * 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 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