From d4e294b7995a921fbc7691cccb7dd577bba52ba7 Mon Sep 17 00:00:00 2001
From: Fabrice Bauzac <fbauzac@amadeus.com>
Date: Mon, 23 Sep 2013 16:57:50 +0200
Subject: [PATCH] fuse: use dlsym() instead of relying on ld.so constructor
 functions

---
 ChangeLog      |   5 ++
 include/fuse.h |  69 ++++++++--------------------
 lib/fuse.c     | 122 ++++++++++++++++++++++++++++++++-----------------
 lib/fuse_i.h   |  15 ++++++
 4 files changed, 118 insertions(+), 93 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index f327084..8ffae80 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2014-02-04  Miklos Szeredi <miklos@szeredi.hu>
+
+	* libfuse: Don't use constructor functions for loading modules.
+	Original patch by Fabrice Bauzac
+
 2014-01-29  Miklos Szeredi <miklos@szeredi.hu>
 
 	* libfuse: Add "async_dio" and "writeback_cache" options.
diff --git a/include/fuse.h b/include/fuse.h
index b8a9307..a3a047e 100644
--- a/include/fuse.h
+++ b/include/fuse.h
@@ -867,64 +867,33 @@ struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size,
 			    void *user_data);
 
 /**
- * Filesystem module
+ * Factory for creating filesystem objects
  *
- * Filesystem modules are registered with the FUSE_REGISTER_MODULE()
- * macro.
+ * The function may use and remove options from 'args' that belong
+ * to this module.
  *
- * If the "-omodules=modname:..." option is present, filesystem
- * objects are created and pushed onto the stack with the 'factory'
- * function.
- */
-struct fuse_module {
-	/**
-	 * Name of filesystem
-	 */
-	const char *name;
-
-	/**
-	 * Factory for creating filesystem objects
-	 *
-	 * The function may use and remove options from 'args' that belong
-	 * to this module.
-	 *
-	 * For now the 'fs' vector always contains exactly one filesystem.
-	 * This is the filesystem which will be below the newly created
-	 * filesystem in the stack.
-	 *
-	 * @param args the command line arguments
-	 * @param fs NULL terminated filesystem object vector
-	 * @return the new filesystem object
-	 */
-	struct fuse_fs *(*factory)(struct fuse_args *args,
-				   struct fuse_fs *fs[]);
-
-	struct fuse_module *next;
-	struct fusemod_so *so;
-	int ctr;
-};
-
-/**
- * Register a filesystem module
+ * For now the 'fs' vector always contains exactly one filesystem.
+ * This is the filesystem which will be below the newly created
+ * filesystem in the stack.
  *
- * This function is used by FUSE_REGISTER_MODULE and there's usually
- * no need to call it directly
+ * @param args the command line arguments
+ * @param fs NULL terminated filesystem object vector
+ * @return the new filesystem object
  */
-void fuse_register_module(struct fuse_module *mod);
-
+typedef struct fuse_fs *(*fuse_module_factory_t)(struct fuse_args *args,
+						 struct fuse_fs *fs[]);
 /**
  * Register filesystem module
  *
- * For the parameters, see description of the fields in 'struct
- * fuse_module'
+ * If the "-omodules=@name_:..." option is present, filesystem
+ * objects are created and pushed onto the stack with the @factory_
+ * function.
+ *
+ * @name_ the name of this filesystem module
+ * @factory_ the factory function for this filesystem module
  */
