[PATCH v3 1/7] erofs-utils: lib: introduce liberofs_global_{init,exit}

Gao Xiang hsiangkao at linux.alibaba.com
Tue Aug 19 13:28:12 AEST 2025


Currently, the codebase only supports a single instance during
the entire running time. This will be extended to allow 3rd-party
applications to formally use liberofs with multiple instances
simultaneously. Therefore, it is necessary to separate global
initialization from per-instance initialization.

Another point is that libcurl will be used for other remote
backends, so it also needs to be separated from the current S3
backend.

Signed-off-by: Gao Xiang <hsiangkao at linux.alibaba.com>
---
 include/erofs/internal.h |  3 ++
 include/erofs/lock.h     |  6 ++++
 lib/Makefile.am          |  5 ++--
 lib/global.c             | 57 +++++++++++++++++++++++++++++++++++
 lib/liberofs_s3.h        |  1 +
 lib/remotes/s3.c         | 65 ++++++++++++++++++----------------------
 mkfs/main.c              | 22 +++++++-------
 7 files changed, 111 insertions(+), 48 deletions(-)
 create mode 100644 lib/global.c

diff --git a/include/erofs/internal.h b/include/erofs/internal.h
index e9b9099..92e83fd 100644
--- a/include/erofs/internal.h
+++ b/include/erofs/internal.h
@@ -426,6 +426,9 @@ struct erofs_map_dev {
 	unsigned int m_deviceid;
 };
 
+int liberofs_global_init(void);
+void liberofs_global_exit(void);
+
 /* super.c */
 int erofs_read_superblock(struct erofs_sb_info *sbi);
 void erofs_put_super(struct erofs_sb_info *sbi);
diff --git a/include/erofs/lock.h b/include/erofs/lock.h
index f7a4b47..c6e3093 100644
--- a/include/erofs/lock.h
+++ b/include/erofs/lock.h
@@ -16,6 +16,9 @@ static inline void erofs_mutex_init(erofs_mutex_t *lock)
 #define erofs_mutex_lock	pthread_mutex_lock
 #define erofs_mutex_unlock	pthread_mutex_unlock
 
+#define EROFS_DEFINE_MUTEX(lock)	\
+	erofs_mutex_t lock = PTHREAD_MUTEX_INITIALIZER
+
 typedef pthread_rwlock_t erofs_rwsem_t;
 
 static inline void erofs_init_rwsem(erofs_rwsem_t *lock)
@@ -33,6 +36,9 @@ static inline void erofs_mutex_init(erofs_mutex_t *lock) {}
 static inline void erofs_mutex_lock(erofs_mutex_t *lock) {}
 static inline void erofs_mutex_unlock(erofs_mutex_t *lock) {}
 
+#define EROFS_DEFINE_MUTEX(lock)	\
+	erofs_mutex_t lock = {}
+
 typedef struct {} erofs_rwsem_t;
 static inline void erofs_init_rwsem(erofs_rwsem_t *lock) {}
 static inline void erofs_down_read(erofs_rwsem_t *lock) {}
diff --git a/lib/Makefile.am b/lib/Makefile.am
index b079897..a3972b1 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -29,7 +29,8 @@ noinst_HEADERS = $(top_srcdir)/include/erofs_fs.h \
       $(top_srcdir)/include/erofs/rebuild.h \
       $(top_srcdir)/lib/liberofs_private.h \
       $(top_srcdir)/lib/liberofs_xxhash.h \
-      $(top_srcdir)/lib/liberofs_metabox.h
+      $(top_srcdir)/lib/liberofs_metabox.h \
+      $(top_srcdir)/lib/liberofs_s3.h
 
 noinst_HEADERS += compressor.h
 liberofs_la_SOURCES = config.c io.c cache.c super.c inode.c xattr.c exclude.c \
@@ -37,7 +38,7 @@ liberofs_la_SOURCES = config.c io.c cache.c super.c inode.c xattr.c exclude.c \
 		      compress_hints.c hashmap.c sha256.c blobchunk.c dir.c \
 		      fragments.c dedupe.c uuid_unparse.c uuid.c tar.c \
 		      block_list.c rebuild.c diskbuf.c bitops.c dedupe_ext.c \
