<font face="'courier new', monospace"><div>This code is ready for Rawhide testing and comment.</div><div>I've used it on Fedora 11 - 15.</div><div><br></div><div>commit b9cdca9fb0ab97239bc68d2184ce16818401a0e2</div>

<div>Author: Frederick Grose <<a href="mailto:fgrose@sugarlabs.org">fgrose@sugarlabs.org</a>></div><div>Date:   Sun Sep 18 20:49:01 2011 -0400</div><div><br></div><div>    Support liveimage mounting from within a booted LiveOS instance.</div>

<div>    </div><div>    Provide functions to add loop devices and remove them on exit, a</div><div>    LiveImageMountError(Exception) class for better error handling,</div><div>    random device-mapper node names, and the ability to liveimage-mount</div>

<div>    an image (in the LiveOS folder) saved in a plain directory.</div><div>    </div><div>    Provide a yum cache mount point option,</div><div>    bind mount /etc/resolv.conf to permit Internet updating,</div><div>    bind mount /dev/mapper to permit device checking,</div>

<div>    support dmsetup versions lacking --noudevrules & --noudevsync,</div><div>    miscellaneous code and whitespace optimizations.</div><div><br></div><div>diff --git a/tools/liveimage-mount b/tools/liveimage-mount</div>

<div>index 80d67d1..aed7606 100755</div><div>--- a/tools/liveimage-mount</div><div>+++ b/tools/liveimage-mount</div><div>@@ -1,9 +1,10 @@</div><div> #!/usr/bin/python -tt</div><div>+# coding: latin-1</div><div>+# 2011-09-18 11:47:42 -0400</div>

<div> #</div><div>-# liveimage-mount: Mount a LiveOS at the specified point, and log</div><div>-# into a shell.</div><div>+# liveimage-mount: Mount a LiveOS at the specified point, and log into a shell.</div><div> #</div>

<div>-# Copyright 2011, Red Hat  Inc.</div><div>+# Copyright 2011, Red Hat  Inc., Sugar Labs®</div><div> #   Code for Live mounting an attached LiveOS device added by Frederick Grose,</div><div> #   <fgrose at <a href="http://sugarlabs.org">sugarlabs.org</a>></div>

<div> #</div><div>@@ -23,23 +24,39 @@</div><div> import os</div><div> import sys</div><div> import stat</div><div>+import time</div><div> import getopt</div><div>+import random</div><div> import tempfile</div><div> import subprocess</div>

<div> </div><div>+extra_loops = []</div><div> </div><div> def usage(ecode):</div><div>     print """Usage:</div><div>-        liveimage-mount [opts] ISO.iso|LiveOSdevice MOUNTPOINT [COMMAND] [ARGS]</div><div>

+       liveimage-mount [opts] ISO.iso|LiveOSdev|dir MOUNTPOINT [COMMAND] [ARGS]</div><div> </div><div>                   where [opts] = [-h|--help</div><div>                                  [--chroot</div><div>                                  [--mount-hacks</div>

<div>-                                 [--persist]]]]]</div><div>+                                 [--persist</div><div>+                                 [-y|--yumcache=YUMCACHEPATH]]]]]</div><div> </div><div>                     and [ARGS] = [arg1[ arg2[ ...]]]\n"""</div>

<div>     sys.exit(ecode)</div><div> </div><div>+class LiveImageMountError(Exception):</div><div>+    """An exception base class for all liveimage-mount errors."""</div><div>+    def __init__(self, msg):</div>

<div>+        Exception.__init__(self, msg)</div><div>+    def __str__(self):</div><div>+        try:</div><div>+            return str(self.message)</div><div>+        except UnicodeEncodeError:</div><div>+            return repr(self.message)</div>

<div>+</div><div>+    def __unicode__(self):</div><div>+        return unicode(self.message)</div><div> </div><div> def call(*popenargs, **kwargs):</div><div>     '''</div><div>@@ -62,43 +79,81 @@ def call(*popenargs, **kwargs):</div>

<div>     return rc</div><div> </div><div> </div><div>-def rcall(args, env=None):</div><div>-    if env:</div><div>+def rcall(args, stdin=None, rpterr=True, env=None):</div><div>+    '''Return stdout, stderr, & returncode from a subprocess call.'''</div>

<div>+</div><div>+    out, err, p, environ = '', '', None, None</div><div>+    if env is not None:</div><div>         environ = os.environ.copy()</div><div>         environ.update(env)</div><div>-    else:</div>

<div>-        environ = None</div><div>     try:</div><div>-        p = subprocess.Popen(args, stdout=subprocess.PIPE,</div><div>-                             stderr=subprocess.PIPE, env=environ)</div><div>-        out, err = p.communicate()</div>

<div>+        p = subprocess.Popen(args, stdin=subprocess.PIPE, env=environ,</div><div>+                             stdout=subprocess.PIPE, stderr=subprocess.PIPE)</div><div>+        out, err = p.communicate(stdin)</div>

<div>     except OSError, e:</div><div>-        raise CreatorError(u"Failed to execute:\n'%s'\n'%s'" % (args, e))</div><div>+        out, err = out, u'Failed executing:\n%s\nerror: %s' % (args, e)</div>

<div>+        if rpterr:</div><div>+            raise LiveImageMountError(u'Failed executing:\n%s\nerror: %s' %</div><div>+                                      (args, e))</div><div>     except:</div><div>-        raise CreatorError(u"""Failed to execute:\n'%s'</div>

<div>-            \renviron: '%s'\nstdout: '%s'\nstderr: '%s'\nreturncode: '%s'""" %</div><div>-            (args, environ, out, err, p.returncode))</div><div>+        out, err = (out, u'Failed to execute:\n%s\nenviron: %s' %</div>

<div>+                          (args, environ))</div><div>+        if rpterr:</div><div>+            raise LiveImageMountError(u'''Failed to execute:\n%s</div><div>+                               \renviron: %s\nstdout: %s\nstderr: %s''' %</div>

<div>+                               (args, environ, out, err))</div><div>     else:</div><div>-        if p.returncode != 0:</div><div>-            raise CreatorError(u"""Error in call:\n'%s'\nenviron: '%s'</div>

<div>-                \rstdout: '%s'\nstderr: '%s'\nreturncode: '%s'""" %</div><div>-                (args, environ, out, err, p.returncode))</div><div>-        return out</div><div>

+        if p.returncode != 0 and rpterr:</div><div>+            raise LiveImageMountError(u'''Error in call:\n%s\nenviron: %s</div><div>+                               \rstdout: %s\nstderr: %s\nreturncode: %s''' %</div>

<div>+                               (args, environ, out, err, p.returncode))</div><div>+    finally:</div><div>+        return out, err</div><div>+</div><div>+</div><div>+def get_fsvalue(format=None, tag=None, token=None, filesystem=None):</div>

<div>+    """Return filesystem information based on a blkid tag, token, or device."""</div><div>+</div><div>+    dev_null = os.open('/dev/null', os.O_WRONLY)</div><div>+    args = ['/sbin/blkid', '-c', '/dev/null']</div>

<div>+    if format:</div><div>+        args.extend(['-o', format])</div><div>+    if tag:</div><div>+        args.extend(['-s', tag])</div><div>+    if token:</div><div>+        args.extend(['-l', '-t', token])</div>

<div>+    if filesystem:</div><div>+        args.extend([filesystem])</div><div>+    try:</div><div>+        fs_value = subprocess.Popen(args,</div><div>+                               stdout=subprocess.PIPE,</div><div>+                               stderr=dev_null).communicate()[0].rstrip()</div>

<div>+    except IOError, e:</div><div>+        raise LiveImageMountError("Failed to determine fs %s: %s" % value, e)</div><div>+    finally:</div><div>+        os.close(dev_null)</div><div>+    return fs_value</div>

<div> </div><div> </div><div>-def get_device_mountpoint(path):</div><div>-    """Return the device and mountpoint for a file or device path."""</div><div>+def add_loop(n=8):</div><div>+    """Add a loop device."""</div>

<div> </div><div>-    info = list()</div><div>-    info[0:5] = [None] * 6</div><div>-    if os.path.exists(path):</div><div>-        st_mode = os.stat(path).st_mode</div><div>-        if stat.S_ISBLK(st_mode) or os.path.ismount(path):</div>

<div>-            devinfo = rcall(['/bin/df', path]).splitlines()</div><div>-            info = devinfo[1].split(None)</div><div>-            if len(info) == 1:</div><div>-                info.extend(devinfo[2].split(None))</div>

<div>-    return [info[0], info[5]]</div><div>+    while os.path.exists('/dev/loop%s' % n):</div><div>+        n += 1</div><div>+    os.mknod('/dev/loop%s' % n, 0660 | stat.S_IFBLK, os.makedev(7, n))</div>

<div>+    extra_loops.extend(['/dev/loop%s' % n])</div><div>+    return '/dev/loop%s' % n</div><div>+</div><div>+</div><div>+def loop_setup(path, ops=None):</div><div>+    """Make and associate a loop device with an image file or device."""</div>

<div>+</div><div>+    loop = add_loop()</div><div>+    args = ['/sbin/losetup', loop, path]</div><div>+    if ops is not None:</div><div>+        args += ops</div><div>+    call(args)</div><div>+    return loop</div>

<div> </div><div> </div><div> def main():</div><div>@@ -108,9 +163,8 @@ def main():</div><div>         return 1</div><div> </div><div>     try:</div><div>-        opts,args = getopt.getopt(sys.argv[1:], 'h', ['help',</div>

<div>-                                                       'chroot', 'persist',</div><div>-                                                       'mount-hacks'])</div><div>+        opts, args = getopt.getopt(sys.argv[1:], 'hy:', ['help', 'chroot',</div>

<div>+                                   'persist', 'mount-hacks', 'yumcache='])</div><div>     except getopt.GetoptError, e:</div><div>         usage(1)</div><div> </div><div>@@ -118,7 +172,9 @@ def main():</div>

<div>     chroot = False</div><div>     persist = False</div><div>     mount_hacks = False</div><div>-    for o,a in opts:</div><div>+    yumcache = None</div><div>+    rm_dir = None</div><div>+    for o, a in opts:</div>

<div>         if o in ('-h', '--help'):</div><div>             usage(0)</div><div>         elif o in ('--chroot', ):</div><div>@@ -128,49 +184,64 @@ def main():</div><div>         elif o in ('--persist', ):</div>

<div>             """Option used to run a command in a spawned process."""</div><div>             persist = True</div><div>+        elif o in ('-y', '--yumcache'):</div><div>
+            yumcache = a</div>
<div> </div><div>     if len(args) < 2:</div><div>         usage(1)</div><div> </div><div>     liveos = args[0]</div><div>     destmnt = args[1]</div><div>-</div><div>+    if not os.path.isdir(destmnt):</div><div>+        os.makedirs(destmnt)</div>

<div>+        rm_mnt = destmnt</div><div>+    if os.path.ismount(destmnt):</div><div>+        print """\n     Exiting...</div><div>+        %s is already in use as a mount point.\n""" % destmnt</div>

<div>+        ecode = 1</div><div>+        return</div><div>+        </div><div>     if not os.path.exists(liveos):</div><div>         print """\n     Exiting...</div><div>         %s is not a file, directory, or device.\n""" % liveos</div>

<div>         ecode = 1</div><div>         return</div><div> </div><div>-    if stat.S_ISBLK(os.stat(liveos).st_mode):</div><div>+    liveos_stat = os.stat(liveos).st_mode</div><div>+    if stat.S_ISBLK(liveos_stat):</div>

<div>         img_type = 'blk'</div><div>-        imgloop = None</div><div>         overlayloop = None</div><div>+        liveosmnt = tempfile.mkdtemp(prefix='livemnt-device-')</div><div>+    elif stat.S_ISDIR(liveos_stat):</div>

<div>+        img_type = 'dir'</div><div>+        liveosmnt = liveos</div><div>     else:</div><div>         img_type = 'iso'</div><div>-</div><div>-    if img_type is 'blk':</div><div>-        liveosmnt = tempfile.mkdtemp(prefix='livemnt-device-')</div>

<div>-    if img_type is 'iso':</div><div>         liveosmnt = tempfile.mkdtemp(prefix='livemnt-iso-')</div><div> </div><div>     liveosdir = os.path.join(liveosmnt, 'LiveOS')</div><div>     homemnt = None</div>

<div>     dm_cow = None</div><div>     mntlive = None</div><div>+    rmmntdir = None</div><div>+    rm_mnt = None</div><div> </div><div>     command = args[2:]</div><div>     verbose = not command</div><div> </div><div>+    dmsetup_cmd = ['/sbin/dmsetup']</div>

<div>+    if '--noudevsync' in rcall(['/sbin/dmsetup', '-h'])[1]:</div><div>+        dmsetup_cmd = ['/sbin/dmsetup', '--noudevrules', '--noudevsync']</div><div>+</div><div>
     squashmnt = tempfile.mkdtemp(prefix='livemnt-squash-')</div>
<div>     squashloop = None</div><div>     imgloop = None</div><div>-    losetup_args = ['/sbin/losetup', '-f', '--show']</div><div> </div><div>     try:</div><div>-        if img_type is 'blk':</div>

<div>+        if img_type == 'blk':</div><div>             call(['/bin/mount', liveos, liveosmnt])</div><div>-        elif img_type is 'iso':</div><div>-            liveosloop = rcall(losetup_args + [liveos]).rstrip()</div>

<div>+        elif img_type == 'iso':</div><div>+            liveosloop = loop_setup(liveos)</div><div>             call(['/bin/mount', '-o', 'ro', liveosloop, liveosmnt])</div><div> </div>

<div>         squash_img = os.path.join(liveosdir, 'squashfs.img')</div><div>@@ -182,57 +253,89 @@ def main():</div><div>                 ecode = 1</div><div>                 return</div><div>         else:</div>
<div>
-            squashloop = rcall(losetup_args + [squash_img]).rstrip()</div><div>+            squashloop = loop_setup(squash_img, ['-r'])</div><div>             call(['/bin/mount', '-o', 'ro', squashloop, squashmnt])</div>

<div>             ext3_img = os.path.join(squashmnt, 'LiveOS', 'ext3fs.img')</div><div> </div><div>-        if img_type is 'blk':</div><div>-            imgloop = rcall(losetup_args + [ext3_img]).rstrip()</div>

<div>-            imgsize = rcall(['/sbin/blockdev', '--getsz', imgloop]).rstrip()</div><div>-            files = os.listdir(liveosdir)</div><div>-            overlay = None</div><div>-            for f in files:</div>

<div>-                if f.find('overlay-') == 0:</div><div>-                    overlay = f</div><div>-                    break</div><div>-            overlayloop = rcall(['/sbin/losetup', '-f']).rstrip()</div>

<div>-            if overlay:</div><div>-                call(['/sbin/losetup', overlayloop, os.path.join(liveosdir,</div><div>-                                                                 overlay)])</div><div>

-                dm_cow = 'live-rw'</div><div>-            else:</div><div>-                overlay = tempfile.NamedTemporaryFile(dir='/dev/shm')</div><div>-                print "\npreparing temporary overlay..."</div>

<div>-                call(['/bin/dd', 'if=/dev/null', 'of=%s' % <a href="http://overlay.name">overlay.name</a>,</div><div>-                      'bs=1024', 'count=1', 'seek=%s' % (512 * 1024)])</div>

<div>-                call(['/sbin/losetup', overlayloop, <a href="http://overlay.name">overlay.name</a>])</div><div>-                dm_cow = 'live-ro'</div><div>-            call(['/sbin/dmsetup', '--noudevrules', '--noudevsync',</div>

<div>-                 'create', dm_cow, '--table=0 %s snapshot %s %s p 8' %</div><div>-                 (imgsize, imgloop, overlayloop)])</div><div>-            call(['/bin/mount', os.path.join('/dev/mapper', dm_cow), destmnt])</div>

<div>-            home_path = os.path.join(liveosdir, 'home.img')</div><div>-            if os.path.exists(home_path):</div><div>-                homemnt = os.path.join(destmnt, 'home')</div><div>-                homeloop = rcall(losetup_args + [home_path]).rstrip()</div>

<div>-                call(['/bin/mount', homeloop, homemnt])</div><div>-            mntlive = os.path.join(destmnt, 'mnt', 'live')</div><div>-            if not os.path.exists(mntlive):</div><div>

-                os.makedirs(mntlive)</div><div>-            call(['/bin/mount', '--bind', liveosmnt, mntlive])</div><div>-        elif img_type is 'iso':</div><div>-            imgloop = rcall(losetup_args + [ext3_img]).rstrip()</div>

<div>-            call(['/bin/mount', '-o', 'ro', imgloop, destmnt])</div><div>+        if img_type in ('blk', 'dir'):</div><div>+            imgloop = loop_setup(ext3_img)</div><div>

+        elif img_type == 'iso':</div><div>+            imgloop = loop_setup(ext3_img, ['-r'])</div><div>+        imgsize = rcall(['/sbin/blockdev', '--getsz', imgloop])[0].rstrip()</div><div>

+        overlay = None</div><div>+        for f in os.listdir(liveosdir):</div><div>+            if f.find('overlay-') == 0:</div><div>+                overlay = f</div><div>+                break</div><div>+        fd, dm_cow = tempfile.mkstemp(prefix='dm-')</div>

<div>+        os.close(fd)</div><div>+        os.unlink(dm_cow)</div><div>+        dm_cow = os.path.basename(dm_cow)</div><div>+        if overlay:</div><div>+            overlayloop = loop_setup(os.path.join(liveosdir, overlay))</div>

<div>+        else:</div><div>+            overlay = tempfile.NamedTemporaryFile(dir='/dev/shm')</div><div>+            print "\npreparing temporary overlay..."</div><div>+            call(['/bin/dd', 'if=/dev/null', 'of=%s' % <a href="http://overlay.name">overlay.name</a>,</div>

<div>+                  'bs=1024', 'count=1', 'seek=%s' % (512 * 1024)])</div><div>+            overlayloop = loop_setup(<a href="http://overlay.name">overlay.name</a>)</div><div>+        call(dmsetup_cmd + ['create', dm_cow,</div>

<div>+                            '--table=0 %s snapshot %s %s p 8' %</div><div>+                            (imgsize, imgloop, overlayloop)])</div><div>+        call(['/bin/mount', os.path.join('/dev/mapper', dm_cow), destmnt])</div>

<div>+        home_path = os.path.join(liveosdir, 'home.img')</div><div>+        if os.path.exists(home_path):</div><div>+            homemnt = os.path.join(destmnt, 'home')</div><div>+            homedev = loop_setup(home_path)</div>

<div>+            if get_fsvalue('value', 'TYPE', None,</div><div>+                           homedev) == 'crypto_LUKS':</div><div>+                call(['/sbin/cryptsetup', 'luksOpen', homedev, 'EncHome'])</div>

<div>+                homedev = os.path.join('/dev', 'mapper', 'EncHome')</div><div>+            if img_type in ('blk', 'dir'):</div><div>+                call(['/bin/mount', homedev, homemnt])</div>

<div>+            elif img_type == 'iso':</div><div>+                call(['/bin/mount', '-o', 'ro', homedev, homemnt])</div><div>+        mntlive = os.path.join(destmnt, 'mnt', 'live')</div>

<div>+        if not os.path.isdir(mntlive):</div><div>+            os.makedirs(mntlive)</div><div>+            rmmntdir = True</div><div>+        call(['/bin/mount', '--bind', liveosmnt, mntlive])</div><div>

 </div><div>         if mount_hacks:</div><div>-            subprocess.check_call(['mount', '-t', 'proc', 'proc', os.path.join(destmnt, 'proc')], stderr=sys.stderr)</div><div>-            subprocess.check_call(['mount', '-t', 'tmpfs', 'tmpfs', os.path.join(destmnt, 'var', 'run')], stderr=sys.stderr)</div>

<div>+            subprocess.check_call(['mount', '-t', 'proc', 'proc',</div><div>+                os.path.join(destmnt, 'proc')], stderr=sys.stderr)</div><div>+            subprocess.check_call(['mount', '-t', 'tmpfs', 'tmpfs',</div>

<div>+                os.path.join(destmnt, 'var', 'run')], stderr=sys.stderr)</div><div>+            subprocess.check_call(['mount', '-t', 'sysfs', 'sys',</div><div>+                os.path.join(destmnt, 'sys')], stderr=sys.stderr)</div>

<div>+            subprocess.check_call(['mount', '-t', 'devpts', 'devpts',</div><div>+                os.path.join(destmnt, 'dev', 'pts')], stderr=sys.stderr)</div><div>+            tmpfs = os.path.join(destmnt, 'dev', 'shm')</div>

<div>+            if not os.path.exists(tmpfs):</div><div>+                os.makedirs(tmpfs)</div><div>+            subprocess.check_call(['mount', '-t', 'tmpfs', 'tmpfs', tmpfs],</div><div>

+                                  stderr=sys.stderr)</div><div>+            run_dir = os.path.join(destmnt, 'run')</div><div>+            if not os.path.exists(run_dir):</div><div>+                os.makedirs(run_dir)</div>

<div>+            call(['/bin/mount', '-t', 'tmpfs', 'run', run_dir])</div><div>+            if yumcache is not None:</div><div>+                call(['/bin/mount', '--bind', yumcache,</div>

<div>+                      os.path.join(destmnt, 'var', 'cache', 'yum')])</div><div>+            else:</div><div>+                subprocess.check_call(['mount', '-t', 'tmpfs', 'varcacheyum',</div>

<div>+                    os.path.join(destmnt, 'var', 'cache', 'yum')],</div><div>+                    stderr=sys.stderr)</div><div>+            call(['/bin/mount', '--bind', '/etc/resolv.conf',</div>

<div>+                  os.path.join(destmnt, 'etc', 'resolv.conf')])</div><div>+            call(['/bin/mount', '--bind', '/dev/mapper',</div><div>+                  os.path.join(destmnt, 'dev', 'mapper')])</div>

<div> </div><div>         if len(command) > 0 and persist:</div><div>             args = []</div><div>             args.extend(command)</div><div>             live = ''</div><div>-            if img_type is 'blk':</div>

<div>+            if img_type in ('blk', 'dir'):</div><div>                 live = 'live-'</div><div>             print """Starting process with this command line:</div><div>             \r%s\n  %s is %smounted.""" % (' '.join(command), liveos, live)</div>

<div>@@ -244,50 +347,68 @@ def main():</div><div>             args.extend(command)</div><div>             ecode = subprocess.call(args, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr)</div><div>         elif chroot:</div>

<div>-            print "Starting subshell in chroot, press Ctrl-D to exit..."</div><div>+            print "Starting subshell in chroot, press Ctrl D to exit..."</div><div>             ecode = subprocess.call(['chroot', destmnt], stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr)</div>

<div>         else:</div><div>-            if dm_cow == 'live-ro':</div><div>+            if img_type in ('blk', 'dir') and dm_cow[:7] == 'live-ro':</div><div>                 status = ' with NO LiveOS persistence,'</div>

<div>             else:</div><div>                 status = ''</div><div>-            print "Entering subshell,%s press Ctrl-D to exit..." % status</div><div>+            print "Entering subshell,%s press Ctrl D to exit..." % status</div>

<div>             ecode = subprocess.call([os.environ['SHELL']], cwd=destmnt, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr)</div><div>     finally:</div><div>+        if verbose:</div><div>+            print """Cleaning up...</div>

<div>+            Please wait if large files were written."""</div><div>         call(['/bin/sync'])</div><div>         if not persist:</div><div>-            if verbose:</div><div>-                print """Cleaning up...</div>

<div>-                Please wait if large files were written."""</div><div>             if mount_hacks:</div><div>-                subprocess.call(['umount', os.path.join(destmnt, 'var', 'run')])</div>

<div>-                subprocess.call(['umount', os.path.join(destmnt, 'proc')])</div><div>+                call(['umount', os.path.join(destmnt, 'var', 'run')])</div><div>+                call(['umount', os.path.join(destmnt, 'proc')])</div>

<div>+                call(['umount', os.path.join(destmnt, 'dev', 'pts')])</div><div>+                call(['umount', os.path.join(destmnt, 'sys')])</div><div>+                call(['umount', os.path.join(destmnt, 'dev', 'shm')])</div>

<div>+                call(['umount', os.path.join(destmnt, 'run')])</div><div>+                call(['umount', os.path.join(destmnt, 'var', 'cache', 'yum')])</div><div>+                call(['umount', os.path.join(destmnt, 'etc', 'resolv.conf')])</div>

<div>+                call(['umount', os.path.join(destmnt, 'dev', 'mapper')])</div><div>+</div><div>             if homemnt:</div><div>                 call(['/bin/umount', homemnt])</div>

<div>-                call(['/sbin/losetup', '-d', homeloop])</div><div>-            if img_type is 'blk':</div><div>-                if mntlive:</div><div>-                    call(['/bin/umount', mntlive])</div>

<div>-                    os.rmdir(mntlive)</div><div>+                if homedev == os.path.join('/dev', 'mapper', 'EncHome'):</div><div>+                    call(dmsetup_cmd + ['remove', 'EncHome'])</div>

<div>+                else:</div><div>+                    call(['/sbin/losetup', '-d', homedev])</div><div>+            if mntlive:</div><div>+                call(['/bin/umount', mntlive])</div>
<div>
+                if rmmntdir:</div><div>+                    time.sleep(2)</div><div>+                    os.removedirs(mntlive)</div><div>             if os.path.ismount(destmnt):</div><div>+                os.chdir('..')</div>

<div>                 call(['/bin/umount', destmnt])</div><div>-            if img_type is 'blk':</div><div>-     m_cow:</div><div>-                    call(['/sbin/dmsetup', '--noudevrules', '--noudevsync',</div>

<div>-                          'remove', dm_cow])</div><div>-                if overlayloop:</div><div>-                    call(['/sbin/losetup', '-d', overlayloop])</div><div>+            if dm_cow:</div>

<div>+                #FIXME -f force option is sometimes needed when there are</div><div>+                # multiple invocations of liveimage-mount active.</div><div>+                call(dmsetup_cmd + ['remove', '-f', dm_cow])</div>

<div>+            if overlayloop:</div><div>+                call(['/sbin/losetup', '-d', overlayloop])</div><div>             if imgloop:</div><div>                 call(['/sbin/losetup', '-d', imgloop])</div>

<div>             if squashloop:</div><div>                 call(['/bin/umount', squashloop])</div><div>                 call(['/sbin/losetup', '-d', squashloop])</div><div>-            call(['/bin/umount', liveosmnt])</div>

<div>-            if not img_type is 'blk':</div><div>+            if os.path.ismount(liveosmnt):</div><div>+                call(['/bin/umount', liveosmnt])</div><div>+            if img_type is 'iso':</div>

<div>                 call(['/sbin/losetup', '-d', liveosloop])</div><div>             os.rmdir(squashmnt)</div><div>-            if not os.path.ismount(liveosmnt):</div><div>+            if not os.path.ismount(liveosmnt) and img_type != 'dir':</div>

<div>                 os.rmdir(liveosmnt)</div><div>+            time.sleep(2)</div><div>+            [call(['/bin/rm', '-f', loop]) for loop in extra_loops]</div><div>+            if rm_mnt is not None:</div>

<div>+                os.removedirs(rm_mnt)</div><div>             if verbose:</div><div>                 print "Cleanup complete"</div><div> </div></font><br><div class="gmail_quote">On Thu, Apr 14, 2011 at 12:10 PM, Frederick Grose <span dir="ltr"><<a href="mailto:fgrose@gmail.com">fgrose@gmail.com</a>></span> wrote:<br>

<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;"><span style="font-family:courier new,monospace">[Patch 3/5]<br>Author: Frederick Grose <<a href="mailto:fgrose@sugarlabs.org" target="_blank">fgrose@sugarlabs.org</a>><br>

Date:   Thu Apr 14 10:37:13 2011 -0400<br><br>    Support usage within a booted LiveOS instance.<br>
    <br>    Provide functions to add loop devices and remove then on exit, a<br>    LiveImageMountError(Exception) class for better error handling, and<br>    random device mapper names.<br><br>diff --git a/tools/liveimage-mount b/tools/liveimage-mount<br>


index 80d67d1..1ee5b16 100755<br>--- a/tools/liveimage-mount<br>+++ b/tools/liveimage-mount<br>@@ -23,10 +23,13 @@<br> import os<br> import sys<br> import stat<br>+import time<br> import getopt<br>+import random<br> import tempfile<br>


 import subprocess<br> <br>+extra_loops = []<br> <br> def usage(ecode):<br>     print """Usage:<br>@@ -40,6 +43,18 @@ def usage(ecode):<br>                     and [ARGS] = [arg1[ arg2[ ...]]]\n"""<br>


     sys.exit(ecode)<br> <br>+class LiveImageMountError(Exception):<br>+    """An exception base class for all liveimage-mount errors."""<br>+    def __init__(self, msg):<br>+        Exception.__init__(self, msg)<br>


+    def __str__(self):<br>+        try:<br>+            return str(self.message)<br>+        except UnicodeEncodeError:<br>+            return repr(self.message)<br>+<br>+    def __unicode__(self):<br>+        return unicode(self.message)<br>


 <br> def call(*popenargs, **kwargs):<br>     '''<br>@@ -62,43 +77,57 @@ def call(*popenargs, **kwargs):<br>     return rc<br> <br> <br>-def rcall(args, env=None):<br>+def rcall(args, stdin=None, stderr=True, env=None):<br>


+    out, err, p, environ = None, None, None, None<br>     if env:<br>         environ = os.environ.copy()<br>         environ.update(env)<br>-    else:<br>-        environ = None<br>     try:<br>-        p = subprocess.Popen(args, stdout=subprocess.PIPE,<br>


-                             stderr=subprocess.PIPE, env=environ)<br>-        out, err = p.communicate()<br>+        p = subprocess.Popen(args, stdin=subprocess.PIPE, env=environ,<br>+                             stdout=subprocess.PIPE, stderr=subprocess.PIPE)<br>


+        out, err = p.communicate(stdin)<br>     except OSError, e:<br>-        raise CreatorError(u"Failed to execute:\n'%s'\n'%s'" % (args, e))<br>+        raise LiveImageMountError(u"Failed executing:\n'%s'\n'%s'" % (args, e))<br>


     except:<br>-        raise CreatorError(u"""Failed to execute:\n'%s'<br>-            \renviron: '%s'\nstdout: '%s'\nstderr: '%s'\nreturncode: '%s'""" %<br>


-            (args, environ, out, err, p.returncode))<br>+        raise LiveImageMountError(u"""Failed to execute:\n'%s'<br>+                               \renviron: '%s'\nstdout: '%s'\nstderr: '%s'""" %<br>


+                                  (args, environ, out, err))<br>     else:<br>-        if p.returncode != 0:<br>-            raise CreatorError(u"""Error in call:\n'%s'\nenviron: '%s'<br>-                \rstdout: '%s'\nstderr: '%s'\nreturncode: '%s'""" %<br>


-                (args, environ, out, err, p.returncode))<br>+        if p.returncode != 0 and stderr:<br>+            raise LiveImageMountError(u'''Error in call:\n'%s'\nenviron: '%s'<br>+                            \rstdout: %s\nstderr: %sreturncode: %s''' %<br>


+                               (args, environ, out, err, p.returncode))<br>         return out<br> <br> <br>-def get_device_mountpoint(path):<br>-    """Return the device and mountpoint for a file or device path."""<br>


