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