static int coroutine_fn qcow2_co_truncate()

in block/qcow2.c [4178:4552]


static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
                                          bool exact, PreallocMode prealloc,
                                          BdrvRequestFlags flags, Error **errp)
{
    BDRVQcow2State *s = bs->opaque;
    uint64_t old_length;
    int64_t new_l1_size;
    int ret;
    QDict *options;

    if (prealloc != PREALLOC_MODE_OFF && prealloc != PREALLOC_MODE_METADATA &&
        prealloc != PREALLOC_MODE_FALLOC && prealloc != PREALLOC_MODE_FULL)
    {
        error_setg(errp, "Unsupported preallocation mode '%s'",
                   PreallocMode_str(prealloc));
        return -ENOTSUP;
    }

    if (!QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)) {
        error_setg(errp, "The new size must be a multiple of %u",
                   (unsigned) BDRV_SECTOR_SIZE);
        return -EINVAL;
    }

    qemu_co_mutex_lock(&s->lock);

    /*
     * Even though we store snapshot size for all images, it was not
     * required until v3, so it is not safe to proceed for v2.
     */
    if (s->nb_snapshots && s->qcow_version < 3) {
        error_setg(errp, "Can't resize a v2 image which has snapshots");
        ret = -ENOTSUP;
        goto fail;
    }

    /* See qcow2-bitmap.c for which bitmap scenarios prevent a resize. */
    if (qcow2_truncate_bitmaps_check(bs, errp)) {
        ret = -ENOTSUP;
        goto fail;
    }

    old_length = bs->total_sectors * BDRV_SECTOR_SIZE;
    new_l1_size = size_to_l1(s, offset);

    if (offset < old_length) {
        int64_t last_cluster, old_file_size;
        if (prealloc != PREALLOC_MODE_OFF) {
            error_setg(errp,
                       "Preallocation can't be used for shrinking an image");
            ret = -EINVAL;
            goto fail;
        }

        ret = qcow2_cluster_discard(bs, ROUND_UP(offset, s->cluster_size),
                                    old_length - ROUND_UP(offset,
                                                          s->cluster_size),
                                    QCOW2_DISCARD_ALWAYS, true);
        if (ret < 0) {
            error_setg_errno(errp, -ret, "Failed to discard cropped clusters");
            goto fail;
        }

        ret = qcow2_shrink_l1_table(bs, new_l1_size);
        if (ret < 0) {
            error_setg_errno(errp, -ret,
                             "Failed to reduce the number of L2 tables");
            goto fail;
        }

        ret = qcow2_shrink_reftable(bs);
        if (ret < 0) {
            error_setg_errno(errp, -ret,
                             "Failed to discard unused refblocks");
            goto fail;
        }

        old_file_size = bdrv_getlength(bs->file->bs);
        if (old_file_size < 0) {
            error_setg_errno(errp, -old_file_size,
                             "Failed to inquire current file length");
            ret = old_file_size;
            goto fail;
        }
        last_cluster = qcow2_get_last_cluster(bs, old_file_size);
        if (last_cluster < 0) {
            error_setg_errno(errp, -last_cluster,
                             "Failed to find the last cluster");
            ret = last_cluster;
            goto fail;
        }
        if ((last_cluster + 1) * s->cluster_size < old_file_size) {
            Error *local_err = NULL;

            /*
             * Do not pass @exact here: It will not help the user if
             * we get an error here just because they wanted to shrink
             * their qcow2 image (on a block device) with qemu-img.
             * (And on the qcow2 layer, the @exact requirement is
             * always fulfilled, so there is no need to pass it on.)
             */
            bdrv_co_truncate(bs->file, (last_cluster + 1) * s->cluster_size,
                             false, PREALLOC_MODE_OFF, 0, &local_err);
            if (local_err) {
                warn_reportf_err(local_err,
                                 "Failed to truncate the tail of the image: ");
            }
        }
    } else {
        ret = qcow2_grow_l1_table(bs, new_l1_size, true);
        if (ret < 0) {
            error_setg_errno(errp, -ret, "Failed to grow the L1 table");
            goto fail;
        }

        if (data_file_is_raw(bs) && prealloc == PREALLOC_MODE_OFF) {
            /*
             * When creating a qcow2 image with data-file-raw, we enforce
             * at least prealloc=metadata, so that the L1/L2 tables are
             * fully allocated and reading from the data file will return
             * the same data as reading from the qcow2 image.  When the
             * image is grown, we must consequently preallocate the
             * metadata structures to cover the added area.
             */
            prealloc = PREALLOC_MODE_METADATA;
        }
    }

    switch (prealloc) {
    case PREALLOC_MODE_OFF:
        if (has_data_file(bs)) {
            /*
             * If the caller wants an exact resize, the external data
             * file should be resized to the exact target size, too,
             * so we pass @exact here.
             */
            ret = bdrv_co_truncate(s->data_file, offset, exact, prealloc, 0,
                                   errp);
            if (ret < 0) {
                goto fail;
            }
        }
        break;

    case PREALLOC_MODE_METADATA:
        ret = preallocate_co(bs, old_length, offset, prealloc, errp);
        if (ret < 0) {
            goto fail;
        }
        break;

    case PREALLOC_MODE_FALLOC:
    case PREALLOC_MODE_FULL:
    {
        int64_t allocation_start, host_offset, guest_offset;
        int64_t clusters_allocated;
        int64_t old_file_size, last_cluster, new_file_size;
        uint64_t nb_new_data_clusters, nb_new_l2_tables;
        bool subclusters_need_allocation = false;

        /* With a data file, preallocation means just allocating the metadata
         * and forwarding the truncate request to the data file */
        if (has_data_file(bs)) {
            ret = preallocate_co(bs, old_length, offset, prealloc, errp);
            if (ret < 0) {
                goto fail;
            }
            break;
        }

        old_file_size = bdrv_getlength(bs->file->bs);
        if (old_file_size < 0) {
            error_setg_errno(errp, -old_file_size,
                             "Failed to inquire current file length");
            ret = old_file_size;
            goto fail;
        }

        last_cluster = qcow2_get_last_cluster(bs, old_file_size);
        if (last_cluster >= 0) {
            old_file_size = (last_cluster + 1) * s->cluster_size;
        } else {
            old_file_size = ROUND_UP(old_file_size, s->cluster_size);
        }

        nb_new_data_clusters = (ROUND_UP(offset, s->cluster_size) -
            start_of_cluster(s, old_length)) >> s->cluster_bits;

        /* This is an overestimation; we will not actually allocate space for
         * these in the file but just make sure the new refcount structures are
         * able to cover them so we will not have to allocate new refblocks
         * while entering the data blocks in the potentially new L2 tables.
         * (We do not actually care where the L2 tables are placed. Maybe they
         *  are already allocated or they can be placed somewhere before
         *  @old_file_size. It does not matter because they will be fully
         *  allocated automatically, so they do not need to be covered by the
         *  preallocation. All that matters is that we will not have to allocate
         *  new refcount structures for them.) */
        nb_new_l2_tables = DIV_ROUND_UP(nb_new_data_clusters,
                                        s->cluster_size / l2_entry_size(s));
        /* The cluster range may not be aligned to L2 boundaries, so add one L2
         * table for a potential head/tail */
        nb_new_l2_tables++;

        allocation_start = qcow2_refcount_area(bs, old_file_size,
                                               nb_new_data_clusters +
                                               nb_new_l2_tables,
                                               true, 0, 0);
        if (allocation_start < 0) {
            error_setg_errno(errp, -allocation_start,
                             "Failed to resize refcount structures");
            ret = allocation_start;
            goto fail;
        }

        clusters_allocated = qcow2_alloc_clusters_at(bs, allocation_start,
                                                     nb_new_data_clusters);
        if (clusters_allocated < 0) {
            error_setg_errno(errp, -clusters_allocated,
                             "Failed to allocate data clusters");
            ret = clusters_allocated;
            goto fail;
        }

        assert(clusters_allocated == nb_new_data_clusters);

        /* Allocate the data area */
        new_file_size = allocation_start +
                        nb_new_data_clusters * s->cluster_size;
        /*
         * Image file grows, so @exact does not matter.
         *
         * If we need to zero out the new area, try first whether the protocol
         * driver can already take care of this.
         */
        if (flags & BDRV_REQ_ZERO_WRITE) {
            ret = bdrv_co_truncate(bs->file, new_file_size, false, prealloc,
                                   BDRV_REQ_ZERO_WRITE, NULL);
            if (ret >= 0) {
                flags &= ~BDRV_REQ_ZERO_WRITE;
                /* Ensure that we read zeroes and not backing file data */
                subclusters_need_allocation = true;
            }
        } else {
            ret = -1;
        }
        if (ret < 0) {
            ret = bdrv_co_truncate(bs->file, new_file_size, false, prealloc, 0,
                                   errp);
        }
        if (ret < 0) {
            error_prepend(errp, "Failed to resize underlying file: ");
            qcow2_free_clusters(bs, allocation_start,
                                nb_new_data_clusters * s->cluster_size,
                                QCOW2_DISCARD_OTHER);
            goto fail;
        }

        /* Create the necessary L2 entries */
        host_offset = allocation_start;
        guest_offset = old_length;
        while (nb_new_data_clusters) {
            int64_t nb_clusters = MIN(
                nb_new_data_clusters,
                s->l2_slice_size - offset_to_l2_slice_index(s, guest_offset));
            unsigned cow_start_length = offset_into_cluster(s, guest_offset);
            QCowL2Meta allocation;
            guest_offset = start_of_cluster(s, guest_offset);
            allocation = (QCowL2Meta) {
                .offset       = guest_offset,
                .alloc_offset = host_offset,
                .nb_clusters  = nb_clusters,
                .cow_start    = {
                    .offset       = 0,
                    .nb_bytes     = cow_start_length,
                },
                .cow_end      = {
                    .offset       = nb_clusters << s->cluster_bits,
                    .nb_bytes     = 0,
                },
                .prealloc     = !subclusters_need_allocation,
            };
            qemu_co_queue_init(&allocation.dependent_requests);

            ret = qcow2_alloc_cluster_link_l2(bs, &allocation);
            if (ret < 0) {
                error_setg_errno(errp, -ret, "Failed to update L2 tables");
                qcow2_free_clusters(bs, host_offset,
                                    nb_new_data_clusters * s->cluster_size,
                                    QCOW2_DISCARD_OTHER);
                goto fail;
            }

            guest_offset += nb_clusters * s->cluster_size;
            host_offset += nb_clusters * s->cluster_size;
            nb_new_data_clusters -= nb_clusters;
        }
        break;
    }

    default:
        g_assert_not_reached();
    }

    if ((flags & BDRV_REQ_ZERO_WRITE) && offset > old_length) {
        uint64_t zero_start = QEMU_ALIGN_UP(old_length, s->subcluster_size);

        /*
         * Use zero clusters as much as we can. qcow2_subcluster_zeroize()
         * requires a subcluster-aligned start. The end may be unaligned if
         * it is at the end of the image (which it is here).
         */
        if (offset > zero_start) {
            ret = qcow2_subcluster_zeroize(bs, zero_start, offset - zero_start,
                                           0);
            if (ret < 0) {
                error_setg_errno(errp, -ret, "Failed to zero out new clusters");
                goto fail;
            }
        }

        /* Write explicit zeros for the unaligned head */
        if (zero_start > old_length) {
            uint64_t len = MIN(zero_start, offset) - old_length;
            uint8_t *buf = qemu_blockalign0(bs, len);
            QEMUIOVector qiov;
            qemu_iovec_init_buf(&qiov, buf, len);

            qemu_co_mutex_unlock(&s->lock);
            ret = qcow2_co_pwritev_part(bs, old_length, len, &qiov, 0, 0);
            qemu_co_mutex_lock(&s->lock);

            qemu_vfree(buf);
            if (ret < 0) {
                error_setg_errno(errp, -ret, "Failed to zero out the new area");
                goto fail;
            }
        }
    }

    if (prealloc != PREALLOC_MODE_OFF) {
        /* Flush metadata before actually changing the image size */
        ret = qcow2_write_caches(bs);
        if (ret < 0) {
            error_setg_errno(errp, -ret,
                             "Failed to flush the preallocated area to disk");
            goto fail;
        }
    }

    bs->total_sectors = offset / BDRV_SECTOR_SIZE;

    /* write updated header.size */
    offset = cpu_to_be64(offset);
    ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, size),
                           &offset, sizeof(offset));
    if (ret < 0) {
        error_setg_errno(errp, -ret, "Failed to update the image size");
        goto fail;
    }

    s->l1_vm_state_index = new_l1_size;

    /* Update cache sizes */
    options = qdict_clone_shallow(bs->options);
    ret = qcow2_update_options(bs, options, s->flags, errp);
    qobject_unref(options);
    if (ret < 0) {
        goto fail;
    }
    ret = 0;
fail:
    qemu_co_mutex_unlock(&s->lock);
    return ret;
}