249 lines
7.7 KiB
Python
249 lines
7.7 KiB
Python
|
#
|
||
|
# Copyright 2006 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
|
||
|
#
|
||
|
|
||
|
"""Some useful functions for the CryptoBox.
|
||
|
"""
|
||
|
|
||
|
__revision__ = "$Id"
|
||
|
|
||
|
import logging
|
||
|
import os
|
||
|
import re
|
||
|
|
||
|
LOGGER = logging.getLogger("CryptoBox")
|
||
|
|
||
|
|
||
|
def get_available_partitions():
|
||
|
"retrieve a list of all available containers"
|
||
|
ret_list = []
|
||
|
try:
|
||
|
## the following reads all lines of /proc/partitions and adds the mentioned devices
|
||
|
fpart = open("/proc/partitions", "r")
|
||
|
try:
|
||
|
line = fpart.readline()
|
||
|
while line:
|
||
|
p_details = line.split()
|
||
|
if (len(p_details) == 4):
|
||
|
## the following code prevents double entries like /dev/hda and /dev/hda1
|
||
|
(p_major, p_minor, p_size, p_device) = p_details
|
||
|
## ignore lines with: invalid minor/major or extend partitions (size=1)
|
||
|
if re.search('^[0-9]*$', p_major) and \
|
||
|
re.search('^[0-9]*$', p_minor) and (p_size != "1"):
|
||
|
## for some parent devices we have to remove a 'p' (partition)
|
||
|
## an example are partitionable mdadm raid devices (e.g. md1p1)
|
||
|
p_parent = re.sub('p?[1-9]?[0-9]$', '', p_device)
|
||
|
if p_parent == p_device:
|
||
|
if [e for e in ret_list
|
||
|
if re.search('^' + p_parent + 'p?[1-9]?[0-9]$', e)]:
|
||
|
## major partition - its children are already in the list
|
||
|
pass
|
||
|
else:
|
||
|
## major partition - but there are no children for now
|
||
|
ret_list.append(p_device)
|
||
|
else:
|
||
|
## minor partition - remove parent if necessary
|
||
|
if p_parent in ret_list:
|
||
|
ret_list.remove(p_parent)
|
||
|
ret_list.append(p_device)
|
||
|
line = fpart.readline()
|
||
|
finally:
|
||
|
fpart.close()
|
||
|
return [ get_absolute_devicename(e) for e in ret_list ]
|
||
|
except IOError:
|
||
|
LOGGER.warning("Could not read /proc/partitions")
|
||
|
return []
|
||
|
|
||
|
|
||
|
def get_absolute_devicename(shortname):
|
||
|
""" returns the absolute file name of a device (e.g.: "hda1" -> "/dev/hda1")
|
||
|
this does also work for device mapper devices
|
||
|
if the result is non-unique, one arbitrary value is returned
|
||
|
"""
|
||
|
if re.search('^/', shortname):
|
||
|
return shortname
|
||
|
default = os.path.join("/dev", shortname)
|
||
|
if os.path.exists(default):
|
||
|
return default
|
||
|
result = find_major_minor_of_device(shortname)
|
||
|
## if no valid major/minor was found -> exit
|
||
|
if not result:
|
||
|
return default
|
||
|
(major, minor) = result
|
||
|
## for device-mapper devices (major == 254) ...
|
||
|
if major == 254:
|
||
|
result = find_major_minor_device("/dev/mapper", major, minor)
|
||
|
if result:
|
||
|
return result[0]
|
||
|
## now check all files in /dev
|
||
|
result = find_major_minor_device("/dev", major, minor)
|
||
|
if result:
|
||
|
return result[0]
|
||
|
return default
|
||
|
|
||
|
|
||
|
def find_major_minor_of_device(device):
|
||
|
"""Return the major/minor numbers of a block device.
|
||
|
"""
|
||
|
if re.match("/", device) or \
|
||
|
not os.path.exists(os.path.join(os.path.sep, "sys", "block", device)):
|
||
|
## maybe it is an absolute device name
|
||
|
if not os.path.exists(device):
|
||
|
return None
|
||
|
## okay - it seems to to a device node
|
||
|
rdev = os.stat(device).st_rdev
|
||
|
return (os.major(rdev), os.minor(rdev))
|
||
|
blockdev_info_file = os.path.join(os.path.join(
|
||
|
os.path.sep,"sys","block", device), "dev")
|
||
|
try:
|
||
|
f_blockdev_info = open(blockdev_info_file, "r")
|
||
|
blockdev_info = f_blockdev_info.read()
|
||
|
f_blockdev_info.close()
|
||
|
(str_major, str_minor) = blockdev_info.split(":")
|
||
|
## numeric conversion
|
||
|
try:
|
||
|
major = int(str_major)
|
||
|
minor = int(str_minor)
|
||
|
return (major, minor)
|
||
|
except ValueError:
|
||
|
## unknown device numbers -> stop guessing
|
||
|
return None
|
||
|
except IOError:
|
||
|
pass
|
||
|
return None
|
||
|
|
||
|
|
||
|
def find_major_minor_device(dirpath, major, minor):
|
||
|
"""Returns the names of devices with the specified major and minor number.
|
||
|
"""
|
||
|
collected = []
|
||
|
try:
|
||
|
subdirs = [os.path.join(dirpath, e) for e in os.listdir(dirpath)
|
||
|
if (not os.path.islink(os.path.join(dirpath, e))) and \
|
||
|
os.path.isdir(os.path.join(dirpath, e))]
|
||
|
## do a recursive call to parse the directory tree
|
||
|
for dirs in subdirs:
|
||
|
collected.extend(find_major_minor_device(dirs, major, minor))
|
||
|
## filter all device inodes in this directory
|
||
|
collected.extend([os.path.realpath(os.path.join(dirpath, e))
|
||
|
for e in os.listdir(dirpath)
|
||
|
if (os.major(os.stat(os.path.join(dirpath, e)).st_rdev) == major) \
|
||
|
and (os.minor(os.stat(os.path.join(dirpath, e)).st_rdev) == minor)])
|
||
|
## remove double entries
|
||
|
result = []
|
||
|
for item in collected:
|
||
|
if item not in result:
|
||
|
result.append(item)
|
||
|
return result
|
||
|
except OSError:
|
||
|
return []
|
||
|
|
||
|
|
||
|
def get_parent_blockdevices():
|
||
|
"""Return a list of all block devices that contain other devices.
|
||
|
"""
|
||
|
devs = []
|
||
|
for line in file("/proc/partitions"):
|
||
|
p_details = line.split()
|
||
|
## we expect four values - otherwise continue with next iteration
|
||
|
if len(p_details) != 4:
|
||
|
continue
|
||
|
(p_major, p_minor, p_size, p_device) = p_details
|
||
|
## we expect numeric values in the first two columns
|
||
|
if re.search(r'\D', p_major) or re.search(r'\D', p_minor):
|
||
|
continue
|
||
|
## now let us check, if it is a (parent) block device or a partition
|
||
|
if not os.path.isdir(os.path.join(os.path.sep, "sys", "block", p_device)):
|
||
|
continue
|
||
|
devs.append(p_device)
|
||
|
return [ get_absolute_devicename(e) for e in devs ]
|
||
|
|
||
|
|
||
|
def is_part_of_blockdevice(parent, subdevice):
|
||
|
"""Check if the given block device is a parent of 'subdevice'.
|
||
|
|
||
|
e.g. for checking if a partition belongs to a block device
|
||
|
"""
|
||
|
try:
|
||
|
(par_major, par_minor) = find_major_minor_of_device(parent)
|
||
|
(sub_major, sub_minor) = find_major_minor_of_device(subdevice)
|
||
|
except TypeError:
|
||
|
## at least one of these devices did not return a valid major/minor combination
|
||
|
return False
|
||
|
## search the entry below '/sys/block' belonging to the parent
|
||
|
root = os.path.join(os.path.sep, 'sys', 'block')
|
||
|
for bldev in os.listdir(root):
|
||
|
blpath = os.path.join(root, bldev, 'dev')
|
||
|
if os.access(blpath, os.R_OK):
|
||
|
try:
|
||
|
if (str(par_major), str(par_minor)) == tuple([e
|
||
|
for e in file(blpath)][0].strip().split(":",1)):
|
||
|
parent_path = os.path.join(root, bldev)
|
||
|
break
|
||
|
except (IndexError, OSError):
|
||
|
pass
|
||
|
else:
|
||
|
## no block device with this major/minor combination found below '/sys/block'
|
||
|
return False
|
||
|
for subbldev in os.listdir(parent_path):
|
||
|
subblpath = os.path.join(parent_path, subbldev, "dev")
|
||
|
if os.access(subblpath, os.R_OK):
|
||
|
try:
|
||
|
if (str(sub_major), str(sub_minor)) == tuple([e
|
||
|
for e in file(subblpath)][0].strip().split(":",1)):
|
||
|
## the name of the subdevice node is not important - we found it!
|
||
|
return True
|
||
|
except (IndexError, OSError):
|
||
|
pass
|
||
|
return False
|
||
|
|
||
|
|
||
|
def get_blockdevice_size(device):
|
||
|
"""Return the size of a blockdevice in megabyte.
|
||
|
"""
|
||
|
if not device:
|
||
|
return -1
|
||
|
try:
|
||
|
rdev = os.stat(device).st_rdev
|
||
|
except OSError:
|
||
|
return -1
|
||
|
minor = os.minor(rdev)
|
||
|
major = os.major(rdev)
|
||
|
for line in file("/proc/partitions"):
|
||
|
try:
|
||
|
elements = line.split()
|
||
|
if len(elements) != 4:
|
||
|
continue
|
||
|
if (int(elements[0]) == major) and (int(elements[1]) == minor):
|
||
|
return int(elements[2])/1024
|
||
|
except ValueError:
|
||
|
pass
|
||
|
return -1
|
||
|
|
||
|
|
||
|
def get_blockdevice_size_humanly(device):
|
||
|
"""Return a human readable size of a blockdevice.
|
||
|
"""
|
||
|
size = get_blockdevice_size(device)
|
||
|
if size > 5120:
|
||
|
return "%dGB" % int(size/1024)
|
||
|
else:
|
||
|
return "%dMB" % size
|
||
|
|