[PATCH] erofs-utils: introduce fsck.erofs

Gao Xiang xiang at kernel.org
Tue Oct 26 10:24:37 AEDT 2021


(Sorry due to just mutt+vim edit by mistake. try to send properly
 again..)

Hi Daeho,

On Mon, Oct 25, 2021 at 12:48:09PM -0700, Daeho Jeong wrote:
> From: Daeho Jeong <daehojeong at google.com>
> 
> I made a fsck.erofs tool to check erofs filesystem image integrity
> and calculate filesystem compression ratio.
> Here are options to support now.
> 
> fsck.erofs [options] IMAGE
> -V      print the version number of fsck.erofs and exit.
> -d#     set output message level to # (maximum 9)\n
> -c      print total compression ratio of all compressed files
> -C      print total compression ratio of all files
> 
> Signed-off-by: Daeho Jeong <daehojeong at google.com>

Many thanks for the patch! I also think some fsck feature is useful in
order to check the image integration.

IMO, Compression ratio calculation is more like a dump fs feature.
Wang Qi developed the erofsdump yet he told me that he is working for
another urgent stuffs for now so he delayed the work:
https://lore.kernel.org/r/20210915093537.2579575-1-guoxuenan@huawei.com

fsck and dump have the similar logic (and like f2fs-tools, we can also
have one program for this 2 features). If fsck is actively developed,
I'm pretty fine to use the fsck codebase and merge erofsdump into it
later instead.

Some comments as below:

