func: static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child,
          int64_t offset, unsigned int bytes, QEMUIOVector *qiov)
  {
      BlockDriverState *bs = child->bs;
      /* Perform I/O through a temporary buffer so that users who scribble over
       * their read buffer while the operation is in progress do not end up
       * modifying the image file.  This is critical for zero-copy guest I/O
       * where anything might happen inside guest memory.
       */
      void *bounce_buffer;
      BlockDriver *drv = bs->drv;
      struct iovec iov;
      QEMUIOVector local_qiov;
      int64_t cluster_offset;
      int64_t cluster_bytes;
      size_t skip_bytes;
      int ret;
      int max_transfer = MIN_NON_ZERO(bs->bl.max_transfer,
                                      BDRV_REQUEST_MAX_BYTES);
      unsigned int progress = 0;
      /* FIXME We cannot require callers to have write permissions when all they
       * are doing is a read request. If we did things right, write permissions
       * would be obtained anyway, but internally by the copy-on-read code. As
       * long as it is implemented here rather than in a separate filter driver,
       * the copy-on-read code doesn't have its own BdrvChild, however, for which
       * it could request permissions. Therefore we have to bypass the permission
       * system for the moment. */
      // assert(child->perm & (BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE));
      /* Cover entire cluster so no additional backing file I/O is required when
       * allocating cluster in the image file.  Note that this value may exceed
       * BDRV_REQUEST_MAX_BYTES (even when the original read did not), which
       * is one reason we loop rather than doing it all at once.
       */
      bdrv_round_to_clusters(bs, offset, bytes, &cluster_offset, &cluster_bytes);
      skip_bytes = offset - cluster_offset;
      trace_bdrv_co_do_copy_on_readv(bs, offset, bytes,
                                     cluster_offset, cluster_bytes);
      bounce_buffer = qemu_try_blockalign(bs,
                                          MIN(MIN(max_transfer, cluster_bytes),
                                              MAX_BOUNCE_BUFFER));
      if (bounce_buffer == NULL) {
          ret = -ENOMEM;
          goto err;
      while (cluster_bytes) {
          int64_t pnum;
          ret = bdrv_is_allocated(bs, cluster_offset,
                                  MIN(cluster_bytes, max_transfer), &pnum);
          if (ret < 0) {
              /* Safe to treat errors in querying allocation as if
               * unallocated; we'll probably fail again soon on the
               * read, but at least that will set a decent errno.
               */
              pnum = MIN(cluster_bytes, max_transfer);
          assert(skip_bytes < pnum);
          if (ret <= 0) {
              /* Must copy-on-read; use the bounce buffer */
              iov.iov_base = bounce_buffer;
              iov.iov_len = pnum = MIN(pnum, MAX_BOUNCE_BUFFER);
              qemu_iovec_init_external(&local_qiov, &iov, 1);
              ret = bdrv_driver_preadv(bs, cluster_offset, pnum,
                                       &local_qiov, 0);
              if (ret < 0) {
                  goto err;
              bdrv_debug_event(bs, BLKDBG_COR_WRITE);
              if (drv->bdrv_co_pwrite_zeroes &&
                  buffer_is_zero(bounce_buffer, pnum)) {
                  /* FIXME: Should we (perhaps conditionally) be setting
                   * BDRV_REQ_MAY_UNMAP, if it will allow for a sparser copy
                   * that still correctly reads as zero? */
                  ret = bdrv_co_do_pwrite_zeroes(bs, cluster_offset, pnum, 0);
              } else {
                  /* This does not change the data on the disk, it is not
                   * necessary to flush even in cache=writethrough mode.
                   */
                  ret = bdrv_driver_pwritev(bs, cluster_offset, pnum,
                                            &local_qiov, 0);
              if (ret < 0) {
                  /* It might be okay to ignore write errors for guest
                   * requests.  If this is a deliberate copy-on-read
                   * then we don't want to ignore the error.  Simply
                   * report it in all cases.
                   */
                  goto err;
              qemu_iovec_from_buf(qiov, progress, bounce_buffer + skip_bytes,
                                  pnum - skip_bytes);
          } else {
              /* Read directly into the destination */
              qemu_iovec_init(&local_qiov, qiov->niov);
              qemu_iovec_concat(&local_qiov, qiov, progress, pnum - skip_bytes);
              ret = bdrv_driver_preadv(bs, offset + progress, local_qiov.size,
                                       &local_qiov, 0);
              qemu_iovec_destroy(&local_qiov);
              if (ret < 0) {
                  goto err;
          cluster_offset += pnum;
          cluster_bytes -= pnum;
          progress += pnum - skip_bytes;
          skip_bytes = 0;
      ret = 0;
  err:
      qemu_vfree(bounce_buffer);
      return ret;