[Libguestfs] [PATCH nbdkit 1/6] filters: Add a function to read full extents from a plugin.

Richard W.M. Jones
Tue, 26 Jan 2021 21:51:47 -0000

This convenience function makes it a bit simpler to read a full set of
extents covering a range from a plugin, especially for plugins which
don't reply with a full set of extents in a single call.
---
 docs/nbdkit-filter.pod  | 22 +++++++++++++++++
 include/nbdkit-filter.h |  5 ++++
 server/extents.c        | 54 +++++++++++++++++++++++++++++++++++++++++
 server/nbdkit.syms      |  1 +
 4 files changed, 82 insertions(+)

diff --git a/docs/nbdkit-filter.pod b/docs/nbdkit-filter.pod
index 82b1ade1..98a08ca4 100644
--- a/docs/nbdkit-filter.pod
+++ b/docs/nbdkit-filter.pod
@@ -857,6 +857,28 @@ Returns the number of extents in the list.

Returns a copy of the C'th extent.

+=head3 Reading the full extents from the plugin
+
+A convenience function is provided to filters only which makes one or
+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,
+                             nbdkit_backend *nxdata,
+                             uint32_t count, uint64_t offset,
+                             uint32_t flags, int *err);
+
+Note this allocates a new C<struct nbdkit_extents> which the caller
+must free.  C<flags> is passed through to the underlying plugin, but
+C<NBDKIT_FLAG_REQ_ONE> is removed from the set of flags so that the
+plugin returns as much information as possible (this is usually what
+you want).
+
+On error this function can return C<NULL>.  In this case it calls
+C<nbdkit_error> and/or C<nbdkit_set_error> as required.
C<*err> will
+be set to a suitable value.
+
 =head3 Enforcing alignment of an nbdkit_extents list

 A convenience function is provided to filters only which makes it
diff --git a/include/nbdkit-filter.h b/include/nbdkit-filter.h
index afe83e0b..0964c6e7 100644
--- a/include/nbdkit-filter.h
+++ b/include/nbdkit-filter.h
@@ -124,6 +124,11 @@ NBDKIT_EXTERN_DECL (size_t, nbdkit_extents_count,
                     (const struct nbdkit_extents *));
 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_backend *nxdata,
+                     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_backend *nxdata,
diff --git a/server/extents.c b/server/extents.c
index 5f6f3e2e..a081156e 100644
--- a/server/extents.c
+++ b/server/extents.c
@@ -292,3 +292,57 @@ nbdkit_extents_aligned (struct nbdkit_next_ops *next_ops,
   /* Once we get here, all extents are aligned. */
   return 0;
 }
+
+/* This is a convenient wrapper around next_ops->extents which can be
+ * used from filters where you want to get a complete set of extents
+ * covering the region [offset..offset+count-1].
+ */
+struct nbdkit_extents *
+nbdkit_extents_full (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,
+                     uint32_t count, uint64_t offset, uint32_t flags,
+                     int *err)
+{
+  struct nbdkit_extents *ret;
+
+  /* Clear REQ_ONE to ask the plugin for as much information as it is
+   * willing to return (the plugin may still truncate if it is too
+   * costly to provide everything).
+   */
+  flags &= ~NBDKIT_FLAG_REQ_ONE;
+
+  ret = nbdkit_extents_new (offset, offset+count);
+  if (ret == NULL) goto error0;
+
+  while (count > 0) {
+    const uint64_t old_offset = offset;
+    size_t i;
+
+    CLEANUP_EXTENTS_FREE struct nbdkit_extents *t
+      = nbdkit_extents_new (offset, offset+count);
+    if (t == NULL) goto error1;
+
+    if (next_ops->extents (nxdata, count, offset, flags, t, err) == -1)
+      goto error0;
+
+    for (i = 0; i < nbdkit_extents_count (t); ++i) {
+      const struct nbdkit_extent e = nbdkit_get_extent (t, i);
+      if (nbdkit_add_extent (ret, e.offset, e.length, e.type) == -1)
+        goto error1;
+
+      assert (e.length <= count);
+      offset += e.length;
+      count -= e.length;
+    }
+
+    /* If the plugin is behaving we must make forward progress. */
+    assert (offset > old_offset);
+  }
+
+  return ret;
+
+ error1:
+  *err = errno;
+ error0:
+  if (ret) nbdkit_extents_free (ret);
+  return NULL;
+}
diff --git a/server/nbdkit.syms b/server/nbdkit.syms
index 3d6b2235..20ee27f3 100644
--- a/server/nbdkit.syms
+++ b/server/nbdkit.syms
@@ -51,6 +51,7 @@
     nbdkit_extents_aligned;
     nbdkit_extents_count;
     nbdkit_extents_free;
+    nbdkit_extents_full;
     nbdkit_extents_new;
     nbdkit_get_extent;
     nbdkit_is_tls;
--
2.29.0.rc2