2011-11-11 19:24:48 +04:00
/*
* Copyright ( C ) 2011 Red Hat , Inc . All rights reserved .
*
* This file is part of LVM2 .
*
* 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 ,
2016-01-21 13:49:46 +03:00
* Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
2011-11-11 19:24:48 +04:00
*/
2018-06-08 15:40:53 +03:00
# include "base/memory/zalloc.h"
2018-05-14 12:30:20 +03:00
# include "lib/misc/lib.h"
# include "lib/filters/filter.h"
# include "lib/activate/activate.h"
2020-12-03 19:48:21 +03:00
# include "lib/commands/toolcontext.h"
2014-09-03 17:48:37 +04:00
# ifdef UDEV_SYNC_SUPPORT
# include <libudev.h>
2018-05-14 12:30:20 +03:00
# include "lib/device/dev-ext-udev-constants.h"
2014-09-03 17:48:37 +04:00
# endif
2011-11-11 19:24:48 +04:00
2013-11-13 17:56:29 +04:00
# ifdef __linux__
2013-06-12 14:08:56 +04:00
2011-11-11 19:24:48 +04:00
# include <dirent.h>
# define MPATH_PREFIX "mpath-"
2019-03-13 15:02:09 +03:00
struct mpath_priv {
struct dm_pool * mem ;
struct dev_filter f ;
struct dev_types * dt ;
struct dm_hash_table * hash ;
} ;
2020-12-03 19:48:21 +03:00
/*
* given " /dev/foo " return " foo "
*/
2014-08-18 19:34:01 +04:00
static const char * _get_sysfs_name ( struct device * dev )
2011-11-11 19:24:48 +04:00
{
const char * name ;
2012-03-01 14:30:48 +04:00
if ( ! ( name = strrchr ( dev_name ( dev ) , ' / ' ) ) ) {
log_error ( " Cannot find '/' in device name. " ) ;
2011-11-11 19:24:48 +04:00
return NULL ;
2012-03-01 14:30:48 +04:00
}
2011-11-11 19:24:48 +04:00
name + + ;
2012-03-01 14:30:48 +04:00
if ( ! * name ) {
log_error ( " Device name is not valid. " ) ;
2011-11-11 19:24:48 +04:00
return NULL ;
2012-03-01 14:30:48 +04:00
}
2011-11-11 19:24:48 +04:00
return name ;
}
2020-12-03 19:48:21 +03:00
/*
* given major : minor
* readlink translates / sys / dev / block / major : minor to / sys / . . . / foo
* from / sys / . . . / foo return " foo "
*/
2014-08-18 19:34:01 +04:00
static const char * _get_sysfs_name_by_devt ( const char * sysfs_dir , dev_t devno ,
2014-08-08 12:49:19 +04:00
char * buf , size_t buf_size )
{
const char * name ;
char path [ PATH_MAX ] ;
int size ;
2021-01-20 02:41:23 +03:00
if ( dm_snprintf ( path , sizeof ( path ) , " %sdev/block/%d:%d " , sysfs_dir ,
2014-08-08 12:49:19 +04:00
( int ) MAJOR ( devno ) , ( int ) MINOR ( devno ) ) < 0 ) {
log_error ( " Sysfs path string is too long. " ) ;
return NULL ;
}
if ( ( size = readlink ( path , buf , buf_size - 1 ) ) < 0 ) {
log_sys_error ( " readlink " , path ) ;
return NULL ;
}
buf [ size ] = ' \0 ' ;
if ( ! ( name = strrchr ( buf , ' / ' ) ) ) {
log_error ( " Cannot find device name in sysfs path. " ) ;
return NULL ;
}
name + + ;
return name ;
}
2014-08-18 19:34:01 +04:00
static int _get_sysfs_string ( const char * path , char * buffer , int max_size )
2011-11-11 19:24:48 +04:00
{
FILE * fp ;
int r = 0 ;
2012-03-01 14:30:48 +04:00
if ( ! ( fp = fopen ( path , " r " ) ) ) {
log_sys_error ( " fopen " , path ) ;
return 0 ;
}
2011-11-11 19:24:48 +04:00
if ( ! fgets ( buffer , max_size , fp ) )
2012-03-01 14:30:48 +04:00
log_sys_error ( " fgets " , path ) ;
2011-11-11 19:24:48 +04:00
else
r = 1 ;
if ( fclose ( fp ) )
2012-03-01 14:30:48 +04:00
log_sys_error ( " fclose " , path ) ;
2011-11-11 19:24:48 +04:00
return r ;
}
2020-12-03 19:48:21 +03:00
static int _get_sysfs_dm_mpath ( struct dev_types * dt , const char * sysfs_dir , const char * holder_name )
2011-11-11 19:24:48 +04:00
{
2020-12-03 19:48:21 +03:00
char path [ PATH_MAX ] ;
char buffer [ 128 ] ;
2011-11-11 19:24:48 +04:00
2020-12-03 19:48:21 +03:00
if ( dm_snprintf ( path , sizeof ( path ) , " %sblock/%s/dm/uuid " , sysfs_dir , holder_name ) < 0 ) {
2012-03-01 14:30:48 +04:00
log_error ( " Sysfs path string is too long. " ) ;
return 0 ;
}
2011-11-11 19:24:48 +04:00
2020-12-03 19:48:21 +03:00
buffer [ 0 ] = ' \0 ' ;
2014-08-18 19:34:01 +04:00
if ( ! _get_sysfs_string ( path , buffer , sizeof ( buffer ) ) )
2012-03-01 14:30:48 +04:00
return_0 ;
2011-11-11 19:24:48 +04:00
2020-12-03 19:48:21 +03:00
if ( ! strncmp ( buffer , MPATH_PREFIX , 6 ) )
return 1 ;
2011-11-11 19:24:48 +04:00
2020-12-03 19:48:21 +03:00
return 0 ;
2011-11-11 19:24:48 +04:00
}
2020-12-03 19:48:21 +03:00
static int _get_holder_name ( const char * dir , char * name , int max_size )
2011-11-11 19:24:48 +04:00
{
struct dirent * d ;
DIR * dr ;
int r = 0 ;
2012-03-01 14:30:48 +04:00
if ( ! ( dr = opendir ( dir ) ) ) {
log_sys_error ( " opendir " , dir ) ;
return 0 ;
}
2011-11-11 19:24:48 +04:00
* name = ' \0 ' ;
while ( ( d = readdir ( dr ) ) ) {
if ( ! strcmp ( d - > d_name , " . " ) | | ! strcmp ( d - > d_name , " .. " ) )
continue ;
/* There should be only one holder if it is multipath */
if ( * name ) {
r = 0 ;
break ;
}
strncpy ( name , d - > d_name , max_size ) ;
r = 1 ;
}
if ( closedir ( dr ) )
2012-03-01 14:30:48 +04:00
log_sys_error ( " closedir " , dir ) ;
2011-11-11 19:24:48 +04:00
return r ;
}
2014-09-03 17:48:37 +04:00
# ifdef UDEV_SYNC_SUPPORT
2020-12-03 19:48:21 +03:00
static int _udev_dev_is_mpath_component ( struct device * dev )
2014-09-03 17:48:37 +04:00
{
const char * value ;
struct dev_ext * ext ;
if ( ! ( ext = dev_ext_get ( dev ) ) )
return_0 ;
2015-01-29 18:44:34 +03:00
value = udev_device_get_property_value ( ( struct udev_device * ) ext - > handle , DEV_EXT_UDEV_BLKID_TYPE ) ;
if ( value & & ! strcmp ( value , DEV_EXT_UDEV_BLKID_TYPE_MPATH ) )
2014-09-03 17:48:37 +04:00
return 1 ;
2015-01-29 18:44:34 +03:00
value = udev_device_get_property_value ( ( struct udev_device * ) ext - > handle , DEV_EXT_UDEV_MPATH_DEVICE_PATH ) ;
2014-09-03 17:48:37 +04:00
if ( value & & ! strcmp ( value , " 1 " ) )
return 1 ;
return 0 ;
}
# else
2020-12-03 19:48:21 +03:00
static int _udev_dev_is_mpath_component ( struct device * dev )
2014-09-03 17:48:37 +04:00
{
return 0 ;
}
# endif
2020-12-03 19:48:21 +03:00
static int _native_dev_is_mpath_component ( struct cmd_context * cmd , struct dev_filter * f , struct device * dev )
2011-11-11 19:24:48 +04:00
{
2019-03-13 15:02:09 +03:00
struct mpath_priv * mp = ( struct mpath_priv * ) f - > private ;
struct dev_types * dt = mp - > dt ;
2020-12-03 19:48:21 +03:00
const char * part_name ;
const char * name ; /* e.g. "sda" for "/dev/sda" */
char link_path [ PATH_MAX ] ; /* some obscure, unpredictable sysfs path */
char holders_path [ PATH_MAX ] ; /* e.g. "/sys/block/sda/holders/" */
char dm_dev_path [ PATH_MAX ] ; /* e.g. "/dev/dm-1" */
char holder_name [ 128 ] = { 0 } ; /* e.g. "dm-1" */
2013-06-12 13:38:48 +04:00
const char * sysfs_dir = dm_sysfs_dir ( ) ;
2020-12-03 19:48:21 +03:00
int dev_major = MAJOR ( dev - > dev ) ;
int dev_minor = MINOR ( dev - > dev ) ;
int dm_dev_major ;
int dm_dev_minor ;
struct stat info ;
2013-06-12 14:20:10 +04:00
dev_t primary_dev ;
2019-03-13 15:02:09 +03:00
long look ;
2011-11-11 19:24:48 +04:00
2020-12-03 19:48:21 +03:00
/* Limit this filter to SCSI or NVME devices */
if ( ! major_is_scsi_device ( dt , dev_major ) & & ! dev_is_nvme ( dt , dev ) )
2011-11-11 19:24:48 +04:00
return 0 ;
2013-06-12 14:20:10 +04:00
switch ( dev_get_primary_dev ( dt , dev , & primary_dev ) ) {
2020-12-03 19:48:21 +03:00
2014-08-19 00:04:39 +04:00
case 2 : /* The dev is partition. */
part_name = dev_name ( dev ) ; /* name of original dev for log_debug msg */
2020-12-03 19:48:21 +03:00
/* gets "foo" for "/dev/foo" where "/dev/foo" comes from major:minor */
if ( ! ( name = _get_sysfs_name_by_devt ( sysfs_dir , primary_dev , link_path , sizeof ( link_path ) ) ) )
2014-08-19 00:04:39 +04:00
return_0 ;
2020-12-03 19:48:21 +03:00
2014-08-19 00:04:39 +04:00
log_debug_devs ( " %s: Device is a partition, using primary "
" device %s for mpath component detection " ,
part_name , name ) ;
break ;
2020-12-03 19:48:21 +03:00
2014-08-19 00:04:39 +04:00
case 1 : /* The dev is already a primary dev. Just continue with the dev. */
2020-12-03 19:48:21 +03:00
/* gets "foo" for "/dev/foo" */
2014-08-18 19:34:01 +04:00
if ( ! ( name = _get_sysfs_name ( dev ) ) )
2014-08-19 00:04:39 +04:00
return_0 ;
break ;
2020-12-03 19:48:21 +03:00
2014-08-19 00:04:39 +04:00
default : /* 0, error. */
2020-12-03 19:48:21 +03:00
log_warn ( " Failed to get primary device for %d:%d. " , dev_major , dev_minor ) ;
2014-08-19 00:04:39 +04:00
return 0 ;
2013-06-12 14:20:10 +04:00
}
2020-12-03 19:48:21 +03:00
if ( dm_snprintf ( holders_path , sizeof ( holders_path ) , " %sblock/%s/holders " , sysfs_dir , name ) < 0 ) {
2019-03-13 14:58:13 +03:00
log_warn ( " Sysfs path to check mpath is too long. " ) ;
2012-03-01 14:30:48 +04:00
return 0 ;
}
2011-11-11 19:24:48 +04:00
/* also will filter out partitions */
2020-12-03 19:48:21 +03:00
if ( stat ( holders_path , & info ) )
2011-11-11 19:24:48 +04:00
return 0 ;
2012-03-01 14:30:48 +04:00
if ( ! S_ISDIR ( info . st_mode ) ) {
2020-12-03 19:48:21 +03:00
log_warn ( " Path %s is not a directory. " , holders_path ) ;
2012-03-01 14:30:48 +04:00
return 0 ;
}
2020-12-03 19:48:21 +03:00
/*
* If holders dir contains an entry such as " dm-1 " , then this sets
* holder_name to " dm-1 " .
*
* If holders dir is empty , return 0 ( this is generally where
* devs that are not mpath components return . )
*/
if ( ! _get_holder_name ( holders_path , holder_name , sizeof ( holder_name ) ) )
2011-11-11 19:24:48 +04:00
return 0 ;
2020-12-03 19:48:21 +03:00
if ( dm_snprintf ( dm_dev_path , sizeof ( dm_dev_path ) , " %s/%s " , cmd - > dev_dir , holder_name ) < 0 ) {
log_warn ( " dm device path to check mpath is too long. " ) ;
return 0 ;
}
2011-11-11 19:24:48 +04:00
2020-12-03 19:48:21 +03:00
/*
* stat " /dev/dm-1 " which is the holder of the dev we ' re checking
* dm_dev_major : dm_dev_minor come from stat ( " /dev/dm-1 " )
*/
if ( stat ( dm_dev_path , & info ) ) {
log_debug ( " filter-mpath %s holder %s stat result %d " ,
dev_name ( dev ) , dm_dev_path , errno ) ;
2011-11-11 19:24:48 +04:00
return 0 ;
2020-12-03 19:48:21 +03:00
}
dm_dev_major = ( int ) MAJOR ( info . st_rdev ) ;
dm_dev_minor = ( int ) MINOR ( info . st_rdev ) ;
if ( dm_dev_major ! = dt - > device_mapper_major ) {
log_debug_devs ( " filter-mpath %s holder %s %d:%d does not have dm major " ,
dev_name ( dev ) , dm_dev_path , dm_dev_major , dm_dev_minor ) ;
return 0 ;
}
2011-11-11 19:24:48 +04:00
2020-12-03 19:48:21 +03:00
/*
* Save the result of checking that " /dev/dm-1 " is an mpath device
* to avoid repeating it for each path component .
* The minor number of " /dev/dm-1 " is added to the hash table with
* const value 2 meaning that dm minor 1 ( for / dev / dm - 1 ) is a multipath dev
* and const value 1 meaning that dm minor 1 is not a multipath dev .
*/
look = ( long ) dm_hash_lookup_binary ( mp - > hash , & dm_dev_minor , sizeof ( dm_dev_minor ) ) ;
2019-03-13 15:02:09 +03:00
if ( look > 0 ) {
2020-12-03 19:48:21 +03:00
log_debug_devs ( " filter-mpath %s holder %s %u:%u already checked as %sbeing mpath. " ,
dev_name ( dev ) , holder_name , dm_dev_major , dm_dev_minor , ( look > 1 ) ? " " : " not " ) ;
2019-04-03 14:06:00 +03:00
return ( look > 1 ) ? 1 : 0 ;
2019-03-13 15:02:09 +03:00
}
2020-12-03 19:48:21 +03:00
/*
* Returns 1 if / sys / block / < holder_name > / dm / uuid indicates that
* < holder_name > is a dm device with dm uuid prefix mpath - .
* When true , < holder_name > will be something like " dm-1 " .
*
* ( Is a hash table worth it to avoid reading one sysfs file ? )
*/
if ( _get_sysfs_dm_mpath ( dt , sysfs_dir , holder_name ) ) {
log_debug_devs ( " filter-mpath %s holder %s %u:%u ignore mpath component " ,
dev_name ( dev ) , holder_name , dm_dev_major , dm_dev_minor ) ;
( void ) dm_hash_insert_binary ( mp - > hash , & dm_dev_minor , sizeof ( dm_dev_minor ) , ( void * ) 2 ) ;
2019-03-13 15:02:09 +03:00
return 1 ;
}
2020-12-03 19:48:21 +03:00
( void ) dm_hash_insert_binary ( mp - > hash , & dm_dev_minor , sizeof ( dm_dev_minor ) , ( void * ) 1 ) ;
2019-03-13 15:02:09 +03:00
return 0 ;
2011-11-11 19:24:48 +04:00
}
2020-12-03 19:48:21 +03:00
static int _dev_is_mpath_component ( struct cmd_context * cmd , struct dev_filter * f , struct device * dev )
2014-09-03 17:48:37 +04:00
{
if ( dev - > ext . src = = DEV_EXT_NONE )
2020-12-03 19:48:21 +03:00
return _native_dev_is_mpath_component ( cmd , f , dev ) ;
2014-09-03 17:48:37 +04:00
if ( dev - > ext . src = = DEV_EXT_UDEV )
2020-12-03 19:48:21 +03:00
return _udev_dev_is_mpath_component ( dev ) ;
2014-09-03 17:48:37 +04:00
log_error ( INTERNAL_ERROR " Missing hook for mpath recognition "
" using external device info source %s " , dev_ext_name ( dev ) ) ;
return 0 ;
}
2015-09-03 15:19:48 +03:00
# define MSG_SKIPPING "%s: Skipping mpath component device"
2020-12-03 19:48:21 +03:00
static int _ignore_mpath_component ( struct cmd_context * cmd , struct dev_filter * f , struct device * dev , const char * use_filter_name )
2011-11-11 19:24:48 +04:00
{
2020-07-20 20:48:36 +03:00
dev - > filtered_flags & = ~ DEV_FILTERED_MPATH_COMPONENT ;
2020-12-03 19:48:21 +03:00
if ( _dev_is_mpath_component ( cmd , f , dev ) = = 1 ) {
2015-09-03 15:19:48 +03:00
if ( dev - > ext . src = = DEV_EXT_NONE )
log_debug_devs ( MSG_SKIPPING , dev_name ( dev ) ) ;
else
log_debug_devs ( MSG_SKIPPING " [%s:%p] " , dev_name ( dev ) ,
dev_ext_name ( dev ) , dev - > ext . handle ) ;
2020-07-20 20:48:36 +03:00
dev - > filtered_flags | = DEV_FILTERED_MPATH_COMPONENT ;
2011-11-11 19:24:48 +04:00
return 0 ;
}
return 1 ;
}
static void _destroy ( struct dev_filter * f )
{
2019-03-13 15:02:09 +03:00
struct mpath_priv * mp = ( struct mpath_priv * ) f - > private ;
2011-11-11 19:24:48 +04:00
if ( f - > use_count )
log_error ( INTERNAL_ERROR " Destroying mpath filter while in use %u times. " , f - > use_count ) ;
2019-03-13 15:02:09 +03:00
dm_hash_destroy ( mp - > hash ) ;
dm_pool_destroy ( mp - > mem ) ;
2011-11-11 19:24:48 +04:00
}
2013-06-12 14:08:56 +04:00
struct dev_filter * mpath_filter_create ( struct dev_types * dt )
2011-11-11 19:24:48 +04:00
{
2013-06-12 13:38:48 +04:00
const char * sysfs_dir = dm_sysfs_dir ( ) ;
2019-03-13 15:02:09 +03:00
struct mpath_priv * mp ;
2020-12-03 19:48:21 +03:00
struct dm_pool * mem ;
2019-03-13 15:02:09 +03:00
struct dm_hash_table * hash ;
2011-11-11 19:24:48 +04:00
if ( ! * sysfs_dir ) {
log_verbose ( " No proc filesystem found: skipping multipath filter " ) ;
return NULL ;
}
2021-03-07 17:33:50 +03:00
if ( ! ( hash = dm_hash_create ( 110 ) ) ) {
2019-03-13 15:02:09 +03:00
log_error ( " mpath hash table creation failed. " ) ;
2011-11-11 19:24:48 +04:00
return NULL ;
}
2019-03-13 15:02:09 +03:00
if ( ! ( mem = dm_pool_create ( " mpath " , 256 ) ) ) {
log_error ( " mpath pool creation failed. " ) ;
dm_hash_destroy ( hash ) ;
return NULL ;
}
if ( ! ( mp = dm_pool_zalloc ( mem , sizeof ( * mp ) ) ) ) {
log_error ( " mpath filter allocation failed. " ) ;
goto bad ;
}
2020-12-03 19:48:21 +03:00
mp - > f . passes_filter = _ignore_mpath_component ;
2019-03-13 15:02:09 +03:00
mp - > f . destroy = _destroy ;
mp - > f . use_count = 0 ;
mp - > f . private = mp ;
mp - > f . name = " mpath " ;
mp - > dt = dt ;
2020-12-03 19:48:21 +03:00
mp - > mem = mem ;
2019-03-13 15:02:09 +03:00
mp - > hash = hash ;
2011-11-11 19:24:48 +04:00
2013-08-14 02:26:58 +04:00
log_debug_devs ( " mpath filter initialised. " ) ;
2019-03-13 15:02:09 +03:00
return & mp - > f ;
bad :
dm_pool_destroy ( mem ) ;
dm_hash_destroy ( hash ) ;
return NULL ;
2011-11-11 19:24:48 +04:00
}
# else
2013-06-17 17:17:15 +04:00
struct dev_filter * mpath_filter_create ( struct dev_types * dt )
2011-11-11 19:24:48 +04:00
{
return NULL ;
}
# endif