@ -26,6 +26,7 @@ __revision__ = "$Id"
import subprocess
import os
import re
import time
from cryptobox . core . exceptions import *
@ -36,6 +37,10 @@ CONTAINERTYPES = {
" swap " : 3 ,
}
FSTYPES = {
" plain " : [ " ext3 " , " ext2 " , " vfat " , " reiserfs " ] ,
" swap " : [ " swap " ] }
## we use this marker to make sure, that we do not remove a non-cryptobox directory
## below the mount directory
@ -46,10 +51,6 @@ class CryptoBoxContainer:
""" Manage a container of the CryptoBox
"""
__fsTypes = {
" plain " : [ " ext3 " , " ext2 " , " vfat " , " reiserfs " ] ,
" swap " : [ " swap " ] }
__dmDir = " /dev/mapper "
@ -101,10 +102,10 @@ class CryptoBoxContainer:
raise CBVolumeIsActive ( " the container must not be active during renaming " )
if not re . search ( r ' ^[a-zA-Z0-9_ \ . \ - ]+$ ' , new_name ) :
raise CBInvalidName ( " the supplied new name contains illegal characters " )
## check for another partition s with the same name
## check for another partition with the same name
if self . cbox . get_container_list ( filter_name = new_name ) :
raise CBNameIsInUse ( " the supplied new name is already in use for anonther partition " )
## maybe there a is an entry in the volumes database (but the partition is not active
## maybe there a is an entry in the volumes database (but the partition is not active )
try :
## remove possibly existing inactive database item
del self . cbox . prefs . volumes_db [ new_name ]
@ -182,23 +183,64 @@ class CryptoBoxContainer:
self . umount = self . __umount_plain
def create ( self , cont_type , password = None ):
def create ( self , cont_type , password = None , fs_type = " ext3 " ):
""" Format a container.
Also set a password for encrypted container .
"""
if not fs_type in FSTYPES [ " plain " ] :
raise CBInvalidType ( " invalid filesystem type supplied: %s " % str ( fs_type ) )
old_name = self . get_name ( )
if cont_type == CONTAINERTYPES [ " luks " ] :
self . __create_luks ( password )
self . __create_luks ( password , fs_type )
elif cont_type == CONTAINERTYPES [ " plain " ] :
self . __create_plain ( )
self . __create_plain ( fs_type )
else :
raise CBInvalidType ( " invalid container type ( %d ) supplied " % ( cont_type , ) )
## no exception was raised during creation -> we can continue
## reset the properties (encryption state, ...) of the device
self . reset_object ( )
## restore the old name (must be after reset_object)
self . set_name ( old_name )
try :
self . set_name ( old_name )
except CBNameIsInUse :
## failure is okay
pass
def set_busy ( self , new_state , time_limit = 300 ) :
""" Set the current busy state.
The timelimit is specified in seconds .
"""
if new_state :
self . cbox . busy_devices [ self . device ] = int ( time . time ( ) + time_limit )
else :
try :
if self . cbox . busy_devices [ self . device ] :
del self . cbox . busy_devices [ self . device ]
except KeyError :
pass
def is_busy ( self ) :
""" Check the busy state of the container.
"""
if not self . cbox . busy_devices . has_key ( self . device ) :
self . cbox . log . debug ( " no ' busy ' attribute for ' %s ' " % self . get_name ( ) )
return False
## invalid value - can happen after saving and loading the database
if not isinstance ( self . cbox . busy_devices [ self . device ] , int ) :
self . cbox . log . debug ( " invalid ' busy ' attribute for ' %s ' " % self . get_name ( ) )
del db_entry [ " busy " ]
return False
if time . time ( ) > = self . cbox . busy_devices [ self . device ] :
self . cbox . log . debug ( " expired ' busy ' attribute for ' %s ' " % self . get_name ( ) )
del db_entry [ " busy " ]
return False
## lock is still active
self . cbox . log . debug ( " active ' busy ' attribute for ' %s ' " % self . get_name ( ) )
return True
def change_password ( self , oldpw , newpw ) :
@ -362,9 +404,9 @@ class CryptoBoxContainer:
if self . __is_luks_partition ( ) :
return CONTAINERTYPES [ " luks " ]
type_of_partition = self . __get_type_id_of_partition ( )
if type_of_partition in self . __fsTypes [ " plain " ] :
if type_of_partition in FSTYPES [ " plain " ] :
return CONTAINERTYPES [ " plain " ]
if type_of_partition in self . __fsTypes [ " swap " ] :
if type_of_partition in FSTYPES [ " swap " ] :
return CONTAINERTYPES [ " swap " ]
return CONTAINERTYPES [ " unused " ]
@ -372,29 +414,28 @@ class CryptoBoxContainer:
def __get_type_id_of_partition ( self ) :
" returns the type of the partition (see ' man blkid ' ) "
devnull = None
try :
devnull = open ( os . devnull , " w " )
except IOError :
self . cbox . log . warn ( " Could not open %s " % ( os . devnull , ) )
proc = subprocess . Popen (
shell = False ,
stdin = None ,
stdout = subprocess . PIPE ,
stderr = subprocess . PIPE ,
args = [ self . cbox . prefs [ " Programs " ] [ " blkid " ] ,
shell = False ,
stdout = subprocess . PIPE ,
stderr = subprocess . PIPE ,
args = [ self . cbox . prefs [ " Programs " ] [ " blkid " ] ,
" -s " , " TYPE " ,
" -o " , " value " ,
" -c " , os . devnull ,
" -w " , os . devnull ,
self . device ] )
proc . wait ( )
output = proc . stdout . read ( ) . strip ( )
if proc . returncode != 0 :
self . device ] )
( stdout , stder ) = proc . communicate ( )
if proc . returncode == 0 :
## we found a uuid
return stdout . strip ( )
elif proc . returncode == 2 :
## failed to find the attribute - no problem
return None
else :
## something strange happened
self . cbox . log . warn ( " retrieving of partition type via ' blkid ' failed: %s " % \
( proc . stderr . read ( ) . strip ( ) , ) )
( stderr. strip ( ) , ) )
return None
devnull . close ( )
return output
def __is_luks_partition ( self ) :
@ -596,50 +637,63 @@ class CryptoBoxContainer:
self . cbox . send_event_notification ( " postumount " , self . __get_event_args ( ) )
def __create_plain ( self ):
def __create_plain ( self , fs_type = " ext3 " ):
" make a plaintext partition "
import threading
if self . is_mounted ( ) :
raise CBVolumeIsActive ( " deactivate the partition before filesystem initialization " )
devnull = None
try :
devnull = open ( os . devnull , " w " )
except IOError :
self . cbox . log . warn ( " Could not open %s " % ( os . devnull , ) )
proc = subprocess . Popen (
shell = False ,
stdin = None ,
stdout = devnull ,
stderr = subprocess . PIPE ,
args = [
self . cbox . prefs [ " Programs " ] [ " mkfs-data " ] ,
self . device ] )
proc . wait ( )
if proc . returncode != 0 :
err_msg = " Could not create the filesystem: %s " % ( proc . stderr . read ( ) . strip ( ) , )
self . cbox . log . error ( err_msg )
raise CBCreateError ( err_msg )
devnull . close ( )
def __create_luks ( self , password ) :
raise CBVolumeIsActive (
" deactivate the partition before filesystem initialization " )
def format ( ) :
import os
old_name = self . get_name ( )
self . set_busy ( True , 600 )
self . cbox . log . debug ( " Turn the busy flag on: %s " % self . device )
## give the main thread a chance to continue
child_pid = os . fork ( )
if child_pid == 0 :
proc = subprocess . Popen (
shell = False ,
stdin = None ,
stdout = subprocess . PIPE ,
stderr = subprocess . PIPE ,
args = [
self . cbox . prefs [ " Programs " ] [ " nice " ] ,
self . cbox . prefs [ " Programs " ] [ " mkfs " ] ,
" -t " , fs_type , self . device ] )
( stdout , sterr ) = proc . communicate ( )
## for to allow error detection
if proc . returncode == 0 :
time . sleep ( 5 )
## skip cleanup stuff (as common for sys.exit)
os . _exit ( 0 )
else :
os . waitpid ( child_pid , 0 )
self . set_name ( old_name )
self . set_busy ( False )
self . cbox . log . debug ( " Turn the busy flag off: %s " % self . device )
bg_task = threading . Thread ( target = format )
bg_task . start ( )
time . sleep ( 3 )
## if the thread exited very fast, then it failed
if not bg_task . isAlive ( ) :
raise CBCreateError ( " Failed to initilize device: %s " % self . device )
def __create_luks ( self , password , fs_type = " ext3 " ) :
""" Create a luks partition.
"""
import threading
if not password :
raise CBInvalidPassword ( " no password supplied for new luks mapping " )
if self . is_mounted ( ) :
raise CBVolumeIsActive ( " deactivate the partition before filesystem initialization " )
devnull = None
try :
devnull = open ( os . devnull , " w " )
except IOError :
self . cbox . log . warn ( " Could not open %s " % ( os . devnull , ) )
## remove any potential open luks mapping
self . __umount_luks ( )
## create the luks header
proc = subprocess . Popen (
shell = False ,
stdin = subprocess . PIPE ,
stdout = devnull ,
stdout = subprocess. PIPE ,
stderr = subprocess . PIPE ,
args = [
self . cbox . prefs [ " Programs " ] [ " super " ] ,
@ -660,7 +714,7 @@ class CryptoBoxContainer:
proc = subprocess . Popen (
shell = False ,
stdin = subprocess . PIPE ,
stdout = devnull ,
stdout = subprocess. PIPE ,
stderr = subprocess . PIPE ,
args = [
self . cbox . prefs [ " Programs " ] [ " super " ] ,
@ -676,24 +730,44 @@ class CryptoBoxContainer:
err_msg = " Could not open the new luks mapping: %s " % ( errout . strip ( ) , )
self . cbox . log . error ( err_msg )
raise CBCreateError ( err_msg )
## make the filesystem
proc = subprocess . Popen (
shell = False ,
stdin = None ,
stdout = devnull ,
stderr = subprocess . PIPE ,
args = [
self . cbox . prefs [ " Programs " ] [ " mkfs-data " ] ,
os . path . join ( self . __dmDir , self . name ) ] )
proc . wait ( )
## remove the mapping - for every exit status
self . __umount_luks ( )
if proc . returncode != 0 :
err_msg = " Could not create the filesystem: %s " % ( proc . stderr . read ( ) . strip ( ) , )
self . cbox . log . error ( err_msg )
## remove the luks mapping
raise CBCreateError ( err_msg )
devnull . close ( )
def format_luks ( ) :
import os
old_name = self . get_name ( )
self . set_busy ( True , 600 )
self . cbox . log . debug ( " Turn the busy flag on: %s " % self . device )
child_pid = os . fork ( )
if child_pid == 0 :
## make the filesystem
proc = subprocess . Popen (
shell = False ,
stdin = None ,
stdout = subprocess . PIPE ,
stderr = subprocess . PIPE ,
args = [
self . cbox . prefs [ " Programs " ] [ " nice " ] ,
self . cbox . prefs [ " Programs " ] [ " mkfs " ] ,
" -t " , fs_type ,
os . path . join ( self . __dmDir , self . name ) ] )
( stdou , stderr ) = proc . communicate ( )
## wait to allow error detection
if proc . returncode == 0 :
time . sleep ( 5 )
## skip cleanup stuff (as common for sys.exit)
os . _exit ( 0 )
else :
os . waitpid ( child_pid , 0 )
self . set_name ( old_name )
self . set_busy ( False )
self . cbox . log . debug ( " Turn the busy flag off: %s " % self . device )
## remove the mapping - for every exit status
self . __umount_luks ( )
bg_task = threading . Thread ( target = format_luks )
bg_task . setDaemon ( True )
bg_task . start ( )
time . sleep ( 3 )
## if the thread exited very fast, then it failed
if not bg_task . isAlive ( ) :
raise CBCreateError ( " Failed to initilize device: %s " % self . device )
def __clean_mount_dirs ( self ) :