qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v8 00/10] iotests: Dump QCOW2 dirty bitmaps metadata
@ 2020-07-03 13:13 Andrey Shinkevich
  2020-07-03 13:13 ` [PATCH v8 01/10] qcow2: Fix capitalization of header extension constant Andrey Shinkevich
                   ` (9 more replies)
  0 siblings, 10 replies; 21+ messages in thread
From: Andrey Shinkevich @ 2020-07-03 13:13 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, vsementsov, qemu-devel, mreitz, andrey.shinkevich, den

Add dirty bitmap information to QCOW2 metadata dump in the qcow2_format.py.

v8:
  01: Hard refactoring of the v7 code in qcow2_format.py based on Vladimir's
      review. See the thread with the message ID:
      <1591920302-1002219-1-git-send-email-andrey.shinkevich@virtuozzo.com>

Andrey Shinkevich (10):
  qcow2: Fix capitalization of header extension constant.
  qcow2_format.py: make printable data an extension class member
  qcow2_format.py: change Qcow2BitmapExt initialization method
  qcow2_format.py: dump bitmap flags in human readable way.
  qcow2_format.py: Dump bitmap directory information
  qcow2_format.py: pass cluster size to substructures
  qcow2_format.py: Dump bitmap table serialized entries
  qcow2.py: Introduce '-j' key to dump in JSON format
  qcow2_format.py: collect fields to dump in JSON format
  qcow2_format.py: support dumping metadata in JSON format

 block/qcow2.c                      |   2 +-
 docs/interop/qcow2.txt             |   2 +-
 tests/qemu-iotests/291.out         |  90 ++++++++++++++++
 tests/qemu-iotests/qcow2.py        |  19 +++-
 tests/qemu-iotests/qcow2_format.py | 212 ++++++++++++++++++++++++++++++++++---
 5 files changed, 304 insertions(+), 21 deletions(-)

-- 
1.8.3.1



^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v8 01/10] qcow2: Fix capitalization of header extension constant.
  2020-07-03 13:13 [PATCH v8 00/10] iotests: Dump QCOW2 dirty bitmaps metadata Andrey Shinkevich
@ 2020-07-03 13:13 ` Andrey Shinkevich
  2020-07-03 13:13 ` [PATCH v8 02/10] qcow2_format.py: make printable data an extension class member Andrey Shinkevich
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 21+ messages in thread
From: Andrey Shinkevich @ 2020-07-03 13:13 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, vsementsov, qemu-devel, mreitz, andrey.shinkevich, den

Make the capitalization of the hexadecimal numbers consistent for the
QCOW2 header extension constants in docs/interop/qcow2.txt.

Suggested-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 block/qcow2.c          | 2 +-
 docs/interop/qcow2.txt | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/block/qcow2.c b/block/qcow2.c
index 0cd2e67..80dfe5f 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -66,7 +66,7 @@ typedef struct {
 } QEMU_PACKED QCowExtension;
 
 #define  QCOW2_EXT_MAGIC_END 0
-#define  QCOW2_EXT_MAGIC_BACKING_FORMAT 0xE2792ACA
+#define  QCOW2_EXT_MAGIC_BACKING_FORMAT 0xe2792aca
 #define  QCOW2_EXT_MAGIC_FEATURE_TABLE 0x6803f857
 #define  QCOW2_EXT_MAGIC_CRYPTO_HEADER 0x0537be77
 #define  QCOW2_EXT_MAGIC_BITMAPS 0x23852875
diff --git a/docs/interop/qcow2.txt b/docs/interop/qcow2.txt
index cb72346..f072e27 100644
--- a/docs/interop/qcow2.txt
+++ b/docs/interop/qcow2.txt
@@ -231,7 +231,7 @@ be stored. Each extension has a structure like the following:
 
     Byte  0 -  3:   Header extension type:
                         0x00000000 - End of the header extension area
-                        0xE2792ACA - Backing file format name string
+                        0xe2792aca - Backing file format name string
                         0x6803f857 - Feature name table
                         0x23852875 - Bitmaps extension
                         0x0537be77 - Full disk encryption header pointer
-- 
1.8.3.1



^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v8 02/10] qcow2_format.py: make printable data an extension class member
  2020-07-03 13:13 [PATCH v8 00/10] iotests: Dump QCOW2 dirty bitmaps metadata Andrey Shinkevich
  2020-07-03 13:13 ` [PATCH v8 01/10] qcow2: Fix capitalization of header extension constant Andrey Shinkevich
@ 2020-07-03 13:13 ` Andrey Shinkevich
  2020-07-03 13:13 ` [PATCH v8 03/10] qcow2_format.py: change Qcow2BitmapExt initialization method Andrey Shinkevich
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 21+ messages in thread
From: Andrey Shinkevich @ 2020-07-03 13:13 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, vsementsov, qemu-devel, mreitz, andrey.shinkevich, den

Let us differ binary data type from string one for the extension data
variable and keep the string as the QcowHeaderExtension class member.

Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 tests/qemu-iotests/qcow2_format.py | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/tests/qemu-iotests/qcow2_format.py b/tests/qemu-iotests/qcow2_format.py
index cc432e7..2f3681b 100644
--- a/tests/qemu-iotests/qcow2_format.py
+++ b/tests/qemu-iotests/qcow2_format.py
@@ -165,6 +165,13 @@ class QcowHeaderExtension(Qcow2Struct):
             self.data = fd.read(padded)
             assert self.data is not None
 
+        data_str = self.data[:self.length]
+        if all(c in string.printable.encode('ascii') for c in data_str):
+            data_str = f"'{ data_str.decode('ascii') }'"
+        else:
+            data_str = '<binary>'
+        self.data_str = data_str
+
         if self.magic == QCOW2_EXT_MAGIC_BITMAPS:
             self.obj = Qcow2BitmapExt(data=self.data)
         else:
@@ -174,12 +181,7 @@ class QcowHeaderExtension(Qcow2Struct):
         super().dump()
 
         if self.obj is None:
-            data = self.data[:self.length]
-            if all(c in string.printable.encode('ascii') for c in data):
-                data = f"'{ data.decode('ascii') }'"
-            else:
-                data = '<binary>'
-            print(f'{"data":<25} {data}')
+            print(f'{"data":<25} {self.data_str}')
         else:
             self.obj.dump()
 
-- 
1.8.3.1



^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v8 03/10] qcow2_format.py: change Qcow2BitmapExt initialization method
  2020-07-03 13:13 [PATCH v8 00/10] iotests: Dump QCOW2 dirty bitmaps metadata Andrey Shinkevich
  2020-07-03 13:13 ` [PATCH v8 01/10] qcow2: Fix capitalization of header extension constant Andrey Shinkevich
  2020-07-03 13:13 ` [PATCH v8 02/10] qcow2_format.py: make printable data an extension class member Andrey Shinkevich
@ 2020-07-03 13:13 ` Andrey Shinkevich
  2020-07-11 16:34   ` Vladimir Sementsov-Ogievskiy
  2020-07-03 13:13 ` [PATCH v8 04/10] qcow2_format.py: dump bitmap flags in human readable way Andrey Shinkevich
                   ` (6 subsequent siblings)
  9 siblings, 1 reply; 21+ messages in thread
From: Andrey Shinkevich @ 2020-07-03 13:13 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, vsementsov, qemu-devel, mreitz, andrey.shinkevich, den

There are two ways to initialize a class derived from Qcow2Struct:
1. Pass a block of binary data to the constructor.
2. Pass the file descriptor to allow reading the file from constructor.
Let's change the Qcow2BitmapExt initialization method from 1 to 2 to
support a scattered reading in the initialization chain.
The implementation comes with the patch that follows.

Suggested-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
---
 tests/qemu-iotests/qcow2_format.py | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/tests/qemu-iotests/qcow2_format.py b/tests/qemu-iotests/qcow2_format.py
index 2f3681b..1435e34 100644
--- a/tests/qemu-iotests/qcow2_format.py
+++ b/tests/qemu-iotests/qcow2_format.py
@@ -63,7 +63,8 @@ class Qcow2StructMeta(type):
 
 class Qcow2Struct(metaclass=Qcow2StructMeta):
 
-    """Qcow2Struct: base class for qcow2 data structures
+    """
+    Qcow2Struct: base class for qcow2 data structures
 
     Successors should define fields class variable, which is: list of tuples,
     each of three elements:
@@ -113,6 +114,9 @@ class Qcow2BitmapExt(Qcow2Struct):
         ('u64', '{:#x}', 'bitmap_directory_offset')
     )
 
+    def __init__(self, fd):
+        super().__init__(fd=fd)
+
 
 QCOW2_EXT_MAGIC_BITMAPS = 0x23852875
 
@@ -173,7 +177,13 @@ class QcowHeaderExtension(Qcow2Struct):
         self.data_str = data_str
 
         if self.magic == QCOW2_EXT_MAGIC_BITMAPS:
-            self.obj = Qcow2BitmapExt(data=self.data)
+            assert fd is not None
+            position = fd.tell()
+            # Step back to reread data
+            padded = (self.length + 7) & ~7
+            fd.seek(-padded, 1)
+            self.obj = Qcow2BitmapExt(fd=fd)
+            fd.seek(position)
         else:
             self.obj = None
 
-- 
1.8.3.1



^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v8 04/10] qcow2_format.py: dump bitmap flags in human readable way.
  2020-07-03 13:13 [PATCH v8 00/10] iotests: Dump QCOW2 dirty bitmaps metadata Andrey Shinkevich
                   ` (2 preceding siblings ...)
  2020-07-03 13:13 ` [PATCH v8 03/10] qcow2_format.py: change Qcow2BitmapExt initialization method Andrey Shinkevich
@ 2020-07-03 13:13 ` Andrey Shinkevich
  2020-07-11 16:43   ` Vladimir Sementsov-Ogievskiy
  2020-07-03 13:13 ` [PATCH v8 05/10] qcow2_format.py: Dump bitmap directory information Andrey Shinkevich
                   ` (5 subsequent siblings)
  9 siblings, 1 reply; 21+ messages in thread
From: Andrey Shinkevich @ 2020-07-03 13:13 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, vsementsov, qemu-devel, mreitz, andrey.shinkevich, den

Introduce the class BitmapFlags that parses a bitmap flags mask.

Suggested-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
---
 tests/qemu-iotests/qcow2_format.py | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/tests/qemu-iotests/qcow2_format.py b/tests/qemu-iotests/qcow2_format.py
index 1435e34..d8c058d 100644
--- a/tests/qemu-iotests/qcow2_format.py
+++ b/tests/qemu-iotests/qcow2_format.py
@@ -40,6 +40,22 @@ class Flags64(Qcow2Field):
         return str(bits)
 
 
+class BitmapFlags(Qcow2Field):
+
+    flags = {
+        0x1: 'in-use',
+        0x2: 'auto'
+    }
+
+    def __str__(self):
+        bits = []
+        for bit in range(64):
+            flag = self.value & (1 << bit)
+            if flag:
+                bits.append(self.flags.get(flag, '{:#x}'.format(flag)))
+        return f'{self.value:#x} ({bits})'
+
+
 class Enum(Qcow2Field):
 
     def __str__(self):
-- 
1.8.3.1



^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v8 05/10] qcow2_format.py: Dump bitmap directory information
  2020-07-03 13:13 [PATCH v8 00/10] iotests: Dump QCOW2 dirty bitmaps metadata Andrey Shinkevich
                   ` (3 preceding siblings ...)
  2020-07-03 13:13 ` [PATCH v8 04/10] qcow2_format.py: dump bitmap flags in human readable way Andrey Shinkevich
@ 2020-07-03 13:13 ` Andrey Shinkevich
  2020-07-11 19:11   ` Vladimir Sementsov-Ogievskiy
  2020-07-16  9:13   ` Vladimir Sementsov-Ogievskiy
  2020-07-03 13:13 ` [PATCH v8 06/10] qcow2_format.py: pass cluster size to substructures Andrey Shinkevich
                   ` (4 subsequent siblings)
  9 siblings, 2 replies; 21+ messages in thread
