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 --disable -f $IMGFMT "$TEST_IMG" b1
|
||||||
$QEMU_IMG bitmap --enable -f $IMGFMT "$TEST_IMG" b2
|
$QEMU_IMG bitmap --enable -f $IMGFMT "$TEST_IMG" b2
|
||||||
$QEMU_IO -c 'w 2M 1M' -f $IMGFMT "$TEST_IMG" | _filter_qemu_io
|
$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
|
||||||
echo "=== Bitmap preservation not possible to non-qcow2 ==="
|
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 \
|
$QEMU_IMG bitmap --remove --image-opts \
|
||||||
driver=$IMGFMT,file.driver=file,file.filename="$TEST_IMG" tmp
|
driver=$IMGFMT,file.driver=file,file.filename="$TEST_IMG" tmp
|
||||||
$QEMU_IMG info "$TEST_IMG" | _filter_img_info --format-specific
|
$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
|
||||||
echo "=== Check bitmap contents ==="
|
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)
|
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
wrote 1048576/1048576 bytes at offset 2097152
|
wrote 1048576/1048576 bytes at offset 2097152
|
||||||
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
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 ===
|
=== Bitmap preservation not possible to non-qcow2 ===
|
||||||
|
|
||||||
@ -65,6 +84,20 @@ Format specific information:
|
|||||||
granularity: 65536
|
granularity: 65536
|
||||||
refcount bits: 16
|
refcount bits: 16
|
||||||
corrupt: false
|
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 ===
|
=== Check bitmap contents ===
|
||||||
|
|
||||||
|
@ -103,6 +103,19 @@ class Qcow2Struct(metaclass=Qcow2StructMeta):
|
|||||||
print('{:<25} {}'.format(f[2], value_str))
|
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 QcowHeaderExtension(Qcow2Struct):
|
||||||
|
|
||||||
class Magic(Enum):
|
class Magic(Enum):
|
||||||
@ -110,7 +123,7 @@ class QcowHeaderExtension(Qcow2Struct):
|
|||||||
0xe2792aca: 'Backing format',
|
0xe2792aca: 'Backing format',
|
||||||
0x6803f857: 'Feature table',
|
0x6803f857: 'Feature table',
|
||||||
0x0537be77: 'Crypto header',
|
0x0537be77: 'Crypto header',
|
||||||
0x23852875: 'Bitmaps',
|
QCOW2_EXT_MAGIC_BITMAPS: 'Bitmaps',
|
||||||
0x44415441: 'Data file'
|
0x44415441: 'Data file'
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,8 +143,11 @@ class QcowHeaderExtension(Qcow2Struct):
|
|||||||
This should be somehow refactored and functionality should be moved to
|
This should be somehow refactored and functionality should be moved to
|
||||||
superclass (to allow creation of any qcow2 struct), but then, fields
|
superclass (to allow creation of any qcow2 struct), but then, fields
|
||||||
of variable length (data here) should be supported in base class
|
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
|
somehow. Note also, that we probably want to parse different
|
||||||
we have more qcow2 structures.
|
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:
|
if fd is None:
|
||||||
assert all(v is not None for v in (magic, length, data))
|
assert all(v is not None for v in (magic, length, data))
|
||||||
@ -148,15 +164,23 @@ class QcowHeaderExtension(Qcow2Struct):
|
|||||||
self.data = fd.read(padded)
|
self.data = fd.read(padded)
|
||||||
assert self.data is not None
|
assert self.data is not None
|
||||||
|
|
||||||
def dump(self):
|
if self.magic == QCOW2_EXT_MAGIC_BITMAPS:
|
||||||
data = self.data[:self.length]
|
self.obj = Qcow2BitmapExt(data=self.data)
|
||||||
if all(c in string.printable.encode('ascii') for c in data):
|
|
||||||
data = f"'{ data.decode('ascii') }'"
|
|
||||||
else:
|
else:
|
||||||
data = '<binary>'
|
self.obj = None
|
||||||
|
|
||||||
|
def dump(self):
|
||||||
super().dump()
|
super().dump()
|
||||||
print(f'{"data":<25} {data}')
|
|
||||||
|
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}')
|
||||||
|
else:
|
||||||
|
self.obj.dump()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, magic, data):
|
def create(cls, magic, data):
|
||||||
|
Loading…
Reference in New Issue
Block a user