From nobody Mon Apr 29 15:48:50 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) client-ip=66.175.222.108; envelope-from=bounce+27952+78445+1787277+3901457@groups.io; helo=mail02.groups.io; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) smtp.mailfrom=bounce+27952+78445+1787277+3901457@groups.io; dmarc=fail(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1627662669; cv=none; d=zohomail.com; s=zohoarc; b=ll6D+ODlvXiulZzeCNDqdZ7YZz1QTH8A2Ebau69pU7DJtcf5cIAQjGLX3tUWyEE8/TFhZ22YqzQgKnjOHPpIq8pM/moJ3HTJOsbeQ5WvG3jtM+X9vLdWpQsKYIUgxHkccNzzY6SrVvqbQJnexAMk+/M9O6N24xv0WjrP0/N5XRY= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1627662669; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:References:Sender:Subject:To; bh=3RSPJr2EVqqLASM8TtWfq5n1WxldtnBOt9Yo+VSVaZA=; b=B959SncZpDBN6RgFPdzlfGEJ7AM6R2X5HzXgZjcEtrE9uDXQkR9HY4EfVRHCb/TXuKXzb0RaxvNCIg4tnrNFd92+K+aUx71dkiHcVMf1Is6mPB4zSZ6PH6TD7UTWi3GK7/2JMyUEB1lQ1yREs0NHEyUmEPyeA5gzXZXnZftjaVw= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) smtp.mailfrom=bounce+27952+78445+1787277+3901457@groups.io; dmarc=fail header.from= (p=none dis=none) Received: from mail02.groups.io (mail02.groups.io [66.175.222.108]) by mx.zohomail.com with SMTPS id 1627662669550966.5440743156046; Fri, 30 Jul 2021 09:31:09 -0700 (PDT) Return-Path: X-Received: by 127.0.0.2 with SMTP id Mbu2YY1788612xLqf1Bl9Uzz; Fri, 30 Jul 2021 09:31:09 -0700 X-Received: from mail-wr1-f46.google.com (mail-wr1-f46.google.com [209.85.221.46]) by mx.groups.io with SMTP id smtpd.web09.59.1627661942707053534 for ; Fri, 30 Jul 2021 09:19:03 -0700 X-Received: by mail-wr1-f46.google.com with SMTP id p5so11983847wro.7 for ; Fri, 30 Jul 2021 09:19:02 -0700 (PDT) X-Gm-Message-State: EBVBZCGIN3U9fJZGnLA1RDQ3x1787277AA= X-Google-Smtp-Source: ABdhPJw1Ov1GbZdUaJRH8fqX8p6snVTWGQcTNsyoPvNeFINvD2W/chjK5RAkUvN/6n2v/2K5FM5UIA== X-Received: by 2002:a5d:424d:: with SMTP id s13mr3938201wrr.356.1627661941098; Fri, 30 Jul 2021 09:19:01 -0700 (PDT) X-Received: from PC-PEDRO.lan (bl8-253-151.dsl.telepac.pt. [85.241.253.151]) by smtp.gmail.com with ESMTPSA id u15sm2011984wmn.6.2021.07.30.09.19.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 30 Jul 2021 09:19:00 -0700 (PDT) From: "Pedro Falcato" To: devel@edk2.groups.io Cc: Pedro Falcato , Leif Lindholm , Michael D Kinney , Bret Barkelew Subject: [edk2-devel] [Patch 1/3] Ext4Pkg: Add Ext4Pkg.dec and Ext4Pkg.uni. Date: Fri, 30 Jul 2021 17:16:40 +0100 Message-Id: <20210730161641.7245-2-pedro.falcato@gmail.com> In-Reply-To: <20210730161641.7245-1-pedro.falcato@gmail.com> References: <20210730161641.7245-1-pedro.falcato@gmail.com> MIME-Version: 1.0 Precedence: Bulk List-Unsubscribe: List-Subscribe: List-Help: Sender: devel@edk2.groups.io List-Id: Mailing-List: list devel@edk2.groups.io; contact devel+owner@edk2.groups.io Reply-To: devel@edk2.groups.io,pedro.falcato@gmail.com Content-Transfer-Encoding: quoted-printable DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=groups.io; q=dns/txt; s=20140610; t=1627662669; bh=LAUDF+lbAM699VJ+o1cBKR6iaxtFJt6tF6vJiqxBqgY=; h=Cc:Date:From:Reply-To:Subject:To; b=VqO2Y3bOihGz3UkmlJhvsF6TrOll4y9JoeW2CTNw1vNzyqh8LaRLo9VUtZ/L0GZ0al6 Wu80UBVkiwOhHrIKazP1crn50MpGSgtQP6vFchQdk/4oyiqwwx8L4cU1oMG6sLQ3TaXXO VWPx8jqjR5WabCvTkqALNhTTbrVXoS44y0Q= X-ZohoMail-DKIM: pass (identity @groups.io) X-ZM-MESSAGEID: 1627662670330100002 Content-Type: text/plain; charset="utf-8" These files are needed to build Ext4Pkg. Cc: Leif Lindholm Cc: Michael D Kinney Cc: Bret Barkelew Signed-off-by: Pedro Falcato --- Features/Ext4Pkg/Ext4Pkg.dec | 17 +++++++++++++++++ Features/Ext4Pkg/Ext4Pkg.uni | 14 ++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 Features/Ext4Pkg/Ext4Pkg.dec create mode 100644 Features/Ext4Pkg/Ext4Pkg.uni diff --git a/Features/Ext4Pkg/Ext4Pkg.dec b/Features/Ext4Pkg/Ext4Pkg.dec new file mode 100644 index 0000000000..f1f8b39c3c --- /dev/null +++ b/Features/Ext4Pkg/Ext4Pkg.dec @@ -0,0 +1,17 @@ +## @file +# Ext4 Package +# +# This package provides libraries and drivers related to the ext4 filesys= tem implementation. +# More details are available at: https://www.kernel.org/doc/html/v5.4/fil= esystems/ext4/index.html +# +# Copyright (c) 2021 Pedro Falcato +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + DEC_SPECIFICATION =3D 0x00010005 + PACKAGE_NAME =3D Ext4Pkg + PACKAGE_UNI_FILE =3D Ext4Pkg.uni + PACKAGE_GUID =3D 6B4BF998-668B-46D3-BCFA-971F99F8708C + PACKAGE_VERSION =3D 0.1 diff --git a/Features/Ext4Pkg/Ext4Pkg.uni b/Features/Ext4Pkg/Ext4Pkg.uni new file mode 100644 index 0000000000..abeadd8fd9 --- /dev/null +++ b/Features/Ext4Pkg/Ext4Pkg.uni @@ -0,0 +1,14 @@ +## @file +# Ext4 Package +# +# This package provides libraries and drivers related to the ext4 filesys= tem implementation. +# More details are available at: https://www.kernel.org/doc/html/v5.4/fil= esystems/ext4/index.html +# +# Copyright (c) 2021 Pedro Falcato +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +#string STR_PACKAGE_ABSTRACT #language en-US "Module implementa= tions for the EXT4 file system" + +#string STR_PACKAGE_DESCRIPTION #language en-US "This package cont= ains UEFI drivers and libraries for the EXT4 file system." --=20 2.32.0 -=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#78445): https://edk2.groups.io/g/devel/message/78445 Mute This Topic: https://groups.io/mt/84553675/1787277 Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org] -=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D- From nobody Mon Apr 29 15:48:50 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) client-ip=66.175.222.108; envelope-from=bounce+27952+78447+1787277+3901457@groups.io; helo=mail02.groups.io; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) smtp.mailfrom=bounce+27952+78447+1787277+3901457@groups.io; dmarc=fail(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1627662674; cv=none; d=zohomail.com; s=zohoarc; b=E/onHHkFbjcQTQc98rJafMJ9gyx/TWkoyT+thi9kh/n4+bVDwo1694ufw+d4Dh1wrIrSw4R3gr8XncfJCdw4RukaEbL/QQeF88MUQYU0023+YoTp1M9p25lCZKe7dDxZvv8yE7of5qKM9TrcoW1Z9f1PXVt4s1fiFq3BtY1PgcY= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1627662674; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:References:Sender:Subject:To; bh=RZEFx52w+GJO0LSiqygn25vcG1KbyGIU7fsXhxEGylw=; b=FjHTpChglU61nWl9vqAwGd190lSQ5ob6Annay4y+tMJhdOz3+ay5YE8b3d6HEYQ07UFs/tOIMx0hNaag6q8QL24vPip6VlLMKAeCzZThzEtwupIKNVkpDXyh5RR47HoUSxt3QSfA9d8QEe+MZKiZwQM+KTFFajAXkv7GxpMzGx4= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) smtp.mailfrom=bounce+27952+78447+1787277+3901457@groups.io; dmarc=fail header.from= (p=none dis=none) Received: from mail02.groups.io (mail02.groups.io [66.175.222.108]) by mx.zohomail.com with SMTPS id 16276626743641014.8663083884043; Fri, 30 Jul 2021 09:31:14 -0700 (PDT) Return-Path: X-Received: by 127.0.0.2 with SMTP id 8x62YY1788612xmL2BGwnfM5; Fri, 30 Jul 2021 09:31:13 -0700 X-Received: from mail-wm1-f43.google.com (mail-wm1-f43.google.com [209.85.128.43]) by mx.groups.io with SMTP id smtpd.web10.56.1627661944774678262 for ; Fri, 30 Jul 2021 09:19:05 -0700 X-Received: by mail-wm1-f43.google.com with SMTP id o5-20020a1c4d050000b02901fc3a62af78so9540226wmh.3 for ; Fri, 30 Jul 2021 09:19:04 -0700 (PDT) X-Gm-Message-State: umq3AEQsVuk4jkHVIF6SdAchx1787277AA= X-Google-Smtp-Source: ABdhPJzZ6Oi7uTe2xSKVIbSUoSWbN2zLyiOJh4L/L5T1e/FcGjwxQBbok7rUD3VoWIwggM83HKm8nw== X-Received: by 2002:a7b:c1d8:: with SMTP id a24mr3827075wmj.155.1627661942564; Fri, 30 Jul 2021 09:19:02 -0700 (PDT) X-Received: from PC-PEDRO.lan (bl8-253-151.dsl.telepac.pt. [85.241.253.151]) by smtp.gmail.com with ESMTPSA id u15sm2011984wmn.6.2021.07.30.09.19.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 30 Jul 2021 09:19:01 -0700 (PDT) From: "Pedro Falcato" To: devel@edk2.groups.io Cc: Pedro Falcato , Leif Lindholm , Michael D Kinney , Bret Barkelew Subject: [edk2-devel] [Patch 2/3] Ext4Pkg: Add Ext4Dxe driver. Date: Fri, 30 Jul 2021 17:16:41 +0100 Message-Id: <20210730161641.7245-3-pedro.falcato@gmail.com> In-Reply-To: <20210730161641.7245-1-pedro.falcato@gmail.com> References: <20210730161641.7245-1-pedro.falcato@gmail.com> MIME-Version: 1.0 Precedence: Bulk List-Unsubscribe: List-Subscribe: List-Help: Sender: devel@edk2.groups.io List-Id: Mailing-List: list devel@edk2.groups.io; contact devel+owner@edk2.groups.io Reply-To: devel@edk2.groups.io,pedro.falcato@gmail.com Content-Transfer-Encoding: quoted-printable DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=groups.io; q=dns/txt; s=20140610; t=1627662673; bh=LQAXGAchPJm9dS2ihawBeqyqk1adYWfNglQQNQmoRhM=; h=Cc:Date:From:Reply-To:Subject:To; b=Y+M4WMJl3h66Q1nsKs9ovmn5TloiQ14z91WH5+++HobGzAEnlxAMvJZWsOWPGEf1aYi +51EQQ9Cxa1KlN0URbFzCDcysp1yhFlfDUlaww1/JJ5Mt4hNgVOeanoSQx8F6cUZWwxXp IZ33AOmcZdddSsFqqF915oDsK1cqYf8DLAU= X-ZohoMail-DKIM: pass (identity @groups.io) X-ZM-MESSAGEID: 1627662674842100005 Content-Type: text/plain; charset="utf-8" Adds a UEFI EXT4 filesystem driver that implements the EFI_FILE_PROTOCOL 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/Ext4D= xe/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 pointer t= o the read inode. + + @return Status of the inode read. + */ +EFI_STATUS +Ext4ReadInode ( + IN EXT4_PARTITION *Partition, IN EXT4_INO_NR InodeNum, OUT EXT4_INODE **= OutIno + ) +{ + UINT64 InodeOffset; + UINT32 BlockGroupNumber; + EXT4_INODE *Inode; + EXT4_BLOCK_GROUP_DESC *BlockGroup; + EXT4_BLOCK_NR InodeTableStart; + EFI_STATUS Status; + + BlockGroupNumber =3D (UINT32)DivU64x64Remainder ( + InodeNum - 1, + Partition->SuperBlock.s_inodes_per_group, + &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 write = 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) + InodeOff= set * 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 partition. + @param[in] BlockGroupDesc Pointer to the block group descriptor. + @param[in] BlockGroupNum Number of the block group. + + @return The checksum. +*/ +STATIC +UINT16 +Ext4CalculateBlockGroupDescChecksumMetadataCsum ( + IN CONST EXT4_PARTITION *Partition, + IN CONST EXT4_BLOCK_GROUP_DESC *BlockGroupDesc, + IN UINT32 BlockGroupNum + ) +{ + UINT32 Csum; + UINT16 Dummy; + + Dummy =3D 0; + + Csum =3D Ext4CalculateChecksum (Partition, &BlockGroupNum, sizeof (Block= GroupNum), Partition->InitialSeed); + Csum =3D Ext4CalculateChecksum (Partition, BlockGroupDesc, OFFSET_OF (EX= T4_BLOCK_GROUP_DESC, bg_checksum), Csum); + Csum =3D Ext4CalculateChecksum (Partition, &Dummy, sizeof (Dummy), Csum); + Csum =3D + Ext4CalculateChecksum ( + Partition, + &BlockGroupDesc->bg_block_bitmap_hi, + Partition->DescSize - OFFSET_OF (EXT4_BLOCK_GROUP_DESC, bg_block_bit= map_hi), + Csum + ); + return (UINT16)Csum; +} + +/** + Calculates the checksum of the block group descriptor for GDT_CSUM enab= led filesystems. + @param[in] Partition Pointer to the opened EXT4 partition. + @param[in] BlockGroupDesc Pointer to the block group descriptor. + @param[in] BlockGroupNum Number of the block group. + + @return The checksum. +*/ +STATIC +UINT16 +Ext4CalculateBlockGroupDescChecksumGdtCsum ( + IN CONST EXT4_PARTITION *Partition, + IN CONST EXT4_BLOCK_GROUP_DESC *BlockGroupDesc, + IN UINT32 BlockGroupNum + ) +{ + UINT16 Csum; + UINT16 Dummy; + + Dummy =3D 0; + + Csum =3D CalculateCrc16 (Partition->SuperBlock.s_uuid, 16, 0); + Csum =3D CalculateCrc16 (&BlockGroupNum, sizeof (BlockGroupNum), Csum); + Csum =3D CalculateCrc16 (BlockGroupDesc, OFFSET_OF (EXT4_BLOCK_GROUP_DES= C, 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_bit= map_hi), + Csum + ); + return Csum; +} + +/** + Checks if the checksum of the block group descriptor is correct. + @param[in] Partition Pointer to the opened EXT4 partition. + @param[in] BlockGroupDesc Pointer to the block group descriptor. + @param[in] BlockGroupNum Number of the block group. + + @return TRUE if checksum is correct, FALSE if there is corruption. +*/ +BOOLEAN +Ext4VerifyBlockGroupDescChecksum ( + IN CONST EXT4_PARTITION *Partition, + IN CONST EXT4_BLOCK_GROUP_DESC *BlockGroupDesc, + IN UINT32 BlockGroupNum + ) +{ + if(!Ext4HasMetadataCsum (Partition) && !Ext4HasGdtCsum (Partition)) { + return TRUE; + } + + return Ext4CalculateBlockGroupDescChecksum (Partition, BlockGroupDesc, B= lockGroupNum) =3D=3D BlockGroupDesc->bg_checksum; +} + +/** + Calculates the checksum of the block group descriptor. + @param[in] Partition Pointer to the opened EXT4 partition. + @param[in] BlockGroupDesc Pointer to the block group descriptor. + @param[in] BlockGroupNum Number of the block group. + + @return The checksum. +*/ +UINT16 +Ext4CalculateBlockGroupDescChecksum ( + IN CONST EXT4_PARTITION *Partition, + IN CONST EXT4_BLOCK_GROUP_DESC *BlockGroupDesc, + IN UINT32 BlockGroupNum + ) +{ + if(Partition->FeaturesRoCompat & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) { + return Ext4CalculateBlockGroupDescChecksumMetadataCsum (Partition, Blo= ckGroupDesc, BlockGroupNum); + } else if(Partition->FeaturesRoCompat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM)= { + return Ext4CalculateBlockGroupDescChecksumGdtCsum (Partition, BlockGro= upDesc, BlockGroupNum); + } + + return 0; +} diff --git a/Features/Ext4Pkg/Ext4Dxe/Collation.c b/Features/Ext4Pkg/Ext4Dx= e/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 NUL= L; + +/* + * Note: This code is heavily based on FatPkg's Unicode collation, since t= hey seem to know what + * they're doing. + * PS: Maybe all this code could be put in a library? It looks heavily sha= reable. + */ +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 &gEfiUnicodeCollationPr= otocolGuid); + RetStatus =3D EFI_UNSUPPORTED; + GetEfiGlobalVariable2 (VariableName, (VOID **)&Language, NULL); + + 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. + 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 str= ing comparisons + within the driver (a good example of an application of this is filename= comparison). + + @param[in] DriverHandle Handle to the driver image. + + @retval EFI_SUCCESS Unicode collation was successfully initialised. + @retval !EFI_SUCCESS Failure. +*/ +EFI_STATUS +Ext4InitialiseUnicodeCollation ( + EFI_HANDLE DriverHandle + ) +{ + EFI_STATUS Status; + + Status =3D EFI_UNSUPPORTED; + + // + // First try to use RFC 4646 Unicode Collation 2 Protocol. + // + Status =3D Ext4InitialiseUnicodeCollationInternal ( + DriverHandle, + &gEfiUnicodeCollation2ProtocolGuid, + L"PlatformLang", + (CONST CHAR8 *)PcdGetPtr (PcdUefiVariableDefaultPlatformLang) + ); + // + // If the attempt to use Unicode Collation 2 Protocol fails, then we fal= l 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_COLLATI= ON_PROTOCOL's StriColl + for more details. + + @param[in] Str1 Pointer to a null terminated string. + @param[in] Str2 Pointer to a null terminated string. + + @retval 0 Str1 is equivalent to Str2. + @retval >0 Str1 is lexically greater than Str2. + @retval <0 Str1 is lexically less than Str2. +*/ +INTN +Ext4StrCmpInsensitive ( + IN CHAR16 *Str1, + IN CHAR16 *Str2 + ) +{ + return gUnicodeCollationInterface->StriColl (gUnicodeCollationInterface,= Str1, Str2); +} diff --git a/Features/Ext4Pkg/Ext4Dxe/Crc16.c b/Features/Ext4Pkg/Ext4Dxe/Cr= c16.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/Ext4Dxe/C= rc32c.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, 0x35f1141c, + 0x26a1e7e8, 0xd4ca64eb, 0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b, + 0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24, 0x105ec76f, 0xe235446c, + 0xf165b798, 0x030e349b, 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384, + 0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, 0x5d1d08bf, 0xaf768bbc, + 0xbc267848, 0x4e4dfb4b, 0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a, + 0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35, 0xaa64d611, 0x580f5512, + 0x4b5fa6e6, 0xb93425e5, 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa, + 0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, 0xf779deae, 0x05125dad, + 0x1642ae59, 0xe4292d5a, 0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a, + 0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595, 0x417b1dbc, 0xb3109ebf, + 0xa0406d4b, 0x522bee48, 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957, + 0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, 0x0c38d26c, 0xfe53516f, + 0xed03a29b, 0x1f682198, 0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927, + 0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38, 0xdbfc821c, 0x2997011f, + 0x3ac7f2eb, 0xc8ac71e8, 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7, + 0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, 0xa65c047d, 0x5437877e, + 0x4767748a, 0xb50cf789, 0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859, + 0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46, 0x7198540d, 0x83f3d70e, + 0x90a324fa, 0x62c8a7f9, 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6, + 0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36, 0x3cdb9bdd, 0xceb018de, + 0xdde0eb2a, 0x2f8b6829, 0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c, + 0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93, 0x082f63b7, 0xfa44e0b4, + 0xe9141340, 0x1b7f9043, 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c, + 0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, 0x55326b08, 0xa759e80b, + 0xb4091bff, 0x466298fc, 0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c, + 0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033, 0xa24bb5a6, 0x502036a5, + 0x4370c551, 0xb11b4652, 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d, + 0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, 0xef087a76, 0x1d63f975, + 0x0e330a81, 0xfc588982, 0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d, + 0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622, 0x38cc2a06, 0xcaa7a905, + 0xd9f75af1, 0x2b9cd9f2, 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed, + 0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, 0x0417b1db, 0xf67c32d8, + 0xe52cc12c, 0x1747422f, 0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff, + 0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0, 0xd3d3e1ab, 0x21b862a8, + 0x32e8915c, 0xc083125f, 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540, + 0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, 0x9e902e7b, 0x6cfbad78, + 0x7fab5e8c, 0x8dc0dd8f, 0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee, + 0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1, 0x69e9f0d5, 0x9b8273d6, + 0x88d28022, 0x7ab90321, 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e, + 0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81, 0x34f4f86a, 0xc69f7b69, + 0xd5cf889d, 0x27a40b9e, 0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e, + 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351 +}; + +/** + Calculates the CRC32c checksum of the given buffer. + + @param[in] Buffer Pointer to the buffer. + @param[in] Length Length of the buffer, in bytes. + @param[in] InitialValue Initial value of the CRC. + + @return The CRC32c checksum. +*/ +UINT32 +CalculateCrc32c ( + IN CONST VOID *Buffer, + IN UINTN Length, + IN UINT32 InitialValue + ) +{ + CONST UINT8 *Buf; + UINT32 Crc; + + Buf =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/Ext4Dx= e/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 UTF-16= /UCS-2 + + @param[in] Entry Pointer to a EXT4_DIR_ENTRY. + @param[out] Ucs2FileName Pointer to an array of CHAR16's, of siz= e EXT4_NAME_MAX + 1. + + @retval EFI_SUCCESS The filename was succesfully retrieved and conver= ted 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-buffe= r-like + // function. Therefore, we need to allocate from the pool (inside UTF8St= rToUCS2), + // copy it to our out buffer (Ucs2FileName) and free. + + 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 entry. + + @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, &BlockRemainder); + if(BlockRemainder !=3D 0) { + // Directory inodes need to have block aligned sizes + return EFI_VOLUME_CORRUPTED; + } + + while(Off < DirInoSize) { + UINTN Length; + + 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); + + if(Entry->name_len > RemainingBlock || Entry->rec_len > RemainingBlo= ck) { + // Corrupted filesystem + // TODO: Do the proper ext4 corruption detection thing and dirty t= he filesystem. + FreePool (Buf); + return EFI_VOLUME_CORRUPTED; + } + + // Ignore names bigger than our limit. + + /* Note: I think having a limit is sane because: + 1) It's nicer to work with. + 2) Linux and a number of BSDs also have a filename limit of 255. + */ + if(Entry->name_len > EXT4_NAME_MAX) { + continue; + } + + // Unused entry + if(Entry->inode =3D=3D 0) { + b +=3D Entry->rec_len; + continue; + } + + CHAR16 Ucs2FileName[EXT4_NAME_MAX + 1]; + + Status =3D Ext4GetUcs2DirentName (Entry, Ucs2FileName); + + /* In theory, this should never fail. + * In reality, it's quite possible that it can fail, considering fil= enames in + * Linux (and probably other nixes) are just null-terminated bags of= bytes, and don't + * need to form valid ASCII/UTF-8 sequences. + */ + if (EFI_ERROR (Status)) { + // If we error out, skip this entry + // I'm not sure if this is correct behaviour, but I don't think th= ere'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; + + 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 be op= en. + @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); + + File->InodeNum =3D Entry->inode; + + Ext4SetupFile (File, (EXT4_PARTITION *)Partition); + + 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 be op= en. + @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))) { + 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->Ope= nFilesListNode); + + 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 to %l= u)\n", Dirent->rec_len, RequiredSize)); + return FALSE; + } + + // Dirent sizes need to be 4 byte aligned + if (Dirent->rec_len % 4) { + return FALSE; + } + + return TRUE; +} + +/** + Reads a directory entry. + + @param[in] Partition Pointer to the ext4 partition. + @param[in] File Pointer to the open directory. + @param[out] Buffer Pointer to the output buffer. + @param[in] Offset Initial directory position. + @param[in out] OutLength Pointer to a UINTN that contains the length= of the buffer, + and the length of the actual EFI_FILE_INFO = after the call. + + @return Result of the operation. +*/ +EFI_STATUS +Ext4ReadDir ( + IN EXT4_PARTITION *Partition, + IN EXT4_FILE *File, + OUT VOID *Buffer, + IN UINT64 Offset, + IN OUT UINTN *OutLength + ) +{ + 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, &BlockRemainder); + 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 time + // Note that we don't need to read any padding that may exist after 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, Off= set)); + #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", Offset= )); + 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, &TempFile, &= Entry); + + if (EFI_ERROR (Status)) { + goto Out; + } + + // TODO: Is this needed? + if (!StrCmp (TempFile->FileName, L".") || !StrCmp (TempFile->FileName,= L"..")) { + Offset +=3D Entry.rec_len; + Ext4CloseInternal (TempFile); + continue; + } + + #if 0 + DEBUG ((EFI_D_INFO, "[ext4] Listing file %s\n", TempFile->FileName)); + #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/Ext4Dxe= /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 read. + + @return Success status of the disk read. + */ +EFI_STATUS +Ext4ReadDiskIo ( + IN EXT4_PARTITION *Partition, + OUT VOID *Buffer, + IN UINTN Length, + IN UINT64 Offset + ) +{ + return Ext4DiskIo (Partition)->ReadDisk (Ext4DiskIo (Partition), Ext4Med= iaId (Partition), Offset, Length, Buffer); +} + +/** + Reads blocks from the partition's disk using the DISK_IO protocol. + + @param[in] Partition Pointer to the opened ext4 partition. + @param[out] Buffer Pointer to a destination buffer. + @param[in] NumberBlocks Length of the read, in filesystem blocks. + @param[in] BlockNumber Starting block number. + + @return Success status of the read. + */ +EFI_STATUS +Ext4ReadBlocks ( + IN EXT4_PARTITION *Partition, + OUT VOID *Buffer, + IN UINTN NumberBlocks, + IN EXT4_BLOCK_NR BlockNumber + ) +{ + return Ext4ReadDiskIo (Partition, Buffer, NumberBlocks * Partition->Bloc= kSize, BlockNumber * Partition->BlockSize); +} + +/** + Allocates a buffer and reads blocks from the partition's disk using the= DISK_IO protocol. + This function is deprecated and will be removed in the future. + + @param[in] Partition Pointer to the opened ext4 partition. + @param[in] NumberBlocks Length of the read, in filesystem blocks. + @param[in] BlockNumber Starting block number. + + @return Buffer allocated by AllocatePool, or NULL if some part of the p= rocess + 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 EFI_S= UCCESS) { + FreePool (Buf); + return NULL; + } + + return Buf; +} diff --git a/Features/Ext4Pkg/Ext4Dxe/Ext4Disk.h b/Features/Ext4Pkg/Ext4Dxe= /Ext4Disk.h new file mode 100644 index 0000000000..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 +=20 + 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). +=20 + An ext2/3/4 filesystem (here on out referred to as simply an ext4 filesy= stem, + due to the similarities) is composed of various concepts: +=20 + 1) Superblock + The superblock is the structure near (1024 bytes offset from the star= t) + the start of the partition, and describes the filesystem in general. + Here, we get to know the size of the filesystem's blocks, which featu= res + it supports or not, whether it's been cleanly unmounted, how many blo= cks + we have, etc. +=20 + 2) Block groups + EXT4 filesystems are divided into block groups, and each block group = covers + s_blocks_per_group(8 * Block Size) blocks. Each block group has an + associated block group descriptor; these are present directly after t= he + 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 previo= usly). +=20 + 3) Blocks + The ext4 filesystem is divided in blocks, of size s_log_block_size ^ = 1024. + Blocks can be allocated using individual block groups's bitmaps. Note + that block 0 is invalid and its presence on extents/block tables means + it's part of a file hole, and that particular location must be read as + a block full of zeros. +=20 + 4) Inodes + The ext4 filesystem divides files/directories into inodes (originally + index nodes). Each file/socket/symlink/directory/etc (here on out ref= erred + to as a file, since there is no distinction under the ext4 filesystem= ) is + stored as a /nameless/ inode, that is stored in some block group's in= ode + table. Each inode has s_inode_size size (or GOOD_OLD_INODE_SIZE if it= 's + an old filesystem), and holds various metadata about the file. Since = the + largest inode structure right now is ~160 bytes, the rest of the inode + contains inline extended attributes. Inodes' data is stored using eit= her + data blocks (under ext2/3) or extents (under ext4). +=20 + 5) Extents + Ext4 inodes store data in extents. These let N contiguous logical blo= cks + that are represented by N contiguous physical blocks be represented b= y a + single extent structure, which minimizes filesystem metadata bloat and + speeds up block mapping (particularly due to the fact that high-quali= ty + ext4 implementations like linux's try /really/ hard to make the file + contiguous, so it's common to have files with almost 0 fragmentation). + Inodes that use extents store them in a tree, and the top of the tree + is stored on i_data. The tree's leaves always start with an + EXT4_EXTENT_HEADER and contain EXT4_EXTENT_INDEX on eh_depth !=3D 0 a= nd + EXT4_EXTENT on eh_depth =3D 0; these entries are always sorted by log= ical + block. +=20 + 6) Directories + Ext4 directories are files that store name -> inode mappings for the + logical directory; this is where files get their names, which means e= xt4 + inodes do not themselves have names, since they can be linked (presen= t) + multiple times with different names. Directories can store entries in= two + different ways: + 1) Classical linear directories: They store entries as a mostly-lin= ked + mostly-list of EXT4_DIR_ENTRY. + 2) Hash tree directories: These are used for larger directories, wi= th + hundreds of entries, and are designed in a backwards compatible = way. + These are not yet implemented in the Ext4Dxe driver. +=20 + 7) Journal + Ext3/4 filesystems have a journal to help protect the filesystem agai= nst + system crashes. This is not yet implemented in Ext4Dxe but is describ= ed + 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 now +#define EXT4_FEATURE_INCOMPAT_DIRDATA 0x01000 +#define EXT4_FEATURE_INCOMPAT_CSUM_SEED 0x02000 +#define EXT4_FEATURE_INCOMPAT_LARGEDIR 0x04000 +#define EXT4_FEATURE_INCOMPAT_INLINE_DATA 0x08000 +#define EXT4_FEATURE_INCOMPAT_ENCRYPT 0x10000 + +#define EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 +#define EXT4_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 +#define EXT4_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 // Unused +#define EXT4_FEATURE_RO_COMPAT_HUGE_FILE 0x0008 +#define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010 +#define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020 +#define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040 +#define EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT 0x0080 // Not implemente= d in ext4 +#define EXT4_FEATURE_RO_COMPAT_QUOTA 0x0100 +#define EXT4_FEATURE_RO_COMPAT_BIGALLOC 0x0200 +#define EXT4_FEATURE_RO_COMPAT_METADATA_CSUM 0x0400 +#define EXT4_FEATURE_RO_COMPAT_REPLICA 0x0800 // Not used + +// We explicitly don't recognise this, so we get read only. +#define EXT4_FEATURE_RO_COMPAT_READONLY 0x1000 +#define EXT4_FEATURE_RO_COMPAT_PROJECT 0x2000 + +/* Important notes about the features + * Absolutely needed features: + * 1) Every incompat, because we might want to mount root filesystems + * 2) Relevant RO_COMPATs(I'm not sure of what to do wrt quota, project) + */ + +#define EXT4_INO_TYPE_FIFO 0x1000 +#define EXT4_INO_TYPE_CHARDEV 0x2000 +#define EXT4_INO_TYPE_DIR 0x4000 +#define EXT4_INO_TYPE_BLOCKDEV 0x6000 +#define EXT4_INO_TYPE_REGFILE 0x8000 +#define EXT4_INO_TYPE_SYMLINK 0xA000 +#define EXT4_INO_TYPE_UNIX_SOCK 0xC000 + +/* Inode flags */ +#define EXT4_SECRM_FL 0x00000001 +#define EXT4_UNRM_FL 0x00000002 +#define EXT4_COMPR_FL 0x00000004 +#define EXT4_SYNC_FL 0x00000008 +#define EXT4_IMMUTABLE_FL 0x00000010 +#define EXT4_APPEND_FL 0x00000020 +#define EXT4_NODUMP_FL 0x00000040 +#define EXT4_NOATIME_FL 0x00000080 +#define EXT4_DIRTY_FL 0x00000100 +#define EXT4_COMPRBLK_FL 0x00000200 +#define EXT4_NOCOMPR_FL 0x00000400 +#define EXT4_ECOMPR_FL 0x00000800 +#define EXT4_BTREE_FL 0x00001000 +#define EXT4_INDEX_FL 0x00002000 +#define EXT4_JOURNAL_DATA_FL 0x00004000 +#define EXT4_NOTAIL_FL 0x00008000 +#define EXT4_DIRSYNC_FL 0x00010000 +#define EXT4_TOPDIR_FL 0x00020000 +#define EXT4_HUGE_FILE_FL 0x00040000 +#define EXT4_EXTENTS_FL 0x00080000 +#define EXT4_VERITY_FL 0x00100000 +#define EXT4_EA_INODE_FL 0x00200000 +#define EXT4_RESERVED_FL 0x80000000 + +/* File type flags that are stored in the directory entries */ +#define EXT4_FT_UNKNOWN 0 +#define EXT4_FT_REG_FILE 1 +#define EXT4_FT_DIR 2 +#define EXT4_FT_CHRDEV 3 +#define EXT4_FT_BLKDEV 4 +#define EXT4_FT_FIFO 5 +#define EXT4_FT_SOCK 6 +#define EXT4_FT_SYMLINK 7 + +typedef struct { + UINT32 s_inodes_count; + UINT32 s_blocks_count; + UINT32 s_r_blocks_count; + UINT32 s_free_blocks_count; + UINT32 s_free_inodes_count; + UINT32 s_first_data_block; + UINT32 s_log_block_size; + UINT32 s_log_frag_size; + UINT32 s_blocks_per_group; + UINT32 s_frags_per_group; + UINT32 s_inodes_per_group; + UINT32 s_mtime; + UINT32 s_wtime; + UINT16 s_mnt_count; + UINT16 s_max_mnt_count; + UINT16 s_magic; + UINT16 s_state; + UINT16 s_errors; + UINT16 s_minor_rev_level; + UINT32 s_lastcheck; + UINT32 s_check_interval; + UINT32 s_creator_os; + UINT32 s_rev_level; + UINT16 s_def_resuid; + UINT16 s_def_resgid; + + /* Every field after this comment is revision >=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 stru= ct 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 from us= ing 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 high 16= bits + // and the low 32 bits - this forms a 48-bit block number + UINT16 ee_start_hi; + UINT32 ee_start_lo; +} EXT4_EXTENT; + +// This on-disk structure is present at all levels except the bottom +typedef struct { + // This index covers logical blocks from 'ei_block' + UINT32 ei_block; + // Block of the next level of the extent tree, similarly split in a high= and low portion. + UINT32 ei_leaf_lo; + UINT16 ei_leaf_hi; + + UINT16 ei_unused; +} EXT4_EXTENT_INDEX; + +typedef struct { + // Needs to be EXT4_EXTENT_HEADER_MAGIC + UINT16 eh_magic; + // Number of entries + UINT16 eh_entries; + // Maximum number of entries that could follow this header + UINT16 eh_max; + // Depth of this node in the tree - the tree can be at most 5 levels deep + UINT16 eh_depth; + // Unused by standard ext4 + UINT32 eh_generation; +} EXT4_EXTENT_HEADER; + +#define EXT4_EXTENT_HEADER_MAGIC 0xF30A + +// Specified by ext4 docs and backed by a bunch of math +#define EXT4_EXTENT_TREE_MAX_DEPTH 5 + +typedef struct { + // CRC32C of UUID + inode number + igeneration + extent block + UINT32 eb_checksum; +} EXT4_EXTENT_TAIL; + +typedef UINT64 EXT4_BLOCK_NR; +typedef UINT32 EXT4_INO_NR; + +#define EXT4_INODE_SIZE(ino) (((UINT64)ino->i_size_hi << 32) | ino->i_siz= e_lo) + +#endif diff --git a/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.c b/Features/Ext4Pkg/Ext4Dxe/= 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 mExt4DriverNameTab= le[] =3D { + { + "eng;en", + L"Ext4 File System Driver" + }, + { + NULL, + NULL + } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mExt4ControllerNam= eTable[] =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 O= PTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +extern EFI_COMPONENT_NAME_PROTOCOL gExt4ComponentName; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gExt4ComponentN= ame =3D { + Ext4ComponentNameGetDriverName, + Ext4ComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gExt4Component= Name2 =3D { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME)Ext4ComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME)Ext4ComponentNameGetControllerN= ame, + "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 O= PTIONAL, + 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, &gEfiDiskIoProto= colGuid); + + 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 the + // 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; + } + + 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, blockIo= ); + + if(!EFI_ERROR (Status)) { + return Status; + } + + DEBUG ((EFI_D_INFO, "[ext4] Error mounting %x\n", Status)); + +Error: + if(DiskIo) { + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + BindingProtocol->ImageHandle, + ControllerHandle + ); + } + + if(DiskIo2) { + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDiskIo2ProtocolGuid, + BindingProtocol->ImageHandle, + ControllerHandle + ); + } + + if(blockIo) { + gBS->CloseProtocol ( + ControllerHandle, + &gEfiBlockIoProtocolGuid, + BindingProtocol->ImageHandle, + ControllerHandle + ); + } + + return Status; +} diff --git a/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.h b/Features/Ext4Pkg/Ext4Dxe/= Ext4Dxe.h new file mode 100644 index 0000000000..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 protocol. + + @param[in] DeviceHandle Handle to the block device. + @param[in] DiskIo Pointer to an EFI_DISK_IO_PROTOCOL. + @param[in opt] DiskIo2 Pointer to an EFI_DISK_IO2_PROTOCOL,= if supported. + @param[in] BlockIo Pointer to an EFI_BLOCK_IO_PROTOCOL. + + @retval EFI_SUCCESS The opening was successful. + !EFI_SUCCESS Opening failed. + */ +EFI_STATUS +Ext4OpenPartition ( + IN EFI_HANDLE DeviceHandle, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN OPTIONAL EFI_DISK_IO2_PROTOCOL *DiskIo2, + IN EFI_BLOCK_IO_PROTOCOL *BlockIo + ); + +typedef struct _Ext4File EXT4_FILE; + +typedef struct _Ext4_PARTITION { + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL Interface; + EFI_DISK_IO_PROTOCOL *DiskIo; + EFI_DISK_IO2_PROTOCOL *DiskIo2; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + + EXT4_SUPERBLOCK SuperBlock; + BOOLEAN Unmounting; + + UINT32 FeaturesIncompat; + UINT32 FeaturesCompat; + UINT32 FeaturesRoCompat; + UINT32 InodeSize; + UINT32 BlockSize; + BOOLEAN ReadOnly; + UINT64 NumberBlockGroups; + EXT4_BLOCK_NR NumberBlocks; + + EXT4_BLOCK_GROUP_DESC *BlockGroups; + UINT32 DescSize; + EXT4_FILE *Root; + + UINT32 InitialSeed; + + LIST_ENTRY OpenFiles; +} EXT4_PARTITION; + +/** + Opens and parses the superblock. + + @param[out] Partition Partition structure to fill with filesystem d= etails. + @retval EFI_SUCCESS Parsing was succesful and the partition is a + valid ext4 partition. + */ +EFI_STATUS +Ext4OpenSuperblock ( + OUT EXT4_PARTITION *Partition + ); + +/** + Retrieves the EFI_BLOCK_IO_PROTOCOL of the partition. + + @param[in] Partition Pointer to the opened ext4 partition. + @return The Block IO protocol associated with the partition. + */ +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 NULL 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 read. + + @return Success status of the disk read. + */ +EFI_STATUS +Ext4ReadDiskIo ( + IN EXT4_PARTITION *Partition, + OUT VOID *Buffer, + IN UINTN Length, + IN UINT64 Offset + ); + +/** + Reads blocks from the partition's disk using the DISK_IO protocol. + + @param[in] Partition Pointer to the opened ext4 partition. + @param[out] Buffer Pointer to a destination buffer. + @param[in] NumberBlocks Length of the read, in filesystem blocks. + @param[in] BlockNumber Starting block number. + + @return Success status of the read. + */ +EFI_STATUS +Ext4ReadBlocks ( + IN EXT4_PARTITION *Partition, + OUT VOID *Buffer, + IN UINTN NumberBlocks, + IN EXT4_BLOCK_NR BlockNumber + ); + +/** + Allocates a buffer and reads blocks from the partition's disk using the= DISK_IO protocol. + This function is deprecated and will be removed in the future. + + @param[in] Partition Pointer to the opened ext4 partition. + @param[in] NumberBlocks Length of the read, in filesystem blocks. + @param[in] BlockNumber Starting block number. + + @return Buffer allocated by AllocatePool, or NULL if some part of the p= rocess + failed. + */ +VOID * +Ext4AllocAndReadBlocks ( + IN EXT4_PARTITION *Partition, + IN UINTN NumberBlocks, + IN EXT4_BLOCK_NR BlockNumber + ); + +/** + Checks if the opened partition has the 64-bit feature (see EXT4_FEATURE= _INCOMPAT_64BIT). + + @param[in] Partition Pointer to the opened ext4 partition. + + @return TRUE if EXT4_FEATURE_INCOMPAT_64BIT is enabled, else FALSE. + */ +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, High. + */ +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 + Block= Group * Partition->DescSize); +} + +/** + Reads an inode from disk. + + @param[in] Partition Pointer to the opened partition. + @param[in] InodeNum Number of the desired Inode + @param[out] OutIno Pointer to where it will be stored a pointer t= o 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 b= ytes. + After a succesful read, it's updated to t= he 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 extent mu= st cover. + @param[out] Extent Pointer to the output buffer, where the e= xtent 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, OpenFile= sListNode) + +/** + Retrieves a directory entry. + + @param[in] Directory Pointer to the opened directory. + @param[in] NameUnicode Pointer to the UCS-2 formatted filename. + @param[in] Partition Pointer to the ext4 partition. + @param[out] Result Pointer to the destination directory entry. + + @return The result of the operation. +*/ +EFI_STATUS +Ext4RetrieveDirent ( + IN EXT4_FILE *Directory, + IN CONST CHAR16 *NameUnicode, + IN EXT4_PARTITION *Partition, + OUT EXT4_DIR_ENTRY *Result + ); + +/** + Opens a file. + + @param[in] Directory Pointer to the opened directory. + @param[in] Name Pointer to the UCS-2 formatted filename. + @param[in] Partition Pointer to the ext4 partition. + @param[in] OpenMode Mode in which the file is supposed to be op= en. + @param[out] OutFile Pointer to the newly opened file. + + @return Result of the operation. +*/ +EFI_STATUS +Ext4OpenFile ( + IN EXT4_FILE *Directory, + IN CONST CHAR16 *Name, + IN EXT4_PARTITION *Partition, + IN UINT64 OpenMode, + OUT EXT4_FILE **OutFile + ); + +/** + Opens a file using a directory entry. + + @param[in] Partition Pointer to the ext4 partition. + @param[in] OpenMode Mode in which the file is supposed to be op= en. + @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 devices = since these concepts are +// at the kernel level and are OS dependent. + +/** + Checks if a file is openable. + @param[in] File Pointer to the file trying to be opened. + + + @return TRUE if file is openable. A file is considered openable if + it's a regular file or a directory, since most other file types + don't make sense under UEFI. +*/ +STATIC inline +BOOLEAN +Ext4FileIsOpenable ( + IN CONST EXT4_FILE *File + ) +{ + return Ext4FileIsReg (File) || Ext4FileIsDir (File); +} + +#define Ext4InodeHasField(Inode, \ + Field) (Inode->i_extra_isize + EXT4_GOOD_OLD_IN= ODE_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 re= corded, + Time is zeroed. +*/ +VOID +Ext4FileCreateTime ( + IN EXT4_FILE *File, OUT EFI_TIME *Time + ); + +/** + Initialises Unicode collation, which is needed for case-insensitive str= ing comparisons + within the driver (a good example of an application of this is filename= comparison). + + @param[in] DriverHandle Handle to the driver image. + + @retval EFI_SUCCESS Unicode collation was successfully initialised. + @retval !EFI_SUCCESS Failure. +*/ +EFI_STATUS +Ext4InitialiseUnicodeCollation ( + EFI_HANDLE DriverHandle + ); + +/** + Does a case-insensitive string comparison. Refer to EFI_UNICODE_COLLATI= ON_PROTOCOL's StriColl + for more details. + + @param[in] Str1 Pointer to a null terminated string. + @param[in] Str2 Pointer to a null terminated string. + + @retval 0 Str1 is equivalent to Str2. + @retval >0 Str1 is lexically greater than Str2. + @retval <0 Str1 is lexically less than Str2. +*/ +INTN +Ext4StrCmpInsensitive ( + IN CHAR16 *Str1, + IN CHAR16 *Str2 + ); + +/** + Retrieves the filename of the directory entry and converts it to UTF-16= /UCS-2 + + @param[in] Entry Pointer to a EXT4_DIR_ENTRY. + @param[out] Ucs2FileName Pointer to an array of CHAR16's, of siz= e EXT4_NAME_MAX + 1. + + @retval EFI_SUCCESS Unicode collation was successfully initialised. + @retval !EFI_SUCCESS Failure. +*/ +EFI_STATUS +Ext4GetUcs2DirentName ( + IN EXT4_DIR_ENTRY *Entry, + OUT CHAR16 Ucs2FileName[EXT4_NAME_MAX + 1] + ); + +/** + Retrieves information about the file and stores it in the EFI_FILE_INFO= format. + + @param[in] File Pointer to an opened file. + @param[out] Info Pointer to a EFI_FILE_INFO. + @param[in out] BufferSize Pointer to the buffer size + + @return Status of the file information request. +*/ +EFI_STATUS +Ext4GetFileInfo ( + IN EXT4_FILE *File, + OUT EFI_FILE_INFO *Info, + IN OUT UINTN *BufferSize + ); + +/** + Reads a directory entry. + + @param[in] Partition Pointer to the ext4 partition. + @param[in] File Pointer to the open directory. + @param[out] Buffer Pointer to the output buffer. + @param[in] Offset Initial directory position. + @param[in out] OutLength Pointer to a UINTN that contains the length= of the buffer, + and the length of the actual EFI_FILE_INFO = after the call. + + @return Result of the operation. +*/ +EFI_STATUS +Ext4ReadDir ( + IN EXT4_PARTITION *Partition, + IN EXT4_FILE *File, + OUT VOID *Buffer, + IN UINT64 Offset, + IN OUT UINTN *OutLength + ); + +/** + Initialises the (empty) extents map, that will work as a cache of exten= ts. + + @param[in] File Pointer to the open file. + + @return Result of the operation. +*/ +EFI_STATUS +Ext4InitExtentsMap ( + IN EXT4_FILE *File + ); + +/** + Frees the extents map, deleting every extent stored. + + @param[in] File Pointer to the open file. +*/ +VOID +Ext4FreeExtentsMap ( + IN EXT4_FILE *File + ); + +/** + Calculates the CRC32c checksum of the given buffer. + + @param[in] Buffer Pointer to the buffer. + @param[in] Length Length of the buffer, in bytes. + @param[in] InitialValue Initial value of the CRC. + + @return The CRC32c checksum. +*/ +UINT32 +CalculateCrc32c ( + IN CONST VOID *Buffer, + IN UINTN Length, + IN UINT32 InitialValue + ); + +/** + Calculates the CRC16 checksum of the given buffer. + + @param[in] Buffer Pointer to the buffer. + @param[in] Length Length of the buffer, in bytes. + @param[in] InitialValue Initial value of the CRC. + + @return The CRC16 checksum. +*/ +UINT16 +CalculateCrc16 ( + IN CONST VOID *Buffer, + IN UINTN Length, + IN UINT16 InitialValue + ); + +/** + Calculates the checksum of the given buffer. + @param[in] Partition Pointer to the opened EXT4 partition. + @param[in] Buffer Pointer to the buffer. + @param[in] Length Length of the buffer, in bytes. + @param[in] InitialValue Initial value of the CRC. + + @return The checksum. +*/ +UINT32 +Ext4CalculateChecksum ( + IN CONST EXT4_PARTITION *Partition, + IN CONST VOID *Buffer, + IN UINTN Length, + IN UINT32 InitialValue + ); + +/** + Calculates the checksum of the given inode. + @param[in] Partition Pointer to the opened EXT4 partition. + @param[in] Inode Pointer to the inode. + @param[in] InodeNum Inode number. + + @return The checksum. +*/ +UINT32 +Ext4CalculateInodeChecksum ( + IN CONST EXT4_PARTITION *Partition, + IN CONST EXT4_INODE *Inode, + IN EXT4_INO_NR InodeNum + ); + +/** + Checks if the checksum of the inode is correct. + @param[in] Partition Pointer to the opened EXT4 partition. + @param[in] Inode Pointer to the inode. + @param[in] InodeNum Inode number. + + @return TRUE if checksum is correct, FALSE if there is corruption. +*/ +BOOLEAN +Ext4CheckInodeChecksum ( + IN CONST EXT4_PARTITION *Partition, + IN CONST EXT4_INODE *Inode, + IN EXT4_INO_NR InodeNum + ); + +/** + Unmounts and frees an ext4 partition. + + @param[in] Partition Pointer to the opened partition. + + @return Status of the unmount. + */ +EFI_STATUS +Ext4UnmountAndFreePartition ( + IN EXT4_PARTITION *Partition + ); + +/** + Checks if the checksum of the block group descriptor is correct. + @param[in] Partition Pointer to the opened EXT4 partition. + @param[in] BlockGroupDesc Pointer to the block group descriptor. + @param[in] BlockGroupNum Number of the block group. + + @return TRUE if checksum is correct, FALSE if there is corruption. +*/ +BOOLEAN +Ext4VerifyBlockGroupDescChecksum ( + IN CONST EXT4_PARTITION *Partition, + IN CONST EXT4_BLOCK_GROUP_DESC *BlockGroupDesc, + IN UINT32 BlockGroupNum + ); + +/** + Calculates the checksum of the block group descriptor. + @param[in] Partition Pointer to the opened EXT4 partition. + @param[in] BlockGroupDesc Pointer to the block group descriptor. + @param[in] BlockGroupNum Number of the block group. + + @return The checksum. +*/ +UINT16 +Ext4CalculateBlockGroupDescChecksum ( + IN CONST EXT4_PARTITION *Partition, + IN CONST EXT4_BLOCK_GROUP_DESC *BlockGroupDesc, + IN UINT32 BlockGroupNum + ); + +/** + Verifies the existance of a particular RO compat feature set. + @param[in] Partition Pointer to the opened EXT4 partitio= n. + @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 RoCompa= tFeatureSet; +} + +/** + Verifies the existance of a particular compat feature set. + @param[in] Partition Pointer to the opened EXT4 partitio= n. + @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 CompatFeatu= reSet; +} + +/** + Verifies the existance of a particular compat feature set. + @param[in] Partition Pointer to the opened EXT4 partitio= n. + @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 Incompa= tFeatureSet; +} + +// Note: Might be a good idea to provide generic Ext4Has$feature() through= macros. + +/** + Checks if metadata_csum is enabled on the partition. + @param[in] Partition Pointer to the opened EXT4 partitio= n. + @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_CSUM); +} + +/** + Checks if gdt_csum is enabled on the partition. + @param[in] Partition Pointer to the opened EXT4 partitio= n. + @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_CSUM); +} + +#endif diff --git a/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.inf b/Features/Ext4Pkg/Ext4Dx= e/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 partiti= on that is formatted +# with the EXT4 file system. +# More details are available at: https://www.kernel.org/doc/html/v5.4/fil= esystems/ext4/index.html +# +# Copyright (c) 2021 Pedro Falcato +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# Layout of an EXT2/3/4 filesystem: +# (note: this driver has been developed using +# https://www.kernel.org/doc/html/latest/filesystems/ext4/index.html as +# documentation). +# =20 +# An ext2/3/4 filesystem (here on out referred to as simply an ext4 file= system, +# due to the similarities) is composed of various concepts: +# =20 +# 1) Superblock +# The superblock is the structure near (1024 bytes offset from the st= art) +# the start of the partition, and describes the filesystem in general. +# Here, we get to know the size of the filesystem's blocks, which fea= tures +# it supports or not, whether it's been cleanly unmounted, how many b= locks +# we have, etc. +# =20 +# 2) Block groups +# EXT4 filesystems are divided into block groups, and each block grou= p covers +# s_blocks_per_group(8 * Block Size) blocks. Each block group has an +# associated block group descriptor; these are present directly after= the +# superblock. Each block group descriptor contains the location of the +# inode table, and the inode and block bitmaps (note these bitmaps ar= e only +# a block long, which gets us the 8 * Block Size formula covered prev= iously). +# =20 +# 3) Blocks +# The ext4 filesystem is divided in blocks, of size s_log_block_size = ^ 1024. +# Blocks can be allocated using individual block groups's bitmaps. No= te +# that block 0 is invalid and its presence on extents/block tables me= ans +# it's part of a file hole, and that particular location must be read= as +# a block full of zeros. +# =20 +# 4) Inodes +# The ext4 filesystem divides files/directories into inodes (original= ly +# index nodes). Each file/socket/symlink/directory/etc (here on out r= eferred +# to as a file, since there is no distinction under the ext4 filesyst= em) is +# stored as a /nameless/ inode, that is stored in some block group's = inode +# table. Each inode has s_inode_size size (or GOOD_OLD_INODE_SIZE if = it's +# an old filesystem), and holds various metadata about the file. Sinc= e the +# largest inode structure right now is ~160 bytes, the rest of the in= ode +# contains inline extended attributes. Inodes' data is stored using e= ither +# data blocks (under ext2/3) or extents (under ext4). +# =20 +# 5) Extents +# Ext4 inodes store data in extents. These let N contiguous logical b= locks +# that are represented by N contiguous physical blocks be represented= by a +# single extent structure, which minimizes filesystem metadata bloat = and +# speeds up block mapping (particularly due to the fact that high-qua= lity +# ext4 implementations like linux's try /really/ hard to make the file +# contiguous, so it's common to have files with almost 0 fragmentatio= n). +# Inodes that use extents store them in a tree, and the top of the tr= ee +# 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 l= ogical +# block. +# =20 +# 6) Directories +# Ext4 directories are files that store name -> inode mappings for the +# logical directory; this is where files get their names, which means= ext4 +# inodes do not themselves have names, since they can be linked (pres= ent) +# multiple times with different names. Directories can store entries = in two +# different ways: +# 1) Classical linear directories: They store entries as a mostly-l= inked +# 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 compatibl= e way. +# These are not yet implemented in the Ext4Dxe driver. +# =20 +# 7) Journal +# Ext3/4 filesystems have a journal to help protect the filesystem ag= ainst +# system crashes. This is not yet implemented in Ext4Dxe but is descr= ibed +# 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-303C7F4E6FD6 + 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 ## UNDEFIN= ED + gEfiFileSystemInfoGuid ## SOMETIMES_CONSUMES ## UNDEFIN= ED + gEfiFileSystemVolumeLabelInfoIdGuid ## SOMETIMES_CONSUMES ## UNDEFIN= ED + +[Protocols] + gEfiDiskIoProtocolGuid ## TO_START + gEfiDiskIo2ProtocolGuid ## TO_START + gEfiBlockIoProtocolGuid ## TO_START + gEfiSimpleFileSystemProtocolGuid ## BY_START + gEfiUnicodeCollationProtocolGuid ## TO_START + gEfiUnicodeCollation2ProtocolGuid ## TO_START + +[Pcd] + gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultLang ## SOMETIM= ES_CONSUMES + gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultPlatformLang ## SOMETIM= ES_CONSUMES diff --git a/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.uni b/Features/Ext4Pkg/Ext4Dx= e/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 partiti= on that is formatted +# with the EXT4 file system. +# More details are available at: https://www.kernel.org/doc/html/v5.4/fil= esystems/ext4/index.html +# +# Copyright (c) 2021 Pedro Falcato +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +#string STR_MODULE_ABSTRACT #language en-US "UEFI driver for th= e EXT4 file system." + +#string STR_MODULE_DESCRIPTION #language en-US "Produces the EFI S= imple File System protocol." diff --git a/Features/Ext4Pkg/Ext4Dxe/Extents.c b/Features/Ext4Pkg/Ext4Dxe/= 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 corruption. +*/ +BOOLEAN +Ext4CheckExtentChecksum ( + IN CONST EXT4_EXTENT_HEADER *ExtHeader, + IN CONST EXT4_FILE *File + ); + +/** + Calculates the checksum of the extent data block. + @param[in] ExtHeader Pointer to the EXT4_EXTENT_HEADER. + @param[in] File Pointer to the file. + + @return The checksum. +*/ +UINT32 +Ext4CalculateExtentChecksum ( + IN CONST EXT4_EXTENT_HEADER *ExtHeader, + IN CONST EXT4_FILE *File + ); + +/** + Caches a range of extents, by allocating pool memory for each extent an= d 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 struct= ure. + + @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", Header= ->eh_depth)); + return FALSE; + } + + if(Header->eh_magic !=3D EXT4_EXTENT_HEADER_MAGIC) { + DEBUG ((EFI_D_ERROR, "[ext4] Invalid extent header magic %x\n", Header= ->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 to a + logical block in a given extent tree node. + + @param[in] Header Pointer to the EXT4_EXTENT_HEADER struct= ure. + @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 sorted. + + 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 struct= ure. + @param[in] LogicalBlock Block that will be searched + + @return Pointer to the found EXT4_EXTENT_INDEX, else NULL if the array = is empty. + Note: The caller must check if the logical block + is actually mapped under the given extent. +*/ +STATIC +EXT4_EXTENT * +Ext4BinsearchExtentExt ( + IN EXT4_EXTENT_HEADER *Header, + IN EXT4_BLOCK_NR LogicalBlock + ) +{ + EXT4_EXTENT *l; + EXT4_EXTENT *r; + EXT4_EXTENT *m; + + l =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 sorted. + + // 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 structu= re. + + @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; + +/** + Retrieves an extent from an EXT4 inode. + @param[in] Partition Pointer to the opened EXT4 partition. + @param[in] File Pointer to the opened file. + @param[in] LogicalBlock Block number which the returned extent mu= st cover. + @param[out] Extent Pointer to the output buffer, where the e= xtent 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", LogicalB= lock)); + + if (!(Inode->i_flags & EXT4_EXTENTS_FL)) { + return EFI_UNSUPPORTED; + } + + // ext4 does not have support for logical block numbers bigger than UINT= 32_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 misses + // We should find a way to get (or cache) holes + if ((Ext =3D Ext4GetExtentFromMap (File, (UINT32)LogicalBlock)) !=3D NUL= L) { + *Extent =3D *Ext; + GetExtentCacheHits++; + + return EFI_SUCCESS; + } + + // Slow path, we'll need to read from disk and (try to) cache those exte= nts. + + 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 any le= aves + // As such, every entry is an EXT4_EXTENT_INDEX entry + // Note: Entries after the extent header, either index or actual exten= t, are always sorted. + // Therefore, we can use binary search, and it's actually the standard= 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, Ext4ExtentIdxLeafBloc= k (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 quite li= kely that we + * may need to access things sequentially. Furthermore, ext4 block alloc= ation as done + * by linux (and possibly other systems) is quite fancy and usually it r= esults in a small number of extents. + * Therefore, we shouldn't have any memory issues. + */ + Ext4CacheExtents (File, (EXT4_EXTENT *)(ExtHeader + 1), ExtHeader->eh_en= tries); + + 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 > Lo= gicalBlock)) { + // 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", Extent= 1->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 embedded ke= y. + Used in the extent map's ORDERED_COLLECTION. + + @param[in] StandaloneKey Pointer to the bare key. + + @param[in] UserStruct Pointer to the user structure with the embedded + key. + + @retval <0 If StandaloneKey compares less than UserStruct's key. + + @retval 0 If StandaloneKey compares equal to UserStruct's key. + + @retval >0 If StandaloneKey compares greater than UserStruct's key. +**/ +STATIC +INTN EFIAPI +Ext4ExtentsMapKeyCompare ( + IN CONST VOID *StandaloneKey, + IN CONST VOID *UserStruct + ) +{ + CONST EXT4_EXTENT *Extent =3D UserStruct; + + // Note that logical blocks are 32-bits in size so no truncation can hap= pen 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_bloc= k)); + 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 exten= ts. + + @param[in] File Pointer to the open file. + + @return Result of the operation. +*/ +EFI_STATUS +Ext4InitExtentsMap ( + IN EXT4_FILE *File + ) +{ + File->ExtentsMap =3D OrderedCollectionInit (Ext4ExtentsMapStructCompare,= Ext4ExtentsMapKeyCompare); + if (!File->ExtentsMap) { + return EFI_OUT_OF_RESOURCES; + } + + return EFI_SUCCESS; +} + +/** + Frees the extents map, deleting every extent stored. + + @param[in] File Pointer to the open file. +*/ +VOID +Ext4FreeExtentsMap ( + IN EXT4_FILE *File + ) +{ + // Keep calling Min(), so we get an arbitrary node we can delete. + // If Min() returns NULL, it's empty. + + ORDERED_COLLECTION_ENTRY *MinEntry =3D NULL; + + while ((MinEntry =3D OrderedCollectionMin (File->ExtentsMap)) !=3D NULL)= { + 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 extent an= d 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 NumberExten= ts + ) +{ + UINT16 i; + + /* Note that any out of memory condition might mean we don't get to cach= e 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 *)(UINTN)= 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, sizeof = (Inode->i_generation), Csum); + Csum =3D Ext4CalculateChecksum (Partition, ExtHeader, Partition->BlockSi= ze - sizeof (EXT4_EXTENT_TAIL), Csum); + + return Csum; +} + +/** + Checks if the checksum of the extent data block is correct. + @param[in] ExtHeader Pointer to the EXT4_EXTENT_HEADER. + @param[in] File Pointer to the file. + + @return TRUE if the checksum is correct, FALSE if there is corruption. +*/ +BOOLEAN +Ext4CheckExtentChecksum ( + IN CONST EXT4_EXTENT_HEADER *ExtHeader, + IN CONST EXT4_FILE *File + ) +{ + EXT4_PARTITION *Partition; + EXT4_EXTENT_TAIL *Tail; + + Partition =3D File->Partition; + + if(!Ext4HasMetadataCsum (Partition)) { + return TRUE; + } + + Tail =3D (EXT4_EXTENT_TAIL *)((CONST CHAR8 *)ExtHeader + (Partition->Blo= ckSize - 4)); + + return Tail->eb_checksum =3D=3D Ext4CalculateExtentChecksum (ExtHeader, = File); +} diff --git a/Features/Ext4Pkg/Ext4Dxe/File.c b/Features/Ext4Pkg/Ext4Dxe/Fil= e.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 l= ong. + @param[out] Length Pointer to the UINTN that will hold the l= ength 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); +} + +/** + 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 perm= ission 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 t= he 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_SUCCESS) { + 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_MOD= E_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 added + 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 fil= e 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, File->I= nodeNum)); + RemoveEntryList (&File->OpenFilesListNode); + FreePool (File->FileName); + FreePool (File->Inode); + Ext4FreeExtentsMap (File); + FreePool (File); + return EFI_SUCCESS; +} + +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, BufferSi= ze); + if(Status =3D=3D EFI_SUCCESS) { + File->Position +=3D *BufferSize; + } + + return Status; + } else if(Ext4FileIsDir (File)) { + Status =3D Ext4ReadDir (Partition, File, Buffer, File->Position, Buffe= rSize); + 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) { + 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 *BufferSize + ) +{ + // 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_FIL= E_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 UINTN *B= ufferSize + ) +{ + // 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_name, 1= 6); + 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, Buffer, = BufferSize); + } + + return EFI_UNSUPPORTED; +} + +/** + Duplicates a file structure. + + @param[in] Original Pointer to the original file. + + @return Pointer to the new file structure. + */ +STATIC +EXT4_FILE * +Ext4DuplicateFile ( + IN CONST EXT4_FILE *Original + ) +{ + EXT4_PARTITION *Partition; + EXT4_FILE *File; + + Partition =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->File= Name); + + 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/Ext4Dxe/In= ode.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 (InodeNum), = Partition->InitialSeed); + Crc =3D Ext4CalculateChecksum (Partition, &Inode->i_generation, sizeof (= 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), Crc); + + RestOfInode =3D &Inode->i_osd2.data_linux.l_i_reserved; + RestOfInodeLength =3D Partition->InodeSize - OFFSET_OF (EXT4_INODE, i_os= d2.data_linux.l_i_reserved); + + if (HasSecondChecksumField) { + UINTN Length =3D OFFSET_OF (EXT4_INODE, i_checksum_hi) - OFFSET_OF (E= XT4_INODE, i_osd2.data_linux.l_i_reserved); + + Crc =3D Ext4CalculateChecksum (Partition, &Inode->i_osd2.data_linux.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_checksum_hi + RestOfInodeLength =3D Partition->InodeSize - EXT4_GOOD_OLD_INODE_SIZE = - 4; + RestOfInode =3D &Inode->i_ctime_extra; + } + + Crc =3D Ext4CalculateChecksum (Partition, RestOfInode, RestOfInodeLength= , Crc); + + return Crc; +} + +/** + Reads from an EXT4 inode. + @param[in] Partition Pointer to the opened EXT4 partition. + @param[in] File Pointer to the opened file. + @param[out] Buffer Pointer to the buffer. + @param[in] Offset Offset of the read. + @param[in out] Length Pointer to the length of the buffer, in b= ytes. + After a succesful read, it's updated to t= he 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, *Len= gth)); + 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 curren= t block + // and then read as much as we can from the current extent. + + Status =3D Ext4GetExtent ( + Partition, + File, + DivU64x32Remainder (CurrentSeek, Partition->BlockSize, &Blo= ckOff), + &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 Exten= tLogicalBytes + UINT64 ExtentOffset; + UINTN ExtentMayRead; + + ExtentStartBytes =3D (((UINT64)Extent.ee_start_hi << 32) | Extent.= ee_start_lo) * Partition->BlockSize; + ExtentLengthBytes =3D Extent.ee_len * Partition->BlockSize; + ExtentLogicalBytes =3D (UINT64)Extent.ee_block * Partition->BlockSiz= e; + ExtentOffset =3D CurrentSeek - ExtentLogicalBytes; + ExtentMayRead =3D (UINTN)(ExtentLengthBytes - ExtentOffset); + + WasRead =3D ExtentMayRead > RemainingRead ? RemainingRead : ExtentMa= yRead; + + // DEBUG((EFI_D_INFO, "[ext4] may read %lu, remaining %lu\n", Extent= MayRead, RemainingRead)); + // DEBUG((EFI_D_INFO, "[ext4] Reading block %lu\n", (ExtentStartByte= s + ExtentOffset) / Partition->BlockSize)); + Status =3D Ext4ReadDiskIo (Partition, Buffer, WasRead, ExtentStartBy= tes + 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, CalculateCrc= 32(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), but in= the future, when + // write support is added and we need to flush inodes to disk, we could = have a bit better + // distinction between the on-disk inode and a separate, nicer to work w= ith 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_TYPE_DI= R; +} + +/** + 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_TYP= E_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_COMPAT_HU= GE_FILE); + Blocks =3D File->Inode->i_blocks; + + if(HugeFile) { + Blocks |=3D ((UINT64)File->Inode->i_osd2.data_linux.l_i_blocks_high) <= < 32; + + // If HUGE_FILE is enabled and EXT4_HUGE_FILE_FL is set in the inode's= flags, each unit + // in i_blocks corresponds to an actual filesystem block + if(File->Inode->i_flags & EXT4_HUGE_FILE_FL) { + return 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 is do= cumented 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_TIME= STAMP_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 re= corded, + 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 b= its. + Csum &=3D 0xffff; + } + + #if 0 + DEBUG ((EFI_D_INFO, "[ext4] Inode %d csum %x vs %x\n", InodeNum, Csum,= DiskCsum)); + #endif + + return Csum =3D=3D DiskCsum; +} diff --git a/Features/Ext4Pkg/Ext4Dxe/Partition.c b/Features/Ext4Pkg/Ext4Dx= e/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 protocol. + + @param[in] DeviceHandle Handle to the block device. + @param[in] DiskIo Pointer to an EFI_DISK_IO_PROTOCOL. + @param[in opt] DiskIo2 Pointer to an EFI_DISK_IO2_PROTOCOL,= if supported. + @param[in] BlockIo Pointer to an EFI_BLOCK_IO_PROTOCOL. + + @retval EFI_SUCCESS The opening was successful. + !EFI_SUCCESS Opening failed. + */ +EFI_STATUS +Ext4OpenPartition ( + IN EFI_HANDLE DeviceHandle, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN OPTIONAL EFI_DISK_IO2_PROTOCOL *DiskIo2, + IN EFI_BLOCK_IO_PROTOCOL *BlockIo + ) +{ + EXT4_PARTITION *Part; + EFI_STATUS Status; + + Part =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_REVISION; + 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/Ext4D= xe/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_ISIZE | + EXT4_FEATURE_RO_COMPAT_HUGE_FILE | EXT4_FEATURE_RO_COMPAT_LARGE_FILE | + EXT4_FEATURE_RO_COMPAT_GDT_CSUM | EXT4_FEATURE_RO_COMPAT_METADATA_CSUM |= EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER; +// 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 elsewhere, +// I think (unless there's some sort of network setup where we're accessin= g 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 EXT4_GO= OD_OLD_REV) { + return FALSE; + } + + // TODO: Is this correct behaviour? Imagine the power cuts out, should t= he 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 requ= irement + // that the checksum is done over a structure with its checksum field = =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 (Partition,= sb); +} + +/** + Opens and parses the superblock. + + @param[out] Partition Partition structure to fill with filesystem d= etails. + @retval EFI_SUCCESS Parsing was succesful and the partition is a + valid ext4 partition. + */ +EFI_STATUS +Ext4OpenSuperblock ( + OUT EXT4_PARTITION *Partition + ) +{ + UINT32 Index; + EFI_STATUS Status; + EXT4_SUPERBLOCK *Sb; + UINT32 NrBlocksRem; + UINTN NrBlocks; + UINT32 UnsupportedRoCompat; + + 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 Partitio= n->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 and t= o avoid + // accidentally opening an ext2/3/4 filesystem we don't understand, whic= h would be disasterous. + + if (Partition->FeaturesIncompat & ~gSupportedIncompatFeat) { + DEBUG ((EFI_D_INFO, "[Ext4] Unsupported %lx\n", Partition->FeaturesInc= ompat & ~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_uui= d, 16, ~0U); + } + + UnsupportedRoCompat =3D Partition->FeaturesRoCompat & ~gSupportedRoCompa= tFeat; + + if (UnsupportedRoCompat !=3D 0) { + DEBUG ((EFI_D_INFO, "[Ext4] Unsupported ro compat %x\n", UnsupportedRo= Compat)); + 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->Bl= ockSize + 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 (Par= tition)) { + // 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", Ext4Calcu= lateSuperblockChecksum (Partition, Sb))); + return EFI_VOLUME_CORRUPTED; + } + + NrBlocks =3D (UINTN)DivU64x32Remainder ( + Partition->NumberBlockGroups * Partition->De= scSize, + Partition->BlockSize, + &NrBlocksRem + ); + + if(NrBlocksRem !=3D 0) { + NrBlocks++; + } + + Partition->BlockGroups =3D Ext4AllocAndReadBlocks (Partition, NrBlocks, = 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; + =20 + Desc =3D Ext4GetBlockGroupDesc (Partition, Index); + if (!Ext4VerifyBlockGroupDescChecksum (Partition, Desc, Index)) { + DEBUG ((EFI_D_INFO, "[ext4] Block group descriptor %u has an invalid= checksum\n", Index)); + return EFI_VOLUME_CORRUPTED; + } + } + + // Note that the cast below is completely safe, because EXT4_FILE is a s= pecialisation 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 Leng= th, + 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 checksums,= so we stick to that here. + return ~CalculateCrc32c(Buffer, Length, ~InitialValue); + default: + UNREACHABLE (); + return 0; + } +} --=20 2.32.0 -=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#78447): https://edk2.groups.io/g/devel/message/78447 Mute This Topic: https://groups.io/mt/84553678/1787277 Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org] -=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D- From nobody Mon Apr 29 15:48:50 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) client-ip=66.175.222.108; envelope-from=bounce+27952+78446+1787277+3901457@groups.io; helo=mail02.groups.io; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) smtp.mailfrom=bounce+27952+78446+1787277+3901457@groups.io; dmarc=fail(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1627662670; cv=none; d=zohomail.com; s=zohoarc; b=D4xFowR3qWVq5Fo0L97JJnllv818sWr6hpR3pQnr3oSBxN0vTqAkwHMe0JT8SB6Zoszl+/6hWoaJXWBtqylqJOVPHYFi79J+VcbMApcdh93IUx6KwRpudmtcn4Q8bPph5e/YfFAZIqZ+4ANhYdEs/7mwYLxWHDoqHP9iJYN6Ol0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1627662670; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:References:Sender:Subject:To; bh=9pVndGiECWD4fVSZGbkXsc2GnGGsaw8C9R/nDkq0L6w=; b=K7UZR0WbfXJrm1YRw+f0hV++M78qZzHyvWClfOZnROHqbIRoQTP7L1Y8kUe550WUdIkIq8A0gwPZ3ekViMlmh5LNU9rlmLTg3Jnkxb4ndUyj/JosLCl33AOygMKMNjjHAasuZO1kElcFESaL8QtVpGqF86XYPh2nt2M5VYsoFlI= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) smtp.mailfrom=bounce+27952+78446+1787277+3901457@groups.io; dmarc=fail header.from= (p=none dis=none) Received: from mail02.groups.io (mail02.groups.io [66.175.222.108]) by mx.zohomail.com with SMTPS id 1627662670733290.3682781641935; Fri, 30 Jul 2021 09:31:10 -0700 (PDT) Return-Path: X-Received: by 127.0.0.2 with SMTP id WceyYY1788612xCbbsL27rkH; Fri, 30 Jul 2021 09:31:10 -0700 X-Received: from mail-wm1-f48.google.com (mail-wm1-f48.google.com [209.85.128.48]) by mx.groups.io with SMTP id smtpd.web12.62.1627661945141113358 for ; Fri, 30 Jul 2021 09:19:05 -0700 X-Received: by mail-wm1-f48.google.com with SMTP id e25-20020a05600c4b99b0290253418ba0fbso6788538wmp.1 for ; Fri, 30 Jul 2021 09:19:04 -0700 (PDT) X-Gm-Message-State: VlnjYxElItJbe37w5BCKtTQQx1787277AA= X-Google-Smtp-Source: ABdhPJxopv4oP4KRBJ3FM7m3F6oYZy+o+ZUXSdnpihFT9f/WbaTCpVSo81joQSlQot7HjN7BHDyodg== X-Received: by 2002:a05:600c:1c1f:: with SMTP id j31mr3851305wms.132.1627661943338; Fri, 30 Jul 2021 09:19:03 -0700 (PDT) X-Received: from PC-PEDRO.lan (bl8-253-151.dsl.telepac.pt. [85.241.253.151]) by smtp.gmail.com with ESMTPSA id u15sm2011984wmn.6.2021.07.30.09.19.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 30 Jul 2021 09:19:03 -0700 (PDT) From: "Pedro Falcato" To: devel@edk2.groups.io Cc: Pedro Falcato , Leif Lindholm , Michael D Kinney , Bret Barkelew Subject: [edk2-devel] [Patch 3/3] Ext4Pkg: Add .DSC file. Date: Fri, 30 Jul 2021 17:16:42 +0100 Message-Id: <20210730161641.7245-4-pedro.falcato@gmail.com> In-Reply-To: <20210730161641.7245-1-pedro.falcato@gmail.com> References: <20210730161641.7245-1-pedro.falcato@gmail.com> MIME-Version: 1.0 Precedence: Bulk List-Unsubscribe: List-Subscribe: List-Help: Sender: devel@edk2.groups.io List-Id: Mailing-List: list devel@edk2.groups.io; contact devel+owner@edk2.groups.io Reply-To: devel@edk2.groups.io,pedro.falcato@gmail.com Content-Transfer-Encoding: quoted-printable DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=groups.io; q=dns/txt; s=20140610; t=1627662670; bh=QJrW9oNyi8jl5ciUa+ed8tfqb6nI6Kk6t8RAlseBzz4=; h=Cc:Date:From:Reply-To:Subject:To; b=UPsgZWnLrJCbdykPGsdVc+MjvTJCdaE2Cp7pUEQKq3+K8TSpXw+vDzxBNpyQdAcia0b r2cnPKOo7vVCVUdZmtAU4oMkb1ywueH9mn8zcxZWCCr5YefutT/3UYAF/mmmf4N/CQx1J JCQF+iFJfnJQVQjDXa7FlSaNWj0mNLljXjI= X-ZohoMail-DKIM: pass (identity @groups.io) X-ZM-MESSAGEID: 1627662672428100006 Content-Type: text/plain; charset="utf-8" This file is required to build Ext4Pkg. Cc: Leif Lindholm Cc: Michael D Kinney Cc: Bret Barkelew Signed-off-by: Pedro Falcato --- Features/Ext4Pkg/Ext4Pkg.dsc | 68 ++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 Features/Ext4Pkg/Ext4Pkg.dsc diff --git a/Features/Ext4Pkg/Ext4Pkg.dsc b/Features/Ext4Pkg/Ext4Pkg.dsc new file mode 100644 index 0000000000..62cb4e69cf --- /dev/null +++ b/Features/Ext4Pkg/Ext4Pkg.dsc @@ -0,0 +1,68 @@ +## @file +# Ext4 Package +# +# This package provides libraries and drivers related to the ext4 filesys= tem implementation. +# More details are available at: https://www.kernel.org/doc/html/v5.4/fil= esystems/ext4/index.html +# +# Copyright (c) 2021 Pedro Falcato +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + + +[Defines] + PLATFORM_NAME =3D Ext4 + PLATFORM_GUID =3D 6B4BF998-668B-46D3-BCFA-971F99F8708C + PLATFORM_VERSION =3D 0.1 + DSC_SPECIFICATION =3D 0x00010005 + SUPPORTED_ARCHITECTURES =3D IA32|X64|EBC|ARM|AARCH64|RISCV64 + OUTPUT_DIRECTORY =3D Build/Ext4Pkg + BUILD_TARGETS =3D DEBUG|RELEASE|NOOPT + SKUID_IDENTIFIER =3D DEFAULT + +[BuildOptions] + *_*_*_CC_FLAGS =3D -D DISABLE_NEW_DEPRECATED_INTER= FACES + +[LibraryClasses] + # + # Entry Point Libraries + # + UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntry= Point.inf + # + # Common Libraries + # + BaseLib|MdePkg/Library/BaseLib/BaseLib.inf + BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf + UefiLib|MdePkg/Library/UefiLib/UefiLib.inf + PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf + PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf + MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAll= ocationLib.inf + UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBoo= tServicesTableLib.inf + UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/U= efiRuntimeServicesTableLib.inf + DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf + DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseD= ebugPrintErrorLevelLib.inf + DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf + OrderedCollectionLib|MdePkg/Library/BaseOrderedCollectionRedBlackTreeLib= /BaseOrderedCollectionRedBlackTreeLib.inf + BaseUcs2Utf8Lib|RedfishPkg/Library/BaseUcs2Utf8Lib/BaseUcs2Utf8Lib.inf + +##########################################################################= ######################### +# +# Components Section - list of the modules and components that will be pro= cessed by compilation +# tools and the EDK II tools to generate PE32/PE32+/C= off image files. +# +# Note: The EDK II DSC file is not used to specify how compiled binary ima= ges get placed +# into firmware volume images. This section is just a list of module= s to compile from +# source into UEFI-compliant binaries. +# It is the FDF file that contains information on combining binary f= iles into firmware +# volume images, whose concept is beyond UEFI and is described in PI= specification. +# Binary modules do not need to be listed in this section, as they s= hould be +# specified in the FDF file. For example: Shell binary (Shell_Full.e= fi), FAT binary (Fat.efi), +# Logo (Logo.bmp), and etc. +# There may also be modules listed in this section that are not requ= ired in the FDF file, +# When a module listed here is excluded from FDF file, then UEFI-com= pliant binary will be +# generated for it, but the binary will not be put into any firmware= volume. +# +##########################################################################= ######################### + +[Components] + Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.inf --=20 2.32.0 -=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#78446): https://edk2.groups.io/g/devel/message/78446 Mute This Topic: https://groups.io/mt/84553677/1787277 Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org] -=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-