New nbdkit already knows how to gracefully handle a plugin that
defines .pwrite_fua but not .pwrite, making it possible to write
a plugin that does not have to provide duplicate boilerplate
code. But for maximum cross-version compatibility, we must make
sure that older nbdkit, which doesn't know about the existance
of the .pwrite_fua callback, can still perform writes when loading
such a newer plugin. The trick is to make our registration
macro provide sane fallback definitions, marked such that gcc
won't warn if the fallbacks are not needed.
Signed-off-by: Eric Blake <eblake(a)redhat.com>
---
docs/nbdkit.pod | 11 +++++------
include/nbdkit-plugin.h | 26 ++++++++++++++++++++++++++
2 files changed, 31 insertions(+), 6 deletions(-)
diff --git a/docs/nbdkit.pod b/docs/nbdkit.pod
index 2b868d9..a72c9b1 100644
--- a/docs/nbdkit.pod
+++ b/docs/nbdkit.pod
@@ -785,12 +785,11 @@ information about that plugin, eg:
[etc]
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, there are cases where a newer plugin may not be fully
-supported by an older nbdkit binary (for example, a plugin that
-supplies C<.pwrite_fua> but not C<.pwrite> may not support writes
-when loaded by the older nbdkit).
+corresponding nbdkit binary. The nbdkit binary will always be able to
+utilize plugins compiled against an older version of the header; the
+converse direction of an older nbdkit driving a newer plugin is not
+guaranteed to work, although the design tries hard to preserve
+back-compatibility.
=head2 Detect if a plugin is installed
diff --git a/include/nbdkit-plugin.h b/include/nbdkit-plugin.h
index d3b0050..0c6e67e 100644
--- a/include/nbdkit-plugin.h
+++ b/include/nbdkit-plugin.h
@@ -117,6 +117,12 @@ extern char *nbdkit_absolute_path (const char *path);
extern int64_t nbdkit_parse_size (const char *str);
extern int nbdkit_read_password (const char *value, char **password);
+#ifdef __GNUC__
+#define NBDKIT_UNUSED __attribute__ (( __unused__ ))
+#else
+#define NBDKIT_UNUSED /* empty */
+#endif
+
#ifdef __cplusplus
#define NBDKIT_CXX_LANG_C extern "C"
#else
@@ -124,6 +130,20 @@ extern int nbdkit_read_password (const char *value, char
**password);
#endif
#define NBDKIT_REGISTER_PLUGIN(plugin) \
+ static int NBDKIT_UNUSED \
+ nbdkit_pwrite_no_fua (void *handle, const void *buf, uint32_t count, \
+ uint64_t offset) { \
+ return (plugin).pwrite_fua (handle, buf, count, offset, 0); \
+ } \
+ static int NBDKIT_UNUSED \
+ nbdkit_zero_no_fua (void *handle, uint32_t count, uint64_t offset, \
+ int may_trim) { \
+ return (plugin).zero_fua (handle, count, offset, may_trim, 0); \
+ } \
+ static int NBDKIT_UNUSED \
+ nbdkit_trim_no_fua (void *handle, uint32_t count, uint64_t offset) { \
+ return (plugin).trim_fua (handle, count, offset, 0); \
+ } \
NBDKIT_CXX_LANG_C \
struct nbdkit_plugin * \
plugin_init (void) \
@@ -131,6 +151,12 @@ extern int nbdkit_read_password (const char *value, char
**password);
(plugin)._struct_size = sizeof (plugin); \
(plugin)._api_version = NBDKIT_API_VERSION; \
(plugin)._thread_model = THREAD_MODEL; \
+ if ((plugin).pwrite_fua && !(plugin).pwrite) \
+ (plugin).pwrite = nbdkit_pwrite_no_fua; \
+ if ((plugin).zero_fua && !(plugin).zero) \
+ (plugin).zero = nbdkit_zero_no_fua; \
+ if ((plugin).trim_fua && !(plugin).trim) \
+ (plugin).trim = nbdkit_trim_no_fua; \
return &(plugin); \
}
--
2.14.3