Skip to content

Commit c5476ba

Browse files
committed
feat(qcow2util): Support Qcow2 subclusters
Create Qcow2 with subclusters if the parent support it. calcOverheadEmpty defaults to extended_l2 because it needs more space. Signed-off-by: Anthoine Bourgeois <anthoine.bourgeois@vates.tech>
1 parent deaa96f commit c5476ba

1 file changed

Lines changed: 18 additions & 4 deletions

File tree

‎drivers/qcow2util.py‎

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class QCowUtil(CowUtil):
5757
QCOW2_MAGIC = 0x514649FB # b"QFI\xfb": Magic number for QCOW2 files
5858
QCOW2_HEADER_SIZE = 104 # In fact the last information we need is at offset 40-47
5959
QCOW2_BACKING_FILE_OFFSET = 8
60+
QCOW2_INCOMPATIBLE_FEATURE_EXTENDED_L2 = 0x10
6061

6162
def _read_qcow2(self, path: str):
6263
with open(path, "rb") as qcow2_file:
@@ -100,6 +101,7 @@ def _read_qcow2_header(file: BinaryIO) -> Dict[str, Any]:
100101
# refcount_table_clusters: u32, // Number of clusters for the refcount table
101102
# nb_snapshots: u32, // Number of snapshots in the image
102103
# snapshots_offset: u64, // Offset to the snapshot table
104+
# incompatible_features: u64, // Bitmask of incompatible features
103105

104106
file.seek(0)
105107
header = file.read(QCowUtil.QCOW2_HEADER_SIZE)
@@ -117,7 +119,8 @@ def _read_qcow2_header(file: BinaryIO) -> Dict[str, Any]:
117119
_,
118120
_,
119121
snapshots_offset,
120-
) = struct.unpack(">IIQIIQIIQQIIQ", header[:72])
122+
incompatible_features,
123+
) = struct.unpack(">IIQIIQIIQQIIQQ", header[:80])
121124

122125
if magic != QCowUtil.QCOW2_MAGIC:
123126
raise ValueError("Not a valid QCOW2 file")
@@ -135,6 +138,7 @@ def _read_qcow2_header(file: BinaryIO) -> Dict[str, Any]:
135138
"refcount_table_offset": refcount_table_offset,
136139
"snapshots_offset": snapshots_offset,
137140
"parent": parent_name,
141+
"incompatible_features": incompatible_features,
138142
}
139143

140144
@staticmethod
@@ -246,6 +250,9 @@ def _add_or_find_custom_header(self) -> int:
246250

247251
return custom_data_offset
248252

253+
def _convert_bool_to_qemu_option_value(self, option: bool) -> str:
254+
return 'on' if option else 'off'
255+
249256
# ----
250257
# Implementation of CowUtil
251258
# ----
@@ -263,6 +270,11 @@ def getBlockSize(self, path: str) -> int:
263270
self._read_qcow2(path)
264271
return 1 << self.header["cluster_bits"]
265272

273+
@override
274+
def getExtendedL2(self, path: str) -> bool:
275+
self._read_qcow2(path)
276+
return bool(self.header["incompatible_features"] & self.QCOW2_INCOMPATIBLE_FEATURE_EXTENDED_L2)
277+
266278
@override
267279
def getFooterSize(self) -> int:
268280
return 0
@@ -282,7 +294,7 @@ def calcOverheadEmpty(self, virtual_size: int, block_size: Optional[int] = None)
282294
cluster_size = block_size
283295
else:
284296
cluster_size = QCOW2_DEFAULT_CLUSTER_SIZE
285-
cmd = [QEMU_IMG, "measure", "-O", "qcow2", "--output", "json", "-o", f"cluster_size={cluster_size}", "--size", f"{virtual_size}"]
297+
cmd = [QEMU_IMG, "measure", "-O", "qcow2", "--output", "json", "-o", f"cluster_size={cluster_size},extended_l2=on", "--size", f"{virtual_size}"]
286298
output = json.loads(self._ioretry(cmd))
287299
return int(output["required"])
288300

@@ -636,9 +648,11 @@ def snapshot(
636648
if parentRaw:
637649
parent_type = RAW_TYPE
638650
cluster_size = QCOW2_DEFAULT_CLUSTER_SIZE
651+
extended_l2 = False
639652
else:
640653
parent_type = QCOW2_TYPE
641654
cluster_size = self.getBlockSize(parent)
655+
extended_l2 = self.getExtendedL2(parent)
642656
args = ["-f", QCOW2_TYPE, "-F", parent_type, "-b", parent]
643657

644658
if is_mirror_image:
@@ -650,9 +664,9 @@ def snapshot(
650664
# Ensuring we go back to a better cluster_size for performance reasons.
651665
# This limit our images max size to 64TiB.
652666
cluster_size = 16 * 1024 # 16KiB
653-
args.extend(["-o", "extended_l2=on"])
667+
extended_l2 = True
654668

655-
args.extend(["-o", f"cluster_size={cluster_size}"])
669+
args.extend(["-o", f"cluster_size={cluster_size},extended_l2={self._convert_bool_to_qemu_option_value(extended_l2)}"])
656670
cmd.extend(args)
657671
cmd.append(path)
658672

0 commit comments

Comments
 (0)