aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaeho Jeong <daehojeong@google.com>2021-10-29 10:13:12 -0700
committerGao Xiang <xiang@kernel.org>2021-11-09 10:11:31 +0800
commitf44043561491255c37903a9ad1334f2e88c95005 (patch)
tree6fe705ef6f9434feeecc9353bcb90d1a1c179195
parentf500db7128ba4d45455f522323477839b73df4a6 (diff)
downloaderofs-utils-f44043561491255c37903a9ad1334f2e88c95005.tar.gz
erofs-utils: introduce fsck.erofs
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) -p print total compression ratio of all files -c check if all compressed files are well decompressed Link: https://lore.kernel.org/r/20211029171312.2189648-1-daeho43@gmail.com Signed-off-by: Daeho Jeong <daehojeong@google.com> Signed-off-by: Gao Xiang <xiang@kernel.org>
-rw-r--r--.gitignore1
-rw-r--r--Makefile.am2
-rw-r--r--configure.ac3
-rw-r--r--fsck/Makefile.am9
-rw-r--r--fsck/main.c587
-rw-r--r--include/erofs/internal.h17
-rw-r--r--lib/super.c1
-rw-r--r--man/fsck.erofs.138
-rw-r--r--mkfs/main.c15
9 files changed, 657 insertions, 16 deletions
diff --git a/.gitignore b/.gitignore
index 27403d4..33e5d30 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,3 +30,4 @@ stamp-h1
/mkfs/mkfs.erofs
/fuse/erofsfuse
/dump/dump.erofs
+/fsck/fsck.erofs
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 b50c00c..6fdb0e4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -347,5 +347,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..479ce87
--- /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 ${libselinux_LIBS} ${libuuid_LIBS} ${liblz4_LIBS}
diff --git a/fsck/main.c b/fsck/main.c
new file mode 100644
index 0000000..d05dd55
--- /dev/null
+++ b/fsck/main.c
@@ -0,0 +1,587 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2021 Google LLC
+ * Author: Daeho Jeong <daehojeong@google.com>
+ */
+#include <stdlib.h>
+#include <getopt.h>
+#include <time.h>
+#include "erofs/print.h"
+#include "erofs/io.h"
+#include "erofs/decompress.h"
+
+static void erofs_check_inode(erofs_nid_t pnid, erofs_nid_t nid);
+
+struct erofsfsck_cfg {
+ bool corrupted;
+ bool print_comp_ratio;
+ bool check_decomp;
+ u64 physical_blocks;
+ u64 logical_blocks;
+};
+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"
+ " -p print total compression ratio of all files\n"
+ " -c check if all compressed files are well decompressed\n"
+ " --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:pc",
+ 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 'p':
+ fsckcfg.print_comp_ratio = true;
+ break;
+ case 'c':
+ fsckcfg.check_decomp = true;
+ 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", 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: %d",
+ ret);
+ return -1;
+ }
+
+ sb = (struct erofs_super_block *)(buf + EROFS_SUPER_OFFSET);
+ sb->checksum = 0;
+
+ crc = erofs_crc32c(~0, (u8 *)sb, EROFS_BLKSIZ - EROFS_SUPER_OFFSET);
+ if (crc != sbi.checksum) {
+ erofs_err("superblock chksum doesn't match: saved(%08xh) calculated(%08xh)",
+ sbi.checksum, crc);
+ fsckcfg.corrupted = true;
+ return -1;
+ }
+ return 0;
+}
+
+static bool check_special_dentry(struct erofs_dirent *de,
+ unsigned int de_namelen, erofs_nid_t nid,
+ erofs_nid_t pnid)
+{
+ if (de_namelen == 2 && de->nid != pnid) {
+ erofs_err("wrong parent dir nid(%llu): pnid(%llu) @ nid(%llu)",
+ de->nid | 0ULL, pnid | 0ULL, nid | 0ULL);
+ return false;
+ }
+
+ if (de_namelen == 1 && de->nid != nid) {
+ erofs_err("wrong current dir nid(%llu) @ nid(%llu)",
+ de->nid | 0ULL, nid | 0ULL);
+ return false;
+ }
+ return true;
+}
+
+static int traverse_dirents(erofs_nid_t pnid, erofs_nid_t nid,
+ void *dentry_blk, erofs_blk_t block,
+ unsigned int next_nameoff, unsigned int maxsize)
+{
+ struct erofs_dirent *de = dentry_blk;
+ const struct erofs_dirent *end = dentry_blk + next_nameoff;
+ unsigned int idx = 0;
+ char *prev_name = NULL, *cur_name = NULL;
+ int ret = 0;
+
+ erofs_dbg("traversing pnid(%llu), nid(%llu)", pnid | 0ULL, nid | 0ULL);
+
+ if (!block && (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;
+
+ 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 (prev_name)
+ free(prev_name);
+ prev_name = cur_name;
+ cur_name = strndup(de_name, de_namelen);
+ if (!cur_name) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ erofs_dbg("traversed filename(%s)", cur_name);
+
+ /* corrupted entry check */
+ if (nameoff != next_nameoff) {
+ erofs_err("bogus dirent with nameoff(%u): expected(%d) @ nid %llu, block %u, idx %u",
+ nameoff, next_nameoff, nid | 0ULL,
+ block, idx);
+ ret = -EFSCORRUPTED;
+ goto out;
+ }
+
+ if (nameoff + de_namelen > maxsize ||
+ de_namelen > EROFS_NAME_LEN) {
+ erofs_err("bogus dirent with namelen(%u) @ nid %llu, block %u, idx %u",
+ de_namelen, nid | 0ULL, block, idx);
+ ret = -EFSCORRUPTED;
+ goto out;
+ }
+
+ if (prev_name && (strcmp(prev_name, cur_name) >= 0)) {
+ erofs_err("wrong dirent name order @ nid %llu block %u idx %u: prev(%s), cur(%s)",
+ nid | 0ULL, block, idx,
+ prev_name, cur_name);
+ ret = -EFSCORRUPTED;
+ goto out;
+ }
+
+ if (is_dot_dotdot(cur_name)) {
+ if (!check_special_dentry(de, de_namelen, nid, pnid)) {
+ ret = -EFSCORRUPTED;
+ goto out;
+ }
+ } else {
+ erofs_check_inode(nid, de->nid);
+ }
+
+ if (fsckcfg.corrupted) {
+ ret = -EFSCORRUPTED;
+ goto out;
+ }
+
+ next_nameoff += de_namelen;
+ ++de;
+ ++idx;
+ }
+
+out:
+ if (prev_name)
+ free(prev_name);
+ if (cur_name)
+ free(cur_name);
+
+ erofs_dbg("traversing ... done nid(%llu)", nid | 0ULL);
+ return ret;
+}
+
+static int verify_uncompressed_inode(struct erofs_inode *inode)
+{
+ struct erofs_map_blocks map = {
+ .index = UINT_MAX,
+ };
+ int ret;
+ erofs_off_t ptr = 0;
+ u64 i_blocks = DIV_ROUND_UP(inode->i_size, EROFS_BLKSIZ);
+
+ 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) {
+ /* reached EOF */
+ ptr = inode->i_size;
+ continue;
+ }
+
+ ptr += map.m_llen;
+ }
+
+ if (fsckcfg.print_comp_ratio) {
+ fsckcfg.logical_blocks += i_blocks;
+ fsckcfg.physical_blocks += i_blocks;
+ }
+
+ return 0;
+}
+
+static int verify_compressed_inode(struct erofs_inode *inode)
+{
+ struct erofs_map_blocks map = {
+ .index = UINT_MAX,
+ };
+ int ret = 0;
+ u64 pchunk_len = 0;
+ erofs_off_t end = inode->i_size;
+ unsigned int algorithmformat, raw_size = 0, buffer_size = 0;
+ char *raw = NULL, *buffer = NULL;
+
+ while (end > 0) {
+ map.m_la = end - 1;
+
+ ret = z_erofs_map_blocks_iter(inode, &map, 0);
+ if (ret)
+ goto out;
+
+ 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);
+ ret = -EFSCORRUPTED;
+ goto out;
+ }
+
+ pchunk_len += map.m_plen;
+ end = map.m_la;
+
+ if (!fsckcfg.check_decomp || !(map.m_flags & EROFS_MAP_MAPPED))
+ continue;
+
+ algorithmformat = map.m_flags & EROFS_MAP_ZIPPED ?
+ Z_EROFS_COMPRESSION_LZ4 :
+ Z_EROFS_COMPRESSION_SHIFTED;
+
+ if (map.m_plen > raw_size) {
+ raw_size = map.m_plen;
+ raw = realloc(raw, raw_size);
+ BUG_ON(!raw);
+ }
+
+ if (map.m_llen > buffer_size) {
+ buffer_size = map.m_llen;
+ buffer = realloc(buffer, buffer_size);
+ BUG_ON(!buffer);
+ }
+
+ ret = dev_read(raw, map.m_pa, map.m_plen);
+ if (ret < 0) {
+ erofs_err("failed to read compressed data of m_pa %" PRIu64 ", m_plen %" PRIu64 " @ nid %llu: %d",
+ map.m_pa, map.m_plen, inode->nid | 0ULL, ret);
+ goto out;
+ }
+
+ ret = z_erofs_decompress(&(struct z_erofs_decompress_req) {
+ .in = raw,
+ .out = buffer,
+ .decodedskip = 0,
+ .inputsize = map.m_plen,
+ .decodedlength = map.m_llen,
+ .alg = algorithmformat,
+ .partial_decoding = 0
+ });
+
+ if (ret < 0) {
+ erofs_err("failed to decompress data of m_pa %" PRIu64 ", m_plen %" PRIu64 " @ nid %llu: %d",
+ map.m_pa, map.m_plen, inode->nid | 0ULL, ret);
+ goto out;
+ }
+ }
+
+ if (fsckcfg.print_comp_ratio) {
+ fsckcfg.logical_blocks +=
+ DIV_ROUND_UP(inode->i_size, EROFS_BLKSIZ);
+ fsckcfg.physical_blocks +=
+ DIV_ROUND_UP(pchunk_len, EROFS_BLKSIZ);
+ }
+out:
+ if (raw)
+ free(raw);
+ if (buffer)
+ free(buffer);
+ return ret < 0 ? ret : 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[EROFS_BLKSIZ];
+
+ 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("failed to read xattr header @ nid %llu: %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 @ nid %llu",
+ inode->nid | 0ULL);
+ ret = -EFSCORRUPTED;
+ 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("failed to read xattr entry @ nid %llu: %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 @ nid %llu",
+ inode->nid | 0ULL);
+ ret = -EFSCORRUPTED;
+ goto out;
+ }
+ addr += entry_sz;
+ remaining -= entry_sz;
+ }
+out:
+ return ret;
+}
+
+static int erofs_verify_inode_data(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_uncompressed_inode(inode);
+ break;
+ case EROFS_INODE_FLAT_COMPRESSION_LEGACY:
+ case EROFS_INODE_FLAT_COMPRESSION:
+ ret = verify_compressed_inode(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_BLKSIZ];
+ erofs_off_t offset;
+
+ erofs_dbg("check inode: nid(%llu)", nid | 0ULL);
+
+ 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_inode_data(&inode);
+ if (ret)
+ goto out;
+
+ if ((inode.i_mode & S_IFMT) != S_IFDIR)
+ goto out;
+
+ offset = 0;
+ while (offset < inode.i_size) {
+ erofs_blk_t block = erofs_blknr(offset);
+ 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, block %u: %d",
+ nid | 0ULL, block, ret);
+ 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 block %u: %d",
+ nameoff, nid | 0ULL, block, ret);
+ ret = -EFSCORRUPTED;
+ goto out;
+ }
+
+ ret = traverse_dirents(pnid, nid, buf, block,
+ nameoff, maxsize);
+ if (ret)
+ goto out;
+
+ offset += maxsize;
+ }
+out:
+ 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 = false;
+ fsckcfg.check_decomp = false;
+ fsckcfg.logical_blocks = 0;
+ fsckcfg.physical_blocks = 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) {
+ erofs_err("Found some filesystem corruption");
+ err = -EFSCORRUPTED;
+ } else {
+ erofs_info("No error found");
+ if (fsckcfg.print_comp_ratio) {
+ double comp_ratio =
+ (double)fsckcfg.physical_blocks * 100 /
+ (double)fsckcfg.logical_blocks;
+
+ erofs_info("Compression ratio: %.2f(%%)", comp_ratio);
+ }
+ }
+
+exit:
+ erofs_exit_configure();
+ return err ? 1 : 0;
+}
diff --git a/include/erofs/internal.h b/include/erofs/internal.h
index 8282f03..f84e6b4 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 */
@@ -271,6 +273,7 @@ int erofs_read_superblock(void);
/* namei.c */
int erofs_read_inode_from_disk(struct erofs_inode *vi);
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,
@@ -287,4 +290,18 @@ int z_erofs_map_blocks_iter(struct erofs_inode *vi,
#else
#define EFSCORRUPTED EIO
#endif
+
+#define CRC32C_POLY_LE 0x82F63B78
+static inline u32 erofs_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/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/man/fsck.erofs.1 b/man/fsck.erofs.1
new file mode 100644
index 0000000..c1481d3
--- /dev/null
+++ b/man/fsck.erofs.1
@@ -0,0 +1,38 @@
+.\" Copyright (c) 2021 Daeho Jeong <daehojeong@google.com>
+.\"
+.TH FSCK.EROFS 1
+.SH NAME
+fsck.erofs \- tool to check the EROFS filesystem's integrity
+.SH SYNOPSIS
+\fBfsck.erofs\fR [\fIOPTIONS\fR] \fIIMAGE\fR
+.SH DESCRIPTION
+fsck.erofs is used to scan an EROFS filesystem \fIIMAGE\fR and check the
+integrity of it.
+.SH OPTIONS
+.TP
+.BI "\-V "
+Print the version number of fsck.erofs and exit.
+.TP
+.BI "\-d " #
+Specify the level of debugging messages. The default is 2, which shows basic
+warning messages.
+.TP
+.BI "\-p "
+Print total compression ratio of all files including compressed and
+non-compressed files.
+.TP
+.BI "\-c "
+Check if all the compressed files are well decompressed. This will induce more
+I/Os to read compressed file data, so it might take too much time depending on
+the image.
+.TP
+.B \-\-help
+Display this help and exit.
+.SH AUTHOR
+This version of \fBfsck.erofs\fR is written by
+Daeho Jeong <daehojeong@google.com>.
+.SH AVAILABILITY
+\fBfsck.erofs\fR is part of erofs-utils package and is available from
+git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs-utils.git.
+.SH SEE ALSO
+.BR fsck (8).
diff --git a/mkfs/main.c b/mkfs/main.c
index 028cf5a..4ea5467 100644
--- a/mkfs/main.c
+++ b/mkfs/main.c
@@ -429,19 +429,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;
@@ -470,7 +457,7 @@ static int erofs_mkfs_superblock_csum_set(void)
/* turn on checksum feature */
sb->feature_compat = cpu_to_le32(le32_to_cpu(sb->feature_compat) |
EROFS_FEATURE_COMPAT_SB_CHKSUM);
- crc = crc32c(~0, (u8 *)sb, EROFS_BLKSIZ - EROFS_SUPER_OFFSET);
+ crc = erofs_crc32c(~0, (u8 *)sb, EROFS_BLKSIZ - EROFS_SUPER_OFFSET);
/* set up checksum field to erofs_super_block */
sb->checksum = cpu_to_le32(crc);