diff options
-rw-r--r-- | scripts/lib/mic/utils/fs_related.py | 930 |
1 files changed, 0 insertions, 930 deletions
diff --git a/scripts/lib/mic/utils/fs_related.py b/scripts/lib/mic/utils/fs_related.py index 182171ffd3..e6e362d0ba 100644 --- a/scripts/lib/mic/utils/fs_related.py +++ b/scripts/lib/mic/utils/fs_related.py @@ -31,19 +31,6 @@ from mic.utils import runner from mic.utils.errors import * from mic.utils.oe.misc import * -def find_binary_inchroot(binary, chroot): - paths = ["/usr/sbin", - "/usr/bin", - "/sbin", - "/bin" - ] - - for path in paths: - bin_path = "%s/%s" % (path, binary) - if os.path.exists("%s/%s" % (chroot, bin_path)): - return bin_path - return None - def find_binary_path(binary): if os.environ.has_key("PATH"): paths = os.environ["PATH"].split(":") @@ -72,176 +59,6 @@ def makedirs(dirname): if err.errno != errno.EEXIST: raise -def mksquashfs(in_img, out_img): - fullpathmksquashfs = find_binary_path("mksquashfs") - args = [fullpathmksquashfs, in_img, out_img] - - if not sys.stdout.isatty(): - args.append("-no-progress") - - ret = runner.show(args) - if ret != 0: - raise SquashfsError("'%s' exited with error (%d)" % (' '.join(args), ret)) - -def resize2fs(fs, size): - resize2fs = find_binary_path("resize2fs") - if size == 0: - # it means to minimalize it - return runner.show([resize2fs, '-M', fs]) - else: - return runner.show([resize2fs, fs, "%sK" % (size / 1024,)]) - -def my_fuser(fp): - fuser = find_binary_path("fuser") - if not os.path.exists(fp): - return False - - rc = runner.quiet([fuser, "-s", fp]) - if rc == 0: - for pid in runner.outs([fuser, fp]).split(): - fd = open("/proc/%s/cmdline" % pid, "r") - cmdline = fd.read() - fd.close() - if cmdline[:-1] == "/bin/bash": - return True - - # not found - return False - -class BindChrootMount: - """Represents a bind mount of a directory into a chroot.""" - def __init__(self, src, chroot, dest = None, option = None): - self.root = os.path.abspath(os.path.expanduser(chroot)) - self.option = option - - self.orig_src = self.src = src - if os.path.islink(src): - self.src = os.readlink(src) - if not self.src.startswith('/'): - self.src = os.path.abspath(os.path.join(os.path.dirname(src), - self.src)) - - if not dest: - dest = self.src - self.dest = os.path.join(self.root, dest.lstrip('/')) - - self.mounted = False - self.mountcmd = find_binary_path("mount") - self.umountcmd = find_binary_path("umount") - - def ismounted(self): - with open('/proc/mounts') as f: - for line in f: - if line.split()[1] == os.path.abspath(self.dest): - return True - - return False - - def has_chroot_instance(self): - lock = os.path.join(self.root, ".chroot.lock") - return my_fuser(lock) - - def mount(self): - if self.mounted or self.ismounted(): - return - - makedirs(self.dest) - rc = runner.show([self.mountcmd, "--bind", self.src, self.dest]) - if rc != 0: - raise MountError("Bind-mounting '%s' to '%s' failed" % - (self.src, self.dest)) - if self.option: - rc = runner.show([self.mountcmd, "--bind", "-o", "remount,%s" % self.option, self.dest]) - if rc != 0: - raise MountError("Bind-remounting '%s' failed" % self.dest) - - self.mounted = True - if os.path.islink(self.orig_src): - dest = os.path.join(self.root, self.orig_src.lstrip('/')) - if not os.path.exists(dest): - os.symlink(self.src, dest) - - def unmount(self): - if self.has_chroot_instance(): - return - - if self.ismounted(): - runner.show([self.umountcmd, "-l", self.dest]) - self.mounted = False - -class LoopbackMount: - """LoopbackMount compatibility layer for old API""" - def __init__(self, lofile, mountdir, fstype = None): - self.diskmount = DiskMount(LoopbackDisk(lofile,size = 0),mountdir,fstype,rmmountdir = True) - self.losetup = False - self.losetupcmd = find_binary_path("losetup") - - def cleanup(self): - self.diskmount.cleanup() - - def unmount(self): - self.diskmount.unmount() - - def lounsetup(self): - if self.losetup: - runner.show([self.losetupcmd, "-d", self.loopdev]) - self.losetup = False - self.loopdev = None - - def loopsetup(self): - if self.losetup: - return - - self.loopdev = get_loop_device(self.losetupcmd, self.lofile) - self.losetup = True - - def mount(self): - self.diskmount.mount() - -class SparseLoopbackMount(LoopbackMount): - """SparseLoopbackMount compatibility layer for old API""" - def __init__(self, lofile, mountdir, size, fstype = None): - self.diskmount = DiskMount(SparseLoopbackDisk(lofile,size),mountdir,fstype,rmmountdir = True) - - def expand(self, create = False, size = None): - self.diskmount.disk.expand(create, size) - - def truncate(self, size = None): - self.diskmount.disk.truncate(size) - - def create(self): - self.diskmount.disk.create() - -class SparseExtLoopbackMount(SparseLoopbackMount): - """SparseExtLoopbackMount compatibility layer for old API""" - def __init__(self, lofile, mountdir, size, fstype, blocksize, fslabel): - self.diskmount = ExtDiskMount(SparseLoopbackDisk(lofile,size), mountdir, fstype, blocksize, fslabel, rmmountdir = True) - - - def __format_filesystem(self): - self.diskmount.__format_filesystem() - - def create(self): - self.diskmount.disk.create() - - def resize(self, size = None): - return self.diskmount.__resize_filesystem(size) - - def mount(self): - self.diskmount.mount() - - def __fsck(self): - self.extdiskmount.__fsck() - - def __get_size_from_filesystem(self): - return self.diskmount.__get_size_from_filesystem() - - def __resize_to_minimal(self): - return self.diskmount.__resize_to_minimal() - - def resparse(self, size = None): - return self.diskmount.resparse(size) - class Disk: """Generic base object for a disk @@ -270,20 +87,6 @@ class Disk: size = property(get_size) -class RawDisk(Disk): - """A Disk backed by a block device. - Note that create() is a no-op. - """ - def __init__(self, size, device): - Disk.__init__(self, size, device) - - def fixed(self): - return True - - def exists(self): - return True - - class DiskImage(Disk): """ A Disk backed by a file. @@ -311,76 +114,6 @@ class DiskImage(Disk): self.device = self.image_file -class LoopbackDisk(Disk): - """A Disk backed by a file via the loop module.""" - def __init__(self, lofile, size): - Disk.__init__(self, size) - self.lofile = lofile - self.losetupcmd = find_binary_path("losetup") - - def fixed(self): - return False - - def exists(self): - return os.path.exists(self.lofile) - - def create(self): - if self.device is not None: - return - - self.device = get_loop_device(self.losetupcmd, self.lofile) - - def cleanup(self): - if self.device is None: - return - msger.debug("Losetup remove %s" % self.device) - rc = runner.show([self.losetupcmd, "-d", self.device]) - self.device = None - -class SparseLoopbackDisk(LoopbackDisk): - """A Disk backed by a sparse file via the loop module.""" - def __init__(self, lofile, size): - LoopbackDisk.__init__(self, lofile, size) - - def expand(self, create = False, size = None): - flags = os.O_WRONLY - if create: - flags |= os.O_CREAT - if not os.path.exists(self.lofile): - makedirs(os.path.dirname(self.lofile)) - - if size is None: - size = self.size - - msger.debug("Extending sparse file %s to %d" % (self.lofile, size)) - if create: - fd = os.open(self.lofile, flags, 0644) - else: - fd = os.open(self.lofile, flags) - - if size <= 0: - size = 1 - try: - os.ftruncate(fd, size) - except: - # may be limited by 2G in 32bit env - os.ftruncate(fd, 2**31L) - - os.close(fd) - - def truncate(self, size = None): - if size is None: - size = self.size - - msger.debug("Truncating sparse file %s to %d" % (self.lofile, size)) - fd = os.open(self.lofile, os.O_WRONLY) - os.ftruncate(fd, size) - os.close(fd) - - def create(self): - self.expand(create = True) - LoopbackDisk.create(self) - class Mount: """A generic base class to deal with mounting things.""" def __init__(self, mountdir): @@ -395,666 +128,3 @@ class Mount: def unmount(self): pass -class DiskMount(Mount): - """A Mount object that handles mounting of a Disk.""" - def __init__(self, disk, mountdir, fstype = None, rmmountdir = True): - Mount.__init__(self, mountdir) - - self.disk = disk - self.fstype = fstype - self.rmmountdir = rmmountdir - - self.mounted = False - self.rmdir = False - if fstype: - self.mkfscmd = find_binary_path("mkfs." + self.fstype) - else: - self.mkfscmd = None - self.mountcmd = find_binary_path("mount") - self.umountcmd = find_binary_path("umount") - - def cleanup(self): - Mount.cleanup(self) - self.disk.cleanup() - - def unmount(self): - if self.mounted: - msger.debug("Unmounting directory %s" % self.mountdir) - runner.quiet('sync') # sync the data on this mount point - rc = runner.show([self.umountcmd, "-l", self.mountdir]) - if rc == 0: - self.mounted = False - else: - raise MountError("Failed to umount %s" % self.mountdir) - if self.rmdir and not self.mounted: - try: - os.rmdir(self.mountdir) - except OSError, e: - pass - self.rmdir = False - - - def __create(self): - self.disk.create() - - - def mount(self, options = None): - if self.mounted: - return - - if not os.path.isdir(self.mountdir): - msger.debug("Creating mount point %s" % self.mountdir) - os.makedirs(self.mountdir) - self.rmdir = self.rmmountdir - - self.__create() - - msger.debug("Mounting %s at %s" % (self.disk.device, self.mountdir)) - if options: - args = [ self.mountcmd, "-o", options, self.disk.device, self.mountdir ] - else: - args = [ self.mountcmd, self.disk.device, self.mountdir ] - if self.fstype: - args.extend(["-t", self.fstype]) - - rc = runner.show(args) - if rc != 0: - raise MountError("Failed to mount '%s' to '%s' with command '%s'. Retval: %s" % - (self.disk.device, self.mountdir, " ".join(args), rc)) - - self.mounted = True - -class ExtDiskMount(DiskMount): - """A DiskMount object that is able to format/resize ext[23] filesystems.""" - def __init__(self, disk, mountdir, fstype, blocksize, fslabel, rmmountdir=True, skipformat = False, fsopts = None): - DiskMount.__init__(self, disk, mountdir, fstype, rmmountdir) - self.blocksize = blocksize - self.fslabel = fslabel.replace("/", "") - self.uuid = str(uuid.uuid4()) - self.skipformat = skipformat - self.fsopts = fsopts - self.extopts = None - self.dumpe2fs = find_binary_path("dumpe2fs") - self.tune2fs = find_binary_path("tune2fs") - - def __parse_field(self, output, field): - for line in output.split("\n"): - if line.startswith(field + ":"): - return line[len(field) + 1:].strip() - - raise KeyError("Failed to find field '%s' in output" % field) - - def __format_filesystem(self): - if self.skipformat: - msger.debug("Skip filesystem format.") - return - - msger.verbose("Formating %s filesystem on %s" % (self.fstype, self.disk.device)) - cmdlist = [self.mkfscmd, "-F", "-L", self.fslabel, "-m", "1", "-b", - str(self.blocksize), "-U", self.uuid] - if self.extopts: - cmdlist.extend(self.extopts.split()) - cmdlist.extend([self.disk.device]) - - rc, errout = runner.runtool(cmdlist, catch=2) - if rc != 0: - raise MountError("Error creating %s filesystem on disk %s:\n%s" % - (self.fstype, self.disk.device, errout)) - - if not self.extopts: - msger.debug("Tuning filesystem on %s" % self.disk.device) - runner.show([self.tune2fs, "-c0", "-i0", "-Odir_index", "-ouser_xattr,acl", self.disk.device]) - - def __resize_filesystem(self, size = None): - current_size = os.stat(self.disk.lofile)[stat.ST_SIZE] - - if size is None: - size = self.disk.size - - if size == current_size: - return - - if size > current_size: - self.disk.expand(size) - - self.__fsck() - - resize2fs(self.disk.lofile, size) - return size - - def __create(self): - resize = False - if not self.disk.fixed() and self.disk.exists(): - resize = True - - self.disk.create() - - if resize: - self.__resize_filesystem() - else: - self.__format_filesystem() - - def mount(self, options = None): - self.__create() - DiskMount.mount(self, options) - - def __fsck(self): - msger.info("Checking filesystem %s" % self.disk.lofile) - runner.quiet(["/sbin/e2fsck", "-f", "-y", self.disk.lofile]) - - def __get_size_from_filesystem(self): - return int(self.__parse_field(runner.outs([self.dumpe2fs, '-h', self.disk.lofile]), - "Block count")) * self.blocksize - - def __resize_to_minimal(self): - self.__fsck() - - # - # Use a binary search to find the minimal size - # we can resize the image to - # - bot = 0 - top = self.__get_size_from_filesystem() - while top != (bot + 1): - t = bot + ((top - bot) / 2) - - if not resize2fs(self.disk.lofile, t): - top = t - else: - bot = t - return top - - def resparse(self, size = None): - self.cleanup() - if size == 0: - minsize = 0 - else: - minsize = self.__resize_to_minimal() - self.disk.truncate(minsize) - - self.__resize_filesystem(size) - return minsize - -class VfatDiskMount(DiskMount): - """A DiskMount object that is able to format vfat/msdos filesystems.""" - def __init__(self, disk, mountdir, fstype, blocksize, fslabel, rmmountdir=True, skipformat = False, fsopts = None): - DiskMount.__init__(self, disk, mountdir, fstype, rmmountdir) - self.blocksize = blocksize - self.fslabel = fslabel.replace("/", "") - rand1 = random.randint(0, 2**16 - 1) - rand2 = random.randint(0, 2**16 - 1) - self.uuid = "%04X-%04X" % (rand1, rand2) - self.skipformat = skipformat - self.fsopts = fsopts - self.fsckcmd = find_binary_path("fsck." + self.fstype) - - def __format_filesystem(self): - if self.skipformat: - msger.debug("Skip filesystem format.") - return - - msger.verbose("Formating %s filesystem on %s" % (self.fstype, self.disk.device)) - rc = runner.show([self.mkfscmd, "-n", self.fslabel, - "-i", self.uuid.replace("-", ""), self.disk.device]) - if rc != 0: - raise MountError("Error creating %s filesystem on disk %s" % (self.fstype,self.disk.device)) - - msger.verbose("Tuning filesystem on %s" % self.disk.device) - - def __resize_filesystem(self, size = None): - current_size = os.stat(self.disk.lofile)[stat.ST_SIZE] - - if size is None: - size = self.disk.size - - if size == current_size: - return - - if size > current_size: - self.disk.expand(size) - - self.__fsck() - - #resize2fs(self.disk.lofile, size) - return size - - def __create(self): - resize = False - if not self.disk.fixed() and self.disk.exists(): - resize = True - - self.disk.create() - - if resize: - self.__resize_filesystem() - else: - self.__format_filesystem() - - def mount(self, options = None): - self.__create() - DiskMount.mount(self, options) - - def __fsck(self): - msger.debug("Checking filesystem %s" % self.disk.lofile) - runner.show([self.fsckcmd, "-y", self.disk.lofile]) - - def __get_size_from_filesystem(self): - return self.disk.size - - def __resize_to_minimal(self): - self.__fsck() - - # - # Use a binary search to find the minimal size - # we can resize the image to - # - bot = 0 - top = self.__get_size_from_filesystem() - return top - - def resparse(self, size = None): - self.cleanup() - minsize = self.__resize_to_minimal() - self.disk.truncate(minsize) - self.__resize_filesystem(size) - return minsize - -class BtrfsDiskMount(DiskMount): - """A DiskMount object that is able to format/resize btrfs filesystems.""" - def __init__(self, disk, mountdir, fstype, blocksize, fslabel, rmmountdir=True, skipformat = False, fsopts = None): - self.__check_btrfs() - DiskMount.__init__(self, disk, mountdir, fstype, rmmountdir) - self.blocksize = blocksize - self.fslabel = fslabel.replace("/", "") - self.uuid = None - self.skipformat = skipformat - self.fsopts = fsopts - self.blkidcmd = find_binary_path("blkid") - self.btrfsckcmd = find_binary_path("btrfsck") - - def __check_btrfs(self): - found = False - """ Need to load btrfs module to mount it """ - load_module("btrfs") - for line in open("/proc/filesystems").xreadlines(): - if line.find("btrfs") > -1: - found = True - break - if not found: - raise MountError("Your system can't mount btrfs filesystem, please make sure your kernel has btrfs support and the module btrfs.ko has been loaded.") - - # disable selinux, selinux will block write - if os.path.exists("/usr/sbin/setenforce"): - runner.show(["/usr/sbin/setenforce", "0"]) - - def __parse_field(self, output, field): - for line in output.split(" "): - if line.startswith(field + "="): - return line[len(field) + 1:].strip().replace("\"", "") - - raise KeyError("Failed to find field '%s' in output" % field) - - def __format_filesystem(self): - if self.skipformat: - msger.debug("Skip filesystem format.") - return - - msger.verbose("Formating %s filesystem on %s" % (self.fstype, self.disk.device)) - rc = runner.show([self.mkfscmd, "-L", self.fslabel, self.disk.device]) - if rc != 0: - raise MountError("Error creating %s filesystem on disk %s" % (self.fstype,self.disk.device)) - - self.uuid = self.__parse_field(runner.outs([self.blkidcmd, self.disk.device]), "UUID") - - def __resize_filesystem(self, size = None): - current_size = os.stat(self.disk.lofile)[stat.ST_SIZE] - - if size is None: - size = self.disk.size - - if size == current_size: - return - - if size > current_size: - self.disk.expand(size) - - self.__fsck() - return size - - def __create(self): - resize = False - if not self.disk.fixed() and self.disk.exists(): - resize = True - - self.disk.create() - - if resize: - self.__resize_filesystem() - else: - self.__format_filesystem() - - def mount(self, options = None): - self.__create() - DiskMount.mount(self, options) - - def __fsck(self): - msger.debug("Checking filesystem %s" % self.disk.lofile) - runner.quiet([self.btrfsckcmd, self.disk.lofile]) - - def __get_size_from_filesystem(self): - return self.disk.size - - def __resize_to_minimal(self): - self.__fsck() - - return self.__get_size_from_filesystem() - - def resparse(self, size = None): - self.cleanup() - minsize = self.__resize_to_minimal() - self.disk.truncate(minsize) - self.__resize_filesystem(size) - return minsize - -class DeviceMapperSnapshot(object): - def __init__(self, imgloop, cowloop): - self.imgloop = imgloop - self.cowloop = cowloop - - self.__created = False - self.__name = None - self.dmsetupcmd = find_binary_path("dmsetup") - - """Load dm_snapshot if it isn't loaded""" - load_module("dm_snapshot") - - def get_path(self): - if self.__name is None: - return None - return os.path.join("/dev/mapper", self.__name) - path = property(get_path) - - def create(self): - if self.__created: - return - - self.imgloop.create() - self.cowloop.create() - - self.__name = "imgcreate-%d-%d" % (os.getpid(), - random.randint(0, 2**16)) - - size = os.stat(self.imgloop.lofile)[stat.ST_SIZE] - - table = "0 %d snapshot %s %s p 8" % (size / 512, - self.imgloop.device, - self.cowloop.device) - - args = [self.dmsetupcmd, "create", self.__name, "--table", table] - if runner.show(args) != 0: - self.cowloop.cleanup() - self.imgloop.cleanup() - raise SnapshotError("Could not create snapshot device using: " + ' '.join(args)) - - self.__created = True - - def remove(self, ignore_errors = False): - if not self.__created: - return - - time.sleep(2) - rc = runner.show([self.dmsetupcmd, "remove", self.__name]) - if not ignore_errors and rc != 0: - raise SnapshotError("Could not remove snapshot device") - - self.__name = None - self.__created = False - - self.cowloop.cleanup() - self.imgloop.cleanup() - - def get_cow_used(self): - if not self.__created: - return 0 - - # - # dmsetup status on a snapshot returns e.g. - # "0 8388608 snapshot 416/1048576" - # or, more generally: - # "A B snapshot C/D" - # where C is the number of 512 byte sectors in use - # - out = runner.outs([self.dmsetupcmd, "status", self.__name]) - try: - return int((out.split()[3]).split('/')[0]) * 512 - except ValueError: - raise SnapshotError("Failed to parse dmsetup status: " + out) - -def create_image_minimizer(path, image, minimal_size): - """ - Builds a copy-on-write image which can be used to - create a device-mapper snapshot of an image where - the image's filesystem is as small as possible - - The steps taken are: - 1) Create a sparse COW - 2) Loopback mount the image and the COW - 3) Create a device-mapper snapshot of the image - using the COW - 4) Resize the filesystem to the minimal size - 5) Determine the amount of space used in the COW - 6) Restroy the device-mapper snapshot - 7) Truncate the COW, removing unused space - 8) Create a squashfs of the COW - """ - imgloop = LoopbackDisk(image, None) # Passing bogus size - doesn't matter - - cowloop = SparseLoopbackDisk(os.path.join(os.path.dirname(path), "osmin"), - 64L * 1024L * 1024L) - - snapshot = DeviceMapperSnapshot(imgloop, cowloop) - - try: - snapshot.create() - - resize2fs(snapshot.path, minimal_size) - - cow_used = snapshot.get_cow_used() - finally: - snapshot.remove(ignore_errors = (not sys.exc_info()[0] is None)) - - cowloop.truncate(cow_used) - - mksquashfs(cowloop.lofile, path) - - os.unlink(cowloop.lofile) - -def load_module(module): - found = False - for line in open('/proc/modules').xreadlines(): - if line.startswith("%s " % module): - found = True - break - if not found: - msger.info("Loading %s..." % module) - runner.quiet(['modprobe', module]) - -class LoopDevice(object): - def __init__(self, loopid=None): - self.device = None - self.loopid = loopid - self.created = False - self.kpartxcmd = find_binary_path("kpartx") - self.losetupcmd = find_binary_path("losetup") - - def register(self, device): - self.device = device - self.loopid = None - self.created = True - - def reg_atexit(self): - import atexit - atexit.register(self.close) - - def _genloopid(self): - import glob - if not glob.glob("/dev/loop[0-9]*"): - return 10 - - fint = lambda x: x[9:].isdigit() and int(x[9:]) or 0 - maxid = 1 + max(filter(lambda x: x<100, - map(fint, glob.glob("/dev/loop[0-9]*")))) - if maxid < 10: maxid = 10 - if maxid >= 100: raise - return maxid - - def _kpseek(self, device): - rc, out = runner.runtool([self.kpartxcmd, '-l', '-v', device]) - if rc != 0: - raise MountError("Can't query dm snapshot on %s" % device) - for line in out.splitlines(): - if line and line.startswith("loop"): - return True - return False - - def _loseek(self, device): - import re - rc, out = runner.runtool([self.losetupcmd, '-a']) - if rc != 0: - raise MountError("Failed to run 'losetup -a'") - for line in out.splitlines(): - m = re.match("([^:]+): .*", line) - if m and m.group(1) == device: - return True - return False - - def create(self): - if not self.created: - if not self.loopid: - self.loopid = self._genloopid() - self.device = "/dev/loop%d" % self.loopid - if os.path.exists(self.device): - if self._loseek(self.device): - raise MountError("Device busy: %s" % self.device) - else: - self.created = True - return - - mknod = find_binary_path('mknod') - rc = runner.show([mknod, '-m664', self.device, 'b', '7', str(self.loopid)]) - if rc != 0: - raise MountError("Failed to create device %s" % self.device) - else: - self.created = True - - def close(self): - if self.created: - try: - self.cleanup() - self.device = None - except MountError, e: - msger.error("%s" % e) - - def cleanup(self): - - if self.device is None: - return - - - if self._kpseek(self.device): - if self.created: - for i in range(3, os.sysconf("SC_OPEN_MAX")): - try: - os.close(i) - except: - pass - runner.quiet([self.kpartxcmd, "-d", self.device]) - if self._loseek(self.device): - runner.quiet([self.losetupcmd, "-d", self.device]) - # FIXME: should sleep a while between two loseek - if self._loseek(self.device): - msger.warning("Can't cleanup loop device %s" % self.device) - elif self.loopid: - os.unlink(self.device) - -DEVICE_PIDFILE_DIR = "/var/tmp/mic/device" -DEVICE_LOCKFILE = "/var/lock/__mic_loopdev.lock" - -def get_loop_device(losetupcmd, lofile): - global DEVICE_PIDFILE_DIR - global DEVICE_LOCKFILE - - import fcntl - makedirs(os.path.dirname(DEVICE_LOCKFILE)) - fp = open(DEVICE_LOCKFILE, 'w') - fcntl.flock(fp, fcntl.LOCK_EX) - try: - loopdev = None - devinst = LoopDevice() - - # clean up left loop device first - clean_loop_devices() - - # provide an avaible loop device - rc, out = runner.runtool([losetupcmd, "--find"]) - if rc == 0: - loopdev = out.split()[0] - devinst.register(loopdev) - if not loopdev or not os.path.exists(loopdev): - devinst.create() - loopdev = devinst.device - - # setup a loop device for image file - rc = runner.show([losetupcmd, loopdev, lofile]) - if rc != 0: - raise MountError("Failed to setup loop device for '%s'" % lofile) - - devinst.reg_atexit() - - # try to save device and pid - makedirs(DEVICE_PIDFILE_DIR) - pidfile = os.path.join(DEVICE_PIDFILE_DIR, os.path.basename(loopdev)) - if os.path.exists(pidfile): - os.unlink(pidfile) - with open(pidfile, 'w') as wf: - wf.write(str(os.getpid())) - - except MountError, err: - raise CreatorError("%s" % str(err)) - except: - raise - finally: - try: - fcntl.flock(fp, fcntl.LOCK_UN) - fp.close() - os.unlink(DEVICE_LOCKFILE) - except: - pass - - return loopdev - -def clean_loop_devices(piddir=DEVICE_PIDFILE_DIR): - if not os.path.exists(piddir) or not os.path.isdir(piddir): - return - - for loopdev in os.listdir(piddir): - pidfile = os.path.join(piddir, loopdev) - try: - with open(pidfile, 'r') as rf: - devpid = int(rf.read()) - except: - devpid = None - - # if the process using this device is alive, skip it - if not devpid or os.path.exists(os.path.join('/proc', str(devpid))): - continue - - # try to clean it up - try: - devinst = LoopDevice() - devinst.register(os.path.join('/dev', loopdev)) - devinst.cleanup() - os.unlink(pidfile) - except: - pass - |