fixed wrong redirection after reboot (causing reboot loop)

clarified interpretation of blkid output
added missing success message to volume_format_fs
execute formatting in the background
mark busy devices as such
fixed a small bug in CryptoBoxRootActions
This commit is contained in:
lars 2006-12-06 16:25:52 +00:00
parent 873bd4ec85
commit f6ddb82f4c
26 changed files with 304 additions and 175 deletions

View File

@ -74,6 +74,7 @@ def checkIfPluginIsValid(plugin):
import imp import imp
try: try:
x = imp.load_source("cbox_plugin",plugin) x = imp.load_source("cbox_plugin",plugin)
#TODO: no wildcard catches, please!
except Exception: except Exception:
return False return False
try: try:
@ -87,7 +88,7 @@ def checkIfPluginIsValid(plugin):
def checkIfEventScriptIsValid(plugin): def checkIfEventScriptIsValid(plugin):
event_dir = os.path.dirname(plugin) event_dir = os.path.dirname(plugin)
if os.path.exists(os.path.join(event_dir,EVENT_MARKER)): if os.path.exists(os.path.join(event_dir, EVENT_MARKER)):
return True return True
else: else:
return False return False
@ -106,7 +107,7 @@ def call_plugin(args):
## check if the plugin is a python program, that is marked as a cryptobox plugin ## check if the plugin is a python program, that is marked as a cryptobox plugin
if not checkIfPluginIsValid(plugin): if not checkIfPluginIsValid(plugin):
raise Exception, "the plugin (%s) is not a correctly marked python script" % plugin raise Exception, "the plugin (%s) is not a correctly marked python script" % plugin
args.insert(0,plugin) args.insert(0, plugin)
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
args = args) args = args)
@ -122,12 +123,12 @@ def call_event(args):
if not os.access(event, os.X_OK): if not os.access(event, os.X_OK):
raise Exception, "could not find executable event script (%s)" % event raise Exception, "could not find executable event script (%s)" % event
## check if the script is valid (the marker file must be in the same directory) ## check if the script is valid (the marker file must be in the same directory)
if not checkIfEventScriptIsValid(plugin): if not checkIfEventScriptIsValid(event):
raise Exception, "the event script (%s) does not reside in a directory with the marker file (%s) - this is not allowed due to abuse prevention" % (plugin,EVENT_MARKER) raise Exception, "the event script (%s) does not reside in a directory with the marker file (%s) - this is not allowed due to abuse prevention" % (event, EVENT_MARKER)
## check if the event (and its parents) are only writeable for root ## check if the event (and its parents) are only writeable for root
if not checkIfFileIsSafe(event): if not checkIfFileIsSafe(event):
raise Exception, "the event (%s) is not safe - check its (and its parents') permissions" % event raise Exception, "the event (%s) is not safe - check its (and its parents') permissions" % event
args.insert(0,event) args.insert(0, event)
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
args = args) args = args)
@ -201,7 +202,7 @@ def run_cryptsetup(args):
cmd_args.append(action) cmd_args.append(action)
cmd_args.append(device) cmd_args.append(device)
elif action == "luksDelKey": elif action == "luksDelKey":
if len(cs_args) < 2: raise "WrongArguments", "missing arguments" if len(args) < 2: raise "WrongArguments", "missing arguments"
device = args[0]; del args[0] device = args[0]; del args[0]
cmd_args.insert(-1, action) cmd_args.insert(-1, action)
cmd_args.insert(-1, device) cmd_args.insert(-1, device)
@ -361,7 +362,7 @@ def getUserInfo(user):
except TypeError: except TypeError:
# if a KeyError is raised again, then the supplied user was invalid # if a KeyError is raised again, then the supplied user was invalid
userinfo = pwd.getpwnam(user) userinfo = pwd.getpwnam(user)
u_groups =[one_group.gr_gid u_groups = [one_group.gr_gid
for one_group in grp.getgrall() for one_group in grp.getgrall()
if userinfo.pw_name in one_group.gr_mem] if userinfo.pw_name in one_group.gr_mem]
if not userinfo.pw_gid in u_groups: u_groups.append(userinfo.pw_gid) if not userinfo.pw_gid in u_groups: u_groups.append(userinfo.pw_gid)

View File

@ -76,11 +76,12 @@ Languages = en, de, sl, fr
[Programs] [Programs]
cryptsetup = /sbin/cryptsetup cryptsetup = /sbin/cryptsetup
mkfs-data = /sbin/mkfs.ext3 mkfs = /sbin/mkfs
blkid = /sbin/blkid blkid = /sbin/blkid
blockdev = /sbin/blockdev blockdev = /sbin/blockdev
mount = /bin/mount mount = /bin/mount
umount = /bin/umount umount = /bin/umount
nice = /usr/bin/nice
super = /usr/bin/super super = /usr/bin/super
# this is the "program" name as defined in /etc/super.tab # this is the "program" name as defined in /etc/super.tab
CryptoBoxRootActions = CryptoBoxRootActions CryptoBoxRootActions = CryptoBoxRootActions