+def get_loop(path):<br>+    """Return the loop device for a given mount point path."""<br>+<br>+    loopdevice = None<br>+    for loop in rcall(['/sbin/losetup', '-a']).splitlines():<br>


+        info = loop.split()<br>+        if path == info[2].strip('()'):<br>+            loopdevice = info[0].rstrip(':')<br>+            break<br>+    return loopdevice<br>+<br>+<br>+def add_loop(n=8):<br>


+    """Add a loop device."""<br>+<br>+    while os.path.exists('/dev/loop%s' % n):<br>+        n += 1<br>+    os.mknod('/dev/loop%s' % n, 0660 | stat.S_IFBLK, os.makedev(7, n))<br>


+    extra_loops.extend(['/dev/loop%s' % n])<br>+    return '/dev/loop%s' % n<br> <br>-    info = list()<br>-    info[0:5] = [None] * 6<br>-    if os.path.exists(path):<br>-        st_mode = os.stat(path).st_mode<br>


-        if stat.S_ISBLK(st_mode) or os.path.ismount(path):<br>-            devinfo = rcall(['/bin/df', path]).splitlines()<br>-            info = devinfo[1].split(None)<br>-            if len(info) == 1:<br>-                info.extend(devinfo[2].split(None))<br>


-    return [info[0], info[5]]<br>+<br>+def loop_setup(path):<br>+    """Make and associate a loop device with an image file or device."""<br>+<br>+    loop = add_loop()<br>+    call(['/sbin/losetup', loop, path])<br>


