summaryrefslogtreecommitdiffstats
path: root/block/qcow2-cluster.c
diff options
context:
space:
mode:
authorPeter Maydell2020-02-06 17:22:05 +0100
committerPeter Maydell2020-02-06 17:22:05 +0100
commit863d2ed5823f90c42dcd481687cc99cbc9c4a17c (patch)
treeaa97d37140f93f48fc52401891dcd767fd3f0b39 /block/qcow2-cluster.c
parentMerge remote-tracking branch 'remotes/vivier2/tags/trivial-branch-pull-reques... (diff)
parentiotests: add test for backup-top failure on permission activation (diff)
downloadqemu-863d2ed5823f90c42dcd481687cc99cbc9c4a17c.tar.gz
qemu-863d2ed5823f90c42dcd481687cc99cbc9c4a17c.tar.xz
qemu-863d2ed5823f90c42dcd481687cc99cbc9c4a17c.zip
Merge remote-tracking branch 'remotes/maxreitz/tags/pull-block-2020-02-06' into staging
Block patches: - Drop BDRV_SECTOR_SIZE from qcow2 - Allow Python iotests to be added to the auto group (and add some) - Fix for the backup job - Fix memleak in bdrv_refresh_filename() - Use GStrings in two places for greater efficiency (than manually handling string allocation) # gpg: Signature made Thu 06 Feb 2020 12:50:14 GMT # gpg: using RSA key 91BEB60A30DB3E8857D11829F407DB0061D5CF40 # gpg: issuer "mreitz@redhat.com" # gpg: Good signature from "Max Reitz <mreitz@redhat.com>" [full] # Primary key fingerprint: 91BE B60A 30DB 3E88 57D1 1829 F407 DB00 61D5 CF40 * remotes/maxreitz/tags/pull-block-2020-02-06: iotests: add test for backup-top failure on permission activation block/backup-top: fix failure path qcow2: Use BDRV_SECTOR_SIZE instead of the hardcoded value qcow2: Don't require aligned offsets in qcow2_co_copy_range_from() qcow2: Use bs->bl.request_alignment when updating an L1 entry qcow2: Tighten cluster_offset alignment assertions qcow2: Don't round the L1 table allocation up to the sector size iotests: Enable more tests in the 'auto' group to improve test coverage iotests: Skip Python-based tests if QEMU does not support virtio-blk iotests: Check for the availability of the required devices in 267 and 127 iotests: Test 183 does not work on macOS and OpenBSD iotests: Test 041 only works on certain systems iotests: remove 'linux' from default supported platforms qcow2: Use a GString in report_unsupported_feature() block: fix memleaks in bdrv_refresh_filename block: Use a GString in bdrv_perm_names() qcow2: Assert that host cluster offsets fit in L2 table entries Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'block/qcow2-cluster.c')
-rw-r--r--block/qcow2-cluster.c44
1 files changed, 29 insertions, 15 deletions
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index 8982b7b762..1947f13a2d 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -124,12 +124,11 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
#endif
new_l1_size2 = sizeof(uint64_t) * new_l1_size;
- new_l1_table = qemu_try_blockalign(bs->file->bs,
- ROUND_UP(new_l1_size2, 512));
+ new_l1_table = qemu_try_blockalign(bs->file->bs, new_l1_size2);
if (new_l1_table == NULL) {
return -ENOMEM;
}
- memset(new_l1_table, 0, ROUND_UP(new_l1_size2, 512));
+ memset(new_l1_table, 0, new_l1_size2);
if (s->l1_size) {
memcpy(new_l1_table, s->l1_table, s->l1_size * sizeof(uint64_t));
@@ -217,26 +216,31 @@ static int l2_load(BlockDriverState *bs, uint64_t offset,
}
/*
- * Writes one sector of the L1 table to the disk (can't update single entries
- * and we really don't want bdrv_pread to perform a read-modify-write)
+ * Writes an L1 entry to disk (note that depending on the alignment
+ * requirements this function may write more that just one entry in
+ * order to prevent bdrv_pwrite from performing a read-modify-write)
*/
-#define L1_ENTRIES_PER_SECTOR (512 / 8)
int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index)
{
BDRVQcow2State *s = bs->opaque;
- uint64_t buf[L1_ENTRIES_PER_SECTOR] = { 0 };
int l1_start_index;
int i, ret;
+ int bufsize = MAX(sizeof(uint64_t),
+ MIN(bs->file->bs->bl.request_alignment, s->cluster_size));
+ int nentries = bufsize / sizeof(uint64_t);
+ g_autofree uint64_t *buf = g_try_new0(uint64_t, nentries);
- l1_start_index = l1_index & ~(L1_ENTRIES_PER_SECTOR - 1);
- for (i = 0; i < L1_ENTRIES_PER_SECTOR && l1_start_index + i < s->l1_size;
- i++)
- {
+ if (buf == NULL) {
+ return -ENOMEM;
+ }
+
+ l1_start_index = QEMU_ALIGN_DOWN(l1_index, nentries);
+ for (i = 0; i < MIN(nentries, s->l1_size - l1_start_index); i++) {
buf[i] = cpu_to_be64(s->l1_table[l1_start_index + i]);
}
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L1,
- s->l1_table_offset + 8 * l1_start_index, sizeof(buf), false);
+ s->l1_table_offset + 8 * l1_start_index, bufsize, false);
if (ret < 0) {
return ret;
}
@@ -244,7 +248,7 @@ int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index)
BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE);
ret = bdrv_pwrite_sync(bs->file,
s->l1_table_offset + 8 * l1_start_index,
- buf, sizeof(buf));
+ buf, bufsize);
if (ret < 0) {
return ret;
}
@@ -777,6 +781,10 @@ int qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
(cluster_offset + compressed_size - 1) / QCOW2_COMPRESSED_SECTOR_SIZE -
(cluster_offset / QCOW2_COMPRESSED_SECTOR_SIZE);
+ /* The offset and size must fit in their fields of the L2 table entry */
+ assert((cluster_offset & s->cluster_offset_mask) == cluster_offset);
+ assert((nb_csectors & s->csize_mask) == nb_csectors);
+
cluster_offset |= QCOW_OFLAG_COMPRESSED |
((uint64_t)nb_csectors << s->csize_shift);
@@ -972,6 +980,7 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
assert(l2_index + m->nb_clusters <= s->l2_slice_size);
for (i = 0; i < m->nb_clusters; i++) {
+ uint64_t offset = cluster_offset + (i << s->cluster_bits);
/* if two concurrent writes happen to the same unallocated cluster
* each write allocates separate cluster and writes data concurrently.
* The first one to complete updates l2 table with pointer to its
@@ -982,8 +991,10 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
old_cluster[j++] = l2_slice[l2_index + i];
}
- l2_slice[l2_index + i] = cpu_to_be64((cluster_offset +
- (i << s->cluster_bits)) | QCOW_OFLAG_COPIED);
+ /* The offset must fit in the offset field of the L2 table entry */
+ assert((offset & L2E_OFFSET_MASK) == offset);
+
+ l2_slice[l2_index + i] = cpu_to_be64(offset | QCOW_OFLAG_COPIED);
}
@@ -1913,6 +1924,9 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
goto fail;
}
+ /* The offset must fit in the offset field */
+ assert((offset & L2E_OFFSET_MASK) == offset);
+
if (l2_refcount > 1) {
/* For shared L2 tables, set the refcount accordingly
* (it is already 1 and needs to be l2_refcount) */