cryptonas/src/cryptobox/core/blockdevice.py

231 lines
6.5 KiB
Python

#
# 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