mirror of https://github.com/xemu-project/xemu.git
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.
|
* on exit, *num is the number of contiguous sectors we can read.
|
||||||
*
|
*
|
||||||
* Return 0, if the offset is found
|
* Returns the cluster type (QCOW2_CLUSTER_*) on success, -errno in error
|
||||||
* Return -errno, otherwise.
|
* cases.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||||
int *num, uint64_t *cluster_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 */
|
/* seek the the l2 offset in the l1 table */
|
||||||
|
|
||||||
l1_index = offset >> l1_bits;
|
l1_index = offset >> l1_bits;
|
||||||
if (l1_index >= s->l1_size)
|
if (l1_index >= s->l1_size) {
|
||||||
|
ret = QCOW2_CLUSTER_UNALLOCATED;
|
||||||
goto out;
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
l2_offset = s->l1_table[l1_index];
|
l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK;
|
||||||
|
if (!l2_offset) {
|
||||||
/* seek the l2 table of the given l2 offset */
|
ret = QCOW2_CLUSTER_UNALLOCATED;
|
||||||
|
|
||||||
if (!l2_offset)
|
|
||||||
goto out;
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
/* load the l2 table in memory */
|
/* load the l2 table in memory */
|
||||||
|
|
||||||
l2_offset &= ~QCOW_OFLAG_COPIED;
|
|
||||||
ret = l2_load(bs, l2_offset, &l2_table);
|
ret = l2_load(bs, l2_offset, &l2_table);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
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]);
|
*cluster_offset = be64_to_cpu(l2_table[l2_index]);
|
||||||
nb_clusters = size_to_clusters(s, nb_needed << 9);
|
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 ? */
|
/* how many empty clusters ? */
|
||||||
c = count_contiguous_free_clusters(nb_clusters, &l2_table[l2_index]);
|
c = count_contiguous_free_clusters(nb_clusters, &l2_table[l2_index]);
|
||||||
} else {
|
*cluster_offset = 0;
|
||||||
|
break;
|
||||||
|
case QCOW2_CLUSTER_NORMAL:
|
||||||
/* how many allocated clusters ? */
|
/* how many allocated clusters ? */
|
||||||
c = count_contiguous_clusters(nb_clusters, s->cluster_size,
|
c = count_contiguous_clusters(nb_clusters, s->cluster_size,
|
||||||
&l2_table[l2_index], 0, QCOW_OFLAG_COPIED);
|
&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);
|
qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
|
||||||
|
|
||||||
nb_available = (c * s->cluster_sectors);
|
nb_available = (c * s->cluster_sectors);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
if (nb_available > nb_needed)
|
if (nb_available > nb_needed)
|
||||||
nb_available = nb_needed;
|
nb_available = nb_needed;
|
||||||
|
|
||||||
*num = nb_available - index_in_cluster;
|
*num = nb_available - index_in_cluster;
|
||||||
|
|
||||||
*cluster_offset &=~QCOW_OFLAG_COPIED;
|
return ret;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -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,
|
qemu_iovec_copy(&hd_qiov, qiov, bytes_done,
|
||||||
cur_nr_sectors * 512);
|
cur_nr_sectors * 512);
|
||||||
|
|
||||||
if (!cluster_offset) {
|
switch (ret) {
|
||||||
|
case QCOW2_CLUSTER_UNALLOCATED:
|
||||||
|
|
||||||
if (bs->backing_hd) {
|
if (bs->backing_hd) {
|
||||||
/* read from the base image */
|
/* 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 */
|
/* Note: in this case, no need to wait */
|
||||||
qemu_iovec_memset(&hd_qiov, 0, 512 * cur_nr_sectors);
|
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 ? */
|
/* add AIO support for compressed blocks ? */
|
||||||
ret = qcow2_decompress_cluster(bs, cluster_offset);
|
ret = qcow2_decompress_cluster(bs, cluster_offset);
|
||||||
if (ret < 0) {
|
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,
|
qemu_iovec_from_buffer(&hd_qiov,
|
||||||
s->cluster_cache + index_in_cluster * 512,
|
s->cluster_cache + index_in_cluster * 512,
|
||||||
512 * cur_nr_sectors);
|
512 * cur_nr_sectors);
|
||||||
} else {
|
break;
|
||||||
|
|
||||||
|
case QCOW2_CLUSTER_NORMAL:
|
||||||
if ((cluster_offset & 511) != 0) {
|
if ((cluster_offset & 511) != 0) {
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
goto fail;
|
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,
|
qemu_iovec_from_buffer(&hd_qiov, cluster_data,
|
||||||
512 * cur_nr_sectors);
|
512 * cur_nr_sectors);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
g_assert_not_reached();
|
||||||
|
ret = -EIO;
|
||||||
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
remaining_sectors -= cur_nr_sectors;
|
remaining_sectors -= cur_nr_sectors;
|
||||||
|
|
|
@ -165,6 +165,16 @@ typedef struct QCowL2Meta
|
||||||
QLIST_ENTRY(QCowL2Meta) next_in_flight;
|
QLIST_ENTRY(QCowL2Meta) next_in_flight;
|
||||||
} QCowL2Meta;
|
} 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)
|
static inline int size_to_clusters(BDRVQcowState *s, int64_t size)
|
||||||
{
|
{
|
||||||
return (size + (s->cluster_size - 1)) >> s->cluster_bits;
|
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;
|
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
|
// FIXME Need qcow2_ prefix to global functions
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue