[Libguestfs] [PATCH nbdkit] server: Pass the export name through filter .open calls.

Richard W.M. Jones rjones at redhat.com
Tue Jul 21 20:47:20 UTC 2020


To allow filters to modify the export name as it passes through the
layers this commit makes several changes:

The filter .open callback now takes an extra parameter, the export
name.  This is always non-NULL (for oldstyle it is "").  This string
has a short lifetime and filters that need to hang on to it must take
a copy.  The filter must pass the exportname parameter down to the
next layer (or potentially modify it).

The plugin .open callback is unchanged, but the binding to it in
server/plugins.c takes a copy of the exportname in the handle and this
is what nbdkit_export_name returns to plugins.  Eventually we will
deprecate that function and the V3 .open callback in plugins will have
the extra parameter, but this is not implemented yet.

nbdkit_export_name can only be called from plugins, not filters.
Filters should use the .open(...exportname) parameter if they need the
export name.

Filter .reopen also takes the exportname.  The only filter that uses
it (retry) must save the exportname from .open in order to pass it to
.reopen.  This filter already does the same for the readonly flag so
this seems reasonable.

The handling of NBD_OPT_SET_META_CONTEXT interacted with the export
name (see commit 20b8509a9ccdab118ce7b7043be63bbad74f1e79).  I have
attempted to keep previous behaviour in this change, but note that
there is no regression test for this.  I added a debug message so we
can tell when this unusual case actually happens which should help
with diagnosis of problems.

All of the above is internal refactoring and should have no visible
external effect on server behaviour or how plugins are written.
Filters need to be updated as discussed above.  All the in-tree
filters have been, and we assume there are no out of tree filters
because of the unstable filter API.

See also this thread:
https://www.redhat.com/archives/libguestfs/2020-July/thread.html#00087
---
 include/nbdkit-common.h              |  1 -
 include/nbdkit-filter.h              |  7 +--
 include/nbdkit-plugin.h              |  1 +
 server/internal.h                    | 26 +++++++----
 server/backend.c                     | 14 +++---
 server/connections.c                 |  2 +
 server/filters.c                     |  6 +--
 server/plugins.c                     | 20 ++++++++-
 server/protocol-handshake-newstyle.c | 65 ++++++++++++++++++----------
 server/protocol-handshake-oldstyle.c |  2 +-
 server/protocol-handshake.c          |  5 ++-
 server/public.c                      |  3 ++
 filters/cow/cow.c                    |  5 ++-
 filters/exitlast/exitlast.c          |  4 +-
 filters/ext2/ext2.c                  | 31 ++++++++++---
 filters/gzip/gzip.c                  |  5 ++-
 filters/limit/limit.c                |  4 +-
 filters/log/log.c                    |  5 ++-
 filters/partition/partition.c        |  5 ++-
 filters/rate/rate.c                  |  5 ++-
 filters/retry/retry.c                | 15 +++++--
 filters/tar/tar.c                    |  5 ++-
 filters/truncate/truncate.c          |  5 ++-
 filters/xz/xz.c                      |  5 ++-
 tests/test-layers-filter.c           |  5 ++-
 25 files changed, 171 insertions(+), 80 deletions(-)

diff --git a/include/nbdkit-common.h b/include/nbdkit-common.h
index 47288050..671cd4a4 100644
--- a/include/nbdkit-common.h
+++ b/include/nbdkit-common.h
@@ -110,7 +110,6 @@ extern int nbdkit_stdio_safe (void);
 extern int nbdkit_read_password (const char *value, char **password);
 extern char *nbdkit_realpath (const char *path);
 extern int nbdkit_nanosleep (unsigned sec, unsigned nsec);
-extern const char *nbdkit_export_name (void);
 extern int nbdkit_peer_name (struct sockaddr *addr, socklen_t *addrlen);
 extern void nbdkit_shutdown (void);
 
diff --git a/include/nbdkit-filter.h b/include/nbdkit-filter.h
index 229a54b4..cec12db7 100644
--- a/include/nbdkit-filter.h
+++ b/include/nbdkit-filter.h
@@ -65,13 +65,14 @@ typedef int nbdkit_next_config_complete (nbdkit_backend *nxdata);
 typedef int nbdkit_next_get_ready (nbdkit_backend *nxdata);
 typedef int nbdkit_next_after_fork (nbdkit_backend *nxdata);
 typedef int nbdkit_next_preconnect (nbdkit_backend *nxdata, int readonly);
