[PATCH 6/9] erofs-utils: support building image with reserved space

Gao Xiang hsiangkao at linux.alibaba.com
Tue Jun 18 16:48:56 AEST 2024


A new mode is prepared in order to preallocate/reserve data blocks only
since some applications tend to fill data after EROFS images are built.

Signed-off-by: Gao Xiang <hsiangkao at linux.alibaba.com>
---
 include/erofs/internal.h |  7 +++-
 include/erofs/rebuild.h  |  9 ++++-
 lib/inode.c              | 45 +++++++++++++++++----
 lib/rebuild.c            | 84 ++++++++++++++++++++++++----------------
 lib/tar.c                |  2 +-
 mkfs/main.c              |  3 +-
 6 files changed, 104 insertions(+), 46 deletions(-)

diff --git a/include/erofs/internal.h b/include/erofs/internal.h
index 39cf066..1d6496a 100644
--- a/include/erofs/internal.h
+++ b/include/erofs/internal.h
@@ -170,6 +170,11 @@ EROFS_FEATURE_FUNCS(xattr_filter, compat, COMPAT_XATTR_FILTER)
 
 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
+
 struct erofs_inode {
 	struct list_head i_hash, i_subdirs, i_xattrs;
 
@@ -216,9 +221,9 @@ struct erofs_inode {
 	unsigned char inode_isize;
 	/* inline tail-end packing size */
 	unsigned short idata_size;
+	char datasource;
 	bool compressed_idata;
 	bool lazy_tailblock;
-	bool with_diskbuf;
 	bool opaque;
 	/* OVL: non-merge dir that may contain whiteout entries */
 	bool whiteouts;
diff --git a/include/erofs/rebuild.h b/include/erofs/rebuild.h
index e99ce74..59b2f6f 100644
--- a/include/erofs/rebuild.h
+++ b/include/erofs/rebuild.h
@@ -9,10 +9,17 @@ extern "C"
 
 #include "internal.h"
 
+enum erofs_rebuild_datamode {
+	EROFS_REBUILD_DATA_BLOB_INDEX,
+	EROFS_REBUILD_DATA_RESVSP,
+	EROFS_REBUILD_DATA_FULL,
+};
+
 struct erofs_dentry *erofs_rebuild_get_dentry(struct erofs_inode *pwd,
 		char *path, bool aufs, bool *whout, bool *opq, bool to_head);
 
-int erofs_rebuild_load_tree(struct erofs_inode *root, struct erofs_sb_info *sbi);
+int erofs_rebuild_load_tree(struct erofs_inode *root, struct erofs_sb_info *sbi,
+			    enum erofs_rebuild_datamode mode);
 
 #ifdef __cplusplus
 }
diff --git a/lib/inode.c b/lib/inode.c
index 9a1fd60..e1706a7 100644
--- a/lib/inode.c
+++ b/lib/inode.c
@@ -143,7 +143,8 @@ unsigned int erofs_iput(struct erofs_inode *inode)
 	list_del(&inode->i_hash);
 	if (inode->i_srcpath)
 		free(inode->i_srcpath);
-	if (inode->with_diskbuf) {
+
+	if (inode->datasource == EROFS_INODE_DATA_SOURCE_DISKBUF) {
 		erofs_diskbuf_close(inode->i_diskbuf);
 		free(inode->i_diskbuf);
 	} else if (inode->i_link) {
@@ -1171,6 +1172,30 @@ static void erofs_fixup_meta_blkaddr(struct erofs_inode *rootdir)
 	rootdir->nid = (off - meta_offset) >> EROFS_ISLOTBITS;
 }
 
+static int erofs_inode_reserve_data_blocks(struct erofs_inode *inode)
+{
+	struct erofs_sb_info *sbi = inode->sbi;
+	erofs_off_t alignedsz = round_up(inode->i_size, erofs_blksiz(sbi));
+	erofs_blk_t nblocks = alignedsz >> sbi->blkszbits;
+	struct erofs_buffer_head *bh;
+
+	/* allocate data blocks */
+	bh = erofs_balloc(DATA, alignedsz, 0, 0);
+	if (IS_ERR(bh))
+		return PTR_ERR(bh);
+
+	/* get blkaddr of the bh */
+	(void)erofs_mapbh(bh->block);
+
+	/* write blocks except for the tail-end block */
+	inode->u.i_blkaddr = bh->block->blkaddr;
+	erofs_bdrop(bh, false);
+
+	inode->datalayout = EROFS_INODE_FLAT_PLAIN;
+	tarerofs_blocklist_write(inode->u.i_blkaddr, nblocks, inode->i_ino[1]);
+	return 0;
+}
+
 struct erofs_mkfs_job_ndir_ctx {
 	struct erofs_inode *inode;
 	void *ictx;
@@ -1183,7 +1208,8 @@ static int erofs_mkfs_job_write_file(struct erofs_mkfs_job_ndir_ctx *ctx)
 	struct erofs_inode *inode = ctx->inode;
 	int ret;
 
-	if (inode->with_diskbuf && lseek(ctx->fd, ctx->fpos, SEEK_SET) < 0) {
+	if (inode->datasource == EROFS_INODE_DATA_SOURCE_DISKBUF &&
+	    lseek(ctx->fd, ctx->fpos, SEEK_SET) < 0) {
 		ret = -errno;
 		goto out;
 	}
@@ -1200,11 +1226,11 @@ static int erofs_mkfs_job_write_file(struct erofs_mkfs_job_ndir_ctx *ctx)
 	/* fallback to all data uncompressed */
 	ret = erofs_write_unencoded_file(inode, ctx->fd, ctx->fpos);
 out:
-	if (inode->with_diskbuf) {
+	if (inode->datasource == EROFS_INODE_DATA_SOURCE_DISKBUF) {
 		erofs_diskbuf_close(inode->i_diskbuf);
 		free(inode->i_diskbuf);
 		inode->i_diskbuf = NULL;
-		inode->with_diskbuf = false;
+		inode->datasource = EROFS_INODE_DATA_SOURCE_NONE;
 	} else {
 		close(ctx->fd);
 	}
@@ -1232,8 +1258,11 @@ static int erofs_mkfs_handle_nondirectory(struct erofs_mkfs_job_ndir_ctx *ctx)
 		ret = erofs_write_file_from_buffer(inode, symlink);
 		free(symlink);
 		inode->i_link = NULL;
-	} else if (inode->i_size && ctx->fd >= 0) {
-		ret = erofs_mkfs_job_write_file(ctx);
+	} else if (inode->i_size) {
+		if (inode->datasource == EROFS_INODE_DATA_SOURCE_RESVSP)
+			ret = erofs_inode_reserve_data_blocks(inode);
+		else if (ctx->fd >= 0)
+			ret = erofs_mkfs_job_write_file(ctx);
 	}
 	if (ret)
 		return ret;
@@ -1624,8 +1653,8 @@ static int erofs_rebuild_handle_inode(struct erofs_inode *inode,
 		struct erofs_mkfs_job_ndir_ctx ctx =
 			{ .inode = inode, .fd = -1 };
 
-		if (!S_ISLNK(inode->i_mode) && inode->i_size &&
-		    inode->with_diskbuf) {
+		if (S_ISREG(inode->i_mode) && inode->i_size &&
+		    inode->datasource == EROFS_INODE_DATA_SOURCE_DISKBUF) {
 			ctx.fd = erofs_diskbuf_getfd(inode->i_diskbuf, &ctx.fpos);
 			if (ctx.fd < 0)
 				return ret;
diff --git a/lib/rebuild.c b/lib/rebuild.c
index 6f2e301..9c1e8f8 100644
--- a/lib/rebuild.c
+++ b/lib/rebuild.c
@@ -128,7 +128,8 @@ struct erofs_dentry *erofs_rebuild_get_dentry(struct erofs_inode *pwd,
 	return d;
 }
 
-static int erofs_rebuild_fixup_inode_index(struct erofs_inode *inode)
+static int erofs_rebuild_write_blob_index(struct erofs_sb_info *dst_sb,
+					  struct erofs_inode *inode)
 {
 	int ret;
 	unsigned int count, unit, chunkbits, i;
@@ -137,26 +138,26 @@ static int erofs_rebuild_fixup_inode_index(struct erofs_inode *inode)
 	erofs_blk_t blkaddr;
 
 	/* TODO: fill data map in other layouts */
-	if (inode->datalayout != EROFS_INODE_CHUNK_BASED &&
-	    inode->datalayout != EROFS_INODE_FLAT_PLAIN) {
-		erofs_err("%s: unsupported datalayout %d", inode->i_srcpath, inode->datalayout);
-		return -EOPNOTSUPP;
-	}
-
-	if (inode->sbi->extra_devices) {
+	if (inode->datalayout == EROFS_INODE_CHUNK_BASED) {
 		chunkbits = inode->u.chunkbits;
-		if (chunkbits < sbi.blkszbits) {
-			erofs_err("%s: chunk size %u is too small to fit the target block size %u",
-				  inode->i_srcpath, 1U << chunkbits, 1U << sbi.blkszbits);
+		if (chunkbits < dst_sb->blkszbits) {
+			erofs_err("%s: chunk size %u is smaller than the target block size %u",
+				  inode->i_srcpath, 1U << chunkbits,
+				  1U << dst_sb->blkszbits);
 			return -EINVAL;
 		}
-	} else {
+	} else if (inode->datalayout == EROFS_INODE_FLAT_PLAIN) {
 		chunkbits = ilog2(inode->i_size - 1) + 1;
-		if (chunkbits < sbi.blkszbits)
-			chunkbits = sbi.blkszbits;
-		if (chunkbits - sbi.blkszbits > EROFS_CHUNK_FORMAT_BLKBITS_MASK)
-			chunkbits = EROFS_CHUNK_FORMAT_BLKBITS_MASK + sbi.blkszbits;
+		if (chunkbits < dst_sb->blkszbits)
+			chunkbits = dst_sb->blkszbits;
+		if (chunkbits - dst_sb->blkszbits > EROFS_CHUNK_FORMAT_BLKBITS_MASK)
+			chunkbits = EROFS_CHUNK_FORMAT_BLKBITS_MASK + dst_sb->blkszbits;
+	} else {
+		erofs_err("%s: unsupported datalayout %d ", inode->i_srcpath,
+			  inode->datalayout);
+		return -EOPNOTSUPP;
 	}
+
 	chunksize = 1ULL << chunkbits;
 	count = DIV_ROUND_UP(inode->i_size, chunksize);
 
@@ -178,7 +179,7 @@ static int erofs_rebuild_fixup_inode_index(struct erofs_inode *inode)
 		if (ret)
 			goto err;
 
-		blkaddr = erofs_blknr(&sbi, map.m_pa);
+		blkaddr = erofs_blknr(dst_sb, map.m_pa);
 		chunk = erofs_get_unhashed_chunk(inode->dev, blkaddr, 0);
 		if (IS_ERR(chunk)) {
 			ret = PTR_ERR(chunk);
@@ -189,7 +190,7 @@ static int erofs_rebuild_fixup_inode_index(struct erofs_inode *inode)
 	}
 	inode->datalayout = EROFS_INODE_CHUNK_BASED;
 	inode->u.chunkformat = EROFS_CHUNK_FORMAT_INDEXES;
-	inode->u.chunkformat |= chunkbits - sbi.blkszbits;
+	inode->u.chunkformat |= chunkbits - dst_sb->blkszbits;
 	return 0;
 err:
 	free(inode->chunkindexes);
@@ -197,8 +198,12 @@ err:
 	return ret;
 }
 
-static int erofs_rebuild_fill_inode(struct erofs_inode *inode)
+static int erofs_rebuild_update_inode(struct erofs_sb_info *dst_sb,
+				      struct erofs_inode *inode,
+				      enum erofs_rebuild_datamode datamode)
 {
+	int err = 0;
+
 	switch (inode->i_mode & S_IFMT) {
 	case S_IFCHR:
 		if (erofs_inode_is_whiteout(inode))
@@ -211,36 +216,44 @@ static int erofs_rebuild_fill_inode(struct erofs_inode *inode)
 		erofs_dbg("\tdev: %d %d", major(inode->u.i_rdev),
 			  minor(inode->u.i_rdev));
 		inode->u.i_rdev = erofs_new_encode_dev(inode->u.i_rdev);
-		return 0;
+		break;
 	case S_IFDIR:
-		return erofs_init_empty_dir(inode);
-	case S_IFLNK: {
-		int ret;
-
+		err = erofs_init_empty_dir(inode);
+		break;
+	case S_IFLNK:
 		inode->i_link = malloc(inode->i_size + 1);
 		if (!inode->i_link)
 			return -ENOMEM;
-		ret = erofs_pread(inode, inode->i_link, inode->i_size, 0);
+		err = erofs_pread(inode, inode->i_link, inode->i_size, 0);
 		erofs_dbg("\tsymlink: %s -> %s", inode->i_srcpath, inode->i_link);
-		return ret;
-	}
+		break;
 	case S_IFREG:
-		if (inode->i_size)
-			return erofs_rebuild_fixup_inode_index(inode);
-		return 0;
-	default:
+		if (!inode->i_size) {
+			inode->u.i_blkaddr = NULL_ADDR;
+			break;
+		}
+		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;
+		else
+			err = -EOPNOTSUPP;
 		break;
+	default:
+		return -EINVAL;
 	}
-	return -EINVAL;
+	return err;
 }
 
 /*
  * @mergedir: parent directory in the merged tree
  * @ctx.dir:  parent directory when itering erofs_iterate_dir()
+ * @datamode: indicate how to import inode data
  */
 struct erofs_rebuild_dir_context {
 	struct erofs_dir_context ctx;
 	struct erofs_inode *mergedir;
+	enum erofs_rebuild_datamode datamode;
 };
 
 static int erofs_rebuild_dirent_iter(struct erofs_dir_context *ctx)
@@ -340,7 +353,8 @@ static int erofs_rebuild_dirent_iter(struct erofs_dir_context *ctx)
 			inode->i_ino[1] = inode->nid;
 			inode->i_nlink = 1;
 
-			ret = erofs_rebuild_fill_inode(inode);
+			ret = erofs_rebuild_update_inode(&sbi, inode,
+							 rctx->datamode);
 			if (ret) {
 				erofs_iput(inode);
 				goto out;
@@ -372,7 +386,8 @@ out:
 	return ret;
 }
 
-int erofs_rebuild_load_tree(struct erofs_inode *root, struct erofs_sb_info *sbi)
+int erofs_rebuild_load_tree(struct erofs_inode *root, struct erofs_sb_info *sbi,
+			    enum erofs_rebuild_datamode mode)
 {
 	struct erofs_inode inode = {};
 	struct erofs_rebuild_dir_context ctx;
@@ -403,6 +418,7 @@ int erofs_rebuild_load_tree(struct erofs_inode *root, struct erofs_sb_info *sbi)
 		.ctx.dir = &inode,
 		.ctx.cb = erofs_rebuild_dirent_iter,
 		.mergedir = root,
+		.datamode = mode,
 	};
 	ret = erofs_iterate_dir(&ctx.ctx, false);
 	free(inode.i_srcpath);
diff --git a/lib/tar.c b/lib/tar.c
index 3a5d484..53a1188 100644
--- a/lib/tar.c
+++ b/lib/tar.c
@@ -607,7 +607,7 @@ static int tarerofs_write_file_data(struct erofs_inode *inode,
 		j -= nread;
 	}
 	erofs_diskbuf_commit(inode->i_diskbuf, inode->i_size);
-	inode->with_diskbuf = true;
+	inode->datasource = EROFS_INODE_DATA_SOURCE_DISKBUF;
 	return 0;
 }
 
diff --git a/mkfs/main.c b/mkfs/main.c
index 2731287..7fc76f7 100644
--- a/mkfs/main.c
+++ b/mkfs/main.c
@@ -1060,7 +1060,8 @@ static int erofs_rebuild_load_trees(struct erofs_inode *root)
 	int ret, idx;
 
 	list_for_each_entry(src, &rebuild_src_list, list) {
-		ret = erofs_rebuild_load_tree(root, src);
+		ret = erofs_rebuild_load_tree(root, src,
+					      EROFS_REBUILD_DATA_BLOB_INDEX);
 		if (ret) {
 			erofs_err("failed to load %s", src->devname);
 			return ret;
-- 
2.39.3



More information about the Linux-erofs mailing list