/** @file | |
This driver uses the EFI_FIRMWARE_VOLUME2_PROTOCOL to expose files in firmware | |
volumes via the the EFI_SIMPLE_FILESYSTEM_PROTOCOL and EFI_FILE_PROTOCOL. | |
It will expose a single directory, containing one file for each file in the firmware | |
volume. If a file has a UI section, its contents will be used as a filename. | |
Otherwise, a string representation of the GUID will be used. | |
Files of an executable type (That is PEIM, DRIVER, COMBINED_PEIM_DRIVER and APPLICATION) | |
will have ".efi" added to their filename. | |
Its primary intended use is to be able to start EFI applications embedded in FVs | |
from the UEFI shell. It is entirely read-only. | |
Copyright (c) 2014, ARM Limited. All rights reserved. | |
Copyright (c) 2014 - 2018, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "FvSimpleFileSystemInternal.h" | |
// | |
// Template for EFI_FILE_SYSTEM_INFO data structure. | |
// | |
EFI_FILE_SYSTEM_INFO mFsInfoTemplate = { | |
0, // Populate at runtime | |
TRUE, // Read-only | |
0, // Don't know volume size | |
0, // No free space | |
0, // Don't know block size | |
L"" // Populate at runtime | |
}; | |
// | |
// Template for EFI_FILE_PROTOCOL data structure. | |
// | |
EFI_FILE_PROTOCOL mFileSystemTemplate = { | |
EFI_FILE_PROTOCOL_REVISION, | |
FvSimpleFileSystemOpen, | |
FvSimpleFileSystemClose, | |
FvSimpleFileSystemDelete, | |
FvSimpleFileSystemRead, | |
FvSimpleFileSystemWrite, | |
FvSimpleFileSystemGetPosition, | |
FvSimpleFileSystemSetPosition, | |
FvSimpleFileSystemGetInfo, | |
FvSimpleFileSystemSetInfo, | |
FvSimpleFileSystemFlush | |
}; | |
/** | |
Find and call ReadSection on the first section found of an executable type. | |
@param FvProtocol A pointer to the EFI_FIRMWARE_VOLUME2_PROTOCOL instance. | |
@param FvFileInfo A pointer to the FV_FILESYSTEM_FILE_INFO instance that is a struct | |
representing a file's info. | |
@param BufferSize Pointer to a caller-allocated UINTN. It indicates the size of | |
the memory represented by *Buffer. | |
@param Buffer Pointer to a pointer to a data buffer to contain file content. | |
@retval EFI_SUCCESS The call completed successfully. | |
@retval EFI_WARN_BUFFER_TOO_SMALL The buffer is too small to contain the requested output. | |
@retval EFI_ACCESS_DENIED The firmware volume is configured to disallow reads. | |
@retval EFI_NOT_FOUND The requested file was not found in the firmware volume. | |
@retval EFI_DEVICE_ERROR A hardware error occurred when attempting toaccess the firmware volume. | |
**/ | |
EFI_STATUS | |
FvFsFindExecutableSection ( | |
IN EFI_FIRMWARE_VOLUME2_PROTOCOL *FvProtocol, | |
IN FV_FILESYSTEM_FILE_INFO *FvFileInfo, | |
IN OUT UINTN *BufferSize, | |
IN OUT VOID **Buffer | |
) | |
{ | |
EFI_SECTION_TYPE SectionType; | |
UINT32 AuthenticationStatus; | |
EFI_STATUS Status; | |
for (SectionType = EFI_SECTION_PE32; SectionType <= EFI_SECTION_TE; SectionType++) { | |
Status = FvProtocol->ReadSection ( | |
FvProtocol, | |
&FvFileInfo->NameGuid, | |
SectionType, | |
0, | |
Buffer, | |
BufferSize, | |
&AuthenticationStatus | |
); | |
if (Status != EFI_NOT_FOUND) { | |
return Status; | |
} | |
} | |
return EFI_NOT_FOUND; | |
} | |
/** | |
Get the size of the buffer that will be returned by FvFsReadFile. | |
@param FvProtocol A pointer to the EFI_FIRMWARE_VOLUME2_PROTOCOL instance. | |
@param FvFileInfo A pointer to the FV_FILESYSTEM_FILE_INFO instance that is a struct | |
representing a file's info. | |
@retval EFI_SUCCESS The file size was gotten correctly. | |
@retval Others The file size wasn't gotten correctly. | |
**/ | |
EFI_STATUS | |
FvFsGetFileSize ( | |
IN EFI_FIRMWARE_VOLUME2_PROTOCOL *FvProtocol, | |
IN OUT FV_FILESYSTEM_FILE_INFO *FvFileInfo | |
) | |
{ | |
UINT32 AuthenticationStatus; | |
EFI_FV_FILETYPE FoundType; | |
EFI_FV_FILE_ATTRIBUTES Attributes; | |
EFI_STATUS Status; | |
UINT8 IgnoredByte; | |
VOID *IgnoredPtr; | |
// | |
// To get the size of a section, we pass 0 for BufferSize. But we can't pass | |
// NULL for Buffer, as that will cause a return of INVALID_PARAMETER, and we | |
// can't pass NULL for *Buffer, as that will cause the callee to allocate | |
// a buffer of the sections size. | |
// | |
IgnoredPtr = &IgnoredByte; | |
FvFileInfo->FileInfo.FileSize = 0; | |
if (FV_FILETYPE_IS_EXECUTABLE (FvFileInfo->Type)) { | |
// | |
// Get the size of the first executable section out of the file. | |
// | |
Status = FvFsFindExecutableSection (FvProtocol, FvFileInfo, (UINTN *)&FvFileInfo->FileInfo.FileSize, &IgnoredPtr); | |
if (Status == EFI_WARN_BUFFER_TOO_SMALL) { | |
return EFI_SUCCESS; | |
} | |
} else if (FvFileInfo->Type == EFI_FV_FILETYPE_FREEFORM) { | |
// | |
// Try to get the size of a raw section out of the file | |
// | |
Status = FvProtocol->ReadSection ( | |
FvProtocol, | |
&FvFileInfo->NameGuid, | |
EFI_SECTION_RAW, | |
0, | |
&IgnoredPtr, | |
(UINTN *)&FvFileInfo->FileInfo.FileSize, | |
&AuthenticationStatus | |
); | |
if (Status == EFI_WARN_BUFFER_TOO_SMALL) { | |
return EFI_SUCCESS; | |
} | |
if (EFI_ERROR (Status)) { | |
// | |
// Didn't find a raw section, just return the whole file's size. | |
// | |
return FvProtocol->ReadFile ( | |
FvProtocol, | |
&FvFileInfo->NameGuid, | |
NULL, | |
(UINTN *)&FvFileInfo->FileInfo.FileSize, | |
&FoundType, | |
&Attributes, | |
&AuthenticationStatus | |
); | |
} | |
} else { | |
// | |
// Get the size of the entire file | |
// | |
return FvProtocol->ReadFile ( | |
FvProtocol, | |
&FvFileInfo->NameGuid, | |
NULL, | |
(UINTN *)&FvFileInfo->FileInfo.FileSize, | |
&FoundType, | |
&Attributes, | |
&AuthenticationStatus | |
); | |
} | |
return Status; | |
} | |
/** | |
Helper function to read a file. | |
The data returned depends on the type of the underlying FV file: | |
- For executable types, the first section found that contains executable code is returned. | |
- For files of type FREEFORM, the driver attempts to return the first section of type RAW. | |
If none is found, the entire contents of the FV file are returned. | |
- On all other files the entire contents of the FV file is returned, as by | |
EFI_FIRMWARE_VOLUME2_PROTOCOL.ReadFile. | |
@param FvProtocol A pointer to the EFI_FIRMWARE_VOLUME2_PROTOCOL instance. | |
@param FvFileInfo A pointer to the FV_FILESYSTEM_FILE_INFO instance that is a struct | |
representing a file's info. | |
@param BufferSize Pointer to a caller-allocated UINTN. It indicates the size of | |
the memory represented by *Buffer. | |
@param Buffer Pointer to a pointer to a data buffer to contain file content. | |
@retval EFI_SUCCESS The call completed successfully. | |
@retval EFI_WARN_BUFFER_TOO_SMALL The buffer is too small to contain the requested output. | |
@retval EFI_ACCESS_DENIED The firmware volume is configured to disallow reads. | |
@retval EFI_NOT_FOUND The requested file was not found in the firmware volume. | |
@retval EFI_DEVICE_ERROR A hardware error occurred when attempting toaccess the firmware volume. | |
**/ | |
EFI_STATUS | |
FvFsReadFile ( | |
IN EFI_FIRMWARE_VOLUME2_PROTOCOL *FvProtocol, | |
IN FV_FILESYSTEM_FILE_INFO *FvFileInfo, | |
IN OUT UINTN *BufferSize, | |
IN OUT VOID **Buffer | |
) | |
{ | |
UINT32 AuthenticationStatus; | |
EFI_FV_FILETYPE FoundType; | |
EFI_FV_FILE_ATTRIBUTES Attributes; | |
EFI_STATUS Status; | |
if (FV_FILETYPE_IS_EXECUTABLE (FvFileInfo->Type)) { | |
// | |
// Read the first executable section out of the file. | |
// | |
Status = FvFsFindExecutableSection (FvProtocol, FvFileInfo, BufferSize, Buffer); | |
} else if (FvFileInfo->Type == EFI_FV_FILETYPE_FREEFORM) { | |
// | |
// Try to read a raw section out of the file | |
// | |
Status = FvProtocol->ReadSection ( | |
FvProtocol, | |
&FvFileInfo->NameGuid, | |
EFI_SECTION_RAW, | |
0, | |
Buffer, | |
BufferSize, | |
&AuthenticationStatus | |
); | |
if (EFI_ERROR (Status)) { | |
// | |
// Didn't find a raw section, just return the whole file. | |
// | |
Status = FvProtocol->ReadFile ( | |
FvProtocol, | |
&FvFileInfo->NameGuid, | |
Buffer, | |
BufferSize, | |
&FoundType, | |
&Attributes, | |
&AuthenticationStatus | |
); | |
} | |
} else { | |
// | |
// Read the entire file | |
// | |
Status = FvProtocol->ReadFile ( | |
FvProtocol, | |
&FvFileInfo->NameGuid, | |
Buffer, | |
BufferSize, | |
&FoundType, | |
&Attributes, | |
&AuthenticationStatus | |
); | |
} | |
return Status; | |
} | |
/** | |
Helper function for populating an EFI_FILE_INFO for a file. | |
Note the CreateTime, LastAccessTime and ModificationTime fields in EFI_FILE_INFO | |
are full zero as FV2 protocol has no corresponding info to fill. | |
@param FvFileInfo A pointer to the FV_FILESYSTEM_FILE_INFO instance that is a struct | |
representing a file's info. | |
@param BufferSize Pointer to a caller-allocated UINTN. It indicates the size of | |
the memory represented by FileInfo. | |
@param FileInfo A pointer to EFI_FILE_INFO to contain the returned file info. | |
@retval EFI_SUCCESS The call completed successfully. | |
@retval EFI_BUFFER_TOO_SMALL The buffer is too small to contain the requested output. | |
**/ | |
EFI_STATUS | |
FvFsGetFileInfo ( | |
IN FV_FILESYSTEM_FILE_INFO *FvFileInfo, | |
IN OUT UINTN *BufferSize, | |
OUT EFI_FILE_INFO *FileInfo | |
) | |
{ | |
UINTN InfoSize; | |
InfoSize = (UINTN)FvFileInfo->FileInfo.Size; | |
if (*BufferSize < InfoSize) { | |
*BufferSize = InfoSize; | |
return EFI_BUFFER_TOO_SMALL; | |
} | |
// | |
// Initialize FileInfo | |
// | |
CopyMem (FileInfo, &FvFileInfo->FileInfo, InfoSize); | |
*BufferSize = InfoSize; | |
return EFI_SUCCESS; | |
} | |
/** | |
Removes the last directory or file entry in a path by changing the last | |
L'\' to a CHAR_NULL. | |
@param Path The pointer to the path to modify. | |
@retval FALSE Nothing was found to remove. | |
@retval TRUE A directory or file was removed. | |
**/ | |
BOOLEAN | |
EFIAPI | |
RemoveLastItemFromPath ( | |
IN OUT CHAR16 *Path | |
) | |
{ | |
CHAR16 *Walker; | |
CHAR16 *LastSlash; | |
// | |
// get directory name from path... ('chop' off extra) | |
// | |
for ( Walker = Path, LastSlash = NULL | |
; Walker != NULL && *Walker != CHAR_NULL | |
; Walker++ | |
) | |
{ | |
if ((*Walker == L'\\') && (*(Walker + 1) != CHAR_NULL)) { | |
LastSlash = Walker + 1; | |
} | |
} | |
if (LastSlash != NULL) { | |
*LastSlash = CHAR_NULL; | |
return (TRUE); | |
} | |
return (FALSE); | |
} | |
/** | |
Function to clean up paths. | |
- Single periods in the path are removed. | |
- Double periods in the path are removed along with a single parent directory. | |
- Forward slashes L'/' are converted to backward slashes L'\'. | |
This will be done inline and the existing buffer may be larger than required | |
upon completion. | |
@param Path The pointer to the string containing the path. | |
@retval NULL An error occurred. | |
@return Path in all other instances. | |
**/ | |
CHAR16 * | |
EFIAPI | |
TrimFilePathToAbsolutePath ( | |
IN CHAR16 *Path | |
) | |
{ | |
CHAR16 *TempString; | |
UINTN TempSize; | |
if (Path == NULL) { | |
return NULL; | |
} | |
// | |
// Fix up the '/' vs '\' | |
// | |
for (TempString = Path; (TempString != NULL) && (*TempString != CHAR_NULL); TempString++) { | |
if (*TempString == L'/') { | |
*TempString = L'\\'; | |
} | |
} | |
// | |
// Fix up the .. | |
// | |
while ((TempString = StrStr (Path, L"\\..\\")) != NULL) { | |
*TempString = CHAR_NULL; | |
TempString += 4; | |
RemoveLastItemFromPath (Path); | |
TempSize = StrSize (TempString); | |
CopyMem (Path + StrLen (Path), TempString, TempSize); | |
} | |
if (((TempString = StrStr (Path, L"\\..")) != NULL) && (*(TempString + 3) == CHAR_NULL)) { | |
*TempString = CHAR_NULL; | |
RemoveLastItemFromPath (Path); | |
} | |
// | |
// Fix up the . | |
// | |
while ((TempString = StrStr (Path, L"\\.\\")) != NULL) { | |
*TempString = CHAR_NULL; | |
TempString += 2; | |
TempSize = StrSize (TempString); | |
CopyMem (Path + StrLen (Path), TempString, TempSize); | |
} | |
if (((TempString = StrStr (Path, L"\\.")) != NULL) && (*(TempString + 2) == CHAR_NULL)) { | |
*(TempString + 1) = CHAR_NULL; | |
} | |
while ((TempString = StrStr (Path, L"\\\\")) != NULL) { | |
*TempString = CHAR_NULL; | |
TempString += 1; | |
TempSize = StrSize (TempString); | |
CopyMem (Path + StrLen (Path), TempString, TempSize); | |
} | |
if (((TempString = StrStr (Path, L"\\\\")) != NULL) && (*(TempString + 1) == CHAR_NULL)) { | |
*(TempString) = CHAR_NULL; | |
} | |
return Path; | |
} | |
/** | |
Opens a new file relative to the source file's location. | |
@param This A pointer to the EFI_FILE_PROTOCOL instance that is the file | |
handle to the source location. This would typically be an open | |
handle to a directory. | |
@param NewHandle A pointer to the location to return the opened handle for the new | |
file. | |
@param FileName The Null-terminated string of the name of the file to be opened. | |
The file name may contain the following path modifiers: "\", ".", | |
and "..". | |
@param OpenMode The mode to open the file. The only valid combinations that the | |
file may be opened with are: Read, Read/Write, or Create/Read/Write. | |
@param Attributes Only valid for EFI_FILE_MODE_CREATE, in which case these are the | |
attribute bits for the newly created file. | |
@retval EFI_SUCCESS The file was opened. | |
@retval EFI_NOT_FOUND The specified file could not be found on the device. | |
@retval EFI_NO_MEDIA The device has no medium. | |
@retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no | |
longer supported. | |
@retval EFI_DEVICE_ERROR The device reported an error. | |
@retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. | |
@retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a file for write | |
when the media is write-protected. | |
@retval EFI_ACCESS_DENIED The service denied access to the file. | |
@retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file. | |
@retval EFI_VOLUME_FULL The volume is full. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
FvSimpleFileSystemOpen ( | |
IN EFI_FILE_PROTOCOL *This, | |
OUT EFI_FILE_PROTOCOL **NewHandle, | |
IN CHAR16 *FileName, | |
IN UINT64 OpenMode, | |
IN UINT64 Attributes | |
) | |
{ | |
FV_FILESYSTEM_INSTANCE *Instance; | |
FV_FILESYSTEM_FILE *File; | |
FV_FILESYSTEM_FILE *NewFile; | |
FV_FILESYSTEM_FILE_INFO *FvFileInfo; | |
LIST_ENTRY *FvFileInfoLink; | |
EFI_STATUS Status; | |
UINTN FileNameLength; | |
UINTN NewFileNameLength; | |
CHAR16 *FileNameWithExtension; | |
// | |
// Check for a valid mode | |
// | |
switch (OpenMode) { | |
case EFI_FILE_MODE_READ: | |
break; | |
default: | |
return EFI_WRITE_PROTECTED; | |
} | |
File = FVFS_FILE_FROM_FILE_THIS (This); | |
Instance = File->Instance; | |
FileName = TrimFilePathToAbsolutePath (FileName); | |
if (FileName == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (FileName[0] == L'\\') { | |
FileName++; | |
} | |
// | |
// Check for opening root | |
// | |
if ((StrCmp (FileName, L".") == 0) || (StrCmp (FileName, L"") == 0)) { | |
NewFile = AllocateZeroPool (sizeof (FV_FILESYSTEM_FILE)); | |
if (NewFile == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
NewFile->Signature = FVFS_FILE_SIGNATURE; | |
NewFile->Instance = Instance; | |
NewFile->FvFileInfo = File->FvFileInfo; | |
CopyMem (&NewFile->FileProtocol, &mFileSystemTemplate, sizeof (mFileSystemTemplate)); | |
InitializeListHead (&NewFile->Link); | |
InsertHeadList (&Instance->FileHead, &NewFile->Link); | |
NewFile->DirReadNext = NULL; | |
if (!IsListEmpty (&Instance->FileInfoHead)) { | |
NewFile->DirReadNext = FVFS_GET_FIRST_FILE_INFO (Instance); | |
} | |
*NewHandle = &NewFile->FileProtocol; | |
return EFI_SUCCESS; | |
} | |
// | |
// Do a linear search for a file in the FV with a matching filename | |
// | |
Status = EFI_NOT_FOUND; | |
FvFileInfo = NULL; | |
for (FvFileInfoLink = GetFirstNode (&Instance->FileInfoHead); | |
!IsNull (&Instance->FileInfoHead, FvFileInfoLink); | |
FvFileInfoLink = GetNextNode (&Instance->FileInfoHead, FvFileInfoLink)) | |
{ | |
FvFileInfo = FVFS_FILE_INFO_FROM_LINK (FvFileInfoLink); | |
if (mUnicodeCollation->StriColl (mUnicodeCollation, &FvFileInfo->FileInfo.FileName[0], FileName) == 0) { | |
Status = EFI_SUCCESS; | |
break; | |
} | |
} | |
// If the file has not been found check if the filename exists with an extension | |
// in case there was no extension present. | |
// FvFileSystem adds a 'virtual' extension '.EFI' to EFI applications and drivers | |
// present in the Firmware Volume | |
if (Status == EFI_NOT_FOUND) { | |
FileNameLength = StrLen (FileName); | |
// Does the filename already contain the '.EFI' extension? | |
if (mUnicodeCollation->StriColl (mUnicodeCollation, FileName + FileNameLength - 4, L".efi") != 0) { | |
// No, there was no extension. So add one and search again for the file | |
// NewFileNameLength = FileNameLength + 1 + 4 = (Number of non-null character) + (file extension) + (a null character) | |
NewFileNameLength = FileNameLength + 1 + 4; | |
FileNameWithExtension = AllocatePool (NewFileNameLength * 2); | |
StrCpyS (FileNameWithExtension, NewFileNameLength, FileName); | |
StrCatS (FileNameWithExtension, NewFileNameLength, L".EFI"); | |
for (FvFileInfoLink = GetFirstNode (&Instance->FileInfoHead); | |
!IsNull (&Instance->FileInfoHead, FvFileInfoLink); | |
FvFileInfoLink = GetNextNode (&Instance->FileInfoHead, FvFileInfoLink)) | |
{ | |
FvFileInfo = FVFS_FILE_INFO_FROM_LINK (FvFileInfoLink); | |
if (mUnicodeCollation->StriColl (mUnicodeCollation, &FvFileInfo->FileInfo.FileName[0], FileNameWithExtension) == 0) { | |
Status = EFI_SUCCESS; | |
break; | |
} | |
} | |
} | |
} | |
if (!EFI_ERROR (Status)) { | |
NewFile = AllocateZeroPool (sizeof (FV_FILESYSTEM_FILE)); | |
if (NewFile == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
NewFile->Signature = FVFS_FILE_SIGNATURE; | |
NewFile->Instance = Instance; | |
NewFile->FvFileInfo = FvFileInfo; | |
CopyMem (&NewFile->FileProtocol, &mFileSystemTemplate, sizeof (mFileSystemTemplate)); | |
InitializeListHead (&NewFile->Link); | |
InsertHeadList (&Instance->FileHead, &NewFile->Link); | |
*NewHandle = &NewFile->FileProtocol; | |
return EFI_SUCCESS; | |
} | |
return EFI_NOT_FOUND; | |
} | |
/** | |
Closes a specified file handle. | |
@param This A pointer to the EFI_FILE_PROTOCOL instance that is the file | |
handle to close. | |
@retval EFI_SUCCESS The file was closed. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
FvSimpleFileSystemClose ( | |
IN EFI_FILE_PROTOCOL *This | |
) | |
{ | |
FV_FILESYSTEM_INSTANCE *Instance; | |
FV_FILESYSTEM_FILE *File; | |
File = FVFS_FILE_FROM_FILE_THIS (This); | |
Instance = File->Instance; | |
if (File != Instance->Root) { | |
RemoveEntryList (&File->Link); | |
FreePool (File); | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Reads data from a file. | |
@param This A pointer to the EFI_FILE_PROTOCOL instance that is the file | |
handle to read data from. | |
@param BufferSize On input, the size of the Buffer. On output, the amount of data | |
returned in Buffer. In both cases, the size is measured in bytes. | |
@param Buffer The buffer into which the data is read. | |
@retval EFI_SUCCESS Data was read. | |
@retval EFI_NO_MEDIA The device has no medium. | |
@retval EFI_DEVICE_ERROR The device reported an error. | |
@retval EFI_DEVICE_ERROR An attempt was made to read from a deleted file. | |
@retval EFI_DEVICE_ERROR On entry, the current file position is beyond the end of the file. | |
@retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. | |
@retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory | |
entry. BufferSize has been updated with the size | |
needed to complete the request. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
FvSimpleFileSystemRead ( | |
IN EFI_FILE_PROTOCOL *This, | |
IN OUT UINTN *BufferSize, | |
OUT VOID *Buffer | |
) | |
{ | |
FV_FILESYSTEM_INSTANCE *Instance; | |
FV_FILESYSTEM_FILE *File; | |
EFI_STATUS Status; | |
LIST_ENTRY *FvFileInfoLink; | |
VOID *FileBuffer; | |
UINTN FileSize; | |
File = FVFS_FILE_FROM_FILE_THIS (This); | |
Instance = File->Instance; | |
if (File->FvFileInfo == Instance->Root->FvFileInfo) { | |
if (File->DirReadNext) { | |
// | |
// Directory read: populate Buffer with an EFI_FILE_INFO | |
// | |
Status = FvFsGetFileInfo (File->DirReadNext, BufferSize, Buffer); | |
if (!EFI_ERROR (Status)) { | |
// | |
// Successfully read a directory entry, now update the pointer to the | |
// next file, which will be read on the next call to this function | |
// | |
FvFileInfoLink = GetNextNode (&Instance->FileInfoHead, &File->DirReadNext->Link); | |
if (IsNull (&Instance->FileInfoHead, FvFileInfoLink)) { | |
// | |
// No more files left | |
// | |
File->DirReadNext = NULL; | |
} else { | |
File->DirReadNext = FVFS_FILE_INFO_FROM_LINK (FvFileInfoLink); | |
} | |
} | |
return Status; | |
} else { | |
// | |
// Directory read. All entries have been read, so return a zero-size | |
// buffer. | |
// | |
*BufferSize = 0; | |
return EFI_SUCCESS; | |
} | |
} else { | |
FileSize = (UINTN)File->FvFileInfo->FileInfo.FileSize; | |
FileBuffer = AllocateZeroPool (FileSize); | |
if (FileBuffer == NULL) { | |
return EFI_DEVICE_ERROR; | |
} | |
Status = FvFsReadFile (File->Instance->FvProtocol, File->FvFileInfo, &FileSize, &FileBuffer); | |
if (EFI_ERROR (Status)) { | |
FreePool (FileBuffer); | |
return EFI_DEVICE_ERROR; | |
} | |
if (*BufferSize + File->Position > FileSize) { | |
*BufferSize = (UINTN)(FileSize - File->Position); | |
} | |
CopyMem (Buffer, (UINT8 *)FileBuffer + File->Position, *BufferSize); | |
File->Position += *BufferSize; | |
FreePool (FileBuffer); | |
return EFI_SUCCESS; | |
} | |
} | |
/** | |
Writes data to a file. | |
@param This A pointer to the EFI_FILE_PROTOCOL instance that is the file | |
handle to write data to. | |
@param BufferSize On input, the size of the Buffer. On output, the amount of data | |
actually written. In both cases, the size is measured in bytes. | |
@param Buffer The buffer of data to write. | |
@retval EFI_SUCCESS Data was written. | |
@retval EFI_UNSUPPORTED Writes to open directory files are not supported. | |
@retval EFI_NO_MEDIA The device has no medium. | |
@retval EFI_DEVICE_ERROR The device reported an error. | |
@retval EFI_DEVICE_ERROR An attempt was made to write to a deleted file. | |
@retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. | |
@retval EFI_WRITE_PROTECTED The file or medium is write-protected. | |
@retval EFI_ACCESS_DENIED The file was opened read only. | |
@retval EFI_VOLUME_FULL The volume is full. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
FvSimpleFileSystemWrite ( | |
IN EFI_FILE_PROTOCOL *This, | |
IN OUT UINTN *BufferSize, | |
IN VOID *Buffer | |
) | |
{ | |
FV_FILESYSTEM_INSTANCE *Instance; | |
FV_FILESYSTEM_FILE *File; | |
File = FVFS_FILE_FROM_FILE_THIS (This); | |
Instance = File->Instance; | |
if (File->FvFileInfo == Instance->Root->FvFileInfo) { | |
return EFI_UNSUPPORTED; | |
} else { | |
return EFI_WRITE_PROTECTED; | |
} | |
} | |
/** | |
Returns a file's current position. | |
@param This A pointer to the EFI_FILE_PROTOCOL instance that is the file | |
handle to get the current position on. | |
@param Position The address to return the file's current position value. | |
@retval EFI_SUCCESS The position was returned. | |
@retval EFI_UNSUPPORTED The request is not valid on open directories. | |
@retval EFI_DEVICE_ERROR An attempt was made to get the position from a deleted file. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
FvSimpleFileSystemGetPosition ( | |
IN EFI_FILE_PROTOCOL *This, | |
OUT UINT64 *Position | |
) | |
{ | |
FV_FILESYSTEM_INSTANCE *Instance; | |
FV_FILESYSTEM_FILE *File; | |
File = FVFS_FILE_FROM_FILE_THIS (This); | |
Instance = File->Instance; | |
if (File->FvFileInfo == Instance->Root->FvFileInfo) { | |
return EFI_UNSUPPORTED; | |
} else { | |
*Position = File->Position; | |
return EFI_SUCCESS; | |
} | |
} | |
/** | |
Sets a file's current position. | |
@param This A pointer to the EFI_FILE_PROTOCOL instance that is the | |
file handle to set the requested position on. | |
@param Position The byte position from the start of the file to set. | |
@retval EFI_SUCCESS The position was set. | |
@retval EFI_UNSUPPORTED The seek request for nonzero is not valid on open | |
directories. | |
@retval EFI_DEVICE_ERROR An attempt was made to set the position of a deleted file. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
FvSimpleFileSystemSetPosition ( | |
IN EFI_FILE_PROTOCOL *This, | |
IN UINT64 Position | |
) | |
{ | |
FV_FILESYSTEM_INSTANCE *Instance; | |
FV_FILESYSTEM_FILE *File; | |
File = FVFS_FILE_FROM_FILE_THIS (This); | |
Instance = File->Instance; | |
if (File->FvFileInfo == Instance->Root->FvFileInfo) { | |
if (Position != 0) { | |
return EFI_UNSUPPORTED; | |
} | |
// | |
// Reset directory position to first entry | |
// | |
if (File->DirReadNext) { | |
File->DirReadNext = FVFS_GET_FIRST_FILE_INFO (Instance); | |
} | |
} else if (Position == 0xFFFFFFFFFFFFFFFFull) { | |
File->Position = File->FvFileInfo->FileInfo.FileSize; | |
} else { | |
File->Position = Position; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Flushes all modified data associated with a file to a device. | |
@param This A pointer to the EFI_FILE_PROTOCOL instance that is the file | |
handle to flush. | |
@retval EFI_SUCCESS The data was flushed. | |
@retval EFI_NO_MEDIA The device has no medium. | |
@retval EFI_DEVICE_ERROR The device reported an error. | |
@retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. | |
@retval EFI_WRITE_PROTECTED The file or medium is write-protected. | |
@retval EFI_ACCESS_DENIED The file was opened read-only. | |
@retval EFI_VOLUME_FULL The volume is full. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
FvSimpleFileSystemFlush ( | |
IN EFI_FILE_PROTOCOL *This | |
) | |
{ | |
return EFI_WRITE_PROTECTED; | |
} | |
/** | |
Close and delete the file handle. | |
@param This A pointer to the EFI_FILE_PROTOCOL instance that is the | |
handle to the file to delete. | |
@retval EFI_SUCCESS The file was closed and deleted, and the handle was closed. | |
@retval EFI_WARN_DELETE_FAILURE The handle was closed, but the file was not deleted. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
FvSimpleFileSystemDelete ( | |
IN EFI_FILE_PROTOCOL *This | |
) | |
{ | |
EFI_STATUS Status; | |
Status = FvSimpleFileSystemClose (This); | |
ASSERT_EFI_ERROR (Status); | |
return EFI_WARN_DELETE_FAILURE; | |
} | |
/** | |
Returns information about a file. | |
@param This A pointer to the EFI_FILE_PROTOCOL instance that is the file | |
handle the requested information is for. | |
@param InformationType The type identifier for the information being requested. | |
@param BufferSize On input, the size of Buffer. On output, the amount of data | |
returned in Buffer. In both cases, the size is measured in bytes. | |
@param Buffer A pointer to the data buffer to return. The buffer's type is | |
indicated by InformationType. | |
@retval EFI_SUCCESS The information was returned. | |
@retval EFI_UNSUPPORTED The InformationType is not known. | |
@retval EFI_NO_MEDIA The device has no medium. | |
@retval EFI_DEVICE_ERROR The device reported an error. | |
@retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. | |
@retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry. | |
BufferSize has been updated with the size needed to complete | |
the request. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
FvSimpleFileSystemGetInfo ( | |
IN EFI_FILE_PROTOCOL *This, | |
IN EFI_GUID *InformationType, | |
IN OUT UINTN *BufferSize, | |
OUT VOID *Buffer | |
) | |
{ | |
FV_FILESYSTEM_FILE *File; | |
EFI_FILE_SYSTEM_INFO *FsInfoOut; | |
EFI_FILE_SYSTEM_VOLUME_LABEL *FsVolumeLabel; | |
FV_FILESYSTEM_INSTANCE *Instance; | |
UINTN Size; | |
EFI_STATUS Status; | |
File = FVFS_FILE_FROM_FILE_THIS (This); | |
if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) { | |
// | |
// Return filesystem info | |
// | |
Instance = File->Instance; | |
Size = sizeof (EFI_FILE_SYSTEM_INFO) + StrSize (Instance->VolumeLabel) - sizeof (CHAR16); | |
if (*BufferSize < Size) { | |
*BufferSize = Size; | |
return EFI_BUFFER_TOO_SMALL; | |
} | |
// | |
// Cast output buffer for convenience | |
// | |
FsInfoOut = (EFI_FILE_SYSTEM_INFO *)Buffer; | |
CopyMem (FsInfoOut, &mFsInfoTemplate, sizeof (EFI_FILE_SYSTEM_INFO)); | |
Status = StrnCpyS ( | |
FsInfoOut->VolumeLabel, | |
(*BufferSize - OFFSET_OF (EFI_FILE_SYSTEM_INFO, VolumeLabel)) / sizeof (CHAR16), | |
Instance->VolumeLabel, | |
StrLen (Instance->VolumeLabel) | |
); | |
ASSERT_EFI_ERROR (Status); | |
FsInfoOut->Size = Size; | |
return Status; | |
} else if (CompareGuid (InformationType, &gEfiFileInfoGuid)) { | |
// | |
// Return file info | |
// | |
return FvFsGetFileInfo (File->FvFileInfo, BufferSize, (EFI_FILE_INFO *)Buffer); | |
} else if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) { | |
// | |
// Return Volume Label | |
// | |
Instance = File->Instance; | |
Size = sizeof (EFI_FILE_SYSTEM_VOLUME_LABEL) + StrSize (Instance->VolumeLabel) - sizeof (CHAR16); | |
if (*BufferSize < Size) { | |
*BufferSize = Size; | |
return EFI_BUFFER_TOO_SMALL; | |
} | |
FsVolumeLabel = (EFI_FILE_SYSTEM_VOLUME_LABEL *)Buffer; | |
Status = StrnCpyS ( | |
FsVolumeLabel->VolumeLabel, | |
(*BufferSize - OFFSET_OF (EFI_FILE_SYSTEM_VOLUME_LABEL, VolumeLabel)) / sizeof (CHAR16), | |
Instance->VolumeLabel, | |
StrLen (Instance->VolumeLabel) | |
); | |
ASSERT_EFI_ERROR (Status); | |
return Status; | |
} else { | |
return EFI_UNSUPPORTED; | |
} | |
} | |
/** | |
Sets information about a file. | |
@param This A pointer to the EFI_FILE_PROTOCOL instance that is the file | |
handle the information is for. | |
@param InformationType The type identifier for the information being set. | |
@param BufferSize The size, in bytes, of Buffer. | |
@param Buffer A pointer to the data buffer to write. The buffer's type is | |
indicated by InformationType. | |
@retval EFI_SUCCESS The information was set. | |
@retval EFI_UNSUPPORTED The InformationType is not known. | |
@retval EFI_NO_MEDIA The device has no medium. | |
@retval EFI_DEVICE_ERROR The device reported an error. | |
@retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. | |
@retval EFI_WRITE_PROTECTED InformationType is EFI_FILE_INFO_ID and the media is | |
read-only. | |
@retval EFI_WRITE_PROTECTED InformationType is EFI_FILE_PROTOCOL_SYSTEM_INFO_ID | |
and the media is read only. | |
@retval EFI_WRITE_PROTECTED InformationType is EFI_FILE_SYSTEM_VOLUME_LABEL_ID | |
and the media is read-only. | |
@retval EFI_ACCESS_DENIED An attempt is made to change the name of a file to a | |
file that is already present. | |
@retval EFI_ACCESS_DENIED An attempt is being made to change the EFI_FILE_DIRECTORY | |
Attribute. | |
@retval EFI_ACCESS_DENIED An attempt is being made to change the size of a directory. | |
@retval EFI_ACCESS_DENIED InformationType is EFI_FILE_INFO_ID and the file was opened | |
read-only and an attempt is being made to modify a field | |
other than Attribute. | |
@retval EFI_VOLUME_FULL The volume is full. | |
@retval EFI_BAD_BUFFER_SIZE BufferSize is smaller than the size of the type indicated | |
by InformationType. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
FvSimpleFileSystemSetInfo ( | |
IN EFI_FILE_PROTOCOL *This, | |
IN EFI_GUID *InformationType, | |
IN UINTN BufferSize, | |
IN VOID *Buffer | |
) | |
{ | |
if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid) || | |
CompareGuid (InformationType, &gEfiFileInfoGuid) || | |
CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) | |
{ | |
return EFI_WRITE_PROTECTED; | |
} | |
return EFI_UNSUPPORTED; | |
} |