[RFC PATCH 2/3] erofs-utils: refactor inode data source vfiles

Yifan Zhao <[email protected]>
Newsgroups org.ozlabs.lists.linux-erofs
Message-ID <[email protected]>
Collect inode data source state into struct erofs_inode_datasource
instead of carrying separate fields on the inode. Add a vfile wrapper
for local path, diskbuf, and rebuild blob sources.

Assisted-by: Codex:GPT-5.5
Signed-off-by: Yifan Zhao <[email protected]>
---
 include/erofs/blobchunk.h |   4 +-
 include/erofs/internal.h  |  32 ++--
 lib/blobchunk.c           |  24 +--
 lib/compress.c            |   7 +
 lib/inode.c               | 330 +++++++++++++++++++++++++++-----------
 lib/liberofs_compress.h   |   2 +
 lib/rebuild.c             |  20 +--
 lib/remotes/s3.c          |  25 +--
 lib/tar.c                 |  23 +--
 9 files changed, 323 insertions(+), 144 deletions(-)

diff --git a/include/erofs/blobchunk.h b/include/erofs/blobchunk.h
index 1761fdd..e2e306f 100644
--- a/include/erofs/blobchunk.h
+++ b/include/erofs/blobchunk.h
@@ -19,8 +19,8 @@ struct erofs_blobchunk *erofs_get_unhashed_chunk(unsigned int device_id,
 void erofs_inode_fixup_chunkformat(struct erofs_inode *inode);
 int erofs_write_chunk_indexes(struct erofs_inode *inode, struct erofs_vfile *vf,
 			      erofs_off_t off);
-int erofs_blob_write_chunked_file(struct erofs_inode *inode, int fd,
-				  erofs_off_t startoff);
+int erofs_blob_write_chunked_file(struct erofs_inode *inode,
+				  struct erofs_vfile *vf);
 int erofs_write_zero_inode(struct erofs_inode *inode);
 int tarerofs_write_chunkes(struct erofs_inode *inode, erofs_off_t data_offset);
 int erofs_mkfs_dump_blobs(struct erofs_sb_info *sbi);
diff --git a/include/erofs/internal.h b/include/erofs/internal.h
index 2cc9cc8..cba1348 100644
--- a/include/erofs/internal.h
+++ b/include/erofs/internal.h
@@ -204,11 +204,24 @@ EROFS_FEATURE_FUNCS(ishare_xattrs, compat, COMPAT_ISHARE_XATTRS)
 
 struct erofs_diskbuf;
 
-#define EROFS_INODE_DATA_SOURCE_NONE		0
-#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
+enum erofs_inode_data_source_type {
+	EROFS_INODE_DATA_SOURCE_NONE,
+	EROFS_INODE_DATA_SOURCE_LOCALPATH,
+	EROFS_INODE_DATA_SOURCE_DISKBUF,
+	EROFS_INODE_DATA_SOURCE_RESVSP,
+	EROFS_INODE_DATA_SOURCE_REBUILD_BLOB,
+};
+
+struct erofs_inode_datasource {
+	unsigned char type;
+	union {
+		struct erofs_diskbuf *diskbuf;
+		struct {
+			char *path;
+			erofs_off_t dataoff;
+		} rebuild_blob;
+	};
+};
 
 enum erofs_idata_type {
 	EROFS_IDATA_TYPE_RAW,
@@ -256,17 +269,12 @@ struct erofs_inode {
 	} u;
 
 	char *i_srcpath;
-	union {
-		char *i_link;
-		struct erofs_diskbuf *i_diskbuf;
-	};
-	char *rebuild_blobpath;
-	erofs_off_t rebuild_src_dataoff;
+	char *i_link;
+	struct erofs_inode_datasource datasource;
 	unsigned char datalayout;
 	unsigned char inode_isize;
 	/* inline tail-end packing size */
 	unsigned short idata_size;
-	char datasource;
 	bool in_metabox;
 	char idata_type;
 	bool lazy_tailblock;
diff --git a/lib/blobchunk.c b/lib/blobchunk.c
index e39bf68..8015c05 100644
--- a/lib/blobchunk.c
+++ b/lib/blobchunk.c
@@ -300,8 +300,8 @@ static bool erofs_blob_can_merge(struct erofs_sb_info *sbi,
 
 	return false;
 }
-int erofs_blob_write_chunked_file(struct erofs_inode *inode, int fd,
-				  erofs_off_t startoff)
+int erofs_blob_write_chunked_file(struct erofs_inode *inode,
+				  struct erofs_vfile *vf)
 {
 	struct erofs_sb_info *sbi = inode->sbi;
 	unsigned int chunkbits = cfg.c_chunkbits;
@@ -314,7 +314,7 @@ int erofs_blob_write_chunked_file(struct erofs_inode *inode, int fd,
 	int ret;
 
 	/* if the file is fully sparsed, use one big chunk instead */
-	if (lseek(fd, startoff, SEEK_DATA) < 0 && errno == ENXIO) {
+	if (erofs_io_lseek(vf, 0, SEEK_DATA) == -ENXIO) {
 		chunkbits = ilog2(inode->i_size - 1) + 1;
 		if (chunkbits < sbi->blkszbits)
 			chunkbits = sbi->blkszbits;
@@ -366,20 +366,18 @@ int erofs_blob_write_chunked_file(struct erofs_inode *inode, int fd,
 	}
 
 	for (pos = 0; pos < inode->i_size; pos += len) {
-		off_t offset = lseek(fd, pos + startoff, SEEK_DATA);
+		off_t offset = erofs_io_lseek(vf, pos, SEEK_DATA);
 
 		if (offset < 0) {
-			if (errno != ENXIO)
+			if (offset != -ENXIO)
 				offset = pos;
 			else
 				offset = ((pos >> chunkbits) + 1) << chunkbits;
 		} else {
-			offset -= startoff;
-
 			if (offset != (offset & ~(chunksize - 1))) {
 				offset &= ~(chunksize - 1);
-				if (lseek(fd, offset + startoff, SEEK_SET) !=
-					  startoff + offset) {
+				if (erofs_io_lseek(vf, offset, SEEK_SET) !=
+						offset) {
 					ret = -EIO;
 					goto err;
 				}
@@ -404,7 +402,13 @@ int erofs_blob_write_chunked_file(struct erofs_inode *inode, int fd,
 		}
 
 		len = min_t(u64, inode->i_size - pos, chunksize);
-		ret = read(fd, chunkdata, len);
+		ret = erofs_io_lseek(vf, pos, SEEK_SET);
+		if (ret != pos) {
+			ret = ret < 0 ? ret : -EIO;
+			goto err;
+		}
+
+		ret = erofs_io_read(vf, chunkdata, len);
 		if (ret < len) {
 			ret = -EIO;
 			goto err;
diff --git a/lib/compress.c b/lib/compress.c
index e7c60b2..633b69a 100644
--- a/lib/compress.c
+++ b/lib/compress.c
@@ -1914,6 +1914,13 @@ void *erofs_prepare_compressed_file(struct erofs_importer *im,
 	return ictx;
 }
 
+void erofs_bind_compressed_file_with_vfile(struct z_erofs_compress_ictx *ictx,
+					   struct erofs_vfile *vf)
+{
+	ictx->vf = vf;
+	ictx->fpos = 0;
+}
+
 void erofs_bind_compressed_file_with_fd(struct z_erofs_compress_ictx *ictx,
 					int fd, u64 fpos)
 {
diff --git a/lib/inode.c b/lib/inode.c
index c225faa..328215b 100644
--- a/lib/inode.c
+++ b/lib/inode.c
@@ -76,6 +76,13 @@ static const umode_t erofs_dtype_by_umode[EROFS_FT_MAX] = {
 	[EROFS_FT_SYMLINK]	= S_IFLNK
 };
 
+struct erofs_inode_datasource_vfile {
+	struct erofs_vfile vf;
+	struct erofs_inode *inode;
+	u64 base;
+	u64 pos;
+};
+
 umode_t erofs_ftype_to_mode(unsigned int ftype, unsigned int perm)
 {
 	if (ftype >= EROFS_FT_MAX)
@@ -138,6 +145,172 @@ struct erofs_inode *erofs_iget(dev_t dev, ino_t ino)
 	return ret;
 }
 
+static void erofs_inode_free_datasource(struct erofs_inode *inode)
+{
+	switch (inode->datasource.type) {
+	case EROFS_INODE_DATA_SOURCE_DISKBUF:
+		erofs_diskbuf_close(inode->datasource.diskbuf);
+		free(inode->datasource.diskbuf);
+		inode->datasource.diskbuf = NULL;
+		break;
+	case EROFS_INODE_DATA_SOURCE_REBUILD_BLOB:
+		free(inode->datasource.rebuild_blob.path);
+		inode->datasource.rebuild_blob.path = NULL;
+		break;
+	default:
+		return;
+	}
+	inode->datasource.type = EROFS_INODE_DATA_SOURCE_NONE;
+}
+
+static ssize_t erofs_inode_vfpread(struct erofs_vfile *vf, void *buf,
+				   size_t len, u64 offset)
+{
+	struct erofs_inode_datasource_vfile *dvf =
+		container_of(vf, struct erofs_inode_datasource_vfile, vf);
+
+	return erofs_io_pread(&(struct erofs_vfile){ .fd = vf->fd },
+			      buf, len, dvf->base + offset);
+}
+
+static ssize_t erofs_inode_vfread(struct erofs_vfile *vf, void *buf,
+				  size_t len)
+{
+	struct erofs_inode_datasource_vfile *dvf =
+		container_of(vf, struct erofs_inode_datasource_vfile, vf);
+	ssize_t ret;
+
+	ret = erofs_inode_vfpread(vf, buf, len, dvf->pos);
+	if (ret > 0)
+		dvf->pos += ret;
+	return ret;
+}
+
+static off_t erofs_inode_vflseek(struct erofs_vfile *vf, u64 offset,
+				 int whence)
+{
+	struct erofs_inode_datasource_vfile *dvf =
+		container_of(vf, struct erofs_inode_datasource_vfile, vf);
+	u64 pos;
+	off_t ret;
+
+	switch (whence) {
+	case SEEK_SET:
+		if (offset > dvf->inode->i_size)
+			return -EINVAL;
+		pos = offset;
+		break;
+	case SEEK_CUR:
+		if (dvf->pos > dvf->inode->i_size ||
+		    offset > dvf->inode->i_size - dvf->pos)
+			return -EINVAL;
+		pos = dvf->pos + offset;
+		break;
+	case SEEK_END:
+		if (offset)
+			return -EINVAL;
+		pos = dvf->inode->i_size;
+		break;
+	case SEEK_DATA:
+	case SEEK_HOLE:
+		if (offset > dvf->inode->i_size)
+			return -ENXIO;
+		if (offset == dvf->inode->i_size) {
+			if (whence == SEEK_DATA)
+				return -ENXIO;
+			dvf->pos = offset;
+			return dvf->pos;
+		}
+
+		ret = erofs_io_lseek(&(struct erofs_vfile){ .fd = vf->fd },
+				     dvf->base + offset, whence);
+		if (ret < 0)
+			return ret;
+		if (ret < dvf->base)
+			return -EIO;
+		if (whence == SEEK_DATA &&
+		    ret >= dvf->base + dvf->inode->i_size)
+			return -ENXIO;
+		if (ret > dvf->base + dvf->inode->i_size)
+			ret = dvf->base + dvf->inode->i_size;
+		dvf->pos = ret - dvf->base;
+		return dvf->pos;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	dvf->pos = pos;
+	return dvf->pos;
+}
+
+static void erofs_inode_vfclose(struct erofs_vfile *vf)
+{
+	struct erofs_inode_datasource_vfile *dvf =
+		container_of(vf, struct erofs_inode_datasource_vfile, vf);
+	struct erofs_inode *inode = dvf->inode;
+
+	if (vf->fd >= 0 &&
+	    (inode->datasource.type == EROFS_INODE_DATA_SOURCE_LOCALPATH ||
+	     inode->datasource.type == EROFS_INODE_DATA_SOURCE_REBUILD_BLOB))
+		close(vf->fd);
+	vf->fd = -1;
+	erofs_inode_free_datasource(inode);
+	free(dvf);
+}
+
+static struct erofs_vfops erofs_inode_vfops = {
+	.pread = erofs_inode_vfpread,
+	.read = erofs_inode_vfread,
+	.lseek = erofs_inode_vflseek,
+	.close = erofs_inode_vfclose,
+};
+
+static struct erofs_inode_datasource_vfile *erofs_inode_open_data_vfile(
+		struct erofs_inode *inode)
+{
+	struct erofs_inode_datasource_vfile *dvf;
+	int ret;
+
+	if (inode->datasource.type != EROFS_INODE_DATA_SOURCE_LOCALPATH &&
+	    inode->datasource.type != EROFS_INODE_DATA_SOURCE_DISKBUF &&
+	    inode->datasource.type != EROFS_INODE_DATA_SOURCE_REBUILD_BLOB)
+		return NULL;
+
+	dvf = calloc(1, sizeof(*dvf));
+	if (!dvf)
+		return ERR_PTR(-ENOMEM);
+
+	dvf->vf = (struct erofs_vfile) {
+		.ops = &erofs_inode_vfops,
+		.fd = -1,
+	};
+	dvf->inode = inode;
+
+	switch (inode->datasource.type) {
+	case EROFS_INODE_DATA_SOURCE_DISKBUF:
+		dvf->vf.fd = erofs_diskbuf_getfd(inode->datasource.diskbuf,
+						 &dvf->base);
+		break;
+	case EROFS_INODE_DATA_SOURCE_LOCALPATH:
+		dvf->vf.fd = open(inode->i_srcpath, O_RDONLY | O_BINARY);
+		break;
+	case EROFS_INODE_DATA_SOURCE_REBUILD_BLOB:
+		dvf->vf.fd = open(inode->datasource.rebuild_blob.path,
+				  O_RDONLY | O_BINARY);
+		dvf->base = inode->datasource.rebuild_blob.dataoff;
+		break;
+	default:
+		break;
+	}
+	if (dvf->vf.fd < 0) {
+		ret = inode->datasource.type == EROFS_INODE_DATA_SOURCE_DISKBUF ?
+		      dvf->vf.fd : -errno;
+		free(dvf);
+		return ERR_PTR(ret);
+	}
+	return dvf;
+}
+
 unsigned int erofs_iput(struct erofs_inode *inode)
 {
 	struct erofs_dentry *d, *t;
@@ -155,14 +328,8 @@ unsigned int erofs_iput(struct erofs_inode *inode)
 	if (!erofs_is_special_identifier(inode->i_srcpath))
 		free(inode->i_srcpath);
 
-	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);
-	}
+	erofs_inode_free_datasource(inode);
+	free(inode->i_link);
 
 	if (inode->datalayout == EROFS_INODE_CHUNK_BASED)
 		free(inode->chunkindexes);
@@ -700,30 +867,32 @@ static int erofs_write_unencoded_data(struct erofs_inode *inode,
 	return 0;
 }
 
-int erofs_write_unencoded_file(struct erofs_inode *inode, int fd, u64 fpos)
+static int erofs_write_unencoded_file(struct erofs_inode *inode,
+				      struct erofs_inode_datasource_vfile *src)
 {
-	struct erofs_vfile vf = { .fd = fd };
+	int ret;
+
+	ret = erofs_io_lseek(&src->vf, 0, SEEK_SET);
+	if (ret)
+		return ret;
 
 	if (cfg.c_chunkbits &&
-	    inode->datasource != EROFS_INODE_DATA_SOURCE_REBUILD_BLOB) {
+	    inode->datasource.type != EROFS_INODE_DATA_SOURCE_REBUILD_BLOB) {
 		inode->u.chunkbits = cfg.c_chunkbits;
 		/* chunk indexes when explicitly specified */
 		inode->u.chunkformat = 0;
 		if (cfg.c_force_chunkformat == FORCE_INODE_CHUNK_INDEXES)
 			inode->u.chunkformat = EROFS_CHUNK_FORMAT_INDEXES;
-		return erofs_blob_write_chunked_file(inode, fd, fpos);
+		return erofs_blob_write_chunked_file(inode, &src->vf);
 	}
 
-	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;
+	if (inode->datasource.type != EROFS_INODE_DATA_SOURCE_REBUILD_BLOB)
+		inode->datalayout = EROFS_INODE_FLAT_INLINE;
 	/* fallback to all data uncompressed */
-	return erofs_write_unencoded_data(inode, &vf, fpos,
-			inode->datasource == EROFS_INODE_DATA_SOURCE_DISKBUF, false);
+	return erofs_write_unencoded_data(inode, &src->vf, 0,
+			inode->datasource.type == EROFS_INODE_DATA_SOURCE_DISKBUF ||
+			inode->datasource.type == EROFS_INODE_DATA_SOURCE_REBUILD_BLOB,
+			false);
 }
 
 static int erofs_write_dir_file(const struct erofs_importer *im,
@@ -1428,7 +1597,7 @@ static struct erofs_inode *erofs_iget_from_local(struct erofs_importer *im,
 		erofs_iput(inode);
 		return ERR_PTR(ret);
 	}
-	inode->datasource = EROFS_INODE_DATA_SOURCE_LOCALPATH;
+	inode->datasource.type = EROFS_INODE_DATA_SOURCE_LOCALPATH;
 	return inode;
 }
 
@@ -1489,9 +1658,8 @@ static int erofs_inode_reserve_data_blocks(struct erofs_inode *inode)
 
 struct erofs_mkfs_job_ndir_ctx {
 	struct erofs_inode *inode;
+	struct erofs_inode_datasource_vfile *src;
 	void *ictx;
-	int fd;
-	u64 fpos;
 };
 
 static int erofs_mkfs_job_write_file(struct erofs_mkfs_job_ndir_ctx *ctx)
@@ -1499,39 +1667,20 @@ static int erofs_mkfs_job_write_file(struct erofs_mkfs_job_ndir_ctx *ctx)
 	struct erofs_inode *inode = ctx->inode;
 	int ret;
 
-	if (inode->datasource == EROFS_INODE_DATA_SOURCE_DISKBUF &&
-	    lseek(ctx->fd, ctx->fpos, SEEK_SET) < 0) {
-		ret = -errno;
-		goto out;
-	}
-
 	if (ctx->ictx) {
+		ret = erofs_io_lseek(&ctx->src->vf, 0, SEEK_SET);
+		if (ret)
+			goto out;
 		ret = erofs_write_compressed_file(ctx->ictx);
 		if (ret != -ENOSPC)
 			goto out;
-		if (lseek(ctx->fd, ctx->fpos, SEEK_SET) < 0) {
-			ret = -errno;
-			goto out;
-		}
 	}
 	/* fallback to all data uncompressed */
-	ret = erofs_write_unencoded_file(inode, ctx->fd, ctx->fpos);
+	ret = erofs_write_unencoded_file(inode, ctx->src);
 out:
-	if (inode->datasource == EROFS_INODE_DATA_SOURCE_DISKBUF) {
-		erofs_diskbuf_close(inode->i_diskbuf);
-		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);
-	}
+	DBG_BUGON(!ctx->src || ctx->src->vf.fd < 0);
+	erofs_io_close(&ctx->src->vf);
+	ctx->src = NULL;
 	return ret;
 }
 
@@ -1549,7 +1698,7 @@ static int erofs_mkfs_handle_nondirectory(const struct erofs_mkfs_btctx *btctx,
 	ret = erofs_prepare_xattr_ibody(inode,
 					btctx->incremental && IS_ROOT(inode));
 	if (ret)
-		return ret;
+		goto out;
 
 	if (S_ISLNK(inode->i_mode)) {
 		char *symlink = inode->i_link;
@@ -1568,16 +1717,22 @@ static int erofs_mkfs_handle_nondirectory(const struct erofs_mkfs_btctx *btctx,
 		free(symlink);
 		inode->i_link = NULL;
 	} else if (inode->i_size) {
-		if (inode->datasource == EROFS_INODE_DATA_SOURCE_RESVSP)
+		if (inode->datasource.type == EROFS_INODE_DATA_SOURCE_RESVSP)
 			ret = erofs_inode_reserve_data_blocks(inode);
-		else if (ctx->fd >= 0)
+		else if (ctx->src)
 			ret = erofs_mkfs_job_write_file(ctx);
 	}
 	if (ret)
-		return ret;
+		goto out;
 	erofs_prepare_inode_buffer(btctx->im, inode);
 	erofs_write_tail_end(btctx->im, inode);
 	return 0;
+out:
+	if (ctx->src) {
+		erofs_io_close(&ctx->src->vf);
+		ctx->src = NULL;
+	}
+	return ret;
 }
 
 static int erofs_mkfs_create_directory(const struct erofs_mkfs_btctx *ctx,
@@ -1760,7 +1915,8 @@ static int erofs_mkfs_go(const struct erofs_mkfs_btctx *ctx,
 	struct erofs_mkfs_jobitem item;
 
 	item.type = type;
-	memcpy(&item.u, elem, size);
+	if (size)
+		memcpy(&item.u, elem, size);
 	return erofs_mkfs_jobfn(ctx, &item);
 }
 static void erofs_mkfs_flushjobs(struct erofs_sb_info *sbi)
@@ -1983,12 +2139,12 @@ static int erofs_prepare_dir_inode(const struct erofs_mkfs_btctx *ctx,
 	return 0;
 }
 
-static int erofs_set_inode_fingerprint(struct erofs_inode *inode, int fd,
-				       erofs_off_t pos)
+static int erofs_set_inode_fingerprint(struct erofs_inode *inode,
+				       struct erofs_vfile *vf)
 {
 	u8 ishare_xattr_prefix_id = inode->sbi->ishare_xattr_prefix_id;
 	erofs_off_t remaining = inode->i_size;
-	struct erofs_vfile vf = { .fd = fd };
+	erofs_off_t pos = 0;
 	struct sha256_state md;
 	u8 out[32 + sizeof("sha256:") - 1];
 	int ret;
@@ -1999,7 +2155,7 @@ static int erofs_set_inode_fingerprint(struct erofs_inode *inode, int fd,
 	do {
 		u8 buf[32768];
 
-		ret = erofs_io_pread(&vf, buf,
+		ret = erofs_io_pread(vf, buf,
 				     min_t(u64, remaining, sizeof(buf)), pos);
 		if (ret < 0)
 			return ret;
@@ -2018,51 +2174,45 @@ static int erofs_mkfs_begin_nondirectory(const struct erofs_mkfs_btctx *btctx,
 					 struct erofs_inode *inode)
 {
 	struct erofs_importer *im = btctx->im;
-	struct erofs_mkfs_job_ndir_ctx ctx =
-		{ .inode = inode, .fd = -1 };
+	struct erofs_mkfs_job_ndir_ctx ctx = { .inode = inode };
 	int ret;
 
 	if (S_ISREG(inode->i_mode) && inode->i_size) {
-		switch (inode->datasource) {
-		case EROFS_INODE_DATA_SOURCE_DISKBUF:
-			ctx.fd = erofs_diskbuf_getfd(inode->i_diskbuf, &ctx.fpos);
-			if (ctx.fd < 0)
-				return ctx.fd;
-			break;
-		case EROFS_INODE_DATA_SOURCE_LOCALPATH:
-			ctx.fd = open(inode->i_srcpath, O_RDONLY | O_BINARY);
-			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:
+		ctx.src = erofs_inode_open_data_vfile(inode);
+		if (IS_ERR(ctx.src))
+			return PTR_ERR(ctx.src);
+		if (!ctx.src)
 			goto out;
-		}
 
-		ret = erofs_set_inode_fingerprint(inode, ctx.fd, ctx.fpos);
+		ret = erofs_set_inode_fingerprint(inode, &ctx.src->vf);
 		if (ret < 0)
-			return ret;
+			goto err_close;
 
-		if (inode->datasource != EROFS_INODE_DATA_SOURCE_REBUILD_BLOB &&
+		if (inode->datasource.type != 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))
-				return PTR_ERR(ctx.ictx);
-			erofs_bind_compressed_file_with_fd(ctx.ictx,
-							   ctx.fd, ctx.fpos);
+			if (IS_ERR(ctx.ictx)) {
+				ret = PTR_ERR(ctx.ictx);
+				goto err_close;
+			}
+			erofs_bind_compressed_file_with_vfile(ctx.ictx,
+							      &ctx.src->vf);
 			ret = erofs_begin_compressed_file(ctx.ictx);
 			if (ret)
-				return ret;
+				goto err_close;
 		}
 	}
 out:
-	return erofs_mkfs_go(btctx, EROFS_MKFS_JOB_NDIR, &ctx, sizeof(ctx));
+	ret = erofs_mkfs_go(btctx, EROFS_MKFS_JOB_NDIR, &ctx, sizeof(ctx));
+#ifdef EROFS_MT_ENABLED
+	if (ret && ctx.src)
+		erofs_io_close(&ctx.src->vf);
+#endif
+	return ret;
+err_close:
+	erofs_io_close(&ctx.src->vf);
+	return ret;
 }
 
 static int erofs_mkfs_handle_inode(const struct erofs_mkfs_btctx *ctx,
@@ -2402,7 +2552,7 @@ struct erofs_inode *erofs_mkfs_build_special_from_fd(struct erofs_importer *im,
 	inode->datalayout = EROFS_INODE_FLAT_INLINE;
 	ret = erofs_write_unencoded_data(inode,
 			&(struct erofs_vfile){ .fd = fd }, 0,
-			inode->datasource == EROFS_INODE_DATA_SOURCE_DISKBUF,
+			inode->datasource.type == EROFS_INODE_DATA_SOURCE_DISKBUF,
 			false);
 	if (ret)
 		return ERR_PTR(ret);
diff --git a/lib/liberofs_compress.h b/lib/liberofs_compress.h
index da6eb1a..55a8943 100644
--- a/lib/liberofs_compress.h
+++ b/lib/liberofs_compress.h
@@ -17,6 +17,8 @@ struct z_erofs_compress_ictx;
 void z_erofs_drop_inline_pcluster(struct erofs_inode *inode);
 void *erofs_prepare_compressed_file(struct erofs_importer *im,
 				    struct erofs_inode *inode);
+void erofs_bind_compressed_file_with_vfile(struct z_erofs_compress_ictx *ictx,
+					   struct erofs_vfile *vf);
 void erofs_bind_compressed_file_with_fd(struct z_erofs_compress_ictx *ictx,
 					int fd, u64 fpos);
 int erofs_begin_compressed_file(struct z_erofs_compress_ictx *ictx);
diff --git a/lib/rebuild.c b/lib/rebuild.c
index 51dfe18..abe3b74 100644
--- a/lib/rebuild.c
+++ b/lib/rebuild.c
@@ -233,24 +233,26 @@ static int erofs_rebuild_write_full_data(struct erofs_inode *inode)
 				return -EFSCORRUPTED;
 			return 0;
 		}
-		inode->rebuild_blobpath = strdup(src_sbi->devname);
-		if (!inode->rebuild_blobpath)
+		inode->datasource.rebuild_blob.path = strdup(src_sbi->devname);
+		if (!inode->datasource.rebuild_blob.path)
 			return -ENOMEM;
-		inode->rebuild_src_dataoff =
+		inode->datasource.rebuild_blob.dataoff =
 			erofs_pos(src_sbi, erofs_inode_dev_baddr(inode));
-		inode->datasource = EROFS_INODE_DATA_SOURCE_REBUILD_BLOB;
+		inode->datasource.type = 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)
+			inode->datasource.rebuild_blob.path =
+				strdup(src_sbi->devname);
+			if (!inode->datasource.rebuild_blob.path)
 				return -ENOMEM;
-			inode->rebuild_src_dataoff =
+			inode->datasource.rebuild_blob.dataoff =
 				erofs_pos(src_sbi,
 					  erofs_inode_dev_baddr(inode));
-			inode->datasource = EROFS_INODE_DATA_SOURCE_REBUILD_BLOB;
+			inode->datasource.type =
+				EROFS_INODE_DATA_SOURCE_REBUILD_BLOB;
 		}
 
 		inode->idata_size = inline_size;
@@ -330,7 +332,7 @@ static int erofs_rebuild_update_inode(struct erofs_sb_info *dst_sb,
 		if (datamode == EROFS_REBUILD_DATA_BLOB_INDEX)
 			err = erofs_rebuild_write_blob_index(dst_sb, inode);
 		else if (datamode == EROFS_REBUILD_DATA_RESVSP)
-			inode->datasource = EROFS_INODE_DATA_SOURCE_RESVSP;
+			inode->datasource.type = EROFS_INODE_DATA_SOURCE_RESVSP;
 		else if (datamode == EROFS_REBUILD_DATA_FULL)
 			err = erofs_rebuild_write_full_data(inode);
 		else
diff --git a/lib/remotes/s3.c b/lib/remotes/s3.c
index 245efe5..fb8a1a5 100644
--- a/lib/remotes/s3.c
+++ b/lib/remotes/s3.c
@@ -1063,33 +1063,36 @@ static int s3erofs_remote_getobject(struct erofs_importer *im,
 			return ret;
 		resp.vf = &sbi->bdev;
 		resp.pos = erofs_pos(inode->sbi, inode->u.i_blkaddr);
-		inode->datasource = EROFS_INODE_DATA_SOURCE_NONE;
+		inode->datasource.type = EROFS_INODE_DATA_SOURCE_NONE;
 	} else {
-		if (!inode->i_diskbuf) {
-			inode->i_diskbuf = calloc(1, sizeof(*inode->i_diskbuf));
-			if (!inode->i_diskbuf)
+		if (!inode->datasource.diskbuf) {
+			inode->datasource.diskbuf =
+				calloc(1, sizeof(*inode->datasource.diskbuf));
+			if (!inode->datasource.diskbuf)
 				return -ENOSPC;
 		} else {
-			erofs_diskbuf_close(inode->i_diskbuf);
+			erofs_diskbuf_close(inode->datasource.diskbuf);
 		}
 
 		vf = (struct erofs_vfile) {.fd =
-			erofs_diskbuf_reserve(inode->i_diskbuf, 0, &diskbuf_off)};
+			erofs_diskbuf_reserve(inode->datasource.diskbuf, 0,
+					       &diskbuf_off)};
 		if (vf.fd < 0)
 			return -EBADF;
 		resp.pos = diskbuf_off;
 		resp.vf = &vf;
-		inode->datasource = EROFS_INODE_DATA_SOURCE_DISKBUF;
+		inode->datasource.type = EROFS_INODE_DATA_SOURCE_DISKBUF;
 	}
 	resp.end = resp.pos + inode->i_size;
 
 	ret = s3erofs_request_perform(s3, &req, &resp);
 	if (resp.vf == &vf) {
-		erofs_diskbuf_commit(inode->i_diskbuf, resp.pos - diskbuf_off);
+		erofs_diskbuf_commit(inode->datasource.diskbuf,
+				     resp.pos - diskbuf_off);
 		if (ret) {
-			erofs_diskbuf_close(inode->i_diskbuf);
-			inode->i_diskbuf = NULL;
-			inode->datasource = EROFS_INODE_DATA_SOURCE_NONE;
+			erofs_diskbuf_close(inode->datasource.diskbuf);
+			inode->datasource.diskbuf = NULL;
+			inode->datasource.type = EROFS_INODE_DATA_SOURCE_NONE;
 		}
 	}
 	if (ret)
diff --git a/lib/tar.c b/lib/tar.c
index 9e53af5..420275a 100644
--- a/lib/tar.c
+++ b/lib/tar.c
@@ -715,7 +715,7 @@ static int tarerofs_write_uncompressed_file(struct erofs_inode *inode,
 			erofs_sha256_process(&md, buf, ret);
 	}
 	inode->idata_size = 0;
-	inode->datasource = EROFS_INODE_DATA_SOURCE_NONE;
+	inode->datasource.type = EROFS_INODE_DATA_SOURCE_NONE;
 	if (ret < 0)
 		return ret;
 
@@ -738,15 +738,16 @@ static int tarerofs_write_file_data(struct erofs_inode *inode,
 	int fd, nread;
 	u64 off, j;
 
-	if (!inode->i_diskbuf) {
-		inode->i_diskbuf = calloc(1, sizeof(*inode->i_diskbuf));
-		if (!inode->i_diskbuf)
+	if (!inode->datasource.diskbuf) {
+		inode->datasource.diskbuf =
+			calloc(1, sizeof(*inode->datasource.diskbuf));
+		if (!inode->datasource.diskbuf)
 			return -ENOSPC;
 	} else {
-		erofs_diskbuf_close(inode->i_diskbuf);
+		erofs_diskbuf_close(inode->datasource.diskbuf);
 	}
 
-	fd = erofs_diskbuf_reserve(inode->i_diskbuf, 0, &off);
+	fd = erofs_diskbuf_reserve(inode->datasource.diskbuf, 0, &off);
 	if (fd < 0)
 		return -EBADF;
 
@@ -766,8 +767,8 @@ static int tarerofs_write_file_data(struct erofs_inode *inode,
 		j -= nread;
 		off += nread;
 	} while (j);
-	erofs_diskbuf_commit(inode->i_diskbuf, inode->i_size);
-	inode->datasource = EROFS_INODE_DATA_SOURCE_DISKBUF;
+	erofs_diskbuf_commit(inode->datasource.diskbuf, inode->i_size);
+	inode->datasource.type = EROFS_INODE_DATA_SOURCE_DISKBUF;
 	return nread < 0 ? nread : 0;
 }
 
@@ -1180,14 +1181,16 @@ new_inode:
 			} else if (tar->ddtaridx_mode) {
 				dataoff = le64_to_cpu(*(__le64 *)(th->devmajor));
 				if (tar->rvsp_mode) {
-					inode->datasource = EROFS_INODE_DATA_SOURCE_RESVSP;
+					inode->datasource.type =
+						EROFS_INODE_DATA_SOURCE_RESVSP;
 					inode->i_ino[1] = dataoff;
 					ret = 0;
 				} else {
 					ret = tarerofs_write_chunkes(inode, dataoff);
 				}
 			} else if (tar->rvsp_mode) {
-				inode->datasource = EROFS_INODE_DATA_SOURCE_RESVSP;
+				inode->datasource.type =
+					EROFS_INODE_DATA_SOURCE_RESVSP;
 				inode->i_ino[1] = dataoff;
 				if (erofs_iostream_lskip(&tar->ios, inode->i_size))
 					ret = -EIO;
-- 
2.47.3
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.