1903cb1bfSPhilippe Mathieu-Daudé#!/usr/bin/env python3 2*16306a7bSVladimir Sementsov-Ogievskiy# 3*16306a7bSVladimir Sementsov-Ogievskiy# Manipulations with qcow2 image 4*16306a7bSVladimir Sementsov-Ogievskiy# 5*16306a7bSVladimir Sementsov-Ogievskiy# This program is free software; you can redistribute it and/or modify 6*16306a7bSVladimir Sementsov-Ogievskiy# it under the terms of the GNU General Public License as published by 7*16306a7bSVladimir Sementsov-Ogievskiy# the Free Software Foundation; either version 2 of the License, or 8*16306a7bSVladimir Sementsov-Ogievskiy# (at your option) any later version. 9*16306a7bSVladimir Sementsov-Ogievskiy# 10*16306a7bSVladimir Sementsov-Ogievskiy# This program is distributed in the hope that it will be useful, 11*16306a7bSVladimir Sementsov-Ogievskiy# but WITHOUT ANY WARRANTY; without even the implied warranty of 12*16306a7bSVladimir Sementsov-Ogievskiy# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13*16306a7bSVladimir Sementsov-Ogievskiy# GNU General Public License for more details. 14*16306a7bSVladimir Sementsov-Ogievskiy# 15*16306a7bSVladimir Sementsov-Ogievskiy# You should have received a copy of the GNU General Public License 16*16306a7bSVladimir Sementsov-Ogievskiy# along with this program. If not, see <http://www.gnu.org/licenses/>. 17*16306a7bSVladimir Sementsov-Ogievskiy# 186e19b3c4SKevin Wolf 196e19b3c4SKevin Wolfimport sys 206e19b3c4SKevin Wolfimport struct 216e19b3c4SKevin Wolfimport string 226e19b3c4SKevin Wolf 2302756054SVladimir Sementsov-Ogievskiy 246e19b3c4SKevin Wolfclass QcowHeaderExtension: 256e19b3c4SKevin Wolf 266e19b3c4SKevin Wolf def __init__(self, magic, length, data): 278884dd1bSKevin Wolf if length % 8 != 0: 288884dd1bSKevin Wolf padding = 8 - (length % 8) 298eb5e674SMax Reitz data += b"\0" * padding 308884dd1bSKevin Wolf 316e19b3c4SKevin Wolf self.magic = magic 326e19b3c4SKevin Wolf self.length = length 336e19b3c4SKevin Wolf self.data = data 346e19b3c4SKevin Wolf 356e19b3c4SKevin Wolf @classmethod 366e19b3c4SKevin Wolf def create(cls, magic, data): 376e19b3c4SKevin Wolf return QcowHeaderExtension(magic, len(data), data) 386e19b3c4SKevin Wolf 3902756054SVladimir Sementsov-Ogievskiy 406e19b3c4SKevin Wolfclass QcowHeader: 416e19b3c4SKevin Wolf 426e19b3c4SKevin Wolf uint32_t = 'I' 436e19b3c4SKevin Wolf uint64_t = 'Q' 446e19b3c4SKevin Wolf 456e19b3c4SKevin Wolf fields = [ 466e19b3c4SKevin Wolf # Version 2 header fields 476e19b3c4SKevin Wolf [uint32_t, '%#x', 'magic'], 486e19b3c4SKevin Wolf [uint32_t, '%d', 'version'], 496e19b3c4SKevin Wolf [uint64_t, '%#x', 'backing_file_offset'], 506e19b3c4SKevin Wolf [uint32_t, '%#x', 'backing_file_size'], 516e19b3c4SKevin Wolf [uint32_t, '%d', 'cluster_bits'], 526e19b3c4SKevin Wolf [uint64_t, '%d', 'size'], 536e19b3c4SKevin Wolf [uint32_t, '%d', 'crypt_method'], 546e19b3c4SKevin Wolf [uint32_t, '%d', 'l1_size'], 556e19b3c4SKevin Wolf [uint64_t, '%#x', 'l1_table_offset'], 566e19b3c4SKevin Wolf [uint64_t, '%#x', 'refcount_table_offset'], 576e19b3c4SKevin Wolf [uint32_t, '%d', 'refcount_table_clusters'], 586e19b3c4SKevin Wolf [uint32_t, '%d', 'nb_snapshots'], 596e19b3c4SKevin Wolf [uint64_t, '%#x', 'snapshot_offset'], 601042ec94SKevin Wolf 611042ec94SKevin Wolf # Version 3 header fields 620485e6eeSMax Reitz [uint64_t, 'mask', 'incompatible_features'], 630485e6eeSMax Reitz [uint64_t, 'mask', 'compatible_features'], 640485e6eeSMax Reitz [uint64_t, 'mask', 'autoclear_features'], 651042ec94SKevin Wolf [uint32_t, '%d', 'refcount_order'], 661042ec94SKevin Wolf [uint32_t, '%d', 'header_length'], 6702756054SVladimir Sementsov-Ogievskiy ] 686e19b3c4SKevin Wolf 696e19b3c4SKevin Wolf fmt = '>' + ''.join(field[0] for field in fields) 706e19b3c4SKevin Wolf 716e19b3c4SKevin Wolf def __init__(self, fd): 726e19b3c4SKevin Wolf 736e19b3c4SKevin Wolf buf_size = struct.calcsize(QcowHeader.fmt) 746e19b3c4SKevin Wolf 756e19b3c4SKevin Wolf fd.seek(0) 766e19b3c4SKevin Wolf buf = fd.read(buf_size) 776e19b3c4SKevin Wolf 786e19b3c4SKevin Wolf header = struct.unpack(QcowHeader.fmt, buf) 796e19b3c4SKevin Wolf self.__dict__ = dict((field[2], header[i]) 806e19b3c4SKevin Wolf for i, field in enumerate(QcowHeader.fields)) 816e19b3c4SKevin Wolf 821042ec94SKevin Wolf self.set_defaults() 836e19b3c4SKevin Wolf self.cluster_size = 1 << self.cluster_bits 846e19b3c4SKevin Wolf 851042ec94SKevin Wolf fd.seek(self.header_length) 866e19b3c4SKevin Wolf self.load_extensions(fd) 876e19b3c4SKevin Wolf 886e19b3c4SKevin Wolf if self.backing_file_offset: 896e19b3c4SKevin Wolf fd.seek(self.backing_file_offset) 906e19b3c4SKevin Wolf self.backing_file = fd.read(self.backing_file_size) 916e19b3c4SKevin Wolf else: 926e19b3c4SKevin Wolf self.backing_file = None 936e19b3c4SKevin Wolf 941042ec94SKevin Wolf def set_defaults(self): 956e19b3c4SKevin Wolf if self.version == 2: 961042ec94SKevin Wolf self.incompatible_features = 0 971042ec94SKevin Wolf self.compatible_features = 0 981042ec94SKevin Wolf self.autoclear_features = 0 991042ec94SKevin Wolf self.refcount_order = 4 1001042ec94SKevin Wolf self.header_length = 72 1016e19b3c4SKevin Wolf 1026e19b3c4SKevin Wolf def load_extensions(self, fd): 1036e19b3c4SKevin Wolf self.extensions = [] 1046e19b3c4SKevin Wolf 1056e19b3c4SKevin Wolf if self.backing_file_offset != 0: 1066e19b3c4SKevin Wolf end = min(self.cluster_size, self.backing_file_offset) 1076e19b3c4SKevin Wolf else: 1086e19b3c4SKevin Wolf end = self.cluster_size 1096e19b3c4SKevin Wolf 1106e19b3c4SKevin Wolf while fd.tell() < end: 1116e19b3c4SKevin Wolf (magic, length) = struct.unpack('>II', fd.read(8)) 1126e19b3c4SKevin Wolf if magic == 0: 1136e19b3c4SKevin Wolf break 1146e19b3c4SKevin Wolf else: 1156e19b3c4SKevin Wolf padded = (length + 7) & ~7 1166e19b3c4SKevin Wolf data = fd.read(padded) 11702756054SVladimir Sementsov-Ogievskiy self.extensions.append(QcowHeaderExtension(magic, length, 11802756054SVladimir Sementsov-Ogievskiy data)) 1196e19b3c4SKevin Wolf 1206e19b3c4SKevin Wolf def update_extensions(self, fd): 1216e19b3c4SKevin Wolf 1221042ec94SKevin Wolf fd.seek(self.header_length) 1236e19b3c4SKevin Wolf extensions = self.extensions 1248eb5e674SMax Reitz extensions.append(QcowHeaderExtension(0, 0, b"")) 1256e19b3c4SKevin Wolf for ex in extensions: 1266e19b3c4SKevin Wolf buf = struct.pack('>II', ex.magic, ex.length) 1276e19b3c4SKevin Wolf fd.write(buf) 1286e19b3c4SKevin Wolf fd.write(ex.data) 1296e19b3c4SKevin Wolf 13002756054SVladimir Sementsov-Ogievskiy if self.backing_file is not None: 1316e19b3c4SKevin Wolf self.backing_file_offset = fd.tell() 1326e19b3c4SKevin Wolf fd.write(self.backing_file) 1336e19b3c4SKevin Wolf 1346e19b3c4SKevin Wolf if fd.tell() > self.cluster_size: 1356e19b3c4SKevin Wolf raise Exception("I think I just broke the image...") 1366e19b3c4SKevin Wolf 1376e19b3c4SKevin Wolf def update(self, fd): 1381042ec94SKevin Wolf header_bytes = self.header_length 1396e19b3c4SKevin Wolf 1406e19b3c4SKevin Wolf self.update_extensions(fd) 1416e19b3c4SKevin Wolf 1426e19b3c4SKevin Wolf fd.seek(0) 1436e19b3c4SKevin Wolf header = tuple(self.__dict__[f] for t, p, f in QcowHeader.fields) 1446e19b3c4SKevin Wolf buf = struct.pack(QcowHeader.fmt, *header) 1456e19b3c4SKevin Wolf buf = buf[0:header_bytes-1] 1466e19b3c4SKevin Wolf fd.write(buf) 1476e19b3c4SKevin Wolf 1486e19b3c4SKevin Wolf def dump(self): 1496e19b3c4SKevin Wolf for f in QcowHeader.fields: 1500485e6eeSMax Reitz value = self.__dict__[f[2]] 1510485e6eeSMax Reitz if f[1] == 'mask': 1520485e6eeSMax Reitz bits = [] 1530485e6eeSMax Reitz for bit in range(64): 1540485e6eeSMax Reitz if value & (1 << bit): 1550485e6eeSMax Reitz bits.append(bit) 1560485e6eeSMax Reitz value_str = str(bits) 1570485e6eeSMax Reitz else: 1580485e6eeSMax Reitz value_str = f[1] % value 1590485e6eeSMax Reitz 1600485e6eeSMax Reitz print("%-25s" % f[2], value_str) 161f03868bdSEduardo Habkost print("") 1626e19b3c4SKevin Wolf 1636e19b3c4SKevin Wolf def dump_extensions(self): 1646e19b3c4SKevin Wolf for ex in self.extensions: 1656e19b3c4SKevin Wolf 1666e19b3c4SKevin Wolf data = ex.data[:ex.length] 1678eb5e674SMax Reitz if all(c in string.printable.encode('ascii') for c in data): 1688eb5e674SMax Reitz data = "'%s'" % data.decode('ascii') 1696e19b3c4SKevin Wolf else: 1706e19b3c4SKevin Wolf data = "<binary>" 1716e19b3c4SKevin Wolf 172f03868bdSEduardo Habkost print("Header extension:") 173f03868bdSEduardo Habkost print("%-25s %#x" % ("magic", ex.magic)) 174f03868bdSEduardo Habkost print("%-25s %d" % ("length", ex.length)) 175f03868bdSEduardo Habkost print("%-25s %s" % ("data", data)) 176f03868bdSEduardo Habkost print("") 1776e19b3c4SKevin Wolf 1786e19b3c4SKevin Wolf 1796e19b3c4SKevin Wolfdef cmd_dump_header(fd): 1806e19b3c4SKevin Wolf h = QcowHeader(fd) 1816e19b3c4SKevin Wolf h.dump() 1826e19b3c4SKevin Wolf h.dump_extensions() 1836e19b3c4SKevin Wolf 18402756054SVladimir Sementsov-Ogievskiy 1851aa6630eSMax Reitzdef cmd_dump_header_exts(fd): 1861aa6630eSMax Reitz h = QcowHeader(fd) 1871aa6630eSMax Reitz h.dump_extensions() 1881aa6630eSMax Reitz 18902756054SVladimir Sementsov-Ogievskiy 190c93331c9SKevin Wolfdef cmd_set_header(fd, name, value): 191c93331c9SKevin Wolf try: 192c93331c9SKevin Wolf value = int(value, 0) 19302756054SVladimir Sementsov-Ogievskiy except ValueError: 194f03868bdSEduardo Habkost print("'%s' is not a valid number" % value) 195c93331c9SKevin Wolf sys.exit(1) 196c93331c9SKevin Wolf 197c93331c9SKevin Wolf fields = (field[2] for field in QcowHeader.fields) 19802756054SVladimir Sementsov-Ogievskiy if name not in fields: 199f03868bdSEduardo Habkost print("'%s' is not a known header field" % name) 200c93331c9SKevin Wolf sys.exit(1) 201c93331c9SKevin Wolf 202c93331c9SKevin Wolf h = QcowHeader(fd) 203c93331c9SKevin Wolf h.__dict__[name] = value 204c93331c9SKevin Wolf h.update(fd) 205c93331c9SKevin Wolf 20602756054SVladimir Sementsov-Ogievskiy 2076e19b3c4SKevin Wolfdef cmd_add_header_ext(fd, magic, data): 2086e19b3c4SKevin Wolf try: 2096e19b3c4SKevin Wolf magic = int(magic, 0) 21002756054SVladimir Sementsov-Ogievskiy except ValueError: 211f03868bdSEduardo Habkost print("'%s' is not a valid magic number" % magic) 2126e19b3c4SKevin Wolf sys.exit(1) 2136e19b3c4SKevin Wolf 2146e19b3c4SKevin Wolf h = QcowHeader(fd) 21502756054SVladimir Sementsov-Ogievskiy h.extensions.append(QcowHeaderExtension.create(magic, 21602756054SVladimir Sementsov-Ogievskiy data.encode('ascii'))) 2176e19b3c4SKevin Wolf h.update(fd) 2186e19b3c4SKevin Wolf 21902756054SVladimir Sementsov-Ogievskiy 22012ac6d3dSKevin Wolfdef cmd_add_header_ext_stdio(fd, magic): 22112ac6d3dSKevin Wolf data = sys.stdin.read() 22212ac6d3dSKevin Wolf cmd_add_header_ext(fd, magic, data) 22312ac6d3dSKevin Wolf 22402756054SVladimir Sementsov-Ogievskiy 2256e19b3c4SKevin Wolfdef cmd_del_header_ext(fd, magic): 2266e19b3c4SKevin Wolf try: 2276e19b3c4SKevin Wolf magic = int(magic, 0) 22802756054SVladimir Sementsov-Ogievskiy except ValueError: 229f03868bdSEduardo Habkost print("'%s' is not a valid magic number" % magic) 2306e19b3c4SKevin Wolf sys.exit(1) 2316e19b3c4SKevin Wolf 2326e19b3c4SKevin Wolf h = QcowHeader(fd) 2336e19b3c4SKevin Wolf found = False 2346e19b3c4SKevin Wolf 2356e19b3c4SKevin Wolf for ex in h.extensions: 2366e19b3c4SKevin Wolf if ex.magic == magic: 2376e19b3c4SKevin Wolf found = True 2386e19b3c4SKevin Wolf h.extensions.remove(ex) 2396e19b3c4SKevin Wolf 2406e19b3c4SKevin Wolf if not found: 241f03868bdSEduardo Habkost print("No such header extension") 2426e19b3c4SKevin Wolf return 2436e19b3c4SKevin Wolf 2446e19b3c4SKevin Wolf h.update(fd) 2456e19b3c4SKevin Wolf 24602756054SVladimir Sementsov-Ogievskiy 2471b2eff62SStefan Hajnoczidef cmd_set_feature_bit(fd, group, bit): 2481b2eff62SStefan Hajnoczi try: 2491b2eff62SStefan Hajnoczi bit = int(bit, 0) 2501b2eff62SStefan Hajnoczi if bit < 0 or bit >= 64: 2511b2eff62SStefan Hajnoczi raise ValueError 25202756054SVladimir Sementsov-Ogievskiy except ValueError: 253f03868bdSEduardo Habkost print("'%s' is not a valid bit number in range [0, 64)" % bit) 2541b2eff62SStefan Hajnoczi sys.exit(1) 2551b2eff62SStefan Hajnoczi 2561b2eff62SStefan Hajnoczi h = QcowHeader(fd) 2571b2eff62SStefan Hajnoczi if group == 'incompatible': 2581b2eff62SStefan Hajnoczi h.incompatible_features |= 1 << bit 2591b2eff62SStefan Hajnoczi elif group == 'compatible': 2601b2eff62SStefan Hajnoczi h.compatible_features |= 1 << bit 2611b2eff62SStefan Hajnoczi elif group == 'autoclear': 2621b2eff62SStefan Hajnoczi h.autoclear_features |= 1 << bit 2631b2eff62SStefan Hajnoczi else: 26402756054SVladimir Sementsov-Ogievskiy print("'%s' is not a valid group, try " 26502756054SVladimir Sementsov-Ogievskiy "'incompatible', 'compatible', or 'autoclear'" % group) 2661b2eff62SStefan Hajnoczi sys.exit(1) 2671b2eff62SStefan Hajnoczi 2681b2eff62SStefan Hajnoczi h.update(fd) 2691b2eff62SStefan Hajnoczi 27002756054SVladimir Sementsov-Ogievskiy 2716e19b3c4SKevin Wolfcmds = [ 27202756054SVladimir Sementsov-Ogievskiy ['dump-header', cmd_dump_header, 0, 27302756054SVladimir Sementsov-Ogievskiy 'Dump image header and header extensions'], 27402756054SVladimir Sementsov-Ogievskiy ['dump-header-exts', cmd_dump_header_exts, 0, 27502756054SVladimir Sementsov-Ogievskiy 'Dump image header extensions'], 276c93331c9SKevin Wolf ['set-header', cmd_set_header, 2, 'Set a field in the header'], 2776e19b3c4SKevin Wolf ['add-header-ext', cmd_add_header_ext, 2, 'Add a header extension'], 27802756054SVladimir Sementsov-Ogievskiy ['add-header-ext-stdio', cmd_add_header_ext_stdio, 1, 27902756054SVladimir Sementsov-Ogievskiy 'Add a header extension, data from stdin'], 2806e19b3c4SKevin Wolf ['del-header-ext', cmd_del_header_ext, 1, 'Delete a header extension'], 2811b2eff62SStefan Hajnoczi ['set-feature-bit', cmd_set_feature_bit, 2, 'Set a feature bit'], 2826e19b3c4SKevin Wolf] 2836e19b3c4SKevin Wolf 28402756054SVladimir Sementsov-Ogievskiy 2856e19b3c4SKevin Wolfdef main(filename, cmd, args): 2866e19b3c4SKevin Wolf fd = open(filename, "r+b") 2876e19b3c4SKevin Wolf try: 2886e19b3c4SKevin Wolf for name, handler, num_args, desc in cmds: 2896e19b3c4SKevin Wolf if name != cmd: 2906e19b3c4SKevin Wolf continue 2916e19b3c4SKevin Wolf elif len(args) != num_args: 2926e19b3c4SKevin Wolf usage() 2936e19b3c4SKevin Wolf return 2946e19b3c4SKevin Wolf else: 2956e19b3c4SKevin Wolf handler(fd, *args) 2966e19b3c4SKevin Wolf return 297f03868bdSEduardo Habkost print("Unknown command '%s'" % cmd) 2986e19b3c4SKevin Wolf finally: 2996e19b3c4SKevin Wolf fd.close() 3006e19b3c4SKevin Wolf 30102756054SVladimir Sementsov-Ogievskiy 3026e19b3c4SKevin Wolfdef usage(): 303f03868bdSEduardo Habkost print("Usage: %s <file> <cmd> [<arg>, ...]" % sys.argv[0]) 304f03868bdSEduardo Habkost print("") 305f03868bdSEduardo Habkost print("Supported commands:") 3066e19b3c4SKevin Wolf for name, handler, num_args, desc in cmds: 307f03868bdSEduardo Habkost print(" %-20s - %s" % (name, desc)) 3086e19b3c4SKevin Wolf 30902756054SVladimir Sementsov-Ogievskiy 310d2ef210cSKevin Wolfif __name__ == '__main__': 3116e19b3c4SKevin Wolf if len(sys.argv) < 3: 3126e19b3c4SKevin Wolf usage() 3136e19b3c4SKevin Wolf sys.exit(1) 3146e19b3c4SKevin Wolf 3156e19b3c4SKevin Wolf main(sys.argv[1], sys.argv[2], sys.argv[3:]) 316