blob: 17914ccf92b30bc4b31ae67f0b4fe8e19cb5c04c [file] [log] [blame]
/** @file
Functions for manipulating file names.
Copyright (c) 2005 - 2015, Intel Corporation. All rights reserved.<BR>
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<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;
}