summaryrefslogtreecommitdiff
path: root/block/qcow2-refcount.c
diff options
context:
space:
mode:
Diffstat (limited to 'block/qcow2-refcount.c')
-rw-r--r--block/qcow2-refcount.c1012
1 files changed, 714 insertions, 298 deletions
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index cc6cf743d..9afdb40b4 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -26,8 +26,6 @@
#include "block/block_int.h"
#include "block/qcow2.h"
#include "qemu/range.h"
-#include "qapi/qmp/types.h"
-#include "qapi-event.h"
static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size);
static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
@@ -46,19 +44,25 @@ int qcow2_refcount_init(BlockDriverState *bs)
assert(s->refcount_table_size <= INT_MAX / sizeof(uint64_t));
refcount_table_size2 = s->refcount_table_size * sizeof(uint64_t);
- s->refcount_table = g_malloc(refcount_table_size2);
+ s->refcount_table = g_try_malloc(refcount_table_size2);
+
if (s->refcount_table_size > 0) {
+ if (s->refcount_table == NULL) {
+ ret = -ENOMEM;
+ goto fail;
+ }
BLKDBG_EVENT(bs->file, BLKDBG_REFTABLE_LOAD);
ret = bdrv_pread(bs->file, s->refcount_table_offset,
s->refcount_table, refcount_table_size2);
- if (ret != refcount_table_size2)
+ if (ret < 0) {
goto fail;
+ }
for(i = 0; i < s->refcount_table_size; i++)
be64_to_cpus(&s->refcount_table[i]);
}
return 0;
fail:
- return -ENOMEM;
+ return ret;
}
void qcow2_refcount_close(BlockDriverState *bs)
@@ -87,7 +91,7 @@ static int load_refcount_block(BlockDriverState *bs,
* return value is the refcount of the cluster, negative values are -errno
* and indicate an error.
*/
-static int get_refcount(BlockDriverState *bs, int64_t cluster_index)
+int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index)
{
BDRVQcowState *s = bs->opaque;
uint64_t refcount_table_index, block_index;
@@ -96,7 +100,7 @@ static int get_refcount(BlockDriverState *bs, int64_t cluster_index)
uint16_t *refcount_block;
uint16_t refcount;
- refcount_table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT);
+ refcount_table_index = cluster_index >> s->refcount_block_bits;
if (refcount_table_index >= s->refcount_table_size)
return 0;
refcount_block_offset =
@@ -104,14 +108,20 @@ static int get_refcount(BlockDriverState *bs, int64_t cluster_index)
if (!refcount_block_offset)
return 0;
+ if (offset_into_cluster(s, refcount_block_offset)) {
+ qcow2_signal_corruption(bs, true, -1, -1, "Refblock offset %#" PRIx64
+ " unaligned (reftable index: %#" PRIx64 ")",
+ refcount_block_offset, refcount_table_index);
+ return -EIO;
+ }
+
ret = qcow2_cache_get(bs, s->refcount_block_cache, refcount_block_offset,
(void**) &refcount_block);
if (ret < 0) {
return ret;
}
- block_index = cluster_index &
- ((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1);
+ block_index = cluster_index & (s->refcount_block_size - 1);
refcount = be16_to_cpu(refcount_block[block_index]);
ret = qcow2_cache_put(bs, s->refcount_block_cache,
@@ -146,8 +156,8 @@ static unsigned int next_refcount_table_size(BDRVQcowState *s,
static int in_same_refcount_block(BDRVQcowState *s, uint64_t offset_a,
uint64_t offset_b)
{
- uint64_t block_a = offset_a >> (2 * s->cluster_bits - REFCOUNT_SHIFT);
- uint64_t block_b = offset_b >> (2 * s->cluster_bits - REFCOUNT_SHIFT);
+ uint64_t block_a = offset_a >> (s->cluster_bits + s->refcount_block_bits);
+ uint64_t block_b = offset_b >> (s->cluster_bits + s->refcount_block_bits);
return (block_a == block_b);
}
@@ -168,7 +178,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC);
/* Find the refcount block for the given cluster */
- refcount_table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT);
+ refcount_table_index = cluster_index >> s->refcount_block_bits;
if (refcount_table_index < s->refcount_table_size) {
@@ -177,6 +187,14 @@ static int alloc_refcount_block(BlockDriverState *bs,
/* If it's already there, we're done */
if (refcount_block_offset) {
+ if (offset_into_cluster(s, refcount_block_offset)) {
+ qcow2_signal_corruption(bs, true, -1, -1, "Refblock offset %#"
+ PRIx64 " unaligned (reftable index: "
+ "%#x)", refcount_block_offset,
+ refcount_table_index);
+ return -EIO;
+ }
+
return load_refcount_block(bs, refcount_block_offset,
(void**) refcount_block);
}
@@ -237,7 +255,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
/* The block describes itself, need to update the cache */
int block_index = (new_block >> s->cluster_bits) &
- ((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1);
+ (s->refcount_block_size - 1);
(*refcount_block)[block_index] = cpu_to_be16(1);
} else {
/* Described somewhere else. This can recurse at most twice before we
@@ -309,8 +327,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
BLKDBG_EVENT(bs->file, BLKDBG_REFTABLE_GROW);
/* Calculate the number of refcount blocks needed so far */
- uint64_t refcount_block_clusters = 1 << (s->cluster_bits - REFCOUNT_SHIFT);
- uint64_t blocks_used = DIV_ROUND_UP(cluster_index, refcount_block_clusters);
+ uint64_t blocks_used = DIV_ROUND_UP(cluster_index, s->refcount_block_size);
if (blocks_used > QCOW_MAX_REFTABLE_SIZE / sizeof(uint64_t)) {
return -EFBIG;
@@ -324,14 +341,14 @@ static int alloc_refcount_block(BlockDriverState *bs,
uint64_t table_clusters =
size_to_clusters(s, table_size * sizeof(uint64_t));
blocks_clusters = 1 +
- ((table_clusters + refcount_block_clusters - 1)
- / refcount_block_clusters);
+ ((table_clusters + s->refcount_block_size - 1)
+ / s->refcount_block_size);
uint64_t meta_clusters = table_clusters + blocks_clusters;
last_table_size = table_size;
table_size = next_refcount_table_size(s, blocks_used +
- ((meta_clusters + refcount_block_clusters - 1)
- / refcount_block_clusters));
+ ((meta_clusters + s->refcount_block_size - 1)
+ / s->refcount_block_size));
} while (last_table_size != table_size);
@@ -341,11 +358,17 @@ static int alloc_refcount_block(BlockDriverState *bs,
#endif
/* Create the new refcount table and blocks */
- uint64_t meta_offset = (blocks_used * refcount_block_clusters) *
+ uint64_t meta_offset = (blocks_used * s->refcount_block_size) *
s->cluster_size;
uint64_t table_offset = meta_offset + blocks_clusters * s->cluster_size;
- uint16_t *new_blocks = g_malloc0(blocks_clusters * s->cluster_size);
- uint64_t *new_table = g_malloc0(table_size * sizeof(uint64_t));
+ uint64_t *new_table = g_try_new0(uint64_t, table_size);
+ uint16_t *new_blocks = g_try_malloc0(blocks_clusters * s->cluster_size);
+
+ assert(table_size > 0 && blocks_clusters > 0);
+ if (new_table == NULL || new_blocks == NULL) {
+ ret = -ENOMEM;
+ goto fail_table;
+ }
/* Fill the new refcount table */
memcpy(new_table, s->refcount_table,
@@ -369,6 +392,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
ret = bdrv_pwrite_sync(bs->file, meta_offset, new_blocks,
blocks_clusters * s->cluster_size);
g_free(new_blocks);
+ new_blocks = NULL;
if (ret < 0) {
goto fail_table;
}
@@ -424,6 +448,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
return -EAGAIN;
fail_table:
+ g_free(new_blocks);
g_free(new_table);
fail_block:
if (*refcount_block != NULL) {
@@ -497,6 +522,7 @@ found:
QTAILQ_REMOVE(&s->discards, p, next);
d->offset = MIN(d->offset, p->offset);
d->bytes += p->bytes;
+ g_free(p);
}
}
@@ -532,8 +558,7 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
{
int block_index, refcount;
int64_t cluster_index = cluster_offset >> s->cluster_bits;
- int64_t table_index =
- cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT);
+ int64_t table_index = cluster_index >> s->refcount_block_bits;
/* Load the refcount block and allocate it if needed */
if (table_index != old_table_index) {
@@ -555,8 +580,7 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
qcow2_cache_entry_mark_dirty(s->refcount_block_cache, refcount_block);
/* we can update the count and save it */
- block_index = cluster_index &
- ((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1);
+ block_index = cluster_index & (s->refcount_block_size - 1);
refcount = be16_to_cpu(refcount_block[block_index]);
refcount += addend;
@@ -605,8 +629,7 @@ fail:
}
/*
- * Increases or decreases the refcount of a given cluster by one.
- * addend must be 1 or -1.
+ * Increases or decreases the refcount of a given cluster.
*
* If the return value is non-negative, it is the new refcount of the cluster.
* If it is negative, it is -errno and indicates an error.
@@ -625,7 +648,7 @@ int qcow2_update_cluster_refcount(BlockDriverState *bs,
return ret;
}
- return get_refcount(bs, cluster_index);
+ return qcow2_get_refcount(bs, cluster_index);
}
@@ -646,7 +669,7 @@ static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size)
retry:
for(i = 0; i < nb_clusters; i++) {
uint64_t next_cluster_index = s->free_cluster_index++;
- refcount = get_refcount(bs, next_cluster_index);
+ refcount = qcow2_get_refcount(bs, next_cluster_index);
if (refcount < 0) {
return refcount;
@@ -710,7 +733,7 @@ int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset,
/* Check how many clusters there are free */
cluster_index = offset >> s->cluster_bits;
for(i = 0; i < nb_clusters; i++) {
- refcount = get_refcount(bs, cluster_index++);
+ refcount = qcow2_get_refcount(bs, cluster_index++);
if (refcount < 0) {
return refcount;
@@ -824,8 +847,14 @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry,
case QCOW2_CLUSTER_NORMAL:
case QCOW2_CLUSTER_ZERO:
if (l2_entry & L2E_OFFSET_MASK) {
- qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK,
- nb_clusters << s->cluster_bits, type);
+ if (offset_into_cluster(s, l2_entry & L2E_OFFSET_MASK)) {
+ qcow2_signal_corruption(bs, false, -1, -1,
+ "Cannot free unaligned cluster %#llx",
+ l2_entry & L2E_OFFSET_MASK);
+ } else {
+ qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK,
+ nb_clusters << s->cluster_bits, type);
+ }
}
break;
case QCOW2_CLUSTER_UNALLOCATED:
@@ -847,7 +876,8 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
int64_t l1_table_offset, int l1_size, int addend)
{
BDRVQcowState *s = bs->opaque;
- uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, l1_allocated;
+ uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2;
+ bool l1_allocated = false;
int64_t old_offset, old_l2_offset;
int i, j, l1_modified = 0, nb_csectors, refcount;
int ret;
@@ -862,8 +892,12 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
* l1_table_offset when it is the current s->l1_table_offset! Be careful
* when changing this! */
if (l1_table_offset != s->l1_table_offset) {
- l1_table = g_malloc0(align_offset(l1_size2, 512));
- l1_allocated = 1;
+ l1_table = g_try_malloc0(align_offset(l1_size2, 512));
+ if (l1_size2 && l1_table == NULL) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+ l1_allocated = true;
ret = bdrv_pread(bs->file, l1_table_offset, l1_table, l1_size2);
if (ret < 0) {
@@ -875,7 +909,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
} else {
assert(l1_size == s->l1_size);
l1_table = s->l1_table;
- l1_allocated = 0;
+ l1_allocated = false;
}
for(i = 0; i < l1_size; i++) {
@@ -884,6 +918,14 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
old_l2_offset = l2_offset;
l2_offset &= L1E_OFFSET_MASK;
+ if (offset_into_cluster(s, l2_offset)) {
+ qcow2_signal_corruption(bs, true, -1, -1, "L2 table offset %#"
+ PRIx64 " unaligned (L1 index: %#x)",
+ l2_offset, i);
+ ret = -EIO;
+ goto fail;
+ }
+
ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset,
(void**) &l2_table);
if (ret < 0) {
@@ -916,6 +958,17 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
case QCOW2_CLUSTER_NORMAL:
case QCOW2_CLUSTER_ZERO:
+ if (offset_into_cluster(s, offset & L2E_OFFSET_MASK)) {
+ qcow2_signal_corruption(bs, true, -1, -1, "Data "
+ "cluster offset %#llx "
+ "unaligned (L2 offset: %#"
+ PRIx64 ", L2 index: %#x)",
+ offset & L2E_OFFSET_MASK,
+ l2_offset, j);
+ ret = -EIO;
+ goto fail;
+ }
+
cluster_index = (offset & L2E_OFFSET_MASK) >> s->cluster_bits;
if (!cluster_index) {
/* unallocated */
@@ -927,7 +980,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
cluster_index, addend,
QCOW2_DISCARD_SNAPSHOT);
} else {
- refcount = get_refcount(bs, cluster_index);
+ refcount = qcow2_get_refcount(bs, cluster_index);
}
if (refcount < 0) {
@@ -967,7 +1020,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
refcount = qcow2_update_cluster_refcount(bs, l2_offset >>
s->cluster_bits, addend, QCOW2_DISCARD_SNAPSHOT);
} else {
- refcount = get_refcount(bs, l2_offset >> s->cluster_bits);
+ refcount = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits);
}
if (refcount < 0) {
ret = refcount;
@@ -1023,36 +1076,52 @@ fail:
*
* Modifies the number of errors in res.
*/
-static void inc_refcounts(BlockDriverState *bs,
- BdrvCheckResult *res,
- uint16_t *refcount_table,
- int refcount_table_size,
- int64_t offset, int64_t size)
+static int inc_refcounts(BlockDriverState *bs,
+ BdrvCheckResult *res,
+ uint16_t **refcount_table,
+ int64_t *refcount_table_size,
+ int64_t offset, int64_t size)
{
BDRVQcowState *s = bs->opaque;
uint64_t start, last, cluster_offset, k;
- if (size <= 0)
- return;
+ if (size <= 0) {
+ return 0;
+ }
start = start_of_cluster(s, offset);
last = start_of_cluster(s, offset + size - 1);
for(cluster_offset = start; cluster_offset <= last;
cluster_offset += s->cluster_size) {
k = cluster_offset >> s->cluster_bits;
- if (k >= refcount_table_size) {
- fprintf(stderr, "Warning: cluster offset=0x%" PRIx64 " is after "
- "the end of the image file, can't properly check refcounts.\n",
- cluster_offset);
- res->check_errors++;
- } else {
- if (++refcount_table[k] == 0) {
- fprintf(stderr, "ERROR: overflow cluster offset=0x%" PRIx64
- "\n", cluster_offset);
- res->corruptions++;
+ if (k >= *refcount_table_size) {
+ int64_t old_refcount_table_size = *refcount_table_size;
+ uint16_t *new_refcount_table;
+
+ *refcount_table_size = k + 1;
+ new_refcount_table = g_try_realloc(*refcount_table,
+ *refcount_table_size *
+ sizeof(**refcount_table));
+ if (!new_refcount_table) {
+ *refcount_table_size = old_refcount_table_size;
+ res->check_errors++;
+ return -ENOMEM;
}
+ *refcount_table = new_refcount_table;
+
+ memset(*refcount_table + old_refcount_table_size, 0,
+ (*refcount_table_size - old_refcount_table_size) *
+ sizeof(**refcount_table));
+ }
+
+ if (++(*refcount_table)[k] == 0) {
+ fprintf(stderr, "ERROR: overflow cluster offset=0x%" PRIx64
+ "\n", cluster_offset);
+ res->corruptions++;
}
}
+
+ return 0;
}
/* Flags for check_refcounts_l1() and check_refcounts_l2() */
@@ -1069,20 +1138,24 @@ enum {
* error occurred.
*/
static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
- uint16_t *refcount_table, int refcount_table_size, int64_t l2_offset,
+ uint16_t **refcount_table, int64_t *refcount_table_size, int64_t l2_offset,
int flags)
{
BDRVQcowState *s = bs->opaque;
uint64_t *l2_table, l2_entry;
uint64_t next_contiguous_offset = 0;
- int i, l2_size, nb_csectors;
+ int i, l2_size, nb_csectors, ret;
/* Read L2 table from disk */
l2_size = s->l2_size * sizeof(uint64_t);
l2_table = g_malloc(l2_size);
- if (bdrv_pread(bs->file, l2_offset, l2_table, l2_size) != l2_size)
+ ret = bdrv_pread(bs->file, l2_offset, l2_table, l2_size);
+ if (ret < 0) {
+ fprintf(stderr, "ERROR: I/O error in check_refcounts_l2\n");
+ res->check_errors++;
goto fail;
+ }
/* Do the actual checks */
for(i = 0; i < s->l2_size; i++) {
@@ -1103,8 +1176,11 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
nb_csectors = ((l2_entry >> s->csize_shift) &
s->csize_mask) + 1;
l2_entry &= s->cluster_offset_mask;
- inc_refcounts(bs, res, refcount_table, refcount_table_size,
- l2_entry & ~511, nb_csectors * 512);
+ ret = inc_refcounts(bs, res, refcount_table, refcount_table_size,
+ l2_entry & ~511, nb_csectors * 512);
+ if (ret < 0) {
+ goto fail;
+ }
if (flags & CHECK_FRAG_INFO) {
res->bfi.allocated_clusters++;
@@ -1139,8 +1215,11 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
}
/* Mark cluster as used */
- inc_refcounts(bs, res, refcount_table,refcount_table_size,
- offset, s->cluster_size);
+ ret = inc_refcounts(bs, res, refcount_table, refcount_table_size,
+ offset, s->cluster_size);
+ if (ret < 0) {
+ goto fail;
+ }
/* Correct offsets are cluster aligned */
if (offset_into_cluster(s, offset)) {
@@ -1163,9 +1242,8 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
return 0;
fail:
- fprintf(stderr, "ERROR: I/O error in check_refcounts_l2\n");
g_free(l2_table);
- return -EIO;
+ return ret;
}
/*
@@ -1178,29 +1256,38 @@ fail:
*/
static int check_refcounts_l1(BlockDriverState *bs,
BdrvCheckResult *res,
- uint16_t *refcount_table,
- int refcount_table_size,
+ uint16_t **refcount_table,
+ int64_t *refcount_table_size,
int64_t l1_table_offset, int l1_size,
int flags)
{
BDRVQcowState *s = bs->opaque;
- uint64_t *l1_table, l2_offset, l1_size2;
+ uint64_t *l1_table = NULL, l2_offset, l1_size2;
int i, ret;
l1_size2 = l1_size * sizeof(uint64_t);
/* Mark L1 table as used */
- inc_refcounts(bs, res, refcount_table, refcount_table_size,
- l1_table_offset, l1_size2);
+ ret = inc_refcounts(bs, res, refcount_table, refcount_table_size,
+ l1_table_offset, l1_size2);
+ if (ret < 0) {
+ goto fail;
+ }
/* Read L1 table entries from disk */
- if (l1_size2 == 0) {
- l1_table = NULL;
- } else {
- l1_table = g_malloc(l1_size2);
- if (bdrv_pread(bs->file, l1_table_offset,
- l1_table, l1_size2) != l1_size2)
+ if (l1_size2 > 0) {
+ l1_table = g_try_malloc(l1_size2);
+ if (l1_table == NULL) {
+ ret = -ENOMEM;
+ res->check_errors++;
+ goto fail;
+ }
+ ret = bdrv_pread(bs->file, l1_table_offset, l1_table, l1_size2);
+ if (ret < 0) {
+ fprintf(stderr, "ERROR: I/O error in check_refcounts_l1\n");
+ res->check_errors++;
goto fail;
+ }
for(i = 0;i < l1_size; i++)
be64_to_cpus(&l1_table[i]);
}
@@ -1211,8 +1298,11 @@ static int check_refcounts_l1(BlockDriverState *bs,
if (l2_offset) {
/* Mark L2 table as used */
l2_offset &= L1E_OFFSET_MASK;
- inc_refcounts(bs, res, refcount_table, refcount_table_size,
- l2_offset, s->cluster_size);
+ ret = inc_refcounts(bs, res, refcount_table, refcount_table_size,
+ l2_offset, s->cluster_size);
+ if (ret < 0) {
+ goto fail;
+ }
/* L2 tables are cluster aligned */
if (offset_into_cluster(s, l2_offset)) {
@@ -1233,18 +1323,16 @@ static int check_refcounts_l1(BlockDriverState *bs,
return 0;
fail:
- fprintf(stderr, "ERROR: I/O error in check_refcounts_l1\n");
- res->check_errors++;
g_free(l1_table);
- return -EIO;
+ return ret;
}
/*
* Checks the OFLAG_COPIED flag for all L1 and L2 entries.
*
* This function does not print an error message nor does it increment
- * check_errors if get_refcount fails (this is because such an error will have
- * been already detected and sufficiently signaled by the calling function
+ * check_errors if qcow2_get_refcount fails (this is because such an error will
+ * have been already detected and sufficiently signaled by the calling function
* (qcow2_check_refcounts) by the time this function is called).
*/
static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
@@ -1265,7 +1353,7 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
continue;
}
- refcount = get_refcount(bs, l2_offset >> s->cluster_bits);
+ refcount = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits);
if (refcount < 0) {
/* don't print message nor increment check_errors */
continue;
@@ -1307,7 +1395,8 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
if ((cluster_type == QCOW2_CLUSTER_NORMAL) ||
((cluster_type == QCOW2_CLUSTER_ZERO) && (data_offset != 0))) {
- refcount = get_refcount(bs, data_offset >> s->cluster_bits);
+ refcount = qcow2_get_refcount(bs,
+ data_offset >> s->cluster_bits);
if (refcount < 0) {
/* don't print message nor increment check_errors */
continue;
@@ -1359,245 +1448,191 @@ fail:
}
/*
- * Writes one sector of the refcount table to the disk
+ * Checks consistency of refblocks and accounts for each refblock in
+ * *refcount_table.
*/
-#define RT_ENTRIES_PER_SECTOR (512 / sizeof(uint64_t))
-static int write_reftable_entry(BlockDriverState *bs, int rt_index)
+static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res,
+ BdrvCheckMode fix, bool *rebuild,
+ uint16_t **refcount_table, int64_t *nb_clusters)
{
BDRVQcowState *s = bs->opaque;
- uint64_t buf[RT_ENTRIES_PER_SECTOR];
- int rt_start_index;
- int i, ret;
-
- rt_start_index = rt_index & ~(RT_ENTRIES_PER_SECTOR - 1);
- for (i = 0; i < RT_ENTRIES_PER_SECTOR; i++) {
- buf[i] = cpu_to_be64(s->refcount_table[rt_start_index + i]);
- }
+ int64_t i, size;
+ int ret;
- ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_REFCOUNT_TABLE,
- s->refcount_table_offset + rt_start_index * sizeof(uint64_t),
- sizeof(buf));
- if (ret < 0) {
- return ret;
- }
+ for(i = 0; i < s->refcount_table_size; i++) {
+ uint64_t offset, cluster;
+ offset = s->refcount_table[i];
+ cluster = offset >> s->cluster_bits;
- BLKDBG_EVENT(bs->file, BLKDBG_REFTABLE_UPDATE);
- ret = bdrv_pwrite_sync(bs->file, s->refcount_table_offset +
- rt_start_index * sizeof(uint64_t), buf, sizeof(buf));
- if (ret < 0) {
- return ret;
- }
+ /* Refcount blocks are cluster aligned */
+ if (offset_into_cluster(s, offset)) {
+ fprintf(stderr, "ERROR refcount block %" PRId64 " is not "
+ "cluster aligned; refcount table entry corrupted\n", i);
+ res->corruptions++;
+ *rebuild = true;
+ continue;
+ }
- return 0;
-}
+ if (cluster >= *nb_clusters) {
+ fprintf(stderr, "%s refcount block %" PRId64 " is outside image\n",
+ fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", i);
-/*
- * Allocates a new cluster for the given refcount block (represented by its
- * offset in the image file) and copies the current content there. This function
- * does _not_ decrement the reference count for the currently occupied cluster.
- *
- * This function prints an informative message to stderr on error (and returns
- * -errno); on success, the offset of the newly allocated cluster is returned.
- */
-static int64_t realloc_refcount_block(BlockDriverState *bs, int reftable_index,
- uint64_t offset)
-{
- BDRVQcowState *s = bs->opaque;
- int64_t new_offset = 0;
- void *refcount_block = NULL;
- int ret;
+ if (fix & BDRV_FIX_ERRORS) {
+ int64_t old_nb_clusters = *nb_clusters;
+ uint16_t *new_refcount_table;
- /* allocate new refcount block */
- new_offset = qcow2_alloc_clusters(bs, s->cluster_size);
- if (new_offset < 0) {
- fprintf(stderr, "Could not allocate new cluster: %s\n",
- strerror(-new_offset));
- ret = new_offset;
- goto done;
- }
+ if (offset > INT64_MAX - s->cluster_size) {
+ ret = -EINVAL;
+ goto resize_fail;
+ }
- /* fetch current refcount block content */
- ret = qcow2_cache_get(bs, s->refcount_block_cache, offset, &refcount_block);
- if (ret < 0) {
- fprintf(stderr, "Could not fetch refcount block: %s\n", strerror(-ret));
- goto fail_free_cluster;
- }
+ ret = bdrv_truncate(bs->file, offset + s->cluster_size);
+ if (ret < 0) {
+ goto resize_fail;
+ }
+ size = bdrv_getlength(bs->file);
+ if (size < 0) {
+ ret = size;
+ goto resize_fail;
+ }
- /* new block has not yet been entered into refcount table, therefore it is
- * no refcount block yet (regarding this check) */
- ret = qcow2_pre_write_overlap_check(bs, 0, new_offset, s->cluster_size);
- if (ret < 0) {
- fprintf(stderr, "Could not write refcount block; metadata overlap "
- "check failed: %s\n", strerror(-ret));
- /* the image will be marked corrupt, so don't even attempt on freeing
- * the cluster */
- goto done;
- }
+ *nb_clusters = size_to_clusters(s, size);
+ assert(*nb_clusters >= old_nb_clusters);
- /* write to new block */
- ret = bdrv_write(bs->file, new_offset / BDRV_SECTOR_SIZE, refcount_block,
- s->cluster_sectors);
- if (ret < 0) {
- fprintf(stderr, "Could not write refcount block: %s\n", strerror(-ret));
- goto fail_free_cluster;
- }
+ new_refcount_table = g_try_realloc(*refcount_table,
+ *nb_clusters *
+ sizeof(**refcount_table));
+ if (!new_refcount_table) {
+ *nb_clusters = old_nb_clusters;
+ res->check_errors++;
+ return -ENOMEM;
+ }
+ *refcount_table = new_refcount_table;
- /* update refcount table */
- assert(!offset_into_cluster(s, new_offset));
- s->refcount_table[reftable_index] = new_offset;
- ret = write_reftable_entry(bs, reftable_index);
- if (ret < 0) {
- fprintf(stderr, "Could not update refcount table: %s\n",
- strerror(-ret));
- goto fail_free_cluster;
- }
+ memset(*refcount_table + old_nb_clusters, 0,
+ (*nb_clusters - old_nb_clusters) *
+ sizeof(**refcount_table));
- goto done;
+ if (cluster >= *nb_clusters) {
+ ret = -EINVAL;
+ goto resize_fail;
+ }
-fail_free_cluster:
- qcow2_free_clusters(bs, new_offset, s->cluster_size, QCOW2_DISCARD_OTHER);
+ res->corruptions_fixed++;
+ ret = inc_refcounts(bs, res, refcount_table, nb_clusters,
+ offset, s->cluster_size);
+ if (ret < 0) {
+ return ret;
+ }
+ /* No need to check whether the refcount is now greater than 1:
+ * This area was just allocated and zeroed, so it can only be
+ * exactly 1 after inc_refcounts() */
+ continue;
-done:
- if (refcount_block) {
- /* This should never fail, as it would only do so if the given refcount
- * block cannot be found in the cache. As this is impossible as long as
- * there are no bugs, assert the success. */
- int tmp = qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block);
- assert(tmp == 0);
- }
+resize_fail:
+ res->corruptions++;
+ *rebuild = true;
+ fprintf(stderr, "ERROR could not resize image: %s\n",
+ strerror(-ret));
+ } else {
+ res->corruptions++;
+ }
+ continue;
+ }
- if (ret < 0) {
- return ret;
+ if (offset != 0) {
+ ret = inc_refcounts(bs, res, refcount_table, nb_clusters,
+ offset, s->cluster_size);
+ if (ret < 0) {
+ return ret;
+ }
+ if ((*refcount_table)[cluster] != 1) {
+ fprintf(stderr, "ERROR refcount block %" PRId64
+ " refcount=%d\n", i, (*refcount_table)[cluster]);
+ res->corruptions++;
+ *rebuild = true;
+ }
+ }
}
- return new_offset;
+ return 0;
}
/*
- * Checks an image for refcount consistency.
- *
- * Returns 0 if no errors are found, the number of errors in case the image is
- * detected as corrupted, and -errno when an internal error occurred.
+ * Calculates an in-memory refcount table.
*/
-int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
- BdrvCheckMode fix)
+static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
+ BdrvCheckMode fix, bool *rebuild,
+ uint16_t **refcount_table, int64_t *nb_clusters)
{
BDRVQcowState *s = bs->opaque;
- int64_t size, i, highest_cluster, nb_clusters;
- int refcount1, refcount2;
+ int64_t i;
QCowSnapshot *sn;
- uint16_t *refcount_table;
int ret;
- size = bdrv_getlength(bs->file);
- if (size < 0) {
- res->check_errors++;
- return size;
- }
-
- nb_clusters = size_to_clusters(s, size);
- if (nb_clusters > INT_MAX) {
- res->check_errors++;
- return -EFBIG;
+ if (!*refcount_table) {
+ *refcount_table = g_try_new0(uint16_t, *nb_clusters);
+ if (*nb_clusters && *refcount_table == NULL) {
+ res->check_errors++;
+ return -ENOMEM;
+ }
}
- refcount_table = g_malloc0(nb_clusters * sizeof(uint16_t));
-
- res->bfi.total_clusters =
- size_to_clusters(s, bs->total_sectors * BDRV_SECTOR_SIZE);
-
/* header */
- inc_refcounts(bs, res, refcount_table, nb_clusters,
- 0, s->cluster_size);
+ ret = inc_refcounts(bs, res, refcount_table, nb_clusters,
+ 0, s->cluster_size);
+ if (ret < 0) {
+ return ret;
+ }
/* current L1 table */
ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters,
s->l1_table_offset, s->l1_size, CHECK_FRAG_INFO);
if (ret < 0) {
- goto fail;
+ return ret;
}
/* snapshots */
- for(i = 0; i < s->nb_snapshots; i++) {
+ for (i = 0; i < s->nb_snapshots; i++) {
sn = s->snapshots + i;
ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters,
- sn->l1_table_offset, sn->l1_size, 0);
+ sn->l1_table_offset, sn->l1_size, 0);
if (ret < 0) {
- goto fail;
+ return ret;
}
}
- inc_refcounts(bs, res, refcount_table, nb_clusters,
- s->snapshots_offset, s->snapshots_size);
+ ret = inc_refcounts(bs, res, refcount_table, nb_clusters,
+ s->snapshots_offset, s->snapshots_size);
+ if (ret < 0) {
+ return ret;
+ }
/* refcount data */
- inc_refcounts(bs, res, refcount_table, nb_clusters,
- s->refcount_table_offset,
- s->refcount_table_size * sizeof(uint64_t));
-
- for(i = 0; i < s->refcount_table_size; i++) {
- uint64_t offset, cluster;
- offset = s->refcount_table[i];
- cluster = offset >> s->cluster_bits;
-
- /* Refcount blocks are cluster aligned */
- if (offset_into_cluster(s, offset)) {
- fprintf(stderr, "ERROR refcount block %" PRId64 " is not "
- "cluster aligned; refcount table entry corrupted\n", i);
- res->corruptions++;
- continue;
- }
-
- if (cluster >= nb_clusters) {
- fprintf(stderr, "ERROR refcount block %" PRId64
- " is outside image\n", i);
- res->corruptions++;
- continue;
- }
-
- if (offset != 0) {
- inc_refcounts(bs, res, refcount_table, nb_clusters,
- offset, s->cluster_size);
- if (refcount_table[cluster] != 1) {
- fprintf(stderr, "%s refcount block %" PRId64
- " refcount=%d\n",
- fix & BDRV_FIX_ERRORS ? "Repairing" :
- "ERROR",
- i, refcount_table[cluster]);
-
- if (fix & BDRV_FIX_ERRORS) {
- int64_t new_offset;
-
- new_offset = realloc_refcount_block(bs, i, offset);
- if (new_offset < 0) {
- res->corruptions++;
- continue;
- }
+ ret = inc_refcounts(bs, res, refcount_table, nb_clusters,
+ s->refcount_table_offset,
+ s->refcount_table_size * sizeof(uint64_t));
+ if (ret < 0) {
+ return ret;
+ }
- /* update refcounts */
- if ((new_offset >> s->cluster_bits) >= nb_clusters) {
- /* increase refcount_table size if necessary */
- int old_nb_clusters = nb_clusters;
- nb_clusters = (new_offset >> s->cluster_bits) + 1;
- refcount_table = g_realloc(refcount_table,
- nb_clusters * sizeof(uint16_t));
- memset(&refcount_table[old_nb_clusters], 0, (nb_clusters
- - old_nb_clusters) * sizeof(uint16_t));
- }
- refcount_table[cluster]--;
- inc_refcounts(bs, res, refcount_table, nb_clusters,
- new_offset, s->cluster_size);
+ return check_refblocks(bs, res, fix, rebuild, refcount_table, nb_clusters);
+}
- res->corruptions_fixed++;
- } else {
- res->corruptions++;
- }
- }
- }
- }
+/*
+ * Compares the actual reference count for each cluster in the image against the
+ * refcount as reported by the refcount structures on-disk.
+ */
+static void compare_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
+ BdrvCheckMode fix, bool *rebuild,
+ int64_t *highest_cluster,
+ uint16_t *refcount_table, int64_t nb_clusters)
+{
+ BDRVQcowState *s = bs->opaque;
+ int64_t i;
+ int refcount1, refcount2, ret;
- /* compare ref counts */
- for (i = 0, highest_cluster = 0; i < nb_clusters; i++) {
- refcount1 = get_refcount(bs, i);
+ for (i = 0, *highest_cluster = 0; i < nb_clusters; i++) {
+ refcount1 = qcow2_get_refcount(bs, i);
if (refcount1 < 0) {
fprintf(stderr, "Can't get refcount for cluster %" PRId64 ": %s\n",
i, strerror(-refcount1));
@@ -1608,14 +1643,15 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
refcount2 = refcount_table[i];
if (refcount1 > 0 || refcount2 > 0) {
- highest_cluster = i;
+ *highest_cluster = i;
}
if (refcount1 != refcount2) {
-
/* Check if we're allowed to fix the mismatch */
int *num_fixed = NULL;
- if (refcount1 > refcount2 && (fix & BDRV_FIX_LEAKS)) {
+ if (refcount1 == 0) {
+ *rebuild = true;
+ } else if (refcount1 > refcount2 && (fix & BDRV_FIX_LEAKS)) {
num_fixed = &res->leaks_fixed;
} else if (refcount1 < refcount2 && (fix & BDRV_FIX_ERRORS)) {
num_fixed = &res->corruptions_fixed;
@@ -1645,6 +1681,397 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
}
}
}
+}
+
+/*
+ * Allocates clusters using an in-memory refcount table (IMRT) in contrast to
+ * the on-disk refcount structures.
+ *
+ * On input, *first_free_cluster tells where to start looking, and need not
+ * actually be a free cluster; the returned offset will not be before that
+ * cluster. On output, *first_free_cluster points to the first gap found, even
+ * if that gap was too small to be used as the returned offset.
+ *
+ * Note that *first_free_cluster is a cluster index whereas the return value is
+ * an offset.
+ */
+static int64_t alloc_clusters_imrt(BlockDriverState *bs,
+ int cluster_count,
+ uint16_t **refcount_table,
+ int64_t *imrt_nb_clusters,
+ int64_t *first_free_cluster)
+{
+ BDRVQcowState *s = bs->opaque;
+ int64_t cluster = *first_free_cluster, i;
+ bool first_gap = true;
+ int contiguous_free_clusters;
+
+ /* Starting at *first_free_cluster, find a range of at least cluster_count
+ * continuously free clusters */
+ for (contiguous_free_clusters = 0;
+ cluster < *imrt_nb_clusters &&
+ contiguous_free_clusters < cluster_count;
+ cluster++)
+ {
+ if (!(*refcount_table)[cluster]) {
+ contiguous_free_clusters++;
+ if (first_gap) {
+ /* If this is the first free cluster found, update
+ * *first_free_cluster accordingly */
+ *first_free_cluster = cluster;
+ first_gap = false;
+ }
+ } else if (contiguous_free_clusters) {
+ contiguous_free_clusters = 0;
+ }
+ }
+
+ /* If contiguous_free_clusters is greater than zero, it contains the number
+ * of continuously free clusters until the current cluster; the first free
+ * cluster in the current "gap" is therefore
+ * cluster - contiguous_free_clusters */
+
+ /* If no such range could be found, grow the in-memory refcount table
+ * accordingly to append free clusters at the end of the image */
+ if (contiguous_free_clusters < cluster_count) {
+ int64_t old_imrt_nb_clusters = *imrt_nb_clusters;
+ uint16_t *new_refcount_table;
+
+ /* contiguous_free_clusters clusters are already empty at the image end;
+ * we need cluster_count clusters; therefore, we have to allocate
+ * cluster_count - contiguous_free_clusters new clusters at the end of
+ * the image (which is the current value of cluster; note that cluster
+ * may exceed old_imrt_nb_clusters if *first_free_cluster pointed beyond
+ * the image end) */
+ *imrt_nb_clusters = cluster + cluster_count - contiguous_free_clusters;
+ new_refcount_table = g_try_realloc(*refcount_table,
+ *imrt_nb_clusters *
+ sizeof(**refcount_table));
+ if (!new_refcount_table) {
+ *imrt_nb_clusters = old_imrt_nb_clusters;
+ return -ENOMEM;
+ }
+ *refcount_table = new_refcount_table;
+
+ memset(*refcount_table + old_imrt_nb_clusters, 0,
+ (*imrt_nb_clusters - old_imrt_nb_clusters) *
+ sizeof(**refcount_table));
+ }
+
+ /* Go back to the first free cluster */
+ cluster -= contiguous_free_clusters;
+ for (i = 0; i < cluster_count; i++) {
+ (*refcount_table)[cluster + i] = 1;
+ }
+
+ return cluster << s->cluster_bits;
+}
+
+/*
+ * Creates a new refcount structure based solely on the in-memory information
+ * given through *refcount_table. All necessary allocations will be reflected
+ * in that array.
+ *
+ * On success, the old refcount structure is leaked (it will be covered by the
+ * new refcount structure).
+ */
+static int rebuild_refcount_structure(BlockDriverState *bs,
+ BdrvCheckResult *res,
+ uint16_t **refcount_table,
+ int64_t *nb_clusters)
+{
+ BDRVQcowState *s = bs->opaque;
+ int64_t first_free_cluster = 0, reftable_offset = -1, cluster = 0;
+ int64_t refblock_offset, refblock_start, refblock_index;
+ uint32_t reftable_size = 0;
+ uint64_t *on_disk_reftable = NULL;
+ uint16_t *on_disk_refblock;
+ int i, ret = 0;
+ struct {
+ uint64_t reftable_offset;
+ uint32_t reftable_clusters;
+ } QEMU_PACKED reftable_offset_and_clusters;
+
+ qcow2_cache_empty(bs, s->refcount_block_cache);
+
+write_refblocks:
+ for (; cluster < *nb_clusters; cluster++) {
+ if (!(*refcount_table)[cluster]) {
+ continue;
+ }
+
+ refblock_index = cluster >> s->refcount_block_bits;
+ refblock_start = refblock_index << s->refcount_block_bits;
+
+ /* Don't allocate a cluster in a refblock already written to disk */
+ if (first_free_cluster < refblock_start) {
+ first_free_cluster = refblock_start;
+ }
+ refblock_offset = alloc_clusters_imrt(bs, 1, refcount_table,
+ nb_clusters, &first_free_cluster);
+ if (refblock_offset < 0) {
+ fprintf(stderr, "ERROR allocating refblock: %s\n",
+ strerror(-refblock_offset));
+ res->check_errors++;
+ ret = refblock_offset;
+ goto fail;
+ }
+
+ if (reftable_size <= refblock_index) {
+ uint32_t old_reftable_size = reftable_size;
+ uint64_t *new_on_disk_reftable;
+
+ reftable_size = ROUND_UP((refblock_index + 1) * sizeof(uint64_t),
+ s->cluster_size) / sizeof(uint64_t);
+ new_on_disk_reftable = g_try_realloc(on_disk_reftable,
+ reftable_size *
+ sizeof(uint64_t));
+ if (!new_on_disk_reftable) {
+ res->check_errors++;
+ ret = -ENOMEM;
+ goto fail;
+ }
+ on_disk_reftable = new_on_disk_reftable;
+
+ memset(on_disk_reftable + old_reftable_size, 0,
+ (reftable_size - old_reftable_size) * sizeof(uint64_t));
+
+ /* The offset we have for the reftable is now no longer valid;
+ * this will leak that range, but we can easily fix that by running
+ * a leak-fixing check after this rebuild operation */
+ reftable_offset = -1;
+ }
+ on_disk_reftable[refblock_index] = refblock_offset;
+
+ /* If this is apparently the last refblock (for now), try to squeeze the
+ * reftable in */
+ if (refblock_index == (*nb_clusters - 1) >> s->refcount_block_bits &&
+ reftable_offset < 0)
+ {
+ uint64_t reftable_clusters = size_to_clusters(s, reftable_size *
+ sizeof(uint64_t));
+ reftable_offset = alloc_clusters_imrt(bs, reftable_clusters,
+ refcount_table, nb_clusters,
+ &first_free_cluster);
+ if (reftable_offset < 0) {
+ fprintf(stderr, "ERROR allocating reftable: %s\n",
+ strerror(-reftable_offset));
+ res->check_errors++;
+ ret = reftable_offset;
+ goto fail;
+ }
+ }
+
+ ret = qcow2_pre_write_overlap_check(bs, 0, refblock_offset,
+ s->cluster_size);
+ if (ret < 0) {
+ fprintf(stderr, "ERROR writing refblock: %s\n", strerror(-ret));
+ goto fail;
+ }
+
+ on_disk_refblock = qemu_blockalign0(bs->file, s->cluster_size);
+ for (i = 0; i < s->refcount_block_size &&
+ refblock_start + i < *nb_clusters; i++)
+ {
+ on_disk_refblock[i] =
+ cpu_to_be16((*refcount_table)[refblock_start + i]);
+ }
+
+ ret = bdrv_write(bs->file, refblock_offset / BDRV_SECTOR_SIZE,
+ (void *)on_disk_refblock, s->cluster_sectors);
+ qemu_vfree(on_disk_refblock);
+ if (ret < 0) {
+ fprintf(stderr, "ERROR writing refblock: %s\n", strerror(-ret));
+ goto fail;
+ }
+
+ /* Go to the end of this refblock */
+ cluster = refblock_start + s->refcount_block_size - 1;
+ }
+
+ if (reftable_offset < 0) {
+ uint64_t post_refblock_start, reftable_clusters;
+
+ post_refblock_start = ROUND_UP(*nb_clusters, s->refcount_block_size);
+ reftable_clusters = size_to_clusters(s,
+ reftable_size * sizeof(uint64_t));
+ /* Not pretty but simple */
+ if (first_free_cluster < post_refblock_start) {
+ first_free_cluster = post_refblock_start;
+ }
+ reftable_offset = alloc_clusters_imrt(bs, reftable_clusters,
+ refcount_table, nb_clusters,
+ &first_free_cluster);
+ if (reftable_offset < 0) {
+ fprintf(stderr, "ERROR allocating reftable: %s\n",
+ strerror(-reftable_offset));
+ res->check_errors++;
+ ret = reftable_offset;
+ goto fail;
+ }
+
+ goto write_refblocks;
+ }
+
+ assert(on_disk_reftable);
+
+ for (refblock_index = 0; refblock_index < reftable_size; refblock_index++) {
+ cpu_to_be64s(&on_disk_reftable[refblock_index]);
+ }
+
+ ret = qcow2_pre_write_overlap_check(bs, 0, reftable_offset,
+ reftable_size * sizeof(uint64_t));
+ if (ret < 0) {
+ fprintf(stderr, "ERROR writing reftable: %s\n", strerror(-ret));
+ goto fail;
+ }
+
+ assert(reftable_size < INT_MAX / sizeof(uint64_t));
+ ret = bdrv_pwrite(bs->file, reftable_offset, on_disk_reftable,
+ reftable_size * sizeof(uint64_t));
+ if (ret < 0) {
+ fprintf(stderr, "ERROR writing reftable: %s\n", strerror(-ret));
+ goto fail;
+ }
+
+ /* Enter new reftable into the image header */
+ cpu_to_be64w(&reftable_offset_and_clusters.reftable_offset,
+ reftable_offset);
+ cpu_to_be32w(&reftable_offset_and_clusters.reftable_clusters,
+ size_to_clusters(s, reftable_size * sizeof(uint64_t)));
+ ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader,
+ refcount_table_offset),
+ &reftable_offset_and_clusters,
+ sizeof(reftable_offset_and_clusters));
+ if (ret < 0) {
+ fprintf(stderr, "ERROR setting reftable: %s\n", strerror(-ret));
+ goto fail;
+ }
+
+ for (refblock_index = 0; refblock_index < reftable_size; refblock_index++) {
+ be64_to_cpus(&on_disk_reftable[refblock_index]);
+ }
+ s->refcount_table = on_disk_reftable;
+ s->refcount_table_offset = reftable_offset;
+ s->refcount_table_size = reftable_size;
+
+ return 0;
+
+fail:
+ g_free(on_disk_reftable);
+ return ret;
+}
+
+/*
+ * Checks an image for refcount consistency.
+ *
+ * Returns 0 if no errors are found, the number of errors in case the image is
+ * detected as corrupted, and -errno when an internal error occurred.
+ */
+int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
+ BdrvCheckMode fix)
+{
+ BDRVQcowState *s = bs->opaque;
+ BdrvCheckResult pre_compare_res;
+ int64_t size, highest_cluster, nb_clusters;
+ uint16_t *refcount_table = NULL;
+ bool rebuild = false;
+ int ret;
+
+ size = bdrv_getlength(bs->file);
+ if (size < 0) {
+ res->check_errors++;
+ return size;
+ }
+
+ nb_clusters = size_to_clusters(s, size);
+ if (nb_clusters > INT_MAX) {
+ res->check_errors++;
+ return -EFBIG;
+ }
+
+ res->bfi.total_clusters =
+ size_to_clusters(s, bs->total_sectors * BDRV_SECTOR_SIZE);
+
+ ret = calculate_refcounts(bs, res, fix, &rebuild, &refcount_table,
+ &nb_clusters);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ /* In case we don't need to rebuild the refcount structure (but want to fix
+ * something), this function is immediately called again, in which case the
+ * result should be ignored */
+ pre_compare_res = *res;
+ compare_refcounts(bs, res, 0, &rebuild, &highest_cluster, refcount_table,
+ nb_clusters);
+
+ if (rebuild && (fix & BDRV_FIX_ERRORS)) {
+ BdrvCheckResult old_res = *res;
+ int fresh_leaks = 0;
+
+ fprintf(stderr, "Rebuilding refcount structure\n");
+ ret = rebuild_refcount_structure(bs, res, &refcount_table,
+ &nb_clusters);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ res->corruptions = 0;
+ res->leaks = 0;
+
+ /* Because the old reftable has been exchanged for a new one the
+ * references have to be recalculated */
+ rebuild = false;
+ memset(refcount_table, 0, nb_clusters * sizeof(uint16_t));
+ ret = calculate_refcounts(bs, res, 0, &rebuild, &refcount_table,
+ &nb_clusters);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ if (fix & BDRV_FIX_LEAKS) {
+ /* The old refcount structures are now leaked, fix it; the result
+ * can be ignored, aside from leaks which were introduced by
+ * rebuild_refcount_structure() that could not be fixed */
+ BdrvCheckResult saved_res = *res;
+ *res = (BdrvCheckResult){ 0 };
+
+ compare_refcounts(bs, res, BDRV_FIX_LEAKS, &rebuild,
+ &highest_cluster, refcount_table, nb_clusters);
+ if (rebuild) {
+ fprintf(stderr, "ERROR rebuilt refcount structure is still "
+ "broken\n");
+ }
+
+ /* Any leaks accounted for here were introduced by
+ * rebuild_refcount_structure() because that function has created a
+ * new refcount structure from scratch */
+ fresh_leaks = res->leaks;
+ *res = saved_res;
+ }
+
+ if (res->corruptions < old_res.corruptions) {
+ res->corruptions_fixed += old_res.corruptions - res->corruptions;
+ }
+ if (res->leaks < old_res.leaks) {
+ res->leaks_fixed += old_res.leaks - res->leaks;
+ }
+ res->leaks += fresh_leaks;
+ } else if (fix) {
+ if (rebuild) {
+ fprintf(stderr, "ERROR need to rebuild refcount structures\n");
+ res->check_errors++;
+ ret = -EIO;
+ goto fail;
+ }
+
+ if (res->leaks || res->corruptions) {
+ *res = pre_compare_res;
+ compare_refcounts(bs, res, fix, &rebuild, &highest_cluster,
+ refcount_table, nb_clusters);
+ }
+ }
/* check OFLAG_COPIED */
ret = check_oflag_copied(bs, res, fix);
@@ -1753,9 +2180,13 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset,
uint64_t l1_ofs = s->snapshots[i].l1_table_offset;
uint32_t l1_sz = s->snapshots[i].l1_size;
uint64_t l1_sz2 = l1_sz * sizeof(uint64_t);
- uint64_t *l1 = g_malloc(l1_sz2);
+ uint64_t *l1 = g_try_malloc(l1_sz2);
int ret;
+ if (l1_sz2 && l1 == NULL) {
+ return -ENOMEM;
+ }
+
ret = bdrv_pread(bs->file, l1_ofs, l1, l1_sz2);
if (ret < 0) {
g_free(l1);
@@ -1807,26 +2238,11 @@ int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset,
return ret;
} else if (ret > 0) {
int metadata_ol_bitnr = ffs(ret) - 1;
- char *message;
-
assert(metadata_ol_bitnr < QCOW2_OL_MAX_BITNR);
- fprintf(stderr, "qcow2: Preventing invalid write on metadata (overlaps "
- "with %s); image marked as corrupt.\n",
- metadata_ol_names[metadata_ol_bitnr]);
- message = g_strdup_printf("Prevented %s overwrite",
- metadata_ol_names[metadata_ol_bitnr]);
- qapi_event_send_block_image_corrupted(bdrv_get_device_name(bs),
- message,
- true,
- offset,
- true,
- size,
- &error_abort);
- g_free(message);
-
- qcow2_mark_corrupt(bs);
- bs->drv = NULL; /* make BDS unusable */
+ qcow2_signal_corruption(bs, true, offset, size, "Preventing invalid "
+ "write on metadata (overlaps with %s)",
+ metadata_ol_names[metadata_ol_bitnr]);
return -EIO;
}