* [RFC PATCH 1/2] erofs-utils: record algorithm id in in-memory compression extents @ 2026-09-11 12:09 Gao Xiang 2026-09-11 12:09 ` [RFC PATCH 2/2] erofs-utils: mkfs: support rebuild mode for compressed inodes Gao Xiang 0 siblings, 1 reply; 3+ messages in thread From: Gao Xiang @ 2026-09-11 12:09 UTC (permalink / raw) To: linux-erofs; +Cc: Jonathan Calmels, Gao Xiang In order to enable the rebuild mode for compressed inodes. Signed-off-by: Gao Xiang <xiang@kernel.org> --- include/erofs/dedupe.h | 1 + lib/compress.c | 13 ++++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/include/erofs/dedupe.h b/include/erofs/dedupe.h index 14dd7dc..dbcb37c 100644 --- a/include/erofs/dedupe.h +++ b/include/erofs/dedupe.h @@ -18,6 +18,7 @@ struct z_erofs_inmem_extent { unsigned int length; unsigned short device_id; bool raw, partial, inlined; + char algofmt; }; struct z_erofs_dedupe_ctx { diff --git a/lib/compress.c b/lib/compress.c index ae09e86..3e8f9b6 100644 --- a/lib/compress.c +++ b/lib/compress.c @@ -189,7 +189,9 @@ static void z_erofs_write_full_indexes(struct z_erofs_index_writer *ctx, DBG_BUGON(!e->raw && !inode->idata_size && !inode->fragment_size); DBG_BUGON(e->partial); type = e->raw ? Z_EROFS_LCLUSTER_TYPE_PLAIN : - Z_EROFS_LCLUSTER_TYPE_HEAD1; + (e->algofmt == inode->z_algorithmtype[0] ? + Z_EROFS_LCLUSTER_TYPE_HEAD1 : + Z_EROFS_LCLUSTER_TYPE_HEAD2); di.di_advise = cpu_to_le16(type); if (inode->datalayout == EROFS_INODE_COMPRESSED_FULL && !e->plen) { @@ -238,7 +240,9 @@ static void z_erofs_write_full_indexes(struct z_erofs_index_writer *ctx, di.di_u.delta[1] = cpu_to_le16(d1); } else { type = e->raw ? Z_EROFS_LCLUSTER_TYPE_PLAIN : - Z_EROFS_LCLUSTER_TYPE_HEAD1; + (e->algofmt == inode->z_algorithmtype[0] ? + Z_EROFS_LCLUSTER_TYPE_HEAD1 : + Z_EROFS_LCLUSTER_TYPE_HEAD2); if (inode->datalayout == EROFS_INODE_COMPRESSED_FULL && !e->plen) { @@ -700,6 +704,7 @@ frag_packing: e->inlined = true; e->plen = blksz; e->raw = false; + e->algofmt = inode->z_algorithmtype[0]; } else { unsigned int padding; @@ -755,6 +760,7 @@ frag_packing: } ctx->poff += e->plen; e->raw = false; + e->algofmt = inode->z_algorithmtype[0]; may_inline = false; may_packing = false; } @@ -1233,7 +1239,7 @@ static void z_erofs_write_extents(struct erofs_inode *inode, plen = inode->fragmentoff; pstart = inode->fragmentoff >> 32; } else { - fmt = ei->e.raw ? 0 : inode->z_algorithmtype[0] + 1; + fmt = ei->e.raw ? 0 : ei->e.algofmt + 1; plen |= fmt << Z_EROFS_EXTENT_PLEN_FMT_BIT; if (ei->e.partial) plen |= Z_EROFS_EXTENT_PLEN_PARTIAL; @@ -1350,6 +1356,7 @@ int z_erofs_compress_segment(struct z_erofs_compress_sctx *ctx, .raw = false, .partial = false, .pstart = ctx->pstart, + .algofmt = inode->z_algorithmtype[0], }; init_list_head(&ei->list); z_erofs_commit_extent(ctx, ei); -- 2.47.3 ^ permalink raw reply related [flat|nested] 3+ messages in thread
* [RFC PATCH 2/2] erofs-utils: mkfs: support rebuild mode for compressed inodes 2026-09-11 12:09 [RFC PATCH 1/2] erofs-utils: record algorithm id in in-memory compression extents Gao Xiang @ 2026-09-11 12:09 ` Gao Xiang 2026-09-11 12:21 ` [RFC PATCH v2 " Gao Xiang 0 siblings, 1 reply; 3+ messages in thread From: Gao Xiang @ 2026-09-11 12:09 UTC (permalink / raw) To: linux-erofs; +Cc: Jonathan Calmels, Gao Xiang Add support for generating a meta-only multidev manifest image with an overlayfs-like merged tree from multiple compressed EROFS images: $ mkfs.erofs -zlz4 -Enoinline_data layerA.erofs layerA/ $ mkfs.erofs -zlz4 -Enoinline_data layerB.erofs layerB/ $ mkfs.erofs -zlz4 fsmeta.erofs layerA.erofs layerB.erofs # mount -t erofs \ -odevice=layerA.erofs,device=layerB.erofs fsmeta.erofs mnt/ Note that `-Efragments` is not yet supported: it needs further userspace development. Cc: Jonathan Calmels <jcalmels@nvidia.com> Signed-off-by: Gao Xiang <xiang@kernel.org> --- lib/compress.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/rebuild.c | 16 +++++++- 2 files changed, 115 insertions(+), 1 deletion(-) diff --git a/lib/compress.c b/lib/compress.c index 3e8f9b6..fb0f92e 100644 --- a/lib/compress.c +++ b/lib/compress.c @@ -2435,3 +2435,103 @@ int z_erofs_compress_exit(struct erofs_sb_info *sbi) free(sbi->zmgr); return 0; } + +int z_erofs_rebuild_load_metadata(struct erofs_sb_info *dst_sbi, + struct erofs_inode *inode) +{ + struct erofs_sb_info *sbi = inode->sbi; + struct erofs_map_blocks map = { + .buf = __EROFS_BUF_INITIALIZER, + .m_la = 0, + }; + struct z_erofs_extent_item *ei, *n; + erofs_off_t pend = EROFS_NULL_ADDR; + bool consecutive = true; + LIST_HEAD(extents); + int err, device_id; + + while (map.m_la < inode->i_size) { + struct erofs_map_dev mdev; + bool raw; + + err = erofs_map_blocks(inode, &map, EROFS_GET_BLOCKS_FIEMAP); + if (err) + goto err_out; + + mdev = (struct erofs_map_dev) { + .m_deviceid = map.m_deviceid, + .m_pa = map.m_pa, + }; + err = erofs_map_dev(sbi, &mdev); + if (err) + goto err_out; + + raw = (map.m_algorithmformat >= Z_EROFS_COMPRESSION_MAX); + + ei = malloc(sizeof(*ei)); + if (!ei) { + err = -ENOMEM; + goto err_out; + } + + init_list_head(&ei->list); + ei->e = (struct z_erofs_inmem_extent) { + .length = map.m_llen, + .plen = map.m_plen, + .pstart = mdev.m_pa, + .device_id = inode->dev, /* FIXME! */ + .partial = map.m_flags & EROFS_MAP_PARTIAL_REF, + .raw = raw, + .inlined = map.m_flags & EROFS_MAP_META, + }; + if (!raw) { + ei->e.algofmt = map.m_algorithmformat; + if (!(dst_sbi->available_compr_algs & (1 << ei->e.algofmt))) { + err = -EOPNOTSUPP; + goto err_free_ei; + } + } + + if (map.m_flags & __EROFS_MAP_FRAGMENT) { + err = -EOPNOTSUPP; + goto err_free_ei; +// inode->fragment_size = map.m_llen; +// DBG_BUGON(inode->fragmentoff != map.m_pa); + } else if (map.m_flags & EROFS_MAP_META) { + DBG_BUGON(inode->idata_size != map.m_plen); + inode->idata = malloc(inode->idata_size); + if (!inode->idata) { + err = -ENOMEM; + goto err_free_ei; + } + err = erofs_dev_read(sbi, 0, inode->idata, mdev.m_pa, + inode->idata_size); + if (err) + goto err_free_ei; + DBG_BUGON(map.m_la + map.m_llen != inode->i_size); + } else { + if (pend != EROFS_NULL_ADDR && (pend != ei->e.pstart || + device_id != ei->e.device_id)) + consecutive = false; + pend = ei->e.pstart + ei->e.plen; + device_id = ei->e.device_id; + } + list_add_tail(&ei->list, &extents); + map.m_la += map.m_llen; + } + + inode->z_lclusterbits = sbi->blkszbits; + err = z_erofs_prepare_layout(inode, &extents, consecutive, false); + if (err) + goto err_out; + return 0; + +err_free_ei: + free(ei); +err_out: + list_for_each_entry_safe(ei, n, &extents, list) { + list_del(&ei->list); + free(ei); + } + return err; +} diff --git a/lib/rebuild.c b/lib/rebuild.c index 38ced3c..fac0ab8 100644 --- a/lib/rebuild.c +++ b/lib/rebuild.c @@ -152,7 +152,7 @@ struct erofs_dentry *erofs_rebuild_get_dentry(struct erofs_inode *pwd, return d; } -static int erofs_rebuild_write_blob_index(struct erofs_sb_info *dst_sb, +static int erofs_rebuild_load_chunk_index(struct erofs_sb_info *dst_sb, struct erofs_inode *inode) { int ret; @@ -222,6 +222,17 @@ err: return ret; } +static int erofs_rebuild_write_blob_index(struct erofs_sb_info *dst_sb, + struct erofs_inode *inode) +{ + int z_erofs_rebuild_load_metadata(struct erofs_sb_info *dst_sb, + struct erofs_inode *inode); + + if (is_inode_layout_compression(inode)) + return z_erofs_rebuild_load_metadata(dst_sb, inode); + return erofs_rebuild_load_chunk_index(dst_sb, inode); +} + static int erofs_rebuild_write_full_data(struct erofs_inode *inode) { struct erofs_sb_info *src_sbi = inode->sbi; @@ -514,6 +525,9 @@ int erofs_rebuild_load_tree(struct erofs_inode *root, struct erofs_sb_info *sbi, return ret; } + if (erofs_sb_has_dedupe(sbi)) + erofs_sb_set_dedupe(root->sbi); + inode.nid = sbi->root_nid; inode.sbi = sbi; ret = erofs_read_inode_from_disk(&inode); -- 2.47.3 ^ permalink raw reply related [flat|nested] 3+ messages in thread
* [RFC PATCH v2 2/2] erofs-utils: mkfs: support rebuild mode for compressed inodes 2026-09-11 12:09 ` [RFC PATCH 2/2] erofs-utils: mkfs: support rebuild mode for compressed inodes Gao Xiang @ 2026-09-11 12:21 ` Gao Xiang 0 siblings, 0 replies; 3+ messages in thread From: Gao Xiang @ 2026-09-11 12:21 UTC (permalink / raw) To: linux-erofs; +Cc: Jonathan Calmels, Gao Xiang Add support for generating a meta-only multidev manifest image with an overlayfs-like merged tree from multiple compressed EROFS images: $ mkfs.erofs -zlz4 -Enoinline_data layerA.erofs layerA/ $ mkfs.erofs -zlz4 -Enoinline_data layerB.erofs layerB/ $ mkfs.erofs -zlz4 fsmeta.erofs layerA.erofs layerB.erofs # mount -t erofs \ -odevice=layerA.erofs,device=layerB.erofs fsmeta.erofs mnt/ Note that `-Efragments` is not yet supported: it needs further userspace development. Cc: Jonathan Calmels <jcalmels@nvidia.com> Signed-off-by: Gao Xiang <xiang@kernel.org> --- lib/compress.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/rebuild.c | 22 ++++++++++- 2 files changed, 121 insertions(+), 1 deletion(-) diff --git a/lib/compress.c b/lib/compress.c index 3e8f9b6..fb0f92e 100644 --- a/lib/compress.c +++ b/lib/compress.c @@ -2435,3 +2435,103 @@ int z_erofs_compress_exit(struct erofs_sb_info *sbi) free(sbi->zmgr); return 0; } + +int z_erofs_rebuild_load_metadata(struct erofs_sb_info *dst_sbi, + struct erofs_inode *inode) +{ + struct erofs_sb_info *sbi = inode->sbi; + struct erofs_map_blocks map = { + .buf = __EROFS_BUF_INITIALIZER, + .m_la = 0, + }; + struct z_erofs_extent_item *ei, *n; + erofs_off_t pend = EROFS_NULL_ADDR; + bool consecutive = true; + LIST_HEAD(extents); + int err, device_id; + + while (map.m_la < inode->i_size) { + struct erofs_map_dev mdev; + bool raw; + + err = erofs_map_blocks(inode, &map, EROFS_GET_BLOCKS_FIEMAP); + if (err) + goto err_out; + + mdev = (struct erofs_map_dev) { + .m_deviceid = map.m_deviceid, + .m_pa = map.m_pa, + }; + err = erofs_map_dev(sbi, &mdev); + if (err) + goto err_out; + + raw = (map.m_algorithmformat >= Z_EROFS_COMPRESSION_MAX); + + ei = malloc(sizeof(*ei)); + if (!ei) { + err = -ENOMEM; + goto err_out; + } + + init_list_head(&ei->list); + ei->e = (struct z_erofs_inmem_extent) { + .length = map.m_llen, + .plen = map.m_plen, + .pstart = mdev.m_pa, + .device_id = inode->dev, /* FIXME! */ + .partial = map.m_flags & EROFS_MAP_PARTIAL_REF, + .raw = raw, + .inlined = map.m_flags & EROFS_MAP_META, + }; + if (!raw) { + ei->e.algofmt = map.m_algorithmformat; + if (!(dst_sbi->available_compr_algs & (1 << ei->e.algofmt))) { + err = -EOPNOTSUPP; + goto err_free_ei; + } + } + + if (map.m_flags & __EROFS_MAP_FRAGMENT) { + err = -EOPNOTSUPP; + goto err_free_ei; +// inode->fragment_size = map.m_llen; +// DBG_BUGON(inode->fragmentoff != map.m_pa); + } else if (map.m_flags & EROFS_MAP_META) { + DBG_BUGON(inode->idata_size != map.m_plen); + inode->idata = malloc(inode->idata_size); + if (!inode->idata) { + err = -ENOMEM; + goto err_free_ei; + } + err = erofs_dev_read(sbi, 0, inode->idata, mdev.m_pa, + inode->idata_size); + if (err) + goto err_free_ei; + DBG_BUGON(map.m_la + map.m_llen != inode->i_size); + } else { + if (pend != EROFS_NULL_ADDR && (pend != ei->e.pstart || + device_id != ei->e.device_id)) + consecutive = false; + pend = ei->e.pstart + ei->e.plen; + device_id = ei->e.device_id; + } + list_add_tail(&ei->list, &extents); + map.m_la += map.m_llen; + } + + inode->z_lclusterbits = sbi->blkszbits; + err = z_erofs_prepare_layout(inode, &extents, consecutive, false); + if (err) + goto err_out; + return 0; + +err_free_ei: + free(ei); +err_out: + list_for_each_entry_safe(ei, n, &extents, list) { + list_del(&ei->list); + free(ei); + } + return err; +} diff --git a/lib/rebuild.c b/lib/rebuild.c index 38ced3c..b431bd4 100644 --- a/lib/rebuild.c +++ b/lib/rebuild.c @@ -152,7 +152,7 @@ struct erofs_dentry *erofs_rebuild_get_dentry(struct erofs_inode *pwd, return d; } -static int erofs_rebuild_write_blob_index(struct erofs_sb_info *dst_sb, +static int erofs_rebuild_load_chunk_index(struct erofs_sb_info *dst_sb, struct erofs_inode *inode) { int ret; @@ -222,6 +222,17 @@ err: return ret; } +static int erofs_rebuild_write_blob_index(struct erofs_sb_info *dst_sb, + struct erofs_inode *inode) +{ + int z_erofs_rebuild_load_metadata(struct erofs_sb_info *dst_sb, + struct erofs_inode *inode); + + if (is_inode_layout_compression(inode)) + return z_erofs_rebuild_load_metadata(dst_sb, inode); + return erofs_rebuild_load_chunk_index(dst_sb, inode); +} + static int erofs_rebuild_write_full_data(struct erofs_inode *inode) { struct erofs_sb_info *src_sbi = inode->sbi; @@ -514,6 +525,15 @@ int erofs_rebuild_load_tree(struct erofs_inode *root, struct erofs_sb_info *sbi, return ret; } + if (erofs_sb_has_dedupe(sbi)) + erofs_sb_set_dedupe(root->sbi); + if (erofs_sb_has_big_pcluster(sbi) && + !erofs_sb_has_big_pcluster(root->sbi)) { + erofs_err("failed to load tree from image %d with big pclusters", + sbi->dev); + return -EOPNOTSUPP; + } + inode.nid = sbi->root_nid; inode.sbi = sbi; ret = erofs_read_inode_from_disk(&inode); -- 2.47.3 ^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-09-11 12:22 UTC | newest] Thread overview: 3+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-09-11 12:09 [RFC PATCH 1/2] erofs-utils: record algorithm id in in-memory compression extents Gao Xiang 2026-09-11 12:09 ` [RFC PATCH 2/2] erofs-utils: mkfs: support rebuild mode for compressed inodes Gao Xiang 2026-09-11 12:21 ` [RFC PATCH v2 " Gao Xiang
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox