[Libguestfs] [nbdkit PATCH 2/4] server: Prepare to use export list from plugin

Eric Blake eblake at redhat.com
Fri Jul 31 22:22:30 UTC 2020


Wire up everything in the server to query the export list from the
plugin.  Actual patches to let plugins and filters return a
non-default list will come later, so for now, the behavior is
unchanged: NBD_OPT_LIST still lists just "", and a client requesting
"" still lets the plugin see an export name of "".

Signed-off-by: Eric Blake <eblake at redhat.com>
---
 server/internal.h                    |  6 +++
 server/backend.c                     | 22 ++++++++
 server/filters.c                     |  9 ++++
 server/plugins.c                     |  9 ++++
 server/protocol-handshake-newstyle.c | 80 ++++++++++++++++------------
 server/protocol-handshake.c          | 24 +++++++++
 6 files changed, 116 insertions(+), 34 deletions(-)

diff --git a/server/internal.h b/server/internal.h
index 1acbbb06..32388d07 100644
--- a/server/internal.h
+++ b/server/internal.h
@@ -361,6 +361,8 @@ struct backend {
   void (*get_ready) (struct backend *);
   void (*after_fork) (struct backend *);
   int (*preconnect) (struct backend *, int readonly);
+  int (*list_exports) (struct backend *, int readonly, int default_only,
+                       struct nbdkit_exports *exports);
   void *(*open) (struct backend *, int readonly, const char *exportname);
   int (*prepare) (struct backend *, void *handle, int readonly);
   int (*finalize) (struct backend *, void *handle);
@@ -405,6 +407,10 @@ 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_list_exports (struct backend *b, int readonly,
+                                 int default_only,
+                                 struct nbdkit_exports *exports)
+  __attribute__((__nonnull__ (1, 4)));
 /* 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.
diff --git a/server/backend.c b/server/backend.c
index d39fdeaf..315bd8d2 100644
--- a/server/backend.c
+++ b/server/backend.c
@@ -151,6 +151,28 @@ backend_unload (struct backend *b, void (*unload) (void))
   free (b->name);
 }

+int
+backend_list_exports (struct backend *b, int readonly, int default_only,
+                      struct nbdkit_exports *exports)
+{
+  GET_CONN;
+  struct handle *h = get_handle (conn, b->i);
+  int r;
+
+  controlpath_debug ("%s: list_exports readonly=%d default_only=%d",
+                     b->name, readonly, default_only);
+
+  assert (h->handle == NULL);
+  assert ((h->state & HANDLE_OPEN) == 0);
+  r = b->list_exports (b, readonly, default_only, exports);
+  if (r == -1)
+    controlpath_debug ("%s: list_exports failed", b->name);
+  else
+    controlpath_debug ("%s: list_exports returned %zu names", b->name,
+                       nbdkit_exports_count (exports));
+  return r;
+}
+
 int
 backend_open (struct backend *b, int readonly, const char *exportname)
 {
diff --git a/server/filters.c b/server/filters.c
index 7d268096..e5b5b860 100644
--- a/server/filters.c
+++ b/server/filters.c
@@ -237,6 +237,14 @@ plugin_magic_config_key (struct backend *b)
   return b->next->magic_config_key (b->next);
 }

+static int
+filter_list_exports (struct backend *b, int readonly, int default_only,
+                     struct nbdkit_exports *exports)
+{
+  /* XXX No filter override yet... */
+  return backend_list_exports (b->next, readonly, default_only, exports);
+}
+
 static void *
 filter_open (struct backend *b, int readonly, const char *exportname)
 {
@@ -540,6 +548,7 @@ static struct backend filter_functions = {
   .get_ready = filter_get_ready,
   .after_fork = filter_after_fork,
   .preconnect = filter_preconnect,
+  .list_exports = filter_list_exports,
   .open = filter_open,
   .prepare = filter_prepare,
   .finalize = filter_finalize,
diff --git a/server/plugins.c b/server/plugins.c
index 8449b1d9..8020046b 100644
--- a/server/plugins.c
+++ b/server/plugins.c
@@ -277,6 +277,14 @@ plugin_preconnect (struct backend *b, int readonly)
   return p->plugin.preconnect (readonly);
 }

+static int
+plugin_list_exports (struct backend *b, int readonly, int default_only,
+                     struct nbdkit_exports *exports)
+{
+  /* XXX No plugin support yet, so for now just advertise "" */
+  return nbdkit_add_export (exports, "", NULL);
+}
+
 static void *
 plugin_open (struct backend *b, int readonly, const char *exportname)
 {
@@ -730,6 +738,7 @@ static struct backend plugin_functions = {
   .get_ready = plugin_get_ready,
   .after_fork = plugin_after_fork,
   .preconnect = plugin_preconnect,
+  .list_exports = plugin_list_exports,
   .open = plugin_open,
   .prepare = plugin_prepare,
   .finalize = plugin_finalize,
diff --git a/server/protocol-handshake-newstyle.c b/server/protocol-handshake-newstyle.c
index 4025a645..8f41c7a8 100644
--- a/server/protocol-handshake-newstyle.c
+++ b/server/protocol-handshake-newstyle.c
@@ -75,44 +75,59 @@ send_newstyle_option_reply (uint32_t option, uint32_t reply)
   return 0;
 }

-/* Reply to NBD_OPT_LIST with a single empty export name.
- * TODO: Ask the plugin for the list of exports.
+/* Reply to NBD_OPT_LIST with the plugin's list of export names.
  */
 static int
-send_newstyle_option_reply_exportname (uint32_t option, uint32_t reply)
+send_newstyle_option_reply_exportnames (uint32_t option)
 {
   GET_CONN;
   struct nbd_fixed_new_option_reply fixed_new_option_reply;
-  const size_t name_len = 0;    /* length of export name */
-  uint32_t len;
+  size_t i;
+  CLEANUP_EXPORTS_FREE struct nbdkit_exports *exps = NULL;

-  fixed_new_option_reply.magic = htobe64 (NBD_REP_MAGIC);
-  fixed_new_option_reply.option = htobe32 (option);
-  fixed_new_option_reply.reply = htobe32 (reply);
-  fixed_new_option_reply.replylen = htobe32 (name_len + sizeof (len));
+  exps = nbdkit_exports_new (false);
+  if (exps == NULL)
+    return send_newstyle_option_reply (option, NBD_REP_ERR_TOO_BIG);
+  if (backend_list_exports (top, read_only, false, exps) == -1)
+    return send_newstyle_option_reply (option, NBD_REP_ERR_PLATFORM);

-  if (conn->send (&fixed_new_option_reply,
-                  sizeof fixed_new_option_reply, SEND_MORE) == -1) {
-    nbdkit_error ("write: %s: %m", name_of_nbd_opt (option));
-    return -1;
-  }
+  for (i = 0; i < nbdkit_exports_count (exps); i++) {
+    const struct nbdkit_export export = nbdkit_get_export (exps, i);
+    size_t name_len = strlen (export.name);
+    size_t desc_len = export.description ? strlen (export.description) : 0;
+    uint32_t len;

-  len = htobe32 (name_len);
-  if (conn->send (&len, sizeof len, SEND_MORE) == -1) {
-    nbdkit_error ("write: %s: %s: %m",
-                  name_of_nbd_opt (option), "sending length");
-    return -1;
-  }
-#if 0
-  /* If we were sending a non-"" export name, this is what we'd use. */
-  if (conn->send (exportname, name_len, 0) == -1) {
-    nbdkit_error ("write: %s: %s: %m",
-                  name_of_nbd_opt (option), "sending export name");
-    return -1;
+    fixed_new_option_reply.magic = htobe64 (NBD_REP_MAGIC);
+    fixed_new_option_reply.option = htobe32 (option);
+    fixed_new_option_reply.reply = htobe32 (NBD_REP_SERVER);
+    fixed_new_option_reply.replylen = htobe32 (name_len + sizeof (len) +
+                                               desc_len);
+
+    if (conn->send (&fixed_new_option_reply,
+                    sizeof fixed_new_option_reply, SEND_MORE) == -1) {
+      nbdkit_error ("write: %s: %m", name_of_nbd_opt (option));
+      return -1;
+    }
+
+    len = htobe32 (name_len);
+    if (conn->send (&len, sizeof len, SEND_MORE) == -1) {
+      nbdkit_error ("write: %s: %s: %m",
+                    name_of_nbd_opt (option), "sending length");
+      return -1;
+    }
+    if (conn->send (export.name, name_len, SEND_MORE) == -1) {
+      nbdkit_error ("write: %s: %s: %m",
+                    name_of_nbd_opt (option), "sending export name");
+      return -1;
+    }
+    if (conn->send (export.description, desc_len, 0) == -1) {
+      nbdkit_error ("write: %s: %s: %m",
+                    name_of_nbd_opt (option), "sending export description");
+      return -1;
+    }
   }
-#endif

-  return 0;
+  return send_newstyle_option_reply (option, NBD_REP_ACK);
 }

 static int
@@ -384,13 +399,10 @@ negotiate_handshake_newstyle_options (void)
         continue;
       }

-      /* Send back the exportname. */
-      debug ("newstyle negotiation: %s: advertising export \"\"",
+      /* Send back the exportname list. */
+      debug ("newstyle negotiation: %s: advertising exports",
              name_of_nbd_opt (option));
-      if (send_newstyle_option_reply_exportname (option, NBD_REP_SERVER) == -1)
-        return -1;
-
-      if (send_newstyle_option_reply (option, NBD_REP_ACK) == -1)
+      if (send_newstyle_option_reply_exportnames (option) == -1)
         return -1;
       break;

diff --git a/server/protocol-handshake.c b/server/protocol-handshake.c
index 80233713..afc954d2 100644
--- a/server/protocol-handshake.c
+++ b/server/protocol-handshake.c
@@ -59,6 +59,8 @@ protocol_handshake ()
 }

 /* Common code used by oldstyle and newstyle protocols to:
+ *
+ * - canonicalize default export name of ""
  *
  * - call the backend .open method
  *
@@ -79,6 +81,28 @@ protocol_common_open (uint64_t *exportsize, uint16_t *flags,
   int64_t size;
   uint16_t eflags = NBD_FLAG_HAS_FLAGS;
   int fl;
+  CLEANUP_FREE char *default_export = NULL;
+
+  if (!*exportname) {
+    /* Map client's request for the default export ("") to plugin's
+     * canonical name.  This is only a best-effort attempt, other
+     * than detection of malloc failure.
+     */
+    CLEANUP_EXPORTS_FREE struct nbdkit_exports *exps = NULL;
+
+    exps = nbdkit_exports_new (true);
+    if (exps == NULL)
+      return -1;
+    if (backend_list_exports (top, read_only, true, exps) == 0 &&
+        nbdkit_exports_count (exps)) {
+      default_export = strdup (nbdkit_get_export (exps, 0).name);
+      if (default_export == NULL) {
+        nbdkit_error ("strdup: %m");
+        return -1;
+      }
+      exportname = default_export;
+    }
+  }

   if (backend_open (top, read_only, exportname) == -1)
     return -1;
-- 
2.28.0




More information about the Libguestfs mailing list