On 17.03.20 19:16, Alberto Garcia wrote: > This patch adds QCow2SubclusterType, which is the subcluster-level > version of QCow2ClusterType. All QCOW2_SUBCLUSTER_* values have the > the same meaning as their QCOW2_CLUSTER_* equivalents (when they > exist). See below for details and caveats. > > In images without extended L2 entries clusters are treated as having > exactly one subcluster so it is possible to replace one data type with > the other while keeping the exact same semantics. > > With extended L2 entries there are new possible values, and every > subcluster in the same cluster can obviously have a different > QCow2SubclusterType so functions need to be adapted to work on the > subcluster level. > > There are several things that have to be taken into account: > > a) QCOW2_SUBCLUSTER_COMPRESSED means that the whole cluster is > compressed. We do not support compression at the subcluster > level. > > b) There are two different values for unallocated subclusters: > QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN which means that the whole > cluster is unallocated, and QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC > which means that the cluster is allocated but the subcluster is > not. The latter can only happen in images with extended L2 > entries. > > c) QCOW2_SUBCLUSTER_INVALID is used to detect the cases where an L2 > entry has a value that violates the specification. The caller is > responsible for handling these situations. > > To prevent compatibility problems with images that have invalid > values but are currently being read by QEMU without causing side > effects, QCOW2_SUBCLUSTER_INVALID is only returned for images > with extended L2 entries. > > qcow2_cluster_to_subcluster_type() is added as a separate function > from qcow2_get_subcluster_type(), but this is only temporary and both > will be merged in a subsequent patch. > > Signed-off-by: Alberto Garcia > --- > block/qcow2.h | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 120 insertions(+) > > diff --git a/block/qcow2.h b/block/qcow2.h > index 9611efbc52..52865787ee 100644 > --- a/block/qcow2.h > +++ b/block/qcow2.h [...] > @@ -447,6 +456,33 @@ typedef struct QCowL2Meta > QLIST_ENTRY(QCowL2Meta) next_in_flight; > } QCowL2Meta; > > +/* > + * In images with standard L2 entries all clusters are treated as if > + * they had one subcluster so QCow2ClusterType and QCow2SubclusterType > + * can be mapped to each other and have the exact same meaning > + * (QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC cannot happen in these images). > + * > + * In images with extended L2 entries QCow2ClusterType refers to the > + * complete cluster and QCow2SubclusterType to each of the individual > + * subclusters, so there are several possible combinations: > + * > + * |--------------+---------------------------| > + * | Cluster type | Possible subcluster types | > + * |--------------+---------------------------| > + * | UNALLOCATED | UNALLOCATED_PLAIN | > + * | | ZERO_PLAIN | > + * |--------------+---------------------------| > + * | NORMAL | UNALLOCATED_ALLOC | > + * | | ZERO_ALLOC | > + * | | NORMAL | > + * |--------------+---------------------------| > + * | COMPRESSED | COMPRESSED | > + * |--------------+---------------------------| > + * > + * QCOW2_SUBCLUSTER_INVALID means that the L2 entry is incorrect and > + * the image should be marked corrupt. > + */ > + Oh, a welcome addition! :) [...] > @@ -632,6 +678,80 @@ static inline QCow2ClusterType qcow2_get_cluster_type(BlockDriverState *bs, [...] > +/* > + * In an image without subsclusters @l2_bitmap is ignored and > + * @sc_index must be 0. > + */ > +static inline > +QCow2SubclusterType qcow2_get_subcluster_type(BlockDriverState *bs, > + uint64_t l2_entry, > + uint64_t l2_bitmap, > + unsigned sc_index) > +{ > + BDRVQcow2State *s = bs->opaque; > + QCow2ClusterType type = qcow2_get_cluster_type(bs, l2_entry); > + assert(sc_index < s->subclusters_per_cluster); > + > + if (has_subclusters(s)) { > + bool sc_zero = l2_bitmap & QCOW_OFLAG_SUB_ZERO(sc_index); > + bool sc_alloc = l2_bitmap & QCOW_OFLAG_SUB_ALLOC(sc_index); > + switch (type) { > + case QCOW2_CLUSTER_COMPRESSED: > + return QCOW2_SUBCLUSTER_COMPRESSED; Why did you drop the check that l2_bitmap == 0 here? Max > + case QCOW2_CLUSTER_ZERO_PLAIN: > + case QCOW2_CLUSTER_ZERO_ALLOC: > + return QCOW2_SUBCLUSTER_INVALID; > + case QCOW2_CLUSTER_NORMAL: > + if (!sc_zero && !sc_alloc) { > + return QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC; > + } else if (!sc_zero && sc_alloc) { > + return QCOW2_SUBCLUSTER_NORMAL; > + } else if (sc_zero && !sc_alloc) { > + return QCOW2_SUBCLUSTER_ZERO_ALLOC; > + } else { /* sc_zero && sc_alloc */ > + return QCOW2_SUBCLUSTER_INVALID; > + } > + case QCOW2_CLUSTER_UNALLOCATED: > + if (!sc_zero && !sc_alloc) { > + return QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN; > + } else if (!sc_zero && sc_alloc) { > + return QCOW2_SUBCLUSTER_INVALID; > + } else if (sc_zero && !sc_alloc) { > + return QCOW2_SUBCLUSTER_ZERO_PLAIN; > + } else { /* sc_zero && sc_alloc */ > + return QCOW2_SUBCLUSTER_INVALID; > + } > + default: > + g_assert_not_reached(); > + } > + } else { > + return qcow2_cluster_to_subcluster_type(type); > + } > +} > + > /* Check whether refcounts are eager or lazy */ > static inline bool qcow2_need_accurate_refcounts(BDRVQcow2State *s) > { >