[PATCH v4 1/2] erofs-utils: mkfs: add rebuild FULLDATA for combined EROFS images
Lucas Karpinski
lkarpinski at nvidia.com
Thu Apr 16 00:08:09 AEST 2026
Add experimental support for merging multiple source images in mkfs. Each
regular file record the source image path and its byte offset. During the
blob mkfs opens the blob and pulls the payload in via erofs_io_xcopy.
This does not yet support chunk-based files or compressed images.
Signed-off-by: Lucas Karpinski <lkarpinski at nvidia.com>
---
include/erofs/internal.h | 3 +++
lib/inode.c | 38 ++++++++++++++++++++++-----
lib/rebuild.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++
mkfs/main.c | 7 +++--
4 files changed, 108 insertions(+), 8 deletions(-)
diff --git a/include/erofs/internal.h b/include/erofs/internal.h
index c780228c..450e2647 100644
--- a/include/erofs/internal.h
+++ b/include/erofs/internal.h
@@ -208,6 +208,7 @@ struct erofs_diskbuf;
#define EROFS_INODE_DATA_SOURCE_LOCALPATH 1
#define EROFS_INODE_DATA_SOURCE_DISKBUF 2
#define EROFS_INODE_DATA_SOURCE_RESVSP 3
+#define EROFS_INODE_DATA_SOURCE_REBUILD_BLOB 4
#define EROFS_I_BLKADDR_DEV_ID_BIT 48
@@ -253,6 +254,8 @@ struct erofs_inode {
char *i_link;
struct erofs_diskbuf *i_diskbuf;
};
+ char *rebuild_blobpath;
+ erofs_off_t rebuild_src_dataoff;
unsigned char datalayout;
unsigned char inode_isize;
/* inline tail-end packing size */
diff --git a/lib/inode.c b/lib/inode.c
index 2f78d9b8..95fd93b9 100644
--- a/lib/inode.c
+++ b/lib/inode.c
@@ -158,6 +158,8 @@ unsigned int erofs_iput(struct erofs_inode *inode)
if (inode->datasource == EROFS_INODE_DATA_SOURCE_DISKBUF) {
erofs_diskbuf_close(inode->i_diskbuf);
free(inode->i_diskbuf);
+ } else if (inode->datasource == EROFS_INODE_DATA_SOURCE_REBUILD_BLOB) {
+ free(inode->rebuild_blobpath);
} else {
free(inode->i_link);
}
@@ -679,8 +681,11 @@ static int erofs_write_unencoded_data(struct erofs_inode *inode,
} while (remaining);
}
- /* read the tail-end data */
- if (inode->idata_size) {
+ /*
+ * Read the tail-end data if inode->idata is NULL; if the tail data
+ * has been prepared then nothing more needs to be done here.
+ */
+ if (inode->idata_size && !inode->idata) {
inode->idata = malloc(inode->idata_size);
if (!inode->idata)
return -ENOMEM;
@@ -697,7 +702,10 @@ static int erofs_write_unencoded_data(struct erofs_inode *inode,
int erofs_write_unencoded_file(struct erofs_inode *inode, int fd, u64 fpos)
{
- if (cfg.c_chunkbits) {
+ struct erofs_vfile vf = { .fd = fd };
+
+ if (cfg.c_chunkbits &&
+ inode->datasource != EROFS_INODE_DATA_SOURCE_REBUILD_BLOB) {
inode->u.chunkbits = cfg.c_chunkbits;
/* chunk indexes when explicitly specified */
inode->u.chunkformat = 0;
@@ -706,10 +714,15 @@ int erofs_write_unencoded_file(struct erofs_inode *inode, int fd, u64 fpos)
return erofs_blob_write_chunked_file(inode, fd, fpos);
}
+ if (inode->datasource == EROFS_INODE_DATA_SOURCE_REBUILD_BLOB) {
+ if (erofs_io_lseek(&vf, fpos, SEEK_SET) != (off_t)fpos)
+ return -EIO;
+ return erofs_write_unencoded_data(inode, &vf, fpos, true, false);
+ }
+
inode->datalayout = EROFS_INODE_FLAT_INLINE;
/* fallback to all data uncompressed */
- return erofs_write_unencoded_data(inode,
- &(struct erofs_vfile){ .fd = fd }, fpos,
+ return erofs_write_unencoded_data(inode, &vf, fpos,
inode->datasource == EROFS_INODE_DATA_SOURCE_DISKBUF, false);
}
@@ -1508,6 +1521,12 @@ out:
free(inode->i_diskbuf);
inode->i_diskbuf = NULL;
inode->datasource = EROFS_INODE_DATA_SOURCE_NONE;
+ } else if (inode->datasource == EROFS_INODE_DATA_SOURCE_REBUILD_BLOB) {
+ free(inode->rebuild_blobpath);
+ inode->rebuild_blobpath = NULL;
+ inode->datasource = EROFS_INODE_DATA_SOURCE_NONE;
+ DBG_BUGON(ctx->fd < 0);
+ close(ctx->fd);
} else {
DBG_BUGON(ctx->fd < 0);
close(ctx->fd);
@@ -2014,6 +2033,12 @@ static int erofs_mkfs_begin_nondirectory(const struct erofs_mkfs_btctx *btctx,
if (ctx.fd < 0)
return -errno;
break;
+ case EROFS_INODE_DATA_SOURCE_REBUILD_BLOB:
+ ctx.fd = open(inode->rebuild_blobpath, O_RDONLY | O_BINARY);
+ if (ctx.fd < 0)
+ return -errno;
+ ctx.fpos = inode->rebuild_src_dataoff;
+ break;
default:
goto out;
}
@@ -2022,7 +2047,8 @@ static int erofs_mkfs_begin_nondirectory(const struct erofs_mkfs_btctx *btctx,
if (ret < 0)
return ret;
- if (inode->sbi->available_compr_algs &&
+ if (inode->datasource != EROFS_INODE_DATA_SOURCE_REBUILD_BLOB &&
+ inode->sbi->available_compr_algs &&
erofs_file_is_compressible(im, inode)) {
ctx.ictx = erofs_prepare_compressed_file(im, inode);
if (IS_ERR(ctx.ictx))
diff --git a/lib/rebuild.c b/lib/rebuild.c
index 7ab2b499..74bbeda3 100644
--- a/lib/rebuild.c
+++ b/lib/rebuild.c
@@ -14,6 +14,7 @@
#include "erofs/xattr.h"
#include "erofs/blobchunk.h"
#include "erofs/internal.h"
+#include "erofs/io.h"
#include "liberofs_rebuild.h"
#include "liberofs_uuid.h"
@@ -221,6 +222,71 @@ err:
return ret;
}
+static int erofs_rebuild_write_full_data(struct erofs_inode *inode)
+{
+ struct erofs_sb_info *src_sbi = inode->sbi;
+ int err = 0;
+
+ if (inode->datalayout == EROFS_INODE_FLAT_PLAIN) {
+ if (inode->u.i_blkaddr == EROFS_NULL_ADDR) {
+ if (inode->i_size)
+ return -EFSCORRUPTED;
+ return 0;
+ }
+ inode->rebuild_blobpath = strdup(src_sbi->devname);
+ if (!inode->rebuild_blobpath)
+ return -ENOMEM;
+ inode->rebuild_src_dataoff =
+ erofs_pos(src_sbi, erofs_inode_dev_baddr(inode));
+ inode->datasource = EROFS_INODE_DATA_SOURCE_REBUILD_BLOB;
+ } else if (inode->datalayout == EROFS_INODE_FLAT_INLINE) {
+ erofs_blk_t nblocks = erofs_blknr(src_sbi, inode->i_size);
+ unsigned int inline_size = inode->i_size % erofs_blksiz(src_sbi);
+
+ if (nblocks > 0 && inode->u.i_blkaddr != EROFS_NULL_ADDR) {
+ inode->rebuild_blobpath = strdup(src_sbi->devname);
+ if (!inode->rebuild_blobpath)
+ return -ENOMEM;
+ inode->rebuild_src_dataoff =
+ erofs_pos(src_sbi,
+ erofs_inode_dev_baddr(inode));
+ inode->datasource = EROFS_INODE_DATA_SOURCE_REBUILD_BLOB;
+ }
+
+ inode->idata_size = inline_size;
+ if (inline_size > 0) {
+ struct erofs_vfile vf;
+ erofs_off_t tail_offset = erofs_pos(src_sbi, nblocks);
+
+ inode->idata = malloc(inline_size);
+ if (!inode->idata)
+ return -ENOMEM;
+ err = erofs_iopen(&vf, inode);
+ if (err) {
+ free(inode->idata);
+ inode->idata = NULL;
+ return err;
+ }
+ err = erofs_pread(&vf, inode->idata, inline_size,
+ tail_offset);
+ if (err) {
+ free(inode->idata);
+ inode->idata = NULL;
+ return err;
+ }
+ }
+ } else if (inode->datalayout == EROFS_INODE_CHUNK_BASED) {
+ erofs_err("chunk-based files not yet supported: %s",
+ inode->i_srcpath);
+ err = -EOPNOTSUPP;
+ } else if (is_inode_layout_compression(inode)) {
+ erofs_err("compressed files not yet supported: %s",
+ inode->i_srcpath);
+ err = -EOPNOTSUPP;
+ }
+ return err;
+}
+
static int erofs_rebuild_update_inode(struct erofs_sb_info *dst_sb,
struct erofs_inode *inode,
enum erofs_rebuild_datamode datamode)
@@ -265,6 +331,8 @@ static int erofs_rebuild_update_inode(struct erofs_sb_info *dst_sb,
err = erofs_rebuild_write_blob_index(dst_sb, inode);
else if (datamode == EROFS_REBUILD_DATA_RESVSP)
inode->datasource = EROFS_INODE_DATA_SOURCE_RESVSP;
+ else if (datamode == EROFS_REBUILD_DATA_FULL)
+ err = erofs_rebuild_write_full_data(inode);
else
err = -EOPNOTSUPP;
break;
diff --git a/mkfs/main.c b/mkfs/main.c
index 6867478b..d75c97b2 100644
--- a/mkfs/main.c
+++ b/mkfs/main.c
@@ -1756,7 +1756,7 @@ static int erofs_mkfs_rebuild_load_trees(struct erofs_inode *root)
extra_devices += src->extra_devices;
}
- if (datamode != EROFS_REBUILD_DATA_BLOB_INDEX)
+ if (datamode == EROFS_REBUILD_DATA_RESVSP)
return 0;
/* Each blob has either no extra device or only one device for TarFS */
@@ -1766,6 +1766,9 @@ static int erofs_mkfs_rebuild_load_trees(struct erofs_inode *root)
return -EOPNOTSUPP;
}
+ if (datamode == EROFS_REBUILD_DATA_FULL)
+ return 0;
+
ret = erofs_mkfs_init_devices(&g_sbi, rebuild_src_count);
if (ret)
return ret;
@@ -1788,7 +1791,7 @@ static int erofs_mkfs_rebuild_load_trees(struct erofs_inode *root)
memcpy(devs[idx].tag, tag, sizeof(devs[0].tag));
else
/* convert UUID of the source image to a hex string */
- erofs_uuid_unparse_as_tag(src->uuid, (char *)g_sbi.devs[idx].tag);
+ erofs_uuid_unparse_as_tag(src->uuid, (char *)devs[idx].tag);
}
return 0;
}
--
Git-155)
More information about the Linux-erofs
mailing list