erofs-utils: tests: implement checksum calculation for fssum
This patch specifically implements the function of calculating image checksum
and provides interface function that enables fsck to call to complete the
calculation of image checksum.
Signed-off-by: Jiawei Wang <kyr1ewang@qq.com>
Signed-off-by: Gao Xiang <xiang@kernel.org>
diff --git a/fsck/Makefile.am b/fsck/Makefile.am
index 5bdee4d..b7f0289 100644
--- a/fsck/Makefile.am
+++ b/fsck/Makefile.am
@@ -4,7 +4,8 @@
AUTOMAKE_OPTIONS = foreign
bin_PROGRAMS = fsck.erofs
AM_CPPFLAGS = ${libuuid_CFLAGS}
-fsck_erofs_SOURCES = main.c
+noinst_HEADERS = fssum.h
+fsck_erofs_SOURCES = main.c fssum.c
fsck_erofs_CFLAGS = -Wall -I$(top_srcdir)/include
fsck_erofs_LDADD = $(top_builddir)/lib/liberofs.la ${libselinux_LIBS} \
${liblz4_LIBS} ${liblzma_LIBS} ${zlib_LIBS} ${libdeflate_LIBS} \
diff --git a/fsck/fssum.c b/fsck/fssum.c
index 7c5798c..eeb07fa 100644
--- a/fsck/fssum.c
+++ b/fsck/fssum.c
@@ -215,7 +215,7 @@
erofs_fssum_MD5Final(cs->out, &cs->md5);
}
-void erofs_fssum_add(struct erofs_sum_t *cs, void* buf, int size)
+void erofs_fssum_add(struct erofs_sum_t *cs, const void *buf, int size)
{
erofs_fssum_MD5Update(&cs->md5, buf, size);
}
@@ -255,3 +255,256 @@
}
return 0;
+}
+
+char* erofs_fssum_sum2string(struct erofs_sum_t *dst)
+{
+ int i;
+ char *s = erofs_fssum_alloc(CS_SIZE * 2 + 1);
+
+ for (i = 0; i < CS_SIZE; ++i)
+ sprintf(s + i * 2, "%02x", dst->out[i]);
+
+ return s;
+}
+
+int erofs_fssum_sum(struct erofs_dir_context *ctx, struct erofs_sum_t *dircs, int level);
+
+int erofs_fssum_traverse_dirents(struct erofs_dir_context *ctx,
+ void *dentry_blk, unsigned int lblk,
+ unsigned int next_nameoff, unsigned int maxsize,
+ struct erofs_sum_t *dircs, int level)
+{
+ struct erofs_dirent *de = dentry_blk;
+ const struct erofs_dirent *end = dentry_blk + next_nameoff;
+ const char *errmsg;
+ int ret = 0;
+ bool silent = false;
+
+ 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;
+
+ ctx->de_nid = le64_to_cpu(de->nid);
+ erofs_dbg("traversed nid (%llu)", ctx->de_nid | 0ULL);
+
+ ret = -EFSCORRUPTED;
+ /* corrupted entry check */
+ if (nameoff != next_nameoff) {
+ errmsg = "bogus dirent nameoff";
+ break;
+ }
+
+ if (nameoff + de_namelen > maxsize || !de_namelen ||
+ de_namelen > EROFS_NAME_LEN) {
+ errmsg = "bogus dirent namelen";
+ break;
+ }
+
+ ctx->dname = de_name;
+ ctx->de_namelen = de_namelen;
+ ctx->de_ftype = de->file_type;
+ ctx->dot_dotdot = is_dot_dotdot_len(de_name, de_namelen);
+ ret = erofs_fssum_sum(ctx, dircs, level);
+ if (ret) {
+ silent = true;
+ break;
+ }
+ next_nameoff += de_namelen;
+ ++de;
+ }
+
+ if (ret && !silent)
+ erofs_err("%s @ nid %llu, lblk %u, index %lu",
+ errmsg, ctx->dir->nid | 0ULL, lblk,
+ (de - (struct erofs_dirent *)dentry_blk) | 0UL);
+ return ret;
+}
+
+int erofs_fssum_iterate_dir(struct erofs_dir_context *ctx, struct erofs_sum_t *dircs, int level)
+{
+ struct erofs_inode *dir = ctx->dir;
+ struct erofs_sb_info *sbi = dir->sbi;
+ int err = 0;
+ erofs_off_t pos;
+ char buf[EROFS_MAX_BLOCK_SIZE];
+
+ if (!S_ISDIR(dir->i_mode))
+ return -ENOTDIR;
+
+ ctx->flags &= ~EROFS_READDIR_ALL_SPECIAL_FOUND;
+ pos = 0;
+ while (pos < dir->i_size) {
+ erofs_blk_t lblk = erofs_blknr(sbi, pos);
+ erofs_off_t maxsize = min_t(erofs_off_t,
+ dir->i_size - pos, erofs_blksiz(sbi));
+ const struct erofs_dirent *de = (const void *)buf;
+ unsigned int nameoff;
+
+ err = erofs_pread(dir, buf, maxsize, pos);
+ if (err) {
+ erofs_err("I/O error occurred when reading dirents @ nid %llu, lblk %u: %d",
+ dir->nid | 0ULL, lblk, err);
+ return err;
+ }
+
+ nameoff = le16_to_cpu(de->nameoff);
+ if (nameoff < sizeof(struct erofs_dirent) ||
+ nameoff >= erofs_blksiz(sbi)) {
+ erofs_err("invalid de[0].nameoff %u @ nid %llu, lblk %u",
+ nameoff, dir->nid | 0ULL, lblk);
+ return -EFSCORRUPTED;
+ }
+ err = erofs_fssum_traverse_dirents(ctx, buf, lblk, nameoff, maxsize, dircs, level);
+ if (err)
+ break;
+ pos += maxsize;
+ }
+
+ return err;
+}
+
+int erofs_fssum_sum(struct erofs_dir_context *ctx, struct erofs_sum_t *dircs, int level)
+{
+ int err;
+ struct erofs_sum_t meta;
+ struct erofs_sum_t cs;
+ struct erofs_inode vi = { .sbi = &g_sbi, .nid = ctx->de_nid };
+
+
+ if (ctx->dot_dotdot)
+ return 0;
+
+ err = erofs_read_inode_from_disk(&vi);
+ if (err) {
+ erofs_err("failed to read file inode from disk");
+ return err;
+ }
+
+ erofs_fssum_init(&meta);
+ erofs_fssum_init(&cs);
+
+ erofs_fssum_addu64(&meta, level);
+ erofs_fssum_add(&meta, ctx->dname, ctx->de_namelen);
+ if (!S_ISDIR(vi.i_mode))
+ erofs_fssum_addu64(&meta, vi.i_nlink);
+ erofs_fssum_addu64(&meta, vi.i_uid);
+ erofs_fssum_addu64(&meta, vi.i_gid);
+ erofs_fssum_addu64(&meta, vi.i_mode);
+ erofs_fssum_addtime(&meta, vi.i_mtime);
+
+ if (S_ISDIR(vi.i_mode) || S_ISREG(vi.i_mode)) {
+ ssize_t kllen;
+ ssize_t ret;
+ char *keylst, *key;
+
+ kllen = erofs_listxattr(&vi, NULL, 0);
+ if (kllen < 0)
+ return kllen;
+
+ keylst = malloc(kllen);
+ if(!keylst)
+ return -ENOMEM;
+
+ ret = erofs_listxattr(&vi, keylst, kllen);
+ if (ret < 0) {
+ free(keylst);
+ return err;
+ }
+
+ for (key = keylst; key < keylst + kllen; key += strlen(key) + 1) {
+ char *value;
+ ssize_t size;
+
+ ret = erofs_getxattr(&vi, key, NULL, 0);
+ if (ret < 0)
+ return ret;
+
+ erofs_fssum_add(&meta, key, strlen(key));
+ if (ret == 0)
+ continue;
+ if (ret) {
+ size = ret;
+ value = malloc(size);
+ if (!value)
+ return -ENOMEM;
+ ret = erofs_getxattr(&vi, key, value, size);
+ if (ret < 0){
+ free(value);
+ free(keylst);
+ return ret;
+ }
+ erofs_fssum_add(&meta, value, size);
+ printf("key:%s val:%s\n", key, value);
+ }
+ }
+ free(keylst);
+ }
+
+ if (S_ISDIR(vi.i_mode)) {
+ struct erofs_dir_context nctx = {
+ .flags = ctx->dir ? EROFS_READDIR_VALID_PNID : 0,
+ .pnid = ctx->dir ? ctx->dir->nid : 0,
+ .dir = &vi,
+ };
+
+ err = erofs_fssum_iterate_dir(&nctx, &cs, level + 1);
+ if (err)
+ return err;
+ } else {
+ if (S_ISREG(vi.i_mode)) {
+ erofs_fssum_addu64(&meta, vi.i_size);
+ err = erofs_fssum_addinode(&cs, vi);
+ if (err)
+ return err;
+ } else if (S_ISLNK(vi.i_mode)) {
+ err = erofs_fssum_addinode(&cs, vi);
+ if (err)
+ return err;
+ } else if (S_ISCHR(vi.i_mode) || S_ISBLK(vi.i_mode)) {
+ erofs_fssum_addu64(&cs, major(vi.u.i_rdev));
+ erofs_fssum_addu64(&cs, minor(vi.u.i_rdev));
+ }
+ }
+
+ erofs_fssum_fini(&cs);
+ erofs_fssum_fini(&meta);
+ erofs_fssum_addsum(dircs, &cs);
+ erofs_fssum_addsum(dircs, &meta);
+
+ return 0;
+}
+
+int erofs_fssum_calculate(struct erofs_dir_context *ctx)
+{
+ struct erofs_sum_t ans_cs;
+ struct erofs_inode vi = { .sbi = &g_sbi, .nid = ctx->de_nid };
+ struct erofs_dir_context nctx = {
+ .flags = ctx->dir ? EROFS_READDIR_VALID_PNID : 0,
+ .pnid = ctx->dir ? ctx->dir->nid : 0,
+ .dir = &vi,
+ };
+ int ret = 0;
+
+ ret = erofs_read_inode_from_disk(&vi);
+ if (ret) {
+ erofs_err("failed to read file inode from disk");
+ return ret;
+ }
+ erofs_fssum_init(&ans_cs);
+ ret = erofs_fssum_iterate_dir(&nctx, &ans_cs, 1);
+ erofs_fssum_fini(&ans_cs);
+ fprintf(stdout, "%s\n", erofs_fssum_sum2string(&ans_cs));
+
+ return ret;
+}