[Libguestfs] [nbdkit PATCH v3 03/15] filters: Adjust callback API for flags/errors

Eric Blake eblake at redhat.com
Thu Mar 8 23:02:59 UTC 2018


It's time to expose the full semantics already in use by the backend
to our filters, by exposing flags and an explicit parameter for
tracking the error value to return to the client.  This is an
incompatible API change, and all existing filters are updated to
match the new semantics.

Of note: access to flags means the cache filter can now behave
correctly in the face of FUA semantics (a FUA write must write
through, even when the cache mode is writeback, rather than
waiting for a subsequent flush).  However, more work to more
fully support FUA through the stack will come in later patches.

I considerered a possible boilerplate reduction option of having
a filter that returns -1 but does not set *err then cause
filters.c to populate *err with the current value of errno; but
for now, manually setting *err at all sites makes it obvious
how much this API change affects.

Signed-off-by: Eric Blake <eblake at redhat.com>

---
v: split off backend change; fold in part of FUA flags change;
retitle the patch; more commit message details
v2: more wording tweaks to documentation
---
 docs/nbdkit-filter.pod        | 115 ++++++++++++++++++++++++++++++++++++------
 include/nbdkit-common.h       |   3 ++
 include/nbdkit-filter.h       |  30 +++++++----
 src/internal.h                |   3 --
 src/filters.c                 | 102 +++++++++++--------------------------
 filters/cache/cache.c         |  62 +++++++++++++++--------
 filters/cow/cow.c             |  34 ++++++++-----
 filters/delay/delay.c         |  15 +++---
 filters/offset/offset.c       |  20 +++++---
 filters/partition/partition.c |  26 ++++++----
 10 files changed, 253 insertions(+), 157 deletions(-)

diff --git a/docs/nbdkit-filter.pod b/docs/nbdkit-filter.pod
index eb72dae..8ef26e3 100644
--- a/docs/nbdkit-filter.pod
+++ b/docs/nbdkit-filter.pod
@@ -163,10 +163,16 @@ short-circuited.

 The filter’s other methods like C<.prepare>, C<.get_size>, C<.pread>
 etc ― always called in the context of a connection ― are passed a
-pointer to C<struct nbdkit_next_ops> which contains a subset of the
-plugin methods that can be called during a connection.  It is possible
-for a filter to issue (for example) extra read calls in response to a
-single C<.pwrite> call.
+pointer to C<struct nbdkit_next_ops> which contains a comparable set
+of accessors to plugin methods that can be called during a connection.
+It is possible for a filter to issue (for example) extra read calls in
+response to a single C<.pwrite> call.  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, allowing a filter to learn or modify this error if desired.

 You can modify parameters when you call the C<next> function.  However
 be careful when modifying strings because for some methods
@@ -324,6 +330,10 @@ 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>.
+If this function is called more than once for the same connection, it
+should return the same value; similarly, the filter may cache
+C<next_ops-E<gt>get_size> for a given connection rather than repeating
+calls.

 =head2 C<.can_write>

@@ -346,65 +356,140 @@ should call C<nbdkit_error> with an error message and return C<-1>.
 These intercept the corresponding plugin methods.

 If there is an error, the callback should call C<nbdkit_error> with an
-error message and return C<-1>.
+error message and return C<-1>.  If these functions are called more
+than once for the same connection, they should return the same value;
+similarly, the filter may cache the results of each counterpart in
+C<next_ops> for a given connection rather than repeating calls.

 =head2 C<.pread>

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

 This intercepts the plugin C<.pread> method and can be used to read or
 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>.
+
 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
-message B<and> set C<errno>, then return C<-1>.
+message B<and> return -1 with C<err> set to the positive errno value
+to return to the client.

 =head2 C<.pwrite>

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

 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.
+
+The parameter C<flags> may include C<NBDKIT_FLAG_FUA> on input based
+on the result of C<.can_flush>.  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_flush> returned true.
+
 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
-message B<and> set C<errno>, then return C<-1>.
+message B<and> return -1 with C<err> set to the positive errno value
+to return to the client.

 =head2 C<.flush>

  int (*flush) (struct nbdkit_next_ops *next_ops, void *nxdata,
-               void *handle);
+               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.
+
+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>.
+
 If there is an error, C<.flush> should call C<nbdkit_error> with an
-error message B<and> set C<errno>, then return C<-1>.
+error message B<and> return -1 with C<err> set to the positive errno
+value to return to the client.

 =head2 C<.trim>

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

 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.
+
+The parameter C<flags> may include C<NBDKIT_FLAG_FUA> on input based
+on the result of C<.can_flush>.  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_flush> returned true.
+
 If there is an error, C<.trim> should call C<nbdkit_error> with an
-error message B<and> set C<errno>, then return C<-1>.
+error message B<and> return -1 with C<err> set to the positive errno
+value to return to the client.

 =head2 C<.zero>

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

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

+This function will not be called if C<.can_write> returned false; in
+turn, the filter should not call C<next_ops-E<gt>zero> if
+C<next_ops-E<gt>can_write> did not return true.
+
+On input, the parameter C<flags> may include C<NBDKIT_FLAG_MAY_TRIM>
+unconditionally, and C<NBDKIT_FLAG_FUA> based on the result of
+C<.can_flush>.  In turn, the filter may pass C<NBDKIT_FLAG_MAY_TRIM>
+unconditionally, but should only pass C<NBDKIT_FLAG_FUA> on to
+C<next_ops-E<gt>zero> if C<next_ops-E<gt>can_flush> returned true.
+
+Note that unlike the plugin C<.zero> which is permitted to fail with
+C<EOPNOTSUPP> to force a fallback to C<.pwrite>, the function
+C<next_ops-E<gt>zero> will never fail with C<err> set to C<EOPNOTSUPP>
+because the fallback has already taken place.
+
 If there is an error, C<.zero> should call C<nbdkit_error> with an
-error message B<and> set C<errno>, then return C<-1>.
+error message B<and> return -1 with C<err> set to the positive errno
+value to return to the client.  The filter should never fail with
+C<EOPNOTSUPP> (while plugins have automatic fallback to C<.pwrite>,
+filters do not).
+
+=head1 ERROR HANDLING
+
+If there is an error in the filter itself, the filter should call
+C<nbdkit_error> to report an error message.  If the callback is
+involved in serving data, the explicit C<err> parameter determines the
+error code that will be sent to the client; other callbacks should
+return the appropriate error indication, eg. C<NULL> or C<-1>.
+
+C<nbdkit_error> has the following prototype and works like
+L<printf(3)>:
+
+ void nbdkit_error (const char *fs, ...);
+ void nbdkit_verror (const char *fs, va_list args);
+
+For convenience, C<nbdkit_error> preserves the value of C<errno>.

 =head1 DEBUGGING

diff --git a/include/nbdkit-common.h b/include/nbdkit-common.h
index 5e69579..f32bf76 100644
--- a/include/nbdkit-common.h
+++ b/include/nbdkit-common.h
@@ -50,6 +50,9 @@ extern "C" {
 #define NBDKIT_THREAD_MODEL_SERIALIZE_REQUESTS        2
 #define NBDKIT_THREAD_MODEL_PARALLEL                  3

+#define NBDKIT_FLAG_MAY_TRIM (1<<0) /* Maps to !NBD_CMD_FLAG_NO_HOLE */
+#define NBDKIT_FLAG_FUA      (1<<1) /* Maps to NBD_CMD_FLAG_FUA */
+
 extern void nbdkit_error (const char *msg, ...)
   __attribute__((__format__ (__printf__, 1, 2)));
 extern void nbdkit_verror (const char *msg, va_list args);
diff --git a/include/nbdkit-filter.h b/include/nbdkit-filter.h
index 0cfc3f8..95be130 100644
--- a/include/nbdkit-filter.h
+++ b/include/nbdkit-filter.h
@@ -42,7 +42,7 @@
 extern "C" {
 #endif

-#define NBDKIT_FILTER_API_VERSION 1
+#define NBDKIT_FILTER_API_VERSION 2

 typedef int nbdkit_next_config (void *nxdata,
                                 const char *key, const char *value);
@@ -58,12 +58,16 @@ struct nbdkit_next_ops {
   int (*is_rotational) (void *nxdata);
   int (*can_trim) (void *nxdata);

-  int (*pread) (void *nxdata, void *buf, uint32_t count, uint64_t offset);
+  int (*pread) (void *nxdata, void *buf, uint32_t count, uint64_t offset,
+                uint32_t flags, int *err);
   int (*pwrite) (void *nxdata,
-                 const void *buf, uint32_t count, uint64_t offset);
-  int (*flush) (void *nxdata);
-  int (*trim) (void *nxdata, uint32_t count, uint64_t offset);
-  int (*zero) (void *nxdata, uint32_t count, uint64_t offset, int may_trim);
+                 const void *buf, uint32_t count, uint64_t offset,
+                 uint32_t flags, int *err);
+  int (*flush) (void *nxdata, uint32_t flags, int *err);
+  int (*trim) (void *nxdata, uint32_t count, uint64_t offset, uint32_t flags,
+               int *err);
+  int (*zero) (void *nxdata, uint32_t count, uint64_t offset, uint32_t flags,
+               int *err);
 };

 struct nbdkit_filter {
@@ -113,16 +117,20 @@ struct nbdkit_filter {
                    void *handle);

   int (*pread) (struct nbdkit_next_ops *next_ops, void *nxdata,
-                void *handle, void *buf, uint32_t count, uint64_t offset);
+                void *handle, void *buf, uint32_t count, uint64_t offset,
+                uint32_t flags, int *err);
   int (*pwrite) (struct nbdkit_next_ops *next_ops, void *nxdata,
                  void *handle,
-                 const void *buf, uint32_t count, uint64_t offset);
+                 const void *buf, uint32_t count, uint64_t offset,
+                 uint32_t flags, int *err);
   int (*flush) (struct nbdkit_next_ops *next_ops, void *nxdata,
-                void *handle);
+                void *handle, uint32_t flags, int *err);
   int (*trim) (struct nbdkit_next_ops *next_ops, void *nxdata,
-               void *handle, uint32_t count, uint64_t offset);
+               void *handle, uint32_t count, uint64_t offset, uint32_t flags,
+               int *err);
   int (*zero) (struct nbdkit_next_ops *next_ops, void *nxdata,
-               void *handle, uint32_t count, uint64_t offset, int may_trim);
+               void *handle, uint32_t count, uint64_t offset, uint32_t flags,
+               int *err);
 };

 #define NBDKIT_REGISTER_FILTER(filter)                                  \
