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 | 177 | static int64_t alloc_bytes(BlockDriverState *bs, int size); |
| 178 | 178 | static void free_clusters(BlockDriverState *bs, |
| 179 | 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 | 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 | 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 | 2573 | uint16_t *refcount_table, |
| 2570 | 2574 | int refcount_table_size, |
| 2571 | 2575 | int64_t offset, int64_t size) |
| ... | ... | @@ -2573,9 +2577,10 @@ static void inc_refcounts(BlockDriverState *bs, |
| 2573 | 2577 | BDRVQcowState *s = bs->opaque; |
| 2574 | 2578 | int64_t start, last, cluster_offset; |
| 2575 | 2579 | int k; |
| 2580 | + int errors = 0; | |
| 2576 | 2581 | |
| 2577 | 2582 | if (size <= 0) |
| 2578 | - return; | |
| 2583 | + return 0; | |
| 2579 | 2584 | |
| 2580 | 2585 | start = offset & ~(s->cluster_size - 1); |
| 2581 | 2586 | last = (offset + size - 1) & ~(s->cluster_size - 1); |
| ... | ... | @@ -2585,13 +2590,17 @@ static void inc_refcounts(BlockDriverState *bs, |
| 2585 | 2590 | if (k < 0 || k >= refcount_table_size) { |
| 2586 | 2591 | fprintf(stderr, "ERROR: invalid cluster offset=0x%" PRIx64 "\n", |
| 2587 | 2592 | cluster_offset); |
| 2593 | + errors++; | |
| 2588 | 2594 | } else { |
| 2589 | 2595 | if (++refcount_table[k] == 0) { |
| 2590 | 2596 | fprintf(stderr, "ERROR: overflow cluster offset=0x%" PRIx64 |
| 2591 | 2597 | "\n", cluster_offset); |
| 2598 | + errors++; | |
| 2592 | 2599 | } |
| 2593 | 2600 | } |
| 2594 | 2601 | } |
| 2602 | + | |
| 2603 | + return errors; | |
| 2595 | 2604 | } |
| 2596 | 2605 | |
| 2597 | 2606 | static int check_refcounts_l1(BlockDriverState *bs, |
| ... | ... | @@ -2603,11 +2612,12 @@ static int check_refcounts_l1(BlockDriverState *bs, |
| 2603 | 2612 | BDRVQcowState *s = bs->opaque; |
| 2604 | 2613 | uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2; |
| 2605 | 2614 | int l2_size, i, j, nb_csectors, refcount; |
| 2615 | + int errors = 0; | |
| 2606 | 2616 | |
| 2607 | 2617 | l2_table = NULL; |
| 2608 | 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 | 2621 | l1_table_offset, l1_size2); |
| 2612 | 2622 | |
| 2613 | 2623 | l1_table = qemu_malloc(l1_size2); |
| ... | ... | @@ -2627,6 +2637,7 @@ static int check_refcounts_l1(BlockDriverState *bs, |
| 2627 | 2637 | if ((refcount == 1) != ((l2_offset & QCOW_OFLAG_COPIED) != 0)) { |
| 2628 | 2638 | fprintf(stderr, "ERROR OFLAG_COPIED: l2_offset=%" PRIx64 |
| 2629 | 2639 | " refcount=%d\n", l2_offset, refcount); |
| 2640 | + errors++; | |
| 2630 | 2641 | } |
| 2631 | 2642 | } |
| 2632 | 2643 | l2_offset &= ~QCOW_OFLAG_COPIED; |
| ... | ... | @@ -2641,11 +2652,12 @@ static int check_refcounts_l1(BlockDriverState *bs, |
| 2641 | 2652 | "copied flag must never be set for compressed " |
| 2642 | 2653 | "clusters\n", offset >> s->cluster_bits); |
| 2643 | 2654 | offset &= ~QCOW_OFLAG_COPIED; |
| 2655 | + errors++; | |
| 2644 | 2656 | } |
| 2645 | 2657 | nb_csectors = ((offset >> s->csize_shift) & |
| 2646 | 2658 | s->csize_mask) + 1; |
| 2647 | 2659 | offset &= s->cluster_offset_mask; |
| 2648 | - inc_refcounts(bs, refcount_table, | |
| 2660 | + errors += inc_refcounts(bs, refcount_table, | |
| 2649 | 2661 | refcount_table_size, |
| 2650 | 2662 | offset & ~511, nb_csectors * 512); |
| 2651 | 2663 | } else { |
| ... | ... | @@ -2654,16 +2666,17 @@ static int check_refcounts_l1(BlockDriverState *bs, |
| 2654 | 2666 | if ((refcount == 1) != ((offset & QCOW_OFLAG_COPIED) != 0)) { |
| 2655 | 2667 | fprintf(stderr, "ERROR OFLAG_COPIED: offset=%" |
| 2656 | 2668 | PRIx64 " refcount=%d\n", offset, refcount); |
| 2669 | + errors++; | |
| 2657 | 2670 | } |
| 2658 | 2671 | } |
| 2659 | 2672 | offset &= ~QCOW_OFLAG_COPIED; |
| 2660 | - inc_refcounts(bs, refcount_table, | |
| 2673 | + errors += inc_refcounts(bs, refcount_table, | |
| 2661 | 2674 | refcount_table_size, |
| 2662 | 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 | 2680 | refcount_table_size, |
| 2668 | 2681 | l2_offset, |
| 2669 | 2682 | s->cluster_size); |
| ... | ... | @@ -2671,7 +2684,7 @@ static int check_refcounts_l1(BlockDriverState *bs, |
| 2671 | 2684 | } |
| 2672 | 2685 | qemu_free(l1_table); |
| 2673 | 2686 | qemu_free(l2_table); |
| 2674 | - return 0; | |
| 2687 | + return errors; | |
| 2675 | 2688 | fail: |
| 2676 | 2689 | fprintf(stderr, "ERROR: I/O error in check_refcounts_l1\n"); |
| 2677 | 2690 | qemu_free(l1_table); |
| ... | ... | @@ -2679,24 +2692,35 @@ static int check_refcounts_l1(BlockDriverState *bs, |
| 2679 | 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 | 2703 | BDRVQcowState *s = bs->opaque; |
| 2685 | 2704 | int64_t size; |
| 2686 | 2705 | int nb_clusters, refcount1, refcount2, i; |
| 2687 | 2706 | QCowSnapshot *sn; |
| 2688 | 2707 | uint16_t *refcount_table; |
| 2708 | + int ret, errors = 0; | |
| 2689 | 2709 | |
| 2690 | 2710 | size = bdrv_getlength(s->hd); |
| 2691 | 2711 | nb_clusters = size_to_clusters(s, size); |
| 2692 | 2712 | refcount_table = qemu_mallocz(nb_clusters * sizeof(uint16_t)); |
| 2693 | 2713 | |
| 2694 | 2714 | /* header */ |
| 2695 | - inc_refcounts(bs, refcount_table, nb_clusters, | |
| 2715 | + errors += inc_refcounts(bs, refcount_table, nb_clusters, | |
| 2696 | 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 | 2719 | s->l1_table_offset, s->l1_size, 1); |
| 2720 | + if (ret < 0) { | |
| 2721 | + return ret; | |
| 2722 | + } | |
| 2723 | + errors += ret; | |
| 2700 | 2724 | |
| 2701 | 2725 | /* snapshots */ |
| 2702 | 2726 | for(i = 0; i < s->nb_snapshots; i++) { |
| ... | ... | @@ -2704,18 +2728,18 @@ static void check_refcounts(BlockDriverState *bs) |
| 2704 | 2728 | check_refcounts_l1(bs, refcount_table, nb_clusters, |
| 2705 | 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 | 2732 | s->snapshots_offset, s->snapshots_size); |
| 2709 | 2733 | |
| 2710 | 2734 | /* refcount data */ |
| 2711 | - inc_refcounts(bs, refcount_table, nb_clusters, | |
| 2735 | + errors += inc_refcounts(bs, refcount_table, nb_clusters, | |
| 2712 | 2736 | s->refcount_table_offset, |
| 2713 | 2737 | s->refcount_table_size * sizeof(uint64_t)); |
| 2714 | 2738 | for(i = 0; i < s->refcount_table_size; i++) { |
| 2715 | 2739 | int64_t offset; |
| 2716 | 2740 | offset = s->refcount_table[i]; |
| 2717 | 2741 | if (offset != 0) { |
| 2718 | - inc_refcounts(bs, refcount_table, nb_clusters, | |
| 2742 | + errors += inc_refcounts(bs, refcount_table, nb_clusters, | |
| 2719 | 2743 | offset, s->cluster_size); |
| 2720 | 2744 | } |
| 2721 | 2745 | } |
| ... | ... | @@ -2724,12 +2748,21 @@ static void check_refcounts(BlockDriverState *bs) |
| 2724 | 2748 | for(i = 0; i < nb_clusters; i++) { |
| 2725 | 2749 | refcount1 = get_refcount(bs, i); |
| 2726 | 2750 | refcount2 = refcount_table[i]; |
| 2727 | - if (refcount1 != refcount2) | |
| 2751 | + if (refcount1 != refcount2) { | |
| 2728 | 2752 | fprintf(stderr, "ERROR cluster %d refcount=%d reference=%d\n", |
| 2729 | 2753 | i, refcount1, refcount2); |
| 2754 | + errors++; | |
| 2755 | + } | |
| 2730 | 2756 | } |
| 2731 | 2757 | |
| 2732 | 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 | 2768 | #if 0 |
| ... | ... | @@ -2751,7 +2784,6 @@ static void dump_refcounts(BlockDriverState *bs) |
| 2751 | 2784 | } |
| 2752 | 2785 | } |
| 2753 | 2786 | #endif |
| 2754 | -#endif | |
| 2755 | 2787 | |
| 2756 | 2788 | static int qcow_put_buffer(BlockDriverState *bs, const uint8_t *buf, |
| 2757 | 2789 | int64_t pos, int size) |
| ... | ... | @@ -2806,4 +2838,5 @@ BlockDriver bdrv_qcow2 = { |
| 2806 | 2838 | .bdrv_get_buffer = qcow_get_buffer, |
| 2807 | 2839 | |
| 2808 | 2840 | .bdrv_create2 = qcow_create2, |
| 2841 | + .bdrv_check = qcow_check, | |
| 2809 | 2842 | }; | ... | ... |
block.c
| ... | ... | @@ -506,6 +506,20 @@ void bdrv_delete(BlockDriverState *bs) |
| 506 | 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 | 523 | /* commit COW file into the raw image */ |
| 510 | 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 | 73 | int bdrv_open2(BlockDriverState *bs, const char *filename, int flags, |
| 74 | 74 | BlockDriver *drv); |
| 75 | 75 | void bdrv_close(BlockDriverState *bs); |
| 76 | +int bdrv_check(BlockDriverState *bs); | |
| 76 | 77 | int bdrv_read(BlockDriverState *bs, int64_t sector_num, |
| 77 | 78 | uint8_t *buf, int nb_sectors); |
| 78 | 79 | int bdrv_write(BlockDriverState *bs, int64_t sector_num, | ... | ... |
block_int.h
| ... | ... | @@ -102,6 +102,9 @@ struct BlockDriver { |
| 102 | 102 | const char *backing_file, const char *backing_format, |
| 103 | 103 | int flags); |
| 104 | 104 | |
| 105 | + /* Returns number of errors in image, -errno for internal errors */ | |
| 106 | + int (*bdrv_check)(BlockDriverState* bs); | |
| 107 | + | |
| 105 | 108 | struct BlockDriver *next; |
| 106 | 109 | }; |
| 107 | 110 | ... | ... |