qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

qcow2.py (4555B)


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