@@ -42,6 +42,16 @@ 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
flag auto
@@ -53,6 +63,16 @@ 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 ===
@@ -128,6 +148,16 @@ 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
flag auto
@@ -139,6 +169,16 @@ 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
table size 8 (bytes)
@@ -149,6 +189,16 @@ 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 ===
@@ -137,6 +137,9 @@ class Qcow2BitmapExt(Qcow2Struct):
shift = ((entry_raw_size + 7) & ~7) - entry_raw_size
fd.seek(shift, FROM_CURRENT)
+ for bm in self.bitmaps:
+ bm.read_bitmap_table(fd)
+
def load(self, fd):
self.read_bitmap_directory(fd)
@@ -181,6 +184,12 @@ class Qcow2BitmapDirEntry(Qcow2Struct):
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_bytes * struct.calcsize('Q')
+ table = [e[0] for e in struct.iter_unpack('>Q', fd.read(table_size))]
+ self.bitmap_table = Qcow2BitmapTable(table)
+
def dump_bitmap_dir_entry(self):
print()
print(f'{"Bitmap name":<25} {self.name}')
@@ -188,6 +197,37 @@ class Qcow2BitmapDirEntry(Qcow2Struct):
print(f'{"flag":<25} {fl}')
print(f'{"table size":<25} {self.bitmap_table_bytes} {"(bytes)"}')
super().dump()
+ self.bitmap_table.print_bitmap_table(self.cluster_size)
+
+
+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):
+ self.entries = []
+ for entry in raw_table:
+ self.entries.append(Qcow2BitmapTableEntry(entry))
+
+ def print_bitmap_table(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} {cluster_size}')
+ print("")
QCOW2_EXT_MAGIC_BITMAPS = 0x23852875