* [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