From nobody Wed Oct 8 06:10:20 2025 Delivered-To: importer@patchew.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=1590504937; cv=none; d=zohomail.com; s=zohoarc; b=IsRFEjtqS4ptBagiBDs1TLVYM39xpicQd5MN3BcEa5wSEQhFpttus4eMFaM8/6avY5g2wClb4BOYhHkkY0TbzfeDTZjMy+ENtvCjpK+oHDY666BchtmCKCZgyGK0Yrbd/RQhnb1RRGjQDEHmLS61EnI7jHfiAYH98Wk8OonRawY= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1590504937; h=Cc:Date:From:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:Message-ID:Sender:Subject:To; bh=ZBbjO3AIjyI9Od1jAkn/2gfgCcrnJQ55FigiRGD2vhE=; b=oGNet5YdqvtUEECikg1V1eIO7zcSuTFbsli6dHOcnH4/PaYvV18o/qyWAfiE9DRHudh9uCyEeEHm5wRm0ktzbSVo2qgqziTIs73Yg7aZG0VxMcs8eNwAzNqu5OJcJGkOqIPzVB+MrKUovlhwp79VDILyKQJNB4BVKPtZ8BoeVCQ= 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 1590504937734736.7865485022097; Tue, 26 May 2020 07:55:37 -0700 (PDT) Received: from localhost ([::1]:49698 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jdazQ-000233-7W for importer@patchew.org; Tue, 26 May 2020 10:55:36 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:43214) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jdayX-0000hf-5J; Tue, 26 May 2020 10:54:41 -0400 Received: from relay.sw.ru ([185.231.240.75]:37058) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jdayU-0005yA-FQ; Tue, 26 May 2020 10:54:39 -0400 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 1jdayL-0001Qx-0O; Tue, 26 May 2020 17:54:29 +0300 From: Andrey Shinkevich To: qemu-block@nongnu.org Subject: [PATCH] iotests: Dump QCOW2 dirty bitmaps metadata Date: Tue, 26 May 2020 17:54:26 +0300 Message-Id: <1590504866-679474-1-git-send-email-andrey.shinkevich@virtuozzo.com> X-Mailer: git-send-email 1.8.3.1 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; Received-SPF: pass client-ip=185.231.240.75; envelope-from=andrey.shinkevich@virtuozzo.com; helo=relay.sw.ru X-detected-operating-system: by eggs.gnu.org: First seen = 2020/05/26 10:54:34 X-ACL-Warn: Detected OS = Linux 3.1-3.10 X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, SPF_PASS=-0.001, URIBL_BLOCKED=0.001 autolearn=_AUTOLEARN X-Spam_action: no action 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, qemu-devel@nongnu.org, mreitz@redhat.com, andrey.shinkevich@virtuozzo.com, den@openvz.org 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" Add dirty bitmap information to QCOW2 metadata dump in qcow2.py script. The sample output: Header extension (Bitmaps): magic 0x23852875 length 24 nb_bitmaps 2 reserved32 0 bitmap_directory_size 0x40 bitmap_directory_offset 0x100000 Bitmap name bitmap-1 flag "auto" bitmap_table_offset 0x90000 bitmap_table_size 8 flags 2 type 1 granularity_bits 15 name_size 8 extra_data_size 0 Bitmap table 0 serialized, offset 0xa0000 1 all-zeroes, offset 0x0 2 all-zeroes, offset 0x0 3 all-zeroes, offset 0x0 4 all-zeroes, offset 0x0 5 all-zeroes, offset 0x0 6 all-zeroes, offset 0x0 7 all-zeroes, offset 0x0 Signed-off-by: Andrey Shinkevich --- tests/qemu-iotests/qcow2.py | 149 ++++++++++++++++++++++++++++++++++++++++= +--- 1 file changed, 141 insertions(+), 8 deletions(-) diff --git a/tests/qemu-iotests/qcow2.py b/tests/qemu-iotests/qcow2.py index 91e4420..41aa030 100755 --- a/tests/qemu-iotests/qcow2.py +++ b/tests/qemu-iotests/qcow2.py @@ -5,6 +5,122 @@ import sys import struct import string =20 + +class Qcow2BitmapDirEntry: + + name =3D '' + BME_FLAG_IN_USE =3D 1 + BME_FLAG_AUTO =3D 1 << 1 + + uint8_t =3D 'B' + uint16_t =3D 'H' + uint32_t =3D 'I' + uint64_t =3D 'Q' + + fields =3D [ + [uint64_t, '%#x', 'bitmap_table_offset'], + [uint32_t, '%d', 'bitmap_table_size'], + [uint32_t, '%d', 'flags'], + [uint8_t, '%d', 'type'], + [uint8_t, '%d', 'granularity_bits'], + [uint16_t, '%d', 'name_size'], + [uint32_t, '%d', 'extra_data_size'] + ] + + fmt =3D '>' + ''.join(field[0] for field in fields) + + def __init__(self, data): + + entry =3D struct.unpack(Qcow2BitmapDirEntry.fmt, data) + self.__dict__ =3D dict((field[2], entry[i]) + for i, field in enumerate( + Qcow2BitmapDirEntry.fields)) + + self.bitmap_table_size =3D self.bitmap_table_size \ + * struct.calcsize(self.uint64_t) + + def bitmap_dir_entry_size(self): + size =3D struct.calcsize(self.fmt) + self.name_size + \ + self.extra_data_size + return (size + 7) & ~7 + + def dump_bitmap_dir_entry(self): + print("%-25s" % 'Bitmap name', self.name) + if (self.flags & self.BME_FLAG_IN_USE) !=3D 0: + print("%-25s" % 'flag', '"in-use"') + if (self.flags & self.BME_FLAG_AUTO) !=3D 0: + print("%-25s" % 'flag', '"auto"') + for f in Qcow2BitmapDirEntry.fields: + value =3D self.__dict__[f[2]] + value_str =3D f[1] % value + + print("%-25s" % f[2], value_str) + print("") + + def dump_bitmap_table(self, fd): + fd.seek(self.bitmap_table_offset) + table_size =3D self.bitmap_table_size * struct.calcsize(self.uint6= 4_t) + bitmap_table =3D [e[0] for e in struct.iter_unpack('>Q', + fd.read(table_siz= e))] + BME_TABLE_ENTRY_OFFSET_MASK =3D 0x00fffffffffffe00 + BME_TABLE_ENTRY_FLAG_ALL_ONES =3D 1 + bmt_type =3D ['all-zeroes', 'all-ones', 'serialized'] + items =3D enumerate(bitmap_table) + print("Bitmap table") + for i, entry in items: + offset =3D entry & BME_TABLE_ENTRY_OFFSET_MASK + if offset !=3D 0: + index =3D 2 + else: + index =3D entry & BME_TABLE_ENTRY_FLAG_ALL_ONES + print(" %-4d %s, offset %#x" % (i, bmt_type[index], offset)) + print("") + + +class Qcow2BitmapExt: + + uint32_t =3D 'I' + uint64_t =3D 'Q' + + fields =3D [ + [uint32_t, '%d', 'nb_bitmaps'], + [uint32_t, '%d', 'reserved32'], + [uint64_t, '%#x', 'bitmap_directory_size'], + [uint64_t, '%#x', 'bitmap_directory_offset'] + ] + + fmt =3D '>' + ''.join(field[0] for field in fields) + + def __init__(self, data): + + extension =3D struct.unpack(Qcow2BitmapExt.fmt, data) + self.__dict__ =3D dict((field[2], extension[i]) + for i, field in enumerate(Qcow2BitmapExt.fiel= ds)) + + def dump_bitmap_ext(self): + for f in Qcow2BitmapExt.fields: + value =3D self.__dict__[f[2]] + value_str =3D f[1] % value + + print("%-25s" % f[2], value_str) + print("") + + def bitmap_directory(self, fd): + offset =3D self.bitmap_directory_offset + buf_size =3D struct.calcsize(Qcow2BitmapDirEntry.fmt) + + for n in range(self.nb_bitmaps): + fd.seek(offset) + buf =3D fd.read(buf_size) + dir_entry =3D Qcow2BitmapDirEntry(buf) + fd.seek(dir_entry.extra_data_size, 1) + bitmap_name =3D fd.read(dir_entry.name_size) + dir_entry.name =3D bitmap_name.decode('ascii') + dir_entry.dump_bitmap_dir_entry() + dir_entry.dump_bitmap_table(fd) + offset +=3D dir_entry.bitmap_dir_entry_size() + + class QcowHeaderExtension: =20 def __init__(self, magic, length, data): @@ -22,6 +138,8 @@ class QcowHeaderExtension: =20 class QcowHeader: =20 + QCOW2_EXT_MAGIC_FEATURE_TABLE =3D 0x6803f857 + QCOW2_EXT_MAGIC_BITMAPS =3D 0x23852875 uint32_t =3D 'I' uint64_t =3D 'Q' =20 @@ -128,6 +246,12 @@ class QcowHeader: buf =3D buf[0:header_bytes-1] fd.write(buf) =20 + def extension_name(self, magic): + return { + self.QCOW2_EXT_MAGIC_FEATURE_TABLE: 'Feature table', + self.QCOW2_EXT_MAGIC_BITMAPS: 'Bitmaps', + }.get(magic, 'Unknown') + def dump(self): for f in QcowHeader.fields: value =3D self.__dict__[f[2]] @@ -143,30 +267,39 @@ class QcowHeader: print("%-25s" % f[2], value_str) print("") =20 - def dump_extensions(self): + def dump_extensions(self, fd): for ex in self.extensions: =20 + print("Header extension (%s):" % self.extension_name(ex.magic)) + print("%-25s %#x" % ("magic", ex.magic)) + print("%-25s %d" % ("length", ex.length)) + data =3D ex.data[:ex.length] if all(c in string.printable.encode('ascii') for c in data): data =3D "'%s'" % data.decode('ascii') + print("%-25s %s" % ("data", data)) else: - data =3D "" + self.dump_extension_data(fd, ex) =20 - print("Header extension:") - print("%-25s %#x" % ("magic", ex.magic)) - print("%-25s %d" % ("length", ex.length)) - print("%-25s %s" % ("data", data)) print("") =20 + def dump_extension_data(self, fd, ext): + if ext.magic =3D=3D self.QCOW2_EXT_MAGIC_BITMAPS: + b_ext =3D Qcow2BitmapExt(ext.data) + b_ext.dump_bitmap_ext() + b_ext.bitmap_directory(fd) + else: + print("%-25s %s" % ("data", "")) + =20 def cmd_dump_header(fd): h =3D QcowHeader(fd) h.dump() - h.dump_extensions() + h.dump_extensions(fd) =20 def cmd_dump_header_exts(fd): h =3D QcowHeader(fd) - h.dump_extensions() + h.dump_extensions(fd) =20 def cmd_set_header(fd, name, value): try: --=20 1.8.3.1