/** @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; }