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(a)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