diff mbox series

[v8,05/10] qcow2_format.py: Dump bitmap directory information

Message ID 1593782030-521984-6-git-send-email-andrey.shinkevich@virtuozzo.com (mailing list archive)
State New, archived
Headers show
Series iotests: Dump QCOW2 dirty bitmaps metadata | expand

Commit Message

Andrey Shinkevich July 3, 2020, 1:13 p.m. UTC
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(+)

Comments

Vladimir Sementsov-Ogievskiy July 11, 2020, 7:11 p.m. UTC | #1
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
>
Andrey Shinkevich July 13, 2020, 7:07 a.m. UTC | #2
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
Vladimir Sementsov-Ogievskiy July 13, 2020, 8:22 a.m. UTC | #3
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.
Vladimir Sementsov-Ogievskiy July 16, 2020, 9:13 a.m. UTC | #4
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>
Vladimir Sementsov-Ogievskiy July 16, 2020, 9:14 a.m. UTC | #5
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.
diff mbox series

Patch

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