The NBD protocol is adding an extension to let servers advertise
initialization state to the client: whether the image contains holes,
and whether it is known to read as all zeroes. For shell-based
plugins (sh and eval), it's relatively straightforward to expose the
two new functions. Likewise, testing with the eval plugin is rather
easy.
Signed-off-by: Eric Blake <eblake(a)redhat.com>
---
plugins/eval/eval.c | 4 +++
plugins/eval/nbdkit-eval-plugin.pod | 9 +++--
plugins/sh/example.sh | 14 ++++++++
plugins/sh/methods.c | 18 +++++++++-
plugins/sh/methods.h | 4 ++-
plugins/sh/nbdkit-sh-plugin.pod | 8 ++++-
plugins/sh/sh.c | 4 ++-
tests/Makefile.am | 3 +-
tests/test-eval-init.sh | 54 +++++++++++++++++++++++++++++
9 files changed, 111 insertions(+), 7 deletions(-)
create mode 100755 tests/test-eval-init.sh
diff --git a/plugins/eval/eval.c b/plugins/eval/eval.c
index 094cac5..0c26628 100644
--- a/plugins/eval/eval.c
+++ b/plugins/eval/eval.c
@@ -71,6 +71,8 @@ static const char *known_methods[] = {
"extents",
"flush",
"get_size",
+ "init_sparse",
+ "init_zero",
"is_rotational",
"missing",
"open",
@@ -419,6 +421,8 @@ static struct nbdkit_plugin plugin = {
.can_multi_conn = sh_can_multi_conn,
.can_cache = sh_can_cache,
.can_fast_zero = sh_can_fast_zero,
+ .init_sparse = sh_init_sparse,
+ .init_zero = sh_init_zero,
.pread = sh_pread,
.pwrite = sh_pwrite,
diff --git a/plugins/eval/nbdkit-eval-plugin.pod b/plugins/eval/nbdkit-eval-plugin.pod
index cbb4133..48937de 100644
--- a/plugins/eval/nbdkit-eval-plugin.pod
+++ b/plugins/eval/nbdkit-eval-plugin.pod
@@ -51,7 +51,8 @@ all other methods are identical).
Create a 64M read-only disk of zeroes:
nbdkit eval get_size=' echo 64M ' \
- pread=' dd if=/dev/zero count=$3 iflag=count_bytes '
+ pread=' dd if=/dev/zero count=$3 iflag=count_bytes ' \
+ init_zero=' exit 0 '
The following command is the eval plugin equivalent of
L<nbdkit-file-plugin(1)> (except not as fast and missing many
@@ -102,6 +103,10 @@ features):
=item B<get_size=>SCRIPT
+=item B<init_sparse=>SCRIPT
+
+=item B<init_zero=>SCRIPT
+
=item B<is_rotational=>SCRIPT
=item B<open=>SCRIPT
@@ -191,4 +196,4 @@ Richard W.M. Jones
=head1 COPYRIGHT
-Copyright (C) 2019 Red Hat Inc.
+Copyright (C) 2019-2020 Red Hat Inc.
diff --git a/plugins/sh/example.sh b/plugins/sh/example.sh
index 99e4e89..638fea0 100755
--- a/plugins/sh/example.sh
+++ b/plugins/sh/example.sh
@@ -184,6 +184,20 @@ case "$1" in
echo "file"
;;
+ init_sparse)
+ # Return true if file is known to be sparse at client open.
+ # This heuristic has false negatives, but gets many cases right.
+ test 1 == $(( $(stat -L -c '%b * %B < %s' $f) )) || exit 3
+ ;;
+
+ init_zero)
+ # Return true if file has all zero content at client open.
+ # A slower 'cmp $f /dev/zero' might catch more cases.
+ # Be careful: blindly returning true may break a second client
+ # if a first client modifies the file.
+ test 0 == $(stat -L -c '%b' $f) || exit 3
+ ;;
+
*)
# Unknown methods must exit with code 2.
exit 2
diff --git a/plugins/sh/methods.c b/plugins/sh/methods.c
index ccedc6c..28112eb 100644
--- a/plugins/sh/methods.c
+++ b/plugins/sh/methods.c
@@ -1,5 +1,5 @@
/* nbdkit
- * Copyright (C) 2018-2019 Red Hat Inc.
+ * Copyright (C) 2018-2020 Red Hat Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -568,6 +568,22 @@ sh_can_fast_zero (void *handle)
return !r;
}
+int
+sh_init_sparse (void *handle)
+{
+ const char *method = "init_sparse";
+ const char *script = get_script (method);
+ return boolean_method (script, method, handle, 0);
+}
+
+int
+sh_init_zero (void *handle)
+{
+ const char *method = "init_zero";
+ const char *script = get_script (method);
+ return boolean_method (script, method, handle, 0);
+}
+
int
sh_flush (void *handle, uint32_t flags)
{
diff --git a/plugins/sh/methods.h b/plugins/sh/methods.h
index c5da214..31a8124 100644
--- a/plugins/sh/methods.h
+++ b/plugins/sh/methods.h
@@ -1,5 +1,5 @@
/* nbdkit
- * Copyright (C) 2018 Red Hat Inc.
+ * Copyright (C) 2018-2020 Red Hat Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -58,6 +58,8 @@ extern int sh_can_fua (void *handle);
extern int sh_can_multi_conn (void *handle);
extern int sh_can_cache (void *handle);
extern int sh_can_fast_zero (void *handle);
+extern int sh_init_sparse (void *handle);
+extern int sh_init_zero (void *handle);
extern int sh_flush (void *handle, uint32_t flags);
extern int sh_trim (void *handle, uint32_t count, uint64_t offset,
uint32_t flags);
diff --git a/plugins/sh/nbdkit-sh-plugin.pod b/plugins/sh/nbdkit-sh-plugin.pod
index 0c4f2f8..913a6b1 100644
--- a/plugins/sh/nbdkit-sh-plugin.pod
+++ b/plugins/sh/nbdkit-sh-plugin.pod
@@ -307,8 +307,14 @@ The script should exit with code C<0> for true or code
C<3> for false.
=item C<can_fast_zero>
+=item C<init_sparse>
+
+=item C<init_zero>
+
/path/to/script is_rotational <handle>
/path/to/script can_fast_zero <handle>
+ /path/to/script init_sparse <handle>
+ /path/to/script init_zero <handle>
The script should exit with code C<0> for true or code C<3> for false.
@@ -467,4 +473,4 @@ Richard W.M. Jones
=head1 COPYRIGHT
-Copyright (C) 2018-2019 Red Hat Inc.
+Copyright (C) 2018-2020 Red Hat Inc.
diff --git a/plugins/sh/sh.c b/plugins/sh/sh.c
index eddba8e..1aea290 100644
--- a/plugins/sh/sh.c
+++ b/plugins/sh/sh.c
@@ -1,5 +1,5 @@
/* nbdkit
- * Copyright (C) 2018-2019 Red Hat Inc.
+ * Copyright (C) 2018-2020 Red Hat Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -325,6 +325,8 @@ static struct nbdkit_plugin plugin = {
.can_multi_conn = sh_can_multi_conn,
.can_cache = sh_can_cache,
.can_fast_zero = sh_can_fast_zero,
+ .init_sparse = sh_init_sparse,
+ .init_zero = sh_init_zero,
.pread = sh_pread,
.pwrite = sh_pwrite,
diff --git a/tests/Makefile.am b/tests/Makefile.am
index a8ff601..ca659d7 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -110,6 +110,7 @@ EXTRA_DIST = \
test-error100.sh \
test-error-triggered.sh \
test-eval.sh \
+ test-eval-init.sh \
test-export-name.sh \
test-extentlist.sh \
test-file-extents.sh \
@@ -515,7 +516,7 @@ test_data_CFLAGS = $(WARNINGS_CFLAGS) $(LIBGUESTFS_CFLAGS)
test_data_LDADD = libtest.la $(LIBGUESTFS_LIBS)
# eval plugin test.
-TESTS += test-eval.sh
+TESTS += test-eval.sh test-eval-init.sh
# ext2 plugin test.
if HAVE_EXT2
diff --git a/tests/test-eval-init.sh b/tests/test-eval-init.sh
new file mode 100755
index 0000000..b72b1fc
--- /dev/null
+++ b/tests/test-eval-init.sh
@@ -0,0 +1,54 @@
+#!/usr/bin/env bash
+# nbdkit
+# Copyright (C) 2020 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 initial state of the eval plugin.
+
+source ./functions.sh
+set -e
+
+requires nbdsh -c 'exit (not hasattr (h, "get_init_flags"))'
+
+# All four combinations of initial flags. Relies on values of NBD protocol.
+for val in 0 1 2 3; do
+ nbdkit -v -U - eval \
+ get_size='echo 1024' \
+ pread='dd if=/dev/zero count=$3 iflag=count_bytes' \
+ init_sparse="exit $((3 * !($val & 1)))" \
+ init_zero="exit $((3 * !($val & 2)))" \
+ --run 'nbdsh --uri $uri -c "assert (h.get_init_flags () ==
'$val')"'
+done
+
+# Omitting is the same as returning false (the safe default).
+nbdkit -v -U - eval \
+ get_size='echo 1024' \
+ pread='dd if=/dev/zero count=$3 iflag=count_bytes' \
+ --run 'nbdsh --uri $uri -c "assert (h.get_init_flags () == 0)"'
--
2.24.1