lars
56e954d1c4
added some log entries use threading module instead of "fork" for background formatting redirection for "network" plugin fixed empty return value of plugins defaults to plugin overview page
359 lines
11 KiB
Python
359 lines
11 KiB
Python
import subprocess
|
|
import os
|
|
import logging
|
|
import CryptoBoxTools
|
|
import CryptoBoxPlugin
|
|
|
|
class partition(CryptoBoxPlugin.CryptoBoxPlugin):
|
|
|
|
PartTypes = {
|
|
"windows" : ["0xC", "vfat"],
|
|
"linux" : ["L", "ext3"]}
|
|
|
|
ConfigPartition = {
|
|
"size" : 5, # size of configuration partition (if necessary) in MB
|
|
"type" : "L",
|
|
"fs" : "ext2"}
|
|
|
|
|
|
def doAction(self, **args):
|
|
## retrieve some values from 'args' - defaults are empty
|
|
self.withConfigPartition = self.__isWithConfigPartition(args)
|
|
self.device = self.__getSelectedDevice(args)
|
|
self.deviceSize = self.__getAvailableDeviceSize(self.device)
|
|
try:
|
|
step = args["step"]
|
|
del args["step"]
|
|
except KeyError:
|
|
step = "select_device"
|
|
## no (or invalid) device was supplied
|
|
if not self.device:
|
|
step == "select_device"
|
|
if step == "add_partition":
|
|
return self.__actionAddPartition(args)
|
|
if step == "del_partition":
|
|
return self.__actionDelPartition(args)
|
|
elif step == "finish":
|
|
return self.__actionFinish(args)
|
|
else: # for "select_device" and for invalid targets
|
|
return self.__actionSelectDevice(args)
|
|
|
|
|
|
def getStatus(self):
|
|
return "%s / %s / %s" % (self.device, self.deviceSize, self.withConfigPartition)
|
|
|
|
|
|
def __getSelectedDevice(self, args):
|
|
try:
|
|
device = args["block_device"]
|
|
except KeyError:
|
|
return None
|
|
if not self.__isDeviceValid(device):
|
|
return None
|
|
if self.__isDeviceBusy(device):
|
|
self.hdf["Data.Warning"] = "Plugins.partition.DiskIsBusy"
|
|
return None
|
|
return device
|
|
|
|
|
|
def __isDeviceValid(self, device):
|
|
if not device:
|
|
return False
|
|
if not self.cbox.isDeviceAllowed(device):
|
|
return False
|
|
if not device in CryptoBoxTools.getParentBlockDevices():
|
|
return False
|
|
return True
|
|
|
|
|
|
def __isDeviceBusy(self, device):
|
|
"""check if the device (or one of its partitions) is mounted"""
|
|
# TODO: the config partition is ignored, as it is not part of the container list - that is not good
|
|
import re
|
|
for c in self.cbox.getContainerList():
|
|
if re.match(device + "\d*$", c.getDevice()):
|
|
if c.isMounted(): return True
|
|
return False
|
|
|
|
|
|
def __actionSelectDevice(self, args):
|
|
block_devices = [e
|
|
for e in CryptoBoxTools.getParentBlockDevices()
|
|
if self.cbox.isDeviceAllowed(e)]
|
|
counter = 0
|
|
for a in block_devices:
|
|
self.hdf[self.hdf_prefix + "BlockDevices.%d" % counter] = a
|
|
self.cbox.log.debug("found a suitable block device: %s" % a)
|
|
counter += 1
|
|
if self.withConfigPartition:
|
|
self.hdf[self.hdf_prefix + "CreateConfigPartition"] = "1"
|
|
## there is no disk available
|
|
if not block_devices:
|
|
self.hdf["Data.Warning"] = "Plugins.partition.NoDisksAvailable"
|
|
return "select_device"
|
|
|
|
|
|
def __actionAddPartition(self, args):
|
|
self.hdf[self.hdf_prefix + "Device"] = self.device
|
|
self.hdf[self.hdf_prefix + "Device.Size"] = self.deviceSize
|
|
parts = self.__getPartitionsFromArgs(args)
|
|
self.__setPartitionData(parts)
|
|
return "set_partitions"
|
|
|
|
|
|
def __actionDelPartition(self, args):
|
|
try:
|
|
part_num = int(args["del_num"])
|
|
except (TypeError,KeyError):
|
|
return self.__actionAddPartition(args)
|
|
self.hdf[self.hdf_prefix + "Device"] = self.device
|
|
self.hdf[self.hdf_prefix + "Device.Size"] = self.deviceSize
|
|
parts = self.__getPartitionsFromArgs(args)
|
|
## valid partition number to be deleted?
|
|
if part_num < len(parts):
|
|
del parts[part_num]
|
|
else:
|
|
return self.__actionAddPartition(args)
|
|
self.__setPartitionData(parts)
|
|
return "set_partitions"
|
|
|
|
|
|
def __actionFinish(self, args):
|
|
parts = self.__getPartitionsFromArgs(args)
|
|
if parts:
|
|
self.__setPartitionData(parts)
|
|
if not self.__runFDisk(parts):
|
|
self.hdf["Data.Warning"] = "Plugins.partition.PartitioningFailed"
|
|
return self.__actionAddPartition(args)
|
|
else:
|
|
# TODO: will we use the "yield"-style? If yes, then remove these messages (success and warning)
|
|
#if self.__formatPartitions(parts):
|
|
# self.hdf["Data.Success"] = "Plugins.partition.Partitioned"
|
|
#else:
|
|
# self.hdf["Data.Warning"] = "Plugins.partition.FormattingFailed"
|
|
"""
|
|
tricky problem: if the device was partitioned, then a created config partition is still part of the containerlist, as the label is not checked again - very ugly!!! So we will call reReadContainerList after formatting the last partition - see below
|
|
"""
|
|
self.cbox.reReadContainerList()
|
|
def result_generator():
|
|
counter = 0
|
|
## initialize the generator
|
|
formatPart_gen = self.__formatPartitions(parts)
|
|
while counter < len(parts):
|
|
## first part: get the device name
|
|
yield formatPart_gen.next()
|
|
counter += 1
|
|
## second part: do the real formatting of a partition
|
|
result = formatPart_gen.next()
|
|
## after the first partiton, we can reRead the containerList (as the possible config partition was already created)
|
|
if self.withConfigPartition and (counter == 1):
|
|
## important: reRead the containerList
|
|
self.cbox.reReadContainerList()
|
|
## return the result
|
|
yield result
|
|
return {
|
|
"template": "show_format_progress",
|
|
"generator": result_generator}
|
|
else:
|
|
return self.__actionAddPartition(args)
|
|
|
|
|
|
def __setPartitionData(self, parts):
|
|
availSize = self.deviceSize
|
|
i = 0
|
|
for part in parts:
|
|
self.cbox.log.debug(part)
|
|
self.hdf[self.hdf_prefix + "Parts.%d.Size" % i] = part["size"]
|
|
self.hdf[self.hdf_prefix + "Parts.%d.Type" % i] = part["type"]
|
|
availSize -= part["size"]
|
|
i += 1
|
|
self.hdf[self.hdf_prefix + "availSize"] = availSize
|
|
if self.withConfigPartition:
|
|
self.hdf[self.hdf_prefix + "CreateConfigPartition"] = "1"
|
|
for t in self.PartTypes.keys():
|
|
self.hdf[self.hdf_prefix + "Types.%s" % t] = t
|
|
|
|
|
|
def __getPartitionsFromArgs(self, args):
|
|
parts = []
|
|
done = False
|
|
availSize = self.deviceSize
|
|
i = -1
|
|
while not done:
|
|
i += 1
|
|
try:
|
|
size = int(args["part%d_size" % i])
|
|
partType = args["part%d_type" % i]
|
|
if int(size) > availSize:
|
|
self.hdf["Data.Warning"] = "Plugins.partition.PartitionTooBig"
|
|
continue
|
|
if int(size) < 10:
|
|
self.hdf["Data.Warning"] = "Plugins.partition.PartitionTooSmall"
|
|
continue
|
|
if not partType in self.PartTypes.keys(): continue
|
|
parts.append({"size":size, "type":partType})
|
|
availSize -= size
|
|
except TypeError:
|
|
pass
|
|
except KeyError:
|
|
done = True
|
|
return parts
|
|
|
|
|
|
def __getAvailableDeviceSize(self, device):
|
|
"""calculate the available size (MB) of the device
|
|
also consider a (possible) configuration partition"""
|
|
if not device: return 0
|
|
rdev = os.stat(device).st_rdev
|
|
minor = os.minor(rdev)
|
|
major = os.major(rdev)
|
|
for f in file("/proc/partitions"):
|
|
try:
|
|
elements = f.split()
|
|
if len(elements) != 4: continue
|
|
if (int(elements[0]) == major) and (int(elements[1]) == minor):
|
|
deviceSize = int(elements[2])/1024
|
|
if self.withConfigPartition:
|
|
deviceSize -= self.ConfigPartition["size"]
|
|
return deviceSize
|
|
except ValueError:
|
|
pass
|
|
return 0
|
|
|
|
|
|
def __isWithConfigPartition(self, args):
|
|
try:
|
|
if args["create_config_partition"]:
|
|
createConfig = True
|
|
else:
|
|
createConfig = False
|
|
except KeyError:
|
|
createConfig = False
|
|
return createConfig
|
|
|
|
|
|
def __runFDisk(self, parts):
|
|
## check if the device is completely filled (to avoid some empty last blocks)
|
|
avail_size = self.deviceSize
|
|
for d in parts: avail_size -= d["size"]
|
|
isFilled = avail_size == 0
|
|
proc = subprocess.Popen(
|
|
shell = False,
|
|
stdin = subprocess.PIPE,
|
|
stdout = subprocess.PIPE,
|
|
stderr = subprocess.PIPE,
|
|
args = [
|
|
self.cbox.prefs["Programs"]["super"],
|
|
self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
|
|
"plugin",
|
|
os.path.join(os.path.dirname(__file__), "root_action.py"),
|
|
"partition",
|
|
self.device])
|
|
for line in self.__getSFDiskLayout(parts, isFilled):
|
|
proc.stdin.write(line + "\n")
|
|
(output, error) = proc.communicate()
|
|
if proc.returncode != 0: self.cbox.log.debug("partitioning failed: %s" % error)
|
|
return proc.returncode == 0
|
|
|
|
|
|
def __getSFDiskLayout(self, paramParts, isFilled):
|
|
parts = paramParts[:]
|
|
## first a (possible) configuration partition - so it will be reusable
|
|
if self.withConfigPartition:
|
|
## fill the main table (including a config partition)
|
|
yield ",%d,%s" % (self.ConfigPartition["size"], self.ConfigPartition["type"])
|
|
## one primary partition
|
|
yield ",%d,%s,*" % (parts[0]["size"], self.PartTypes[parts[0]["type"]][0])
|
|
del parts[0]
|
|
## no extended partition, if there is only one disk
|
|
if not parts: return
|
|
## an extended container for the rest
|
|
yield ",,E"
|
|
## an empty partition in main table
|
|
yield ";"
|
|
## maybe another empty partition if there is no config partition
|
|
if not self.withConfigPartition: yield ";"
|
|
while parts:
|
|
if isFilled and (len(parts) == 1):
|
|
yield ",,%s" % (self.PartTypes[parts[0]["type"]][0],)
|
|
else:
|
|
yield ",%d,%s" % (parts[0]["size"], self.PartTypes[parts[0]["type"]][0])
|
|
del parts[0]
|
|
|
|
|
|
def __formatPartitions(self, paramParts):
|
|
import threading
|
|
parts = paramParts[:]
|
|
part_num = 1
|
|
## maybe a config partition?
|
|
if self.withConfigPartition:
|
|
dev_name = self.device + str(part_num)
|
|
self.cbox.log.info("formatting config partition (%s)" % dev_name)
|
|
if self.__formatOnePartition(dev_name, self.ConfigPartition["fs"]):
|
|
self.__setLabelOfPartition(dev_name, self.cbox.prefs["Main"]["ConfigVolumeLabel"])
|
|
part_num += 1
|
|
## the first data partition
|
|
dev_name = self.device + str(part_num)
|
|
partType = self.PartTypes[parts[0]["type"]][1]
|
|
self.cbox.log.info("formatting partition (%s) as '%s'" % (dev_name, partType))
|
|
yield dev_name
|
|
if self.__formatOnePartition(dev_name, partType):
|
|
yield "OK"
|
|
else:
|
|
yield "<b>Failed!</b>"
|
|
del parts[0]
|
|
## other data partitions
|
|
part_num = 5
|
|
while parts:
|
|
dev_name = self.device + str(part_num)
|
|
partType = self.PartTypes[parts[0]["type"]][1]
|
|
self.cbox.log.info("formatting partition (%s) as '%s'" % (dev_name, partType))
|
|
yield dev_name
|
|
if self.__formatOnePartition(dev_name, partType):
|
|
yield "OK"
|
|
else:
|
|
yield "<b>Failed!</b>"
|
|
part_num += 1
|
|
del parts[0]
|
|
return
|
|
|
|
|
|
def __formatOnePartition(self, dev_name, type):
|
|
proc = subprocess.Popen(
|
|
shell = False,
|
|
args = [
|
|
self.cbox.prefs["Programs"]["super"],
|
|
self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
|
|
"plugin",
|
|
os.path.join(os.path.dirname(__file__), "root_action.py"),
|
|
"format",
|
|
dev_name,
|
|
type])
|
|
(output, error) = proc.communicate()
|
|
if proc.returncode != 0:
|
|
self.cbox.log.warn("failed to create filesystem on %s: %s" % (dev_name, error))
|
|
return False
|
|
else:
|
|
return True
|
|
|
|
|
|
def __setLabelOfPartition(self, dev_name, label):
|
|
proc = subprocess.Popen(
|
|
shell = False,
|
|
stdout = subprocess.PIPE,
|
|
stderr = subprocess.PIPE,
|
|
args = [
|
|
self.cbox.prefs["Programs"]["super"],
|
|
self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
|
|
"plugin",
|
|
os.path.join(os.path.dirname(__file__), "root_action.py"),
|
|
"label",
|
|
dev_name,
|
|
label])
|
|
(output, error) = proc.communicate()
|
|
if proc.returncode == 0:
|
|
return True
|
|
else:
|
|
self.cbox.log.warn("failed to create filesystem on %s: %s" % (dev_name, error))
|
|
return False
|
|
|