+    return loop<br> <br> <br> def main():<br>@@ -145,18 +174,16 @@ def main():<br>         img_type = 'blk'<br>         imgloop = None<br>         overlayloop = None<br>+        liveosmnt = tempfile.mkdtemp(prefix='livemnt-device-')<br>


     else:<br>         img_type = 'iso'<br>-<br>-    if img_type is 'blk':<br>-        liveosmnt = tempfile.mkdtemp(prefix='livemnt-device-')<br>-    if img_type is 'iso':<br>         liveosmnt = tempfile.mkdtemp(prefix='livemnt-iso-')<br>


 <br>     liveosdir = os.path.join(liveosmnt, 'LiveOS')<br>     homemnt = None<br>     dm_cow = None<br>     mntlive = None<br>+    rmmntdir = None<br> <br>     command = args[2:]<br>     verbose = not command<br>


@@ -164,13 +191,12 @@ def main():<br>     squashmnt = tempfile.mkdtemp(prefix='livemnt-squash-')<br>     squashloop = None<br>     imgloop = None<br>-    losetup_args = ['/sbin/losetup', '-f', '--show']<br>


 <br>     try:<br>-        if img_type is 'blk':<br>+        if img_type == 'blk':<br>             call(['/bin/mount', liveos, liveosmnt])<br>-        elif img_type is 'iso':<br>-            liveosloop = rcall(losetup_args + [liveos]).rstrip()<br>


