mirror of https://github.com/acidanthera/audk.git
Ext4Pkg: Various improvements based on Sydr fuzzing results.
Signed-off-by: Savva Mitrofanov <savvamtr@gmail.com>
This commit is contained in:
parent
76daa1e807
commit
8c29fe63c3
|
@ -50,6 +50,11 @@ Ext4ReadInode (
|
|||
EXT4_BLOCK_NR InodeTableStart;
|
||||
EFI_STATUS Status;
|
||||
|
||||
if (!EXT4_IS_VALID_INODE_NR (Partition, InodeNum)) {
|
||||
DEBUG ((DEBUG_ERROR, "[ext4] Error reading inode: inode number %lu isn't valid\n", InodeNum));
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
BlockGroupNumber = (UINT32)DivU64x64Remainder (
|
||||
InodeNum - 1,
|
||||
Partition->SuperBlock.s_inodes_per_group,
|
||||
|
@ -164,14 +169,10 @@ Ext4CalculateBlockGroupDescChecksumGdtCsum (
|
|||
)
|
||||
{
|
||||
UINT16 Csum;
|
||||
UINT16 Dummy;
|
||||
|
||||
Dummy = 0;
|
||||
|
||||
Csum = CalculateCrc16Ansi (Partition->SuperBlock.s_uuid, 16, 0);
|
||||
Csum = CalculateCrc16Ansi (Partition->SuperBlock.s_uuid, sizeof (Partition->SuperBlock.s_uuid), 0xFFFF);
|
||||
Csum = CalculateCrc16Ansi (&BlockGroupNum, sizeof (BlockGroupNum), Csum);
|
||||
Csum = CalculateCrc16Ansi (BlockGroupDesc, OFFSET_OF (EXT4_BLOCK_GROUP_DESC, bg_checksum), Csum);
|
||||
Csum = CalculateCrc16Ansi (&Dummy, sizeof (Dummy), Csum);
|
||||
Csum =
|
||||
CalculateCrc16Ansi (
|
||||
&BlockGroupDesc->bg_block_bitmap_hi,
|
||||
|
|
|
@ -131,7 +131,7 @@ Ext4GetBlockPath (
|
|||
|
||||
/**
|
||||
@brief Get an extent from a block map
|
||||
Note: Also parses file holes and creates uninitialised extents from them.
|
||||
Note: Also parses file holes and creates uninitialized extents from them.
|
||||
|
||||
@param[in] Buffer Buffer of block pointers
|
||||
@param[in] IndEntries Number of entries in this block pointer table
|
||||
|
@ -173,7 +173,7 @@ Ext4GetExtentInBlockMap (
|
|||
}
|
||||
}
|
||||
|
||||
// We mark the extent as uninitialised, although there's a difference between uninit
|
||||
// We mark the extent as uninitialized, although there's a difference between uninit
|
||||
// extents and file holes.
|
||||
Extent->ee_len = EXT4_EXTENT_MAX_INITIALIZED + Count;
|
||||
return;
|
||||
|
@ -206,7 +206,7 @@ Ext4GetExtentInBlockMap (
|
|||
@param[in] LogicalBlock Block number which the returned extent must cover.
|
||||
@param[out] Extent Pointer to the output buffer, where the extent will be copied to.
|
||||
|
||||
@retval EFI_SUCCESS Retrieval was succesful.
|
||||
@retval EFI_SUCCESS Retrieval was successful.
|
||||
@retval EFI_NO_MAPPING Block has no mapping.
|
||||
**/
|
||||
EFI_STATUS
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/** @file
|
||||
Unicode collation routines
|
||||
|
||||
Copyright (c) 2021 Pedro Falcato All rights reserved.
|
||||
Copyright (c) 2021 - 2023 Pedro Falcato All rights reserved.
|
||||
Copyright (c) 2005 - 2017, Intel Corporation. All rights reserved.
|
||||
|
||||
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <Uefi.h>
|
||||
|
||||
#include <Library/DebugLib.h>
|
||||
#include <Library/UefiLib.h>
|
||||
#include <Library/UefiBootServicesTableLib.h>
|
||||
#include <Library/MemoryAllocationLib.h>
|
||||
|
@ -23,6 +24,21 @@ STATIC EFI_UNICODE_COLLATION_PROTOCOL *gUnicodeCollationInterface = NULL;
|
|||
* PS: Maybe all this code could be put in a library? It looks heavily shareable.
|
||||
**/
|
||||
|
||||
/**
|
||||
Check if unicode collation is initialized
|
||||
|
||||
@retval TRUE if Ext4InitialiseUnicodeCollation() was already called successfully
|
||||
@retval FALSE if Ext4InitialiseUnicodeCollation() was not yet called successfully
|
||||
**/
|
||||
STATIC
|
||||
BOOLEAN
|
||||
Ext4IsCollationInitialized (
|
||||
VOID
|
||||
)
|
||||
{
|
||||
return gUnicodeCollationInterface != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
Worker function to initialize Unicode Collation support.
|
||||
|
||||
|
@ -127,6 +143,11 @@ Ext4InitialiseUnicodeCollation (
|
|||
|
||||
Status = EFI_UNSUPPORTED;
|
||||
|
||||
// If already done, just return success.
|
||||
if (Ext4IsCollationInitialized ()) {
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
//
|
||||
// First try to use RFC 4646 Unicode Collation 2 Protocol.
|
||||
//
|
||||
|
@ -169,5 +190,6 @@ Ext4StrCmpInsensitive (
|
|||
IN CHAR16 *Str2
|
||||
)
|
||||
{
|
||||
ASSERT (gUnicodeCollationInterface != NULL);
|
||||
return gUnicodeCollationInterface->StriColl (gUnicodeCollationInterface, Str1, Str2);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/** @file
|
||||
Directory related routines
|
||||
|
||||
Copyright (c) 2021 Pedro Falcato All rights reserved.
|
||||
Copyright (c) 2021 - 2023 Pedro Falcato All rights reserved.
|
||||
|
||||
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
**/
|
||||
|
@ -16,8 +16,9 @@
|
|||
@param[in] Entry Pointer to a EXT4_DIR_ENTRY.
|
||||
@param[out] Ucs2FileName Pointer to an array of CHAR16's, of size EXT4_NAME_MAX + 1.
|
||||
|
||||
@retval EFI_SUCCESS The filename was succesfully retrieved and converted to UCS2.
|
||||
@retval !EFI_SUCCESS Failure.
|
||||
@retval EFI_SUCCESS The filename was successfully retrieved and converted to UCS2.
|
||||
@retval EFI_INVALID_PARAMETER The filename is not valid UTF-8.
|
||||
@retval !EFI_SUCCESS Failure.
|
||||
**/
|
||||
EFI_STATUS
|
||||
Ext4GetUcs2DirentName (
|
||||
|
@ -27,9 +28,16 @@ Ext4GetUcs2DirentName (
|
|||
{
|
||||
CHAR8 Utf8NameBuf[EXT4_NAME_MAX + 1];
|
||||
UINT16 *Str;
|
||||
UINT8 Index;
|
||||
EFI_STATUS Status;
|
||||
|
||||
CopyMem (Utf8NameBuf, Entry->name, Entry->name_len);
|
||||
for (Index = 0; Index < Entry->name_len; ++Index) {
|
||||
if (Entry->name[Index] == '\0') {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
Utf8NameBuf[Index] = Entry->name[Index];
|
||||
}
|
||||
|
||||
Utf8NameBuf[Entry->name_len] = '\0';
|
||||
|
||||
|
@ -112,8 +120,7 @@ Ext4RetrieveDirent (
|
|||
UINTN ToCopy;
|
||||
UINTN BlockOffset;
|
||||
|
||||
Status = EFI_NOT_FOUND;
|
||||
Buf = AllocatePool (Partition->BlockSize);
|
||||
Buf = AllocatePool (Partition->BlockSize);
|
||||
|
||||
if (Buf == NULL) {
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
|
@ -127,7 +134,8 @@ Ext4RetrieveDirent (
|
|||
DivU64x32Remainder (DirInoSize, Partition->BlockSize, &BlockRemainder);
|
||||
if (BlockRemainder != 0) {
|
||||
// Directory inodes need to have block aligned sizes
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
Status = EFI_VOLUME_CORRUPTED;
|
||||
goto Out;
|
||||
}
|
||||
|
||||
while (Off < DirInoSize) {
|
||||
|
@ -136,8 +144,7 @@ Ext4RetrieveDirent (
|
|||
Status = Ext4Read (Partition, Directory, Buf, Off, &Length);
|
||||
|
||||
if (Status != EFI_SUCCESS) {
|
||||
FreePool (Buf);
|
||||
return Status;
|
||||
goto Out;
|
||||
}
|
||||
|
||||
for (BlockOffset = 0; BlockOffset < Partition->BlockSize; ) {
|
||||
|
@ -145,19 +152,19 @@ Ext4RetrieveDirent (
|
|||
RemainingBlock = Partition->BlockSize - BlockOffset;
|
||||
// Check if the minimum directory entry fits inside [BlockOffset, EndOfBlock]
|
||||
if (RemainingBlock < EXT4_MIN_DIR_ENTRY_LEN) {
|
||||
FreePool (Buf);
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
Status = EFI_VOLUME_CORRUPTED;
|
||||
goto Out;
|
||||
}
|
||||
|
||||
if (!Ext4ValidDirent (Entry)) {
|
||||
FreePool (Buf);
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
Status = EFI_VOLUME_CORRUPTED;
|
||||
goto Out;
|
||||
}
|
||||
|
||||
if ((Entry->name_len > RemainingBlock) || (Entry->rec_len > RemainingBlock)) {
|
||||
// Corrupted filesystem
|
||||
FreePool (Buf);
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
Status = EFI_VOLUME_CORRUPTED;
|
||||
goto Out;
|
||||
}
|
||||
|
||||
// Unused entry
|
||||
|
@ -174,10 +181,16 @@ Ext4RetrieveDirent (
|
|||
* need to form valid ASCII/UTF-8 sequences.
|
||||
*/
|
||||
if (EFI_ERROR (Status)) {
|
||||
// If we error out, skip this entry
|
||||
// I'm not sure if this is correct behaviour, but I don't think there's a precedent here.
|
||||
BlockOffset += Entry->rec_len;
|
||||
continue;
|
||||
if (Status == EFI_INVALID_PARAMETER) {
|
||||
// If we error out due to a bad UTF-8 sequence (see Ext4GetUcs2DirentName), skip this entry.
|
||||
// I'm not sure if this is correct behaviour, but I don't think there's a precedent here.
|
||||
BlockOffset += Entry->rec_len;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Other sorts of errors should just error out.
|
||||
FreePool (Buf);
|
||||
return Status;
|
||||
}
|
||||
|
||||
if ((Entry->name_len == StrLen (Name)) &&
|
||||
|
@ -186,8 +199,8 @@ Ext4RetrieveDirent (
|
|||
ToCopy = MIN (Entry->rec_len, sizeof (EXT4_DIR_ENTRY));
|
||||
|
||||
CopyMem (Result, Entry, ToCopy);
|
||||
FreePool (Buf);
|
||||
return EFI_SUCCESS;
|
||||
Status = EFI_SUCCESS;
|
||||
goto Out;
|
||||
}
|
||||
|
||||
BlockOffset += Entry->rec_len;
|
||||
|
@ -196,8 +209,11 @@ Ext4RetrieveDirent (
|
|||
Off += Partition->BlockSize;
|
||||
}
|
||||
|
||||
Status = EFI_NOT_FOUND;
|
||||
|
||||
Out:
|
||||
FreePool (Buf);
|
||||
return EFI_NOT_FOUND;
|
||||
return Status;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -248,13 +264,18 @@ Ext4OpenDirent (
|
|||
// Using the parent's parent's dentry
|
||||
File->Dentry = Directory->Dentry->Parent;
|
||||
|
||||
ASSERT (File->Dentry != NULL);
|
||||
if (!File->Dentry) {
|
||||
// Someone tried .. on root, so direct them to /
|
||||
// This is an illegal EFI Open() but is possible to hit from a variety of internal code
|
||||
File->Dentry = Directory->Dentry;
|
||||
}
|
||||
|
||||
Ext4RefDentry (File->Dentry);
|
||||
} else {
|
||||
File->Dentry = Ext4CreateDentry (FileName, Directory->Dentry);
|
||||
|
||||
if (!File->Dentry) {
|
||||
if (File->Dentry == NULL) {
|
||||
Status = EFI_OUT_OF_RESOURCES;
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
|
@ -436,6 +457,7 @@ Ext4ReadDir (
|
|||
EXT4_FILE *TempFile;
|
||||
BOOLEAN ShouldSkip;
|
||||
BOOLEAN IsDotOrDotDot;
|
||||
CHAR16 DirentUcs2Name[EXT4_NAME_MAX + 1];
|
||||
|
||||
DirIno = File->Inode;
|
||||
Status = EFI_SUCCESS;
|
||||
|
@ -491,18 +513,35 @@ Ext4ReadDir (
|
|||
// or a checksum at the end of the directory block.
|
||||
// memcmp (and CompareMem) return 0 when the passed length is 0.
|
||||
|
||||
IsDotOrDotDot = Entry.name_len != 0 &&
|
||||
(CompareMem (Entry.name, ".", Entry.name_len) == 0 ||
|
||||
CompareMem (Entry.name, "..", Entry.name_len) == 0);
|
||||
// We must bound name_len as > 0 and <= 2 to avoid any out-of-bounds accesses or bad detection of
|
||||
// "." and "..".
|
||||
IsDotOrDotDot = Entry.name_len > 0 && Entry.name_len <= 2 &&
|
||||
CompareMem (Entry.name, "..", Entry.name_len) == 0;
|
||||
|
||||
// When inode = 0, it's unused.
|
||||
ShouldSkip = Entry.inode == 0 || IsDotOrDotDot;
|
||||
// When inode = 0, it's unused. When name_len == 0, it's a nameless entry
|
||||
// (which we should not expose to ReadDir).
|
||||
ShouldSkip = Entry.inode == 0 || Entry.name_len == 0 || IsDotOrDotDot;
|
||||
|
||||
if (ShouldSkip) {
|
||||
Offset += Entry.rec_len;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Test if the dirent is valid utf-8. This is already done inside Ext4OpenDirent but EFI_INVALID_PARAMETER
|
||||
// has the danger of its meaning being overloaded in many places, so we can't skip according to that.
|
||||
// So test outside of it, explicitly.
|
||||
Status = Ext4GetUcs2DirentName (&Entry, DirentUcs2Name);
|
||||
|
||||
if (EFI_ERROR (Status)) {
|
||||
if (Status == EFI_INVALID_PARAMETER) {
|
||||
// Bad UTF-8, skip.
|
||||
Offset += Entry.rec_len;
|
||||
continue;
|
||||
}
|
||||
|
||||
goto Out;
|
||||
}
|
||||
|
||||
Status = Ext4OpenDirent (Partition, EFI_FILE_MODE_READ, &TempFile, &Entry, File);
|
||||
|
||||
if (EFI_ERROR (Status)) {
|
||||
|
|
|
@ -54,17 +54,20 @@ Ext4ReadBlocks (
|
|||
UINT64 Offset;
|
||||
UINTN Length;
|
||||
|
||||
ASSERT (NumberBlocks != 0);
|
||||
ASSERT (BlockNumber != EXT4_BLOCK_FILE_HOLE);
|
||||
|
||||
Offset = MultU64x32 (BlockNumber, Partition->BlockSize);
|
||||
Length = NumberBlocks * Partition->BlockSize;
|
||||
|
||||
// Check for overflow on the block -> byte conversions.
|
||||
// Partition->BlockSize is never 0, so we don't need to check for that.
|
||||
|
||||
if (Offset > DivU64x32 ((UINT64)-1, Partition->BlockSize)) {
|
||||
if (DivU64x64Remainder (Offset, BlockNumber, NULL) != Partition->BlockSize) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (Length > (UINTN)-1/Partition->BlockSize) {
|
||||
if (Length / NumberBlocks != Partition->BlockSize) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
|
@ -92,14 +95,21 @@ Ext4AllocAndReadBlocks (
|
|||
VOID *Buf;
|
||||
UINTN Length;
|
||||
|
||||
// Check that number of blocks isn't empty, because
|
||||
// this is incorrect condition for opened partition,
|
||||
// so we just early-exit
|
||||
if ((NumberBlocks == 0) || (BlockNumber == EXT4_BLOCK_FILE_HOLE)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Length = NumberBlocks * Partition->BlockSize;
|
||||
|
||||
if (Length > (UINTN)-1/Partition->BlockSize) {
|
||||
// Check for integer overflow
|
||||
if (Length / NumberBlocks != Partition->BlockSize) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Buf = AllocatePool (Length);
|
||||
|
||||
if (Buf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/** @file
|
||||
Raw filesystem data structures
|
||||
|
||||
Copyright (c) 2021 Pedro Falcato All rights reserved.
|
||||
Copyright (c) 2021 - 2023 Pedro Falcato All rights reserved.
|
||||
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
|
||||
Layout of an EXT2/3/4 filesystem:
|
||||
|
@ -397,12 +397,29 @@ typedef struct _Ext4Inode {
|
|||
UINT32 i_projid;
|
||||
} EXT4_INODE;
|
||||
|
||||
#define EXT4_NAME_MAX 255
|
||||
|
||||
typedef struct {
|
||||
// offset 0x0: inode number (if 0, unused entry, should skip.)
|
||||
UINT32 inode;
|
||||
// offset 0x4: Directory entry's length.
|
||||
// Note: rec_len >= name_len + EXT4_MIN_DIR_ENTRY_LEN and rec_len % 4 == 0.
|
||||
UINT16 rec_len;
|
||||
// offset 0x6: Directory entry's name's length
|
||||
UINT8 name_len;
|
||||
// offset 0x7: Directory entry's file type indicator
|
||||
UINT8 file_type;
|
||||
CHAR8 name[255];
|
||||
// offset 0x8: name[name_len]: Variable length character array; not null-terminated.
|
||||
CHAR8 name[EXT4_NAME_MAX];
|
||||
// Further notes on names:
|
||||
// 1) We use EXT4_NAME_MAX here instead of flexible arrays for ease of use around the driver.
|
||||
//
|
||||
// 2) ext4 directories are defined, as the documentation puts it, as:
|
||||
// "a directory is more or less a flat file that maps an arbitrary byte string
|
||||
// (usually ASCII) to an inode number on the filesystem". So, they are not
|
||||
// necessarily encoded with ASCII, UTF-8, or any of the sort. We must treat it
|
||||
// as a bag of bytes. When interacting with EFI interfaces themselves (which expect UCS-2)
|
||||
// we skip any directory entry that is not valid UTF-8.
|
||||
} EXT4_DIR_ENTRY;
|
||||
|
||||
#define EXT4_MIN_DIR_ENTRY_LEN 8
|
||||
|
@ -467,8 +484,17 @@ typedef UINT64 EXT4_BLOCK_NR;
|
|||
typedef UINT32 EXT2_BLOCK_NR;
|
||||
typedef UINT32 EXT4_INO_NR;
|
||||
|
||||
// 2 is always the root inode number in ext4
|
||||
#define EXT4_ROOT_INODE_NR 2
|
||||
/* Special inode numbers */
|
||||
#define EXT4_ROOT_INODE_NR 2
|
||||
#define EXT4_USR_QUOTA_INODE_NR 3
|
||||
#define EXT4_GRP_QUOTA_INODE_NR 4
|
||||
#define EXT4_BOOT_LOADER_INODE_NR 5
|
||||
#define EXT4_UNDEL_DIR_INODE_NR 6
|
||||
#define EXT4_RESIZE_INODE_NR 7
|
||||
#define EXT4_JOURNAL_INODE_NR 8
|
||||
|
||||
/* First non-reserved inode for old ext4 filesystems */
|
||||
#define EXT4_GOOD_OLD_FIRST_INODE_NR 11
|
||||
|
||||
#define EXT4_BLOCK_FILE_HOLE 0
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/** @file
|
||||
Driver entry point
|
||||
|
||||
Copyright (c) 2021 Pedro Falcato All rights reserved.
|
||||
Copyright (c) 2021 - 2023 Pedro Falcato All rights reserved.
|
||||
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
**/
|
||||
|
||||
|
@ -513,26 +513,18 @@ Ext4EntryPoint (
|
|||
IN EFI_SYSTEM_TABLE *SystemTable
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
|
||||
Status = EfiLibInstallAllDriverProtocols2 (
|
||||
ImageHandle,
|
||||
SystemTable,
|
||||
&gExt4BindingProtocol,
|
||||
ImageHandle,
|
||||
&gExt4ComponentName,
|
||||
&gExt4ComponentName2,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
);
|
||||
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
|
||||
return Ext4InitialiseUnicodeCollation (ImageHandle);
|
||||
return EfiLibInstallAllDriverProtocols2 (
|
||||
ImageHandle,
|
||||
SystemTable,
|
||||
&gExt4BindingProtocol,
|
||||
ImageHandle,
|
||||
&gExt4ComponentName,
|
||||
&gExt4ComponentName2,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -761,6 +753,17 @@ Ext4Bind (
|
|||
BlockIo = NULL;
|
||||
DiskIo = NULL;
|
||||
|
||||
// Note: We initialize collation here since this is called in BDS, when we are likely
|
||||
// to have the Unicode Collation protocols available.
|
||||
Status = Ext4InitialiseUnicodeCollation (BindingProtocol->ImageHandle);
|
||||
if (EFI_ERROR (Status)) {
|
||||
// Lets throw a loud error into the log
|
||||
// It is very unlikely something like this may fire out of the blue. Chances are either
|
||||
// the platform configuration is wrong, or we are.
|
||||
DEBUG ((DEBUG_ERROR, "[ext4] Error: Unicode Collation not available - failure to Start() - error %r\n", Status));
|
||||
goto Error;
|
||||
}
|
||||
|
||||
Status = gBS->OpenProtocol (
|
||||
ControllerHandle,
|
||||
&gEfiDiskIoProtocolGuid,
|
||||
|
@ -774,7 +777,7 @@ Ext4Bind (
|
|||
goto Error;
|
||||
}
|
||||
|
||||
DEBUG ((DEBUG_INFO, "[Ext4] Controller supports DISK_IO\n"));
|
||||
DEBUG ((DEBUG_INFO, "[ext4] Controller supports DISK_IO\n"));
|
||||
|
||||
Status = gBS->OpenProtocol (
|
||||
ControllerHandle,
|
||||
|
@ -787,7 +790,7 @@ Ext4Bind (
|
|||
// It's okay to not support DISK_IO2
|
||||
|
||||
if (DiskIo2 != NULL) {
|
||||
DEBUG ((DEBUG_INFO, "[Ext4] Controller supports DISK_IO2\n"));
|
||||
DEBUG ((DEBUG_INFO, "[ext4] Controller supports DISK_IO2\n"));
|
||||
}
|
||||
|
||||
Status = gBS->OpenProtocol (
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/** @file
|
||||
Common header for the driver
|
||||
|
||||
Copyright (c) 2021 - 2022 Pedro Falcato All rights reserved.
|
||||
Copyright (c) 2021 - 2023 Pedro Falcato All rights reserved.
|
||||
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
**/
|
||||
|
||||
|
@ -31,8 +31,7 @@
|
|||
|
||||
#include "Ext4Disk.h"
|
||||
|
||||
#define SYMLOOP_MAX 8
|
||||
#define EXT4_NAME_MAX 255
|
||||
#define SYMLOOP_MAX 8
|
||||
//
|
||||
// We need to specify path length limit for security purposes, to prevent possible
|
||||
// overflows and dead-loop conditions. Originally this limit is absent in FS design,
|
||||
|
@ -41,6 +40,20 @@
|
|||
#define EXT4_EFI_PATH_MAX 4096
|
||||
#define EXT4_DRIVER_VERSION 0x0000
|
||||
|
||||
//
|
||||
// The EXT4 Specification doesn't strictly limit block size and this value could be up to 2^31,
|
||||
// but in practice it is limited by PAGE_SIZE due to performance significant impact.
|
||||
// Many EXT4 implementations have size of block limited to PAGE_SIZE. In many cases it's limited
|
||||
// to 4096, which is a commonly supported page size on most MMU-capable hardware, and up to 65536.
|
||||
// So, to take a balance between compatibility and security measures, it is decided to use the
|
||||
// value of 2MiB as the limit, which is equal to large page size on new hardware.
|
||||
// As for supporting big block sizes, EXT4 has a RO_COMPAT_FEATURE called BIGALLOC, which changes
|
||||
// EXT4 to use clustered allocation, so that each bit in the ext4 block allocation bitmap addresses
|
||||
// a power of two number of blocks. So it would be wiser to implement and use this feature
|
||||
// if there is such a need instead of big block size.
|
||||
//
|
||||
#define EXT4_LOG_BLOCK_SIZE_MAX 11
|
||||
|
||||
/**
|
||||
Opens an ext4 partition and installs the Simple File System protocol.
|
||||
|
||||
|
@ -156,7 +169,7 @@ Ext4UnrefDentry (
|
|||
|
||||
@param[out] Partition Partition structure to fill with filesystem
|
||||
details.
|
||||
@retval EFI_SUCCESS Parsing was succesful and the partition is a
|
||||
@retval EFI_SUCCESS Parsing was successful and the partition is a
|
||||
valid ext4 partition.
|
||||
**/
|
||||
EFI_STATUS
|
||||
|
@ -288,6 +301,16 @@ Ext4GetBlockGroupDesc (
|
|||
IN UINT32 BlockGroup
|
||||
);
|
||||
|
||||
/**
|
||||
Checks inode number validity across superblock of the opened partition.
|
||||
|
||||
@param[in] Partition Pointer to the opened ext4 partition.
|
||||
|
||||
@return TRUE if inode number is valid.
|
||||
**/
|
||||
#define EXT4_IS_VALID_INODE_NR(Partition, InodeNum) \
|
||||
(((InodeNum) > 0) && (InodeNum) <= (Partition->SuperBlock.s_inodes_count))
|
||||
|
||||
/**
|
||||
Reads an inode from disk.
|
||||
|
||||
|
@ -323,7 +346,7 @@ Ext4ReadInode (
|
|||
@param[out] Buffer Pointer to the buffer.
|
||||
@param[in] Offset Offset of the read.
|
||||
@param[in out] Length Pointer to the length of the buffer, in bytes.
|
||||
After a succesful read, it's updated to the
|
||||
After a successful read, it's updated to the
|
||||
number of read bytes.
|
||||
|
||||
@return Status of the read operation.
|
||||
|
@ -356,7 +379,7 @@ cover.
|
|||
@param[out] Extent Pointer to the output buffer, where the extent
|
||||
will be copied to.
|
||||
|
||||
@retval EFI_SUCCESS Retrieval was succesful.
|
||||
@retval EFI_SUCCESS Retrieval was successful.
|
||||
@retval EFI_NO_MAPPING Block has no mapping.
|
||||
**/
|
||||
EFI_STATUS
|
||||
|
@ -945,11 +968,11 @@ Ext4StrCmpInsensitive (
|
|||
Retrieves the filename of the directory entry and converts it to UTF-16/UCS-2
|
||||
|
||||
@param[in] Entry Pointer to a EXT4_DIR_ENTRY.
|
||||
@param[out] Ucs2FileName Pointer to an array of CHAR16's, of size
|
||||
EXT4_NAME_MAX + 1.
|
||||
@param[out] Ucs2FileName Pointer to an array of CHAR16's, of size EXT4_NAME_MAX + 1.
|
||||
|
||||
@retval EFI_SUCCESS Unicode collation was successfully initialised.
|
||||
@retval !EFI_SUCCESS Failure.
|
||||
@retval EFI_SUCCESS The filename was successfully retrieved and converted to UCS2.
|
||||
@retval EFI_INVALID_PARAMETER The filename is not valid UTF-8.
|
||||
@retval !EFI_SUCCESS Failure.
|
||||
**/
|
||||
EFI_STATUS
|
||||
Ext4GetUcs2DirentName (
|
||||
|
@ -1107,7 +1130,7 @@ Ext4CalculateBlockGroupDescChecksum (
|
|||
);
|
||||
|
||||
/**
|
||||
Verifies the existance of a particular RO compat feature set.
|
||||
Verifies the existence of a particular RO compat feature set.
|
||||
@param[in] Partition Pointer to the opened EXT4 partition.
|
||||
@param[in] RoCompatFeatureSet Feature set to test.
|
||||
|
||||
|
@ -1117,7 +1140,7 @@ Ext4CalculateBlockGroupDescChecksum (
|
|||
((Partition->FeaturesRoCompat & RoCompatFeatureSet) == RoCompatFeatureSet)
|
||||
|
||||
/**
|
||||
Verifies the existance of a particular compat feature set.
|
||||
Verifies the existence of a particular compat feature set.
|
||||
@param[in] Partition Pointer to the opened EXT4 partition.
|
||||
@param[in] CompatFeatureSet Feature set to test.
|
||||
|
||||
|
@ -1127,7 +1150,7 @@ Ext4CalculateBlockGroupDescChecksum (
|
|||
((Partition->FeaturesCompat & CompatFeatureSet) == CompatFeatureSet)
|
||||
|
||||
/**
|
||||
Verifies the existance of a particular compat feature set.
|
||||
Verifies the existence of a particular compat feature set.
|
||||
@param[in] Partition Pointer to the opened EXT4 partition.
|
||||
@param[in] IncompatFeatureSet Feature set to test.
|
||||
|
||||
|
@ -1218,7 +1241,7 @@ Ext4GetExtentLength (
|
|||
@param[in] LogicalBlock Block number which the returned extent must cover.
|
||||
@param[out] Extent Pointer to the output buffer, where the extent will be copied to.
|
||||
|
||||
@retval EFI_SUCCESS Retrieval was succesful.
|
||||
@retval EFI_SUCCESS Retrieval was successful.
|
||||
@retval EFI_NO_MAPPING Block has no mapping.
|
||||
**/
|
||||
EFI_STATUS
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/** @file
|
||||
Extent related routines
|
||||
|
||||
Copyright (c) 2021 - 2022 Pedro Falcato All rights reserved.
|
||||
Copyright (c) 2021 - 2023 Pedro Falcato All rights reserved.
|
||||
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
**/
|
||||
|
||||
|
@ -36,14 +36,17 @@ Ext4CalculateExtentChecksum (
|
|||
/**
|
||||
Caches a range of extents, by allocating pool memory for each extent and adding it to the tree.
|
||||
|
||||
@param[in] File Pointer to the open file.
|
||||
@param[in] Extents Pointer to an array of extents.
|
||||
@param[in] NumberExtents Length of the array.
|
||||
**/
|
||||
VOID
|
||||
Ext4CacheExtents (
|
||||
IN EXT4_FILE *File,
|
||||
IN CONST EXT4_EXTENT *Extents,
|
||||
@param[in] File Pointer to the open file.
|
||||
@param[in] Extents Pointer to an array of extents.
|
||||
@param[in] NumberExtents Length of the array.
|
||||
|
||||
@return Result of the caching
|
||||
**/
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
Ext4CacheExtents (
|
||||
IN EXT4_FILE *File,
|
||||
IN CONST EXT4_EXTENT *Extents,
|
||||
IN UINT16 NumberExtents
|
||||
);
|
||||
|
||||
|
@ -80,13 +83,15 @@ Ext4GetInoExtentHeader (
|
|||
/**
|
||||
Checks if an extent header is valid.
|
||||
@param[in] Header Pointer to the EXT4_EXTENT_HEADER structure.
|
||||
@param[in] MaxEntries Maximum number of entries possible for this tree node.
|
||||
|
||||
@return TRUE if valid, FALSE if not.
|
||||
**/
|
||||
STATIC
|
||||
BOOLEAN
|
||||
Ext4ExtentHeaderValid (
|
||||
IN CONST EXT4_EXTENT_HEADER *Header
|
||||
IN CONST EXT4_EXTENT_HEADER *Header,
|
||||
IN UINT16 MaxEntries
|
||||
)
|
||||
{
|
||||
if (Header->eh_depth > EXT4_EXTENT_TREE_MAX_DEPTH) {
|
||||
|
@ -99,6 +104,18 @@ Ext4ExtentHeaderValid (
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
// Note: We do not need to check eh_entries here, as the next branch makes sure max >= entries
|
||||
if (Header->eh_max > MaxEntries) {
|
||||
DEBUG ((
|
||||
DEBUG_ERROR,
|
||||
"[ext4] Invalid extent header max entries (%u eh_max, "
|
||||
"theoretical max is %u) (larger than permitted)\n",
|
||||
Header->eh_max,
|
||||
MaxEntries
|
||||
));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (Header->eh_max < Header->eh_entries) {
|
||||
DEBUG ((
|
||||
DEBUG_ERROR,
|
||||
|
@ -212,6 +229,9 @@ Ext4ExtentIdxLeafBlock (
|
|||
return LShiftU64 (Index->ei_leaf_hi, 32) | Index->ei_leaf_lo;
|
||||
}
|
||||
|
||||
// Results of sizeof(i_data) / sizeof(extent) - 1 = 4
|
||||
#define EXT4_NR_INLINE_EXTENTS 4
|
||||
|
||||
/**
|
||||
Retrieves an extent from an EXT4 inode.
|
||||
@param[in] Partition Pointer to the opened EXT4 partition.
|
||||
|
@ -219,7 +239,7 @@ Ext4ExtentIdxLeafBlock (
|
|||
@param[in] LogicalBlock Block number which the returned extent must cover.
|
||||
@param[out] Extent Pointer to the output buffer, where the extent will be copied to.
|
||||
|
||||
@retval EFI_SUCCESS Retrieval was succesful.
|
||||
@retval EFI_SUCCESS Retrieval was successful.
|
||||
@retval EFI_NO_MAPPING Block has no mapping.
|
||||
**/
|
||||
EFI_STATUS
|
||||
|
@ -237,6 +257,8 @@ Ext4GetExtent (
|
|||
EXT4_EXTENT_HEADER *ExtHeader;
|
||||
EXT4_EXTENT_INDEX *Index;
|
||||
EFI_STATUS Status;
|
||||
UINT16 MaxExtentsPerNode;
|
||||
EXT4_BLOCK_NR BlockNumber;
|
||||
|
||||
Inode = File->Inode;
|
||||
Ext = NULL;
|
||||
|
@ -261,25 +283,36 @@ Ext4GetExtent (
|
|||
// If this is an older ext2/ext3 filesystem, emulate Ext4GetExtent using the block map
|
||||
// By specification files using block maps are limited to 2^32 blocks,
|
||||
// so we can safely cast LogicalBlock to uint32
|
||||
Status = Ext4GetBlocks (Partition, File, (UINT32)LogicalBlock, Extent);
|
||||
|
||||
if (!EFI_ERROR (Status)) {
|
||||
Ext4CacheExtents (File, Extent, 1);
|
||||
}
|
||||
|
||||
return Status;
|
||||
Status = Ext4GetBlocks (Partition, File, (UINT32)LogicalBlock, Extent);
|
||||
|
||||
if (!EFI_ERROR (Status)) {
|
||||
Status = Ext4CacheExtents (File, Extent, 1);
|
||||
|
||||
if (EFI_ERROR (Status) && (Status != EFI_OUT_OF_RESOURCES)) {
|
||||
return Status;
|
||||
}
|
||||
|
||||
Status = EFI_SUCCESS;
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
// Slow path, we'll need to read from disk and (try to) cache those extents.
|
||||
|
||||
ExtHeader = Ext4GetInoExtentHeader (Inode);
|
||||
|
||||
if (!Ext4ExtentHeaderValid (ExtHeader)) {
|
||||
if (!Ext4ExtentHeaderValid (ExtHeader, EXT4_NR_INLINE_EXTENTS)) {
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
CurrentDepth = ExtHeader->eh_depth;
|
||||
|
||||
// A single node fits into a single block, so we can only have (BlockSize / sizeof(EXT4_EXTENT)) - 1
|
||||
// extents in a single node. Note the -1, because both leaf and internal node headers are 12 bytes,
|
||||
// and so are individual entries.
|
||||
MaxExtentsPerNode = (UINT16)((Partition->BlockSize / sizeof (EXT4_EXTENT)) - 1);
|
||||
|
||||
while (ExtHeader->eh_depth != 0) {
|
||||
CurrentDepth--;
|
||||
// While depth != 0, we're traversing the tree itself and not any leaves
|
||||
|
@ -288,7 +321,17 @@ Ext4GetExtent (
|
|||
// Therefore, we can use binary search, and it's actually the standard for doing so
|
||||
// (see FreeBSD).
|
||||
|
||||
Index = Ext4BinsearchExtentIndex (ExtHeader, LogicalBlock);
|
||||
Index = Ext4BinsearchExtentIndex (ExtHeader, LogicalBlock);
|
||||
BlockNumber = Ext4ExtentIdxLeafBlock (Index);
|
||||
|
||||
// Check that block isn't file hole
|
||||
if (BlockNumber == EXT4_BLOCK_FILE_HOLE) {
|
||||
if (Buffer != NULL) {
|
||||
FreePool (Buffer);
|
||||
}
|
||||
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
if (Buffer == NULL) {
|
||||
Buffer = AllocatePool (Partition->BlockSize);
|
||||
|
@ -299,7 +342,7 @@ Ext4GetExtent (
|
|||
|
||||
// Read the leaf block onto the previously-allocated buffer.
|
||||
|
||||
Status = Ext4ReadBlocks (Partition, Buffer, 1, Ext4ExtentIdxLeafBlock (Index));
|
||||
Status = Ext4ReadBlocks (Partition, Buffer, 1, BlockNumber);
|
||||
if (EFI_ERROR (Status)) {
|
||||
FreePool (Buffer);
|
||||
return Status;
|
||||
|
@ -307,7 +350,7 @@ Ext4GetExtent (
|
|||
|
||||
ExtHeader = Buffer;
|
||||
|
||||
if (!Ext4ExtentHeaderValid (ExtHeader)) {
|
||||
if (!Ext4ExtentHeaderValid (ExtHeader, MaxExtentsPerNode)) {
|
||||
FreePool (Buffer);
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
@ -326,13 +369,21 @@ Ext4GetExtent (
|
|||
|
||||
/* We try to cache every extent under a single leaf, since it's quite likely that we
|
||||
* may need to access things sequentially. Furthermore, ext4 block allocation as done
|
||||
* by linux (and possibly other systems) is quite fancy and usually it results in a small number of extents.
|
||||
* Therefore, we shouldn't have any memory issues.
|
||||
**/
|
||||
Ext4CacheExtents (File, (EXT4_EXTENT *)(ExtHeader + 1), ExtHeader->eh_entries);
|
||||
|
||||
Ext = Ext4BinsearchExtentExt (ExtHeader, LogicalBlock);
|
||||
|
||||
* by linux (and possibly other systems) is quite fancy and usually it results in a small number of extents.
|
||||
* Therefore, we shouldn't have any memory issues.
|
||||
**/
|
||||
Status = Ext4CacheExtents (File, (EXT4_EXTENT *)(ExtHeader + 1), ExtHeader->eh_entries);
|
||||
|
||||
if (EFI_ERROR (Status) && (Status != EFI_OUT_OF_RESOURCES)) {
|
||||
if (Buffer != NULL) {
|
||||
FreePool (Buffer);
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
Ext = Ext4BinsearchExtentExt (ExtHeader, LogicalBlock);
|
||||
|
||||
if (!Ext) {
|
||||
if (Buffer != NULL) {
|
||||
FreePool (Buffer);
|
||||
|
@ -482,14 +533,17 @@ Ext4FreeExtentsMap (
|
|||
/**
|
||||
Caches a range of extents, by allocating pool memory for each extent and adding it to the tree.
|
||||
|
||||
@param[in] File Pointer to the open file.
|
||||
@param[in] Extents Pointer to an array of extents.
|
||||
@param[in] NumberExtents Length of the array.
|
||||
**/
|
||||
VOID
|
||||
Ext4CacheExtents (
|
||||
IN EXT4_FILE *File,
|
||||
IN CONST EXT4_EXTENT *Extents,
|
||||
@param[in] File Pointer to the open file.
|
||||
@param[in] Extents Pointer to an array of extents.
|
||||
@param[in] NumberExtents Length of the array.
|
||||
|
||||
@return Result of the caching
|
||||
**/
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
Ext4CacheExtents (
|
||||
IN EXT4_FILE *File,
|
||||
IN CONST EXT4_EXTENT *Extents,
|
||||
IN UINT16 NumberExtents
|
||||
)
|
||||
{
|
||||
|
@ -499,16 +553,21 @@ Ext4CacheExtents (
|
|||
|
||||
/* Note that any out of memory condition might mean we don't get to cache a whole leaf of extents
|
||||
* in which case, future insertions might fail.
|
||||
*/
|
||||
|
||||
for (Idx = 0; Idx < NumberExtents; Idx++, Extents++) {
|
||||
Extent = AllocatePool (sizeof (EXT4_EXTENT));
|
||||
|
||||
if (Extent == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
CopyMem (Extent, Extents, sizeof (EXT4_EXTENT));
|
||||
*/
|
||||
|
||||
for (Idx = 0; Idx < NumberExtents; Idx++, Extents++) {
|
||||
if (Extents->ee_len == 0) {
|
||||
// 0-sized extent, must be corruption
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
Extent = AllocatePool (sizeof (EXT4_EXTENT));
|
||||
|
||||
if (Extent == NULL) {
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
CopyMem (Extent, Extents, sizeof (EXT4_EXTENT));
|
||||
Status = OrderedCollectionInsert (File->ExtentsMap, NULL, Extent);
|
||||
|
||||
// EFI_ALREADY_STARTED = already exists in the tree.
|
||||
|
@ -516,15 +575,17 @@ Ext4CacheExtents (
|
|||
FreePool (Extent);
|
||||
|
||||
if (Status == EFI_ALREADY_STARTED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
continue;
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
Gets an extent from the extents cache of the file.
|
||||
|
||||
@param[in] File Pointer to the open file.
|
||||
|
@ -615,7 +676,7 @@ Ext4GetExtentLength (
|
|||
IN CONST EXT4_EXTENT *Extent
|
||||
)
|
||||
{
|
||||
// If it's an unintialized extent, the true length is ee_len - 2^15
|
||||
// If it's an uninitialized extent, the true length is ee_len - 2^15
|
||||
if (EXT4_EXTENT_IS_UNINITIALIZED (Extent)) {
|
||||
return Extent->ee_len - EXT4_EXTENT_MAX_INITIALIZED;
|
||||
}
|
||||
|
|
|
@ -105,7 +105,7 @@ Ext4IsLastPathSegment (
|
|||
@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 succesful, false if we don't have
|
||||
@return True if the open was successful, false if we don't have
|
||||
enough permissions.
|
||||
**/
|
||||
STATIC
|
||||
|
@ -207,6 +207,11 @@ Ext4OpenInternal (
|
|||
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++;
|
||||
|
@ -219,6 +224,10 @@ Ext4OpenInternal (
|
|||
return EFI_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
if (!Ext4FileIsDir (Current)) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// Discard leading path separators
|
||||
while (FileName[0] == L'\\') {
|
||||
FileName++;
|
||||
|
@ -242,10 +251,6 @@ Ext4OpenInternal (
|
|||
|
||||
DEBUG ((DEBUG_FS, "[ext4] Opening %s\n", PathSegment));
|
||||
|
||||
if (!Ext4FileIsDir (Current)) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!Ext4IsLastPathSegment (FileName)) {
|
||||
if (!Ext4DirCanLookup (Current)) {
|
||||
return EFI_ACCESS_DENIED;
|
||||
|
@ -714,7 +719,11 @@ Ext4GetVolumeName (
|
|||
|
||||
VolNameLength = StrLen (VolumeName);
|
||||
} else {
|
||||
VolumeName = AllocateZeroPool (sizeof (CHAR16));
|
||||
VolumeName = AllocateZeroPool (sizeof (CHAR16));
|
||||
if (VolumeName == NULL) {
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
VolNameLength = 0;
|
||||
}
|
||||
|
||||
|
@ -781,7 +790,9 @@ Ext4GetFilesystemInfo (
|
|||
Info->VolumeSize = MultU64x32 (TotalBlocks, Part->BlockSize);
|
||||
Info->FreeSpace = MultU64x32 (FreeBlocks, Part->BlockSize);
|
||||
|
||||
StrCpyS (Info->VolumeLabel, VolNameLength + 1, VolumeName);
|
||||
Status = StrCpyS (Info->VolumeLabel, VolNameLength + 1, VolumeName);
|
||||
|
||||
ASSERT_EFI_ERROR (Status);
|
||||
|
||||
FreePool (VolumeName);
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ Ext4CalculateInodeChecksum (
|
|||
@param[out] Buffer Pointer to the buffer.
|
||||
@param[in] Offset Offset of the read.
|
||||
@param[in out] Length Pointer to the length of the buffer, in bytes.
|
||||
After a succesful read, it's updated to the number of read bytes.
|
||||
After a successful read, it's updated to the number of read bytes.
|
||||
|
||||
@return Status of the read operation.
|
||||
**/
|
||||
|
@ -152,7 +152,7 @@ Ext4Read (
|
|||
} else {
|
||||
// Uninitialized extents behave exactly the same as file holes, except they have
|
||||
// blocks already allocated to them.
|
||||
HoleLen = (Ext4GetExtentLength (&Extent) * Partition->BlockSize) - HoleOff;
|
||||
HoleLen = MultU64x32 (Ext4GetExtentLength (&Extent), Partition->BlockSize) - HoleOff;
|
||||
}
|
||||
|
||||
WasRead = HoleLen > RemainingRead ? RemainingRead : (UINTN)HoleLen;
|
||||
|
@ -166,7 +166,7 @@ Ext4Read (
|
|||
Partition->BlockSize
|
||||
);
|
||||
ExtentLengthBytes = Extent.ee_len * Partition->BlockSize;
|
||||
ExtentLogicalBytes = (UINT64)Extent.ee_block * Partition->BlockSize;
|
||||
ExtentLogicalBytes = MultU64x32 ((UINT64)Extent.ee_block, Partition->BlockSize);
|
||||
ExtentOffset = CurrentSeek - ExtentLogicalBytes;
|
||||
ExtentMayRead = (UINTN)(ExtentLengthBytes - ExtentOffset);
|
||||
|
||||
|
@ -230,7 +230,7 @@ Ext4AllocateInode (
|
|||
|
||||
Inode = AllocateZeroPool (InodeSize);
|
||||
|
||||
if (!Inode) {
|
||||
if (Inode == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/** @file
|
||||
Superblock managing routines
|
||||
|
||||
Copyright (c) 2021 - 2022 Pedro Falcato All rights reserved.
|
||||
Copyright (c) 2021 - 2023 Pedro Falcato All rights reserved.
|
||||
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
**/
|
||||
|
||||
|
@ -18,7 +18,7 @@ STATIC CONST UINT32 gSupportedIncompatFeat =
|
|||
EXT4_FEATURE_INCOMPAT_64BIT | EXT4_FEATURE_INCOMPAT_DIRDATA |
|
||||
EXT4_FEATURE_INCOMPAT_FLEX_BG | EXT4_FEATURE_INCOMPAT_FILETYPE |
|
||||
EXT4_FEATURE_INCOMPAT_EXTENTS | EXT4_FEATURE_INCOMPAT_LARGEDIR |
|
||||
EXT4_FEATURE_INCOMPAT_MMP | EXT4_FEATURE_INCOMPAT_RECOVER;
|
||||
EXT4_FEATURE_INCOMPAT_MMP | EXT4_FEATURE_INCOMPAT_RECOVER | EXT4_FEATURE_INCOMPAT_CSUM_SEED;
|
||||
|
||||
// Future features that may be nice additions in the future:
|
||||
// 1) Btree support: Required for write support and would speed up lookups in large directories.
|
||||
|
@ -151,7 +151,7 @@ Ext4VerifySuperblockChecksum (
|
|||
Opens and parses the superblock.
|
||||
|
||||
@param[out] Partition Partition structure to fill with filesystem details.
|
||||
@retval EFI_SUCCESS Parsing was succesful and the partition is a
|
||||
@retval EFI_SUCCESS Parsing was successful and the partition is a
|
||||
valid ext4 partition.
|
||||
**/
|
||||
EFI_STATUS
|
||||
|
@ -203,7 +203,7 @@ Ext4OpenSuperblock (
|
|||
|
||||
// Now, check for the feature set of the filesystem
|
||||
// It's essential to check for this to avoid filesystem corruption and to avoid
|
||||
// accidentally opening an ext2/3/4 filesystem we don't understand, which would be disasterous.
|
||||
// accidentally opening an ext2/3/4 filesystem we don't understand, which would be disastrous.
|
||||
|
||||
if (Partition->FeaturesIncompat & ~gSupportedIncompatFeat) {
|
||||
DEBUG ((
|
||||
|
@ -220,13 +220,11 @@ Ext4OpenSuperblock (
|
|||
}
|
||||
|
||||
// At the time of writing, it's the only supported checksum.
|
||||
if (Partition->FeaturesCompat & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM &&
|
||||
(Sb->s_checksum_type != EXT4_CHECKSUM_CRC32C))
|
||||
{
|
||||
if (EXT4_HAS_METADATA_CSUM (Partition) && (Sb->s_checksum_type != EXT4_CHECKSUM_CRC32C)) {
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
if ((Partition->FeaturesIncompat & EXT4_FEATURE_INCOMPAT_CSUM_SEED) != 0) {
|
||||
if (EXT4_HAS_INCOMPAT (Partition, EXT4_FEATURE_INCOMPAT_CSUM_SEED)) {
|
||||
Partition->InitialSeed = Sb->s_checksum_seed;
|
||||
} else {
|
||||
Partition->InitialSeed = Ext4CalculateChecksum (Partition, Sb->s_uuid, 16, ~0U);
|
||||
|
@ -245,6 +243,16 @@ Ext4OpenSuperblock (
|
|||
|
||||
DEBUG ((DEBUG_FS, "Read only = %u\n", Partition->ReadOnly));
|
||||
|
||||
if (Sb->s_inodes_per_group == 0) {
|
||||
DEBUG ((DEBUG_ERROR, "[ext4] Inodes per group can not be zero\n"));
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
if (Sb->s_log_block_size > EXT4_LOG_BLOCK_SIZE_MAX) {
|
||||
DEBUG ((DEBUG_ERROR, "[ext4] SuperBlock s_log_block_size %lu is too big\n", Sb->s_log_block_size));
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
Partition->BlockSize = (UINT32)LShiftU64 (1024, Sb->s_log_block_size);
|
||||
|
||||
// The size of a block group can also be calculated as 8 * Partition->BlockSize
|
||||
|
@ -312,7 +320,7 @@ Ext4OpenSuperblock (
|
|||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
// Note that the cast below is completely safe, because EXT4_FILE is a specialisation of EFI_FILE_PROTOCOL
|
||||
// Note that the cast below is completely safe, because EXT4_FILE is a specialization of EFI_FILE_PROTOCOL
|
||||
Status = Ext4OpenVolume (&Partition->Interface, (EFI_FILE_PROTOCOL **)&Partition->Root);
|
||||
|
||||
if (EFI_ERROR (Status)) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/** @file
|
||||
Symbolic links routines
|
||||
|
||||
Copyright (c) 2022 Savva Mitrofanov All rights reserved.
|
||||
Copyright (c) 2022-2023 Savva Mitrofanov All rights reserved.
|
||||
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
**/
|
||||
|
||||
|
@ -155,19 +155,20 @@ Ext4ReadSlowSymlink (
|
|||
return Status;
|
||||
}
|
||||
|
||||
//
|
||||
// Add null-terminator
|
||||
//
|
||||
SymlinkTmp[SymlinkSizeTmp] = '\0';
|
||||
|
||||
if (SymlinkSizeTmp != ReadSize) {
|
||||
DEBUG ((
|
||||
DEBUG_FS,
|
||||
"[ext4] Error! The size of the read block doesn't match the value from the inode!\n"
|
||||
));
|
||||
FreePool (SymlinkTmp);
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
//
|
||||
// Add null-terminator
|
||||
//
|
||||
SymlinkTmp[ReadSize] = '\0';
|
||||
|
||||
*AsciiSymlinkSize = SymlinkAllocateSize;
|
||||
*AsciiSymlink = SymlinkTmp;
|
||||
|
||||
|
@ -201,7 +202,7 @@ Ext4ReadSymlink (
|
|||
CHAR16 *Needle;
|
||||
|
||||
//
|
||||
// Assume that we alread read Inode via Ext4ReadInode
|
||||
// Assume that we already read Inode via Ext4ReadInode
|
||||
// Skip reading, just check encryption flag
|
||||
//
|
||||
if ((File->Inode->i_flags & EXT4_ENCRYPT_FL) != 0) {
|
||||
|
@ -242,7 +243,6 @@ Ext4ReadSymlink (
|
|||
Status
|
||||
));
|
||||
FreePool (Symlink16Tmp);
|
||||
FreePool (SymlinkTmp);
|
||||
return Status;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue