1903cb1bfSPhilippe Mathieu-Daudé#!/usr/bin/env python3 216306a7bSVladimir Sementsov-Ogievskiy# 316306a7bSVladimir Sementsov-Ogievskiy# Manipulations with qcow2 image 416306a7bSVladimir Sementsov-Ogievskiy# 516306a7bSVladimir Sementsov-Ogievskiy# This program is free software; you can redistribute it and/or modify 616306a7bSVladimir Sementsov-Ogievskiy# it under the terms of the GNU General Public License as published by 716306a7bSVladimir Sementsov-Ogievskiy# the Free Software Foundation; either version 2 of the License, or 816306a7bSVladimir Sementsov-Ogievskiy# (at your option) any later version. 916306a7bSVladimir Sementsov-Ogievskiy# 1016306a7bSVladimir Sementsov-Ogievskiy# This program is distributed in the hope that it will be useful, 1116306a7bSVladimir Sementsov-Ogievskiy# but WITHOUT ANY WARRANTY; without even the implied warranty of 1216306a7bSVladimir Sementsov-Ogievskiy# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1316306a7bSVladimir Sementsov-Ogievskiy# GNU General Public License for more details. 1416306a7bSVladimir Sementsov-Ogievskiy# 1516306a7bSVladimir Sementsov-Ogievskiy# You should have received a copy of the GNU General Public License 1616306a7bSVladimir Sementsov-Ogievskiy# along with this program. If not, see <http://www.gnu.org/licenses/>. 1716306a7bSVladimir Sementsov-Ogievskiy# 186e19b3c4SKevin Wolf 196e19b3c4SKevin Wolfimport sys 206e19b3c4SKevin Wolf 21*d5262c71SVladimir Sementsov-Ogievskiyfrom qcow2_format import ( 22*d5262c71SVladimir Sementsov-Ogievskiy QcowHeader, 23*d5262c71SVladimir Sementsov-Ogievskiy QcowHeaderExtension 24*d5262c71SVladimir Sementsov-Ogievskiy) 256e19b3c4SKevin Wolf 266e19b3c4SKevin Wolf 276e19b3c4SKevin Wolfdef cmd_dump_header(fd): 286e19b3c4SKevin Wolf h = QcowHeader(fd) 296e19b3c4SKevin Wolf h.dump() 306e19b3c4SKevin Wolf h.dump_extensions() 316e19b3c4SKevin Wolf 3202756054SVladimir Sementsov-Ogievskiy 331aa6630eSMax Reitzdef cmd_dump_header_exts(fd): 341aa6630eSMax Reitz h = QcowHeader(fd) 351aa6630eSMax Reitz h.dump_extensions() 361aa6630eSMax Reitz 3702756054SVladimir Sementsov-Ogievskiy 38c93331c9SKevin Wolfdef cmd_set_header(fd, name, value): 39c93331c9SKevin Wolf try: 40c93331c9SKevin Wolf value = int(value, 0) 4102756054SVladimir Sementsov-Ogievskiy except ValueError: 42f03868bdSEduardo Habkost print("'%s' is not a valid number" % value) 43c93331c9SKevin Wolf sys.exit(1) 44c93331c9SKevin Wolf 45c93331c9SKevin Wolf fields = (field[2] for field in QcowHeader.fields) 4602756054SVladimir Sementsov-Ogievskiy if name not in fields: 47f03868bdSEduardo Habkost print("'%s' is not a known header field" % name) 48c93331c9SKevin Wolf sys.exit(1) 49c93331c9SKevin Wolf 50c93331c9SKevin Wolf h = QcowHeader(fd) 51c93331c9SKevin Wolf h.__dict__[name] = value 52c93331c9SKevin Wolf h.update(fd) 53c93331c9SKevin Wolf 5402756054SVladimir Sementsov-Ogievskiy 556e19b3c4SKevin Wolfdef cmd_add_header_ext(fd, magic, data): 566e19b3c4SKevin Wolf try: 576e19b3c4SKevin Wolf magic = int(magic, 0) 5802756054SVladimir Sementsov-Ogievskiy except ValueError: 59f03868bdSEduardo Habkost print("'%s' is not a valid magic number" % magic) 606e19b3c4SKevin Wolf sys.exit(1) 616e19b3c4SKevin Wolf 626e19b3c4SKevin Wolf h = QcowHeader(fd) 6302756054SVladimir Sementsov-Ogievskiy h.extensions.append(QcowHeaderExtension.create(magic, 6402756054SVladimir Sementsov-Ogievskiy data.encode('ascii'))) 656e19b3c4SKevin Wolf h.update(fd) 666e19b3c4SKevin Wolf 6702756054SVladimir Sementsov-Ogievskiy 6812ac6d3dSKevin Wolfdef cmd_add_header_ext_stdio(fd, magic): 6912ac6d3dSKevin Wolf data = sys.stdin.read() 7012ac6d3dSKevin Wolf cmd_add_header_ext(fd, magic, data) 7112ac6d3dSKevin Wolf 7202756054SVladimir Sementsov-Ogievskiy 736e19b3c4SKevin Wolfdef cmd_del_header_ext(fd, magic): 746e19b3c4SKevin Wolf try: 756e19b3c4SKevin Wolf magic = int(magic, 0) 7602756054SVladimir Sementsov-Ogievskiy except ValueError: 77f03868bdSEduardo Habkost print("'%s' is not a valid magic number" % magic) 786e19b3c4SKevin Wolf sys.exit(1) 796e19b3c4SKevin Wolf 806e19b3c4SKevin Wolf h = QcowHeader(fd) 816e19b3c4SKevin Wolf found = False 826e19b3c4SKevin Wolf 836e19b3c4SKevin Wolf for ex in h.extensions: 846e19b3c4SKevin Wolf if ex.magic == magic: 856e19b3c4SKevin Wolf found = True 866e19b3c4SKevin Wolf h.extensions.remove(ex) 876e19b3c4SKevin Wolf 886e19b3c4SKevin Wolf if not found: 89f03868bdSEduardo Habkost print("No such header extension") 906e19b3c4SKevin Wolf return 916e19b3c4SKevin Wolf 926e19b3c4SKevin Wolf h.update(fd) 936e19b3c4SKevin Wolf 9402756054SVladimir Sementsov-Ogievskiy 951b2eff62SStefan Hajnoczidef cmd_set_feature_bit(fd, group, bit): 961b2eff62SStefan Hajnoczi try: 971b2eff62SStefan Hajnoczi bit = int(bit, 0) 981b2eff62SStefan Hajnoczi if bit < 0 or bit >= 64: 991b2eff62SStefan Hajnoczi raise ValueError 10002756054SVladimir Sementsov-Ogievskiy except ValueError: 101f03868bdSEduardo Habkost print("'%s' is not a valid bit number in range [0, 64)" % bit) 1021b2eff62SStefan Hajnoczi sys.exit(1) 1031b2eff62SStefan Hajnoczi 1041b2eff62SStefan Hajnoczi h = QcowHeader(fd) 1051b2eff62SStefan Hajnoczi if group == 'incompatible': 1061b2eff62SStefan Hajnoczi h.incompatible_features |= 1 << bit 1071b2eff62SStefan Hajnoczi elif group == 'compatible': 1081b2eff62SStefan Hajnoczi h.compatible_features |= 1 << bit 1091b2eff62SStefan Hajnoczi elif group == 'autoclear': 1101b2eff62SStefan Hajnoczi h.autoclear_features |= 1 << bit 1111b2eff62SStefan Hajnoczi else: 11202756054SVladimir Sementsov-Ogievskiy print("'%s' is not a valid group, try " 11302756054SVladimir Sementsov-Ogievskiy "'incompatible', 'compatible', or 'autoclear'" % group) 1141b2eff62SStefan Hajnoczi sys.exit(1) 1151b2eff62SStefan Hajnoczi 1161b2eff62SStefan Hajnoczi h.update(fd) 1171b2eff62SStefan Hajnoczi 11802756054SVladimir Sementsov-Ogievskiy 1196e19b3c4SKevin Wolfcmds = [ 12002756054SVladimir Sementsov-Ogievskiy ['dump-header', cmd_dump_header, 0, 12102756054SVladimir Sementsov-Ogievskiy 'Dump image header and header extensions'], 12202756054SVladimir Sementsov-Ogievskiy ['dump-header-exts', cmd_dump_header_exts, 0, 12302756054SVladimir Sementsov-Ogievskiy 'Dump image header extensions'], 124c93331c9SKevin Wolf ['set-header', cmd_set_header, 2, 'Set a field in the header'], 1256e19b3c4SKevin Wolf ['add-header-ext', cmd_add_header_ext, 2, 'Add a header extension'], 12602756054SVladimir Sementsov-Ogievskiy ['add-header-ext-stdio', cmd_add_header_ext_stdio, 1, 12702756054SVladimir Sementsov-Ogievskiy 'Add a header extension, data from stdin'], 1286e19b3c4SKevin Wolf ['del-header-ext', cmd_del_header_ext, 1, 'Delete a header extension'], 1291b2eff62SStefan Hajnoczi ['set-feature-bit', cmd_set_feature_bit, 2, 'Set a feature bit'], 1306e19b3c4SKevin Wolf] 1316e19b3c4SKevin Wolf 13202756054SVladimir Sementsov-Ogievskiy 1336e19b3c4SKevin Wolfdef main(filename, cmd, args): 1346e19b3c4SKevin Wolf fd = open(filename, "r+b") 1356e19b3c4SKevin Wolf try: 1366e19b3c4SKevin Wolf for name, handler, num_args, desc in cmds: 1376e19b3c4SKevin Wolf if name != cmd: 1386e19b3c4SKevin Wolf continue 1396e19b3c4SKevin Wolf elif len(args) != num_args: 1406e19b3c4SKevin Wolf usage() 1416e19b3c4SKevin Wolf return 1426e19b3c4SKevin Wolf else: 1436e19b3c4SKevin Wolf handler(fd, *args) 1446e19b3c4SKevin Wolf return 145f03868bdSEduardo Habkost print("Unknown command '%s'" % cmd) 1466e19b3c4SKevin Wolf finally: 1476e19b3c4SKevin Wolf fd.close() 1486e19b3c4SKevin Wolf 14902756054SVladimir Sementsov-Ogievskiy 1506e19b3c4SKevin Wolfdef usage(): 151f03868bdSEduardo Habkost print("Usage: %s <file> <cmd> [<arg>, ...]" % sys.argv[0]) 152f03868bdSEduardo Habkost print("") 153f03868bdSEduardo Habkost print("Supported commands:") 1546e19b3c4SKevin Wolf for name, handler, num_args, desc in cmds: 155f03868bdSEduardo Habkost print(" %-20s - %s" % (name, desc)) 1566e19b3c4SKevin Wolf 15702756054SVladimir Sementsov-Ogievskiy 158d2ef210cSKevin Wolfif __name__ == '__main__': 1596e19b3c4SKevin Wolf if len(sys.argv) < 3: 1606e19b3c4SKevin Wolf usage() 1616e19b3c4SKevin Wolf sys.exit(1) 1626e19b3c4SKevin Wolf 1636e19b3c4SKevin Wolf main(sys.argv[1], sys.argv[2], sys.argv[3:]) 164