[PATCH v3 3/4] erofs-utils: mfks: add rebuild FULLDATA for combined EROFS images

zhaoyifan (H) zhaoyifan28 at huawei.com
Wed Apr 15 13:35:06 AEST 2026


This patch incorrectly handles inline inode:

Reproduce in erofs-utils directory:
     mkfs/mkfs.erofs 1.erofs man/
     mkfs/mkfs.erofs 2.erofs docs/
     mkfs/mkfs.erofs --clean=data merged.erofs 1.erofs 2.erofs

Then PERFORMANCE.md in merged.erofs contains incorrect data after offset 
0x2000.

Fixed with following diff:

diff --git a/lib/inode.c b/lib/inode.c
   index bd10e26..36dce56 100644
   --- a/lib/inode.c
   +++ b/lib/inode.c
   @@ -683,6 +683,13 @@ static int erofs_write_unencoded_data(struct 
erofs_inode *inode,

         /* read the tail-end data */
         if (inode->idata_size) {
   +             /*
   +              * If inode->idata is already present, the caller has 
prepared
   +              * the tail data and nothing more needs to be done here.
   +              */
   +             if (inode->idata)
   +                     return 0;
   +
                 inode->idata = malloc(inode->idata_size);
                 if (!inode->idata)
                         return -ENOMEM;


On 2026/4/15 3:10, Lucas Karpinski wrote:
> This patch introduces 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              | 31 ++++++++++++++++++---
>   lib/rebuild.c            | 70 ++++++++++++++++++++++++++++++++++++++++++++++++
>   mkfs/main.c              |  7 +++--
>   4 files changed, 105 insertions(+), 6 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..bd10e267 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);
>   	}
> @@ -697,7 +699,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 +711,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 +1518,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 +2030,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 +2044,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..3785afd0 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"

Unnecessary include `liberofs_cache.h`


Thanks,

Yifan Zhao

>   
>   #ifdef HAVE_LINUX_AUFS_TYPE_H
>   #include <linux/aufs_type.h>
> @@ -221,6 +223,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 +332,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;
> @@ -553,3 +622,4 @@ int erofs_rebuild_load_basedir(struct erofs_inode *dir, u64 *nr_subdirs,
>   	};
>   	return erofs_iterate_dir(&ctx.ctx, false);
>   }
> +
> 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;
>   }
>


More information about the Linux-erofs mailing list