2018-03-26 14:48:07 +02:00
/*
* virdevmapper . c : Functions for handling device mapper
*
* Copyright ( C ) 2018 Red Hat , Inc .
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2.1 of the License , or ( at your option ) any later version .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library . If not , see
* < http : //www.gnu.org/licenses/>.
*/
# include <config.h>
2020-07-23 16:02:00 +02:00
# include "virdevmapper.h"
# include "internal.h"
2020-01-24 17:32:48 +00:00
# ifdef __linux__
2018-03-26 14:48:07 +02:00
# include <sys / sysmacros.h>
2020-07-23 16:02:00 +02:00
# include <linux / dm-ioctl.h>
# include <sys / ioctl.h>
# include <sys / types.h>
# include <sys / stat.h>
# include <fcntl.h>
2018-03-26 14:48:07 +02:00
2020-07-23 16:02:00 +02:00
# include "virthread.h"
# include "viralloc.h"
# include "virstring.h"
# include "virfile.h"
2020-08-19 13:35:55 +02:00
# include "virlog.h"
2021-09-20 14:30:59 +02:00
# include "virglibutil.h"
2020-07-23 16:02:00 +02:00
# define VIR_FROM_THIS VIR_FROM_STORAGE
2020-08-19 13:35:55 +02:00
VIR_LOG_INIT ( " util.virdevmapper " ) ;
2020-07-23 16:02:00 +02:00
# define PROC_DEVICES " / proc / devices"
2021-03-29 15:03:50 +02:00
# define PROC_DEVICES_BUF_SIZE (16 * 1024)
2020-07-23 16:02:00 +02:00
# define DM_NAME "device-mapper"
# define DEV_DM_DIR " / dev / " DM_DIR
# define CONTROL_PATH DEV_DM_DIR " / " DM_CONTROL_NODE
2021-03-29 15:03:50 +02:00
# define VIR_DEVMAPPER_IOCTL_BUF_SIZE_INCREMENT (16 * 1024)
G_STATIC_ASSERT ( VIR_DEVMAPPER_IOCTL_BUF_SIZE_INCREMENT > sizeof ( struct dm_ioctl ) ) ;
2020-07-23 16:02:00 +02:00
2018-03-26 14:48:07 +02:00
static int
2020-08-18 11:08:15 +02:00
virDevMapperGetMajor ( unsigned int * major )
2018-03-26 14:48:07 +02:00
{
2020-07-23 16:02:00 +02:00
g_autofree char * buf = NULL ;
2020-12-01 09:21:32 +01:00
g_auto ( GStrv ) lines = NULL ;
2020-07-23 16:02:00 +02:00
size_t i ;
2020-08-18 11:04:24 +02:00
if ( ! virFileExists ( CONTROL_PATH ) )
return - 2 ;
2021-03-29 15:03:50 +02:00
if ( virFileReadAll ( PROC_DEVICES , PROC_DEVICES_BUF_SIZE , & buf ) < 0 )
2020-07-23 16:02:00 +02:00
return - 1 ;
2021-02-05 18:35:07 +01:00
lines = g_strsplit ( buf , " \n " , 0 ) ;
2020-07-23 16:02:00 +02:00
if ( ! lines )
return - 1 ;
for ( i = 0 ; lines [ i ] ; i + + ) {
g_autofree char * dev = NULL ;
unsigned int maj ;
if ( sscanf ( lines [ i ] , " %u %ms \n " , & maj , & dev ) = = 2 & &
STREQ ( dev , DM_NAME ) ) {
2020-08-18 11:08:15 +02:00
* major = maj ;
2020-07-23 16:02:00 +02:00
break ;
}
}
if ( ! lines [ i ] ) {
virReportError ( VIR_ERR_INTERNAL_ERROR ,
2023-03-09 15:11:50 +01:00
_ ( " Unable to find major for %1$s " ) ,
2020-07-23 16:02:00 +02:00
DM_NAME ) ;
return - 1 ;
}
2018-03-26 14:48:07 +02:00
return 0 ;
}
2020-07-23 16:02:00 +02:00
static void *
virDMIoctl ( int controlFD , int cmd , struct dm_ioctl * dm , char * * buf )
{
2021-03-29 15:03:50 +02:00
size_t bufsize = VIR_DEVMAPPER_IOCTL_BUF_SIZE_INCREMENT ;
2020-07-23 16:02:00 +02:00
reread :
* buf = g_new0 ( char , bufsize ) ;
dm - > version [ 0 ] = DM_VERSION_MAJOR ;
dm - > version [ 1 ] = 0 ;
dm - > version [ 2 ] = 0 ;
dm - > data_size = bufsize ;
dm - > data_start = sizeof ( struct dm_ioctl ) ;
memcpy ( * buf , dm , sizeof ( struct dm_ioctl ) ) ;
if ( ioctl ( controlFD , cmd , * buf ) < 0 ) {
VIR_FREE ( * buf ) ;
return NULL ;
}
memcpy ( dm , * buf , sizeof ( struct dm_ioctl ) ) ;
if ( dm - > flags & DM_BUFFER_FULL_FLAG ) {
2021-03-29 15:03:50 +02:00
bufsize + = VIR_DEVMAPPER_IOCTL_BUF_SIZE_INCREMENT ;
2020-07-23 16:02:00 +02:00
VIR_FREE ( * buf ) ;
goto reread ;
}
return * buf + dm - > data_start ;
}
2018-03-26 14:48:07 +02:00
static int
2020-07-23 16:02:00 +02:00
virDMOpen ( void )
{
VIR_AUTOCLOSE controlFD = - 1 ;
2023-08-02 10:09:52 +02:00
struct dm_ioctl dm = { 0 } ;
2020-07-23 16:02:00 +02:00
g_autofree char * tmp = NULL ;
int ret ;
2020-08-18 11:04:24 +02:00
if ( ( controlFD = open ( CONTROL_PATH , O_RDWR ) ) < 0 ) {
2020-08-19 13:35:55 +02:00
/* We can't talk to devmapper. Produce a warning and let
* the caller decide what to do next . */
if ( errno = = ENOENT ) {
VIR_DEBUG ( " device mapper not available " ) ;
} else {
VIR_WARN ( " unable to open %s: %s " ,
CONTROL_PATH , g_strerror ( errno ) ) ;
}
return - 2 ;
2020-08-18 11:04:24 +02:00
}
2020-07-23 16:02:00 +02:00
if ( ! virDMIoctl ( controlFD , DM_VERSION , & dm , & tmp ) ) {
virReportSystemError ( errno , " %s " ,
_ ( " Unable to get device-mapper version " ) ) ;
return - 1 ;
}
if ( dm . version [ 0 ] ! = DM_VERSION_MAJOR ) {
virReportError ( VIR_ERR_OPERATION_UNSUPPORTED ,
2023-03-09 15:11:50 +01:00
_ ( " Unsupported device-mapper version. Expected %1$d got %2$d " ) ,
2020-07-23 16:02:00 +02:00
DM_VERSION_MAJOR , dm . version [ 0 ] ) ;
return - 1 ;
}
ret = controlFD ;
controlFD = - 1 ;
return ret ;
}
static char *
virDMSanitizepath ( const char * path )
{
g_autofree char * dmDirPath = NULL ;
struct dirent * ent = NULL ;
struct stat sb [ 2 ] ;
2020-10-25 17:50:51 -04:00
g_autoptr ( DIR ) dh = NULL ;
2020-07-23 16:02:00 +02:00
const char * p ;
/* If a path is NOT provided then assume it's DM name */
p = strrchr ( path , ' / ' ) ;
if ( ! p )
return g_strdup ( path ) ;
else
p + + ;
/* It's a path. Check if the last component is DM name */
if ( stat ( path , & sb [ 0 ] ) < 0 ) {
virReportError ( errno ,
2023-03-09 15:11:50 +01:00
_ ( " Unable to stat %1$p " ) ,
2020-07-23 16:02:00 +02:00
path ) ;
return NULL ;
}
dmDirPath = g_strdup_printf ( DEV_DM_DIR " /%s " , p ) ;
if ( stat ( dmDirPath , & sb [ 1 ] ) = = 0 & &
sb [ 0 ] . st_rdev = = sb [ 1 ] . st_rdev ) {
return g_strdup ( p ) ;
}
/* The last component of @path wasn't DM name. Let's check if
* there ' s a device under / dev / mapper / with the same rdev . */
if ( virDirOpen ( & dh , DEV_DM_DIR ) < 0 )
return NULL ;
2020-08-03 13:36:39 +02:00
while ( virDirRead ( dh , & ent , DEV_DM_DIR ) > 0 ) {
2020-07-23 16:02:00 +02:00
g_autofree char * tmp = g_strdup_printf ( DEV_DM_DIR " /%s " , ent - > d_name ) ;
if ( stat ( tmp , & sb [ 1 ] ) = = 0 & &
2020-11-13 10:45:30 +01:00
sb [ 0 ] . st_rdev = = sb [ 1 ] . st_rdev ) {
2020-10-27 17:49:11 -04:00
return g_steal_pointer ( & tmp ) ;
2020-07-23 16:02:00 +02:00
}
}
2020-10-27 17:49:11 -04:00
return NULL ;
2020-07-23 16:02:00 +02:00
}
static int
virDevMapperGetTargetsImpl ( int controlFD ,
const char * path ,
2021-09-20 14:30:59 +02:00
GSList * * devPaths ,
2018-03-26 14:48:07 +02:00
unsigned int ttl )
{
2020-07-23 16:02:00 +02:00
g_autofree char * sanitizedPath = NULL ;
g_autofree char * buf = NULL ;
2023-08-02 10:09:52 +02:00
struct dm_ioctl dm = { 0 } ;
2020-07-23 16:02:00 +02:00
struct dm_target_deps * deps = NULL ;
2018-03-26 14:48:07 +02:00
size_t i ;
if ( ttl = = 0 ) {
errno = ELOOP ;
2020-07-23 16:02:00 +02:00
return - 1 ;
2018-03-26 14:48:07 +02:00
}
2020-06-11 12:14:33 +02:00
if ( ! virIsDevMapperDevice ( path ) )
2020-04-24 13:17:51 +02:00
return 0 ;
2020-07-23 16:02:00 +02:00
if ( ! ( sanitizedPath = virDMSanitizepath ( path ) ) )
return 0 ;
2018-03-26 14:48:07 +02:00
2021-03-06 11:47:59 +01:00
if ( virStrcpy ( dm . name , sanitizedPath , DM_NAME_LEN ) < 0 ) {
2020-07-23 16:02:00 +02:00
virReportError ( VIR_ERR_OPERATION_UNSUPPORTED , " %s " ,
_ ( " Resolved device mapper name too long " ) ) ;
return - 1 ;
2018-04-10 08:00:59 +02:00
}
2018-03-26 14:48:07 +02:00
2020-07-23 16:02:00 +02:00
deps = virDMIoctl ( controlFD , DM_TABLE_DEPS , & dm , & buf ) ;
if ( ! deps ) {
if ( errno = = ENXIO )
return 0 ;
2018-03-26 14:48:07 +02:00
2020-07-23 16:02:00 +02:00
virReportSystemError ( errno ,
2023-03-09 15:11:50 +01:00
_ ( " Unable to query dependencies for %1$s " ) ,
2020-07-23 16:02:00 +02:00
path ) ;
return - 1 ;
2018-03-26 14:48:07 +02:00
}
for ( i = 0 ; i < deps - > count ; i + + ) {
2021-09-20 14:30:59 +02:00
char * curpath = g_strdup_printf ( " /dev/block/%u:%u " ,
major ( deps - > dev [ i ] ) ,
minor ( deps - > dev [ i ] ) ) ;
2018-03-26 14:48:07 +02:00
2021-09-20 14:30:59 +02:00
* devPaths = g_slist_prepend ( * devPaths , curpath ) ;
2018-03-26 14:48:07 +02:00
2021-09-20 14:30:59 +02:00
if ( virDevMapperGetTargetsImpl ( controlFD , curpath , devPaths , ttl - 1 ) < 0 )
2020-07-23 16:02:00 +02:00
return - 1 ;
2018-03-26 14:48:07 +02:00
}
2020-07-23 16:02:00 +02:00
return 0 ;
2018-03-26 14:48:07 +02:00
}
/**
* virDevMapperGetTargets :
* @ path : devmapper target
2021-09-20 14:30:59 +02:00
* @ devPaths : filled in by a GSList containing the paths
2018-03-26 14:48:07 +02:00
*
* For given @ path figure out its targets , and store them in
2021-09-20 14:30:59 +02:00
* @ devPaths .
2018-03-26 14:48:07 +02:00
*
* If @ path is not a devmapper device , @ devPaths is set to NULL and
* success is returned .
*
* If @ path consists of yet another devmapper targets these are
* consulted recursively .
*
* Returns 0 on success ,
* - 1 otherwise ( with errno set , no libvirt error is
* reported )
*/
int
virDevMapperGetTargets ( const char * path ,
2021-09-20 14:30:59 +02:00
GSList * * devPaths )
2018-03-26 14:48:07 +02:00
{
2020-07-23 16:02:00 +02:00
VIR_AUTOCLOSE controlFD = - 1 ;
2018-03-26 14:48:07 +02:00
const unsigned int ttl = 32 ;
2021-09-20 14:30:59 +02:00
g_autoptr ( virGSListString ) paths = NULL ;
2018-03-26 14:48:07 +02:00
/* Arbitrary limit on recursion level. A devmapper target can
* consist of devices or yet another targets . If that ' s the
* case , we have to stop recursion somewhere . */
2020-08-18 11:04:24 +02:00
if ( ( controlFD = virDMOpen ( ) ) < 0 ) {
if ( controlFD = = - 2 ) {
2020-08-19 13:35:55 +02:00
/* The CONTROL_PATH doesn't exist or is unusable.
* Probably the module isn ' t loaded , yet . Don ' t error
* out , just exit . */
2020-08-18 11:04:24 +02:00
return 0 ;
}
2020-07-23 16:02:00 +02:00
return - 1 ;
2020-08-18 11:04:24 +02:00
}
2020-07-23 16:02:00 +02:00
2021-09-20 14:30:59 +02:00
if ( virDevMapperGetTargetsImpl ( controlFD , path , & paths , ttl ) < 0 )
return - 1 ;
* devPaths = g_slist_reverse ( g_steal_pointer ( & paths ) ) ;
return 0 ;
2018-03-26 14:48:07 +02:00
}
2020-06-11 11:57:14 +02:00
bool
virIsDevMapperDevice ( const char * dev_name )
{
struct stat buf ;
2020-08-18 11:08:15 +02:00
unsigned int major ;
2020-06-11 11:57:14 +02:00
2020-08-18 11:08:15 +02:00
if ( virDevMapperGetMajor ( & major ) < 0 )
2020-07-23 16:02:00 +02:00
return false ;
2020-06-11 11:57:14 +02:00
if ( ! stat ( dev_name , & buf ) & &
S_ISBLK ( buf . st_mode ) & &
2020-08-18 11:08:15 +02:00
major ( buf . st_rdev ) = = major )
2020-07-23 16:02:00 +02:00
return true ;
2020-06-11 11:57:14 +02:00
return false ;
}
2020-07-23 16:02:00 +02:00
# else /* !defined(__linux__) */
2020-06-11 11:57:14 +02:00
2020-07-23 16:30:56 +02:00
int
virDevMapperGetTargets ( const char * path G_GNUC_UNUSED ,
2021-09-21 14:30:19 +02:00
GSList * * devPaths G_GNUC_UNUSED )
2020-07-23 16:30:56 +02:00
{
errno = ENOSYS ;
return - 1 ;
}
2020-06-11 11:57:14 +02:00
bool
virIsDevMapperDevice ( const char * dev_name G_GNUC_UNUSED )
{
return false ;
}
2020-07-23 16:02:00 +02:00
# endif /* ! defined(__linux__) */