diff mbox series

[v4,18/30] qcow2: Add subcluster support to qcow2_get_host_offset()

Message ID 1cc780f735044ac9138808234589d2c278c9cfbf.1584468723.git.berto@igalia.com (mailing list archive)
State New, archived
Headers show
Series Add subcluster allocation to qcow2 | expand

Commit Message

Alberto Garcia March 17, 2020, 6:16 p.m. UTC
The logic of this function remains pretty much the same, except that
it uses count_contiguous_subclusters(), which combines the logic of
count_contiguous_clusters() / count_contiguous_clusters_unallocated()
and checks individual subclusters.

Signed-off-by: Alberto Garcia <berto@igalia.com>
---
 block/qcow2.h         |  38 +++++------
 block/qcow2-cluster.c | 143 +++++++++++++++++++++---------------------
 2 files changed, 85 insertions(+), 96 deletions(-)

Comments

Max Reitz April 8, 2020, 12:49 p.m. UTC | #1
On 17.03.20 19:16, Alberto Garcia wrote:
> The logic of this function remains pretty much the same, except that
> it uses count_contiguous_subclusters(), which combines the logic of
> count_contiguous_clusters() / count_contiguous_clusters_unallocated()
> and checks individual subclusters.
> 
> Signed-off-by: Alberto Garcia <berto@igalia.com>
> ---
>  block/qcow2.h         |  38 +++++------
>  block/qcow2-cluster.c | 143 +++++++++++++++++++++---------------------
>  2 files changed, 85 insertions(+), 96 deletions(-)

Reviewed-by: Max Reitz <mreitz@redhat.com>

(Oops, totally missed the L1 entry out of bounds / L1 entry empty part
in v3.)
Alberto Garcia April 8, 2020, 5:35 p.m. UTC | #2
On Wed 08 Apr 2020 02:49:14 PM CEST, Max Reitz wrote:
> (Oops, totally missed the L1 entry out of bounds / L1 entry empty part
> in v3.)

Yeah, and you can mix values between different enum types in C quite
easily without the compiler producing a warning.

Berto
Vladimir Sementsov-Ogievskiy April 22, 2020, 8:07 a.m. UTC | #3
17.03.2020 21:16, Alberto Garcia wrote:
> The logic of this function remains pretty much the same, except that
> it uses count_contiguous_subclusters(), which combines the logic of
> count_contiguous_clusters() / count_contiguous_clusters_unallocated()
> and checks individual subclusters.
> 
> Signed-off-by: Alberto Garcia <berto@igalia.com>

Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>

> ---

[..]

> +static int count_contiguous_subclusters(BlockDriverState *bs, int nb_clusters,
> +                                        unsigned sc_index, uint64_t *l2_slice,
> +                                        int l2_index)
>   {
>       BDRVQcow2State *s = bs->opaque;

preexist, but, worth asserting that nb_clusters are all in this l2_slice?

[..]

> +        for (j = (i == 0) ? sc_index : 0; j < s->subclusters_per_cluster; j++) {
> +            if (qcow2_get_subcluster_type(bs, l2_entry, l2_bitmap, j) != type) {
> +                goto out;

why not just return count from here? And then you don't need goto at all. Hmm, may be out: code will be extended in further patches..

> +            }
> +            count++;
>           }
> +        expected_offset += s->cluster_size;
>       }
>   
> -    return i;
> +out:
> +    return count;
>   }
>   

[..]

> @@ -607,21 +607,20 @@ int qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset,
>               goto fail;
>           }
>           /* Compressed clusters can only be processed one by one */
> -        c = 1;
> +        sc = s->subclusters_per_cluster - sc_index;

should not we assert here that sc_index == 0? Otherwise the caller definitely doing something wrong.

