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 "".
If the client calls NBD_OPT_LIST, we cache the default export name for
later use in case the client then requests export "", to avoid having
to re-call .list_exports a second time. But there is no caching if
the client calls NBD_OPT_LIST more than once, and we still have to
resolve the default name even if the client does not use NBD_OPT_LIST.
Signed-off-by: Eric Blake <eblake(a)redhat.com>
---
server/internal.h | 10 ++++
server/backend.c | 43 +++++++++++++++
server/filters.c | 9 ++++
server/plugins.c | 9 ++++
server/protocol-handshake-newstyle.c | 80 ++++++++++++++++------------
5 files changed, 117 insertions(+), 34 deletions(-)
diff --git a/server/internal.h b/server/internal.h
index 1acbbb06..f84696ca 100644
--- a/server/internal.h
+++ b/server/internal.h
@@ -202,6 +202,8 @@ struct handle {
unsigned char state; /* Bitmask of HANDLE_* values */
+ char *default_exportname;
+
uint64_t exportsize;
int can_write;
int can_flush;
@@ -220,6 +222,8 @@ reset_handle (struct handle *h)
{
h->handle = NULL;
h->state = 0;
+ free (h->default_exportname);
+ h->default_exportname = NULL;
h->exportsize = -1;
h->can_write = -1;
h->can_flush = -1;
@@ -361,6 +365,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 +411,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..18e44590 100644
--- a/server/backend.c
+++ b/server/backend.c
@@ -151,6 +151,35 @@ 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);
+ if (default_only && h->default_exportname)
+ return nbdkit_add_export (exports, h->default_exportname, NULL);
+
+ r = b->list_exports (b, readonly, default_only, exports);
+ if (r == -1)
+ controlpath_debug ("%s: list_exports failed", b->name);
+ else {
+ size_t count = nbdkit_exports_count (exports);
+ controlpath_debug ("%s: list_exports returned %zu names", b->name,
count);
+ /* Best effort caching of default export name */
+ if (!h->default_exportname && count)
+ h->default_exportname = strdup (nbdkit_get_export (exports, 0).name);
+ }
+ return r;
+}
+
int
backend_open (struct backend *b, int readonly, const char *exportname)
{
@@ -166,6 +195,20 @@ backend_open (struct backend *b, int readonly, const char
*exportname)
if (readonly)
h->can_write = 0;
+ /* Best-effort determination of the canonical name for default export */
+ if (!*exportname) {
+ if (!h->default_exportname) {
+ CLEANUP_EXPORTS_FREE struct nbdkit_exports *exps = NULL;
+
+ exps = nbdkit_exports_new (true);
+ if (b->list_exports (b, readonly, true, exps) == 0 &&
+ nbdkit_exports_count (exps))
+ h->default_exportname = strdup (nbdkit_get_export (exps, 0).name);
+ }
+ if (h->default_exportname)
+ exportname = h->default_exportname;
+ }
+
/* Most filters will call next_open first, resulting in
* inner-to-outer ordering.
*/
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;
--
2.28.0