From 668be102d27fa47229431d3488021659d786135a Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Fri, 2 May 2014 16:58:35 -0500 Subject: [PATCH 03/26] qcow2: Ignore reserved bits in get_cluster_offset RH-Author: Max Reitz Message-id: <1399049936-13496-4-git-send-email-mreitz@redhat.com> Patchwork-id: 58649 O-Subject: [RHEL-6.6 qemu-kvm PATCH v3 03/24] qcow2: Ignore reserved bits in get_cluster_offset Bugzilla: 1004420 RH-Acked-by: Laszlo Ersek RH-Acked-by: Kevin Wolf RH-Acked-by: Stefan Hajnoczi From: Kevin Wolf BZ: 1004420 With this change, reading from a qcow2 image ignores all reserved bits that are set in an L1 or L2 table entry. Now get_cluster_offset() assigns *cluster_offset only the offset without any other flags. The cluster type is not longer encoded in the offset, but a positive return value in case of success. Signed-off-by: Kevin Wolf (cherry picked from commit 68d000a39074fe3888680491444a7fde2354cd84) Conflicts: block/qcow2-cluster.c block/qcow2.c block/qcow2.h As there is no qcow2_read() upstream, there was no need to adjust it to the new behavior of get_cluster_offset(). In RHEL 6, on the other hand, there is, and since it calls get_cluster_offset(), it has to be modified accordingly. qcow2_co_get_block_status() has been backported to RHEL 6 before this patch, whereas upstream it was exactly the other way around. This led to the downstream qcow2_co_get_block_status() to rely on the old get_cluster_offset() behavior. This deviation from upstream code can (and must) now be reverted, except for zero clusters which do not exist downstream. c10f3f53b8e446a501363dd614e9c619c8693266 introduced qcow2_max_refcount_clusters() into qcow2.h. These commits being out of order, a contextual conflict appears. Signed-off-by: Max Reitz --- block/qcow2-cluster.c | 53 +++++++++++++++++++++++++++++++++------------------ block/qcow2.c | 34 +++++++++++++++++++++++---------- block/qcow2.h | 21 ++++++++++++++++++++ 3 files changed, 79 insertions(+), 29 deletions(-) Signed-off-by: Jeff E. Nelson --- block/qcow2-cluster.c | 53 +++++++++++++++++++++++++++++++----------------- block/qcow2.c | 34 ++++++++++++++++++++++--------- block/qcow2.h | 21 +++++++++++++++++++ 3 files changed, 79 insertions(+), 29 deletions(-) diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 9a9faa8..0d488c4 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -308,7 +308,8 @@ static int qcow2_read(BlockDriverState *bs, int64_t sector_num, } index_in_cluster = sector_num & (s->cluster_sectors - 1); - if (!cluster_offset) { + switch (ret) { + case QCOW2_CLUSTER_UNALLOCATED: if (bs->backing_hd) { /* read from the base image */ iov.iov_base = buf; @@ -325,11 +326,13 @@ static int qcow2_read(BlockDriverState *bs, int64_t sector_num, } else { memset(buf, 0, 512 * n); } - } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { + break; + case QCOW2_CLUSTER_COMPRESSED: if (qcow2_decompress_cluster(bs, cluster_offset) < 0) return -1; memcpy(buf, s->cluster_cache + index_in_cluster * 512, 512 * n); - } else { + break; + case QCOW2_CLUSTER_NORMAL: BLKDBG_EVENT(bs->file, BLKDBG_READ); ret = bdrv_pread(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512); if (ret != n * 512) @@ -338,6 +341,9 @@ static int qcow2_read(BlockDriverState *bs, int64_t sector_num, qcow2_encrypt_sectors(s, sector_num, buf, buf, n, 0, &s->aes_decrypt_key); } + break; + default: + abort(); } nb_sectors -= n; sector_num += n; @@ -385,11 +391,9 @@ static int copy_sectors(BlockDriverState *bs, uint64_t start_sect, * * on exit, *num is the number of contiguous sectors we can read. * - * Return 0, if the offset is found - * Return -errno, otherwise. - * + * Returns the cluster type (QCOW2_CLUSTER_*) on success, -errno in error + * cases. */ - int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, int *num, uint64_t *cluster_offset) { @@ -425,19 +429,19 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, /* seek the the l2 offset in the l1 table */ l1_index = offset >> l1_bits; - if (l1_index >= s->l1_size) + if (l1_index >= s->l1_size) { + ret = QCOW2_CLUSTER_UNALLOCATED; goto out; + } - l2_offset = s->l1_table[l1_index]; - - /* seek the l2 table of the given l2 offset */ - - if (!l2_offset) + l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK; + if (!l2_offset) { + ret = QCOW2_CLUSTER_UNALLOCATED; goto out; + } /* load the l2 table in memory */ - l2_offset &= ~QCOW_OFLAG_COPIED; ret = l2_load(bs, l2_offset, &l2_table); if (ret < 0) { return ret; @@ -449,26 +453,37 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, *cluster_offset = be64_to_cpu(l2_table[l2_index]); nb_clusters = size_to_clusters(s, nb_needed << 9); - if (!*cluster_offset) { + ret = qcow2_get_cluster_type(*cluster_offset); + switch (ret) { + case QCOW2_CLUSTER_COMPRESSED: + /* Compressed clusters can only be processed one by one */ + c = 1; + *cluster_offset &= L2E_COMPRESSED_OFFSET_SIZE_MASK; + break; + case QCOW2_CLUSTER_UNALLOCATED: /* how many empty clusters ? */ c = count_contiguous_free_clusters(nb_clusters, &l2_table[l2_index]); - } else { + *cluster_offset = 0; + break; + case QCOW2_CLUSTER_NORMAL: /* how many allocated clusters ? */ c = count_contiguous_clusters(nb_clusters, s->cluster_size, &l2_table[l2_index], 0, QCOW_OFLAG_COPIED); + *cluster_offset &= L2E_OFFSET_MASK; + break; } qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); - nb_available = (c * s->cluster_sectors); + nb_available = (c * s->cluster_sectors); + out: if (nb_available > nb_needed) nb_available = nb_needed; *num = nb_available - index_in_cluster; - *cluster_offset &=~QCOW_OFLAG_COPIED; - return 0; + return ret; } /* diff --git a/block/qcow2.c b/block/qcow2.c index ec85f44..f836f64 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -473,6 +473,7 @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs, BDRVQcowState *s = bs->opaque; uint64_t cluster_offset; int index_in_cluster, ret; + int status = 0; *pnum = nb_sectors; qemu_co_mutex_lock(&s->lock); @@ -481,15 +482,17 @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs, if (ret < 0) { return ret; } - if (!cluster_offset) { - return 0; + + if (cluster_offset != 0 && ret != QCOW2_CLUSTER_COMPRESSED && + !s->crypt_method) { + index_in_cluster = sector_num & (s->cluster_sectors - 1); + cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS); + status |= BDRV_BLOCK_OFFSET_VALID | cluster_offset; } - if ((cluster_offset & QCOW_OFLAG_COMPRESSED) || s->crypt_method) { - return BDRV_BLOCK_DATA; + if (ret != QCOW2_CLUSTER_UNALLOCATED) { + status |= BDRV_BLOCK_DATA; } - index_in_cluster = sector_num & (s->cluster_sectors - 1); - cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS); - return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | cluster_offset; + return status; } /* handle reading after the end of the backing file */ @@ -546,7 +549,8 @@ static int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, qemu_iovec_copy(&hd_qiov, qiov, bytes_done, cur_nr_sectors * 512); - if (!cluster_offset) { + switch (ret) { + case QCOW2_CLUSTER_UNALLOCATED: if (bs->backing_hd) { /* read from the base image */ @@ -566,7 +570,9 @@ static int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, /* Note: in this case, no need to wait */ qemu_iovec_memset(&hd_qiov, 0, 512 * cur_nr_sectors); } - } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { + break; + + case QCOW2_CLUSTER_COMPRESSED: /* add AIO support for compressed blocks ? */ ret = qcow2_decompress_cluster(bs, cluster_offset); if (ret < 0) { @@ -576,7 +582,9 @@ static int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, qemu_iovec_from_buffer(&hd_qiov, s->cluster_cache + index_in_cluster * 512, 512 * cur_nr_sectors); - } else { + break; + + case QCOW2_CLUSTER_NORMAL: if ((cluster_offset & 511) != 0) { ret = -EIO; goto fail; @@ -617,6 +625,12 @@ static int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, qemu_iovec_from_buffer(&hd_qiov, cluster_data, 512 * cur_nr_sectors); } + break; + + default: + g_assert_not_reached(); + ret = -EIO; + goto fail; } remaining_sectors -= cur_nr_sectors; diff --git a/block/qcow2.h b/block/qcow2.h index 919dd27..23eb660 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -194,6 +194,16 @@ typedef struct QCowL2Meta QLIST_ENTRY(QCowL2Meta) next_in_flight; } QCowL2Meta; +enum { + QCOW2_CLUSTER_UNALLOCATED, + QCOW2_CLUSTER_NORMAL, + QCOW2_CLUSTER_COMPRESSED, +}; + +#define L1E_OFFSET_MASK 0x00ffffffffffff00ULL +#define L2E_OFFSET_MASK 0x00ffffffffffff00ULL +#define L2E_COMPRESSED_OFFSET_SIZE_MASK 0x3fffffffffffffffULL + static inline int size_to_clusters(BDRVQcowState *s, int64_t size) { return (size + (s->cluster_size - 1)) >> s->cluster_bits; @@ -216,6 +226,17 @@ static inline uint64_t qcow2_max_refcount_clusters(BDRVQcowState *s) return QCOW_MAX_REFTABLE_SIZE >> s->cluster_bits; } +static inline int qcow2_get_cluster_type(uint64_t l2_entry) +{ + if (l2_entry & QCOW_OFLAG_COMPRESSED) { + return QCOW2_CLUSTER_COMPRESSED; + } else if (!(l2_entry & L2E_OFFSET_MASK)) { + return QCOW2_CLUSTER_UNALLOCATED; + } else { + return QCOW2_CLUSTER_NORMAL; + } +} + // FIXME Need qcow2_ prefix to global functions /* qcow2.c functions */ -- 1.7.1