From nobody Fri Apr 19 05:50:12 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=virtuozzo.com ARC-Seal: i=1; a=rsa-sha256; t=1578931041; cv=none; d=zohomail.com; s=zohoarc; b=cr9CZTq+MM8GJbk/EpxDNfHZGug0vJ06ZKVNMTrRrI3vXhHcux9+zxMmgTJutEzvl8r/OfBMjOOCXi+cxofh+zPcwCGmD7qyDi9FE3TCCLod8GBXLjpW0YFy306mSG6YjH3Hv9i0WQO6kQ2ZXpb9O9L+hK+TPUEgEiQEjD+LLgA= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1578931041; h=Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:Message-ID:References:Sender:Subject:To; bh=bGYGigrD988gt3URCqQ0T7Qi2767/KoWb9dCyozzGlM=; b=jX1FeciEnsuqLrmdEkgNeDkDD/AXHiaVUhihIv2prXORKjoGdpnWHn/tBC2ies7DOikhWJFTqjx7oysnpydvniddWNet++HaLqXt07hR17/S6U/jcEW44um1nOPD4Er0XgcOyh0Xulie830cGj51lx9QG92VC1ZoSE9BiWFU+90= ARC-Authentication-Results: i=1; mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1578931041411493.3969604056898; Mon, 13 Jan 2020 07:57:21 -0800 (PST) Received: from localhost ([::1]:52142 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ir25f-0001Br-57 for importer@patchew.org; Mon, 13 Jan 2020 10:57:19 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:38829) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ir249-00084X-Ew for qemu-devel@nongnu.org; Mon, 13 Jan 2020 10:55:46 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ir248-00049E-00 for qemu-devel@nongnu.org; Mon, 13 Jan 2020 10:55:45 -0500 Received: from relay.sw.ru ([185.231.240.75]:36152) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1ir244-000436-9M; Mon, 13 Jan 2020 10:55:40 -0500 Received: from dhcp-172-16-25-136.sw.ru ([172.16.25.136] helo=localhost.sw.ru) by relay.sw.ru with esmtp (Exim 4.92.3) (envelope-from ) id 1ir23x-0002bW-UP; Mon, 13 Jan 2020 18:55:34 +0300 From: Andrey Shinkevich To: qemu-block@nongnu.org Subject: [PATCH v2 1/2] qcow2: introduce Qcow2Metadata structure Date: Mon, 13 Jan 2020 18:55:32 +0300 Message-Id: <1578930933-69721-2-git-send-email-andrey.shinkevich@virtuozzo.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1578930933-69721-1-git-send-email-andrey.shinkevich@virtuozzo.com> References: <1578930933-69721-1-git-send-email-andrey.shinkevich@virtuozzo.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x [fuzzy] X-Received-From: 185.231.240.75 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kwolf@redhat.com, vsementsov@virtuozzo.com, armbru@redhat.com, qemu-devel@nongnu.org, andrey.shinkevich@virtuozzo.com, den@openvz.org, mreitz@redhat.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" The preliminary patch to provide an extendable structure for dumping QCOW2 metadata allocations in image. The command line optional key is introduced in the patch that follows. Suggested-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Andrey Shinkevich --- qapi/block-core.json | 209 +++++++++++++++++++++++++++++++++++++++++++++++= +++- 1 file changed, 208 insertions(+), 1 deletion(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index 7ff5e5e..fab7435 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -176,6 +176,209 @@ '*backing-image': 'ImageInfo', '*format-specific': 'ImageInfoSpecific' } } =20 + +## +# @Qcow2Metadata: +# +# Encapsulates QCOW2 metadata information +# +# @qcow2-header: QCOW2 header info +# +# @active-l1: L1 active table info +# +# @refcount-table: refcount table info +# +# @crypt-header: encryption header info +# +# @bitmaps: bitmap tables info +# +# @snapshot-table: snapshot tables info +# +# Since: 5.0 +## +{ 'struct': 'Qcow2Metadata', + 'data': { 'qcow2-header': 'Qcow2Header', + 'refcount-table': 'Qcow2RefcountTable', + 'active-l1': 'Qcow2L1Table', + '*crypt-header': 'Qcow2EncryptionHeader', + '*bitmaps': 'Qcow2Bitmaps', + '*snapshot-table': 'Qcow2SnapshotsTable' } } + +## +# @Qcow2Header: +# +# QCOW2 header information +# +# @version: version number +# +# @location: header offset and size in image +# +# Since: 5.0 +## +{ 'struct': 'Qcow2Header', + 'data': {'version': 'int', + 'location': 'Qcow2Allocation' } } + +## +# @Qcow2EncryptionHeader: +# +# QCOW2 encryption header information +# +# @location: header offset and size in image +# +# Since: 5.0 +## +{ 'struct': 'Qcow2EncryptionHeader', + 'data': {'location': 'Qcow2Allocation' } } + +## +# @Qcow2RefcountTable: +# +# QCOW2 refcount table information +# +# @location: refcount table offset and size in image +# +# Since: 5.0 +## +{ 'struct': 'Qcow2RefcountTable', + 'data': {'location': 'Qcow2Allocation' } } + +## +# @Qcow2L1Table: +# +# L1 table information +# +# @l2-list: list of L2 table locations +# +# @location: L1 table offset and size in image +# +# @name: entity name the table relates to +# +# Since: 5.0 +## +{ 'struct': 'Qcow2L1Table', + 'data': {'l2-list': ['Qcow2Allocation'], + 'location': 'Qcow2Allocation', + 'name': 'str'} } + +## +# @Qcow2Allocation: +# +# QCOW2 data location in image +# +# @offset: data offset +# +# @size: data size +# +# Since: 5.0 +## +{ 'struct': 'Qcow2Allocation', + 'data': {'offset': 'uint64', 'size': 'uint64' } } + +## +# @Qcow2Bitmaps: +# +# QCOW2 bitmaps information +# +# @nb-bitmaps: the number of bitmaps contained in the image +# +# @bitmap-dir: bitmap directory information +# +# Since: 5.0 +## +{ 'struct': 'Qcow2Bitmaps', + 'data': {'nb-bitmaps': 'int', + 'bitmap-dir': 'Qcow2BitmapDir' } } + +## +# @Qcow2BitmapDir: +# +# QCOW2 bitmap directory information +# +# @dir-entries: list of bitmap directory entries +# +# @location: bitmap directory offset and size in image +# +# Since: 5.0 +## +{ 'struct': 'Qcow2BitmapDir', + 'data': {'dir-entries': ['Qcow2BitmapDirectoryEntry'], + 'location': 'Qcow2Allocation' } } + +## +# @Qcow2BitmapDirectoryEntry: +# +# QCOW2 bitmap directory entry information +# +# @bitmap-table: bitmap table offset and size in image +# +# Since: 5.0 +## +{ 'struct': 'Qcow2BitmapDirectoryEntry', + 'data': {'bitmap-table': 'Qcow2BitmapTableInfo', + 'bitmap-name': 'str' } } + +## +# @Qcow2BitmapTableInfo: +# +# QCOW2 bitmap table information +# +# @table-entries: list of bitmap table entries +# +# @location: bitmap table offset and size in image +# +# Since: 5.0 +## +{ 'struct': 'Qcow2BitmapTableInfo', + 'data': {'table-entries': ['Qcow2BitmapTableInfoEntry'], + 'location': 'Qcow2Allocation' } } + +## +# @Qcow2BitmapTableInfoEntry: +# +# QCOW2 bitmap table entry information +# +# @type: bitmap table entry type +# +# @cluster: bitmap table entry offset and size in image +# +# Since: 5.0 +## +{ 'struct': 'Qcow2BitmapTableInfoEntry', + 'data': {'type': 'Qcow2BitmapTableInfoEntryType', + '*cluster': 'Qcow2Allocation' } } + +## +# @Qcow2BitmapTableInfoEntryType: +# +# An enumeration of cluster types in bitmap table +# +# @all-zeroes: cluster should be read as all zeroes +# +# @all-ones: cluster should be read as all ones +# +# @serialized: cluster data are written on disk +# +# Since: 5.0 +## +{ 'enum': 'Qcow2BitmapTableInfoEntryType', + 'data': ['all-zeroes', 'all-ones', 'serialized'] } + +## +# @Qcow2SnapshotsTable: +# +# Snapshots table location in image file. +# +# @location: offset and size of snapshot table +# +# @l1-list: list of snapshots L1 tables +# +# Since: 5.0 +## +{ 'struct': 'Qcow2SnapshotsTable', + 'data': {'location': 'Qcow2Allocation', + 'l1-list': ['Qcow2L1Table'] } } + ## # @ImageCheck: # @@ -215,6 +418,9 @@ # field is present if the driver for the image format # supports it # +# @metadata: encapsulates QCOW2 tables allocation information (default: no= ne, +# turned on with the command line optional key; since 5.0) +# # Since: 1.4 # ## @@ -223,7 +429,8 @@ '*image-end-offset': 'int', '*corruptions': 'int', '*leaks': 'i= nt', '*corruptions-fixed': 'int', '*leaks-fixed': 'int', '*total-clusters': 'int', '*allocated-clusters': 'int', - '*fragmented-clusters': 'int', '*compressed-clusters': 'int' } } + '*fragmented-clusters': 'int', '*compressed-clusters': 'int', + '*metadata': 'Qcow2Metadata' } } =20 ## # @MapEntry: --=20 1.8.3.1 From nobody Fri Apr 19 05:50:12 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=virtuozzo.com ARC-Seal: i=1; a=rsa-sha256; t=1578931043; cv=none; d=zohomail.com; s=zohoarc; b=L2DnS4ondAxWffoR1qe9GHA7sSWoenB+WuuQzvQJipqoezWJZOt0vr5uiw3cWtQHipQe1Rz7xgJspWrgpmPKKPTA4YioYw/mC/kjf7Ts4/R4zbMmDvb3uZR4jkBkgmJK+cC5Ym6kQFl3lmLDIid6Ioj9Qpz9SRqv/SFtIJqB+dM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1578931043; h=Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:Message-ID:References:Sender:Subject:To; bh=vNV8RmwaynJk0x30VsDJe8YhzpSaj8bZVK9wgCRS47I=; b=YqxAca9grZGMCSaiGJJIRaKTHqvjoCCvXF11uZ6TRkeKWk2S3FA65mOYRvluA1i5SUqzo0De09sr302qM6d06m+ZZcUIs9lNLpP12OmJ9Po28iXL04XiZhl3QYdIEU6nVBSMpE2m7aMRck/zIPJNrr9t7/MZIUhRicICu0J/Nh4= ARC-Authentication-Results: i=1; mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1578931043618821.0622397815308; Mon, 13 Jan 2020 07:57:23 -0800 (PST) Received: from localhost ([::1]:52146 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ir25i-0001GD-9K for importer@patchew.org; Mon, 13 Jan 2020 10:57:22 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:38861) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ir24C-00086s-Bl for qemu-devel@nongnu.org; Mon, 13 Jan 2020 10:55:50 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ir249-0004Bq-Qi for qemu-devel@nongnu.org; Mon, 13 Jan 2020 10:55:48 -0500 Received: from relay.sw.ru ([185.231.240.75]:36158) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1ir244-000437-9O; Mon, 13 Jan 2020 10:55:40 -0500 Received: from dhcp-172-16-25-136.sw.ru ([172.16.25.136] helo=localhost.sw.ru) by relay.sw.ru with esmtp (Exim 4.92.3) (envelope-from ) id 1ir23y-0002bW-BJ; Mon, 13 Jan 2020 18:55:34 +0300 From: Andrey Shinkevich To: qemu-block@nongnu.org Subject: [PATCH v2 2/2] qcow2: dump QCOW2 metadata Date: Mon, 13 Jan 2020 18:55:33 +0300 Message-Id: <1578930933-69721-3-git-send-email-andrey.shinkevich@virtuozzo.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1578930933-69721-1-git-send-email-andrey.shinkevich@virtuozzo.com> References: <1578930933-69721-1-git-send-email-andrey.shinkevich@virtuozzo.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x [fuzzy] X-Received-From: 185.231.240.75 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kwolf@redhat.com, vsementsov@virtuozzo.com, armbru@redhat.com, qemu-devel@nongnu.org, andrey.shinkevich@virtuozzo.com, den@openvz.org, mreitz@redhat.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Let QEMU-IMG CHECK command show QCOW2 structure to inform a user about metadata allocations on disk. Introduce '-M'('--dump-meta') key option. Suggested-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Andrey Shinkevich --- block/qcow2-bitmap.c | 54 +++++++++++++++++++++++++++++--- block/qcow2-refcount.c | 84 +++++++++++++++++++++++++++++++++++++++++-----= ---- block/qcow2.c | 30 ++++++++++++++++++ block/qcow2.h | 6 ++-- include/block/block.h | 3 +- qemu-img.c | 30 +++++++++++++++++- qemu-img.texi | 6 +++- 7 files changed, 189 insertions(+), 24 deletions(-) diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index d41f5d0..15e035a 100644 --- a/block/qcow2-bitmap.c +++ b/block/qcow2-bitmap.c @@ -659,20 +659,29 @@ fail: =20 int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *r= es, void **refcount_table, - int64_t *refcount_table_size) + int64_t *refcount_table_size, + Qcow2Bitmaps *bitmaps) { int ret; BDRVQcow2State *s =3D bs->opaque; Qcow2BitmapList *bm_list; Qcow2Bitmap *bm; + Qcow2BitmapDirectoryEntryList **pp_dir =3D + bitmaps ? &bitmaps->bitmap_dir->dir_entries : NULL; =20 if (s->nb_bitmaps =3D=3D 0) { return 0; } =20 + if (bitmaps) { + bitmaps->nb_bitmaps =3D s->nb_bitmaps; + } + ret =3D qcow2_inc_refcounts_imrt(bs, res, refcount_table, refcount_tab= le_size, s->bitmap_directory_offset, - s->bitmap_directory_size); + s->bitmap_directory_size, + bitmaps ? bitmaps->bitmap_dir->location + : NULL); if (ret < 0) { return ret; } @@ -686,12 +695,28 @@ int qcow2_check_bitmaps_refcounts(BlockDriverState *b= s, BdrvCheckResult *res, =20 QSIMPLEQ_FOREACH(bm, bm_list, entry) { uint64_t *bitmap_table =3D NULL; + Qcow2BitmapTableInfoEntryList **pp_table; int i; =20 + Qcow2BitmapDirectoryEntry *bmde =3D NULL; + if (bitmaps) { + bmde =3D g_new0(Qcow2BitmapDirectoryEntry, 1); + bmde->bitmap_name =3D g_strdup(bm->name); + bmde->bitmap_table =3D g_new0(Qcow2BitmapTableInfo, 1); + bmde->bitmap_table->location =3D g_new0(Qcow2Allocation, 1); + Qcow2BitmapDirectoryEntryList *obj =3D + g_new0(Qcow2BitmapDirectoryEntryList, 1); + obj->value =3D bmde; + *pp_dir =3D obj; + pp_dir =3D &obj->next; + } + ret =3D qcow2_inc_refcounts_imrt(bs, res, refcount_table, refcount_table_size, bm->table.offset, - bm->table.size * sizeof(uint64_t)); + bm->table.size * sizeof(uint64_t), + bmde ? bmde->bitmap_table->location + : NULL); if (ret < 0) { goto out; } @@ -702,6 +727,8 @@ int qcow2_check_bitmaps_refcounts(BlockDriverState *bs,= BdrvCheckResult *res, goto out; } =20 + pp_table =3D bmde ? &bmde->bitmap_table->table_entries : NULL; + for (i =3D 0; i < bm->table.size; ++i) { uint64_t entry =3D bitmap_table[i]; uint64_t offset =3D entry & BME_TABLE_ENTRY_OFFSET_MASK; @@ -711,13 +738,32 @@ int qcow2_check_bitmaps_refcounts(BlockDriverState *b= s, BdrvCheckResult *res, continue; } =20 + Qcow2BitmapTableInfoEntry *bmte =3D NULL; + if (bmde) { + bmte =3D g_new0(Qcow2BitmapTableInfoEntry, 1); + bmte->type =3D offset ? + QCOW2_BITMAP_TABLE_INFO_ENTRY_TYPE_SERIALIZED : + entry & BME_TABLE_ENTRY_FLAG_ALL_ONES; + if (offset) { + bmte->cluster =3D g_new0(Qcow2Allocation, 1); + } + bmte->has_cluster =3D !!(bmte->cluster); + Qcow2BitmapTableInfoEntryList *elem =3D + g_new0(Qcow2BitmapTableInfoEntryList, 1); + elem->value =3D bmte; + *pp_table =3D elem; + pp_table =3D &elem->next; + } + if (offset =3D=3D 0) { continue; } =20 ret =3D qcow2_inc_refcounts_imrt(bs, res, refcount_table, refcount_table_= size, - offset, s->cluster_size); + offset, s->cluster_size, + bmte && bmte->cluster ? bmte->c= luster + : NULL); if (ret < 0) { g_free(bitmap_table); goto out; diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index f67ac6b..f5444fc 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -1521,7 +1521,8 @@ static int realloc_refcount_array(BDRVQcow2State *s, = void **array, int qcow2_inc_refcounts_imrt(BlockDriverState *bs, BdrvCheckResult *res, void **refcount_table, int64_t *refcount_table_size, - int64_t offset, int64_t size) + int64_t offset, int64_t size, + Qcow2Allocation *qcow2_alloc) { BDRVQcow2State *s =3D bs->opaque; uint64_t start, last, cluster_offset, k, refcount; @@ -1550,6 +1551,11 @@ int qcow2_inc_refcounts_imrt(BlockDriverState *bs, B= drvCheckResult *res, return 0; } =20 + if (qcow2_alloc) { + qcow2_alloc->offset =3D offset; + qcow2_alloc->size =3D size; + } + start =3D start_of_cluster(s, offset); last =3D start_of_cluster(s, offset + size - 1); for(cluster_offset =3D start; cluster_offset <=3D last; @@ -1643,7 +1649,7 @@ static int check_refcounts_l2(BlockDriverState *bs, B= drvCheckResult *res, ret =3D qcow2_inc_refcounts_imrt( bs, res, refcount_table, refcount_table_size, l2_entry & QCOW2_COMPRESSED_SECTOR_MASK, - nb_csectors * QCOW2_COMPRESSED_SECTOR_SIZE); + nb_csectors * QCOW2_COMPRESSED_SECTOR_SIZE, NULL); if (ret < 0) { goto fail; } @@ -1731,7 +1737,7 @@ static int check_refcounts_l2(BlockDriverState *bs, B= drvCheckResult *res, if (!has_data_file(bs)) { ret =3D qcow2_inc_refcounts_imrt(bs, res, refcount_table, refcount_table_size, - offset, s->cluster_size); + offset, s->cluster_size, NU= LL); if (ret < 0) { goto fail; } @@ -1769,17 +1775,20 @@ 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, BdrvCheckMode fix, bool active) + int flags, BdrvCheckMode fix, bool active, + Qcow2L1Table *l1t) { BDRVQcow2State *s =3D bs->opaque; uint64_t *l1_table =3D NULL, l2_offset, l1_size2; + Qcow2AllocationList **plist =3D l1t ? &l1t->l2_list : NULL; int i, ret; =20 l1_size2 =3D l1_size * sizeof(uint64_t); =20 /* Mark L1 table as used */ ret =3D qcow2_inc_refcounts_imrt(bs, res, refcount_table, refcount_tab= le_size, - l1_table_offset, l1_size2); + l1_table_offset, l1_size2, + l1t ? l1t->location : NULL); if (ret < 0) { goto fail; } @@ -1808,9 +1817,19 @@ static int check_refcounts_l1(BlockDriverState *bs, if (l2_offset) { /* Mark L2 table as used */ l2_offset &=3D L1E_OFFSET_MASK; + + Qcow2Allocation *l2t =3D NULL; + if (l1t) { + l2t =3D g_new0(Qcow2Allocation, 1); + Qcow2AllocationList *obj =3D g_new0(Qcow2AllocationList, 1= ); + obj->value =3D l2t; + *plist =3D obj; + plist =3D &obj->next; + } + ret =3D qcow2_inc_refcounts_imrt(bs, res, refcount_table, refcount_table_= size, - l2_offset, s->cluster_size); + l2_offset, s->cluster_size, l2t= ); if (ret < 0) { goto fail; } @@ -2047,7 +2066,7 @@ static int check_refblocks(BlockDriverState *bs, Bdrv= CheckResult *res, res->corruptions_fixed++; ret =3D qcow2_inc_refcounts_imrt(bs, res, refcount_table, nb_clusters, - offset, s->cluster_size); + offset, s->cluster_size, NU= LL); if (ret < 0) { return ret; } @@ -2066,7 +2085,7 @@ resize_fail: =20 if (offset !=3D 0) { ret =3D qcow2_inc_refcounts_imrt(bs, res, refcount_table, nb_c= lusters, - offset, s->cluster_size); + offset, s->cluster_size, NULL); if (ret < 0) { return ret; } @@ -2093,6 +2112,9 @@ static int calculate_refcounts(BlockDriverState *bs, = BdrvCheckResult *res, BDRVQcow2State *s =3D bs->opaque; int64_t i; QCowSnapshot *sn; + bool has_snapshots =3D res->metadata && res->metadata->snapshot_table; + Qcow2L1TableList **plist =3D has_snapshots ? + &res->metadata->snapshot_table->l1_list : NULL; int ret; =20 if (!*refcount_table) { @@ -2106,16 +2128,25 @@ static int calculate_refcounts(BlockDriverState *bs= , BdrvCheckResult *res, } =20 /* header */ + if (res->metadata) { + res->metadata->qcow2_header->version =3D s->qcow_version; + } ret =3D qcow2_inc_refcounts_imrt(bs, res, refcount_table, nb_clusters, - 0, s->cluster_size); + 0, s->cluster_size, + res->metadata ? + res->metadata->qcow2_header->location := NULL); if (ret < 0) { return ret; } =20 /* current L1 table */ + if (res->metadata) { + res->metadata->active_l1->name =3D g_strdup("L1 active table"); + } ret =3D check_refcounts_l1(bs, res, refcount_table, nb_clusters, s->l1_table_offset, s->l1_size, CHECK_FRAG_IN= FO, - fix, true); + fix, true, + res->metadata ? res->metadata->active_l1 : NU= LL); if (ret < 0) { return ret; } @@ -2143,15 +2174,30 @@ static int calculate_refcounts(BlockDriverState *bs= , BdrvCheckResult *res, res->corruptions++; continue; } + + Qcow2L1Table *l1t =3D NULL; + if (has_snapshots) { + l1t =3D g_new0(Qcow2L1Table, 1); + l1t->location =3D g_new0(Qcow2Allocation, 1); + l1t->name =3D g_strdup(sn->name); + Qcow2L1TableList *obj =3D g_new0(Qcow2L1TableList, 1); + obj->value =3D l1t; + *plist =3D obj; + plist =3D &obj->next; + } + ret =3D check_refcounts_l1(bs, res, refcount_table, nb_clusters, sn->l1_table_offset, sn->l1_size, 0, fix, - false); + false, l1t); if (ret < 0) { return ret; } } ret =3D qcow2_inc_refcounts_imrt(bs, res, refcount_table, nb_clusters, - s->snapshots_offset, s->snapshots_size); + s->snapshots_offset, s->snapshots_size, + has_snapshots ? + res->metadata->snapshot_table->location + : NULL); if (ret < 0) { return ret; } @@ -2159,7 +2205,10 @@ static int calculate_refcounts(BlockDriverState *bs,= BdrvCheckResult *res, /* refcount data */ ret =3D qcow2_inc_refcounts_imrt(bs, res, refcount_table, nb_clusters, s->refcount_table_offset, - s->refcount_table_size * sizeof(uint64_= t)); + s->refcount_table_size * sizeof(uint64_= t), + res->metadata ? + res->metadata->refcount_table->location + : NULL); if (ret < 0) { return ret; } @@ -2168,14 +2217,19 @@ static int calculate_refcounts(BlockDriverState *bs= , BdrvCheckResult *res, if (s->crypto_header.length) { ret =3D qcow2_inc_refcounts_imrt(bs, res, refcount_table, nb_clust= ers, s->crypto_header.offset, - s->crypto_header.length); + s->crypto_header.length, + res->metadata ? + res->metadata->crypt_header->locati= on + : NULL); if (ret < 0) { return ret; } } =20 /* bitmaps */ - ret =3D qcow2_check_bitmaps_refcounts(bs, res, refcount_table, nb_clus= ters); + ret =3D qcow2_check_bitmaps_refcounts(bs, res, refcount_table, nb_clus= ters, + res->metadata ? res->metadata->bit= maps + : NULL); if (ret < 0) { return ret; } diff --git a/block/qcow2.c b/block/qcow2.c index cef9d72..634b642 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -590,6 +590,7 @@ static int coroutine_fn qcow2_co_check_locked(BlockDriv= erState *bs, BdrvCheckResult *result, BdrvCheckMode fix) { + BDRVQcow2State *s =3D bs->opaque; BdrvCheckResult snapshot_res =3D {}; BdrvCheckResult refcount_res =3D {}; int ret; @@ -602,6 +603,35 @@ static int coroutine_fn qcow2_co_check_locked(BlockDri= verState *bs, return ret; } =20 + if (fix & BDRV_DUMP_META) { + result->metadata =3D g_new0(Qcow2Metadata, 1); + result->metadata->qcow2_header =3D g_new0(Qcow2Header, 1); + result->metadata->qcow2_header->location =3D g_new0(Qcow2Allocatio= n, 1); + result->metadata->active_l1 =3D g_new0(Qcow2L1Table, 1); + result->metadata->active_l1->location =3D g_new0(Qcow2Allocation, = 1); + result->metadata->refcount_table =3D g_new0(Qcow2RefcountTable, 1); + result->metadata->refcount_table->location =3D g_new0(Qcow2Allocat= ion, 1); + + refcount_res.metadata =3D result->metadata; + + if (s->crypto_header.length) { + result->metadata->crypt_header =3D g_new0(Qcow2EncryptionHeade= r, 1); + result->metadata->crypt_header->location =3D + g_new0(Qcow2Allocation, 1); + } + if (s->nb_bitmaps) { + result->metadata->bitmaps =3D g_new0(Qcow2Bitmaps, 1); + result->metadata->bitmaps->bitmap_dir =3D g_new0(Qcow2BitmapDi= r, 1); + result->metadata->bitmaps->bitmap_dir->location =3D + g_new0(Qcow2Allocation, 1); + } + if (s->nb_snapshots) { + result->metadata->snapshot_table =3D g_new0(Qcow2SnapshotsTabl= e, 1); + result->metadata->snapshot_table->location =3D + g_new0(Qcow2Allocation, 1); + } + } + ret =3D qcow2_check_refcounts(bs, &refcount_res, fix); qcow2_add_check_result(result, &refcount_res, true); if (ret < 0) { diff --git a/block/qcow2.h b/block/qcow2.h index 0942126..8d615e2 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -662,7 +662,8 @@ int qcow2_pre_write_overlap_check(BlockDriverState *bs,= int ign, int64_t offset, int qcow2_inc_refcounts_imrt(BlockDriverState *bs, BdrvCheckResult *res, void **refcount_table, int64_t *refcount_table_size, - int64_t offset, int64_t size); + int64_t offset, int64_t size, + Qcow2Allocation *qcow2_alloc); =20 int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order, BlockDriverAmendStatusCB *status_cb, @@ -751,7 +752,8 @@ void qcow2_cache_discard(Qcow2Cache *c, void *table); /* qcow2-bitmap.c functions */ int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *r= es, void **refcount_table, - int64_t *refcount_table_size); + int64_t *refcount_table_size, + Qcow2Bitmaps *bitmaps); bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp); Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs, Error **errp); diff --git a/include/block/block.h b/include/block/block.h index e9dcfef..cfc9d68 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -374,7 +374,6 @@ int bdrv_freeze_backing_chain(BlockDriverState *bs, Blo= ckDriverState *base, Error **errp); void bdrv_unfreeze_backing_chain(BlockDriverState *bs, BlockDriverState *b= ase); =20 - typedef struct BdrvCheckResult { int corruptions; int leaks; @@ -383,11 +382,13 @@ typedef struct BdrvCheckResult { int leaks_fixed; int64_t image_end_offset; BlockFragInfo bfi; + Qcow2Metadata *metadata; } BdrvCheckResult; =20 typedef enum { BDRV_FIX_LEAKS =3D 1, BDRV_FIX_ERRORS =3D 2, + BDRV_DUMP_META =3D 4, } BdrvCheckMode; =20 int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode f= ix); diff --git a/qemu-img.c b/qemu-img.c index 6233b8c..84e9f56 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -173,6 +173,7 @@ static void QEMU_NORETURN help(void) " '-r leaks' repairs only cluster leaks, whereas '-r all'= fixes all\n" " kinds of errors, with a higher risk of choosing the wro= ng fix or\n" " hiding corruption that has already occurred.\n" + " '-M' Dump qcow2 metadata to stdout in JSON format.\n" "\n" "Parameters to convert subcommand:\n" " '-m' specifies how many coroutines work in parallel during t= he convert\n" @@ -659,6 +660,15 @@ static int collect_image_check(BlockDriverState *bs, check->has_fragmented_clusters =3D result.bfi.fragmented_clusters != =3D 0; check->compressed_clusters =3D result.bfi.compressed_clusters; check->has_compressed_clusters =3D result.bfi.compressed_clusters != =3D 0; + check->metadata =3D result.metadata; + check->has_metadata =3D !!(result.metadata); + + if (check->has_metadata) { + check->metadata->has_crypt_header =3D !!(check->metadata->crypt_he= ader); + check->metadata->has_bitmaps =3D !!(check->metadata->bitmaps); + check->metadata->has_snapshot_table =3D + !!(check->metadata->snapshot_table); + } =20 return 0; } @@ -701,9 +711,10 @@ static int img_check(int argc, char **argv) {"object", required_argument, 0, OPTION_OBJECT}, {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, {"force-share", no_argument, 0, 'U'}, + {"dump-meta", no_argument, 0, 'M'}, {0, 0, 0, 0} }; - c =3D getopt_long(argc, argv, ":hf:r:T:qU", + c =3D getopt_long(argc, argv, ":hf:Mr:T:qU", long_options, &option_index); if (c =3D=3D -1) { break; @@ -721,6 +732,9 @@ static int img_check(int argc, char **argv) case 'f': fmt =3D optarg; break; + case 'M': + fix |=3D BDRV_DUMP_META; + break; case 'r': flags |=3D BDRV_O_RDWR; =20 @@ -772,6 +786,11 @@ static int img_check(int argc, char **argv) return 1; } =20 + if ((fix & BDRV_DUMP_META) && output_format !=3D OFORMAT_JSON) { + error_report("Metadata output is in JSON format only"); + return 1; + } + if (qemu_opts_foreach(&qemu_object_opts, user_creatable_add_opts_foreach, qemu_img_object_print_help, &error_fatal)) { @@ -792,6 +811,15 @@ static int img_check(int argc, char **argv) bs =3D blk_bs(blk); =20 check =3D g_new0(ImageCheck, 1); + + if (fix & BDRV_DUMP_META) { + if (strcmp(bs->drv->format_name, "qcow2")) { + error_report("Metadata output supported for QCOW2 format only"= ); + ret =3D -ENOTSUP; + goto fail; + } + } + ret =3D collect_image_check(bs, check, filename, fmt, fix); =20 if (ret =3D=3D -ENOTSUP) { diff --git a/qemu-img.texi b/qemu-img.texi index b5156d6..080e46a 100644 --- a/qemu-img.texi +++ b/qemu-img.texi @@ -230,7 +230,7 @@ specified as well. For write tests, by default a buffer filled with zeros is written. This ca= n be overridden with a pattern byte specified by @var{pattern}. =20 -@item check [--object @var{objectdef}] [--image-opts] [-q] [-f @var{fmt}] = [--output=3D@var{ofmt}] [-r [leaks | all]] [-T @var{src_cache}] [-U] @var{f= ilename} +@item check [--object @var{objectdef}] [--image-opts] [-M] [-q] [-f @var{f= mt}] [--output=3D@var{ofmt}] [-r [leaks | all]] [-T @var{src_cache}] [-U] @= var{filename} =20 Perform a consistency check on the disk image @var{filename}. The command = can output in the format @var{ofmt} which is either @code{human} or @code{json= }. @@ -241,6 +241,10 @@ during the check. @code{-r leaks} repairs only cluster= leaks, whereas @code{-r all} fixes all kinds of errors, with a higher risk of choosing the wrong fix or hiding corruption that has already occurred. =20 +If @code{-M} is specified, qemu-img dumps metadata allocations in the imag= e. +This option works with @code{json} format output and is effective for the +@code{qcow2} format only. + Only the formats @code{qcow2}, @code{qed} and @code{vdi} support consistency checks. =20 --=20 1.8.3.1