The source for the easter egg example is not included, but it is:
org 0x7c00
; clear screen
mov ah,0
mov al,3
int 0x10
; print string
mov ah,0x13
mov bl,0xa
mov al,1
mov cx,len
mov dh,0
mov dl,0
mov bp,hello
int 0x10
hlt
hello: db "*** Hello from nbdkit! ***",0xd,0xa
len: equ $-hello
; pad to end of sector
times 510-($-$$) db 0
; boot signature
db 0x55,0xaa
---
configure.ac | 2 +
docs/nbdkit-plugin.pod | 2 +
plugins/data/nbdkit-data-plugin.pod | 1 +
plugins/memory/nbdkit-memory-plugin.pod | 3 +-
plugins/reflection/Makefile.am | 64 +++++
.../reflection/nbdkit-reflection-plugin.pod | 104 +++++++
plugins/reflection/reflection.c | 265 ++++++++++++++++++
tests/Makefile.am | 8 +
tests/test-reflection-base64.sh | 98 +++++++
tests/test-reflection-raw.sh | 68 +++++
10 files changed, 614 insertions(+), 1 deletion(-)
diff --git a/configure.ac b/configure.ac
index 4af403c..8261a7f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -846,6 +846,7 @@ non_lang_plugins="\
partitioning \
pattern \
random \
+ reflection \
split \
ssh \
streaming \
@@ -922,6 +923,7 @@ AC_CONFIG_FILES([Makefile
plugins/perl/Makefile
plugins/python/Makefile
plugins/random/Makefile
+ plugins/reflection/Makefile
plugins/ruby/Makefile
plugins/rust/Cargo.toml
plugins/rust/Makefile
diff --git a/docs/nbdkit-plugin.pod b/docs/nbdkit-plugin.pod
index e465410..39fa643 100644
--- a/docs/nbdkit-plugin.pod
+++ b/docs/nbdkit-plugin.pod
@@ -389,6 +389,8 @@ client data, be cautious when parsing it.>
On error, C<nbdkit_error> is called and the call returns C<NULL>.
+See also L<nbdkit-reflection-plugin(1)>.
+
=head1 CALLBACKS
=head2 C<.name>
diff --git a/plugins/data/nbdkit-data-plugin.pod b/plugins/data/nbdkit-data-plugin.pod
index b0957ad..fd342b4 100644
--- a/plugins/data/nbdkit-data-plugin.pod
+++ b/plugins/data/nbdkit-data-plugin.pod
@@ -240,6 +240,7 @@ L<nbdkit-null-plugin(1)>,
L<nbdkit-partitioning-plugin(1)>,
L<nbdkit-pattern-plugin(1)>,
L<nbdkit-random-plugin(1)>,
+L<nbdkit-reflection-plugin(1)>,
L<nbdkit-zero-plugin(1)>,
L<https://github.com/libguestfs/nbdkit/blob/master/plugins/data/disk2d...;,
L<https://en.wikipedia.org/wiki/Base64>.
diff --git a/plugins/memory/nbdkit-memory-plugin.pod
b/plugins/memory/nbdkit-memory-plugin.pod
index 76824d6..4503651 100644
--- a/plugins/memory/nbdkit-memory-plugin.pod
+++ b/plugins/memory/nbdkit-memory-plugin.pod
@@ -86,7 +86,8 @@ L<nbdkit(1)>,
L<nbdkit-plugin(3)>,
L<nbdkit-loop(1)>,
L<nbdkit-data-plugin(1)>,
-L<nbdkit-file-plugin(1)>.
+L<nbdkit-file-plugin(1)>,
+L<nbdkit-reflection-plugin(1)>.
=head1 AUTHORS
diff --git a/plugins/reflection/Makefile.am b/plugins/reflection/Makefile.am
new file mode 100644
index 0000000..40aa786
--- /dev/null
+++ b/plugins/reflection/Makefile.am
@@ -0,0 +1,64 @@
+# nbdkit
+# Copyright (C) 2017-2019 Red Hat Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# * Neither the name of Red Hat nor the names of its contributors may be
+# used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+include $(top_srcdir)/common-rules.mk
+
+EXTRA_DIST = nbdkit-reflection-plugin.pod
+
+plugin_LTLIBRARIES = nbdkit-reflection-plugin.la
+
+nbdkit_reflection_plugin_la_SOURCES = \
+ reflection.c \
+ $(top_srcdir)/include/nbdkit-plugin.h \
+ $(NULL)
+
+nbdkit_reflection_plugin_la_CPPFLAGS = \
+ -I$(top_srcdir)/include \
+ $(NULL)
+nbdkit_reflection_plugin_la_CFLAGS = $(WARNINGS_CFLAGS)
+nbdkit_reflection_plugin_la_LDFLAGS = \
+ -module -avoid-version -shared \
+ -Wl,--version-script=$(top_srcdir)/plugins/plugins.syms \
+ $(NULL)
+nbdkit_reflection_plugin_la_LIBADD = \
+ $(NULL)
+
+if HAVE_POD
+
+man_MANS = nbdkit-reflection-plugin.1
+CLEANFILES += $(man_MANS)
+
+nbdkit-reflection-plugin.1: nbdkit-reflection-plugin.pod
+ $(PODWRAPPER) --section=1 --man $@ \
+ --html $(top_builddir)/html/$@.html \
+ $<
+
+endif HAVE_POD
diff --git a/plugins/reflection/nbdkit-reflection-plugin.pod
b/plugins/reflection/nbdkit-reflection-plugin.pod
new file mode 100644
index 0000000..1b260b6
--- /dev/null
+++ b/plugins/reflection/nbdkit-reflection-plugin.pod
@@ -0,0 +1,104 @@
+=head1 NAME
+
+nbdkit-reflection-plugin - reflect export name back to the client
+
+=head1 SYNOPSIS
+
+ nbdkit reflection [mode=]exportname|base64exportname
+
+=head1 DESCRIPTION
+
+C<nbdkit-reflection-plugin> is a test plugin which reflects
+information sent by the client back to the client.
+
+In its default mode (C<mode=exportname>) it converts the export name
+passed from the client into a disk image. C<mode=base64exportname> is
+similar except the client must base64-encode the data in the export
+name, allowing arbitrary binary data to be sent (see L</EXAMPLES>
+below to make this clearer).
+
+The plugin only supports read-only access. To make the disk writable,
+add L<nbdkit-cow-filter(1)> on top.
+
+=head1 EXAMPLES
+
+Create a reflection disk. By setting the export name to C<"hello">
+when we open it, a virtual disk of only 5 bytes containing these
+characters is created. We then display the contents:
+
+ $ nbdkit reflection mode=exportname
+ $ nbdsh -u 'nbd://localhost/hello' -c - <<'EOF'
+ size = h.get_size()
+ print("size = %d" % size)
+ buf = h.pread(size, 0)
+ print("buf = %r" % buf)
+ EOF
+
+ size = 5
+ buf = b"hello"
+
+By running a reflection plugin, you can pass whole bootable VMs on the
+qemu command line:
+
+ $ nbdkit reflection mode=base64exportname
+ $ qemu-system-x86_64 \
+ -drive
'snapshot=on,file.driver=nbd,file.host=localhost,file.port=10809,file.export=
+ tACwA80QtBOzCrABuRwAtgCyAL0ZfM0Q9CoqKiBIZWxsbyBmcm9tIG5iZGtp
+ dCEgKioqDQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAVao=
+ '
+
+=head1 PARAMETERS
+
+=over 4
+
+=item [B<mode=>]B<base64exportname>
+
+Reflect the export name passed by the client, assuming the client
+string is base64 encoded.
+
+This mode is only supported if nbdkit was compiled with GnuTLS E<ge>
+3.6.0. You can find out by checking if:
+
+ $ nbdkit reflection --dump-plugin
+
+contains:
+
+ reflection_base64=yes
+
+=item [B<mode=>]B<exportname>
+
+Reflect the raw export name passed by the client. Note the export
+name cannot contain ASCII NUL characters.
+
+This is the default mode.
+
+C<mode=> is a magic config key and may be omitted in most cases.
+See L<nbdkit(1)/Magic parameters>.
+
+=back
+
+=head1 SEE ALSO
+
+L<nbdkit(1)>,
+L<nbdkit-plugin(3)>,
+L<nbdkit-cow-filter(1)>,
+L<nbdkit-data-plugin(1)>,
+L<nbdkit-memory-plugin(1)/Preloading small amounts of data>.
+
+=head1 AUTHORS
+
+Richard W.M. Jones
+
+=head1 COPYRIGHT
+
+Copyright (C) 2019 Red Hat Inc.
diff --git a/plugins/reflection/reflection.c b/plugins/reflection/reflection.c
new file mode 100644
index 0000000..74f7169
--- /dev/null
+++ b/plugins/reflection/reflection.c
@@ -0,0 +1,265 @@
+/* nbdkit
+ * Copyright (C) 2017-2019 Red Hat Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of Red Hat nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined(HAVE_GNUTLS) && defined(HAVE_GNUTLS_BASE64_DECODE2)
+#include <gnutls/gnutls.h>
+#define HAVE_BASE64 1
+#endif
+
+#define NBDKIT_API_VERSION 2
+
+#include <nbdkit-plugin.h>
+
+/* The mode. */
+enum mode {
+ MODE_EXPORTNAME,
+ MODE_BASE64EXPORTNAME,
+};
+static enum mode mode = MODE_EXPORTNAME;
+
+static int
+reflection_config (const char *key, const char *value)
+{
+ if (strcmp (key, "mode") == 0) {
+ if (strcasecmp (value, "exportname") == 0) {
+ mode = MODE_EXPORTNAME;
+ }
+ else if (strcasecmp (value, "base64exportname") == 0) {
+#ifdef HAVE_BASE64
+ mode = MODE_BASE64EXPORTNAME;
+#else
+ nbdkit_error ("the plugin was compiled without base64 support");
+ return -1;
+#endif
+ }
+ else {
+ nbdkit_error ("unknown mode: '%s'", value);
+ return -1;
+ }
+ }
+ else {
+ nbdkit_error ("unknown parameter '%s'", key);
+ return -1;
+ }
+
+ return 0;
+}
+
+#define reflection_config_help \
+ "mode=MODE Plugin mode."
+
+/* Provide a way to detect if the base64 feature is supported. */
+static void
+reflection_dump_plugin (void)
+{
+#ifdef HAVE_BASE64
+ printf ("reflection_base64=yes\n");
+#endif
+}
+
+/* Per-connection handle. */
+struct handle {
+ void *data; /* Block device data. */
+ size_t len; /* Length of data in bytes. */
+};
+
+static int
+decode_base64 (const char *data, size_t len, struct handle *ret)
+{
+#ifdef HAVE_BASE64
+ gnutls_datum_t in, out;
+ int err;
+
+ /* For unclear reasons gnutls_base64_decode2 won't handle an empty
+ * string, even though base64("") == "".
+ *
https://tools.ietf.org/html/rfc4648#section-10
+ *
https://gitlab.com/gnutls/gnutls/issues/834
+ * So we have to special-case it.
+ */
+ if (len == 0) {
+ ret->data = NULL;
+ ret->len = 0;
+ return 0;
+ }
+
+ in.data = (unsigned char *) data;
+ in.size = len;
+ err = gnutls_base64_decode2 (&in, &out);
+ if (err != GNUTLS_E_SUCCESS) {
+ nbdkit_error ("base64: %s", gnutls_strerror (err));
+ /* We don't have to free out.data. I verified that it is freed on
+ * the error path of gnutls_base64_decode2.
+ */
+ return -1;
+ }
+
+ ret->data = out.data; /* caller frees, eventually */
+ ret->len = out.size;
+ return 0;
+#else
+ nbdkit_error ("the plugin was compiled without base64 support");
+ return -1;
+#endif
+}
+
+/* Create the per-connection handle.
+ *
+ * This is a rather unusual plugin because it has to parse data sent
+ * by the client. For security reasons, be careful about:
+ *
+ * - Returning more data than is sent by the client.
+ *
+ * - Inputs that result in unbounded output.
+ *
+ * - Inputs that could hang, crash or exploit the server.
+ */
+static void *
+reflection_open (int readonly)
+{
+ const char *export_name;
+ size_t export_name_len;
+ struct handle *h;
+
+ h = malloc (sizeof *h);
+ if (h == NULL) {
+ nbdkit_error ("malloc: %m");
+ return NULL;
+ }
+
+ switch (mode) {
+ case MODE_EXPORTNAME:
+ case MODE_BASE64EXPORTNAME:
+ export_name = nbdkit_export_name ();
+ if (export_name == NULL) {
+ free (h);
+ return NULL;
+ }
+ export_name_len = strlen (export_name);
+
+ if (mode == MODE_EXPORTNAME) {
+ h->len = export_name_len;
+ h->data = strdup (export_name);
+ if (h->data == NULL) {
+ nbdkit_error ("strdup: %m");
+ free (h);
+ return NULL;
+ }
+ return h;
+ }
+ else /* mode == MODE_BASE64EXPORTNAME */ {
+ if (decode_base64 (export_name, export_name_len, h) == -1) {
+ free (h);
+ return NULL;
+ }
+ return h;
+ }
+
+ default:
+ abort ();
+ }
+}
+
+/* Close the per-connection handle. */
+static void
+reflection_close (void *handle)
+{
+ struct handle *h = handle;
+
+ free (h->data);
+ free (h);
+}
+
+#define THREAD_MODEL NBDKIT_THREAD_MODEL_PARALLEL
+
+/* Get the disk size. */
+static int64_t
+reflection_get_size (void *handle)
+{
+ struct handle *h = handle;
+
+ return (int64_t) h->len;
+}
+
+/* Read-only plugin so multi-conn is safe. */
+static int
+reflection_can_multi_conn (void *handle)
+{
+ return 1;
+}
+
+/* Cache. */
+static int
+reflection_can_cache (void *handle)
+{
+ /* Everything is already in memory, returning this without
+ * implementing .cache lets nbdkit do the correct no-op.
+ */
+ return NBDKIT_CACHE_NATIVE;
+}
+
+/* Read data. */
+static int
+reflection_pread (void *handle, void *buf, uint32_t count, uint64_t offset,
+ uint32_t flags)
+{
+ struct handle *h = handle;
+
+ memcpy (buf, h->data + offset, count);
+ return 0;
+}
+
+static struct nbdkit_plugin plugin = {
+ .name = "reflection",
+ .version = PACKAGE_VERSION,
+ .config = reflection_config,
+ .config_help = reflection_config_help,
+ .dump_plugin = reflection_dump_plugin,
+ .magic_config_key = "mode",
+ .open = reflection_open,
+ .close = reflection_close,
+ .get_size = reflection_get_size,
+ .can_multi_conn = reflection_can_multi_conn,
+ .can_cache = reflection_can_cache,
+ .pread = reflection_pread,
+ /* In this plugin, errno is preserved properly along error return
+ * paths from failed system calls.
+ */
+ .errno_is_preserved = 1,
+};
+
+NBDKIT_REGISTER_PLUGIN(plugin)
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 9eec75e..69d5d5e 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -111,6 +111,8 @@ EXTRA_DIST = \
test-rate-dynamic.sh \
test.rb \
test-readahead-copy.sh \
+ test-reflection-base64.sh \
+ test-reflection-raw.sh \
test-shutdown.sh \
test-ssh.sh \
test.tcl \
@@ -602,6 +604,12 @@ test_random_CPPFLAGS = -I $(top_srcdir)/common/include
test_random_CFLAGS = $(WARNINGS_CFLAGS) $(LIBGUESTFS_CFLAGS)
test_random_LDADD = libtest.la $(LIBGUESTFS_LIBS)
+# reflection plugin test.
+TESTS += \
+ test-reflection-base64.sh \
+ test-reflection-raw.sh \
+ $(NULL)
+
# split files plugin test.
check_DATA += split1 split2 split3
CLEANFILES += split1 split2 split3
diff --git a/tests/test-reflection-base64.sh b/tests/test-reflection-base64.sh
new file mode 100755
index 0000000..cf21bf0
--- /dev/null
+++ b/tests/test-reflection-base64.sh
@@ -0,0 +1,98 @@
+#!/usr/bin/env bash
+# nbdkit
+# Copyright (C) 2018-2019 Red Hat Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# * Neither the name of Red Hat nor the names of its contributors may be
+# used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+# Test the relection plugin with base64-encoded export name.
+
+source ./functions.sh
+set -e
+set -x
+
+requires nbdsh --version
+# XXX This needs to test $PYTHON somehow.
+#requires python -c 'import base64'
+
+# Test if mode=base64exportname is supported in this build.
+if ! nbdkit reflection --dump-plugin | grep -sq "reflection_base64=yes"; then
+ echo "$0: mode=base64exportname is not supported in this build"
+ exit 77
+fi
+
+sock=`mktemp -u`
+files="reflection-base64.out reflection-base64.pid $sock"
+rm -f $files
+cleanup_fn rm -f $files
+
+# Run nbdkit.
+start_nbdkit -P reflection-base64.pid -U $sock \
+ reflection mode=base64exportname
+
+export e sock
+for e in "" "test" "テスト" \
+
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+do
+ nbdsh -c '
+import os
+import base64
+
+e = os.environ["e"]
+b = base64.b64encode(e.encode("utf-8")).decode("utf-8")
+print ("e = %r, b = %r" % (e,b))
+h.set_export_name (b)
+h.connect_unix (os.environ["sock"])
+
+size = h.get_size ()
+assert size == len (e.encode("utf-8"))
+
+# Zero-sized reads are not defined in the NBD protocol.
+if size > 0:
+ buf = h.pread (size, 0)
+ assert buf == e.encode("utf-8")
+'
+done
+
+# Test that it fails if the caller passes in non-base64 data. The
+# server drops the connection in this case so it's not very graceful
+# but we should at least get an nbd.Error and not something else.
+nbdsh -c '
+import os
+import sys
+
+h.set_export_name ("xyz")
+try:
+ h.connect_unix (os.environ["sock"])
+ # This should not happen.
+ sys.exit (1)
+except nbd.Error as ex:
+ sys.exit (0)
+# This should not happen.
+sys.exit (1)
+'
diff --git a/tests/test-reflection-raw.sh b/tests/test-reflection-raw.sh
new file mode 100755
index 0000000..675e316
--- /dev/null
+++ b/tests/test-reflection-raw.sh
@@ -0,0 +1,68 @@
+#!/usr/bin/env bash
+# nbdkit
+# Copyright (C) 2018-2019 Red Hat Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# * Neither the name of Red Hat nor the names of its contributors may be
+# used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+# Test the relection plugin with raw export name.
+
+source ./functions.sh
+set -e
+set -x
+
+requires nbdsh --version
+
+sock=`mktemp -u`
+files="reflection-raw.out reflection-raw.pid $sock"
+rm -f $files
+cleanup_fn rm -f $files
+
+# Run nbdkit.
+start_nbdkit -P reflection-raw.pid -U $sock reflection
+
+for e in "" "test" "テスト" \
+
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+do
+ export e sock
+ nbdsh -c '
+import os
+
+e = os.environ["e"]
+h.set_export_name (e)
+h.connect_unix (os.environ["sock"])
+
+size = h.get_size ()
+assert size == len (e.encode("utf-8"))
+
+# Zero-sized reads are not defined in the NBD protocol.
+if size > 0:
+ buf = h.pread (size, 0)
+ assert buf == e.encode("utf-8")
+'
+done
--
2.23.0