Add in the libfuse version a program was compiled with (#942)
authorBernd Schubert <bernd.schubert@fastmail.fm>
Mon, 13 May 2024 10:32:06 +0000 (12:32 +0200)
committerGitHub <noreply@github.com>
Mon, 13 May 2024 10:32:06 +0000 (12:32 +0200)
The API stays the same, the libfuse version comes from
inlined functions, which are defined fuse_lowlevel.h
and fuse.h. As these inlined functions are defined in the header
files they get added into the application, similar as if these
were preprocessor macros.
Macro vs inlined function is then just a style issue - I personally
prefer the latter.

fuse_session_new() -> static inlinei, in the application
_fuse_session_new -> inside of libfuse

fuse_new() -> static inline, in the application
_fuse_new() -> inside of libfuse

Note: Entirely untested is the fuse 30 api - we need a test
for it. And we do not have any ABI tests at all.

Signed-off-by: Bernd Schubert <bernd.schubert@fastmail.fm>
include/fuse.h
include/fuse_common.h
include/fuse_lowlevel.h
lib/fuse.c
lib/fuse_i.h
lib/fuse_lowlevel.c
lib/fuse_versionscript
lib/helper.c

index 90ee4bbaa8673661ebfa1e9d4ba30f3f04fbfc37..a3549cb19e3bd06e45fd905c4e109bd574cbbb07 100644 (file)
@@ -854,6 +854,22 @@ struct fuse_context {
        mode_t umask;
 };
 
+#if (defined(LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS))
+/**
+ * The real main function
+ *
+ * Do not call this directly, use fuse_main()
+ */
+int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
+                  size_t op_size, struct libfuse_version *version,
+                  void *user_data);
+#else
+int fuse_main_real_317(int argc, char *argv[], const struct fuse_operations *op,
+                  size_t op_size, struct libfuse_version *version, void *user_data);
+#define fuse_main_real(argc, argv, op, op_size, version, user_data) \
+       fuse_main_real_317(argc, argv, op, op_size, version, user_data);
+#endif
+
 /**
  * Main function of FUSE.
  *
@@ -908,12 +924,19 @@ struct fuse_context {
  *
  * Example usage, see hello.c
  */
-/*
-  int fuse_main(int argc, char *argv[], const struct fuse_operations *op,
-  void *private_data);
-*/
-#define fuse_main(argc, argv, op, private_data)                                \
-       fuse_main_real(argc, argv, op, sizeof(*(op)), private_data)
+static inline int
+fuse_main(int argc, char *argv[], const struct fuse_operations *op,
+         void *user_data)
+{
+       struct libfuse_version version = {
+               .major  = FUSE_MAJOR_VERSION,
+               .minor  = FUSE_MINOR_VERSION,
+               .hotfix = FUSE_HOTFIX_VERSION,
+               .padding = 0
+       };
+       return fuse_main_real(argc, argv, op, sizeof(*(op)), &version,
+                             user_data);
+}
 
 /* ----------------------------------------------------------- *
  * More detailed API                                          *
@@ -932,6 +955,11 @@ struct fuse_context {
  */
 void fuse_lib_help(struct fuse_args *args);
 
+struct fuse *_fuse_new(struct fuse_args *args,
+                      const struct fuse_operations *op,
+                      size_t op_size, struct libfuse_version *version,
+                      void *user_data);
+
 /**
  * Create a new FUSE filesystem.
  *
@@ -960,18 +988,60 @@ void fuse_lib_help(struct fuse_args *args);
  * @return the created FUSE handle
  */
 #if FUSE_USE_VERSION == 30
-struct fuse *fuse_new_30(struct fuse_args *args, const struct fuse_operations *op,
-                        size_t op_size, void *private_data);
-#define fuse_new(args, op, size, data) fuse_new_30(args, op, size, data)
+struct fuse *_fuse_new_30(struct fuse_args *args,
+                        const struct fuse_operations *op,
+                        size_t op_size, void *user_data);
+static inline struct fuse *
+fuse_new(struct fuse_args *args,
+        const struct fuse_operations *op, size_t op_size,
+        void *user_data)
+{
+       struct libfuse_version version = {
+               .major = FUSE_MAJOR_VERSION,
+               .minor = FUSE_MINOR_VERSION,
+               .hotfix = FUSE_HOTFIX_VERSION,
+               .padding = 0
+       };
+
+       return _fuse_new_30(args, op, op_size, &version, user_data);
+}
 #else
 #if (defined(LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS))
-struct fuse *fuse_new(struct fuse_args *args, const struct fuse_operations *op,
-                     size_t op_size, void *private_data);
+static inline struct fuse *
+fuse_new(struct fuse_args *args,
+        const struct fuse_operations *op, size_t op_size,
+        void *user_data)
+{
+       struct libfuse_version version = {
+               .major = FUSE_MAJOR_VERSION,
+               .minor = FUSE_MINOR_VERSION,
+               .hotfix = FUSE_HOTFIX_VERSION,
+               .padding = 0
+       };
+
+       return _fuse_new(args, op, op_size, &version, user_data);
+}
 #else /* LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS */
