#!/usr/bin/python

######################################################################
# imount
# version 0.3
# Copyright (C) 2007 by Daniel Lenski <lenski@umd.edu>
# Time-stamp: <2007-04-05 19:24:39 dlenski>
#
# Released under the terms of the
# GNU General Public License version 2 or later
######################################################################
usage='''usage: %prog [options] imagefile [directory]

Easily mount an image file (e.g. ISO or disk image), even if compressed.
This program will create the directory if it does not exist, and will
come up with a default directory name if not specified.'''

import os
from sys import *
from tempfile import NamedTemporaryFile
from optparse import OptionParser

def runcmd(*args, **redir):
    fh = dict(stdin=0, stdout=1, stderr=2)
    savefh = dict((k,os.dup(fh[k])) for k in redir)
    for k in redir: os.dup2(redir[k].fileno(), fh[k])
            
    if len(args)==1 and hasattr(args[0],'__len__'): args=args[0]
    s = os.spawnv(os.P_WAIT, args[0], args)

    for k in redir: os.dup2(savefh[k], fh[k])
    return s

def nonify(t, n):
    return t+[None]*(n-len(t))

# parse arguments

p = OptionParser(usage=usage)
p.add_option('-t', dest='type', default='auto',
             help='filesystem type, passed to mount(8)')
p.add_option('-u', dest='setugid', default=True,
             action='store_const', const=False,
             help='do not try to set UID/GID on mounted image')
p.add_option('-o', action='append', dest='mountopts', default=[],
             help='mounting options, passed to mount(8)')
p.add_option('-j', action='store_const',
             const=('/bin/bunzip2', '-c'), dest='filter',
             help='filter the image through bunzip2 before mounting')
p.add_option('-z', action='store_const',
             const=('/bin/gunzip', '-c'), dest='filter',
             help='filter the image through gunzip bfore mounting')
p.prog = os.path.basename(argv[0])
opt, args = p.parse_args()

if not (1 <= len(args) <= 2):
    if len(args)<1: print>>stderr, "%s: image file name expected!" % p.prog
    if len(args)>2: print>>stderr, "%s: too many arguments!" % p.prog
    p.print_help()
    exit(1)
else:
    image, mdir = nonify(args, 2)

# filter through a decompressor if necessary

if opt.filter:
    tmp = NamedTemporaryFile()
    s = runcmd(opt.filter + (image,), stdout=tmp)
    if s: raise OSError('%s: filter failed with status %d' % (p.prog, s))
    imagefile = tmp.name
    opt.mountopts.append('ro')
    print>>stderr, "%s: filtered image will be mounted read-only" % p.prog
else:
    imagefile = image

# pick a directory name if none specified

if not mdir:
    fn, ext = os.path.splitext(image)
    mdir=(ext and fn or fn+'.dir')
    print>>stderr, "%s: image will be mounted in directory %s" % (p.prog, mdir)

# create the directory if it doesn't exist

if not os.access(mdir, os.F_OK):
    os.mkdir(mdir)
    print>>stderr, "%s: created directory %s to mount image" % (p.prog, mdir)

# get root permissions, and mount

opt.mountopts.insert(0, 'loop')
if opt.setugid:
  opt.mountopts.extend(['uid=%d'%os.getuid(), 'gid=%d'%os.getgid()])

cmdline = ('/usr/bin/sudo', '/bin/mount', '-s', '-o', ','.join(opt.mountopts),
           '-t', opt.type, imagefile, mdir)
print cmdline
s = runcmd('/usr/bin/sudo', '/bin/mount', '-s', '-o', ','.join(opt.mountopts),
           '-t', opt.type, imagefile, mdir)
if s: raise OSError('mount failed with status %d' % s)
