[PATCH 2/2] erofs-utils: get rid of the global `g_cfg`

Hongzhen Luo hongzhen at linux.alibaba.com
Thu Sep 19 01:30:12 AEST 2024


This patch introduces the per-superblock configuration
`bt_args` to get rid of the global `g_cfg`.

Signed-off-by: Hongzhen Luo <hongzhen at linux.alibaba.com>
---
 dump/main.c              |   4 +-
 fsck/main.c              |   4 +-
 fuse/main.c              |   4 +-
 include/erofs/compress.h |   2 +-
 include/erofs/config.h   |  22 ++-
 include/erofs/internal.h |   2 +
 lib/blobchunk.c          |   3 +-
 lib/block_list.c         |  25 +--
 lib/compress.c           |  95 +++++++-----
 lib/compress_hints.c     |  18 ++-
 lib/compressor_liblzma.c |   4 +-
 lib/compressor_libzstd.c |   4 +-
 lib/config.c             |  82 ++++++----
 lib/inode.c              |  82 ++++++----
 lib/io.c                 |  31 +++-
 lib/xattr.c              |  64 ++++----
 mkfs/main.c              | 328 +++++++++++++++++++++------------------
 17 files changed, 456 insertions(+), 318 deletions(-)

diff --git a/dump/main.c b/dump/main.c
index e85b853..29ddc3b 100644
--- a/dump/main.c
+++ b/dump/main.c
@@ -676,7 +676,7 @@ int main(int argc, char **argv)
 {
 	int err;
 
-	erofs_init_configure();
+	erofs_init_configure(&g_cfg);
 	err = erofsdump_parse_options_cfg(argc, argv);
 	if (err) {
 		if (err == -EINVAL)
@@ -720,6 +720,6 @@ exit_dev_close:
 	erofs_dev_close(&g_sbi);
 exit:
 	erofs_blob_closeall(&g_sbi);
-	erofs_exit_configure();
+	erofs_exit_configure(&g_cfg);
 	return err;
 }
diff --git a/fsck/main.c b/fsck/main.c
index 6096683..718051e 100644
--- a/fsck/main.c
+++ b/fsck/main.c
@@ -1049,7 +1049,7 @@ int main(int argc, char *argv[])
 {
 	int err;
 
-	erofs_init_configure();
+	erofs_init_configure(&g_cfg);
 
 	fsckcfg.physical_blocks = 0;
 	fsckcfg.logical_blocks = 0;
@@ -1136,7 +1136,7 @@ exit_dev_close:
 	erofs_dev_close(&g_sbi);
 exit:
 	erofs_blob_closeall(&g_sbi);
-	erofs_exit_configure();
+	erofs_exit_configure(&g_cfg);
 	return err ? 1 : 0;
 }
 
diff --git a/fuse/main.c b/fuse/main.c
index bb92a7b..4d0cdae 100644
--- a/fuse/main.c
+++ b/fuse/main.c
@@ -642,7 +642,7 @@ int main(int argc, char *argv[])
 	} opts = {};
 #endif
 
-	erofs_init_configure();
+	erofs_init_configure(&g_cfg);
 	fusecfg.debug_lvl = g_cfg.c_dbg_lvl;
 	printf("erofsfuse %s\n", g_cfg.c_version);
 
@@ -752,6 +752,6 @@ err_fuse_free_args:
 	free(opts.mountpoint);
 	fuse_opt_free_args(&args);
 err:
-	erofs_exit_configure();
+	erofs_exit_configure(&g_cfg);
 	return ret ? EXIT_FAILURE : EXIT_SUCCESS;
 }
diff --git a/include/erofs/compress.h b/include/erofs/compress.h
index c9831a7..4731a8b 100644
--- a/include/erofs/compress.h
+++ b/include/erofs/compress.h
@@ -25,7 +25,7 @@ int erofs_write_compressed_file(struct z_erofs_compress_ictx *ictx);
 
 int z_erofs_compress_init(struct erofs_sb_info *sbi,
 			  struct erofs_buffer_head *bh);
-int z_erofs_compress_exit(void);
+int z_erofs_compress_exit(struct erofs_sb_info *sbi);
 
 const char *z_erofs_list_supported_algorithms(int i, unsigned int *mask);
 const struct erofs_algorithm *z_erofs_list_available_compressors(int *i);
diff --git a/include/erofs/config.h b/include/erofs/config.h
index 41d6c54..46f1111 100644
--- a/include/erofs/config.h
+++ b/include/erofs/config.h
@@ -98,9 +98,18 @@ struct erofs_configure {
 
 extern struct erofs_configure g_cfg;
 
-void erofs_init_configure(void);
-void erofs_show_config(void);
-void erofs_exit_configure(void);
+struct erofs_mkfs_buildtree_args {
+	struct erofs_configure *cfg;
+};
+
+struct erofs_sb_info;
+
+void erofs_init_configure(struct erofs_configure *cfg);
+void erofs_show_config(struct erofs_configure *cfg);
+void erofs_exit_configure(struct erofs_configure *cfg);
+void erofs_init_buildtree_cfg(struct erofs_sb_info *sbi,
+			      struct erofs_configure *cfg);
+void erofs_exit_buildtree_cfg(struct erofs_sb_info *sbi);
 
 /* (will be deprecated) temporary helper for updating global the cfg */
 struct erofs_configure *erofs_get_configure();
@@ -109,15 +118,16 @@ void erofs_set_fs_root(const char *rootdir);
 const char *erofs_fspath(const char *fullpath);
 
 #ifdef HAVE_LIBSELINUX
-int erofs_selabel_open(const char *file_contexts);
+int erofs_selabel_open(struct erofs_sb_info *sbi, const char *file_contexts);
 #else
-static inline int erofs_selabel_open(const char *file_contexts)
+static inline int erofs_selabel_open(struct erofs_sb_info *sbi,
+				     const char *file_contexts)
 {
 	return -EINVAL;
 }
 #endif
 
-void erofs_update_progressinfo(const char *fmt, ...);
+void erofs_update_progressinfo(struct erofs_sb_info *sbi, const char *fmt, ...);
 char *erofs_trim_for_progressinfo(const char *str, int placeholder);
 unsigned int erofs_get_available_processors(void);
 
diff --git a/include/erofs/internal.h b/include/erofs/internal.h
index 2edc1b4..71e1cb1 100644
--- a/include/erofs/internal.h
+++ b/include/erofs/internal.h
@@ -27,6 +27,7 @@ typedef unsigned short umode_t;
 #endif
 #include "atomic.h"
 #include "io.h"
+#include "config.h"
 
 #ifndef PATH_MAX
 #define PATH_MAX        4096    /* # chars in a path name including nul */
@@ -140,6 +141,7 @@ struct erofs_sb_info {
 #endif
 	struct erofs_bufmgr *bmgr;
 	bool useqpl;
+	struct erofs_mkfs_buildtree_args *bt_args;
 };
 
 #define EROFS_SUPER_END (EROFS_SUPER_OFFSET + sizeof(struct erofs_super_block))
diff --git a/lib/blobchunk.c b/lib/blobchunk.c
index 90e3b28..e6b278c 100644
--- a/lib/blobchunk.c
+++ b/lib/blobchunk.c
@@ -261,7 +261,8 @@ int erofs_blob_write_chunked_file(struct erofs_inode *inode, int fd,
 				  erofs_off_t startoff)
 {
 	struct erofs_sb_info *sbi = inode->sbi;
-	unsigned int chunkbits = g_cfg.c_chunkbits;
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
+	unsigned int chunkbits = cfg->c_chunkbits;
 	unsigned int count, unit;
 	struct erofs_blobchunk *chunk, *lastch;
 	struct erofs_inode_chunk_index *idx;
diff --git a/lib/block_list.c b/lib/block_list.c
index d745ea2..74365f3 100644
--- a/lib/block_list.c
+++ b/lib/block_list.c
@@ -42,14 +42,15 @@ void tarerofs_blocklist_write(erofs_blk_t blkaddr, erofs_blk_t nblocks,
 }
 
 #ifdef WITH_ANDROID
-static void blocklist_write(const char *path, erofs_blk_t blk_start,
+static void blocklist_write(struct erofs_sb_info *sbi, const char *path, erofs_blk_t blk_start,
 			    erofs_blk_t nblocks, bool first_extent,
 			    bool last_extent)
 {
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
 	const char *fspath = erofs_fspath(path);
 
 	if (first_extent) {
-		fprintf(block_list_fp, "/%s", g_cfg.mount_point);
+		fprintf(block_list_fp, "/%s", cfg->mount_point);
 
 		if (fspath[0] != '/')
 			fprintf(block_list_fp, "/");
@@ -72,7 +73,9 @@ void erofs_droid_blocklist_write_extent(struct erofs_inode *inode,
 					erofs_blk_t nblocks, bool first_extent,
 					bool last_extent)
 {
-	if (!block_list_fp || !g_cfg.mount_point)
+	struct erofs_configure *cfg = inode->sbi->bt_args->cfg;
+
+	if (!block_list_fp || !cfg->mount_point)
 		return;
 
 	if (!nblocks) {
@@ -81,24 +84,28 @@ void erofs_droid_blocklist_write_extent(struct erofs_inode *inode,
 		return;
 	}
 
-	blocklist_write(inode->i_srcpath, blk_start, nblocks, first_extent,
-			last_extent);
+	blocklist_write(inode->sbi, inode->i_srcpath, blk_start, nblocks,
+			first_extent, last_extent);
 }
 
 void erofs_droid_blocklist_write(struct erofs_inode *inode,
 				 erofs_blk_t blk_start, erofs_blk_t nblocks)
 {
-	if (!block_list_fp || !g_cfg.mount_point || !nblocks)
+	struct erofs_configure *cfg = inode->sbi->bt_args->cfg;
+
+	if (!block_list_fp || !cfg->mount_point || !nblocks)
 		return;
 
-	blocklist_write(inode->i_srcpath, blk_start, nblocks,
+	blocklist_write(inode->sbi, inode->i_srcpath, blk_start, nblocks,
 			true, !inode->idata_size);
 }
 
 void erofs_droid_blocklist_write_tail_end(struct erofs_inode *inode,
 					  erofs_blk_t blkaddr)
 {
-	if (!block_list_fp || !g_cfg.mount_point)
+	struct erofs_configure *cfg = inode->sbi->bt_args->cfg;
+
+	if (!block_list_fp || !cfg->mount_point)
 		return;
 
 	/* XXX: a bit hacky.. may need a better approach */
@@ -114,6 +121,6 @@ void erofs_droid_blocklist_write_tail_end(struct erofs_inode *inode,
 		return;
 	}
 	if (blkaddr != NULL_ADDR)
-		blocklist_write(inode->i_srcpath, blkaddr, 1, true, true);
+		blocklist_write(inode->sbi, inode->i_srcpath, blkaddr, 1, true, true);
 }
 #endif
diff --git a/lib/compress.c b/lib/compress.c
index ea47927..d3fe50b 100644
--- a/lib/compress.c
+++ b/lib/compress.c
@@ -142,6 +142,7 @@ static void z_erofs_write_extent(struct z_erofs_compress_ictx *ctx,
 	unsigned int d0 = 0, d1 = (clusterofs + count) / erofs_blksiz(sbi);
 	struct z_erofs_lcluster_index di;
 	unsigned int type, advise;
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
 
 	DBG_BUGON(!count);
 	di.di_clusterofs = cpu_to_le16(ctx->clusterofs);
@@ -152,7 +153,7 @@ static void z_erofs_write_extent(struct z_erofs_compress_ictx *ctx,
 		 * A lcluster cannot have three parts with the middle one which
 		 * is well-compressed for !ztailpacking cases.
 		 */
-		DBG_BUGON(!e->raw && !g_cfg.c_ztailpacking && !g_cfg.c_fragments);
+		DBG_BUGON(!e->raw && !cfg->c_ztailpacking && !cfg->c_fragments);
 		DBG_BUGON(e->partial);
 		type = e->raw ? Z_EROFS_LCLUSTER_TYPE_PLAIN :
 			Z_EROFS_LCLUSTER_TYPE_HEAD1;
@@ -415,21 +416,23 @@ static int write_uncompressed_extent(struct z_erofs_compress_sctx *ctx,
 
 static unsigned int z_erofs_get_max_pclustersize(struct erofs_inode *inode)
 {
+	struct erofs_configure *cfg = inode->sbi->bt_args->cfg;
+
 	if (erofs_is_packed_inode(inode)) {
-		return g_cfg.c_mkfs_pclustersize_packed;
+		return cfg->c_mkfs_pclustersize_packed;
 #ifndef NDEBUG
-	} else if (g_cfg.c_random_pclusterblks) {
+	} else if (cfg->c_random_pclusterblks) {
 		unsigned int pclusterblks =
-			g_cfg.c_mkfs_pclustersize_max >> inode->sbi->blkszbits;
+			cfg->c_mkfs_pclustersize_max >> inode->sbi->blkszbits;
 
 		return (1 + rand() % pclusterblks) << inode->sbi->blkszbits;
 #endif
-	} else if (g_cfg.c_compress_hints_file) {
+	} else if (cfg->c_compress_hints_file) {
 		z_erofs_apply_compress_hints(inode);
 		DBG_BUGON(!inode->z_physical_clusterblks);
 		return inode->z_physical_clusterblks << inode->sbi->blkszbits;
 	}
-	return g_cfg.c_mkfs_pclustersize_def;
+	return cfg->c_mkfs_pclustersize_def;
 }
 
 static int z_erofs_fill_inline_data(struct erofs_inode *inode, void *data,
@@ -515,15 +518,16 @@ static int __z_erofs_compress_one(struct z_erofs_compress_sctx *ctx,
 	struct z_erofs_compress_ictx *ictx = ctx->ictx;
 	struct erofs_inode *inode = ictx->inode;
 	struct erofs_sb_info *sbi = inode->sbi;
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
 	unsigned int blksz = erofs_blksiz(sbi);
 	char *const dst = dstbuf + blksz;
 	struct erofs_compress *const h = ctx->chandle;
 	unsigned int len = ctx->tail - ctx->head;
 	bool is_packed_inode = erofs_is_packed_inode(inode);
 	bool tsg = (ctx->seg_idx + 1 >= ictx->seg_num), final = !ctx->remaining;
-	bool may_packing = (g_cfg.c_fragments && tsg && final &&
+	bool may_packing = (cfg->c_fragments && tsg && final &&
 			    !is_packed_inode && !z_erofs_mt_enabled);
-	bool may_inline = (g_cfg.c_ztailpacking && tsg && final && !may_packing);
+	bool may_inline = (cfg->c_ztailpacking && tsg && final && !may_packing);
 	unsigned int compressedsize;
 	int ret;
 
@@ -543,7 +547,7 @@ static int __z_erofs_compress_one(struct z_erofs_compress_sctx *ctx,
 			goto nocompression;
 	}
 
-	e->length = min(len, g_cfg.c_max_decompressed_extent_bytes);
+	e->length = min(len, cfg->c_max_decompressed_extent_bytes);
 	ret = erofs_compress_destsize(h, ctx->queue + ctx->head,
 				      &e->length, dst, ctx->pclustersize);
 	if (ret <= 0) {
@@ -1065,6 +1069,7 @@ int erofs_commit_compressed_file(struct z_erofs_compress_ictx *ictx,
 {
 	struct erofs_inode *inode = ictx->inode;
 	struct erofs_sb_info *sbi = inode->sbi;
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
 	unsigned int legacymetasize;
 	u8 *compressmeta;
 	int ret;
@@ -1114,7 +1119,7 @@ int erofs_commit_compressed_file(struct z_erofs_compress_ictx *ictx,
 		ret = erofs_bh_balloon(bh, erofs_pos(sbi, compressed_blocks));
 		DBG_BUGON(ret != erofs_blksiz(sbi));
 	} else {
-		if (!g_cfg.c_fragments && !g_cfg.c_dedupe)
+		if (!cfg->c_fragments && !cfg->c_dedupe)
 			DBG_BUGON(!inode->idata_size);
 	}
 
@@ -1233,6 +1238,7 @@ void z_erofs_mt_workfn(struct erofs_work *work, void *tlsp)
 	struct z_erofs_compress_ictx *ictx = sctx->ictx;
 	struct erofs_inode *inode = ictx->inode;
 	struct erofs_sb_info *sbi = inode->sbi;
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
 	int ret = 0;
 
 	ret = z_erofs_mt_wq_tls_init_compr(sbi, tls, cwork->alg_id,
@@ -1253,7 +1259,7 @@ void z_erofs_mt_workfn(struct erofs_work *work, void *tlsp)
 	}
 	sctx->memoff = 0;
 
-	ret = z_erofs_compress_segment(sctx, sctx->seg_idx * g_cfg.c_mkfs_segment_size,
+	ret = z_erofs_compress_segment(sctx, sctx->seg_idx * cfg->c_mkfs_segment_size,
 				       EROFS_NULL_ADDR);
 
 out:
@@ -1304,7 +1310,8 @@ int z_erofs_mt_compress(struct z_erofs_compress_ictx *ictx)
 	struct erofs_compress_work *cur, *head = NULL, **last = &head;
 	struct erofs_compress_cfg *ccfg = ictx->ccfg;
 	struct erofs_inode *inode = ictx->inode;
-	int nsegs = DIV_ROUND_UP(inode->i_size, g_cfg.c_mkfs_segment_size);
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
+	int nsegs = DIV_ROUND_UP(inode->i_size, cfg->c_mkfs_segment_size);
 	int i;
 
 	ictx->seg_num = nsegs;
@@ -1338,9 +1345,9 @@ int z_erofs_mt_compress(struct z_erofs_compress_ictx *ictx)
 		if (i == nsegs - 1)
 			cur->ctx.remaining = inode->i_size -
 					      inode->fragment_size -
-					      i * g_cfg.c_mkfs_segment_size;
+					      i * cfg->c_mkfs_segment_size;
 		else
-			cur->ctx.remaining = g_cfg.c_mkfs_segment_size;
+			cur->ctx.remaining = cfg->c_mkfs_segment_size;
 
 		cur->alg_id = ccfg->handle.alg->id;
 		cur->alg_name = ccfg->handle.alg->name;
@@ -1418,13 +1425,14 @@ static struct z_erofs_compress_ictx g_ictx;
 void *erofs_begin_compressed_file(struct erofs_inode *inode, int fd, u64 fpos)
 {
 	struct erofs_sb_info *sbi = inode->sbi;
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
 	struct z_erofs_compress_ictx *ictx;
 	int ret;
 
 	/* initialize per-file compression setting */
 	inode->z_advise = 0;
 	inode->z_logical_clusterbits = sbi->blkszbits;
-	if (!g_cfg.c_legacy_compress && inode->z_logical_clusterbits <= 14) {
+	if (!cfg->c_legacy_compress && inode->z_logical_clusterbits <= 14) {
 		if (inode->z_logical_clusterbits <= 12)
 			inode->z_advise |= Z_EROFS_ADVISE_COMPACTED_2B;
 		inode->datalayout = EROFS_INODE_COMPRESSED_COMPACT;
@@ -1437,11 +1445,11 @@ void *erofs_begin_compressed_file(struct erofs_inode *inode, int fd, u64 fpos)
 		if (inode->datalayout == EROFS_INODE_COMPRESSED_COMPACT)
 			inode->z_advise |= Z_EROFS_ADVISE_BIG_PCLUSTER_2;
 	}
-	if (g_cfg.c_fragments && !g_cfg.c_dedupe)
+	if (cfg->c_fragments && !cfg->c_dedupe)
 		inode->z_advise |= Z_EROFS_ADVISE_INTERLACED_PCLUSTER;
 
 #ifndef NDEBUG
-	if (g_cfg.c_random_algorithms) {
+	if (cfg->c_random_algorithms) {
 		while (1) {
 			inode->z_algorithmtype[0] =
 				rand() % EROFS_MAX_COMPR_CFGS;
@@ -1478,7 +1486,7 @@ void *erofs_begin_compressed_file(struct erofs_inode *inode, int fd, u64 fpos)
 	 * Handle tails in advance to avoid writing duplicated
 	 * parts into the packed inode.
 	 */
-	if (g_cfg.c_fragments && !erofs_is_packed_inode(inode)) {
+	if (cfg->c_fragments && !erofs_is_packed_inode(inode)) {
 		ret = z_erofs_fragments_dedupe(inode, fd, &ictx->tof_chksum);
 		if (ret < 0)
 			goto err_free_ictx;
@@ -1490,7 +1498,7 @@ void *erofs_begin_compressed_file(struct erofs_inode *inode, int fd, u64 fpos)
 	ictx->fix_dedupedfrag = false;
 	ictx->fragemitted = false;
 
-	if (g_cfg.c_all_fragments && !erofs_is_packed_inode(inode) &&
+	if (cfg->c_all_fragments && !erofs_is_packed_inode(inode) &&
 	    !inode->fragment_size) {
 		ret = z_erofs_pack_file_from_fd(inode, fd, ictx->tof_chksum);
 		if (ret)
@@ -1580,6 +1588,7 @@ static int z_erofs_build_compr_cfgs(struct erofs_sb_info *sbi,
 				    struct erofs_buffer_head *sb_bh,
 				    u32 *max_dict_size)
 {
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
 	struct erofs_buffer_head *bh = sb_bh;
 	int ret = 0;
 
@@ -1593,7 +1602,7 @@ static int z_erofs_build_compr_cfgs(struct erofs_sb_info *sbi,
 				.max_distance =
 					cpu_to_le16(sbi->lz4.max_distance),
 				.max_pclusterblks =
-					g_cfg.c_mkfs_pclustersize_max >> sbi->blkszbits,
+					cfg->c_mkfs_pclustersize_max >> sbi->blkszbits,
 			}
 		};
 
@@ -1687,13 +1696,14 @@ int z_erofs_compress_init(struct erofs_sb_info *sbi, struct erofs_buffer_head *s
 	int i, ret, id;
 	u32 max_dict_size[Z_EROFS_COMPRESSION_MAX] = {};
 	u32 available_compr_algs = 0;
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
 
-	for (i = 0; g_cfg.c_compr_opts[i].alg; ++i) {
+	for (i = 0; cfg->c_compr_opts[i].alg; ++i) {
 		struct erofs_compress *c = &erofs_ccfg[i].handle;
 
-		ret = erofs_compressor_init(sbi, c, g_cfg.c_compr_opts[i].alg,
-					    g_cfg.c_compr_opts[i].level,
-					    g_cfg.c_compr_opts[i].dict_size);
+		ret = erofs_compressor_init(sbi, c, cfg->c_compr_opts[i].alg,
+					    cfg->c_compr_opts[i].level,
+					    cfg->c_compr_opts[i].dict_size);
 		if (ret)
 			return ret;
 
@@ -1712,7 +1722,7 @@ int z_erofs_compress_init(struct erofs_sb_info *sbi, struct erofs_buffer_head *s
 	 * clear 0PADDING feature for old kernel compatibility.
 	 */
 	if (!available_compr_algs ||
-	    (g_cfg.c_legacy_compress && available_compr_algs == 1))
+	    (cfg->c_legacy_compress && available_compr_algs == 1))
 		erofs_sb_clear_lz4_0padding(sbi);
 
 	if (!available_compr_algs)
@@ -1728,9 +1738,9 @@ int z_erofs_compress_init(struct erofs_sb_info *sbi, struct erofs_buffer_head *s
 		}
 		if (available_compr_algs & (1 << Z_EROFS_COMPRESSION_LZ4) &&
 		    sbi->lz4.max_pclusterblks << sbi->blkszbits <
-			g_cfg.c_mkfs_pclustersize_max) {
+			cfg->c_mkfs_pclustersize_max) {
 			erofs_err("pclustersize %u is too large on incremental builds",
-				  g_cfg.c_mkfs_pclustersize_max);
+				  cfg->c_mkfs_pclustersize_max);
 			return -EOPNOTSUPP;
 		}
 	} else {
@@ -1741,17 +1751,17 @@ int z_erofs_compress_init(struct erofs_sb_info *sbi, struct erofs_buffer_head *s
 	 * if big pcluster is enabled, an extra CBLKCNT lcluster index needs
 	 * to be loaded in order to get those compressed block counts.
 	 */
-	if (g_cfg.c_mkfs_pclustersize_max > erofs_blksiz(sbi)) {
-		if (g_cfg.c_mkfs_pclustersize_max > Z_EROFS_PCLUSTER_MAX_SIZE) {
+	if (cfg->c_mkfs_pclustersize_max > erofs_blksiz(sbi)) {
+		if (cfg->c_mkfs_pclustersize_max > Z_EROFS_PCLUSTER_MAX_SIZE) {
 			erofs_err("unsupported pclustersize %u (too large)",
-				  g_cfg.c_mkfs_pclustersize_max);
+				  cfg->c_mkfs_pclustersize_max);
 			return -EINVAL;
 		}
 		erofs_sb_set_big_pcluster(sbi);
 	}
-	if (g_cfg.c_mkfs_pclustersize_packed > g_cfg.c_mkfs_pclustersize_max) {
+	if (cfg->c_mkfs_pclustersize_packed > cfg->c_mkfs_pclustersize_max) {
 		erofs_err("invalid pclustersize for the packed file %u",
-			  g_cfg.c_mkfs_pclustersize_packed);
+			  cfg->c_mkfs_pclustersize_packed);
 		return -EINVAL;
 	}
 
@@ -1763,19 +1773,19 @@ int z_erofs_compress_init(struct erofs_sb_info *sbi, struct erofs_buffer_head *s
 
 	z_erofs_mt_enabled = false;
 #ifdef EROFS_MT_ENABLED
-	if (g_cfg.c_mt_workers >= 1 && (g_cfg.c_dedupe ||
-				      (g_cfg.c_fragments && !g_cfg.c_all_fragments))) {
-		if (g_cfg.c_dedupe)
+	if (cfg->c_mt_workers >= 1 && (cfg->c_dedupe ||
+				      (cfg->c_fragments && !cfg->c_all_fragments))) {
+		if (cfg->c_dedupe)
 			erofs_warn("multi-threaded dedupe is NOT implemented for now");
-		if (g_cfg.c_fragments)
+		if (cfg->c_fragments)
 			erofs_warn("multi-threaded fragments is NOT implemented for now");
-		g_cfg.c_mt_workers = 0;
+		cfg->c_mt_workers = 0;
 	}
 
-	if (g_cfg.c_mt_workers >= 1) {
+	if (cfg->c_mt_workers >= 1) {
 		ret = erofs_alloc_workqueue(&z_erofs_mt_ctrl.wq,
-					    g_cfg.c_mt_workers,
-					    g_cfg.c_mt_workers << 2,
+					    cfg->c_mt_workers,
+					    cfg->c_mt_workers << 2,
 					    z_erofs_mt_wq_tls_alloc,
 					    z_erofs_mt_wq_tls_free);
 		z_erofs_mt_enabled = !ret;
@@ -1786,11 +1796,12 @@ int z_erofs_compress_init(struct erofs_sb_info *sbi, struct erofs_buffer_head *s
 	return 0;
 }
 
-int z_erofs_compress_exit(void)
+int z_erofs_compress_exit(struct erofs_sb_info *sbi)
 {
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
 	int i, ret;
 
-	for (i = 0; g_cfg.c_compr_opts[i].alg; ++i) {
+	for (i = 0; cfg->c_compr_opts[i].alg; ++i) {
 		ret = erofs_compressor_exit(&erofs_ccfg[i].handle);
 		if (ret)
 			return ret;
diff --git a/lib/compress_hints.c b/lib/compress_hints.c
index ae7c231..d0928f0 100644
--- a/lib/compress_hints.c
+++ b/lib/compress_hints.c
@@ -50,12 +50,13 @@ bool z_erofs_apply_compress_hints(struct erofs_inode *inode)
 	const char *s;
 	struct erofs_compress_hints *r;
 	unsigned int pclusterblks, algorithmtype;
+	struct erofs_configure *cfg = inode->sbi->bt_args->cfg;
 
 	if (inode->z_physical_clusterblks)
 		return true;
 
 	s = erofs_fspath(inode->i_srcpath);
-	pclusterblks = g_cfg.c_mkfs_pclustersize_def >> inode->sbi->blkszbits;
+	pclusterblks = cfg->c_mkfs_pclustersize_def >> inode->sbi->blkszbits;
 	algorithmtype = 0;
 
 	list_for_each_entry(r, &compress_hints_head, list) {
@@ -92,11 +93,12 @@ int erofs_load_compress_hints(struct erofs_sb_info *sbi)
 	FILE *f;
 	unsigned int line, max_pclustersize = 0;
 	int ret = 0;
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
 
-	if (!g_cfg.c_compress_hints_file)
+	if (!cfg->c_compress_hints_file)
 		return 0;
 
-	f = fopen(g_cfg.c_compress_hints_file, "r");
+	f = fopen(cfg->c_compress_hints_file, "r");
 	if (!f)
 		return -errno;
 
@@ -125,7 +127,7 @@ int erofs_load_compress_hints(struct erofs_sb_info *sbi)
 		} else {
 			ccfg = atoi(alg);
 			if (ccfg >= EROFS_MAX_COMPR_CFGS ||
-			    !g_cfg.c_compr_opts[ccfg].alg) {
+			    !cfg->c_compr_opts[ccfg].alg) {
 				erofs_err("invalid compressing configuration \"%s\" at line %u",
 					  alg, line);
 				ret = -EINVAL;
@@ -136,7 +138,7 @@ int erofs_load_compress_hints(struct erofs_sb_info *sbi)
 		if (pclustersize % erofs_blksiz(sbi)) {
 			erofs_warn("invalid physical clustersize %u, "
 				   "use default pclusterblks %u",
-				   pclustersize, g_cfg.c_mkfs_pclustersize_def);
+				   pclustersize, cfg->c_mkfs_pclustersize_def);
 			continue;
 		}
 		erofs_insert_compress_hints(pattern,
@@ -146,10 +148,10 @@ int erofs_load_compress_hints(struct erofs_sb_info *sbi)
 			max_pclustersize = pclustersize;
 	}
 
-	if (g_cfg.c_mkfs_pclustersize_max < max_pclustersize) {
-		g_cfg.c_mkfs_pclustersize_max = max_pclustersize;
+	if (cfg->c_mkfs_pclustersize_max < max_pclustersize) {
+		cfg->c_mkfs_pclustersize_max = max_pclustersize;
 		erofs_warn("update max pclustersize to %u",
-			   g_cfg.c_mkfs_pclustersize_max);
+			   cfg->c_mkfs_pclustersize_max);
 	}
 out:
 	fclose(f);
diff --git a/lib/compressor_liblzma.c b/lib/compressor_liblzma.c
index 4b0b069..7282fcd 100644
--- a/lib/compressor_liblzma.c
+++ b/lib/compressor_liblzma.c
@@ -70,12 +70,14 @@ static int erofs_compressor_liblzma_setlevel(struct erofs_compress *c,
 static int erofs_compressor_liblzma_setdictsize(struct erofs_compress *c,
 						u32 dict_size)
 {
+	struct erofs_configure *cfg = c->sbi->bt_args->cfg;
+
 	if (!dict_size) {
 		if (erofs_compressor_lzma.default_dictsize) {
 			dict_size = erofs_compressor_lzma.default_dictsize;
 		} else {
 			dict_size = min_t(u32, Z_EROFS_LZMA_MAX_DICT_SIZE,
-					  g_cfg.c_mkfs_pclustersize_max << 3);
+					  cfg->c_mkfs_pclustersize_max << 3);
 			if (dict_size < 32768)
 				dict_size = 32768;
 		}
diff --git a/lib/compressor_libzstd.c b/lib/compressor_libzstd.c
index dfdb728..92bfc82 100644
--- a/lib/compressor_libzstd.c
+++ b/lib/compressor_libzstd.c
@@ -81,12 +81,14 @@ static int erofs_compressor_libzstd_setlevel(struct erofs_compress *c,
 static int erofs_compressor_libzstd_setdictsize(struct erofs_compress *c,
 						u32 dict_size)
 {
+	struct erofs_configure *cfg = c->sbi->bt_args->cfg;
+
 	if (!dict_size) {
 		if (erofs_compressor_libzstd.default_dictsize) {
 			dict_size = erofs_compressor_libzstd.default_dictsize;
 		} else {
 			dict_size = min_t(u32, Z_EROFS_ZSTD_MAX_DICT_SIZE,
-					  g_cfg.c_mkfs_pclustersize_max << 3);
+					  cfg->c_mkfs_pclustersize_max << 3);
 			dict_size = 1 << ilog2(dict_size);
 		}
 	}
diff --git a/lib/config.c b/lib/config.c
index 353411a..25447d5 100644
--- a/lib/config.c
+++ b/lib/config.c
@@ -22,26 +22,26 @@ struct erofs_configure g_cfg;
 struct erofs_sb_info g_sbi;
 bool erofs_stdout_tty;
 
-void erofs_init_configure(void)
+void erofs_init_configure(struct erofs_configure *cfg)
 {
-	memset(&g_cfg, 0, sizeof(g_cfg));
-
-	g_cfg.c_dbg_lvl  = EROFS_WARN;
-	g_cfg.c_version  = PACKAGE_VERSION;
-	g_cfg.c_dry_run  = false;
-	g_cfg.c_ignore_mtime = false;
-	g_cfg.c_force_inodeversion = 0;
-	g_cfg.c_inline_xattr_tolerance = 2;
-	g_cfg.c_unix_timestamp = -1;
-	g_cfg.c_uid = -1;
-	g_cfg.c_gid = -1;
-	g_cfg.c_max_decompressed_extent_bytes = -1;
+	memset(cfg, 0, sizeof(*cfg));
+
+	cfg->c_dbg_lvl  = EROFS_WARN;
+	cfg->c_version  = PACKAGE_VERSION;
+	cfg->c_dry_run  = false;
+	cfg->c_ignore_mtime = false;
+	cfg->c_force_inodeversion = 0;
+	cfg->c_inline_xattr_tolerance = 2;
+	cfg->c_unix_timestamp = -1;
+	cfg->c_uid = -1;
+	cfg->c_gid = -1;
+	cfg->c_max_decompressed_extent_bytes = -1;
 	erofs_stdout_tty = isatty(STDOUT_FILENO);
 }
 
-void erofs_show_config(void)
+void erofs_show_config(struct erofs_configure *cfg)
 {
-	const struct erofs_configure *c = &g_cfg;
+	const struct erofs_configure *c = cfg;
 
 	if (c->c_dbg_lvl < EROFS_INFO)
 		return;
@@ -50,20 +50,20 @@ void erofs_show_config(void)
 	erofs_dump("\tc_dry_run:           [%8d]\n", c->c_dry_run);
 }
 
-void erofs_exit_configure(void)
+void erofs_exit_configure(struct erofs_configure *cfg)
 {
 	int i;
 
 #ifdef HAVE_LIBSELINUX
-	if (g_cfg.sehnd)
-		selabel_close(g_cfg.sehnd);
+	if (cfg->sehnd)
+		selabel_close(cfg->sehnd);
 #endif
-	if (g_cfg.c_img_path)
-		free(g_cfg.c_img_path);
-	if (g_cfg.c_src_path)
-		free(g_cfg.c_src_path);
-	for (i = 0; i < EROFS_MAX_COMPR_CFGS && g_cfg.c_compr_opts[i].alg; i++)
-		free(g_cfg.c_compr_opts[i].alg);
+	if (cfg->c_img_path)
+		free(cfg->c_img_path);
+	if (cfg->c_src_path)
+		free(cfg->c_src_path);
+	for (i = 0; i < EROFS_MAX_COMPR_CFGS && cfg->c_compr_opts[i].alg; i++)
+		free(cfg->c_compr_opts[i].alg);
 }
 
 struct erofs_configure *erofs_get_configure()
@@ -88,20 +88,22 @@ const char *erofs_fspath(const char *fullpath)
 }
 
 #ifdef HAVE_LIBSELINUX
-int erofs_selabel_open(const char *file_contexts)
+int erofs_selabel_open(struct erofs_sb_info *sbi, const char *file_contexts)
 {
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
+
 	struct selinux_opt seopts[] = {
 		{ .type = SELABEL_OPT_PATH, .value = file_contexts }
 	};
 
-	if (g_cfg.sehnd) {
+	if (cfg->sehnd) {
 		erofs_info("ignore duplicated file contexts \"%s\"",
 			   file_contexts);
 		return -EBUSY;
 	}
 
-	g_cfg.sehnd = selabel_open(SELABEL_CTX_FILE, seopts, 1);
-	if (!g_cfg.sehnd) {
+	cfg->sehnd = selabel_open(SELABEL_CTX_FILE, seopts, 1);
+	if (!cfg->sehnd) {
 		erofs_err("failed to open file contexts \"%s\"",
 			  file_contexts);
 		return -EINVAL;
@@ -161,12 +163,13 @@ void erofs_msg(int dbglv, const char *fmt, ...)
 	va_end(ap);
 }
 
-void erofs_update_progressinfo(const char *fmt, ...)
+void erofs_update_progressinfo(struct erofs_sb_info *sbi, const char *fmt, ...)
 {
 	char msg[8192];
 	va_list ap;
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
 
-	if (g_cfg.c_dbg_lvl >= EROFS_INFO || !g_cfg.c_showprogress)
+	if (cfg->c_dbg_lvl >= EROFS_INFO || !cfg->c_showprogress)
 		return;
 
 	va_start(ap, fmt);
@@ -191,3 +194,22 @@ unsigned int erofs_get_available_processors(void)
 	return 0;
 #endif
 }
+
+void erofs_init_buildtree_cfg(struct erofs_sb_info *sbi,
+			      struct erofs_configure *cfg)
+{
+	sbi->bt_args = malloc(sizeof(struct erofs_mkfs_buildtree_args));
+	if (!sbi->bt_args) {
+		erofs_err("fail to prepare for erofs_mkfs_buildtree_args");
+		return;
+	}
+	sbi->bt_args->cfg = cfg;
+	erofs_init_configure(sbi->bt_args->cfg);
+}
+
+void erofs_exit_buildtree_cfg(struct erofs_sb_info *sbi)
+{
+	erofs_exit_configure(sbi->bt_args->cfg);
+	sbi->bt_args->cfg = NULL;
+	free(sbi->bt_args);
+}
diff --git a/lib/inode.c b/lib/inode.c
index c3d2edb..2f6e3a5 100644
--- a/lib/inode.c
+++ b/lib/inode.c
@@ -507,7 +507,9 @@ int erofs_write_file_from_buffer(struct erofs_inode *inode, char *buf)
 /* rules to decide whether a file could be compressed or not */
 static bool erofs_file_is_compressible(struct erofs_inode *inode)
 {
-	if (g_cfg.c_compress_hints_file)
+	struct erofs_configure *cfg = inode->sbi->bt_args->cfg;
+
+	if (cfg->c_compress_hints_file)
 		return z_erofs_apply_compress_hints(inode);
 	return true;
 }
@@ -557,11 +559,13 @@ static int write_uncompressed_file_from_fd(struct erofs_inode *inode, int fd)
 
 int erofs_write_unencoded_file(struct erofs_inode *inode, int fd, u64 fpos)
 {
-	if (g_cfg.c_chunkbits) {
-		inode->u.chunkbits = g_cfg.c_chunkbits;
+	struct erofs_configure *cfg = inode->sbi->bt_args->cfg;
+
+	if (cfg->c_chunkbits) {
+		inode->u.chunkbits = cfg->c_chunkbits;
 		/* chunk indexes when explicitly specified */
 		inode->u.chunkformat = 0;
-		if (g_cfg.c_force_chunkformat == FORCE_INODE_CHUNK_INDEXES)
+		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);
 	}
@@ -748,6 +752,7 @@ static int erofs_prepare_inode_buffer(struct erofs_inode *inode)
 	struct erofs_bufmgr *bmgr = inode->sbi->bmgr;
 	unsigned int inodesize;
 	struct erofs_buffer_head *bh, *ibh;
+	struct erofs_configure *cfg = inode->sbi->bt_args->cfg;
 
 	DBG_BUGON(inode->bh || inode->bh_inline);
 
@@ -763,7 +768,7 @@ static int erofs_prepare_inode_buffer(struct erofs_inode *inode)
 		goto noinline;
 
 	if (!is_inode_layout_compression(inode)) {
-		if (!g_cfg.c_inline_data && S_ISREG(inode->i_mode)) {
+		if (!cfg->c_inline_data && S_ISREG(inode->i_mode)) {
 			inode->datalayout = EROFS_INODE_FLAT_PLAIN;
 			goto noinline;
 		}
@@ -796,7 +801,7 @@ noinline:
 		return PTR_ERR(bh);
 	} else if (inode->idata_size) {
 		if (is_inode_layout_compression(inode)) {
-			DBG_BUGON(!g_cfg.c_ztailpacking);
+			DBG_BUGON(!cfg->c_ztailpacking);
 			erofs_dbg("Inline %scompressed data (%u bytes) to %s",
 				  inode->compressed_idata ? "" : "un",
 				  inode->idata_size, inode->i_srcpath);
@@ -933,7 +938,9 @@ out:
 
 static bool erofs_should_use_inode_extended(struct erofs_inode *inode)
 {
-	if (g_cfg.c_force_inodeversion == FORCE_INODE_EXTENDED)
+	struct erofs_configure *cfg = inode->sbi->bt_args->cfg;
+
+	if (cfg->c_force_inodeversion == FORCE_INODE_EXTENDED)
 		return true;
 	if (inode->i_size > UINT_MAX)
 		return true;
@@ -947,7 +954,7 @@ static bool erofs_should_use_inode_extended(struct erofs_inode *inode)
 		return true;
 	if ((inode->i_mtime != inode->sbi->build_time ||
 	     inode->i_mtime_nsec != inode->sbi->build_time_nsec) &&
-	    !g_cfg.c_ignore_mtime)
+	    !cfg->c_ignore_mtime)
 		return true;
 	return false;
 }
@@ -965,6 +972,7 @@ int erofs_droid_inode_fsconfig(struct erofs_inode *inode,
 			       struct stat *st,
 			       const char *path)
 {
+	struct erofs_configure *cfg = inode->sbi->bt_args->cfg;
 	/* filesystem_config does not preserve file type bits */
 	mode_t stat_file_type_mask = st->st_mode & S_IFMT;
 	unsigned int uid = 0, gid = 0, mode = 0;
@@ -972,30 +980,30 @@ int erofs_droid_inode_fsconfig(struct erofs_inode *inode,
 	char *decorated = NULL;
 
 	inode->capabilities = 0;
-	if (!g_cfg.fs_config_file && !g_cfg.mount_point)
+	if (!cfg->fs_config_file && !cfg->mount_point)
 		return 0;
 	/* avoid loading special inodes */
 	if (path == EROFS_PACKED_INODE)
 		return 0;
 
-	if (!g_cfg.mount_point ||
+	if (!cfg->mount_point ||
 	/* have to drop the mountpoint for rootdir of canned fsconfig */
-	    (g_cfg.fs_config_file && erofs_fspath(path)[0] == '\0')) {
+	    (cfg->fs_config_file && erofs_fspath(path)[0] == '\0')) {
 		fspath = erofs_fspath(path);
 	} else {
-		if (asprintf(&decorated, "%s/%s", g_cfg.mount_point,
+		if (asprintf(&decorated, "%s/%s", cfg->mount_point,
 			     erofs_fspath(path)) <= 0)
 			return -ENOMEM;
 		fspath = decorated;
 	}
 
-	if (g_cfg.fs_config_file)
+	if (cfg->fs_config_file)
 		canned_fs_config(fspath, S_ISDIR(st->st_mode),
-				 g_cfg.target_out_path,
+				 cfg->target_out_path,
 				 &uid, &gid, &mode, &inode->capabilities);
 	else
 		fs_config(fspath, S_ISDIR(st->st_mode),
-			  g_cfg.target_out_path,
+			  cfg->target_out_path,
 			  &uid, &gid, &mode, &inode->capabilities);
 
 	erofs_dbg("/%s -> mode = 0x%x, uid = 0x%x, gid = 0x%x, capabilities = 0x%" PRIx64,
@@ -1022,25 +1030,26 @@ int __erofs_fill_inode(struct erofs_inode *inode, struct stat *st,
 {
 	int err = erofs_droid_inode_fsconfig(inode, st, path);
 	struct erofs_sb_info *sbi = inode->sbi;
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
 
 	if (err)
 		return err;
 
-	inode->i_uid = g_cfg.c_uid == -1 ? st->st_uid : g_cfg.c_uid;
-	inode->i_gid = g_cfg.c_gid == -1 ? st->st_gid : g_cfg.c_gid;
+	inode->i_uid = cfg->c_uid == -1 ? st->st_uid : cfg->c_uid;
+	inode->i_gid = cfg->c_gid == -1 ? st->st_gid : cfg->c_gid;
 
-	if (inode->i_uid + g_cfg.c_uid_offset < 0)
+	if (inode->i_uid + cfg->c_uid_offset < 0)
 		erofs_err("uid overflow @ %s", path);
-	inode->i_uid += g_cfg.c_uid_offset;
+	inode->i_uid += cfg->c_uid_offset;
 
-	if (inode->i_gid + g_cfg.c_gid_offset < 0)
+	if (inode->i_gid + cfg->c_gid_offset < 0)
 		erofs_err("gid overflow @ %s", path);
-	inode->i_gid += g_cfg.c_gid_offset;
+	inode->i_gid += cfg->c_gid_offset;
 
 	inode->i_mtime = st->st_mtime;
 	inode->i_mtime_nsec = ST_MTIM_NSEC(st);
 
-	switch (g_cfg.c_timeinherit) {
+	switch (cfg->c_timeinherit) {
 	case TIMESTAMP_CLAMPING:
 		if (inode->i_mtime < sbi->build_time)
 			break;
@@ -1057,6 +1066,8 @@ int __erofs_fill_inode(struct erofs_inode *inode, struct stat *st,
 static int erofs_fill_inode(struct erofs_inode *inode, struct stat *st,
 			    const char *path)
 {
+	struct erofs_configure *cfg = inode->sbi->bt_args->cfg;
+
 	int err = __erofs_fill_inode(inode, st, path);
 
 	if (err)
@@ -1087,7 +1098,7 @@ static int erofs_fill_inode(struct erofs_inode *inode, struct stat *st,
 		return -ENOMEM;
 
 	if (erofs_should_use_inode_extended(inode)) {
-		if (g_cfg.c_force_inodeversion == FORCE_INODE_COMPACT) {
+		if (cfg->c_force_inodeversion == FORCE_INODE_COMPACT) {
 			erofs_err("file %s cannot be in compact form",
 				  inode->i_srcpath);
 			return -EINVAL;
@@ -1534,9 +1545,10 @@ static int erofs_rebuild_handle_directory(struct erofs_inode *dir,
 					  bool incremental)
 {
 	struct erofs_sb_info *sbi = dir->sbi;
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
 	struct erofs_dentry *d, *n;
 	unsigned int nr_subdirs, i_nlink;
-	bool delwht = g_cfg.c_ovlfs_strip && dir->whiteouts;
+	bool delwht = cfg->c_ovlfs_strip && dir->whiteouts;
 	int ret;
 
 	nr_subdirs = 0;
@@ -1578,13 +1590,14 @@ static int erofs_rebuild_handle_directory(struct erofs_inode *dir,
 
 static int erofs_mkfs_handle_inode(struct erofs_inode *inode)
 {
+	struct erofs_configure *cfg = inode->sbi->bt_args->cfg;
 	const char *relpath = erofs_fspath(inode->i_srcpath);
 	char *trimmed;
 	int ret;
 
 	trimmed = erofs_trim_for_progressinfo(relpath[0] ? relpath : "/",
 					      sizeof("Processing  ...") - 1);
-	erofs_update_progressinfo("Processing %s ...", trimmed);
+	erofs_update_progressinfo(inode->sbi, "Processing %s ...", trimmed);
 	free(trimmed);
 
 	ret = erofs_scan_file_xattrs(inode);
@@ -1603,7 +1616,7 @@ static int erofs_mkfs_handle_inode(struct erofs_inode *inode)
 			if (ctx.fd < 0)
 				return -errno;
 
-			if (g_cfg.c_compr_opts[0].alg &&
+			if (cfg->c_compr_opts[0].alg &&
 			    erofs_file_is_compressible(inode)) {
 				ctx.ictx = erofs_begin_compressed_file(inode,
 								ctx.fd, 0);
@@ -1623,16 +1636,17 @@ static int erofs_mkfs_handle_inode(struct erofs_inode *inode)
 static int erofs_rebuild_handle_inode(struct erofs_inode *inode,
 				      bool incremental)
 {
+	struct erofs_configure *cfg = inode->sbi->bt_args->cfg;
 	char *trimmed;
 	int ret;
 
 	trimmed = erofs_trim_for_progressinfo(erofs_fspath(inode->i_srcpath),
 					      sizeof("Processing  ...") - 1);
-	erofs_update_progressinfo("Processing %s ...", trimmed);
+	erofs_update_progressinfo(inode->sbi, "Processing %s ...", trimmed);
 	free(trimmed);
 
 	if (erofs_should_use_inode_extended(inode)) {
-		if (g_cfg.c_force_inodeversion == FORCE_INODE_COMPACT) {
+		if (cfg->c_force_inodeversion == FORCE_INODE_COMPACT) {
 			erofs_err("file %s cannot be in compact form",
 				  inode->i_srcpath);
 			return -EINVAL;
@@ -1650,7 +1664,7 @@ static int erofs_rebuild_handle_inode(struct erofs_inode *inode,
 	}
 
 	/* strip all unnecessary overlayfs xattrs when ovlfs_strip is enabled */
-	if (g_cfg.c_ovlfs_strip)
+	if (cfg->c_ovlfs_strip)
 		erofs_clear_opaque_xattr(inode);
 	else if (inode->whiteouts)
 		erofs_set_origin_xattr(inode);
@@ -1669,7 +1683,7 @@ static int erofs_rebuild_handle_inode(struct erofs_inode *inode,
 			if (ctx.fd < 0)
 				return ret;
 
-			if (g_cfg.c_compr_opts[0].alg &&
+			if (cfg->c_compr_opts[0].alg &&
 			    erofs_file_is_compressible(inode)) {
 				ctx.ictx = erofs_begin_compressed_file(inode,
 							ctx.fd, ctx.fpos);
@@ -1702,6 +1716,7 @@ static int erofs_mkfs_dump_tree(struct erofs_inode *root, bool rebuild,
 				bool incremental)
 {
 	struct erofs_sb_info *sbi = root->sbi;
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
 	struct erofs_inode *dumpdir = erofs_igrab(root);
 	int err;
 
@@ -1713,8 +1728,8 @@ static int erofs_mkfs_dump_tree(struct erofs_inode *root, bool rebuild,
 		root->i_ino[1] = sbi->root_nid;
 		list_del(&root->i_hash);
 		erofs_insert_ihash(root);
-	} else if (g_cfg.c_root_xattr_isize) {
-		root->xattr_isize = g_cfg.c_root_xattr_isize;
+	} else if (cfg->c_root_xattr_isize) {
+		root->xattr_isize = cfg->c_root_xattr_isize;
 	}
 
 	err = !rebuild ? erofs_mkfs_handle_inode(root) :
@@ -1886,6 +1901,7 @@ int erofs_rebuild_dump_tree(struct erofs_inode *root, bool incremental)
 struct erofs_inode *erofs_mkfs_build_special_from_fd(struct erofs_sb_info *sbi,
 						     int fd, const char *name)
 {
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
 	struct stat st;
 	struct erofs_inode *inode;
 	void *ictx;
@@ -1919,7 +1935,7 @@ struct erofs_inode *erofs_mkfs_build_special_from_fd(struct erofs_sb_info *sbi,
 		inode->nid = inode->sbi->packed_nid;
 	}
 
-	if (g_cfg.c_compr_opts[0].alg &&
+	if (cfg->c_compr_opts[0].alg &&
 	    erofs_file_is_compressible(inode)) {
 		ictx = erofs_begin_compressed_file(inode, fd, 0);
 		if (IS_ERR(ictx))
diff --git a/lib/io.c b/lib/io.c
index 6d2c708..f284a35 100644
--- a/lib/io.c
+++ b/lib/io.c
@@ -28,7 +28,11 @@
 
 int erofs_io_fstat(struct erofs_vfile *vf, struct stat *buf)
 {
-	if (__erofs_unlikely(g_cfg.c_dry_run)) {
+	struct erofs_sb_info *sbi = container_of(vf, struct erofs_sb_info,
+						 bdev);
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
+
+	if (__erofs_unlikely(cfg->c_dry_run)) {
 		buf->st_size = 0;
 		buf->st_mode = S_IFREG | 0777;
 		return 0;
@@ -42,9 +46,12 @@ int erofs_io_fstat(struct erofs_vfile *vf, struct stat *buf)
 ssize_t erofs_io_pwrite(struct erofs_vfile *vf, const void *buf,
 			u64 pos, size_t len)
 {
+	struct erofs_sb_info *sbi = container_of(vf, struct erofs_sb_info,
+						 bdev);
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
 	ssize_t ret, written = 0;
 
-	if (__erofs_unlikely(g_cfg.c_dry_run))
+	if (__erofs_unlikely(cfg->c_dry_run))
 		return 0;
 
 	if (vf->ops)
@@ -76,9 +83,12 @@ ssize_t erofs_io_pwrite(struct erofs_vfile *vf, const void *buf,
 
 int erofs_io_fsync(struct erofs_vfile *vf)
 {
+	struct erofs_sb_info *sbi = container_of(vf, struct erofs_sb_info,
+						 bdev);
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
 	int ret;
 
-	if (__erofs_unlikely(g_cfg.c_dry_run))
+	if (__erofs_unlikely(cfg->c_dry_run))
 		return 0;
 
 	if (vf->ops)
@@ -95,10 +105,13 @@ int erofs_io_fsync(struct erofs_vfile *vf)
 ssize_t erofs_io_fallocate(struct erofs_vfile *vf, u64 offset,
 			   size_t len, bool zeroout)
 {
+	struct erofs_sb_info *sbi = container_of(vf, struct erofs_sb_info,
+						 bdev);
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
 	static const char zero[EROFS_MAX_BLOCK_SIZE] = {0};
 	ssize_t ret;
 
-	if (__erofs_unlikely(g_cfg.c_dry_run))
+	if (__erofs_unlikely(cfg->c_dry_run))
 		return 0;
 
 	if (vf->ops)
@@ -123,8 +136,11 @@ int erofs_io_ftruncate(struct erofs_vfile *vf, u64 length)
 {
 	int ret;
 	struct stat st;
+	struct erofs_sb_info *sbi = container_of(vf, struct erofs_sb_info,
+						 bdev);
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
 
-	if (__erofs_unlikely(g_cfg.c_dry_run))
+	if (__erofs_unlikely(cfg->c_dry_run))
 		return 0;
 
 	if (vf->ops)
@@ -143,9 +159,12 @@ int erofs_io_ftruncate(struct erofs_vfile *vf, u64 length)
 
 ssize_t erofs_io_pread(struct erofs_vfile *vf, void *buf, u64 pos, size_t len)
 {
+	struct erofs_sb_info *sbi = container_of(vf, struct erofs_sb_info,
+						 bdev);
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
 	ssize_t ret, read = 0;
 
-	if (__erofs_unlikely(g_cfg.c_dry_run))
+	if (__erofs_unlikely(cfg->c_dry_run))
 		return 0;
 
 	if (vf->ops)
diff --git a/lib/xattr.c b/lib/xattr.c
index b22a76f..b932426 100644
--- a/lib/xattr.c
+++ b/lib/xattr.c
@@ -293,26 +293,29 @@ out:
 	return ERR_PTR(ret);
 }
 
-static struct xattr_item *erofs_get_selabel_xattr(const char *srcpath,
+static struct xattr_item *erofs_get_selabel_xattr(struct erofs_sb_info *sbi,
+						  const char *srcpath,
 						  mode_t mode)
 {
 #ifdef HAVE_LIBSELINUX
-	if (g_cfg.sehnd) {
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
+
+	if (cfg->sehnd) {
 		char *secontext;
 		int ret;
 		unsigned int len[2];
 		char *kvbuf, *fspath;
 		struct xattr_item *item;
 
-		if (g_cfg.mount_point)
-			ret = asprintf(&fspath, "/%s/%s", g_cfg.mount_point,
+		if (cfg->mount_point)
+			ret = asprintf(&fspath, "/%s/%s", cfg->mount_point,
 				       erofs_fspath(srcpath));
 		else
 			ret = asprintf(&fspath, "/%s", erofs_fspath(srcpath));
 		if (ret <= 0)
 			return ERR_PTR(-ENOMEM);
 
-		ret = selabel_lookup(g_cfg.sehnd, &secontext, fspath, mode);
+		ret = selabel_lookup(cfg->sehnd, &secontext, fspath, mode);
 		free(fspath);
 
 		if (ret) {
@@ -364,12 +367,15 @@ static int shared_xattr_add(struct xattr_item *item)
 	return ++shared_xattrs_count;
 }
 
-static int erofs_xattr_add(struct list_head *ixattrs, struct xattr_item *item)
+static int erofs_xattr_add(struct erofs_sb_info *sbi, struct list_head *ixattrs,
+			   struct xattr_item *item)
 {
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
+
 	if (ixattrs)
 		return inode_xattr_add(ixattrs, item);
 
-	if (item->count == g_cfg.c_inline_xattr_tolerance + 1) {
+	if (item->count == cfg->c_inline_xattr_tolerance + 1) {
 		int ret = shared_xattr_add(item);
 
 		if (ret < 0)
@@ -378,18 +384,20 @@ static int erofs_xattr_add(struct list_head *ixattrs, struct xattr_item *item)
 	return 0;
 }
 
-static bool erofs_is_skipped_xattr(const char *key)
+static bool erofs_is_skipped_xattr(struct erofs_sb_info *sbi, const char *key)
 {
 #ifdef HAVE_LIBSELINUX
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
+
 	/* if sehnd is valid, selabels will be overridden */
-	if (g_cfg.sehnd && !strcmp(key, XATTR_SECURITY_PREFIX "selinux"))
+	if (cfg->sehnd && !strcmp(key, XATTR_SECURITY_PREFIX "selinux"))
 		return true;
 #endif
 	return false;
 }
 
-static int read_xattrs_from_file(const char *path, mode_t mode,
-				 struct list_head *ixattrs)
+static int read_xattrs_from_file(struct erofs_sb_info *sbi, const char *path,
+				 mode_t mode, struct list_head *ixattrs)
 {
 #ifdef HAVE_LLISTXATTR
 	ssize_t kllen = llistxattr(path, NULL, 0);
@@ -441,7 +449,7 @@ static int read_xattrs_from_file(const char *path, mode_t mode,
 
 	for (key = keylst; key != klend; key += keylen + 1) {
 		keylen = strlen(key);
-		if (erofs_is_skipped_xattr(key))
+		if (erofs_is_skipped_xattr(sbi, key))
 			continue;
 
 		item = parse_one_xattr(path, key, keylen);
@@ -453,7 +461,7 @@ static int read_xattrs_from_file(const char *path, mode_t mode,
 		if (!item)
 			continue;
 
-		ret = erofs_xattr_add(ixattrs, item);
+		ret = erofs_xattr_add(sbi, ixattrs, item);
 		if (ret < 0)
 			goto err;
 	}
@@ -461,11 +469,11 @@ static int read_xattrs_from_file(const char *path, mode_t mode,
 
 out:
 	/* if some selabel is avilable, need to add right now */
-	item = erofs_get_selabel_xattr(path, mode);
+	item = erofs_get_selabel_xattr(sbi, path, mode);
 	if (IS_ERR(item))
 		return PTR_ERR(item);
 	if (item)
-		ret = erofs_xattr_add(ixattrs, item);
+		ret = erofs_xattr_add(sbi, ixattrs, item);
 	return ret;
 
 err:
@@ -497,7 +505,7 @@ int erofs_setxattr(struct erofs_inode *inode, char *key,
 	}
 	DBG_BUGON(!item);
 
-	return erofs_xattr_add(&inode->i_xattrs, item);
+	return erofs_xattr_add(inode->sbi, &inode->i_xattrs, item);
 }
 
 static void erofs_removexattr(struct erofs_inode *inode, const char *key)
@@ -562,7 +570,7 @@ static int erofs_droid_xattr_set_caps(struct erofs_inode *inode)
 	}
 	DBG_BUGON(!item);
 
-	return erofs_xattr_add(&inode->i_xattrs, item);
+	return erofs_xattr_add(inode->sbi, &inode->i_xattrs, item);
 }
 #else
 static int erofs_droid_xattr_set_caps(struct erofs_inode *inode)
@@ -575,12 +583,14 @@ int erofs_scan_file_xattrs(struct erofs_inode *inode)
 {
 	int ret;
 	struct list_head *ixattrs = &inode->i_xattrs;
+	struct erofs_configure *cfg = inode->sbi->bt_args->cfg;
 
 	/* check if xattr is disabled */
-	if (g_cfg.c_inline_xattr_tolerance < 0)
+	if (cfg->c_inline_xattr_tolerance < 0)
 		return 0;
 
-	ret = read_xattrs_from_file(inode->i_srcpath, inode->i_mode, ixattrs);
+	ret = read_xattrs_from_file(inode->sbi, inode->i_srcpath,
+				    inode->i_mode, ixattrs);
 	if (ret < 0)
 		return ret;
 
@@ -705,7 +715,7 @@ out:
 	return ret;
 }
 
-static int erofs_count_all_xattrs_from_path(const char *path)
+static int erofs_count_all_xattrs_from_path(struct erofs_sb_info *sbi, const char *path)
 {
 	int ret;
 	DIR *_dir;
@@ -750,14 +760,14 @@ static int erofs_count_all_xattrs_from_path(const char *path)
 			goto fail;
 		}
 
-		ret = read_xattrs_from_file(buf, st.st_mode, NULL);
+		ret = read_xattrs_from_file(sbi, buf, st.st_mode, NULL);
 		if (ret)
 			goto fail;
 
 		if (!S_ISDIR(st.st_mode))
 			continue;
 
-		ret = erofs_count_all_xattrs_from_path(buf);
+		ret = erofs_count_all_xattrs_from_path(sbi, buf);
 		if (ret)
 			goto fail;
 	}
@@ -883,10 +893,11 @@ int erofs_build_shared_xattrs_from_path(struct erofs_sb_info *sbi, const char *p
 	unsigned int p, i;
 	erofs_off_t off;
 	erofs_off_t shared_xattrs_size = 0;
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
 
 	/* check if xattr or shared xattr is disabled */
-	if (g_cfg.c_inline_xattr_tolerance < 0 ||
-	    g_cfg.c_inline_xattr_tolerance == INT_MAX)
+	if (cfg->c_inline_xattr_tolerance < 0 ||
+	    cfg->c_inline_xattr_tolerance == INT_MAX)
 		return 0;
 
 	if (shared_xattrs_count) {
@@ -894,7 +905,7 @@ int erofs_build_shared_xattrs_from_path(struct erofs_sb_info *sbi, const char *p
 		return -EINVAL;
 	}
 
-	ret = erofs_count_all_xattrs_from_path(path);
+	ret = erofs_count_all_xattrs_from_path(sbi, path);
 	if (ret)
 		return ret;
 
@@ -957,6 +968,7 @@ out:
 
 char *erofs_export_xattr_ibody(struct erofs_inode *inode)
 {
+	struct erofs_configure *cfg = inode->sbi->bt_args->cfg;
 	struct list_head *ixattrs = &inode->i_xattrs;
 	unsigned int size = inode->xattr_isize;
 	struct inode_xattr_node *node, *n;
@@ -972,7 +984,7 @@ char *erofs_export_xattr_ibody(struct erofs_inode *inode)
 	header = (struct erofs_xattr_ibody_header *)buf;
 	header->h_shared_count = 0;
 
-	if (g_cfg.c_xattr_name_filter) {
+	if (cfg->c_xattr_name_filter) {
 		u32 name_filter = 0;
 		int hashbit;
 		unsigned int base_len;
diff --git a/mkfs/main.c b/mkfs/main.c
index 8ca1050..edfb4c2 100644
--- a/mkfs/main.c
+++ b/mkfs/main.c
@@ -215,10 +215,12 @@ static void usage(int argc, char **argv)
 	);
 }
 
-static void version(void)
+static void version(struct erofs_sb_info *sbi)
 {
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
+
 	printf("mkfs.erofs (erofs-utils) %s\navailable compressors: ",
-	       g_cfg.c_version);
+	       cfg->c_version);
 	print_available_compressors(stdout, ", ");
 }
 
@@ -240,32 +242,41 @@ static LIST_HEAD(rebuild_src_list);
 static u8 fixeduuid[16];
 static bool valid_fixeduuid;
 
-static int erofs_mkfs_feat_set_legacy_compress(bool en, const char *val,
+static int erofs_mkfs_feat_set_legacy_compress(struct erofs_sb_info *sbi,
+					       bool en, const char *val,
 					       unsigned int vallen)
 {
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
+
 	if (vallen)
 		return -EINVAL;
 	/* disable compacted indexes and 0padding */
-	g_cfg.c_legacy_compress = en;
+	cfg->c_legacy_compress = en;
 	return 0;
 }
 
-static int erofs_mkfs_feat_set_ztailpacking(bool en, const char *val,
+static int erofs_mkfs_feat_set_ztailpacking(struct erofs_sb_info *sbi,
+					    bool en, const char *val,
 					    unsigned int vallen)
 {
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
+
 	if (vallen)
 		return -EINVAL;
-	g_cfg.c_ztailpacking = en;
+	cfg->c_ztailpacking = en;
 	return 0;
 }
 
-static int erofs_mkfs_feat_set_fragments(bool en, const char *val,
+static int erofs_mkfs_feat_set_fragments(struct erofs_sb_info *sbi,
+					 bool en, const char *val,
 					 unsigned int vallen)
 {
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
+
 	if (!en) {
 		if (vallen)
 			return -EINVAL;
-		g_cfg.c_fragments = false;
+		cfg->c_fragments = false;
 		return 0;
 	}
 
@@ -279,29 +290,36 @@ static int erofs_mkfs_feat_set_fragments(bool en, const char *val,
 		}
 		pclustersize_packed = i;
 	}
-	g_cfg.c_fragments = true;
+	cfg->c_fragments = true;
 	return 0;
 }
 
-static int erofs_mkfs_feat_set_all_fragments(bool en, const char *val,
+static int erofs_mkfs_feat_set_all_fragments(struct erofs_sb_info *sbi,
+					     bool en, const char *val,
 					     unsigned int vallen)
 {
-	g_cfg.c_all_fragments = en;
-	return erofs_mkfs_feat_set_fragments(en, val, vallen);
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
+
+	cfg->c_all_fragments = en;
+	return erofs_mkfs_feat_set_fragments(sbi, en, val, vallen);
 }
 
-static int erofs_mkfs_feat_set_dedupe(bool en, const char *val,
+static int erofs_mkfs_feat_set_dedupe(struct erofs_sb_info *sbi,
+				      bool en, const char *val,
 				      unsigned int vallen)
 {
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
+
 	if (vallen)
 		return -EINVAL;
-	g_cfg.c_dedupe = en;
+	cfg->c_dedupe = en;
 	return 0;
 }
 
 static struct {
 	char *feat;
-	int (*set)(bool en, const char *val, unsigned int len);
+	int (*set)(struct erofs_sb_info *sbi, bool en, const char *val,
+		   unsigned int len);
 } z_erofs_mkfs_features[] = {
 	{"legacy-compress", erofs_mkfs_feat_set_legacy_compress},
 	{"ztailpacking", erofs_mkfs_feat_set_ztailpacking},
@@ -311,8 +329,10 @@ static struct {
 	{NULL, NULL},
 };
 
-static int parse_extended_opts(const char *opts)
+static int parse_extended_opts(struct erofs_sb_info *sbi, const char *opts)
 {
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
+
 #define MATCH_EXTENTED_OPT(opt, token, keylen) \
 	(keylen == strlen(opt) && !memcmp(token, opt, keylen))
 
@@ -356,12 +376,12 @@ static int parse_extended_opts(const char *opts)
 		if (MATCH_EXTENTED_OPT("force-inode-compact", token, keylen)) {
 			if (vallen)
 				return -EINVAL;
-			g_cfg.c_force_inodeversion = FORCE_INODE_COMPACT;
-			g_cfg.c_ignore_mtime = true;
+			cfg->c_force_inodeversion = FORCE_INODE_COMPACT;
+			cfg->c_ignore_mtime = true;
 		} else if (MATCH_EXTENTED_OPT("force-inode-extended", token, keylen)) {
 			if (vallen)
 				return -EINVAL;
-			g_cfg.c_force_inodeversion = FORCE_INODE_EXTENDED;
+			cfg->c_force_inodeversion = FORCE_INODE_EXTENDED;
 		} else if (MATCH_EXTENTED_OPT("nosbcrc", token, keylen)) {
 			if (vallen)
 				return -EINVAL;
@@ -369,23 +389,23 @@ static int parse_extended_opts(const char *opts)
 		} else if (MATCH_EXTENTED_OPT("noinline_data", token, keylen)) {
 			if (vallen)
 				return -EINVAL;
-			g_cfg.c_inline_data = false;
+			cfg->c_inline_data = false;
 		} else if (MATCH_EXTENTED_OPT("inline_data", token, keylen)) {
 			if (vallen)
 				return -EINVAL;
-			g_cfg.c_inline_data = !clear;
+			cfg->c_inline_data = !clear;
 		} else if (MATCH_EXTENTED_OPT("force-inode-blockmap", token, keylen)) {
 			if (vallen)
 				return -EINVAL;
-			g_cfg.c_force_chunkformat = FORCE_INODE_BLOCK_MAP;
+			cfg->c_force_chunkformat = FORCE_INODE_BLOCK_MAP;
 		} else if (MATCH_EXTENTED_OPT("force-chunk-indexes", token, keylen)) {
 			if (vallen)
 				return -EINVAL;
-			g_cfg.c_force_chunkformat = FORCE_INODE_CHUNK_INDEXES;
+			cfg->c_force_chunkformat = FORCE_INODE_CHUNK_INDEXES;
 		} else if (MATCH_EXTENTED_OPT("xattr-name-filter", token, keylen)) {
 			if (vallen)
 				return -EINVAL;
-			g_cfg.c_xattr_name_filter = !clear;
+			cfg->c_xattr_name_filter = !clear;
 		} else {
 			int i, err;
 
@@ -393,7 +413,7 @@ static int parse_extended_opts(const char *opts)
 				if (!MATCH_EXTENTED_OPT(z_erofs_mkfs_features[i].feat,
 							token, keylen))
 					continue;
-				err = z_erofs_mkfs_features[i].set(!clear, value, vallen);
+				err = z_erofs_mkfs_features[i].set(sbi, !clear, value, vallen);
 				if (err)
 					return err;
 				break;
@@ -409,7 +429,7 @@ static int parse_extended_opts(const char *opts)
 	return 0;
 }
 
-static int mkfs_apply_zfeature_bits(uintmax_t bits)
+static int mkfs_apply_zfeature_bits(struct erofs_sb_info *sbi, uintmax_t bits)
 {
 	int i;
 
@@ -420,7 +440,7 @@ static int mkfs_apply_zfeature_bits(uintmax_t bits)
 			erofs_err("unsupported zfeature bit %u", i);
 			return -EINVAL;
 		}
-		err = z_erofs_mkfs_features[i].set(bits & 1, NULL, 0);
+		err = z_erofs_mkfs_features[i].set(sbi, bits & 1, NULL, 0);
 		if (err) {
 			erofs_err("failed to apply zfeature %s",
 				  z_erofs_mkfs_features[i].feat);
@@ -514,8 +534,9 @@ static int mkfs_parse_one_compress_alg(char *alg,
 	return 0;
 }
 
-static int mkfs_parse_compress_algs(char *algs)
+static int mkfs_parse_compress_algs(struct erofs_sb_info *sbi, char *algs)
 {
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
 	unsigned int i;
 	char *s;
 	int ret;
@@ -526,7 +547,7 @@ static int mkfs_parse_compress_algs(char *algs)
 			return -EINVAL;
 		}
 
-		ret = mkfs_parse_one_compress_alg(s, &g_cfg.c_compr_opts[i]);
+		ret = mkfs_parse_one_compress_alg(s, &cfg->c_compr_opts[i]);
 		if (ret)
 			return ret;
 	}
@@ -546,19 +567,21 @@ static void erofs_rebuild_cleanup(void)
 	rebuild_src_count = 0;
 }
 
-static int mkfs_parse_options_cfg(int argc, char *argv[])
+static int mkfs_parse_options_cfg(struct erofs_sb_info *sbi, int argc,
+				  char *argv[])
 {
 	char *endptr;
 	int opt, i, err;
 	bool quiet = false;
 	int tarerofs_decoder = 0;
 	bool has_timestamp = false;
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
 
 	while ((opt = getopt_long(argc, argv, "C:E:L:T:U:b:d:x:z:Vh",
 				  long_options, NULL)) != -1) {
 		switch (opt) {
 		case 'z':
-			i = mkfs_parse_compress_algs(optarg);
+			i = mkfs_parse_compress_algs(sbi, optarg);
 			if (i)
 				return i;
 			break;
@@ -578,7 +601,7 @@ static int mkfs_parse_options_cfg(int argc, char *argv[])
 				erofs_err("invalid debug level %d", i);
 				return -EINVAL;
 			}
-			g_cfg.c_dbg_lvl = i;
+			cfg->c_dbg_lvl = i;
 			break;
 
 		case 'x':
@@ -587,11 +610,11 @@ static int mkfs_parse_options_cfg(int argc, char *argv[])
 				erofs_err("invalid xattr tolerance %s", optarg);
 				return -EINVAL;
 			}
-			g_cfg.c_inline_xattr_tolerance = i;
+			cfg->c_inline_xattr_tolerance = i;
 			break;
 
 		case 'E':
-			opt = parse_extended_opts(optarg);
+			opt = parse_extended_opts(sbi, optarg);
 			if (opt)
 				return opt;
 			break;
@@ -607,8 +630,8 @@ static int mkfs_parse_options_cfg(int argc, char *argv[])
 			break;
 
 		case 'T':
-			g_cfg.c_unix_timestamp = strtoull(optarg, &endptr, 0);
-			if (g_cfg.c_unix_timestamp == -1 || *endptr != '\0') {
+			cfg->c_unix_timestamp = strtoull(optarg, &endptr, 0);
+			if (cfg->c_unix_timestamp == -1 || *endptr != '\0') {
 				erofs_err("invalid UNIX timestamp %s", optarg);
 				return -EINVAL;
 			}
@@ -639,37 +662,37 @@ static int mkfs_parse_options_cfg(int argc, char *argv[])
 			break;
 
 		case 4:
-			opt = erofs_selabel_open(optarg);
+			opt = erofs_selabel_open(sbi, optarg);
 			if (opt && opt != -EBUSY)
 				return opt;
 			break;
 		case 5:
-			g_cfg.c_uid = strtoul(optarg, &endptr, 0);
-			if (g_cfg.c_uid == -1 || *endptr != '\0') {
+			cfg->c_uid = strtoul(optarg, &endptr, 0);
+			if (cfg->c_uid == -1 || *endptr != '\0') {
 				erofs_err("invalid uid %s", optarg);
 				return -EINVAL;
 			}
 			break;
 		case 6:
-			g_cfg.c_gid = strtoul(optarg, &endptr, 0);
-			if (g_cfg.c_gid == -1 || *endptr != '\0') {
+			cfg->c_gid = strtoul(optarg, &endptr, 0);
+			if (cfg->c_gid == -1 || *endptr != '\0') {
 				erofs_err("invalid gid %s", optarg);
 				return -EINVAL;
 			}
 			break;
 		case 7:
-			g_cfg.c_uid = g_cfg.c_gid = 0;
+			cfg->c_uid = cfg->c_gid = 0;
 			break;
 #ifndef NDEBUG
 		case 8:
-			g_cfg.c_random_pclusterblks = true;
+			cfg->c_random_pclusterblks = true;
 			break;
 		case 18:
-			g_cfg.c_random_algorithms = true;
+			cfg->c_random_algorithms = true;
 			break;
 #endif
 		case 9:
-			g_cfg.c_max_decompressed_extent_bytes =
+			cfg->c_max_decompressed_extent_bytes =
 				strtoul(optarg, &endptr, 0);
 			if (*endptr != '\0') {
 				erofs_err("invalid maximum uncompressed extent size %s",
@@ -678,24 +701,24 @@ static int mkfs_parse_options_cfg(int argc, char *argv[])
 			}
 			break;
 		case 10:
-			g_cfg.c_compress_hints_file = optarg;
+			cfg->c_compress_hints_file = optarg;
 			break;
 		case 512:
-			g_cfg.mount_point = optarg;
+			cfg->mount_point = optarg;
 			/* all trailing '/' should be deleted */
-			opt = strlen(g_cfg.mount_point);
+			opt = strlen(cfg->mount_point);
 			if (opt && optarg[opt - 1] == '/')
 				optarg[opt - 1] = '\0';
 			break;
 #ifdef WITH_ANDROID
 		case 513:
-			g_cfg.target_out_path = optarg;
+			cfg->target_out_path = optarg;
 			break;
 		case 514:
-			g_cfg.fs_config_file = optarg;
+			cfg->fs_config_file = optarg;
 			break;
 		case 515:
-			g_cfg.block_list_file = optarg;
+			cfg->block_list_file = optarg;
 			break;
 #endif
 		case 'C':
@@ -713,8 +736,8 @@ static int mkfs_parse_options_cfg(int argc, char *argv[])
 				erofs_err("invalid chunksize %s", optarg);
 				return -EINVAL;
 			}
-			g_cfg.c_chunkbits = ilog2(i);
-			if ((1 << g_cfg.c_chunkbits) != i) {
+			cfg->c_chunkbits = ilog2(i);
+			if ((1 << cfg->c_chunkbits) != i) {
 				erofs_err("chunksize %s must be a power of two",
 					  optarg);
 				return -EINVAL;
@@ -725,17 +748,17 @@ static int mkfs_parse_options_cfg(int argc, char *argv[])
 			quiet = true;
 			break;
 		case 13:
-			g_cfg.c_blobdev_path = optarg;
+			cfg->c_blobdev_path = optarg;
 			break;
 		case 14:
-			g_cfg.c_ignore_mtime = true;
+			cfg->c_ignore_mtime = true;
 			break;
 		case 15:
-			g_cfg.c_ignore_mtime = false;
+			cfg->c_ignore_mtime = false;
 			break;
 		case 16:
 			errno = 0;
-			g_cfg.c_uid_offset = strtoll(optarg, &endptr, 0);
+			cfg->c_uid_offset = strtoll(optarg, &endptr, 0);
 			if (errno || *endptr != '\0') {
 				erofs_err("invalid uid offset %s", optarg);
 				return -EINVAL;
@@ -743,7 +766,7 @@ static int mkfs_parse_options_cfg(int argc, char *argv[])
 			break;
 		case 17:
 			errno = 0;
-			g_cfg.c_gid_offset = strtoll(optarg, &endptr, 0);
+			cfg->c_gid_offset = strtoll(optarg, &endptr, 0);
 			if (errno || *endptr != '\0') {
 				erofs_err("invalid gid offset %s", optarg);
 				return -EINVAL;
@@ -757,7 +780,7 @@ static int mkfs_parse_options_cfg(int argc, char *argv[])
 					  erofs_strerror(opt));
 				return opt;
 			}
-			g_cfg.c_extra_ea_name_prefixes = true;
+			cfg->c_extra_ea_name_prefixes = true;
 			break;
 		case 20:
 			mkfs_parse_tar_cfg(optarg);
@@ -767,9 +790,9 @@ static int mkfs_parse_options_cfg(int argc, char *argv[])
 			break;
 		case 516:
 			if (!optarg || !strcmp(optarg, "1"))
-				g_cfg.c_ovlfs_strip = true;
+				cfg->c_ovlfs_strip = true;
 			else
-				g_cfg.c_ovlfs_strip = false;
+				cfg->c_ovlfs_strip = false;
 			break;
 		case 517:
 			g_sbi.bdev.offset = strtoull(optarg, &endptr, 0);
@@ -788,16 +811,16 @@ static int mkfs_parse_options_cfg(int argc, char *argv[])
 		case 520: {
 			unsigned int processors;
 
-			g_cfg.c_mt_workers = strtoul(optarg, &endptr, 0);
+			cfg->c_mt_workers = strtoul(optarg, &endptr, 0);
 			if (errno || *endptr != '\0') {
 				erofs_err("invalid worker number %s", optarg);
 				return -EINVAL;
 			}
 
 			processors = erofs_get_available_processors();
-			if (g_cfg.c_mt_workers > processors)
+			if (cfg->c_mt_workers > processors)
 				erofs_warn("%d workers exceed %d processors, potentially impacting performance.",
-					   g_cfg.c_mt_workers, processors);
+					   cfg->c_mt_workers, processors);
 			break;
 		}
 #endif
@@ -807,7 +830,7 @@ static int mkfs_parse_options_cfg(int argc, char *argv[])
 				erofs_err("invalid zfeature bits %s", optarg);
 				return -EINVAL;
 			}
-			err = mkfs_apply_zfeature_bits(i);
+			err = mkfs_apply_zfeature_bits(sbi, i);
 			if (err)
 				return err;
 			break;
@@ -828,20 +851,20 @@ static int mkfs_parse_options_cfg(int argc, char *argv[])
 			incremental_mode = (opt == 523);
 			break;
 		case 524:
-			g_cfg.c_root_xattr_isize = strtoull(optarg, &endptr, 0);
+			cfg->c_root_xattr_isize = strtoull(optarg, &endptr, 0);
 			if (*endptr != '\0') {
 				erofs_err("invalid the minimum inline xattr size %s", optarg);
 				return -EINVAL;
 			}
 			break;
 		case 525:
-			g_cfg.c_timeinherit = TIMESTAMP_NONE;
+			cfg->c_timeinherit = TIMESTAMP_NONE;
 			break;
 		case 526:
-			g_cfg.c_timeinherit = TIMESTAMP_FIXED;
+			cfg->c_timeinherit = TIMESTAMP_FIXED;
 			break;
 		case 'V':
-			version();
+			version(sbi);
 			exit(0);
 		case 'h':
 			usage(argc, argv);
@@ -852,14 +875,14 @@ static int mkfs_parse_options_cfg(int argc, char *argv[])
 		}
 	}
 
-	if (g_cfg.c_blobdev_path && g_cfg.c_chunkbits < g_sbi.blkszbits) {
+	if (cfg->c_blobdev_path && cfg->c_chunkbits < g_sbi.blkszbits) {
 		erofs_err("--blobdev must be used together with --chunksize");
 		return -EINVAL;
 	}
 
 	/* TODO: can be implemented with (deviceslot) mapped_blkaddr */
-	if (g_cfg.c_blobdev_path &&
-	    g_cfg.c_force_chunkformat == FORCE_INODE_BLOCK_MAP) {
+	if (cfg->c_blobdev_path &&
+	    cfg->c_force_chunkformat == FORCE_INODE_BLOCK_MAP) {
 		erofs_err("--blobdev cannot work with block map currently");
 		return -EINVAL;
 	}
@@ -869,8 +892,8 @@ static int mkfs_parse_options_cfg(int argc, char *argv[])
 		return -EINVAL;
 	}
 
-	g_cfg.c_img_path = strdup(argv[optind++]);
-	if (!g_cfg.c_img_path)
+	cfg->c_img_path = strdup(argv[optind++]);
+	if (!cfg->c_img_path)
 		return -ENOMEM;
 
 	if (optind >= argc) {
@@ -894,18 +917,18 @@ static int mkfs_parse_options_cfg(int argc, char *argv[])
 	} else {
 		struct stat st;
 
-		g_cfg.c_src_path = realpath(argv[optind++], NULL);
-		if (!g_cfg.c_src_path) {
+		cfg->c_src_path = realpath(argv[optind++], NULL);
+		if (!cfg->c_src_path) {
 			erofs_err("failed to parse source directory: %s",
 				  erofs_strerror(-errno));
 			return -ENOENT;
 		}
 
 		if (tar_mode) {
-			int fd = open(g_cfg.c_src_path, O_RDONLY);
+			int fd = open(cfg->c_src_path, O_RDONLY);
 
 			if (fd < 0) {
-				erofs_err("failed to open file: %s", g_cfg.c_src_path);
+				erofs_err("failed to open file: %s", cfg->c_src_path);
 				return -errno;
 			}
 			err = erofs_iostream_open(&erofstar.ios, fd,
@@ -924,17 +947,17 @@ static int mkfs_parse_options_cfg(int argc, char *argv[])
 				erofstar.ios.dumpfd = fd;
 			}
 		} else {
-			err = lstat(g_cfg.c_src_path, &st);
+			err = lstat(cfg->c_src_path, &st);
 			if (err)
 				return -errno;
 			if (S_ISDIR(st.st_mode))
-				erofs_set_fs_root(g_cfg.c_src_path);
+				erofs_set_fs_root(cfg->c_src_path);
 			else
 				rebuild_mode = true;
 		}
 
 		if (rebuild_mode) {
-			char *srcpath = g_cfg.c_src_path;
+			char *srcpath = cfg->c_src_path;
 			struct erofs_sb_info *src;
 
 			do {
@@ -961,11 +984,11 @@ static int mkfs_parse_options_cfg(int argc, char *argv[])
 		}
 	}
 	if (quiet) {
-		g_cfg.c_dbg_lvl = EROFS_ERR;
-		g_cfg.c_showprogress = false;
+		cfg->c_dbg_lvl = EROFS_ERR;
+		cfg->c_showprogress = false;
 	}
 
-	if (g_cfg.c_compr_opts[0].alg && erofs_blksiz(&g_sbi) != getpagesize())
+	if (cfg->c_compr_opts[0].alg && erofs_blksiz(&g_sbi) != getpagesize())
 		erofs_warn("Please note that subpage blocksize with compression isn't yet supported in kernel. "
 			   "This compressed image will only work with bs = ps = %u bytes",
 			   erofs_blksiz(&g_sbi));
@@ -977,12 +1000,12 @@ static int mkfs_parse_options_cfg(int argc, char *argv[])
 				  pclustersize_max);
 			return -EINVAL;
 		}
-		g_cfg.c_mkfs_pclustersize_max = pclustersize_max;
-		g_cfg.c_mkfs_pclustersize_def = g_cfg.c_mkfs_pclustersize_max;
+		cfg->c_mkfs_pclustersize_max = pclustersize_max;
+		cfg->c_mkfs_pclustersize_def = cfg->c_mkfs_pclustersize_max;
 	}
-	if (g_cfg.c_chunkbits && g_cfg.c_chunkbits < g_sbi.blkszbits) {
+	if (cfg->c_chunkbits && cfg->c_chunkbits < g_sbi.blkszbits) {
 		erofs_err("chunksize %u must be larger than block size",
-			  1u << g_cfg.c_chunkbits);
+			  1u << cfg->c_chunkbits);
 		return -EINVAL;
 	}
 
@@ -993,35 +1016,38 @@ static int mkfs_parse_options_cfg(int argc, char *argv[])
 				  pclustersize_packed);
 			return -EINVAL;
 		}
-		g_cfg.c_mkfs_pclustersize_packed = pclustersize_packed;
+		cfg->c_mkfs_pclustersize_packed = pclustersize_packed;
 	}
 
-	if (has_timestamp && g_cfg.c_timeinherit == TIMESTAMP_UNSPECIFIED)
-		g_cfg.c_timeinherit = TIMESTAMP_FIXED;
+	if (has_timestamp && cfg->c_timeinherit == TIMESTAMP_UNSPECIFIED)
+		cfg->c_timeinherit = TIMESTAMP_FIXED;
 	return 0;
 }
 
-static void erofs_mkfs_default_options(void)
+static void erofs_mkfs_default_options(struct erofs_sb_info *sbi)
 {
-	g_cfg.c_showprogress = true;
-	g_cfg.c_legacy_compress = false;
-	g_cfg.c_inline_data = true;
-	g_cfg.c_xattr_name_filter = true;
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
+
+	cfg->c_showprogress = true;
+	cfg->c_legacy_compress = false;
+	cfg->c_inline_data = true;
+	cfg->c_xattr_name_filter = true;
 #ifdef EROFS_MT_ENABLED
-	g_cfg.c_mt_workers = erofs_get_available_processors();
-	g_cfg.c_mkfs_segment_size = 16ULL * 1024 * 1024;
+	cfg->c_mt_workers = erofs_get_available_processors();
+	cfg->c_mkfs_segment_size = 16ULL * 1024 * 1024;
 #endif
 	g_sbi.blkszbits = ilog2(min_t(u32, getpagesize(), EROFS_MAX_BLOCK_SIZE));
-	g_cfg.c_mkfs_pclustersize_max = erofs_blksiz(&g_sbi);
-	g_cfg.c_mkfs_pclustersize_def = g_cfg.c_mkfs_pclustersize_max;
+	cfg->c_mkfs_pclustersize_max = erofs_blksiz(&g_sbi);
+	cfg->c_mkfs_pclustersize_def = cfg->c_mkfs_pclustersize_max;
 	g_sbi.feature_incompat = EROFS_FEATURE_INCOMPAT_ZERO_PADDING;
 	g_sbi.feature_compat = EROFS_FEATURE_COMPAT_SB_CHKSUM |
 			     EROFS_FEATURE_COMPAT_MTIME;
 }
 
 /* https://reproducible-builds.org/specs/source-date-epoch/ for more details */
-int parse_source_date_epoch(void)
+int parse_source_date_epoch(struct erofs_sb_info *sbi)
 {
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
 	char *source_date_epoch;
 	unsigned long long epoch = -1ULL;
 	char *endptr;
@@ -1037,19 +1063,21 @@ int parse_source_date_epoch(void)
 		return -EINVAL;
 	}
 
-	if (g_cfg.c_force_inodeversion != FORCE_INODE_EXTENDED)
+	if (cfg->c_force_inodeversion != FORCE_INODE_EXTENDED)
 		erofs_info("SOURCE_DATE_EPOCH is set, forcely generate extended inodes instead");
 
-	g_cfg.c_force_inodeversion = FORCE_INODE_EXTENDED;
-	g_cfg.c_unix_timestamp = epoch;
-	g_cfg.c_timeinherit = TIMESTAMP_CLAMPING;
+	cfg->c_force_inodeversion = FORCE_INODE_EXTENDED;
+	cfg->c_unix_timestamp = epoch;
+	cfg->c_timeinherit = TIMESTAMP_CLAMPING;
 	return 0;
 }
 
-void erofs_show_progs(int argc, char *argv[])
+void erofs_show_progs(struct erofs_sb_info *sbi, int argc, char *argv[])
 {
-	if (g_cfg.c_dbg_lvl >= EROFS_WARN)
-		printf("%s %s\n", basename(argv[0]), g_cfg.c_version);
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
+
+	if (cfg->c_dbg_lvl >= EROFS_WARN)
+		printf("%s %s\n", basename(argv[0]), cfg->c_version);
 }
 
 static int erofs_mkfs_rebuild_load_trees(struct erofs_inode *root)
@@ -1131,12 +1159,13 @@ static int erofs_mkfs_rebuild_load_trees(struct erofs_inode *root)
 	return 0;
 }
 
-static void erofs_mkfs_showsummaries(erofs_blk_t nblocks)
+static void erofs_mkfs_showsummaries(struct erofs_sb_info *sbi, erofs_blk_t nblocks)
 {
+	struct erofs_configure *cfg = sbi->bt_args->cfg;
 	char uuid_str[37] = {};
 	char *incr = incremental_mode ? "new" : "total";
 
-	if (!(g_cfg.c_dbg_lvl > EROFS_ERR && g_cfg.c_showprogress))
+	if (!(cfg->c_dbg_lvl > EROFS_ERR && cfg->c_showprogress))
 		return;
 
 	erofs_uuid_unparse_lower(g_sbi.uuid, uuid_str);
@@ -1161,33 +1190,36 @@ int main(int argc, char **argv)
 	FILE *packedfile = NULL;
 	FILE *blklst = NULL;
 	u32 crc;
+	struct erofs_configure *cfg = NULL;
 
-	erofs_init_configure();
-	erofs_mkfs_default_options();
+	erofs_init_buildtree_cfg(&g_sbi, &g_cfg);
+	erofs_mkfs_default_options(&g_sbi);
 
-	err = mkfs_parse_options_cfg(argc, argv);
-	erofs_show_progs(argc, argv);
+	cfg = g_sbi.bt_args->cfg;
+	BUG_ON(!cfg);
+	err = mkfs_parse_options_cfg(&g_sbi, argc, argv);
+	erofs_show_progs(&g_sbi, argc, argv);
 	if (err) {
 		if (err == -EINVAL)
 			fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]);
 		return 1;
 	}
 
-	err = parse_source_date_epoch();
+	err = parse_source_date_epoch(&g_sbi);
 	if (err) {
 		fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]);
 		return 1;
 	}
 
-	if (g_cfg.c_unix_timestamp != -1) {
-		g_sbi.build_time      = g_cfg.c_unix_timestamp;
+	if (cfg->c_unix_timestamp != -1) {
+		g_sbi.build_time      = cfg->c_unix_timestamp;
 		g_sbi.build_time_nsec = 0;
 	} else if (!gettimeofday(&t, NULL)) {
 		g_sbi.build_time      = t.tv_sec;
 		g_sbi.build_time_nsec = t.tv_usec;
 	}
 
-	err = erofs_dev_open(&g_sbi, g_cfg.c_img_path, O_RDWR |
+	err = erofs_dev_open(&g_sbi, cfg->c_img_path, O_RDWR |
 				(incremental_mode ? 0 : O_TRUNC));
 	if (err) {
 		fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]);
@@ -1195,24 +1227,24 @@ int main(int argc, char **argv)
 	}
 
 #ifdef WITH_ANDROID
-	if (g_cfg.fs_config_file &&
-	    load_canned_fs_config(g_cfg.fs_config_file) < 0) {
-		erofs_err("failed to load fs config %s", g_cfg.fs_config_file);
+	if (cfg->fs_config_file &&
+	    load_canned_fs_config(cfg->fs_config_file) < 0) {
+		erofs_err("failed to load fs config %s", cfg->fs_config_file);
 		return 1;
 	}
 
-	if (g_cfg.block_list_file) {
-		blklst = fopen(g_cfg.block_list_file, "w");
+	if (cfg->block_list_file) {
+		blklst = fopen(cfg->block_list_file, "w");
 		if (!blklst || erofs_blocklist_open(blklst, false)) {
-			erofs_err("failed to open %s", g_cfg.block_list_file);
+			erofs_err("failed to open %s", cfg->block_list_file);
 			return 1;
 		}
 	}
 #endif
-	erofs_show_config();
-	if (g_cfg.c_fragments || g_cfg.c_extra_ea_name_prefixes) {
-		if (!g_cfg.c_mkfs_pclustersize_packed)
-			g_cfg.c_mkfs_pclustersize_packed = g_cfg.c_mkfs_pclustersize_def;
+	erofs_show_config(cfg);
+	if (cfg->c_fragments || cfg->c_extra_ea_name_prefixes) {
+		if (!cfg->c_mkfs_pclustersize_packed)
+			cfg->c_mkfs_pclustersize_packed = cfg->c_mkfs_pclustersize_def;
 
 		packedfile = erofs_packedfile_init();
 		if (IS_ERR(packedfile)) {
@@ -1221,7 +1253,7 @@ int main(int argc, char **argv)
 		}
 	}
 
-	if (g_cfg.c_fragments) {
+	if (cfg->c_fragments) {
 		err = z_erofs_fragments_init();
 		if (err) {
 			erofs_err("failed to initialize fragments");
@@ -1230,7 +1262,7 @@ int main(int argc, char **argv)
 	}
 
 #ifndef NDEBUG
-	if (g_cfg.c_random_pclusterblks)
+	if (cfg->c_random_pclusterblks)
 		srand(time(NULL));
 #endif
 	if (tar_mode) {
@@ -1325,7 +1357,7 @@ int main(int argc, char **argv)
 	err = erofs_load_compress_hints(&g_sbi);
 	if (err) {
 		erofs_err("failed to load compress hints %s",
-			  g_cfg.c_compress_hints_file);
+			  cfg->c_compress_hints_file);
 		goto exit;
 	}
 
@@ -1336,10 +1368,10 @@ int main(int argc, char **argv)
 		goto exit;
 	}
 
-	if (g_cfg.c_dedupe) {
-		if (!g_cfg.c_compr_opts[0].alg) {
+	if (cfg->c_dedupe) {
+		if (!cfg->c_compr_opts[0].alg) {
 			erofs_err("Compression is not enabled.  Turn on chunk-based data deduplication instead.");
-			g_cfg.c_chunkbits = g_sbi.blkszbits;
+			cfg->c_chunkbits = g_sbi.blkszbits;
 		} else {
 			err = z_erofs_dedupe_init(erofs_blksiz(&g_sbi));
 			if (err) {
@@ -1350,14 +1382,14 @@ int main(int argc, char **argv)
 		}
 	}
 
-	if (g_cfg.c_chunkbits) {
-		err = erofs_blob_init(g_cfg.c_blobdev_path, 1 << g_cfg.c_chunkbits);
+	if (cfg->c_chunkbits) {
+		err = erofs_blob_init(cfg->c_blobdev_path, 1 << cfg->c_chunkbits);
 		if (err)
 			return 1;
 	}
 
 	if (((erofstar.index_mode && !erofstar.headeronly_mode) &&
-	    !erofstar.mapfile) || g_cfg.c_blobdev_path) {
+	    !erofstar.mapfile) || cfg->c_blobdev_path) {
 		err = erofs_mkfs_init_devices(&g_sbi, 1);
 		if (err) {
 			erofs_err("failed to generate device table: %s",
@@ -1397,17 +1429,17 @@ int main(int argc, char **argv)
 		if (err)
 			goto exit;
 	} else {
-		err = erofs_build_shared_xattrs_from_path(&g_sbi, g_cfg.c_src_path);
+		err = erofs_build_shared_xattrs_from_path(&g_sbi, cfg->c_src_path);
 		if (err) {
 			erofs_err("failed to build shared xattrs: %s",
 				  erofs_strerror(err));
 			goto exit;
 		}
 
-		if (g_cfg.c_extra_ea_name_prefixes)
+		if (cfg->c_extra_ea_name_prefixes)
 			erofs_xattr_write_name_prefixes(&g_sbi, packedfile);
 
-		root = erofs_mkfs_build_tree_from_path(&g_sbi, g_cfg.c_src_path);
+		root = erofs_mkfs_build_tree_from_path(&g_sbi, cfg->c_src_path);
 		if (IS_ERR(root)) {
 			err = PTR_ERR(root);
 			goto exit;
@@ -1418,13 +1450,13 @@ int main(int argc, char **argv)
 		g_sbi.devs[0].blocks = BLK_ROUND_UP(&g_sbi, erofstar.offset);
 
 	if (erofs_sb_has_fragments(&g_sbi)) {
-		erofs_update_progressinfo("Handling packed data ...");
+		erofs_update_progressinfo(&g_sbi, "Handling packed data ...");
 		err = erofs_flush_packed_inode(&g_sbi);
 		if (err)
 			goto exit;
 	}
 
-	if (erofstar.index_mode || g_cfg.c_chunkbits || g_sbi.extra_devices) {
+	if (erofstar.index_mode || cfg->c_chunkbits || g_sbi.extra_devices) {
 		err = erofs_mkfs_dump_blobs(&g_sbi);
 		if (err)
 			goto exit;
@@ -1458,7 +1490,7 @@ int main(int argc, char **argv)
 exit:
 	if (root)
 		erofs_iput(root);
-	z_erofs_compress_exit();
+	z_erofs_compress_exit(&g_sbi);
 	z_erofs_dedupe_exit();
 	blklst = erofs_blocklist_close();
 	if (blklst)
@@ -1466,15 +1498,14 @@ exit:
 	erofs_dev_close(&g_sbi);
 	erofs_cleanup_compress_hints();
 	erofs_cleanup_exclude_rules();
-	if (g_cfg.c_chunkbits)
+	if (cfg->c_chunkbits)
 		erofs_blob_exit();
-	if (g_cfg.c_fragments)
+	if (cfg->c_fragments)
 		z_erofs_fragments_exit();
 	erofs_packedfile_exit();
 	erofs_xattr_cleanup_name_prefixes();
 	erofs_rebuild_cleanup();
 	erofs_diskbuf_exit();
-	erofs_exit_configure();
 	if (tar_mode) {
 		erofs_iostream_close(&erofstar.ios);
 		if (erofstar.ios.dumpfd >= 0)
@@ -1486,8 +1517,9 @@ exit:
 			  erofs_strerror(err));
 		return 1;
 	}
-	erofs_update_progressinfo("Build completed.\n");
-	erofs_mkfs_showsummaries(nblocks);
+	erofs_update_progressinfo(&g_sbi, "Build completed.\n");
+	erofs_mkfs_showsummaries(&g_sbi, nblocks);
+	erofs_exit_buildtree_cfg(&g_sbi);
 	erofs_put_super(&g_sbi);
 	return 0;
 }
-- 
2.43.5



More information about the Linux-erofs mailing list