From 6377af48b0445e7ee50db6e0bd73a3a70098f5f4 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 16 Mar 2012 15:02:38 +0100 Subject: [PATCH] qcow2: Support reading zero clusters This adds support for reading zero clusters in version 3 images. Signed-off-by: Kevin Wolf --- block/qcow2-cluster.c | 17 +++++++++++++---- block/qcow2-refcount.c | 7 +++++++ block/qcow2.c | 8 ++++++++ block/qcow2.h | 5 +++++ 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index b8836ba009..5e5f5cf9ad 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -453,6 +453,12 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, c = 1; *cluster_offset &= L2E_COMPRESSED_OFFSET_SIZE_MASK; 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: /* how many empty clusters ? */ 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: /* how many allocated clusters ? */ 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; break; } @@ -720,6 +727,7 @@ static int count_cow_clusters(BDRVQcowState *s, int nb_clusters, break; case QCOW2_CLUSTER_UNALLOCATED: case QCOW2_CLUSTER_COMPRESSED: + case QCOW2_CLUSTER_ZERO: break; default: abort(); @@ -868,9 +876,10 @@ again: && (cluster_offset & QCOW_OFLAG_COPIED)) { /* We keep all QCOW_OFLAG_COPIED clusters */ - keep_clusters = count_contiguous_clusters(nb_clusters, s->cluster_size, - &l2_table[l2_index], 0, - QCOW_OFLAG_COPIED); + keep_clusters = + count_contiguous_clusters(nb_clusters, s->cluster_size, + &l2_table[l2_index], 0, + QCOW_OFLAG_COPIED | QCOW_OFLAG_ZERO); assert(keep_clusters <= nb_clusters); nb_clusters -= keep_clusters; } else { diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 0112cc37b0..812c93c5c7 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -703,6 +703,7 @@ void qcow2_free_any_clusters(BlockDriverState *bs, nb_clusters << s->cluster_bits); break; case QCOW2_CLUSTER_UNALLOCATED: + case QCOW2_CLUSTER_ZERO: break; default: abort(); @@ -973,6 +974,12 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, l2_entry & ~511, nb_csectors * 512); break; + case QCOW2_CLUSTER_ZERO: + if ((l2_entry & L2E_OFFSET_MASK) == 0) { + break; + } + /* fall through */ + case QCOW2_CLUSTER_NORMAL: { /* QCOW_OFLAG_COPIED must be set iff refcount == 1 */ diff --git a/block/qcow2.c b/block/qcow2.c index 3e1482fa7e..9a8b354d0e 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -536,6 +536,14 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, } 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: /* add AIO support for compressed blocks ? */ ret = qcow2_decompress_cluster(bs, cluster_offset); diff --git a/block/qcow2.h b/block/qcow2.h index 2dc8ca26fb..df2bdfd0c6 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -43,6 +43,8 @@ #define QCOW_OFLAG_COPIED (1LL << 63) /* indicate that the cluster is compressed (they never have the copied flag) */ #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 */ @@ -184,6 +186,7 @@ enum { QCOW2_CLUSTER_UNALLOCATED, QCOW2_CLUSTER_NORMAL, QCOW2_CLUSTER_COMPRESSED, + QCOW2_CLUSTER_ZERO }; #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) { return QCOW2_CLUSTER_COMPRESSED; + } else if (l2_entry & QCOW_OFLAG_ZERO) { + return QCOW2_CLUSTER_ZERO; } else if (!(l2_entry & L2E_OFFSET_MASK)) { return QCOW2_CLUSTER_UNALLOCATED; } else {