I'm about to add an 'exportname' filter, and in the process, I noticed
a few shortcomings in our API. Time to fix those before the 1.22
release locks our API in stone. Having .default_export makes it easy
to answer NBD_INFO_NAME in response to a client request during
NBD_OPT_GO, but answering NBD_INFO_DESCRIPTION is awkward - there's no
guarantee the export name was given with a description in
.list_exports. Note, however, that while we map "" into a canonical
name prior to .open (as the plugin is easier to write if it can just
assume the client already passed in the canonical name), we don't need
a description until after .open, alongside .get_size.
The recent libnbd 1.4 release has added 'nbd_set_full_info()' as the
knob for whether a client will request name and description during
NBD_OPT_GO; adding this functionality in nbdkit makes it easier to
test that API in libnbd.
---
docs/nbdkit-filter.pod | 9 ++++
docs/nbdkit-plugin.pod | 20 ++++++++
docs/nbdkit-protocol.pod | 14 ++++++
plugins/eval/nbdkit-eval-plugin.pod | 2 +
plugins/sh/nbdkit-sh-plugin.pod | 11 +++++
include/nbdkit-filter.h | 3 ++
include/nbdkit-plugin.h | 1 +
server/internal.h | 3 ++
server/backend.c | 22 +++++++++
server/filters.c | 13 +++++
server/plugins.c | 13 +++++
server/protocol-handshake-newstyle.c | 25 ++++++++--
plugins/sh/methods.h | 1 +
plugins/cc/cc.c | 59 +++++++++++++----------
plugins/eval/eval.c | 70 ++++++++++++++-------------
plugins/sh/methods.c | 32 +++++++++++++
plugins/sh/sh.c | 71 ++++++++++++++--------------
filters/tls-fallback/tls-fallback.c | 42 +++++++++-------
tests/test-export-info.sh | 34 ++++++-------
tests/test-tls-fallback.sh | 6 +++
TODO | 5 --
21 files changed, 320 insertions(+), 136 deletions(-)
diff --git a/docs/nbdkit-filter.pod b/docs/nbdkit-filter.pod
index 79c39f0d..b6a73f96 100644
--- a/docs/nbdkit-filter.pod
+++ b/docs/nbdkit-filter.pod
@@ -551,6 +551,15 @@ This function is only called once per connection and cached by
nbdkit.
Similarly, repeated calls to C<next_ops-E<gt>get_size> will return a
cached value.
+=head2 C<.export_description>
+
+ const char *export_description (struct nbdkit_next_ops *next_ops,
+ void *nxdata, void *handle);
+
+This intercepts the plugin C<.export_description> method and can be
+used to read or modify the export description that the NBD client
+will see.
+
=head2 C<.can_write>
=head2 C<.can_flush>
diff --git a/docs/nbdkit-plugin.pod b/docs/nbdkit-plugin.pod
index 9f5d6356..18412b6a 100644
--- a/docs/nbdkit-plugin.pod
+++ b/docs/nbdkit-plugin.pod
@@ -814,6 +814,26 @@ to get the size (in bytes) of the block device being exported.
The returned size must be E<ge> 0. If there is an error, C<.get_size>
should call C<nbdkit_error> with an error message and return C<-1>.
+=head2 C<.export_description>
+
+ const char *export_description (void *handle);
+
+This is called during the option negotiation phase only if the
+client specifically requested an export description (see the
+C<NBD_INFO_DESCRIPTION> response to C<NBD_OPT_GO>). Any
+description provided must be human-readable UTF-8, no longer
+than 4096 bytes. Ideally, this description should match any
+description set during C<.list_exports>, but that is not
+enforced.
+
+If the plugin returns C<NULL> or an invalid string (such as longer
+than 4096 bytes), or if this callback is omitted, no description is
+offered to the client. As this is not an error in the protocol, it is
+not necessary to call C<nbdkit_error>. If the callback will not be
+returning a compile-time constant string, you may find
+C<nbdkit_strdup_intern> helpful for returning a value that avoids a
+memory leak.
+
=head2 C<.can_write>
int can_write (void *handle);
diff --git a/docs/nbdkit-protocol.pod b/docs/nbdkit-protocol.pod
index b923d367..2f46d735 100644
--- a/docs/nbdkit-protocol.pod
+++ b/docs/nbdkit-protocol.pod
@@ -200,6 +200,20 @@ request is no faster than a counterpart write would be, while normal
zero requests still benefit from compressed network traffic regardless
of the time taken.
+=item C<NBD_INFO_NAME>, C<NBD_INFO_DESCRIPTION>
+
+Supported in nbdkit E<ge> 1.22.0.
+
+These protocol extensions allow a client to learn more information
+about an export during C<NBD_OPT_GO>. The C<.default_export> callback
+can inform a client of a canonical non-empty name in place of the
+default export C<"">, and the C<.export_description> callback can
give
+a client more details about the export.
+
+=item C<NBD_INFO_BLOCK_SIZE>
+
+I<Not supported>.
+
=item Resize Extension
I<Not supported>.
diff --git a/plugins/eval/nbdkit-eval-plugin.pod b/plugins/eval/nbdkit-eval-plugin.pod
index eb3c7f5a..22167ec5 100644
--- a/plugins/eval/nbdkit-eval-plugin.pod
+++ b/plugins/eval/nbdkit-eval-plugin.pod
@@ -100,6 +100,8 @@ features):
=item B<dump_plugin=>SCRIPT
+=item B<export_description=>SCRIPT
+
=item B<extents=>SCRIPT
=item B<flush=>SCRIPT
diff --git a/plugins/sh/nbdkit-sh-plugin.pod b/plugins/sh/nbdkit-sh-plugin.pod
index c6e9c432..fe86e53c 100644
--- a/plugins/sh/nbdkit-sh-plugin.pod
+++ b/plugins/sh/nbdkit-sh-plugin.pod
@@ -350,6 +350,17 @@ handle will be C<""> (empty string).
/path/to/script close <handle>
+=item C<export_description>
+
+ /path/to/script export_description <handle>
+
+The script should print a human-readable description of the disk image
+on stdout. If the description ends with a newline character then the
+newline is removed.
+
+This method is I<not> required; if it is absent, no export description
+will be provided to the client.
+
=item C<get_size>
/path/to/script get_size <handle>
diff --git a/include/nbdkit-filter.h b/include/nbdkit-filter.h
index 63458d59..afe83e0b 100644
--- a/include/nbdkit-filter.h
+++ b/include/nbdkit-filter.h
@@ -80,6 +80,7 @@ struct nbdkit_next_ops {
/* The rest of the next ops are the same as normal plugin operations. */
int64_t (*get_size) (nbdkit_backend *nxdata);
+ const char * (*export_description) (nbdkit_backend *nxdata);
int (*can_write) (nbdkit_backend *nxdata);
int (*can_flush) (nbdkit_backend *nxdata);
@@ -195,6 +196,8 @@ struct nbdkit_filter {
int64_t (*get_size) (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,
void *handle);
+ const char * (*export_description) (struct nbdkit_next_ops *next_ops,
+ nbdkit_backend *nxdata, void *handle);
int (*can_write) (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,
void *handle);
diff --git a/include/nbdkit-plugin.h b/include/nbdkit-plugin.h
index 539e3f6e..db010a5a 100644
--- a/include/nbdkit-plugin.h
+++ b/include/nbdkit-plugin.h
@@ -143,6 +143,7 @@ struct nbdkit_plugin {
int (*list_exports) (int readonly, int is_tls,
struct nbdkit_exports *exports);
const char * (*default_export) (int readonly, int is_tls);
+ const char * (*export_description) (void *handle);
};
NBDKIT_EXTERN_DECL (void, nbdkit_set_error, (int err));
diff --git a/server/internal.h b/server/internal.h
index fe485117..e7619469 100644
--- a/server/internal.h
+++ b/server/internal.h
@@ -379,6 +379,7 @@ struct backend {
int (*finalize) (struct backend *, void *handle);
void (*close) (struct backend *, void *handle);
+ const char *(*export_description) (struct backend *, void *handle);
int64_t (*get_size) (struct backend *, void *handle);
int (*can_write) (struct backend *, void *handle);
int (*can_flush) (struct backend *, void *handle);
@@ -423,6 +424,8 @@ extern int backend_list_exports (struct backend *b, int readonly,
__attribute__((__nonnull__ (1, 3)));
extern const char *backend_default_export (struct backend *b, int readonly)
__attribute__((__nonnull__ (1)));
+extern const char *backend_export_description (struct backend *b)
+ __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.
diff --git a/server/backend.c b/server/backend.c
index 2023c40b..89bf1015 100644
--- a/server/backend.c
+++ b/server/backend.c
@@ -360,6 +360,28 @@ backend_reopen (struct backend *b, int readonly, const char
*exportname)
return 0;
}
+const char *
+backend_export_description (struct backend *b)
+{
+ GET_CONN;
+ struct handle *h = get_handle (conn, b->i);
+ const char *s;
+
+ controlpath_debug ("%s: export_description", b->name);
+
+ assert (h->handle && (h->state & HANDLE_CONNECTED));
+ /* Caching is not useful for this value. */
+ s = b->export_description (b, h->handle);
+
+ /* Ignore over-length strings. XXX Also ignore non-UTF8? */
+ if (s && strnlen (s, NBD_MAX_STRING + 1) > NBD_MAX_STRING) {
+ controlpath_debug ("%s: export_description: ignoring invalid string",
+ b->name);
+ s = NULL;
+ }
+ return s;
+}
+
int64_t
backend_get_size (struct backend *b)
{
diff --git a/server/filters.c b/server/filters.c
index dd4417be..54abf9a4 100644
--- a/server/filters.c
+++ b/server/filters.c
@@ -291,6 +291,7 @@ filter_close (struct backend *b, void *handle)
static struct nbdkit_next_ops next_ops = {
.reopen = backend_reopen,
+ .export_description = backend_export_description,
.get_size = backend_get_size,
.can_write = backend_can_write,
.can_flush = backend_can_flush,
@@ -334,6 +335,17 @@ filter_finalize (struct backend *b, void *handle)
return 0;
}
+static const char *
+filter_export_description (struct backend *b, void *handle)
+{
+ struct backend_filter *f = container_of (b, struct backend_filter, backend);
+
+ if (f->filter.export_description)
+ return f->filter.export_description (&next_ops, b->next, handle);
+ else
+ return backend_export_description (b->next);
+}
+
static int64_t
filter_get_size (struct backend *b, void *handle)
{
@@ -571,6 +583,7 @@ static struct backend filter_functions = {
.prepare = filter_prepare,
.finalize = filter_finalize,
.close = filter_close,
+ .export_description = filter_export_description,
.get_size = filter_get_size,
.can_write = filter_can_write,
.can_flush = filter_can_flush,
diff --git a/server/plugins.c b/server/plugins.c
index 8061b2e8..38db3a89 100644
--- a/server/plugins.c
+++ b/server/plugins.c
@@ -167,6 +167,7 @@ plugin_dump_fields (struct backend *b)
HAS (open);
HAS (close);
+ HAS (export_description);
HAS (get_size);
HAS (can_write);
HAS (can_flush);
@@ -366,6 +367,17 @@ plugin_close (struct backend *b, void *handle)
conn->exportname = NULL;
}
+static const char *
+plugin_export_description (struct backend *b, void *handle)
+{
+ struct backend_plugin *p = container_of (b, struct backend_plugin, backend);
+
+ if (p->plugin.export_description)
+ return p->plugin.export_description (handle);
+ else
+ return NULL;
+}
+
static int64_t
plugin_get_size (struct backend *b, void *handle)
{
@@ -772,6 +784,7 @@ static struct backend plugin_functions = {
.prepare = plugin_prepare,
.finalize = plugin_finalize,
.close = plugin_close,
+ .export_description = plugin_export_description,
.get_size = plugin_get_size,
.can_write = plugin_can_write,
.can_flush = plugin_can_flush,
diff --git a/server/protocol-handshake-newstyle.c b/server/protocol-handshake-newstyle.c
index 7a034360..6d3e53c7 100644
--- a/server/protocol-handshake-newstyle.c
+++ b/server/protocol-handshake-newstyle.c
@@ -563,10 +563,10 @@ negotiate_handshake_newstyle_options (void)
exportsize) == -1)
return -1;
- /* For now we send NBD_INFO_NAME if requested, and ignore all
- * other info requests (including NBD_INFO_EXPORT if it was
- * requested, because we replied already above).
- * XXX NBD_INFO_DESCRIPTION is easy once we add .export_description.
+ /* For now we send NBD_INFO_NAME and NBD_INFO_DESCRIPTION if
+ * requested, and ignore all other info requests (including
+ * NBD_INFO_EXPORT if it was requested, because we replied
+ * already above).
*/
for (i = 0; i < nrinfos; ++i) {
memcpy (&info, &data[4 + exportnamelen + 2 + i*2], 2);
@@ -594,6 +594,23 @@ negotiate_handshake_newstyle_options (void)
return -1;
}
break;
+ case NBD_INFO_DESCRIPTION:
+ {
+ const char *desc = backend_export_description (top);
+
+ if (!desc) {
+ debug ("newstyle negotiation: %s: "
+ "NBD_INFO_DESCRIPTION: no description to send",
+ optname);
+ break;
+ }
+ if (send_newstyle_option_reply_info_str (option,
+ NBD_REP_INFO,
+ NBD_INFO_DESCRIPTION,
+ desc, -1) == -1)
+ return -1;
+ }
+ break;
default:
debug ("newstyle negotiation: %s: "
"ignoring NBD_INFO_* request %u (%s)",
diff --git a/plugins/sh/methods.h b/plugins/sh/methods.h
index 3fd4ef42..42eb560c 100644
--- a/plugins/sh/methods.h
+++ b/plugins/sh/methods.h
@@ -49,6 +49,7 @@ extern int sh_list_exports (int readonly, int default_only,
extern const char *sh_default_export (int readonly, int is_tls);
extern void *sh_open (int readonly);
extern void sh_close (void *handle);
+extern const char *sh_export_description (void *handle);
extern int64_t sh_get_size (void *handle);
extern int sh_pread (void *handle, void *buf, uint32_t count, uint64_t offset,
uint32_t flags);
diff --git a/plugins/cc/cc.c b/plugins/cc/cc.c
index 381d1f7e..ae641088 100644
--- a/plugins/cc/cc.c
+++ b/plugins/cc/cc.c
@@ -377,6 +377,14 @@ cc_close (void *handle)
subplugin.close (handle);
}
+static const char *
+cc_export_description (void *handle)
+{
+ if (subplugin.export_description)
+ return subplugin.export_description (handle);
+ return NULL;
+}
+
static int64_t
cc_get_size (void *handle)
{
@@ -575,34 +583,35 @@ static struct nbdkit_plugin plugin = {
/* And we must provide callbacks for everything else, which are
* passed through to the subplugin.
*/
- .get_ready = cc_get_ready,
- .after_fork = cc_after_fork,
+ .get_ready = cc_get_ready,
+ .after_fork = cc_after_fork,
- .preconnect = cc_preconnect,
- .list_exports = cc_list_exports,
- .default_export = cc_default_export,
- .open = cc_open,
- .close = cc_close,
+ .preconnect = cc_preconnect,
+ .list_exports = cc_list_exports,
+ .default_export = cc_default_export,
+ .open = cc_open,
+ .close = cc_close,
- .get_size = cc_get_size,
- .can_write = cc_can_write,
- .can_flush = cc_can_flush,
- .is_rotational = cc_is_rotational,
- .can_trim = cc_can_trim,
- .can_zero = cc_can_zero,
- .can_fast_zero = cc_can_fast_zero,
- .can_extents = cc_can_extents,
- .can_fua = cc_can_fua,
- .can_multi_conn = cc_can_multi_conn,
- .can_cache = cc_can_cache,
+ .export_description = cc_export_description,
+ .get_size = cc_get_size,
+ .can_write = cc_can_write,
+ .can_flush = cc_can_flush,
+ .is_rotational = cc_is_rotational,
+ .can_trim = cc_can_trim,
+ .can_zero = cc_can_zero,
+ .can_fast_zero = cc_can_fast_zero,
+ .can_extents = cc_can_extents,
+ .can_fua = cc_can_fua,
+ .can_multi_conn = cc_can_multi_conn,
+ .can_cache = cc_can_cache,
- .pread = cc_pread,
- .pwrite = cc_pwrite,
- .flush = cc_flush,
- .trim = cc_trim,
- .zero = cc_zero,
- .extents = cc_extents,
- .cache = cc_cache,
+ .pread = cc_pread,
+ .pwrite = cc_pwrite,
+ .flush = cc_flush,
+ .trim = cc_trim,
+ .zero = cc_zero,
+ .extents = cc_extents,
+ .cache = cc_cache,
.errno_is_preserved = 1,
};
diff --git a/plugins/eval/eval.c b/plugins/eval/eval.c
index a123734e..ae84bd40 100644
--- a/plugins/eval/eval.c
+++ b/plugins/eval/eval.c
@@ -70,6 +70,7 @@ static const char *known_methods[] = {
"config_complete",
"default_export",
"dump_plugin",
+ "export_description",
"extents",
"flush",
"get_ready",
@@ -380,45 +381,46 @@ eval_config_complete (void)
#define THREAD_MODEL NBDKIT_THREAD_MODEL_PARALLEL
static struct nbdkit_plugin plugin = {
- .name = "eval",
- .version = PACKAGE_VERSION,
- .load = eval_load,
- .unload = eval_unload,
+ .name = "eval",
+ .version = PACKAGE_VERSION,
+ .load = eval_load,
+ .unload = eval_unload,
- .dump_plugin = sh_dump_plugin,
+ .dump_plugin = sh_dump_plugin,
- .config = eval_config,
- .config_complete = eval_config_complete,
- .config_help = eval_config_help,
- .thread_model = sh_thread_model,
- .get_ready = sh_get_ready,
- .after_fork = sh_after_fork,
+ .config = eval_config,
+ .config_complete = eval_config_complete,
+ .config_help = eval_config_help,
+ .thread_model = sh_thread_model,
+ .get_ready = sh_get_ready,
+ .after_fork = sh_after_fork,
- .preconnect = sh_preconnect,
- .list_exports = sh_list_exports,
- .default_export = sh_default_export,
- .open = sh_open,
- .close = sh_close,
+ .preconnect = sh_preconnect,
+ .list_exports = sh_list_exports,
+ .default_export = sh_default_export,
+ .open = sh_open,
+ .close = sh_close,
- .get_size = sh_get_size,
- .can_write = sh_can_write,
- .can_flush = sh_can_flush,
- .is_rotational = sh_is_rotational,
- .can_trim = sh_can_trim,
- .can_zero = sh_can_zero,
- .can_extents = sh_can_extents,
- .can_fua = sh_can_fua,
- .can_multi_conn = sh_can_multi_conn,
- .can_cache = sh_can_cache,
- .can_fast_zero = sh_can_fast_zero,
+ .export_description = sh_export_description,
+ .get_size = sh_get_size,
+ .can_write = sh_can_write,
+ .can_flush = sh_can_flush,
+ .is_rotational = sh_is_rotational,
+ .can_trim = sh_can_trim,
+ .can_zero = sh_can_zero,
+ .can_extents = sh_can_extents,
+ .can_fua = sh_can_fua,
+ .can_multi_conn = sh_can_multi_conn,
+ .can_cache = sh_can_cache,
+ .can_fast_zero = sh_can_fast_zero,
- .pread = sh_pread,
- .pwrite = sh_pwrite,
- .flush = sh_flush,
- .trim = sh_trim,
- .zero = sh_zero,
- .extents = sh_extents,
- .cache = sh_cache,
+ .pread = sh_pread,
+ .pwrite = sh_pwrite,
+ .flush = sh_flush,
+ .trim = sh_trim,
+ .zero = sh_zero,
+ .extents = sh_extents,
+ .cache = sh_cache,
.errno_is_preserved = 1,
};
diff --git a/plugins/sh/methods.c b/plugins/sh/methods.c
index a0446fb2..beafcd25 100644
--- a/plugins/sh/methods.c
+++ b/plugins/sh/methods.c
@@ -457,6 +457,38 @@ sh_close (void *handle)
}
}
+const char *
+sh_export_description (void *handle)
+{
+ const char *method = "export_description";
+ const char *script = get_script (method);
+ struct sh_handle *h = handle;
+ const char *args[] = { script, method, h->h, NULL };
+ CLEANUP_FREE char *s = NULL;
+ size_t slen;
+
+ switch (call_read (&s, &slen, args)) {
+ case OK:
+ if (slen > 0 && s[slen-1] == '\n')
+ s[slen-1] = '\0';
+ return nbdkit_strdup_intern (s);
+
+ case MISSING:
+ return NULL;
+
+ case ERROR:
+ return NULL;
+
+ case RET_FALSE:
+ nbdkit_error ("%s: %s method returned unexpected code (3/false)",
+ script, method);
+ errno = EIO;
+ return NULL;
+
+ default: abort ();
+ }
+}
+
int64_t
sh_get_size (void *handle)
{
diff --git a/plugins/sh/sh.c b/plugins/sh/sh.c
index 4fcc2a5a..db75d386 100644
--- a/plugins/sh/sh.c
+++ b/plugins/sh/sh.c
@@ -284,46 +284,47 @@ sh_config_complete (void)
#define THREAD_MODEL NBDKIT_THREAD_MODEL_PARALLEL
static struct nbdkit_plugin plugin = {
- .name = "sh",
- .version = PACKAGE_VERSION,
- .load = sh_load,
- .unload = sh_unload,
+ .name = "sh",
+ .version = PACKAGE_VERSION,
+ .load = sh_load,
+ .unload = sh_unload,
- .dump_plugin = sh_dump_plugin,
+ .dump_plugin = sh_dump_plugin,
- .config = sh_config,
- .config_complete = sh_config_complete,
- .config_help = sh_config_help,
- .magic_config_key = "script",
- .thread_model = sh_thread_model,
- .get_ready = sh_get_ready,
- .after_fork = sh_after_fork,
+ .config = sh_config,
+ .config_complete = sh_config_complete,
+ .config_help = sh_config_help,
+ .magic_config_key = "script",
+ .thread_model = sh_thread_model,
+ .get_ready = sh_get_ready,
+ .after_fork = sh_after_fork,
- .preconnect = sh_preconnect,
- .list_exports = sh_list_exports,
- .default_export = sh_default_export,
- .open = sh_open,
- .close = sh_close,
+ .preconnect = sh_preconnect,
+ .list_exports = sh_list_exports,
+ .default_export = sh_default_export,
+ .open = sh_open,
+ .close = sh_close,
- .get_size = sh_get_size,
- .can_write = sh_can_write,
- .can_flush = sh_can_flush,
- .is_rotational = sh_is_rotational,
- .can_trim = sh_can_trim,
- .can_zero = sh_can_zero,
- .can_extents = sh_can_extents,
- .can_fua = sh_can_fua,
- .can_multi_conn = sh_can_multi_conn,
- .can_cache = sh_can_cache,
- .can_fast_zero = sh_can_fast_zero,
+ .export_description = sh_export_description,
+ .get_size = sh_get_size,
+ .can_write = sh_can_write,
+ .can_flush = sh_can_flush,
+ .is_rotational = sh_is_rotational,
+ .can_trim = sh_can_trim,
+ .can_zero = sh_can_zero,
+ .can_extents = sh_can_extents,
+ .can_fua = sh_can_fua,
+ .can_multi_conn = sh_can_multi_conn,
+ .can_cache = sh_can_cache,
+ .can_fast_zero = sh_can_fast_zero,
- .pread = sh_pread,
- .pwrite = sh_pwrite,
- .flush = sh_flush,
- .trim = sh_trim,
- .zero = sh_zero,
- .extents = sh_extents,
- .cache = sh_cache,
+ .pread = sh_pread,
+ .pwrite = sh_pwrite,
+ .flush = sh_flush,
+ .trim = sh_trim,
+ .zero = sh_zero,
+ .extents = sh_extents,
+ .cache = sh_cache,
.errno_is_preserved = 1,
};
diff --git a/filters/tls-fallback/tls-fallback.c b/filters/tls-fallback/tls-fallback.c
index c1e549df..bec983be 100644
--- a/filters/tls-fallback/tls-fallback.c
+++ b/filters/tls-fallback/tls-fallback.c
@@ -119,6 +119,15 @@ tls_fallback_open (nbdkit_next_open *next, void *nxdata, int
readonly,
* can_zero, can_fast_zero, and can_fua).
*/
+static const char *
+tls_fallback_export_description (struct nbdkit_next_ops *next_ops,
+ void *nxdata, void *handle)
+{
+ if (NOT_TLS)
+ return NULL;
+ return next_ops->export_description (nxdata);
+}
+
static int64_t
tls_fallback_get_size (struct nbdkit_next_ops *next_ops, void *nxdata,
void *handle)
@@ -195,22 +204,23 @@ tls_fallback_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
}
static struct nbdkit_filter filter = {
- .name = "tls-fallback",
- .longname = "nbdkit tls-fallback filter",
- .config = tls_fallback_config,
- .config_help = tls_fallback_config_help,
- .get_ready = tls_fallback_get_ready,
- .list_exports = tls_fallback_list_exports,
- .default_export = tls_fallback_default_export,
- .open = tls_fallback_open,
- .get_size = tls_fallback_get_size,
- .can_write = tls_fallback_can_write,
- .can_flush = tls_fallback_can_flush,
- .is_rotational = tls_fallback_is_rotational,
- .can_extents = tls_fallback_can_extents,
- .can_multi_conn = tls_fallback_can_multi_conn,
- .can_cache = tls_fallback_can_cache,
- .pread = tls_fallback_pread,
+ .name = "tls-fallback",
+ .longname = "nbdkit tls-fallback filter",
+ .config = tls_fallback_config,
+ .config_help = tls_fallback_config_help,
+ .get_ready = tls_fallback_get_ready,
+ .list_exports = tls_fallback_list_exports,
+ .default_export = tls_fallback_default_export,
+ .open = tls_fallback_open,
+ .export_description = tls_fallback_export_description,
+ .get_size = tls_fallback_get_size,
+ .can_write = tls_fallback_can_write,
+ .can_flush = tls_fallback_can_flush,
+ .is_rotational = tls_fallback_is_rotational,
+ .can_extents = tls_fallback_can_extents,
+ .can_multi_conn = tls_fallback_can_multi_conn,
+ .can_cache = tls_fallback_can_cache,
+ .pread = tls_fallback_pread,
};
NBDKIT_REGISTER_FILTER(filter)
diff --git a/tests/test-export-info.sh b/tests/test-export-info.sh
index 984d86c8..5d390835 100755
--- a/tests/test-export-info.sh
+++ b/tests/test-export-info.sh
@@ -43,7 +43,6 @@ rm -f $files
cleanup_fn rm -f $files
# Create an nbdkit sh plugin for checking NBD_INFO replies to NBD_OPT_GO.
-# XXX Update when .export_description is implemented in sh
start_nbdkit -P export-info.pid -U $sock \
sh - <<'EOF'
case "$1" in
@@ -59,36 +58,34 @@ EOF
nbdsh -c '
import os
+def must_fail (f, *args, **kwds):
+ try:
+ f ( *args, **kwds)
+ assert False
+ except nbd.Error as ex:
+ pass
+
h.set_opt_mode (True)
h.connect_unix (os.environ["sock"])
h.set_export_name ("a")
h.opt_info ()
-try:
- h.get_canonical_export_name ()
- assert False
-except nbd.Error as ex:
- pass
+must_fail (h.get_canonical_export_name)
+must_fail (h.get_export_description)
h.set_export_name ("")
h.opt_info ()
-try:
- h.get_canonical_export_name ()
- assert False
-except nbd.Error as ex:
- pass
+must_fail (h.get_canonical_export_name)
+must_fail (h.get_export_description)
h.opt_go ()
-try:
- h.get_canonical_export_name ()
- assert False
-except nbd.Error as ex:
- pass
+must_fail (h.get_canonical_export_name)
+must_fail (h.get_export_description)
h.shutdown ()
'
-# With client request, reflect the export name back
+# With client request, reflect the export name and description back
nbdsh -c '
import os
@@ -99,13 +96,16 @@ h.connect_unix (os.environ["sock"])
h.set_export_name ("a")
h.opt_info ()
assert h.get_canonical_export_name () == "a"
+assert h.get_export_description () == "a world"
h.set_export_name ("")
h.opt_info ()
assert h.get_canonical_export_name () == "hello"
+assert h.get_export_description () == "hello world"
h.opt_go ()
assert h.get_canonical_export_name () == "hello"
+assert h.get_export_description () == "hello world"
h.shutdown ()
'
diff --git a/tests/test-tls-fallback.sh b/tests/test-tls-fallback.sh
index fb232bf9..b6c92918 100755
--- a/tests/test-tls-fallback.sh
+++ b/tests/test-tls-fallback.sh
@@ -93,6 +93,11 @@ h.connect_unix (os.environ["sock"])
assert h.opt_list (f) == 1
h.opt_info ()
assert h.get_canonical_export_name() == ""
+try:
+ h.get_export_description()
+ assert False
+except nbd.Error as ex:
+ pass
h.set_export_name ("hello")
h.opt_go ()
assert h.get_size () == 512
@@ -121,6 +126,7 @@ h.connect_unix (os.environ["sock"])
assert h.opt_list (f) == 2
h.opt_info ()
assert h.get_canonical_export_name() == "hello"
+assert h.get_export_description() == "=hello="
h.opt_go ()
assert h.get_size () == 6
assert h.can_trim ()
diff --git a/TODO b/TODO
index e4dd072f..ff902c7d 100644
--- a/TODO
+++ b/TODO
@@ -71,11 +71,6 @@ General ideas for improvements
continue to keep their non-standard handshake while utilizing nbdkit
to prototype new behaviors in serving the kernel.
-* At present, we ignore NBD_INFO_DESCRIPTION requests from a client
- during NBD_OPT_GO. This may be easiest if we add a new
- .get_description callback, called after .open alongside .get_size
- and .can_write.
-
* Background thread for filters. Some filters (readahead, cache and
proposed scan filter - see below) could be more effective if they
were able to defer work to a background thread. However it's not as
--
2.28.0