From 35a6570a52dd4ca1ec0c6658ac7b116ac776735b Mon Sep 17 00:00:00 2001 From: lars Date: Fri, 17 Aug 2007 11:25:51 +0000 Subject: [PATCH] improved implementation of blockdevice cache added UUID detection for blockdevices changed interface of cryptobox.core.container and cryptobox.core.main for new blockdevice detection * beware: the change is not finished - the cryptobox is not working properly for now --- src/cryptobox/core/__init__.py | 2 +- src/cryptobox/core/blockdevice.py | 453 +++++++++++++++++++++++------- src/cryptobox/core/container.py | 106 ++----- src/cryptobox/core/main.py | 57 ++-- 4 files changed, 403 insertions(+), 215 deletions(-) diff --git a/src/cryptobox/core/__init__.py b/src/cryptobox/core/__init__.py index e984929..ed5e52c 100644 --- a/src/cryptobox/core/__init__.py +++ b/src/cryptobox/core/__init__.py @@ -3,5 +3,5 @@ __revision__ = "$Id$" -__all__ = [ 'main', 'container', 'exceptions', 'tools', 'settings' ] +__all__ = [ 'main', 'container', 'exceptions', 'blockdevice', 'settings' ] diff --git a/src/cryptobox/core/blockdevice.py b/src/cryptobox/core/blockdevice.py index 92e2644..69e55ad 100644 --- a/src/cryptobox/core/blockdevice.py +++ b/src/cryptobox/core/blockdevice.py @@ -25,13 +25,13 @@ These classes detect and filter available blockdevices. __revision__ = "$Id$" -#TODO: use logger to report interesting behaviour - import os import subprocess import time +import logging import cryptobox.core.settings +LOGGER = logging.getLogger("CryptoBox") DEFAULT_SYSBLOCK_DIR = '/sys/block' DEFAULT_DEVNODE_DIR = '/dev' @@ -40,14 +40,18 @@ MAJOR_DEVNUM_RAM = 1 MAJOR_DEVNUM_LOOP = 7 MAJOR_DEVNUM_MD_RAID = 9 -USE_CACHE = True +## cache settings +CACHE_ENABLED = True CACHE_EXPIRE_SECONDS = 60 +CACHE_MONITOR_FILE = '/proc/partitions' -#TODO: remove this after profiling +## useful for manual profiling IS_VISIBLE = True ## caching is quite important for the following implementation -CACHED_VALUES = {} +## the object will be initializes later below +CACHE = None + class Blockdevices: """handle all blockdevices of this system @@ -72,6 +76,18 @@ class Blockdevices: return self.devices[:] + def get_storage_devices(self): + """return a list of devices with the 'storage' flag + """ + return [ dev for dev in self.devices if dev.is_storage() ] + + + def get_partitionable_devices(self): + """return a list of devices with the 'partitionable' flag + """ + return [ dev for dev in self.devices if dev.is_partitionable() ] + + class Blockdevice: @@ -84,17 +100,40 @@ class Blockdevice: self.devnode_dir = devnode_dir self.sysblock_dir = sysblock_dir self.name = os.path.basename(self.devdir) + ## "reset" below will fill these values + self.devnum = None + self.size = None + self.size_human = None + self.range = None + self.slaves = None + self.holders = None + self.children = None + self.devnodes = None + self.uuid = None + self.label = None + self.reset() + + + def reset(self): + """reread the data of the device + """ + CACHE.reset(["blockdevice_info", self.name]) self.devnum = self.__get_major_minor() self.size = self.__get_size() + self.size_human = self.__get_size_human() self.range = self.__get_device_range() self.slaves = self.__get_dev_related("slaves") self.holders = self.__get_dev_related("holders") self.children = self.__get_children() self.devnodes = self.__get_device_nodes() + self.uuid = self.__get_uuid() + self.label = self.__get_label() def is_valid(self): - """ check if the device is usable and valid + """check if the device is usable and valid + + causes of invalidity: ram device, loop device, removable device """ if not self.devnodes: return False @@ -109,6 +148,9 @@ class Blockdevice: ## loop devices are ignored if major == MAJOR_DEVNUM_LOOP: return False + ## removable devices are ignored (due to long timeouts) + if self.is_removable(): + return False except TypeError: return False return True @@ -119,38 +161,38 @@ class Blockdevice: """ ## check the cache first cache_link = ["blockdevice_info", self.name, "is_storage"] - cached = _get_cached_value(cache_link) + cached = CACHE.get(cache_link) if not cached is None: return cached if self.range > 1: ## partitionable blockdevice - _set_cached_value(cache_link, False) + CACHE.set(cache_link, False) return False if self.size < MINIMUM_STORAGE_SIZE: ## extended partition, unused loop device - _set_cached_value(cache_link, False) + CACHE.set(cache_link, False) return False if self.devnum[0] == MAJOR_DEVNUM_RAM: ## ram device - _set_cached_value(cache_link, False) + CACHE.set(cache_link, False) return False ## are we the device mapper of a luks device? for slave in self.slaves: if get_blockdevice(slave, self.sysblock_dir, self.devnode_dir).is_luks(): - _set_cached_value(cache_link, False) + CACHE.set(cache_link, False) return False ## if we are a luks device with exactly one child, then ## we are a storage if (len(self.children) == 1) and self.is_luks(): - _set_cached_value(cache_link, True) + CACHE.set(cache_link, True) return True if self.children: ## a parent blockdevice - _set_cached_value(cache_link, False) + CACHE.set(cache_link, False) return False - _set_cached_value(cache_link, True) + CACHE.set(cache_link, True) return True @@ -168,16 +210,16 @@ class Blockdevice: """ ## check the cache first cache_link = ["blockdevice_info", self.name, "is_lvm_pv"] - cached = _get_cached_value(cache_link) + cached = CACHE.get(cache_link) if not cached is None: return cached ## is one of the devnodes of the device a physical volume? for one_lvm_pv in find_lvm_pv(): if one_lvm_pv in self.devnodes: - _set_cached_value(cache_link, True) + CACHE.set(cache_link, True) return True - _set_cached_value(cache_link, False) + CACHE.set(cache_link, False) return False @@ -186,23 +228,23 @@ class Blockdevice: """ ## check the cache first cache_link = ["blockdevice_info", self.name, "is_lvm_lv"] - cached = _get_cached_value(cache_link) + cached = CACHE.get(cache_link) if not cached is None: return cached ## is one of the devnodes of the device a physical volume? ## logical LVM volumes always depend on their physical volumes if not self.slaves: - _set_cached_value(cache_link, False) + CACHE.set(cache_link, False) return False ## is one of the LVM physical volumes a device node of our slave(s)? for one_lvm_pv in find_lvm_pv(): for one_slave in self.slaves: if one_lvm_pv in get_blockdevice(one_slave, self.sysblock_dir, self.devnode_dir).devnodes: - _set_cached_value(cache_link, True) + CACHE.set(cache_link, True) return True - _set_cached_value(cache_link, False) + CACHE.set(cache_link, False) return False @@ -211,7 +253,7 @@ class Blockdevice: """ ## check the cache first cache_link = ["blockdevice_info", self.name, "is_md_raid"] - cached = _get_cached_value(cache_link) + cached = CACHE.get(cache_link) if not cached is None: return cached @@ -229,7 +271,7 @@ class Blockdevice: result = False ## store result and return - _set_cached_value(cache_link, result) + CACHE.set(cache_link, result) return result @@ -238,7 +280,7 @@ class Blockdevice: """ ## check the cache first cache_link = ["blockdevice_info", self.name, "is_luks"] - cached = _get_cached_value(cache_link) + cached = CACHE.get(cache_link) if not cached is None: return cached @@ -262,7 +304,33 @@ class Blockdevice: proc.wait() result = proc.returncode == 0 ## store result and return - _set_cached_value(cache_link, result) + CACHE.set(cache_link, result) + return result + + + def is_removable(self): + """check if the device is marked as 'removable' + """ + ## check the cache first + cache_link = ["blockdevice_info", self.name, "is_removable"] + cached = CACHE.get(cache_link) + if not cached is None: + return cached + + removable_file = os.path.join(self.devdir, "removable") + if os.path.isfile(removable_file): + try: + content = file(removable_file).read().strip() + if content == "1": + return True + else: + return False + except IOError: + result = False + else: + result = False + + CACHE.set(cache_link, result) return result @@ -275,12 +343,23 @@ class Blockdevice: return [] + def __get_size_human(self): + """return a human readable string representing the size of the device + """ + size = self.size + if self.size > 5120: + return "%dGB" % int(self.size/1024) + else: + return "%dMB" % self.size + + def __get_size(self): - """return the size (in kB) of the blockdevice + """return the size (in MB) of the blockdevice """ default = 0 try: - return int(file(os.path.join(self.devdir, 'size')).read()) + size_kb = int(file(os.path.join(self.devdir, 'size')).read()) + return int(size_kb/1024) except OSError: return default except ValueError: @@ -358,6 +437,150 @@ class Blockdevice: return result + def __get_uuid(self): + """determine the unique identifier of this device + + returns None in case of error or for invalid devices (see "is_valid") + """ + if not self.is_valid(): + return None + ## partitionable devices do not have a UUID + if self.is_partitionable(): + return None + ## UUIDs of physical LVM volumes can only be determined via pvdisplay + if self.is_lvm_pv(): + return self.__get_uuid_lvm_pv() + ## UUIDs of luks devices can be determined via luksDump + if self.is_luks(): + return self.__get_uuid_luks() + return self.__get_uuid_default() + + + def __get_uuid_luks(self): + """determine the unique identifier of luks devices + """ + prefs = _load_preferences() + try: + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ prefs["Programs"]["cryptsetup"], + "luksUUID", self.devnodes[0] ]) + proc.wait() + except OSError, err_msg: + LOGGER.warning("Failed to call '%s' to determine UUID: %s" \ + % (prefs["Programs"]["cryptsetup"], err_msg)) + return None + if proc.returncode != 0: + LOGGER.warning("Execution of '%s' failed: %s" % \ + (prefs["Programs"]["cryptsetup"], + proc.stderr.read().strip())) + return None + result = proc.stdout.read().strip() + if result: + return result + else: + return None + + + def __get_uuid_lvm_pv(self): + """determine the unique identifier of physical LVM volumes + """ + prefs = _load_preferences() + try: + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ prefs["Programs"]["super"], + prefs["Programs"]["CryptoBoxRootActions"], + "program", "pvdisplay" ]) + proc.wait() + except OSError, err_msg: + LOGGER.warning("Failed to call '%s' via 'super' to determine " \ + % prefs["Programs"]["pvdisplay"] + "UUID: %s" % err_msg) + return None + if proc.returncode != 0: + LOGGER.warning("Execution of 'pvdisplay' failed: %s" % \ + proc.stderr.read().strip()) + return None + for line in proc.stdout.readlines(): + items = line.strip().split(":") + if (len(items) == 12) and (items[0] in self.devnodes): + return items[11] + ## not found + return None + + + def __get_uuid_default(self): + """determine the unique identifier for non-special devices + + luks and lvm_pv devices must be treated differently + """ + prefs = _load_preferences() + try: + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ prefs["Programs"]["blkid"], + "-s", "UUID", + "-o", "value", + "-c", os.devnull, + "-w", os.devnull, + self.devnodes[0] ]) + proc.wait() + except OSError, err_msg: + LOGGER.warning("Failed to call '%s' to determine UUID: %s" % \ + (prefs["Programs"]["blkid"], err_msg)) + return None + if proc.returncode != 0: + LOGGER.warning("Execution of '%s' failed: %s" % \ + (prefs["Programs"]["blkid"], proc.stderr.read().strip())) + return None + result = proc.stdout.read().strip() + if result: + return result + else: + return None + + + def __get_label(self): + """determine the label of a filesystem contained in a device + + return None for errors, empty labels and for non-storage devices + """ + if not self.is_valid(): + return None + prefs = _load_preferences() + try: + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ prefs["Programs"]["blkid"], + "-s", "LABEL", + "-o", "value", + "-c", os.devnull, + "-w", os.devnull, + self.devnodes[0]]) + proc.wait() + except OSError, err_msg: + LOGGER.warning("Failed to call '%s' to determine label: %s" % \ + (prefs["Programs"]["blkid"], err_msg)) + return None + if proc.returncode != 0: + LOGGER.warning("Execution of '%s' failed: %s" % \ + (prefs["Programs"]["blkid"], proc.stderr.read().strip())) + return None + result = proc.stdout.read().strip() + if result: + return result + else: + return None + + def __str__(self): """display the name of the device """ @@ -370,6 +593,8 @@ class Blockdevice: output = "%s:\n" % self.name output += "\t%s:\t%s\n" % ("blockdir", self.devdir) output += "\t%s:\t%s\n" % ("major/minor", self.devnum) + output += "\t%s:\t\t%s\n" % ("label", self.label) + output += "\t%s:\t\t%s\n" % ("UUID", self.uuid) output += "\t%s:\t\t%s\n" % ("range", self.range) output += "\t%s:\t\t%s\n" % ("size", self.size) output += "\t%s:\t\t%s\n" % ("slaves", self.slaves) @@ -377,14 +602,98 @@ class Blockdevice: output += "\t%s:\t%s\n" % ("children", self.children) output += "\t%s:\t%s\n" % ("device nodes", self.devnodes) output += "\tflags:\t\t" - for funcname in [ "storage", "md_raid", "partitionable", "luks", - "lvm_pv", "lvm_lv"]: - if getattr(self, "is_%s" % funcname)(): - output += "%s " % funcname + for funcname in [ func for func in dir(self) + if func.startswith("is_") and callable(getattr(self, func))]: + if getattr(self, funcname)(): + output += "%s " % funcname[3:] output += "\n" return output +class BlockdeviceCache: + """manage cached results of blockdevce queries + + the cache expires every 60 seconds or as soon as CACHE_MONITOR_FILE changes + """ + + def __init__(self): + self.values = {} + self.expires = None + self.partitions_save = None + self.reset() + + + def reset(self, link=None): + """empty the cache and reset the expire time + """ + if not link: + self.values = {} + try: + self.partitions_save = file(CACHE_MONITOR_FILE).read() + except IOError, err_msg: + LOGGER.warning("Failed to read '%s': %s" % \ + (CACHE_MONITOR_FILE, err_msg)) + self.partitions_save = "" + self.expires = int(time.time()) + CACHE_EXPIRE_SECONDS + else: + ## we do no reset the expire date + self.set(link, {}) + + + def __is_expired(self): + """check if the cache is expired + """ + try: + if (file(CACHE_MONITOR_FILE).read() != self.partitions_save) or \ + (self.expires < int(time.time())): + return True + except IOError: + LOGGER.warning("Failed to read '%s': %s" % \ + (CACHE_MONITOR_FILE, err_msg)) + return False + + + def get(self, link): + """return a cached value + + "link" is an array of the hierachie of the accessed item + e.g. link = ["blockdevices", "hda"] + return None if the value is not in the cache or if CACHE_ENABLED is False + """ + if not CACHE_ENABLED: + return None + if self.__is_expired(): + self.reset() + ## walk down the tree + ref = self.values + for element in link: + if element in ref: + ref = ref[element] + else: + return None + return ref + + + def set(self, link, item): + """store an item in the cache + + "link" is an array of the hierachie of the accessed item + e.g. link = ["blockdevices", "hda"] + """ + if not CACHE_ENABLED: + return + ## walk down the tree + ref = self.values + for element in link[:-1]: + if not element in ref: + ## create a non-existing sub element + ref[element] = {} + ref = ref[element] + ## store the item + ref[link[-1]] = item + + + def get_blockdevice(dev, sysblock_dir=DEFAULT_SYSBLOCK_DIR, devnode_dir=DEFAULT_DEVNODE_DIR): @@ -401,16 +710,18 @@ def get_blockdevice(dev, else: return None devname = os.path.basename(devdir) - dev = _get_cached_value(["blockdevices", devname]) + cache_link = ["blockdevices", devname] + dev = CACHE.get(cache_link) if dev is None: dev = Blockdevice(devdir, sysblock_dir, devnode_dir) - _set_cached_value(["blockdevices", devname], dev) + CACHE.set(cache_link, dev) return dev def find_blockdevices(top_dir): - cached = _get_cached_value(["blockdevice_dirs", top_dir]) + cache_link = ["blockdevice_dirs", top_dir] + cached = CACHE.get(cache_link) if not cached is None: return cached[:] @@ -431,98 +742,44 @@ def find_blockdevices(top_dir): fnames.remove(fname) os.path.walk(top_dir, look4dev_dirs, 'dev') - _set_cached_value(["blockdevice_dirs", top_dir], dev_dirs) + CACHE.set(cache_link, dev_dirs) return dev_dirs[:] def find_lvm_pv(): """return the blockdevice names of all physical LVM volumes """ - cached = _get_cached_value(["lvm", "pv"]) + cache_link = ["lvm", "pv"] + cached = CACHE.get(cache_link) if not cached is None: return cached[:] - #TODO: should we check, if LVM is supported at all? - # e.g. by checking the existence of pvdisplay? prefs = _load_preferences() result = None try: proc = subprocess.Popen( shell = False, stdout = subprocess.PIPE, + stderr = subprocess.PIPE, args = [ prefs["Programs"]["super"], prefs["Programs"]["CryptoBoxRootActions"], "program", "pvdisplay" ]) proc.wait() except OSError, err_msg: - # TODO: add a logging warning + LOGGER.info("Failed to call 'pvdisplay' via 'super': %s" % err_msg) result = [] if proc.returncode != 0: - # TODO: add a logging warning + LOGGER.info("Execution of 'pvdisplay' failed: %s" % \ + proc.stderr.read().strip()) result = [] if result is None: result = [] for line in proc.stdout.readlines(): result.append(line.split(":", 1)[0].strip()) - _set_cached_value(["lvm", "pv"], result) + CACHE.set(cache_link, result) return result[:] -def _get_cached_value(link): - """return a cached value - - "link" is an array of the hierachie of the accessed item - e.g. link = ["blockdevices", "hda"] - return None if the value is not in the cache or if USE_CACHE is False - """ - if not USE_CACHE: - return None - - if "expires" in CACHED_VALUES: - if CACHED_VALUES["expires"] < int(time.time()): - reset_cache() - else: - __reset_cache_timer() - - ref = CACHED_VALUES - for element in link: - if element in ref: - ref = ref[element] - else: - return None - return ref - - -def reset_cache(): - ## refresh the cache - for item in CACHED_VALUES: - CACHED_VALUES[item] = {} - __reset_cache_timer() - - -def __reset_cache_timer(): - CACHED_VALUES["expires"] = int(time.time()) + CACHE_EXPIRE_SECONDS - - - -def _set_cached_value(link, item): - """store an item in the cache - - "link" is an array of the hierachie of the accessed item - e.g. link = ["blockdevices", "hda"] - """ - if not USE_CACHE: - return - ref = CACHED_VALUES - for element in link[:-1]: - if not element in ref: - ## create a non-existing sub element - ref[element] = {} - ref = ref[element] - ## store the item - ref[link[-1]] = item - - def _load_preferences(): prefs = cryptobox.core.settings.get_current_settings() if not prefs is None: @@ -540,6 +797,10 @@ def _load_preferences(): +## initialize cache +CACHE = BlockdeviceCache() + + if __name__ == '__main__': ## list the properties of all available devices ## this is just for testing purposes diff --git a/src/cryptobox/core/container.py b/src/cryptobox/core/container.py index 1890f96..374cb3f 100644 --- a/src/cryptobox/core/container.py +++ b/src/cryptobox/core/container.py @@ -1,5 +1,5 @@ # -# Copyright 2006 sense.lab e.V. +# Copyright 02006-02007 sense.lab e.V. # # This file is part of the CryptoBox. # @@ -55,6 +55,10 @@ class CryptoBoxContainer: def __init__(self, device, cbox): + """initialize the container + + "device" is a cryptobox.core.blockdevice object + """ self.device = device self.cbox = cbox self.uuid = None @@ -149,7 +153,7 @@ class CryptoBoxContainer: e.g.: /dev/hdc1 Available since: 0.3.0 """ - return self.device + return self.device.devnodes[0] def get_type(self): @@ -198,8 +202,7 @@ class CryptoBoxContainer: an error is indicated by "-1" Available since: 0.3.0 """ - import cryptobox.core.tools as cbxtools - return cbxtools.get_blockdevice_size(self.device) + return self.device.size def reset_object(self): @@ -208,6 +211,7 @@ class CryptoBoxContainer: this is especially useful after changing the type via 'create' Available since: 0.3.0 """ + self.device.reset() self.uuid = self.__get_uuid() self.cont_type = self.__get_type_of_partition() self.fs_type = self.__get_fs_type() @@ -277,7 +281,7 @@ class CryptoBoxContainer: self.cbox.prefs["Programs"]["CryptoBoxRootActions"], "program", "cryptsetup", "luksAddKey", - self.device, + self.device.devnodes[0], "--batch-mode"]) proc.stdin.write("%s\n%s" % (oldpw, newpw)) (output, errout) = proc.communicate() @@ -302,7 +306,7 @@ class CryptoBoxContainer: self.cbox.prefs["Programs"]["cryptsetup"], "--batch-mode", "luksDelKey", - self.device, + self.device.devnodes[0], "%d" % (keyslot, )]) proc.wait() if proc.returncode != 0: @@ -318,7 +322,7 @@ class CryptoBoxContainer: while it is being formatted or similar. Available since: 0.3.1 """ - return self.cbox.get_device_busy_state(self.device) + return self.cbox.get_device_busy_state(self.device.name) def set_busy(self, new_state, timeout=300): @@ -328,7 +332,7 @@ class CryptoBoxContainer: The timeout is optional and defaults to five minutes. Available since: 0.3.1 """ - self.cbox.set_device_busy_state(self.device, new_state, timeout) + self.cbox.set_device_busy_state(self.device.name, new_state, timeout) ## ****************** internal stuff ********************* @@ -358,56 +362,13 @@ class CryptoBoxContainer: def __get_uuid(self): """Retrieve the uuid of the container device. """ - if self.__get_type_of_partition() == CONTAINERTYPES["luks"]: - guess = self.__get_luks_uuid() - else: - guess = self.__get_non_luks_uuid() + guess = self.device.uuid ## did we get a valid value? if guess: return guess else: ## emergency default value - return self.device.replace(os.path.sep, "_") - - - def __get_luks_uuid(self): - """get uuid for luks devices""" - proc = subprocess.Popen( - shell = False, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [self.cbox.prefs["Programs"]["cryptsetup"], - "luksUUID", - self.device]) - (stdout, stderr) = proc.communicate() - if proc.returncode != 0: - self.cbox.log.info("could not retrieve luks uuid (%s): %s", - (self.device, stderr.strip())) - return None - return stdout.strip() - - - def __get_non_luks_uuid(self): - """return UUID for ext2/3 and vfat filesystems""" - proc = subprocess.Popen( - shell=False, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - args=[self.cbox.prefs["Programs"]["blkid"], - "-s", "UUID", - "-o", "value", - "-c", os.devnull, - "-w", os.devnull, - self.device]) - (stdout, stderr) = proc.communicate() - ## execution failed? - if proc.returncode != 0: - self.cbox.log.info("retrieving of partition type (" + str(self.device) \ - + ") via 'blkid' failed: " + str(stderr.strip()) \ - + " - maybe it is encrypted?") - return None - ## return output of blkid - return stdout.strip() + return self.device.name def __get_type_of_partition(self): @@ -415,7 +376,7 @@ class CryptoBoxContainer: see cryptobox.core.container.CONTAINERTYPES """ - if self.__is_luks_partition(): + if self.device.is_luks(): return CONTAINERTYPES["luks"] type_of_partition = self.__get_type_id_of_partition() if type_of_partition in FSTYPES["plain"]: @@ -436,7 +397,7 @@ class CryptoBoxContainer: "-o", "value", "-c", os.devnull, "-w", os.devnull, - self.device ]) + self.device.devnodes[0] ]) (stdout, stderr) = proc.communicate() if proc.returncode == 0: ## we found a uuid @@ -454,13 +415,14 @@ class CryptoBoxContainer: def __get_fs_type(self): "returns the filesystem used on a container" ## should we handle device mapping or plain device - if self.__is_luks_partition() and self.name: + if self.device.is_luks() and self.name: + #TODO: replace this by self.device.holders ... container = os.path.join(self.__dmDir, self.name) ## can't determine fs while encrypted if not self.is_mounted(): return "unavailable" else: - container = self.device + container = self.device.devnodes[0] proc = subprocess.Popen( shell = False, stdout = subprocess.PIPE, @@ -479,22 +441,6 @@ class CryptoBoxContainer: return - def __is_luks_partition(self): - "check if the given device is a luks partition" - proc = subprocess.Popen( - shell = False, - stdin = None, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [ - self.cbox.prefs["Programs"]["cryptsetup"], - "--batch-mode", - "isLuks", - self.device]) - proc.wait() - return proc.returncode == 0 - - def __get_mount_point(self): "return the name of the mountpoint of this volume" return os.path.join(self.cbox.prefs["Locations"]["MountParentDir"], self.name) @@ -525,7 +471,7 @@ class CryptoBoxContainer: self.cbox.prefs["Programs"]["CryptoBoxRootActions"], "program", "cryptsetup", "luksOpen", - self.device, + self.device.devnodes[0], self.name, "--batch-mode"]) proc.stdin.write(password) @@ -621,7 +567,7 @@ class CryptoBoxContainer: self.cbox.prefs["Programs"]["super"], self.cbox.prefs["Programs"]["CryptoBoxRootActions"], "program", "mount", - self.device, + self.device.devnodes[0], self.__get_mount_point()]) proc.wait() if proc.returncode != 0: @@ -691,7 +637,7 @@ class CryptoBoxContainer: args = [ self.cbox.prefs["Programs"]["nice"], self.cbox.prefs["Programs"]["mkfs"], - "-t", fs_type, self.device]) + "-t", fs_type, self.device.devnodes[0]]) loc_data.proc.wait() ## wait to allow error detection if loc_data.proc.returncode == 0: @@ -711,7 +657,7 @@ class CryptoBoxContainer: time.sleep(3) ## if the thread exited very fast, then it failed if not bg_task.isAlive(): - raise CBCreateError("formatting of device (%s) failed out " % self.device \ + raise CBCreateError("formatting of device (%s) failed out " % self.device.devnodes[0] \ + "of unknown reasons") @@ -736,7 +682,7 @@ class CryptoBoxContainer: self.cbox.prefs["Programs"]["CryptoBoxRootActions"], "program", "cryptsetup", "luksFormat", - self.device, + self.device.devnodes[0], "--batch-mode", "--cipher", self.cbox.prefs["Main"]["DefaultCipher"], "--iter-time", "2000"]) @@ -757,7 +703,7 @@ class CryptoBoxContainer: self.cbox.prefs["Programs"]["CryptoBoxRootActions"], "program", "cryptsetup", "luksOpen", - self.device, + self.device.devnodes[0], self.name, "--batch-mode"]) proc.stdin.write(password) @@ -808,7 +754,7 @@ class CryptoBoxContainer: time.sleep(3) ## if the thread exited very fast, then it failed if not bg_task.isAlive(): - raise CBCreateError("formatting of device (%s) failed out " % self.device \ + raise CBCreateError("formatting of device (%s) failed out " % self.device.devnodes[0] \ + "of unknown reasons") diff --git a/src/cryptobox/core/main.py b/src/cryptobox/core/main.py index 07b7b7c..8ccef58 100644 --- a/src/cryptobox/core/main.py +++ b/src/cryptobox/core/main.py @@ -1,5 +1,5 @@ # -# Copyright 2006 sense.lab e.V. +# Copyright 02006-02007 sense.lab e.V. # # This file is part of the CryptoBox. # @@ -27,9 +27,8 @@ __revision__ = "$Id$" import sys import cryptobox.core.container as cbxContainer from cryptobox.core.exceptions import CBEnvironmentError, CBConfigUndefinedError -import re +import cryptobox.core.blockdevice as blockdevice import os -import cryptobox.core.tools as cbxTools import subprocess import threading @@ -154,7 +153,8 @@ class CryptoBox: """ self.log.debug("rereading container list") self.__containers = [] - for device in cbxTools.get_available_partitions(): + blockdevice.CACHE.reset() + for device in blockdevice.Blockdevices().get_storage_devices(): if self.is_device_allowed(device) and not self.is_config_partition(device): self.__containers.append(cbxContainer.CryptoBoxContainer(device, self)) ## sort by container name @@ -204,49 +204,30 @@ class CryptoBox: The check is done by comparing the label of the filesystem with a string. """ - proc = subprocess.Popen( - shell = False, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [ - self.prefs["Programs"]["blkid"], - "-c", os.path.devnull, - "-o", "value", - "-s", "LABEL", - device]) - (output, error) = proc.communicate() - return output.strip() == self.prefs["Main"]["ConfigVolumeLabel"] + return device.label == self.prefs["Main"]["ConfigVolumeLabel"] - def is_device_allowed(self, devicename): + def is_device_allowed(self, device): """check if a device is white-listed for being used as cryptobox containers also check, if the device is readable and writeable for the current user """ import types - devicename = os.path.abspath(devicename) - if not os.access(devicename, os.R_OK): - self.log.debug("Skipping device without read permissions: %s" % devicename) - return False - if not os.access(devicename, os.W_OK): - self.log.debug("Skipping device without write permissions: %s" % devicename) - return False allowed = self.prefs["Main"]["AllowedDevices"] if type(allowed) == types.StringType: allowed = [allowed] - for a_dev in allowed: - if not a_dev: - continue - ## double dots are not allowed (e.g. /dev/ide/../sda) - if re.search("/\.\./", devicename): - continue - ## it is not possible to check for 'realpath' - that does not work - ## for the cryptobox as /dev/ is bind-mounted (real hda-name is /opt/...) - if re.search('^%s' % a_dev, devicename): - self.log.debug("Adding valid device: %s" % devicename) - return True - self.log.debug("Skipping device not listed in Main->AllowedDevices: %s" \ - % devicename) + for devnode in device.devnodes: + if [ a_dev for a_dev in allowed if devnode.startswith(a_dev) ]: + if os.access(devnode, os.R_OK | os.W_OK): + self.log.debug("Adding valid device: %s" % devnode) + ## move the device to the first position + device.devnodes.remove(devnode) + device.devnodes.insert(0, devnode) + return True + else: + self.log.debug("Skipping device without read and write" \ + + "permissions: %s" % device.name) + self.log.debug("Skipping unusable device: %s" % device.name) return False @@ -269,7 +250,7 @@ class CryptoBox: def get_container(self, device): "retrieve the container element for this device" - all = [e for e in self.get_container_list() if e.device == device] + all = [e for e in self.get_container_list() if e.device.name == device.name] if all: return all[0] else: