qcow2: Add subcluster support to qcow2_get_host_offset()
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. qcow2_cluster_to_subcluster_type() is not necessary as a separate function anymore so it's inlined into its caller. Signed-off-by: Alberto Garcia <berto@igalia.com> Reviewed-by: Max Reitz <mreitz@redhat.com> Message-Id: <d2193fd48653a350d80f0eca1c67b1d9053fb2f3.1594396418.git.berto@igalia.com> [mreitz: Initialize expected_type to anything] Signed-off-by: Max Reitz <mreitz@redhat.com>
This commit is contained in:
parent
d53ec3d8d8
commit
3f9c6b3b1f
@ -426,66 +426,66 @@ static int qcow2_get_subcluster_range_type(BlockDriverState *bs,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Checks how many clusters in a given L2 slice are contiguous in the image
|
* Return the number of contiguous subclusters of the exact same type
|
||||||
* file. As soon as one of the flags in the bitmask stop_flags changes compared
|
* in a given L2 slice, starting from cluster @l2_index, subcluster
|
||||||
* to the first cluster, the search is stopped and the cluster is not counted
|
* @sc_index. Allocated subclusters are required to be contiguous in
|
||||||
* as contiguous. (This allows it, for example, to stop at the first compressed
|
* the image file.
|
||||||
* cluster which may require a different handling)
|
* 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.
|
||||||
|
* On failure return -errno and update @l2_index to point to the
|
||||||
|
* invalid entry.
|
||||||
*/
|
*/
|
||||||
static int count_contiguous_clusters(BlockDriverState *bs, int nb_clusters,
|
static int count_contiguous_subclusters(BlockDriverState *bs, int nb_clusters,
|
||||||
int cluster_size, uint64_t *l2_slice, int l2_index, uint64_t stop_flags)
|
unsigned sc_index, uint64_t *l2_slice,
|
||||||
|
unsigned *l2_index)
|
||||||
{
|
{
|
||||||
BDRVQcow2State *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int i;
|
int i, count = 0;
|
||||||
QCow2ClusterType first_cluster_type;
|
bool check_offset = false;
|
||||||
uint64_t mask = stop_flags | L2E_OFFSET_MASK | QCOW_OFLAG_COMPRESSED;
|
uint64_t expected_offset = 0;
|
||||||
uint64_t first_entry = get_l2_entry(s, l2_slice, l2_index);
|
QCow2SubclusterType expected_type = QCOW2_SUBCLUSTER_NORMAL, type;
|
||||||
uint64_t offset = first_entry & mask;
|
|
||||||
|
|
||||||
first_cluster_type = qcow2_get_cluster_type(bs, first_entry);
|
assert(*l2_index + nb_clusters <= s->l2_slice_size);
|
||||||
if (first_cluster_type == QCOW2_CLUSTER_UNALLOCATED) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* must be allocated */
|
|
||||||
assert(first_cluster_type == QCOW2_CLUSTER_NORMAL ||
|
|
||||||
first_cluster_type == QCOW2_CLUSTER_ZERO_ALLOC);
|
|
||||||
|
|
||||||
for (i = 0; i < nb_clusters; i++) {
|
for (i = 0; i < nb_clusters; i++) {
|
||||||
uint64_t l2_entry = get_l2_entry(s, l2_slice, l2_index + i) & mask;
|
unsigned first_sc = (i == 0) ? sc_index : 0;
|
||||||
if (offset + (uint64_t) i * cluster_size != l2_entry) {
|
uint64_t l2_entry = get_l2_entry(s, l2_slice, *l2_index + i);
|
||||||
|
uint64_t l2_bitmap = get_l2_bitmap(s, l2_slice, *l2_index + i);
|
||||||
|
int ret = qcow2_get_subcluster_range_type(bs, l2_entry, l2_bitmap,
|
||||||
|
first_sc, &type);
|
||||||
|
if (ret < 0) {
|
||||||
|
*l2_index += i; /* Point to the invalid entry */
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
if (i == 0) {
|
||||||
|
if (type == QCOW2_SUBCLUSTER_COMPRESSED) {
|
||||||
|
/* Compressed clusters are always processed one by one */
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
expected_type = type;
|
||||||
|
expected_offset = l2_entry & L2E_OFFSET_MASK;
|
||||||
|
check_offset = (type == QCOW2_SUBCLUSTER_NORMAL ||
|
||||||
|
type == QCOW2_SUBCLUSTER_ZERO_ALLOC ||
|
||||||
|
type == QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC);
|
||||||
|
} else if (type != expected_type) {
|
||||||
|
break;
|
||||||
|
} else if (check_offset) {
|
||||||
|
expected_offset += s->cluster_size;
|
||||||
|
if (expected_offset != (l2_entry & L2E_OFFSET_MASK)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
count += ret;
|
||||||
|
/* Stop if there are type changes before the end of the cluster */
|
||||||
|
if (first_sc + ret < s->subclusters_per_cluster) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return i;
|
return count;
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coroutine_fn do_perform_cow_read(BlockDriverState *bs,
|
static int coroutine_fn do_perform_cow_read(BlockDriverState *bs,
|
||||||
@ -574,12 +574,12 @@ int qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset,
|
|||||||
QCow2SubclusterType *subcluster_type)
|
QCow2SubclusterType *subcluster_type)
|
||||||
{
|
{
|
||||||
BDRVQcow2State *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
unsigned int l2_index;
|
unsigned int l2_index, sc_index;
|
||||||
uint64_t l1_index, l2_offset, *l2_slice, l2_entry;
|
uint64_t l1_index, l2_offset, *l2_slice, l2_entry, l2_bitmap;
|
||||||
int c;
|
int sc;
|
||||||
unsigned int offset_in_cluster;
|
unsigned int offset_in_cluster;
|
||||||
uint64_t bytes_available, bytes_needed, nb_clusters;
|
uint64_t bytes_available, bytes_needed, nb_clusters;
|
||||||
QCow2ClusterType type;
|
QCow2SubclusterType type;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
offset_in_cluster = offset_into_cluster(s, offset);
|
offset_in_cluster = offset_into_cluster(s, offset);
|
||||||
@ -602,13 +602,13 @@ int qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset,
|
|||||||
|
|
||||||
l1_index = offset_to_l1_index(s, offset);
|
l1_index = offset_to_l1_index(s, offset);
|
||||||
if (l1_index >= s->l1_size) {
|
if (l1_index >= s->l1_size) {
|
||||||
type = QCOW2_CLUSTER_UNALLOCATED;
|
type = QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK;
|
l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK;
|
||||||
if (!l2_offset) {
|
if (!l2_offset) {
|
||||||
type = QCOW2_CLUSTER_UNALLOCATED;
|
type = QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -629,7 +629,9 @@ int qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset,
|
|||||||
/* find the cluster offset for the given disk offset */
|
/* find the cluster offset for the given disk offset */
|
||||||
|
|
||||||
l2_index = offset_to_l2_slice_index(s, 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_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);
|
nb_clusters = size_to_clusters(s, bytes_needed);
|
||||||
/* bytes_needed <= *bytes + offset_in_cluster, both of which are unsigned
|
/* bytes_needed <= *bytes + offset_in_cluster, both of which are unsigned
|
||||||
@ -637,9 +639,9 @@ int qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset,
|
|||||||
* true */
|
* true */
|
||||||
assert(nb_clusters <= INT_MAX);
|
assert(nb_clusters <= INT_MAX);
|
||||||
|
|
||||||
type = qcow2_get_cluster_type(bs, l2_entry);
|
type = qcow2_get_subcluster_type(bs, l2_entry, l2_bitmap, sc_index);
|
||||||
if (s->qcow_version < 3 && (type == QCOW2_CLUSTER_ZERO_PLAIN ||
|
if (s->qcow_version < 3 && (type == QCOW2_SUBCLUSTER_ZERO_PLAIN ||
|
||||||
type == QCOW2_CLUSTER_ZERO_ALLOC)) {
|
type == QCOW2_SUBCLUSTER_ZERO_ALLOC)) {
|
||||||
qcow2_signal_corruption(bs, true, -1, -1, "Zero cluster entry found"
|
qcow2_signal_corruption(bs, true, -1, -1, "Zero cluster entry found"
|
||||||
" in pre-v3 image (L2 offset: %#" PRIx64
|
" in pre-v3 image (L2 offset: %#" PRIx64
|
||||||
", L2 index: %#x)", l2_offset, l2_index);
|
", L2 index: %#x)", l2_offset, l2_index);
|
||||||
@ -647,7 +649,9 @@ int qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset,
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case QCOW2_CLUSTER_COMPRESSED:
|
case QCOW2_SUBCLUSTER_INVALID:
|
||||||
|
break; /* This is handled by count_contiguous_subclusters() below */
|
||||||
|
case QCOW2_SUBCLUSTER_COMPRESSED:
|
||||||
if (has_data_file(bs)) {
|
if (has_data_file(bs)) {
|
||||||
qcow2_signal_corruption(bs, true, -1, -1, "Compressed cluster "
|
qcow2_signal_corruption(bs, true, -1, -1, "Compressed cluster "
|
||||||
"entry found in image with external data "
|
"entry found in image with external data "
|
||||||
@ -656,23 +660,16 @@ int qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset,
|
|||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
/* Compressed clusters can only be processed one by one */
|
|
||||||
c = 1;
|
|
||||||
*host_offset = l2_entry & L2E_COMPRESSED_OFFSET_SIZE_MASK;
|
*host_offset = l2_entry & L2E_COMPRESSED_OFFSET_SIZE_MASK;
|
||||||
break;
|
break;
|
||||||
case QCOW2_CLUSTER_ZERO_PLAIN:
|
case QCOW2_SUBCLUSTER_ZERO_PLAIN:
|
||||||
case QCOW2_CLUSTER_UNALLOCATED:
|
case QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN:
|
||||||
/* how many empty clusters ? */
|
|
||||||
c = count_contiguous_clusters_unallocated(bs, nb_clusters,
|
|
||||||
l2_slice, l2_index, type);
|
|
||||||
break;
|
break;
|
||||||
case QCOW2_CLUSTER_ZERO_ALLOC:
|
case QCOW2_SUBCLUSTER_ZERO_ALLOC:
|
||||||
case QCOW2_CLUSTER_NORMAL: {
|
case QCOW2_SUBCLUSTER_NORMAL:
|
||||||
|
case QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC: {
|
||||||
uint64_t host_cluster_offset = l2_entry & L2E_OFFSET_MASK;
|
uint64_t host_cluster_offset = l2_entry & L2E_OFFSET_MASK;
|
||||||
*host_offset = host_cluster_offset + offset_in_cluster;
|
*host_offset = host_cluster_offset + offset_in_cluster;
|
||||||
/* how many allocated clusters ? */
|
|
||||||
c = count_contiguous_clusters(bs, nb_clusters, s->cluster_size,
|
|
||||||
l2_slice, l2_index, QCOW_OFLAG_ZERO);
|
|
||||||
if (offset_into_cluster(s, host_cluster_offset)) {
|
if (offset_into_cluster(s, host_cluster_offset)) {
|
||||||
qcow2_signal_corruption(bs, true, -1, -1,
|
qcow2_signal_corruption(bs, true, -1, -1,
|
||||||
"Cluster allocation offset %#"
|
"Cluster allocation offset %#"
|
||||||
@ -698,9 +695,18 @@ int qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset,
|
|||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sc = count_contiguous_subclusters(bs, nb_clusters, sc_index,
|
||||||
|
l2_slice, &l2_index);
|
||||||
|
if (sc < 0) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice);
|
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:
|
out:
|
||||||
if (bytes_available > bytes_needed) {
|
if (bytes_available > bytes_needed) {
|
||||||
@ -713,7 +719,7 @@ out:
|
|||||||
assert(bytes_available - offset_in_cluster <= UINT_MAX);
|
assert(bytes_available - offset_in_cluster <= UINT_MAX);
|
||||||
*bytes = bytes_available - offset_in_cluster;
|
*bytes = bytes_available - offset_in_cluster;
|
||||||
|
|
||||||
*subcluster_type = qcow2_cluster_to_subcluster_type(type);
|
*subcluster_type = type;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -710,29 +710,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
|
* In an image without subsclusters @l2_bitmap is ignored and
|
||||||
* @sc_index must be 0.
|
* @sc_index must be 0.
|
||||||
@ -776,7 +753,20 @@ QCow2SubclusterType qcow2_get_subcluster_type(BlockDriverState *bs,
|
|||||||
g_assert_not_reached();
|
g_assert_not_reached();
|
||||||
}
|
}
|
||||||
} else {
|
} 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user