File systems not supporting FALLOC_FL_ZERO_RANGE yet fall back to
manual
 zeroing.
 We can avoid this by combining two fallocate calls:
     fallocate(FALLOC_FL_PUNCH_HOLE)
     fallocate(0)
 Based on my tests this is much more efficient compared to manual
 zeroing. The idea came from this qemu patch:
 
https://github.com/qemu/qemu/commit/1cdc3239f1bb
 Here is an example run with NFS 4.2 without this change, converting
 fedora 27 image created with virt-builder:
 $ export SOCK=/tmp/nbd.sock
 $ export FILE=/nfs-mount/fedora-27.img
 $ src/nbdkit plugins/file/.libs/nbdkit-file-plugin.so file=$FILE -U $SOCK
 $ time qemu-img convert -n -f raw -O raw /var/tmp/fedora-27.img
 nbd:unix:/tmp/nbd.sock
 real    0m17.481s
 user    0m0.199s
 sys     0m0.691s
 $ time qemu-img convert -n -f raw -O raw -W /var/tmp/fedora-27.img
 nbd:unix:/tmp/nbd.sock
 real    0m17.072s
 user    0m0.191s
 sys     0m0.738s
 With this change:
 $ time qemu-img convert -n -f raw -O raw /var/tmp/fedora-27.img
 nbd:unix:/tmp/nbd.sock
 real    0m6.285s
 user    0m0.217s
 sys     0m0.829s
 $ time qemu-img convert -n -f raw -O raw -W /var/tmp/fedora-27.img
 nbd:unix:/tmp/nbd.sock
 real    0m3.967s
 user    0m0.193s
 sys     0m0.702s
 Note: the image is sparse, but nbdkit creates a fully allocated image.
 This may be a bug in nbdkit or qemu-img.
 ---
  plugins/file/file.c | 32 ++++++++++++++++++++++++++++++++
  1 file changed, 32 insertions(+)
 diff --git a/plugins/file/file.c b/plugins/file/file.c
 index 5daab63..a2cea4a 100644
 --- a/plugins/file/file.c
 +++ b/plugins/file/file.c
 @@ -121,6 +121,7 @@ struct handle {
    int fd;
    bool can_punch_hole;
    bool can_zero_range;
 +  bool can_fallocate;
  };
  /* Create the per-connection handle. */
 @@ -161,6 +162,8 @@ file_open (int readonly)
    h->can_zero_range = false;
  #endif
 +  h->can_fallocate = true;
 +
    return h;
  }
 @@ -301,6 +304,35 @@ file_zero (void *handle, uint32_t count, uint64_t
 offset, int may_trim)
    }
  #endif
 +#ifdef FALLOC_FL_PUNCH_HOLE
 +  /* If we can punch hole but may not trim, we can combine punching hole
 and
 +   * fallocate to zero a range. This is expected to be more efficient than
 +   * writing zeros manually. */
 +  if (h->can_punch_hole && h->can_fallocate) {
 +    r = do_fallocate (h->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
 +                      offset, count);
 +    if (r == 0) {
 +      r = do_fallocate(h->fd, 0, offset, count);
 
 +      if (r == 0)
 +        return r;
 +
 +      if (errno != EOPNOTSUPP) {
 +        nbdkit_error ("zero: %m");
 +        return r;
 +      }
 +
 +      h->can_fallocate = false;
 +    } else {
 +      if (errno != EOPNOTSUPP) {
 +        nbdkit_error ("zero: %m");
 +        return r;
 +      }
 +
 +      h->can_punch_hole = false;
 +    }
 +  }
 +#endif
 +
    /* Trigger a fall back to writing */
    errno = EOPNOTSUPP;
    return r;
 --
 2.17.1