From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-vs1-f54.google.com (mail-vs1-f54.google.com [209.85.217.54]) by mx.groups.io with SMTP id smtpd.web08.22068.1627919802935937069 for ; Mon, 02 Aug 2021 08:56:43 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20161025 header.b=eQ6BOyO4; spf=pass (domain: gmail.com, ip: 209.85.217.54, mailfrom: pedro.falcato@gmail.com) Received: by mail-vs1-f54.google.com with SMTP id x144so980446vsx.3 for ; Mon, 02 Aug 2021 08:56:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc:content-transfer-encoding; bh=8oRYyHKhak+hf/2wZUHXHdjKRPa/Gwn/suAcGRr+8rs=; b=eQ6BOyO4r6LetCN1a2M0CVLMM2/ySU5t2r9PK5TasxVr6f5oHkmtGxr7RewyXDY+M4 ajL2kk/ZeQfU+VzF1MaBYQwNnb+KWy5winH8ZRSzUcZRnWOZo08NPOwwv4/zBWsOLX7l 4WL26sv1US+8+urHyPm4uIuxcUghAtiZSRYUyWDu2fH+7vVHfKJzY1d7tZJbmelKJ8Zi wLh9CqVH4me5QEj2uXHnHxw6aN3oHvFVv93Xbz8MBR3qDtRCDh5nDFlsHXI+zjbkQoBG DsxyDwo4H06XN+bIm9/3SxT61PdsTNSJrJv4wPYMiY/wm1eeA83r/2Wbta9Ie56nhOqd yYIw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc:content-transfer-encoding; bh=8oRYyHKhak+hf/2wZUHXHdjKRPa/Gwn/suAcGRr+8rs=; b=NkDRSW1ZVtlZBohcjUerq0FX7O2Q1ZJwa2IIUCf7aE4B+RunC6asPPyy7SjfwofP5u RPoKlig16oemA7+GYu7w81P/ICi4Sz/Ap93/i6b1KMg/iX/S/F0NEt+h6VjHg7XgLQ1j U3J011ZsK0AKZWWTgij+8dXqF5Eavr4N3AjUwrOJF0nzqyAoTawithwEV35YQ5NXxGL9 Z1pIYPg3nEv/9Ayrp3kldz8m/xfJN+GbnJo/mBZ8FD2e97HDrgMJgnTfLxxSAnVQVKO4 wO1sgbNpZr7M+0nOLcofbqbKwhBmSZ2n1p5cMM7M8g3rb3Z2qIUl0neIBDTQ1GzTasOE ltag== X-Gm-Message-State: AOAM533udd3/dhvjP5PPTQLVPArsng82uxOHmTbrSPAe2AgksnVH/fro 9HNH137+fZZJ/Q4IpAmQ2PdFs9QMQNEBCAymnSY= X-Google-Smtp-Source: ABdhPJwkSMFShfBfjrDpeUt30Qdqdx3fFS3knbxLfbgYDRLa7HjVQhRwwHt+doUHL7jlOk2ZuoU4QsyC432TwXQpFJg= X-Received: by 2002:a67:b40d:: with SMTP id x13mr4797975vsl.54.1627919801512; Mon, 02 Aug 2021 08:56:41 -0700 (PDT) MIME-Version: 1.0 References: <20210730161641.7245-1-pedro.falcato@gmail.com> <20210730161641.7245-3-pedro.falcato@gmail.com> In-Reply-To: From: "Pedro Falcato" Date: Mon, 2 Aug 2021 16:56:27 +0100 Message-ID: Subject: Re: [edk2-devel] [Patch 2/3] Ext4Pkg: Add Ext4Dxe driver. To: =?UTF-8?Q?Marvin_H=C3=A4user?= Cc: edk2-devel-groups-io , Leif Lindholm , Michael D Kinney , Bret Barkelew Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Hi Marvin, Thanks for the quick feedback! Most of your criticism seems valid. I'll take it into consideration for v2 of the patch-set. As for the nitpick in Collation.c, while I do understand what you mean by that, large parts of that file were mostly borrowed from FatPkg (such that I kept the original Intel copyright header); I think separating that code into a library of some sorts would be a good idea, I was completely lost when trying to figure out all the tiny Unicode/language details that don't seem to be documented anywhere (I think!). As for the EFI_OPEN_PROTOCOL_GET_PROTOCOL, I should have RTFM'd harder :) Best regards, Pedro On Fri, 30 Jul 2021 at 21:28, Marvin H=C3=A4user wrote= : > > Hey Pedro, > > Sorry, I went through this in a great rush, but I wanted to get some > feedback out asap to have enough time for discussion till end of GSoC. > Some comments are inline. > > Best regards, > Marvin > > On 30.07.21 18:16, Pedro Falcato wrote: > > Adds a UEFI EXT4 filesystem driver that implements the EFI_FILE_PROTOCO= L > > and EFI_SIMPLE_FILE_SYSTEM_PROTOCOL. > > > > Cc: Leif Lindholm > > Cc: Michael D Kinney > > Cc: Bret Barkelew > > > > Signed-off-by: Pedro Falcato > > --- > > Features/Ext4Pkg/Ext4Dxe/BlockGroup.c | 208 ++++++ > > Features/Ext4Pkg/Ext4Dxe/Collation.c | 157 +++++ > > Features/Ext4Pkg/Ext4Dxe/Crc16.c | 75 ++ > > Features/Ext4Pkg/Ext4Dxe/Crc32c.c | 84 +++ > > Features/Ext4Pkg/Ext4Dxe/Directory.c | 492 ++++++++++++++ > > Features/Ext4Pkg/Ext4Dxe/DiskUtil.c | 83 +++ > > Features/Ext4Pkg/Ext4Dxe/Ext4Disk.h | 450 ++++++++++++ > > Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.c | 454 +++++++++++++ > > Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.h | 942 +++++++++++++++++++++++++= + > > Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.inf | 147 ++++ > > Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.uni | 15 + > > Features/Ext4Pkg/Ext4Dxe/Extents.c | 616 +++++++++++++++++ > > Features/Ext4Pkg/Ext4Dxe/File.c | 583 ++++++++++++++++ > > Features/Ext4Pkg/Ext4Dxe/Inode.c | 468 +++++++++++++ > > Features/Ext4Pkg/Ext4Dxe/Partition.c | 120 ++++ > > Features/Ext4Pkg/Ext4Dxe/Superblock.c | 257 +++++++ > > 16 files changed, 5151 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/E= xt4Dxe/BlockGroup.c > > new file mode 100644 > > index 0000000000..10a82d40a0 > > --- /dev/null > > +++ b/Features/Ext4Pkg/Ext4Dxe/BlockGroup.c > > @@ -0,0 +1,208 @@ > > +/** > > + @file Block group related routines > > + > > + Copyright (c) 2021 Pedro Falcato All rights reserved. > > + Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved. > > + > > + SPDX-License-Identifier: BSD-2-Clause-Patent > > + */ > > + > > +#include "Ext4Dxe.h" > > + > > +/** > > + 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 point= er to the read inode. > > + > > + @return Status of the inode read. > > + */ > > +EFI_STATUS > > +Ext4ReadInode ( > > + IN EXT4_PARTITION *Partition, IN EXT4_INO_NR InodeNum, OUT EXT4_INOD= E **OutIno > > + ) > > +{ > > + UINT64 InodeOffset; > > + UINT32 BlockGroupNumber; > > + EXT4_INODE *Inode; > > + EXT4_BLOCK_GROUP_DESC *BlockGroup; > > + EXT4_BLOCK_NR InodeTableStart; > > + EFI_STATUS Status; > > + > > + BlockGroupNumber =3D (UINT32)DivU64x64Remainder ( > > + InodeNum - 1, > > + Partition->SuperBlock.s_inodes_per_grou= p, > > + &InodeOffset > > + ); > > + > > + // Check for the block group number's correctness > > + if (BlockGroupNumber >=3D Partition->NumberBlockGroups) { > > + return EFI_VOLUME_CORRUPTED; > > + } > > + > > + Inode =3D Ext4AllocateInode (Partition); > > + > > + if (Inode =3D=3D NULL) { > > + return EFI_OUT_OF_RESOURCES; > > + } > > + > > + BlockGroup =3D Ext4GetBlockGroupDesc (Partition, BlockGroupNumber); > > + > > + // Note: We'll need to check INODE_UNINIT and friends when we add wr= ite support > > + > > + InodeTableStart =3D Ext4MakeBlockNumberFromHalfs ( > > + Partition, > > + BlockGroup->bg_inode_table_lo, > > + BlockGroup->bg_inode_table_hi > > + ); > > + > > + Status =3D Ext4ReadDiskIo ( > > + Partition, > > + Inode, > > + Partition->InodeSize, > > + Ext4BlockToByteOffset (Partition, InodeTableStart) + Inod= eOffset * Partition->InodeSize > > + ); > > + > > + if (EFI_ERROR (Status)) { > > + DEBUG (( > > + EFI_D_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 (( > > + EFI_D_ERROR, > > + "[ext4] Inode %llu has invalid checksum (calculated %x)\n", > > + InodeNum, > > + Ext4CalculateInodeChecksum (Partition, Inode, InodeNum) > > + )); > > + FreePool (Inode); > > + return EFI_VOLUME_CORRUPTED; > > + } > > + > > + *OutIno =3D 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 partitio= n. > > + @param[in] BlockGroupDesc Pointer to the block group descript= or. > > + @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 =3D 0; > > + > > + Csum =3D Ext4CalculateChecksum (Partition, &BlockGroupNum, sizeof (B= lockGroupNum), Partition->InitialSeed); > > + Csum =3D Ext4CalculateChecksum (Partition, BlockGroupDesc, OFFSET_OF= (EXT4_BLOCK_GROUP_DESC, bg_checksum), Csum); > > + Csum =3D Ext4CalculateChecksum (Partition, &Dummy, sizeof (Dummy), C= sum); > > + Csum =3D > > + 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 partitio= n. > > + @param[in] BlockGroupDesc Pointer to the block group descript= or. > > + @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 =3D 0; > > + > > + Csum =3D CalculateCrc16 (Partition->SuperBlock.s_uuid, 16, 0); > > + Csum =3D CalculateCrc16 (&BlockGroupNum, sizeof (BlockGroupNum), Csu= m); > > + Csum =3D CalculateCrc16 (BlockGroupDesc, OFFSET_OF (EXT4_BLOCK_GROUP= _DESC, bg_checksum), Csum); > > + Csum =3D CalculateCrc16 (&Dummy, sizeof (Dummy), Csum); > > + Csum =3D > > + 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 partitio= n. > > + @param[in] BlockGroupDesc Pointer to the block group descript= or. > > + @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(!Ext4HasMetadataCsum (Partition) && !Ext4HasGdtCsum (Partition)) = { > > + return TRUE; > > + } > > + > > + return Ext4CalculateBlockGroupDescChecksum (Partition, BlockGroupDes= c, BlockGroupNum) =3D=3D BlockGroupDesc->bg_checksum; > > +} > > + > > +/** > > + Calculates the checksum of the block group descriptor. > > + @param[in] Partition Pointer to the opened EXT4 partitio= n. > > + @param[in] BlockGroupDesc Pointer to the block group descript= or. > > + @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_CSU= M) { > > + return Ext4CalculateBlockGroupDescChecksumMetadataCsum (Partition,= BlockGroupDesc, BlockGroupNum); > > + } else if(Partition->FeaturesRoCompat & EXT4_FEATURE_RO_COMPAT_GDT_C= SUM) { > > + return Ext4CalculateBlockGroupDescChecksumGdtCsum (Partition, Bloc= kGroupDesc, BlockGroupNum); > > + } > > + > > + return 0; > > +} > > diff --git a/Features/Ext4Pkg/Ext4Dxe/Collation.c b/Features/Ext4Pkg/Ex= t4Dxe/Collation.c > > new file mode 100644 > > index 0000000000..92d6a9184b > > --- /dev/null > > +++ b/Features/Ext4Pkg/Ext4Dxe/Collation.c > > @@ -0,0 +1,157 @@ > > +/** > > + @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 > > + > > +#include > > +#include > > +#include > > + > > +#include > > + > > +STATIC EFI_UNICODE_COLLATION_PROTOCOL *gUnicodeCollationInterface =3D= NULL; > > + > > +/* > > + * Note: This code is heavily based on FatPkg's Unicode collation, sin= ce they seem to know what > > + * they're doing. > > + * PS: Maybe all this code could be put in a library? It looks heavily= shareable. > > + */ > > +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; > > + > > + Iso639Language =3D (BOOLEAN)(ProtocolGuid =3D=3D &gEfiUnicodeCollati= onProtocolGuid); > > + RetStatus =3D EFI_UNSUPPORTED; > > + GetEfiGlobalVariable2 (VariableName, (VOID **)&Language, NULL); > > I know that this is done across EDK II drivers, however please do not > discard return results and rely on the function initialising all > parameters. C code traditionally is driven by returned results, i.e. the > function succeeded if and only if the returned status indicates success. > There are many reasons not to rely on this behaviour, most importantly: > 1) A future library class instance may not unconditionally initialise > the argument pointers. The function documentation has no mention of this > behaviour, and it is awkward. > 2) The callee may pass the parameters down to other functions and > accidentally initialise them with non-trivial values, making it look > like the function has succeeded when not considering the returned status. > 3) This is basically 2.1, the functions must all follow this pattern > down the call tree (or workarounds need to be implemented), which wastes > resources. > > > + > > + Status =3D gBS->LocateHandleBuffer ( > > + ByProtocol, > > + ProtocolGuid, > > + NULL, > > + &NumHandles, > > + &Handles > > + ); > > + if (EFI_ERROR (Status)) { > > + return Status; > > + } > > + > > + // Note: FatPkg also doesn't close unneeded protocols. > > + // This looks like a leak but I'm likely wrong. > > I'm not 100 % sure what you mean here, but in case you refer to below's > code, protocols retrieved by EFI_OPEN_PROTOCOL_GET_PROTOCOL do not need > to be closed as per UEFI 2.9, page 194. > > + for(UINTN i =3D 0; i < NumHandles; i++) { > > + Status =3D gBS->OpenProtocol ( > > + Handles[i], > > + ProtocolGuid, > > + (VOID **)&Uci, > > + DriverHandle, > > + NULL, > > + EFI_OPEN_PROTOCOL_GET_PROTOCOL > > + ); > > + > > + if(EFI_ERROR (Status)) { > > + continue; > > + } > > + > > + CHAR8 *BestLanguage =3D GetBestLanguage ( > > + Uci->SupportedLanguages, > > + Iso639Language, > > + (Language =3D=3D NULL) ? "" : Language, > > + DefaultLanguage, > > + NULL > > + ); > > + if (BestLanguage !=3D NULL) { > > + FreePool (BestLanguage); > > + gUnicodeCollationInterface =3D Uci; > > + RetStatus =3D EFI_SUCCESS; > > + break; > > + } > > + } > > + > > + if (Language !=3D 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 file= name comparison). > > + > > + @param[in] DriverHandle Handle to the driver image. > > + > > + @retval EFI_SUCCESS Unicode collation was successfully initialise= d. > > + @retval !EFI_SUCCESS Failure. > > +*/ > > +EFI_STATUS > > +Ext4InitialiseUnicodeCollation ( > > + EFI_HANDLE DriverHandle > > + ) > > +{ > > + EFI_STATUS Status; > > + > > + Status =3D EFI_UNSUPPORTED; > > + > > + // > > + // First try to use RFC 4646 Unicode Collation 2 Protocol. > > + // > > + Status =3D Ext4InitialiseUnicodeCollationInternal ( > > + DriverHandle, > > + &gEfiUnicodeCollation2ProtocolGuid, > > + L"PlatformLang", > > + (CONST CHAR8 *)PcdGetPtr (PcdUefiVariableDefaultPlatformL= ang) > > + ); > > + // > > + // 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 =3D Ext4InitialiseUnicodeCollationInternal ( > > + DriverHandle, > > + &gEfiUnicodeCollationProtocolGuid, > > + L"Lang", > > + (CONST CHAR8 *)PcdGetPtr (PcdUefiVariableDefaultLang) > > + ); > > + } > > + > > + return Status; > > +} > > + > > +/** > > + Does a case-insensitive string comparison. Refer to EFI_UNICODE_COL= LATION_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 (gUnicodeCollationInterf= ace, Str1, Str2); > > +} > > diff --git a/Features/Ext4Pkg/Ext4Dxe/Crc16.c b/Features/Ext4Pkg/Ext4Dx= e/Crc16.c > > new file mode 100644 > > index 0000000000..25a11cfde3 > > --- /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 > > + > > +STATIC CONST UINT16 gCrc16LookupTable[256] =3D > > +{ > > + 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 =3D Buffer; > > + > > + Crc =3D ~InitialValue; > > + > > + while(Length-- !=3D 0) { > > + Crc =3D gCrc16LookupTable[(Crc & 0xFF) ^ *(Buf++)] ^ (Crc >> 8); > > + } > > + > > + return ~Crc; > > +} > > diff --git a/Features/Ext4Pkg/Ext4Dxe/Crc32c.c b/Features/Ext4Pkg/Ext4D= xe/Crc32c.c > > new file mode 100644 > > index 0000000000..3986c9b10f > > --- /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 > > + > > +STATIC CONST UINT32 gCrc32cLookupTable[256] =3D { > > + 0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, 0xc79a971f, 0x35f114= 1c, > > + 0x26a1e7e8, 0xd4ca64eb, 0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab= 3b, > > + 0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24, 0x105ec76f, 0xe23544= 6c, > > + 0xf165b798, 0x030e349b, 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a3= 84, > > + 0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, 0x5d1d08bf, 0xaf768b= bc, > > + 0xbc267848, 0x4e4dfb4b, 0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d= 2a, > > + 0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35, 0xaa64d611, 0x580f55= 12, > > + 0x4b5fa6e6, 0xb93425e5, 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2= fa, > > + 0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, 0xf779deae, 0x05125d= ad, > > + 0x1642ae59, 0xe4292d5a, 0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae2= 8a, > > + 0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595, 0x417b1dbc, 0xb3109e= bf, > > + 0xa0406d4b, 0x522bee48, 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b179= 57, > > + 0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, 0x0c38d26c, 0xfe5351= 6f, > > + 0xed03a29b, 0x1f682198, 0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x427529= 27, > > + 0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38, 0xdbfc821c, 0x299701= 1f, > > + 0x3ac7f2eb, 0xc8ac71e8, 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6= f7, > > + 0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, 0xa65c047d, 0x543787= 7e, > > + 0x4767748a, 0xb50cf789, 0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f38= 59, > > + 0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46, 0x7198540d, 0x83f3d7= 0e, > > + 0x90a324fa, 0x62c8a7f9, 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230= e6, > > + 0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36, 0x3cdb9bdd, 0xceb018= de, > > + 0xdde0eb2a, 0x2f8b6829, 0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c8= 8c, > > + 0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93, 0x082f63b7, 0xfa44e0= b4, > > + 0xe9141340, 0x1b7f9043, 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce507= 5c, > > + 0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, 0x55326b08, 0xa759e8= 0b, > > + 0xb4091bff, 0x466298fc, 0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b2157= 2c, > > + 0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033, 0xa24bb5a6, 0x502036= a5, > > + 0x4370c551, 0xb11b4652, 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d1= 4d, > > + 0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, 0xef087a76, 0x1d63f9= 75, > > + 0x0e330a81, 0xfc588982, 0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa14581= 3d, > > + 0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622, 0x38cc2a06, 0xcaa7a9= 05, > > + 0xd9f75af1, 0x2b9cd9f2, 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064e= ed, > > + 0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, 0x0417b1db, 0xf67c32= d8, > > + 0xe52cc12c, 0x1747422f, 0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048d= ff, > > + 0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0, 0xd3d3e1ab, 0x21b862= a8, > > + 0x32e8915c, 0xc083125f, 0x144976b4, 0xe622f5b7, 0xf5720643, 0x071985= 40, > > + 0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, 0x9e902e7b, 0x6cfbad= 78, > > + 0x7fab5e8c, 0x8dc0dd8f, 0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605b= ee, > > + 0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1, 0x69e9f0d5, 0x9b8273= d6, > > + 0x88d28022, 0x7ab90321, 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd2394= 3e, > > + 0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81, 0x34f4f86a, 0xc69f7b= 69, > > + 0xd5cf889d, 0x27a40b9e, 0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c4= 4e, > > + 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 =3D Buffer; > > + Crc =3D ~InitialValue; > > + > > + while(Length-- !=3D 0) { > > + Crc =3D gCrc32cLookupTable[(Crc & 0xFF) ^ *(Buf++)] ^ (Crc >> 8); > > + } > > + > > + return ~Crc; > > +} > > diff --git a/Features/Ext4Pkg/Ext4Dxe/Directory.c b/Features/Ext4Pkg/Ex= t4Dxe/Directory.c > > new file mode 100644 > > index 0000000000..caa97cf9f1 > > --- /dev/null > > +++ b/Features/Ext4Pkg/Ext4Dxe/Directory.c > > @@ -0,0 +1,492 @@ > > +/** > > + @file Directory related routines > > + > > + Copyright (c) 2021 Pedro Falcato All rights reserved. > > + > > + SPDX-License-Identifier: BSD-2-Clause-Patent > > + */ > > + > > +#include "Ext4Dxe.h" > > + > > +#include > > + > > +/** > > + Retrieves the filename of the directory entry and converts it to UT= F-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 co= nverted 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] =3D '\0'; > > + > > + // Unfortunately, BaseUcs2Utf8Lib doesn't have a convert-buffer-to-b= uffer-like > > + // function. Therefore, we need to allocate from the pool (inside UT= F8StrToUCS2), > > + // copy it to our out buffer (Ucs2FileName) and free. > > Maybe that's a good idea to propose then? Dynamic memory allocations are > never nice. :) > > > + Status =3D UTF8StrToUCS2 (Utf8NameBuf, &Str); > > + > > + if (EFI_ERROR (Status)) { > > + return Status; > > + } > > + > > + Status =3D 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 en= try. > > + > > + @return The result of the operation. > > +*/ > > +EFI_STATUS > > +Ext4RetrieveDirent ( > > + IN EXT4_FILE *File, > > + IN CONST CHAR16 *Name, > > + IN EXT4_PARTITION *Partition, > > + OUT EXT4_DIR_ENTRY *res > > + ) > > +{ > > + EFI_STATUS Status; > > + CHAR8 *Buf; > > + UINT64 Off; > > + EXT4_INODE *Inode; > > + UINT64 DirInoSize; > > + UINT32 BlockRemainder; > > + > > + Status =3D EFI_NOT_FOUND; > > + Buf =3D AllocatePool (Partition->BlockSize); > > + > > + if(Buf =3D=3D NULL) { > > + return EFI_OUT_OF_RESOURCES; > > + } > > + > > + Off =3D 0; > > + > > + Inode =3D File->Inode; > > + DirInoSize =3D EXT4_INODE_SIZE (Inode); > > + > > + DivU64x32Remainder (DirInoSize, Partition->BlockSize, &BlockRemainde= r); > > + if(BlockRemainder !=3D 0) { > > + // Directory inodes need to have block aligned sizes > > + return EFI_VOLUME_CORRUPTED; > > + } > > + > > + while(Off < DirInoSize) { > > + UINTN Length; > > Variable not declared at function entry. > > > + > > + Length =3D Partition->BlockSize; > > + > > + Status =3D Ext4Read (Partition, File, Buf, Off, &Length); > > + > > + if (Status !=3D EFI_SUCCESS) { > > + FreePool (Buf); > > + return Status; > > + } > > + > > + for(CHAR8 *b =3D Buf; b < Buf + Partition->BlockSize; ) { > > + EXT4_DIR_ENTRY *Entry; > > + UINTN RemainingBlock; > > + > > + Entry =3D (EXT4_DIR_ENTRY *)b; > > + ASSERT (Entry->rec_len !=3D 0); > > + > > + RemainingBlock =3D Partition->BlockSize - (b - Buf); > > I think this would be easier to read and verify if the loop variant as > RemainingBlock. > > > + > > + if(Entry->name_len > RemainingBlock || Entry->rec_len > Remainin= gBlock) { > > + // Corrupted filesystem > > + // TODO: Do the proper ext4 corruption detection thing and dir= ty the 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. > > When in doubt, say it's for security. :) An upper limit gives you a > definite guarantee you need to (and can) test your code against, you can > avoid dynamic memory allocations and their classes of potential > exploits, etc... > Also, I'm not sure C++ comments are permitted in function bodies, at > least I have not seen them. > > > + 2) Linux and a number of BSDs also have a filename limit of 25= 5. > > + */ > > + if(Entry->name_len > EXT4_NAME_MAX) { > > + continue; > > + } > > + > > + // Unused entry > > + if(Entry->inode =3D=3D 0) { > > + b +=3D Entry->rec_len; > > + continue; > > + } > > + > > + CHAR16 Ucs2FileName[EXT4_NAME_MAX + 1]; > > I have seen this type a lot now, maybe it'd be a good idea to typedef it? > > > + > > + Status =3D Ext4GetUcs2DirentName (Entry, Ucs2FileName); > > + > > + /* 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 bag= s 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 thin= k there's a precedent here. > > + b +=3D Entry->rec_len; > > + continue; > > + } > > + > > + if (Entry->name_len =3D=3D StrLen (Name) && > > + !Ext4StrCmpInsensitive (Ucs2FileName, (CHAR16 *)Name)) { > > + UINTN ToCopy; > > + > > + ToCopy =3D Entry->rec_len > sizeof (EXT4_DIR_ENTRY) ? > > + sizeof (EXT4_DIR_ENTRY) : > > + Entry->rec_len; > > Please use MIN(). > > > + > > + CopyMem (res, Entry, ToCopy); > > + FreePool (Buf); > > + return EFI_SUCCESS; > > + } > > + > > + b +=3D Entry->rec_len; > > + } > > + > > + Off +=3D 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 b= e 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 =3D AllocateZeroPool (sizeof (EXT4_FILE)); > > + > > + if (File =3D=3D NULL) { > > + Status =3D EFI_OUT_OF_RESOURCES; > > + goto Error; > > + } > > + > > + Status =3D Ext4GetUcs2DirentName (Entry, FileName); > > + > > + if (EFI_ERROR (Status)) { > > + goto Error; > > + } > > + > > + File->FileName =3D AllocateZeroPool (StrSize (FileName)); > > + > > + if (!File->FileName) { > > + Status =3D EFI_OUT_OF_RESOURCES; > > + goto Error; > > + } > > + > > + Status =3D Ext4InitExtentsMap (File); > > + > > + if (EFI_ERROR (Status)) { > > + goto Error; > > + } > > + > > + // This should not fail. > > + StrCpyS (File->FileName, EXT4_NAME_MAX + 1, FileName); > > Maybe use ARRAY_SIZE to avoid size constants? > > > + > > + File->InodeNum =3D Entry->inode; > > + > > + Ext4SetupFile (File, (EXT4_PARTITION *)Partition); > > Is Partition not already this type? > > > + > > + Status =3D Ext4ReadInode (Partition, Entry->inode, &File->Inode); > > + > > + if (EFI_ERROR (Status)) { > > + goto Error; > > + } > > + > > + *OutFile =3D File; > > + > > + InsertTailList (&Partition->OpenFiles, &File->OpenFilesListNode); > > + > > + return EFI_SUCCESS; > > + > > +Error: > > + if (File !=3D NULL) { > > + if (File->FileName !=3D NULL) { > > + FreePool (File->FileName); > > + } > > + > > + if (File->ExtentsMap !=3D 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 b= e 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 =3D 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 =3D=3D Directory->InodeNum) { > > + return EFI_NOT_FOUND; > > + } > > + > > + return Ext4OpenDirent (Partition, OpenMode, OutFile, &Entry); > > +} > > + > > +EFI_STATUS EFIAPI > > +Ext4OpenVolume ( > > + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Partition, EFI_FILE_PROTOCOL **Root > > + ) > > +{ > > + EXT4_INODE *RootInode; > > + EFI_STATUS Status; > > + > > + Status =3D Ext4ReadInode ((EXT4_PARTITION *)Partition, 2, &RootInode= ); > > + > > + if(EFI_ERROR (Status)) { > > + DEBUG ((EFI_D_ERROR, "[ext4] Could not open root inode - status %x= \n", Status)); > > + return Status; > > + } > > + > > + EXT4_FILE *RootDir =3D AllocateZeroPool (sizeof (EXT4_FILE)); > > + > > + if(!RootDir) { > > + FreePool (RootInode); > > + return EFI_OUT_OF_RESOURCES; > > + } > > + > > + // The filename will be "\"(null terminated of course) > > + RootDir->FileName =3D AllocateZeroPool (2 * sizeof (CHAR16)); > > + > > + if (!RootDir->FileName) { > > + FreePool (RootDir); > > + FreePool (RootInode); > > + return EFI_OUT_OF_RESOURCES; > > + } > > + > > + RootDir->FileName[0] =3D L'\\'; > > + > > + RootDir->Inode =3D RootInode; > > + RootDir->InodeNum =3D 2; > > + > > + if (EFI_ERROR (Ext4InitExtentsMap (RootDir))) { > > I'm not sure it's allowed to call functions in such expressions, but > if-clauses with side-effects are generally error-prone and hard to read > in my opinion. > > > + FreePool (RootDir->FileName); > > + FreePool (RootInode); > > + FreePool (RootDir); > > + return EFI_OUT_OF_RESOURCES; > > + } > > + > > + Ext4SetupFile (RootDir, (EXT4_PARTITION *)Partition); > > + *Root =3D &RootDir->Protocol; > > + > > + InsertTailList (&((EXT4_PARTITION *)Partition)->OpenFiles, &RootDir-= >OpenFilesListNode); > > Casting like this is unusual in EDK II, usually the private part of your > structure would start with a Signature and you would use CR() to get a > pointer to the private part. > > > + > > + 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 =3D Dirent->name_len + EXT4_MIN_DIR_ENTRY_LEN; > > + > > + if (Dirent->rec_len < RequiredSize) { > > + DEBUG ((EFI_D_ERROR, "[ext4] dirent size %lu too small (compared t= o %lu)\n", Dirent->rec_len, RequiredSize)); > > I *think* "EFI_D_" is considered legacy in favour of "DEBUG_", but not > 100 % sure. > > > + return FALSE; > > + } > > + > > + // Dirent sizes need to be 4 byte aligned > > + if (Dirent->rec_len % 4) { > > The offset is used for casts to EXT4_DIR_ENTRY, maybe add a > STATIC_ASSERT the alignment is sufficient? There is no macro for type > alignment yet, but I will submit it soon, so maybe add a TODO if you agre= e. > > > + 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 le= ngth of the buffer, > > + and the length of the actual EFI_FILE_I= NFO 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 > > + ) > > +{ > > + DEBUG ((EFI_D_INFO, "[ext4] Ext4ReadDir offset %lu\n", Offset)); > > + EXT4_INODE *DirIno; > > + EFI_STATUS Status; > > + UINT64 DirInoSize; > > + UINTN Len; > > + UINT32 BlockRemainder; > > + EXT4_DIR_ENTRY Entry; > > + > > + DirIno =3D File->Inode; > > + Status =3D EFI_SUCCESS; > > + DirInoSize =3D Ext4InodeSize (DirIno); > > + > > + DivU64x32Remainder (DirInoSize, Partition->BlockSize, &BlockRemainde= r); > > + if(BlockRemainder !=3D 0) { > > + // Directory inodes need to have block aligned sizes > > + return EFI_VOLUME_CORRUPTED; > > + } > > + > > + while(TRUE) { > > + EXT4_FILE *TempFile; > > + > > + TempFile =3D NULL; > > + > > + // We (try to) read the maximum size of a directory entry at a tim= e > > + // Note that we don't need to read any padding that may exist afte= r it. > > + Len =3D sizeof (Entry); > > + Status =3D Ext4Read (Partition, File, &Entry, Offset, &Len); > > + > > + if (EFI_ERROR (Status)) { > > + goto Out; > > + } > > + > > + #if 0 > > + DEBUG ((EFI_D_INFO, "[ext4] Length read %lu, offset %lu\n", Len,= Offset)); > > + #endif > > + > > + if (Len =3D=3D 0) { > > + *OutLength =3D 0; > > + Status =3D EFI_SUCCESS; > > + goto Out; > > + } > > + > > + if (Len < EXT4_MIN_DIR_ENTRY_LEN) { > > + Status =3D EFI_VOLUME_CORRUPTED; > > + goto Out; > > + } > > + > > + // Invalid directory entry length > > + if (!Ext4ValidDirent (&Entry)) { > > + DEBUG ((EFI_D_ERROR, "[ext4] Invalid dirent at offset %lu\n", Of= fset)); > > + Status =3D EFI_VOLUME_CORRUPTED; > > + goto Out; > > + } > > + > > + DEBUG ((EFI_D_INFO, "[ext4] dirent size %lu\n", Entry.rec_len)); > > + > > + if (Entry.inode =3D=3D 0) { > > + // When inode =3D 0, it's unused > > + Offset +=3D Entry.rec_len; > > + continue; > > + } > > + > > + Status =3D Ext4OpenDirent (Partition, EFI_FILE_MODE_READ, &TempFil= e, &Entry); > > + > > + if (EFI_ERROR (Status)) { > > + goto Out; > > + } > > + > > + // TODO: Is this needed? > > + if (!StrCmp (TempFile->FileName, L".") || !StrCmp (TempFile->FileN= ame, L"..")) { > > Integer comparisons with boolean operators are disallowed, please > compare against int literals (-> "!=3D 0"). > > > + Offset +=3D Entry.rec_len; > > + Ext4CloseInternal (TempFile); > > + continue; > > + } > > + > > + #if 0 > > + DEBUG ((EFI_D_INFO, "[ext4] Listing file %s\n", TempFile->FileNa= me)); > > + #endif > > + > > + Status =3D Ext4GetFileInfo (TempFile, Buffer, OutLength); > > + if (!EFI_ERROR (Status)) { > > + File->Position =3D Offset + Entry.rec_len; > > + } > > + > > + Ext4CloseInternal (TempFile); > > + > > + break; > > + } > > + > > + Status =3D EFI_SUCCESS; > > +Out: > > + return Status; > > +} > > diff --git a/Features/Ext4Pkg/Ext4Dxe/DiskUtil.c b/Features/Ext4Pkg/Ext= 4Dxe/DiskUtil.c > > new file mode 100644 > > index 0000000000..1cafdd64cd > > --- /dev/null > > +++ b/Features/Ext4Pkg/Ext4Dxe/DiskUtil.c > > @@ -0,0 +1,83 @@ > > +/** > > + @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 rea= d. > > + > > + @return Success status of the disk read. > > + */ > > +EFI_STATUS > > +Ext4ReadDiskIo ( > > + IN EXT4_PARTITION *Partition, > > + OUT VOID *Buffer, > > + IN UINTN Length, > > + IN UINT64 Offset > > + ) > > +{ > > + return Ext4DiskIo (Partition)->ReadDisk (Ext4DiskIo (Partition), Ext= 4MediaId (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, > > Nitpicking, but I'd say any "IN" pointer should be CONST. > > > + OUT VOID *Buffer, > > + IN UINTN NumberBlocks, > > + IN EXT4_BLOCK_NR BlockNumber > > + ) > > +{ > > + return Ext4ReadDiskIo (Partition, Buffer, NumberBlocks * Partition->= BlockSize, BlockNumber * Partition->BlockSize); > > I've seen a few multiplications, how do we know they do not wrap around? > > > +} > > + > > +/** > > + 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 t= he process > > + failed. > > + */ > > +VOID * > > +Ext4AllocAndReadBlocks ( > > + IN EXT4_PARTITION *Partition, > > + IN UINTN NumberBlocks, > > + IN EXT4_BLOCK_NR BlockNumber > > + ) > > +{ > > + VOID *Buf; > > + > > + Buf =3D AllocatePool (NumberBlocks * Partition->BlockSize); > > + > > + if(Buf =3D=3D NULL) { > > + return NULL; > > + } > > + > > + if(Ext4ReadBlocks (Partition, Buf, NumberBlocks, BlockNumber) !=3D E= FI_SUCCESS) { > > + FreePool (Buf); > > + return NULL; > > + } > > + > > + return Buf; > > +} > > diff --git a/Features/Ext4Pkg/Ext4Dxe/Ext4Disk.h b/Features/Ext4Pkg/Ext= 4Dxe/Ext4Disk.h > > new file mode 100644 > > index 0000000000..d790e70be1 > > --- /dev/null > > +++ b/Features/Ext4Pkg/Ext4Dxe/Ext4Disk.h > > @@ -0,0 +1,450 @@ > > +/** > > + @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 fi= lesystem, > > + 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 gener= al. > > + Here, we get to know the size of the filesystem's blocks, which f= eatures > > + 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 gr= oup covers > > + s_blocks_per_group(8 * Block Size) blocks. Each block group has a= n > > + associated block group descriptor; these are present directly aft= er 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 pr= eviously). > > + > > + 3) Blocks > > + The ext4 filesystem is divided in blocks, of size s_log_block_siz= e ^ 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 re= ad as > > + a block full of zeros. > > + > > + 4) Inodes > > + The ext4 filesystem divides files/directories into inodes (origin= ally > > + index nodes). Each file/socket/symlink/directory/etc (here on out= referred > > + to as a file, since there is no distinction under the ext4 filesy= stem) 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 i= f it's > > + an old filesystem), and holds various metadata about the file. Si= nce 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 represent= ed by a > > + single extent structure, which minimizes filesystem metadata bloa= t and > > + speeds up block mapping (particularly due to the fact that high-q= uality > > + ext4 implementations like linux's try /really/ hard to make the f= ile > > + contiguous, so it's common to have files with almost 0 fragmentat= ion). > > + 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 !=3D= 0 and > > + EXT4_EXTENT on eh_depth =3D 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 mea= ns ext4 > > + inodes do not themselves have names, since they can be linked (pr= esent) > > + multiple times with different names. Directories can store entrie= s 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 compati= ble 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 des= cribed > > + in detail in the Linux kernel's documentation. > > + */ > > + > > +#ifndef _EXT4_DISK_H > > +#define _EXT4_DISK_H > > + > > +#include > > + > > +#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 no= w > > +#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 implem= ented 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 filesyste= ms > > + * 2) Relevant RO_COMPATs(I'm not sure of what to do wrt quota, pro= ject) > > + */ > > + > > +#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 >=3D 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) =3D=3D 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) =3D=3D 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 _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; > > + union { > > + // Note: Toolchain-specific defines (such as "linux") stops us fro= m using simpler names down here. > > + 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; > > + } data_linux; > > + > > + 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; > > + } data_hurd; > > + } 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 hig= h 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; > > + > > +#define EXT4_INODE_SIZE(ino) (((UINT64)ino->i_size_hi << 32) | ino->i= _size_lo) > > + > > +#endif > > diff --git a/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.c b/Features/Ext4Pkg/Ext4= Dxe/Ext4Dxe.c > > new file mode 100644 > > index 0000000000..d1e289f8fa > > --- /dev/null > > +++ b/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.c > > @@ -0,0 +1,454 @@ > > +/** > > + @file Driver entry point > > + > > + Copyright (c) 2021 Pedro Falcato All rights reserved. > > + SPDX-License-Identifier: BSD-2-Clause-Patent > > + */ > > + > > +#include "Ext4Dxe.h" > > + > > +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mExt4DriverNam= eTable[] =3D { > > + { > > + "eng;en", > > + L"Ext4 File System Driver" > > + }, > > + { > > + NULL, > > + NULL > > + } > > +}; > > + > > +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mExt4Controlle= rNameTable[] =3D { > > + { > > + "eng;en", > > + L"Ext4 File System" > > + }, > > + { > > + NULL, > > + NULL > > + } > > +}; > > + > > +// Needed by gExt4ComponentName* > > + > > +EFI_STATUS > > +EFIAPI > > +Ext4ComponentNameGetDriverName ( > > + IN EFI_COMPONENT_NAME_PROTOCOL *This, > > + IN CHAR8 *Language, > > + OUT CHAR16 **DriverName > > + ); > > + > > +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 gExt4Compon= entName =3D { > > + Ext4ComponentNameGetDriverName, > > + Ext4ComponentNameGetControllerName, > > + "eng" > > +}; > > + > > +// > > +// EFI Component Name 2 Protocol > > +// > > +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gExt4Compo= nentName2 =3D { > > + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME)Ext4ComponentNameGetDriverName, > > + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME)Ext4ComponentNameGetControl= lerName, > > + "en" > > +}; > > + > > +// Needed by gExt4BindingProtocol > > + > > +EFI_STATUS EFIAPI > > +Ext4IsBindingSupported ( > > + IN EFI_DRIVER_BINDING_PROTOCOL *BindingProtocol, > > + IN EFI_HANDLE ControllerHandle, > > + IN EFI_DEVICE_PATH *RemainingDevicePath OPTIONAL > > + ); > > + > > +EFI_STATUS EFIAPI > > +Ext4Bind ( > > + IN EFI_DRIVER_BINDING_PROTOCOL *BindingProtocol, > > + IN EFI_HANDLE ControllerHandle, > > + IN EFI_DEVICE_PATH *RemainingDevicePath OPTIONAL > > + ); > > + > > +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 =3D > > +{ > > + Ext4IsBindingSupported, > > + Ext4Bind, > > + Ext4Stop, > > + EXT4_DRIVER_VERSION > > +}; > > + > > +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 > > + ) > > +{ > > + // TODO: Do we need to test whether we're managing the handle, like = FAT does? > > + return LookupUnicodeString2 ( > > + Language, > > + This->SupportedLanguages, > > + mExt4ControllerNameTable, > > + ControllerName, > > + (BOOLEAN)(This =3D=3D &gExt4ComponentName) > > + ); > > +} > > + > > +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 =3D=3D &gExt4ComponentName) > > + ); > > +} > > + > > +EFI_STATUS EFIAPI > > +Ext4IsBindingSupported ( > > + IN EFI_DRIVER_BINDING_PROTOCOL *BindingProtocol, > > + IN EFI_HANDLE ControllerHandle, > > + IN EFI_DEVICE_PATH *RemainingDevicePath OPTIONAL > > + ); > > + > > +EFI_STATUS EFIAPI > > +Ext4Bind ( > > + IN EFI_DRIVER_BINDING_PROTOCOL *BindingProtocol, > > + IN EFI_HANDLE ControllerHandle, > > + IN EFI_DEVICE_PATH *RemainingDevicePath OPTIONAL > > + ); > > + > > +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 =3D gBS->OpenProtocol ( > > + ControllerHandle, > > + &gEfiSimpleFileSystemProtocolGuid, > > + (VOID **)&Sfs, > > + This->DriverBindingHandle, > > + ControllerHandle, > > + EFI_OPEN_PROTOCOL_GET_PROTOCOL > > + ); > > + > > + if (EFI_ERROR (Status)) { > > + return Status; > > + } > > + > > + Partition =3D (EXT4_PARTITION *)Sfs; > > + > > + HasDiskIo2 =3D Ext4DiskIo2 (Partition) !=3D NULL; > > + > > + Status =3D Ext4UnmountAndFreePartition (Partition); > > + > > + if (EFI_ERROR (Status)) { > > + return Status; > > + } > > + > > + Status =3D gBS->UninstallMultipleProtocolInterfaces ( > > + ControllerHandle, > > + &gEfiSimpleFileSystemProtocolGuid, > > + &Partition->Interface, > > + NULL > > + ); > > + > > + if (EFI_ERROR (Status)) { > > + return Status; > > + } > > + > > + // Close all open protocols (DiskIo, DiskIo2, BlockIo) > > + > > + Status =3D gBS->CloseProtocol ( > > + ControllerHandle, > > + &gEfiDiskIoProtocolGuid, > > + This->DriverBindingHandle, > > + ControllerHandle > > + ); > > + > > + if (EFI_ERROR (Status)) { > > + return Status; > > + } > > + > > + Status =3D gBS->CloseProtocol ( > > + ControllerHandle, > > + &gEfiBlockIoProtocolGuid, > > + This->DriverBindingHandle, > > + ControllerHandle > > + ); > > + > > + if (EFI_ERROR (Status)) { > > + return Status; > > + } > > + > > + if(HasDiskIo2) { > > + Status =3D gBS->CloseProtocol ( > > + ControllerHandle, > > + &gEfiDiskIo2ProtocolGuid, > > + This->DriverBindingHandle, > > + ControllerHandle > > + ); > > + > > + if (EFI_ERROR (Status)) { > > + return Status; > > + } > > + } > > + > > + return Status; > > +} > > + > > +EFI_STATUS > > +EFIAPI > > +Ext4EntryPoint ( > > + IN EFI_HANDLE ImageHandle, > > + IN EFI_SYSTEM_TABLE *SystemTable > > + ) > > +{ > > + EFI_STATUS Status; > > + > > + Status =3D EfiLibInstallAllDriverProtocols2 ( > > + ImageHandle, > > + SystemTable, > > + &gExt4BindingProtocol, > > + ImageHandle, > > + &gExt4ComponentName, > > + &gExt4ComponentName2, > > + NULL, > > + NULL, > > + NULL, > > + NULL > > + ); > > + > > + if(EFI_ERROR (Status)) { > > + return Status; > > + } > > + > > + return Ext4InitialiseUnicodeCollation (ImageHandle); > > +} > > + > > +EFI_STATUS > > +EFIAPI > > +Ext4Unload ( > > + IN EFI_HANDLE ImageHandle > > + ) > > +{ > > + EFI_STATUS Status; > > + EFI_HANDLE *DeviceHandleBuffer; > > + UINTN DeviceHandleCount; > > + UINTN Index; > > + > > + Status =3D gBS->LocateHandleBuffer ( > > + AllHandles, > > + NULL, > > + NULL, > > + &DeviceHandleCount, > > + &DeviceHandleBuffer > > + ); > > + if (EFI_ERROR (Status)) { > > + return Status; > > + } > > + > > + for(Index =3D 0; Index < DeviceHandleCount; Index++) { > > + EFI_HANDLE Handle; > > + > > + Handle =3D DeviceHandleBuffer[Index]; > > + > > + Status =3D EfiTestManagedDevice (Handle, ImageHandle, &gEfiDiskIoP= rotocolGuid); > > + > > + if(Status =3D=3D EFI_SUCCESS) { > > + Status =3D gBS->DisconnectController (Handle, ImageHandle, NULL)= ; > > + > > + if (EFI_ERROR (Status)) { > > + break; > > + } > > + } > > + } > > + > > + FreePool (DeviceHandleBuffer); > > + > > + Status =3D EfiLibUninstallAllDriverProtocols2 ( > > + &gExt4BindingProtocol, > > + &gExt4ComponentName, > > + &gExt4ComponentName2, > > + NULL, > > + NULL, > > + NULL, > > + NULL > > + ); > > + > > + return Status; > > +} > > + > > +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 t= he > > + // protocol and ignore the output argument entirely > > + > > + EFI_STATUS Status; > > + > > + Status =3D gBS->OpenProtocol ( > > + ControllerHandle, > > + &gEfiDiskIoProtocolGuid, > > + NULL, > > + BindingProtocol->ImageHandle, > > + ControllerHandle, > > + EFI_OPEN_PROTOCOL_TEST_PROTOCOL > > + ); > > + > > + if(EFI_ERROR (Status)) { > > + return Status; > > + } > > + > > + Status =3D gBS->OpenProtocol ( > > + ControllerHandle, > > + &gEfiBlockIoProtocolGuid, > > + NULL, > > + BindingProtocol->ImageHandle, > > + ControllerHandle, > > + EFI_OPEN_PROTOCOL_TEST_PROTOCOL > > + ); > > + return Status; > > +} > > + > > +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 =3D NULL; > > + > > + DEBUG ((EFI_D_INFO, "[Ext4] Binding to controller\n")); > > + > > + Status =3D gBS->OpenProtocol ( > > + ControllerHandle, > > + &gEfiDiskIoProtocolGuid, > > + (VOID **)&DiskIo, > > + BindingProtocol->ImageHandle, > > + ControllerHandle, > > + EFI_OPEN_PROTOCOL_BY_DRIVER > > + ); > > + > > + if(EFI_ERROR (Status)) { > > + return Status; > > Why does this return when the others jump to Error? > > > + } > > + > > + DEBUG ((EFI_D_INFO, "[Ext4] Controller supports DISK_IO\n")); > > + > > + Status =3D gBS->OpenProtocol ( > > + ControllerHandle, > > + &gEfiDiskIo2ProtocolGuid, > > + (VOID **)&DiskIo2, > > + BindingProtocol->ImageHandle, > > + ControllerHandle, > > + EFI_OPEN_PROTOCOL_BY_DRIVER > > + ); > > + // It's okay to not support DISK_IO2 > > + > > + if(DiskIo2 !=3D NULL) { > > + DEBUG ((EFI_D_INFO, "[Ext4] Controller supports DISK_IO2\n")); > > + } > > + > > + Status =3D gBS->OpenProtocol ( > > + ControllerHandle, > > + &gEfiBlockIoProtocolGuid, > > + (VOID **)&blockIo, > > + BindingProtocol->ImageHandle, > > + ControllerHandle, > > + EFI_OPEN_PROTOCOL_GET_PROTOCOL > > + ); > > + > > + if(EFI_ERROR (Status)) { > > + goto Error; > > + } > > + > > + DEBUG ((EFI_D_INFO, "Opening partition\n")); > > + > > + Status =3D Ext4OpenPartition (ControllerHandle, DiskIo, DiskIo2, blo= ckIo); > > + > > + if(!EFI_ERROR (Status)) { > > + return Status; > > + } > > + > > + DEBUG ((EFI_D_INFO, "[ext4] Error mounting %x\n", Status)); > > You can print EFI_STATUS with "%r" to get a name string. I would move > this into an error clause for Ext4OpenPartition() and jump to Error, > with the outside branch returning success, to upkeep consistency and > make it obvious to what the error message refers. > > > + > > +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/Ext4= Dxe/Ext4Dxe.h > > new file mode 100644 > > index 0000000000..f6875c919e > > --- /dev/null > > +++ b/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.h > > @@ -0,0 +1,942 @@ > > +/** > > + @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 > > + > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > + > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > + > > +#include "Ext4Disk.h" > > + > > +#define EXT4_NAME_MAX 255 > > + > > +#define EXT4_DRIVER_VERSION 0x0000 > > + > > +/** > > + Opens an ext4 partition and installs the Simple File System protoco= l. > > + > > + @param[in] DeviceHandle Handle to the block device. > > + @param[in] DiskIo Pointer to an EFI_DISK_IO_PROTOC= OL. > > + @param[in opt] DiskIo2 Pointer to an EFI_DISK_IO2_PROTO= COL, if supported. > > + @param[in] BlockIo Pointer to an EFI_BLOCK_IO_PROTO= COL. > > + > > + @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 filesyst= em details. > > + @retval EFI_SUCCESS Parsing was succesful and the partition i= s 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. > > + */ > > +STATIC inline > > +EFI_BLOCK_IO_PROTOCOL * > > +Ext4BlockIo ( > > + EXT4_PARTITION *Partition > > + ) > > +{ > > + return 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. > > + */ > > +STATIC inline > > +EFI_DISK_IO_PROTOCOL * > > +Ext4DiskIo ( > > + EXT4_PARTITION *Partition > > + ) > > +{ > > + return 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 NU= LL if > > + not supported. > > + */ > > +STATIC inline > > +EFI_DISK_IO2_PROTOCOL * > > +Ext4DiskIo2 ( > > + EXT4_PARTITION *Partition > > + ) > > +{ > > + return 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. > > + */ > > +STATIC inline > > +UINT32 > > +Ext4MediaId ( > > + EXT4_PARTITION *Partition > > + ) > > +{ > > + return 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 rea= d. > > + > > + @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 t= he 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_FEA= TURE_INCOMPAT_64BIT). > > + > > + @param[in] Partition Pointer to the opened ext4 partition. > > + > > + @return TRUE if EXT4_FEATURE_INCOMPAT_64BIT is enabled, else FALSE. > > + */ > > +STATIC inline > > +BOOLEAN > > +Ext4Is64Bit ( > > + IN CONST EXT4_PARTITION *Partition > > + ) > > +{ > > + return Partition->FeaturesIncompat & EXT4_FEATURE_INCOMPAT_64BIT; > > +} > > + > > +/** > > + 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, H= igh. > > + */ > > +STATIC inline > > +EXT4_BLOCK_NR > > +Ext4MakeBlockNumberFromHalfs ( > > + IN CONST EXT4_PARTITION *Partition, > > + IN UINT32 Low, > > + IN UINT32 High > > + ) > > +{ > > + // High might have garbage if it's not a 64 bit filesystem > > + return Ext4Is64Bit (Partition) ? Low | ((UINT64)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. > > + */ > > +STATIC inline > > +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 + B= lockGroup * 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 point= er 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. > > + */ > > +STATIC inline > > +UINT64 > > +Ext4BlockToByteOffset ( > > + IN CONST EXT4_PARTITION *Partition, > > + IN EXT4_BLOCK_NR Block > > + ) > > +{ > > + return Partition->BlockSize * Block; > > +} > > + > > +/** > > + 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. > > + */ > > +STATIC inline > > +UINT64 > > +Ext4InodeSize ( > > + CONST EXT4_INODE *Inode > > + ) > > +{ > > + return ((UINT64)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 exten= t must cover. > > + @param[out] Extent Pointer to the output buffer, where t= he 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 Ext4FileFromOpenFileNode(Node) BASE_CR (Node, EXT4_FILE, Open= FilesListNode) > > + > > +/** > > + 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 en= try. > > + > > + @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 b= e 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 b= e 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 > > + > > +EFI_STATUS EFIAPI > > +Ext4OpenVolume ( > > + IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Partition, > > + 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 > > + > > +EFI_STATUS EFIAPI > > +Ext4Open ( > > + IN EFI_FILE_PROTOCOL *This, > > + OUT EFI_FILE_PROTOCOL **NewHandle, > > + IN CHAR16 *FileName, > > + IN UINT64 OpenMode, > > + IN UINT64 Attributes > > + ); > > + > > +EFI_STATUS EFIAPI > > +Ext4Close ( > > + IN EFI_FILE_PROTOCOL *This > > + ); > > + > > +EFI_STATUS EFIAPI > > +Ext4Delete ( > > + IN EFI_FILE_PROTOCOL *This > > + ); > > + > > +EFI_STATUS > > +EFIAPI > > +Ext4ReadFile ( > > + IN EFI_FILE_PROTOCOL *This, > > + IN OUT UINTN *BufferSize, > > + OUT VOID *Buffer > > + ); > > + > > +EFI_STATUS > > +EFIAPI > > +Ext4WriteFile ( > > + IN EFI_FILE_PROTOCOL *This, > > + IN OUT UINTN *BufferSize, > > + IN VOID *Buffer > > + ); > > + > > +EFI_STATUS > > +EFIAPI > > +Ext4GetPosition ( > > + IN EFI_FILE_PROTOCOL *This, > > + OUT UINT64 *Position > > + ); > > + > > +EFI_STATUS > > +EFIAPI > > +Ext4SetPosition ( > > + IN EFI_FILE_PROTOCOL *This, > > + IN UINT64 Position > > + ); > > + > > +EFI_STATUS > > +EFIAPI > > +Ext4GetInfo ( > > + IN EFI_FILE_PROTOCOL *This, > > + IN EFI_GUID *InformationType, > > + IN OUT UINTN *BufferSize, > > + OUT 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 devi= ces 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 t= ypes > > + don't make sense under UEFI. > > +*/ > > +STATIC inline > > +BOOLEAN > > +Ext4FileIsOpenable ( > > + IN CONST EXT4_FILE *File > > + ) > > +{ > > + return Ext4FileIsReg (File) || Ext4FileIsDir (File); > > +} > > + > > +#define Ext4InodeHasField(Inode, \ > > + Field) (Inode->i_extra_isize + EXT4_GOOD_OL= D_INODE_SIZE >=3D 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 ( > > + 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 file= name comparison). > > + > > + @param[in] DriverHandle Handle to the driver image. > > + > > + @retval EFI_SUCCESS Unicode collation was successfully initialise= d. > > + @retval !EFI_SUCCESS Failure. > > +*/ > > +EFI_STATUS > > +Ext4InitialiseUnicodeCollation ( > > + EFI_HANDLE DriverHandle > > + ); > > + > > +/** > > + Does a case-insensitive string comparison. Refer to EFI_UNICODE_COL= LATION_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 > > Why are they not CONST? The caller needs to cast this way. You can cast > in the callee in my opinion. > > > + ); > > + > > +/** > > + Retrieves the filename of the directory entry and converts it to UT= F-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 initialise= d. > > + @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 le= ngth of the buffer, > > + and the length of the actual EFI_FILE_I= NFO 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 e= xtents. > > + > > + @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 partitio= n. > > + @param[in] BlockGroupDesc Pointer to the block group descript= or. > > + @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 partitio= n. > > + @param[in] BlockGroupDesc Pointer to the block group descript= or. > > + @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 part= ition. > > + @param[in] RoCompatFeatureSet Feature set to test. > > + > > + @return TRUE if all features are supported, else FALSE. > > +*/ > > +STATIC inline > > +BOOLEAN > > +Ext4HasRoCompat ( > > + IN CONST EXT4_PARTITION *Partition, > > + IN UINT32 RoCompatFeatureSet > > + ) > > +{ > > + return (Partition->FeaturesRoCompat & RoCompatFeatureSet) =3D=3D RoC= ompatFeatureSet; > > +} > > + > > +/** > > + Verifies the existance of a particular compat feature set. > > + @param[in] Partition Pointer to the opened EXT4 part= ition. > > + @param[in] RoCompatFeatureSet Feature set to test. > > + > > + @return TRUE if all features are supported, else FALSE. > > +*/ > > +STATIC inline > > +BOOLEAN > > +Ext4HasCompat ( > > + IN CONST EXT4_PARTITION *Partition, > > + IN UINT32 CompatFeatureSet > > + ) > > +{ > > + return (Partition->FeaturesCompat & CompatFeatureSet) =3D=3D CompatF= eatureSet; > > +} > > + > > +/** > > + Verifies the existance of a particular compat feature set. > > + @param[in] Partition Pointer to the opened EXT4 part= ition. > > + @param[in] RoCompatFeatureSet Feature set to test. > > + > > + @return TRUE if all features are supported, else FALSE. > > +*/ > > +STATIC inline > > +BOOLEAN > > +Ext4HasIncompat ( > > + IN CONST EXT4_PARTITION *Partition, > > + IN UINT32 IncompatFeatureSet > > + ) > > +{ > > + return (Partition->FeaturesIncompat & IncompatFeatureSet) =3D=3D Inc= ompatFeatureSet; > > +} > > + > > +// Note: Might be a good idea to provide generic Ext4Has$feature() thr= ough macros. > > + > > +/** > > + Checks if metadata_csum is enabled on the partition. > > + @param[in] Partition Pointer to the opened EXT4 part= ition. > > + @param[in] RoCompatFeatureSet Feature set to test. > > + > > + @return TRUE if the feature is supported, else FALSE. > > +*/ > > +STATIC inline > > +BOOLEAN > > +Ext4HasMetadataCsum ( > > + IN CONST EXT4_PARTITION *Partition > > + ) > > +{ > > + return Ext4HasRoCompat (Partition, EXT4_FEATURE_RO_COMPAT_METADATA_C= SUM); > > +} > > + > > +/** > > + Checks if gdt_csum is enabled on the partition. > > + @param[in] Partition Pointer to the opened EXT4 part= ition. > > + @param[in] RoCompatFeatureSet Feature set to test. > > + > > + @return TRUE if the feature is supported, else FALSE. > > +*/ > > +STATIC inline > > +BOOLEAN > > +Ext4HasGdtCsum ( > > + IN CONST EXT4_PARTITION *Partition > > + ) > > +{ > > + return Ext4HasRoCompat (Partition, EXT4_FEATURE_RO_COMPAT_METADATA_C= SUM); > > +} > > + > > +#endif > > diff --git a/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.inf b/Features/Ext4Pkg/Ex= t4Dxe/Ext4Dxe.inf > > new file mode 100644 > > index 0000000000..102b12d613 > > --- /dev/null > > +++ b/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.inf > > @@ -0,0 +1,147 @@ > > +## @file > > +# Ext4 Package > > +# > > +# UEFI Driver that produces the Simple File System Protocol for a par= tition 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.htm= l 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 th= e start) > > +# the start of the partition, and describes the filesystem in gen= eral. > > +# 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 ma= ny 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 a= fter the > > +# superblock. Each block group descriptor contains the location o= f the > > +# inode table, and the inode and block bitmaps (note these bitmap= s 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_s= ize ^ 1024. > > +# Blocks can be allocated using individual block groups's bitmaps= . Note > > +# that block 0 is invalid and its presence on extents/block table= s 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 (orig= inally > > +# index nodes). Each file/socket/symlink/directory/etc (here on o= ut referred > > +# to as a file, since there is no distinction under the ext4 file= system) is > > +# stored as a /nameless/ inode, that is stored in some block grou= p'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 th= e inode > > +# contains inline extended attributes. Inodes' data is stored usi= ng either > > +# data blocks (under ext2/3) or extents (under ext4). > > +# > > +# 5) Extents > > +# Ext4 inodes store data in extents. These let N contiguous logic= al blocks > > +# that are represented by N contiguous physical blocks be represe= nted by a > > +# single extent structure, which minimizes filesystem metadata bl= oat 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 fragment= ation). > > +# Inodes that use extents store them in a tree, and the top of th= e 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 != =3D 0 and > > +# EXT4_EXTENT on eh_depth =3D 0; these entries are always sorted = by logical > > +# block. > > +# > > +# 6) Directories > > +# Ext4 directories are files that store name -> inode mappings fo= r the > > +# logical directory; this is where files get their names, which m= eans ext4 > > +# inodes do not themselves have names, since they can be linked (= present) > > +# multiple times with different names. Directories can store entr= ies in two > > +# different ways: > > +# 1) Classical linear directories: They store entries as a most= ly-linked > > +# mostly-list of EXT4_DIR_ENTRY. > > +# 2) Hash tree directories: These are used for larger directori= es, with > > +# hundreds of entries, and are designed in a backwards compa= tible way. > > +# These are not yet implemented in the Ext4Dxe driver. > > +# > > +# 7) Journal > > +# Ext3/4 filesystems have a journal to help protect the filesyste= m against > > +# system crashes. This is not yet implemented in Ext4Dxe but is d= escribed > > +# in detail in the Linux kernel's documentation. > > +## > > + > > + > > +[Defines] > > + INF_VERSION =3D 0x00010005 > > + BASE_NAME =3D Ext4 > > + MODULE_UNI_FILE =3D Ext4Dxe.uni > > + FILE_GUID =3D 75F2B676-D73B-45CB-B7C1-303C7F4E6= FD6 > > + MODULE_TYPE =3D UEFI_DRIVER > > + VERSION_STRING =3D 1.0 > > + > > + ENTRY_POINT =3D Ext4EntryPoint > > + UNLOAD_IMAGE =3D Ext4Unload > > + > > +# > > +# The following information is for reference only and not required by = the build tools. > > +# > > +# VALID_ARCHITECTURES =3D 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 > > + > > +[Packages] > > + MdePkg/MdePkg.dec > > + RedfishPkg/RedfishPkg.dec > > + > > +[LibraryClasses] > > + UefiRuntimeServicesTableLib > > + UefiBootServicesTableLib > > + MemoryAllocationLib > > + BaseMemoryLib > > + BaseLib > > + UefiLib > > + UefiDriverEntryPoint > > + DebugLib > > + PcdLib > > + OrderedCollectionLib > > + BaseUcs2Utf8Lib > > + > > +[Guids] > > + gEfiFileInfoGuid ## SOMETIMES_CONSUMES ## UND= EFINED > > + gEfiFileSystemInfoGuid ## SOMETIMES_CONSUMES ## UND= EFINED > > + gEfiFileSystemVolumeLabelInfoIdGuid ## SOMETIMES_CONSUMES ## UND= EFINED > > + > > +[Protocols] > > + gEfiDiskIoProtocolGuid ## TO_START > > + gEfiDiskIo2ProtocolGuid ## TO_START > > + gEfiBlockIoProtocolGuid ## TO_START > > + gEfiSimpleFileSystemProtocolGuid ## BY_START > > + gEfiUnicodeCollationProtocolGuid ## TO_START > > + gEfiUnicodeCollation2ProtocolGuid ## TO_START > > + > > +[Pcd] > > + gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultLang ## SOM= ETIMES_CONSUMES > > + gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultPlatformLang ## SOM= ETIMES_CONSUMES > > diff --git a/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.uni b/Features/Ext4Pkg/Ex= t4Dxe/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 par= tition 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 fo= r the EXT4 file system." > > + > > +#string STR_MODULE_DESCRIPTION #language en-US "Produces the E= FI Simple File System protocol." > > diff --git a/Features/Ext4Pkg/Ext4Dxe/Extents.c b/Features/Ext4Pkg/Ext4= Dxe/Extents.c > > new file mode 100644 > > index 0000000000..db4bf5aa3f > > --- /dev/null > > +++ b/Features/Ext4Pkg/Ext4Dxe/Extents.c > > @@ -0,0 +1,616 @@ > > +/** > > + @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 corrupti= on. > > +*/ > > +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 exten= t 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 st= ructure. > > + > > + @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 ((EFI_D_ERROR, "[ext4] Invalid extent header depth %u\n", He= ader->eh_depth)); > > + return FALSE; > > + } > > + > > + if(Header->eh_magic !=3D EXT4_EXTENT_HEADER_MAGIC) { > > + DEBUG ((EFI_D_ERROR, "[ext4] Invalid extent header magic %x\n", He= ader->eh_magic)); > > + return FALSE; > > + } > > + > > + if(Header->eh_max < Header->eh_entries) { > > + DEBUG (( > > + EFI_D_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 t= o a > > + logical block in a given extent tree node. > > + > > + @param[in] Header Pointer to the EXT4_EXTENT_HEADER st= ructure. > > + @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 =3D ((EXT4_EXTENT_INDEX *)(Header + 1)) + 1; > > + r =3D ((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 sort= ed. > > + > > + while(l <=3D r) { > > + m =3D l + (r - l) / 2; > > + > > + if(LogicalBlock < m->ei_block) { > > + r =3D m - 1; > > + } else { > > + l =3D 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 st= ructure. > > + @param[in] LogicalBlock Block that will be searched > > + > > + @return Pointer to the found EXT4_EXTENT_INDEX, else NULL if the ar= ray 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 =3D ((EXT4_EXTENT *)(Header + 1)) + 1; > > + r =3D ((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 sort= ed. > > + > > + // Empty array > > + if(Header->eh_entries =3D=3D 0) { > > + return NULL; > > + } > > + > > + while(l <=3D r) { > > + m =3D l + (r - l) / 2; > > + > > + if(LogicalBlock < m->ee_block) { > > + r =3D m - 1; > > + } else { > > + l =3D m + 1; > > + } > > + } > > + > > + return l - 1; > > +} > > + > > +/** > > + Retrieves the leaf block from an EXT4_EXTENT_INDEX. > > + > > + @param[in] Index Pointer to the EXT4_EXTENT_INDEX str= ucture. > > + > > + @return Block number of the leaf node. > > +*/ > > +STATIC > > +EXT4_BLOCK_NR > > +Ext4ExtentIdxLeafBlock ( > > + IN EXT4_EXTENT_INDEX *Index > > + ) > > +{ > > + return ((UINT64)Index->ei_leaf_hi << 32) | Index->ei_leaf_lo; > > +} > > + > > +STATIC UINTN GetExtentRequests =3D 0; > > +STATIC UINTN GetExtentCacheHits =3D 0; > > Private globals are defined with "m" ("module") and public ones with "g". > > > + > > +/** > > + 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 exten= t must cover. > > + @param[out] Extent Pointer to the output buffer, where t= he 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; > > + > > + Inode =3D File->Inode; > > + Ext =3D NULL; > > + Buffer =3D NULL; > > + > > + DEBUG ((EFI_D_INFO, "[ext4] Looking up extent for block %lu\n", Logi= calBlock)); > > + > > + 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; > > + } > > + > > + GetExtentRequests++; > > + #if DEBUG_EXTENT_CACHE > > + DEBUG (( > > + EFI_D_INFO, > > + "[ext4] Requests %lu, hits %lu, misses %lu\n", > > + GetExtentRequests, > > + GetExtentCacheHits, > > + GetExtentRequests - GetExtentCacheHits > > + )); > > + #endif > > + > > + // Note: Right now, holes are the single biggest reason for cache mi= sses > > + // We should find a way to get (or cache) holes > > + if ((Ext =3D Ext4GetExtentFromMap (File, (UINT32)LogicalBlock)) !=3D= NULL) { > > + *Extent =3D *Ext; > > + GetExtentCacheHits++; > > + > > + return EFI_SUCCESS; > > + } > > + > > + // Slow path, we'll need to read from disk and (try to) cache those = extents. > > + > > + ExtHeader =3D Ext4GetInoExtentHeader (Inode); > > + > > + if(!Ext4ExtentHeaderValid (ExtHeader)) { > > + return EFI_VOLUME_CORRUPTED; > > + } > > + > > + CurrentDepth =3D ExtHeader->eh_depth; > > + > > + while(ExtHeader->eh_depth !=3D 0) { > > + EXT4_EXTENT_INDEX *Index; > > + EFI_STATUS Status; > > + > > + CurrentDepth--; > > + // While depth !=3D 0, we're traversing the tree itself and not an= y leaves > > + // As such, every entry is an EXT4_EXTENT_INDEX entry > > + // Note: Entries after the extent header, either index or actual e= xtent, are always sorted. > > + // Therefore, we can use binary search, and it's actually the stan= dard for doing so > > + // (see FreeBSD). > > + > > + Index =3D Ext4BinsearchExtentIndex (ExtHeader, LogicalBlock); > > + > > + if(Buffer =3D=3D NULL) { > > + Buffer =3D AllocatePool (Partition->BlockSize); > > + if(Buffer =3D=3D NULL) { > > + return EFI_OUT_OF_RESOURCES; > > + } > > + } > > + > > + // Read the leaf block onto the previously-allocated buffer. > > + > > + Status =3D Ext4ReadBlocks (Partition, Buffer, 1, Ext4ExtentIdxLeaf= Block (Index)); > > + if(EFI_ERROR (Status)) { > > + FreePool (Buffer); > > + return Status; > > + } > > + > > + ExtHeader =3D Buffer; > > + > > + if(!Ext4ExtentHeaderValid (ExtHeader)) { > > + FreePool (Buffer); > > + return EFI_VOLUME_CORRUPTED; > > + } > > + > > + if(!Ext4CheckExtentChecksum (ExtHeader, File)) { > > + DEBUG ((EFI_D_ERROR, "[ext4] Invalid extent checksum\n")); > > + FreePool (Buffer); > > + return EFI_VOLUME_CORRUPTED; > > + } > > + > > + if(ExtHeader->eh_depth !=3D CurrentDepth) { > > + FreePool (Buffer); > > + return EFI_VOLUME_CORRUPTED; > > + } > > + } > > + > > + /* We try to cache every extent under a single leaf, since it's quit= e likely that we > > + * may need to access things sequentially. Furthermore, ext4 block a= llocation 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->e= h_entries); > > + > > + Ext =3D Ext4BinsearchExtentExt (ExtHeader, LogicalBlock); > > + > > + if(!Ext) { > > + if(Buffer !=3D NULL) { > > + FreePool (Buffer); > > + } > > + > > + return EFI_NO_MAPPING; > > + } > > + > > + if(!(LogicalBlock >=3D Ext->ee_block && Ext->ee_block + Ext->ee_len = > LogicalBlock)) { > > + // This extent does not cover the block > > + if(Buffer !=3D NULL) { > > + FreePool (Buffer); > > + } > > + > > + return EFI_NO_MAPPING; > > + } > > + > > + *Extent =3D *Ext; > > + > > + if(Buffer !=3D 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 =3D UserStruct1; > > + CONST EXT4_EXTENT *Extent2 =3D UserStruct2; > > + > > + // TODO: Detect extent overlaps? in case of corruption. > > + > > + /* DEBUG((EFI_D_INFO, "[ext4] extent 1 %u extent 2 %u =3D %ld\n", Ex= tent1->ee_block, > > + Extent2->ee_block, Extent1->ee_block - Extent2->ee_block)); */ > > + 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 embedde= d 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 emb= edded > > + 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 =3D UserStruct; > > + > > + // Note that logical blocks are 32-bits in size so no truncation can= happen here > > + // with regards to 32-bit architectures. > > + UINT32 Block =3D (UINT32)(UINTN)StandaloneKey; > > + > > + // DEBUG((EFI_D_INFO, "[ext4] comparing %u %u\n", Block, Extent->ee_= block)); > > + if(Block >=3D 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 e= xtents. > > + > > + @param[in] File Pointer to the open file. > > + > > + @return Result of the operation. > > +*/ > > +EFI_STATUS > > +Ext4InitExtentsMap ( > > + IN EXT4_FILE *File > > + ) > > +{ > > + File->ExtentsMap =3D OrderedCollectionInit (Ext4ExtentsMapStructComp= are, 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 =3D NULL; > > + > > + while ((MinEntry =3D OrderedCollectionMin (File->ExtentsMap)) !=3D N= ULL) { > > + EXT4_EXTENT *Ext; > > + OrderedCollectionDelete (File->ExtentsMap, MinEntry, (VOID **)&Ext= ); > > + FreePool (Ext); > > + } > > + > > + ASSERT (OrderedCollectionIsEmpty (File->ExtentsMap)); > > + > > + OrderedCollectionUninit (File->ExtentsMap); > > + File->ExtentsMap =3D NULL; > > +} > > + > > +/** > > + Caches a range of extents, by allocating pool memory for each exten= t 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 NumberE= xtents > > + ) > > +{ > > + UINT16 i; > > + > > + /* 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(i =3D 0; i < NumberExtents; i++, Extents++) { > > + EXT4_EXTENT *Extent; > > + EFI_STATUS Status; > > + > > + Extent =3D AllocatePool (sizeof (EXT4_EXTENT)); > > + > > + if (Extent =3D=3D NULL) { > > + return; > > + } > > + > > + CopyMem (Extent, Extents, sizeof (EXT4_EXTENT)); > > + Status =3D OrderedCollectionInsert (File->ExtentsMap, NULL, Extent= ); > > + > > + // EFI_ALREADY_STARTED =3D already exists in the tree. > > + if (EFI_ERROR (Status)) { > > + FreePool (Extent); > > + > > + if(Status =3D=3D EFI_ALREADY_STARTED) { > > + continue; > > + } > > + > > + return; > > + } > > + > > + #if DEBUG_EXTENT_CACHE > > + DEBUG (( > > + EFI_D_INFO, > > + "[ext4] Cached extent [%lu, %lu]\n", > > + Extent->ee_block, > > + Extent->ee_block + Extent->ee_len - 1 > > + )); > > + #endif > > + } > > +} > > + > > +/** > > + 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 =3D OrderedCollectionFind (File->ExtentsMap, (CONST VOID *)(UI= NTN)Block); > > + > > + if (Entry =3D=3D 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 =3D File->Partition; > > + Inode =3D File->Inode; > > + > > + Csum =3D Ext4CalculateChecksum (Partition, &File->InodeNum, sizeof (= EXT4_INO_NR), Partition->InitialSeed); > > + Csum =3D Ext4CalculateChecksum (Partition, &Inode->i_generation, siz= eof (Inode->i_generation), Csum); > > + Csum =3D Ext4CalculateChecksum (Partition, ExtHeader, Partition->Blo= ckSize - 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 corrupti= on. > > +*/ > > +BOOLEAN > > +Ext4CheckExtentChecksum ( > > + IN CONST EXT4_EXTENT_HEADER *ExtHeader, > > + IN CONST EXT4_FILE *File > > + ) > > +{ > > + EXT4_PARTITION *Partition; > > + EXT4_EXTENT_TAIL *Tail; > > + > > + Partition =3D File->Partition; > > + > > + if(!Ext4HasMetadataCsum (Partition)) { > > + return TRUE; > > + } > > + > > + Tail =3D (EXT4_EXTENT_TAIL *)((CONST CHAR8 *)ExtHeader + (Partition-= >BlockSize - 4)); > > + > > + return Tail->eb_checksum =3D=3D Ext4CalculateExtentChecksum (ExtHead= er, File); > > +} > > diff --git a/Features/Ext4Pkg/Ext4Dxe/File.c b/Features/Ext4Pkg/Ext4Dxe= /File.c > > new file mode 100644 > > index 0000000000..10dda64b16 > > --- /dev/null > > +++ b/Features/Ext4Pkg/Ext4Dxe/File.c > > @@ -0,0 +1,583 @@ > > +/** > > + @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 > > + > > +/** > > + 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 t= he 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 =3D Path; > > + CONST CHAR16 *End =3D Path; > > + > > + // The path segment ends on a backslash or a null terminator > > + for( ; *End !=3D L'\0' && *End !=3D L'\\'; End++) { > > + } > > + > > + *Length =3D End - Start; > > + > > + return StrnCpyS (PathSegment, EXT4_NAME_MAX, Start, End - Start); > > Why not EXT4_NAME_MAX + 1? > > > +} > > + > > +/** > > + 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] =3D=3D L'\\') { > > + Path++; > > + } > > + > > + return Path[0] =3D=3D '\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 =3D 0; > > + > > + if((OpenMode & EFI_FILE_MODE_READ) !=3D 0) { > > + NeededPerms |=3D EXT4_INO_PERM_READ_OWNER; > > + } > > + > > + if((OpenMode & EFI_FILE_MODE_WRITE) !=3D 0) { > > + NeededPerms |=3D EXT4_INO_PERM_WRITE_OWNER; > > + } > > + > > + if((File->Inode->i_mode & NeededPerms) !=3D NeededPerms) { > > + return FALSE; > > + } > > + > > + File->OpenMode =3D 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) =3D=3D EXT4_= INO_PERM_EXEC_OWNER; > > +} > > + > > +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; > > + > > + Current =3D (EXT4_FILE *)This; > > + Partition =3D Current->Partition; > > + Level =3D 0; > > + > > + DEBUG ((EFI_D_INFO, "[ext4] Ext4Open %s\n", FileName)); > > + // If the path starts with a backslash, we treat the root directory = as the base directory > > + if(FileName[0] =3D=3D L'\\') { > > + FileName++; > > + Current =3D Partition->Root; > > + } > > + > > + while(FileName[0] !=3D L'\0') { > > + CHAR16 PathSegment[EXT4_NAME_MAX + 1]; > > + UINTN Length; > > + EXT4_FILE *File; > > + EFI_STATUS Status; > > + > > + // Discard leading path separators > > + while(FileName[0] =3D=3D L'\\') { > > + FileName++; > > + } > > + > > + if(GetPathSegment (FileName, PathSegment, &Length) !=3D EFI_SUCCES= S) { > > + return EFI_BUFFER_TOO_SMALL; > > + } > > + > > + // Reached the end of the path > > + if(Length =3D=3D 0) { > > + break; > > + } > > + > > + FileName +=3D Length; > > + > > + DEBUG ((EFI_D_INFO, "[ext4] Opening %s\n", PathSegment)); > > + > > + // TODO: What to do with symlinks? They're nonsense when absolute = but may > > + // be useful when they're relative. > > + > > + if (!Ext4FileIsDir (Current)) { > > + return EFI_INVALID_PARAMETER; > > + } > > + > > + if (!Ext4IsLastPathSegment (FileName)) { > > + if (!Ext4DirCanLookup (Current)) { > > + return EFI_ACCESS_DENIED; > > + } > > + } > > + > > + Status =3D Ext4OpenFile (Current, PathSegment, Partition, EFI_FILE= _MODE_READ, &File); > > + > > + if(EFI_ERROR (Status) && Status !=3D EFI_NOT_FOUND) { > > + return Status; > > + } else if(Status =3D=3D EFI_NOT_FOUND) { > > + // WRITE SUPPORT: Handle file creation when write support is add= ed > > + return Status; > > + } > > + > > + // Check if this is a valid file to open in EFI > > + if(!Ext4FileIsOpenable (File)) { > > + Ext4CloseInternal (File); > > + // This looks like an /okay/ status to return. > > + return EFI_ACCESS_DENIED; > > + } > > + > > + if(Level !=3D 0) { > > + // Careful not to close the base directory > > + Ext4CloseInternal (Current); > > + } > > + > > + Level++; > > + > > + Current =3D File; > > + } > > + > > + if (Level =3D=3D 0) { > > + // We opened the base directory again, so we need to duplicate the= file structure > > + Current =3D Ext4DuplicateFile (Current); > > + if (Current =3D=3D NULL) { > > + return EFI_OUT_OF_RESOURCES; > > + } > > + } > > + > > + if(!Ext4ApplyPermissions (Current, OpenMode)) { > > + Ext4CloseInternal (Current); > > + return EFI_ACCESS_DENIED; > > + } > > + > > + *NewHandle =3D &Current->Protocol; > > + > > + DEBUG ((EFI_D_INFO, "Open successful\n")); > > + DEBUG ((EFI_D_INFO, "Opened filename %s\n", Current->FileName)); > > + return EFI_SUCCESS; > > +} > > + > > +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 =3D=3D File->Partition->Root && !File->Partition->Unmounting= ) { > > + return EFI_SUCCESS; > > + } > > + > > + DEBUG ((EFI_D_INFO, "[ext4] Closed file %p (inode %lu)\n", File, Fil= e->InodeNum)); > > + RemoveEntryList (&File->OpenFilesListNode); > > + FreePool (File->FileName); > > + FreePool (File->Inode); > > + Ext4FreeExtentsMap (File); > > + FreePool (File); > > + return EFI_SUCCESS; > > As this function can only ever return success, and all direct uses > discard the status anyway, in my opinion make it VOID and return success > in Ext3Close(). > > > +} > > + > > +EFI_STATUS EFIAPI > > +Ext4Delete ( > > + IN EFI_FILE_PROTOCOL *This > > + ) > > +{ > > + // Write support > > + Ext4Close (This); > > + return EFI_WARN_DELETE_FAILURE; > > +} > > + > > +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 =3D (EXT4_FILE *)This; > > + Partition =3D File->Partition; > > + > > + ASSERT (Ext4FileIsOpenable (File)); > > + > > + if(Ext4FileIsReg (File)) { > > + Status =3D Ext4Read (Partition, File, Buffer, File->Position, Buff= erSize); > > + if(Status =3D=3D EFI_SUCCESS) { > > + File->Position +=3D *BufferSize; > > + } > > + > > + return Status; > > + } else if(Ext4FileIsDir (File)) { > > + Status =3D Ext4ReadDir (Partition, File, Buffer, File->Position, B= ufferSize); > > + DEBUG ((EFI_D_INFO, "[ext4] ReadDir status %lx\n", Status)); > > + > > + if(Status =3D=3D EFI_SUCCESS) { > > + DEBUG ((EFI_D_INFO, "[ext4] ReadDir retlen %lu\n", *BufferSize))= ; > > + } > > + > > + return Status; > > + } > > + > > + return EFI_SUCCESS; > > +} > > + > > +EFI_STATUS > > +EFIAPI > > +Ext4WriteFile ( > > + IN EFI_FILE_PROTOCOL *This, > > + IN OUT UINTN *BufferSize, > > + IN VOID *Buffer > > + ) > > +{ > > + EXT4_FILE *File =3D (EXT4_FILE *)This; > > + > > + if(!(File->OpenMode & EFI_FILE_MODE_WRITE)) { > > + return EFI_ACCESS_DENIED; > > + } > > + > > + // TODO: Add write support > > + return EFI_WRITE_PROTECTED; > > +} > > + > > +EFI_STATUS > > +EFIAPI > > +Ext4GetPosition ( > > + IN EFI_FILE_PROTOCOL *This, > > + OUT UINT64 *Position > > + ) > > +{ > > + EXT4_FILE *File =3D (EXT4_FILE *)This; > > + > > + if(Ext4FileIsDir (File)) { > > + return EFI_UNSUPPORTED; > > + } > > + > > + *Position =3D File->Position; > > + > > + return EFI_SUCCESS; > > +} > > + > > +EFI_STATUS > > +EFIAPI > > +Ext4SetPosition ( > > + IN EFI_FILE_PROTOCOL *This, > > + IN UINT64 Position > > + ) > > +{ > > + EXT4_FILE *File =3D (EXT4_FILE *)This; > > + > > + // Only seeks to 0 (so it resets the ReadDir operation) are allowed > > + if(Ext4FileIsDir (File) && Position !=3D 0) { > > + return EFI_UNSUPPORTED; > > + } > > + > > + // -1 (0xffffff.......) seeks to the end of the file > > + if(Position =3D=3D (UINT64)- 1) { > > MAX_UINT64? > > > + Position =3D EXT4_INODE_SIZE (File->Inode); > > + } > > + > > + File->Position =3D 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 *BufferSiz= e > > + ) > > +{ > > + // TODO: Get a way to set the directory entry for SetFileInfo > > + UINTN FileNameLen =3D StrLen (File->FileName); > > + UINTN FileNameSize =3D StrSize (File->FileName); > > + > > + UINTN NeededLength =3D SIZE_OF_EFI_FILE_INFO + FileNameSize; > > + > > + if(*BufferSize < NeededLength) { > > + *BufferSize =3D NeededLength; > > + return EFI_BUFFER_TOO_SMALL; > > + } > > + > > + Info->FileSize =3D EXT4_INODE_SIZE (File->Inode); > > + Info->PhysicalSize =3D Ext4FilePhysicalSpace (File); > > + Ext4FileATime (File, &Info->LastAccessTime); > > + Ext4FileMTime (File, &Info->ModificationTime); > > + Ext4FileCreateTime (File, &Info->LastAccessTime); > > + Info->Attribute =3D 0; > > + Info->Size =3D NeededLength; > > + > > + if(Ext4FileIsDir (File)) { > > + Info->Attribute |=3D EFI_FILE_DIRECTORY; > > + } > > + > > + *BufferSize =3D 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] File Pointer to an opened file. > > + @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 UINT= N *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 =3D 0; > > + VolumeName =3D NULL; > > + > > + // s_volume_name is only valid on dynamic revision; old filesystems = don't support this > > + if(Part->SuperBlock.s_rev_level =3D=3D EXT4_DYNAMIC_REV) { > > + CopyMem (TempVolName, (CONST CHAR8 *)Part->SuperBlock.s_volume_nam= e, 16); > > + TempVolName[16] =3D '\0'; > > + > > + Status =3D UTF8StrToUCS2 (TempVolName, &VolumeName); > > + > > + if(EFI_ERROR (Status)) { > > + return Status; > > + } > > + > > + VolNameLength =3D StrLen (VolumeName); > > + } > > + > > + NeededLength =3D SIZE_OF_EFI_FILE_SYSTEM_INFO; > > + > > + if (VolumeName !=3D NULL) { > > + NeededLength +=3D StrSize (VolumeName); > > + } else { > > + // If we don't have a volume name, we set VolumeLabel to a single = null terminator > > + NeededLength +=3D sizeof (CHAR16); > > + } > > + > > + if(*BufferSize < NeededLength) { > > + *BufferSize =3D NeededLength; > > + > > + if (VolumeName !=3D NULL) { > > + FreePool (VolumeName); > > + } > > + > > + return EFI_BUFFER_TOO_SMALL; > > + } > > + > > + TotalBlocks =3D Part->NumberBlocks; > > + > > + FreeBlocks =3D Ext4MakeBlockNumberFromHalfs ( > > + Part, > > + Part->SuperBlock.s_free_blocks_count, > > + Part->SuperBlock.s_free_blocks_count_hi > > + ); > > + > > + Info->BlockSize =3D Part->BlockSize; > > + Info->Size =3D NeededLength; > > + Info->ReadOnly =3D Part->ReadOnly; > > + Info->VolumeSize =3D TotalBlocks * Part->BlockSize; > > + Info->FreeSpace =3D FreeBlocks * Part->BlockSize; > > + > > + if (VolumeName !=3D NULL) { > > + StrCpyS (Info->VolumeLabel, VolNameLength + 1, VolumeName); > > + } else { > > + Info->VolumeLabel[0] =3D L'\0'; > > + } > > + > > + if (VolumeName !=3D NULL) { > > + FreePool (VolumeName); > > + } > > + > > + *BufferSize =3D NeededLength; > > + > > + return EFI_SUCCESS; > > +} > > + > > +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, Buff= er, 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 =3D Original->Partition; > > + File =3D AllocateZeroPool (sizeof (EXT4_FILE)); > > + > > + if (File =3D=3D NULL) { > > + return NULL; > > + } > > + > > + File->Inode =3D Ext4AllocateInode (Partition); > > + if (File->Inode =3D=3D NULL) { > > + FreePool (File); > > + return NULL; > > + } > > + > > + CopyMem (File->Inode, Original->Inode, Partition->InodeSize); > > + > > + File->FileName =3D AllocateZeroPool (StrSize (Original->FileName)); > > + if (File->FileName =3D=3D NULL) { > > + FreePool (File->Inode); > > + FreePool (File); > > + return NULL; > > + } > > + > > + StrCpyS (File->FileName, StrLen (Original->FileName) + 1, Original->= FileName); > > + > > + File->Position =3D 0; > > + Ext4SetupFile (File, Partition); > > + File->InodeNum =3D Original->InodeNum; > > + File->OpenMode =3D 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; > > +} > > diff --git a/Features/Ext4Pkg/Ext4Dxe/Inode.c b/Features/Ext4Pkg/Ext4Dx= e/Inode.c > > new file mode 100644 > > index 0000000000..304bf0c4a9 > > --- /dev/null > > +++ b/Features/Ext4Pkg/Ext4Dxe/Inode.c > > @@ -0,0 +1,468 @@ > > +/** > > + @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" > > + > > +#include > > + > > +/** > > + 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; > > + > > + HasSecondChecksumField =3D Ext4InodeHasField (Inode, i_checksum_hi); > > + > > + Dummy =3D 0; > > + > > + Crc =3D Ext4CalculateChecksum (Partition, &InodeNum, sizeof (InodeNu= m), Partition->InitialSeed); > > + Crc =3D Ext4CalculateChecksum (Partition, &Inode->i_generation, size= of (Inode->i_generation), Crc); > > + > > + Crc =3D Ext4CalculateChecksum ( > > + Partition, > > + Inode, > > + OFFSET_OF (EXT4_INODE, i_osd2.data_linux.l_i_checksum_lo), > > + Crc > > + ); > > + > > + Crc =3D Ext4CalculateChecksum (Partition, &Dummy, sizeof (Dummy), Cr= c); > > + > > + RestOfInode =3D &Inode->i_osd2.data_linux.l_i_reserved; > > + RestOfInodeLength =3D Partition->InodeSize - OFFSET_OF (EXT4_INODE, = i_osd2.data_linux.l_i_reserved); > > + > > + if (HasSecondChecksumField) { > > + UINTN Length =3D OFFSET_OF (EXT4_INODE, i_checksum_hi) - OFFSET_O= F (EXT4_INODE, i_osd2.data_linux.l_i_reserved); > > + > > + Crc =3D Ext4CalculateChecksum (Partition, &Inode->i_osd2.data_linu= x.l_i_reserved, Length, Crc); > > + Crc =3D Ext4CalculateChecksum (Partition, &Dummy, sizeof (Dummy), = Crc); > > + > > + // 4 is the size of the i_extra_size field + the size of i_checksu= m_hi > > + RestOfInodeLength =3D Partition->InodeSize - EXT4_GOOD_OLD_INODE_S= IZE - 4; > > + RestOfInode =3D &Inode->i_ctime_extra; > > + } > > + > > + Crc =3D Ext4CalculateChecksum (Partition, RestOfInode, RestOfInodeLe= ngth, 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((EFI_D_INFO, "Ext4Read[Offset %lu, Length %lu]\n", Offset, = *Length)); > > + EXT4_INODE *Inode; > > + UINT64 InodeSize; > > + UINT64 CurrentSeek; > > + UINTN RemainingRead; > > + UINTN BeenRead; > > + > > + Inode =3D File->Inode; > > + InodeSize =3D Ext4InodeSize (Inode); > > + CurrentSeek =3D Offset; > > + RemainingRead =3D *Length; > > + BeenRead =3D 0; > > + > > + if(Offset > InodeSize) { > > + return EFI_DEVICE_ERROR; > > + } > > + > > + if (RemainingRead > InodeSize - Offset) { > > + RemainingRead =3D (UINTN)(InodeSize - Offset); > > + } > > + > > + while(RemainingRead !=3D 0) { > > + UINTN WasRead; > > + EXT4_EXTENT Extent; > > + UINT32 BlockOff; > > + EFI_STATUS Status; > > + BOOLEAN HasBackingExtent; > > + > > + WasRead =3D 0; > > + > > + // The algorithm here is to get the extent corresponding to the cu= rrent block > > + // and then read as much as we can from the current extent. > > + > > + Status =3D Ext4GetExtent ( > > + Partition, > > + File, > > + DivU64x32Remainder (CurrentSeek, Partition->BlockSize, = &BlockOff), > > + &Extent > > + ); > > + > > + if(Status !=3D EFI_SUCCESS && Status !=3D EFI_NO_MAPPING) { > > + return Status; > > + } > > + > > + HasBackingExtent =3D Status !=3D EFI_NO_MAPPING; > > + > > + if(!HasBackingExtent) { > > + UINT32 HoleOff; > > + UINTN HoleLen; > > + > > + HoleOff =3D BlockOff; > > + HoleLen =3D Partition->BlockSize - HoleOff; > > + WasRead =3D HoleLen > RemainingRead ? RemainingRead : HoleLen; > > + // TODO: Get the hole size and memset all that > > + SetMem (Buffer, WasRead, 0); > > + } else { > > + UINT64 ExtentStartBytes; > > + UINT64 ExtentLengthBytes; > > + UINT64 ExtentLogicalBytes; > > + > > + // Our extent offset is the difference between CurrentSeek and E= xtentLogicalBytes > > + UINT64 ExtentOffset; > > + UINTN ExtentMayRead; > > + > > + ExtentStartBytes =3D (((UINT64)Extent.ee_start_hi << 32) | Ext= ent.ee_start_lo) * Partition->BlockSize; > > + ExtentLengthBytes =3D Extent.ee_len * Partition->BlockSize; > > + ExtentLogicalBytes =3D (UINT64)Extent.ee_block * Partition->Bloc= kSize; > > + ExtentOffset =3D CurrentSeek - ExtentLogicalBytes; > > + ExtentMayRead =3D (UINTN)(ExtentLengthBytes - ExtentOffset); > > + > > + WasRead =3D ExtentMayRead > RemainingRead ? RemainingRead : Exte= ntMayRead; > > + > > + // DEBUG((EFI_D_INFO, "[ext4] may read %lu, remaining %lu\n", Ex= tentMayRead, RemainingRead)); > > + // DEBUG((EFI_D_INFO, "[ext4] Reading block %lu\n", (ExtentStart= Bytes + ExtentOffset) / Partition->BlockSize)); > > + Status =3D Ext4ReadDiskIo (Partition, Buffer, WasRead, ExtentSta= rtBytes + ExtentOffset); > > + > > + if(EFI_ERROR (Status)) { > > + DEBUG (( > > + EFI_D_ERROR, > > + "[ext4] Error %x reading [%lu, %lu]\n", > > + Status, > > + ExtentStartBytes + ExtentOffset, > > + ExtentStartBytes + ExtentOffset + WasRead - 1 > > + )); > > + return Status; > > + } > > + } > > + > > + RemainingRead -=3D WasRead; > > + Buffer =3D (VOID *)((CHAR8 *)Buffer + WasRead); > > + BeenRead +=3D WasRead; > > + CurrentSeek +=3D WasRead; > > + } > > + > > + *Length =3D BeenRead; > > + > > + // DEBUG((EFI_D_INFO, "File length %lu crc %x\n", BeenRead, Calculat= eCrc32(original, 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 =3D FALSE; > > + InodeSize =3D Partition->InodeSize; > > + > > + // HACK!: We allocate a structure of at least sizeof(EXT4_INODE), bu= t in the future, when > > + // write support is added and we need to flush inodes to disk, we co= uld have a bit better > > + // distinction between the on-disk inode and a separate, nicer to wo= rk with inode struct. > > + if (InodeSize < sizeof (EXT4_INODE)) { > > + InodeSize =3D sizeof (EXT4_INODE); > > + NeedsToZeroRest =3D TRUE; > > + } > > + > > + Inode =3D AllocateZeroPool (InodeSize); > > + > > + if (!Inode) { > > + return NULL; > > + } > > + > > + if (NeedsToZeroRest) { > > + Inode->i_extra_isize =3D 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) =3D=3D EXT4_INO_TYP= E_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) =3D=3D 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 ( > > + EXT4_FILE *File > > + ) > > +{ > > + BOOLEAN HugeFile; > > + UINT64 Blocks; > > + > > + HugeFile =3D Ext4HasRoCompat (File->Partition, EXT4_FEATURE_RO_COMPA= T_HUGE_FILE); > > + Blocks =3D File->Inode->i_blocks; > > + > > + if(HugeFile) { > > + Blocks |=3D ((UINT64)File->Inode->i_osd2.data_linux.l_i_blocks_hig= h) << 32; > > + > > + // If HUGE_FILE is enabled and EXT4_HUGE_FILE_FL is set in the ino= de's flags, each unit > > + // in i_blocks corresponds to an actual filesystem block > > + if(File->Inode->i_flags & EXT4_HUGE_FILE_FL) { > > + return Blocks * File->Partition->BlockSize; > > + } > > + } > > + > > + // Else, each i_blocks unit corresponds to 512 bytes > > + return 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 EpochSeconds Epoch seconds. > > + @param 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 =3D (EpochSeconds / 86400) + 2440588; > > + j =3D J + 32044; > > + g =3D j / 146097; > > + dg =3D j % 146097; > > + c =3D (((dg / 36524) + 1) * 3) / 4; > > + dc =3D dg - (c * 36524); > > + b =3D dc / 1461; > > + db =3D dc % 1461; > > + a =3D (((db / 365) + 1) * 3) / 4; > > + da =3D db - (a * 365); > > + y =3D (g * 400) + (c * 100) + (b * 4) + a; > > + m =3D (((da * 5) + 308) / 153) - 2; > > + d =3D da - (((m + 4) * 153) / 5) + 122; > > + > > + Time->Year =3D (UINT16)(y - 4800 + ((m + 2) / 12)); > > + Time->Month =3D ((m + 2) % 12) + 1; > > + Time->Day =3D (UINT8)(d + 1); > > + > > + ss =3D EpochSeconds % 60; > > + a =3D (EpochSeconds - ss) / 60; > > + mm =3D a % 60; > > + b =3D (a - mm) / 60; > > + hh =3D b % 24; > > + > > + Time->Hour =3D (UINT8)hh; > > + Time->Minute =3D (UINT8)mm; > > + Time->Second =3D (UINT8)ss; > > + Time->Nanosecond =3D 0; > > + > > +} > > + > > +// The time format used to (de/en)code timestamp and timestamp_extra i= s 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 =3D File->Inode; \ > > + UINT64 SecondsEpoch =3D Inode->Field; \ > > + UINT32 Nanoseconds =3D 0; \ > > + \ > > + if (Ext4InodeHasField (Inode, Field ## _extra)) { \ > > + SecondsEpoch |=3D ((UINT64)(Inode->Field ## _extra & EXT4_EXTRA_= TIMESTAMP_MASK)) << 32; \ > > + Nanoseconds =3D Inode->Field ## _extra >> 2; = \ > > + } = \ > > + EpochToEfiTime ((UINTN)SecondsEpoch, Time); = \ > > + Time->Nanosecond =3D 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 =3D File->Inode; > > + > > + if (!Ext4InodeHasField (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(!Ext4HasMetadataCsum (Partition)) { > > + return TRUE; > > + } > > + > > + Csum =3D Ext4CalculateInodeChecksum (Partition, Inode, InodeNum); > > + > > + DiskCsum =3D Inode->i_osd2.data_linux.l_i_checksum_lo; > > + > > + if (Ext4InodeHasField (Inode, i_checksum_hi)) { > > + DiskCsum |=3D ((UINT32)Inode->i_checksum_hi) << 16; > > + } else { > > + // Only keep the lower bits for the comparison if the checksum is = 16 bits. > > + Csum &=3D 0xffff; > > + } > > + > > + #if 0 > > + DEBUG ((EFI_D_INFO, "[ext4] Inode %d csum %x vs %x\n", InodeNum, C= sum, DiskCsum)); > > + #endif > > + > > + return Csum =3D=3D DiskCsum; > > +} > > diff --git a/Features/Ext4Pkg/Ext4Dxe/Partition.c b/Features/Ext4Pkg/Ex= t4Dxe/Partition.c > > new file mode 100644 > > index 0000000000..a2c4c57e78 > > --- /dev/null > > +++ b/Features/Ext4Pkg/Ext4Dxe/Partition.c > > @@ -0,0 +1,120 @@ > > +/** > > + @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 protoco= l. > > + > > + @param[in] DeviceHandle Handle to the block device. > > + @param[in] DiskIo Pointer to an EFI_DISK_IO_PROTOC= OL. > > + @param[in opt] DiskIo2 Pointer to an EFI_DISK_IO2_PROTO= COL, if supported. > > + @param[in] BlockIo Pointer to an EFI_BLOCK_IO_PROTO= COL. > > + > > + @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 =3D AllocateZeroPool (sizeof (*Part)); > > + > > + if(Part =3D=3D NULL) { > > + return EFI_OUT_OF_RESOURCES; > > + } > > + > > + InitializeListHead(&Part->OpenFiles); > > + > > + Part->BlockIo =3D BlockIo; > > + Part->DiskIo =3D DiskIo; > > + Part->DiskIo2 =3D DiskIo2; > > + > > + Status =3D Ext4OpenSuperblock (Part); > > + > > + if(EFI_ERROR (Status)) { > > + FreePool (Part); > > + return Status; > > + } > > + > > + Part->Interface.Revision =3D EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVIS= ION; > > + Part->Interface.OpenVolume =3D Ext4OpenVolume; > > + Status =3D 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, EXT4_PARTITION *Partition > > + ) > > +{ > > + // TODO: Support revision 2 (needs DISK_IO2 + asynchronous IO) > > + File->Protocol.Revision =3D EFI_FILE_PROTOCOL_REVISION; > > + File->Protocol.Open =3D Ext4Open; > > + File->Protocol.Close =3D Ext4Close; > > + File->Protocol.Delete =3D Ext4Delete; > > + File->Protocol.Read =3D Ext4ReadFile; > > + File->Protocol.Write =3D Ext4WriteFile; > > + File->Protocol.SetPosition =3D Ext4SetPosition; > > + File->Protocol.GetPosition =3D Ext4GetPosition; > > + File->Protocol.GetInfo =3D Ext4GetInfo; > > + > > + File->Partition =3D 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; > > + Partition->Unmounting =3D TRUE; > > + Ext4CloseInternal (Partition->Root); > > + > > + BASE_LIST_FOR_EACH_SAFE(Entry, NextEntry, &Partition->OpenFiles) { > > + EXT4_FILE *File =3D Ext4FileFromOpenFileNode(Entry); > > + > > + Ext4CloseInternal(File); > > + } > > + > > + FreePool (Partition->BlockGroups); > > + FreePool (Partition); > > + > > + return EFI_SUCCESS; > > +} > > diff --git a/Features/Ext4Pkg/Ext4Dxe/Superblock.c b/Features/Ext4Pkg/E= xt4Dxe/Superblock.c > > new file mode 100644 > > index 0000000000..18d8295a1f > > --- /dev/null > > +++ b/Features/Ext4Pkg/Ext4Dxe/Superblock.c > > @@ -0,0 +1,257 @@ > > +/** > > + @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 =3D EXT4_FEATURE_COMPAT_EXT_= ATTR; > > + > > +STATIC CONST UINT32 gSupportedRoCompatFeat =3D > > + EXT4_FEATURE_RO_COMPAT_DIR_NLINK | EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZ= E | > > + EXT4_FEATURE_RO_COMPAT_HUGE_FILE | EXT4_FEATURE_RO_COMPAT_LARGE_FILE= | > > + EXT4_FEATURE_RO_COMPAT_GDT_CSUM | EXT4_FEATURE_RO_COMPAT_METADATA_CS= UM | EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER; > > +// TODO: Add btree support > > +STATIC CONST UINT32 gSupportedIncompatFeat =3D > > + 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; > > + > > +// TODO: Add meta_bg support > > + > > +// Note: We ignore MMP because it's impossible that it's mapped elsewh= ere, > > +// I think (unless there's some sort of network setup where we're acce= ssing a remote partition). > > + > > +BOOLEAN > > +Ext4SuperblockValidate ( > > + EXT4_SUPERBLOCK *sb > > + ) > > +{ > > + if(sb->s_magic !=3D EXT4_SIGNATURE) { > > + return FALSE; > > + } > > + > > + // TODO: We should try to support EXT2/3 partitions too > > + if(sb->s_rev_level !=3D EXT4_DYNAMIC_REV && sb->s_rev_level !=3D EXT= 4_GOOD_OLD_REV) { > > + return FALSE; > > + } > > + > > + // TODO: Is this correct behaviour? Imagine the power cuts out, shou= ld the system fail to boot because > > + // we're scared of touching something corrupt? > > + if((sb->s_state & EXT4_FS_STATE_UNMOUNTED) =3D=3D 0) { > > + return FALSE; > > + } > > + > > + return TRUE; > > +} > > + > > +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 fiel= d =3D 0. > > + UINT32 Checksum =3D Ext4CalculateChecksum ( > > + Partition, > > + sb, > > + OFFSET_OF (EXT4_SUPERBLOCK, s_checksum), > > + ~0U > > + ); > > + > > + return Checksum; > > +} > > + > > +STATIC BOOLEAN > > +Ext4VerifySuperblockChecksum ( > > + EXT4_PARTITION *Partition, CONST EXT4_SUPERBLOCK *sb > > + ) > > +{ > > + if(!Ext4HasMetadataCsum (Partition)) { > > + return TRUE; > > + } > > + > > + return sb->s_checksum =3D=3D Ext4CalculateSuperblockChecksum (Partit= ion, sb); > > +} > > + > > +/** > > + Opens and parses the superblock. > > + > > + @param[out] Partition Partition structure to fill with filesyst= em details. > > + @retval EFI_SUCCESS Parsing was succesful and the partition i= s 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; > > + > > + Status =3D Ext4ReadDiskIo ( > > + Partition, > > + &Partition->SuperBlock, > > + sizeof (EXT4_SUPERBLOCK), > > + EXT4_SUPERBLOCK_OFFSET > > + ); > > + > > + if (EFI_ERROR (Status)) { > > + return Status; > > + } > > + > > + Sb =3D &Partition->SuperBlock; > > + > > + if (!Ext4SuperblockValidate (Sb)) { > > + return EFI_VOLUME_CORRUPTED; > > + } > > + > > + if (Sb->s_rev_level =3D=3D EXT4_DYNAMIC_REV) { > > + Partition->FeaturesCompat =3D Sb->s_feature_compat; > > + Partition->FeaturesIncompat =3D Sb->s_feature_incompat; > > + Partition->FeaturesRoCompat =3D Sb->s_feature_ro_compat; > > + Partition->InodeSize =3D Sb->s_inode_size; > > + } else { > > + // GOOD_OLD_REV > > + Partition->FeaturesCompat =3D Partition->FeaturesIncompat =3D Part= ition->FeaturesRoCompat =3D 0; > > + Partition->InodeSize =3D 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 a= nd to avoid > > + // accidentally opening an ext2/3/4 filesystem we don't understand, = which would be disasterous. > > + > > + if (Partition->FeaturesIncompat & ~gSupportedIncompatFeat) { > > + DEBUG ((EFI_D_INFO, "[Ext4] Unsupported %lx\n", Partition->Feature= sIncompat & ~gSupportedIncompatFeat)); > > + return EFI_UNSUPPORTED; > > + } > > + > > + // This should be removed once we add ext2/3 support in the future. > > + if ((Partition->FeaturesIncompat & EXT4_FEATURE_INCOMPAT_EXTENTS) = =3D=3D 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 !=3D EXT4_CHECKSUM_CRC32C) { > > + return EFI_UNSUPPORTED; > > + } > > + > > + if (Partition->FeaturesIncompat & EXT4_FEATURE_INCOMPAT_CSUM_SEED) { > > + Partition->InitialSeed =3D Sb->s_checksum_seed; > > + } else { > > + Partition->InitialSeed =3D Ext4CalculateChecksum (Partition, Sb->s= _uuid, 16, ~0U); > > + } > > + > > + UnsupportedRoCompat =3D Partition->FeaturesRoCompat & ~gSupportedRoC= ompatFeat; > > + > > + if (UnsupportedRoCompat !=3D 0) { > > + DEBUG ((EFI_D_INFO, "[Ext4] Unsupported ro compat %x\n", Unsupport= edRoCompat)); > > + Partition->ReadOnly =3D TRUE; > > + } > > + > > + (VOID)gSupportedCompatFeat; > > + > > + DEBUG ((EFI_D_INFO, "Read only =3D %u\n", Partition->ReadOnly)); > > + > > + Partition->BlockSize =3D 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 !=3D 8 * Partition->BlockSize) { > > + return EFI_UNSUPPORTED; > > + } > > + > > + Partition->NumberBlocks =3D Ext4MakeBlockNumberFromHalfs (Partition,= Sb->s_blocks_count, Sb->s_blocks_count_hi); > > + Partition->NumberBlockGroups =3D DivU64x32 (Partition->NumberBlocks,= Sb->s_blocks_per_group); > > + > > + DEBUG (( > > + EFI_D_INFO, > > + "[ext4] Number of blocks =3D %lu\n[ext4] Number of block groups: %= lu\n", > > + Partition->NumberBlocks, > > + Partition->NumberBlockGroups > > + )); > > + > > + if (Ext4Is64Bit (Partition)) { > > + Partition->DescSize =3D Sb->s_desc_size; > > + } else { > > + Partition->DescSize =3D EXT4_OLD_BLOCK_DESC_SIZE; > > + } > > + > > + if (Partition->DescSize < EXT4_64BIT_BLOCK_DESC_SIZE && Ext4Is64Bit = (Partition)) { > > + // 64 bit filesystems need DescSize to be 64 bytes > > + return EFI_VOLUME_CORRUPTED; > > + } > > + > > + if (!Ext4VerifySuperblockChecksum (Partition, Sb)) { > > + DEBUG ((EFI_D_ERROR, "[ext4] Bad superblock checksum %lx\n", Ext4C= alculateSuperblockChecksum (Partition, Sb))); > > + return EFI_VOLUME_CORRUPTED; > > + } > > + > > + NrBlocks =3D (UINTN)DivU64x32Remainder ( > > + Partition->NumberBlockGroups * Partition= ->DescSize, > > + Partition->BlockSize, > > + &NrBlocksRem > > + ); > > + > > + if(NrBlocksRem !=3D 0) { > > + NrBlocks++; > > + } > > + > > + Partition->BlockGroups =3D Ext4AllocAndReadBlocks (Partition, NrBloc= ks, Partition->BlockSize =3D=3D 1024 ? 2 : 1); > > + > > + if (!Partition->BlockGroups) { > > + return EFI_OUT_OF_RESOURCES; > > + } > > + > > + for (Index =3D 0; Index < Partition->NumberBlockGroups; Index++) { > > + EXT4_BLOCK_GROUP_DESC *Desc; > > + > > + Desc =3D Ext4GetBlockGroupDesc (Partition, Index); > > + if (!Ext4VerifyBlockGroupDescChecksum (Partition, Desc, Index)) { > > + DEBUG ((EFI_D_INFO, "[ext4] Block group descriptor %u has an inv= alid 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 =3D Ext4OpenVolume (&Partition->Interface, (EFI_FILE_PROTOCOL= **)&Partition->Root); > > + > > + DEBUG ((EFI_D_INFO, "[ext4] Root File %p\n", 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(!Ext4HasMetadataCsum (Partition)) { > > + return 0; > > + } > > + > > + switch(Partition->SuperBlock.s_checksum_type) { > > + case EXT4_CHECKSUM_CRC32C: > > + // For some reason, EXT4 really likes non-inverted CRC32C checks= ums, so we stick to that here. > > + return ~CalculateCrc32c(Buffer, Length, ~InitialValue); > > + default: > > + UNREACHABLE (); > > UNREACHABLE allows the compiler to optimise the control flow with this > assumption, e.g. remove the redundant "return 0" below. Basically you > make a case distinction here (switch), and then tell the compiler only > one of those cases is reachable. In my opinion, remove the switch > altogether and add an ASSERT on the equality constraint. This would > avoid awkward code generation and potentially reachable control flow > interruptions (in case the constraint does not hold and the compiler > decided to generate the branch, but just terminate it immediately or > whatever), add debug feedback, increase readability, and so on. Also > nitpicking, but I try to document *why* ASSERTs hold. > > > + return 0; > > + } > > +} > --=20 Pedro Falcato