1#!/usr/bin/env python3 2# 3# Manipulations with qcow2 image 4# 5# This program is free software; you can redistribute it and/or modify 6# it under the terms of the GNU General Public License as published by 7# the Free Software Foundation; either version 2 of the License, or 8# (at your option) any later version. 9# 10# This program is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13# GNU General Public License for more details. 14# 15# You should have received a copy of the GNU General Public License 16# along with this program. If not, see <http://www.gnu.org/licenses/>. 17# 18 19import sys 20 21from qcow2_format import ( 22 QcowHeader, 23 QcowHeaderExtension 24) 25 26 27def cmd_dump_header(fd): 28 h = QcowHeader(fd) 29 h.dump() 30 print() 31 h.dump_extensions() 32 33 34def cmd_dump_header_exts(fd): 35 h = QcowHeader(fd) 36 h.dump_extensions() 37 38 39def cmd_set_header(fd, name, value): 40 try: 41 value = int(value, 0) 42 except ValueError: 43 print("'%s' is not a valid number" % value) 44 sys.exit(1) 45 46 fields = (field[2] for field in QcowHeader.fields) 47 if name not in fields: 48 print("'%s' is not a known header field" % name) 49 sys.exit(1) 50 51 h = QcowHeader(fd) 52 h.__dict__[name] = value 53 h.update(fd) 54 55 56def cmd_add_header_ext(fd, magic, data): 57 try: 58 magic = int(magic, 0) 59 except ValueError: 60 print("'%s' is not a valid magic number" % magic) 61 sys.exit(1) 62 63 h = QcowHeader(fd) 64 h.extensions.append(QcowHeaderExtension.create(magic, 65 data.encode('ascii'))) 66 h.update(fd) 67 68 69def cmd_add_header_ext_stdio(fd, magic): 70 data = sys.stdin.read() 71 cmd_add_header_ext(fd, magic, data) 72 73 74def cmd_del_header_ext(fd, magic): 75 try: 76 magic = int(magic, 0) 77 except ValueError: 78 print("'%s' is not a valid magic number" % magic) 79 sys.exit(1) 80 81 h = QcowHeader(fd) 82 found = False 83 84 for ex in h.extensions: 85 if ex.magic == magic: 86 found = True 87 h.extensions.remove(ex) 88 89 if not found: 90 print("No such header extension") 91 return 92 93 h.update(fd) 94 95 96def cmd_set_feature_bit(fd, group, bit): 97 try: 98 bit = int(bit, 0) 99 if bit < 0 or bit >= 64: 100 raise ValueError 101 except ValueError: 102 print("'%s' is not a valid bit number in range [0, 64)" % bit) 103 sys.exit(1) 104 105 h = QcowHeader(fd) 106 if group == 'incompatible': 107 h.incompatible_features |= 1 << bit 108 elif group == 'compatible': 109 h.compatible_features |= 1 << bit 110 elif group == 'autoclear': 111 h.autoclear_features |= 1 << bit 112 else: 113 print("'%s' is not a valid group, try " 114 "'incompatible', 'compatible', or 'autoclear'" % group) 115 sys.exit(1) 116 117 h.update(fd) 118 119 120cmds = [ 121 ['dump-header', cmd_dump_header, 0, 122 'Dump image header and header extensions'], 123 ['dump-header-exts', cmd_dump_header_exts, 0, 124 'Dump image header extensions'], 125 ['set-header', cmd_set_header, 2, 'Set a field in the header'], 126 ['add-header-ext', cmd_add_header_ext, 2, 'Add a header extension'], 127 ['add-header-ext-stdio', cmd_add_header_ext_stdio, 1, 128 'Add a header extension, data from stdin'], 129 ['del-header-ext', cmd_del_header_ext, 1, 'Delete a header extension'], 130 ['set-feature-bit', cmd_set_feature_bit, 2, 'Set a feature bit'], 131] 132 133 134def main(filename, cmd, args): 135 fd = open(filename, "r+b") 136 try: 137 for name, handler, num_args, desc in cmds: 138 if name != cmd: 139 continue 140 elif len(args) != num_args: 141 usage() 142 return 143 else: 144 handler(fd, *args) 145 return 146 print("Unknown command '%s'" % cmd) 147 finally: 148 fd.close() 149 150 151def usage(): 152 print("Usage: %s <file> <cmd> [<arg>, ...]" % sys.argv[0]) 153 print("") 154 print("Supported commands:") 155 for name, handler, num_args, desc in cmds: 156 print(" %-20s - %s" % (name, desc)) 157 158 159if __name__ == '__main__': 160 if len(sys.argv) < 3: 161 usage() 162 sys.exit(1) 163 164 main(sys.argv[1], sys.argv[2], sys.argv[3:]) 165