@@ -463,6 +463,14 @@ typedef struct QCowL2Meta
*/
bool skip_cow;
+ /**
+ * Indicates that this is not a normal write request but a preallocation.
+ * If the image has extended L2 entries this means that no new individual
+ * subclusters will be marked as allocated in the L2 bitmap (but any
+ * existing contents of that bitmap will be kept).
+ */
+ bool prealloc;
+
/**
* The I/O vector with the data from the actual guest write request.
* If non-NULL, this is meant to be merged together with the data
@@ -1067,7 +1067,7 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
set_l2_entry(s, l2_slice, l2_index + i, offset | QCOW_OFLAG_COPIED);
/* Update bitmap with the subclusters that were just written */
- if (has_subclusters(s)) {
+ if (has_subclusters(s) && !m->prealloc) {
uint64_t l2_bitmap = get_l2_bitmap(s, l2_slice, l2_index + i);
unsigned written_from = m->cow_start.offset;
unsigned written_to = m->cow_end.offset + m->cow_end.nb_bytes ?:
@@ -2096,6 +2096,7 @@ static coroutine_fn int qcow2_handle_l2meta(BlockDriverState *bs,
QCowL2Meta *next;
if (link_l2) {
+ assert(!l2meta->prealloc);
ret = qcow2_alloc_cluster_link_l2(bs, l2meta);
if (ret) {
goto out;
@@ -3130,6 +3131,7 @@ static int coroutine_fn preallocate_co(BlockDriverState *bs, uint64_t offset,
while (meta) {
QCowL2Meta *next = meta->next;
+ meta->prealloc = true;
ret = qcow2_alloc_cluster_link_l2(bs, meta);
if (ret < 0) {
@@ -4217,6 +4219,7 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t 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 */
@@ -4298,6 +4301,8 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
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;
@@ -4336,6 +4341,7 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
.offset = nb_clusters << s->cluster_bits,
.nb_bytes = 0,
},
+ .prealloc = !subclusters_need_allocation,
};
qemu_co_queue_init(&allocation.dependent_requests);