[PATCH-v5] erofs-utils:code for calculating crc checksum of erofs blocks

Gao Xiang hsiangkao at aol.com
Mon Oct 21 01:11:09 AEDT 2019


Hi Pratik and other folks,

On Sat, Oct 19, 2019 at 08:38:03PM +0530, Pratik Shinde wrote:
> Added code for calculating crc of erofs blocks (4K size).for now it calculates
> checksum of first block. but can modified to calculate crc for any no. of blocks
> 
> Fill 'feature_compat' field of erofs_super_block so that it
> can be used on kernel side. also fixing one typo.
> 
> Signed-off-by: Pratik Shinde <pratikshinde320 at gmail.com>

I will submit the following patch to experimental branch, some
TODOs in it (and an additional TODO which is not mentioned in code
is to move crc32c() to include/erofs/crc32.h).

Further updates or comments for this version are welcomed.

BTW, for all valid fields in feature_compat or feature_incompat,
I'd like to introduce a -O option to toggle them as well (e.g.
-O +sbcrc or -O -sbcrc), which is not in this version as well...

Thanks,
Gao Xiang

>From d96b797f86f2526e4f94a483d6b6442b53e61861 Mon Sep 17 00:00:00 2001
From: Pratik Shinde <pratikshinde320 at gmail.com>
Date: Sat, 19 Oct 2019 20:38:03 +0530
Subject: [PATCH v6] erofs-utils: code for calculating crc checksum of erofs
 blocks

Added code for calculating crc of erofs blocks (4K size).
For now it calculates checksum of first block. but it can
be modified to calculate crc for any no. of blocks

Fill 'feature_compat' field of erofs_super_block so that it
can be used on kernel side. also fixing one typo.

Signed-off-by: Pratik Shinde <pratikshinde320 at gmail.com>
[ Gao Xiang: minor updates based on Pratik patch. ]
Signed-off-by: Gao Xiang <hsiangkao at aol.com>
---
change since v5:
 - rename 'feature' to 'feature_compat' (so does related macro).
 - add some print messages;
 - fill all checksum fields in erofs_write_sb_checksum();
 - fix a memory leak in error handling path.

 include/erofs/internal.h |  1 +
 include/erofs/io.h       |  8 ++++
 include/erofs_fs.h       |  6 ++-
 lib/io.c                 | 27 ++++++++++++
 mkfs/main.c              | 92 ++++++++++++++++++++++++++++++++++++++++
 5 files changed, 132 insertions(+), 2 deletions(-)

diff --git a/include/erofs/internal.h b/include/erofs/internal.h
index 25ce7b5..9e2bb9c 100644
--- a/include/erofs/internal.h
+++ b/include/erofs/internal.h
@@ -52,6 +52,7 @@ struct erofs_sb_info {
 	erofs_blk_t meta_blkaddr;
 	erofs_blk_t xattr_blkaddr;
 
+	u32 feature_compat;
 	u32 feature_incompat;
 	u64 build_time;
 	u32 build_time_nsec;
diff --git a/include/erofs/io.h b/include/erofs/io.h
index 9775047..e0ca8d9 100644
--- a/include/erofs/io.h
+++ b/include/erofs/io.h
@@ -19,6 +19,7 @@
 int dev_open(const char *devname);
 void dev_close(void);
 int dev_write(const void *buf, u64 offset, size_t len);
+int dev_read(void *buf, u64 offset, size_t len);
 int dev_fillzero(u64 offset, size_t len, bool padding);
 int dev_fsync(void);
 int dev_resize(erofs_blk_t nblocks);
@@ -31,5 +32,12 @@ static inline int blk_write(const void *buf, erofs_blk_t blkaddr,
 			 blknr_to_addr(nblocks));
 }
 
+static inline int blk_read(void *buf, erofs_blk_t start,
+			    u32 nblocks)
+{
+	return dev_read(buf, blknr_to_addr(start),
+			 blknr_to_addr(nblocks));
+}
+
 #endif
 
diff --git a/include/erofs_fs.h b/include/erofs_fs.h
index f29aa25..8daf043 100644
--- a/include/erofs_fs.h
+++ b/include/erofs_fs.h
@@ -13,6 +13,8 @@
 #define EROFS_SUPER_MAGIC_V1    0xE0F5E1E2
 #define EROFS_SUPER_OFFSET      1024
 
+#define EROFS_FEATURE_COMPAT_SB_CHKSUM		0x00000001
+
 /*
  * Any bits that aren't in EROFS_ALL_FEATURE_INCOMPAT should
  * be incompatible with this kernel version.
@@ -39,8 +41,8 @@ struct erofs_super_block {
 	__u8 uuid[16];          /* 128-bit uuid for volume */
 	__u8 volume_name[16];   /* volume name */
 	__le32 feature_incompat;
