This patch introduces experimental support for merging multiple source
images in mkfs. Each source image uses its stored UUID as the device table
tag. The raw block data from each source is copied using
erofs_copy_file_range.
This does not yet support chunk-based files at this time or compressed
images.
Signed-off-by: Lucas Karpinski <[email protected]>
---
lib/cache.c | 6 ++
lib/liberofs_cache.h | 1 +
lib/liberofs_rebuild.h | 5 ++
lib/rebuild.c | 169 ++++++++++++++++++++++++++++++++++++++++++++++++-
mkfs/main.c | 6 +-
5 files changed, 183 insertions(+), 4 deletions(-)
diff --git a/lib/cache.c b/lib/cache.c
index 4c7c386..49742bc 100644
--- a/lib/cache.c
+++ b/lib/cache.c
@@ -544,6 +544,12 @@ erofs_blk_t erofs_total_metablocks(struct erofs_bufmgr
*bmgr)
return bmgr->metablkcnt;
}
+void erofs_bset_tail(struct erofs_bufmgr *bmgr, erofs_blk_t blkaddr)
+{
+ if (blkaddr > bmgr->tail_blkaddr)
+ bmgr->tail_blkaddr = blkaddr;
+}
+
void erofs_buffer_exit(struct erofs_bufmgr *bmgr)
{
DBG_BUGON(__erofs_bflush(bmgr, NULL, true));
diff --git a/lib/liberofs_cache.h b/lib/liberofs_cache.h
index baac609..55e8f25 100644
--- a/lib/liberofs_cache.h
+++ b/lib/liberofs_cache.h
@@ -138,6 +138,7 @@ int erofs_bflush(struct erofs_bufmgr *bmgr,
struct erofs_buffer_block *bb);
void erofs_bdrop(struct erofs_buffer_head *bh, bool tryrevoke);
+void erofs_bset_tail(struct erofs_bufmgr *bmgr, erofs_blk_t blkaddr);
void erofs_buffer_exit(struct erofs_bufmgr *bmgr);
#ifdef __cplusplus
diff --git a/lib/liberofs_rebuild.h b/lib/liberofs_rebuild.h
index d8c4c8a..32d9e2f 100644
--- a/lib/liberofs_rebuild.h
+++ b/lib/liberofs_rebuild.h
@@ -17,6 +17,11 @@ int erofs_rebuild_load_tree(struct erofs_inode *root, struct
erofs_sb_info *sbi,
enum erofs_rebuild_datamode mode,
erofs_blk_t uniaddr_offset);
+int erofs_rebuild_load_trees_full(struct erofs_inode *root,
+ struct erofs_sb_info *sbi,
+ struct list_head *src_list,
+ unsigned int src_count);
+
int erofs_rebuild_load_basedir(struct erofs_inode *dir, u64 *nr_subdirs,
unsigned int *i_nlink);
#endif
diff --git a/lib/rebuild.c b/lib/rebuild.c
index 7e62bc9..16ef0cf 100644
--- a/lib/rebuild.c
+++ b/lib/rebuild.c
@@ -14,8 +14,10 @@
#include "erofs/xattr.h"
#include "erofs/blobchunk.h"
#include "erofs/internal.h"
+#include "erofs/io.h"
#include "liberofs_rebuild.h"
#include "liberofs_uuid.h"
+#include "liberofs_cache.h"
#ifdef HAVE_LINUX_AUFS_TYPE_H
#include <linux/aufs_type.h>
@@ -221,9 +223,60 @@ err:
return ret;
}
+static int erofs_rebuild_write_full_data(struct erofs_inode *inode,
+ erofs_blk_t uniaddr_offset)
+{
+ 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)
+ inode->u.i_blkaddr += uniaddr_offset;
+ } 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->u.i_blkaddr += uniaddr_offset;
+
+ 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)
+ enum erofs_rebuild_datamode datamode,
+ erofs_blk_t uniaddr_offset)
{
int err = 0;
@@ -265,6 +318,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,
uniaddr_offset);
else
err = -EOPNOTSUPP;
break;
@@ -387,7 +442,8 @@ static int erofs_rebuild_dirent_iter(struct
erofs_dir_context *ctx)
inode->i_nlink = 1;
ret = erofs_rebuild_update_inode(&g_sbi, inode,
- rctx->datamode);
+ rctx->datamode,
+ rctx->uniaddr_offset);
if (ret) {
erofs_iput(inode);
goto out;
@@ -425,6 +481,7 @@ int erofs_rebuild_load_tree(struct erofs_inode *root,
struct erofs_sb_info *sbi,
{
struct erofs_inode inode = {};
struct erofs_rebuild_dir_context ctx;
+ struct erofs_inode *mergedir;
char uuid_str[37];
char *fsid = sbi->devname;
int ret;
@@ -447,16 +504,19 @@ int erofs_rebuild_load_tree(struct erofs_inode *root,
struct erofs_sb_info *sbi,
erofs_err("failed to read root inode of %s", fsid);
return ret;
}
+
+ mergedir = root;
inode.i_srcpath = strdup("/");
ctx = (struct erofs_rebuild_dir_context) {
.ctx.dir = &inode,
.ctx.cb = erofs_rebuild_dirent_iter,
- .mergedir = root,
+ .mergedir = mergedir,
.datamode = mode,
.uniaddr_offset = uniaddr_offset,
};
ret = erofs_iterate_dir(&ctx.ctx, false);
+
free(inode.i_srcpath);
return ret;
}
@@ -556,3 +616,106 @@ int erofs_rebuild_load_basedir(struct erofs_inode *dir,
u64 *nr_subdirs,
};
return erofs_iterate_dir(&ctx.ctx, false);
}
+
+static int erofs_rebuild_copy_src_blocks(struct erofs_sb_info *sbi,
+ struct list_head *src_list)
+{
+ struct erofs_device_info *devs = sbi->devs;
+ struct erofs_sb_info *src;
+ erofs_blk_t current_addr = sbi->primarydevice_blocks;
+ int idx = 0;
+
+ list_for_each_entry(src, src_list, list) {
+ erofs_blk_t src_blocks = devs[idx].blocks;
+ u64 src_off = 0, dst_off;
+ u64 len;
+ int src_fd, dst_fd;
+
+ devs[idx].uniaddr = current_addr;
+
+ erofs_info("Copying %s: %u blocks at unified address %u",
+ src->devname, src_blocks, current_addr);
+
+ src_fd = src->bdev.fd;
+ dst_fd = sbi->bdev.fd;
+
+ if (src_fd < 0 || dst_fd < 0) {
+ erofs_err("failed to get file descriptors");
+ return -EINVAL;
+ }
+
+ dst_off = erofs_pos(sbi, current_addr);
+ len = erofs_pos(src, src_blocks);
+
+ while (len > 0) {
+ ssize_t copied = erofs_copy_file_range(
+ src_fd, &src_off, dst_fd, &dst_off, len);
+ if (copied < 0) {
+ erofs_err("failed to copy data from %s: %s",
+ src->devname,
erofs_strerror(-copied));
+ return copied;
+ }
+ if (copied == 0)
+ break;
+ len -= copied;
+ }
+
+ current_addr += src_blocks;
+ idx++;
+ }
+ sbi->primarydevice_blocks = current_addr;
+ return 0;
+}
+
+int erofs_rebuild_load_trees_full(struct erofs_inode *root,
+ struct erofs_sb_info *sbi,
+ struct list_head *src_list,
+ unsigned int src_count)
+{
+ struct erofs_device_info *devs;
+ struct erofs_sb_info *src;
+ int ret, idx = 0;
+
+ ret = erofs_mkfs_init_devices(sbi, src_count);
+ if (ret) {
+ erofs_err("failed to initialize devices: %s",
+ erofs_strerror(ret));
+ return ret;
+ }
+ devs = sbi->devs;
+
+ /* Read source superblocks and populate device table */
+ list_for_each_entry(src, src_list, list) {
+ ret = erofs_read_superblock(src);
+ if (ret) {
+ erofs_err("failed to read superblock of %s: %s",
+ src->devname, erofs_strerror(ret));
+ return ret;
+ }
+ devs[idx].blocks = src->primarydevice_blocks;
+ erofs_uuid_unparse_as_tag(src->uuid, (char *)devs[idx].tag);
+ idx++;
+ }
+
+ /* Copy source data blocks */
+ ret = erofs_rebuild_copy_src_blocks(sbi, src_list);
+ if (ret)
+ return ret;
+
+ /* Advance buffer manager past copied data */
+ erofs_bset_tail(sbi->bmgr, sbi->primarydevice_blocks);
+
+ /* Load filesystem trees with unified block addresses */
+ idx = 0;
+ list_for_each_entry(src, src_list, list) {
+ ret = erofs_rebuild_load_tree(root, src,
+ EROFS_REBUILD_DATA_FULL,
+ devs[idx].uniaddr);
+ if (ret) {
+ erofs_err("failed to load %s", src->devname);
+ return ret;
+ }
+ idx++;
+ }
+ return 0;
+}
diff --git a/mkfs/main.c b/mkfs/main.c
index a8f9a5e..124a024 100644
--- a/mkfs/main.c
+++ b/mkfs/main.c
@@ -15,9 +15,11 @@
#include <getopt.h>
#include "erofs/config.h"
#include "erofs/print.h"
+#include "erofs/io.h"
#include "erofs/importer.h"
#include "erofs/diskbuf.h"
#include "erofs/inode.h"
+#include "erofs/dir.h"
#include "erofs/tar.h"
#include "erofs/dedupe.h"
#include "erofs/xattr.h"
@@ -1726,7 +1728,9 @@ static int erofs_mkfs_rebuild_load_trees(struct
erofs_inode *root)
break;
case EROFS_MKFS_DATA_IMPORT_FULLDATA:
datamode = EROFS_REBUILD_DATA_FULL;
- break;
+ return erofs_rebuild_load_trees_full(root, &g_sbi,
+ &rebuild_src_list,
+ rebuild_src_count);
case EROFS_MKFS_DATA_IMPORT_RVSP:
datamode = EROFS_REBUILD_DATA_RESVSP;
break;