> ---
>  Makefile.am              |   2 +-
>  configure.ac             |   3 +-
>  fsck/Makefile.am         |   9 +
>  fsck/main.c              | 548 +++++++++++++++++++++++++++++++++++++++
>  include/erofs/internal.h |   5 +
>  include/erofs_fs.h       |  13 +
>  lib/data.c               |   4 +-
>  lib/namei.c              |   2 +-
>  lib/super.c              |   1 +
>  mkfs/main.c              |  13 -
>  10 files changed, 582 insertions(+), 18 deletions(-)
>  create mode 100644 fsck/Makefile.am
>  create mode 100644 fsck/main.c
> 
> diff --git a/Makefile.am b/Makefile.am
> index 24e1d38..fc464e8 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -2,7 +2,7 @@
>  
>  ACLOCAL_AMFLAGS = -I m4
>  
> -SUBDIRS = man lib mkfs dump
> +SUBDIRS = man lib mkfs dump fsck
>  if ENABLE_FUSE
>  SUBDIRS += fuse
>  endif
> diff --git a/configure.ac b/configure.ac
> index b2c3225..5698b2e 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -298,5 +298,6 @@ AC_CONFIG_FILES([Makefile
>  		 lib/Makefile
>  		 mkfs/Makefile
>  		 dump/Makefile
> -		 fuse/Makefile])
> +		 fuse/Makefile
> +		 fsck/Makefile])
>  AC_OUTPUT
> diff --git a/fsck/Makefile.am b/fsck/Makefile.am
> new file mode 100644
> index 0000000..82973ba
> --- /dev/null
> +++ b/fsck/Makefile.am
> @@ -0,0 +1,9 @@
> +# SPDX-License-Identifier: GPL-2.0+
> +# Makefile.am
> +
> +AUTOMAKE_OPTIONS = foreign
> +bin_PROGRAMS     = fsck.erofs
> +AM_CPPFLAGS = ${libuuid_CFLAGS}
> +fsck_erofs_SOURCES = main.c
> +fsck_erofs_CFLAGS = -Wall -Werror -I$(top_srcdir)/include
> +fsck_erofs_LDADD = $(top_builddir)/lib/liberofs.la ${libuuid_LIBS} ${liblz4_LIBS}
> diff --git a/fsck/main.c b/fsck/main.c
> new file mode 100644
> index 0000000..c397d19
> --- /dev/null
> +++ b/fsck/main.c
> @@ -0,0 +1,548 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright 2021 Google LLC
> + * Author: Daeho Jeong <daehojeong at google.com>
> + */
> +#include <stdlib.h>
> +#include <getopt.h>
> +#include <time.h>
> +#include "erofs/print.h"
> +#include "erofs/io.h"
> +
> +enum {
> +	NO_PRINT_COMP_RATIO		= 0,
> +	PRINT_COMP_RATIO_ALL		= 1,
> +	PRINT_COMP_RATIO_COMP_FILE	= 2,
> +};
> +
> +static void erofs_check_inode(erofs_nid_t pnid, erofs_nid_t nid);
> +
> +struct erofsfsck_cfg {
> +	bool corrupted;
> +	int print_comp_ratio;
> +	u64 ondisk_len;
> +	u64 logical_len;
> +};
> +static struct erofsfsck_cfg fsckcfg;
> +
> +static struct option long_options[] = {
> +	{"help", no_argument, 0, 1},
> +	{0, 0, 0, 0},
> +};
> +
> +static void usage(void)
> +{
> +	fputs("usage: [options] IMAGE\n\n"
> +	      "Check erofs filesystem integrity of IMAGE, and [options] are:\n"
> +	      " -V      print the version number of fsck.erofs and exit.\n"
> +	      " -d#     set output message level to # (maximum 9)\n"
> +	      " -c      print total compression ratio of all compressed files\n"
> +	      " -C      print total compression ratio of all files\n"

Apart from erofsdump consideration.. Can we merge these 2 options into one?

> +	      " --help  display this help and exit.\n",
> +	      stderr);
> +}
> +
> +static void erofsfsck_print_version(void)
> +{
> +	fprintf(stderr, "fsck.erofs %s\n", cfg.c_version);
> +}
> +
> +static int erofsfsck_parse_options_cfg(int argc, char **argv)
> +{
> +	int opt, i;
> +
> +	while ((opt = getopt_long(argc, argv, "Vd:Cc",
> +				  long_options, NULL)) != -1) {
> +		switch (opt) {
> +		case 'V':
> +			erofsfsck_print_version();
> +			exit(0);
> +		case 'd':
> +			i = atoi(optarg);
> +			if (i < EROFS_MSG_MIN || i > EROFS_MSG_MAX) {
> +				erofs_err("invalid debug level %d", i);
> +				return -EINVAL;
> +			}
> +			cfg.c_dbg_lvl = i;
> +			break;
> +		case 'C':
> +			fsckcfg.print_comp_ratio = PRINT_COMP_RATIO_ALL;
> +			break;
> +		case 'c':
> +			fsckcfg.print_comp_ratio = PRINT_COMP_RATIO_COMP_FILE;
> +			break;
> +		case 1:
> +			usage();
> +			exit(0);
> +		default:
> +			return -EINVAL;
> +		}
> +	}
> +
> +	if (optind >= argc)
> +		return -EINVAL;
> +
> +	cfg.c_img_path = strdup(argv[optind++]);
> +	if (!cfg.c_img_path)
> +		return -ENOMEM;
> +
> +	if (optind < argc) {
> +		erofs_err("unexpected argument: %s\n", argv[optind]);
> +		return -EINVAL;
> +	}
> +	return 0;
> +}
> +
> +static int erofs_check_sb_chksum(void)
> +{
> +	int ret;
> +	u8 buf[EROFS_BLKSIZ];
> +	u32 crc;
> +	struct erofs_super_block *sb;
> +
> +	ret = blk_read(buf, 0, 1);
> +	if (ret) {
> +		erofs_err("failed to read superblock to check checksum: "
> +			  "errno(%d)", ret);
> +		return -1;
> +	}
> +
> +	sb = (struct erofs_super_block *)(buf + EROFS_SUPER_OFFSET);
> +	sb->checksum = 0;
> +
> +	crc = crc32c(~0, (u8 *)sb, EROFS_BLKSIZ - EROFS_SUPER_OFFSET);
> +	if (crc != sbi.checksum) {
> +		erofs_err("superblock checksum doesn't match: saved(0x%08x) "
> +			  "calculated(0x%08x)", sbi.checksum, crc);
> +		fsckcfg.corrupted = true;
> +		return -1;
> +	}
> +	return 0;
> +}
> +
> +static int check_special_dentries(struct erofs_dirent *de, const char *de_name,
> +				  unsigned int de_namelen, erofs_nid_t nid,
> +				  erofs_nid_t pnid, bool is_curdir)
> +{
> +	unsigned int dirname_len = is_curdir ? 1 : 2;
> +	const char *dirname = is_curdir ? "." : "..";
> +	erofs_nid_t correct_nid = is_curdir ? nid : pnid;
> +
> +	if (de_namelen != dirname_len || memcmp(de_name, dirname, de_namelen)) {
> +		char *dbgname = strndup(de_name, de_namelen);
> +
> +		BUG_ON(!dbgname);
> +		if (is_curdir)
> +			erofs_err("wrong current dir name(%s) @ nid(%llu)",
> +				  dbgname, nid | 0ULL);
> +		else
> +			erofs_err("wrong parent dir name(%s) @ nid(%llu)",
> +				  dbgname, nid | 0ULL);
> +		free(dbgname);
> +		return -1;
> +	}
> +
> +	if (de->nid != correct_nid) {
> +		if (is_curdir)
> +			erofs_err("wrong current dir nid(%llu) @ nid(%llu)",
> +				  de->nid | 0ULL, nid | 0ULL);
> +		else
> +			erofs_err("wrong parent dir nid(%llu): "
> +				  "pnid(%llu) @ nid(%llu)",
> +				  de->nid | 0ULL, pnid | 0ULL, nid | 0ULL);
> +		return -1;
> +	}
> +
> +	return 0;
> +}
> +
> +static int traverse_dirents(erofs_nid_t pnid, erofs_nid_t nid,
> +			    void *dentry_blk, erofs_off_t offset,
> +			    unsigned int next_nameoff, unsigned int maxsize)