-
-	__u8 reserved2[44];
+	__le32 chksum_blocks;	/* number of blocks used for checksum */
+	__u8 reserved2[40];
 };
 
 /*
diff --git a/lib/io.c b/lib/io.c
index 7f5f94d..52f9424 100644
--- a/lib/io.c
+++ b/lib/io.c
@@ -207,3 +207,30 @@ int dev_resize(unsigned int blocks)
 	return dev_fillzero(st.st_size, length, true);
 }
 
+int dev_read(void *buf, u64 offset, size_t len)
+{
+	int ret;
+
+	if (cfg.c_dry_run)
+		return 0;
+
+	if (!buf) {
+		erofs_err("buf is NULL");
+		return -EINVAL;
+	}
+	if (offset >= erofs_devsz || len > erofs_devsz ||
+	    offset > erofs_devsz - len) {
+		erofs_err("read posion[%" PRIu64 ", %zd] is too large beyond"
+			  "the end of device(%" PRIu64 ").",
+			  offset, len, erofs_devsz);
+		return -EINVAL;
+	}
+
+	ret = pread64(erofs_devfd, buf, len, (off64_t)offset);
+	if (ret != (int)len) {
+		erofs_err("Failed to read data from device - %s:[%" PRIu64 ", %zd].",
+			  erofs_devname, offset, len);
+		return -errno;
+	}
+	return 0;
+}
diff --git a/mkfs/main.c b/mkfs/main.c
index 71c81f5..fe84441 100644
--- a/mkfs/main.c
+++ b/mkfs/main.c
@@ -23,6 +23,9 @@
 
 #define EROFS_SUPER_END (EROFS_SUPER_OFFSET + sizeof(struct erofs_super_block))
 
+/* number of blocks for calculating checksum */
+#define EROFS_CKSUM_BLOCKS	1
+
 static void usage(void)
 {
 	fprintf(stderr, "usage: [options] FILE DIRECTORY\n\n");
@@ -87,6 +90,12 @@ static int parse_extended_opts(const char *opts)
 				return -EINVAL;
 			cfg.c_force_inodeversion = FORCE_INODE_EXTENDED;
 		}
+
+		if (MATCH_EXTENTED_OPT("nosbcrc", token, keylen)) {
+			if (vallen)
+				return -EINVAL;
+			sbi.feature_compat &= ~EROFS_FEATURE_COMPAT_SB_CHKSUM;
+		}
 	}
 	return 0;
 }
@@ -191,6 +200,8 @@ int erofs_mkfs_update_super_block(struct erofs_buffer_head *bh,
 		.meta_blkaddr  = sbi.meta_blkaddr,
 		.xattr_blkaddr = sbi.xattr_blkaddr,
 		.feature_incompat = cpu_to_le32(sbi.feature_incompat),
+		.feature_compat = cpu_to_le32(sbi.feature_compat &
+					      ~EROFS_FEATURE_COMPAT_SB_CHKSUM),
 	};
 	const unsigned int sb_blksize =
 		round_up(EROFS_SUPER_END, EROFS_BLKSIZ);
@@ -213,6 +224,83 @@ int erofs_mkfs_update_super_block(struct erofs_buffer_head *bh,
 	return 0;
 }
 
+#define CRCPOLY	0x82F63B78
+static inline u32 crc32c(u32 seed, const u8 *in, size_t len)
+{
+	int i;
+	u32 crc = seed;
+
+	while (len--) {
+		crc ^= *in++;
+		for (i = 0; i < 8; i++) {
+			crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY : 0);
+		}
+	}
+	return crc;
+}
+
+/* calculate checksum for first n blocks */
+static int erofs_calc_blk_checksum(erofs_blk_t nblks, u32 *crc)
+{
+	char *buf;
+	int err;
+
+	/* TODO: limit memory size by mutiple reads */
+	buf = malloc(nblks * EROFS_BLKSIZ);
+	if (!buf)
+		return -ENOMEM;
+
+	err = blk_read(buf, 0, nblks);
+	if (err)
+		goto out;
+
+	*crc = crc32c(0, (const u8 *)buf, nblks * EROFS_BLKSIZ);
+out:
+	free(buf);
+	return err;
+}
+
+static int erofs_write_sb_checksum(void)
+{
+	struct erofs_super_block *sb;
+	char buf[EROFS_BLKSIZ];
+	int ret;
+	u32 crc;
+
+	ret = erofs_calc_blk_checksum(EROFS_CKSUM_BLOCKS, &crc);
+	if (ret) {
+		erofs_err("failed to calculate checksum: %s",
+			  erofs_strerror(ret));
+		return ret;
+	}
+
+	/* TODO: no need to reread super block */
+	ret = blk_read(buf, 0, 1);
+	if (ret) {
+		erofs_err("failed to read superblock to fill checksum");
+		return -EIO;
+	}
+
+	sb = (struct erofs_super_block *)((u8 *)buf + EROFS_SUPER_OFFSET);
+	if (le32_to_cpu(sb->magic) != EROFS_SUPER_MAGIC_V1) {
+		erofs_err("internal error: not an erofs valid image");
+		return -EFAULT;
+	}
+	/* set up checksum field to erofs_super_block */
+	sb->feature_compat = cpu_to_le32(le32_to_cpu(sb->feature_compat) |
+					 EROFS_FEATURE_COMPAT_SB_CHKSUM);
+	sb->chksum_blocks = cpu_to_le32(EROFS_CKSUM_BLOCKS);
+	sb->checksum = cpu_to_le32(crc);
+	ret = blk_write(buf, 0, 1);
+	if (ret) {
+		erofs_err("failed to write checksumed superblock: %s",
+			  erofs_strerror(ret));
+		return ret;
+	}
+	erofs_info("superblock checksum 0x%08x written", crc);
+	return 0;
+}
+
 int main(int argc, char **argv)
 {
 	int err = 0;
@@ -228,6 +316,7 @@ int main(int argc, char **argv)
 
 	cfg.c_legacy_compress = false;
 	sbi.feature_incompat = EROFS_FEATURE_INCOMPAT_LZ4_0PADDING;
+	sbi.feature_compat = EROFS_FEATURE_COMPAT_SB_CHKSUM;
 
 	err = mkfs_parse_options_cfg(argc, argv);
 	if (err) {
@@ -310,6 +399,9 @@ int main(int argc, char **argv)
 		err = -EIO;
 	else
 		err = dev_resize(nblocks);
+
+	if (sbi.feature_compat & EROFS_FEATURE_COMPAT_SB_CHKSUM)
+		erofs_write_sb_checksum();
 exit:
 	z_erofs_compress_exit();
 	dev_close();
-- 
2.17.1




More information about the Linux-erofs mailing list