grub-devel.gnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v5 0/2] Introduce EROFS support
@ 2024-03-03 17:15 Yifan Zhao
  2024-03-03 17:15 ` [PATCH v5 1/2] fs/erofs: Add support for EROFS Yifan Zhao
                   ` (2 more replies)
  0 siblings, 3 replies; 14+ messages in thread
From: Yifan Zhao @ 2024-03-03 17:15 UTC (permalink / raw
  To: dkiper, development; +Cc: grub-devel, hsiangkao, zhaoyifan

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 together with related tests.
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

changelog since v4:
- correct the order of 'erofs-utils' in INSTALL
- fix format and alignment issue of #define
- use #define instead of enum for some constants
- fix incorrect usage of grub_off_t, use grub_uint64_t instead
- drop inline keyword for some functions
- 'if (err)' -> 'if (err != GRUB_ERR_NONE)'
- do not split lines slightly over 80 characters
- drop 'grub_' prefix for local functions in erofs module
- use safe math (and add bound check) for risky math operations
- other variable name and style changes according to Daniel's review

This interdiff shows the changes between v5 and v4 for easier review. In
addition, another interdiff between v4 and v2 (previous RVB version) is
provided to Glenn for reference.

Yifan Zhao (2):
  fs/erofs: Add support for EROFS
  fs/erofs: Add tests for EROFS in grub-fs-tester

 .gitignore                   |   1 +
 INSTALL                      |   8 +-
 Makefile.util.def            |   7 +
 docs/grub.texi               |   3 +-
 grub-core/Makefile.core.def  |   5 +
 grub-core/fs/erofs.c         | 978 +++++++++++++++++++++++++++++++++++
 grub-core/kern/misc.c        |  14 +
 include/grub/misc.h          |   1 +
 tests/erofs_test.in          |  20 +
 tests/util/grub-fs-tester.in |  32 +-
 10 files changed, 1057 insertions(+), 12 deletions(-)
 create mode 100644 grub-core/fs/erofs.c
 create mode 100644 tests/erofs_test.in

Interdiff against v4:
diff --git a/INSTALL b/INSTALL
index 545015ba2..84030c9f4 100644
--- a/INSTALL
+++ b/INSTALL
@@ -83,7 +83,7 @@ Prerequisites for make-check:
     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, erofs-utils, e2fsprogs, exfat-utils, f2fs-tools,
+  - 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
diff --git a/grub-core/fs/erofs.c b/grub-core/fs/erofs.c
index de57aaa5e..34f16ba20 100644
--- a/grub-core/fs/erofs.c
+++ b/grub-core/fs/erofs.c
@@ -1,7 +1,7 @@
 /* erofs.c - Enhanced Read-Only File System */
 /*
  *  GRUB  --  GRand Unified Bootloader
- *  Copyright (C) 2023 Free Software Foundation, Inc.
+ *  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
@@ -30,12 +30,12 @@
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
-#define EROFS_SUPER_OFFSET (1024)
+#define EROFS_SUPER_OFFSET	1024
 #define EROFS_MAGIC		0xE0F5E1E2
-#define EROFS_ISLOTBITS (5)
+#define EROFS_ISLOTBITS		5
 
 #define EROFS_FEATURE_INCOMPAT_CHUNKED_FILE	0x00000004
-#define EROFS_ALL_FEATURE_INCOMPAT (EROFS_FEATURE_INCOMPAT_CHUNKED_FILE)
+#define EROFS_ALL_FEATURE_INCOMPAT		EROFS_FEATURE_INCOMPAT_CHUNKED_FILE
 
 struct grub_erofs_super
 {
@@ -53,7 +53,7 @@ struct grub_erofs_super
   grub_uint32_t blocks;
   grub_uint32_t meta_blkaddr;
   grub_uint32_t xattr_blkaddr;
-  grub_uint8_t uuid[16];
+  grub_packed_guid_t uuid;
   grub_uint8_t volume_name[16];
   grub_uint32_t feature_incompat;
 
@@ -75,15 +75,11 @@ struct grub_erofs_super
 #define EROFS_INODE_LAYOUT_COMPACT	0
 #define EROFS_INODE_LAYOUT_EXTENDED	1
 
-enum
-{
-  EROFS_INODE_FLAT_PLAIN = 0,
-  EROFS_INODE_COMPRESSED_FULL = 1,
-  EROFS_INODE_FLAT_INLINE = 2,
-  EROFS_INODE_COMPRESSED_COMPACT = 3,
-  EROFS_INODE_CHUNK_BASED = 4,
-  EROFS_INODE_DATALAYOUT_MAX
-};
+#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
@@ -101,8 +97,11 @@ struct grub_erofs_inode_chunk_info
 #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_NULL_ADDR			1
+#define EROFS_NAME_LEN			255
+#define EROFS_MAX_LOG2_BLOCK_SIZE	16
 
 struct grub_erofs_inode_chunk_index
 {
@@ -160,20 +159,14 @@ struct grub_erofs_inode_extended
   grub_uint8_t i_reserved2[16];
 } GRUB_PACKED;
 
-enum
-{
-  EROFS_FT_UNKNOWN,
-  EROFS_FT_REG_FILE,
-  EROFS_FT_DIR,
-  EROFS_FT_CHRDEV,
-  EROFS_FT_BLKDEV,
-  EROFS_FT_FIFO,
-  EROFS_FT_SOCK,
-  EROFS_FT_SYMLINK,
-  EROFS_FT_MAX
-};
-
-#define EROFS_NAME_LEN 255
+#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
 {
@@ -183,12 +176,12 @@ struct grub_erofs_dirent
   grub_uint8_t reserved;
 } GRUB_PACKED;
 
-#define EROFS_MAP_MAPPED 0x02
-
 struct grub_erofs_map_blocks
 {
-  grub_off_t m_pa, m_la;
-  grub_off_t m_plen, m_llen;
+  grub_uint64_t m_pa;
+  grub_uint64_t m_la;
+  grub_uint64_t m_plen;
+  grub_uint64_t m_llen;
   grub_uint32_t m_flags;
 };
 
@@ -210,7 +203,7 @@ struct grub_fshelp_node
   grub_uint8_t inode_datalayout;
 
   /* if the inode has been read into memory? */
-  bool inode_read;
+  bool inode_loaded;
 };
 
 struct grub_erofs_data
@@ -221,19 +214,18 @@ struct grub_erofs_data
   struct grub_fshelp_node inode;
 };
 
-#define erofs_blocksz(data) (1u << data->sb.log2_blksz)
+#define erofs_blocksz(data) (((grub_uint32_t) 1) << data->sb.log2_blksz)
 
-static inline grub_uint64_t
+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);
+  return (grub_le_to_cpu32 (sb->meta_blkaddr) << sb->log2_blksz) + (node->ino << EROFS_ISLOTBITS);
 }
 
 static grub_err_t
-grub_erofs_read_inode (struct grub_erofs_data *data, grub_fshelp_node_t node)
+erofs_read_inode (struct grub_erofs_data *data, grub_fshelp_node_t node)
 {
   struct grub_erofs_inode_compact *dic;
   grub_err_t err;
@@ -244,13 +236,11 @@ grub_erofs_read_inode (struct grub_erofs_data *data, grub_fshelp_node_t node)
   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)
+  if (err != GRUB_ERR_NONE)
     return err;
 
-  node->inode_type =
-      (dic->i_format >> EROFS_I_VERSION_BIT) & EROFS_I_VERSION_MASKS;
-  node->inode_datalayout =
-      (dic->i_format >> EROFS_I_DATALAYOUT_BIT) & EROFS_I_DATALAYOUT_MASKS;
+  node->inode_type = (dic->i_format >> EROFS_I_VERSION_BIT) & EROFS_I_VERSION_MASKS;
+  node->inode_datalayout = (dic->i_format >> EROFS_I_DATALAYOUT_BIT) & EROFS_I_DATALAYOUT_MASKS;
 
   switch (node->inode_type)
     {
@@ -259,26 +249,24 @@ grub_erofs_read_inode (struct grub_erofs_data *data, grub_fshelp_node_t node)
       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),
+	  sizeof (struct grub_erofs_inode_extended) - sizeof (struct grub_erofs_inode_compact),
 	  (char *) dic + sizeof (struct grub_erofs_inode_compact));
-      if (err)
+      if (err != GRUB_ERR_NONE)
 	return err;
       break;
     case EROFS_INODE_LAYOUT_COMPACT:
       break;
     default:
-      return grub_error (GRUB_ERR_BAD_FS,
-			 "invalid inode version %u @ inode %" PRIuGRUB_UINT64_T,
+      return grub_error (GRUB_ERR_BAD_FS, "invalid type %u @ inode %" PRIuGRUB_UINT64_T,
 			 node->inode_type, node->ino);
     }
 
-  node->inode_read = true;
+  node->inode_loaded = true;
 
   return 0;
 }
 
-static inline grub_uint64_t
+static grub_uint64_t
 erofs_inode_size (grub_fshelp_node_t node)
 {
   return node->inode_type == EROFS_INODE_LAYOUT_COMPACT
@@ -286,28 +274,28 @@ erofs_inode_size (grub_fshelp_node_t node)
 	     : sizeof (struct grub_erofs_inode_extended);
 }
 
-static inline grub_uint64_t
+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;
+  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 inline grub_uint32_t
+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);
 
-  return cnt ? sizeof (struct grub_erofs_xattr_ibody_header) +
-		   (cnt - 1) * sizeof (grub_uint32_t)
-	     : 0;
+  if (cnt == 0)
+    return 0;
+
+  return sizeof (struct grub_erofs_xattr_ibody_header) + (cnt - 1) * sizeof (grub_uint32_t);
 }
 
-static inline grub_uint64_t
+static grub_uint64_t
 erofs_inode_mtime (grub_fshelp_node_t node)
 {
   return node->inode_type == EROFS_INODE_LAYOUT_COMPACT
@@ -316,31 +304,35 @@ erofs_inode_mtime (grub_fshelp_node_t node)
 }
 
 static grub_err_t
