qcow2: Ignore reserved bits in get_cluster_offset
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 <kwolf@redhat.com>
This commit is contained in:
parent
90b277593d
commit
68d000a390
@ -367,11 +367,9 @@ out:
|
||||
*
|
||||
* 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)
|
||||
{
|
||||
@ -407,19 +405,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;
|
||||
@ -431,26 +429,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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -449,7 +449,8 @@ static coroutine_fn 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 */
|
||||
@ -469,7 +470,9 @@ static coroutine_fn 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) {
|
||||
@ -479,7 +482,9 @@ static coroutine_fn 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;
|
||||
@ -520,6 +525,12 @@ static coroutine_fn 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;
|
||||
|
@ -165,6 +165,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;
|
||||
@ -182,6 +192,17 @@ static inline int64_t align_offset(int64_t offset, int n)
|
||||
return offset;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user