[PATCH] erofs-utils: fix superblock checksum for sub-page block filesystems

Gao Xiang hsiangkao at linux.alibaba.com
Wed Jun 25 16:46:48 AEST 2025


 - sb_csum isn't generated properly for 512- or 1024-byte filesystem block sizes.

 - Add `--no-sbcrc` option to fsck.erofs to bypass the sb_csum check.

Signed-off-by: Gao Xiang <hsiangkao at linux.alibaba.com>
---
 fsck/main.c              | 40 ++++++++-----------------------
 include/erofs/internal.h |  1 +
 lib/super.c              | 51 ++++++++++++++++++++++++++++++----------
 man/fsck.erofs.1         |  3 +++
 4 files changed, 52 insertions(+), 43 deletions(-)

diff --git a/fsck/main.c b/fsck/main.c
index cb4758b..91c193f 100644
--- a/fsck/main.c
+++ b/fsck/main.c
@@ -41,6 +41,7 @@ struct erofsfsck_cfg {
 	bool preserve_owner;
 	bool preserve_perms;
 	bool dump_xattrs;
+	bool nosbcrc;
 };
 static struct erofsfsck_cfg fsckcfg;
 
@@ -60,6 +61,7 @@ static struct option long_options[] = {
 	{"offset", required_argument, 0, 12},
 	{"xattrs", no_argument, 0, 13},
 	{"no-xattrs", no_argument, 0, 14},
+	{"no-sbcrc", no_argument, 0, 512},
 	{0, 0, 0, 0},
 };
 
@@ -110,6 +112,7 @@ static void usage(int argc, char **argv)
 		" --extract[=X]          check if all files are well encoded, optionally\n"
 		"                        extract to X\n"
 		" --offset=#             skip # bytes at the beginning of IMAGE\n"
+		" --no-sbcrc             bypass the superblock checksum verification\n"
 		" --[no-]xattrs          whether to dump extended attributes (default off)\n"
 		"\n"
 		" -a, -A, -y             no-op, for compatibility with fsck of other filesystems\n"
@@ -244,6 +247,9 @@ static int erofsfsck_parse_options_cfg(int argc, char **argv)
 		case 14:
 			fsckcfg.dump_xattrs = false;
 			break;
+		case 512:
+			fsckcfg.nosbcrc = true;
+			break;
 		default:
 			return -EINVAL;
 		}
@@ -322,35 +328,6 @@ static void erofsfsck_set_attributes(struct erofs_inode *inode, char *path)
 	}
 }
 
-static int erofs_check_sb_chksum(void)
-{
-#ifndef FUZZING
-	u8 buf[EROFS_MAX_BLOCK_SIZE];
-	u32 crc;
-	struct erofs_super_block *sb;
-	int ret;
-
-	ret = erofs_blk_read(&g_sbi, 0, buf, 0, 1);
-	if (ret) {
-		erofs_err("failed to read superblock to check checksum: %d",
-			  ret);
-		return -1;
-	}
-
-	sb = (struct erofs_super_block *)(buf + EROFS_SUPER_OFFSET);
-	sb->checksum = 0;
-
-	crc = erofs_crc32c(~0, (u8 *)sb, erofs_blksiz(&g_sbi) - EROFS_SUPER_OFFSET);
-	if (crc != g_sbi.checksum) {
-		erofs_err("superblock chksum doesn't match: saved(%08xh) calculated(%08xh)",
-			  g_sbi.checksum, crc);
-		fsckcfg.corrupted = true;
-		return -1;
-	}
-#endif
-	return 0;
-}
-
 static int erofs_verify_xattr(struct erofs_inode *inode)
 {
 	struct erofs_sb_info *sbi = inode->sbi;
@@ -1066,6 +1043,7 @@ int main(int argc, char *argv[])
 
 #ifdef FUZZING
 	cfg.c_dbg_lvl = -1;
+	fsckcfg.nosbcrc = true;
 #endif
 
 	err = erofs_dev_open(&g_sbi, cfg.c_img_path, O_RDONLY);
@@ -1080,7 +1058,9 @@ int main(int argc, char *argv[])
 		goto exit_dev_close;
 	}
 
