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 ,
* Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include "lib.h"
2013-08-14 02:26:58 +04:00
# include "filter.h"
2011-11-11 19:24:48 +04:00
# include "activate.h"
2014-09-03 17:48:37 +04:00
# ifdef UDEV_SYNC_SUPPORT
# include <libudev.h>
# 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-"
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 ;
}
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 ;
if ( dm_snprintf ( path , sizeof ( path ) , " %s/dev/block/%d:%d " , sysfs_dir ,
( 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 ;
}
2014-08-18 19:34:01 +04:00
static int _get_sysfs_get_major_minor ( const char * sysfs_dir , const char * kname , int * major , int * minor )
2011-11-11 19:24:48 +04:00
{
char path [ PATH_MAX ] , buffer [ 64 ] ;
2012-03-01 14:30:48 +04:00
if ( dm_snprintf ( path , sizeof ( path ) , " %s/block/%s/dev " , sysfs_dir , kname ) < 0 ) {
log_error ( " Sysfs path string is too long. " ) ;
return 0 ;
}
2011-11-11 19:24:48 +04:00
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
2012-03-01 14:30:48 +04:00
if ( sscanf ( buffer , " %d:%d " , major , minor ) ! = 2 ) {
log_error ( " Failed to parse major minor from %s " , buffer ) ;
2011-11-11 19:24:48 +04:00
return 0 ;
2012-03-01 14:30:48 +04:00
}
2011-11-11 19:24:48 +04:00
return 1 ;
}
2014-08-18 19:34:01 +04:00
static int _get_parent_mpath ( 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
static int _udev_dev_is_mpath ( struct device * dev )
{
const char * value ;
struct dev_ext * ext ;
if ( ! ( ext = dev_ext_get ( dev ) ) )
return_0 ;
value = udev_device_get_property_value ( ( struct udev_device * ) ext - > handle , " ID_FS_TYPE " ) ;
if ( value & & ! strcmp ( value , " mpath_member " ) )
return 1 ;
value = udev_device_get_property_value ( ( struct udev_device * ) ext - > handle , " DM_MULTIPATH_DEVICE_PATH " ) ;
if ( value & & ! strcmp ( value , " 1 " ) )
return 1 ;
return 0 ;
}
# else
static int _udev_dev_is_mpath ( struct device * dev )
{
return 0 ;
}
# endif
static int _native_dev_is_mpath ( struct dev_filter * f , struct device * dev )
2011-11-11 19:24:48 +04:00
{
2013-06-12 14:08:56 +04:00
struct dev_types * dt = ( struct dev_types * ) f - > private ;
2014-08-08 12:49:19 +04:00
const char * part_name , * name ;
2011-11-11 19:24:48 +04:00
struct stat info ;
2014-08-19 13:35:18 +04:00
char path [ PATH_MAX ] , parent_name [ PATH_MAX ] ;
2013-06-12 13:38:48 +04:00
const char * sysfs_dir = dm_sysfs_dir ( ) ;
2013-06-12 14:08:56 +04:00
int major = MAJOR ( dev - > dev ) ;
int minor = MINOR ( dev - > dev ) ;
2013-06-12 14:20:10 +04:00
dev_t primary_dev ;
2011-11-11 19:24:48 +04:00
/* Limit this filter only to SCSI devices */
2013-06-12 14:08:56 +04:00
if ( ! major_is_scsi_device ( dt , MAJOR ( dev - > 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 ) ) {
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 */
2014-08-18 19:34:01 +04:00
if ( ! ( name = _get_sysfs_name_by_devt ( sysfs_dir , primary_dev , parent_name , sizeof ( parent_name ) ) ) )
2014-08-19 00:04:39 +04:00
return_0 ;
log_debug_devs ( " %s: Device is a partition, using primary "
" device %s for mpath component detection " ,
part_name , name ) ;
break ;
case 1 : /* The dev is already a primary dev. Just continue with the dev. */
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 ;
default : /* 0, error. */
log_error ( " Failed to get primary device for %d:%d. " , major , minor ) ;
return 0 ;
2013-06-12 14:20:10 +04:00
}
2014-08-19 13:35:18 +04:00
if ( dm_snprintf ( path , sizeof ( path ) , " %s/block/%s/holders " , sysfs_dir , name ) < 0 ) {
2012-03-01 14:30:48 +04:00
log_error ( " Sysfs path to check mpath is too long. " ) ;
return 0 ;
}
2011-11-11 19:24:48 +04:00
/* also will filter out partitions */
2012-03-01 14:30:48 +04:00
if ( stat ( 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 ) ) {
log_error ( " Path %s is not a directory. " , path ) ;
return 0 ;
}
2014-08-18 19:34:01 +04:00
if ( ! _get_parent_mpath ( path , parent_name , sizeof ( parent_name ) ) )
2011-11-11 19:24:48 +04:00
return 0 ;
2014-08-18 19:34:01 +04:00
if ( ! _get_sysfs_get_major_minor ( sysfs_dir , parent_name , & major , & minor ) )
2012-03-01 14:30:48 +04:00
return_0 ;
2011-11-11 19:24:48 +04:00
2013-08-21 16:07:01 +04:00
if ( major ! = dt - > device_mapper_major )
2011-11-11 19:24:48 +04:00
return 0 ;
2011-11-11 20:41:37 +04:00
return lvm_dm_prefix_check ( major , minor , MPATH_PREFIX ) ;
2011-11-11 19:24:48 +04:00
}
2014-09-03 17:48:37 +04:00
static int _dev_is_mpath ( struct dev_filter * f , struct device * dev )
{
if ( dev - > ext . src = = DEV_EXT_NONE )
return _native_dev_is_mpath ( f , dev ) ;
if ( dev - > ext . src = = DEV_EXT_UDEV )
return _udev_dev_is_mpath ( dev ) ;
log_error ( INTERNAL_ERROR " Missing hook for mpath recognition "
" using external device info source %s " , dev_ext_name ( dev ) ) ;
return 0 ;
}
2011-11-11 19:24:48 +04:00
static int _ignore_mpath ( struct dev_filter * f , struct device * dev )
{
2014-08-18 19:34:01 +04:00
if ( _dev_is_mpath ( f , dev ) = = 1 ) {
2014-09-03 17:48:37 +04:00
log_debug_devs ( " %s: Skipping mpath component device [%s:%p] " ,
dev_name ( dev ) , dev_ext_name ( dev ) , dev - > ext . handle ) ;
2011-11-11 19:24:48 +04:00
return 0 ;
}
return 1 ;
}
static void _destroy ( struct dev_filter * f )
{
if ( f - > use_count )
log_error ( INTERNAL_ERROR " Destroying mpath filter while in use %u times. " , f - > use_count ) ;
dm_free ( f ) ;
}
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 ( ) ;
2011-11-11 19:24:48 +04:00
struct dev_filter * f ;
if ( ! * sysfs_dir ) {
log_verbose ( " No proc filesystem found: skipping multipath filter " ) ;
return NULL ;
}
2012-08-18 20:59:07 +04:00
if ( ! ( f = dm_zalloc ( sizeof ( * f ) ) ) ) {
2011-11-11 19:24:48 +04:00
log_error ( " mpath filter allocation failed " ) ;
return NULL ;
}
f - > passes_filter = _ignore_mpath ;
f - > destroy = _destroy ;
f - > use_count = 0 ;
2013-06-12 14:08:56 +04:00
f - > private = dt ;
2011-11-11 19:24:48 +04:00
2013-08-14 02:26:58 +04:00
log_debug_devs ( " mpath filter initialised. " ) ;
2011-11-11 19:24:48 +04:00
return f ;
}
# 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