Commit 820c6bee authored by Vladimir Sementsov-Ogievskiy's avatar Vladimir Sementsov-Ogievskiy Committed by Eric Blake
Browse files

qcow2_format.py: dump bitmaps header extension



Add class for bitmap extension and dump its fields. Further work is to
dump bitmap directory.

Test new functionality inside 291 iotest.

Signed-off-by: default avatarVladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Reviewed-by: default avatarAndrey Shinkevich <andrey.shinkevich@virtuozzo.com>
Message-Id: <20200606081806.23897-14-vsementsov@virtuozzo.com>
[eblake: fix iotest output]
Signed-off-by: default avatarEric Blake <eblake@redhat.com>
parent aef87784
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -62,6 +62,8 @@ $QEMU_IO -c 'w 1M 1M' -f $IMGFMT "$TEST_IMG" | _filter_qemu_io
$QEMU_IMG bitmap --disable -f $IMGFMT "$TEST_IMG" b1
$QEMU_IMG bitmap --enable -f $IMGFMT "$TEST_IMG" b2
$QEMU_IO -c 'w 2M 1M' -f $IMGFMT "$TEST_IMG" | _filter_qemu_io
echo "Check resulting qcow2 header extensions:"
$PYTHON qcow2.py "$TEST_IMG" dump-header-exts

echo
echo "=== Bitmap preservation not possible to non-qcow2 ==="
@@ -88,6 +90,8 @@ $QEMU_IMG bitmap --merge tmp -f $IMGFMT "$TEST_IMG" b0
$QEMU_IMG bitmap --remove --image-opts \
    driver=$IMGFMT,file.driver=file,file.filename="$TEST_IMG" tmp
$QEMU_IMG info "$TEST_IMG" | _filter_img_info --format-specific
echo "Check resulting qcow2 header extensions:"
$PYTHON qcow2.py "$TEST_IMG" dump-header-exts

echo
echo "=== Check bitmap contents ==="
+33 −0
Original line number Diff line number Diff line
@@ -14,6 +14,25 @@ wrote 1048576/1048576 bytes at offset 1048576
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 1048576/1048576 bytes at offset 2097152
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
Check resulting qcow2 header extensions:
Header extension:
magic                     0xe2792aca (Backing format)
length                    5
data                      'qcow2'

Header extension:
magic                     0x6803f857 (Feature table)
length                    336
data                      <binary>

Header extension:
magic                     0x23852875 (Bitmaps)
length                    24
nb_bitmaps                2
reserved32                0
bitmap_directory_size     0x40
bitmap_directory_offset   0x510000


=== Bitmap preservation not possible to non-qcow2 ===

@@ -65,6 +84,20 @@ Format specific information:
            granularity: 65536
    refcount bits: 16
    corrupt: false
Check resulting qcow2 header extensions:
Header extension:
magic                     0x6803f857 (Feature table)
length                    336
data                      <binary>

Header extension:
magic                     0x23852875 (Bitmaps)
length                    24
nb_bitmaps                3
reserved32                0
bitmap_directory_size     0x60
bitmap_directory_offset   0x520000


=== Check bitmap contents ===

+33 −9
Original line number Diff line number Diff line
@@ -103,6 +103,19 @@ class Qcow2Struct(metaclass=Qcow2StructMeta):
            print('{:<25} {}'.format(f[2], value_str))


class Qcow2BitmapExt(Qcow2Struct):

    fields = (
        ('u32', '{}', 'nb_bitmaps'),
        ('u32', '{}', 'reserved32'),
        ('u64', '{:#x}', 'bitmap_directory_size'),
        ('u64', '{:#x}', 'bitmap_directory_offset')
    )


QCOW2_EXT_MAGIC_BITMAPS = 0x23852875


class QcowHeaderExtension(Qcow2Struct):

    class Magic(Enum):
@@ -110,7 +123,7 @@ class QcowHeaderExtension(Qcow2Struct):
            0xe2792aca: 'Backing format',
            0x6803f857: 'Feature table',
            0x0537be77: 'Crypto header',
            0x23852875: 'Bitmaps',
            QCOW2_EXT_MAGIC_BITMAPS: 'Bitmaps',
            0x44415441: 'Data file'
        }

@@ -130,8 +143,11 @@ class QcowHeaderExtension(Qcow2Struct):
        This should be somehow refactored and functionality should be moved to
        superclass (to allow creation of any qcow2 struct), but then, fields
        of variable length (data here) should be supported in base class
        somehow. So, it's a TODO. We'll see how to properly refactor this when
        we have more qcow2 structures.
        somehow. Note also, that we probably want to parse different
        extensions. Should they be subclasses of this class, or how to do it
        better? Should it be something like QAPI union with discriminator field
        (magic here). So, it's a TODO. We'll see how to properly refactor this
        when we have more qcow2 structures.
        """
        if fd is None:
            assert all(v is not None for v in (magic, length, data))
@@ -148,15 +164,23 @@ class QcowHeaderExtension(Qcow2Struct):
            self.data = fd.read(padded)
            assert self.data is not None

        if self.magic == QCOW2_EXT_MAGIC_BITMAPS:
            self.obj = Qcow2BitmapExt(data=self.data)
        else:
            self.obj = None

    def dump(self):
        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>'

        super().dump()
            print(f'{"data":<25} {data}')
        else:
            self.obj.dump()

    @classmethod
    def create(cls, magic, data):