-	if (erofs_sb_has_sb_chksum(&g_sbi) && erofs_check_sb_chksum()) {
+	if (!fsckcfg.nosbcrc && erofs_sb_has_sb_chksum(&g_sbi) &&
+	    erofs_superblock_csum_verify(&g_sbi)) {
+		fsckcfg.corrupted = true;
 		erofs_err("failed to verify superblock checksum");
 		goto exit_put_super;
 	}
diff --git a/include/erofs/internal.h b/include/erofs/internal.h
index bf3efb5..d380c45 100644
--- a/include/erofs/internal.h
+++ b/include/erofs/internal.h
@@ -419,6 +419,7 @@ struct erofs_buffer_head *erofs_reserve_sb(struct erofs_bufmgr *bmgr);
 int erofs_mkfs_init_devices(struct erofs_sb_info *sbi, unsigned int devices);
 int erofs_write_device_table(struct erofs_sb_info *sbi);
 int erofs_enable_sb_chksum(struct erofs_sb_info *sbi, u32 *crc);
+int erofs_superblock_csum_verify(struct erofs_sb_info *sbi);
 
 /* namei.c */
 int erofs_read_inode_from_disk(struct erofs_inode *vi);
diff --git a/lib/super.c b/lib/super.c
index 8efef50..5b90db5 100644
--- a/lib/super.c
+++ b/lib/super.c
@@ -259,19 +259,22 @@ int erofs_enable_sb_chksum(struct erofs_sb_info *sbi, u32 *crc)
 	unsigned int len;
 	struct erofs_super_block *sb;
 
-	ret = erofs_blk_read(sbi, 0, buf, 0, erofs_blknr(sbi, EROFS_SUPER_END) + 1);
+	/*
+	 * skip the first 1024 bytes, to allow for the installation
+	 * of x86 boot sectors and other oddities.
+	 */
+	if (erofs_blksiz(sbi) > EROFS_SUPER_OFFSET)
+		len = erofs_blksiz(sbi) - EROFS_SUPER_OFFSET;
+	else
+		len = erofs_blksiz(sbi);
+	ret = erofs_dev_read(sbi, 0, buf, EROFS_SUPER_OFFSET, len);
 	if (ret) {
 		erofs_err("failed to read superblock to set checksum: %s",
 			  erofs_strerror(ret));
 		return ret;
 	}
 
-	/*
-	 * skip the first 1024 bytes, to allow for the installation
-	 * of x86 boot sectors and other oddities.
-	 */
-	sb = (struct erofs_super_block *)(buf + EROFS_SUPER_OFFSET);
-
+	sb = (struct erofs_super_block *)buf;
 	if (le32_to_cpu(sb->magic) != EROFS_SUPER_MAGIC_V1) {
 		erofs_err("internal error: not an erofs valid image");
 		return -EFAULT;
@@ -280,25 +283,47 @@ int erofs_enable_sb_chksum(struct erofs_sb_info *sbi, u32 *crc)
 	/* turn on checksum feature */
 	sb->feature_compat = cpu_to_le32(le32_to_cpu(sb->feature_compat) |
 					 EROFS_FEATURE_COMPAT_SB_CHKSUM);
-	if (erofs_blksiz(sbi) > EROFS_SUPER_OFFSET)
-		len = erofs_blksiz(sbi) - EROFS_SUPER_OFFSET;
-	else
-		len = erofs_blksiz(sbi);
 	*crc = erofs_crc32c(~0, (u8 *)sb, len);
 
 	/* set up checksum field to erofs_super_block */
 	sb->checksum = cpu_to_le32(*crc);
 
-	ret = erofs_blk_write(sbi, buf, 0, 1);
+	ret = erofs_dev_write(sbi, buf, EROFS_SUPER_OFFSET, len);
 	if (ret) {
 		erofs_err("failed to write checksummed superblock: %s",
 			  erofs_strerror(ret));
 		return ret;
 	}
-
 	return 0;
 }
 
+int erofs_superblock_csum_verify(struct erofs_sb_info *sbi)
+{
+	u32 len = erofs_blksiz(sbi), crc;
+	u8 buf[EROFS_MAX_BLOCK_SIZE];
+	struct erofs_super_block *sb;
+	int ret;
+
+	if (len > EROFS_SUPER_OFFSET)
+		len -= EROFS_SUPER_OFFSET;
+	ret = erofs_dev_read(sbi, 0, buf, EROFS_SUPER_OFFSET, len);
+	if (ret) {
+		erofs_err("failed to read superblock to calculate sbcsum: %d",
+			  ret);
+		return -1;
+	}
+
+	sb = (struct erofs_super_block *)buf;
+	sb->checksum = 0;
+
+	crc = erofs_crc32c(~0, (u8 *)sb, len);
+	if (crc == sbi->checksum)
+		return 0;
+	erofs_err("invalid checksum 0x%08x, 0x%08x expected",
+		  sbi->checksum, crc);
+	return -EBADMSG;
+}
+
 int erofs_mkfs_init_devices(struct erofs_sb_info *sbi, unsigned int devices)
 {
 	struct erofs_buffer_head *bh;
diff --git a/man/fsck.erofs.1 b/man/fsck.erofs.1
index af0e6ab..fb255b4 100644
--- a/man/fsck.erofs.1
+++ b/man/fsck.erofs.1
@@ -34,6 +34,9 @@ take a long time depending on the image size.
 
 Optionally extract contents of the \fIIMAGE\fR to \fIdirectory\fR.
 .TP
+.B "--no-sbcrc"
+Bypass the on-disk superblock checksum verification.
+.TP
 .BI "--[no-]xattrs"
 Whether to dump extended attributes during extraction (default off).
 .TP
-- 
2.43.5



More information about the Linux-erofs mailing list