Skip to content

Commit 189b763

Browse files
authored
fix(qcow2util): removed some dead code (#153)
After the introduction of qcow2helper, most things were done in C for performance reasons. This dead code wasn't needed anymore. It was used to read the allocation table of a QCOW2. Signed-off-by: Damien Thenot <damien.thenot@vates.tech>
1 parent f3d5fa7 commit 189b763

1 file changed

Lines changed: 3 additions & 182 deletions

File tree

drivers/qcow2util.py

Lines changed: 3 additions & 182 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
# You should have received a copy of the GNU General Public License
1515
# along with this program. If not, see <https://www.gnu.org/licenses/>.
1616

17-
from sm_typing import Any, Callable, Dict, Final, List, Optional, Tuple, cast, override
17+
from sm_typing import Any, Callable, Dict, Final, Optional, Tuple, cast, override
1818
from typing import BinaryIO
1919

2020
import errno
@@ -56,84 +56,12 @@ class QCowUtil(CowUtil):
5656

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
59-
QCOW2_L2_SIZE = QCOW2_DEFAULT_CLUSTER_SIZE
6059
QCOW2_BACKING_FILE_OFFSET = 8
6160

62-
ALLOCATED_ENTRY_BIT = (
63-
0x8000_0000_0000_0000 # Bit 63 is the allocated bit for standard cluster
64-
)
65-
CLUSTER_TYPE_BIT = 0x4000_0000_0000_0000 # 0 for standard, 1 for compressed cluster
66-
L2_OFFSET_MASK = 0x00FF_FFFF_FFFF_FF00 # Bits 9-55 are offset of L2 table.
67-
CLUSTER_DESCRIPTION_MASK = 0x3FFF_FFFF_FFFF_FFFF # Bit 0-61 is cluster description
68-
STANDARD_CLUSTER_OFFSET_MASK = (
69-
0x00FF_FFFF_FFFF_FF00 # Bits 9-55 are offset of standard cluster
70-
)
71-
72-
def __init__(self):
73-
self.qcow_read = False
74-
75-
def _read_qcow2(self, path: str, read_clusters: bool = False):
76-
phys_disk_size = self.getSizePhys(path)
61+
def _read_qcow2(self, path: str):
7762
with open(path, "rb") as qcow2_file:
7863
self.filename = path # Keep the filename if clean is called
7964
self.header = self._read_qcow2_header(qcow2_file)
80-
if read_clusters:
81-
self.l1 = self._get_l1_entries(qcow2_file)
82-
# The l1_to_l2 allows to get L2 entries for a given L1. If L1 entry
83-
# is not allocated we store an empty list.
84-
self.l1_to_l2: Dict[int, List[int]] = {}
85-
86-
for l1_entry in self.l1:
87-
l2_offset = l1_entry & self.L2_OFFSET_MASK
88-
if l2_offset == 0:
89-
self.l1_to_l2[l1_entry] = []
90-
elif l2_offset > phys_disk_size: #TODO: This sometime happen for a correct VDI (while coalescing online?)
91-
raise xs_errors.XenError("VDISize", "L2 Offset is bigger than physical disk {}".format(path))
92-
else:
93-
self.l1_to_l2[l1_entry] = self._get_l2_entries(
94-
qcow2_file, l2_offset
95-
)
96-
self.qcow_read = True
97-
98-
def _get_l1_entries(self, file: BinaryIO) -> List[int]:
99-
"""Returns the list of all L1 entries.
100-
101-
Args:
102-
file: The qcow2 file object.
103-
104-
Returns:
105-
list: List of all L1 entries
106-
"""
107-
l1_table_offset = self.header["l1_table_offset"]
108-
file.seek(l1_table_offset)
109-
110-
l1_table_size = self.header["l1_size"] * 8 # Each L1 entry is 8 bytes
111-
l1_table = file.read(l1_table_size)
112-
113-
return [
114-
struct.unpack(">Q", l1_table[i : i + 8])[0]
115-
for i in range(0, len(l1_table), 8)
116-
]
117-
118-
@staticmethod
119-
def _get_l2_entries(file: BinaryIO, l2_offset: int) -> List[int]:
120-
"""Returns the list of all L2 entries at a given L2 offset.
121-
122-
Args:
123-
file: The qcow2 file.
124-
l2_offset: the L2 offset where to look for entries
125-
126-
Returns:
127-
list: List of all L2 entries
128-
"""
129-
# The size of L2 is 65536 bytes and each entry is 8 bytes.
130-
file.seek(l2_offset)
131-
l2_table = file.read(QCowUtil.QCOW2_L2_SIZE)
132-
133-
return [
134-
struct.unpack(">Q", l2_table[i : i + 8])[0]
135-
for i in range(0, len(l2_table), 8)
136-
]
13765

13866
@staticmethod
13967
def _read_qcow2_backingfile(file: BinaryIO, backing_file_offset: int , backing_file_size: int) -> str:
@@ -209,78 +137,6 @@ def _read_qcow2_header(file: BinaryIO) -> Dict[str, Any]:
209137
"parent": parent_name,
210138
}
211139