diff --git a/src/internal.h b/src/internal.h
index be79e10..12dfc14 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -98,9 +98,6 @@
       (type *) ((char *) __mptr - offsetof(type, member));       \
     })

-#define NBDKIT_FLAG_MAY_TRIM (1<<0) /* Maps to !NBD_CMD_FLAG_NO_HOLE */
-#define NBDKIT_FLAG_FUA      (1<<1) /* Maps to NBD_CMD_FLAG_FUA */
-
 /* main.c */
 extern const char *exportname;
 extern const char *ipaddr;
diff --git a/src/filters.c b/src/filters.c
index ae5edfb..9d821f2 100644
--- a/src/filters.c
+++ b/src/filters.c
@@ -39,7 +39,6 @@
 #include <string.h>
 #include <inttypes.h>
 #include <assert.h>
-#include <errno.h>

 #include <dlfcn.h>

@@ -289,66 +288,44 @@ next_can_trim (void *nxdata)
 }

 static int
-next_pread (void *nxdata, void *buf, uint32_t count, uint64_t offset)
+next_pread (void *nxdata, void *buf, uint32_t count, uint64_t offset,
+            uint32_t flags, int *err)
 {
   struct b_conn *b_conn = nxdata;
-  int err = 0;
-  int r = b_conn->b->pread (b_conn->b, b_conn->conn, buf, count, offset, 0,
-                            &err);
-  if (r == -1)
-    errno = err;
-  return r;
+  return b_conn->b->pread (b_conn->b, b_conn->conn, buf, count, offset, flags,
+                           err);
 }

 static int
-next_pwrite (void *nxdata, const void *buf, uint32_t count, uint64_t offset)
+next_pwrite (void *nxdata, const void *buf, uint32_t count, uint64_t offset,
+             uint32_t flags, int *err)
 {
   struct b_conn *b_conn = nxdata;
-  int err = 0;
-  int r = b_conn->b->pwrite (b_conn->b, b_conn->conn, buf, count, offset, 0,
-                             &err);
-  if (r == -1)
-    errno = err;
-  return r;
+  return b_conn->b->pwrite (b_conn->b, b_conn->conn, buf, count, offset, flags,
+                            err);
 }

 static int
-next_flush (void *nxdata)
+next_flush (void *nxdata, uint32_t flags, int *err)
 {
   struct b_conn *b_conn = nxdata;
-  int err = 0;
-  int r = b_conn->b->flush (b_conn->b, b_conn->conn, 0, &err);
-  if (r == -1)
-    errno = err;
-  return r;
+  return b_conn->b->flush (b_conn->b, b_conn->conn, flags, err);
 }

 static int
