/** @file Functions for manipulating file names. Copyright (c) 2005 - 2015, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "Fat.h" /** This function checks whether the input FileName is a valid 8.3 short name. If the input FileName is a valid 8.3, the output is the 8.3 short name; otherwise, the output is the base tag of 8.3 short name. @param FileName - The input unicode filename. @param File8Dot3Name - The output ascii 8.3 short name or base tag of 8.3 short name. @retval TRUE - The input unicode filename is a valid 8.3 short name. @retval FALSE - The input unicode filename is not a valid 8.3 short name. **/ BOOLEAN FatCheckIs8Dot3Name ( IN CHAR16 *FileName, OUT CHAR8 *File8Dot3Name ) { BOOLEAN PossibleShortName; CHAR16 *TempName; CHAR16 *ExtendName; CHAR16 *SeparateDot; UINTN MainNameLen; UINTN ExtendNameLen; PossibleShortName = TRUE; SeparateDot = NULL; SetMem (File8Dot3Name, FAT_NAME_LEN, ' '); for (TempName = FileName; *TempName != '\0'; TempName++) { if (*TempName == L'.') { SeparateDot = TempName; } } if (SeparateDot == NULL) { // // Extended filename is not detected // MainNameLen = TempName - FileName; ExtendName = TempName; ExtendNameLen = 0; } else { // // Extended filename is detected // MainNameLen = SeparateDot - FileName; ExtendName = SeparateDot + 1; ExtendNameLen = TempName - ExtendName; } // // We scan the filename for the second time // to check if there exists any extra blanks and dots // while (--TempName >= FileName) { if ((*TempName == L'.' || *TempName == L' ') && (TempName != SeparateDot)) { // // There exist extra blanks and dots // PossibleShortName = FALSE; } } if (MainNameLen == 0) { PossibleShortName = FALSE; } if (MainNameLen > FAT_MAIN_NAME_LEN) { PossibleShortName = FALSE; MainNameLen = FAT_MAIN_NAME_LEN; } if (ExtendNameLen > FAT_EXTEND_NAME_LEN) { PossibleShortName = FALSE; ExtendNameLen = FAT_EXTEND_NAME_LEN; } if (FatStrToFat (FileName, MainNameLen, File8Dot3Name)) { PossibleShortName = FALSE; } if (FatStrToFat (ExtendName, ExtendNameLen, File8Dot3Name + FAT_MAIN_NAME_LEN)) { PossibleShortName = FALSE; } return PossibleShortName; } /** Trim the trailing blanks of fat name. @param Name - The Char8 string needs to be trimmed. @param Len - The length of the fat name. The real length of the fat name after the trailing blanks are trimmed. **/ STATIC UINTN FatTrimAsciiTrailingBlanks ( IN CHAR8 *Name, IN UINTN Len ) { while (Len > 0 && Name[Len - 1] == ' ') { Len--; } return Len; } /** Convert the ascii fat name to the unicode string and strip trailing spaces, and if necessary, convert the unicode string to lower case. @param FatName - The Char8 string needs to be converted. @param Len - The length of the fat name. @param LowerCase - Indicate whether to convert the string to lower case. @param Str - The result of the conversion. **/ VOID FatNameToStr ( IN CHAR8 *FatName, IN UINTN Len, IN UINTN LowerCase, OUT CHAR16 *Str ) { // // First, trim the trailing blanks // Len = FatTrimAsciiTrailingBlanks (FatName, Len); // // Convert fat string to unicode string // FatFatToStr (Len, FatName, Str); // // If the name is to be lower cased, do it now // if (LowerCase != 0) { FatStrLwr (Str); } } /** This function generates 8Dot3 name from user specified name for a newly created file. @param Parent - The parent directory. @param DirEnt - The directory entry whose 8Dot3Name needs to be generated. **/ VOID FatCreate8Dot3Name ( IN FAT_OFILE *Parent, IN FAT_DIRENT *DirEnt ) { CHAR8 *ShortName; CHAR8 *ShortNameChar; UINTN BaseTagLen; UINTN Index; UINTN Retry; UINT8 Segment; union { UINT32 Crc; struct HEX_DATA { UINT8 Segment : HASH_VALUE_TAG_LEN; } Hex[HASH_VALUE_TAG_LEN]; } HashValue; // // Make sure the whole directory has been loaded // ASSERT (Parent->ODir->EndOfDir); ShortName = DirEnt->Entry.FileName; // // Trim trailing blanks of 8.3 name // BaseTagLen = FatTrimAsciiTrailingBlanks (ShortName, FAT_MAIN_NAME_LEN); if (BaseTagLen > SPEC_BASE_TAG_LEN) { BaseTagLen = SPEC_BASE_TAG_LEN; } // // We first use the algorithm described by spec. // ShortNameChar = ShortName + BaseTagLen; *ShortNameChar++ = '~'; *ShortNameChar = '1'; Retry = 0; while (*FatShortNameHashSearch (Parent->ODir, ShortName) != NULL) { *ShortNameChar = (CHAR8)(*ShortNameChar + 1); if (++Retry == MAX_SPEC_RETRY) { // // We use new algorithm to generate 8.3 name // ASSERT (DirEnt->FileString != NULL); gBS->CalculateCrc32 (DirEnt->FileString, StrSize (DirEnt->FileString), &HashValue.Crc); if (BaseTagLen > HASH_BASE_TAG_LEN) { BaseTagLen = HASH_BASE_TAG_LEN; } ShortNameChar = ShortName + BaseTagLen; for (Index = 0; Index < HASH_VALUE_TAG_LEN; Index++) { Segment = HashValue.Hex[Index].Segment; if (Segment > 9) { *ShortNameChar++ = (CHAR8)(Segment - 10 + 'A'); } else { *ShortNameChar++ = (CHAR8)(Segment + '0'); } } *ShortNameChar++ = '~'; *ShortNameChar = '1'; } } } /** Check the string is lower case or upper case and it is used by fatname to dir entry count @param Str - The string which needs to be checked. @param InCaseFlag - The input case flag which is returned when the string is lower case. @retval OutCaseFlag - The output case flag. **/ STATIC UINT8 FatCheckNameCase ( IN CHAR16 *Str, IN UINT8 InCaseFlag ) { CHAR16 Buffer[FAT_MAIN_NAME_LEN + 1 + FAT_EXTEND_NAME_LEN + 1]; UINT8 OutCaseFlag; // // Assume the case of input string is mixed // OutCaseFlag = FAT_CASE_MIXED; // // Lower case a copy of the string, if it matches the // original then the string is lower case // StrCpyS (Buffer, ARRAY_SIZE (Buffer), Str); FatStrLwr (Buffer); if (StrCmp (Str, Buffer) == 0) { OutCaseFlag = InCaseFlag; } // // Upper case a copy of the string, if it matches the // original then the string is upper case // StrCpyS (Buffer, ARRAY_SIZE (Buffer), Str); FatStrUpr (Buffer); if (StrCmp (Str, Buffer) == 0) { OutCaseFlag = 0; } return OutCaseFlag; } /** Set the caseflag value for the directory entry. @param DirEnt - The logical directory entry whose caseflag value is to be set. **/ VOID FatSetCaseFlag ( IN FAT_DIRENT *DirEnt ) { CHAR16 LfnBuffer[FAT_MAIN_NAME_LEN + 1 + FAT_EXTEND_NAME_LEN + 1]; CHAR16 *TempCharPtr; CHAR16 *ExtendName; CHAR16 *FileNameCharPtr; UINT8 CaseFlag; ExtendName = NULL; TempCharPtr = LfnBuffer; FileNameCharPtr = DirEnt->FileString; ASSERT (StrSize (DirEnt->FileString) <= sizeof (LfnBuffer)); while ((*TempCharPtr = *FileNameCharPtr) != 0) { if (*TempCharPtr == L'.') { ExtendName = TempCharPtr; } TempCharPtr++; FileNameCharPtr++; } CaseFlag = 0; if (ExtendName != NULL) { *ExtendName = 0; ExtendName++; CaseFlag = (UINT8)(CaseFlag | FatCheckNameCase (ExtendName, FAT_CASE_EXT_LOWER)); } CaseFlag = (UINT8)(CaseFlag | FatCheckNameCase (LfnBuffer, FAT_CASE_NAME_LOWER)); if ((CaseFlag & FAT_CASE_MIXED) == 0) { // // We just need one directory entry to store this file name entry // DirEnt->Entry.CaseFlag = CaseFlag; } else { // // We need one extra directory entry to store the mixed case entry // DirEnt->Entry.CaseFlag = 0; DirEnt->EntryCount++; } } /** Convert the 8.3 ASCII fat name to cased Unicode string according to case flag. @param DirEnt - The corresponding directory entry. @param FileString - The output Unicode file name. @param FileStringMax The max length of FileString. **/ VOID FatGetFileNameViaCaseFlag ( IN FAT_DIRENT *DirEnt, IN OUT CHAR16 *FileString, IN UINTN FileStringMax ) { UINT8 CaseFlag; CHAR8 *File8Dot3Name; CHAR16 TempExt[1 + FAT_EXTEND_NAME_LEN + 1]; // // Store file extension like ".txt" // CaseFlag = DirEnt->Entry.CaseFlag; File8Dot3Name = DirEnt->Entry.FileName; FatNameToStr (File8Dot3Name, FAT_MAIN_NAME_LEN, CaseFlag & FAT_CASE_NAME_LOWER, FileString); FatNameToStr (File8Dot3Name + FAT_MAIN_NAME_LEN, FAT_EXTEND_NAME_LEN, CaseFlag & FAT_CASE_EXT_LOWER, &TempExt[1]); if (TempExt[1] != 0) { TempExt[0] = L'.'; StrCatS (FileString, FileStringMax, TempExt); } } /** Get the Check sum for a short name. @param ShortNameString - The short name for a file. @retval Sum - UINT8 checksum. **/ UINT8 FatCheckSum ( IN CHAR8 *ShortNameString ) { UINTN ShortNameLen; UINT8 Sum; Sum = 0; for (ShortNameLen = FAT_NAME_LEN; ShortNameLen != 0; ShortNameLen--) { Sum = (UINT8)((((Sum & 1) != 0) ? 0x80 : 0) + (Sum >> 1) + *ShortNameString++); } return Sum; } /** Takes Path as input, returns the next name component in Name, and returns the position after Name (e.g., the start of the next name component) @param Path - The path of one file. @param Name - The next name component in Path. The position after Name in the Path **/ CHAR16 * FatGetNextNameComponent ( IN CHAR16 *Path, OUT CHAR16 *Name ) { while (*Path != 0 && *Path != PATH_NAME_SEPARATOR) { *Name++ = *Path++; } *Name = 0; // // Get off of trailing path name separator // while (*Path == PATH_NAME_SEPARATOR) { Path++; } return Path; } /** Check whether the IFileName is valid long file name. If the IFileName is a valid long file name, then we trim the possible leading blanks and leading/trailing dots. the trimmed filename is stored in OutputFileName @param InputFileName - The input file name. @param OutputFileName - The output file name. @retval TRUE - The InputFileName is a valid long file name. @retval FALSE - The InputFileName is not a valid long file name. **/ BOOLEAN FatFileNameIsValid ( IN CHAR16 *InputFileName, OUT CHAR16 *OutputFileName ) { CHAR16 *TempNamePointer; CHAR16 TempChar; // // Trim Leading blanks // while (*InputFileName == L' ') { InputFileName++; } TempNamePointer = OutputFileName; while (*InputFileName != 0) { *TempNamePointer++ = *InputFileName++; } // // Trim Trailing blanks and dots // while (TempNamePointer > OutputFileName) { TempChar = *(TempNamePointer - 1); if (TempChar != L' ' && TempChar != L'.') { break; } TempNamePointer--; } *TempNamePointer = 0; // // Per FAT Spec the file name should meet the following criteria: // C1. Length (FileLongName) <= 255 // C2. Length (X:FileFullPath) <= 260 // Here we check C1. // if (TempNamePointer - OutputFileName > EFI_FILE_STRING_LENGTH) { return FALSE; } // // See if there is any illegal characters within the name // do { if (*OutputFileName < 0x20 || *OutputFileName == '\"' || *OutputFileName == '*' || *OutputFileName == '/' || *OutputFileName == ':' || *OutputFileName == '<' || *OutputFileName == '>' || *OutputFileName == '?' || *OutputFileName == '\\' || *OutputFileName == '|' ) { return FALSE; } OutputFileName++; } while (*OutputFileName != 0); return TRUE; }