212-
@staticmethod
213-
def _is_l1_allocated(entry: int) -> bool:
214-
"""Checks if the given L1 entry is allocated.
215-
216-
If the offset is 0 then the L2 table and all clusters described
217-
by this L2 table are unallocated.
218-
219-
Args:
220-
entry: L1 entry
221-
222-
Returns:
223-
bool: True if the L1 entry is allocated (ie has a valid offset).
224-
False otherwise.
225-
"""
226-
return (entry & QCowUtil.L2_OFFSET_MASK) != 0
227-
228-
@staticmethod
229-
def _is_l2_allocated(entry: int) -> bool:
230-
"""Checks if a given entry is allocated.
231-
232-
Currently we only support standard clusters. And for standard clusters
233-
the bit 63 is set to 1 for allocated ones or offset is not 0.
234-
235-
Args:
236-
entry: L2 entry
237-
238-
Returns:
239-
bool: Returns True if the L2 entry is allocated, False otherwise
240-
241-
Raises:
242-
raise an exception if the cluster is not a standard one.
243-
"""
244-
assert entry & QCowUtil.CLUSTER_TYPE_BIT == 0
245-
return (entry & QCowUtil.ALLOCATED_ENTRY_BIT != 0) or (
246-
entry & QCowUtil.STANDARD_CLUSTER_OFFSET_MASK != 0
247-
)
248-
249-
@staticmethod
250-
def _get_allocated_clusters(l2_entries: List[int]) -> List[int]:
251-
"""Get all allocated clusters in a given list of L2 entries.
252-
253-
Args:
254-
l2_entries: A list of L2 entries.
255-
256-
Returns:
257-
A list of all allocated entries
258-
"""
259-
return [entry for entry in l2_entries if QCowUtil._is_l2_allocated(entry)]
260-
261-
@staticmethod
262-
def _get_cluster_to_byte(clusters: int, cluster_bits: int) -> int:
263-
# (1 << cluster_bits) give cluster size in byte
264-
return clusters * (1 << cluster_bits)
265-
266-
def _get_number_of_allocated_clusters(self) -> int:
267-
"""Get the number of allocated clusters.
268-
269-
Args:
270-
self: A QcowInfo object.
271-
272-
Returns:
273-
An integer that is the list of allocated clusters.
274-
"""
275-
assert(self.qcow_read)
276-
277-
allocated_clusters = 0
278-
279-
for l2_entries in self.l1_to_l2.values():
280-
allocated_clusters += len(self._get_allocated_clusters(l2_entries))
281-
282-
return allocated_clusters
283-
284140
@staticmethod
285141
def _move_backing_file(
286142
f: BinaryIO, old_offset: int, new_offset: int, data_size: int
@@ -323,8 +179,6 @@ def _add_or_find_custom_header(self) -> int:
323179
If data offset is 0 something weird happens.
324180
The qcow2 file in self.filename can be modified.
325181
"""
326-
assert self.qcow_read
327-
328182
header_length = 72 # This is the default value for version 2 images
329183

330184
custom_header_type = 0x76617465 # vate: it is easy to recognize with hexdump -C
@@ -392,39 +246,6 @@ def _add_or_find_custom_header(self) -> int:
392246

393247
return custom_data_offset
394248

395-
def _set_l1_zero(self):
396-
zero = int(0).to_bytes(1, "little")
397-
nb_of_entries_per_cluster = QCOW2_DEFAULT_CLUSTER_SIZE/8
398-
return list(zero * int(nb_of_entries_per_cluster/8))
399-
400-
def _set_l2_zero(self, b, i):
401-
return b & ~(1 << i)
402-
403-
def _set_l2_one(self, b, i):
404-
return b | (1 << i)
405-
406-
def _create_bitmap(self) -> bytes:
407-
idx: int = 0
408-
bitmap = list()
409-
b = 0
410-
for l1_entry in self.l1:
411-
if not self._is_l1_allocated(l1_entry):
412-
bitmap.extend(self._set_l1_zero())
413-
continue
414-
415-
l2_table = self.l1_to_l2[l1_entry] #L2 is cluster_size/8 entries of cluster_size page
416-
for l2_entry in l2_table:
417-
if self._is_l2_allocated(l2_entry):
418-
b = self._set_l2_one(b, idx)
419-
else:
420-
b = self._set_l2_zero(b, idx)
421-
idx += 1
422-
if idx == 8:
423-
bitmap.append(b)
424-
b = 0
425-
idx = 0
426-
return struct.pack("B"*len(bitmap), *bitmap)
427-
428249
# ----
429250
# Implementation of CowUtil
430251
# ----
@@ -687,7 +508,7 @@ def killData(self, path: str) -> None:
687508
Returns:
688509
nothing.
689510
"""
690-
self._read_qcow2(path, read_clusters=True)
511+
self._read_qcow2(path)
691512
# We need to reset L1 entries and then just truncate the file right
692513
# after L1 entries
693514
with open(self.filename, "r+b") as file:

0 commit comments

Comments
 (0)