diff --git a/pythonrewrite/bin/CryptoBox.py b/pythonrewrite/bin/CryptoBox.py index 4a3e9b2..f1769a5 100755 --- a/pythonrewrite/bin/CryptoBox.py +++ b/pythonrewrite/bin/CryptoBox.py @@ -100,7 +100,6 @@ class CryptoBox: self.log.info(string) - # RFC: why should CryptoBoxProps inherit CryptoBox? [l] # RFC: shouldn't we move all useful functions of CryptoBoxProps to CryptoBox? [l] class CryptoBoxProps(CryptoBox): @@ -119,12 +118,27 @@ class CryptoBoxProps(CryptoBox): def reReadContainerList(self): self.containers = [] for device in CryptoBoxTools.getAvailablePartitions(): - if self.isDeviceAllowed(device): + if self.isDeviceAllowed(device) and not self.isConfigPartition(device): self.containers.append(CryptoBoxContainer.CryptoBoxContainer(device, self)) ## sort by container name self.containers.sort(cmp = lambda x,y: x.getName() < y.getName() and -1 or 1) + def isConfigPartition(self, device): + import subprocess + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + args = [ + self.prefs["Programs"]["blkid"], + "-c", os.path.devnull, + "-o", "value", + "-s", "LABEL", + device]) + (output, error) = proc.communicate() + return output.strip() == self.prefs["Main"]["ConfigVolumeLabel"] + + def isDeviceAllowed(self, devicename): "check if a device is white-listed for being used as cryptobox containers" import types diff --git a/pythonrewrite/bin/CryptoBoxSettings.py b/pythonrewrite/bin/CryptoBoxSettings.py index dfa266a..f16a02f 100644 --- a/pythonrewrite/bin/CryptoBoxSettings.py +++ b/pythonrewrite/bin/CryptoBoxSettings.py @@ -164,6 +164,7 @@ class CryptoBoxSettings: AllowedDevices = list(min=1) DefaultVolumePrefix = string(min=1) DefaultCipher = string(default="aes-cbc-essiv:sha256") +ConfigVolumeLabel = string(min=1,default="cbox_config") [Locations] MountParentDir = directoryExists(default="/var/cache/cryptobox/mnt") @@ -186,7 +187,6 @@ DocLanguage = string(min=1, default="en") [Programs] cryptsetup = fileExecutable(default="/sbin/cryptsetup") mkfs-data = fileExecutable(default="/sbin/mkfs.ext3") -mkfs-config = fileExecutable(default="/sbin/mkfs.ext2") blkid = fileExecutable(default="/sbin/blkid") mount = fileExecutable(default="/bin/mount") umount = fileExecutable(default="/bin/umount") diff --git a/pythonrewrite/bin/cryptobox.conf b/pythonrewrite/bin/cryptobox.conf index c609f0d..7797d50 100644 --- a/pythonrewrite/bin/cryptobox.conf +++ b/pythonrewrite/bin/cryptobox.conf @@ -4,12 +4,16 @@ # beware: .e.g "/dev/hd" grants access to _all_ harddisks AllowedDevices = /dev/loop + # the default name prefix of not unnamed containers DefaultVolumePrefix = "Data " # which cipher should cryptsetup-luks use? DefaultCipher = aes-cbc-essiv:sha256 +# label of the configuration partition (you should never change this) +ConfigVolumeLabel = cbox_config + [Locations] # where should we mount volumes? @@ -70,7 +74,6 @@ DocLanguage = de [Programs] cryptsetup = /sbin/cryptsetup mkfs-data = /sbin/mkfs.ext3 -mkfs-config = /sbin/mkfs.ext2 blkid = /sbin/blkid mount = /bin/mount umount = /bin/umount diff --git a/pythonrewrite/plugins/partition/current_partition_info.cs b/pythonrewrite/plugins/partition/current_partition_info.cs index e896e23..eaeb349 100644 --- a/pythonrewrite/plugins/partition/current_partition_info.cs +++ b/pythonrewrite/plugins/partition/current_partition_info.cs @@ -5,4 +5,7 @@ + + + diff --git a/pythonrewrite/plugins/partition/lang/en.hdf b/pythonrewrite/plugins/partition/lang/en.hdf index fdd0a27..f3dca8c 100644 --- a/pythonrewrite/plugins/partition/lang/en.hdf +++ b/pythonrewrite/plugins/partition/lang/en.hdf @@ -45,8 +45,23 @@ WarningMessage { Text = The partitioning of the device failed for some reason - sorry! } + FormattingFailed { + Title = Formatting failed + Text = The formatting of the filesystems of the device failed - sorry! + } + DiskIsBusy { Title = This disk is busy Text = Please deactivate all containers of this disk before partitioning. } + + PartitionTooBig { + Title = Invalid size + Text = The container size you entered exceeded the available size of the disk. + } + + PartitionTooSmall { + Title = Invalid size + Text = The minimum size of a container is 10 megabytes. + } } diff --git a/pythonrewrite/plugins/partition/partition.py b/pythonrewrite/plugins/partition/partition.py index dbdf517..2dd3011 100644 --- a/pythonrewrite/plugins/partition/partition.py +++ b/pythonrewrite/plugins/partition/partition.py @@ -1,16 +1,20 @@ -import re import subprocess -import imp import os import logging import CryptoBoxTools PartTypes = { - "linux" : "L", - "windows" : "0xC"} + "windows" : ["0xC", "vfat"], + "linux" : ["L", "ext3"]} + +ConfigPartition = { + "size" : 5, # size of configuration partition (if necessary) in MB + "type" : "L", + "fs" : "ext2"} logger = logging.getLogger("CryptoBox") + def doAction(hdf, cbox, **args): try: step = args["step"] @@ -32,7 +36,6 @@ def getStatus(cbox): def __isDeviceValid(device, cbox): - ## TODO: also check for "is device busy" add output a warning if not cbox.isDeviceAllowed(device): return False if not device in CryptoBoxTools.getParentBlockDevices(): @@ -42,6 +45,8 @@ def __isDeviceValid(device, cbox): def __isDeviceBusy(device, cbox): """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 cbox.getContainerList(): if re.match(device + "\d*$", c.getDevice()): if c.isMounted(): return True @@ -57,6 +62,8 @@ def __actionSelectDevice(hdf, cbox, args): hdf["Data.Plugins.partition.BlockDevices.%d" % counter] = a cbox.log.debug("found a suitable block device: %s" % a) counter += 1 + if __withConfigPartition(args): + hdf["Data.Plugins.partition.CreateConfigPartition"] = "1" ## there is no disk available if not block_devices: hdf["Data.Warning"] = "Plugins.partition.NoDisksAvailable" @@ -72,11 +79,11 @@ def __actionAddPartition(hdf, cbox, args): if __isDeviceBusy(device, cbox): hdf["Data.Warning"] = "Plugins.partition.DiskIsBusy" return __actionSelectDevice(hdf, cbox, args) - size = __getDeviceSize(device) + size = __getAvailableDeviceSize(device, __withConfigPartition(args)) hdf["Data.Plugins.partition.Device"] = device hdf["Data.Plugins.partition.Device.Size"] = size - parts = __getPartitionsFromArgs(args, size) - __setPartitionData(hdf, parts, size) + parts = __getPartitionsFromArgs(hdf, args, size) + __setPartitionData(hdf, parts, size, __withConfigPartition(args)) return "set_partitions" @@ -90,16 +97,16 @@ def __actionDelPartition(hdf, cbox, args): if __isDeviceBusy(device, cbox): hdf["Data.Warning"] = "Plugins.partition.DiskIsBusy" return __actionSelectDevice(hdf, cbox, args) - size = __getDeviceSize(device) + size = __getAvailableDeviceSize(device, __withConfigPartition(args)) hdf["Data.Plugins.partition.Device"] = device hdf["Data.Plugins.partition.Device.Size"] = size - parts = __getPartitionsFromArgs(args, size) + parts = __getPartitionsFromArgs(hdf, args, size) ## valid partition number to be deleted? if part_num < len(parts): del parts[part_num] else: return __actionSelectDevice(hdf, cbox, args) - __setPartitionData(hdf, parts, size) + __setPartitionData(hdf, parts, size, __withConfigPartition(args)) return "set_partitions" @@ -112,21 +119,24 @@ def __actionFinish(hdf, cbox, args): if __isDeviceBusy(device, cbox): hdf["Data.Warning"] = "Plugins.partition.DiskIsBusy" return __actionSelectDevice(hdf, cbox, args) - size = __getDeviceSize(device) - parts = __getPartitionsFromArgs(args, size) + size = __getAvailableDeviceSize(device, __withConfigPartition(args)) + parts = __getPartitionsFromArgs(hdf, args, size) if parts: - if not __runFDisk(cbox, device, parts): + if not __runFDisk(cbox, device, parts, __withConfigPartition(args)): hdf["Data.Warning"] = "Plugins.partition.PartitioningFailed" return __actionAddPartition(hdf, cbox, args) else: - hdf["Data.Success"] = "Plugins.partition.Partitioned" + if __formatPartitions(cbox, device, parts, __withConfigPartition(args)): + hdf["Data.Success"] = "Plugins.partition.Partitioned" + else: + hdf["Data.Warning"] = "Plugins.partition.FormattingFailed" cbox.reReadContainerList() return "form_system" else: return __actionSelectDevice(hdf, cbox, args) -def __setPartitionData(hdf, parts, size): +def __setPartitionData(hdf, parts, size, withConfigPartition): availSize = size i = 0 for part in parts: @@ -136,11 +146,13 @@ def __setPartitionData(hdf, parts, size): availSize -= part["size"] i += 1 hdf["Data.Plugins.partition.availSize"] = availSize + if withConfigPartition: + hdf["Data.Plugins.partition.CreateConfigPartition"] = "1" for t in PartTypes.keys(): hdf["Data.Plugins.partition.Types.%s" % t] = t -def __getPartitionsFromArgs(args, maxSize): +def __getPartitionsFromArgs(hdf, args, maxSize): parts = [] done = False availSize = maxSize @@ -150,8 +162,12 @@ def __getPartitionsFromArgs(args, maxSize): try: size = int(args["part%d_size" % i]) partType = args["part%d_type" % i] - if int(size) > availSize: continue - if int(size) <= 0: continue + if int(size) > availSize: + hdf["Data.Warning"] = "Plugins.partition.PartitionTooBig" + continue + if int(size) < 10: + hdf["Data.Warning"] = "Plugins.partition.PartitionTooSmall" + continue if not partType in PartTypes.keys(): continue parts.append({"size":size, "type":partType}) availSize -= size @@ -162,7 +178,9 @@ def __getPartitionsFromArgs(args, maxSize): return parts -def __getDeviceSize(device): +def __getAvailableDeviceSize(device, withConfigPartition): + """calculate the available size (MB) of the device + also consider a (possible) configuration partition""" rdev = os.stat(device).st_rdev minor = os.minor(rdev) major = os.major(rdev) @@ -171,16 +189,34 @@ def __getDeviceSize(device): elements = f.split() if len(elements) != 4: continue if (int(elements[0]) == major) and (int(elements[1]) == minor): - return int(elements[2])/1024 + deviceSize = int(elements[2])/1024 + if withConfigPartition: deviceSize -= ConfigPartition["size"] + return deviceSize except ValueError: pass return 0 -def __runFDisk(cbox, device, parts): +def __withConfigPartition(args): + try: + if args["create_config_partition"]: + createConfig = True + else: + createConfig = False + except KeyError: + createConfig = False + return createConfig + + +def __runFDisk(cbox, device, parts, withConfigPartition): + ## check if the device is completely filled (to avoid some empty last blocks) + avail_size = __getAvailableDeviceSize(device, withConfigPartition) + 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 = [ cbox.prefs["Programs"]["super"], @@ -189,23 +225,123 @@ def __runFDisk(cbox, device, parts): os.path.join(os.path.dirname(__file__), "root_action.py"), "partition", device]) - import logging - logger = logging.getLogger("CryptoBox") - for line in __getSFDiskLayout(parts): proc.stdin.write(line + "\n") + for line in __getSFDiskLayout(parts, withConfigPartition, isFilled): + proc.stdin.write(line + "\n") (output, error) = proc.communicate() - if error: logger.debug("partitioning failed: %s" % error) + if proc.returncode != 0: logger.debug("partitioning failed: %s" % error) return proc.returncode == 0 -def __getSFDiskLayout(paramParts): +def __getSFDiskLayout(paramParts, withConfigPartition, isFilled): parts = paramParts[:] - ## first a primary partition - yield ",%d,%s,*" % (parts[0]["size"], PartTypes[parts[0]["type"]]) + ## first a (possible) configuration partition - so it will be reusable + if withConfigPartition: + ## fill the main table (including a config partition) + yield ",%d,%s" % (ConfigPartition["size"], ConfigPartition["type"]) + ## one primary partition + yield ",%d,%s,*" % (parts[0]["size"], PartTypes[parts[0]["type"]][0]) del parts[0] + ## no extended partition, if there is only one disk if not parts: return - yield ",,E" # extended container for the rest - yield ";" # empty partition in main table - yield ";" # another empty partition in main table + ## 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 withConfigPartition: yield ";" while parts: - yield ",%d,%s" % (parts[0]["size"], PartTypes[parts[0]["type"]]) + if isFilled and (len(parts) == 1): + yield ",,%s" % (PartTypes[parts[0]["type"]][0],) + else: + yield ",%d,%s" % (parts[0]["size"], PartTypes[parts[0]["type"]][0]) del parts[0] + + +def __formatPartitions(cbox, device, paramParts, withConfigPartition): + success = True + parts = paramParts[:] + part_num = 1 + ## maybe a config partition? + if withConfigPartition: + dev_name = device + str(part_num) + logger.info("formatting config partition (%s)" % dev_name) + if __formatOnePartition(cbox, dev_name, ConfigPartition["fs"]): + __setLabelOfPartition(cbox, dev_name, cbox.prefs["Main"]["ConfigVolumeLabel"]) + else: + success = False + part_num += 1 + ## the first data partition + dev_name = device + str(part_num) + partType = PartTypes[parts[0]["type"]][1] + logger.info("formatting partition (%s) as '%s'" % (dev_name, partType)) + if not __formatOnePartition(cbox, dev_name, partType): + success = False + del parts[0] + ## other data partitions + part_num = 5 + while parts: + dev_name = device + str(part_num) + partType = PartTypes[parts[0]["type"]][1] + logger.info("formatting partition (%s) as '%s'" % (dev_name, partType)) + if not __formatOnePartition(cbox, dev_name, partType): + success = False + part_num += 1 + del parts[0] + return success + + +def __formatOnePartition(cbox, dev_name, type): + import time, sys + child_pid = os.fork() + ## we run formatting as a parallel thread + ## TODO: the parent thread still waits for the last child - that is not good for big harddisks + if child_pid == 0: + ## we are the child process + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + cbox.prefs["Programs"]["super"], + 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: + logger.warn("failed to create filesystem on %s: %s" % (dev_name, error)) + sys.exit(1) + else: + sys.exit(0) + else: + time.sleep(1) + (pid, exit_state) = os.waitpid(child_pid, os.WNOHANG) + if ((pid == 0) and (exit_state == 0)) \ + or ((pid == child_pid) and (exit_state == 0)): + return True + else: + return False + + +def __setLabelOfPartition(cbox, dev_name, label): + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + cbox.prefs["Programs"]["super"], + 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: + logger.warn("failed to create filesystem on %s: %s" % (device + str(part_num), error)) + return False + diff --git a/pythonrewrite/plugins/partition/root_action.py b/pythonrewrite/plugins/partition/root_action.py index 96082cf..5601f43 100755 --- a/pythonrewrite/plugins/partition/root_action.py +++ b/pythonrewrite/plugins/partition/root_action.py @@ -4,6 +4,8 @@ PLUGIN_TYPE = "cryptobox" SFDISK_BIN = "/sbin/sfdisk" +MKFS_BIN = "/sbin/mkfs" +LABEL_BIN = "/sbin/e2label" import subprocess import re @@ -23,27 +25,55 @@ def __partitionDevice(device): return proc.returncode == 0 +def __formatPartition(device, type): + proc = subprocess.Popen( + shell = False, + args = [ + MKFS_BIN, + "-t", type, + device]) + proc.communicate() + return proc.returncode == 0 + + +def __labelPartition(device, label): + proc = subprocess.Popen( + shell = False, + args = [ + LABEL_BIN, + device, + label]) + proc.communicate() + return proc.returncode == 0 + + if __name__ == "__main__": args = sys.argv[1:] self_bin =sys.argv[0] - if len(args) > 2: - sys.stderr.write("%s: too many arguments (%s)\n" % (self_bin, args)) - sys.exit(1) - if len(args) == 0: sys.stderr.write("%s: no argument supplied\n" % self_bin) sys.exit(1) - if args[0] == "partition": - if len(args) < 2: - sys.stderr.write("%s: not enough arguments (%s)\n" % (self_bin, args)) + try: + if args[0] == "partition": + if len(args) != 2: raise "InvalidArgNum" + result = __partitionDevice(args[1]) + elif args[0] == "format": + if len(args) != 3: raise "InvalidArgNum" + result = __formatPartition(args[1], args[2]) + elif args[0] == "label": + if len(args) != 3: raise "InvalidArgNum" + result = __labelPartition(args[1], args[2]) + else: + sys.stderr.write("%s: invalid action (%s)\n" % (self_bin, args[0])) sys.exit(1) - if __partitionDevice(args[1]): + if result: sys.exit(0) else: sys.exit(1) - else: + except "InvalidArgNum": + sys.stderr.write("%s: invalid number of arguments (%s)\n" % (self_bin, args)) sys.exit(1) - + diff --git a/pythonrewrite/plugins/partition/select_device.cs b/pythonrewrite/plugins/partition/select_device.cs index 8da649a..adb39c0 100644 --- a/pythonrewrite/plugins/partition/select_device.cs +++ b/pythonrewrite/plugins/partition/select_device.cs @@ -13,6 +13,13 @@

+

+ +

diff --git a/pythonrewrite/plugins/partition/set_partitions.cs b/pythonrewrite/plugins/partition/set_partitions.cs index 2774d76..9491718 100644 --- a/pythonrewrite/plugins/partition/set_partitions.cs +++ b/pythonrewrite/plugins/partition/set_partitions.cs @@ -36,7 +36,8 @@ - +