Signed-off-by: Benoit Canet <ben...@irqsave.net> --- block/qcow2-refcount.c | 65 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 9 deletions(-)
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index f7a283a..3077a9f 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -1047,6 +1047,43 @@ fail: return -EIO; } +static int check_dedup_l2(BlockDriverState *bs, BdrvCheckResult *res, + int64_t l2_offset) +{ + BDRVQcowState *s = bs->opaque; + uint64_t *l2_table; + int i, l2_size; + + /* Read L2 table from disk */ + l2_size = s->cluster_size; + l2_table = g_malloc(l2_size); + + if (bdrv_pread(bs->file, l2_offset, l2_table, l2_size) != l2_size) { + goto fail; + } + + /* Do the actual checks */ + for (i = 0; i < (s->l2_size - 5); i += 5) { + uint64_t first_logical_offset = be64_to_cpu(l2_table[i + 4]) & + ~QCOW_FLAG_FIRST; + if (first_logical_offset > (bs->total_sectors * BDRV_SECTOR_SIZE)) { + fprintf(stderr, "ERROR: l2 deduplication first_logical_offset" + "=%" PRIi64 " outside of deduplicated volume in l2 table " + "with offset %" PRIi64 ".\n", first_logical_offset, + l2_offset); + res->corruptions++; + } + } + + g_free(l2_table); + return 0; + +fail: + fprintf(stderr, "ERROR: I/O error in check_dedup_l2\n"); + g_free(l2_table); + return -EIO; +} + /* * Increases the refcount for the L1 table, its L2 tables and all referenced * clusters in the given refcount table. While doing so, performs some checks @@ -1060,7 +1097,8 @@ static int check_refcounts_l1(BlockDriverState *bs, uint16_t *refcount_table, int refcount_table_size, int64_t l1_table_offset, int l1_size, - int check_copied) + int check_copied, + bool dedup) { BDRVQcowState *s = bs->opaque; uint64_t *l1_table, l2_offset, l1_size2; @@ -1116,11 +1154,19 @@ static int check_refcounts_l1(BlockDriverState *bs, res->corruptions++; } - /* Process and check L2 entries */ - ret = check_refcounts_l2(bs, res, refcount_table, - refcount_table_size, l2_offset, check_copied); - if (ret < 0) { - goto fail; + if (dedup) { + /* Process and check dedup l2 entries */ + ret = check_dedup_l2(bs, res, l2_offset); + if (ret < 0) { + goto fail; + } + } else { + /* Process and check L2 entries */ + ret = check_refcounts_l2(bs, res, refcount_table, + refcount_table_size, l2_offset, check_copied); + if (ret < 0) { + goto fail; + } } } } @@ -1160,14 +1206,15 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, /* current L1 table */ ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters, - s->l1_table_offset, s->l1_size, 1); + s->l1_table_offset, s->l1_size, 1, false); if (ret < 0) { goto fail; } if (s->has_dedup) { ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters, - s->dedup_table_offset, s->dedup_table_size, 0); + s->dedup_table_offset, s->dedup_table_size, + 0, true); if (ret < 0) { goto fail; } @@ -1177,7 +1224,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, 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, false); if (ret < 0) { goto fail; } -- 1.7.10.4