* fixed some minor problems caused by revision [1162]
* try to refresh the block device cache once if a device was requested, that is currently not in the list (after a minimum cache age passed) * added an implicit sorting function for block devices (based on major/minor values) * try all found device nodes, in case the first does not exist anymore (e.g. temporary device nodes)
This commit is contained in:
parent
22021866b4
commit
7b0de22e3a
|
@ -53,6 +53,7 @@ MAJOR_DEVNUM_MD_RAID = 9
|
||||||
## cache settings
|
## cache settings
|
||||||
CACHE_ENABLED = True
|
CACHE_ENABLED = True
|
||||||
CACHE_EXPIRE_SECONDS = 120
|
CACHE_EXPIRE_SECONDS = 120
|
||||||
|
CACHE_MINIMUM_AGE_FOR_REBUILD = 3
|
||||||
CACHE_MONITOR_FILE = '/proc/partitions'
|
CACHE_MONITOR_FILE = '/proc/partitions'
|
||||||
|
|
||||||
## useful for manual profiling
|
## useful for manual profiling
|
||||||
|
@ -121,7 +122,12 @@ class Blockdevice:
|
||||||
"""
|
"""
|
||||||
self.devnode_dir = devnode_dir
|
self.devnode_dir = devnode_dir
|
||||||
self.sysblock_dir = sysblock_dir
|
self.sysblock_dir = sysblock_dir
|
||||||
self.major, self.minor = major_minor
|
try:
|
||||||
|
self.major, self.minor = major_minor
|
||||||
|
except ValueError:
|
||||||
|
# invalid device given
|
||||||
|
raise cryptobox.core.exceptions.CBInternalError(
|
||||||
|
"invalid block device requested: %s" % str(major_minor))
|
||||||
# find the devdir (usually in /sys/block/)
|
# find the devdir (usually in /sys/block/)
|
||||||
for devdir, one_major_minor in find_blockdevices(self.sysblock_dir).items():
|
for devdir, one_major_minor in find_blockdevices(self.sysblock_dir).items():
|
||||||
if major_minor == one_major_minor:
|
if major_minor == one_major_minor:
|
||||||
|
@ -147,6 +153,16 @@ class Blockdevice:
|
||||||
self.reset(empty_cache=False)
|
self.reset(empty_cache=False)
|
||||||
|
|
||||||
|
|
||||||
|
def __cmp__(self, other):
|
||||||
|
if (self.major < other.major) or \
|
||||||
|
((self.major == other.major) and (self.minor < other.minor)):
|
||||||
|
return -1
|
||||||
|
elif (self.major == other.major) and (self.minor == other.minor):
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
def reset(self, empty_cache=True):
|
def reset(self, empty_cache=True):
|
||||||
"""reread the data of the device
|
"""reread the data of the device
|
||||||
|
|
||||||
|
@ -171,6 +187,23 @@ class Blockdevice:
|
||||||
self.uuid = attributes["uuid"]
|
self.uuid = attributes["uuid"]
|
||||||
|
|
||||||
|
|
||||||
|
def get_device(self):
|
||||||
|
"""Returns the path of a device node representing this device
|
||||||
|
|
||||||
|
e.g.: /dev/hdc1
|
||||||
|
"""
|
||||||
|
# we need to check, which listed device nodes exists
|
||||||
|
# This is necessary, since temporary device nodes seem to be created
|
||||||
|
# immediately after partitioning a disk (e.g. "/dev/.tmp-22-1").
|
||||||
|
for dev in self.devnodes:
|
||||||
|
if os.path.exists(dev):
|
||||||
|
return dev
|
||||||
|
# none of the device nodes exists
|
||||||
|
self.cbox.log.warn("No valid device node found for %s out of %s" % \
|
||||||
|
(self.name, str(self.devnodes)))
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def is_valid(self):
|
def is_valid(self):
|
||||||
"""check if the device is usable and valid
|
"""check if the device is usable and valid
|
||||||
|
|
||||||
|
@ -343,7 +376,7 @@ class Blockdevice:
|
||||||
prefs["Programs"]["super"],
|
prefs["Programs"]["super"],
|
||||||
prefs["Programs"]["CryptoBoxRootActions"],
|
prefs["Programs"]["CryptoBoxRootActions"],
|
||||||
"program", "cryptsetup",
|
"program", "cryptsetup",
|
||||||
"isLuks", self.devnodes[0]])
|
"isLuks", self.get_device()])
|
||||||
proc.wait()
|
proc.wait()
|
||||||
result = proc.returncode == 0
|
result = proc.returncode == 0
|
||||||
## store result and return
|
## store result and return
|
||||||
|
@ -495,7 +528,7 @@ class Blockdevice:
|
||||||
prefs["Programs"]["super"],
|
prefs["Programs"]["super"],
|
||||||
prefs["Programs"]["CryptoBoxRootActions"],
|
prefs["Programs"]["CryptoBoxRootActions"],
|
||||||
"program", "cryptsetup",
|
"program", "cryptsetup",
|
||||||
"luksUUID", self.devnodes[0] ])
|
"luksUUID", self.get_device()])
|
||||||
(output, error) = proc.communicate()
|
(output, error) = proc.communicate()
|
||||||
except OSError, err_msg:
|
except OSError, err_msg:
|
||||||
LOGGER.warning("Failed to call '%s' to determine UUID: %s" \
|
LOGGER.warning("Failed to call '%s' to determine UUID: %s" \
|
||||||
|
@ -503,7 +536,7 @@ class Blockdevice:
|
||||||
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" % \
|
||||||
(prefs["Programs"]["cryptsetup"], self.devnodes[0],
|
(prefs["Programs"]["cryptsetup"], self.get_device(),
|
||||||
error))
|
error))
|
||||||
return None
|
return None
|
||||||
result = output.strip()
|
result = output.strip()
|
||||||
|
@ -557,8 +590,6 @@ class Blockdevice:
|
||||||
result = {"label": None, "type_id": None, "uuid": None}
|
result = {"label": None, "type_id": None, "uuid": None}
|
||||||
if not self.is_valid():
|
if not self.is_valid():
|
||||||
return result
|
return result
|
||||||
if self.is_luks():
|
|
||||||
return result
|
|
||||||
prefs = _load_preferences()
|
prefs = _load_preferences()
|
||||||
try:
|
try:
|
||||||
proc = subprocess.Popen(
|
proc = subprocess.Popen(
|
||||||
|
@ -571,12 +602,12 @@ class Blockdevice:
|
||||||
"-s", "UUID",
|
"-s", "UUID",
|
||||||
"-c", os.devnull,
|
"-c", os.devnull,
|
||||||
"-w", os.devnull,
|
"-w", os.devnull,
|
||||||
self.devnodes[0]])
|
self.get_device()])
|
||||||
(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 for " \
|
LOGGER.warning("Failed to call '%s' to determine label for " \
|
||||||
% prefs["Programs"]["blkid"] + "'%s': %s" % \
|
% prefs["Programs"]["blkid"] + "'%s': %s" % \
|
||||||
(self.devnodes[0], err_msg))
|
(self.get_device(), err_msg))
|
||||||
return result
|
return result
|
||||||
if proc.returncode == 2:
|
if proc.returncode == 2:
|
||||||
## the device does not contain a filesystem (e.g. it is zeroed or
|
## the device does not contain a filesystem (e.g. it is zeroed or
|
||||||
|
@ -584,7 +615,7 @@ class Blockdevice:
|
||||||
return result
|
return result
|
||||||
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" % \
|
||||||
(prefs["Programs"]["blkid"], self.devnodes[0],
|
(prefs["Programs"]["blkid"], self.get_device(),
|
||||||
error.strip()))
|
error.strip()))
|
||||||
return result
|
return result
|
||||||
# scan the output string for results
|
# scan the output string for results
|
||||||
|
@ -592,7 +623,7 @@ class Blockdevice:
|
||||||
# /dev/hda1: TYPE="ext3" LABEL="neu"de"
|
# /dev/hda1: TYPE="ext3" LABEL="neu"de"
|
||||||
pattern = {"LABEL": "label", "TYPE": "type_id", "UUID": "uuid"}
|
pattern = {"LABEL": "label", "TYPE": "type_id", "UUID": "uuid"}
|
||||||
for name, attr in pattern.items():
|
for name, attr in pattern.items():
|
||||||
match = re.search(r' %s="(.*?)"(?: [A-Z]+="|$)' % name, output)
|
match = re.search(r' %s="(.*?)"(?: [A-Z]+="| ?$)' % name, output)
|
||||||
if match:
|
if match:
|
||||||
result[attr] = match.groups()[0]
|
result[attr] = match.groups()[0]
|
||||||
# check for special attributes of LUKS devices and LVM physical volumes
|
# check for special attributes of LUKS devices and LVM physical volumes
|
||||||
|
@ -625,6 +656,7 @@ class Blockdevice:
|
||||||
output += "\t%s:\t%s\n" % ("blockdir", self.devdir)
|
output += "\t%s:\t%s\n" % ("blockdir", self.devdir)
|
||||||
output += "\t%s:\t%d/%d\n" % ("major/minor", self.major, self.minor)
|
output += "\t%s:\t%d/%d\n" % ("major/minor", self.major, self.minor)
|
||||||
output += "\t%s:\t\t%s\n" % ("label", self.label)
|
output += "\t%s:\t\t%s\n" % ("label", self.label)
|
||||||
|
output += "\t%s:\t\t%s\n" % ("type_id", self.type_id)
|
||||||
output += "\t%s:\t\t%s\n" % ("UUID", self.uuid)
|
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" % ("range", self.range)
|
||||||
output += "\t%s:\t\t%s\n" % ("size", self.size)
|
output += "\t%s:\t\t%s\n" % ("size", self.size)
|
||||||
|
@ -726,7 +758,14 @@ class BlockdeviceCache:
|
||||||
ref = ref[element]
|
ref = ref[element]
|
||||||
## store the item
|
## store the item
|
||||||
ref[link[-1]] = item
|
ref[link[-1]] = item
|
||||||
|
|
||||||
|
|
||||||
|
def get_age(self):
|
||||||
|
age = CACHE_EXPIRE_SECONDS - (self.expires - int(time.time()))
|
||||||
|
if age < 0:
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
return age
|
||||||
|
|
||||||
|
|
||||||
def __get_major_minor(dev):
|
def __get_major_minor(dev):
|
||||||
|
@ -771,7 +810,7 @@ def __get_major_minor(dev):
|
||||||
|
|
||||||
def get_blockdevice(dev,
|
def get_blockdevice(dev,
|
||||||
sysblock_dir=DEFAULT_SYSBLOCK_DIR,
|
sysblock_dir=DEFAULT_SYSBLOCK_DIR,
|
||||||
devnode_dir=DEFAULT_DEVNODE_DIR):
|
devnode_dir=DEFAULT_DEVNODE_DIR, retry_once=True):
|
||||||
if isinstance(dev, Blockdevice):
|
if isinstance(dev, Blockdevice):
|
||||||
# it is already a blockdevice
|
# it is already a blockdevice
|
||||||
major_minor = (dev.major, dev.minor)
|
major_minor = (dev.major, dev.minor)
|
||||||
|
@ -788,13 +827,29 @@ def get_blockdevice(dev,
|
||||||
major_minor = one_major_minor
|
major_minor = one_major_minor
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
return None
|
# rebuild the cache if it is rather old and try again
|
||||||
cache_link = ["blockdevices", major_minor]
|
# this is necessary for the "partition" plugin
|
||||||
dev = CACHE.get(cache_link)
|
if retry_once and (CACHE.get_age() > CACHE_MINIMUM_AGE_FOR_REBUILD):
|
||||||
if dev is None:
|
CACHE.reset()
|
||||||
dev = Blockdevice(major_minor, sysblock_dir, devnode_dir)
|
device = get_blockdevice(dev, sysblock_dir, devnode_dir, False)
|
||||||
CACHE.set(cache_link, dev)
|
if not device is None:
|
||||||
return dev
|
major_minor = (device.major, device.minor)
|
||||||
|
else:
|
||||||
|
major_minor = None
|
||||||
|
else:
|
||||||
|
# it seems like it does really not exist
|
||||||
|
major_minor = None
|
||||||
|
if major_minor:
|
||||||
|
cache_link = ["blockdevices", major_minor]
|
||||||
|
dev = CACHE.get(cache_link)
|
||||||
|
if dev is None:
|
||||||
|
dev = Blockdevice(major_minor, sysblock_dir, devnode_dir)
|
||||||
|
if not dev is None:
|
||||||
|
CACHE.set(cache_link, dev)
|
||||||
|
return dev
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def find_blockdevices(top_dir):
|
def find_blockdevices(top_dir):
|
||||||
|
@ -939,12 +994,13 @@ def _load_preferences():
|
||||||
CACHE = BlockdeviceCache()
|
CACHE = BlockdeviceCache()
|
||||||
|
|
||||||
|
|
||||||
def show_devices(blocks, show):
|
def show_devices(blocks):
|
||||||
|
result = ""
|
||||||
if len(blocks) > 0:
|
if len(blocks) > 0:
|
||||||
## show all devices and their properties
|
## show all devices and their properties
|
||||||
show("Properties of all devices:")
|
result += "Properties of all devices:\n"
|
||||||
for device in blocks:
|
for device in blocks:
|
||||||
show(device.info())
|
result += device.info() + "\n"
|
||||||
|
|
||||||
## discover all self-check methods
|
## discover all self-check methods
|
||||||
example = blocks[0]
|
example = blocks[0]
|
||||||
|
@ -953,28 +1009,24 @@ def show_devices(blocks, show):
|
||||||
and method.startswith("is_")]
|
and method.startswith("is_")]
|
||||||
## list all checks and the respective devices
|
## list all checks and the respective devices
|
||||||
for check in flag_checker:
|
for check in flag_checker:
|
||||||
show("List of '%s' devices:" % check[3:])
|
result += "List of '%s' devices:" % check[3:] + "\n"
|
||||||
for device in blocks:
|
for device in blocks:
|
||||||
try:
|
try:
|
||||||
if getattr(device, check)():
|
if getattr(device, check)():
|
||||||
show("\t%s" % device)
|
result += "\t%s" % device + "\n"
|
||||||
except TypeError:
|
except TypeError:
|
||||||
# ignore tests that need a second argument
|
# ignore tests that need a second argument
|
||||||
pass
|
pass
|
||||||
show()
|
result += "\n"
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
def get_devices_and_show():
|
def get_devices_and_show():
|
||||||
## list the properties of all available devices
|
## list the properties of all available devices
|
||||||
## this is just for testing purposes
|
## this is just for testing purposes
|
||||||
blocks = Blockdevices().get_devices()
|
blocks = Blockdevices().get_devices()
|
||||||
blocks.sort(key=lambda x: x.name)
|
blocks.sort(key=lambda x: x.name)
|
||||||
|
return show_devices(blocks)
|
||||||
## do we want to show the result?
|
|
||||||
def show(text=""):
|
|
||||||
if IS_VISIBLE:
|
|
||||||
print text
|
|
||||||
|
|
||||||
show_devices(blocks, show)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -983,12 +1035,11 @@ if __name__ == '__main__':
|
||||||
# show some profiling information (requires the python-profiler package)
|
# show some profiling information (requires the python-profiler package)
|
||||||
import cProfile
|
import cProfile
|
||||||
import pstats
|
import pstats
|
||||||
IS_VISIBLE = False
|
|
||||||
for index in range(3):
|
for index in range(3):
|
||||||
print "Run: %d" % index
|
print "Run: %d" % index
|
||||||
cProfile.run('get_devices_and_show()', 'profinfo')
|
cProfile.run('get_devices_and_show()', 'profinfo')
|
||||||
p = pstats.Stats('profinfo')
|
p = pstats.Stats('profinfo')
|
||||||
p.sort_stats('cumulative').print_stats(20)
|
p.sort_stats('cumulative').print_stats(20)
|
||||||
else:
|
else:
|
||||||
get_devices_and_show()
|
print get_devices_and_show()
|
||||||
|
|
||||||
|
|
|
@ -147,12 +147,12 @@ class CryptoBoxContainer:
|
||||||
|
|
||||||
|
|
||||||
def get_device(self):
|
def get_device(self):
|
||||||
"""Return the device name of the container
|
"""Returns the path of a device node representing this device
|
||||||
|
|
||||||
e.g.: /dev/hdc1
|
e.g.: /dev/hdc1
|
||||||
Available since: 0.3.0
|
Available since: 0.3.0
|
||||||
"""
|
"""
|
||||||
return self.device.devnodes[0]
|
return self.device.get_device()
|
||||||
|
|
||||||
|
|
||||||
def get_type(self):
|
def get_type(self):
|
||||||
|
@ -467,7 +467,7 @@ class CryptoBoxContainer:
|
||||||
if self.device.holders:
|
if self.device.holders:
|
||||||
## the decrypted blockdevice is available
|
## the decrypted blockdevice is available
|
||||||
plain_device = cryptobox.core.blockdevice.get_blockdevice(
|
plain_device = cryptobox.core.blockdevice.get_blockdevice(
|
||||||
self.device.holders[0]).devnodes[0]
|
self.device.holders[0]).get_device()
|
||||||
else:
|
else:
|
||||||
err_msg = "Could not find the plaintext container for " \
|
err_msg = "Could not find the plaintext container for " \
|
||||||
+ "'%s': %s" % (self.get_device(), "no hold devices found")
|
+ "'%s': %s" % (self.get_device(), "no hold devices found")
|
||||||
|
@ -616,6 +616,11 @@ class CryptoBoxContainer:
|
||||||
if self.is_mounted():
|
if self.is_mounted():
|
||||||
raise CBVolumeIsActive(
|
raise CBVolumeIsActive(
|
||||||
"deactivate the partition before filesystem initialization")
|
"deactivate the partition before filesystem initialization")
|
||||||
|
if self.get_device() is None:
|
||||||
|
raise CBCreateError("No valid device for (%s) found: %s" % \
|
||||||
|
(self.device.devdir, self.device.devnodes))
|
||||||
|
# useful for debugging: disable threading
|
||||||
|
ENABLE_THREADING = True
|
||||||
def format():
|
def format():
|
||||||
"""This function will get called as a seperate thread.
|
"""This function will get called as a seperate thread.
|
||||||
|
|
||||||
|
@ -628,8 +633,14 @@ class CryptoBoxContainer:
|
||||||
loc_data.old_name = self.get_name()
|
loc_data.old_name = self.get_name()
|
||||||
self.set_busy(True, 600)
|
self.set_busy(True, 600)
|
||||||
## give the main thread a chance to continue
|
## give the main thread a chance to continue
|
||||||
loc_data.child_pid = os.fork()
|
if ENABLE_THREADING:
|
||||||
if loc_data.child_pid == 0:
|
loc_data.child_pid = os.fork()
|
||||||
|
primary_thread_enabled = loc_data.child_pid == 0
|
||||||
|
secondary_thread_enabled = not primary_thread_enabled
|
||||||
|
else:
|
||||||
|
primary_thread_enabled = True
|
||||||
|
secondary_thread_enabled = True
|
||||||
|
if primary_thread_enabled:
|
||||||
loc_data.proc = subprocess.Popen(
|
loc_data.proc = subprocess.Popen(
|
||||||
shell = False,
|
shell = False,
|
||||||
stdin = None,
|
stdin = None,
|
||||||
|
@ -640,26 +651,35 @@ class CryptoBoxContainer:
|
||||||
self.cbox.prefs["Programs"]["mkfs"],
|
self.cbox.prefs["Programs"]["mkfs"],
|
||||||
"-t", fs_type, self.get_device()])
|
"-t", fs_type, self.get_device()])
|
||||||
loc_data.proc.wait()
|
loc_data.proc.wait()
|
||||||
## wait to allow error detection
|
if ENABLE_THREADING:
|
||||||
if loc_data.proc.returncode == 0:
|
## wait to allow error detection
|
||||||
time.sleep(5)
|
if loc_data.proc.returncode == 0:
|
||||||
## skip cleanup stuff (as common for sys.exit)
|
time.sleep(5)
|
||||||
os._exit(0)
|
## skip cleanup stuff (as common for sys.exit)
|
||||||
else:
|
os._exit(0)
|
||||||
os.waitpid(loc_data.child_pid, 0)
|
if secondary_thread_enabled:
|
||||||
|
if ENABLE_THREADING:
|
||||||
|
os.waitpid(loc_data.child_pid, 0)
|
||||||
try:
|
try:
|
||||||
self.set_name(loc_data.old_name)
|
self.set_name(loc_data.old_name)
|
||||||
except CBNameIsInUse:
|
except CBNameIsInUse:
|
||||||
pass
|
pass
|
||||||
self.set_busy(False)
|
self.set_busy(False)
|
||||||
bg_task = threading.Thread(target=format)
|
return (loc_data.proc.returncode == 0)
|
||||||
bg_task.setDaemon(True)
|
if ENABLE_THREADING:
|
||||||
bg_task.start()
|
bg_task = threading.Thread(target=format)
|
||||||
time.sleep(3)
|
bg_task.setDaemon(True)
|
||||||
## if the thread exited very fast, then it failed
|
bg_task.start()
|
||||||
if not bg_task.isAlive():
|
time.sleep(3)
|
||||||
|
## if the thread exited very fast, then it failed
|
||||||
|
success = bg_task.isAlive()
|
||||||
|
else:
|
||||||
|
success = format()
|
||||||
|
if not success:
|
||||||
raise CBCreateError("formatting of device (%s) failed out " % \
|
raise CBCreateError("formatting of device (%s) failed out " % \
|
||||||
self.get_device() + "of unknown reasons")
|
self.get_device() + "of unknown reasons")
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def __create_luks(self, password, fs_type="ext3"):
|
def __create_luks(self, password, fs_type="ext3"):
|
||||||
|
@ -718,7 +738,7 @@ class CryptoBoxContainer:
|
||||||
if self.device.holders:
|
if self.device.holders:
|
||||||
## the decrypted blockdevice is available
|
## the decrypted blockdevice is available
|
||||||
plain_device = cryptobox.core.blockdevice.get_blockdevice(
|
plain_device = cryptobox.core.blockdevice.get_blockdevice(
|
||||||
self.device.holders[0]).devnodes[0]
|
self.device.holders[0]).get_device()
|
||||||
else:
|
else:
|
||||||
err_msg = "Could not find the plaintext container for " \
|
err_msg = "Could not find the plaintext container for " \
|
||||||
+ "'%s': %s" % (self.get_device(), "no hold devices found")
|
+ "'%s': %s" % (self.get_device(), "no hold devices found")
|
||||||
|
|
Loading…
Reference in New Issue