summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBernd Schubert <bschubert@ddn.com>2022-04-08 22:30:27 +0200
committerNikolaus Rath <Nikolaus@rath.org>2022-09-04 13:07:15 +0100
commit30a126c5f9009e8ff8e369c563eb941679bec252 (patch)
treeaa27206391ed90acf95a9339b13f1c4202c63105
parent7657ec158becdc59656c59dff40346fefde78cb2 (diff)
downloadfuse-30a126c5f9009e8ff8e369c563eb941679bec252.tar.gz
API update for fuse_loop_config additions
struct fuse_loop_config was passed as a plain struct, without any version identifer. This had two implications 1) Any addition of new parameters required a FUSE_SYMVER for fuse_session_loop_mt() and fuse_loop_mt() as otherwise a read beyond end-of previous struct size might have happened. 2) Filesystems also might have been recompiled and the developer might not have noticed the struct extensions and unexpected for the developer (or people recomliling the code) uninitialized parameters would have been passed. Code is updated to have struct fuse_loop_config as an opaque/private data type for file systems that want version 312 (FUSE_MAKE_VERSION(3, 12)). The deprecated fuse_loop_config_v1 is visible, but should not be used outside of internal conversion functions File systems that want version >= 32 < 312 get the previous struct (through ifdefs) and the #define of fuse_loop_mt and fuse_session_loop_mt ensures that these recompiled file systems call into the previous API, which then converts the struct. This is similar to existing compiled applications when just libfuse updated, but binaries it is solved with the FUSE_SYMVER ABI compact declarations. Signed-off-by: Bernd Schubert <bschubert@ddn.com>
-rw-r--r--include/fuse.h3
-rw-r--r--include/fuse_common.h45
-rw-r--r--include/fuse_lowlevel.h41
-rw-r--r--lib/cuse_lowlevel.c7
-rw-r--r--lib/fuse.c40
-rw-r--r--lib/fuse_i.h56
-rw-r--r--lib/fuse_loop_mt.c93
-rw-r--r--lib/fuse_misc.h3
-rw-r--r--lib/fuse_versionscript14
-rw-r--r--lib/helper.c18
-rw-r--r--lib/meson.build2
-rw-r--r--meson.build2
-rw-r--r--util/meson.build2
13 files changed, 278 insertions, 48 deletions
diff --git a/include/fuse.h b/include/fuse.h
index 1a2f841..917a91c 100644
--- a/include/fuse.h
+++ b/include/fuse.h
@@ -1011,6 +1011,9 @@ void fuse_exit(struct fuse *f);
#if FUSE_USE_VERSION < 32
int fuse_loop_mt_31(struct fuse *f, int clone_fd);
#define fuse_loop_mt(f, clone_fd) fuse_loop_mt_31(f, clone_fd)
+#elif FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
+int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config *config);
+#define fuse_loop_mt(f, config) fuse_loop_mt_32(f, config)
#else
/**
* FUSE event loop with multiple threads
diff --git a/include/fuse_common.h b/include/fuse_common.h
index 3f7260c..b40814f 100644
--- a/include/fuse_common.h
+++ b/include/fuse_common.h
@@ -23,7 +23,7 @@
#define FUSE_MAJOR_VERSION 3
/** Minor version of FUSE library interface */
-#define FUSE_MINOR_VERSION 11
+#define FUSE_MINOR_VERSION 12
#define FUSE_MAKE_VERSION(maj, min) ((maj) * 100 + (min))
#define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION)
@@ -104,11 +104,20 @@ struct fuse_file_info {
uint32_t poll_events;
};
+
+
/**
* Configuration parameters passed to fuse_session_loop_mt() and
* fuse_loop_mt().
+ * Deprecated and replaced by a newer private struct in FUSE API
+ * version 312 (FUSE_MAKE_VERSION(3, 12)
*/
+#if FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
+struct fuse_loop_config_v1; /* forward declarition */
struct fuse_loop_config {
+#else
+struct fuse_loop_config_v1 {
+#endif
/**
* whether to use separate device fds for each thread
* (may increase performance)
@@ -128,6 +137,7 @@ struct fuse_loop_config {
unsigned int max_idle_threads;
};
+
/**************************************************************************
* Capability bits for 'fuse_conn_info.capable' and 'fuse_conn_info.want' *
**************************************************************************/
@@ -834,6 +844,39 @@ int fuse_set_signal_handlers(struct fuse_session *se);
*/
void fuse_remove_signal_handlers(struct fuse_session *se);
+/**
+ * Create and set default config for fuse_session_loop_mt and fuse_loop_mt.
+ *
+ * @return anonymous config struct
+ */
+struct fuse_loop_config *fuse_loop_cfg_create(void);
+
+/**
+ * Free the config data structure
+ */
+void fuse_loop_cfg_destroy(struct fuse_loop_config *config);
+
+/**
+ * fuse_loop_config2 setter to set the number of max idle threads.
+ */
+void fuse_loop_cfg_set_idle_threads(struct fuse_loop_config *config,
+ unsigned int value);
+
+/**
+ * fuse_loop_config2 setter to enable the clone_fd feature
+ */
+void fuse_loop_cfg_set_clone_fd(struct fuse_loop_config *config,
+ unsigned int value);
+
+/**
+ * Convert old config to more recernt fuse_loop_config2
+ *
+ * @param config current config2 type
+ * @param v1_conf older config1 type (below FUSE API 312)
+ */
+void fuse_loop_cfg_convert(struct fuse_loop_config *config,
+ struct fuse_loop_config_v1 *v1_conf);
+
/* ----------------------------------------------------------- *
* Compatibility stuff *
* ----------------------------------------------------------- */
diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h
index d73e9fa..378742d 100644
--- a/include/fuse_lowlevel.h
+++ b/include/fuse_lowlevel.h
@@ -1968,26 +1968,29 @@ int fuse_session_mount(struct fuse_session *se, const char *mountpoint);
int fuse_session_loop(struct fuse_session *se);
#if FUSE_USE_VERSION < 32
-int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd);
-#define fuse_session_loop_mt(se, clone_fd) fuse_session_loop_mt_31(se, clone_fd)
+ int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd);
+ #define fuse_session_loop_mt(se, clone_fd) fuse_session_loop_mt_31(se, clone_fd)
+#elif FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
+ int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config *config);
+ #define fuse_session_loop_mt(se, config) fuse_session_loop_mt_32(se, config)
#else
-#if (!defined(__UCLIBC__) && !defined(__APPLE__))
-/**
- * Enter a multi-threaded event loop.
- *
- * For a description of the return value and the conditions when the
- * event loop exits, refer to the documentation of
- * fuse_session_loop().
- *
- * @param se the session
- * @param config session loop configuration
- * @return see fuse_session_loop()
- */
-int fuse_session_loop_mt(struct fuse_session *se, struct fuse_loop_config *config);
-#else
-int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config *config);
-#define fuse_session_loop_mt(se, config) fuse_session_loop_mt_32(se, config)
-#endif
+ #if (!defined(__UCLIBC__) && !defined(__APPLE__))
+ /**
+ * Enter a multi-threaded event loop.
+ *
+ * For a description of the return value and the conditions when the
+ * event loop exits, refer to the documentation of
+ * fuse_session_loop().
+ *
+ * @param se the session
+ * @param config session loop configuration
+ * @return see fuse_session_loop()
+ */
+ int fuse_session_loop_mt(struct fuse_session *se, struct fuse_loop_config *config);
+ #else
+ int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config);
+ #define fuse_session_loop_mt(se, config) fuse_session_loop_mt_312(se, config)
+ #endif
#endif
/**
diff --git a/lib/cuse_lowlevel.c b/lib/cuse_lowlevel.c
index b70947e..01a62ab 100644
--- a/lib/cuse_lowlevel.c
+++ b/lib/cuse_lowlevel.c
@@ -351,10 +351,9 @@ int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci,
return 1;
if (multithreaded) {
- struct fuse_loop_config config;
- config.clone_fd = 0;
- config.max_idle_threads = 10;
- res = fuse_session_loop_mt_32(se, &config);
+ struct fuse_loop_config *config = fuse_loop_cfg_create();
+ res = fuse_session_loop_mt(se, config);
+ fuse_loop_cfg_destroy(config);
}
else
res = fuse_session_loop(se);
diff --git a/lib/fuse.c b/lib/fuse.c
index 1924caa..17850e3 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -4577,8 +4577,8 @@ int fuse_loop(struct fuse *f)
return fuse_session_loop(f->se);
}
-FUSE_SYMVER("fuse_loop_mt_32", "fuse_loop_mt@@FUSE_3.2")
-int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config *config)
+FUSE_SYMVER("fuse_loop_mt_312", "fuse_loop_mt@@FUSE_3.12")
+int fuse_loop_mt_312(struct fuse *f, struct fuse_loop_config *config)
{
if (f == NULL)
return -1;
@@ -4587,19 +4587,45 @@ int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config *config)
if (res)
return -1;
- res = fuse_session_loop_mt_32(fuse_get_session(f), config);
+ res = fuse_session_loop_mt_312(fuse_get_session(f), config);
fuse_stop_cleanup_thread(f);
return res;
}
+int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config_v1 *config_v1);
+FUSE_SYMVER("fuse_loop_mt_32", "fuse_loop_mt@FUSE_3.2")
+int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config_v1 *config_v1)
+{
+ struct fuse_loop_config *config = fuse_loop_cfg_create();
+ if (config == NULL)
+ return ENOMEM;
+
+ fuse_loop_cfg_convert(config, config_v1);
+
+ int res = fuse_loop_mt_312(f, config);
+
+ fuse_loop_cfg_destroy(config);
+
+ return res;
+}
+
int fuse_loop_mt_31(struct fuse *f, int clone_fd);
FUSE_SYMVER("fuse_loop_mt_31", "fuse_loop_mt@FUSE_3.0")
int fuse_loop_mt_31(struct fuse *f, int clone_fd)
{
- struct fuse_loop_config config;
- config.clone_fd = clone_fd;
- config.max_idle_threads = 10;
- return fuse_loop_mt_32(f, &config);
+ int err;
+ struct fuse_loop_config *config = fuse_loop_cfg_create();
+
+ if (config == NULL)
+ return ENOMEM;
+
+ fuse_loop_cfg_set_clone_fd(config, clone_fd);
+
+ err = fuse_loop_mt_312(f, config);
+
+ fuse_loop_cfg_destroy(config);
+
+ return err;
}
void fuse_exit(struct fuse *f)
diff --git a/lib/fuse_i.h b/lib/fuse_i.h
index d38b630..6930a20 100644
--- a/lib/fuse_i.h
+++ b/lib/fuse_i.h
@@ -87,6 +87,50 @@ struct fuse_module {
int ctr;
};
+/**
+ * Configuration parameters passed to fuse_session_loop_mt() and
+ * fuse_loop_mt().
+ *
+ * Internal API to avoid exposing the plain data structure and
+ * causing compat issues after adding or removing struct members.
+ *
+ */
+#if FUSE_USE_VERSION >= FUSE_MAKE_VERSION(3, 12)
+struct fuse_loop_config
+{
+ /* verififier that a correct struct was was passed. This is especially
+ * needed, as versions below (3, 12) were using a public struct
+ * (now called fuse_loop_config_v1), which was hard to extend with
+ * additional parameters, without risking that file system implementations
+ * would not have noticed and might either pass uninitialized members
+ * or even too small structs.
+ * fuse_loop_config_v1 has clone_fd at this offset, which should be either 0
+ * or 1. v2 or even higher version just need to set a value here
+ * which not conflicting and very unlikely as having been set by
+ * file system implementation.
+ */
+ int version_id;
+
+ /**
+ * whether to use separate device fds for each thread
+ * (may increase performance)
+ */
+ int clone_fd;
+ /**
+ * The maximum number of available worker threads before they
+ * start to get deleted when they become idle. If not
+ * specified, the default is 10.
+ *
+ * Adjusting this has performance implications; a very small number
+ * of threads in the pool will cause a lot of thread creation and
+ * deletion overhead and performance may suffer. When set to 0, a new
+ * thread will be created to service every operation.
+ * The special value of -1 means that this parameter is disabled.
+ */
+ int max_idle_threads;
+};
+#endif
+
/* ----------------------------------------------------------- *
* Channel interface (when using -o clone_fd) *
* ----------------------------------------------------------- */
@@ -128,8 +172,16 @@ void fuse_session_process_buf_int(struct fuse_session *se,
struct fuse *fuse_new_31(struct fuse_args *args, const struct fuse_operations *op,
size_t op_size, void *private_data);
-int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config *config);
-int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config *config);
+int fuse_loop_mt_312(struct fuse *f, struct fuse_loop_config *config);
+int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config);
+
+/**
+ * Internal verifier for the given config.
+ *
+ * @return negative standard error code or 0 on success
+ */
+int fuse_loop_cfg_verify(struct fuse_loop_config *config);
+
#define FUSE_MAX_MAX_PAGES 256
#define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32
diff --git a/lib/fuse_loop_mt.c b/lib/fuse_loop_mt.c
index 8fcc46c..2f0470b 100644
--- a/lib/fuse_loop_mt.c
+++ b/lib/fuse_loop_mt.c
@@ -24,10 +24,16 @@
#include <sys/time.h>
#include <sys/ioctl.h>
#include <assert.h>
+#include <limits.h>
/* Environment var controlling the thread stack size */
#define ENVNAME_THREAD_STACK "FUSE_THREAD_STACK"
+#define FUSE_LOOP_MT_V2_IDENTIFIER INT_MAX - 2
+#define FUSE_LOOP_MT_DEF_CLONE_FD 0
+#define FUSE_LOOP_MT_DEF_IDLE_THREADS -1 /* thread destruction is disabled
+ * by default */
+
struct fuse_worker {
struct fuse_worker *prev;
struct fuse_worker *next;
@@ -303,13 +309,18 @@ static void fuse_join_worker(struct fuse_mt *mt, struct fuse_worker *w)
free(w);
}
-FUSE_SYMVER("fuse_session_loop_mt_32", "fuse_session_loop_mt@@FUSE_3.2")
-int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config *config)
+int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config);
+FUSE_SYMVER("fuse_session_loop_mt_312", "fuse_session_loop_mt@@FUSE_3.12")
+int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config)
{
- int err;
+int err;
struct fuse_mt mt;
struct fuse_worker *w;
+ err = fuse_loop_cfg_verify(config);
+ if (err)
+ return err;
+
memset(&mt, 0, sizeof(struct fuse_mt));
mt.se = se;
mt.clone_fd = config->clone_fd;
@@ -347,15 +358,83 @@ int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config *co
if(se->error != 0)
err = se->error;
fuse_session_reset(se);
+
+ return err;
+}
+
+int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config_v1 *config_v1);
+FUSE_SYMVER("fuse_session_loop_mt_32", "fuse_session_loop_mt@FUSE_3.2")
+int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config_v1 *config_v1)
+{
+ int err;
+
+ struct fuse_loop_config *config = fuse_loop_cfg_create();
+ if (config == NULL)
+ return ENOMEM;
+
+ fuse_loop_cfg_convert(config, config_v1);
+
+ err = fuse_session_loop_mt_312(se, config);
+
+ fuse_loop_cfg_destroy(config);
+
return err;
}
+
int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd);
FUSE_SYMVER("fuse_session_loop_mt_31", "fuse_session_loop_mt@FUSE_3.0")
int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd)
{
- struct fuse_loop_config config;
- config.clone_fd = clone_fd;
- config.max_idle_threads = 10;
- return fuse_session_loop_mt_32(se, &config);
+ struct fuse_loop_config *config = fuse_loop_cfg_create();
+ if (clone_fd > 0)
+ fuse_loop_cfg_set_clone_fd(config, clone_fd);
+ return fuse_session_loop_mt_312(se, config);
+}
+
+struct fuse_loop_config *fuse_loop_cfg_create(void)
+{
+ struct fuse_loop_config *config = calloc(1, sizeof(*config));
+ if (config == NULL)
+ return NULL;
+
+ config->version_id = FUSE_LOOP_MT_V2_IDENTIFIER;
+ config->max_idle_threads = FUSE_LOOP_MT_DEF_IDLE_THREADS;
+ config->clone_fd = FUSE_LOOP_MT_DEF_CLONE_FD;
+
+ return config;
+}
+
+void fuse_loop_cfg_destroy(struct fuse_loop_config *config)
+{
+ free(config);
+}
+
+int fuse_loop_cfg_verify(struct fuse_loop_config *config)
+{
+ if (config->version_id != FUSE_LOOP_MT_V2_IDENTIFIER)
+ return -EINVAL;
+
+ return 0;
+}
+
+void fuse_loop_cfg_convert(struct fuse_loop_config *config,
+ struct fuse_loop_config_v1 *v1_conf)
+{
+ fuse_loop_cfg_set_idle_threads(config, v1_conf->max_idle_threads);
+
+ fuse_loop_cfg_set_clone_fd(config, v1_conf->clone_fd);
+}
+
+void fuse_loop_cfg_set_idle_threads(struct fuse_loop_config *config,
+ unsigned int value)
+{
+ config->max_idle_threads = value;
}
+
+void fuse_loop_cfg_set_clone_fd(struct fuse_loop_config *config,
+ unsigned int value)
+{
+ config->clone_fd = value;
+}
+
diff --git a/lib/fuse_misc.h b/lib/fuse_misc.h
index f956ab7..e2e9ba5 100644
--- a/lib/fuse_misc.h
+++ b/lib/fuse_misc.h
@@ -11,6 +11,9 @@
/*
Versioned symbols cannot be used in some cases because it
- not supported on MacOSX (in MachO binary format)
+
+ Note: "@@" denotes the default symbol, "@" is binary a compat version.
+
*/
#ifndef __APPLE__
# if HAVE_SYMVER_ATTRIBUTE
diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript
index a06f768..aff7e84 100644
--- a/lib/fuse_versionscript
+++ b/lib/fuse_versionscript
@@ -168,6 +168,20 @@ FUSE_3.7 {
fuse_log;
} FUSE_3.4;
+FUSE_3.12 {
+ global:
+ fuse_session_loop_mt;
+ fuse_session_loop_mt_312;
+ fuse_loop_mt;
+ fuse_loop_mt_32;
+ fuse_loop_mt_312;
+ fuse_loop_cfg_create;
+ fuse_loop_cfg_destroy;
+ fuse_loop_cfg_set_idle_threads;
+ fuse_loop_cfg_set_clone_fd;
+ fuse_loop_cfg_convert;
+} FUSE_3.4;
+
# Local Variables:
# indent-tabs-mode: t
# End:
diff --git a/lib/helper.c b/lib/helper.c
index 64ff7ad..fc6a6ee 100644
--- a/lib/helper.c
+++ b/lib/helper.c
@@ -283,6 +283,7 @@ int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
struct fuse *fuse;
struct fuse_cmdline_opts opts;
int res;
+ struct fuse_loop_config *loop_config = NULL;
if (fuse_parse_cmdline(&args, &opts) != 0)
return 1;
@@ -338,13 +339,19 @@ int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
if (opts.singlethread)
res = fuse_loop(fuse);
else {
- struct fuse_loop_config loop_config;
- loop_config.clone_fd = opts.clone_fd;
- loop_config.max_idle_threads = opts.max_idle_threads;
- res = fuse_loop_mt_32(fuse, &loop_config);
+ loop_config = fuse_loop_cfg_create();
+ if (loop_config == NULL) {
+ res = 7;
+ goto out3;
+ }
+
+ fuse_loop_cfg_set_clone_fd(loop_config, opts.clone_fd);
+
+ fuse_loop_cfg_set_idle_threads(loop_config, opts.max_idle_threads);
+ res = fuse_loop_mt(fuse, loop_config);
}
if (res)
- res = 7;
+ res = 8;
fuse_remove_signal_handlers(se);
out3:
@@ -352,6 +359,7 @@ out3:
out2:
fuse_destroy(fuse);
out1:
+ fuse_loop_cfg_destroy(loop_config);
free(opts.mountpoint);
fuse_opt_free_args(&args);
return res;
diff --git a/lib/meson.build b/lib/meson.build
index 98461d8..ef0e11e 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -37,7 +37,7 @@ libfuse = library('fuse3', libfuse_sources, version: meson.project_version(),
soversion: '3', include_directories: include_dirs,
dependencies: deps, install: true,
link_depends: 'fuse_versionscript',
- c_args: [ '-DFUSE_USE_VERSION=35',
+ c_args: [ '-DFUSE_USE_VERSION=312',
'-DFUSERMOUNT_DIR="@0@"'.format(fusermount_path) ],
link_args: ['-Wl,--version-script,' + meson.current_source_dir()
+ '/fuse_versionscript' ])
diff --git a/meson.build b/meson.build
index 342dee1..3cef64f 100644
--- a/meson.build
+++ b/meson.build
@@ -1,4 +1,4 @@
-project('libfuse3', ['c'], version: '3.11.0',
+project('libfuse3', ['c'], version: '3.12.0',
meson_version: '>= 0.42',
default_options: [
'buildtype=debugoptimized',
diff --git a/util/meson.build b/util/meson.build
index 577668f..7846870 100644
--- a/util/meson.build
+++ b/util/meson.build
@@ -11,7 +11,7 @@ executable('mount.fuse3', ['mount.fuse.c'],
link_with: [ libfuse ],
install: true,
install_dir: get_option('sbindir'),
- c_args: '-DFUSE_USE_VERSION=35')
+ c_args: '-DFUSE_USE_VERSION=312')
udevrulesdir = get_option('udevrulesdir')