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
This commit is contained in:
lars 2007-08-17 11:25:51 +00:00
parent b72310097c
commit 35a6570a52
4 changed files with 403 additions and 215 deletions

View File

@ -3,5 +3,5 @@
__revision__ = "$Id$"
__all__ = [ 'main', 'container', 'exceptions', 'tools', 'settings' ]
__all__ = [ 'main', 'container', 'exceptions', 'blockdevice', 'settings' ]

View File

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

View File

@ -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")

View File

@ -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)
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
self.log.debug("Skipping device not listed in Main->AllowedDevices: %s" \
% devicename)
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: