qcow2: truncate the tail of the image file after shrinking the image

Now after shrinking the image, at the end of the image file, there might be a
tail that probably will never be used. So we can find the last used cluster and
cut the tail.

Signed-off-by: Pavel Butsykin <pbutsykin@virtuozzo.com>
Reviewed-by: John Snow <jsnow@redhat.com>
Message-id: 20170929121613.25997-3-pbutsykin@virtuozzo.com
Signed-off-by: Max Reitz <mreitz@redhat.com>
This commit is contained in:
Pavel Butsykin 2017-09-29 15:16:13 +03:00 committed by Max Reitz
parent 76a2a30a99
commit 163bc39d2c
3 changed files with 46 additions and 0 deletions

View File

@ -3181,3 +3181,25 @@ out:
g_free(reftable_tmp);
return ret;
}
int64_t qcow2_get_last_cluster(BlockDriverState *bs, int64_t size)
{
BDRVQcow2State *s = bs->opaque;
int64_t i;
for (i = size_to_clusters(s, size) - 1; i >= 0; i--) {
uint64_t refcount;
int ret = qcow2_get_refcount(bs, i, &refcount);
if (ret < 0) {
fprintf(stderr, "Can't get refcount for cluster %" PRId64 ": %s\n",
i, strerror(-ret));
return ret;
}
if (refcount > 0) {
return i;
}
}
qcow2_signal_corruption(bs, true, -1, -1,
"There are no references in the refcount table.");
return -EIO;
}

View File

@ -3107,6 +3107,7 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset,
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");
@ -3135,6 +3136,28 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset,
"Failed to discard unused refblocks");
return ret;
}
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");
return old_file_size;
}
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");
return last_cluster;
}
if ((last_cluster + 1) * s->cluster_size < old_file_size) {
ret = bdrv_truncate(bs->file, (last_cluster + 1) * s->cluster_size,
PREALLOC_MODE_OFF, NULL);
if (ret < 0) {
warn_report("Failed to truncate the tail of the image: %s",
strerror(-ret));
ret = 0;
}
}
} else {
ret = qcow2_grow_l1_table(bs, new_l1_size, true);
if (ret < 0) {

View File

@ -597,6 +597,7 @@ int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order,
BlockDriverAmendStatusCB *status_cb,
void *cb_opaque, Error **errp);
int qcow2_shrink_reftable(BlockDriverState *bs);
int64_t qcow2_get_last_cluster(BlockDriverState *bs, int64_t size);
/* qcow2-cluster.c functions */
int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,