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