Not quite sure if we could introduce some
erofs_foreach_dirent() and customized callbacks to cleanup similar functions
in fuse and fsck...

> +{
> +	struct erofs_dirent *de = dentry_blk;
> +	const struct erofs_dirent *end = dentry_blk + next_nameoff;
> +	unsigned int idx = 0;
> +
> +	erofs_dbg("traversing pnid(%llu), nid(%llu)", pnid | 0ULL, nid | 0ULL);
> +
> +	if (offset == 0 && (next_nameoff < 2 * sizeof(struct erofs_dirent))) {
> +		erofs_err("too small dirents of size(%d) in nid(%llu)",
> +			  next_nameoff, nid | 0ULL);
> +		return -EFSCORRUPTED;
> +	}
> +
> +	while (de < end) {
> +		const char *de_name;
> +		unsigned int de_namelen;
> +		unsigned int nameoff;
> +		char *dbgname;
> +
> +		nameoff = le16_to_cpu(de->nameoff);
> +		de_name = (char *)dentry_blk + nameoff;
> +
> +		/* the last dirent check */
> +		if (de + 1 >= end)
> +			de_namelen = strnlen(de_name, maxsize - nameoff);
> +		else
> +			de_namelen = le16_to_cpu(de[1].nameoff) - nameoff;
> +
> +		if (cfg.c_dbg_lvl >= EROFS_DBG) {
> +			dbgname = strndup(de_name, de_namelen);
> +			BUG_ON(!dbgname);
> +			erofs_dbg("traversed filename(%s)", dbgname);
> +			free(dbgname);
> +		}
> +
> +		/* corrupted entry check */
> +		if (nameoff != next_nameoff) {
> +			erofs_err("bogus dirent with nameoff(%u): expected(%d) "
> +				  "@ nid(%llu), offset(%llu), idx(%u)",
> +				  nameoff, next_nameoff, nid | 0ULL,
> +				  offset | 0ULL, idx);
> +			return -EFSCORRUPTED;
> +		}
> +
> +		if (nameoff + de_namelen > maxsize ||
> +				de_namelen > EROFS_NAME_LEN) {
> +			erofs_err("bogus dirent with namelen(%u) @ nid(%llu), "
> +				  "offset(%llu), idx(%u)",
> +				  de_namelen, nid | 0ULL, offset | 0ULL, idx);
> +			return -EFSCORRUPTED;
> +		}
> +
> +		if (offset == 0 && (idx == 0 || idx == 1)) {

We may not assume "." and ".." special dirents as 0 or 1... Actually
all dirents are sorted in the alphabetical order. I'm not sure if
some dirents are smaller than "." and "..". 

We could also check if all dirents are in alphabetical order as well
since it's used to do binary search runtimely.

> +			if (check_special_dentries(de, de_name, de_namelen, nid,
> +						   pnid, idx == 0))
> +				return -EFSCORRUPTED;
> +		} else {
> +			erofs_check_inode(nid, de->nid);
> +			if (fsckcfg.corrupted)
> +				return -EFSCORRUPTED;
> +		}
> +
> +		next_nameoff += de_namelen;
> +		++de;
> +		++idx;
> +	}
> +
> +	erofs_dbg("traversing ... done nid(%llu)", nid | 0ULL);
> +	return 0;
> +}
> +
> +static int verify_raw_data_chunk(struct erofs_inode *inode)
> +{
> +	struct erofs_map_blocks map = {
> +		.index = UINT_MAX,
> +	};
> +	int ret;
> +	erofs_off_t ptr = 0;
> +
> +	if (fsckcfg.print_comp_ratio == PRINT_COMP_RATIO_ALL) {
> +		fsckcfg.logical_len += inode->i_size;
> +		fsckcfg.ondisk_len += inode->i_size;
> +	}
> +
> +	while (ptr < inode->i_size) {
> +		map.m_la = ptr;
> +		ret = erofs_map_blocks(inode, &map, 0);
> +		if (ret)
> +			return ret;
> +
> +		if (map.m_plen != map.m_llen || ptr != map.m_la) {
> +			erofs_err("broken data chunk layout m_la %" PRIu64
> +				  " ptr %" PRIu64 " m_llen %" PRIu64 " m_plen %"
> +				  PRIu64, map.m_la, ptr, map.m_llen,
> +				  map.m_plen);
> +			return -EFSCORRUPTED;
> +		}
> +
> +		if (!(map.m_flags & EROFS_MAP_MAPPED) && !map.m_llen) {
> +			/* readched EOF */
> +			ptr = inode->i_size;
> +			continue;
> +		}
> +
> +		ptr += map.m_llen;
> +	}
> +	return 0;
> +}
> +
> +static int verify_compressed_chunk(struct erofs_inode *inode)
> +{
> +	struct erofs_map_blocks map = {
> +		.index = UINT_MAX,
> +	};
> +	int ret = 0;
> +	bool count_pchunk = fsckcfg.print_comp_ratio != NO_PRINT_COMP_RATIO;
> +	u64 pchunk_len = 0;
> +	erofs_off_t offset = 0, end = inode->i_size;
> +
> +	while (end > offset) {
> +		map.m_la = end - 1;
> +
> +		ret = z_erofs_map_blocks_iter(inode, &map);
> +		if (ret)
> +			return ret;
> +
> +		if (end > map.m_la + map.m_llen) {
> +			erofs_err("broken compressed chunk layout m_la %" PRIu64
> +				  " m_llen %" PRIu64 " end %" PRIu64,
> +				  map.m_la, map.m_llen, end);
> +			return -EFSCORRUPTED;
> +		}

IMO, A simple way to check this is to try to decompress by calling 
z_erofs_decompress() with .partial_decoding == 0 and see if any
error when decompressing as well.

> +
> +		if (count_pchunk)
> +			pchunk_len += map.m_plen;
> +
> +		end = map.m_la;
> +	}
> +
> +	if (count_pchunk) {
> +		fsckcfg.logical_len += inode->i_size;
> +		fsckcfg.ondisk_len += pchunk_len;

we can also check another field called compressed_blocks in on-disk
inode...

> +	}
> +
> +	return 0;
> +}
> +
> +static int erofs_verify_xattr(struct erofs_inode *inode)
> +{
> +	unsigned int xattr_hdr_size = sizeof(struct erofs_xattr_ibody_header);
> +	unsigned int xattr_entry_size = sizeof(struct erofs_xattr_entry);
> +	erofs_off_t addr;
> +	unsigned int ofs, xattr_shared_count;
> +	struct erofs_xattr_ibody_header *ih;
> +	struct erofs_xattr_entry *entry;
> +	int i, remaining = inode->xattr_isize, ret = 0;
> +	char *buf = calloc(EROFS_BLKSIZ, 1);
> +
> +	BUG_ON(!buf);
> +
> +	if (inode->xattr_isize == xattr_hdr_size) {
> +		erofs_err("xattr_isize %d of nid %llu is not supported yet",
> +			  inode->xattr_isize, inode->nid | 0ULL);
> +		ret = -EFSCORRUPTED;
> +		goto out;
> +	} else if (inode->xattr_isize < xattr_hdr_size) {
> +		if (inode->xattr_isize) {
> +			erofs_err("bogus xattr ibody @ nid %llu",
> +				  inode->nid | 0ULL);
> +			ret = -EFSCORRUPTED;
> +			goto out;
> +		}
> +	}
> +
> +	addr = iloc(inode->nid) + inode->inode_isize;
> +	ret = dev_read(buf, addr, xattr_hdr_size);
> +	if (ret < 0) {
> +		erofs_err("an error occurred when reading xattr header "
> +			  "of nid(%llu): errno(%d)", inode->nid | 0ULL, ret);
> +		goto out;
> +	}
> +	ih = (struct erofs_xattr_ibody_header *)buf;
> +	xattr_shared_count = ih->h_shared_count;
> +
> +	ofs = erofs_blkoff(addr) + xattr_hdr_size;
> +	addr += xattr_hdr_size;
> +	remaining -= xattr_hdr_size;
> +	for (i = 0; i < xattr_shared_count; ++i) {
> +		if (ofs >= EROFS_BLKSIZ) {
> +			if (ofs != EROFS_BLKSIZ) {
> +				erofs_err("unaligned xattr entry in "
> +					  "xattr shared area of nid(%llu)",
> +					  inode->nid | 0ULL);
> +				ret = -EFSCORRUPTED;

IMO, it won't happen due to proper alignment.. but I'm fine as this too.

Thanks,
Gao Xiang

> +				goto out;
> +			}
> +			ofs = 0;
> +		}
> +		ofs += xattr_entry_size;
> +		addr += xattr_entry_size;
> +		remaining -= xattr_entry_size;
> +	}
> +
> +	while (remaining > 0) {
> +		unsigned int entry_sz;
> +
> +		ret = dev_read(buf, addr, xattr_entry_size);
> +		if (ret) {
> +			erofs_err("an error occurred when reading xattr entry "
> +				  "of nid(%llu): errno(%d)",
> +				  inode->nid | 0ULL, ret);
> +			goto out;
> +		}
> +
> +		entry = (struct erofs_xattr_entry *)buf;
> +		entry_sz = erofs_xattr_entry_size(entry);
> +		if (remaining < entry_sz) {
> +			erofs_err("xattr on-disk corruption: xattr entry "
> +				  "beyond xattr_isize of nid(%llu)",
> +				  inode->nid | 0ULL);
> +			ret = -EFSCORRUPTED;
> +			goto out;
> +		}
> +		addr += entry_sz;
> +		remaining -= entry_sz;
> +	}
> +out:
> +	free(buf);
> +	return ret;
> +}
> +
> +static int erofs_verify_data_chunk(struct erofs_inode *inode)
> +{
> +	int ret;
> +
> +	erofs_dbg("verify data chunk of nid(%llu): type(%d)",
> +		  inode->nid | 0ULL, inode->datalayout);
> +
> +	switch (inode->datalayout) {
> +	case EROFS_INODE_FLAT_PLAIN:
> +	case EROFS_INODE_FLAT_INLINE:
> +	case EROFS_INODE_CHUNK_BASED:
> +		ret = verify_raw_data_chunk(inode);
> +		break;
> +	case EROFS_INODE_FLAT_COMPRESSION_LEGACY:
> +	case EROFS_INODE_FLAT_COMPRESSION:
> +		ret = verify_compressed_chunk(inode);
> +		break;
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	if (ret == -EIO)
> +		erofs_err("I/O error occurred when verifying "
> +			  "data chunk of nid(%llu)", inode->nid | 0ULL);
> +
> +	return ret;
> +}
> +
> +static void erofs_check_inode(erofs_nid_t pnid, erofs_nid_t nid)
> +{
> +	int ret;
> +	struct erofs_inode *inode;
> +	char *buf;
> +	erofs_off_t offset;
> +
> +	erofs_dbg("check inode: nid(%llu)", nid | 0ULL);
> +	inode = calloc(1, sizeof(struct erofs_inode));
> +	BUG_ON(!inode);
> +	buf = calloc(EROFS_BLKSIZ, 1);
> +	BUG_ON(!buf);
> +
> +	inode->nid = nid;
> +	ret = erofs_read_inode_from_disk(inode);
> +	if (ret) {
> +		if (ret == -EIO)
> +			erofs_err("I/O error occurred when reading nid(%llu)",
> +				  nid | 0ULL);
> +		goto out;
> +	}
> +
> +	/* verify xattr field */
> +	ret = erofs_verify_xattr(inode);
> +	if (ret)
> +		goto out;
> +
> +	/* verify data chunk layout */
> +	ret = erofs_verify_data_chunk(inode);
> +	if (ret)
> +		goto out;
> +
> +	if ((inode->i_mode & S_IFMT) != S_IFDIR)
> +		goto out;
> +
> +	offset = 0;
> +	while (offset < inode->i_size) {
> +		erofs_off_t maxsize = min_t(erofs_off_t,
> +					inode->i_size - offset, EROFS_BLKSIZ);
> +		struct erofs_dirent *de = (void *)buf;
> +		unsigned int nameoff;
> +
> +		ret = erofs_pread(inode, buf, maxsize, offset);
> +		if (ret) {
> +			erofs_err("I/O error occurred when reading dirents: "
> +				  "nid(%llu), offset(%llu), size(%llu)",
> +				  nid | 0ULL, offset | 0ULL, maxsize | 0ULL);
> +			goto out;
> +		}
> +
> +		nameoff = le16_to_cpu(de->nameoff);
> +		if (nameoff < sizeof(struct erofs_dirent) ||
> +				nameoff >= PAGE_SIZE) {
> +			erofs_err("invalid de[0].nameoff %u @ nid(%llu), "
> +				  "offset(%llu)",
> +				  nameoff, nid | 0ULL, offset | 0ULL);
> +			ret = -EFSCORRUPTED;
> +			goto out;
> +		}
> +
> +		ret = traverse_dirents(pnid, nid, buf, offset,
> +				       nameoff, maxsize);
> +		if (ret)
> +			goto out;
> +
> +		offset += maxsize;
> +	}
> +out:
> +	free(buf);
> +	free(inode);
> +	if (ret && ret != -EIO)
> +		fsckcfg.corrupted = true;
> +}
> +
> +int main(int argc, char **argv)
> +{
> +	int err;
> +
> +	erofs_init_configure();
> +
> +	fsckcfg.corrupted = false;
> +	fsckcfg.print_comp_ratio = NO_PRINT_COMP_RATIO;
> +	fsckcfg.logical_len = 0;
> +	fsckcfg.ondisk_len = 0;
> +
> +	err = erofsfsck_parse_options_cfg(argc, argv);
> +	if (err) {
> +		if (err == -EINVAL)
> +			usage();
> +		goto exit;
> +	}
> +
> +	err = dev_open_ro(cfg.c_img_path);
> +	if (err) {
> +		erofs_err("failed to open image file");
> +		goto exit;
> +	}
> +
> +	err = erofs_read_superblock();
> +	if (err) {
> +		erofs_err("failed to read superblock");
> +		goto exit;
> +	}
> +
> +	if (erofs_sb_has_sb_chksum() && erofs_check_sb_chksum()) {
> +		erofs_err("failed to verify superblock checksum");
> +		goto exit;
> +	}
> +
> +	erofs_check_inode(sbi.root_nid, sbi.root_nid);
> +
> +	if (fsckcfg.corrupted) {
> +		fprintf(stderr, "Found some filesystem corruption\n");
> +	} else {
> +		fprintf(stderr, "No error found\n");
> +		if (fsckcfg.print_comp_ratio != NO_PRINT_COMP_RATIO) {
> +			double comp_ratio = (double)fsckcfg.ondisk_len * 100 /
> +					    (double)fsckcfg.logical_len;
> +			fprintf(stderr, "Compression Ratio: %.2f(%%)\n",
> +				comp_ratio);
> +		}
> +	}
> +
> +exit:
> +	erofs_exit_configure();
> +	return err;
> +}
> diff --git a/include/erofs/internal.h b/include/erofs/internal.h
> index 8b154ed..80065b2 100644
> --- a/include/erofs/internal.h
> +++ b/include/erofs/internal.h
> @@ -82,6 +82,8 @@ struct erofs_sb_info {
>  
>  	u16 available_compr_algs;
>  	u16 lz4_max_distance;
> +
> +	u32 checksum;
>  };
>  
>  /* global sbi */
> @@ -264,10 +266,13 @@ int erofs_read_superblock(void);
>  
>  /* namei.c */
>  int erofs_ilookup(const char *path, struct erofs_inode *vi);
> +int erofs_read_inode_from_disk(struct erofs_inode *vi);
>  
>  /* data.c */
>  int erofs_pread(struct erofs_inode *inode, char *buf,
>  		erofs_off_t count, erofs_off_t offset);
> +int erofs_map_blocks(struct erofs_inode *inode,
> +		     struct erofs_map_blocks *map, int flags);
>  /* zmap.c */
>  int z_erofs_fill_inode(struct erofs_inode *vi);
>  int z_erofs_map_blocks_iter(struct erofs_inode *vi,
> diff --git a/include/erofs_fs.h b/include/erofs_fs.h
> index 66a68e3..62e9981 100644
> --- a/include/erofs_fs.h
> +++ b/include/erofs_fs.h
> @@ -400,4 +400,17 @@ static inline void erofs_check_ondisk_layout_definitions(void)
>  		     Z_EROFS_VLE_CLUSTER_TYPE_MAX - 1);
>  }
>  
> +#define CRC32C_POLY_LE	0x82F63B78
> +static inline u32 crc32c(u32 crc, const u8 *in, size_t len)
> +{
> +	int i;
> +
> +	while (len--) {
> +		crc ^= *in++;
> +		for (i = 0; i < 8; i++)
> +			crc = (crc >> 1) ^ ((crc & 1) ? CRC32C_POLY_LE : 0);
> +	}
> +	return crc;
> +}
> +
>  #endif
> diff --git a/lib/data.c b/lib/data.c
> index 641d840..6cb7eeb 100644
> --- a/lib/data.c
> +++ b/lib/data.c
> @@ -61,8 +61,8 @@ err_out:
>  	return err;
>  }
>  
> -static int erofs_map_blocks(struct erofs_inode *inode,
> -			    struct erofs_map_blocks *map, int flags)
> +int erofs_map_blocks(struct erofs_inode *inode,
> +		     struct erofs_map_blocks *map, int flags)
>  {
>  	struct erofs_inode *vi = inode;
>  	struct erofs_inode_chunk_index *idx;
> diff --git a/lib/namei.c b/lib/namei.c
> index b4bdabf..56f199a 100644
> --- a/lib/namei.c
> +++ b/lib/namei.c
> @@ -22,7 +22,7 @@ static dev_t erofs_new_decode_dev(u32 dev)
>  	return makedev(major, minor);
>  }
>  
> -static int erofs_read_inode_from_disk(struct erofs_inode *vi)
> +int erofs_read_inode_from_disk(struct erofs_inode *vi)
>  {
>  	int ret, ifmt;
>  	char buf[sizeof(struct erofs_inode_extended)];
> diff --git a/lib/super.c b/lib/super.c
> index 0fa69ab..0c30403 100644
> --- a/lib/super.c
> +++ b/lib/super.c
> @@ -62,6 +62,7 @@ int erofs_read_superblock(void)
>  	sbi.islotbits = EROFS_ISLOTBITS;
>  	sbi.root_nid = le16_to_cpu(dsb->root_nid);
>  	sbi.inos = le64_to_cpu(dsb->inos);
> +	sbi.checksum = le32_to_cpu(dsb->checksum);
>  
>  	sbi.build_time = le64_to_cpu(dsb->build_time);
>  	sbi.build_time_nsec = le32_to_cpu(dsb->build_time_nsec);
> diff --git a/mkfs/main.c b/mkfs/main.c
> index 1c8dea5..b9b46f5 100644
> --- a/mkfs/main.c
> +++ b/mkfs/main.c
> @@ -424,19 +424,6 @@ int erofs_mkfs_update_super_block(struct erofs_buffer_head *bh,
>  	return 0;
>  }
>  
> -#define CRC32C_POLY_LE	0x82F63B78
> -static inline u32 crc32c(u32 crc, const u8 *in, size_t len)
> -{
> -	int i;
> -
> -	while (len--) {
> -		crc ^= *in++;
> -		for (i = 0; i < 8; i++)
> -			crc = (crc >> 1) ^ ((crc & 1) ? CRC32C_POLY_LE : 0);
> -	}
> -	return crc;
> -}
> -
>  static int erofs_mkfs_superblock_csum_set(void)
>  {
>  	int ret;
> -- 
> 2.33.0.1079.g6e70778dc9-goog
> 


More information about the Linux-erofs mailing list