cryptonas-branches/pythonrewrite/plugins/partition/partition.py

360 lines
11 KiB
Python
Raw Normal View History

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