Commit ac5b787a authored by Max Reitz's avatar Max Reitz
Browse files

qcow2: Repair unaligned preallocated zero clusters



We can easily repair unaligned preallocated zero clusters by discarding
them, so why not do it?

Signed-off-by: default avatarMax Reitz <mreitz@redhat.com>
Message-id: 20171110203759.14018-2-mreitz@redhat.com
Reviewed-by: default avatarEric Blake <eblake@redhat.com>
Signed-off-by: default avatarMax Reitz <mreitz@redhat.com>
parent 7c3a3028
Loading
Loading
Loading
Loading
+58 −12
Original line number Diff line number Diff line
@@ -1508,7 +1508,7 @@ enum {
static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
                              void **refcount_table,
                              int64_t *refcount_table_size, int64_t l2_offset,
                              int flags)
                              int flags, BdrvCheckMode fix)
{
    BDRVQcow2State *s = bs->opaque;
    uint64_t *l2_table, l2_entry;
@@ -1579,6 +1579,57 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
                next_contiguous_offset = offset + s->cluster_size;
            }

            /* Correct offsets are cluster aligned */
            if (offset_into_cluster(s, offset)) {
                if (qcow2_get_cluster_type(l2_entry) ==
                    QCOW2_CLUSTER_ZERO_ALLOC)
                {
                    fprintf(stderr, "%s offset=%" PRIx64 ": Preallocated zero "
                            "cluster is not properly aligned; L2 entry "
                            "corrupted.\n",
                            fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR",
                            offset);
                    if (fix & BDRV_FIX_ERRORS) {
                        uint64_t l2e_offset =
                            l2_offset + (uint64_t)i * sizeof(uint64_t);

                        l2_entry = QCOW_OFLAG_ZERO;
                        l2_table[i] = cpu_to_be64(l2_entry);
                        ret = qcow2_pre_write_overlap_check(bs,
                                QCOW2_OL_ACTIVE_L2 | QCOW2_OL_INACTIVE_L2,
                                l2e_offset, sizeof(uint64_t));
                        if (ret < 0) {
                            fprintf(stderr, "ERROR: Overlap check failed\n");
                            res->check_errors++;
                            /* Something is seriously wrong, so abort checking
                             * this L2 table */
                            goto fail;
                        }

                        ret = bdrv_pwrite_sync(bs->file, l2e_offset,
                                               &l2_table[i], sizeof(uint64_t));
                        if (ret < 0) {
                            fprintf(stderr, "ERROR: Failed to overwrite L2 "
                                    "table entry: %s\n", strerror(-ret));
                            res->check_errors++;
                            /* Do not abort, continue checking the rest of this
                             * L2 table's entries */
                        } else {
                            res->corruptions_fixed++;
                            /* Skip marking the cluster as used
                             * (it is unused now) */
                            continue;
                        }
                    } else {
                        res->corruptions++;
                    }
                } else {
                    fprintf(stderr, "ERROR offset=%" PRIx64 ": Data cluster is "
                        "not properly aligned; L2 entry corrupted.\n", offset);
                    res->corruptions++;
                }
            }

            /* Mark cluster as used */
            ret = qcow2_inc_refcounts_imrt(bs, res,
                                           refcount_table, refcount_table_size,
@@ -1586,13 +1637,6 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
            if (ret < 0) {
                goto fail;
            }

            /* Correct offsets are cluster aligned */
            if (offset_into_cluster(s, offset)) {
                fprintf(stderr, "ERROR offset=%" PRIx64 ": Cluster is not "
                    "properly aligned; L2 entry corrupted.\n", offset);
                res->corruptions++;
            }
            break;
        }

@@ -1626,7 +1670,7 @@ static int check_refcounts_l1(BlockDriverState *bs,
                              void **refcount_table,
                              int64_t *refcount_table_size,
                              int64_t l1_table_offset, int l1_size,
                              int flags)
                              int flags, BdrvCheckMode fix)
{
    BDRVQcow2State *s = bs->opaque;
    uint64_t *l1_table = NULL, l2_offset, l1_size2;
@@ -1681,7 +1725,8 @@ static int check_refcounts_l1(BlockDriverState *bs,

            /* Process and check L2 entries */
            ret = check_refcounts_l2(bs, res, refcount_table,
                                     refcount_table_size, l2_offset, flags);
                                     refcount_table_size, l2_offset, flags,
                                     fix);
            if (ret < 0) {
                goto fail;
            }
@@ -1957,7 +2002,8 @@ static int calculate_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, CHECK_FRAG_INFO);
                             s->l1_table_offset, s->l1_size, CHECK_FRAG_INFO,
                             fix);
    if (ret < 0) {
        return ret;
    }
@@ -1966,7 +2012,7 @@ static int calculate_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, fix);
        if (ret < 0) {
            return ret;
        }
+2 −1
Original line number Diff line number Diff line
@@ -335,7 +335,8 @@ poke_file "$TEST_IMG" "$l2_offset" "\x80\x00\x00\x00\x00\x00\x2a\x01"
# Let's write to it!
$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io

# Can't repair this yet (TODO: We can just deallocate the cluster)
echo '--- Repairing ---'
_check_test_img -r all

echo
echo '=== Discarding with an unaligned refblock ==='
+9 −0
Original line number Diff line number Diff line
@@ -317,6 +317,15 @@ discard 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
qcow2: Marking image as corrupt: Preallocated zero cluster offset 0x2a00 unaligned (guest offset: 0); further corruption events will be suppressed
write failed: Input/output error
--- Repairing ---
Repairing offset=2a00: Preallocated zero cluster is not properly aligned; L2 entry corrupted.
The following inconsistencies were found and repaired:

    0 leaked clusters
    1 corruptions

Double checking the fixed image now...
No errors were found on the image.

=== Discarding with an unaligned refblock ===