[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