CloverBootloader/Ext4Pkg/Ext4Dxe/File.c

1013 lines
30 KiB
C

/** @file
EFI_FILE_PROTOCOL implementation for EXT4
Copyright (c) 2021 Pedro Falcato All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "Ext4Dxe.h"
#include <Library/BaseUcs2Utf8Lib.h>
/**
Reads a symlink file.
@param[in] Partition Pointer to the ext4 partition.
@param[in] File Pointer to the open symlink file.
@param[out] Symlink Pointer to the output unicode symlink string.
@retval EFI_SUCCESS Symlink was read.
@retval EFI_ACCESS_DENIED Symlink is encrypted.
@retval EFI_OUT_OF_RESOURCES Memory allocation error.
@retval EFI_INVALID_PARAMETER Symlink path has incorrect length
@retval EFI_VOLUME_CORRUPTED Symlink read block size differ from inode value
**/
EFI_STATUS
Ext4ReadSymlink (
IN EXT4_PARTITION *Partition,
IN EXT4_FILE *File,
OUT CHAR16 **Symlink
);
/**
Duplicates a file structure.
@param[in] Original Pointer to the original file.
@return Pointer to the new file structure.
**/
STATIC
EXT4_FILE *
Ext4DuplicateFile (
IN CONST EXT4_FILE *Original
);
/**
Gets the next path segment.
@param[in] Path Pointer to the rest of the path.
@param[out] PathSegment Pointer to the buffer that will hold the path segment.
Note: It's necessarily EXT4_NAME_MAX +1 long.
@param[out] Length Pointer to the UINTN that will hold the length of the path segment.
@retval !EFI_SUCCESS The path segment is too large(> EXT4_NAME_MAX).
**/
STATIC
EFI_STATUS
GetPathSegment (
IN CONST CHAR16 *Path,
OUT CHAR16 *PathSegment,
OUT UINTN *Length
)
{
CONST CHAR16 *Start;
CONST CHAR16 *End;
Start = Path;
End = Path;
// The path segment ends on a backslash or a null terminator
for ( ; *End != L'\0' && *End != L'\\'; End++) {
}
*Length = End - Start;
return StrnCpyS (PathSegment, EXT4_NAME_MAX, Start, End - Start);
}
/**
Detects if we have more path segments on the path.
@param[in] Path Pointer to the rest of the path.
@return True if we're on the last segment, false if there are more
segments.
**/
STATIC
BOOLEAN
Ext4IsLastPathSegment (
IN CONST CHAR16 *Path
)
{
while (Path[0] == L'\\') {
Path++;
}
return Path[0] == '\0';
}
#define EXT4_INO_PERM_READ_OWNER 0400
#define EXT4_INO_PERM_WRITE_OWNER 0200
#define EXT4_INO_PERM_EXEC_OWNER 0100
/**
Detects if we have permissions to open the file on the desired mode.
@param[in out] File Pointer to the file we're opening.
@param[in] OpenMode Mode in which to open the file.
@return True if the open was successful, false if we don't have
enough permissions.
**/
STATIC
BOOLEAN
Ext4ApplyPermissions (
IN OUT EXT4_FILE *File,
IN UINT64 OpenMode
)
{
UINT16 NeededPerms;
NeededPerms = 0;
if ((OpenMode & EFI_FILE_MODE_READ) != 0) {
NeededPerms |= EXT4_INO_PERM_READ_OWNER;
}
if ((OpenMode & EFI_FILE_MODE_WRITE) != 0) {
NeededPerms |= EXT4_INO_PERM_WRITE_OWNER;
}
if ((File->Inode->i_mode & NeededPerms) != NeededPerms) {
return FALSE;
}
File->OpenMode = OpenMode;
return TRUE;
}
/**
Detects if we have permissions to search on the directory.
@param[in out] File Pointer to the open directory.
@return True if we have permission to search, else false.
**/
STATIC
BOOLEAN
Ext4DirCanLookup (
IN CONST EXT4_FILE *File
)
{
// In UNIX, executable permission on directories means that we have permission to look up
// files in a directory.
return (File->Inode->i_mode & EXT4_INO_PERM_EXEC_OWNER) == EXT4_INO_PERM_EXEC_OWNER;
}
/**
Opens a new file relative to the source file's location.
@param[out] FoundFile A pointer to the location to return the opened handle for the new
file.
@param[in] Source A pointer to the EXT4_FILE instance that is the file
handle to the source location. This would typically be an open
handle to a directory.
@param[in] 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[in] 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[in] 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
Ext4OpenInternal (
OUT EXT4_FILE **FoundFile,
IN EXT4_FILE *Source,
IN CHAR16 *FileName,
IN UINT64 OpenMode,
IN UINT64 Attributes
)
{
EXT4_FILE *Current;
EXT4_PARTITION *Partition;
UINTN Level;
CHAR16 PathSegment[EXT4_NAME_MAX + 1];
UINTN Length;
EXT4_FILE *File;
CHAR16 *Symlink;
EFI_STATUS Status;
Current = Source;
Partition = Current->Partition;
Level = 0;
DEBUG ((DEBUG_FS, "[ext4] Ext4OpenInternal %s\n", FileName));
if (!Ext4FileIsDir (Current)) {
return EFI_INVALID_PARAMETER;
}
// If the path starts with a backslash, we treat the root directory as the base directory
if (FileName[0] == L'\\') {
FileName++;
Current = Partition->Root;
}
while (FileName[0] != L'\0') {
if (Partition->Root->SymLoops > SYMLOOP_MAX) {
DEBUG ((DEBUG_FS, "[ext4] Symloop limit is hit !\n"));
return EFI_ACCESS_DENIED;
}
if (!Ext4FileIsDir (Current)) {
return EFI_INVALID_PARAMETER;
}
// Discard leading path separators
while (FileName[0] == L'\\') {
FileName++;
}
if (GetPathSegment (FileName, PathSegment, &Length) != EFI_SUCCESS) {
return EFI_BUFFER_TOO_SMALL;
}
// Reached the end of the path
if (Length == 0) {
break;
}
FileName += Length;
if (StrCmp (PathSegment, L".") == 0) {
// Opens of "." are a no-op
continue;
}
DEBUG ((DEBUG_FS, "[ext4] Opening %s\n", PathSegment));
if (!Ext4IsLastPathSegment (FileName)) {
if (!Ext4DirCanLookup (Current)) {
return EFI_ACCESS_DENIED;
}
}
Status = Ext4OpenFile (Current, PathSegment, Partition, EFI_FILE_MODE_READ, &File);
if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
return Status;
} else if (Status == EFI_NOT_FOUND) {
// We explicitly ignore the EFI_FILE_MODE_CREATE flag, since we don't have write support
/// If/ we add write support, this should be changed.
return Status;
}
// Check if this is a valid file to open in EFI
if (!Ext4FileIsOpenable (File)) {
Ext4CloseInternal (File);
// This looks like an /okay/ status to return.
return EFI_ACCESS_DENIED;
}
//
// Reading symlink and then trying to follow it
//
if (Ext4FileIsSymlink (File)) {
Partition->Root->SymLoops++;
DEBUG ((DEBUG_FS, "[ext4] File %s is symlink, trying to read it\n", PathSegment));
Status = Ext4ReadSymlink (Partition, File, &Symlink);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_FS, "[ext4] Error reading %s symlink!\n", PathSegment));
return Status;
}
DEBUG ((DEBUG_FS, "[ext4] File %s is linked to %s\n", PathSegment, Symlink));
//
// Close symlink file
//
Ext4CloseInternal (File);
//
// Open linked file by recursive call of Ext4OpenFile
//
Status = Ext4OpenInternal (FoundFile, Current, Symlink, OpenMode, Attributes);
FreePool (Symlink);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_FS, "[ext4] Error opening linked file %s\n", Symlink));
return Status;
}
//
// Set File to newly opened
//
File = *FoundFile;
}
if (Level != 0) {
// Careful not to close the base directory
Ext4CloseInternal (Current);
}
Level++;
Current = File;
}
if (Level == 0) {
// We opened the base directory again, so we need to duplicate the file structure
Current = Ext4DuplicateFile (Current);
if (Current == NULL) {
return EFI_OUT_OF_RESOURCES;
}
}
if (!Ext4ApplyPermissions (Current, OpenMode)) {
Ext4CloseInternal (Current);
return EFI_ACCESS_DENIED;
}
*FoundFile = Current;
DEBUG ((DEBUG_FS, "[ext4] Opened filename %s\n", Current->Dentry->Name));
return EFI_SUCCESS;
}
/**
Opens a new file relative to the source file's location.
@param[in] 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[out] NewHandle A pointer to the location to return the opened handle for the new
file.
@param[in] 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[in] 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[in] 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
Ext4Open (
IN EFI_FILE_PROTOCOL *This,
OUT EFI_FILE_PROTOCOL **NewHandle,
IN CHAR16 *FileName,
IN UINT64 OpenMode,
IN UINT64 Attributes
)
{
EFI_STATUS Status;
EXT4_FILE *FoundFile;
EXT4_FILE *Source;
Source = EXT4_FILE_FROM_THIS (This);
//
// Reset SymLoops counter
//
Source->Partition->Root->SymLoops = 0;
Status = Ext4OpenInternal (
&FoundFile,
Source,
FileName,
OpenMode,
Attributes
);
if (!EFI_ERROR (Status)) {
*NewHandle = &FoundFile->Protocol;
}
return Status;
}
/**
Closes a specified file handle.
@param[in] 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
Ext4Close (
IN EFI_FILE_PROTOCOL *This
)
{
return Ext4CloseInternal (EXT4_FILE_FROM_THIS (This));
}
/**
Closes a file.
@param[in] File Pointer to the file.
@return Status of the closing of the file.
**/
EFI_STATUS
Ext4CloseInternal (
IN EXT4_FILE *File
)
{
if ((File == File->Partition->Root) && !File->Partition->Unmounting) {
return EFI_SUCCESS;
}
DEBUG ((DEBUG_FS, "[ext4] Closed file %p (inode %lu)\n", File, File->InodeNum));
RemoveEntryList (&File->OpenFilesListNode);
FreePool (File->Inode);
Ext4FreeExtentsMap (File);
Ext4UnrefDentry (File->Dentry);
FreePool (File);
return EFI_SUCCESS;
}
/**
Close and delete the file handle.
@param[in] 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
Ext4Delete (
IN EFI_FILE_PROTOCOL *This
)
{
// We do a regular close here since we don't have write support.
Ext4Close (This);
return EFI_WARN_DELETE_FAILURE;
}
/**
Reads data from a file.
@param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file
handle to read data from.
@param[in out] 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[out] 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
Ext4ReadFile (
IN EFI_FILE_PROTOCOL *This,
IN OUT UINTN *BufferSize,
OUT VOID *Buffer
)
{
EXT4_FILE *File;
EXT4_PARTITION *Partition;
EFI_STATUS Status;
File = EXT4_FILE_FROM_THIS (This);
Partition = File->Partition;
ASSERT (Ext4FileIsOpenable (File));
if (Ext4FileIsReg (File)) {
Status = Ext4Read (Partition, File, Buffer, File->Position, BufferSize);
if (Status == EFI_SUCCESS) {
File->Position += *BufferSize;
}
return Status;
} else if (Ext4FileIsDir (File)) {
Status = Ext4ReadDir (Partition, File, Buffer, File->Position, BufferSize);
return Status;
}
return EFI_SUCCESS;
}
/**
Writes data to a file.
@param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file
handle to write data to.
@param[in out] 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[in] 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
Ext4WriteFile (
IN EFI_FILE_PROTOCOL *This,
IN OUT UINTN *BufferSize,
IN VOID *Buffer
)
{
EXT4_FILE *File;
File = EXT4_FILE_FROM_THIS (This);
if (!(File->OpenMode & EFI_FILE_MODE_WRITE)) {
return EFI_ACCESS_DENIED;
}
return EFI_WRITE_PROTECTED;
}
/**
Returns a file's current position.
@param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file
handle to get the current position on.
@param[out] 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
Ext4GetPosition (
IN EFI_FILE_PROTOCOL *This,
OUT UINT64 *Position
)
{
EXT4_FILE *File;
File = EXT4_FILE_FROM_THIS (This);
if (Ext4FileIsDir (File)) {
return EFI_UNSUPPORTED;
}
*Position = File->Position;
return EFI_SUCCESS;
}
/**
Sets a file's current position.
@param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the
file handle to set the requested position on.
@param[in] 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
Ext4SetPosition (
IN EFI_FILE_PROTOCOL *This,
IN UINT64 Position
)
{
EXT4_FILE *File;
File = EXT4_FILE_FROM_THIS (This);
// Only seeks to 0 (so it resets the ReadDir operation) are allowed
if (Ext4FileIsDir (File) && (Position != 0)) {
return EFI_UNSUPPORTED;
}
// -1 (0xffffff.......) seeks to the end of the file
if (Position == (UINT64)-1) {
Position = EXT4_INODE_SIZE (File->Inode);
}
File->Position = Position;
return EFI_SUCCESS;
}
/**
Retrieves information about the file and stores it in the EFI_FILE_INFO format.
@param[in] File Pointer to an opened file.
@param[out] Info Pointer to a EFI_FILE_INFO.
@param[in out] BufferSize Pointer to the buffer size
@return Status of the file information request.
**/
EFI_STATUS
Ext4GetFileInfo (
IN EXT4_FILE *File,
OUT EFI_FILE_INFO *Info,
IN OUT UINTN *BufferSize
)
{
UINTN FileNameLen;
UINTN FileNameSize;
UINTN NeededLength;
CONST CHAR16 *FileName;
if (File->InodeNum == EXT4_ROOT_INODE_NR) {
// Root inode gets a filename of "", regardless of how it was opened.
FileName = L"";
} else {
FileName = File->Dentry->Name;
}
FileNameLen = StrLen (FileName);
FileNameSize = StrSize (FileName);
NeededLength = SIZE_OF_EFI_FILE_INFO + FileNameSize;
if (*BufferSize < NeededLength) {
*BufferSize = NeededLength;
return EFI_BUFFER_TOO_SMALL;
}
Info->FileSize = EXT4_INODE_SIZE (File->Inode);
Info->PhysicalSize = Ext4FilePhysicalSpace (File);
Ext4FileATime (File, &Info->LastAccessTime);
Ext4FileMTime (File, &Info->ModificationTime);
Ext4FileCreateTime (File, &Info->LastAccessTime);
Info->Attribute = 0;
Info->Size = NeededLength;
if (Ext4FileIsDir (File)) {
Info->Attribute |= EFI_FILE_DIRECTORY;
}
*BufferSize = NeededLength;
return StrCpyS (Info->FileName, FileNameLen + 1, FileName);
}
/**
Retrieves the volume name.
@param[in] Part Pointer to the opened partition.
@param[out] Info Pointer to a CHAR16*.
@param[out] BufferSize Pointer to a UINTN, where the string length
of the name will be put.
@return Status of the volume name request.
**/
EFI_STATUS
Ext4GetVolumeName (
IN EXT4_PARTITION *Partition,
OUT CHAR16 **OutVolName,
OUT UINTN *VolNameLen
)
{
CHAR8 TempVolName[16 + 1];
CHAR16 *VolumeName;
UINTN VolNameLength;
EFI_STATUS Status;
VolNameLength = 0;
VolumeName = NULL;
// s_volume_name is only valid on dynamic revision; old filesystems don't support this
if (Partition->SuperBlock.s_rev_level == EXT4_DYNAMIC_REV) {
CopyMem (TempVolName, Partition->SuperBlock.s_volume_name, 16);
TempVolName[16] = '\0';
Status = UTF8StrToUCS2 (TempVolName, &VolumeName);
if (EFI_ERROR (Status)) {
return Status;
}
VolNameLength = StrLen (VolumeName);
} else {
VolumeName = AllocateZeroPool (sizeof (CHAR16));
if (VolumeName == NULL) {
return EFI_OUT_OF_RESOURCES;
}
VolNameLength = 0;
}
*OutVolName = VolumeName;
*VolNameLen = VolNameLength;
return EFI_SUCCESS;
}
/**
Retrieves information about the filesystem and stores it in the EFI_FILE_SYSTEM_INFO format.
@param[in] Part Pointer to the opened partition.
@param[out] Info Pointer to a EFI_FILE_SYSTEM_INFO.
@param[in out] BufferSize Pointer to the buffer size
@return Status of the file information request.
**/
STATIC
EFI_STATUS
Ext4GetFilesystemInfo (
IN EXT4_PARTITION *Part,
OUT EFI_FILE_SYSTEM_INFO *Info,
IN OUT UINTN *BufferSize
)
{
// Length of s_volume_name + null terminator
EFI_STATUS Status;
UINTN NeededLength;
EXT4_BLOCK_NR TotalBlocks;
EXT4_BLOCK_NR FreeBlocks;
CHAR16 *VolumeName;
UINTN VolNameLength;
Status = Ext4GetVolumeName (Part, &VolumeName, &VolNameLength);
if (EFI_ERROR (Status)) {
return Status;
}
NeededLength = SIZE_OF_EFI_FILE_SYSTEM_INFO;
NeededLength += StrSize (VolumeName);
if (*BufferSize < NeededLength) {
*BufferSize = NeededLength;
FreePool (VolumeName);
return EFI_BUFFER_TOO_SMALL;
}
TotalBlocks = Part->NumberBlocks;
FreeBlocks = EXT4_BLOCK_NR_FROM_HALFS (
Part,
Part->SuperBlock.s_free_blocks_count,
Part->SuperBlock.s_free_blocks_count_hi
);
Info->BlockSize = Part->BlockSize;
Info->Size = NeededLength;
Info->ReadOnly = Part->ReadOnly;
Info->VolumeSize = MultU64x32 (TotalBlocks, Part->BlockSize);
Info->FreeSpace = MultU64x32 (FreeBlocks, Part->BlockSize);
Status = StrCpyS (Info->VolumeLabel, VolNameLength + 1, VolumeName);
ASSERT_EFI_ERROR (Status);
FreePool (VolumeName);
*BufferSize = NeededLength;
return EFI_SUCCESS;
}
/**
Retrieves the volume label and stores it in the EFI_FILE_SYSTEM_VOLUME_LABEL format.
@param[in] Part Pointer to the opened partition.
@param[out] Info Pointer to a EFI_FILE_SYSTEM_VOLUME_LABEL.
@param[in out] BufferSize Pointer to the buffer size
@return Status of the file information request.
**/
STATIC
EFI_STATUS
Ext4GetVolumeLabelInfo (
IN EXT4_PARTITION *Part,
OUT EFI_FILE_SYSTEM_VOLUME_LABEL *Info,
IN OUT UINTN *BufferSize
)
{
// Length of s_volume_name + null terminator
CHAR16 *VolumeName;
UINTN VolNameLength;
EFI_STATUS Status;
UINTN NeededLength;
Status = Ext4GetVolumeName (Part, &VolumeName, &VolNameLength);
if (EFI_ERROR (Status)) {
return Status;
}
NeededLength = (VolNameLength + 1) * sizeof (CHAR16);
if (NeededLength > *BufferSize) {
*BufferSize = NeededLength;
FreePool (VolumeName);
return EFI_BUFFER_TOO_SMALL;
}
Status = StrCpyS (Info->VolumeLabel, VolNameLength + 1, VolumeName);
ASSERT_EFI_ERROR (Status);
FreePool (VolumeName);
*BufferSize = NeededLength;
return EFI_SUCCESS;
}
/**
Returns information about a file.
@param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file
handle the requested information is for.
@param[in] InformationType The type identifier for the information being requested.
@param[in out] 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[out] 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
Ext4GetInfo (
IN EFI_FILE_PROTOCOL *This,
IN EFI_GUID *InformationType,
IN OUT UINTN *BufferSize,
OUT VOID *Buffer
)
{
EXT4_FILE *File;
EXT4_PARTITION *Partition;
File = EXT4_FILE_FROM_THIS (This);
Partition = File->Partition;
if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {
return Ext4GetFileInfo (File, Buffer, BufferSize);
}
if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) {
return Ext4GetFilesystemInfo (Partition, Buffer, BufferSize);
}
if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) {
return Ext4GetVolumeLabelInfo (Partition, Buffer, BufferSize);
}
return EFI_UNSUPPORTED;
}
/**
Duplicates a file structure.
@param[in] Original Pointer to the original file.
@return Pointer to the new file structure.
**/
STATIC
EXT4_FILE *
Ext4DuplicateFile (
IN CONST EXT4_FILE *Original
)
{
EXT4_PARTITION *Partition;
EXT4_FILE *File;
EFI_STATUS Status;
Partition = Original->Partition;
File = AllocateZeroPool (sizeof (EXT4_FILE));
if (File == NULL) {
return NULL;
}
File->Inode = Ext4AllocateInode (Partition);
if (File->Inode == NULL) {
FreePool (File);
return NULL;
}
CopyMem (File->Inode, Original->Inode, Partition->InodeSize);
File->Position = 0;
Ext4SetupFile (File, Partition);
File->InodeNum = Original->InodeNum;
File->OpenMode = 0; // Will be filled by other code
Status = Ext4InitExtentsMap (File);
if (EFI_ERROR (Status)) {
FreePool (File->Inode);
FreePool (File);
return NULL;
}
File->Dentry = Original->Dentry;
Ext4RefDentry (File->Dentry);
InsertTailList (&Partition->OpenFiles, &File->OpenFilesListNode);
return File;
}
/**
Sets information about a file.
@param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file
handle the information is for.
@param[in] InformationType The type identifier for the information being set.
@param[in] BufferSize The size, in bytes, of Buffer.
@param[in] 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
Ext4SetInfo (
IN EFI_FILE_PROTOCOL *This,
IN EFI_GUID *InformationType,
IN UINTN BufferSize,
IN VOID *Buffer
)
{
EXT4_FILE *File;
EXT4_PARTITION *Partition;
File = EXT4_FILE_FROM_THIS (This);
Partition = File->Partition;
if (Partition->ReadOnly) {
return EFI_WRITE_PROTECTED;
}
// There's no write support just yet.
return EFI_UNSUPPORTED;
}