qcow2: Support for fixing refcount inconsistencies

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
Kevin Wolf 2012-05-11 18:18:36 +02:00
parent ccf34716ee
commit 166acf546f
3 changed files with 37 additions and 15 deletions

View File

@ -1122,11 +1122,12 @@ fail:
* Returns 0 if no errors are found, the number of errors in case the image is * 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. * detected as corrupted, and -errno when an internal error occurred.
*/ */
int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res) int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
BdrvCheckMode fix)
{ {
BDRVQcowState *s = bs->opaque; BDRVQcowState *s = bs->opaque;
int64_t size; int64_t size, i;
int nb_clusters, refcount1, refcount2, i; int nb_clusters, refcount1, refcount2;
QCowSnapshot *sn; QCowSnapshot *sn;
uint16_t *refcount_table; uint16_t *refcount_table;
int ret; int ret;
@ -1170,14 +1171,15 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res)
/* Refcount blocks are cluster aligned */ /* Refcount blocks are cluster aligned */
if (offset & (s->cluster_size - 1)) { if (offset & (s->cluster_size - 1)) {
fprintf(stderr, "ERROR refcount block %d is not " fprintf(stderr, "ERROR refcount block %" PRId64 " is not "
"cluster aligned; refcount table entry corrupted\n", i); "cluster aligned; refcount table entry corrupted\n", i);
res->corruptions++; res->corruptions++;
continue; continue;
} }
if (cluster >= nb_clusters) { if (cluster >= nb_clusters) {
fprintf(stderr, "ERROR refcount block %d is outside image\n", i); fprintf(stderr, "ERROR refcount block %" PRId64
" is outside image\n", i);
res->corruptions++; res->corruptions++;
continue; continue;
} }
@ -1186,7 +1188,8 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res)
inc_refcounts(bs, res, refcount_table, nb_clusters, inc_refcounts(bs, res, refcount_table, nb_clusters,
offset, s->cluster_size); offset, s->cluster_size);
if (refcount_table[cluster] != 1) { if (refcount_table[cluster] != 1) {
fprintf(stderr, "ERROR refcount block %d refcount=%d\n", fprintf(stderr, "ERROR refcount block %" PRId64
" refcount=%d\n",
i, refcount_table[cluster]); i, refcount_table[cluster]);
res->corruptions++; res->corruptions++;
} }
@ -1197,7 +1200,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res)
for(i = 0; i < nb_clusters; i++) { for(i = 0; i < nb_clusters; i++) {
refcount1 = get_refcount(bs, i); refcount1 = get_refcount(bs, i);
if (refcount1 < 0) { if (refcount1 < 0) {
fprintf(stderr, "Can't get refcount for cluster %d: %s\n", fprintf(stderr, "Can't get refcount for cluster %" PRId64 ": %s\n",
i, strerror(-refcount1)); i, strerror(-refcount1));
res->check_errors++; res->check_errors++;
continue; continue;
@ -1205,9 +1208,31 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res)
refcount2 = refcount_table[i]; refcount2 = refcount_table[i];
if (refcount1 != refcount2) { if (refcount1 != refcount2) {
fprintf(stderr, "%s cluster %d refcount=%d reference=%d\n",
refcount1 < refcount2 ? "ERROR" : "Leaked", /* Check if we're allowed to fix the mismatch */
int *num_fixed = NULL;
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;
}
fprintf(stderr, "%s cluster %" PRId64 " refcount=%d reference=%d\n",
num_fixed != NULL ? "Repairing" :
refcount1 < refcount2 ? "ERROR" :
"Leaked",
i, refcount1, refcount2); i, refcount1, refcount2);
if (num_fixed) {
ret = update_refcount(bs, i << s->cluster_bits, 1,
refcount2 - refcount1);
if (ret >= 0) {
(*num_fixed)++;
continue;
}
}
/* And if we couldn't, print an error */
if (refcount1 < refcount2) { if (refcount1 < refcount2) {
res->corruptions++; res->corruptions++;
} else { } else {

View File

@ -1473,11 +1473,7 @@ static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result, static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result,
BdrvCheckMode fix) BdrvCheckMode fix)
{ {
if (fix) { return qcow2_check_refcounts(bs, result, fix);
return -ENOTSUP;
}
return qcow2_check_refcounts(bs, result);
} }
#if 0 #if 0

View File

@ -261,7 +261,8 @@ void qcow2_free_any_clusters(BlockDriverState *bs,
int qcow2_update_snapshot_refcount(BlockDriverState *bs, int qcow2_update_snapshot_refcount(BlockDriverState *bs,
int64_t l1_table_offset, int l1_size, int addend); int64_t l1_table_offset, int l1_size, int addend);
int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res); int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
BdrvCheckMode fix);
/* qcow2-cluster.c functions */ /* qcow2-cluster.c functions */
int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size); int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size);