View File

@ -1,17 +0,0 @@
[global]
server.socketPort = 8080
#server.environment = "production"
server.environment = "development"
server.logToScreen = True
server.log_tracebacks = True
server.threadPool = 1
server.reverseDNS = False
server.logFile = "cryptoboxwebserver.log"
[/favicon.ico]
static_filter.on = True
# TODO: use live-cd/live-cd-tree.d/var/www/favicon.ico
static_filter.file = "/usr/share/doc/python-cherrypy/cherrypy/favicon.ico"
[/test_stream]
stream_response = True

View File

@ -75,11 +75,12 @@ Languages = de, en, fr
[Programs] [Programs]
cryptsetup = /sbin/cryptsetup cryptsetup = /sbin/cryptsetup
mkfs-data = /sbin/mkfs.ext3 mkfs = /sbin/mkfs
blkid = /sbin/blkid blkid = /sbin/blkid
blockdev = /sbin/blockdev blockdev = /sbin/blockdev
mount = /bin/mount mount = /bin/mount
umount = /bin/umount umount = /bin/umount
nice = /usr/bin/nice
super = /usr/bin/super super = /usr/bin/super
# this is the "program" name as defined in /etc/super.tab # this is the "program" name as defined in /etc/super.tab
CryptoBoxRootActions = CryptoBoxRootActions CryptoBoxRootActions = CryptoBoxRootActions

7
debian/changelog vendored
View File

@ -1,3 +1,10 @@
cryptobox (0.2.52-1) unstable; urgency=low
* format partitions in background
* mark busy partitions
-- Lars Kruse <devel@sumpfralle.de> Wed, 6 Dec 2006 14:57:43 +0100
cryptobox (0.2.51-1) unstable; urgency=low cryptobox (0.2.51-1) unstable; urgency=low
* favicon included * favicon included

View File

@ -28,6 +28,7 @@ import os
import logging import logging
import cryptobox.core.tools as cbxTools import cryptobox.core.tools as cbxTools
import cryptobox.plugins.base import cryptobox.plugins.base
from cryptobox.core.exceptions import *
PARTTYPES = { PARTTYPES = {
@ -228,7 +229,7 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin):
## breaks the flow (hanging process) ## breaks the flow (hanging process)
#self.cbox.reReadContainerList() #self.cbox.reReadContainerList()
## write config data ## write config data
self.cbox.prefs.mountPartition() self.cbox.prefs.mount_partition()
self.cbox.prefs.write() self.cbox.prefs.write()
self.cbox.log.info("settings stored on config partition") self.cbox.log.info("settings stored on config partition")
## return the result ## return the result
@ -456,6 +457,26 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin):
def __format_one_partition(self, dev_name, fs_type): def __format_one_partition(self, dev_name, fs_type):
"""Format a single partition
"""
import cryptobox.core.container
## first: retrieve UUID - it can be removed from the database afterwards
prev_name = [e.get_name() for e in self.cbox.get_container_list()
if e.get_device() == dev_name]
## call "mkfs"
try:
cont = cryptobox.core.container.CryptoBoxContainer(dev_name, self.cbox)
cont.create(cryptobox.core.container.CONTAINERTYPES["plain"], fs_type=fs_type)
except (CBInvalidType, CBCreateError, CBVolumeIsActive), err_msg:
self.cbox.log.warn(err_msg)
return False
## remove unused volume entry
if prev_name:
del self.cbox.prefs.volumes_db[prev_name[0]]
return True
def __old_format_one_partition(self, dev_name, fs_type):
"""Format a single partition """Format a single partition
""" """
## first: retrieve UUID - it can be removed from the database afterwards ## first: retrieve UUID - it can be removed from the database afterwards

View File

@ -22,7 +22,7 @@ __revision__ = "$Id"
import cryptobox.plugins.base import cryptobox.plugins.base
REDIRECT_DELAY = 180 REDIRECT_DELAY = 120
class shutdown(cryptobox.plugins.base.CryptoBoxPlugin): class shutdown(cryptobox.plugins.base.CryptoBoxPlugin):
@ -44,7 +44,7 @@ class shutdown(cryptobox.plugins.base.CryptoBoxPlugin):
elif type == "reboot": elif type == "reboot":
if self.__do_shutdown("reboot"): if self.__do_shutdown("reboot"):
self.hdf["Data.Success"] = "Plugins.shutdown.Reboot" self.hdf["Data.Success"] = "Plugins.shutdown.Reboot"
self.hdf["Data.Redirect.URL"] = "" self.hdf["Data.Redirect.URL"] = "/"
self.hdf["Data.Redirect.Delay"] = REDIRECT_DELAY self.hdf["Data.Redirect.Delay"] = REDIRECT_DELAY
return "progress_reboot" return "progress_reboot"
else: else:

View File

