From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-lj1-f170.google.com (mail-lj1-f170.google.com [209.85.208.170]) by mx.groups.io with SMTP id smtpd.web10.8016.1662559355071112403 for ; Wed, 07 Sep 2022 07:02:35 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20210112 header.b=gRo3pWet; spf=pass (domain: gmail.com, ip: 209.85.208.170, mailfrom: savvamtr@gmail.com) Received: by mail-lj1-f170.google.com with SMTP id y18so1342213ljh.12 for ; Wed, 07 Sep 2022 07:02:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date; bh=/DrVkwhOrFsJnuTST3VY9fb4M46HvY/Iq0XfspZnIu0=; b=gRo3pWetmP6yf9UfOx7hJbv63kru6sAxVNKT10GRd0CHFRmXD1tbsF+BTJQwhoP/FY iczSL2XnOSf0t7r4Wa38jaouRfTs3akPK15S8E7a0OxJvOt+2BtB862Vkm176hHHHviI 69SR8rq8XxCCaVSbSKPZ9aXELid5l+ZLFf3ltA2pSEUAsKDZXztpkcXfIj1AGpOO5DRd CxpHmFFz8rhTAl81i4C4QIQQnaPnjyoBdb58Lu1Jv6G+rLwlmaYVawheQuRs/Ct7U9bx RPI4/y+R7fg0FYCuIR6YfCUyOUi6j2Tx56h6JaeOIOM6eF/TjnwdGZ8w2FL9uQjG/vyo 6n8Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date; bh=/DrVkwhOrFsJnuTST3VY9fb4M46HvY/Iq0XfspZnIu0=; b=YA75097vXROcXCeCWC2Q/v6pk3/sdG6etbJ2V0HoFiWiDYkoKJnkHZ8K/TyluXdMix dGJJ7Mujbx0VBbp7VGtW2SETOMtXHAby9TiCPNFwpvLGmW3B1wbC8zBezx+m6oN3zDRF ux9lgFNb9R6MBwGHYOv+gMmpUz9427qPEnHvbBa/riL4W15mJZ4EJewKWIdiXlKzQYW4 3XUqsvIHRereA6YUc4btmZCtobUtVzkznhOR+sA+C8SlSyPlO1PqQIpewbUn2DRhXbg/ /bOYizuxA5zE/Y376b8g3x27FXrTwq5PR1DEPSGghMU8vihIfxEf34Mn4OntChdFXjKW VswQ== X-Gm-Message-State: ACgBeo2pN2Fi2ogJpoOl5DhVy8IQW0QzEFXQSGFh1jhnqcAlNgHsy1/r 1/brELSZEfit7e/l7Pqdv7qiSTuC0lo= X-Google-Smtp-Source: AA6agR73Nicd3YoQR0No4gnmzOa3knGdQKMlPPzKctsvgwbel0xQYeLAJExFaIXFXMWnW79F2k28Zw== X-Received: by 2002:a2e:3003:0:b0:268:7c2d:765c with SMTP id w3-20020a2e3003000000b002687c2d765cmr1030912ljw.120.1662559350246; Wed, 07 Sep 2022 07:02:30 -0700 (PDT) Return-Path: Received: from localhost.localdomain ([176.59.149.223]) by smtp.gmail.com with ESMTPSA id o12-20020ac24e8c000000b0049482979fe0sm2506399lfr.179.2022.09.07.07.02.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 07 Sep 2022 07:02:29 -0700 (PDT) From: "Savva Mitrofanov" To: devel@edk2.groups.io Cc: =?UTF-8?q?Marvin=20H=C3=A4user?= , Pedro Falcato , Vitaly Cheptsov Subject: [edk2-platforms][PATCH v5 1/2] Ext4Pkg: Add symbolic links support Date: Wed, 7 Sep 2022 20:02:12 +0600 Message-Id: <20220907140213.13286-2-savvamtr@gmail.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20220907140213.13286-1-savvamtr@gmail.com> References: <20220907140213.13286-1-savvamtr@gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3D3677 Provided support for symlink file type. Added routine which allows reading and following them through recursive open() call. As a security meausure implemented simple symlink loop check with nest level limit equal 8. Also this patch moves Ext4Open functionality to internal routine. Cc: Marvin H=C3=A4user Cc: Pedro Falcato Cc: Vitaly Cheptsov Signed-off-by: Savva Mitrofanov --- Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.inf | 1 + Features/Ext4Pkg/Ext4Dxe/Ext4Disk.h | 13 +- Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.h | 72 +++++- Features/Ext4Pkg/Ext4Dxe/File.c | 169 +++++++++++-- Features/Ext4Pkg/Ext4Dxe/Inode.c | 15 ++ Features/Ext4Pkg/Ext4Dxe/Symlink.c | 261 ++++++++++++++++++++ 6 files changed, 492 insertions(+), 39 deletions(-) diff --git a/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.inf b/Features/Ext4Pkg/Ext4Dx= e/Ext4Dxe.inf index deaf89fb3743..a153fc41ccd6 100644 --- a/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.inf +++ b/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.inf @@ -108,6 +108,7 @@ Directory.c=0D Extents.c=0D File.c=0D + Symlink.c=0D Collation.c=0D Ext4Disk.h=0D Ext4Dxe.h=0D diff --git a/Features/Ext4Pkg/Ext4Dxe/Ext4Disk.h b/Features/Ext4Pkg/Ext4Dxe= /Ext4Disk.h index 7a19d2f79d53..4fd91a423324 100644 --- a/Features/Ext4Pkg/Ext4Dxe/Ext4Disk.h +++ b/Features/Ext4Pkg/Ext4Dxe/Ext4Disk.h @@ -171,7 +171,7 @@ #define EXT4_DIRTY_FL 0x00000100=0D #define EXT4_COMPRBLK_FL 0x00000200=0D #define EXT4_NOCOMPR_FL 0x00000400=0D -#define EXT4_ECOMPR_FL 0x00000800=0D +#define EXT4_ENCRYPT_FL 0x00000800=0D #define EXT4_BTREE_FL 0x00001000=0D #define EXT4_INDEX_FL 0x00002000=0D #define EXT4_JOURNAL_DATA_FL 0x00004000=0D @@ -332,11 +332,12 @@ STATIC_ASSERT ( "ext4 block group descriptor struct has incorrect size"=0D );=0D =0D -#define EXT4_DBLOCKS 12=0D -#define EXT4_IND_BLOCK 12=0D -#define EXT4_DIND_BLOCK 13=0D -#define EXT4_TIND_BLOCK 14=0D -#define EXT4_NR_BLOCKS 15=0D +#define EXT4_DBLOCKS 12=0D +#define EXT4_IND_BLOCK 12=0D +#define EXT4_DIND_BLOCK 13=0D +#define EXT4_TIND_BLOCK 14=0D +#define EXT4_NR_BLOCKS 15=0D +#define EXT4_FAST_SYMLINK_MAX_SIZE EXT4_NR_BLOCKS * sizeof(UINT32)=0D =0D #define EXT4_GOOD_OLD_INODE_SIZE 128U=0D =0D diff --git a/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.h b/Features/Ext4Pkg/Ext4Dxe/= Ext4Dxe.h index 81e59a4babc9..6d352d3995f1 100644 --- a/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.h +++ b/Features/Ext4Pkg/Ext4Dxe/Ext4Dxe.h @@ -31,8 +31,14 @@ =0D #include "Ext4Disk.h"=0D =0D +#define SYMLOOP_MAX 8=0D #define EXT4_NAME_MAX 255=0D -=0D +//=0D +// We need to specify path length limit for security purposes, to prevent = possible=0D +// overflows and dead-loop conditions. Originally this limit is absent in = FS design,=0D +// but present in UNIX distros and shell environments, which may varies fr= om 1024 to 4096.=0D +//=0D +#define EXT4_EFI_PATH_MAX 4096=0D #define EXT4_DRIVER_VERSION 0x0000=0D =0D /**=0D @@ -324,11 +330,11 @@ number of read bytes. **/=0D EFI_STATUS=0D Ext4Read (=0D - IN EXT4_PARTITION *Partition,=0D - IN EXT4_FILE *File,=0D - OUT VOID *Buffer,=0D - IN UINT64 Offset,=0D - IN OUT UINTN *Length=0D + IN EXT4_PARTITION *Partition,=0D + IN EXT4_FILE *File,=0D + OUT VOID *Buffer,=0D + IN UINT64 Offset,=0D + IN OUT UINTN *Length=0D );=0D =0D /**=0D @@ -368,6 +374,7 @@ struct _Ext4File { =0D UINT64 OpenMode;=0D UINT64 Position;=0D + UINT32 SymLoops;=0D =0D EXT4_PARTITION *Partition;=0D =0D @@ -497,6 +504,45 @@ Ext4SetupFile ( IN EXT4_PARTITION *Partition=0D );=0D =0D +/**=0D + Opens a new file relative to the source file's location.=0D +=0D + @param[out] FoundFile A pointer to the location to return the opened ha= ndle for the new=0D + file.=0D + @param[in] Source A pointer to the EXT4_FILE instance that is the f= ile=0D + handle to the source location. This would typical= ly be an open=0D + handle to a directory.=0D + @param[in] FileName The Null-terminated string of the name of the fil= e to be opened.=0D + The file name may contain the following path modi= fiers: "\", ".",=0D + and "..".=0D + @param[in] OpenMode The mode to open the file. The only valid combina= tions that the=0D + file may be opened with are: Read, Read/Write, or= Create/Read/Write.=0D + @param[in] Attributes Only valid for EFI_FILE_MODE_CREATE, in which cas= e these are the=0D + attribute bits for the newly created file.=0D +=0D + @retval EFI_SUCCESS The file was opened.=0D + @retval EFI_NOT_FOUND The specified file could not be found on th= e device.=0D + @retval EFI_NO_MEDIA The device has no medium.=0D + @retval EFI_MEDIA_CHANGED The device has a different medium in it or = the medium is no=0D + longer supported.=0D + @retval EFI_DEVICE_ERROR The device reported an error.=0D + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.=0D + @retval EFI_WRITE_PROTECTED An attempt was made to create a file, or op= en a file for write=0D + when the media is write-protected.=0D + @retval EFI_ACCESS_DENIED The service denied access to the file.=0D + @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open= the file.=0D + @retval EFI_VOLUME_FULL The volume is full.=0D +=0D +**/=0D +EFI_STATUS=0D +Ext4OpenInternal (=0D + OUT EXT4_FILE **FoundFile,=0D + IN EXT4_FILE *Source,=0D + IN CHAR16 *FileName,=0D + IN UINT64 OpenMode,=0D + IN UINT64 Attributes=0D + );=0D +=0D /**=0D Closes a file.=0D =0D @@ -774,6 +820,18 @@ Ext4FileIsDir ( IN CONST EXT4_FILE *File=0D );=0D =0D +/**=0D + Checks if a file is a symlink.=0D +=0D + @param[in] File Pointer to the opened file.=0D +=0D + @return BOOLEAN Whether file is a symlink=0D +**/=0D +BOOLEAN=0D +Ext4FileIsSymlink (=0D + IN CONST EXT4_FILE *File=0D + );=0D +=0D /**=0D Checks if a file is a regular file.=0D @param[in] File Pointer to the opened file.=0D @@ -797,7 +855,7 @@ Ext4FileIsReg ( it's a regular file or a directory, since most other file types= =0D don't make sense under UEFI.=0D **/=0D -#define Ext4FileIsOpenable(File) (Ext4FileIsReg(File) || Ext4FileIsDir(Fi= le))=0D +#define Ext4FileIsOpenable(File) (Ext4FileIsReg (File) || Ext4FileIsDir (= File) || Ext4FileIsSymlink (File))=0D =0D #define EXT4_INODE_HAS_FIELD(Inode, Field) = \=0D (Inode->i_extra_isize + EXT4_GOOD_OLD_INODE_SIZE >=3D = \=0D diff --git a/Features/Ext4Pkg/Ext4Dxe/File.c b/Features/Ext4Pkg/Ext4Dxe/Fil= e.c index ff1746d5640a..86ccfff8603a 100644 --- a/Features/Ext4Pkg/Ext4Dxe/File.c +++ b/Features/Ext4Pkg/Ext4Dxe/File.c @@ -9,6 +9,26 @@ =0D #include =0D =0D +/**=0D + Reads a symlink file.=0D +=0D + @param[in] Partition Pointer to the ext4 partition.=0D + @param[in] File Pointer to the open symlink file.=0D + @param[out] Symlink Pointer to the output unicode symlink string= .=0D +=0D + @retval EFI_SUCCESS Symlink was read.=0D + @retval EFI_ACCESS_DENIED Symlink is encrypted.=0D + @retval EFI_OUT_OF_RESOURCES Memory allocation error.=0D + @retval EFI_INVALID_PARAMETER Symlink path has incorrect length=0D + @retval EFI_VOLUME_CORRUPTED Symlink read block size differ from inode = value=0D +**/=0D +EFI_STATUS=0D +Ext4ReadSymlink (=0D + IN EXT4_PARTITION *Partition,=0D + IN EXT4_FILE *File,=0D + OUT CHAR16 **Symlink=0D + );=0D +=0D /**=0D Duplicates a file structure.=0D =0D @@ -137,11 +157,11 @@ Ext4DirCanLookup ( /**=0D Opens a new file relative to the source file's location.=0D =0D - @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that = is the file=0D + @param[out] FoundFile A pointer to the location to return the opened ha= ndle for the new=0D + file.=0D + @param[in] Source A pointer to the EXT4_FILE instance that is the f= ile=0D handle to the source location. This would typical= ly be an open=0D handle to a directory.=0D - @param[out] NewHandle A pointer to the location to return the opened ha= ndle for the new=0D - file.=0D @param[in] FileName The Null-terminated string of the name of the fil= e to be opened.=0D The file name may contain the following path modi= fiers: "\", ".",=0D and "..".=0D @@ -165,13 +185,12 @@ Ext4DirCanLookup ( =0D **/=0D EFI_STATUS=0D -EFIAPI=0D -Ext4Open (=0D - IN EFI_FILE_PROTOCOL *This,=0D - OUT EFI_FILE_PROTOCOL **NewHandle,=0D - IN CHAR16 *FileName,=0D - IN UINT64 OpenMode,=0D - IN UINT64 Attributes=0D +Ext4OpenInternal (=0D + OUT EXT4_FILE **FoundFile,=0D + IN EXT4_FILE *Source,=0D + IN CHAR16 *FileName,=0D + IN UINT64 OpenMode,=0D + IN UINT64 Attributes=0D )=0D {=0D EXT4_FILE *Current;=0D @@ -180,13 +199,14 @@ Ext4Open ( CHAR16 PathSegment[EXT4_NAME_MAX + 1];=0D UINTN Length;=0D EXT4_FILE *File;=0D + CHAR16 *Symlink;=0D EFI_STATUS Status;=0D =0D - Current =3D (EXT4_FILE *)This;=0D + Current =3D Source;=0D Partition =3D Current->Partition;=0D Level =3D 0;=0D =0D - DEBUG ((DEBUG_FS, "[ext4] Ext4Open %s\n", FileName));=0D + DEBUG ((DEBUG_FS, "[ext4] Ext4OpenInternal %s\n", FileName));=0D // If the path starts with a backslash, we treat the root directory as t= he base directory=0D if (FileName[0] =3D=3D L'\\') {=0D FileName++;=0D @@ -194,6 +214,11 @@ Ext4Open ( }=0D =0D while (FileName[0] !=3D L'\0') {=0D + if (Partition->Root->SymLoops > SYMLOOP_MAX) {=0D + DEBUG ((DEBUG_FS, "[ext4] Symloop limit is hit !\n"));=0D + return EFI_ACCESS_DENIED;=0D + }=0D +=0D // Discard leading path separators=0D while (FileName[0] =3D=3D L'\\') {=0D FileName++;=0D @@ -238,18 +263,45 @@ Ext4Open ( }=0D =0D // Check if this is a valid file to open in EFI=0D -=0D - // What to do with symlinks? They're nonsense when absolute but may=0D - // be useful when they're relative. Right now, they're ignored, since = they=0D - // bring a lot of trouble for something that's not as useful in our ca= se.=0D - // If you want to link, use hard links.=0D -=0D if (!Ext4FileIsOpenable (File)) {=0D Ext4CloseInternal (File);=0D // This looks like an /okay/ status to return.=0D return EFI_ACCESS_DENIED;=0D }=0D =0D + //=0D + // Reading symlink and then trying to follow it=0D + //=0D + if (Ext4FileIsSymlink (File)) {=0D + Partition->Root->SymLoops++;=0D + DEBUG ((DEBUG_FS, "[ext4] File %s is symlink, trying to read it\n", = PathSegment));=0D + Status =3D Ext4ReadSymlink (Partition, File, &Symlink);=0D + if (EFI_ERROR (Status)) {=0D + DEBUG ((DEBUG_FS, "[ext4] Error reading %s symlink!\n", PathSegmen= t));=0D + return Status;=0D + }=0D +=0D + DEBUG ((DEBUG_FS, "[ext4] File %s is linked to %s\n", PathSegment, S= ymlink));=0D + //=0D + // Close symlink file=0D + //=0D + Ext4CloseInternal (File);=0D + //=0D + // Open linked file by recursive call of Ext4OpenFile=0D + //=0D + Status =3D Ext4OpenInternal (FoundFile, Current, Symlink, OpenMode, = Attributes);=0D + FreePool (Symlink);=0D + if (EFI_ERROR (Status)) {=0D + DEBUG ((DEBUG_FS, "[ext4] Error opening linked file %s\n", Symlink= ));=0D + return Status;=0D + }=0D +=0D + //=0D + // Set File to newly opened=0D + //=0D + File =3D *FoundFile;=0D + }=0D +=0D if (Level !=3D 0) {=0D // Careful not to close the base directory=0D Ext4CloseInternal (Current);=0D @@ -273,12 +325,75 @@ Ext4Open ( return EFI_ACCESS_DENIED;=0D }=0D =0D - *NewHandle =3D &Current->Protocol;=0D + *FoundFile =3D Current;=0D =0D DEBUG ((DEBUG_FS, "[ext4] Opened filename %s\n", Current->Dentry->Name))= ;=0D return EFI_SUCCESS;=0D }=0D =0D +/**=0D + Opens a new file relative to the source file's location.=0D + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that = is the file=0D + handle to the source location. This would typical= ly be an open=0D + handle to a directory.=0D + @param[out] NewHandle A pointer to the location to return the opened ha= ndle for the new=0D + file.=0D + @param[in] FileName The Null-terminated string of the name of the fil= e to be opened.=0D + The file name may contain the following path modi= fiers: "\", ".",=0D + and "..".=0D + @param[in] OpenMode The mode to open the file. The only valid combina= tions that the=0D + file may be opened with are: Read, Read/Write, or= Create/Read/Write.=0D + @param[in] Attributes Only valid for EFI_FILE_MODE_CREATE, in which cas= e these are the=0D + attribute bits for the newly created file.=0D + @retval EFI_SUCCESS The file was opened.=0D + @retval EFI_NOT_FOUND The specified file could not be found on th= e device.=0D + @retval EFI_NO_MEDIA The device has no medium.=0D + @retval EFI_MEDIA_CHANGED The device has a different medium in it or = the medium is no=0D + longer supported.=0D + @retval EFI_DEVICE_ERROR The device reported an error.=0D + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.=0D + @retval EFI_WRITE_PROTECTED An attempt was made to create a file, or op= en a file for write=0D + when the media is write-protected.=0D + @retval EFI_ACCESS_DENIED The service denied access to the file.=0D + @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open= the file.=0D + @retval EFI_VOLUME_FULL The volume is full.=0D +**/=0D +EFI_STATUS=0D +EFIAPI=0D +Ext4Open (=0D + IN EFI_FILE_PROTOCOL *This,=0D + OUT EFI_FILE_PROTOCOL **NewHandle,=0D + IN CHAR16 *FileName,=0D + IN UINT64 OpenMode,=0D + IN UINT64 Attributes=0D + )=0D +{=0D + EFI_STATUS Status;=0D + EXT4_FILE *FoundFile;=0D + EXT4_FILE *Source;=0D +=0D + Source =3D (EXT4_FILE *)This;=0D +=0D + //=0D + // Reset SymLoops counter=0D + //=0D + Source->Partition->Root->SymLoops =3D 0;=0D +=0D + Status =3D Ext4OpenInternal (=0D + &FoundFile,=0D + Source,=0D + FileName,=0D + OpenMode,=0D + Attributes=0D + );=0D +=0D + if (!EFI_ERROR (Status)) {=0D + *NewHandle =3D &FoundFile->Protocol;=0D + }=0D +=0D + return Status;=0D +}=0D +=0D /**=0D Closes a specified file handle.=0D =0D @@ -588,7 +703,7 @@ Ext4GetVolumeName ( =0D // s_volume_name is only valid on dynamic revision; old filesystems don'= t support this=0D if (Partition->SuperBlock.s_rev_level =3D=3D EXT4_DYNAMIC_REV) {=0D - CopyMem (TempVolName, (CONST CHAR8 *)Partition->SuperBlock.s_volume_na= me, 16);=0D + CopyMem (TempVolName, Partition->SuperBlock.s_volume_name, 16);=0D TempVolName[16] =3D '\0';=0D =0D Status =3D UTF8StrToUCS2 (TempVolName, &VolumeName);=0D @@ -754,12 +869,14 @@ Ext4GetInfo ( OUT VOID *Buffer=0D )=0D {=0D + EXT4_FILE *File;=0D EXT4_PARTITION *Partition;=0D =0D - Partition =3D ((EXT4_FILE *)This)->Partition;=0D + File =3D (EXT4_FILE *)This;=0D + Partition =3D File->Partition;=0D =0D if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {=0D - return Ext4GetFileInfo ((EXT4_FILE *)This, Buffer, BufferSize);=0D + return Ext4GetFileInfo (File, Buffer, BufferSize);=0D }=0D =0D if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) {=0D @@ -870,12 +987,12 @@ Ext4SetInfo ( )=0D {=0D EXT4_FILE *File;=0D - EXT4_PARTITION *Part;=0D + EXT4_PARTITION *Partition;=0D =0D - File =3D (EXT4_FILE *)This;=0D - Part =3D File->Partition;=0D + File =3D (EXT4_FILE *)This;=0D + Partition =3D File->Partition;=0D =0D - if (Part->ReadOnly) {=0D + if (Partition->ReadOnly) {=0D return EFI_WRITE_PROTECTED;=0D }=0D =0D diff --git a/Features/Ext4Pkg/Ext4Dxe/Inode.c b/Features/Ext4Pkg/Ext4Dxe/In= ode.c index 7f8be2f02643..5ccb4d2bfc42 100644 --- a/Features/Ext4Pkg/Ext4Dxe/Inode.c +++ b/Features/Ext4Pkg/Ext4Dxe/Inode.c @@ -255,6 +255,21 @@ Ext4FileIsDir ( return (File->Inode->i_mode & EXT4_INO_TYPE_DIR) =3D=3D EXT4_INO_TYPE_DI= R;=0D }=0D =0D +/**=0D + Checks if a file is a symlink.=0D +=0D + @param[in] File Pointer to the opened file.=0D +=0D + @return BOOLEAN Whether file is a symlink=0D +**/=0D +BOOLEAN=0D +Ext4FileIsSymlink (=0D + IN CONST EXT4_FILE *File=0D + )=0D +{=0D + return (File->Inode->i_mode & EXT4_INO_TYPE_SYMLINK) =3D=3D EXT4_INO_TYP= E_SYMLINK;=0D +}=0D +=0D /**=0D Checks if a file is a regular file.=0D @param[in] File Pointer to the opened file.=0D diff --git a/Features/Ext4Pkg/Ext4Dxe/Symlink.c b/Features/Ext4Pkg/Ext4Dxe/= Symlink.c new file mode 100644 index 000000000000..0905417ffb88 --- /dev/null +++ b/Features/Ext4Pkg/Ext4Dxe/Symlink.c @@ -0,0 +1,261 @@ +/** @file=0D + Symbolic links routines=0D +=0D + Copyright (c) 2022 Savva Mitrofanov All rights reserved.=0D + SPDX-License-Identifier: BSD-2-Clause-Patent=0D +**/=0D +=0D +#include "Ext4Dxe.h"=0D +=0D +/**=0D + Detects if a symlink is a fast symlink.=0D +=0D + @param[in] File Pointer to the opened file.=0D +=0D + @return BOOLEAN Whether symlink is a fast symlink=0D +**/=0D +STATIC=0D +BOOLEAN=0D +Ext4SymlinkIsFastSymlink (=0D + IN CONST EXT4_FILE *File=0D + )=0D +{=0D + //=0D + // Detection logic of the fast-symlink splits into two behaviors - old a= nd new.=0D + // The old behavior is based on comparing the extended attribute blocks= =0D + // with the inode's i_blocks, and if it's zero we know the inode isn't s= toring=0D + // the link in filesystem blocks, so we look to the inode->i_data.=0D + // The new behavior is apparently needed only with the large EA inode fe= ature.=0D + // In this case we check that inode size less than maximum fast symlink = size.=0D + // So, we revert to the old behavior if the large EA inode feature is no= t set.=0D + //=0D + UINT32 FileAcl;=0D + UINT32 ExtAttrBlocks;=0D +=0D + if ((File->Inode->i_flags & EXT4_EA_INODE_FL) =3D=3D 0) {=0D + FileAcl =3D File->Inode->i_file_acl;=0D + if (EXT4_IS_64_BIT (File->Partition)) {=0D + //=0D + // We don't care about final value, we are just checking for any bit= is set=0D + // so, thats why we neglect LShiftU64(.., 32)=0D + //=0D + FileAcl |=3D File->Inode->i_osd2.data_linux.l_i_file_acl_high;=0D + }=0D +=0D + ExtAttrBlocks =3D FileAcl !=3D 0 ? (File->Partition->BlockSize >> 9) := 0;=0D +=0D + return File->Inode->i_blocks =3D=3D ExtAttrBlocks;=0D + }=0D +=0D + return EXT4_INODE_SIZE (File->Inode) <=3D EXT4_FAST_SYMLINK_MAX_SIZE;=0D +}=0D +=0D +/**=0D + Reads a fast symlink file.=0D +=0D + @param[in] Partition Pointer to the ext4 partition.=0D + @param[in] File Pointer to the open symlink file.=0D + @param[out] AsciiSymlink Pointer to the output ascii symlink str= ing.=0D + @param[out] AsciiSymlinkSize Pointer to the output ascii symlink str= ing length.=0D +=0D + @retval EFI_SUCCESS Fast symlink was read.=0D + @retval EFI_OUT_OF_RESOURCES Memory allocation error.=0D +**/=0D +STATIC=0D +EFI_STATUS=0D +Ext4ReadFastSymlink (=0D + IN EXT4_PARTITION *Partition,=0D + IN EXT4_FILE *File,=0D + OUT CHAR8 **AsciiSymlink,=0D + OUT UINT32 *AsciiSymlinkSize=0D + )=0D +{=0D + UINT32 SymlinkSize;=0D + CHAR8 *AsciiSymlinkTmp;=0D +=0D + //=0D + // Fast-symlink's EXT4_INODE_SIZE is not necessarily validated when we c= hecked it in=0D + // Ext4SymlinkIsFastSymlink(), so truncate if necessary.=0D + //=0D + SymlinkSize =3D (UINT32)MIN (EXT4_INODE_SIZE (File->Inode), EXT4_FAST_SY= MLINK_MAX_SIZE);=0D +=0D + AsciiSymlinkTmp =3D AllocatePool (SymlinkSize + 1);=0D + if (AsciiSymlinkTmp =3D=3D NULL) {=0D + DEBUG ((DEBUG_ERROR, "[ext4] Failed to allocate symlink ascii string b= uffer\n"));=0D + return EFI_OUT_OF_RESOURCES;=0D + }=0D +=0D + CopyMem (AsciiSymlinkTmp, File->Inode->i_data, SymlinkSize);=0D +=0D + //=0D + // Add null-terminator=0D + //=0D + AsciiSymlinkTmp[SymlinkSize] =3D '\0';=0D +=0D + *AsciiSymlink =3D AsciiSymlinkTmp;=0D + *AsciiSymlinkSize =3D SymlinkSize + 1;=0D +=0D + return EFI_SUCCESS;=0D +}=0D +=0D +/**=0D + Reads a slow symlink file.=0D +=0D + @param[in] Partition Pointer to the ext4 partition.=0D + @param[in] File Pointer to the open symlink file.=0D + @param[out] AsciiSymlink Pointer to the output ascii symlink str= ing.=0D + @param[out] AsciiSymlinkSize Pointer to the output ascii symlink str= ing length.=0D +=0D + @retval EFI_SUCCESS Slow symlink was read.=0D + @retval EFI_OUT_OF_RESOURCES Memory allocation error.=0D + @retval EFI_INVALID_PARAMETER Slow symlink path has incorrect length=0D + @retval EFI_VOLUME_CORRUPTED Symlink read block size differ from inode = value=0D +**/=0D +STATIC=0D +EFI_STATUS=0D +Ext4ReadSlowSymlink (=0D + IN EXT4_PARTITION *Partition,=0D + IN EXT4_FILE *File,=0D + OUT CHAR8 **AsciiSymlink,=0D + OUT UINT32 *AsciiSymlinkSize=0D + )=0D +{=0D + EFI_STATUS Status;=0D + CHAR8 *SymlinkTmp;=0D + UINT64 SymlinkSizeTmp;=0D + UINT32 SymlinkAllocateSize;=0D + UINTN ReadSize;=0D +=0D + SymlinkSizeTmp =3D EXT4_INODE_SIZE (File->Inode);=0D +=0D + //=0D + // Allocate EXT4_INODE_SIZE + 1=0D + //=0D + if (SymlinkSizeTmp >=3D EXT4_EFI_PATH_MAX) {=0D + DEBUG ((=0D + DEBUG_WARN,=0D + "[ext4] Warn: symlink path maximum length was hit!\n"=0D + ));=0D + return EFI_INVALID_PARAMETER;=0D + }=0D +=0D + SymlinkAllocateSize =3D (UINT32)SymlinkSizeTmp + 1;=0D +=0D + SymlinkTmp =3D AllocatePool (SymlinkAllocateSize);=0D + if (SymlinkTmp =3D=3D NULL) {=0D + DEBUG ((DEBUG_FS, "[ext4] Failed to allocate symlink ascii string buff= er\n"));=0D + return EFI_OUT_OF_RESOURCES;=0D + }=0D +=0D + ReadSize =3D (UINTN)SymlinkSizeTmp;=0D + Status =3D Ext4Read (Partition, File, SymlinkTmp, File->Position, &Rea= dSize);=0D + if (EFI_ERROR (Status)) {=0D + DEBUG ((DEBUG_FS, "[ext4] Failed to read symlink from blocks with stat= us %r\n", Status));=0D + FreePool (SymlinkTmp);=0D + return Status;=0D + }=0D +=0D + //=0D + // Add null-terminator=0D + //=0D + SymlinkTmp[SymlinkSizeTmp] =3D '\0';=0D +=0D + if (SymlinkSizeTmp !=3D ReadSize) {=0D + DEBUG ((=0D + DEBUG_FS,=0D + "[ext4] Error! The size of the read block doesn't match the value fr= om the inode!\n"=0D + ));=0D + return EFI_VOLUME_CORRUPTED;=0D + }=0D +=0D + *AsciiSymlinkSize =3D SymlinkAllocateSize;=0D + *AsciiSymlink =3D SymlinkTmp;=0D +=0D + return EFI_SUCCESS;=0D +}=0D +=0D +/**=0D + Reads a symlink file.=0D +=0D + @param[in] Partition Pointer to the ext4 partition.=0D + @param[in] File Pointer to the open symlink file.=0D + @param[out] Symlink Pointer to the output unicode symlink string= .=0D +=0D + @retval EFI_SUCCESS Symlink was read.=0D + @retval EFI_ACCESS_DENIED Symlink is encrypted.=0D + @retval EFI_OUT_OF_RESOURCES Memory allocation error.=0D + @retval EFI_INVALID_PARAMETER Symlink path has incorrect length=0D + @retval EFI_VOLUME_CORRUPTED Symlink read block size differ from inode = value=0D +**/=0D +EFI_STATUS=0D +Ext4ReadSymlink (=0D + IN EXT4_PARTITION *Partition,=0D + IN EXT4_FILE *File,=0D + OUT CHAR16 **Symlink=0D + )=0D +{=0D + EFI_STATUS Status;=0D + CHAR8 *SymlinkTmp;=0D + UINT32 SymlinkSize;=0D + CHAR16 *Symlink16Tmp;=0D + CHAR16 *Needle;=0D +=0D + //=0D + // Assume that we alread read Inode via Ext4ReadInode=0D + // Skip reading, just check encryption flag=0D + //=0D + if ((File->Inode->i_flags & EXT4_ENCRYPT_FL) !=3D 0) {=0D + DEBUG ((DEBUG_WARN, "[ext4] Warn: symlink is encrypted\n"));=0D + return EFI_ACCESS_DENIED;=0D + }=0D +=0D + if (Ext4SymlinkIsFastSymlink (File)) {=0D + Status =3D Ext4ReadFastSymlink (Partition, File, &SymlinkTmp, &Symlink= Size);=0D + } else {=0D + Status =3D Ext4ReadSlowSymlink (Partition, File, &SymlinkTmp, &Symlink= Size);=0D + }=0D +=0D + if (EFI_ERROR (Status)) {=0D + DEBUG ((DEBUG_FS, "[ext4] Symlink read error with Status %r\n", Status= ));=0D + return Status;=0D + }=0D +=0D + Symlink16Tmp =3D AllocatePool (SymlinkSize * sizeof (CHAR16));=0D + if (Symlink16Tmp =3D=3D NULL) {=0D + DEBUG ((DEBUG_FS, "[ext4] Failed to allocate symlink unicode string bu= ffer\n"));=0D + FreePool (SymlinkTmp);=0D + return EFI_OUT_OF_RESOURCES;=0D + }=0D +=0D + Status =3D AsciiStrToUnicodeStrS (=0D + SymlinkTmp,=0D + Symlink16Tmp,=0D + SymlinkSize=0D + );=0D +=0D + FreePool (SymlinkTmp);=0D +=0D + if (EFI_ERROR (Status)) {=0D + DEBUG ((=0D + DEBUG_FS,=0D + "[ext4] Failed to convert ascii symlink to unicode with Status %r\n"= ,=0D + Status=0D + ));=0D + FreePool (Symlink16Tmp);=0D + FreePool (SymlinkTmp);=0D + return Status;=0D + }=0D +=0D + //=0D + // Convert to UEFI slashes=0D + //=0D + for (Needle =3D Symlink16Tmp; *Needle !=3D L'\0'; Needle++) {=0D + if (*Needle =3D=3D L'/') {=0D + *Needle =3D L'\\';=0D + }=0D + }=0D +=0D + *Symlink =3D Symlink16Tmp;=0D +=0D + return Status;=0D +}=0D --=20 2.37.3