diff options
-rw-r--r-- | .SRCINFO | 20 | ||||
-rw-r--r-- | PKGBUILD | 28 | ||||
-rw-r--r-- | patch | 1320 |
3 files changed, 1368 insertions, 0 deletions
diff --git a/.SRCINFO b/.SRCINFO new file mode 100644 index 000000000000..7ea0a9bbc670 --- /dev/null +++ b/.SRCINFO @@ -0,0 +1,20 @@ +pkgbase = pmount-safe-removal + pkgdesc = mount removable devices as normal user, with safe removal of device + pkgver = 0.9.23 + pkgrel = 4 + url = http://pmount.alioth.debian.org/ + arch = i686 + arch = x86_64 + license = GPL2 + makedepends = intltool + depends = sysfsutils>=2.0.0-1 + depends = bash + provides = pmount=0.9.23 + backup = etc/pmount.allow + source = https://alioth.debian.org/frs/download.php/3310/pmount-0.9.23.tar.bz2 + source = patch + md5sums = db19f5bf3151b1b41705ec7bafa439d3 + md5sums = a1c8ef20e809ab63b7f89d2c68b05620 + +pkgname = pmount-safe-removal + diff --git a/PKGBUILD b/PKGBUILD new file mode 100644 index 000000000000..8b3c946648a2 --- /dev/null +++ b/PKGBUILD @@ -0,0 +1,28 @@ +# Maintainer: jjacky +# Contributor: Tobias Powalowski <tpowa@archlinux.org> + +_pkgname=pmount +pkgname=$_pkgname-safe-removal +pkgver=0.9.23 +pkgrel=4 +pkgdesc="mount removable devices as normal user, with safe removal of device" +arch=(i686 x86_64) +license=('GPL2') +url="http://pmount.alioth.debian.org/" +backup=('etc/pmount.allow') +depends=('sysfsutils>=2.0.0-1' 'bash') +makedepends=('intltool') +provides=('pmount=0.9.23') +source=(https://alioth.debian.org/frs/download.php/3310/pmount-0.9.23.tar.bz2 + patch) + +build() { + cd "${srcdir}/${_pkgname}-${pkgver}" + patch -p1 -i ../patch + ./configure --prefix=/usr --disable-hal + make + make DESTDIR="${pkgdir}" install +} + +md5sums=('db19f5bf3151b1b41705ec7bafa439d3' + 'a1c8ef20e809ab63b7f89d2c68b05620') diff --git a/patch b/patch new file mode 100644 index 000000000000..e7a4a8f736a4 --- /dev/null +++ b/patch @@ -0,0 +1,1320 @@ +diff -r 97fe1772e5c4 src/Makefile.am +--- a/src/Makefile.am Sun Nov 20 20:46:53 2011 +0100 ++++ b/src/Makefile.am Mon Nov 21 15:00:50 2011 +0100 +@@ -2,11 +2,12 @@ + + noinst_LIBRARIES = libpmount-util.a + libpmount_util_a_SOURCES = \ +- fs.c \ +- luks.c \ +- policy.c \ +- utils.c \ +- realpath.c ++ fs.c \ ++ luks.c \ ++ policy.c \ ++ utils.c \ ++ realpath.c \ ++ conf.c + + if PMOUNT_HAL + EXTRABIN=pmount-hal +@@ -25,11 +26,12 @@ + pumount_LDADD = libpmount-util.a + + EXTRA_DIST = \ +- fs.h \ +- luks.h \ +- policy.h \ +- utils.h \ +- realpath.h ++ fs.h \ ++ luks.h \ ++ policy.h \ ++ utils.h \ ++ realpath.h \ ++ conf.h + + INSTALL_DIR = $(DESTDIR)/$(prefix)/bin + INSTALL_SRC = $(top_builddir)/src +diff -r 97fe1772e5c4 src/Makefile.in +--- a/src/Makefile.in Sun Nov 20 20:46:53 2011 +0100 ++++ b/src/Makefile.in Mon Nov 21 15:00:50 2011 +0100 +@@ -52,7 +52,8 @@ + libpmount_util_a_AR = $(AR) $(ARFLAGS) + libpmount_util_a_LIBADD = + am_libpmount_util_a_OBJECTS = fs.$(OBJEXT) luks.$(OBJEXT) \ +- policy.$(OBJEXT) utils.$(OBJEXT) realpath.$(OBJEXT) ++ policy.$(OBJEXT) utils.$(OBJEXT) realpath.$(OBJEXT) \ ++ conf.$(OBJEXT) + libpmount_util_a_OBJECTS = $(am_libpmount_util_a_OBJECTS) + @PMOUNT_HAL_TRUE@am__EXEEXT_1 = pmount-hal$(EXEEXT) + am__installdirs = "$(DESTDIR)$(bindir)" +@@ -106,6 +107,7 @@ + DATADIRNAME = @DATADIRNAME@ + DEFS = @DEFS@ + DEPDIR = @DEPDIR@ ++DLLTOOL = @DLLTOOL@ + DSYMUTIL = @DSYMUTIL@ + DUMPBIN = @DUMPBIN@ + ECHO_C = @ECHO_C@ +@@ -141,6 +143,7 @@ + LTLIBOBJS = @LTLIBOBJS@ + MAINT = @MAINT@ + MAKEINFO = @MAKEINFO@ ++MANIFEST_TOOL = @MANIFEST_TOOL@ + MKDIR_P = @MKDIR_P@ + MKINSTALLDIRS = @MKINSTALLDIRS@ + MSGFMT = @MSGFMT@ +@@ -179,6 +182,7 @@ + abs_srcdir = @abs_srcdir@ + abs_top_builddir = @abs_top_builddir@ + abs_top_srcdir = @abs_top_srcdir@ ++ac_ct_AR = @ac_ct_AR@ + ac_ct_CC = @ac_ct_CC@ + ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ + am__include = @am__include@ +@@ -211,7 +215,6 @@ + libexecdir = @libexecdir@ + localedir = @localedir@ + localstatedir = @localstatedir@ +-lt_ECHO = @lt_ECHO@ + mandir = @mandir@ + mkdir_p = @mkdir_p@ + oldincludedir = @oldincludedir@ +@@ -230,11 +233,12 @@ + INCLUDES = $(HAL_CFLAGS) + noinst_LIBRARIES = libpmount-util.a + libpmount_util_a_SOURCES = \ +- fs.c \ +- luks.c \ +- policy.c \ +- utils.c \ +- realpath.c ++ fs.c \ ++ luks.c \ ++ policy.c \ ++ utils.c \ ++ realpath.c \ ++ conf.c + + @PMOUNT_HAL_TRUE@EXTRABIN = pmount-hal + pmount_SOURCES = pmount.c +@@ -244,11 +248,12 @@ + pumount_SOURCES = pumount.c + pumount_LDADD = libpmount-util.a + EXTRA_DIST = \ +- fs.h \ +- luks.h \ +- policy.h \ +- utils.h \ +- realpath.h ++ fs.h \ ++ luks.h \ ++ policy.h \ ++ utils.h \ ++ realpath.h \ ++ conf.h + + INSTALL_DIR = $(DESTDIR)/$(prefix)/bin + INSTALL_SRC = $(top_builddir)/src +@@ -361,6 +366,7 @@ + distclean-compile: + -rm -f *.tab.c + ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conf.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fs.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/luks.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pmount-hal.Po@am__quote@ +diff -r 97fe1772e5c4 src/conf.c +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/src/conf.c Mon Nov 21 15:00:50 2011 +0100 +@@ -0,0 +1,137 @@ ++ ++#include <stdio.h> ++#include <stdlib.h> ++#include <limits.h> ++#include <string.h> ++ ++#include "conf.h" ++#include "utils.h" ++#include "realpath.h" ++ ++#define BUFLEN 1024 ++ ++int ++get_conf_for_device(const char *device, char **fs, char **charset, ++ char **passphrase, char **mntpt, char **options) ++{ ++ FILE *f; ++ char buffer[BUFLEN], section[PATH_MAX]; ++ char *buf, *sec, *s; ++ int skip_section = 0; ++ int in_section = 0; ++ int len; ++ ++ if( NULL == ( f = fopen( CONF_FILE, "r" ) ) ) { ++ debug( "unable to open conf file %s\n", CONF_FILE ); ++ return 1; ++ } ++ sec = NULL; ++ while( !feof( f ) ) { ++ if( fgets( buffer, BUFLEN, f ) ) { ++ /* skip spaces & tabs */ ++ for ( buf = buffer; *buf == ' ' || *buf == '\t'; ++buf ) ++ ; ++ /* ignore commented & empty lines */ ++ if( *buf == ';' || *buf == '#' || *buf == '\n' ) { ++ continue; ++ } ++ /* new section? */ ++ if( *buf == '[' ) { ++ /* if we were in section, we're done */ ++ if( in_section ) { ++ break; ++ } ++ ++buf; ++ if( NULL == ( s = strchr( buf, ']' ) ) ) { ++ fclose( f ); ++ fprintf( stderr, "invalid syntax in %s: %s\n", CONF_FILE, buf ); ++ return 2; ++ } ++ if( s - buf >= PATH_MAX ) { ++ fclose( f ); ++ fprintf( stderr, "invalid section name in %s: %s\n", CONF_FILE, buf ); ++ return 3; ++ } ++ strncpy( section, buf, s - buf ); ++ /* NULL-terminate the string */ ++ s = section + (s - buf); ++ *s = 0; ++ debug( "found section for %s\n", section ); ++ /* try to resolve, might be e.g. a /dev/disk/by-uuid/... */ ++ if( !realpath( section, section ) ) { ++ if( !is_block( section ) ) { ++ /* probably section for a device not plugged in */ ++ debug( "unable to resolve, not a block, skipping section\n" ); ++ skip_section = 1; ++ continue; ++ } ++ } else { ++ debug( "resolved to %s\n", section ); ++ } ++ /* is this the device we're looking for? */ ++ if( strcmp( device, section ) ) { ++ debug( "no match, skipping section\n" ); ++ skip_section = 1; ++ continue; ++ } ++ debug( "match found!\n" ); ++ sec = section; ++ skip_section = 0; ++ in_section = 1; ++ continue; ++ } else if( skip_section ) { ++ continue; ++ } else if( NULL == sec ) { ++ fclose( f ); ++ debug( "no matching section found\n" ); ++ return -1; ++ } ++ /* we're in a section, must be a name=value */ ++ if ( NULL == ( s = strchr( buf, '=' ) ) ) { ++ fprintf( stderr, "invalid syntax in %s: %s\n", CONF_FILE, buf ); ++ continue; ++ } ++ /* ignore spaces & tabs */ ++ for ( --s; *s == ' ' || *s == '\t'; --s ) ++ ; ++ ++s; ++ len = s - buf; ++ if( NULL != fs && !strncmp( buf, "fs", len ) ) { ++ conf_set_value( buf, fs ); ++ debug( "file system set to %s\n", *fs ); ++ } else if( NULL != charset && !strncmp( buf, "charset", len )) { ++ conf_set_value( buf, charset ); ++ debug( "charset set to %s\n", *charset ); ++ } else if( NULL != passphrase && !strncmp( buf, "passphrase", len )) { ++ conf_set_value( buf, passphrase ); ++ debug( "passphrase set to %s\n", *passphrase ); ++ } else if( NULL != mntpt && !strncmp( buf, "mntpt", len )) { ++ conf_set_value( buf, mntpt ); ++ debug( "mount point set to %s\n", *mntpt ); ++ } else if( NULL != options && !strncmp( buf, "options", len )) { ++ conf_set_value( buf, options ); ++ debug( "options set to %s\n", *options ); ++ } else { ++ debug( "ignoring: %s", buf ); ++ } ++ } ++ } ++ fclose( f ); ++ return !in_section; ++} ++ ++void ++conf_set_value( char *buf, char **dest ) ++{ ++ char *s; ++ /* position to beginning of value */ ++ buf = strchr( buf, '=' ); ++ /* skip spaces & tabs */ ++ for( ++buf; *buf == ' ' || *buf == '\t'; ++buf) ++ ; ++ /* find end position */ ++ for( s = buf; *s != ' ' && *s != ';' && *s != '#' && *s != '\n' && *s != 0; ++s) ++ ; ++ /* set value */ ++ *dest = strndup( buf, s - buf ); ++} +diff -r 97fe1772e5c4 src/conf.h +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/src/conf.h Mon Nov 21 15:00:50 2011 +0100 +@@ -0,0 +1,17 @@ ++ ++#ifndef CONF_H ++#define CONF_H ++ ++#define CONF_FILE "/etc/pmount.conf" ++ ++ ++int ++get_conf_for_device(const char *device, char **fs, char **charset, ++ char **passphrase, char **mntpt, char **options); ++ ++void ++conf_set_value( char *buf, char **dest ); ++ ++ ++#endif /* CONF_H */ ++ +diff -r 97fe1772e5c4 src/luks.c +--- a/src/luks.c Sun Nov 20 20:46:53 2011 +0100 ++++ b/src/luks.c Mon Nov 21 15:00:50 2011 +0100 +@@ -82,7 +82,7 @@ + else if( status == 1 ) + result = DECRYPT_FAILED; + else { +- fprintf( stderr, "Internal error: cryptsetup luksOpen failed" ); ++ fprintf( stderr, "Internal error: cryptsetup luksOpen failed\n" ); + exit( 100 ); + } + +diff -r 97fe1772e5c4 src/pmount.c +--- a/src/pmount.c Sun Nov 20 20:46:53 2011 +0100 ++++ b/src/pmount.c Mon Nov 21 15:00:50 2011 +0100 +@@ -28,6 +28,7 @@ + #include "policy.h" + #include "utils.h" + #include "luks.h" ++#include "conf.h" + #include "config.h" + + /* Enable autodetection if possible */ +@@ -82,6 +83,7 @@ + " -s, --sync : mount <device> with the 'sync' option (default: 'async')\n" + " -A, --noatime\n" + " mount <device> with the 'noatime' option (default: 'atime')\n" ++ " -D : mount all partitions of <device> (or its parent, if a partition)\n" + " -e, --exec : mount <device> with the 'exec' option (default: 'noexec')\n" + " -t <fs> : mount as file system type <fs> (default: autodetected)\n" + " -c <charset>: use given I/O character set (default: 'utf8' if called\n" +@@ -223,6 +225,7 @@ + * @param fmask User specified fmask (NULL for umask) + * @param dmask User specified dmask (NULL for umask) + * @param suppress_errors: if true, stderr is redirected to /dev/null ++ * @param fs_options Options to use instead of the default from FS (if not NULL) + * @return exit status of mount, or -1 on failure. + */ + int +@@ -230,7 +233,8 @@ + int noatime, int exec, int force_write, const char* iocharset, + int utf8, + const char* umask, const char *fmask, const char *dmask, +- int suppress_errors ) ++ int suppress_errors, ++ const char *fs_options ) + { + const struct FS* fs; + char ugid_opt[100]; +@@ -370,9 +374,13 @@ + snprintf( iocharset_opt, sizeof( iocharset_opt ), + fs->iocharset_format, "iso8859-1"); + } ++ ++ if( NULL == fs_options ) { ++ fs_options = fs->options; ++ } + + snprintf( options, sizeof( options ), "%s%s%s%s%s%s%s%s%s", +- fs->options, sync_opt, atime_opt, exec_opt, access_opt, ugid_opt, ++ fs_options, sync_opt, atime_opt, exec_opt, access_opt, ugid_opt, + umask_opt, fdmask_opt, iocharset_opt ); + + /* go for it */ +@@ -397,13 +405,15 @@ + * @param umask User specified umask (NULL for default) + * @param fmask User specified fmask (NULL for umask) + * @param dmask User specified dmask (NULL for umask) ++ * @param fs_options Options to use instead of the default from FS (if not NULL) + * @return last return value of do_mount (i. e. 0 on success, != 0 on error) + */ + int + do_mount_auto( const char* device, const char* mntpt, int async, + int noatime, int exec, int force_write, const char* iocharset, + int utf8, +- const char* umask, const char *fmask, const char *dmask ) ++ const char* umask, const char *fmask, const char *dmask, ++ const char *fs_options ) + { + const struct FS* fs; + int nostderr = 1; +@@ -424,7 +434,7 @@ + } + result = do_mount( device, mntpt, tp, async, noatime, exec, + force_write, iocharset, utf8, umask, fmask, +- dmask, nostderr ); ++ dmask, nostderr, fs_options ); + if(result == 0) + return result; + debug("blkid-detected FS failed, trying manually \n"); +@@ -445,14 +455,16 @@ + if( (fs+1)->fsname == NULL ) + nostderr = 0; + result = do_mount( device, mntpt, fs->fsname, async, noatime, exec, +- force_write, iocharset, utf8, umask, fmask, dmask, nostderr ); ++ force_write, iocharset, utf8, umask, fmask, dmask, nostderr, ++ fs_options ); + if( result == 0 ) + break; + + /* sometimes VFAT fails when using iocharset; try again without */ + if( iocharset ) + result = do_mount( device, mntpt, fs->fsname, async, noatime, exec, +- force_write, NULL, utf8, umask, fmask, dmask, nostderr ); ++ force_write, NULL, utf8, umask, fmask, dmask, nostderr, ++ fs_options ); + if( result <= 0 ) + break; + } +@@ -602,33 +614,207 @@ + drop_root(); + } + ++ ++static char *devarg = NULL, *arg2 = NULL; ++static char mntpt[MEDIA_STRING_SIZE]; ++static char device[PATH_MAX], mntptdev[PATH_MAX]; ++static int async = 1; ++static int noatime = 0; ++static int exec = 0; ++static int force_write = -1; /* 0: ro, 1: rw, -1: default */ ++static const char* use_fstype = NULL; ++static const char* iocharset = NULL; ++static const char* _umask = NULL; ++static const char* _fmask = NULL; ++static const char* _dmask = NULL; ++static const char* passphrase = NULL; ++ ++static enum { MOUNT, LOCK, UNLOCK } mode = MOUNT; ++ ++int ++mount_device( void ) ++{ ++ char decrypted_device[PATH_MAX]; ++ int utf8 = -1; /* Whether we live in a UTF-8 world or not */ ++ int result; ++ ++ static const char *l_use_fstype; ++ static const char *l_iocharset; ++ static const char *l_passphrase; ++ ++ char *o_fs = NULL; ++ char *o_charset = NULL; ++ char *o_passphrase = NULL; ++ char *o_mntpt = NULL; ++ char *o_options = NULL; ++ ++ l_use_fstype = use_fstype; ++ l_iocharset = iocharset; ++ l_passphrase = passphrase; ++ ++ switch( mode ) { ++ case MOUNT: ++ /* let's see if there are options in CONF_FILE */ ++ if( !get_conf_for_device( device, &o_fs, &o_charset, &o_passphrase, ++ &o_mntpt, &o_options ) ) { ++ if( NULL != o_fs ) { ++ l_use_fstype = (const char *) o_fs; ++ } ++ if( NULL != o_charset ) { ++ l_iocharset = (const char *) o_charset; ++ } ++ if( NULL != o_passphrase ) { ++ l_passphrase = (const char *) o_passphrase; ++ } ++ if( NULL != o_mntpt ) { ++ snprintf( mntpt, sizeof (mntpt ), "%s", o_mntpt ); ++ } ++ } ++ ++ /* determine mount point name; note that we use devarg instead of ++ * device to preserve symlink names (like '/dev/usbflash' instead ++ * of '/dev/sda1') */ ++ if( NULL == o_mntpt && make_mountpoint_name( devarg, arg2, mntpt, ++ sizeof( mntpt ) ) ) ++ return E_MNTPT; ++ ++ /* if no charset was set explicitly, autodetect UTF-8 */ ++ if( !l_iocharset ) { ++ const char* codeset; ++ codeset = nl_langinfo( CODESET ); ++ ++ debug( "no iocharset given, current locale encoding is %s\n", codeset ); ++ ++ if( codeset && !strcmp( codeset, "UTF-8" ) ) { ++ debug( "locale encoding uses UTF-8, setting iocharset to 'utf8'\n" ); ++ l_iocharset = "utf8"; ++ } ++ } ++ /* If user did not choose explicitly for or against utf8 */ ++ if( utf8 == -1 ) { ++ const char* codeset; ++ codeset = nl_langinfo( CODESET ); ++ if( codeset && !strcmp( codeset, "UTF-8" ) ) { ++ debug( "locale encoding uses UTF-8: will mount FAT with utf8 option" ); ++ utf8 = 1; ++ } else { ++ utf8 = 0; ++ } ++ } ++ ++ /* clean stale locks */ ++ clean_lock_dir( device ); ++ ++ if( check_mount_policy( device, mntpt ) ) ++ return E_POLICY; ++ ++ /* check for encrypted device */ ++ enum decrypt_status decrypt = luks_decrypt( device, ++ decrypted_device, sizeof( decrypted_device ), l_passphrase, ++ force_write == 0 ? 1 : 0 ); ++ ++ switch (decrypt) { ++ case DECRYPT_FAILED: ++ fprintf( stderr, _("Error: could not decrypt device (wrong passphrase?)\n") ); ++ return E_POLICY; ++ case DECRYPT_EXISTS: ++ fprintf( stderr, _("Error: mapped device already exists\n") ); ++ return E_POLICY; ++ case DECRYPT_OK: ++ /* We create a luks lockfile _on the decrypted device !_*/ ++ if(! luks_create_lockfile(decrypted_device)) ++ fprintf(stderr, _("Warning: could not create luks lockfile\n")); ++ case DECRYPT_NOTENCRYPTED: ++ break; ++ default: ++ fprintf( stderr, "Internal error: unhandled decrypt_status %i\n", ++ (int) decrypt); ++ return E_INTERNAL; ++ } ++ ++ /* lock the mount directory */ ++ debug( "locking mount point directory\n" ); ++ if( lock_dir( mntpt ) < 0) { ++ fprintf( stderr, _("Error: could not lock the mount directory. Another pmount is probably running for this mount point.\n")); ++ return E_LOCKED; ++ } ++ debug( "mount point directory locked\n" ); ++ ++ /* off we go */ ++ if( l_use_fstype ) ++ result = do_mount( decrypted_device, mntpt, l_use_fstype, async, noatime, ++ exec, force_write, l_iocharset, utf8, _umask, _fmask, _dmask, 0, ++ o_options); ++ else ++ result = do_mount_auto( decrypted_device, mntpt, async, noatime, exec, ++ force_write, l_iocharset, utf8, _umask, _fmask, _dmask, o_options ); ++ ++ /* unlock the mount point again */ ++ debug( "unlocking mount point directory\n" ); ++ unlock_dir( mntpt ); ++ debug( "mount point directory unlocked\n" ); ++ ++ if( NULL != o_fs ) { ++ free( o_fs ); ++ } ++ if( NULL != o_charset ) { ++ free( o_charset ); ++ } ++ if( NULL != o_passphrase ) { ++ free( o_passphrase ); ++ } ++ if( NULL != o_mntpt ) { ++ free( o_mntpt ); ++ } ++ if( NULL != o_options ) { ++ free( o_options ); ++ } ++ ++ if( result ) { ++ if( decrypt == DECRYPT_OK ) ++ luks_release( decrypted_device, 0 ); ++ ++ /* mount failed, delete the mount point again */ ++ if( remove_pmount_mntpt( mntpt ) ) { ++ perror( _("Error: could not delete mount point") ); ++ return -1; ++ } ++ return E_EXECMOUNT; ++ } ++ ++ return 0; ++ ++ case LOCK: ++ if( device_valid( device ) ) ++ if( do_lock( device, parse_unsigned( arg2, E_PID ) ) ) ++ return E_INTERNAL; ++ return 0; ++ ++ case UNLOCK: ++ if( device_valid( device ) ) ++ if( do_unlock( device, parse_unsigned( arg2, E_PID ) ) ) ++ return E_UNLOCK; ++ return 0; ++ } ++ ++ fprintf( stderr, _("Internal error: mode %i not handled.\n"), (int) mode ); ++ return E_INTERNAL; ++} ++ ++ ++ + /** + * Entry point. + */ + int + main( int argc, char** argv ) + { +- char *devarg = NULL, *arg2 = NULL; +- char mntpt[MEDIA_STRING_SIZE]; +- char device[PATH_MAX], mntptdev[PATH_MAX]; +- char decrypted_device[PATH_MAX]; + const char* fstab_device; + int is_real_path = 0; +- int async = 1; +- int noatime = 0; +- int exec = 0; +- int force_write = -1; /* 0: ro, 1: rw, -1: default */ +- const char* use_fstype = NULL; +- const char* iocharset = NULL; +- const char* umask = NULL; +- const char* fmask = NULL; +- const char* dmask = NULL; +- const char* passphrase = NULL; +- int utf8 = -1; /* Whether we live in a UTF-8 world or not */ +- int result; ++ int full_device = 0; + +- enum { MOUNT, LOCK, UNLOCK } mode = MOUNT; +- ++ int result, error_occurred = 0; ++ + int option; + static struct option long_opts[] = { + { "help", 0, NULL, 'h'}, +@@ -647,6 +833,7 @@ + { "read-only", 0, NULL, 'r' }, + { "read-write", 0, NULL, 'w' }, + { "version", 0, NULL, 'V' }, ++ { "full-device", 0, NULL, 'D' }, + { NULL, 0, NULL, 0} + }; + +@@ -678,7 +865,7 @@ + + /* parse command line options */ + do { +- switch( option = getopt_long( argc, argv, "+hdelLsArwp:t:c:u:V", long_opts, NULL ) ) { ++ switch( option = getopt_long( argc, argv, "+hdelLsArwp:t:c:u:DV", long_opts, NULL ) ) { + case -1: break; /* end of arguments */ + case ':': + case '?': return E_ARGS; /* unknown argument */ +@@ -701,17 +888,19 @@ + + case 'c': iocharset = optarg; break; + +- case 'u': umask = optarg; break; ++ case 'u': _umask = optarg; break; + +- case OPT_FMASK: fmask = optarg; break; ++ case OPT_FMASK: _fmask = optarg; break; + +- case OPT_DMASK: dmask = optarg; break; ++ case OPT_DMASK: _dmask = optarg; break; + + case 'p': passphrase = optarg; break; + + case 'r': force_write = 0; break; + + case 'w': force_write = 1; break; ++ ++ case 'D': full_device = 1; break; + + case 'V': puts(VERSION); return 0; + +@@ -793,117 +982,76 @@ + fprintf( stderr, _("Error: invalid device %s (must be in /dev/)\n"), device ); + return E_DEVICE; + } ++ ++ /* we need to get the full device name (e.g. /dev/sde), get list of all its ++ partitions, and try to mount them all... */ ++ if( full_device ) { ++ char devdirname[MEDIA_STRING_SIZE]; ++ if( !find_sysfs_device( device, devdirname, MEDIA_STRING_SIZE) ) { ++ fprintf( stderr, _("Warning: unable to find device path for %s," ++ " full-device mode disabled\n"), device ); ++ full_device = 0; ++ } else { ++ debug( "device path for %s is %s\n", device, devdirname ); ++ ++ DIR *partdir; ++ struct dirent *partdirent; ++ char partdirname[MEDIA_STRING_SIZE]; ++ struct stat stat_info; ++ ++ partdir = opendir( devdirname ); ++ if( !partdir ) { ++ perror( _("Error: could not open <sysfs dir>/block/<device>/") ); ++ exit( -1 ); ++ } ++ while( ( partdirent = readdir( partdir ) ) != NULL ) { ++ if( partdirent->d_type != DT_DIR ++ || !strcmp( partdirent->d_name, "." ) ++ || !strcmp( partdirent->d_name, ".." ) ) ++ continue; ++ ++ /* construct /sys/block/<device>/<partition>/dev */ ++ snprintf( partdirname, sizeof( partdirname ), "%s/%s/%s", ++ devdirname, partdirent->d_name, "dev" ); + +- switch( mode ) { +- case MOUNT: +- /* determine mount point name; note that we use devarg instead of +- * device to preserve symlink names (like '/dev/usbflash' instead +- * of '/dev/sda1') */ +- if( make_mountpoint_name( devarg, arg2, mntpt, sizeof( mntpt ) ) ) +- return E_MNTPT; ++ /* make sure it is a device, i.e has a file dev */ ++ if( 0 != stat( partdirname, &stat_info ) ) { ++ /* ENOENT (does not exist) is "okay" we just ignore this one */ ++ if( ENOENT != errno ) { ++ perror( _("Error: could not stat <sysfs dir>/block/<device>/<part>/dev") ); ++ exit( -1 ); ++ } ++ continue; ++ } ++ /* must be a file */ ++ if( !S_ISREG( stat_info.st_mode ) ) { ++ continue; ++ } + +- /* if no charset was set explicitly, autodetect UTF-8 */ +- if( !iocharset ) { +- const char* codeset; +- codeset = nl_langinfo( CODESET ); +- +- debug( "no iocharset given, current locale encoding is %s\n", codeset ); +- +- if( codeset && !strcmp( codeset, "UTF-8" ) ) { +- debug( "locale encoding uses UTF-8, setting iocharset to 'utf8'\n" ); +- iocharset = "utf8"; ++ /* construct /dev/<partition> */ ++ snprintf( device, sizeof( device ), "%s%s", DEVDIR, partdirent->d_name ); ++ debug( "processing found partition: %s\n", device ); ++ ++ /* We need to lookup again in fstab: */ ++ fstab_device = fstab_has_device( "/etc/fstab", device, NULL, NULL ); ++ if( mode == MOUNT && fstab_device ) { ++ fprintf( stderr, _("Error: device %s handled by fstab\n"), fstab_device ); ++ exit( -1 ); ++ } ++ ++ devarg = device; ++ result = mount_device(); ++ if( result != 0 ) { ++ fprintf( stderr, _("Failed to mount device %s : error %d\n"), device, result ); ++ error_occurred = -1; ++ } else { ++ printf( _("Device %s mounted\n"), device ); + } + } +- /* If user did not choose explicitly for or against utf8 */ +- if( utf8 == -1 ) { +- const char* codeset; +- codeset = nl_langinfo( CODESET ); +- if( codeset && !strcmp( codeset, "UTF-8" ) ) { +- debug( "locale encoding uses UTF-8: will mount FAT with utf8 option" ); +- utf8 = 1; +- } else { +- utf8 = 0; +- } +- } +- +- /* clean stale locks */ +- clean_lock_dir( device ); +- +- if( check_mount_policy( device, mntpt ) ) +- return E_POLICY; +- +- /* check for encrypted device */ +- enum decrypt_status decrypt = luks_decrypt( device, +- decrypted_device, sizeof( decrypted_device ), passphrase, +- force_write == 0 ? 1 : 0 ); +- +- switch (decrypt) { +- case DECRYPT_FAILED: +- fprintf( stderr, _("Error: could not decrypt device (wrong passphrase?)\n") ); +- exit( E_POLICY ); +- case DECRYPT_EXISTS: +- fprintf( stderr, _("Error: mapped device already exists\n") ); +- exit( E_POLICY ); +- case DECRYPT_OK: +- /* We create a luks lockfile _on the decrypted device !_*/ +- if(! luks_create_lockfile(decrypted_device)) +- fprintf(stderr, _("Warning: could not create luks lockfile\n")); +- case DECRYPT_NOTENCRYPTED: +- break; +- default: +- fprintf( stderr, "Internal error: unhandled decrypt_status %i\n", +- (int) decrypt); +- exit( E_INTERNAL ); +- } +- +- /* lock the mount directory */ +- debug( "locking mount point directory\n" ); +- if( lock_dir( mntpt ) < 0) { +- fprintf( stderr, _("Error: could not lock the mount directory. Another pmount is probably running for this mount point.\n")); +- exit( E_LOCKED ); +- } +- debug( "mount point directory locked\n" ); +- +- /* off we go */ +- if( use_fstype ) +- result = do_mount( decrypted_device, mntpt, use_fstype, async, noatime, +- exec, force_write, iocharset, utf8, umask, fmask, dmask, 0 ); +- else +- result = do_mount_auto( decrypted_device, mntpt, async, noatime, exec, +- force_write, iocharset, utf8, umask, fmask, dmask ); +- +- /* unlock the mount point again */ +- debug( "unlocking mount point directory\n" ); +- unlock_dir( mntpt ); +- debug( "mount point directory unlocked\n" ); +- +- if( result ) { +- if( decrypt == DECRYPT_OK ) +- luks_release( decrypted_device, 0 ); +- +- /* mount failed, delete the mount point again */ +- if( remove_pmount_mntpt( mntpt ) ) { +- perror( _("Error: could not delete mount point") ); +- return -1; +- } +- return E_EXECMOUNT; +- } +- +- return 0; +- +- case LOCK: +- if( device_valid( device ) ) +- if( do_lock( device, parse_unsigned( arg2, E_PID ) ) ) +- return E_INTERNAL; +- return 0; +- +- case UNLOCK: +- if( device_valid( device ) ) +- if( do_unlock( device, parse_unsigned( arg2, E_PID ) ) ) +- return E_UNLOCK; +- return 0; ++ closedir( partdir ); ++ return error_occurred; ++ } + } +- +- fprintf( stderr, _("Internal error: mode %i not handled.\n"), (int) mode ); +- return E_INTERNAL; ++ ++ return mount_device(); + } +diff -r 97fe1772e5c4 src/policy.c +--- a/src/policy.c Sun Nov 20 20:46:53 2011 +0100 ++++ b/src/policy.c Mon Nov 21 15:00:50 2011 +0100 +@@ -164,7 +164,9 @@ + exit( -1 ); + } + while( ( partdirent = readdir( partdir ) ) != NULL ) { +- if( partdirent->d_type != DT_DIR ) ++ if( partdirent->d_type != DT_DIR ++ || !strcmp( partdirent->d_name, "." ) ++ || !strcmp( partdirent->d_name, ".." ) ) + continue; + + /* construct /sys/block/<device>/<partition>/dev */ +@@ -549,7 +551,7 @@ + blockdevpath, whitelisted_bus); + } + else +- debug("Device %s does not belong to any whitelisted bus\n"); ++ debug("Device %s does not belong to any whitelisted bus\n", blockdevpath); + } + return removable; + } +diff -r 97fe1772e5c4 src/pumount.c +--- a/src/pumount.c Sun Nov 20 20:46:53 2011 +0100 ++++ b/src/pumount.c Mon Nov 21 15:00:50 2011 +0100 +@@ -19,10 +19,14 @@ + #include <getopt.h> + #include <libintl.h> + #include <locale.h> ++#include <sys/stat.h> ++#include <errno.h> ++#include <dirent.h> + + #include "policy.h" + #include "utils.h" + #include "luks.h" ++#include "conf.h" + #include "config.h" + + /* error codes */ +@@ -46,11 +50,12 @@ + " are met (see pumount(1) for details). The mount point directory is removed\n" + " afterwards.\n\n" + "Options:\n" +- " -l, --lazy : umount lazily, see umount(8)\n" +- " --luks-force : luksClose devices pmount didn't open\n" +- " -d, --debug : enable debug output (very verbose)\n" +- " -h, --help : print help message and exit successfuly\n" +- " --version : print version number and exit successfully\n"), ++ " -l, --lazy : umount lazily, see umount(8)\n" ++ " --luks-force : luksClose devices pmount didn't open\n" ++ " -D : umount all partitions of the device, then stops it for safe removal\n" ++ " -d, --debug : enable debug output (very verbose)\n" ++ " -h, --help : print help message and exit successfuly\n" ++ " -V, --version : print version number and exit successfully\n"), + exename, MEDIADIR ); + } + +@@ -88,9 +93,23 @@ + + /* mount point must be below MEDIADIR */ + if( strncmp( mntpt, mediadir, strlen( mediadir ) ) ) { +- fprintf( stderr, _("Error: mount point %s is not below %s\n"), mntpt, +- MEDIADIR ); +- return -1; ++ /* check CONF_FILE, it might be okay */ ++ char *o_mntpt = NULL; ++ int passed = 0; ++ if( !get_conf_for_device( device, NULL, NULL, NULL, &o_mntpt, NULL ) ) { ++ if( NULL != o_mntpt ) { ++ if( !strcmp( mntpt, o_mntpt ) ) { ++ debug( "mount point allowed from config: %s\n", mntpt ); ++ passed = 1; ++ } ++ free( o_mntpt ); ++ } ++ } ++ if( !passed ) { ++ fprintf( stderr, _("Error: mount point %s is not below %s\n"), mntpt, ++ MEDIADIR ); ++ return -1; ++ } + } + + debug( "policy check passed\n" ); +@@ -98,13 +117,15 @@ + } + + /** +- * Drop all privileges and exec 'umount device'. Does not return on success, if +- * it returns, UMOUNTPROG could not be executed. ++ * Drop all privileges and exec 'umount device'. + * @param lazy 0 for normal umount, 1 for lazy umount ++ * @return 0 on success, E_EXECUMOUNT if UMOUNTPROG could not be executed. + */ +-void ++int + do_umount_fstab( const char* device, int lazy, const char * fstab_mntpt ) + { ++ int status; ++ + /* drop all privileges */ + get_root(); + if( setuid( getuid() ) ) { +@@ -120,10 +141,16 @@ + } + + if( lazy ) +- execl( UMOUNTPROG, UMOUNTPROG, "-l", device, NULL ); ++ status = spawnl( 0, UMOUNTPROG, UMOUNTPROG, "-l", device, NULL ); + else +- execl( UMOUNTPROG, UMOUNTPROG, device, NULL ); +- perror( _("Error: could not execute umount") ); ++ status = spawnl( 0, UMOUNTPROG, UMOUNTPROG, device, NULL ); ++ ++ if( status != 0 ) { ++ perror( _("Error: could not execute umount") ); ++ return E_EXECUMOUNT; ++ } ++ ++ return 0; + } + + /** +@@ -152,6 +179,41 @@ + return 0; + } + ++int ++umount_device( const char* device, size_t devicesize, const char* mntpt, ++ int do_lazy, int full_device ) ++{ ++ const char* fstab_device; ++ char fstab_mntpt[MEDIA_STRING_SIZE]; ++ ++ /* in full device mode, we need to check is the device is handled by fstab */ ++ if( full_device ) { ++ fstab_device = fstab_has_device( "/etc/fstab", device, fstab_mntpt, NULL ); ++ if( fstab_device && device_mounted( device, 1, NULL ) ) { ++ return do_umount_fstab( fstab_device, do_lazy, fstab_mntpt ); ++ } ++ /* in regular mode, we check if we have a dmcrypt device */ ++ } else if( luks_get_mapped_device( device, (char *) device, devicesize ) ) { ++ debug( "Unmounting mapped device %s instead.\n", device ); ++ } ++ ++ /* Now, we accept when devices have gone missing */ ++ if( check_umount_policy( device, 1 ) ) ++ return E_POLICY; ++ ++ /* go for it */ ++ if( do_umount( device, do_lazy ) ) ++ return E_EXECUMOUNT; ++ ++ /* release LUKS device, if appropriate */ ++ luks_release( device, 1 ); ++ ++ /* delete mount point */ ++ remove_pmount_mntpt( mntpt ); ++ ++ return 0; ++} ++ + /** + * Entry point. + * +@@ -165,6 +227,10 @@ + int is_real_path = 0; + int do_lazy = 0; + int luks_force = 0; ++ int full_device = 0; ++ ++ int error_occurred = 0; ++ int result; + + int option; + static struct option long_opts[] = { +@@ -173,6 +239,7 @@ + { "lazy", 0, NULL, 'l'}, + { "yes-I-really-want-lazy-unmount", 0, NULL, 'R'}, + { "luks-force", 0, NULL, 'L'}, ++ { "full-device", 0, NULL, 'D'}, + { "version", 0, NULL, 'V' }, + { NULL, 0, NULL, 0} + }; +@@ -193,7 +260,7 @@ + + /* parse command line options */ + do { +- switch( option = getopt_long( argc, argv, "+hdluV", long_opts, NULL ) ) { ++ switch( option = getopt_long( argc, argv, "+hdluDV", long_opts, NULL ) ) { + case -1: break; /* end of arguments */ + case '?': return E_ARGS; /* unknown argument */ + +@@ -212,6 +279,8 @@ + do_lazy = 1; break; + + case 'L': luks_force = 1; break; ++ ++ case 'D': full_device = 1; break; + + case 'V': puts(VERSION); return 0; + +@@ -251,11 +320,17 @@ + snprintf( device, sizeof( device ), "%s", argv[optind] ); + } + +- /* is the device already handled by fstab? */ +- fstab_device = fstab_has_device( "/etc/fstab", device, fstab_mntpt, NULL ); +- if( fstab_device ) { +- do_umount_fstab( fstab_device, do_lazy, fstab_mntpt ); +- return E_EXECUMOUNT; ++ /* in full_device mode, we'll deal with all partitions anyways */ ++ if( !full_device ) { ++ /* is the device already handled by fstab? */ ++ fstab_device = fstab_has_device( "/etc/fstab", device, fstab_mntpt, NULL ); ++ if( fstab_device ) { ++ if( device_mounted( device, 1, NULL ) ) { ++ return do_umount_fstab( fstab_device, do_lazy, fstab_mntpt ); ++ } else { ++ return 0; ++ } ++ } + } + + /* we cannot really check the real path when unmounting lazily since the +@@ -271,13 +346,18 @@ + } + debug( "trying to prepend '" DEVDIR + "' to device argument, now '%s'\n", device ); +- /* We need to lookup again in fstab: */ +- fstab_device = fstab_has_device( "/etc/fstab", device, +- fstab_mntpt, NULL ); +- if( fstab_device ) { +- do_umount_fstab( fstab_device, do_lazy, fstab_mntpt ); +- return E_EXECUMOUNT; +- } ++ if( !full_device ) { ++ /* We need to lookup again in fstab: */ ++ fstab_device = fstab_has_device( "/etc/fstab", device, ++ fstab_mntpt, NULL ); ++ if( fstab_device ) { ++ if( device_mounted( device, 1, NULL ) ) { ++ return do_umount_fstab( fstab_device, do_lazy, fstab_mntpt ); ++ } else { ++ return 0; ++ } ++ } ++ } + } + } + +@@ -286,24 +366,151 @@ + fprintf( stderr, _("Error: invalid device %s (must be in /dev/)\n"), device ); + return E_DEVICE; + } ++ ++ /* we need to get the full device name (e.g. /dev/sde), get list of all its ++ partitions, and try to unmount them all... */ ++ if( full_device ) { ++ char devdirname[MEDIA_STRING_SIZE]; ++ if( !find_sysfs_device( device, devdirname, MEDIA_STRING_SIZE) ) { ++ fprintf( stderr, _("Warning: unable to find device path for %s, " ++ "full-device mode disabled\n"), ++ device ); ++ full_device = 0; ++ } else { ++ debug( "device path for %s is %s\n", device, devdirname ); ++ ++ DIR *partdir; ++ struct dirent *partdirent; ++ char partdirname[MEDIA_STRING_SIZE]; ++ struct stat stat_info; ++ ++ partdir = opendir( devdirname ); ++ if( !partdir ) { ++ perror( _("Error: could not open <sysfs dir>/block/<device>/") ); ++ exit( -1 ); ++ } ++ while( ( partdirent = readdir( partdir ) ) != NULL ) { ++ if( partdirent->d_type != DT_DIR ++ || !strcmp( partdirent->d_name, "." ) ++ || !strcmp( partdirent->d_name, ".." ) ) ++ continue; ++ ++ /* construct /sys/block/<device>/<partition>/dev */ ++ snprintf( partdirname, sizeof( partdirname ), "%s/%s/%s", ++ devdirname, partdirent->d_name, "dev" ); + +- /* check if we have a dmcrypt device */ +- if( luks_get_mapped_device( device, device, sizeof( device ) ) ) +- debug( "Unmounting mapped device %s instead.\n", device ); ++ /* make sure it is a device, i.e has a file dev */ ++ if( 0 != stat( partdirname, &stat_info ) ) { ++ /* ENOENT (does not exist) is "okay" we just ignore this one */ ++ if( ENOENT != errno ) { ++ perror( _("Error: could not stat <sysfs dir>/block/<device>/<part>/dev") ); ++ exit( -1 ); ++ } ++ continue; ++ } ++ /* must be a file */ ++ if( !S_ISREG( stat_info.st_mode ) ) { ++ continue; ++ } + +- /* Now, we accept when devices have gone missing */ +- if( check_umount_policy( device, 1 ) ) +- return E_POLICY; ++ /* construct /dev/<partition> */ ++ snprintf( device, sizeof( device ), "%s%s", DEVDIR, partdirent->d_name ); ++ debug( "processing found partition: %s\n", device ); ++ ++ /* check if we have a dmcrypt device */ ++ if( luks_get_mapped_device( device, device, sizeof( device ) ) ) ++ debug( "Using mapped device %s instead.\n", device ); + +- /* go for it */ +- if( do_umount( device, do_lazy ) ) +- return E_EXECUMOUNT; ++ if( device_mounted( device, 1, mntpt ) ) { ++ debug( "device %s mounted, unmounting\n", device ); ++ result = umount_device( device, sizeof( device ), mntpt, ++ do_lazy, full_device ); ++ if( result != 0 ) { ++ fprintf( stderr, _("Failed to umount device %s : error %d\n"), ++ device, result ); ++ error_occurred = -1; ++ } else { ++ printf( _("Device %s umounted\n"), device ); ++ } ++ } ++ } ++ closedir( partdir ); ++ ++ /* no errors: let's stop the device completely, for safe removal */ ++ if ( !error_occurred ) { ++ char *c; ++ FILE *f; + +- /* release LUKS device, if appropriate */ +- luks_release( device, 1 ); ++ /* flush buffers */ ++ sync(); ++ ++ /* resolve devdirname (<sysfs>/block/<device> to something like: ++ * /sys/devices/pci0000:00/0000:00:06.0/usb1/1-2/1-2:1.0/host5/target5:0:0/5:0:0:0/block/sdd */ ++ if( !realpath( devdirname, device ) ) { ++ debug( "unable to resolve %s\n", device ); ++ goto err_stop; ++ } ++ debug( "device %s resolved to %s\n", devdirname, device ); ++ ++ /* now extract the part we want, up to the grand-parent of the host ++ e.g: /sys/devices/pci0000:00/0000:00:06.0/usb1/1-2 */ ++ while( c = strrchr( device, '/' ) ) { ++ /* end the string there, to move back */ ++ *c = 0; ++ /* found the host part? */ ++ if( !strncmp( c + 1, "host", 4 ) ) { ++ break; ++ } ++ } ++ if( c == NULL ) { ++ debug( "unable to find host for %s\n", device ); ++ goto err_stop; ++ } ++ /* we need to move back one more time */ ++ if( NULL == ( c = strrchr( device, '/' ) ) ) { ++ debug( "cannot move back one last time in %s\n", device ); ++ goto err_stop; ++ } ++ /* end the string there */ ++ *c = 0; ++ debug( "full name is %s\n", device ); ++ /* now we need the last component, aka the bus id */ ++ if( NULL == ( c = strrchr( device, '/' ) ) ) { ++ debug( "cannot extract last component of %s\n", device ); ++ goto err_stop; ++ } ++ /* move up, so this points to the name only, e.g. 1-2 */ ++ ++c; ++ ++ /* unbind driver: write the bus id to <device>/driver/unbind */ ++ snprintf( path, sizeof( path ), "%s/driver/unbind", device ); ++ if ( root_write_to_file( path, c ) ) { ++ goto err_stop; ++ } ++ ++ /* suspend device. step 1: write "0" to <device>/power/autosuspend */ ++ snprintf( path, sizeof( path ), "%s/power/autosuspend", device ); ++ if ( root_write_to_file( path, "0" ) ) { ++ goto err_stop; ++ } ++ /* step 2: write "auto" to <device>/power/control */ ++ snprintf( path, sizeof( path ), "%s/power/control", device ); ++ if ( root_write_to_file( path, "auto" ) ) { ++ goto err_stop; ++ } ++ ++ c = strrchr( devdirname, '/' ); ++ printf( _("Device %s%s stopped, you should now be able to safely unplug it\n"), ++ DEVDIR, c + 1); ++ } ++ ++ return error_occurred; + +- /* delete mount point */ +- remove_pmount_mntpt( mntpt ); ++err_stop: ++ fputs( _("Error: Unable to stop device\n"), stderr ); ++ return -1; ++ } ++ } + +- return 0; ++ return umount_device( device, sizeof( device ), mntpt, do_lazy, full_device ); + } +diff -r 97fe1772e5c4 src/utils.c +--- a/src/utils.c Sun Nov 20 20:46:53 2011 +0100 ++++ b/src/utils.c Mon Nov 21 15:00:50 2011 +0100 +@@ -439,3 +439,27 @@ + drop_root(); + } + ++int ++root_write_to_file( const char* path, const char* data ) ++{ ++ FILE *f; ++ size_t expected, actual; ++ ++ get_root(); ++ f = fopen( path, "w" ); ++ drop_root(); ++ if( !f ) { ++ debug( "could not open %s\n", path ); ++ return -1; ++ } ++ expected = sizeof( char ) * strlen( data ); ++ actual = fwrite( data, sizeof( char ), strlen( data ), f ); ++ if( actual != expected ) { ++ fclose( f ); ++ debug( "error when writing to %s; expected %d bytes, only %d written\n", ++ path, expected, actual ); ++ return -1; ++ } ++ fclose( f ); ++ return 0; ++} +diff -r 97fe1772e5c4 src/utils.h +--- a/src/utils.h Sun Nov 20 20:46:53 2011 +0100 ++++ b/src/utils.h Mon Nov 21 15:00:50 2011 +0100 +@@ -90,6 +90,16 @@ + int read_number_colon_number( const char* file, unsigned char* first, unsigned char* second ); + + /** ++ * Writes given data to the specified, opening it as root ++ * (this is used to unbind driver, etc) ++ * @param path path/file to write to ++ * @param data data to write ++ * @return 0 on success, else -1 ++ */ ++int ++root_write_to_file( const char* path, const char* data ); ++ ++/** + * Parse s as nonnegative number. Exits the program immediately if s cannot be + * parsed as a number. + * @param s string to parse as a number |