+        elif img_type == 'iso':<br>+            liveosloop = loop_setup(liveos)<br>             call(['/bin/mount', '-o', 'ro', liveosloop, liveosmnt])<br> <br>         squash_img = os.path.join(liveosdir, 'squashfs.img')<br>


@@ -182,12 +208,12 @@ def main():<br>                 ecode = 1<br>                 return<br>         else:<br>-            squashloop = rcall(losetup_args + [squash_img]).rstrip()<br>+            squashloop = loop_setup(squash_img)<br>


             call(['/bin/mount', '-o', 'ro', squashloop, squashmnt])<br>             ext3_img = os.path.join(squashmnt, 'LiveOS', 'ext3fs.img')<br> <br>-        if img_type is 'blk':<br>


-            imgloop = rcall(losetup_args + [ext3_img]).rstrip()<br>+        if img_type == 'blk':<br>+            imgloop = loop_setup(ext3_img)<br>             imgsize = rcall(['/sbin/blockdev', '--getsz', imgloop]).rstrip()<br>


             files = os.listdir(liveosdir)<br>             overlay = None<br>@@ -195,18 +221,19 @@ def main():<br>                 if f.find('overlay-') == 0:<br>                     overlay = f<br>                     break<br>


-            overlayloop = rcall(['/sbin/losetup', '-f']).rstrip()<br>             if overlay:<br>-                call(['/sbin/losetup', overlayloop, os.path.join(liveosdir,<br>-                                                                 overlay)])<br>


