[SoaS] [PATCH 3/5] Liveimage-mount within running LiveOS instance

Frederick Grose fgrose at gmail.com
Thu Apr 14 12:10:04 EDT 2011


[Patch 3/5]
Author: Frederick Grose <fgrose at sugarlabs.org>
Date:   Thu Apr 14 10:37:13 2011 -0400

    Support usage within a booted LiveOS instance.

    Provide functions to add loop devices and remove then on exit, a
    LiveImageMountError(Exception) class for better error handling, and
    random device mapper names.

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

+extra_loops = []

 def usage(ecode):
     print """Usage:
@@ -40,6 +43,18 @@ def usage(ecode):
                     and [ARGS] = [arg1[ arg2[ ...]]]\n"""
     sys.exit(ecode)

+class LiveImageMountError(Exception):
+    """An exception base class for all liveimage-mount errors."""
+    def __init__(self, msg):
+        Exception.__init__(self, msg)
+    def __str__(self):
+        try:
+            return str(self.message)
+        except UnicodeEncodeError:
+            return repr(self.message)
+
+    def __unicode__(self):
+        return unicode(self.message)

 def call(*popenargs, **kwargs):
     '''
@@ -62,43 +77,57 @@ def call(*popenargs, **kwargs):
     return rc


-def rcall(args, env=None):
+def rcall(args, stdin=None, stderr=True, env=None):
+    out, err, p, environ = None, None, None, None
     if env:
         environ = os.environ.copy()
         environ.update(env)
-    else:
-        environ = None
     try:
-        p = subprocess.Popen(args, stdout=subprocess.PIPE,
-                             stderr=subprocess.PIPE, env=environ)
-        out, err = p.communicate()
+        p = subprocess.Popen(args, stdin=subprocess.PIPE, env=environ,
+                             stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
+        out, err = p.communicate(stdin)
     except OSError, e:
-        raise CreatorError(u"Failed to execute:\n'%s'\n'%s'" % (args, e))
+        raise LiveImageMountError(u"Failed executing:\n'%s'\n'%s'" % (args,
e))
     except:
-        raise CreatorError(u"""Failed to execute:\n'%s'
-            \renviron: '%s'\nstdout: '%s'\nstderr: '%s'\nreturncode:
'%s'""" %
-            (args, environ, out, err, p.returncode))
+        raise LiveImageMountError(u"""Failed to execute:\n'%s'
+                               \renviron: '%s'\nstdout: '%s'\nstderr:
'%s'""" %
+                                  (args, environ, out, err))
     else:
-        if p.returncode != 0:
-            raise CreatorError(u"""Error in call:\n'%s'\nenviron: '%s'
-                \rstdout: '%s'\nstderr: '%s'\nreturncode: '%s'""" %
-                (args, environ, out, err, p.returncode))
+        if p.returncode != 0 and stderr:
+            raise LiveImageMountError(u'''Error in call:\n'%s'\nenviron:
'%s'
+                            \rstdout: %s\nstderr: %sreturncode: %s''' %
+                               (args, environ, out, err, p.returncode))
         return out


-def get_device_mountpoint(path):
-    """Return the device and mountpoint for a file or device path."""
+def get_loop(path):
+    """Return the loop device for a given mount point path."""
+
+    loopdevice = None
+    for loop in rcall(['/sbin/losetup', '-a']).splitlines():
+        info = loop.split()
+        if path == info[2].strip('()'):
+            loopdevice = info[0].rstrip(':')
+            break
+    return loopdevice
+
+
+def add_loop(n=8):
+    """Add a loop device."""
+
+    while os.path.exists('/dev/loop%s' % n):
+        n += 1
+    os.mknod('/dev/loop%s' % n, 0660 | stat.S_IFBLK, os.makedev(7, n))
+    extra_loops.extend(['/dev/loop%s' % n])
+    return '/dev/loop%s' % n

-    info = list()
-    info[0:5] = [None] * 6
-    if os.path.exists(path):
-        st_mode = os.stat(path).st_mode
-        if stat.S_ISBLK(st_mode) or os.path.ismount(path):
-            devinfo = rcall(['/bin/df', path]).splitlines()
-            info = devinfo[1].split(None)
-            if len(info) == 1:
-                info.extend(devinfo[2].split(None))
-    return [info[0], info[5]]
+
+def loop_setup(path):
+    """Make and associate a loop device with an image file or device."""
+
+    loop = add_loop()
+    call(['/sbin/losetup', loop, path])
+    return loop


 def main():
@@ -145,18 +174,16 @@ def main():
         img_type = 'blk'
         imgloop = None
         overlayloop = None
+        liveosmnt = tempfile.mkdtemp(prefix='livemnt-device-')
     else:
         img_type = 'iso'
-
-    if img_type is 'blk':
-        liveosmnt = tempfile.mkdtemp(prefix='livemnt-device-')
-    if img_type is 'iso':
         liveosmnt = tempfile.mkdtemp(prefix='livemnt-iso-')

     liveosdir = os.path.join(liveosmnt, 'LiveOS')
     homemnt = None
     dm_cow = None
     mntlive = None
+    rmmntdir = None

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

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

         squash_img = os.path.join(liveosdir, 'squashfs.img')
@@ -182,12 +208,12 @@ def main():
                 ecode = 1
                 return
         else:
-            squashloop = rcall(losetup_args + [squash_img]).rstrip()
+            squashloop = loop_setup(squash_img)
             call(['/bin/mount', '-o', 'ro', squashloop, squashmnt])
             ext3_img = os.path.join(squashmnt, 'LiveOS', 'ext3fs.img')

-        if img_type is 'blk':
-            imgloop = rcall(losetup_args + [ext3_img]).rstrip()
+        if img_type == 'blk':
+            imgloop = loop_setup(ext3_img)
             imgsize = rcall(['/sbin/blockdev', '--getsz',
imgloop]).rstrip()
             files = os.listdir(liveosdir)
             overlay = None
@@ -195,18 +221,19 @@ def main():
                 if f.find('overlay-') == 0:
                     overlay = f
                     break
-            overlayloop = rcall(['/sbin/losetup', '-f']).rstrip()
             if overlay:
-                call(['/sbin/losetup', overlayloop, os.path.join(liveosdir,
-                                                                 overlay)])
-                dm_cow = 'live-rw'
+                overlayloop = loop_setup(os.path.join(liveosdir, overlay))
+                dm_cow = "live-rw-%d-%d" % (os.getpid(),
+                                            random.randint(0, 2**16))
+
             else:
                 overlay = tempfile.NamedTemporaryFile(dir='/dev/shm')
                 print "\npreparing temporary overlay..."
                 call(['/bin/dd', 'if=/dev/null', 'of=%s' % overlay.name,
                       'bs=1024', 'count=1', 'seek=%s' % (512 * 1024)])
-                call(['/sbin/losetup', overlayloop, overlay.name])
-                dm_cow = 'live-ro'
+                overlayloop = loop_setup(overlay.name)
+                dm_cow = "live-ro-%d-%d" % (os.getpid(),
+                                            random.randint(0, 2**16))
             call(['/sbin/dmsetup', '--noudevrules', '--noudevsync',
                  'create', dm_cow, '--table=0 %s snapshot %s %s p 8' %
                  (imgsize, imgloop, overlayloop)])
@@ -214,14 +241,15 @@ def main():
             home_path = os.path.join(liveosdir, 'home.img')
             if os.path.exists(home_path):
                 homemnt = os.path.join(destmnt, 'home')
-                homeloop = rcall(losetup_args + [home_path]).rstrip()
+                homeloop = loop_setup(home_path)
                 call(['/bin/mount', homeloop, homemnt])
             mntlive = os.path.join(destmnt, 'mnt', 'live')
             if not os.path.exists(mntlive):
                 os.makedirs(mntlive)
+                rmmntdir = True
             call(['/bin/mount', '--bind', liveosmnt, mntlive])
-        elif img_type is 'iso':
-            imgloop = rcall(losetup_args + [ext3_img]).rstrip()
+        elif img_type == 'iso':
+            imgloop = loop_setup(ext3_img)
             call(['/bin/mount', '-o', 'ro', imgloop, destmnt])

         if mount_hacks:
@@ -232,7 +260,7 @@ def main():
             args = []
             args.extend(command)
             live = ''
-            if img_type is 'blk':
+            if img_type == 'blk':
                 live = 'live-'
             print """Starting process with this command line:
             \r%s\n  %s is %smounted.""" % (' '.join(command), liveos, live)
@@ -247,7 +275,7 @@ def main():
             print "Starting subshell in chroot, press Ctrl-D to exit..."
             ecode = subprocess.call(['chroot', destmnt], stdin=sys.stdin,
stdout=sys.stdout, stderr=sys.stderr)
         else:
-            if dm_cow == 'live-ro':
+            if img_type == 'blk' and dm_cow[:7] == 'live-ro':
                 status = ' with NO LiveOS persistence,'
             else:
                 status = ''
@@ -268,11 +296,13 @@ def main():
             if img_type is 'blk':
                 if mntlive:
                     call(['/bin/umount', mntlive])
-                    os.rmdir(mntlive)
+                    if rmmntdir:
+                        time.sleep(2)
+                        os.rmdir(mntlive)
             if os.path.ismount(destmnt):
                 call(['/bin/umount', destmnt])
             if img_type is 'blk':
-     m_cow:
+                if dm_cow:
                     call(['/sbin/dmsetup', '--noudevrules', '--noudevsync',
                           'remove', dm_cow])
                 if overlayloop:
@@ -288,6 +318,10 @@ def main():
             os.rmdir(squashmnt)
             if not os.path.ismount(liveosmnt):
                 os.rmdir(liveosmnt)
+            time.sleep(2)
+            for loop in extra_loops:
+                if loop:
+                    call(['/bin/rm', '-f', loop])
             if verbose:
                 print "Cleanup complete"
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.sugarlabs.org/archive/soas/attachments/20110414/aac0b854/attachment.html>


More information about the SoaS mailing list