From: Andrey Shinkevich @ 2020-07-03 13:13 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, vsementsov, qemu-devel, mreitz, andrey.shinkevich, den

Read and dump entries from the bitmap directory of QCOW2 image.
It extends the output in the test case #291.

Header extension:
magic                     0x23852875 (Bitmaps)
...

Bitmap name               bitmap-1
bitmap_table_offset       0xf0000
bitmap_table_size         1
flags                     0x2 (['auto'])
type                      1
granularity_bits          16
name_size                 8
extra_data_size           0

Suggested-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
---
 tests/qemu-iotests/291.out         | 45 ++++++++++++++++++++++++++++++++++++++
 tests/qemu-iotests/qcow2_format.py | 44 +++++++++++++++++++++++++++++++++++++
 2 files changed, 89 insertions(+)

diff --git a/tests/qemu-iotests/291.out b/tests/qemu-iotests/291.out
index 08bfaaa..53a8eeb 100644
--- a/tests/qemu-iotests/291.out
+++ b/tests/qemu-iotests/291.out
@@ -33,6 +33,24 @@ reserved32                0
 bitmap_directory_size     0x40
 bitmap_directory_offset   0x510000
 
+Bitmap name               b1
+bitmap_table_offset       0x4e0000
+bitmap_table_size         1
+flags                     0x0 ([])
+type                      1
+granularity_bits          19
+name_size                 2
+extra_data_size           0
+
+Bitmap name               b2
+bitmap_table_offset       0x500000
+bitmap_table_size         1
+flags                     0x2 (['auto'])
+type                      1
+granularity_bits          16
+name_size                 2
+extra_data_size           0
+
 
 === Bitmap preservation not possible to non-qcow2 ===
 
@@ -98,6 +116,33 @@ reserved32                0
 bitmap_directory_size     0x60
 bitmap_directory_offset   0x520000
 
+Bitmap name               b1
+bitmap_table_offset       0x470000
+bitmap_table_size         1
+flags                     0x0 ([])
+type                      1
+granularity_bits          19
+name_size                 2
+extra_data_size           0
+
+Bitmap name               b2
+bitmap_table_offset       0x490000
+bitmap_table_size         1
+flags                     0x2 (['auto'])
+type                      1
+granularity_bits          16
+name_size                 2
+extra_data_size           0
+
+Bitmap name               b0
+bitmap_table_offset       0x510000
+bitmap_table_size         1
+flags                     0x0 ([])
+type                      1
+granularity_bits          16
+name_size                 2
+extra_data_size           0
+
 
 === Check bitmap contents ===
 
diff --git a/tests/qemu-iotests/qcow2_format.py b/tests/qemu-iotests/qcow2_format.py
index d8c058d..7c0dc9a 100644
--- a/tests/qemu-iotests/qcow2_format.py
+++ b/tests/qemu-iotests/qcow2_format.py
@@ -132,6 +132,50 @@ class Qcow2BitmapExt(Qcow2Struct):
 
     def __init__(self, fd):
         super().__init__(fd=fd)
+        self.read_bitmap_directory(fd)
+
+    def read_bitmap_directory(self, fd):
+        fd.seek(self.bitmap_directory_offset)
+        self.bitmap_directory = \
+            [Qcow2BitmapDirEntry(fd) for _ in range(self.nb_bitmaps)]
+
+    def dump(self):
+        super().dump()
+        for entry in self.bitmap_directory:
+            print()
+            entry.dump()
+
+
+class Qcow2BitmapDirEntry(Qcow2Struct):
+
+    fields = (
+        ('u64', '{:#x}', 'bitmap_table_offset'),
+        ('u32', '{}', 'bitmap_table_size'),
+        ('u32', BitmapFlags, 'flags'),
+        ('u8',  '{}', 'type'),
+        ('u8',  '{}', 'granularity_bits'),
+        ('u16', '{}', 'name_size'),
+        ('u32', '{}', 'extra_data_size')
+    )
+
+    def __init__(self, fd):
+        super().__init__(fd=fd)
+        # Seek relative to the current position in the file
+        fd.seek(self.extra_data_size, 1)
+        bitmap_name = fd.read(self.name_size)
+        self.name = bitmap_name.decode('ascii')
+        # Move position to the end of the entry in the directory
+        entry_raw_size = self.bitmap_dir_entry_raw_size()
+        padding = ((entry_raw_size + 7) & ~7) - entry_raw_size
+        fd.seek(padding, 1)
+
+    def bitmap_dir_entry_raw_size(self):
+        return struct.calcsize(self.fmt) + self.name_size + \
+            self.extra_data_size
+
+    def dump(self):
+        print(f'{"Bitmap name":<25} {self.name}')
+        super(Qcow2BitmapDirEntry, self).dump()
 
 
 QCOW2_EXT_MAGIC_BITMAPS = 0x23852875
-- 
1.8.3.1



^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v8 06/10] qcow2_format.py: pass cluster size to substructures
  2020-07-03 13:13 [PATCH v8 00/10] iotests: Dump QCOW2 dirty bitmaps metadata Andrey Shinkevich
                   ` (4 preceding siblings ...)
  2020-07-03 13:13 ` [PATCH v8 05/10] qcow2_format.py: Dump bitmap directory information Andrey Shinkevich
@ 2020-07-03 13:13 ` Andrey Shinkevich
  2020-07-03 13:13 ` [PATCH v8 07/10] qcow2_format.py: Dump bitmap table serialized entries Andrey Shinkevich
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 21+ messages in thread
From: Andrey Shinkevich @ 2020-07-03 13:13 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, vsementsov, qemu-devel, mreitz, andrey.shinkevich, den

The cluster size of an image is the QcowHeader class member and may be
obtained by dependent extension structures such as Qcow2BitmapExt for
further bitmap table details print.

Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
---
 tests/qemu-iotests/qcow2_format.py | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/tests/qemu-iotests/qcow2_format.py b/tests/qemu-iotests/qcow2_format.py