@ -31,8 +31,8 @@ AdviceMessage {
SuccessMessage { SuccessMessage {
FormatSuccess { FormatSuccess {
Title = Formatting successful Title = Formatting is running
Text = The selected filesystem was successfully formatted. Text = The selected filesystem is being formatted in the background. This may take some time (depending on the size of your disk).
} }
} }

View File

@ -40,7 +40,7 @@ class volume_format_fs(cryptobox.plugins.base.CryptoBoxPlugin):
def do_action(self, store=None, fs_type="windows", container_type="luks", crypto_password=None, crypto_password2=None, confirm=None): def do_action(self, store=None, fs_type="windows", container_type="luks", crypto_password=None, crypto_password2=None, confirm=None):
if not fs_type in FSTYPES.keys(): if not fs_type in FSTYPES.keys():
self.cbox.info self.cbox.log.info("invalid filesystem type choosen: %s" % str(fs_type))
return "format_volume" return "format_volume"
self.hdf[self.hdf_prefix + "fs_type"] = fs_type self.hdf[self.hdf_prefix + "fs_type"] = fs_type
self.hdf[self.hdf_prefix + "container_type"] = container_type self.hdf[self.hdf_prefix + "container_type"] = container_type
@ -54,10 +54,10 @@ class volume_format_fs(cryptobox.plugins.base.CryptoBoxPlugin):
if container_type == "luks": if container_type == "luks":
return "volume_format_luks" return "volume_format_luks"
elif container_type == "plain": elif container_type == "plain":
return self.__format_plain(fs_type) return self.__format_plain(FSTYPES[fs_type])
elif store == "step2": elif store == "step2":
if container_type == "luks": if container_type == "luks":
return self.__format_luks(fs_type, crypto_password, crypto_password2) return self.__format_luks(FSTYPES[fs_type], crypto_password, crypto_password2)
else: else:
self.cbox.log.info("invalid input value for 'container_type': %s" % container_type) self.cbox.log.info("invalid input value for 'container_type': %s" % container_type)
return "volume_format" return "volume_format"
@ -72,36 +72,10 @@ class volume_format_fs(cryptobox.plugins.base.CryptoBoxPlugin):
return "no status" return "no status"
def __format_plain(self, fsType): def __format_plain(self, fs_type):
try: try:
container = self.cbox.get_container(self.device) container = self.cbox.get_container(self.device)
container.create(cbx_container.CONTAINERTYPES["plain"]) container.create(cbx_container.CONTAINERTYPES["plain"], fs_type=fs_type)
except CBVolumeIsActive:
self.hdf["Data.Warning"] = "VolumeMayNotBeMounted"
self.cbox.log.info("initialization is not possible as long as the device (%s) is mounted" % self.device)
return None
except CBContainerError, errMsg:
self.hdf["Data.Warning"] = "Plugins.volume_format_fs.FormatFailed"
self.cbox.log.warn("initialization of device '%s' failed" % self.device)
self.cbox.log.warn("reason: %s" % errMsg)
return "volume_format"
else:
self.cbox.log.info("successfully initialized device '%s'" % self.device)
return None
def __format_luks(self, fsType, pw, pw2):
if not pw:
self.hdf["Data.Warning"] = "EmptyPassword"
self.cbox.log.warn("no crypto password was supplied for initialization of device '%s'" % self.device)
return "volume_format"
if pw != pw2:
self.hdf["Data.Warning"] = "DifferentPasswords"
self.cbox.log.warn("the crypto password was not repeated correctly for initialization of device '%s'" % self.device)
return "volume_format"
container = self.cbox.get_container(self.device)
try:
container.create(cbx_container.CONTAINERTYPES["luks"], pw)
except CBVolumeIsActive: except CBVolumeIsActive:
self.hdf["Data.Warning"] = "VolumeMayNotBeMounted" self.hdf["Data.Warning"] = "VolumeMayNotBeMounted"
self.cbox.log.info("initialization is not possible as long as the device (%s) is mounted" % self.device) self.cbox.log.info("initialization is not possible as long as the device (%s) is mounted" % self.device)
@ -114,5 +88,33 @@ class volume_format_fs(cryptobox.plugins.base.CryptoBoxPlugin):
else: else:
self.hdf["Data.Success"] = "Plugins.volume_format_fs.FormatSuccess" self.hdf["Data.Success"] = "Plugins.volume_format_fs.FormatSuccess"
self.cbox.log.info("successfully initialized device '%s'" % self.device) self.cbox.log.info("successfully initialized device '%s'" % self.device)
return None return { "plugin":"disks", "values":{} }
def __format_luks(self, fs_type, pw, pw2):
if not pw:
self.hdf["Data.Warning"] = "EmptyPassword"
self.cbox.log.warn("no crypto password was supplied for initialization of device '%s'" % self.device)
return "volume_format"
if pw != pw2:
self.hdf["Data.Warning"] = "DifferentPasswords"
self.cbox.log.warn("the crypto password was not repeated correctly for initialization of device '%s'" % self.device)
return "volume_format"
container = self.cbox.get_container(self.device)
try:
container.create(cbx_container.CONTAINERTYPES["luks"],
fs_type=fs_type, password=pw)
except CBVolumeIsActive:
self.hdf["Data.Warning"] = "VolumeMayNotBeMounted"
self.cbox.log.info("initialization is not possible as long as the device (%s) is mounted" % self.device)
return None
except CBContainerError, errMsg:
self.hdf["Data.Warning"] = "Plugins.volume_format_fs.FormatFailed"
self.cbox.log.warn("initialization of device '%s' failed" % self.device)
self.cbox.log.warn("reason: %s" % errMsg)
return "volume_format"
else:
self.hdf["Data.Success"] = "Plugins.volume_format_fs.FormatSuccess"
self.cbox.log.info("successfully initialized device '%s'" % self.device)
return { "plugin":"disks", "values":{} }

View File

@ -26,6 +26,7 @@ __revision__ = "$Id"
import subprocess import subprocess
import os import os
import re import re
import time
from cryptobox.core.exceptions import * from cryptobox.core.exceptions import *
@ -36,6 +37,10 @@ CONTAINERTYPES = {
"swap":3, "swap":3,
} }
FSTYPES = {
"plain":["ext3", "ext2", "vfat", "reiserfs"],
"swap":["swap"]}
## we use this marker to make sure, that we do not remove a non-cryptobox directory ## we use this marker to make sure, that we do not remove a non-cryptobox directory
## below the mount directory ## below the mount directory
@ -46,10 +51,6 @@ class CryptoBoxContainer:
"""Manage a container of the CryptoBox """Manage a container of the CryptoBox
""" """
__fsTypes = {
"plain":["ext3", "ext2", "vfat", "reiserfs"],
"swap":["swap"]}
__dmDir = "/dev/mapper" __dmDir = "/dev/mapper"
@ -101,10 +102,10 @@ class CryptoBoxContainer:
raise CBVolumeIsActive("the container must not be active during renaming") raise CBVolumeIsActive("the container must not be active during renaming")
if not re.search(r'^[a-zA-Z0-9_\.\- ]+$', new_name): if not re.search(r'^[a-zA-Z0-9_\.\- ]+$', new_name):
raise CBInvalidName("the supplied new name contains illegal characters") raise CBInvalidName("the supplied new name contains illegal characters")
## check for another partitions with the same name ## check for another partition with the same name
if self.cbox.get_container_list(filter_name=new_name): if self.cbox.get_container_list(filter_name=new_name):
raise CBNameIsInUse("the supplied new name is already in use for anonther partition") raise CBNameIsInUse("the supplied new name is already in use for anonther partition")
## maybe there a is an entry in the volumes database (but the partition is not active ## maybe there a is an entry in the volumes database (but the partition is not active)
try: try:
## remove possibly existing inactive database item ## remove possibly existing inactive database item
del self.cbox.prefs.volumes_db[new_name] del self.cbox.prefs.volumes_db[new_name]
@ -182,23 +183,64 @@ class CryptoBoxContainer:
self.umount = self.__umount_plain self.umount = self.__umount_plain
def create(self, cont_type, password=None): def create(self, cont_type, password=None, fs_type="ext3"):
"""Format a container. """Format a container.
Also set a password for encrypted container. Also set a password for encrypted container.
""" """
if not fs_type in FSTYPES["plain"]:
raise CBInvalidType("invalid filesystem type supplied: %s" % str(fs_type))
old_name = self.get_name() old_name = self.get_name()
if cont_type == CONTAINERTYPES["luks"]: if cont_type == CONTAINERTYPES["luks"]:
self.__create_luks(password) self.__create_luks(password, fs_type)
elif cont_type == CONTAINERTYPES["plain"]: elif cont_type == CONTAINERTYPES["plain"]:
self.__create_plain() self.__create_plain(fs_type)
else: else:
raise CBInvalidType("invalid container type (%d) supplied" % (cont_type, )) raise CBInvalidType("invalid container type (%d) supplied" % (cont_type, ))
## 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 (must be after reset_object)
self.set_name(old_name) try:
self.set_name(old_name)
except CBNameIsInUse:
## failure is okay
pass
def set_busy(self, new_state, time_limit=300):
"""Set the current busy state.
The timelimit is specified in seconds.
"""
if new_state:
self.cbox.busy_devices[self.device] = int(time.time() + time_limit)
else:
try:
if self.cbox.busy_devices[self.device]:
del self.cbox.busy_devices[self.device]
except KeyError:
pass
def is_busy(self):
"""Check the busy state of the container.
"""
if not self.cbox.busy_devices.has_key(self.device):
self.cbox.log.debug("no 'busy' attribute for '%s'" % self.get_name())
return False
## invalid value - can happen after saving and loading the database
if not isinstance(self.cbox.busy_devices[self.device], int):
self.cbox.log.debug("invalid 'busy' attribute for '%s'" % self.get_name())
del db_entry["busy"]
return False
if time.time() >= self.cbox.busy_devices[self.device]:
self.cbox.log.debug("expired 'busy' attribute for '%s'" % self.get_name())
del db_entry["busy"]
return False
## lock is still active
self.cbox.log.debug("active 'busy' attribute for '%s'" % self.get_name())
return True
def change_password(self, oldpw, newpw): def change_password(self, oldpw, newpw):
@ -362,9 +404,9 @@ class CryptoBoxContainer:
if self.__is_luks_partition(): if self.__is_luks_partition():
return CONTAINERTYPES["luks"] return CONTAINERTYPES["luks"]
type_of_partition = self.__get_type_id_of_partition() type_of_partition = self.__get_type_id_of_partition()
if type_of_partition in self.__fsTypes["plain"]: if type_of_partition in FSTYPES["plain"]:
return CONTAINERTYPES["plain"] return CONTAINERTYPES["plain"]
if type_of_partition in self.__fsTypes["swap"]: if type_of_partition in FSTYPES["swap"]:
return CONTAINERTYPES["swap"] return CONTAINERTYPES["swap"]
return CONTAINERTYPES["unused"] return CONTAINERTYPES["unused"]
@ -372,29 +414,28 @@ 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')"
devnull = None devnull = None
try:
devnull = open(os.devnull, "w")
except IOError:
self.cbox.log.warn("Could not open %s" % (os.devnull, ))
proc = subprocess.Popen( proc = subprocess.Popen(
shell=False, shell = False,
stdin=None, stdout = subprocess.PIPE,
stdout=subprocess.PIPE, stderr = subprocess.PIPE,
stderr=subprocess.PIPE, args = [ self.cbox.prefs["Programs"]["blkid"],
args=[self.cbox.prefs["Programs"]["blkid"],
"-s", "TYPE", "-s", "TYPE",
"-o", "value", "-o", "value",
"-c", os.devnull, "-c", os.devnull,
"-w", os.devnull, "-w", os.devnull,
self.device]) self.device ])
proc.wait() (stdout, stder) = proc.communicate()
output = proc.stdout.read().strip() if proc.returncode == 0:
if proc.returncode != 0: ## we found a uuid
self.cbox.log.warn("retrieving of partition type via 'blkid' failed: %s" % \ return stdout.strip()
(proc.stderr.read().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 return None
devnull.close()
return output
def __is_luks_partition(self): def __is_luks_partition(self):
@ -596,50 +637,63 @@ class CryptoBoxContainer:
self.cbox.send_event_notification("postumount", self.__get_event_args()) self.cbox.send_event_notification("postumount", self.__get_event_args())
def __create_plain(self): def __create_plain(self, fs_type="ext3"):
"make a plaintext partition" "make a plaintext partition"
import threading
if self.is_mounted(): if self.is_mounted():
raise CBVolumeIsActive("deactivate the partition before filesystem initialization") raise CBVolumeIsActive(
devnull = None "deactivate the partition before filesystem initialization")
try: def format():
devnull = open(os.devnull, "w") import os
except IOError: old_name = self.get_name()
self.cbox.log.warn("Could not open %s" % (os.devnull, )) self.set_busy(True, 600)
proc = subprocess.Popen( self.cbox.log.debug("Turn the busy flag on: %s" % self.device)
shell = False, ## give the main thread a chance to continue
stdin = None, child_pid = os.fork()
stdout = devnull, if child_pid == 0:
stderr = subprocess.PIPE, proc = subprocess.Popen(
args = [ shell = False,
self.cbox.prefs["Programs"]["mkfs-data"], stdin = None,
self.device]) stdout = subprocess.PIPE,
proc.wait() stderr = subprocess.PIPE,
if proc.returncode != 0: args = [
err_msg = "Could not create the filesystem: %s" % (proc.stderr.read().strip(), ) self.cbox.prefs["Programs"]["nice"],
self.cbox.log.error(err_msg) self.cbox.prefs["Programs"]["mkfs"],
raise CBCreateError(err_msg) "-t", fs_type, self.device])
devnull.close() (stdout, sterr) = proc.communicate()
## for to allow error detection
if proc.returncode == 0:
time.sleep(5)
## skip cleanup stuff (as common for sys.exit)
os._exit(0)
else:
os.waitpid(child_pid, 0)
self.set_name(old_name)
self.set_busy(False)
self.cbox.log.debug("Turn the busy flag off: %s" % self.device)
bg_task = threading.Thread(target=format)
bg_task.start()
time.sleep(3)
## if the thread exited very fast, then it failed
if not bg_task.isAlive():
raise CBCreateError("Failed to initilize device: %s" % self.device)
def __create_luks(self, password): def __create_luks(self, password, fs_type="ext3"):
"""Create a luks partition. """Create a luks partition.
""" """
import threading
if not password: if not password:
raise CBInvalidPassword("no password supplied for new luks mapping") raise CBInvalidPassword("no password supplied for new luks mapping")
if self.is_mounted(): if self.is_mounted():
raise CBVolumeIsActive("deactivate the partition before filesystem initialization") raise CBVolumeIsActive("deactivate the partition before filesystem initialization")
devnull = None
try:
devnull = open(os.devnull, "w")
except IOError:
self.cbox.log.warn("Could not open %s" % (os.devnull, ))
## remove any potential open luks mapping ## remove any potential open luks mapping
self.__umount_luks() self.__umount_luks()
## create the luks header ## create the luks header
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdin = subprocess.PIPE, stdin = subprocess.PIPE,
stdout = devnull, stdout = subprocess.PIPE,
stderr = subprocess.PIPE, stderr = subprocess.PIPE,
args = [ args = [
self.cbox.prefs["Programs"]["super"], self.cbox.prefs["Programs"]["super"],
@ -660,7 +714,7 @@ class CryptoBoxContainer:
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdin = subprocess.PIPE, stdin = subprocess.PIPE,
stdout = devnull, stdout = subprocess.PIPE,
stderr = subprocess.PIPE, stderr = subprocess.PIPE,
args = [ args = [
self.cbox.prefs["Programs"]["super"], self.cbox.prefs["Programs"]["super"],
@ -676,24 +730,44 @@ 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)
## make the filesystem def format_luks():
proc = subprocess.Popen( import os
shell = False, old_name = self.get_name()
stdin = None, self.set_busy(True, 600)
stdout = devnull, self.cbox.log.debug("Turn the busy flag on: %s" % self.device)
stderr = subprocess.PIPE, child_pid = os.fork()
args = [ if child_pid == 0:
self.cbox.prefs["Programs"]["mkfs-data"], ## make the filesystem
os.path.join(self.__dmDir, self.name)]) proc = subprocess.Popen(
proc.wait() shell = False,
## remove the mapping - for every exit status stdin = None,
self.__umount_luks() stdout = subprocess.PIPE,
if proc.returncode != 0: stderr = subprocess.PIPE,
err_msg = "Could not create the filesystem: %s" % (proc.stderr.read().strip(), ) args = [
self.cbox.log.error(err_msg) self.cbox.prefs["Programs"]["nice"],
## remove the luks mapping self.cbox.prefs["Programs"]["mkfs"],
raise CBCreateError(err_msg) "-t", fs_type,
devnull.close() os.path.join(self.__dmDir, self.name)])
(stdou, stderr) = proc.communicate()
## wait to allow error detection
if proc.returncode == 0:
time.sleep(5)
## skip cleanup stuff (as common for sys.exit)
os._exit(0)
else:
os.waitpid(child_pid, 0)
self.set_name(old_name)
self.set_busy(False)
self.cbox.log.debug("Turn the busy flag off: %s" % self.device)
## remove the mapping - for every exit status
self.__umount_luks()
bg_task = threading.Thread(target=format_luks)
bg_task.setDaemon(True)
bg_task.start()
time.sleep(3)
## if the thread exited very fast, then it failed
if not bg_task.isAlive():
raise CBCreateError("Failed to initilize device: %s" % self.device)
def __clean_mount_dirs(self): def __clean_mount_dirs(self):

View File

@ -46,6 +46,7 @@ class CryptoBox:
self.prefs = cbxSettings.CryptoBoxSettings(config_file) self.prefs = cbxSettings.CryptoBoxSettings(config_file)
self.__run_tests() self.__run_tests()
self.__containers = [] self.__containers = []
self.busy_devices = {}
self.reread_container_list() self.reread_container_list()

View File

@ -455,7 +455,8 @@ Languages = list(min=1,default=list("en"))
[Programs] [Programs]
cryptsetup = fileExecutable(default="/sbin/cryptsetup") cryptsetup = fileExecutable(default="/sbin/cryptsetup")
mkfs-data = fileExecutable(default="/sbin/mkfs.ext3") mkfs = fileExecutable(default="/sbin/mkfs")
nice = fileExecutable(default="/usr/bin/nice")
blkid = fileExecutable(default="/sbin/blkid") blkid = fileExecutable(default="/sbin/blkid")
blockdev = fileExecutable(default="/sbin/blockdev") blockdev = fileExecutable(default="/sbin/blockdev")
mount = fileExecutable(default="/bin/mount") mount = fileExecutable(default="/bin/mount")

View File

@ -191,6 +191,14 @@ class CryptoBoxPlugin:
return self.plugin_visibility return self.plugin_visibility
def reset(self):
"""Reinitialize the plugin.
This function should be called before every run
"""
self.hdf = {}
def get_test_class(self): def get_test_class(self):
"""Return the unittest class of the feature. """Return the unittest class of the feature.
""" """

View File

@ -103,11 +103,13 @@ class WebInterfaceDataset(dict):
is_plain = (container.get_type() == \ is_plain = (container.get_type() == \
cbxContainer.CONTAINERTYPES["plain"]) and 1 or 0 cbxContainer.CONTAINERTYPES["plain"]) and 1 or 0
is_mounted = container.is_mounted() and 1 or 0 is_mounted = container.is_mounted() and 1 or 0
is_busy = container.is_busy() and 1 or 0
self["Data.CurrentDisk.device"] = container.get_device() self["Data.CurrentDisk.device"] = container.get_device()
self["Data.CurrentDisk.name"] = container.get_name() self["Data.CurrentDisk.name"] = container.get_name()
self["Data.CurrentDisk.encryption"] = is_encrypted self["Data.CurrentDisk.encryption"] = is_encrypted
self["Data.CurrentDisk.plaintext"] = is_plain self["Data.CurrentDisk.plaintext"] = is_plain
self["Data.CurrentDisk.active"] = is_mounted self["Data.CurrentDisk.active"] = is_mounted
self["Data.CurrentDisk.busy"] = is_busy
self["Data.CurrentDisk.size"] = cbxTools.get_blockdevice_size_humanly( self["Data.CurrentDisk.size"] = cbxTools.get_blockdevice_size_humanly(
container.get_device()) container.get_device())
if is_mounted: if is_mounted:
@ -133,10 +135,12 @@ class WebInterfaceDataset(dict):
is_plain = (container.get_type() == \ is_plain = (container.get_type() == \
cbxContainer.CONTAINERTYPES["plain"]) and 1 or 0 cbxContainer.CONTAINERTYPES["plain"]) and 1 or 0
is_mounted = container.is_mounted() and 1 or 0 is_mounted = container.is_mounted() and 1 or 0
is_busy = container.is_busy() and 1 or 0
self["Data.Disks.%d.device" % avail_counter] = container.get_device() self["Data.Disks.%d.device" % avail_counter] = container.get_device()
self["Data.Disks.%d.name" % avail_counter] = container.get_name() self["Data.Disks.%d.name" % avail_counter] = container.get_name()
self["Data.Disks.%d.encryption" % avail_counter] = is_encrypted self["Data.Disks.%d.encryption" % avail_counter] = is_encrypted
self["Data.Disks.%d.plaintext" % avail_counter] = is_plain self["Data.Disks.%d.plaintext" % avail_counter] = is_plain
self["Data.Disks.%d.busy" % avail_counter] = is_busy
self["Data.Disks.%d.active" % avail_counter] = is_mounted self["Data.Disks.%d.active" % avail_counter] = is_mounted
self["Data.Disks.%d.size" % avail_counter] = \ self["Data.Disks.%d.size" % avail_counter] = \
cbxTools.get_blockdevice_size_humanly(container.get_device()) cbxTools.get_blockdevice_size_humanly(container.get_device())

View File

@ -252,28 +252,20 @@ class WebInterfaceSites:
""" returns a function that is suitable for handling a cherrypy """ returns a function that is suitable for handling a cherrypy
page request page request
""" """
def handler(self, **args): def handler(self, weblang="", device=None, redirect=None, message_keep=None, **args):
"""this function handles a cherrypy page request """this function handles a cherrypy page request
""" """
plugin.reset()
self.__reset_dataset() self.__reset_dataset()
self.__check_environment() self.__check_environment()
## set web interface language self.__set_web_lang(weblang)
try:
self.__set_web_lang(args["weblang"])
del args["weblang"]
except KeyError:
self.__set_web_lang("")
## we always read the "device" setting - otherwise volume-plugin ## we always read the "device" setting - otherwise volume-plugin
## links would not work easily ## links would not work easily
## (see "volume_props" linking to "volume_format_fs") ## (see "volume_props" linking to "volume_format_fs")
## it will get ignored for non-volume plugins ## it will get ignored for non-volume plugins
try: plugin.device = None
plugin.device = None if device and self.__set_device(device):
if self.__set_device(args["device"]): plugin.device = device
plugin.device = args["device"]
del args["device"]
except KeyError:
plugin.device = None
## check the device argument of volume plugins ## check the device argument of volume plugins
if "volume" in plugin.plugin_capabilities: if "volume" in plugin.plugin_capabilities:
## initialize the dataset of the selected device if necessary ## initialize the dataset of the selected device if necessary
@ -285,22 +277,18 @@ class WebInterfaceSites:
## check if there is a "redirect" setting - this will override ## check if there is a "redirect" setting - this will override
## the return value of the do_action function ## the return value of the do_action function
## (e.g. useful for umount-before-format) ## (e.g. useful for umount-before-format)
try: override_next_template = None
if args["redirect"]: if redirect:
override_next_template = { "plugin":args["redirect"] } override_next_template = { "plugin": redirect }
if "volume" in plugin.plugin_capabilities: if "volume" in plugin.plugin_capabilities:
override_next_template["values"] = {"device":plugin.device} override_next_template["values"] = {"device":plugin.device}
del args["redirect"]
except KeyError:
override_next_template = None
## check for information to be kept after the last call ## check for information to be kept after the last call
try: if message_keep:
keep_values = args["message_keep"] for (key, value) in message_keep["dataset"].items():
del args["message_keep"]
for key, value in keep_values["dataset"].items():
self.__dataset[key] = value self.__dataset[key] = value
except KeyError: ## check if the device is busy
keep_values = None if plugin.device and self.cbox.get_container(plugin.device).is_busy():
return self.__render("volume_busy")
## call the plugin handler ## call the plugin handler
next_template = plugin.do_action(**args) next_template = plugin.do_action(**args)
## for 'volume' plugins: reread the dataset of the current disk ## for 'volume' plugins: reread the dataset of the current disk
@ -330,7 +318,7 @@ class WebInterfaceSites:
and self.__plugin_manager.get_plugin(next_template["plugin"]): and self.__plugin_manager.get_plugin(next_template["plugin"]):
value_dict = dict(next_template["values"]) value_dict = dict(next_template["values"])
## force the current weblang attribute - otherwise it gets lost ## force the current weblang attribute - otherwise it gets lost
value_dict["weblang"] = self.__dataset["Settings.Language"] value_dict["weblang"] = self.lang_order[0]
## check for warnings/success messages, that should be kept ## check for warnings/success messages, that should be kept
if "Data.Success" in plugin.hdf.keys() \ if "Data.Success" in plugin.hdf.keys() \
or "Data.Warning" in plugin.hdf.keys(): or "Data.Warning" in plugin.hdf.keys():
@ -354,7 +342,6 @@ class WebInterfaceSites:
@cherrypy.expose @cherrypy.expose
@__request_auth
def test(self, weblang=""): def test(self, weblang=""):
"""test authentication - this function may be safely removed """test authentication - this function may be safely removed
""" """
@ -509,7 +496,10 @@ class WebInterfaceSites:
if key == "LINK": if key == "LINK":
return return
try: try:
node.setValue("", translator.ugettext(node.value())) #TODO: we should use unicode - or not?
#node.setValue("", translator.ugettext(node.value()))
## quite obscure: ugettext can handle None - gettext breaks instead
node.setValue("", str(translator.gettext(node.value())))
except UnicodeEncodeError, err_msg: except UnicodeEncodeError, err_msg:
self.cbox.log.info( self.cbox.log.info(
"Failed unicode encoding for gettext: %s - %s" \ "Failed unicode encoding for gettext: %s - %s" \

View File

@ -22,6 +22,17 @@ Button {
} }
AdviceMessage {
VolumeIsBusy {
Title = Disk is busy
Text = This disk is currently busy. Please wait for a moment.
Link.Rel = /
Link.Text = Show all disks
}
}
WarningMessage { WarningMessage {
AccessDenied { AccessDenied {

View File

@ -119,15 +119,31 @@ def:show_volume_icon(volume) ?><?cs
# show the appropriate icon for the current state of the volume ?><?cs # show the appropriate icon for the current state of the volume ?><?cs
if:volume.active ?><?cs if:volume.active ?><?cs
if:volume.encryption ?><?cs if:volume.encryption ?><?cs
set:filename='volume_active_crypto.gif' ?><?cs if:volume.busy ?><?cs
set:filename='volume_active_crypto_busy.gif' ?><?cs
else ?><?cs
set:filename='volume_active_crypto.gif' ?><?cs
/if ?><?cs
else ?><?cs else ?><?cs
set:filename='volume_active_plain.gif' ?><?cs if:volume.busy ?><?cs
set:filename='volume_active_plain_busy.gif' ?><?cs
else ?><?cs
set:filename='volume_active_plain.gif' ?><?cs
/if ?><?cs
/if ?><?cs /if ?><?cs
else ?><?cs else ?><?cs
if:volume.encryption ?><?cs if:volume.encryption ?><?cs
set:filename='volume_passive_crypto.gif' ?><?cs if:volume.busy ?><?cs
set:filename='volume_passive_crypto_busy.gif' ?><?cs
else ?><?cs
set:filename='volume_passive_crypto.gif' ?><?cs
/if ?><?cs
else ?><?cs else ?><?cs
set:filename='volume_passive_plain.gif' ?><?cs if:volume.busy ?><?cs
set:filename='volume_passive_plain_busy.gif' ?><?cs
else ?><?cs
set:filename='volume_passive_plain.gif' ?><?cs
/if ?><?cs
/if ?><?cs /if ?><?cs
/if ?><img src="<?cs call:link('cryptobox-misc/' + filename,'','','','') ?>" alt="icon: volume" /><?cs /if ?><img src="<?cs call:link('cryptobox-misc/' + filename,'','','','') ?>" alt="icon: volume" /><?cs
/def ?><?cs /def ?><?cs

8
templates/volume_busy.cs Normal file
View File

@ -0,0 +1,8 @@
<?cs # $Id$ ?>
<?cs # display a warning because of a busy volume ?>
<?cs call:handle_messages() ?>
<?cs call:hint('VolumeIsBusy') ?>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB