<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>