static int coroutine_fn copy_sectors(BlockDriverState *bs,
                                     uint64_t start_sect,
                                     uint64_t cluster_offset,
                                     int n_start, int n_end)
{
    BDRVQcowState *s = bs->opaque;
    QEMUIOVector qiov;
    struct iovec iov;
    int n, ret;
    /*
     * If this is the last cluster and it is only partially used, we must only
     * copy until the end of the image, or bdrv_check_request will fail for the
     * bdrv_read/write calls below.
     */
    if (start_sect + n_end > bs->total_sectors) {
        n_end = bs->total_sectors - start_sect;
    n = n_end - n_start;
    if (n <= 0) {
        return 0;
    iov.iov_len = n * BDRV_SECTOR_SIZE;
    iov.iov_base = qemu_blockalign(bs, iov.iov_len);
    qemu_iovec_init_external(&qiov, &iov, 1);
    BLKDBG_EVENT(bs->file, BLKDBG_COW_READ);
    /* Call .bdrv_co_readv() directly instead of using the public block-layer
     * interface.  This avoids double I/O throttling and request tracking,
     * which can lead to deadlock when block layer copy-on-read is enabled.
     */
    ret = bs->drv->bdrv_co_readv(bs, start_sect + n_start, n, &qiov);
    if (ret < 0) {
        goto out;
    if (s->crypt_method) {
        qcow2_encrypt_sectors(s, start_sect + n_start,
                        iov.iov_base, iov.iov_base, n, 1,
                        &s->aes_encrypt_key);
    ret = qcow2_pre_write_overlap_check(bs, 0,
            cluster_offset + n_start * BDRV_SECTOR_SIZE, n * BDRV_SECTOR_SIZE);
    if (ret < 0) {
        goto out;
    BLKDBG_EVENT(bs->file, BLKDBG_COW_WRITE);
    ret = bdrv_co_writev(bs->file, (cluster_offset >> 9) + n_start, n, &qiov);
    if (ret < 0) {
        goto out;
    ret = 0;
out:
    qemu_vfree(iov.iov_base);
    return ret;