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 <[email protected]> --- 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 _______________________________________________ Libguestfs mailing list [email protected] https://www.redhat.com/mailman/listinfo/libguestfs
