From 6e16e19476a1677f748aabd82ff438a93ab56879 Mon Sep 17 00:00:00 2001 From: Kevin Wolf <kwolf@redhat.com> Date: Thu, 8 Jul 2010 15:34:08 -0300 Subject: [PATCH 4/4] qcow2: Change check to distinguish error cases RH-Author: Kevin Wolf <kwolf@redhat.com> Message-id: <patch-10577-clone-for-rhel55-rhel55> Patchwork-id: 10580 O-Subject: [RHEL-5.6 KVM PATCH 2/2] qcow2: Change check to distinguish error cases Bugzilla: 618206 RH-Acked-by: Jes Sorensen <Jes.Sorensen@redhat.com> RH-Acked-by: Christoph Hellwig <chellwig@redhat.com> RH-Acked-by: Juan Quintela <quintela@redhat.com> Bugzilla: 606651 Upstream commit: 9ac228e02cf16202547e7025ef300369e0db7781 This distinguishes between harmless leaks and real corruption. Hopefully users better understand what qemu-img check wants to tell them. Signed-off-by: Kevin Wolf <kwolf@redhat.com> --- qemu/block-qcow2.c | 89 +++++++++++++++++++++++++++++----------------------- qemu/block.c | 3 +- qemu/block_int.h | 7 +++- 3 files changed, 56 insertions(+), 43 deletions(-) Signed-off-by: Eduardo Habkost <ehabkost@redhat.com> --- qemu/block-qcow2.c | 89 +++++++++++++++++++++++++++++----------------------- qemu/block.c | 3 +- qemu/block_int.h | 7 +++- 3 files changed, 56 insertions(+), 43 deletions(-) diff --git a/qemu/block-qcow2.c b/qemu/block-qcow2.c index 913679f..9501ea1 100644 --- a/qemu/block-qcow2.c +++ b/qemu/block-qcow2.c @@ -181,7 +181,7 @@ static int64_t alloc_clusters(BlockDriverState *bs, int64_t size); static int64_t alloc_bytes(BlockDriverState *bs, int size); static void free_clusters(BlockDriverState *bs, int64_t offset, int64_t size); -static int check_refcounts(BlockDriverState *bs); +static int check_refcounts(BlockDriverState *bs, BdrvCheckResult *res); static inline int64_t align_offset(int64_t offset, int n) { @@ -3164,9 +3164,10 @@ fail: * This is used to construct a temporary refcount table out of L1 and L2 tables * which can be compared the the refcount table saved in the image. * - * Returns the number of errors in the image that were found + * Modifies the number of errors in res. */ -static int inc_refcounts(BlockDriverState *bs, +static void inc_refcounts(BlockDriverState *bs, + BdrvCheckResult *res, uint16_t *refcount_table, int refcount_table_size, int64_t offset, int64_t size) @@ -3174,33 +3175,35 @@ static int inc_refcounts(BlockDriverState *bs, BDRVQcowState *s = bs->opaque; int64_t start, last, cluster_offset; int k; - int errors = 0; if (size <= 0) - return 0; + return; start = offset & ~(s->cluster_size - 1); last = (offset + size - 1) & ~(s->cluster_size - 1); for(cluster_offset = start; cluster_offset <= last; cluster_offset += s->cluster_size) { k = cluster_offset >> s->cluster_bits; - if (k < 0 || k >= refcount_table_size) { + if (k < 0) { fprintf(stderr, "ERROR: invalid cluster offset=0x%" PRIx64 "\n", cluster_offset); - errors++; + res->corruptions++; + } else 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); - errors++; + res->corruptions++; } } } - - return errors; } -static int check_refcounts_l1(BlockDriverState *bs, +static int check_refcounts_l1(BlockDriverState *bs, BdrvCheckResult *res, uint16_t *refcount_table, int refcount_table_size, int64_t l1_table_offset, int l1_size, @@ -3209,12 +3212,11 @@ static int check_refcounts_l1(BlockDriverState *bs, BDRVQcowState *s = bs->opaque; uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2; int l2_size, i, j, nb_csectors, refcount; - int errors = 0; l2_table = NULL; l1_size2 = l1_size * sizeof(uint64_t); - errors += inc_refcounts(bs, refcount_table, refcount_table_size, + inc_refcounts(bs, res, refcount_table, refcount_table_size, l1_table_offset, l1_size2); l1_table = qemu_malloc(l1_size2); @@ -3242,7 +3244,7 @@ static int check_refcounts_l1(BlockDriverState *bs, if ((refcount == 1) != ((l2_offset & QCOW_OFLAG_COPIED) != 0)) { fprintf(stderr, "ERROR OFLAG_COPIED: l2_offset=%" PRIx64 " refcount=%d\n", l2_offset, refcount); - errors++; + res->corruptions++; } } l2_offset &= ~QCOW_OFLAG_COPIED; @@ -3257,12 +3259,12 @@ static int check_refcounts_l1(BlockDriverState *bs, "copied flag must never be set for compressed " "clusters\n", offset >> s->cluster_bits); offset &= ~QCOW_OFLAG_COPIED; - errors++; + res->corruptions++; } nb_csectors = ((offset >> s->csize_shift) & s->csize_mask) + 1; offset &= s->cluster_offset_mask; - errors += inc_refcounts(bs, refcount_table, + inc_refcounts(bs, res, refcount_table, refcount_table_size, offset & ~511, nb_csectors * 512); } else { @@ -3275,11 +3277,11 @@ static int check_refcounts_l1(BlockDriverState *bs, if ((refcount == 1) != ((offset & QCOW_OFLAG_COPIED) != 0)) { fprintf(stderr, "ERROR OFLAG_COPIED: offset=%" PRIx64 " refcount=%d\n", offset, refcount); - errors++; + res->corruptions++; } } offset &= ~QCOW_OFLAG_COPIED; - errors += inc_refcounts(bs, refcount_table, + inc_refcounts(bs, res, refcount_table, refcount_table_size, offset, s->cluster_size); @@ -3287,12 +3289,12 @@ static int check_refcounts_l1(BlockDriverState *bs, if (offset & (s->cluster_size - 1)) { fprintf(stderr, "ERROR offset=%" PRIx64 ": Cluster is not " "properly aligned; L2 entry corrupted.\n", offset); - errors++; + res->corruptions++; } } } } - errors += inc_refcounts(bs, refcount_table, + inc_refcounts(bs, res, refcount_table, refcount_table_size, l2_offset, s->cluster_size); @@ -3301,16 +3303,17 @@ static int check_refcounts_l1(BlockDriverState *bs, if (l2_offset & (s->cluster_size - 1)) { fprintf(stderr, "ERROR l2_offset=%" PRIx64 ": Table is not " "cluster aligned; L1 entry corrupted\n", l2_offset); - errors++; + res->corruptions++; } } } qemu_free(l1_table); qemu_free(l2_table); - return errors; + return 0; fail: fprintf(stderr, "ERROR: I/O error in check_refcounts_l1\n"); + res->check_errors++; qemu_free(l1_table); qemu_free(l2_table); return -EIO; @@ -3322,41 +3325,43 @@ static int check_refcounts_l1(BlockDriverState *bs, * 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 occured. */ -static int check_refcounts(BlockDriverState *bs) +static int check_refcounts(BlockDriverState *bs, BdrvCheckResult *res) { BDRVQcowState *s = bs->opaque; int64_t size; int nb_clusters, refcount1, refcount2, i; QCowSnapshot *sn; uint16_t *refcount_table; - int ret, errors = 0; + int ret; size = bdrv_getlength(s->hd); nb_clusters = size_to_clusters(s, size); refcount_table = qemu_mallocz(nb_clusters * sizeof(uint16_t)); /* header */ - errors += inc_refcounts(bs, refcount_table, nb_clusters, + inc_refcounts(bs, res, refcount_table, nb_clusters, 0, s->cluster_size); - ret = check_refcounts_l1(bs, refcount_table, nb_clusters, + ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters, s->l1_table_offset, s->l1_size, 1); if (ret < 0) { return ret; } - errors += ret; /* snapshots */ for(i = 0; i < s->nb_snapshots; i++) { sn = s->snapshots + i; - check_refcounts_l1(bs, refcount_table, nb_clusters, - sn->l1_table_offset, sn->l1_size, 0); + ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters, + sn->l1_table_offset, sn->l1_size, 0); + if (ret < 0) { + return ret; + } } - errors += inc_refcounts(bs, refcount_table, nb_clusters, + inc_refcounts(bs, res, refcount_table, nb_clusters, s->snapshots_offset, s->snapshots_size); /* refcount data */ - errors += inc_refcounts(bs, refcount_table, nb_clusters, + 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++) { @@ -3368,22 +3373,23 @@ static int check_refcounts(BlockDriverState *bs) if (offset & (s->cluster_size - 1)) { fprintf(stderr, "ERROR refcount block %d is not " "cluster aligned; refcount table entry corrupted\n", i); - errors++; + res->corruptions++; continue; } if (cluster >= nb_clusters) { fprintf(stderr, "ERROR refcount block %d is outside image\n", i); - errors++; + res->corruptions++; continue; } if (offset != 0) { - errors += inc_refcounts(bs, refcount_table, nb_clusters, + inc_refcounts(bs, res, refcount_table, nb_clusters, offset, s->cluster_size); if (refcount_table[cluster] != 1) { fprintf(stderr, "ERROR refcount block %d refcount=%d\n", i, refcount_table[cluster]); + res->corruptions++; } } } @@ -3399,20 +3405,25 @@ static int check_refcounts(BlockDriverState *bs) refcount2 = refcount_table[i]; if (refcount1 != refcount2) { - fprintf(stderr, "ERROR cluster %d refcount=%d reference=%d\n", + fprintf(stderr, "%s cluster %d refcount=%d reference=%d\n", + refcount1 < refcount2 ? "ERROR" : "Leaked", i, refcount1, refcount2); - errors++; + if (refcount1 < refcount2) { + res->corruptions++; + } else { + res->leaks++; + } } } qemu_free(refcount_table); - return errors; + return 0; } -static int qcow_check(BlockDriverState *bs) +static int qcow_check(BlockDriverState *bs, BdrvCheckResult *result) { - return check_refcounts(bs); + return check_refcounts(bs, result); } #if 0 diff --git a/qemu/block.c b/qemu/block.c index 9ed7b91..585204e 100644 --- a/qemu/block.c +++ b/qemu/block.c @@ -600,8 +600,7 @@ int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res) } memset(res, 0, sizeof(*res)); - res->corruptions = bs->drv->bdrv_check(bs); - return res->corruptions < 0 ? res->corruptions : 0; + return bs->drv->bdrv_check(bs, res); } /* commit COW file into the raw image */ diff --git a/qemu/block_int.h b/qemu/block_int.h index 43a4e6a..dbb5534 100644 --- a/qemu/block_int.h +++ b/qemu/block_int.h @@ -102,8 +102,11 @@ struct BlockDriver { const char *backing_file, const char *backing_format, int flags); - /* Returns number of errors in image, -errno for internal errors */ - int (*bdrv_check)(BlockDriverState* bs); + /* + * Returns 0 for completed check, -errno for internal errors. + * The check results are stored in result. + */ + int (*bdrv_check)(BlockDriverState* bs, BdrvCheckResult *result); struct BlockDriver *next; }; -- 1.7.0.3