From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:37327) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1XNTvQ-0007Jz-Oe for qemu-devel@nongnu.org; Fri, 29 Aug 2014 17:41:45 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1XNTvK-0008Cs-Nf for qemu-devel@nongnu.org; Fri, 29 Aug 2014 17:41:40 -0400 Received: from mx1.redhat.com ([209.132.183.28]:16708) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1XNTvK-0008CK-Ek for qemu-devel@nongnu.org; Fri, 29 Aug 2014 17:41:34 -0400 From: Max Reitz Date: Fri, 29 Aug 2014 23:40:59 +0200 Message-Id: <1409348463-16627-8-git-send-email-mreitz@redhat.com> In-Reply-To: <1409348463-16627-1-git-send-email-mreitz@redhat.com> References: <1409348463-16627-1-git-send-email-mreitz@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Subject: [Qemu-devel] [PATCH v5 07/11] qcow2: Do not perform potentially damaging repairs List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: Kevin Wolf , Stefan Hajnoczi , =?UTF-8?q?Beno=C3=AEt=20Canet?= , Max Reitz If a referenced cluster has a refcount of 0, increasing its refcount may result in clusters being allocated for the refcount structures. This may overwrite the referenced cluster, therefore we cannot simply increase the refcount then. In such cases, we can either try to replicate all the refcount operations solely for the check operation, basing the allocations on the in-memory refcount table; or we can simply rebuild the whole refcount structure based on the in-memory refcount table. Since the latter will be much easier, do that. To prepare for this, introduce a "rebuild" boolean which should be set to true whenever a fix is rather dangerous or too complicated using the current refcount structures. Another example for this is refcount blocks being referenced more than once. Signed-off-by: Max Reitz Reviewed-by: Beno=C3=AEt Canet --- block/qcow2-refcount.c | 185 ++++++++-----------------------------------= ------ 1 file changed, 27 insertions(+), 158 deletions(-) diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 394a402..6300cec 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -1382,127 +1382,12 @@ fail: } =20 /* - * Writes one sector of the refcount table to the disk - */ -#define RT_ENTRIES_PER_SECTOR (512 / sizeof(uint64_t)) -static int write_reftable_entry(BlockDriverState *bs, int rt_index) -{ - BDRVQcowState *s =3D bs->opaque; - uint64_t buf[RT_ENTRIES_PER_SECTOR]; - int rt_start_index; - int i, ret; - - rt_start_index =3D rt_index & ~(RT_ENTRIES_PER_SECTOR - 1); - for (i =3D 0; i < RT_ENTRIES_PER_SECTOR; i++) { - buf[i] =3D cpu_to_be64(s->refcount_table[rt_start_index + i]); - } - - ret =3D qcow2_pre_write_overlap_check(bs, QCOW2_OL_REFCOUNT_TABLE, - s->refcount_table_offset + rt_start_index * sizeof(uint64_t)= , - sizeof(buf)); - if (ret < 0) { - return ret; - } - - BLKDBG_EVENT(bs->file, BLKDBG_REFTABLE_UPDATE); - ret =3D bdrv_pwrite_sync(bs->file, s->refcount_table_offset + - rt_start_index * sizeof(uint64_t), buf, sizeof(buf)); - if (ret < 0) { - return ret; - } - - return 0; -} - -/* - * Allocates a new cluster for the given refcount block (represented by = its - * offset in the image file) and copies the current content there. This = function - * does _not_ decrement the reference count for the currently occupied c= luster. - * - * This function prints an informative message to stderr on error (and r= eturns - * -errno); on success, the offset of the newly allocated cluster is ret= urned. - */ -static int64_t realloc_refcount_block(BlockDriverState *bs, int reftable= _index, - uint64_t offset) -{ - BDRVQcowState *s =3D bs->opaque; - int64_t new_offset =3D 0; - void *refcount_block =3D NULL; - int ret; - - /* allocate new refcount block */ - new_offset =3D qcow2_alloc_clusters(bs, s->cluster_size); - if (new_offset < 0) { - fprintf(stderr, "Could not allocate new cluster: %s\n", - strerror(-new_offset)); - ret =3D new_offset; - goto done; - } - - /* fetch current refcount block content */ - ret =3D qcow2_cache_get(bs, s->refcount_block_cache, offset, &refcou= nt_block); - if (ret < 0) { - fprintf(stderr, "Could not fetch refcount block: %s\n", strerror= (-ret)); - goto fail_free_cluster; - } - - /* new block has not yet been entered into refcount table, therefore= it is - * no refcount block yet (regarding this check) */ - ret =3D qcow2_pre_write_overlap_check(bs, 0, new_offset, s->cluster_= size); - if (ret < 0) { - fprintf(stderr, "Could not write refcount block; metadata overla= p " - "check failed: %s\n", strerror(-ret)); - /* the image will be marked corrupt, so don't even attempt on fr= eeing - * the cluster */ - goto done; - } - - /* write to new block */ - ret =3D bdrv_write(bs->file, new_offset / BDRV_SECTOR_SIZE, refcount= _block, - s->cluster_sectors); - if (ret < 0) { - fprintf(stderr, "Could not write refcount block: %s\n", strerror= (-ret)); - goto fail_free_cluster; - } - - /* update refcount table */ - assert(!offset_into_cluster(s, new_offset)); - s->refcount_table[reftable_index] =3D new_offset; - ret =3D write_reftable_entry(bs, reftable_index); - if (ret < 0) { - fprintf(stderr, "Could not update refcount table: %s\n", - strerror(-ret)); - goto fail_free_cluster; - } - - goto done; - -fail_free_cluster: - qcow2_free_clusters(bs, new_offset, s->cluster_size, QCOW2_DISCARD_O= THER); - -done: - if (refcount_block) { - /* This should never fail, as it would only do so if the given r= efcount - * block cannot be found in the cache. As this is impossible as = long as - * there are no bugs, assert the success. */ - int tmp =3D qcow2_cache_put(bs, s->refcount_block_cache, &refcou= nt_block); - assert(tmp =3D=3D 0); - } - - if (ret < 0) { - return ret; - } - - return new_offset; -} - -/* * Checks consistency of refblocks and accounts for each refblock in * *refcount_table. */ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res, - BdrvCheckMode fix, uint16_t **refcount_table, - int64_t *nb_clusters) + BdrvCheckMode fix, bool *rebuild, + uint16_t **refcount_table, int64_t *nb_cluste= rs) { BDRVQcowState *s =3D bs->opaque; int64_t i, size; @@ -1518,6 +1403,7 @@ static int check_refblocks(BlockDriverState *bs, Bd= rvCheckResult *res, fprintf(stderr, "ERROR refcount block %" PRId64 " is not " "cluster aligned; refcount table entry corrupted\n", i); res->corruptions++; + *rebuild =3D true; continue; } =20 @@ -1573,6 +1459,7 @@ static int check_refblocks(BlockDriverState *bs, Bd= rvCheckResult *res, =20 resize_fail: res->corruptions++; + *rebuild =3D true; fprintf(stderr, "ERROR could not resize image: %s\n", strerror(-ret)); } else { @@ -1585,40 +1472,10 @@ resize_fail: inc_refcounts(bs, res, *refcount_table, *nb_clusters, offset, s->cluster_size); if ((*refcount_table)[cluster] !=3D 1) { - fprintf(stderr, "%s refcount block %" PRId64 - " refcount=3D%d\n", - fix & BDRV_FIX_ERRORS ? "Repairing" : - "ERROR", - i, (*refcount_table)[cluster]); - - if (fix & BDRV_FIX_ERRORS) { - int64_t new_offset; - - new_offset =3D realloc_refcount_block(bs, i, offset)= ; - if (new_offset < 0) { - res->corruptions++; - continue; - } - - /* update refcounts */ - if ((new_offset >> s->cluster_bits) >=3D *nb_cluster= s) { - /* increase refcount_table size if necessary */ - int old_nb_clusters =3D *nb_clusters; - *nb_clusters =3D (new_offset >> s->cluster_bits)= + 1; - *refcount_table =3D g_renew(uint16_t, *refcount_= table, - *nb_clusters); - memset(&(*refcount_table)[old_nb_clusters], 0, - (*nb_clusters - old_nb_clusters) * - sizeof(uint16_t)); - } - (*refcount_table)[cluster]--; - inc_refcounts(bs, res, *refcount_table, *nb_clusters= , - new_offset, s->cluster_size); - - res->corruptions_fixed++; - } else { - res->corruptions++; - } + fprintf(stderr, "ERROR refcount block %" PRId64 + " refcount=3D%d\n", i, (*refcount_table)[cluster= ]); + res->corruptions++; + *rebuild =3D true; } } } @@ -1630,8 +1487,8 @@ resize_fail: * Calculates an in-memory refcount table. */ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *re= s, - BdrvCheckMode fix, uint16_t **refcount_ta= ble, - int64_t *nb_clusters) + BdrvCheckMode fix, bool *rebuild, + uint16_t **refcount_table, int64_t *nb_cl= usters) { BDRVQcowState *s =3D bs->opaque; int64_t i; @@ -1674,7 +1531,7 @@ static int calculate_refcounts(BlockDriverState *bs= , BdrvCheckResult *res, s->refcount_table_offset, s->refcount_table_size * sizeof(uint64_t)); =20 - return check_refblocks(bs, res, fix, refcount_table, nb_clusters); + return check_refblocks(bs, res, fix, rebuild, refcount_table, nb_clu= sters); } =20 /* @@ -1682,7 +1539,8 @@ static int calculate_refcounts(BlockDriverState *bs= , BdrvCheckResult *res, * refcount as reported by the refcount structures on-disk. */ static void compare_refcounts(BlockDriverState *bs, BdrvCheckResult *res= , - BdrvCheckMode fix, int64_t *highest_cluste= r, + BdrvCheckMode fix, bool *rebuild, + int64_t *highest_cluster, uint16_t *refcount_table, int64_t nb_clust= ers) { BDRVQcowState *s =3D bs->opaque; @@ -1709,10 +1567,15 @@ static void compare_refcounts(BlockDriverState *b= s, BdrvCheckResult *res, int *num_fixed =3D NULL; if (refcount1 > refcount2 && (fix & BDRV_FIX_LEAKS)) { num_fixed =3D &res->leaks_fixed; - } else if (refcount1 < refcount2 && (fix & BDRV_FIX_ERRORS))= { + } else if (refcount1 > 0 && refcount1 < refcount2 && + (fix & BDRV_FIX_ERRORS)) { num_fixed =3D &res->corruptions_fixed; } =20 + if (refcount1 =3D=3D 0) { + *rebuild =3D true; + } + fprintf(stderr, "%s cluster %" PRId64 " refcount=3D%d refere= nce=3D%d\n", num_fixed !=3D NULL ? "Repairing" : refcount1 < refcount2 ? "ERROR" : @@ -1751,6 +1614,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, Bdr= vCheckResult *res, BDRVQcowState *s =3D bs->opaque; int64_t size, highest_cluster, nb_clusters; uint16_t *refcount_table =3D NULL; + bool rebuild =3D false; int ret; =20 size =3D bdrv_getlength(bs->file); @@ -1768,14 +1632,19 @@ int qcow2_check_refcounts(BlockDriverState *bs, B= drvCheckResult *res, res->bfi.total_clusters =3D size_to_clusters(s, bs->total_sectors * BDRV_SECTOR_SIZE); =20 - ret =3D calculate_refcounts(bs, res, fix, &refcount_table, &nb_clust= ers); + ret =3D calculate_refcounts(bs, res, fix, &rebuild, &refcount_table, + &nb_clusters); if (ret < 0) { goto fail; } =20 - compare_refcounts(bs, res, fix, &highest_cluster, refcount_table, + compare_refcounts(bs, res, fix, &rebuild, &highest_cluster, refcount= _table, nb_clusters); =20 + if (rebuild) { + fprintf(stderr, "ERROR need to rebuild refcount structures\n"); + } + /* check OFLAG_COPIED */ ret =3D check_oflag_copied(bs, res, fix); if (ret < 0) { --=20 2.1.0