add remove safely
This commit is contained in:
parent
b28f39ed4a
commit
2817e4e774
1
AUTHORS
1
AUTHORS
|
@ -3,6 +3,7 @@ IgnorantGuru <ignorantguru@gmx.com> http://igurublog.wordpress.com/
|
||||||
Source code taken from other projects:
|
Source code taken from other projects:
|
||||||
* spacefm (udev support)
|
* spacefm (udev support)
|
||||||
* util-linux (secure canonicalize)
|
* util-linux (secure canonicalize)
|
||||||
|
* pmount-jjk (remove device code - modified)
|
||||||
* udisks 1.0.4 (device info functions - modified)
|
* udisks 1.0.4 (device info functions - modified)
|
||||||
* pmount 0.99 (physical tty test function - modified)
|
* pmount 0.99 (physical tty test function - modified)
|
||||||
|
|
||||||
|
|
348
src/udevil.c
348
src/udevil.c
|
@ -75,7 +75,8 @@ enum {
|
||||||
CMD_UNMOUNT,
|
CMD_UNMOUNT,
|
||||||
CMD_MONITOR,
|
CMD_MONITOR,
|
||||||
CMD_INFO,
|
CMD_INFO,
|
||||||
CMD_CLEAN
|
CMD_CLEAN,
|
||||||
|
CMD_REMOVE
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
|
@ -1732,6 +1733,7 @@ static gboolean path_is_mounted_block( const char* path, char** device_file )
|
||||||
udev_device_unref( udevice );
|
udev_device_unref( udevice );
|
||||||
}
|
}
|
||||||
udev_unref( udev );
|
udev_unref( udev );
|
||||||
|
udev = NULL;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
*device_file = NULL;
|
*device_file = NULL;
|
||||||
|
@ -1739,6 +1741,33 @@ static gboolean path_is_mounted_block( const char* path, char** device_file )
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int root_write_to_file( const char* path, const char* data )
|
||||||
|
{
|
||||||
|
FILE *f;
|
||||||
|
size_t expected, actual;
|
||||||
|
|
||||||
|
if ( !( data && data[0] != '\0' ) )
|
||||||
|
return 1;
|
||||||
|
restore_privileges();
|
||||||
|
f = fopen( path, "w" );
|
||||||
|
drop_privileges( 0 );
|
||||||
|
if ( !f )
|
||||||
|
{
|
||||||
|
wlog( "udevil: error: could not open %s\n", path, 2 );
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
expected = sizeof( char ) * strlen( data );
|
||||||
|
actual = fwrite( data, sizeof( char ), strlen( data ), f );
|
||||||
|
if( actual != expected )
|
||||||
|
{
|
||||||
|
fclose( f );
|
||||||
|
wlog( "udevil: error: error writing to %s\n", path, 2 );
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
fclose( f );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int exec_program( const char* var, const char* msg, gboolean show_error,
|
static int exec_program( const char* var, const char* msg, gboolean show_error,
|
||||||
gboolean as_root )
|
gboolean as_root )
|
||||||
{
|
{
|
||||||
|
@ -2832,6 +2861,7 @@ _get_type:
|
||||||
wlog( "udevil: error: no udev device for device %s\n",
|
wlog( "udevil: error: no udev device for device %s\n",
|
||||||
data->device_file, 2 );
|
data->device_file, 2 );
|
||||||
udev_unref( udev );
|
udev_unref( udev );
|
||||||
|
udev = NULL;
|
||||||
ret = 1;
|
ret = 1;
|
||||||
goto _finish;
|
goto _finish;
|
||||||
}
|
}
|
||||||
|
@ -2845,6 +2875,7 @@ _get_type:
|
||||||
}
|
}
|
||||||
udev_device_unref( udevice );
|
udev_device_unref( udevice );
|
||||||
udev_unref( udev );
|
udev_unref( udev );
|
||||||
|
udev = NULL;
|
||||||
if ( ret != 0 )
|
if ( ret != 0 )
|
||||||
goto _finish;
|
goto _finish;
|
||||||
|
|
||||||
|
@ -3797,6 +3828,297 @@ _finish:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int command_remove( CommandData* data )
|
||||||
|
{
|
||||||
|
struct stat statbuf;
|
||||||
|
struct udev_device *udevice;
|
||||||
|
const char* device_file = data->device_file;
|
||||||
|
char* str;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
// got root?
|
||||||
|
if ( orig_euid != 0 )
|
||||||
|
{
|
||||||
|
wlog( "udevil: error: udevil was not run suid root\n", NULL, 2 );
|
||||||
|
wlog( " To correct this problem: sudo chmod +s /usr/bin/udevil\n", NULL, 2 );
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !device_file || ( device_file && device_file[0] == '\0' ) )
|
||||||
|
{
|
||||||
|
wlog( "udevil: error: remove requires DEVICE argument\n", NULL, 2 );
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( stat( device_file, &statbuf ) != 0 )
|
||||||
|
{
|
||||||
|
str = g_strdup_printf( "udevil: error: cannot stat %s: %s\n",
|
||||||
|
device_file, g_strerror( errno ) );
|
||||||
|
wlog( str, NULL, 2 );
|
||||||
|
g_free( str );
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if ( statbuf.st_rdev == 0 || !S_ISBLK( statbuf.st_mode ) )
|
||||||
|
{
|
||||||
|
wlog( "udevil: error: %s is not a block device\n", device_file, 2 );
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
udev = udev_new();
|
||||||
|
if ( udev == NULL )
|
||||||
|
{
|
||||||
|
wlog( "udevil: error: error initializing libudev\n", NULL, 2 );
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
udevice = udev_device_new_from_devnum( udev, 'b', statbuf.st_rdev );
|
||||||
|
if ( udevice == NULL )
|
||||||
|
{
|
||||||
|
wlog( "udevil: error: no udev device for device %s\n", device_file, 2 );
|
||||||
|
udev_unref( udev );
|
||||||
|
udev = NULL;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
device_t *device = device_alloc( udevice );
|
||||||
|
if ( !device_get_info( device, devmounts ) )
|
||||||
|
{
|
||||||
|
wlog( "udevil: error: unable to get device info\n", NULL, 2 );
|
||||||
|
udev_device_unref( udevice );
|
||||||
|
udev_unref( udev );
|
||||||
|
udev = NULL;
|
||||||
|
device_free( device );
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// is device internal ?
|
||||||
|
gboolean skip_driver = FALSE;
|
||||||
|
if ( device->device_is_system_internal )
|
||||||
|
{
|
||||||
|
wlog( "udevil: warning: device %s is an internal device - not unbinding driver\n",
|
||||||
|
data->device_file, 1 );
|
||||||
|
skip_driver = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !skip_driver
|
||||||
|
&& strcmp( device->drive_connection_interface, "ata_serial_esata" )
|
||||||
|
&& strcmp( device->drive_connection_interface, "sdio" )
|
||||||
|
&& strcmp( device->drive_connection_interface, "usb" )
|
||||||
|
&& strcmp( device->drive_connection_interface, "firewire" ) )
|
||||||
|
{
|
||||||
|
wlog( "udevil: warning: interface is not usb, firewire, sdio, esata - not unbinding driver\n",
|
||||||
|
data->device_file, 1 );
|
||||||
|
skip_driver = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// allowed
|
||||||
|
if ( !validate_in_list( "allowed_devices", device->id_type, data->device_file ) )
|
||||||
|
{
|
||||||
|
wlog( "udevil: denied: device %s is not an allowed device\n",
|
||||||
|
data->device_file, 2 );
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
if ( validate_in_list( "forbidden_devices", device->id_type, data->device_file ) )
|
||||||
|
{
|
||||||
|
wlog( "udevil: denied: device %s is a forbidden device\n",
|
||||||
|
data->device_file, 2 );
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// unmount all partitions on this device - contains code from pmount-jjk
|
||||||
|
GDir *partdir;
|
||||||
|
const char* filename;
|
||||||
|
char* partdirname;
|
||||||
|
char* path;
|
||||||
|
char* host_path;
|
||||||
|
CommandData* data2;
|
||||||
|
int result, n;
|
||||||
|
|
||||||
|
// get host device
|
||||||
|
//printf("native_path=%s\n", device->native_path );
|
||||||
|
host_path = g_strdup( udev_device_get_property_value( udevice,
|
||||||
|
"UDISKS_PARTITION_SLAVE") );
|
||||||
|
if ( !host_path )
|
||||||
|
{
|
||||||
|
// not all partitions have UDISKS_PARTITION_* - or this is full device?
|
||||||
|
host_path = g_strdup( device->native_path );
|
||||||
|
path = g_build_filename( host_path, "partition", NULL );
|
||||||
|
if ( g_file_test( path, G_FILE_TEST_EXISTS ) )
|
||||||
|
{
|
||||||
|
// is a partition so determine host
|
||||||
|
for ( n = strlen( host_path ) - 1; n >= 0 && host_path[n] != '/'; n-- )
|
||||||
|
host_path[n] = '\0';
|
||||||
|
host_path[n] = '\0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// looks like a full device, unmount device if mounted
|
||||||
|
if ( device_is_mounted_mtab( device->devnode, NULL, NULL ) )
|
||||||
|
{
|
||||||
|
wlog( "udevil: unmount %s\n", device->devnode, 1 );
|
||||||
|
data2 = g_slice_new0( CommandData );
|
||||||
|
data2->cmd_type = CMD_UNMOUNT;
|
||||||
|
data2->device_file = g_strdup( device->devnode );
|
||||||
|
data2->point = NULL;
|
||||||
|
data2->fstype = NULL;
|
||||||
|
data2->options = NULL;
|
||||||
|
data2->label = NULL;
|
||||||
|
data2->uuid = NULL;
|
||||||
|
data2->force = data->force;
|
||||||
|
data2->lazy = data->lazy;
|
||||||
|
result = command_mount( data2 );
|
||||||
|
free_command_data( data2 );
|
||||||
|
|
||||||
|
if( result != 0 )
|
||||||
|
{
|
||||||
|
g_free( path );
|
||||||
|
g_free( host_path );
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_free( path );
|
||||||
|
}
|
||||||
|
udev_device_unref( udevice );
|
||||||
|
udev_unref( udev );
|
||||||
|
udev = NULL;
|
||||||
|
device_free( device );
|
||||||
|
|
||||||
|
// read partitions in host_path
|
||||||
|
//printf("host_path=%s\n", host_path );
|
||||||
|
partdir = g_dir_open( host_path, 0, NULL );
|
||||||
|
if( !partdir )
|
||||||
|
{
|
||||||
|
wlog( "udevil: error: unable to access dir %s\n", host_path, 2 );
|
||||||
|
g_free( host_path );
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
while( ( filename = g_dir_read_name( partdir ) ) != NULL )
|
||||||
|
{
|
||||||
|
if ( !strcmp( filename, "." ) || !strcmp( filename, ".." ) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* construct /sys/block/<device>/<partition>/dev */
|
||||||
|
partdirname = g_build_filename( host_path, filename, "dev", NULL );
|
||||||
|
|
||||||
|
/* make sure it is a device, i.e has a file dev */
|
||||||
|
if( 0 != stat( partdirname, &statbuf ) )
|
||||||
|
{
|
||||||
|
g_free( partdirname );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
g_free( partdirname );
|
||||||
|
/* must be a file */
|
||||||
|
if( !S_ISREG( statbuf.st_mode ) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* construct /dev/<partition> */
|
||||||
|
path = g_strdup_printf( "/dev/%s", filename );
|
||||||
|
wlog( "udevil: examining partition %s\n", path, 0 );
|
||||||
|
|
||||||
|
while( device_is_mounted_mtab( path, NULL, NULL ) )
|
||||||
|
{
|
||||||
|
// unmount partition
|
||||||
|
wlog( "udevil: unmount partition %s\n", path, 1 );
|
||||||
|
data2 = g_slice_new0( CommandData );
|
||||||
|
data2->cmd_type = CMD_UNMOUNT;
|
||||||
|
data2->device_file = g_strdup( path );
|
||||||
|
data2->point = NULL;
|
||||||
|
data2->fstype = NULL;
|
||||||
|
data2->options = NULL;
|
||||||
|
data2->label = NULL;
|
||||||
|
data2->uuid = NULL;
|
||||||
|
data2->force = data->force;
|
||||||
|
data2->lazy = data->lazy;
|
||||||
|
result = command_mount( data2 );
|
||||||
|
free_command_data( data2 );
|
||||||
|
|
||||||
|
if( result != 0 )
|
||||||
|
{
|
||||||
|
g_free( path );
|
||||||
|
g_free( host_path );
|
||||||
|
g_dir_close( partdir );
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_free( path );
|
||||||
|
}
|
||||||
|
g_dir_close( partdir );
|
||||||
|
|
||||||
|
// flush buffers
|
||||||
|
sync();
|
||||||
|
|
||||||
|
if ( skip_driver )
|
||||||
|
{
|
||||||
|
g_free( host_path );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop device - contains code from pmount-jjk
|
||||||
|
char *c;
|
||||||
|
FILE *f;
|
||||||
|
path = NULL;
|
||||||
|
|
||||||
|
// 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( host_path, '/' ) )
|
||||||
|
{
|
||||||
|
// end the string there, to move back
|
||||||
|
*c = 0;
|
||||||
|
// found the host part?
|
||||||
|
if ( !strncmp( c + 1, "host", 4 ) )
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ( c == NULL )
|
||||||
|
{
|
||||||
|
wlog( "udevil: error: unable to find host for %s\n", host_path, 2 );
|
||||||
|
goto _remove_error;
|
||||||
|
}
|
||||||
|
// we need to move back one more time
|
||||||
|
if ( NULL == ( c = strrchr( host_path, '/' ) ) )
|
||||||
|
{
|
||||||
|
wlog( "udevil: error: unable to find host for %s\n", host_path, 2 );
|
||||||
|
goto _remove_error;
|
||||||
|
}
|
||||||
|
// end the string there
|
||||||
|
*c = 0;
|
||||||
|
|
||||||
|
// now we need the last component, aka the bus id
|
||||||
|
if ( NULL == ( c = strrchr( host_path, '/' ) ) )
|
||||||
|
{
|
||||||
|
wlog( "udevil: error: unable to find last component for %s\n", host_path, 2 );
|
||||||
|
goto _remove_error;
|
||||||
|
}
|
||||||
|
// move up, so this points to the name only, e.g. 1-2
|
||||||
|
++c;
|
||||||
|
|
||||||
|
// unbind driver: write the bus id to <device>/driver/unbind
|
||||||
|
path = g_build_filename( host_path, "driver", "unbind", NULL );
|
||||||
|
if ( root_write_to_file( path, c ) )
|
||||||
|
goto _remove_error;
|
||||||
|
g_free( path );
|
||||||
|
|
||||||
|
// suspend device. step 1: write "0" to <device>/power/autosuspend
|
||||||
|
path = g_build_filename( host_path, "power", "autosuspend", NULL );
|
||||||
|
if ( g_file_test( path, G_FILE_TEST_EXISTS ) && root_write_to_file( path, "0" ) )
|
||||||
|
goto _remove_error;
|
||||||
|
g_free( path );
|
||||||
|
|
||||||
|
// step 2: write "auto" to <device>/power/control
|
||||||
|
path = g_build_filename( host_path, "power", "control", NULL );
|
||||||
|
if ( g_file_test( path, G_FILE_TEST_EXISTS ) && root_write_to_file( path, "auto" ) )
|
||||||
|
goto _remove_error;
|
||||||
|
g_free( path );
|
||||||
|
|
||||||
|
wlog( "Stopped device %s\n", host_path, -1 );
|
||||||
|
g_free( host_path );
|
||||||
|
return 0;
|
||||||
|
_remove_error:
|
||||||
|
g_free( path );
|
||||||
|
g_free( host_path );
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static int command_clean()
|
static int command_clean()
|
||||||
{
|
{
|
||||||
char* list = NULL;
|
char* list = NULL;
|
||||||
|
@ -3914,6 +4236,7 @@ static int command_info( CommandData* data )
|
||||||
{
|
{
|
||||||
wlog( "udevil: error: no udev device for device %s\n", device_file, 2 );
|
wlog( "udevil: error: no udev device for device %s\n", device_file, 2 );
|
||||||
udev_unref( udev );
|
udev_unref( udev );
|
||||||
|
udev = NULL;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3934,6 +4257,7 @@ static int command_info( CommandData* data )
|
||||||
device_free( device );
|
device_free( device );
|
||||||
udev_device_unref( udevice );
|
udev_device_unref( udevice );
|
||||||
udev_unref( udev );
|
udev_unref( udev );
|
||||||
|
udev = NULL;
|
||||||
fflush( stdout );
|
fflush( stdout );
|
||||||
fflush( stderr );
|
fflush( stderr );
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -4129,6 +4453,14 @@ static void show_help()
|
||||||
printf( " udevil umount /media/disk\n" );
|
printf( " udevil umount /media/disk\n" );
|
||||||
printf( " udevil umount -l /media/disk\n" );
|
printf( " udevil umount -l /media/disk\n" );
|
||||||
printf( " udevil umount /tmp/example.iso\n" );
|
printf( " udevil umount /tmp/example.iso\n" );
|
||||||
|
printf( "REMOVE - Unmount all partitions on host of DEVICE and prepare for safe\n" );
|
||||||
|
printf( " removal (sync, stop, unbind driver, and power off):\n" );
|
||||||
|
printf( " udevil remove|--remove|--detach [OPTIONS] [-b|--block-device] DEVICE\n" );
|
||||||
|
printf( " OPTIONS:\n" );
|
||||||
|
printf( " -l lazy unmount (see man umount)\n" );
|
||||||
|
printf( " -f force unmount (see man umount)\n" );
|
||||||
|
printf( " --no-user-interaction ignored (for compatibility)\n" );
|
||||||
|
printf( " EXAMPLE: udevil remove /dev/sdd\n" );
|
||||||
printf( "INFO - Show information about DEVICE emulating udisks v1 output:\n" );
|
printf( "INFO - Show information about DEVICE emulating udisks v1 output:\n" );
|
||||||
printf( " udevil info|--show-info|--info [-b|--block-device] DEVICE\n" );
|
printf( " udevil info|--show-info|--info [-b|--block-device] DEVICE\n" );
|
||||||
printf( " EXAMPLE: udevil info /dev/sdd1\n" );
|
printf( " EXAMPLE: udevil info /dev/sdd1\n" );
|
||||||
|
@ -4327,6 +4659,16 @@ printf("\n-----------------------\n");
|
||||||
ac += next_inc;
|
ac += next_inc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if ( !strcmp( arg, "remove" ) || !strcmp( arg, "--remove" )
|
||||||
|
|| !strcmp( arg, "--detach" ) )
|
||||||
|
{
|
||||||
|
data->cmd_type = CMD_REMOVE;
|
||||||
|
if ( arg_next )
|
||||||
|
{
|
||||||
|
data->device_file = g_strdup( arg_next );
|
||||||
|
ac += next_inc;
|
||||||
|
}
|
||||||
|
}
|
||||||
else if ( !strcmp( arg, "--verbose" ) )
|
else if ( !strcmp( arg, "--verbose" ) )
|
||||||
verbose = 0;
|
verbose = 0;
|
||||||
else if ( !strcmp( arg, "--quiet" ) )
|
else if ( !strcmp( arg, "--quiet" ) )
|
||||||
|
@ -4414,6 +4756,7 @@ printf("\n-----------------------\n");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CMD_UNMOUNT:
|
case CMD_UNMOUNT:
|
||||||
|
case CMD_REMOVE:
|
||||||
if ( !strcmp( arg, "-b" ) || !strcmp( arg, "--block-device" ) )
|
if ( !strcmp( arg, "-b" ) || !strcmp( arg, "--block-device" ) )
|
||||||
{
|
{
|
||||||
if ( !arg_next )
|
if ( !arg_next )
|
||||||
|
@ -4547,6 +4890,9 @@ printf("\n-----------------------\n");
|
||||||
drop_privileges( 1 );
|
drop_privileges( 1 );
|
||||||
ret = command_info( data );
|
ret = command_info( data );
|
||||||
break;
|
break;
|
||||||
|
case CMD_REMOVE:
|
||||||
|
ret = command_remove( data );
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
dump_log();
|
dump_log();
|
||||||
drop_privileges( 1 );
|
drop_privileges( 1 );
|
||||||
|
|
Loading…
Reference in New Issue
Block a user