cryptonas-branches/pythonrewrite/plugins/partition/partition.py
lars 56e954d1c4 plugin interface changed: now we use classes (inherited by CryptoBoxPlugin) instead of plain scripts
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
2006-09-25 12:21:39 +00:00

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