commit fa44cefe07771aa22530cf816a5a0db220546bc8 Author: CentOS Sources Date: Tue Jun 9 20:49:57 2020 +0000 import nbdkit-1.16.2-2.module+el8.3.0+6423+e4cb6418 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9bf8ce5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +SOURCES/libguestfs.keyring +SOURCES/nbdkit-1.16.2.tar.gz diff --git a/.nbdkit.metadata b/.nbdkit.metadata new file mode 100644 index 0000000..fdcb45a --- /dev/null +++ b/.nbdkit.metadata @@ -0,0 +1,2 @@ +1bbc40f501a7fef9eef2a39b701a71aee2fea7c4 SOURCES/libguestfs.keyring +42a5761cd3403c02c43cdf7d541ff3faaf8b4769 SOURCES/nbdkit-1.16.2.tar.gz diff --git a/SOURCES/0001-server-Allow-D-nbdkit.-debug-flags-for-the-core-serv.patch b/SOURCES/0001-server-Allow-D-nbdkit.-debug-flags-for-the-core-serv.patch new file mode 100644 index 0000000..a1c635b --- /dev/null +++ b/SOURCES/0001-server-Allow-D-nbdkit.-debug-flags-for-the-core-serv.patch @@ -0,0 +1,74 @@ +From d7836fb0a7131c725e3c02be7e48e99c671637c3 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Thu, 12 Dec 2019 08:57:15 +0000 +Subject: [PATCH] server: Allow -D nbdkit.* debug flags for the core server. + +These work like plugin/filter debug flags, but apply to the internals +of the server. + +(cherry picked from commit 3b45db234a691f8ff926a6fef583e11c3601f112) +--- + docs/nbdkit.pod | 7 +++++++ + docs/synopsis.txt | 2 +- + server/main.c | 3 +++ + server/nbdkit.syms | 2 ++ + 4 files changed, 13 insertions(+), 1 deletion(-) + +diff --git a/docs/nbdkit.pod b/docs/nbdkit.pod +index a2e72b1..346d833 100644 +--- a/docs/nbdkit.pod ++++ b/docs/nbdkit.pod +@@ -177,6 +177,13 @@ Display brief command line usage information and exit. + Set the plugin or filter Debug Flag called C to the integer + value C. See L. + ++=item B<-D> nbdkit.FLAG=N ++ ++=item B<--debug> nbdkit.FLAG=N ++ ++Set the nbdkit server Debug Flag called C to the integer value ++C. ++ + =item B<--dump-config> + + Dump out the compile-time configuration values and exit. +diff --git a/docs/synopsis.txt b/docs/synopsis.txt +index 3c23937..c367542 100644 +--- a/docs/synopsis.txt ++++ b/docs/synopsis.txt +@@ -1,4 +1,4 @@ +-nbdkit [-D|--debug PLUGIN|FILTER.FLAG=N] ++nbdkit [-D|--debug PLUGIN|FILTER|nbdkit.FLAG=N] + [-e|--exportname EXPORTNAME] [--exit-with-parent] + [--filter FILTER ...] [-f|--foreground] + [-g|--group GROUP] [-i|--ipaddr IPADDR] +diff --git a/server/main.c b/server/main.c +index d39941b..11ba1e6 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -563,6 +563,9 @@ main (int argc, char *argv[]) + free (t); + } + ++ /* Apply nbdkit.* flags for the server. */ ++ apply_debug_flags (NULL, "nbdkit"); ++ + /* Check all debug flags were used, and free them. */ + free_debug_flags (); + +diff --git a/server/nbdkit.syms b/server/nbdkit.syms +index 390972e..96c22c0 100644 +--- a/server/nbdkit.syms ++++ b/server/nbdkit.syms +@@ -67,6 +67,8 @@ + nbdkit_vdebug; + nbdkit_verror; + ++ nbdkit_debug_*; ++ + # Everything else is hidden. + local: *; + }; +-- +2.18.2 + diff --git a/SOURCES/0002-server-Allow-D-debug-flags-to-contain-dots-for-names.patch b/SOURCES/0002-server-Allow-D-debug-flags-to-contain-dots-for-names.patch new file mode 100644 index 0000000..1a87086 --- /dev/null +++ b/SOURCES/0002-server-Allow-D-debug-flags-to-contain-dots-for-names.patch @@ -0,0 +1,66 @@ +From e5d2d44fff9214725506cbc84e7b3c035ec0eae9 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Thu, 12 Dec 2019 11:06:36 +0000 +Subject: [PATCH] server: Allow -D debug flags to contain dots for namespacing. + +This is just a convenience. Either of: + + -D myplugin.foo_bar=1 + -D myplugin.foo.bar=1 + +correspond to the same plugin variable "myplugin_debug_foo_bar". + +(cherry picked from commit a895fa84aaa50f52af68319523020046394c789f) +--- + docs/nbdkit-plugin.pod | 8 ++++++++ + server/debug-flags.c | 10 +++++++++- + 2 files changed, 17 insertions(+), 1 deletion(-) + +diff --git a/docs/nbdkit-plugin.pod b/docs/nbdkit-plugin.pod +index b69cb82..879ddf0 100644 +--- a/docs/nbdkit-plugin.pod ++++ b/docs/nbdkit-plugin.pod +@@ -1298,6 +1298,14 @@ You should only use this feature for debug settings. For general + settings use ordinary plugin parameters. Debug Flags can only be C + ints. They are not supported by non-C language plugins. + ++For convenience C<'.'> characters are replaced with C<'_'> characters ++in the variable name, so both of these parameters: ++ ++ -D myplugin.foo_bar=1 ++ -D myplugin.foo.bar=1 ++ ++correspond to the plugin variable C. ++ + =head1 INSTALLING THE PLUGIN + + The plugin is a C<*.so> file and possibly a manual page. You can of +diff --git a/server/debug-flags.c b/server/debug-flags.c +index 9344d85..5e06f5e 100644 +--- a/server/debug-flags.c ++++ b/server/debug-flags.c +@@ -56,12 +56,20 @@ static char * + symbol_of_debug_flag (const char *name, const char *flag) + { + char *var; ++ size_t i; ++ int len; + +- if (asprintf (&var, "%s_debug_%s", name, flag) == -1) { ++ if ((len = asprintf (&var, "%s_debug_%s", name, flag)) == -1) { + perror ("asprintf"); + exit (EXIT_FAILURE); + } + ++ /* If there are any '.'s remaining in the name, convert them to '_'. */ ++ for (i = 0; i < (size_t) len; ++i) { ++ if (var[i] == '.') ++ var[i] = '_'; ++ } ++ + return var; /* caller frees */ + } + +-- +2.18.2 + diff --git a/SOURCES/0003-server-Add-D-nbdkit.backend.controlpath-and-D-nbdkit.patch b/SOURCES/0003-server-Add-D-nbdkit.backend.controlpath-and-D-nbdkit.patch new file mode 100644 index 0000000..ce0a52f --- /dev/null +++ b/SOURCES/0003-server-Add-D-nbdkit.backend.controlpath-and-D-nbdkit.patch @@ -0,0 +1,451 @@ +From 83c72d9bf9d6a9ccf6939b4ebd0028b62673a78a Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Thu, 12 Dec 2019 10:57:52 +0000 +Subject: [PATCH] server: Add -D nbdkit.backend.controlpath and -D + nbdkit.backend.datapath. + +These can be used to suppress verbose debugging messages from the +backend. + +BugLink: https://bugzilla.redhat.com/1782868 + +Cherry picked from commit 231717e8cd5f27d76631be6651062d5a5ccf7fdc. +Remove use of nofilter from the test. +--- + docs/nbdkit.pod | 35 ++++++++++++- + server/backend.c | 83 ++++++++++++++++++------------ + tests/Makefile.am | 4 ++ + tests/test-nbdkit-backend-debug.sh | 70 +++++++++++++++++++++++++ + 4 files changed, 158 insertions(+), 34 deletions(-) + create mode 100755 tests/test-nbdkit-backend-debug.sh + +diff --git a/docs/nbdkit.pod b/docs/nbdkit.pod +index 346d833..38e6bfc 100644 +--- a/docs/nbdkit.pod ++++ b/docs/nbdkit.pod +@@ -182,7 +182,7 @@ value C. See L. + =item B<--debug> nbdkit.FLAG=N + + Set the nbdkit server Debug Flag called C to the integer value +-C. ++C. See L below. + + =item B<--dump-config> + +@@ -527,6 +527,39 @@ languages. The file should be executable. For example: + + (see L for a full example). + ++=head1 SERVER DEBUG FLAGS ++ ++As well as enabling or disabling debugging in the server using ++I<--verbose> you can control extra debugging in the server using the ++C<-D nbdkit.*> flags listed in this section. Note these flags are an ++internal implementation detail of the server and may be changed or ++removed at any time in the future. ++ ++=over 4 ++ ++=item B<-D nbdkit.backend.controlpath=0> ++ ++=item B<-D nbdkit.backend.controlpath=1> ++ ++=item B<-D nbdkit.backend.datapath=0> ++ ++=item B<-D nbdkit.backend.datapath=1> ++ ++These flags control the verbosity of nbdkit backend debugging messages ++(the ones which show every request processed by the server). The ++default for both settings is C<1> (normal debugging) but you can set ++them to C<0> to suppress these messages. ++ ++C<-D nbdkit.backend.datapath=0> is the more useful setting which lets you ++suppress messages about pread, pwrite, zero, trim, etc. commands. ++When transferring large amounts of data these messages are numerous ++and not usually very interesting. ++ ++C<-D nbdkit.backend.controlpath=0> suppresses the non-datapath ++commands (config, open, close, can_write, etc.) ++ ++=back ++ + =head1 SIGNALS + + nbdkit responds to the following signals: +diff --git a/server/backend.c b/server/backend.c +index b9fe2a2..208c07b 100644 +--- a/server/backend.c ++++ b/server/backend.c +@@ -46,6 +46,22 @@ + + /* Helpers for registering a new backend. */ + ++/* Use: ++ * -D nbdkit.backend.controlpath=0 to suppress control path debugging. ++ * -D nbdkit.backend.datapath=0 to suppress data path debugging. ++ */ ++int nbdkit_debug_backend_controlpath = 1; ++int nbdkit_debug_backend_datapath = 1; ++ ++#define controlpath_debug(fs, ...) \ ++ do { \ ++ if (nbdkit_debug_backend_controlpath) debug ((fs), ##__VA_ARGS__); \ ++ } while (0) ++#define datapath_debug(fs, ...) \ ++ do { \ ++ if (nbdkit_debug_backend_datapath) debug ((fs), ##__VA_ARGS__); \ ++ } while (0) ++ + void + backend_init (struct backend *b, struct backend *next, size_t index, + const char *filename, void *dl, const char *type) +@@ -108,7 +124,7 @@ backend_load (struct backend *b, const char *name, void (*load) (void)) + apply_debug_flags (b->dl, name); + + /* Call the on-load callback if it exists. */ +- debug ("%s: load", name); ++ controlpath_debug ("%s: load", name); + if (load) + load (); + } +@@ -121,7 +137,7 @@ backend_unload (struct backend *b, void (*unload) (void)) + */ + lock_unload (); + +- debug ("%s: unload %s", b->name, b->type); ++ controlpath_debug ("%s: unload %s", b->name, b->type); + if (unload) + unload (); + +@@ -139,7 +155,7 @@ backend_open (struct backend *b, struct connection *conn, int readonly) + { + struct b_conn_handle *h = &conn->handles[b->i]; + +- debug ("%s: open readonly=%d", b->name, readonly); ++ controlpath_debug ("%s: open readonly=%d", b->name, readonly); + + assert (h->handle == NULL); + assert ((h->state & HANDLE_OPEN) == 0); +@@ -151,7 +167,7 @@ backend_open (struct backend *b, struct connection *conn, int readonly) + * inner-to-outer ordering. + */ + h->handle = b->open (b, conn, readonly); +- debug ("%s: open returned handle %p", b->name, h->handle); ++ controlpath_debug ("%s: open returned handle %p", b->name, h->handle); + + if (h->handle == NULL) { + if (b->i) /* Do not strand backend if this layer failed */ +@@ -179,7 +195,7 @@ backend_prepare (struct backend *b, struct connection *conn) + if (b->i && backend_prepare (b->next, conn) == -1) + return -1; + +- debug ("%s: prepare readonly=%d", b->name, h->can_write == 0); ++ controlpath_debug ("%s: prepare readonly=%d", b->name, h->can_write == 0); + + if (b->prepare (b, conn, h->handle, h->can_write == 0) == -1) + return -1; +@@ -196,7 +212,7 @@ backend_finalize (struct backend *b, struct connection *conn) + * filter furthest away from the plugin, and matching .close order. + */ + +- debug ("%s: finalize", b->name); ++ controlpath_debug ("%s: finalize", b->name); + + /* Once finalize fails, we can do nothing further on this connection */ + if (h->state & HANDLE_FAILED) +@@ -223,7 +239,7 @@ backend_close (struct backend *b, struct connection *conn) + struct b_conn_handle *h = &conn->handles[b->i]; + + /* outer-to-inner order, opposite .open */ +- debug ("%s: close", b->name); ++ controlpath_debug ("%s: close", b->name); + + if (h->handle) { + assert (h->state & HANDLE_OPEN); +@@ -252,7 +268,7 @@ backend_valid_range (struct backend *b, struct connection *conn, + int + backend_reopen (struct backend *b, struct connection *conn, int readonly) + { +- debug ("%s: reopen readonly=%d", b->name, readonly); ++ controlpath_debug ("%s: reopen readonly=%d", b->name, readonly); + + if (backend_finalize (b, conn) == -1) + return -1; +@@ -274,7 +290,7 @@ backend_get_size (struct backend *b, struct connection *conn) + { + struct b_conn_handle *h = &conn->handles[b->i]; + +- debug ("%s: get_size", b->name); ++ controlpath_debug ("%s: get_size", b->name); + + assert (h->handle && (h->state & HANDLE_CONNECTED)); + if (h->exportsize == -1) +@@ -287,7 +303,7 @@ backend_can_write (struct backend *b, struct connection *conn) + { + struct b_conn_handle *h = &conn->handles[b->i]; + +- debug ("%s: can_write", b->name); ++ controlpath_debug ("%s: can_write", b->name); + + assert (h->handle && (h->state & HANDLE_CONNECTED)); + if (h->can_write == -1) +@@ -300,7 +316,7 @@ backend_can_flush (struct backend *b, struct connection *conn) + { + struct b_conn_handle *h = &conn->handles[b->i]; + +- debug ("%s: can_flush", b->name); ++ controlpath_debug ("%s: can_flush", b->name); + + assert (h->handle && (h->state & HANDLE_CONNECTED)); + if (h->can_flush == -1) +@@ -313,7 +329,7 @@ backend_is_rotational (struct backend *b, struct connection *conn) + { + struct b_conn_handle *h = &conn->handles[b->i]; + +- debug ("%s: is_rotational", b->name); ++ controlpath_debug ("%s: is_rotational", b->name); + + assert (h->handle && (h->state & HANDLE_CONNECTED)); + if (h->is_rotational == -1) +@@ -327,7 +343,7 @@ backend_can_trim (struct backend *b, struct connection *conn) + struct b_conn_handle *h = &conn->handles[b->i]; + int r; + +- debug ("%s: can_trim", b->name); ++ controlpath_debug ("%s: can_trim", b->name); + + assert (h->handle && (h->state & HANDLE_CONNECTED)); + if (h->can_trim == -1) { +@@ -347,7 +363,7 @@ backend_can_zero (struct backend *b, struct connection *conn) + struct b_conn_handle *h = &conn->handles[b->i]; + int r; + +- debug ("%s: can_zero", b->name); ++ controlpath_debug ("%s: can_zero", b->name); + + assert (h->handle && (h->state & HANDLE_CONNECTED)); + if (h->can_zero == -1) { +@@ -367,7 +383,7 @@ backend_can_fast_zero (struct backend *b, struct connection *conn) + struct b_conn_handle *h = &conn->handles[b->i]; + int r; + +- debug ("%s: can_fast_zero", b->name); ++ controlpath_debug ("%s: can_fast_zero", b->name); + + assert (h->handle && (h->state & HANDLE_CONNECTED)); + if (h->can_fast_zero == -1) { +@@ -386,7 +402,7 @@ backend_can_extents (struct backend *b, struct connection *conn) + { + struct b_conn_handle *h = &conn->handles[b->i]; + +- debug ("%s: can_extents", b->name); ++ controlpath_debug ("%s: can_extents", b->name); + + assert (h->handle && (h->state & HANDLE_CONNECTED)); + if (h->can_extents == -1) +@@ -400,7 +416,7 @@ backend_can_fua (struct backend *b, struct connection *conn) + struct b_conn_handle *h = &conn->handles[b->i]; + int r; + +- debug ("%s: can_fua", b->name); ++ controlpath_debug ("%s: can_fua", b->name); + + assert (h->handle && (h->state & HANDLE_CONNECTED)); + if (h->can_fua == -1) { +@@ -420,7 +436,7 @@ backend_can_multi_conn (struct backend *b, struct connection *conn) + struct b_conn_handle *h = &conn->handles[b->i]; + + assert (h->handle && (h->state & HANDLE_CONNECTED)); +- debug ("%s: can_multi_conn", b->name); ++ controlpath_debug ("%s: can_multi_conn", b->name); + + if (h->can_multi_conn == -1) + h->can_multi_conn = b->can_multi_conn (b, conn, h->handle); +@@ -432,7 +448,7 @@ backend_can_cache (struct backend *b, struct connection *conn) + { + struct b_conn_handle *h = &conn->handles[b->i]; + +- debug ("%s: can_cache", b->name); ++ controlpath_debug ("%s: can_cache", b->name); + + assert (h->handle && (h->state & HANDLE_CONNECTED)); + if (h->can_cache == -1) +@@ -451,8 +467,8 @@ backend_pread (struct backend *b, struct connection *conn, + assert (h->handle && (h->state & HANDLE_CONNECTED)); + assert (backend_valid_range (b, conn, offset, count)); + assert (flags == 0); +- debug ("%s: pread count=%" PRIu32 " offset=%" PRIu64, +- b->name, count, offset); ++ datapath_debug ("%s: pread count=%" PRIu32 " offset=%" PRIu64, ++ b->name, count, offset); + + r = b->pread (b, conn, h->handle, buf, count, offset, flags, err); + if (r == -1) +@@ -475,8 +491,8 @@ backend_pwrite (struct backend *b, struct connection *conn, + assert (!(flags & ~NBDKIT_FLAG_FUA)); + if (fua) + assert (h->can_fua > NBDKIT_FUA_NONE); +- debug ("%s: pwrite count=%" PRIu32 " offset=%" PRIu64 " fua=%d", +- b->name, count, offset, fua); ++ datapath_debug ("%s: pwrite count=%" PRIu32 " offset=%" PRIu64 " fua=%d", ++ b->name, count, offset, fua); + + r = b->pwrite (b, conn, h->handle, buf, count, offset, flags, err); + if (r == -1) +@@ -494,7 +510,7 @@ backend_flush (struct backend *b, struct connection *conn, + assert (h->handle && (h->state & HANDLE_CONNECTED)); + assert (h->can_flush == 1); + assert (flags == 0); +- debug ("%s: flush", b->name); ++ datapath_debug ("%s: flush", b->name); + + r = b->flush (b, conn, h->handle, flags, err); + if (r == -1) +@@ -518,8 +534,8 @@ backend_trim (struct backend *b, struct connection *conn, + assert (!(flags & ~NBDKIT_FLAG_FUA)); + if (fua) + assert (h->can_fua > NBDKIT_FUA_NONE); +- debug ("%s: trim count=%" PRIu32 " offset=%" PRIu64 " fua=%d", +- b->name, count, offset, fua); ++ datapath_debug ("%s: trim count=%" PRIu32 " offset=%" PRIu64 " fua=%d", ++ b->name, count, offset, fua); + + r = b->trim (b, conn, h->handle, count, offset, flags, err); + if (r == -1) +@@ -547,9 +563,10 @@ backend_zero (struct backend *b, struct connection *conn, + assert (h->can_fua > NBDKIT_FUA_NONE); + if (fast) + assert (h->can_fast_zero == 1); +- debug ("%s: zero count=%" PRIu32 " offset=%" PRIu64 +- " may_trim=%d fua=%d fast=%d", +- b->name, count, offset, !!(flags & NBDKIT_FLAG_MAY_TRIM), fua, fast); ++ datapath_debug ("%s: zero count=%" PRIu32 " offset=%" PRIu64 ++ " may_trim=%d fua=%d fast=%d", ++ b->name, count, offset, ++ !!(flags & NBDKIT_FLAG_MAY_TRIM), fua, fast); + + r = b->zero (b, conn, h->handle, count, offset, flags, err); + if (r == -1) { +@@ -572,8 +589,8 @@ backend_extents (struct backend *b, struct connection *conn, + assert (h->can_extents >= 0); + assert (backend_valid_range (b, conn, offset, count)); + assert (!(flags & ~NBDKIT_FLAG_REQ_ONE)); +- debug ("%s: extents count=%" PRIu32 " offset=%" PRIu64 " req_one=%d", +- b->name, count, offset, !!(flags & NBDKIT_FLAG_REQ_ONE)); ++ datapath_debug ("%s: extents count=%" PRIu32 " offset=%" PRIu64 " req_one=%d", ++ b->name, count, offset, !!(flags & NBDKIT_FLAG_REQ_ONE)); + + if (h->can_extents == 0) { + /* By default it is safe assume that everything in the range is +@@ -602,8 +619,8 @@ backend_cache (struct backend *b, struct connection *conn, + assert (h->can_cache > NBDKIT_CACHE_NONE); + assert (backend_valid_range (b, conn, offset, count)); + assert (flags == 0); +- debug ("%s: cache count=%" PRIu32 " offset=%" PRIu64, +- b->name, count, offset); ++ datapath_debug ("%s: cache count=%" PRIu32 " offset=%" PRIu64, ++ b->name, count, offset); + + if (h->can_cache == NBDKIT_CACHE_EMULATE) { + static char buf[MAX_REQUEST_SIZE]; /* data sink, never read */ +diff --git a/tests/Makefile.am b/tests/Makefile.am +index 0134197..d225cc6 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -135,6 +135,7 @@ EXTRA_DIST = \ + test-nbd-extents.sh \ + test-nbd-tls.sh \ + test-nbd-tls-psk.sh \ ++ test-nbdkit-backend-debug.sh \ + test-nozero.sh \ + test-null-extents.sh \ + test_ocaml_plugin.ml \ +@@ -746,6 +747,9 @@ endif HAVE_VDDK + # zero plugin test. + TESTS += test-zero.sh + ++# -D nbdkit.backend.* settings. ++TESTS += test-nbdkit-backend-debug.sh ++ + #---------------------------------------------------------------------- + # Tests of language plugins. + +diff --git a/tests/test-nbdkit-backend-debug.sh b/tests/test-nbdkit-backend-debug.sh +new file mode 100755 +index 0000000..69a69a7 +--- /dev/null ++++ b/tests/test-nbdkit-backend-debug.sh +@@ -0,0 +1,70 @@ ++#!/usr/bin/env bash ++# nbdkit ++# Copyright (C) 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. ++ ++source ./functions.sh ++set -x ++set -e ++ ++requires qemu-img --version ++ ++out="test-nbdkit-backend-debug.out" ++debug="test-nbdkit-backend-debug.debug" ++files="$out $debug" ++rm -f $files ++cleanup_fn rm -f $files ++ ++nbdkit -U - \ ++ -v \ ++ memory 10M \ ++ --run "qemu-img convert \$nbd $out" |& tee $debug ++ ++# Should contain all debugging messages. ++grep '^nbdkit:.*debug: memory: open' $debug ++grep '^nbdkit:.*debug: memory: pread' $debug ++ ++nbdkit -U - \ ++ -v -D nbdkit.backend.controlpath=0 \ ++ memory 10M \ ++ --run "qemu-img convert \$nbd $out" |& tee $debug ++ ++# Should contain only datapath messages. ++grep -v '^nbdkit:.*debug: memory: open' $debug ++grep '^nbdkit:.*debug: memory: pread' $debug ++ ++nbdkit -U - \ ++ -v -D nbdkit.backend.datapath=0 \ ++ memory 10M \ ++ --run "qemu-img convert \$nbd $out" |& tee $debug ++ ++# Should contain only controlpath messages. ++grep '^nbdkit:.*debug: memory: open' $debug ++grep -v '^nbdkit:.*debug: memory: pread' $debug +-- +2.18.2 + diff --git a/SOURCES/0004-python-Add-various-constants-to-the-API.patch b/SOURCES/0004-python-Add-various-constants-to-the-API.patch new file mode 100644 index 0000000..8827874 --- /dev/null +++ b/SOURCES/0004-python-Add-various-constants-to-the-API.patch @@ -0,0 +1,65 @@ +From b646050b8da51c39cf21f95fa847c12784a1169c Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Thu, 21 Nov 2019 15:02:44 +0000 +Subject: [PATCH] python: Add various constants to the API. + +These are accessible from the plugin by: + + import nbdkit + + if flags & nbdkit.FLAG_MAY_TRIM: + &c. + +Many (all?) of these are not yet useful for plugins, some will never +be useful, but they only consume a tiny bit of memory and it's nice to +have the complete set available for future use. + +(cherry picked from commit 14b7fe2e0de881e3dfc8803484ade29a61e323c9) +--- + plugins/python/python.c | 30 ++++++++++++++++++++++++++++++ + 1 file changed, 30 insertions(+) + +diff --git a/plugins/python/python.c b/plugins/python/python.c +index 7052aac..47da083 100644 +--- a/plugins/python/python.c ++++ b/plugins/python/python.c +@@ -231,6 +231,36 @@ create_nbdkit_module (void) + nbdkit_error ("could not create the nbdkit API module"); + exit (EXIT_FAILURE); + } ++ ++ /* Constants corresponding to various flags. */ ++#define ADD_INT_CONSTANT(name) \ ++ if (PyModule_AddIntConstant (m, #name, NBDKIT_##name) == -1) { \ ++ nbdkit_error ("could not add constant %s to nbdkit API module", \ ++ #name); \ ++ exit (EXIT_FAILURE); \ ++ } ++ ADD_INT_CONSTANT (THREAD_MODEL_SERIALIZE_CONNECTIONS); ++ ADD_INT_CONSTANT (THREAD_MODEL_SERIALIZE_ALL_REQUESTS); ++ ADD_INT_CONSTANT (THREAD_MODEL_SERIALIZE_REQUESTS); ++ ADD_INT_CONSTANT (THREAD_MODEL_PARALLEL); ++ ++ ADD_INT_CONSTANT (FLAG_MAY_TRIM); ++ ADD_INT_CONSTANT (FLAG_FUA); ++ ADD_INT_CONSTANT (FLAG_REQ_ONE); ++ ADD_INT_CONSTANT (FLAG_FAST_ZERO); ++ ++ ADD_INT_CONSTANT (FUA_NONE); ++ ADD_INT_CONSTANT (FUA_EMULATE); ++ ADD_INT_CONSTANT (FUA_NATIVE); ++ ++ ADD_INT_CONSTANT (CACHE_NONE); ++ ADD_INT_CONSTANT (CACHE_EMULATE); ++ ADD_INT_CONSTANT (CACHE_NATIVE); ++ ++ ADD_INT_CONSTANT (EXTENT_HOLE); ++ ADD_INT_CONSTANT (EXTENT_ZERO); ++#undef ADD_INT_CONSTANT ++ + return m; + } + +-- +2.18.2 + diff --git a/SOURCES/0005-python-Implement-nbdkit-API-version-2.patch b/SOURCES/0005-python-Implement-nbdkit-API-version-2.patch new file mode 100644 index 0000000..382c54c --- /dev/null +++ b/SOURCES/0005-python-Implement-nbdkit-API-version-2.patch @@ -0,0 +1,558 @@ +From 49ef7e7d7c3602cc8e53d2052fce9d3a12840ea2 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Thu, 21 Nov 2019 15:44:39 +0000 +Subject: [PATCH] python: Implement nbdkit API version 2. + +To avoid breaking existing plugins, Python plugins wishing to use +version 2 of the API must opt in by declaring: + + API_VERSION = 2 + +(Plugins which do not do this are assumed to want API version 1). + +For v2 API, we also avoid a copy by passing a buffer into pread. + +It's more efficient if we pass the C buffer directly to Python code. +In some cases the Python code will be able to write directly into the +C buffer using functions like file.readinto and socket.recv_into. +This avoids an extra copy. + +Thanks: Nir Soffer +https://www.redhat.com/archives/libguestfs/2019-November/thread.html#00220 +(cherry picked from commit a9b2637cf4f00fb8a25ffaf31ee83be5fe019ae2) +--- + plugins/python/example.py | 20 +++- + plugins/python/nbdkit-python-plugin.pod | 69 +++++++----- + plugins/python/python.c | 139 +++++++++++++++++++----- + tests/python-exception.py | 4 +- + tests/shebang.py | 5 +- + tests/test.py | 28 +++-- + 6 files changed, 190 insertions(+), 75 deletions(-) + +diff --git a/plugins/python/example.py b/plugins/python/example.py +index 60f9d7f..c04b7e2 100644 +--- a/plugins/python/example.py ++++ b/plugins/python/example.py +@@ -34,6 +34,12 @@ import errno + disk = bytearray(1024 * 1024) + + ++# There are several variants of the API. nbdkit will call this ++# function first to determine which one you want to use. This is the ++# latest version at the time this example was written. ++API_VERSION = 2 ++ ++ + # This just prints the extra command line parameters, but real plugins + # should parse them and reject any unknown parameters. + def config(key, value): +@@ -54,20 +60,22 @@ def get_size(h): + return len(disk) + + +-def pread(h, count, offset): ++def pread(h, buf, offset, flags): + global disk +- return disk[offset:offset+count] ++ end = offset + len(buf) ++ buf[:] = disk[offset:end] ++ # or if reading from a file you can use: ++ #f.readinto(buf) + +- +-def pwrite(h, buf, offset): ++def pwrite(h, buf, offset, flags): + global disk + end = offset + len(buf) + disk[offset:end] = buf + + +-def zero(h, count, offset, may_trim): ++def zero(h, count, offset, flags): + global disk +- if may_trim: ++ if flags & nbdkit.FLAG_MAY_TRIM: + disk[offset:offset+count] = bytearray(count) + else: + nbdkit.set_error(errno.EOPNOTSUPP) +diff --git a/plugins/python/nbdkit-python-plugin.pod b/plugins/python/nbdkit-python-plugin.pod +index 3680fd6..4923d9d 100644 +--- a/plugins/python/nbdkit-python-plugin.pod ++++ b/plugins/python/nbdkit-python-plugin.pod +@@ -33,11 +33,12 @@ To write a Python nbdkit plugin, you create a Python file which + contains at least the following required functions (in the top level + C<__main__> module): + ++ API_VERSION = 2 + def open(readonly): + # see below + def get_size(h): + # see below +- def pread(h, count, offset): ++ def pread(h, buf, offset, flags): + # see below + + Note that the subroutines must have those literal names (like C), +@@ -82,6 +83,18 @@ I<--dump-plugin> option, eg: + python_version=3.7.0 + python_pep_384_abi_version=3 + ++=head2 API versions ++ ++The nbdkit API has evolved and new versions are released periodically. ++To ensure backwards compatibility plugins have to opt in to the new ++version. From Python you do this by declaring a constant in your ++module: ++ ++ API_VERSION = 2 ++ ++(where 2 is the latest version at the time this documentation was ++written). All newly written Python modules must have this constant. ++ + =head2 Executable script + + If you want you can make the script executable and include a "shebang" +@@ -199,16 +212,12 @@ contents will be garbage collected. + + (Required) + +- def pread(h, count, offset): +- # construct a buffer of length count bytes and return it ++ def pread(h, buf, offset, flags): ++ # read into the buffer + +-The body of your C function should construct a buffer of length +-(at least) C bytes. You should read C bytes from the +-disk starting at C. +- +-The returned buffer can be any type compatible with the Python 3 +-buffer protocol, such as bytearray, bytes or memoryview +-(L) ++The body of your C function should read exactly C ++bytes of data starting at disk C and write it into the buffer ++C. C is always 0. + + NBD only supports whole reads, so your function should try to read + the whole region (perhaps requiring a loop). If the read fails or +@@ -219,13 +228,13 @@ C first. + + (Optional) + +- def pwrite(h, buf, offset): ++ def pwrite(h, buf, offset, flags): + length = len (buf) + # no return value + + The body of your C function should write the buffer C to + the disk. You should write C bytes to the disk starting at +-C. ++C. C may contain C. + + NBD only supports whole writes, so your function should try to + write the whole region (perhaps requiring a loop). If the write +@@ -236,11 +245,12 @@ fails or is partial, your function should throw an exception, + + (Optional) + +- def flush(h): ++ def flush(h, flags): + # no return value + + The body of your C function should do a L or + L or equivalent on the backing store. ++C is always 0. + + If the flush fails, your function should throw an exception, optionally + using C first. +@@ -249,32 +259,35 @@ using C first. + + (Optional) + +- def trim(h, count, offset): ++ def trim(h, count, offset, flags): + # no return value + +-The body of your C function should "punch a hole" in the +-backing store. If the trim fails, your function should throw an +-exception, optionally using C first. ++The body of your C function should "punch a hole" in the backing ++store. C may contain C. If the trim fails, ++your function should throw an exception, optionally using ++C first. + + =item C + + (Optional) + +- def zero(h, count, offset, may_trim): ++ def zero(h, count, offset, flags): + # no return value + +-The body of your C function should ensure that C bytes +-of the disk, starting at C, will read back as zero. If +-C is true, the operation may be optimized as a trim as long +-as subsequent reads see zeroes. ++The body of your C function should ensure that C bytes of ++the disk, starting at C, will read back as zero. C is ++a bitmask which may include C, ++C, C. + + NBD only supports whole writes, so your function should try to +-write the whole region (perhaps requiring a loop). If the write +-fails or is partial, your function should throw an exception, +-optionally using C first. In particular, if +-you would like to automatically fall back to C (perhaps +-because there is nothing to optimize if C is false), +-use C. ++write the whole region (perhaps requiring a loop). ++ ++If the write fails or is partial, your function should throw an ++exception, optionally using C first. In particular, ++if you would like to automatically fall back to C (perhaps ++because there is nothing to optimize if ++S> is false), use ++S>. + + =back + +diff --git a/plugins/python/python.c b/plugins/python/python.c +index 47da083..0f28595 100644 +--- a/plugins/python/python.c ++++ b/plugins/python/python.c +@@ -46,6 +46,8 @@ + #define PY_SSIZE_T_CLEAN 1 + #include + ++#define NBDKIT_API_VERSION 2 ++ + #include + + #include "cleanup.h" +@@ -60,6 +62,7 @@ + */ + static const char *script; + static PyObject *module; ++static int py_api_version = 1; + + static int last_error; + +@@ -285,9 +288,14 @@ py_dump_plugin (void) + PyObject *fn; + PyObject *r; + ++ /* Python version and ABI. */ + printf ("python_version=%s\n", PY_VERSION); + printf ("python_pep_384_abi_version=%d\n", PYTHON_ABI_VERSION); + ++ /* Maximum nbdkit API version supported. */ ++ printf ("nbdkit_python_maximum_api_version=%d\n", NBDKIT_API_VERSION); ++ ++ /* If the script has a dump_plugin function, call it. */ + if (script && callback_defined ("dump_plugin", &fn)) { + PyErr_Clear (); + +@@ -297,6 +305,30 @@ py_dump_plugin (void) + } + } + ++static int ++get_py_api_version (void) ++{ ++ PyObject *obj; ++ long value; ++ ++ obj = PyObject_GetAttrString (module, "API_VERSION"); ++ if (obj == NULL) ++ return 1; /* Default to API version 1. */ ++ ++ value = PyLong_AsLong (obj); ++ Py_DECREF (obj); ++ ++ if (value < 1 || value > NBDKIT_API_VERSION) { ++ nbdkit_error ("%s: API_VERSION requested unknown version: %ld. " ++ "This plugin supports API versions between 1 and %d.", ++ script, value, NBDKIT_API_VERSION); ++ return -1; ++ } ++ ++ nbdkit_debug ("module requested API_VERSION %ld", value); ++ return (int) value; ++} ++ + static int + py_config (const char *key, const char *value) + { +@@ -359,6 +391,11 @@ py_config (const char *key, const char *value) + "nbdkit requires these callbacks.", script); + return -1; + } ++ ++ /* Get the API version. */ ++ py_api_version = get_py_api_version (); ++ if (py_api_version == -1) ++ return -1; + } + else if (callback_defined ("config", &fn)) { + /* Other parameters are passed to the Python .config callback. */ +@@ -469,8 +506,8 @@ py_get_size (void *handle) + } + + static int +-py_pread (void *handle, void *buf, +- uint32_t count, uint64_t offset) ++py_pread (void *handle, void *buf, uint32_t count, uint64_t offset, ++ uint32_t flags) + { + PyObject *obj = handle; + PyObject *fn; +@@ -485,24 +522,40 @@ py_pread (void *handle, void *buf, + + PyErr_Clear (); + +- r = PyObject_CallFunction (fn, "OiL", obj, count, offset); ++ switch (py_api_version) { ++ case 1: ++ r = PyObject_CallFunction (fn, "OiL", obj, count, offset); ++ break; ++ case 2: ++ r = PyObject_CallFunction (fn, "ONLI", obj, ++ PyMemoryView_FromMemory ((char *)buf, count, PyBUF_WRITE), ++ offset, flags); ++ break; ++ default: abort (); ++ } + Py_DECREF (fn); + if (check_python_failure ("pread") == -1) + return ret; + +- if (PyObject_GetBuffer (r, &view, PyBUF_SIMPLE) == -1) { +- nbdkit_error ("%s: value returned from pread does not support the " +- "buffer protocol", +- script); +- goto out; +- } ++ if (py_api_version == 1) { ++ /* In API v1 the Python pread function had to return a buffer ++ * protocol compatible function. In API v2+ it writes directly to ++ * the C buffer so this code is not used. ++ */ ++ if (PyObject_GetBuffer (r, &view, PyBUF_SIMPLE) == -1) { ++ nbdkit_error ("%s: value returned from pread does not support the " ++ "buffer protocol", ++ script); ++ goto out; ++ } + +- if (view.len < count) { +- nbdkit_error ("%s: buffer returned from pread is too small", script); +- goto out; +- } ++ if (view.len < count) { ++ nbdkit_error ("%s: buffer returned from pread is too small", script); ++ goto out; ++ } + +- memcpy (buf, view.buf, count); ++ memcpy (buf, view.buf, count); ++ } + ret = 0; + + out: +@@ -515,8 +568,8 @@ out: + } + + static int +-py_pwrite (void *handle, const void *buf, +- uint32_t count, uint64_t offset) ++py_pwrite (void *handle, const void *buf, uint32_t count, uint64_t offset, ++ uint32_t flags) + { + PyObject *obj = handle; + PyObject *fn; +@@ -525,9 +578,19 @@ py_pwrite (void *handle, const void *buf, + if (callback_defined ("pwrite", &fn)) { + PyErr_Clear (); + +- r = PyObject_CallFunction (fn, "ONL", obj, ++ switch (py_api_version) { ++ case 1: ++ r = PyObject_CallFunction (fn, "ONL", obj, + PyMemoryView_FromMemory ((char *)buf, count, PyBUF_READ), + offset); ++ break; ++ case 2: ++ r = PyObject_CallFunction (fn, "ONLI", obj, ++ PyMemoryView_FromMemory ((char *)buf, count, PyBUF_READ), ++ offset, flags); ++ break; ++ default: abort (); ++ } + Py_DECREF (fn); + if (check_python_failure ("pwrite") == -1) + return -1; +@@ -542,7 +605,7 @@ py_pwrite (void *handle, const void *buf, + } + + static int +-py_flush (void *handle) ++py_flush (void *handle, uint32_t flags) + { + PyObject *obj = handle; + PyObject *fn; +@@ -551,7 +614,15 @@ py_flush (void *handle) + if (callback_defined ("flush", &fn)) { + PyErr_Clear (); + +- r = PyObject_CallFunctionObjArgs (fn, obj, NULL); ++ switch (py_api_version) { ++ case 1: ++ r = PyObject_CallFunctionObjArgs (fn, obj, NULL); ++ break; ++ case 2: ++ r = PyObject_CallFunction (fn, "OI", obj, flags); ++ break; ++ default: abort (); ++ } + Py_DECREF (fn); + if (check_python_failure ("flush") == -1) + return -1; +@@ -566,7 +637,7 @@ py_flush (void *handle) + } + + static int +-py_trim (void *handle, uint32_t count, uint64_t offset) ++py_trim (void *handle, uint32_t count, uint64_t offset, uint32_t flags) + { + PyObject *obj = handle; + PyObject *fn; +@@ -575,7 +646,15 @@ py_trim (void *handle, uint32_t count, uint64_t offset) + if (callback_defined ("trim", &fn)) { + PyErr_Clear (); + +- r = PyObject_CallFunction (fn, "OiL", obj, count, offset); ++ switch (py_api_version) { ++ case 1: ++ r = PyObject_CallFunction (fn, "OiL", obj, count, offset); ++ break; ++ case 2: ++ r = PyObject_CallFunction (fn, "OiLI", obj, count, offset, flags); ++ break; ++ default: abort (); ++ } + Py_DECREF (fn); + if (check_python_failure ("trim") == -1) + return -1; +@@ -590,7 +669,7 @@ py_trim (void *handle, uint32_t count, uint64_t offset) + } + + static int +-py_zero (void *handle, uint32_t count, uint64_t offset, int may_trim) ++py_zero (void *handle, uint32_t count, uint64_t offset, uint32_t flags) + { + PyObject *obj = handle; + PyObject *fn; +@@ -600,9 +679,19 @@ py_zero (void *handle, uint32_t count, uint64_t offset, int may_trim) + PyErr_Clear (); + + last_error = 0; +- r = PyObject_CallFunction (fn, "OiLO", +- obj, count, offset, +- may_trim ? Py_True : Py_False); ++ switch (py_api_version) { ++ case 1: { ++ int may_trim = flags & NBDKIT_FLAG_MAY_TRIM; ++ r = PyObject_CallFunction (fn, "OiLO", ++ obj, count, offset, ++ may_trim ? Py_True : Py_False); ++ break; ++ } ++ case 2: ++ r = PyObject_CallFunction (fn, "OiLI", obj, count, offset, flags); ++ break; ++ default: abort (); ++ } + Py_DECREF (fn); + if (last_error == EOPNOTSUPP || last_error == ENOTSUP) { + /* When user requests this particular error, we want to +diff --git a/tests/python-exception.py b/tests/python-exception.py +index d0c79bb..ee4a3f3 100644 +--- a/tests/python-exception.py ++++ b/tests/python-exception.py +@@ -62,5 +62,5 @@ def get_size(h): + return 0 + + +-def pread(h, count, offset): +- return "" ++def pread(h, buf, offset): ++ buf[:] = bytearray(len(buf)) +diff --git a/tests/shebang.py b/tests/shebang.py +index 6f33623..0634589 100755 +--- a/tests/shebang.py ++++ b/tests/shebang.py +@@ -13,6 +13,7 @@ def get_size(h): + return len(disk) + + +-def pread(h, count, offset): ++def pread(h, buf, offset): + global disk +- return disk[offset:offset+count] ++ end = offset + len(buf) ++ buf[:] = disk[offset:end] +diff --git a/tests/test.py b/tests/test.py +index 9a2e947..4db5662 100644 +--- a/tests/test.py ++++ b/tests/test.py +@@ -3,6 +3,9 @@ import nbdkit + disk = bytearray(1024*1024) + + ++API_VERSION = 2 ++ ++ + def config_complete(): + print ("set_error = %r" % nbdkit.set_error) + +@@ -32,25 +35,26 @@ def can_trim(h): + return True + + +-def pread(h, count, offset): ++def pread(h, buf, offset, flags): + global disk +- return disk[offset:offset+count] ++ end = offset + len(buf) ++ buf[:] = disk[offset:end] + + +-def pwrite(h, buf, offset): ++def pwrite(h, buf, offset, flags): + global disk + end = offset + len(buf) + disk[offset:end] = buf + + +-def zero(h, count, offset, may_trim=False): ++def flush(h, flags): ++ pass ++ ++ ++def trim(h, count, offset, flags): ++ pass ++ ++ ++def zero(h, count, offset, flags): + global disk + disk[offset:offset+count] = bytearray(count) +- +- +-def flush(h): +- pass +- +- +-def trim(h, count, offset): +- pass +-- +2.18.2 + diff --git a/SOURCES/0006-python-Implement-cache.patch b/SOURCES/0006-python-Implement-cache.patch new file mode 100644 index 0000000..d6e0268 --- /dev/null +++ b/SOURCES/0006-python-Implement-cache.patch @@ -0,0 +1,98 @@ +From c5b1fac4c67078f0164bd23eab6d4d2b8c9830b0 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Thu, 21 Nov 2019 16:42:02 +0000 +Subject: [PATCH] python: Implement cache. + +However this does not implement can_cache, since that is not a simple +boolean. + +(cherry picked from commit e61ffb73c7a0af0c383184fdb8f08d30784a195e) +--- + plugins/python/nbdkit-python-plugin.pod | 14 ++++++++++- + plugins/python/python.c | 31 +++++++++++++++++++++++++ + 2 files changed, 44 insertions(+), 1 deletion(-) + +diff --git a/plugins/python/nbdkit-python-plugin.pod b/plugins/python/nbdkit-python-plugin.pod +index 4923d9d..0ea8dee 100644 +--- a/plugins/python/nbdkit-python-plugin.pod ++++ b/plugins/python/nbdkit-python-plugin.pod +@@ -289,6 +289,19 @@ because there is nothing to optimize if + S> is false), use + S>. + ++=item C ++ ++(Optional) ++ ++ def cache(h, count, offset, flags): ++ # no return value ++ ++The body of your C function should prefetch data in the ++indicated range. ++ ++If the cache operation fails, your function should throw an exception, ++optionally using C first. ++ + =back + + =head2 Missing callbacks +@@ -317,7 +330,6 @@ C, + C, + C, + C, +-C, + C. + + These are not yet supported. +diff --git a/plugins/python/python.c b/plugins/python/python.c +index 0f28595..c5cf38e 100644 +--- a/plugins/python/python.c ++++ b/plugins/python/python.c +@@ -714,6 +714,36 @@ py_zero (void *handle, uint32_t count, uint64_t offset, uint32_t flags) + return -1; + } + ++static int ++py_cache (void *handle, uint32_t count, uint64_t offset, uint32_t flags) ++{ ++ PyObject *obj = handle; ++ PyObject *fn; ++ PyObject *r; ++ ++ if (callback_defined ("cache", &fn)) { ++ PyErr_Clear (); ++ ++ switch (py_api_version) { ++ case 1: ++ case 2: ++ r = PyObject_CallFunction (fn, "OiLI", obj, count, offset, flags, NULL); ++ break; ++ default: abort (); ++ } ++ Py_DECREF (fn); ++ if (check_python_failure ("cache") == -1) ++ return -1; ++ Py_DECREF (r); ++ } ++ else { ++ nbdkit_error ("%s not implemented", "cache"); ++ return -1; ++ } ++ ++ return 0; ++} ++ + static int + boolean_callback (void *handle, const char *can_fn, const char *plain_fn) + { +@@ -799,6 +829,7 @@ static struct nbdkit_plugin plugin = { + .flush = py_flush, + .trim = py_trim, + .zero = py_zero, ++ .cache = py_cache, + }; + + NBDKIT_REGISTER_PLUGIN (plugin) +-- +2.18.2 + diff --git a/SOURCES/0007-python-Implement-can_zero-can_fast_zero.patch b/SOURCES/0007-python-Implement-can_zero-can_fast_zero.patch new file mode 100644 index 0000000..f308909 --- /dev/null +++ b/SOURCES/0007-python-Implement-can_zero-can_fast_zero.patch @@ -0,0 +1,80 @@ +From 17721b316dd66b0a1ed792eeccd2489fb97828df Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Thu, 21 Nov 2019 16:42:59 +0000 +Subject: [PATCH] python: Implement can_zero, can_fast_zero. + +(cherry picked from commit 039f600d2ad7a9ff04523a165eb2fe41b9c87c01) +--- + plugins/python/nbdkit-python-plugin.pod | 16 ++++++++++++++-- + plugins/python/python.c | 14 ++++++++++++++ + 2 files changed, 28 insertions(+), 2 deletions(-) + +diff --git a/plugins/python/nbdkit-python-plugin.pod b/plugins/python/nbdkit-python-plugin.pod +index 0ea8dee..1f1c30f 100644 +--- a/plugins/python/nbdkit-python-plugin.pod ++++ b/plugins/python/nbdkit-python-plugin.pod +@@ -208,6 +208,20 @@ contents will be garbage collected. + def can_trim(h): + # return a boolean + ++=item C ++ ++(Optional) ++ ++ def can_zero(h): ++ # return a boolean ++ ++=item C ++ ++(Optional) ++ ++ def can_fast_zero(h): ++ # return a boolean ++ + =item C + + (Required) +@@ -326,8 +340,6 @@ C, + C, + C, + C, +-C, +-C, + C, + C, + C. +diff --git a/plugins/python/python.c b/plugins/python/python.c +index c5cf38e..38fc119 100644 +--- a/plugins/python/python.c ++++ b/plugins/python/python.c +@@ -797,6 +797,18 @@ py_can_trim (void *handle) + return boolean_callback (handle, "can_trim", "trim"); + } + ++static int ++py_can_zero (void *handle) ++{ ++ return boolean_callback (handle, "can_zero", "zero"); ++} ++ ++static int ++py_can_fast_zero (void *handle) ++{ ++ return boolean_callback (handle, "can_fast_zero", NULL); ++} ++ + #define py_config_help \ + "script= (required) The Python plugin to run.\n" \ + "[other arguments may be used by the plugin that you load]" +@@ -823,6 +835,8 @@ static struct nbdkit_plugin plugin = { + .can_write = py_can_write, + .can_flush = py_can_flush, + .can_trim = py_can_trim, ++ .can_zero = py_can_zero, ++ .can_fast_zero = py_can_fast_zero, + + .pread = py_pread, + .pwrite = py_pwrite, +-- +2.18.2 + diff --git a/SOURCES/0008-python-Implement-can_multi_conn.patch b/SOURCES/0008-python-Implement-can_multi_conn.patch new file mode 100644 index 0000000..242026f --- /dev/null +++ b/SOURCES/0008-python-Implement-can_multi_conn.patch @@ -0,0 +1,65 @@ +From 2a85ce81ad95eb2f9b2f29666480b814ea0f80d9 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Thu, 21 Nov 2019 16:46:11 +0000 +Subject: [PATCH] python: Implement can_multi_conn. + +(cherry picked from commit 21dd7bf49d3238c7e75918d4bf324b617f458d83) +--- + plugins/python/nbdkit-python-plugin.pod | 8 +++++++- + plugins/python/python.c | 7 +++++++ + 2 files changed, 14 insertions(+), 1 deletion(-) + +diff --git a/plugins/python/nbdkit-python-plugin.pod b/plugins/python/nbdkit-python-plugin.pod +index 1f1c30f..b92bb56 100644 +--- a/plugins/python/nbdkit-python-plugin.pod ++++ b/plugins/python/nbdkit-python-plugin.pod +@@ -187,6 +187,13 @@ contents will be garbage collected. + def is_rotational(h): + # return a boolean + ++=item C ++ ++(Optional) ++ ++ def can_multi_conn(h): ++ # return a boolean ++ + =item C + + (Optional) +@@ -341,7 +348,6 @@ C, + C, + C, + C, +-C, + C. + + These are not yet supported. +diff --git a/plugins/python/python.c b/plugins/python/python.c +index 38fc119..b186b99 100644 +--- a/plugins/python/python.c ++++ b/plugins/python/python.c +@@ -779,6 +779,12 @@ py_is_rotational (void *handle) + return boolean_callback (handle, "is_rotational", NULL); + } + ++static int ++py_can_multi_conn (void *handle) ++{ ++ return boolean_callback (handle, "can_multi_conn", NULL); ++} ++ + static int + py_can_write (void *handle) + { +@@ -832,6 +838,7 @@ static struct nbdkit_plugin plugin = { + + .get_size = py_get_size, + .is_rotational = py_is_rotational, ++ .can_multi_conn = py_can_multi_conn, + .can_write = py_can_write, + .can_flush = py_can_flush, + .can_trim = py_can_trim, +-- +2.18.2 + diff --git a/SOURCES/0009-python-Implement-can_fua-and-can_cache.patch b/SOURCES/0009-python-Implement-can_fua-and-can_cache.patch new file mode 100644 index 0000000..8e60a5c --- /dev/null +++ b/SOURCES/0009-python-Implement-can_fua-and-can_cache.patch @@ -0,0 +1,126 @@ +From 38124a137974e1433d68732640ca7f88664557da Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Fri, 22 Nov 2019 19:25:53 +0000 +Subject: [PATCH] python: Implement can_fua and can_cache. + +(cherry picked from commit 97c46f885edec5a61a96ac86eccb9d8c874c602e) +--- + plugins/python/nbdkit-python-plugin.pod | 18 +++++++- + plugins/python/python.c | 58 +++++++++++++++++++++++++ + 2 files changed, 74 insertions(+), 2 deletions(-) + +diff --git a/plugins/python/nbdkit-python-plugin.pod b/plugins/python/nbdkit-python-plugin.pod +index b92bb56..4065ec7 100644 +--- a/plugins/python/nbdkit-python-plugin.pod ++++ b/plugins/python/nbdkit-python-plugin.pod +@@ -229,6 +229,22 @@ contents will be garbage collected. + def can_fast_zero(h): + # return a boolean + ++=item C ++ ++(Optional) ++ ++ def can_fua(h): ++ # return nbdkit.FUA_NONE or nbdkit.FUA_EMULATE ++ # or nbdkit.FUA_NATIVE ++ ++=item C ++ ++(Optional) ++ ++ def can_cache(h): ++ # return nbdkit.CACHE_NONE or nbdkit.CACHE_EMULATE ++ # or nbdkit.CACHE_NATIVE ++ + =item C + + (Required) +@@ -345,8 +361,6 @@ C, + C, + C, + C, +-C, +-C, + C, + C. + +diff --git a/plugins/python/python.c b/plugins/python/python.c +index b186b99..5e2e526 100644 +--- a/plugins/python/python.c ++++ b/plugins/python/python.c +@@ -815,6 +815,62 @@ py_can_fast_zero (void *handle) + return boolean_callback (handle, "can_fast_zero", NULL); + } + ++static int ++py_can_fua (void *handle) ++{ ++ PyObject *obj = handle; ++ PyObject *fn; ++ PyObject *r; ++ int ret; ++ ++ if (callback_defined ("can_fua", &fn)) { ++ PyErr_Clear (); ++ ++ r = PyObject_CallFunctionObjArgs (fn, obj, NULL); ++ Py_DECREF (fn); ++ if (check_python_failure ("can_fua") == -1) ++ return -1; ++ ret = PyLong_AsLong (r); ++ Py_DECREF (r); ++ return ret; ++ } ++ /* No Python ‘can_fua’, but check if there's a Python ‘flush’ ++ * callback defined. (In C modules, nbdkit would do this). ++ */ ++ else if (callback_defined ("flush", NULL)) ++ return NBDKIT_FUA_EMULATE; ++ else ++ return NBDKIT_FUA_NONE; ++} ++ ++static int ++py_can_cache (void *handle) ++{ ++ PyObject *obj = handle; ++ PyObject *fn; ++ PyObject *r; ++ int ret; ++ ++ if (callback_defined ("can_cache", &fn)) { ++ PyErr_Clear (); ++ ++ r = PyObject_CallFunctionObjArgs (fn, obj, NULL); ++ Py_DECREF (fn); ++ if (check_python_failure ("can_cache") == -1) ++ return -1; ++ ret = PyLong_AsLong (r); ++ Py_DECREF (r); ++ return ret; ++ } ++ /* No Python ‘can_cache’, but check if there's a Python ‘cache’ ++ * callback defined. (In C modules, nbdkit would do this). ++ */ ++ else if (callback_defined ("cache", NULL)) ++ return NBDKIT_CACHE_NATIVE; ++ else ++ return NBDKIT_CACHE_NONE; ++} ++ + #define py_config_help \ + "script= (required) The Python plugin to run.\n" \ + "[other arguments may be used by the plugin that you load]" +@@ -844,6 +900,8 @@ static struct nbdkit_plugin plugin = { + .can_trim = py_can_trim, + .can_zero = py_can_zero, + .can_fast_zero = py_can_fast_zero, ++ .can_fua = py_can_fua, ++ .can_cache = py_can_cache, + + .pread = py_pread, + .pwrite = py_pwrite, +-- +2.18.2 + diff --git a/SOURCES/0010-tests-Test-the-Python-plugin-thoroughly.patch b/SOURCES/0010-tests-Test-the-Python-plugin-thoroughly.patch new file mode 100644 index 0000000..ca3af69 --- /dev/null +++ b/SOURCES/0010-tests-Test-the-Python-plugin-thoroughly.patch @@ -0,0 +1,597 @@ +From 7cb79aef2a12f29f1286caf3858001e47214f871 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Thu, 21 Nov 2019 20:54:41 +0000 +Subject: [PATCH] tests: Test the Python plugin thoroughly. + +This tests the Python plugin thoroughly by issuing client commands +through libnbd and checking we get the expected results. + +(cherry picked from commit 8ead4a82ec3227dbecb6cbfc419f1a18f2817d62) +--- + .gitignore | 1 + + README | 2 + + tests/Makefile.am | 15 +-- + tests/test-lang-plugins.c | 3 +- + tests/test-python-plugin.py | 133 +++++++++++++++++++++ + tests/test-python.sh | 49 ++++++++ + tests/test.py | 60 ---------- + tests/test_python.py | 222 ++++++++++++++++++++++++++++++++++++ + 8 files changed, 413 insertions(+), 72 deletions(-) + create mode 100644 tests/test-python-plugin.py + create mode 100755 tests/test-python.sh + delete mode 100644 tests/test.py + create mode 100755 tests/test_python.py + +diff --git a/.gitignore b/.gitignore +index b25ac7f..e25bd99 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -71,6 +71,7 @@ Makefile.in + /server/synopsis.c + /server/test-public + /stamp-h1 ++/tests/__pycache__/ + /tests/disk + /tests/disk.gz + /tests/disk.xz +diff --git a/README b/README +index 40f4cd3..05f1e06 100644 +--- a/README ++++ b/README +@@ -130,6 +130,8 @@ For the Python plugin: + + - python development libraries + ++ - python unittest to run the test suite ++ + For the OCaml plugin: + + - OCaml >= 4.02.2 +diff --git a/tests/Makefile.am b/tests/Makefile.am +index d225cc6..09103fb 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -67,6 +67,7 @@ EXTRA_PROGRAMS = + TESTS_ENVIRONMENT = \ + PATH=$(abs_top_builddir):$(PATH) \ + SRCDIR=$(srcdir) \ ++ PYTHON=$(PYTHON) \ + LIBGUESTFS_ATTACH_METHOD=appliance \ + LIBGUESTFS_DEBUG=1 \ + LIBGUESTFS_TRACE=1 \ +@@ -160,7 +161,9 @@ EXTRA_DIST = \ + test-probe-plugin.sh \ + test-python-exception.sh \ + test.pl \ +- test.py \ ++ test_python.py \ ++ test-python-plugin.py \ ++ test-python.sh \ + test-rate.sh \ + test-rate-dynamic.sh \ + test.rb \ +@@ -801,18 +804,10 @@ endif HAVE_PERL + if HAVE_PYTHON + + TESTS += \ ++ test-python.sh \ + test-python-exception.sh \ + test-shebang-python.sh \ + $(NULL) +-LIBGUESTFS_TESTS += test-python +- +-test_python_SOURCES = test-lang-plugins.c test.h +-test_python_CFLAGS = \ +- -DLANG='"python"' -DSCRIPT='"$(srcdir)/test.py"' \ +- $(WARNINGS_CFLAGS) \ +- $(LIBGUESTFS_CFLAGS) \ +- $(NULL) +-test_python_LDADD = libtest.la $(LIBGUESTFS_LIBS) + + endif HAVE_PYTHON + +diff --git a/tests/test-lang-plugins.c b/tests/test-lang-plugins.c +index ffb1918..93f9938 100644 +--- a/tests/test-lang-plugins.c ++++ b/tests/test-lang-plugins.c +@@ -56,8 +56,7 @@ main (int argc, char *argv[]) + */ + s = getenv ("NBDKIT_VALGRIND"); + if (s && strcmp (s, "1") == 0 && +- (strcmp (LANG, "python") == 0 || +- strcmp (LANG, "ruby") == 0 || ++ (strcmp (LANG, "ruby") == 0 || + strcmp (LANG, "tcl") == 0)) { + fprintf (stderr, "%s test skipped under valgrind.\n", LANG); + exit (77); /* Tells automake to skip the test. */ +diff --git a/tests/test-python-plugin.py b/tests/test-python-plugin.py +new file mode 100644 +index 0000000..8e90bc2 +--- /dev/null ++++ b/tests/test-python-plugin.py +@@ -0,0 +1,133 @@ ++# nbdkit test plugin ++# Copyright (C) 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. ++ ++"""See test-python.py.""" ++ ++import nbdkit ++import sys ++import pickle ++import base64 ++ ++API_VERSION = 2 ++ ++cfg = {} ++ ++def config (k, v): ++ global cfg ++ if k == "cfg": ++ cfg = pickle.loads (base64.b64decode (v.encode())) ++ ++def config_complete (): ++ print ("set_error = %r" % nbdkit.set_error) ++ ++def open (readonly): ++ return { ++ 'disk': bytearray (cfg.get ('size', 0)) ++ } ++ ++def get_size (h): ++ return len (h['disk']) ++ ++def is_rotational (h): ++ return cfg.get ('is_rotational', False) ++ ++def can_multi_conn (h): ++ return cfg.get ('can_multi_conn', False) ++ ++def can_write (h): ++ return cfg.get ('can_write', True) ++ ++def can_flush (h): ++ return cfg.get ('can_flush', False) ++ ++def can_trim (h): ++ return cfg.get ('can_trim', False) ++ ++def can_zero (h): ++ return cfg.get ('can_zero', False) ++ ++def can_fast_zero (h): ++ return cfg.get ('can_fast_zero', False) ++ ++def can_fua (h): ++ fua = cfg.get ('can_fua', "none") ++ if fua == "none": ++ return nbdkit.FUA_NONE ++ elif fua == "emulate": ++ return nbdkit.FUA_EMULATE ++ elif fua == "native": ++ return nbdkit.FUA_NATIVE ++ ++def can_cache (h): ++ cache = cfg.get ('can_cache', "none") ++ if cache == "none": ++ return nbdkit.CACHE_NONE ++ elif cache == "emulate": ++ return nbdkit.CACHE_EMULATE ++ elif cache == "native": ++ return nbdkit.CACHE_NATIVE ++ ++def pread (h, buf, offset, flags): ++ assert flags == 0 ++ end = offset + len(buf) ++ buf[:] = h['disk'][offset:end] ++ ++def pwrite (h, buf, offset, flags): ++ expect_fua = cfg.get ('pwrite_expect_fua', False) ++ actual_fua = bool (flags & nbdkit.FLAG_FUA) ++ assert expect_fua == actual_fua ++ end = offset + len(buf) ++ h['disk'][offset:end] = buf ++ ++def flush (h, flags): ++ assert flags == 0 ++ ++def trim (h, count, offset, flags): ++ expect_fua = cfg.get ('trim_expect_fua', False) ++ actual_fua = bool (flags & nbdkit.FLAG_FUA) ++ assert expect_fua == actual_fua ++ h['disk'][offset:offset+count] = bytearray(count) ++ ++def zero (h, count, offset, flags): ++ expect_fua = cfg.get ('zero_expect_fua', False) ++ actual_fua = bool (flags & nbdkit.FLAG_FUA) ++ assert expect_fua == actual_fua ++ expect_may_trim = cfg.get ('zero_expect_may_trim', False) ++ actual_may_trim = bool (flags & nbdkit.FLAG_MAY_TRIM) ++ assert expect_may_trim == actual_may_trim ++ expect_fast_zero = cfg.get ('zero_expect_fast_zero', False) ++ actual_fast_zero = bool (flags & nbdkit.FLAG_FAST_ZERO) ++ assert expect_fast_zero == actual_fast_zero ++ h['disk'][offset:offset+count] = bytearray(count) ++ ++def cache (h, count, offset, flags): ++ assert flags == 0 ++ # do nothing +diff --git a/tests/test-python.sh b/tests/test-python.sh +new file mode 100755 +index 0000000..50324d0 +--- /dev/null ++++ b/tests/test-python.sh +@@ -0,0 +1,49 @@ ++#!/usr/bin/env bash ++# nbdkit ++# Copyright (C) 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. ++ ++source ./functions.sh ++set -e ++set -x ++ ++requires $PYTHON --version ++requires $PYTHON -c 'import unittest' ++requires $PYTHON -c 'import nbd' ++requires test -f test_python.py ++requires test -f test-python-plugin.py ++ ++# Python has proven very difficult to valgrind, therefore it is disabled. ++if [ "$NBDKIT_VALGRIND" = "1" ]; then ++ echo "$0: skipping Python test under valgrind." ++ exit 77 ++fi ++ ++$PYTHON -m unittest test_python +diff --git a/tests/test.py b/tests/test.py +deleted file mode 100644 +index 4db5662..0000000 +--- a/tests/test.py ++++ /dev/null +@@ -1,60 +0,0 @@ +-import nbdkit +- +-disk = bytearray(1024*1024) +- +- +-API_VERSION = 2 +- +- +-def config_complete(): +- print ("set_error = %r" % nbdkit.set_error) +- +- +-def open(readonly): +- return 1 +- +- +-def get_size(h): +- global disk +- return len(disk) +- +- +-def can_write(h): +- return True +- +- +-def can_flush(h): +- return True +- +- +-def is_rotational(h): +- return False +- +- +-def can_trim(h): +- return True +- +- +-def pread(h, buf, offset, flags): +- global disk +- end = offset + len(buf) +- buf[:] = disk[offset:end] +- +- +-def pwrite(h, buf, offset, flags): +- global disk +- end = offset + len(buf) +- disk[offset:end] = buf +- +- +-def flush(h, flags): +- pass +- +- +-def trim(h, count, offset, flags): +- pass +- +- +-def zero(h, count, offset, flags): +- global disk +- disk[offset:offset+count] = bytearray(count) +diff --git a/tests/test_python.py b/tests/test_python.py +new file mode 100755 +index 0000000..6b9f297 +--- /dev/null ++++ b/tests/test_python.py +@@ -0,0 +1,222 @@ ++#!/usr/bin/env python3 ++# nbdkit ++# Copyright (C) 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. ++ ++""" ++This tests the Python plugin thoroughly by issuing client commands ++through libnbd and checking we get the expected results. It uses an ++associated plugin (test-python-plugin.sh). ++""" ++ ++import os ++import sys ++import nbd ++import unittest ++import pickle ++import base64 ++ ++class Test (unittest.TestCase): ++ def setUp (self): ++ self.h = nbd.NBD () ++ ++ def tearDown (self): ++ del self.h ++ ++ def connect (self, cfg): ++ cfg = base64.b64encode (pickle.dumps (cfg)).decode() ++ cmd = ["nbdkit", "-v", "-s", "--exit-with-parent", ++ "python", "test-python-plugin.py", "cfg=" + cfg] ++ self.h.connect_command (cmd) ++ ++ def test_none (self): ++ """ ++ Test we can send an empty pickled test configuration and do ++ nothing else. This is just to ensure the machinery of the ++ test works. ++ """ ++ self.connect ({}) ++ ++ def test_size_512 (self): ++ """Test the size.""" ++ self.connect ({"size": 512}) ++ assert self.h.get_size() == 512 ++ ++ def test_size_1m (self): ++ """Test the size.""" ++ self.connect ({"size": 1024*1024}) ++ assert self.h.get_size() == 1024*1024 ++ ++ # Test each flag call. ++ def test_is_rotational_true (self): ++ self.connect ({"size": 512, "is_rotational": True}) ++ assert self.h.is_rotational() ++ ++ def test_is_rotational_false (self): ++ self.connect ({"size": 512, "is_rotational": False}) ++ assert not self.h.is_rotational() ++ ++ def test_can_multi_conn_true (self): ++ self.connect ({"size": 512, "can_multi_conn": True}) ++ assert self.h.can_multi_conn() ++ ++ def test_can_multi_conn_false (self): ++ self.connect ({"size": 512, "can_multi_conn": False}) ++ assert not self.h.can_multi_conn() ++ ++ def test_read_write (self): ++ self.connect ({"size": 512, "can_write": True}) ++ assert not self.h.is_read_only() ++ ++ def test_read_only (self): ++ self.connect ({"size": 512, "can_write": False}) ++ assert self.h.is_read_only() ++ ++ def test_can_flush_true (self): ++ self.connect ({"size": 512, "can_flush": True}) ++ assert self.h.can_flush() ++ ++ def test_can_flush_false (self): ++ self.connect ({"size": 512, "can_flush": False}) ++ assert not self.h.can_flush() ++ ++ def test_can_trim_true (self): ++ self.connect ({"size": 512, "can_trim": True}) ++ assert self.h.can_trim() ++ ++ def test_can_trim_false (self): ++ self.connect ({"size": 512, "can_trim": False}) ++ assert not self.h.can_trim() ++ ++ # nbdkit can always zero because it emulates it. ++ #self.connect ({"size": 512, "can_zero": True}) ++ #assert self.h.can_zero() ++ #self.connect ({"size": 512, "can_zero": False}) ++ #assert not self.h.can_zero() ++ ++ def test_can_fast_zero_true (self): ++ self.connect ({"size": 512, "can_fast_zero": True}) ++ assert self.h.can_fast_zero() ++ ++ def test_can_fast_zero_false (self): ++ self.connect ({"size": 512, "can_fast_zero": False}) ++ assert not self.h.can_fast_zero() ++ ++ def test_can_fua_none (self): ++ self.connect ({"size": 512, "can_fua": "none"}) ++ assert not self.h.can_fua() ++ ++ def test_can_fua_emulate (self): ++ self.connect ({"size": 512, "can_fua": "emulate"}) ++ assert self.h.can_fua() ++ ++ def test_can_fua_native (self): ++ self.connect ({"size": 512, "can_fua": "native"}) ++ assert self.h.can_fua() ++ ++ def test_can_cache_none (self): ++ self.connect ({"size": 512, "can_cache": "none"}) ++ assert not self.h.can_cache() ++ ++ def test_can_cache_emulate (self): ++ self.connect ({"size": 512, "can_cache": "emulate"}) ++ assert self.h.can_cache() ++ ++ def test_can_cache_native (self): ++ self.connect ({"size": 512, "can_cache": "native"}) ++ assert self.h.can_cache() ++ ++ # Not yet implemented: can_extents. ++ ++ def test_pread (self): ++ """Test pread.""" ++ self.connect ({"size": 512}) ++ buf = self.h.pread (512, 0) ++ assert buf == bytearray (512) ++ ++ # Test pwrite + flags. ++ def test_pwrite (self): ++ self.connect ({"size": 512}) ++ buf = bytearray (512) ++ self.h.pwrite (buf, 0) ++ ++ def test_pwrite_fua (self): ++ self.connect ({"size": 512, ++ "can_fua": "native", ++ "pwrite_expect_fua": True}) ++ buf = bytearray (512) ++ self.h.pwrite (buf, 0, nbd.CMD_FLAG_FUA) ++ ++ def test_flush (self): ++ """Test flush.""" ++ self.connect ({"size": 512, "can_flush": True}) ++ self.h.flush () ++ ++ # Test trim + flags. ++ def test_trim (self): ++ self.connect ({"size": 512, "can_trim": True}) ++ self.h.trim (512, 0) ++ ++ def test_trim_fua (self): ++ self.connect ({"size": 512, ++ "can_trim": True, ++ "can_fua": "native", ++ "trim_expect_fua": True}) ++ self.h.trim (512, 0, nbd.CMD_FLAG_FUA) ++ ++ # Test zero + flags. ++ def test_zero (self): ++ self.connect ({"size": 512, "can_zero": True}) ++ self.h.zero (512, 0, nbd.CMD_FLAG_NO_HOLE) ++ ++ def test_zero_fua (self): ++ self.connect ({"size": 512, ++ "can_zero": True, ++ "can_fua": "native", ++ "zero_expect_fua": True}) ++ self.h.zero (512, 0, nbd.CMD_FLAG_NO_HOLE | nbd.CMD_FLAG_FUA) ++ ++ def test_zero_may_trim (self): ++ self.connect ({"size": 512, ++ "can_zero": True, ++ "zero_expect_may_trim": True}) ++ self.h.zero (512, 0, 0) # absence of nbd.CMD_FLAG_NO_HOLE ++ ++ def test_zero_fast_zero (self): ++ self.connect ({"size": 512, ++ "can_zero": True, ++ "can_fast_zero": True, ++ "zero_expect_fast_zero": True}) ++ self.h.zero (512, 0, nbd.CMD_FLAG_NO_HOLE | nbd.CMD_FLAG_FAST_ZERO) ++ ++ def test_cache (self): ++ """Test cache.""" ++ self.connect ({"size": 512, "can_cache": "native"}) ++ self.h.cache (512, 0) +-- +2.18.2 + diff --git a/SOURCES/0011-New-filter-extentlist.patch b/SOURCES/0011-New-filter-extentlist.patch new file mode 100644 index 0000000..6e8ca81 --- /dev/null +++ b/SOURCES/0011-New-filter-extentlist.patch @@ -0,0 +1,790 @@ +From e744dcb38cc52cbe64977efcdd4bc60e802d1b17 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Thu, 23 Jan 2020 19:52:00 +0000 +Subject: [PATCH] New filter: extentlist. + +Allows a list of extents to be placed on top of an existing plugin. + +(cherry picked from commit 3e770b6d6620a62546849a2863638041c0b00640) +--- + TODO | 4 + + configure.ac | 2 + + .../nbdkit-cacheextents-filter.pod | 1 + + filters/extentlist/Makefile.am | 67 ++++ + filters/extentlist/extentlist.c | 326 ++++++++++++++++++ + .../extentlist/nbdkit-extentlist-filter.pod | 90 +++++ + filters/noextents/nbdkit-noextents-filter.pod | 1 + + tests/Makefile.am | 4 + + tests/test-extentlist.sh | 175 ++++++++++ + 9 files changed, 670 insertions(+) + create mode 100644 filters/extentlist/Makefile.am + create mode 100644 filters/extentlist/extentlist.c + create mode 100644 filters/extentlist/nbdkit-extentlist-filter.pod + create mode 100755 tests/test-extentlist.sh + +diff --git a/TODO b/TODO +index d2aca44..2a3e89d 100644 +--- a/TODO ++++ b/TODO +@@ -187,6 +187,10 @@ Suggestions for filters + MBs of extra data) + https://github.com/facebook/zstd/issues/395#issuecomment-535875379 + ++* nbdkit-extentlist-filter could read the extents generated by ++ qemu-img map, allowing extents to be ported from a qemu block ++ device. ++ + nbdkit-rate-filter: + + * allow other kinds of traffic shaping such as VBR +diff --git a/configure.ac b/configure.ac +index fde498b..41e68de 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -896,6 +896,7 @@ filters="\ + cow \ + delay \ + error \ ++ extentlist \ + fua \ + log \ + nocache \ +@@ -979,6 +980,7 @@ AC_CONFIG_FILES([Makefile + filters/cow/Makefile + filters/delay/Makefile + filters/error/Makefile ++ filters/extentlist/Makefile + filters/fua/Makefile + filters/log/Makefile + filters/nocache/Makefile +diff --git a/filters/cacheextents/nbdkit-cacheextents-filter.pod b/filters/cacheextents/nbdkit-cacheextents-filter.pod +index fdd2285..bb2514a 100644 +--- a/filters/cacheextents/nbdkit-cacheextents-filter.pod ++++ b/filters/cacheextents/nbdkit-cacheextents-filter.pod +@@ -52,6 +52,7 @@ C first appeared in nbdkit 1.14. + + L, + L, ++L, + L, + L, + L, +diff --git a/filters/extentlist/Makefile.am b/filters/extentlist/Makefile.am +new file mode 100644 +index 0000000..88a9afe +--- /dev/null ++++ b/filters/extentlist/Makefile.am +@@ -0,0 +1,67 @@ ++# nbdkit ++# Copyright (C) 2019-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. ++ ++include $(top_srcdir)/common-rules.mk ++ ++EXTRA_DIST = nbdkit-extentlist-filter.pod ++ ++filter_LTLIBRARIES = nbdkit-extentlist-filter.la ++ ++nbdkit_extentlist_filter_la_SOURCES = \ ++ extentlist.c \ ++ $(top_srcdir)/include/nbdkit-filter.h \ ++ $(NULL) ++ ++nbdkit_extentlist_filter_la_CPPFLAGS = \ ++ -I$(top_srcdir)/include \ ++ -I$(top_srcdir)/common/include \ ++ -I$(top_srcdir)/common/utils \ ++ $(NULL) ++nbdkit_extentlist_filter_la_CFLAGS = $(WARNINGS_CFLAGS) ++nbdkit_extentlist_filter_la_LDFLAGS = \ ++ -module -avoid-version -shared \ ++ -Wl,--version-script=$(top_srcdir)/filters/filters.syms \ ++ $(NULL) ++nbdkit_extentlist_filter_la_LIBADD = \ ++ $(top_builddir)/common/utils/libutils.la \ ++ $(NULL) ++ ++if HAVE_POD ++ ++man_MANS = nbdkit-extentlist-filter.1 ++CLEANFILES += $(man_MANS) ++ ++nbdkit-extentlist-filter.1: nbdkit-extentlist-filter.pod ++ $(PODWRAPPER) --section=1 --man $@ \ ++ --html $(top_builddir)/html/$@.html \ ++ $< ++ ++endif HAVE_POD +diff --git a/filters/extentlist/extentlist.c b/filters/extentlist/extentlist.c +new file mode 100644 +index 0000000..5f4990b +--- /dev/null ++++ b/filters/extentlist/extentlist.c +@@ -0,0 +1,326 @@ ++/* nbdkit ++ * Copyright (C) 2019-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. ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "cleanup.h" ++#include "minmax.h" ++ ++#define HOLE (NBDKIT_EXTENT_HOLE|NBDKIT_EXTENT_ZERO) ++ ++static const char *extentlist; ++ ++/* List of extents. Once we've finally parsed them this will be ++ * ordered, non-overlapping and have no gaps. ++ */ ++struct extent { ++ uint64_t offset, length; ++ uint32_t type; ++}; ++static struct extent *extents; ++static size_t nr_extents, allocated; ++ ++/* Insert an extent before i. If i = nr_extents, inserts at the end. */ ++static void ++insert_extent (size_t i, struct extent new_extent) ++{ ++ if (nr_extents >= allocated) { ++ allocated = allocated == 0 ? 1 : allocated * 2; ++ extents = realloc (extents, (sizeof (struct extent) * allocated)); ++ if (extents == NULL) { ++ nbdkit_error ("realloc: %m"); ++ exit (EXIT_FAILURE); ++ } ++ } ++ memmove (&extents[i+1], &extents[i], ++ sizeof (struct extent) * (nr_extents-i)); ++ extents[i] = new_extent; ++ nr_extents++; ++} ++ ++static void ++extentlist_unload (void) ++{ ++ free (extents); ++} ++ ++/* Called for each key=value passed on the command line. */ ++static int ++extentlist_config (nbdkit_next_config *next, void *nxdata, ++ const char *key, const char *value) ++{ ++ if (strcmp (key, "extentlist") == 0) { ++ if (extentlist != NULL) { ++ nbdkit_error ("extentlist cannot appear twice"); ++ exit (EXIT_FAILURE); ++ } ++ extentlist = value; ++ return 0; ++ } ++ else ++ return next (nxdata, key, value); ++} ++ ++static int ++compare_offsets (const void *ev1, const void *ev2) ++{ ++ const struct extent *e1 = ev1; ++ const struct extent *e2 = ev2; ++ ++ if (e1->offset < e2->offset) ++ return -1; ++ else if (e1->offset > e2->offset) ++ return 1; ++ else ++ return 0; ++} ++ ++static int ++compare_ranges (const void *ev1, const void *ev2) ++{ ++ const struct extent *e1 = ev1; ++ const struct extent *e2 = ev2; ++ ++ if (e1->offset < e2->offset) ++ return -1; ++ else if (e1->offset >= e2->offset + e2->length) ++ return 1; ++ else ++ return 0; ++} ++ ++/* Similar to parse_extents in plugins/sh/methods.c */ ++static void ++parse_extentlist (void) ++{ ++ FILE *fp; ++ CLEANUP_FREE char *line = NULL; ++ size_t linelen = 0; ++ ssize_t len; ++ size_t i; ++ uint64_t end; ++ ++ assert (extentlist != NULL); ++ assert (extents == NULL); ++ assert (nr_extents == 0); ++ ++ fp = fopen (extentlist, "r"); ++ if (!fp) { ++ nbdkit_error ("open: %s: %m", extentlist); ++ exit (EXIT_FAILURE); ++ } ++ ++ while ((len = getline (&line, &linelen, fp)) != -1) { ++ const char *delim = " \t"; ++ char *sp, *p; ++ int64_t offset, length; ++ uint32_t type; ++ ++ if (len > 0 && line[len-1] == '\n') { ++ line[len-1] = '\0'; ++ len--; ++ } ++ ++ if ((p = strtok_r (line, delim, &sp)) == NULL) { ++ parse_error: ++ nbdkit_error ("%s: cannot parse %s", extentlist, line); ++ exit (EXIT_FAILURE); ++ } ++ offset = nbdkit_parse_size (p); ++ if (offset == -1) ++ exit (EXIT_FAILURE); ++ ++ if ((p = strtok_r (NULL, delim, &sp)) == NULL) ++ goto parse_error; ++ length = nbdkit_parse_size (p); ++ if (length == -1) ++ exit (EXIT_FAILURE); ++ ++ /* Skip zero length extents. Makes the rest of the code easier. */ ++ if (length == 0) ++ continue; ++ ++ if ((p = strtok_r (NULL, delim, &sp)) == NULL) ++ /* empty type field means allocated data (0) */ ++ type = 0; ++ else if (sscanf (p, "%" SCNu32, &type) == 1) ++ ; ++ else { ++ type = 0; ++ if (strstr (p, "hole") != NULL) ++ type |= NBDKIT_EXTENT_HOLE; ++ if (strstr (p, "zero") != NULL) ++ type |= NBDKIT_EXTENT_ZERO; ++ } ++ ++ insert_extent (nr_extents, ++ (struct extent){.offset = offset, .length=length, ++ .type=type}); ++ } ++ ++ fclose (fp); ++ ++ /* Sort the extents by offset. */ ++ qsort (extents, nr_extents, sizeof (struct extent), compare_offsets); ++ ++ /* There must not be overlaps at this point. */ ++ end = 0; ++ for (i = 0; i < nr_extents; ++i) { ++ if (extents[i].offset < end || ++ extents[i].offset + extents[i].length < extents[i].offset) { ++ nbdkit_error ("extents in the extent list are overlapping"); ++ exit (EXIT_FAILURE); ++ } ++ end = extents[i].offset + extents[i].length; ++ } ++ ++ /* If there's a gap at the beginning, insert a hole|zero extent. */ ++ if (nr_extents == 0 || extents[0].offset > 0) { ++ end = nr_extents == 0 ? UINT64_MAX : extents[0].offset; ++ insert_extent (0, (struct extent){.offset = 0, .length = end, ++ .type = HOLE}); ++ } ++ ++ /* Now insert hole|zero extents after every extent where there ++ * is a gap between that extent and the next one. ++ */ ++ for (i = 0; i < nr_extents-1; ++i) { ++ end = extents[i].offset + extents[i].length; ++ if (end < extents[i+1].offset) ++ insert_extent (i+1, (struct extent){.offset = end, ++ .length = extents[i+1].offset - end, ++ .type = HOLE}); ++ } ++ ++ /* If there's a gap at the end, insert a hole|zero extent. */ ++ end = extents[nr_extents-1].offset + extents[nr_extents-1].length; ++ if (end < UINT64_MAX) ++ insert_extent (nr_extents, (struct extent){.offset = end, ++ .length = UINT64_MAX-end, ++ .type = HOLE}); ++ ++ /* Debug the final list. */ ++ for (i = 0; i < nr_extents; ++i) { ++ nbdkit_debug ("extentlist: " ++ "extent[%zu] = %" PRIu64 "-%" PRIu64 " (length %" PRIu64 ")" ++ " type %" PRIu32, ++ i, extents[i].offset, ++ extents[i].offset + extents[i].length - 1, ++ extents[i].length, ++ extents[i].type); ++ } ++} ++ ++static int ++extentlist_config_complete (nbdkit_next_config_complete *next, void *nxdata) ++{ ++ if (extentlist == NULL) { ++ nbdkit_error ("you must supply the extentlist parameter " ++ "on the command line"); ++ return -1; ++ } ++ ++ parse_extentlist (); ++ ++ return next (nxdata); ++} ++ ++static int ++extentlist_can_extents (struct nbdkit_next_ops *next_ops, void *nxdata, ++ void *handle) ++{ ++ return 1; ++} ++ ++/* Use ‘-D extentlist.lookup=1’ to debug the function below. */ ++int extentlist_debug_lookup = 0; ++ ++/* Read extents. */ ++static int ++extentlist_extents (struct nbdkit_next_ops *next_ops, void *nxdata, ++ void *handle, uint32_t count, uint64_t offset, ++ uint32_t flags, ++ struct nbdkit_extents *ret_extents, ++ int *err) ++{ ++ const struct extent eoffset = { .offset = offset }; ++ struct extent *p; ++ ssize_t i; ++ uint64_t end; ++ ++ /* Find the starting point in the extents list. */ ++ p = bsearch (&eoffset, extents, ++ nr_extents, sizeof (struct extent), compare_ranges); ++ assert (p != NULL); ++ i = p - extents; ++ ++ /* Add extents to the output. */ ++ while (count > 0) { ++ if (extentlist_debug_lookup) ++ nbdkit_debug ("extentlist lookup: " ++ "loop i=%zd count=%" PRIu32 " offset=%" PRIu64, ++ i, count, offset); ++ ++ end = extents[i].offset + extents[i].length; ++ if (nbdkit_add_extent (ret_extents, offset, end - offset, ++ extents[i].type) == -1) ++ return -1; ++ ++ count -= MIN (count, end-offset); ++ offset = end; ++ i++; ++ } ++ ++ return 0; ++} ++ ++static struct nbdkit_filter filter = { ++ .name = "extentlist", ++ .longname = "nbdkit extentlist filter", ++ .unload = extentlist_unload, ++ .config = extentlist_config, ++ .config_complete = extentlist_config_complete, ++ .can_extents = extentlist_can_extents, ++ .extents = extentlist_extents, ++}; ++ ++NBDKIT_REGISTER_FILTER(filter) +diff --git a/filters/extentlist/nbdkit-extentlist-filter.pod b/filters/extentlist/nbdkit-extentlist-filter.pod +new file mode 100644 +index 0000000..adfb4ad +--- /dev/null ++++ b/filters/extentlist/nbdkit-extentlist-filter.pod +@@ -0,0 +1,90 @@ ++=head1 NAME ++ ++nbdkit-extentlist-filter - place extent list over a plugin ++ ++=head1 SYNOPSIS ++ ++ nbdkit --filter=extentlist plugin extentlist=FILENAME ++ ++=head1 DESCRIPTION ++ ++C is an nbdkit filter lets you place a ++static list of extents on top of an existing plugin. Extents record ++whether or not specific parts of the disk are allocated or sparse. ++ ++You can use this with plugins which cannot get extent information ++themselves, but you can get this information from another source. One ++place where it is useful is with L because the ++sftp protocol does not support reading sparseness information, but you ++may be able to get this information directly from the source disk on ++the remote server. ++ ++=head1 FILE FORMAT ++ ++The list of extents is specified in a text file. There is one extent ++specified per line. Each line has the format: ++ ++ offset length type ++ ++The C and C fields may use any format understood by ++C. The optional C field may be an integer, ++missing (same as 0), or a comma-separated list of the words C ++and C. (The fields correspond to the inputs of the ++C function, see L). ++ ++An example of a valid set of extents covering a C<10M> disk where the ++first megabyte only is allocated data: ++ ++ 0 1M ++ 1M 9M hole,zero ++ ++Or you could omit the C extent since any gaps are assumed ++to be holes with that type: ++ ++ 0 1M ++ ++The extent list need not cover the whole disk, and does not need to be ++in ascending order, but it must I contain overlapping extents. ++ ++=head1 PARAMETERS ++ ++=over 4 ++ ++=item BFILENAME ++ ++Specify the file containing the extent list, in the format described ++in L above. ++ ++=back ++ ++=head1 FILES ++ ++=over 4 ++ ++=item F<$filterdir/nbdkit-extentlist-filter.so> ++ ++The filter. ++ ++Use C to find the location of C<$filterdir>. ++ ++=back ++ ++=head1 VERSION ++ ++C first appeared in nbdkit 1.18. ++ ++=head1 SEE ALSO ++ ++L, ++L, ++L, ++L, ++L. ++ ++=head1 AUTHORS ++ ++Richard W.M. Jones ++ ++=head1 COPYRIGHT ++ ++Copyright (C) 2020 Red Hat Inc. +diff --git a/filters/noextents/nbdkit-noextents-filter.pod b/filters/noextents/nbdkit-noextents-filter.pod +index 991ecfe..0260a5c 100644 +--- a/filters/noextents/nbdkit-noextents-filter.pod ++++ b/filters/noextents/nbdkit-noextents-filter.pod +@@ -47,6 +47,7 @@ C first appeared in nbdkit 1.14. + + L, + L, ++L, + L, + L, + L, +diff --git a/tests/Makefile.am b/tests/Makefile.am +index 09103fb..b99952f 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -110,6 +110,7 @@ EXTRA_DIST = \ + test-error100.sh \ + test-error-triggered.sh \ + test-export-name.sh \ ++ test-extentlist.sh \ + test-file-extents.sh \ + test-floppy.sh \ + test-foreground.sh \ +@@ -1009,6 +1010,9 @@ TESTS += \ + test-error-triggered.sh \ + $(NULL) + ++# extentlist filter test. ++TESTS += test-extentlist.sh ++ + # fua filter test. + TESTS += test-fua.sh + +diff --git a/tests/test-extentlist.sh b/tests/test-extentlist.sh +new file mode 100755 +index 0000000..7d05de4 +--- /dev/null ++++ b/tests/test-extentlist.sh +@@ -0,0 +1,175 @@ ++#!/usr/bin/env bash ++# nbdkit ++# Copyright (C) 2016-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 extentlist filter. ++ ++source ./functions.sh ++set -e ++set -x ++ ++requires jq --version ++requires qemu-img --version ++requires qemu-img map --help ++ ++out=test-extentlist.out ++input=test-extentlist.in ++expected=test-extentlist.expected ++files="$out $input $expected" ++rm -f $files ++cleanup_fn rm $files ++ ++test () ++{ ++ nbdkit -v -D extentlist.lookup=1 \ ++ -U - \ ++ --filter=extentlist \ ++ null size=$1 extentlist=$input \ ++ --run 'qemu-img map -f raw --output=json $nbd' | ++ jq -c '.[] | {start:.start, length:.length, data:.data, zero:.zero}' \ ++ > $out ++ diff -u $out $expected ++} ++ ++# Empty extent list. ++cat > $input <<'EOF' ++EOF ++ ++cat > $expected <<'EOF' ++{"start":0,"length":0,"data":false,"zero":false} ++EOF ++test 0 ++cat > $expected <<'EOF' ++{"start":0,"length":1048576,"data":false,"zero":true} ++EOF ++test 1M ++ ++# Extent list covering 0-1M with data. ++cat > $input <<'EOF' ++0 1M ++EOF ++ ++cat > $expected <<'EOF' ++{"start":0,"length":0,"data":false,"zero":false} ++EOF ++test 0 ++cat > $expected <<'EOF' ++{"start":0,"length":1048576,"data":true,"zero":false} ++EOF ++test 1M ++ ++# Extent list covering 1-2M with data. ++cat > $input <<'EOF' ++1M 1M ++EOF ++ ++cat > $expected <<'EOF' ++{"start":0,"length":0,"data":false,"zero":false} ++EOF ++test 0 ++cat > $expected <<'EOF' ++{"start":0,"length":1048576,"data":false,"zero":true} ++EOF ++test 1M ++cat > $expected <<'EOF' ++{"start":0,"length":1048576,"data":false,"zero":true} ++{"start":1048576,"length":1048576,"data":true,"zero":false} ++EOF ++test 2M ++cat > $expected <<'EOF' ++{"start":0,"length":1048576,"data":false,"zero":true} ++{"start":1048576,"length":1048576,"data":true,"zero":false} ++{"start":2097152,"length":1048576,"data":false,"zero":true} ++EOF ++test 3M ++ ++# Extent list covering 1-2M with data, but in a more fragmented ++# way than the above. ++cat > $input <<'EOF' ++1024K 512K ++1536K 512K ++EOF ++ ++cat > $expected <<'EOF' ++{"start":0,"length":0,"data":false,"zero":false} ++EOF ++test 0 ++cat > $expected <<'EOF' ++{"start":0,"length":1048576,"data":false,"zero":true} ++EOF ++test 1M ++cat > $expected <<'EOF' ++{"start":0,"length":1048576,"data":false,"zero":true} ++{"start":1048576,"length":1048576,"data":true,"zero":false} ++EOF ++test 2M ++cat > $expected <<'EOF' ++{"start":0,"length":1048576,"data":false,"zero":true} ++{"start":1048576,"length":1048576,"data":true,"zero":false} ++{"start":2097152,"length":1048576,"data":false,"zero":true} ++EOF ++test 3M ++ ++# Adjacent data and holes. ++cat > $input <<'EOF' ++0 1M ++2M 1M ++4M 1M ++EOF ++ ++cat > $expected <<'EOF' ++{"start":0,"length":0,"data":false,"zero":false} ++EOF ++test 0 ++cat > $expected <<'EOF' ++{"start":0,"length":1048576,"data":true,"zero":false} ++EOF ++test 1M ++cat > $expected <<'EOF' ++{"start":0,"length":1048576,"data":true,"zero":false} ++{"start":1048576,"length":1048576,"data":false,"zero":true} ++EOF ++test 2M ++cat > $expected <<'EOF' ++{"start":0,"length":1048576,"data":true,"zero":false} ++{"start":1048576,"length":1048576,"data":false,"zero":true} ++{"start":2097152,"length":1048576,"data":true,"zero":false} ++EOF ++test 3M ++cat > $expected <<'EOF' ++{"start":0,"length":1048576,"data":true,"zero":false} ++{"start":1048576,"length":1048576,"data":false,"zero":true} ++{"start":2097152,"length":1048576,"data":true,"zero":false} ++{"start":3145728,"length":1048576,"data":false,"zero":true} ++{"start":4194304,"length":1048576,"data":true,"zero":false} ++{"start":5242880,"length":1048576,"data":false,"zero":true} ++EOF ++test 6M +-- +2.18.2 + diff --git a/SOURCES/0012-extentlist-Documentation-and-test-fixes.patch b/SOURCES/0012-extentlist-Documentation-and-test-fixes.patch new file mode 100644 index 0000000..c0275ec --- /dev/null +++ b/SOURCES/0012-extentlist-Documentation-and-test-fixes.patch @@ -0,0 +1,125 @@ +From 2a3e909e9e1ccb608bde75b76524acd753b33889 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Sat, 25 Jan 2020 11:38:14 +0000 +Subject: [PATCH] extentlist: Documentation and test fixes. + +Updates commit 3e770b6d6620a62546849a2863638041c0b00640. + +(cherry picked from commit c16709ef663a5ed9fd9ddef4e379f316d84c9a07) +--- + TODO | 12 +++++++---- + .../extentlist/nbdkit-extentlist-filter.pod | 21 +++++++++++++------ + plugins/curl/nbdkit-curl-plugin.pod | 1 + + plugins/ssh/nbdkit-ssh-plugin.pod | 1 + + tests/test-extentlist.sh | 2 +- + 5 files changed, 26 insertions(+), 11 deletions(-) + +diff --git a/TODO b/TODO +index 2a3e89d..e1ac71c 100644 +--- a/TODO ++++ b/TODO +@@ -187,10 +187,6 @@ Suggestions for filters + MBs of extra data) + https://github.com/facebook/zstd/issues/395#issuecomment-535875379 + +-* nbdkit-extentlist-filter could read the extents generated by +- qemu-img map, allowing extents to be ported from a qemu block +- device. +- + nbdkit-rate-filter: + + * allow other kinds of traffic shaping such as VBR +@@ -216,6 +212,14 @@ nbdkit-retry-filter: + + * subsecond times + ++nbdkit-extentlist-filter: ++ ++* read the extents generated by qemu-img map, allowing extents to be ++ ported from a qemu block device ++ ++* make non-read-only access safe by updating the extent list when the ++ filter sees writes and trims ++ + Filters for security + -------------------- + +diff --git a/filters/extentlist/nbdkit-extentlist-filter.pod b/filters/extentlist/nbdkit-extentlist-filter.pod +index adfb4ad..5d1a38a 100644 +--- a/filters/extentlist/nbdkit-extentlist-filter.pod ++++ b/filters/extentlist/nbdkit-extentlist-filter.pod +@@ -4,7 +4,7 @@ nbdkit-extentlist-filter - place extent list over a plugin + + =head1 SYNOPSIS + +- nbdkit --filter=extentlist plugin extentlist=FILENAME ++ nbdkit -r --filter=extentlist plugin extentlist=FILENAME + + =head1 DESCRIPTION + +@@ -13,11 +13,20 @@ static list of extents on top of an existing plugin. Extents record + whether or not specific parts of the disk are allocated or sparse. + + You can use this with plugins which cannot get extent information +-themselves, but you can get this information from another source. One +-place where it is useful is with L because the +-sftp protocol does not support reading sparseness information, but you +-may be able to get this information directly from the source disk on +-the remote server. ++themselves, but where you can get this information from another ++source. One place where it is useful is with L ++because the sftp protocol does not support reading sparseness ++information, but you may be able to get this information directly from ++the source disk on the remote server using commands such as ++L. A similar situation applies to ++L. ++ ++Note that the extent list is read-only. This filter does not monitor ++writes and trims in order to update the extent list. What can happen ++is that you would write to a “hole” in the disk, but would not be able ++to read it back because the NBD client would still think that part of ++the disk is a hole. So it is generally only safe to use this filter ++in read-only mode (I<-r> option). + + =head1 FILE FORMAT + +diff --git a/plugins/curl/nbdkit-curl-plugin.pod b/plugins/curl/nbdkit-curl-plugin.pod +index 827e0bd..d3c8524 100644 +--- a/plugins/curl/nbdkit-curl-plugin.pod ++++ b/plugins/curl/nbdkit-curl-plugin.pod +@@ -182,6 +182,7 @@ L, + L + L, + L, ++L, + L, + L, + L, +diff --git a/plugins/ssh/nbdkit-ssh-plugin.pod b/plugins/ssh/nbdkit-ssh-plugin.pod +index 0a0421d..3fc3146 100644 +--- a/plugins/ssh/nbdkit-ssh-plugin.pod ++++ b/plugins/ssh/nbdkit-ssh-plugin.pod +@@ -316,6 +316,7 @@ C first appeared in nbdkit 1.12. + + L, + L, ++L, + L, + L, + L, +diff --git a/tests/test-extentlist.sh b/tests/test-extentlist.sh +index 7d05de4..73ce3ca 100755 +--- a/tests/test-extentlist.sh ++++ b/tests/test-extentlist.sh +@@ -50,7 +50,7 @@ cleanup_fn rm $files + test () + { + nbdkit -v -D extentlist.lookup=1 \ +- -U - \ ++ -r -U - \ + --filter=extentlist \ + null size=$1 extentlist=$input \ + --run 'qemu-img map -f raw --output=json $nbd' | +-- +2.18.2 + diff --git a/SOURCES/nbdkit-1.16.2.tar.gz.sig b/SOURCES/nbdkit-1.16.2.tar.gz.sig new file mode 100644 index 0000000..1360653 --- /dev/null +++ b/SOURCES/nbdkit-1.16.2.tar.gz.sig @@ -0,0 +1,17 @@ +-----BEGIN PGP SIGNATURE----- + +iQJFBAABCAAvFiEE93dPsa0HSn6Mh2fqkXOPc+G3aKAFAl4wFvoRHHJpY2hAYW5u +ZXhpYS5vcmcACgkQkXOPc+G3aKASexAAmpZw61rCI7SY8zm4O0gb+pIx7oLYx0Lq +2puIftzxUUw9Q6pFJJyXSvlsvHy3qUF7HiMVdpW61ItIChV1xBDVKEPAacNzsZh4 +30CI7kfJMfj6u+hpOCVlLk4uJFjZkmIpEKkDpEBemxLMME4JsLJdawKzKhjT2PI7 +dWMjYkOeD4NkAzQLQGskEswoIgZQ0twuyPUErjEL9fcXw4OjxFvQJG85FsIF2lR6 +FUDQg5y9YLzeMJMsjW4rO+LAz2c1mJwYR1EgYP43avm/pJfd1mVQLGRoLb7NwMSw +6mkwhJ4Kvq6BN0PSqpKqQtXZrDoElWN8cVJVf+dAjONcvzYi0gsHWDL+FZ731Q2M +s4nq0aRscBTL2DOaE9DzBY2AO1jKUB/+02qRpidWTYBmsmL2QQI8n33Q7JuDuEXX +bVm1RDA4ike4PUXXY5KJ6MZhKID5453SVFausFse+u4MCQHQPFYspkXmaNWRhjgs +yu2zPc9jHdBkpzNov/CCZoFketFRz/BKexBeH2vcfTYfREVf9lEZi7qEa0kQHDn9 +EMTFsCqmGat9TEVbt9t8c/tODTeRE00MFx4gPspzy+m4YP+Gl3ySHsAbPQ90uBGX +c8xggwqWXr1GAP5HbAhs/Bs7USrWMMgqii1ppnzoAkHh+j4rsdL4dS2dmhxX756u +IKP/JC2oA8U= +=mV8z +-----END PGP SIGNATURE----- diff --git a/SPECS/nbdkit.spec b/SPECS/nbdkit.spec new file mode 100644 index 0000000..69154f9 --- /dev/null +++ b/SPECS/nbdkit.spec @@ -0,0 +1,993 @@ +%global _hardened_build 1 + +%ifarch aarch64 %{arm} x86_64 ppc %{power64} +%global have_libguestfs 1 +%endif + +# Architectures where the complete test suite must pass. +# +# On all other architectures, a simpler test suite must pass. This +# omits any tests that run full qemu, since running qemu under TCG is +# often broken on non-x86_64 arches. +%global complete_test_arches x86_64 + +# Disable libvirt on riscv64 for now. +%ifnarch riscv64 +%global have_libvirt 1 +%endif + +# If we should verify tarball signature with GPGv2. +%global verify_tarball_signature 1 + +# If there are patches which touch autotools files, set this to 1. +%global patches_touch_autotools 1 + +# The source directory. +%global source_directory 1.16-stable + +Name: nbdkit +Version: 1.16.2 +Release: 2%{?dist} +Summary: NBD server + +License: BSD +URL: https://github.com/libguestfs/nbdkit + +Source0: http://libguestfs.org/download/nbdkit/%{source_directory}/%{name}-%{version}.tar.gz +%if 0%{verify_tarball_signature} +Source1: http://libguestfs.org/download/nbdkit/%{source_directory}/%{name}-%{version}.tar.gz.sig +# Keyring used to verify tarball signature. +Source2: libguestfs.keyring +%endif + +# Patches come from this upstream branch: +# https://github.com/libguestfs/nbdkit/tree/rhel-8.2 + +# Patches. +Patch0001: 0001-server-Allow-D-nbdkit.-debug-flags-for-the-core-serv.patch +Patch0002: 0002-server-Allow-D-debug-flags-to-contain-dots-for-names.patch +Patch0003: 0003-server-Add-D-nbdkit.backend.controlpath-and-D-nbdkit.patch +Patch0004: 0004-python-Add-various-constants-to-the-API.patch +Patch0005: 0005-python-Implement-nbdkit-API-version-2.patch +Patch0006: 0006-python-Implement-cache.patch +Patch0007: 0007-python-Implement-can_zero-can_fast_zero.patch +Patch0008: 0008-python-Implement-can_multi_conn.patch +Patch0009: 0009-python-Implement-can_fua-and-can_cache.patch +Patch0010: 0010-tests-Test-the-Python-plugin-thoroughly.patch +Patch0011: 0011-New-filter-extentlist.patch +Patch0012: 0012-extentlist-Documentation-and-test-fixes.patch + +%if 0%{patches_touch_autotools} +BuildRequires: autoconf, automake, libtool +%endif + +%if 0%{patches_touch_autotools} +BuildRequires: autoconf, automake, libtool +%endif + +%if 0%{?rhel} == 8 +# On RHEL 8, we cannot build the package on i686 (no virt stack). +ExcludeArch: i686 +%endif + +%ifnarch %{complete_test_arches} +BuildRequires: autoconf, automake, libtool +%endif +BuildRequires: /usr/bin/pod2man +BuildRequires: gnutls-devel +BuildRequires: libselinux-devel +%if 0%{?have_libguestfs} +BuildRequires: libguestfs-devel +%endif +%if 0%{?have_libvirt} +BuildRequires: libvirt-devel +%endif +BuildRequires: xz-devel +BuildRequires: zlib-devel +BuildRequires: libcurl-devel +BuildRequires: libssh-devel +BuildRequires: e2fsprogs, e2fsprogs-devel +BuildRequires: bash-completion +BuildRequires: perl-devel +BuildRequires: perl(ExtUtils::Embed) +BuildRequires: python3-devel +%if 0%{verify_tarball_signature} +BuildRequires: gnupg2 +%endif + +# Only for running the test suite: +BuildRequires: /usr/bin/certtool +BuildRequires: jq +BuildRequires: /usr/bin/nbdsh +BuildRequires: /usr/bin/qemu-img +BuildRequires: /usr/bin/socat +BuildRequires: /usr/sbin/ss +BuildRequires: /usr/bin/ssh-keygen + +# nbdkit is a metapackage pulling the server and a useful subset +# of the plugins and filters. +Requires: nbdkit-server%{?_isa} = %{version}-%{release} +Requires: nbdkit-basic-plugins%{?_isa} = %{version}-%{release} +Requires: nbdkit-basic-filters%{?_isa} = %{version}-%{release} + + +%description +NBD is a protocol for accessing block devices (hard disks and +disk-like things) over the network. + +nbdkit is a toolkit for creating NBD servers. + +The key features are: + +* Multithreaded NBD server written in C with good performance. + +* Minimal dependencies for the basic server. + +* Liberal license (BSD) allows nbdkit to be linked to proprietary + libraries or included in proprietary code. + +* Well-documented, simple plugin API with a stable ABI guarantee. + Lets you to export "unconventional" block devices easily. + +* You can write plugins in C or many other languages. + +* Filters can be stacked in front of plugins to transform the output. + +In Red Hat Enterprise Linux, '%{name}' is a meta-package which pulls +in the core server and a useful subset of plugins and filters. + +If you want just the server, install '%{name}-server'. + +To develop plugins, install the '%{name}-devel' package and start by +reading the nbdkit(1) and nbdkit-plugin(3) manual pages. + + +%package server +Summary: The %{name} server +License: BSD + +Conflicts: nbdkit < 1.12 + + +%description server +This package contains the %{name} server with no plugins or filters. + + +%package basic-plugins +Summary: Basic plugins for %{name} +License: BSD + +Requires: %{name}-server%{?_isa} = %{version}-%{release} +Provides: %{name}-data-plugin = %{version}-%{release} +Provides: %{name}-file-plugin = %{version}-%{release} +Provides: %{name}-floppy-plugin = %{version}-%{release} +Provides: %{name}-full-plugin = %{version}-%{release} +Provides: %{name}-info-plugin = %{version}-%{release} +Provides: %{name}-memory-plugin = %{version}-%{release} +Provides: %{name}-null-plugin = %{version}-%{release} +Provides: %{name}-pattern-plugin = %{version}-%{release} +Provides: %{name}-partitioning-plugin = %{version}-%{release} +Provides: %{name}-random-plugin = %{version}-%{release} +Provides: %{name}-sh-plugin = %{version}-%{release} +Provides: %{name}-split-plugin = %{version}-%{release} +Provides: %{name}-streaming-plugin = %{version}-%{release} +Provides: %{name}-zero-plugin = %{version}-%{release} + + +%description basic-plugins +This package contains some basic plugins for %{name} which have only +trivial dependencies. + +nbdkit-data-plugin Serve small amounts of data from the command line. + +nbdkit-file-plugin The normal file plugin for serving files. + +nbdkit-floppy-plugin Create a virtual floppy disk from a directory. + +nbdkit-full-plugin A virtual disk that returns ENOSPC errors. + +nbdkit-info-plugin Serve client and server information. + +nbdkit-memory-plugin A virtual memory plugin. + +nbdkit-null-plugin A null (bitbucket) plugin. + +nbdkit-pattern-plugin Fixed test pattern. + +nbdkit-partitioning-plugin Create virtual disks from partitions. + +nbdkit-random-plugin Random content plugin for testing. + +nbdkit-sh-plugin Write plugins as shell scripts or executables. + +nbdkit-split-plugin Concatenate one or more files. + +nbdkit-streaming-plugin A streaming file serving plugin. + +nbdkit-zero-plugin Zero-length plugin for testing. + + +%package example-plugins +Summary: Example plugins for %{name} +License: BSD + +Requires: %{name}-server%{?_isa} = %{version}-%{release} + + +%description example-plugins +This package contains example plugins for %{name}. + + +# The plugins below have non-trivial dependencies are so are +# packaged separately. + +%package curl-plugin +Summary: HTTP/FTP (cURL) plugin for %{name} +License: BSD + +Requires: %{name}-server%{?_isa} = %{version}-%{release} + + +%description curl-plugin +This package contains cURL (HTTP/FTP) support for %{name}. + + +%package gzip-plugin +Summary: GZip file serving plugin for %{name} +License: BSD + +# Upgrade path from RHEL 8.0 +Provides: %{name}-plugin-gzip = %{version}-%{release} +Obsoletes: %{name}-plugin-gzip <= %{version}-%{release} + +Requires: %{name}-server%{?_isa} = %{version}-%{release} + + +%description gzip-plugin +This package is a gzip file serving plugin for %{name}. + + +%package linuxdisk-plugin +Summary: Virtual Linux disk plugin for %{name} +License: BSD + +Requires: %{name}-server%{?_isa} = %{version}-%{release} +# for mke2fs +Requires: e2fsprogs + + +%description linuxdisk-plugin +This package is a virtual Linux disk plugin for %{name}. + + +%package python-plugin +Summary: Python 3 plugin for %{name} +License: BSD + +# Upgrade path from RHEL 8.0 +Provides: %{name}-plugin-python-common = %{version}-%{release} +Obsoletes: %{name}-plugin-python-common <= %{version}-%{release} +Provides: %{name}-plugin-python3 = %{version}-%{release} +Obsoletes: %{name}-plugin-python3 <= %{version}-%{release} + +Requires: %{name}-server%{?_isa} = %{version}-%{release} + + +%description python-plugin +This package lets you write Python 3 plugins for %{name}. + + +%package ssh-plugin +Summary: SSH plugin for %{name} +License: BSD + +Requires: %{name}-server%{?_isa} = %{version}-%{release} + + +%description ssh-plugin +This package contains SSH support for %{name}. + + +%ifarch %{ix86} x86_64 +%package vddk-plugin +Summary: VMware VDDK plugin for %{name} +License: BSD + +# Upgrade path from RHEL 8.0 +Provides: %{name}-plugin-vddk = %{version}-%{release} +Obsoletes: %{name}-plugin-vddk <= %{version}-%{release} + +Requires: %{name}-server%{?_isa} = %{version}-%{release} + + +%description vddk-plugin +This package is a plugin for %{name} which connects to +VMware VDDK for accessing VMware disks and servers. +%endif + + +%package basic-filters +Summary: Basic filters for %{name} +License: BSD + +Requires: %{name}-server%{?_isa} = %{version}-%{release} +Provides: %{name}-blocksize-filter = %{version}-%{release} +Provides: %{name}-cache-filter = %{version}-%{release} +Provides: %{name}-cacheextents-filter = %{version}-%{release} +Provides: %{name}-cow-filter = %{version}-%{release} +Provides: %{name}-delay-filter = %{version}-%{release} +Provides: %{name}-error-filter = %{version}-%{release} +Provides: %{name}-extentlist-filter = %{version}-%{release} +Provides: %{name}-fua-filter = %{version}-%{release} +Provides: %{name}-log-filter = %{version}-%{release} +Provides: %{name}-nocache-filter = %{version}-%{release} +Provides: %{name}-noextents-filter = %{version}-%{release} +Provides: %{name}-noparallel-filter = %{version}-%{release} +Provides: %{name}-nozero-filter = %{version}-%{release} +Provides: %{name}-offset-filter = %{version}-%{release} +Provides: %{name}-partition-filter = %{version}-%{release} +Provides: %{name}-rate-filter = %{version}-%{release} +Provides: %{name}-readahead-filter = %{version}-%{release} +Provides: %{name}-retry-filter = %{version}-%{release} +Provides: %{name}-stats-filter = %{version}-%{release} +Provides: %{name}-truncate-filter = %{version}-%{release} + +%description basic-filters +This package contains some basic filters for %{name} which have only +trivial dependencies. + +nbdkit-blocksize-filter Adjust block size of requests sent to plugins. + +nbdkit-cache-filter Server-side cache. + +nbdkit-cacheextents-filter Cache extents. + +nbdkit-cow-filter Copy-on-write overlay for read-only plugins. + +nbdkit-delay-filter Inject read and write delays. + +nbdkit-error-filter Inject errors. + +nbdkit-extentlist-filter Place extent list over a plugin. + +nbdkit-fua-filter Modify flush behaviour in plugins. + +nbdkit-log-filter Log all transactions to a file. + +nbdkit-nocache-filter Disable cache requests in the underlying plugin. + +nbdkit-noextents-filter Disable extents in the underlying plugin. + +nbdkit-noparallel-filter Serialize requests to the underlying plugin. + +nbdkit-nozero-filter Adjust handling of zero requests by plugins. + +nbdkit-offset-filter Serve an offset and range. + +nbdkit-partition-filter Serve a single partition. + +nbdkit-rate-filter Limit bandwidth by connection or server. + +nbdkit-readahead-filter Prefetch data when reading sequentially. + +nbdkit-retry-filter Reopen connection on error. + +nbdkit-stats-filter Display statistics about operations. + +nbdkit-truncate-filter Truncate, expand, round up or round down size. + + +%package xz-filter +Summary: XZ filter for %{name} +License: BSD + +# Upgrade path from RHEL 8.0 +Provides: %{name}-plugin-xz = %{version}-%{release} +Obsoletes: %{name}-plugin-xz <= %{version}-%{release} + +Requires: %{name}-server%{?_isa} = %{version}-%{release} + + +%description xz-filter +This package is the xz filter for %{name}. + + +%package devel +Summary: Development files and documentation for %{name} +License: BSD + +Requires: %{name}-server%{?_isa} = %{version}-%{release} +Requires: pkgconfig + + +%description devel +This package contains development files and documentation +for %{name}. Install this package if you want to develop +plugins for %{name}. + + +%package bash-completion +Summary: Bash tab-completion for %{name} +BuildArch: noarch +Requires: bash-completion >= 2.0 +Requires: %{name}-server = %{version}-%{release} + + +%description bash-completion +Install this package if you want intelligent bash tab-completion +for %{name}. + + +%prep +%if 0%{verify_tarball_signature} +tmphome="$(mktemp -d)" +gpgv2 --homedir "$tmphome" --keyring %{SOURCE2} %{SOURCE1} %{SOURCE0} +%endif +%autosetup -p1 +%if 0%{patches_touch_autotools} +autoreconf -i +%endif + +%ifnarch %{complete_test_arches} +# Simplify the test suite so it doesn't require qemu. +sed -i -e '/^if HAVE_LIBGUESTFS/,/^endif HAVE_LIBGUESTFS/d' tests/Makefile.am +sed -i -e '/^if HAVE_GUESTFISH/,/^endif HAVE_GUESTFISH/d' tests/Makefile.am +autoreconf -i +%endif + + +%build +%configure \ + PYTHON=%{_bindir}/python3 \ + --disable-static \ + --disable-lua \ + --disable-perl \ + --disable-nbd-plugin \ + --disable-ocaml \ + --disable-ruby \ + --disable-rust \ + --disable-ruby \ + --disable-tcl \ + --without-ext2 \ + --without-iso \ + --without-libguestfs \ + --without-libvirt \ + --with-tls-priority=@NBDKIT,SYSTEM + +# Verify that it picked the correct version of Python +# to avoid RHBZ#1404631 happening again silently. +grep '^PYTHON_VERSION = 3' Makefile + +make %{?_smp_mflags} + + +%install +%make_install + +# Delete libtool crap. +find $RPM_BUILD_ROOT -name '*.la' -delete + +# If cargo happens to be installed on the machine then the +# rust plugin is built. Delete it if this happens. +rm -f $RPM_BUILD_ROOT%{_mandir}/man3/nbdkit-rust-plugin.3* + + +%check +# Workaround for broken libvirt (RHBZ#1138604). +mkdir -p $HOME/.cache/libvirt + +# tests/test-captive.sh is racy especially on s390x. We need to +# rethink this test upstream. +truncate -s 0 tests/test-captive.sh + +# Temporarily kill tests/test-shutdown.sh because this test is racy on +# slow hardware. +truncate -s 0 tests/test-shutdown.sh + +%ifarch s390x +# Temporarily kill tests/test-cache-max-size.sh since it fails +# sometimes on s390x for unclear reasons. +truncate -s 0 tests/test-cache-max-size.sh +%endif + +# Make sure we can see the debug messages (RHBZ#1230160). +export LIBGUESTFS_DEBUG=1 +export LIBGUESTFS_TRACE=1 + +make %{?_smp_mflags} check || { + cat tests/test-suite.log + exit 1 + } + + +%files +# metapackage so empty + + +%files server +%doc README +%license LICENSE +%{_sbindir}/nbdkit +%dir %{_libdir}/%{name} +%dir %{_libdir}/%{name}/plugins +%dir %{_libdir}/%{name}/filters +%{_mandir}/man1/nbdkit.1* +%{_mandir}/man1/nbdkit-captive.1* +%{_mandir}/man1/nbdkit-loop.1* +%{_mandir}/man1/nbdkit-probing.1* +%{_mandir}/man1/nbdkit-protocol.1* +%{_mandir}/man1/nbdkit-service.1* +%{_mandir}/man1/nbdkit-security.1* +%{_mandir}/man1/nbdkit-tls.1* + + +%files basic-plugins +%doc README +%license LICENSE +%{_libdir}/%{name}/plugins/nbdkit-data-plugin.so +%{_libdir}/%{name}/plugins/nbdkit-file-plugin.so +%{_libdir}/%{name}/plugins/nbdkit-floppy-plugin.so +%{_libdir}/%{name}/plugins/nbdkit-full-plugin.so +%{_libdir}/%{name}/plugins/nbdkit-info-plugin.so +%{_libdir}/%{name}/plugins/nbdkit-memory-plugin.so +%{_libdir}/%{name}/plugins/nbdkit-null-plugin.so +%{_libdir}/%{name}/plugins/nbdkit-partitioning-plugin.so +%{_libdir}/%{name}/plugins/nbdkit-pattern-plugin.so +%{_libdir}/%{name}/plugins/nbdkit-random-plugin.so +%{_libdir}/%{name}/plugins/nbdkit-sh-plugin.so +%{_libdir}/%{name}/plugins/nbdkit-split-plugin.so +%{_libdir}/%{name}/plugins/nbdkit-streaming-plugin.so +%{_libdir}/%{name}/plugins/nbdkit-zero-plugin.so +%{_mandir}/man1/nbdkit-data-plugin.1* +%{_mandir}/man1/nbdkit-file-plugin.1* +%{_mandir}/man1/nbdkit-floppy-plugin.1* +%{_mandir}/man1/nbdkit-full-plugin.1* +%{_mandir}/man1/nbdkit-info-plugin.1* +%{_mandir}/man1/nbdkit-memory-plugin.1* +%{_mandir}/man1/nbdkit-null-plugin.1* +%{_mandir}/man1/nbdkit-partitioning-plugin.1* +%{_mandir}/man1/nbdkit-pattern-plugin.1* +%{_mandir}/man1/nbdkit-random-plugin.1* +%{_mandir}/man3/nbdkit-sh-plugin.3* +%{_mandir}/man1/nbdkit-split-plugin.1* +%{_mandir}/man1/nbdkit-streaming-plugin.1* +%{_mandir}/man1/nbdkit-zero-plugin.1* + + +%files example-plugins +%doc README +%license LICENSE +%{_libdir}/%{name}/plugins/nbdkit-example*-plugin.so +%{_mandir}/man1/nbdkit-example*-plugin.1* + + +%files curl-plugin +%doc README +%license LICENSE +%{_libdir}/%{name}/plugins/nbdkit-curl-plugin.so +%{_mandir}/man1/nbdkit-curl-plugin.1* + + +%files gzip-plugin +%doc README +%license LICENSE +%{_libdir}/%{name}/plugins/nbdkit-gzip-plugin.so +%{_mandir}/man1/nbdkit-gzip-plugin.1* + + +%files linuxdisk-plugin +%doc README +%license LICENSE +%{_libdir}/%{name}/plugins/nbdkit-linuxdisk-plugin.so +%{_mandir}/man1/nbdkit-linuxdisk-plugin.1* + + +%files python-plugin +%doc README +%license LICENSE +%{_libdir}/%{name}/plugins/nbdkit-python-plugin.so +%{_mandir}/man3/nbdkit-python-plugin.3* + + +%files ssh-plugin +%doc README +%license LICENSE +%{_libdir}/%{name}/plugins/nbdkit-ssh-plugin.so +%{_mandir}/man1/nbdkit-ssh-plugin.1* + + +%ifarch %{ix86} x86_64 +%files vddk-plugin +%doc README +%license LICENSE +%{_libdir}/%{name}/plugins/nbdkit-vddk-plugin.so +%{_mandir}/man1/nbdkit-vddk-plugin.1* +%endif + + +%files basic-filters +%doc README +%license LICENSE +%{_libdir}/%{name}/filters/nbdkit-blocksize-filter.so +%{_libdir}/%{name}/filters/nbdkit-cache-filter.so +%{_libdir}/%{name}/filters/nbdkit-cacheextents-filter.so +%{_libdir}/%{name}/filters/nbdkit-cow-filter.so +%{_libdir}/%{name}/filters/nbdkit-delay-filter.so +%{_libdir}/%{name}/filters/nbdkit-error-filter.so +%{_libdir}/%{name}/filters/nbdkit-extentlist-filter.so +%{_libdir}/%{name}/filters/nbdkit-fua-filter.so +%{_libdir}/%{name}/filters/nbdkit-log-filter.so +%{_libdir}/%{name}/filters/nbdkit-nocache-filter.so +%{_libdir}/%{name}/filters/nbdkit-noextents-filter.so +%{_libdir}/%{name}/filters/nbdkit-noparallel-filter.so +%{_libdir}/%{name}/filters/nbdkit-nozero-filter.so +%{_libdir}/%{name}/filters/nbdkit-offset-filter.so +%{_libdir}/%{name}/filters/nbdkit-partition-filter.so +%{_libdir}/%{name}/filters/nbdkit-rate-filter.so +%{_libdir}/%{name}/filters/nbdkit-readahead-filter.so +%{_libdir}/%{name}/filters/nbdkit-retry-filter.so +%{_libdir}/%{name}/filters/nbdkit-stats-filter.so +%{_libdir}/%{name}/filters/nbdkit-truncate-filter.so +%{_mandir}/man1/nbdkit-blocksize-filter.1* +%{_mandir}/man1/nbdkit-cache-filter.1* +%{_mandir}/man1/nbdkit-cacheextents-filter.1* +%{_mandir}/man1/nbdkit-cow-filter.1* +%{_mandir}/man1/nbdkit-delay-filter.1* +%{_mandir}/man1/nbdkit-error-filter.1* +%{_mandir}/man1/nbdkit-extentlist-filter.1* +%{_mandir}/man1/nbdkit-fua-filter.1* +%{_mandir}/man1/nbdkit-log-filter.1* +%{_mandir}/man1/nbdkit-nocache-filter.1* +%{_mandir}/man1/nbdkit-noextents-filter.1* +%{_mandir}/man1/nbdkit-noparallel-filter.1* +%{_mandir}/man1/nbdkit-nozero-filter.1* +%{_mandir}/man1/nbdkit-offset-filter.1* +%{_mandir}/man1/nbdkit-partition-filter.1* +%{_mandir}/man1/nbdkit-rate-filter.1* +%{_mandir}/man1/nbdkit-readahead-filter.1* +%{_mandir}/man1/nbdkit-retry-filter.1* +%{_mandir}/man1/nbdkit-stats-filter.1* +%{_mandir}/man1/nbdkit-truncate-filter.1* + + +%files xz-filter +%doc README +%license LICENSE +%{_libdir}/%{name}/filters/nbdkit-xz-filter.so +%{_mandir}/man1/nbdkit-xz-filter.1* + + +%files devel +%doc BENCHMARKING OTHER_PLUGINS README SECURITY TODO +%license LICENSE +# Include the source of the example plugins in the documentation. +%doc plugins/example*/*.c +%doc plugins/python/example.py +%doc plugins/sh/example.sh +%{_includedir}/nbdkit-common.h +%{_includedir}/nbdkit-filter.h +%{_includedir}/nbdkit-plugin.h +%{_includedir}/nbdkit-version.h +%{_includedir}/nbd-protocol.h +%{_mandir}/man3/nbdkit-filter.3* +%{_mandir}/man3/nbdkit-plugin.3* +%{_mandir}/man1/nbdkit-release-notes-1.*.1* +%{_libdir}/pkgconfig/nbdkit.pc + + +%files bash-completion +%license LICENSE +%dir %{_datadir}/bash-completion/completions +%{_datadir}/bash-completion/completions/nbdkit + + +%changelog +* Mon Apr 27 2020 Danilo C. L. de Paula - 1.16.2 +- Resolves: bz#1810193 + (Upgrade components in virt:rhel module:stream for RHEL-8.3 release) + +* Fri Jun 28 2019 Danilo de Paula - 1.4.2-5 +- Rebuild all virt packages to fix RHEL's upgrade path +- Resolves: rhbz#1695587 + (Ensure modular RPM upgrade path) + +* Mon Dec 17 2018 Richard W.M. Jones - 1.4.2-4 +- Remove misguided LDFLAGS hack which removed server hardening. + https://bugzilla.redhat.com/show_bug.cgi?id=1624149#c6 + resolves: rhbz#1624149 + +* Fri Dec 14 2018 Richard W.M. Jones - 1.4.2-3 +- Use platform-python + resolves: rhbz#1659159 + +* Fri Aug 10 2018 Richard W.M. Jones - 1.4.2-2 +- Add Enhanced Python error reporting + resolves: rhbz#1614750. +- Use copy-patches.sh script. + +* Wed Aug 1 2018 Richard W.M. Jones - 1.4.2-1 +- New stable version 1.4.2. + +* Wed Jul 25 2018 Richard W.M. Jones - 1.4.1-3 +- Enable VDDK plugin on x86-64 only. + +* Fri Jul 20 2018 Richard W.M. Jones - 1.4.1-1 +- New upstream version 1.4.1. +- Small refactorings in the spec file. + +* Fri Jul 6 2018 Richard W.M. Jones - 1.4.0-1 +- New upstream version 1.4.0. +- New plugins: random, zero. +- New bash tab completion subpackage. +- Remove unused build dependencies. + +* Sun Jul 1 2018 Richard W.M. Jones - 1.2.4-3 +- Add all upstream patches since 1.2.4 was released. + +* Tue Jun 12 2018 Richard W.M. Jones - 1.2.4-2 +- Add all upstream patches since 1.2.4 was released. + +* Mon Jun 11 2018 Richard W.M. Jones - 1.2.4-2 +- Disable plugins and filters that we do not want to ship in RHEL 8. + +* Sat Jun 9 2018 Richard W.M. Jones - 1.2.4-1 +- New stable version 1.2.4. +- Remove upstream patches. +- Enable tarball signatures. +- Add upstream patch to fix tests when guestfish not available. + +* Wed Jun 6 2018 Richard W.M. Jones - 1.2.3-1 +- New stable version 1.2.3. +- Add patch to work around libvirt problem with relative socket paths. +- Add patch to fix the xz plugin test with recent guestfish. + +* Sat Apr 21 2018 Richard W.M. Jones - 1.2.2-1 +- New stable version 1.2.2. + +* Mon Apr 9 2018 Richard W.M. Jones - 1.2.1-1 +- New stable version 1.2.1. + +* Fri Apr 6 2018 Richard W.M. Jones - 1.2.0-1 +- Move to stable branch version 1.2.0. + +* Fri Feb 09 2018 Igor Gnatenko - 1.1.28-5 +- Escape macros in %%changelog + +* Thu Feb 08 2018 Fedora Release Engineering - 1.1.28-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Wed Jan 31 2018 Igor Gnatenko - 1.1.28-3 +- Switch to %%ldconfig_scriptlets + +* Fri Jan 26 2018 Richard W.M. Jones - 1.1.28-2 +- Run a simplified test suite on all arches. + +* Mon Jan 22 2018 Richard W.M. Jones - 1.1.28-1 +- New upstream version 1.1.28. +- Add two new filters to nbdkit-basic-filters. + +* Sat Jan 20 2018 Björn Esser - 1.1.27-2 +- Rebuilt for switch to libxcrypt + +* Sat Jan 20 2018 Richard W.M. Jones - 1.1.27-1 +- New upstream version 1.1.27. +- Add new subpackage nbdkit-basic-filters containing new filters. + +* Thu Jan 11 2018 Richard W.M. Jones - 1.1.26-2 +- Rebuild against updated Ruby. + +* Sat Dec 23 2017 Richard W.M. Jones - 1.1.26-1 +- New upstream version 1.1.26. +- Add new pkg-config file and dependency. + +* Wed Dec 06 2017 Richard W.M. Jones - 1.1.25-1 +- New upstream version 1.1.25. + +* Tue Dec 05 2017 Richard W.M. Jones - 1.1.24-1 +- New upstream version 1.1.24. +- Add tar plugin (new subpackage nbdkit-plugin-tar). + +* Tue Dec 05 2017 Richard W.M. Jones - 1.1.23-1 +- New upstream version 1.1.23. +- Add example4 plugin. +- Python3 tests require libguestfs so disable on s390x. + +* Sun Dec 03 2017 Richard W.M. Jones - 1.1.22-1 +- New upstream version 1.1.22. +- Enable tests on Fedora. + +* Sat Dec 02 2017 Richard W.M. Jones - 1.1.20-1 +- New upstream version 1.1.20. +- Add nbdkit-split-plugin to basic plugins. + +* Sat Dec 02 2017 Richard W.M. Jones - 1.1.19-2 +- OCaml 4.06.0 rebuild. + +* Thu Nov 30 2017 Richard W.M. Jones - 1.1.19-1 +- New upstream version 1.1.19. +- Combine all the simple plugins in %%{name}-basic-plugins. +- Add memory and null plugins. +- Rename the example plugins subpackage. +- Use %%license instead of %%doc for license file. +- Remove patches now upstream. + +* Wed Nov 29 2017 Richard W.M. Jones - 1.1.18-4 +- Fix Python 3 builds / RHEL macros (RHBZ#1404631). + +* Tue Nov 21 2017 Richard W.M. Jones - 1.1.18-3 +- New upstream version 1.1.18. +- Add NBD forwarding plugin. +- Add libselinux-devel so that SELinux support is enabled in the daemon. +- Apply all patches from upstream since 1.1.18. + +* Fri Oct 20 2017 Richard W.M. Jones - 1.1.16-2 +- New upstream version 1.1.16. +- Disable python3 plugin on RHEL/EPEL <= 7. +- Only ship on x86_64 in RHEL/EPEL <= 7. + +* Wed Sep 27 2017 Richard W.M. Jones - 1.1.15-1 +- New upstream version 1.1.15. +- Enable TLS support. + +* Fri Sep 01 2017 Richard W.M. Jones - 1.1.14-1 +- New upstream version 1.1.14. + +* Fri Aug 25 2017 Richard W.M. Jones - 1.1.13-1 +- New upstream version 1.1.13. +- Remove patches which are all upstream. +- Remove grubby hack, should not be needed with modern supermin. + +* Sat Aug 19 2017 Richard W.M. Jones - 1.1.12-13 +- Rebuild for OCaml 4.05.0. + +* Thu Aug 03 2017 Fedora Release Engineering - 1.1.12-12 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Wed Jul 26 2017 Fedora Release Engineering - 1.1.12-11 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Tue Jun 27 2017 Richard W.M. Jones - 1.1.12-10 +- Rebuild for OCaml 4.04.2. + +* Sun Jun 04 2017 Jitka Plesnikova - 1.1.12-9 +- Perl 5.26 rebuild + +* Mon May 15 2017 Richard W.M. Jones - 1.1.12-8 +- Rebuild for OCaml 4.04.1. + +* Fri Feb 10 2017 Fedora Release Engineering - 1.1.12-7 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Thu Jan 12 2017 Vít Ondruch - 1.1.12-6 +- Rebuilt for https://fedoraproject.org/wiki/Changes/Ruby_2.4 + +* Fri Dec 23 2016 Richard W.M. Jones - 1.1.12-5 +- Rebuild for Python 3.6 update. + +* Wed Dec 14 2016 Richard W.M. Jones - 1.1.12-4 +- Fix python3 subpackage so it really uses python3 (RHBZ#1404631). + +* Sat Nov 05 2016 Richard W.M. Jones - 1.1.12-3 +- Rebuild for OCaml 4.04.0. + +* Mon Oct 03 2016 Richard W.M. Jones - 1.1.12-2 +- Compile Python 2 and Python 3 versions of the plugin. + +* Wed Jun 08 2016 Richard W.M. Jones - 1.1.12-1 +- New upstream version 1.1.12 +- Enable Ruby plugin. +- Disable tests on Rawhide because libvirt is broken again (RHBZ#1344016). + +* Wed May 25 2016 Richard W.M. Jones - 1.1.11-10 +- Add another upstream patch since 1.1.11. + +* Mon May 23 2016 Richard W.M. Jones - 1.1.11-9 +- Add all patches upstream since 1.1.11 (fixes RHBZ#1336758). + +* Tue May 17 2016 Jitka Plesnikova - 1.1.11-7 +- Perl 5.24 rebuild + +* Wed Mar 09 2016 Richard W.M. Jones - 1.1.11-6 +- When tests fail, dump out test-suite.log so we can debug it. + +* Fri Feb 05 2016 Richard W.M. Jones - 1.1.11-5 +- Don't run tests on x86, because kernel is broken there + (https://bugzilla.redhat.com/show_bug.cgi?id=1302071) + +* Thu Feb 04 2016 Fedora Release Engineering - 1.1.11-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Mon Jan 11 2016 Richard W.M. Jones - 1.1.11-3 +- Add support for newstyle NBD protocol (RHBZ#1297100). + +* Sat Oct 31 2015 Richard W.M. Jones - 1.1.11-1 +- New upstream version 1.1.11. + +* Thu Jul 30 2015 Richard W.M. Jones - 1.1.10-3 +- OCaml 4.02.3 rebuild. + +* Sat Jun 20 2015 Richard W.M. Jones - 1.1.10-2 +- Enable libguestfs plugin on aarch64. + +* Fri Jun 19 2015 Richard W.M. Jones - 1.1.10-1 +- New upstream version. +- Enable now working OCaml plugin (requires OCaml >= 4.02.2). + +* Wed Jun 17 2015 Fedora Release Engineering - 1.1.9-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Thu Jun 11 2015 Jitka Plesnikova - 1.1.9-5 +- Perl 5.22 rebuild + +* Wed Jun 10 2015 Richard W.M. Jones - 1.1.9-4 +- Enable debugging messages when running make check. + +* Sat Jun 06 2015 Jitka Plesnikova - 1.1.9-3 +- Perl 5.22 rebuild + +* Tue Oct 14 2014 Richard W.M. Jones - 1.1.9-2 +- New upstream version 1.1.9. +- Add the streaming plugin. +- Include fix for streaming plugin in 1.1.9. + +* Wed Sep 10 2014 Richard W.M. Jones - 1.1.8-4 +- Rebuild for updated Perl in Rawhide. +- Workaround for broken libvirt (RHBZ#1138604). + +* Sun Aug 17 2014 Fedora Release Engineering - 1.1.8-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild + +* Sat Jun 21 2014 Richard W.M. Jones - 1.1.8-1 +- New upstream version 1.1.8. +- Add support for cURL, and new nbdkit-plugin-curl package. + +* Fri Jun 20 2014 Richard W.M. Jones - 1.1.7-1 +- New upstream version 1.1.7. +- Remove patches which are now all upstream. + +* Sat Jun 07 2014 Fedora Release Engineering - 1.1.6-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Thu Mar 06 2014 Dan Horák - 1.1.6-4 +- libguestfs is available only on selected arches + +* Fri Feb 21 2014 Richard W.M. Jones - 1.1.6-3 +- Backport some upstream patches, fixing a minor bug and adding more tests. +- Enable the tests since kernel bug is fixed. + +* Sun Feb 16 2014 Richard W.M. Jones - 1.1.6-1 +- New upstream version 1.1.6. + +* Sat Feb 15 2014 Richard W.M. Jones - 1.1.5-2 +- New upstream version 1.1.5. +- Enable the new Python plugin. +- Perl plugin man page moved to section 3. +- Perl now requires ExtUtils::Embed. + +* Mon Feb 10 2014 Richard W.M. Jones - 1.1.4-1 +- New upstream version 1.1.4. +- Enable the new Perl plugin. + +* Sun Aug 4 2013 Richard W.M. Jones - 1.1.3-1 +- New upstream version 1.1.3 which fixes some test problems. +- Disable tests because Rawhide kernel is broken (RHBZ#991808). +- Remove a single quote from description which confused emacs. +- Remove patch, now upstream. + +* Sat Aug 03 2013 Fedora Release Engineering - 1.1.2-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild + +* Sun Jul 21 2013 Richard W.M. Jones - 1.1.2-3 +- Fix segfault when IPv6 client is used (RHBZ#986601). + +* Tue Jul 16 2013 Richard W.M. Jones - 1.1.2-2 +- New development version 1.1.2. +- Disable the tests on Fedora <= 18. + +* Tue Jun 25 2013 Richard W.M. Jones - 1.1.1-1 +- New development version 1.1.1. +- Add libguestfs plugin. +- Run the test suite. + +* Mon Jun 24 2013 Richard W.M. Jones - 1.0.0-4 +- Initial release.