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"
# include "filter-mpath.h"
# include "activate.h"
# 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-"
static const char * get_sysfs_name ( struct device * dev )
{
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 ;
}
static int get_sysfs_string ( const char * path , char * buffer , int max_size )
{
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 ;
}
static int get_sysfs_get_major_minor ( const char * sysfs_dir , const char * kname , int * major , int * minor )
{
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
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 ;
}
static int get_parent_mpath ( const char * dir , char * name , int max_size )
{
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 ;
}
static int dev_is_mpath ( struct dev_filter * f , struct device * dev )
{
2013-06-12 14:08:56 +04:00
struct dev_types * dt = ( struct dev_types * ) f - > private ;
2011-11-11 19:24:48 +04:00
const char * name ;
char path [ PATH_MAX + 1 ] ;
char parent_name [ PATH_MAX + 1 ] ;
struct stat info ;
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 ) ) {
2013-06-12 16:33:28 +04:00
case 0 :
2013-06-12 14:20:10 +04:00
/* Error. */
log_error ( " Failed to get primary device for %d:%d. " , major , minor ) ;
return 0 ;
2013-06-12 16:33:28 +04:00
case 1 :
2013-06-12 14:20:10 +04:00
/* The dev is already a primary dev. Just continue with the dev. */
break ;
2013-06-12 16:33:28 +04:00
case 2 :
2013-06-12 14:20:10 +04:00
/* The dev is partition. */
name = dev_name ( dev ) ; /* name of original dev for log_debug msg */
/* Get primary dev from cache. */
if ( ! ( dev = dev_cache_get_by_devt ( primary_dev , NULL ) ) ) {
log_error ( " dev_is_mpath: failed to get device for %d:%d " ,
major , minor ) ;
return 0 ;
}
major = ( int ) MAJOR ( primary_dev ) ;
minor = ( int ) MINOR ( primary_dev ) ;
log_debug_devs ( " %s: Device is a partition, using primary "
" device %s for mpath component detection " ,
name , dev_name ( dev ) ) ;
break ;
}
2012-03-01 14:30:48 +04:00
if ( ! ( name = get_sysfs_name ( dev ) ) )
2011-11-11 19:24:48 +04:00
return_0 ;
2012-03-01 14:30:48 +04:00
if ( dm_snprintf ( path , PATH_MAX , " %s/block/%s/holders " , sysfs_dir , name ) < 0 ) {
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 ;
}
2011-11-11 19:24:48 +04:00
if ( ! get_parent_mpath ( path , parent_name , PATH_MAX ) )
return 0 ;
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-06-12 14:08:56 +04:00
if ( major ! = dt - > device_mapper_major ) {
log_error ( " mpath major %d is not dm major %d. " , major ,
dt - > device_mapper_major ) ;
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
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
}
static int _ignore_mpath ( struct dev_filter * f , struct device * dev )
{
if ( dev_is_mpath ( f , dev ) = = 1 ) {
2013-01-08 02:30:29 +04:00
log_debug_devs ( " %s: Skipping mpath component device " , dev_name ( dev ) ) ;
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
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