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