2018-05-14 14:16:43 +03:00
/*
* Copyright ( C ) 2001 - 2004 Sistina Software , Inc . All rights reserved .
* Copyright ( C ) 2004 - 2013 Red Hat , Inc . All rights reserved .
*
* This file is part of the device - mapper userspace tools .
*
* This copyrighted material is made available to anyone wishing to use ,
* modify , copy , or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License v .2 .1 .
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program ; if not , write to the Free Software Foundation ,
* Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
*/
2018-06-08 15:40:53 +03:00
# include "base/memory/zalloc.h"
2018-06-04 15:22:14 +03:00
# include "device_mapper/misc/dmlib.h"
2018-12-01 02:43:53 +03:00
# include "device_mapper/misc/dm-ioctl.h"
2018-06-04 15:22:14 +03:00
# include "device_mapper/ioctl/libdm-targets.h"
# include "device_mapper/libdm-common.h"
2018-05-14 14:16:43 +03:00
# include <stddef.h>
# include <fcntl.h>
# include <dirent.h>
# include <sys/ioctl.h>
# include <sys/utsname.h>
# include <limits.h>
# include <unistd.h>
# ifdef __linux__
2018-06-04 15:22:14 +03:00
# include "device_mapper / misc / kdev_t.h"
2018-05-14 14:16:43 +03:00
# include <linux / limits.h>
# else
# define MAJOR(x) major((x))
# define MINOR(x) minor((x))
2018-11-03 19:13:10 +03:00
# define MKDEV(x,y) makedev(((dev_t)x),((dev_t)y))
2018-05-14 14:16:43 +03:00
# endif
/*
* Ensure build compatibility .
* The hard - coded versions here are the highest present
* in the _cmd_data arrays .
*/
# if !((DM_VERSION_MAJOR == 4 && DM_VERSION_MINOR >= 6))
# error The version of dm-ioctl.h included is incompatible.
# endif
/* FIXME This should be exported in device-mapper.h */
# define DM_NAME "device-mapper"
# define PROC_MISC " / proc / misc"
# define PROC_DEVICES " / proc / devices"
# define MISC_NAME "misc"
# define NUMBER_OF_MAJORS 4096
/*
* Static minor number assigned since kernel version 2.6 .36 .
* The original definition is in kernel ' s include / linux / miscdevice . h .
* This number is also visible in modules . devname exported by depmod
* utility ( support included in module - init - tools version > = 3.12 ) .
*/
# define MAPPER_CTRL_MINOR 236
# define MISC_MAJOR 10
/* dm major version no for running kernel */
static unsigned _dm_version = DM_VERSION_MAJOR ;
static unsigned _dm_version_minor = 0 ;
static unsigned _dm_version_patchlevel = 0 ;
static int _log_suppress = 0 ;
static struct dm_timestamp * _dm_ioctl_timestamp = NULL ;
/*
* If the kernel dm driver only supports one major number
* we store it in _dm_device_major . Otherwise we indicate
* which major numbers have been claimed by device - mapper
* in _dm_bitset .
*/
static unsigned _dm_multiple_major_support = 1 ;
static dm_bitset_t _dm_bitset = NULL ;
static uint32_t _dm_device_major = 0 ;
static int _control_fd = - 1 ;
static int _hold_control_fd_open = 0 ;
static int _version_checked = 0 ;
static int _version_ok = 1 ;
static unsigned _ioctl_buffer_double_factor = 0 ;
const int _dm_compat = 0 ;
/* *INDENT-OFF* */
2024-05-03 15:28:58 +03:00
static const struct cmd_data _cmd_data_v4 [ ] = {
2018-05-14 14:16:43 +03:00
{ " create " , DM_DEV_CREATE , { 4 , 0 , 0 } } ,
{ " reload " , DM_TABLE_LOAD , { 4 , 0 , 0 } } ,
{ " remove " , DM_DEV_REMOVE , { 4 , 0 , 0 } } ,
{ " remove_all " , DM_REMOVE_ALL , { 4 , 0 , 0 } } ,
{ " suspend " , DM_DEV_SUSPEND , { 4 , 0 , 0 } } ,
{ " resume " , DM_DEV_SUSPEND , { 4 , 0 , 0 } } ,
{ " info " , DM_DEV_STATUS , { 4 , 0 , 0 } } ,
{ " deps " , DM_TABLE_DEPS , { 4 , 0 , 0 } } ,
{ " rename " , DM_DEV_RENAME , { 4 , 0 , 0 } } ,
{ " version " , DM_VERSION , { 4 , 0 , 0 } } ,
{ " status " , DM_TABLE_STATUS , { 4 , 0 , 0 } } ,
{ " table " , DM_TABLE_STATUS , { 4 , 0 , 0 } } ,
{ " waitevent " , DM_DEV_WAIT , { 4 , 0 , 0 } } ,
{ " names " , DM_LIST_DEVICES , { 4 , 0 , 0 } } ,
{ " clear " , DM_TABLE_CLEAR , { 4 , 0 , 0 } } ,
{ " mknodes " , DM_DEV_STATUS , { 4 , 0 , 0 } } ,
# ifdef DM_LIST_VERSIONS
{ " versions " , DM_LIST_VERSIONS , { 4 , 1 , 0 } } ,
# endif
# ifdef DM_TARGET_MSG
{ " message " , DM_TARGET_MSG , { 4 , 2 , 0 } } ,
# endif
# ifdef DM_DEV_SET_GEOMETRY
{ " setgeometry " , DM_DEV_SET_GEOMETRY , { 4 , 6 , 0 } } ,
# endif
2018-11-16 17:58:16 +03:00
# ifdef DM_DEV_ARM_POLL
{ " armpoll " , DM_DEV_ARM_POLL , { 4 , 36 , 0 } } ,
# endif
2019-09-16 12:58:10 +03:00
# ifdef DM_GET_TARGET_VERSION
{ " target-version " , DM_GET_TARGET_VERSION , { 4 , 41 , 0 } } ,
# endif
2018-05-14 14:16:43 +03:00
} ;
/* *INDENT-ON* */
# define ALIGNMENT 8
/* FIXME Rejig library to record & use errno instead */
# ifndef DM_EXISTS_FLAG
# define DM_EXISTS_FLAG 0x00000004
# endif
static char * _align ( char * ptr , unsigned int a )
{
register unsigned long agn = - - a ;
return ( char * ) ( ( ( unsigned long ) ptr + agn ) & ~ agn ) ;
}
static unsigned _kernel_major = 0 ;
static unsigned _kernel_minor = 0 ;
static unsigned _kernel_release = 0 ;
static int _uname ( void )
{
static int _uts_set = 0 ;
struct utsname _uts ;
int parts ;
if ( _uts_set )
return 1 ;
if ( uname ( & _uts ) ) {
log_error ( " uname failed: %s " , strerror ( errno ) ) ;
return 0 ;
}
parts = sscanf ( _uts . release , " %u.%u.%u " ,
& _kernel_major , & _kernel_minor , & _kernel_release ) ;
/* Kernels with a major number of 2 always had 3 parts. */
if ( parts < 1 | | ( _kernel_major < 3 & & parts < 3 ) ) {
log_error ( " Could not determine kernel version used. " ) ;
return 0 ;
}
_uts_set = 1 ;
return 1 ;
}
int get_uname_version ( unsigned * major , unsigned * minor , unsigned * release )
{
if ( ! _uname ( ) )
return_0 ;
* major = _kernel_major ;
* minor = _kernel_minor ;
* release = _kernel_release ;
return 1 ;
}
2023-01-15 23:42:13 +03:00
# ifdef DM_IOCTLS
2018-05-14 14:16:43 +03:00
/*
* Set number to NULL to populate _dm_bitset - otherwise first
* match is returned .
* Returns :
* 0 - error
* 1 - success - number found
* 2 - success - number not found ( only if require_module_loaded = 0 )
*/
static int _get_proc_number ( const char * file , const char * name ,
uint32_t * number , int require_module_loaded )
{
FILE * fl ;
char nm [ 256 ] ;
char * line = NULL ;
size_t len ;
uint32_t num ;
if ( ! ( fl = fopen ( file , " r " ) ) ) {
log_sys_error ( " fopen " , file ) ;
return 0 ;
}
while ( getline ( & line , & len , fl ) ! = - 1 ) {
2020-08-28 20:35:25 +03:00
if ( sscanf ( line , " %u %255s \n " , & num , & nm [ 0 ] ) = = 2 ) {
2018-05-14 14:16:43 +03:00
if ( ! strcmp ( name , nm ) ) {
if ( number ) {
* number = num ;
if ( fclose ( fl ) )
log_sys_error ( " fclose " , file ) ;
free ( line ) ;
return 1 ;
}
dm_bit_set ( _dm_bitset , num ) ;
}
}
}
if ( fclose ( fl ) )
log_sys_error ( " fclose " , file ) ;
free ( line ) ;
if ( number ) {
if ( require_module_loaded ) {
log_error ( " %s: No entry for %s found " , file , name ) ;
return 0 ;
}
return 2 ;
}
return 1 ;
}
static int _control_device_number ( uint32_t * major , uint32_t * minor )
{
if ( ! _get_proc_number ( PROC_DEVICES , MISC_NAME , major , 1 ) | |
! _get_proc_number ( PROC_MISC , DM_NAME , minor , 1 ) ) {
* major = 0 ;
return 0 ;
}
return 1 ;
}
2024-05-07 23:34:17 +03:00
static int _control_unlink ( const char * control )
{
if ( unlink ( control ) & & ( errno ! = ENOENT ) ) {
log_sys_error ( " unlink " , control ) ;
return - 1 ;
}
return 0 ;
}
2018-05-14 14:16:43 +03:00
/*
* Returns 1 if it exists on returning ; 0 if it doesn ' t ; - 1 if it ' s wrong .
*/
static int _control_exists ( const char * control , uint32_t major , uint32_t minor )
{
struct stat buf ;
if ( stat ( control , & buf ) < 0 ) {
if ( errno ! = ENOENT )
log_sys_error ( " stat " , control ) ;
return 0 ;
}
if ( ! S_ISCHR ( buf . st_mode ) ) {
log_verbose ( " %s: Wrong inode type " , control ) ;
2024-05-07 23:34:17 +03:00
return _control_unlink ( control ) ;
2018-05-14 14:16:43 +03:00
}
2018-11-03 19:13:10 +03:00
if ( major & & buf . st_rdev ! = MKDEV ( major , minor ) ) {
2018-05-14 14:16:43 +03:00
log_verbose ( " %s: Wrong device number: (%u, %u) instead of "
" (%u, %u) " , control ,
MAJOR ( buf . st_mode ) , MINOR ( buf . st_mode ) ,
major , minor ) ;
2024-05-07 23:34:17 +03:00
return _control_unlink ( control ) ;
2018-05-14 14:16:43 +03:00
}
return 1 ;
}
static int _create_control ( const char * control , uint32_t major , uint32_t minor )
{
int ret ;
mode_t old_umask ;
/*
* Return if the control already exists with intended major / minor
* or there ' s an error unlinking an apparently incorrect one .
*/
ret = _control_exists ( control , major , minor ) ;
if ( ret = = - 1 )
return_0 ; /* Failed to unlink existing incorrect node */
if ( ret )
return 1 ; /* Already exists and correct */
( void ) dm_prepare_selinux_context ( dm_dir ( ) , S_IFDIR ) ;
old_umask = umask ( DM_DEV_DIR_UMASK ) ;
ret = dm_create_dir ( dm_dir ( ) ) ;
umask ( old_umask ) ;
( void ) dm_prepare_selinux_context ( NULL , 0 ) ;
if ( ! ret )
return_0 ;
log_verbose ( " Creating device %s (%u, %u) " , control , major , minor ) ;
( void ) dm_prepare_selinux_context ( control , S_IFCHR ) ;
old_umask = umask ( DM_CONTROL_NODE_UMASK ) ;
if ( mknod ( control , S_IFCHR | S_IRUSR | S_IWUSR ,
2018-11-03 19:13:10 +03:00
MKDEV ( major , minor ) ) < 0 ) {
2023-02-13 01:44:43 +03:00
if ( errno ! = EEXIST ) {
log_sys_error ( " mknod " , control ) ;
ret = 0 ;
} else if ( _control_exists ( control , major , minor ) ! = 1 ) {
stack ; /* Invalid control node created by parallel command ? */
ret = 0 ;
}
2018-05-14 14:16:43 +03:00
}
umask ( old_umask ) ;
( void ) dm_prepare_selinux_context ( NULL , 0 ) ;
return ret ;
}
# endif
/*
* FIXME Update bitset in long - running process if dm claims new major numbers .
*/
/*
* If require_module_loaded = 0 , caller is responsible to check
* whether _dm_device_major or _dm_bitset is really set . If
* it ' s not , it means the module is not loaded .
*/
static int _create_dm_bitset ( int require_module_loaded )
{
int r ;
# ifdef DM_IOCTLS
if ( _dm_bitset | | _dm_device_major )
return 1 ;
if ( ! _uname ( ) )
return 0 ;
/*
* 2.6 kernels are limited to one major number .
* Assume 2.4 kernels are patched not to .
* FIXME Check _dm_version and _dm_version_minor if 2.6 changes this .
*/
if ( KERNEL_VERSION ( _kernel_major , _kernel_minor , _kernel_release ) > =
KERNEL_VERSION ( 2 , 6 , 0 ) )
_dm_multiple_major_support = 0 ;
if ( ! _dm_multiple_major_support ) {
if ( ! _get_proc_number ( PROC_DEVICES , DM_NAME , & _dm_device_major ,
require_module_loaded ) )
return 0 ;
return 1 ;
}
/* Multiple major numbers supported */
if ( ! ( _dm_bitset = dm_bitset_create ( NULL , NUMBER_OF_MAJORS ) ) )
return 0 ;
r = _get_proc_number ( PROC_DEVICES , DM_NAME , NULL , require_module_loaded ) ;
if ( ! r | | r = = 2 ) {
dm_bitset_destroy ( _dm_bitset ) ;
_dm_bitset = NULL ;
/*
* It ' s not an error if we didn ' t find anything and we
* didn ' t require module to be loaded at the same time .
*/
return r = = 2 ;
}
return 1 ;
# else
return 0 ;
# endif
}
int dm_is_dm_major ( uint32_t major )
{
if ( ! _create_dm_bitset ( 0 ) )
return 0 ;
if ( _dm_multiple_major_support ) {
if ( ! _dm_bitset )
return 0 ;
return dm_bit ( _dm_bitset , major ) ? 1 : 0 ;
}
if ( ! _dm_device_major )
return 0 ;
return ( major = = _dm_device_major ) ? 1 : 0 ;
}
static void _close_control_fd ( void )
{
if ( _control_fd ! = - 1 ) {
if ( close ( _control_fd ) < 0 )
2023-09-20 16:36:54 +03:00
log_sys_debug ( " close " , " _control_fd " ) ;
2018-05-14 14:16:43 +03:00
_control_fd = - 1 ;
}
}
# ifdef DM_IOCTLS
static int _open_and_assign_control_fd ( const char * control )
{
if ( ( _control_fd = open ( control , O_RDWR ) ) < 0 ) {
log_sys_error ( " open " , control ) ;
return 0 ;
}
return 1 ;
}
# endif
static int _open_control ( void )
{
# ifdef DM_IOCTLS
char control [ PATH_MAX ] ;
uint32_t major = MISC_MAJOR ;
uint32_t minor = MAPPER_CTRL_MINOR ;
if ( _control_fd ! = - 1 )
return 1 ;
if ( ! _uname ( ) )
return 0 ;
if ( dm_snprintf ( control , sizeof ( control ) , " %s/%s " , dm_dir ( ) , DM_CONTROL_NODE ) < 0 )
goto_bad ;
/*
* Prior to 2.6 .36 the minor number should be looked up in / proc .
*/
if ( ( KERNEL_VERSION ( _kernel_major , _kernel_minor , _kernel_release ) <
KERNEL_VERSION ( 2 , 6 , 36 ) ) & &
! _control_device_number ( & major , & minor ) )
goto_bad ;
/*
* Create the node with correct major and minor if not already done .
* Udev may already have created / dev / mapper / control
* from the modules . devname file generated by depmod .
*/
if ( ! _create_control ( control , major , minor ) )
goto_bad ;
/*
* As of 2.6 .36 kernels , the open can trigger autoloading dm - mod .
*/
if ( ! _open_and_assign_control_fd ( control ) )
goto_bad ;
if ( ! _create_dm_bitset ( 1 ) ) {
log_error ( " Failed to set up list of device-mapper major numbers " ) ;
return 0 ;
}
return 1 ;
bad :
log_error ( " Failure to communicate with kernel device-mapper driver. " ) ;
if ( ! geteuid ( ) )
log_error ( " Check that device-mapper is available in the kernel. " ) ;
return 0 ;
# else
return 1 ;
# endif
}
static void _dm_zfree_string ( char * string )
{
if ( string ) {
memset ( string , 0 , strlen ( string ) ) ;
2018-11-16 18:01:03 +03:00
asm volatile ( " " : : : " memory " ) ; /* Compiler barrier. */
2018-06-08 15:40:53 +03:00
free ( string ) ;
2018-05-14 14:16:43 +03:00
}
}
static void _dm_zfree_dmi ( struct dm_ioctl * dmi )
{
if ( dmi ) {
memset ( dmi , 0 , dmi - > data_size ) ;
2018-11-16 18:01:03 +03:00
asm volatile ( " " : : : " memory " ) ; /* Compiler barrier. */
2018-06-08 15:40:53 +03:00
free ( dmi ) ;
2018-05-14 14:16:43 +03:00
}
}
static void _dm_task_free_targets ( struct dm_task * dmt )
{
struct target * t , * n ;
for ( t = dmt - > head ; t ; t = n ) {
n = t - > next ;
2021-02-27 19:08:26 +03:00
if ( dmt - > secure_data )
_dm_zfree_string ( t - > params ) ;
else
free ( t - > params ) ;
2018-06-08 15:40:53 +03:00
free ( t - > type ) ;
free ( t ) ;
2018-05-14 14:16:43 +03:00
}
dmt - > head = dmt - > tail = NULL ;
}
void dm_task_destroy ( struct dm_task * dmt )
{
_dm_task_free_targets ( dmt ) ;
2021-02-27 19:08:26 +03:00
if ( dmt - > secure_data )
_dm_zfree_dmi ( dmt - > dmi . v4 ) ;
else
free ( dmt - > dmi . v4 ) ;
2018-06-08 15:40:53 +03:00
free ( dmt - > dev_name ) ;
free ( dmt - > mangled_dev_name ) ;
free ( dmt - > newname ) ;
free ( dmt - > message ) ;
free ( dmt - > geometry ) ;
free ( dmt - > uuid ) ;
free ( dmt - > mangled_uuid ) ;
free ( dmt ) ;
2018-05-14 14:16:43 +03:00
}
/*
* Protocol Version 4 functions .
*/
int dm_task_get_driver_version ( struct dm_task * dmt , char * version , size_t size )
{
unsigned * v ;
if ( ! dmt - > dmi . v4 ) {
if ( version )
version [ 0 ] = ' \0 ' ;
return 0 ;
}
v = dmt - > dmi . v4 - > version ;
_dm_version_minor = v [ 1 ] ;
_dm_version_patchlevel = v [ 2 ] ;
if ( version & &
( snprintf ( version , size , " %u.%u.%u " , v [ 0 ] , v [ 1 ] , v [ 2 ] ) < 0 ) ) {
log_error ( " Buffer for version is to short. " ) ;
if ( size > 0 )
version [ 0 ] = ' \0 ' ;
return 0 ;
}
return 1 ;
}
static int _check_version ( char * version , size_t size , int log_suppress )
{
struct dm_task * task ;
int r ;
if ( ! ( task = dm_task_create ( DM_DEVICE_VERSION ) ) ) {
log_error ( " Failed to get device-mapper version " ) ;
version [ 0 ] = ' \0 ' ;
return 0 ;
}
if ( log_suppress )
_log_suppress = 1 ;
r = dm_task_run ( task ) ;
if ( ! dm_task_get_driver_version ( task , version , size ) )
stack ;
dm_task_destroy ( task ) ;
_log_suppress = 0 ;
return r ;
}
/*
* Find out device - mapper ' s major version number the first time
* this is called and whether or not we support it .
*/
int dm_check_version ( void )
{
char libversion [ 64 ] = " " , dmversion [ 64 ] = " " ;
const char * compat = " " ;
if ( _version_checked )
return _version_ok ;
_version_checked = 1 ;
if ( _check_version ( dmversion , sizeof ( dmversion ) , _dm_compat ) )
return 1 ;
if ( ! _dm_compat )
goto_bad ;
log_verbose ( " device-mapper ioctl protocol version %u failed. "
" Trying protocol version 1. " , _dm_version ) ;
_dm_version = 1 ;
if ( _check_version ( dmversion , sizeof ( dmversion ) , 0 ) ) {
log_verbose ( " Using device-mapper ioctl protocol version 1 " ) ;
return 1 ;
}
compat = " (compat) " ;
bad :
dm_get_library_version ( libversion , sizeof ( libversion ) ) ;
log_error ( " Incompatible libdevmapper %s%s and kernel driver %s. " ,
* libversion ? libversion : " (unknown version) " , compat ,
* dmversion ? dmversion : " (unknown version) " ) ;
_version_ok = 0 ;
return 0 ;
}
int dm_cookie_supported ( void )
{
return ( dm_check_version ( ) & &
2021-12-08 12:56:21 +03:00
( ( _dm_version = = 4 ) ? _dm_version_minor > = 15 : _dm_version > 4 ) ) ;
2018-05-14 14:16:43 +03:00
}
static int _dm_inactive_supported ( void )
{
int inactive_supported = 0 ;
if ( dm_check_version ( ) & & _dm_version > = 4 ) {
if ( _dm_version_minor > = 16 )
inactive_supported = 1 ; /* upstream */
else if ( _dm_version_minor = = 11 & &
( _dm_version_patchlevel > = 6 & &
_dm_version_patchlevel < = 40 ) ) {
inactive_supported = 1 ; /* RHEL 5.7 */
}
}
return inactive_supported ;
}
void * dm_get_next_target ( struct dm_task * dmt , void * next ,
uint64_t * start , uint64_t * length ,
char * * target_type , char * * params )
{
struct target * t = ( struct target * ) next ;
if ( ! t )
t = dmt - > head ;
if ( ! t ) {
* start = 0 ;
* length = 0 ;
* target_type = 0 ;
* params = 0 ;
return NULL ;
}
* start = t - > start ;
* length = t - > length ;
* target_type = t - > type ;
* params = t - > params ;
return t - > next ;
}
/* Unmarshall the target info returned from a status call */
static int _unmarshal_status ( struct dm_task * dmt , struct dm_ioctl * dmi )
{
char * outbuf = ( char * ) dmi + dmi - > data_start ;
char * outptr = outbuf ;
uint32_t i ;
struct dm_target_spec * spec ;
_dm_task_free_targets ( dmt ) ;
for ( i = 0 ; i < dmi - > target_count ; i + + ) {
spec = ( struct dm_target_spec * ) outptr ;
if ( ! dm_task_add_target ( dmt , spec - > sector_start ,
spec - > length ,
spec - > target_type ,
outptr + sizeof ( * spec ) ) ) {
return 0 ;
}
outptr = outbuf + spec - > next ;
}
return 1 ;
}
int dm_format_dev ( char * buf , int bufsize , uint32_t dev_major ,
uint32_t dev_minor )
{
int r ;
if ( bufsize < 8 )
return 0 ;
r = snprintf ( buf , ( size_t ) bufsize , " %u:%u " , dev_major , dev_minor ) ;
if ( r < 0 | | r > bufsize - 1 )
return 0 ;
return 1 ;
}
int dm_task_get_info ( struct dm_task * dmt , struct dm_info * info )
{
if ( ! dmt - > dmi . v4 )
return 0 ;
memset ( info , 0 , sizeof ( * info ) ) ;
info - > exists = dmt - > dmi . v4 - > flags & DM_EXISTS_FLAG ? 1 : 0 ;
if ( ! info - > exists )
return 1 ;
info - > suspended = dmt - > dmi . v4 - > flags & DM_SUSPEND_FLAG ? 1 : 0 ;
info - > read_only = dmt - > dmi . v4 - > flags & DM_READONLY_FLAG ? 1 : 0 ;
info - > live_table = dmt - > dmi . v4 - > flags & DM_ACTIVE_PRESENT_FLAG ? 1 : 0 ;
info - > inactive_table = dmt - > dmi . v4 - > flags & DM_INACTIVE_PRESENT_FLAG ?
1 : 0 ;
info - > deferred_remove = dmt - > dmi . v4 - > flags & DM_DEFERRED_REMOVE ;
info - > internal_suspend = ( dmt - > dmi . v4 - > flags & DM_INTERNAL_SUSPEND_FLAG ) ? 1 : 0 ;
info - > target_count = dmt - > dmi . v4 - > target_count ;
info - > open_count = dmt - > dmi . v4 - > open_count ;
info - > event_nr = dmt - > dmi . v4 - > event_nr ;
info - > major = MAJOR ( dmt - > dmi . v4 - > dev ) ;
info - > minor = MINOR ( dmt - > dmi . v4 - > dev ) ;
return 1 ;
}
uint32_t dm_task_get_read_ahead ( const struct dm_task * dmt , uint32_t * read_ahead )
{
const char * dev_name ;
* read_ahead = 0 ;
if ( ! dmt - > dmi . v4 | | ! ( dmt - > dmi . v4 - > flags & DM_EXISTS_FLAG ) )
return 0 ;
if ( * dmt - > dmi . v4 - > name )
dev_name = dmt - > dmi . v4 - > name ;
else if ( ! ( dev_name = DEV_NAME ( dmt ) ) ) {
log_error ( " Get read ahead request failed: device name unrecorded. " ) ;
return 0 ;
}
return get_dev_node_read_ahead ( dev_name , MAJOR ( dmt - > dmi . v4 - > dev ) ,
MINOR ( dmt - > dmi . v4 - > dev ) , read_ahead ) ;
}
struct dm_deps * dm_task_get_deps ( struct dm_task * dmt )
{
2024-04-09 12:35:23 +03:00
if ( ! dmt ) {
log_error ( INTERNAL_ERROR " Missing dm_task. " ) ;
return NULL ;
}
2018-05-14 14:16:43 +03:00
return ( struct dm_deps * ) ( ( ( char * ) dmt - > dmi . v4 ) +
dmt - > dmi . v4 - > data_start ) ;
}
2021-12-08 12:45:13 +03:00
/*
* Round up the ptr to an 8 - byte boundary .
* Follow kernel pattern .
*/
# define ALIGN_MASK 7
static size_t _align_val ( size_t val )
{
return ( val + ALIGN_MASK ) & ~ ALIGN_MASK ;
}
static void * _align_ptr ( void * ptr )
{
return ( void * ) _align_val ( ( size_t ) ptr ) ;
}
static int _check_has_event_nr ( void ) {
static int _has_event_nr = - 1 ;
if ( _has_event_nr < 0 )
_has_event_nr = dm_check_version ( ) & &
( ( _dm_version = = 4 ) ? _dm_version_minor > = 38 : _dm_version > 4 ) ;
return _has_event_nr ;
}
2021-12-15 13:34:50 +03:00
struct dm_device_list {
struct dm_list list ;
unsigned count ;
unsigned features ;
struct dm_hash_table * uuids ;
} ;
int dm_task_get_device_list ( struct dm_task * dmt , struct dm_list * * devs_list ,
unsigned * devs_features )
{
struct dm_names * names , * names1 ;
struct dm_active_device * dm_dev , * dm_new_dev ;
struct dm_device_list * devs ;
unsigned next = 0 ;
uint32_t * event_nr ;
char * uuid_ptr ;
size_t len ;
int cnt = 0 ;
* devs_list = 0 ;
* devs_features = 0 ;
if ( ( names = dm_task_get_names ( dmt ) ) & & names - > dev ) {
names1 = names ;
if ( ! names - > name [ 0 ] )
cnt = - 1 ; /* -> cnt == 0 when no device is really present */
do {
names1 = ( struct dm_names * ) ( ( char * ) names1 + next ) ;
next = names1 - > next ;
+ + cnt ;
} while ( next ) ;
}
if ( ! ( devs = malloc ( sizeof ( * devs ) + ( cnt ? cnt * sizeof ( * dm_dev ) + ( char * ) names1 - ( char * ) names + 256 : 0 ) ) ) )
return_0 ;
dm_list_init ( & devs - > list ) ;
devs - > count = cnt ;
devs - > uuids = NULL ;
if ( ! cnt ) {
/* nothing in the list -> mark all features present */
* devs_features | = ( DM_DEVICE_LIST_HAS_EVENT_NR | DM_DEVICE_LIST_HAS_UUID ) ;
goto out ; /* nothing else to do */
}
dm_dev = ( struct dm_active_device * ) ( devs + 1 ) ;
do {
names = ( struct dm_names * ) ( ( char * ) names + next ) ;
dm_dev - > major = MAJOR ( names - > dev ) ;
dm_dev - > minor = MINOR ( names - > dev ) ;
dm_dev - > name = ( char * ) ( dm_dev + 1 ) ;
dm_dev - > event_nr = 0 ;
dm_dev - > uuid = NULL ;
len = strlen ( names - > name ) + 1 ;
2023-02-14 22:59:56 +03:00
memcpy ( dm_dev - > name , names - > name , len ) ;
2021-12-15 13:34:50 +03:00
dm_new_dev = _align_ptr ( ( char * ) ( dm_dev + 1 ) + len ) ;
if ( _check_has_event_nr ( ) ) {
/* Hash for UUIDs with some more bits to reduce colision count */
if ( ! devs - > uuids & & ! ( devs - > uuids = dm_hash_create ( cnt * 8 ) ) ) {
free ( devs ) ;
return_0 ;
}
* devs_features | = DM_DEVICE_LIST_HAS_EVENT_NR ;
event_nr = _align_ptr ( names - > name + len ) ;
dm_dev - > event_nr = event_nr [ 0 ] ;
if ( ( event_nr [ 1 ] & DM_NAME_LIST_FLAG_HAS_UUID ) ) {
* devs_features | = DM_DEVICE_LIST_HAS_UUID ;
uuid_ptr = _align_ptr ( event_nr + 2 ) ;
dm_dev - > uuid = ( char * ) dm_new_dev ;
2023-02-14 22:59:56 +03:00
len = strlen ( uuid_ptr ) + 1 ;
dm_new_dev = _align_ptr ( ( char * ) dm_new_dev + len ) ;
memcpy ( dm_dev - > uuid , uuid_ptr , len ) ;
2021-12-15 13:34:50 +03:00
if ( ! dm_hash_insert ( devs - > uuids , dm_dev - > uuid , dm_dev ) )
return_0 ; // FIXME
#if 0
log_debug ( " Active %s (%s) %d:%d event:%u " ,
dm_dev - > name , dm_dev - > uuid ,
dm_dev - > major , dm_dev - > minor , dm_dev - > event_nr ) ;
# endif
}
}
dm_list_add ( & devs - > list , & dm_dev - > list ) ;
dm_dev = dm_new_dev ;
next = names - > next ;
} while ( next ) ;
out :
* devs_list = ( struct dm_list * ) devs ;
return 1 ;
}
int dm_device_list_find_by_uuid ( struct dm_list * devs_list , const char * uuid ,
const struct dm_active_device * * dev )
{
struct dm_device_list * devs = ( struct dm_device_list * ) devs_list ;
struct dm_active_device * dm_dev ;
if ( devs - > uuids & &
( dm_dev = dm_hash_lookup ( devs - > uuids , uuid ) ) ) {
if ( dev )
* dev = dm_dev ;
return 1 ;
}
return 0 ;
}
void dm_device_list_destroy ( struct dm_list * * devs_list )
{
struct dm_device_list * devs = ( struct dm_device_list * ) * devs_list ;
if ( devs ) {
if ( devs - > uuids )
dm_hash_destroy ( devs - > uuids ) ;
free ( devs ) ;
* devs_list = NULL ;
}
}
2018-05-14 14:16:43 +03:00
struct dm_names * dm_task_get_names ( struct dm_task * dmt )
{
return ( struct dm_names * ) ( ( ( char * ) dmt - > dmi . v4 ) +
dmt - > dmi . v4 - > data_start ) ;
}
struct dm_versions * dm_task_get_versions ( struct dm_task * dmt )
{
return ( struct dm_versions * ) ( ( ( char * ) dmt - > dmi . v4 ) +
dmt - > dmi . v4 - > data_start ) ;
}
const char * dm_task_get_message_response ( struct dm_task * dmt )
{
const char * start , * end ;
if ( ! ( dmt - > dmi . v4 - > flags & DM_DATA_OUT_FLAG ) )
return NULL ;
start = ( const char * ) dmt - > dmi . v4 + dmt - > dmi . v4 - > data_start ;
end = ( const char * ) dmt - > dmi . v4 + dmt - > dmi . v4 - > data_size ;
if ( end < start ) {
log_error ( INTERNAL_ERROR " Corrupted message structure returned: start %d > end %d " , ( int ) dmt - > dmi . v4 - > data_start , ( int ) dmt - > dmi . v4 - > data_size ) ;
return NULL ;
}
if ( ! memchr ( start , 0 , end - start ) ) {
log_error ( INTERNAL_ERROR " Message response doesn't contain terminating NUL character " ) ;
return NULL ;
}
return start ;
}
int dm_task_set_ro ( struct dm_task * dmt )
{
dmt - > read_only = 1 ;
return 1 ;
}
int dm_task_set_read_ahead ( struct dm_task * dmt , uint32_t read_ahead ,
uint32_t read_ahead_flags )
{
dmt - > read_ahead = read_ahead ;
dmt - > read_ahead_flags = read_ahead_flags ;
return 1 ;
}
int dm_task_suppress_identical_reload ( struct dm_task * dmt )
{
dmt - > suppress_identical_reload = 1 ;
return 1 ;
}
2019-11-21 01:07:27 +03:00
void dm_task_skip_reload_params_compare ( struct dm_task * dmt )
{
dmt - > skip_reload_params_compare = 1 ;
}
2018-05-14 14:16:43 +03:00
int dm_task_set_add_node ( struct dm_task * dmt , dm_add_node_t add_node )
{
switch ( add_node ) {
case DM_ADD_NODE_ON_RESUME :
case DM_ADD_NODE_ON_CREATE :
dmt - > add_node = add_node ;
return 1 ;
default :
log_error ( " Unknown add node parameter " ) ;
return 0 ;
}
}
int dm_task_set_newuuid ( struct dm_task * dmt , const char * newuuid )
{
dm_string_mangling_t mangling_mode = dm_get_name_mangling_mode ( ) ;
char mangled_uuid [ DM_UUID_LEN ] ;
int r = 0 ;
if ( strlen ( newuuid ) > = DM_UUID_LEN ) {
log_error ( " Uuid \" %s \" too long " , newuuid ) ;
return 0 ;
}
if ( ! check_multiple_mangled_string_allowed ( newuuid , " new UUID " , mangling_mode ) )
return_0 ;
if ( mangling_mode ! = DM_STRING_MANGLING_NONE & &
( r = mangle_string ( newuuid , " new UUID " , strlen ( newuuid ) , mangled_uuid ,
sizeof ( mangled_uuid ) , mangling_mode ) ) < 0 ) {
log_error ( " Failed to mangle new device UUID \" %s \" " , newuuid ) ;
return 0 ;
}
if ( r ) {
log_debug_activation ( " New device uuid mangled [%s]: %s --> %s " ,
mangling_mode = = DM_STRING_MANGLING_AUTO ? " auto " : " hex " ,
newuuid , mangled_uuid ) ;
newuuid = mangled_uuid ;
}
2018-06-08 15:40:53 +03:00
free ( dmt - > newname ) ;
if ( ! ( dmt - > newname = strdup ( newuuid ) ) ) {
2018-05-14 14:16:43 +03:00
log_error ( " dm_task_set_newuuid: strdup(%s) failed " , newuuid ) ;
return 0 ;
}
dmt - > new_uuid = 1 ;
return 1 ;
}
int dm_task_set_message ( struct dm_task * dmt , const char * message )
{
2018-06-08 15:40:53 +03:00
free ( dmt - > message ) ;
if ( ! ( dmt - > message = strdup ( message ) ) ) {
2018-05-14 14:16:43 +03:00
log_error ( " dm_task_set_message: strdup failed " ) ;
return 0 ;
}
return 1 ;
}
int dm_task_set_sector ( struct dm_task * dmt , uint64_t sector )
{
dmt - > sector = sector ;
return 1 ;
}
int dm_task_set_geometry ( struct dm_task * dmt , const char * cylinders , const char * heads ,
const char * sectors , const char * start )
{
2018-06-08 15:40:53 +03:00
free ( dmt - > geometry ) ;
2018-05-14 14:16:43 +03:00
if ( dm_asprintf ( & ( dmt - > geometry ) , " %s %s %s %s " ,
cylinders , heads , sectors , start ) < 0 ) {
log_error ( " dm_task_set_geometry: sprintf failed " ) ;
return 0 ;
}
return 1 ;
}
int dm_task_no_flush ( struct dm_task * dmt )
{
dmt - > no_flush = 1 ;
return 1 ;
}
int dm_task_no_open_count ( struct dm_task * dmt )
{
dmt - > no_open_count = 1 ;
return 1 ;
}
int dm_task_skip_lockfs ( struct dm_task * dmt )
{
dmt - > skip_lockfs = 1 ;
return 1 ;
}
int dm_task_secure_data ( struct dm_task * dmt )
{
dmt - > secure_data = 1 ;
return 1 ;
}
2021-09-22 18:30:42 +03:00
int dm_task_ima_measurement ( struct dm_task * dmt )
{
dmt - > ima_measurement = 1 ;
return 1 ;
}
2018-05-14 14:16:43 +03:00
int dm_task_retry_remove ( struct dm_task * dmt )
{
dmt - > retry_remove = 1 ;
return 1 ;
}
int dm_task_deferred_remove ( struct dm_task * dmt )
{
dmt - > deferred_remove = 1 ;
return 1 ;
}
int dm_task_query_inactive_table ( struct dm_task * dmt )
{
dmt - > query_inactive_table = 1 ;
return 1 ;
}
int dm_task_set_event_nr ( struct dm_task * dmt , uint32_t event_nr )
{
dmt - > event_nr = event_nr ;
return 1 ;
}
int dm_task_set_record_timestamp ( struct dm_task * dmt )
{
if ( ! _dm_ioctl_timestamp )
_dm_ioctl_timestamp = dm_timestamp_alloc ( ) ;
if ( ! _dm_ioctl_timestamp )
return_0 ;
dmt - > record_timestamp = 1 ;
return 1 ;
}
struct dm_timestamp * dm_task_get_ioctl_timestamp ( struct dm_task * dmt )
{
return dmt - > record_timestamp ? _dm_ioctl_timestamp : NULL ;
}
struct target * create_target ( uint64_t start , uint64_t len , const char * type ,
const char * params )
{
struct target * t ;
if ( strlen ( type ) > = DM_MAX_TYPE_NAME ) {
log_error ( " Target type name %s is too long. " , type ) ;
return NULL ;
}
2018-06-08 15:40:53 +03:00
if ( ! ( t = zalloc ( sizeof ( * t ) ) ) ) {
2018-05-14 14:16:43 +03:00
log_error ( " create_target: malloc(% " PRIsize_t " ) failed " ,
sizeof ( * t ) ) ;
return NULL ;
}
2018-06-08 15:40:53 +03:00
if ( ! ( t - > params = strdup ( params ) ) ) {
2018-05-14 14:16:43 +03:00
log_error ( " create_target: strdup(params) failed " ) ;
goto bad ;
}
2018-06-08 15:40:53 +03:00
if ( ! ( t - > type = strdup ( type ) ) ) {
2018-05-14 14:16:43 +03:00
log_error ( " create_target: strdup(type) failed " ) ;
goto bad ;
}
t - > start = start ;
t - > length = len ;
return t ;
bad :
_dm_zfree_string ( t - > params ) ;
2018-06-08 15:40:53 +03:00
free ( t - > type ) ;
free ( t ) ;
2018-05-14 14:16:43 +03:00
return NULL ;
}
static char * _add_target ( struct target * t , char * out , char * end )
{
char * out_sp = out ;
struct dm_target_spec sp ;
size_t sp_size = sizeof ( struct dm_target_spec ) ;
unsigned int backslash_count = 0 ;
int len ;
char * pt ;
if ( strlen ( t - > type ) > = sizeof ( sp . target_type ) ) {
log_error ( " Target type name %s is too long. " , t - > type ) ;
return NULL ;
}
sp . status = 0 ;
sp . sector_start = t - > start ;
sp . length = t - > length ;
strncpy ( sp . target_type , t - > type , sizeof ( sp . target_type ) - 1 ) ;
sp . target_type [ sizeof ( sp . target_type ) - 1 ] = ' \0 ' ;
out + = sp_size ;
pt = t - > params ;
while ( * pt )
if ( * pt + + = = ' \\ ' )
backslash_count + + ;
2023-02-14 22:59:56 +03:00
len = strlen ( t - > params ) + 1 ;
if ( ( out > = end ) | | ( out + len + backslash_count ) > = end ) {
2018-05-14 14:16:43 +03:00
log_error ( " Ran out of memory building ioctl parameter " ) ;
return NULL ;
}
if ( backslash_count ) {
/* replace "\" with "\\" */
pt = t - > params ;
do {
if ( * pt = = ' \\ ' )
* out + + = ' \\ ' ;
* out + + = * pt + + ;
} while ( * pt ) ;
* out + + = ' \0 ' ;
}
else {
2023-02-14 22:59:56 +03:00
memcpy ( out , t - > params , len ) ;
out + = len + backslash_count ;
2018-05-14 14:16:43 +03:00
}
/* align next block */
out = _align ( out , ALIGNMENT ) ;
sp . next = out - out_sp ;
memcpy ( out_sp , & sp , sp_size ) ;
return out ;
}
static int _lookup_dev_name ( uint64_t dev , char * buf , size_t len )
{
struct dm_names * names ;
unsigned next = 0 ;
struct dm_task * dmt ;
int r = 0 ;
if ( ! ( dmt = dm_task_create ( DM_DEVICE_LIST ) ) )
return 0 ;
if ( ! dm_task_run ( dmt ) )
goto out ;
if ( ! ( names = dm_task_get_names ( dmt ) ) )
goto out ;
if ( ! names - > dev )
goto out ;
do {
names = ( struct dm_names * ) ( ( char * ) names + next ) ;
if ( names - > dev = = dev ) {
2024-04-09 01:31:57 +03:00
memccpy ( buf , names - > name , 0 , len ) ;
2018-05-14 14:16:43 +03:00
r = 1 ;
break ;
}
next = names - > next ;
} while ( next ) ;
out :
dm_task_destroy ( dmt ) ;
return r ;
}
2018-11-16 17:57:15 +03:00
static int _add_params ( int type )
{
switch ( type ) {
case DM_DEVICE_REMOVE_ALL :
case DM_DEVICE_CREATE :
case DM_DEVICE_REMOVE :
case DM_DEVICE_SUSPEND :
case DM_DEVICE_STATUS :
case DM_DEVICE_CLEAR :
case DM_DEVICE_ARM_POLL :
return 0 ; /* IOCTL_FLAGS_NO_PARAMS in drivers/md/dm-ioctl.c */
default :
return 1 ;
}
}
2018-05-14 14:16:43 +03:00
static struct dm_ioctl * _flatten ( struct dm_task * dmt , unsigned repeat_count )
{
2021-02-27 23:22:41 +03:00
size_t min_size ;
2018-05-14 14:16:43 +03:00
const int ( * version ) [ 3 ] ;
struct dm_ioctl * dmi ;
struct target * t ;
struct dm_target_msg * tmsg ;
size_t len = sizeof ( struct dm_ioctl ) ;
2023-02-14 22:59:56 +03:00
size_t message_len = 0 , newname_len = 0 , geometry_len = 0 ;
2018-05-14 14:16:43 +03:00
char * b , * e ;
int count = 0 ;
2018-11-16 17:57:15 +03:00
if ( _add_params ( dmt - > type ) )
for ( t = dmt - > head ; t ; t = t - > next ) {
len + = sizeof ( struct dm_target_spec ) ;
len + = strlen ( t - > params ) + 1 + ALIGNMENT ;
count + + ;
}
else if ( dmt - > head )
log_debug_activation ( INTERNAL_ERROR " dm '%s' ioctl should not define parameters. " ,
_cmd_data_v4 [ dmt - > type ] . name ) ;
2021-02-27 23:22:41 +03:00
switch ( dmt - > type ) {
case DM_DEVICE_CREATE :
case DM_DEVICE_DEPS :
case DM_DEVICE_LIST :
case DM_DEVICE_STATUS :
case DM_DEVICE_TABLE :
case DM_DEVICE_TARGET_MSG :
min_size = 16 * 1024 ;
2021-03-09 16:58:09 +03:00
break ;
2021-02-27 23:22:41 +03:00
default :
min_size = 2 * 1024 ;
}
2018-05-14 14:16:43 +03:00
if ( count & & ( dmt - > sector | | dmt - > message ) ) {
log_error ( " targets and message are incompatible " ) ;
return NULL ;
}
if ( count & & dmt - > newname ) {
log_error ( " targets and rename are incompatible " ) ;
return NULL ;
}
if ( count & & dmt - > geometry ) {
log_error ( " targets and geometry are incompatible " ) ;
return NULL ;
}
if ( dmt - > newname & & ( dmt - > sector | | dmt - > message ) ) {
log_error ( " message and rename are incompatible " ) ;
return NULL ;
}
if ( dmt - > newname & & dmt - > geometry ) {
log_error ( " geometry and rename are incompatible " ) ;
return NULL ;
}
if ( dmt - > geometry & & ( dmt - > sector | | dmt - > message ) ) {
log_error ( " geometry and message are incompatible " ) ;
return NULL ;
}
if ( dmt - > sector & & ! dmt - > message ) {
log_error ( " message is required with sector " ) ;
return NULL ;
}
2023-02-14 22:59:56 +03:00
if ( dmt - > newname ) {
newname_len = strlen ( dmt - > newname ) + 1 ;
len + = newname_len ;
}
2018-05-14 14:16:43 +03:00
2023-02-14 22:59:56 +03:00
if ( dmt - > message ) {
message_len = strlen ( dmt - > message ) + 1 ;
len + = sizeof ( struct dm_target_msg ) + message_len ;
}
2018-05-14 14:16:43 +03:00
2023-02-14 22:59:56 +03:00
if ( dmt - > geometry ) {
geometry_len = strlen ( dmt - > geometry ) + 1 ;
len + = geometry_len ;
}
2018-05-14 14:16:43 +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 ;
/* Increase buffer size if repeating because buffer was too small */
while ( repeat_count - - )
len * = 2 ;
2018-06-08 15:40:53 +03:00
if ( ! ( dmi = zalloc ( len ) ) )
2018-05-14 14:16:43 +03:00
return NULL ;
version = & _cmd_data_v4 [ dmt - > type ] . version ;
dmi - > version [ 0 ] = ( * version ) [ 0 ] ;
dmi - > version [ 1 ] = ( * version ) [ 1 ] ;
dmi - > version [ 2 ] = ( * version ) [ 2 ] ;
dmi - > data_size = len ;
dmi - > data_start = sizeof ( struct dm_ioctl ) ;
if ( dmt - > minor > = 0 ) {
if ( ! _dm_multiple_major_support & & dmt - > allow_default_major_fallback & &
dmt - > major ! = ( int ) _dm_device_major ) {
log_verbose ( " Overriding major number of %d "
" with %u for persistent device. " ,
dmt - > major , _dm_device_major ) ;
dmt - > major = _dm_device_major ;
}
if ( dmt - > major < = 0 ) {
log_error ( " Missing major number for persistent device. " ) ;
goto bad ;
}
dmi - > flags | = DM_PERSISTENT_DEV_FLAG ;
2018-11-03 19:13:10 +03:00
dmi - > dev = MKDEV ( dmt - > major , dmt - > minor ) ;
2018-05-14 14:16:43 +03:00
}
/* Does driver support device number referencing? */
if ( _dm_version_minor < 3 & & ! DEV_NAME ( dmt ) & & ! DEV_UUID ( dmt ) & & dmi - > dev ) {
if ( ! _lookup_dev_name ( dmi - > dev , dmi - > name , sizeof ( dmi - > name ) ) ) {
log_error ( " Unable to find name for device (% " PRIu32
" :% " PRIu32 " ) " , dmt - > major , dmt - > minor ) ;
goto bad ;
}
log_verbose ( " device (% " PRIu32 " :% " PRIu32 " ) is %s "
" for compatibility with old kernel " ,
dmt - > major , dmt - > minor , dmi - > name ) ;
}
/* FIXME Until resume ioctl supplies name, use dev_name for readahead */
if ( DEV_NAME ( dmt ) & & ( dmt - > type ! = DM_DEVICE_RESUME | | dmt - > minor < 0 | |
dmt - > major < 0 ) )
2024-04-09 01:31:57 +03:00
memccpy ( dmi - > name , DEV_NAME ( dmt ) , 0 , sizeof ( dmi - > name ) ) ;
2018-05-14 14:16:43 +03:00
if ( DEV_UUID ( dmt ) )
2024-04-09 01:31:57 +03:00
memccpy ( dmi - > uuid , DEV_UUID ( dmt ) , 0 , sizeof ( dmi - > uuid ) ) ;
2018-05-14 14:16:43 +03:00
if ( dmt - > type = = DM_DEVICE_SUSPEND )
dmi - > flags | = DM_SUSPEND_FLAG ;
if ( dmt - > no_flush ) {
if ( _dm_version_minor < 12 )
log_verbose ( " No flush flag unsupported by kernel. "
" Buffers will be flushed. " ) ;
else
dmi - > flags | = DM_NOFLUSH_FLAG ;
}
if ( dmt - > read_only )
dmi - > flags | = DM_READONLY_FLAG ;
if ( dmt - > skip_lockfs )
dmi - > flags | = DM_SKIP_LOCKFS_FLAG ;
if ( dmt - > deferred_remove & & ( dmt - > type = = DM_DEVICE_REMOVE | | dmt - > type = = DM_DEVICE_REMOVE_ALL ) )
dmi - > flags | = DM_DEFERRED_REMOVE ;
if ( dmt - > secure_data ) {
if ( _dm_version_minor < 20 )
log_verbose ( " Secure data flag unsupported by kernel. "
" Buffers will not be wiped after use. " ) ;
dmi - > flags | = DM_SECURE_DATA_FLAG ;
}
if ( dmt - > query_inactive_table ) {
if ( ! _dm_inactive_supported ( ) )
log_warn ( " WARNING: Inactive table query unsupported "
" by kernel. It will use live table. " ) ;
dmi - > flags | = DM_QUERY_INACTIVE_TABLE_FLAG ;
}
if ( dmt - > new_uuid ) {
if ( _dm_version_minor < 19 ) {
log_error ( " WARNING: Setting UUID unsupported by "
" kernel. Aborting operation. " ) ;
goto bad ;
}
dmi - > flags | = DM_UUID_FLAG ;
}
2021-09-22 18:30:42 +03:00
if ( dmt - > ima_measurement ) {
if ( _dm_version_minor < 45 ) {
log_error ( " WARNING: IMA measurement unsupported by "
" kernel. Aborting operation. " ) ;
goto bad ;
}
dmi - > flags | = DM_IMA_MEASUREMENT_FLAG ;
}
2018-05-14 14:16:43 +03:00
dmi - > target_count = count ;
dmi - > event_nr = dmt - > event_nr ;
b = ( char * ) ( dmi + 1 ) ;
e = ( char * ) dmi + len ;
2018-11-16 17:57:15 +03:00
if ( _add_params ( dmt - > type ) )
for ( t = dmt - > head ; t ; t = t - > next )
if ( ! ( b = _add_target ( t , b , e ) ) )
goto_bad ;
2018-05-14 14:16:43 +03:00
if ( dmt - > newname )
2023-02-14 22:59:56 +03:00
memcpy ( b , dmt - > newname , newname_len ) ;
2018-05-14 14:16:43 +03:00
if ( dmt - > message ) {
tmsg = ( struct dm_target_msg * ) b ;
tmsg - > sector = dmt - > sector ;
2023-02-14 22:59:56 +03:00
memcpy ( tmsg - > message , dmt - > message , message_len ) ;
2018-05-14 14:16:43 +03:00
}
if ( dmt - > geometry )
2023-02-14 22:59:56 +03:00
memcpy ( b , dmt - > geometry , geometry_len ) ;
2018-05-14 14:16:43 +03:00
return dmi ;
bad :
_dm_zfree_dmi ( dmi ) ;
return NULL ;
}
static int _process_mapper_dir ( struct dm_task * dmt )
{
struct dirent * dirent ;
DIR * d ;
const char * dir ;
int r = 1 ;
dir = dm_dir ( ) ;
if ( ! ( d = opendir ( dir ) ) ) {
log_sys_error ( " opendir " , dir ) ;
return 0 ;
}
while ( ( dirent = readdir ( d ) ) ) {
if ( ! strcmp ( dirent - > d_name , " . " ) | |
! strcmp ( dirent - > d_name , " .. " ) | |
! strcmp ( dirent - > d_name , " control " ) )
continue ;
if ( ! dm_task_set_name ( dmt , dirent - > d_name ) ) {
r = 0 ;
stack ;
continue ; /* try next name */
}
if ( ! dm_task_run ( dmt ) ) {
r = 0 ;
stack ; /* keep going */
}
}
if ( closedir ( d ) )
2021-03-09 13:08:47 +03:00
log_sys_debug ( " closedir " , dir ) ;
2018-05-14 14:16:43 +03:00
return r ;
}
static int _process_all_v4 ( struct dm_task * dmt )
{
struct dm_task * task ;
struct dm_names * names ;
unsigned next = 0 ;
int r = 1 ;
if ( ! ( task = dm_task_create ( DM_DEVICE_LIST ) ) )
return 0 ;
if ( ! dm_task_run ( task ) ) {
r = 0 ;
goto out ;
}
if ( ! ( names = dm_task_get_names ( task ) ) ) {
r = 0 ;
goto out ;
}
if ( ! names - > dev )
goto out ;
do {
names = ( struct dm_names * ) ( ( char * ) names + next ) ;
if ( ! dm_task_set_name ( dmt , names - > name ) ) {
r = 0 ;
goto out ;
}
if ( ! dm_task_run ( dmt ) )
r = 0 ;
next = names - > next ;
} while ( next ) ;
out :
dm_task_destroy ( task ) ;
return r ;
}
static int _mknodes_v4 ( struct dm_task * dmt )
{
( void ) _process_mapper_dir ( dmt ) ;
return _process_all_v4 ( dmt ) ;
}
/*
* If an operation that uses a cookie fails , decrement the
* semaphore instead of udev .
*/
static int _udev_complete ( struct dm_task * dmt )
{
uint16_t base ;
if ( dmt - > cookie_set & &
( base = dmt - > event_nr & ~ DM_UDEV_FLAGS_MASK ) )
/* strip flags from the cookie and use cookie magic instead */
return dm_udev_complete ( base | ( DM_COOKIE_MAGIC < <
DM_UDEV_FLAGS_SHIFT ) ) ;
return 1 ;
}
# ifdef DM_IOCTLS
static int _check_uevent_generated ( struct dm_ioctl * dmi )
{
if ( ! dm_check_version ( ) | |
2021-12-08 12:56:21 +03:00
( ( _dm_version = = 4 ) ? _dm_version_minor < 17 : _dm_version < 4 ) )
2018-05-14 14:16:43 +03:00
/* can't check, assume uevent is generated */
return 1 ;
return dmi - > flags & DM_UEVENT_GENERATED_FLAG ;
}
# endif
static int _create_and_load_v4 ( struct dm_task * dmt )
{
struct dm_task * task ;
2023-10-13 14:18:13 +03:00
int r , ioctl_errno = 0 ;
2018-05-14 14:16:43 +03:00
uint32_t cookie ;
/* Use new task struct to create the device */
if ( ! ( task = dm_task_create ( DM_DEVICE_CREATE ) ) ) {
_udev_complete ( dmt ) ;
return_0 ;
}
/* Copy across relevant fields */
if ( dmt - > dev_name & & ! dm_task_set_name ( task , dmt - > dev_name ) )
goto_bad ;
if ( dmt - > uuid & & ! dm_task_set_uuid ( task , dmt - > uuid ) )
goto_bad ;
task - > major = dmt - > major ;
task - > minor = dmt - > minor ;
task - > uid = dmt - > uid ;
task - > gid = dmt - > gid ;
task - > mode = dmt - > mode ;
/* FIXME: Just for udev_check in dm_task_run. Can we avoid this? */
task - > event_nr = dmt - > event_nr & DM_UDEV_FLAGS_MASK ;
task - > cookie_set = dmt - > cookie_set ;
task - > add_node = dmt - > add_node ;
2023-10-13 14:18:13 +03:00
if ( ! dm_task_run ( task ) ) {
ioctl_errno = task - > ioctl_errno ;
2018-05-14 14:16:43 +03:00
goto_bad ;
2023-10-13 14:18:13 +03:00
}
2018-05-14 14:16:43 +03:00
dm_task_destroy ( task ) ;
/* Next load the table */
if ( ! ( task = dm_task_create ( DM_DEVICE_RELOAD ) ) ) {
stack ;
_udev_complete ( dmt ) ;
goto revert ;
}
/* Copy across relevant fields */
if ( dmt - > dev_name & & ! dm_task_set_name ( task , dmt - > dev_name ) ) {
stack ;
dm_task_destroy ( task ) ;
_udev_complete ( dmt ) ;
goto revert ;
}
task - > read_only = dmt - > read_only ;
task - > head = dmt - > head ;
task - > tail = dmt - > tail ;
task - > secure_data = dmt - > secure_data ;
2021-09-22 18:30:42 +03:00
task - > ima_measurement = dmt - > ima_measurement ;
2018-05-14 14:16:43 +03:00
r = dm_task_run ( task ) ;
2023-10-13 14:18:13 +03:00
if ( ! r )
ioctl_errno = task - > ioctl_errno ;
2018-05-14 14:16:43 +03:00
task - > head = NULL ;
task - > tail = NULL ;
dm_task_destroy ( task ) ;
if ( ! r ) {
stack ;
_udev_complete ( dmt ) ;
goto revert ;
}
/* Use the original structure last so the info will be correct */
dmt - > type = DM_DEVICE_RESUME ;
2018-06-08 15:40:53 +03:00
free ( dmt - > uuid ) ;
2018-05-14 14:16:43 +03:00
dmt - > uuid = NULL ;
2018-06-08 15:40:53 +03:00
free ( dmt - > mangled_uuid ) ;
2018-05-14 14:16:43 +03:00
dmt - > mangled_uuid = NULL ;
2024-04-09 19:34:01 +03:00
/* coverity[double_free] recursive function call */
2018-11-16 17:54:09 +03:00
_dm_task_free_targets ( dmt ) ;
2018-05-14 14:16:43 +03:00
if ( dm_task_run ( dmt ) )
return 1 ;
revert :
dmt - > type = DM_DEVICE_REMOVE ;
2018-06-08 15:40:53 +03:00
free ( dmt - > uuid ) ;
2018-05-14 14:16:43 +03:00
dmt - > uuid = NULL ;
2018-06-08 15:40:53 +03:00
free ( dmt - > mangled_uuid ) ;
2018-05-14 14:16:43 +03:00
dmt - > mangled_uuid = NULL ;
2024-04-09 19:34:01 +03:00
/* coverity[double_free] recursive function call */
2018-11-16 17:54:09 +03:00
_dm_task_free_targets ( dmt ) ;
2018-05-14 14:16:43 +03:00
/*
* Also udev - synchronize " remove " dm task that is a part of this revert !
* But only if the original dm task was supposed to be synchronized .
*/
if ( dmt - > cookie_set ) {
cookie = ( dmt - > event_nr & ~ DM_UDEV_FLAGS_MASK ) |
( DM_COOKIE_MAGIC < < DM_UDEV_FLAGS_SHIFT ) ;
if ( ! dm_task_set_cookie ( dmt , & cookie ,
( dmt - > event_nr & DM_UDEV_FLAGS_MASK ) > >
DM_UDEV_FLAGS_SHIFT ) )
stack ; /* keep going */
}
if ( ! dm_task_run ( dmt ) )
log_error ( " Failed to revert device creation. " ) ;
2023-10-13 14:18:13 +03:00
if ( ioctl_errno ! = 0 )
dmt - > ioctl_errno = ioctl_errno ;
2018-05-14 14:16:43 +03:00
return 0 ;
bad :
dm_task_destroy ( task ) ;
_udev_complete ( dmt ) ;
2023-10-13 14:18:13 +03:00
if ( ioctl_errno ! = 0 )
dmt - > ioctl_errno = ioctl_errno ;
2018-05-14 14:16:43 +03:00
return 0 ;
}
uint64_t dm_task_get_existing_table_size ( struct dm_task * dmt )
{
return dmt - > existing_table_size ;
}
static int _reload_with_suppression_v4 ( struct dm_task * dmt )
{
struct dm_task * task ;
struct target * t1 , * t2 ;
size_t len ;
int r ;
/* New task to get existing table information */
if ( ! ( task = dm_task_create ( DM_DEVICE_TABLE ) ) ) {
log_error ( " Failed to create device-mapper task struct " ) ;
return 0 ;
}
/* Copy across relevant fields */
if ( dmt - > dev_name & & ! dm_task_set_name ( task , dmt - > dev_name ) ) {
dm_task_destroy ( task ) ;
return 0 ;
}
if ( dmt - > uuid & & ! dm_task_set_uuid ( task , dmt - > uuid ) ) {
dm_task_destroy ( task ) ;
return 0 ;
}
task - > major = dmt - > major ;
task - > minor = dmt - > minor ;
r = dm_task_run ( task ) ;
if ( ! r ) {
dm_task_destroy ( task ) ;
return r ;
}
/* Store existing table size */
t2 = task - > head ;
while ( t2 & & t2 - > next )
t2 = t2 - > next ;
dmt - > existing_table_size = t2 ? t2 - > start + t2 - > length : 0 ;
if ( ( ( task - > dmi . v4 - > flags & DM_READONLY_FLAG ) ? 1 : 0 ) ! = dmt - > read_only )
goto no_match ;
t1 = dmt - > head ;
t2 = task - > head ;
while ( t1 & & t2 ) {
len = strlen ( t2 - > params ) ;
while ( len - - > 0 & & t2 - > params [ len ] = = ' ' )
t2 - > params [ len ] = ' \0 ' ;
2019-11-21 01:07:27 +03:00
if ( t1 - > start ! = t2 - > start ) {
2021-02-03 22:44:10 +03:00
log_debug ( " reload %u:%u diff start %llu %llu type %s %s " , task - > major , task - > minor ,
( unsigned long long ) t1 - > start , ( unsigned long long ) t2 - > start , t1 - > type , t2 - > type ) ;
2019-11-21 01:07:27 +03:00
goto no_match ;
}
if ( t1 - > length ! = t2 - > length ) {
2021-02-03 22:44:10 +03:00
log_debug ( " reload %u:%u diff length %llu %llu type %s %s " , task - > major , task - > minor ,
( unsigned long long ) t1 - > length , ( unsigned long long ) t2 - > length , t1 - > type , t2 - > type ) ;
2018-05-14 14:16:43 +03:00
goto no_match ;
2019-11-21 01:07:27 +03:00
}
if ( strcmp ( t1 - > type , t2 - > type ) ) {
2021-02-03 22:44:10 +03:00
log_debug ( " reload %u:%u diff type %s %s " , task - > major , task - > minor , t1 - > type , t2 - > type ) ;
2019-11-21 01:07:27 +03:00
goto no_match ;
}
if ( strcmp ( t1 - > params , t2 - > params ) ) {
2021-02-03 22:44:10 +03:00
if ( dmt - > skip_reload_params_compare ) {
log_debug ( " reload %u:%u diff params ignore for type %s " ,
task - > major , task - > minor , t1 - > type ) ;
log_debug ( " reload params1 %s " , t1 - > params ) ;
log_debug ( " reload params2 %s " , t2 - > params ) ;
} else {
log_debug ( " reload %u:%u diff params for type %s " ,
task - > major , task - > minor , t1 - > type ) ;
log_debug ( " reload params1 %s " , t1 - > params ) ;
log_debug ( " reload params2 %s " , t2 - > params ) ;
2019-11-21 01:07:27 +03:00
goto no_match ;
}
}
2018-05-14 14:16:43 +03:00
t1 = t1 - > next ;
t2 = t2 - > next ;
}
if ( ! t1 & & ! t2 ) {
dmt - > dmi . v4 = task - > dmi . v4 ;
task - > dmi . v4 = NULL ;
dm_task_destroy ( task ) ;
return 1 ;
}
no_match :
dm_task_destroy ( task ) ;
/* Now do the original reload */
dmt - > suppress_identical_reload = 0 ;
r = dm_task_run ( dmt ) ;
return r ;
}
static int _check_children_not_suspended_v4 ( struct dm_task * dmt , uint64_t device )
{
struct dm_task * task ;
struct dm_info info ;
struct dm_deps * deps ;
int r = 0 ;
uint32_t i ;
/* Find dependencies */
if ( ! ( task = dm_task_create ( DM_DEVICE_DEPS ) ) )
return 0 ;
/* Copy across or set relevant fields */
if ( device ) {
task - > major = MAJOR ( device ) ;
task - > minor = MINOR ( device ) ;
} else {
if ( dmt - > dev_name & & ! dm_task_set_name ( task , dmt - > dev_name ) )
goto out ;
if ( dmt - > uuid & & ! dm_task_set_uuid ( task , dmt - > uuid ) )
goto out ;
task - > major = dmt - > major ;
task - > minor = dmt - > minor ;
}
task - > uid = dmt - > uid ;
task - > gid = dmt - > gid ;
task - > mode = dmt - > mode ;
/* FIXME: Just for udev_check in dm_task_run. Can we avoid this? */
task - > event_nr = dmt - > event_nr & DM_UDEV_FLAGS_MASK ;
task - > cookie_set = dmt - > cookie_set ;
task - > add_node = dmt - > add_node ;
if ( ! ( r = dm_task_run ( task ) ) )
goto out ;
if ( ! dm_task_get_info ( task , & info ) | | ! info . exists )
goto out ;
/*
* Warn if any of the devices this device depends upon are already
* suspended : I / O could become trapped between the two devices .
*/
if ( info . suspended ) {
if ( ! device )
log_debug_activation ( " Attempting to suspend a device that is already suspended "
" (%u:%u) " , info . major , info . minor ) ;
else
log_error ( INTERNAL_ERROR " Attempt to suspend device %s%s%s%.0d%s%.0d%s%s "
" that uses already-suspended device (%u:%u) " ,
DEV_NAME ( dmt ) ? : " " , DEV_UUID ( dmt ) ? : " " ,
dmt - > major > 0 ? " ( " : " " ,
dmt - > major > 0 ? dmt - > major : 0 ,
dmt - > major > 0 ? " : " : " " ,
dmt - > minor > 0 ? dmt - > minor : 0 ,
dmt - > major > 0 & & dmt - > minor = = 0 ? " 0 " : " " ,
dmt - > major > 0 ? " ) " : " " ,
info . major , info . minor ) ;
/* No need for further recursion */
r = 1 ;
goto out ;
}
if ( ! ( deps = dm_task_get_deps ( task ) ) )
goto out ;
for ( i = 0 ; i < deps - > count ; i + + ) {
/* Only recurse with dm devices */
if ( MAJOR ( deps - > device [ i ] ) ! = _dm_device_major )
continue ;
if ( ! _check_children_not_suspended_v4 ( task , deps - > device [ i ] ) )
goto out ;
}
r = 1 ;
out :
dm_task_destroy ( task ) ;
return r ;
}
static int _suspend_with_validation_v4 ( struct dm_task * dmt )
{
/* Avoid recursion */
dmt - > enable_checks = 0 ;
/*
* Ensure we can ' t leave any I / O trapped between suspended devices .
*/
if ( ! _check_children_not_suspended_v4 ( dmt , 0 ) )
return 0 ;
/* Finally, perform the original suspend. */
return dm_task_run ( dmt ) ;
}
static const char * _sanitise_message ( char * message )
{
const char * sanitised_message = message ? : " " ;
/* FIXME: Check for whitespace variations. */
/* This traps what cryptsetup sends us. */
if ( message & & ! strncasecmp ( message , " key set " , 7 ) )
sanitised_message = " key set " ;
return sanitised_message ;
}
# ifdef DM_IOCTLS
static int _do_dm_ioctl_unmangle_string ( char * str , const char * str_name ,
char * buf , size_t buf_size ,
dm_string_mangling_t mode )
{
int r ;
if ( mode = = DM_STRING_MANGLING_NONE )
return 1 ;
if ( ! check_multiple_mangled_string_allowed ( str , str_name , mode ) )
return_0 ;
if ( ( r = unmangle_string ( str , str_name , strlen ( str ) , buf , buf_size , mode ) ) < 0 ) {
log_debug_activation ( " _do_dm_ioctl_unmangle_string: failed to "
" unmangle %s \" %s \" " , str_name , str ) ;
return 0 ;
}
if ( r )
memcpy ( str , buf , strlen ( buf ) + 1 ) ;
return 1 ;
}
static int _dm_ioctl_unmangle_names ( int type , struct dm_ioctl * dmi )
{
char buf [ DM_NAME_LEN ] ;
2021-12-08 12:45:13 +03:00
char buf_uuid [ DM_UUID_LEN ] ;
struct dm_name_list * names ;
2018-05-14 14:16:43 +03:00
unsigned next = 0 ;
char * name ;
int r = 1 ;
2021-12-08 12:45:13 +03:00
uint32_t * event_nr ;
char * uuid_ptr ;
dm_string_mangling_t mangling_mode = dm_get_name_mangling_mode ( ) ;
2018-05-14 14:16:43 +03:00
if ( ( name = dmi - > name ) )
2021-12-08 12:45:13 +03:00
r & = _do_dm_ioctl_unmangle_string ( name , " name " , buf , sizeof ( buf ) ,
mangling_mode ) ;
2018-05-14 14:16:43 +03:00
if ( type = = DM_DEVICE_LIST & &
2021-12-08 12:45:13 +03:00
( ( names = ( ( struct dm_name_list * ) ( ( char * ) dmi + dmi - > data_start ) ) ) ) & &
2018-05-14 14:16:43 +03:00
names - > dev ) {
do {
2021-12-08 12:45:13 +03:00
names = ( struct dm_name_list * ) ( ( char * ) names + next ) ;
event_nr = _align_ptr ( names - > name + strlen ( names - > name ) + 1 ) ;
r & = _do_dm_ioctl_unmangle_string ( names - > name , " name " ,
buf , sizeof ( buf ) , mangling_mode ) ;
/* Unmangle also UUID within same loop */
if ( _check_has_event_nr ( ) & &
( event_nr [ 1 ] & DM_NAME_LIST_FLAG_HAS_UUID ) ) {
uuid_ptr = _align_ptr ( event_nr + 2 ) ;
r & = _do_dm_ioctl_unmangle_string ( uuid_ptr , " UUID " , buf_uuid ,
sizeof ( buf_uuid ) , mangling_mode ) ;
}
2018-05-14 14:16:43 +03:00
next = names - > next ;
} while ( next ) ;
}
return r ;
}
static int _dm_ioctl_unmangle_uuids ( int type , struct dm_ioctl * dmi )
{
char buf [ DM_UUID_LEN ] ;
char * uuid = dmi - > uuid ;
if ( uuid )
return _do_dm_ioctl_unmangle_string ( uuid , " UUID " , buf , sizeof ( buf ) ,
dm_get_name_mangling_mode ( ) ) ;
return 1 ;
}
# endif
static struct dm_ioctl * _do_dm_ioctl ( struct dm_task * dmt , unsigned command ,
unsigned buffer_repeat_count ,
unsigned retry_repeat_count ,
int * retryable )
{
struct dm_ioctl * dmi ;
int ioctl_with_uevent ;
int r ;
dmt - > ioctl_errno = 0 ;
dmi = _flatten ( dmt , buffer_repeat_count ) ;
if ( ! dmi ) {
log_error ( " Couldn't create ioctl argument. " ) ;
return NULL ;
}
if ( dmt - > type = = DM_DEVICE_TABLE )
dmi - > flags | = DM_STATUS_TABLE_FLAG ;
dmi - > flags | = DM_EXISTS_FLAG ; /* FIXME */
if ( dmt - > no_open_count )
dmi - > flags | = DM_SKIP_BDGET_FLAG ;
ioctl_with_uevent = dmt - > type = = DM_DEVICE_RESUME | |
dmt - > type = = DM_DEVICE_REMOVE | |
dmt - > type = = DM_DEVICE_RENAME ;
if ( ioctl_with_uevent & & dm_cookie_supported ( ) ) {
/*
* Always mark events coming from libdevmapper as
* " primary sourced " . This is needed to distinguish
* any spurious events so we can act appropriately .
* This needs to be applied even when udev_sync is
* not used because udev flags could be used alone .
*/
dmi - > event_nr | = DM_UDEV_PRIMARY_SOURCE_FLAG < <
DM_UDEV_FLAGS_SHIFT ;
/*
* Prevent udev vs . libdevmapper race when processing nodes
* and symlinks . This can happen when the udev rules are
2023-07-02 17:41:34 +03:00
* installed and udev synchronization code is enabled in
2018-05-14 14:16:43 +03:00
* libdevmapper but the software using libdevmapper does not
* make use of it ( by not calling dm_task_set_cookie before ) .
* We need to instruct the udev rules not to be applied at
* all in this situation so we can gracefully fallback to
* libdevmapper ' s node and symlink creation code .
*/
if ( ! dmt - > cookie_set & & dm_udev_get_sync_support ( ) ) {
log_debug_activation ( " Cookie value is not set while trying to call %s "
" ioctl. Please, consider using libdevmapper's udev "
2023-07-02 17:41:34 +03:00
" synchronization interface or disable it explicitly "
2018-05-14 14:16:43 +03:00
" by calling dm_udev_set_sync_support(0). " ,
dmt - > type = = DM_DEVICE_RESUME ? " DM_DEVICE_RESUME " :
dmt - > type = = DM_DEVICE_REMOVE ? " DM_DEVICE_REMOVE " :
" DM_DEVICE_RENAME " ) ;
log_debug_activation ( " Switching off device-mapper and all subsystem related "
" udev rules. Falling back to libdevmapper node creation. " ) ;
/*
* Disable general dm and subsystem rules but keep
* dm disk rules if not flagged out explicitly before .
* We need / dev / disk content for the software that expects it .
*/
dmi - > event_nr | = ( DM_UDEV_DISABLE_DM_RULES_FLAG |
DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG ) < <
DM_UDEV_FLAGS_SHIFT ;
}
}
log_debug_activation ( " dm %s %s%s %s%s%s %s%.0d%s%.0d%s "
2021-09-22 18:30:42 +03:00
" %s[ %s%s%s%s%s%s%s%s%s%s] %.0 " PRIu64 " %s [%u] (*%u) " ,
2018-05-14 14:16:43 +03:00
_cmd_data_v4 [ dmt - > type ] . name ,
dmt - > new_uuid ? " UUID " : " " ,
dmi - > name , dmi - > uuid , dmt - > newname ? " " : " " ,
dmt - > newname ? dmt - > newname : " " ,
dmt - > major > 0 ? " ( " : " " ,
dmt - > major > 0 ? dmt - > major : 0 ,
dmt - > major > 0 ? " : " : " " ,
dmt - > minor > 0 ? dmt - > minor : 0 ,
dmt - > major > 0 & & dmt - > minor = = 0 ? " 0 " : " " ,
dmt - > major > 0 ? " ) " : " " ,
dmt - > no_open_count ? " noopencount " : " opencount " ,
dmt - > no_flush ? " noflush " : " flush " ,
dmt - > read_only ? " readonly " : " " ,
dmt - > skip_lockfs ? " skiplockfs " : " " ,
dmt - > retry_remove ? " retryremove " : " " ,
dmt - > deferred_remove ? " deferredremove " : " " ,
dmt - > secure_data ? " securedata " : " " ,
2021-09-22 18:30:42 +03:00
dmt - > ima_measurement ? " ima_measurement " : " " ,
2018-05-14 14:16:43 +03:00
dmt - > query_inactive_table ? " inactive " : " " ,
dmt - > enable_checks ? " enablechecks " : " " ,
dmt - > sector , _sanitise_message ( dmt - > message ) ,
dmi - > data_size , retry_repeat_count ) ;
# ifdef DM_IOCTLS
r = ioctl ( _control_fd , command , dmi ) ;
if ( dmt - > record_timestamp )
if ( ! dm_timestamp_get ( _dm_ioctl_timestamp ) )
stack ;
if ( r < 0 & & dmt - > expected_errno ! = errno ) {
dmt - > ioctl_errno = errno ;
if ( dmt - > ioctl_errno = = ENXIO & & ( ( dmt - > type = = DM_DEVICE_INFO ) | |
( dmt - > type = = DM_DEVICE_MKNODES ) | |
( dmt - > type = = DM_DEVICE_STATUS ) ) )
dmi - > flags & = ~ DM_EXISTS_FLAG ; /* FIXME */
else {
if ( _log_suppress | | dmt - > ioctl_errno = = EINTR )
log_verbose ( " device-mapper: %s ioctl on %s %s%s%.0d%s%.0d%s%s "
" failed: %s " ,
_cmd_data_v4 [ dmt - > type ] . name ,
dmi - > name , dmi - > uuid ,
dmt - > major > 0 ? " ( " : " " ,
dmt - > major > 0 ? dmt - > major : 0 ,
dmt - > major > 0 ? " : " : " " ,
dmt - > minor > 0 ? dmt - > minor : 0 ,
dmt - > major > 0 & & dmt - > minor = = 0 ? " 0 " : " " ,
dmt - > major > 0 ? " ) " : " " ,
strerror ( dmt - > ioctl_errno ) ) ;
else
log_error ( " device-mapper: %s ioctl on %s %s%s%.0d%s%.0d%s%s "
" failed: %s " ,
_cmd_data_v4 [ dmt - > type ] . name ,
dmi - > name , dmi - > uuid ,
dmt - > major > 0 ? " ( " : " " ,
dmt - > major > 0 ? dmt - > major : 0 ,
dmt - > major > 0 ? " : " : " " ,
dmt - > minor > 0 ? dmt - > minor : 0 ,
dmt - > major > 0 & & dmt - > minor = = 0 ? " 0 " : " " ,
dmt - > major > 0 ? " ) " : " " ,
strerror ( dmt - > ioctl_errno ) ) ;
/*
* It ' s sometimes worth retrying after EBUSY in case
* it ' s a transient failure caused by an asynchronous
* process quickly scanning the device .
*/
* retryable = dmt - > ioctl_errno = = EBUSY ;
goto error ;
}
}
if ( ioctl_with_uevent & & dm_udev_get_sync_support ( ) & &
! _check_uevent_generated ( dmi ) ) {
log_debug_activation ( " Uevent not generated! Calling udev_complete "
" internally to avoid process lock-up. " ) ;
_udev_complete ( dmt ) ;
}
if ( ! _dm_ioctl_unmangle_names ( dmt - > type , dmi ) )
goto error ;
if ( dmt - > type ! = DM_DEVICE_REMOVE & &
! _dm_ioctl_unmangle_uuids ( dmt - > type , dmi ) )
goto error ;
# else /* Userspace alternative for testing */
goto error ;
# endif
return dmi ;
error :
_dm_zfree_dmi ( dmi ) ;
return NULL ;
}
void dm_task_update_nodes ( void )
{
update_devs ( ) ;
}
# define DM_IOCTL_RETRIES 25
# define DM_RETRY_USLEEP_DELAY 200000
int dm_task_get_errno ( struct dm_task * dmt )
{
return dmt - > ioctl_errno ;
}
int dm_task_run ( struct dm_task * dmt )
{
struct dm_ioctl * dmi ;
unsigned command ;
int check_udev ;
int rely_on_udev ;
int suspended_counter ;
unsigned ioctl_retry = 1 ;
int retryable = 0 ;
const char * dev_name = DEV_NAME ( dmt ) ;
const char * dev_uuid = DEV_UUID ( dmt ) ;
if ( ( unsigned ) dmt - > type > = DM_ARRAY_SIZE ( _cmd_data_v4 ) ) {
log_error ( INTERNAL_ERROR " unknown device-mapper task %d " ,
dmt - > type ) ;
return 0 ;
}
command = _cmd_data_v4 [ dmt - > type ] . cmd ;
/* Old-style creation had a table supplied */
if ( dmt - > type = = DM_DEVICE_CREATE & & dmt - > head )
return _create_and_load_v4 ( dmt ) ;
if ( dmt - > type = = DM_DEVICE_MKNODES & & ! dev_name & &
! dev_uuid & & dmt - > major < = 0 )
return _mknodes_v4 ( dmt ) ;
if ( ( dmt - > type = = DM_DEVICE_RELOAD ) & & dmt - > suppress_identical_reload )
return _reload_with_suppression_v4 ( dmt ) ;
if ( ( dmt - > type = = DM_DEVICE_SUSPEND ) & & dmt - > enable_checks )
return _suspend_with_validation_v4 ( dmt ) ;
if ( ! _open_control ( ) ) {
_udev_complete ( dmt ) ;
return_0 ;
}
if ( ( suspended_counter = dm_get_suspended_counter ( ) ) & &
dmt - > type = = DM_DEVICE_RELOAD )
log_error ( INTERNAL_ERROR " Performing unsafe table load while %d device(s) "
" are known to be suspended: "
" %s%s%s %s%.0d%s%.0d%s%s " ,
suspended_counter ,
dev_name ? : " " ,
dev_uuid ? " UUID " : " " ,
dev_uuid ? : " " ,
dmt - > major > 0 ? " ( " : " " ,
dmt - > major > 0 ? dmt - > major : 0 ,
dmt - > major > 0 ? " : " : " " ,
dmt - > minor > 0 ? dmt - > minor : 0 ,
dmt - > major > 0 & & dmt - > minor = = 0 ? " 0 " : " " ,
dmt - > major > 0 ? " ) " : " " ) ;
/* FIXME Detect and warn if cookie set but should not be. */
repeat_ioctl :
if ( ! ( dmi = _do_dm_ioctl ( dmt , command , _ioctl_buffer_double_factor ,
ioctl_retry , & retryable ) ) ) {
/*
* Async udev rules that scan devices commonly cause transient
* failures . Normally you ' d expect the user to have made sure
* nothing was using the device before issuing REMOVE , so it ' s
* worth retrying in case the failure is indeed transient .
*/
if ( retryable & & dmt - > type = = DM_DEVICE_REMOVE & &
dmt - > retry_remove & & + + ioctl_retry < = DM_IOCTL_RETRIES ) {
usleep ( DM_RETRY_USLEEP_DELAY ) ;
goto repeat_ioctl ;
}
_udev_complete ( dmt ) ;
return 0 ;
}
if ( dmi - > flags & DM_BUFFER_FULL_FLAG ) {
switch ( dmt - > type ) {
case DM_DEVICE_LIST_VERSIONS :
case DM_DEVICE_LIST :
case DM_DEVICE_DEPS :
case DM_DEVICE_STATUS :
case DM_DEVICE_TABLE :
case DM_DEVICE_WAITEVENT :
case DM_DEVICE_TARGET_MSG :
_ioctl_buffer_double_factor + + ;
_dm_zfree_dmi ( dmi ) ;
goto repeat_ioctl ;
default :
log_error ( " WARNING: libdevmapper buffer too small for data " ) ;
}
}
/*
* Are we expecting a udev operation to occur that we need to check for ?
*/
check_udev = dmt - > cookie_set & &
! ( dmt - > event_nr > > DM_UDEV_FLAGS_SHIFT &
DM_UDEV_DISABLE_DM_RULES_FLAG ) ;
rely_on_udev = dmt - > cookie_set ? ( dmt - > event_nr > > DM_UDEV_FLAGS_SHIFT &
DM_UDEV_DISABLE_LIBRARY_FALLBACK ) : 0 ;
switch ( dmt - > type ) {
case DM_DEVICE_CREATE :
if ( ( dmt - > add_node = = DM_ADD_NODE_ON_CREATE ) & &
dev_name & & * dev_name & & ! rely_on_udev )
add_dev_node ( dev_name , MAJOR ( dmi - > dev ) ,
MINOR ( dmi - > dev ) , dmt - > uid , dmt - > gid ,
dmt - > mode , check_udev , rely_on_udev ) ;
break ;
case DM_DEVICE_REMOVE :
/* FIXME Kernel needs to fill in dmi->name */
if ( dev_name & & ! rely_on_udev )
rm_dev_node ( dev_name , check_udev , rely_on_udev ) ;
break ;
case DM_DEVICE_RENAME :
/* FIXME Kernel needs to fill in dmi->name */
if ( ! dmt - > new_uuid & & dev_name )
rename_dev_node ( dev_name , dmt - > newname ,
check_udev , rely_on_udev ) ;
break ;
case DM_DEVICE_RESUME :
if ( ( dmt - > add_node = = DM_ADD_NODE_ON_RESUME ) & &
dev_name & & * dev_name )
add_dev_node ( dev_name , MAJOR ( dmi - > dev ) ,
MINOR ( dmi - > dev ) , dmt - > uid , dmt - > gid ,
dmt - > mode , check_udev , rely_on_udev ) ;
/* FIXME Kernel needs to fill in dmi->name */
set_dev_node_read_ahead ( dev_name ,
MAJOR ( dmi - > dev ) , MINOR ( dmi - > dev ) ,
dmt - > read_ahead , dmt - > read_ahead_flags ) ;
break ;
case DM_DEVICE_MKNODES :
if ( dmi - > flags & DM_EXISTS_FLAG )
add_dev_node ( dmi - > name , MAJOR ( dmi - > dev ) ,
MINOR ( dmi - > dev ) , dmt - > uid ,
dmt - > gid , dmt - > mode , 0 , rely_on_udev ) ;
else if ( dev_name )
rm_dev_node ( dev_name , 0 , rely_on_udev ) ;
break ;
case DM_DEVICE_STATUS :
case DM_DEVICE_TABLE :
case DM_DEVICE_WAITEVENT :
if ( ! _unmarshal_status ( dmt , dmi ) )
goto bad ;
break ;
}
/* Was structure reused? */
_dm_zfree_dmi ( dmt - > dmi . v4 ) ;
dmt - > dmi . v4 = dmi ;
return 1 ;
bad :
_dm_zfree_dmi ( dmi ) ;
return 0 ;
}
void dm_hold_control_dev ( int hold_open )
{
_hold_control_fd_open = hold_open ? 1 : 0 ;
log_debug ( " Hold of control device is now %sset. " ,
_hold_control_fd_open ? " " : " un " ) ;
}
void dm_lib_release ( void )
{
if ( ! _hold_control_fd_open )
_close_control_fd ( ) ;
dm_timestamp_destroy ( _dm_ioctl_timestamp ) ;
_dm_ioctl_timestamp = NULL ;
update_devs ( ) ;
}
void dm_pools_check_leaks ( void ) ;
void dm_lib_exit ( void )
{
int suspended_counter ;
static unsigned _exited = 0 ;
if ( _exited + + )
return ;
if ( ( suspended_counter = dm_get_suspended_counter ( ) ) )
log_error ( " libdevmapper exiting with %d device(s) still suspended. " , suspended_counter ) ;
dm_lib_release ( ) ;
selinux_release ( ) ;
if ( _dm_bitset )
dm_bitset_destroy ( _dm_bitset ) ;
_dm_bitset = NULL ;
dm_pools_check_leaks ( ) ;
_version_ok = 1 ;
_version_checked = 0 ;
}