>           *host_offset = l2_entry & L2E_COMPRESSED_OFFSET_SIZE_MASK;
>           break;
> -    case QCOW2_CLUSTER_ZERO_PLAIN:
> -    case QCOW2_CLUSTER_UNALLOCATED:
> -        /* how many empty clusters ? */
> -        c = count_contiguous_clusters_unallocated(bs, nb_clusters,
> -                                                  l2_slice, l2_index, type);
> +    case QCOW2_SUBCLUSTER_ZERO_PLAIN:
> +    case QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN:
> +        sc = count_contiguous_subclusters(bs, nb_clusters, sc_index,
> +                                          l2_slice, l2_index);
>           *host_offset = 0;
>           break;
> -    case QCOW2_CLUSTER_ZERO_ALLOC:
> -    case QCOW2_CLUSTER_NORMAL:
> -        /* how many allocated clusters ? */
> -        c = count_contiguous_clusters(bs, nb_clusters, s->cluster_size,
> -                                      l2_slice, l2_index, QCOW_OFLAG_ZERO);
> +    case QCOW2_SUBCLUSTER_ZERO_ALLOC:
> +    case QCOW2_SUBCLUSTER_NORMAL:
> +    case QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC:
> +        sc = count_contiguous_subclusters(bs, nb_clusters, sc_index,
> +                                          l2_slice, l2_index);
>           *host_offset = l2_entry & L2E_OFFSET_MASK;
>           if (offset_into_cluster(s, *host_offset)) {

Hmm, you may move "sc = count_contiguous_subclusters" to be after the switch-block, as it is universal now. And keep only offset calculation and error checking in the switch-block.

>               qcow2_signal_corruption(bs, true, -1, -1,
> @@ -651,7 +650,7 @@ int qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset,
>   
>       qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice);
>   
> -    bytes_available = (int64_t)c * s->cluster_size;
> +    bytes_available = ((int64_t)sc + sc_index) << s->subcluster_bits;
>   
>   out:
>       if (bytes_available > bytes_needed) {
> @@ -664,7 +663,7 @@ out:
>       assert(bytes_available - offset_in_cluster <= UINT_MAX);
>       *bytes = bytes_available - offset_in_cluster;
>   
> -    *subcluster_type = qcow2_cluster_to_subcluster_type(type);
> +    *subcluster_type = type;
>   
>       return 0;
>   
>
Alberto Garcia April 22, 2020, 11:54 a.m. UTC | #4
On Wed 22 Apr 2020 10:07:30 AM CEST, Vladimir Sementsov-Ogievskiy wrote:
>> +static int count_contiguous_subclusters(BlockDriverState *bs, int nb_clusters,
>> +                                        unsigned sc_index, uint64_t *l2_slice,
>> +                                        int l2_index)
>>   {
>>       BDRVQcow2State *s = bs->opaque;
>
> preexist, but, worth asserting that nb_clusters are all in this
> l2_slice?

Ok.

>> +        for (j = (i == 0) ? sc_index : 0; j < s->subclusters_per_cluster; j++) {
>> +            if (qcow2_get_subcluster_type(bs, l2_entry, l2_bitmap, j) != type) {
>> +                goto out;
>
> why not just return count from here? And then you don't need goto at
> all. Hmm, may be out: code will be extended in further patches..

It's not extended in further patches. I generally prefer having a single
exit point but you're right that it probably doesn't make sense here.

>>           /* Compressed clusters can only be processed one by one */
>> -        c = 1;
>> +        sc = s->subclusters_per_cluster - sc_index;
>
> should not we assert here that sc_index == 0? Otherwise the caller
> definitely doing something wrong.

No, no, the guest offset doesn't need to be cluster aligned so sc_index
can perfectly be != 0.

>> +    case QCOW2_SUBCLUSTER_ZERO_ALLOC:
>> +    case QCOW2_SUBCLUSTER_NORMAL:
>> +    case QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC:
>> +        sc = count_contiguous_subclusters(bs, nb_clusters, sc_index,
>> +                                          l2_slice, l2_index);
>>           *host_offset = l2_entry & L2E_OFFSET_MASK;
>>           if (offset_into_cluster(s, *host_offset)) {
>
> Hmm, you may move "sc = count_contiguous_subclusters" to be after the
> switch-block, as it is universal now. And keep only offset calculation
> and error checking in the switch-block.

That's actually a good idea, thanks !! (plus we actually get to use the
QCOW2_SUBCLUSTER_COMPRESSED check in count_contiguous_subclusters(),
which is currently dead code).

Berto
Vladimir Sementsov-Ogievskiy April 22, 2020, 12:18 p.m. UTC | #5
22.04.2020 14:54, Alberto Garcia wrote:
> On Wed 22 Apr 2020 10:07:30 AM CEST, Vladimir Sementsov-Ogievskiy wrote:
>>> +static int count_contiguous_subclusters(BlockDriverState *bs, int nb_clusters,
>>> +                                        unsigned sc_index, uint64_t *l2_slice,
>>> +                                        int l2_index)
>>>    {
>>>        BDRVQcow2State *s = bs->opaque;
>>
>> preexist, but, worth asserting that nb_clusters are all in this
>> l2_slice?
> 
> Ok.
> 
>>> +        for (j = (i == 0) ? sc_index : 0; j < s->subclusters_per_cluster; j++) {
>>> +            if (qcow2_get_subcluster_type(bs, l2_entry, l2_bitmap, j) != type) {
>>> +                goto out;
>>
>> why not just return count from here? And then you don't need goto at
>> all. Hmm, may be out: code will be extended in further patches..
> 
> It's not extended in further patches. I generally prefer having a single
> exit point but you're right that it probably doesn't make sense here.
> 
>>>            /* Compressed clusters can only be processed one by one */
>>> -        c = 1;
>>> +        sc = s->subclusters_per_cluster - sc_index;
>>
>> should not we assert here that sc_index == 0? Otherwise the caller
>> definitely doing something wrong.
> 
> No, no, the guest offset doesn't need to be cluster aligned so sc_index
> can perfectly be != 0.

Hmm. yes. The only caller actually doesn't call count_contiguous_subclusters for compressed cluster case, but it may be refactored to do so, and then it does

   bytes_available = ((int64_t)sc + sc_index) << s->subcluster_bits;

so, even if intermediate sc is not very meaningful for compressed clusters (as we can't access sub-chunk of compressed cluster in any way), the resulting bytes_available is meaningful and it rely on sc being exactly what it is..

Ok

> 
>>> +    case QCOW2_SUBCLUSTER_ZERO_ALLOC:
>>> +    case QCOW2_SUBCLUSTER_NORMAL:
>>> +    case QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC:
>>> +        sc = count_contiguous_subclusters(bs, nb_clusters, sc_index,
>>> +                                          l2_slice, l2_index);
>>>            *host_offset = l2_entry & L2E_OFFSET_MASK;
>>>            if (offset_into_cluster(s, *host_offset)) {
>>
>> Hmm, you may move "sc = count_contiguous_subclusters" to be after the
>> switch-block, as it is universal now. And keep only offset calculation
>> and error checking in the switch-block.
> 
> That's actually a good idea, thanks !! (plus we actually get to use the
> QCOW2_SUBCLUSTER_COMPRESSED check in count_contiguous_subclusters(),
> which is currently dead code).
> 
> Berto
>
diff mbox series

Patch

diff --git a/block/qcow2.h b/block/qcow2.h
index e6fbb7d987..031ce823b3 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -678,29 +678,6 @@  static inline QCow2ClusterType qcow2_get_cluster_type(BlockDriverState *bs,
     }
 }
 
-/*
- * For an image without extended L2 entries, return the
- * QCow2SubclusterType equivalent of a given QCow2ClusterType.
- */
-static inline
-QCow2SubclusterType qcow2_cluster_to_subcluster_type(QCow2ClusterType type)
-{
-    switch (type) {
-    case QCOW2_CLUSTER_COMPRESSED:
-        return QCOW2_SUBCLUSTER_COMPRESSED;
-    case QCOW2_CLUSTER_ZERO_PLAIN:
-        return QCOW2_SUBCLUSTER_ZERO_PLAIN;
-    case QCOW2_CLUSTER_ZERO_ALLOC:
-        return QCOW2_SUBCLUSTER_ZERO_ALLOC;
-    case QCOW2_CLUSTER_NORMAL:
-        return QCOW2_SUBCLUSTER_NORMAL;
-    case QCOW2_CLUSTER_UNALLOCATED:
-        return QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN;
-    default:
-        g_assert_not_reached();
-    }
-}
-
 /*
  * In an image without subsclusters @l2_bitmap is ignored and
  * @sc_index must be 0.
@@ -748,7 +725,20 @@  QCow2SubclusterType qcow2_get_subcluster_type(BlockDriverState *bs,
             g_assert_not_reached();
         }
     } else {
-        return qcow2_cluster_to_subcluster_type(type);
+        switch (type) {
+        case QCOW2_CLUSTER_COMPRESSED:
+            return QCOW2_SUBCLUSTER_COMPRESSED;
+        case QCOW2_CLUSTER_ZERO_PLAIN:
+            return QCOW2_SUBCLUSTER_ZERO_PLAIN;
+        case QCOW2_CLUSTER_ZERO_ALLOC:
+            return QCOW2_SUBCLUSTER_ZERO_ALLOC;
+        case QCOW2_CLUSTER_NORMAL:
+            return QCOW2_SUBCLUSTER_NORMAL;
+        case QCOW2_CLUSTER_UNALLOCATED:
+            return QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN;
+        default:
+            g_assert_not_reached();
+        }
     }
 }
 
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index c6f3cc9237..6f2643ba53 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -376,66 +376,58 @@  fail:
 }
 
 /*
- * Checks how many clusters in a given L2 slice are contiguous in the image
- * file. As soon as one of the flags in the bitmask stop_flags changes compared
- * to the first cluster, the search is stopped and the cluster is not counted
- * as contiguous. (This allows it, for example, to stop at the first compressed
- * cluster which may require a different handling)
+ * Return the number of contiguous subclusters of the exact same type
+ * in a given L2 slice, starting from cluster @l2_index, subcluster
+ * @sc_index. Allocated subclusters are required to be contiguous in
+ * the image file.
+ * At most @nb_clusters are checked (note that this means clusters,
+ * not subclusters).
+ * Compressed clusters are always processed one by one but for the
+ * purpose of this count they are treated as if they were divided into
+ * subclusters of size s->subcluster_size.
  */
-static int count_contiguous_clusters(BlockDriverState *bs, int nb_clusters,
-        int cluster_size, uint64_t *l2_slice, int l2_index, uint64_t stop_flags)
+static int count_contiguous_subclusters(BlockDriverState *bs, int nb_clusters,
+                                        unsigned sc_index, uint64_t *l2_slice,
+                                        int l2_index)
 {
     BDRVQcow2State *s = bs->opaque;
-    int i;
-    QCow2ClusterType first_cluster_type;
-    uint64_t mask = stop_flags | L2E_OFFSET_MASK | QCOW_OFLAG_COMPRESSED;
-    uint64_t first_entry = get_l2_entry(s, l2_slice, l2_index);
-    uint64_t offset = first_entry & mask;
-
-    first_cluster_type = qcow2_get_cluster_type(bs, first_entry);
-    if (first_cluster_type == QCOW2_CLUSTER_UNALLOCATED) {
-        return 0;
+    int i, j, count = 0;
+    uint64_t l2_entry = get_l2_entry(s, l2_slice, l2_index);
+    uint64_t l2_bitmap = get_l2_bitmap(s, l2_slice, l2_index);
+    uint64_t expected_offset = l2_entry & L2E_OFFSET_MASK;
+    bool check_offset = true;
+    QCow2SubclusterType type =
+        qcow2_get_subcluster_type(bs, l2_entry, l2_bitmap, sc_index);
+
+    assert(type != QCOW2_SUBCLUSTER_INVALID); /* The caller should check this */
+
+    if (type == QCOW2_SUBCLUSTER_COMPRESSED) {
+        /* Compressed clusters are always processed one by one */
+        return s->subclusters_per_cluster - sc_index;
     }
 
-    /* must be allocated */
-    assert(first_cluster_type == QCOW2_CLUSTER_NORMAL ||
-           first_cluster_type == QCOW2_CLUSTER_ZERO_ALLOC);
-
-    for (i = 0; i < nb_clusters; i++) {
-        uint64_t l2_entry = get_l2_entry(s, l2_slice, l2_index + i) & mask;
-        if (offset + (uint64_t) i * cluster_size != l2_entry) {
-            break;
-        }
+    if (type == QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN ||
+        type == QCOW2_SUBCLUSTER_ZERO_PLAIN) {
+        check_offset = false;
     }
 
-        return i;
-}
-
-/*
- * Checks how many consecutive unallocated clusters in a given L2
- * slice have the same cluster type.
- */
-static int count_contiguous_clusters_unallocated(BlockDriverState *bs,
-                                                 int nb_clusters,
-                                                 uint64_t *l2_slice,
-                                                 int l2_index,
-                                                 QCow2ClusterType wanted_type)
-{
-    BDRVQcow2State *s = bs->opaque;
-    int i;
-
-    assert(wanted_type == QCOW2_CLUSTER_ZERO_PLAIN ||
-           wanted_type == QCOW2_CLUSTER_UNALLOCATED);
     for (i = 0; i < nb_clusters; i++) {
-        uint64_t entry = get_l2_entry(s, l2_slice, l2_index + i);
-        QCow2ClusterType type = qcow2_get_cluster_type(bs, entry);
-
-        if (type != wanted_type) {
-            break;
+        l2_entry = get_l2_entry(s, l2_slice, l2_index + i);
+        l2_bitmap = get_l2_bitmap(s, l2_slice, l2_index + i);
+        if (check_offset && expected_offset != (l2_entry & L2E_OFFSET_MASK)) {
+            goto out;
+        }
+        for (j = (i == 0) ? sc_index : 0; j < s->subclusters_per_cluster; j++) {
+            if (qcow2_get_subcluster_type(bs, l2_entry, l2_bitmap, j) != type) {
+                goto out;
+            }
+            count++;
         }
+        expected_offset += s->cluster_size;
     }
 
-    return i;
+out:
+    return count;
 }
 
 static int coroutine_fn do_perform_cow_read(BlockDriverState *bs,
@@ -524,12 +516,12 @@  int qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset,
                           QCow2SubclusterType *subcluster_type)
 {
     BDRVQcow2State *s = bs->opaque;
-    unsigned int l2_index;
-    uint64_t l1_index, l2_offset, *l2_slice, l2_entry;
-    int c;
+    unsigned int l2_index, sc_index;
+    uint64_t l1_index, l2_offset, *l2_slice, l2_entry, l2_bitmap;
+    int sc;
     unsigned int offset_in_cluster;
     uint64_t bytes_available, bytes_needed, nb_clusters;
-    QCow2ClusterType type;
+    QCow2SubclusterType type;
     int ret;
 
     offset_in_cluster = offset_into_cluster(s, offset);
@@ -552,13 +544,13 @@  int qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset,
 
     l1_index = offset_to_l1_index(s, offset);
     if (l1_index >= s->l1_size) {
-        type = QCOW2_CLUSTER_UNALLOCATED;
+        type = QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN;
         goto out;
     }
 
     l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK;
     if (!l2_offset) {
-        type = QCOW2_CLUSTER_UNALLOCATED;
+        type = QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN;
         goto out;
     }
 
@@ -579,7 +571,9 @@  int qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset,
     /* find the cluster offset for the given disk offset */
 
     l2_index = offset_to_l2_slice_index(s, offset);
+    sc_index = offset_to_sc_index(s, offset);
     l2_entry = get_l2_entry(s, l2_slice, l2_index);
+    l2_bitmap = get_l2_bitmap(s, l2_slice, l2_index);
 
     nb_clusters = size_to_clusters(s, bytes_needed);
     /* bytes_needed <= *bytes + offset_in_cluster, both of which are unsigned
@@ -587,9 +581,9 @@  int qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset,
      * true */
     assert(nb_clusters <= INT_MAX);
 
-    type = qcow2_get_cluster_type(bs, l2_entry);
-    if (s->qcow_version < 3 && (type == QCOW2_CLUSTER_ZERO_PLAIN ||
-                                type == QCOW2_CLUSTER_ZERO_ALLOC)) {
+    type = qcow2_get_subcluster_type(bs, l2_entry, l2_bitmap, sc_index);
+    if (s->qcow_version < 3 && (type == QCOW2_SUBCLUSTER_ZERO_PLAIN ||
+                                type == QCOW2_SUBCLUSTER_ZERO_ALLOC)) {
         qcow2_signal_corruption(bs, true, -1, -1, "Zero cluster entry found"
                                 " in pre-v3 image (L2 offset: %#" PRIx64
                                 ", L2 index: %#x)", l2_offset, l2_index);
@@ -597,7 +591,13 @@  int qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset,
         goto fail;
     }
     switch (type) {
-    case QCOW2_CLUSTER_COMPRESSED:
+    case QCOW2_SUBCLUSTER_INVALID:
+        qcow2_signal_corruption(bs, true, -1, -1, "Invalid cluster entry found "
+                                " (L2 offset: %#" PRIx64 ", L2 index: %#x)",
+                                l2_offset, l2_index);
+        ret = -EIO;
+        goto fail;
+    case QCOW2_SUBCLUSTER_COMPRESSED:
         if (has_data_file(bs)) {
             qcow2_signal_corruption(bs, true, -1, -1, "Compressed cluster "
                                     "entry found in image with external data "
@@ -607,21 +607,20 @@  int qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset,
             goto fail;
         }
         /* Compressed clusters can only be processed one by one */
-        c = 1;
+        sc = s->subclusters_per_cluster - sc_index;
         *host_offset = l2_entry & L2E_COMPRESSED_OFFSET_SIZE_MASK;
         break;
-    case QCOW2_CLUSTER_ZERO_PLAIN:
-    case QCOW2_CLUSTER_UNALLOCATED:
-        /* how many empty clusters ? */
-        c = count_contiguous_clusters_unallocated(bs, nb_clusters,
-                                                  l2_slice, l2_index, type);
+    case QCOW2_SUBCLUSTER_ZERO_PLAIN:
+    case QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN:
+        sc = count_contiguous_subclusters(bs, nb_clusters, sc_index,
+                                          l2_slice, l2_index);
         *host_offset = 0;
         break;
-    case QCOW2_CLUSTER_ZERO_ALLOC:
-    case QCOW2_CLUSTER_NORMAL:
-        /* how many allocated clusters ? */
-        c = count_contiguous_clusters(bs, nb_clusters, s->cluster_size,
-                                      l2_slice, l2_index, QCOW_OFLAG_ZERO);
+    case QCOW2_SUBCLUSTER_ZERO_ALLOC:
+    case QCOW2_SUBCLUSTER_NORMAL:
+    case QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC:
+        sc = count_contiguous_subclusters(bs, nb_clusters, sc_index,
+                                          l2_slice, l2_index);
         *host_offset = l2_entry & L2E_OFFSET_MASK;
         if (offset_into_cluster(s, *host_offset)) {
             qcow2_signal_corruption(bs, true, -1, -1,
@@ -651,7 +650,7 @@  int qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset,
 
     qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice);
 
-    bytes_available = (int64_t)c * s->cluster_size;
+    bytes_available = ((int64_t)sc + sc_index) << s->subcluster_bits;
 
 out:
     if (bytes_available > bytes_needed) {
@@ -664,7 +663,7 @@  out:
     assert(bytes_available - offset_in_cluster <= UINT_MAX);
     *bytes = bytes_available - offset_in_cluster;
 
-    *subcluster_type = qcow2_cluster_to_subcluster_type(type);
+    *subcluster_type = type;
 
     return 0;