Add qemu 2.4.0
[kvmfornfv.git] / qemu / tests / qemu-iotests / qcow2.py
1 #!/usr/bin/env python
2
3 import sys
4 import struct
5 import string
6
7 class QcowHeaderExtension:
8
9     def __init__(self, magic, length, data):
10         if length % 8 != 0:
11             padding = 8 - (length % 8)
12             data += "\0" * padding
13
14         self.magic  = magic
15         self.length = length
16         self.data   = data
17
18     @classmethod
19     def create(cls, magic, data):
20         return QcowHeaderExtension(magic, len(data), data)
21
22 class QcowHeader:
23
24     uint32_t = 'I'
25     uint64_t = 'Q'
26
27     fields = [
28         # Version 2 header fields
29         [ uint32_t, '%#x',  'magic' ],
30         [ uint32_t, '%d',   'version' ],
31         [ uint64_t, '%#x',  'backing_file_offset' ],
32         [ uint32_t, '%#x',  'backing_file_size' ],
33         [ uint32_t, '%d',   'cluster_bits' ],
34         [ uint64_t, '%d',   'size' ],
35         [ uint32_t, '%d',   'crypt_method' ],
36         [ uint32_t, '%d',   'l1_size' ],
37         [ uint64_t, '%#x',  'l1_table_offset' ],
38         [ uint64_t, '%#x',  'refcount_table_offset' ],
39         [ uint32_t, '%d',   'refcount_table_clusters' ],
40         [ uint32_t, '%d',   'nb_snapshots' ],
41         [ uint64_t, '%#x',  'snapshot_offset' ],
42
43         # Version 3 header fields
44         [ uint64_t, '%#x',  'incompatible_features' ],
45         [ uint64_t, '%#x',  'compatible_features' ],
46         [ uint64_t, '%#x',  'autoclear_features' ],
47         [ uint32_t, '%d',   'refcount_order' ],
48         [ uint32_t, '%d',   'header_length' ],
49     ];
50
51     fmt = '>' + ''.join(field[0] for field in fields)
52
53     def __init__(self, fd):
54
55         buf_size = struct.calcsize(QcowHeader.fmt)
56
57         fd.seek(0)
58         buf = fd.read(buf_size)
59
60         header = struct.unpack(QcowHeader.fmt, buf)
61         self.__dict__ = dict((field[2], header[i])
62             for i, field in enumerate(QcowHeader.fields))
63
64         self.set_defaults()
65         self.cluster_size = 1 << self.cluster_bits
66
67         fd.seek(self.header_length)
68         self.load_extensions(fd)
69
70         if self.backing_file_offset:
71             fd.seek(self.backing_file_offset)
72             self.backing_file = fd.read(self.backing_file_size)
73         else:
74             self.backing_file = None
75
76     def set_defaults(self):
77         if self.version == 2:
78             self.incompatible_features = 0
79             self.compatible_features = 0
80             self.autoclear_features = 0
81             self.refcount_order = 4
82             self.header_length = 72
83
84     def load_extensions(self, fd):
85         self.extensions = []
86
87         if self.backing_file_offset != 0:
88             end = min(self.cluster_size, self.backing_file_offset)
89         else:
90             end = self.cluster_size
91
92         while fd.tell() < end:
93             (magic, length) = struct.unpack('>II', fd.read(8))
94             if magic == 0:
95                 break
96             else:
97                 padded = (length + 7) & ~7
98                 data = fd.read(padded)
99                 self.extensions.append(QcowHeaderExtension(magic, length, data))
100
101     def update_extensions(self, fd):
102
103         fd.seek(self.header_length)
104         extensions = self.extensions
105         extensions.append(QcowHeaderExtension(0, 0, ""))
106         for ex in extensions:
107             buf = struct.pack('>II', ex.magic, ex.length)
108             fd.write(buf)
109             fd.write(ex.data)
110
111         if self.backing_file != None:
112             self.backing_file_offset = fd.tell()
113             fd.write(self.backing_file)
114
115         if fd.tell() > self.cluster_size:
116             raise Exception("I think I just broke the image...")
117
118
119     def update(self, fd):
120         header_bytes = self.header_length
121
122         self.update_extensions(fd)
123
124         fd.seek(0)
125         header = tuple(self.__dict__[f] for t, p, f in QcowHeader.fields)
126         buf = struct.pack(QcowHeader.fmt, *header)
127         buf = buf[0:header_bytes-1]
128         fd.write(buf)
129
130     def dump(self):
131         for f in QcowHeader.fields:
132             print "%-25s" % f[2], f[1] % self.__dict__[f[2]]
133         print ""
134
135     def dump_extensions(self):
136         for ex in self.extensions:
137
138             data = ex.data[:ex.length]
139             if all(c in string.printable for c in data):
140                 data = "'%s'" % data
141             else:
142                 data = "<binary>"
143
144             print "Header extension:"
145             print "%-25s %#x" % ("magic", ex.magic)
146             print "%-25s %d" % ("length", ex.length)
147             print "%-25s %s" % ("data", data)
148             print ""
149
150
151 def cmd_dump_header(fd):
152     h = QcowHeader(fd)
153     h.dump()
154     h.dump_extensions()
155
156 def cmd_set_header(fd, name, value):
157     try:
158         value = int(value, 0)
159     except:
160         print "'%s' is not a valid number" % value
161         sys.exit(1)
162
163     fields = (field[2] for field in QcowHeader.fields)
164     if not name in fields:
165         print "'%s' is not a known header field" % name
166         sys.exit(1)
167
168     h = QcowHeader(fd)
169     h.__dict__[name] = value
170     h.update(fd)
171
172 def cmd_add_header_ext(fd, magic, data):
173     try:
174         magic = int(magic, 0)
175     except:
176         print "'%s' is not a valid magic number" % magic
177         sys.exit(1)
178
179     h = QcowHeader(fd)
180     h.extensions.append(QcowHeaderExtension.create(magic, data))
181     h.update(fd)
182
183 def cmd_add_header_ext_stdio(fd, magic):
184     data = sys.stdin.read()
185     cmd_add_header_ext(fd, magic, data)
186
187 def cmd_del_header_ext(fd, magic):
188     try:
189         magic = int(magic, 0)
190     except:
191         print "'%s' is not a valid magic number" % magic
192         sys.exit(1)
193
194     h = QcowHeader(fd)
195     found = False
196
197     for ex in h.extensions:
198         if ex.magic == magic:
199             found = True
200             h.extensions.remove(ex)
201
202     if not found:
203         print "No such header extension"
204         return
205
206     h.update(fd)
207
208 def cmd_set_feature_bit(fd, group, bit):
209     try:
210         bit = int(bit, 0)
211         if bit < 0 or bit >= 64:
212             raise ValueError
213     except:
214         print "'%s' is not a valid bit number in range [0, 64)" % bit
215         sys.exit(1)
216
217     h = QcowHeader(fd)
218     if group == 'incompatible':
219         h.incompatible_features |= 1 << bit
220     elif group == 'compatible':
221         h.compatible_features |= 1 << bit
222     elif group == 'autoclear':
223         h.autoclear_features |= 1 << bit
224     else:
225         print "'%s' is not a valid group, try 'incompatible', 'compatible', or 'autoclear'" % group
226         sys.exit(1)
227
228     h.update(fd)
229
230 cmds = [
231     [ 'dump-header',          cmd_dump_header,          0, 'Dump image header and header extensions' ],
232     [ 'set-header',           cmd_set_header,           2, 'Set a field in the header'],
233     [ 'add-header-ext',       cmd_add_header_ext,       2, 'Add a header extension' ],
234     [ 'add-header-ext-stdio', cmd_add_header_ext_stdio, 1, 'Add a header extension, data from stdin' ],
235     [ 'del-header-ext',       cmd_del_header_ext,       1, 'Delete a header extension' ],
236     [ 'set-feature-bit',      cmd_set_feature_bit,      2, 'Set a feature bit'],
237 ]
238
239 def main(filename, cmd, args):
240     fd = open(filename, "r+b")
241     try:
242         for name, handler, num_args, desc in cmds:
243             if name != cmd:
244                 continue
245             elif len(args) != num_args:
246                 usage()
247                 return
248             else:
249                 handler(fd, *args)
250                 return
251         print "Unknown command '%s'" % cmd
252     finally:
253         fd.close()
254
255 def usage():
256     print "Usage: %s <file> <cmd> [<arg>, ...]" % sys.argv[0]
257     print ""
258     print "Supported commands:"
259     for name, handler, num_args, desc in cmds:
260         print "    %-20s - %s" % (name, desc)
261
262 if __name__ == '__main__':
263     if len(sys.argv) < 3:
264         usage()
265         sys.exit(1)
266
267     main(sys.argv[1], sys.argv[2], sys.argv[3:])