[Libguestfs] [nbdkit PATCH v3 14/16] filters: Adjust API for type-safety and merged next_ops/nxdata

Eric Blake eblake at redhat.com
Fri Mar 5 23:31:18 UTC 2021


As promised in several previous patches, this is a mechanical change
that does two things to every filter: instead of using void* for
callback parameters, we now use an actual opaque type (which helps
avoid errors of passing the wrong pointer around), and instead of
passing identical pointers for next_ops and nxdata, we pass a single
parameter.

The changes in docs and server are manual, but the bulk of the changes
in filters were driven by:

$ cat s.cocci
@rule1@
identifier fn, next_ops, nxdata;
typedef nbdkit_next;
@@
 fn(
- struct nbdkit_next_ops *next_ops, void *nxdata
+ nbdkit_next *next
 ,...)
 {
 ...
 }

@@
identifier rule1.next_ops, rule1.nxdata;
identifier op;
expression list args;
expression exp;
@@
(
-next_ops->op (nxdata, args)
+next->op (next, args)
|
-exp (next_ops, nxdata, args)
+exp (next, args)
)

@@
identifier fn, next, nxdata;
@@
-fn(struct nbdkit_next_open *next, void *nxdata
+fn(struct nbdkit_next_open *next, nbdkit_context *nxdata
 ,...)
 {
 ...
 }

@@
identifier fn, next;
identifier nxdata != handle;
type nbdkit_foo;
@@
 fn(
- nbdkit_foo *next, void *nxdata
+ nbdkit_foo *next, nbdkit_backend *nxdata
 , ...)
 {
 ...
 }
$ spatch --sp-file s.cocci --in-place --no-show-diff \
  filters/ tests/test-layers-filter.c
$ git grep -l 'next->[a-z_]*(' filters/ | \
  xargs sed -i 's/next->\([a-z_]*\)(/next->\1 (/g'

Followed by manual touchups until everything compiled.
---
 docs/nbdkit-filter.pod              | 219 ++++++++++++++--------------
 include/nbdkit-filter.h             |  58 +++-----
 server/extents.c                    |  19 +--
 server/filters.c                    |  42 +++---
 filters/cache/blk.h                 |  16 +-
 filters/cow/blk.h                   |   8 +-
 filters/ext2/io.h                   |   1 -
 filters/partition/partition.h       |   4 +-
 filters/xz/xzfile.h                 |   6 +-
 filters/blocksize/blocksize.c       |  76 +++++-----
 filters/cache/blk.c                 |  16 +-
 filters/cache/cache.c               |  94 ++++++------
 filters/cacheextents/cacheextents.c |  16 +-
 filters/checkwrite/checkwrite.c     |  30 ++--
 filters/cow/blk.c                   |  10 +-
 filters/cow/cow.c                   |  80 +++++-----
 filters/ddrescue/ddrescue.c         |  10 +-
 filters/delay/delay.c               |  30 ++--
 filters/error/error.c               |  26 ++--
 filters/exitwhen/exitwhen.c         |   9 +-
 filters/exportname/exportname.c     |  14 +-
 filters/ext2/ext2.c                 |  45 +++---
 filters/extentlist/extentlist.c     |  11 +-
 filters/fua/fua.c                   |  38 ++---
 filters/gzip/gzip.c                 |  30 ++--
 filters/ip/ip.c                     |   7 +-
 filters/log/log.c                   |  63 ++++----
 filters/nocache/nocache.c           |   6 +-
 filters/noextents/noextents.c       |   2 +-
 filters/noparallel/noparallel.c     |   2 +-
 filters/nozero/nozero.c             |  22 +--
 filters/offset/offset.c             |  33 ++---
 filters/partition/partition-gpt.c   |   7 +-
 filters/partition/partition-mbr.c   |   5 +-
 filters/partition/partition.c       |  52 +++----
 filters/pause/pause.c               |  31 ++--
 filters/rate/rate.c                 |  15 +-
 filters/readahead/readahead.c       |  39 +++--
 filters/retry/retry.c               |  17 +--
 filters/stats/stats.c               |  36 ++---
 filters/swab/swab.c                 |  32 ++--
 filters/tar/tar.c                   |  49 ++++---
 filters/tls-fallback/tls-fallback.c |  50 ++++---
 filters/truncate/truncate.c         |  50 +++----
 filters/xz/xz.c                     |  28 ++--
 filters/xz/xzfile.c                 |  45 +++---
 tests/test-truncate4.sh             |   2 +-
 tests/test-layers-filter.c          | 148 +++++++++----------
 48 files changed, 814 insertions(+), 835 deletions(-)

diff --git a/docs/nbdkit-filter.pod b/docs/nbdkit-filter.pod
index d13ac1d7..a06f4e37 100644
--- a/docs/nbdkit-filter.pod
+++ b/docs/nbdkit-filter.pod
@@ -139,17 +139,19 @@ C<.default_export>, and can also be obtained by using
 C<nbdkit_context_get_backend> on the C<context> parameter to C<.open>.

 Meanwhile, if the filter does not use C<nbdkit_context_set_next>, the
-value of C<nxdata> passed to C<.prepare> has a stable lifetime that
+value of C<next> passed to C<.prepare> has a stable lifetime that
 lasts to the corresponding C<.finalize>, with all intermediate
 functions (such as C<.pread>) receiving the same value for
 convenience.  Functions where C<nxdata> is not reused are C<.config>,
-C<.config_complete>, C<.get_ready>, and C<.after_fork>, which are
-called during initialization outside any connections, and
-C<.preconnect>, C<.list_exports>, C<.default_export>, and C<.open>
-which are called based on client connections but prior to the stable
-lifetime of C<.prepare>.  The value of C<context> passed to C<.open>
-has a lifetime that lasts until the matching C<.close> for use by
-C<nbdkit_context_get_backend>.
+C<.config_complete>, and C<.get_ready>, which are all called during
+initialization outside any connections.  The value of C<backend>
+passed to C<.after_fork> also occurs without connections, but is
+shared with C<.preconnect>, C<.list_exports>, and C<.default_export>,
+and can also be obtained from the C<context> passed to C<.open>, and
+has a lifetime that lasts to C<.unload> for use by
+C<nbdkit_context_next_open>.  In turn, the value of C<context> passed
+to C<.open> has a lifetime that lasts until the matching C<.close> for
+use by C<nbdkit_context_get_backend>.

 =head2 Next config, open and close

@@ -164,19 +166,20 @@ The filter’s C<.close> method is called when an old connection closed,
 and this has no C<next> parameter because it cannot be
 short-circuited.

-=head2 C<next_ops>
+=head2 C<nbdkit_next>

 The filter generally needs to call into the underlying plugin, which
-is done via a pointer to C<struct nbdkit_next_ops>.  The most common
-behavior is to create a next context per connection by calling the
-C<next_open> parameter during C<.open>, at which point the next
-context will be automatically provided to the filter’s other methods
-like C<.prepare>, C<.get_size>, C<.pread> etc.  The C<next_ops> struct
-contains a comparable set of accessors to plugin methods that can be
-called during a connection.  When using automatic registration, the
-C<next_ops> parameter is stable between C<.prepare> and C<.finalize>,
-and nbdkit automatically prepares, finalizes, and closes the next
-context at the right point in the filter connection lifecycle.
+is done via a pointer to C<struct nbdkit_next_ops>, also available as
+the typedef C<nbdkit_next>.  The most common behavior is to create a
+next context per connection by calling the C<next_open> parameter
+during C<.open>, at which point the next context will be automatically
+provided to the filter’s other methods like C<.prepare>, C<.get_size>,
+C<.pread> etc.  The C<nbdkit_next> struct contains a comparable set of
+accessors to plugin methods that can be called during a connection.
+When using automatic registration, the C<next> parameter is stable
+between C<.prepare> and C<.finalize>, and nbdkit automatically
+prepares, finalizes, and closes the next context at the right point in
+the filter connection lifecycle.

 Alternatively, the filter can manage plugin contexts manually, whether
 to multiplex multiple client connections through a single context into
@@ -193,7 +196,7 @@ context, and must reclaim the memory using C<next-E<gt>finalize
 (next)> and C<nbdkit_next_context_close> when done.  A filter using
 manual lifecycle management may use C<nbdkit_context_set_next> to
 associate the next context into the current connection, which lets
-nbdkit then pass that context as the C<next_ops> parameter to future
+nbdkit then pass that context as the C<next> parameter to future
 connection-related functions like C<.pread> and take over lifecycle
 responsibility.

@@ -238,14 +241,14 @@ C<nbdkit_context_set_next> prior to the C<.close> callback.

 This function associates a plugin context with the filter's current
 connection context, given by the C<context> parameter to C<.open>.
-Once associated, this plugin context will be given as the C<next_ops>
+Once associated, this plugin context will be given as the C<next>
 parameter to all other connection-specific callbacks.  If associated
 during C<.open>, nbdkit will take care of preparing the context prior
 to C<.prepare>; if still associated before C<.finalize>, nbdkit will
 take care of finalizing the context, and also for closing it.  A
 filter may also pass C<NULL> for C<next>, to remove any association;
 if no plugin context is associated with the connection, then filter
-callbacks such as C<.pread> will receive C<NULL> for their C<next_ops>
+callbacks such as C<.pread> will receive C<NULL> for their C<next>
 parameter.

 This function returns the previous context that had been associated
@@ -254,21 +257,29 @@ this result will be C<NULL> if there was no previous association.  The
 filter assumes manual responsibility for any remaining lifecycle
 functions that must be called on the returned context.

-=head2 Using C<next_ops>
+=head2 Using C<nbdkit_next>

 Regardless of whether the plugin context is managed automatically or
 manually, it is possible for a filter to issue (for example) extra
-C<next_ops-E<gt>pread> calls in response to a single C<.pwrite> call.
+C<next-E<gt>pread> calls in response to a single C<.pwrite> call.
+
+The C<next> parameter serves two purposes: it serves as the struct
+to access the pointers to all the plugin connection functions, and it
+serves as the opaque data that must be passed as the first parameter
+to those functions.  For example, calling the plugin's can_flush
+functionality would be done via
+
+ next-E<gt>can_flush (next)

 Note that the semantics of the functions in C<struct nbdkit_next_ops>
 are slightly different from what a plugin implements: for example,
 when a plugin's C<.pread> returns -1 on error, the error value to
 advertise to the client is implicit (via the plugin calling
 C<nbdkit_set_error> or setting C<errno>), whereas
-C<next_ops-E<gt>pread> exposes this via an explicit parameter,
+C<next-E<gt>pread> exposes this via an explicit parameter,
 allowing a filter to learn or modify this error if desired.

-Use of C<next_ops-E<gt>prepare> and C<next_ops-E<gt>finalize> is only
+Use of C<next-E<gt>prepare> and C<next-E<gt>finalize> is only
 needed when manually managing the plugin context lifetime.

 =head2 Other considerations
@@ -284,7 +295,7 @@ Note that if your filter registers a callback but in that callback it
 doesn't call the C<next> function then the corresponding method in the
 plugin will never be called.  In particular, your C<.open> method, if
 you have one, B<must> call the C<next> method if you want the
-underlying plugin to be available to all further C<next_ops> use.
+underlying plugin to be available to all further C<nbdkit_next> use.

 =head1 CALLBACKS

@@ -412,7 +423,7 @@ an error message and return C<-1>.

 =head2 C<.after_fork>

- int (*after_fork) (nbdkit_next_after_fork *next, void *nxdata);
+ int (*after_fork) (nbdkit_next_after_fork *next, nbdkit_backend *nxdata);

 This intercepts the plugin C<.after_fork> method and can be used by
 the filter to start background threads (although these have limited
@@ -423,7 +434,7 @@ an error message and return C<-1>.

 =head2 C<.preconnect>

- int (*preconnect) (nbdkit_next_preconnect *next, void *nxdata,
+ int (*preconnect) (nbdkit_next_preconnect *next, nbdkit_backend *nxdata,
                     int readonly);

 This intercepts the plugin C<.preconnect> method and can be used to
@@ -434,7 +445,7 @@ an error message and return C<-1>.

 =head2 C<.list_exports>

- int (*list_exports) (nbdkit_next_list_exports *next, void *nxdata,
+ int (*list_exports) (nbdkit_next_list_exports *next, nbdkit_backend *nxdata,
                       int readonly, int is_tls,
                       struct nbdkit_exports *exports);

@@ -510,7 +521,8 @@ Returns a copy of the C<i>'th export.

 =head2 C<.default_export>

- const char *default_export (nbdkit_next_default_export *next, void *nxdata,
+ const char *default_export (nbdkit_next_default_export *next,
+                             nbdkit_backend *nxdata,
                              int readonly, int is_tls)

 This intercepts the plugin C<.default_export> method and can be used to
@@ -563,11 +575,11 @@ C<nbdkit_is_tls>.
 The filter should generally call C<next> as its first step, to
 allocate from the plugin outwards, so that C<.close> running from the
 outer filter to the plugin will be in reverse.  Skipping a call to
-C<next> is acceptable if the filter will not access C<next_ops> during
-any of the remaining callbacks reached on the same connection.  The
-C<next> function is provided for convenience; the same functionality
-can be obtained manually (other than error checking) by using the
-following:
+C<next> is acceptable if the filter will not access C<nbdkit_next>
+during any of the remaining callbacks reached on the same connection.
+The C<next> function is provided for convenience; the same
+functionality can be obtained manually (other than error checking) by
+using the following:

  nbdkit_context_set_next (context, nbdkit_next_context_open
     (nbdkit_context_get_backend (context), readonly, exportname));
@@ -594,10 +606,8 @@ cannot report failures or access the underlying plugin.

 =head2 C<.finalize>

-  int (*prepare) (struct nbdkit_next_ops *next_ops, void *nxdata,
-                  void *handle, int readonly);
-  int (*finalize) (struct nbdkit_next_ops *next_ops, void *nxdata,
-                   void *handle);
+  int (*prepare) (nbdkit_next *next, void *handle, int readonly);
+  int (*finalize) (nbdkit_next *next, void *handle);

 These two methods can be used to perform any necessary operations just
 after opening the connection (C<.prepare>) or just before closing the
@@ -605,7 +615,7 @@ connection (C<.finalize>).

 For example if you need to scan the underlying disk to check for a
 partition table, you could do it in your C<.prepare> method (calling
-the plugin's C<.get_size> and C<.pread> methods via C<next_ops>).  Or
+the plugin's C<.get_size> and C<.pread> methods via C<next>).  Or
 if you need to cleanly update superblock data in the image on close
 you can do it in your C<.finalize> method (calling the plugin's
 C<.pwrite> method).  Doing these things in the filter's C<.open> or
@@ -616,18 +626,18 @@ For C<.prepare>, the value of C<readonly> is the same as was passed to
 C<.open>, declaring how this filter will be used.

 Note that nbdkit performs sanity checking on requests made to the
-underlying plugin; for example, C<next_ops-E<gt>pread> cannot be
-called on a given connection unless C<next_ops-E<gt>get_size> has
+underlying plugin; for example, C<next-E<gt>pread> cannot be
+called on a given connection unless C<next-E<gt>get_size> has
 first been called at least once in the same connection (to ensure the
-read requests are in bounds), and C<next_ops-E<gt>pwrite> further
-requires an earlier successful call to C<next_ops-E<gt>can_write>.  In
+read requests are in bounds), and C<next-E<gt>pwrite> further
+requires an earlier successful call to C<next-E<gt>can_write>.  In
 many filters, these prerequisites will be automatically called during
 the client negotiation phase, but there are cases where a filter
 overrides query functions or makes I/O calls into the plugin before
 handshaking is complete, where the filter needs to make those
 prerequisite calls manually during C<.prepare>.

-While there are C<next_ops-E<gt>prepare> and C<next_ops-E<gt>finalize>
+While there are C<next-E<gt>prepare> and C<next-E<gt>finalize>
 functions, these are different from other filter methods, in that any
 plugin context associated with the current connection (via the C<next>
 parameter to C<.open>, or via C<nbdkit_context_set_next>, is prepared
@@ -648,8 +658,7 @@ in C<.finalize> forces the client to disconnect.

 =head2 C<.get_size>

- int64_t (*get_size) (struct nbdkit_next_ops *next_ops, void *nxdata,
-                      void *handle);
+ int64_t (*get_size) (nbdkit_next *next, void *handle);

 This intercepts the plugin C<.get_size> method and can be used to read
 or modify the apparent size of the block device that the NBD client
@@ -658,13 +667,12 @@ will see.
 The returned size must be E<ge> 0.  If there is an error, C<.get_size>
 should call C<nbdkit_error> with an error message and return C<-1>.
 This function is only called once per connection and cached by nbdkit.
-Similarly, repeated calls to C<next_ops-E<gt>get_size> will return a
+Similarly, repeated calls to C<next-E<gt>get_size> will return a
 cached value.

 =head2 C<.export_description>

- const char *export_description (struct nbdkit_next_ops *next_ops,
-                                 void *nxdata, void *handle);
+ const char *export_description (nbdkit_next *next, void *handle);

 This intercepts the plugin C<.export_description> method and can be
 used to read or modify the export description that the NBD client
@@ -690,27 +698,16 @@ will see.

 =head2 C<.can_cache>

- int (*can_write) (struct nbdkit_next_ops *next_ops, void *nxdata,
-                   void *handle);
- int (*can_flush) (struct nbdkit_next_ops *next_ops, void *nxdata,
-                   void *handle);
- int (*is_rotational) (struct nbdkit_next_ops *next_ops,
-                       void *nxdata,
-                       void *handle);
- int (*can_trim) (struct nbdkit_next_ops *next_ops, void *nxdata,
-                  void *handle);
- int (*can_zero) (struct nbdkit_next_ops *next_ops, void *nxdata,
-                  void *handle);
- int (*can_fast_zero) (struct nbdkit_next_ops *next_ops, void *nxdata,
-                       void *handle);
- int (*can_extents) (struct nbdkit_next_ops *next_ops, void *nxdata,
-                     void *handle);
- int (*can_fua) (struct nbdkit_next_ops *next_ops, void *nxdata,
-                 void *handle);
- int (*can_multi_conn) (struct nbdkit_next_ops *next_ops, void *nxdata,
-                        void *handle);
- int (*can_cache) (struct nbdkit_next_ops *next_ops, void *nxdata,
-                   void *handle);
+ int (*can_write) (nbdkit_next *next, void *handle);
+ int (*can_flush) (nbdkit_next *next, void *handle);
+ int (*is_rotational) (nbdkit_next *next, void *handle);
+ int (*can_trim) (nbdkit_next *next, void *handle);
+ int (*can_zero) (nbdkit_next *next, void *handle);
+ int (*can_fast_zero) (nbdkit_next *next, void *handle);
+ int (*can_extents) (nbdkit_next *next, void *handle);
+ int (*can_fua) (nbdkit_next *next, void *handle);
+ int (*can_multi_conn) (nbdkit_next *next, void *handle);
+ int (*can_cache) (nbdkit_next *next, void *handle);

 These intercept the corresponding plugin methods, and control feature
 bits advertised to the client.
@@ -730,7 +727,7 @@ be done from filters, not plugins).

 Inform nbdkit that write zeroes should immediately fall back to
 C<.pwrite> emulation without trying C<.zero> (this value is returned
-by C<next_ops-E<gt>can_zero> if the plugin returned false in its
+by C<next-E<gt>can_zero> if the plugin returned false in its
 C<.can_zero>).

 =item C<NBDKIT_ZERO_NATIVE>
@@ -738,7 +735,7 @@ C<.can_zero>).
 Inform nbdkit that write zeroes should attempt to use C<.zero>,
 although it may still fall back to C<.pwrite> emulation for C<ENOTSUP>
 or C<EOPNOTSUPP> failures (this value is returned by
-C<next_ops-E<gt>can_zero> if the plugin returned true in its
+C<next-E<gt>can_zero> if the plugin returned true in its
 C<.can_zero>).

 =back
@@ -749,25 +746,25 @@ C<.can_cache> have three success values.

 The difference between C<.can_fua> values may affect choices made in
 the filter: when splitting a write request that requested FUA from the
-client, if C<next_ops-E<gt>can_fua> returns C<NBDKIT_FUA_NATIVE>, then
+client, if C<next-E<gt>can_fua> returns C<NBDKIT_FUA_NATIVE>, then
 the filter should pass the FUA flag on to each sub-request; while if
 it is known that FUA is emulated by a flush because of a return of
 C<NBDKIT_FUA_EMULATE>, it is more efficient to only flush once after
 all sub-requests have completed (often by passing C<NBDKIT_FLAG_FUA>
 on to only the final sub-request, or by dropping the flag and ending
-with a direct call to C<next_ops-E<gt>flush>).
+with a direct call to C<next-E<gt>flush>).

 If there is an error, the callback should call C<nbdkit_error> with an
 error message and return C<-1>.  These functions are called at most
 once per connection and cached by nbdkit. Similarly, repeated calls to
-any of the C<next_ops> counterparts will return a cached value; by
+any of the C<nbdkit_next> counterparts will return a cached value; by
 calling into the plugin during C<.prepare>, you can ensure that later
 use of the cached values during data commands like <.pwrite> will not
 fail.

 =head2 C<.pread>

- int (*pread) (struct nbdkit_next_ops *next_ops, void *nxdata,
+ int (*pread) (nbdkit_next *next,
                void *handle, void *buf, uint32_t count, uint64_t offset,
                uint32_t flags, int *err);

@@ -776,7 +773,7 @@ modify data read by the plugin.

 The parameter C<flags> exists in case of future NBD protocol
 extensions; at this time, it will be 0 on input, and the filter should
-not pass any flags to C<next_ops-E<gt>pread>.
+not pass any flags to C<next-E<gt>pread>.

 If there is an error (including a short read which couldn't be
 recovered from), C<.pread> should call C<nbdkit_error> with an error
@@ -785,7 +782,7 @@ to return to the client.

 =head2 C<.pwrite>

- int (*pwrite) (struct nbdkit_next_ops *next_ops, void *nxdata,
+ int (*pwrite) (nbdkit_next *next,
                 void *handle,
                 const void *buf, uint32_t count, uint64_t offset,
                 uint32_t flags, int *err);
@@ -794,13 +791,13 @@ This intercepts the plugin C<.pwrite> method and can be used to modify
 data written by the plugin.

 This function will not be called if C<.can_write> returned false; in
-turn, the filter should not call C<next_ops-E<gt>pwrite> if
-C<next_ops-E<gt>can_write> did not return true.
+turn, the filter should not call C<next-E<gt>pwrite> if
+C<next-E<gt>can_write> did not return true.

 The parameter C<flags> may include C<NBDKIT_FLAG_FUA> on input based
 on the result of C<.can_fua>.  In turn, the filter should only pass
-C<NBDKIT_FLAG_FUA> on to C<next_ops-E<gt>pwrite> if
-C<next_ops-E<gt>can_fua> returned a positive value.
+C<NBDKIT_FLAG_FUA> on to C<next-E<gt>pwrite> if
+C<next-E<gt>can_fua> returned a positive value.

 If there is an error (including a short write which couldn't be
 recovered from), C<.pwrite> should call C<nbdkit_error> with an error
@@ -809,19 +806,19 @@ to return to the client.

 =head2 C<.flush>

- int (*flush) (struct nbdkit_next_ops *next_ops, void *nxdata,
+ int (*flush) (nbdkit_next *next,
                void *handle, uint32_t flags, int *err);

 This intercepts the plugin C<.flush> method and can be used to modify
 flush requests.

 This function will not be called if C<.can_flush> returned false; in
-turn, the filter should not call C<next_ops-E<gt>flush> if
-C<next_ops-E<gt>can_flush> did not return true.
+turn, the filter should not call C<next-E<gt>flush> if
+C<next-E<gt>can_flush> did not return true.

 The parameter C<flags> exists in case of future NBD protocol
 extensions; at this time, it will be 0 on input, and the filter should
-not pass any flags to C<next_ops-E<gt>flush>.
+not pass any flags to C<next-E<gt>flush>.

 If there is an error, C<.flush> should call C<nbdkit_error> with an
 error message B<and> return -1 with C<err> set to the positive errno
@@ -829,7 +826,7 @@ value to return to the client.

 =head2 C<.trim>

- int (*trim) (struct nbdkit_next_ops *next_ops, void *nxdata,
+ int (*trim) (nbdkit_next *next,
               void *handle, uint32_t count, uint64_t offset,
               uint32_t flags, int *err);

@@ -837,13 +834,13 @@ This intercepts the plugin C<.trim> method and can be used to modify
 trim requests.

 This function will not be called if C<.can_trim> returned false; in
-turn, the filter should not call C<next_ops-E<gt>trim> if
-C<next_ops-E<gt>can_trim> did not return true.
+turn, the filter should not call C<next-E<gt>trim> if
+C<next-E<gt>can_trim> did not return true.

 The parameter C<flags> may include C<NBDKIT_FLAG_FUA> on input based
 on the result of C<.can_fua>.  In turn, the filter should only pass
-C<NBDKIT_FLAG_FUA> on to C<next_ops-E<gt>trim> if
-C<next_ops-E<gt>can_fua> returned a positive value.
+C<NBDKIT_FLAG_FUA> on to C<next-E<gt>trim> if
+C<next-E<gt>can_fua> returned a positive value.

 If there is an error, C<.trim> should call C<nbdkit_error> with an
 error message B<and> return -1 with C<err> set to the positive errno
@@ -851,7 +848,7 @@ value to return to the client.

 =head2 C<.zero>

- int (*zero) (struct nbdkit_next_ops *next_ops, void *nxdata,
+ int (*zero) (nbdkit_next *next,
               void *handle, uint32_t count, uint64_t offset, uint32_t flags,
               int *err);

@@ -860,7 +857,7 @@ zero requests.

 This function will not be called if C<.can_zero> returned
 C<NBDKIT_ZERO_NONE>; in turn, the filter should not call
-C<next_ops-E<gt>zero> if C<next_ops-E<gt>can_zero> returned
+C<next-E<gt>zero> if C<next-E<gt>can_zero> returned
 C<NBDKIT_ZERO_NONE>.

 On input, the parameter C<flags> may include C<NBDKIT_FLAG_MAY_TRIM>
@@ -869,12 +866,12 @@ C<.can_fua>, and C<NBDKIT_FLAG_FAST_ZERO> based on the result of
 C<.can_fast_zero>.  In turn, the filter may pass
 C<NBDKIT_FLAG_MAY_TRIM> unconditionally, but should only pass
 C<NBDKIT_FLAG_FUA> or C<NBDKIT_FLAG_FAST_ZERO> on to
-C<next_ops-E<gt>zero> if the corresponding C<next_ops-E<gt>can_fua> or
-C<next_ops-E<gt>can_fast_zero> returned a positive value.
+C<next-E<gt>zero> if the corresponding C<next-E<gt>can_fua> or
+C<next-E<gt>can_fast_zero> returned a positive value.

 Note that unlike the plugin C<.zero> which is permitted to fail with
 C<ENOTSUP> or C<EOPNOTSUPP> to force a fallback to C<.pwrite>, the
-function C<next_ops-E<gt>zero> will not fail with C<err> set to
+function C<next-E<gt>zero> will not fail with C<err> set to
 C<ENOTSUP> or C<EOPNOTSUPP> unless C<NBDKIT_FLAG_FAST_ZERO> was used,
 because otherwise the fallback has already taken place.

@@ -887,7 +884,7 @@ C<.pwrite>, filters do not).

 =head2 C<.extents>

- int (*extents) (struct nbdkit_next_ops *next_ops, void *nxdata,
+ int (*extents) (nbdkit_next *next,
                  void *handle, uint32_t count, uint64_t offset, uint32_t flags,
                  struct nbdkit_extents *extents,
                  int *err);
@@ -896,8 +893,8 @@ This intercepts the plugin C<.extents> method and can be used to
 modify extent requests.

 This function will not be called if C<.can_extents> returned false; in
-turn, the filter should not call C<next_ops-E<gt>extents> if
-C<next_ops-E<gt>can_extents> did not return true.
+turn, the filter should not call C<next-E<gt>extents> if
+C<next-E<gt>can_extents> did not return true.

 It is possible for filters to transform the extents list received back
 from the layer below.  Without error checking it would look like this:
@@ -909,9 +906,9 @@ from the layer below.  Without error checking it would look like this:
    struct nbdkit_extent e;
    int64_t size;

-   size = next_ops->get_size (nxdata);
+   size = next->get_size (next);
    extents2 = nbdkit_extents_new (offset + shift, size);
-   next_ops->extents (nxdata, count, offset + shift, flags, extents2, err);
+   next->extents (next, count, offset + shift, flags, extents2, err);
    for (i = 0; i < nbdkit_extents_count (extents2); ++i) {
      e = nbdkit_get_extent (extents2, i);
      e.offset -= shift;
@@ -972,8 +969,7 @@ more requests to the underlying plugin until we have a full set of
 extents covering the region C<[offset..offset+count-1]>.

  struct nbdkit_extents *nbdkit_extents_full (
-                             struct nbdkit_next_ops *next_ops,
-                             void *nxdata,
+                             nbdkit_next *next,
                              uint32_t count, uint64_t offset,
                              uint32_t flags, int *err);

@@ -992,13 +988,12 @@ be set to a suitable value.
 A convenience function is provided to filters only which makes it
 easier to ensure that the client only encounters aligned extents.

- int nbdkit_extents_aligned (struct nbdkit_next_ops *next_ops,
-                             void *nxdata,
+ int nbdkit_extents_aligned (nbdkit_next *next,
                              uint32_t count, uint64_t offset,
                              uint32_t flags, uint32_t align,
                              struct nbdkit_extents *extents, int *err);

-Calls next_ops->extents as needed until at least C<align> bytes are
+Calls C<next-E<gt>extents> as needed until at least C<align> bytes are
 obtained, where C<align> is a power of 2.  Anywhere the underlying
 plugin returns differing extents within C<align> bytes, this function
 treats that portion of the disk as a single extent with zero and
@@ -1008,7 +1003,7 @@ C<offset> that is not already aligned.

 =head2 C<.cache>

- int (*cache) (struct nbdkit_next_ops *next_ops, void *nxdata,
+ int (*cache) (nbdkit_next *next,
                void *handle, uint32_t count, uint64_t offset,
                uint32_t flags, int *err);

@@ -1017,12 +1012,12 @@ cache requests.

 This function will not be called if C<.can_cache> returned
 C<NBDKIT_CACHE_NONE> or C<NBDKIT_CACHE_EMULATE>; in turn, the filter
-should not call C<next_ops-E<gt>cache> unless
-C<next_ops-E<gt>can_cache> returned C<NBDKIT_CACHE_NATIVE>.
+should not call C<next-E<gt>cache> unless
+C<next-E<gt>can_cache> returned C<NBDKIT_CACHE_NATIVE>.

 The parameter C<flags> exists in case of future NBD protocol
 extensions; at this time, it will be 0 on input, and the filter should
-not pass any flags to C<next_ops-E<gt>cache>.
+not pass any flags to C<next-E<gt>cache>.

 If there is an error, C<.cache> should call C<nbdkit_error> with an
 error message B<and> return -1 with C<err> set to the positive errno
diff --git a/include/nbdkit-filter.h b/include/nbdkit-filter.h
index a8819c9b..62c9d1a8 100644
--- a/include/nbdkit-filter.h
+++ b/include/nbdkit-filter.h
@@ -56,14 +56,10 @@ extern "C" {
 typedef struct backend nbdkit_backend;
 typedef struct context nbdkit_context;
 typedef struct context nbdkit_next;
-#elif defined NBDKIT_TYPESAFE /* Temporary define while converting filters */
+#else
 typedef struct nbdkit_backend nbdkit_backend;
 typedef struct nbdkit_context nbdkit_context;
 typedef struct nbdkit_next_ops nbdkit_next;
-#else
-typedef void nbdkit_backend;
-typedef void nbdkit_context;
-typedef void nbdkit_next;
 #endif

 /* Next ops. */
@@ -134,13 +130,11 @@ NBDKIT_EXTERN_DECL (size_t, nbdkit_extents_count,
 NBDKIT_EXTERN_DECL (struct nbdkit_extent, nbdkit_get_extent,
                     (const struct nbdkit_extents *, size_t));
 NBDKIT_EXTERN_DECL (struct nbdkit_extents *, nbdkit_extents_full,
-                    (struct nbdkit_next_ops *next_ops,
-                     nbdkit_next *nxdata,
+                    (nbdkit_next *next,
                      uint32_t count, uint64_t offset,
                      uint32_t flags, int *err));
 NBDKIT_EXTERN_DECL (int, nbdkit_extents_aligned,
-                    (struct nbdkit_next_ops *next_ops,
-                     nbdkit_next *nxdata,
+                    (nbdkit_next *next,
                      uint32_t count, uint64_t offset,
                      uint32_t flags, uint32_t align,
                      struct nbdkit_extents *extents, int *err));
@@ -213,56 +207,52 @@ struct nbdkit_filter {
                   int readonly, const char *exportname, int is_tls);
   void (*close) (void *handle);

-  int (*prepare) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
+  int (*prepare) (nbdkit_next *next,
                   void *handle, int readonly);
-  int (*finalize) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
+  int (*finalize) (nbdkit_next *next,
                    void *handle);

-  int64_t (*get_size) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
+  int64_t (*get_size) (nbdkit_next *next,
                        void *handle);
-  const char * (*export_description) (struct nbdkit_next_ops *next_ops,
-                                      nbdkit_next *nxdata, void *handle);
+  const char * (*export_description) (nbdkit_next *next, void *handle);

-  int (*can_write) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
+  int (*can_write) (nbdkit_next *next,
                     void *handle);
-  int (*can_flush) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
+  int (*can_flush) (nbdkit_next *next,
                     void *handle);
-  int (*is_rotational) (struct nbdkit_next_ops *next_ops,
-                        nbdkit_next *nxdata, void *handle);
-  int (*can_trim) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
+  int (*is_rotational) (nbdkit_next *next, void *handle);
+  int (*can_trim) (nbdkit_next *next,
                    void *handle);
-  int (*can_zero) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
+  int (*can_zero) (nbdkit_next *next,
                    void *handle);
-  int (*can_fast_zero) (struct nbdkit_next_ops *next_ops,
-                        nbdkit_next *nxdata, void *handle);
-  int (*can_extents) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
+  int (*can_fast_zero) (nbdkit_next *next, void *handle);
+  int (*can_extents) (nbdkit_next *next,
                       void *handle);
-  int (*can_fua) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
+  int (*can_fua) (nbdkit_next *next,
                   void *handle);
-  int (*can_multi_conn) (struct nbdkit_next_ops *next_ops,
-                         nbdkit_next *nxdata, void *handle);
-  int (*can_cache) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
+  int (*can_multi_conn) (nbdkit_next *next, void *handle);
+  int (*can_cache) (nbdkit_next *next,
                     void *handle);

-  int (*pread) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
+  int (*pread) (nbdkit_next *next,
                 void *handle, void *buf, uint32_t count, uint64_t offset,
                 uint32_t flags, int *err);
-  int (*pwrite) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
+  int (*pwrite) (nbdkit_next *next,
                  void *handle,
                  const void *buf, uint32_t count, uint64_t offset,
                  uint32_t flags, int *err);
-  int (*flush) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
+  int (*flush) (nbdkit_next *next,
                 void *handle, uint32_t flags, int *err);
-  int (*trim) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
+  int (*trim) (nbdkit_next *next,
                void *handle, uint32_t count, uint64_t offset, uint32_t flags,
                int *err);
-  int (*zero) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
+  int (*zero) (nbdkit_next *next,
                void *handle, uint32_t count, uint64_t offset, uint32_t flags,
                int *err);
-  int (*extents) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
+  int (*extents) (nbdkit_next *next,
                   void *handle, uint32_t count, uint64_t offset, uint32_t flags,
                   struct nbdkit_extents *extents, int *err);
-  int (*cache) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
+  int (*cache) (nbdkit_next *next,
                 void *handle, uint32_t count, uint64_t offset, uint32_t flags,
                 int *err);
 };
diff --git a/server/extents.c b/server/extents.c
index 9699fe6a..4b323595 100644
--- a/server/extents.c
+++ b/server/extents.c
@@ -212,19 +212,19 @@ nbdkit_add_extent (struct nbdkit_extents *exts,

 /* Compute aligned extents on behalf of a filter. */
 int
-nbdkit_extents_aligned (struct nbdkit_next_ops *next_ops,
-                        nbdkit_next *nxdata,
+nbdkit_extents_aligned (struct context *next_c,
                         uint32_t count, uint64_t offset,
                         uint32_t flags, uint32_t align,
                         struct nbdkit_extents *exts, int *err)
 {
+  struct nbdkit_next_ops *next = &next_c->next;
   size_t i;
   struct nbdkit_extent *e, *e2;

   assert (IS_ALIGNED(count | offset, align));

   /* Perform an initial query, then scan for the first unaligned extent. */
-  if (next_ops->extents (nxdata, count, offset, flags, exts, err) == -1)
+  if (next->extents (next_c, count, offset, flags, exts, err) == -1)
     return -1;
   for (i = 0; i < exts->extents.size; ++i) {
     e = &exts->extents.ptr[i];
@@ -267,10 +267,10 @@ nbdkit_extents_aligned (struct nbdkit_next_ops *next_ops,
             *err = errno;
             return -1;
           }
-          if (next_ops->extents (nxdata, align - e->length,
-                                 offset + e->length,
-                                 flags & ~NBDKIT_FLAG_REQ_ONE,
-                                 extents2, err) == -1)
+          if (next->extents (next_c, align - e->length,
+                             offset + e->length,
+                             flags & ~NBDKIT_FLAG_REQ_ONE,
+                             extents2, err) == -1)
             return -1;
           e2 = &extents2->extents.ptr[0];
           assert (e2->offset == e->offset + e->length);
@@ -298,10 +298,11 @@ nbdkit_extents_aligned (struct nbdkit_next_ops *next_ops,
  * covering the region [offset..offset+count-1].
  */
 struct nbdkit_extents *
-nbdkit_extents_full (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
+nbdkit_extents_full (struct context *next_c,
                      uint32_t count, uint64_t offset, uint32_t flags,
                      int *err)
 {
+  struct nbdkit_next_ops *next = &next_c->next;
   struct nbdkit_extents *ret;

   /* Clear REQ_ONE to ask the plugin for as much information as it is
@@ -321,7 +322,7 @@ nbdkit_extents_full (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
       = nbdkit_extents_new (offset, offset+count);
     if (t == NULL) goto error1;

-    if (next_ops->extents (nxdata, count, offset, flags, t, err) == -1)
+    if (next->extents (next_c, count, offset, flags, t, err) == -1)
       goto error0;

     for (i = 0; i < nbdkit_extents_count (t); ++i) {
diff --git a/server/filters.c b/server/filters.c
index d40c928a..41fa20e0 100644
--- a/server/filters.c
+++ b/server/filters.c
@@ -314,7 +314,7 @@ filter_prepare (struct context *c, int readonly)
   struct context *c_next = c->c_next;

   if (f->filter.prepare &&
-      f->filter.prepare (&c_next->next, c_next, c->handle, readonly) == -1)
+      f->filter.prepare (c_next, c->handle, readonly) == -1)
     return -1;

   return 0;
@@ -328,7 +328,7 @@ filter_finalize (struct context *c)
   struct context *c_next = c->c_next;

   if (f->filter.finalize &&
-      f->filter.finalize (&c_next->next, c_next, c->handle) == -1)
+      f->filter.finalize (c_next, c->handle) == -1)
     return -1;
   return 0;
 }
@@ -341,7 +341,7 @@ filter_export_description (struct context *c)
   struct context *c_next = c->c_next;

   if (f->filter.export_description)
-    return f->filter.export_description (&c_next->next, c_next, c->handle);
+    return f->filter.export_description (c_next, c->handle);
   else
     return backend_export_description (c_next);
 }
@@ -354,7 +354,7 @@ filter_get_size (struct context *c)
   struct context *c_next = c->c_next;

   if (f->filter.get_size)
-    return f->filter.get_size (&c_next->next, c_next, c->handle);
+    return f->filter.get_size (c_next, c->handle);
   else
     return backend_get_size (c_next);
 }
@@ -367,7 +367,7 @@ filter_can_write (struct context *c)
   struct context *c_next = c->c_next;

   if (f->filter.can_write)
-    return f->filter.can_write (&c_next->next, c_next, c->handle);
+    return f->filter.can_write (c_next, c->handle);
   else
     return backend_can_write (c_next);
 }
@@ -380,7 +380,7 @@ filter_can_flush (struct context *c)
   struct context *c_next = c->c_next;

   if (f->filter.can_flush)
-    return f->filter.can_flush (&c_next->next, c_next, c->handle);
+    return f->filter.can_flush (c_next, c->handle);
   else
     return backend_can_flush (c_next);
 }
@@ -393,7 +393,7 @@ filter_is_rotational (struct context *c)
   struct context *c_next = c->c_next;

   if (f->filter.is_rotational)
-    return f->filter.is_rotational (&c_next->next, c_next, c->handle);
+    return f->filter.is_rotational (c_next, c->handle);
   else
     return backend_is_rotational (c_next);
 }
@@ -406,7 +406,7 @@ filter_can_trim (struct context *c)
   struct context *c_next = c->c_next;

   if (f->filter.can_trim)
-    return f->filter.can_trim (&c_next->next, c_next, c->handle);
+    return f->filter.can_trim (c_next, c->handle);
   else
     return backend_can_trim (c_next);
 }
@@ -419,7 +419,7 @@ filter_can_zero (struct context *c)
   struct context *c_next = c->c_next;

   if (f->filter.can_zero)
-    return f->filter.can_zero (&c_next->next, c_next, c->handle);
+    return f->filter.can_zero (c_next, c->handle);
   else
     return backend_can_zero (c_next);
 }
@@ -432,7 +432,7 @@ filter_can_fast_zero (struct context *c)
   struct context *c_next = c->c_next;

   if (f->filter.can_fast_zero)
-    return f->filter.can_fast_zero (&c_next->next, c_next, c->handle);
+    return f->filter.can_fast_zero (c_next, c->handle);
   else
     return backend_can_fast_zero (c_next);
 }
@@ -445,7 +445,7 @@ filter_can_extents (struct context *c)
   struct context *c_next = c->c_next;

   if (f->filter.can_extents)
-    return f->filter.can_extents (&c_next->next, c_next, c->handle);
+    return f->filter.can_extents (c_next, c->handle);
   else
     return backend_can_extents (c_next);
 }
@@ -458,7 +458,7 @@ filter_can_fua (struct context *c)
   struct context *c_next = c->c_next;

   if (f->filter.can_fua)
-    return f->filter.can_fua (&c_next->next, c_next, c->handle);
+    return f->filter.can_fua (c_next, c->handle);
   else
     return backend_can_fua (c_next);
 }
@@ -471,7 +471,7 @@ filter_can_multi_conn (struct context *c)
   struct context *c_next = c->c_next;

   if (f->filter.can_multi_conn)
-    return f->filter.can_multi_conn (&c_next->next, c_next, c->handle);
+    return f->filter.can_multi_conn (c_next, c->handle);
   else
     return backend_can_multi_conn (c_next);
 }
@@ -484,7 +484,7 @@ filter_can_cache (struct context *c)
   struct context *c_next = c->c_next;

   if (f->filter.can_cache)
-    return f->filter.can_cache (&c_next->next, c_next, c->handle);
+    return f->filter.can_cache (c_next, c->handle);
   else
     return backend_can_cache (c_next);
 }
@@ -499,7 +499,7 @@ filter_pread (struct context *c,
   struct context *c_next = c->c_next;

   if (f->filter.pread)
-    return f->filter.pread (&c_next->next, c_next, c->handle,
+    return f->filter.pread (c_next, c->handle,
                             buf, count, offset, flags, err);
   else
     return backend_pread (c_next, buf, count, offset, flags, err);
@@ -515,7 +515,7 @@ filter_pwrite (struct context *c,
   struct context *c_next = c->c_next;

   if (f->filter.pwrite)
-    return f->filter.pwrite (&c_next->next, c_next, c->handle,
+    return f->filter.pwrite (c_next, c->handle,
                              buf, count, offset, flags, err);
   else
     return backend_pwrite (c_next, buf, count, offset, flags, err);
@@ -530,7 +530,7 @@ filter_flush (struct context *c,
   struct context *c_next = c->c_next;

   if (f->filter.flush)
-    return f->filter.flush (&c_next->next, c_next, c->handle, flags, err);
+    return f->filter.flush (c_next, c->handle, flags, err);
   else
     return backend_flush (c_next, flags, err);
 }
@@ -545,7 +545,7 @@ filter_trim (struct context *c,
   struct context *c_next = c->c_next;

   if (f->filter.trim)
-    return f->filter.trim (&c_next->next, c_next, c->handle, count, offset,
+    return f->filter.trim (c_next, c->handle, count, offset,
                            flags, err);
   else
     return backend_trim (c_next, count, offset, flags, err);
@@ -560,7 +560,7 @@ filter_zero (struct context *c,
   struct context *c_next = c->c_next;

   if (f->filter.zero)
-    return f->filter.zero (&c_next->next, c_next, c->handle,
+    return f->filter.zero (c_next, c->handle,
                            count, offset, flags, err);
   else
     return backend_zero (c_next, count, offset, flags, err);
@@ -576,7 +576,7 @@ filter_extents (struct context *c,
   struct context *c_next = c->c_next;

   if (f->filter.extents)
-    return f->filter.extents (&c_next->next, c_next, c->handle,
+    return f->filter.extents (c_next, c->handle,
                               count, offset, flags,
                               extents, err);
   else
@@ -594,7 +594,7 @@ filter_cache (struct context *c,
   struct context *c_next = c->c_next;

   if (f->filter.cache)
-    return f->filter.cache (&c_next->next, c_next, c->handle,
+    return f->filter.cache (c_next, c->handle,
                             count, offset, flags, err);
   else
     return backend_cache (c_next, count, offset, flags, err);
diff --git a/filters/cache/blk.h b/filters/cache/blk.h
index 84db4cbb..87c753e2 100644
--- a/filters/cache/blk.h
+++ b/filters/cache/blk.h
@@ -51,20 +51,20 @@ extern int blk_set_size (uint64_t new_size);

 /* Read a single block from the cache or plugin. If cache_on_read is set,
  * also ensure it is cached. */
-extern int blk_read (struct nbdkit_next_ops *next_ops, void *nxdata,
+extern int blk_read (nbdkit_next *next,
                      uint64_t blknum, uint8_t *block, int *err)
-  __attribute__((__nonnull__ (1, 4, 5)));
+  __attribute__((__nonnull__ (1, 3, 4)));

 /* If a single block is not cached, copy it from the plugin. */
-extern int blk_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
+extern int blk_cache (nbdkit_next *next,
                       uint64_t blknum, uint8_t *block, int *err)
-  __attribute__((__nonnull__ (1, 4, 5)));
+  __attribute__((__nonnull__ (1, 3, 4)));

 /* Write to the cache and the plugin. */
-extern int blk_writethrough (struct nbdkit_next_ops *next_ops, void *nxdata,
+extern int blk_writethrough (nbdkit_next *next,
                              uint64_t blknum, const uint8_t *block,
                              uint32_t flags, int *err)
-  __attribute__((__nonnull__ (1, 4, 6)));
+  __attribute__((__nonnull__ (1, 3, 5)));

 /* Write a whole block.
  *
@@ -74,10 +74,10 @@ extern int blk_writethrough (struct nbdkit_next_ops *next_ops, void *nxdata,
  *
  * Otherwise it will only write to the cache.
  */
-extern int blk_write (struct nbdkit_next_ops *next_ops, void *nxdata,
+extern int blk_write (nbdkit_next *next,
                       uint64_t blknum, const uint8_t *block,
                       uint32_t flags, int *err)
-  __attribute__((__nonnull__ (1, 4, 6)));
+  __attribute__((__nonnull__ (1, 3, 5)));

 /* Iterates over each dirty block in the cache. */
 typedef int (*block_callback) (uint64_t blknum, void *vp);
diff --git a/filters/cow/blk.h b/filters/cow/blk.h
index be3e764a..e6fd7417 100644
--- a/filters/cow/blk.h
+++ b/filters/cow/blk.h
@@ -51,9 +51,9 @@ extern int blk_set_size (uint64_t new_size);
 extern void blk_status (uint64_t blknum, bool *present, bool *trimmed);

 /* Read a single block from the overlay or plugin. */
-extern int blk_read (struct nbdkit_next_ops *next_ops, void *nxdata,
+extern int blk_read (nbdkit_next *next,
                      uint64_t blknum, uint8_t *block, int *err)
-  __attribute__((__nonnull__ (1, 4, 5)));
+  __attribute__((__nonnull__ (1, 3, 4)));

 /* Cache mode for blocks not already in overlay */
 enum cache_mode {
@@ -64,10 +64,10 @@ enum cache_mode {
 };

 /* Cache a single block from the plugin. */
-extern int blk_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
+extern int blk_cache (nbdkit_next *next,
                       uint64_t blknum, uint8_t *block, enum cache_mode,
                       int *err)
-  __attribute__((__nonnull__ (1, 4, 6)));
+  __attribute__((__nonnull__ (1, 3, 5)));

 /* Write a single block. */
 extern int blk_write (uint64_t blknum, const uint8_t *block, int *err)
diff --git a/filters/ext2/io.h b/filters/ext2/io.h
index 2112e541..845cffdb 100644
--- a/filters/ext2/io.h
+++ b/filters/ext2/io.h
@@ -35,7 +35,6 @@

 #include <ext2_io.h>

-#define NBDKIT_TYPESAFE /* HACK to get type-safe parameters. */
 #include <nbdkit-filter.h>

 #define EXT2_ET_MAGIC_NBDKIT_IO_CHANNEL EXT2_ET_MAGIC_RESERVED_19
diff --git a/filters/partition/partition.h b/filters/partition/partition.h
index ba9d50da..432b8eb4 100644
--- a/filters/partition/partition.h
+++ b/filters/partition/partition.h
@@ -39,10 +39,10 @@

 extern unsigned partnum;

-extern int find_mbr_partition (struct nbdkit_next_ops *next_ops, void *nxdata,
+extern int find_mbr_partition (nbdkit_next *next,
                                int64_t size, uint8_t *mbr,
                                int64_t *offset_r, int64_t *range_r);
-extern int find_gpt_partition (struct nbdkit_next_ops *next_ops, void *nxdata,
+extern int find_gpt_partition (nbdkit_next *next,
                                int64_t size, uint8_t *header_bytes,
                                int64_t *offset_r, int64_t *range_r);

diff --git a/filters/xz/xzfile.h b/filters/xz/xzfile.h
index 79a92668..13deb2a5 100644
--- a/filters/xz/xzfile.h
+++ b/filters/xz/xzfile.h
@@ -40,7 +40,7 @@
 typedef struct xzfile xzfile;

 /* Open (and verify) the named xz file. */
-extern xzfile *xzfile_open (struct nbdkit_next_ops *next_ops, void *nxdata);
+extern xzfile *xzfile_open (nbdkit_next *next);

 /* Close the file and free up all resources. */
 extern void xzfile_close (xzfile *);
@@ -62,8 +62,8 @@ extern uint64_t xzfile_get_size (xzfile *);
  * file are returned in *start and *size.
  */
 extern char *xzfile_read_block (xzfile *xz,
-                                struct nbdkit_next_ops *next_ops,
-                                void *nxdata, uint32_t flags, int *err,
+                                nbdkit_next *next,
+                                uint32_t flags, int *err,
                                 uint64_t offset,
                                 uint64_t *start, uint64_t *size);

diff --git a/filters/blocksize/blocksize.c b/filters/blocksize/blocksize.c
index 61549522..a6fa00cb 100644
--- a/filters/blocksize/blocksize.c
+++ b/filters/blocksize/blocksize.c
@@ -85,7 +85,7 @@ blocksize_parse (const char *name, const char *s, unsigned int *v)

 /* Called for each key=value passed on the command line. */
 static int
-blocksize_config (nbdkit_next_config *next, void *nxdata,
+blocksize_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
                   const char *key, const char *value)
 {

@@ -100,7 +100,8 @@ blocksize_config (nbdkit_next_config *next, void *nxdata,

 /* Check that limits are sane. */
 static int
-blocksize_config_complete (nbdkit_next_config_complete *next, void *nxdata)
+blocksize_config_complete (nbdkit_next_config_complete *next,
+                           nbdkit_backend *nxdata)
 {
   if (minblock) {
     if (minblock & (minblock - 1)) {
@@ -145,10 +146,10 @@ blocksize_config_complete (nbdkit_next_config_complete *next, void *nxdata)

 /* Round size down to avoid issues at end of file. */
 static int64_t
-blocksize_get_size (struct nbdkit_next_ops *next_ops, void *nxdata,
+blocksize_get_size (nbdkit_next *next,
                     void *handle)
 {
-  int64_t size = next_ops->get_size (nxdata);
+  int64_t size = next->get_size (next);

   if (size == -1)
     return -1;
@@ -156,7 +157,7 @@ blocksize_get_size (struct nbdkit_next_ops *next_ops, void *nxdata,
 }

 static int
-blocksize_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
+blocksize_pread (nbdkit_next *next,
                  void *handle, void *b, uint32_t count, uint64_t offs,
                  uint32_t flags, int *err)
 {
@@ -169,8 +170,7 @@ blocksize_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
     ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
     drop = offs & (minblock - 1);
     keep = MIN (minblock - drop, count);
-    if (next_ops->pread (nxdata, bounce, minblock, offs - drop, flags,
-                         err) == -1)
+    if (next->pread (next, bounce, minblock, offs - drop, flags, err) == -1)
       return -1;
     memcpy (buf, bounce + drop, keep);
     buf += keep;
@@ -181,7 +181,7 @@ blocksize_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
   /* Aligned body */
   while (count >= minblock) {
     keep = MIN (maxdata, ROUND_DOWN (count, minblock));
-    if (next_ops->pread (nxdata, buf, keep, offs, flags, err) == -1)
+    if (next->pread (next, buf, keep, offs, flags, err) == -1)
       return -1;
     buf += keep;
     offs += keep;
@@ -191,7 +191,7 @@ blocksize_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
   /* Unaligned tail */
   if (count) {
     ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
-    if (next_ops->pread (nxdata, bounce, minblock, offs, flags, err) == -1)
+    if (next->pread (next, bounce, minblock, offs, flags, err) == -1)
       return -1;
     memcpy (buf, bounce, count);
   }
@@ -200,7 +200,7 @@ blocksize_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
 }

 static int
-blocksize_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
+blocksize_pwrite (nbdkit_next *next,
                   void *handle, const void *b, uint32_t count, uint64_t offs,
                   uint32_t flags, int *err)
 {
@@ -210,7 +210,7 @@ blocksize_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
   bool need_flush = false;

   if ((flags & NBDKIT_FLAG_FUA) &&
-      next_ops->can_fua (nxdata) == NBDKIT_FUA_EMULATE) {
+      next->can_fua (next) == NBDKIT_FUA_EMULATE) {
     flags &= ~NBDKIT_FLAG_FUA;
     need_flush = true;
   }
@@ -220,11 +220,10 @@ blocksize_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
     ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
     drop = offs & (minblock - 1);
     keep = MIN (minblock - drop, count);
-    if (next_ops->pread (nxdata, bounce, minblock, offs - drop, 0, err) == -1)
+    if (next->pread (next, bounce, minblock, offs - drop, 0, err) == -1)
       return -1;
     memcpy (bounce + drop, buf, keep);
-    if (next_ops->pwrite (nxdata, bounce, minblock, offs - drop, flags,
-                          err) == -1)
+    if (next->pwrite (next, bounce, minblock, offs - drop, flags, err) == -1)
       return -1;
     buf += keep;
     offs += keep;
@@ -234,7 +233,7 @@ blocksize_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
   /* Aligned body */
   while (count >= minblock) {
     keep = MIN (maxdata, ROUND_DOWN (count, minblock));
-    if (next_ops->pwrite (nxdata, buf, keep, offs, flags, err) == -1)
+    if (next->pwrite (next, buf, keep, offs, flags, err) == -1)
       return -1;
     buf += keep;
     offs += keep;
@@ -244,20 +243,20 @@ blocksize_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
   /* Unaligned tail */
   if (count) {
     ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
-    if (next_ops->pread (nxdata, bounce, minblock, offs, 0, err) == -1)
+    if (next->pread (next, bounce, minblock, offs, 0, err) == -1)
       return -1;
     memcpy (bounce, buf, count);
-    if (next_ops->pwrite (nxdata, bounce, minblock, offs, flags, err) == -1)
+    if (next->pwrite (next, bounce, minblock, offs, flags, err) == -1)
       return -1;
   }

   if (need_flush)
-    return next_ops->flush (nxdata, 0, err);
+    return next->flush (next, 0, err);
   return 0;
 }

 static int
-blocksize_trim (struct nbdkit_next_ops *next_ops, void *nxdata,
+blocksize_trim (nbdkit_next *next,
                 void *handle, uint32_t count, uint64_t offs, uint32_t flags,
                 int *err)
 {
@@ -265,7 +264,7 @@ blocksize_trim (struct nbdkit_next_ops *next_ops, void *nxdata,
   bool need_flush = false;

   if ((flags & NBDKIT_FLAG_FUA) &&
-      next_ops->can_fua (nxdata) == NBDKIT_FUA_EMULATE) {
+      next->can_fua (next) == NBDKIT_FUA_EMULATE) {
     flags &= ~NBDKIT_FLAG_FUA;
     need_flush = true;
   }
@@ -283,19 +282,19 @@ blocksize_trim (struct nbdkit_next_ops *next_ops, void *nxdata,
   /* Aligned body */
   while (count) {
     keep = MIN (maxlen, count);
-    if (next_ops->trim (nxdata, keep, offs, flags, err) == -1)
+    if (next->trim (next, keep, offs, flags, err) == -1)
       return -1;
     offs += keep;
     count -= keep;
   }

   if (need_flush)
-    return next_ops->flush (nxdata, 0, err);
+    return next->flush (next, 0, err);
   return 0;
 }

 static int
-blocksize_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+blocksize_zero (nbdkit_next *next,
                 void *handle, uint32_t count, uint64_t offs, uint32_t flags,
                 int *err)
 {
@@ -316,7 +315,7 @@ blocksize_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
   }

   if ((flags & NBDKIT_FLAG_FUA) &&
-      next_ops->can_fua (nxdata) == NBDKIT_FUA_EMULATE) {
+      next->can_fua (next) == NBDKIT_FUA_EMULATE) {
     flags &= ~NBDKIT_FLAG_FUA;
     need_flush = true;
   }
@@ -326,11 +325,11 @@ blocksize_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
     ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
     drop = offs & (minblock - 1);
     keep = MIN (minblock - drop, count);
-    if (next_ops->pread (nxdata, bounce, minblock, offs - drop, 0, err) == -1)
+    if (next->pread (next, bounce, minblock, offs - drop, 0, err) == -1)
       return -1;
     memset (bounce + drop, 0, keep);
-    if (next_ops->pwrite (nxdata, bounce, minblock, offs - drop,
-                          flags & ~NBDKIT_FLAG_MAY_TRIM, err) == -1)
+    if (next->pwrite (next, bounce, minblock, offs - drop,
+                      flags & ~NBDKIT_FLAG_MAY_TRIM, err) == -1)
       return -1;
     offs += keep;
     count -= keep;
@@ -339,7 +338,7 @@ blocksize_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
   /* Aligned body */
   while (count >= minblock) {
     keep = MIN (maxlen, ROUND_DOWN (count, minblock));
-    if (next_ops->zero (nxdata, keep, offs, flags, err) == -1)
+    if (next->zero (next, keep, offs, flags, err) == -1)
       return -1;
     offs += keep;
     count -= keep;
@@ -348,21 +347,21 @@ blocksize_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
   /* Unaligned tail */
   if (count) {
     ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
-    if (next_ops->pread (nxdata, bounce, minblock, offs, 0, err) == -1)
+    if (next->pread (next, bounce, minblock, offs, 0, err) == -1)
       return -1;
     memset (bounce, 0, count);
-    if (next_ops->pwrite (nxdata, bounce, minblock, offs,
-                          flags & ~NBDKIT_FLAG_MAY_TRIM, err) == -1)
+    if (next->pwrite (next, bounce, minblock, offs,
+                      flags & ~NBDKIT_FLAG_MAY_TRIM, err) == -1)
       return -1;
   }

   if (need_flush)
-    return next_ops->flush (nxdata, 0, err);
+    return next->flush (next, 0, err);
   return 0;
 }

 static int
-blocksize_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
+blocksize_extents (nbdkit_next *next,
                    void *handle, uint32_t count, uint64_t offset,
                    uint32_t flags, struct nbdkit_extents *extents, int *err)
 {
@@ -383,10 +382,9 @@ blocksize_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
     return -1;
   }

-  if (nbdkit_extents_aligned (next_ops, nxdata,
-                              MIN (ROUND_UP (count, minblock), maxlen),
-                              ROUND_DOWN (offset, minblock),
-                              flags, minblock, extents2, err) == -1)
+  if (nbdkit_extents_aligned (next, MIN (ROUND_UP (count, minblock), maxlen),
+                              ROUND_DOWN (offset, minblock), flags, minblock,
+                              extents2, err) == -1)
     return -1;

   for (i = 0; i < nbdkit_extents_count (extents2); ++i) {
@@ -400,7 +398,7 @@ blocksize_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
 }

 static int
-blocksize_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
+blocksize_cache (nbdkit_next *next,
                  void *handle, uint32_t count, uint64_t offs, uint32_t flags,
                  int *err)
 {
@@ -418,7 +416,7 @@ blocksize_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
   /* Aligned body */
   while (remaining) {
     limit = MIN (maxdata, remaining);
-    if (next_ops->cache (nxdata, limit, offs, flags, err) == -1)
+    if (next->cache (next, limit, offs, flags, err) == -1)
       return -1;
     offs += limit;
     remaining -= limit;
diff --git a/filters/cache/blk.c b/filters/cache/blk.c
index b9af6909..12e8407e 100644
--- a/filters/cache/blk.c
+++ b/filters/cache/blk.c
@@ -191,7 +191,7 @@ blk_set_size (uint64_t new_size)
 }

 int
-blk_read (struct nbdkit_next_ops *next_ops, void *nxdata,
+blk_read (nbdkit_next *next,
           uint64_t blknum, uint8_t *block, int *err)
 {
   off_t offset = blknum * blksize;
@@ -214,7 +214,7 @@ blk_read (struct nbdkit_next_ops *next_ops, void *nxdata,
       n -= tail;
     }

-    if (next_ops->pread (nxdata, block, n, offset, 0, err) == -1)
+    if (next->pread (next, block, n, offset, 0, err) == -1)
       return -1;

     /* Normally we're reading whole blocks, but at the very end of the
@@ -251,7 +251,7 @@ blk_read (struct nbdkit_next_ops *next_ops, void *nxdata,
 }

 int
-blk_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
+blk_cache (nbdkit_next *next,
            uint64_t blknum, uint8_t *block, int *err)
 {
   off_t offset = blknum * blksize;
@@ -275,7 +275,7 @@ blk_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
       n -= tail;
     }

-    if (next_ops->pread (nxdata, block, n, offset, 0, err) == -1)
+    if (next->pread (next, block, n, offset, 0, err) == -1)
       return -1;

     /* Normally we're reading whole blocks, but at the very end of the
@@ -310,7 +310,7 @@ blk_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
 }

 int
-blk_writethrough (struct nbdkit_next_ops *next_ops, void *nxdata,
+blk_writethrough (nbdkit_next *next,
                   uint64_t blknum, const uint8_t *block, uint32_t flags,
                   int *err)
 {
@@ -333,7 +333,7 @@ blk_writethrough (struct nbdkit_next_ops *next_ops, void *nxdata,
     return -1;
   }

-  if (next_ops->pwrite (nxdata, block, n, offset, flags, err) == -1)
+  if (next->pwrite (next, block, n, offset, flags, err) == -1)
     return -1;

   bitmap_set_blk (&bm, blknum, BLOCK_CLEAN);
@@ -343,7 +343,7 @@ blk_writethrough (struct nbdkit_next_ops *next_ops, void *nxdata,
 }

 int
-blk_write (struct nbdkit_next_ops *next_ops, void *nxdata,
+blk_write (nbdkit_next *next,
            uint64_t blknum, const uint8_t *block, uint32_t flags,
            int *err)
 {
@@ -351,7 +351,7 @@ blk_write (struct nbdkit_next_ops *next_ops, void *nxdata,

   if (cache_mode == CACHE_MODE_WRITETHROUGH ||
       (cache_mode == CACHE_MODE_WRITEBACK && (flags & NBDKIT_FLAG_FUA)))
-    return blk_writethrough (next_ops, nxdata, blknum, block, flags, err);
+    return blk_writethrough (next, blknum, block, flags, err);

   offset = blknum * blksize;

diff --git a/filters/cache/cache.c b/filters/cache/cache.c
index cc412e59..499aec68 100644
--- a/filters/cache/cache.c
+++ b/filters/cache/cache.c
@@ -76,7 +76,8 @@ int64_t max_size = -1;
 unsigned hi_thresh = 95, lo_thresh = 80;
 bool cache_on_read = false;

-static int cache_flush (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle, uint32_t flags, int *err);
+static int cache_flush (nbdkit_next *next, void *handle, uint32_t flags,
+                        int *err);

 static void
 cache_load (void)
@@ -92,7 +93,7 @@ cache_unload (void)
 }

 static int
-cache_config (nbdkit_next_config *next, void *nxdata,
+cache_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
               const char *key, const char *value)
 {
   if (strcmp (key, "cache") == 0) {
@@ -187,7 +188,8 @@ cache_config (nbdkit_next_config *next, void *nxdata,
 #endif

 static int
-cache_config_complete (nbdkit_next_config_complete *next, void *nxdata)
+cache_config_complete (nbdkit_next_config_complete *next,
+                       nbdkit_backend *nxdata)
 {
   /* If cache-max-size was set then check the thresholds. */
   if (max_size != -1) {
@@ -203,13 +205,13 @@ cache_config_complete (nbdkit_next_config_complete *next, void *nxdata)

 /* Get the file size, set the cache size. */
 static int64_t
-cache_get_size (struct nbdkit_next_ops *next_ops, void *nxdata,
+cache_get_size (nbdkit_next *next,
                 void *handle)
 {
   int64_t size;
   int r;

-  size = next_ops->get_size (nxdata);
+  size = next->get_size (next);
   if (size == -1)
     return -1;

@@ -228,12 +230,12 @@ cache_get_size (struct nbdkit_next_ops *next_ops, void *nxdata,
  * calls.
  */
 static int
-cache_prepare (struct nbdkit_next_ops *next_ops, void *nxdata,
+cache_prepare (nbdkit_next *next,
                void *handle, int readonly)
 {
   int64_t r;

-  r = cache_get_size (next_ops, nxdata, handle);
+  r = cache_get_size (next, handle);
   if (r < 0)
     return -1;
   return 0;
@@ -241,14 +243,14 @@ cache_prepare (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Override the plugin's .can_cache, because we are caching here instead */
 static int
-cache_can_cache (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle)
+cache_can_cache (nbdkit_next *next, void *handle)
 {
   return NBDKIT_CACHE_NATIVE;
 }

 /* Override the plugin's .can_fast_zero, because our .zero is not fast */
 static int
-cache_can_fast_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+cache_can_fast_zero (nbdkit_next *next,
                      void *handle)
 {
   /* It is better to advertise support even when we always reject fast
@@ -259,28 +261,28 @@ cache_can_fast_zero (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Override the plugin's .can_flush, if we are cache=unsafe */
 static int
-cache_can_flush (struct nbdkit_next_ops *next_ops, void *nxdata,
+cache_can_flush (nbdkit_next *next,
                  void *handle)
 {
   if (cache_mode == CACHE_MODE_UNSAFE)
     return 1;
-  return next_ops->can_flush (nxdata);
+  return next->can_flush (next);
 }


 /* Override the plugin's .can_fua, if we are cache=unsafe */
 static int
-cache_can_fua (struct nbdkit_next_ops *next_ops, void *nxdata,
+cache_can_fua (nbdkit_next *next,
                void *handle)
 {
   if (cache_mode == CACHE_MODE_UNSAFE)
     return NBDKIT_FUA_NATIVE;
-  return next_ops->can_fua (nxdata);
+  return next->can_fua (next);
 }

 /* Override the plugin's .can_multi_conn, if we are not cache=writethrough */
 static int
-cache_can_multi_conn (struct nbdkit_next_ops *next_ops, void *nxdata,
+cache_can_multi_conn (nbdkit_next *next,
                       void *handle)
 {
   /* For CACHE_MODE_UNSAFE, we always advertise a no-op flush because
@@ -301,12 +303,12 @@ cache_can_multi_conn (struct nbdkit_next_ops *next_ops, void *nxdata,
    */
   if (cache_mode != CACHE_MODE_WRITETHROUGH)
     return 1;
-  return next_ops->can_multi_conn (nxdata);
+  return next->can_multi_conn (next);
 }

 /* Read data. */
 static int
-cache_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
+cache_pread (nbdkit_next *next,
              void *handle, void *buf, uint32_t count, uint64_t offset,
              uint32_t flags, int *err)
 {
@@ -333,7 +335,7 @@ cache_pread (struct nbdkit_next_ops *next_ops, void *nxdata,

     assert (block);
     ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
-    r = blk_read (next_ops, nxdata, blknum, block, err);
+    r = blk_read (next, blknum, block, err);
     if (r == -1)
       return -1;

@@ -354,7 +356,7 @@ cache_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
    */
   while (count >= blksize) {
     ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
-    r = blk_read (next_ops, nxdata, blknum, buf, err);
+    r = blk_read (next, blknum, buf, err);
     if (r == -1)
       return -1;

@@ -368,7 +370,7 @@ cache_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
   if (count) {
     assert (block);
     ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
-    r = blk_read (next_ops, nxdata, blknum, block, err);
+    r = blk_read (next, blknum, block, err);
     if (r == -1)
       return -1;

@@ -380,7 +382,7 @@ cache_pread (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Write data. */
 static int
-cache_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
+cache_pwrite (nbdkit_next *next,
               void *handle, const void *buf, uint32_t count, uint64_t offset,
               uint32_t flags, int *err)
 {
@@ -400,7 +402,7 @@ cache_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,

   if ((flags & NBDKIT_FLAG_FUA) &&
       (cache_mode == CACHE_MODE_UNSAFE ||
-       next_ops->can_fua (nxdata) == NBDKIT_FUA_EMULATE)) {
+       next->can_fua (next) == NBDKIT_FUA_EMULATE)) {
     flags &= ~NBDKIT_FLAG_FUA;
     need_flush = true;
   }
@@ -417,10 +419,10 @@ cache_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
      */
     assert (block);
     ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
-    r = blk_read (next_ops, nxdata, blknum, block, err);
+    r = blk_read (next, blknum, block, err);
     if (r != -1) {
       memcpy (&block[blkoffs], buf, n);
-      r = blk_write (next_ops, nxdata, blknum, block, flags, err);
+      r = blk_write (next, blknum, block, flags, err);
     }
     if (r == -1)
       return -1;
@@ -434,7 +436,7 @@ cache_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
   /* Aligned body */
   while (count >= blksize) {
     ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
-    r = blk_write (next_ops, nxdata, blknum, buf, flags, err);
+    r = blk_write (next, blknum, buf, flags, err);
     if (r == -1)
       return -1;

@@ -448,23 +450,23 @@ cache_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
   if (count) {
     assert (block);
     ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
-    r = blk_read (next_ops, nxdata, blknum, block, err);
+    r = blk_read (next, blknum, block, err);
     if (r != -1) {
       memcpy (block, buf, count);
-      r = blk_write (next_ops, nxdata, blknum, block, flags, err);
+      r = blk_write (next, blknum, block, flags, err);
     }
     if (r == -1)
       return -1;
   }

   if (need_flush)
-    return cache_flush (next_ops, nxdata, handle, 0, err);
+    return cache_flush (next, handle, 0, err);
   return 0;
 }

 /* Zero data. */
 static int
-cache_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+cache_zero (nbdkit_next *next,
             void *handle, uint32_t count, uint64_t offset, uint32_t flags,
             int *err)
 {
@@ -473,7 +475,7 @@ cache_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
   int r;
   bool need_flush = false;

-  /* We are purposefully avoiding next_ops->zero, so a zero request is
+  /* We are purposefully avoiding next->zero, so a zero request is
    * never faster than plain writes.
    */
   if (flags & NBDKIT_FLAG_FAST_ZERO) {
@@ -491,7 +493,7 @@ cache_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
   flags &= ~NBDKIT_FLAG_MAY_TRIM;
   if ((flags & NBDKIT_FLAG_FUA) &&
       (cache_mode == CACHE_MODE_UNSAFE ||
-       next_ops->can_fua (nxdata) == NBDKIT_FUA_EMULATE)) {
+       next->can_fua (next) == NBDKIT_FUA_EMULATE)) {
     flags &= ~NBDKIT_FLAG_FUA;
     need_flush = true;
   }
@@ -507,10 +509,10 @@ cache_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
      * Hold the lock over the whole operation.
      */
     ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
-    r = blk_read (next_ops, nxdata, blknum, block, err);
+    r = blk_read (next, blknum, block, err);
     if (r != -1) {
       memset (&block[blkoffs], 0, n);
-      r = blk_write (next_ops, nxdata, blknum, block, flags, err);
+      r = blk_write (next, blknum, block, flags, err);
     }
     if (r == -1)
       return -1;
@@ -524,9 +526,9 @@ cache_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
   if (count >= blksize)
     memset (block, 0, blksize);
   while (count >=blksize) {
-    /* Intentional that we do not use next_ops->zero */
+    /* Intentional that we do not use next->zero */
     ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
-    r = blk_write (next_ops, nxdata, blknum, block, flags, err);
+    r = blk_write (next, blknum, block, flags, err);
     if (r == -1)
       return -1;

@@ -538,17 +540,17 @@ cache_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
   /* Unaligned tail */
   if (count) {
     ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
-    r = blk_read (next_ops, nxdata, blknum, block, err);
+    r = blk_read (next, blknum, block, err);
     if (r != -1) {
       memset (&block[count], 0, blksize - count);
-      r = blk_write (next_ops, nxdata, blknum, block, flags, err);
+      r = blk_write (next, blknum, block, flags, err);
     }
     if (r == -1)
       return -1;
   }

   if (need_flush)
-    return cache_flush (next_ops, nxdata, handle, 0, err);
+    return cache_flush (next, handle, 0, err);
   return 0;
 }

@@ -557,19 +559,18 @@ struct flush_data {
   uint8_t *block;               /* bounce buffer */
   unsigned errors;              /* count of errors seen */
   int first_errno;              /* first errno seen */
-  struct nbdkit_next_ops *next_ops;
-  void *nxdata;
+  nbdkit_next *next;
 };

 static int flush_dirty_block (uint64_t blknum, void *);

 static int
-cache_flush (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle,
+cache_flush (nbdkit_next *next, void *handle,
              uint32_t flags, int *err)
 {
   CLEANUP_FREE uint8_t *block = NULL;
   struct flush_data data =
-    { .errors = 0, .first_errno = 0, .next_ops = next_ops, .nxdata = nxdata };
+    { .errors = 0, .first_errno = 0, .next = next };
   int tmp;

   if (cache_mode == CACHE_MODE_UNSAFE)
@@ -597,8 +598,7 @@ cache_flush (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle,
   }

   /* Now issue a flush request to the underlying storage. */
-  if (next_ops->flush (nxdata, 0,
-                       data.errors ? &tmp : &data.first_errno) == -1)
+  if (next->flush (next, 0, data.errors ? &tmp : &data.first_errno) == -1)
     data.errors++;

   if (data.errors > 0) {
@@ -617,10 +617,10 @@ flush_dirty_block (uint64_t blknum, void *datav)
   /* Perform a read + writethrough which will read from the
    * cache and write it through to the underlying storage.
    */
-  if (blk_read (data->next_ops, data->nxdata, blknum, data->block,
+  if (blk_read (data->next, blknum, data->block,
                 data->errors ? &tmp : &data->first_errno) == -1)
     goto err;
-  if (blk_writethrough (data->next_ops, data->nxdata, blknum, data->block, 0,
+  if (blk_writethrough (data->next, blknum, data->block, 0,
                         data->errors ? &tmp : &data->first_errno) == -1)
     goto err;

@@ -634,7 +634,7 @@ flush_dirty_block (uint64_t blknum, void *datav)

 /* Cache data. */
 static int
-cache_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
+cache_cache (nbdkit_next *next,
              void *handle, uint32_t count, uint64_t offset,
              uint32_t flags, int *err)
 {
@@ -664,7 +664,7 @@ cache_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
   /* Aligned body */
   while (remaining) {
     ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
-    r = blk_cache (next_ops, nxdata, blknum, block, err);
+    r = blk_cache (next, blknum, block, err);
     if (r == -1)
       return -1;

diff --git a/filters/cacheextents/cacheextents.c b/filters/cacheextents/cacheextents.c
index 95b3c00f..82c5919e 100644
--- a/filters/cacheextents/cacheextents.c
+++ b/filters/cacheextents/cacheextents.c
@@ -119,7 +119,7 @@ fill (struct nbdkit_extents *extents, int *err)
 }

 static int
-cacheextents_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
+cacheextents_extents (nbdkit_next *next,
                       void *handle, uint32_t count, uint64_t offset,
                       uint32_t flags,
                       struct nbdkit_extents *extents,
@@ -149,7 +149,7 @@ cacheextents_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
    * costly to provide everything).
    */
   flags &= ~(NBDKIT_FLAG_REQ_ONE);
-  if (next_ops->extents (nxdata, count, offset, flags, extents, err) == -1)
+  if (next->extents (next, count, offset, flags, extents, err) == -1)
     return -1;

   return fill (extents, err);
@@ -170,33 +170,33 @@ kill_cacheextents (void)
 }

 static int
-cacheextents_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
+cacheextents_pwrite (nbdkit_next *next,
                      void *handle,
                      const void *buf, uint32_t count, uint64_t offset,
                      uint32_t flags, int *err)
 {
   kill_cacheextents ();
-  return next_ops->pwrite (nxdata, buf, count, offset, flags, err);
+  return next->pwrite (next, buf, count, offset, flags, err);
 }

 static int
-cacheextents_trim (struct nbdkit_next_ops *next_ops, void *nxdata,
+cacheextents_trim (nbdkit_next *next,
                    void *handle,
                    uint32_t count, uint64_t offset, uint32_t flags,
                    int *err)
 {
   kill_cacheextents ();
-  return next_ops->trim (nxdata, count, offset, flags, err);
+  return next->trim (next, count, offset, flags, err);
 }

 static int
-cacheextents_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+cacheextents_zero (nbdkit_next *next,
                    void *handle,
                    uint32_t count, uint64_t offset, uint32_t flags,
                    int *err)
 {
   kill_cacheextents ();
-  return next_ops->zero (nxdata, count, offset, flags, err);
+  return next->zero (next, count, offset, flags, err);
 }

 static struct nbdkit_filter filter = {
diff --git a/filters/checkwrite/checkwrite.c b/filters/checkwrite/checkwrite.c
index 2dc93ab1..e3eea0a4 100644
--- a/filters/checkwrite/checkwrite.c
+++ b/filters/checkwrite/checkwrite.c
@@ -57,42 +57,42 @@ checkwrite_open (nbdkit_next_open *next, nbdkit_context *nxdata,
  * write-like operations.
  */
 static int
-checkwrite_can_write (struct nbdkit_next_ops *next_ops, void *nxdata,
+checkwrite_can_write (nbdkit_next *next,
                       void *handle)
 {
   return 1;
 }

 static int
-checkwrite_can_flush (struct nbdkit_next_ops *next_ops, void *nxdata,
+checkwrite_can_flush (nbdkit_next *next,
                       void *handle)
 {
   return 1;
 }

 static int
-checkwrite_can_fua (struct nbdkit_next_ops *next_ops, void *nxdata,
+checkwrite_can_fua (nbdkit_next *next,
                     void *handle)
 {
   return NBDKIT_FUA_NATIVE;
 }

 static int
-checkwrite_can_trim (struct nbdkit_next_ops *next_ops, void *nxdata,
+checkwrite_can_trim (nbdkit_next *next,
                      void *handle)
 {
   return 1;
 }

 static int
-checkwrite_can_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+checkwrite_can_zero (nbdkit_next *next,
                      void *handle)
 {
   return NBDKIT_ZERO_NATIVE;
 }

 static int
-checkwrite_can_fast_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+checkwrite_can_fast_zero (nbdkit_next *next,
                           void *handle)
 {
   /* It is better to advertise support, even if we always reject fast
@@ -102,7 +102,7 @@ checkwrite_can_fast_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
 }

 static int
-checkwrite_can_multi_conn (struct nbdkit_next_ops *next_ops, void *nxdata,
+checkwrite_can_multi_conn (nbdkit_next *next,
                            void *handle)
 {
   return 1;
@@ -118,7 +118,7 @@ data_does_not_match (int *err)

 /* Provide write-like operations which perform the additional checks. */
 static int
-checkwrite_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
+checkwrite_pwrite (nbdkit_next *next,
                    void *handle,
                    const void *buf, uint32_t count, uint64_t offset,
                    uint32_t flags, int *err)
@@ -133,7 +133,7 @@ checkwrite_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
   }

   /* Read underlying plugin data into the buffer. */
-  if (next_ops->pread (nxdata, expected, count, offset, 0, err) == -1)
+  if (next->pread (next, expected, count, offset, 0, err) == -1)
     return -1;

   /* If data written doesn't match data expected, inject EIO. */
@@ -144,7 +144,7 @@ checkwrite_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
 }

 static int
-checkwrite_flush (struct nbdkit_next_ops *next_ops, void *nxdata,
+checkwrite_flush (nbdkit_next *next,
                   void *handle, uint32_t flags, int *err)
 {
   /* Does nothing, we just have to support it. */
@@ -161,15 +161,15 @@ checkwrite_flush (struct nbdkit_next_ops *next_ops, void *nxdata,
  * or create a fully allocated target (nbdcopy --allocated).
  */
 static int
-checkwrite_trim_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+checkwrite_trim_zero (nbdkit_next *next,
                       void *handle, uint32_t count, uint64_t offset,
                       uint32_t flags, int *err)
 {
   /* If the plugin supports extents, speed this up by using them. */
-  if (next_ops->can_extents (nxdata)) {
+  if (next->can_extents (next)) {
     size_t i, n;
     CLEANUP_EXTENTS_FREE struct nbdkit_extents *exts =
-      nbdkit_extents_full (next_ops, nxdata, count, offset, 0, err);
+      nbdkit_extents_full (next, count, offset, 0, err);
     if (exts == NULL)
       return -1;

@@ -203,7 +203,7 @@ checkwrite_trim_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
           return -1;
         }

-        if (next_ops->pread (nxdata, buf, buflen, offset, 0, err) == -1)
+        if (next->pread (next, buf, buflen, offset, 0, err) == -1)
           return -1;
         if (! is_zero (buf, buflen))
           return data_does_not_match (err);
@@ -234,7 +234,7 @@ checkwrite_trim_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
     while (count > 0) {
       uint32_t n = MIN (MAX_REQUEST_SIZE, count);

-      if (next_ops->pread (nxdata, buf, n, offset, 0, err) == -1)
+      if (next->pread (next, buf, n, offset, 0, err) == -1)
         return -1;
       if (! is_zero (buf, n))
         return data_does_not_match (err);
diff --git a/filters/cow/blk.c b/filters/cow/blk.c
index cbd40188..7b088e00 100644
--- a/filters/cow/blk.c
+++ b/filters/cow/blk.c
@@ -224,7 +224,7 @@ blk_status (uint64_t blknum, bool *present, bool *trimmed)
  * whole block of size ‘blksize’.
  */
 int
-blk_read (struct nbdkit_next_ops *next_ops, void *nxdata,
+blk_read (nbdkit_next *next,
           uint64_t blknum, uint8_t *block, int *err)
 {
   off_t offset = blknum * BLKSIZE;
@@ -252,7 +252,7 @@ blk_read (struct nbdkit_next_ops *next_ops, void *nxdata,
       n -= tail;
     }

-    if (next_ops->pread (nxdata, block, n, offset, 0, err) == -1)
+    if (next->pread (next, block, n, offset, 0, err) == -1)
       return -1;

     /* Normally we're reading whole blocks, but at the very end of the
@@ -277,7 +277,7 @@ blk_read (struct nbdkit_next_ops *next_ops, void *nxdata,
 }

 int
-blk_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
+blk_cache (nbdkit_next *next,
            uint64_t blknum, uint8_t *block, enum cache_mode mode, int *err)
 {
   /* XXX Could make this lock more fine-grained with some thought. */
@@ -310,9 +310,9 @@ blk_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
   if (mode == BLK_CACHE_IGNORE)
     return 0;
   if (mode == BLK_CACHE_PASSTHROUGH)
-    return next_ops->cache (nxdata, n, offset, 0, err);
+    return next->cache (next, n, offset, 0, err);

-  if (next_ops->pread (nxdata, block, n, offset, 0, err) == -1)
+  if (next->pread (next, block, n, offset, 0, err) == -1)
     return -1;
   /* Normally we're reading whole blocks, but at the very end of the
    * file we might read a partial block.  Deal with that case by
diff --git a/filters/cow/cow.c b/filters/cow/cow.c
index 8027f05c..83844845 100644
--- a/filters/cow/cow.c
+++ b/filters/cow/cow.c
@@ -73,7 +73,7 @@ cow_unload (void)
 }

 static int
-cow_config (nbdkit_next_config *next, void *nxdata,
+cow_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
             const char *key, const char *value)
 {
   if (strcmp (key, "cow-on-cache") == 0) {
@@ -94,7 +94,7 @@ cow_config (nbdkit_next_config *next, void *nxdata,
   "cow-on-cache=<BOOL>  Set to true to treat client cache requests as writes.\n"

 static void *
-cow_open (nbdkit_next_open *next, void *nxdata,
+cow_open (nbdkit_next_open *next, nbdkit_context *nxdata,
           int readonly, const char *exportname, int is_tls)
 {
   /* Always pass readonly=1 to the underlying plugin. */
@@ -106,13 +106,13 @@ cow_open (nbdkit_next_open *next, void *nxdata,

 /* Get the file size, set the cache size. */
 static int64_t
-cow_get_size (struct nbdkit_next_ops *next_ops, void *nxdata,
+cow_get_size (nbdkit_next *next,
               void *handle)
 {
   int64_t size;
   int r;

-  size = next_ops->get_size (nxdata);
+  size = next->get_size (next);
   if (size == -1)
     return -1;

@@ -130,59 +130,59 @@ cow_get_size (struct nbdkit_next_ops *next_ops, void *nxdata,
  * calls.
  */
 static int
-cow_prepare (struct nbdkit_next_ops *next_ops, void *nxdata,
+cow_prepare (nbdkit_next *next,
              void *handle, int readonly)
 {
   int64_t r;

-  r = cow_get_size (next_ops, nxdata, handle);
+  r = cow_get_size (next, handle);
   return r >= 0 ? 0 : -1;
 }

 static int
-cow_can_write (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle)
+cow_can_write (nbdkit_next *next, void *handle)
 {
   return 1;
 }

 static int
-cow_can_trim (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle)
+cow_can_trim (nbdkit_next *next, void *handle)
 {
   return 1;
 }

 static int
-cow_can_extents (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle)
+cow_can_extents (nbdkit_next *next, void *handle)
 {
   return 1;
 }

 static int
-cow_can_flush (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle)
+cow_can_flush (nbdkit_next *next, void *handle)
 {
   return 1;
 }

 static int
-cow_can_fua (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle)
+cow_can_fua (nbdkit_next *next, void *handle)
 {
   return NBDKIT_FUA_NATIVE;
 }

 static int
-cow_can_cache (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle)
+cow_can_cache (nbdkit_next *next, void *handle)
 {
-  /* Cache next_ops->can_cache now, so later calls to next_ops->cache
+  /* Cache next->can_cache now, so later calls to next->cache
    * don't fail, even though we override the answer here.
    */
-  int r = next_ops->can_cache (nxdata);
+  int r = next->can_cache (next);
   if (r == -1)
     return -1;
   return NBDKIT_CACHE_NATIVE;
 }

 static int
-cow_can_multi_conn (struct nbdkit_next_ops *next_ops, void *nxdata,
+cow_can_multi_conn (nbdkit_next *next,
                     void *handle)
 {
   /* Our cache is consistent between connections.  */
@@ -191,7 +191,7 @@ cow_can_multi_conn (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Override the plugin's .can_fast_zero, because our .zero is not fast */
 static int
-cow_can_fast_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+cow_can_fast_zero (nbdkit_next *next,
                    void *handle)
 {
   /* It is better to advertise support even when we always reject fast
@@ -200,11 +200,12 @@ cow_can_fast_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
   return 1;
 }

-static int cow_flush (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle, uint32_t flags, int *err);
+static int cow_flush (nbdkit_next *next, void *handle, uint32_t flags,
+                      int *err);

 /* Read data. */
 static int
-cow_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
+cow_pread (nbdkit_next *next,
            void *handle, void *buf, uint32_t count, uint64_t offset,
            uint32_t flags, int *err)
 {
@@ -229,7 +230,7 @@ cow_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
     uint64_t n = MIN (BLKSIZE - blkoffs, count);

     assert (block);
-    r = blk_read (next_ops, nxdata, blknum, block, err);
+    r = blk_read (next, blknum, block, err);
     if (r == -1)
       return -1;

@@ -249,7 +250,7 @@ cow_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
    * smarter here.
    */
   while (count >= BLKSIZE) {
-    r = blk_read (next_ops, nxdata, blknum, buf, err);
+    r = blk_read (next, blknum, buf, err);
     if (r == -1)
       return -1;

@@ -262,7 +263,7 @@ cow_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
   /* Unaligned tail */
   if (count) {
     assert (block);
-    r = blk_read (next_ops, nxdata, blknum, block, err);
+    r = blk_read (next, blknum, block, err);
     if (r == -1)
       return -1;

@@ -274,7 +275,7 @@ cow_pread (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Write data. */
 static int
-cow_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
+cow_pwrite (nbdkit_next *next,
             void *handle, const void *buf, uint32_t count, uint64_t offset,
             uint32_t flags, int *err)
 {
@@ -303,7 +304,7 @@ cow_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
      */
     assert (block);
     ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&rmw_lock);
-    r = blk_read (next_ops, nxdata, blknum, block, err);
+    r = blk_read (next, blknum, block, err);
     if (r != -1) {
       memcpy (&block[blkoffs], buf, n);
       r = blk_write (blknum, block, err);
@@ -333,7 +334,7 @@ cow_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
   if (count) {
     assert (block);
     ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&rmw_lock);
-    r = blk_read (next_ops, nxdata, blknum, block, err);
+    r = blk_read (next, blknum, block, err);
     if (r != -1) {
       memcpy (block, buf, count);
       r = blk_write (blknum, block, err);
@@ -349,7 +350,7 @@ cow_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Zero data. */
 static int
-cow_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+cow_zero (nbdkit_next *next,
           void *handle, uint32_t count, uint64_t offset, uint32_t flags,
           int *err)
 {
@@ -357,7 +358,7 @@ cow_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
   uint64_t blknum, blkoffs;
   int r;

-  /* We are purposefully avoiding next_ops->zero, so a zero request is
+  /* We are purposefully avoiding next->zero, so a zero request is
    * never faster than plain writes.
    */
   if (flags & NBDKIT_FLAG_FAST_ZERO) {
@@ -383,7 +384,7 @@ cow_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
      * Hold the rmw_lock over the whole operation.
      */
     ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&rmw_lock);
-    r = blk_read (next_ops, nxdata, blknum, block, err);
+    r = blk_read (next, blknum, block, err);
     if (r != -1) {
       memset (&block[blkoffs], 0, n);
       r = blk_write (blknum, block, err);
@@ -415,7 +416,7 @@ cow_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
   /* Unaligned tail */
   if (count) {
     ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&rmw_lock);
-    r = blk_read (next_ops, nxdata, blknum, block, err);
+    r = blk_read (next, blknum, block, err);
     if (r != -1) {
       memset (&block[count], 0, BLKSIZE - count);
       r = blk_write (blknum, block, err);
@@ -431,7 +432,7 @@ cow_zero (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Trim data. */
 static int
-cow_trim (struct nbdkit_next_ops *next_ops, void *nxdata,
+cow_trim (nbdkit_next *next,
           void *handle, uint32_t count, uint64_t offset, uint32_t flags,
           int *err)
 {
@@ -459,7 +460,7 @@ cow_trim (struct nbdkit_next_ops *next_ops, void *nxdata,
      * Hold the lock over the whole operation.
      */
     ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&rmw_lock);
-    r = blk_read (next_ops, nxdata, blknum, block, err);
+    r = blk_read (next, blknum, block, err);
     if (r != -1) {
       memset (&block[blkoffs], 0, n);
       r = blk_write (blknum, block, err);
@@ -486,7 +487,7 @@ cow_trim (struct nbdkit_next_ops *next_ops, void *nxdata,
   /* Unaligned tail */
   if (count) {
     ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&rmw_lock);
-    r = blk_read (next_ops, nxdata, blknum, block, err);
+    r = blk_read (next, blknum, block, err);
     if (r != -1) {
       memset (&block[count], 0, BLKSIZE - count);
       r = blk_write (blknum, block, err);
@@ -501,7 +502,7 @@ cow_trim (struct nbdkit_next_ops *next_ops, void *nxdata,
 }

 static int
-cow_flush (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle,
+cow_flush (nbdkit_next *next, void *handle,
            uint32_t flags, int *err)
 {
   /* Deliberately ignored. */
@@ -509,7 +510,7 @@ cow_flush (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle,
 }

 static int
-cow_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
+cow_cache (nbdkit_next *next,
            void *handle, uint32_t count, uint64_t offset,
            uint32_t flags, int *err)
 {
@@ -519,7 +520,7 @@ cow_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
   uint64_t remaining = count; /* Rounding out could exceed 32 bits */
   enum cache_mode mode;

-  switch (next_ops->can_cache (nxdata)) {
+  switch (next->can_cache (next)) {
   case NBDKIT_CACHE_NONE:
     mode = BLK_CACHE_IGNORE;
     break;
@@ -555,7 +556,7 @@ cow_cache (struct nbdkit_next_ops *next_ops, void *nxdata,

   /* Aligned body */
   while (remaining) {
-    r = blk_cache (next_ops, nxdata, blknum, block, mode, err);
+    r = blk_cache (next, blknum, block, mode, err);
     if (r == -1)
       return -1;

@@ -569,11 +570,11 @@ cow_cache (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Extents. */
 static int
-cow_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
+cow_extents (nbdkit_next *next,
              void *handle, uint32_t count, uint64_t offset, uint32_t flags,
              struct nbdkit_extents *extents, int *err)
 {
-  const bool can_extents = next_ops->can_extents (nxdata);
+  const bool can_extents = next->can_extents (next);
   const bool req_one = flags & NBDKIT_FLAG_REQ_ONE;
   uint64_t end;
   uint64_t blknum;
@@ -638,7 +639,7 @@ cow_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
       }

       /* Don't ask for extent data beyond the end of the plugin. */
-      size = next_ops->get_size (nxdata);
+      size = next->get_size (next);
       if (size == -1)
         return -1;

@@ -648,8 +649,7 @@ cow_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
       }

       CLEANUP_EXTENTS_FREE struct nbdkit_extents *extents2 =
-        nbdkit_extents_full (next_ops, nxdata,
-                             range_count, range_offset, flags, err);
+        nbdkit_extents_full (next, range_count, range_offset, flags, err);
       if (extents2 == NULL)
         return -1;

diff --git a/filters/ddrescue/ddrescue.c b/filters/ddrescue/ddrescue.c
index 53a03f8d..7b1c9c1e 100644
--- a/filters/ddrescue/ddrescue.c
+++ b/filters/ddrescue/ddrescue.c
@@ -137,7 +137,7 @@ ddrescue_unload (void)
 }

 static int
-ddrescue_config (nbdkit_next_config *next, void *nxdata,
+ddrescue_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
                  const char *key, const char *value)
 {
   if (strcmp (key, "ddrescue-mapfile") == 0) {
@@ -159,14 +159,14 @@ ddrescue_config (nbdkit_next_config *next, void *nxdata,
  * below.
  */
 static int
-ddrescue_can_write (struct nbdkit_next_ops *next_ops, void *nxdata,
+ddrescue_can_write (nbdkit_next *next,
                     void *handle)
 {
   return 0;
 }

 static int
-ddrescue_can_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
+ddrescue_can_cache (nbdkit_next *next,
                     void *handle)
 {
   return 0;
@@ -174,7 +174,7 @@ ddrescue_can_cache (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Read data. */
 static int
-ddrescue_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
+ddrescue_pread (nbdkit_next *next,
                 void *handle, void *buf, uint32_t count, uint64_t offset,
                 uint32_t flags, int *err)
 {
@@ -186,7 +186,7 @@ ddrescue_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
     if (offset >= map.ranges.ptr[i].start && offset <= map.ranges.ptr[i].end) {
       if (offset + count - 1 <= map.ranges.ptr[i].end) {
         /* entirely contained within this range */
-        return next_ops->pread (nxdata, buf, count, offset, flags, err);
+        return next->pread (next, buf, count, offset, flags, err);
       }
     }
   }
diff --git a/filters/delay/delay.c b/filters/delay/delay.c
index f440e5a6..7e7fe195 100644
--- a/filters/delay/delay.c
+++ b/filters/delay/delay.c
@@ -130,7 +130,7 @@ cache_delay (int *err)

 /* Called for each key=value passed on the command line. */
 static int
-delay_config (nbdkit_next_config *next, void *nxdata,
+delay_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
               const char *key, const char *value)
 {
   if (strcmp (key, "rdelay") == 0 ||
@@ -208,41 +208,41 @@ delay_config (nbdkit_next_config *next, void *nxdata,

 /* Override the plugin's .can_fast_zero if needed */
 static int
-delay_can_fast_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+delay_can_fast_zero (nbdkit_next *next,
                      void *handle)
 {
   /* Advertise if we are handling fast zero requests locally */
   if (delay_zero_ms && !delay_fast_zero)
     return 1;
-  return next_ops->can_fast_zero (nxdata);
+  return next->can_fast_zero (next);
 }

 /* Read data. */
 static int
-delay_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
+delay_pread (nbdkit_next *next,
              void *handle, void *buf, uint32_t count, uint64_t offset,
              uint32_t flags, int *err)
 {
   if (read_delay (err) == -1)
     return -1;
-  return next_ops->pread (nxdata, buf, count, offset, flags, err);
+  return next->pread (next, buf, count, offset, flags, err);
 }

 /* Write data. */
 static int
-delay_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
+delay_pwrite (nbdkit_next *next,
               void *handle,
               const void *buf, uint32_t count, uint64_t offset, uint32_t flags,
               int *err)
 {
   if (write_delay (err) == -1)
     return -1;
-  return next_ops->pwrite (nxdata, buf, count, offset, flags, err);
+  return next->pwrite (next, buf, count, offset, flags, err);
 }

 /* Zero data. */
 static int
-delay_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+delay_zero (nbdkit_next *next,
             void *handle, uint32_t count, uint64_t offset, uint32_t flags,
             int *err)
 {
@@ -252,40 +252,40 @@ delay_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
   }
   if (zero_delay (err) == -1)
     return -1;
-  return next_ops->zero (nxdata, count, offset, flags, err);
+  return next->zero (next, count, offset, flags, err);
 }

 /* Trim data. */
 static int
-delay_trim (struct nbdkit_next_ops *next_ops, void *nxdata,
+delay_trim (nbdkit_next *next,
             void *handle, uint32_t count, uint64_t offset,
             uint32_t flags, int *err)
 {
   if (trim_delay (err) == -1)
     return -1;
-  return next_ops->trim (nxdata, count, offset, flags, err);
+  return next->trim (next, count, offset, flags, err);
 }

 /* Extents. */
 static int
-delay_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
+delay_extents (nbdkit_next *next,
                void *handle, uint32_t count, uint64_t offset, uint32_t flags,
                struct nbdkit_extents *extents, int *err)
 {
   if (extents_delay (err) == -1)
     return -1;
-  return next_ops->extents (nxdata, count, offset, flags, extents, err);
+  return next->extents (next, count, offset, flags, extents, err);
 }

 /* Cache. */
 static int
-delay_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
+delay_cache (nbdkit_next *next,
              void *handle, uint32_t count, uint64_t offset, uint32_t flags,
              int *err)
 {
   if (cache_delay (err) == -1)
     return -1;
-  return next_ops->cache (nxdata, count, offset, flags, err);
+  return next->cache (next, count, offset, flags, err);
 }

 static struct nbdkit_filter filter = {
diff --git a/filters/error/error.c b/filters/error/error.c
index a7ed3f3b..4b513a81 100644
--- a/filters/error/error.c
+++ b/filters/error/error.c
@@ -155,7 +155,7 @@ parse_error_rate (const char *key, const char *value, double *retp)

 /* Called for each key=value passed on the command line. */
 static int
-error_config (nbdkit_next_config *next, void *nxdata,
+error_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
               const char *key, const char *value)
 {
   int i;
@@ -302,19 +302,19 @@ random_error (const struct error_settings *error_settings,

 /* Read data. */
 static int
-error_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
+error_pread (nbdkit_next *next,
              void *handle, void *buf, uint32_t count, uint64_t offset,
              uint32_t flags, int *err)
 {
   if (random_error (&pread_settings, "pread", err))
     return -1;

-  return next_ops->pread (nxdata, buf, count, offset, flags, err);
+  return next->pread (next, buf, count, offset, flags, err);
 }

 /* Write data. */
 static int
-error_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
+error_pwrite (nbdkit_next *next,
               void *handle,
               const void *buf, uint32_t count, uint64_t offset,
               uint32_t flags, int *err)
@@ -322,55 +322,55 @@ error_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
   if (random_error (&pwrite_settings, "pwrite", err))
     return -1;

-  return next_ops->pwrite (nxdata, buf, count, offset, flags, err);
+  return next->pwrite (next, buf, count, offset, flags, err);
 }

 /* Trim data. */
 static int
-error_trim (struct nbdkit_next_ops *next_ops, void *nxdata,
+error_trim (nbdkit_next *next,
             void *handle, uint32_t count, uint64_t offset,
             uint32_t flags, int *err)
 {
   if (random_error (&trim_settings, "trim", err))
     return -1;

-  return next_ops->trim (nxdata, count, offset, flags, err);
+  return next->trim (next, count, offset, flags, err);
 }

 /* Zero data. */
 static int
-error_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+error_zero (nbdkit_next *next,
             void *handle, uint32_t count, uint64_t offset,
             uint32_t flags, int *err)
 {
   if (random_error (&zero_settings, "zero", err))
     return -1;

-  return next_ops->zero (nxdata, count, offset, flags, err);
+  return next->zero (next, count, offset, flags, err);
 }

 /* Extents. */
 static int
-error_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
+error_extents (nbdkit_next *next,
                void *handle, uint32_t count, uint64_t offset,
                uint32_t flags, struct nbdkit_extents *extents, int *err)
 {
   if (random_error (&extents_settings, "extents", err))
     return -1;

-  return next_ops->extents (nxdata, count, offset, flags, extents, err);
+  return next->extents (next, count, offset, flags, extents, err);
 }

 /* Extents. */
 static int
-error_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
+error_cache (nbdkit_next *next,
              void *handle, uint32_t count, uint64_t offset,
              uint32_t flags, int *err)
 {
   if (random_error (&cache_settings, "cache", err))
     return -1;

-  return next_ops->cache (nxdata, count, offset, flags, err);
+  return next->cache (next, count, offset, flags, err);
 }

 static struct nbdkit_filter filter = {
diff --git a/filters/exitwhen/exitwhen.c b/filters/exitwhen/exitwhen.c
index 67e87dde..80836357 100644
--- a/filters/exitwhen/exitwhen.c
+++ b/filters/exitwhen/exitwhen.c
@@ -352,7 +352,7 @@ resume_polling_thread (void)

 /* Read command line parameters are build events list. */
 static int
-exitwhen_config (nbdkit_next_config *next, void *nxdata,
+exitwhen_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
                  const char *key, const char *value)
 {
   struct event event;
@@ -436,7 +436,7 @@ exitwhen_config (nbdkit_next_config *next, void *nxdata,
  * then we exit immediately.
  */
 static int
-exitwhen_get_ready (nbdkit_next_get_ready *next, void *nxdata,
+exitwhen_get_ready (nbdkit_next_get_ready *next, nbdkit_backend *nxdata,
                     int thread_model)
 {
   ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
@@ -451,7 +451,7 @@ exitwhen_get_ready (nbdkit_next_get_ready *next, void *nxdata,
  * polling.
  */
 static int
-exitwhen_after_fork (nbdkit_next_after_fork *next, void *nxdata)
+exitwhen_after_fork (nbdkit_next_after_fork *next, nbdkit_backend *nxdata)
 {
   int err;
   pthread_t thread;
@@ -466,7 +466,8 @@ exitwhen_after_fork (nbdkit_next_after_fork *next, void *nxdata)
 }

 static int
-exitwhen_preconnect (nbdkit_next_preconnect *next, void *nxdata, int readonly)
+exitwhen_preconnect (nbdkit_next_preconnect *next, nbdkit_backend *nxdata,
+                     int readonly)
 {
   ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);

diff --git a/filters/exportname/exportname.c b/filters/exportname/exportname.c
index 164641ba..a4bebf12 100644
--- a/filters/exportname/exportname.c
+++ b/filters/exportname/exportname.c
@@ -77,7 +77,7 @@ exportname_unload (void)

 /* Called for each key=value passed on the command line. */
 static int
-exportname_config (nbdkit_next_config *next, void *nxdata,
+exportname_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
                    const char *key, const char *value)
 {
   int r;
@@ -204,7 +204,8 @@ get_desc (const char *name, const char *def)
 }

 static int
-exportname_list_exports (nbdkit_next_list_exports *next, void *nxdata,
+exportname_list_exports (nbdkit_next_list_exports *next,
+                         nbdkit_backend *nxdata,
                          int readonly, int is_tls,
                          struct nbdkit_exports *exps)
 {
@@ -245,7 +246,8 @@ exportname_list_exports (nbdkit_next_list_exports *next, void *nxdata,
 }

 static const char *
-exportname_default_export (nbdkit_next_default_export *next, void *nxdata,
+exportname_default_export (nbdkit_next_default_export *next,
+                           nbdkit_backend *nxdata,
                            int readonly, int is_tls)
 {
   size_t i;
@@ -269,7 +271,7 @@ struct handle {
 };

 static void *
-exportname_open (nbdkit_next_open *next, void *nxdata,
+exportname_open (nbdkit_next_open *next, nbdkit_context *nxdata,
                  int readonly, const char *exportname, int is_tls)
 {
   size_t i;
@@ -313,14 +315,14 @@ exportname_close (void *handle)
 }

 static const char *
-exportname_export_description (struct nbdkit_next_ops *next_ops, void *nxdata,
+exportname_export_description (nbdkit_next *next,
                                void *handle)
 {
   struct handle *h = handle;
   const char *def = NULL;

   if (desc_mode == DESC_KEEP)
-    def = next_ops->export_description (nxdata);
+    def = next->export_description (next);

   return get_desc (h->name, def);
 }
diff --git a/filters/ext2/ext2.c b/filters/ext2/ext2.c
index 9a6eec94..cc80458f 100644
--- a/filters/ext2/ext2.c
+++ b/filters/ext2/ext2.c
@@ -45,7 +45,6 @@

 #define NBDKIT_API_VERSION 2

-#define NBDKIT_TYPESAFE /* HACK to get type-safe parameters. */
 #include <nbdkit-filter.h>

 #include "cleanup.h"
@@ -186,8 +185,7 @@ ext2_open (nbdkit_next_open *next, nbdkit_context *nxdata,
 }

 static int
-ext2_prepare (nbdkit_next *next_ops, nbdkit_next *nxdata, void *handle,
-              int readonly)
+ext2_prepare (nbdkit_next *next, void *handle, int readonly)
 {
   struct handle *h = handle;
   errcode_t err;
@@ -203,10 +201,10 @@ ext2_prepare (nbdkit_next *next_ops, nbdkit_next *nxdata, void *handle,
 #ifdef EXT2_FLAG_64BITS
   fs_flags |= EXT2_FLAG_64BITS;
 #endif
-  r = next_ops->get_size (nxdata);
+  r = next->get_size (next);
   if (r == -1)
     return -1;
-  r = next_ops->can_write (nxdata);
+  r = next->can_write (next);
   if (r == -1)
     return -1;
   if (r == 0)
@@ -215,8 +213,8 @@ ext2_prepare (nbdkit_next *next_ops, nbdkit_next *nxdata, void *handle,
   if (!readonly)
     fs_flags |= EXT2_FLAG_RW;

-  h->next = next_ops;
-  name = nbdkit_io_encode (next_ops);
+  h->next = next;
+  name = nbdkit_io_encode (next);
   if (!name) {
     nbdkit_error ("nbdkit_io_encode: %m");
     return -1;
@@ -293,21 +291,20 @@ ext2_close (void *handle)
 }

 static int
-ext2_can_fua (nbdkit_next *next_ops, nbdkit_next *nxdata, void *handle)
+ext2_can_fua (nbdkit_next *next, void *handle)
 {
   return NBDKIT_FUA_NATIVE;
 }

 static int
-ext2_can_cache (nbdkit_next *next_ops, nbdkit_next *nxdata, void *handle)
+ext2_can_cache (nbdkit_next *next, void *handle)
 {
   /* Let nbdkit call pread to populate the file system cache. */
   return NBDKIT_CACHE_EMULATE;
 }

 static int
-ext2_can_multi_conn (nbdkit_next *next_ops, nbdkit_next *nxdata,
-                     void *handle)
+ext2_can_multi_conn (nbdkit_next *next, void *handle)
 {
   /* Since we do not permit parallel connections, it does not matter
    * what we advertise here, and we could just as easily inherit the
@@ -320,15 +317,14 @@ ext2_can_multi_conn (nbdkit_next *next_ops, nbdkit_next *nxdata,
 }

 static int
-ext2_can_flush (nbdkit_next *next_ops, nbdkit_next *nxdata,
-                void *handle)
+ext2_can_flush (nbdkit_next *next, void *handle)
 {
   /* Regardless of the underlying plugin, we handle flush at the level
    * of the filesystem.  However, we also need to cache the underlying
    * plugin ability, since ext2 wants to flush the filesystem into
    * permanent storage when possible.
    */
-  if (next_ops->can_flush (nxdata) == -1)
+  if (next->can_flush (next) == -1)
     return -1;
   return 1;
 }
@@ -338,23 +334,23 @@ ext2_can_flush (nbdkit_next *next_ops, nbdkit_next *nxdata,
  * is very obscure.
  */
 static int
-ext2_can_zero (nbdkit_next *next_ops, nbdkit_next *nxdata, void *handle)
+ext2_can_zero (nbdkit_next *next, void *handle)
 {
   /* For now, tell nbdkit to call .pwrite instead of any optimization.
    * However, we also want to cache the underlying plugin support.
    */
-  if (next_ops->can_zero (nxdata) == -1)
+  if (next->can_zero (next) == -1)
     return -1;
   return NBDKIT_ZERO_EMULATE;
 }

 static int
-ext2_can_trim (nbdkit_next *next_ops, nbdkit_next *nxdata, void *handle)
+ext2_can_trim (nbdkit_next *next, void *handle)
 {
   /* For now, tell nbdkit to never call .trim.  However, we also want
    * to cache the underlying plugin support.
    */
-  if (next_ops->can_trim (nxdata) == -1)
+  if (next->can_trim (next) == -1)
     return -1;
   return 0;
 }
@@ -378,13 +374,12 @@ static int ext2_thread_model (void)

 /* Description. */
 static const char *
-ext2_export_description (nbdkit_next *next_ops, nbdkit_next *nxdata,
-                         void *handle)
+ext2_export_description (nbdkit_next *next, void *handle)
 {
   struct handle *h = handle;
   const char *fname = file ?: h->exportname;
   const char *slash = fname[0] == '/' ? "" : "/";
-  const char *base = next_ops->export_description (nxdata);
+  const char *base = next->export_description (next);

   if (!base)
     return NULL;
@@ -394,7 +389,7 @@ ext2_export_description (nbdkit_next *next_ops, nbdkit_next *nxdata,

 /* Get the disk size. */
 static int64_t
-ext2_get_size (nbdkit_next *next_ops, nbdkit_next *nxdata, void *handle)
+ext2_get_size (nbdkit_next *next, void *handle)
 {
   struct handle *h = handle;
   errcode_t err;
@@ -410,7 +405,7 @@ ext2_get_size (nbdkit_next *next_ops, nbdkit_next *nxdata, void *handle)

 /* Read data. */
 static int
-ext2_pread (nbdkit_next *next_ops, nbdkit_next *nxdata,
+ext2_pread (nbdkit_next *next,
             void *handle, void *buf, uint32_t count, uint64_t offset,
             uint32_t flags, int *errp)
 {
@@ -447,7 +442,7 @@ ext2_pread (nbdkit_next *next_ops, nbdkit_next *nxdata,

 /* Write data to the file. */
 static int
-ext2_pwrite (nbdkit_next *next_ops, nbdkit_next *nxdata,
+ext2_pwrite (nbdkit_next *next,
              void *handle, const void *buf, uint32_t count, uint64_t offset,
              uint32_t flags, int *errp)
 {
@@ -488,7 +483,7 @@ ext2_pwrite (nbdkit_next *next_ops, nbdkit_next *nxdata,
 }

 static int
-ext2_flush (nbdkit_next *next_ops, nbdkit_next *nxdata,
+ext2_flush (nbdkit_next *next,
             void *handle, uint32_t flags, int *errp)
 {
   struct handle *h = handle;
diff --git a/filters/extentlist/extentlist.c b/filters/extentlist/extentlist.c
index 98b65f9e..c609062c 100644
--- a/filters/extentlist/extentlist.c
+++ b/filters/extentlist/extentlist.c
@@ -69,7 +69,7 @@ extentlist_unload (void)

 /* Called for each key=value passed on the command line. */
 static int
-extentlist_config (nbdkit_next_config *next, void *nxdata,
+extentlist_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
                    const char *key, const char *value)
 {
   if (strcmp (key, "extentlist") == 0) {
@@ -85,7 +85,8 @@ extentlist_config (nbdkit_next_config *next, void *nxdata,
 }

 static int
-extentlist_config_complete (nbdkit_next_config_complete *next, void *nxdata)
+extentlist_config_complete (nbdkit_next_config_complete *next,
+                            nbdkit_backend *nxdata)
 {
   if (extentlist == NULL) {
     nbdkit_error ("you must supply the extentlist parameter "
@@ -261,7 +262,7 @@ parse_extentlist (void)
 }

 static int
-extentlist_get_ready (nbdkit_next_get_ready *next, void *nxdata,
+extentlist_get_ready (nbdkit_next_get_ready *next, nbdkit_backend *nxdata,
                       int thread_model)
 {
   parse_extentlist ();
@@ -270,7 +271,7 @@ extentlist_get_ready (nbdkit_next_get_ready *next, void *nxdata,
 }

 static int
-extentlist_can_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
+extentlist_can_extents (nbdkit_next *next,
                         void *handle)
 {
   return 1;
@@ -281,7 +282,7 @@ int extentlist_debug_lookup = 0;

 /* Read extents. */
 static int
-extentlist_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
+extentlist_extents (nbdkit_next *next,
                     void *handle, uint32_t count, uint64_t offset,
                     uint32_t flags,
                     struct nbdkit_extents *ret_extents,
diff --git a/filters/fua/fua.c b/filters/fua/fua.c
index 229d83db..7f9eb760 100644
--- a/filters/fua/fua.c
+++ b/filters/fua/fua.c
@@ -51,7 +51,7 @@ static enum FuaMode {
 } fuamode;

 static int
-fua_config (nbdkit_next_config *next, void *nxdata,
+fua_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
             const char *key, const char *value)
 {
   if (strcmp (key, "fuamode") == 0) {
@@ -82,7 +82,7 @@ fua_config (nbdkit_next_config *next, void *nxdata,

 /* Check that desired mode is supported by plugin. */
 static int
-fua_prepare (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle,
+fua_prepare (nbdkit_next *next, void *handle,
              int readonly)
 {
   int r;
@@ -97,7 +97,7 @@ fua_prepare (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle,
   case DISCARD:
     break;
   case EMULATE:
-    r = next_ops->can_flush (nxdata);
+    r = next->can_flush (next);
     if (r == -1)
       return -1;
     if (r == 0) {
@@ -107,7 +107,7 @@ fua_prepare (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle,
     break;
   case NATIVE:
   case FORCE:
-    r = next_ops->can_fua (nxdata);
+    r = next->can_fua (next);
     if (r == -1)
       return -1;
     if (r == NBDKIT_FUA_NONE) {
@@ -122,7 +122,7 @@ fua_prepare (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle,

 /* Advertise proper flush support. */
 static int
-fua_can_flush (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle)
+fua_can_flush (nbdkit_next *next, void *handle)
 {
   switch (fuamode) {
   case FORCE:
@@ -132,14 +132,14 @@ fua_can_flush (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle)
   case EMULATE:
   case NATIVE:
   case PASS:
-    return next_ops->can_flush (nxdata);
+    return next->can_flush (next);
   }
   abort ();
 }

 /* Advertise desired fua mode. */
 static int
-fua_can_fua (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle)
+fua_can_fua (nbdkit_next *next, void *handle)
 {
   switch (fuamode) {
   case NONE:
@@ -151,13 +151,13 @@ fua_can_fua (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle)
   case DISCARD:
     return NBDKIT_FUA_NATIVE;
   case PASS:
-    return next_ops->can_fua (nxdata);
+    return next->can_fua (next);
   }
   abort ();
 }

 static int
-fua_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
+fua_pwrite (nbdkit_next *next,
             void *handle, const void *buf, uint32_t count, uint64_t offs,
             uint32_t flags, int *err)
 {
@@ -184,14 +184,14 @@ fua_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
     flags &= ~NBDKIT_FLAG_FUA;
     break;
   }
-  r = next_ops->pwrite (nxdata, buf, count, offs, flags, err);
+  r = next->pwrite (next, buf, count, offs, flags, err);
   if (r != -1 && need_flush)
-    r = next_ops->flush (nxdata, 0, err);
+    r = next->flush (next, 0, err);
   return r;
 }

 static int
-fua_flush (struct nbdkit_next_ops *next_ops, void *nxdata,
+fua_flush (nbdkit_next *next,
            void *handle, uint32_t flags, int *err)
 {
   switch (fuamode) {
@@ -203,13 +203,13 @@ fua_flush (struct nbdkit_next_ops *next_ops, void *nxdata,
   case EMULATE:
   case NATIVE:
   case PASS:
-    return next_ops->flush (nxdata, flags, err);
+    return next->flush (next, flags, err);
   }
   abort ();
 }

 static int
-fua_trim (struct nbdkit_next_ops *next_ops, void *nxdata,
+fua_trim (nbdkit_next *next,
           void *handle, uint32_t count, uint64_t offs, uint32_t flags,
           int *err)
 {
@@ -236,14 +236,14 @@ fua_trim (struct nbdkit_next_ops *next_ops, void *nxdata,
     flags &= ~NBDKIT_FLAG_FUA;
     break;
   }
-  r = next_ops->trim (nxdata, count, offs, flags, err);
+  r = next->trim (next, count, offs, flags, err);
   if (r != -1 && need_flush)
-    r = next_ops->flush (nxdata, 0, err);
+    r = next->flush (next, 0, err);
   return r;
 }

 static int
-fua_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+fua_zero (nbdkit_next *next,
           void *handle, uint32_t count, uint64_t offs, uint32_t flags,
           int *err)
 {
@@ -270,9 +270,9 @@ fua_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
     flags &= ~NBDKIT_FLAG_FUA;
     break;
   }
-  r = next_ops->zero (nxdata, count, offs, flags, err);
+  r = next->zero (next, count, offs, flags, err);
   if (r != -1 && need_flush)
-    r = next_ops->flush (nxdata, 0, err);
+    r = next->flush (next, 0, err);
   return r;
 }

diff --git a/filters/gzip/gzip.c b/filters/gzip/gzip.c
index 0b3f48b7..dff9634d 100644
--- a/filters/gzip/gzip.c
+++ b/filters/gzip/gzip.c
@@ -125,7 +125,7 @@ xwrite (const void *buf, size_t count)

 /* The first thread to call gzip_prepare uncompresses the whole plugin. */
 static int
-do_uncompress (struct nbdkit_next_ops *next_ops, void *nxdata)
+do_uncompress (nbdkit_next *next)
 {
   z_stream strm;
   int zerr;
@@ -143,7 +143,7 @@ do_uncompress (struct nbdkit_next_ops *next_ops, void *nxdata)
   assert (size == -1);

   /* Get the size of the underlying plugin. */
-  compressed_size = next_ops->get_size (nxdata);
+  compressed_size = next->get_size (next);
   if (compressed_size == -1)
     return -1;

@@ -207,8 +207,8 @@ do_uncompress (struct nbdkit_next_ops *next_ops, void *nxdata)
       size_t n = MIN (block_size, compressed_size - strm.total_in);
       int err = 0;

-      if (next_ops->pread (nxdata, in_block, (uint32_t) n, strm.total_in,
-                           0, &err) == -1) {
+      if (next->pread (next, in_block, (uint32_t)n, strm.total_in, 0,
+                       &err) == -1) {
         errno = err;
         return -1;
       }
@@ -248,19 +248,19 @@ do_uncompress (struct nbdkit_next_ops *next_ops, void *nxdata)
 }

 static int
-gzip_prepare (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle,
+gzip_prepare (nbdkit_next *next, void *handle,
               int readonly)
 {
   ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);

   if (size >= 0)
     return 0;
-  return do_uncompress (next_ops, nxdata);
+  return do_uncompress (next);
 }

 /* Whatever the plugin says, this filter makes it read-only. */
 static int
-gzip_can_write (struct nbdkit_next_ops *next_ops, void *nxdata,
+gzip_can_write (nbdkit_next *next,
                 void *handle)
 {
   return 0;
@@ -268,7 +268,7 @@ gzip_can_write (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Whatever the plugin says, this filter is consistent across connections. */
 static int
-gzip_can_multi_conn (struct nbdkit_next_ops *next_ops, void *nxdata,
+gzip_can_multi_conn (nbdkit_next *next,
                      void *handle)
 {
   return 1;
@@ -278,7 +278,7 @@ gzip_can_multi_conn (struct nbdkit_next_ops *next_ops, void *nxdata,
  * supported.
  */
 static int
-gzip_can_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
+gzip_can_extents (nbdkit_next *next,
                   void *handle)
 {
   return 0;
@@ -289,7 +289,7 @@ gzip_can_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
  * behavior of calling .pread for caching.
  */
 static int
-gzip_can_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
+gzip_can_cache (nbdkit_next *next,
                 void *handle)
 {
   return NBDKIT_CACHE_EMULATE;
@@ -297,10 +297,10 @@ gzip_can_cache (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Description. */
 static const char *
-gzip_export_description (struct nbdkit_next_ops *next_ops, void *nxdata,
+gzip_export_description (nbdkit_next *next,
                          void *handle)
 {
-  const char *base = next_ops->export_description (nxdata);
+  const char *base = next->export_description (next);

   if (!base)
     return NULL;
@@ -309,7 +309,7 @@ gzip_export_description (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Get the file size. */
 static int64_t
-gzip_get_size (struct nbdkit_next_ops *next_ops, void *nxdata,
+gzip_get_size (nbdkit_next *next,
                void *handle)
 {
   int64_t t;
@@ -318,7 +318,7 @@ gzip_get_size (struct nbdkit_next_ops *next_ops, void *nxdata,
   assert (size >= 0);

   /* Check the plugin size didn't change underneath us. */
-  t = next_ops->get_size (nxdata);
+  t = next->get_size (next);
   if (t == -1)
     return -1;
   if (t != compressed_size) {
@@ -333,7 +333,7 @@ gzip_get_size (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Read data from the temporary file. */
 static int
-gzip_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
+gzip_pread (nbdkit_next *next,
             void *handle, void *buf, uint32_t count, uint64_t offset,
             uint32_t flags, int *err)
 {
diff --git a/filters/ip/ip.c b/filters/ip/ip.c
index eebb3a24..9c3449ac 100644
--- a/filters/ip/ip.c
+++ b/filters/ip/ip.c
@@ -399,7 +399,7 @@ parse_rules (const char *paramname,
 }

 static int
-ip_config (nbdkit_next_config *next, void *nxdata,
+ip_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
            const char *key, const char *value)
 {
   /* For convenience we permit multiple allow and deny parameters,
@@ -420,7 +420,7 @@ ip_config (nbdkit_next_config *next, void *nxdata,
 }

 static int
-ip_config_complete (nbdkit_next_config_complete *next, void *nxdata)
+ip_config_complete (nbdkit_next_config_complete *next, nbdkit_backend *nxdata)
 {
   if (ip_debug_rules) {
     print_rules ("ip: parsed allow", allow_rules);
@@ -586,7 +586,8 @@ check_if_allowed (const struct sockaddr *addr)
 }

 static int
-ip_preconnect (nbdkit_next_preconnect *next, void *nxdata, int readonly)
+ip_preconnect (nbdkit_next_preconnect *next, nbdkit_backend *nxdata,
+               int readonly)
 {
   struct sockaddr_storage addr;
   socklen_t addrlen = sizeof addr;
diff --git a/filters/log/log.c b/filters/log/log.c
index 7b81a8f1..26358302 100644
--- a/filters/log/log.c
+++ b/filters/log/log.c
@@ -76,7 +76,7 @@ log_unload (void)

 /* Called for each key=value passed on the command line. */
 static int
-log_config (nbdkit_next_config *next, void *nxdata,
+log_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
             const char *key, const char *value)
 {
   if (strcmp (key, "logfile") == 0) {
@@ -103,7 +103,8 @@ log_config (nbdkit_next_config *next, void *nxdata,

 /* Open the logfile. */
 static int
-log_get_ready (nbdkit_next_get_ready *next, void *nxdata, int thread_model)
+log_get_ready (nbdkit_next_get_ready *next, nbdkit_backend *nxdata,
+               int thread_model)
 {
   int fd;

@@ -136,7 +137,7 @@ log_get_ready (nbdkit_next_get_ready *next, void *nxdata, int thread_model)
 }

 static int
-log_after_fork (nbdkit_next_after_fork *next, void *nxdata)
+log_after_fork (nbdkit_next_after_fork *next, nbdkit_backend *nxdata)
 {
   /* Only issue this message if we actually fork. */
   if (getpid () != saved_pid)
@@ -147,7 +148,7 @@ log_after_fork (nbdkit_next_after_fork *next, void *nxdata)

 /* List exports. */
 static int
-log_list_exports (nbdkit_next_list_exports *next, void *nxdata,
+log_list_exports (nbdkit_next_list_exports *next, nbdkit_backend *nxdata,
                   int readonly, int is_tls,
                   struct nbdkit_exports *exports)
 {
@@ -204,7 +205,7 @@ log_preconnect (nbdkit_next_preconnect *next, nbdkit_backend *nxdata,

 /* Open a connection. */
 static void *
-log_open (nbdkit_next_open *next, void *nxdata,
+log_open (nbdkit_next_open *next, nbdkit_context *nxdata,
           int readonly, const char *exportname, int is_tls)
 {
   struct handle *h;
@@ -242,7 +243,7 @@ log_close (void *handle)
 }

 static int
-log_prepare (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle,
+log_prepare (nbdkit_next *next, void *handle,
              int readonly)
 {
   FILE *fp;
@@ -250,16 +251,16 @@ log_prepare (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle,
   size_t len = 0;
   struct handle *h = handle;
   const char *exportname = h->exportname;
-  int64_t size = next_ops->get_size (nxdata);
-  int w = next_ops->can_write (nxdata);
-  int f = next_ops->can_flush (nxdata);
-  int r = next_ops->is_rotational (nxdata);
-  int t = next_ops->can_trim (nxdata);
-  int z = next_ops->can_zero (nxdata);
-  int F = next_ops->can_fua (nxdata);
-  int e = next_ops->can_extents (nxdata);
-  int c = next_ops->can_cache (nxdata);
-  int Z = next_ops->can_fast_zero (nxdata);
+  int64_t size = next->get_size (next);
+  int w = next->can_write (next);
+  int f = next->can_flush (next);
+  int r = next->is_rotational (next);
+  int t = next->can_trim (next);
+  int z = next->can_zero (next);
+  int F = next->can_fua (next);
+  int e = next->can_extents (next);
+  int c = next->can_cache (next);
+  int Z = next->can_fast_zero (next);

   if (size < 0 || w < 0 || f < 0 || r < 0 || t < 0 || z < 0 || F < 0 ||
       e < 0 || c < 0 || Z < 0)
@@ -284,7 +285,7 @@ log_prepare (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle,
 }

 static int
-log_finalize (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle)
+log_finalize (nbdkit_next *next, void *handle)
 {
   struct handle *h = handle;

@@ -294,7 +295,7 @@ log_finalize (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle)

 /* Read data. */
 static int
-log_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
+log_pread (nbdkit_next *next,
            void *handle, void *buf, uint32_t count, uint64_t offs,
            uint32_t flags, int *err)
 {
@@ -304,12 +305,12 @@ log_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
   LOG (h, "Read", r, err, "offset=0x%" PRIx64 " count=0x%x", offs, count);

   assert (!flags);
-  return r = next_ops->pread (nxdata, buf, count, offs, flags, err);
+  return r = next->pread (next, buf, count, offs, flags, err);
 }

 /* Write data. */
 static int
-log_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
+log_pwrite (nbdkit_next *next,
             void *handle, const void *buf, uint32_t count, uint64_t offs,
             uint32_t flags, int *err)
 {
@@ -321,12 +322,12 @@ log_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
        offs, count, !!(flags & NBDKIT_FLAG_FUA));

   assert (!(flags & ~NBDKIT_FLAG_FUA));
-  return r = next_ops->pwrite (nxdata, buf, count, offs, flags, err);
+  return r = next->pwrite (next, buf, count, offs, flags, err);
 }

 /* Flush. */
 static int
-log_flush (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle,
+log_flush (nbdkit_next *next, void *handle,
            uint32_t flags, int *err)
 {
   struct handle *h = handle;
@@ -335,12 +336,12 @@ log_flush (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle,
   LOG (h, "Flush", r, err, "");

   assert (!flags);
-  return r = next_ops->flush (nxdata, flags, err);
+  return r = next->flush (next, flags, err);
 }

 /* Trim data. */
 static int
-log_trim (struct nbdkit_next_ops *next_ops, void *nxdata,
+log_trim (nbdkit_next *next,
           void *handle, uint32_t count, uint64_t offs, uint32_t flags,
           int *err)
 {
@@ -352,12 +353,12 @@ log_trim (struct nbdkit_next_ops *next_ops, void *nxdata,
        offs, count, !!(flags & NBDKIT_FLAG_FUA));

   assert (!(flags & ~NBDKIT_FLAG_FUA));
-  return r = next_ops->trim (nxdata, count, offs, flags, err);
+  return r = next->trim (next, count, offs, flags, err);
 }

 /* Zero data. */
 static int
-log_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+log_zero (nbdkit_next *next,
           void *handle, uint32_t count, uint64_t offs, uint32_t flags,
           int *err)
 {
@@ -372,12 +373,12 @@ log_zero (struct nbdkit_next_ops *next_ops, void *nxdata,

   assert (!(flags & ~(NBDKIT_FLAG_FUA | NBDKIT_FLAG_MAY_TRIM |
                       NBDKIT_FLAG_FAST_ZERO)));
-  return r = next_ops->zero (nxdata, count, offs, flags, err);
+  return r = next->zero (next, count, offs, flags, err);
 }

 /* Extents. */
 static int
-log_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
+log_extents (nbdkit_next *next,
              void *handle, uint32_t count, uint64_t offs, uint32_t flags,
              struct nbdkit_extents *extents, int *err)
 {
@@ -389,7 +390,7 @@ log_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
   enter (h, id, "Extents",
          "offset=0x%" PRIx64 " count=0x%x req_one=%d",
          offs, count, !!(flags & NBDKIT_FLAG_REQ_ONE));
-  r = next_ops->extents (nxdata, count, offs, flags, extents, err);
+  r = next->extents (next, count, offs, flags, extents, err);
   if (r == -1)
     leave_simple (h, id, "Extents", r, err);
   else {
@@ -430,7 +431,7 @@ log_extents (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Cache data. */
 static int
-log_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
+log_cache (nbdkit_next *next,
            void *handle, uint32_t count, uint64_t offs, uint32_t flags,
            int *err)
 {
@@ -440,7 +441,7 @@ log_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
   LOG (h, "Cache", r, err, "offset=0x%" PRIx64 " count=0x%x", offs, count);

   assert (!flags);
-  return r = next_ops->cache (nxdata, count, offs, flags, err);
+  return r = next->cache (next, count, offs, flags, err);
 }

 static struct nbdkit_filter filter = {
diff --git a/filters/nocache/nocache.c b/filters/nocache/nocache.c
index fe11243e..169fc37d 100644
--- a/filters/nocache/nocache.c
+++ b/filters/nocache/nocache.c
@@ -50,7 +50,7 @@ static enum CacheMode {
 } cachemode;

 static int
-nocache_config (nbdkit_next_config *next, void *nxdata,
+nocache_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
                 const char *key, const char *value)
 {
   if (strcmp (key, "cachemode") == 0) {
@@ -73,7 +73,7 @@ nocache_config (nbdkit_next_config *next, void *nxdata,

 /* Advertise desired FLAG_SEND_CACHE mode. */
 static int
-nocache_can_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
+nocache_can_cache (nbdkit_next *next,
                    void *handle)
 {
   switch (cachemode) {
@@ -88,7 +88,7 @@ nocache_can_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
 }

 static int
-nocache_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
+nocache_cache (nbdkit_next *next,
                void *handle, uint32_t count, uint64_t offs, uint32_t flags,
                int *err)
 {
diff --git a/filters/noextents/noextents.c b/filters/noextents/noextents.c
index e6ac33b2..f3044809 100644
--- a/filters/noextents/noextents.c
+++ b/filters/noextents/noextents.c
@@ -35,7 +35,7 @@
 #include <nbdkit-filter.h>

 static int
-noextents_can_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
+noextents_can_extents (nbdkit_next *next,
                        void *handle)
 {
   return 0;
diff --git a/filters/noparallel/noparallel.c b/filters/noparallel/noparallel.c
index f9006c06..1a24b106 100644
--- a/filters/noparallel/noparallel.c
+++ b/filters/noparallel/noparallel.c
@@ -44,7 +44,7 @@
 static int thread_model = NBDKIT_THREAD_MODEL_SERIALIZE_REQUESTS;

 static int
-noparallel_config (nbdkit_next_config *next, void *nxdata,
+noparallel_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
                    const char *key, const char *value)
 {
   if (strcmp (key, "serialize") == 0 ||
diff --git a/filters/nozero/nozero.c b/filters/nozero/nozero.c
index 22d5914c..7c5d67c4 100644
--- a/filters/nozero/nozero.c
+++ b/filters/nozero/nozero.c
@@ -66,7 +66,7 @@ static enum FastZeroMode {
 } fastzeromode;

 static int
-nozero_config (nbdkit_next_config *next, void *nxdata,
+nozero_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
                const char *key, const char *value)
 {
   if (strcmp (key, "zeromode") == 0) {
@@ -106,7 +106,7 @@ nozero_config (nbdkit_next_config *next, void *nxdata,

 /* Check that desired mode is supported by plugin. */
 static int
-nozero_prepare (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle,
+nozero_prepare (nbdkit_next *next, void *handle,
                 int readonly)
 {
   int r;
@@ -116,7 +116,7 @@ nozero_prepare (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle,
     return 0;

   if (zeromode == NOTRIM || zeromode == PLUGIN) {
-    r = next_ops->can_zero (nxdata);
+    r = next->can_zero (next);
     if (r == -1)
       return -1;
     if (!r) {
@@ -130,7 +130,7 @@ nozero_prepare (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle,

 /* Advertise desired WRITE_ZEROES mode. */
 static int
-nozero_can_zero (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle)
+nozero_can_zero (nbdkit_next *next, void *handle)
 {
   switch (zeromode) {
   case NONE:
@@ -138,24 +138,24 @@ nozero_can_zero (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle)
   case EMULATE:
     return NBDKIT_ZERO_EMULATE;
   default:
-    return next_ops->can_zero (nxdata);
+    return next->can_zero (next);
   }
 }

 /* Advertise desired FAST_ZERO mode. */
 static int
-nozero_can_fast_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+nozero_can_fast_zero (nbdkit_next *next,
                       void *handle)
 {
   if (zeromode == NONE)
     return 0;
   if (zeromode != EMULATE && fastzeromode == DEFAULT)
-    return next_ops->can_fast_zero (nxdata);
+    return next->can_fast_zero (next);
   return fastzeromode != NOFAST;
 }

 static int
-nozero_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+nozero_zero (nbdkit_next *next,
              void *handle, uint32_t count, uint64_t offs, uint32_t flags,
              int *err)
 {
@@ -178,10 +178,10 @@ nozero_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
     flags &= ~NBDKIT_FLAG_MAY_TRIM;

   if (zeromode != EMULATE)
-    return next_ops->zero (nxdata, count, offs, flags, err);
+    return next->zero (next, count, offs, flags, err);

   if (flags & NBDKIT_FLAG_FUA) {
-    if (next_ops->can_fua (nxdata) == NBDKIT_FUA_EMULATE)
+    if (next->can_fua (next) == NBDKIT_FUA_EMULATE)
       need_flush = true;
     else
       writeflags = NBDKIT_FLAG_FUA;
@@ -197,7 +197,7 @@ nozero_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
     if (size == count && need_flush)
       writeflags = NBDKIT_FLAG_FUA;

-    if (next_ops->pwrite (nxdata, buffer, size, offs, writeflags, err) == -1)
+    if (next->pwrite (next, buffer, size, offs, writeflags, err) == -1)
       return -1;
     offs += size;
     count -= size;
diff --git a/filters/offset/offset.c b/filters/offset/offset.c
index ae1ce230..ff0b858c 100644
--- a/filters/offset/offset.c
+++ b/filters/offset/offset.c
@@ -46,7 +46,7 @@ static int64_t offset = 0, range = -1;

 /* Called for each key=value passed on the command line. */
 static int
-offset_config (nbdkit_next_config *next, void *nxdata,
+offset_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
                const char *key, const char *value)
 {
   if (strcmp (key, "offset") == 0) {
@@ -71,10 +71,10 @@ offset_config (nbdkit_next_config *next, void *nxdata,

 /* Get the file size. */
 static int64_t
-offset_get_size (struct nbdkit_next_ops *next_ops, void *nxdata,
+offset_get_size (nbdkit_next *next,
                  void *handle)
 {
-  int64_t real_size = next_ops->get_size (nxdata);
+  int64_t real_size = next->get_size (next);

   if (real_size == -1)
     return -1;
@@ -102,59 +102,58 @@ offset_get_size (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Read data. */
 static int
-offset_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
+offset_pread (nbdkit_next *next,
               void *handle, void *buf, uint32_t count, uint64_t offs,
               uint32_t flags, int *err)
 {
-  return next_ops->pread (nxdata, buf, count, offs + offset, flags, err);
+  return next->pread (next, buf, count, offs + offset, flags, err);
 }

 /* Write data. */
 static int
-offset_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
+offset_pwrite (nbdkit_next *next,
                void *handle,
                const void *buf, uint32_t count, uint64_t offs, uint32_t flags,
                int *err)
 {
-  return next_ops->pwrite (nxdata, buf, count, offs + offset, flags, err);
+  return next->pwrite (next, buf, count, offs + offset, flags, err);
 }

 /* Trim data. */
 static int
-offset_trim (struct nbdkit_next_ops *next_ops, void *nxdata,
+offset_trim (nbdkit_next *next,
              void *handle, uint32_t count, uint64_t offs, uint32_t flags,
              int *err)
 {
-  return next_ops->trim (nxdata, count, offs + offset, flags, err);
+  return next->trim (next, count, offs + offset, flags, err);
 }

 /* Zero data. */
 static int
-offset_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+offset_zero (nbdkit_next *next,
              void *handle, uint32_t count, uint64_t offs, uint32_t flags,
              int *err)
 {
-  return next_ops->zero (nxdata, count, offs + offset, flags, err);
+  return next->zero (next, count, offs + offset, flags, err);
 }

 /* Extents. */
 static int
-offset_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
+offset_extents (nbdkit_next *next,
                 void *handle, uint32_t count, uint64_t offs, uint32_t flags,
                 struct nbdkit_extents *extents, int *err)
 {
   size_t i;
   CLEANUP_EXTENTS_FREE struct nbdkit_extents *extents2 = NULL;
   struct nbdkit_extent e;
-  int64_t end = range >= 0 ? offset + range : next_ops->get_size (nxdata);
+  int64_t end = range >= 0 ? offset + range : next->get_size (next);

   extents2 = nbdkit_extents_new (offs + offset, end);
   if (extents2 == NULL) {
     *err = errno;
     return -1;
   }
-  if (next_ops->extents (nxdata, count, offs + offset,
-                         flags, extents2, err) == -1)
+  if (next->extents (next, count, offs + offset, flags, extents2, err) == -1)
     return -1;

   for (i = 0; i < nbdkit_extents_count (extents2); ++i) {
@@ -170,11 +169,11 @@ offset_extents (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Cache data. */
 static int
-offset_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
+offset_cache (nbdkit_next *next,
               void *handle, uint32_t count, uint64_t offs, uint32_t flags,
               int *err)
 {
-  return next_ops->cache (nxdata, count, offs + offset, flags, err);
+  return next->cache (next, count, offs + offset, flags, err);
 }

 static struct nbdkit_filter filter = {
diff --git a/filters/partition/partition-gpt.c b/filters/partition/partition-gpt.c
index f2ff2116..555483c3 100644
--- a/filters/partition/partition-gpt.c
+++ b/filters/partition/partition-gpt.c
@@ -66,7 +66,7 @@ get_gpt_partition (uint8_t *bytes,
 }

 int
-find_gpt_partition (struct nbdkit_next_ops *next_ops, void *nxdata,
+find_gpt_partition (nbdkit_next *next,
                     int64_t size, uint8_t *header_bytes,
                     int64_t *offset_r, int64_t *range_r)
 {
@@ -100,9 +100,8 @@ find_gpt_partition (struct nbdkit_next_ops *next_ops, void *nxdata,

   for (i = 0; i < nr_partition_entries; ++i) {
     /* We already checked these are within bounds above. */
-    if (next_ops->pread (nxdata, partition_bytes, sizeof partition_bytes,
-                         2*SECTOR_SIZE + i*size_partition_entry, 0,
-                         &err) == -1)
+    if (next->pread (next, partition_bytes, sizeof partition_bytes,
+                     2 * SECTOR_SIZE + i * size_partition_entry, 0, &err) == -1)
       return -1;
     get_gpt_partition (partition_bytes,
                        partition_type_guid, &first_lba, &last_lba);
diff --git a/filters/partition/partition-mbr.c b/filters/partition/partition-mbr.c
index 22532eff..d964e0c6 100644
--- a/filters/partition/partition-mbr.c
+++ b/filters/partition/partition-mbr.c
@@ -68,7 +68,7 @@ get_mbr_partition (uint8_t *sector, int i, struct mbr_partition *part)
 }

 int
-find_mbr_partition (struct nbdkit_next_ops *next_ops, void *nxdata,
+find_mbr_partition (nbdkit_next *next,
                     int64_t size, uint8_t *mbr,
                     int64_t *offset_r, int64_t *range_r)
 {
@@ -132,8 +132,7 @@ find_mbr_partition (struct nbdkit_next_ops *next_ops, void *nxdata,

       /* Read the EBR sector. */
       nbdkit_debug ("partition: reading EBR at %" PRIi64, ebr);
-      if (next_ops->pread (nxdata, sector, sizeof sector, ebr, 0,
-                           &errno) == -1)
+      if (next->pread (next, sector, sizeof sector, ebr, 0, &errno) == -1)
         return -1;

       if (partnum == i) {
diff --git a/filters/partition/partition.c b/filters/partition/partition.c
index d897b374..4f91d04b 100644
--- a/filters/partition/partition.c
+++ b/filters/partition/partition.c
@@ -50,7 +50,7 @@ unsigned partnum = 0;

 /* Called for each key=value passed on the command line. */
 static int
-partition_config (nbdkit_next_config *next, void *nxdata,
+partition_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
                   const char *key, const char *value)
 {
   if (strcmp (key, "partition") == 0) {
@@ -68,7 +68,8 @@ partition_config (nbdkit_next_config *next, void *nxdata,

 /* Check the user did pass partition number. */
 static int
-partition_config_complete (nbdkit_next_config_complete *next, void *nxdata)
+partition_config_complete (nbdkit_next_config_complete *next,
+                           nbdkit_backend *nxdata)
 {
   if (partnum == 0) {
     nbdkit_error ("you must supply the partition parameter on the command line");
@@ -89,7 +90,7 @@ struct handle {

 /* Open a connection. */
 static void *
-partition_open (nbdkit_next_open *next, void *nxdata,
+partition_open (nbdkit_next_open *next, nbdkit_context *nxdata,
                 int readonly, const char *exportname, int is_tls)
 {
   struct handle *h;
@@ -117,7 +118,7 @@ partition_close (void *handle)
 }

 static int
-partition_prepare (struct nbdkit_next_ops *next_ops, void *nxdata,
+partition_prepare (nbdkit_next *next,
                    void *handle, int readonly)
 {
   struct handle *h = handle;
@@ -126,7 +127,7 @@ partition_prepare (struct nbdkit_next_ops *next_ops, void *nxdata,
   int r;
   int err;

-  size = next_ops->get_size (nxdata);
+  size = next->get_size (next);
   if (size == -1)
     return -1;
   if (size < 2 * SECTOR_SIZE) {
@@ -136,20 +137,19 @@ partition_prepare (struct nbdkit_next_ops *next_ops, void *nxdata,

   nbdkit_debug ("disk size=%" PRIi64, size);

-  if (next_ops->pread (nxdata, lba01, sizeof lba01, 0, 0, &err) == -1)
+  if (next->pread (next, lba01, sizeof lba01, 0, 0, &err) == -1)
     return -1;

   /* Is it GPT? */
   if (size >= 2 * 34 * SECTOR_SIZE &&
       memcmp (&lba01[SECTOR_SIZE], "EFI PART", 8) == 0) {
-    r = find_gpt_partition (next_ops, nxdata, size, &lba01[SECTOR_SIZE],
-                            &h->offset, &h->range);
+    r = find_gpt_partition (next, size, &lba01[SECTOR_SIZE], &h->offset,
+                            &h->range);
     h->type = "GPT";
   }
   /* Is it MBR? */
   else if (lba01[0x1fe] == 0x55 && lba01[0x1ff] == 0xAA) {
-    r = find_mbr_partition (next_ops, nxdata, size, lba01,
-                            &h->offset, &h->range);
+    r = find_mbr_partition (next, size, lba01, &h->offset, &h->range);
     h->type = "MBR";
   }
   else {
@@ -176,11 +176,11 @@ partition_prepare (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Description. */
 static const char *
-partition_export_description (struct nbdkit_next_ops *next_ops, void *nxdata,
+partition_export_description (nbdkit_next *next,
                               void *handle)
 {
   struct handle *h = handle;
-  const char *base = next_ops->export_description (nxdata);
+  const char *base = next->export_description (next);

   assert (h->type);
   if (!base)
@@ -191,7 +191,7 @@ partition_export_description (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Get the file size. */
 static int64_t
-partition_get_size (struct nbdkit_next_ops *next_ops, void *nxdata,
+partition_get_size (nbdkit_next *next,
                     void *handle)
 {
   struct handle *h = handle;
@@ -201,52 +201,52 @@ partition_get_size (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Read data. */
 static int
-partition_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
+partition_pread (nbdkit_next *next,
                  void *handle, void *buf, uint32_t count, uint64_t offs,
                  uint32_t flags, int *err)
 {
   struct handle *h = handle;

-  return next_ops->pread (nxdata, buf, count, offs + h->offset, flags, err);
+  return next->pread (next, buf, count, offs + h->offset, flags, err);
 }

 /* Write data. */
 static int
-partition_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
+partition_pwrite (nbdkit_next *next,
                   void *handle,
                   const void *buf, uint32_t count, uint64_t offs,
                   uint32_t flags, int *err)
 {
   struct handle *h = handle;

-  return next_ops->pwrite (nxdata, buf, count, offs + h->offset, flags, err);
+  return next->pwrite (next, buf, count, offs + h->offset, flags, err);
 }

 /* Trim data. */
 static int
-partition_trim (struct nbdkit_next_ops *next_ops, void *nxdata,
+partition_trim (nbdkit_next *next,
                 void *handle, uint32_t count, uint64_t offs, uint32_t flags,
                 int *err)
 {
   struct handle *h = handle;

-  return next_ops->trim (nxdata, count, offs + h->offset, flags, err);
+  return next->trim (next, count, offs + h->offset, flags, err);
 }

 /* Zero data. */
 static int
-partition_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+partition_zero (nbdkit_next *next,
                 void *handle, uint32_t count, uint64_t offs, uint32_t flags,
                 int *err)
 {
   struct handle *h = handle;

-  return next_ops->zero (nxdata, count, offs + h->offset, flags, err);
+  return next->zero (next, count, offs + h->offset, flags, err);
 }

 /* Extents. */
 static int
-partition_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
+partition_extents (nbdkit_next *next,
                    void *handle, uint32_t count, uint64_t offs, uint32_t flags,
                    struct nbdkit_extents *extents, int *err)
 {
@@ -260,8 +260,8 @@ partition_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
     *err = errno;
     return -1;
   }
-  if (next_ops->extents (nxdata, count, offs + h->offset,
-                         flags, extents2, err) == -1)
+  if (next->extents (next, count, offs + h->offset, flags, extents2,
+                     err) == -1)
     return -1;

   for (i = 0; i < nbdkit_extents_count (extents2); ++i) {
@@ -277,13 +277,13 @@ partition_extents (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Cache data. */
 static int
-partition_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
+partition_cache (nbdkit_next *next,
                  void *handle, uint32_t count, uint64_t offs, uint32_t flags,
                  int *err)
 {
   struct handle *h = handle;

-  return next_ops->cache (nxdata, count, offs + h->offset, flags, err);
+  return next->cache (next, count, offs + h->offset, flags, err);
 }

 static struct nbdkit_filter filter = {
diff --git a/filters/pause/pause.c b/filters/pause/pause.c
index 62d44391..90511b93 100644
--- a/filters/pause/pause.c
+++ b/filters/pause/pause.c
@@ -64,7 +64,7 @@ pause_unload (void)
 }

 static int
-pause_config (nbdkit_next_config *next, void *nxdata,
+pause_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
               const char *key, const char *value)
 {
   if (strcmp (key, "pause-control") == 0) {
@@ -79,7 +79,8 @@ pause_config (nbdkit_next_config *next, void *nxdata,
 }

 static int
-pause_config_complete (nbdkit_next_config_complete *next, void *nxdata)
+pause_config_complete (nbdkit_next_config_complete *next,
+                       nbdkit_backend *nxdata)
 {
   size_t len;
   struct sockaddr_un addr;
@@ -230,7 +231,7 @@ control_socket_thread (void *arg)

 /* Start the background thread after fork. */
 static int
-pause_after_fork (nbdkit_next_after_fork *next, void *nxdata)
+pause_after_fork (nbdkit_next_after_fork *next, nbdkit_backend *nxdata)
 {
   int err;
   pthread_t thread;
@@ -267,21 +268,21 @@ end_request (void)

 /* Read data. */
 static int
-pause_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
+pause_pread (nbdkit_next *next,
              void *handle, void *buf, uint32_t count, uint64_t offset,
              uint32_t flags, int *err)
 {
   int r;

   begin_request ();
-  r = next_ops->pread (nxdata, buf, count, offset, flags, err);
+  r = next->pread (next, buf, count, offset, flags, err);
   end_request ();
   return r;
 }

 /* Write data. */
 static int
-pause_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
+pause_pwrite (nbdkit_next *next,
               void *handle,
               const void *buf, uint32_t count, uint64_t offset, uint32_t flags,
               int *err)
@@ -289,63 +290,63 @@ pause_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
   int r;

   begin_request ();
-  r = next_ops->pwrite (nxdata, buf, count, offset, flags, err);
+  r = next->pwrite (next, buf, count, offset, flags, err);
   end_request ();
   return r;
 }

 /* Zero data. */
 static int
-pause_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+pause_zero (nbdkit_next *next,
             void *handle, uint32_t count, uint64_t offset, uint32_t flags,
             int *err)
 {
   int r;

   begin_request ();
-  r = next_ops->zero (nxdata, count, offset, flags, err);
+  r = next->zero (next, count, offset, flags, err);
   end_request ();
   return r;
 }

 /* Trim data. */
 static int
-pause_trim (struct nbdkit_next_ops *next_ops, void *nxdata,
+pause_trim (nbdkit_next *next,
             void *handle, uint32_t count, uint64_t offset,
             uint32_t flags, int *err)
 {
   int r;

   begin_request ();
-  r = next_ops->trim (nxdata, count, offset, flags, err);
+  r = next->trim (next, count, offset, flags, err);
   end_request ();
   return r;
 }

 /* Extents. */
 static int
-pause_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
+pause_extents (nbdkit_next *next,
                void *handle, uint32_t count, uint64_t offset, uint32_t flags,
                struct nbdkit_extents *extents, int *err)
 {
   int r;

   begin_request ();
-  r = next_ops->extents (nxdata, count, offset, flags, extents, err);
+  r = next->extents (next, count, offset, flags, extents, err);
   end_request ();
   return r;
 }

 /* Cache. */
 static int
-pause_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
+pause_cache (nbdkit_next *next,
              void *handle, uint32_t count, uint64_t offset, uint32_t flags,
              int *err)
 {
   int r;

   begin_request ();
-  r = next_ops->cache (nxdata, count, offset, flags, err);
+  r = next->cache (next, count, offset, flags, err);
   end_request ();
   return r;
 }
diff --git a/filters/rate/rate.c b/filters/rate/rate.c
index 103eae0b..7b07b5bf 100644
--- a/filters/rate/rate.c
+++ b/filters/rate/rate.c
@@ -97,7 +97,7 @@ rate_unload (void)

 /* Called for each key=value passed on the command line. */
 static int
-rate_config (nbdkit_next_config *next, void *nxdata,
+rate_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
              const char *key, const char *value)
 {
   if (strcmp (key, "rate") == 0) {
@@ -147,7 +147,8 @@ rate_config (nbdkit_next_config *next, void *nxdata,
 }

 static int
-rate_get_ready (nbdkit_next_get_ready *next, void *nxdata, int thread_model)
+rate_get_ready (nbdkit_next_get_ready *next, nbdkit_backend *nxdata,
+                int thread_model)
 {
   /* Initialize the global buckets. */
   bucket_init (&read_bucket, rate, BUCKET_CAPACITY);
@@ -164,7 +165,7 @@ rate_get_ready (nbdkit_next_get_ready *next, void *nxdata, int thread_model)

 /* Create the per-connection handle. */
 static void *
-rate_open (nbdkit_next_open *next, void *nxdata,
+rate_open (nbdkit_next_open *next, nbdkit_context *nxdata,
            int readonly, const char *exportname, int is_tls)
 {
   struct rate_handle *h;
@@ -275,7 +276,7 @@ maybe_sleep (struct bucket *bucket, pthread_mutex_t *lock, uint32_t count,

 /* Read data. */
 static int
-rate_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
+rate_pread (nbdkit_next *next,
             void *handle, void *buf, uint32_t count, uint64_t offset,
             uint32_t flags, int *err)
 {
@@ -288,12 +289,12 @@ rate_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
   if (maybe_sleep (&h->read_bucket, &h->read_bucket_lock, count, err))
     return -1;

-  return next_ops->pread (nxdata, buf, count, offset, flags, err);
+  return next->pread (next, buf, count, offset, flags, err);
 }

 /* Write data. */
 static int
-rate_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
+rate_pwrite (nbdkit_next *next,
              void *handle,
              const void *buf, uint32_t count, uint64_t offset, uint32_t flags,
              int *err)
@@ -307,7 +308,7 @@ rate_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
   if (maybe_sleep (&h->write_bucket, &h->write_bucket_lock, count, err))
     return -1;

-  return next_ops->pwrite (nxdata, buf, count, offset, flags, err);
+  return next->pwrite (next, buf, count, offset, flags, err);
 }

 static struct nbdkit_filter filter = {
diff --git a/filters/readahead/readahead.c b/filters/readahead/readahead.c
index f0e67a67..bb6f55c7 100644
--- a/filters/readahead/readahead.c
+++ b/filters/readahead/readahead.c
@@ -76,28 +76,27 @@ readahead_unload (void)
   free (buffer);
 }

-static int64_t readahead_get_size (struct nbdkit_next_ops *next_ops,
-                                   void *nxdata, void *handle);
+static int64_t readahead_get_size (nbdkit_next *next, void *handle);

 /* In prepare, force a call to get_size which sets the size global. */
 static int
-readahead_prepare (struct nbdkit_next_ops *next_ops, void *nxdata,
+readahead_prepare (nbdkit_next *next,
                    void *handle, int readonly)
 {
   int64_t r;

-  r = readahead_get_size (next_ops, nxdata, handle);
+  r = readahead_get_size (next, handle);
   return r >= 0 ? 0 : -1;
 }

 /* Get the size. */
 static int64_t
-readahead_get_size (struct nbdkit_next_ops *next_ops, void *nxdata,
+readahead_get_size (nbdkit_next *next,
                     void *handle)
 {
   int64_t r;

-  r = next_ops->get_size (nxdata);
+  r = next->get_size (next);
   if (r == -1)
     return -1;

@@ -109,7 +108,7 @@ readahead_get_size (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Cache */
 static int
-readahead_can_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
+readahead_can_cache (nbdkit_next *next,
                      void *handle)
 {
   /* We are already operating as a cache regardless of the plugin's
@@ -122,7 +121,7 @@ readahead_can_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
 /* Read data. */

 static int
-fill_readahead (struct nbdkit_next_ops *next_ops, void *nxdata,
+fill_readahead (nbdkit_next *next,
                 uint32_t count, uint64_t offset, uint32_t flags, int *err)
 {
   position = offset;
@@ -147,7 +146,7 @@ fill_readahead (struct nbdkit_next_ops *next_ops, void *nxdata,
     bufsize = length;
   }

-  if (next_ops->pread (nxdata, buffer, length, offset, flags, err) == -1) {
+  if (next->pread (next, buffer, length, offset, flags, err) == -1) {
     length = 0;           /* failed to fill the prefetch buffer */
     return -1;
   }
@@ -156,7 +155,7 @@ fill_readahead (struct nbdkit_next_ops *next_ops, void *nxdata,
 }

 static int
-readahead_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
+readahead_pread (nbdkit_next *next,
                  void *handle, void *buf, uint32_t count, uint64_t offset,
                  uint32_t flags, int *err)
 {
@@ -168,7 +167,7 @@ readahead_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
        * first request or reset after a miss.
        */
       window = READAHEAD_MIN;
-      if (fill_readahead (next_ops, nxdata, count, offset, flags, err) == -1)
+      if (fill_readahead (next, count, offset, flags, err) == -1)
         return -1;
     }

@@ -188,7 +187,7 @@ readahead_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
      */
     else if (offset == position + length) {
       window = MIN (window * 2, READAHEAD_MAX);
-      if (fill_readahead (next_ops, nxdata, count, offset, flags, err) == -1)
+      if (fill_readahead (next, count, offset, flags, err) == -1)
         return -1;
     }

@@ -215,41 +214,41 @@ kill_readahead (void)
 }

 static int
-readahead_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
+readahead_pwrite (nbdkit_next *next,
                   void *handle,
                   const void *buf, uint32_t count, uint64_t offset,
                   uint32_t flags, int *err)
 {
   kill_readahead ();
-  return next_ops->pwrite (nxdata, buf, count, offset, flags, err);
+  return next->pwrite (next, buf, count, offset, flags, err);
 }

 static int
-readahead_trim (struct nbdkit_next_ops *next_ops, void *nxdata,
+readahead_trim (nbdkit_next *next,
                 void *handle,
                 uint32_t count, uint64_t offset, uint32_t flags,
                 int *err)
 {
   kill_readahead ();
-  return next_ops->trim (nxdata, count, offset, flags, err);
+  return next->trim (next, count, offset, flags, err);
 }

 static int
-readahead_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+readahead_zero (nbdkit_next *next,
                 void *handle,
                 uint32_t count, uint64_t offset, uint32_t flags,
                 int *err)
 {
   kill_readahead ();
-  return next_ops->zero (nxdata, count, offset, flags, err);
+  return next->zero (next, count, offset, flags, err);
 }

 static int
-readahead_flush (struct nbdkit_next_ops *next_ops, void *nxdata,
+readahead_flush (nbdkit_next *next,
                  void *handle, uint32_t flags, int *err)
 {
   kill_readahead ();
-  return next_ops->flush (nxdata, flags, err);
+  return next->flush (next, flags, err);
 }

 static struct nbdkit_filter filter = {
diff --git a/filters/retry/retry.c b/filters/retry/retry.c
index 22527c7d..f1c5d37f 100644
--- a/filters/retry/retry.c
+++ b/filters/retry/retry.c
@@ -40,7 +40,6 @@
 #include <string.h>
 #include <sys/time.h>

-#define NBDKIT_TYPESAFE /* Hack to expose context APIs */
 #include <nbdkit-filter.h>

 #include "cleanup.h"
@@ -164,7 +163,7 @@ struct retry_data {
 };

 static bool
-valid_range (struct nbdkit_next_ops *next,
+valid_range (nbdkit_next *next,
              uint32_t count, uint64_t offset, bool is_write, int *err)
 {
   if ((int64_t) offset + count > next->get_size (next)) {
@@ -249,7 +248,7 @@ do_retry (struct retry_handle *h, struct retry_data *data,
 }

 static int
-retry_pread (struct nbdkit_next_ops *next_ops, nbdkit_next *next,
+retry_pread (nbdkit_next *next,
              void *handle, void *buf, uint32_t count, uint64_t offset,
              uint32_t flags, int *err)
 {
@@ -270,7 +269,7 @@ retry_pread (struct nbdkit_next_ops *next_ops, nbdkit_next *next,

 /* Write. */
 static int
-retry_pwrite (struct nbdkit_next_ops *next_ops, nbdkit_next *next,
+retry_pwrite (nbdkit_next *next,
               void *handle,
               const void *buf, uint32_t count, uint64_t offset,
               uint32_t flags, int *err)
@@ -305,7 +304,7 @@ retry_pwrite (struct nbdkit_next_ops *next_ops, nbdkit_next *next,

 /* Trim. */
 static int
-retry_trim (struct nbdkit_next_ops *next_ops, nbdkit_next *next,
+retry_trim (nbdkit_next *next,
             void *handle,
             uint32_t count, uint64_t offset, uint32_t flags,
             int *err)
@@ -340,7 +339,7 @@ retry_trim (struct nbdkit_next_ops *next_ops, nbdkit_next *next,

 /* Flush. */
 static int
-retry_flush (struct nbdkit_next_ops *next_ops, nbdkit_next *next,
+retry_flush (nbdkit_next *next,
              void *handle, uint32_t flags,
              int *err)
 {
@@ -365,7 +364,7 @@ retry_flush (struct nbdkit_next_ops *next_ops, nbdkit_next *next,

 /* Zero. */
 static int
-retry_zero (struct nbdkit_next_ops *next_ops, nbdkit_next *next,
+retry_zero (nbdkit_next *next,
             void *handle,
             uint32_t count, uint64_t offset, uint32_t flags,
             int *err)
@@ -405,7 +404,7 @@ retry_zero (struct nbdkit_next_ops *next_ops, nbdkit_next *next,

 /* Extents. */
 static int
-retry_extents (struct nbdkit_next_ops *next_ops, nbdkit_next *next,
+retry_extents (nbdkit_next *next,
                void *handle,
                uint32_t count, uint64_t offset, uint32_t flags,
                struct nbdkit_extents *extents, int *err)
@@ -453,7 +452,7 @@ retry_extents (struct nbdkit_next_ops *next_ops, nbdkit_next *next,

 /* Cache. */
 static int
-retry_cache (struct nbdkit_next_ops *next_ops, nbdkit_next *next,
+retry_cache (nbdkit_next *next,
              void *handle,
              uint32_t count, uint64_t offset, uint32_t flags,
              int *err)
diff --git a/filters/stats/stats.c b/filters/stats/stats.c
index 639ceacf..7fd043a3 100644
--- a/filters/stats/stats.c
+++ b/filters/stats/stats.c
@@ -176,7 +176,7 @@ stats_unload (void)
 }

 static int
-stats_config (nbdkit_next_config *next, void *nxdata,
+stats_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
               const char *key, const char *value)
 {
   int r;
@@ -200,7 +200,8 @@ stats_config (nbdkit_next_config *next, void *nxdata,
 }

 static int
-stats_config_complete (nbdkit_next_config_complete *next, void *nxdata)
+stats_config_complete (nbdkit_next_config_complete *next,
+                       nbdkit_backend *nxdata)
 {
   if (filename == NULL) {
     nbdkit_error ("stats filter requires statsfile parameter");
@@ -211,7 +212,8 @@ stats_config_complete (nbdkit_next_config_complete *next, void *nxdata)
 }

 static int
-stats_get_ready (nbdkit_next_get_ready *next, void *nxdata, int thread_model)
+stats_get_ready (nbdkit_next_get_ready *next, nbdkit_backend *nxdata,
+                 int thread_model)
 {
   int fd;

@@ -260,7 +262,7 @@ record_stat (nbdstat *st, uint32_t count, const struct timeval *start)

 /* Read. */
 static int
-stats_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
+stats_pread (nbdkit_next *next,
              void *handle, void *buf, uint32_t count, uint64_t offset,
              uint32_t flags, int *err)
 {
@@ -268,14 +270,14 @@ stats_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
   int r;

   gettimeofday (&start, NULL);
-  r = next_ops->pread (nxdata, buf, count, offset, flags, err);
+  r = next->pread (next, buf, count, offset, flags, err);
   if (r == 0) record_stat (&pread_st, count, &start);
   return r;
 }

 /* Write. */
 static int
-stats_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
+stats_pwrite (nbdkit_next *next,
               void *handle,
               const void *buf, uint32_t count, uint64_t offset,
               uint32_t flags, int *err)
@@ -284,14 +286,14 @@ stats_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
   int r;

   gettimeofday (&start, NULL);
-  r = next_ops->pwrite (nxdata, buf, count, offset, flags, err);
+  r = next->pwrite (next, buf, count, offset, flags, err);
   if (r == 0) record_stat (&pwrite_st, count, &start);
   return r;
 }

 /* Trim. */
 static int
-stats_trim (struct nbdkit_next_ops *next_ops, void *nxdata,
+stats_trim (nbdkit_next *next,
             void *handle,
             uint32_t count, uint64_t offset, uint32_t flags,
             int *err)
@@ -300,14 +302,14 @@ stats_trim (struct nbdkit_next_ops *next_ops, void *nxdata,
   int r;

   gettimeofday (&start, NULL);
-  r = next_ops->trim (nxdata, count, offset, flags, err);
+  r = next->trim (next, count, offset, flags, err);
   if (r == 0) record_stat (&trim_st, count, &start);
   return r;
 }

 /* Flush. */
 static int
-stats_flush (struct nbdkit_next_ops *next_ops, void *nxdata,
+stats_flush (nbdkit_next *next,
              void *handle, uint32_t flags,
              int *err)
 {
@@ -315,14 +317,14 @@ stats_flush (struct nbdkit_next_ops *next_ops, void *nxdata,
   int r;

   gettimeofday (&start, NULL);
-  r = next_ops->flush (nxdata, flags, err);
+  r = next->flush (next, flags, err);
   if (r == 0) record_stat (&flush_st, 0, &start);
   return r;
 }

 /* Zero. */
 static int
-stats_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+stats_zero (nbdkit_next *next,
             void *handle,
             uint32_t count, uint64_t offset, uint32_t flags,
             int *err)
@@ -331,14 +333,14 @@ stats_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
   int r;

   gettimeofday (&start, NULL);
-  r = next_ops->zero (nxdata, count, offset, flags, err);
+  r = next->zero (next, count, offset, flags, err);
   if (r == 0) record_stat (&zero_st, count, &start);
   return r;
 }

 /* Extents. */
 static int
-stats_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
+stats_extents (nbdkit_next *next,
                void *handle,
                uint32_t count, uint64_t offset, uint32_t flags,
                struct nbdkit_extents *extents, int *err)
@@ -347,7 +349,7 @@ stats_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
   int r;

   gettimeofday (&start, NULL);
-  r = next_ops->extents (nxdata, count, offset, flags, extents, err);
+  r = next->extents (next, count, offset, flags, extents, err);
   /* XXX There's a case for trying to determine how long the extents
    * will be that are returned to the client (instead of simply using
    * count), given the flags and the complex rules in the protocol.
@@ -358,7 +360,7 @@ stats_extents (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Cache. */
 static int
-stats_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
+stats_cache (nbdkit_next *next,
              void *handle,
              uint32_t count, uint64_t offset, uint32_t flags,
              int *err)
@@ -367,7 +369,7 @@ stats_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
   int r;

   gettimeofday (&start, NULL);
-  r = next_ops->cache (nxdata, count, offset, flags, err);
+  r = next->cache (next, count, offset, flags, err);
   if (r == 0) record_stat (&cache_st, count, &start);
   return r;
 }
diff --git a/filters/swab/swab.c b/filters/swab/swab.c
index c9f4b066..68776eee 100644
--- a/filters/swab/swab.c
+++ b/filters/swab/swab.c
@@ -51,7 +51,7 @@ static int bits = 16;

 /* Called for each key=value passed on the command line. */
 static int
-swab_config (nbdkit_next_config *next, void *nxdata,
+swab_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
              const char *key, const char *value)
 {
   int r;
@@ -75,10 +75,10 @@ swab_config (nbdkit_next_config *next, void *nxdata,

 /* Round size down to avoid issues at end of file. */
 static int64_t
-swab_get_size (struct nbdkit_next_ops *next_ops, void *nxdata,
+swab_get_size (nbdkit_next *next,
                void *handle)
 {
-  int64_t size = next_ops->get_size (nxdata);
+  int64_t size = next->get_size (next);

   if (size == -1)
     return -1;
@@ -133,7 +133,7 @@ buf_bswap (void *dest, const void *src, uint32_t count)

 /* Read data. */
 static int
-swab_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
+swab_pread (nbdkit_next *next,
             void *handle, void *buf, uint32_t count, uint64_t offset,
             uint32_t flags, int *err)
 {
@@ -141,7 +141,7 @@ swab_pread (struct nbdkit_next_ops *next_ops, void *nxdata,

   if (!is_aligned (count, offset, err)) return -1;

-  r = next_ops->pread (nxdata, buf, count, offset, flags, err);
+  r = next->pread (next, buf, count, offset, flags, err);
   if (r == -1)
     return -1;

@@ -152,7 +152,7 @@ swab_pread (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Write data. */
 static int
-swab_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
+swab_pwrite (nbdkit_next *next,
              void *handle, const void *buf, uint32_t count, uint64_t offset,
              uint32_t flags, int *err)
 {
@@ -169,48 +169,48 @@ swab_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,

   buf_bswap (block, buf, count);

-  return next_ops->pwrite (nxdata, block, count, offset, flags, err);
+  return next->pwrite (next, block, count, offset, flags, err);
 }

 /* Trim data. */
 static int
-swab_trim (struct nbdkit_next_ops *next_ops, void *nxdata,
+swab_trim (nbdkit_next *next,
            void *handle, uint32_t count, uint64_t offset, uint32_t flags,
            int *err)
 {
   if (!is_aligned (count, offset, err)) return -1;
-  return next_ops->trim (nxdata, count, offset, flags, err);
+  return next->trim (next, count, offset, flags, err);
 }

 /* Zero data. */
 static int
-swab_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+swab_zero (nbdkit_next *next,
            void *handle, uint32_t count, uint64_t offset, uint32_t flags,
            int *err)
 {
   if (!is_aligned (count, offset, err)) return -1;
-  return next_ops->zero (nxdata, count, offset, flags, err);
+  return next->zero (next, count, offset, flags, err);
 }

 /* Extents. */
 static int
-swab_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
+swab_extents (nbdkit_next *next,
               void *handle, uint32_t count, uint64_t offset, uint32_t flags,
               struct nbdkit_extents *extents, int *err)
 {
   if (!is_aligned (count, offset, err)) return -1;
-  return nbdkit_extents_aligned (next_ops, nxdata, count, offset, flags,
-                                 bits/8, extents, err);
+  return nbdkit_extents_aligned (next, count, offset, flags, bits / 8, extents,
+                                 err);
 }

 /* Cache. */
 static int
-swab_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
+swab_cache (nbdkit_next *next,
             void *handle, uint32_t count, uint64_t offset, uint32_t flags,
             int *err)
 {
   if (!is_aligned (count, offset, err)) return -1;
-  return next_ops->cache (nxdata, count, offset, flags, err);
+  return next->cache (next, count, offset, flags, err);
 }


diff --git a/filters/tar/tar.c b/filters/tar/tar.c
index 7cf2cd26..b9f40784 100644
--- a/filters/tar/tar.c
+++ b/filters/tar/tar.c
@@ -65,7 +65,7 @@ static bool initialized = false;
 static uint64_t tar_offset, tar_size;

 static int
-tar_config (nbdkit_next_config *next, void *nxdata,
+tar_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
             const char *key, const char *value)
 {
   if (strcmp (key, "tar-entry") == 0) {
@@ -85,7 +85,8 @@ tar_config (nbdkit_next_config *next, void *nxdata,
 }

 static int
-tar_config_complete (nbdkit_next_config_complete *next, void *nxdata)
+tar_config_complete (nbdkit_next_config_complete *next,
+                     nbdkit_backend *nxdata)
 {
   if (entry == NULL) {
     nbdkit_error ("you must supply the tar-entry=<FILENAME> parameter");
@@ -139,7 +140,7 @@ tar_close (void *handle)
  * https://www.redhat.com/archives/libguestfs/2020-July/msg00017.html
  */
 static int
-calculate_offset_of_entry (struct nbdkit_next_ops *next_ops, void *nxdata)
+calculate_offset_of_entry (nbdkit_next *next)
 {
   const size_t bufsize = 65536;
   char output[] = "/tmp/tarXXXXXX";
@@ -191,7 +192,7 @@ calculate_offset_of_entry (struct nbdkit_next_ops *next_ops, void *nxdata)
     nbdkit_error ("malloc: %m");
     return -1;
   }
-  copysize = next_ops->get_size (nxdata);
+  copysize = next->get_size (next);
   if (copysize == -1)
     return -1;

@@ -215,7 +216,7 @@ calculate_offset_of_entry (struct nbdkit_next_ops *next_ops, void *nxdata)
     int64_t j;
     struct stat statbuf;

-    r = next_ops->pread (nxdata, buf, count, i, 0, &err);
+    r = next->pread (next, buf, count, i, 0, &err);
     if (r == -1) {
       errno = err;
       nbdkit_error ("pread: %m");
@@ -277,14 +278,14 @@ calculate_offset_of_entry (struct nbdkit_next_ops *next_ops, void *nxdata)
 }

 static int
-tar_prepare (struct nbdkit_next_ops *next_ops, void *nxdata,
+tar_prepare (nbdkit_next *next,
              void *handle, int readonly)
 {
   struct handle *h = handle;
   ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);

   if (!initialized) {
-    if (calculate_offset_of_entry (next_ops, nxdata) == -1)
+    if (calculate_offset_of_entry (next) == -1)
       return -1;
   }

@@ -297,10 +298,10 @@ tar_prepare (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Description. */
 static const char *
-tar_export_description (struct nbdkit_next_ops *next_ops, void *nxdata,
+tar_export_description (nbdkit_next *next,
                         void *handle)
 {
-  const char *base = next_ops->export_description (nxdata);
+  const char *base = next->export_description (next);

   if (!base)
     return NULL;
@@ -310,7 +311,7 @@ tar_export_description (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Get the file size. */
 static int64_t
-tar_get_size (struct nbdkit_next_ops *next_ops, void *nxdata,
+tar_get_size (nbdkit_next *next,
               void *handle)
 {
   struct handle *h = handle;
@@ -319,7 +320,7 @@ tar_get_size (struct nbdkit_next_ops *next_ops, void *nxdata,
   /* We must call underlying get_size even though we don't use the
    * result, because it caches the plugin size in server/backend.c.
    */
-  size = next_ops->get_size (nxdata);
+  size = next->get_size (next);
   if (size == -1)
     return -1;

@@ -328,47 +329,47 @@ tar_get_size (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Read data from the file. */
 static int
-tar_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
+tar_pread (nbdkit_next *next,
            void *handle, void *buf, uint32_t count, uint64_t offs,
            uint32_t flags, int *err)
 {
   struct handle *h = handle;
-  return next_ops->pread (nxdata, buf, count, offs + h->offset, flags, err);
+  return next->pread (next, buf, count, offs + h->offset, flags, err);
 }

 /* Write data to the file. */
 static int
-tar_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
+tar_pwrite (nbdkit_next *next,
             void *handle, const void *buf, uint32_t count, uint64_t offs,
             uint32_t flags, int *err)
 {
   struct handle *h = handle;
-  return next_ops->pwrite (nxdata, buf, count, offs + h->offset, flags, err);
+  return next->pwrite (next, buf, count, offs + h->offset, flags, err);
 }

 /* Trim data. */
 static int
-tar_trim (struct nbdkit_next_ops *next_ops, void *nxdata,
+tar_trim (nbdkit_next *next,
           void *handle, uint32_t count, uint64_t offs, uint32_t flags,
           int *err)
 {
   struct handle *h = handle;
-  return next_ops->trim (nxdata, count, offs + h->offset, flags, err);
+  return next->trim (next, count, offs + h->offset, flags, err);
 }

 /* Zero data. */
 static int
-tar_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+tar_zero (nbdkit_next *next,
           void *handle, uint32_t count, uint64_t offs, uint32_t flags,
           int *err)
 {
   struct handle *h = handle;
-  return next_ops->zero (nxdata, count, offs + h->offset, flags, err);
+  return next->zero (next, count, offs + h->offset, flags, err);
 }

 /* Extents. */
 static int
-tar_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
+tar_extents (nbdkit_next *next,
              void *handle, uint32_t count, uint64_t offs, uint32_t flags,
              struct nbdkit_extents *extents, int *err)
 {
@@ -382,8 +383,8 @@ tar_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
     *err = errno;
     return -1;
   }
-  if (next_ops->extents (nxdata, count, offs + h->offset,
-                         flags, extents2, err) == -1)
+  if (next->extents (next, count, offs + h->offset, flags, extents2,
+                     err) == -1)
     return -1;

   for (i = 0; i < nbdkit_extents_count (extents2); ++i) {
@@ -399,12 +400,12 @@ tar_extents (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Cache data. */
 static int
-tar_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
+tar_cache (nbdkit_next *next,
            void *handle, uint32_t count, uint64_t offs, uint32_t flags,
            int *err)
 {
   struct handle *h = handle;
-  return next_ops->cache (nxdata, count, offs + h->offset, flags, err);
+  return next->cache (next, count, offs + h->offset, flags, err);
 }

 static struct nbdkit_filter filter = {
diff --git a/filters/tls-fallback/tls-fallback.c b/filters/tls-fallback/tls-fallback.c
index bec983be..ee3e6784 100644
--- a/filters/tls-fallback/tls-fallback.c
+++ b/filters/tls-fallback/tls-fallback.c
@@ -49,7 +49,7 @@ static char message[512] NONSTRING = "This NBD server requires TLS "

 /* Called for each key=value passed on the command line. */
 static int
-tls_fallback_config (nbdkit_next_config *next, void *nxdata,
+tls_fallback_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
                      const char *key, const char *value)
 {
   if (strcmp (key, "tlsreadme") == 0) {
@@ -63,7 +63,7 @@ tls_fallback_config (nbdkit_next_config *next, void *nxdata,
   "tlsreadme=<MESSAGE>  Alternative contents for the plaintext dummy export.\n"

 int
-tls_fallback_get_ready (nbdkit_next_get_ready *next, void *nxdata,
+tls_fallback_get_ready (nbdkit_next_get_ready *next, nbdkit_backend *nxdata,
                         int thread_model)
 {
   if (thread_model == NBDKIT_THREAD_MODEL_SERIALIZE_CONNECTIONS) {
@@ -75,7 +75,8 @@ tls_fallback_get_ready (nbdkit_next_get_ready *next, void *nxdata,
 }

 static int
-tls_fallback_list_exports (nbdkit_next_list_exports *next, void *nxdata,
+tls_fallback_list_exports (nbdkit_next_list_exports *next,
+                           nbdkit_backend *nxdata,
                            int readonly, int is_tls,
                            struct nbdkit_exports *exports)
 {
@@ -85,7 +86,8 @@ tls_fallback_list_exports (nbdkit_next_list_exports *next, void *nxdata,
 }

 static const char *
-tls_fallback_default_export (nbdkit_next_default_export *next, void *nxdata,
+tls_fallback_default_export (nbdkit_next_default_export *next,
+                             nbdkit_backend *nxdata,
                              int readonly, int is_tls)
 {
   if (!is_tls)
@@ -101,7 +103,8 @@ tls_fallback_default_export (nbdkit_next_default_export *next, void *nxdata,
 #define NOT_TLS (handle == &message)

 static void *
-tls_fallback_open (nbdkit_next_open *next, void *nxdata, int readonly,
+tls_fallback_open (nbdkit_next_open *next, nbdkit_context *nxdata,
+                   int readonly,
                    const char *exportname, int is_tls)
 {
   /* We do NOT want to call next() when insecure, because we don't
@@ -120,79 +123,78 @@ tls_fallback_open (nbdkit_next_open *next, void *nxdata, int readonly,
  */

 static const char *
-tls_fallback_export_description (struct nbdkit_next_ops *next_ops,
-                                 void *nxdata, void *handle)
+tls_fallback_export_description (nbdkit_next *next, void *handle)
 {
   if (NOT_TLS)
     return NULL;
-  return next_ops->export_description (nxdata);
+  return next->export_description (next);
 }

 static int64_t
-tls_fallback_get_size (struct nbdkit_next_ops *next_ops, void *nxdata,
+tls_fallback_get_size (nbdkit_next *next,
                        void *handle)
 {
   if (NOT_TLS)
     return sizeof message;
-  return next_ops->get_size (nxdata);
+  return next->get_size (next);
 }

 static int
-tls_fallback_can_write (struct nbdkit_next_ops *next_ops, void *nxdata,
+tls_fallback_can_write (nbdkit_next *next,
                         void *handle)
 {
   if (NOT_TLS)
     return 0;
-  return next_ops->can_write (nxdata);
+  return next->can_write (next);
 }

 static int
-tls_fallback_can_flush (struct nbdkit_next_ops *next_ops, void *nxdata,
+tls_fallback_can_flush (nbdkit_next *next,
                         void *handle)
 {
   if (NOT_TLS)
     return 0;
-  return next_ops->can_flush (nxdata);
+  return next->can_flush (next);
 }

 static int
-tls_fallback_is_rotational (struct nbdkit_next_ops *next_ops, void *nxdata,
+tls_fallback_is_rotational (nbdkit_next *next,
                             void *handle)
 {
   if (NOT_TLS)
     return 0;
-  return next_ops->is_rotational (nxdata);
+  return next->is_rotational (next);
 }

 static int
-tls_fallback_can_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
+tls_fallback_can_extents (nbdkit_next *next,
                           void *handle)
 {
   if (NOT_TLS)
     return 0;
-  return next_ops->can_extents (nxdata);
+  return next->can_extents (next);
 }

 static int
-tls_fallback_can_multi_conn (struct nbdkit_next_ops *next_ops, void *nxdata,
+tls_fallback_can_multi_conn (nbdkit_next *next,
                              void *handle)
 {
   if (NOT_TLS)
     return 0;
-  return next_ops->can_multi_conn (nxdata);
+  return next->can_multi_conn (next);
 }

 static int
-tls_fallback_can_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
+tls_fallback_can_cache (nbdkit_next *next,
                         void *handle)
 {
   if (NOT_TLS)
     return NBDKIT_CACHE_NONE;
-  return next_ops->can_cache (nxdata);
+  return next->can_cache (next);
 }

 static int
-tls_fallback_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
+tls_fallback_pread (nbdkit_next *next,
                     void *handle, void *b, uint32_t count, uint64_t offs,
                     uint32_t flags, int *err)
 {
@@ -200,7 +202,7 @@ tls_fallback_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
     memcpy (b, message + offs, count);
     return 0;
   }
-  return next_ops->pread (nxdata, b, count, offs, flags, err);
+  return next->pread (next, b, count, offs, flags, err);
 }

 static struct nbdkit_filter filter = {
diff --git a/filters/truncate/truncate.c b/filters/truncate/truncate.c
index 00b5d8ce..10fb2fc9 100644
--- a/filters/truncate/truncate.c
+++ b/filters/truncate/truncate.c
@@ -85,7 +85,7 @@ parse_round_param (const char *key, const char *value, unsigned *ret)

 /* Called for each key=value passed on the command line. */
 static int
-truncate_config (nbdkit_next_config *next, void *nxdata,
+truncate_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
                  const char *key, const char *value)
 {
   if (strcmp (key, "truncate") == 0) {
@@ -124,7 +124,7 @@ struct handle {

 /* Open a connection. */
 static void *
-truncate_open (nbdkit_next_open *next, void *nxdata,
+truncate_open (nbdkit_next_open *next, nbdkit_context *nxdata,
                int readonly, const char *exportname, int is_tls)
 {
   struct handle *h;
@@ -149,18 +149,18 @@ truncate_close (void *handle)
   free (h);
 }

-/* In prepare, force a call to next_ops->get_size in order to set
+/* In prepare, force a call to next->get_size in order to set
  * per-connection real_size & size; these values are not changed
  * during the life of the connection.
  */
 static int
-truncate_prepare (struct nbdkit_next_ops *next_ops, void *nxdata,
+truncate_prepare (nbdkit_next *next,
                   void *handle, int readonly)
 {
   int64_t r;
   struct handle *h = handle;

-  r = next_ops->get_size (nxdata);
+  r = next->get_size (next);
   if (r == -1)
     return -1;

@@ -188,7 +188,7 @@ truncate_prepare (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Get the size. */
 static int64_t
-truncate_get_size (struct nbdkit_next_ops *next_ops, void *nxdata,
+truncate_get_size (nbdkit_next *next,
                    void *handle)
 {
   struct handle *h = handle;
@@ -204,13 +204,13 @@ truncate_get_size (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Advertise extents support. */
 static int
-truncate_can_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
+truncate_can_extents (nbdkit_next *next,
                       void *handle)
 {
   /* Advertise unconditional support for the image tail, but also call
-   * into next_ops to ensure next_ops->extents doesn't fail later.
+   * into next to ensure next->extents doesn't fail later.
    */
-  int r = next_ops->can_extents (nxdata);
+  int r = next->can_extents (next);
   if (r == -1)
     return -1;
   return 1;
@@ -218,13 +218,13 @@ truncate_can_extents (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Override the plugin's .can_fast_zero, because zeroing a tail is fast. */
 static int
-truncate_can_fast_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+truncate_can_fast_zero (nbdkit_next *next,
                         void *handle)
 {
-  /* Cache next_ops->can_fast_zero now, so later calls don't fail,
+  /* Cache next->can_fast_zero now, so later calls don't fail,
    * even though we override the answer here.
    */
-  int r = next_ops->can_fast_zero (nxdata);
+  int r = next->can_fast_zero (next);
   if (r == -1)
     return -1;
   return 1;
@@ -232,7 +232,7 @@ truncate_can_fast_zero (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Read data. */
 static int
-truncate_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
+truncate_pread (nbdkit_next *next,
                 void *handle, void *buf, uint32_t count, uint64_t offset,
                 uint32_t flags, int *err)
 {
@@ -245,7 +245,7 @@ truncate_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
       n = count;
     else
       n = h->real_size - offset;
-    r = next_ops->pread (nxdata, buf, n, offset, flags, err);
+    r = next->pread (next, buf, n, offset, flags, err);
     if (r == -1)
       return -1;
     count -= n;
@@ -260,7 +260,7 @@ truncate_pread (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Write data. */
 static int
-truncate_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
+truncate_pwrite (nbdkit_next *next,
                  void *handle,
                  const void *buf, uint32_t count, uint64_t offset,
                  uint32_t flags, int *err)
@@ -274,7 +274,7 @@ truncate_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
       n = count;
     else
       n = h->real_size - offset;
-    r = next_ops->pwrite (nxdata, buf, n, offset, flags, err);
+    r = next->pwrite (next, buf, n, offset, flags, err);
     if (r == -1)
       return -1;
     count -= n;
@@ -295,7 +295,7 @@ truncate_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Trim data. */
 static int
-truncate_trim (struct nbdkit_next_ops *next_ops, void *nxdata,
+truncate_trim (nbdkit_next *next,
                void *handle, uint32_t count, uint64_t offset,
                uint32_t flags, int *err)
 {
@@ -307,14 +307,14 @@ truncate_trim (struct nbdkit_next_ops *next_ops, void *nxdata,
       n = count;
     else
       n = h->real_size - offset;
-    return next_ops->trim (nxdata, n, offset, flags, err);
+    return next->trim (next, n, offset, flags, err);
   }
   return 0;
 }

 /* Zero data. */
 static int
-truncate_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+truncate_zero (nbdkit_next *next,
                void *handle, uint32_t count, uint64_t offset,
                uint32_t flags, int *err)
 {
@@ -327,18 +327,18 @@ truncate_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
     else
       n = h->real_size - offset;
     if (flags & NBDKIT_FLAG_FAST_ZERO &&
-        next_ops->can_fast_zero (nxdata) == 0) {
+        next->can_fast_zero (next) == 0) {
       *err = ENOTSUP;
       return -1;
     }
-    return next_ops->zero (nxdata, n, offset, flags, err);
+    return next->zero (next, n, offset, flags, err);
   }
   return 0;
 }

 /* Extents. */
 static int
-truncate_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
+truncate_extents (nbdkit_next *next,
                   void *handle, uint32_t count, uint64_t offset,
                   uint32_t flags, struct nbdkit_extents *extents, int *err)
 {
@@ -378,7 +378,7 @@ truncate_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
     n = count;
   else
     n = h->real_size - offset;
-  if (next_ops->extents (nxdata, n, offset, flags, extents2, err) == -1)
+  if (next->extents (next, n, offset, flags, extents2, err) == -1)
     return -1;

   for (i = 0; i < nbdkit_extents_count (extents2); ++i) {
@@ -395,7 +395,7 @@ truncate_extents (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Cache. */
 static int
-truncate_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
+truncate_cache (nbdkit_next *next,
                 void *handle, uint32_t count, uint64_t offset,
                 uint32_t flags, int *err)
 {
@@ -408,7 +408,7 @@ truncate_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
       n = count;
     else
       n = h->real_size - offset;
-    r = next_ops->cache (nxdata, n, offset, flags, err);
+    r = next->cache (next, n, offset, flags, err);
     if (r == -1)
       return -1;
   }
diff --git a/filters/xz/xz.c b/filters/xz/xz.c
index 63adb37e..5e717a8f 100644
--- a/filters/xz/xz.c
+++ b/filters/xz/xz.c
@@ -53,7 +53,7 @@ static uint64_t maxblock = 512 * 1024 * 1024;
 static uint32_t maxdepth = 8;

 static int
-xz_config (nbdkit_next_config *next, void *nxdata,
+xz_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
            const char *key, const char *value)
 {
   if (strcmp (key, "xz-max-block") == 0) {
@@ -90,7 +90,7 @@ struct xz_handle {

 /* Create the per-connection handle. */
 static void *
-xz_open (nbdkit_next_open *next, void *nxdata,
+xz_open (nbdkit_next_open *next, nbdkit_context *nxdata,
          int readonly, const char *exportname, int is_tls)
 {
   struct xz_handle *h;
@@ -133,12 +133,12 @@ xz_close (void *handle)
 }

 static int
-xz_prepare (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle,
+xz_prepare (nbdkit_next *next, void *handle,
             int readonly)
 {
   struct xz_handle *h = handle;

-  h->xz = xzfile_open (next_ops, nxdata);
+  h->xz = xzfile_open (next);
   if (!h->xz)
     return -1;

@@ -159,10 +159,10 @@ xz_prepare (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle,

 /* Description. */
 static const char *
-xz_export_description (struct nbdkit_next_ops *next_ops, void *nxdata,
+xz_export_description (nbdkit_next *next,
                        void *handle)
 {
-  const char *base = next_ops->export_description (nxdata);
+  const char *base = next->export_description (next);

   if (!base)
     return NULL;
@@ -171,7 +171,7 @@ xz_export_description (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Get the file size. */
 static int64_t
-xz_get_size (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle)
+xz_get_size (nbdkit_next *next, void *handle)
 {
   struct xz_handle *h = handle;

@@ -184,7 +184,7 @@ xz_get_size (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle)
  * below.  This is possibly a bug in nbdkit.
  */
 static int
-xz_can_write (struct nbdkit_next_ops *next_ops, void *nxdata,
+xz_can_write (nbdkit_next *next,
               void *handle)
 {
   return 0;
@@ -192,7 +192,7 @@ xz_can_write (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Whatever the plugin says, this filter is consistent across connections. */
 static int
-xz_can_multi_conn (struct nbdkit_next_ops *next_ops, void *nxdata,
+xz_can_multi_conn (nbdkit_next *next,
                    void *handle)
 {
   return 1;
@@ -202,7 +202,7 @@ xz_can_multi_conn (struct nbdkit_next_ops *next_ops, void *nxdata,
  * sparseness so in future we should generate extents information. XXX
  */
 static int
-xz_can_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
+xz_can_extents (nbdkit_next *next,
                 void *handle)
 {
   return 0;
@@ -210,7 +210,7 @@ xz_can_extents (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Cache */
 static int
-xz_can_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
+xz_can_cache (nbdkit_next *next,
               void *handle)
 {
   /* We are already operating as a cache regardless of the plugin's
@@ -222,7 +222,7 @@ xz_can_cache (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Read data from the file. */
 static int
-xz_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
+xz_pread (nbdkit_next *next,
           void *handle, void *buf, uint32_t count, uint64_t offset,
           uint32_t flags, int *err)
 {
@@ -235,7 +235,7 @@ xz_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
   data = get_block (h->c, offset, &start, &size);
   if (!data) {
     /* Not in the cache.  We need to read the block from the xz file. */
-    data = xzfile_read_block (h->xz, next_ops, nxdata, flags, err,
+    data = xzfile_read_block (h->xz, next, flags, err,
                               offset, &start, &size);
     if (data == NULL)
       return -1;
@@ -255,7 +255,7 @@ xz_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
   count -= n;
   offset += n;
   if (count > 0)
-    return xz_pread (next_ops, nxdata, h, buf, count, offset, flags, err);
+    return xz_pread (next, h, buf, count, offset, flags, err);

   return 0;
 }
diff --git a/filters/xz/xzfile.c b/filters/xz/xzfile.c
index 1ad010ef..287a8248 100644
--- a/filters/xz/xzfile.c
+++ b/filters/xz/xzfile.c
@@ -64,12 +64,12 @@ struct xzfile {
   uint64_t max_uncompressed_block_size;
 };

-static bool check_header_magic (struct nbdkit_next_ops *next_ops, void *nxdata);
-static lzma_index *parse_indexes (struct nbdkit_next_ops *next_ops, void *nxdata, size_t *);
+static bool check_header_magic (nbdkit_next *next);
+static lzma_index *parse_indexes (nbdkit_next *next, size_t *);
 static int iter_indexes (lzma_index *idx, size_t *, uint64_t *);

 xzfile *
-xzfile_open (struct nbdkit_next_ops *next_ops, void *nxdata)
+xzfile_open (nbdkit_next *next)
 {
   xzfile *xz;
   uint64_t size;
@@ -81,13 +81,13 @@ xzfile_open (struct nbdkit_next_ops *next_ops, void *nxdata)
   }

   /* Check file magic. */
-  if (!check_header_magic (next_ops, nxdata)) {
+  if (!check_header_magic (next)) {
     nbdkit_error ("xz: not an xz file");
     goto err1;
   }

   /* Read and parse the indexes. */
-  xz->idx = parse_indexes (next_ops, nxdata, &xz->nr_streams);
+  xz->idx = parse_indexes (next, &xz->nr_streams);
   if (xz->idx == NULL)
     goto err1;

@@ -112,16 +112,16 @@ xzfile_open (struct nbdkit_next_ops *next_ops, void *nxdata)
 }

 static bool
-check_header_magic (struct nbdkit_next_ops *next_ops, void *nxdata)
+check_header_magic (nbdkit_next *next)
 {
   char buf[XZ_HEADER_MAGIC_LEN];
   int err;

-  if (next_ops->get_size (nxdata) < XZ_HEADER_MAGIC_LEN) {
+  if (next->get_size (next) < XZ_HEADER_MAGIC_LEN) {
     nbdkit_error ("xz: file too short");
     return false;
   }
-  if (next_ops->pread (nxdata, buf, XZ_HEADER_MAGIC_LEN, 0, 0, &err) == -1) {
+  if (next->pread (next, buf, XZ_HEADER_MAGIC_LEN, 0, 0, &err) == -1) {
     nbdkit_error ("xz: could not read header magic: error %d", err);
     return false;
   }
@@ -134,7 +134,7 @@ check_header_magic (struct nbdkit_next_ops *next_ops, void *nxdata)
  * in the xz sources.
  */
 static lzma_index *
-parse_indexes (struct nbdkit_next_ops *next_ops, void *nxdata,
+parse_indexes (nbdkit_next *next,
                size_t *nr_streams)
 {
   lzma_ret r;
@@ -152,7 +152,7 @@ parse_indexes (struct nbdkit_next_ops *next_ops, void *nxdata,
   *nr_streams = 0;

   /* Check file size is a multiple of 4 bytes. */
-  pos = size = next_ops->get_size (nxdata);
+  pos = size = next->get_size (next);
   if (pos == -1) {
     nbdkit_error ("xz: get_size: %m");
     goto err;
@@ -171,9 +171,8 @@ parse_indexes (struct nbdkit_next_ops *next_ops, void *nxdata,
       goto err;
     }

-    if (next_ops->pread (nxdata, footer, LZMA_STREAM_HEADER_SIZE,
-                         pos - LZMA_STREAM_HEADER_SIZE,
-                         0, &err) == -1) {
+    if (next->pread (next, footer, LZMA_STREAM_HEADER_SIZE,
+                     pos - LZMA_STREAM_HEADER_SIZE, 0, &err) == -1) {
       nbdkit_error ("xz: read stream footer: error %d", err);
       goto err;
     }
@@ -224,7 +223,7 @@ parse_indexes (struct nbdkit_next_ops *next_ops, void *nxdata,
       if (pos + strm.avail_in > size)
         strm.avail_in = size - pos;

-      if (next_ops->pread (nxdata, buf, strm.avail_in, offs, 0, &err) == -1) {
+      if (next->pread (next, buf, strm.avail_in, offs, 0, &err) == -1) {
         nbdkit_error ("xz: read index: error %d", err);
         goto err;
       }
@@ -245,8 +244,8 @@ parse_indexes (struct nbdkit_next_ops *next_ops, void *nxdata,
     nbdkit_debug ("decode stream header at pos = %" PRIi64, pos);

     /* Read and decode the stream header. */
-    if (next_ops->pread (nxdata, header, LZMA_STREAM_HEADER_SIZE, pos, 0,
-                         &err) == -1) {
+    if (next->pread (next, header, LZMA_STREAM_HEADER_SIZE, pos, 0,
+                     &err) == -1) {
       nbdkit_error ("xz: read stream header: error %d", err);
       goto err;
     }
@@ -349,8 +348,8 @@ xzfile_get_size (xzfile *xz)

 char *
 xzfile_read_block (xzfile *xz,
-                   struct nbdkit_next_ops *next_ops,
-                   void *nxdata, uint32_t flags, int *err,
+                   nbdkit_next *next,
+                   uint32_t flags, int *err,
                    uint64_t offset,
                    uint64_t *start_rtn, uint64_t *size_rtn)
 {
@@ -369,7 +368,7 @@ xzfile_read_block (xzfile *xz,
   /* Read the total size of the underlying disk, so we don't
    * read over the end.
    */
-  size = next_ops->get_size (nxdata);
+  size = next->get_size (next);
   if (size == -1) {
     nbdkit_error ("xz: get_size: %m");
     return NULL;
@@ -393,7 +392,7 @@ xzfile_read_block (xzfile *xz,
    * tell us how big the block header is.
    */
   offs = iter.block.compressed_file_offset;
-  if (next_ops->pread (nxdata, header, 1, offs, 0, err) == -1) {
+  if (next->pread (next, header, 1, offs, 0, err) == -1) {
     nbdkit_error ("xz: read: could not read block header byte: error %d", *err);
     return NULL;
   }
@@ -410,8 +409,8 @@ xzfile_read_block (xzfile *xz,
   block.header_size = lzma_block_header_size_decode (header[0]);

   /* Now read and decode the block header. */
-  if (next_ops->pread (nxdata, &header[1], block.header_size-1, offs,
-                       0, err) == -1) {
+  if (next->pread (next, &header[1], block.header_size-1, offs,
+                   0, err) == -1) {
     nbdkit_error ("xz: read: could not read block of compressed data: "
                   "error %d", *err);
     return NULL;
@@ -467,7 +466,7 @@ xzfile_read_block (xzfile *xz,
         strm.avail_in = size - offs;
       if (strm.avail_in > 0) {
         strm.next_in = buf;
-        if (next_ops->pread (nxdata, buf, strm.avail_in, offs, 0, err) == -1) {
+        if (next->pread (next, buf, strm.avail_in, offs, 0, err) == -1) {
           nbdkit_error ("xz: read: error %d", *err);
           goto err2;
         }
diff --git a/tests/test-truncate4.sh b/tests/test-truncate4.sh
index 4a27cbc2..f3ebcddb 100755
--- a/tests/test-truncate4.sh
+++ b/tests/test-truncate4.sh
@@ -30,7 +30,7 @@
 # OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 # SUCH DAMAGE.

-# Regression test when next_ops->get_size changes between connections.
+# Regression test when next->get_size changes between connections.
 #
 # For now, NBD does not support dynamic resize; but the file plugin
 # reads size from the file system for each new connection, at which
diff --git a/tests/test-layers-filter.c b/tests/test-layers-filter.c
index ed06a008..08461ccf 100644
--- a/tests/test-layers-filter.c
+++ b/tests/test-layers-filter.c
@@ -43,10 +43,9 @@
 #define str(s) #s
 #define DEBUG_FUNCTION nbdkit_debug ("%s: %s", layer, __func__)

-/* Perform sanity checking on next_ops(nxdata) stability */
+/* Perform sanity checking on nbdkit_next stability */
 struct handle {
-  void *nxdata;
-  struct nbdkit_next_ops *next_ops;
+  nbdkit_next *next;
 };

 static void
@@ -62,7 +61,7 @@ test_layers_filter_unload (void)
 }

 static int
-test_layers_filter_config (nbdkit_next_config *next, void *nxdata,
+test_layers_filter_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
                            const char *key, const char *value)
 {
   DEBUG_FUNCTION;
@@ -71,7 +70,7 @@ test_layers_filter_config (nbdkit_next_config *next, void *nxdata,

 static int
 test_layers_filter_config_complete (nbdkit_next_config_complete *next,
-                                    void *nxdata)
+                                    nbdkit_backend *nxdata)
 {
   DEBUG_FUNCTION;
   return next (nxdata);
@@ -89,7 +88,7 @@ test_layers_filter_thread_model (void)

 static int
 test_layers_filter_get_ready (nbdkit_next_get_ready *next,
-                              void *nxdata, int thread_model)
+                              nbdkit_backend *nxdata, int thread_model)
 {
   DEBUG_FUNCTION;
   return next (nxdata);
@@ -97,7 +96,7 @@ test_layers_filter_get_ready (nbdkit_next_get_ready *next,

 static int
 test_layers_filter_after_fork (nbdkit_next_after_fork *next,
-                               void *nxdata)
+                               nbdkit_backend *nxdata)
 {
   DEBUG_FUNCTION;
   return next (nxdata);
@@ -105,14 +104,15 @@ test_layers_filter_after_fork (nbdkit_next_after_fork *next,

 static int
 test_layers_filter_preconnect (nbdkit_next_preconnect *next,
-                               void *nxdata, int readonly)
+                               nbdkit_backend *nxdata, int readonly)
 {
   DEBUG_FUNCTION;
   return next (nxdata, readonly);
 }

 static int
-test_layers_filter_list_exports (nbdkit_next_list_exports *next, void *nxdata,
+test_layers_filter_list_exports (nbdkit_next_list_exports *next,
+                                 nbdkit_backend *nxdata,
                                  int readonly, int is_tls,
                                  struct nbdkit_exports *exports)
 {
@@ -122,14 +122,15 @@ test_layers_filter_list_exports (nbdkit_next_list_exports *next, void *nxdata,

 static const char *
 test_layers_filter_default_export (nbdkit_next_default_export *next,
-                                   void *nxdata, int readonly, int is_tls)
+                                   nbdkit_backend *nxdata, int readonly,
+                                   int is_tls)
 {
   DEBUG_FUNCTION;
   return next (nxdata, readonly);
 }

 static void *
-test_layers_filter_open (nbdkit_next_open *next, void *nxdata,
+test_layers_filter_open (nbdkit_next_open *next, nbdkit_context *nxdata,
                          int readonly, const char *exportname, int is_tls)
 {
   struct handle *h = calloc (1, sizeof *h);
@@ -156,240 +157,233 @@ test_layers_filter_close (void *handle)
 }

 static int
-test_layers_filter_prepare (struct nbdkit_next_ops *next_ops, void *nxdata,
+test_layers_filter_prepare (nbdkit_next *next,
                             void *handle, int readonly)
 {
   struct handle *h = handle;

-  assert (h->next_ops == NULL);
-  h->next_ops = next_ops;
-  h->nxdata = nxdata;
-  assert (next_ops == nxdata);
+  assert (h->next == NULL);
+  h->next = next;
   DEBUG_FUNCTION;
   return 0;
 }

 static int
-test_layers_filter_finalize (struct nbdkit_next_ops *next_ops, void *nxdata,
+test_layers_filter_finalize (nbdkit_next *next,
                              void *handle)
 {
   struct handle *h = handle;

-  assert (h->next_ops == next_ops && h->nxdata == nxdata);
+  assert (h->next == next);
   DEBUG_FUNCTION;
   return 0;
 }

 static int64_t
-test_layers_filter_get_size (struct nbdkit_next_ops *next_ops, void *nxdata,
+test_layers_filter_get_size (nbdkit_next *next,
                              void *handle)
 {
   struct handle *h = handle;

-  assert (h->next_ops == next_ops && h->nxdata == nxdata);
+  assert (h->next == next);
   DEBUG_FUNCTION;
-  return next_ops->get_size (nxdata);
+  return next->get_size (next);
 }

 static int
-test_layers_filter_can_write (struct nbdkit_next_ops *next_ops, void *nxdata,
+test_layers_filter_can_write (nbdkit_next *next,
                               void *handle)
 {
   struct handle *h = handle;

-  assert (h->next_ops == next_ops && h->nxdata == nxdata);
+  assert (h->next == next);
   DEBUG_FUNCTION;
-  return next_ops->can_write (nxdata);
+  return next->can_write (next);
 }

 static int
-test_layers_filter_can_flush (struct nbdkit_next_ops *next_ops, void *nxdata,
+test_layers_filter_can_flush (nbdkit_next *next,
                               void *handle)
 {
   struct handle *h = handle;

-  assert (h->next_ops == next_ops && h->nxdata == nxdata);
+  assert (h->next == next);
   DEBUG_FUNCTION;
-  return next_ops->can_flush (nxdata);
+  return next->can_flush (next);
 }

 static int
-test_layers_filter_is_rotational (struct nbdkit_next_ops *next_ops,
-                                  void *nxdata,
+test_layers_filter_is_rotational (nbdkit_next *next,
                                   void *handle)
 {
   struct handle *h = handle;

-  assert (h->next_ops == next_ops && h->nxdata == nxdata);
+  assert (h->next == next);
   DEBUG_FUNCTION;
-  return next_ops->is_rotational (nxdata);
+  return next->is_rotational (next);
 }

 static int
-test_layers_filter_can_trim (struct nbdkit_next_ops *next_ops, void *nxdata,
+test_layers_filter_can_trim (nbdkit_next *next,
                              void *handle)
 {
   struct handle *h = handle;

-  assert (h->next_ops == next_ops && h->nxdata == nxdata);
+  assert (h->next == next);
   DEBUG_FUNCTION;
-  return next_ops->can_trim (nxdata);
+  return next->can_trim (next);
 }

 static int
-test_layers_filter_can_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+test_layers_filter_can_zero (nbdkit_next *next,
                              void *handle)
 {
   struct handle *h = handle;

-  assert (h->next_ops == next_ops && h->nxdata == nxdata);
+  assert (h->next == next);
   DEBUG_FUNCTION;
-  return next_ops->can_zero (nxdata);
+  return next->can_zero (next);
 }

 static int
-test_layers_filter_can_fast_zero (struct nbdkit_next_ops *next_ops,
-                                  void *nxdata, void *handle)
+test_layers_filter_can_fast_zero (nbdkit_next *next, void *handle)
 {
   struct handle *h = handle;

-  assert (h->next_ops == next_ops && h->nxdata == nxdata);
+  assert (h->next == next);
   DEBUG_FUNCTION;
-  return next_ops->can_fast_zero (nxdata);
+  return next->can_fast_zero (next);
 }

 static int
-test_layers_filter_can_fua (struct nbdkit_next_ops *next_ops, void *nxdata,
+test_layers_filter_can_fua (nbdkit_next *next,
                             void *handle)
 {
   struct handle *h = handle;

-  assert (h->next_ops == next_ops && h->nxdata == nxdata);
+  assert (h->next == next);
   DEBUG_FUNCTION;
-  return next_ops->can_fua (nxdata);
+  return next->can_fua (next);
 }

 static int
-test_layers_filter_can_multi_conn (struct nbdkit_next_ops *next_ops,
-                                   void *nxdata,
+test_layers_filter_can_multi_conn (nbdkit_next *next,
                                    void *handle)
 {
   struct handle *h = handle;

-  assert (h->next_ops == next_ops && h->nxdata == nxdata);
+  assert (h->next == next);
   DEBUG_FUNCTION;
-  return next_ops->can_multi_conn (nxdata);
+  return next->can_multi_conn (next);
 }

 static int
-test_layers_filter_can_extents (struct nbdkit_next_ops *next_ops,
-                                void *nxdata,
+test_layers_filter_can_extents (nbdkit_next *next,
                                 void *handle)
 {
   struct handle *h = handle;

-  assert (h->next_ops == next_ops && h->nxdata == nxdata);
+  assert (h->next == next);
   DEBUG_FUNCTION;
-  return next_ops->can_extents (nxdata);
+  return next->can_extents (next);
 }

 static int
-test_layers_filter_can_cache (struct nbdkit_next_ops *next_ops,
-                              void *nxdata,
+test_layers_filter_can_cache (nbdkit_next *next,
                               void *handle)
 {
   struct handle *h = handle;

-  assert (h->next_ops == next_ops && h->nxdata == nxdata);
+  assert (h->next == next);
   DEBUG_FUNCTION;
-  return next_ops->can_cache (nxdata);
+  return next->can_cache (next);
 }

 static int
-test_layers_filter_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
+test_layers_filter_pread (nbdkit_next *next,
                           void *handle, void *buf,
                           uint32_t count, uint64_t offset,
                           uint32_t flags, int *err)
 {
   struct handle *h = handle;

-  assert (h->next_ops == next_ops && h->nxdata == nxdata);
+  assert (h->next == next);
   DEBUG_FUNCTION;
-  return next_ops->pread (nxdata, buf, count, offset, flags, err);
+  return next->pread (next, buf, count, offset, flags, err);
 }

 static int
-test_layers_filter_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
+test_layers_filter_pwrite (nbdkit_next *next,
                            void *handle,
                            const void *buf, uint32_t count, uint64_t offset,
                            uint32_t flags, int *err)
 {
   struct handle *h = handle;

-  assert (h->next_ops == next_ops && h->nxdata == nxdata);
+  assert (h->next == next);
   DEBUG_FUNCTION;
-  return next_ops->pwrite (nxdata, buf, count, offset, flags, err);
+  return next->pwrite (next, buf, count, offset, flags, err);
 }

 static int
-test_layers_filter_flush (struct nbdkit_next_ops *next_ops, void *nxdata,
+test_layers_filter_flush (nbdkit_next *next,
                           void *handle,
                           uint32_t flags, int *err)
 {
   struct handle *h = handle;

-  assert (h->next_ops == next_ops && h->nxdata == nxdata);
+  assert (h->next == next);
   DEBUG_FUNCTION;
-  return next_ops->flush (nxdata, flags, err);
+  return next->flush (next, flags, err);
 }

 static int
-test_layers_filter_trim (struct nbdkit_next_ops *next_ops, void *nxdata,
+test_layers_filter_trim (nbdkit_next *next,
                          void *handle, uint32_t count, uint64_t offset,
                          uint32_t flags, int *err)
 {
   struct handle *h = handle;

-  assert (h->next_ops == next_ops && h->nxdata == nxdata);
+  assert (h->next == next);
   DEBUG_FUNCTION;
-  return next_ops->trim (nxdata, count, offset, flags, err);
+  return next->trim (next, count, offset, flags, err);
 }

 static int
-test_layers_filter_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+test_layers_filter_zero (nbdkit_next *next,
                          void *handle, uint32_t count, uint64_t offset,
                          uint32_t flags, int *err)
 {
   struct handle *h = handle;

-  assert (h->next_ops == next_ops && h->nxdata == nxdata);
+  assert (h->next == next);
   DEBUG_FUNCTION;
-  return next_ops->zero (nxdata, count, offset, flags, err);
+  return next->zero (next, count, offset, flags, err);
 }

 static int
-test_layers_filter_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
+test_layers_filter_extents (nbdkit_next *next,
                             void *handle, uint32_t count, uint64_t offset,
                             uint32_t flags, struct nbdkit_extents *extents,
                             int *err)
 {
   struct handle *h = handle;

-  assert (h->next_ops == next_ops && h->nxdata == nxdata);
+  assert (h->next == next);
   DEBUG_FUNCTION;
-  return next_ops->extents (nxdata, count, offset, flags, extents, err);
+  return next->extents (next, count, offset, flags, extents, err);
 }

 static int
-test_layers_filter_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
+test_layers_filter_cache (nbdkit_next *next,
                           void *handle, uint32_t count, uint64_t offset,
                           uint32_t flags, int *err)
 {
   struct handle *h = handle;

-  assert (h->next_ops == next_ops && h->nxdata == nxdata);
+  assert (h->next == next);
   DEBUG_FUNCTION;
-  return next_ops->cache (nxdata, count, offset, flags, err);
+  return next->cache (next, count, offset, flags, err);
 }

 static struct nbdkit_filter filter = {
-- 
2.30.1




More information about the Libguestfs mailing list