index 7c0dc9a..cfd9646 100644
--- a/tests/qemu-iotests/qcow2_format.py
+++ b/tests/qemu-iotests/qcow2_format.py
@@ -130,14 +130,16 @@ class Qcow2BitmapExt(Qcow2Struct):
         ('u64', '{:#x}', 'bitmap_directory_offset')
     )
 
-    def __init__(self, fd):
+    def __init__(self, fd, cluster_size):
         super().__init__(fd=fd)
+        self.cluster_size = cluster_size
         self.read_bitmap_directory(fd)
 
     def read_bitmap_directory(self, fd):
         fd.seek(self.bitmap_directory_offset)
         self.bitmap_directory = \
-            [Qcow2BitmapDirEntry(fd) for _ in range(self.nb_bitmaps)]
+            [Qcow2BitmapDirEntry(fd, cluster_size=self.cluster_size)
+             for _ in range(self.nb_bitmaps)]
 
     def dump(self):
         super().dump()
@@ -158,8 +160,9 @@ class Qcow2BitmapDirEntry(Qcow2Struct):
         ('u32', '{}', 'extra_data_size')
     )
 
-    def __init__(self, fd):
+    def __init__(self, fd, cluster_size):
         super().__init__(fd=fd)
+        self.cluster_size = cluster_size
         # Seek relative to the current position in the file
         fd.seek(self.extra_data_size, 1)
         bitmap_name = fd.read(self.name_size)
@@ -199,11 +202,13 @@ class QcowHeaderExtension(Qcow2Struct):
         # then padding to next multiply of 8
     )
 