-#define FUSE_REGISTER_MODULE(name_, factory_)				  \
-	static __attribute__((constructor)) void name_ ## _register(void) \
-	{								  \
-		static struct fuse_module mod =				  \
-			{ #name_, factory_, NULL, NULL, 0 };		  \
-		fuse_register_module(&mod);				  \
-	}
+#define FUSE_REGISTER_MODULE(name_, factory_) \
+	fuse_module_factory_t fuse_module_ ## name_ ## _factory = factory_;
 
 /** Get session from fuse object */
 struct fuse_session *fuse_get_session(struct fuse *f);
diff --git a/lib/fuse.c b/lib/fuse.c
index 7508c54..3f8e601 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -208,55 +208,90 @@ struct fuse_context_i {
 	fuse_req_t req;
 };
 
+/* Defined by FUSE_REGISTER_MODULE() in lib/modules/subdir.c and iconv.c.  */
+extern fuse_module_factory_t fuse_module_subdir_factory;
+extern fuse_module_factory_t fuse_module_iconv_factory;
+
 static pthread_key_t fuse_context_key;
 static pthread_mutex_t fuse_context_lock = PTHREAD_MUTEX_INITIALIZER;
 static int fuse_context_ref;
-static struct fusemod_so *fuse_current_so;
-static struct fuse_module *fuse_modules;
+static struct fuse_module *fuse_modules = NULL;
 
-static int fuse_load_so_name(const char *soname)
+static int fuse_register_module(const char *name,
+				fuse_module_factory_t factory,
+				struct fusemod_so *so)
 {
-	struct fusemod_so *so;
+	struct fuse_module *mod;
 
-	so = calloc(1, sizeof(struct fusemod_so));
-	if (!so) {
-		fprintf(stderr, "fuse: memory allocation failed\n");
+	mod = calloc(1, sizeof(struct fuse_module));
+	if (!mod) {
+		fprintf(stderr, "fuse: failed to allocate module\n");
 		return -1;
 	}
-
-	fuse_current_so = so;
-	so->handle = dlopen(soname, RTLD_NOW);
-	fuse_current_so = NULL;
-	if (!so->handle) {
-		fprintf(stderr, "fuse: %s\n", dlerror());
-		goto err;
-	}
-	if (!so->ctr) {
-		fprintf(stderr, "fuse: %s did not register any modules\n",
-			soname);
-		goto err;
+	mod->name = strdup(name);
+	if (!mod->name) {
+		fprintf(stderr, "fuse: failed to allocate module name\n");
+		free(mod);
+		return -1;
 	}
-	return 0;
+	mod->factory = factory;
+	mod->ctr = 0;
+	mod->so = so;
+	if (mod->so)
+		mod->so->ctr++;
+	mod->next = fuse_modules;
+	fuse_modules = mod;
 
-err:
-	if (so->handle)
-		dlclose(so->handle);
-	free(so);
-	return -1;
+	return 0;
 }
 
+
 static int fuse_load_so_module(const char *module)
 {
-	int res;
-	char *soname = malloc(strlen(module) + 64);
-	if (!soname) {
+	int ret = -1;
+	char *tmp;
+	struct fusemod_so *so;
+	fuse_module_factory_t factory;
+
+	tmp = malloc(strlen(module) + 64);
+	if (!tmp) {
 		fprintf(stderr, "fuse: memory allocation failed\n");
 		return -1;
 	}
-	sprintf(soname, "libfusemod_%s.so", module);
-	res = fuse_load_so_name(soname);
-	free(soname);
-	return res;
+	sprintf(tmp, "libfusemod_%s.so", module);
+	so = calloc(1, sizeof(struct fusemod_so));
+	if (!so) {
+		fprintf(stderr, "fuse: failed to allocate module so\n");
+		goto out;
+	}
+
+	so->handle = dlopen(tmp, RTLD_NOW);
+	if (so->handle == NULL) {
+		fprintf(stderr, "fuse: dlopen(%s) failed: %s\n",
+			tmp, dlerror());
+		goto out_free_so;
+	}
+
+	sprintf(tmp, "fuse_module_%s_factory", module);
+	factory = dlsym(so->handle, tmp);
+	if (factory == NULL) {
+		fprintf(stderr, "fuse: symbol <%s> not found in module: %s\n",
+			tmp, dlerror());
+		goto out_dlclose;
+	}
+	ret = fuse_register_module(module, factory, so);
+	if (ret)
+		goto out_dlclose;
+
+out:
+	free(tmp);
+	return ret;
+
+out_dlclose:
+	dlclose(so->handle);
+out_free_so:
+	free(so);
+	goto out;
 }
 
 static struct fuse_module *fuse_find_module(const char *module)
@@ -4342,6 +4377,18 @@ struct fuse *fuse_new(struct fuse_chan *ch, struct fuse_args *args,
 	struct fuse_fs *fs;
 	struct fuse_lowlevel_ops llop = fuse_path_ops;
 
+	pthread_mutex_lock(&fuse_context_lock);
+	static int builtin_modules_registered = 0;
+	/* Have the builtin modules already been registered? */
+	if (builtin_modules_registered == 0) {
+		/* If not, register them. */
+		fuse_register_module("subdir", fuse_module_subdir_factory, NULL);
+		fuse_register_module("iconv", fuse_module_iconv_factory, NULL);
+		builtin_modules_registered= 1;
+	}
+	pthread_mutex_unlock(&fuse_context_lock);
+
+
 	if (fuse_create_context_key() == -1)
 		goto out;
 
@@ -4522,14 +4569,3 @@ void fuse_destroy(struct fuse *f)
 	fuse_delete_context_key();
 }
 
-/* called with fuse_context_lock held or during initialization (before
-   main() has been called) */
-void fuse_register_module(struct fuse_module *mod)
-{
-	mod->ctr = 0;
-	mod->so = fuse_current_so;
-	if (mod->so)
-		mod->so->ctr++;
-	mod->next = fuse_modules;
-	fuse_modules = mod;
-}
diff --git a/lib/fuse_i.h b/lib/fuse_i.h
index 30fe415..4bbcbd6 100644
--- a/lib/fuse_i.h
+++ b/lib/fuse_i.h
@@ -95,6 +95,21 @@ struct fuse_ll {
 	size_t bufsize;
 };
 
+/**
+ * Filesystem module
+ *
+ * Filesystem modules are registered with the FUSE_REGISTER_MODULE()
+ * macro.
+ *
+ */
+struct fuse_module {
+	char *name;
+	fuse_module_factory_t factory;
+	struct fuse_module *next;
+	struct fusemod_so *so;
+	int ctr;
+};
+
 int fuse_chan_clearfd(struct fuse_chan *ch);
 void fuse_chan_close(struct fuse_chan *ch);
 
-- 
2.30.2