-struct fuse *fuse_new_31(struct fuse_args *args,
-                     const struct fuse_operations *op,
-                     size_t op_size, void *private_data);
-#define fuse_new(args, op, size, data) fuse_new_31(args, op, size, data)
+struct fuse *_fuse_new_317(struct fuse_args *args,
+                      const struct fuse_operations *op, size_t op_size,
+                     struct libfuse_version *version,
+                     void *private_data);
+#define _fuse_new(args, op, size, version, data) \
+       _fuse_new_317(args, op, size, version, data)
+static inline struct fuse *
+fuse_new(struct fuse_args *args,
+        const struct fuse_operations *op, size_t op_size,
+        void *user_data)
+{
+       struct libfuse_version version = {
+               .major = FUSE_MAJOR_VERSION,
+               .minor = FUSE_MINOR_VERSION,
+               .hotfix = FUSE_HOTFIX_VERSION,
+               .padding = 0
+       };
+
+       return _fuse_new(args, op, op_size, &version, user_data);
+}
 #endif /* LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS */
 #endif
 
@@ -1127,14 +1197,6 @@ int fuse_interrupted(void);
  */
 int fuse_invalidate_path(struct fuse *f, const char *path);
 
-/**
- * The real main function
- *
- * Do not call this directly, use fuse_main()
- */
-int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
-                  size_t op_size, void *private_data);
-
 /**
  * Start the cleanup thread when using option "remember".
  *
index 8803b68177e7502ab3c808581c63237fd2ab1f9d..a614fb0af8dde477df3c69335624fcd8fe1f1d1a 100644 (file)
@@ -836,6 +836,18 @@ struct fuse_bufvec {
        struct fuse_buf buf[1];
 };
 
+/**
+ * libfuse version a file system was compiled with. Should be filled in from
+ * defines in 'libfuse_config.h'
+ */
+struct libfuse_version
+{
+       int major;
+       int minor;
+       int hotfix;
+       int padding;
+};
+
 /* Initialize bufvec with a single buffer of given size */
 #define FUSE_BUFVEC_INIT(size__)                               \
        ((struct fuse_bufvec) {                                 \
index 8ff111a70a5b3a5359678ff967a42178c9331d03..2ada62b06e58db9bd4b1213ccf5063de1f4e41f5 100644 (file)
@@ -2005,6 +2005,36 @@ int fuse_parse_cmdline_312(struct fuse_args *args,
 #endif
 #endif
 
+/*
+ * This should mostly not be called directly, but instead the fuse_session_new()
+ * macro should be used, which fills in the libfuse version compilation
+ * is done against automatically.
+ */
+struct fuse_session *_fuse_session_new_317(struct fuse_args *args,
+                                         const struct fuse_lowlevel_ops *op,
+                                         size_t op_size,
+                                         struct libfuse_version *version,
+                                         void *userdata);
+
+/* Do not call this directly, but only through fuse_session_new() */
+#if (defined(LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS))
+struct fuse_session *
+_fuse_session_new(struct fuse_args *args,
+                const struct fuse_lowlevel_ops *op,
+                size_t op_size,
+                struct libfuse_version *version,
+                void *userdata);
+#else
+struct fuse_session *
+_fuse_session_new_317(struct fuse_args *args,
+                     const struct fuse_lowlevel_ops *op,
+                     size_t op_size,
+                     struct libfuse_version *version,
+                     void *userdata);
+#define _fuse_session_new(args, op, op_size, version, userdata)        \
+       _fuse_session_new_317(args, op, op_size, version, userdata)
+#endif
+
 /**
  * Create a low level session.
  *
@@ -2029,13 +2059,25 @@ int fuse_parse_cmdline_312(struct fuse_args *args,
  * @param args argument vector
  * @param op the (low-level) filesystem operations
  * @param op_size sizeof(struct fuse_lowlevel_ops)
+ * @param version the libfuse version a file system server was compiled against
  * @param userdata user data
- *
  * @return the fuse session on success, NULL on failure
  **/
-struct fuse_session *fuse_session_new(struct fuse_args *args,
-                                     const struct fuse_lowlevel_ops *op,
-                                     size_t op_size, void *userdata);
+static inline struct fuse_session *
+fuse_session_new(struct fuse_args *args,
+                const struct fuse_lowlevel_ops *op,
+                size_t op_size,
+                void *userdata)
+{
+       struct libfuse_version version = {
+               .major = FUSE_MAJOR_VERSION,
+               .minor = FUSE_MINOR_VERSION,
+               .hotfix = FUSE_HOTFIX_VERSION,
+               .padding = 0
+       };
+
+       return _fuse_session_new(args, op, op_size, &version, userdata);
+}
 
 /**
  * Set a file descriptor for the session.
index f619c0003a9530c549084a1481e211d325893b3c..861716890a582b61e267ccc50d43929f4c2d407d 100644 (file)
@@ -4890,11 +4890,19 @@ void fuse_stop_cleanup_thread(struct fuse *f)
        }
 }
 
-
-FUSE_SYMVER("fuse_new_31", "fuse_new@@FUSE_3.1")
-struct fuse *fuse_new_31(struct fuse_args *args,
-                     const struct fuse_operations *op,
-                     size_t op_size, void *user_data)
+/*
+ * Not supposed to be called directly, but supposed to be called
+ * through the fuse_new macro
+ */
+struct fuse *_fuse_new_317(struct fuse_args *args,
+                          const struct fuse_operations *op,
+                          size_t op_size, struct libfuse_version *version,
+                          void *user_data);
+FUSE_SYMVER("_fuse_new_317", "_fuse_new@@FUSE_3.17")
+struct fuse *_fuse_new_317(struct fuse_args *args,
+                          const struct fuse_operations *op,
+                          size_t op_size, struct libfuse_version *version,
+                          void *user_data)
 {
        struct fuse *f;
        struct node *root;
@@ -4976,7 +4984,7 @@ struct fuse *fuse_new_31(struct fuse_args *args,
        f->conf.readdir_ino = 1;
 #endif
 
-       f->se = fuse_session_new(args, &llop, sizeof(llop), f);
+       f->se = _fuse_session_new(args, &llop, sizeof(llop), version, f);
        if (f->se == NULL)
                goto out_free_fs;
 
@@ -5041,16 +5049,62 @@ out:
 }
 
 /* Emulates 3.0-style fuse_new(), which processes --help */
+struct fuse *_fuse_new_30(struct fuse_args *args, const struct fuse_operations *op,
+                        size_t op_size,
+                        struct libfuse_version *version,
+                        void *user_data);
+FUSE_SYMVER("_fuse_new_30", "_fuse_new@FUSE_3.0")
+struct fuse *_fuse_new_30(struct fuse_args *args,
+                        const struct fuse_operations *op,
+                        size_t op_size,
+                        struct libfuse_version *version,
+                        void *user_data)
+{
+       struct fuse_config conf = {0};
+
+       const struct fuse_opt opts[] = {
+               FUSE_LIB_OPT("-h", show_help, 1),
+               FUSE_LIB_OPT("--help", show_help, 1),
+               FUSE_OPT_END
+       };
+
+       if (fuse_opt_parse(args, &conf, opts,
+                          fuse_lib_opt_proc) == -1)
+               return NULL;
+
+       if (conf.show_help) {
+               fuse_lib_help(args);
+               return NULL;
+       } else
+               return _fuse_new_317(args, op, op_size, version, user_data);
+}
+
+/* ABI compat version */
+struct fuse *fuse_new_31(struct fuse_args *args, const struct fuse_operations *op,
+                        size_t op_size, void *user_data);
+FUSE_SYMVER("fuse_new_31", "fuse_new@FUSE_3.1")
+struct fuse *fuse_new_31(struct fuse_args *args,
+                        const struct fuse_operations *op,
+                        size_t op_size, void *user_data)
+{
+               /* unknown version */
+       struct libfuse_version version = { 0 };
+
+       return _fuse_new_317(args, op, op_size, &version, user_data);
+}
+
+/*
+ * ABI compat version
+ * Emulates 3.0-style fuse_new(), which processes --help
+ */
 struct fuse *fuse_new_30(struct fuse_args *args, const struct fuse_operations *op,
-                        size_t op_size, void *private_data);
+                        size_t op_size, void *user_data);
 FUSE_SYMVER("fuse_new_30", "fuse_new@FUSE_3.0")
 struct fuse *fuse_new_30(struct fuse_args *args,
                         const struct fuse_operations *op,
                         size_t op_size, void *user_data)
 {
-       struct fuse_config conf;
-
-       memset(&conf, 0, sizeof(conf));
+       struct fuse_config conf = {0};
 
        const struct fuse_opt opts[] = {
                FUSE_LIB_OPT("-h", show_help, 1),
@@ -5069,6 +5123,7 @@ struct fuse *fuse_new_30(struct fuse_args *args,
                return fuse_new_31(args, op, op_size, user_data);
 }
 
+
 void fuse_destroy(struct fuse *f)
 {
        size_t i;
index 74cfe36fb8101ea8ec086f782fc46a86803af62f..7a2ae0465b9e3392c43705d9f7a7eaaeca5705af 100644 (file)
@@ -65,6 +65,11 @@ struct fuse_session {
        struct fuse_notify_req notify_list;
        size_t bufsize;
        int error;
+
+       /* This is useful if any kind of ABI incompatibility is found at
+        * a later version, to 'fix' it at run time.
+        */
+       struct libfuse_version version;
 };
 
 struct fuse_chan {
index 46aef98815a722d78907a027bbaf7fdb5017c898..c08c99cd579dd6b956b73d46ae9fbd16279d61a6 100644 (file)
@@ -3024,9 +3024,12 @@ restart:
        return res;
 }
 
-struct fuse_session *fuse_session_new(struct fuse_args *args,
-                                     const struct fuse_lowlevel_ops *op,
-                                     size_t op_size, void *userdata)
+FUSE_SYMVER("_fuse_session_new_317", "_fuse_session_new@@FUSE_3.17")
+struct fuse_session *_fuse_session_new_317(struct fuse_args *args,
+                                         const struct fuse_lowlevel_ops *op,
+                                         size_t op_size,
+                                         struct libfuse_version *version,
+                                         void *userdata)
 {
        int err;
        struct fuse_session *se;
@@ -3105,6 +3108,14 @@ struct fuse_session *fuse_session_new(struct fuse_args *args,
        se->userdata = userdata;
 
        se->mo = mo;
+
+       /* Fuse server application should pass the version it was compiled
+        * against and pass it. If a libfuse version accidentally introduces an
+        * ABI incompatibility, it might be possible to 'fix' that at run time,
+        * by checking the version numbers.
+        */
+       se->version = *version;
+
        return se;
 
 out5:
@@ -3120,6 +3131,22 @@ out1:
        return NULL;
 }
 
+struct fuse_session *fuse_session_new_30(struct fuse_args *args,
+                                         const struct fuse_lowlevel_ops *op,
+                                         size_t op_size,
+                                         void *userdata);
+FUSE_SYMVER("fuse_session_new_30", "fuse_session_new@FUSE_3.0")
+struct fuse_session *fuse_session_new_30(struct fuse_args *args,
+                                         const struct fuse_lowlevel_ops *op,
+                                         size_t op_size,
+                                         void *userdata)
+{
+       /* unknown version */
+       struct libfuse_version version = { 0 };
+
+       return _fuse_session_new_317(args, op, op_size, &version, userdata);
+}
+
 int fuse_session_custom_io(struct fuse_session *se, const struct fuse_custom_io *io,
                           int fd)
 {
index c0d58bfac1e35bea00cc6250300a64dee67d5918..22782bc3aa9c3d33683f278118d44c62c3998450 100644 (file)
@@ -187,6 +187,15 @@ FUSE_3.12 {
                fuse_lowlevel_notify_expire_entry;
 } FUSE_3.4;
 
+FUSE_3.17 {
+       global:
+               _fuse_session_new_317;
+               _fuse_new;
+               _fuse_new_30;
+               _fuse_new_317;
+               fuse_main_real_317;
+} FUSE_3.12;
+
 # Local Variables:
 # indent-tabs-mode: t
 # End:
index 14a0df3301b5af8bfc2733cdddae099ebcd17099..e84c857cedb5f94f4269146c8b228cca4791eb3f 100644 (file)
@@ -304,8 +304,11 @@ int fuse_daemonize(int foreground)
        return 0;
 }
 
-int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
-                  size_t op_size, void *user_data)
+int fuse_main_real_317(int argc, char *argv[], const struct fuse_operations *op,
+                  size_t op_size, struct libfuse_version *version, void *user_data);
+FUSE_SYMVER("fuse_main_real_317", "fuse_main_real@@FUSE_3.17")
+int fuse_main_real_317(int argc, char *argv[], const struct fuse_operations *op,
+                  size_t op_size, struct libfuse_version *version, void *user_data)
 {
        struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
        struct fuse *fuse;
@@ -341,8 +344,7 @@ int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
                goto out1;
        }
 
-
-       fuse = fuse_new_31(&args, op, op_size, user_data);
+       fuse = _fuse_new(&args, op, op_size, version, user_data);
        if (fuse == NULL) {
                res = 3;
                goto out1;
@@ -394,6 +396,16 @@ out1:
        return res;
 }
 
+int fuse_main_real_30(int argc, char *argv[], const struct fuse_operations *op,
+                     size_t op_size, void *user_data);
+FUSE_SYMVER("fuse_main_real_30", "fuse_main_real@FUSE_3.0")
+int fuse_main_real_30(int argc, char *argv[], const struct fuse_operations *op,
+                     size_t op_size, void *user_data)
+{
+       struct libfuse_version version = { 0 };
+
+       return fuse_main_real_317(argc, argv, op, op_size, &version, user_data);
+}
 
 void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts,
                               struct fuse_conn_info *conn)