[Libguestfs] [nbdkit PATCH v3 15/15] RFC: plugins: Add back-compat for new plugin with old nbdkit

Eric Blake eblake at redhat.com
Thu Mar 8 23:03:11 UTC 2018


If we bump NBDKIT_API_VERSION, we have forcefully made older
nbdkit reject all plugins that opt to the newer API:

$ nbdkit ./plugins/nbd/.libs/nbdkit-nbd-plugin.so --dump-plugin
nbdkit: ./plugins/nbd/.libs/nbdkit-nbd-plugin.so: plugin is incompatible with this version of nbdkit (_api_version = 2)

But with a bit of finesse, we can make opting in to FUA support
orthogonal to NBDKIT_API_VERSION, by introducing a new witness
level that the user controls, and by providing sane fallbacks
so that newer plugins correctly populate the fields expected by
older nbdkit.

---
v3: rework entirely, add new variable instead of NBDKIT_API_VERSION

This patch is still RFC; if you like it, it's probably better to
rebase portions of this patch into the rest of the series, rather
than churning NBDKIT_API_VERSION temporarily to 2 and now back to 1

Signed-off-by: Eric Blake <eblake at redhat.com>
---
 docs/nbdkit-plugin.pod  | 12 ++++-----
 docs/nbdkit.pod         |  6 ++---
 include/nbdkit-plugin.h | 71 ++++++++++++++++++++++++++++++++++++++++++++-----
 src/internal.h          |  2 +-
 src/plugins.c           |  4 +--
 plugins/nbd/nbd.c       |  2 +-
 plugins/null/null.c     |  2 +-
 7 files changed, 77 insertions(+), 22 deletions(-)

diff --git a/docs/nbdkit-plugin.pod b/docs/nbdkit-plugin.pod
index 933f710..a82ced8 100644
--- a/docs/nbdkit-plugin.pod
+++ b/docs/nbdkit-plugin.pod
@@ -6,7 +6,7 @@ nbdkit-plugin - How to write nbdkit plugins

 =head1 SYNOPSIS

- #define NBDKIT_API_VERSION 2
+ #define NBDKIT_PLUGIN_LEVEL 2
  
  #include <nbdkit-plugin.h>
  
@@ -65,22 +65,22 @@ L<nbdkit-perl-plugin(3)>,
 L<nbdkit-python-plugin(3)>,
 L<nbdkit-ruby-plugin(3)>.

-=head1 C<#define NBDKIT_API_VERSION 2>
+=head1 C<#define NBDKIT_PLUGIN_LEVEL 2>

 Plugins must choose which API version they want to use, by defining
-NBDKIT_API_VERSION to a positive integer prior to including
+NBDKIT_PLUGIN_LEVEL to a positive integer prior to including
 C<nbdkit-plugin.h> (or any other nbdkit header).  The default version
 is 1 for backwards-compatibility with nbdkit v1.1.26 and earlier;
 however, it is recommended that new plugins be written to the maximum
-version (currently 2) as it enables more features and better
+level (currently 2) as it enables more features and better
 interaction with nbdkit filters.  Therefore, the rest of this document
 only covers the version 2 interface.  A newer nbdkit will always
-support plugins compiled against any prior API version.
+support plugins compiled against any prior API level.

 =head1 C<nbdkit-plugin.h>

 All plugins should start by including this header file, after
-optionally choosing an API version:
+optionally choosing an API level:

  #include <nbdkit-plugin.h>

diff --git a/docs/nbdkit.pod b/docs/nbdkit.pod
index d49ae53..ee27d54 100644
--- a/docs/nbdkit.pod
+++ b/docs/nbdkit.pod
@@ -829,10 +829,8 @@ information about that plugin, eg:
 Plugins which ship with nbdkit usually have the same version as the
 corresponding nbdkit binary.  The nbdkit binary will always be able to
 utilize plugins compiled against an older version of the header;
