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 bounce_qiov;

    int64_t cluster_offset;

    unsigned int cluster_bytes;

    size_t skip_bytes;

    int ret;



    /* 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.

     */

    bdrv_round_to_clusters(bs, offset, bytes, &cluster_offset, &cluster_bytes);



    trace_bdrv_co_do_copy_on_readv(bs, offset, bytes,

                                   cluster_offset, cluster_bytes);



    iov.iov_len = cluster_bytes;

    iov.iov_base = bounce_buffer = qemu_try_blockalign(bs, iov.iov_len);

    if (bounce_buffer == NULL) {

        ret = -ENOMEM;

        goto err;

    }



    qemu_iovec_init_external(&bounce_qiov, &iov, 1);



    ret = bdrv_driver_preadv(bs, cluster_offset, cluster_bytes,

                             &bounce_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, iov.iov_len)) {

        /* 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, cluster_bytes, 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, cluster_bytes,

                                  &bounce_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;

    }



    skip_bytes = offset - cluster_offset;

    qemu_iovec_from_buf(qiov, 0, bounce_buffer + skip_bytes, bytes);



err:

    qemu_vfree(bounce_buffer);

    return ret;

}
