xref: /openbmc/qemu/scripts/qcow2-to-stdout.py (revision 0462a32b4f63b2448b4a196381138afd50719dc4)
1*df957115SAlberto Garcia#!/usr/bin/env python3
2*df957115SAlberto Garcia
3*df957115SAlberto Garcia# This tool reads a disk image in any format and converts it to qcow2,
4*df957115SAlberto Garcia# writing the result directly to stdout.
5*df957115SAlberto Garcia#
6*df957115SAlberto Garcia# Copyright (C) 2024 Igalia, S.L.
7*df957115SAlberto Garcia#
8*df957115SAlberto Garcia# Authors: Alberto Garcia <berto@igalia.com>
9*df957115SAlberto Garcia#          Madeeha Javed <javed@igalia.com>
10*df957115SAlberto Garcia#
11*df957115SAlberto Garcia# SPDX-License-Identifier: GPL-2.0-or-later
12*df957115SAlberto Garcia#
13*df957115SAlberto Garcia# qcow2 files produced by this script are always arranged like this:
14*df957115SAlberto Garcia#
15*df957115SAlberto Garcia# - qcow2 header
16*df957115SAlberto Garcia# - refcount table
17*df957115SAlberto Garcia# - refcount blocks
18*df957115SAlberto Garcia# - L1 table
19*df957115SAlberto Garcia# - L2 tables
20*df957115SAlberto Garcia# - Data clusters
21*df957115SAlberto Garcia#
22*df957115SAlberto Garcia# A note about variable names: in qcow2 there is one refcount table
23*df957115SAlberto Garcia# and one (active) L1 table, although each can occupy several
24*df957115SAlberto Garcia# clusters. For the sake of simplicity the code sometimes talks about
25*df957115SAlberto Garcia# refcount tables and L1 tables when referring to those clusters.
26*df957115SAlberto Garcia
27*df957115SAlberto Garciaimport argparse
28*df957115SAlberto Garciaimport errno
29*df957115SAlberto Garciaimport math
30*df957115SAlberto Garciaimport os
31*df957115SAlberto Garciaimport signal
32*df957115SAlberto Garciaimport struct
33*df957115SAlberto Garciaimport subprocess
34*df957115SAlberto Garciaimport sys
35*df957115SAlberto Garciaimport tempfile
36*df957115SAlberto Garciaimport time
37*df957115SAlberto Garciafrom contextlib import contextmanager
38*df957115SAlberto Garcia
39*df957115SAlberto GarciaQCOW2_DEFAULT_CLUSTER_SIZE = 65536
40*df957115SAlberto GarciaQCOW2_DEFAULT_REFCOUNT_BITS = 16
41*df957115SAlberto GarciaQCOW2_FEATURE_NAME_TABLE = 0x6803F857
42*df957115SAlberto GarciaQCOW2_DATA_FILE_NAME_STRING = 0x44415441
43*df957115SAlberto GarciaQCOW2_V3_HEADER_LENGTH = 112  # Header length in QEMU 9.0. Must be a multiple of 8
44*df957115SAlberto GarciaQCOW2_INCOMPAT_DATA_FILE_BIT = 2
45*df957115SAlberto GarciaQCOW2_AUTOCLEAR_DATA_FILE_RAW_BIT = 1
46*df957115SAlberto GarciaQCOW_OFLAG_COPIED = 1 << 63
47*df957115SAlberto GarciaQEMU_STORAGE_DAEMON = "qemu-storage-daemon"
48*df957115SAlberto Garcia
49*df957115SAlberto Garcia
50*df957115SAlberto Garciadef bitmap_set(bitmap, idx):
51*df957115SAlberto Garcia    bitmap[idx // 8] |= 1 << (idx % 8)
52*df957115SAlberto Garcia
53*df957115SAlberto Garcia
54*df957115SAlberto Garciadef bitmap_is_set(bitmap, idx):
55*df957115SAlberto Garcia    return (bitmap[idx // 8] & (1 << (idx % 8))) != 0
56*df957115SAlberto Garcia
57*df957115SAlberto Garcia
58*df957115SAlberto Garciadef bitmap_iterator(bitmap, length):
59*df957115SAlberto Garcia    for idx in range(length):
60*df957115SAlberto Garcia        if bitmap_is_set(bitmap, idx):
61*df957115SAlberto Garcia            yield idx
62*df957115SAlberto Garcia
63*df957115SAlberto Garcia
64*df957115SAlberto Garciadef align_up(num, d):
65*df957115SAlberto Garcia    return d * math.ceil(num / d)
66*df957115SAlberto Garcia
67*df957115SAlberto Garcia
68*df957115SAlberto Garcia# Holes in the input file contain only zeroes so we can skip them and
69*df957115SAlberto Garcia# save time. This function returns the indexes of the clusters that
70*df957115SAlberto Garcia# are known to contain data. Those are the ones that we need to read.
71*df957115SAlberto Garciadef clusters_with_data(fd, cluster_size):
72*df957115SAlberto Garcia    data_to = 0
73*df957115SAlberto Garcia    while True:
74*df957115SAlberto Garcia        try:
75*df957115SAlberto Garcia            data_from = os.lseek(fd, data_to, os.SEEK_DATA)
76*df957115SAlberto Garcia            data_to = align_up(os.lseek(fd, data_from, os.SEEK_HOLE), cluster_size)
77*df957115SAlberto Garcia            for idx in range(data_from // cluster_size, data_to // cluster_size):
78*df957115SAlberto Garcia                yield idx
79*df957115SAlberto Garcia        except OSError as err:
80*df957115SAlberto Garcia            if err.errno == errno.ENXIO:  # End of file reached
81*df957115SAlberto Garcia                break
82*df957115SAlberto Garcia            raise err
83*df957115SAlberto Garcia
84*df957115SAlberto Garcia
85*df957115SAlberto Garcia# write_qcow2_content() expects a raw input file. If we have a different
86*df957115SAlberto Garcia# format we can use qemu-storage-daemon to make it appear as raw.
87*df957115SAlberto Garcia@contextmanager
88*df957115SAlberto Garciadef get_input_as_raw_file(input_file, input_format):
89*df957115SAlberto Garcia    if input_format == "raw":
90*df957115SAlberto Garcia        yield input_file
91*df957115SAlberto Garcia        return
92*df957115SAlberto Garcia    try:
93*df957115SAlberto Garcia        temp_dir = tempfile.mkdtemp()
94*df957115SAlberto Garcia        pid_file = os.path.join(temp_dir, "pid")
95*df957115SAlberto Garcia        raw_file = os.path.join(temp_dir, "raw")
96*df957115SAlberto Garcia        open(raw_file, "wb").close()
97*df957115SAlberto Garcia        ret = subprocess.run(
98*df957115SAlberto Garcia            [
99*df957115SAlberto Garcia                QEMU_STORAGE_DAEMON,
100*df957115SAlberto Garcia                "--daemonize",
101*df957115SAlberto Garcia                "--pidfile", pid_file,
102*df957115SAlberto Garcia                "--blockdev", f"driver=file,node-name=file0,driver=file,filename={input_file},read-only=on",
103*df957115SAlberto Garcia                "--blockdev", f"driver={input_format},node-name=disk0,file=file0,read-only=on",
104*df957115SAlberto Garcia                "--export", f"type=fuse,id=export0,node-name=disk0,mountpoint={raw_file},writable=off",
105*df957115SAlberto Garcia            ],
106*df957115SAlberto Garcia            capture_output=True,
107*df957115SAlberto Garcia        )
108*df957115SAlberto Garcia        if ret.returncode != 0:
109*df957115SAlberto Garcia            sys.exit("[Error] Could not start the qemu-storage-daemon:\n" +
110*df957115SAlberto Garcia                     ret.stderr.decode().rstrip('\n'))
111*df957115SAlberto Garcia        yield raw_file
112*df957115SAlberto Garcia    finally:
113*df957115SAlberto Garcia        # Kill the storage daemon on exit
114*df957115SAlberto Garcia        # and remove all temporary files
115*df957115SAlberto Garcia        if os.path.exists(pid_file):
116*df957115SAlberto Garcia            with open(pid_file, "r") as f:
117*df957115SAlberto Garcia                pid = int(f.readline())
118*df957115SAlberto Garcia            os.kill(pid, signal.SIGTERM)
119*df957115SAlberto Garcia            while os.path.exists(pid_file):
120*df957115SAlberto Garcia                time.sleep(0.1)
121*df957115SAlberto Garcia        os.unlink(raw_file)
122*df957115SAlberto Garcia        os.rmdir(temp_dir)
123*df957115SAlberto Garcia
124*df957115SAlberto Garcia
125*df957115SAlberto Garciadef write_features(cluster, offset, data_file_name):
126*df957115SAlberto Garcia    if data_file_name is not None:
127*df957115SAlberto Garcia        encoded_name = data_file_name.encode("utf-8")
128*df957115SAlberto Garcia        padded_name_len = align_up(len(encoded_name), 8)
129*df957115SAlberto Garcia        struct.pack_into(f">II{padded_name_len}s", cluster, offset,
130*df957115SAlberto Garcia                         QCOW2_DATA_FILE_NAME_STRING,
131*df957115SAlberto Garcia                         len(encoded_name),
132*df957115SAlberto Garcia                         encoded_name)
133*df957115SAlberto Garcia        offset += 8 + padded_name_len
134*df957115SAlberto Garcia
135*df957115SAlberto Garcia    qcow2_features = [
136*df957115SAlberto Garcia        # Incompatible
137*df957115SAlberto Garcia        (0, 0, "dirty bit"),
138*df957115SAlberto Garcia        (0, 1, "corrupt bit"),
139*df957115SAlberto Garcia        (0, 2, "external data file"),
140*df957115SAlberto Garcia        (0, 3, "compression type"),
141*df957115SAlberto Garcia        (0, 4, "extended L2 entries"),
142*df957115SAlberto Garcia        # Compatible
143*df957115SAlberto Garcia        (1, 0, "lazy refcounts"),
144*df957115SAlberto Garcia        # Autoclear
145*df957115SAlberto Garcia        (2, 0, "bitmaps"),
146*df957115SAlberto Garcia        (2, 1, "raw external data"),
147*df957115SAlberto Garcia    ]
148*df957115SAlberto Garcia    struct.pack_into(">I", cluster, offset, QCOW2_FEATURE_NAME_TABLE)
149*df957115SAlberto Garcia    struct.pack_into(">I", cluster, offset + 4, len(qcow2_features) * 48)
150*df957115SAlberto Garcia    offset += 8
151*df957115SAlberto Garcia    for feature_type, feature_bit, feature_name in qcow2_features:
152*df957115SAlberto Garcia        struct.pack_into(">BB46s", cluster, offset,
153*df957115SAlberto Garcia                         feature_type, feature_bit, feature_name.encode("ascii"))
154*df957115SAlberto Garcia        offset += 48
155*df957115SAlberto Garcia
156*df957115SAlberto Garcia
157*df957115SAlberto Garciadef write_qcow2_content(input_file, cluster_size, refcount_bits, data_file_name, data_file_raw):
158*df957115SAlberto Garcia    # Some basic values
159*df957115SAlberto Garcia    l1_entries_per_table = cluster_size // 8
160*df957115SAlberto Garcia    l2_entries_per_table = cluster_size // 8
161*df957115SAlberto Garcia    refcounts_per_table  = cluster_size // 8
162*df957115SAlberto Garcia    refcounts_per_block  = cluster_size * 8 // refcount_bits
163*df957115SAlberto Garcia
164*df957115SAlberto Garcia    # Virtual disk size, number of data clusters and L1 entries
165*df957115SAlberto Garcia    disk_size = align_up(os.path.getsize(input_file), 512)
166*df957115SAlberto Garcia    total_data_clusters = math.ceil(disk_size / cluster_size)
167*df957115SAlberto Garcia    l1_entries = math.ceil(total_data_clusters / l2_entries_per_table)
168*df957115SAlberto Garcia    allocated_l1_tables = math.ceil(l1_entries / l1_entries_per_table)
169*df957115SAlberto Garcia
170*df957115SAlberto Garcia    # Max L1 table size is 32 MB (QCOW_MAX_L1_SIZE in block/qcow2.h)
171*df957115SAlberto Garcia    if (l1_entries * 8) > (32 * 1024 * 1024):
172*df957115SAlberto Garcia        sys.exit("[Error] The image size is too large. Try using a larger cluster size.")
173*df957115SAlberto Garcia
174*df957115SAlberto Garcia    # Two bitmaps indicating which L1 and L2 entries are set
175*df957115SAlberto Garcia    l1_bitmap = bytearray(allocated_l1_tables * l1_entries_per_table // 8)
176*df957115SAlberto Garcia    l2_bitmap = bytearray(l1_entries * l2_entries_per_table // 8)
177*df957115SAlberto Garcia    allocated_l2_tables = 0
178*df957115SAlberto Garcia    allocated_data_clusters = 0
179*df957115SAlberto Garcia
180*df957115SAlberto Garcia    if data_file_raw:
181*df957115SAlberto Garcia        # If data_file_raw is set then all clusters are allocated and
182*df957115SAlberto Garcia        # we don't need to read the input file at all.
183*df957115SAlberto Garcia        allocated_l2_tables = l1_entries
184*df957115SAlberto Garcia        for idx in range(l1_entries):
185*df957115SAlberto Garcia            bitmap_set(l1_bitmap, idx)
186*df957115SAlberto Garcia        for idx in range(total_data_clusters):
187*df957115SAlberto Garcia            bitmap_set(l2_bitmap, idx)
188*df957115SAlberto Garcia    else:
189*df957115SAlberto Garcia        # Open the input file for reading
190*df957115SAlberto Garcia        fd = os.open(input_file, os.O_RDONLY)
191*df957115SAlberto Garcia        zero_cluster = bytes(cluster_size)
192*df957115SAlberto Garcia        # Read all the clusters that contain data
193*df957115SAlberto Garcia        for idx in clusters_with_data(fd, cluster_size):
194*df957115SAlberto Garcia            cluster = os.pread(fd, cluster_size, cluster_size * idx)
195*df957115SAlberto Garcia            # If the last cluster is smaller than cluster_size pad it with zeroes
196*df957115SAlberto Garcia            if len(cluster) < cluster_size:
197*df957115SAlberto Garcia                cluster += bytes(cluster_size - len(cluster))
198*df957115SAlberto Garcia            # If a cluster has non-zero data then it must be allocated
199*df957115SAlberto Garcia            # in the output file and its L2 entry must be set
200*df957115SAlberto Garcia            if cluster != zero_cluster:
201*df957115SAlberto Garcia                bitmap_set(l2_bitmap, idx)
202*df957115SAlberto Garcia                allocated_data_clusters += 1
203*df957115SAlberto Garcia                # Allocated data clusters also need their corresponding L1 entry and L2 table
204*df957115SAlberto Garcia                l1_idx = math.floor(idx / l2_entries_per_table)
205*df957115SAlberto Garcia                if not bitmap_is_set(l1_bitmap, l1_idx):
206*df957115SAlberto Garcia                    bitmap_set(l1_bitmap, l1_idx)
207*df957115SAlberto Garcia                    allocated_l2_tables += 1
208*df957115SAlberto Garcia
209*df957115SAlberto Garcia    # Total amount of allocated clusters excluding the refcount blocks and table
210*df957115SAlberto Garcia    total_allocated_clusters = 1 + allocated_l1_tables + allocated_l2_tables
211*df957115SAlberto Garcia    if data_file_name is None:
212*df957115SAlberto Garcia        total_allocated_clusters += allocated_data_clusters
213*df957115SAlberto Garcia
214*df957115SAlberto Garcia    # Clusters allocated for the refcount blocks and table
215*df957115SAlberto Garcia    allocated_refcount_blocks = math.ceil(total_allocated_clusters  / refcounts_per_block)
216*df957115SAlberto Garcia    allocated_refcount_tables = math.ceil(allocated_refcount_blocks / refcounts_per_table)
217*df957115SAlberto Garcia
218*df957115SAlberto Garcia    # Now we have a problem because allocated_refcount_blocks and allocated_refcount_tables...
219*df957115SAlberto Garcia    # (a) increase total_allocated_clusters, and
220*df957115SAlberto Garcia    # (b) need to be recalculated when total_allocated_clusters is increased
221*df957115SAlberto Garcia    # So we need to repeat the calculation as long as the numbers change
222*df957115SAlberto Garcia    while True:
223*df957115SAlberto Garcia        new_total_allocated_clusters = total_allocated_clusters + allocated_refcount_tables + allocated_refcount_blocks
224*df957115SAlberto Garcia        new_allocated_refcount_blocks = math.ceil(new_total_allocated_clusters / refcounts_per_block)
225*df957115SAlberto Garcia        if new_allocated_refcount_blocks > allocated_refcount_blocks:
226*df957115SAlberto Garcia            allocated_refcount_blocks = new_allocated_refcount_blocks
227*df957115SAlberto Garcia            allocated_refcount_tables = math.ceil(allocated_refcount_blocks / refcounts_per_table)
228*df957115SAlberto Garcia        else:
229*df957115SAlberto Garcia            break
230*df957115SAlberto Garcia
231*df957115SAlberto Garcia    # Now that we have the final numbers we can update total_allocated_clusters
232*df957115SAlberto Garcia    total_allocated_clusters += allocated_refcount_tables + allocated_refcount_blocks
233*df957115SAlberto Garcia
234*df957115SAlberto Garcia    # At this point we have the exact number of clusters that the output
235*df957115SAlberto Garcia    # image is going to use so we can calculate all the offsets.
236*df957115SAlberto Garcia    current_cluster_idx = 1
237*df957115SAlberto Garcia
238*df957115SAlberto Garcia    refcount_table_offset = current_cluster_idx * cluster_size
239*df957115SAlberto Garcia    current_cluster_idx += allocated_refcount_tables
240*df957115SAlberto Garcia
241*df957115SAlberto Garcia    refcount_block_offset = current_cluster_idx * cluster_size
242*df957115SAlberto Garcia    current_cluster_idx += allocated_refcount_blocks
243*df957115SAlberto Garcia
244*df957115SAlberto Garcia    l1_table_offset = current_cluster_idx * cluster_size
245*df957115SAlberto Garcia    current_cluster_idx += allocated_l1_tables
246*df957115SAlberto Garcia
247*df957115SAlberto Garcia    l2_table_offset = current_cluster_idx * cluster_size
248*df957115SAlberto Garcia    current_cluster_idx += allocated_l2_tables
249*df957115SAlberto Garcia
250*df957115SAlberto Garcia    data_clusters_offset = current_cluster_idx * cluster_size
251*df957115SAlberto Garcia
252*df957115SAlberto Garcia    # Calculate some values used in the qcow2 header
253*df957115SAlberto Garcia    if allocated_l1_tables == 0:
254*df957115SAlberto Garcia        l1_table_offset = 0
255*df957115SAlberto Garcia
256*df957115SAlberto Garcia    hdr_cluster_bits = int(math.log2(cluster_size))
257*df957115SAlberto Garcia    hdr_refcount_bits = int(math.log2(refcount_bits))
258*df957115SAlberto Garcia    hdr_length = QCOW2_V3_HEADER_LENGTH
259*df957115SAlberto Garcia    hdr_incompat_features = 0
260*df957115SAlberto Garcia    if data_file_name is not None:
261*df957115SAlberto Garcia        hdr_incompat_features |= 1 << QCOW2_INCOMPAT_DATA_FILE_BIT
262*df957115SAlberto Garcia    hdr_autoclear_features = 0
263*df957115SAlberto Garcia    if data_file_raw:
264*df957115SAlberto Garcia        hdr_autoclear_features |= 1 << QCOW2_AUTOCLEAR_DATA_FILE_RAW_BIT
265*df957115SAlberto Garcia
266*df957115SAlberto Garcia    ### Write qcow2 header
267*df957115SAlberto Garcia    cluster = bytearray(cluster_size)
268*df957115SAlberto Garcia    struct.pack_into(">4sIQIIQIIQQIIQQQQII", cluster, 0,
269*df957115SAlberto Garcia        b"QFI\xfb",            # QCOW magic string
270*df957115SAlberto Garcia        3,                     # version
271*df957115SAlberto Garcia        0,                     # backing file offset
272*df957115SAlberto Garcia        0,                     # backing file sizes
273*df957115SAlberto Garcia        hdr_cluster_bits,
274*df957115SAlberto Garcia        disk_size,
275*df957115SAlberto Garcia        0,                     # encryption method
276*df957115SAlberto Garcia        l1_entries,
277*df957115SAlberto Garcia        l1_table_offset,
278*df957115SAlberto Garcia        refcount_table_offset,
279*df957115SAlberto Garcia        allocated_refcount_tables,
280*df957115SAlberto Garcia        0,                     # number of snapshots
281*df957115SAlberto Garcia        0,                     # snapshot table offset
282*df957115SAlberto Garcia        hdr_incompat_features,
283*df957115SAlberto Garcia        0,                     # compatible features
284*df957115SAlberto Garcia        hdr_autoclear_features,
285*df957115SAlberto Garcia        hdr_refcount_bits,
286*df957115SAlberto Garcia        hdr_length,
287*df957115SAlberto Garcia    )
288*df957115SAlberto Garcia
289*df957115SAlberto Garcia    write_features(cluster, hdr_length, data_file_name)
290*df957115SAlberto Garcia
291*df957115SAlberto Garcia    sys.stdout.buffer.write(cluster)
292*df957115SAlberto Garcia
293*df957115SAlberto Garcia    ### Write refcount table
294*df957115SAlberto Garcia    cur_offset = refcount_block_offset
295*df957115SAlberto Garcia    remaining_refcount_table_entries = allocated_refcount_blocks # Each entry is a pointer to a refcount block
296*df957115SAlberto Garcia    while remaining_refcount_table_entries > 0:
297*df957115SAlberto Garcia        cluster = bytearray(cluster_size)
298*df957115SAlberto Garcia        to_write = min(remaining_refcount_table_entries, refcounts_per_table)
299*df957115SAlberto Garcia        remaining_refcount_table_entries -= to_write
300*df957115SAlberto Garcia        for idx in range(to_write):
301*df957115SAlberto Garcia            struct.pack_into(">Q", cluster, idx * 8, cur_offset)
302*df957115SAlberto Garcia            cur_offset += cluster_size
303*df957115SAlberto Garcia        sys.stdout.buffer.write(cluster)
304*df957115SAlberto Garcia
305*df957115SAlberto Garcia    ### Write refcount blocks
306*df957115SAlberto Garcia    remaining_refcount_block_entries = total_allocated_clusters # One entry for each allocated cluster
307*df957115SAlberto Garcia    for tbl in range(allocated_refcount_blocks):
308*df957115SAlberto Garcia        cluster = bytearray(cluster_size)
309*df957115SAlberto Garcia        to_write = min(remaining_refcount_block_entries, refcounts_per_block)
310*df957115SAlberto Garcia        remaining_refcount_block_entries -= to_write
311*df957115SAlberto Garcia        # All refcount entries contain the number 1. The only difference
312*df957115SAlberto Garcia        # is their bit width, defined when the image is created.
313*df957115SAlberto Garcia        for idx in range(to_write):
314*df957115SAlberto Garcia            if refcount_bits == 64:
315*df957115SAlberto Garcia                struct.pack_into(">Q", cluster, idx * 8, 1)
316*df957115SAlberto Garcia            elif refcount_bits == 32:
317*df957115SAlberto Garcia                struct.pack_into(">L", cluster, idx * 4, 1)
318*df957115SAlberto Garcia            elif refcount_bits == 16:
319*df957115SAlberto Garcia                struct.pack_into(">H", cluster, idx * 2, 1)
320*df957115SAlberto Garcia            elif refcount_bits == 8:
321*df957115SAlberto Garcia                cluster[idx] = 1
322*df957115SAlberto Garcia            elif refcount_bits == 4:
323*df957115SAlberto Garcia                cluster[idx // 2] |= 1 << ((idx % 2) * 4)
324*df957115SAlberto Garcia            elif refcount_bits == 2:
325*df957115SAlberto Garcia                cluster[idx // 4] |= 1 << ((idx % 4) * 2)
326*df957115SAlberto Garcia            elif refcount_bits == 1:
327*df957115SAlberto Garcia                cluster[idx // 8] |= 1 << (idx % 8)
328*df957115SAlberto Garcia        sys.stdout.buffer.write(cluster)
329*df957115SAlberto Garcia
330*df957115SAlberto Garcia    ### Write L1 table
331*df957115SAlberto Garcia    cur_offset = l2_table_offset
332*df957115SAlberto Garcia    for tbl in range(allocated_l1_tables):
333*df957115SAlberto Garcia        cluster = bytearray(cluster_size)
334*df957115SAlberto Garcia        for idx in range(l1_entries_per_table):
335*df957115SAlberto Garcia            l1_idx = tbl * l1_entries_per_table + idx
336*df957115SAlberto Garcia            if bitmap_is_set(l1_bitmap, l1_idx):
337*df957115SAlberto Garcia                struct.pack_into(">Q", cluster, idx * 8, cur_offset | QCOW_OFLAG_COPIED)
338*df957115SAlberto Garcia                cur_offset += cluster_size
339*df957115SAlberto Garcia        sys.stdout.buffer.write(cluster)
340*df957115SAlberto Garcia
341*df957115SAlberto Garcia    ### Write L2 tables
342*df957115SAlberto Garcia    cur_offset = data_clusters_offset
343*df957115SAlberto Garcia    for tbl in range(l1_entries):
344*df957115SAlberto Garcia        # Skip the empty L2 tables. We can identify them because
345*df957115SAlberto Garcia        # there is no L1 entry pointing at them.
346*df957115SAlberto Garcia        if bitmap_is_set(l1_bitmap, tbl):
347*df957115SAlberto Garcia            cluster = bytearray(cluster_size)
348*df957115SAlberto Garcia            for idx in range(l2_entries_per_table):
349*df957115SAlberto Garcia                l2_idx = tbl * l2_entries_per_table + idx
350*df957115SAlberto Garcia                if bitmap_is_set(l2_bitmap, l2_idx):
351*df957115SAlberto Garcia                    if data_file_name is None:
352*df957115SAlberto Garcia                        struct.pack_into(">Q", cluster, idx * 8, cur_offset | QCOW_OFLAG_COPIED)
353*df957115SAlberto Garcia                        cur_offset += cluster_size
354*df957115SAlberto Garcia                    else:
355*df957115SAlberto Garcia                        struct.pack_into(">Q", cluster, idx * 8, (l2_idx * cluster_size) | QCOW_OFLAG_COPIED)
356*df957115SAlberto Garcia            sys.stdout.buffer.write(cluster)
357*df957115SAlberto Garcia
358*df957115SAlberto Garcia    ### Write data clusters
359*df957115SAlberto Garcia    if data_file_name is None:
360*df957115SAlberto Garcia        for idx in bitmap_iterator(l2_bitmap, total_data_clusters):
361*df957115SAlberto Garcia            cluster = os.pread(fd, cluster_size, cluster_size * idx)
362*df957115SAlberto Garcia            # If the last cluster is smaller than cluster_size pad it with zeroes
363*df957115SAlberto Garcia            if len(cluster) < cluster_size:
364*df957115SAlberto Garcia                cluster += bytes(cluster_size - len(cluster))
365*df957115SAlberto Garcia            sys.stdout.buffer.write(cluster)
366*df957115SAlberto Garcia
367*df957115SAlberto Garcia    if not data_file_raw:
368*df957115SAlberto Garcia        os.close(fd)
369*df957115SAlberto Garcia
370*df957115SAlberto Garcia
371*df957115SAlberto Garciadef main():
372*df957115SAlberto Garcia    # Command-line arguments
373*df957115SAlberto Garcia    parser = argparse.ArgumentParser(
374*df957115SAlberto Garcia        description="This program converts a QEMU disk image to qcow2 "
375*df957115SAlberto Garcia        "and writes it to the standard output"
376*df957115SAlberto Garcia    )
377*df957115SAlberto Garcia    parser.add_argument("input_file", help="name of the input file")
378*df957115SAlberto Garcia    parser.add_argument(
379*df957115SAlberto Garcia        "-f",
380*df957115SAlberto Garcia        dest="input_format",
381*df957115SAlberto Garcia        metavar="input_format",
382*df957115SAlberto Garcia        help="format of the input file (default: raw)",
383*df957115SAlberto Garcia        default="raw",
384*df957115SAlberto Garcia    )
385*df957115SAlberto Garcia    parser.add_argument(
386*df957115SAlberto Garcia        "-c",
387*df957115SAlberto Garcia        dest="cluster_size",
388*df957115SAlberto Garcia        metavar="cluster_size",
389*df957115SAlberto Garcia        help=f"qcow2 cluster size (default: {QCOW2_DEFAULT_CLUSTER_SIZE})",
390*df957115SAlberto Garcia        default=QCOW2_DEFAULT_CLUSTER_SIZE,
391*df957115SAlberto Garcia        type=int,
392*df957115SAlberto Garcia        choices=[1 << x for x in range(9, 22)],
393*df957115SAlberto Garcia    )
394*df957115SAlberto Garcia    parser.add_argument(
395*df957115SAlberto Garcia        "-r",
396*df957115SAlberto Garcia        dest="refcount_bits",
397*df957115SAlberto Garcia        metavar="refcount_bits",
398*df957115SAlberto Garcia        help=f"width of the reference count entries (default: {QCOW2_DEFAULT_REFCOUNT_BITS})",
399*df957115SAlberto Garcia        default=QCOW2_DEFAULT_REFCOUNT_BITS,
400*df957115SAlberto Garcia        type=int,
401*df957115SAlberto Garcia        choices=[1 << x for x in range(7)],
402*df957115SAlberto Garcia    )
403*df957115SAlberto Garcia    parser.add_argument(
404*df957115SAlberto Garcia        "-d",
405*df957115SAlberto Garcia        dest="data_file",
406*df957115SAlberto Garcia        help="create an image with input_file as an external data file",
407*df957115SAlberto Garcia        action="store_true",
408*df957115SAlberto Garcia    )
409*df957115SAlberto Garcia    parser.add_argument(
410*df957115SAlberto Garcia        "-R",
411*df957115SAlberto Garcia        dest="data_file_raw",
412*df957115SAlberto Garcia        help="enable data_file_raw on the generated image (implies -d)",
413*df957115SAlberto Garcia        action="store_true",
414*df957115SAlberto Garcia    )
415*df957115SAlberto Garcia    args = parser.parse_args()
416*df957115SAlberto Garcia
417*df957115SAlberto Garcia    if args.data_file_raw:
418*df957115SAlberto Garcia        args.data_file = True
419*df957115SAlberto Garcia
420*df957115SAlberto Garcia    if not os.path.isfile(args.input_file):
421*df957115SAlberto Garcia        sys.exit(f"[Error] {args.input_file} does not exist or is not a regular file.")
422*df957115SAlberto Garcia
423*df957115SAlberto Garcia    if args.data_file and args.input_format != "raw":
424*df957115SAlberto Garcia        sys.exit("[Error] External data files can only be used with raw input images")
425*df957115SAlberto Garcia
426*df957115SAlberto Garcia    # A 512 byte header is too small for the data file name extension
427*df957115SAlberto Garcia    if args.data_file and args.cluster_size == 512:
428*df957115SAlberto Garcia        sys.exit("[Error] External data files require a larger cluster size")
429*df957115SAlberto Garcia
430*df957115SAlberto Garcia    if sys.stdout.isatty():
431*df957115SAlberto Garcia        sys.exit("[Error] Refusing to write to a tty. Try redirecting stdout.")
432*df957115SAlberto Garcia
433*df957115SAlberto Garcia    if args.data_file:
434*df957115SAlberto Garcia        data_file_name = args.input_file
435*df957115SAlberto Garcia    else:
436*df957115SAlberto Garcia        data_file_name = None
437*df957115SAlberto Garcia
438*df957115SAlberto Garcia    with get_input_as_raw_file(args.input_file, args.input_format) as raw_file:
439*df957115SAlberto Garcia        write_qcow2_content(
440*df957115SAlberto Garcia            raw_file,
441*df957115SAlberto Garcia            args.cluster_size,
442*df957115SAlberto Garcia            args.refcount_bits,
443*df957115SAlberto Garcia            data_file_name,
444*df957115SAlberto Garcia            args.data_file_raw,
445*df957115SAlberto Garcia        )
446*df957115SAlberto Garcia
447*df957115SAlberto Garcia
448*df957115SAlberto Garciaif __name__ == "__main__":
449*df957115SAlberto Garcia    main()
450