Skip to content

Commit 2e2d0aa

Browse files
authored
Some QCOW2 improvements and unit tests (#61)
1 parent f9bdad9 commit 2e2d0aa

14 files changed

Lines changed: 570 additions & 148 deletions

dissect/hypervisor/disk/c_qcow2.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,6 @@
171171
)
172172

173173

174-
def ctz(value: int, size: int = 32) -> int:
174+
def ctz(value: int) -> int:
175175
"""Count the number of zero bits in an integer of a given size."""
176-
for i in range(size):
177-
if value & (1 << i):
178-
return i
179-
return 0
176+
return (value & -value).bit_length() - 1 if value else 0
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
# Generated by cstruct-stubgen
2+
from typing import BinaryIO, Literal, overload
3+
4+
import dissect.cstruct as __cs__
5+
from typing_extensions import TypeAlias
6+
7+
class _c_qcow2(__cs__.cstruct):
8+
MIN_CLUSTER_BITS: Literal[9] = ...
9+
MAX_CLUSTER_BITS: Literal[21] = ...
10+
QCOW2_COMPRESSED_SECTOR_SIZE: Literal[512] = ...
11+
QCOW2_COMPRESSION_TYPE_ZLIB: Literal[0] = ...
12+
QCOW2_COMPRESSION_TYPE_ZSTD: Literal[1] = ...
13+
L1E_SIZE: Literal[8] = ...
14+
L2E_SIZE_NORMAL: Literal[8] = ...
15+
L2E_SIZE_EXTENDED: Literal[16] = ...
16+
L1E_OFFSET_MASK: Literal[72057594037927424] = ...
17+
L2E_OFFSET_MASK: Literal[72057594037927424] = ...
18+
L2E_COMPRESSED_OFFSET_SIZE_MASK: Literal[4611686018427387903] = ...
19+
QCOW_OFLAG_COPIED: Literal[9223372036854775808] = ...
20+
QCOW_OFLAG_COMPRESSED: Literal[4611686018427387904] = ...
21+
QCOW_OFLAG_ZERO: Literal[1] = ...
22+
QCOW_EXTL2_SUBCLUSTERS_PER_CLUSTER: Literal[32] = ...
23+
QCOW2_INCOMPAT_DIRTY_BITNR: Literal[0] = ...
24+
QCOW2_INCOMPAT_CORRUPT_BITNR: Literal[1] = ...
25+
QCOW2_INCOMPAT_DATA_FILE_BITNR: Literal[2] = ...
26+
QCOW2_INCOMPAT_COMPRESSION_BITNR: Literal[3] = ...
27+
QCOW2_INCOMPAT_EXTL2_BITNR: Literal[4] = ...
28+
QCOW2_INCOMPAT_DIRTY: Literal[1] = ...
29+
QCOW2_INCOMPAT_CORRUPT: Literal[2] = ...
30+
QCOW2_INCOMPAT_DATA_FILE: Literal[4] = ...
31+
QCOW2_INCOMPAT_COMPRESSION: Literal[8] = ...
32+
QCOW2_INCOMPAT_EXTL2: Literal[16] = ...
33+
QCOW2_EXT_MAGIC_END: Literal[0] = ...
34+
QCOW2_EXT_MAGIC_BACKING_FORMAT: Literal[3799591626] = ...
35+
QCOW2_EXT_MAGIC_FEATURE_TABLE: Literal[1745090647] = ...
36+
QCOW2_EXT_MAGIC_CRYPTO_HEADER: Literal[87539319] = ...
37+
QCOW2_EXT_MAGIC_BITMAPS: Literal[595929205] = ...
38+
QCOW2_EXT_MAGIC_DATA_FILE: Literal[1145132097] = ...
39+
class QCowHeader(__cs__.Structure):
40+
magic: _c_qcow2.uint32
41+
version: _c_qcow2.uint32
42+
backing_file_offset: _c_qcow2.uint64
43+
backing_file_size: _c_qcow2.uint32
44+
cluster_bits: _c_qcow2.uint32
45+
size: _c_qcow2.uint64
46+
crypt_method: _c_qcow2.uint32
47+
l1_size: _c_qcow2.uint32
48+
l1_table_offset: _c_qcow2.uint64
49+
refcount_table_offset: _c_qcow2.uint64
50+
refcount_table_clusters: _c_qcow2.uint32
51+
nb_snapshots: _c_qcow2.uint32
52+
snapshots_offset: _c_qcow2.uint64
53+
incompatible_features: _c_qcow2.uint64
54+
compatible_features: _c_qcow2.uint64
55+
autoclear_features: _c_qcow2.uint64
56+
refcount_order: _c_qcow2.uint32
57+
header_length: _c_qcow2.uint32
58+
compression_type: _c_qcow2.uint8
59+
padding: __cs__.Array[_c_qcow2.uint8]
60+
@overload
61+
def __init__(
62+
self,
63+
magic: _c_qcow2.uint32 | None = ...,
64+
version: _c_qcow2.uint32 | None = ...,
65+
backing_file_offset: _c_qcow2.uint64 | None = ...,
66+
backing_file_size: _c_qcow2.uint32 | None = ...,
67+
cluster_bits: _c_qcow2.uint32 | None = ...,
68+
size: _c_qcow2.uint64 | None = ...,
69+
crypt_method: _c_qcow2.uint32 | None = ...,
70+
l1_size: _c_qcow2.uint32 | None = ...,
71+
l1_table_offset: _c_qcow2.uint64 | None = ...,
72+
refcount_table_offset: _c_qcow2.uint64 | None = ...,
73+
refcount_table_clusters: _c_qcow2.uint32 | None = ...,
74+
nb_snapshots: _c_qcow2.uint32 | None = ...,
75+
snapshots_offset: _c_qcow2.uint64 | None = ...,
76+
incompatible_features: _c_qcow2.uint64 | None = ...,
77+
compatible_features: _c_qcow2.uint64 | None = ...,
78+
autoclear_features: _c_qcow2.uint64 | None = ...,
79+
refcount_order: _c_qcow2.uint32 | None = ...,
80+
header_length: _c_qcow2.uint32 | None = ...,
81+
compression_type: _c_qcow2.uint8 | None = ...,
82+
padding: __cs__.Array[_c_qcow2.uint8] | None = ...,
83+
): ...
84+
@overload
85+
def __init__(self, fh: bytes | memoryview | bytearray | BinaryIO, /): ...
86+
87+
class QCowExtension(__cs__.Structure):
88+
magic: _c_qcow2.uint32
89+
len: _c_qcow2.uint32
90+
@overload
91+
def __init__(self, magic: _c_qcow2.uint32 | None = ..., len: _c_qcow2.uint32 | None = ...): ...
92+
@overload
93+
def __init__(self, fh: bytes | memoryview | bytearray | BinaryIO, /): ...
94+
95+
class Qcow2CryptoHeaderExtension(__cs__.Structure):
96+
offset: _c_qcow2.uint64
97+
length: _c_qcow2.uint64
98+
@overload
99+
def __init__(self, offset: _c_qcow2.uint64 | None = ..., length: _c_qcow2.uint64 | None = ...): ...
100+
@overload
101+
def __init__(self, fh: bytes | memoryview | bytearray | BinaryIO, /): ...
102+
103+
class Qcow2BitmapHeaderExt(__cs__.Structure):
104+
nb_bitmaps: _c_qcow2.uint32
105+
reserved32: _c_qcow2.uint32
106+
bitmap_directory_size: _c_qcow2.uint64
107+
bitmap_directory_offset: _c_qcow2.uint64
108+
@overload
109+
def __init__(
110+
self,
111+
nb_bitmaps: _c_qcow2.uint32 | None = ...,
112+
reserved32: _c_qcow2.uint32 | None = ...,
113+
bitmap_directory_size: _c_qcow2.uint64 | None = ...,
114+
bitmap_directory_offset: _c_qcow2.uint64 | None = ...,
115+
): ...
116+
@overload
117+
def __init__(self, fh: bytes | memoryview | bytearray | BinaryIO, /): ...
118+
119+
class QCowSnapshotHeader(__cs__.Structure):
120+
l1_table_offset: _c_qcow2.uint64
121+
l1_size: _c_qcow2.uint32
122+
id_str_size: _c_qcow2.uint16
123+
name_size: _c_qcow2.uint16
124+
date_sec: _c_qcow2.uint32
125+
date_nsec: _c_qcow2.uint32
126+
vm_clock_nsec: _c_qcow2.uint64
127+
vm_state_size: _c_qcow2.uint32
128+
extra_data_size: _c_qcow2.uint32
129+
@overload
130+
def __init__(
131+
self,
132+
l1_table_offset: _c_qcow2.uint64 | None = ...,
133+
l1_size: _c_qcow2.uint32 | None = ...,
134+
id_str_size: _c_qcow2.uint16 | None = ...,
135+
name_size: _c_qcow2.uint16 | None = ...,
136+
date_sec: _c_qcow2.uint32 | None = ...,
137+
date_nsec: _c_qcow2.uint32 | None = ...,
138+
vm_clock_nsec: _c_qcow2.uint64 | None = ...,
139+
vm_state_size: _c_qcow2.uint32 | None = ...,
140+
extra_data_size: _c_qcow2.uint32 | None = ...,
141+
): ...
142+
@overload
143+
def __init__(self, fh: bytes | memoryview | bytearray | BinaryIO, /): ...
144+
145+
class QCowSnapshotExtraData(__cs__.Structure):
146+
vm_state_size_large: _c_qcow2.uint64
147+
disk_size: _c_qcow2.uint64
148+
icount: _c_qcow2.uint64
149+
@overload
150+
def __init__(
151+
self,
152+
vm_state_size_large: _c_qcow2.uint64 | None = ...,
153+
disk_size: _c_qcow2.uint64 | None = ...,
154+
icount: _c_qcow2.uint64 | None = ...,
155+
): ...
156+
@overload
157+
def __init__(self, fh: bytes | memoryview | bytearray | BinaryIO, /): ...
158+
159+
class QCow2ClusterType(__cs__.Enum):
160+
QCOW2_CLUSTER_UNALLOCATED = ...
161+
QCOW2_CLUSTER_ZERO_PLAIN = ...
162+
QCOW2_CLUSTER_ZERO_ALLOC = ...
163+
QCOW2_CLUSTER_NORMAL = ...
164+
QCOW2_CLUSTER_COMPRESSED = ...
165+
166+
class QCow2SubclusterType(__cs__.Enum):
167+
QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN = ...
168+
QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC = ...
169+
QCOW2_SUBCLUSTER_ZERO_PLAIN = ...
170+
QCOW2_SUBCLUSTER_ZERO_ALLOC = ...
171+
QCOW2_SUBCLUSTER_NORMAL = ...
172+
QCOW2_SUBCLUSTER_COMPRESSED = ...
173+
QCOW2_SUBCLUSTER_INVALID = ...
174+
175+
# Technically `c_qcow2` is an instance of `_c_qcow2`, but then we can't use it in type hints
176+
c_qcow2: TypeAlias = _c_qcow2
177+
178+
QCOW2_MAGIC: int
179+
QCOW2_MAGIC_BYTES: bytes
180+
181+
QCOW2_INCOMPAT_MASK: int
182+
183+
QCow2ClusterType: TypeAlias = _c_qcow2.QCow2ClusterType
184+
QCow2SubclusterType: TypeAlias = _c_qcow2.QCow2SubclusterType
185+
186+
NORMAL_SUBCLUSTER_TYPES: tuple[QCow2SubclusterType, ...]
187+
ZERO_SUBCLUSTER_TYPES: tuple[QCow2SubclusterType, ...]
188+
UNALLOCATED_SUBCLUSTER_TYPES: tuple[QCow2SubclusterType, ...]
189+
190+
def ctz(value: int) -> int: ...

0 commit comments

Comments
 (0)