diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/DirectoryManage.c')
-rw-r--r-- | src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/DirectoryManage.c | 1391 |
1 files changed, 1391 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/DirectoryManage.c b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/DirectoryManage.c new file mode 100644 index 00000000..7d9abbc5 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/DirectoryManage.c @@ -0,0 +1,1391 @@ +/** @file + Functions for performing directory entry io. + +Copyright (c) 2005 - 2015, Intel Corporation. All rights reserved.<BR> +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 ((EFI_D_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<NUL>) <= 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; +} + |