From 53e09ff825948acdd52a3adb443b119a1bf7c3ed Mon Sep 17 00:00:00 2001 From: lars Date: Tue, 14 Aug 2007 12:44:53 +0000 Subject: [PATCH] new approach for blockdevice detection and handling --- src/cryptobox/core/blockdevice.py | 230 ++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 src/cryptobox/core/blockdevice.py diff --git a/src/cryptobox/core/blockdevice.py b/src/cryptobox/core/blockdevice.py new file mode 100644 index 0000000..a71848d --- /dev/null +++ b/src/cryptobox/core/blockdevice.py @@ -0,0 +1,230 @@ +# +# Copyright 2007 sense.lab e.V. +# +# This file is part of the CryptoBox. +# +# The CryptoBox is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# The CryptoBox is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with the CryptoBox; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +''' +These classes detect and filter available blockdevices. +''' + +__revision__ = "$Id$" + + +""" +TODO: + - implement some caching + - find the devnodes for each device (e.g. /dev/hda) + - detect luks devices + - detect LVM +""" + + +import os + +class Blockdevices: + + def __init__(self, sysblock_dir='/sys/block', devnode_dir='/dev'): + self.sysblock_dir = sysblock_dir + self.devnode_dir = devnode_dir + self.devices = [] + for devdir in find_blockdevices(self.sysblock_dir): + blockdevice = Blockdevice(devdir, self.devnode_dir) + if not blockdevice is None: + self.devices.append(blockdevice) + + + def get_devices(self): + """return a copy of the device list + """ + return self.devices[:] + + + + +class Blockdevice: + + def __init__(self, dev, devnode_dir='/dev', sysblock_dir='/sys/block'): + self.devnode_dir = devnode_dir + self.sysblock_dir = sysblock_dir + if os.path.isabs(dev): + self.devdir = dev + else: + self.devdir = self.__find_relative_device(dev) + if self.devdir is None: + self = None + return + self.name = os.path.basename(self.devdir) + self.devnum = self.__get_major_minor() + ## check valid devnum + try: + major, minor = self.devnum + except TypeError: + self = None + return + self.size = self.__get_size() + self.range = self.__get_device_range() + self.slaves = self.__get_dev_related("slaves") + self.holders = self.__get_dev_related("holders") + self.children = self.__get_children() + + + def isstorage(self): + if self.range > 1: + ## partitionable blockdevice + return False + if self.size < 20: + ## extended partition, unused loop device + return False + if self.devnum[0] == 1: + ## ram device + return False + if self.children: + ## a parent blockdevice + return False + return True + + + def ispartitionable(self): + if self.range > 1: + return True + + + def __find_relative_device(self, devname): + for devdir in find_blockdevices(self.sysblock_dir): + if os.path.basename(devdir) == devname: + return devdir + return None + + + def __get_dev_related(self, subdir): + """return the content of sub directories (e.g. 'holders' or 'slaves') + """ + try: + return os.listdir(os.path.join(self.devdir, subdir)) + except OSError: + return [] + + + def __get_size(self): + default = 0 + try: + return int(file(os.path.join(self.devdir, 'size')).read()) + except OSError: + return default + except ValueError: + return default + + + def __get_major_minor(self): + """return the major and minor of the device""" + default = (0 ,0) + try: + content = file(os.path.join(self.devdir, "dev")).read() + except IOError: + return default + major, minor = content.split(":", 2) + try: + return int(major), int(minor) + except ValueError: + return default + + + def __get_device_range(self): + """number of possible subdevices + + partitionable blockdevices have a range > 1 + """ + default = 1 + try: + content = file(os.path.join(self.devdir, "range")).read() + except IOError: + return default + try: + return int(content) + except ValueError: + return default + + + def __get_children(self): + """return all devices depending on the current one + + all holders, subdevices and children of subdevices + """ + direct_children = [Blockdevice(child).name + for child in find_blockdevices(self.devdir)] + direct_children.extend(self.holders[:]) + children = direct_children[:] + for dchild in direct_children: + children.extend(Blockdevice(dchild).children) + return children + + + def __str__(self): + return self.name + + + def info(self): + output = "%s:\n" % self.name + output += "\t%s:\t%s\n" % ("blockdir", self.devdir) + output += "\t%s:\t%s\n" % ("major/minor", self.devnum) + 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" % ("slaves", self.slaves) + output += "\t%s:\t%s\n" % ("holders", self.holders) + output += "\t%s:\t%s\n" % ("children", self.children) + return output + + + +def find_blockdevices(top_dir): + + dev_dirs = [] + + def look4dev_dirs(arg, dirname, fnames): + ## ignore the top level directory to avoid infinite recursion for + ## get_children + if os.path.samefile(dirname, top_dir): + return + ## add directories containing the file 'dev' to the list + if (arg in fnames) and os.path.isfile(os.path.join(dirname, arg)): + dev_dirs.append(dirname) + for fname in fnames: + ## remove symlinks and non-directories + fullname = os.path.join(dirname, fname) + if os.path.islink(fullname) or (not os.path.isdir(fullname)): + fnames.remove(fname) + + os.path.walk(top_dir, look4dev_dirs, 'dev') + return dev_dirs + + +if __name__ == '__main__': + blocks = Blockdevices() + for dev in blocks.get_devices(): + print dev.info() + print + print "Usable storage devices:" + for dev in blocks.get_devices(): + if dev.isstorage(): + print dev + print + print "Partitionable devices:" + for dev in blocks.get_devices(): + if dev.ispartitionable(): + print dev +