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 h.dump_extensions() 31 32 33def cmd_dump_header_exts(fd): 34 h = QcowHeader(fd) 35 h.dump_extensions() 36 37 38def cmd_set_header(fd, name, value): 39 try: 40 value = int(value, 0) 41 except ValueError: 42 print("'%s' is not a valid number" % value) 43 sys.exit(1) 44 45 fields = (field[2] for field in QcowHeader.fields) 46 if name not in fields: 47 print("'%s' is not a known header field" % name) 48 sys.exit(1) 49 50 h = QcowHeader(fd) 51 h.__dict__[name] = value 52 h.update(fd) 53 54 55def cmd_add_header_ext(fd, magic, data): 56 try: 57 magic = int(magic, 0) 58 except ValueError: 59 print("'%s' is not a valid magic number" % magic) 60 sys.exit(1) 61 62 h = QcowHeader(fd) 63 h.extensions.append(QcowHeaderExtension.create(magic, 64 data.encode('ascii'))) 65 h.update(fd) 66 67 68def cmd_add_header_ext_stdio(fd, magic): 69 data = sys.stdin.read() 70 cmd_add_header_ext(fd, magic, data) 71 72 73def cmd_del_header_ext(fd, magic): 74 try: 75 magic = int(magic, 0) 76 except ValueError: 77 print("'%s' is not a valid magic number" % magic) 78 sys.exit(1) 79 80 h = QcowHeader(fd) 81 found = False 82 83 for ex in h.extensions: 84 if ex.magic == magic: 85 found = True 86 h.extensions.remove(ex) 87 88 if not found: 89 print("No such header extension") 90 return 91 92 h.update(fd) 93 94 95def cmd_set_feature_bit(fd, group, bit): 96 try: 97 bit = int(bit, 0) 98 if bit < 0 or bit >= 64: 99 raise ValueError 100 except ValueError: 101 print("'%s' is not a valid bit number in range [0, 64)" % bit) 102 sys.exit(1) 103 104 h = QcowHeader(fd) 105 if group == 'incompatible': 106 h.incompatible_features |= 1 << bit 107 elif group == 'compatible': 108 h.compatible_features |= 1 << bit 109 elif group == 'autoclear': 110 h.autoclear_features |= 1 << bit 111 else: 112 print("'%s' is not a valid group, try " 113 "'incompatible', 'compatible', or 'autoclear'" % group) 114 sys.exit(1) 115 116 h.update(fd) 117 118 119cmds = [ 120 ['dump-header', cmd_dump_header, 0, 121 'Dump image header and header extensions'], 122 ['dump-header-exts', cmd_dump_header_exts, 0, 123 'Dump image header extensions'], 124 ['set-header', cmd_set_header, 2, 'Set a field in the header'], 125 ['add-header-ext', cmd_add_header_ext, 2, 'Add a header extension'], 126 ['add-header-ext-stdio', cmd_add_header_ext_stdio, 1, 127 'Add a header extension, data from stdin'], 128 ['del-header-ext', cmd_del_header_ext, 1, 'Delete a header extension'], 129 ['set-feature-bit', cmd_set_feature_bit, 2, 'Set a feature bit'], 130] 131 132 133def main(filename, cmd, args): 134 fd = open(filename, "r+b") 135 try: 136 for name, handler, num_args, desc in cmds: 137 if name != cmd: 138 continue 139 elif len(args) != num_args: 140 usage() 141 return 142 else: 143 handler(fd, *args) 144 return 145 print("Unknown command '%s'" % cmd) 146 finally: 147 fd.close() 148 149 150def usage(): 151 print("Usage: %s <file> <cmd> [<arg>, ...]" % sys.argv[0]) 152 print("") 153 print("Supported commands:") 154 for name, handler, num_args, desc in cmds: 155 print(" %-20s - %s" % (name, desc)) 156 157 158if __name__ == '__main__': 159 if len(sys.argv) < 3: 160 usage() 161 sys.exit(1) 162 163 main(sys.argv[1], sys.argv[2], sys.argv[3:]) 164