-                dm_cow = 'live-rw'<br>+                overlayloop = loop_setup(os.path.join(liveosdir, overlay))<br>+                dm_cow = "live-rw-%d-%d" % (os.getpid(),<br>+                                            random.randint(0, 2**16))<br>


+<br>             else:<br>                 overlay = tempfile.NamedTemporaryFile(dir='/dev/shm')<br>                 print "\npreparing temporary overlay..."<br>                 call(['/bin/dd', 'if=/dev/null', 'of=%s' % <a href="http://overlay.name" target="_blank">overlay.name</a>,<br>


                       'bs=1024', 'count=1', 'seek=%s' % (512 * 1024)])<br>-                call(['/sbin/losetup', overlayloop, <a href="http://overlay.name" target="_blank">overlay.name</a>])<br>

-                dm_cow = 'live-ro'<br>
+                overlayloop = loop_setup(<a href="http://overlay.name" target="_blank">overlay.name</a>)<br>+                dm_cow = "live-ro-%d-%d" % (os.getpid(),<br>+                                            random.randint(0, 2**16))<br>


             call(['/sbin/dmsetup', '--noudevrules', '--noudevsync',<br>                  'create', dm_cow, '--table=0 %s snapshot %s %s p 8' %<br>                  (imgsize, imgloop, overlayloop)])<br>