-grub_erofs_map_blocks_flatmode (grub_fshelp_node_t node,
+erofs_map_blocks_flatmode (grub_fshelp_node_t node,
 			   struct grub_erofs_map_blocks *map)
 {
-  grub_off_t nblocks, lastblk, file_size;
-  grub_off_t tailendpacking =
-      (node->inode_datalayout == EROFS_INODE_FLAT_INLINE) ? 1 : 0;
+  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;
+  lastblk = nblocks - (tailendpacking ? 1 : 0);
 
   map->m_flags = EROFS_MAP_MAPPED;
 
   if (map->m_la < (lastblk * blocksz))
     {
-      map->m_pa =
-	  grub_le_to_cpu32 (node->inode.i_u.raw_blkaddr) * blocksz + map->m_la;
-      map->m_plen = lastblk * blocksz - map->m_la;
+      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)
     {
-      map->m_pa = erofs_iloc (node) + erofs_inode_size (node) +
-		  erofs_inode_xattr_ibody_size (node) + (map->m_la % blocksz);
-      map->m_plen = file_size - map->m_la;
+      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 (
@@ -359,12 +351,11 @@ grub_erofs_map_blocks_flatmode (grub_fshelp_node_t node,
 }
 
 static grub_err_t
-grub_erofs_map_blocks_chunkmode (grub_fshelp_node_t node,
+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_off_t unit, pos;
-  grub_uint64_t chunknr;
+  grub_uint64_t unit, pos, chunknr;
   grub_uint8_t chunkbits;
   grub_err_t err;
 
@@ -373,19 +364,26 @@ grub_erofs_map_blocks_chunkmode (grub_fshelp_node_t node,
   else
     unit = EROFS_BLOCK_MAP_ENTRY_SIZE;
 
-  chunkbits = node->data->sb.log2_blksz +
-	      (chunk_format & EROFS_CHUNK_FORMAT_BLKBITS_MASK);
+  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;
-  pos = ALIGN_UP (erofs_iloc (node) + erofs_inode_size (node) +
-		      erofs_inode_xattr_ibody_size (node),
-		  unit);
-  pos += chunknr * unit;
+
+  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;
-  map->m_plen = grub_min (1ULL << chunkbits,
-			  ALIGN_UP (erofs_inode_file_size (node) - map->m_la,
-				    erofs_blocksz (node->data)));
+
+  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)
     {
@@ -394,7 +392,7 @@ grub_erofs_map_blocks_chunkmode (grub_fshelp_node_t node,
 
       err = grub_disk_read (node->data->disk, pos >> GRUB_DISK_SECTOR_BITS,
 			    pos & (GRUB_DISK_SECTOR_SIZE - 1), unit, &idx);
-      if (err)
+      if (err != GRUB_ERR_NONE)
 	return err;
 
       blkaddr = grub_le_to_cpu32 (idx.blkaddr);
@@ -414,10 +412,9 @@ grub_erofs_map_blocks_chunkmode (grub_fshelp_node_t node,
     {
       grub_uint32_t blkaddr_le, blkaddr;
 
-      err =
-	  grub_disk_read (node->data->disk, pos >> GRUB_DISK_SECTOR_BITS,
+      err = grub_disk_read (node->data->disk, pos >> GRUB_DISK_SECTOR_BITS,
 			    pos & (GRUB_DISK_SECTOR_SIZE - 1), unit, &blkaddr_le);
-      if (err)
+      if (err != GRUB_ERR_NONE)
 	return err;
 
       blkaddr = grub_le_to_cpu32 (blkaddr_le);
@@ -438,8 +435,7 @@ grub_erofs_map_blocks_chunkmode (grub_fshelp_node_t node,
 }
 
 static grub_err_t
-grub_erofs_map_blocks (grub_fshelp_node_t node,
-		       struct grub_erofs_map_blocks *map)
+erofs_map_blocks (grub_fshelp_node_t node, struct grub_erofs_map_blocks *map)
 {
   if (map->m_la >= erofs_inode_file_size (node))
     {
@@ -450,39 +446,38 @@ grub_erofs_map_blocks (grub_fshelp_node_t node,
     }
 
   if (node->inode_datalayout != EROFS_INODE_CHUNK_BASED)
-    return grub_erofs_map_blocks_flatmode (node, map);
+    return erofs_map_blocks_flatmode (node, map);
   else
-    return grub_erofs_map_blocks_chunkmode (node, map);
+    return erofs_map_blocks_chunkmode (node, map);
 }
 
 static grub_err_t
-grub_erofs_read_raw_data (grub_fshelp_node_t node, char *buf, grub_off_t size,
-			  grub_off_t offset, grub_off_t *bytes)
+erofs_read_raw_data (grub_fshelp_node_t node, char *buf, grub_uint64_t size,
+		     grub_uint64_t offset, grub_uint64_t *bytes)
 {
-  struct grub_erofs_map_blocks map;
+  struct grub_erofs_map_blocks map = {0};
+  grub_uint64_t cur;
   grub_err_t err;
 
   if (bytes)
     *bytes = 0;
 
-  if (!node->inode_read)
+  if (!node->inode_loaded)
     {
-      err = grub_erofs_read_inode (node->data, node);
-      if (err)
+      err = erofs_read_inode (node->data, node);
+      if (err != GRUB_ERR_NONE)
 	return err;
     }
 
-  grub_memset (&map, 0, sizeof (map));
-
-  grub_off_t cur = offset;
+  cur = offset;
   while (cur < offset + size)
     {
       char *const estart = buf + cur - offset;
-      grub_off_t eend, moff = 0;
+      grub_uint64_t eend, moff = 0;
 
       map.m_la = cur;
-      err = grub_erofs_map_blocks (node, &map);
-      if (err)
+      err = erofs_map_blocks (node, &map);
+      if (err != GRUB_ERR_NONE)
 	return err;
 
       eend = grub_min (offset + size, map.m_la + map.m_llen);
@@ -498,9 +493,9 @@ grub_erofs_read_raw_data (grub_fshelp_node_t node, char *buf, grub_off_t size,
 
 	  /* Hole */
 	  grub_memset (estart, 0, eend - cur);
-	  cur = eend;
 	  if (bytes)
 	    *bytes += eend - cur;
+	  cur = eend;
 	  continue;
 	}
 
@@ -514,7 +509,7 @@ grub_erofs_read_raw_data (grub_fshelp_node_t node, char *buf, grub_off_t size,
 			    (map.m_pa + moff) >> GRUB_DISK_SECTOR_BITS,
 			    (map.m_pa + moff) & (GRUB_DISK_SECTOR_SIZE - 1),
 			    eend - map.m_la, estart);
-      if (err)
+      if (err != GRUB_ERR_NONE)
 	return err;
 
       if (bytes)
@@ -527,18 +522,18 @@ grub_erofs_read_raw_data (grub_fshelp_node_t node, char *buf, grub_off_t size,
 }
 
 static int
-grub_erofs_iterate_dir (grub_fshelp_node_t dir,
-			grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
+erofs_iterate_dir (grub_fshelp_node_t dir, grub_fshelp_iterate_dir_hook_t hook,
+		   void *hook_data)
 {
-  grub_off_t offset = 0, file_size;
-  grub_err_t err;
+  grub_uint64_t offset = 0, file_size;
   grub_uint32_t blocksz = erofs_blocksz (dir->data);
   char *buf;
+  grub_err_t err;
 
-  if (!dir->inode_read)
+  if (!dir->inode_loaded)
     {
-      err = grub_erofs_read_inode (dir->data, dir);
-      if (err)
+      err = erofs_read_inode (dir->data, dir);
+      if (err != GRUB_ERR_NONE)
 	return 0;
     }
 
@@ -552,30 +547,30 @@ grub_erofs_iterate_dir (grub_fshelp_node_t dir,
 
   while (offset < file_size)
     {
-      grub_off_t maxsize = grub_min (blocksz, file_size - offset);
+      grub_uint64_t maxsize = grub_min (blocksz, file_size - offset);
       struct grub_erofs_dirent *de = (void *) buf, *end;
       grub_uint16_t nameoff;
 
-      err = grub_erofs_read_raw_data (dir, buf, maxsize, offset, NULL);
-      if (err)
+      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 > blocksz)
+      if (nameoff < sizeof (struct grub_erofs_dirent) || nameoff > maxsize)
 	{
 	  grub_error (GRUB_ERR_BAD_FS,
-		      "invalid de[0].nameoff %u @ inode %" PRIuGRUB_UINT64_T,
+		      "invalid nameoff %u @ inode %" PRIuGRUB_UINT64_T,
 		      nameoff, dir->ino);
 	  goto not_found;
 	}
 
-      end = (struct grub_erofs_dirent *) ((char *) de + nameoff);
+      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];
-	  unsigned int de_namelen;
+	  grub_size_t de_namelen;
 	  const char *de_name;
 
 	  fdiro = grub_malloc (sizeof (struct grub_fshelp_node));
@@ -587,15 +582,32 @@ grub_erofs_iterate_dir (grub_fshelp_node_t dir,
 
 	  fdiro->data = dir->data;
 	  fdiro->ino = grub_le_to_cpu64 (de->nid);
-	  fdiro->inode_read = false;
+	  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);
+	      goto not_found;
+	    }
+
 	  de_name = 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);
+	      goto not_found;
+	    }
+
 	  grub_memcpy (filename, de_name, de_namelen);
 	  filename[de_namelen] = '\0';
 
@@ -617,7 +629,6 @@ grub_erofs_iterate_dir (grub_fshelp_node_t dir,
 	    case EROFS_FT_UNKNOWN:
 	    default:
 	      type = GRUB_FSHELP_UNKNOWN;
-	      break;
 	    }
 
 	  if (hook (filename, type, fdiro, hook_data))
@@ -638,15 +649,16 @@ not_found:
 }
 
 static char *
-grub_erofs_read_symlink (grub_fshelp_node_t node)
+erofs_read_symlink (grub_fshelp_node_t node)
 {
   char *symlink;
   grub_size_t sz;
+  grub_err_t err;
 
-  if (!node->inode_read)
+  if (!node->inode_loaded)
     {
-      grub_erofs_read_inode (node->data, node);
-      if (grub_errno)
+      err = erofs_read_inode (node->data, node);
+      if (err != GRUB_ERR_NONE)
 	return NULL;
     }
 
@@ -660,8 +672,8 @@ grub_erofs_read_symlink (grub_fshelp_node_t node)
   if (!symlink)
     return NULL;
 
-  grub_erofs_read_raw_data (node, symlink, sz - 1, 0, NULL);
-  if (grub_errno)
+  err = erofs_read_raw_data (node, symlink, sz - 1, 0, NULL);
+  if (err != GRUB_ERR_NONE)
     {
       grub_free (symlink);
       return NULL;
@@ -672,7 +684,7 @@ grub_erofs_read_symlink (grub_fshelp_node_t node)
 }
 
 static struct grub_erofs_data *
-grub_erofs_mount (grub_disk_t disk, bool read_root)
+erofs_mount (grub_disk_t disk, bool read_root)
 {
   struct grub_erofs_super sb;
   grub_err_t err;
@@ -683,9 +695,10 @@ grub_erofs_mount (grub_disk_t disk, bool read_root)
 			sizeof (sb), &sb);
   if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
     grub_error (GRUB_ERR_BAD_FS, "not a valid erofs filesystem");
-  if (err)
+  if (err != GRUB_ERR_NONE)
     return NULL;
-  if (sb.magic != grub_cpu_to_le32_compile_time (EROFS_MAGIC))
+  if (sb.magic != grub_cpu_to_le32_compile_time (EROFS_MAGIC) ||
+      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;
@@ -713,8 +726,8 @@ grub_erofs_mount (grub_disk_t disk, bool read_root)
     {
       data->inode.data = data;
       data->inode.ino = grub_le_to_cpu16 (sb.root_nid);
-      err = grub_erofs_read_inode (data, &data->inode);
-      if (err)
+      err = erofs_read_inode (data, &data->inode);
+      if (err != GRUB_ERR_NONE)
 	{
 	  grub_free (data);
 	  return NULL;
@@ -734,20 +747,19 @@ struct grub_erofs_dir_ctx
 
 /* Helper for grub_erofs_dir. */
 static int
-grub_erofs_dir_iter (const char *filename, enum grub_fshelp_filetype filetype,
+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;
+  struct grub_dirhook_info info = {0};
 
-  grub_memset (&info, 0, sizeof (info));
-  if (!node->inode_read)
+  if (!node->inode_loaded)
     {
-      grub_erofs_read_inode (ctx->data, node);
+      erofs_read_inode (ctx->data, node);
       grub_errno = GRUB_ERR_NONE;
     }
 
-  if (node->inode_read)
+  if (node->inode_loaded)
     {
       info.mtimeset = 1;
       info.mtime = erofs_inode_mtime (node);
@@ -763,21 +775,22 @@ 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,
+      .hook_data = hook_data
   };
 
-  ctx.data = grub_erofs_mount (device->disk, true);
+  ctx.data = erofs_mount (device->disk, true);
   if (!ctx.data)
     goto fail;
 
-  grub_fshelp_find_file (path, &ctx.data->inode, &fdiro, grub_erofs_iterate_dir,
-			 grub_erofs_read_symlink, GRUB_FSHELP_DIR);
-  if (grub_errno)
+  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;
 
-  grub_erofs_iterate_dir (fdiro, grub_erofs_dir_iter, &ctx);
+  erofs_iterate_dir (fdiro, erofs_dir_iter, &ctx);
 
  fail:
   if (fdiro != &ctx.data->inode)
@@ -794,23 +807,22 @@ grub_erofs_open (grub_file_t file, const char *name)
   struct grub_fshelp_node *fdiro = 0;
   grub_err_t err;
 
-  data = grub_erofs_mount (file->device->disk, true);
+  data = erofs_mount (file->device->disk, true);
   if (!data)
     {
       err = grub_errno;
       goto fail;
     }
 
-  err =
-      grub_fshelp_find_file (name, &data->inode, &fdiro, grub_erofs_iterate_dir,
-			     grub_erofs_read_symlink, GRUB_FSHELP_REG);
-  if (err)
+  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_read)
+  if (!fdiro->inode_loaded)
     {
-      err = grub_erofs_read_inode (data, fdiro);
-      if (err)
+      err = erofs_read_inode (data, fdiro);
+      if (err != GRUB_ERR_NONE)
 	goto fail;
     }
 
@@ -835,39 +847,40 @@ 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, ret = 0;
-  grub_off_t file_size;
+  grub_off_t off = file->offset;
+  grub_uint64_t ret = 0, file_size;
+  grub_err_t err;
 
-  if (!inode->inode_read)
+  if (!inode->inode_loaded)
     {
-      grub_erofs_read_inode (data, inode);
-      if (grub_errno)
+      err = erofs_read_inode (data, inode);
+      if (err != GRUB_ERR_NONE)
 	{
-	  ret = 0;
-	  grub_error (GRUB_ERR_IO, "cannot read @ inode %" PRIuGRUB_UINT64_T,
-		      inode->ino);
-	  goto end;
+	  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)
-    goto end;
+  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;
 
-  grub_erofs_read_raw_data (inode, buf, len, off, &ret);
-  if (grub_errno)
+  err = erofs_read_raw_data (inode, buf, len, off, &ret);
+  if (err != GRUB_ERR_NONE)
     {
-      ret = 0;
-      grub_error (GRUB_ERR_IO, "cannot read file @ inode %" PRIuGRUB_UINT64_T,
-		  inode->ino);
-      goto end;
+      grub_error (GRUB_ERR_IO, "cannot read file @ inode %" PRIuGRUB_UINT64_T, inode->ino);
+      return -1;
     }
 
-end:
   return ret;
 }
 
@@ -884,24 +897,18 @@ grub_erofs_uuid (grub_device_t device, char **uuid)
 {
   struct grub_erofs_data *data;
 
-  grub_errno = GRUB_ERR_NONE;
-  data = grub_erofs_mount (device->disk, false);
-
-  if (data)
-    *uuid = grub_xasprintf (
-	"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%"
-	"02x",
-	data->sb.uuid[0], data->sb.uuid[1], data->sb.uuid[2], data->sb.uuid[3],
-	data->sb.uuid[4], data->sb.uuid[5], data->sb.uuid[6], data->sb.uuid[7],
-	data->sb.uuid[8], data->sb.uuid[9], data->sb.uuid[10],
-	data->sb.uuid[11], data->sb.uuid[12], data->sb.uuid[13],
-	data->sb.uuid[14], data->sb.uuid[15]);
-  else
+  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_errno;
+  return GRUB_ERR_NONE;
 }
 
 static grub_err_t
@@ -909,18 +916,20 @@ grub_erofs_label (grub_device_t device, char **label)
 {
   struct grub_erofs_data *data;
 
-  grub_errno = GRUB_ERR_NONE;
-  data = grub_erofs_mount (device->disk, false);
-
-  if (data)
-    *label = grub_strndup ((char *) data->sb.volume_name,
-			   sizeof (data->sb.volume_name));
-  else
+  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_errno;
+  return GRUB_ERR_NONE;
 }
 
 static grub_err_t
@@ -928,17 +937,18 @@ grub_erofs_mtime (grub_device_t device, grub_int64_t *tm)
 {
   struct grub_erofs_data *data;
 
-  grub_errno = GRUB_ERR_NONE;
-  data = grub_erofs_mount (device->disk, false);
-
+  data = erofs_mount (device->disk, false);
   if (!data)
+    {
       *tm = 0;
-  else
+      return grub_errno;
+    }
+
   *tm = grub_le_to_cpu64 (data->sb.build_time);
 
   grub_free (data);
 
-  return grub_errno;
+  return GRUB_ERR_NONE;
 }
 
 static struct grub_fs grub_erofs_fs = {
-- 
2.44.0


_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v5 1/2] fs/erofs: Add support for EROFS
  2024-03-03 17:15 [PATCH v5 0/2] Introduce EROFS support Yifan Zhao
@ 2024-03-03 17:15 ` Yifan Zhao
  2024-03-06  2:18   ` Gao Xiang
  2024-04-18  8:16   ` Glenn Washburn
  2024-03-03 17:15 ` [PATCH v5 2/2] fs/erofs: Add tests for EROFS in grub-fs-tester Yifan Zhao
  2024-03-03 17:17 ` [PATCH v4~v2 Interdiff] Introduce EROFS support Yifan Zhao
  2 siblings, 2 replies; 14+ messages in thread
From: Yifan Zhao @ 2024-03-03 17:15 UTC (permalink / raw
  To: dkiper, development; +Cc: grub-devel, hsiangkao, zhaoyifan

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 <zhaoyifan@sjtu.edu.cn>
---
 INSTALL                     |   8 +-
 Makefile.util.def           |   1 +
 docs/grub.texi              |   3 +-
 grub-core/Makefile.core.def |   5 +
 grub-core/fs/erofs.c        | 978 ++++++++++++++++++++++++++++++++++++
 grub-core/kern/misc.c       |  14 +
 include/grub/misc.h         |   1 +
 7 files changed, 1005 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 1571421d7..5aaeaef0c 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -1438,6 +1438,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..34f16ba20
--- /dev/null
+++ b/grub-core/fs/erofs.c
@@ -0,0 +1,978 @@
+/* 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_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;
+  grub_uint64_t m_la;
+  grub_uint64_t m_plen;
+  grub_uint64_t m_llen;
+  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_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;
+
+  node->inode_type = (dic->i_format >> EROFS_I_VERSION_BIT) & EROFS_I_VERSION_MASKS;
+  node->inode_datalayout = (dic->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),
+	  (char *) 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 ? 1 : 0);
+
+  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, char *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)
+    {
+      char *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);
+  char *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);
+	      goto not_found;
+	    }
+
+	  de_name = 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);
+	      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, 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_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 = 0;
+  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, 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..ecb27fa5a 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 (*p && n--)
+    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

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v5 2/2] fs/erofs: Add tests for EROFS in grub-fs-tester
  2024-03-03 17:15 [PATCH v5 0/2] Introduce EROFS support Yifan Zhao
  2024-03-03 17:15 ` [PATCH v5 1/2] fs/erofs: Add support for EROFS Yifan Zhao
@ 2024-03-03 17:15 ` Yifan Zhao
  2024-04-18  8:19   ` Glenn Washburn
  2024-03-03 17:17 ` [PATCH v4~v2 Interdiff] Introduce EROFS support Yifan Zhao
  2 siblings, 1 reply; 14+ messages in thread
From: Yifan Zhao @ 2024-03-03 17:15 UTC (permalink / raw
  To: dkiper, development; +Cc: grub-devel, hsiangkao, zhaoyifan

In this patch, three tests of EROFS are introduced and they cover
compact, extended and chunk-based inodes, respectively.

Signed-off-by: Yifan Zhao <zhaoyifan@sjtu.edu.cn>
---
 .gitignore                   |  1 +
 Makefile.util.def            |  6 ++++++
 tests/erofs_test.in          | 20 ++++++++++++++++++++
 tests/util/grub-fs-tester.in | 32 +++++++++++++++++++++++++-------
 4 files changed, 52 insertions(+), 7 deletions(-)
 create mode 100644 tests/erofs_test.in

diff --git a/.gitignore b/.gitignore
index 4d0dfb700..9e8730471 100644
--- a/.gitignore
+++ b/.gitignore
@@ -105,6 +105,7 @@ widthspec.bin
 /docs/version-dev.texi
 /docs/version.texi
 /ehci_test
+/erofs_test
 /example_grub_script_test
 /example_scripted_test
 /example_unit_test
diff --git a/Makefile.util.def b/Makefile.util.def
index 8d3bc107f..0f74a1680 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -764,6 +764,12 @@ script = {
   dependencies = 'garbage-gen$(BUILD_EXEEXT)';
 };
 
+script = {
+  testcase = native;
+  name = erofs_test;
+  common = tests/erofs_test.in;
+};
+
 script = {
   testcase = native;
   name = ext234_test;
diff --git a/tests/erofs_test.in b/tests/erofs_test.in
new file mode 100644
index 000000000..51111627a
--- /dev/null
+++ b/tests/erofs_test.in
@@ -0,0 +1,20 @@
+#!@BUILD_SHEBANG@
+
+set -e
+
+if [ "x$EUID" = "x" ] ; then
+  EUID=`id -u`
+fi
+
+if [ "$EUID" != 0 ] ; then
+   exit 99
+fi
+
+if ! which mkfs.erofs >/dev/null 2>&1; then
+   echo "mkfs.erofs not installed; cannot test erofs."
+   exit 99
+fi
+
+"@builddir@/grub-fs-tester" erofs_compact
+"@builddir@/grub-fs-tester" erofs_extended
+"@builddir@/grub-fs-tester" erofs_chunk
diff --git a/tests/util/grub-fs-tester.in b/tests/util/grub-fs-tester.in
index ea8b2d1f6..df5dc7542 100644
--- a/tests/util/grub-fs-tester.in
+++ b/tests/util/grub-fs-tester.in
@@ -227,6 +227,10 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do
 	xsquash*)
 	    MINBLKSIZE=4096
 	    MAXBLKSIZE=1048576;;
+	x"erofs_"*)
+		MINBLKSIZE=4096
+		MAXBLKSIZE=4096
+		;;
 	xxfs|xf2fs)
 	    MINBLKSIZE=$SECSIZE
 	    # OS Limitation: GNU/Linux doesn't accept > 4096
@@ -382,8 +386,8 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do
 		    FSLABEL="g;/_é䏌䐓䏕䎛䎾䏴кит u"
 		    #FSLABEL="g;/_é莭莽😁кит u"
 		    ;;
-		# FS LIMITATION: reiserfs, extN and jfs label is at most 16 UTF-8 characters
-		x"reiserfs_old" | x"reiserfs" | x"ext"* | x"lvm"* | x"luks"* | x"mdraid"* | x"jfs" | x"jfs_caseins")
+		# FS LIMITATION: reiserfs, extN, jfs and erofs label is at most 16 UTF-8 characters
+		x"reiserfs_old" | x"reiserfs" | x"ext"* | x"lvm"* | x"luks"* | x"mdraid"* | x"jfs" | x"jfs_caseins" | x"erofs_"*)
 		    FSLABEL="g;/éт 莭😁";;
 		# FS LIMITATION: No underscore, space, semicolon, slash or international characters in UFS* in label. Limited to 32 UTF-8 characters
 		x"ufs1" | x"ufs1_sun" | x"ufs2")
@@ -661,7 +665,7 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do
 		x"tarfs" | x"cpio_"*| x"ziso9660" | x"romfs" | x"squash4_"*\
 		    | x"iso9660" | xjoliet | xrockridge | xrockridge_joliet \
 		    | x"iso9660_1999" | xjoliet_1999 | xrockridge_1999 \
-		    | xrockridge_joliet_1999)
+		    | xrockridge_joliet_1999 | x"erofs_"*)
 		    MNTPOINTRW="$MASTER"
 		    MNTPOINTRO="$MASTER"
 		    mkdir -p "$MASTER";;
@@ -805,7 +809,7 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do
 		    sleep 1
 		    "zfs" create "$FSLABEL"/"grub fs"
 		    sleep 1;;
-		x"tarfs" | x"cpio_"* | x"iso9660" | xjoliet | xrockridge | xrockridge_joliet | x"iso9660_1999" | xjoliet_1999 | xrockridge_1999 | xrockridge_joliet_1999 | x"ziso9660" | x"romfs" | x"squash4_"*)
+		x"tarfs" | x"cpio_"* | x"iso9660" | xjoliet | xrockridge | xrockridge_joliet | x"iso9660_1999" | xjoliet_1999 | xrockridge_1999 | xrockridge_joliet_1999 | x"ziso9660" | x"romfs" | x"squash4_"* | x"erofs_"*)
 		    INSTDEVICE=/dev/null;;
 		x"reiserfs")
 		    "mkfs.reiserfs" --format=3.6 -b $BLKSIZE -l "$FSLABEL" -q "${MOUNTDEVICE}" ;;
@@ -990,7 +994,7 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do
 		x"zfs"*)
 		    OSDIR="grub fs/"
 		    GRUBDIR="($GRUBDEVICE)/grub fs@";;
-		x"tarfs" | x"cpio_"* | x"iso9660" | xjoliet | xrockridge | xrockridge_joliet | x"iso9660_1999" | xjoliet_1999 | xrockridge_1999 | xrockridge_joliet_1999 | x"ziso9660" | x"romfs" | x"squash4_"* | xafs)
+		x"tarfs" | x"cpio_"* | x"iso9660" | xjoliet | xrockridge | xrockridge_joliet | x"iso9660_1999" | xjoliet_1999 | xrockridge_1999 | xrockridge_joliet_1999 | x"ziso9660" | x"romfs" | x"squash4_"* | xafs | x"erofs_"*)
 		    ;;
 		*)
 		    if ! mount -t "$MOUNTFS" "${MOUNTDEVICE}" "$MNTPOINTRW" -o ${MOUNTOPTS}${SELINUXOPTS}rw  ; then
@@ -1116,6 +1120,18 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do
 		xsquash4_*)
 		    echo mksquashfs "$MASTER" "${FSIMAGEP}0.img" -always-use-fragments -comp "${fs/squash4_/}" -b $BLKSIZE
 		    mksquashfs "$MASTER" "${FSIMAGEP}0.img" -always-use-fragments -comp "${fs/squash4_/}" -b $BLKSIZE ;;
+		x"erofs_compact")
+			echo mkfs.erofs -Eforce-inode-compact -L "$FSLABEL" "${FSIMAGEP}0.img" "$MNTPOINTRW"
+			mkfs.erofs -Eforce-inode-compact -L "$FSLABEL" "${FSIMAGEP}0.img" "$MNTPOINTRW"
+			;;
+		x"erofs_extended")
+			echo mkfs.erofs -Eforce-inode-extended -L "$FSLABEL" "${FSIMAGEP}0.img" "$MNTPOINTRW"
+			mkfs.erofs -Eforce-inode-extended -L "$FSLABEL" "${FSIMAGEP}0.img" "$MNTPOINTRW"
+			;;
+		x"erofs_chunk")
+			echo mkfs.erofs --chunksize=1048576 -L "$FSLABEL" "${FSIMAGEP}0.img" "$MNTPOINTRW"
+			mkfs.erofs --chunksize=1048576 -L "$FSLABEL" "${FSIMAGEP}0.img" "$MNTPOINTRW"
+			;;
 		x"bfs")
 		    sleep 1
 		    fusermount -u "$MNTPOINTRW"
@@ -1187,6 +1203,8 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do
 		    ;;
 		xsquash4_*)
 		    ;;
+		x"erofs_"*)
+		    ;;
 		xlvm*)
 		    vgchange -a y grub_test
 		    sleep 1
@@ -1624,7 +1642,7 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do
 			sleep 1;
 		    done
 		    sleep 5;;
-		x"tarfs" | x"cpio_"* | xrockridge | xjoliet | xrockridge_joliet | x"ziso9660" | x"romfs" | x"squash4_"* | xiso9660 | xiso9660_1999 | xrockridge_1999 | xjoliet_1999 | xrockridge_joliet_1999)
+		x"tarfs" | x"cpio_"* | xrockridge | xjoliet | xrockridge_joliet | x"ziso9660" | x"romfs" | x"squash4_"* | xiso9660 | xiso9660_1999 | xrockridge_1999 | xjoliet_1999 | xrockridge_joliet_1999 | x"erofs_"*)
 		    rm -rf "$MNTPOINTRW";;
 		x"afs")
 		    rm -rf "$MNTPOINTRO"
@@ -1651,7 +1669,7 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do
 		    ;;
 	    esac
 	    case x"$fs" in
-		x"tarfs" | x"cpio_"* | x"iso9660" | xrockridge | xjoliet | xrockridge_joliet | x"ziso9660" | x"romfs" | x"squash4_"* | x"iso9660_1999" | xrockridge_1999 | xjoliet_1999 | xrockridge_joliet_1999) ;;
+		x"tarfs" | x"cpio_"* | x"iso9660" | xrockridge | xjoliet | xrockridge_joliet | x"ziso9660" | x"romfs" | x"squash4_"* | x"iso9660_1999" | xrockridge_1999 | xjoliet_1999 | xrockridge_joliet_1999 | x"erofs_"*) ;;
 		*)
 		    for lodev in $LODEVICES; do
 			while ! losetup -d "$lodev"; do
-- 
2.44.0


_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v4~v2 Interdiff] Introduce EROFS support
  2024-03-03 17:15 [PATCH v5 0/2] Introduce EROFS support Yifan Zhao
  2024-03-03 17:15 ` [PATCH v5 1/2] fs/erofs: Add support for EROFS Yifan Zhao
  2024-03-03 17:15 ` [PATCH v5 2/2] fs/erofs: Add tests for EROFS in grub-fs-tester Yifan Zhao
@ 2024-03-03 17:17 ` Yifan Zhao
  2 siblings, 0 replies; 14+ messages in thread
From: Yifan Zhao @ 2024-03-03 17:17 UTC (permalink / raw
  To: development; +Cc: grub-devel, dkiper, hsiangkao, zhaoyifan

This interdiff shows the slight change between v4 and v2 for reference.

Interdiff against v2:
diff --git a/INSTALL b/INSTALL
index eb81314d1..545015ba2 100644
--- a/INSTALL
+++ b/INSTALL
@@ -83,7 +83,7 @@ Prerequisites for make-check:
     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, erofs_utils, e2fsprogs, exfat-utils, f2fs-tools,
+  - btrfs-progs, dosfstools, erofs-utils, e2fsprogs, 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
diff --git a/grub-core/fs/erofs.c b/grub-core/fs/erofs.c
index 1d44862b8..de57aaa5e 100644
--- a/grub-core/fs/erofs.c
+++ b/grub-core/fs/erofs.c
@@ -183,13 +183,7 @@ struct grub_erofs_dirent
   grub_uint8_t reserved;
 } GRUB_PACKED;
 
-enum
-{
-  BH_Meta,
-  BH_Mapped,
-};
-
-#define EROFS_MAP_MAPPED (1 << BH_Mapped)
+#define EROFS_MAP_MAPPED 0x02
 
 struct grub_erofs_map_blocks
 {
-- 
2.44.0


_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [PATCH v5 1/2] fs/erofs: Add support for EROFS
  2024-03-03 17:15 ` [PATCH v5 1/2] fs/erofs: Add support for EROFS Yifan Zhao
@ 2024-03-06  2:18   ` Gao Xiang
  2024-04-04 20:56     ` Daniel Kiper
  2024-04-18  8:16   ` Glenn Washburn
  1 sibling, 1 reply; 14+ messages in thread
From: Gao Xiang @ 2024-03-06  2:18 UTC (permalink / raw
  To: Yifan Zhao, development; +Cc: grub-devel, Daniel Kiper, Daniel Kiper



On 2024/3/4 01:15, Yifan Zhao wrote:
> 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 <zhaoyifan@sjtu.edu.cn>

After checking the v4->v5 diff, it still looks good to me,
Reviewed-by: Gao Xiang <hsiangkao@linux.alibaba.com>


BTW, Glenn, would you mind taking some time checking if
this version looks good to you? Thanks a lot!

Thanks,
Gao Xiang

_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v5 1/2] fs/erofs: Add support for EROFS
  2024-03-06  2:18   ` Gao Xiang
@ 2024-04-04 20:56     ` Daniel Kiper
  2024-04-10 11:23       ` Gao Xiang
  0 siblings, 1 reply; 14+ messages in thread
From: Daniel Kiper @ 2024-04-04 20:56 UTC (permalink / raw
  To: development; +Cc: grub-devel, Yifan Zhao, Gao Xiang

On Wed, Mar 06, 2024 at 10:18:21AM +0800, Gao Xiang wrote:
> On 2024/3/4 01:15, Yifan Zhao wrote:
> > 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 <zhaoyifan@sjtu.edu.cn>
>
> After checking the v4->v5 diff, it still looks good to me,
> Reviewed-by: Gao Xiang <hsiangkao@linux.alibaba.com>
>
> BTW, Glenn, would you mind taking some time checking if
> this version looks good to you? Thanks a lot!

Glenn, Do you have any comments on this version of EROFS patch set?

Daniel

_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v5 1/2] fs/erofs: Add support for EROFS
  2024-04-04 20:56     ` Daniel Kiper
@ 2024-04-10 11:23       ` Gao Xiang
  0 siblings, 0 replies; 14+ messages in thread
From: Gao Xiang @ 2024-04-10 11:23 UTC (permalink / raw
  To: Daniel Kiper, development; +Cc: grub-devel, Yifan Zhao, Gao Xiang

Hi Gleen, Daniel,

On 2024/4/5 04:56, Daniel Kiper wrote:
> On Wed, Mar 06, 2024 at 10:18:21AM +0800, Gao Xiang wrote:
>> On 2024/3/4 01:15, Yifan Zhao wrote:
>>> 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 <zhaoyifan@sjtu.edu.cn>
>>
>> After checking the v4->v5 diff, it still looks good to me,
>> Reviewed-by: Gao Xiang <hsiangkao@linux.alibaba.com>
>>
>> BTW, Glenn, would you mind taking some time checking if
>> this version looks good to you? Thanks a lot!
> 
> Glenn, Do you have any comments on this version of EROFS patch set?

Yeah, friendly ping.. Thanks in advance!


There is the diffs from the latest reviewed version (v2):

v2(last reviewed)->v4: https://lore.kernel.org/r/20240303171739.801243-1-zhaoyifan@sjtu.edu.cn
v4->v5: https://lore.kernel.org/r/20240303171558.800691-1-zhaoyifan@sjtu.edu.cn

Could we have some progress on uncompressed EROFS so that it can
be used for GRUB?  Then we will focus on upstreaming compression
support.

Thanks again!
Gao Xiang

> 
> Daniel

_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v5 1/2] fs/erofs: Add support for EROFS
  2024-03-03 17:15 ` [PATCH v5 1/2] fs/erofs: Add support for EROFS Yifan Zhao
  2024-03-06  2:18   ` Gao Xiang
@ 2024-04-18  8:16   ` Glenn Washburn
  2024-04-18 17:12     ` Yifan Zhao
  1 sibling, 1 reply; 14+ messages in thread
From: Glenn Washburn @ 2024-04-18  8:16 UTC (permalink / raw
  To: Yifan Zhao; +Cc: dkiper, grub-devel, hsiangkao

On Mon,  4 Mar 2024 01:15:54 +0800
Yifan Zhao <zhaoyifan@sjtu.edu.cn> wrote:

> 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 <zhaoyifan@sjtu.edu.cn>
> ---
>  INSTALL                     |   8 +-
>  Makefile.util.def           |   1 +
>  docs/grub.texi              |   3 +-
>  grub-core/Makefile.core.def |   5 +
>  grub-core/fs/erofs.c        | 978 ++++++++++++++++++++++++++++++++++++
>  grub-core/kern/misc.c       |  14 +
>  include/grub/misc.h         |   1 +
>  7 files changed, 1005 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 1571421d7..5aaeaef0c 100644
> --- a/grub-core/Makefile.core.def
> +++ b/grub-core/Makefile.core.def
> @@ -1438,6 +1438,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..34f16ba20
> --- /dev/null
> +++ b/grub-core/fs/erofs.c
> @@ -0,0 +1,978 @@
> +/* 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_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;
> +  grub_uint64_t m_la;
> +  grub_uint64_t m_plen;
> +  grub_uint64_t m_llen;
> +  grub_uint32_t m_flags;

Since the names have been shortened, it would be nice to have some
comments noting what they mean. I'm guessing that 'l' is short for
'logical', 'p' for 'physical', and 'a' for address.

> +};
> +
> +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_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;
> +
> +  node->inode_type = (dic->i_format >> EROFS_I_VERSION_BIT) & EROFS_I_VERSION_MASKS;
> +  node->inode_datalayout = (dic->i_format >> EROFS_I_DATALAYOUT_BIT) & EROFS_I_DATALAYOUT_MASKS;

The use of dic->i_format assumes endianness here, correct? Should
"grub_le_to_cpu16 (dic->i_format)" be used? Please double check the rest
of the code to make sure its endian agnostic. Does this pass the
filesystem tests on a big endian machine?

> +
> +  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),
> +	  (char *) dic + sizeof (struct grub_erofs_inode_compact));

Daniel mentioned in a previous review that grub_uint8_t would be better
than char. There are other casts that this applies to as well.

> +      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);

I would prefer explicit parenthesis here. It makes it more readable. In
general, I prefer not relying on operator precedence.

> +}
> +
> +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 ? 1 : 0);

Is this tertiary operator needed here? Or is it used to be very
explicit?

> +
> +  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, char *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)
> +    {
> +      char *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);
> +  char *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);
> +	      goto not_found;
> +	    }
> +
> +	  de_name = 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);
> +	      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, 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_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 = 0;

s/0/NULL/

Glenn

> +  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, 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..ecb27fa5a 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 (*p && n--)
> +    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'. */

_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v5 2/2] fs/erofs: Add tests for EROFS in grub-fs-tester
  2024-03-03 17:15 ` [PATCH v5 2/2] fs/erofs: Add tests for EROFS in grub-fs-tester Yifan Zhao
@ 2024-04-18  8:19   ` Glenn Washburn
  0 siblings, 0 replies; 14+ messages in thread
From: Glenn Washburn @ 2024-04-18  8:19 UTC (permalink / raw
  To: Yifan Zhao; +Cc: dkiper, grub-devel, hsiangkao

On Mon,  4 Mar 2024 01:15:55 +0800
Yifan Zhao <zhaoyifan@sjtu.edu.cn> wrote:

> In this patch, three tests of EROFS are introduced and they cover
> compact, extended and chunk-based inodes, respectively.
> 
> Signed-off-by: Yifan Zhao <zhaoyifan@sjtu.edu.cn>

Reviewed-by: Glenn Washburn <development@efficientek.com>

Glenn

> ---
>  .gitignore                   |  1 +
>  Makefile.util.def            |  6 ++++++
>  tests/erofs_test.in          | 20 ++++++++++++++++++++
>  tests/util/grub-fs-tester.in | 32 +++++++++++++++++++++++++-------
>  4 files changed, 52 insertions(+), 7 deletions(-)
>  create mode 100644 tests/erofs_test.in
> 
> diff --git a/.gitignore b/.gitignore
> index 4d0dfb700..9e8730471 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -105,6 +105,7 @@ widthspec.bin
>  /docs/version-dev.texi
>  /docs/version.texi
>  /ehci_test
> +/erofs_test
>  /example_grub_script_test
>  /example_scripted_test
>  /example_unit_test
> diff --git a/Makefile.util.def b/Makefile.util.def
> index 8d3bc107f..0f74a1680 100644
> --- a/Makefile.util.def
> +++ b/Makefile.util.def
> @@ -764,6 +764,12 @@ script = {
>    dependencies = 'garbage-gen$(BUILD_EXEEXT)';
>  };
>  
> +script = {
> +  testcase = native;
> +  name = erofs_test;
> +  common = tests/erofs_test.in;
> +};
> +
>  script = {
>    testcase = native;
>    name = ext234_test;
> diff --git a/tests/erofs_test.in b/tests/erofs_test.in
> new file mode 100644
> index 000000000..51111627a
> --- /dev/null
> +++ b/tests/erofs_test.in
> @@ -0,0 +1,20 @@
> +#!@BUILD_SHEBANG@
> +
> +set -e
> +
> +if [ "x$EUID" = "x" ] ; then
> +  EUID=`id -u`
> +fi
> +
> +if [ "$EUID" != 0 ] ; then
> +   exit 99
> +fi
> +
> +if ! which mkfs.erofs >/dev/null 2>&1; then
> +   echo "mkfs.erofs not installed; cannot test erofs."
> +   exit 99
> +fi
> +
> +"@builddir@/grub-fs-tester" erofs_compact
> +"@builddir@/grub-fs-tester" erofs_extended
> +"@builddir@/grub-fs-tester" erofs_chunk
> diff --git a/tests/util/grub-fs-tester.in b/tests/util/grub-fs-tester.in
> index ea8b2d1f6..df5dc7542 100644
> --- a/tests/util/grub-fs-tester.in
> +++ b/tests/util/grub-fs-tester.in
> @@ -227,6 +227,10 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do
>  	xsquash*)
>  	    MINBLKSIZE=4096
>  	    MAXBLKSIZE=1048576;;
> +	x"erofs_"*)
> +		MINBLKSIZE=4096
> +		MAXBLKSIZE=4096
> +		;;
>  	xxfs|xf2fs)
>  	    MINBLKSIZE=$SECSIZE
>  	    # OS Limitation: GNU/Linux doesn't accept > 4096
> @@ -382,8 +386,8 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do
>  		    FSLABEL="g;/_é䏌䐓䏕䎛䎾䏴кит u"
>  		    #FSLABEL="g;/_é莭莽😁кит u"
>  		    ;;
> -		# FS LIMITATION: reiserfs, extN and jfs label is at most 16 UTF-8 characters
> -		x"reiserfs_old" | x"reiserfs" | x"ext"* | x"lvm"* | x"luks"* | x"mdraid"* | x"jfs" | x"jfs_caseins")
> +		# FS LIMITATION: reiserfs, extN, jfs and erofs label is at most 16 UTF-8 characters
> +		x"reiserfs_old" | x"reiserfs" | x"ext"* | x"lvm"* | x"luks"* | x"mdraid"* | x"jfs" | x"jfs_caseins" | x"erofs_"*)
>  		    FSLABEL="g;/éт 莭😁";;
>  		# FS LIMITATION: No underscore, space, semicolon, slash or international characters in UFS* in label. Limited to 32 UTF-8 characters
>  		x"ufs1" | x"ufs1_sun" | x"ufs2")
> @@ -661,7 +665,7 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do
>  		x"tarfs" | x"cpio_"*| x"ziso9660" | x"romfs" | x"squash4_"*\
>  		    | x"iso9660" | xjoliet | xrockridge | xrockridge_joliet \
>  		    | x"iso9660_1999" | xjoliet_1999 | xrockridge_1999 \
> -		    | xrockridge_joliet_1999)
> +		    | xrockridge_joliet_1999 | x"erofs_"*)
>  		    MNTPOINTRW="$MASTER"
>  		    MNTPOINTRO="$MASTER"
>  		    mkdir -p "$MASTER";;
> @@ -805,7 +809,7 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do
>  		    sleep 1
>  		    "zfs" create "$FSLABEL"/"grub fs"
>  		    sleep 1;;
> -		x"tarfs" | x"cpio_"* | x"iso9660" | xjoliet | xrockridge | xrockridge_joliet | x"iso9660_1999" | xjoliet_1999 | xrockridge_1999 | xrockridge_joliet_1999 | x"ziso9660" | x"romfs" | x"squash4_"*)
> +		x"tarfs" | x"cpio_"* | x"iso9660" | xjoliet | xrockridge | xrockridge_joliet | x"iso9660_1999" | xjoliet_1999 | xrockridge_1999 | xrockridge_joliet_1999 | x"ziso9660" | x"romfs" | x"squash4_"* | x"erofs_"*)
>  		    INSTDEVICE=/dev/null;;
>  		x"reiserfs")
>  		    "mkfs.reiserfs" --format=3.6 -b $BLKSIZE -l "$FSLABEL" -q "${MOUNTDEVICE}" ;;
> @@ -990,7 +994,7 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do
>  		x"zfs"*)
>  		    OSDIR="grub fs/"
>  		    GRUBDIR="($GRUBDEVICE)/grub fs@";;
> -		x"tarfs" | x"cpio_"* | x"iso9660" | xjoliet | xrockridge | xrockridge_joliet | x"iso9660_1999" | xjoliet_1999 | xrockridge_1999 | xrockridge_joliet_1999 | x"ziso9660" | x"romfs" | x"squash4_"* | xafs)
> +		x"tarfs" | x"cpio_"* | x"iso9660" | xjoliet | xrockridge | xrockridge_joliet | x"iso9660_1999" | xjoliet_1999 | xrockridge_1999 | xrockridge_joliet_1999 | x"ziso9660" | x"romfs" | x"squash4_"* | xafs | x"erofs_"*)
>  		    ;;
>  		*)
>  		    if ! mount -t "$MOUNTFS" "${MOUNTDEVICE}" "$MNTPOINTRW" -o ${MOUNTOPTS}${SELINUXOPTS}rw  ; then
> @@ -1116,6 +1120,18 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do
>  		xsquash4_*)
>  		    echo mksquashfs "$MASTER" "${FSIMAGEP}0.img" -always-use-fragments -comp "${fs/squash4_/}" -b $BLKSIZE
>  		    mksquashfs "$MASTER" "${FSIMAGEP}0.img" -always-use-fragments -comp "${fs/squash4_/}" -b $BLKSIZE ;;
> +		x"erofs_compact")
> +			echo mkfs.erofs -Eforce-inode-compact -L "$FSLABEL" "${FSIMAGEP}0.img" "$MNTPOINTRW"
> +			mkfs.erofs -Eforce-inode-compact -L "$FSLABEL" "${FSIMAGEP}0.img" "$MNTPOINTRW"
> +			;;
> +		x"erofs_extended")
> +			echo mkfs.erofs -Eforce-inode-extended -L "$FSLABEL" "${FSIMAGEP}0.img" "$MNTPOINTRW"
> +			mkfs.erofs -Eforce-inode-extended -L "$FSLABEL" "${FSIMAGEP}0.img" "$MNTPOINTRW"
> +			;;
> +		x"erofs_chunk")
> +			echo mkfs.erofs --chunksize=1048576 -L "$FSLABEL" "${FSIMAGEP}0.img" "$MNTPOINTRW"
> +			mkfs.erofs --chunksize=1048576 -L "$FSLABEL" "${FSIMAGEP}0.img" "$MNTPOINTRW"
> +			;;
>  		x"bfs")
>  		    sleep 1
>  		    fusermount -u "$MNTPOINTRW"
> @@ -1187,6 +1203,8 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do
>  		    ;;
>  		xsquash4_*)
>  		    ;;
> +		x"erofs_"*)
> +		    ;;
>  		xlvm*)
>  		    vgchange -a y grub_test
>  		    sleep 1
> @@ -1624,7 +1642,7 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do
>  			sleep 1;
>  		    done
>  		    sleep 5;;
> -		x"tarfs" | x"cpio_"* | xrockridge | xjoliet | xrockridge_joliet | x"ziso9660" | x"romfs" | x"squash4_"* | xiso9660 | xiso9660_1999 | xrockridge_1999 | xjoliet_1999 | xrockridge_joliet_1999)
> +		x"tarfs" | x"cpio_"* | xrockridge | xjoliet | xrockridge_joliet | x"ziso9660" | x"romfs" | x"squash4_"* | xiso9660 | xiso9660_1999 | xrockridge_1999 | xjoliet_1999 | xrockridge_joliet_1999 | x"erofs_"*)
>  		    rm -rf "$MNTPOINTRW";;
>  		x"afs")
>  		    rm -rf "$MNTPOINTRO"
> @@ -1651,7 +1669,7 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do
>  		    ;;
>  	    esac
>  	    case x"$fs" in
> -		x"tarfs" | x"cpio_"* | x"iso9660" | xrockridge | xjoliet | xrockridge_joliet | x"ziso9660" | x"romfs" | x"squash4_"* | x"iso9660_1999" | xrockridge_1999 | xjoliet_1999 | xrockridge_joliet_1999) ;;
> +		x"tarfs" | x"cpio_"* | x"iso9660" | xrockridge | xjoliet | xrockridge_joliet | x"ziso9660" | x"romfs" | x"squash4_"* | x"iso9660_1999" | xrockridge_1999 | xjoliet_1999 | xrockridge_joliet_1999 | x"erofs_"*) ;;
>  		*)
>  		    for lodev in $LODEVICES; do
>  			while ! losetup -d "$lodev"; do

_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v5 1/2] fs/erofs: Add support for EROFS
  2024-04-18  8:16   ` Glenn Washburn
@ 2024-04-18 17:12     ` Yifan Zhao
  2024-04-22  3:15       ` Glenn Washburn
  0 siblings, 1 reply; 14+ messages in thread
From: Yifan Zhao @ 2024-04-18 17:12 UTC (permalink / raw
  To: development; +Cc: grub-devel, Daniel Kiper, Gao Xiang


On 2024/4/18 16:16, Glenn Washburn wrote:
> On Mon,  4 Mar 2024 01:15:54 +0800
> Yifan Zhao <zhaoyifan@sjtu.edu.cn> wrote:
>
>> 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 <zhaoyifan@sjtu.edu.cn>
>> ---
>>   INSTALL                     |   8 +-
>>   Makefile.util.def           |   1 +
>>   docs/grub.texi              |   3 +-
>>   grub-core/Makefile.core.def |   5 +
>>   grub-core/fs/erofs.c        | 978 ++++++++++++++++++++++++++++++++++++
>>   grub-core/kern/misc.c       |  14 +
>>   include/grub/misc.h         |   1 +
>>   7 files changed, 1005 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 1571421d7..5aaeaef0c 100644
>> --- a/grub-core/Makefile.core.def
>> +++ b/grub-core/Makefile.core.def
>> @@ -1438,6 +1438,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..34f16ba20
>> --- /dev/null
>> +++ b/grub-core/fs/erofs.c
>> @@ -0,0 +1,978 @@
>> +/* 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_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;
>> +  grub_uint64_t m_la;
>> +  grub_uint64_t m_plen;
>> +  grub_uint64_t m_llen;
>> +  grub_uint32_t m_flags;
> Since the names have been shortened, it would be nice to have some
> comments noting what they mean. I'm guessing that 'l' is short for
> 'logical', 'p' for 'physical', and 'a' for address.
This understanding is correct. Comments have been added.
>
>> +};
>> +
>> +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_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;
>> +
>> +  node->inode_type = (dic->i_format >> EROFS_I_VERSION_BIT) & EROFS_I_VERSION_MASKS;
>> +  node->inode_datalayout = (dic->i_format >> EROFS_I_DATALAYOUT_BIT) & EROFS_I_DATALAYOUT_MASKS;
> The use of dic->i_format assumes endianness here, correct? Should
> "grub_le_to_cpu16 (dic->i_format)" be used? Please double check the rest
> of the code to make sure its endian agnostic. Does this pass the
> filesystem tests on a big endian machine?
Thanks for pointing it out. Fixed and have checked the rest of code for 
endianness issues.
>
>> +
>> +  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),
>> +	  (char *) dic + sizeof (struct grub_erofs_inode_compact));
> Daniel mentioned in a previous review that grub_uint8_t would be better
> than char. There are other casts that this applies to as well.
Fixed, and this is the only 'char => grub_uint8_t' issue I've found so far.
>
>> +      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);
> I would prefer explicit parenthesis here. It makes it more readable. In
> general, I prefer not relying on operator precedence.
Fixed.
>
>> +}
>> +
>> +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 ? 1 : 0);
> Is this tertiary operator needed here? Or is it used to be very
> explicit?
Thanks. I think it's unnecessary to use tertiary operator here. Fixed.
>
>> +
>> +  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, char *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)
>> +    {
>> +      char *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);
>> +  char *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);
>> +	      goto not_found;
>> +	    }
>> +
>> +	  de_name = 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);
>> +	      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, 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_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 = 0;
> s/0/NULL/
>
> Glenn

Fixed.


Thanks,

Yifan Zhao

>> +  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, 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..ecb27fa5a 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 (*p && n--)
>> +    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'. */

_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v5 1/2] fs/erofs: Add support for EROFS
  2024-04-18 17:12     ` Yifan Zhao
@ 2024-04-22  3:15       ` Glenn Washburn
  2024-04-22  3:35         ` Yifan Zhao
  2024-04-22  4:20         ` Yifan Zhao
  0 siblings, 2 replies; 14+ messages in thread
From: Glenn Washburn @ 2024-04-22  3:15 UTC (permalink / raw
  To: Yifan Zhao; +Cc: grub-devel, Daniel Kiper, Gao Xiang

On Fri, 19 Apr 2024 01:12:40 +0800
Yifan Zhao <zhaoyifan@sjtu.edu.cn> wrote:

> 
> On 2024/4/18 16:16, Glenn Washburn wrote:
> > On Mon,  4 Mar 2024 01:15:54 +0800
> > Yifan Zhao <zhaoyifan@sjtu.edu.cn> wrote:
> >
> >> 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 <zhaoyifan@sjtu.edu.cn>
> >> ---
> >>   INSTALL                     |   8 +-
> >>   Makefile.util.def           |   1 +
> >>   docs/grub.texi              |   3 +-
> >>   grub-core/Makefile.core.def |   5 +
> >>   grub-core/fs/erofs.c        | 978 ++++++++++++++++++++++++++++++++++++
> >>   grub-core/kern/misc.c       |  14 +
> >>   include/grub/misc.h         |   1 +
> >>   7 files changed, 1005 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 1571421d7..5aaeaef0c 100644
> >> --- a/grub-core/Makefile.core.def
> >> +++ b/grub-core/Makefile.core.def
> >> @@ -1438,6 +1438,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..34f16ba20
> >> --- /dev/null
> >> +++ b/grub-core/fs/erofs.c
> >> @@ -0,0 +1,978 @@
> >> +/* 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_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;
> >> +  grub_uint64_t m_la;
> >> +  grub_uint64_t m_plen;
> >> +  grub_uint64_t m_llen;
> >> +  grub_uint32_t m_flags;
> > Since the names have been shortened, it would be nice to have some
> > comments noting what they mean. I'm guessing that 'l' is short for
> > 'logical', 'p' for 'physical', and 'a' for address.
> This understanding is correct. Comments have been added.
> >
> >> +};
> >> +
> >> +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_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;
> >> +
> >> +  node->inode_type = (dic->i_format >> EROFS_I_VERSION_BIT) & EROFS_I_VERSION_MASKS;
> >> +  node->inode_datalayout = (dic->i_format >> EROFS_I_DATALAYOUT_BIT) & EROFS_I_DATALAYOUT_MASKS;
> > The use of dic->i_format assumes endianness here, correct? Should
> > "grub_le_to_cpu16 (dic->i_format)" be used? Please double check the rest
> > of the code to make sure its endian agnostic. Does this pass the
> > filesystem tests on a big endian machine?
> Thanks for pointing it out. Fixed and have checked the rest of code for 
> endianness issues.
> >
> >> +
> >> +  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),
> >> +	  (char *) dic + sizeof (struct grub_erofs_inode_compact));
> > Daniel mentioned in a previous review that grub_uint8_t would be better
> > than char. There are other casts that this applies to as well.
> Fixed, and this is the only 'char => grub_uint8_t' issue I've found so far.

I've noted some others below.

> >
> >> +      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);
> > I would prefer explicit parenthesis here. It makes it more readable. In
> > general, I prefer not relying on operator precedence.
> Fixed.
> >
> >> +}
> >> +
> >> +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 ? 1 : 0);
> > Is this tertiary operator needed here? Or is it used to be very
> > explicit?
> Thanks. I think it's unnecessary to use tertiary operator here. Fixed.
> >
> >> +
> >> +  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, char *buf, grub_uint64_t size,

Seems like this should be a grub_uint8_t *.

> >> +		     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)
> >> +    {
> >> +      char *const estart = buf + cur - offset;

And grub_uint8_t * here too.

> >> +      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);
> >> +  char *buf;

And grub_uint8_t * here too.

> >> +  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);
> >> +	      goto not_found;
> >> +	    }
> >> +
> >> +	  de_name = 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);
> >> +	      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, 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_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 = 0;
> > s/0/NULL/
> >
> > Glenn
> 
> Fixed.
> 
> 
> Thanks,
> 
> Yifan Zhao
> 
> >> +  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)

And grub_uint8_t * here too.

These are all the ones I see.

Glenn

> >> +{
> >> +  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, 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..ecb27fa5a 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 (*p && n--)
> >> +    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'. */

_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v5 1/2] fs/erofs: Add support for EROFS
  2024-04-22  3:15       ` Glenn Washburn
@ 2024-04-22  3:35         ` Yifan Zhao
  2024-04-22  4:20         ` Yifan Zhao
  1 sibling, 0 replies; 14+ messages in thread
From: Yifan Zhao @ 2024-04-22  3:35 UTC (permalink / raw
  To: development; +Cc: grub-devel, dkiper, hsiangkao


On 2024/4/22 11:15, Glenn Washburn wrote:
> On Fri, 19 Apr 2024 01:12:40 +0800
> Yifan Zhao <zhaoyifan@sjtu.edu.cn> wrote:
>
>> On 2024/4/18 16:16, Glenn Washburn wrote:
>>> On Mon,  4 Mar 2024 01:15:54 +0800
>>> Yifan Zhao <zhaoyifan@sjtu.edu.cn> wrote:
>>>
>>>> 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 <zhaoyifan@sjtu.edu.cn>
>>>> ---
>>>>    INSTALL                     |   8 +-
>>>>    Makefile.util.def           |   1 +
>>>>    docs/grub.texi              |   3 +-
>>>>    grub-core/Makefile.core.def |   5 +
>>>>    grub-core/fs/erofs.c        | 978 ++++++++++++++++++++++++++++++++++++
>>>>    grub-core/kern/misc.c       |  14 +
>>>>    include/grub/misc.h         |   1 +
>>>>    7 files changed, 1005 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 1571421d7..5aaeaef0c 100644
>>>> --- a/grub-core/Makefile.core.def
>>>> +++ b/grub-core/Makefile.core.def
>>>> @@ -1438,6 +1438,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..34f16ba20
>>>> --- /dev/null
>>>> +++ b/grub-core/fs/erofs.c
>>>> @@ -0,0 +1,978 @@
>>>> +/* 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_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;
>>>> +  grub_uint64_t m_la;
>>>> +  grub_uint64_t m_plen;
>>>> +  grub_uint64_t m_llen;
>>>> +  grub_uint32_t m_flags;
>>> Since the names have been shortened, it would be nice to have some
>>> comments noting what they mean. I'm guessing that 'l' is short for
>>> 'logical', 'p' for 'physical', and 'a' for address.
>> This understanding is correct. Comments have been added.
>>>> +};
>>>> +
>>>> +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_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;
>>>> +
>>>> +  node->inode_type = (dic->i_format >> EROFS_I_VERSION_BIT) & EROFS_I_VERSION_MASKS;
>>>> +  node->inode_datalayout = (dic->i_format >> EROFS_I_DATALAYOUT_BIT) & EROFS_I_DATALAYOUT_MASKS;
>>> The use of dic->i_format assumes endianness here, correct? Should
>>> "grub_le_to_cpu16 (dic->i_format)" be used? Please double check the rest
>>> of the code to make sure its endian agnostic. Does this pass the
>>> filesystem tests on a big endian machine?
>> Thanks for pointing it out. Fixed and have checked the rest of code for
>> endianness issues.
>>>> +
>>>> +  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),
>>>> +	  (char *) dic + sizeof (struct grub_erofs_inode_compact));
>>> Daniel mentioned in a previous review that grub_uint8_t would be better
>>> than char. There are other casts that this applies to as well.
>> Fixed, and this is the only 'char => grub_uint8_t' issue I've found so far.
> I've noted some others below.

Thank you for pointing it out. Will fix them soon.


Yifan Zhao

>
>>>> +      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);
>>> I would prefer explicit parenthesis here. It makes it more readable. In
>>> general, I prefer not relying on operator precedence.
>> Fixed.
>>>> +}
>>>> +
>>>> +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 ? 1 : 0);
>>> Is this tertiary operator needed here? Or is it used to be very
>>> explicit?
>> Thanks. I think it's unnecessary to use tertiary operator here. Fixed.
>>>> +
>>>> +  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, char *buf, grub_uint64_t size,
> Seems like this should be a grub_uint8_t *.
>
>>>> +		     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)
>>>> +    {
>>>> +      char *const estart = buf + cur - offset;
> And grub_uint8_t * here too.
>
>>>> +      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);
>>>> +  char *buf;
> And grub_uint8_t * here too.
>
>>>> +  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);
>>>> +	      goto not_found;
>>>> +	    }
>>>> +
>>>> +	  de_name = 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);
>>>> +	      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, 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_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 = 0;
>>> s/0/NULL/
>>>
>>> Glenn
>> Fixed.
>>
>>
>> Thanks,
>>
>> Yifan Zhao
>>
>>>> +  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)
> And grub_uint8_t * here too.
>
> These are all the ones I see.
>
> Glenn
>
>>>> +{
>>>> +  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, 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..ecb27fa5a 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 (*p && n--)
>>>> +    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'. */

_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v5 1/2] fs/erofs: Add support for EROFS
  2024-04-22  3:15       ` Glenn Washburn
  2024-04-22  3:35         ` Yifan Zhao
@ 2024-04-22  4:20         ` Yifan Zhao
  2024-04-27  6:42           ` Glenn Washburn
  1 sibling, 1 reply; 14+ messages in thread
From: Yifan Zhao @ 2024-04-22  4:20 UTC (permalink / raw
  To: development; +Cc: grub-devel, dkiper, Gao Xiang


On 2024/4/22 11:15, Glenn Washburn wrote:
> On Fri, 19 Apr 2024 01:12:40 +0800
> Yifan Zhao <zhaoyifan@sjtu.edu.cn> wrote:
>
>> On 2024/4/18 16:16, Glenn Washburn wrote:
>>> On Mon,  4 Mar 2024 01:15:54 +0800
>>> Yifan Zhao <zhaoyifan@sjtu.edu.cn> wrote:
>>>
>>>> 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 <zhaoyifan@sjtu.edu.cn>
>>>> ---
>>>>    INSTALL                     |   8 +-
>>>>    Makefile.util.def           |   1 +
>>>>    docs/grub.texi              |   3 +-
>>>>    grub-core/Makefile.core.def |   5 +
>>>>    grub-core/fs/erofs.c        | 978 ++++++++++++++++++++++++++++++++++++
>>>>    grub-core/kern/misc.c       |  14 +
>>>>    include/grub/misc.h         |   1 +
>>>>    7 files changed, 1005 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 1571421d7..5aaeaef0c 100644
>>>> --- a/grub-core/Makefile.core.def
>>>> +++ b/grub-core/Makefile.core.def
>>>> @@ -1438,6 +1438,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..34f16ba20
>>>> --- /dev/null
>>>> +++ b/grub-core/fs/erofs.c
>>>> @@ -0,0 +1,978 @@
>>>> +/* 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_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;
>>>> +  grub_uint64_t m_la;
>>>> +  grub_uint64_t m_plen;
>>>> +  grub_uint64_t m_llen;
>>>> +  grub_uint32_t m_flags;
>>> Since the names have been shortened, it would be nice to have some
>>> comments noting what they mean. I'm guessing that 'l' is short for
>>> 'logical', 'p' for 'physical', and 'a' for address.
>> This understanding is correct. Comments have been added.
>>>> +};
>>>> +
>>>> +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_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;
>>>> +
>>>> +  node->inode_type = (dic->i_format >> EROFS_I_VERSION_BIT) & EROFS_I_VERSION_MASKS;
>>>> +  node->inode_datalayout = (dic->i_format >> EROFS_I_DATALAYOUT_BIT) & EROFS_I_DATALAYOUT_MASKS;
>>> The use of dic->i_format assumes endianness here, correct? Should
>>> "grub_le_to_cpu16 (dic->i_format)" be used? Please double check the rest
>>> of the code to make sure its endian agnostic. Does this pass the
>>> filesystem tests on a big endian machine?
>> Thanks for pointing it out. Fixed and have checked the rest of code for
>> endianness issues.
>>>> +
>>>> +  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),
>>>> +	  (char *) dic + sizeof (struct grub_erofs_inode_compact));
>>> Daniel mentioned in a previous review that grub_uint8_t would be better
>>> than char. There are other casts that this applies to as well.
>> Fixed, and this is the only 'char => grub_uint8_t' issue I've found so far.
> I've noted some others below.
>
>>>> +      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);
>>> I would prefer explicit parenthesis here. It makes it more readable. In
>>> general, I prefer not relying on operator precedence.
>> Fixed.
>>>> +}
>>>> +
>>>> +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 ? 1 : 0);
>>> Is this tertiary operator needed here? Or is it used to be very
>>> explicit?
>> Thanks. I think it's unnecessary to use tertiary operator here. Fixed.
>>>> +
>>>> +  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, char *buf, grub_uint64_t size,
> Seems like this should be a grub_uint8_t *.
>
>>>> +		     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)
>>>> +    {
>>>> +      char *const estart = buf + cur - offset;
> And grub_uint8_t * here too.
>
>>>> +      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);
>>>> +  char *buf;
> And grub_uint8_t * here too.
>
>>>> +  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);
>>>> +	      goto not_found;
>>>> +	    }
>>>> +
>>>> +	  de_name = 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);
>>>> +	      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, 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_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 = 0;
>>> s/0/NULL/
>>>
>>> Glenn
>> Fixed.
>>
>>
>> Thanks,
>>
>> Yifan Zhao
>>
>>>> +  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)
> And grub_uint8_t * here too.

One additional point. I don't think this could be changed as the 
interface declaration in `struct grub_fs` is `char*`.

Other modifications as required.


Thanks,

Yifan Zhao

>
> These are all the ones I see.
>
> Glenn
>
>>>> +{
>>>> +  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, 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..ecb27fa5a 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 (*p && n--)
>>>> +    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'. */

_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v5 1/2] fs/erofs: Add support for EROFS
  2024-04-22  4:20         ` Yifan Zhao
@ 2024-04-27  6:42           ` Glenn Washburn
  0 siblings, 0 replies; 14+ messages in thread
From: Glenn Washburn @ 2024-04-27  6:42 UTC (permalink / raw
  To: Yifan Zhao; +Cc: grub-devel, dkiper, Gao Xiang

On Mon, 22 Apr 2024 12:20:35 +0800
Yifan Zhao <zhaoyifan@sjtu.edu.cn> wrote:

> 
> On 2024/4/22 11:15, Glenn Washburn wrote:
> > On Fri, 19 Apr 2024 01:12:40 +0800
> > Yifan Zhao <zhaoyifan@sjtu.edu.cn> wrote:
> >
> >> On 2024/4/18 16:16, Glenn Washburn wrote:
> >>> On Mon,  4 Mar 2024 01:15:54 +0800
> >>> Yifan Zhao <zhaoyifan@sjtu.edu.cn> wrote:
> >>>
> >>>> 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 <zhaoyifan@sjtu.edu.cn>
> >>>> ---
> >>>>    INSTALL                     |   8 +-
> >>>>    Makefile.util.def           |   1 +
> >>>>    docs/grub.texi              |   3 +-
> >>>>    grub-core/Makefile.core.def |   5 +
> >>>>    grub-core/fs/erofs.c        | 978 ++++++++++++++++++++++++++++++++++++
> >>>>    grub-core/kern/misc.c       |  14 +
> >>>>    include/grub/misc.h         |   1 +
> >>>>    7 files changed, 1005 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 1571421d7..5aaeaef0c 100644
> >>>> --- a/grub-core/Makefile.core.def
> >>>> +++ b/grub-core/Makefile.core.def
> >>>> @@ -1438,6 +1438,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..34f16ba20
> >>>> --- /dev/null
> >>>> +++ b/grub-core/fs/erofs.c
> >>>> @@ -0,0 +1,978 @@
> >>>> +/* 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_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;
> >>>> +  grub_uint64_t m_la;
> >>>> +  grub_uint64_t m_plen;
> >>>> +  grub_uint64_t m_llen;
> >>>> +  grub_uint32_t m_flags;
> >>> Since the names have been shortened, it would be nice to have some
> >>> comments noting what they mean. I'm guessing that 'l' is short for
> >>> 'logical', 'p' for 'physical', and 'a' for address.
> >> This understanding is correct. Comments have been added.
> >>>> +};
> >>>> +
> >>>> +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_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;
> >>>> +
> >>>> +  node->inode_type = (dic->i_format >> EROFS_I_VERSION_BIT) & EROFS_I_VERSION_MASKS;
> >>>> +  node->inode_datalayout = (dic->i_format >> EROFS_I_DATALAYOUT_BIT) & EROFS_I_DATALAYOUT_MASKS;
> >>> The use of dic->i_format assumes endianness here, correct? Should
> >>> "grub_le_to_cpu16 (dic->i_format)" be used? Please double check the rest
> >>> of the code to make sure its endian agnostic. Does this pass the
> >>> filesystem tests on a big endian machine?
> >> Thanks for pointing it out. Fixed and have checked the rest of code for
> >> endianness issues.
> >>>> +
> >>>> +  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),
> >>>> +	  (char *) dic + sizeof (struct grub_erofs_inode_compact));
> >>> Daniel mentioned in a previous review that grub_uint8_t would be better
> >>> than char. There are other casts that this applies to as well.
> >> Fixed, and this is the only 'char => grub_uint8_t' issue I've found so far.
> > I've noted some others below.
> >
> >>>> +      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);
> >>> I would prefer explicit parenthesis here. It makes it more readable. In
> >>> general, I prefer not relying on operator precedence.
> >> Fixed.
> >>>> +}
> >>>> +
> >>>> +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 ? 1 : 0);
> >>> Is this tertiary operator needed here? Or is it used to be very
> >>> explicit?
> >> Thanks. I think it's unnecessary to use tertiary operator here. Fixed.
> >>>> +
> >>>> +  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, char *buf, grub_uint64_t size,
> > Seems like this should be a grub_uint8_t *.
> >
> >>>> +		     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)
> >>>> +    {
> >>>> +      char *const estart = buf + cur - offset;
> > And grub_uint8_t * here too.
> >
> >>>> +      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);
> >>>> +  char *buf;
> > And grub_uint8_t * here too.
> >
> >>>> +  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);
> >>>> +	      goto not_found;
> >>>> +	    }
> >>>> +
> >>>> +	  de_name = 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);
> >>>> +	      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, 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_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 = 0;
> >>> s/0/NULL/
> >>>
> >>> Glenn
> >> Fixed.
> >>
> >>
> >> Thanks,
> >>
> >> Yifan Zhao
> >>
> >>>> +  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)
> > And grub_uint8_t * here too.
> 
> One additional point. I don't think this could be changed as the 
> interface declaration in `struct grub_fs` is `char*`.

Good point, keep as is.

Glenn

> 
> Other modifications as required.
> 
> 
> Thanks,
> 
> Yifan Zhao
> 
> >
> > These are all the ones I see.
> >
> > Glenn
> >
> >>>> +{
> >>>> +  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, 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..ecb27fa5a 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 (*p && n--)
> >>>> +    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'. */

_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2024-04-27  6:43 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-03-03 17:15 [PATCH v5 0/2] Introduce EROFS support Yifan Zhao
2024-03-03 17:15 ` [PATCH v5 1/2] fs/erofs: Add support for EROFS Yifan Zhao
2024-03-06  2:18   ` Gao Xiang
2024-04-04 20:56     ` Daniel Kiper
2024-04-10 11:23       ` Gao Xiang
2024-04-18  8:16   ` Glenn Washburn
2024-04-18 17:12     ` Yifan Zhao
2024-04-22  3:15       ` Glenn Washburn
2024-04-22  3:35         ` Yifan Zhao
2024-04-22  4:20         ` Yifan Zhao
2024-04-27  6:42           ` Glenn Washburn
2024-03-03 17:15 ` [PATCH v5 2/2] fs/erofs: Add tests for EROFS in grub-fs-tester Yifan Zhao
2024-04-18  8:19   ` Glenn Washburn
2024-03-03 17:17 ` [PATCH v4~v2 Interdiff] Introduce EROFS support Yifan Zhao

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).