diff options
author | Alberto Garcia <berto@igalia.com> | 2020-06-17 16:00:36 +0200 |
---|---|---|
committer | Max Reitz <mreitz@redhat.com> | 2020-07-06 08:33:06 +0200 |
commit | a5675f390116a927e320984f02f473a6affc9fcd (patch) | |
tree | 33283366384bc7c3703cf36c497761c4afd7c018 /block/qcow2.c | |
parent | e8de7ba9ea086c427cd36a10bc3506ac20aa4895 (diff) |
qcow2: Fix preallocation on images with unaligned sizes
When resizing an image with qcow2_co_truncate() using the falloc or
full preallocation modes the code assumes that both the old and new
sizes are cluster-aligned.
There are two problems with this:
1) The calculation of how many clusters are involved does not always
get the right result.
Example: creating a 60KB image and resizing it (with
preallocation=full) to 80KB won't allocate the second cluster.
2) No copy-on-write is performed, so in the previous example if
there is a backing file then the first 60KB of the first cluster
won't be filled with data from the backing file.
This patch fixes both issues.
Signed-off-by: Alberto Garcia <berto@igalia.com>
Message-Id: <20200617140036.20311-1-berto@igalia.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>
Diffstat (limited to 'block/qcow2.c')
-rw-r--r-- | block/qcow2.c | 17 |
1 files changed, 14 insertions, 3 deletions
diff --git a/block/qcow2.c b/block/qcow2.c index 0cd2e6757e..e20590c3b7 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -4239,8 +4239,8 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, old_file_size = ROUND_UP(old_file_size, s->cluster_size); } - nb_new_data_clusters = DIV_ROUND_UP(offset - old_length, - 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 @@ -4317,10 +4317,21 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, int64_t nb_clusters = MIN( nb_new_data_clusters, s->l2_slice_size - offset_to_l2_slice_index(s, guest_offset)); - QCowL2Meta allocation = { + 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, + }, }; qemu_co_queue_init(&allocation.dependent_requests); |