Not a full review. Please don't put new functions like strnlen into kernel of they are useful only to one module. Kernel size is restricted to a different degree on some platforms
Le mer. 24 avr. 2024, 14:31, Yifan Zhao <zhaoyi...@sjtu.edu.cn> a écrit : > EROFS [1] is a lightweight read-only filesystem designed for performance > which has already been shipped in most Linux distributions as well as > widely > used in several scenarios, such as Android system partitions, container > images, and rootfs for embedded devices. > > This patch brings EROFS uncompressed support. Now, it's possible to boot > directly through GRUB with an EROFS rootfs. > > EROFS compressed files will be supported later since it has more work to > polish. > > [1] https://erofs.docs.kernel.org > > Signed-off-by: Yifan Zhao <zhaoyi...@sjtu.edu.cn> > --- > INSTALL | 8 +- > Makefile.util.def | 1 + > docs/grub.texi | 3 +- > grub-core/Makefile.core.def | 5 + > grub-core/fs/erofs.c | 984 ++++++++++++++++++++++++++++++++++++ > grub-core/kern/misc.c | 14 + > include/grub/misc.h | 1 + > 7 files changed, 1011 insertions(+), 5 deletions(-) > create mode 100644 grub-core/fs/erofs.c > > diff --git a/INSTALL b/INSTALL > index 8d9207c84..84030c9f4 100644 > --- a/INSTALL > +++ b/INSTALL > @@ -77,15 +77,15 @@ Prerequisites for make-check: > > * If running a Linux kernel the following modules must be loaded: > - fuse, loop > - - btrfs, ext4, f2fs, fat, hfs, hfsplus, jfs, mac-roman, minix, nilfs2, > + - btrfs, erofs, ext4, f2fs, fat, hfs, hfsplus, jfs, mac-roman, minix, > nilfs2, > reiserfs, udf, xfs > - On newer kernels, the exfat kernel modules may be used instead of the > exfat FUSE filesystem > * The following are Debian named packages required mostly for the full > suite of filesystem testing (but some are needed by other tests as > well): > - - btrfs-progs, dosfstools, e2fsprogs, exfat-utils, f2fs-tools, genromfs, > - hfsprogs, jfsutils, nilfs-tools, ntfs-3g, reiserfsprogs, > squashfs-tools, > - reiserfsprogs, udftools, xfsprogs, zfs-fuse > + - btrfs-progs, dosfstools, e2fsprogs, erofs-utils, exfat-utils, > f2fs-tools, > + genromfs, hfsprogs, jfsutils, nilfs-tools, ntfs-3g, reiserfsprogs, > + squashfs-tools, reiserfsprogs, udftools, xfsprogs, zfs-fuse > - exfat-fuse, if not using the exfat kernel module > - gzip, lzop, xz-utils > - attr, cpio, g++, gawk, parted, recode, tar, util-linux > diff --git a/Makefile.util.def b/Makefile.util.def > index 9432365a9..8d3bc107f 100644 > --- a/Makefile.util.def > +++ b/Makefile.util.def > @@ -98,6 +98,7 @@ library = { > common = grub-core/fs/cpio_be.c; > common = grub-core/fs/odc.c; > common = grub-core/fs/newc.c; > + common = grub-core/fs/erofs.c; > common = grub-core/fs/ext2.c; > common = grub-core/fs/fat.c; > common = grub-core/fs/exfat.c; > diff --git a/docs/grub.texi b/docs/grub.texi > index a225f9a88..396f711df 100644 > --- a/docs/grub.texi > +++ b/docs/grub.texi > @@ -353,6 +353,7 @@ blocklist notation. The currently supported filesystem > types are @dfn{Amiga > Fast FileSystem (AFFS)}, @dfn{AtheOS fs}, @dfn{BeFS}, > @dfn{BtrFS} (including raid0, raid1, raid10, gzip and lzo), > @dfn{cpio} (little- and big-endian bin, odc and newc variants), > +@dfn{EROFS} (only uncompressed support for now), > @dfn{Linux ext2/ext3/ext4}, @dfn{DOS FAT12/FAT16/FAT32}, > @dfn{exFAT}, @dfn{F2FS}, @dfn{HFS}, @dfn{HFS+}, > @dfn{ISO9660} (including Joliet, Rock-ridge and multi-chunk files), > @@ -6267,7 +6268,7 @@ assumed to be encoded in UTF-8. > NTFS, JFS, UDF, HFS+, exFAT, long filenames in FAT, Joliet part of > ISO9660 are treated as UTF-16 as per specification. AFS and BFS are read > as UTF-8, again according to specification. BtrFS, cpio, tar, squash4, > minix, > -minix2, minix3, ROMFS, ReiserFS, XFS, ext2, ext3, ext4, FAT (short names), > +minix2, minix3, ROMFS, ReiserFS, XFS, EROFS, ext2, ext3, ext4, FAT (short > names), > F2FS, RockRidge part of ISO9660, nilfs2, UFS1, UFS2 and ZFS are assumed > to be UTF-8. This might be false on systems configured with legacy charset > but as long as the charset used is superset of ASCII you should be able to > diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def > index 007ff628e..8b4dbcd66 100644 > --- a/grub-core/Makefile.core.def > +++ b/grub-core/Makefile.core.def > @@ -1442,6 +1442,11 @@ module = { > common = fs/odc.c; > }; > > +module = { > + name = erofs; > + common = fs/erofs.c; > +}; > + > module = { > name = ext2; > common = fs/ext2.c; > diff --git a/grub-core/fs/erofs.c b/grub-core/fs/erofs.c > new file mode 100644 > index 000000000..13f92e71a > --- /dev/null > +++ b/grub-core/fs/erofs.c > @@ -0,0 +1,984 @@ > +/* erofs.c - Enhanced Read-Only File System */ > +/* > + * GRUB -- GRand Unified Bootloader > + * Copyright (C) 2024 Free Software Foundation, Inc. > + * > + * GRUB is free software: you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation, either version 3 of the License, or > + * (at your option) any later version. > + * > + * GRUB is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. > + */ > + > +#include <grub/disk.h> > +#include <grub/dl.h> > +#include <grub/err.h> > +#include <grub/file.h> > +#include <grub/fs.h> > +#include <grub/fshelp.h> > +#include <grub/misc.h> > +#include <grub/mm.h> > +#include <grub/safemath.h> > +#include <grub/types.h> > + > +GRUB_MOD_LICENSE ("GPLv3+"); > + > +#define EROFS_SUPER_OFFSET 1024 > +#define EROFS_MAGIC 0xE0F5E1E2 > +#define EROFS_ISLOTBITS 5 > + > +#define EROFS_FEATURE_INCOMPAT_CHUNKED_FILE 0x00000004 > +#define EROFS_ALL_FEATURE_INCOMPAT > EROFS_FEATURE_INCOMPAT_CHUNKED_FILE > + > +struct grub_erofs_super > +{ > + grub_uint32_t magic; > + grub_uint32_t checksum; > + grub_uint32_t feature_compat; > + grub_uint8_t log2_blksz; > + grub_uint8_t sb_extslots; > + > + grub_uint16_t root_nid; > + grub_uint64_t inos; > + > + grub_uint64_t build_time; > + grub_uint32_t build_time_nsec; > + grub_uint32_t blocks; > + grub_uint32_t meta_blkaddr; > + grub_uint32_t xattr_blkaddr; > + grub_packed_guid_t uuid; > + grub_uint8_t volume_name[16]; > + grub_uint32_t feature_incompat; > + > + union > + { > + grub_uint16_t available_compr_algs; > + grub_uint16_t lz4_max_distance; > + } GRUB_PACKED u1; > + > + grub_uint16_t extra_devices; > + grub_uint16_t devt_slotoff; > + grub_uint8_t log2_dirblksz; > + grub_uint8_t xattr_prefix_count; > + grub_uint32_t xattr_prefix_start; > + grub_uint64_t packed_nid; > + grub_uint8_t reserved2[24]; > +} GRUB_PACKED; > + > +#define EROFS_INODE_LAYOUT_COMPACT 0 > +#define EROFS_INODE_LAYOUT_EXTENDED 1 > + > +#define EROFS_INODE_FLAT_PLAIN 0 > +#define EROFS_INODE_COMPRESSED_FULL 1 > +#define EROFS_INODE_FLAT_INLINE 2 > +#define EROFS_INODE_COMPRESSED_COMPACT 3 > +#define EROFS_INODE_CHUNK_BASED 4 > + > +#define EROFS_I_VERSION_MASKS 0x01 > +#define EROFS_I_DATALAYOUT_MASKS 0x07 > + > +#define EROFS_I_VERSION_BIT 0 > +#define EROFS_I_DATALAYOUT_BIT 1 > + > +struct grub_erofs_inode_chunk_info > +{ > + grub_uint16_t format; > + grub_uint16_t reserved; > +} GRUB_PACKED; > + > +#define EROFS_CHUNK_FORMAT_BLKBITS_MASK 0x001F > +#define EROFS_CHUNK_FORMAT_INDEXES 0x0020 > + > +#define EROFS_BLOCK_MAP_ENTRY_SIZE 4 > +#define EROFS_MAP_MAPPED 0x02 > + > +#define EROFS_NULL_ADDR 1 > +#define EROFS_NAME_LEN 255 > +#define EROFS_MIN_LOG2_BLOCK_SIZE 9 > +#define EROFS_MAX_LOG2_BLOCK_SIZE 16 > + > +struct grub_erofs_inode_chunk_index > +{ > + grub_uint16_t advise; > + grub_uint16_t device_id; > + grub_uint32_t blkaddr; > +}; > + > +union grub_erofs_inode_i_u > +{ > + grub_uint32_t compressed_blocks; > + grub_uint32_t raw_blkaddr; > + > + grub_uint32_t rdev; > + > + struct grub_erofs_inode_chunk_info c; > +}; > + > +struct grub_erofs_inode_compact > +{ > + grub_uint16_t i_format; > + > + grub_uint16_t i_xattr_icount; > + grub_uint16_t i_mode; > + grub_uint16_t i_nlink; > + grub_uint32_t i_size; > + grub_uint32_t i_reserved; > + > + union grub_erofs_inode_i_u i_u; > + > + grub_uint32_t i_ino; > + grub_uint16_t i_uid; > + grub_uint16_t i_gid; > + grub_uint32_t i_reserved2; > +} GRUB_PACKED; > + > +struct grub_erofs_inode_extended > +{ > + grub_uint16_t i_format; > + > + grub_uint16_t i_xattr_icount; > + grub_uint16_t i_mode; > + grub_uint16_t i_reserved; > + grub_uint64_t i_size; > + > + union grub_erofs_inode_i_u i_u; > + > + grub_uint32_t i_ino; > + > + grub_uint32_t i_uid; > + grub_uint32_t i_gid; > + grub_uint64_t i_mtime; > + grub_uint32_t i_mtime_nsec; > + grub_uint32_t i_nlink; > + grub_uint8_t i_reserved2[16]; > +} GRUB_PACKED; > + > +#define EROFS_FT_UNKNOWN 0 > +#define EROFS_FT_REG_FILE 1 > +#define EROFS_FT_DIR 2 > +#define EROFS_FT_CHRDEV 3 > +#define EROFS_FT_BLKDEV 4 > +#define EROFS_FT_FIFO 5 > +#define EROFS_FT_SOCK 6 > +#define EROFS_FT_SYMLINK 7 > + > +struct grub_erofs_dirent > +{ > + grub_uint64_t nid; > + grub_uint16_t nameoff; > + grub_uint8_t file_type; > + grub_uint8_t reserved; > +} GRUB_PACKED; > + > +struct grub_erofs_map_blocks > +{ > + grub_uint64_t m_pa; /* physical address */ > + grub_uint64_t m_la; /* logical address */ > + grub_uint64_t m_plen; /* physical length */ > + grub_uint64_t m_llen; /* logical length */ > + grub_uint32_t m_flags; > +}; > + > +struct grub_erofs_xattr_ibody_header > +{ > + grub_uint32_t h_reserved; > + grub_uint8_t h_shared_count; > + grub_uint8_t h_reserved2[7]; > + grub_uint32_t h_shared_xattrs[0]; > +}; > + > +struct grub_fshelp_node > +{ > + struct grub_erofs_data *data; > + struct grub_erofs_inode_extended inode; > + > + grub_uint64_t ino; > + grub_uint8_t inode_type; > + grub_uint8_t inode_datalayout; > + > + /* if the inode has been read into memory? */ > + bool inode_loaded; > +}; > + > +struct grub_erofs_data > +{ > + grub_disk_t disk; > + struct grub_erofs_super sb; > + > + struct grub_fshelp_node inode; > +}; > + > +#define erofs_blocksz(data) (((grub_uint32_t) 1) << data->sb.log2_blksz) > + > +static grub_uint64_t > +erofs_iloc (grub_fshelp_node_t node) > +{ > + struct grub_erofs_super *sb = &node->data->sb; > + > + return (grub_le_to_cpu32 (sb->meta_blkaddr) << sb->log2_blksz) + > (node->ino << EROFS_ISLOTBITS); > +} > + > +static grub_err_t > +erofs_read_inode (struct grub_erofs_data *data, grub_fshelp_node_t node) > +{ > + struct grub_erofs_inode_compact *dic; > + grub_err_t err; > + grub_uint16_t i_format; > + grub_uint64_t addr = erofs_iloc (node); > + > + dic = (struct grub_erofs_inode_compact *) &node->inode; > + > + err = grub_disk_read (data->disk, addr >> GRUB_DISK_SECTOR_BITS, > + addr & (GRUB_DISK_SECTOR_SIZE - 1), > + sizeof (struct grub_erofs_inode_compact), dic); > + if (err != GRUB_ERR_NONE) > + return err; > + > + i_format = grub_le_to_cpu16 (dic->i_format); > + node->inode_type = (i_format >> EROFS_I_VERSION_BIT) & > EROFS_I_VERSION_MASKS; > + node->inode_datalayout = (i_format >> EROFS_I_DATALAYOUT_BIT) & > EROFS_I_DATALAYOUT_MASKS; > + > + switch (node->inode_type) > + { > + case EROFS_INODE_LAYOUT_EXTENDED: > + addr += sizeof (struct grub_erofs_inode_compact); > + err = grub_disk_read ( > + data->disk, addr >> GRUB_DISK_SECTOR_BITS, > + addr & (GRUB_DISK_SECTOR_SIZE - 1), > + sizeof (struct grub_erofs_inode_extended) - sizeof (struct > grub_erofs_inode_compact), > + (grub_uint8_t *) dic + sizeof (struct grub_erofs_inode_compact)); > + if (err != GRUB_ERR_NONE) > + return err; > + break; > + case EROFS_INODE_LAYOUT_COMPACT: > + break; > + default: > + return grub_error (GRUB_ERR_BAD_FS, "invalid type %u @ inode %" > PRIuGRUB_UINT64_T, > + node->inode_type, node->ino); > + } > + > + node->inode_loaded = true; > + > + return 0; > +} > + > +static grub_uint64_t > +erofs_inode_size (grub_fshelp_node_t node) > +{ > + return node->inode_type == EROFS_INODE_LAYOUT_COMPACT > + ? sizeof (struct grub_erofs_inode_compact) > + : sizeof (struct grub_erofs_inode_extended); > +} > + > +static grub_uint64_t > +erofs_inode_file_size (grub_fshelp_node_t node) > +{ > + struct grub_erofs_inode_compact *dic = (struct grub_erofs_inode_compact > *) &node->inode; > + > + return node->inode_type == EROFS_INODE_LAYOUT_COMPACT > + ? grub_le_to_cpu32 (dic->i_size) > + : grub_le_to_cpu64 (node->inode.i_size); > +} > + > +static grub_uint32_t > +erofs_inode_xattr_ibody_size (grub_fshelp_node_t node) > +{ > + grub_uint16_t cnt = grub_le_to_cpu16 (node->inode.i_xattr_icount); > + > + if (cnt == 0) > + return 0; > + > + return sizeof (struct grub_erofs_xattr_ibody_header) + ((cnt - 1) * > sizeof (grub_uint32_t)); > +} > + > +static grub_uint64_t > +erofs_inode_mtime (grub_fshelp_node_t node) > +{ > + return node->inode_type == EROFS_INODE_LAYOUT_COMPACT > + ? grub_le_to_cpu64 (node->data->sb.build_time) > + : grub_le_to_cpu64 (node->inode.i_mtime); > +} > + > +static grub_err_t > +erofs_map_blocks_flatmode (grub_fshelp_node_t node, > + struct grub_erofs_map_blocks *map) > +{ > + grub_uint64_t nblocks, lastblk, file_size; > + bool tailendpacking = (node->inode_datalayout == > EROFS_INODE_FLAT_INLINE); > + grub_uint32_t blocksz = erofs_blocksz (node->data); > + > + file_size = erofs_inode_file_size (node); > + nblocks = (file_size + blocksz - 1) >> node->data->sb.log2_blksz; > + lastblk = nblocks - tailendpacking; > + > + map->m_flags = EROFS_MAP_MAPPED; > + > + if (map->m_la < (lastblk * blocksz)) > + { > + if (grub_mul (grub_le_to_cpu32 (node->inode.i_u.raw_blkaddr), > blocksz, &map->m_pa) || > + grub_add (map->m_pa, map->m_la, &map->m_pa)) > + return GRUB_ERR_OUT_OF_RANGE; > + if (grub_sub (lastblk * blocksz, map->m_la, &map->m_plen)) > + return GRUB_ERR_OUT_OF_RANGE; > + } > + else if (tailendpacking) > + { > + if (grub_add (erofs_iloc (node), erofs_inode_size (node), > &map->m_pa) || > + grub_add (map->m_pa, erofs_inode_xattr_ibody_size (node), > &map->m_pa) || > + grub_add (map->m_pa, map->m_la % blocksz, &map->m_pa)) > + return GRUB_ERR_OUT_OF_RANGE; > + if (grub_sub (file_size, map->m_la, &map->m_plen)) > + return GRUB_ERR_OUT_OF_RANGE; > + > + if (((map->m_pa % blocksz) + map->m_plen) > blocksz) > + return grub_error ( > + GRUB_ERR_BAD_FS, > + "inline data cross block boundary @ inode %" PRIuGRUB_UINT64_T, > + node->ino); > + } > + else > + return grub_error (GRUB_ERR_BAD_FS, > + "invalid map->m_la=%" PRIuGRUB_UINT64_T > + " @ inode %" PRIuGRUB_UINT64_T, > + map->m_la, node->ino); > + > + map->m_llen = map->m_plen; > + return GRUB_ERR_NONE; > +} > + > +static grub_err_t > +erofs_map_blocks_chunkmode (grub_fshelp_node_t node, > + struct grub_erofs_map_blocks *map) > +{ > + grub_uint16_t chunk_format = grub_le_to_cpu16 > (node->inode.i_u.c.format); > + grub_uint64_t unit, pos, chunknr; > + grub_uint8_t chunkbits; > + grub_err_t err; > + > + if (chunk_format & EROFS_CHUNK_FORMAT_INDEXES) > + unit = sizeof (struct grub_erofs_inode_chunk_index); > + else > + unit = EROFS_BLOCK_MAP_ENTRY_SIZE; > + > + chunkbits = node->data->sb.log2_blksz + (chunk_format & > EROFS_CHUNK_FORMAT_BLKBITS_MASK); > + if (chunkbits > 64) > + return grub_error (GRUB_ERR_BAD_FS, "invalid chunkbits %u @ inode %" > PRIuGRUB_UINT64_T, > + chunkbits, node->ino); > + > + chunknr = map->m_la >> chunkbits; > + > + if (grub_add (erofs_iloc (node), erofs_inode_size (node), &pos) || > + grub_add (pos, erofs_inode_xattr_ibody_size (node), &pos)) > + return GRUB_ERR_OUT_OF_RANGE; > + pos = ALIGN_UP (pos, unit); > + if (grub_add (pos, chunknr * unit, &pos)) > + return GRUB_ERR_OUT_OF_RANGE; > + > + map->m_la = chunknr << chunkbits; > + > + if (grub_sub (erofs_inode_file_size (node), map->m_la, &map->m_plen)) > + return GRUB_ERR_OUT_OF_RANGE; > + map->m_plen = grub_min (((grub_uint64_t) 1) << chunkbits, > + ALIGN_UP (map->m_plen, erofs_blocksz > (node->data))); > + > + if (chunk_format & EROFS_CHUNK_FORMAT_INDEXES) > + { > + struct grub_erofs_inode_chunk_index idx; > + grub_uint32_t blkaddr; > + > + err = grub_disk_read (node->data->disk, pos >> > GRUB_DISK_SECTOR_BITS, > + pos & (GRUB_DISK_SECTOR_SIZE - 1), unit, &idx); > + if (err != GRUB_ERR_NONE) > + return err; > + > + blkaddr = grub_le_to_cpu32 (idx.blkaddr); > + > + if (blkaddr == (grub_uint32_t) EROFS_NULL_ADDR) > + { > + map->m_pa = 0; > + map->m_flags = 0; > + } > + else > + { > + map->m_pa = blkaddr << node->data->sb.log2_blksz; > + map->m_flags = EROFS_MAP_MAPPED; > + } > + } > + else > + { > + grub_uint32_t blkaddr_le, blkaddr; > + > + err = grub_disk_read (node->data->disk, pos >> > GRUB_DISK_SECTOR_BITS, > + pos & (GRUB_DISK_SECTOR_SIZE - 1), unit, > &blkaddr_le); > + if (err != GRUB_ERR_NONE) > + return err; > + > + blkaddr = grub_le_to_cpu32 (blkaddr_le); > + if (blkaddr == (grub_uint32_t) EROFS_NULL_ADDR) > + { > + map->m_pa = 0; > + map->m_flags = 0; > + } > + else > + { > + map->m_pa = blkaddr << node->data->sb.log2_blksz; > + map->m_flags = EROFS_MAP_MAPPED; > + } > + } > + > + map->m_llen = map->m_plen; > + return GRUB_ERR_NONE; > +} > + > +static grub_err_t > +erofs_map_blocks (grub_fshelp_node_t node, struct grub_erofs_map_blocks > *map) > +{ > + if (map->m_la >= erofs_inode_file_size (node)) > + { > + map->m_llen = map->m_plen = 0; > + map->m_pa = 0; > + map->m_flags = 0; > + return GRUB_ERR_NONE; > + } > + > + if (node->inode_datalayout != EROFS_INODE_CHUNK_BASED) > + return erofs_map_blocks_flatmode (node, map); > + else > + return erofs_map_blocks_chunkmode (node, map); > +} > + > +static grub_err_t > +erofs_read_raw_data (grub_fshelp_node_t node, grub_uint8_t *buf, > grub_uint64_t size, > + grub_uint64_t offset, grub_uint64_t *bytes) > +{ > + struct grub_erofs_map_blocks map = {0}; > + grub_uint64_t cur; > + grub_err_t err; > + > + if (bytes) > + *bytes = 0; > + > + if (!node->inode_loaded) > + { > + err = erofs_read_inode (node->data, node); > + if (err != GRUB_ERR_NONE) > + return err; > + } > + > + cur = offset; > + while (cur < offset + size) > + { > + grub_uint8_t *const estart = buf + cur - offset; > + grub_uint64_t eend, moff = 0; > + > + map.m_la = cur; > + err = erofs_map_blocks (node, &map); > + if (err != GRUB_ERR_NONE) > + return err; > + > + eend = grub_min (offset + size, map.m_la + map.m_llen); > + if (!(map.m_flags & EROFS_MAP_MAPPED)) > + { > + if (!map.m_llen) > + { > + /* reached EOF */ > + grub_memset (estart, 0, offset + size - cur); > + cur = offset + size; > + continue; > + } > + > + /* Hole */ > + grub_memset (estart, 0, eend - cur); > + if (bytes) > + *bytes += eend - cur; > + cur = eend; > + continue; > + } > + > + if (cur > map.m_la) > + { > + moff = cur - map.m_la; > + map.m_la = cur; > + } > + > + err = grub_disk_read (node->data->disk, > + (map.m_pa + moff) >> GRUB_DISK_SECTOR_BITS, > + (map.m_pa + moff) & (GRUB_DISK_SECTOR_SIZE - > 1), > + eend - map.m_la, estart); > + if (err != GRUB_ERR_NONE) > + return err; > + > + if (bytes) > + *bytes += eend - map.m_la; > + > + cur = eend; > + } > + > + return GRUB_ERR_NONE; > +} > + > +static int > +erofs_iterate_dir (grub_fshelp_node_t dir, grub_fshelp_iterate_dir_hook_t > hook, > + void *hook_data) > +{ > + grub_uint64_t offset = 0, file_size; > + grub_uint32_t blocksz = erofs_blocksz (dir->data); > + grub_uint8_t *buf; > + grub_err_t err; > + > + if (!dir->inode_loaded) > + { > + err = erofs_read_inode (dir->data, dir); > + if (err != GRUB_ERR_NONE) > + return 0; > + } > + > + file_size = erofs_inode_file_size (dir); > + buf = grub_malloc (blocksz); > + if (!buf) > + { > + grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); > + return 0; > + } > + > + while (offset < file_size) > + { > + grub_uint64_t maxsize = grub_min (blocksz, file_size - offset); > + struct grub_erofs_dirent *de = (void *) buf, *end; > + grub_uint16_t nameoff; > + > + err = erofs_read_raw_data (dir, buf, maxsize, offset, NULL); > + if (err != GRUB_ERR_NONE) > + goto not_found; > + > + nameoff = grub_le_to_cpu16 (de->nameoff); > + if (nameoff < sizeof (struct grub_erofs_dirent) || nameoff >= > maxsize) > + { > + grub_error (GRUB_ERR_BAD_FS, > + "invalid nameoff %u @ inode %" PRIuGRUB_UINT64_T, > + nameoff, dir->ino); > + goto not_found; > + } > + > + end = (struct grub_erofs_dirent *) ((grub_uint8_t *) de + nameoff); > + while (de < end) > + { > + struct grub_fshelp_node *fdiro; > + enum grub_fshelp_filetype type; > + char filename[EROFS_NAME_LEN + 1]; > + grub_size_t de_namelen; > + const char *de_name; > + > + fdiro = grub_malloc (sizeof (struct grub_fshelp_node)); > + if (!fdiro) > + { > + grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); > + goto not_found; > + } > + > + fdiro->data = dir->data; > + fdiro->ino = grub_le_to_cpu64 (de->nid); > + fdiro->inode_loaded = false; > + > + nameoff = grub_le_to_cpu16 (de->nameoff); > + if (nameoff < sizeof (struct grub_erofs_dirent) || nameoff >= > maxsize) > + { > + grub_error (GRUB_ERR_BAD_FS, > + "invalid nameoff %u @ inode %" PRIuGRUB_UINT64_T, > + nameoff, dir->ino); > + grub_free (fdiro); > + goto not_found; > + } > + > + de_name = (char *) buf + nameoff; > + if (de + 1 >= end) > + de_namelen = grub_strnlen (de_name, maxsize - nameoff); > + else > + de_namelen = grub_le_to_cpu16 (de[1].nameoff) - nameoff; > + > + if (nameoff + de_namelen > maxsize || de_namelen > > EROFS_NAME_LEN) > + { > + grub_error (GRUB_ERR_BAD_FS, > + "invalid de_namelen %" PRIuGRUB_SIZE > + " @ inode %" PRIuGRUB_UINT64_T, > + de_namelen, dir->ino); > + grub_free (fdiro); > + goto not_found; > + } > + > + grub_memcpy (filename, de_name, de_namelen); > + filename[de_namelen] = '\0'; > + > + switch (grub_le_to_cpu16 (de->file_type)) > + { > + case EROFS_FT_REG_FILE: > + case EROFS_FT_BLKDEV: > + case EROFS_FT_CHRDEV: > + case EROFS_FT_FIFO: > + case EROFS_FT_SOCK: > + type = GRUB_FSHELP_REG; > + break; > + case EROFS_FT_DIR: > + type = GRUB_FSHELP_DIR; > + break; > + case EROFS_FT_SYMLINK: > + type = GRUB_FSHELP_SYMLINK; > + break; > + case EROFS_FT_UNKNOWN: > + default: > + type = GRUB_FSHELP_UNKNOWN; > + } > + > + if (hook (filename, type, fdiro, hook_data)) > + { > + grub_free (buf); > + return 1; > + } > + > + ++de; > + } > + > + offset += maxsize; > + } > + > + not_found: > + grub_free (buf); > + return 0; > +} > + > +static char * > +erofs_read_symlink (grub_fshelp_node_t node) > +{ > + char *symlink; > + grub_size_t sz; > + grub_err_t err; > + > + if (!node->inode_loaded) > + { > + err = erofs_read_inode (node->data, node); > + if (err != GRUB_ERR_NONE) > + return NULL; > + } > + > + if (grub_add (erofs_inode_file_size (node), 1, &sz)) > + { > + grub_error (GRUB_ERR_OUT_OF_RANGE, N_ ("overflow is detected")); > + return NULL; > + } > + > + symlink = grub_malloc (sz); > + if (!symlink) > + return NULL; > + > + err = erofs_read_raw_data (node, (grub_uint8_t *) symlink, sz - 1, 0, > NULL); > + if (err != GRUB_ERR_NONE) > + { > + grub_free (symlink); > + return NULL; > + } > + > + symlink[sz - 1] = '\0'; > + return symlink; > +} > + > +static struct grub_erofs_data * > +erofs_mount (grub_disk_t disk, bool read_root) > +{ > + struct grub_erofs_super sb; > + grub_err_t err; > + struct grub_erofs_data *data; > + grub_uint32_t feature; > + > + err = grub_disk_read (disk, EROFS_SUPER_OFFSET >> > GRUB_DISK_SECTOR_BITS, 0, > + sizeof (sb), &sb); > + if (grub_errno == GRUB_ERR_OUT_OF_RANGE) > + grub_error (GRUB_ERR_BAD_FS, "not a valid erofs filesystem"); > + if (err != GRUB_ERR_NONE) > + return NULL; > + if (sb.magic != grub_cpu_to_le32_compile_time (EROFS_MAGIC) || > + grub_le_to_cpu32 (sb.log2_blksz) < EROFS_MIN_LOG2_BLOCK_SIZE || > + grub_le_to_cpu32 (sb.log2_blksz) > EROFS_MAX_LOG2_BLOCK_SIZE) > + { > + grub_error (GRUB_ERR_BAD_FS, "not a valid erofs filesystem"); > + return NULL; > + } > + > + feature = grub_le_to_cpu32 (sb.feature_incompat); > + if (feature & ~EROFS_ALL_FEATURE_INCOMPAT) > + { > + grub_error (GRUB_ERR_BAD_FS, "unsupported features: 0x%x", > + feature & ~EROFS_ALL_FEATURE_INCOMPAT); > + return NULL; > + } > + > + data = grub_malloc (sizeof (*data)); > + if (!data) > + { > + grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); > + return NULL; > + } > + > + data->disk = disk; > + data->sb = sb; > + > + if (read_root) > + { > + data->inode.data = data; > + data->inode.ino = grub_le_to_cpu16 (sb.root_nid); > + err = erofs_read_inode (data, &data->inode); > + if (err != GRUB_ERR_NONE) > + { > + grub_free (data); > + return NULL; > + } > + } > + > + return data; > +} > + > +/* Context for grub_erofs_dir. */ > +struct grub_erofs_dir_ctx > +{ > + grub_fs_dir_hook_t hook; > + void *hook_data; > + struct grub_erofs_data *data; > +}; > + > +/* Helper for grub_erofs_dir. */ > +static int > +erofs_dir_iter (const char *filename, enum grub_fshelp_filetype filetype, > + grub_fshelp_node_t node, void *data) > +{ > + struct grub_erofs_dir_ctx *ctx = data; > + struct grub_dirhook_info info = {0}; > + > + if (!node->inode_loaded) > + { > + erofs_read_inode (ctx->data, node); > + grub_errno = GRUB_ERR_NONE; > + } > + > + if (node->inode_loaded) > + { > + info.mtimeset = 1; > + info.mtime = erofs_inode_mtime (node); > + } > + > + info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); > + grub_free (node); > + return ctx->hook (filename, &info, ctx->hook_data); > +} > + > +static grub_err_t > +grub_erofs_dir (grub_device_t device, const char *path, > grub_fs_dir_hook_t hook, > + void *hook_data) > +{ > + grub_fshelp_node_t fdiro = NULL; > + grub_err_t err; > + struct grub_erofs_dir_ctx ctx = { > + .hook = hook, > + .hook_data = hook_data > + }; > + > + ctx.data = erofs_mount (device->disk, true); > + if (!ctx.data) > + goto fail; > + > + err = grub_fshelp_find_file (path, &ctx.data->inode, &fdiro, > erofs_iterate_dir, > + erofs_read_symlink, GRUB_FSHELP_DIR); > + if (err != GRUB_ERR_NONE) > + goto fail; > + > + erofs_iterate_dir (fdiro, erofs_dir_iter, &ctx); > + > + fail: > + if (fdiro != &ctx.data->inode) > + grub_free (fdiro); > + grub_free (ctx.data); > + > + return grub_errno; > +} > + > +static grub_err_t > +grub_erofs_open (grub_file_t file, const char *name) > +{ > + struct grub_erofs_data *data; > + struct grub_fshelp_node *fdiro = NULL; > + grub_err_t err; > + > + data = erofs_mount (file->device->disk, true); > + if (!data) > + { > + err = grub_errno; > + goto fail; > + } > + > + err = grub_fshelp_find_file (name, &data->inode, &fdiro, > erofs_iterate_dir, > + erofs_read_symlink, GRUB_FSHELP_REG); > + if (err != GRUB_ERR_NONE) > + goto fail; > + > + if (!fdiro->inode_loaded) > + { > + err = erofs_read_inode (data, fdiro); > + if (err != GRUB_ERR_NONE) > + goto fail; > + } > + > + grub_memcpy (&data->inode, fdiro, sizeof (*fdiro)); > + grub_free (fdiro); > + > + file->data = data; > + file->size = erofs_inode_file_size (&data->inode); > + > + return GRUB_ERR_NONE; > + > + fail: > + if (fdiro != &data->inode) > + grub_free (fdiro); > + grub_free (data); > + > + return err; > +} > + > +static grub_ssize_t > +grub_erofs_read (grub_file_t file, char *buf, grub_size_t len) > +{ > + struct grub_erofs_data *data = file->data; > + struct grub_fshelp_node *inode = &data->inode; > + grub_off_t off = file->offset; > + grub_uint64_t ret = 0, file_size; > + grub_err_t err; > + > + if (!inode->inode_loaded) > + { > + err = erofs_read_inode (data, inode); > + if (err != GRUB_ERR_NONE) > + { > + grub_error (GRUB_ERR_IO, "cannot read @ inode %" > PRIuGRUB_UINT64_T, inode->ino); > + return -1; > + } > + } > + > + file_size = erofs_inode_file_size (inode); > + > + if (off > file_size) > + { > + grub_error (GRUB_ERR_IO, "read past EOF @ inode %" > PRIuGRUB_UINT64_T, inode->ino); > + return -1; > + } > + if (off == file_size) > + return 0; > + > + if (off + len > file_size) > + len = file_size - off; > + > + err = erofs_read_raw_data (inode, (grub_uint8_t *) buf, len, off, &ret); > + if (err != GRUB_ERR_NONE) > + { > + grub_error (GRUB_ERR_IO, "cannot read file @ inode %" > PRIuGRUB_UINT64_T, inode->ino); > + return -1; > + } > + > + return ret; > +} > + > +static grub_err_t > +grub_erofs_close (grub_file_t file) > +{ > + grub_free (file->data); > + > + return GRUB_ERR_NONE; > +} > + > +static grub_err_t > +grub_erofs_uuid (grub_device_t device, char **uuid) > +{ > + struct grub_erofs_data *data; > + > + data = erofs_mount (device->disk, false); > + if (!data) > + { > + *uuid = NULL; > + return grub_errno; > + } > + > + *uuid = grub_xasprintf ("%pG", &data->sb.uuid); > + > + grub_free (data); > + > + return GRUB_ERR_NONE; > +} > + > +static grub_err_t > +grub_erofs_label (grub_device_t device, char **label) > +{ > + struct grub_erofs_data *data; > + > + data = erofs_mount (device->disk, false); > + if (!data) > + { > + *label = NULL; > + return grub_errno; > + } > + > + *label = grub_strndup ((char *) data->sb.volume_name, sizeof > (data->sb.volume_name)); > + if (!*label) > + return grub_errno; > + > + grub_free (data); > + > + return GRUB_ERR_NONE; > +} > + > +static grub_err_t > +grub_erofs_mtime (grub_device_t device, grub_int64_t *tm) > +{ > + struct grub_erofs_data *data; > + > + data = erofs_mount (device->disk, false); > + if (!data) > + { > + *tm = 0; > + return grub_errno; > + } > + > + *tm = grub_le_to_cpu64 (data->sb.build_time); > + > + grub_free (data); > + > + return GRUB_ERR_NONE; > +} > + > +static struct grub_fs grub_erofs_fs = { > + .name = "erofs", > + .fs_dir = grub_erofs_dir, > + .fs_open = grub_erofs_open, > + .fs_read = grub_erofs_read, > + .fs_close = grub_erofs_close, > + .fs_uuid = grub_erofs_uuid, > + .fs_label = grub_erofs_label, > + .fs_mtime = grub_erofs_mtime, > +#ifdef GRUB_UTIL > + .reserved_first_sector = 1, > + .blocklist_install = 0, > +#endif > + .next = 0, > +}; > + > +GRUB_MOD_INIT (erofs) > +{ > + grub_fs_register (&grub_erofs_fs); > +} > + > +GRUB_MOD_FINI (erofs) > +{ > + grub_fs_unregister (&grub_erofs_fs); > +} > \ No newline at end of file > diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c > index 7cee5d75c..67240d64d 100644 > --- a/grub-core/kern/misc.c > +++ b/grub-core/kern/misc.c > @@ -594,6 +594,20 @@ grub_strlen (const char *s) > return p - s; > } > > +grub_size_t > +grub_strnlen (const char *s, grub_size_t n) > +{ > + const char *p = s; > + > + if (n == 0) > + return 0; > + > + while (n-- && *p) > + p++; > + > + return p - s; > +} > + > static inline void > grub_reverse (char *str) > { > diff --git a/include/grub/misc.h b/include/grub/misc.h > index 1b35a167f..eb4aa55c1 100644 > --- a/include/grub/misc.h > +++ b/include/grub/misc.h > @@ -335,6 +335,7 @@ char *EXPORT_FUNC(grub_strdup) (const char *s) > WARN_UNUSED_RESULT; > char *EXPORT_FUNC(grub_strndup) (const char *s, grub_size_t n) > WARN_UNUSED_RESULT; > void *EXPORT_FUNC(grub_memset) (void *s, int c, grub_size_t n); > grub_size_t EXPORT_FUNC(grub_strlen) (const char *s) WARN_UNUSED_RESULT; > +grub_size_t EXPORT_FUNC(grub_strnlen) (const char *s, grub_size_t n) > WARN_UNUSED_RESULT; > > /* Replace all `ch' characters of `input' with `with' and copy the > result into `output'; return EOS address of `output'. */ > -- > 2.44.0 > > > _______________________________________________ > Grub-devel mailing list > Grub-devel@gnu.org > https://lists.gnu.org/mailman/listinfo/grub-devel >
_______________________________________________ Grub-devel mailing list Grub-devel@gnu.org https://lists.gnu.org/mailman/listinfo/grub-devel