[PATCH 2/2] erofs-utils: mkfs: add `--inode-digest-xattr` option

Gao Xiang hsiangkao at linux.alibaba.com
Tue Dec 30 00:15:10 AEDT 2025


Hi Hongbo,

On 2025/12/29 21:06, Hongbo Li wrote:
> Hi Xiang,
> 
> On 2025/12/29 15:49, Gao Xiang wrote:
>> Based on the original Hongbo's version [1], it enables storing the
>> SHA-256 digest of each inode as an extended attribute, in preparation
>> for the upcoming page cache sharing feature.
>>
>> Example usage:
>>   $ mkfs.erofs --inode-digest-xattr=trusted.erofs.fingerprint [-zlz4hc] foo.erofs foo/
>>
>> Co-developed-by: Hongbo Li <lihongbo22 at huawei.com>
>> [1] https://lore.kernel.org/r/20251118015849.228939-1-lihongbo22@huawei.com
>> Signed-off-by: Gao Xiang <hsiangkao at linux.alibaba.com>
>> ---
>>   include/erofs/internal.h |  2 ++
>>   include/erofs/xattr.h    |  2 ++
>>   include/erofs_fs.h       |  4 +++-
>>   lib/inode.c              | 44 ++++++++++++++++++++++++++++++++++++++--
>>   lib/super.c              | 13 ++++++++++--
>>   lib/xattr.c              | 14 ++++++++++++-
>>   mkfs/main.c              | 27 ++++++++++++++++++++----
>>   7 files changed, 96 insertions(+), 10 deletions(-)
>>
>> diff --git a/include/erofs/internal.h b/include/erofs/internal.h
>> index 62594b877151..5798f10e89c2 100644
>> --- a/include/erofs/internal.h
>> +++ b/include/erofs/internal.h
>> @@ -130,6 +130,7 @@ struct erofs_sb_info {
>>       u32 xattr_prefix_start;
>>       u8 xattr_prefix_count;
>> +    u8 ishare_xattr_prefix_id;
>>       struct erofs_xattr_prefix_item *xattr_prefixes;
>>       struct erofs_vfile bdev;
>> @@ -189,6 +190,7 @@ EROFS_FEATURE_FUNCS(metabox, incompat, INCOMPAT_METABOX)
>>   EROFS_FEATURE_FUNCS(sb_chksum, compat, COMPAT_SB_CHKSUM)
>>   EROFS_FEATURE_FUNCS(xattr_filter, compat, COMPAT_XATTR_FILTER)
>>   EROFS_FEATURE_FUNCS(plain_xattr_pfx, compat, COMPAT_PLAIN_XATTR_PFX)
>> +EROFS_FEATURE_FUNCS(ishare_xattrs, compat, COMPAT_ISHARE_XATTRS)
>>   #define EROFS_I_EA_INITED_BIT    0
>>   #define EROFS_I_Z_INITED_BIT    1
>> diff --git a/include/erofs/xattr.h b/include/erofs/xattr.h
>> index 941bed778956..96546364f316 100644
>> --- a/include/erofs/xattr.h
>> +++ b/include/erofs/xattr.h
>> @@ -33,6 +33,8 @@ char *erofs_export_xattr_ibody(struct erofs_inode *inode);
>>   int erofs_load_shared_xattrs_from_path(struct erofs_sb_info *sbi, const char *path,
>>                          long inlinexattr_tolerance);
>>   int erofs_xattr_insert_name_prefix(const char *prefix);
>> +int erofs_xattr_set_ishare_prefix(struct erofs_sb_info *sbi,
>> +                  const char *prefix);
>>   void erofs_xattr_cleanup_name_prefixes(void);
>>   int erofs_xattr_flush_name_prefixes(struct erofs_importer *im, bool plain);
>>   int erofs_xattr_prefixes_init(struct erofs_sb_info *sbi);
>> diff --git a/include/erofs_fs.h b/include/erofs_fs.h
>> index 887f37faba59..8b0d155f8c4c 100644
>> --- a/include/erofs_fs.h
>> +++ b/include/erofs_fs.h
>> @@ -17,6 +17,7 @@
>>   #define EROFS_FEATURE_COMPAT_MTIME              0x00000002
>>   #define EROFS_FEATURE_COMPAT_XATTR_FILTER    0x00000004
>>   #define EROFS_FEATURE_COMPAT_PLAIN_XATTR_PFX    0x00000010
>> +#define EROFS_FEATURE_COMPAT_ISHARE_XATTRS    0x00000020
>>   /*
>>    * Any bits that aren't in EROFS_ALL_FEATURE_INCOMPAT should
>> @@ -82,7 +83,8 @@ struct erofs_super_block {
>>       __le32 xattr_prefix_start;    /* start of long xattr prefixes */
>>       __le64 packed_nid;    /* nid of the special packed inode */
>>       __u8 xattr_filter_reserved; /* reserved for xattr name filter */
>> -    __u8 reserved[3];
>> +    __u8 ishare_xattr_prefix_id;
>> +    __u8 reserved[2];
>>       __le32 build_time;    /* seconds added to epoch for mkfs time */
>>       __le64 rootnid_8b;    /* (48BIT on) nid of root directory */
>>       __le64 reserved2;
>> diff --git a/lib/inode.c b/lib/inode.c
>> index 1d08e39317c0..c6085a52281a 100644
>> --- a/lib/inode.c
>> +++ b/lib/inode.c
>> @@ -31,6 +31,7 @@
>>   #include "liberofs_metabox.h"
>>   #include "liberofs_private.h"
>>   #include "liberofs_rebuild.h"
>> +#include "sha256.h"
>>   static inline bool erofs_is_special_identifier(const char *path)
>>   {
>> @@ -1879,6 +1880,37 @@ static int erofs_prepare_dir_inode(struct erofs_importer *im,
>>       return 0;
>>   }
>> +static int erofs_set_inode_fingerprint(struct erofs_inode *inode, int fd,
>> +                       erofs_off_t pos)
>> +{
>> +    u8 ishare_xattr_prefix_id = inode->sbi->ishare_xattr_prefix_id;
>> +    erofs_off_t remaining = inode->i_size;
>> +    struct erofs_vfile vf = { .fd = fd };
>> +    struct sha256_state md;
>> +    u8 out[32 + sizeof("sha256:") - 1];
>> +    int ret;
>> +
>> +    if (!ishare_xattr_prefix_id)
>> +        return 0;
>> +    erofs_sha256_init(&md);
>> +    do {
>> +        u8 buf[32768];
>> +
>> +        ret = erofs_io_pread(&vf, buf,
>> +                     min_t(u64, remaining, sizeof(buf)), pos);
>> +        if (ret < 0)
>> +            return ret;
>> +        if (ret > 0)
>> +            erofs_sha256_process(&md, buf, ret);
>> +        remaining -= ret;
>> +        pos += ret;
>> +    } while (remaining);
>> +    erofs_sha256_done(&md, out + sizeof("sha256:") - 1);
>> +    memcpy(out, "sha256:", sizeof("sha256:") - 1);
>> +    return erofs_setxattr(inode, ishare_xattr_prefix_id, "",
>> +                  out, sizeof(out));
>> +}
>> +
>>   static int erofs_mkfs_begin_nondirectory(struct erofs_importer *im,
>>                        struct erofs_inode *inode)
>>   {
>> @@ -1899,9 +1931,16 @@ static int erofs_mkfs_begin_nondirectory(struct erofs_importer *im,
>>                   return -errno;
>>               __erofs_fallthrough;
>>           default:
>> -            break;
>> +            goto out;
> 
> We should keep use break here to set inode fingerprint in following step.
> 
> Another is that, here we use "erofs_set_inode_fingerprint" to hook the fingerprint xattr in inode->xattrs, but the "erofs_prepare_xattr_ibody" has calculated the xattr_size before. So should we move the "erofs_prepare_xattr_ibody" later so that to make sure the fingerprint xattr is set?

Yeah, thanks for catching this, I will refine another version later.

Thanks,
Gao Xiang

> 
> Thanks,
> Hongbo
> 
>> +        }
>> +
>> +        if (S_ISREG(inode->i_mode) && inode->i_size) {
>> +            ret = erofs_set_inode_fingerprint(inode, ctx.fd, ctx.fpos);
>> +            if (ret < 0)
>> +                return ret;
>>           }
>> -        if (ctx.fd >= 0 && cfg.c_compr_opts[0].alg &&
>> +
>> +        if (cfg.c_compr_opts[0].alg &&
>>               erofs_file_is_compressible(im, inode)) {
>>               ctx.ictx = erofs_prepare_compressed_file(im, inode);
>>               if (IS_ERR(ctx.ictx))
>> @@ -1913,6 +1952,7 @@ static int erofs_mkfs_begin_nondirectory(struct erofs_importer *im,
>>                   return ret;
>>           }
>>       }
>> +out:
>>       return erofs_mkfs_go(im, EROFS_MKFS_JOB_NDIR, &ctx, sizeof(ctx));
>>   }
>> diff --git a/lib/super.c b/lib/super.c
>> index e54aff2d4ab7..a4837e5702ed 100644
>> --- a/lib/super.c
>> +++ b/lib/super.c
>> @@ -146,7 +146,15 @@ int erofs_read_superblock(struct erofs_sb_info *sbi)
>>       sbi->build_time = le32_to_cpu(dsb->build_time);
>>       memcpy(&sbi->uuid, dsb->uuid, sizeof(dsb->uuid));
>> -
>> +    if (erofs_sb_has_ishare_xattrs(sbi)) {
>> +        if (dsb->ishare_xattr_prefix_id >= sbi->xattr_prefix_count) {
>> +            erofs_err("invalid ishare xattr prefix id %d",
>> +                  dsb->ishare_xattr_prefix_id);
>> +            return -EFSCORRUPTED;
>> +        }
>> +        sbi->ishare_xattr_prefix_id =
>> +            dsb->ishare_xattr_prefix_id | EROFS_XATTR_LONG_PREFIX;
>> +    }
>>       ret = z_erofs_parse_cfgs(sbi, dsb);
>>       if (ret)
>>           return ret;
>> @@ -160,7 +168,6 @@ int erofs_read_superblock(struct erofs_sb_info *sbi)
>>           free(sbi->devs);
>>           sbi->devs = NULL;
>>       }
>> -
>>       sbi->sb_valid = !ret;
>>       return ret;
>>   }
>> @@ -206,6 +213,8 @@ int erofs_writesb(struct erofs_sb_info *sbi)
>>           .extra_devices = cpu_to_le16(sbi->extra_devices),
>>           .devt_slotoff = cpu_to_le16(sbi->devt_slotoff),
>>           .packed_nid = cpu_to_le64(sbi->packed_nid),
>> +        .ishare_xattr_prefix_id = sbi->ishare_xattr_prefix_id &
>> +            EROFS_XATTR_LONG_PREFIX_MASK,
>>       };
>>       char *buf;
>>       int ret;
>> diff --git a/lib/xattr.c b/lib/xattr.c
>> index b6b1a5e600fb..764aee3be3c3 100644
>> --- a/lib/xattr.c
>> +++ b/lib/xattr.c
>> @@ -1495,9 +1495,21 @@ int erofs_xattr_insert_name_prefix(const char *prefix)
>>       }
>>       tnode->index = EROFS_XATTR_LONG_PREFIX | ea_prefix_count;
>> -    ea_prefix_count++;
>>       init_list_head(&tnode->list);
>>       list_add_tail(&tnode->list, &ea_name_prefixes);
>> +    return ea_prefix_count++;
>> +}
>> +
>> +int erofs_xattr_set_ishare_prefix(struct erofs_sb_info *sbi,
>> +                  const char *prefix)
>> +{
>> +    int err;
>> +
>> +    err = erofs_xattr_insert_name_prefix(prefix);
>> +    if (err < 0)
>> +        return err;
>> +    sbi->ishare_xattr_prefix_id = EROFS_XATTR_LONG_PREFIX | err;
>> +    erofs_sb_set_ishare_xattrs(sbi);
>>       return 0;
>>   }
>> diff --git a/mkfs/main.c b/mkfs/main.c
>> index aaa0300bca1b..7fd6e8419d44 100644
>> --- a/mkfs/main.c
>> +++ b/mkfs/main.c
>> @@ -102,6 +102,7 @@ static struct option long_options[] = {
>>   #endif
>>       {"zD", optional_argument, NULL, 536},
>>       {"ZI", optional_argument, NULL, 537},
>> +    {"inode-digest-xattr", required_argument, NULL, 538},
>>       {0, 0, 0, 0},
>>   };
>> @@ -1262,7 +1263,7 @@ static int mkfs_parse_options_cfg(struct erofs_importer_params *params,
>>           case 19:
>>               errno = 0;
>>               opt = erofs_xattr_insert_name_prefix(optarg);
>> -            if (opt) {
>> +            if (opt < 0) {
>>                   erofs_err("failed to parse xattr name prefix: %s",
>>                         erofs_strerror(opt));
>>                   return opt;
>> @@ -1424,6 +1425,14 @@ static int mkfs_parse_options_cfg(struct erofs_importer_params *params,
>>               else
>>                   mkfscfg.inode_metazone = false;
>>               break;
>> +        case 538:
>> +            err = erofs_xattr_set_ishare_prefix(&g_sbi, optarg);
>> +            if (err < 0) {
>> +                erofs_err("failed to parse ishare name: %s",
>> +                      erofs_strerror(err));
>> +                return err;
>> +            }
>> +            break;
>>           case 'V':
>>               version();
>>               exit(0);
>> @@ -1878,9 +1887,12 @@ int main(int argc, char **argv)
>>               goto exit;
>>           }
>> -        if (cfg.c_extra_ea_name_prefixes)
>> -            erofs_xattr_flush_name_prefixes(&importer,
>> -                            mkfs_plain_xattr_pfx);
>> +        err = erofs_xattr_flush_name_prefixes(&importer,
>> +                              mkfs_plain_xattr_pfx);
>> +        if (err) {
>> +            erofs_err("failed to flush long xattr prefixes: %s",
>> +                  erofs_strerror(err));
>> +        }
>>           root = erofs_new_inode(&g_sbi);
>>           if (IS_ERR(root)) {
>> @@ -1888,6 +1900,13 @@ int main(int argc, char **argv)
>>               goto exit;
>>           }
>>       } else {
>> +        err = erofs_xattr_flush_name_prefixes(&importer,
>> +                              mkfs_plain_xattr_pfx);
>> +        if (err) {
>> +            erofs_err("failed to flush long xattr prefixes: %s",
>> +                  erofs_strerror(err));
>> +        }
>> +
>>           root = erofs_rebuild_make_root(&g_sbi);
>>           if (IS_ERR(root)) {
>>               err = PTR_ERR(root);



More information about the Linux-erofs mailing list