-however, newer plugins may not be fully supported by an older nbdkit
-binary (for example, a plugin compiled with C<NBDKIT_API_VERSION> of 2
-fails to load with an older nbdkit that only knows
-C<NBDKIT_API_VERSION> 1).
+however, although the design tries hard to allow it, newer plugins may
+not be fully supported by an older nbdkit binary.

 =head2 Detect if a plugin is installed

diff --git a/include/nbdkit-plugin.h b/include/nbdkit-plugin.h
index 3f541a1..9ecddeb 100644
--- a/include/nbdkit-plugin.h
+++ b/include/nbdkit-plugin.h
@@ -42,11 +42,15 @@
 extern "C" {
 #endif

-/* By default, a plugin gets API version 1; but you may request
- * version 2 prior to including this header */
-#ifndef NBDKIT_API_VERSION
-#define NBDKIT_API_VERSION                            1
-#elif (NBDKIT_API_VERSION - 0) < 1 || NBDKIT_API_VERSION > 2
+/* Remaining compatible with API version 1 allows plugins compiled
+ * against newer nbdkit headers to run with older nbdkit binaries.  */
+#define NBDKIT_API_VERSION 1
+
+/* By default, a plugin gets API version 1 level 1; but you may
+ * request version 1 level 2 prior to including this header. */
+#ifndef NBDKIT_PLUGIN_LEVEL
+#define NBDKIT_PLUGIN_LEVEL 1
+#elif (NBDKIT_PLUGIN_LEVEL - 0) < 1 || NBDKIT_PLUGIN_LEVEL > 2
 #error Unsupported API version
 #endif

@@ -86,7 +90,7 @@ struct nbdkit_plugin {
   int (*is_rotational) (void *handle);
   int (*can_trim) (void *handle);

-#if NBDKIT_API_VERSION == 1
+#if NBDKIT_PLUGIN_LEVEL == 1
   int (*pread) (void *handle, void *buf, uint32_t count, uint64_t offset);
   int (*pwrite) (void *handle, const void *buf, uint32_t count, uint64_t offset);
   int (*flush) (void *handle);
@@ -105,7 +109,7 @@ struct nbdkit_plugin {
   void (*dump_plugin) (void);

   int (*can_fua) (void *handle);
-#if NBDKIT_API_VERSION == 1
+#if NBDKIT_PLUGIN_LEVEL == 1
   int (*_unused1) (void *, void *, uint32_t, uint64_t);
   int (*_unused2) (void *, const void *, uint32_t, uint64_t, uint32_t);
   int (*_unused3) (void *, uint32_t);
@@ -125,6 +129,7 @@ struct nbdkit_plugin {

 extern void nbdkit_set_error (int err);

+#if NBDKIT_PLUGIN_LEVEL == 1
 #define NBDKIT_REGISTER_PLUGIN(plugin)                                  \
   NBDKIT_CXX_LANG_C                                                     \
   struct nbdkit_plugin *                                                \
@@ -136,6 +141,58 @@ extern void nbdkit_set_error (int err);
     return &(plugin);                                                   \
   }

+#else
+#define NBDKIT_REGISTER_PLUGIN(plugin)                                  \
+  static int                                                            \
+  nbdkit_pread_old (void *handle, void *buf, uint32_t count,            \
+                    uint64_t offset)                                    \
+  {                                                                     \
+    return (plugin).pread (handle, buf, count, offset, 0);              \
+  }                                                                     \
+  static int                                                            \
+  nbdkit_pwrite_old (void *handle, const void *buf, uint32_t count,     \
+                     uint64_t offset)                                   \
+  {                                                                     \
+    return (plugin).pwrite (handle, buf, count, offset, 0);             \
+  }                                                                     \
+  static int                                                            \
+  nbdkit_flush_old (void *handle)                                       \
+  {                                                                     \
+    return (plugin).flush (handle, 0);                                  \
+  }                                                                     \
+  static int                                                            \
+  nbdkit_trim_old (void *handle, uint32_t count, uint64_t offset)       \
+  {                                                                     \
+    return (plugin).trim (handle, count, offset, 0);                    \
+  }                                                                     \
+  static int                                                            \
+  nbdkit_zero_old (void *handle, uint32_t count, uint64_t offset,       \
+                   int may_trim)                                        \
+  {                                                                     \
+    return (plugin).zero (handle, count, offset,                        \
+                          may_trim ? NBDKIT_FLAG_MAY_TRIM : 0);         \
+  }                                                                     \
+  NBDKIT_CXX_LANG_C                                                     \
+  struct nbdkit_plugin *                                                \
+  plugin_init (void)                                                    \
+  {                                                                     \
+    (plugin)._struct_size = sizeof (plugin);                            \
+    (plugin)._api_version = NBDKIT_API_VERSION;                         \
+    (plugin)._thread_model = THREAD_MODEL;                              \
+    (plugin)._pread_old = nbdkit_pread_old;                             \
+    if ((plugin).pwrite)                                                \
+      (plugin)._pwrite_old = nbdkit_pwrite_old;                         \
+    if ((plugin).flush)                                                 \
+      (plugin)._flush_old = nbdkit_flush_old;                           \
+    if ((plugin).trim)                                                  \
+      (plugin)._trim_old = nbdkit_trim_old;                             \
+    if ((plugin).zero)                                                  \
+      (plugin)._zero_old = nbdkit_zero_old;                             \
+    return &(plugin);                                                   \
+  }
+
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/internal.h b/src/internal.h
index ea3155c..e803875 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -40,7 +40,7 @@
 #include <sys/socket.h>
 #include <pthread.h>

-#define NBDKIT_API_VERSION 2
+#define NBDKIT_PLUGIN_LEVEL 2
 #include "nbdkit-plugin.h"
 #include "nbdkit-filter.h"

diff --git a/src/plugins.c b/src/plugins.c
index d44e724..e583fac 100644
--- a/src/plugins.c
+++ b/src/plugins.c
@@ -389,7 +389,7 @@ plugin_can_fua (struct backend *b, struct connection *conn)

   if (p->plugin.can_fua) {
     r = p->plugin.can_fua (connection_get_handle (conn, 0));
-    if (r > NBDKIT_FUA_EMULATE && p->plugin._api_version == 1)
+    if (r > NBDKIT_FUA_EMULATE && !p->plugin.pread)
       r = NBDKIT_FUA_EMULATE;
     return r;
   }
@@ -655,7 +655,7 @@ plugin_register (size_t index, const char *filename,
   }

   /* Check for incompatible future versions. */
-  if (plugin->_api_version < 0 || plugin->_api_version > 2) {
+  if (plugin->_api_version != NBDKIT_API_VERSION) {
     fprintf (stderr, "%s: %s: plugin is incompatible with this version of nbdkit (_api_version = %d)\n",
              program_name, p->filename, plugin->_api_version);
     exit (EXIT_FAILURE);
diff --git a/plugins/nbd/nbd.c b/plugins/nbd/nbd.c
index db53746..bd1b48b 100644
--- a/plugins/nbd/nbd.c
+++ b/plugins/nbd/nbd.c
@@ -46,7 +46,7 @@
 #include <assert.h>
 #include <pthread.h>

-#define NBDKIT_API_VERSION 2
+#define NBDKIT_PLUGIN_LEVEL 2

 #include <nbdkit-plugin.h>
 #include "protocol.h"
diff --git a/plugins/null/null.c b/plugins/null/null.c
index 905cc64..436892d 100644
--- a/plugins/null/null.c
+++ b/plugins/null/null.c
@@ -39,7 +39,7 @@
 #include <string.h>
 #include <errno.h>

-#define NBDKIT_API_VERSION 2
+#define NBDKIT_PLUGIN_LEVEL 2

 #include <nbdkit-plugin.h>

-- 
2.14.3




More information about the Libguestfs mailing list