xref: /openbmc/qemu/tests/qemu-iotests/qcow2.py (revision 6e19b3c4e0b40b08ccb550a0c0a65798f3a17ac8)
1*6e19b3c4SKevin Wolf#!/usr/bin/env python
2*6e19b3c4SKevin Wolf
3*6e19b3c4SKevin Wolfimport sys
4*6e19b3c4SKevin Wolfimport struct
5*6e19b3c4SKevin Wolfimport string
6*6e19b3c4SKevin Wolf
7*6e19b3c4SKevin Wolfclass QcowHeaderExtension:
8*6e19b3c4SKevin Wolf
9*6e19b3c4SKevin Wolf    def __init__(self, magic, length, data):
10*6e19b3c4SKevin Wolf        self.magic  = magic
11*6e19b3c4SKevin Wolf        self.length = length
12*6e19b3c4SKevin Wolf        self.data   = data
13*6e19b3c4SKevin Wolf
14*6e19b3c4SKevin Wolf    @classmethod
15*6e19b3c4SKevin Wolf    def create(cls, magic, data):
16*6e19b3c4SKevin Wolf        return QcowHeaderExtension(magic, len(data), data)
17*6e19b3c4SKevin Wolf
18*6e19b3c4SKevin Wolfclass QcowHeader:
19*6e19b3c4SKevin Wolf
20*6e19b3c4SKevin Wolf    uint32_t = 'I'
21*6e19b3c4SKevin Wolf    uint64_t = 'Q'
22*6e19b3c4SKevin Wolf
23*6e19b3c4SKevin Wolf    fields = [
24*6e19b3c4SKevin Wolf        # Version 2 header fields
25*6e19b3c4SKevin Wolf        [ uint32_t, '%#x',  'magic' ],
26*6e19b3c4SKevin Wolf        [ uint32_t, '%d',   'version' ],
27*6e19b3c4SKevin Wolf        [ uint64_t, '%#x',  'backing_file_offset' ],
28*6e19b3c4SKevin Wolf        [ uint32_t, '%#x',  'backing_file_size' ],
29*6e19b3c4SKevin Wolf        [ uint32_t, '%d',   'cluster_bits' ],
30*6e19b3c4SKevin Wolf        [ uint64_t, '%d',   'size' ],
31*6e19b3c4SKevin Wolf        [ uint32_t, '%d',   'crypt_method' ],
32*6e19b3c4SKevin Wolf        [ uint32_t, '%d',   'l1_size' ],
33*6e19b3c4SKevin Wolf        [ uint64_t, '%#x',  'l1_table_offset' ],
34*6e19b3c4SKevin Wolf        [ uint64_t, '%#x',  'refcount_table_offset' ],
35*6e19b3c4SKevin Wolf        [ uint32_t, '%d',   'refcount_table_clusters' ],
36*6e19b3c4SKevin Wolf        [ uint32_t, '%d',   'nb_snapshots' ],
37*6e19b3c4SKevin Wolf        [ uint64_t, '%#x',  'snapshot_offset' ],
38*6e19b3c4SKevin Wolf    ];
39*6e19b3c4SKevin Wolf
40*6e19b3c4SKevin Wolf    fmt = '>' + ''.join(field[0] for field in fields)
41*6e19b3c4SKevin Wolf
42*6e19b3c4SKevin Wolf    def __init__(self, fd):
43*6e19b3c4SKevin Wolf
44*6e19b3c4SKevin Wolf        buf_size = struct.calcsize(QcowHeader.fmt)
45*6e19b3c4SKevin Wolf
46*6e19b3c4SKevin Wolf        fd.seek(0)
47*6e19b3c4SKevin Wolf        buf = fd.read(buf_size)
48*6e19b3c4SKevin Wolf
49*6e19b3c4SKevin Wolf        header = struct.unpack(QcowHeader.fmt, buf)
50*6e19b3c4SKevin Wolf        self.__dict__ = dict((field[2], header[i])
51*6e19b3c4SKevin Wolf            for i, field in enumerate(QcowHeader.fields))
52*6e19b3c4SKevin Wolf
53*6e19b3c4SKevin Wolf        self.cluster_size = 1 << self.cluster_bits
54*6e19b3c4SKevin Wolf
55*6e19b3c4SKevin Wolf        fd.seek(self.get_header_length())
56*6e19b3c4SKevin Wolf        self.load_extensions(fd)
57*6e19b3c4SKevin Wolf
58*6e19b3c4SKevin Wolf        if self.backing_file_offset:
59*6e19b3c4SKevin Wolf            fd.seek(self.backing_file_offset)
60*6e19b3c4SKevin Wolf            self.backing_file = fd.read(self.backing_file_size)
61*6e19b3c4SKevin Wolf        else:
62*6e19b3c4SKevin Wolf            self.backing_file = None
63*6e19b3c4SKevin Wolf
64*6e19b3c4SKevin Wolf    def get_header_length(self):
65*6e19b3c4SKevin Wolf        if self.version == 2:
66*6e19b3c4SKevin Wolf            return 72
67*6e19b3c4SKevin Wolf        else:
68*6e19b3c4SKevin Wolf            raise Exception("version != 2 not supported")
69*6e19b3c4SKevin Wolf
70*6e19b3c4SKevin Wolf    def load_extensions(self, fd):
71*6e19b3c4SKevin Wolf        self.extensions = []
72*6e19b3c4SKevin Wolf
73*6e19b3c4SKevin Wolf        if self.backing_file_offset != 0:
74*6e19b3c4SKevin Wolf            end = min(self.cluster_size, self.backing_file_offset)
75*6e19b3c4SKevin Wolf        else:
76*6e19b3c4SKevin Wolf            end = self.cluster_size
77*6e19b3c4SKevin Wolf
78*6e19b3c4SKevin Wolf        while fd.tell() < end:
79*6e19b3c4SKevin Wolf            (magic, length) = struct.unpack('>II', fd.read(8))
80*6e19b3c4SKevin Wolf            if magic == 0:
81*6e19b3c4SKevin Wolf                break
82*6e19b3c4SKevin Wolf            else:
83*6e19b3c4SKevin Wolf                padded = (length + 7) & ~7
84*6e19b3c4SKevin Wolf                data = fd.read(padded)
85*6e19b3c4SKevin Wolf                self.extensions.append(QcowHeaderExtension(magic, length, data))
86*6e19b3c4SKevin Wolf
87*6e19b3c4SKevin Wolf    def update_extensions(self, fd):
88*6e19b3c4SKevin Wolf
89*6e19b3c4SKevin Wolf        fd.seek(self.get_header_length())
90*6e19b3c4SKevin Wolf        extensions = self.extensions
91*6e19b3c4SKevin Wolf        extensions.append(QcowHeaderExtension(0, 0, ""))
92*6e19b3c4SKevin Wolf        for ex in extensions:
93*6e19b3c4SKevin Wolf            buf = struct.pack('>II', ex.magic, ex.length)
94*6e19b3c4SKevin Wolf            fd.write(buf)
95*6e19b3c4SKevin Wolf            fd.write(ex.data)
96*6e19b3c4SKevin Wolf
97*6e19b3c4SKevin Wolf        if self.backing_file != None:
98*6e19b3c4SKevin Wolf            self.backing_file_offset = fd.tell()
99*6e19b3c4SKevin Wolf            fd.write(self.backing_file)
100*6e19b3c4SKevin Wolf
101*6e19b3c4SKevin Wolf        if fd.tell() > self.cluster_size:
102*6e19b3c4SKevin Wolf            raise Exception("I think I just broke the image...")
103*6e19b3c4SKevin Wolf
104*6e19b3c4SKevin Wolf
105*6e19b3c4SKevin Wolf    def update(self, fd):
106*6e19b3c4SKevin Wolf        header_bytes = self.get_header_length()
107*6e19b3c4SKevin Wolf
108*6e19b3c4SKevin Wolf        self.update_extensions(fd)
109*6e19b3c4SKevin Wolf
110*6e19b3c4SKevin Wolf        fd.seek(0)
111*6e19b3c4SKevin Wolf        header = tuple(self.__dict__[f] for t, p, f in QcowHeader.fields)
112*6e19b3c4SKevin Wolf        buf = struct.pack(QcowHeader.fmt, *header)
113*6e19b3c4SKevin Wolf        buf = buf[0:header_bytes-1]
114*6e19b3c4SKevin Wolf        fd.write(buf)
115*6e19b3c4SKevin Wolf
116*6e19b3c4SKevin Wolf    def dump(self):
117*6e19b3c4SKevin Wolf        for f in QcowHeader.fields:
118*6e19b3c4SKevin Wolf            print "%-25s" % f[2], f[1] % self.__dict__[f[2]]
119*6e19b3c4SKevin Wolf        print ""
120*6e19b3c4SKevin Wolf
121*6e19b3c4SKevin Wolf    def dump_extensions(self):
122*6e19b3c4SKevin Wolf        for ex in self.extensions:
123*6e19b3c4SKevin Wolf
124*6e19b3c4SKevin Wolf            data = ex.data[:ex.length]
125*6e19b3c4SKevin Wolf            if all(c in string.printable for c in data):
126*6e19b3c4SKevin Wolf                data = "'%s'" % data
127*6e19b3c4SKevin Wolf            else:
128*6e19b3c4SKevin Wolf                data = "<binary>"
129*6e19b3c4SKevin Wolf
130*6e19b3c4SKevin Wolf            print "Header extension:"
131*6e19b3c4SKevin Wolf            print "%-25s %#x" % ("magic", ex.magic)
132*6e19b3c4SKevin Wolf            print "%-25s %d" % ("length", ex.length)
133*6e19b3c4SKevin Wolf            print "%-25s %s" % ("data", data)
134*6e19b3c4SKevin Wolf            print ""
135*6e19b3c4SKevin Wolf
136*6e19b3c4SKevin Wolf
137*6e19b3c4SKevin Wolfdef cmd_dump_header(fd):
138*6e19b3c4SKevin Wolf    h = QcowHeader(fd)
139*6e19b3c4SKevin Wolf    h.dump()
140*6e19b3c4SKevin Wolf    h.dump_extensions()
141*6e19b3c4SKevin Wolf
142*6e19b3c4SKevin Wolfdef cmd_add_header_ext(fd, magic, data):
143*6e19b3c4SKevin Wolf    try:
144*6e19b3c4SKevin Wolf        magic = int(magic, 0)
145*6e19b3c4SKevin Wolf    except:
146*6e19b3c4SKevin Wolf        print "'%s' is not a valid magic number" % magic
147*6e19b3c4SKevin Wolf        sys.exit(1)
148*6e19b3c4SKevin Wolf
149*6e19b3c4SKevin Wolf    h = QcowHeader(fd)
150*6e19b3c4SKevin Wolf    h.extensions.append(QcowHeaderExtension.create(magic, data))
151*6e19b3c4SKevin Wolf    h.update(fd)
152*6e19b3c4SKevin Wolf
153*6e19b3c4SKevin Wolfdef cmd_del_header_ext(fd, magic):
154*6e19b3c4SKevin Wolf    try:
155*6e19b3c4SKevin Wolf        magic = int(magic, 0)
156*6e19b3c4SKevin Wolf    except:
157*6e19b3c4SKevin Wolf        print "'%s' is not a valid magic number" % magic
158*6e19b3c4SKevin Wolf        sys.exit(1)
159*6e19b3c4SKevin Wolf
160*6e19b3c4SKevin Wolf    h = QcowHeader(fd)
161*6e19b3c4SKevin Wolf    found = False
162*6e19b3c4SKevin Wolf
163*6e19b3c4SKevin Wolf    for ex in h.extensions:
164*6e19b3c4SKevin Wolf        if ex.magic == magic:
165*6e19b3c4SKevin Wolf            found = True
166*6e19b3c4SKevin Wolf            h.extensions.remove(ex)
167*6e19b3c4SKevin Wolf
168*6e19b3c4SKevin Wolf    if not found:
169*6e19b3c4SKevin Wolf        print "No such header extension"
170*6e19b3c4SKevin Wolf        return
171*6e19b3c4SKevin Wolf
172*6e19b3c4SKevin Wolf    h.update(fd)
173*6e19b3c4SKevin Wolf
174*6e19b3c4SKevin Wolfcmds = [
175*6e19b3c4SKevin Wolf    [ 'dump-header',    cmd_dump_header,    0, 'Dump image header and header extensions' ],
176*6e19b3c4SKevin Wolf    [ 'add-header-ext', cmd_add_header_ext, 2, 'Add a header extension' ],
177*6e19b3c4SKevin Wolf    [ 'del-header-ext', cmd_del_header_ext, 1, 'Delete a header extension' ],
178*6e19b3c4SKevin Wolf]
179*6e19b3c4SKevin Wolf
180*6e19b3c4SKevin Wolfdef main(filename, cmd, args):
181*6e19b3c4SKevin Wolf    fd = open(filename, "r+b")
182*6e19b3c4SKevin Wolf    try:
183*6e19b3c4SKevin Wolf        for name, handler, num_args, desc in cmds:
184*6e19b3c4SKevin Wolf            if name != cmd:
185*6e19b3c4SKevin Wolf                continue
186*6e19b3c4SKevin Wolf            elif len(args) != num_args:
187*6e19b3c4SKevin Wolf                usage()
188*6e19b3c4SKevin Wolf                return
189*6e19b3c4SKevin Wolf            else:
190*6e19b3c4SKevin Wolf                handler(fd, *args)
191*6e19b3c4SKevin Wolf                return
192*6e19b3c4SKevin Wolf        print "Unknown command '%s'" % cmd
193*6e19b3c4SKevin Wolf    finally:
194*6e19b3c4SKevin Wolf        fd.close()
195*6e19b3c4SKevin Wolf
196*6e19b3c4SKevin Wolfdef usage():
197*6e19b3c4SKevin Wolf    print "Usage: %s <file> <cmd> [<arg>, ...]" % sys.argv[0]
198*6e19b3c4SKevin Wolf    print ""
199*6e19b3c4SKevin Wolf    print "Supported commands:"
200*6e19b3c4SKevin Wolf    for name, handler, num_args, desc in cmds:
201*6e19b3c4SKevin Wolf        print "    %-20s - %s" % (name, desc)
202*6e19b3c4SKevin Wolf
203*6e19b3c4SKevin Wolfif len(sys.argv) < 3:
204*6e19b3c4SKevin Wolf    usage()
205*6e19b3c4SKevin Wolf    sys.exit(1)
206*6e19b3c4SKevin Wolf
207*6e19b3c4SKevin Wolfmain(sys.argv[1], sys.argv[2], sys.argv[3:])
208