On Tue, Jul 07, 2020 at 05:22:46PM -0500, Eric Blake wrote:
[...]
 +/* Compute aligned extents on behalf of a filter. */
 +int
 +nbdkit_extents_aligned (struct nbdkit_next_ops *next_ops,
 +                        nbdkit_backend *nxdata,
 +                        uint32_t count, uint64_t offset,
 +                        uint32_t flags, uint32_t align,
 +                        struct nbdkit_extents *exts, int *err)
 +{
 +  size_t i;
 +  struct nbdkit_extent e, e2;
 +
 +  if (!IS_ALIGNED(count | offset, align)) {
 +    nbdkit_error ("nbdkit_extents_aligned: unaligned request");
 +    *err = EINVAL;
 +    return -1;
 +  } 
I wonder if this also should be an assert?  This is less clear to me
than the vector case however.
 +  /* Perform an initial query, then scan for the first unaligned
extent. */
 +  if (next_ops->extents (nxdata, count, offset, flags, exts, err) == -1)
 +    return -1;
 +  for (i = 0; i < exts->extents.size; ++i) {
 +    e = exts->extents.ptr[i];
 +    if (!IS_ALIGNED(e.length, align)) {
 +      /* If the unalignment is past align, just truncate and return early */
 +      if (e.offset + e.length > offset + align) {
 +        e.length = ROUND_DOWN (e.length, align);
 +        exts->extents.size = i + !!e.length;
 +        exts->next = e.offset + e.length;
 +        break;
 +      }
 +
 +      /* Otherwise, coalesce until we have at least align bytes, which
 +       * may require further queries.
 +       */
 +      assert (i == 0);
 +      while (e.length < align) {
 +        if (exts->extents.size > 1) {
 +          e.length += exts->extents.ptr[1].length;
 +          e.type &= exts->extents.ptr[1].type;
 +          extents_remove (&exts->extents, 1);
 +        }
 +        else {
 +          /* The plugin needs a fresh extents object each time, but
 +           * with care, we can merge it into the callers' extents.
 +           */
 +          extents tmp;
 +          CLEANUP_EXTENTS_FREE struct nbdkit_extents *extents2 = NULL;
 +
 +          extents2 = nbdkit_extents_new (e.offset + e.length, offset + align);
 +          if (next_ops->extents (nxdata, offset + align - e.length,
 +                                 e.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);
 +          e2.offset = e.offset;
 +          e2.length += e.length;
 +          e2.type &= e.type; 
So we're intersecting (&) the types defined as:
#define NBDKIT_EXTENT_HOLE    (1<<0) /* Same as NBD_STATE_HOLE */
#define NBDKIT_EXTENT_ZERO    (1<<1) /* Same as NBD_STATE_ZERO */
If all extents are holes, then it's a hole.  If all extents are zero,
then it's a zero.  Otherwise it's non-zero data.
This seems correct.
All looks good to me, so ACK.
Rich.
-- 
Richard Jones, Virtualization Group, Red Hat 
http://people.redhat.com/~rjones
Read my programming and virtualization blog: 
http://rwmj.wordpress.com
virt-builder quickly builds VMs from scratch
http://libguestfs.org/virt-builder.1.html