* [Patch v4 0/3] Ext4Pkg: Add Ext4Pkg
@ 2021-08-16 18:06 Pedro Falcato
2021-08-16 18:06 ` [Patch v4 1/3] Ext4Pkg: Add Ext4Pkg.dec and Ext4Pkg.uni Pedro Falcato
` (3 more replies)
0 siblings, 4 replies; 6+ messages in thread
From: Pedro Falcato @ 2021-08-16 18:06 UTC (permalink / raw)
To: devel; +Cc: Pedro Falcato, Leif Lindholm, Michael D Kinney, Bret Barkelew
This patch-set adds Ext4Pkg, a package designed to hold various drivers and
utilities related to the EXT4 filesystem.
Right now, it holds a single read-only UEFI EXT4 driver (Ext4Dxe), which consumes the
DISK_IO, BLOCK_IO and DISK_IO2 protocols and produce EFI_FILE_PROTOCOL and
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL; this driver allows the mounting of EXT4 partitions and
the reading of their contents.
A relevant RFC discussion, which includes a more in-depth walkthrough of EXT4 internals and
driver limitations is available at https://edk2.groups.io/g/devel/topic/84368561.
This patch set is version 4 and attempts to address issues raised by the
community in v3's code review.
Cc: Leif Lindholm <leif@nuviainc.com>
Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Bret Barkelew <Bret.Barkelew@microsoft.com>
Pedro Falcato (3):
Ext4Pkg: Add Ext4Pkg.dec and Ext4Pkg.uni.
Ext4Pkg: Add Ext4Dxe driver.
Ext4Pkg: Add .DSC file.
Features/Ext4Pkg/Ext4Dxe/BlockGroup.c | 228 ++++++
Features/Ext4Pkg/Ext4Dxe/Collation.c | 173 +++++
Features/Ext4Pkg/Ext4Dxe/Crc16.c | 75 ++
Features/Ext4Pkg/Ext4Dxe/Crc32c.c | 84 ++
Features/Ext4Pkg/Ext4Dxe/Directory.c | 498 ++++++++++++
Features/Ext4Pkg/Ext4Dxe/DiskUtil.c | 113 +++
Features/Ext4Pkg/Ext4Dxe/Ext4Disk.h | 454 +++++++++++
Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.c | 808 +++++++++++++++++++
Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.h | 1038 +++++++++++++++++++++++++
Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.inf | 149 ++++
Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.uni | 15 +
Features/Ext4Pkg/Ext4Dxe/Extents.c | 595 ++++++++++++++
Features/Ext4Pkg/Ext4Dxe/File.c | 787 +++++++++++++++++++
Features/Ext4Pkg/Ext4Dxe/Inode.c | 465 +++++++++++
Features/Ext4Pkg/Ext4Dxe/Partition.c | 125 +++
Features/Ext4Pkg/Ext4Dxe/Superblock.c | 297 +++++++
Features/Ext4Pkg/Ext4Pkg.dec | 17 +
Features/Ext4Pkg/Ext4Pkg.dsc | 70 ++
Features/Ext4Pkg/Ext4Pkg.uni | 14 +
19 files changed, 6005 insertions(+)
create mode 100644 Features/Ext4Pkg/Ext4Dxe/BlockGroup.c
create mode 100644 Features/Ext4Pkg/Ext4Dxe/Collation.c
create mode 100644 Features/Ext4Pkg/Ext4Dxe/Crc16.c
create mode 100644 Features/Ext4Pkg/Ext4Dxe/Crc32c.c
create mode 100644 Features/Ext4Pkg/Ext4Dxe/Directory.c
create mode 100644 Features/Ext4Pkg/Ext4Dxe/DiskUtil.c
create mode 100644 Features/Ext4Pkg/Ext4Dxe/Ext4Disk.h
create mode 100644 Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.c
create mode 100644 Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.h
create mode 100644 Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.inf
create mode 100644 Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.uni
create mode 100644 Features/Ext4Pkg/Ext4Dxe/Extents.c
create mode 100644 Features/Ext4Pkg/Ext4Dxe/File.c
create mode 100644 Features/Ext4Pkg/Ext4Dxe/Inode.c
create mode 100644 Features/Ext4Pkg/Ext4Dxe/Partition.c
create mode 100644 Features/Ext4Pkg/Ext4Dxe/Superblock.c
create mode 100644 Features/Ext4Pkg/Ext4Pkg.dec
create mode 100644 Features/Ext4Pkg/Ext4Pkg.dsc
create mode 100644 Features/Ext4Pkg/Ext4Pkg.uni
--
2.32.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [Patch v4 1/3] Ext4Pkg: Add Ext4Pkg.dec and Ext4Pkg.uni.
2021-08-16 18:06 [Patch v4 0/3] Ext4Pkg: Add Ext4Pkg Pedro Falcato
@ 2021-08-16 18:06 ` Pedro Falcato
2021-08-16 18:06 ` [Patch v4 2/3] Ext4Pkg: Add Ext4Dxe driver Pedro Falcato
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: Pedro Falcato @ 2021-08-16 18:06 UTC (permalink / raw)
To: devel; +Cc: Pedro Falcato, Leif Lindholm, Michael D Kinney, Bret Barkelew
These files are needed to build Ext4Pkg.
Cc: Leif Lindholm <leif@nuviainc.com>
Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Bret Barkelew <Bret.Barkelew@microsoft.com>
Signed-off-by: Pedro Falcato <pedro.falcato@gmail.com>
---
Features/Ext4Pkg/Ext4Pkg.dec | 17 +++++++++++++++++
Features/Ext4Pkg/Ext4Pkg.uni | 14 ++++++++++++++
2 files changed, 31 insertions(+)
create mode 100644 Features/Ext4Pkg/Ext4Pkg.dec
create mode 100644 Features/Ext4Pkg/Ext4Pkg.uni
diff --git a/Features/Ext4Pkg/Ext4Pkg.dec b/Features/Ext4Pkg/Ext4Pkg.dec
new file mode 100644
index 0000000000..f1f8b39c3c
--- /dev/null
+++ b/Features/Ext4Pkg/Ext4Pkg.dec
@@ -0,0 +1,17 @@
+## @file
+# Ext4 Package
+#
+# This package provides libraries and drivers related to the ext4 filesystem implementation.
+# More details are available at: https://www.kernel.org/doc/html/v5.4/filesystems/ext4/index.html
+#
+# Copyright (c) 2021 Pedro Falcato
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ DEC_SPECIFICATION = 0x00010005
+ PACKAGE_NAME = Ext4Pkg
+ PACKAGE_UNI_FILE = Ext4Pkg.uni
+ PACKAGE_GUID = 6B4BF998-668B-46D3-BCFA-971F99F8708C
+ PACKAGE_VERSION = 0.1
diff --git a/Features/Ext4Pkg/Ext4Pkg.uni b/Features/Ext4Pkg/Ext4Pkg.uni
new file mode 100644
index 0000000000..abeadd8fd9
--- /dev/null
+++ b/Features/Ext4Pkg/Ext4Pkg.uni
@@ -0,0 +1,14 @@
+## @file
+# Ext4 Package
+#
+# This package provides libraries and drivers related to the ext4 filesystem implementation.
+# More details are available at: https://www.kernel.org/doc/html/v5.4/filesystems/ext4/index.html
+#
+# Copyright (c) 2021 Pedro Falcato
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+#string STR_PACKAGE_ABSTRACT #language en-US "Module implementations for the EXT4 file system"
+
+#string STR_PACKAGE_DESCRIPTION #language en-US "This package contains UEFI drivers and libraries for the EXT4 file system."
--
2.32.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [Patch v4 2/3] Ext4Pkg: Add Ext4Dxe driver.
2021-08-16 18:06 [Patch v4 0/3] Ext4Pkg: Add Ext4Pkg Pedro Falcato
2021-08-16 18:06 ` [Patch v4 1/3] Ext4Pkg: Add Ext4Pkg.dec and Ext4Pkg.uni Pedro Falcato
@ 2021-08-16 18:06 ` Pedro Falcato
2021-08-16 18:06 ` [Patch v4 3/3] Ext4Pkg: Add .DSC file Pedro Falcato
2021-08-18 5:16 ` [edk2-devel] [Patch v4 0/3] Ext4Pkg: Add Ext4Pkg Michael D Kinney
3 siblings, 0 replies; 6+ messages in thread
From: Pedro Falcato @ 2021-08-16 18:06 UTC (permalink / raw)
To: devel; +Cc: Pedro Falcato, Leif Lindholm, Michael D Kinney, Bret Barkelew
Adds a UEFI EXT4 filesystem driver that implements the EFI_FILE_PROTOCOL
and EFI_SIMPLE_FILE_SYSTEM_PROTOCOL.
Cc: Leif Lindholm <leif@nuviainc.com>
Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Bret Barkelew <Bret.Barkelew@microsoft.com>
Signed-off-by: Pedro Falcato <pedro.falcato@gmail.com>
---
Features/Ext4Pkg/Ext4Dxe/BlockGroup.c | 228 ++++++
Features/Ext4Pkg/Ext4Dxe/Collation.c | 173 +++++
Features/Ext4Pkg/Ext4Dxe/Crc16.c | 75 ++
Features/Ext4Pkg/Ext4Dxe/Crc32c.c | 84 ++
Features/Ext4Pkg/Ext4Dxe/Directory.c | 498 ++++++++++++
Features/Ext4Pkg/Ext4Dxe/DiskUtil.c | 113 +++
Features/Ext4Pkg/Ext4Dxe/Ext4Disk.h | 454 +++++++++++
Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.c | 808 +++++++++++++++++++
Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.h | 1038 +++++++++++++++++++++++++
Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.inf | 149 ++++
Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.uni | 15 +
Features/Ext4Pkg/Ext4Dxe/Extents.c | 595 ++++++++++++++
Features/Ext4Pkg/Ext4Dxe/File.c | 787 +++++++++++++++++++
Features/Ext4Pkg/Ext4Dxe/Inode.c | 465 +++++++++++
Features/Ext4Pkg/Ext4Dxe/Partition.c | 125 +++
Features/Ext4Pkg/Ext4Dxe/Superblock.c | 297 +++++++
16 files changed, 5904 insertions(+)
create mode 100644 Features/Ext4Pkg/Ext4Dxe/BlockGroup.c
create mode 100644 Features/Ext4Pkg/Ext4Dxe/Collation.c
create mode 100644 Features/Ext4Pkg/Ext4Dxe/Crc16.c
create mode 100644 Features/Ext4Pkg/Ext4Dxe/Crc32c.c
create mode 100644 Features/Ext4Pkg/Ext4Dxe/Directory.c
create mode 100644 Features/Ext4Pkg/Ext4Dxe/DiskUtil.c
create mode 100644 Features/Ext4Pkg/Ext4Dxe/Ext4Disk.h
create mode 100644 Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.c
create mode 100644 Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.h
create mode 100644 Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.inf
create mode 100644 Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.uni
create mode 100644 Features/Ext4Pkg/Ext4Dxe/Extents.c
create mode 100644 Features/Ext4Pkg/Ext4Dxe/File.c
create mode 100644 Features/Ext4Pkg/Ext4Dxe/Inode.c
create mode 100644 Features/Ext4Pkg/Ext4Dxe/Partition.c
create mode 100644 Features/Ext4Pkg/Ext4Dxe/Superblock.c
diff --git a/Features/Ext4Pkg/Ext4Dxe/BlockGroup.c b/Features/Ext4Pkg/Ext4Dxe/BlockGroup.c
new file mode 100644
index 0000000000..abfeff52b1
--- /dev/null
+++ b/Features/Ext4Pkg/Ext4Dxe/BlockGroup.c
@@ -0,0 +1,228 @@
+/** @file
+ Block group related routines
+
+ Copyright (c) 2021 Pedro Falcato All rights reserved.
+ Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "Ext4Dxe.h"
+
+/**
+ Retrieves a block group descriptor of the ext4 filesystem.
+
+ @param[in] Partition Pointer to the opened ext4 partition.
+ @param[in] BlockGroup Block group number.
+
+ @return A pointer to the block group descriptor.
+**/
+EXT4_BLOCK_GROUP_DESC *
+Ext4GetBlockGroupDesc (
+ IN EXT4_PARTITION *Partition,
+ IN UINT32 BlockGroup
+ )
+{
+ // Maybe assert that the block group nr isn't a nonsense number?
+ return (EXT4_BLOCK_GROUP_DESC *)((CHAR8 *)Partition->BlockGroups + BlockGroup * Partition->DescSize);
+}
+
+/**
+ Reads an inode from disk.
+
+ @param[in] Partition Pointer to the opened partition.
+ @param[in] InodeNum Number of the desired Inode
+ @param[out] OutIno Pointer to where it will be stored a pointer to the read inode.
+
+ @return Status of the inode read.
+**/
+EFI_STATUS
+Ext4ReadInode (
+ IN EXT4_PARTITION *Partition,
+ IN EXT4_INO_NR InodeNum,
+ OUT EXT4_INODE **OutIno
+ )
+{
+ UINT64 InodeOffset;
+ UINT32 BlockGroupNumber;
+ EXT4_INODE *Inode;
+ EXT4_BLOCK_GROUP_DESC *BlockGroup;
+ EXT4_BLOCK_NR InodeTableStart;
+ EFI_STATUS Status;
+
+ BlockGroupNumber = (UINT32)DivU64x64Remainder (
+ InodeNum - 1,
+ Partition->SuperBlock.s_inodes_per_group,
+ &InodeOffset
+ );
+
+ // Check for the block group number's correctness
+ if (BlockGroupNumber >= Partition->NumberBlockGroups) {
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ Inode = Ext4AllocateInode (Partition);
+
+ if (Inode == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ BlockGroup = Ext4GetBlockGroupDesc (Partition, BlockGroupNumber);
+
+ // Note: We'll need to check INODE_UNINIT and friends when/if we add write support
+
+ InodeTableStart = EXT4_BLOCK_NR_FROM_HALFS (
+ Partition,
+ BlockGroup->bg_inode_table_lo,
+ BlockGroup->bg_inode_table_hi
+ );
+
+ Status = Ext4ReadDiskIo (
+ Partition,
+ Inode,
+ Partition->InodeSize,
+ EXT4_BLOCK_TO_BYTES (Partition, InodeTableStart) + MultU64x32 (InodeOffset, Partition->InodeSize)
+ );
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "[ext4] Error reading inode: status %x; inode offset %lx"
+ " inode table start %lu block group %lu\n",
+ Status,
+ InodeOffset,
+ InodeTableStart,
+ BlockGroupNumber
+ ));
+ FreePool (Inode);
+ return Status;
+ }
+
+ if (!Ext4CheckInodeChecksum (Partition, Inode, InodeNum)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "[ext4] Inode %llu has invalid checksum (calculated %x)\n",
+ InodeNum,
+ Ext4CalculateInodeChecksum (Partition, Inode, InodeNum)
+ ));
+ FreePool (Inode);
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ *OutIno = Inode;
+ return EFI_SUCCESS;
+}
+
+/**
+ Calculates the checksum of the block group descriptor for METADATA_CSUM enabled filesystems.
+ @param[in] Partition Pointer to the opened EXT4 partition.
+ @param[in] BlockGroupDesc Pointer to the block group descriptor.
+ @param[in] BlockGroupNum Number of the block group.
+
+ @return The checksum.
+**/
+STATIC
+UINT16
+Ext4CalculateBlockGroupDescChecksumMetadataCsum (
+ IN CONST EXT4_PARTITION *Partition,
+ IN CONST EXT4_BLOCK_GROUP_DESC *BlockGroupDesc,
+ IN UINT32 BlockGroupNum
+ )
+{
+ UINT32 Csum;
+ UINT16 Dummy;
+
+ Dummy = 0;
+
+ Csum = Ext4CalculateChecksum (Partition, &BlockGroupNum, sizeof (BlockGroupNum), Partition->InitialSeed);
+ Csum = Ext4CalculateChecksum (Partition, BlockGroupDesc, OFFSET_OF (EXT4_BLOCK_GROUP_DESC, bg_checksum), Csum);
+ Csum = Ext4CalculateChecksum (Partition, &Dummy, sizeof (Dummy), Csum);
+ Csum =
+ Ext4CalculateChecksum (
+ Partition,
+ &BlockGroupDesc->bg_block_bitmap_hi,
+ Partition->DescSize - OFFSET_OF (EXT4_BLOCK_GROUP_DESC, bg_block_bitmap_hi),
+ Csum
+ );
+ return (UINT16)Csum;
+}
+
+/**
+ Calculates the checksum of the block group descriptor for GDT_CSUM enabled filesystems.
+ @param[in] Partition Pointer to the opened EXT4 partition.
+ @param[in] BlockGroupDesc Pointer to the block group descriptor.
+ @param[in] BlockGroupNum Number of the block group.
+
+ @return The checksum.
+**/
+STATIC
+UINT16
+Ext4CalculateBlockGroupDescChecksumGdtCsum (
+ IN CONST EXT4_PARTITION *Partition,
+ IN CONST EXT4_BLOCK_GROUP_DESC *BlockGroupDesc,
+ IN UINT32 BlockGroupNum
+ )
+{
+ UINT16 Csum;
+ UINT16 Dummy;
+
+ Dummy = 0;
+
+ Csum = CalculateCrc16 (Partition->SuperBlock.s_uuid, 16, 0);
+ Csum = CalculateCrc16 (&BlockGroupNum, sizeof (BlockGroupNum), Csum);
+ Csum = CalculateCrc16 (BlockGroupDesc, OFFSET_OF (EXT4_BLOCK_GROUP_DESC, bg_checksum), Csum);
+ Csum = CalculateCrc16 (&Dummy, sizeof (Dummy), Csum);
+ Csum =
+ CalculateCrc16 (
+ &BlockGroupDesc->bg_block_bitmap_hi,
+ Partition->DescSize - OFFSET_OF (EXT4_BLOCK_GROUP_DESC, bg_block_bitmap_hi),
+ Csum
+ );
+ return Csum;
+}
+
+/**
+ Checks if the checksum of the block group descriptor is correct.
+ @param[in] Partition Pointer to the opened EXT4 partition.
+ @param[in] BlockGroupDesc Pointer to the block group descriptor.
+ @param[in] BlockGroupNum Number of the block group.
+
+ @return TRUE if checksum is correct, FALSE if there is corruption.
+**/
+BOOLEAN
+Ext4VerifyBlockGroupDescChecksum (
+ IN CONST EXT4_PARTITION *Partition,
+ IN CONST EXT4_BLOCK_GROUP_DESC *BlockGroupDesc,
+ IN UINT32 BlockGroupNum
+ )
+{
+ if (!EXT4_HAS_METADATA_CSUM (Partition) && !EXT4_HAS_GDT_CSUM (Partition)) {
+ return TRUE;
+ }
+
+ return Ext4CalculateBlockGroupDescChecksum (Partition, BlockGroupDesc, BlockGroupNum) == BlockGroupDesc->bg_checksum;
+}
+
+/**
+ Calculates the checksum of the block group descriptor.
+ @param[in] Partition Pointer to the opened EXT4 partition.
+ @param[in] BlockGroupDesc Pointer to the block group descriptor.
+ @param[in] BlockGroupNum Number of the block group.
+
+ @return The checksum.
+**/
+UINT16
+Ext4CalculateBlockGroupDescChecksum (
+ IN CONST EXT4_PARTITION *Partition,
+ IN CONST EXT4_BLOCK_GROUP_DESC *BlockGroupDesc,
+ IN UINT32 BlockGroupNum
+ )
+{
+ if (Partition->FeaturesRoCompat & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) {
+ return Ext4CalculateBlockGroupDescChecksumMetadataCsum (Partition, BlockGroupDesc, BlockGroupNum);
+ } else if (Partition->FeaturesRoCompat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) {
+ return Ext4CalculateBlockGroupDescChecksumGdtCsum (Partition, BlockGroupDesc, BlockGroupNum);
+ }
+
+ return 0;
+}
diff --git a/Features/Ext4Pkg/Ext4Dxe/Collation.c b/Features/Ext4Pkg/Ext4Dxe/Collation.c
new file mode 100644
index 0000000000..510ab24ebd
--- /dev/null
+++ b/Features/Ext4Pkg/Ext4Dxe/Collation.c
@@ -0,0 +1,173 @@
+/** @file
+ Unicode collation routines
+
+ Copyright (c) 2021 Pedro Falcato All rights reserved.
+ Copyright (c) 2005 - 2017, Intel Corporation. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+
+#include <Library/UefiLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+#include <Protocol/UnicodeCollation.h>
+
+STATIC EFI_UNICODE_COLLATION_PROTOCOL *gUnicodeCollationInterface = NULL;
+
+/*
+ * Note: This code is heavily based on FatPkg's Unicode collation, since they seem to know what
+ * they're doing.
+ * PS: Maybe all this code could be put in a library? It looks heavily shareable.
+**/
+
+/**
+ Worker function to initialize Unicode Collation support.
+
+ It tries to locate Unicode Collation (2) protocol and matches it with current
+ platform language code.
+
+ @param[in] DriverHandle The handle used to open Unicode Collation (2) protocol.
+ @param[in] ProtocolGuid The pointer to Unicode Collation (2) protocol GUID.
+ @param[in] VariableName The name of the RFC 4646 or ISO 639-2 language variable.
+ @param[in] DefaultLanguage The default language in case the RFC 4646 or ISO 639-2 language is absent.
+
+ @retval EFI_SUCCESS The Unicode Collation (2) protocol has been successfully located.
+ @retval Others The Unicode Collation (2) protocol has not been located.
+
+**/
+STATIC
+EFI_STATUS
+Ext4InitialiseUnicodeCollationInternal (
+ IN EFI_HANDLE DriverHandle,
+ IN EFI_GUID *ProtocolGuid,
+ IN CONST CHAR16 *VariableName,
+ IN CONST CHAR8 *DefaultLanguage
+ )
+{
+ UINTN NumHandles;
+ EFI_HANDLE *Handles;
+ EFI_UNICODE_COLLATION_PROTOCOL *Uci;
+ BOOLEAN Iso639Language;
+ CHAR8 *Language;
+ EFI_STATUS RetStatus;
+ EFI_STATUS Status;
+ UINTN Idx;
+ CHAR8 *BestLanguage;
+
+ Iso639Language = (BOOLEAN)(ProtocolGuid == &gEfiUnicodeCollationProtocolGuid);
+ RetStatus = EFI_UNSUPPORTED;
+ GetEfiGlobalVariable2 (VariableName, (VOID **)&Language, NULL);
+
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ ProtocolGuid,
+ NULL,
+ &NumHandles,
+ &Handles
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ for (Idx = 0; Idx < NumHandles; Idx++) {
+ Status = gBS->OpenProtocol (
+ Handles[Idx],
+ ProtocolGuid,
+ (VOID **)&Uci,
+ DriverHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+
+ BestLanguage = GetBestLanguage (
+ Uci->SupportedLanguages,
+ Iso639Language,
+ (Language == NULL) ? "" : Language,
+ DefaultLanguage,
+ NULL
+ );
+ if (BestLanguage != NULL) {
+ FreePool (BestLanguage);
+ gUnicodeCollationInterface = Uci;
+ RetStatus = EFI_SUCCESS;
+ break;
+ }
+ }
+
+ if (Language != NULL) {
+ FreePool (Language);
+ }
+
+ FreePool (Handles);
+ return RetStatus;
+}
+
+/**
+ Initialises Unicode collation, which is needed for case-insensitive string comparisons
+ within the driver (a good example of an application of this is filename comparison).
+
+ @param[in] DriverHandle Handle to the driver image.
+
+ @retval EFI_SUCCESS Unicode collation was successfully initialised.
+ @retval !EFI_SUCCESS Failure.
+**/
+EFI_STATUS
+Ext4InitialiseUnicodeCollation (
+ EFI_HANDLE DriverHandle
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EFI_UNSUPPORTED;
+
+ //
+ // First try to use RFC 4646 Unicode Collation 2 Protocol.
+ //
+ Status = Ext4InitialiseUnicodeCollationInternal (
+ DriverHandle,
+ &gEfiUnicodeCollation2ProtocolGuid,
+ L"PlatformLang",
+ (CONST CHAR8 *)PcdGetPtr (PcdUefiVariableDefaultPlatformLang)
+ );
+ //
+ // If the attempt to use Unicode Collation 2 Protocol fails, then we fall back
+ // on the ISO 639-2 Unicode Collation Protocol.
+ //
+ if (EFI_ERROR (Status)) {
+ Status = Ext4InitialiseUnicodeCollationInternal (
+ DriverHandle,
+ &gEfiUnicodeCollationProtocolGuid,
+ L"Lang",
+ (CONST CHAR8 *)PcdGetPtr (PcdUefiVariableDefaultLang)
+ );
+ }
+
+ return Status;
+}
+
+/**
+ Does a case-insensitive string comparison. Refer to EFI_UNICODE_COLLATION_PROTOCOL's StriColl
+ for more details.
+
+ @param[in] Str1 Pointer to a null terminated string.
+ @param[in] Str2 Pointer to a null terminated string.
+
+ @retval 0 Str1 is equivalent to Str2.
+ @retval >0 Str1 is lexically greater than Str2.
+ @retval <0 Str1 is lexically less than Str2.
+**/
+INTN
+Ext4StrCmpInsensitive (
+ IN CHAR16 *Str1,
+ IN CHAR16 *Str2
+ )
+{
+ return gUnicodeCollationInterface->StriColl (gUnicodeCollationInterface, Str1, Str2);
+}
diff --git a/Features/Ext4Pkg/Ext4Dxe/Crc16.c b/Features/Ext4Pkg/Ext4Dxe/Crc16.c
new file mode 100644
index 0000000000..58d0220e4d
--- /dev/null
+++ b/Features/Ext4Pkg/Ext4Dxe/Crc16.c
@@ -0,0 +1,75 @@
+/** @file
+ CRC16 calculation routines.
+
+ Copyright (c) 2021 Pedro Falcato All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+
+STATIC CONST UINT16 gCrc16LookupTable[256] =
+{
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x0919, 0x1890, 0x2a0b, 0x3b82, 0x4f3d, 0x5eb4, 0x6c2f, 0x7da6,
+ 0x8551, 0x94d8, 0xa643, 0xb7ca, 0xc375, 0xd2fc, 0xe067, 0xf1ee,
+ 0x1232, 0x03bb, 0x3120, 0x20a9, 0x5416, 0x459f, 0x7704, 0x668d,
+ 0x9e7a, 0x8ff3, 0xbd68, 0xace1, 0xd85e, 0xc9d7, 0xfb4c, 0xeac5,
+ 0x1b2b, 0x0aa2, 0x3839, 0x29b0, 0x5d0f, 0x4c86, 0x7e1d, 0x6f94,
+ 0x9763, 0x86ea, 0xb471, 0xa5f8, 0xd147, 0xc0ce, 0xf255, 0xe3dc,
+ 0x2464, 0x35ed, 0x0776, 0x16ff, 0x6240, 0x73c9, 0x4152, 0x50db,
+ 0xa82c, 0xb9a5, 0x8b3e, 0x9ab7, 0xee08, 0xff81, 0xcd1a, 0xdc93,
+ 0x2d7d, 0x3cf4, 0x0e6f, 0x1fe6, 0x6b59, 0x7ad0, 0x484b, 0x59c2,
+ 0xa135, 0xb0bc, 0x8227, 0x93ae, 0xe711, 0xf698, 0xc403, 0xd58a,
+ 0x3656, 0x27df, 0x1544, 0x04cd, 0x7072, 0x61fb, 0x5360, 0x42e9,
+ 0xba1e, 0xab97, 0x990c, 0x8885, 0xfc3a, 0xedb3, 0xdf28, 0xcea1,
+ 0x3f4f, 0x2ec6, 0x1c5d, 0x0dd4, 0x796b, 0x68e2, 0x5a79, 0x4bf0,
+ 0xb307, 0xa28e, 0x9015, 0x819c, 0xf523, 0xe4aa, 0xd631, 0xc7b8,
+ 0x48c8, 0x5941, 0x6bda, 0x7a53, 0x0eec, 0x1f65, 0x2dfe, 0x3c77,
+ 0xc480, 0xd509, 0xe792, 0xf61b, 0x82a4, 0x932d, 0xa1b6, 0xb03f,
+ 0x41d1, 0x5058, 0x62c3, 0x734a, 0x07f5, 0x167c, 0x24e7, 0x356e,
+ 0xcd99, 0xdc10, 0xee8b, 0xff02, 0x8bbd, 0x9a34, 0xa8af, 0xb926,
+ 0x5afa, 0x4b73, 0x79e8, 0x6861, 0x1cde, 0x0d57, 0x3fcc, 0x2e45,
+ 0xd6b2, 0xc73b, 0xf5a0, 0xe429, 0x9096, 0x811f, 0xb384, 0xa20d,
+ 0x53e3, 0x426a, 0x70f1, 0x6178, 0x15c7, 0x044e, 0x36d5, 0x275c,
+ 0xdfab, 0xce22, 0xfcb9, 0xed30, 0x998f, 0x8806, 0xba9d, 0xab14,
+ 0x6cac, 0x7d25, 0x4fbe, 0x5e37, 0x2a88, 0x3b01, 0x099a, 0x1813,
+ 0xe0e4, 0xf16d, 0xc3f6, 0xd27f, 0xa6c0, 0xb749, 0x85d2, 0x945b,
+ 0x65b5, 0x743c, 0x46a7, 0x572e, 0x2391, 0x3218, 0x0083, 0x110a,
+ 0xe9fd, 0xf874, 0xcaef, 0xdb66, 0xafd9, 0xbe50, 0x8ccb, 0x9d42,
+ 0x7e9e, 0x6f17, 0x5d8c, 0x4c05, 0x38ba, 0x2933, 0x1ba8, 0x0a21,
+ 0xf2d6, 0xe35f, 0xd1c4, 0xc04d, 0xb4f2, 0xa57b, 0x97e0, 0x8669,
+ 0x7787, 0x660e, 0x5495, 0x451c, 0x31a3, 0x202a, 0x12b1, 0x0338,
+ 0xfbcf, 0xea46, 0xd8dd, 0xc954, 0xbdeb, 0xac62, 0x9ef9, 0x8f70
+};
+
+/**
+ Calculates the CRC16 checksum of the given buffer.
+
+ @param[in] Buffer Pointer to the buffer.
+ @param[in] Length Length of the buffer, in bytes.
+ @param[in] InitialValue Initial value of the CRC.
+
+ @return The CRC16 checksum.
+**/
+UINT16
+CalculateCrc16 (
+ IN CONST VOID *Buffer,
+ IN UINTN Length,
+ IN UINT16 InitialValue
+ )
+{
+ CONST UINT8 *Buf;
+ UINT16 Crc;
+
+ Buf = Buffer;
+
+ Crc = ~InitialValue;
+
+ while (Length-- != 0) {
+ Crc = gCrc16LookupTable[(Crc & 0xFF) ^ *(Buf++)] ^ (Crc >> 8);
+ }
+
+ return ~Crc;
+}
diff --git a/Features/Ext4Pkg/Ext4Dxe/Crc32c.c b/Features/Ext4Pkg/Ext4Dxe/Crc32c.c
new file mode 100644
index 0000000000..9601316240
--- /dev/null
+++ b/Features/Ext4Pkg/Ext4Dxe/Crc32c.c
@@ -0,0 +1,84 @@
+/** @file
+ CRC32c calculation routines.
+
+ Copyright (c) 2021 Pedro Falcato All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+
+STATIC CONST UINT32 gCrc32cLookupTable[256] = {
+ 0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, 0xc79a971f, 0x35f1141c,
+ 0x26a1e7e8, 0xd4ca64eb, 0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b,
+ 0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24, 0x105ec76f, 0xe235446c,
+ 0xf165b798, 0x030e349b, 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384,
+ 0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, 0x5d1d08bf, 0xaf768bbc,
+ 0xbc267848, 0x4e4dfb4b, 0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a,
+ 0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35, 0xaa64d611, 0x580f5512,
+ 0x4b5fa6e6, 0xb93425e5, 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa,
+ 0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, 0xf779deae, 0x05125dad,
+ 0x1642ae59, 0xe4292d5a, 0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a,
+ 0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595, 0x417b1dbc, 0xb3109ebf,
+ 0xa0406d4b, 0x522bee48, 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957,
+ 0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, 0x0c38d26c, 0xfe53516f,
+ 0xed03a29b, 0x1f682198, 0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927,
+ 0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38, 0xdbfc821c, 0x2997011f,
+ 0x3ac7f2eb, 0xc8ac71e8, 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7,
+ 0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, 0xa65c047d, 0x5437877e,
+ 0x4767748a, 0xb50cf789, 0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859,
+ 0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46, 0x7198540d, 0x83f3d70e,
+ 0x90a324fa, 0x62c8a7f9, 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6,
+ 0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36, 0x3cdb9bdd, 0xceb018de,
+ 0xdde0eb2a, 0x2f8b6829, 0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c,
+ 0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93, 0x082f63b7, 0xfa44e0b4,
+ 0xe9141340, 0x1b7f9043, 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c,
+ 0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, 0x55326b08, 0xa759e80b,
+ 0xb4091bff, 0x466298fc, 0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c,
+ 0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033, 0xa24bb5a6, 0x502036a5,
+ 0x4370c551, 0xb11b4652, 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d,
+ 0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, 0xef087a76, 0x1d63f975,
+ 0x0e330a81, 0xfc588982, 0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d,
+ 0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622, 0x38cc2a06, 0xcaa7a905,
+ 0xd9f75af1, 0x2b9cd9f2, 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed,
+ 0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, 0x0417b1db, 0xf67c32d8,
+ 0xe52cc12c, 0x1747422f, 0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff,
+ 0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0, 0xd3d3e1ab, 0x21b862a8,
+ 0x32e8915c, 0xc083125f, 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540,
+ 0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, 0x9e902e7b, 0x6cfbad78,
+ 0x7fab5e8c, 0x8dc0dd8f, 0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee,
+ 0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1, 0x69e9f0d5, 0x9b8273d6,
+ 0x88d28022, 0x7ab90321, 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e,
+ 0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81, 0x34f4f86a, 0xc69f7b69,
+ 0xd5cf889d, 0x27a40b9e, 0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e,
+ 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351
+};
+
+/**
+ Calculates the CRC32c checksum of the given buffer.
+
+ @param[in] Buffer Pointer to the buffer.
+ @param[in] Length Length of the buffer, in bytes.
+ @param[in] InitialValue Initial value of the CRC.
+
+ @return The CRC32c checksum.
+**/
+UINT32
+CalculateCrc32c (
+ IN CONST VOID *Buffer,
+ IN UINTN Length,
+ IN UINT32 InitialValue
+ )
+{
+ CONST UINT8 *Buf;
+ UINT32 Crc;
+
+ Buf = Buffer;
+ Crc = ~InitialValue;
+
+ while (Length-- != 0) {
+ Crc = gCrc32cLookupTable[(Crc & 0xFF) ^ *(Buf++)] ^ (Crc >> 8);
+ }
+
+ return ~Crc;
+}
diff --git a/Features/Ext4Pkg/Ext4Dxe/Directory.c b/Features/Ext4Pkg/Ext4Dxe/Directory.c
new file mode 100644
index 0000000000..081c6bf0f4
--- /dev/null
+++ b/Features/Ext4Pkg/Ext4Dxe/Directory.c
@@ -0,0 +1,498 @@
+/** @file
+ Directory related routines
+
+ Copyright (c) 2021 Pedro Falcato All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "Ext4Dxe.h"
+
+#include <Library/BaseUcs2Utf8Lib.h>
+
+/**
+ 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.
+
+ @retval EFI_SUCCESS The filename was succesfully retrieved and converted to UCS2.
+ @retval !EFI_SUCCESS Failure.
+**/
+EFI_STATUS
+Ext4GetUcs2DirentName (
+ IN EXT4_DIR_ENTRY *Entry,
+ OUT CHAR16 Ucs2FileName[EXT4_NAME_MAX + 1]
+ )
+{
+ CHAR8 Utf8NameBuf[EXT4_NAME_MAX + 1];
+ UINT16 *Str;
+ EFI_STATUS Status;
+
+ CopyMem (Utf8NameBuf, Entry->name, Entry->name_len);
+
+ Utf8NameBuf[Entry->name_len] = '\0';
+
+ // Unfortunately, BaseUcs2Utf8Lib doesn't have a convert-buffer-to-buffer-like
+ // function. Therefore, we need to allocate from the pool (inside UTF8StrToUCS2),
+ // copy it to our out buffer (Ucs2FileName) and free.
+
+ Status = UTF8StrToUCS2 (Utf8NameBuf, &Str);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = StrCpyS (Ucs2FileName, EXT4_NAME_MAX + 1, Str);
+
+ FreePool (Str);
+
+ return Status;
+}
+
+/**
+ Retrieves a directory entry.
+
+ @param[in] Directory Pointer to the opened directory.
+ @param[in] NameUnicode Pointer to the UCS-2 formatted filename.
+ @param[in] Partition Pointer to the ext4 partition.
+ @param[out] Result Pointer to the destination directory entry.
+
+ @return The result of the operation.
+**/
+EFI_STATUS
+Ext4RetrieveDirent (
+ IN EXT4_FILE *Directory,
+ IN CONST CHAR16 *Name,
+ IN EXT4_PARTITION *Partition,
+ OUT EXT4_DIR_ENTRY *Result
+ )
+{
+ EFI_STATUS Status;
+ CHAR8 *Buf;
+ UINT64 Off;
+ EXT4_INODE *Inode;
+ UINT64 DirInoSize;
+ UINT32 BlockRemainder;
+ UINTN Length;
+ CHAR8 *BufPtr;
+ EXT4_DIR_ENTRY *Entry;
+ UINTN RemainingBlock;
+ CHAR16 DirentUcs2Name[EXT4_NAME_MAX + 1];
+ UINTN ToCopy;
+
+ Status = EFI_NOT_FOUND;
+ Buf = AllocatePool (Partition->BlockSize);
+
+ if (Buf == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Off = 0;
+
+ Inode = Directory->Inode;
+ DirInoSize = EXT4_INODE_SIZE (Inode);
+
+ DivU64x32Remainder (DirInoSize, Partition->BlockSize, &BlockRemainder);
+ if (BlockRemainder != 0) {
+ // Directory inodes need to have block aligned sizes
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ while (Off < DirInoSize) {
+ Length = Partition->BlockSize;
+
+ Status = Ext4Read (Partition, Directory, Buf, Off, &Length);
+
+ if (Status != EFI_SUCCESS) {
+ FreePool (Buf);
+ return Status;
+ }
+
+ for (BufPtr = Buf; BufPtr < Buf + Partition->BlockSize; ) {
+ Entry = (EXT4_DIR_ENTRY *)BufPtr;
+ if (Entry->rec_len == 0) {
+ FreePool (Buf);
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ RemainingBlock = Partition->BlockSize - (BufPtr - Buf);
+
+ if (Entry->name_len > RemainingBlock || Entry->rec_len > RemainingBlock) {
+ // Corrupted filesystem
+ FreePool (Buf);
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ // Ignore names bigger than our limit.
+
+ /* Note: I think having a limit is sane because:
+ 1) It's nicer to work with.
+ 2) Linux and a number of BSDs also have a filename limit of 255.
+ */
+ if (Entry->name_len > EXT4_NAME_MAX) {
+ continue;
+ }
+
+ // Unused entry
+ if (Entry->inode == 0) {
+ BufPtr += Entry->rec_len;
+ continue;
+ }
+
+ Status = Ext4GetUcs2DirentName (Entry, DirentUcs2Name);
+
+ /* In theory, this should never fail.
+ * In reality, it's quite possible that it can fail, considering filenames in
+ * Linux (and probably other nixes) are just null-terminated bags of bytes, and don't
+ * 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.
+ BufPtr += Entry->rec_len;
+ continue;
+ }
+
+ if (Entry->name_len == StrLen (Name) &&
+ !Ext4StrCmpInsensitive (DirentUcs2Name, (CHAR16 *)Name)) {
+ ToCopy = MIN (Entry->rec_len, sizeof (EXT4_DIR_ENTRY));
+
+ CopyMem (Result, Entry, ToCopy);
+ FreePool (Buf);
+ return EFI_SUCCESS;
+ }
+
+ BufPtr += Entry->rec_len;
+ }
+
+ Off += Partition->BlockSize;
+ }
+
+ FreePool (Buf);
+ return EFI_NOT_FOUND;
+}
+
+/**
+ Opens a file using a directory entry.
+
+ @param[in] Partition Pointer to the ext4 partition.
+ @param[in] OpenMode Mode in which the file is supposed to be open.
+ @param[out] OutFile Pointer to the newly opened file.
+ @param[in] Entry Directory entry to be used.
+
+ @retval EFI_STATUS Result of the operation
+**/
+EFI_STATUS
+Ext4OpenDirent (
+ IN EXT4_PARTITION *Partition,
+ IN UINT64 OpenMode,
+ OUT EXT4_FILE **OutFile,
+ IN EXT4_DIR_ENTRY *Entry
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 FileName[EXT4_NAME_MAX + 1];
+ EXT4_FILE *File;
+
+ File = AllocateZeroPool (sizeof (EXT4_FILE));
+
+ if (File == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ Status = Ext4GetUcs2DirentName (Entry, FileName);
+
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+
+ File->FileName = AllocateZeroPool (StrSize (FileName));
+
+ if (!File->FileName) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ Status = Ext4InitExtentsMap (File);
+
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+
+ // This should not fail.
+ StrCpyS (File->FileName, ARRAY_SIZE (FileName), FileName);
+
+ File->InodeNum = Entry->inode;
+
+ Ext4SetupFile (File, Partition);
+
+ Status = Ext4ReadInode (Partition, Entry->inode, &File->Inode);
+
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+
+ *OutFile = File;
+
+ InsertTailList (&Partition->OpenFiles, &File->OpenFilesListNode);
+
+ return EFI_SUCCESS;
+
+Error:
+ if (File != NULL) {
+ if (File->FileName != NULL) {
+ FreePool (File->FileName);
+ }
+
+ if (File->ExtentsMap != NULL) {
+ OrderedCollectionUninit (File->ExtentsMap);
+ }
+
+ FreePool (File);
+ }
+
+ return Status;
+}
+
+/**
+ Opens a file.
+
+ @param[in] Directory Pointer to the opened directory.
+ @param[in] Name Pointer to the UCS-2 formatted filename.
+ @param[in] Partition Pointer to the ext4 partition.
+ @param[in] OpenMode Mode in which the file is supposed to be open.
+ @param[out] OutFile Pointer to the newly opened file.
+
+ @return Result of the operation.
+**/
+EFI_STATUS
+Ext4OpenFile (
+ IN EXT4_FILE *Directory,
+ IN CONST CHAR16 *Name,
+ IN EXT4_PARTITION *Partition,
+ IN UINT64 OpenMode,
+ OUT EXT4_FILE **OutFile
+ )
+{
+ EXT4_DIR_ENTRY Entry;
+ EFI_STATUS Status;
+
+ Status = Ext4RetrieveDirent (Directory, Name, Partition, &Entry);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ // EFI requires us to error out on ".." opens for the root directory
+ if (Entry.inode == Directory->InodeNum) {
+ return EFI_NOT_FOUND;
+ }
+
+ return Ext4OpenDirent (Partition, OpenMode, OutFile, &Entry);
+}
+
+/**
+ Open the root directory on a volume.
+
+ @param[in] This A pointer to the volume to open the root directory.
+ @param[out] Root A pointer to the location to return the opened file handle for the
+ root directory.
+
+ @retval EFI_SUCCESS The device was opened.
+ @retval EFI_UNSUPPORTED This volume does not support the requested file system type.
+ @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_ACCESS_DENIED The service denied access to the file.
+ @retval EFI_OUT_OF_RESOURCES The volume was not opened due to lack of resources.
+ @retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no
+ longer supported. Any existing file handles for this volume are
+ no longer valid. To access the files on the new medium, the
+ volume must be reopened with OpenVolume().
+
+**/
+EFI_STATUS
+EFIAPI
+Ext4OpenVolume (
+ IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This,
+ OUT EFI_FILE_PROTOCOL **Root
+ )
+{
+ EXT4_INODE *RootInode;
+ EFI_STATUS Status;
+ EXT4_FILE *RootDir;
+
+ // 2 is always the root inode number in ext4
+ Status = Ext4ReadInode ((EXT4_PARTITION *)This, 2, &RootInode);
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "[ext4] Could not open root inode - status %x\n", Status));
+ return Status;
+ }
+
+ RootDir = AllocateZeroPool (sizeof (EXT4_FILE));
+
+ if (!RootDir) {
+ FreePool (RootInode);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ // The filename will be "\"(null terminated of course)
+ RootDir->FileName = AllocateZeroPool (2 * sizeof (CHAR16));
+
+ if (!RootDir->FileName) {
+ FreePool (RootDir);
+ FreePool (RootInode);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ RootDir->FileName[0] = L'\\';
+
+ RootDir->Inode = RootInode;
+ RootDir->InodeNum = 2;
+
+ Status = Ext4InitExtentsMap (RootDir);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (RootDir->FileName);
+ FreePool (RootInode);
+ FreePool (RootDir);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Ext4SetupFile (RootDir, (EXT4_PARTITION *)This);
+ *Root = &RootDir->Protocol;
+
+ InsertTailList (&((EXT4_PARTITION *)This)->OpenFiles, &RootDir->OpenFilesListNode);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Validates a directory entry.
+
+ @param[in] Dirent Pointer to the directory entry.
+
+ @retval TRUE Valid directory entry.
+ FALSE Invalid directory entry.
+**/
+STATIC
+BOOLEAN
+Ext4ValidDirent (
+ IN CONST EXT4_DIR_ENTRY *Dirent
+ )
+{
+ UINTN RequiredSize;
+
+ RequiredSize = Dirent->name_len + EXT4_MIN_DIR_ENTRY_LEN;
+
+ if (Dirent->rec_len < RequiredSize) {
+ DEBUG ((DEBUG_ERROR, "[ext4] dirent size %lu too small (compared to %lu)\n", Dirent->rec_len, RequiredSize));
+ return FALSE;
+ }
+
+ // Dirent sizes need to be 4 byte aligned
+ if (Dirent->rec_len % 4) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ Reads a directory entry.
+
+ @param[in] Partition Pointer to the ext4 partition.
+ @param[in] File Pointer to the open directory.
+ @param[out] Buffer Pointer to the output buffer.
+ @param[in] Offset Initial directory position.
+ @param[in out] OutLength Pointer to a UINTN that contains the length of the buffer,
+ and the length of the actual EFI_FILE_INFO after the call.
+
+ @return Result of the operation.
+**/
+EFI_STATUS
+Ext4ReadDir (
+ IN EXT4_PARTITION *Partition,
+ IN EXT4_FILE *File,
+ OUT VOID *Buffer,
+ IN UINT64 Offset,
+ IN OUT UINTN *OutLength
+ )
+{
+ EXT4_INODE *DirIno;
+ EFI_STATUS Status;
+ UINT64 DirInoSize;
+ UINTN Len;
+ UINT32 BlockRemainder;
+ EXT4_DIR_ENTRY Entry;
+ EXT4_FILE *TempFile;
+
+ DirIno = File->Inode;
+ Status = EFI_SUCCESS;
+ DirInoSize = EXT4_INODE_SIZE (DirIno);
+
+ DivU64x32Remainder (DirInoSize, Partition->BlockSize, &BlockRemainder);
+ if (BlockRemainder != 0) {
+ // Directory inodes need to have block aligned sizes
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ while (TRUE) {
+ TempFile = NULL;
+
+ // We (try to) read the maximum size of a directory entry at a time
+ // Note that we don't need to read any padding that may exist after it.
+ Len = sizeof (Entry);
+ Status = Ext4Read (Partition, File, &Entry, Offset, &Len);
+
+ if (EFI_ERROR (Status)) {
+ goto Out;
+ }
+
+ if (Len == 0) {
+ *OutLength = 0;
+ Status = EFI_SUCCESS;
+ goto Out;
+ }
+
+ if (Len < EXT4_MIN_DIR_ENTRY_LEN) {
+ Status = EFI_VOLUME_CORRUPTED;
+ goto Out;
+ }
+
+ // Invalid directory entry length
+ if (!Ext4ValidDirent (&Entry)) {
+ DEBUG ((DEBUG_ERROR, "[ext4] Invalid dirent at offset %lu\n", Offset));
+ Status = EFI_VOLUME_CORRUPTED;
+ goto Out;
+ }
+
+ if (Entry.inode == 0) {
+ // When inode = 0, it's unused
+ Offset += Entry.rec_len;
+ continue;
+ }
+
+ Status = Ext4OpenDirent (Partition, EFI_FILE_MODE_READ, &TempFile, &Entry);
+
+ if (EFI_ERROR (Status)) {
+ goto Out;
+ }
+
+ Status = Ext4GetFileInfo (TempFile, Buffer, OutLength);
+ if (!EFI_ERROR (Status)) {
+ File->Position = Offset + Entry.rec_len;
+ }
+
+ Ext4CloseInternal (TempFile);
+
+ goto Out;
+ }
+
+ Status = EFI_SUCCESS;
+Out:
+ return Status;
+}
diff --git a/Features/Ext4Pkg/Ext4Dxe/DiskUtil.c b/Features/Ext4Pkg/Ext4Dxe/DiskUtil.c
new file mode 100644
index 0000000000..da81203747
--- /dev/null
+++ b/Features/Ext4Pkg/Ext4Dxe/DiskUtil.c
@@ -0,0 +1,113 @@
+/** @file
+ Disk utilities
+
+ Copyright (c) 2021 Pedro Falcato All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+#include "Ext4Dxe.h"
+
+/**
+ Reads from the partition's disk using the DISK_IO protocol.
+
+ @param[in] Partition Pointer to the opened ext4 partition.
+ @param[out] Buffer Pointer to a destination buffer.
+ @param[in] Length Length of the destination buffer.
+ @param[in] Offset Offset, in bytes, of the location to read.
+
+ @return Success status of the disk read.
+**/
+EFI_STATUS
+Ext4ReadDiskIo (
+ IN EXT4_PARTITION *Partition,
+ OUT VOID *Buffer,
+ IN UINTN Length,
+ IN UINT64 Offset
+ )
+{
+ return EXT4_DISK_IO (Partition)->ReadDisk (
+ EXT4_DISK_IO (Partition),
+ EXT4_MEDIA_ID (Partition),
+ Offset,
+ Length,
+ Buffer
+ );
+}
+
+/**
+ Reads blocks from the partition's disk using the DISK_IO protocol.
+
+ @param[in] Partition Pointer to the opened ext4 partition.
+ @param[out] Buffer Pointer to a destination buffer.
+ @param[in] NumberBlocks Length of the read, in filesystem blocks.
+ @param[in] BlockNumber Starting block number.
+
+ @return Success status of the read.
+**/
+EFI_STATUS
+Ext4ReadBlocks (
+ IN EXT4_PARTITION *Partition,
+ OUT VOID *Buffer,
+ IN UINTN NumberBlocks,
+ IN EXT4_BLOCK_NR BlockNumber
+ )
+{
+ UINT64 Offset;
+ UINTN Length;
+
+ 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)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Length > (UINTN)- 1/Partition->BlockSize) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return Ext4ReadDiskIo (Partition, Buffer, Length, Offset);
+}
+
+/**
+ Allocates a buffer and reads blocks from the partition's disk using the DISK_IO protocol.
+ This function is deprecated and will be removed in the future.
+
+ @param[in] Partition Pointer to the opened ext4 partition.
+ @param[in] NumberBlocks Length of the read, in filesystem blocks.
+ @param[in] BlockNumber Starting block number.
+
+ @return Buffer allocated by AllocatePool, or NULL if some part of the process
+ failed.
+**/
+VOID *
+Ext4AllocAndReadBlocks (
+ IN EXT4_PARTITION *Partition,
+ IN UINTN NumberBlocks,
+ IN EXT4_BLOCK_NR BlockNumber
+ )
+{
+ VOID *Buf;
+ UINTN Length;
+
+ Length = NumberBlocks * Partition->BlockSize;
+
+ if (Length > (UINTN)- 1/Partition->BlockSize) {
+ return NULL;
+ }
+
+ Buf = AllocatePool (Length);
+
+ if (Buf == NULL) {
+ return NULL;
+ }
+
+ if (Ext4ReadBlocks (Partition, Buf, NumberBlocks, BlockNumber) != EFI_SUCCESS) {
+ FreePool (Buf);
+ return NULL;
+ }
+
+ return Buf;
+}
diff --git a/Features/Ext4Pkg/Ext4Dxe/Ext4Disk.h b/Features/Ext4Pkg/Ext4Dxe/Ext4Disk.h
new file mode 100644
index 0000000000..b387ebcd36
--- /dev/null
+++ b/Features/Ext4Pkg/Ext4Dxe/Ext4Disk.h
@@ -0,0 +1,454 @@
+/** @file
+ Raw filesystem data structures
+
+ Copyright (c) 2021 Pedro Falcato All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ Layout of an EXT2/3/4 filesystem:
+ (note: this driver has been developed using
+ https://www.kernel.org/doc/html/latest/filesystems/ext4/index.html as
+ documentation).
+
+ An ext2/3/4 filesystem (here on out referred to as simply an ext4 filesystem,
+ due to the similarities) is composed of various concepts:
+
+ 1) Superblock
+ The superblock is the structure near (1024 bytes offset from the start)
+ the start of the partition, and describes the filesystem in general.
+ Here, we get to know the size of the filesystem's blocks, which features
+ it supports or not, whether it's been cleanly unmounted, how many blocks
+ we have, etc.
+
+ 2) Block groups
+ EXT4 filesystems are divided into block groups, and each block group covers
+ s_blocks_per_group(8 * Block Size) blocks. Each block group has an
+ associated block group descriptor; these are present directly after the
+ superblock. Each block group descriptor contains the location of the
+ inode table, and the inode and block bitmaps (note these bitmaps are only
+ a block long, which gets us the 8 * Block Size formula covered previously).
+
+ 3) Blocks
+ The ext4 filesystem is divided in blocks, of size s_log_block_size ^ 1024.
+ Blocks can be allocated using individual block groups's bitmaps. Note
+ that block 0 is invalid and its presence on extents/block tables means
+ it's part of a file hole, and that particular location must be read as
+ a block full of zeros.
+
+ 4) Inodes
+ The ext4 filesystem divides files/directories into inodes (originally
+ index nodes). Each file/socket/symlink/directory/etc (here on out referred
+ to as a file, since there is no distinction under the ext4 filesystem) is
+ stored as a /nameless/ inode, that is stored in some block group's inode
+ table. Each inode has s_inode_size size (or GOOD_OLD_INODE_SIZE if it's
+ an old filesystem), and holds various metadata about the file. Since the
+ largest inode structure right now is ~160 bytes, the rest of the inode
+ contains inline extended attributes. Inodes' data is stored using either
+ data blocks (under ext2/3) or extents (under ext4).
+
+ 5) Extents
+ Ext4 inodes store data in extents. These let N contiguous logical blocks
+ that are represented by N contiguous physical blocks be represented by a
+ single extent structure, which minimizes filesystem metadata bloat and
+ speeds up block mapping (particularly due to the fact that high-quality
+ ext4 implementations like linux's try /really/ hard to make the file
+ contiguous, so it's common to have files with almost 0 fragmentation).
+ Inodes that use extents store them in a tree, and the top of the tree
+ is stored on i_data. The tree's leaves always start with an
+ EXT4_EXTENT_HEADER and contain EXT4_EXTENT_INDEX on eh_depth != 0 and
+ EXT4_EXTENT on eh_depth = 0; these entries are always sorted by logical
+ block.
+
+ 6) Directories
+ Ext4 directories are files that store name -> inode mappings for the
+ logical directory; this is where files get their names, which means ext4
+ inodes do not themselves have names, since they can be linked (present)
+ multiple times with different names. Directories can store entries in two
+ different ways:
+ 1) Classical linear directories: They store entries as a mostly-linked
+ mostly-list of EXT4_DIR_ENTRY.
+ 2) Hash tree directories: These are used for larger directories, with
+ hundreds of entries, and are designed in a backwards compatible way.
+ These are not yet implemented in the Ext4Dxe driver.
+
+ 7) Journal
+ Ext3/4 filesystems have a journal to help protect the filesystem against
+ system crashes. This is not yet implemented in Ext4Dxe but is described
+ in detail in the Linux kernel's documentation.
+**/
+
+#ifndef EXT4_DISK_H_
+#define EXT4_DISK_H_
+
+#include <Uefi.h>
+
+#define EXT4_SUPERBLOCK_OFFSET 1024U
+
+#define EXT4_SIGNATURE 0xEF53U
+
+#define EXT4_FS_STATE_UNMOUNTED 0x1
+#define EXT4_FS_STATE_ERRORS_DETECTED 0x2
+#define EXT4_FS_STATE_RECOVERING_ORPHANS 0x4
+
+#define EXT4_ERRORS_CONTINUE 1
+#define EXT4_ERRORS_RO 2
+#define EXT4_ERRORS_PANIC 3
+
+#define EXT4_LINUX_ID 0
+#define EXT4_GNU_HURD_ID 1
+#define EXT4_MASIX_ID 2
+#define EXT4_FREEBSD_ID 3
+#define EXT4_LITES_ID 4
+
+#define EXT4_GOOD_OLD_REV 0
+#define EXT4_DYNAMIC_REV 1
+
+#define EXT4_CHECKSUM_CRC32C 0x1
+
+#define EXT4_FEATURE_COMPAT_DIR_PREALLOC 0x01
+#define EXT4_FEATURE_COMPAT_IMAGIC_INODES 0x02
+#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x04
+#define EXT4_FEATURE_COMPAT_EXT_ATTR 0x08
+#define EXT4_FEATURE_COMPAT_RESIZE_INO 0x10
+#define EXT4_FEATURE_COMPAT_DIR_INDEX 0x20
+
+#define EXT4_FEATURE_INCOMPAT_COMPRESSION 0x00001
+#define EXT4_FEATURE_INCOMPAT_FILETYPE 0x00002
+#define EXT4_FEATURE_INCOMPAT_RECOVER 0x00004
+#define EXT4_FEATURE_INCOMPAT_JOURNAL_DEV 0x00008
+#define EXT4_FEATURE_INCOMPAT_META_BG 0x00010
+#define EXT4_FEATURE_INCOMPAT_EXTENTS 0x00040
+#define EXT4_FEATURE_INCOMPAT_64BIT 0x00080
+#define EXT4_FEATURE_INCOMPAT_MMP 0x00100
+#define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x00200
+#define EXT4_FEATURE_INCOMPAT_EA_INODE 0x00400
+// It's not clear whether or not this feature (below) is used right now
+#define EXT4_FEATURE_INCOMPAT_DIRDATA 0x01000
+#define EXT4_FEATURE_INCOMPAT_CSUM_SEED 0x02000
+#define EXT4_FEATURE_INCOMPAT_LARGEDIR 0x04000
+#define EXT4_FEATURE_INCOMPAT_INLINE_DATA 0x08000
+#define EXT4_FEATURE_INCOMPAT_ENCRYPT 0x10000
+
+#define EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001
+#define EXT4_FEATURE_RO_COMPAT_LARGE_FILE 0x0002
+#define EXT4_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 // Unused
+#define EXT4_FEATURE_RO_COMPAT_HUGE_FILE 0x0008
+#define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010
+#define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020
+#define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040
+#define EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT 0x0080 // Not implemented in ext4
+#define EXT4_FEATURE_RO_COMPAT_QUOTA 0x0100
+#define EXT4_FEATURE_RO_COMPAT_BIGALLOC 0x0200
+#define EXT4_FEATURE_RO_COMPAT_METADATA_CSUM 0x0400
+#define EXT4_FEATURE_RO_COMPAT_REPLICA 0x0800 // Not used
+
+// We explicitly don't recognise this, so we get read only.
+#define EXT4_FEATURE_RO_COMPAT_READONLY 0x1000
+#define EXT4_FEATURE_RO_COMPAT_PROJECT 0x2000
+
+/* Important notes about the features
+ * Absolutely needed features:
+ * 1) Every incompat, because we might want to mount root filesystems
+ * 2) Relevant RO_COMPATs(I'm not sure of what to do wrt quota, project)
+**/
+
+#define EXT4_INO_TYPE_FIFO 0x1000
+#define EXT4_INO_TYPE_CHARDEV 0x2000
+#define EXT4_INO_TYPE_DIR 0x4000
+#define EXT4_INO_TYPE_BLOCKDEV 0x6000
+#define EXT4_INO_TYPE_REGFILE 0x8000
+#define EXT4_INO_TYPE_SYMLINK 0xA000
+#define EXT4_INO_TYPE_UNIX_SOCK 0xC000
+
+/* Inode flags */
+#define EXT4_SECRM_FL 0x00000001
+#define EXT4_UNRM_FL 0x00000002
+#define EXT4_COMPR_FL 0x00000004
+#define EXT4_SYNC_FL 0x00000008
+#define EXT4_IMMUTABLE_FL 0x00000010
+#define EXT4_APPEND_FL 0x00000020
+#define EXT4_NODUMP_FL 0x00000040
+#define EXT4_NOATIME_FL 0x00000080
+#define EXT4_DIRTY_FL 0x00000100
+#define EXT4_COMPRBLK_FL 0x00000200
+#define EXT4_NOCOMPR_FL 0x00000400
+#define EXT4_ECOMPR_FL 0x00000800
+#define EXT4_BTREE_FL 0x00001000
+#define EXT4_INDEX_FL 0x00002000
+#define EXT4_JOURNAL_DATA_FL 0x00004000
+#define EXT4_NOTAIL_FL 0x00008000
+#define EXT4_DIRSYNC_FL 0x00010000
+#define EXT4_TOPDIR_FL 0x00020000
+#define EXT4_HUGE_FILE_FL 0x00040000
+#define EXT4_EXTENTS_FL 0x00080000
+#define EXT4_VERITY_FL 0x00100000
+#define EXT4_EA_INODE_FL 0x00200000
+#define EXT4_RESERVED_FL 0x80000000
+
+/* File type flags that are stored in the directory entries */
+#define EXT4_FT_UNKNOWN 0
+#define EXT4_FT_REG_FILE 1
+#define EXT4_FT_DIR 2
+#define EXT4_FT_CHRDEV 3
+#define EXT4_FT_BLKDEV 4
+#define EXT4_FT_FIFO 5
+#define EXT4_FT_SOCK 6
+#define EXT4_FT_SYMLINK 7
+
+typedef struct {
+ UINT32 s_inodes_count;
+ UINT32 s_blocks_count;
+ UINT32 s_r_blocks_count;
+ UINT32 s_free_blocks_count;
+ UINT32 s_free_inodes_count;
+ UINT32 s_first_data_block;
+ UINT32 s_log_block_size;
+ UINT32 s_log_frag_size;
+ UINT32 s_blocks_per_group;
+ UINT32 s_frags_per_group;
+ UINT32 s_inodes_per_group;
+ UINT32 s_mtime;
+ UINT32 s_wtime;
+ UINT16 s_mnt_count;
+ UINT16 s_max_mnt_count;
+ UINT16 s_magic;
+ UINT16 s_state;
+ UINT16 s_errors;
+ UINT16 s_minor_rev_level;
+ UINT32 s_lastcheck;
+ UINT32 s_check_interval;
+ UINT32 s_creator_os;
+ UINT32 s_rev_level;
+ UINT16 s_def_resuid;
+ UINT16 s_def_resgid;
+
+ /* Every field after this comment is revision >= 1 */
+
+ UINT32 s_first_ino;
+ UINT16 s_inode_size;
+ UINT16 s_block_group_nr;
+ UINT32 s_feature_compat;
+ UINT32 s_feature_incompat;
+ UINT32 s_feature_ro_compat;
+ UINT8 s_uuid[16];
+ UINT8 s_volume_name[16];
+ UINT8 s_last_mounted[64];
+ UINT32 s_algo_bitmap;
+ UINT8 s_prealloc_blocks;
+ UINT8 s_prealloc_dir_blocks;
+ UINT16 unused;
+ UINT8 s_journal_uuid[16];
+ UINT32 s_journal_inum;
+ UINT32 s_journal_dev;
+ UINT32 s_last_orphan;
+ UINT32 s_hash_seed[4];
+ UINT8 s_def_hash_version;
+ UINT8 s_jnl_backup_type;
+ UINT16 s_desc_size;
+ UINT32 s_default_mount_options;
+ UINT32 s_first_meta_bg;
+ UINT32 s_mkfs_time;
+ UINT32 s_jnl_blocks[17];
+ UINT32 s_blocks_count_hi;
+ UINT32 s_r_blocks_count_hi;
+ UINT32 s_free_blocks_count_hi;
+ UINT16 s_min_extra_isize;
+ UINT16 s_want_extra_isize;
+ UINT32 s_flags;
+ UINT16 s_raid_stride;
+ UINT16 s_mmp_interval;
+ UINT64 s_mmp_block;
+ UINT32 s_raid_stride_width;
+ UINT8 s_log_groups_per_flex;
+ UINT8 s_checksum_type; // Only valid value is 1 - CRC32C
+ UINT16 s_reserved_pad;
+ UINT64 s_kbytes_written;
+
+ // Snapshot stuff isn't used in Linux and isn't implemented here
+ UINT32 s_snapshot_inum;
+ UINT32 s_snapshot_id;
+ UINT64 s_snapshot_r_blocks_count;
+ UINT32 s_snapshot_list;
+ UINT32 s_error_count;
+ UINT32 s_first_error_time;
+ UINT32 s_first_error_ino;
+ UINT64 s_first_error_block;
+ UINT8 s_first_error_func[32];
+ UINT32 s_first_error_line;
+ UINT32 s_last_error_time;
+ UINT32 s_last_error_ino;
+ UINT32 s_last_error_line;
+ UINT64 s_last_error_block;
+ UINT8 s_last_error_func[32];
+ UINT8 s_mount_opts[64];
+ UINT32 s_usr_quota_inum;
+ UINT32 s_grp_quota_inum;
+ UINT32 s_overhead_blocks;
+ UINT32 s_backup_bgs[2]; // sparse_super2
+ UINT8 s_encrypt_algos[4];
+ UINT8 s_encrypt_pw_salt[16];
+ UINT32 s_lpf_ino;
+ UINT32 s_prj_quota_inum;
+ UINT32 s_checksum_seed;
+ UINT32 s_reserved[98];
+ UINT32 s_checksum;
+} EXT4_SUPERBLOCK;
+
+STATIC_ASSERT (sizeof (EXT4_SUPERBLOCK) == 1024, "ext4 superblock struct has incorrect size");
+
+typedef struct {
+ UINT32 bg_block_bitmap_lo;
+ UINT32 bg_inode_bitmap_lo;
+ UINT32 bg_inode_table_lo;
+ UINT16 bg_free_blocks_count_lo;
+ UINT16 bg_free_inodes_count_lo;
+ UINT16 bg_used_dirs_count_lo;
+ UINT16 bg_flags;
+ UINT32 bg_exclude_bitmap_lo;
+ UINT16 bg_block_bitmap_csum_lo;
+ UINT16 bg_inode_bitmap_csum_lo;
+ UINT16 bg_itable_unused_lo;
+ UINT16 bg_checksum;
+ UINT32 bg_block_bitmap_hi;
+ UINT32 bg_inode_bitmap_hi;
+ UINT32 bg_inode_table_hi;
+ UINT16 bg_free_blocks_count_hi;
+ UINT16 bg_free_inodes_count_hi;
+ UINT16 bg_used_dirs_count_hi;
+ UINT16 bg_itable_unused_hi;
+ UINT32 bg_exclude_bitmap_hi;
+ UINT16 bg_block_bitmap_csum_hi;
+ UINT16 bg_inode_bitmap_csum_hi;
+ UINT32 bg_reserved;
+} EXT4_BLOCK_GROUP_DESC;
+
+#define EXT4_OLD_BLOCK_DESC_SIZE 32
+#define EXT4_64BIT_BLOCK_DESC_SIZE 64
+
+STATIC_ASSERT (
+ sizeof (EXT4_BLOCK_GROUP_DESC) == EXT4_64BIT_BLOCK_DESC_SIZE,
+ "ext4 block group descriptor struct has incorrect size"
+ );
+
+#define EXT4_DBLOCKS 12
+#define EXT4_IND_BLOCK 12
+#define EXT4_DIND_BLOCK 13
+#define EXT4_TIND_BLOCK 14
+#define EXT4_NR_BLOCKS 15
+
+#define EXT4_GOOD_OLD_INODE_SIZE 128
+
+typedef struct _Ext4_I_OSD2_Linux {
+ UINT16 l_i_blocks_high;
+ UINT16 l_i_file_acl_high;
+ UINT16 l_i_uid_high;
+ UINT16 l_i_gid_high;
+ UINT16 l_i_checksum_lo;
+ UINT16 l_i_reserved;
+} EXT4_OSD2_LINUX;
+
+typedef struct _Ext4_I_OSD2_Hurd {
+ UINT16 h_i_reserved1;
+ UINT16 h_i_mode_high;
+ UINT16 h_i_uid_high;
+ UINT16 h_i_gid_high;
+ UINT32 h_i_author;
+} EXT4_OSD2_HURD;
+
+typedef union {
+ // Note: Toolchain-specific defines (such as "linux") stops us from using simpler names down here.
+ EXT4_OSD2_LINUX data_linux;
+ EXT4_OSD2_HURD data_hurd;
+} EXT4_OSD2;
+
+typedef struct _Ext4Inode {
+ UINT16 i_mode;
+ UINT16 i_uid;
+ UINT32 i_size_lo;
+ UINT32 i_atime;
+ UINT32 i_ctime;
+ UINT32 i_mtime;
+ UINT32 i_dtime;
+ UINT16 i_gid;
+ UINT16 i_links;
+ UINT32 i_blocks;
+ UINT32 i_flags;
+ UINT32 i_os_spec;
+ UINT32 i_data[EXT4_NR_BLOCKS];
+ UINT32 i_generation;
+ UINT32 i_file_acl;
+ UINT32 i_size_hi;
+ UINT32 i_faddr;
+
+ EXT4_OSD2 i_osd2;
+
+ UINT16 i_extra_isize;
+ UINT16 i_checksum_hi;
+ UINT32 i_ctime_extra;
+ UINT32 i_mtime_extra;
+ UINT32 i_atime_extra;
+ UINT32 i_crtime;
+ UINT32 i_crtime_extra;
+ UINT32 i_version_hi;
+ UINT32 i_projid;
+} EXT4_INODE;
+
+typedef struct {
+ UINT32 inode;
+ UINT16 rec_len;
+ UINT8 name_len;
+ UINT8 file_type;
+ CHAR8 name[255];
+} EXT4_DIR_ENTRY;
+
+#define EXT4_MIN_DIR_ENTRY_LEN 8
+
+// This on-disk structure is present at the bottom of the extent tree
+typedef struct {
+ // First logical block
+ UINT32 ee_block;
+ // Length of the extent, in blocks
+ UINT16 ee_len;
+ // The physical (filesystem-relative) block is split between the high 16 bits
+ // and the low 32 bits - this forms a 48-bit block number
+ UINT16 ee_start_hi;
+ UINT32 ee_start_lo;
+} EXT4_EXTENT;
+
+// This on-disk structure is present at all levels except the bottom
+typedef struct {
+ // This index covers logical blocks from 'ei_block'
+ UINT32 ei_block;
+ // Block of the next level of the extent tree, similarly split in a high and low portion.
+ UINT32 ei_leaf_lo;
+ UINT16 ei_leaf_hi;
+
+ UINT16 ei_unused;
+} EXT4_EXTENT_INDEX;
+
+typedef struct {
+ // Needs to be EXT4_EXTENT_HEADER_MAGIC
+ UINT16 eh_magic;
+ // Number of entries
+ UINT16 eh_entries;
+ // Maximum number of entries that could follow this header
+ UINT16 eh_max;
+ // Depth of this node in the tree - the tree can be at most 5 levels deep
+ UINT16 eh_depth;
+ // Unused by standard ext4
+ UINT32 eh_generation;
+} EXT4_EXTENT_HEADER;
+
+#define EXT4_EXTENT_HEADER_MAGIC 0xF30A
+
+// Specified by ext4 docs and backed by a bunch of math
+#define EXT4_EXTENT_TREE_MAX_DEPTH 5
+
+typedef struct {
+ // CRC32C of UUID + inode number + igeneration + extent block
+ UINT32 eb_checksum;
+} EXT4_EXTENT_TAIL;
+
+typedef UINT64 EXT4_BLOCK_NR;
+typedef UINT32 EXT4_INO_NR;
+
+#endif
diff --git a/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.c b/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.c
new file mode 100644
index 0000000000..71360ea894
--- /dev/null
+++ b/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.c
@@ -0,0 +1,808 @@
+/** @file
+ Driver entry point
+
+ Copyright (c) 2021 Pedro Falcato All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "Ext4Dxe.h"
+#include "Uefi/UefiBaseType.h"
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mExt4DriverNameTable[] = {
+ {
+ "eng;en",
+ L"Ext4 File System Driver"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mExt4ControllerNameTable[] = {
+ {
+ "eng;en",
+ L"Ext4 File System"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+// Needed by gExt4ComponentName*
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the EFI Driver.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ @param[in] Language A pointer to a three-character ISO 639-2 language identifier.
+ This is the language of the driver name that that the caller
+ is requesting, and it must match one of the languages specified
+ in SupportedLanguages. The number of languages supported by a
+ driver is up to the driver writer.
+ @param[out] DriverName A pointer to the Unicode string to return. This Unicode string
+ is the name of the driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by This
+ and the language specified by Language was returned
+ in DriverName.
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.
+ @retval EFI_UNSUPPORTED The driver specified by This does not support the
+ language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+Ext4ComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+/**
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by an EFI Driver.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of a controller that the driver specified by
+ This is managing. This handle specifies the controller
+ whose name is to be returned.
+ @param[in] ChildHandle The handle of the child controller to retrieve the name
+ of. This is an optional parameter that may be NULL. It
+ will be NULL for device drivers. It will also be NULL
+ for a bus drivers that wish to retrieve the name of the
+ bus controller. It will not be NULL for a bus driver
+ that wishes to retrieve the name of a child controller.
+ @param[in] Language A pointer to a three character ISO 639-2 language
+ identifier. This is the language of the controller name
+ that the caller is requesting, and it must match one
+ of the languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up to the
+ driver writer.
+ @param[out] ControllerName A pointer to the Unicode string to return. This Unicode
+ string is the name of the controller specified by
+ ControllerHandle and ChildHandle in the language specified
+ by Language, from the point of view of the driver specified
+ by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in the
+ language specified by Language for the driver
+ specified by This was returned in DriverName.
+ @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid EFI_HANDLE.
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently managing
+ the controller specified by ControllerHandle and
+ ChildHandle.
+ @retval EFI_UNSUPPORTED The driver specified by This does not support the
+ language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+Ext4ComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+extern EFI_COMPONENT_NAME_PROTOCOL gExt4ComponentName;
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gExt4ComponentName = {
+ Ext4ComponentNameGetDriverName,
+ Ext4ComponentNameGetControllerName,
+ "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gExt4ComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME)Ext4ComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME)Ext4ComponentNameGetControllerName,
+ "en"
+};
+
+// Needed by gExt4BindingProtocol
+
+/**
+ Tests to see if this driver supports a given controller. If a child device is provided,
+ it further tests to see if this driver supports creating a handle for the specified child device.
+
+ This function checks to see if the driver specified by This supports the device specified by
+ ControllerHandle. Drivers will typically use the device path attached to
+ ControllerHandle and/or the services from the bus I/O abstraction attached to
+ ControllerHandle to determine if the driver supports ControllerHandle. This function
+ may be called many times during platform initialization. In order to reduce boot times, the tests
+ performed by this function must be very small, and take as little time as possible to execute. This
+ function must not change the state of any hardware devices, and this function must be aware that the
+ device specified by ControllerHandle may already be managed by the same driver or a
+ different driver. This function must match its calls to AllocatePages() with FreePages(),
+ AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol().
+ Because ControllerHandle may have been previously started by the same driver, if a protocol is
+ already in the opened state, then it must not be closed with CloseProtocol(). This is required
+ to guarantee the state of ControllerHandle is not modified by this function.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to test. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For bus drivers, if this parameter is not NULL, then
+ the bus driver must determine if the bus controller specified
+ by ControllerHandle and the child controller specified
+ by RemainingDevicePath are both supported by this
+ bus driver.
+
+ @retval EFI_SUCCESS The device specified by ControllerHandle and
+ RemainingDevicePath is supported by the driver specified by This.
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by the driver
+ specified by This.
+ @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by a different
+ driver or an application that requires exclusive access.
+ Currently not implemented.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
+ RemainingDevicePath is not supported by the driver specified by This.
+**/
+EFI_STATUS
+EFIAPI
+Ext4IsBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *BindingProtocol,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Starts a device controller or a bus controller.
+
+ The Start() function is designed to be invoked from the EFI boot service ConnectController().
+ As a result, much of the error checking on the parameters to Start() has been moved into this
+ common boot service. It is legal to call Start() from other locations,
+ but the following calling restrictions must be followed, or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE.
+ 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned
+ EFI_DEVICE_PATH_PROTOCOL.
+ 3. Prior to calling Start(), the Supported() function for the driver specified by This must
+ have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to start. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For a bus driver, if this parameter is NULL, then handles
+ for all the children of Controller are created by this driver.
+ If this parameter is not NULL and the first Device Path Node is
+ not the End of Device Path Node, then only the handle for the
+ child device specified by the first Device Path Node of
+ RemainingDevicePath is created by this driver.
+ If the first Device Path Node of RemainingDevicePath is
+ the End of Device Path Node, no child handle is created by this
+ driver.
+
+ @retval EFI_SUCCESS The device was started.
+ @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+ @retval Others The driver failded to start the device.
+
+**/
+EFI_STATUS
+EFIAPI
+Ext4Bind (
+ IN EFI_DRIVER_BINDING_PROTOCOL *BindingProtocol,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Stops a device controller or a bus controller.
+
+ The Stop() function is designed to be invoked from the EFI boot service DisconnectController().
+ As a result, much of the error checking on the parameters to Stop() has been moved
+ into this common boot service. It is legal to call Stop() from other locations,
+ but the following calling restrictions must be followed, or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this
+ same driver's Start() function.
+ 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid
+ EFI_HANDLE. In addition, all of these handles must have been created in this driver's
+ Start() function, and the Start() function must have called OpenProtocol() on
+ ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle A handle to the device being stopped. The handle must
+ support a bus specific I/O protocol for the driver
+ to use to stop the device.
+ @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer.
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL
+ if NumberOfChildren is 0.
+
+ @retval EFI_SUCCESS The device was stopped.
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+Ext4Stop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ );
+
+EFI_DRIVER_BINDING_PROTOCOL gExt4BindingProtocol =
+{
+ Ext4IsBindingSupported,
+ Ext4Bind,
+ Ext4Stop,
+ EXT4_DRIVER_VERSION
+};
+
+/**
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by an EFI Driver.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of a controller that the driver specified by
+ This is managing. This handle specifies the controller
+ whose name is to be returned.
+ @param[in] ChildHandle The handle of the child controller to retrieve the name
+ of. This is an optional parameter that may be NULL. It
+ will be NULL for device drivers. It will also be NULL
+ for a bus drivers that wish to retrieve the name of the
+ bus controller. It will not be NULL for a bus driver
+ that wishes to retrieve the name of a child controller.
+ @param[in] Language A pointer to a three character ISO 639-2 language
+ identifier. This is the language of the controller name
+ that the caller is requesting, and it must match one
+ of the languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up to the
+ driver writer.
+ @param[out] ControllerName A pointer to the Unicode string to return. This Unicode
+ string is the name of the controller specified by
+ ControllerHandle and ChildHandle in the language specified
+ by Language, from the point of view of the driver specified
+ by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in the
+ language specified by Language for the driver
+ specified by This was returned in DriverName.
+ @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid EFI_HANDLE.
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently managing
+ the controller specified by ControllerHandle and
+ ChildHandle.
+ @retval EFI_UNSUPPORTED The driver specified by This does not support the
+ language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+Ext4ComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ EFI_STATUS Status;
+
+ if (ChildHandle != NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ // Test if the driver manages ControllHandle
+ Status = EfiTestManagedDevice (
+ ControllerHandle,
+ gExt4BindingProtocol.DriverBindingHandle,
+ &gEfiDiskIoProtocolGuid
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mExt4ControllerNameTable,
+ ControllerName,
+ (BOOLEAN)(This == &gExt4ComponentName)
+ );
+}
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the EFI Driver.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ @param[in] Language A pointer to a three-character ISO 639-2 language identifier.
+ This is the language of the driver name that that the caller
+ is requesting, and it must match one of the languages specified
+ in SupportedLanguages. The number of languages supported by a
+ driver is up to the driver writer.
+ @param[out] DriverName A pointer to the Unicode string to return. This Unicode string
+ is the name of the driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by This
+ and the language specified by Language was returned
+ in DriverName.
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.
+ @retval EFI_UNSUPPORTED The driver specified by This does not support the
+ language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+Ext4ComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mExt4DriverNameTable,
+ DriverName,
+ (BOOLEAN)(This == &gExt4ComponentName)
+ );
+}
+
+/**
+ Stops a device controller or a bus controller.
+
+ The Stop() function is designed to be invoked from the EFI boot service DisconnectController().
+ As a result, much of the error checking on the parameters to Stop() has been moved
+ into this common boot service. It is legal to call Stop() from other locations,
+ but the following calling restrictions must be followed, or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this
+ same driver's Start() function.
+ 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid
+ EFI_HANDLE. In addition, all of these handles must have been created in this driver's
+ Start() function, and the Start() function must have called OpenProtocol() on
+ ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle A handle to the device being stopped. The handle must
+ support a bus specific I/O protocol for the driver
+ to use to stop the device.
+ @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer.
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL
+ if NumberOfChildren is 0.
+
+ @retval EFI_SUCCESS The device was stopped.
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+Ext4Stop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Sfs;
+ EXT4_PARTITION *Partition;
+ BOOLEAN HasDiskIo2;
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiSimpleFileSystemProtocolGuid,
+ (VOID **)&Sfs,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Partition = (EXT4_PARTITION *)Sfs;
+
+ HasDiskIo2 = EXT4_DISK_IO2 (Partition) != NULL;
+
+ Status = Ext4UnmountAndFreePartition (Partition);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ ControllerHandle,
+ &gEfiSimpleFileSystemProtocolGuid,
+ &Partition->Interface,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ // Close all open protocols (DiskIo, DiskIo2, BlockIo)
+
+ Status = gBS->CloseProtocol (
+ ControllerHandle,
+ &gEfiDiskIoProtocolGuid,
+ This->DriverBindingHandle,
+ ControllerHandle
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->CloseProtocol (
+ ControllerHandle,
+ &gEfiBlockIoProtocolGuid,
+ This->DriverBindingHandle,
+ ControllerHandle
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (HasDiskIo2) {
+ Status = gBS->CloseProtocol (
+ ControllerHandle,
+ &gEfiDiskIo2ProtocolGuid,
+ This->DriverBindingHandle,
+ ControllerHandle
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Ext4Dxe Driver's entry point.
+
+ Called at load time.
+
+ @param[in] ImageHandle Handle to the image.
+ @param[in] SystemTable Pointer to the EFI_SYSTEM_TABLE.
+ @return Result of the load.
+**/
+EFI_STATUS
+EFIAPI
+Ext4EntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ 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);
+}
+
+/**
+ Ext4Dxe Driver's unload callback.
+
+ Called at unload time.
+
+ @param[in] ImageHandle Handle to the image.
+ @return Result of the unload.
+**/
+EFI_STATUS
+EFIAPI
+Ext4Unload (
+ IN EFI_HANDLE ImageHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE *DeviceHandleBuffer;
+ UINTN DeviceHandleCount;
+ UINTN Index;
+ EFI_HANDLE Handle;
+
+ Status = gBS->LocateHandleBuffer (
+ AllHandles,
+ NULL,
+ NULL,
+ &DeviceHandleCount,
+ &DeviceHandleBuffer
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ for (Index = 0; Index < DeviceHandleCount; Index++) {
+ Handle = DeviceHandleBuffer[Index];
+
+ Status = EfiTestManagedDevice (Handle, ImageHandle, &gEfiDiskIoProtocolGuid);
+
+ if (Status == EFI_SUCCESS) {
+ Status = gBS->DisconnectController (Handle, ImageHandle, NULL);
+
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ }
+ }
+
+ FreePool (DeviceHandleBuffer);
+
+ Status = EfiLibUninstallAllDriverProtocols2 (
+ &gExt4BindingProtocol,
+ &gExt4ComponentName,
+ &gExt4ComponentName2,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ );
+
+ return Status;
+}
+
+/**
+ Tests to see if this driver supports a given controller. If a child device is provided,
+ it further tests to see if this driver supports creating a handle for the specified child device.
+
+ This function checks to see if the driver specified by This supports the device specified by
+ ControllerHandle. Drivers will typically use the device path attached to
+ ControllerHandle and/or the services from the bus I/O abstraction attached to
+ ControllerHandle to determine if the driver supports ControllerHandle. This function
+ may be called many times during platform initialization. In order to reduce boot times, the tests
+ performed by this function must be very small, and take as little time as possible to execute. This
+ function must not change the state of any hardware devices, and this function must be aware that the
+ device specified by ControllerHandle may already be managed by the same driver or a
+ different driver. This function must match its calls to AllocatePages() with FreePages(),
+ AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol().
+ Because ControllerHandle may have been previously started by the same driver, if a protocol is
+ already in the opened state, then it must not be closed with CloseProtocol(). This is required
+ to guarantee the state of ControllerHandle is not modified by this function.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to test. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For bus drivers, if this parameter is not NULL, then
+ the bus driver must determine if the bus controller specified
+ by ControllerHandle and the child controller specified
+ by RemainingDevicePath are both supported by this
+ bus driver.
+
+ @retval EFI_SUCCESS The device specified by ControllerHandle and
+ RemainingDevicePath is supported by the driver specified by This.
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by the driver
+ specified by This.
+ @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by a different
+ driver or an application that requires exclusive access.
+ Currently not implemented.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
+ RemainingDevicePath is not supported by the driver specified by This.
+**/
+EFI_STATUS
+EFIAPI
+Ext4IsBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *BindingProtocol,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH *RemainingDevicePath OPTIONAL
+ )
+{
+ // Note to self: EFI_OPEN_PROTOCOL_TEST_PROTOCOL lets us not close the
+ // protocol and ignore the output argument entirely
+
+ EFI_STATUS Status;
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiDiskIoProtocolGuid,
+ NULL,
+ BindingProtocol->ImageHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiBlockIoProtocolGuid,
+ NULL,
+ BindingProtocol->ImageHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ return Status;
+}
+
+/**
+ Starts a device controller or a bus controller.
+
+ The Start() function is designed to be invoked from the EFI boot service ConnectController().
+ As a result, much of the error checking on the parameters to Start() has been moved into this
+ common boot service. It is legal to call Start() from other locations,
+ but the following calling restrictions must be followed, or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE.
+ 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned
+ EFI_DEVICE_PATH_PROTOCOL.
+ 3. Prior to calling Start(), the Supported() function for the driver specified by This must
+ have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to start. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For a bus driver, if this parameter is NULL, then handles
+ for all the children of Controller are created by this driver.
+ If this parameter is not NULL and the first Device Path Node is
+ not the End of Device Path Node, then only the handle for the
+ child device specified by the first Device Path Node of
+ RemainingDevicePath is created by this driver.
+ If the first Device Path Node of RemainingDevicePath is
+ the End of Device Path Node, no child handle is created by this
+ driver.
+
+ @retval EFI_SUCCESS The device was started.
+ @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+ @retval Others The driver failded to start the device.
+
+**/
+EFI_STATUS
+EFIAPI
+Ext4Bind (
+ IN EFI_DRIVER_BINDING_PROTOCOL *BindingProtocol,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_DISK_IO_PROTOCOL *DiskIo;
+ EFI_DISK_IO2_PROTOCOL *DiskIo2;
+ EFI_BLOCK_IO_PROTOCOL *BlockIo;
+ EFI_STATUS Status;
+
+ DiskIo2 = NULL;
+ BlockIo = NULL;
+ DiskIo = NULL;
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiDiskIoProtocolGuid,
+ (VOID **)&DiskIo,
+ BindingProtocol->ImageHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+
+ DEBUG ((DEBUG_INFO, "[Ext4] Controller supports DISK_IO\n"));
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiDiskIo2ProtocolGuid,
+ (VOID **)&DiskIo2,
+ BindingProtocol->ImageHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ // It's okay to not support DISK_IO2
+
+ if (DiskIo2 != NULL) {
+ DEBUG ((DEBUG_INFO, "[Ext4] Controller supports DISK_IO2\n"));
+ }
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiBlockIoProtocolGuid,
+ (VOID **)&BlockIo,
+ BindingProtocol->ImageHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+
+ Status = Ext4OpenPartition (ControllerHandle, DiskIo, DiskIo2, BlockIo);
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "[ext4] Error mounting: %r\n", Status));
+ // Falls through to Error
+ } else {
+ return Status;
+ }
+
+Error:
+ if (DiskIo) {
+ gBS->CloseProtocol (
+ ControllerHandle,
+ &gEfiDiskIoProtocolGuid,
+ BindingProtocol->ImageHandle,
+ ControllerHandle
+ );
+ }
+
+ if (DiskIo2) {
+ gBS->CloseProtocol (
+ ControllerHandle,
+ &gEfiDiskIo2ProtocolGuid,
+ BindingProtocol->ImageHandle,
+ ControllerHandle
+ );
+ }
+
+ if (BlockIo) {
+ gBS->CloseProtocol (
+ ControllerHandle,
+ &gEfiBlockIoProtocolGuid,
+ BindingProtocol->ImageHandle,
+ ControllerHandle
+ );
+ }
+
+ return Status;
+}
diff --git a/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.h b/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.h
new file mode 100644
index 0000000000..93f0a8a04a
--- /dev/null
+++ b/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.h
@@ -0,0 +1,1038 @@
+/** @file
+ Common header for the driver
+
+ Copyright (c) 2021 Pedro Falcato All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef EXT4_H_
+#define EXT4_H_
+
+#include <Uefi.h>
+
+#include <Guid/FileInfo.h>
+#include <Guid/FileSystemInfo.h>
+#include <Guid/FileSystemVolumeLabelInfo.h>
+#include <Protocol/BlockIo.h>
+#include <Protocol/DiskIo.h>
+#include <Protocol/DiskIo2.h>
+#include <Protocol/SimpleFileSystem.h>
+
+#include <Library/PcdLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/OrderedCollectionLib.h>
+
+#include "Ext4Disk.h"
+
+#define EXT4_NAME_MAX 255
+
+#define EXT4_DRIVER_VERSION 0x0000
+
+/**
+ Opens an ext4 partition and installs the Simple File System protocol.
+
+ @param[in] DeviceHandle Handle to the block device.
+ @param[in] DiskIo Pointer to an EFI_DISK_IO_PROTOCOL.
+ @param[in opt] DiskIo2 Pointer to an EFI_DISK_IO2_PROTOCOL, if supported.
+ @param[in] BlockIo Pointer to an EFI_BLOCK_IO_PROTOCOL.
+
+ @retval EFI_SUCCESS The opening was successful.
+ !EFI_SUCCESS Opening failed.
+**/
+EFI_STATUS
+Ext4OpenPartition (
+ IN EFI_HANDLE DeviceHandle,
+ IN EFI_DISK_IO_PROTOCOL *DiskIo,
+ IN OPTIONAL EFI_DISK_IO2_PROTOCOL *DiskIo2,
+ IN EFI_BLOCK_IO_PROTOCOL *BlockIo
+ );
+
+typedef struct _Ext4File EXT4_FILE;
+
+typedef struct _Ext4_PARTITION {
+ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL Interface;
+ EFI_DISK_IO_PROTOCOL *DiskIo;
+ EFI_DISK_IO2_PROTOCOL *DiskIo2;
+ EFI_BLOCK_IO_PROTOCOL *BlockIo;
+
+ EXT4_SUPERBLOCK SuperBlock;
+ BOOLEAN Unmounting;
+
+ UINT32 FeaturesIncompat;
+ UINT32 FeaturesCompat;
+ UINT32 FeaturesRoCompat;
+ UINT32 InodeSize;
+ UINT32 BlockSize;
+ BOOLEAN ReadOnly;
+ UINT64 NumberBlockGroups;
+ EXT4_BLOCK_NR NumberBlocks;
+
+ EXT4_BLOCK_GROUP_DESC *BlockGroups;
+ UINT32 DescSize;
+ EXT4_FILE *Root;
+
+ UINT32 InitialSeed;
+
+ LIST_ENTRY OpenFiles;
+} EXT4_PARTITION;
+
+/**
+ 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
+ valid ext4 partition.
+**/
+EFI_STATUS
+Ext4OpenSuperblock (
+ OUT EXT4_PARTITION *Partition
+ );
+
+/**
+ Retrieves the EFI_BLOCK_IO_PROTOCOL of the partition.
+
+ @param[in] Partition Pointer to the opened ext4 partition.
+ @return The Block IO protocol associated with the partition.
+**/
+#define EXT4_BLOCK_IO(Partition) Partition->BlockIo
+
+/**
+ Retrieves the EFI_DISK_IO_PROTOCOL of the partition.
+
+ @param[in] Partition Pointer to the opened ext4 partition.
+ @return The Disk IO protocol associated with the partition.
+**/
+#define EXT4_DISK_IO(Partition) Partition->DiskIo
+
+/**
+ Retrieves the EFI_DISK_IO2_PROTOCOL of the partition.
+
+ @param[in] Partition Pointer to the opened ext4 partition.
+ @return The Disk IO 2 protocol associated with the partition, or NULL if
+ not supported.
+**/
+#define EXT4_DISK_IO2(Partition) Partition->DiskIo2
+
+/**
+ Retrieves the media ID of the partition.
+
+ @param[in] Partition Pointer to the opened ext4 partition.
+ @return The media ID associated with the partition.
+**/
+#define EXT4_MEDIA_ID(Partition) Partition->BlockIo->Media->MediaId
+
+/**
+ Reads from the partition's disk using the DISK_IO protocol.
+
+ @param[in] Partition Pointer to the opened ext4 partition.
+ @param[out] Buffer Pointer to a destination buffer.
+ @param[in] Length Length of the destination buffer.
+ @param[in] Offset Offset, in bytes, of the location to read.
+
+ @return Success status of the disk read.
+**/
+EFI_STATUS
+Ext4ReadDiskIo (
+ IN EXT4_PARTITION *Partition,
+ OUT VOID *Buffer,
+ IN UINTN Length,
+ IN UINT64 Offset
+ );
+
+/**
+ Reads blocks from the partition's disk using the DISK_IO protocol.
+
+ @param[in] Partition Pointer to the opened ext4 partition.
+ @param[out] Buffer Pointer to a destination buffer.
+ @param[in] NumberBlocks Length of the read, in filesystem blocks.
+ @param[in] BlockNumber Starting block number.
+
+ @return Success status of the read.
+**/
+EFI_STATUS
+Ext4ReadBlocks (
+ IN EXT4_PARTITION *Partition,
+ OUT VOID *Buffer,
+ IN UINTN NumberBlocks,
+ IN EXT4_BLOCK_NR BlockNumber
+ );
+
+/**
+ Allocates a buffer and reads blocks from the partition's disk using the DISK_IO protocol.
+ This function is deprecated and will be removed in the future.
+
+ @param[in] Partition Pointer to the opened ext4 partition.
+ @param[in] NumberBlocks Length of the read, in filesystem blocks.
+ @param[in] BlockNumber Starting block number.
+
+ @return Buffer allocated by AllocatePool, or NULL if some part of the process
+ failed.
+**/
+VOID *
+Ext4AllocAndReadBlocks (
+ IN EXT4_PARTITION *Partition,
+ IN UINTN NumberBlocks,
+ IN EXT4_BLOCK_NR BlockNumber
+ );
+
+/**
+ Checks if the opened partition has the 64-bit feature (see EXT4_FEATURE_INCOMPAT_64BIT).
+
+ @param[in] Partition Pointer to the opened ext4 partition.
+
+ @return TRUE if EXT4_FEATURE_INCOMPAT_64BIT is enabled, else FALSE.
+**/
+#define EXT4_IS_64_BIT(Partition) ((Partition->FeaturesIncompat & EXT4_FEATURE_INCOMPAT_64BIT) != 0)
+
+/**
+ Composes an EXT4_BLOCK_NR safely, from two halfs.
+
+ @param[in] Partition Pointer to the opened ext4 partition.
+ @param[in] Low Low half of the block number.
+ @param[in] High High half of the block number.
+
+ @return The block number formed by Low, and if 64 bit is enabled, High.
+**/
+#define EXT4_BLOCK_NR_FROM_HALFS(Partition, Low, High) \
+ EXT4_IS_64_BIT (Partition) ? (Low | LShiftU64 (High, 32)) : Low
+
+/**
+ Retrieves a block group descriptor of the ext4 filesystem.
+
+ @param[in] Partition Pointer to the opened ext4 partition.
+ @param[in] BlockGroup Block group number.
+
+ @return A pointer to the block group descriptor.
+**/
+EXT4_BLOCK_GROUP_DESC *
+Ext4GetBlockGroupDesc (
+ IN EXT4_PARTITION *Partition,
+ IN UINT32 BlockGroup
+ );
+
+/**
+ Reads an inode from disk.
+
+ @param[in] Partition Pointer to the opened partition.
+ @param[in] InodeNum Number of the desired Inode
+ @param[out] OutIno Pointer to where it will be stored a pointer to the read inode.
+
+ @return Status of the inode read.
+**/
+EFI_STATUS
+Ext4ReadInode (
+ IN EXT4_PARTITION *Partition,
+ IN EXT4_INO_NR InodeNum,
+ OUT EXT4_INODE **OutIno
+ );
+
+/**
+ Converts blocks to bytes.
+
+ @param[in] Partition Pointer to the opened partition.
+ @param[in] Block Block number/number of blocks.
+
+ @return The number of bytes.
+**/
+#define EXT4_BLOCK_TO_BYTES(Partition, Block) MultU64x32 (Block, Partition->BlockSize)
+
+/**
+ Reads from an EXT4 inode.
+ @param[in] Partition Pointer to the opened EXT4 partition.
+ @param[in] File Pointer to the opened file.
+ @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.
+
+ @return Status of the read operation.
+**/
+EFI_STATUS
+Ext4Read (
+ IN EXT4_PARTITION *Partition,
+ IN EXT4_FILE *File,
+ OUT VOID *Buffer,
+ IN UINT64 Offset,
+ IN OUT UINTN *Length
+ );
+
+/**
+ Retrieves the size of the inode.
+
+ @param[in] Inode Pointer to the ext4 inode.
+
+ @return The size of the inode, in bytes.
+**/
+#define EXT4_INODE_SIZE(Inode) (LShiftU64 (Inode->i_size_hi, 32) | Inode->i_size_lo)
+
+/**
+ Retrieves an extent from an EXT4 inode.
+ @param[in] Partition Pointer to the opened EXT4 partition.
+ @param[in] File Pointer to the opened file.
+ @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_NO_MAPPING Block has no mapping.
+**/
+EFI_STATUS
+Ext4GetExtent (
+ IN EXT4_PARTITION *Partition,
+ IN EXT4_FILE *File,
+ IN EXT4_BLOCK_NR LogicalBlock,
+ OUT EXT4_EXTENT *Extent
+ );
+
+struct _Ext4File {
+ EFI_FILE_PROTOCOL Protocol;
+ EXT4_INODE *Inode;
+ EXT4_INO_NR InodeNum;
+
+ UINT64 OpenMode;
+ UINT64 Position;
+
+ EXT4_PARTITION *Partition;
+ CHAR16 *FileName;
+
+ ORDERED_COLLECTION *ExtentsMap;
+
+ LIST_ENTRY OpenFilesListNode;
+};
+
+#define EXT4_FILE_FROM_OPEN_FILES_NODE(Node) BASE_CR (Node, EXT4_FILE, OpenFilesListNode)
+
+/**
+ Retrieves a directory entry.
+
+ @param[in] Directory Pointer to the opened directory.
+ @param[in] NameUnicode Pointer to the UCS-2 formatted filename.
+ @param[in] Partition Pointer to the ext4 partition.
+ @param[out] Result Pointer to the destination directory entry.
+
+ @return The result of the operation.
+**/
+EFI_STATUS
+Ext4RetrieveDirent (
+ IN EXT4_FILE *Directory,
+ IN CONST CHAR16 *NameUnicode,
+ IN EXT4_PARTITION *Partition,
+ OUT EXT4_DIR_ENTRY *Result
+ );
+
+/**
+ Opens a file.
+
+ @param[in] Directory Pointer to the opened directory.
+ @param[in] Name Pointer to the UCS-2 formatted filename.
+ @param[in] Partition Pointer to the ext4 partition.
+ @param[in] OpenMode Mode in which the file is supposed to be open.
+ @param[out] OutFile Pointer to the newly opened file.
+
+ @return Result of the operation.
+**/
+EFI_STATUS
+Ext4OpenFile (
+ IN EXT4_FILE *Directory,
+ IN CONST CHAR16 *Name,
+ IN EXT4_PARTITION *Partition,
+ IN UINT64 OpenMode,
+ OUT EXT4_FILE **OutFile
+ );
+
+/**
+ Opens a file using a directory entry.
+
+ @param[in] Partition Pointer to the ext4 partition.
+ @param[in] OpenMode Mode in which the file is supposed to be open.
+ @param[out] OutFile Pointer to the newly opened file.
+ @param[in] Entry Directory entry to be used.
+
+ @retval EFI_STATUS Result of the operation
+**/
+EFI_STATUS
+Ext4OpenDirent (
+ IN EXT4_PARTITION *Partition,
+ IN UINT64 OpenMode,
+ OUT EXT4_FILE **OutFile,
+ IN EXT4_DIR_ENTRY *Entry
+ );
+
+/**
+ Allocates a zeroed inode structure.
+ @param[in] Partition Pointer to the opened EXT4 partition.
+
+ @return Pointer to the allocated structure, from the pool,
+ with size Partition->InodeSize.
+**/
+EXT4_INODE *
+Ext4AllocateInode (
+ IN EXT4_PARTITION *Partition
+ );
+
+// Part of the EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
+
+/**
+ Open the root directory on a volume.
+
+ @param[in] This A pointer to the volume to open the root directory.
+ @param[out] Root A pointer to the location to return the opened file handle for the
+ root directory.
+
+ @retval EFI_SUCCESS The device was opened.
+ @retval EFI_UNSUPPORTED This volume does not support the requested file system type.
+ @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_ACCESS_DENIED The service denied access to the file.
+ @retval EFI_OUT_OF_RESOURCES The volume was not opened due to lack of resources.
+ @retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no
+ longer supported. Any existing file handles for this volume are
+ no longer valid. To access the files on the new medium, the
+ volume must be reopened with OpenVolume().
+
+**/
+EFI_STATUS
+EFIAPI
+Ext4OpenVolume (
+ IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This,
+ IN EFI_FILE_PROTOCOL **Root
+ );
+
+// End of EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
+
+/**
+ Sets up the protocol and metadata of a file that is being opened.
+
+ @param[in out] File Pointer to the file.
+ @param[in] Partition Pointer to the opened partition.
+**/
+VOID
+Ext4SetupFile (
+ IN OUT EXT4_FILE *File,
+ IN EXT4_PARTITION *Partition
+ );
+
+/**
+ 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
+ );
+
+// Part of the EFI_FILE_PROTOCOL
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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
+ );
+
+// EFI_FILE_PROTOCOL implementation ends here.
+
+/**
+ Checks if a file is a directory.
+ @param[in] File Pointer to the opened file.
+
+ @return TRUE if file is a directory.
+**/
+BOOLEAN
+Ext4FileIsDir (
+ IN CONST EXT4_FILE *File
+ );
+
+/**
+ Checks if a file is a regular file.
+ @param[in] File Pointer to the opened file.
+
+ @return BOOLEAN TRUE if file is a regular file.
+**/
+BOOLEAN
+Ext4FileIsReg (
+ IN CONST EXT4_FILE *File
+ );
+
+// In EFI we can't open FIFO pipes, UNIX sockets, character/block devices since these concepts are
+// at the kernel level and are OS dependent.
+
+/**
+ Checks if a file is openable.
+ @param[in] File Pointer to the file trying to be opened.
+
+
+ @return TRUE if file is openable. A file is considered openable if
+ it's a regular file or a directory, since most other file types
+ don't make sense under UEFI.
+**/
+#define Ext4FileIsOpenable(File) (Ext4FileIsReg (File) || Ext4FileIsDir (File))
+
+#define EXT4_INODE_HAS_FIELD(Inode, \
+ Field) (Inode->i_extra_isize + EXT4_GOOD_OLD_INODE_SIZE >= OFFSET_OF (EXT4_INODE, Field) + \
+ sizeof (((EXT4_INODE *)NULL)->Field))
+
+/**
+ Calculates the physical space used by a file.
+ @param[in] File Pointer to the opened file.
+
+ @return Physical space used by a file, in bytes.
+**/
+UINT64
+Ext4FilePhysicalSpace (
+ IN EXT4_FILE *File
+ );
+
+/**
+ Gets the file's last access time.
+ @param[in] File Pointer to the opened file.
+ @param[out] Time Pointer to an EFI_TIME structure.
+**/
+VOID
+Ext4FileATime (
+ IN EXT4_FILE *File,
+ OUT EFI_TIME *Time
+ );
+
+/**
+ Gets the file's last (data) modification time.
+ @param[in] File Pointer to the opened file.
+ @param[out] Time Pointer to an EFI_TIME structure.
+**/
+VOID
+Ext4FileMTime (
+ IN EXT4_FILE *File,
+ OUT EFI_TIME *Time
+ );
+
+/**
+ Gets the file's creation time, if possible.
+ @param[in] File Pointer to the opened file.
+ @param[out] Time Pointer to an EFI_TIME structure.
+ In the case where the the creation time isn't recorded,
+ Time is zeroed.
+**/
+VOID
+Ext4FileCreateTime (
+ IN EXT4_FILE *File,
+ OUT EFI_TIME *Time
+ );
+
+/**
+ Initialises Unicode collation, which is needed for case-insensitive string comparisons
+ within the driver (a good example of an application of this is filename comparison).
+
+ @param[in] DriverHandle Handle to the driver image.
+
+ @retval EFI_SUCCESS Unicode collation was successfully initialised.
+ @retval !EFI_SUCCESS Failure.
+**/
+EFI_STATUS
+Ext4InitialiseUnicodeCollation (
+ EFI_HANDLE DriverHandle
+ );
+
+/**
+ Does a case-insensitive string comparison. Refer to EFI_UNICODE_COLLATION_PROTOCOL's StriColl
+ for more details.
+
+ @param[in] Str1 Pointer to a null terminated string.
+ @param[in] Str2 Pointer to a null terminated string.
+
+ @retval 0 Str1 is equivalent to Str2.
+ @retval >0 Str1 is lexically greater than Str2.
+ @retval <0 Str1 is lexically less than Str2.
+**/
+INTN
+Ext4StrCmpInsensitive (
+ IN CHAR16 *Str1,
+ IN CHAR16 *Str2
+ );
+
+/**
+ 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.
+
+ @retval EFI_SUCCESS Unicode collation was successfully initialised.
+ @retval !EFI_SUCCESS Failure.
+**/
+EFI_STATUS
+Ext4GetUcs2DirentName (
+ IN EXT4_DIR_ENTRY *Entry,
+ OUT CHAR16 Ucs2FileName[EXT4_NAME_MAX + 1]
+ );
+
+/**
+ 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
+ );
+
+/**
+ Reads a directory entry.
+
+ @param[in] Partition Pointer to the ext4 partition.
+ @param[in] File Pointer to the open directory.
+ @param[out] Buffer Pointer to the output buffer.
+ @param[in] Offset Initial directory position.
+ @param[in out] OutLength Pointer to a UINTN that contains the length of the buffer,
+ and the length of the actual EFI_FILE_INFO after the call.
+
+ @return Result of the operation.
+**/
+EFI_STATUS
+Ext4ReadDir (
+ IN EXT4_PARTITION *Partition,
+ IN EXT4_FILE *File,
+ OUT VOID *Buffer,
+ IN UINT64 Offset,
+ IN OUT UINTN *OutLength
+ );
+
+/**
+ Initialises the (empty) extents map, that will work as a cache of extents.
+
+ @param[in] File Pointer to the open file.
+
+ @return Result of the operation.
+**/
+EFI_STATUS
+Ext4InitExtentsMap (
+ IN EXT4_FILE *File
+ );
+
+/**
+ Frees the extents map, deleting every extent stored.
+
+ @param[in] File Pointer to the open file.
+**/
+VOID
+Ext4FreeExtentsMap (
+ IN EXT4_FILE *File
+ );
+
+/**
+ Calculates the CRC32c checksum of the given buffer.
+
+ @param[in] Buffer Pointer to the buffer.
+ @param[in] Length Length of the buffer, in bytes.
+ @param[in] InitialValue Initial value of the CRC.
+
+ @return The CRC32c checksum.
+**/
+UINT32
+CalculateCrc32c (
+ IN CONST VOID *Buffer,
+ IN UINTN Length,
+ IN UINT32 InitialValue
+ );
+
+/**
+ Calculates the CRC16 checksum of the given buffer.
+
+ @param[in] Buffer Pointer to the buffer.
+ @param[in] Length Length of the buffer, in bytes.
+ @param[in] InitialValue Initial value of the CRC.
+
+ @return The CRC16 checksum.
+**/
+UINT16
+CalculateCrc16 (
+ IN CONST VOID *Buffer,
+ IN UINTN Length,
+ IN UINT16 InitialValue
+ );
+
+/**
+ Calculates the checksum of the given buffer.
+ @param[in] Partition Pointer to the opened EXT4 partition.
+ @param[in] Buffer Pointer to the buffer.
+ @param[in] Length Length of the buffer, in bytes.
+ @param[in] InitialValue Initial value of the CRC.
+
+ @return The checksum.
+**/
+UINT32
+Ext4CalculateChecksum (
+ IN CONST EXT4_PARTITION *Partition,
+ IN CONST VOID *Buffer,
+ IN UINTN Length,
+ IN UINT32 InitialValue
+ );
+
+/**
+ Calculates the checksum of the given inode.
+ @param[in] Partition Pointer to the opened EXT4 partition.
+ @param[in] Inode Pointer to the inode.
+ @param[in] InodeNum Inode number.
+
+ @return The checksum.
+**/
+UINT32
+Ext4CalculateInodeChecksum (
+ IN CONST EXT4_PARTITION *Partition,
+ IN CONST EXT4_INODE *Inode,
+ IN EXT4_INO_NR InodeNum
+ );
+
+/**
+ Checks if the checksum of the inode is correct.
+ @param[in] Partition Pointer to the opened EXT4 partition.
+ @param[in] Inode Pointer to the inode.
+ @param[in] InodeNum Inode number.
+
+ @return TRUE if checksum is correct, FALSE if there is corruption.
+**/
+BOOLEAN
+Ext4CheckInodeChecksum (
+ IN CONST EXT4_PARTITION *Partition,
+ IN CONST EXT4_INODE *Inode,
+ IN EXT4_INO_NR InodeNum
+ );
+
+/**
+ Unmounts and frees an ext4 partition.
+
+ @param[in] Partition Pointer to the opened partition.
+
+ @return Status of the unmount.
+**/
+EFI_STATUS
+Ext4UnmountAndFreePartition (
+ IN EXT4_PARTITION *Partition
+ );
+
+/**
+ Checks if the checksum of the block group descriptor is correct.
+ @param[in] Partition Pointer to the opened EXT4 partition.
+ @param[in] BlockGroupDesc Pointer to the block group descriptor.
+ @param[in] BlockGroupNum Number of the block group.
+
+ @return TRUE if checksum is correct, FALSE if there is corruption.
+**/
+BOOLEAN
+Ext4VerifyBlockGroupDescChecksum (
+ IN CONST EXT4_PARTITION *Partition,
+ IN CONST EXT4_BLOCK_GROUP_DESC *BlockGroupDesc,
+ IN UINT32 BlockGroupNum
+ );
+
+/**
+ Calculates the checksum of the block group descriptor.
+ @param[in] Partition Pointer to the opened EXT4 partition.
+ @param[in] BlockGroupDesc Pointer to the block group descriptor.
+ @param[in] BlockGroupNum Number of the block group.
+
+ @return The checksum.
+**/
+UINT16
+Ext4CalculateBlockGroupDescChecksum (
+ IN CONST EXT4_PARTITION *Partition,
+ IN CONST EXT4_BLOCK_GROUP_DESC *BlockGroupDesc,
+ IN UINT32 BlockGroupNum
+ );
+
+/**
+ Verifies the existance of a particular RO compat feature set.
+ @param[in] Partition Pointer to the opened EXT4 partition.
+ @param[in] RoCompatFeatureSet Feature set to test.
+
+ @return TRUE if all features are supported, else FALSE.
+**/
+#define EXT4_HAS_RO_COMPAT(Partition, RoCompatFeatureSet) \
+ ((Partition->FeaturesRoCompat & RoCompatFeatureSet) == RoCompatFeatureSet)
+
+/**
+ Verifies the existance of a particular compat feature set.
+ @param[in] Partition Pointer to the opened EXT4 partition.
+ @param[in] CompatFeatureSet Feature set to test.
+
+ @return TRUE if all features are supported, else FALSE.
+**/
+#define EXT4_HAS_COMPAT(Partition, CompatFeatureSet) \
+ ((Partition->FeaturesCompat & CompatFeatureSet) == CompatFeatureSet)
+
+/**
+ Verifies the existance of a particular compat feature set.
+ @param[in] Partition Pointer to the opened EXT4 partition.
+ @param[in] IncompatFeatureSet Feature set to test.
+
+ @return TRUE if all features are supported, else FALSE.
+**/
+#define EXT4_HAS_INCOMPAT(Partition, IncompatFeatureSet) \
+ ((Partition->FeaturesIncompat & IncompatFeatureSet) == IncompatFeatureSet)
+
+// Note: Might be a good idea to provide generic Ext4Has$feature() through macros.
+
+/**
+ Checks if metadata_csum is enabled on the partition.
+ @param[in] Partition Pointer to the opened EXT4 partition.
+
+ @return TRUE if the metadata_csum is supported, else FALSE.
+**/
+#define EXT4_HAS_METADATA_CSUM(Partition) \
+ EXT4_HAS_RO_COMPAT (Partition, EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)
+
+/**
+ Checks if gdt_csum is enabled on the partition.
+ @param[in] Partition Pointer to the opened EXT4 partition.
+
+ @return TRUE if the gdt_csum is supported, else FALSE.
+**/
+#define EXT4_HAS_GDT_CSUM(Partition) \
+ EXT4_HAS_RO_COMPAT (Partition, EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)
+
+#endif
diff --git a/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.inf b/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.inf
new file mode 100644
index 0000000000..6533de6247
--- /dev/null
+++ b/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.inf
@@ -0,0 +1,149 @@
+## @file
+# Ext4 Package
+#
+# UEFI Driver that produces the Simple File System Protocol for a partition that is formatted
+# with the EXT4 file system.
+# More details are available at: https://www.kernel.org/doc/html/v5.4/filesystems/ext4/index.html
+#
+# Copyright (c) 2021 Pedro Falcato
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+# Layout of an EXT2/3/4 filesystem:
+# (note: this driver has been developed using
+# https://www.kernel.org/doc/html/latest/filesystems/ext4/index.html as
+# documentation).
+#
+# An ext2/3/4 filesystem (here on out referred to as simply an ext4 filesystem,
+# due to the similarities) is composed of various concepts:
+#
+# 1) Superblock
+# The superblock is the structure near (1024 bytes offset from the start)
+# the start of the partition, and describes the filesystem in general.
+# Here, we get to know the size of the filesystem's blocks, which features
+# it supports or not, whether it's been cleanly unmounted, how many blocks
+# we have, etc.
+#
+# 2) Block groups
+# EXT4 filesystems are divided into block groups, and each block group covers
+# s_blocks_per_group(8 * Block Size) blocks. Each block group has an
+# associated block group descriptor; these are present directly after the
+# superblock. Each block group descriptor contains the location of the
+# inode table, and the inode and block bitmaps (note these bitmaps are only
+# a block long, which gets us the 8 * Block Size formula covered previously).
+#
+# 3) Blocks
+# The ext4 filesystem is divided in blocks, of size s_log_block_size ^ 1024.
+# Blocks can be allocated using individual block groups's bitmaps. Note
+# that block 0 is invalid and its presence on extents/block tables means
+# it's part of a file hole, and that particular location must be read as
+# a block full of zeros.
+#
+# 4) Inodes
+# The ext4 filesystem divides files/directories into inodes (originally
+# index nodes). Each file/socket/symlink/directory/etc (here on out referred
+# to as a file, since there is no distinction under the ext4 filesystem) is
+# stored as a /nameless/ inode, that is stored in some block group's inode
+# table. Each inode has s_inode_size size (or GOOD_OLD_INODE_SIZE if it's
+# an old filesystem), and holds various metadata about the file. Since the
+# largest inode structure right now is ~160 bytes, the rest of the inode
+# contains inline extended attributes. Inodes' data is stored using either
+# data blocks (under ext2/3) or extents (under ext4).
+#
+# 5) Extents
+# Ext4 inodes store data in extents. These let N contiguous logical blocks
+# that are represented by N contiguous physical blocks be represented by a
+# single extent structure, which minimizes filesystem metadata bloat and
+# speeds up block mapping (particularly due to the fact that high-quality
+# ext4 implementations like linux's try /really/ hard to make the file
+# contiguous, so it's common to have files with almost 0 fragmentation).
+# Inodes that use extents store them in a tree, and the top of the tree
+# is stored on i_data. The tree's leaves always start with an
+# EXT4_EXTENT_HEADER and contain EXT4_EXTENT_INDEX on eh_depth != 0 and
+# EXT4_EXTENT on eh_depth = 0; these entries are always sorted by logical
+# block.
+#
+# 6) Directories
+# Ext4 directories are files that store name -> inode mappings for the
+# logical directory; this is where files get their names, which means ext4
+# inodes do not themselves have names, since they can be linked (present)
+# multiple times with different names. Directories can store entries in two
+# different ways:
+# 1) Classical linear directories: They store entries as a mostly-linked
+# mostly-list of EXT4_DIR_ENTRY.
+# 2) Hash tree directories: These are used for larger directories, with
+# hundreds of entries, and are designed in a backwards compatible way.
+# These are not yet implemented in the Ext4Dxe driver.
+#
+# 7) Journal
+# Ext3/4 filesystems have a journal to help protect the filesystem against
+# system crashes. This is not yet implemented in Ext4Dxe but is described
+# in detail in the Linux kernel's documentation.
+##
+
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = Ext4Dxe
+ MODULE_UNI_FILE = Ext4Dxe.uni
+ FILE_GUID = 75F2B676-D73B-45CB-B7C1-303C7F4E6FD6
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+
+ ENTRY_POINT = Ext4EntryPoint
+ UNLOAD_IMAGE = Ext4Unload
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC
+#
+
+[Sources]
+ Ext4Dxe.c
+ Partition.c
+ DiskUtil.c
+ Superblock.c
+ BlockGroup.c
+ Inode.c
+ Directory.c
+ Extents.c
+ File.c
+ Collation.c
+ Crc32c.c
+ Crc16.c
+ Ext4Disk.h
+ Ext4Dxe.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ RedfishPkg/RedfishPkg.dec
+
+[LibraryClasses]
+ UefiRuntimeServicesTableLib
+ UefiBootServicesTableLib
+ MemoryAllocationLib
+ BaseMemoryLib
+ BaseLib
+ UefiLib
+ UefiDriverEntryPoint
+ DebugLib
+ PcdLib
+ OrderedCollectionLib
+ BaseUcs2Utf8Lib
+
+[Guids]
+ gEfiFileInfoGuid ## SOMETIMES_CONSUMES ## UNDEFINED
+ gEfiFileSystemInfoGuid ## SOMETIMES_CONSUMES ## UNDEFINED
+ gEfiFileSystemVolumeLabelInfoIdGuid ## SOMETIMES_CONSUMES ## UNDEFINED
+
+[Protocols]
+ gEfiDiskIoProtocolGuid ## TO_START
+ gEfiDiskIo2ProtocolGuid ## TO_START
+ gEfiBlockIoProtocolGuid ## TO_START
+ gEfiSimpleFileSystemProtocolGuid ## BY_START
+ gEfiUnicodeCollationProtocolGuid ## TO_START
+ gEfiUnicodeCollation2ProtocolGuid ## TO_START
+
+[Pcd]
+ gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultLang ## SOMETIMES_CONSUMES
+ gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultPlatformLang ## SOMETIMES_CONSUMES
diff --git a/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.uni b/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.uni
new file mode 100644
index 0000000000..7476fbf9bd
--- /dev/null
+++ b/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.uni
@@ -0,0 +1,15 @@
+## @file
+# Ext4 Package
+#
+# UEFI Driver that produces the Simple File System Protocol for a partition that is formatted
+# with the EXT4 file system.
+# More details are available at: https://www.kernel.org/doc/html/v5.4/filesystems/ext4/index.html
+#
+# Copyright (c) 2021 Pedro Falcato
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+#string STR_MODULE_ABSTRACT #language en-US "UEFI driver for the EXT4 file system."
+
+#string STR_MODULE_DESCRIPTION #language en-US "Produces the EFI Simple File System protocol."
diff --git a/Features/Ext4Pkg/Ext4Dxe/Extents.c b/Features/Ext4Pkg/Ext4Dxe/Extents.c
new file mode 100644
index 0000000000..5fa2fe098d
--- /dev/null
+++ b/Features/Ext4Pkg/Ext4Dxe/Extents.c
@@ -0,0 +1,595 @@
+/** @file
+ Extent related routines
+
+ Copyright (c) 2021 Pedro Falcato All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "Ext4Dxe.h"
+
+/**
+ Checks if the checksum of the extent data block is correct.
+ @param[in] ExtHeader Pointer to the EXT4_EXTENT_HEADER.
+ @param[in] File Pointer to the file.
+
+ @return TRUE if the checksum is correct, FALSE if there is corruption.
+**/
+BOOLEAN
+Ext4CheckExtentChecksum (
+ IN CONST EXT4_EXTENT_HEADER *ExtHeader,
+ IN CONST EXT4_FILE *File
+ );
+
+/**
+ Calculates the checksum of the extent data block.
+ @param[in] ExtHeader Pointer to the EXT4_EXTENT_HEADER.
+ @param[in] File Pointer to the file.
+
+ @return The checksum.
+**/
+UINT32
+Ext4CalculateExtentChecksum (
+ IN CONST EXT4_EXTENT_HEADER *ExtHeader,
+ IN CONST EXT4_FILE *File
+ );
+
+/**
+ 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,
+ IN UINT16 NumberExtents
+ );
+
+/**
+ Gets an extent from the extents cache of the file.
+
+ @param[in] File Pointer to the open file.
+ @param[in] Block Block we want to grab.
+
+ @return Pointer to the extent, or NULL if it was not found.
+**/
+EXT4_EXTENT *
+Ext4GetExtentFromMap (
+ IN EXT4_FILE *File,
+ IN UINT32 Block
+ );
+
+/**
+ Retrieves the pointer to the top of the extent tree.
+ @param[in] Inode Pointer to the inode structure.
+
+ @return Pointer to an EXT4_EXTENT_HEADER. This pointer is inside
+ the inode and must not be freed.
+**/
+STATIC
+EXT4_EXTENT_HEADER *
+Ext4GetInoExtentHeader (
+ IN EXT4_INODE *Inode
+ )
+{
+ return (EXT4_EXTENT_HEADER *)Inode->i_data;
+}
+
+/**
+ Checks if an extent header is valid.
+ @param[in] Header Pointer to the EXT4_EXTENT_HEADER structure.
+
+ @return TRUE if valid, FALSE if not.
+**/
+STATIC
+BOOLEAN
+Ext4ExtentHeaderValid (
+ IN CONST EXT4_EXTENT_HEADER *Header
+ )
+{
+ if (Header->eh_depth > EXT4_EXTENT_TREE_MAX_DEPTH) {
+ DEBUG ((DEBUG_ERROR, "[ext4] Invalid extent header depth %u\n", Header->eh_depth));
+ return FALSE;
+ }
+
+ if (Header->eh_magic != EXT4_EXTENT_HEADER_MAGIC) {
+ DEBUG ((DEBUG_ERROR, "[ext4] Invalid extent header magic %x\n", Header->eh_magic));
+ return FALSE;
+ }
+
+ if (Header->eh_max < Header->eh_entries) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "[ext4] Invalid extent header num entries %u max entries %u\n",
+ Header->eh_entries,
+ Header->eh_max
+ ));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ Performs a binary search for a EXT4_EXTENT_INDEX that corresponds to a
+ logical block in a given extent tree node.
+
+ @param[in] Header Pointer to the EXT4_EXTENT_HEADER structure.
+ @param[in] LogicalBlock Block that will be searched
+
+ @return Pointer to the found EXT4_EXTENT_INDEX.
+**/
+STATIC
+EXT4_EXTENT_INDEX *
+Ext4BinsearchExtentIndex (
+ IN EXT4_EXTENT_HEADER *Header,
+ IN EXT4_BLOCK_NR LogicalBlock
+ )
+{
+ EXT4_EXTENT_INDEX *l;
+ EXT4_EXTENT_INDEX *r;
+ EXT4_EXTENT_INDEX *m;
+
+ l = ((EXT4_EXTENT_INDEX *)(Header + 1)) + 1;
+ r = ((EXT4_EXTENT_INDEX *)(Header + 1)) + Header->eh_entries - 1;
+
+ // Perform a mostly-standard binary search on the array
+ // This works very nicely because the extents arrays are always sorted.
+
+ while (l <= r) {
+ m = l + (r - l) / 2;
+
+ if (LogicalBlock < m->ei_block) {
+ r = m - 1;
+ } else {
+ l = m + 1;
+ }
+ }
+
+ return l - 1;
+}
+
+/**
+ Performs a binary search for a EXT4_EXTENT that corresponds to a
+ logical block in a given extent tree node.
+
+ @param[in] Header Pointer to the EXT4_EXTENT_HEADER structure.
+ @param[in] LogicalBlock Block that will be searched
+
+ @return Pointer to the found EXT4_EXTENT_INDEX, else NULL if the array is empty.
+ Note: The caller must check if the logical block
+ is actually mapped under the given extent.
+**/
+STATIC
+EXT4_EXTENT *
+Ext4BinsearchExtentExt (
+ IN EXT4_EXTENT_HEADER *Header,
+ IN EXT4_BLOCK_NR LogicalBlock
+ )
+{
+ EXT4_EXTENT *l;
+ EXT4_EXTENT *r;
+ EXT4_EXTENT *m;
+
+ l = ((EXT4_EXTENT *)(Header + 1)) + 1;
+ r = ((EXT4_EXTENT *)(Header + 1)) + Header->eh_entries - 1;
+ // Perform a mostly-standard binary search on the array
+ // This works very nicely because the extents arrays are always sorted.
+
+ // Empty array
+ if (Header->eh_entries == 0) {
+ return NULL;
+ }
+
+ while (l <= r) {
+ m = l + (r - l) / 2;
+
+ if (LogicalBlock < m->ee_block) {
+ r = m - 1;
+ } else {
+ l = m + 1;
+ }
+ }
+
+ return l - 1;
+}
+
+/**
+ Retrieves the leaf block from an EXT4_EXTENT_INDEX.
+
+ @param[in] Index Pointer to the EXT4_EXTENT_INDEX structure.
+
+ @return Block number of the leaf node.
+**/
+STATIC
+EXT4_BLOCK_NR
+Ext4ExtentIdxLeafBlock (
+ IN EXT4_EXTENT_INDEX *Index
+ )
+{
+ return LShiftU64 (Index->ei_leaf_hi, 32) | Index->ei_leaf_lo;
+}
+
+/**
+ Retrieves an extent from an EXT4 inode.
+ @param[in] Partition Pointer to the opened EXT4 partition.
+ @param[in] File Pointer to the opened file.
+ @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_NO_MAPPING Block has no mapping.
+**/
+EFI_STATUS
+Ext4GetExtent (
+ IN EXT4_PARTITION *Partition,
+ IN EXT4_FILE *File,
+ IN EXT4_BLOCK_NR LogicalBlock,
+ OUT EXT4_EXTENT *Extent
+ )
+{
+ EXT4_INODE *Inode;
+ VOID *Buffer;
+ EXT4_EXTENT *Ext;
+ UINT32 CurrentDepth;
+ EXT4_EXTENT_HEADER *ExtHeader;
+ EXT4_EXTENT_INDEX *Index;
+ EFI_STATUS Status;
+
+ Inode = File->Inode;
+ Ext = NULL;
+ Buffer = NULL;
+
+ DEBUG ((DEBUG_FS, "[ext4] Looking up extent for block %lu\n", LogicalBlock));
+
+ if (!(Inode->i_flags & EXT4_EXTENTS_FL)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ // ext4 does not have support for logical block numbers bigger than UINT32_MAX
+ if (LogicalBlock > (UINT32)- 1) {
+ return EFI_NO_MAPPING;
+ }
+
+ // Note: Right now, holes are the single biggest reason for cache misses
+ // We should find a way to get (or cache) holes
+ if ((Ext = Ext4GetExtentFromMap (File, (UINT32)LogicalBlock)) != NULL) {
+ *Extent = *Ext;
+
+ return EFI_SUCCESS;
+ }
+
+ // Slow path, we'll need to read from disk and (try to) cache those extents.
+
+ ExtHeader = Ext4GetInoExtentHeader (Inode);
+
+ if (!Ext4ExtentHeaderValid (ExtHeader)) {
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ CurrentDepth = ExtHeader->eh_depth;
+
+ while (ExtHeader->eh_depth != 0) {
+ CurrentDepth--;
+ // While depth != 0, we're traversing the tree itself and not any leaves
+ // As such, every entry is an EXT4_EXTENT_INDEX entry
+ // Note: Entries after the extent header, either index or actual extent, are always sorted.
+ // Therefore, we can use binary search, and it's actually the standard for doing so
+ // (see FreeBSD).
+
+ Index = Ext4BinsearchExtentIndex (ExtHeader, LogicalBlock);
+
+ if (Buffer == NULL) {
+ Buffer = AllocatePool (Partition->BlockSize);
+ if (Buffer == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ }
+
+ // Read the leaf block onto the previously-allocated buffer.
+
+ Status = Ext4ReadBlocks (Partition, Buffer, 1, Ext4ExtentIdxLeafBlock (Index));
+ if (EFI_ERROR (Status)) {
+ FreePool (Buffer);
+ return Status;
+ }
+
+ ExtHeader = Buffer;
+
+ if (!Ext4ExtentHeaderValid (ExtHeader)) {
+ FreePool (Buffer);
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ if (!Ext4CheckExtentChecksum (ExtHeader, File)) {
+ DEBUG ((DEBUG_ERROR, "[ext4] Invalid extent checksum\n"));
+ FreePool (Buffer);
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ if (ExtHeader->eh_depth != CurrentDepth) {
+ FreePool (Buffer);
+ return EFI_VOLUME_CORRUPTED;
+ }
+ }
+
+ /* 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);
+
+ if (!Ext) {
+ if (Buffer != NULL) {
+ FreePool (Buffer);
+ }
+
+ return EFI_NO_MAPPING;
+ }
+
+ if (!(LogicalBlock >= Ext->ee_block && Ext->ee_block + Ext->ee_len > LogicalBlock)) {
+ // This extent does not cover the block
+ if (Buffer != NULL) {
+ FreePool (Buffer);
+ }
+
+ return EFI_NO_MAPPING;
+ }
+
+ *Extent = *Ext;
+
+ if (Buffer != NULL) {
+ FreePool (Buffer);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Compare two EXT4_EXTENT structs.
+ Used in the extent map's ORDERED_COLLECTION.
+
+ @param[in] UserStruct1 Pointer to the first user structure.
+
+ @param[in] UserStruct2 Pointer to the second user structure.
+
+ @retval <0 If UserStruct1 compares less than UserStruct2.
+
+ @retval 0 If UserStruct1 compares equal to UserStruct2.
+
+ @retval >0 If UserStruct1 compares greater than UserStruct2.
+**/
+STATIC
+INTN
+EFIAPI
+Ext4ExtentsMapStructCompare (
+ IN CONST VOID *UserStruct1,
+ IN CONST VOID *UserStruct2
+ )
+{
+ CONST EXT4_EXTENT *Extent1;
+ CONST EXT4_EXTENT *Extent2;
+
+ Extent1 = UserStruct1;
+ Extent2 = UserStruct2;
+
+ return Extent1->ee_block < Extent2->ee_block ? - 1 :
+ Extent1->ee_block > Extent2->ee_block ? 1 : 0;
+}
+
+/**
+ Compare a standalone key against a EXT4_EXTENT containing an embedded key.
+ Used in the extent map's ORDERED_COLLECTION.
+
+ @param[in] StandaloneKey Pointer to the bare key.
+
+ @param[in] UserStruct Pointer to the user structure with the embedded
+ key.
+
+ @retval <0 If StandaloneKey compares less than UserStruct's key.
+
+ @retval 0 If StandaloneKey compares equal to UserStruct's key.
+
+ @retval >0 If StandaloneKey compares greater than UserStruct's key.
+**/
+STATIC
+INTN
+EFIAPI
+Ext4ExtentsMapKeyCompare (
+ IN CONST VOID *StandaloneKey,
+ IN CONST VOID *UserStruct
+ )
+{
+ CONST EXT4_EXTENT *Extent;
+ UINT32 Block;
+
+ // Note that logical blocks are 32-bits in size so no truncation can happen here
+ // with regards to 32-bit architectures.
+ Extent = UserStruct;
+ Block = (UINT32)(UINTN)StandaloneKey;
+
+ if (Block >= Extent->ee_block && Block < Extent->ee_block + Extent->ee_len) {
+ return 0;
+ }
+
+ return Block < Extent->ee_block ? - 1 :
+ Block > Extent->ee_block ? 1 : 0;
+}
+
+/**
+ Initialises the (empty) extents map, that will work as a cache of extents.
+
+ @param[in] File Pointer to the open file.
+
+ @return Result of the operation.
+**/
+EFI_STATUS
+Ext4InitExtentsMap (
+ IN EXT4_FILE *File
+ )
+{
+ File->ExtentsMap = OrderedCollectionInit (Ext4ExtentsMapStructCompare, Ext4ExtentsMapKeyCompare);
+ if (!File->ExtentsMap) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Frees the extents map, deleting every extent stored.
+
+ @param[in] File Pointer to the open file.
+**/
+VOID
+Ext4FreeExtentsMap (
+ IN EXT4_FILE *File
+ )
+{
+ // Keep calling Min(), so we get an arbitrary node we can delete.
+ // If Min() returns NULL, it's empty.
+
+ ORDERED_COLLECTION_ENTRY *MinEntry;
+ EXT4_EXTENT *Ext;
+
+ MinEntry = NULL;
+
+ while ((MinEntry = OrderedCollectionMin (File->ExtentsMap)) != NULL) {
+ OrderedCollectionDelete (File->ExtentsMap, MinEntry, (VOID **)&Ext);
+ FreePool (Ext);
+ }
+
+ ASSERT (OrderedCollectionIsEmpty (File->ExtentsMap));
+
+ OrderedCollectionUninit (File->ExtentsMap);
+ File->ExtentsMap = NULL;
+}
+
+/**
+ 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,
+ IN UINT16 NumberExtents
+ )
+{
+ UINT16 Idx;
+ EXT4_EXTENT *Extent;
+ EFI_STATUS Status;
+
+ /* 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));
+ Status = OrderedCollectionInsert (File->ExtentsMap, NULL, Extent);
+
+ // EFI_ALREADY_STARTED = already exists in the tree.
+ if (EFI_ERROR (Status)) {
+ FreePool (Extent);
+
+ if (Status == EFI_ALREADY_STARTED) {
+ continue;
+ }
+
+ return;
+ }
+ }
+}
+
+/**
+ Gets an extent from the extents cache of the file.
+
+ @param[in] File Pointer to the open file.
+ @param[in] Block Block we want to grab.
+
+ @return Pointer to the extent, or NULL if it was not found.
+**/
+EXT4_EXTENT *
+Ext4GetExtentFromMap (
+ IN EXT4_FILE *File,
+ IN UINT32 Block
+ )
+{
+ ORDERED_COLLECTION_ENTRY *Entry;
+
+ Entry = OrderedCollectionFind (File->ExtentsMap, (CONST VOID *)(UINTN)Block);
+
+ if (Entry == NULL) {
+ return NULL;
+ }
+
+ return OrderedCollectionUserStruct (Entry);
+}
+
+/**
+ Calculates the checksum of the extent data block.
+ @param[in] ExtHeader Pointer to the EXT4_EXTENT_HEADER.
+ @param[in] File Pointer to the file.
+
+ @return The checksum.
+**/
+UINT32
+Ext4CalculateExtentChecksum (
+ IN CONST EXT4_EXTENT_HEADER *ExtHeader,
+ IN CONST EXT4_FILE *File
+ )
+{
+ UINT32 Csum;
+ EXT4_PARTITION *Partition;
+ EXT4_INODE *Inode;
+
+ Partition = File->Partition;
+ Inode = File->Inode;
+
+ Csum = Ext4CalculateChecksum (Partition, &File->InodeNum, sizeof (EXT4_INO_NR), Partition->InitialSeed);
+ Csum = Ext4CalculateChecksum (Partition, &Inode->i_generation, sizeof (Inode->i_generation), Csum);
+ Csum = Ext4CalculateChecksum (Partition, ExtHeader, Partition->BlockSize - sizeof (EXT4_EXTENT_TAIL), Csum);
+
+ return Csum;
+}
+
+/**
+ Checks if the checksum of the extent data block is correct.
+ @param[in] ExtHeader Pointer to the EXT4_EXTENT_HEADER.
+ @param[in] File Pointer to the file.
+
+ @return TRUE if the checksum is correct, FALSE if there is corruption.
+**/
+BOOLEAN
+Ext4CheckExtentChecksum (
+ IN CONST EXT4_EXTENT_HEADER *ExtHeader,
+ IN CONST EXT4_FILE *File
+ )
+{
+ EXT4_PARTITION *Partition;
+ EXT4_EXTENT_TAIL *Tail;
+
+ Partition = File->Partition;
+
+ if (!EXT4_HAS_METADATA_CSUM (Partition)) {
+ return TRUE;
+ }
+
+ Tail = (EXT4_EXTENT_TAIL *)((CONST CHAR8 *)ExtHeader + (Partition->BlockSize - 4));
+
+ return Tail->eb_checksum == Ext4CalculateExtentChecksum (ExtHeader, File);
+}
diff --git a/Features/Ext4Pkg/Ext4Dxe/File.c b/Features/Ext4Pkg/Ext4Dxe/File.c
new file mode 100644
index 0000000000..0f5fa6f73f
--- /dev/null
+++ b/Features/Ext4Pkg/Ext4Dxe/File.c
@@ -0,0 +1,787 @@
+/** @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>
+
+/**
+ 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 succesful, 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[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
+ )
+{
+ EXT4_FILE *Current;
+ EXT4_PARTITION *Partition;
+ UINTN Level;
+ CHAR16 PathSegment[EXT4_NAME_MAX + 1];
+ UINTN Length;
+ EXT4_FILE *File;
+ EFI_STATUS Status;
+
+ Current = (EXT4_FILE *)This;
+ Partition = Current->Partition;
+ Level = 0;
+
+ DEBUG ((DEBUG_FS, "[ext4] Ext4Open %s\n", FileName));
+ // 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') {
+ // 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;
+
+ 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;
+ }
+ }
+
+ 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
+
+ // What to do with symlinks? They're nonsense when absolute but may
+ // be useful when they're relative. Right now, they're ignored, since they
+ // bring a lot of trouble for something that's not as useful in our case.
+ // If you want to link, use hard links.
+
+ if (!Ext4FileIsOpenable (File)) {
+ Ext4CloseInternal (File);
+ // This looks like an /okay/ status to return.
+ return EFI_ACCESS_DENIED;
+ }
+
+ 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;
+ }
+
+ *NewHandle = &Current->Protocol;
+
+ DEBUG ((DEBUG_FS, "Opened filename %s\n", Current->FileName));
+ return EFI_SUCCESS;
+}
+
+/**
+ 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 *)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->FileName);
+ FreePool (File->Inode);
+ Ext4FreeExtentsMap (File);
+ 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 *)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 *)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 *)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 *)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;
+
+ FileNameLen = StrLen (File->FileName);
+ FileNameSize = StrSize (File->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, File->FileName);
+}
+
+/**
+ 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
+ CHAR8 TempVolName[16 + 1];
+ CHAR16 *VolumeName;
+ UINTN VolNameLength;
+ EFI_STATUS Status;
+ UINTN NeededLength;
+ EXT4_BLOCK_NR TotalBlocks;
+ EXT4_BLOCK_NR FreeBlocks;
+
+ VolNameLength = 0;
+ VolumeName = NULL;
+
+ // s_volume_name is only valid on dynamic revision; old filesystems don't support this
+ if (Part->SuperBlock.s_rev_level == EXT4_DYNAMIC_REV) {
+ CopyMem (TempVolName, (CONST CHAR8 *)Part->SuperBlock.s_volume_name, 16);
+ TempVolName[16] = '\0';
+
+ Status = UTF8StrToUCS2 (TempVolName, &VolumeName);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ VolNameLength = StrLen (VolumeName);
+ }
+
+ NeededLength = SIZE_OF_EFI_FILE_SYSTEM_INFO;
+
+ if (VolumeName != NULL) {
+ NeededLength += StrSize (VolumeName);
+ } else {
+ // If we don't have a volume name, we set VolumeLabel to a single null terminator
+ NeededLength += sizeof (CHAR16);
+ }
+
+ if (*BufferSize < NeededLength) {
+ *BufferSize = NeededLength;
+
+ if (VolumeName != NULL) {
+ 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);
+
+ if (VolumeName != NULL) {
+ StrCpyS (Info->VolumeLabel, VolNameLength + 1, VolumeName);
+ } else {
+ Info->VolumeLabel[0] = L'\0';
+ }
+
+ if (VolumeName != NULL) {
+ 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
+ )
+{
+ if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {
+ return Ext4GetFileInfo ((EXT4_FILE *)This, Buffer, BufferSize);
+ }
+
+ if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) {
+ return Ext4GetFilesystemInfo (((EXT4_FILE *)This)->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;
+
+ 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->FileName = AllocateZeroPool (StrSize (Original->FileName));
+ if (File->FileName == NULL) {
+ FreePool (File->Inode);
+ FreePool (File);
+ return NULL;
+ }
+
+ StrCpyS (File->FileName, StrLen (Original->FileName) + 1, Original->FileName);
+
+ File->Position = 0;
+ Ext4SetupFile (File, Partition);
+ File->InodeNum = Original->InodeNum;
+ File->OpenMode = 0; // Will be filled by other code
+
+ if (!Ext4InitExtentsMap (File)) {
+ FreePool (File->FileName);
+ FreePool (File->Inode);
+ FreePool (File);
+ return NULL;
+ }
+
+ 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 *Part;
+
+ File = (EXT4_FILE *)This;
+ Part = File->Partition;
+
+ if (Part->ReadOnly) {
+ return EFI_WRITE_PROTECTED;
+ }
+
+ // There's no write support just yet.
+ return EFI_UNSUPPORTED;
+}
diff --git a/Features/Ext4Pkg/Ext4Dxe/Inode.c b/Features/Ext4Pkg/Ext4Dxe/Inode.c
new file mode 100644
index 0000000000..1bbff9e69f
--- /dev/null
+++ b/Features/Ext4Pkg/Ext4Dxe/Inode.c
@@ -0,0 +1,465 @@
+/** @file
+ Inode related routines
+
+ Copyright (c) 2021 Pedro Falcato All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ EpochToEfiTime copied from EmbeddedPkg/Library/TimeBaseLib.c
+ Copyright (c) 2016, Hisilicon Limited. All rights reserved.
+ Copyright (c) 2016-2019, Linaro Limited. All rights reserved.
+ Copyright (c) 2021, Ampere Computing LLC. All rights reserved.
+**/
+
+#include "Ext4Dxe.h"
+
+/**
+ Calculates the checksum of the given inode.
+ @param[in] Partition Pointer to the opened EXT4 partition.
+ @param[in] Inode Pointer to the inode.
+ @param[in] InodeNum Inode number.
+
+ @return The checksum.
+**/
+UINT32
+Ext4CalculateInodeChecksum (
+ IN CONST EXT4_PARTITION *Partition,
+ IN CONST EXT4_INODE *Inode,
+ IN EXT4_INO_NR InodeNum
+ )
+{
+ UINT32 Crc;
+ UINT16 Dummy;
+ BOOLEAN HasSecondChecksumField;
+ CONST VOID *RestOfInode;
+ UINTN RestOfInodeLength;
+ UINTN Length;
+
+ HasSecondChecksumField = EXT4_INODE_HAS_FIELD (Inode, i_checksum_hi);
+
+ Dummy = 0;
+
+ Crc = Ext4CalculateChecksum (Partition, &InodeNum, sizeof (InodeNum), Partition->InitialSeed);
+ Crc = Ext4CalculateChecksum (Partition, &Inode->i_generation, sizeof (Inode->i_generation), Crc);
+
+ Crc = Ext4CalculateChecksum (
+ Partition,
+ Inode,
+ OFFSET_OF (EXT4_INODE, i_osd2.data_linux.l_i_checksum_lo),
+ Crc
+ );
+
+ Crc = Ext4CalculateChecksum (Partition, &Dummy, sizeof (Dummy), Crc);
+
+ RestOfInode = &Inode->i_osd2.data_linux.l_i_reserved;
+ RestOfInodeLength = Partition->InodeSize - OFFSET_OF (EXT4_INODE, i_osd2.data_linux.l_i_reserved);
+
+ if (HasSecondChecksumField) {
+ Length = OFFSET_OF (EXT4_INODE, i_checksum_hi) - OFFSET_OF (EXT4_INODE, i_osd2.data_linux.l_i_reserved);
+
+ Crc = Ext4CalculateChecksum (Partition, &Inode->i_osd2.data_linux.l_i_reserved, Length, Crc);
+ Crc = Ext4CalculateChecksum (Partition, &Dummy, sizeof (Dummy), Crc);
+
+ // 4 is the size of the i_extra_size field + the size of i_checksum_hi
+ RestOfInodeLength = Partition->InodeSize - EXT4_GOOD_OLD_INODE_SIZE - 4;
+ RestOfInode = &Inode->i_ctime_extra;
+ }
+
+ Crc = Ext4CalculateChecksum (Partition, RestOfInode, RestOfInodeLength, Crc);
+
+ return Crc;
+}
+
+/**
+ Reads from an EXT4 inode.
+ @param[in] Partition Pointer to the opened EXT4 partition.
+ @param[in] File Pointer to the opened file.
+ @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.
+
+ @return Status of the read operation.
+**/
+EFI_STATUS
+Ext4Read (
+ IN EXT4_PARTITION *Partition,
+ IN EXT4_FILE *File,
+ OUT VOID *Buffer,
+ IN UINT64 Offset,
+ IN OUT UINTN *Length
+ )
+{
+ DEBUG ((DEBUG_FS, "[ext4] Ext4Read(Offset %lu, Length %lu)\n", Offset, *Length));
+ EXT4_INODE *Inode;
+ UINT64 InodeSize;
+ UINT64 CurrentSeek;
+ UINTN RemainingRead;
+ UINTN BeenRead;
+ UINTN WasRead;
+ EXT4_EXTENT Extent;
+ UINT32 BlockOff;
+ EFI_STATUS Status;
+ BOOLEAN HasBackingExtent;
+ UINT32 HoleOff;
+ UINTN HoleLen;
+ UINT64 ExtentStartBytes;
+ UINT64 ExtentLengthBytes;
+ UINT64 ExtentLogicalBytes;
+
+ // Our extent offset is the difference between CurrentSeek and ExtentLogicalBytes
+ UINT64 ExtentOffset;
+ UINTN ExtentMayRead;
+
+ Inode = File->Inode;
+ InodeSize = EXT4_INODE_SIZE (Inode);
+ CurrentSeek = Offset;
+ RemainingRead = *Length;
+ BeenRead = 0;
+
+ if (Offset > InodeSize) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (RemainingRead > InodeSize - Offset) {
+ RemainingRead = (UINTN)(InodeSize - Offset);
+ }
+
+ while (RemainingRead != 0) {
+ WasRead = 0;
+
+ // The algorithm here is to get the extent corresponding to the current block
+ // and then read as much as we can from the current extent.
+
+ Status = Ext4GetExtent (
+ Partition,
+ File,
+ DivU64x32Remainder (CurrentSeek, Partition->BlockSize, &BlockOff),
+ &Extent
+ );
+
+ if (Status != EFI_SUCCESS && Status != EFI_NO_MAPPING) {
+ return Status;
+ }
+
+ HasBackingExtent = Status != EFI_NO_MAPPING;
+
+ if (!HasBackingExtent) {
+ HoleOff = BlockOff;
+ HoleLen = Partition->BlockSize - HoleOff;
+ WasRead = HoleLen > RemainingRead ? RemainingRead : HoleLen;
+ // Potential improvement: In the future, we could get the hole's tota
+ // size and memset all that
+ SetMem (Buffer, WasRead, 0);
+ } else {
+ ExtentStartBytes = MultU64x32 (
+ LShiftU64 (Extent.ee_start_hi, 32) |
+ Extent.ee_start_lo,
+ Partition->BlockSize
+ );
+ ExtentLengthBytes = Extent.ee_len * Partition->BlockSize;
+ ExtentLogicalBytes = (UINT64)Extent.ee_block * Partition->BlockSize;
+ ExtentOffset = CurrentSeek - ExtentLogicalBytes;
+ ExtentMayRead = (UINTN)(ExtentLengthBytes - ExtentOffset);
+
+ WasRead = ExtentMayRead > RemainingRead ? RemainingRead : ExtentMayRead;
+
+ Status = Ext4ReadDiskIo (Partition, Buffer, WasRead, ExtentStartBytes + ExtentOffset);
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "[ext4] Error %x reading [%lu, %lu]\n",
+ Status,
+ ExtentStartBytes + ExtentOffset,
+ ExtentStartBytes + ExtentOffset + WasRead - 1
+ ));
+ return Status;
+ }
+ }
+
+ RemainingRead -= WasRead;
+ Buffer = (VOID *)((CHAR8 *)Buffer + WasRead);
+ BeenRead += WasRead;
+ CurrentSeek += WasRead;
+ }
+
+ *Length = BeenRead;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Allocates a zeroed inode structure.
+ @param[in] Partition Pointer to the opened EXT4 partition.
+
+ @return Pointer to the allocated structure, from the pool,
+ with size Partition->InodeSize.
+**/
+EXT4_INODE *
+Ext4AllocateInode (
+ IN EXT4_PARTITION *Partition
+ )
+{
+ BOOLEAN NeedsToZeroRest;
+ UINT32 InodeSize;
+ EXT4_INODE *Inode;
+
+ NeedsToZeroRest = FALSE;
+ InodeSize = Partition->InodeSize;
+
+ // We allocate a structure of at least sizeof(EXT4_INODE), but in the future, when
+ // write support is added and we need to flush inodes to disk, we could have a bit better
+ // distinction between the on-disk inode and a separate, nicer to work with inode struct.
+ // It's important to note that EXT4_INODE includes fields that may not exist in an actual
+ // filesystem (the minimum inode size is 128 byte and at the moment the size of EXT4_INODE
+ // is 160 bytes).
+
+ if (InodeSize < sizeof (EXT4_INODE)) {
+ InodeSize = sizeof (EXT4_INODE);
+ NeedsToZeroRest = TRUE;
+ }
+
+ Inode = AllocateZeroPool (InodeSize);
+
+ if (!Inode) {
+ return NULL;
+ }
+
+ if (NeedsToZeroRest) {
+ Inode->i_extra_isize = 0;
+ }
+
+ return Inode;
+}
+
+/**
+ Checks if a file is a directory.
+ @param[in] File Pointer to the opened file.
+
+ @return TRUE if file is a directory.
+**/
+BOOLEAN
+Ext4FileIsDir (
+ IN CONST EXT4_FILE *File
+ )
+{
+ return (File->Inode->i_mode & EXT4_INO_TYPE_DIR) == EXT4_INO_TYPE_DIR;
+}
+
+/**
+ Checks if a file is a regular file.
+ @param[in] File Pointer to the opened file.
+
+ @return BOOLEAN TRUE if file is a regular file.
+**/
+BOOLEAN
+Ext4FileIsReg (
+ IN CONST EXT4_FILE *File
+ )
+{
+ return (File->Inode->i_mode & EXT4_INO_TYPE_REGFILE) == EXT4_INO_TYPE_REGFILE;
+}
+
+/**
+ Calculates the physical space used by a file.
+ @param[in] File Pointer to the opened file.
+
+ @return Physical space used by a file, in bytes.
+**/
+UINT64
+Ext4FilePhysicalSpace (
+ IN EXT4_FILE *File
+ )
+{
+ BOOLEAN HugeFile;
+ UINT64 Blocks;
+
+ HugeFile = EXT4_HAS_RO_COMPAT (File->Partition, EXT4_FEATURE_RO_COMPAT_HUGE_FILE);
+ Blocks = File->Inode->i_blocks;
+
+ if (HugeFile) {
+ Blocks |= LShiftU64 (File->Inode->i_osd2.data_linux.l_i_blocks_high, 32);
+
+ // If HUGE_FILE is enabled and EXT4_HUGE_FILE_FL is set in the inode's flags, each unit
+ // in i_blocks corresponds to an actual filesystem block
+ if (File->Inode->i_flags & EXT4_HUGE_FILE_FL) {
+ return MultU64x32 (Blocks, File->Partition->BlockSize);
+ }
+ }
+
+ // Else, each i_blocks unit corresponds to 512 bytes
+ return MultU64x32 (Blocks, 512);
+}
+
+// Copied from EmbeddedPkg at my mentor's request.
+// The lack of comments and good variable names is frightening...
+
+/**
+ Converts Epoch seconds (elapsed since 1970 JANUARY 01, 00:00:00 UTC) to EFI_TIME.
+
+ @param[in] EpochSeconds Epoch seconds.
+ @param[out] Time The time converted to UEFI format.
+
+**/
+STATIC
+VOID
+EFIAPI
+EpochToEfiTime (
+ IN UINTN EpochSeconds,
+ OUT EFI_TIME *Time
+ )
+{
+ UINTN a;
+ UINTN b;
+ UINTN c;
+ UINTN d;
+ UINTN g;
+ UINTN j;
+ UINTN m;
+ UINTN y;
+ UINTN da;
+ UINTN db;
+ UINTN dc;
+ UINTN dg;
+ UINTN hh;
+ UINTN mm;
+ UINTN ss;
+ UINTN J;
+
+ J = (EpochSeconds / 86400) + 2440588;
+ j = J + 32044;
+ g = j / 146097;
+ dg = j % 146097;
+ c = (((dg / 36524) + 1) * 3) / 4;
+ dc = dg - (c * 36524);
+ b = dc / 1461;
+ db = dc % 1461;
+ a = (((db / 365) + 1) * 3) / 4;
+ da = db - (a * 365);
+ y = (g * 400) + (c * 100) + (b * 4) + a;
+ m = (((da * 5) + 308) / 153) - 2;
+ d = da - (((m + 4) * 153) / 5) + 122;
+
+ Time->Year = (UINT16)(y - 4800 + ((m + 2) / 12));
+ Time->Month = ((m + 2) % 12) + 1;
+ Time->Day = (UINT8)(d + 1);
+
+ ss = EpochSeconds % 60;
+ a = (EpochSeconds - ss) / 60;
+ mm = a % 60;
+ b = (a - mm) / 60;
+ hh = b % 24;
+
+ Time->Hour = (UINT8)hh;
+ Time->Minute = (UINT8)mm;
+ Time->Second = (UINT8)ss;
+ Time->Nanosecond = 0;
+}
+
+// The time format used to (de/en)code timestamp and timestamp_extra is documented on
+// the ext4 docs page in kernel.org
+#define EXT4_EXTRA_TIMESTAMP_MASK ((1 << 2) - 1)
+
+#define EXT4_FILE_GET_TIME_GENERIC(Name, Field) \
+ VOID \
+ Ext4File ## Name (IN EXT4_FILE *File, OUT EFI_TIME *Time) \
+ { \
+ EXT4_INODE *Inode = File->Inode; \
+ UINT64 SecondsEpoch = Inode->Field; \
+ UINT32 Nanoseconds = 0; \
+ \
+ if (EXT4_INODE_HAS_FIELD (Inode, Field ## _extra)) { \
+ SecondsEpoch |= LShiftU64 ((UINT64)(Inode->Field ## _extra & EXT4_EXTRA_TIMESTAMP_MASK), 32); \
+ Nanoseconds = Inode->Field ## _extra >> 2; \
+ } \
+ EpochToEfiTime ((UINTN)SecondsEpoch, Time); \
+ Time->Nanosecond = Nanoseconds; \
+ }
+
+// Note: EpochToEfiTime should be adjusted to take in a UINT64 instead of a UINTN, in order to avoid Y2038
+// on 32-bit systems.
+
+/**
+ Gets the file's last access time.
+ @param[in] File Pointer to the opened file.
+ @param[out] Time Pointer to an EFI_TIME structure.
+**/
+EXT4_FILE_GET_TIME_GENERIC (ATime, i_atime);
+
+/**
+ Gets the file's last (data) modification time.
+ @param[in] File Pointer to the opened file.
+ @param[out] Time Pointer to an EFI_TIME structure.
+**/
+EXT4_FILE_GET_TIME_GENERIC (MTime, i_mtime);
+
+/**
+ Gets the file's creation time.
+ @param[in] File Pointer to the opened file.
+ @param[out] Time Pointer to an EFI_TIME structure.
+**/
+STATIC
+EXT4_FILE_GET_TIME_GENERIC (
+ CrTime, i_crtime
+ );
+
+/**
+ Gets the file's creation time, if possible.
+ @param[in] File Pointer to the opened file.
+ @param[out] Time Pointer to an EFI_TIME structure.
+ In the case where the the creation time isn't recorded,
+ Time is zeroed.
+**/
+VOID
+Ext4FileCreateTime (
+ IN EXT4_FILE *File,
+ OUT EFI_TIME *Time
+ )
+{
+ EXT4_INODE *Inode;
+
+ Inode = File->Inode;
+
+ if (!EXT4_INODE_HAS_FIELD (Inode, i_crtime)) {
+ SetMem (Time, sizeof (EFI_TIME), 0);
+ return;
+ }
+
+ Ext4FileCrTime (File, Time);
+}
+
+/**
+ Checks if the checksum of the inode is correct.
+ @param[in] Partition Pointer to the opened EXT4 partition.
+ @param[in] Inode Pointer to the inode.
+ @param[in] InodeNum Inode number.
+
+ @return TRUE if checksum is correct, FALSE if there is corruption.
+**/
+BOOLEAN
+Ext4CheckInodeChecksum (
+ IN CONST EXT4_PARTITION *Partition,
+ IN CONST EXT4_INODE *Inode,
+ IN EXT4_INO_NR InodeNum
+ )
+{
+ UINT32 Csum;
+ UINT32 DiskCsum;
+
+ if (!EXT4_HAS_METADATA_CSUM (Partition)) {
+ return TRUE;
+ }
+
+ Csum = Ext4CalculateInodeChecksum (Partition, Inode, InodeNum);
+
+ DiskCsum = Inode->i_osd2.data_linux.l_i_checksum_lo;
+
+ if (EXT4_INODE_HAS_FIELD (Inode, i_checksum_hi)) {
+ DiskCsum |= ((UINT32)Inode->i_checksum_hi) << 16;
+ } else {
+ // Only keep the lower bits for the comparison if the checksum is 16 bits.
+ Csum &= 0xffff;
+ }
+
+ return Csum == DiskCsum;
+}
diff --git a/Features/Ext4Pkg/Ext4Dxe/Partition.c b/Features/Ext4Pkg/Ext4Dxe/Partition.c
new file mode 100644
index 0000000000..2258bac76a
--- /dev/null
+++ b/Features/Ext4Pkg/Ext4Dxe/Partition.c
@@ -0,0 +1,125 @@
+/** @file
+ Driver entry point
+
+ Copyright (c) 2021 Pedro Falcato All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "Ext4Dxe.h"
+
+/**
+ Opens an ext4 partition and installs the Simple File System protocol.
+
+ @param[in] DeviceHandle Handle to the block device.
+ @param[in] DiskIo Pointer to an EFI_DISK_IO_PROTOCOL.
+ @param[in opt] DiskIo2 Pointer to an EFI_DISK_IO2_PROTOCOL, if supported.
+ @param[in] BlockIo Pointer to an EFI_BLOCK_IO_PROTOCOL.
+
+ @retval EFI_SUCCESS The opening was successful.
+ !EFI_SUCCESS Opening failed.
+**/
+EFI_STATUS
+Ext4OpenPartition (
+ IN EFI_HANDLE DeviceHandle,
+ IN EFI_DISK_IO_PROTOCOL *DiskIo,
+ IN OPTIONAL EFI_DISK_IO2_PROTOCOL *DiskIo2,
+ IN EFI_BLOCK_IO_PROTOCOL *BlockIo
+ )
+{
+ EXT4_PARTITION *Part;
+ EFI_STATUS Status;
+
+ Part = AllocateZeroPool (sizeof (*Part));
+
+ if (Part == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ InitializeListHead (&Part->OpenFiles);
+
+ Part->BlockIo = BlockIo;
+ Part->DiskIo = DiskIo;
+ Part->DiskIo2 = DiskIo2;
+
+ Status = Ext4OpenSuperblock (Part);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Part);
+ return Status;
+ }
+
+ Part->Interface.Revision = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION;
+ Part->Interface.OpenVolume = Ext4OpenVolume;
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &DeviceHandle,
+ &gEfiSimpleFileSystemProtocolGuid,
+ &Part->Interface,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ FreePool (Part);
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Sets up the protocol and metadata of a file that is being opened.
+
+ @param[in out] File Pointer to the file.
+ @param[in] Partition Pointer to the opened partition.
+**/
+VOID
+Ext4SetupFile (
+ IN OUT EXT4_FILE *File,
+ IN EXT4_PARTITION *Partition
+ )
+{
+ // Note: We don't yet support revision 2 of the file protocol
+ // (needs DISK_IO2 + asynchronous IO)
+ File->Protocol.Revision = EFI_FILE_PROTOCOL_REVISION;
+ File->Protocol.Open = Ext4Open;
+ File->Protocol.Close = Ext4Close;
+ File->Protocol.Delete = Ext4Delete;
+ File->Protocol.Read = Ext4ReadFile;
+ File->Protocol.Write = Ext4WriteFile;
+ File->Protocol.SetPosition = Ext4SetPosition;
+ File->Protocol.GetPosition = Ext4GetPosition;
+ File->Protocol.GetInfo = Ext4GetInfo;
+ File->Protocol.SetInfo = Ext4SetInfo;
+
+ File->Partition = Partition;
+}
+
+/**
+ Unmounts and frees an ext4 partition.
+
+ @param[in] Partition Pointer to the opened partition.
+
+ @retval Status of the unmount.
+**/
+EFI_STATUS
+Ext4UnmountAndFreePartition (
+ IN EXT4_PARTITION *Partition
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+ EXT4_FILE *File;
+
+ Partition->Unmounting = TRUE;
+ Ext4CloseInternal (Partition->Root);
+
+ BASE_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Partition->OpenFiles) {
+ File = EXT4_FILE_FROM_OPEN_FILES_NODE (Entry);
+
+ Ext4CloseInternal (File);
+ }
+
+ FreePool (Partition->BlockGroups);
+ FreePool (Partition);
+
+ return EFI_SUCCESS;
+}
diff --git a/Features/Ext4Pkg/Ext4Dxe/Superblock.c b/Features/Ext4Pkg/Ext4Dxe/Superblock.c
new file mode 100644
index 0000000000..8231831115
--- /dev/null
+++ b/Features/Ext4Pkg/Ext4Dxe/Superblock.c
@@ -0,0 +1,297 @@
+/** @file
+ Superblock managing routines
+
+ Copyright (c) 2021 Pedro Falcato All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "Ext4Dxe.h"
+
+STATIC CONST UINT32 gSupportedCompatFeat = EXT4_FEATURE_COMPAT_EXT_ATTR;
+
+STATIC CONST UINT32 gSupportedRoCompatFeat =
+ EXT4_FEATURE_RO_COMPAT_DIR_NLINK | EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE |
+ EXT4_FEATURE_RO_COMPAT_HUGE_FILE | EXT4_FEATURE_RO_COMPAT_LARGE_FILE |
+ EXT4_FEATURE_RO_COMPAT_GDT_CSUM | EXT4_FEATURE_RO_COMPAT_METADATA_CSUM | EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER;
+
+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;
+
+// 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.
+// 2) meta_bg: Required to mount meta_bg-enabled partitions.
+
+// Note: We ignore MMP because it's impossible that it's mapped elsewhere,
+// I think (unless there's some sort of network setup where we're accessing a remote partition).
+
+// Note on corruption signaling:
+// We (Ext4Dxe) could signal corruption by setting s_state to |= EXT4_FS_STATE_ERRORS_DETECTED.
+// I've decided against that, because right now the driver is read-only, and
+// that would mean we would need to writeback the superblock. If something like
+// this is desired, it's fairly trivial to look for EFI_VOLUME_CORRUPTED
+// references and add some Ext4SignalCorruption function + function call.
+
+/**
+ Does brief validation of the ext4 superblock.
+
+ @param[in] Sb Pointer to the read superblock.
+
+ @return TRUE if a valid ext4 superblock, else FALSE.
+**/
+BOOLEAN
+Ext4SuperblockValidate (
+ CONST EXT4_SUPERBLOCK *Sb
+ )
+{
+ if (Sb->s_magic != EXT4_SIGNATURE) {
+ return FALSE;
+ }
+
+ if (Sb->s_rev_level != EXT4_DYNAMIC_REV && Sb->s_rev_level != EXT4_GOOD_OLD_REV) {
+ return FALSE;
+ }
+
+ // Is this correct behaviour? Imagine the power cuts out, should the system fail to boot because
+ // we're scared of touching something corrupt?
+ if ((Sb->s_state & EXT4_FS_STATE_UNMOUNTED) == 0) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ Calculates the superblock's checksum.
+
+ @param[in] Partition Pointer to the opened partition.
+ @param[in] Sb Pointer to the superblock.
+
+ @return The superblock's checksum.
+**/
+STATIC
+UINT32
+Ext4CalculateSuperblockChecksum (
+ EXT4_PARTITION *Partition,
+ CONST EXT4_SUPERBLOCK *Sb
+ )
+{
+ // Most checksums require us to go through a dummy 0 as part of the requirement
+ // that the checksum is done over a structure with its checksum field = 0.
+ UINT32 Checksum;
+
+ Checksum = Ext4CalculateChecksum (
+ Partition,
+ Sb,
+ OFFSET_OF (EXT4_SUPERBLOCK, s_checksum),
+ ~0U
+ );
+
+ return Checksum;
+}
+
+/**
+ Verifies that the superblock's checksum is valid.
+
+ @param[in] Partition Pointer to the opened partition.
+ @param[in] Sb Pointer to the superblock.
+
+ @return The superblock's checksum.
+**/
+STATIC
+BOOLEAN
+Ext4VerifySuperblockChecksum (
+ EXT4_PARTITION *Partition,
+ CONST EXT4_SUPERBLOCK *Sb
+ )
+{
+ if (!EXT4_HAS_METADATA_CSUM (Partition)) {
+ return TRUE;
+ }
+
+ return Sb->s_checksum == Ext4CalculateSuperblockChecksum (Partition, Sb);
+}
+
+/**
+ 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
+ valid ext4 partition.
+**/
+EFI_STATUS
+Ext4OpenSuperblock (
+ OUT EXT4_PARTITION *Partition
+ )
+{
+ UINT32 Index;
+ EFI_STATUS Status;
+ EXT4_SUPERBLOCK *Sb;
+ UINT32 NrBlocksRem;
+ UINTN NrBlocks;
+ UINT32 UnsupportedRoCompat;
+ EXT4_BLOCK_GROUP_DESC *Desc;
+
+ Status = Ext4ReadDiskIo (
+ Partition,
+ &Partition->SuperBlock,
+ sizeof (EXT4_SUPERBLOCK),
+ EXT4_SUPERBLOCK_OFFSET
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Sb = &Partition->SuperBlock;
+
+ if (!Ext4SuperblockValidate (Sb)) {
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ if (Sb->s_rev_level == EXT4_DYNAMIC_REV) {
+ Partition->FeaturesCompat = Sb->s_feature_compat;
+ Partition->FeaturesIncompat = Sb->s_feature_incompat;
+ Partition->FeaturesRoCompat = Sb->s_feature_ro_compat;
+ Partition->InodeSize = Sb->s_inode_size;
+ } else {
+ // GOOD_OLD_REV
+ Partition->FeaturesCompat = Partition->FeaturesIncompat = Partition->FeaturesRoCompat = 0;
+ Partition->InodeSize = EXT4_GOOD_OLD_INODE_SIZE;
+ }
+
+ // 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.
+
+ if (Partition->FeaturesIncompat & ~gSupportedIncompatFeat) {
+ DEBUG ((DEBUG_ERROR, "[ext4] Unsupported features %lx\n",
+ Partition->FeaturesIncompat & ~gSupportedIncompatFeat));
+ return EFI_UNSUPPORTED;
+ }
+
+ // This should be removed once we add ext2/3 support in the future.
+ if ((Partition->FeaturesIncompat & EXT4_FEATURE_INCOMPAT_EXTENTS) == 0) {
+ return EFI_UNSUPPORTED;
+ }
+
+ // 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) {
+ return EFI_UNSUPPORTED;
+ }
+
+ if (Partition->FeaturesIncompat & EXT4_FEATURE_INCOMPAT_CSUM_SEED) {
+ Partition->InitialSeed = Sb->s_checksum_seed;
+ } else {
+ Partition->InitialSeed = Ext4CalculateChecksum (Partition, Sb->s_uuid, 16, ~0U);
+ }
+
+ UnsupportedRoCompat = Partition->FeaturesRoCompat & ~gSupportedRoCompatFeat;
+
+ if (UnsupportedRoCompat != 0) {
+ DEBUG ((DEBUG_WARN, "[ext4] Unsupported ro compat %x\n", UnsupportedRoCompat));
+ Partition->ReadOnly = TRUE;
+ }
+
+ // gSupportedCompatFeat is documentation-only since we never need to access it.
+ // The line below avoids unused variable warnings.
+ (VOID)gSupportedCompatFeat;
+
+ DEBUG ((DEBUG_FS, "Read only = %u\n", Partition->ReadOnly));
+
+ Partition->BlockSize = (UINT32)LShiftU64 (1024, Sb->s_log_block_size);
+
+ // The size of a block group can also be calculated as 8 * Partition->BlockSize
+ if (Sb->s_blocks_per_group != 8 * Partition->BlockSize) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Partition->NumberBlocks = EXT4_BLOCK_NR_FROM_HALFS (Partition, Sb->s_blocks_count, Sb->s_blocks_count_hi);
+ Partition->NumberBlockGroups = DivU64x32 (Partition->NumberBlocks, Sb->s_blocks_per_group);
+
+ DEBUG ((
+ DEBUG_FS,
+ "[ext4] Number of blocks = %lu\n[ext4] Number of block groups: %lu\n",
+ Partition->NumberBlocks,
+ Partition->NumberBlockGroups
+ ));
+
+ if (EXT4_IS_64_BIT (Partition)) {
+ Partition->DescSize = Sb->s_desc_size;
+ } else {
+ Partition->DescSize = EXT4_OLD_BLOCK_DESC_SIZE;
+ }
+
+ if (Partition->DescSize < EXT4_64BIT_BLOCK_DESC_SIZE && EXT4_IS_64_BIT (Partition)) {
+ // 64 bit filesystems need DescSize to be 64 bytes
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ if (!Ext4VerifySuperblockChecksum (Partition, Sb)) {
+ DEBUG ((DEBUG_ERROR, "[ext4] Bad superblock checksum %lx\n", Ext4CalculateSuperblockChecksum (Partition, Sb)));
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ NrBlocks = (UINTN)DivU64x32Remainder (
+ MultU64x32 (Partition->NumberBlockGroups, Partition->DescSize),
+ Partition->BlockSize,
+ &NrBlocksRem
+ );
+
+ if (NrBlocksRem != 0) {
+ NrBlocks++;
+ }
+
+ Partition->BlockGroups = Ext4AllocAndReadBlocks (Partition, NrBlocks, Partition->BlockSize == 1024 ? 2 : 1);
+
+ if (!Partition->BlockGroups) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ for (Index = 0; Index < Partition->NumberBlockGroups; Index++) {
+ Desc = Ext4GetBlockGroupDesc (Partition, Index);
+ if (!Ext4VerifyBlockGroupDescChecksum (Partition, Desc, Index)) {
+ DEBUG ((DEBUG_ERROR, "[ext4] Block group descriptor %u has an invalid checksum\n", Index));
+ return EFI_VOLUME_CORRUPTED;
+ }
+ }
+
+ // Note that the cast below is completely safe, because EXT4_FILE is a specialisation of EFI_FILE_PROTOCOL
+ Status = Ext4OpenVolume (&Partition->Interface, (EFI_FILE_PROTOCOL **)&Partition->Root);
+
+ return Status;
+}
+
+/**
+ Calculates the checksum of the given buffer.
+ @param[in] Partition Pointer to the opened EXT4 partition.
+ @param[in] Buffer Pointer to the buffer.
+ @param[in] Length Length of the buffer, in bytes.
+ @param[in] InitialValue Initial value of the CRC.
+
+ @return The checksum.
+**/
+UINT32
+Ext4CalculateChecksum (
+ IN CONST EXT4_PARTITION *Partition,
+ IN CONST VOID *Buffer,
+ IN UINTN Length,
+ IN UINT32 InitialValue
+ )
+{
+ if (!EXT4_HAS_METADATA_CSUM (Partition)) {
+ return 0;
+ }
+
+ switch (Partition->SuperBlock.s_checksum_type) {
+ case EXT4_CHECKSUM_CRC32C:
+ // For some reason, EXT4 really likes non-inverted CRC32C checksums, so we stick to that here.
+ return ~CalculateCrc32c(Buffer, Length, ~InitialValue);
+ default:
+ UNREACHABLE ();
+ return 0;
+ }
+}
--
2.32.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [Patch v4 3/3] Ext4Pkg: Add .DSC file.
2021-08-16 18:06 [Patch v4 0/3] Ext4Pkg: Add Ext4Pkg Pedro Falcato
2021-08-16 18:06 ` [Patch v4 1/3] Ext4Pkg: Add Ext4Pkg.dec and Ext4Pkg.uni Pedro Falcato
2021-08-16 18:06 ` [Patch v4 2/3] Ext4Pkg: Add Ext4Dxe driver Pedro Falcato
@ 2021-08-16 18:06 ` Pedro Falcato
2021-08-18 5:16 ` [edk2-devel] [Patch v4 0/3] Ext4Pkg: Add Ext4Pkg Michael D Kinney
3 siblings, 0 replies; 6+ messages in thread
From: Pedro Falcato @ 2021-08-16 18:06 UTC (permalink / raw)
To: devel; +Cc: Pedro Falcato, Leif Lindholm, Michael D Kinney, Bret Barkelew
This file is required to build Ext4Pkg.
Cc: Leif Lindholm <leif@nuviainc.com>
Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Bret Barkelew <Bret.Barkelew@microsoft.com>
Signed-off-by: Pedro Falcato <pedro.falcato@gmail.com>
---
Features/Ext4Pkg/Ext4Pkg.dsc | 70 ++++++++++++++++++++++++++++++++++++
1 file changed, 70 insertions(+)
create mode 100644 Features/Ext4Pkg/Ext4Pkg.dsc
diff --git a/Features/Ext4Pkg/Ext4Pkg.dsc b/Features/Ext4Pkg/Ext4Pkg.dsc
new file mode 100644
index 0000000000..57f279a4d9
--- /dev/null
+++ b/Features/Ext4Pkg/Ext4Pkg.dsc
@@ -0,0 +1,70 @@
+## @file
+# Ext4 Package
+#
+# This package provides libraries and drivers related to the ext4 filesystem implementation.
+# More details are available at: https://www.kernel.org/doc/html/v5.4/filesystems/ext4/index.html
+#
+# Copyright (c) 2021 Pedro Falcato
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+
+[Defines]
+ PLATFORM_NAME = Ext4
+ PLATFORM_GUID = 6B4BF998-668B-46D3-BCFA-971F99F8708C
+ PLATFORM_VERSION = 0.1
+ DSC_SPECIFICATION = 0x00010005
+ SUPPORTED_ARCHITECTURES = IA32|X64|EBC|ARM|AARCH64|RISCV64
+ OUTPUT_DIRECTORY = Build/Ext4Pkg
+ BUILD_TARGETS = DEBUG|RELEASE|NOOPT
+ SKUID_IDENTIFIER = DEFAULT
+
+!include MdePkg/MdeLibs.dsc.inc
+
+[BuildOptions]
+ *_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES
+
+[LibraryClasses]
+ #
+ # Entry Point Libraries
+ #
+ UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf
+ #
+ # Common Libraries
+ #
+ BaseLib|MdePkg/Library/BaseLib/BaseLib.inf
+ BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
+ UefiLib|MdePkg/Library/UefiLib/UefiLib.inf
+ PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf
+ PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
+ MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
+ UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf
+ UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf
+ DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf
+ DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf
+ DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
+ OrderedCollectionLib|MdePkg/Library/BaseOrderedCollectionRedBlackTreeLib/BaseOrderedCollectionRedBlackTreeLib.inf
+ BaseUcs2Utf8Lib|RedfishPkg/Library/BaseUcs2Utf8Lib/BaseUcs2Utf8Lib.inf
+
+###################################################################################################
+#
+# Components Section - list of the modules and components that will be processed by compilation
+# tools and the EDK II tools to generate PE32/PE32+/Coff image files.
+#
+# Note: The EDK II DSC file is not used to specify how compiled binary images get placed
+# into firmware volume images. This section is just a list of modules to compile from
+# source into UEFI-compliant binaries.
+# It is the FDF file that contains information on combining binary files into firmware
+# volume images, whose concept is beyond UEFI and is described in PI specification.
+# Binary modules do not need to be listed in this section, as they should be
+# specified in the FDF file. For example: Shell binary (Shell_Full.efi), FAT binary (Fat.efi),
+# Logo (Logo.bmp), and etc.
+# There may also be modules listed in this section that are not required in the FDF file,
+# When a module listed here is excluded from FDF file, then UEFI-compliant binary will be
+# generated for it, but the binary will not be put into any firmware volume.
+#
+###################################################################################################
+
+[Components]
+ Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.inf
--
2.32.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [edk2-devel] [Patch v4 0/3] Ext4Pkg: Add Ext4Pkg
2021-08-16 18:06 [Patch v4 0/3] Ext4Pkg: Add Ext4Pkg Pedro Falcato
` (2 preceding siblings ...)
2021-08-16 18:06 ` [Patch v4 3/3] Ext4Pkg: Add .DSC file Pedro Falcato
@ 2021-08-18 5:16 ` Michael D Kinney
2021-08-18 5:20 ` Michael D Kinney
3 siblings, 1 reply; 6+ messages in thread
From: Michael D Kinney @ 2021-08-18 5:16 UTC (permalink / raw)
To: devel@edk2.groups.io, pedro.falcato@gmail.com, Kinney, Michael D
Cc: Leif Lindholm, Bret Barkelew
Series Reviewed-by: Michael D Kinney <michael.d.kinney@intel.com>
Mike
> -----Original Message-----
> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Pedro Falcato
> Sent: Monday, August 16, 2021 11:07 AM
> To: devel@edk2.groups.io
> Cc: Pedro Falcato <pedro.falcato@gmail.com>; Leif Lindholm <leif@nuviainc.com>; Kinney, Michael D
> <michael.d.kinney@intel.com>; Bret Barkelew <Bret.Barkelew@microsoft.com>
> Subject: [edk2-devel] [Patch v4 0/3] Ext4Pkg: Add Ext4Pkg
>
> This patch-set adds Ext4Pkg, a package designed to hold various drivers and
> utilities related to the EXT4 filesystem.
>
> Right now, it holds a single read-only UEFI EXT4 driver (Ext4Dxe), which consumes the
> DISK_IO, BLOCK_IO and DISK_IO2 protocols and produce EFI_FILE_PROTOCOL and
> EFI_SIMPLE_FILE_SYSTEM_PROTOCOL; this driver allows the mounting of EXT4 partitions and
> the reading of their contents.
>
> A relevant RFC discussion, which includes a more in-depth walkthrough of EXT4 internals and
> driver limitations is available at https://edk2.groups.io/g/devel/topic/84368561.
>
> This patch set is version 4 and attempts to address issues raised by the
> community in v3's code review.
>
> Cc: Leif Lindholm <leif@nuviainc.com>
> Cc: Michael D Kinney <michael.d.kinney@intel.com>
> Cc: Bret Barkelew <Bret.Barkelew@microsoft.com>
>
> Pedro Falcato (3):
> Ext4Pkg: Add Ext4Pkg.dec and Ext4Pkg.uni.
> Ext4Pkg: Add Ext4Dxe driver.
> Ext4Pkg: Add .DSC file.
>
> Features/Ext4Pkg/Ext4Dxe/BlockGroup.c | 228 ++++++
> Features/Ext4Pkg/Ext4Dxe/Collation.c | 173 +++++
> Features/Ext4Pkg/Ext4Dxe/Crc16.c | 75 ++
> Features/Ext4Pkg/Ext4Dxe/Crc32c.c | 84 ++
> Features/Ext4Pkg/Ext4Dxe/Directory.c | 498 ++++++++++++
> Features/Ext4Pkg/Ext4Dxe/DiskUtil.c | 113 +++
> Features/Ext4Pkg/Ext4Dxe/Ext4Disk.h | 454 +++++++++++
> Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.c | 808 +++++++++++++++++++
> Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.h | 1038 +++++++++++++++++++++++++
> Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.inf | 149 ++++
> Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.uni | 15 +
> Features/Ext4Pkg/Ext4Dxe/Extents.c | 595 ++++++++++++++
> Features/Ext4Pkg/Ext4Dxe/File.c | 787 +++++++++++++++++++
> Features/Ext4Pkg/Ext4Dxe/Inode.c | 465 +++++++++++
> Features/Ext4Pkg/Ext4Dxe/Partition.c | 125 +++
> Features/Ext4Pkg/Ext4Dxe/Superblock.c | 297 +++++++
> Features/Ext4Pkg/Ext4Pkg.dec | 17 +
> Features/Ext4Pkg/Ext4Pkg.dsc | 70 ++
> Features/Ext4Pkg/Ext4Pkg.uni | 14 +
> 19 files changed, 6005 insertions(+)
> create mode 100644 Features/Ext4Pkg/Ext4Dxe/BlockGroup.c
> create mode 100644 Features/Ext4Pkg/Ext4Dxe/Collation.c
> create mode 100644 Features/Ext4Pkg/Ext4Dxe/Crc16.c
> create mode 100644 Features/Ext4Pkg/Ext4Dxe/Crc32c.c
> create mode 100644 Features/Ext4Pkg/Ext4Dxe/Directory.c
> create mode 100644 Features/Ext4Pkg/Ext4Dxe/DiskUtil.c
> create mode 100644 Features/Ext4Pkg/Ext4Dxe/Ext4Disk.h
> create mode 100644 Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.c
> create mode 100644 Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.h
> create mode 100644 Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.inf
> create mode 100644 Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.uni
> create mode 100644 Features/Ext4Pkg/Ext4Dxe/Extents.c
> create mode 100644 Features/Ext4Pkg/Ext4Dxe/File.c
> create mode 100644 Features/Ext4Pkg/Ext4Dxe/Inode.c
> create mode 100644 Features/Ext4Pkg/Ext4Dxe/Partition.c
> create mode 100644 Features/Ext4Pkg/Ext4Dxe/Superblock.c
> create mode 100644 Features/Ext4Pkg/Ext4Pkg.dec
> create mode 100644 Features/Ext4Pkg/Ext4Pkg.dsc
> create mode 100644 Features/Ext4Pkg/Ext4Pkg.uni
>
> --
> 2.32.0
>
>
>
>
>
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [edk2-devel] [Patch v4 0/3] Ext4Pkg: Add Ext4Pkg
2021-08-18 5:16 ` [edk2-devel] [Patch v4 0/3] Ext4Pkg: Add Ext4Pkg Michael D Kinney
@ 2021-08-18 5:20 ` Michael D Kinney
0 siblings, 0 replies; 6+ messages in thread
From: Michael D Kinney @ 2021-08-18 5:20 UTC (permalink / raw)
To: devel@edk2.groups.io, pedro.falcato@gmail.com, Kinney, Michael D
Cc: Leif Lindholm, Bret Barkelew
pushed to edk2-platforms/master
109380a7a8..c2ab6bab4b
Mike
> -----Original Message-----
> From: Kinney, Michael D <michael.d.kinney@intel.com>
> Sent: Tuesday, August 17, 2021 10:16 PM
> To: devel@edk2.groups.io; pedro.falcato@gmail.com; Kinney, Michael D <michael.d.kinney@intel.com>
> Cc: Leif Lindholm <leif@nuviainc.com>; Bret Barkelew <Bret.Barkelew@microsoft.com>
> Subject: RE: [edk2-devel] [Patch v4 0/3] Ext4Pkg: Add Ext4Pkg
>
> Series Reviewed-by: Michael D Kinney <michael.d.kinney@intel.com>
>
> Mike
>
> > -----Original Message-----
> > From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Pedro Falcato
> > Sent: Monday, August 16, 2021 11:07 AM
> > To: devel@edk2.groups.io
> > Cc: Pedro Falcato <pedro.falcato@gmail.com>; Leif Lindholm <leif@nuviainc.com>; Kinney, Michael D
> > <michael.d.kinney@intel.com>; Bret Barkelew <Bret.Barkelew@microsoft.com>
> > Subject: [edk2-devel] [Patch v4 0/3] Ext4Pkg: Add Ext4Pkg
> >
> > This patch-set adds Ext4Pkg, a package designed to hold various drivers and
> > utilities related to the EXT4 filesystem.
> >
> > Right now, it holds a single read-only UEFI EXT4 driver (Ext4Dxe), which consumes the
> > DISK_IO, BLOCK_IO and DISK_IO2 protocols and produce EFI_FILE_PROTOCOL and
> > EFI_SIMPLE_FILE_SYSTEM_PROTOCOL; this driver allows the mounting of EXT4 partitions and
> > the reading of their contents.
> >
> > A relevant RFC discussion, which includes a more in-depth walkthrough of EXT4 internals and
> > driver limitations is available at https://edk2.groups.io/g/devel/topic/84368561.
> >
> > This patch set is version 4 and attempts to address issues raised by the
> > community in v3's code review.
> >
> > Cc: Leif Lindholm <leif@nuviainc.com>
> > Cc: Michael D Kinney <michael.d.kinney@intel.com>
> > Cc: Bret Barkelew <Bret.Barkelew@microsoft.com>
> >
> > Pedro Falcato (3):
> > Ext4Pkg: Add Ext4Pkg.dec and Ext4Pkg.uni.
> > Ext4Pkg: Add Ext4Dxe driver.
> > Ext4Pkg: Add .DSC file.
> >
> > Features/Ext4Pkg/Ext4Dxe/BlockGroup.c | 228 ++++++
> > Features/Ext4Pkg/Ext4Dxe/Collation.c | 173 +++++
> > Features/Ext4Pkg/Ext4Dxe/Crc16.c | 75 ++
> > Features/Ext4Pkg/Ext4Dxe/Crc32c.c | 84 ++
> > Features/Ext4Pkg/Ext4Dxe/Directory.c | 498 ++++++++++++
> > Features/Ext4Pkg/Ext4Dxe/DiskUtil.c | 113 +++
> > Features/Ext4Pkg/Ext4Dxe/Ext4Disk.h | 454 +++++++++++
> > Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.c | 808 +++++++++++++++++++
> > Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.h | 1038 +++++++++++++++++++++++++
> > Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.inf | 149 ++++
> > Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.uni | 15 +
> > Features/Ext4Pkg/Ext4Dxe/Extents.c | 595 ++++++++++++++
> > Features/Ext4Pkg/Ext4Dxe/File.c | 787 +++++++++++++++++++
> > Features/Ext4Pkg/Ext4Dxe/Inode.c | 465 +++++++++++
> > Features/Ext4Pkg/Ext4Dxe/Partition.c | 125 +++
> > Features/Ext4Pkg/Ext4Dxe/Superblock.c | 297 +++++++
> > Features/Ext4Pkg/Ext4Pkg.dec | 17 +
> > Features/Ext4Pkg/Ext4Pkg.dsc | 70 ++
> > Features/Ext4Pkg/Ext4Pkg.uni | 14 +
> > 19 files changed, 6005 insertions(+)
> > create mode 100644 Features/Ext4Pkg/Ext4Dxe/BlockGroup.c
> > create mode 100644 Features/Ext4Pkg/Ext4Dxe/Collation.c
> > create mode 100644 Features/Ext4Pkg/Ext4Dxe/Crc16.c
> > create mode 100644 Features/Ext4Pkg/Ext4Dxe/Crc32c.c
> > create mode 100644 Features/Ext4Pkg/Ext4Dxe/Directory.c
> > create mode 100644 Features/Ext4Pkg/Ext4Dxe/DiskUtil.c
> > create mode 100644 Features/Ext4Pkg/Ext4Dxe/Ext4Disk.h
> > create mode 100644 Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.c
> > create mode 100644 Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.h
> > create mode 100644 Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.inf
> > create mode 100644 Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.uni
> > create mode 100644 Features/Ext4Pkg/Ext4Dxe/Extents.c
> > create mode 100644 Features/Ext4Pkg/Ext4Dxe/File.c
> > create mode 100644 Features/Ext4Pkg/Ext4Dxe/Inode.c
> > create mode 100644 Features/Ext4Pkg/Ext4Dxe/Partition.c
> > create mode 100644 Features/Ext4Pkg/Ext4Dxe/Superblock.c
> > create mode 100644 Features/Ext4Pkg/Ext4Pkg.dec
> > create mode 100644 Features/Ext4Pkg/Ext4Pkg.dsc
> > create mode 100644 Features/Ext4Pkg/Ext4Pkg.uni
> >
> > --
> > 2.32.0
> >
> >
> >
> >
> >
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2021-08-18 5:21 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2021-08-16 18:06 [Patch v4 0/3] Ext4Pkg: Add Ext4Pkg Pedro Falcato
2021-08-16 18:06 ` [Patch v4 1/3] Ext4Pkg: Add Ext4Pkg.dec and Ext4Pkg.uni Pedro Falcato
2021-08-16 18:06 ` [Patch v4 2/3] Ext4Pkg: Add Ext4Dxe driver Pedro Falcato
2021-08-16 18:06 ` [Patch v4 3/3] Ext4Pkg: Add .DSC file Pedro Falcato
2021-08-18 5:16 ` [edk2-devel] [Patch v4 0/3] Ext4Pkg: Add Ext4Pkg Michael D Kinney
2021-08-18 5:20 ` Michael D Kinney
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox