linux-erofs.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/4] erofs-utils: remove global erofs_devfd
@ 2022-09-09  2:18 Huang Jianan
  2022-09-09  2:18 ` [PATCH 2/4] erofs-utils: remove global sbi Huang Jianan
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Huang Jianan @ 2022-09-09  2:18 UTC (permalink / raw)
  To: linux-erofs

Move global erofs_dev related variables to sbi->dev, which will
make erofs-utils more library friendly.

Signed-off-by: Huang Jianan <jnhuang@linux.alibaba.com>
---
 dump/main.c              |  20 ++++--
 fsck/main.c              |  28 +++++---
 fuse/main.c              |  14 ++--
 include/erofs/cache.h    |   6 +-
 include/erofs/config.h   |   2 +
 include/erofs/internal.h |   8 +++
 include/erofs/io.h       |  37 ++++++-----
 lib/blobchunk.c          |  11 ++--
 lib/cache.c              |  12 ++--
 lib/compress.c           |  11 ++--
 lib/config.c             |  12 ++++
 lib/data.c               |   6 +-
 lib/inode.c              |  28 ++++----
 lib/io.c                 | 139 ++++++++++++++++++++-------------------
 lib/namei.c              |   5 +-
 lib/super.c              |   4 +-
 lib/xattr.c              |  17 ++---
 lib/zmap.c               |   4 +-
 mkfs/main.c              |  19 ++++--
 19 files changed, 225 insertions(+), 158 deletions(-)

diff --git a/dump/main.c b/dump/main.c
index f2a09b6..07fa151 100644
--- a/dump/main.c
+++ b/dump/main.c
@@ -152,7 +152,7 @@ static int erofsdump_parse_options_cfg(int argc, char **argv)
 			usage();
 			exit(0);
 		case 3:
-			err = blob_open_ro(optarg);
+			err = blob_open_ro(sbi.dev, optarg);
 			if (err)
 				return err;
 			++sbi.extra_devices;
@@ -599,17 +599,24 @@ int main(int argc, char **argv)
 	int err;
 
 	erofs_init_configure();
+
+	sbi.dev = erofs_init_dev();
+	if (IS_ERR(sbi.dev)) {
+		err = PTR_ERR(sbi.dev);
+		goto exit;
+	}
+
 	err = erofsdump_parse_options_cfg(argc, argv);
 	if (err) {
 		if (err == -EINVAL)
 			usage();
-		goto exit;
+		goto exit_blob_close;
 	}
 
-	err = dev_open_ro(cfg.c_img_path);
+	err = dev_open_ro(sbi.dev, cfg.c_img_path);
 	if (err) {
 		erofs_err("failed to open image file");
-		goto exit;
+		goto exit_blob_close;
 	}
 
 	err = erofs_read_superblock();
@@ -639,9 +646,10 @@ int main(int argc, char **argv)
 exit_put_super:
 	erofs_put_super();
 exit_dev_close:
-	dev_close();
+	dev_close(sbi.dev);
+exit_blob_close:
+	blob_closeall(sbi.dev);
 exit:
-	blob_closeall();
 	erofs_exit_configure();
 	return err;
 }
diff --git a/fsck/main.c b/fsck/main.c
index 410e756..1c8f567 100644
--- a/fsck/main.c
+++ b/fsck/main.c
@@ -143,7 +143,7 @@ static int erofsfsck_parse_options_cfg(int argc, char **argv)
 			}
 			break;
 		case 3:
-			ret = blob_open_ro(optarg);
+			ret = blob_open_ro(sbi.dev, optarg);
 			if (ret)
 				return ret;
 			++sbi.extra_devices;
@@ -263,7 +263,7 @@ static int erofs_check_sb_chksum(void)
 	u32 crc;
 	struct erofs_super_block *sb;
 
-	ret = blk_read(0, buf, 0, 1);
+	ret = blk_read(sbi.dev, 0, buf, 0, 1);
 	if (ret) {
 		erofs_err("failed to read superblock to check checksum: %d",
 			  ret);
@@ -291,6 +291,7 @@ static int erofs_verify_xattr(struct erofs_inode *inode)
 	unsigned int ofs, xattr_shared_count;
 	struct erofs_xattr_ibody_header *ih;
 	struct erofs_xattr_entry *entry;
+	struct erofs_device *dev = sbi.dev;
 	int i, remaining = inode->xattr_isize, ret = 0;
 	char buf[EROFS_BLKSIZ];
 
@@ -309,7 +310,7 @@ static int erofs_verify_xattr(struct erofs_inode *inode)
 	}
 
 	addr = iloc(inode->nid) + inode->inode_isize;
-	ret = dev_read(0, buf, addr, xattr_hdr_size);
+	ret = dev_read(dev, 0, buf, addr, xattr_hdr_size);
 	if (ret < 0) {
 		erofs_err("failed to read xattr header @ nid %llu: %d",
 			  inode->nid | 0ULL, ret);
@@ -339,7 +340,7 @@ static int erofs_verify_xattr(struct erofs_inode *inode)
 	while (remaining > 0) {
 		unsigned int entry_sz;
 
-		ret = dev_read(0, buf, addr, xattr_entry_size);
+		ret = dev_read(dev, 0, buf, addr, xattr_entry_size);
 		if (ret) {
 			erofs_err("failed to read xattr entry @ nid %llu: %d",
 				  inode->nid | 0ULL, ret);
@@ -445,7 +446,7 @@ static int erofs_verify_inode_data(struct erofs_inode *inode, int outfd)
 			BUG_ON(!buffer);
 		}
 
-		ret = dev_read(mdev.m_deviceid, raw, mdev.m_pa, map.m_plen);
+		ret = dev_read(sbi.dev, mdev.m_deviceid, raw, mdev.m_pa, map.m_plen);
 		if (ret < 0) {
 			erofs_err("failed to read data of m_pa %" PRIu64 ", m_plen %" PRIu64 " @ nid %llu: %d",
 				  mdev.m_pa, map.m_plen, inode->nid | 0ULL,
@@ -781,6 +782,12 @@ int main(int argc, char **argv)
 
 	erofs_init_configure();
 
+	sbi.dev = erofs_init_dev();
+	if (IS_ERR(sbi.dev)) {
+		err = PTR_ERR(sbi.dev);
+		goto exit;
+	}
+
 	fsckcfg.physical_blocks = 0;
 	fsckcfg.logical_blocks = 0;
 	fsckcfg.extract_path = NULL;
@@ -799,13 +806,13 @@ int main(int argc, char **argv)
 	if (err) {
 		if (err == -EINVAL)
 			usage();
-		goto exit;
+		goto exit_blob_close;
 	}
 
-	err = dev_open_ro(cfg.c_img_path);
+	err = dev_open_ro(sbi.dev, cfg.c_img_path);
 	if (err) {
 		erofs_err("failed to open image file");
-		goto exit;
+		goto exit_blob_close;
 	}
 
 	err = erofs_read_superblock();
@@ -844,9 +851,10 @@ int main(int argc, char **argv)
 exit_put_super:
 	erofs_put_super();
 exit_dev_close:
-	dev_close();
+	dev_close(sbi.dev);
+exit_blob_close:
+	blob_closeall(sbi.dev);
 exit:
-	blob_closeall();
 	erofs_exit_configure();
 	return err ? 1 : 0;
 }
diff --git a/fuse/main.c b/fuse/main.c
index f1d1b47..ee86e50 100644
--- a/fuse/main.c
+++ b/fuse/main.c
@@ -239,7 +239,7 @@ static int optional_opt_func(void *data, const char *arg, int key,
 
 	switch (key) {
 	case 1:
-		ret = blob_open_ro(arg + sizeof("--device=") - 1);
+		ret = blob_open_ro(sbi.dev, arg + sizeof("--device=") - 1);
 		if (ret)
 			return -1;
 		++sbi.extra_devices;
@@ -297,6 +297,12 @@ int main(int argc, char *argv[])
 	erofs_init_configure();
 	printf("%s %s\n", basename(argv[0]), cfg.c_version);
 
+	sbi.dev = erofs_init_dev();
+	if (IS_ERR(sbi.dev)) {
+		ret = PTR_ERR(sbi.dev);
+		goto err;
+	}
+
 #if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE)
 	if (signal(SIGSEGV, signal_handle_sigsegv) == SIG_ERR) {
 		fprintf(stderr, "failed to initialize signals\n");
@@ -320,7 +326,7 @@ int main(int argc, char *argv[])
 	cfg.c_offset = fusecfg.offset;
 
 	erofsfuse_dumpcfg();
-	ret = dev_open_ro(fusecfg.disk);
+	ret = dev_open_ro(sbi.dev, fusecfg.disk);
 	if (ret) {
 		fprintf(stderr, "failed to open: %s\n", fusecfg.disk);
 		goto err_fuse_free_args;
@@ -336,8 +342,8 @@ int main(int argc, char *argv[])
 
 	erofs_put_super();
 err_dev_close:
-	blob_closeall();
-	dev_close();
+	blob_closeall(sbi.dev);
+	dev_close(sbi.dev);
 err_fuse_free_args:
 	fuse_opt_free_args(&args);
 err:
diff --git a/include/erofs/cache.h b/include/erofs/cache.h
index de12399..e7aec2b 100644
--- a/include/erofs/cache.h
+++ b/include/erofs/cache.h
@@ -45,6 +45,7 @@ struct erofs_buffer_head {
 struct erofs_buffer_block {
 	struct list_head list;
 	struct list_head mapped_list;
+	struct erofs_device *dev;
 
 	erofs_blk_t blkaddr;
 	int type;
@@ -98,14 +99,15 @@ static inline bool erofs_bh_flush_generic_end(struct erofs_buffer_head *bh)
 struct erofs_buffer_head *erofs_buffer_init(void);
 int erofs_bh_balloon(struct erofs_buffer_head *bh, erofs_off_t incr);
 
-struct erofs_buffer_head *erofs_balloc(int type, erofs_off_t size,
+struct erofs_buffer_head *erofs_balloc(struct erofs_device *dev,
+				       int type, erofs_off_t size,
 				       unsigned int required_ext,
 				       unsigned int inline_ext);
 struct erofs_buffer_head *erofs_battach(struct erofs_buffer_head *bh,
 					int type, unsigned int size);
 
 erofs_blk_t erofs_mapbh(struct erofs_buffer_block *bb);
-bool erofs_bflush(struct erofs_buffer_block *bb);
+bool erofs_bflush(struct erofs_device *dev, struct erofs_buffer_block *bb);
 
 void erofs_bdrop(struct erofs_buffer_head *bh, bool tryrevoke);
 
diff --git a/include/erofs/config.h b/include/erofs/config.h
index 539d813..6c96664 100644
--- a/include/erofs/config.h
+++ b/include/erofs/config.h
@@ -85,6 +85,8 @@ void erofs_init_configure(void);
 void erofs_show_config(void);
 void erofs_exit_configure(void);
 
+struct erofs_device *erofs_init_dev(void);
+
 void erofs_set_fs_root(const char *rootdir);
 const char *erofs_fspath(const char *fullpath);
 
diff --git a/include/erofs/internal.h b/include/erofs/internal.h
index 2e0aae8..c4b36e9 100644
--- a/include/erofs/internal.h
+++ b/include/erofs/internal.h
@@ -65,12 +65,20 @@ typedef u32 erofs_blk_t;
 
 struct erofs_buffer_head;
 
+struct erofs_device {
+	const char *name;
+	int fd;
+	u64 size;
+	unsigned int nblobs, blobfd[256];
+};
+
 struct erofs_device_info {
 	u32 blocks;
 	u32 mapped_blkaddr;
 };
 
 struct erofs_sb_info {
+	struct erofs_device *dev;
 	struct erofs_device_info *devs;
 
 	u64 total_blocks;
diff --git a/include/erofs/io.h b/include/erofs/io.h
index 0f58c70..abbb2e0 100644
--- a/include/erofs/io.h
+++ b/include/erofs/io.h
@@ -22,17 +22,20 @@ extern "C"
 #define O_BINARY	0
 #endif
 
-void blob_closeall(void);
-int blob_open_ro(const char *dev);
-int dev_open(const char *devname);
-int dev_open_ro(const char *dev);
-void dev_close(void);
-int dev_write(const void *buf, u64 offset, size_t len);
-int dev_read(int device_id, void *buf, u64 offset, size_t len);
-int dev_fillzero(u64 offset, size_t len, bool padding);
-int dev_fsync(void);
-int dev_resize(erofs_blk_t nblocks);
-u64 dev_length(void);
+void blob_closeall(struct erofs_device *dev);
+int blob_open_ro(struct erofs_device *dev, const char *devname);
+int dev_open(struct erofs_device *dev, const char *devname);
+int dev_open_ro(struct erofs_device *dev, const char *devname);
+void dev_close(struct erofs_device *dev);
+int dev_write(struct erofs_device *dev,
+	      const void *buf, u64 offset, size_t len);
+int dev_read(struct erofs_device *dev, int device_id,
+	     void *buf, u64 offset, size_t len);
+int dev_fillzero(struct erofs_device *dev,
+		 u64 offset, size_t len, bool padding);
+int dev_fsync(struct erofs_device *dev);
+int dev_resize(struct erofs_device *dev, erofs_blk_t nblocks);
+u64 dev_length(struct erofs_device *dev);
 
 extern int erofs_devfd;
 
@@ -40,17 +43,17 @@ ssize_t erofs_copy_file_range(int fd_in, erofs_off_t *off_in,
 			      int fd_out, erofs_off_t *off_out,
 			      size_t length);
 
-static inline int blk_write(const void *buf, erofs_blk_t blkaddr,
-			    u32 nblocks)
+static inline int blk_write(struct erofs_device *dev, const void *buf,
+			    erofs_blk_t blkaddr, u32 nblocks)
 {
-	return dev_write(buf, blknr_to_addr(blkaddr),
+	return dev_write(dev, buf, blknr_to_addr(blkaddr),
 			 blknr_to_addr(nblocks));
 }
 
-static inline int blk_read(int device_id, void *buf,
-			   erofs_blk_t start, u32 nblocks)
+static inline int blk_read(struct erofs_device *dev, int device_id,
+			   void *buf, erofs_blk_t start, u32 nblocks)
 {
-	return dev_read(device_id, buf, blknr_to_addr(start),
+	return dev_read(dev, device_id, buf, blknr_to_addr(start),
 			 blknr_to_addr(nblocks));
 }
 
diff --git a/lib/blobchunk.c b/lib/blobchunk.c
index 77b0c17..d11d7db 100644
--- a/lib/blobchunk.c
+++ b/lib/blobchunk.c
@@ -158,7 +158,7 @@ int erofs_blob_write_chunk_indexes(struct erofs_inode *inode,
 	erofs_droid_blocklist_write_extent(inode, extent_start, extents_blks,
 					   first_extent, true);
 
-	return dev_write(inode->chunkindexes, off, inode->extent_isize);
+	return dev_write(sbi.dev, inode->chunkindexes, off, inode->extent_isize);
 }
 
 int erofs_blob_write_chunked_file(struct erofs_inode *inode)
@@ -214,6 +214,7 @@ err:
 
 int erofs_blob_remap(void)
 {
+	struct erofs_device *dev = sbi.dev;
 	struct erofs_buffer_head *bh;
 	ssize_t length;
 	erofs_off_t pos_in, pos_out;
@@ -229,7 +230,7 @@ int erofs_blob_remap(void)
 		};
 
 		pos_out = erofs_btell(bh_devt, false);
-		ret = dev_write(&dis, pos_out, sizeof(dis));
+		ret = dev_write(dev, &dis, pos_out, sizeof(dis));
 		if (ret)
 			return ret;
 
@@ -237,7 +238,7 @@ int erofs_blob_remap(void)
 		erofs_bdrop(bh_devt, false);
 		return 0;
 	}
-	bh = erofs_balloc(DATA, length, 0, 0);
+	bh = erofs_balloc(dev, DATA, length, 0, 0);
 	if (IS_ERR(bh))
 		return PTR_ERR(bh);
 
@@ -246,7 +247,7 @@ int erofs_blob_remap(void)
 	pos_in = 0;
 	remapped_base = erofs_blknr(pos_out);
 	ret = erofs_copy_file_range(fileno(blobfile), &pos_in,
-				    erofs_devfd, &pos_out, length);
+				    dev->fd, &pos_out, length);
 	bh->op = &erofs_drop_directly_bhops;
 	erofs_bdrop(bh, false);
 	return ret < length ? -EIO : 0;
@@ -287,7 +288,7 @@ int erofs_generate_devtable(void)
 	if (!multidev)
 		return 0;
 
-	bh_devt = erofs_balloc(DEVT, sizeof(dis), 0, 0);
+	bh_devt = erofs_balloc(sbi.dev, DEVT, sizeof(dis), 0, 0);
 	if (IS_ERR(bh_devt))
 		return PTR_ERR(bh_devt);
 
diff --git a/lib/cache.c b/lib/cache.c
index c735363..60b0ee6 100644
--- a/lib/cache.c
+++ b/lib/cache.c
@@ -45,7 +45,7 @@ int erofs_bh_flush_generic_write(struct erofs_buffer_head *bh, void *buf)
 	erofs_off_t offset = erofs_btell(bh, false);
 
 	DBG_BUGON(nbh->off < bh->off);
-	return dev_write(buf, offset, nbh->off - bh->off);
+	return dev_write(bh->block->dev, buf, offset, nbh->off - bh->off);
 }
 
 static bool erofs_bh_flush_buf_write(struct erofs_buffer_head *bh)
@@ -66,7 +66,7 @@ const struct erofs_bhops erofs_buf_write_bhops = {
 struct erofs_buffer_head *erofs_buffer_init(void)
 {
 	int i, j;
-	struct erofs_buffer_head *bh = erofs_balloc(META, 0, 0, 0);
+	struct erofs_buffer_head *bh = erofs_balloc(sbi.dev, META, 0, 0, 0);
 
 	if (IS_ERR(bh))
 		return bh;
@@ -252,7 +252,8 @@ skip_mapped:
 	return 0;
 }
 
-struct erofs_buffer_head *erofs_balloc(int type, erofs_off_t size,
+struct erofs_buffer_head *erofs_balloc(struct erofs_device *dev,
+				       int type, erofs_off_t size,
 				       unsigned int required_ext,
 				       unsigned int inline_ext)
 {
@@ -284,6 +285,7 @@ struct erofs_buffer_head *erofs_balloc(int type, erofs_off_t size,
 		if (!bb)
 			return ERR_PTR(-ENOMEM);
 
+		bb->dev = dev;
 		bb->type = type;
 		bb->blkaddr = NULL_ADDR;
 		bb->buffers.off = 0;
@@ -367,7 +369,7 @@ erofs_blk_t erofs_mapbh(struct erofs_buffer_block *bb)
 	return tail_blkaddr;
 }
 
-bool erofs_bflush(struct erofs_buffer_block *bb)
+bool erofs_bflush(struct erofs_device *dev, struct erofs_buffer_block *bb)
 {
 	struct erofs_buffer_block *p, *n;
 	erofs_blk_t blkaddr;
@@ -398,7 +400,7 @@ bool erofs_bflush(struct erofs_buffer_block *bb)
 
 		padding = EROFS_BLKSIZ - p->buffers.off % EROFS_BLKSIZ;
 		if (padding != EROFS_BLKSIZ)
-			dev_fillzero(blknr_to_addr(blkaddr) - padding,
+			dev_fillzero(dev, blknr_to_addr(blkaddr) - padding,
 				     padding, true);
 
 		DBG_BUGON(!list_empty(&p->buffers.list));
diff --git a/lib/compress.c b/lib/compress.c
index fd02053..ded0399 100644
--- a/lib/compress.c
+++ b/lib/compress.c
@@ -161,7 +161,7 @@ static int write_uncompressed_extent(struct z_erofs_vle_compress_ctx *ctx,
 
 	erofs_dbg("Writing %u uncompressed data to block %u",
 		  count, ctx->blkaddr);
-	ret = blk_write(dst, ctx->blkaddr, 1);
+	ret = blk_write(sbi.dev, dst, ctx->blkaddr, 1);
 	if (ret)
 		return ret;
 	return count;
@@ -317,7 +317,7 @@ nocompression:
 			erofs_dbg("Writing %u compressed data to %u of %u blocks",
 				  count, ctx->blkaddr, ctx->compressedblks);
 
-			ret = blk_write(dst - padding, ctx->blkaddr,
+			ret = blk_write(sbi.dev, dst - padding, ctx->blkaddr,
 					ctx->compressedblks);
 			if (ret)
 				return ret;
@@ -625,7 +625,7 @@ int erofs_write_compressed_file(struct erofs_inode *inode)
 	}
 
 	/* allocate main data buffer */
-	bh = erofs_balloc(DATA, 0, 0, 0);
+	bh = erofs_balloc(sbi.dev, DATA, 0, 0, 0);
 	if (IS_ERR(bh)) {
 		ret = PTR_ERR(bh);
 		goto err_close;
@@ -746,6 +746,7 @@ static int erofs_get_compress_algorithm_id(const char *name)
 int z_erofs_build_compr_cfgs(struct erofs_buffer_head *sb_bh)
 {
 	struct erofs_buffer_head *bh = sb_bh;
+	struct erofs_device *dev = sbi.dev;
 	int ret = 0;
 
 	if (sbi.available_compr_algs & (1 << Z_EROFS_COMPRESSION_LZ4)) {
@@ -767,7 +768,7 @@ int z_erofs_build_compr_cfgs(struct erofs_buffer_head *sb_bh)
 			return PTR_ERR(bh);
 		}
 		erofs_mapbh(bh->block);
-		ret = dev_write(&lz4alg, erofs_btell(bh, false),
+		ret = dev_write(dev, &lz4alg, erofs_btell(bh, false),
 				sizeof(lz4alg));
 		bh->op = &erofs_drop_directly_bhops;
 	}
@@ -789,7 +790,7 @@ int z_erofs_build_compr_cfgs(struct erofs_buffer_head *sb_bh)
 			return PTR_ERR(bh);
 		}
 		erofs_mapbh(bh->block);
-		ret = dev_write(&lzmaalg, erofs_btell(bh, false),
+		ret = dev_write(dev, &lzmaalg, erofs_btell(bh, false),
 				sizeof(lzmaalg));
 		bh->op = &erofs_drop_directly_bhops;
 	}
diff --git a/lib/config.c b/lib/config.c
index 3963df2..3cc1d22 100644
--- a/lib/config.c
+++ b/lib/config.c
@@ -57,6 +57,18 @@ void erofs_exit_configure(void)
 		free(cfg.c_img_path);
 }
 
+struct erofs_device *erofs_init_dev(void)
+{
+	struct erofs_device *dev;
+	dev = calloc(1, sizeof(struct erofs_device));
+	if (!dev)
+		return ERR_PTR(-ENOMEM);
+
+	dev->fd = -1;
+
+	return dev;
+}
+
 static unsigned int fullpath_prefix;	/* root directory prefix length */
 
 void erofs_set_fs_root(const char *rootdir)
diff --git a/lib/data.c b/lib/data.c
index ad7b2cb..abfa3f4 100644
--- a/lib/data.c
+++ b/lib/data.c
@@ -92,7 +92,7 @@ int erofs_map_blocks(struct erofs_inode *inode,
 	pos = roundup(iloc(vi->nid) + vi->inode_isize +
 		      vi->xattr_isize, unit) + unit * chunknr;
 
-	err = blk_read(0, buf, erofs_blknr(pos), 1);
+	err = blk_read(sbi.dev, 0, buf, erofs_blknr(pos), 1);
 	if (err < 0)
 		return -EIO;
 
@@ -208,7 +208,7 @@ static int erofs_read_raw_data(struct erofs_inode *inode, char *buffer,
 			map.m_la = ptr;
 		}
 
-		ret = dev_read(mdev.m_deviceid, estart, mdev.m_pa,
+		ret = dev_read(sbi.dev, mdev.m_deviceid, estart, mdev.m_pa,
 			       eend - map.m_la);
 		if (ret < 0)
 			return -EIO;
@@ -283,7 +283,7 @@ static int z_erofs_read_data(struct erofs_inode *inode, char *buffer,
 				break;
 			}
 		}
-		ret = dev_read(mdev.m_deviceid, raw, mdev.m_pa, map.m_plen);
+		ret = dev_read(sbi.dev, mdev.m_deviceid, raw, mdev.m_pa, map.m_plen);
 		if (ret < 0)
 			break;
 
diff --git a/lib/inode.c b/lib/inode.c
index 4da28b3..09cd79f 100644
--- a/lib/inode.c
+++ b/lib/inode.c
@@ -151,7 +151,7 @@ static int __allocate_inode_bh_data(struct erofs_inode *inode,
 	}
 
 	/* allocate main data buffer */
-	bh = erofs_balloc(DATA, blknr_to_addr(nblocks), 0, 0);
+	bh = erofs_balloc(sbi.dev, DATA, blknr_to_addr(nblocks), 0, 0);
 	if (IS_ERR(bh))
 		return PTR_ERR(bh);
 
@@ -278,7 +278,7 @@ static int write_dirblock(unsigned int q, struct erofs_dentry *head,
 	char buf[EROFS_BLKSIZ];
 
 	fill_dirblock(buf, EROFS_BLKSIZ, q, head, end);
-	return blk_write(buf, blkaddr, 1);
+	return blk_write(sbi.dev, buf, blkaddr, 1);
 }
 
 static int erofs_write_dir_file(struct erofs_inode *dir)
@@ -340,7 +340,7 @@ static int erofs_write_file_from_buffer(struct erofs_inode *inode, char *buf)
 		return ret;
 
 	if (nblocks)
-		blk_write(buf, inode->u.i_blkaddr, nblocks);
+		blk_write(sbi.dev, buf, inode->u.i_blkaddr, nblocks);
 	inode->idata_size = inode->i_size % EROFS_BLKSIZ;
 	if (inode->idata_size) {
 		inode->idata = malloc(inode->idata_size);
@@ -382,7 +382,7 @@ static int write_uncompressed_file_from_fd(struct erofs_inode *inode, int fd)
 			return -EAGAIN;
 		}
 
-		ret = blk_write(buf, inode->u.i_blkaddr + i, 1);
+		ret = blk_write(sbi.dev, buf, inode->u.i_blkaddr + i, 1);
 		if (ret)
 			return ret;
 	}
@@ -443,6 +443,7 @@ int erofs_write_file(struct erofs_inode *inode)
 static bool erofs_bh_flush_write_inode(struct erofs_buffer_head *bh)
 {
 	struct erofs_inode *const inode = bh->fsprivate;
+	struct erofs_device *dev = sbi.dev;
 	const u16 icount = EROFS_INODE_XATTR_ICOUNT(inode->xattr_isize);
 	erofs_off_t off = erofs_btell(bh, false);
 	union {
@@ -529,7 +530,7 @@ static bool erofs_bh_flush_write_inode(struct erofs_buffer_head *bh)
 		BUG_ON(1);
 	}
 
-	ret = dev_write(&u, off, inode->inode_isize);
+	ret = dev_write(dev, &u, off, inode->inode_isize);
 	if (ret)
 		return false;
 	off += inode->inode_isize;
@@ -540,7 +541,7 @@ static bool erofs_bh_flush_write_inode(struct erofs_buffer_head *bh)
 		if (IS_ERR(xattrs))
 			return false;
 
-		ret = dev_write(xattrs, off, inode->xattr_isize);
+		ret = dev_write(dev, xattrs, off, inode->xattr_isize);
 		free(xattrs);
 		if (ret)
 			return false;
@@ -556,7 +557,7 @@ static bool erofs_bh_flush_write_inode(struct erofs_buffer_head *bh)
 		} else {
 			/* write compression metadata */
 			off = Z_EROFS_VLE_EXTENT_ALIGN(off);
-			ret = dev_write(inode->compressmeta, off,
+			ret = dev_write(dev, inode->compressmeta, off,
 					inode->extent_isize);
 			if (ret)
 				return false;
@@ -583,7 +584,7 @@ static int erofs_prepare_tail_block(struct erofs_inode *inode)
 
 	bh = inode->bh_data;
 	if (!bh) {
-		bh = erofs_balloc(DATA, EROFS_BLKSIZ, 0, 0);
+		bh = erofs_balloc(sbi.dev, DATA, EROFS_BLKSIZ, 0, 0);
 		if (IS_ERR(bh))
 			return PTR_ERR(bh);
 		bh->op = &erofs_skip_write_bhops;
@@ -631,7 +632,7 @@ static int erofs_prepare_inode_buffer(struct erofs_inode *inode)
 			inode->datalayout = EROFS_INODE_FLAT_PLAIN;
 	}
 
-	bh = erofs_balloc(INODE, inodesize, 0, inode->idata_size);
+	bh = erofs_balloc(sbi.dev, INODE, inodesize, 0, inode->idata_size);
 	if (bh == ERR_PTR(-ENOSPC)) {
 		int ret;
 
@@ -644,7 +645,7 @@ noinline:
 		ret = erofs_prepare_tail_block(inode);
 		if (ret)
 			return ret;
-		bh = erofs_balloc(INODE, inodesize, 0, 0);
+		bh = erofs_balloc(sbi.dev, INODE, inodesize, 0, 0);
 		if (IS_ERR(bh))
 			return PTR_ERR(bh);
 		DBG_BUGON(inode->bh_inline);
@@ -684,7 +685,7 @@ static bool erofs_bh_flush_write_inline(struct erofs_buffer_head *bh)
 	const erofs_off_t off = erofs_btell(bh, false);
 	int ret;
 
-	ret = dev_write(inode->idata, off, inode->idata_size);
+	ret = dev_write(sbi.dev, inode->idata, off, inode->idata_size);
 	if (ret)
 		return false;
 
@@ -703,6 +704,7 @@ static struct erofs_bhops erofs_write_inline_bhops = {
 static int erofs_write_tail_end(struct erofs_inode *inode)
 {
 	struct erofs_buffer_head *bh, *ibh;
+	struct erofs_device *dev = sbi.dev;
 
 	bh = inode->bh_data;
 
@@ -732,13 +734,13 @@ static int erofs_write_tail_end(struct erofs_inode *inode)
 			/* pad 0'ed data for the other cases */
 			zero_pos = pos + inode->idata_size;
 		}
-		ret = dev_write(inode->idata, pos, inode->idata_size);
+		ret = dev_write(dev, inode->idata, pos, inode->idata_size);
 		if (ret)
 			return ret;
 
 		DBG_BUGON(inode->idata_size > EROFS_BLKSIZ);
 		if (inode->idata_size < EROFS_BLKSIZ) {
-			ret = dev_fillzero(zero_pos,
+			ret = dev_fillzero(dev, zero_pos,
 					   EROFS_BLKSIZ - inode->idata_size,
 					   false);
 			if (ret)
diff --git a/lib/io.c b/lib/io.c
index 524cfb4..2b9811e 100644
--- a/lib/io.c
+++ b/lib/io.c
@@ -23,11 +23,6 @@
 #define EROFS_MODNAME	"erofs_io"
 #include "erofs/print.h"
 
-static const char *erofs_devname;
-int erofs_devfd = -1;
-static u64 erofs_devsz;
-static unsigned int erofs_nblobs, erofs_blobfd[256];
-
 int dev_get_blkdev_size(int fd, u64 *bytes)
 {
 	errno = ENOTSUP;
@@ -48,111 +43,119 @@ int dev_get_blkdev_size(int fd, u64 *bytes)
 	return -errno;
 }
 
-void dev_close(void)
+void dev_close(struct erofs_device *dev)
 {
-	close(erofs_devfd);
-	erofs_devname = NULL;
-	erofs_devfd   = -1;
-	erofs_devsz   = 0;
+	if (!dev)
+		return;
+
+	close(dev->fd);
+	dev->name = NULL;
+	dev->fd   = -1;
+	dev->size   = 0;
 }
 
-int dev_open(const char *dev)
+int dev_open(struct erofs_device *dev, const char *devname)
 {
 	struct stat st;
 	int fd, ret;
 
-	fd = open(dev, O_RDWR | O_CREAT | O_BINARY, 0644);
+	fd = open(devname, O_RDWR | O_CREAT | O_BINARY, 0644);
 	if (fd < 0) {
-		erofs_err("failed to open(%s).", dev);
+		erofs_err("failed to open(%s).", devname);
 		return -errno;
 	}
 
 	ret = fstat(fd, &st);
 	if (ret) {
-		erofs_err("failed to fstat(%s).", dev);
+		erofs_err("failed to fstat(%s).", devname);
 		close(fd);
 		return -errno;
 	}
 
 	switch (st.st_mode & S_IFMT) {
 	case S_IFBLK:
-		ret = dev_get_blkdev_size(fd, &erofs_devsz);
+		ret = dev_get_blkdev_size(fd, &dev->size);
 		if (ret) {
-			erofs_err("failed to get block device size(%s).", dev);
+			erofs_err("failed to get block device size(%s).",
+				  devname);
 			close(fd);
 			return ret;
 		}
-		erofs_devsz = round_down(erofs_devsz, EROFS_BLKSIZ);
+		dev->size = round_down(dev->size, EROFS_BLKSIZ);
 		break;
 	case S_IFREG:
 		ret = ftruncate(fd, 0);
 		if (ret) {
-			erofs_err("failed to ftruncate(%s).", dev);
+			erofs_err("failed to ftruncate(%s).", devname);
 			close(fd);
 			return -errno;
 		}
 		/* INT64_MAX is the limit of kernel vfs */
-		erofs_devsz = INT64_MAX;
+		dev->size = INT64_MAX;
 		break;
 	default:
-		erofs_err("bad file type (%s, %o).", dev, st.st_mode);
+		erofs_err("bad file type (%s, %o).", devname, st.st_mode);
 		close(fd);
 		return -EINVAL;
 	}
 
-	erofs_devname = dev;
-	erofs_devfd = fd;
+	dev->name = devname;
+	dev->fd = fd;
 
-	erofs_info("successfully to open %s", dev);
-	return 0;
+	erofs_info("successfully to open %s", devname);
+	return ret;
 }
 
-void blob_closeall(void)
+void blob_closeall(struct erofs_device *dev)
 {
 	unsigned int i;
 
-	for (i = 0; i < erofs_nblobs; ++i)
-		close(erofs_blobfd[i]);
-	erofs_nblobs = 0;
+	if (!dev)
+		return;
+
+	for (i = 0; i < dev->nblobs; ++i)
+		close(dev->blobfd[i]);
+	dev->nblobs = 0;
 }
 
-int blob_open_ro(const char *dev)
+int blob_open_ro(struct erofs_device *dev, const char *devname)
 {
-	int fd = open(dev, O_RDONLY | O_BINARY);
+	int fd = open(devname, O_RDONLY | O_BINARY);
 
 	if (fd < 0) {
-		erofs_err("failed to open(%s).", dev);
+		erofs_err("failed to open(%s).", devname);
 		return -errno;
 	}
 
-	erofs_blobfd[erofs_nblobs] = fd;
-	erofs_info("successfully to open blob%u %s", erofs_nblobs, dev);
-	++erofs_nblobs;
+	dev->blobfd[dev->nblobs] = fd;
+	erofs_info("successfully to open blob%u %s", dev->nblobs, devname);
+	++dev->nblobs;
 	return 0;
 }
 
 /* XXX: temporary soluation. Disk I/O implementation needs to be refactored. */
-int dev_open_ro(const char *dev)
+int dev_open_ro(struct erofs_device *dev, const char *devname)
 {
-	int fd = open(dev, O_RDONLY | O_BINARY);
+	int fd = open(devname, O_RDONLY | O_BINARY);
 
 	if (fd < 0) {
-		erofs_err("failed to open(%s).", dev);
+		erofs_err("failed to open(%s).", devname);
 		return -errno;
 	}
 
-	erofs_devfd = fd;
-	erofs_devname = dev;
-	erofs_devsz = INT64_MAX;
+	dev->fd = fd;
+	dev->name = devname;
+	dev->size = INT64_MAX;
 	return 0;
 }
 
-u64 dev_length(void)
+u64 dev_length(struct erofs_device *dev)
 {
-	return erofs_devsz;
+	return dev->size;
 }
 
-int dev_write(const void *buf, u64 offset, size_t len)
+int dev_write(struct erofs_device *dev,
+	      const void *buf, u64 offset, size_t len)
 {
 	int ret;
 
@@ -164,33 +167,34 @@ int dev_write(const void *buf, u64 offset, size_t len)
 		return -EINVAL;
 	}
 
-	if (offset >= erofs_devsz || len > erofs_devsz ||
-	    offset > erofs_devsz - len) {
+	if (offset >= dev->size || len > dev->size ||
+	    offset > dev->size - len) {
 		erofs_err("Write posion[%" PRIu64 ", %zd] is too large beyond the end of device(%" PRIu64 ").",
-			  offset, len, erofs_devsz);
+			  offset, len, dev->size);
 		return -EINVAL;
 	}
 
 #ifdef HAVE_PWRITE64
-	ret = pwrite64(erofs_devfd, buf, len, (off64_t)offset);
+	ret = pwrite64(dev->fd, buf, len, (off64_t)offset);
 #else
-	ret = pwrite(erofs_devfd, buf, len, (off_t)offset);
+	ret = pwrite(dev->fd, buf, len, (off_t)offset);
 #endif
 	if (ret != (int)len) {
 		if (ret < 0) {
 			erofs_err("Failed to write data into device - %s:[%" PRIu64 ", %zd].",
-				  erofs_devname, offset, len);
+				  dev->name, offset, len);
 			return -errno;
 		}
 
 		erofs_err("Writing data into device - %s:[%" PRIu64 ", %zd] - was truncated.",
-			  erofs_devname, offset, len);
+			  dev->name, offset, len);
 		return -ERANGE;
 	}
 	return 0;
 }
 
-int dev_fillzero(u64 offset, size_t len, bool padding)
+int dev_fillzero(struct erofs_device *dev,
+		 u64 offset, size_t len, bool padding)
 {
 	static const char zero[EROFS_BLKSIZ] = {0};
 	int ret;
@@ -199,25 +203,25 @@ int dev_fillzero(u64 offset, size_t len, bool padding)
 		return 0;
 
 #if defined(HAVE_FALLOCATE) && defined(FALLOC_FL_PUNCH_HOLE)
-	if (!padding && fallocate(erofs_devfd, FALLOC_FL_PUNCH_HOLE |
+	if (!padding && fallocate(dev->fd, FALLOC_FL_PUNCH_HOLE |
 				  FALLOC_FL_KEEP_SIZE, offset, len) >= 0)
 		return 0;
 #endif
 	while (len > EROFS_BLKSIZ) {
-		ret = dev_write(zero, offset, EROFS_BLKSIZ);
+		ret = dev_write(dev, zero, offset, EROFS_BLKSIZ);
 		if (ret)
 			return ret;
 		len -= EROFS_BLKSIZ;
 		offset += EROFS_BLKSIZ;
 	}
-	return dev_write(zero, offset, len);
+	return dev_write(dev, zero, offset, len);
 }
 
-int dev_fsync(void)
+int dev_fsync(struct erofs_device *dev)
 {
 	int ret;
 
-	ret = fsync(erofs_devfd);
+	ret = fsync(dev->fd);
 	if (ret) {
 		erofs_err("Could not fsync device!!!");
 		return -EIO;
@@ -225,16 +229,16 @@ int dev_fsync(void)
 	return 0;
 }
 
-int dev_resize(unsigned int blocks)
+int dev_resize(struct erofs_device *dev, unsigned int blocks)
 {
 	int ret;
 	struct stat st;
 	u64 length;
 
-	if (cfg.c_dry_run || erofs_devsz != INT64_MAX)
+	if (cfg.c_dry_run || dev->size != INT64_MAX)
 		return 0;
 
-	ret = fstat(erofs_devfd, &st);
+	ret = fstat(dev->fd, &st);
 	if (ret) {
 		erofs_err("failed to fstat.");
 		return -errno;
@@ -244,17 +248,18 @@ int dev_resize(unsigned int blocks)
 	if (st.st_size == length)
 		return 0;
 	if (st.st_size > length)
-		return ftruncate(erofs_devfd, length);
+		return ftruncate(dev->fd, length);
 
 	length = length - st.st_size;
 #if defined(HAVE_FALLOCATE)
-	if (fallocate(erofs_devfd, 0, st.st_size, length) >= 0)
+	if (fallocate(dev->fd, 0, st.st_size, length) >= 0)
 		return 0;
 #endif
-	return dev_fillzero(st.st_size, length, true);
+	return dev_fillzero(dev, st.st_size, length, true);
 }
 
-int dev_read(int device_id, void *buf, u64 offset, size_t len)
+int dev_read(struct erofs_device *dev, int device_id,
+	     void *buf, u64 offset, size_t len)
 {
 	int ret, fd;
 
@@ -269,13 +274,13 @@ int dev_read(int device_id, void *buf, u64 offset, size_t len)
 	}
 
 	if (!device_id) {
-		fd = erofs_devfd;
+		fd = dev->fd;
 	} else {
-		if (device_id > erofs_nblobs) {
+		if (device_id > dev->nblobs) {
 			erofs_err("invalid device id %d", device_id);
 			return -ENODEV;
 		}
-		fd = erofs_blobfd[device_id - 1];
+		fd = dev->blobfd[device_id - 1];
 	}
 
 #ifdef HAVE_PREAD64
@@ -285,7 +290,7 @@ int dev_read(int device_id, void *buf, u64 offset, size_t len)
 #endif
 	if (ret != (int)len) {
 		erofs_err("Failed to read data from device - %s:[%" PRIu64 ", %zd].",
-			  erofs_devname, offset, len);
+			  dev->name, offset, len);
 		return -errno;
 	}
 	return 0;
diff --git a/lib/namei.c b/lib/namei.c
index 7b69a59..bb14d30 100644
--- a/lib/namei.c
+++ b/lib/namei.c
@@ -28,9 +28,10 @@ int erofs_read_inode_from_disk(struct erofs_inode *vi)
 	char buf[sizeof(struct erofs_inode_extended)];
 	struct erofs_inode_compact *dic;
 	struct erofs_inode_extended *die;
+	struct erofs_device *dev = sbi.dev;
 	const erofs_off_t inode_loc = iloc(vi->nid);
 
-	ret = dev_read(0, buf, inode_loc, sizeof(*dic));
+	ret = dev_read(dev, 0, buf, inode_loc, sizeof(*dic));
 	if (ret < 0)
 		return -EIO;
 
@@ -47,7 +48,7 @@ int erofs_read_inode_from_disk(struct erofs_inode *vi)
 	case EROFS_INODE_LAYOUT_EXTENDED:
 		vi->inode_isize = sizeof(struct erofs_inode_extended);
 
-		ret = dev_read(0, buf + sizeof(*dic), inode_loc + sizeof(*dic),
+		ret = dev_read(dev, 0, buf + sizeof(*dic), inode_loc + sizeof(*dic),
 			       sizeof(*die) - sizeof(*dic));
 		if (ret < 0)
 			return -EIO;
diff --git a/lib/super.c b/lib/super.c
index b267412..8727161 100644
--- a/lib/super.c
+++ b/lib/super.c
@@ -53,7 +53,7 @@ static int erofs_init_devices(struct erofs_sb_info *sbi,
 		struct erofs_deviceslot dis;
 		int ret;
 
-		ret = dev_read(0, &dis, pos, sizeof(dis));
+		ret = dev_read(sbi->dev, 0, &dis, pos, sizeof(dis));
 		if (ret < 0) {
 			free(sbi->devs);
 			return ret;
@@ -73,7 +73,7 @@ int erofs_read_superblock(void)
 	unsigned int blkszbits;
 	int ret;
 
-	ret = blk_read(0, data, 0, 1);
+	ret = blk_read(sbi.dev, 0, data, 0, 1);
 	if (ret < 0) {
 		erofs_err("cannot read erofs superblock: %d", ret);
 		return -EIO;
diff --git a/lib/xattr.c b/lib/xattr.c
index 8979dcc..e0bf922 100644
--- a/lib/xattr.c
+++ b/lib/xattr.c
@@ -549,7 +549,7 @@ static void erofs_cleanxattrs(bool sharedxattrs)
 static bool erofs_bh_flush_write_shared_xattrs(struct erofs_buffer_head *bh)
 {
 	void *buf = bh->fsprivate;
-	int err = dev_write(buf, erofs_btell(bh, false), shared_xattrs_size);
+	int err = dev_write(sbi.dev, buf, erofs_btell(bh, false), shared_xattrs_size);
 
 	if (err)
 		return false;
@@ -609,7 +609,7 @@ int erofs_build_shared_xattrs_from_path(const char *path)
 	if (!buf)
 		return -ENOMEM;
 
-	bh = erofs_balloc(XATTR, shared_xattrs_size, 0, 0);
+	bh = erofs_balloc(sbi.dev, XATTR, shared_xattrs_size, 0, 0);
 	if (IS_ERR(bh)) {
 		free(buf);
 		return PTR_ERR(bh);
@@ -729,6 +729,7 @@ static int init_inode_xattrs(struct erofs_inode *vi)
 	struct xattr_iter it;
 	unsigned int i;
 	struct erofs_xattr_ibody_header *ih;
+	struct erofs_device *dev = sbi.dev;
 	int ret = 0;
 
 	/* the most case is that xattrs of this inode are initialized. */
@@ -759,7 +760,7 @@ static int init_inode_xattrs(struct erofs_inode *vi)
 	it.blkaddr = erofs_blknr(iloc(vi->nid) + vi->inode_isize);
 	it.ofs = erofs_blkoff(iloc(vi->nid) + vi->inode_isize);
 
-	ret = blk_read(0, it.page, it.blkaddr, 1);
+	ret = blk_read(dev, 0, it.page, it.blkaddr, 1);
 	if (ret < 0)
 		return -EIO;
 
@@ -779,7 +780,7 @@ static int init_inode_xattrs(struct erofs_inode *vi)
 			/* cannot be unaligned */
 			DBG_BUGON(it.ofs != EROFS_BLKSIZ);
 
-			ret = blk_read(0, it.page, ++it.blkaddr, 1);
+			ret = blk_read(dev, 0, it.page, ++it.blkaddr, 1);
 			if (ret < 0) {
 				free(vi->xattr_shared_xattrs);
 				vi->xattr_shared_xattrs = NULL;
@@ -824,7 +825,7 @@ static inline int xattr_iter_fixup(struct xattr_iter *it)
 
 	it->blkaddr += erofs_blknr(it->ofs);
 
-	ret = blk_read(0, it->page, it->blkaddr, 1);
+	ret = blk_read(sbi.dev, 0, it->page, it->blkaddr, 1);
 	if (ret < 0)
 		return -EIO;
 
@@ -850,7 +851,7 @@ static int inline_xattr_iter_pre(struct xattr_iter *it,
 	it->blkaddr = erofs_blknr(iloc(vi->nid) + inline_xattr_ofs);
 	it->ofs = erofs_blkoff(iloc(vi->nid) + inline_xattr_ofs);
 
-	ret = blk_read(0, it->page, it->blkaddr, 1);
+	ret = blk_read(sbi.dev, 0, it->page, it->blkaddr, 1);
 	if (ret < 0)
 		return -EIO;
 
@@ -1047,7 +1048,7 @@ static int shared_getxattr(struct erofs_inode *vi, struct getxattr_iter *it)
 		it->it.ofs = xattrblock_offset(vi->xattr_shared_xattrs[i]);
 
 		if (!i || blkaddr != it->it.blkaddr) {
-			ret = blk_read(0, it->it.page, blkaddr, 1);
+			ret = blk_read(sbi.dev, 0, it->it.page, blkaddr, 1);
 			if (ret < 0)
 				return -EIO;
 
@@ -1186,7 +1187,7 @@ static int shared_listxattr(struct erofs_inode *vi, struct listxattr_iter *it)
 
 		it->it.ofs = xattrblock_offset(vi->xattr_shared_xattrs[i]);
 		if (!i || blkaddr != it->it.blkaddr) {
-			ret = blk_read(0, it->it.page, blkaddr, 1);
+			ret = blk_read(sbi.dev, 0, it->it.page, blkaddr, 1);
 			if (ret < 0)
 				return -EIO;
 
diff --git a/lib/zmap.c b/lib/zmap.c
index abe0d31..6e5a5d4 100644
--- a/lib/zmap.c
+++ b/lib/zmap.c
@@ -44,7 +44,7 @@ static int z_erofs_fill_inode_lazy(struct erofs_inode *vi)
 		  vi->datalayout == EROFS_INODE_FLAT_COMPRESSION_LEGACY);
 	pos = round_up(iloc(vi->nid) + vi->inode_isize + vi->xattr_isize, 8);
 
-	ret = dev_read(0, buf, pos, sizeof(buf));
+	ret = dev_read(sbi.dev, 0, buf, pos, sizeof(buf));
 	if (ret < 0)
 		return -EIO;
 
@@ -111,7 +111,7 @@ static int z_erofs_reload_indexes(struct z_erofs_maprecorder *m,
 	if (map->index == eblk)
 		return 0;
 
-	ret = blk_read(0, mpage, eblk, 1);
+	ret = blk_read(sbi.dev, 0, mpage, eblk, 1);
 	if (ret < 0)
 		return -EIO;
 
diff --git a/mkfs/main.c b/mkfs/main.c
index b969b35..e3ad9d9 100644
--- a/mkfs/main.c
+++ b/mkfs/main.c
@@ -508,8 +508,9 @@ static int erofs_mkfs_superblock_csum_set(void)
 	u8 buf[EROFS_BLKSIZ];
 	u32 crc;
 	struct erofs_super_block *sb;
+	struct erofs_device *dev = sbi.dev;
 
-	ret = blk_read(0, buf, 0, 1);
+	ret = blk_read(dev, 0, buf, 0, 1);
 	if (ret) {
 		erofs_err("failed to read superblock to set checksum: %s",
 			  erofs_strerror(ret));
@@ -535,7 +536,7 @@ static int erofs_mkfs_superblock_csum_set(void)
 	/* set up checksum field to erofs_super_block */
 	sb->checksum = cpu_to_le32(crc);
 
-	ret = blk_write(buf, 0, 1);
+	ret = blk_write(dev, buf, 0, 1);
 	if (ret) {
 		erofs_err("failed to write checksummed superblock: %s",
 			  erofs_strerror(ret));
@@ -607,8 +608,12 @@ int main(int argc, char **argv)
 	char uuid_str[37] = "not available";
 
 	erofs_init_configure();
-	erofs_mkfs_default_options();
 
+	sbi.dev = erofs_init_dev();
+	if (IS_ERR(sbi.dev))
+		return 1;
+
+	erofs_mkfs_default_options();
 	err = mkfs_parse_options_cfg(argc, argv);
 	erofs_show_progs(argc, argv);
 	if (err) {
@@ -647,7 +652,7 @@ int main(int argc, char **argv)
 		sbi.build_time_nsec = t.tv_usec;
 	}
 
-	err = dev_open(cfg.c_img_path);
+	err = dev_open(sbi.dev, cfg.c_img_path);
 	if (err) {
 		usage();
 		return 1;
@@ -744,10 +749,10 @@ int main(int argc, char **argv)
 		goto exit;
 
 	/* flush all remaining buffers */
-	if (!erofs_bflush(NULL))
+	if (!erofs_bflush(sbi.dev, NULL))
 		err = -EIO;
 	else
-		err = dev_resize(nblocks);
+		err = dev_resize(sbi.dev, nblocks);
 
 	if (!err && erofs_sb_has_sb_chksum())
 		err = erofs_mkfs_superblock_csum_set();
@@ -756,7 +761,7 @@ exit:
 #ifdef WITH_ANDROID
 	erofs_droid_blocklist_fclose();
 #endif
-	dev_close();
+	dev_close(sbi.dev);
 	erofs_cleanup_compress_hints();
 	erofs_cleanup_exclude_rules();
 	if (cfg.c_chunkbits)
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 2/4] erofs-utils: remove global sbi
  2022-09-09  2:18 [PATCH 1/4] erofs-utils: remove global erofs_devfd Huang Jianan
@ 2022-09-09  2:18 ` Huang Jianan
  2022-09-09  2:18 ` [PATCH 3/4] erofs-utils: tests: add test for dump.erofs Huang Jianan
  2022-09-09  2:18 ` [PATCH 4/4] erofs-utils: tests: add test for fsck.erofs Huang Jianan
  2 siblings, 0 replies; 4+ messages in thread
From: Huang Jianan @ 2022-09-09  2:18 UTC (permalink / raw)
  To: linux-erofs

Remove global sbi which will make erofs-utils more library friendly.

Signed-off-by: Huang Jianan <jnhuang@linux.alibaba.com>
---
 dump/main.c                | 71 ++++++++++++++++-------------
 fsck/main.c                | 55 +++++++++++++----------
 fuse/main.c                | 42 +++++++++++-------
 include/erofs/blobchunk.h  |  4 +-
 include/erofs/cache.h      |  2 +-
 include/erofs/compress.h   |  3 +-
 include/erofs/config.h     |  5 ++-
 include/erofs/decompress.h |  1 +
 include/erofs/dir.h        |  4 +-
 include/erofs/inode.h      |  3 +-
 include/erofs/internal.h   | 25 +++++------
 include/erofs/xattr.h      |  8 ++--
 lib/blobchunk.c            | 17 +++----
 lib/cache.c                |  4 +-
 lib/compress.c             | 71 +++++++++++++++--------------
 lib/compressor.h           |  1 +
 lib/compressor_lz4.c       |  2 +-
 lib/compressor_lz4hc.c     |  2 +-
 lib/config.c               | 17 +++++--
 lib/data.c                 | 21 +++++----
 lib/decompress.c           |  2 +-
 lib/dir.c                  | 17 ++++---
 lib/inode.c                | 68 ++++++++++++++++------------
 lib/io.c                   |  2 +
 lib/namei.c                | 19 +++++---
 lib/super.c                | 38 ++++++++--------
 lib/xattr.c                | 40 ++++++++++-------
 lib/zmap.c                 | 26 ++++++-----
 mkfs/main.c                | 91 ++++++++++++++++++++------------------
 29 files changed, 382 insertions(+), 279 deletions(-)

diff --git a/dump/main.c b/dump/main.c
index 07fa151..586302b 100644
--- a/dump/main.c
+++ b/dump/main.c
@@ -152,10 +152,10 @@ static int erofsdump_parse_options_cfg(int argc, char **argv)
 			usage();
 			exit(0);
 		case 3:
-			err = blob_open_ro(sbi.dev, optarg);
+			err = blob_open_ro(cfg.sbi->dev, optarg);
 			if (err)
 				return err;
-			++sbi.extra_devices;
+			++cfg.sbi->extra_devices;
 			break;
 		case 4:
 			dumpcfg.inode_path = optarg;
@@ -281,7 +281,10 @@ static int erofsdump_readdir(struct erofs_dir_context *ctx)
 {
 	int err;
 	erofs_off_t occupied_size = 0;
-	struct erofs_inode vi = { .nid = ctx->de_nid };
+	struct erofs_inode vi = {
+		.sbi = ctx->sbi,
+		.nid = ctx->de_nid,
+	};
 
 	err = erofs_read_inode_from_disk(&vi);
 	if (err) {
@@ -307,6 +310,7 @@ static int erofsdump_readdir(struct erofs_dir_context *ctx)
 	/* XXXX: the dir depth should be restricted in order to avoid loops */
 	if (S_ISDIR(vi.i_mode)) {
 		struct erofs_dir_context nctx = {
+			.sbi = ctx->sbi,
 			.flags = ctx->dir ? EROFS_READDIR_VALID_PNID : 0,
 			.pnid = ctx->dir ? ctx->dir->nid : 0,
 			.dir = &vi,
@@ -326,7 +330,7 @@ static int erofsdump_map_blocks(struct erofs_inode *inode,
 	return erofs_map_blocks(inode, map, flags);
 }
 
-static void erofsdump_show_fileinfo(bool show_extent)
+static void erofsdump_show_fileinfo(struct erofs_sb_info *sbi, bool show_extent)
 {
 	const char *ext_fmt[] = {
 		"%4d: %8" PRIu64 "..%8" PRIu64 " | %7" PRIu64 " : %10" PRIu64 "..%10" PRIu64 " | %7" PRIu64 "\n",
@@ -335,11 +339,14 @@ static void erofsdump_show_fileinfo(bool show_extent)
 	int err, i;
 	erofs_off_t size;
 	u16 access_mode;
-	struct erofs_inode inode = { .nid = dumpcfg.nid };
 	char path[PATH_MAX];
 	char access_mode_str[] = "rwxrwxrwx";
 	char timebuf[128] = {0};
 	unsigned int extent_count = 0;
+	struct erofs_inode inode = {
+		.sbi = sbi,
+		.nid = dumpcfg.nid,
+	};
 	struct erofs_map_blocks map = {
 		.index = UINT_MAX,
 		.m_la = 0,
@@ -366,7 +373,7 @@ static void erofsdump_show_fileinfo(bool show_extent)
 		return;
 	}
 
-	err = erofs_get_pathname(inode.nid, path, sizeof(path));
+	err = erofs_get_pathname(sbi, inode.nid, path, sizeof(path));
 	if (err < 0) {
 		erofs_err("file path not found @ nid %llu", inode.nid | 0ULL);
 		return;
@@ -396,6 +403,7 @@ static void erofsdump_show_fileinfo(bool show_extent)
 
 	if (dumpcfg.show_subdirectories) {
 		struct erofs_dir_context ctx = {
+			.sbi = sbi,
 			.flags = EROFS_READDIR_VALID_PNID,
 			.pnid = inode.nid,
 			.dir = &inode,
@@ -431,7 +439,7 @@ static void erofsdump_show_fileinfo(bool show_extent)
 			.m_deviceid = map.m_deviceid,
 			.m_pa = map.m_pa,
 		};
-		err = erofs_map_dev(&sbi, &mdev);
+		err = erofs_map_dev(sbi, &mdev);
 		if (err) {
 			erofs_err("failed to map device");
 			return;
@@ -531,15 +539,16 @@ static void erofsdump_file_statistic(void)
 			stats.compress_rate);
 }
 
-static void erofsdump_print_statistic(void)
+static void erofsdump_print_statistic(struct erofs_sb_info *sbi)
 {
 	int err;
 	struct erofs_dir_context ctx = {
+		.sbi = sbi,
 		.flags = 0,
 		.pnid = 0,
 		.dir = NULL,
 		.cb = erofsdump_dirent_iter,
-		.de_nid = sbi.root_nid,
+		.de_nid = sbi->root_nid,
 		.dname = "",
 		.de_namelen = 0,
 	};
@@ -559,36 +568,36 @@ static void erofsdump_print_statistic(void)
 	erofsdump_filetype_distribution(file_types, OTHERFILETYPE);
 }
 
-static void erofsdump_show_superblock(void)
+static void erofsdump_show_superblock(struct erofs_sb_info *sbi)
 {
-	time_t time = sbi.build_time;
+	time_t time = sbi->build_time;
 	char uuid_str[37] = "not available";
 	int i = 0;
 
 	fprintf(stdout, "Filesystem magic number:                      0x%04X\n",
 			EROFS_SUPER_MAGIC_V1);
 	fprintf(stdout, "Filesystem blocks:                            %llu\n",
-			sbi.total_blocks | 0ULL);
+			sbi->total_blocks | 0ULL);
 	fprintf(stdout, "Filesystem inode metadata start block:        %u\n",
-			sbi.meta_blkaddr);
+			sbi->meta_blkaddr);
 	fprintf(stdout, "Filesystem shared xattr metadata start block: %u\n",
-			sbi.xattr_blkaddr);
+			sbi->xattr_blkaddr);
 	fprintf(stdout, "Filesystem root nid:                          %llu\n",
-			sbi.root_nid | 0ULL);
+			sbi->root_nid | 0ULL);
 	fprintf(stdout, "Filesystem inode count:                       %llu\n",
-			sbi.inos | 0ULL);
+			sbi->inos | 0ULL);
 	fprintf(stdout, "Filesystem created:                           %s",
 			ctime(&time));
 	fprintf(stdout, "Filesystem features:                          ");
 	for (; i < ARRAY_SIZE(feature_lists); i++) {
 		u32 feat = le32_to_cpu(feature_lists[i].compat ?
-				       sbi.feature_compat :
-				       sbi.feature_incompat);
+				       sbi->feature_compat :
+				       sbi->feature_incompat);
 		if (feat & feature_lists[i].flag)
 			fprintf(stdout, "%s ", feature_lists[i].name);
 	}
 #ifdef HAVE_LIBUUID
-	uuid_unparse_lower(sbi.uuid, uuid_str);
+	uuid_unparse_lower(sbi->uuid, uuid_str);
 #endif
 	fprintf(stdout, "\nFilesystem UUID:                              %s\n",
 			uuid_str);
@@ -597,14 +606,16 @@ static void erofsdump_show_superblock(void)
 int main(int argc, char **argv)
 {
 	int err;
+	struct erofs_sb_info *sbi;
 
 	erofs_init_configure();
 
-	sbi.dev = erofs_init_dev();
-	if (IS_ERR(sbi.dev)) {
-		err = PTR_ERR(sbi.dev);
+	sbi = erofs_init_sbi();
+	if (IS_ERR(sbi)) {
+		err = PTR_ERR(sbi);
 		goto exit;
 	}
+	cfg.sbi = sbi;
 
 	err = erofsdump_parse_options_cfg(argc, argv);
 	if (err) {
@@ -613,13 +624,13 @@ int main(int argc, char **argv)
 		goto exit_blob_close;
 	}
 
-	err = dev_open_ro(sbi.dev, cfg.c_img_path);
+	err = dev_open_ro(sbi->dev, cfg.c_img_path);
 	if (err) {
 		erofs_err("failed to open image file");
 		goto exit_blob_close;
 	}
 
-	err = erofs_read_superblock();
+	err = erofs_read_superblock(sbi);
 	if (err) {
 		erofs_err("failed to read superblock");
 		goto exit_dev_close;
@@ -630,10 +641,10 @@ int main(int argc, char **argv)
 		dumpcfg.totalshow = 1;
 	}
 	if (dumpcfg.show_superblock)
-		erofsdump_show_superblock();
+		erofsdump_show_superblock(sbi);
 
 	if (dumpcfg.show_statistics)
-		erofsdump_print_statistic();
+		erofsdump_print_statistic(sbi);
 
 	if (dumpcfg.show_extent && !dumpcfg.show_inode) {
 		usage();
@@ -641,14 +652,14 @@ int main(int argc, char **argv)
 	}
 
 	if (dumpcfg.show_inode)
-		erofsdump_show_fileinfo(dumpcfg.show_extent);
+		erofsdump_show_fileinfo(sbi, dumpcfg.show_extent);
 
 exit_put_super:
-	erofs_put_super();
+	erofs_put_super(sbi);
 exit_dev_close:
-	dev_close(sbi.dev);
+	dev_close(sbi->dev);
 exit_blob_close:
-	blob_closeall(sbi.dev);
+	blob_closeall(sbi->dev);
 exit:
 	erofs_exit_configure();
 	return err;
diff --git a/fsck/main.c b/fsck/main.c
index 1c8f567..ddfbf82 100644
--- a/fsck/main.c
+++ b/fsck/main.c
@@ -15,7 +15,8 @@
 #include "erofs/decompress.h"
 #include "erofs/dir.h"
 
-static int erofsfsck_check_inode(erofs_nid_t pnid, erofs_nid_t nid);
+static int erofsfsck_check_inode(struct erofs_sb_info *sbi,
+				 erofs_nid_t pnid, erofs_nid_t nid);
 
 struct erofsfsck_cfg {
 	u64 physical_blocks;
@@ -143,10 +144,10 @@ static int erofsfsck_parse_options_cfg(int argc, char **argv)
 			}
 			break;
 		case 3:
-			ret = blob_open_ro(sbi.dev, optarg);
+			ret = blob_open_ro(cfg.sbi->dev, optarg);
 			if (ret)
 				return ret;
-			++sbi.extra_devices;
+			++cfg.sbi->extra_devices;
 			break;
 		case 4:
 			fsckcfg.force = true;
@@ -256,14 +257,14 @@ static void erofsfsck_set_attributes(struct erofs_inode *inode, char *path)
 	}
 }
 
-static int erofs_check_sb_chksum(void)
+static int erofs_check_sb_chksum(struct erofs_sb_info *sbi)
 {
 	int ret;
 	u8 buf[EROFS_BLKSIZ];
 	u32 crc;
 	struct erofs_super_block *sb;
 
-	ret = blk_read(sbi.dev, 0, buf, 0, 1);
+	ret = blk_read(sbi->dev, 0, buf, 0, 1);
 	if (ret) {
 		erofs_err("failed to read superblock to check checksum: %d",
 			  ret);
@@ -274,9 +275,9 @@ static int erofs_check_sb_chksum(void)
 	sb->checksum = 0;
 
 	crc = erofs_crc32c(~0, (u8 *)sb, EROFS_BLKSIZ - EROFS_SUPER_OFFSET);
-	if (crc != sbi.checksum) {
+	if (crc != sbi->checksum) {
 		erofs_err("superblock chksum doesn't match: saved(%08xh) calculated(%08xh)",
-			  sbi.checksum, crc);
+			  sbi->checksum, crc);
 		fsckcfg.corrupted = true;
 		return -1;
 	}
@@ -291,7 +292,7 @@ static int erofs_verify_xattr(struct erofs_inode *inode)
 	unsigned int ofs, xattr_shared_count;
 	struct erofs_xattr_ibody_header *ih;
 	struct erofs_xattr_entry *entry;
-	struct erofs_device *dev = sbi.dev;
+	struct erofs_device *dev = inode->sbi->dev;
 	int i, remaining = inode->xattr_isize, ret = 0;
 	char buf[EROFS_BLKSIZ];
 
@@ -309,7 +310,7 @@ static int erofs_verify_xattr(struct erofs_inode *inode)
 		}
 	}
 
-	addr = iloc(inode->nid) + inode->inode_isize;
+	addr = iloc(inode->sbi, inode->nid) + inode->inode_isize;
 	ret = dev_read(dev, 0, buf, addr, xattr_hdr_size);
 	if (ret < 0) {
 		erofs_err("failed to read xattr header @ nid %llu: %d",
@@ -367,6 +368,7 @@ static int erofs_verify_inode_data(struct erofs_inode *inode, int outfd)
 	struct erofs_map_blocks map = {
 		.index = UINT_MAX,
 	};
+	struct erofs_sb_info *sbi = inode->sbi;
 	struct erofs_map_dev mdev;
 	int ret = 0;
 	bool compressed;
@@ -432,7 +434,7 @@ static int erofs_verify_inode_data(struct erofs_inode *inode, int outfd)
 			.m_deviceid = map.m_deviceid,
 			.m_pa = map.m_pa,
 		};
-		ret = erofs_map_dev(&sbi, &mdev);
+		ret = erofs_map_dev(sbi, &mdev);
 		if (ret) {
 			erofs_err("failed to map device of m_pa %" PRIu64 ", m_deviceid %u @ nid %llu: %d",
 				  map.m_pa, map.m_deviceid, inode->nid | 0ULL,
@@ -446,7 +448,7 @@ static int erofs_verify_inode_data(struct erofs_inode *inode, int outfd)
 			BUG_ON(!buffer);
 		}
 
-		ret = dev_read(sbi.dev, mdev.m_deviceid, raw, mdev.m_pa, map.m_plen);
+		ret = dev_read(sbi->dev, mdev.m_deviceid, raw, mdev.m_pa, map.m_plen);
 		if (ret < 0) {
 			erofs_err("failed to read data of m_pa %" PRIu64 ", m_plen %" PRIu64 " @ nid %llu: %d",
 				  mdev.m_pa, map.m_plen, inode->nid | 0ULL,
@@ -456,6 +458,7 @@ static int erofs_verify_inode_data(struct erofs_inode *inode, int outfd)
 
 		if (compressed) {
 			struct z_erofs_decompress_req rq = {
+				.sbi = sbi,
 				.in = raw,
 				.out = buffer,
 				.decodedskip = 0,
@@ -694,7 +697,8 @@ static int erofsfsck_dirent_iter(struct erofs_dir_context *ctx)
 		fsckcfg.extract_pos = curr_pos;
 	}
 
-	ret = erofsfsck_check_inode(ctx->dir->nid, ctx->de_nid);
+	ret = erofsfsck_check_inode(ctx->dir->sbi,
+				    ctx->dir->nid, ctx->de_nid);
 
 	if (fsckcfg.extract_path) {
 		fsckcfg.extract_path[prev_pos] = '\0';
@@ -703,7 +707,8 @@ static int erofsfsck_dirent_iter(struct erofs_dir_context *ctx)
 	return ret;
 }
 
-static int erofsfsck_check_inode(erofs_nid_t pnid, erofs_nid_t nid)
+static int erofsfsck_check_inode(struct erofs_sb_info *sbi,
+				 erofs_nid_t pnid, erofs_nid_t nid)
 {
 	int ret;
 	struct erofs_inode inode;
@@ -711,6 +716,7 @@ static int erofsfsck_check_inode(erofs_nid_t pnid, erofs_nid_t nid)
 	erofs_dbg("check inode: nid(%llu)", nid | 0ULL);
 
 	inode.nid = nid;
+	inode.sbi = sbi;
 	ret = erofs_read_inode_from_disk(&inode);
 	if (ret) {
 		if (ret == -EIO)
@@ -756,6 +762,7 @@ verify:
 	/* XXXX: the dir depth should be restricted in order to avoid loops */
 	if (S_ISDIR(inode.i_mode)) {
 		struct erofs_dir_context ctx = {
+			.sbi = sbi,
 			.flags = EROFS_READDIR_VALID_PNID,
 			.pnid = pnid,
 			.dir = &inode,
@@ -779,14 +786,16 @@ out:
 int main(int argc, char **argv)
 {
 	int err;
+	struct erofs_sb_info *sbi;
 
 	erofs_init_configure();
 
-	sbi.dev = erofs_init_dev();
-	if (IS_ERR(sbi.dev)) {
-		err = PTR_ERR(sbi.dev);
+	sbi = erofs_init_sbi();
+	if (IS_ERR(sbi)) {
+		err = PTR_ERR(sbi);
 		goto exit;
 	}
+	cfg.sbi = sbi;
 
 	fsckcfg.physical_blocks = 0;
 	fsckcfg.logical_blocks = 0;
@@ -809,24 +818,24 @@ int main(int argc, char **argv)
 		goto exit_blob_close;
 	}
 
-	err = dev_open_ro(sbi.dev, cfg.c_img_path);
+	err = dev_open_ro(sbi->dev, cfg.c_img_path);
 	if (err) {
 		erofs_err("failed to open image file");
 		goto exit_blob_close;
 	}
 
-	err = erofs_read_superblock();
+	err = erofs_read_superblock(sbi);
 	if (err) {
 		erofs_err("failed to read superblock");
 		goto exit_dev_close;
 	}
 
-	if (erofs_sb_has_sb_chksum() && erofs_check_sb_chksum()) {
+	if (erofs_sb_has_sb_chksum(sbi) && erofs_check_sb_chksum(sbi)) {
 		erofs_err("failed to verify superblock checksum");
 		goto exit_put_super;
 	}
 
-	err = erofsfsck_check_inode(sbi.root_nid, sbi.root_nid);
+	err = erofsfsck_check_inode(sbi, sbi->root_nid, sbi->root_nid);
 	if (fsckcfg.corrupted) {
 		if (!fsckcfg.extract_path)
 			erofs_err("Found some filesystem corruption");
@@ -849,11 +858,11 @@ int main(int argc, char **argv)
 	}
 
 exit_put_super:
-	erofs_put_super();
+	erofs_put_super(sbi);
 exit_dev_close:
-	dev_close(sbi.dev);
+	dev_close(sbi->dev);
 exit_blob_close:
-	blob_closeall(sbi.dev);
+	blob_closeall(sbi->dev);
 exit:
 	erofs_exit_configure();
 	return err ? 1 : 0;
diff --git a/fuse/main.c b/fuse/main.c
index ee86e50..edabdff 100644
--- a/fuse/main.c
+++ b/fuse/main.c
@@ -39,8 +39,10 @@ int erofsfuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
 		      off_t offset, struct fuse_file_info *fi)
 {
 	int ret;
-	struct erofs_inode dir;
+	struct erofs_sb_info *sbi = fuse_get_context()->private_data;
+	struct erofs_inode dir = { .sbi = sbi };
 	struct erofsfuse_dir_context ctx = {
+		.ctx.sbi = sbi,
 		.ctx.dir = &dir,
 		.ctx.cb = erofsfuse_fill_dentries,
 		.filler = filler,
@@ -69,7 +71,7 @@ int erofsfuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
 static void *erofsfuse_init(struct fuse_conn_info *info)
 {
 	erofs_info("Using FUSE protocol %d.%d", info->proto_major, info->proto_minor);
-	return NULL;
+	return fuse_get_context()->private_data;
 }
 
 static int erofsfuse_open(const char *path, struct fuse_file_info *fi)
@@ -84,7 +86,8 @@ static int erofsfuse_open(const char *path, struct fuse_file_info *fi)
 
 static int erofsfuse_getattr(const char *path, struct stat *stbuf)
 {
-	struct erofs_inode vi = {};
+	struct erofs_sb_info *sbi = fuse_get_context()->private_data;
+	struct erofs_inode vi = { .sbi = sbi };
 	int ret;
 
 	erofs_dbg("getattr(%s)", path);
@@ -111,7 +114,8 @@ static int erofsfuse_read(const char *path, char *buffer,
 			  struct fuse_file_info *fi)
 {
 	int ret;
-	struct erofs_inode vi;
+	struct erofs_sb_info *sbi = fuse_get_context()->private_data;
+	struct erofs_inode vi = { .sbi = sbi };
 
 	erofs_dbg("path:%s size=%zd offset=%llu", path, size, (long long)offset);
 
@@ -146,7 +150,8 @@ static int erofsfuse_getxattr(const char *path, const char *name, char *value,
 			size_t size)
 {
 	int ret;
-	struct erofs_inode vi;
+	struct erofs_sb_info *sbi = fuse_get_context()->private_data;
+	struct erofs_inode vi = { .sbi = sbi };
 
 	erofs_dbg("getxattr(%s): name=%s size=%llu", path, name, size);
 
@@ -160,7 +165,8 @@ static int erofsfuse_getxattr(const char *path, const char *name, char *value,
 static int erofsfuse_listxattr(const char *path, char *list, size_t size)
 {
 	int ret;
-	struct erofs_inode vi;
+	struct erofs_sb_info *sbi = fuse_get_context()->private_data;
+	struct erofs_inode vi = { .sbi = sbi };
 
 	erofs_dbg("listxattr(%s): size=%llu", path, size);
 
@@ -239,10 +245,10 @@ static int optional_opt_func(void *data, const char *arg, int key,
 
 	switch (key) {
 	case 1:
-		ret = blob_open_ro(sbi.dev, arg + sizeof("--device=") - 1);
+		ret = blob_open_ro(cfg.sbi->dev, arg + sizeof("--device=") - 1);
 		if (ret)
 			return -1;
-		++sbi.extra_devices;
+		++cfg.sbi->extra_devices;
 		return 0;
 	case FUSE_OPT_KEY_NONOPT:
 		if (fusecfg.mountpoint)
@@ -293,15 +299,17 @@ int main(int argc, char *argv[])
 {
 	int ret;
 	struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
+	struct erofs_sb_info *sbi;
 
 	erofs_init_configure();
 	printf("%s %s\n", basename(argv[0]), cfg.c_version);
 
-	sbi.dev = erofs_init_dev();
-	if (IS_ERR(sbi.dev)) {
-		ret = PTR_ERR(sbi.dev);
+	sbi = erofs_init_sbi();
+	if (IS_ERR(sbi)) {
+		ret = PTR_ERR(sbi);
 		goto err;
 	}
+	cfg.sbi = sbi;
 
 #if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE)
 	if (signal(SIGSEGV, signal_handle_sigsegv) == SIG_ERR) {
@@ -326,24 +334,24 @@ int main(int argc, char *argv[])
 	cfg.c_offset = fusecfg.offset;
 
 	erofsfuse_dumpcfg();
-	ret = dev_open_ro(sbi.dev, fusecfg.disk);
+	ret = dev_open_ro(sbi->dev, fusecfg.disk);
 	if (ret) {
 		fprintf(stderr, "failed to open: %s\n", fusecfg.disk);
 		goto err_fuse_free_args;
 	}
 
-	ret = erofs_read_superblock();
+	ret = erofs_read_superblock(sbi);
 	if (ret) {
 		fprintf(stderr, "failed to read erofs super block\n");
 		goto err_dev_close;
 	}
 
-	ret = fuse_main(args.argc, args.argv, &erofs_ops, NULL);
+	ret = fuse_main(args.argc, args.argv, &erofs_ops, sbi);
 
-	erofs_put_super();
+	erofs_put_super(sbi);
 err_dev_close:
-	blob_closeall(sbi.dev);
-	dev_close(sbi.dev);
+	blob_closeall(sbi->dev);
+	dev_close(sbi->dev);
 err_fuse_free_args:
 	fuse_opt_free_args(&args);
 err:
diff --git a/include/erofs/blobchunk.h b/include/erofs/blobchunk.h
index 49cb7bf..82426b7 100644
--- a/include/erofs/blobchunk.h
+++ b/include/erofs/blobchunk.h
@@ -16,10 +16,10 @@ extern "C"
 
 int erofs_blob_write_chunk_indexes(struct erofs_inode *inode, erofs_off_t off);
 int erofs_blob_write_chunked_file(struct erofs_inode *inode);
-int erofs_blob_remap(void);
+int erofs_blob_remap(struct erofs_sb_info *sbi);
 void erofs_blob_exit(void);
 int erofs_blob_init(const char *blobfile_path);
-int erofs_generate_devtable(void);
+int erofs_generate_devtable(struct erofs_sb_info *sbi);
 
 #ifdef __cplusplus
 }
diff --git a/include/erofs/cache.h b/include/erofs/cache.h
index e7aec2b..c11d676 100644
--- a/include/erofs/cache.h
+++ b/include/erofs/cache.h
@@ -96,7 +96,7 @@ static inline bool erofs_bh_flush_generic_end(struct erofs_buffer_head *bh)
 	return true;
 }
 
-struct erofs_buffer_head *erofs_buffer_init(void);
+struct erofs_buffer_head *erofs_buffer_init(struct erofs_sb_info *sbi);
 int erofs_bh_balloon(struct erofs_buffer_head *bh, erofs_off_t incr);
 
 struct erofs_buffer_head *erofs_balloc(struct erofs_device *dev,
diff --git a/include/erofs/compress.h b/include/erofs/compress.h
index 24f6204..b2c1fda 100644
--- a/include/erofs/compress.h
+++ b/include/erofs/compress.h
@@ -20,7 +20,8 @@ extern "C"
 void z_erofs_drop_inline_pcluster(struct erofs_inode *inode);
 int erofs_write_compressed_file(struct erofs_inode *inode);
 
-int z_erofs_compress_init(struct erofs_buffer_head *bh);
+int z_erofs_compress_init(struct erofs_sb_info *sbi,
+			  struct erofs_buffer_head *bh);
 int z_erofs_compress_exit(void);
 
 const char *z_erofs_list_available_compressors(unsigned int i);
diff --git a/include/erofs/config.h b/include/erofs/config.h
index 6c96664..1129989 100644
--- a/include/erofs/config.h
+++ b/include/erofs/config.h
@@ -14,6 +14,7 @@ extern "C"
 
 #include "defs.h"
 #include "err.h"
+#include "internal.h"
 
 
 enum {
@@ -77,6 +78,8 @@ struct erofs_configure {
 
 	/* offset when reading multi partition images */
 	u64 c_offset;
+
+	struct erofs_sb_info *sbi;
 };
 
 extern struct erofs_configure cfg;
@@ -85,7 +88,7 @@ void erofs_init_configure(void);
 void erofs_show_config(void);
 void erofs_exit_configure(void);
 
-struct erofs_device *erofs_init_dev(void);
+struct erofs_sb_info *erofs_init_sbi(void);
 
 void erofs_set_fs_root(const char *rootdir);
 const char *erofs_fspath(const char *fullpath);
diff --git a/include/erofs/decompress.h b/include/erofs/decompress.h
index 82bf7b8..dd7eee2 100644
--- a/include/erofs/decompress.h
+++ b/include/erofs/decompress.h
@@ -14,6 +14,7 @@ extern "C"
 #include "internal.h"
 
 struct z_erofs_decompress_req {
+	struct erofs_sb_info *sbi;
 	char *in, *out;
 
 	/*
diff --git a/include/erofs/dir.h b/include/erofs/dir.h
index 74bffb5..cd257c4 100644
--- a/include/erofs/dir.h
+++ b/include/erofs/dir.h
@@ -39,6 +39,7 @@ typedef int (*erofs_readdir_cb)(struct erofs_dir_context *);
  * the callback context. |de_namelen| is the exact dirent name length.
  */
 struct erofs_dir_context {
+	struct erofs_sb_info *sbi;
 	/*
 	 * During execution of |erofs_iterate_dir|, the function needs to
 	 * read the values inside |erofs_inode* dir|. So it is important
@@ -62,7 +63,8 @@ struct erofs_dir_context {
 /* Iterate over inodes that are in directory */
 int erofs_iterate_dir(struct erofs_dir_context *ctx, bool fsck);
 /* Get a full pathname of the inode NID */
-int erofs_get_pathname(erofs_nid_t nid, char *buf, size_t size);
+int erofs_get_pathname(struct erofs_sb_info *sbi, erofs_nid_t nid,
+		       char *buf, size_t size);
 
 #ifdef __cplusplus
 }
diff --git a/include/erofs/inode.h b/include/erofs/inode.h
index 79b8d89..f76c675 100644
--- a/include/erofs/inode.h
+++ b/include/erofs/inode.h
@@ -20,7 +20,8 @@ unsigned char erofs_ftype_to_dtype(unsigned int filetype);
 void erofs_inode_manager_init(void);
 unsigned int erofs_iput(struct erofs_inode *inode);
 erofs_nid_t erofs_lookupnid(struct erofs_inode *inode);
-struct erofs_inode *erofs_mkfs_build_tree_from_path(struct erofs_inode *parent,
+struct erofs_inode *erofs_mkfs_build_tree_from_path(struct erofs_sb_info *sbi,
+						    struct erofs_inode *parent,
 						    const char *path);
 
 #ifdef __cplusplus
diff --git a/include/erofs/internal.h b/include/erofs/internal.h
index c4b36e9..98cfed1 100644
--- a/include/erofs/internal.h
+++ b/include/erofs/internal.h
@@ -112,26 +112,23 @@ struct erofs_sb_info {
 	};
 };
 
-/* global sbi */
-extern struct erofs_sb_info sbi;
-
-static inline erofs_off_t iloc(erofs_nid_t nid)
+static inline erofs_off_t iloc(struct erofs_sb_info *sbi, erofs_nid_t nid)
 {
-	return blknr_to_addr(sbi.meta_blkaddr) + (nid << sbi.islotbits);
+	return blknr_to_addr(sbi->meta_blkaddr) + (nid << sbi->islotbits);
 }
 
 #define EROFS_FEATURE_FUNCS(name, compat, feature) \
-static inline bool erofs_sb_has_##name(void) \
+static inline bool erofs_sb_has_##name(struct erofs_sb_info *sbi) \
 { \
-	return sbi.feature_##compat & EROFS_FEATURE_##feature; \
+	return sbi->feature_##compat & EROFS_FEATURE_##feature; \
 } \
-static inline void erofs_sb_set_##name(void) \
+static inline void erofs_sb_set_##name(struct erofs_sb_info *sbi) \
 { \
-	sbi.feature_##compat |= EROFS_FEATURE_##feature; \
+	sbi->feature_##compat |= EROFS_FEATURE_##feature; \
 } \
-static inline void erofs_sb_clear_##name(void) \
+static inline void erofs_sb_clear_##name(struct erofs_sb_info *sbi) \
 { \
-	sbi.feature_##compat &= ~EROFS_FEATURE_##feature; \
+	sbi->feature_##compat &= ~EROFS_FEATURE_##feature; \
 }
 
 EROFS_FEATURE_FUNCS(lz4_0padding, incompat, INCOMPAT_LZ4_0PADDING)
@@ -156,6 +153,7 @@ struct erofs_inode {
 	};
 	unsigned int i_count;
 	struct erofs_inode *i_parent;
+	struct erofs_sb_info *sbi;
 
 	umode_t i_mode;
 	erofs_off_t i_size;
@@ -246,6 +244,7 @@ static inline unsigned int erofs_inode_datalayout(unsigned int value)
 
 struct erofs_dentry {
 	struct list_head d_child;	/* child of parent list */
+	struct erofs_sb_info *sbi;
 
 	unsigned int type;
 	char name[EROFS_NAME_LEN];
@@ -328,8 +327,8 @@ struct erofs_map_dev {
 };
 
 /* super.c */
-int erofs_read_superblock(void);
-void erofs_put_super(void);
+int erofs_read_superblock(struct erofs_sb_info *sbi);
+void erofs_put_super(struct erofs_sb_info *sbi);
 
 /* namei.c */
 int erofs_read_inode_from_disk(struct erofs_inode *vi);
diff --git a/include/erofs/xattr.h b/include/erofs/xattr.h
index a0528c0..0b63439 100644
--- a/include/erofs/xattr.h
+++ b/include/erofs/xattr.h
@@ -24,9 +24,10 @@ static inline unsigned int inlinexattr_header_size(struct erofs_inode *vi)
 		sizeof(u32) * vi->xattr_shared_count;
 }
 
-static inline erofs_blk_t xattrblock_addr(unsigned int xattr_id)
+static inline erofs_blk_t xattrblock_addr(struct erofs_sb_info *sbi,
+					  unsigned int xattr_id)
 {
-	return sbi.xattr_blkaddr +
+	return sbi->xattr_blkaddr +
 		xattr_id * sizeof(__u32) / EROFS_BLKSIZ;
 }
 
@@ -68,7 +69,8 @@ static inline unsigned int xattrblock_offset(unsigned int xattr_id)
 
 int erofs_prepare_xattr_ibody(struct erofs_inode *inode);
 char *erofs_export_xattr_ibody(struct list_head *ixattrs, unsigned int size);
-int erofs_build_shared_xattrs_from_path(const char *path);
+int erofs_build_shared_xattrs_from_path(struct erofs_sb_info *sbi,
+					const char *path);
 
 #ifdef __cplusplus
 }
diff --git a/lib/blobchunk.c b/lib/blobchunk.c
index d11d7db..b91a0cf 100644
--- a/lib/blobchunk.c
+++ b/lib/blobchunk.c
@@ -104,6 +104,7 @@ static int erofs_blob_hashmap_cmp(const void *a, const void *b,
 int erofs_blob_write_chunk_indexes(struct erofs_inode *inode,
 				   erofs_off_t off)
 {
+	struct erofs_device *dev = inode->sbi->dev;
 	struct erofs_inode_chunk_index idx = {0};
 	erofs_blk_t extent_start = EROFS_NULL_ADDR;
 	erofs_blk_t extent_end, extents_blks;
@@ -158,7 +159,7 @@ int erofs_blob_write_chunk_indexes(struct erofs_inode *inode,
 	erofs_droid_blocklist_write_extent(inode, extent_start, extents_blks,
 					   first_extent, true);
 
-	return dev_write(sbi.dev, inode->chunkindexes, off, inode->extent_isize);
+	return dev_write(dev, inode->chunkindexes, off, inode->extent_isize);
 }
 
 int erofs_blob_write_chunked_file(struct erofs_inode *inode)
@@ -212,9 +213,9 @@ err:
 	return ret;
 }
 
-int erofs_blob_remap(void)
+int erofs_blob_remap(struct erofs_sb_info *sbi)
 {
-	struct erofs_device *dev = sbi.dev;
+	struct erofs_device *dev = sbi->dev;
 	struct erofs_buffer_head *bh;
 	ssize_t length;
 	erofs_off_t pos_in, pos_out;
@@ -281,22 +282,22 @@ int erofs_blob_init(const char *blobfile_path)
 	return 0;
 }
 
-int erofs_generate_devtable(void)
+int erofs_generate_devtable(struct erofs_sb_info *sbi)
 {
 	struct erofs_deviceslot dis;
 
 	if (!multidev)
 		return 0;
 
-	bh_devt = erofs_balloc(sbi.dev, DEVT, sizeof(dis), 0, 0);
+	bh_devt = erofs_balloc(sbi->dev, DEVT, sizeof(dis), 0, 0);
 	if (IS_ERR(bh_devt))
 		return PTR_ERR(bh_devt);
 
 	dis = (struct erofs_deviceslot) {};
 	erofs_mapbh(bh_devt->block);
 	bh_devt->op = &erofs_skip_write_bhops;
-	sbi.devt_slotoff = erofs_btell(bh_devt, false) / EROFS_DEVT_SLOT_SIZE;
-	sbi.extra_devices = 1;
-	erofs_sb_set_device_table();
+	sbi->devt_slotoff = erofs_btell(bh_devt, false) / EROFS_DEVT_SLOT_SIZE;
+	sbi->extra_devices = 1;
+	erofs_sb_set_device_table(sbi);
 	return 0;
 }
diff --git a/lib/cache.c b/lib/cache.c
index 60b0ee6..478ac17 100644
--- a/lib/cache.c
+++ b/lib/cache.c
@@ -63,10 +63,10 @@ const struct erofs_bhops erofs_buf_write_bhops = {
 };
 
 /* return buffer_head of erofs super block (with size 0) */
-struct erofs_buffer_head *erofs_buffer_init(void)
+struct erofs_buffer_head *erofs_buffer_init(struct erofs_sb_info *sbi)
 {
 	int i, j;
-	struct erofs_buffer_head *bh = erofs_balloc(sbi.dev, META, 0, 0, 0);
+	struct erofs_buffer_head *bh = erofs_balloc(sbi->dev, META, 0, 0, 0);
 
 	if (IS_ERR(bh))
 		return bh;
diff --git a/lib/compress.c b/lib/compress.c
index ded0399..4f8d763 100644
--- a/lib/compress.c
+++ b/lib/compress.c
@@ -30,6 +30,7 @@ struct z_erofs_vle_compress_ctx {
 	unsigned int compressedblks;
 	erofs_blk_t blkaddr;		/* pointing to the next blkaddr */
 	u16 clusterofs;
+	struct erofs_inode *vi;
 };
 
 #define Z_EROFS_LEGACY_MAP_HEADER_SIZE	\
@@ -93,7 +94,7 @@ static void vle_write_indexes(struct z_erofs_vle_compress_ctx *ctx,
 
 	do {
 		/* XXX: big pcluster feature should be per-inode */
-		if (d0 == 1 && erofs_sb_has_big_pcluster()) {
+		if (d0 == 1 && erofs_sb_has_big_pcluster(ctx->vi->sbi)) {
 			type = Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD;
 			di.di_u.delta[0] = cpu_to_le16(ctx->compressedblks |
 					Z_EROFS_VLE_DI_D0_CBLKCNT);
@@ -142,12 +143,13 @@ static void vle_write_indexes(struct z_erofs_vle_compress_ctx *ctx,
 static int write_uncompressed_extent(struct z_erofs_vle_compress_ctx *ctx,
 				     unsigned int *len, char *dst)
 {
+	struct erofs_sb_info *sbi = ctx->vi->sbi;
 	int ret;
 	unsigned int count;
 
 	/* reset clusterofs to 0 if permitted */
-	if (!erofs_sb_has_lz4_0padding() && ctx->clusterofs &&
-	    ctx->head >= ctx->clusterofs) {
+	if (!erofs_sb_has_lz4_0padding(sbi) &&
+	    ctx->clusterofs && ctx->head >= ctx->clusterofs) {
 		ctx->head -= ctx->clusterofs;
 		*len += ctx->clusterofs;
 		ctx->clusterofs = 0;
@@ -161,7 +163,7 @@ static int write_uncompressed_extent(struct z_erofs_vle_compress_ctx *ctx,
 
 	erofs_dbg("Writing %u uncompressed data to block %u",
 		  count, ctx->blkaddr);
-	ret = blk_write(sbi.dev, dst, ctx->blkaddr, 1);
+	ret = blk_write(sbi->dev, dst, ctx->blkaddr, 1);
 	if (ret)
 		return ret;
 	return count;
@@ -307,7 +309,7 @@ nocompression:
 			DBG_BUGON(ctx->compressedblks * EROFS_BLKSIZ >= count);
 
 			/* zero out garbage trailing data for non-0padding */
-			if (!erofs_sb_has_lz4_0padding())
+			if (!erofs_sb_has_lz4_0padding(ctx->vi->sbi))
 				memset(dst + ret, 0,
 				       roundup(ret, EROFS_BLKSIZ) - ret);
 			else if (tailused)
@@ -317,8 +319,8 @@ nocompression:
 			erofs_dbg("Writing %u compressed data to %u of %u blocks",
 				  count, ctx->blkaddr, ctx->compressedblks);
 
-			ret = blk_write(sbi.dev, dst - padding, ctx->blkaddr,
-					ctx->compressedblks);
+			ret = blk_write(inode->sbi->dev, dst - padding,
+					ctx->blkaddr, ctx->compressedblks);
 			if (ret)
 				return ret;
 			raw = false;
@@ -384,10 +386,10 @@ static void *write_compacted_indexes(u8 *out,
 				     erofs_blk_t *blkaddr_ret,
 				     unsigned int destsize,
 				     unsigned int logical_clusterbits,
-				     bool final, bool *dummy_head)
+				     bool final, bool *dummy_head,
+				     bool update_blkaddr)
 {
 	unsigned int vcnt, encodebits, pos, i, cblks;
-	bool update_blkaddr;
 	erofs_blk_t blkaddr;
 
 	if (destsize == 4)
@@ -398,7 +400,6 @@ static void *write_compacted_indexes(u8 *out,
 		return ERR_PTR(-EINVAL);
 	encodebits = (vcnt * destsize * 8 - 32) / vcnt;
 	blkaddr = *blkaddr_ret;
-	update_blkaddr = erofs_sb_has_big_pcluster();
 
 	pos = 0;
 	for (i = 0; i < vcnt; ++i) {
@@ -463,7 +464,8 @@ int z_erofs_convert_to_compacted_format(struct erofs_inode *inode,
 	/* # of 8-byte units so that it can be aligned with 32 bytes */
 	unsigned int compacted_4b_initial, compacted_4b_end;
 	unsigned int compacted_2b;
-	bool dummy_head;
+	struct erofs_sb_info *sbi = inode->sbi;
+	bool dummy_head, big_pcluster = erofs_sb_has_big_pcluster(sbi);
 
 	if (logical_clusterbits < LOG_BLOCK_SIZE || LOG_BLOCK_SIZE < 12)
 		return -EINVAL;
@@ -495,7 +497,7 @@ int z_erofs_convert_to_compacted_format(struct erofs_inode *inode,
 
 	dummy_head = false;
 	/* prior to bigpcluster, blkaddr was bumped up once coming into HEAD */
-	if (!erofs_sb_has_big_pcluster()) {
+	if (!big_pcluster) {
 		--blkaddr;
 		dummy_head = true;
 	}
@@ -505,7 +507,7 @@ int z_erofs_convert_to_compacted_format(struct erofs_inode *inode,
 		in = parse_legacy_indexes(cv, 2, in);
 		out = write_compacted_indexes(out, cv, &blkaddr,
 					      4, logical_clusterbits, false,
-					      &dummy_head);
+					      &dummy_head, big_pcluster);
 		compacted_4b_initial -= 2;
 	}
 	DBG_BUGON(compacted_4b_initial);
@@ -515,7 +517,7 @@ int z_erofs_convert_to_compacted_format(struct erofs_inode *inode,
 		in = parse_legacy_indexes(cv, 16, in);
 		out = write_compacted_indexes(out, cv, &blkaddr,
 					      2, logical_clusterbits, false,
-					      &dummy_head);
+					      &dummy_head, big_pcluster);
 		compacted_2b -= 16;
 	}
 	DBG_BUGON(compacted_2b);
@@ -525,7 +527,7 @@ int z_erofs_convert_to_compacted_format(struct erofs_inode *inode,
 		in = parse_legacy_indexes(cv, 2, in);
 		out = write_compacted_indexes(out, cv, &blkaddr,
 					      4, logical_clusterbits, false,
-					      &dummy_head);
+					      &dummy_head, big_pcluster);
 		compacted_4b_end -= 2;
 	}
 
@@ -535,7 +537,7 @@ int z_erofs_convert_to_compacted_format(struct erofs_inode *inode,
 		in = parse_legacy_indexes(cv, 1, in);
 		out = write_compacted_indexes(out, cv, &blkaddr,
 					      4, logical_clusterbits, true,
-					      &dummy_head);
+					      &dummy_head, big_pcluster);
 	}
 	inode->extent_isize = out - (u8 *)compressmeta;
 	return 0;
@@ -625,7 +627,7 @@ int erofs_write_compressed_file(struct erofs_inode *inode)
 	}
 
 	/* allocate main data buffer */
-	bh = erofs_balloc(sbi.dev, DATA, 0, 0, 0);
+	bh = erofs_balloc(inode->sbi->dev, DATA, 0, 0, 0);
 	if (IS_ERR(bh)) {
 		ret = PTR_ERR(bh);
 		goto err_close;
@@ -640,7 +642,7 @@ int erofs_write_compressed_file(struct erofs_inode *inode)
 		inode->datalayout = EROFS_INODE_FLAT_COMPRESSION_LEGACY;
 	}
 
-	if (erofs_sb_has_big_pcluster()) {
+	if (erofs_sb_has_big_pcluster(inode->sbi)) {
 		inode->z_advise |= Z_EROFS_ADVISE_BIG_PCLUSTER_1;
 		if (inode->datalayout == EROFS_INODE_FLAT_COMPRESSION)
 			inode->z_advise |= Z_EROFS_ADVISE_BIG_PCLUSTER_2;
@@ -654,6 +656,7 @@ int erofs_write_compressed_file(struct erofs_inode *inode)
 	ctx.metacur = compressmeta + Z_EROFS_LEGACY_MAP_HEADER_SIZE;
 	ctx.head = ctx.tail = 0;
 	ctx.clusterofs = 0;
+	ctx.vi = inode;
 	remaining = inode->i_size;
 
 	while (remaining) {
@@ -743,13 +746,14 @@ static int erofs_get_compress_algorithm_id(const char *name)
 	return -ENOTSUP;
 }
 
-int z_erofs_build_compr_cfgs(struct erofs_buffer_head *sb_bh)
+int z_erofs_build_compr_cfgs(struct erofs_sb_info *sbi,
+			     struct erofs_buffer_head *sb_bh)
 {
 	struct erofs_buffer_head *bh = sb_bh;
-	struct erofs_device *dev = sbi.dev;
+	struct erofs_device *dev = sbi->dev;
 	int ret = 0;
 
-	if (sbi.available_compr_algs & (1 << Z_EROFS_COMPRESSION_LZ4)) {
+	if (sbi->available_compr_algs & (1 << Z_EROFS_COMPRESSION_LZ4)) {
 		struct {
 			__le16 size;
 			struct z_erofs_lz4_cfgs lz4;
@@ -757,7 +761,7 @@ int z_erofs_build_compr_cfgs(struct erofs_buffer_head *sb_bh)
 			.size = cpu_to_le16(sizeof(struct z_erofs_lz4_cfgs)),
 			.lz4 = {
 				.max_distance =
-					cpu_to_le16(sbi.lz4_max_distance),
+					cpu_to_le16(sbi->lz4_max_distance),
 				.max_pclusterblks = cfg.c_pclusterblks_max,
 			}
 		};
@@ -773,7 +777,7 @@ int z_erofs_build_compr_cfgs(struct erofs_buffer_head *sb_bh)
 		bh->op = &erofs_drop_directly_bhops;
 	}
 #ifdef HAVE_LIBLZMA
-	if (sbi.available_compr_algs & (1 << Z_EROFS_COMPRESSION_LZMA)) {
+	if (sbi->available_compr_algs & (1 << Z_EROFS_COMPRESSION_LZMA)) {
 		struct {
 			__le16 size;
 			struct z_erofs_lzma_cfgs lzma;
@@ -798,12 +802,15 @@ int z_erofs_build_compr_cfgs(struct erofs_buffer_head *sb_bh)
 	return ret;
 }
 
-int z_erofs_compress_init(struct erofs_buffer_head *sb_bh)
+int z_erofs_compress_init(struct erofs_sb_info *sbi,
+			  struct erofs_buffer_head *sb_bh)
 {
+	int ret;
+
+	compresshandle.sbi = sbi;
 	/* initialize for primary compression algorithm */
-	int ret = erofs_compressor_init(&compresshandle,
+	ret = erofs_compressor_init(&compresshandle,
 					cfg.c_compr_alg_master);
-
 	if (ret)
 		return ret;
 
@@ -813,7 +820,7 @@ int z_erofs_compress_init(struct erofs_buffer_head *sb_bh)
 	 */
 	if (!cfg.c_compr_alg_master ||
 	    (cfg.c_legacy_compress && !strcmp(cfg.c_compr_alg_master, "lz4")))
-		erofs_sb_clear_lz4_0padding();
+		erofs_sb_clear_lz4_0padding(sbi);
 
 	if (!cfg.c_compr_alg_master)
 		return 0;
@@ -841,15 +848,15 @@ int z_erofs_compress_init(struct erofs_buffer_head *sb_bh)
 				  cfg.c_pclusterblks_max);
 			return -EINVAL;
 		}
-		erofs_sb_set_big_pcluster();
+		erofs_sb_set_big_pcluster(sbi);
 	}
 
 	if (ret != Z_EROFS_COMPRESSION_LZ4)
-		erofs_sb_set_compr_cfgs();
+		erofs_sb_set_compr_cfgs(sbi);
 
-	if (erofs_sb_has_compr_cfgs()) {
-		sbi.available_compr_algs |= 1 << ret;
-		return z_erofs_build_compr_cfgs(sb_bh);
+	if (erofs_sb_has_compr_cfgs(sbi)) {
+		sbi->available_compr_algs |= 1 << ret;
+		return z_erofs_build_compr_cfgs(sbi, sb_bh);
 	}
 	return 0;
 }
diff --git a/lib/compressor.h b/lib/compressor.h
index cf063f1..6b2401a 100644
--- a/lib/compressor.h
+++ b/lib/compressor.h
@@ -28,6 +28,7 @@ struct erofs_compressor {
 
 struct erofs_compress {
 	const struct erofs_compressor *alg;
+	struct erofs_sb_info *sbi;
 
 	unsigned int compress_threshold;
 	unsigned int compression_level;
diff --git a/lib/compressor_lz4.c b/lib/compressor_lz4.c
index b6f6e7e..e507b70 100644
--- a/lib/compressor_lz4.c
+++ b/lib/compressor_lz4.c
@@ -33,7 +33,7 @@ static int compressor_lz4_exit(struct erofs_compress *c)
 static int compressor_lz4_init(struct erofs_compress *c)
 {
 	c->alg = &erofs_compressor_lz4;
-	sbi.lz4_max_distance = LZ4_DISTANCE_MAX;
+	c->sbi->lz4_max_distance = LZ4_DISTANCE_MAX;
 	return 0;
 }
 
diff --git a/lib/compressor_lz4hc.c b/lib/compressor_lz4hc.c
index eec1c84..f2120d8 100644
--- a/lib/compressor_lz4hc.c
+++ b/lib/compressor_lz4hc.c
@@ -44,7 +44,7 @@ static int compressor_lz4hc_init(struct erofs_compress *c)
 	if (!c->private_data)
 		return -ENOMEM;
 
-	sbi.lz4_max_distance = LZ4_DISTANCE_MAX;
+	c->sbi->lz4_max_distance = LZ4_DISTANCE_MAX;
 	return 0;
 }
 
diff --git a/lib/config.c b/lib/config.c
index 3cc1d22..53a54e8 100644
--- a/lib/config.c
+++ b/lib/config.c
@@ -15,7 +15,6 @@
 #endif
 
 struct erofs_configure cfg;
-struct erofs_sb_info sbi;
 
 void erofs_init_configure(void)
 {
@@ -55,18 +54,28 @@ void erofs_exit_configure(void)
 #endif
 	if (cfg.c_img_path)
 		free(cfg.c_img_path);
+	if (cfg.sbi)
+		free(cfg.sbi);
 }
 
-struct erofs_device *erofs_init_dev(void)
+struct erofs_sb_info *erofs_init_sbi(void)
 {
 	struct erofs_device *dev;
+	struct erofs_sb_info *sbi;
+
 	dev = calloc(1, sizeof(struct erofs_device));
 	if (!dev)
 		return ERR_PTR(-ENOMEM);
-
 	dev->fd = -1;
 
-	return dev;
+	sbi = calloc(1, sizeof(struct erofs_sb_info));
+	if (!dev) {
+		free(dev);
+		return ERR_PTR(-ENOMEM);
+	}
+	sbi->dev = dev;
+
+	return sbi;
 }
 
 static unsigned int fullpath_prefix;	/* root directory prefix length */
diff --git a/lib/data.c b/lib/data.c
index abfa3f4..b87a505 100644
--- a/lib/data.c
+++ b/lib/data.c
@@ -33,7 +33,7 @@ static int erofs_map_blocks_flatmode(struct erofs_inode *inode,
 		map->m_plen = blknr_to_addr(lastblk) - offset;
 	} else if (tailendpacking) {
 		/* 2 - inode inline B: inode, [xattrs], inline last blk... */
-		map->m_pa = iloc(vi->nid) + vi->inode_isize +
+		map->m_pa = iloc(vi->sbi, vi->nid) + vi->inode_isize +
 			vi->xattr_isize + erofs_blkoff(map->m_la);
 		map->m_plen = inode->i_size - offset;
 
@@ -65,6 +65,8 @@ int erofs_map_blocks(struct erofs_inode *inode,
 		struct erofs_map_blocks *map, int flags)
 {
 	struct erofs_inode *vi = inode;
+	struct erofs_sb_info *sbi = vi->sbi;
+	struct erofs_device *dev = vi->sbi->dev;
 	struct erofs_inode_chunk_index *idx;
 	u8 buf[EROFS_BLKSIZ];
 	u64 chunknr;
@@ -89,10 +91,10 @@ int erofs_map_blocks(struct erofs_inode *inode,
 		unit = EROFS_BLOCK_MAP_ENTRY_SIZE;	/* block map */
 
 	chunknr = map->m_la >> vi->u.chunkbits;
-	pos = roundup(iloc(vi->nid) + vi->inode_isize +
+	pos = roundup(iloc(sbi, vi->nid) + vi->inode_isize +
 		      vi->xattr_isize, unit) + unit * chunknr;
 
-	err = blk_read(sbi.dev, 0, buf, erofs_blknr(pos), 1);
+	err = blk_read(dev, 0, buf, erofs_blknr(pos), 1);
 	if (err < 0)
 		return -EIO;
 
@@ -120,7 +122,7 @@ int erofs_map_blocks(struct erofs_inode *inode,
 		break;
 	default:
 		map->m_deviceid = le16_to_cpu(idx->device_id) &
-			sbi.device_id_mask;
+			sbi->device_id_mask;
 		map->m_pa = blknr_to_addr(le32_to_cpu(idx->blkaddr));
 		map->m_flags = EROFS_MAP_MAPPED;
 		break;
@@ -161,6 +163,7 @@ int erofs_map_dev(struct erofs_sb_info *sbi, struct erofs_map_dev *map)
 static int erofs_read_raw_data(struct erofs_inode *inode, char *buffer,
 			       erofs_off_t size, erofs_off_t offset)
 {
+	struct erofs_sb_info *sbi = inode->sbi;
 	struct erofs_map_blocks map = {
 		.index = UINT_MAX,
 	};
@@ -183,7 +186,7 @@ static int erofs_read_raw_data(struct erofs_inode *inode, char *buffer,
 			.m_deviceid = map.m_deviceid,
 			.m_pa = map.m_pa,
 		};
-		ret = erofs_map_dev(&sbi, &mdev);
+		ret = erofs_map_dev(sbi, &mdev);
 		if (ret)
 			return ret;
 
@@ -208,7 +211,7 @@ static int erofs_read_raw_data(struct erofs_inode *inode, char *buffer,
 			map.m_la = ptr;
 		}
 
-		ret = dev_read(sbi.dev, mdev.m_deviceid, estart, mdev.m_pa,
+		ret = dev_read(sbi->dev, mdev.m_deviceid, estart, mdev.m_pa,
 			       eend - map.m_la);
 		if (ret < 0)
 			return -EIO;
@@ -225,6 +228,7 @@ static int z_erofs_read_data(struct erofs_inode *inode, char *buffer,
 		.index = UINT_MAX,
 	};
 	struct erofs_map_dev mdev;
+	struct erofs_sb_info *sbi = inode->sbi;
 	bool partial;
 	unsigned int bufsize = 0;
 	char *raw = NULL;
@@ -242,7 +246,7 @@ static int z_erofs_read_data(struct erofs_inode *inode, char *buffer,
 		mdev = (struct erofs_map_dev) {
 			.m_pa = map.m_pa,
 		};
-		ret = erofs_map_dev(&sbi, &mdev);
+		ret = erofs_map_dev(sbi, &mdev);
 		if (ret) {
 			DBG_BUGON(1);
 			break;
@@ -283,11 +287,12 @@ static int z_erofs_read_data(struct erofs_inode *inode, char *buffer,
 				break;
 			}
 		}
-		ret = dev_read(sbi.dev, mdev.m_deviceid, raw, mdev.m_pa, map.m_plen);
+		ret = dev_read(sbi->dev, mdev.m_deviceid, raw, mdev.m_pa, map.m_plen);
 		if (ret < 0)
 			break;
 
 		ret = z_erofs_decompress(&(struct z_erofs_decompress_req) {
+					.sbi = sbi,
 					.in = raw,
 					.out = buffer + end - offset,
 					.decodedskip = skip,
diff --git a/lib/decompress.c b/lib/decompress.c
index 1661f91..fc4e049 100644
--- a/lib/decompress.c
+++ b/lib/decompress.c
@@ -82,7 +82,7 @@ static int z_erofs_decompress_lz4(struct z_erofs_decompress_req *rq)
 	bool support_0padding = false;
 	unsigned int inputmargin = 0;
 
-	if (erofs_sb_has_lz4_0padding()) {
+	if (erofs_sb_has_lz4_0padding(rq->sbi)) {
 		support_0padding = true;
 
 		while (!src[inputmargin & ~PAGE_MASK])
diff --git a/lib/dir.c b/lib/dir.c
index e6b9283..073d704 100644
--- a/lib/dir.c
+++ b/lib/dir.c
@@ -76,8 +76,8 @@ static int traverse_dirents(struct erofs_dir_context *ctx,
 					goto out;
 				}
 				ctx->flags |= EROFS_READDIR_DOTDOT_FOUND;
-				if (sbi.root_nid == ctx->dir->nid) {
-					ctx->pnid = sbi.root_nid;
+				if (ctx->sbi->root_nid == ctx->dir->nid) {
+					ctx->pnid = ctx->sbi->root_nid;
 					ctx->flags |= EROFS_READDIR_VALID_PNID;
 				}
 				if (fsck &&
@@ -203,7 +203,9 @@ static int erofs_get_pathname_iter(struct erofs_dir_context *ctx)
 	}
 
 	if (ctx->de_ftype == EROFS_FT_DIR || ctx->de_ftype == EROFS_FT_UNKNOWN) {
-		struct erofs_inode dir = { .nid = ctx->de_nid };
+		struct erofs_inode dir = {
+			.sbi = ctx->sbi,
+			.nid = ctx->de_nid, };
 
 		ret = erofs_read_inode_from_disk(&dir);
 		if (ret) {
@@ -229,11 +231,16 @@ static int erofs_get_pathname_iter(struct erofs_dir_context *ctx)
 	return 0;
 }
 
-int erofs_get_pathname(erofs_nid_t nid, char *buf, size_t size)
+int erofs_get_pathname(struct erofs_sb_info *sbi, erofs_nid_t nid,
+		       char *buf, size_t size)
 {
 	int ret;
-	struct erofs_inode root = { .nid = sbi.root_nid };
+	struct erofs_inode root = {
+		.sbi = sbi,
+		.nid = sbi->root_nid,
+	};
 	struct erofs_get_pathname_context pathctx = {
+		.ctx.sbi = sbi,
 		.ctx.flags = 0,
 		.ctx.dir = &root,
 		.ctx.cb = erofs_get_pathname_iter,
diff --git a/lib/inode.c b/lib/inode.c
index 09cd79f..cc21fd1 100644
--- a/lib/inode.c
+++ b/lib/inode.c
@@ -130,6 +130,7 @@ struct erofs_dentry *erofs_d_alloc(struct erofs_inode *parent,
 	if (!d)
 		return ERR_PTR(-ENOMEM);
 
+	d->sbi = parent->sbi;
 	strncpy(d->name, name, EROFS_NAME_LEN - 1);
 	d->name[EROFS_NAME_LEN - 1] = '\0';
 
@@ -151,7 +152,8 @@ static int __allocate_inode_bh_data(struct erofs_inode *inode,
 	}
 
 	/* allocate main data buffer */
-	bh = erofs_balloc(sbi.dev, DATA, blknr_to_addr(nblocks), 0, 0);
+	bh = erofs_balloc(inode->sbi->dev, DATA, blknr_to_addr(nblocks),
+			  0, 0);
 	if (IS_ERR(bh))
 		return PTR_ERR(bh);
 
@@ -278,7 +280,7 @@ static int write_dirblock(unsigned int q, struct erofs_dentry *head,
 	char buf[EROFS_BLKSIZ];
 
 	fill_dirblock(buf, EROFS_BLKSIZ, q, head, end);
-	return blk_write(sbi.dev, buf, blkaddr, 1);
+	return blk_write(head->sbi->dev, buf, blkaddr, 1);
 }
 
 static int erofs_write_dir_file(struct erofs_inode *dir)
@@ -340,7 +342,8 @@ static int erofs_write_file_from_buffer(struct erofs_inode *inode, char *buf)
 		return ret;
 
 	if (nblocks)
-		blk_write(sbi.dev, buf, inode->u.i_blkaddr, nblocks);
+		blk_write(inode->sbi->dev, buf, inode->u.i_blkaddr,
+			  nblocks);
 	inode->idata_size = inode->i_size % EROFS_BLKSIZ;
 	if (inode->idata_size) {
 		inode->idata = malloc(inode->idata_size);
@@ -382,7 +385,8 @@ static int write_uncompressed_file_from_fd(struct erofs_inode *inode, int fd)
 			return -EAGAIN;
 		}
 
-		ret = blk_write(sbi.dev, buf, inode->u.i_blkaddr + i, 1);
+		ret = blk_write(inode->sbi->dev, buf,
+				inode->u.i_blkaddr + i, 1);
 		if (ret)
 			return ret;
 	}
@@ -443,7 +447,7 @@ int erofs_write_file(struct erofs_inode *inode)
 static bool erofs_bh_flush_write_inode(struct erofs_buffer_head *bh)
 {
 	struct erofs_inode *const inode = bh->fsprivate;
-	struct erofs_device *dev = sbi.dev;
+	struct erofs_device *dev = bh->block->dev;
 	const u16 icount = EROFS_INODE_XATTR_ICOUNT(inode->xattr_isize);
 	erofs_off_t off = erofs_btell(bh, false);
 	union {
@@ -584,7 +588,8 @@ static int erofs_prepare_tail_block(struct erofs_inode *inode)
 
 	bh = inode->bh_data;
 	if (!bh) {
-		bh = erofs_balloc(sbi.dev, DATA, EROFS_BLKSIZ, 0, 0);
+		bh = erofs_balloc(inode->sbi->dev, DATA, EROFS_BLKSIZ,
+				  0, 0);
 		if (IS_ERR(bh))
 			return PTR_ERR(bh);
 		bh->op = &erofs_skip_write_bhops;
@@ -607,6 +612,7 @@ static int erofs_prepare_inode_buffer(struct erofs_inode *inode)
 {
 	unsigned int inodesize;
 	struct erofs_buffer_head *bh, *ibh;
+	struct erofs_sb_info *sbi = inode->sbi;
 
 	DBG_BUGON(inode->bh || inode->bh_inline);
 
@@ -632,7 +638,7 @@ static int erofs_prepare_inode_buffer(struct erofs_inode *inode)
 			inode->datalayout = EROFS_INODE_FLAT_PLAIN;
 	}
 
-	bh = erofs_balloc(sbi.dev, INODE, inodesize, 0, inode->idata_size);
+	bh = erofs_balloc(sbi->dev, INODE, inodesize, 0, inode->idata_size);
 	if (bh == ERR_PTR(-ENOSPC)) {
 		int ret;
 
@@ -645,7 +651,7 @@ noinline:
 		ret = erofs_prepare_tail_block(inode);
 		if (ret)
 			return ret;
-		bh = erofs_balloc(sbi.dev, INODE, inodesize, 0, 0);
+		bh = erofs_balloc(sbi->dev, INODE, inodesize, 0, 0);
 		if (IS_ERR(bh))
 			return PTR_ERR(bh);
 		DBG_BUGON(inode->bh_inline);
@@ -657,7 +663,7 @@ noinline:
 			erofs_dbg("Inline %scompressed data (%u bytes) to %s",
 				  inode->compressed_idata ? "" : "un",
 				  inode->idata_size, inode->i_srcpath);
-			erofs_sb_set_ztailpacking();
+			erofs_sb_set_ztailpacking(sbi);
 		} else {
 			inode->datalayout = EROFS_INODE_FLAT_INLINE;
 			erofs_dbg("Inline tail-end data (%u bytes) to %s",
@@ -685,7 +691,7 @@ static bool erofs_bh_flush_write_inline(struct erofs_buffer_head *bh)
 	const erofs_off_t off = erofs_btell(bh, false);
 	int ret;
 
-	ret = dev_write(sbi.dev, inode->idata, off, inode->idata_size);
+	ret = dev_write(bh->block->dev, inode->idata, off, inode->idata_size);
 	if (ret)
 		return false;
 
@@ -704,7 +710,7 @@ static struct erofs_bhops erofs_write_inline_bhops = {
 static int erofs_write_tail_end(struct erofs_inode *inode)
 {
 	struct erofs_buffer_head *bh, *ibh;
-	struct erofs_device *dev = sbi.dev;
+	struct erofs_sb_info *sbi = inode->sbi;
 
 	bh = inode->bh_data;
 
@@ -727,20 +733,20 @@ static int erofs_write_tail_end(struct erofs_inode *inode)
 		pos = erofs_btell(bh, true) - EROFS_BLKSIZ;
 
 		/* 0'ed data should be padded at head for 0padding conversion */
-		if (erofs_sb_has_lz4_0padding() && inode->compressed_idata) {
+		if (erofs_sb_has_lz4_0padding(sbi) && inode->compressed_idata) {
 			zero_pos = pos;
 			pos += EROFS_BLKSIZ - inode->idata_size;
 		} else {
 			/* pad 0'ed data for the other cases */
 			zero_pos = pos + inode->idata_size;
 		}
-		ret = dev_write(dev, inode->idata, pos, inode->idata_size);
+		ret = dev_write(sbi->dev, inode->idata, pos, inode->idata_size);
 		if (ret)
 			return ret;
 
 		DBG_BUGON(inode->idata_size > EROFS_BLKSIZ);
 		if (inode->idata_size < EROFS_BLKSIZ) {
-			ret = dev_fillzero(dev, zero_pos,
+			ret = dev_fillzero(sbi->dev, zero_pos,
 					   EROFS_BLKSIZ - inode->idata_size,
 					   false);
 			if (ret)
@@ -767,6 +773,8 @@ out:
 
 static bool erofs_should_use_inode_extended(struct erofs_inode *inode)
 {
+	struct erofs_sb_info *sbi = inode->sbi;
+
 	if (cfg.c_force_inodeversion == FORCE_INODE_EXTENDED)
 		return true;
 	if (inode->i_size > UINT_MAX)
@@ -777,8 +785,8 @@ static bool erofs_should_use_inode_extended(struct erofs_inode *inode)
 		return true;
 	if (inode->i_nlink > USHRT_MAX)
 		return true;
-	if ((inode->i_mtime != sbi.build_time ||
-	     inode->i_mtime_nsec != sbi.build_time_nsec) &&
+	if ((inode->i_mtime != sbi->build_time ||
+	     inode->i_mtime_nsec != sbi->build_time_nsec) &&
 	    !cfg.c_ignore_mtime)
 		return true;
 	return false;
@@ -850,6 +858,7 @@ static int erofs_fill_inode(struct erofs_inode *inode,
 			    struct stat64 *st,
 			    const char *path)
 {
+	struct erofs_sb_info *sbi = inode->sbi;
 	int err = erofs_droid_inode_fsconfig(inode, st, path);
 
 	if (err)
@@ -871,11 +880,11 @@ static int erofs_fill_inode(struct erofs_inode *inode,
 
 	switch (cfg.c_timeinherit) {
 	case TIMESTAMP_CLAMPING:
-		if (inode->i_mtime < sbi.build_time)
+		if (inode->i_mtime < sbi->build_time)
 			break;
 	case TIMESTAMP_FIXED:
-		inode->i_mtime = sbi.build_time;
-		inode->i_mtime_nsec = sbi.build_time_nsec;
+		inode->i_mtime = sbi->build_time;
+		inode->i_mtime_nsec = sbi->build_time_nsec;
 	default:
 		break;
 	}
@@ -921,7 +930,7 @@ static int erofs_fill_inode(struct erofs_inode *inode,
 	return 0;
 }
 
-static struct erofs_inode *erofs_new_inode(void)
+static struct erofs_inode *erofs_new_inode(struct erofs_sb_info *sbi)
 {
 	struct erofs_inode *inode;
 
@@ -929,7 +938,8 @@ static struct erofs_inode *erofs_new_inode(void)
 	if (!inode)
 		return ERR_PTR(-ENOMEM);
 
-	inode->i_ino[0] = sbi.inos++;	/* inode serial number */
+	inode->sbi = sbi;
+	inode->i_ino[0] = sbi->inos++;	/* inode serial number */
 	inode->i_count = 1;
 
 	init_list_head(&inode->i_subdirs);
@@ -938,7 +948,8 @@ static struct erofs_inode *erofs_new_inode(void)
 }
 
 /* get the inode from the (source) path */
-static struct erofs_inode *erofs_iget_from_path(const char *path, bool is_src)
+static struct erofs_inode *erofs_iget_from_path(struct erofs_sb_info *sbi,
+						const char *path, bool is_src)
 {
 	struct stat64 st;
 	struct erofs_inode *inode;
@@ -964,7 +975,7 @@ static struct erofs_inode *erofs_iget_from_path(const char *path, bool is_src)
 	}
 
 	/* cannot find in the inode cache */
-	inode = erofs_new_inode();
+	inode = erofs_new_inode(sbi);
 	if (IS_ERR(inode))
 		return inode;
 
@@ -990,7 +1001,7 @@ static void erofs_fixup_meta_blkaddr(struct erofs_inode *rootdir)
 		meta_offset = round_up(off - rootnid_maxoffset, EROFS_BLKSIZ);
 	else
 		meta_offset = 0;
-	sbi.meta_blkaddr = erofs_blknr(meta_offset);
+	rootdir->sbi->meta_blkaddr = erofs_blknr(meta_offset);
 	rootdir->nid = (off - meta_offset) >> EROFS_ISLOTBITS;
 }
 
@@ -1005,7 +1016,7 @@ erofs_nid_t erofs_lookupnid(struct erofs_inode *inode)
 	erofs_mapbh(bh->block);
 	off = erofs_btell(bh, false);
 
-	meta_offset = blknr_to_addr(sbi.meta_blkaddr);
+	meta_offset = blknr_to_addr(inode->sbi->meta_blkaddr);
 	DBG_BUGON(off < meta_offset);
 	return inode->nid = (off - meta_offset) >> EROFS_ISLOTBITS;
 }
@@ -1132,7 +1143,7 @@ static struct erofs_inode *erofs_mkfs_build_tree(struct erofs_inode *dir)
 					sizeof("Processing  ...") - 1);
 		erofs_update_progressinfo("Processing %s ...", trimmed);
 		free(trimmed);
-		d->inode = erofs_mkfs_build_tree_from_path(dir, buf);
+		d->inode = erofs_mkfs_build_tree_from_path(dir->sbi, dir, buf);
 		if (IS_ERR(d->inode)) {
 			ret = PTR_ERR(d->inode);
 fail:
@@ -1160,10 +1171,11 @@ err:
 	return ERR_PTR(ret);
 }
 
-struct erofs_inode *erofs_mkfs_build_tree_from_path(struct erofs_inode *parent,
+struct erofs_inode *erofs_mkfs_build_tree_from_path(struct erofs_sb_info *sbi,
+						    struct erofs_inode *parent,
 						    const char *path)
 {
-	struct erofs_inode *const inode = erofs_iget_from_path(path, true);
+	struct erofs_inode *const inode = erofs_iget_from_path(sbi, path, true);
 
 	if (IS_ERR(inode))
 		return inode;
diff --git a/lib/io.c b/lib/io.c
index 2b9811e..d4d127e 100644
--- a/lib/io.c
+++ b/lib/io.c
@@ -10,6 +10,7 @@
 #ifndef _GNU_SOURCE
 #define _GNU_SOURCE
 #endif
+#include <stdlib.h>
 #include <sys/stat.h>
 #include <sys/ioctl.h>
 #include "erofs/io.h"
@@ -52,6 +53,7 @@ void dev_close(struct erofs_device *dev)
 	dev->name = NULL;
 	dev->fd   = -1;
 	dev->size   = 0;
+	free(dev);
 }
 
 int dev_open(struct erofs_device *dev, const char *devname)
diff --git a/lib/namei.c b/lib/namei.c
index bb14d30..8612b81 100644
--- a/lib/namei.c
+++ b/lib/namei.c
@@ -28,8 +28,9 @@ int erofs_read_inode_from_disk(struct erofs_inode *vi)
 	char buf[sizeof(struct erofs_inode_extended)];
 	struct erofs_inode_compact *dic;
 	struct erofs_inode_extended *die;
-	struct erofs_device *dev = sbi.dev;
-	const erofs_off_t inode_loc = iloc(vi->nid);
+	struct erofs_sb_info *sbi = vi->sbi;
+	struct erofs_device *dev = vi->sbi->dev;
+	const erofs_off_t inode_loc = iloc(sbi, vi->nid);
 
 	ret = dev_read(dev, 0, buf, inode_loc, sizeof(*dic));
 	if (ret < 0)
@@ -115,8 +116,8 @@ int erofs_read_inode_from_disk(struct erofs_inode *vi)
 		vi->i_gid = le16_to_cpu(dic->i_gid);
 		vi->i_nlink = le16_to_cpu(dic->i_nlink);
 
-		vi->i_mtime = sbi.build_time;
-		vi->i_mtime_nsec = sbi.build_time_nsec;
+		vi->i_mtime = sbi->build_time;
+		vi->i_mtime_nsec = sbi->build_time_nsec;
 
 		vi->i_size = le32_to_cpu(dic->i_size);
 		if (vi->datalayout == EROFS_INODE_CHUNK_BASED)
@@ -183,6 +184,7 @@ struct erofs_dirent *find_target_dirent(erofs_nid_t pnid,
 }
 
 struct nameidata {
+	struct erofs_sb_info *sbi;
 	erofs_nid_t	nid;
 	unsigned int	ftype;
 };
@@ -193,7 +195,10 @@ int erofs_namei(struct nameidata *nd,
 	erofs_nid_t nid = nd->nid;
 	int ret;
 	char buf[EROFS_BLKSIZ];
-	struct erofs_inode vi = { .nid = nid };
+	struct erofs_inode vi = {
+		.sbi = nd->sbi,
+		.nid = nid,
+	};
 	erofs_off_t offset;
 
 	ret = erofs_read_inode_from_disk(&vi);
@@ -235,7 +240,7 @@ int erofs_namei(struct nameidata *nd,
 
 static int link_path_walk(const char *name, struct nameidata *nd)
 {
-	nd->nid = sbi.root_nid;
+	nd->nid = nd->sbi->root_nid;
 
 	while (*name == '/')
 		name++;
@@ -265,7 +270,7 @@ static int link_path_walk(const char *name, struct nameidata *nd)
 int erofs_ilookup(const char *path, struct erofs_inode *vi)
 {
 	int ret;
-	struct nameidata nd;
+	struct nameidata nd = { .sbi = vi->sbi };
 
 	ret = link_path_walk(path, &nd);
 	if (ret)
diff --git a/lib/super.c b/lib/super.c
index 8727161..fcd27b6 100644
--- a/lib/super.c
+++ b/lib/super.c
@@ -31,7 +31,7 @@ static int erofs_init_devices(struct erofs_sb_info *sbi,
 
 	sbi->total_blocks = sbi->primarydevice_blocks;
 
-	if (!erofs_sb_has_device_table())
+	if (!erofs_sb_has_device_table(sbi))
 		ondisk_extradevs = 0;
 	else
 		ondisk_extradevs = le16_to_cpu(dsb->extra_devices);
@@ -66,14 +66,14 @@ static int erofs_init_devices(struct erofs_sb_info *sbi,
 	return 0;
 }
 
-int erofs_read_superblock(void)
+int erofs_read_superblock(struct erofs_sb_info *sbi)
 {
 	char data[EROFS_BLKSIZ];
 	struct erofs_super_block *dsb;
 	unsigned int blkszbits;
 	int ret;
 
-	ret = blk_read(sbi.dev, 0, data, 0, 1);
+	ret = blk_read(sbi->dev, 0, data, 0, 1);
 	if (ret < 0) {
 		erofs_err("cannot read erofs superblock: %d", ret);
 		return -EIO;
@@ -86,7 +86,7 @@ int erofs_read_superblock(void)
 		return ret;
 	}
 
-	sbi.feature_compat = le32_to_cpu(dsb->feature_compat);
+	sbi->feature_compat = le32_to_cpu(dsb->feature_compat);
 
 	blkszbits = dsb->blkszbits;
 	/* 9(512 bytes) + LOG_SECTORS_PER_BLOCK == LOG_BLOCK_SIZE */
@@ -96,26 +96,26 @@ int erofs_read_superblock(void)
 		return ret;
 	}
 
-	if (!check_layout_compatibility(&sbi, dsb))
+	if (!check_layout_compatibility(sbi, dsb))
 		return ret;
 
-	sbi.primarydevice_blocks = le32_to_cpu(dsb->blocks);
-	sbi.meta_blkaddr = le32_to_cpu(dsb->meta_blkaddr);
-	sbi.xattr_blkaddr = le32_to_cpu(dsb->xattr_blkaddr);
-	sbi.islotbits = EROFS_ISLOTBITS;
-	sbi.root_nid = le16_to_cpu(dsb->root_nid);
-	sbi.inos = le64_to_cpu(dsb->inos);
-	sbi.checksum = le32_to_cpu(dsb->checksum);
+	sbi->primarydevice_blocks = le32_to_cpu(dsb->blocks);
+	sbi->meta_blkaddr = le32_to_cpu(dsb->meta_blkaddr);
+	sbi->xattr_blkaddr = le32_to_cpu(dsb->xattr_blkaddr);
+	sbi->islotbits = EROFS_ISLOTBITS;
+	sbi->root_nid = le16_to_cpu(dsb->root_nid);
+	sbi->inos = le64_to_cpu(dsb->inos);
+	sbi->checksum = le32_to_cpu(dsb->checksum);
 
-	sbi.build_time = le64_to_cpu(dsb->build_time);
-	sbi.build_time_nsec = le32_to_cpu(dsb->build_time_nsec);
+	sbi->build_time = le64_to_cpu(dsb->build_time);
+	sbi->build_time_nsec = le32_to_cpu(dsb->build_time_nsec);
 
-	memcpy(&sbi.uuid, dsb->uuid, sizeof(dsb->uuid));
-	return erofs_init_devices(&sbi, dsb);
+	memcpy(sbi->uuid, dsb->uuid, sizeof(dsb->uuid));
+	return erofs_init_devices(sbi, dsb);
 }
 
-void erofs_put_super(void)
+void erofs_put_super(struct erofs_sb_info *sbi)
 {
-	if (sbi.devs)
-		free(sbi.devs);
+	if (sbi->devs)
+		free(sbi->devs);
 }
diff --git a/lib/xattr.c b/lib/xattr.c
index e0bf922..2496004 100644
--- a/lib/xattr.c
+++ b/lib/xattr.c
@@ -549,7 +549,8 @@ static void erofs_cleanxattrs(bool sharedxattrs)
 static bool erofs_bh_flush_write_shared_xattrs(struct erofs_buffer_head *bh)
 {
 	void *buf = bh->fsprivate;
-	int err = dev_write(sbi.dev, buf, erofs_btell(bh, false), shared_xattrs_size);
+	int err = dev_write(bh->block->dev, buf, erofs_btell(bh, false),
+			    shared_xattrs_size);
 
 	if (err)
 		return false;
@@ -579,7 +580,8 @@ static int comp_xattr_item(const void *a, const void *b)
 	return la > lb;
 }
 
-int erofs_build_shared_xattrs_from_path(const char *path)
+int erofs_build_shared_xattrs_from_path(struct erofs_sb_info *sbi,
+					const char *path)
 {
 	int ret;
 	struct erofs_buffer_head *bh;
@@ -609,7 +611,7 @@ int erofs_build_shared_xattrs_from_path(const char *path)
 	if (!buf)
 		return -ENOMEM;
 
-	bh = erofs_balloc(sbi.dev, XATTR, shared_xattrs_size, 0, 0);
+	bh = erofs_balloc(sbi->dev, XATTR, shared_xattrs_size, 0, 0);
 	if (IS_ERR(bh)) {
 		free(buf);
 		return PTR_ERR(bh);
@@ -619,7 +621,7 @@ int erofs_build_shared_xattrs_from_path(const char *path)
 	erofs_mapbh(bh->block);
 	off = erofs_btell(bh, false);
 
-	sbi.xattr_blkaddr = off / EROFS_BLKSIZ;
+	sbi->xattr_blkaddr = off / EROFS_BLKSIZ;
 	off %= EROFS_BLKSIZ;
 	p = 0;
 
@@ -716,6 +718,7 @@ char *erofs_export_xattr_ibody(struct list_head *ixattrs, unsigned int size)
 }
 
 struct xattr_iter {
+	struct erofs_sb_info *sbi;
 	char page[EROFS_BLKSIZ];
 
 	void *kaddr;
@@ -729,7 +732,8 @@ static int init_inode_xattrs(struct erofs_inode *vi)
 	struct xattr_iter it;
 	unsigned int i;
 	struct erofs_xattr_ibody_header *ih;
-	struct erofs_device *dev = sbi.dev;
+	struct erofs_sb_info *sbi = vi->sbi;
+	struct erofs_device *dev = vi->sbi->dev;
 	int ret = 0;
 
 	/* the most case is that xattrs of this inode are initialized. */
@@ -757,8 +761,8 @@ static int init_inode_xattrs(struct erofs_inode *vi)
 		return -ENOATTR;
 	}
 
-	it.blkaddr = erofs_blknr(iloc(vi->nid) + vi->inode_isize);
-	it.ofs = erofs_blkoff(iloc(vi->nid) + vi->inode_isize);
+	it.blkaddr = erofs_blknr(iloc(sbi, vi->nid) + vi->inode_isize);
+	it.ofs = erofs_blkoff(iloc(sbi, vi->nid) + vi->inode_isize);
 
 	ret = blk_read(dev, 0, it.page, it.blkaddr, 1);
 	if (ret < 0)
@@ -825,7 +829,7 @@ static inline int xattr_iter_fixup(struct xattr_iter *it)
 
 	it->blkaddr += erofs_blknr(it->ofs);
 
-	ret = blk_read(sbi.dev, 0, it->page, it->blkaddr, 1);
+	ret = blk_read(it->sbi->dev, 0, it->page, it->blkaddr, 1);
 	if (ret < 0)
 		return -EIO;
 
@@ -837,6 +841,8 @@ static inline int xattr_iter_fixup(struct xattr_iter *it)
 static int inline_xattr_iter_pre(struct xattr_iter *it,
 				   struct erofs_inode *vi)
 {
+	struct erofs_sb_info *sbi = vi->sbi;
+	struct erofs_device *dev = vi->sbi->dev;
 	unsigned int xattr_header_sz, inline_xattr_ofs;
 	int ret;
 
@@ -848,10 +854,10 @@ static int inline_xattr_iter_pre(struct xattr_iter *it,
 
 	inline_xattr_ofs = vi->inode_isize + xattr_header_sz;
 
-	it->blkaddr = erofs_blknr(iloc(vi->nid) + inline_xattr_ofs);
-	it->ofs = erofs_blkoff(iloc(vi->nid) + inline_xattr_ofs);
+	it->blkaddr = erofs_blknr(iloc(sbi, vi->nid) + inline_xattr_ofs);
+	it->ofs = erofs_blkoff(iloc(sbi, vi->nid) + inline_xattr_ofs);
 
-	ret = blk_read(sbi.dev, 0, it->page, it->blkaddr, 1);
+	ret = blk_read(dev, 0, it->page, it->blkaddr, 1);
 	if (ret < 0)
 		return -EIO;
 
@@ -1038,17 +1044,18 @@ static int inline_getxattr(struct erofs_inode *vi, struct getxattr_iter *it)
 
 static int shared_getxattr(struct erofs_inode *vi, struct getxattr_iter *it)
 {
+	struct erofs_sb_info *sbi = vi->sbi;
 	unsigned int i;
 	int ret = -ENOATTR;
 
 	for (i = 0; i < vi->xattr_shared_count; ++i) {
 		erofs_blk_t blkaddr =
-			xattrblock_addr(vi->xattr_shared_xattrs[i]);
+			xattrblock_addr(sbi, vi->xattr_shared_xattrs[i]);
 
 		it->it.ofs = xattrblock_offset(vi->xattr_shared_xattrs[i]);
 
 		if (!i || blkaddr != it->it.blkaddr) {
-			ret = blk_read(sbi.dev, 0, it->it.page, blkaddr, 1);
+			ret = blk_read(sbi->dev, 0, it->it.page, blkaddr, 1);
 			if (ret < 0)
 				return -EIO;
 
@@ -1091,6 +1098,7 @@ int erofs_getxattr(struct erofs_inode *vi, const char *name, char *buffer,
 	it.buffer = buffer;
 	it.buffer_size = buffer_size;
 
+	it.it.sbi = vi->sbi;
 	ret = inline_getxattr(vi, &it);
 	if (ret == -ENOATTR)
 		ret = shared_getxattr(vi, &it);
@@ -1178,16 +1186,17 @@ static int inline_listxattr(struct erofs_inode *vi, struct listxattr_iter *it)
 
 static int shared_listxattr(struct erofs_inode *vi, struct listxattr_iter *it)
 {
+	struct erofs_sb_info *sbi = vi->sbi;
 	unsigned int i;
 	int ret = 0;
 
 	for (i = 0; i < vi->xattr_shared_count; ++i) {
 		erofs_blk_t blkaddr =
-			xattrblock_addr(vi->xattr_shared_xattrs[i]);
+			xattrblock_addr(sbi, vi->xattr_shared_xattrs[i]);
 
 		it->it.ofs = xattrblock_offset(vi->xattr_shared_xattrs[i]);
 		if (!i || blkaddr != it->it.blkaddr) {
-			ret = blk_read(sbi.dev, 0, it->it.page, blkaddr, 1);
+			ret = blk_read(sbi->dev, 0, it->it.page, blkaddr, 1);
 			if (ret < 0)
 				return -EIO;
 
@@ -1218,6 +1227,7 @@ int erofs_listxattr(struct erofs_inode *vi, char *buffer, size_t buffer_size)
 	it.buffer_size = buffer_size;
 	it.buffer_ofs = 0;
 
+	it.it.sbi = vi->sbi;
 	ret = inline_listxattr(vi, &it);
 	if (ret < 0 && ret != -ENOATTR)
 		return ret;
diff --git a/lib/zmap.c b/lib/zmap.c
index 6e5a5d4..0ddc922 100644
--- a/lib/zmap.c
+++ b/lib/zmap.c
@@ -16,8 +16,10 @@ static int z_erofs_do_map_blocks(struct erofs_inode *vi,
 
 int z_erofs_fill_inode(struct erofs_inode *vi)
 {
-	if (!erofs_sb_has_big_pcluster() &&
-	    !erofs_sb_has_ztailpacking() &&
+	struct erofs_sb_info *sbi = vi->sbi;
+
+	if (!erofs_sb_has_big_pcluster(sbi) &&
+	    !erofs_sb_has_ztailpacking(sbi) &&
 	    vi->datalayout == EROFS_INODE_FLAT_COMPRESSION_LEGACY) {
 		vi->z_advise = 0;
 		vi->z_algorithmtype[0] = 0;
@@ -31,6 +33,7 @@ int z_erofs_fill_inode(struct erofs_inode *vi)
 
 static int z_erofs_fill_inode_lazy(struct erofs_inode *vi)
 {
+	struct erofs_sb_info *sbi = vi->sbi;
 	int ret;
 	erofs_off_t pos;
 	struct z_erofs_map_header *h;
@@ -39,12 +42,13 @@ static int z_erofs_fill_inode_lazy(struct erofs_inode *vi)
 	if (vi->flags & EROFS_I_Z_INITED)
 		return 0;
 
-	DBG_BUGON(!erofs_sb_has_big_pcluster() &&
-		  !erofs_sb_has_ztailpacking() &&
+	DBG_BUGON(!erofs_sb_has_big_pcluster(sbi) &&
+		  !erofs_sb_has_ztailpacking(sbi) &&
 		  vi->datalayout == EROFS_INODE_FLAT_COMPRESSION_LEGACY);
-	pos = round_up(iloc(vi->nid) + vi->inode_isize + vi->xattr_isize, 8);
+	pos = round_up(iloc(vi->sbi, vi->nid) + vi->inode_isize +
+		       vi->xattr_isize, 8);
 
-	ret = dev_read(sbi.dev, 0, buf, pos, sizeof(buf));
+	ret = dev_read(sbi->dev, 0, buf, pos, sizeof(buf));
 	if (ret < 0)
 		return -EIO;
 
@@ -111,7 +115,7 @@ static int z_erofs_reload_indexes(struct z_erofs_maprecorder *m,
 	if (map->index == eblk)
 		return 0;
 
-	ret = blk_read(sbi.dev, 0, mpage, eblk, 1);
+	ret = blk_read(m->inode->sbi->dev, 0, mpage, eblk, 1);
 	if (ret < 0)
 		return -EIO;
 
@@ -124,7 +128,7 @@ static int legacy_load_cluster_from_disk(struct z_erofs_maprecorder *m,
 					 unsigned long lcn)
 {
 	struct erofs_inode *const vi = m->inode;
-	const erofs_off_t ibase = iloc(vi->nid);
+	const erofs_off_t ibase = iloc(vi->sbi, vi->nid);
 	const erofs_off_t pos =
 		Z_EROFS_VLE_LEGACY_INDEX_ALIGN(ibase + vi->inode_isize +
 					       vi->xattr_isize) +
@@ -322,9 +326,9 @@ static int compacted_load_cluster_from_disk(struct z_erofs_maprecorder *m,
 {
 	struct erofs_inode *const vi = m->inode;
 	const unsigned int lclusterbits = vi->z_logical_clusterbits;
-	const erofs_off_t ebase = round_up(iloc(vi->nid) + vi->inode_isize +
-					   vi->xattr_isize, 8) +
-		sizeof(struct z_erofs_map_header);
+	const erofs_off_t ebase = round_up(iloc(vi->sbi, vi->nid) +
+					   vi->inode_isize + vi->xattr_isize, 8) +
+				  sizeof(struct z_erofs_map_header);
 	const unsigned int totalidx = BLK_ROUND_UP(vi->i_size);
 	unsigned int compacted_4b_initial, compacted_2b;
 	unsigned int amortizedshift;
diff --git a/mkfs/main.c b/mkfs/main.c
index e3ad9d9..3e8a1c0 100644
--- a/mkfs/main.c
+++ b/mkfs/main.c
@@ -176,7 +176,7 @@ static int parse_extended_opts(const char *opts)
 		if (MATCH_EXTENTED_OPT("nosbcrc", token, keylen)) {
 			if (vallen)
 				return -EINVAL;
-			erofs_sb_clear_sb_chksum();
+			erofs_sb_clear_sb_chksum(cfg.sbi);
 		}
 
 		if (MATCH_EXTENTED_OPT("noinline_data", token, keylen)) {
@@ -265,7 +265,7 @@ static int mkfs_parse_options_cfg(int argc, char *argv[])
 			break;
 #ifdef HAVE_LIBUUID
 		case 'U':
-			if (uuid_parse(optarg, sbi.uuid)) {
+			if (uuid_parse(optarg, cfg.sbi->uuid)) {
 				erofs_err("invalid UUID %s", optarg);
 				return -EINVAL;
 			}
@@ -373,7 +373,7 @@ static int mkfs_parse_options_cfg(int argc, char *argv[])
 					  optarg);
 				return -EINVAL;
 			}
-			erofs_sb_set_chunked_file();
+			erofs_sb_set_chunked_file(cfg.sbi);
 			break;
 		case 12:
 			quiet = true;
@@ -456,24 +456,25 @@ static int mkfs_parse_options_cfg(int argc, char *argv[])
 	return 0;
 }
 
-int erofs_mkfs_update_super_block(struct erofs_buffer_head *bh,
+int erofs_mkfs_update_super_block(struct erofs_sb_info *sbi,
+				  struct erofs_buffer_head *bh,
 				  erofs_nid_t root_nid,
 				  erofs_blk_t *blocks)
 {
 	struct erofs_super_block sb = {
 		.magic     = cpu_to_le32(EROFS_SUPER_MAGIC_V1),
 		.blkszbits = LOG_BLOCK_SIZE,
-		.inos   = cpu_to_le64(sbi.inos),
-		.build_time = cpu_to_le64(sbi.build_time),
-		.build_time_nsec = cpu_to_le32(sbi.build_time_nsec),
+		.inos   = cpu_to_le64(sbi->inos),
+		.build_time = cpu_to_le64(sbi->build_time),
+		.build_time_nsec = cpu_to_le32(sbi->build_time_nsec),
 		.blocks = 0,
-		.meta_blkaddr  = sbi.meta_blkaddr,
-		.xattr_blkaddr = sbi.xattr_blkaddr,
-		.feature_incompat = cpu_to_le32(sbi.feature_incompat),
-		.feature_compat = cpu_to_le32(sbi.feature_compat &
+		.meta_blkaddr  = sbi->meta_blkaddr,
+		.xattr_blkaddr = sbi->xattr_blkaddr,
+		.feature_incompat = cpu_to_le32(sbi->feature_incompat),
+		.feature_compat = cpu_to_le32(sbi->feature_compat &
 					      ~EROFS_FEATURE_COMPAT_SB_CHKSUM),
-		.extra_devices = cpu_to_le16(sbi.extra_devices),
-		.devt_slotoff = cpu_to_le16(sbi.devt_slotoff),
+		.extra_devices = cpu_to_le16(sbi->extra_devices),
+		.devt_slotoff = cpu_to_le16(sbi->devt_slotoff),
 	};
 	const unsigned int sb_blksize =
 		round_up(EROFS_SUPER_END, EROFS_BLKSIZ);
@@ -482,12 +483,12 @@ int erofs_mkfs_update_super_block(struct erofs_buffer_head *bh,
 	*blocks         = erofs_mapbh(NULL);
 	sb.blocks       = cpu_to_le32(*blocks);
 	sb.root_nid     = cpu_to_le16(root_nid);
-	memcpy(sb.uuid, sbi.uuid, sizeof(sb.uuid));
+	memcpy(sb.uuid, sbi->uuid, sizeof(sb.uuid));
 
-	if (erofs_sb_has_compr_cfgs())
-		sb.u1.available_compr_algs = sbi.available_compr_algs;
+	if (erofs_sb_has_compr_cfgs(sbi))
+		sb.u1.available_compr_algs = sbi->available_compr_algs;
 	else
-		sb.u1.lz4_max_distance = cpu_to_le16(sbi.lz4_max_distance);
+		sb.u1.lz4_max_distance = cpu_to_le16(sbi->lz4_max_distance);
 
 	buf = calloc(sb_blksize, 1);
 	if (!buf) {
@@ -502,13 +503,13 @@ int erofs_mkfs_update_super_block(struct erofs_buffer_head *bh,
 	return 0;
 }
 
-static int erofs_mkfs_superblock_csum_set(void)
+static int erofs_mkfs_superblock_csum_set(struct erofs_sb_info *sbi)
 {
 	int ret;
 	u8 buf[EROFS_BLKSIZ];
 	u32 crc;
 	struct erofs_super_block *sb;
-	struct erofs_device *dev = sbi.dev;
+	struct erofs_device *dev = sbi->dev;
 
 	ret = blk_read(dev, 0, buf, 0, 1);
 	if (ret) {
@@ -551,15 +552,15 @@ static void erofs_mkfs_default_options(void)
 {
 	cfg.c_showprogress = true;
 	cfg.c_legacy_compress = false;
-	sbi.feature_incompat = EROFS_FEATURE_INCOMPAT_LZ4_0PADDING;
-	sbi.feature_compat = EROFS_FEATURE_COMPAT_SB_CHKSUM |
+	cfg.sbi->feature_incompat = EROFS_FEATURE_INCOMPAT_LZ4_0PADDING;
+	cfg.sbi->feature_compat = EROFS_FEATURE_COMPAT_SB_CHKSUM |
 			     EROFS_FEATURE_COMPAT_MTIME;
 
 	/* generate a default uuid first */
 #ifdef HAVE_LIBUUID
 	do {
-		uuid_generate(sbi.uuid);
-	} while (uuid_is_null(sbi.uuid));
+		uuid_generate(cfg.sbi->uuid);
+	} while (uuid_is_null(cfg.sbi->uuid));
 #endif
 }
 
@@ -599,6 +600,7 @@ void erofs_show_progs(int argc, char *argv[])
 int main(int argc, char **argv)
 {
 	int err = 0;
+	struct erofs_sb_info *sbi;
 	struct erofs_buffer_head *sb_bh;
 	struct erofs_inode *root_inode;
 	erofs_nid_t root_nid;
@@ -609,9 +611,10 @@ int main(int argc, char **argv)
 
 	erofs_init_configure();
 
-	sbi.dev = erofs_init_dev();
-	if (IS_ERR(sbi.dev))
+	sbi = erofs_init_sbi();
+	if (IS_ERR(sbi))
 		return 1;
+	cfg.sbi = sbi;
 
 	erofs_mkfs_default_options();
 	err = mkfs_parse_options_cfg(argc, argv);
@@ -645,14 +648,14 @@ int main(int argc, char **argv)
 	}
 
 	if (cfg.c_unix_timestamp != -1) {
-		sbi.build_time      = cfg.c_unix_timestamp;
-		sbi.build_time_nsec = 0;
+		sbi->build_time      = cfg.c_unix_timestamp;
+		sbi->build_time_nsec = 0;
 	} else if (!gettimeofday(&t, NULL)) {
-		sbi.build_time      = t.tv_sec;
-		sbi.build_time_nsec = t.tv_usec;
+		sbi->build_time      = t.tv_sec;
+		sbi->build_time_nsec = t.tv_usec;
 	}
 
-	err = dev_open(sbi.dev, cfg.c_img_path);
+	err = dev_open(sbi->dev, cfg.c_img_path);
 	if (err) {
 		usage();
 		return 1;
@@ -671,7 +674,7 @@ int main(int argc, char **argv)
 	}
 #endif
 	erofs_show_config();
-	if (erofs_sb_has_chunked_file())
+	if (erofs_sb_has_chunked_file(sbi))
 		erofs_warn("EXPERIMENTAL chunked file feature in use. Use at your own risk!");
 	if (cfg.c_ztailpacking)
 		erofs_warn("EXPERIMENTAL compressed inline data feature in use. Use at your own risk!");
@@ -680,7 +683,7 @@ int main(int argc, char **argv)
 	if (cfg.c_random_pclusterblks)
 		srand(time(NULL));
 #endif
-	sb_bh = erofs_buffer_init();
+	sb_bh = erofs_buffer_init(sbi);
 	if (IS_ERR(sb_bh)) {
 		err = PTR_ERR(sb_bh);
 		erofs_err("failed to initialize buffers: %s",
@@ -701,34 +704,34 @@ int main(int argc, char **argv)
 		goto exit;
 	}
 
-	err = z_erofs_compress_init(sb_bh);
+	err = z_erofs_compress_init(sbi, sb_bh);
 	if (err) {
 		erofs_err("failed to initialize compressor: %s",
 			  erofs_strerror(err));
 		goto exit;
 	}
 
-	err = erofs_generate_devtable();
+	err = erofs_generate_devtable(sbi);
 	if (err) {
 		erofs_err("failed to generate device table: %s",
 			  erofs_strerror(err));
 		goto exit;
 	}
 #ifdef HAVE_LIBUUID
-	uuid_unparse_lower(sbi.uuid, uuid_str);
+	uuid_unparse_lower(sbi->uuid, uuid_str);
 #endif
 	erofs_info("filesystem UUID: %s", uuid_str);
 
 	erofs_inode_manager_init();
 
-	err = erofs_build_shared_xattrs_from_path(cfg.c_src_path);
+	err = erofs_build_shared_xattrs_from_path(sbi, cfg.c_src_path);
 	if (err) {
 		erofs_err("failed to build shared xattrs: %s",
 			  erofs_strerror(err));
 		goto exit;
 	}
 
-	root_inode = erofs_mkfs_build_tree_from_path(NULL, cfg.c_src_path);
+	root_inode = erofs_mkfs_build_tree_from_path(sbi, NULL, cfg.c_src_path);
 	if (IS_ERR(root_inode)) {
 		err = PTR_ERR(root_inode);
 		goto exit;
@@ -739,29 +742,29 @@ int main(int argc, char **argv)
 
 	if (cfg.c_chunkbits) {
 		erofs_info("total metadata: %u blocks", erofs_mapbh(NULL));
-		err = erofs_blob_remap();
+		err = erofs_blob_remap(sbi);
 		if (err)
 			goto exit;
 	}
 
-	err = erofs_mkfs_update_super_block(sb_bh, root_nid, &nblocks);
+	err = erofs_mkfs_update_super_block(sbi, sb_bh, root_nid, &nblocks);
 	if (err)
 		goto exit;
 
 	/* flush all remaining buffers */
-	if (!erofs_bflush(sbi.dev, NULL))
+	if (!erofs_bflush(sbi->dev, NULL))
 		err = -EIO;
 	else
-		err = dev_resize(sbi.dev, nblocks);
+		err = dev_resize(sbi->dev, nblocks);
 
-	if (!err && erofs_sb_has_sb_chksum())
-		err = erofs_mkfs_superblock_csum_set();
+	if (!err && erofs_sb_has_sb_chksum(sbi))
+		err = erofs_mkfs_superblock_csum_set(sbi);
 exit:
 	z_erofs_compress_exit();
 #ifdef WITH_ANDROID
 	erofs_droid_blocklist_fclose();
 #endif
-	dev_close(sbi.dev);
+	dev_close(sbi->dev);
 	erofs_cleanup_compress_hints();
 	erofs_cleanup_exclude_rules();
 	if (cfg.c_chunkbits)
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 3/4] erofs-utils: tests: add test for dump.erofs
  2022-09-09  2:18 [PATCH 1/4] erofs-utils: remove global erofs_devfd Huang Jianan
  2022-09-09  2:18 ` [PATCH 2/4] erofs-utils: remove global sbi Huang Jianan
@ 2022-09-09  2:18 ` Huang Jianan
  2022-09-09  2:18 ` [PATCH 4/4] erofs-utils: tests: add test for fsck.erofs Huang Jianan
  2 siblings, 0 replies; 4+ messages in thread
From: Huang Jianan @ 2022-09-09  2:18 UTC (permalink / raw)
  To: linux-erofs

Add basic functional check for dump.erofs.

Signed-off-by: Huang Jianan <jnhuang@linux.alibaba.com>
---
 tests/Makefile.am   |  6 +++-
 tests/erofs/021     | 83 +++++++++++++++++++++++++++++++++++++++++++++
 tests/erofs/021.out |  2 ++
 3 files changed, 90 insertions(+), 1 deletion(-)
 create mode 100755 tests/erofs/021
 create mode 100644 tests/erofs/021.out

diff --git a/tests/Makefile.am b/tests/Makefile.am
index b85ae89..bb1624d 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -22,7 +22,8 @@ TESTS_ENVIRONMENT = \
 	fi; \
 	[ -z $$MKFS_EROFS_PROG ] && export MKFS_EROFS_PROG=../mkfs/mkfs.erofs; \
 	[ -z $$FSCK_EROFS_PROG ] && export FSCK_EROFS_PROG=../fsck/fsck.erofs; \
-	[ -z $$EROFSFUSE_PROG ] && export EROFSFUSE_PROG=../fuse/erofsfuse;
+	[ -z $$EROFSFUSE_PROG ] && export EROFSFUSE_PROG=../fuse/erofsfuse; \
+	[ -z $$DUMP_EROFS_PROG ] && export DUMP_EROFS_PROG=../dump/dump.erofs;
 
 if ENABLE_LZ4
 TESTS_ENVIRONMENT += export lz4_on=1;
@@ -91,6 +92,9 @@ TESTS += erofs/018
 # 019 - check extended attribute functionality
 TESTS += erofs/019
 
+# 021 - check dump basic functionality
+TESTS += erofs/021
+
 EXTRA_DIST = common/rc erofs
 
 clean-local: clean-local-check
diff --git a/tests/erofs/021 b/tests/erofs/021
new file mode 100755
index 0000000..2bb45e7
--- /dev/null
+++ b/tests/erofs/021
@@ -0,0 +1,83 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0+
+#
+# 021 check dump basic functionality
+#
+seq=`basename $0`
+seqres=$RESULT_DIR/$(echo $0 | awk '{print $((NF-1))"/"$NF}' FS="/")
+
+# get standard environment, filters and checks
+. "${srcdir}/common/rc"
+
+cleanup()
+{
+	cd /
+	rm -rf $tmp.*
+}
+
+_require_erofs
+
+# remove previous $seqres.full before test
+rm -f $seqres.full
+
+# real QA test starts here
+echo "QA output created by $seq"
+
+[ -z "$lz4hc_on" ] && \
+	_notrun "lz4hc compression is disabled, skipped."
+
+if [ -z ]; then
+	SCRATCH_DEV=$tmp/erofs_$seq.img
+	rm -f SCRATCH_DEV
+fi
+
+localdir="$tmp/$seq"
+rm -rf $localdir
+mkdir -p $localdir
+
+# collect files pending for verification
+dirs=`find ../ -maxdepth 1 -type d -printf '%p:'`
+IFS=':'
+for d in $dirs; do
+	[ $d = '../' ] && continue
+	[ -z "${d##\.\./tests*}" ] && continue
+	[ -z "${d##\.\./\.*}" ] && continue
+	cp -nR $d $localdir
+done
+unset IFS
+
+uuid="98136b8d-d5a7-429c-b6ea-2021f19d7d93"
+timestamp="Thu Jan  1 08:00:00 1970"
+
+MKFS_OPTIONS="${MKFS_OPTIONS} -zlz4hc -T0 -U$uuid"
+_scratch_mkfs $localdir >> $seqres.full 2>&1 || _fail "failed to mkfs"
+_scratch_mount 2>>$seqres.full
+
+do_dump="$DUMP_EROFS_PROG $SCRATCH_DEV"
+echo "dump information for $SCRATCH_DEV" >>$seqres.full
+
+uuid_dump=`$do_dump |awk '/UUID/{print $3}'`
+echo "uuid: $uuid_dump" >>$seqres.full
+[ "x$uuid" = "x$uuid_dump" ] || _fail "check uuid fail"
+
+timestamp_dump=`$do_dump |awk -F"created:[ ]+" '/created:/{print $2}'`
+echo "timestamp: $timestamp_dump" >>$seqres.full
+[ "x$timestamp" = "x$timestamp_dump" ] || _fail "check timestamp fail"
+
+nid_sb=`$do_dump |awk '/root nid/{print $4}'`
+echo "root nid from superblock: $nid_sb" >>$seqres.full
+nid_ino=`$do_dump --path=/ |awk '/NID/{print $2}'`
+echo "root nid from inode: $nid_ino" >>$seqres.full
+[ "x$nid_sb" = "x$nid_ino" ] || _fail "check root nid fail"
+
+dirs_dump=`$do_dump --path=/ --ls |awk '/\s*[0-9]+\s+[0-9]+\s+/{print $3}'`
+echo "list root directory:\n$dirs_dump" >>$seqres.full
+dirs_ls=`ls -aU $SCRATCH_MNT`
+echo "list root directory for $SCRATCH_MNT:\n$dirs_ls" >>$seqres.full
+[ "x$dirs_dump" = "x$dirs_ls" ] || _fail "check ls fail"
+
+_scratch_unmount
+
+echo Silence is golden
+status=0
+exit 0
diff --git a/tests/erofs/021.out b/tests/erofs/021.out
new file mode 100644
index 0000000..09f4062
--- /dev/null
+++ b/tests/erofs/021.out
@@ -0,0 +1,2 @@
+QA output created by 021
+Silence is golden
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 4/4] erofs-utils: tests: add test for fsck.erofs
  2022-09-09  2:18 [PATCH 1/4] erofs-utils: remove global erofs_devfd Huang Jianan
  2022-09-09  2:18 ` [PATCH 2/4] erofs-utils: remove global sbi Huang Jianan
  2022-09-09  2:18 ` [PATCH 3/4] erofs-utils: tests: add test for dump.erofs Huang Jianan
@ 2022-09-09  2:18 ` Huang Jianan
  2 siblings, 0 replies; 4+ messages in thread
From: Huang Jianan @ 2022-09-09  2:18 UTC (permalink / raw)
  To: linux-erofs

Add basic functional check for fsck.erofs.

Signed-off-by: Huang Jianan <jnhuang@linux.alibaba.com>
---
 tests/Makefile.am   |  3 +++
 tests/common/rc     | 46 +++++++++++++++++++++++++++++++++
 tests/erofs/022     | 62 +++++++++++++++++++++++++++++++++++++++++++++
 tests/erofs/022.out |  2 ++
 4 files changed, 113 insertions(+)
 create mode 100755 tests/erofs/022
 create mode 100644 tests/erofs/022.out

diff --git a/tests/Makefile.am b/tests/Makefile.am
index bb1624d..25c1400 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -95,6 +95,9 @@ TESTS += erofs/019
 # 021 - check dump basic functionality
 TESTS += erofs/021
 
+# 021 - check fsck basic functionality
+TESTS += erofs/022
+
 EXTRA_DIST = common/rc erofs
 
 clean-local: clean-local-check
diff --git a/tests/common/rc b/tests/common/rc
index a9ae369..7b72573 100644
--- a/tests/common/rc
+++ b/tests/common/rc
@@ -189,6 +189,52 @@ _test_mkfs()
 	_mkfs $TEST_DEV "$*"
 }
 
+_do_fsck()
+{
+	local fsck_cmd=$1
+	local fsck_filter=$2
+	local fsck_dev=$3
+	shift 3
+	local extra_fsck_options="$*"
+	local fsck_status
+	local tmp=`mktemp -u`
+
+	eval "$fsck_cmd $FSCK_OPTIONS $fsck_dev $extra_fsck_options" \
+		1>$tmp.fsckstd 2>$tmp.fsckerr
+	fsck_status=$?
+
+	# output stored fsck output, filtering unnecessary output from stderr
+	cat $tmp.fsckstd
+	eval "cat $tmp.fsckerr | $fsck_filter" >&2
+
+	rm -f $tmp.fsckerr $tmp.fsckstd
+	return $fsck_status
+}
+
+_fsck()
+{
+	local fsck_dev=$1
+	shift 1
+	local extra_fsck_options="$*"
+	local fsck_cmd="$FSCK_EROFS_PROG"
+	local fsck_filter="true"
+
+	_do_fsck "$fsck_cmd" "$fsck_filter" "$fsck_dev" "$extra_fsck_options" \
+		2>$tmp.fsckerr 1>$tmp.fsckstd
+	fsck_status=$?
+
+	# output fsck stdout and stderr
+	cat $tmp.fsckstd
+	cat $tmp.fsckerr >&2
+	rm -f $tmp.fsckerr $tmp.fsckstd
+
+	return $fsck_status
+}
+
+_scratch_fsck()
+{
+	_fsck $SCRATCH_DEV "$*"
+}
 
 # Do the actual mount work.
 _do_mount()
diff --git a/tests/erofs/022 b/tests/erofs/022
new file mode 100755
index 0000000..d3baf7a
--- /dev/null
+++ b/tests/erofs/022
@@ -0,0 +1,62 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0+
+#
+# 022 check fsck basic functionality
+#
+seq=`basename $0`
+seqres=$RESULT_DIR/$(echo $0 | awk '{print $((NF-1))"/"$NF}' FS="/")
+
+# get standard environment, filters and checks
+. "${srcdir}/common/rc"
+
+cleanup()
+{
+	cd /
+	rm -rf $tmp.*
+}
+
+_require_fssum
+
+# remove previous $seqres.full before test
+rm -f $seqres.full
+
+# real QA test starts here
+echo "QA output created by $seq"
+
+if [ -z $SCRATCH_DEV ]; then
+	SCRATCH_DEV=$tmp/erofs_$seq.img
+	rm -f SCRATCH_DEV
+fi
+
+localdir="$tmp/$seq"
+rm -rf $localdir
+mkdir -p $localdir
+
+# collect files pending for verification
+dirs=`find ../ -maxdepth 1 -type d -printf '%p:'`
+IFS=':'
+for d in $dirs; do
+	[ $d = '../' ] && continue
+	[ -z "${d##\.\./tests*}" ] && continue
+	[ -z "${d##\.\./\.*}" ] && continue
+	cp -nR $d $localdir
+done
+unset IFS
+
+_scratch_mkfs $localdir >> $seqres.full 2>&1 || _fail "failed to mkfs"
+
+extradir="$tmp/extra$seq"
+mkdir -p $extradir
+FSCK_OPTIONS="$FSCK_OPTIONS --extract=$extradir --preserve"
+_scratch_fsck >> $seqres.full 2>&1 || _fail "failed to fsck"
+
+sum1=`$FSSUM_PROG $FSSUM_OPTS $localdir`
+echo "$localdir checksum is $sum1" >>$seqres.full
+sum2=`$FSSUM_PROG $FSSUM_OPTS $extradir`
+echo "$extradir checksum is $sum2" >>$seqres.full
+
+[ "x$sum1" = "x$sum2" ] || _fail "-->checkMD5 FAILED"
+
+echo Silence is golden
+status=0
+exit 0
diff --git a/tests/erofs/022.out b/tests/erofs/022.out
new file mode 100644
index 0000000..394c6a7
--- /dev/null
+++ b/tests/erofs/022.out
@@ -0,0 +1,2 @@
+QA output created by 022
+Silence is golden
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2022-09-09  2:24 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-09-09  2:18 [PATCH 1/4] erofs-utils: remove global erofs_devfd Huang Jianan
2022-09-09  2:18 ` [PATCH 2/4] erofs-utils: remove global sbi Huang Jianan
2022-09-09  2:18 ` [PATCH 3/4] erofs-utils: tests: add test for dump.erofs Huang Jianan
2022-09-09  2:18 ` [PATCH 4/4] erofs-utils: tests: add test for fsck.erofs Huang Jianan

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).