removed dependency on /dev/mapper location from container.py

replaced some functions in container.py with their new blockdevice.py counterparts
move "type_id" detection from container.py to blockdevice.py
reduce calls of "reset" for blockdevices
This commit is contained in:
lars 2007-08-20 11:21:56 +00:00
parent 3224d59dfe
commit 1270b00a5f
2 changed files with 139 additions and 78 deletions

View file

@ -22,6 +22,9 @@
These classes detect and filter available blockdevices. These classes detect and filter available blockdevices.
''' '''
#TODO: call blkid only once for all devices and scan the result
__revision__ = "$Id$" __revision__ = "$Id$"
@ -111,11 +114,14 @@ class Blockdevice:
self.devnodes = None self.devnodes = None
self.uuid = None self.uuid = None
self.label = None self.label = None
self.reset() self.reset(empty_cache=False)
def reset(self): def reset(self, empty_cache=True):
"""reread the data of the device """reread the data of the device
usually we will have to reset the cache, too
just in case of first-time initialization, this is not necessary
""" """
CACHE.reset(["blockdevice_info", self.name]) CACHE.reset(["blockdevice_info", self.name])
self.devnum = self.__get_major_minor() self.devnum = self.__get_major_minor()
@ -128,6 +134,7 @@ class Blockdevice:
self.devnodes = self.__get_device_nodes() self.devnodes = self.__get_device_nodes()
self.uuid = self.__get_uuid() self.uuid = self.__get_uuid()
self.label = self.__get_label() self.label = self.__get_label()
self.type_id = self.__get_type_id()
def is_valid(self): def is_valid(self):
@ -569,8 +576,49 @@ class Blockdevice:
self.devnodes[0]]) self.devnodes[0]])
(output, error) = proc.communicate() (output, error) = proc.communicate()
except OSError, err_msg: except OSError, err_msg:
LOGGER.warning("Failed to call '%s' to determine label: %s" % \ LOGGER.warning("Failed to call '%s' to determine label for " \
(prefs["Programs"]["blkid"], err_msg)) % prefs["Programs"]["blkid"] + "'%s': %s" % \
(self.devnodes[0], err_msg))
return None
if proc.returncode != 0:
LOGGER.warning("Execution of '%s' for '%s' failed: %s" % \
(prefs["Programs"]["blkid"], self.devnodes[0],
error.strip()))
return None
result = output.strip()
if result:
return result
else:
return None
def __get_type_id(self):
"""determine the type id of a filesystem contained in a device
possible results are: ext2, ext3, vfat, reiserfs, swap, ...
return None for errors, empty labels and for luks or non-storage devices
"""
if not self.is_valid():
return None
if self.is_luks():
return None
prefs = _load_preferences()
try:
proc = subprocess.Popen(
shell = False,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE,
args = [ prefs["Programs"]["blkid"],
"-s", "TYPE",
"-o", "value",
"-c", os.devnull,
"-w", os.devnull,
self.devnodes[0]])
(output, error) = proc.communicate()
except OSError, err_msg:
LOGGER.warning("Failed to call '%s' to determine type id for" \
% prefs["Programs"]["blkid"] + " '%s': %s" % \
(self.devnodes[0], err_msg))
return None return None
if proc.returncode != 0: if proc.returncode != 0:
LOGGER.warning("Execution of '%s' for '%s' failed: %s" % \ LOGGER.warning("Execution of '%s' for '%s' failed: %s" % \

View file

@ -28,6 +28,7 @@ import os
import re import re
import time import time
from cryptobox.core.exceptions import * from cryptobox.core.exceptions import *
import cryptobox.core.blockdevice
CONTAINERTYPES = { CONTAINERTYPES = {
@ -51,8 +52,6 @@ class CryptoBoxContainer:
"""Manage a container of the CryptoBox """Manage a container of the CryptoBox
""" """
__dmDir = "/dev/mapper"
def __init__(self, device, cbox): def __init__(self, device, cbox):
"""initialize the container """initialize the container
@ -68,7 +67,7 @@ class CryptoBoxContainer:
self.mount = None self.mount = None
self.umount = None self.umount = None
self.attributes = None self.attributes = None
self.reset_object() self.reset_object(reset_device=False)
def get_name(self): def get_name(self):
@ -205,13 +204,15 @@ class CryptoBoxContainer:
return self.device.size return self.device.size
def reset_object(self): def reset_object(self, reset_device=True):
"""recheck the information about this container """recheck the information about this container
this is especially useful after changing the type via 'create' this is especially useful after changing the type via 'create'
the 'device' attribute does not need to be reset during __init__
Available since: 0.3.0 Available since: 0.3.0
""" """
self.device.reset() if reset_device():
self.device.reset()
self.uuid = self.__get_uuid() self.uuid = self.__get_uuid()
self.cont_type = self.__get_type_of_partition() self.cont_type = self.__get_type_of_partition()
self.fs_type = self.__get_fs_type() self.fs_type = self.__get_fs_type()
@ -243,7 +244,8 @@ class CryptoBoxContainer:
## no exception was raised during creation -> we can continue ## no exception was raised during creation -> we can continue
## reset the properties (encryption state, ...) of the device ## reset the properties (encryption state, ...) of the device
self.reset_object() self.reset_object()
## restore the old name (must be after reset_object) ## restore the old name, as the uuid was changed during 'create'
## must happen after reset_object
try: try:
self.set_name(old_name) self.set_name(old_name)
except CBNameIsInUse: except CBNameIsInUse:
@ -266,6 +268,7 @@ class CryptoBoxContainer:
## return if new and old passwords are the same ## return if new and old passwords are the same
if oldpw == newpw: if oldpw == newpw:
return return
#TODO: why can we do this only for non-mounted volumes?
if self.is_mounted(): if self.is_mounted():
raise CBVolumeIsActive("this container is currently active") raise CBVolumeIsActive("this container is currently active")
## remove any potential open luks mapping ## remove any potential open luks mapping
@ -339,13 +342,18 @@ class CryptoBoxContainer:
def __get_name_of_container(self): def __get_name_of_container(self):
"""retrieve the name of the container by querying the database """retrieve the name of the container by querying the database
call this function only for the initial setup of the container object""" call this function only for the initial setup of the container object
"""
found_name = None found_name = None
for key in self.cbox.prefs.volumes_db.keys(): for key in self.cbox.prefs.volumes_db.keys():
if self.cbox.prefs.volumes_db[key]["uuid"] == self.uuid: if self.cbox.prefs.volumes_db[key]["uuid"] == self.uuid:
found_name = key found_name = key
## the name may not be equal to the name of another existing device
## otherwise problems regarding the mount directory would arise
if found_name: if found_name:
return found_name test_device = cryptobox.core.blockdevice.get_blockdevice(found_name)
if (test_device is None) or (test_device == self.device):
return found_name
## there is no name defined for this uuid - we will propose a good one ## there is no name defined for this uuid - we will propose a good one
prefix = self.cbox.prefs["Main"]["DefaultVolumePrefix"] prefix = self.cbox.prefs["Main"]["DefaultVolumePrefix"]
unused_found = False unused_found = False
@ -388,62 +396,32 @@ class CryptoBoxContainer:
def __get_type_id_of_partition(self): def __get_type_id_of_partition(self):
"returns the type of the partition (see 'man blkid')" "returns the type of the partition (see 'man blkid')"
proc = subprocess.Popen( return self.device.type_id
shell = False,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE,
args = [ self.cbox.prefs["Programs"]["blkid"],
"-s", "TYPE",
"-o", "value",
"-c", os.devnull,
"-w", os.devnull,
self.get_device() ])
(stdout, stderr) = proc.communicate()
if proc.returncode == 0:
## we found a uuid
return stdout.strip()
elif proc.returncode == 2:
## failed to find the attribute - no problem
return None
else:
## something strange happened
self.cbox.log.warn("retrieving of partition type via 'blkid' failed: %s" % \
(stderr.strip(), ))
return None
def __get_fs_type(self): def __get_fs_type(self):
"returns the filesystem used on a container" """returns the filesystem used on a container
## should we handle device mapping or plain device
if self.device.is_luks() and self.name: for luks devices: return the type of the encrypted container
#TODO: replace this by self.device.holders ... return None for invalid device, for non-storage devices, ...
container = os.path.join(self.__dmDir, self.name) """
## can't determine fs while encrypted if self.device.is_luks():
if not self.is_mounted(): ## luks devices need special care ...
return "unavailable" if self.device.holders:
return cryptobox.core.blockdevice.get_blockdevice(
self.device.holders[0]).type_id
else:
## the encrypted container is not open
return None
else: else:
container = self.get_device() ## common (non-luks) devices
proc = subprocess.Popen( return self.device.type_id
shell = False,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE,
args = [ self.cbox.prefs["Programs"]["blkid"],
"-s","TYPE",
container ])
(stdout, stderr) = proc.communicate()
if proc.returncode == 0:
return stdout.split("TYPE=")[1]
else:
## if something goes wrong don't dig deeper
self.cbox.log.warn("Filesystem determination (%s) failed: %s" % \
(self.get_device(), stderr.strip()))
return "undetermined"
return
def __get_mount_point(self): def __get_mount_point(self):
"return the name of the mountpoint of this volume" "return the name of the mountpoint of this volume"
return os.path.join(self.cbox.prefs["Locations"]["MountParentDir"], self.name) return os.path.join(self.cbox.prefs["Locations"]["MountParentDir"],
self.name)
def __mount_luks(self, password): def __mount_luks(self, password):
@ -457,10 +435,12 @@ class CryptoBoxContainer:
if not os.path.exists(self.__get_mount_point()): if not os.path.exists(self.__get_mount_point()):
self.__create_mount_directory(self.__get_mount_point()) self.__create_mount_directory(self.__get_mount_point())
if not os.path.exists(self.__get_mount_point()): if not os.path.exists(self.__get_mount_point()):
err_msg = "Could not create mountpoint (%s)" % (self.__get_mount_point(), ) err_msg = "Could not create mountpoint (%s)" % \
(self.__get_mount_point(), )
self.cbox.log.error(err_msg) self.cbox.log.error(err_msg)
raise CBMountError(err_msg) raise CBMountError(err_msg)
self.cbox.send_event_notification("premount", self.__get_event_args()) self.cbox.send_event_notification("premount",
self.__get_event_args())
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdin = subprocess.PIPE, stdin = subprocess.PIPE,
@ -480,6 +460,17 @@ class CryptoBoxContainer:
err_msg = "Could not open the luks mapping: %s" % (errout.strip(), ) err_msg = "Could not open the luks mapping: %s" % (errout.strip(), )
self.cbox.log.warn(err_msg) self.cbox.log.warn(err_msg)
raise CBMountError(err_msg) raise CBMountError(err_msg)
## reset device info (reread self.device.holders)
self.device.reset()
if self.device.holders:
## the decrypted blockdevice is available
plain_device = cryptobox.core.blockdevice.get_blockdevice(
self.device.holders[0]).devnodes[0]
else:
err_msg = "Could not find the plaintext container for " \
+ "'%s': %s" % (self.get_device(), "no hold devices found")
self.cbox.log.warn(err_msg)
raise CBMountError(err_msg)
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdin = None, stdin = None,
@ -489,20 +480,23 @@ class CryptoBoxContainer:
self.cbox.prefs["Programs"]["super"], self.cbox.prefs["Programs"]["super"],
self.cbox.prefs["Programs"]["CryptoBoxRootActions"], self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
"program", "mount", "program", "mount",
os.path.join(self.__dmDir, self.name), plain_device,
self.__get_mount_point()]) self.__get_mount_point()])
proc.wait() proc.wait()
if proc.returncode != 0: if proc.returncode != 0:
err_msg = "Could not mount the filesystem: %s" % (proc.stderr.read().strip(), ) err_msg = "Could not mount the filesystem: %s" \
% (proc.stderr.read().strip(), )
self.cbox.log.warn(err_msg) self.cbox.log.warn(err_msg)
raise CBMountError(err_msg) raise CBMountError(err_msg)
## chmod the mount directory to 0777 - this is the easy way to avoid problems ## chmod the mount directory to 0777
## this only works for ext2/3 - vfat silently ignore it ## this is the easy way to avoid problems
## it only works for ext2/3 - vfat silently ignore it
## we mounted vfat partitions with umask=0000 ## we mounted vfat partitions with umask=0000
try: try:
os.chmod(self.__get_mount_point(), 0777) os.chmod(self.__get_mount_point(), 0777)
except OSError: except OSError:
self.cbox.log.warn("Failed to set write permission for the mount directory") self.cbox.log.warn("Failed to set write permission for the " \
+ "mount directory")
self.cbox.send_event_notification("postmount", self.__get_event_args()) self.cbox.send_event_notification("postmount", self.__get_event_args())
@ -522,10 +516,14 @@ class CryptoBoxContainer:
self.__get_mount_point()]) self.__get_mount_point()])
proc.wait() proc.wait()
if proc.returncode != 0: if proc.returncode != 0:
err_msg = "Could not umount the filesystem: %s" % (proc.stderr.read().strip(), ) err_msg = "Could not umount the filesystem: %s" % \
(proc.stderr.read().strip(), )
self.cbox.log.warn(err_msg) self.cbox.log.warn(err_msg)
raise CBUmountError(err_msg) raise CBUmountError(err_msg)
if os.path.exists(os.path.join(self.__dmDir, self.name)): ## reset device (reread self.device.holders)
self.device.reset()
## are there any dependent devices?
if self.device.holders:
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdin = None, stdin = None,
@ -574,8 +572,9 @@ class CryptoBoxContainer:
err_msg = "Could not mount the filesystem: %s" % (proc.stderr.read().strip(), ) err_msg = "Could not mount the filesystem: %s" % (proc.stderr.read().strip(), )
self.cbox.log.warn(err_msg) self.cbox.log.warn(err_msg)
raise CBMountError(err_msg) raise CBMountError(err_msg)
## chmod the mount directory to 0777 - this is the easy way to avoid problems ## chmod the mount directory to 0777
## this only works for ext2/3 - vfat silently ignore it ## this is the easy way to avoid problems
## it only works for ext2/3 - vfat silently ignore it
## we mounted vfat partitions with umask=0000 ## we mounted vfat partitions with umask=0000
try: try:
os.chmod(self.__get_mount_point(), 0777) os.chmod(self.__get_mount_point(), 0777)
@ -712,8 +711,19 @@ class CryptoBoxContainer:
err_msg = "Could not open the new luks mapping: %s" % (errout.strip(), ) err_msg = "Could not open the new luks mapping: %s" % (errout.strip(), )
self.cbox.log.error(err_msg) self.cbox.log.error(err_msg)
raise CBCreateError(err_msg) raise CBCreateError(err_msg)
## reset device info (reread self.device.holders)
self.device.reset()
if self.device.holders:
## the decrypted blockdevice is available
plain_device = cryptobox.core.blockdevice.get_blockdevice(
self.device.holders[0]).devnodes[0]
else:
err_msg = "Could not find the plaintext container for " \
+ "'%s': %s" % (self.get_device(), "no hold devices found")
self.cbox.log.warn(err_msg)
raise CBMountError(err_msg)
def format_luks(): def format_luks():
"""This function will get called as a seperate thread. """This function will get called as a separate thread.
To avoid the non-sharing cpu distribution between the formatting thread To avoid the non-sharing cpu distribution between the formatting thread
and the main interface, we fork and let the parent wait for the child. and the main interface, we fork and let the parent wait for the child.
@ -735,7 +745,7 @@ class CryptoBoxContainer:
self.cbox.prefs["Programs"]["nice"], self.cbox.prefs["Programs"]["nice"],
self.cbox.prefs["Programs"]["mkfs"], self.cbox.prefs["Programs"]["mkfs"],
"-t", fs_type, "-t", fs_type,
os.path.join(self.__dmDir, self.name)]) plain_device ] )
loc_data.proc.wait() loc_data.proc.wait()
## wait to allow error detection ## wait to allow error detection
if loc_data.proc.returncode == 0: if loc_data.proc.returncode == 0:
@ -767,15 +777,16 @@ class CryptoBoxContainer:
if (not os.path.islink(abs_dir)) \ if (not os.path.islink(abs_dir)) \
and os.path.isdir(abs_dir) \ and os.path.isdir(abs_dir) \
and (not os.path.ismount(abs_dir)) \ and (not os.path.ismount(abs_dir)) \
and (os.path.isfile(os.path.join(abs_dir,MOUNT_DIR_MARKER))) \ and (os.path.isfile(os.path.join(abs_dir,
MOUNT_DIR_MARKER))) \
and (len(os.listdir(abs_dir)) == 1): and (len(os.listdir(abs_dir)) == 1):
try: try:
os.remove(os.path.join(abs_dir, MOUNT_DIR_MARKER)) os.remove(os.path.join(abs_dir, MOUNT_DIR_MARKER))
os.rmdir(abs_dir) os.rmdir(abs_dir)
except OSError, err_msg: except OSError, err_msg:
## we do not care too much about unclean cleaning ... ## we do not care too much about unclean cleaning ...
self.cbox.log.info("failed to clean a mountpoint (%s): %s" % \ self.cbox.log.info("failed to clean a mountpoint (%s): %s" \
(abs_dir, str(err_msg))) % (abs_dir, str(err_msg)))
def __create_mount_directory(self, dirname): def __create_mount_directory(self, dirname):
@ -787,7 +798,8 @@ class CryptoBoxContainer:
mark_file.close() mark_file.close()
except OSError, err_msg: except OSError, err_msg:
## we do not care too much about the marking ## we do not care too much about the marking
self.cbox.log.info("failed to mark a mountpoint (%s): %s" % (dirname, str(err_msg))) self.cbox.log.info("failed to mark a mountpoint (%s): %s" % \
(dirname, str(err_msg)))
def __get_event_args(self): def __get_event_args(self):
@ -797,5 +809,6 @@ class CryptoBoxContainer:
""" """
type_text = [e for e in CONTAINERTYPES.keys() type_text = [e for e in CONTAINERTYPES.keys()
if CONTAINERTYPES[e] == self.get_type()][0] if CONTAINERTYPES[e] == self.get_type()][0]
return [self.get_device(), self.get_name(), type_text, self.__get_mount_point()] return [self.get_device(), self.get_name(), type_text,
self.__get_mount_point()]