-    def __init__(self, magic=None, length=None, data=None, fd=None):
+    def __init__(self, magic=None, length=None, data=None, fd=None,
+                 cluster_size=None):
         """
         Support both loading from fd and creation from user data.
         For fd-based creation current position in a file will be used to read
         the data.
+        The cluster_size value may be obtained by dependent structures.
 
         This should be somehow refactored and functionality should be moved to
         superclass (to allow creation of any qcow2 struct), but then, fields
@@ -242,7 +247,7 @@ class QcowHeaderExtension(Qcow2Struct):
             # Step back to reread data
             padded = (self.length + 7) & ~7
             fd.seek(-padded, 1)
-            self.obj = Qcow2BitmapExt(fd=fd)
+            self.obj = Qcow2BitmapExt(fd=fd, cluster_size=cluster_size)
             fd.seek(position)
         else:
             self.obj = None
@@ -318,7 +323,7 @@ class QcowHeader(Qcow2Struct):
             end = self.cluster_size
 
         while fd.tell() < end:
-            ext = QcowHeaderExtension(fd=fd)
+            ext = QcowHeaderExtension(fd=fd, cluster_size=self.cluster_size)
             if ext.magic == 0:
                 break
             else:
-- 
1.8.3.1



^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v8 07/10] qcow2_format.py: Dump bitmap table serialized entries
  2020-07-03 13:13 [PATCH v8 00/10] iotests: Dump QCOW2 dirty bitmaps metadata Andrey Shinkevich
                   ` (5 preceding siblings ...)
  2020-07-03 13:13 ` [PATCH v8 06/10] qcow2_format.py: pass cluster size to substructures Andrey Shinkevich
@ 2020-07-03 13:13 ` Andrey Shinkevich
  2020-07-03 20:05   ` Andrey Shinkevich
  2020-07-03 13:13 ` [PATCH v8 08/10] qcow2.py: Introduce '-j' key to dump in JSON format Andrey Shinkevich
                   ` (2 subsequent siblings)
  9 siblings, 1 reply; 21+ messages in thread
From: Andrey Shinkevich @ 2020-07-03 13:13 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, vsementsov, qemu-devel, mreitz, andrey.shinkevich, den

Add bitmap table information to the QCOW2 metadata dump.
It extends the output of the test case #291

Bitmap name               bitmap-1
...
Bitmap table   type            offset                   size
0              serialized      4718592                  65536
1              serialized      4294967296               65536
2              serialized      5348033147437056         65536
3              serialized      13792273858822144        65536
4              serialized      4718592                  65536
5              serialized      4294967296               65536
6              serialized      4503608217305088         65536
7              serialized      14073748835532800        65536

Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
---
 tests/qemu-iotests/291.out         | 45 ++++++++++++++++++++++++++++++++++++++
 tests/qemu-iotests/qcow2_format.py | 42 +++++++++++++++++++++++++++++++++++
 2 files changed, 87 insertions(+)

diff --git a/tests/qemu-iotests/291.out b/tests/qemu-iotests/291.out
index 53a8eeb..9f465c6 100644
--- a/tests/qemu-iotests/291.out
+++ b/tests/qemu-iotests/291.out
@@ -41,6 +41,15 @@ type                      1
 granularity_bits          19
 name_size                 2
 extra_data_size           0
+Bitmap table   type            offset                   size
+0              serialized      5046272                  65536
+1              all-zeroes      0                        65536
+2              all-zeroes      0                        65536
+3              all-zeroes      0                        65536
+4              all-zeroes      0                        65536
+5              all-zeroes      0                        65536
+6              all-zeroes      0                        65536
+7              all-zeroes      0                        65536
 
 Bitmap name               b2
 bitmap_table_offset       0x500000
@@ -50,6 +59,15 @@ type                      1
 granularity_bits          16
 name_size                 2
 extra_data_size           0
+Bitmap table   type            offset                   size
+0              serialized      5177344                  65536
+1              all-zeroes      0                        65536
+2              all-zeroes      0                        65536
+3              all-zeroes      0                        65536
+4              all-zeroes      0                        65536
+5              all-zeroes      0                        65536
+6              all-zeroes      0                        65536
+7              all-zeroes      0                        65536
 
 
 === Bitmap preservation not possible to non-qcow2 ===
@@ -124,6 +142,15 @@ type                      1
 granularity_bits          19
 name_size                 2
 extra_data_size           0
+Bitmap table   type            offset                   size
+0              serialized      4587520                  65536
+1              all-zeroes      0                        65536
+2              all-zeroes      0                        65536
+3              all-zeroes      0                        65536
+4              all-zeroes      0                        65536
+5              all-zeroes      0                        65536
+6              all-zeroes      0                        65536
+7              all-zeroes      0                        65536
 
 Bitmap name               b2
 bitmap_table_offset       0x490000
@@ -133,6 +160,15 @@ type                      1
 granularity_bits          16
 name_size                 2
 extra_data_size           0
+Bitmap table   type            offset                   size
+0              serialized      4718592                  65536
+1              serialized      4294967296               65536
+2              serialized      5348033147437056         65536
+3              serialized      13792273858822144        65536
+4              serialized      4718592                  65536
+5              serialized      4294967296               65536
+6              serialized      4503608217305088         65536
+7              serialized      14073748835532800        65536
 
 Bitmap name               b0
 bitmap_table_offset       0x510000
@@ -142,6 +178,15 @@ type                      1
 granularity_bits          16
 name_size                 2
 extra_data_size           0
+Bitmap table   type            offset                   size
+0              serialized      5242880                  65536
+1              all-zeroes      0                        65536
+2              all-zeroes      0                        65536
+3              all-zeroes      0                        65536
+4              all-zeroes      0                        65536
+5              all-zeroes      0                        65536
+6              all-zeroes      0                        65536
+7              all-zeroes      0                        65536
 
 
 === Check bitmap contents ===
diff --git a/tests/qemu-iotests/qcow2_format.py b/tests/qemu-iotests/qcow2_format.py
index cfd9646..b512f84 100644
--- a/tests/qemu-iotests/qcow2_format.py
+++ b/tests/qemu-iotests/qcow2_format.py
@@ -171,14 +171,56 @@ class Qcow2BitmapDirEntry(Qcow2Struct):
         entry_raw_size = self.bitmap_dir_entry_raw_size()
         padding = ((entry_raw_size + 7) & ~7) - entry_raw_size
         fd.seek(padding, 1)
+        position = fd.tell()
+        self.read_bitmap_table(fd)
+        fd.seek(position)
 
     def bitmap_dir_entry_raw_size(self):
         return struct.calcsize(self.fmt) + self.name_size + \
             self.extra_data_size
 
+    def read_bitmap_table(self, fd):
+        fd.seek(self.bitmap_table_offset)
+        table_size = self.bitmap_table_size * 8 * 8
+        table = [e[0] for e in struct.iter_unpack('>Q', fd.read(table_size))]
+        self.bitmap_table = Qcow2BitmapTable(raw_table=table,
+                                             cluster_size=self.cluster_size)
+
     def dump(self):
         print(f'{"Bitmap name":<25} {self.name}')
         super(Qcow2BitmapDirEntry, self).dump()
+        self.bitmap_table.dump()
+
+
+class Qcow2BitmapTableEntry:
+
+    BME_TABLE_ENTRY_OFFSET_MASK = 0x00fffffffffffe00
+    BME_TABLE_ENTRY_FLAG_ALL_ONES = 1
+
+    def __init__(self, entry):
+        self.offset = entry & self.BME_TABLE_ENTRY_OFFSET_MASK
+        if self.offset:
+            self.type = 'serialized'
+        elif entry & self.BME_TABLE_ENTRY_FLAG_ALL_ONES:
+            self.type = 'all-ones'
+        else:
+            self.type = 'all-zeroes'
+
+
+class Qcow2BitmapTable:
+
+    def __init__(self, raw_table, cluster_size):
+        self.entries = []
+        self.cluster_size = cluster_size
+        for entry in raw_table:
+            self.entries.append(Qcow2BitmapTableEntry(entry))
+
+    def dump(self):
+        size = self.cluster_size
+        bitmap_table = enumerate(self.entries)
+        print(f'{"Bitmap table":<14} {"type":<15} {"offset":<24} {"size"}')
+        for i, entry in bitmap_table:
+            print(f'{i:<14} {entry.type:<15} {entry.offset:<24} {size}')
 
 
 QCOW2_EXT_MAGIC_BITMAPS = 0x23852875
-- 
1.8.3.1



^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v8 08/10] qcow2.py: Introduce '-j' key to dump in JSON format
  2020-07-03 13:13 [PATCH v8 00/10] iotests: Dump QCOW2 dirty bitmaps metadata Andrey Shinkevich
                   ` (6 preceding siblings ...)
  2020-07-03 13:13 ` [PATCH v8 07/10] qcow2_format.py: Dump bitmap table serialized entries Andrey Shinkevich
@ 2020-07-03 13:13 ` Andrey Shinkevich
  2020-07-03 13:13 ` [PATCH v8 09/10] qcow2_format.py: collect fields " Andrey Shinkevich
  2020-07-03 13:13 ` [PATCH v8 10/10] qcow2_format.py: support dumping metadata " Andrey Shinkevich
  9 siblings, 0 replies; 21+ messages in thread
From: Andrey Shinkevich @ 2020-07-03 13:13 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, vsementsov, qemu-devel, mreitz, andrey.shinkevich, den

Add the command key to the qcow2.py arguments list to dump QCOW2
metadata in JSON format. Here is the suggested way to do that. The
implementation of the dump in JSON format is in the patch that follows.

Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
---
 tests/qemu-iotests/qcow2.py        | 19 +++++++++++++++----
 tests/qemu-iotests/qcow2_format.py | 16 ++++++++--------
 2 files changed, 23 insertions(+), 12 deletions(-)

diff --git a/tests/qemu-iotests/qcow2.py b/tests/qemu-iotests/qcow2.py
index 0910e6a..7402279 100755
--- a/tests/qemu-iotests/qcow2.py
+++ b/tests/qemu-iotests/qcow2.py
@@ -26,16 +26,19 @@ from qcow2_format import (
 )
 
 
+dump_json = False
+
+
 def cmd_dump_header(fd):
     h = QcowHeader(fd)
-    h.dump()
+    h.dump(dump_json)
     print()
-    h.dump_extensions()
+    h.dump_extensions(dump_json)
 
 
 def cmd_dump_header_exts(fd):
     h = QcowHeader(fd)
-    h.dump_extensions()
+    h.dump_extensions(dump_json)
 
 
 def cmd_set_header(fd, name, value):
@@ -134,6 +137,11 @@ cmds = [
 
 
 def main(filename, cmd, args):
+    global dump_json
+    dump_json = '-j' in sys.argv
+    if dump_json:
+        sys.argv.remove('-j')
+        args.remove('-j')
     fd = open(filename, "r+b")
     try:
         for name, handler, num_args, desc in cmds:
@@ -151,11 +159,14 @@ def main(filename, cmd, args):
 
 
 def usage():
-    print("Usage: %s <file> <cmd> [<arg>, ...]" % sys.argv[0])
+    print("Usage: %s <file> <cmd> [<arg>, ...] [<key>, ...]" % sys.argv[0])
     print("")
     print("Supported commands:")
     for name, handler, num_args, desc in cmds:
         print("    %-20s - %s" % (name, desc))
+    print("")
+    print("Supported keys:")
+    print("    %-20s - %s" % ('-j', 'Dump in JSON format'))
 
 
 if __name__ == '__main__':
diff --git a/tests/qemu-iotests/qcow2_format.py b/tests/qemu-iotests/qcow2_format.py
index b512f84..dae6b6e 100644
--- a/tests/qemu-iotests/qcow2_format.py
+++ b/tests/qemu-iotests/qcow2_format.py
@@ -110,7 +110,7 @@ class Qcow2Struct(metaclass=Qcow2StructMeta):
         self.__dict__ = dict((field[2], values[i])
                              for i, field in enumerate(self.fields))
 
-    def dump(self):
+    def dump(self, dump_json=None):
         for f in self.fields:
             value = self.__dict__[f[2]]
             if isinstance(f[1], str):
@@ -141,8 +141,8 @@ class Qcow2BitmapExt(Qcow2Struct):
             [Qcow2BitmapDirEntry(fd, cluster_size=self.cluster_size)
              for _ in range(self.nb_bitmaps)]
 
-    def dump(self):
-        super().dump()
+    def dump(self, dump_json=None):
+        super().dump(dump_json)
         for entry in self.bitmap_directory:
             print()
             entry.dump()
@@ -186,7 +186,7 @@ class Qcow2BitmapDirEntry(Qcow2Struct):
         self.bitmap_table = Qcow2BitmapTable(raw_table=table,
                                              cluster_size=self.cluster_size)
 
-    def dump(self):
+    def dump(self, dump_json=None):
         print(f'{"Bitmap name":<25} {self.name}')
         super(Qcow2BitmapDirEntry, self).dump()
         self.bitmap_table.dump()
@@ -294,13 +294,13 @@ class QcowHeaderExtension(Qcow2Struct):
         else:
             self.obj = None
 
-    def dump(self):
+    def dump(self, dump_json=None):
         super().dump()
 
         if self.obj is None:
             print(f'{"data":<25} {self.data_str}')
         else:
-            self.obj.dump()
+            self.obj.dump(dump_json)
 
     @classmethod
     def create(cls, magic, data):
@@ -399,8 +399,8 @@ class QcowHeader(Qcow2Struct):
         buf = buf[0:header_bytes-1]
         fd.write(buf)
 
-    def dump_extensions(self):
+    def dump_extensions(self, dump_json=None):
         for ex in self.extensions:
             print('Header extension:')
-            ex.dump()
+            ex.dump(dump_json)
             print()
-- 
1.8.3.1



^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v8 09/10] qcow2_format.py: collect fields to dump in JSON format
  2020-07-03 13:13 [PATCH v8 00/10] iotests: Dump QCOW2 dirty bitmaps metadata Andrey Shinkevich
                   ` (7 preceding siblings ...)
  2020-07-03 13:13 ` [PATCH v8 08/10] qcow2.py: Introduce '-j' key to dump in JSON format Andrey Shinkevich
@ 2020-07-03 13:13 ` Andrey Shinkevich
  2020-07-03 13:13 ` [PATCH v8 10/10] qcow2_format.py: support dumping metadata " Andrey Shinkevich
  9 siblings, 0 replies; 21+ messages in thread
From: Andrey Shinkevich @ 2020-07-03 13:13 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, vsementsov, qemu-devel, mreitz, andrey.shinkevich, den

As __dict__ is being extended with class members we do not want to
print, make a light copy of the initial __dict__ and extend the copy
by adding lists we have to print in the JSON output.

Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
---
 tests/qemu-iotests/qcow2_format.py | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/tests/qemu-iotests/qcow2_format.py b/tests/qemu-iotests/qcow2_format.py
index dae6b6e..e8eb36f 100644
--- a/tests/qemu-iotests/qcow2_format.py
+++ b/tests/qemu-iotests/qcow2_format.py
@@ -110,6 +110,8 @@ class Qcow2Struct(metaclass=Qcow2StructMeta):
         self.__dict__ = dict((field[2], values[i])
                              for i, field in enumerate(self.fields))
 
+        self.fields_dict = self.__dict__.copy()
+
     def dump(self, dump_json=None):
         for f in self.fields:
             value = self.__dict__[f[2]]
@@ -140,6 +142,7 @@ class Qcow2BitmapExt(Qcow2Struct):
         self.bitmap_directory = \
             [Qcow2BitmapDirEntry(fd, cluster_size=self.cluster_size)
              for _ in range(self.nb_bitmaps)]
+        self.fields_dict.update(bitmap_directory=self.bitmap_directory)
 
     def dump(self, dump_json=None):
         super().dump(dump_json)
@@ -185,6 +188,7 @@ class Qcow2BitmapDirEntry(Qcow2Struct):
         table = [e[0] for e in struct.iter_unpack('>Q', fd.read(table_size))]
         self.bitmap_table = Qcow2BitmapTable(raw_table=table,
                                              cluster_size=self.cluster_size)
+        self.fields_dict.update(bitmap_table=self.bitmap_table)
 
     def dump(self, dump_json=None):
         print(f'{"Bitmap name":<25} {self.name}')
-- 
1.8.3.1



^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v8 10/10] qcow2_format.py: support dumping metadata in JSON format
  2020-07-03 13:13 [PATCH v8 00/10] iotests: Dump QCOW2 dirty bitmaps metadata Andrey Shinkevich
                   ` (8 preceding siblings ...)
  2020-07-03 13:13 ` [PATCH v8 09/10] qcow2_format.py: collect fields " Andrey Shinkevich
@ 2020-07-03 13:13 ` Andrey Shinkevich
  9 siblings, 0 replies; 21+ messages in thread
From: Andrey Shinkevich @ 2020-07-03 13:13 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, vsementsov, qemu-devel, mreitz, andrey.shinkevich, den

Implementation of dumping QCOW2 image metadata.
The sample output:
{
    "Header_extensions": [
        {
            "name": "Feature table",
            "magic": 1745090647,
            "length": 192,
            "data_str": "<binary>"
        },
        {
            "name": "Bitmaps",
            "magic": 595929205,
            "length": 24,
            "data": {
                "nb_bitmaps": 2,
                "reserved32": 0,
                "bitmap_directory_size": 64,
                "bitmap_directory_offset": 1048576,
                "bitmap_directory": [
                    {
                        "name": "bitmap-1",
                        "bitmap_table_offset": 589824,
                        "bitmap_table_size": 1,
                        "flags": 2,
                        "type": 1,
                        "granularity_bits": 15,
                        "name_size": 8,
                        "extra_data_size": 0,
                        "bitmap_table": {
                            "entries": [
                                {
                                    "type": "serialized",
                                    "offset": 655360
                                },
                                ...

Suggested-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
---
 tests/qemu-iotests/qcow2_format.py | 59 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 59 insertions(+)

diff --git a/tests/qemu-iotests/qcow2_format.py b/tests/qemu-iotests/qcow2_format.py
index e8eb36f..70be4a1 100644
--- a/tests/qemu-iotests/qcow2_format.py
+++ b/tests/qemu-iotests/qcow2_format.py
@@ -19,6 +19,15 @@
 
 import struct
 import string
+import json
+
+
+class ComplexEncoder(json.JSONEncoder):
+    def default(self, obj):
+        if hasattr(obj, 'get_fields_dict'):
+            return obj.get_fields_dict()
+        else:
+            return json.JSONEncoder.default(self, obj)
 
 
 class Qcow2Field:
@@ -113,6 +122,11 @@ class Qcow2Struct(metaclass=Qcow2StructMeta):
         self.fields_dict = self.__dict__.copy()
 
     def dump(self, dump_json=None):
+        if dump_json:
+            print(json.dumps(self.get_fields_dict(), indent=4,
+                             cls=ComplexEncoder))
+            return
+
         for f in self.fields:
             value = self.__dict__[f[2]]
             if isinstance(f[1], str):
@@ -150,6 +164,9 @@ class Qcow2BitmapExt(Qcow2Struct):
             print()
             entry.dump()
 
+    def get_fields_dict(self):
+        return self.fields_dict
+
 
 class Qcow2BitmapDirEntry(Qcow2Struct):
 
@@ -195,6 +212,11 @@ class Qcow2BitmapDirEntry(Qcow2Struct):
         super(Qcow2BitmapDirEntry, self).dump()
         self.bitmap_table.dump()
 
+    def get_fields_dict(self):
+        bmp_name = dict(name=self.name)
+        bme_dict = {**bmp_name, **self.fields_dict}
+        return bme_dict
+
 
 class Qcow2BitmapTableEntry:
 
@@ -210,6 +232,9 @@ class Qcow2BitmapTableEntry:
         else:
             self.type = 'all-zeroes'
 
+    def get_fields_dict(self):
+        return dict(type=self.type, offset=self.offset)
+
 
 class Qcow2BitmapTable:
 
@@ -226,6 +251,18 @@ class Qcow2BitmapTable:
         for i, entry in bitmap_table:
             print(f'{i:<14} {entry.type:<15} {entry.offset:<24} {size}')
 
+    def get_fields_dict(self):
+        return dict(entries=self.entries)
+
+
+class Qcow2HeaderExtensionsDoc:
+
+    def __init__(self, extensions):
+        self.extensions = extensions
+
+    def get_fields_dict(self):
+        return dict(Header_extensions=self.extensions)
+
 
 QCOW2_EXT_MAGIC_BITMAPS = 0x23852875
 
@@ -241,6 +278,9 @@ class QcowHeaderExtension(Qcow2Struct):
             0x44415441: 'Data file'
         }
 
+        def get_fields_dict(self):
+            return self.mapping.get(self.value, "<unknown>")
+
     fields = (
         ('u32', Magic, 'magic'),
         ('u32', '{}', 'length')
@@ -306,6 +346,16 @@ class QcowHeaderExtension(Qcow2Struct):
         else:
             self.obj.dump(dump_json)
 
+    def get_fields_dict(self):
+        ext_name = dict(name=self.Magic(self.magic))
+        he_dict = {**ext_name, **self.fields_dict}
+        if self.obj is not None:
+            he_dict.update(data=self.obj)
+        else:
+            he_dict.update(data_str=self.data_str)
+
+        return he_dict
+
     @classmethod
     def create(cls, magic, data):
         return QcowHeaderExtension(magic, len(data), data)
@@ -404,7 +454,16 @@ class QcowHeader(Qcow2Struct):
         fd.write(buf)
 
     def dump_extensions(self, dump_json=None):
+        if dump_json:
+            ext_doc = Qcow2HeaderExtensionsDoc(self.extensions)
+            print(json.dumps(ext_doc.get_fields_dict(), indent=4,
+                             cls=ComplexEncoder))
+            return
+
         for ex in self.extensions:
             print('Header extension:')
             ex.dump(dump_json)
             print()
+
+    def get_fields_dict(self):
+        return self.fields_dict
-- 
1.8.3.1



^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [PATCH v8 07/10] qcow2_format.py: Dump bitmap table serialized entries
  2020-07-03 13:13 ` [PATCH v8 07/10] qcow2_format.py: Dump bitmap table serialized entries Andrey Shinkevich
@ 2020-07-03 20:05   ` Andrey Shinkevich
  0 siblings, 0 replies; 21+ messages in thread
From: Andrey Shinkevich @ 2020-07-03 20:05 UTC (permalink / raw)
  To: qemu-block; +Cc: kwolf, vsementsov, qemu-devel, mreitz, den

On 03.07.2020 16:13, Andrey Shinkevich wrote:
> Add bitmap table information to the QCOW2 metadata dump.
...
> --- a/tests/qemu-iotests/qcow2_format.py
> +++ b/tests/qemu-iotests/qcow2_format.py
> @@ -171,14 +171,56 @@ class Qcow2BitmapDirEntry(Qcow2Struct):
>           entry_raw_size = self.bitmap_dir_entry_raw_size()
>           padding = ((entry_raw_size + 7) & ~7) - entry_raw_size
>           fd.seek(padding, 1)
> +        position = fd.tell()
> +        self.read_bitmap_table(fd)
> +        fd.seek(position)
>   
>       def bitmap_dir_entry_raw_size(self):
>           return struct.calcsize(self.fmt) + self.name_size + \
>               self.extra_data_size
>   
> +    def read_bitmap_table(self, fd):
> +        fd.seek(self.bitmap_table_offset)
> +        table_size = self.bitmap_table_size * 8 * 8
> +        table = [e[0] for e in struct.iter_unpack('>Q', fd.read(table_size))]
> +        self.bitmap_table = Qcow2BitmapTable(raw_table=table,
> +                                             cluster_size=self.cluster_size)

As an option, one make the class Qcow2BitmapTableEntry derived from 
Qcow2Struct

with the member:

fields = (

('u64', '{:#x}', 'table_entry')

)

and read entry by entry bitmap_table_size times from the disk in a loop.


Andrey

> +
>       def dump(self):
>           print(f'{"Bitmap name":<25} {self.name}')
>           super(Qcow2BitmapDirEntry, self).dump()
> +        self.bitmap_table.dump()
> +
> +
> +class Qcow2BitmapTableEntry:
> +
> +    BME_TABLE_ENTRY_OFFSET_MASK = 0x00fffffffffffe00
> +    BME_TABLE_ENTRY_FLAG_ALL_ONES = 1
> +
> +    def __init__(self, entry):
> +        self.offset = entry & self.BME_TABLE_ENTRY_OFFSET_MASK
> +        if self.offset:
> +            self.type = 'serialized'
> +        elif entry & self.BME_TABLE_ENTRY_FLAG_ALL_ONES:
> +            self.type = 'all-ones'
> +        else:
> +            self.type = 'all-zeroes'
> +
> +
> +class Qcow2BitmapTable:
> +
> +    def __init__(self, raw_table, cluster_size):
> +        self.entries = []
> +        self.cluster_size = cluster_size
> +        for entry in raw_table:
> +            self.entries.append(Qcow2BitmapTableEntry(entry))
> +
> +    def dump(self):
> +        size = self.cluster_size
> +        bitmap_table = enumerate(self.entries)
> +        print(f'{"Bitmap table":<14} {"type":<15} {"offset":<24} {"size"}')
> +        for i, entry in bitmap_table:
> +            print(f'{i:<14} {entry.type:<15} {entry.offset:<24} {size}')
>   
>   
>   QCOW2_EXT_MAGIC_BITMAPS = 0x23852875


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v8 03/10] qcow2_format.py: change Qcow2BitmapExt initialization method
  2020-07-03 13:13 ` [PATCH v8 03/10] qcow2_format.py: change Qcow2BitmapExt initialization method Andrey Shinkevich
@ 2020-07-11 16:34   ` Vladimir Sementsov-Ogievskiy
  2020-07-13  4:49     ` Andrey Shinkevich
  0 siblings, 1 reply; 21+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-07-11 16:34 UTC (permalink / raw)
  To: Andrey Shinkevich, qemu-block; +Cc: kwolf, den, qemu-devel, mreitz

03.07.2020 16:13, Andrey Shinkevich wrote:
> There are two ways to initialize a class derived from Qcow2Struct:
> 1. Pass a block of binary data to the constructor.
> 2. Pass the file descriptor to allow reading the file from constructor.
> Let's change the Qcow2BitmapExt initialization method from 1 to 2 to
> support a scattered reading in the initialization chain.
> The implementation comes with the patch that follows.
> 
> Suggested-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
> ---
>   tests/qemu-iotests/qcow2_format.py | 14 ++++++++++++--
>   1 file changed, 12 insertions(+), 2 deletions(-)
> 
> diff --git a/tests/qemu-iotests/qcow2_format.py b/tests/qemu-iotests/qcow2_format.py
> index 2f3681b..1435e34 100644
> --- a/tests/qemu-iotests/qcow2_format.py
> +++ b/tests/qemu-iotests/qcow2_format.py
> @@ -63,7 +63,8 @@ class Qcow2StructMeta(type):
>   
>   class Qcow2Struct(metaclass=Qcow2StructMeta):
>   
> -    """Qcow2Struct: base class for qcow2 data structures
> +    """
> +    Qcow2Struct: base class for qcow2 data structures

Unrelated chunk. And why?

>   
>       Successors should define fields class variable, which is: list of tuples,
>       each of three elements:
> @@ -113,6 +114,9 @@ class Qcow2BitmapExt(Qcow2Struct):
>           ('u64', '{:#x}', 'bitmap_directory_offset')
>       )
>   
> +    def __init__(self, fd):
> +        super().__init__(fd=fd)

this does nothing. We inherit the __init__ of super class, no need to define it just to call same __init__.

> +
>   
>   QCOW2_EXT_MAGIC_BITMAPS = 0x23852875
>   
> @@ -173,7 +177,13 @@ class QcowHeaderExtension(Qcow2Struct):
>           self.data_str = data_str
>   
>           if self.magic == QCOW2_EXT_MAGIC_BITMAPS:
> -            self.obj = Qcow2BitmapExt(data=self.data)
> +            assert fd is not None
> +            position = fd.tell()
> +            # Step back to reread data

This definitely shows that we are doing something wrong

> +            padded = (self.length + 7) & ~7
> +            fd.seek(-padded, 1)
> +            self.obj = Qcow2BitmapExt(fd=fd)
> +            fd.seek(position)
>           else:
>               self.obj = None
>   
> 


-- 
Best regards,
Vladimir


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v8 04/10] qcow2_format.py: dump bitmap flags in human readable way.
  2020-07-03 13:13 ` [PATCH v8 04/10] qcow2_format.py: dump bitmap flags in human readable way Andrey Shinkevich
@ 2020-07-11 16:43   ` Vladimir Sementsov-Ogievskiy
  0 siblings, 0 replies; 21+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-07-11 16:43 UTC (permalink / raw)
  To: Andrey Shinkevich, qemu-block; +Cc: kwolf, den, qemu-devel, mreitz

03.07.2020 16:13, Andrey Shinkevich wrote:
> Introduce the class BitmapFlags that parses a bitmap flags mask.
> 
> Suggested-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
> ---
>   tests/qemu-iotests/qcow2_format.py | 16 ++++++++++++++++
>   1 file changed, 16 insertions(+)
> 
> diff --git a/tests/qemu-iotests/qcow2_format.py b/tests/qemu-iotests/qcow2_format.py
> index 1435e34..d8c058d 100644
> --- a/tests/qemu-iotests/qcow2_format.py
> +++ b/tests/qemu-iotests/qcow2_format.py
> @@ -40,6 +40,22 @@ class Flags64(Qcow2Field):
>           return str(bits)
>   
>   
> +class BitmapFlags(Qcow2Field):
> +
> +    flags = {
> +        0x1: 'in-use',
> +        0x2: 'auto'
> +    }
> +
> +    def __str__(self):
> +        bits = []
> +        for bit in range(64):
> +            flag = self.value & (1 << bit)
> +            if flag:
> +                bits.append(self.flags.get(flag, '{:#x}'.format(flag)))

please use f-strings where possible, for consistency.

Also, more obvious notation for unknown bits would be f'bit-{bit}' I think, so you see number of bit, not big hex number.

> +        return f'{self.value:#x} ({bits})'
> +
> +
>   class Enum(Qcow2Field):
>   
>       def __str__(self):
> 


-- 
Best regards,
Vladimir


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v8 05/10] qcow2_format.py: Dump bitmap directory information
  2020-07-03 13:13 ` [PATCH v8 05/10] qcow2_format.py: Dump bitmap directory information Andrey Shinkevich
@ 2020-07-11 19:11   ` Vladimir Sementsov-Ogievskiy
  2020-07-13  7:07     ` Andrey Shinkevich
  2020-07-16  9:13   ` Vladimir Sementsov-Ogievskiy
  1 sibling, 1 reply; 21+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-07-11 19:11 UTC (permalink / raw)
  To: Andrey Shinkevich, qemu-block; +Cc: kwolf, den, qemu-devel, mreitz

03.07.2020 16:13, Andrey Shinkevich wrote:
> Read and dump entries from the bitmap directory of QCOW2 image.
> It extends the output in the test case #291.
> 
> Header extension:
> magic                     0x23852875 (Bitmaps)
> ...
> 
> Bitmap name               bitmap-1
> bitmap_table_offset       0xf0000
> bitmap_table_size         1
> flags                     0x2 (['auto'])
> type                      1
> granularity_bits          16
> name_size                 8
> extra_data_size           0
> 
> Suggested-by: Kevin Wolf <kwolf@redhat.com>
> Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
> ---
>   tests/qemu-iotests/291.out         | 45 ++++++++++++++++++++++++++++++++++++++
>   tests/qemu-iotests/qcow2_format.py | 44 +++++++++++++++++++++++++++++++++++++
>   2 files changed, 89 insertions(+)
> 
> diff --git a/tests/qemu-iotests/291.out b/tests/qemu-iotests/291.out
> index 08bfaaa..53a8eeb 100644
> --- a/tests/qemu-iotests/291.out
> +++ b/tests/qemu-iotests/291.out
> @@ -33,6 +33,24 @@ reserved32                0
>   bitmap_directory_size     0x40
>   bitmap_directory_offset   0x510000
>   
> +Bitmap name               b1
> +bitmap_table_offset       0x4e0000
> +bitmap_table_size         1
> +flags                     0x0 ([])
> +type                      1
> +granularity_bits          19
> +name_size                 2
> +extra_data_size           0
> +
> +Bitmap name               b2
> +bitmap_table_offset       0x500000
> +bitmap_table_size         1
> +flags                     0x2 (['auto'])
> +type                      1
> +granularity_bits          16
> +name_size                 2
> +extra_data_size           0
> +
>   
>   === Bitmap preservation not possible to non-qcow2 ===
>   
> @@ -98,6 +116,33 @@ reserved32                0
>   bitmap_directory_size     0x60
>   bitmap_directory_offset   0x520000
>   
> +Bitmap name               b1
> +bitmap_table_offset       0x470000
> +bitmap_table_size         1
> +flags                     0x0 ([])
> +type                      1
> +granularity_bits          19
> +name_size                 2
> +extra_data_size           0
> +
> +Bitmap name               b2
> +bitmap_table_offset       0x490000
> +bitmap_table_size         1
> +flags                     0x2 (['auto'])
> +type                      1
> +granularity_bits          16
> +name_size                 2
> +extra_data_size           0
> +
> +Bitmap name               b0
> +bitmap_table_offset       0x510000
> +bitmap_table_size         1
> +flags                     0x0 ([])
> +type                      1
> +granularity_bits          16
> +name_size                 2
> +extra_data_size           0
> +
>   
>   === Check bitmap contents ===
>   
> diff --git a/tests/qemu-iotests/qcow2_format.py b/tests/qemu-iotests/qcow2_format.py
> index d8c058d..7c0dc9a 100644
> --- a/tests/qemu-iotests/qcow2_format.py
> +++ b/tests/qemu-iotests/qcow2_format.py
> @@ -132,6 +132,50 @@ class Qcow2BitmapExt(Qcow2Struct):
>   
>       def __init__(self, fd):
>           super().__init__(fd=fd)
> +        self.read_bitmap_directory(fd)
> +
> +    def read_bitmap_directory(self, fd):
> +        fd.seek(self.bitmap_directory_offset)
> +        self.bitmap_directory = \
> +            [Qcow2BitmapDirEntry(fd) for _ in range(self.nb_bitmaps)]

sounds good. I think, we should restore fd position after reading bitmap_directory, to point at the end of extension, to not break further extensions loading

> +
> +    def dump(self):
> +        super().dump()
> +        for entry in self.bitmap_directory:
> +            print()
> +            entry.dump()
> +
> +
> +class Qcow2BitmapDirEntry(Qcow2Struct):
> +
> +    fields = (
> +        ('u64', '{:#x}', 'bitmap_table_offset'),
> +        ('u32', '{}', 'bitmap_table_size'),
> +        ('u32', BitmapFlags, 'flags'),
> +        ('u8',  '{}', 'type'),
> +        ('u8',  '{}', 'granularity_bits'),
> +        ('u16', '{}', 'name_size'),
> +        ('u32', '{}', 'extra_data_size')
> +    )
> +
> +    def __init__(self, fd):
> +        super().__init__(fd=fd)
> +        # Seek relative to the current position in the file
> +        fd.seek(self.extra_data_size, 1)
> +        bitmap_name = fd.read(self.name_size)
> +        self.name = bitmap_name.decode('ascii')
> +        # Move position to the end of the entry in the directory
> +        entry_raw_size = self.bitmap_dir_entry_raw_size()
> +        padding = ((entry_raw_size + 7) & ~7) - entry_raw_size
> +        fd.seek(padding, 1)
> +
> +    def bitmap_dir_entry_raw_size(self):
> +        return struct.calcsize(self.fmt) + self.name_size + \
> +            self.extra_data_size
> +
> +    def dump(self):
> +        print(f'{"Bitmap name":<25} {self.name}')
> +        super(Qcow2BitmapDirEntry, self).dump()
>   
>   
>   QCOW2_EXT_MAGIC_BITMAPS = 0x23852875
> 


-- 
Best regards,
Vladimir


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v8 03/10] qcow2_format.py: change Qcow2BitmapExt initialization method
  2020-07-11 16:34   ` Vladimir Sementsov-Ogievskiy
@ 2020-07-13  4:49     ` Andrey Shinkevich
  2020-07-13  8:33       ` Vladimir Sementsov-Ogievskiy
  0 siblings, 1 reply; 21+ messages in thread
From: Andrey Shinkevich @ 2020-07-13  4:49 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block; +Cc: kwolf, den, qemu-devel, mreitz

On 11.07.2020 19:34, Vladimir Sementsov-Ogievskiy wrote:
> 03.07.2020 16:13, Andrey Shinkevich wrote:
>> There are two ways to initialize a class derived from Qcow2Struct:
>> 1. Pass a block of binary data to the constructor.
>> 2. Pass the file descriptor to allow reading the file from constructor.
>> Let's change the Qcow2BitmapExt initialization method from 1 to 2 to
>> support a scattered reading in the initialization chain.
>> The implementation comes with the patch that follows.
>>
>> Suggested-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
>> Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
>> ---
>>   tests/qemu-iotests/qcow2_format.py | 14 ++++++++++++--
>>   1 file changed, 12 insertions(+), 2 deletions(-)
>>
>> diff --git a/tests/qemu-iotests/qcow2_format.py 
>> b/tests/qemu-iotests/qcow2_format.py
>> index 2f3681b..1435e34 100644
>> --- a/tests/qemu-iotests/qcow2_format.py
>> +++ b/tests/qemu-iotests/qcow2_format.py
>> @@ -63,7 +63,8 @@ class Qcow2StructMeta(type):
>>     class Qcow2Struct(metaclass=Qcow2StructMeta):
>>   -    """Qcow2Struct: base class for qcow2 data structures
>> +    """
>> +    Qcow2Struct: base class for qcow2 data structures
>
> Unrelated chunk. And why?


To conform to the common style for comments in the file as it is at

class QcowHeaderExtension::__init__()


>
>>         Successors should define fields class variable, which is: 
>> list of tuples,
>>       each of three elements:
>> @@ -113,6 +114,9 @@ class Qcow2BitmapExt(Qcow2Struct):
>>           ('u64', '{:#x}', 'bitmap_directory_offset')
>>       )
>>   +    def __init__(self, fd):
>> +        super().__init__(fd=fd)
>
> this does nothing. We inherit the __init__ of super class, no need to 
> define it just to call same __init__.
>
>> +
>>     QCOW2_EXT_MAGIC_BITMAPS = 0x23852875
>>   @@ -173,7 +177,13 @@ class QcowHeaderExtension(Qcow2Struct):
>>           self.data_str = data_str
>>             if self.magic == QCOW2_EXT_MAGIC_BITMAPS:
>> -            self.obj = Qcow2BitmapExt(data=self.data)
>> +            assert fd is not None
>> +            position = fd.tell()
>> +            # Step back to reread data
>
> This definitely shows that we are doing something wrong


For Qcow2BitmapExt, we need both fd and data and they are mutualy exclusive

in the constructor of the class Qcow2Struct. Rereading the bitmap extension

is a solution without changing the Qcow2Struct. Any other suggestion?

Andrey


>
>> +            padded = (self.length + 7) & ~7
>> +            fd.seek(-padded, 1)
>> +            self.obj = Qcow2BitmapExt(fd=fd)
>> +            fd.seek(position)
>>           else:
>>               self.obj = None
>>
>
>


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v8 05/10] qcow2_format.py: Dump bitmap directory information
  2020-07-11 19:11   ` Vladimir Sementsov-Ogievskiy
@ 2020-07-13  7:07     ` Andrey Shinkevich
  2020-07-13  8:22       ` Vladimir Sementsov-Ogievskiy
  0 siblings, 1 reply; 21+ messages in thread
From: Andrey Shinkevich @ 2020-07-13  7:07 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block; +Cc: kwolf, den, qemu-devel, mreitz

On 11.07.2020 22:11, Vladimir Sementsov-Ogievskiy wrote:
> 03.07.2020 16:13, Andrey Shinkevich wrote:
>> Read and dump entries from the bitmap directory of QCOW2 image.
>> It extends the output in the test case #291.
>>
>>
...
>>   diff --git a/tests/qemu-iotests/qcow2_format.py 
>> b/tests/qemu-iotests/qcow2_format.py
>> index d8c058d..7c0dc9a 100644
>> --- a/tests/qemu-iotests/qcow2_format.py
>> +++ b/tests/qemu-iotests/qcow2_format.py
>> @@ -132,6 +132,50 @@ class Qcow2BitmapExt(Qcow2Struct):
>>         def __init__(self, fd):
>>           super().__init__(fd=fd)
>> +        self.read_bitmap_directory(fd)
>> +
>> +    def read_bitmap_directory(self, fd):
>> +        fd.seek(self.bitmap_directory_offset)
>> +        self.bitmap_directory = \
>> +            [Qcow2BitmapDirEntry(fd) for _ in range(self.nb_bitmaps)]
>
> sounds good. I think, we should restore fd position after reading 
> bitmap_directory, to point at the end of extension, to not break 
> further extensions loading
>
>
Yes, it is done in the constructor of QcowHeaderExtension:

if self.magic == QCOW2_EXT_MAGIC_BITMAPS:

...

position = fd.tell()

...

self.obj = Qcow2BitmapExt(fd=fd)

fd.seek(position)


Andrey



^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v8 05/10] qcow2_format.py: Dump bitmap directory information
  2020-07-13  7:07     ` Andrey Shinkevich
@ 2020-07-13  8:22       ` Vladimir Sementsov-Ogievskiy
  0 siblings, 0 replies; 21+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-07-13  8:22 UTC (permalink / raw)
  To: Andrey Shinkevich, qemu-block; +Cc: kwolf, den, qemu-devel, mreitz

13.07.2020 10:07, Andrey Shinkevich wrote:
> On 11.07.2020 22:11, Vladimir Sementsov-Ogievskiy wrote:
>> 03.07.2020 16:13, Andrey Shinkevich wrote:
>>> Read and dump entries from the bitmap directory of QCOW2 image.
>>> It extends the output in the test case #291.
>>>
>>>
> ...
>>>   diff --git a/tests/qemu-iotests/qcow2_format.py b/tests/qemu-iotests/qcow2_format.py
>>> index d8c058d..7c0dc9a 100644
>>> --- a/tests/qemu-iotests/qcow2_format.py
>>> +++ b/tests/qemu-iotests/qcow2_format.py
>>> @@ -132,6 +132,50 @@ class Qcow2BitmapExt(Qcow2Struct):
>>>         def __init__(self, fd):
>>>           super().__init__(fd=fd)
>>> +        self.read_bitmap_directory(fd)
>>> +
>>> +    def read_bitmap_directory(self, fd):
>>> +        fd.seek(self.bitmap_directory_offset)
>>> +        self.bitmap_directory = \
>>> +            [Qcow2BitmapDirEntry(fd) for _ in range(self.nb_bitmaps)]
>>
>> sounds good. I think, we should restore fd position after reading bitmap_directory, to point at the end of extension, to not break further extensions loading
>>
>>
> Yes, it is done in the constructor of QcowHeaderExtension:
> 
> if self.magic == QCOW2_EXT_MAGIC_BITMAPS:
> 
> ...
> 
> position = fd.tell()
> 
> ...
> 
> self.obj = Qcow2BitmapExt(fd=fd)
> 
> fd.seek(position)
> 

I don't like it. If you want caller to care about fd, caller should know size of created child. But passing fd to constructor implies that caller not aware of size of new created structure. So I think good api is: constuctor starts to read the structure and left after this structure on exit from consturctor (so, caller may read following structures). Constructor may read some nested structures, but is responsible for restoring fd after it.


-- 
Best regards,
Vladimir


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v8 03/10] qcow2_format.py: change Qcow2BitmapExt initialization method
  2020-07-13  4:49     ` Andrey Shinkevich
@ 2020-07-13  8:33       ` Vladimir Sementsov-Ogievskiy
  0 siblings, 0 replies; 21+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-07-13  8:33 UTC (permalink / raw)
  To: Andrey Shinkevich, qemu-block; +Cc: kwolf, den, qemu-devel, mreitz

13.07.2020 07:49, Andrey Shinkevich wrote:
> On 11.07.2020 19:34, Vladimir Sementsov-Ogievskiy wrote:
>> 03.07.2020 16:13, Andrey Shinkevich wrote:
>>> There are two ways to initialize a class derived from Qcow2Struct:
>>> 1. Pass a block of binary data to the constructor.
>>> 2. Pass the file descriptor to allow reading the file from constructor.
>>> Let's change the Qcow2BitmapExt initialization method from 1 to 2 to
>>> support a scattered reading in the initialization chain.
>>> The implementation comes with the patch that follows.
>>>
>>> Suggested-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
>>> Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
>>> ---
>>>   tests/qemu-iotests/qcow2_format.py | 14 ++++++++++++--
>>>   1 file changed, 12 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/tests/qemu-iotests/qcow2_format.py b/tests/qemu-iotests/qcow2_format.py
>>> index 2f3681b..1435e34 100644
>>> --- a/tests/qemu-iotests/qcow2_format.py
>>> +++ b/tests/qemu-iotests/qcow2_format.py
>>> @@ -63,7 +63,8 @@ class Qcow2StructMeta(type):
>>>     class Qcow2Struct(metaclass=Qcow2StructMeta):
>>>   -    """Qcow2Struct: base class for qcow2 data structures
>>> +    """
>>> +    Qcow2Struct: base class for qcow2 data structures
>>
>> Unrelated chunk. And why?
> 
> 
> To conform to the common style for comments in the file as it is at
> 
> class QcowHeaderExtension::__init__()

It's OK to additionally fix/change style in the chunk where you change some logic anyway. But changing style in another places - I think it doesn't worth it. Remember that it may make patch porting more complicated for nothing.

And this is not only my position, https://wiki.qemu.org/Contribute/SubmitAPatch directly statates it in "Don't include irrelevant changes" paragraph:

     In particular, don't include formatting, coding style or whitespace changes to bits of code that would otherwise not be touched by the patch.

> 
> 
>>
>>>         Successors should define fields class variable, which is: list of tuples,
>>>       each of three elements:
>>> @@ -113,6 +114,9 @@ class Qcow2BitmapExt(Qcow2Struct):
>>>           ('u64', '{:#x}', 'bitmap_directory_offset')
>>>       )
>>>   +    def __init__(self, fd):
>>> +        super().__init__(fd=fd)
>>
>> this does nothing. We inherit the __init__ of super class, no need to define it just to call same __init__.
>>
>>> +
>>>     QCOW2_EXT_MAGIC_BITMAPS = 0x23852875
>>>   @@ -173,7 +177,13 @@ class QcowHeaderExtension(Qcow2Struct):
>>>           self.data_str = data_str
>>>             if self.magic == QCOW2_EXT_MAGIC_BITMAPS:
>>> -            self.obj = Qcow2BitmapExt(data=self.data)
>>> +            assert fd is not None
>>> +            position = fd.tell()
>>> +            # Step back to reread data
>>
>> This definitely shows that we are doing something wrong
> 
> 
> For Qcow2BitmapExt, we need both fd and data and they are mutualy exclusive
> 
> in the constructor of the class Qcow2Struct. Rereading the bitmap extension
> 
> is a solution without changing the Qcow2Struct. Any other suggestion?
> 

I think, we want to modify QcowHeaderExtension so that it creates/reads its children appropriately. Probably, for bitmaps extension exclusively we pass fd to Qcow2BitmapExt, not reading data by ourselves, and for other extensions keep current semantics of just reading data by hand (or you may add a simple class Qcow2Ext.

> 
> 
>>
>>> +            padded = (self.length + 7) & ~7
>>> +            fd.seek(-padded, 1)
>>> +            self.obj = Qcow2BitmapExt(fd=fd)
>>> +            fd.seek(position)
>>>           else:
>>>               self.obj = None
>>>
>>
>>


-- 
Best regards,
Vladimir


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v8 05/10] qcow2_format.py: Dump bitmap directory information
  2020-07-03 13:13 ` [PATCH v8 05/10] qcow2_format.py: Dump bitmap directory information Andrey Shinkevich
  2020-07-11 19:11   ` Vladimir Sementsov-Ogievskiy
@ 2020-07-16  9:13   ` Vladimir Sementsov-Ogievskiy
  2020-07-16  9:14     ` Vladimir Sementsov-Ogievskiy
  1 sibling, 1 reply; 21+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-07-16  9:13 UTC (permalink / raw)
  To: Andrey Shinkevich, qemu-block; +Cc: kwolf, den, qemu-devel, mreitz

03.07.2020 16:13, Andrey Shinkevich wrote:
> Read and dump entries from the bitmap directory of QCOW2 image.
> It extends the output in the test case #291.
> 
> Header extension:
> magic                     0x23852875 (Bitmaps)
> ...
> 
> Bitmap name               bitmap-1
> bitmap_table_offset       0xf0000
> bitmap_table_size         1
> flags                     0x2 (['auto'])
> type                      1
> granularity_bits          16
> name_size                 8
> extra_data_size           0
> 
> Suggested-by: Kevin Wolf <kwolf@redhat.com>
> Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>

Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>

-- 
Best regards,
Vladimir


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v8 05/10] qcow2_format.py: Dump bitmap directory information
  2020-07-16  9:13   ` Vladimir Sementsov-Ogievskiy
@ 2020-07-16  9:14     ` Vladimir Sementsov-Ogievskiy
  0 siblings, 0 replies; 21+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-07-16  9:14 UTC (permalink / raw)
  To: Andrey Shinkevich, qemu-block; +Cc: kwolf, den, qemu-devel, mreitz

16.07.2020 12:13, Vladimir Sementsov-Ogievskiy wrote:
> 
> Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>

Oops, sorry, I wanted to answer v10 patch. Ignore this.


-- 
Best regards,
Vladimir


^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2020-07-16  9:15 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-07-03 13:13 [PATCH v8 00/10] iotests: Dump QCOW2 dirty bitmaps metadata Andrey Shinkevich
2020-07-03 13:13 ` [PATCH v8 01/10] qcow2: Fix capitalization of header extension constant Andrey Shinkevich
2020-07-03 13:13 ` [PATCH v8 02/10] qcow2_format.py: make printable data an extension class member Andrey Shinkevich
2020-07-03 13:13 ` [PATCH v8 03/10] qcow2_format.py: change Qcow2BitmapExt initialization method Andrey Shinkevich
2020-07-11 16:34   ` Vladimir Sementsov-Ogievskiy
2020-07-13  4:49     ` Andrey Shinkevich
2020-07-13  8:33       ` Vladimir Sementsov-Ogievskiy
2020-07-03 13:13 ` [PATCH v8 04/10] qcow2_format.py: dump bitmap flags in human readable way Andrey Shinkevich
2020-07-11 16:43   ` Vladimir Sementsov-Ogievskiy
2020-07-03 13:13 ` [PATCH v8 05/10] qcow2_format.py: Dump bitmap directory information Andrey Shinkevich
2020-07-11 19:11   ` Vladimir Sementsov-Ogievskiy
2020-07-13  7:07     ` Andrey Shinkevich
2020-07-13  8:22       ` Vladimir Sementsov-Ogievskiy
2020-07-16  9:13   ` Vladimir Sementsov-Ogievskiy
2020-07-16  9:14     ` Vladimir Sementsov-Ogievskiy
2020-07-03 13:13 ` [PATCH v8 06/10] qcow2_format.py: pass cluster size to substructures Andrey Shinkevich
2020-07-03 13:13 ` [PATCH v8 07/10] qcow2_format.py: Dump bitmap table serialized entries Andrey Shinkevich
2020-07-03 20:05   ` Andrey Shinkevich
2020-07-03 13:13 ` [PATCH v8 08/10] qcow2.py: Introduce '-j' key to dump in JSON format Andrey Shinkevich
2020-07-03 13:13 ` [PATCH v8 09/10] qcow2_format.py: collect fields " Andrey Shinkevich
2020-07-03 13:13 ` [PATCH v8 10/10] qcow2_format.py: support dumping metadata " Andrey Shinkevich

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).