-next_trim (void *nxdata, uint32_t count, uint64_t offset)
+next_trim (void *nxdata, uint32_t count, uint64_t offset, uint32_t flags,
+           int *err)
 {
   struct b_conn *b_conn = nxdata;
-  int err = 0;
-  int r = b_conn->b->trim (b_conn->b, b_conn->conn, count, offset, 0, &err);
-  if (r == -1)
-    errno = err;
-  return r;
+  return b_conn->b->trim (b_conn->b, b_conn->conn, count, offset, flags, err);
 }

 static int
-next_zero (void *nxdata, uint32_t count, uint64_t offset, int may_trim)
+next_zero (void *nxdata, uint32_t count, uint64_t offset, uint32_t flags,
+           int *err)
 {
   struct b_conn *b_conn = nxdata;
-  uint32_t f = 0;
-  int err = 0;
-  int r;
-
-  if (may_trim)
-    f |= NBDKIT_FLAG_MAY_TRIM;
-
-  r = b_conn->b->zero (b_conn->b, b_conn->conn, count, offset, f, &err);
-  if (r == -1)
-    errno = err;
-  return r;
+  return b_conn->b->zero (b_conn->b, b_conn->conn, count, offset, flags, err);
 }

 static struct nbdkit_next_ops next_ops = {
@@ -483,13 +460,9 @@ filter_pread (struct backend *b, struct connection *conn,
   debug ("pread count=%" PRIu32 " offset=%" PRIu64 " flags=0x%" PRIx32,
          count, offset, flags);

-  if (f->filter.pread) {
-    int r = f->filter.pread (&next_ops, &nxdata, handle,
-                             buf, count, offset);
-    if (r == -1)
-      *err = errno;
-    return r;
-  }
+  if (f->filter.pread)
+    return f->filter.pread (&next_ops, &nxdata, handle,
+                            buf, count, offset, flags, err);
   else
     return f->backend.next->pread (f->backend.next, conn,
                                    buf, count, offset, flags, err);
@@ -509,13 +482,9 @@ filter_pwrite (struct backend *b, struct connection *conn,
   debug ("pwrite count=%" PRIu32 " offset=%" PRIu64 " flags=0x%" PRIx32,
          count, offset, flags);

-  if (f->filter.pwrite) {
-    int r = f->filter.pwrite (&next_ops, &nxdata, handle,
-                              buf, count, offset);
-    if (r == -1)
-      *err = errno;
-    return r;
-  }
+  if (f->filter.pwrite)
+    return f->filter.pwrite (&next_ops, &nxdata, handle,
+                             buf, count, offset, flags, err);
   else
     return f->backend.next->pwrite (f->backend.next, conn,
                                     buf, count, offset, flags, err);
@@ -533,12 +502,8 @@ filter_flush (struct backend *b, struct connection *conn, uint32_t flags,

   debug ("flush flags=0x%" PRIx32, flags);

-  if (f->filter.flush) {
-    int r = f->filter.flush (&next_ops, &nxdata, handle);
-    if (r == -1)
-      *err = errno;
-    return r;
-  }
+  if (f->filter.flush)
+    return f->filter.flush (&next_ops, &nxdata, handle, flags, err);
   else
     return f->backend.next->flush (f->backend.next, conn, flags, err);
 }
@@ -557,12 +522,9 @@ filter_trim (struct backend *b, struct connection *conn,
   debug ("trim count=%" PRIu32 " offset=%" PRIu64 " flags=0x%" PRIx32,
          count, offset, flags);

-  if (f->filter.trim) {
-    int r = f->filter.trim (&next_ops, &nxdata, handle, count, offset);
-    if (r == -1)
-      *err = errno;
-    return r;
-  }
+  if (f->filter.trim)
+    return f->filter.trim (&next_ops, &nxdata, handle, count, offset, flags,
+                           err);
   else
     return f->backend.next->trim (f->backend.next, conn, count, offset, flags,
                                   err);
@@ -581,13 +543,9 @@ filter_zero (struct backend *b, struct connection *conn,
   debug ("zero count=%" PRIu32 " offset=%" PRIu64 " flags=0x%" PRIx32,
          count, offset, flags);

-  if (f->filter.zero) {
-    int r = f->filter.zero (&next_ops, &nxdata, handle,
-                            count, offset, !!(flags & NBDKIT_FLAG_MAY_TRIM));
-    if (r == -1)
-      *err = errno;
-    return r;
-  }
+  if (f->filter.zero)
+    return f->filter.zero (&next_ops, &nxdata, handle,
+                           count, offset, flags, err);
   else
     return f->backend.next->zero (f->backend.next, conn,
                                   count, offset, flags, err);
diff --git a/filters/cache/cache.c b/filters/cache/cache.c
index 9473f2c..c8dd791 100644
--- a/filters/cache/cache.c
+++ b/filters/cache/cache.c
@@ -45,6 +45,7 @@
 #include <errno.h>
 #include <sys/types.h>
 #include <sys/ioctl.h>
+#include <assert.h>

 #include <nbdkit-filter.h>

@@ -55,6 +56,8 @@

 /* Size of a block in the cache.  A 4K block size means that we need
  * 64 MB of memory to store the bitmaps for a 1 TB underlying image.
+ * It is also smaller than the usual hole size for sparse files, which
+ * means we have no reason to call next_ops->zero.
  */
 #define BLKSIZE 4096

@@ -263,7 +266,7 @@ blk_set_bitmap_entry (uint64_t blknum, enum bm_entry state)
  */
 static int
 blk_read (struct nbdkit_next_ops *next_ops, void *nxdata,
-          uint64_t blknum, uint8_t *block)
+          uint64_t blknum, uint8_t *block, int *err)
 {
   off_t offset = blknum * BLKSIZE;
   enum bm_entry state = blk_get_bitmap_entry (blknum);
@@ -276,9 +279,10 @@ blk_read (struct nbdkit_next_ops *next_ops, void *nxdata,
                 "unknown");

   if (state == BLOCK_NOT_CACHED) /* Read underlying plugin. */
-    return next_ops->pread (nxdata, block, BLKSIZE, offset);
+    return next_ops->pread (nxdata, block, BLKSIZE, offset, 0, err);
   else {                         /* Read cache. */
     if (pread (fd, block, BLKSIZE, offset) == -1) {
+      *err = errno;
       nbdkit_error ("pread: %m");
       return -1;
     }
@@ -289,7 +293,8 @@ blk_read (struct nbdkit_next_ops *next_ops, void *nxdata,
 /* Write to the cache and the plugin. */
 static int
 blk_writethrough (struct nbdkit_next_ops *next_ops, void *nxdata,
-                  uint64_t blknum, const uint8_t *block)
+                  uint64_t blknum, const uint8_t *block, uint32_t flags,
+                  int *err)
 {
   off_t offset = blknum * BLKSIZE;

@@ -298,11 +303,12 @@ blk_writethrough (struct nbdkit_next_ops *next_ops, void *nxdata,
                 blknum, (uint64_t) offset);

   if (pwrite (fd, block, BLKSIZE, offset) == -1) {
+    *err = errno;
     nbdkit_error ("pwrite: %m");
     return -1;
   }

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

   blk_set_bitmap_entry (blknum, BLOCK_CLEAN);
@@ -313,12 +319,14 @@ blk_writethrough (struct nbdkit_next_ops *next_ops, void *nxdata,
 /* Write to the cache only. */
 static int
 blk_writeback (struct nbdkit_next_ops *next_ops, void *nxdata,
-               uint64_t blknum, const uint8_t *block)
+               uint64_t blknum, const uint8_t *block, uint32_t flags,
+               int *err)
 {
   off_t offset;

-  if (cache_mode == CACHE_MODE_WRITETHROUGH)
-    return blk_writethrough (next_ops, nxdata, blknum, block);
+  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);

   offset = blknum * BLKSIZE;

@@ -327,6 +335,7 @@ blk_writeback (struct nbdkit_next_ops *next_ops, void *nxdata,
                 blknum, (uint64_t) offset);

   if (pwrite (fd, block, BLKSIZE, offset) == -1) {
+    *err = errno;
     nbdkit_error ("pwrite: %m");
     return -1;
   }
@@ -338,12 +347,15 @@ blk_writeback (struct nbdkit_next_ops *next_ops, void *nxdata,
 /* Read data. */
 static int
 cache_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
-             void *handle, void *buf, uint32_t count, uint64_t offset)
+             void *handle, void *buf, uint32_t count, uint64_t offset,
+             uint32_t flags, int *err)
 {
   uint8_t *block;

+  assert (!flags);
   block = malloc (BLKSIZE);
   if (block == NULL) {
+    *err = errno;
     nbdkit_error ("malloc: %m");
     return -1;
   }
@@ -357,7 +369,7 @@ cache_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
     if (n > count)
       n = count;

-    if (blk_read (next_ops, nxdata, blknum, block) == -1) {
+    if (blk_read (next_ops, nxdata, blknum, block, err) == -1) {
       free (block);
       return -1;
     }
@@ -376,12 +388,14 @@ cache_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
 /* Write data. */
 static int
 cache_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
-              void *handle, const void *buf, uint32_t count, uint64_t offset)
+              void *handle, const void *buf, uint32_t count, uint64_t offset,
+              uint32_t flags, int *err)
 {
   uint8_t *block;

   block = malloc (BLKSIZE);
   if (block == NULL) {
+    *err = errno;
     nbdkit_error ("malloc: %m");
     return -1;
   }
@@ -396,12 +410,12 @@ cache_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
       n = count;

     /* Do a read-modify-write operation on the current block. */
-    if (blk_read (next_ops, nxdata, blknum, block) == -1) {
+    if (blk_read (next_ops, nxdata, blknum, block, err) == -1){
       free (block);
       return -1;
     }
     memcpy (&block[blkoffs], buf, n);
-    if (blk_writeback (next_ops, nxdata, blknum, block) == -1) {
+    if (blk_writeback (next_ops, nxdata, blknum, block, flags, err) == -1) {
       free (block);
       return -1;
     }
@@ -418,16 +432,19 @@ cache_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
 /* Zero data. */
 static int
 cache_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
-            void *handle, uint32_t count, uint64_t offset, int may_trim)
+            void *handle, uint32_t count, uint64_t offset, uint32_t flags,
+            int *err)
 {
   uint8_t *block;

   block = malloc (BLKSIZE);
   if (block == NULL) {
+    *err = errno;
     nbdkit_error ("malloc: %m");
     return -1;
   }

+  flags &= ~NBDKIT_FLAG_MAY_TRIM; /* See BLKSIZE comment above. */
   while (count > 0) {
     uint64_t blknum, blkoffs, n;

@@ -437,12 +454,12 @@ cache_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
     if (n > count)
       n = count;

-    if (blk_read (next_ops, nxdata, blknum, block) == -1) {
+    if (blk_read (next_ops, nxdata, blknum, block, err) == -1) {
       free (block);
       return -1;
     }
     memset (&block[blkoffs], 0, n);
-    if (blk_writeback (next_ops, nxdata, blknum, block) == -1) {
+    if (blk_writeback (next_ops, nxdata, blknum, block, flags, err) == -1) {
       free (block);
       return -1;
     }
@@ -457,13 +474,15 @@ cache_zero (struct nbdkit_next_ops *next_ops, void *nxdata,

 /* Flush: Go through all the dirty blocks, flushing them to disk. */
 static int
-cache_flush (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle)
+cache_flush (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle,
+             uint32_t flags, int *err)
 {
   uint8_t *block = NULL;
   uint64_t i, j;
   uint64_t blknum;
   enum bm_entry state;
   unsigned errors = 0;
+  int tmp;

   if (cache_mode == CACHE_MODE_UNSAFE)
     return 0;
@@ -473,7 +492,7 @@ cache_flush (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle)
    * to be sure.  Also we still need to issue the flush to the
    * underlying storage.
    */
-
+  assert (!flags);
   for (i = 0; i < bm_size; ++i) {
     if (bitmap[i] != 0) {
       /* The bitmap stores information about 4 blocks per byte,
@@ -487,6 +506,7 @@ cache_flush (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle)
           if (!block) {
             block = malloc (BLKSIZE);
             if (block == NULL) {
+              *err = errno;
               nbdkit_error ("malloc: %m");
               return -1;
             }
@@ -494,8 +514,10 @@ cache_flush (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle)
           /* Perform a read + writethrough which will read from the
            * cache and write it through to the underlying storage.
            */
-          if (blk_read (next_ops, nxdata, blknum, block) == -1 ||
-              blk_writethrough (next_ops, nxdata, blknum, block)) {
+          if (blk_read (next_ops, nxdata, blknum, block,
+                        errors ? &tmp : err) == -1 ||
+              blk_writethrough (next_ops, nxdata, blknum, block, 0,
+                                errors ? &tmp : err) == -1) {
             nbdkit_error ("cache: flush of block %" PRIu64 " failed", blknum);
             errors++;
           }
@@ -507,7 +529,7 @@ cache_flush (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle)
   free (block);

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

   return errors == 0 ? 0 : -1;
diff --git a/filters/cow/cow.c b/filters/cow/cow.c
index 5c2fcd0..cbf4ed4 100644
--- a/filters/cow/cow.c
+++ b/filters/cow/cow.c
@@ -270,7 +270,7 @@ blk_set_allocated (uint64_t blknum)
  */
 static int
 blk_read (struct nbdkit_next_ops *next_ops, void *nxdata,
-          uint64_t blknum, uint8_t *block)
+          uint64_t blknum, uint8_t *block, int *err)
 {
   off_t offset = blknum * BLKSIZE;
   bool allocated = blk_is_allocated (blknum);
@@ -280,9 +280,10 @@ blk_read (struct nbdkit_next_ops *next_ops, void *nxdata,
                 !allocated ? "a hole" : "allocated");

   if (!allocated)               /* Read underlying plugin. */
-    return next_ops->pread (nxdata, block, BLKSIZE, offset);
+    return next_ops->pread (nxdata, block, BLKSIZE, offset, 0, err);
   else {                        /* Read overlay. */
     if (pread (fd, block, BLKSIZE, offset) == -1) {
+      *err = errno;
       nbdkit_error ("pread: %m");
       return -1;
     }
@@ -291,7 +292,7 @@ blk_read (struct nbdkit_next_ops *next_ops, void *nxdata,
 }

 static int
-blk_write (uint64_t blknum, const uint8_t *block)
+blk_write (uint64_t blknum, const uint8_t *block, int *err)
 {
   off_t offset = blknum * BLKSIZE;

@@ -299,6 +300,7 @@ blk_write (uint64_t blknum, const uint8_t *block)
                 blknum, (uint64_t) offset);

   if (pwrite (fd, block, BLKSIZE, offset) == -1) {
+    *err = errno;
     nbdkit_error ("pwrite: %m");
     return -1;
   }
@@ -310,12 +312,14 @@ blk_write (uint64_t blknum, const uint8_t *block)
 /* Read data. */
 static int
 cow_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
-           void *handle, void *buf, uint32_t count, uint64_t offset)
+           void *handle, void *buf, uint32_t count, uint64_t offset,
+           uint32_t flags, int *err)
 {
   uint8_t *block;

   block = malloc (BLKSIZE);
   if (block == NULL) {
+    *err = errno;
     nbdkit_error ("malloc: %m");
     return -1;
   }
@@ -329,7 +333,7 @@ cow_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
     if (n > count)
       n = count;

-    if (blk_read (next_ops, nxdata, blknum, block) == -1) {
+    if (blk_read (next_ops, nxdata, blknum, block, err) == -1) {
       free (block);
       return -1;
     }
@@ -348,12 +352,14 @@ cow_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
 /* Write data. */
 static int
 cow_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
-            void *handle, const void *buf, uint32_t count, uint64_t offset)
+            void *handle, const void *buf, uint32_t count, uint64_t offset,
+            uint32_t flags, int *err)
 {
   uint8_t *block;

   block = malloc (BLKSIZE);
   if (block == NULL) {
+    *err = errno;
     nbdkit_error ("malloc: %m");
     return -1;
   }
@@ -368,12 +374,12 @@ cow_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
       n = count;

     /* Do a read-modify-write operation on the current block. */
-    if (blk_read (next_ops, nxdata, blknum, block) == -1) {
+    if (blk_read (next_ops, nxdata, blknum, block, err) == -1) {
       free (block);
       return -1;
     }
     memcpy (&block[blkoffs], buf, n);
-    if (blk_write (blknum, block) == -1) {
+    if (blk_write (blknum, block, err) == -1) {
       free (block);
       return -1;
     }
@@ -390,12 +396,14 @@ cow_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
 /* Zero data. */
 static int
 cow_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
-          void *handle, uint32_t count, uint64_t offset, int may_trim)
+          void *handle, uint32_t count, uint64_t offset, uint32_t flags,
+          int *err)
 {
   uint8_t *block;

   block = malloc (BLKSIZE);
   if (block == NULL) {
+    *err = errno;
     nbdkit_error ("malloc: %m");
     return -1;
   }
@@ -412,12 +420,12 @@ cow_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
     /* XXX There is the possibility of optimizing this: ONLY if we are
      * writing a whole, aligned block, then use FALLOC_FL_ZERO_RANGE.
      */
-    if (blk_read (next_ops, nxdata, blknum, block) == -1) {
+    if (blk_read (next_ops, nxdata, blknum, block, err) == -1) {
       free (block);
       return -1;
     }
     memset (&block[blkoffs], 0, n);
-    if (blk_write (blknum, block) == -1) {
+    if (blk_write (blknum, block, err) == -1) {
       free (block);
       return -1;
     }
@@ -431,12 +439,14 @@ cow_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
 }

 static int
-cow_flush (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle)
+cow_flush (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle,
+           uint32_t flags, int *err)
 {
   /* I think we don't care about file metadata for this temporary
    * file, so only flush the data.
    */
   if (fdatasync (fd) == -1) {
+    *err = errno;
     nbdkit_error ("fdatasync: %m");
     return -1;
   }
diff --git a/filters/delay/delay.c b/filters/delay/delay.c
index ebe3386..d8d0c8e 100644
--- a/filters/delay/delay.c
+++ b/filters/delay/delay.c
@@ -124,29 +124,32 @@ delay_config (nbdkit_next_config *next, void *nxdata,
 /* Read data. */
 static int
 delay_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
-             void *handle, void *buf, uint32_t count, uint64_t offset)
+             void *handle, void *buf, uint32_t count, uint64_t offset,
+             uint32_t flags, int *err)
 {
   read_delay ();
-  return next_ops->pread (nxdata, buf, count, offset);
+  return next_ops->pread (nxdata, buf, count, offset, flags, err);
 }

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

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

 static struct nbdkit_filter filter = {
diff --git a/filters/offset/offset.c b/filters/offset/offset.c
index b6687f2..bb63ed4 100644
--- a/filters/offset/offset.c
+++ b/filters/offset/offset.c
@@ -97,34 +97,38 @@ 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,
-              void *handle, void *buf, uint32_t count, uint64_t offs)
+              void *handle, void *buf, uint32_t count, uint64_t offs,
+              uint32_t flags, int *err)
 {
-  return next_ops->pread (nxdata, buf, count, offs + offset);
+  return next_ops->pread (nxdata, buf, count, offs + offset, flags, err);
 }

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

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

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

 static struct nbdkit_filter filter = {
diff --git a/filters/partition/partition.c b/filters/partition/partition.c
index f74b3af..1fe0bc7 100644
--- a/filters/partition/partition.c
+++ b/filters/partition/partition.c
@@ -196,6 +196,7 @@ find_gpt_partition (struct nbdkit_next_ops *next_ops, void *nxdata,
   struct gpt_header header;
   struct gpt_partition partition;
   int i;
+  int err;

   if (partnum > 128) {
   out_of_range:
@@ -216,7 +217,7 @@ find_gpt_partition (struct nbdkit_next_ops *next_ops, void *nxdata,
      * partition_prepare call above.
      */
     if (next_ops->pread (nxdata, partition_bytes, sizeof partition_bytes,
-                         2*512 + i*128) == -1)
+                         2*512 + i*128, 0, &err) == -1)
       return -1;
     get_gpt_partition (partition_bytes, &partition);
     if (memcmp (partition.partition_type_guid,
@@ -240,6 +241,7 @@ partition_prepare (struct nbdkit_next_ops *next_ops, void *nxdata,
   int64_t size;
   uint8_t lba01[1024];          /* LBA 0 and 1 */
   int r;
+  int err;

   size = next_ops->get_size (nxdata);
   if (size == -1)
@@ -251,7 +253,7 @@ 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)
+  if (next_ops->pread (nxdata, lba01, sizeof lba01, 0, 0, &err) == -1)
     return -1;

   /* Is it GPT? */
@@ -295,42 +297,46 @@ 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,
-                 void *handle, void *buf, uint32_t count, uint64_t offs)
+                 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);
+  return next_ops->pread (nxdata, buf, count, offs + h->offset, flags, err);
 }

 /* Write data. */
 static int
 partition_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
                   void *handle,
-                  const void *buf, uint32_t count, uint64_t offs)
+                  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);
+  return next_ops->pwrite (nxdata, buf, count, offs + h->offset, flags, err);
 }

 /* Trim data. */
 static int
 partition_trim (struct nbdkit_next_ops *next_ops, void *nxdata,
-                void *handle, uint32_t count, uint64_t offs)
+                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);
+  return next_ops->trim (nxdata, count, offs + h->offset, flags, err);
 }

 /* Zero data. */
 static int
 partition_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
-                void *handle, uint32_t count, uint64_t offs, int may_trim)
+                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, may_trim);
+  return next_ops->zero (nxdata, count, offs + h->offset, flags, err);
 }

 static struct nbdkit_filter filter = {
-- 
2.14.3




More information about the Libguestfs mailing list