| /** @file | |
| Functions for manipulating file names. | |
| Copyright (c) 2005 - 2015, Intel Corporation. All rights reserved.<BR> | |
| This program and the accompanying materials are licensed and made available | |
| under the terms and conditions of the BSD License which accompanies this | |
| distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #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 trimed. | |
| @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 convertion. | |
| **/ | |
| 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<NUL>) <= 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; | |
| } |