Commit e97fc193e1c65deb51643d5251e98affe07c59ca
1 parent
8ddbc04f
Introduce bdrv_check (Kevin Wolf)
From: Kevin Wolf <kwolf@redhat.com> Introduce a new bdrv_check function pointer for block drivers. Modify qcow2 to return an error status in check_refcounts(), so it can implement bdrv_check. Signed-off-by: Kevin Wolf <kwolf@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com> git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@7214 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
4 changed files
with
70 additions
and
19 deletions
block-qcow2.c
@@ -177,9 +177,7 @@ static int64_t alloc_clusters(BlockDriverState *bs, int64_t size); | @@ -177,9 +177,7 @@ static int64_t alloc_clusters(BlockDriverState *bs, int64_t size); | ||
177 | static int64_t alloc_bytes(BlockDriverState *bs, int size); | 177 | static int64_t alloc_bytes(BlockDriverState *bs, int size); |
178 | static void free_clusters(BlockDriverState *bs, | 178 | static void free_clusters(BlockDriverState *bs, |
179 | int64_t offset, int64_t size); | 179 | int64_t offset, int64_t size); |
180 | -#ifdef DEBUG_ALLOC | ||
181 | -static void check_refcounts(BlockDriverState *bs); | ||
182 | -#endif | 180 | +static int check_refcounts(BlockDriverState *bs); |
183 | 181 | ||
184 | static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename) | 182 | static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename) |
185 | { | 183 | { |
@@ -2564,8 +2562,14 @@ static void update_refcount(BlockDriverState *bs, | @@ -2564,8 +2562,14 @@ static void update_refcount(BlockDriverState *bs, | ||
2564 | } | 2562 | } |
2565 | } | 2563 | } |
2566 | 2564 | ||
2567 | -#ifdef DEBUG_ALLOC | ||
2568 | -static void inc_refcounts(BlockDriverState *bs, | 2565 | +/* |
2566 | + * Increases the refcount for a range of clusters in a given refcount table. | ||
2567 | + * This is used to construct a temporary refcount table out of L1 and L2 tables | ||
2568 | + * which can be compared the the refcount table saved in the image. | ||
2569 | + * | ||
2570 | + * Returns the number of errors in the image that were found | ||
2571 | + */ | ||
2572 | +static int inc_refcounts(BlockDriverState *bs, | ||
2569 | uint16_t *refcount_table, | 2573 | uint16_t *refcount_table, |
2570 | int refcount_table_size, | 2574 | int refcount_table_size, |
2571 | int64_t offset, int64_t size) | 2575 | int64_t offset, int64_t size) |
@@ -2573,9 +2577,10 @@ static void inc_refcounts(BlockDriverState *bs, | @@ -2573,9 +2577,10 @@ static void inc_refcounts(BlockDriverState *bs, | ||
2573 | BDRVQcowState *s = bs->opaque; | 2577 | BDRVQcowState *s = bs->opaque; |
2574 | int64_t start, last, cluster_offset; | 2578 | int64_t start, last, cluster_offset; |
2575 | int k; | 2579 | int k; |
2580 | + int errors = 0; | ||
2576 | 2581 | ||
2577 | if (size <= 0) | 2582 | if (size <= 0) |
2578 | - return; | 2583 | + return 0; |
2579 | 2584 | ||
2580 | start = offset & ~(s->cluster_size - 1); | 2585 | start = offset & ~(s->cluster_size - 1); |
2581 | last = (offset + size - 1) & ~(s->cluster_size - 1); | 2586 | last = (offset + size - 1) & ~(s->cluster_size - 1); |
@@ -2585,13 +2590,17 @@ static void inc_refcounts(BlockDriverState *bs, | @@ -2585,13 +2590,17 @@ static void inc_refcounts(BlockDriverState *bs, | ||
2585 | if (k < 0 || k >= refcount_table_size) { | 2590 | if (k < 0 || k >= refcount_table_size) { |
2586 | fprintf(stderr, "ERROR: invalid cluster offset=0x%" PRIx64 "\n", | 2591 | fprintf(stderr, "ERROR: invalid cluster offset=0x%" PRIx64 "\n", |
2587 | cluster_offset); | 2592 | cluster_offset); |
2593 | + errors++; | ||
2588 | } else { | 2594 | } else { |
2589 | if (++refcount_table[k] == 0) { | 2595 | if (++refcount_table[k] == 0) { |
2590 | fprintf(stderr, "ERROR: overflow cluster offset=0x%" PRIx64 | 2596 | fprintf(stderr, "ERROR: overflow cluster offset=0x%" PRIx64 |
2591 | "\n", cluster_offset); | 2597 | "\n", cluster_offset); |
2598 | + errors++; | ||
2592 | } | 2599 | } |
2593 | } | 2600 | } |
2594 | } | 2601 | } |
2602 | + | ||
2603 | + return errors; | ||
2595 | } | 2604 | } |
2596 | 2605 | ||
2597 | static int check_refcounts_l1(BlockDriverState *bs, | 2606 | static int check_refcounts_l1(BlockDriverState *bs, |
@@ -2603,11 +2612,12 @@ static int check_refcounts_l1(BlockDriverState *bs, | @@ -2603,11 +2612,12 @@ static int check_refcounts_l1(BlockDriverState *bs, | ||
2603 | BDRVQcowState *s = bs->opaque; | 2612 | BDRVQcowState *s = bs->opaque; |
2604 | uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2; | 2613 | uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2; |
2605 | int l2_size, i, j, nb_csectors, refcount; | 2614 | int l2_size, i, j, nb_csectors, refcount; |
2615 | + int errors = 0; | ||
2606 | 2616 | ||
2607 | l2_table = NULL; | 2617 | l2_table = NULL; |
2608 | l1_size2 = l1_size * sizeof(uint64_t); | 2618 | l1_size2 = l1_size * sizeof(uint64_t); |
2609 | 2619 | ||
2610 | - inc_refcounts(bs, refcount_table, refcount_table_size, | 2620 | + errors += inc_refcounts(bs, refcount_table, refcount_table_size, |
2611 | l1_table_offset, l1_size2); | 2621 | l1_table_offset, l1_size2); |
2612 | 2622 | ||
2613 | l1_table = qemu_malloc(l1_size2); | 2623 | l1_table = qemu_malloc(l1_size2); |
@@ -2627,6 +2637,7 @@ static int check_refcounts_l1(BlockDriverState *bs, | @@ -2627,6 +2637,7 @@ static int check_refcounts_l1(BlockDriverState *bs, | ||
2627 | if ((refcount == 1) != ((l2_offset & QCOW_OFLAG_COPIED) != 0)) { | 2637 | if ((refcount == 1) != ((l2_offset & QCOW_OFLAG_COPIED) != 0)) { |
2628 | fprintf(stderr, "ERROR OFLAG_COPIED: l2_offset=%" PRIx64 | 2638 | fprintf(stderr, "ERROR OFLAG_COPIED: l2_offset=%" PRIx64 |
2629 | " refcount=%d\n", l2_offset, refcount); | 2639 | " refcount=%d\n", l2_offset, refcount); |
2640 | + errors++; | ||
2630 | } | 2641 | } |
2631 | } | 2642 | } |
2632 | l2_offset &= ~QCOW_OFLAG_COPIED; | 2643 | l2_offset &= ~QCOW_OFLAG_COPIED; |
@@ -2641,11 +2652,12 @@ static int check_refcounts_l1(BlockDriverState *bs, | @@ -2641,11 +2652,12 @@ static int check_refcounts_l1(BlockDriverState *bs, | ||
2641 | "copied flag must never be set for compressed " | 2652 | "copied flag must never be set for compressed " |
2642 | "clusters\n", offset >> s->cluster_bits); | 2653 | "clusters\n", offset >> s->cluster_bits); |
2643 | offset &= ~QCOW_OFLAG_COPIED; | 2654 | offset &= ~QCOW_OFLAG_COPIED; |
2655 | + errors++; | ||
2644 | } | 2656 | } |
2645 | nb_csectors = ((offset >> s->csize_shift) & | 2657 | nb_csectors = ((offset >> s->csize_shift) & |
2646 | s->csize_mask) + 1; | 2658 | s->csize_mask) + 1; |
2647 | offset &= s->cluster_offset_mask; | 2659 | offset &= s->cluster_offset_mask; |
2648 | - inc_refcounts(bs, refcount_table, | 2660 | + errors += inc_refcounts(bs, refcount_table, |
2649 | refcount_table_size, | 2661 | refcount_table_size, |
2650 | offset & ~511, nb_csectors * 512); | 2662 | offset & ~511, nb_csectors * 512); |
2651 | } else { | 2663 | } else { |
@@ -2654,16 +2666,17 @@ static int check_refcounts_l1(BlockDriverState *bs, | @@ -2654,16 +2666,17 @@ static int check_refcounts_l1(BlockDriverState *bs, | ||
2654 | if ((refcount == 1) != ((offset & QCOW_OFLAG_COPIED) != 0)) { | 2666 | if ((refcount == 1) != ((offset & QCOW_OFLAG_COPIED) != 0)) { |
2655 | fprintf(stderr, "ERROR OFLAG_COPIED: offset=%" | 2667 | fprintf(stderr, "ERROR OFLAG_COPIED: offset=%" |
2656 | PRIx64 " refcount=%d\n", offset, refcount); | 2668 | PRIx64 " refcount=%d\n", offset, refcount); |
2669 | + errors++; | ||
2657 | } | 2670 | } |
2658 | } | 2671 | } |
2659 | offset &= ~QCOW_OFLAG_COPIED; | 2672 | offset &= ~QCOW_OFLAG_COPIED; |
2660 | - inc_refcounts(bs, refcount_table, | 2673 | + errors += inc_refcounts(bs, refcount_table, |
2661 | refcount_table_size, | 2674 | refcount_table_size, |
2662 | offset, s->cluster_size); | 2675 | offset, s->cluster_size); |
2663 | } | 2676 | } |
2664 | } | 2677 | } |
2665 | } | 2678 | } |
2666 | - inc_refcounts(bs, refcount_table, | 2679 | + errors += inc_refcounts(bs, refcount_table, |
2667 | refcount_table_size, | 2680 | refcount_table_size, |
2668 | l2_offset, | 2681 | l2_offset, |
2669 | s->cluster_size); | 2682 | s->cluster_size); |
@@ -2671,7 +2684,7 @@ static int check_refcounts_l1(BlockDriverState *bs, | @@ -2671,7 +2684,7 @@ static int check_refcounts_l1(BlockDriverState *bs, | ||
2671 | } | 2684 | } |
2672 | qemu_free(l1_table); | 2685 | qemu_free(l1_table); |
2673 | qemu_free(l2_table); | 2686 | qemu_free(l2_table); |
2674 | - return 0; | 2687 | + return errors; |
2675 | fail: | 2688 | fail: |
2676 | fprintf(stderr, "ERROR: I/O error in check_refcounts_l1\n"); | 2689 | fprintf(stderr, "ERROR: I/O error in check_refcounts_l1\n"); |
2677 | qemu_free(l1_table); | 2690 | qemu_free(l1_table); |
@@ -2679,24 +2692,35 @@ static int check_refcounts_l1(BlockDriverState *bs, | @@ -2679,24 +2692,35 @@ static int check_refcounts_l1(BlockDriverState *bs, | ||
2679 | return -EIO; | 2692 | return -EIO; |
2680 | } | 2693 | } |
2681 | 2694 | ||
2682 | -static void check_refcounts(BlockDriverState *bs) | 2695 | +/* |
2696 | + * Checks an image for refcount consistency. | ||
2697 | + * | ||
2698 | + * Returns 0 if no errors are found, the number of errors in case the image is | ||
2699 | + * detected as corrupted, and -errno when an internal error occured. | ||
2700 | + */ | ||
2701 | +static int check_refcounts(BlockDriverState *bs) | ||
2683 | { | 2702 | { |
2684 | BDRVQcowState *s = bs->opaque; | 2703 | BDRVQcowState *s = bs->opaque; |
2685 | int64_t size; | 2704 | int64_t size; |
2686 | int nb_clusters, refcount1, refcount2, i; | 2705 | int nb_clusters, refcount1, refcount2, i; |
2687 | QCowSnapshot *sn; | 2706 | QCowSnapshot *sn; |
2688 | uint16_t *refcount_table; | 2707 | uint16_t *refcount_table; |
2708 | + int ret, errors = 0; | ||
2689 | 2709 | ||
2690 | size = bdrv_getlength(s->hd); | 2710 | size = bdrv_getlength(s->hd); |
2691 | nb_clusters = size_to_clusters(s, size); | 2711 | nb_clusters = size_to_clusters(s, size); |
2692 | refcount_table = qemu_mallocz(nb_clusters * sizeof(uint16_t)); | 2712 | refcount_table = qemu_mallocz(nb_clusters * sizeof(uint16_t)); |
2693 | 2713 | ||
2694 | /* header */ | 2714 | /* header */ |
2695 | - inc_refcounts(bs, refcount_table, nb_clusters, | 2715 | + errors += inc_refcounts(bs, refcount_table, nb_clusters, |
2696 | 0, s->cluster_size); | 2716 | 0, s->cluster_size); |
2697 | 2717 | ||
2698 | - check_refcounts_l1(bs, refcount_table, nb_clusters, | 2718 | + ret = check_refcounts_l1(bs, refcount_table, nb_clusters, |
2699 | s->l1_table_offset, s->l1_size, 1); | 2719 | s->l1_table_offset, s->l1_size, 1); |
2720 | + if (ret < 0) { | ||
2721 | + return ret; | ||
2722 | + } | ||
2723 | + errors += ret; | ||
2700 | 2724 | ||
2701 | /* snapshots */ | 2725 | /* snapshots */ |
2702 | for(i = 0; i < s->nb_snapshots; i++) { | 2726 | for(i = 0; i < s->nb_snapshots; i++) { |
@@ -2704,18 +2728,18 @@ static void check_refcounts(BlockDriverState *bs) | @@ -2704,18 +2728,18 @@ static void check_refcounts(BlockDriverState *bs) | ||
2704 | check_refcounts_l1(bs, refcount_table, nb_clusters, | 2728 | check_refcounts_l1(bs, refcount_table, nb_clusters, |
2705 | sn->l1_table_offset, sn->l1_size, 0); | 2729 | sn->l1_table_offset, sn->l1_size, 0); |
2706 | } | 2730 | } |
2707 | - inc_refcounts(bs, refcount_table, nb_clusters, | 2731 | + errors += inc_refcounts(bs, refcount_table, nb_clusters, |
2708 | s->snapshots_offset, s->snapshots_size); | 2732 | s->snapshots_offset, s->snapshots_size); |
2709 | 2733 | ||
2710 | /* refcount data */ | 2734 | /* refcount data */ |
2711 | - inc_refcounts(bs, refcount_table, nb_clusters, | 2735 | + errors += inc_refcounts(bs, refcount_table, nb_clusters, |
2712 | s->refcount_table_offset, | 2736 | s->refcount_table_offset, |
2713 | s->refcount_table_size * sizeof(uint64_t)); | 2737 | s->refcount_table_size * sizeof(uint64_t)); |
2714 | for(i = 0; i < s->refcount_table_size; i++) { | 2738 | for(i = 0; i < s->refcount_table_size; i++) { |
2715 | int64_t offset; | 2739 | int64_t offset; |
2716 | offset = s->refcount_table[i]; | 2740 | offset = s->refcount_table[i]; |
2717 | if (offset != 0) { | 2741 | if (offset != 0) { |
2718 | - inc_refcounts(bs, refcount_table, nb_clusters, | 2742 | + errors += inc_refcounts(bs, refcount_table, nb_clusters, |
2719 | offset, s->cluster_size); | 2743 | offset, s->cluster_size); |
2720 | } | 2744 | } |
2721 | } | 2745 | } |
@@ -2724,12 +2748,21 @@ static void check_refcounts(BlockDriverState *bs) | @@ -2724,12 +2748,21 @@ static void check_refcounts(BlockDriverState *bs) | ||
2724 | for(i = 0; i < nb_clusters; i++) { | 2748 | for(i = 0; i < nb_clusters; i++) { |
2725 | refcount1 = get_refcount(bs, i); | 2749 | refcount1 = get_refcount(bs, i); |
2726 | refcount2 = refcount_table[i]; | 2750 | refcount2 = refcount_table[i]; |
2727 | - if (refcount1 != refcount2) | 2751 | + if (refcount1 != refcount2) { |
2728 | fprintf(stderr, "ERROR cluster %d refcount=%d reference=%d\n", | 2752 | fprintf(stderr, "ERROR cluster %d refcount=%d reference=%d\n", |
2729 | i, refcount1, refcount2); | 2753 | i, refcount1, refcount2); |
2754 | + errors++; | ||
2755 | + } | ||
2730 | } | 2756 | } |
2731 | 2757 | ||
2732 | qemu_free(refcount_table); | 2758 | qemu_free(refcount_table); |
2759 | + | ||
2760 | + return errors; | ||
2761 | +} | ||
2762 | + | ||
2763 | +static int qcow_check(BlockDriverState *bs) | ||
2764 | +{ | ||
2765 | + return check_refcounts(bs); | ||
2733 | } | 2766 | } |
2734 | 2767 | ||
2735 | #if 0 | 2768 | #if 0 |
@@ -2751,7 +2784,6 @@ static void dump_refcounts(BlockDriverState *bs) | @@ -2751,7 +2784,6 @@ static void dump_refcounts(BlockDriverState *bs) | ||
2751 | } | 2784 | } |
2752 | } | 2785 | } |
2753 | #endif | 2786 | #endif |
2754 | -#endif | ||
2755 | 2787 | ||
2756 | static int qcow_put_buffer(BlockDriverState *bs, const uint8_t *buf, | 2788 | static int qcow_put_buffer(BlockDriverState *bs, const uint8_t *buf, |
2757 | int64_t pos, int size) | 2789 | int64_t pos, int size) |
@@ -2806,4 +2838,5 @@ BlockDriver bdrv_qcow2 = { | @@ -2806,4 +2838,5 @@ BlockDriver bdrv_qcow2 = { | ||
2806 | .bdrv_get_buffer = qcow_get_buffer, | 2838 | .bdrv_get_buffer = qcow_get_buffer, |
2807 | 2839 | ||
2808 | .bdrv_create2 = qcow_create2, | 2840 | .bdrv_create2 = qcow_create2, |
2841 | + .bdrv_check = qcow_check, | ||
2809 | }; | 2842 | }; |
block.c
@@ -506,6 +506,20 @@ void bdrv_delete(BlockDriverState *bs) | @@ -506,6 +506,20 @@ void bdrv_delete(BlockDriverState *bs) | ||
506 | qemu_free(bs); | 506 | qemu_free(bs); |
507 | } | 507 | } |
508 | 508 | ||
509 | +/* | ||
510 | + * Run consistency checks on an image | ||
511 | + * | ||
512 | + * Returns the number of errors or -errno when an internal error occurs | ||
513 | + */ | ||
514 | +int bdrv_check(BlockDriverState *bs) | ||
515 | +{ | ||
516 | + if (bs->drv->bdrv_check == NULL) { | ||
517 | + return -ENOTSUP; | ||
518 | + } | ||
519 | + | ||
520 | + return bs->drv->bdrv_check(bs); | ||
521 | +} | ||
522 | + | ||
509 | /* commit COW file into the raw image */ | 523 | /* commit COW file into the raw image */ |
510 | int bdrv_commit(BlockDriverState *bs) | 524 | int bdrv_commit(BlockDriverState *bs) |
511 | { | 525 | { |
block.h
@@ -73,6 +73,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags); | @@ -73,6 +73,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags); | ||
73 | int bdrv_open2(BlockDriverState *bs, const char *filename, int flags, | 73 | int bdrv_open2(BlockDriverState *bs, const char *filename, int flags, |
74 | BlockDriver *drv); | 74 | BlockDriver *drv); |
75 | void bdrv_close(BlockDriverState *bs); | 75 | void bdrv_close(BlockDriverState *bs); |
76 | +int bdrv_check(BlockDriverState *bs); | ||
76 | int bdrv_read(BlockDriverState *bs, int64_t sector_num, | 77 | int bdrv_read(BlockDriverState *bs, int64_t sector_num, |
77 | uint8_t *buf, int nb_sectors); | 78 | uint8_t *buf, int nb_sectors); |
78 | int bdrv_write(BlockDriverState *bs, int64_t sector_num, | 79 | int bdrv_write(BlockDriverState *bs, int64_t sector_num, |
block_int.h
@@ -102,6 +102,9 @@ struct BlockDriver { | @@ -102,6 +102,9 @@ struct BlockDriver { | ||
102 | const char *backing_file, const char *backing_format, | 102 | const char *backing_file, const char *backing_format, |
103 | int flags); | 103 | int flags); |
104 | 104 | ||
105 | + /* Returns number of errors in image, -errno for internal errors */ | ||
106 | + int (*bdrv_check)(BlockDriverState* bs); | ||
107 | + | ||
105 | struct BlockDriver *next; | 108 | struct BlockDriver *next; |
106 | }; | 109 | }; |
107 | 110 |