/** @file
Functions for performing directory entry io.
Copyright (c) 2005 - 2015, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "Fat.h"
/**
Get a directory entry from disk for the Ofile.
@param Parent - The parent of the OFile which need to update.
@param IoMode - Indicate whether to read directory entry or write directory entry.
@param EntryPos - The position of the directory entry to be accessed.
@param Entry - The directory entry read or written.
@retval EFI_SUCCESS - Access the directory entry successfully.
@return other - An error occurred when reading the directory entry.
**/
STATIC
EFI_STATUS
FatAccessEntry (
IN FAT_OFILE *Parent,
IN IO_MODE IoMode,
IN UINTN EntryPos,
IN OUT VOID *Entry
)
{
UINTN Position;
UINTN BufferSize;
Position = EntryPos * sizeof (FAT_DIRECTORY_ENTRY);
if (Position >= Parent->FileSize) {
//
// End of directory
//
ASSERT (IoMode == ReadData);
((FAT_DIRECTORY_ENTRY *)Entry)->FileName[0] = EMPTY_ENTRY_MARK;
((FAT_DIRECTORY_ENTRY *)Entry)->Attributes = 0;
return EFI_SUCCESS;
}
BufferSize = sizeof (FAT_DIRECTORY_ENTRY);
return FatAccessOFile (Parent, IoMode, Position, &BufferSize, Entry, NULL);
}
/**
Save the directory entry to disk.
@param OFile - The parent OFile which needs to update.
@param DirEnt - The directory entry to be saved.
@retval EFI_SUCCESS - Store the directory entry successfully.
@return other - An error occurred when writing the directory entry.
**/
EFI_STATUS
FatStoreDirEnt (
IN FAT_OFILE *OFile,
IN FAT_DIRENT *DirEnt
)
{
EFI_STATUS Status;
FAT_DIRECTORY_LFN LfnEntry;
UINTN EntryPos;
CHAR16 *LfnBufferPointer;
CHAR16 LfnBuffer[MAX_LFN_ENTRIES * LFN_CHAR_TOTAL + 1];
UINT8 EntryCount;
UINT8 LfnOrdinal;
EntryPos = DirEnt->EntryPos;
EntryCount = DirEnt->EntryCount;
//
// Write directory entry
//
Status = FatAccessEntry (OFile, WriteData, EntryPos, &DirEnt->Entry);
if (EFI_ERROR (Status)) {
return Status;
}
if (--EntryCount > 0) {
//
// Write LFN directory entry
//
SetMem (LfnBuffer, sizeof (CHAR16) * LFN_CHAR_TOTAL * EntryCount, 0xff);
Status = StrCpyS (
LfnBuffer,
ARRAY_SIZE (LfnBuffer),
DirEnt->FileString
);
if (EFI_ERROR (Status)) {
return Status;
}
LfnBufferPointer = LfnBuffer;
LfnEntry.Attributes = FAT_ATTRIBUTE_LFN;
LfnEntry.Type = 0;
LfnEntry.MustBeZero = 0;
LfnEntry.Checksum = FatCheckSum (DirEnt->Entry.FileName);
for (LfnOrdinal = 1; LfnOrdinal <= EntryCount; LfnOrdinal++) {
LfnEntry.Ordinal = LfnOrdinal;
if (LfnOrdinal == EntryCount) {
LfnEntry.Ordinal |= FAT_LFN_LAST;
}
CopyMem (LfnEntry.Name1, LfnBufferPointer, sizeof (CHAR16) * LFN_CHAR1_LEN);
LfnBufferPointer += LFN_CHAR1_LEN;
CopyMem (LfnEntry.Name2, LfnBufferPointer, sizeof (CHAR16) * LFN_CHAR2_LEN);
LfnBufferPointer += LFN_CHAR2_LEN;
CopyMem (LfnEntry.Name3, LfnBufferPointer, sizeof (CHAR16) * LFN_CHAR3_LEN);
LfnBufferPointer += LFN_CHAR3_LEN;
EntryPos--;
if (DirEnt->Invalid) {
LfnEntry.Ordinal = DELETE_ENTRY_MARK;
}
Status = FatAccessEntry (OFile, WriteData, EntryPos, &LfnEntry);
if (EFI_ERROR (Status)) {
return Status;
}
}
}
return EFI_SUCCESS;
}
/**
Determine whether the directory entry is "." or ".." entry.
@param DirEnt - The corresponding directory entry.
@retval TRUE - The directory entry is "." or ".." directory entry
@retval FALSE - The directory entry is not "." or ".." directory entry
**/
BOOLEAN
FatIsDotDirEnt (
IN FAT_DIRENT *DirEnt
)
{
CHAR16 *FileString;
FileString = DirEnt->FileString;
if ((StrCmp (FileString, L".") == 0) || (StrCmp (FileString, L"..") == 0)) {
return TRUE;
}
return FALSE;
}
/**
Set the OFile's cluster info in its directory entry.
@param OFile - The corresponding OFile.
**/
STATIC
VOID
FatSetDirEntCluster (
IN FAT_OFILE *OFile
)
{
UINTN Cluster;
FAT_DIRENT *DirEnt;
DirEnt = OFile->DirEnt;
Cluster = OFile->FileCluster;
DirEnt->Entry.FileClusterHigh = (UINT16)(Cluster >> 16);
DirEnt->Entry.FileCluster = (UINT16)Cluster;
}
/**
Set the OFile's cluster and size info in its directory entry.
@param OFile - The corresponding OFile.
**/
VOID
FatUpdateDirEntClusterSizeInfo (
IN FAT_OFILE *OFile
)
{
ASSERT (OFile->ODir == NULL);
OFile->DirEnt->Entry.FileSize = (UINT32)OFile->FileSize;
FatSetDirEntCluster (OFile);
}
/**
Copy all the information of DirEnt2 to DirEnt1 except for 8.3 name.
@param DirEnt1 - The destination directory entry.
@param DirEnt2 - The source directory entry.
**/
VOID
FatCloneDirEnt (
IN FAT_DIRENT *DirEnt1,
IN FAT_DIRENT *DirEnt2
)
{
UINT8 *Entry1;
UINT8 *Entry2;
Entry1 = (UINT8 *)&DirEnt1->Entry;
Entry2 = (UINT8 *)&DirEnt2->Entry;
CopyMem (
Entry1 + FAT_ENTRY_INFO_OFFSET,
Entry2 + FAT_ENTRY_INFO_OFFSET,
sizeof (FAT_DIRECTORY_ENTRY) - FAT_ENTRY_INFO_OFFSET
);
}
/**
Get the LFN for the directory entry.
@param Parent - The parent directory.
@param DirEnt - The directory entry to get LFN.
**/
STATIC
VOID
FatLoadLongNameEntry (
IN FAT_OFILE *Parent,
IN FAT_DIRENT *DirEnt
)
{
CHAR16 LfnBuffer[MAX_LFN_ENTRIES * LFN_CHAR_TOTAL + 1];
CHAR16 *LfnBufferPointer;
CHAR8 *File8Dot3Name;
UINTN EntryPos;
UINT8 LfnOrdinal;
UINT8 LfnChecksum;
FAT_DIRECTORY_LFN LfnEntry;
EFI_STATUS Status;
EntryPos = DirEnt->EntryPos;
File8Dot3Name = DirEnt->Entry.FileName;
LfnBufferPointer = LfnBuffer;
//
// Computes checksum for LFN
//
LfnChecksum = FatCheckSum (File8Dot3Name);
LfnOrdinal = 1;
do {
if (EntryPos == 0) {
LfnBufferPointer = LfnBuffer;
break;
}
EntryPos--;
Status = FatAccessEntry (Parent, ReadData, EntryPos, &LfnEntry);
if (EFI_ERROR (Status) ||
(LfnEntry.Attributes != FAT_ATTRIBUTE_LFN) ||
(LfnEntry.MustBeZero != 0) ||
(LfnEntry.Checksum != LfnChecksum) ||
((LfnEntry.Ordinal & (~FAT_LFN_LAST)) != LfnOrdinal) ||
(LfnOrdinal > MAX_LFN_ENTRIES)
)
{
//
// The directory entry does not have a long file name or
// some error occurs when loading long file name for a directory entry,
// and then we load the long name from short name
//
LfnBufferPointer = LfnBuffer;
break;
}
CopyMem (LfnBufferPointer, LfnEntry.Name1, sizeof (CHAR16) * LFN_CHAR1_LEN);
LfnBufferPointer += LFN_CHAR1_LEN;
CopyMem (LfnBufferPointer, LfnEntry.Name2, sizeof (CHAR16) * LFN_CHAR2_LEN);
LfnBufferPointer += LFN_CHAR2_LEN;
CopyMem (LfnBufferPointer, LfnEntry.Name3, sizeof (CHAR16) * LFN_CHAR3_LEN);
LfnBufferPointer += LFN_CHAR3_LEN;
LfnOrdinal++;
} while ((LfnEntry.Ordinal & FAT_LFN_LAST) == 0);
DirEnt->EntryCount = LfnOrdinal;
//
// Terminate current Lfnbuffer
//
*LfnBufferPointer = 0;
if (LfnBufferPointer == LfnBuffer) {
//
// Fail to get the long file name from long file name entry,
// get the file name from short name
//
FatGetFileNameViaCaseFlag (
DirEnt,
LfnBuffer,
ARRAY_SIZE (LfnBuffer)
);
}
DirEnt->FileString = AllocateCopyPool (StrSize (LfnBuffer), LfnBuffer);
}
/**
Add this directory entry node to the list of directory entries and hash table.
@param ODir - The parent OFile which needs to be updated.
@param DirEnt - The directory entry to be added.
**/
STATIC
VOID
FatAddDirEnt (
IN FAT_ODIR *ODir,
IN FAT_DIRENT *DirEnt
)
{
if (DirEnt->Link.BackLink == NULL) {
DirEnt->Link.BackLink = &ODir->ChildList;
}
InsertTailList (DirEnt->Link.BackLink, &DirEnt->Link);
FatInsertToHashTable (ODir, DirEnt);
}
/**
Load from disk the next directory entry at current end of directory position.
@param OFile - The parent OFile.
@param PtrDirEnt - The directory entry that is loaded.
@retval EFI_SUCCESS - Load the directory entry successfully.
@retval EFI_OUT_OF_RESOURCES - Out of resource.
@return other - An error occurred when reading the directory entries.
**/
STATIC
EFI_STATUS
FatLoadNextDirEnt (
IN FAT_OFILE *OFile,
OUT FAT_DIRENT **PtrDirEnt
)
{
EFI_STATUS Status;
FAT_DIRENT *DirEnt;
FAT_ODIR *ODir;
FAT_DIRECTORY_ENTRY Entry;
ODir = OFile->ODir;
//
// Make sure the parent's directory has been opened
//
ASSERT (ODir != NULL);
//
// Assert we have not reached the end of directory
//
ASSERT (!ODir->EndOfDir);
DirEnt = NULL;
for ( ; ;) {
//
// Read the next directory entry until we find a valid directory entry (excluding lfn entry)
//
Status = FatAccessEntry (OFile, ReadData, ODir->CurrentEndPos, &Entry);
if (EFI_ERROR (Status)) {
return Status;
}
if (((UINT8)Entry.FileName[0] != DELETE_ENTRY_MARK) && ((Entry.Attributes & FAT_ATTRIBUTE_VOLUME_ID) == 0)) {
//
// We get a valid directory entry, then handle it
//
break;
}
ODir->CurrentEndPos++;
}
if (Entry.FileName[0] != EMPTY_ENTRY_MARK) {
//
// Although FAT spec states this field is always 0 for FAT12 & FAT16, some applications
// might use it for some special usage, it is safer to zero it in memory for FAT12 & FAT16.
//
if (OFile->Volume->FatType != Fat32) {
Entry.FileClusterHigh = 0;
}
//
// This is a valid directory entry
//
DirEnt = AllocateZeroPool (sizeof (FAT_DIRENT));
if (DirEnt == NULL) {
return EFI_OUT_OF_RESOURCES;
}
DirEnt->Signature = FAT_DIRENT_SIGNATURE;
//
// Remember the directory's entry position on disk
//
DirEnt->EntryPos = (UINT16)ODir->CurrentEndPos;
CopyMem (&DirEnt->Entry, &Entry, sizeof (FAT_DIRECTORY_ENTRY));
FatLoadLongNameEntry (OFile, DirEnt);
if (DirEnt->FileString == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto Done;
}
//
// Add this directory entry to directory
//
FatAddDirEnt (ODir, DirEnt);
//
// Point to next directory entry
//
ODir->CurrentEndPos++;
} else {
ODir->EndOfDir = TRUE;
}
*PtrDirEnt = DirEnt;
return EFI_SUCCESS;
Done:
FatFreeDirEnt (DirEnt);
return Status;
}
/**
Get the directory entry's info into Buffer.
@param Volume - FAT file system volume.
@param DirEnt - The corresponding directory entry.
@param BufferSize - Size of Buffer.
@param Buffer - Buffer containing file info.
@retval EFI_SUCCESS - Get the file info successfully.
@retval EFI_BUFFER_TOO_SMALL - The buffer is too small.
**/
EFI_STATUS
FatGetDirEntInfo (
IN FAT_VOLUME *Volume,
IN FAT_DIRENT *DirEnt,
IN OUT UINTN *BufferSize,
OUT VOID *Buffer
)
{
UINTN Size;
UINTN NameSize;
UINTN ResultSize;
UINTN Cluster;
EFI_STATUS Status;
EFI_FILE_INFO *Info;
FAT_DIRECTORY_ENTRY *Entry;
FAT_DATE_TIME FatLastAccess;
ASSERT_VOLUME_LOCKED (Volume);
Size = SIZE_OF_EFI_FILE_INFO;
NameSize = StrSize (DirEnt->FileString);
ResultSize = Size + NameSize;
Status = EFI_BUFFER_TOO_SMALL;
if (*BufferSize >= ResultSize) {
Status = EFI_SUCCESS;
Entry = &DirEnt->Entry;
Info = Buffer;
Info->Size = ResultSize;
if ((Entry->Attributes & FAT_ATTRIBUTE_DIRECTORY) != 0) {
Cluster = (Entry->FileClusterHigh << 16) | Entry->FileCluster;
Info->PhysicalSize = FatPhysicalDirSize (Volume, Cluster);
Info->FileSize = Info->PhysicalSize;
} else {
Info->FileSize = Entry->FileSize;
Info->PhysicalSize = FatPhysicalFileSize (Volume, Entry->FileSize);
}
ZeroMem (&FatLastAccess.Time, sizeof (FatLastAccess.Time));
CopyMem (&FatLastAccess.Date, &Entry->FileLastAccess, sizeof (FatLastAccess.Date));
FatFatTimeToEfiTime (&FatLastAccess, &Info->LastAccessTime);
FatFatTimeToEfiTime (&Entry->FileCreateTime, &Info->CreateTime);
FatFatTimeToEfiTime (&Entry->FileModificationTime, &Info->ModificationTime);
Info->Attribute = Entry->Attributes & EFI_FILE_VALID_ATTR;
CopyMem ((CHAR8 *)Buffer + Size, DirEnt->FileString, NameSize);
}
*BufferSize = ResultSize;
return Status;
}
/**
Search the directory for the directory entry whose filename is FileNameString.
@param OFile - The parent OFile whose directory is to be searched.
@param FileNameString - The filename to be searched.
@param PtrDirEnt - pointer to the directory entry if found.
@retval EFI_SUCCESS - Find the directory entry or not found.
@return other - An error occurred when reading the directory entries.
**/
STATIC
EFI_STATUS
FatSearchODir (
IN FAT_OFILE *OFile,
IN CHAR16 *FileNameString,
OUT FAT_DIRENT **PtrDirEnt
)
{
BOOLEAN PossibleShortName;
CHAR8 File8Dot3Name[FAT_NAME_LEN];
FAT_ODIR *ODir;
FAT_DIRENT *DirEnt;
EFI_STATUS Status;
ODir = OFile->ODir;
ASSERT (ODir != NULL);
//
// Check if the file name is a valid short name
//
PossibleShortName = FatCheckIs8Dot3Name (FileNameString, File8Dot3Name);
//
// Search the hash table first
//
DirEnt = *FatLongNameHashSearch (ODir, FileNameString);
if ((DirEnt == NULL) && PossibleShortName) {
DirEnt = *FatShortNameHashSearch (ODir, File8Dot3Name);
}
if (DirEnt == NULL) {
//
// We fail to get the directory entry from hash table; we then
// search the rest directory
//
while (!ODir->EndOfDir) {
Status = FatLoadNextDirEnt (OFile, &DirEnt);
if (EFI_ERROR (Status)) {
return Status;
}
if (DirEnt != NULL) {
if (FatStriCmp (FileNameString, DirEnt->FileString) == 0) {
break;
}
if (PossibleShortName && (CompareMem (File8Dot3Name, DirEnt->Entry.FileName, FAT_NAME_LEN) == 0)) {
break;
}
}
}
}
*PtrDirEnt = DirEnt;
return EFI_SUCCESS;
}
/**
Set the OFile's current directory cursor to the list head.
@param OFile - The directory OFile whose directory cursor is reset.
**/
VOID
FatResetODirCursor (
IN FAT_OFILE *OFile
)
{
FAT_ODIR *ODir;
ODir = OFile->ODir;
ASSERT (ODir != NULL);
ODir->CurrentCursor = &(ODir->ChildList);
ODir->CurrentPos = 0;
}
/**
Set the directory's cursor to the next and get the next directory entry.
@param OFile - The parent OFile.
@param PtrDirEnt - The next directory entry.
@retval EFI_SUCCESS - We get the next directory entry successfully.
@return other - An error occurred when get next directory entry.
**/
EFI_STATUS
FatGetNextDirEnt (
IN FAT_OFILE *OFile,
OUT FAT_DIRENT **PtrDirEnt
)
{
EFI_STATUS Status;
FAT_DIRENT *DirEnt;
FAT_ODIR *ODir;
ODir = OFile->ODir;
ASSERT (ODir != NULL);
if (ODir->CurrentCursor->ForwardLink == &ODir->ChildList) {
//
// End of directory, we will try one more time
//
if (!ODir->EndOfDir) {
//
// Read directory from disk
//
Status = FatLoadNextDirEnt (OFile, &DirEnt);
if (EFI_ERROR (Status)) {
return Status;
}
}
}
if (ODir->CurrentCursor->ForwardLink == &ODir->ChildList) {
//
// End of directory, return NULL
//
DirEnt = NULL;
ODir->CurrentPos = ODir->CurrentEndPos;
} else {
ODir->CurrentCursor = ODir->CurrentCursor->ForwardLink;
DirEnt = DIRENT_FROM_LINK (ODir->CurrentCursor);
ODir->CurrentPos = DirEnt->EntryPos + 1;
}
*PtrDirEnt = DirEnt;
return EFI_SUCCESS;
}
/**
Set the directory entry count according to the filename.
@param OFile - The corresponding OFile.
@param DirEnt - The directory entry to be set.
**/
STATIC
VOID
FatSetEntryCount (
IN FAT_OFILE *OFile,
IN FAT_DIRENT *DirEnt
)
{
CHAR16 *FileString;
CHAR8 *File8Dot3Name;
//
// Get new entry count and set the 8.3 name
//
DirEnt->EntryCount = 1;
FileString = DirEnt->FileString;
File8Dot3Name = DirEnt->Entry.FileName;
SetMem (File8Dot3Name, FAT_NAME_LEN, ' ');
if (StrCmp (FileString, L".") == 0) {
//
// "." entry
//
File8Dot3Name[0] = '.';
FatCloneDirEnt (DirEnt, OFile->DirEnt);
} else if (StrCmp (FileString, L"..") == 0) {
//
// ".." entry
//
File8Dot3Name[0] = '.';
File8Dot3Name[1] = '.';
FatCloneDirEnt (DirEnt, OFile->Parent->DirEnt);
} else {
//
// Normal name
//
if (FatCheckIs8Dot3Name (FileString, File8Dot3Name)) {
//
// This file name is a valid 8.3 file name, we need to further check its case flag
//
FatSetCaseFlag (DirEnt);
} else {
//
// The file name is not a valid 8.3 name we need to generate an 8.3 name for it
//
FatCreate8Dot3Name (OFile, DirEnt);
DirEnt->EntryCount = (UINT8)(LFN_ENTRY_NUMBER (StrLen (FileString)) + DirEnt->EntryCount);
}
}
}
/**
Append a zero cluster to the current OFile.
@param OFile - The directory OFile which needs to be updated.
@retval EFI_SUCCESS - Append a zero cluster to the OFile successfully.
@return other - An error occurred when appending the zero cluster.
**/
STATIC
EFI_STATUS
FatExpandODir (
IN FAT_OFILE *OFile
)
{
return FatExpandOFile (OFile, OFile->FileSize + OFile->Volume->ClusterSize);
}
/**
Search the Root OFile for the possible volume label.
@param Root - The Root OFile.
@param DirEnt - The returned directory entry of volume label.
@retval EFI_SUCCESS - The search process is completed successfully.
@return other - An error occurred when searching volume label.
**/
STATIC
EFI_STATUS
FatSeekVolumeId (
IN FAT_OFILE *Root,
OUT FAT_DIRENT *DirEnt
)
{
EFI_STATUS Status;
UINTN EntryPos;
FAT_DIRECTORY_ENTRY *Entry;
EntryPos = 0;
Entry = &DirEnt->Entry;
DirEnt->Invalid = TRUE;
do {
Status = FatAccessEntry (Root, ReadData, EntryPos, Entry);
if (EFI_ERROR (Status)) {
return Status;
}
if (((UINT8)Entry->FileName[0] != DELETE_ENTRY_MARK) && (((Entry->Attributes) & (~FAT_ATTRIBUTE_ARCHIVE)) == FAT_ATTRIBUTE_VOLUME_ID)) {
DirEnt->EntryPos = (UINT16)EntryPos;
DirEnt->EntryCount = 1;
DirEnt->Invalid = FALSE;
break;
}
EntryPos++;
} while (Entry->FileName[0] != EMPTY_ENTRY_MARK);
return EFI_SUCCESS;
}
/**
Use First Fit Algorithm to insert directory entry.
Only this function will erase "E5" entries in a directory.
In view of safest recovery, this function will only be triggered
when maximum directory entry number has reached.
@param OFile - The corresponding OFile.
@param DirEnt - The directory entry to be inserted.
@retval EFI_SUCCESS - The directory entry has been successfully inserted.
@retval EFI_VOLUME_FULL - The directory can not hold more directory entries.
@return Others - Some error occurred when inserting new directory entries.
**/
STATIC
EFI_STATUS
FatFirstFitInsertDirEnt (
IN FAT_OFILE *OFile,
IN FAT_DIRENT *DirEnt
)
{
EFI_STATUS Status;
FAT_ODIR *ODir;
LIST_ENTRY *CurrentEntry;
FAT_DIRENT *CurrentDirEnt;
UINT32 CurrentPos;
UINT32 LabelPos;
UINT32 NewEntryPos;
UINT16 EntryCount;
FAT_DIRENT LabelDirEnt;
LabelPos = 0;
if (OFile->Parent == NULL) {
Status = FatSeekVolumeId (OFile, &LabelDirEnt);
if (EFI_ERROR (Status)) {
return Status;
}
if (!LabelDirEnt.Invalid) {
LabelPos = LabelDirEnt.EntryPos;
}
}
EntryCount = DirEnt->EntryCount;
NewEntryPos = EntryCount;
CurrentPos = 0;
ODir = OFile->ODir;
for (CurrentEntry = ODir->ChildList.ForwardLink;
CurrentEntry != &ODir->ChildList;
CurrentEntry = CurrentEntry->ForwardLink
)
{
CurrentDirEnt = DIRENT_FROM_LINK (CurrentEntry);
if (NewEntryPos + CurrentDirEnt->EntryCount <= CurrentDirEnt->EntryPos) {
if ((LabelPos > NewEntryPos) || (LabelPos <= CurrentPos)) {
//
// first fit succeeded
//
goto Done;
}
}
CurrentPos = CurrentDirEnt->EntryPos;
NewEntryPos = CurrentPos + EntryCount;
}
if (NewEntryPos >= ODir->CurrentEndPos) {
return EFI_VOLUME_FULL;
}
Done:
DirEnt->EntryPos = (UINT16)NewEntryPos;
DirEnt->Link.BackLink = CurrentEntry;
return EFI_SUCCESS;
}
/**
Find the new directory entry position for the directory entry.
@param OFile - The corresponding OFile.
@param DirEnt - The directory entry whose new position is to be set.
@retval EFI_SUCCESS - The new directory entry position is successfully found.
@retval EFI_VOLUME_FULL - The directory has reach its maximum capacity.
@return other - An error occurred when reading the directory entry.
**/
STATIC
EFI_STATUS
FatNewEntryPos (
IN FAT_OFILE *OFile,
IN FAT_DIRENT *DirEnt
)
{
EFI_STATUS Status;
FAT_ODIR *ODir;
FAT_DIRENT *TempDirEnt;
UINT32 NewEndPos;
ODir = OFile->ODir;
ASSERT (ODir != NULL);
//
// Make sure the whole directory has been loaded
//
while (!ODir->EndOfDir) {
Status = FatLoadNextDirEnt (OFile, &TempDirEnt);
if (EFI_ERROR (Status)) {
return Status;
}
}
//
// We will append this entry to the end of directory
//
FatGetCurrentFatTime (&DirEnt->Entry.FileCreateTime);
CopyMem (&DirEnt->Entry.FileModificationTime, &DirEnt->Entry.FileCreateTime, sizeof (FAT_DATE_TIME));
CopyMem (&DirEnt->Entry.FileLastAccess, &DirEnt->Entry.FileCreateTime.Date, sizeof (FAT_DATE));
NewEndPos = ODir->CurrentEndPos + DirEnt->EntryCount;
if (NewEndPos * sizeof (FAT_DIRECTORY_ENTRY) > OFile->FileSize) {
if (NewEndPos >= (OFile->IsFixedRootDir ? OFile->Volume->RootEntries : FAT_MAX_DIRENTRY_COUNT)) {
//
// We try to use fist fit algorithm to insert this directory entry
//
return FatFirstFitInsertDirEnt (OFile, DirEnt);
}
//
// We should allocate a new cluster for this directory
//
Status = FatExpandODir (OFile);
if (EFI_ERROR (Status)) {
return Status;
}
}
//
// We append our directory entry at the end of directory file
//
ODir->CurrentEndPos = NewEndPos;
DirEnt->EntryPos = (UINT16)(ODir->CurrentEndPos - 1);
return EFI_SUCCESS;
}
/**
Get the directory entry for the volume.
@param Volume - FAT file system volume.
@param Name - The file name of the volume.
@retval EFI_SUCCESS - Update the volume with the directory entry successfully.
@return others - An error occurred when getting volume label.
**/
EFI_STATUS
FatGetVolumeEntry (
IN FAT_VOLUME *Volume,
IN CHAR16 *Name
)
{
EFI_STATUS Status;
FAT_DIRENT LabelDirEnt;
*Name = 0;
Status = FatSeekVolumeId (Volume->Root, &LabelDirEnt);
if (!EFI_ERROR (Status)) {
if (!LabelDirEnt.Invalid) {
FatNameToStr (LabelDirEnt.Entry.FileName, FAT_NAME_LEN, FALSE, Name);
}
}
return Status;
}
/**
Set the relevant directory entry into disk for the volume.
@param Volume - FAT file system volume.
@param Name - The new file name of the volume.
@retval EFI_SUCCESS - Update the Volume successfully.
@retval EFI_UNSUPPORTED - The input label is not a valid volume label.
@return other - An error occurred when setting volume label.
**/
EFI_STATUS
FatSetVolumeEntry (
IN FAT_VOLUME *Volume,
IN CHAR16 *Name
)
{
EFI_STATUS Status;
FAT_DIRENT LabelDirEnt;
FAT_OFILE *Root;
Root = Volume->Root;
Status = FatSeekVolumeId (Volume->Root, &LabelDirEnt);
if (EFI_ERROR (Status)) {
return Status;
}
if (LabelDirEnt.Invalid) {
//
// If there is not the relevant directory entry, create a new one
//
ZeroMem (&LabelDirEnt, sizeof (FAT_DIRENT));
LabelDirEnt.EntryCount = 1;
Status = FatNewEntryPos (Root, &LabelDirEnt);
if (EFI_ERROR (Status)) {
return Status;
}
LabelDirEnt.Entry.Attributes = FAT_ATTRIBUTE_VOLUME_ID;
}
SetMem (LabelDirEnt.Entry.FileName, FAT_NAME_LEN, ' ');
if (FatStrToFat (Name, FAT_NAME_LEN, LabelDirEnt.Entry.FileName)) {
return EFI_UNSUPPORTED;
}
FatGetCurrentFatTime (&LabelDirEnt.Entry.FileModificationTime);
return FatStoreDirEnt (Root, &LabelDirEnt);
}
/**
Create "." and ".." directory entries in the newly-created parent OFile.
@param OFile - The parent OFile.
@retval EFI_SUCCESS - The dot directory entries are successfully created.
@return other - An error occurred when creating the directory entry.
**/
EFI_STATUS
FatCreateDotDirEnts (
IN FAT_OFILE *OFile
)
{
EFI_STATUS Status;
FAT_DIRENT *DirEnt;
Status = FatExpandODir (OFile);
if (EFI_ERROR (Status)) {
return Status;
}
FatSetDirEntCluster (OFile);
//
// Create "."
//
Status = FatCreateDirEnt (OFile, L".", FAT_ATTRIBUTE_DIRECTORY, &DirEnt);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Create ".."
//
Status = FatCreateDirEnt (OFile, L"..", FAT_ATTRIBUTE_DIRECTORY, &DirEnt);
return Status;
}
/**
Create a directory entry in the parent OFile.
@param OFile - The parent OFile.
@param FileName - The filename of the newly-created directory entry.
@param Attributes - The attribute of the newly-created directory entry.
@param PtrDirEnt - The pointer to the newly-created directory entry.
@retval EFI_SUCCESS - The directory entry is successfully created.
@retval EFI_OUT_OF_RESOURCES - Not enough memory to create the directory entry.
@return other - An error occurred when creating the directory entry.
**/
EFI_STATUS
FatCreateDirEnt (
IN FAT_OFILE *OFile,
IN CHAR16 *FileName,
IN UINT8 Attributes,
OUT FAT_DIRENT **PtrDirEnt
)
{
FAT_DIRENT *DirEnt;
FAT_ODIR *ODir;
EFI_STATUS Status;
ASSERT (OFile != NULL);
ODir = OFile->ODir;
ASSERT (ODir != NULL);
DirEnt = AllocateZeroPool (sizeof (FAT_DIRENT));
if (DirEnt == NULL) {
return EFI_OUT_OF_RESOURCES;
}
DirEnt->Signature = FAT_DIRENT_SIGNATURE;
DirEnt->FileString = AllocateCopyPool (StrSize (FileName), FileName);
if (DirEnt->FileString == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto Done;
}
//
// Determine how many directory entries we need
//
FatSetEntryCount (OFile, DirEnt);
//
// Determine the file's directory entry position
//
Status = FatNewEntryPos (OFile, DirEnt);
if (EFI_ERROR (Status)) {
goto Done;
}
FatAddDirEnt (ODir, DirEnt);
DirEnt->Entry.Attributes = Attributes;
*PtrDirEnt = DirEnt;
DEBUG ((DEBUG_INFO, "FSOpen: Created new directory entry '%S'\n", DirEnt->FileString));
return FatStoreDirEnt (OFile, DirEnt);
Done:
FatFreeDirEnt (DirEnt);
return Status;
}
/**
Remove this directory entry node from the list of directory entries and hash table.
@param OFile - The parent OFile.
@param DirEnt - The directory entry to be removed.
@retval EFI_SUCCESS - The directory entry is successfully removed.
@return other - An error occurred when removing the directory entry.
**/
EFI_STATUS
FatRemoveDirEnt (
IN FAT_OFILE *OFile,
IN FAT_DIRENT *DirEnt
)
{
FAT_ODIR *ODir;
ODir = OFile->ODir;
if (ODir->CurrentCursor == &DirEnt->Link) {
//
// Move the directory cursor to its previous directory entry
//
ODir->CurrentCursor = ODir->CurrentCursor->BackLink;
}
//
// Remove from directory entry list
//
RemoveEntryList (&DirEnt->Link);
//
// Remove from hash table
//
FatDeleteFromHashTable (ODir, DirEnt);
DirEnt->Entry.FileName[0] = DELETE_ENTRY_MARK;
DirEnt->Invalid = TRUE;
return FatStoreDirEnt (OFile, DirEnt);
}
/**
Open the directory entry to get the OFile.
@param Parent - The parent OFile.
@param DirEnt - The directory entry to be opened.
@retval EFI_SUCCESS - The directory entry is successfully opened.
@retval EFI_OUT_OF_RESOURCES - not enough memory to allocate a new OFile.
@return other - An error occurred when opening the directory entry.
**/
EFI_STATUS
FatOpenDirEnt (
IN FAT_OFILE *Parent,
IN FAT_DIRENT *DirEnt
)
{
FAT_OFILE *OFile;
FAT_VOLUME *Volume;
if (DirEnt->OFile == NULL) {
//
// Open the directory entry
//
OFile = AllocateZeroPool (sizeof (FAT_OFILE));
if (OFile == NULL) {
return EFI_OUT_OF_RESOURCES;
}
OFile->Signature = FAT_OFILE_SIGNATURE;
InitializeListHead (&OFile->Opens);
InitializeListHead (&OFile->ChildHead);
OFile->Parent = Parent;
OFile->DirEnt = DirEnt;
if (Parent != NULL) {
//
// The newly created OFile is not root
//
Volume = Parent->Volume;
OFile->FullPathLen = Parent->FullPathLen + 1 + StrLen (DirEnt->FileString);
OFile->FileCluster = ((DirEnt->Entry.FileClusterHigh) << 16) | (DirEnt->Entry.FileCluster);
InsertTailList (&Parent->ChildHead, &OFile->ChildLink);
} else {
//
// The newly created OFile is root
//
Volume = VOLUME_FROM_ROOT_DIRENT (DirEnt);
Volume->Root = OFile;
OFile->FileCluster = Volume->RootCluster;
if (Volume->FatType != Fat32) {
OFile->IsFixedRootDir = TRUE;
}
}
OFile->FileCurrentCluster = OFile->FileCluster;
OFile->Volume = Volume;
InsertHeadList (&Volume->CheckRef, &OFile->CheckLink);
OFile->FileSize = DirEnt->Entry.FileSize;
if ((DirEnt->Entry.Attributes & FAT_ATTRIBUTE_DIRECTORY) != 0) {
if (OFile->IsFixedRootDir) {
OFile->FileSize = Volume->RootEntries * sizeof (FAT_DIRECTORY_ENTRY);
} else {
OFile->FileSize = FatPhysicalDirSize (Volume, OFile->FileCluster);
}
FatRequestODir (OFile);
if (OFile->ODir == NULL) {
return EFI_OUT_OF_RESOURCES;
}
}
DirEnt->OFile = OFile;
}
return EFI_SUCCESS;
}
/**
Close the directory entry and free the OFile.
@param DirEnt - The directory entry to be closed.
**/
VOID
FatCloseDirEnt (
IN FAT_DIRENT *DirEnt
)
{
FAT_OFILE *OFile;
FAT_VOLUME *Volume;
OFile = DirEnt->OFile;
ASSERT (OFile != NULL);
Volume = OFile->Volume;
if (OFile->ODir != NULL) {
FatDiscardODir (OFile);
}
if (OFile->Parent == NULL) {
Volume->Root = NULL;
} else {
RemoveEntryList (&OFile->ChildLink);
}
FreePool (OFile);
DirEnt->OFile = NULL;
if (DirEnt->Invalid == TRUE) {
//
// Free directory entry itself
//
FatFreeDirEnt (DirEnt);
}
}
/**
Traverse filename and open all OFiles that can be opened.
Update filename pointer to the component that can't be opened.
If more than one name component remains, returns an error;
otherwise, return the remaining name component so that the caller might choose to create it.
@param PtrOFile - As input, the reference OFile; as output, the located OFile.
@param FileName - The file name relevant to the OFile.
@param Attributes - The attribute of the destination OFile.
@param NewFileName - The remaining file name.
@retval EFI_NOT_FOUND - The file name can't be opened and there is more than one
components within the name left (this means the name can
not be created either).
@retval EFI_INVALID_PARAMETER - The parameter is not valid.
@retval EFI_SUCCESS - Open the file successfully.
@return other - An error occurred when locating the OFile.
**/
EFI_STATUS
FatLocateOFile (
IN OUT FAT_OFILE **PtrOFile,
IN CHAR16 *FileName,
IN UINT8 Attributes,
OUT CHAR16 *NewFileName
)
{
EFI_STATUS Status;
FAT_VOLUME *Volume;
CHAR16 ComponentName[EFI_PATH_STRING_LENGTH];
UINTN FileNameLen;
BOOLEAN DirIntended;
CHAR16 *Next;
FAT_OFILE *OFile;
FAT_DIRENT *DirEnt;
DirEnt = NULL;
FileNameLen = StrLen (FileName);
if (FileNameLen == 0) {
return EFI_INVALID_PARAMETER;
}
OFile = *PtrOFile;
Volume = OFile->Volume;
DirIntended = FALSE;
if (FileName[FileNameLen - 1] == PATH_NAME_SEPARATOR) {
DirIntended = TRUE;
}
//
// If name starts with path name separator, then move to root OFile
//
if (*FileName == PATH_NAME_SEPARATOR) {
OFile = Volume->Root;
FileName++;
FileNameLen--;
}
//
// Per FAT Spec the file name should meet the following criteria:
// C1. Length (FileLongName) <= 255
// C2. Length (X:FileFullPath) <= 260
// Here we check C2 first.
//
if (2 + OFile->FullPathLen + 1 + FileNameLen + 1 > EFI_PATH_STRING_LENGTH) {
//
// Full path length can not surpass 256
//
return EFI_INVALID_PARAMETER;
}
//
// Start at current location
//
Next = FileName;
for ( ; ;) {
//
// Get the next component name
//
FileName = Next;
Next = FatGetNextNameComponent (FileName, ComponentName);
//
// If end of the file name, we're done
//
if (ComponentName[0] == 0) {
if (DirIntended && (OFile->ODir == NULL)) {
return EFI_NOT_FOUND;
}
NewFileName[0] = 0;
break;
}
//
// If "dot", then current
//
if (StrCmp (ComponentName, L".") == 0) {
continue;
}
//
// If "dot dot", then parent
//
if (StrCmp (ComponentName, L"..") == 0) {
if (OFile->Parent == NULL) {
return EFI_INVALID_PARAMETER;
}
OFile = OFile->Parent;
continue;
}
if (!FatFileNameIsValid (ComponentName, NewFileName)) {
return EFI_INVALID_PARAMETER;
}
//
// We have a component name, try to open it
//
if (OFile->ODir == NULL) {
//
// This file isn't a directory, can't open it
//
return EFI_NOT_FOUND;
}
//
// Search the compName in the directory
//
Status = FatSearchODir (OFile, NewFileName, &DirEnt);
if (EFI_ERROR (Status)) {
return Status;
}
if (DirEnt == NULL) {
//
// component name is not found in the directory
//
if (*Next != 0) {
return EFI_NOT_FOUND;
}
if (DirIntended && ((Attributes & FAT_ATTRIBUTE_DIRECTORY) == 0)) {
return EFI_INVALID_PARAMETER;
}
//
// It's the last component name - return with the open
// path and the remaining name
//
break;
}
Status = FatOpenDirEnt (OFile, DirEnt);
if (EFI_ERROR (Status)) {
return Status;
}
OFile = DirEnt->OFile;
}
*PtrOFile = OFile;
return EFI_SUCCESS;
}