-typedef int nbdkit_next_open (nbdkit_backend *nxdata, int readonly);
+typedef int nbdkit_next_open (nbdkit_backend *nxdata,
+                              int readonly, const char *exportname);
 
 struct nbdkit_next_ops {
   /* Performs close + open on the underlying chain.
    * Used by the retry filter.
    */
-  int (*reopen) (nbdkit_backend *nxdata, int readonly);
+  int (*reopen) (nbdkit_backend *nxdata, int readonly, const char *exportname);
 
   /* The rest of the next ops are the same as normal plugin operations. */
   int64_t (*get_size) (nbdkit_backend *nxdata);
@@ -156,7 +157,7 @@ struct nbdkit_filter {
                      int readonly);
 
   void * (*open) (nbdkit_next_open *next, nbdkit_backend *nxdata,
-                  int readonly);
+                  int readonly, const char *exportname);
   void (*close) (void *handle);
 
   int (*prepare) (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,
diff --git a/include/nbdkit-plugin.h b/include/nbdkit-plugin.h
index cc261635..c6c1256c 100644
--- a/include/nbdkit-plugin.h
+++ b/include/nbdkit-plugin.h
@@ -142,6 +142,7 @@ struct nbdkit_plugin {
 };
 
 extern void nbdkit_set_error (int err);
+extern const char *nbdkit_export_name (void);
 
 #define NBDKIT_REGISTER_PLUGIN(plugin)                                  \
   NBDKIT_CXX_LANG_C                                                     \
diff --git a/server/internal.h b/server/internal.h
index ebc63a52..4911a49b 100644
--- a/server/internal.h
+++ b/server/internal.h
@@ -246,8 +246,6 @@ struct connection {
   struct handle *handles;       /* One per plugin and filter. */
   size_t nr_handles;
 
-  char exportname[NBD_MAX_STRING + 1];
-  uint32_t exportnamelen;
   uint32_t cflags;
   uint16_t eflags;
   bool handshake_complete;
@@ -255,6 +253,9 @@ struct connection {
   bool structured_replies;
   bool meta_context_base_allocation;
 
+  char *exportname_from_set_meta_context;
+  char *exportname;
+
   int sockin, sockout;
   connection_recv_function recv;
   connection_send_function send;
@@ -273,8 +274,9 @@ extern int connection_set_status (int value);
 
 /* protocol-handshake.c */
 extern int protocol_handshake (void);
-extern int protocol_common_open (uint64_t *exportsize, uint16_t *flags)
-  __attribute__((__nonnull__ (1, 2)));
+extern int protocol_common_open (uint64_t *exportsize, uint16_t *flags,
+                                 const char *exportname)
+  __attribute__((__nonnull__ (1, 2, 3)));
 
 /* protocol-handshake-oldstyle.c */
 extern int protocol_handshake_oldstyle (void);
@@ -358,7 +360,7 @@ struct backend {
   void (*get_ready) (struct backend *);
   void (*after_fork) (struct backend *);
   int (*preconnect) (struct backend *, int readonly);
-  void *(*open) (struct backend *, int readonly);
+  void *(*open) (struct backend *, int readonly, const char *exportname);
   int (*prepare) (struct backend *, void *handle, int readonly);
   int (*finalize) (struct backend *, void *handle);
   void (*close) (struct backend *, void *handle);
@@ -402,8 +404,13 @@ extern void backend_load (struct backend *b, const char *name,
 extern void backend_unload (struct backend *b, void (*unload) (void))
   __attribute__((__nonnull__ (1)));
 
-extern int backend_open (struct backend *b, int readonly)
-  __attribute__((__nonnull__ (1)));
+/* exportname is only valid for this call and almost certainly will be
+ * freed on return of this function, so backends must save the
+ * exportname if they need to refer to it later.
+ */
+extern int backend_open (struct backend *b,
+                         int readonly, const char *exportname)
+  __attribute__((__nonnull__ (1, 3)));
 extern int backend_prepare (struct backend *b)
   __attribute__((__nonnull__ (1)));
 extern int backend_finalize (struct backend *b)
@@ -414,8 +421,9 @@ extern bool backend_valid_range (struct backend *b,
                                  uint64_t offset, uint32_t count)
   __attribute__((__nonnull__ (1)));
 
-extern int backend_reopen (struct backend *b, int readonly)
-  __attribute__((__nonnull__ (1)));
+extern int backend_reopen (struct backend *b,
+                           int readonly, const char *exportname)
+  __attribute__((__nonnull__ (1, 3)));
 extern int64_t backend_get_size (struct backend *b)
   __attribute__((__nonnull__ (1)));
 extern int backend_can_write (struct backend *b)
diff --git a/server/backend.c b/server/backend.c
index cf87e35a..d39fdeaf 100644
--- a/server/backend.c
+++ b/server/backend.c
@@ -152,12 +152,13 @@ backend_unload (struct backend *b, void (*unload) (void))
 }
 
 int
-backend_open (struct backend *b, int readonly)
+backend_open (struct backend *b, int readonly, const char *exportname)
 {
   GET_CONN;
   struct handle *h = get_handle (conn, b->i);
 
-  controlpath_debug ("%s: open readonly=%d", b->name, readonly);
+  controlpath_debug ("%s: open readonly=%d exportname=\"%s\"",
+                     b->name, readonly, exportname);
 
   assert (h->handle == NULL);
   assert ((h->state & HANDLE_OPEN) == 0);
@@ -168,7 +169,7 @@ backend_open (struct backend *b, int readonly)
   /* Most filters will call next_open first, resulting in
    * inner-to-outer ordering.
    */
-  h->handle = b->open (b, readonly);
+  h->handle = b->open (b, readonly, exportname);
   controlpath_debug ("%s: open returned handle %p", b->name, h->handle);
 
   if (h->handle == NULL) {
@@ -268,14 +269,15 @@ backend_valid_range (struct backend *b, uint64_t offset, uint32_t count)
 /* Wrappers for all callbacks in a filter's struct nbdkit_next_ops. */
 
 int
-backend_reopen (struct backend *b, int readonly)
+backend_reopen (struct backend *b, int readonly, const char *exportname)
 {
-  controlpath_debug ("%s: reopen readonly=%d", b->name, readonly);
+  controlpath_debug ("%s: reopen readonly=%d exportname=\"%s\"",
+                     b->name, readonly, exportname);
 
   if (backend_finalize (b) == -1)
     return -1;
   backend_close (b);
-  if (backend_open (b, readonly) == -1) {
+  if (backend_open (b, readonly, exportname) == -1) {
     backend_close (b);
     return -1;
   }
diff --git a/server/connections.c b/server/connections.c
index cc552b90..69e4c0c0 100644
--- a/server/connections.c
+++ b/server/connections.c
@@ -356,6 +356,8 @@ free_connection (struct connection *conn)
   pthread_mutex_destroy (&conn->write_lock);
   pthread_mutex_destroy (&conn->status_lock);
 
+  free (conn->exportname_from_set_meta_context);
+  free (conn->exportname);
   free (conn->handles);
   free (conn);
   threadlocal_set_conn (NULL);
diff --git a/server/filters.c b/server/filters.c
index 2d705e1e..7d268096 100644
--- a/server/filters.c
+++ b/server/filters.c
@@ -238,7 +238,7 @@ plugin_magic_config_key (struct backend *b)
 }
 
 static void *
-filter_open (struct backend *b, int readonly)
+filter_open (struct backend *b, int readonly, const char *exportname)
 {
   struct backend_filter *f = container_of (b, struct backend_filter, backend);
   void *handle;
@@ -247,8 +247,8 @@ filter_open (struct backend *b, int readonly)
    * inner-to-outer ordering.
    */
   if (f->filter.open)
-    handle = f->filter.open (backend_open, b->next, readonly);
-  else if (backend_open (b->next, readonly) == -1)
+    handle = f->filter.open (backend_open, b->next, readonly, exportname);
+  else if (backend_open (b->next, readonly, exportname) == -1)
     handle = NULL;
   else
     handle = NBDKIT_HANDLE_NOT_NEEDED;
diff --git a/server/plugins.c b/server/plugins.c
index 285569bb..8449b1d9 100644
--- a/server/plugins.c
+++ b/server/plugins.c
@@ -278,12 +278,30 @@ plugin_preconnect (struct backend *b, int readonly)
 }
 
 static void *
-plugin_open (struct backend *b, int readonly)
+plugin_open (struct backend *b, int readonly, const char *exportname)
 {
+  GET_CONN;
   struct backend_plugin *p = container_of (b, struct backend_plugin, backend);
 
   assert (p->plugin.open != NULL);
 
+  /* Save the exportname since the lifetime of the string passed in
+   * here is likely to be brief.  In addition this provides a place
+   * for nbdkit_export_name to retrieve it if called from the plugin.
+   *
+   * In API V3 we propose to pass the exportname as an extra parameter
+   * to the (new) plugin.open and deprecate nbdkit_export_name for V3
+   * users.  Even then we will still need to save it in the handle
+   * because of the lifetime issue.
+   */
+  if (conn->exportname == NULL) {
+    conn->exportname = strdup (exportname);
+    if (conn->exportname == NULL) {
+      nbdkit_error ("strdup: %m");
+      return NULL;
+    }
+  }
+
   return p->plugin.open (readonly);
 }
 
diff --git a/server/protocol-handshake-newstyle.c b/server/protocol-handshake-newstyle.c
index fa44f253..0ed3c7c7 100644
--- a/server/protocol-handshake-newstyle.c
+++ b/server/protocol-handshake-newstyle.c
@@ -200,11 +200,29 @@ conn_recv_full (void *buf, size_t len, const char *fmt, ...)
  * in that function, and must not cause any wire traffic.
  */
 static int
-finish_newstyle_options (uint64_t *exportsize)
+finish_newstyle_options (uint64_t *exportsize,
+                         const char *exportname_in, uint32_t exportnamelen)
 {
   GET_CONN;
 
-  if (protocol_common_open (exportsize, &conn->eflags) == -1)
+  /* Since the exportname string passed here comes directly out of the
+   * NBD protocol make a temporary copy of the exportname into a
+   * \0-terminated buffer.
+   */
+  CLEANUP_FREE char *exportname = strndup (exportname_in, exportnamelen);
+  if (exportname == NULL) {
+    nbdkit_error ("strndup: %m");
+    return -1;
+  }
+
+  if (conn->exportname_from_set_meta_context &&
+      strcmp (conn->exportname_from_set_meta_context, exportname) != 0) {
+    debug ("newstyle negotiation: NBD_OPT_SET_META_CONTEXT export name \"%s\" ≠ final client exportname \"%s\", so discarding the previous context",
+           conn->exportname_from_set_meta_context, exportname);
+    conn->meta_context_base_allocation = false;
+  }
+
+  if (protocol_common_open (exportsize, &conn->eflags, exportname) == -1)
     return -1;
 
   debug ("newstyle negotiation: flags: export 0x%x", conn->eflags);
@@ -238,22 +256,13 @@ check_string (uint32_t option, char *buf, uint32_t len, uint32_t maxlen,
  */
 static int
 check_export_name (uint32_t option, char *buf,
-                   uint32_t exportnamelen, uint32_t maxlen, bool save)
+                   uint32_t exportnamelen, uint32_t maxlen)
 {
   GET_CONN;
 
   if (check_string (option, buf, exportnamelen, maxlen, "export name") == -1)
     return -1;
 
-  assert (exportnamelen < sizeof conn->exportname);
-  if (save) {
-    if (exportnamelen != conn->exportnamelen ||
-        memcmp (conn->exportname, buf, exportnamelen) != 0)
-      conn->meta_context_base_allocation = false;
-    memcpy (conn->exportname, buf, exportnamelen);
-    conn->exportname[exportnamelen] = '\0';
-    conn->exportnamelen = exportnamelen;
-  }
   debug ("newstyle negotiation: %s: client requested export '%.*s'",
          name_of_nbd_opt (option), (int) exportnamelen, buf);
   return 0;
@@ -329,13 +338,13 @@ negotiate_handshake_newstyle_options (void)
       if (conn_recv_full (data, optlen,
                           "read: %s: %m", name_of_nbd_opt (option)) == -1)
         return -1;
-      if (check_export_name (option, data, optlen, optlen, true) == -1)
+      if (check_export_name (option, data, optlen, optlen) == -1)
         return -1;
 
       /* We have to finish the handshake by sending handshake_finish.
        * On failure, we have to disconnect.
        */
-      if (finish_newstyle_options (&exportsize) == -1)
+      if (finish_newstyle_options (&exportsize, data, optlen) == -1)
         return -1;
 
       memset (&handshake_finish, 0, sizeof handshake_finish);
@@ -468,7 +477,7 @@ negotiate_handshake_newstyle_options (void)
          * or else we drop the support for that context.
          */
         if (check_export_name (option, &data[4], exportnamelen,
-                               optlen - 6, true) == -1) {
+                               optlen - 6) == -1) {
           if (send_newstyle_option_reply (option, NBD_REP_ERR_INVALID)
               == -1)
             return -1;
@@ -483,7 +492,8 @@ negotiate_handshake_newstyle_options (void)
          * client and let them try another NBD_OPT, rather than
          * disconnecting.
          */
-        if (finish_newstyle_options (&exportsize) == -1) {
+        if (finish_newstyle_options (&exportsize,
+                                     &data[4], exportnamelen) == -1) {
           if (backend_finalize (top) == -1)
             return -1;
           backend_close (top);
@@ -601,17 +611,26 @@ negotiate_handshake_newstyle_options (void)
           continue;
         }
 
+        memcpy (&exportnamelen, &data[0], 4);
+        exportnamelen = be32toh (exportnamelen);
+        what = "validating export name";
+        if (check_export_name (option, &data[4], exportnamelen,
+                               optlen - 8) == -1)
+          goto opt_meta_invalid_option_len;
+
         /* Remember the export name: the NBD spec says that if the client
          * later uses NBD_OPT_GO on a different export, then the context
          * returned here is not usable.
          */
-        memcpy (&exportnamelen, &data[0], 4);
-        exportnamelen = be32toh (exportnamelen);
-        what = "validating export name";
-        if (check_export_name (option, &data[4], exportnamelen,
-                               optlen - 8,
-                               option == NBD_OPT_SET_META_CONTEXT) == -1)
-          goto opt_meta_invalid_option_len;
+        if (option == NBD_OPT_SET_META_CONTEXT) {
+          conn->exportname_from_set_meta_context =
+            strndup (&data[4], exportnamelen);
+          if (conn->exportname_from_set_meta_context == NULL) {
+            nbdkit_error ("malloc: %m");
+            return -1;
+          }
+        }
+
         opt_index = 4 + exportnamelen;
 
         /* Read the number of queries. */
diff --git a/server/protocol-handshake-oldstyle.c b/server/protocol-handshake-oldstyle.c
index 4ad4720f..d4692362 100644
--- a/server/protocol-handshake-oldstyle.c
+++ b/server/protocol-handshake-oldstyle.c
@@ -56,7 +56,7 @@ protocol_handshake_oldstyle (void)
   /* With oldstyle, our only option if .open or friends fail is to
    * disconnect, as we cannot report the problem to the client.
    */
-  if (protocol_common_open (&exportsize, &eflags) == -1)
+  if (protocol_common_open (&exportsize, &eflags, "") == -1)
     return -1;
 
   gflags = 0;
diff --git a/server/protocol-handshake.c b/server/protocol-handshake.c
index 5e4b5e6e..80233713 100644
--- a/server/protocol-handshake.c
+++ b/server/protocol-handshake.c
@@ -72,14 +72,15 @@ protocol_handshake ()
  * simply opening a TCP connection.
  */
 int
-protocol_common_open (uint64_t *exportsize, uint16_t *flags)
+protocol_common_open (uint64_t *exportsize, uint16_t *flags,
+                      const char *exportname)
 {
   GET_CONN;
   int64_t size;
   uint16_t eflags = NBD_FLAG_HAS_FLAGS;
   int fl;
 
-  if (backend_open (top, read_only) == -1)
+  if (backend_open (top, read_only, exportname) == -1)
     return -1;
 
   /* Prepare (for filters), called just after open. */
diff --git a/server/public.c b/server/public.c
index cef25426..c00a5cb6 100644
--- a/server/public.c
+++ b/server/public.c
@@ -654,6 +654,9 @@ nbdkit_nanosleep (unsigned sec, unsigned nsec)
 #endif
 }
 
+/* This function will be deprecated for API V3 users.  The preferred
+ * approach will be to get the exportname from .open().
+ */
 const char *
 nbdkit_export_name (void)
 {
diff --git a/filters/cow/cow.c b/filters/cow/cow.c
index fae36174..0faf2726 100644
--- a/filters/cow/cow.c
+++ b/filters/cow/cow.c
@@ -93,10 +93,11 @@ cow_config (nbdkit_next_config *next, void *nxdata,
   "cow-on-cache=<BOOL>  Set to true to treat client cache requests as writes.\n"
 
 static void *
-cow_open (nbdkit_next_open *next, void *nxdata, int readonly)
+cow_open (nbdkit_next_open *next, void *nxdata,
+          int readonly, const char *exportname)
 {
   /* Always pass readonly=1 to the underlying plugin. */
-  if (next (nxdata, 1) == -1)
+  if (next (nxdata, 1, exportname) == -1)
     return NULL;
 
   return NBDKIT_HANDLE_NOT_NEEDED;
diff --git a/filters/exitlast/exitlast.c b/filters/exitlast/exitlast.c
index 51efda03..378cda75 100644
--- a/filters/exitlast/exitlast.c
+++ b/filters/exitlast/exitlast.c
@@ -51,9 +51,9 @@ static _Atomic unsigned connections;
 
 static void *
 exitlast_open (nbdkit_next_open *next, nbdkit_backend *nxdata,
-               int readonly)
+               int readonly, const char *exportname)
 {
-  if (next (nxdata, readonly) == -1)
+  if (next (nxdata, readonly, exportname) == -1)
     return NULL;
 
   connections++;
diff --git a/filters/ext2/ext2.c b/filters/ext2/ext2.c
index 92c8601a..72b7ac9f 100644
--- a/filters/ext2/ext2.c
+++ b/filters/ext2/ext2.c
@@ -112,6 +112,7 @@ ext2_config_complete (nbdkit_next_config_complete *next, void *nxdata)
 
 /* The per-connection handle. */
 struct handle {
+  char *exportname;             /* Client export name. */
   ext2_filsys fs;               /* Filesystem handle. */
   ext2_ino_t ino;               /* Inode of open file. */
   ext2_file_t file;             /* File handle. */
@@ -120,20 +121,37 @@ struct handle {
 
 /* Create the per-connection handle. */
 static void *
-ext2_open (nbdkit_next_open *next, void *nxdata, int readonly)
+ext2_open (nbdkit_next_open *next, void *nxdata,
+           int readonly, const char *exportname)
 {
   struct handle *h;
 
-  /* Request write access to the underlying plugin, for journal replay. */
-  if (next (nxdata, 0) == -1)
-    return NULL;
-
   h = calloc (1, sizeof *h);
   if (h == NULL) {
     nbdkit_error ("calloc: %m");
     return NULL;
   }
 
+  /* Save the client exportname in the handle. */
+  h->exportname = strdup (exportname);
+  if (h->exportname == NULL) {
+    nbdkit_error ("strdup: %m");
+    free (h);
+    return NULL;
+  }
+
+  /* If file == NULL (ie. using exportname) then don't
+   * pass the client exportname to the lower layers.
+   */
+  exportname = file ? exportname : "";
+
+  /* Request write access to the underlying plugin, for journal replay. */
+  if (next (nxdata, 0, exportname) == -1) {
+    free (h->exportname);
+    free (h);
+    return NULL;
+  }
+
   return h;
 }
 
@@ -148,7 +166,7 @@ ext2_prepare (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle,
   struct ext2_inode inode;
   int64_t r;
   CLEANUP_FREE char *name = NULL;
-  const char *fname = file ?: nbdkit_export_name ();
+  const char *fname = file ?: h->exportname;
   CLEANUP_FREE char *absname = NULL;
 
   fs_flags = 0;
@@ -242,6 +260,7 @@ ext2_close (void *handle)
     ext2fs_file_close (h->file);
     ext2fs_close (h->fs);
   }
+  free (h->exportname);
   free (h);
 }
 
diff --git a/filters/gzip/gzip.c b/filters/gzip/gzip.c
index 31fde3bb..8323b882 100644
--- a/filters/gzip/gzip.c
+++ b/filters/gzip/gzip.c
@@ -74,10 +74,11 @@ gzip_thread_model (void)
 }
 
 static void *
-gzip_open (nbdkit_next_open *next, nbdkit_backend *nxdata, int readonly)
+gzip_open (nbdkit_next_open *next, nbdkit_backend *nxdata,
+           int readonly, const char *exportname)
 {
   /* Always pass readonly=1 to the underlying plugin. */
-  if (next (nxdata, 1) == -1)
+  if (next (nxdata, 1, exportname) == -1)
     return NULL;
 
   return NBDKIT_HANDLE_NOT_NEEDED;
diff --git a/filters/limit/limit.c b/filters/limit/limit.c
index 563481fa..7c4477eb 100644
--- a/filters/limit/limit.c
+++ b/filters/limit/limit.c
@@ -91,9 +91,9 @@ limit_preconnect (nbdkit_next_preconnect *next, nbdkit_backend *nxdata,
 
 static void *
 limit_open (nbdkit_next_open *next, nbdkit_backend *nxdata,
-            int readonly)
+            int readonly, const char *exportname)
 {
-  if (next (nxdata, readonly) == -1)
+  if (next (nxdata, readonly, exportname) == -1)
     return NULL;
 
   ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
diff --git a/filters/log/log.c b/filters/log/log.c
index f47a6a93..8b146f1c 100644
--- a/filters/log/log.c
+++ b/filters/log/log.c
@@ -227,11 +227,12 @@ output_return (struct handle *h, const char *act, uint64_t id, int r, int *err)
 
 /* Open a connection. */
 static void *
-log_open (nbdkit_next_open *next, void *nxdata, int readonly)
+log_open (nbdkit_next_open *next, void *nxdata,
+          int readonly, const char *exportname)
 {
   struct handle *h;
 
-  if (next (nxdata, readonly) == -1)
+  if (next (nxdata, readonly, exportname) == -1)
     return NULL;
 
   h = malloc (sizeof *h);
diff --git a/filters/partition/partition.c b/filters/partition/partition.c
index 41b85312..c348664b 100644
--- a/filters/partition/partition.c
+++ b/filters/partition/partition.c
@@ -87,11 +87,12 @@ struct handle {
 
 /* Open a connection. */
 static void *
-partition_open (nbdkit_next_open *next, void *nxdata, int readonly)
+partition_open (nbdkit_next_open *next, void *nxdata,
+                int readonly, const char *exportname)
 {
   struct handle *h;
 
-  if (next (nxdata, readonly) == -1)
+  if (next (nxdata, readonly, exportname) == -1)
     return NULL;
 
   h = malloc (sizeof *h);
diff --git a/filters/rate/rate.c b/filters/rate/rate.c
index 95b83024..6e99fcc7 100644
--- a/filters/rate/rate.c
+++ b/filters/rate/rate.c
@@ -162,11 +162,12 @@ rate_get_ready (nbdkit_next_get_ready *next, void *nxdata)
 
 /* Create the per-connection handle. */
 static void *
-rate_open (nbdkit_next_open *next, void *nxdata, int readonly)
+rate_open (nbdkit_next_open *next, void *nxdata,
+           int readonly, const char *exportname)
 {
   struct rate_handle *h;
 
-  if (next (nxdata, readonly) == -1)
+  if (next (nxdata, readonly, exportname) == -1)
     return NULL;
 
   h = malloc (sizeof *h);
diff --git a/filters/retry/retry.c b/filters/retry/retry.c
index 8037f383..be334c39 100644
--- a/filters/retry/retry.c
+++ b/filters/retry/retry.c
@@ -106,16 +106,18 @@ retry_config (nbdkit_next_config *next, void *nxdata,
 
 struct retry_handle {
   int readonly;                 /* Save original readonly setting. */
+  const char *exportname;       /* Client exportname. */
   unsigned reopens;
   bool open;
 };
 
 static void *
-retry_open (nbdkit_next_open *next, void *nxdata, int readonly)
+retry_open (nbdkit_next_open *next, void *nxdata,
+            int readonly, const char *exportname)
 {
   struct retry_handle *h;
 
-  if (next (nxdata, readonly) == -1)
+  if (next (nxdata, readonly, exportname) == -1)
     return NULL;
 
   h = malloc (sizeof *h);
@@ -125,6 +127,12 @@ retry_open (nbdkit_next_open *next, void *nxdata, int readonly)
   }
 
   h->readonly = readonly;
+  h->exportname = strdup (exportname);
+  if (h->exportname == NULL) {
+    nbdkit_error ("strdup: %m");
+    free (h);
+    return NULL;
+  }
   h->reopens = 0;
   h->open = true;
 
@@ -198,7 +206,8 @@ do_retry (struct retry_handle *h,
 
   /* Reopen the connection. */
   h->reopens++;
-  if (next_ops->reopen (nxdata, h->readonly || force_readonly) == -1) {
+  if (next_ops->reopen (nxdata,
+                        h->readonly || force_readonly, h->exportname) == -1) {
     /* If the reopen fails we treat it the same way as a command
      * failing.
      */
diff --git a/filters/tar/tar.c b/filters/tar/tar.c
index a2b26935..c04f09dd 100644
--- a/filters/tar/tar.c
+++ b/filters/tar/tar.c
@@ -112,11 +112,12 @@ struct handle {
 };
 
 static void *
-tar_open (nbdkit_next_open *next, nbdkit_backend *nxdata, int readonly)
+tar_open (nbdkit_next_open *next, nbdkit_backend *nxdata,
+          int readonly, const char *exportname)
 {
   struct handle *h;
 
-  if (next (nxdata, readonly) == -1)
+  if (next (nxdata, readonly, exportname) == -1)
     return NULL;
 
   h = calloc (1, sizeof *h);
diff --git a/filters/truncate/truncate.c b/filters/truncate/truncate.c
index 485f7f45..ee384871 100644
--- a/filters/truncate/truncate.c
+++ b/filters/truncate/truncate.c
@@ -124,11 +124,12 @@ struct handle {
 
 /* Open a connection. */
 static void *
-truncate_open (nbdkit_next_open *next, void *nxdata, int readonly)
+truncate_open (nbdkit_next_open *next, void *nxdata,
+               int readonly, const char *exportname)
 {
   struct handle *h;
 
-  if (next (nxdata, readonly) == -1)
+  if (next (nxdata, readonly, exportname) == -1)
     return NULL;
 
   h = malloc (sizeof *h); /* h is populated during .prepare */
diff --git a/filters/xz/xz.c b/filters/xz/xz.c
index 365958da..9154dbeb 100644
--- a/filters/xz/xz.c
+++ b/filters/xz/xz.c
@@ -89,12 +89,13 @@ struct xz_handle {
 
 /* Create the per-connection handle. */
 static void *
-xz_open (nbdkit_next_open *next, void *nxdata, int readonly)
+xz_open (nbdkit_next_open *next, void *nxdata,
+         int readonly, const char *exportname)
 {
   struct xz_handle *h;
 
   /* Always pass readonly=1 to the underlying plugin. */
-  if (next (nxdata, 1) == -1)
+  if (next (nxdata, 1, exportname) == -1)
     return NULL;
 
   h = malloc (sizeof *h);
diff --git a/tests/test-layers-filter.c b/tests/test-layers-filter.c
index b6ca05bd..29d8b669 100644
--- a/tests/test-layers-filter.c
+++ b/tests/test-layers-filter.c
@@ -107,7 +107,8 @@ test_layers_filter_preconnect (nbdkit_next_preconnect *next,
 }
 
 static void *
-test_layers_filter_open (nbdkit_next_open *next, void *nxdata, int readonly)
+test_layers_filter_open (nbdkit_next_open *next, void *nxdata,
+                         int readonly, const char *exportname)
 {
   struct handle *h = malloc (sizeof *h);
 
@@ -118,7 +119,7 @@ test_layers_filter_open (nbdkit_next_open *next, void *nxdata, int readonly)
   h->nxdata = nxdata;
   h->next_ops = NULL;
 
-  if (next (nxdata, readonly) == -1)
+  if (next (nxdata, readonly, exportname) == -1)
     return NULL;
 
   /* Debug after recursing, to show opposite order from .close */
-- 
2.27.0




More information about the Libguestfs mailing list