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: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> Reviewed-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com> Message-Id: <20200606081806.23897-14-vsementsov@virtuozzo.com> [eblake: fix iotest output] Signed-off-by: Eric Blake <eblake@redhat.com>
This commit is contained in:
parent
aef87784f9
commit
820c6bee53
|
@ -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 ==="
|
||||
|
|
|
@ -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 ===
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in a new issue