1903cb1bfSPhilippe Mathieu-Daudé#!/usr/bin/env python3 216306a7bSVladimir Sementsov-Ogievskiy# 316306a7bSVladimir Sementsov-Ogievskiy# Manipulations with qcow2 image 416306a7bSVladimir Sementsov-Ogievskiy# 53419ec71SEric Blake# Copyright (C) 2012 Red Hat, Inc. 63419ec71SEric Blake# 716306a7bSVladimir Sementsov-Ogievskiy# This program is free software; you can redistribute it and/or modify 816306a7bSVladimir Sementsov-Ogievskiy# it under the terms of the GNU General Public License as published by 916306a7bSVladimir Sementsov-Ogievskiy# the Free Software Foundation; either version 2 of the License, or 1016306a7bSVladimir Sementsov-Ogievskiy# (at your option) any later version. 1116306a7bSVladimir Sementsov-Ogievskiy# 1216306a7bSVladimir Sementsov-Ogievskiy# This program is distributed in the hope that it will be useful, 1316306a7bSVladimir Sementsov-Ogievskiy# but WITHOUT ANY WARRANTY; without even the implied warranty of 1416306a7bSVladimir Sementsov-Ogievskiy# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1516306a7bSVladimir Sementsov-Ogievskiy# GNU General Public License for more details. 1616306a7bSVladimir Sementsov-Ogievskiy# 1716306a7bSVladimir Sementsov-Ogievskiy# You should have received a copy of the GNU General Public License 1816306a7bSVladimir Sementsov-Ogievskiy# along with this program. If not, see <http://www.gnu.org/licenses/>. 1916306a7bSVladimir Sementsov-Ogievskiy# 206e19b3c4SKevin Wolf 216e19b3c4SKevin Wolfimport sys 226e19b3c4SKevin Wolf 23d5262c71SVladimir Sementsov-Ogievskiyfrom qcow2_format import ( 24d5262c71SVladimir Sementsov-Ogievskiy QcowHeader, 25d5262c71SVladimir Sementsov-Ogievskiy QcowHeaderExtension 26d5262c71SVladimir Sementsov-Ogievskiy) 276e19b3c4SKevin Wolf 286e19b3c4SKevin Wolf 29*2c6d9ca4SAndrey Shinkevichis_json = False 30*2c6d9ca4SAndrey Shinkevich 31*2c6d9ca4SAndrey Shinkevich 326e19b3c4SKevin Wolfdef cmd_dump_header(fd): 336e19b3c4SKevin Wolf h = QcowHeader(fd) 34*2c6d9ca4SAndrey Shinkevich h.dump(is_json) 35eeafed5fSVladimir Sementsov-Ogievskiy print() 36*2c6d9ca4SAndrey Shinkevich h.dump_extensions(is_json) 376e19b3c4SKevin Wolf 3802756054SVladimir Sementsov-Ogievskiy 391aa6630eSMax Reitzdef cmd_dump_header_exts(fd): 401aa6630eSMax Reitz h = QcowHeader(fd) 41*2c6d9ca4SAndrey Shinkevich h.dump_extensions(is_json) 421aa6630eSMax Reitz 4302756054SVladimir Sementsov-Ogievskiy 44c93331c9SKevin Wolfdef cmd_set_header(fd, name, value): 45c93331c9SKevin Wolf try: 46c93331c9SKevin Wolf value = int(value, 0) 4702756054SVladimir Sementsov-Ogievskiy except ValueError: 48f03868bdSEduardo Habkost print("'%s' is not a valid number" % value) 49c93331c9SKevin Wolf sys.exit(1) 50c93331c9SKevin Wolf 51c93331c9SKevin Wolf fields = (field[2] for field in QcowHeader.fields) 5202756054SVladimir Sementsov-Ogievskiy if name not in fields: 53f03868bdSEduardo Habkost print("'%s' is not a known header field" % name) 54c93331c9SKevin Wolf sys.exit(1) 55c93331c9SKevin Wolf 56c93331c9SKevin Wolf h = QcowHeader(fd) 57c93331c9SKevin Wolf h.__dict__[name] = value 58c93331c9SKevin Wolf h.update(fd) 59c93331c9SKevin Wolf 6002756054SVladimir Sementsov-Ogievskiy 616e19b3c4SKevin Wolfdef cmd_add_header_ext(fd, magic, data): 626e19b3c4SKevin Wolf try: 636e19b3c4SKevin Wolf magic = int(magic, 0) 6402756054SVladimir Sementsov-Ogievskiy except ValueError: 65f03868bdSEduardo Habkost print("'%s' is not a valid magic number" % magic) 666e19b3c4SKevin Wolf sys.exit(1) 676e19b3c4SKevin Wolf 686e19b3c4SKevin Wolf h = QcowHeader(fd) 6902756054SVladimir Sementsov-Ogievskiy h.extensions.append(QcowHeaderExtension.create(magic, 7002756054SVladimir Sementsov-Ogievskiy data.encode('ascii'))) 716e19b3c4SKevin Wolf h.update(fd) 726e19b3c4SKevin Wolf 7302756054SVladimir Sementsov-Ogievskiy 7412ac6d3dSKevin Wolfdef cmd_add_header_ext_stdio(fd, magic): 7512ac6d3dSKevin Wolf data = sys.stdin.read() 7612ac6d3dSKevin Wolf cmd_add_header_ext(fd, magic, data) 7712ac6d3dSKevin Wolf 7802756054SVladimir Sementsov-Ogievskiy 796e19b3c4SKevin Wolfdef cmd_del_header_ext(fd, magic): 806e19b3c4SKevin Wolf try: 816e19b3c4SKevin Wolf magic = int(magic, 0) 8202756054SVladimir Sementsov-Ogievskiy except ValueError: 83f03868bdSEduardo Habkost print("'%s' is not a valid magic number" % magic) 846e19b3c4SKevin Wolf sys.exit(1) 856e19b3c4SKevin Wolf 866e19b3c4SKevin Wolf h = QcowHeader(fd) 876e19b3c4SKevin Wolf found = False 886e19b3c4SKevin Wolf 896e19b3c4SKevin Wolf for ex in h.extensions: 906e19b3c4SKevin Wolf if ex.magic == magic: 916e19b3c4SKevin Wolf found = True 926e19b3c4SKevin Wolf h.extensions.remove(ex) 936e19b3c4SKevin Wolf 946e19b3c4SKevin Wolf if not found: 95f03868bdSEduardo Habkost print("No such header extension") 966e19b3c4SKevin Wolf return 976e19b3c4SKevin Wolf 986e19b3c4SKevin Wolf h.update(fd) 996e19b3c4SKevin Wolf 10002756054SVladimir Sementsov-Ogievskiy 1011b2eff62SStefan Hajnoczidef cmd_set_feature_bit(fd, group, bit): 1021b2eff62SStefan Hajnoczi try: 1031b2eff62SStefan Hajnoczi bit = int(bit, 0) 1041b2eff62SStefan Hajnoczi if bit < 0 or bit >= 64: 1051b2eff62SStefan Hajnoczi raise ValueError 10602756054SVladimir Sementsov-Ogievskiy except ValueError: 107f03868bdSEduardo Habkost print("'%s' is not a valid bit number in range [0, 64)" % bit) 1081b2eff62SStefan Hajnoczi sys.exit(1) 1091b2eff62SStefan Hajnoczi 1101b2eff62SStefan Hajnoczi h = QcowHeader(fd) 1111b2eff62SStefan Hajnoczi if group == 'incompatible': 1121b2eff62SStefan Hajnoczi h.incompatible_features |= 1 << bit 1131b2eff62SStefan Hajnoczi elif group == 'compatible': 1141b2eff62SStefan Hajnoczi h.compatible_features |= 1 << bit 1151b2eff62SStefan Hajnoczi elif group == 'autoclear': 1161b2eff62SStefan Hajnoczi h.autoclear_features |= 1 << bit 1171b2eff62SStefan Hajnoczi else: 11802756054SVladimir Sementsov-Ogievskiy print("'%s' is not a valid group, try " 11902756054SVladimir Sementsov-Ogievskiy "'incompatible', 'compatible', or 'autoclear'" % group) 1201b2eff62SStefan Hajnoczi sys.exit(1) 1211b2eff62SStefan Hajnoczi 1221b2eff62SStefan Hajnoczi h.update(fd) 1231b2eff62SStefan Hajnoczi 12402756054SVladimir Sementsov-Ogievskiy 1256e19b3c4SKevin Wolfcmds = [ 12602756054SVladimir Sementsov-Ogievskiy ['dump-header', cmd_dump_header, 0, 12702756054SVladimir Sementsov-Ogievskiy 'Dump image header and header extensions'], 12802756054SVladimir Sementsov-Ogievskiy ['dump-header-exts', cmd_dump_header_exts, 0, 12902756054SVladimir Sementsov-Ogievskiy 'Dump image header extensions'], 130c93331c9SKevin Wolf ['set-header', cmd_set_header, 2, 'Set a field in the header'], 1316e19b3c4SKevin Wolf ['add-header-ext', cmd_add_header_ext, 2, 'Add a header extension'], 13202756054SVladimir Sementsov-Ogievskiy ['add-header-ext-stdio', cmd_add_header_ext_stdio, 1, 13302756054SVladimir Sementsov-Ogievskiy 'Add a header extension, data from stdin'], 1346e19b3c4SKevin Wolf ['del-header-ext', cmd_del_header_ext, 1, 'Delete a header extension'], 1351b2eff62SStefan Hajnoczi ['set-feature-bit', cmd_set_feature_bit, 2, 'Set a feature bit'], 1366e19b3c4SKevin Wolf] 1376e19b3c4SKevin Wolf 13802756054SVladimir Sementsov-Ogievskiy 1396e19b3c4SKevin Wolfdef main(filename, cmd, args): 1406e19b3c4SKevin Wolf fd = open(filename, "r+b") 1416e19b3c4SKevin Wolf try: 1426e19b3c4SKevin Wolf for name, handler, num_args, desc in cmds: 1436e19b3c4SKevin Wolf if name != cmd: 1446e19b3c4SKevin Wolf continue 1456e19b3c4SKevin Wolf elif len(args) != num_args: 1466e19b3c4SKevin Wolf usage() 1476e19b3c4SKevin Wolf return 1486e19b3c4SKevin Wolf else: 1496e19b3c4SKevin Wolf handler(fd, *args) 1506e19b3c4SKevin Wolf return 151f03868bdSEduardo Habkost print("Unknown command '%s'" % cmd) 1526e19b3c4SKevin Wolf finally: 1536e19b3c4SKevin Wolf fd.close() 1546e19b3c4SKevin Wolf 15502756054SVladimir Sementsov-Ogievskiy 1566e19b3c4SKevin Wolfdef usage(): 157*2c6d9ca4SAndrey Shinkevich print("Usage: %s <file> <cmd> [<arg>, ...] [<key>, ...]" % sys.argv[0]) 158f03868bdSEduardo Habkost print("") 159f03868bdSEduardo Habkost print("Supported commands:") 1606e19b3c4SKevin Wolf for name, handler, num_args, desc in cmds: 161f03868bdSEduardo Habkost print(" %-20s - %s" % (name, desc)) 162*2c6d9ca4SAndrey Shinkevich print("") 163*2c6d9ca4SAndrey Shinkevich print("Supported keys:") 164*2c6d9ca4SAndrey Shinkevich print(" %-20s - %s" % ('-j', 'Dump in JSON format')) 1656e19b3c4SKevin Wolf 16602756054SVladimir Sementsov-Ogievskiy 167d2ef210cSKevin Wolfif __name__ == '__main__': 1686e19b3c4SKevin Wolf if len(sys.argv) < 3: 1696e19b3c4SKevin Wolf usage() 1706e19b3c4SKevin Wolf sys.exit(1) 1716e19b3c4SKevin Wolf 172*2c6d9ca4SAndrey Shinkevich is_json = '-j' in sys.argv 173*2c6d9ca4SAndrey Shinkevich if is_json: 174*2c6d9ca4SAndrey Shinkevich sys.argv.remove('-j') 175*2c6d9ca4SAndrey Shinkevich 1766e19b3c4SKevin Wolf main(sys.argv[1], sys.argv[2], sys.argv[3:]) 177