<span style="font-family: courier new,monospace;">[Patch 3/5]<br>Author: Frederick Grose <<a href="mailto:fgrose@sugarlabs.org">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">overlay.name</a>,<br>

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

+                overlayloop = loop_setup(<a href="http://overlay.name">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>