113776Sgambordr@oregonstate.edu#!/usr/bin/python2.7
28265Sgblack@eecs.umich.edu#
38265Sgblack@eecs.umich.edu# gem5img.py
48265Sgblack@eecs.umich.edu# Script for managing a gem5 disk image.
58265Sgblack@eecs.umich.edu#
68265Sgblack@eecs.umich.edu
78265Sgblack@eecs.umich.edufrom optparse import OptionParser
88265Sgblack@eecs.umich.eduimport os
98265Sgblack@eecs.umich.edufrom os import environ as env
108265Sgblack@eecs.umich.eduimport string
118265Sgblack@eecs.umich.edufrom subprocess import CalledProcessError, Popen, PIPE, STDOUT
128265Sgblack@eecs.umich.edufrom sys import exit, argv
138265Sgblack@eecs.umich.edu
148265Sgblack@eecs.umich.edu
158265Sgblack@eecs.umich.edu# Some constants.
168265Sgblack@eecs.umich.eduMaxLBACylinders = 16383
178265Sgblack@eecs.umich.eduMaxLBAHeads = 16
188265Sgblack@eecs.umich.eduMaxLBASectors = 63
198265Sgblack@eecs.umich.eduMaxLBABlocks = MaxLBACylinders * MaxLBAHeads * MaxLBASectors
208265Sgblack@eecs.umich.edu
218265Sgblack@eecs.umich.eduBlockSize = 512
228265Sgblack@eecs.umich.eduMB = 1024 * 1024
238265Sgblack@eecs.umich.edu
248265Sgblack@eecs.umich.edu# Setup PATH to look in the sbins.
258265Sgblack@eecs.umich.eduenv['PATH'] += ':/sbin:/usr/sbin'
268265Sgblack@eecs.umich.edu
278265Sgblack@eecs.umich.edu# Whether to print debug output.
288265Sgblack@eecs.umich.edudebug = False
298265Sgblack@eecs.umich.edu
308265Sgblack@eecs.umich.edu# Figure out cylinders, heads and sectors from a size in blocks.
318265Sgblack@eecs.umich.edudef chsFromSize(sizeInBlocks):
328265Sgblack@eecs.umich.edu    if sizeInBlocks >= MaxLBABlocks:
338265Sgblack@eecs.umich.edu        sizeInMBs = (sizeInBlocks * BlockSize) / MB
348265Sgblack@eecs.umich.edu        print '%d MB is too big for LBA, truncating file.' % sizeInMBs
358265Sgblack@eecs.umich.edu        return (MaxLBACylinders, MaxLBAHeads, MaxLBASectors)
368265Sgblack@eecs.umich.edu
378265Sgblack@eecs.umich.edu    sectors = sizeInBlocks
388265Sgblack@eecs.umich.edu    if sizeInBlocks > 63:
398265Sgblack@eecs.umich.edu        sectors = 63
408265Sgblack@eecs.umich.edu
418265Sgblack@eecs.umich.edu    headSize = sizeInBlocks / sectors
428265Sgblack@eecs.umich.edu    heads = 16
438265Sgblack@eecs.umich.edu    if headSize < 16:
448265Sgblack@eecs.umich.edu        heads = sizeInBlocks
458265Sgblack@eecs.umich.edu
468265Sgblack@eecs.umich.edu    cylinders = sizeInBlocks / (sectors * heads)
478265Sgblack@eecs.umich.edu
488265Sgblack@eecs.umich.edu    return (cylinders, heads, sectors)
498265Sgblack@eecs.umich.edu
508265Sgblack@eecs.umich.edu
518265Sgblack@eecs.umich.edu# Figure out if we should use sudo.
528265Sgblack@eecs.umich.edudef needSudo():
538265Sgblack@eecs.umich.edu    if not hasattr(needSudo, 'notRoot'):
548265Sgblack@eecs.umich.edu        needSudo.notRoot = (os.geteuid() != 0)
558265Sgblack@eecs.umich.edu        if needSudo.notRoot:
568265Sgblack@eecs.umich.edu            print 'You are not root. Using sudo.'
578265Sgblack@eecs.umich.edu    return needSudo.notRoot
588265Sgblack@eecs.umich.edu
598265Sgblack@eecs.umich.edu# Run an external command.
608265Sgblack@eecs.umich.edudef runCommand(command, inputVal=''):
618265Sgblack@eecs.umich.edu    print "%>", ' '.join(command)
628265Sgblack@eecs.umich.edu    proc = Popen(command, stdin=PIPE)
638265Sgblack@eecs.umich.edu    proc.communicate(inputVal)
648265Sgblack@eecs.umich.edu    return proc.returncode
658265Sgblack@eecs.umich.edu
668265Sgblack@eecs.umich.edu# Run an external command and capture its output. This is intended to be
678265Sgblack@eecs.umich.edu# used with non-interactive commands where the output is for internal use.
688265Sgblack@eecs.umich.edudef getOutput(command, inputVal=''):
698265Sgblack@eecs.umich.edu    global debug
708265Sgblack@eecs.umich.edu    if debug:
718265Sgblack@eecs.umich.edu        print "%>", ' '.join(command)
728265Sgblack@eecs.umich.edu    proc = Popen(command, stderr=STDOUT,
738265Sgblack@eecs.umich.edu                 stdin=PIPE, stdout=PIPE)
748265Sgblack@eecs.umich.edu    (out, err) = proc.communicate(inputVal)
758265Sgblack@eecs.umich.edu    return (out, proc.returncode)
768265Sgblack@eecs.umich.edu
778265Sgblack@eecs.umich.edu# Run a command as root, using sudo if necessary.
788265Sgblack@eecs.umich.edudef runPriv(command, inputVal=''):
798265Sgblack@eecs.umich.edu    realCommand = command
808265Sgblack@eecs.umich.edu    if needSudo():
818265Sgblack@eecs.umich.edu        realCommand = [findProg('sudo')] + command
828265Sgblack@eecs.umich.edu    return runCommand(realCommand, inputVal)
838265Sgblack@eecs.umich.edu
848265Sgblack@eecs.umich.edudef privOutput(command, inputVal=''):
858265Sgblack@eecs.umich.edu    realCommand = command
868265Sgblack@eecs.umich.edu    if needSudo():
878265Sgblack@eecs.umich.edu        realCommand = [findProg('sudo')] + command
888265Sgblack@eecs.umich.edu    return getOutput(realCommand, inputVal)
898265Sgblack@eecs.umich.edu
908265Sgblack@eecs.umich.edu# Find the path to a program.
918265Sgblack@eecs.umich.edudef findProg(program, cleanupDev=None):
928265Sgblack@eecs.umich.edu    (out, returncode) = getOutput(['which', program])
938265Sgblack@eecs.umich.edu    if returncode != 0:
948265Sgblack@eecs.umich.edu        if cleanupDev:
958265Sgblack@eecs.umich.edu            cleanupDev.destroy()
968265Sgblack@eecs.umich.edu        exit("Unable to find program %s, check your PATH variable." % program)
978265Sgblack@eecs.umich.edu    return string.strip(out)
988265Sgblack@eecs.umich.edu
998265Sgblack@eecs.umich.educlass LoopbackDevice(object):
1008265Sgblack@eecs.umich.edu    def __init__(self, devFile=None):
1018265Sgblack@eecs.umich.edu        self.devFile = devFile
1028265Sgblack@eecs.umich.edu    def __str__(self):
1038265Sgblack@eecs.umich.edu        return str(self.devFile)
1048265Sgblack@eecs.umich.edu
1058265Sgblack@eecs.umich.edu    def setup(self, fileName, offset=False):
1068265Sgblack@eecs.umich.edu        assert not self.devFile
1078265Sgblack@eecs.umich.edu        (out, returncode) = privOutput([findProg('losetup'), '-f'])
1088265Sgblack@eecs.umich.edu        if returncode != 0:
1098265Sgblack@eecs.umich.edu            print out
1108265Sgblack@eecs.umich.edu            return returncode
1118265Sgblack@eecs.umich.edu        self.devFile = string.strip(out)
1128265Sgblack@eecs.umich.edu        command = [findProg('losetup'), self.devFile, fileName]
1138265Sgblack@eecs.umich.edu        if offset:
1148265Sgblack@eecs.umich.edu            off = findPartOffset(self.devFile, fileName, 0)
1158265Sgblack@eecs.umich.edu            command = command[:1] + \
1168265Sgblack@eecs.umich.edu                      ["-o", "%d" % off] + \
1178265Sgblack@eecs.umich.edu                      command[1:]
1188265Sgblack@eecs.umich.edu        return runPriv(command)
1198265Sgblack@eecs.umich.edu
1208265Sgblack@eecs.umich.edu    def destroy(self):
1218265Sgblack@eecs.umich.edu        assert self.devFile
1228265Sgblack@eecs.umich.edu        returncode = runPriv([findProg('losetup'), '-d', self.devFile])
1238265Sgblack@eecs.umich.edu        self.devFile = None
1248265Sgblack@eecs.umich.edu        return returncode
1258265Sgblack@eecs.umich.edu
1268265Sgblack@eecs.umich.edudef findPartOffset(devFile, fileName, partition):
1278265Sgblack@eecs.umich.edu    # Attach a loopback device to the file so we can use sfdisk on it.
1288265Sgblack@eecs.umich.edu    dev = LoopbackDevice()
1298265Sgblack@eecs.umich.edu    dev.setup(fileName)
1308265Sgblack@eecs.umich.edu    # Dump the partition information.
1318265Sgblack@eecs.umich.edu    command = [findProg('sfdisk'), '-d', dev.devFile]
1328265Sgblack@eecs.umich.edu    (out, returncode) = privOutput(command)
1338265Sgblack@eecs.umich.edu    if returncode != 0:
1348265Sgblack@eecs.umich.edu        print out
1358265Sgblack@eecs.umich.edu        exit(returncode)
1368265Sgblack@eecs.umich.edu    lines = out.splitlines()
1378265Sgblack@eecs.umich.edu    # Make sure the first few lines of the output look like what we expect.
1388265Sgblack@eecs.umich.edu    assert(lines[0][0] == '#')
1398265Sgblack@eecs.umich.edu    assert(lines[1] == 'unit: sectors')
1408265Sgblack@eecs.umich.edu    assert(lines[2] == '')
1418265Sgblack@eecs.umich.edu    # This line has information about the first partition.
1428265Sgblack@eecs.umich.edu    chunks = lines[3].split()
1438265Sgblack@eecs.umich.edu    # The fourth chunk is the offset of the partition in sectors followed by
1448265Sgblack@eecs.umich.edu    # a comma. We drop the comma and convert that to an integer.
1458265Sgblack@eecs.umich.edu    sectors = string.atoi(chunks[3][:-1])
1468265Sgblack@eecs.umich.edu    # Free the loopback device and return an answer.
1478265Sgblack@eecs.umich.edu    dev.destroy()
1488265Sgblack@eecs.umich.edu    return sectors * BlockSize
1498265Sgblack@eecs.umich.edu
1508265Sgblack@eecs.umich.edudef mountPointToDev(mountPoint):
1518265Sgblack@eecs.umich.edu    (mountTable, returncode) = getOutput([findProg('mount')])
1528265Sgblack@eecs.umich.edu    if returncode != 0:
1538265Sgblack@eecs.umich.edu        print mountTable
1548265Sgblack@eecs.umich.edu        exit(returncode)
1558265Sgblack@eecs.umich.edu    mountTable = mountTable.splitlines()
1568265Sgblack@eecs.umich.edu    for line in mountTable:
1578265Sgblack@eecs.umich.edu        chunks = line.split()
1588265Sgblack@eecs.umich.edu        if os.path.samefile(chunks[2], mountPoint):
1598265Sgblack@eecs.umich.edu            return LoopbackDevice(chunks[0])
1608265Sgblack@eecs.umich.edu    return None
1618265Sgblack@eecs.umich.edu
1628265Sgblack@eecs.umich.edu
1638265Sgblack@eecs.umich.edu# Commands for the gem5img.py script
1648265Sgblack@eecs.umich.educommands = {}
1658265Sgblack@eecs.umich.educommandOrder = []
1668265Sgblack@eecs.umich.edu
1678265Sgblack@eecs.umich.educlass Command(object):
1688265Sgblack@eecs.umich.edu    def addOption(self, *args, **kargs):
1698265Sgblack@eecs.umich.edu        self.parser.add_option(*args, **kargs)
1708265Sgblack@eecs.umich.edu
1718265Sgblack@eecs.umich.edu    def __init__(self, name, description, posArgs):
1728265Sgblack@eecs.umich.edu        self.name = name
1738265Sgblack@eecs.umich.edu        self.description = description
1748265Sgblack@eecs.umich.edu        self.func = None
1758265Sgblack@eecs.umich.edu        self.posArgs = posArgs
1768265Sgblack@eecs.umich.edu        commands[self.name] = self
1778265Sgblack@eecs.umich.edu        commandOrder.append(self.name)
1788265Sgblack@eecs.umich.edu        usage = 'usage: %prog [options]'
1798265Sgblack@eecs.umich.edu        posUsage = ''
1808265Sgblack@eecs.umich.edu        for posArg in posArgs:
1818265Sgblack@eecs.umich.edu            (argName, argDesc) = posArg
1828265Sgblack@eecs.umich.edu            usage += ' %s' % argName
1838265Sgblack@eecs.umich.edu            posUsage += '\n  %s: %s' % posArg
1848265Sgblack@eecs.umich.edu        usage += posUsage
1858265Sgblack@eecs.umich.edu        self.parser = OptionParser(usage=usage, description=description)
1868265Sgblack@eecs.umich.edu        self.addOption('-d', '--debug', dest='debug', action='store_true',
1878265Sgblack@eecs.umich.edu                       help='Verbose output.')
1888265Sgblack@eecs.umich.edu
1898265Sgblack@eecs.umich.edu    def parseArgs(self, argv):
1908265Sgblack@eecs.umich.edu        (self.options, self.args) = self.parser.parse_args(argv[2:])
1918265Sgblack@eecs.umich.edu        if len(self.args) != len(self.posArgs):
1928265Sgblack@eecs.umich.edu            self.parser.error('Incorrect number of arguments')
1938265Sgblack@eecs.umich.edu        global debug
1948265Sgblack@eecs.umich.edu        if self.options.debug:
1958265Sgblack@eecs.umich.edu            debug = True
1968265Sgblack@eecs.umich.edu
1978265Sgblack@eecs.umich.edu    def runCom(self):
1988265Sgblack@eecs.umich.edu        if not self.func:
1998265Sgblack@eecs.umich.edu            exit('Unimplemented command %s!' % self.name)
2008265Sgblack@eecs.umich.edu        self.func(self.options, self.args)
2018265Sgblack@eecs.umich.edu
2028265Sgblack@eecs.umich.edu
2038265Sgblack@eecs.umich.edu# A command which prepares an image with an partition table and an empty file
2048265Sgblack@eecs.umich.edu# system.
2058265Sgblack@eecs.umich.eduinitCom = Command('init', 'Create an image with an empty file system.',
2068265Sgblack@eecs.umich.edu                  [('file', 'Name of the image file.'),
2078265Sgblack@eecs.umich.edu                   ('mb', 'Size of the file in MB.')])
2088265Sgblack@eecs.umich.eduinitCom.addOption('-t', '--type', dest='fstype', action='store',
2098265Sgblack@eecs.umich.edu                  default='ext2',
2108265Sgblack@eecs.umich.edu                  help='Type of file system to use. Appended to mkfs.')
2118265Sgblack@eecs.umich.edu
2128265Sgblack@eecs.umich.edu# A command to mount the first partition in the image.
2138265Sgblack@eecs.umich.edumountCom = Command('mount', 'Mount the first partition in the disk image.',
2148265Sgblack@eecs.umich.edu                   [('file', 'Name of the image file.'),
2158265Sgblack@eecs.umich.edu                    ('mount point', 'Where to mount the image.')])
2168265Sgblack@eecs.umich.edu
2178265Sgblack@eecs.umich.edudef mountComFunc(options, args):
2188265Sgblack@eecs.umich.edu    (path, mountPoint) = args
2198265Sgblack@eecs.umich.edu    if not os.path.isdir(mountPoint):
2208265Sgblack@eecs.umich.edu        print "Mount point %s is not a directory." % mountPoint
2218265Sgblack@eecs.umich.edu
2228265Sgblack@eecs.umich.edu    dev = LoopbackDevice()
2238265Sgblack@eecs.umich.edu    if dev.setup(path, offset=True) != 0:
2248265Sgblack@eecs.umich.edu        exit(1)
2258265Sgblack@eecs.umich.edu
2268265Sgblack@eecs.umich.edu    if runPriv([findProg('mount'), str(dev), mountPoint]) != 0:
2278265Sgblack@eecs.umich.edu        dev.destroy()
2288265Sgblack@eecs.umich.edu        exit(1)
2298265Sgblack@eecs.umich.edu
2308265Sgblack@eecs.umich.edumountCom.func = mountComFunc
2318265Sgblack@eecs.umich.edu
2328265Sgblack@eecs.umich.edu# A command to unmount the first partition in the image.
2338265Sgblack@eecs.umich.eduumountCom = Command('umount', 'Unmount the first partition in the disk image.',
2348265Sgblack@eecs.umich.edu                    [('mount point', 'What mount point to unmount.')])
2358265Sgblack@eecs.umich.edu
2368265Sgblack@eecs.umich.edudef umountComFunc(options, args):
2378265Sgblack@eecs.umich.edu    (mountPoint,) = args
2388265Sgblack@eecs.umich.edu    if not os.path.isdir(mountPoint):
2398265Sgblack@eecs.umich.edu        print "Mount point %s is not a directory." % mountPoint
2408265Sgblack@eecs.umich.edu        exit(1)
2418265Sgblack@eecs.umich.edu
2428265Sgblack@eecs.umich.edu    dev = mountPointToDev(mountPoint)
2438265Sgblack@eecs.umich.edu    if not dev:
2448265Sgblack@eecs.umich.edu        print "Unable to find mount information for %s." % mountPoint
2458265Sgblack@eecs.umich.edu
2468265Sgblack@eecs.umich.edu    # Unmount the loopback device.
2478265Sgblack@eecs.umich.edu    if runPriv([findProg('umount'), mountPoint]) != 0:
2488265Sgblack@eecs.umich.edu        exit(1)
2498265Sgblack@eecs.umich.edu
2508265Sgblack@eecs.umich.edu    # Destroy the loopback device.
2518265Sgblack@eecs.umich.edu    dev.destroy()
2528265Sgblack@eecs.umich.edu
2538265Sgblack@eecs.umich.eduumountCom.func = umountComFunc
2548265Sgblack@eecs.umich.edu
2558265Sgblack@eecs.umich.edu
2568265Sgblack@eecs.umich.edu# A command to create an empty file to hold the image.
2578265Sgblack@eecs.umich.edunewCom = Command('new', 'File creation part of "init".',
2588265Sgblack@eecs.umich.edu                 [('file', 'Name of the image file.'),
2598265Sgblack@eecs.umich.edu                  ('mb', 'Size of the file in MB.')])
2608265Sgblack@eecs.umich.edu
2618265Sgblack@eecs.umich.edudef newImage(file, mb):
2628265Sgblack@eecs.umich.edu    (cylinders, heads, sectors) = chsFromSize((mb * MB) / BlockSize)
2638265Sgblack@eecs.umich.edu    size = cylinders * heads * sectors * BlockSize
2648265Sgblack@eecs.umich.edu
2658265Sgblack@eecs.umich.edu    # We lseek to the end of the file and only write one byte there. This
2668265Sgblack@eecs.umich.edu    # leaves a "hole" which many file systems are smart enough not to actually
2678265Sgblack@eecs.umich.edu    # store to disk and which is defined to read as zero.
2688265Sgblack@eecs.umich.edu    fd = os.open(file, os.O_WRONLY | os.O_CREAT)
2698265Sgblack@eecs.umich.edu    os.lseek(fd, size - 1, os.SEEK_SET)
2708265Sgblack@eecs.umich.edu    os.write(fd, '\0')
2718265Sgblack@eecs.umich.edu
2728265Sgblack@eecs.umich.edudef newComFunc(options, args):
2738265Sgblack@eecs.umich.edu    (file, mb) = args
2748265Sgblack@eecs.umich.edu    mb = string.atoi(mb)
2758265Sgblack@eecs.umich.edu    newImage(file, mb)
2768265Sgblack@eecs.umich.edu
2778265Sgblack@eecs.umich.edu
2788265Sgblack@eecs.umich.edunewCom.func = newComFunc
2798265Sgblack@eecs.umich.edu
2808265Sgblack@eecs.umich.edu# A command to partition the image file like a raw disk device.
2818265Sgblack@eecs.umich.edupartitionCom = Command('partition', 'Partition part of "init".',
2828265Sgblack@eecs.umich.edu                       [('file', 'Name of the image file.')])
2838265Sgblack@eecs.umich.edu
2848265Sgblack@eecs.umich.edudef partition(dev, cylinders, heads, sectors):
2858265Sgblack@eecs.umich.edu    # Use fdisk to partition the device
2868265Sgblack@eecs.umich.edu    comStr = '0,\n;\n;\n;\n'
2878265Sgblack@eecs.umich.edu    return runPriv([findProg('sfdisk'), '--no-reread', '-D', \
2888265Sgblack@eecs.umich.edu                   '-C', "%d" % cylinders, \
2898265Sgblack@eecs.umich.edu                   '-H', "%d" % heads, \
2908265Sgblack@eecs.umich.edu                   '-S', "%d" % sectors, \
2918265Sgblack@eecs.umich.edu                   str(dev)], inputVal=comStr)
2928265Sgblack@eecs.umich.edu
2938265Sgblack@eecs.umich.edudef partitionComFunc(options, args):
2948265Sgblack@eecs.umich.edu    (path,) = args
2958265Sgblack@eecs.umich.edu
2968265Sgblack@eecs.umich.edu    dev = LoopbackDevice()
2978265Sgblack@eecs.umich.edu    if dev.setup(path) != 0:
2988265Sgblack@eecs.umich.edu        exit(1)
2998265Sgblack@eecs.umich.edu
3008265Sgblack@eecs.umich.edu    # Figure out the dimensions of the file.
3018265Sgblack@eecs.umich.edu    size = os.path.getsize(path)
3028265Sgblack@eecs.umich.edu    if partition(dev, *chsFromSize(size / BlockSize)) != 0:
3038265Sgblack@eecs.umich.edu        dev.destroy()
3048265Sgblack@eecs.umich.edu        exit(1)
3058265Sgblack@eecs.umich.edu
3068265Sgblack@eecs.umich.edu    dev.destroy()
3078265Sgblack@eecs.umich.edu
3088265Sgblack@eecs.umich.edupartitionCom.func = partitionComFunc
3098265Sgblack@eecs.umich.edu
3108265Sgblack@eecs.umich.edu# A command to format the first partition in the image.
3118265Sgblack@eecs.umich.eduformatCom = Command('format', 'Formatting part of "init".',
3128265Sgblack@eecs.umich.edu                    [('file', 'Name of the image file.')])
3138265Sgblack@eecs.umich.eduformatCom.addOption('-t', '--type', dest='fstype', action='store',
3148265Sgblack@eecs.umich.edu                    default='ext2',
3158265Sgblack@eecs.umich.edu                    help='Type of file system to use. Appended to mkfs.')
3168265Sgblack@eecs.umich.edu
3178265Sgblack@eecs.umich.edudef formatImage(dev, fsType):
3188265Sgblack@eecs.umich.edu    return runPriv([findProg('mkfs.%s' % fsType, dev), str(dev)])
3198265Sgblack@eecs.umich.edu
3208265Sgblack@eecs.umich.edudef formatComFunc(options, args):
3218265Sgblack@eecs.umich.edu    (path,) = args
3228265Sgblack@eecs.umich.edu
3238265Sgblack@eecs.umich.edu    dev = LoopbackDevice()
3248265Sgblack@eecs.umich.edu    if dev.setup(path, offset=True) != 0:
3258265Sgblack@eecs.umich.edu        exit(1)
3268265Sgblack@eecs.umich.edu
3278265Sgblack@eecs.umich.edu    # Format the device.
3288265Sgblack@eecs.umich.edu    if formatImage(dev, options.fstype) != 0:
3298265Sgblack@eecs.umich.edu        dev.destroy()
3308265Sgblack@eecs.umich.edu        exit(1)
3318265Sgblack@eecs.umich.edu
3328265Sgblack@eecs.umich.edu    dev.destroy()
3338265Sgblack@eecs.umich.edu
3348265Sgblack@eecs.umich.eduformatCom.func = formatComFunc
3358265Sgblack@eecs.umich.edu
3368265Sgblack@eecs.umich.edudef initComFunc(options, args):
3378265Sgblack@eecs.umich.edu    (path, mb) = args
3388265Sgblack@eecs.umich.edu    mb = string.atoi(mb)
3398265Sgblack@eecs.umich.edu    newImage(path, mb)
3408265Sgblack@eecs.umich.edu    dev = LoopbackDevice()
3418265Sgblack@eecs.umich.edu    if dev.setup(path) != 0:
3428265Sgblack@eecs.umich.edu        exit(1)
3438265Sgblack@eecs.umich.edu    size = os.path.getsize(path)
3448265Sgblack@eecs.umich.edu    if partition(dev, *chsFromSize((mb * MB) / BlockSize)) != 0:
3458265Sgblack@eecs.umich.edu        dev.destroy()
3468265Sgblack@eecs.umich.edu        exit(1)
3478265Sgblack@eecs.umich.edu    dev.destroy()
3488265Sgblack@eecs.umich.edu    if dev.setup(path, offset=True) != 0:
3498265Sgblack@eecs.umich.edu        exit(1)
3508265Sgblack@eecs.umich.edu    if formatImage(dev, options.fstype) != 0:
3518265Sgblack@eecs.umich.edu        dev.destroy()
3528265Sgblack@eecs.umich.edu        exit(1)
3538265Sgblack@eecs.umich.edu    dev.destroy()
3548265Sgblack@eecs.umich.edu
3558265Sgblack@eecs.umich.eduinitCom.func = initComFunc
3568265Sgblack@eecs.umich.edu
3578265Sgblack@eecs.umich.edu
3588265Sgblack@eecs.umich.edu# Figure out what command was requested and execute it.
3598265Sgblack@eecs.umich.eduif len(argv) < 2 or argv[1] not in commands:
3608265Sgblack@eecs.umich.edu    print 'Usage: %s [command] <command arguments>'
3618265Sgblack@eecs.umich.edu    print 'where [command] is one of '
3628265Sgblack@eecs.umich.edu    for name in commandOrder:
3638265Sgblack@eecs.umich.edu        command = commands[name]
3648265Sgblack@eecs.umich.edu        print '    %s: %s' % (command.name, command.description)
3658265Sgblack@eecs.umich.edu    print 'Watch for orphaned loopback devices and delete them with'
3668265Sgblack@eecs.umich.edu    print 'losetup -d. Mounted images will belong to root, so you may need'
3678265Sgblack@eecs.umich.edu    print 'to use sudo to modify their contents.'
3688265Sgblack@eecs.umich.edu    exit(1)
3698265Sgblack@eecs.umich.edu
3708265Sgblack@eecs.umich.educommand = commands[argv[1]]
3718265Sgblack@eecs.umich.educommand.parseArgs(argv)
3728265Sgblack@eecs.umich.educommand.runCom()
373