X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/f003d07337a6d4d02c43429b26a4270459afb51a..7cb6d3c9be2cbda05d2b14e7897e74839e48fbb2:/block/qcow2-refcount.c diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index d46b69d7f3..46082aeac1 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -26,7 +26,7 @@ #include "qapi/error.h" #include "qemu-common.h" #include "block/block_int.h" -#include "block/qcow2.h" +#include "qcow2.h" #include "qemu/range.h" #include "qemu/bswap.h" #include "qemu/cutils.h" @@ -734,7 +734,7 @@ void qcow2_process_discards(BlockDriverState *bs, int ret) /* Discard is optional, ignore the return value */ if (ret >= 0) { - bdrv_pdiscard(bs->file->bs, d->offset, d->bytes); + bdrv_pdiscard(bs->file, d->offset, d->bytes); } g_free(d); @@ -839,6 +839,13 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, qcow2_cache_put(s->refcount_block_cache, &refcount_block); } ret = alloc_refcount_block(bs, cluster_index, &refcount_block); + /* If the caller needs to restart the search for free clusters, + * try the same ones first to see if they're still free. */ + if (ret == -EAGAIN) { + if (s->free_cluster_index > (start >> s->cluster_bits)) { + s->free_cluster_index = (start >> s->cluster_bits); + } + } if (ret < 0) { goto fail; } @@ -1171,7 +1178,35 @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry, } } +int coroutine_fn qcow2_write_caches(BlockDriverState *bs) +{ + BDRVQcow2State *s = bs->opaque; + int ret; + + ret = qcow2_cache_write(bs, s->l2_table_cache); + if (ret < 0) { + return ret; + } + + if (qcow2_need_accurate_refcounts(s)) { + ret = qcow2_cache_write(bs, s->refcount_block_cache); + if (ret < 0) { + return ret; + } + } + + return 0; +} +int coroutine_fn qcow2_flush_caches(BlockDriverState *bs) +{ + int ret = qcow2_write_caches(bs); + if (ret < 0) { + return ret; + } + + return bdrv_flush(bs->file->bs); +} /*********************************************************/ /* snapshots and image creation */ @@ -1204,7 +1239,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, * l1_table_offset when it is the current s->l1_table_offset! Be careful * when changing this! */ if (l1_table_offset != s->l1_table_offset) { - l1_table = g_try_malloc0(align_offset(l1_size2, 512)); + l1_table = g_try_malloc0(ROUND_UP(l1_size2, 512)); if (l1_size2 && l1_table == NULL) { ret = -ENOMEM; goto fail; @@ -1542,9 +1577,9 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, case QCOW2_CLUSTER_COMPRESSED: /* Compressed clusters don't have QCOW_OFLAG_COPIED */ if (l2_entry & QCOW_OFLAG_COPIED) { - fprintf(stderr, "ERROR: cluster %" PRId64 ": " + fprintf(stderr, "ERROR: coffset=0x%" PRIx64 ": " "copied flag must never be set for compressed " - "clusters\n", l2_entry >> s->cluster_bits); + "clusters\n", l2_entry & s->cluster_offset_mask); l2_entry &= ~QCOW_OFLAG_COPIED; res->corruptions++; } @@ -1764,6 +1799,19 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, int ret; uint64_t refcount; int i, j; + bool repair; + + if (fix & BDRV_FIX_ERRORS) { + /* Always repair */ + repair = true; + } else if (fix & BDRV_FIX_LEAKS) { + /* Repair only if that seems safe: This function is always + * called after the refcounts have been fixed, so the refcount + * is accurate if that repair was successful */ + repair = !res->check_errors && !res->corruptions && !res->leaks; + } else { + repair = false; + } for (i = 0; i < s->l1_size; i++) { uint64_t l1_entry = s->l1_table[i]; @@ -1783,10 +1831,8 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, if ((refcount == 1) != ((l1_entry & QCOW_OFLAG_COPIED) != 0)) { fprintf(stderr, "%s OFLAG_COPIED L2 cluster: l1_index=%d " "l1_entry=%" PRIx64 " refcount=%" PRIu64 "\n", - fix & BDRV_FIX_ERRORS ? "Repairing" : - "ERROR", - i, l1_entry, refcount); - if (fix & BDRV_FIX_ERRORS) { + repair ? "Repairing" : "ERROR", i, l1_entry, refcount); + if (repair) { s->l1_table[i] = refcount == 1 ? l1_entry | QCOW_OFLAG_COPIED : l1_entry & ~QCOW_OFLAG_COPIED; @@ -1827,10 +1873,8 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) { fprintf(stderr, "%s OFLAG_COPIED data cluster: " "l2_entry=%" PRIx64 " refcount=%" PRIu64 "\n", - fix & BDRV_FIX_ERRORS ? "Repairing" : - "ERROR", - l2_entry, refcount); - if (fix & BDRV_FIX_ERRORS) { + repair ? "Repairing" : "ERROR", l2_entry, refcount); + if (repair) { l2_table[j] = cpu_to_be64(refcount == 1 ? l2_entry | QCOW_OFLAG_COPIED : l2_entry & ~QCOW_OFLAG_COPIED); @@ -2019,6 +2063,20 @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res, /* snapshots */ for (i = 0; i < s->nb_snapshots; i++) { sn = s->snapshots + i; + if (offset_into_cluster(s, sn->l1_table_offset)) { + fprintf(stderr, "ERROR snapshot %s (%s) l1_offset=%#" PRIx64 ": " + "L1 table is not cluster aligned; snapshot table entry " + "corrupted\n", sn->id_str, sn->name, sn->l1_table_offset); + res->corruptions++; + continue; + } + if (sn->l1_size > QCOW_MAX_L1_SIZE / sizeof(uint64_t)) { + fprintf(stderr, "ERROR snapshot %s (%s) l1_size=%#" PRIx32 ": " + "L1 table is too large; snapshot table entry corrupted\n", + sn->id_str, sn->name, sn->l1_size); + res->corruptions++; + continue; + } ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters, sn->l1_table_offset, sn->l1_size, 0, fix); if (ret < 0) { @@ -2553,7 +2611,7 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, } /* align range to test to cluster boundaries */ - size = align_offset(offset_into_cluster(s, offset) + size, s->cluster_size); + size = ROUND_UP(offset_into_cluster(s, offset) + size, s->cluster_size); offset = start_of_cluster(s, offset); if ((chk & QCOW2_OL_ACTIVE_L1) && s->l1_size) { @@ -2614,9 +2672,17 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, uint64_t l1_ofs = s->snapshots[i].l1_table_offset; uint32_t l1_sz = s->snapshots[i].l1_size; uint64_t l1_sz2 = l1_sz * sizeof(uint64_t); - uint64_t *l1 = g_try_malloc(l1_sz2); + uint64_t *l1; int ret; + ret = qcow2_validate_table(bs, l1_ofs, l1_sz, sizeof(uint64_t), + QCOW_MAX_L1_SIZE, "", NULL); + if (ret < 0) { + return ret; + } + + l1 = g_try_malloc(l1_sz2); + if (l1_sz2 && l1 == NULL) { return -ENOMEM; } @@ -2639,19 +2705,31 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, } } + if ((chk & QCOW2_OL_BITMAP_DIRECTORY) && + (s->autoclear_features & QCOW2_AUTOCLEAR_BITMAPS)) + { + if (overlaps_with(s->bitmap_directory_offset, + s->bitmap_directory_size)) + { + return QCOW2_OL_BITMAP_DIRECTORY; + } + } + return 0; } static const char *metadata_ol_names[] = { - [QCOW2_OL_MAIN_HEADER_BITNR] = "qcow2_header", - [QCOW2_OL_ACTIVE_L1_BITNR] = "active L1 table", - [QCOW2_OL_ACTIVE_L2_BITNR] = "active L2 table", - [QCOW2_OL_REFCOUNT_TABLE_BITNR] = "refcount table", - [QCOW2_OL_REFCOUNT_BLOCK_BITNR] = "refcount block", - [QCOW2_OL_SNAPSHOT_TABLE_BITNR] = "snapshot table", - [QCOW2_OL_INACTIVE_L1_BITNR] = "inactive L1 table", - [QCOW2_OL_INACTIVE_L2_BITNR] = "inactive L2 table", + [QCOW2_OL_MAIN_HEADER_BITNR] = "qcow2_header", + [QCOW2_OL_ACTIVE_L1_BITNR] = "active L1 table", + [QCOW2_OL_ACTIVE_L2_BITNR] = "active L2 table", + [QCOW2_OL_REFCOUNT_TABLE_BITNR] = "refcount table", + [QCOW2_OL_REFCOUNT_BLOCK_BITNR] = "refcount block", + [QCOW2_OL_SNAPSHOT_TABLE_BITNR] = "snapshot table", + [QCOW2_OL_INACTIVE_L1_BITNR] = "inactive L1 table", + [QCOW2_OL_INACTIVE_L2_BITNR] = "inactive L2 table", + [QCOW2_OL_BITMAP_DIRECTORY_BITNR] = "bitmap directory", }; +QEMU_BUILD_BUG_ON(QCOW2_OL_MAX_BITNR != ARRAY_SIZE(metadata_ol_names)); /* * First performs a check for metadata overlaps (through