[PATCH v3] erofs-utils: mkfs: add `--xattr-inode-digest` option
Gao Xiang
hsiangkao at linux.alibaba.com
Thu Jan 22 13:04:44 AEDT 2026
On 2026/1/22 09:08, Hongbo Li wrote:
> Hi, Xiang
>
> On 2026/1/21 11:19, 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 --xattr-inode-digest=system.erofs.fingerprint [-zlz4hc] foo.erofs foo/
>>
>> [1] https://lore.kernel.org/r/20251118015849.228939-1-lihongbo22@huawei.com
>>
>> Co-developed-by: Hongbo Li <lihongbo22 at huawei.com>
>> Signed-off-by: Gao Xiang <hsiangkao at linux.alibaba.com>
>> Tested-by: Hongbo Li <lihongbo22 at huawei.com>
>> ---
>> v2: https://lore.kernel.org/r/20251229180646.3017326-4-hsiangkao@linux.alibaba.com
>> v3:
>> - Support the hidden xattr namespace, so that user programs cannot
>> access inode fingerprints via normal mounts in any case.
>>
>> include/erofs/internal.h | 2 +
>> include/erofs/xattr.h | 2 +
>> include/erofs_fs.h | 4 +-
>> lib/inode.c | 46 +++++++++-
>> lib/super.c | 13 ++-
>> lib/xattr.c | 19 +++-
>> mkfs/main.c | 187 ++++++++++++++++++++++-----------------
>> 7 files changed, 182 insertions(+), 91 deletions(-)
>>
>> diff --git a/include/erofs/internal.h b/include/erofs/internal.h
>> index 26bf612..ef019a5 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;
>> @@ -190,6 +191,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 941bed7..9654636 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 887f37f..8b0d155 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 299ec46..e3ee79a 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)
>> {
>> @@ -1954,6 +1955,37 @@ static int erofs_prepare_dir_inode(const struct erofs_mkfs_btctx *ctx,
>> 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(const struct erofs_mkfs_btctx *btctx,
>> struct erofs_inode *inode)
>> {
>> @@ -1973,11 +2005,18 @@ static int erofs_mkfs_begin_nondirectory(const struct erofs_mkfs_btctx *btctx,
>> ctx.fd = open(inode->i_srcpath, O_RDONLY | O_BINARY);
>> if (ctx.fd < 0)
>> return -errno;
>> - __erofs_fallthrough;
>> - default:
>> break;
>> + default:
>> + goto out;
>> }
>> - if (ctx.fd >= 0 && cfg.c_compr_opts[0].alg &&
>> +
>> + 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 (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))
>> @@ -1989,6 +2028,7 @@ static int erofs_mkfs_begin_nondirectory(const struct erofs_mkfs_btctx *btctx,
>> return ret;
>> }
>> }
>> +out:
>> return erofs_mkfs_go(btctx, EROFS_MKFS_JOB_NDIR, &ctx, sizeof(ctx));
>> }
>> diff --git a/lib/super.c b/lib/super.c
>> index a203f96..0180087 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 9b0f2ca..d8c7bff 100644
>> --- a/lib/xattr.c
>> +++ b/lib/xattr.c
>> @@ -1483,8 +1483,9 @@ int erofs_xattr_insert_name_prefix(const char *prefix)
>> if (!erofs_xattr_prefix_matches(prefix, &tnode->base_index,
>> &tnode->base_len)) {
>> - free(tnode);
>> - return -ENODATA;
>> + /* Use internal hidden xattrs */
>> + tnode->base_index = 0;
>> + tnode->base_len = 0;
>
> So, should we add parameter bool hidden to this helper to distinct the cases?
>
> Others looks good to me!
Nope, just my own thought, I think for other xattr prefix cases
(if they really would like to add a hidden xattr prefix), that
should be okay too.
Thanks,
Gao Xiang
>
>
> Thanks,
> Hongbo
>
More information about the Linux-erofs
mailing list