diff --git a/plugins/partition/partition.py b/plugins/partition/partition.py index 791c693..8b44d96 100644 --- a/plugins/partition/partition.py +++ b/plugins/partition/partition.py @@ -27,7 +27,7 @@ import subprocess import os import re import logging -import cryptobox.core.tools as cbox_tools +import cryptobox.core.blockdevice as blockdevice_tools import cryptobox.plugins.base from cryptobox.core.exceptions import * @@ -58,15 +58,19 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): self.__prepare_dataset() ## retrieve some values from 'args' - defaults are empty self.blockdevice = self.__get_selected_device(args) - self.with_config_partition = self.__is_with_config_partition() + if self.blockdevice: + self.with_config_partition = self.__is_with_config_partition() + self.blockdevice_size = self.__get_available_device_size(self.blockdevice) + else: + self.with_config_partition = False + self.blockdevice_size = 0 self.cbox.log.debug( "partition plugin: selected device=%s" % str(self.blockdevice)) - self.blockdevice_size = self.__get_available_device_size(self.blockdevice) ## no (or invalid) device was supplied if not self.blockdevice: return self.__action_select_device() ## exit if the blockdevice is not writeable - if not os.access(self.blockdevice, os.W_OK): + if not os.access(self.blockdevice.devnodes[0], os.W_OK): self.hdf["Data.Warning"] = "DeviceNotWriteable" return self.__action_select_device() ## no confirm setting? @@ -91,15 +95,19 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): "partition: invalid partition number to delete (%s)" % del_args[0]) return self.__action_select_device() return self.__action_del_partition(args, num_part) - else: # for "select_device" and for invalid targets + else: + ## for "select_device" and for invalid targets return self.__action_select_device() def get_status(self): """The status of this plugin is the selected device and some information. """ - return "%s / %s / %s" % (self.blockdevice, self.blockdevice_size, - self.with_config_partition) + if not self.blockdevice: + return "no blockdevice selected" + else: + return "%s / %s / %s" % (self.blockdevice.name, + self.blockdevice_size, self.with_config_partition) def get_warnings(self): @@ -131,7 +139,13 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): """Check the selected device (valid, not busy, ...). """ try: - blockdevice = args["block_device"] + blockdevice_name = args["block_device"] + found = [ dev for dev in + blockdevice_tools.Blockdevices().get_partitionable_devices() + if dev.name == blockdevice_name ] + if not found: + return None + blockdevice = found[0] except KeyError: return None if not self.__is_device_valid(blockdevice): @@ -149,8 +163,6 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): return False if not self.cbox.is_device_allowed(blockdevice): return False - if not blockdevice in cbox_tools.get_parent_blockdevices(): - return False return True @@ -158,9 +170,10 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): """check if the device (or one of its partitions) is mounted """ ## the config partition is ignored, as it will get unmounted if necessary - for cont in self.cbox.get_container_list(): - if cbox_tools.is_part_of_blockdevice(blockdevice, cont.get_device()) \ - and cont.is_mounted(): + for dev in blockdevice.children: + container = self.cbox.get_container( + blockdevice_tools.get_blockdevice(dev).devnodes[0]) + if container and (container.is_mounted() or container.is_busy()): return True return False @@ -168,15 +181,17 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): def __action_select_device(self): """Show a form to select the device for partitioning. """ - block_devices = [e - for e in cbox_tools.get_parent_blockdevices() - if self.cbox.is_device_allowed(e)] + block_devices = [ e for e in + blockdevice_tools.Blockdevices().get_partitionable_devices() + if self.cbox.is_device_allowed(e) ] counter = 0 for dev in block_devices: - self.hdf[self.hdf_prefix + "BlockDevices.%d.name" % counter] = dev + self.hdf[self.hdf_prefix + "BlockDevices.%d.name" % counter] = \ + dev.name self.hdf[self.hdf_prefix + "BlockDevices.%d.size" % counter] = \ - cbox_tools.get_blockdevice_size_humanly(dev) - self.cbox.log.debug("found a suitable block device: %s" % dev) + dev.size_human + self.cbox.log.debug("found a suitable block device: %s" % \ + dev.devnodes[0]) counter += 1 if self.with_config_partition: self.hdf[self.hdf_prefix + "CreateConfigPartition"] = "1" @@ -189,7 +204,7 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): def __action_add_partition(self, args): """Add a selected partition to the currently proposed partition table. """ - self.hdf[self.hdf_prefix + "Device"] = self.blockdevice + self.hdf[self.hdf_prefix + "Device"] = self.blockdevice.name self.hdf[self.hdf_prefix + "Device.Size"] = self.blockdevice_size parts = self.__get_partitions_from_args(args) self.__set_partition_data(parts) @@ -199,7 +214,7 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): def __action_del_partition(self, args, part_num): """Remove a partition from the proposed partition table. """ - self.hdf[self.hdf_prefix + "Device"] = self.blockdevice + self.hdf[self.hdf_prefix + "Device"] = self.blockdevice.name self.hdf[self.hdf_prefix + "Device.Size"] = self.blockdevice_size parts = self.__get_partitions_from_args(args) ## valid partition number to be deleted? @@ -215,8 +230,11 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): parts = self.__get_partitions_from_args(args) if parts: self.__set_partition_data(parts) - if cbox_tools.is_part_of_blockdevice(self.blockdevice, - self.cbox.prefs.get_active_partition()): + ## umount config partition if necessary + config_partition = self.cbox.prefs.get_active_partition() + if [ dev for dev in self.blockdevice.children + if config_partition in + blockdevice_tools.get_blockdevice(dev).devnodes ]: self.cbox.prefs.umount_partition() if not self.__run_fdisk(parts): self.hdf["Data.Warning"] = "Plugins.partition.PartitioningFailed" @@ -257,9 +275,10 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): format_ok = False if format_ok: self.hdf["Data.Success"] = "Plugins.partition.Partitioned" + return { "plugin":"system_preferences", "values":[] } else: self.hdf["Data.Warning"] = "Plugins.partition.FormattingFailed" - return "empty" + return "empty" else: return self.__action_add_partition(args) @@ -270,15 +289,17 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): import types ## we do not have to take special care for a possible config partition parts = [ { "size": self.blockdevice_size, "type": "windows" } ] - ## umount partition if necessary - if cbox_tools.is_part_of_blockdevice(self.blockdevice, - self.cbox.prefs.get_active_partition()): + ## umount config partition if necessary + config_partition = self.cbox.prefs.get_active_partition() + if [ dev for dev in self.blockdevice.children + if config_partition in + blockdevice_tools.get_blockdevice(dev).devnodes ]: self.cbox.prefs.umount_partition() ## partition it if not self.__run_fdisk(parts): self.hdf["Data.Warning"] = "Plugins.partition.PartitioningFailed" return None - ## "formatPartitions" is a generator, returning device names and bolean values + ## "formatPartitions" is a generator, returning device names and boolean values result = [e for e in self.__format_partitions(parts) if type(e) == types.BooleanType] if self.with_config_partition: @@ -317,11 +338,12 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): for ptype in PARTTYPES.keys(): self.hdf[self.hdf_prefix + "Types.%s" % ptype] = ptype ## store the currently existing partitions of the choosen block device - current_containers = [ e for e in self.cbox.get_container_list() - if cbox_tools.is_part_of_blockdevice(self.blockdevice, e.get_device()) ] + current_containers = [ blockdevice_tools.get_blockdevice(dev) + for dev in self.blockdevice.children + if blockdevice_tools.get_blockdevice(dev).is_storage() ] for (index, cont) in enumerate(current_containers): self.hdf[self.hdf_prefix + "ExistingContainers.%d" % index] = \ - cont.get_device() + cont.name def __get_partitions_from_args(self, args): @@ -361,11 +383,11 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): """calculate the available size (MB) of the device also consider a (possible) configuration partition """ - device_size = cbox_tools.get_blockdevice_size(device) - if device_size < 0: - return 0 + device_size = device.size if self.with_config_partition: device_size -= CONFIGPARTITION["size"] + if device_size < 0: + return 0 return device_size @@ -378,7 +400,12 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): if not active: return True ## check if the active one is part of the current device - return cbox_tools.is_part_of_blockdevice(self.blockdevice, active) + if [ dev for dev in self.blockdevice.children + if active in + blockdevice_tools.get_blockdevice(dev).devnodes ]: + return True + else: + return False return False @@ -402,7 +429,7 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): "plugin", os.path.join(self.plugin_dir, "root_action.py"), "partition", - self.blockdevice]) + self.blockdevice.devnodes[0]]) for line in self.__get_sfdisk_layout(parts, is_filled): proc.stdin.write(line + "\n") #TODO: if running inside of an uml, then sfdisk hangs at "nanosleep({3,0})" @@ -455,22 +482,22 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): part_num = 1 ## maybe a config partition? if self.with_config_partition: - dev_name = self.__get_partition_name(self.blockdevice, part_num) + dev_name = self.__get_partition_device(self.blockdevice, part_num) self.cbox.log.info("formatting config partition (%s)" % dev_name) if self.__format_one_partition(dev_name, CONFIGPARTITION["fs"]): self.__set_label_of_partition(dev_name, self.cbox.prefs["Main"]["ConfigVolumeLabel"]) part_num += 1 ## the first data partition - dev_name = self.__get_partition_name(self.blockdevice, part_num) + dev_name = self.__get_partition_device(self.blockdevice, part_num) part_type = PARTTYPES[parts[0]["type"]][1] self.cbox.log.info("formatting partition (%s) as '%s'" % (dev_name, part_type)) yield self.__format_one_partition(dev_name, part_type) del parts[0] ## other data partitions - part_num = 5 + part_num += 1 while parts: - dev_name = self.__get_partition_name(self.blockdevice, part_num) + dev_name = self.__get_partition_device(self.blockdevice, part_num) part_type = PARTTYPES[parts[0]["type"]][1] self.cbox.log.info("formatting partition (%s) as '%s'" % \ (dev_name, part_type)) @@ -480,17 +507,26 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): return - def __get_partition_name(self, blockdev, number): + def __get_partition_device(self, blockdev, number): """Return the devicename of a specific partition of a device No tests are performed, whether the partition exists or not. """ - if re.search("[0-9]$", blockdev): - ## blockdev ends with a digit, so it is a partition, we insert a 'p' - return "%sp%d" % (blockdev, number) - else: - ## whole disk, no 'p' necessary - return "%s%d" % (blockdev, number) + valid_children = [] + ## filter the storage devices + for child in blockdev.children: + childdev = blockdevice_tools.get_blockdevice(child) + if childdev and childdev.is_storage(): + valid_children.append(childdev) + sorted = blockdevice_tools.get_sorted_devices(valid_children) + if number <= len(sorted): + childdev = sorted[number-1] + if childdev: + return childdev.devnodes[0] + self.cbox.log.warn("Failed to get the partition name (%s, %d)" % \ + (blockdev, number)) + ## return some guessed value - we should never get here ... + return "%s%d" % (blockdev.devnodes[0], number) def __format_one_partition(self, dev_name, fs_type): @@ -502,7 +538,8 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): if e.get_device() == dev_name] ## call "mkfs" try: - cont = cryptobox.core.container.CryptoBoxContainer(dev_name, self.cbox) + format_dev = blockdevice_tools.get_blockdevice(dev_name) + cont = cryptobox.core.container.CryptoBoxContainer(format_dev, 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) diff --git a/src/cryptobox/core/blockdevice.py b/src/cryptobox/core/blockdevice.py index e5ee214..cfe7bfe 100644 --- a/src/cryptobox/core/blockdevice.py +++ b/src/cryptobox/core/blockdevice.py @@ -38,7 +38,7 @@ LOGGER = logging.getLogger("CryptoBox") DEFAULT_SYSBLOCK_DIR = '/sys/block' DEFAULT_DEVNODE_DIR = '/dev' -MINIMUM_STORAGE_SIZE = 20 +MINIMUM_STORAGE_SIZE = 10 MAJOR_DEVNUM_RAM = 1 MAJOR_DEVNUM_LOOP = 7 MAJOR_DEVNUM_MD_RAID = 9 @@ -98,6 +98,13 @@ class Blockdevice: sysblock_dir=DEFAULT_SYSBLOCK_DIR, devnode_dir=DEFAULT_DEVNODE_DIR): """initialize the blockdevice + + @type dev: string + @param dev: The /sys/block/ subdirectory describing a blockdevice + @type sysblock_dir: string + @param sysblock_dir: The linux /sys/ directory. Default is '/sys'. + @type devnode_dir: string + @param devnode_dir: The linux /dev/ directory. Default is '/dev'. """ self.devdir = dev self.devnode_dir = devnode_dir @@ -122,6 +129,9 @@ class Blockdevice: usually we will have to reset the cache, too just in case of first-time initialization, this is not necessary + + @type empty_cache: boolean + @param empty_cache: Whether to discard the cached information or not. """ CACHE.reset(["blockdevice_info", self.name]) self.devnum = self.__get_major_minor() @@ -141,6 +151,9 @@ class Blockdevice: """check if the device is usable and valid causes of invalidity: ram device, loop device, removable device + + @rtype: boolean + @return: 'True' for a valid blockdevice """ if not self.devnodes: return False @@ -165,6 +178,9 @@ class Blockdevice: def is_storage(self): """return if this device can be used as a storage + + @rtype: boolean + @return: 'True' for a device usable as a storage """ ## check the cache first cache_link = ["blockdevice_info", self.name, "is_storage"] @@ -755,11 +771,24 @@ def get_blockdevice(dev, sysblock_dir=DEFAULT_SYSBLOCK_DIR, devnode_dir=DEFAULT_DEVNODE_DIR): if os.path.isabs(dev): - if os.path.isfile(os.path.join(dev, "dev")): - devdir = dev + ## it is an absolute path + if dev.startswith(devnode_dir): + ## it is the name of a devicenode (e.g.: '/dev/hda1') + found_dev = [ a_dev for a_dev in Blockdevices( + sysblock_dir, devnode_dir).get_devices() + if dev in a_dev.devnodes ] + if found_dev: + devdir = found_dev[0].devdir + else: + return None else: - return None + ## it is the path of a /sys/ subdirectory (e.g.: '/sys/block/hda') + if os.path.isfile(os.path.join(dev, "dev")): + devdir = dev + else: + return None else: + ## the name of a blockdevice (e.g.: 'dm-0') for one_devdir in find_blockdevices(sysblock_dir): if os.path.basename(one_devdir) == dev: devdir = one_devdir @@ -836,6 +865,25 @@ def find_lvm_pv(): return result[:] +def get_sorted_devices(names): + """return the names of devices in a sorted order + + e.g.: "hda1", "hda5", "hda6", ..., "hda10", "hda11" + """ + # TODO: implement this for devicenames like "hda12" + def compare_device_names(x, y): + if x.name < y.name: + return -1 + elif x.name == y.name: + return 0 + else: + return 1 + + result = names[:] + result.sort(cmp=compare_device_names) + return result + + def _load_preferences(): prefs = cryptobox.core.settings.get_current_settings() if not prefs is None: diff --git a/src/cryptobox/core/main.py b/src/cryptobox/core/main.py index 619edaa..9ce1d6d 100644 --- a/src/cryptobox/core/main.py +++ b/src/cryptobox/core/main.py @@ -213,6 +213,11 @@ class CryptoBox: also check, if the device is readable and writeable for the current user """ import types + ## if "device" is a string, then turn it into a blockdevice object + if type(device) == types.StringType: + device = blockdevice.get_blockdevice(device) + if device is None: + return False allowed = self.prefs["Main"]["AllowedDevices"] if type(allowed) == types.StringType: allowed = [allowed] diff --git a/src/cryptobox/core/tools.py b/src/cryptobox/core/tools.py deleted file mode 100644 index 3c08a70..0000000 --- a/src/cryptobox/core/tools.py +++ /dev/null @@ -1,248 +0,0 @@ -# -# Copyright 2006 sense.lab e.V. -# -# This file is part of the CryptoBox. -# -# The CryptoBox is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# The CryptoBox is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with the CryptoBox; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# - -"""Some useful functions for the CryptoBox. -""" - -__revision__ = "$Id" - -import logging -import os -import re - -LOGGER = logging.getLogger("CryptoBox") - - -def get_available_partitions(): - "retrieve a list of all available containers" - ret_list = [] - try: - ## the following reads all lines of /proc/partitions and adds the mentioned devices - fpart = open("/proc/partitions", "r") - try: - line = fpart.readline() - while line: - p_details = line.split() - if (len(p_details) == 4): - ## the following code prevents double entries like /dev/hda and /dev/hda1 - (p_major, p_minor, p_size, p_device) = p_details - ## ignore lines with: invalid minor/major or extend partitions (size=1) - if re.search('^[0-9]*$', p_major) and \ - re.search('^[0-9]*$', p_minor) and (p_size != "1"): - ## for some parent devices we have to remove a 'p' (partition) - ## an example are partitionable mdadm raid devices (e.g. md1p1) - p_parent = re.sub('p?[1-9]?[0-9]$', '', p_device) - if p_parent == p_device: - if [e for e in ret_list - if re.search('^' + p_parent + 'p?[1-9]?[0-9]$', e)]: - ## major partition - its children are already in the list - pass - else: - ## major partition - but there are no children for now - ret_list.append(p_device) - else: - ## minor partition - remove parent if necessary - if p_parent in ret_list: - ret_list.remove(p_parent) - ret_list.append(p_device) - line = fpart.readline() - finally: - fpart.close() - return [ get_absolute_devicename(e) for e in ret_list ] - except IOError: - LOGGER.warning("Could not read /proc/partitions") - return [] - - -def get_absolute_devicename(shortname): - """ returns the absolute file name of a device (e.g.: "hda1" -> "/dev/hda1") - this does also work for device mapper devices - if the result is non-unique, one arbitrary value is returned - """ - if re.search('^/', shortname): - return shortname - default = os.path.join("/dev", shortname) - if os.path.exists(default): - return default - result = find_major_minor_of_device(shortname) - ## if no valid major/minor was found -> exit - if not result: - return default - (major, minor) = result - ## for device-mapper devices (major == 254) ... - if major == 254: - result = find_major_minor_device("/dev/mapper", major, minor) - if result: - return result[0] - ## now check all files in /dev - result = find_major_minor_device("/dev", major, minor) - if result: - return result[0] - return default - - -def find_major_minor_of_device(device): - """Return the major/minor numbers of a block device. - """ - if re.match("/", device) or \ - not os.path.exists(os.path.join(os.path.sep, "sys", "block", device)): - ## maybe it is an absolute device name - if not os.path.exists(device): - return None - ## okay - it seems to to a device node - rdev = os.stat(device).st_rdev - return (os.major(rdev), os.minor(rdev)) - blockdev_info_file = os.path.join(os.path.join( - os.path.sep,"sys","block", device), "dev") - try: - f_blockdev_info = open(blockdev_info_file, "r") - blockdev_info = f_blockdev_info.read() - f_blockdev_info.close() - (str_major, str_minor) = blockdev_info.split(":") - ## numeric conversion - try: - major = int(str_major) - minor = int(str_minor) - return (major, minor) - except ValueError: - ## unknown device numbers -> stop guessing - return None - except IOError: - pass - return None - - -def find_major_minor_device(dirpath, major, minor): - """Returns the names of devices with the specified major and minor number. - """ - collected = [] - try: - subdirs = [os.path.join(dirpath, e) for e in os.listdir(dirpath) - if (not os.path.islink(os.path.join(dirpath, e))) and \ - os.path.isdir(os.path.join(dirpath, e))] - ## do a recursive call to parse the directory tree - for dirs in subdirs: - collected.extend(find_major_minor_device(dirs, major, minor)) - ## filter all device inodes in this directory - collected.extend([os.path.realpath(os.path.join(dirpath, e)) - for e in os.listdir(dirpath) - if (os.major(os.stat(os.path.join(dirpath, e)).st_rdev) == major) \ - and (os.minor(os.stat(os.path.join(dirpath, e)).st_rdev) == minor)]) - ## remove double entries - result = [] - for item in collected: - if item not in result: - result.append(item) - return result - except OSError: - return [] - - -def get_parent_blockdevices(): - """Return a list of all block devices that contain other devices. - """ - devs = [] - for line in file("/proc/partitions"): - p_details = line.split() - ## we expect four values - otherwise continue with next iteration - if len(p_details) != 4: - continue - (p_major, p_minor, p_size, p_device) = p_details - ## we expect numeric values in the first two columns - if re.search(r'\D', p_major) or re.search(r'\D', p_minor): - continue - ## now let us check, if it is a (parent) block device or a partition - if not os.path.isdir(os.path.join(os.path.sep, "sys", "block", p_device)): - continue - devs.append(p_device) - return [ get_absolute_devicename(e) for e in devs ] - - -def is_part_of_blockdevice(parent, subdevice): - """Check if the given block device is a parent of 'subdevice'. - - e.g. for checking if a partition belongs to a block device - """ - try: - (par_major, par_minor) = find_major_minor_of_device(parent) - (sub_major, sub_minor) = find_major_minor_of_device(subdevice) - except TypeError: - ## at least one of these devices did not return a valid major/minor combination - return False - ## search the entry below '/sys/block' belonging to the parent - root = os.path.join(os.path.sep, 'sys', 'block') - for bldev in os.listdir(root): - blpath = os.path.join(root, bldev, 'dev') - if os.access(blpath, os.R_OK): - try: - if (str(par_major), str(par_minor)) == tuple([e - for e in file(blpath)][0].strip().split(":",1)): - parent_path = os.path.join(root, bldev) - break - except (IndexError, OSError): - pass - else: - ## no block device with this major/minor combination found below '/sys/block' - return False - for subbldev in os.listdir(parent_path): - subblpath = os.path.join(parent_path, subbldev, "dev") - if os.access(subblpath, os.R_OK): - try: - if (str(sub_major), str(sub_minor)) == tuple([e - for e in file(subblpath)][0].strip().split(":",1)): - ## the name of the subdevice node is not important - we found it! - return True - except (IndexError, OSError): - pass - return False - - -def get_blockdevice_size(device): - """Return the size of a blockdevice in megabyte. - """ - if not device: - return -1 - try: - rdev = os.stat(device).st_rdev - except OSError: - return -1 - minor = os.minor(rdev) - major = os.major(rdev) - for line in file("/proc/partitions"): - try: - elements = line.split() - if len(elements) != 4: - continue - if (int(elements[0]) == major) and (int(elements[1]) == minor): - return int(elements[2])/1024 - except ValueError: - pass - return -1 - - -def get_blockdevice_size_humanly(device): - """Return a human readable size of a blockdevice. - """ - size = get_blockdevice_size(device) - if size > 5120: - return "%dGB" % int(size/1024) - else: - return "%dMB" % size - diff --git a/src/cryptobox/web/sites.py b/src/cryptobox/web/sites.py index 7e80bbd..7d3c6c1 100644 --- a/src/cryptobox/web/sites.py +++ b/src/cryptobox/web/sites.py @@ -324,7 +324,7 @@ class WebInterfaceSites: ## it will get ignored for non-volume plugins plugin.device = None if device and self.__set_device(device): - plugin.device = self.cbox.get_container(device).device + plugin.device = self.cbox.get_container(device) ## check the device argument of volume plugins if "volume" in plugin.plugin_capabilities: ## initialize the dataset of the selected device if necessary