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