qcow2: Support reading zero clusters
This adds support for reading zero clusters in version 3 images. Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
parent
6744cbab8c
commit
6377af48b0
@ -453,6 +453,12 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
|||||||
c = 1;
|
c = 1;
|
||||||
*cluster_offset &= L2E_COMPRESSED_OFFSET_SIZE_MASK;
|
*cluster_offset &= L2E_COMPRESSED_OFFSET_SIZE_MASK;
|
||||||
break;
|
break;
|
||||||
|
case QCOW2_CLUSTER_ZERO:
|
||||||
|
c = count_contiguous_clusters(nb_clusters, s->cluster_size,
|
||||||
|
&l2_table[l2_index], 0,
|
||||||
|
QCOW_OFLAG_COMPRESSED | QCOW_OFLAG_ZERO);
|
||||||
|
*cluster_offset = 0;
|
||||||
|
break;
|
||||||
case QCOW2_CLUSTER_UNALLOCATED:
|
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]);
|
||||||
@ -461,7 +467,8 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
|||||||
case QCOW2_CLUSTER_NORMAL:
|
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_COMPRESSED);
|
&l2_table[l2_index], 0,
|
||||||
|
QCOW_OFLAG_COMPRESSED | QCOW_OFLAG_ZERO);
|
||||||
*cluster_offset &= L2E_OFFSET_MASK;
|
*cluster_offset &= L2E_OFFSET_MASK;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -720,6 +727,7 @@ static int count_cow_clusters(BDRVQcowState *s, int nb_clusters,
|
|||||||
break;
|
break;
|
||||||
case QCOW2_CLUSTER_UNALLOCATED:
|
case QCOW2_CLUSTER_UNALLOCATED:
|
||||||
case QCOW2_CLUSTER_COMPRESSED:
|
case QCOW2_CLUSTER_COMPRESSED:
|
||||||
|
case QCOW2_CLUSTER_ZERO:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
abort();
|
abort();
|
||||||
@ -868,9 +876,10 @@ again:
|
|||||||
&& (cluster_offset & QCOW_OFLAG_COPIED))
|
&& (cluster_offset & QCOW_OFLAG_COPIED))
|
||||||
{
|
{
|
||||||
/* We keep all QCOW_OFLAG_COPIED clusters */
|
/* We keep all QCOW_OFLAG_COPIED clusters */
|
||||||
keep_clusters = count_contiguous_clusters(nb_clusters, s->cluster_size,
|
keep_clusters =
|
||||||
&l2_table[l2_index], 0,
|
count_contiguous_clusters(nb_clusters, s->cluster_size,
|
||||||
QCOW_OFLAG_COPIED);
|
&l2_table[l2_index], 0,
|
||||||
|
QCOW_OFLAG_COPIED | QCOW_OFLAG_ZERO);
|
||||||
assert(keep_clusters <= nb_clusters);
|
assert(keep_clusters <= nb_clusters);
|
||||||
nb_clusters -= keep_clusters;
|
nb_clusters -= keep_clusters;
|
||||||
} else {
|
} else {
|
||||||
|
@ -703,6 +703,7 @@ void qcow2_free_any_clusters(BlockDriverState *bs,
|
|||||||
nb_clusters << s->cluster_bits);
|
nb_clusters << s->cluster_bits);
|
||||||
break;
|
break;
|
||||||
case QCOW2_CLUSTER_UNALLOCATED:
|
case QCOW2_CLUSTER_UNALLOCATED:
|
||||||
|
case QCOW2_CLUSTER_ZERO:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
abort();
|
abort();
|
||||||
@ -973,6 +974,12 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
|
|||||||
l2_entry & ~511, nb_csectors * 512);
|
l2_entry & ~511, nb_csectors * 512);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case QCOW2_CLUSTER_ZERO:
|
||||||
|
if ((l2_entry & L2E_OFFSET_MASK) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* fall through */
|
||||||
|
|
||||||
case QCOW2_CLUSTER_NORMAL:
|
case QCOW2_CLUSTER_NORMAL:
|
||||||
{
|
{
|
||||||
/* QCOW_OFLAG_COPIED must be set iff refcount == 1 */
|
/* QCOW_OFLAG_COPIED must be set iff refcount == 1 */
|
||||||
|
@ -536,6 +536,14 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case QCOW2_CLUSTER_ZERO:
|
||||||
|
if (s->qcow_version < 3) {
|
||||||
|
ret = -EIO;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
qemu_iovec_memset(&hd_qiov, 0, 512 * cur_nr_sectors);
|
||||||
|
break;
|
||||||
|
|
||||||
case QCOW2_CLUSTER_COMPRESSED:
|
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);
|
||||||
|
@ -43,6 +43,8 @@
|
|||||||
#define QCOW_OFLAG_COPIED (1LL << 63)
|
#define QCOW_OFLAG_COPIED (1LL << 63)
|
||||||
/* indicate that the cluster is compressed (they never have the copied flag) */
|
/* indicate that the cluster is compressed (they never have the copied flag) */
|
||||||
#define QCOW_OFLAG_COMPRESSED (1LL << 62)
|
#define QCOW_OFLAG_COMPRESSED (1LL << 62)
|
||||||
|
/* The cluster reads as all zeros */
|
||||||
|
#define QCOW_OFLAG_ZERO (1LL << 0)
|
||||||
|
|
||||||
#define REFCOUNT_SHIFT 1 /* refcount size is 2 bytes */
|
#define REFCOUNT_SHIFT 1 /* refcount size is 2 bytes */
|
||||||
|
|
||||||
@ -184,6 +186,7 @@ enum {
|
|||||||
QCOW2_CLUSTER_UNALLOCATED,
|
QCOW2_CLUSTER_UNALLOCATED,
|
||||||
QCOW2_CLUSTER_NORMAL,
|
QCOW2_CLUSTER_NORMAL,
|
||||||
QCOW2_CLUSTER_COMPRESSED,
|
QCOW2_CLUSTER_COMPRESSED,
|
||||||
|
QCOW2_CLUSTER_ZERO
|
||||||
};
|
};
|
||||||
|
|
||||||
#define L1E_OFFSET_MASK 0x00ffffffffffff00ULL
|
#define L1E_OFFSET_MASK 0x00ffffffffffff00ULL
|
||||||
@ -213,6 +216,8 @@ static inline int qcow2_get_cluster_type(uint64_t l2_entry)
|
|||||||
{
|
{
|
||||||
if (l2_entry & QCOW_OFLAG_COMPRESSED) {
|
if (l2_entry & QCOW_OFLAG_COMPRESSED) {
|
||||||
return QCOW2_CLUSTER_COMPRESSED;
|
return QCOW2_CLUSTER_COMPRESSED;
|
||||||
|
} else if (l2_entry & QCOW_OFLAG_ZERO) {
|
||||||
|
return QCOW2_CLUSTER_ZERO;
|
||||||
} else if (!(l2_entry & L2E_OFFSET_MASK)) {
|
} else if (!(l2_entry & L2E_OFFSET_MASK)) {
|
||||||
return QCOW2_CLUSTER_UNALLOCATED;
|
return QCOW2_CLUSTER_UNALLOCATED;
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user