diff --git a/.gitignore b/.gitignore
index 9bf8ce5..e138377 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,2 @@
 SOURCES/libguestfs.keyring
-SOURCES/nbdkit-1.16.2.tar.gz
+SOURCES/nbdkit-1.24.0.tar.gz
diff --git a/.nbdkit.metadata b/.nbdkit.metadata
index fdcb45a..2f1c571 100644
--- a/.nbdkit.metadata
+++ b/.nbdkit.metadata
@@ -1,2 +1,2 @@
 1bbc40f501a7fef9eef2a39b701a71aee2fea7c4 SOURCES/libguestfs.keyring
-42a5761cd3403c02c43cdf7d541ff3faaf8b4769 SOURCES/nbdkit-1.16.2.tar.gz
+069720cc0d1502b007652101d293a57d7b4d7c41 SOURCES/nbdkit-1.24.0.tar.gz
diff --git a/SOURCES/0001-cache-cow-Fix-data-corruption-in-zero-and-trim-on-un.patch b/SOURCES/0001-cache-cow-Fix-data-corruption-in-zero-and-trim-on-un.patch
new file mode 100644
index 0000000..ad8500b
--- /dev/null
+++ b/SOURCES/0001-cache-cow-Fix-data-corruption-in-zero-and-trim-on-un.patch
@@ -0,0 +1,82 @@
+From 99788909d9ec36e3210cf85976fe5b18da690ddd Mon Sep 17 00:00:00 2001
+From: "Richard W.M. Jones" <rjones@redhat.com>
+Date: Wed, 4 Aug 2021 20:24:59 +0100
+Subject: [PATCH] cache, cow: Fix data corruption in zero and trim on unaligned
+ tail
+
+Commit eb6009b092 ("cache, cow: Reduce use of bounce-buffer") first
+introduced in nbdkit 1.14 added an optimization of the
+read-modify-write mechanism used for unaligned heads and tails when
+zeroing in the cache layer.
+
+Unfortunately the part applied to the tail contained a mistake: It
+zeroes the end of the buffer rather than the beginning.  This causes
+data corruption when you use the zero or trim function with an offset
+and count which is not aligned to the block size.
+
+Although the bug has been around for years, a recent change made it
+more likely to happen.  Commit c1905b0a28 ("cache, cow: Use a 64K
+block size by default") increased the default block size from 4K to
+64K.  Most filesystems use a 4K block size so operations like fstrim
+will make 4K-aligned requests, and with a 4K block size also in the
+cache or cow filter the unaligned case would never have been hit
+before.
+
+We can demonstrate the bug simply by filling a buffer with data
+(100000 bytes in the example), and then trimming that data, which
+ought to zero it out.
+
+Before this commit there is data visible after the trim:
+
+$ nbdkit --filter=cow data "0x21 * 100000" --run 'nbdsh -u $uri -c "h.trim(100000, 0)" ; nbdcopy $uri - | hexdump -C'
+00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+*
+00018000  21 21 21 21 21 21 21 21  21 21 21 21 21 21 21 21  |!!!!!!!!!!!!!!!!|
+*
+000186a0
+
+After this commit the trim completely clears the data:
+
+$ nbdkit --filter=cow data "0x21 * 100000" --run 'nbdsh -u $uri -c "h.trim(100000, 0)" ; nbdcopy $uri - | hexdump -C'
+00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+*
+000186a0
+
+Thanks: Ming Xie for finding the bug
+Fixes: commit eb6009b092ae642ed25f133d487dd40ef7bf70f8
+(cherry picked from commit a0ae7b2158598ce48ac31706319007f716d01c87)
+(cherry picked from commit c0b15574647672cb5c48178333acdd07424692ef)
+---
+ filters/cache/cache.c | 2 +-
+ filters/cow/cow.c     | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/filters/cache/cache.c b/filters/cache/cache.c
+index 91dcc43d..0616cc7b 100644
+--- a/filters/cache/cache.c
++++ b/filters/cache/cache.c
+@@ -493,7 +493,7 @@ cache_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+     ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
+     r = blk_read (next_ops, nxdata, blknum, block, err);
+     if (r != -1) {
+-      memset (&block[count], 0, blksize - count);
++      memset (block, 0, count);
+       r = blk_write (next_ops, nxdata, blknum, block, flags, err);
+     }
+     if (r == -1)
+diff --git a/filters/cow/cow.c b/filters/cow/cow.c
+index 51ca64a4..1cfcc4e7 100644
+--- a/filters/cow/cow.c
++++ b/filters/cow/cow.c
+@@ -419,7 +419,7 @@ cow_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+     ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
+     r = blk_read (next_ops, nxdata, blknum, block, err);
+     if (r != -1) {
+-      memset (&block[count], 0, BLKSIZE - count);
++      memset (block, 0, count);
+       r = blk_write (blknum, block, err);
+     }
+     if (r == -1)
+-- 
+2.18.4
+
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
deleted file mode 100644
index 758f2f0..0000000
--- a/SOURCES/0001-server-Allow-D-nbdkit.-debug-flags-for-the-core-serv.patch
+++ /dev/null
@@ -1,75 +0,0 @@
-From d7836fb0a7131c725e3c02be7e48e99c671637c3 Mon Sep 17 00:00:00 2001
-From: "Richard W.M. Jones" <rjones@redhat.com>
-Date: Thu, 12 Dec 2019 08:57:15 +0000
-Subject: [PATCH 01/19] 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 a2e72b13..346d8332 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<FLAG> to the integer
- value C<N>.  See L<nbdkit-plugin(3)/Debug Flags>.
- 
-+=item B<-D> nbdkit.FLAG=N
-+
-+=item B<--debug> nbdkit.FLAG=N
-+
-+Set the nbdkit server Debug Flag called C<FLAG> to the integer value
-+C<N>.
-+
- =item B<--dump-config>
- 
- Dump out the compile-time configuration values and exit.
-diff --git a/docs/synopsis.txt b/docs/synopsis.txt
-index 3c239373..c3675422 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 d39941b1..11ba1e6d 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 390972e2..96c22c07 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
deleted file mode 100644
index c7f1786..0000000
--- a/SOURCES/0002-server-Allow-D-debug-flags-to-contain-dots-for-names.patch
+++ /dev/null
@@ -1,67 +0,0 @@
-From e5d2d44fff9214725506cbc84e7b3c035ec0eae9 Mon Sep 17 00:00:00 2001
-From: "Richard W.M. Jones" <rjones@redhat.com>
-Date: Thu, 12 Dec 2019 11:06:36 +0000
-Subject: [PATCH 02/19] 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 b69cb825..879ddf09 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<myplugin_debug_foo_bar>.
-+
- =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 9344d85c..5e06f5ed 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/0002-server-CVE-2021-3716-reset-structured-replies-on-sta.patch b/SOURCES/0002-server-CVE-2021-3716-reset-structured-replies-on-sta.patch
new file mode 100644
index 0000000..df33105
--- /dev/null
+++ b/SOURCES/0002-server-CVE-2021-3716-reset-structured-replies-on-sta.patch
@@ -0,0 +1,94 @@
+From 6b9d4380df9bd0be91f49aad8c4f47b4e672adde Mon Sep 17 00:00:00 2001
+From: Eric Blake <eblake@redhat.com>
+Date: Mon, 16 Aug 2021 13:43:29 -0500
+Subject: [PATCH] server: CVE-2021-3716 reset structured replies on starttls
+
+https://nostarttls.secvuln.info/ pointed out a series of CVEs in
+common implementation flaw in various SMTP and IMAP clients and
+servers, all with a common thread of improperly caching plaintext
+state across the STARTTLS encryption boundary; and recommended that
+other protocols with a STARTTLS operation perform a similar audit.
+
+It turns out that nbdkit has the same vulnerability in regards to the
+NBD protocol: when nbdkit is run in opportunistic TLS mode, an
+attacker is able to inject a plaintext NBD_OPT_STRUCTURED_REPLY before
+proxying everything else a client sends to the server; if the server
+then acts on that plaintext request (as nbdkit did before this patch),
+then the server ends up sending structured replies to at least
+NBD_CMD_READ, even though the client was assuming that the transition
+to TLS has ruled out a MitM attack.
+
+On the bright side, nbdkit's behavior on a second
+NBD_OPT_STRUCTURED_REPLY was to still reply with success, so a client
+that always requests structured replies after starting TLS sees no
+difference in behavior (that is, qemu 2.12 and later are immune) (had
+nbdkit given an error to the second request, that may have caused
+confusion to more clients).  And there is always the mitigation of
+using --tls=require, which lets nbdkit reject the MitM message
+pre-encryption.  However, nbd-client 3.15 to the present do not
+understand structured replies, and I have confirmed that a MitM
+attacker can thus cause a denial-of-service attack that does not
+trigger until the client does its first encrypted NBD_CMD_READ.
+
+The NBD spec has been recently tightened to declare the nbdkit
+behavior to be a security hole:
+https://github.com/NetworkBlockDevice/nbd/commit/77e55378096aa
+Fixes: eaa4c6e9a2c4bd (server: Minimal implementation of NBD Structured Replies.)
+
+(cherry picked from commit 09a13dafb7bb3a38ab52eb5501cba786365ba7fd)
+(cherry picked from commit 6185b15a81e6915734d678f0781e31d45a7941a1)
+---
+ docs/nbdkit-security.pod             | 11 +++++++++--
+ server/protocol-handshake-newstyle.c |  3 ++-
+ 2 files changed, 11 insertions(+), 3 deletions(-)
+
+diff --git a/docs/nbdkit-security.pod b/docs/nbdkit-security.pod
+index 3a28e54d..5a4e6da8 100644
+--- a/docs/nbdkit-security.pod
++++ b/docs/nbdkit-security.pod
+@@ -10,7 +10,7 @@ For how to report new security issues, see the C<SECURITY> file in the
+ top level source directory, also available online here:
+ L<https://github.com/libguestfs/nbdkit/blob/master/SECURITY>
+ 
+-=head2 CVE-2019-14850 
++=head2 CVE-2019-14850
+ denial of service due to premature opening of back-end connection
+ 
+ See the full announcement and links to mitigation, tests and fixes
+@@ -26,6 +26,13 @@ See the full announcement and links to mitigation, tests and fixes
+ here:
+ https://www.redhat.com/archives/libguestfs/2019-September/msg00272.html
+ 
++=head2 CVE-2021-3716
++structured read denial of service attack against starttls
++
++See the full announcement and links to mitigation, tests and fixes
++here:
++https://www.redhat.com/archives/libguestfs/2021-August/msg00083.html
++
+ =head1 SEE ALSO
+ 
+ L<nbdkit(1)>.
+@@ -38,4 +45,4 @@ Richard W.M. Jones
+ 
+ =head1 COPYRIGHT
+ 
+-Copyright (C) 2013-2020 Red Hat Inc.
++Copyright (C) 2013-2021 Red Hat Inc.
+diff --git a/server/protocol-handshake-newstyle.c b/server/protocol-handshake-newstyle.c
+index 0a76a814..b94950e2 100644
+--- a/server/protocol-handshake-newstyle.c
++++ b/server/protocol-handshake-newstyle.c
+@@ -495,7 +495,8 @@ negotiate_handshake_newstyle_options (void)
+           return -1;
+         conn->using_tls = true;
+         debug ("using TLS on this connection");
+-        /* Wipe out any cached default export name. */
++        /* Wipe out any cached state. */
++        conn->structured_replies = false;
+         for_each_backend (b) {
+           struct handle *h = get_handle (conn, b->i);
+           free (h->default_exportname);
+-- 
+2.18.4
+
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
deleted file mode 100644
index 7aac76a..0000000
--- a/SOURCES/0003-server-Add-D-nbdkit.backend.controlpath-and-D-nbdkit.patch
+++ /dev/null
@@ -1,451 +0,0 @@
-From 83c72d9bf9d6a9ccf6939b4ebd0028b62673a78a Mon Sep 17 00:00:00 2001
-From: "Richard W.M. Jones" <rjones@redhat.com>
-Date: Thu, 12 Dec 2019 10:57:52 +0000
-Subject: [PATCH 03/19] 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 346d8332..38e6bfca 100644
---- a/docs/nbdkit.pod
-+++ b/docs/nbdkit.pod
-@@ -182,7 +182,7 @@ value C<N>.  See L<nbdkit-plugin(3)/Debug Flags>.
- =item B<--debug> nbdkit.FLAG=N
- 
- Set the nbdkit server Debug Flag called C<FLAG> to the integer value
--C<N>.
-+C<N>.  See L</SERVER DEBUG FLAGS> below.
- 
- =item B<--dump-config>
- 
-@@ -527,6 +527,39 @@ languages.  The file should be executable.  For example:
- 
- (see L<nbdkit-perl-plugin(3)> 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 b9fe2a21..208c07b1 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 01341973..d225cc63 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 00000000..69a69a7c
---- /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/0003-server-reset-meta-context-replies-on-starttls.patch b/SOURCES/0003-server-reset-meta-context-replies-on-starttls.patch
new file mode 100644
index 0000000..18213dc
--- /dev/null
+++ b/SOURCES/0003-server-reset-meta-context-replies-on-starttls.patch
@@ -0,0 +1,40 @@
+From add9b794b9dc697a1b52115c997fcfb6e06bf64c Mon Sep 17 00:00:00 2001
+From: Eric Blake <eblake@redhat.com>
+Date: Mon, 16 Aug 2021 13:43:29 -0500
+Subject: [PATCH] server: reset meta context replies on starttls
+
+Related to CVE-2021-3716, but not as severe.  No compliant client will
+send NBD_CMD_BLOCK_STATUS unless it first negotiates
+NBD_OPT_SET_META_CONTEXT.  If an attacker injects a premature
+SET_META_CONTEXT, either the client will never notice (because it
+never uses BLOCK_STATUS), or the client will overwrite the attacker's
+attempt with the client's own SET_META_CONTEXT request after
+encryption is enabled.  So I don't class this as having the potential
+to trigger denial-of-service due to any protocol mismatch between
+compliant client and server (I don't care what happens with
+non-compliant clients).
+
+Fixes: 26455d45 (server: protocol: Implement Block Status "base:allocation".)
+(cherry picked from commit 6c5faac6a37077cf2366388a80862bb00616d0d8)
+(cherry picked from commit 814d8103fb4b581dc01dfd25d2cd81596576f211)
+---
+ server/protocol-handshake-newstyle.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/server/protocol-handshake-newstyle.c b/server/protocol-handshake-newstyle.c
+index b94950e2..eb0f3961 100644
+--- a/server/protocol-handshake-newstyle.c
++++ b/server/protocol-handshake-newstyle.c
+@@ -497,6 +497,9 @@ negotiate_handshake_newstyle_options (void)
+         debug ("using TLS on this connection");
+         /* Wipe out any cached state. */
+         conn->structured_replies = false;
++        free (conn->exportname_from_set_meta_context);
++        conn->exportname_from_set_meta_context = NULL;
++        conn->meta_context_base_allocation = false;
+         for_each_backend (b) {
+           struct handle *h = get_handle (conn, b->i);
+           free (h->default_exportname);
+-- 
+2.18.4
+
diff --git a/SOURCES/0004-python-Add-various-constants-to-the-API.patch b/SOURCES/0004-python-Add-various-constants-to-the-API.patch
deleted file mode 100644
index 8268475..0000000
--- a/SOURCES/0004-python-Add-various-constants-to-the-API.patch
+++ /dev/null
@@ -1,65 +0,0 @@
-From b646050b8da51c39cf21f95fa847c12784a1169c Mon Sep 17 00:00:00 2001
-From: "Richard W.M. Jones" <rjones@redhat.com>
-Date: Thu, 21 Nov 2019 15:02:44 +0000
-Subject: [PATCH 04/19] 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 7052aac0..47da0838 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
deleted file mode 100644
index 1fa6ee2..0000000
--- a/SOURCES/0005-python-Implement-nbdkit-API-version-2.patch
+++ /dev/null
@@ -1,558 +0,0 @@
-From 49ef7e7d7c3602cc8e53d2052fce9d3a12840ea2 Mon Sep 17 00:00:00 2001
-From: "Richard W.M. Jones" <rjones@redhat.com>
-Date: Thu, 21 Nov 2019 15:44:39 +0000
-Subject: [PATCH 05/19] 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 60f9d7f0..c04b7e29 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 3680fd65..4923d9da 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<open>),
-@@ -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<pread> function should construct a buffer of length
--(at least) C<count> bytes.  You should read C<count> bytes from the
--disk starting at C<offset>.
--
--The returned buffer can be any type compatible with the Python 3
--buffer protocol, such as bytearray, bytes or memoryview
--(L<https://docs.python.org/3/c-api/buffer.html>)
-+The body of your C<pread> function should read exactly C<len(buf)>
-+bytes of data starting at disk C<offset> and write it into the buffer
-+C<buf>.  C<flags> 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<nbdkit.set_error> first.
- 
- (Optional)
- 
-- def pwrite(h, buf, offset):
-+ def pwrite(h, buf, offset, flags):
-    length = len (buf)
-    # no return value
- 
- The body of your C<pwrite> function should write the buffer C<buf> to
- the disk.  You should write C<count> bytes to the disk starting at
--C<offset>.
-+C<offset>.  C<flags> may contain C<nbdkit.FLAG_FUA>.
- 
- 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<flush> function should do a L<sync(2)> or
- L<fdatasync(2)> or equivalent on the backing store.
-+C<flags> is always 0.
- 
- If the flush fails, your function should throw an exception, optionally
- using C<nbdkit.set_error> first.
-@@ -249,32 +259,35 @@ using C<nbdkit.set_error> first.
- 
- (Optional)
- 
-- def trim(h, count, offset):
-+ def trim(h, count, offset, flags):
-    # no return value
- 
--The body of your C<trim> function should "punch a hole" in the
--backing store.  If the trim fails, your function should throw an
--exception, optionally using C<nbdkit.set_error> first.
-+The body of your C<trim> function should "punch a hole" in the backing
-+store.  C<flags> may contain C<nbdkit.FLAG_FUA>.  If the trim fails,
-+your function should throw an exception, optionally using
-+C<nbdkit.set_error> first.
- 
- =item C<zero>
- 
- (Optional)
- 
-- def zero(h, count, offset, may_trim):
-+ def zero(h, count, offset, flags):
-    # no return value
- 
--The body of your C<zero> function should ensure that C<count> bytes
--of the disk, starting at C<offset>, will read back as zero.  If
--C<may_trim> is true, the operation may be optimized as a trim as long
--as subsequent reads see zeroes.
-+The body of your C<zero> function should ensure that C<count> bytes of
-+the disk, starting at C<offset>, will read back as zero.  C<flags> is
-+a bitmask which may include C<nbdkit.FLAG_MAY_TRIM>,
-+C<nbdkit.FLAG_FUA>, C<nbdkit.FLAG_FAST_ZERO>.
- 
- 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<nbdkit.set_error> first.  In particular, if
--you would like to automatically fall back to C<pwrite> (perhaps
--because there is nothing to optimize if C<may_trim> is false),
--use C<nbdkit.set_error(errno.EOPNOTSUPP)>.
-+write the whole region (perhaps requiring a loop).
-+
-+If the write fails or is partial, your function should throw an
-+exception, optionally using C<nbdkit.set_error> first.  In particular,
-+if you would like to automatically fall back to C<pwrite> (perhaps
-+because there is nothing to optimize if
-+S<C<flags & nbdkit.FLAG_MAY_TRIM>> is false), use
-+S<C<nbdkit.set_error (errno.EOPNOTSUPP)>>.
- 
- =back
- 
-diff --git a/plugins/python/python.c b/plugins/python/python.c
-index 47da0838..0f28595f 100644
---- a/plugins/python/python.c
-+++ b/plugins/python/python.c
-@@ -46,6 +46,8 @@
- #define PY_SSIZE_T_CLEAN 1
- #include <Python.h>
- 
-+#define NBDKIT_API_VERSION 2
-+
- #include <nbdkit-plugin.h>
- 
- #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 d0c79bb0..ee4a3f3a 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 6f336230..0634589a 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 9a2e947d..4db56623 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
deleted file mode 100644
index 032327d..0000000
--- a/SOURCES/0006-python-Implement-cache.patch
+++ /dev/null
@@ -1,98 +0,0 @@
-From c5b1fac4c67078f0164bd23eab6d4d2b8c9830b0 Mon Sep 17 00:00:00 2001
-From: "Richard W.M. Jones" <rjones@redhat.com>
-Date: Thu, 21 Nov 2019 16:42:02 +0000
-Subject: [PATCH 06/19] 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 4923d9da..0ea8deef 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<C<flags & nbdkit.FLAG_MAY_TRIM>> is false), use
- S<C<nbdkit.set_error (errno.EOPNOTSUPP)>>.
- 
-+=item C<cache>
-+
-+(Optional)
-+
-+ def cache(h, count, offset, flags):
-+   # no return value
-+
-+The body of your C<cache> function should prefetch data in the
-+indicated range.
-+
-+If the cache operation fails, your function should throw an exception,
-+optionally using C<nbdkit.set_error> first.
-+
- =back
- 
- =head2 Missing callbacks
-@@ -317,7 +330,6 @@ C<can_zero>,
- C<can_fast_zero>,
- C<can_extents>,
- C<can_multi_conn>,
--C<cache>,
- C<extents>.
- 
- These are not yet supported.
-diff --git a/plugins/python/python.c b/plugins/python/python.c
-index 0f28595f..c5cf38e5 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
deleted file mode 100644
index e6a82a4..0000000
--- a/SOURCES/0007-python-Implement-can_zero-can_fast_zero.patch
+++ /dev/null
@@ -1,80 +0,0 @@
-From 17721b316dd66b0a1ed792eeccd2489fb97828df Mon Sep 17 00:00:00 2001
-From: "Richard W.M. Jones" <rjones@redhat.com>
-Date: Thu, 21 Nov 2019 16:42:59 +0000
-Subject: [PATCH 07/19] 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 0ea8deef..1f1c30f6 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<can_zero>
-+
-+(Optional)
-+
-+ def can_zero(h):
-+   # return a boolean
-+
-+=item C<can_fast_zero>
-+
-+(Optional)
-+
-+ def can_fast_zero(h):
-+   # return a boolean
-+
- =item C<pread>
- 
- (Required)
-@@ -326,8 +340,6 @@ C<config_help>,
- C<magic_config_key>,
- C<can_fua>,
- C<can_cache>,
--C<can_zero>,
--C<can_fast_zero>,
- C<can_extents>,
- C<can_multi_conn>,
- C<extents>.
-diff --git a/plugins/python/python.c b/plugins/python/python.c
-index c5cf38e5..38fc1193 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=<FILENAME>     (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
deleted file mode 100644
index d854076..0000000
--- a/SOURCES/0008-python-Implement-can_multi_conn.patch
+++ /dev/null
@@ -1,65 +0,0 @@
-From 2a85ce81ad95eb2f9b2f29666480b814ea0f80d9 Mon Sep 17 00:00:00 2001
-From: "Richard W.M. Jones" <rjones@redhat.com>
-Date: Thu, 21 Nov 2019 16:46:11 +0000
-Subject: [PATCH 08/19] 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 1f1c30f6..b92bb56a 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<can_multi_conn>
-+
-+(Optional)
-+
-+ def can_multi_conn(h):
-+   # return a boolean
-+
- =item C<can_write>
- 
- (Optional)
-@@ -341,7 +348,6 @@ C<magic_config_key>,
- C<can_fua>,
- C<can_cache>,
- C<can_extents>,
--C<can_multi_conn>,
- C<extents>.
- 
- These are not yet supported.
-diff --git a/plugins/python/python.c b/plugins/python/python.c
-index 38fc1193..b186b991 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
deleted file mode 100644
index f979c4e..0000000
--- a/SOURCES/0009-python-Implement-can_fua-and-can_cache.patch
+++ /dev/null
@@ -1,126 +0,0 @@
-From 38124a137974e1433d68732640ca7f88664557da Mon Sep 17 00:00:00 2001
-From: "Richard W.M. Jones" <rjones@redhat.com>
-Date: Fri, 22 Nov 2019 19:25:53 +0000
-Subject: [PATCH 09/19] 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 b92bb56a..4065ec75 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<can_fua>
-+
-+(Optional)
-+
-+ def can_fua(h):
-+   # return nbdkit.FUA_NONE or nbdkit.FUA_EMULATE
-+   # or nbdkit.FUA_NATIVE
-+
-+=item C<can_cache>
-+
-+(Optional)
-+
-+ def can_cache(h):
-+   # return nbdkit.CACHE_NONE or nbdkit.CACHE_EMULATE
-+   # or nbdkit.CACHE_NATIVE
-+
- =item C<pread>
- 
- (Required)
-@@ -345,8 +361,6 @@ C<longname>,
- C<description>,
- C<config_help>,
- C<magic_config_key>,
--C<can_fua>,
--C<can_cache>,
- C<can_extents>,
- C<extents>.
- 
-diff --git a/plugins/python/python.c b/plugins/python/python.c
-index b186b991..5e2e5269 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=<FILENAME>     (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
deleted file mode 100644
index 27270af..0000000
--- a/SOURCES/0010-tests-Test-the-Python-plugin-thoroughly.patch
+++ /dev/null
@@ -1,597 +0,0 @@
-From 7cb79aef2a12f29f1286caf3858001e47214f871 Mon Sep 17 00:00:00 2001
-From: "Richard W.M. Jones" <rjones@redhat.com>
-Date: Thu, 21 Nov 2019 20:54:41 +0000
-Subject: [PATCH 10/19] 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 b25ac7fe..e25bd99b 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 40f4cd37..05f1e060 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 d225cc63..09103fbb 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 ffb19180..93f99381 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 00000000..8e90bc23
---- /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 00000000..50324d0f
---- /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 4db56623..00000000
---- 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 00000000..6b9f2979
---- /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
deleted file mode 100644
index d33eb00..0000000
--- a/SOURCES/0011-New-filter-extentlist.patch
+++ /dev/null
@@ -1,790 +0,0 @@
-From e744dcb38cc52cbe64977efcdd4bc60e802d1b17 Mon Sep 17 00:00:00 2001
-From: "Richard W.M. Jones" <rjones@redhat.com>
-Date: Thu, 23 Jan 2020 19:52:00 +0000
-Subject: [PATCH 11/19] 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 d2aca440..2a3e89dc 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 fde498b8..41e68de3 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 fdd2285a..bb2514a4 100644
---- a/filters/cacheextents/nbdkit-cacheextents-filter.pod
-+++ b/filters/cacheextents/nbdkit-cacheextents-filter.pod
-@@ -52,6 +52,7 @@ C<nbdkit-cacheextents-filter> first appeared in nbdkit 1.14.
- 
- L<nbdkit(1)>,
- L<nbdkit-cache-filter(1)>,
-+L<nbdkit-extentlist-filter(1)>,
- L<nbdkit-readahead-filter(1)>,
- L<nbdkit-vddk-plugin(1)>,
- L<nbdkit-filter(3)>,
-diff --git a/filters/extentlist/Makefile.am b/filters/extentlist/Makefile.am
-new file mode 100644
-index 00000000..88a9afe1
---- /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 00000000..5f4990b3
---- /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 <config.h>
-+
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <stdint.h>
-+#include <inttypes.h>
-+#include <string.h>
-+#include <errno.h>
-+#include <assert.h>
-+
-+#include <nbdkit-filter.h>
-+
-+#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 00000000..adfb4ad8
---- /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<nbdkit-extentlist-filter> 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<nbdkit-ssh-plugin(1)> 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<offset> and C<length> fields may use any format understood by
-+C<nbdkit_parse_size>.  The optional C<type> field may be an integer,
-+missing (same as 0), or a comma-separated list of the words C<hole>
-+and C<zero>.  (The fields correspond to the inputs of the
-+C<nbdkit_add_extent> function, see L<nbdkit-plugin(3)>).
-+
-+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<hole,zero> 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<not> contain overlapping extents.
-+
-+=head1 PARAMETERS
-+
-+=over 4
-+
-+=item B<extentlist=>FILENAME
-+
-+Specify the file containing the extent list, in the format described
-+in L</FILE FORMAT> above.
-+
-+=back
-+
-+=head1 FILES
-+
-+=over 4
-+
-+=item F<$filterdir/nbdkit-extentlist-filter.so>
-+
-+The filter.
-+
-+Use C<nbdkit --dump-config> to find the location of C<$filterdir>.
-+
-+=back
-+
-+=head1 VERSION
-+
-+C<nbdkit-extentlist-filter> first appeared in nbdkit 1.18.
-+
-+=head1 SEE ALSO
-+
-+L<nbdkit(1)>,
-+L<nbdkit-cacheextents-filter(1)>,
-+L<nbdkit-noextents-filter(1)>,
-+L<nbdkit-filter(3)>,
-+L<nbdkit-plugin(3)>.
-+
-+=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 991ecfe8..0260a5cf 100644
---- a/filters/noextents/nbdkit-noextents-filter.pod
-+++ b/filters/noextents/nbdkit-noextents-filter.pod
-@@ -47,6 +47,7 @@ C<nbdkit-noextents-filter> first appeared in nbdkit 1.14.
- 
- L<nbdkit(1)>,
- L<nbdkit-filter(3)>,
-+L<nbdkit-extentlist-filter(1)>,
- L<nbdkit-fua-filter(1)>,
- L<nbdkit-nocache-filter(1)>,
- L<nbdkit-noparallel-filter(1)>,
-diff --git a/tests/Makefile.am b/tests/Makefile.am
-index 09103fbb..b99952f4 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 00000000..7d05de4f
---- /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
deleted file mode 100644
index 62bdab0..0000000
--- a/SOURCES/0012-extentlist-Documentation-and-test-fixes.patch
+++ /dev/null
@@ -1,125 +0,0 @@
-From 2a3e909e9e1ccb608bde75b76524acd753b33889 Mon Sep 17 00:00:00 2001
-From: "Richard W.M. Jones" <rjones@redhat.com>
-Date: Sat, 25 Jan 2020 11:38:14 +0000
-Subject: [PATCH 12/19] 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 2a3e89dc..e1ac71cd 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 adfb4ad8..5d1a38ae 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<nbdkit-ssh-plugin(1)> 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<nbdkit-ssh-plugin(1)>
-+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<xfs_bmap(8)>.  A similar situation applies to
-+L<nbdkit-curl-plugin(1)>.
-+
-+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 827e0bd1..d3c85248 100644
---- a/plugins/curl/nbdkit-curl-plugin.pod
-+++ b/plugins/curl/nbdkit-curl-plugin.pod
-@@ -182,6 +182,7 @@ L<libcurl(3)>,
- L<CURLOPT_COOKIE(3)>
- L<CURLOPT_VERBOSE(3)>,
- L<nbdkit(1)>,
-+L<nbdkit-extentlist-filter(1)>,
- L<nbdkit-readahead-filter(1)>,
- L<nbdkit-retry-filter(1)>,
- L<nbdkit-ssh-plugin(1)>,
-diff --git a/plugins/ssh/nbdkit-ssh-plugin.pod b/plugins/ssh/nbdkit-ssh-plugin.pod
-index 0a0421d5..3fc3146a 100644
---- a/plugins/ssh/nbdkit-ssh-plugin.pod
-+++ b/plugins/ssh/nbdkit-ssh-plugin.pod
-@@ -316,6 +316,7 @@ C<nbdkit-ssh-plugin> first appeared in nbdkit 1.12.
- 
- L<nbdkit(1)>,
- L<nbdkit-curl-plugin(1)>,
-+L<nbdkit-extentlist-filter(1)>,
- L<nbdkit-readahead-filter(1)>,
- L<nbdkit-retry-filter(1)>,
- L<nbdkit-plugin(3)>,
-diff --git a/tests/test-extentlist.sh b/tests/test-extentlist.sh
-index 7d05de4f..73ce3ca6 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/0013-vddk-Update-for-VDDK-7.0-RHBZ-1831969.patch b/SOURCES/0013-vddk-Update-for-VDDK-7.0-RHBZ-1831969.patch
deleted file mode 100644
index dcfd092..0000000
--- a/SOURCES/0013-vddk-Update-for-VDDK-7.0-RHBZ-1831969.patch
+++ /dev/null
@@ -1,51 +0,0 @@
-From bf1eabb211004f3dc74dd243e2adf52a13290377 Mon Sep 17 00:00:00 2001
-From: "Richard W.M. Jones" <rjones@redhat.com>
-Date: Wed, 6 May 2020 09:33:32 +0100
-Subject: [PATCH 13/19] vddk: Update for VDDK 7.0 (RHBZ#1831969).
-
-This version of VDDK changes the soname to libvixDiskLib.so.7.
-
-Thanks: Ming Xie
-(cherry picked from commit 7f53999179af98aa47569c6771517f7dfa08c5d0)
----
- plugins/vddk/vddk-structs.h | 4 +++-
- plugins/vddk/vddk.c         | 1 +
- 2 files changed, 4 insertions(+), 1 deletion(-)
-
-diff --git a/plugins/vddk/vddk-structs.h b/plugins/vddk/vddk-structs.h
-index 86087871..fff7201b 100644
---- a/plugins/vddk/vddk-structs.h
-+++ b/plugins/vddk/vddk-structs.h
-@@ -32,7 +32,7 @@
- 
- /* Types and structs that we pass to or return from the VDDK API.
-  *
-- * Updated to VDDK 6.7
-+ * Updated to VDDK 7.0
-  */
- 
- #ifndef NBDKIT_VDDK_STRUCTS_H
-@@ -127,6 +127,8 @@ typedef struct VixDiskLibInfo {
-   int numLinks;
-   char *parentFileNameHint;
-   char *uuid;
-+  uint32_t logicalSectorSize;   /* Added in 7.0. */
-+  uint32_t physicalSectorSize;  /* Added in 7.0. */
- } VixDiskLibInfo;
- 
- typedef struct {
-diff --git a/plugins/vddk/vddk.c b/plugins/vddk/vddk.c
-index 5d3764d6..97ef5297 100644
---- a/plugins/vddk/vddk.c
-+++ b/plugins/vddk/vddk.c
-@@ -149,6 +149,7 @@ vddk_load (void)
- {
-   static const char *sonames[] = {
-     /* Prefer the newest library in case multiple exist. */
-+    "libvixDiskLib.so.7",
-     "libvixDiskLib.so.6",
-     "libvixDiskLib.so.5",
-   };
--- 
-2.18.2
-
diff --git a/SOURCES/0014-common-include-Add-ASCII-only-ctype-header-and-ascii.patch b/SOURCES/0014-common-include-Add-ASCII-only-ctype-header-and-ascii.patch
deleted file mode 100644
index 6383c63..0000000
--- a/SOURCES/0014-common-include-Add-ASCII-only-ctype-header-and-ascii.patch
+++ /dev/null
@@ -1,354 +0,0 @@
-From cb3d83d0606d5267752895151bb3c229c48d6fb6 Mon Sep 17 00:00:00 2001
-From: "Richard W.M. Jones" <rjones@redhat.com>
-Date: Tue, 19 May 2020 12:03:23 +0100
-Subject: [PATCH 14/19] common/include: Add ASCII-only ctype header and
- ascii_is* functions.
-
-Our existing uses of <ctype.h> were not necessarily safe if the locale
-was changed.
-
-Also I removed the unnecessary use of isascii, deprecated by POSIX.1-2008.
-
-(cherry picked from commit 9f34db74786fdc92b290a7d47e4b003bd84fec69)
----
- .gitignore                           |  1 +
- common/include/Makefile.am           |  6 +++
- common/include/ascii-ctype.h         | 60 ++++++++++++++++++++++++++
- common/include/test-ascii-ctype.c    | 63 ++++++++++++++++++++++++++++
- plugins/partitioning/partition-gpt.c | 12 +++---
- plugins/sh/Makefile.am               |  1 +
- plugins/sh/call.c                    |  6 +--
- server/backend.c                     |  7 ++--
- server/public.c                      |  4 +-
- 9 files changed, 146 insertions(+), 14 deletions(-)
- create mode 100644 common/include/ascii-ctype.h
- create mode 100644 common/include/test-ascii-ctype.c
-
-diff --git a/.gitignore b/.gitignore
-index e25bd99b..523894b7 100644
---- a/.gitignore
-+++ b/.gitignore
-@@ -26,6 +26,7 @@ Makefile.in
- /aclocal.m4
- /autom4te.cache
- /common/bitmap/test-bitmap
-+/common/include/test-ascii-ctype
- /common/include/test-byte-swapping
- /common/include/test-current-dir-name
- /common/include/test-isaligned
-diff --git a/common/include/Makefile.am b/common/include/Makefile.am
-index 4482de37..d7b0d7a8 100644
---- a/common/include/Makefile.am
-+++ b/common/include/Makefile.am
-@@ -34,6 +34,7 @@ include $(top_srcdir)/common-rules.mk
- # These headers contain only common code shared by the core server,
- # plugins and/or filters.  They are not installed.
- EXTRA_DIST = \
-+	ascii-ctype.h \
- 	byte-swapping.h \
- 	exit-with-parent.h \
- 	get-current-dir-name.h \
-@@ -50,6 +51,7 @@ EXTRA_DIST = \
- # Unit tests.
- 
- TESTS = \
-+	test-ascii-ctype \
- 	test-byte-swapping \
- 	test-current-dir-name \
- 	test-isaligned \
-@@ -62,6 +64,10 @@ TESTS = \
- 	$(NULL)
- check_PROGRAMS = $(TESTS)
- 
-+test_ascii_ctype_SOURCES = test-ascii-ctype.c ascii-ctype.h
-+test_ascii_ctype_CPPFLAGS = -I$(srcdir)
-+test_ascii_ctype_CFLAGS = $(WARNINGS_CFLAGS)
-+
- test_byte_swapping_SOURCES = test-byte-swapping.c byte-swapping.h
- test_byte_swapping_CPPFLAGS = -I$(srcdir)
- test_byte_swapping_CFLAGS = $(WARNINGS_CFLAGS)
-diff --git a/common/include/ascii-ctype.h b/common/include/ascii-ctype.h
-new file mode 100644
-index 00000000..5e8bf237
---- /dev/null
-+++ b/common/include/ascii-ctype.h
-@@ -0,0 +1,60 @@
-+/* nbdkit
-+ * Copyright (C) 2013-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.
-+ */
-+
-+/* Normal ctype functions are affected by the current locale.  For
-+ * example isupper() might recognize Ä in some but not all locales.
-+ * These functions match only 7 bit ASCII characters.
-+ */
-+
-+#ifndef NBDKIT_ASCII_CTYPE_H
-+#define NBDKIT_ASCII_CTYPE_H
-+
-+#define ascii_isalnum(c) (ascii_isalpha (c) || ascii_isdigit (c))
-+
-+#define ascii_isalpha(c)                                        \
-+  (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z'))
-+
-+#define ascii_isdigit(c)                        \
-+  ((c) >= '0' && (c) <= '9')
-+
-+#define ascii_isspace(c)                                                \
-+  ((c) == '\t' || (c) == '\n' || (c) == '\f' || (c) == '\r' || (c) == ' ')
-+
-+#define ascii_isxdigit(c)                                               \
-+  ((c) == '0' || (c) == '1' || (c) == '2' || (c) == '3' || (c) == '4' || \
-+   (c) == '5' || (c) == '6' || (c) == '7' || (c) == '8' || (c) == '9' || \
-+   (c) == 'a' || (c) == 'b' || (c) == 'c' ||                            \
-+   (c) == 'd' || (c) == 'e' || (c) == 'f' ||                            \
-+   (c) == 'A' || (c) == 'B' || (c) == 'C' ||                            \
-+   (c) == 'D' || (c) == 'E' || (c) == 'F')
-+
-+#endif /* NBDKIT_ASCII_CTYPE_H */
-diff --git a/common/include/test-ascii-ctype.c b/common/include/test-ascii-ctype.c
-new file mode 100644
-index 00000000..edf27aa6
---- /dev/null
-+++ b/common/include/test-ascii-ctype.c
-@@ -0,0 +1,63 @@
-+/* nbdkit
-+ * Copyright (C) 2020 Red Hat Inc.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are
-+ * met:
-+ *
-+ * * Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * * Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in the
-+ * documentation and/or other materials provided with the distribution.
-+ *
-+ * * Neither the name of Red Hat nor the names of its contributors may be
-+ * used to endorse or promote products derived from this software without
-+ * specific prior written permission.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
-+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
-+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
-+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-+ * SUCH DAMAGE.
-+ */
-+
-+#include <config.h>
-+
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <assert.h>
-+
-+#include "ascii-ctype.h"
-+
-+int
-+main (void)
-+{
-+  assert (ascii_isspace (' '));
-+  assert (ascii_isspace ('\t'));
-+  assert (ascii_isspace ('\n'));
-+  assert (! ascii_isspace ('a'));
-+
-+  assert (ascii_isalpha ('a'));
-+  assert (ascii_isalpha ('Z'));
-+  assert (ascii_isalpha ('z'));
-+  assert (! ascii_isalpha (' '));
-+  assert (! ascii_isalpha ('0'));
-+  { const char *s = "Ä"; assert (! ascii_isalpha (s[0])); }
-+  { const char *s = "®"; assert (! ascii_isalpha (s[0])); }
-+
-+  assert (ascii_isdigit ('0'));
-+  assert (ascii_isdigit ('9'));
-+  { const char *s = "Ø"; assert (! ascii_isdigit (s[0])); } /* U+00D8 */
-+  { const char *s = "9"; assert (! ascii_isdigit (s[0])); } /* U+FF19 */
-+
-+  exit (EXIT_SUCCESS);
-+}
-diff --git a/plugins/partitioning/partition-gpt.c b/plugins/partitioning/partition-gpt.c
-index 75b4643a..819e9abe 100644
---- a/plugins/partitioning/partition-gpt.c
-+++ b/plugins/partitioning/partition-gpt.c
-@@ -36,12 +36,12 @@
- #include <stdlib.h>
- #include <stdbool.h>
- #include <string.h>
--#include <ctype.h>
- #include <inttypes.h>
- #include <assert.h>
- 
- #include <nbdkit-plugin.h>
- 
-+#include "ascii-ctype.h"
- #include "byte-swapping.h"
- 
- #include "efi-crc32.h"
-@@ -244,19 +244,19 @@ parse_guid (const char *str, char *out)
-     return -1;
- 
-   for (i = 0; i < 8; ++i)
--    if (!isxdigit (str[i]))
-+    if (!ascii_isxdigit (str[i]))
-       return -1;
-   for (i = 9; i < 13; ++i)
--    if (!isxdigit (str[i]))
-+    if (!ascii_isxdigit (str[i]))
-       return -1;
-   for (i = 14; i < 18; ++i)
--    if (!isxdigit (str[i]))
-+    if (!ascii_isxdigit (str[i]))
-       return -1;
-   for (i = 19; i < 23; ++i)
--    if (!isxdigit (str[i]))
-+    if (!ascii_isxdigit (str[i]))
-       return -1;
-   for (i = 24; i < 36; ++i)
--    if (!isxdigit (str[i]))
-+    if (!ascii_isxdigit (str[i]))
-       return -1;
- 
-   /* The first, second and third blocks are parsed as little endian,
-diff --git a/plugins/sh/Makefile.am b/plugins/sh/Makefile.am
-index 445cdcd5..1f42b64c 100644
---- a/plugins/sh/Makefile.am
-+++ b/plugins/sh/Makefile.am
-@@ -48,6 +48,7 @@ nbdkit_sh_plugin_la_SOURCES = \
- 
- nbdkit_sh_plugin_la_CPPFLAGS = \
- 	-I$(top_srcdir)/include \
-+	-I$(top_srcdir)/common/include \
- 	-I$(top_srcdir)/common/utils \
- 	$(NULL)
- nbdkit_sh_plugin_la_CFLAGS = $(WARNINGS_CFLAGS)
-diff --git a/plugins/sh/call.c b/plugins/sh/call.c
-index 2d99a120..ae0cc0ac 100644
---- a/plugins/sh/call.c
-+++ b/plugins/sh/call.c
-@@ -44,10 +44,10 @@
- #include <poll.h>
- #include <sys/types.h>
- #include <sys/wait.h>
--#include <ctype.h>
- 
- #include <nbdkit-plugin.h>
- 
-+#include "ascii-ctype.h"
- #include "cleanup.h"
- #include "utils.h"
- 
-@@ -392,7 +392,7 @@ handle_script_error (const char *argv0, char *ebuf, size_t len)
-   }
- 
-   if (skip && ebuf[skip]) {
--    if (!isspace ((unsigned char) ebuf[skip])) {
-+    if (!ascii_isspace ((unsigned char) ebuf[skip])) {
-       /* Treat 'EINVALID' as EIO, not EINVAL */
-       err = EIO;
-       skip = 0;
-@@ -400,7 +400,7 @@ handle_script_error (const char *argv0, char *ebuf, size_t len)
-     else
-       do
-         skip++;
--      while (isspace ((unsigned char) ebuf[skip]));
-+      while (ascii_isspace ((unsigned char) ebuf[skip]));
-   }
- 
-   while (len > 0 && ebuf[len-1] == '\n')
-diff --git a/server/backend.c b/server/backend.c
-index 208c07b1..9add341f 100644
---- a/server/backend.c
-+++ b/server/backend.c
-@@ -37,13 +37,14 @@
- #include <inttypes.h>
- #include <string.h>
- #include <assert.h>
--#include <ctype.h>
- 
- #include <dlfcn.h>
- 
--#include "internal.h"
-+#include "ascii-ctype.h"
- #include "minmax.h"
- 
-+#include "internal.h"
-+
- /* Helpers for registering a new backend. */
- 
- /* Use:
-@@ -100,7 +101,7 @@ backend_load (struct backend *b, const char *name, void (*load) (void))
-   for (i = 0; i < len; ++i) {
-     unsigned char c = name[i];
- 
--    if (!(isascii (c) && isalnum (c))) {
-+    if (! ascii_isalnum (c)) {
-       fprintf (stderr,
-                "%s: %s: %s.name ('%s') field "
-                "must contain only ASCII alphanumeric characters\n",
-diff --git a/server/public.c b/server/public.c
-index 418945fe..98b78482 100644
---- a/server/public.c
-+++ b/server/public.c
-@@ -45,13 +45,13 @@
- #include <string.h>
- #include <unistd.h>
- #include <limits.h>
--#include <ctype.h>
- #include <termios.h>
- #include <errno.h>
- #include <poll.h>
- #include <signal.h>
- #include <sys/socket.h>
- 
-+#include "ascii-ctype.h"
- #include "get-current-dir-name.h"
- 
- #include "internal.h"
-@@ -210,7 +210,7 @@ nbdkit_parse_int64_t (const char *what, const char *str, int64_t *rp)
-  */
- #define PARSE_ERROR_IF_NEGATIVE                                         \
-   do {                                                                  \
--    while (isspace (*str))                                              \
-+    while (ascii_isspace (*str))                                        \
-       str++;                                                            \
-     if (*str == '-') {                                                  \
-       nbdkit_error ("%s: negative numbers are not allowed", what);      \
--- 
-2.18.2
-
diff --git a/SOURCES/0015-curl-Remove-some-useless-debug-messages.patch b/SOURCES/0015-curl-Remove-some-useless-debug-messages.patch
deleted file mode 100644
index fe49773..0000000
--- a/SOURCES/0015-curl-Remove-some-useless-debug-messages.patch
+++ /dev/null
@@ -1,46 +0,0 @@
-From 0aa8e873e626c8af5f47e2e9896f33dcff4d7bb6 Mon Sep 17 00:00:00 2001
-From: "Richard W.M. Jones" <rjones@redhat.com>
-Date: Sat, 28 Mar 2020 16:59:55 +0000
-Subject: [PATCH 15/19] curl: Remove some useless debug messages.
-
-You can get the same information by setting -D curl.verbose=1
-
-(cherry picked from commit e3539d55241adcbf1bc8102c019a0dd0ae8a3407)
----
- plugins/curl/curl.c | 6 ------
- 1 file changed, 6 deletions(-)
-
-diff --git a/plugins/curl/curl.c b/plugins/curl/curl.c
-index 007449bc..8b341ae0 100644
---- a/plugins/curl/curl.c
-+++ b/plugins/curl/curl.c
-@@ -311,8 +311,6 @@ curl_open (int readonly)
-     goto err;
-   }
- 
--  nbdkit_debug ("opened libcurl easy handle");
--
-   /* Note this writes the output to stderr directly.  We should
-    * consider using CURLOPT_DEBUGFUNCTION so we can handle it with
-    * nbdkit_debug.
-@@ -340,8 +338,6 @@ curl_open (int readonly)
-     goto err;
-   }
- 
--  nbdkit_debug ("set libcurl URL: %s", url);
--
-   curl_easy_setopt (h->c, CURLOPT_AUTOREFERER, 1);
-   curl_easy_setopt (h->c, CURLOPT_FOLLOWLOCATION, 1);
-   curl_easy_setopt (h->c, CURLOPT_FAILONERROR, 1);
-@@ -436,8 +432,6 @@ curl_open (int readonly)
-     curl_easy_setopt (h->c, CURLOPT_READDATA, h);
-   }
- 
--  nbdkit_debug ("returning new handle %p", h);
--
-   return h;
- 
-  err:
--- 
-2.18.2
-
diff --git a/SOURCES/0016-curl-Fix-D-curl.verbose-1-option.patch b/SOURCES/0016-curl-Fix-D-curl.verbose-1-option.patch
deleted file mode 100644
index 51e0233..0000000
--- a/SOURCES/0016-curl-Fix-D-curl.verbose-1-option.patch
+++ /dev/null
@@ -1,182 +0,0 @@
-From f421e599d3507f22d3d06b2dab070811e7e4f41c Mon Sep 17 00:00:00 2001
-From: "Richard W.M. Jones" <rjones@redhat.com>
-Date: Sat, 28 Mar 2020 18:22:42 +0000
-Subject: [PATCH 16/19] curl: Fix -D curl.verbose=1 option.
-
-It didn't work previously for various reasons:
-
- - Passed int instead of long to curl_easy_setopt.
-
- - No CURLOPT_DEBUGFUNCTION callback was supplied, so messages were
-   sent to stderr, which meant they were never logged for the majority
-   of use cases.
-
-This also removes extra debugging in the regular header callback.
-This is no longer needed as the now-working -D curl.verbose=1 option
-will log the headers.
-
-Fixes commit 2ba11ee8f154ad1c84e10b43479b265fca2e996b.
-
-(cherry picked from commit 6791c69bddf76577b65fa3ddfde652c0594ce340)
----
- plugins/curl/Makefile.am |  2 ++
- plugins/curl/curl.c      | 73 ++++++++++++++++++++++++++++++----------
- tests/test-curl.c        |  3 +-
- 3 files changed, 60 insertions(+), 18 deletions(-)
-
-diff --git a/plugins/curl/Makefile.am b/plugins/curl/Makefile.am
-index 6595eb95..024ddb6d 100644
---- a/plugins/curl/Makefile.am
-+++ b/plugins/curl/Makefile.am
-@@ -44,6 +44,7 @@ nbdkit_curl_plugin_la_SOURCES = \
- 
- nbdkit_curl_plugin_la_CPPFLAGS = \
- 	-I$(top_srcdir)/include \
-+	-I$(top_srcdir)/common/utils \
- 	$(NULL)
- nbdkit_curl_plugin_la_CFLAGS = \
- 	$(WARNINGS_CFLAGS) \
-@@ -51,6 +52,7 @@ nbdkit_curl_plugin_la_CFLAGS = \
- 	$(NULL)
- nbdkit_curl_plugin_la_LIBADD = \
- 	$(CURL_LIBS) \
-+	$(top_builddir)/common/utils/libutils.la \
- 	$(NULL)
- nbdkit_curl_plugin_la_LDFLAGS = \
- 	-module -avoid-version -shared \
-diff --git a/plugins/curl/curl.c b/plugins/curl/curl.c
-index 8b341ae0..b1693dc0 100644
---- a/plugins/curl/curl.c
-+++ b/plugins/curl/curl.c
-@@ -56,6 +56,8 @@
- 
- #include <nbdkit-plugin.h>
- 
-+#include "cleanup.h"
-+
- static const char *url = NULL;
- static const char *user = NULL;
- static char *password = NULL;
-@@ -283,6 +285,8 @@ struct curl_handle {
-                   curl_easy_strerror ((r)), (h)->errbuf);       \
-   } while (0)
- 
-+static int debug_cb (CURL *handle, curl_infotype type,
-+                     const char *data, size_t size, void *);
- static size_t header_cb (void *ptr, size_t size, size_t nmemb, void *opaque);
- static size_t write_cb (char *ptr, size_t size, size_t nmemb, void *opaque);
- static size_t read_cb (void *ptr, size_t size, size_t nmemb, void *opaque);
-@@ -311,11 +315,13 @@ curl_open (int readonly)
-     goto err;
-   }
- 
--  /* Note this writes the output to stderr directly.  We should
--   * consider using CURLOPT_DEBUGFUNCTION so we can handle it with
--   * nbdkit_debug.
--   */
--  curl_easy_setopt (h->c, CURLOPT_VERBOSE, curl_debug_verbose);
-+  if (curl_debug_verbose) {
-+    /* NB: Constants must be explicitly long because the parameter is
-+     * varargs.
-+     */
-+    curl_easy_setopt (h->c, CURLOPT_VERBOSE, 1L);
-+    curl_easy_setopt (h->c, CURLOPT_DEBUGFUNCTION, debug_cb);
-+  }
- 
-   curl_easy_setopt (h->c, CURLOPT_ERRORBUFFER, h->errbuf);
- 
-@@ -441,12 +447,56 @@ curl_open (int readonly)
-   return NULL;
- }
- 
-+/* When using CURLOPT_VERBOSE, this callback is used to redirect
-+ * messages to nbdkit_debug (instead of stderr).
-+ */
-+static int
-+debug_cb (CURL *handle, curl_infotype type,
-+          const char *data, size_t size, void *opaque)
-+{
-+  size_t origsize = size;
-+  CLEANUP_FREE char *str;
-+
-+  /* The data parameter passed is NOT \0-terminated, but also it may
-+   * have \n or \r\n line endings.  The only sane way to deal with
-+   * this is to copy the string.  (The data strings may also be
-+   * multi-line, but we don't deal with that here).
-+   */
-+  str = malloc (size + 1);
-+  if (str == NULL)
-+    goto out;
-+  memcpy (str, data, size);
-+  str[size] = '\0';
-+
-+  while (size > 0 && (str[size-1] == '\n' || str[size-1] == '\r')) {
-+    str[size-1] = '\0';
-+    size--;
-+  }
-+
-+  switch (type) {
-+  case CURLINFO_TEXT:
-+    nbdkit_debug ("%s", str);
-+    break;
-+  case CURLINFO_HEADER_IN:
-+    nbdkit_debug ("S: %s", str);
-+    break;
-+  case CURLINFO_HEADER_OUT:
-+    nbdkit_debug ("C: %s", str);
-+    break;
-+  default:
-+    /* Assume everything else is binary data that we cannot print. */
-+    nbdkit_debug ("<data with size=%zu>", origsize);
-+  }
-+
-+ out:
-+  return 0;
-+}
-+
- static size_t
- header_cb (void *ptr, size_t size, size_t nmemb, void *opaque)
- {
-   struct curl_handle *h = opaque;
-   size_t realsize = size * nmemb;
--  size_t len;
-   const char *accept_line = "Accept-Ranges: bytes";
-   const char *line = ptr;
- 
-@@ -454,17 +504,6 @@ header_cb (void *ptr, size_t size, size_t nmemb, void *opaque)
-       strncmp (line, accept_line, strlen (accept_line)) == 0)
-     h->accept_range = true;
- 
--  /* Useful to print the server headers when debugging.  However we
--   * must strip off trailing \r?\n from each line.
--   */
--  len = realsize;
--  if (len > 0 && line[len-1] == '\n')
--    len--;
--  if (len > 0 && line[len-1] == '\r')
--    len--;
--  if (len > 0)
--    nbdkit_debug ("S: %.*s", (int) len, line);
--
-   return realsize;
- }
- 
-diff --git a/tests/test-curl.c b/tests/test-curl.c
-index 2b7e3beb..165edb35 100644
---- a/tests/test-curl.c
-+++ b/tests/test-curl.c
-@@ -74,9 +74,10 @@ main (int argc, char *argv[])
-     exit (EXIT_FAILURE);
-   }
-   if (test_start_nbdkit ("curl",
-+                         "-D", "curl.verbose=1",
-+                         "http://localhost/disk",
-                          "cookie=foo=bar; baz=1;",
-                          usp_param, /* unix-socket-path=... */
--                         "http://localhost/disk",
-                          NULL) == -1)
-     exit (EXIT_FAILURE);
- 
--- 
-2.18.2
-
diff --git a/SOURCES/0017-curl-Case-insensitive-check-for-accept-ranges-RHBZ-1.patch b/SOURCES/0017-curl-Case-insensitive-check-for-accept-ranges-RHBZ-1.patch
deleted file mode 100644
index 0e83b5d..0000000
--- a/SOURCES/0017-curl-Case-insensitive-check-for-accept-ranges-RHBZ-1.patch
+++ /dev/null
@@ -1,116 +0,0 @@
-From aa62596afcc9143aa663bf834d305441cdd4cc70 Mon Sep 17 00:00:00 2001
-From: "Richard W.M. Jones" <rjones@redhat.com>
-Date: Tue, 19 May 2020 11:15:07 +0100
-Subject: [PATCH 17/19] curl: Case insensitive check for accept-ranges
- (RHBZ#1837337).
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-When accessing an HTTP/2 server we read lowercase headers so the
-existing test for byte range support did not work.  You would see an
-error like this:
-
-  nbdkit: curl[1]: error: server does not support 'range' (byte range) requests
-
-This commit copies the bug fix which was recently added to qemu’s
-block/curl.c.
-
-qemu commits:
-
-  commit 69032253c33ae1774233c63cedf36d32242a85fc
-  Author: David Edmondson <david.edmondson@oracle.com>
-  Date:   Mon Feb 24 10:13:10 2020 +0000
-
-    block/curl: HTTP header field names are case insensitive
-
-  commit 7788a319399f17476ff1dd43164c869e320820a2
-  Author: David Edmondson <david.edmondson@oracle.com>
-  Date:   Mon Feb 24 10:13:09 2020 +0000
-
-    block/curl: HTTP header fields allow whitespace around values
-
-Thanks: David Edmondson, Pino Toscano, Zi Liu
-(cherry picked from commit c1260ec1f6538831e10f164567b53054a2ec0c2a)
----
- plugins/curl/Makefile.am |  1 +
- plugins/curl/curl.c      | 29 ++++++++++++++++++++++++-----
- tests/web-server.c       |  2 +-
- 3 files changed, 26 insertions(+), 6 deletions(-)
-
-diff --git a/plugins/curl/Makefile.am b/plugins/curl/Makefile.am
-index 024ddb6d..3dbe3ca8 100644
---- a/plugins/curl/Makefile.am
-+++ b/plugins/curl/Makefile.am
-@@ -44,6 +44,7 @@ nbdkit_curl_plugin_la_SOURCES = \
- 
- nbdkit_curl_plugin_la_CPPFLAGS = \
- 	-I$(top_srcdir)/include \
-+	-I$(top_srcdir)/common/include \
- 	-I$(top_srcdir)/common/utils \
- 	$(NULL)
- nbdkit_curl_plugin_la_CFLAGS = \
-diff --git a/plugins/curl/curl.c b/plugins/curl/curl.c
-index b1693dc0..ac30cbdd 100644
---- a/plugins/curl/curl.c
-+++ b/plugins/curl/curl.c
-@@ -57,6 +57,7 @@
- #include <nbdkit-plugin.h>
- 
- #include "cleanup.h"
-+#include "ascii-ctype.h"
- 
- static const char *url = NULL;
- static const char *user = NULL;
-@@ -497,12 +498,30 @@ header_cb (void *ptr, size_t size, size_t nmemb, void *opaque)
- {
-   struct curl_handle *h = opaque;
-   size_t realsize = size * nmemb;
--  const char *accept_line = "Accept-Ranges: bytes";
--  const char *line = ptr;
-+  const char *header = ptr;
-+  const char *end = header + realsize;
-+  const char *accept_ranges = "accept-ranges:";
-+  const char *bytes = "bytes";
- 
--  if (realsize >= strlen (accept_line) &&
--      strncmp (line, accept_line, strlen (accept_line)) == 0)
--    h->accept_range = true;
-+  if (realsize >= strlen (accept_ranges) &&
-+      strncasecmp (header, accept_ranges, strlen (accept_ranges)) == 0) {
-+    const char *p = strchr (header, ':') + 1;
-+
-+    /* Skip whitespace between the header name and value. */
-+    while (p < end && *p && ascii_isspace (*p))
-+      p++;
-+
-+    if (end - p >= strlen (bytes)
-+        && strncmp (p, bytes, strlen (bytes)) == 0) {
-+      /* Check that there is nothing but whitespace after the value. */
-+      p += strlen (bytes);
-+      while (p < end && *p && ascii_isspace (*p))
-+        p++;
-+
-+      if (p == end || !*p)
-+        h->accept_range = true;
-+    }
-+  }
- 
-   return realsize;
- }
-diff --git a/tests/web-server.c b/tests/web-server.c
-index f27ee70d..f9f10917 100644
---- a/tests/web-server.c
-+++ b/tests/web-server.c
-@@ -235,7 +235,7 @@ handle_request (int s, bool headers_only)
-   const char response1_ok[] = "HTTP/1.1 200 OK\r\n";
-   const char response1_partial[] = "HTTP/1.1 206 Partial Content\r\n";
-   const char response2[] =
--    "Accept-Ranges: bytes\r\n"
-+    "Accept-rANGES:     bytes\r\n" /* See RHBZ#1837337 */
-     "Connection: keep-alive\r\n"
-     "Content-Type: application/octet-stream\r\n";
-   char response3[64];
--- 
-2.18.2
-
diff --git a/SOURCES/0018-sh-Don-t-need-to-cast-parameter-of-ascii_is-to-unsig.patch b/SOURCES/0018-sh-Don-t-need-to-cast-parameter-of-ascii_is-to-unsig.patch
deleted file mode 100644
index 5bbed35..0000000
--- a/SOURCES/0018-sh-Don-t-need-to-cast-parameter-of-ascii_is-to-unsig.patch
+++ /dev/null
@@ -1,41 +0,0 @@
-From d5947881c2918196d61d7795adba0abb881a86b9 Mon Sep 17 00:00:00 2001
-From: "Richard W.M. Jones" <rjones@redhat.com>
-Date: Tue, 19 May 2020 15:29:55 +0100
-Subject: [PATCH 18/19] sh: Don't need to cast parameter of ascii_is* to
- (unsigned char).
-
-Our replacements for these functions are not undefined for negative
-values.
-
-Thanks: Eric Blake
-Fixes: commit 9f34db74786fdc92b290a7d47e4b003bd84fec69.
-(cherry picked from commit 06a79b8bb8cfd97a272223c967601d8858acb817)
----
- plugins/sh/call.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/plugins/sh/call.c b/plugins/sh/call.c
-index ae0cc0ac..ba9f055f 100644
---- a/plugins/sh/call.c
-+++ b/plugins/sh/call.c
-@@ -392,7 +392,7 @@ handle_script_error (const char *argv0, char *ebuf, size_t len)
-   }
- 
-   if (skip && ebuf[skip]) {
--    if (!ascii_isspace ((unsigned char) ebuf[skip])) {
-+    if (!ascii_isspace (ebuf[skip])) {
-       /* Treat 'EINVALID' as EIO, not EINVAL */
-       err = EIO;
-       skip = 0;
-@@ -400,7 +400,7 @@ handle_script_error (const char *argv0, char *ebuf, size_t len)
-     else
-       do
-         skip++;
--      while (ascii_isspace ((unsigned char) ebuf[skip]));
-+      while (ascii_isspace (ebuf[skip]));
-   }
- 
-   while (len > 0 && ebuf[len-1] == '\n')
--- 
-2.18.2
-
diff --git a/SOURCES/0019-common-include-Add-locale-safe-ascii_strcasecmp-and-.patch b/SOURCES/0019-common-include-Add-locale-safe-ascii_strcasecmp-and-.patch
deleted file mode 100644
index 5437c9e..0000000
--- a/SOURCES/0019-common-include-Add-locale-safe-ascii_strcasecmp-and-.patch
+++ /dev/null
@@ -1,559 +0,0 @@
-From 9a99549f5df6ef69dd1d2b509c13aaff4e431742 Mon Sep 17 00:00:00 2001
-From: "Richard W.M. Jones" <rjones@redhat.com>
-Date: Tue, 19 May 2020 16:11:28 +0100
-Subject: [PATCH 19/19] common/include: Add locale-safe ascii_strcasecmp and
- ascii_strncasecmp.
-
-These are derived from the FreeBSD functions here:
-https://github.com/freebsd/freebsd/blob/master/sys/libkern/strcasecmp.c
-
-Thanks: Eric Blake.
-(cherry picked from commit 46a29b8e91d69e812d78df53e91b526a560000fe)
----
- .gitignore                          |  1 +
- common/include/Makefile.am          |  6 +++
- common/include/ascii-ctype.h        |  6 +++
- common/include/ascii-string.h       | 77 ++++++++++++++++++++++++++++
- common/include/test-ascii-string.c  | 79 +++++++++++++++++++++++++++++
- plugins/curl/curl.c                 |  7 +--
- plugins/info/info.c                 | 17 ++++---
- plugins/nbd/nbd.c                   |  8 +--
- plugins/partitioning/partitioning.c | 10 ++--
- plugins/sh/call.c                   | 25 ++++-----
- server/main.c                       |  8 +--
- server/public.c                     | 21 ++++----
- 12 files changed, 222 insertions(+), 43 deletions(-)
- create mode 100644 common/include/ascii-string.h
- create mode 100644 common/include/test-ascii-string.c
-
-diff --git a/.gitignore b/.gitignore
-index 523894b7..aa148f04 100644
---- a/.gitignore
-+++ b/.gitignore
-@@ -27,6 +27,7 @@ Makefile.in
- /autom4te.cache
- /common/bitmap/test-bitmap
- /common/include/test-ascii-ctype
-+/common/include/test-ascii-string
- /common/include/test-byte-swapping
- /common/include/test-current-dir-name
- /common/include/test-isaligned
-diff --git a/common/include/Makefile.am b/common/include/Makefile.am
-index d7b0d7a8..eff71863 100644
---- a/common/include/Makefile.am
-+++ b/common/include/Makefile.am
-@@ -35,6 +35,7 @@ include $(top_srcdir)/common-rules.mk
- # plugins and/or filters.  They are not installed.
- EXTRA_DIST = \
- 	ascii-ctype.h \
-+	ascii-string.h \
- 	byte-swapping.h \
- 	exit-with-parent.h \
- 	get-current-dir-name.h \
-@@ -52,6 +53,7 @@ EXTRA_DIST = \
- 
- TESTS = \
- 	test-ascii-ctype \
-+	test-ascii-string \
- 	test-byte-swapping \
- 	test-current-dir-name \
- 	test-isaligned \
-@@ -68,6 +70,10 @@ test_ascii_ctype_SOURCES = test-ascii-ctype.c ascii-ctype.h
- test_ascii_ctype_CPPFLAGS = -I$(srcdir)
- test_ascii_ctype_CFLAGS = $(WARNINGS_CFLAGS)
- 
-+test_ascii_string_SOURCES = test-ascii-string.c ascii-string.h
-+test_ascii_string_CPPFLAGS = -I$(srcdir)
-+test_ascii_string_CFLAGS = $(WARNINGS_CFLAGS)
-+
- test_byte_swapping_SOURCES = test-byte-swapping.c byte-swapping.h
- test_byte_swapping_CPPFLAGS = -I$(srcdir)
- test_byte_swapping_CFLAGS = $(WARNINGS_CFLAGS)
-diff --git a/common/include/ascii-ctype.h b/common/include/ascii-ctype.h
-index 5e8bf237..a700563e 100644
---- a/common/include/ascii-ctype.h
-+++ b/common/include/ascii-ctype.h
-@@ -49,6 +49,9 @@
- #define ascii_isspace(c)                                                \
-   ((c) == '\t' || (c) == '\n' || (c) == '\f' || (c) == '\r' || (c) == ' ')
- 
-+#define ascii_isupper(c)                        \
-+  ((c) >= 'A' && (c) <= 'Z')
-+
- #define ascii_isxdigit(c)                                               \
-   ((c) == '0' || (c) == '1' || (c) == '2' || (c) == '3' || (c) == '4' || \
-    (c) == '5' || (c) == '6' || (c) == '7' || (c) == '8' || (c) == '9' || \
-@@ -57,4 +60,7 @@
-    (c) == 'A' || (c) == 'B' || (c) == 'C' ||                            \
-    (c) == 'D' || (c) == 'E' || (c) == 'F')
- 
-+#define ascii_tolower(c)                        \
-+  (ascii_isupper ((c)) ? (c) - 'A' + 'a' : (c))
-+
- #endif /* NBDKIT_ASCII_CTYPE_H */
-diff --git a/common/include/ascii-string.h b/common/include/ascii-string.h
-new file mode 100644
-index 00000000..0a60d5f4
---- /dev/null
-+++ b/common/include/ascii-string.h
-@@ -0,0 +1,77 @@
-+/* nbdkit
-+ * Copyright (C) 2013-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.
-+ */
-+
-+/* Case insensitive string comparison functions (like strcasecmp,
-+ * strncasecmp) which work correctly in any locale.  They can only be
-+ * used for comparison when one or both strings is 7 bit ASCII.
-+ */
-+
-+#ifndef NBDKIT_ASCII_STRING_H
-+#define NBDKIT_ASCII_STRING_H
-+
-+#include "ascii-ctype.h"
-+
-+static inline int
-+ascii_strcasecmp (const char *s1, const char *s2)
-+{
-+  const unsigned char *us1 = (const unsigned char *)s1;
-+  const unsigned char *us2 = (const unsigned char *)s2;
-+
-+  while (ascii_tolower (*us1) == ascii_tolower (*us2)) {
-+    if (*us1++ == '\0')
-+      return 0;
-+    us2++;
-+  }
-+
-+  return ascii_tolower (*us1) - ascii_tolower (*us2);
-+}
-+
-+static inline int
-+ascii_strncasecmp (const char *s1, const char *s2, size_t n)
-+{
-+  if (n != 0) {
-+    const unsigned char *us1 = (const unsigned char *)s1;
-+    const unsigned char *us2 = (const unsigned char *)s2;
-+
-+    do {
-+      if (ascii_tolower (*us1) != ascii_tolower (*us2))
-+        return ascii_tolower (*us1) - ascii_tolower (*us2);
-+      if (*us1++ == '\0')
-+        break;
-+      us2++;
-+    } while (--n != 0);
-+  }
-+
-+  return 0;
-+}
-+
-+#endif /* NBDKIT_ASCII_STRING_H */
-diff --git a/common/include/test-ascii-string.c b/common/include/test-ascii-string.c
-new file mode 100644
-index 00000000..0fa4a483
---- /dev/null
-+++ b/common/include/test-ascii-string.c
-@@ -0,0 +1,79 @@
-+/* nbdkit
-+ * Copyright (C) 2020 Red Hat Inc.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are
-+ * met:
-+ *
-+ * * Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * * Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in the
-+ * documentation and/or other materials provided with the distribution.
-+ *
-+ * * Neither the name of Red Hat nor the names of its contributors may be
-+ * used to endorse or promote products derived from this software without
-+ * specific prior written permission.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
-+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
-+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
-+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-+ * SUCH DAMAGE.
-+ */
-+
-+#include <config.h>
-+
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <assert.h>
-+
-+#include "ascii-string.h"
-+
-+int
-+main (void)
-+{
-+  assert (ascii_strcasecmp ("", "") == 0);
-+  assert (ascii_strcasecmp ("a", "a") == 0);
-+  assert (ascii_strcasecmp ("abc", "abc") == 0);
-+  assert (ascii_strcasecmp ("a", "b") < 0);
-+  assert (ascii_strcasecmp ("b", "a") > 0);
-+  assert (ascii_strcasecmp ("aa", "a") > 0);
-+
-+  /* Second string contains Turkish dotless lowercase letter ı. */
-+  assert (ascii_strcasecmp ("hi", "hı") != 0);
-+
-+  /* Check that we got our rounding behaviour correct. */
-+  assert (ascii_strcasecmp ("\x1", "\x7f") < 0);
-+  assert (ascii_strcasecmp ("\x1", "\x80") < 0);
-+  assert (ascii_strcasecmp ("\x1", "\x81") < 0);
-+  assert (ascii_strcasecmp ("\x1", "\xff") < 0);
-+
-+  assert (ascii_strncasecmp ("", "", 0) == 0);
-+  assert (ascii_strncasecmp ("a", "a", 1) == 0);
-+  assert (ascii_strncasecmp ("abc", "abc", 3) == 0);
-+  assert (ascii_strncasecmp ("abc", "def", 0) == 0);
-+  assert (ascii_strncasecmp ("abc", "abd", 2) == 0);
-+  assert (ascii_strncasecmp ("a", "b", 1) < 0);
-+  assert (ascii_strncasecmp ("b", "a", 1) > 0);
-+  assert (ascii_strncasecmp ("aa", "a", 2) > 0);
-+  assert (ascii_strncasecmp ("aa", "a", 100) > 0);
-+
-+  assert (ascii_strncasecmp ("hi", "hı", 1) == 0);
-+  assert (ascii_strncasecmp ("hi", "hı", 2) != 0);
-+
-+  assert (ascii_strncasecmp ("\x1", "\x7f", 1) < 0);
-+  assert (ascii_strncasecmp ("\x1", "\x80", 1) < 0);
-+  assert (ascii_strncasecmp ("\x1", "\x81", 1) < 0);
-+  assert (ascii_strncasecmp ("\x1", "\xff", 1) < 0);
-+
-+  exit (EXIT_SUCCESS);
-+}
-diff --git a/plugins/curl/curl.c b/plugins/curl/curl.c
-index ac30cbdd..00f0628a 100644
---- a/plugins/curl/curl.c
-+++ b/plugins/curl/curl.c
-@@ -58,6 +58,7 @@
- 
- #include "cleanup.h"
- #include "ascii-ctype.h"
-+#include "ascii-string.h"
- 
- static const char *url = NULL;
- static const char *user = NULL;
-@@ -419,8 +420,8 @@ curl_open (int readonly)
- #endif
-   nbdkit_debug ("content length: %" PRIi64, h->exportsize);
- 
--  if (strncasecmp (url, "http://", strlen ("http://")) == 0 ||
--      strncasecmp (url, "https://", strlen ("https://")) == 0) {
-+  if (ascii_strncasecmp (url, "http://", strlen ("http://")) == 0 ||
-+      ascii_strncasecmp (url, "https://", strlen ("https://")) == 0) {
-     if (!h->accept_range) {
-       nbdkit_error ("server does not support 'range' (byte range) requests");
-       goto err;
-@@ -504,7 +505,7 @@ header_cb (void *ptr, size_t size, size_t nmemb, void *opaque)
-   const char *bytes = "bytes";
- 
-   if (realsize >= strlen (accept_ranges) &&
--      strncasecmp (header, accept_ranges, strlen (accept_ranges)) == 0) {
-+      ascii_strncasecmp (header, accept_ranges, strlen (accept_ranges)) == 0) {
-     const char *p = strchr (header, ':') + 1;
- 
-     /* Skip whitespace between the header name and value. */
-diff --git a/plugins/info/info.c b/plugins/info/info.c
-index 329a3684..e04d672b 100644
---- a/plugins/info/info.c
-+++ b/plugins/info/info.c
-@@ -49,6 +49,7 @@
- 
- #include <nbdkit-plugin.h>
- 
-+#include "ascii-string.h"
- #include "byte-swapping.h"
- #include "tvdiff.h"
- 
-@@ -76,12 +77,12 @@ static int
- info_config (const char *key, const char *value)
- {
-   if (strcmp (key, "mode") == 0) {
--    if (strcasecmp (value, "exportname") == 0 ||
--        strcasecmp (value, "export-name") == 0) {
-+    if (ascii_strcasecmp (value, "exportname") == 0 ||
-+        ascii_strcasecmp (value, "export-name") == 0) {
-       mode = MODE_EXPORTNAME;
-     }
--    else if (strcasecmp (value, "base64exportname") == 0 ||
--             strcasecmp (value, "base64-export-name") == 0) {
-+    else if (ascii_strcasecmp (value, "base64exportname") == 0 ||
-+             ascii_strcasecmp (value, "base64-export-name") == 0) {
- #ifdef HAVE_BASE64
-       mode = MODE_BASE64EXPORTNAME;
- #else
-@@ -89,13 +90,13 @@ info_config (const char *key, const char *value)
-       return -1;
- #endif
-     }
--    else if (strcasecmp (value, "address") == 0)
-+    else if (ascii_strcasecmp (value, "address") == 0)
-       mode = MODE_ADDRESS;
--    else if (strcasecmp (value, "time") == 0)
-+    else if (ascii_strcasecmp (value, "time") == 0)
-       mode = MODE_TIME;
--    else if (strcasecmp (value, "uptime") == 0)
-+    else if (ascii_strcasecmp (value, "uptime") == 0)
-       mode = MODE_UPTIME;
--    else if (strcasecmp (value, "conntime") == 0)
-+    else if (ascii_strcasecmp (value, "conntime") == 0)
-       mode = MODE_CONNTIME;
-     else {
-       nbdkit_error ("unknown mode: '%s'", value);
-diff --git a/plugins/nbd/nbd.c b/plugins/nbd/nbd.c
-index d020beec..980ce8ec 100644
---- a/plugins/nbd/nbd.c
-+++ b/plugins/nbd/nbd.c
-@@ -52,6 +52,8 @@
- #define NBDKIT_API_VERSION 2
- 
- #include <nbdkit-plugin.h>
-+
-+#include "ascii-string.h"
- #include "byte-swapping.h"
- #include "cleanup.h"
- #include "utils.h"
-@@ -152,9 +154,9 @@ nbdplug_config (const char *key, const char *value)
-     shared = r;
-   }
-   else if (strcmp (key, "tls") == 0) {
--    if (strcasecmp (value, "require") == 0 ||
--        strcasecmp (value, "required") == 0 ||
--        strcasecmp (value, "force") == 0)
-+    if (ascii_strcasecmp (value, "require") == 0 ||
-+        ascii_strcasecmp (value, "required") == 0 ||
-+        ascii_strcasecmp (value, "force") == 0)
-       tls = LIBNBD_TLS_REQUIRE;
-     else {
-       r = nbdkit_parse_bool (value);
-diff --git a/plugins/partitioning/partitioning.c b/plugins/partitioning/partitioning.c
-index 6e426b93..e35764dc 100644
---- a/plugins/partitioning/partitioning.c
-+++ b/plugins/partitioning/partitioning.c
-@@ -48,6 +48,7 @@
- 
- #include <nbdkit-plugin.h>
- 
-+#include "ascii-string.h"
- #include "byte-swapping.h"
- #include "isaligned.h"
- #include "iszero.h"
-@@ -176,9 +177,10 @@ partitioning_config (const char *key, const char *value)
-     nr_files++;
-   }
-   else if (strcmp (key, "partition-type") == 0) {
--    if (strcasecmp (value, "mbr") == 0 || strcasecmp (value, "dos") == 0)
-+    if (ascii_strcasecmp (value, "mbr") == 0 ||
-+        ascii_strcasecmp (value, "dos") == 0)
-       parttype = PARTTYPE_MBR;
--    else if (strcasecmp (value, "gpt") == 0)
-+    else if (ascii_strcasecmp (value, "gpt") == 0)
-       parttype = PARTTYPE_GPT;
-     else {
-       nbdkit_error ("unknown partition-type: %s", value);
-@@ -209,13 +211,13 @@ partitioning_config (const char *key, const char *value)
-     alignment = r;
-   }
-   else if (strcmp (key, "mbr-id") == 0) {
--    if (strcasecmp (value, "default") == 0)
-+    if (ascii_strcasecmp (value, "default") == 0)
-       mbr_id = DEFAULT_MBR_ID;
-     else if (nbdkit_parse_uint8_t ("mbr-id", value, &mbr_id) == -1)
-       return -1;
-   }
-   else if (strcmp (key, "type-guid") == 0) {
--    if (strcasecmp (value, "default") == 0)
-+    if (ascii_strcasecmp (value, "default") == 0)
-       parse_guid (DEFAULT_TYPE_GUID, type_guid);
-     else if (parse_guid (value, type_guid) == -1) {
-       nbdkit_error ("could not validate GUID: %s", value);
-diff --git a/plugins/sh/call.c b/plugins/sh/call.c
-index ba9f055f..554e2f78 100644
---- a/plugins/sh/call.c
-+++ b/plugins/sh/call.c
-@@ -48,6 +48,7 @@
- #include <nbdkit-plugin.h>
- 
- #include "ascii-ctype.h"
-+#include "ascii-string.h"
- #include "cleanup.h"
- #include "utils.h"
- 
-@@ -332,48 +333,48 @@ handle_script_error (const char *argv0, char *ebuf, size_t len)
-   }
- 
-   /* Recognize the errno values that match NBD protocol errors */
--  if (strncasecmp (ebuf, "EPERM", 5) == 0) {
-+  if (ascii_strncasecmp (ebuf, "EPERM", 5) == 0) {
-     err = EPERM;
-     skip = 5;
-   }
--  else if (strncasecmp (ebuf, "EIO", 3) == 0) {
-+  else if (ascii_strncasecmp (ebuf, "EIO", 3) == 0) {
-     err = EIO;
-     skip = 3;
-   }
--  else if (strncasecmp (ebuf, "ENOMEM", 6) == 0) {
-+  else if (ascii_strncasecmp (ebuf, "ENOMEM", 6) == 0) {
-     err = ENOMEM;
-     skip = 6;
-   }
--  else if (strncasecmp (ebuf, "EINVAL", 6) == 0) {
-+  else if (ascii_strncasecmp (ebuf, "EINVAL", 6) == 0) {
-     err = EINVAL;
-     skip = 6;
-   }
--  else if (strncasecmp (ebuf, "ENOSPC", 6) == 0) {
-+  else if (ascii_strncasecmp (ebuf, "ENOSPC", 6) == 0) {
-     err = ENOSPC;
-     skip = 6;
-   }
--  else if (strncasecmp (ebuf, "EOVERFLOW", 9) == 0) {
-+  else if (ascii_strncasecmp (ebuf, "EOVERFLOW", 9) == 0) {
-     err = EOVERFLOW;
-     skip = 9;
-   }
--  else if (strncasecmp (ebuf, "ESHUTDOWN", 9) == 0) {
-+  else if (ascii_strncasecmp (ebuf, "ESHUTDOWN", 9) == 0) {
-     err = ESHUTDOWN;
-     skip = 9;
-   }
--  else if (strncasecmp (ebuf, "ENOTSUP", 7) == 0) {
-+  else if (ascii_strncasecmp (ebuf, "ENOTSUP", 7) == 0) {
-     err = ENOTSUP;
-     skip = 7;
-   }
--  else if (strncasecmp (ebuf, "EOPNOTSUPP", 10) == 0) {
-+  else if (ascii_strncasecmp (ebuf, "EOPNOTSUPP", 10) == 0) {
-     err = EOPNOTSUPP;
-     skip = 10;
-   }
-   /* Other errno values that server/protocol.c treats specially */
--  else if (strncasecmp (ebuf, "EROFS", 5) == 0) {
-+  else if (ascii_strncasecmp (ebuf, "EROFS", 5) == 0) {
-     err = EROFS;
-     skip = 5;
-   }
--  else if (strncasecmp (ebuf, "EDQUOT", 6) == 0) {
-+  else if (ascii_strncasecmp (ebuf, "EDQUOT", 6) == 0) {
- #ifdef EDQUOT
-     err = EDQUOT;
- #else
-@@ -381,7 +382,7 @@ handle_script_error (const char *argv0, char *ebuf, size_t len)
- #endif
-     skip = 6;
-   }
--  else if (strncasecmp (ebuf, "EFBIG", 5) == 0) {
-+  else if (ascii_strncasecmp (ebuf, "EFBIG", 5) == 0) {
-     err = EFBIG;
-     skip = 5;
-   }
-diff --git a/server/main.c b/server/main.c
-index 11ba1e6d..08116d74 100644
---- a/server/main.c
-+++ b/server/main.c
-@@ -55,6 +55,8 @@
- 
- #include <dlfcn.h>
- 
-+#include "ascii-string.h"
-+
- #include "internal.h"
- #include "nbd-protocol.h"
- #include "options.h"
-@@ -282,9 +284,9 @@ main (int argc, char *argv[])
- 
-     case TLS_OPTION:
-       tls_set_on_cli = true;
--      if (strcasecmp (optarg, "require") == 0 ||
--          strcasecmp (optarg, "required") == 0 ||
--          strcasecmp (optarg, "force") == 0)
-+      if (ascii_strcasecmp (optarg, "require") == 0 ||
-+          ascii_strcasecmp (optarg, "required") == 0 ||
-+          ascii_strcasecmp (optarg, "force") == 0)
-         tls = 2;
-       else {
-         tls = nbdkit_parse_bool (optarg);
-diff --git a/server/public.c b/server/public.c
-index 98b78482..919f082d 100644
---- a/server/public.c
-+++ b/server/public.c
-@@ -52,6 +52,7 @@
- #include <sys/socket.h>
- 
- #include "ascii-ctype.h"
-+#include "ascii-string.h"
- #include "get-current-dir-name.h"
- 
- #include "internal.h"
-@@ -385,19 +386,19 @@ int
- nbdkit_parse_bool (const char *str)
- {
-   if (!strcmp (str, "1") ||
--      !strcasecmp (str, "true") ||
--      !strcasecmp (str, "t") ||
--      !strcasecmp (str, "yes") ||
--      !strcasecmp (str, "y") ||
--      !strcasecmp (str, "on"))
-+      !ascii_strcasecmp (str, "true") ||
-+      !ascii_strcasecmp (str, "t") ||
-+      !ascii_strcasecmp (str, "yes") ||
-+      !ascii_strcasecmp (str, "y") ||
-+      !ascii_strcasecmp (str, "on"))
-     return 1;
- 
-   if (!strcmp (str, "0") ||
--      !strcasecmp (str, "false") ||
--      !strcasecmp (str, "f") ||
--      !strcasecmp (str, "no") ||
--      !strcasecmp (str, "n") ||
--      !strcasecmp (str, "off"))
-+      !ascii_strcasecmp (str, "false") ||
-+      !ascii_strcasecmp (str, "f") ||
-+      !ascii_strcasecmp (str, "no") ||
-+      !ascii_strcasecmp (str, "n") ||
-+      !ascii_strcasecmp (str, "off"))
-     return 0;
- 
-   nbdkit_error ("could not decipher boolean (%s)", str);
--- 
-2.18.2
-
diff --git a/SOURCES/copy-patches.sh b/SOURCES/copy-patches.sh
new file mode 100755
index 0000000..299454a
--- /dev/null
+++ b/SOURCES/copy-patches.sh
@@ -0,0 +1,55 @@
+#!/bin/bash -
+
+set -e
+
+# Maintainer script to copy patches from the git repo to the current
+# directory.  Use it like this:
+#   ./copy-patches.sh
+
+rhel_version=8.6
+
+# Check we're in the right directory.
+if [ ! -f nbdkit.spec ]; then
+    echo "$0: run this from the directory containing 'nbdkit.spec'"
+    exit 1
+fi
+
+git_checkout=$HOME/d/nbdkit-rhel-$rhel_version
+if [ ! -d $git_checkout ]; then
+    echo "$0: $git_checkout does not exist"
+    echo "This script is only for use by the maintainer when preparing a"
+    echo "nbdkit release on RHEL."
+    exit 1
+fi
+
+# Get the base version of nbdkit.
+version=`grep '^Version:' nbdkit.spec | awk '{print $2}'`
+tag="v$version"
+
+# Remove any existing patches.
+git rm -f [0-9]*.patch ||:
+rm -f [0-9]*.patch
+
+# Get the patches.
+(cd $git_checkout; rm -f [0-9]*.patch; git format-patch -N $tag)
+mv $git_checkout/[0-9]*.patch .
+
+# Remove any not to be applied.
+rm -f *NOT-FOR-RPM*.patch
+
+# Add the patches.
+git add [0-9]*.patch
+
+# Print out the patch lines.
+echo
+echo "--- Copy the following text into nbdkit.spec file"
+echo
+
+echo "# Patches."
+for f in [0-9]*.patch; do
+    n=`echo $f | awk -F- '{print $1}'`
+    echo "Patch$n:     $f"
+done
+
+echo
+echo "--- End of text"
diff --git a/SOURCES/nbdkit-1.16.2.tar.gz.sig b/SOURCES/nbdkit-1.16.2.tar.gz.sig
deleted file mode 100644
index 1360653..0000000
--- a/SOURCES/nbdkit-1.16.2.tar.gz.sig
+++ /dev/null
@@ -1,17 +0,0 @@
------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/SOURCES/nbdkit-1.24.0.tar.gz.sig b/SOURCES/nbdkit-1.24.0.tar.gz.sig
new file mode 100644
index 0000000..14ff4e8
--- /dev/null
+++ b/SOURCES/nbdkit-1.24.0.tar.gz.sig
@@ -0,0 +1,17 @@
+-----BEGIN PGP SIGNATURE-----
+
+iQJFBAABCAAvFiEE93dPsa0HSn6Mh2fqkXOPc+G3aKAFAl/3RBgRHHJpY2hAYW5u
+ZXhpYS5vcmcACgkQkXOPc+G3aKBIIRAAmgoGrmJ8aYO7z+kKgNFjd/p0QxRTZhS/
+ol59ojG6jIzN2x/C2PFbRmPB6HJTEg4anrDX04WrP6R+lID1RrH9pTFQabv0YDQC
+z49oeXAqINYHvAqgFUJCwlymd7BHEYUudLlK3yu7gQKxMM+J/2v0glpxrtLM7KlD
+vvSZkVfbvHlCWIbMWLWIaRHeoWZIXNOjsAp3uEWN2YgikDoxbXVKoh07JoQx5tJ5
+2U+a/zo4BQuRspjnhmWc252ZF/8d954/L8J+2mKvbRRf2iAmsqPgS+MNi7WKWO4K
+w7/urKn0osuOaArs5xYHJnApmJ9U88CzZpoHQkYhcGgnDOipW9ByJRzT41vVQPW5
+IluQODpZUuawWtRIwV/Eoi+LaV2gINAL48Afr02UFYj4gmYQ5TeayLP7NKRQO0VL
+jwL4Z3a0cDyUX4i1OArn2ll8THfiog38HfLb70AG1l3P1BVoVVBYWCYbs4xgC9IK
+LWkjPKuGXvkGVfZi0nCGdPTOoB1CqCXUvKHXm52FCHg12uJMrBQEivodBoCTbtl0
+fSjULQcfrovUEb4d/rDAX7EgJbFS+1jDnodaFHsmNToo3CqfkMBdhLkxG3XExwjy
+OOR34wZssjTLsLlWH/RPucWD25RDy1vdPBska9QvvO7W0p+aOtFbnttkTh5cqs45
+rHg/sDEiaLA=
+=OrsS
+-----END PGP SIGNATURE-----
diff --git a/SPECS/nbdkit.spec b/SPECS/nbdkit.spec
index 416c628..8dc63fc 100644
--- a/SPECS/nbdkit.spec
+++ b/SPECS/nbdkit.spec
@@ -4,6 +4,12 @@
 %global have_libguestfs 1
 %endif
 
+# We can only compile the OCaml plugin on platforms which have native
+# OCaml support (not bytecode).
+%ifarch %{ocaml_native_compiler}
+%global have_ocaml 1
+%endif
+
 # Architectures where the complete test suite must pass.
 #
 # On all other architectures, a simpler test suite must pass.  This
@@ -11,28 +17,40 @@
 # 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
+# If the test suite is broken on a particular architecture, document
+# it as a bug and add it to this list.
+%global broken_test_arches NONE
+
+%if 0%{?rhel} == 7
+# On RHEL 7, nothing in the virt stack is shipped on aarch64 and
+# libguestfs was not shipped on POWER (fixed in 7.5).  We could in
+# theory make all of this work by having lots more conditionals, but
+# for now limit this package to x86_64 on RHEL.
+ExclusiveArch:  x86_64
 %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
+%global patches_touch_autotools %{nil}
 
 # The source directory.
-%global source_directory 1.16-stable
+%global source_directory 1.24-stable
 
 Name:           nbdkit
-Version:        1.16.2
-Release:        4%{?dist}
+Version:        1.24.0
+Release:        3%{?dist}
 Summary:        NBD server
 
 License:        BSD
 URL:            https://github.com/libguestfs/nbdkit
 
+%if 0%{?rhel} >= 8
+# On RHEL 8+, we cannot build the package on i686 (no virt stack).
+ExcludeArch:    i686
+%endif
+
 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
@@ -40,76 +58,86 @@ Source1:        http://libguestfs.org/download/nbdkit/%{source_directory}/%{name
 Source2:        libguestfs.keyring
 %endif
 
+# Maintainer script which helps with handling patches.
+Source3:        copy-patches.sh
+
 # Patches come from this upstream branch:
-# https://github.com/libguestfs/nbdkit/tree/rhel-8.2
+# https://github.com/libguestfs/nbdkit/tree/rhel-8.6
 
 # 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
-Patch0013:     0013-vddk-Update-for-VDDK-7.0-RHBZ-1831969.patch
-Patch0014:     0014-common-include-Add-ASCII-only-ctype-header-and-ascii.patch
-Patch0015:     0015-curl-Remove-some-useless-debug-messages.patch
-Patch0016:     0016-curl-Fix-D-curl.verbose-1-option.patch
-Patch0017:     0017-curl-Case-insensitive-check-for-accept-ranges-RHBZ-1.patch
-Patch0018:     0018-sh-Don-t-need-to-cast-parameter-of-ascii_is-to-unsig.patch
-Patch0019:     0019-common-include-Add-locale-safe-ascii_strcasecmp-and-.patch
+Patch0001:     0001-cache-cow-Fix-data-corruption-in-zero-and-trim-on-un.patch
+Patch0002:     0002-server-CVE-2021-3716-reset-structured-replies-on-sta.patch
+Patch0003:     0003-server-reset-meta-context-replies-on-starttls.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
+BuildRequires:  autoconf, automake, libtool
 %endif
 
 %ifnarch %{complete_test_arches}
 BuildRequires:  autoconf, automake, libtool
 %endif
+BuildRequires:  gcc, gcc-c++
 BuildRequires:  /usr/bin/pod2man
 BuildRequires:  gnutls-devel
 BuildRequires:  libselinux-devel
-%if 0%{?have_libguestfs}
+%if !0%{?rhel} && 0%{?have_libguestfs}
 BuildRequires:  libguestfs-devel
 %endif
-%if 0%{?have_libvirt}
 BuildRequires:  libvirt-devel
-%endif
 BuildRequires:  xz-devel
 BuildRequires:  zlib-devel
+BuildRequires:  libzstd-devel
 BuildRequires:  libcurl-devel
+BuildRequires:  libnbd-devel >= 1.3.11
 BuildRequires:  libssh-devel
 BuildRequires:  e2fsprogs, e2fsprogs-devel
+%if !0%{?rhel}
+BuildRequires:  genisoimage
+BuildRequires:  rb_libtorrent-devel
+%endif
 BuildRequires:  bash-completion
 BuildRequires:  perl-devel
 BuildRequires:  perl(ExtUtils::Embed)
+%if !0%{?rhel}
 BuildRequires:  python3-devel
+%else
+BuildRequires:  platform-python-devel
+%endif
+%if !0%{?rhel}
+%if 0%{?have_ocaml}
+# Requires OCaml 4.02.2 which contains fix for
+# http://caml.inria.fr/mantis/view.php?id=6693
+BuildRequires:  ocaml >= 4.02.2
+BuildRequires:  ocaml-ocamldoc
+%endif
+BuildRequires:  ruby-devel
+BuildRequires:  tcl-devel
+BuildRequires:  lua-devel
+%endif
 %if 0%{verify_tarball_signature}
 BuildRequires:  gnupg2
 %endif
 
 # Only for running the test suite:
-BuildRequires:  /usr/bin/certtool
+BuildRequires:  %{_bindir}/bc
+BuildRequires:  %{_bindir}/certtool
+BuildRequires:  %{_bindir}/cut
+BuildRequires:  expect
+BuildRequires:  %{_bindir}/hexdump
+BuildRequires:  %{_sbindir}/ip
 BuildRequires:  jq
-BuildRequires:  /usr/bin/nbdsh
-BuildRequires:  /usr/bin/qemu-img
-BuildRequires:  /usr/bin/socat
-BuildRequires:  /usr/sbin/ss
-BuildRequires:  /usr/bin/ssh-keygen
+BuildRequires:  %{_bindir}/nbdcopy
+BuildRequires:  %{_bindir}/nbdinfo
+BuildRequires:  %{_bindir}/nbdsh
+BuildRequires:  %{_bindir}/qemu-img
+BuildRequires:  %{_bindir}/qemu-io
+BuildRequires:  %{_bindir}/qemu-nbd
+BuildRequires:  %{_sbindir}/sfdisk
+BuildRequires:  %{_bindir}/socat
+BuildRequires:  %{_sbindir}/ss
+BuildRequires:  %{_bindir}/stat
+BuildRequires:  %{_bindir}/ssh-keygen
 
 # nbdkit is a metapackage pulling the server and a useful subset
 # of the plugins and filters.
@@ -140,8 +168,8 @@ The key features are:
 
 * 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.
+'%{name}' is a meta-package which pulls in the core server and a
+useful subset of plugins and filters with minimal dependencies.
 
 If you want just the server, install '%{name}-server'.
 
@@ -153,8 +181,6 @@ reading the nbdkit(1) and nbdkit-plugin(3) manual pages.
 Summary:        The %{name} server
 License:        BSD
 
-Conflicts:      nbdkit < 1.12
-
 
 %description server
 This package contains the %{name} server with no plugins or filters.
@@ -166,52 +192,62 @@ License:        BSD
 
 Requires:       %{name}-server%{?_isa} = %{version}-%{release}
 Provides:       %{name}-data-plugin = %{version}-%{release}
+Provides:       %{name}-eval-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}-ondemand-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}-sparse-random-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.
+This package contains plugins for %{name} which only depend on simple
+C libraries: glibc, gnutls, libzstd.  Other plugins for nbdkit with
+more complex dependencies are packaged separately.
+
+nbdkit-data-plugin          Serve small amounts of data from the command line.
+
+nbdkit-eval-plugin          Write a shell script plugin on the command line.
 
-nbdkit-data-plugin         Serve small amounts of data from the command line.
+nbdkit-file-plugin          The normal file plugin for serving files.
 
-nbdkit-file-plugin         The normal file plugin for serving files.
+nbdkit-floppy-plugin        Create a virtual floppy disk from a directory.
 
-nbdkit-floppy-plugin       Create a virtual floppy disk from a directory.
+nbdkit-full-plugin          A virtual disk that returns ENOSPC errors.
 
-nbdkit-full-plugin         A virtual disk that returns ENOSPC errors.
+nbdkit-info-plugin          Serve client and server information.
 
-nbdkit-info-plugin         Serve client and server information.
+nbdkit-memory-plugin        A virtual memory plugin.
 
-nbdkit-memory-plugin       A virtual memory plugin.
+nbdkit-null-plugin          A null (bitbucket) plugin.
 
-nbdkit-null-plugin         A null (bitbucket) plugin.
+nbdkit-ondemand-plugin      Create filesystems on demand.
 
-nbdkit-pattern-plugin      Fixed test pattern.
+nbdkit-pattern-plugin       Fixed test pattern.
 
-nbdkit-partitioning-plugin Create virtual disks from partitions.
+nbdkit-partitioning-plugin  Create virtual disks from partitions.
 
-nbdkit-random-plugin       Random content plugin for testing.
+nbdkit-random-plugin        Random content plugin for testing.
 
-nbdkit-sh-plugin           Write plugins as shell scripts or executables.
+nbdkit-sh-plugin            Write plugins as shell scripts or executables.
 
-nbdkit-split-plugin        Concatenate one or more files.
+nbdkit-sparse-random-plugin Make sparse random disks.
 
-nbdkit-streaming-plugin    A streaming file serving plugin.
+nbdkit-split-plugin         Concatenate one or more files.
 
-nbdkit-zero-plugin         Zero-length plugin for testing.
+nbdkit-streaming-plugin     A streaming file serving plugin.
+
+nbdkit-zero-plugin          Zero-length plugin for testing.
 
 
 %package example-plugins
@@ -219,6 +255,10 @@ Summary:        Example plugins for %{name}
 License:        BSD
 
 Requires:       %{name}-server%{?_isa} = %{version}-%{release}
+%if !0%{?rhel}
+# example4 is written in Perl.
+Requires:       %{name}-perl-plugin
+%endif
 
 
 %description example-plugins
@@ -228,6 +268,38 @@ This package contains example plugins for %{name}.
 # The plugins below have non-trivial dependencies are so are
 # packaged separately.
 
+%if !0%{?rhel}
+%package cc-plugin
+Summary:        Write small inline C plugins and scripts for %{name}
+License:        BSD
+
+Requires:       %{name}-server%{?_isa} = %{version}-%{release}
+Requires:       gcc
+Requires:       %{_bindir}/cat
+
+
+%description cc-plugin
+This package contains support for writing inline C plugins and scripts
+for %{name}.  NOTE this is NOT the right package for writing plugins
+in C, install %{name}-devel for that.
+%endif
+
+
+%if !0%{?rhel}
+%package cdi-plugin
+Summary:        Containerized Data Import plugin for %{name}
+License:        BSD
+
+Requires:       %{name}-server%{?_isa} = %{version}-%{release}
+Requires:       jq
+Requires:       podman
+
+
+%description cdi-plugin
+This package contains Containerized Data Import support for %{name}.
+%endif
+
+
 %package curl-plugin
 Summary:        HTTP/FTP (cURL) plugin for %{name}
 License:        BSD
@@ -239,19 +311,59 @@ Requires:       %{name}-server%{?_isa} = %{version}-%{release}
 This package contains cURL (HTTP/FTP) support for %{name}.
 
 
-%package gzip-plugin
-Summary:        GZip file serving plugin for %{name}
+%if !0%{?rhel} && 0%{?have_libguestfs}
+%package guestfs-plugin
+Summary:        libguestfs 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 guestfs-plugin
+This package is a libguestfs plugin for %{name}.
+%endif
+
+
+%if 0%{?rhel} == 8
+%package gzip-plugin
+Summary:        GZip plugin for %{name}
+License:        BSD
 
 Requires:       %{name}-server%{?_isa} = %{version}-%{release}
 
 
 %description gzip-plugin
-This package is a gzip file serving plugin for %{name}.
+This package is a gzip plugin for %{name}.
+%endif
+
+
+%if !0%{?rhel}
+%package iso-plugin
+Summary:        Virtual ISO 9660 plugin for %{name}
+License:        BSD
+
+Requires:       %{name}-server%{?_isa} = %{version}-%{release}
+Requires:       genisoimage
+
+
+%description iso-plugin
+This package is a virtual ISO 9660 (CD-ROM) plugin for %{name}.
+%endif
+
+
+%if !0%{?rhel}
+%package libvirt-plugin
+Summary:        Libvirt plugin for %{name}
+License:        BSD
+
+Requires:       %{name}-server%{?_isa} = %{version}-%{release}
+
+
+%description libvirt-plugin
+This package is a libvirt plugin for %{name}.  It lets you access
+libvirt guest disks readonly.  It is implemented using the libvirt
+virDomainBlockPeek API.
+%endif
 
 
 %package linuxdisk-plugin
@@ -267,16 +379,76 @@ Requires:       e2fsprogs
 This package is a virtual Linux disk plugin for %{name}.
 
 
+%if !0%{?rhel}
+%package lua-plugin
+Summary:        Lua plugin for %{name}
+License:        BSD
+
+Requires:       %{name}-server%{?_isa} = %{version}-%{release}
+
+
+%description lua-plugin
+This package lets you write Lua plugins for %{name}.
+%endif
+
+
+%package nbd-plugin
+Summary:        NBD proxy / forward plugin for %{name}
+License:        BSD
+
+Requires:       %{name}-server%{?_isa} = %{version}-%{release}
+
+
+%description nbd-plugin
+This package lets you forward NBD connections from %{name}
+to another NBD server.
+
+
+%if !0%{?rhel} && 0%{?have_ocaml}
+%package ocaml-plugin
+Summary:        OCaml plugin for %{name}
+License:        BSD
+
+Requires:       %{name}-server%{?_isa} = %{version}-%{release}
+
+
+%description ocaml-plugin
+This package lets you run OCaml plugins for %{name}.
+
+To compile OCaml plugins you will also need to install
+%{name}-ocaml-plugin-devel.
+
+
+%package ocaml-plugin-devel
+Summary:        OCaml development environment for %{name}
+License:        BSD
+
+Requires:       %{name}-server%{?_isa} = %{version}-%{release}
+Requires:       %{name}-ocaml-plugin%{?_isa} = %{version}-%{release}
+
+
+%description ocaml-plugin-devel
+This package lets you write OCaml plugins for %{name}.
+%endif
+
+
+%if !0%{?rhel}
+%package perl-plugin
+Summary:        Perl plugin for %{name}
+License:        BSD
+
+Requires:       %{name}-server%{?_isa} = %{version}-%{release}
+
+
+%description perl-plugin
+This package lets you write Perl plugins for %{name}.
+%endif
+
+
 %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}
 
 
@@ -284,6 +456,19 @@ Requires:       %{name}-server%{?_isa} = %{version}-%{release}
 This package lets you write Python 3 plugins for %{name}.
 
 
+%if !0%{?rhel}
+%package ruby-plugin
+Summary:        Ruby plugin for %{name}
+License:        BSD
+
+Requires:       %{name}-server%{?_isa} = %{version}-%{release}
+
+
+%description ruby-plugin
+This package lets you write Ruby plugins for %{name}.
+%endif
+
+
 %package ssh-plugin
 Summary:        SSH plugin for %{name}
 License:        BSD
@@ -295,15 +480,67 @@ Requires:       %{name}-server%{?_isa} = %{version}-%{release}
 This package contains SSH support for %{name}.
 
 
-%ifarch %{ix86} x86_64
+%package tar-plugin
+Summary:        Tar archive plugin for %{name}
+License:        BSD
+
+Requires:       %{name}-server%{?_isa} = %{version}-%{release}
+Requires:       tar
+
+
+%description tar-plugin
+This package is a tar archive plugin for %{name}.
+
+
+%if !0%{?rhel}
+%package tcl-plugin
+Summary:        Tcl plugin for %{name}
+License:        BSD
+
+Requires:       %{name}-server%{?_isa} = %{version}-%{release}
+
+
+%description tcl-plugin
+This package lets you write Tcl plugins for %{name}.
+%endif
+
+
+%package tmpdisk-plugin
+Summary:        Remote temporary filesystem disk plugin for %{name}
+License:        BSD
+
+Requires:       %{name}-server%{?_isa} = %{version}-%{release}
+# For mkfs and mke2fs (defaults).
+Requires:       util-linux, e2fsprogs
+# For other filesystems.
+Suggests:       xfsprogs
+%if !0%{?rhel}
+Suggests:       ntfsprogs, dosfstools
+%endif
+
+
+%description tmpdisk-plugin
+This package is a remote temporary filesystem disk plugin for %{name}.
+
+
+%if !0%{?rhel}
+%package torrent-plugin
+Summary:        BitTorrent plugin for %{name}
+License:        BSD
+
+Requires:       %{name}-server%{?_isa} = %{version}-%{release}
+
+
+%description torrent-plugin
+This package is a BitTorrent plugin for %{name}.
+%endif
+
+
+%ifarch 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}
 
 
@@ -321,27 +558,40 @@ 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}-checkwrite-filter = %{version}-%{release}
 Provides:       %{name}-cow-filter = %{version}-%{release}
+Provides:       %{name}-ddrescue-filter = %{version}-%{release}
 Provides:       %{name}-delay-filter = %{version}-%{release}
 Provides:       %{name}-error-filter = %{version}-%{release}
+Provides:       %{name}-exitlast-filter = %{version}-%{release}
+Provides:       %{name}-exitwhen-filter = %{version}-%{release}
+Provides:       %{name}-exportname-filter = %{version}-%{release}
 Provides:       %{name}-extentlist-filter = %{version}-%{release}
 Provides:       %{name}-fua-filter = %{version}-%{release}
+Provides:       %{name}-ip-filter = %{version}-%{release}
+Provides:       %{name}-limit-filter = %{version}-%{release}
 Provides:       %{name}-log-filter = %{version}-%{release}
 Provides:       %{name}-nocache-filter = %{version}-%{release}
 Provides:       %{name}-noextents-filter = %{version}-%{release}
+Provides:       %{name}-nofilter-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}-pause-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}-swab-filter = %{version}-%{release}
+Provides:       %{name}-tls-fallback-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.
+This package contains filters for %{name} which only depend on simple
+C libraries: glibc, gnutls.  Other filters for nbdkit with more
+complex dependencies are packaged separately.
 
 nbdkit-blocksize-filter    Adjust block size of requests sent to plugins.
 
@@ -349,22 +599,38 @@ nbdkit-cache-filter        Server-side cache.
 
 nbdkit-cacheextents-filter Cache extents.
 
+nbdkit-checkwrite-filter   Check writes match contents of plugin.
+
 nbdkit-cow-filter          Copy-on-write overlay for read-only plugins.
 
+nbdkit-ddrescue-filter     Filter for serving from ddrescue dump.
+
 nbdkit-delay-filter        Inject read and write delays.
 
 nbdkit-error-filter        Inject errors.
 
+nbdkit-exitlast-filter     Exit on last client connection.
+
+nbdkit-exitwhen-filter     Exit gracefully when an event occurs.
+
+nbdkit-exportname-filter   Adjust export names between client and plugin.
+
 nbdkit-extentlist-filter   Place extent list over a plugin.
 
 nbdkit-fua-filter          Modify flush behaviour in plugins.
 
+nbdkit-ip-filter           Filter clients by IP address.
+
+nbdkit-limit-filter        Limit nr clients that can connect concurrently.
+
 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-nofilter-filter     Passthrough filter.
+
 nbdkit-noparallel-filter   Serialize requests to the underlying plugin.
 
 nbdkit-nozero-filter       Adjust handling of zero requests by plugins.
@@ -373,6 +639,8 @@ nbdkit-offset-filter       Serve an offset and range.
 
 nbdkit-partition-filter    Serve a single partition.
 
+nbdkit-pause-filter        Pause NBD requests.
+
 nbdkit-rate-filter         Limit bandwidth by connection or server.
 
 nbdkit-readahead-filter    Prefetch data when reading sequentially.
@@ -381,17 +649,58 @@ nbdkit-retry-filter        Reopen connection on error.
 
 nbdkit-stats-filter        Display statistics about operations.
 
+nbdkit-swab-filter         Filter for swapping byte order.
+
+nbdkit-tls-fallback-filter TLS protection filter.
+
 nbdkit-truncate-filter     Truncate, expand, round up or round down size.
 
 
+%if !0%{?rhel}
+%package ext2-filter
+Summary:        ext2, ext3 and ext4 filesystem support for %{name}
+License:        BSD
+
+Requires:       %{name}-server%{?_isa} = %{version}-%{release}
+
+# Remove in Fedora 34:
+Provides:       %{name}-ext2-plugin = %{version}-%{release}
+Obsoletes:      %{name}-ext2-plugin <= %{version}-%{release}
+
+
+%description ext2-filter
+This package contains ext2, ext3 and ext4 filesystem support for
+%{name}.
+%endif
+
+
+%package gzip-filter
+Summary:        GZip filter for %{name}
+License:        BSD
+
+Requires:       %{name}-server%{?_isa} = %{version}-%{release}
+
+
+%description gzip-filter
+This package is a gzip filter for %{name}.
+
+
+%package tar-filter
+Summary:        Tar archive filter for %{name}
+License:        BSD
+
+Requires:       %{name}-server%{?_isa} = %{version}-%{release}
+Requires:       tar
+
+
+%description tar-filter
+This package is a tar archive filter for %{name}.
+
+
 %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}
 
 
@@ -437,35 +746,51 @@ autoreconf -i
 
 %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 's/^LIBGUESTFS_TESTS/xLIBGUESTFS_TESTS/' tests/Makefile.am
 sed -i -e '/^if HAVE_GUESTFISH/,/^endif HAVE_GUESTFISH/d' tests/Makefile.am
 autoreconf -i
 %endif
 
 
 %build
+# Golang bindings are not enabled in the build since they don't
+# need to be.  Most people would use them by copying the upstream
+# package into their vendor/ directory.
+#
+# %%{__python3} expands to platform-python, so we don't depend on
+# the python module (see RHBZ#1659159, RHBZ#1867964).
+export PYTHON=%{__python3}
 %configure \
-    PYTHON=%{_bindir}/python3 \
+    --with-extra='%{name}-%{version}-%{release}' \
     --disable-static \
+    --disable-golang \
+    --disable-rust \
+%if !0%{?rhel} && 0%{?have_ocaml}
+    --enable-ocaml \
+%else
+    --disable-ocaml \
+%endif
+%if 0%{?rhel}
     --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 \
+%endif
+%if !0%{?rhel} && 0%{?have_libguestfs}
+    --with-libguestfs \
+%else
+    --without-libguestfs \
+%endif
     --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}
+%make_build
 
 
 %install
@@ -478,8 +803,25 @@ find $RPM_BUILD_ROOT -name '*.la' -delete
 # rust plugin is built.  Delete it if this happens.
 rm -f $RPM_BUILD_ROOT%{_mandir}/man3/nbdkit-rust-plugin.3*
 
+%if 0%{?rhel} != 8
+# Remove the deprecated gzip plugin (use gzip filter instead).
+rm $RPM_BUILD_ROOT%{_libdir}/%{name}/plugins/nbdkit-gzip-plugin.so
+rm $RPM_BUILD_ROOT%{_mandir}/man1/nbdkit-gzip-plugin.1*
+%endif
+
+%if 0%{?rhel}
+# In RHEL, remove some plugins we cannot --disable.
+for f in cc cdi torrent; do
+    rm -f $RPM_BUILD_ROOT%{_libdir}/%{name}/plugins/nbdkit-$f-plugin.so
+    rm -f $RPM_BUILD_ROOT%{_mandir}/man?/nbdkit-$f-plugin.*
+done
+rm -f $RPM_BUILD_ROOT%{_libdir}/%{name}/plugins/nbdkit-S3-plugin
+rm -f $RPM_BUILD_ROOT%{_mandir}/man1/nbdkit-S3-plugin.1*
+%endif
+
 
 %check
+%ifnarch %{broken_test_arches}
 # Workaround for broken libvirt (RHBZ#1138604).
 mkdir -p $HOME/.cache/libvirt
 
@@ -487,24 +829,33 @@ mkdir -p $HOME/.cache/libvirt
 # 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
 
+# Temporarily kill test-nbd-tls.sh and test-nbd-tls-psk.sh
+# https://www.redhat.com/archives/libguestfs/2020-March/msg00191.html
+truncate -s 0 tests/test-nbd-tls.sh tests/test-nbd-tls-psk.sh
+
+# Kill tests/test-cc-ocaml.sh.  Requires upstream fix (commit bce54e7df25).
+truncate -s 0 tests/test-cc-ocaml.sh
+
 # Make sure we can see the debug messages (RHBZ#1230160).
 export LIBGUESTFS_DEBUG=1
 export LIBGUESTFS_TRACE=1
 
-make %{?_smp_mflags} check || {
+%make_build check || {
     cat tests/test-suite.log
     exit 1
   }
+%endif
+
+
+%if 0%{?have_ocaml}
+%ldconfig_scriptlets plugin-ocaml
+%endif
 
 
 %files
@@ -520,6 +871,7 @@ make %{?_smp_mflags} check || {
 %dir %{_libdir}/%{name}/filters
 %{_mandir}/man1/nbdkit.1*
 %{_mandir}/man1/nbdkit-captive.1*
+%{_mandir}/man1/nbdkit-client.1*
 %{_mandir}/man1/nbdkit-loop.1*
 %{_mandir}/man1/nbdkit-probing.1*
 %{_mandir}/man1/nbdkit-protocol.1*
@@ -532,30 +884,36 @@ make %{?_smp_mflags} check || {
 %doc README
 %license LICENSE
 %{_libdir}/%{name}/plugins/nbdkit-data-plugin.so
+%{_libdir}/%{name}/plugins/nbdkit-eval-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-ondemand-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-sparse-random-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-eval-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-ondemand-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-sparse-random-plugin.1*
 %{_mandir}/man1/nbdkit-split-plugin.1*
 %{_mandir}/man1/nbdkit-streaming-plugin.1*
 %{_mandir}/man1/nbdkit-zero-plugin.1*
@@ -565,9 +923,30 @@ make %{?_smp_mflags} check || {
 %doc README
 %license LICENSE
 %{_libdir}/%{name}/plugins/nbdkit-example*-plugin.so
+%if !0%{?rhel}
+%{_libdir}/%{name}/plugins/nbdkit-example4-plugin
+%endif
 %{_mandir}/man1/nbdkit-example*-plugin.1*
 
 
+%if !0%{?rhel}
+%files cc-plugin
+%doc README
+%license LICENSE
+%{_libdir}/%{name}/plugins/nbdkit-cc-plugin.so
+%{_mandir}/man3/nbdkit-cc-plugin.3*
+%endif
+
+
+%if !0%{?rhel}
+%files cdi-plugin
+%doc README
+%license LICENSE
+%{_libdir}/%{name}/plugins/nbdkit-cdi-plugin.so
+%{_mandir}/man1/nbdkit-cdi-plugin.1*
+%endif
+
+
 %files curl-plugin
 %doc README
 %license LICENSE
@@ -575,11 +954,40 @@ make %{?_smp_mflags} check || {
 %{_mandir}/man1/nbdkit-curl-plugin.1*
 
 
+%if !0%{?rhel} && 0%{?have_libguestfs}
+%files guestfs-plugin
+%doc README
+%license LICENSE
+%{_libdir}/%{name}/plugins/nbdkit-guestfs-plugin.so
+%{_mandir}/man1/nbdkit-guestfs-plugin.1*
+%endif
+
+
+%if 0%{?rhel} == 8
 %files gzip-plugin
 %doc README
 %license LICENSE
 %{_libdir}/%{name}/plugins/nbdkit-gzip-plugin.so
 %{_mandir}/man1/nbdkit-gzip-plugin.1*
+%endif
+
+
+%if !0%{?rhel}
+%files iso-plugin
+%doc README
+%license LICENSE
+%{_libdir}/%{name}/plugins/nbdkit-iso-plugin.so
+%{_mandir}/man1/nbdkit-iso-plugin.1*
+%endif
+
+
+%if !0%{?rhel}
+%files libvirt-plugin
+%doc README
+%license LICENSE
+%{_libdir}/%{name}/plugins/nbdkit-libvirt-plugin.so
+%{_mandir}/man1/nbdkit-libvirt-plugin.1*
+%endif
 
 
 %files linuxdisk-plugin
@@ -589,6 +997,45 @@ make %{?_smp_mflags} check || {
 %{_mandir}/man1/nbdkit-linuxdisk-plugin.1*
 
 
+%if !0%{?rhel}
+%files lua-plugin
+%doc README
+%license LICENSE
+%{_libdir}/%{name}/plugins/nbdkit-lua-plugin.so
+%{_mandir}/man3/nbdkit-lua-plugin.3*
+%endif
+
+
+%files nbd-plugin
+%doc README
+%license LICENSE
+%{_libdir}/%{name}/plugins/nbdkit-nbd-plugin.so
+%{_mandir}/man1/nbdkit-nbd-plugin.1*
+
+
+%if !0%{?rhel} && 0%{?have_ocaml}
+%files ocaml-plugin
+%doc README
+%license LICENSE
+%{_libdir}/libnbdkitocaml.so.*
+
+%files ocaml-plugin-devel
+%{_libdir}/libnbdkitocaml.so
+%{_libdir}/ocaml/NBDKit.*
+%{_mandir}/man3/nbdkit-ocaml-plugin.3*
+%{_mandir}/man3/NBDKit.3*
+%endif
+
+
+%if !0%{?rhel}
+%files perl-plugin
+%doc README
+%license LICENSE
+%{_libdir}/%{name}/plugins/nbdkit-perl-plugin.so
+%{_mandir}/man3/nbdkit-perl-plugin.3*
+%endif
+
+
 %files python-plugin
 %doc README
 %license LICENSE
@@ -596,6 +1043,15 @@ make %{?_smp_mflags} check || {
 %{_mandir}/man3/nbdkit-python-plugin.3*
 
 
+%if !0%{?rhel}
+%files ruby-plugin
+%doc README
+%license LICENSE
+%{_libdir}/%{name}/plugins/nbdkit-ruby-plugin.so
+%{_mandir}/man3/nbdkit-ruby-plugin.3*
+%endif
+
+
 %files ssh-plugin
 %doc README
 %license LICENSE
@@ -603,7 +1059,39 @@ make %{?_smp_mflags} check || {
 %{_mandir}/man1/nbdkit-ssh-plugin.1*
 
 
-%ifarch %{ix86} x86_64
+%files tar-plugin
+%doc README
+%license LICENSE
+%{_libdir}/%{name}/plugins/nbdkit-tar-plugin.so
+%{_mandir}/man1/nbdkit-tar-plugin.1*
+
+
+%if !0%{?rhel}
+%files tcl-plugin
+%doc README
+%license LICENSE
+%{_libdir}/%{name}/plugins/nbdkit-tcl-plugin.so
+%{_mandir}/man3/nbdkit-tcl-plugin.3*
+%endif
+
+
+%files tmpdisk-plugin
+%doc README
+%license LICENSE
+%{_libdir}/%{name}/plugins/nbdkit-tmpdisk-plugin.so
+%{_mandir}/man1/nbdkit-tmpdisk-plugin.1*
+
+
+%if !0%{?rhel}
+%files torrent-plugin
+%doc README
+%license LICENSE
+%{_libdir}/%{name}/plugins/nbdkit-torrent-plugin.so
+%{_mandir}/man1/nbdkit-torrent-plugin.1*
+%endif
+
+
+%ifarch x86_64
 %files vddk-plugin
 %doc README
 %license LICENSE
@@ -618,45 +1106,90 @@ make %{?_smp_mflags} check || {
 %{_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-checkwrite-filter.so
 %{_libdir}/%{name}/filters/nbdkit-cow-filter.so
+%{_libdir}/%{name}/filters/nbdkit-ddrescue-filter.so
 %{_libdir}/%{name}/filters/nbdkit-delay-filter.so
 %{_libdir}/%{name}/filters/nbdkit-error-filter.so
+%{_libdir}/%{name}/filters/nbdkit-exitlast-filter.so
+%{_libdir}/%{name}/filters/nbdkit-exitwhen-filter.so
+%{_libdir}/%{name}/filters/nbdkit-exportname-filter.so
 %{_libdir}/%{name}/filters/nbdkit-extentlist-filter.so
 %{_libdir}/%{name}/filters/nbdkit-fua-filter.so
+%{_libdir}/%{name}/filters/nbdkit-ip-filter.so
+%{_libdir}/%{name}/filters/nbdkit-limit-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-nofilter-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-pause-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-swab-filter.so
+%{_libdir}/%{name}/filters/nbdkit-tls-fallback-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-checkwrite-filter.1*
 %{_mandir}/man1/nbdkit-cow-filter.1*
+%{_mandir}/man1/nbdkit-ddrescue-filter.1*
 %{_mandir}/man1/nbdkit-delay-filter.1*
 %{_mandir}/man1/nbdkit-error-filter.1*
+%{_mandir}/man1/nbdkit-exitlast-filter.1*
+%{_mandir}/man1/nbdkit-exitwhen-filter.1*
+%{_mandir}/man1/nbdkit-exportname-filter.1*
 %{_mandir}/man1/nbdkit-extentlist-filter.1*
 %{_mandir}/man1/nbdkit-fua-filter.1*
+%{_mandir}/man1/nbdkit-ip-filter.1*
+%{_mandir}/man1/nbdkit-limit-filter.1*
 %{_mandir}/man1/nbdkit-log-filter.1*
 %{_mandir}/man1/nbdkit-nocache-filter.1*
 %{_mandir}/man1/nbdkit-noextents-filter.1*
+%{_mandir}/man1/nbdkit-nofilter-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-pause-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-swab-filter.1*
+%{_mandir}/man1/nbdkit-tls-fallback-filter.1*
 %{_mandir}/man1/nbdkit-truncate-filter.1*
 
 
+%if !0%{?rhel}
+%files ext2-filter
+%doc README
+%license LICENSE
+%{_libdir}/%{name}/filters/nbdkit-ext2-filter.so
+%{_mandir}/man1/nbdkit-ext2-filter.1*
+%endif
+
+
+%files gzip-filter
+%doc README
+%license LICENSE
+%{_libdir}/%{name}/filters/nbdkit-gzip-filter.so
+%{_mandir}/man1/nbdkit-gzip-filter.1*
+
+
+%files tar-filter
+%doc README
+%license LICENSE
+%{_libdir}/%{name}/filters/nbdkit-tar-filter.so
+%{_mandir}/man1/nbdkit-tar-filter.1*
+
+
 %files xz-filter
 %doc README
 %license LICENSE
@@ -669,8 +1202,24 @@ make %{?_smp_mflags} check || {
 %license LICENSE
 # Include the source of the example plugins in the documentation.
 %doc plugins/example*/*.c
-%doc plugins/python/example.py
+%if !0%{?rhel}
+%doc plugins/example4/nbdkit-example4-plugin
+%doc plugins/lua/example.lua
+%endif
+%if !0%{?rhel} && 0%{?have_ocaml}
+%doc plugins/ocaml/example.ml
+%endif
+%if !0%{?rhel}
+%doc plugins/perl/example.pl
+%endif
+%doc plugins/python/examples/*.py
+%if !0%{?rhel}
+%doc plugins/ruby/example.rb
+%endif
 %doc plugins/sh/example.sh
+%if !0%{?rhel}
+%doc plugins/tcl/example.tcl
+%endif
 %{_includedir}/nbdkit-common.h
 %{_includedir}/nbdkit-filter.h
 %{_includedir}/nbdkit-plugin.h
@@ -689,6 +1238,18 @@ make %{?_smp_mflags} check || {
 
 
 %changelog
+* Mon Sep 06 2021 Richard W.M. Jones <rjones@redhat.com> - 1.24.0-3
+- Fix CVE-2021-3716 NBD_OPT_STRUCTURED_REPLY injection on STARTTLS
+  resolves: rhbz#1994915
+
+* Mon Sep 06 2021 Richard W.M. Jones <rjones@redhat.com> - 1.24.0-2
+- Fix data corruption in zero and trim on unaligned tail
+  resolves: rhbz#1990135
+
+* Thu Sep 2 2021 Danilo C. L. de Paula <ddepaula@redhat.com> - 1.24.0-1.el8
+- Resolves: bz#2000225
+  (Rebase virt:rhel module:stream based on AV-8.6)
+
 * Thu Jun 04 2020 Danilo C. L. de Paula <ddepaula@redhat.com> - 1.16.2
 - Resolves: bz#1810193
   (Upgrade components in virt:rhel module:stream for RHEL-8.3 release)