-		      vmdk.c metabox.c
+		      vmdk.c metabox.c global.c
 
 liberofs_la_CFLAGS = -Wall ${libuuid_CFLAGS} -I$(top_srcdir)/include
 if ENABLE_LZ4
diff --git a/lib/global.c b/lib/global.c
new file mode 100644
index 0000000..b1a6d76
--- /dev/null
+++ b/lib/global.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0+ OR Apache-2.0
+/*
+ * Copyright (C) 2025 Alibaba Cloud
+ */
+#include "erofs/lock.h"
+#ifdef HAVE_CURL_CURL_H
+#include <curl/curl.h>
+#endif
+#ifdef HAVE_LIBXML_PARSER_H
+#include <libxml/parser.h>
+#endif
+#include "erofs/err.h"
+#include "erofs/config.h"
+
+static EROFS_DEFINE_MUTEX(erofs_global_mutex);
+#ifdef HAVE_LIBCURL
+static bool erofs_global_curl_initialized;
+#endif
+
+int liberofs_global_init(void)
+{
+	int err = 0;
+
+	erofs_mutex_lock(&erofs_global_mutex);
+	erofs_init_configure();
+#ifdef S3EROFS_ENABLED
+	xmlInitParser();
+#endif
+#ifdef HAVE_LIBCURL
+	if (!erofs_global_curl_initialized) {
+		if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) {
+			err = -EFAULT;
+			goto out_unlock;
+		}
+		erofs_global_curl_initialized = true;
+	}
+out_unlock:
+#endif
+	erofs_mutex_unlock(&erofs_global_mutex);
+	return err;
+}
+
+void liberofs_global_exit(void)
+{
+	erofs_mutex_lock(&erofs_global_mutex);
+#ifdef HAVE_LIBCURL
+	if (erofs_global_curl_initialized) {
+		curl_global_cleanup();
+		erofs_global_curl_initialized = false;
+	}
+#endif
+#ifdef S3EROFS_ENABLED
+	xmlCleanupParser();
+#endif
+	erofs_exit_configure();
+	erofs_mutex_unlock(&erofs_global_mutex);
+}
diff --git a/lib/liberofs_s3.h b/lib/liberofs_s3.h
index 27b041c..a178c64 100644
--- a/lib/liberofs_s3.h
+++ b/lib/liberofs_s3.h
@@ -25,6 +25,7 @@ enum s3erofs_signature_version {
 #define S3_SECRET_KEY_LEN 256
 
 struct erofs_s3 {
+	void *easy_curl;
 	const char *endpoint;
 	char access_key[S3_ACCESS_KEY_LEN + 1];
 	char secret_key[S3_SECRET_KEY_LEN + 1];
diff --git a/lib/remotes/s3.c b/lib/remotes/s3.c
index 1497b54..f4a364d 100644
--- a/lib/remotes/s3.c
+++ b/lib/remotes/s3.c
@@ -26,8 +26,6 @@
 
 #define BASE64_ENCODE_LEN(len)	(((len + 2) / 3) * 4)
 
-static CURL *easy_curl;
-
 struct s3erofs_query_params {
 	int num;
 	const char *key[S3EROFS_MAX_QUERY_PARAMS];
@@ -213,6 +211,7 @@ static int s3erofs_request_perform(struct erofs_s3 *s3,
 				   struct s3erofs_curl_request *req, void *resp)
 {
 	struct curl_slist *request_headers = NULL;
+	CURL *curl = s3->easy_curl;
 	long http_code = 0;
 	int ret;
 
@@ -226,11 +225,11 @@ static int s3erofs_request_perform(struct erofs_s3 *s3,
 		}
 	}
 
-	curl_easy_setopt(easy_curl, CURLOPT_URL, req->url);
-	curl_easy_setopt(easy_curl, CURLOPT_WRITEDATA, resp);
-	curl_easy_setopt(easy_curl, CURLOPT_HTTPHEADER, request_headers);
+	curl_easy_setopt(curl, CURLOPT_URL, req->url);
+	curl_easy_setopt(curl, CURLOPT_WRITEDATA, resp);
+	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, request_headers);
 
-	ret = curl_easy_perform(easy_curl);
+	ret = curl_easy_perform(curl);
 	if (ret != CURLE_OK) {
 		erofs_err("curl_easy_perform() failed: %s",
 			  curl_easy_strerror(ret));
@@ -238,7 +237,7 @@ static int s3erofs_request_perform(struct erofs_s3 *s3,
 		goto err_header;
 	}
 
-	ret = curl_easy_getinfo(easy_curl, CURLINFO_RESPONSE_CODE, &http_code);
+	ret = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
 	if (ret != CURLE_OK) {
 		erofs_err("curl_easy_getinfo() failed: %s",
 			  curl_easy_strerror(ret));
@@ -473,7 +472,7 @@ static int s3erofs_list_objects(struct s3erofs_object_iterator *it)
 	if (ret < 0)
 		return ret;
 
-	if (curl_easy_setopt(easy_curl, CURLOPT_WRITEFUNCTION,
+	if (curl_easy_setopt(s3->easy_curl, CURLOPT_WRITEFUNCTION,
 			     s3erofs_request_write_memory_cb) != CURLE_OK)
 		return -EIO;
 
@@ -547,43 +546,37 @@ s3erofs_get_next_object(struct s3erofs_object_iterator *it)
 	return NULL;
 }
 
-static int s3erofs_global_init(void)
+static int s3erofs_curl_easy_init(struct erofs_s3 *s3)
 {
-	if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK)
-		return -EIO;
+	CURL *curl;
 
-	easy_curl = curl_easy_init();
-	if (!easy_curl)
-		goto out_err;
+	curl = curl_easy_init();
+	if (!curl)
+		return -ENOMEM;
 
-	if (curl_easy_setopt(easy_curl, CURLOPT_FOLLOWLOCATION, 1L) != CURLE_OK)
-		goto out_err;
+	if (curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L) != CURLE_OK)
+		goto out_cleanup;
 
-	if (curl_easy_setopt(easy_curl, CURLOPT_CONNECTTIMEOUT, 30L) != CURLE_OK)
-		goto out_err;
+	if (curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30L) != CURLE_OK)
+		goto out_cleanup;
 
-	if (curl_easy_setopt(easy_curl, CURLOPT_USERAGENT,
+	if (curl_easy_setopt(curl, CURLOPT_USERAGENT,
 			     "s3erofs/" PACKAGE_VERSION) != CURLE_OK)
-		goto out_err;
+		goto out_cleanup;
 
-	xmlInitParser();
+	s3->easy_curl = curl;
 	return 0;
-out_err:
-	curl_global_cleanup();
-	return -EIO;
+out_cleanup:
+	curl_easy_cleanup(curl);
+	return -EFAULT;
 }
 
-static void s3erofs_global_exit(void)
+static void s3erofs_curl_easy_exit(struct erofs_s3 *s3)
 {
-	if (!easy_curl)
+	if (!s3->easy_curl)
 		return;
-
-	xmlCleanupParser();
-
-	curl_easy_cleanup(easy_curl);
-	easy_curl = NULL;
-
-	curl_global_cleanup();
+	curl_easy_cleanup(s3->easy_curl);
+	s3->easy_curl = NULL;
 }
 
 struct s3erofs_curl_getobject_resp {
@@ -623,7 +616,7 @@ static int s3erofs_remote_getobject(struct erofs_s3 *s3,
 	if (ret < 0)
 		return ret;
 
-	if (curl_easy_setopt(easy_curl, CURLOPT_WRITEFUNCTION,
+	if (curl_easy_setopt(s3->easy_curl, CURLOPT_WRITEFUNCTION,
 			     s3erofs_remote_getobject_cb) != CURLE_OK)
 		return -EIO;
 
@@ -689,7 +682,7 @@ int s3erofs_build_trees(struct erofs_inode *root, struct erofs_s3 *s3,
 	st.st_uid = root->i_uid;
 	st.st_gid = root->i_gid;
 
-	ret = s3erofs_global_init();
+	ret = s3erofs_curl_easy_init(s3);
 	if (ret) {
 		erofs_err("failed to initialize s3erofs: %s", erofs_strerror(ret));
 		return ret;
@@ -764,6 +757,6 @@ int s3erofs_build_trees(struct erofs_inode *root, struct erofs_s3 *s3,
 err_iter:
 	s3erofs_destroy_object_iterator(iter);
 err_global:
-	s3erofs_global_exit();
+	s3erofs_curl_easy_exit(s3);
 	return ret;
 }
diff --git a/mkfs/main.c b/mkfs/main.c
index 804d483..b8773fd 100644
--- a/mkfs/main.c
+++ b/mkfs/main.c
@@ -1481,10 +1481,12 @@ int main(int argc, char **argv)
 	struct timeval t;
 	FILE *blklst = NULL;
 	s64 mkfs_time = 0;
-	int err = 0;
+	int err;
 	u32 crc;
 
-	erofs_init_configure();
+	err = liberofs_global_init();
+	if (err)
+		return 1;
 	erofs_mkfs_default_options();
 
 	err = mkfs_parse_options_cfg(argc, argv);
@@ -1492,13 +1494,13 @@ int main(int argc, char **argv)
 	if (err) {
 		if (err == -EINVAL)
 			fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]);
-		return 1;
+		goto exit;
 	}
 
 	err = parse_source_date_epoch();
 	if (err) {
 		fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]);
-		return 1;
+		goto exit;
 	}
 
 	g_sbi.fixed_nsec = 0;
@@ -1521,14 +1523,14 @@ int main(int argc, char **argv)
 				(incremental_mode ? 0 : O_TRUNC));
 	if (err) {
 		fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]);
-		return 1;
+		goto exit;
 	}
 
 #ifdef WITH_ANDROID
 	if (cfg.fs_config_file &&
 	    load_canned_fs_config(cfg.fs_config_file) < 0) {
 		erofs_err("failed to load fs config %s", cfg.fs_config_file);
-		return 1;
+		goto exit;
 	}
 #endif
 	erofs_show_config();
@@ -1540,7 +1542,7 @@ int main(int argc, char **argv)
 		if (err) {
 			erofs_err("failed to initialize packedfile: %s",
 				  strerror(-err));
-			return 1;
+			goto exit;
 		}
 	}
 
@@ -1549,7 +1551,7 @@ int main(int argc, char **argv)
 		if (err) {
 			erofs_err("failed to initialize metabox: %s",
 				  erofs_strerror(err));
-			return 1;
+			goto exit;
 		}
 	}
 
@@ -1687,7 +1689,7 @@ int main(int argc, char **argv)
 	if (cfg.c_chunkbits) {
 		err = erofs_blob_init(cfg.c_blobdev_path, 1 << cfg.c_chunkbits);
 		if (err)
-			return 1;
+			goto exit;
 	}
 
 	if (tar_index_512b || cfg.c_blobdev_path) {
@@ -1851,7 +1853,6 @@ exit:
 	erofs_xattr_cleanup_name_prefixes();
 	erofs_rebuild_cleanup();
 	erofs_diskbuf_exit();
-	erofs_exit_configure();
 	if (source_mode == EROFS_MKFS_SOURCE_TAR) {
 		erofs_iostream_close(&erofstar.ios);
 		if (erofstar.ios.dumpfd >= 0)
@@ -1866,5 +1867,6 @@ exit:
 	erofs_update_progressinfo("Build completed.\n");
 	erofs_mkfs_showsummaries();
 	erofs_put_super(&g_sbi);
+	liberofs_global_exit();
 	return 0;
 }
-- 
2.43.5



More information about the Linux-erofs mailing list