mirror of https://github.com/acidanthera/audk.git
500 lines
12 KiB
C
500 lines
12 KiB
C
/** @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;
|
|
}
|