@@ -214,14 +241,15 @@ def main():<br>             home_path = os.path.join(liveosdir, 'home.img')<br>             if os.path.exists(home_path):<br>                 homemnt = os.path.join(destmnt, 'home')<br>


-                homeloop = rcall(losetup_args + [home_path]).rstrip()<br>+                homeloop = loop_setup(home_path)<br>                 call(['/bin/mount', homeloop, homemnt])<br>             mntlive = os.path.join(destmnt, 'mnt', 'live')<br>


             if not os.path.exists(mntlive):<br>                 os.makedirs(mntlive)<br>+                rmmntdir = True<br>             call(['/bin/mount', '--bind', liveosmnt, mntlive])<br>-        elif img_type is 'iso':<br>


-            imgloop = rcall(losetup_args + [ext3_img]).rstrip()<br>+        elif img_type == 'iso':<br>+            imgloop = loop_setup(ext3_img)<br>             call(['/bin/mount', '-o', 'ro', imgloop, destmnt])<br>


 <br>         if mount_hacks:<br>@@ -232,7 +260,7 @@ def main():<br>             args = []<br>             args.extend(command)<br>             live = ''<br>-            if img_type is 'blk':<br>+            if img_type == 'blk':<br>


                 live = 'live-'<br>             print """Starting process with this command line:<br>             \r%s\n  %s is %smounted.""" % (' '.join(command), liveos, live)<br>


@@ -247,7 +275,7 @@ def main():<br>             print "Starting subshell in chroot, press Ctrl-D to exit..."<br>             ecode = subprocess.call(['chroot', destmnt], stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr)<br>


         else:<br>-            if dm_cow == 'live-ro':<br>+            if img_type == 'blk' and dm_cow[:7] == 'live-ro':<br>                 status = ' with NO LiveOS persistence,'<br>             else:<br>


                 status = ''<br>@@ -268,11 +296,13 @@ def main():<br>             if img_type is 'blk':<br>                 if mntlive:<br>                     call(['/bin/umount', mntlive])<br>-                    os.rmdir(mntlive)<br>


+                    if rmmntdir:<br>+                        time.sleep(2)<br>+                        os.rmdir(mntlive)<br>             if os.path.ismount(destmnt):<br>                 call(['/bin/umount', destmnt])<br>


             if img_type is 'blk':<br>-     m_cow:<br>+                if dm_cow:<br>                     call(['/sbin/dmsetup', '--noudevrules', '--noudevsync',<br>                           'remove', dm_cow])<br>


                 if overlayloop:<br>@@ -288,6 +318,10 @@ def main():<br>             os.rmdir(squashmnt)<br>             if not os.path.ismount(liveosmnt):<br>                 os.rmdir(liveosmnt)<br>+            time.sleep(2)<br>


+            for loop in extra_loops:<br>+                if loop:<br>+                    call(['/bin/rm', '-f', loop])<br>             if verbose:<br>                 print "Cleanup complete"</span>
</blockquote></div><br>