xref: /qemu/tests/qemu-iotests/qcow2.py (revision d5262c712488674fba74a749e81894e098226bfa)
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