2001-11-21 15:47:42 +03:00
/*
* Copyright ( C ) 2001 Sistina Software ( UK ) Limited .
*
* This file is released under the LGPL .
*/
# include "libdevmapper.h"
# include <stdio.h>
# include <stdlib.h>
# include <stdarg.h>
# include <string.h>
# include <unistd.h>
# include <fcntl.h>
# include <sys/stat.h>
# include <errno.h>
2001-11-21 18:14:35 +03:00
# include <linux/kdev_t.h>
2001-11-21 15:47:42 +03:00
2002-06-19 17:07:05 +04:00
# include <linux/limits.h>
2001-12-05 19:41:52 +03:00
# include <sys/ioctl.h>
# include <linux/dm-ioctl.h>
2001-11-21 15:47:42 +03:00
2001-12-05 19:41:52 +03:00
# include "libdm-targets.h"
# include "libdm-common.h"
2001-11-21 15:47:42 +03:00
2001-12-05 19:41:52 +03:00
# define ALIGNMENT sizeof(int)
2001-11-21 15:47:42 +03:00
2002-06-19 17:07:05 +04:00
/*
* Ensure build compatibility . The hard - coded version here ( major , minor )
* is the highest present in the _cmd_data array below .
*/
# if DM_VERSION_MAJOR != 1 || DM_VERSION_MINOR < 0
# error The version of dm-ioctl.h included is incompatible.
# endif
static struct {
char * name ;
int cmd ;
int version [ 3 ] ;
} _cmd_data [ ] = {
{ " create " , DM_DEV_CREATE , { 1 , 0 , 0 } } ,
{ " reload " , DM_DEV_RELOAD , { 1 , 0 , 0 } } ,
{ " remove " , DM_DEV_REMOVE , { 1 , 0 , 0 } } ,
{ " remove_all " , DM_REMOVE_ALL , { 1 , 0 , 0 } } ,
{ " suspend " , DM_DEV_SUSPEND , { 1 , 0 , 0 } } ,
{ " resume " , DM_DEV_SUSPEND , { 1 , 0 , 0 } } ,
{ " info " , DM_DEV_STATUS , { 1 , 0 , 0 } } ,
{ " deps " , DM_DEV_DEPS , { 1 , 0 , 0 } } ,
{ " rename " , DM_DEV_RENAME , { 1 , 0 , 0 } } ,
{ " version " , DM_VERSION , { 1 , 0 , 0 } } ,
{ " status " , DM_TARGET_STATUS , { 1 , 0 , 0 } } ,
{ " table " , DM_TARGET_STATUS , { 1 , 0 , 0 } } ,
{ " waitevent " , DM_TARGET_WAIT , { 1 , 0 , 0 } } ,
2002-03-20 17:34:15 +03:00
} ;
2002-05-03 15:55:58 +04:00
static void * _align ( void * ptr , unsigned int a )
{
register unsigned long align = - - a ;
return ( void * ) ( ( ( unsigned long ) ptr + align ) & ~ align ) ;
}
2001-11-21 15:47:42 +03:00
void dm_task_destroy ( struct dm_task * dmt )
{
struct target * t , * n ;
for ( t = dmt - > head ; t ; t = n ) {
n = t - > next ;
2002-04-15 17:24:14 +04:00
free ( t - > params ) ;
free ( t - > type ) ;
2001-11-21 15:47:42 +03:00
free ( t ) ;
}
2002-03-14 02:19:20 +03:00
if ( dmt - > dev_name )
free ( dmt - > dev_name ) ;
2002-01-11 02:29:16 +03:00
if ( dmt - > newname )
free ( dmt - > newname ) ;
2001-11-21 15:47:42 +03:00
if ( dmt - > dmi )
free ( dmt - > dmi ) ;
2002-03-12 01:44:36 +03:00
if ( dmt - > uuid )
free ( dmt - > uuid ) ;
2001-11-21 15:47:42 +03:00
free ( dmt ) ;
}
2002-03-07 23:56:10 +03:00
int dm_task_get_driver_version ( struct dm_task * dmt , char * version , size_t size )
2002-01-15 18:21:57 +03:00
{
2002-06-17 19:50:17 +04:00
unsigned int * v ;
2002-01-15 18:21:57 +03:00
if ( ! dmt - > dmi )
return 0 ;
2002-06-17 19:50:17 +04:00
v = dmt - > dmi - > version ;
snprintf ( version , size , " %u.%u.%u " , v [ 0 ] , v [ 1 ] , v [ 2 ] ) ;
2002-01-15 18:21:57 +03:00
return 1 ;
}
2002-05-10 19:25:38 +04:00
void * dm_get_next_target ( struct dm_task * dmt , void * next ,
uint64_t * start , uint64_t * length ,
char * * target_type , char * * params )
2002-05-03 15:55:58 +04:00
{
2002-05-10 19:25:38 +04:00
struct target * t = ( struct target * ) next ;
2002-05-03 15:55:58 +04:00
2002-05-10 19:25:38 +04:00
if ( ! t )
t = dmt - > head ;
2002-05-03 15:55:58 +04:00
2002-05-10 19:25:38 +04:00
if ( ! t )
return NULL ;
2002-05-03 15:55:58 +04:00
* start = t - > start ;
* length = t - > length ;
* target_type = t - > type ;
* params = t - > params ;
2002-05-10 19:25:38 +04:00
2002-05-03 15:55:58 +04:00
return t - > next ;
}
/* Unmarshall the target info returned from a status call */
2002-05-10 19:25:38 +04:00
static int _unmarshal_status ( struct dm_task * dmt , struct dm_ioctl * dmi )
2002-05-03 15:55:58 +04:00
{
2002-05-10 19:25:38 +04:00
char * outbuf = ( char * ) dmi + sizeof ( struct dm_ioctl ) ;
2002-05-03 15:55:58 +04:00
char * outptr = outbuf ;
int i ;
2002-05-10 19:25:38 +04:00
for ( i = 0 ; i < dmi - > target_count ; i + + ) {
struct dm_target_spec * spec = ( struct dm_target_spec * ) outptr ;
2002-05-03 15:55:58 +04:00
2002-05-10 19:25:38 +04:00
if ( ! dm_task_add_target ( dmt , spec - > sector_start , spec - > length ,
spec - > target_type ,
outptr + sizeof ( * spec ) ) )
return 0 ;
outptr + = sizeof ( struct dm_target_spec ) ;
outptr + = strlen ( outptr ) + 1 ;
_align ( outptr , ALIGNMENT ) ;
2002-05-03 15:55:58 +04:00
}
2002-05-10 19:25:38 +04:00
2002-05-03 15:55:58 +04:00
return 1 ;
}
2001-11-21 15:47:42 +03:00
int dm_task_get_info ( struct dm_task * dmt , struct dm_info * info )
{
if ( ! dmt - > dmi )
return 0 ;
2002-01-15 02:07:32 +03:00
memset ( info , 0 , sizeof ( * info ) ) ;
2002-01-15 18:21:57 +03:00
info - > exists = dmt - > dmi - > flags & DM_EXISTS_FLAG ? 1 : 0 ;
2002-01-15 02:07:32 +03:00
if ( ! info - > exists )
return 1 ;
2002-01-15 18:21:57 +03:00
info - > suspended = dmt - > dmi - > flags & DM_SUSPEND_FLAG ? 1 : 0 ;
info - > read_only = dmt - > dmi - > flags & DM_READONLY_FLAG ? 1 : 0 ;
2001-11-21 15:47:42 +03:00
info - > target_count = dmt - > dmi - > target_count ;
2002-01-15 02:07:32 +03:00
info - > open_count = dmt - > dmi - > open_count ;
info - > major = MAJOR ( dmt - > dmi - > dev ) ;
info - > minor = MINOR ( dmt - > dmi - > dev ) ;
2001-11-21 15:47:42 +03:00
return 1 ;
}
2002-03-13 19:19:17 +03:00
const char * dm_task_get_uuid ( struct dm_task * dmt )
{
return ( dmt - > dmi - > uuid ) ;
}
2002-03-06 17:38:25 +03:00
struct dm_deps * dm_task_get_deps ( struct dm_task * dmt )
{
return ( struct dm_deps * ) ( ( ( void * ) dmt - > dmi ) + dmt - > dmi - > data_start ) ;
}
2002-01-03 13:39:21 +03:00
int dm_task_set_ro ( struct dm_task * dmt )
{
dmt - > read_only = 1 ;
return 1 ;
}
2002-01-11 02:29:16 +03:00
int dm_task_set_newname ( struct dm_task * dmt , const char * newname )
{
if ( ! ( dmt - > newname = strdup ( newname ) ) ) {
2002-01-18 22:37:26 +03:00
log_error ( " dm_task_set_newname: strdup(%s) failed " , newname ) ;
2002-01-11 02:29:16 +03:00
return 0 ;
}
return 1 ;
}
2002-05-10 19:25:38 +04:00
struct target * create_target ( uint64_t start , uint64_t len , const char * type ,
const char * params )
2001-11-21 15:47:42 +03:00
{
struct target * t = malloc ( sizeof ( * t ) ) ;
2001-12-05 19:41:52 +03:00
if ( ! t ) {
2002-03-07 23:56:10 +03:00
log_error ( " create_target: malloc(%d) failed " , sizeof ( * t ) ) ;
2001-11-21 15:47:42 +03:00
return NULL ;
2001-12-05 19:41:52 +03:00
}
2001-11-21 15:47:42 +03:00
memset ( t , 0 , sizeof ( * t ) ) ;
if ( ! ( t - > params = strdup ( params ) ) ) {
2002-01-18 22:37:26 +03:00
log_error ( " create_target: strdup(params) failed " ) ;
2001-11-21 15:47:42 +03:00
goto bad ;
}
if ( ! ( t - > type = strdup ( type ) ) ) {
2002-01-18 22:37:26 +03:00
log_error ( " create_target: strdup(type) failed " ) ;
2001-11-21 15:47:42 +03:00
goto bad ;
}
t - > start = start ;
t - > length = len ;
return t ;
2002-03-07 23:56:10 +03:00
bad :
2001-11-21 15:47:42 +03:00
free ( t - > params ) ;
free ( t - > type ) ;
free ( t ) ;
return NULL ;
}
static void * _add_target ( struct target * t , void * out , void * end )
{
void * out_sp = out ;
struct dm_target_spec sp ;
int len ;
const char no_space [ ] = " Ran out of memory building ioctl parameter " ;
out + = sizeof ( struct dm_target_spec ) ;
if ( out > = end ) {
2002-01-18 22:37:26 +03:00
log_error ( no_space ) ;
2001-11-21 15:47:42 +03:00
return NULL ;
}
sp . status = 0 ;
sp . sector_start = t - > start ;
sp . length = t - > length ;
strncpy ( sp . target_type , t - > type , sizeof ( sp . target_type ) ) ;
len = strlen ( t - > params ) ;
if ( ( out + len + 1 ) > = end ) {
2002-01-18 22:37:26 +03:00
log_error ( no_space ) ;
2001-11-21 15:47:42 +03:00
2002-01-18 22:37:26 +03:00
log_error ( " t->params= '%s' " , t - > params ) ;
2001-11-21 15:47:42 +03:00
return NULL ;
}
strcpy ( ( char * ) out , t - > params ) ;
out + = len + 1 ;
/* align next block */
out = _align ( out , ALIGNMENT ) ;
sp . next = out - out_sp ;
memcpy ( out_sp , & sp , sizeof ( sp ) ) ;
return out ;
}
static struct dm_ioctl * _flatten ( struct dm_task * dmt )
{
2002-03-06 17:38:25 +03:00
const size_t min_size = 16 * 1024 ;
2001-11-21 15:47:42 +03:00
struct dm_ioctl * dmi ;
struct target * t ;
size_t len = sizeof ( struct dm_ioctl ) ;
void * b , * e ;
int count = 0 ;
for ( t = dmt - > head ; t ; t = t - > next ) {
len + = sizeof ( struct dm_target_spec ) ;
len + = strlen ( t - > params ) + 1 + ALIGNMENT ;
count + + ;
}
2002-01-11 02:29:16 +03:00
if ( count & & dmt - > newname ) {
2002-01-18 22:37:26 +03:00
log_error ( " targets and newname are incompatible " ) ;
2002-01-11 02:29:16 +03:00
return NULL ;
}
if ( dmt - > newname )
len + = strlen ( dmt - > newname ) + 1 ;
2002-03-06 17:38:25 +03:00
/*
* Give len a minimum size so that we have space to store
* dependencies or status information .
*/
if ( len < min_size )
len = min_size ;
2001-11-21 15:47:42 +03:00
if ( ! ( dmi = malloc ( len ) ) )
return NULL ;
2002-01-08 01:01:50 +03:00
memset ( dmi , 0 , len ) ;
2002-06-19 17:07:05 +04:00
dmi - > version [ 0 ] = _cmd_data [ dmt - > type ] . version [ 0 ] ;
dmi - > version [ 1 ] = _cmd_data [ dmt - > type ] . version [ 1 ] ;
dmi - > version [ 2 ] = _cmd_data [ dmt - > type ] . version [ 2 ] ;
2001-11-21 15:47:42 +03:00
dmi - > data_size = len ;
2002-01-15 18:21:57 +03:00
dmi - > data_start = sizeof ( struct dm_ioctl ) ;
if ( dmt - > dev_name )
strncpy ( dmi - > name , dmt - > dev_name , sizeof ( dmi - > name ) ) ;
2002-01-15 02:07:32 +03:00
if ( dmt - > type = = DM_DEVICE_SUSPEND )
2002-01-15 18:21:57 +03:00
dmi - > flags | = DM_SUSPEND_FLAG ;
2002-01-15 02:07:32 +03:00
if ( dmt - > read_only )
2002-01-15 18:21:57 +03:00
dmi - > flags | = DM_READONLY_FLAG ;
2002-01-15 02:07:32 +03:00
2002-02-01 20:39:20 +03:00
if ( dmt - > minor > = 0 ) {
2002-01-15 18:21:57 +03:00
dmi - > flags | = DM_PERSISTENT_DEV_FLAG ;
2002-01-15 02:07:32 +03:00
dmi - > dev = MKDEV ( 0 , dmt - > minor ) ;
}
2001-11-21 15:47:42 +03:00
2002-03-12 01:44:36 +03:00
if ( dmt - > uuid )
strncpy ( dmi - > uuid , dmt - > uuid , sizeof ( dmi - > uuid ) ) ;
2001-11-21 15:47:42 +03:00
dmi - > target_count = count ;
b = ( void * ) ( dmi + 1 ) ;
e = ( void * ) ( ( char * ) dmi + len ) ;
for ( t = dmt - > head ; t ; t = t - > next )
if ( ! ( b = _add_target ( t , b , e ) ) )
goto bad ;
2002-01-11 02:29:16 +03:00
if ( dmt - > newname )
strcpy ( b , dmt - > newname ) ;
2001-11-21 15:47:42 +03:00
return dmi ;
2002-03-07 23:56:10 +03:00
bad :
2001-11-21 15:47:42 +03:00
free ( dmi ) ;
return NULL ;
}
int dm_task_run ( struct dm_task * dmt )
{
int fd = - 1 ;
struct dm_ioctl * dmi = _flatten ( dmt ) ;
unsigned int command ;
2001-11-21 20:08:37 +03:00
char control [ PATH_MAX ] ;
2001-11-21 15:47:42 +03:00
if ( ! dmi ) {
2002-01-18 22:37:26 +03:00
log_error ( " Couldn't create ioctl argument " ) ;
2001-11-21 15:47:42 +03:00
return 0 ;
}
2001-12-05 19:41:52 +03:00
snprintf ( control , sizeof ( control ) , " %s/control " , dm_dir ( ) ) ;
2001-11-21 20:08:37 +03:00
if ( ( fd = open ( control , O_RDWR ) ) < 0 ) {
2002-11-01 19:16:42 +03:00
log_error ( " %s: open failed: %s " , control , strerror ( errno ) ) ;
log_error ( " Is device-mapper driver missing from kernel? " ) ;
2001-11-21 15:47:42 +03:00
goto bad ;
}
2002-06-19 17:07:05 +04:00
if ( dmt - > type > = ( sizeof ( _cmd_data ) / sizeof ( * _cmd_data ) ) ) {
2002-01-18 22:37:26 +03:00
log_error ( " Internal error: unknown device-mapper task %d " ,
2002-03-07 23:56:10 +03:00
dmt - > type ) ;
2001-11-21 15:47:42 +03:00
goto bad ;
}
2002-06-19 17:07:05 +04:00
command = _cmd_data [ dmt - > type ] . cmd ;
if ( dmt - > type = = DM_DEVICE_TABLE )
dmi - > flags | = DM_STATUS_TABLE_FLAG ;
log_debug ( " dm %s %s %s %s " , _cmd_data [ dmt - > type ] . name , dmi - > name ,
2002-03-20 17:34:15 +03:00
dmi - > uuid , dmt - > newname ? dmt - > newname : " " ) ;
2001-11-21 15:47:42 +03:00
if ( ioctl ( fd , command , dmi ) < 0 ) {
2002-06-19 17:07:05 +04:00
log_error ( " device-mapper ioctl cmd %d failed: %s " ,
_IOC_NR ( command ) , strerror ( errno ) ) ;
2001-11-21 15:47:42 +03:00
goto bad ;
}
switch ( dmt - > type ) {
case DM_DEVICE_CREATE :
2002-01-15 02:07:32 +03:00
add_dev_node ( dmt - > dev_name , dmi - > dev ) ;
2001-11-21 15:47:42 +03:00
break ;
case DM_DEVICE_REMOVE :
2001-12-05 19:41:52 +03:00
rm_dev_node ( dmt - > dev_name ) ;
2001-11-21 15:47:42 +03:00
break ;
2002-04-11 16:45:18 +04:00
case DM_DEVICE_RENAME :
2002-04-24 01:47:50 +04:00
rename_dev_node ( dmt - > dev_name , dmt - > newname ) ;
2002-04-11 16:45:18 +04:00
break ;
2002-05-03 15:55:58 +04:00
case DM_DEVICE_STATUS :
case DM_DEVICE_TABLE :
2002-05-10 19:25:38 +04:00
if ( ! _unmarshal_status ( dmt , dmi ) )
goto bad ;
break ;
2001-11-21 15:47:42 +03:00
}
dmt - > dmi = dmi ;
2002-02-26 21:30:02 +03:00
close ( fd ) ;
2001-11-21 15:47:42 +03:00
return 1 ;
2002-03-07 23:56:10 +03:00
bad :
2001-11-21 15:47:42 +03:00
free ( dmi ) ;
if ( fd > = 0 )
close ( fd ) ;
return 0 ;
}