2004-02-13 17:46:04 +03:00
/*
2007-08-21 00:55:30 +04:00
* Copyright ( C ) 2004 - 2007 Red Hat , Inc . All rights reserved .
2004-02-13 17:46:04 +03:00
*
2004-03-30 23:35:44 +04:00
* 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
2007-08-21 00:55:30 +04:00
* of the GNU Lesser General Public License v .2 .1 .
2004-03-30 23:35:44 +04:00
*
2007-08-21 00:55:30 +04:00
* You should have received a copy of the GNU Lesser General Public License
2004-03-30 23:35:44 +04:00
* along with this program ; if not , write to the Free Software Foundation ,
* Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
2004-02-13 17:46:04 +03:00
*/
# include "lib.h"
2013-08-14 02:26:58 +04:00
# include "filter.h"
2004-02-13 17:46:04 +03:00
2004-03-26 21:54:55 +03:00
# ifdef linux
2004-02-13 17:46:04 +03:00
# include <dirent.h>
2008-09-19 07:42:37 +04:00
static int _locate_sysfs_blocks ( const char * sysfs_dir , char * path , size_t len ,
2007-10-10 15:31:21 +04:00
unsigned * sysfs_depth )
2004-02-13 17:46:04 +03:00
{
2007-10-10 15:31:21 +04:00
struct stat info ;
2004-02-13 17:46:04 +03:00
2007-10-10 15:31:21 +04:00
/*
* unified classification directory for all kernel subsystems
*
* / sys / subsystem / block / devices
* | - - sda - > . . / . . / . . / devices / pci0000 : 00 / 0000 : 00 : 1f .2 / host0 / target0 : 0 : 0 / 0 : 0 : 0 : 0 / block / sda
* | - - sda1 - > . . / . . / . . / devices / pci0000 : 00 / 0000 : 00 : 1f .2 / host0 / target0 : 0 : 0 / 0 : 0 : 0 : 0 / block / sda / sda1
* ` - - sr0 - > . . / . . / . . / devices / pci0000 : 00 / 0000 : 00 : 1f .2 / host1 / target1 : 0 : 0 / 1 : 0 : 0 : 0 / block / sr0
*
*/
2008-09-19 07:42:37 +04:00
if ( dm_snprintf ( path , len , " %s/%s " , sysfs_dir ,
2007-10-10 15:31:21 +04:00
" subsystem/block/devices " ) > = 0 ) {
if ( ! stat ( path , & info ) ) {
* sysfs_depth = 0 ;
return 1 ;
}
}
/*
* block subsystem as a class
*
* / sys / class / block
* | - - sda - > . . / . . / devices / pci0000 : 00 / 0000 : 00 : 1f .2 / host0 / target0 : 0 : 0 / 0 : 0 : 0 : 0 / block / sda
* | - - sda1 - > . . / . . / devices / pci0000 : 00 / 0000 : 00 : 1f .2 / host0 / target0 : 0 : 0 / 0 : 0 : 0 : 0 / block / sda / sda1
* ` - - sr0 - > . . / . . / devices / pci0000 : 00 / 0000 : 00 : 1f .2 / host1 / target1 : 0 : 0 / 1 : 0 : 0 : 0 / block / sr0
*
*/
2008-09-19 07:42:37 +04:00
if ( dm_snprintf ( path , len , " %s/%s " , sysfs_dir , " class/block " ) > = 0 ) {
2007-10-10 15:31:21 +04:00
if ( ! stat ( path , & info ) ) {
* sysfs_depth = 0 ;
return 1 ;
}
}
/*
* old block subsystem layout with nested directories
*
* / sys / block /
* | - - sda
* | | - - capability
* | | - - dev
* . . .
* | | - - sda1
* | | | - - dev
* . . .
* |
* ` - - sr0
* | - - capability
* | - - dev
* . . .
*
*/
2008-09-19 07:42:37 +04:00
if ( dm_snprintf ( path , len , " %s/%s " , sysfs_dir , " block " ) > = 0 ) {
2007-10-10 15:31:21 +04:00
if ( ! stat ( path , & info ) ) {
* sysfs_depth = 1 ;
return 1 ;
}
}
return 0 ;
2004-02-13 17:46:04 +03:00
}
/*----------------------------------------------------------------
* We need to store a set of dev_t .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
struct entry {
struct entry * next ;
dev_t dev ;
} ;
# define SET_BUCKETS 64
struct dev_set {
2005-10-17 03:03:59 +04:00
struct dm_pool * mem ;
2004-02-13 17:46:04 +03:00
const char * sys_block ;
2007-10-10 15:31:21 +04:00
unsigned sysfs_depth ;
2004-02-13 17:46:04 +03:00
int initialised ;
struct entry * slots [ SET_BUCKETS ] ;
} ;
2007-10-10 15:31:21 +04:00
static struct dev_set * _dev_set_create ( struct dm_pool * mem ,
const char * sys_block ,
unsigned sysfs_depth )
2004-02-13 17:46:04 +03:00
{
struct dev_set * ds ;
2005-10-17 03:03:59 +04:00
if ( ! ( ds = dm_pool_zalloc ( mem , sizeof ( * ds ) ) ) )
2004-02-13 17:46:04 +03:00
return NULL ;
ds - > mem = mem ;
2012-02-27 13:56:27 +04:00
if ( ! ( ds - > sys_block = dm_pool_strdup ( mem , sys_block ) ) )
return NULL ;
2007-10-10 15:31:21 +04:00
ds - > sysfs_depth = sysfs_depth ;
2004-02-13 17:46:04 +03:00
ds - > initialised = 0 ;
return ds ;
}
2006-04-19 19:33:07 +04:00
static unsigned _hash_dev ( dev_t dev )
2004-02-13 17:46:04 +03:00
{
return ( major ( dev ) ^ minor ( dev ) ) & ( SET_BUCKETS - 1 ) ;
}
/*
* Doesn ' t check that the set already contains dev .
*/
static int _set_insert ( struct dev_set * ds , dev_t dev )
{
struct entry * e ;
unsigned h = _hash_dev ( dev ) ;
2005-10-17 03:03:59 +04:00
if ( ! ( e = dm_pool_alloc ( ds - > mem , sizeof ( * e ) ) ) )
2004-02-13 17:46:04 +03:00
return 0 ;
e - > next = ds - > slots [ h ] ;
e - > dev = dev ;
ds - > slots [ h ] = e ;
return 1 ;
}
static int _set_lookup ( struct dev_set * ds , dev_t dev )
{
unsigned h = _hash_dev ( dev ) ;
struct entry * e ;
for ( e = ds - > slots [ h ] ; e ; e = e - > next )
if ( e - > dev = = dev )
return 1 ;
return 0 ;
}
/*----------------------------------------------------------------
* filter methods
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static int _parse_dev ( const char * file , FILE * fp , dev_t * result )
{
unsigned major , minor ;
char buffer [ 64 ] ;
if ( ! fgets ( buffer , sizeof ( buffer ) , fp ) ) {
log_error ( " Empty sysfs device file: %s " , file ) ;
return 0 ;
}
if ( sscanf ( buffer , " %u:%u " , & major , & minor ) ! = 2 ) {
log_info ( " sysfs device file not correct format " ) ;
return 0 ;
}
* result = makedev ( major , minor ) ;
return 1 ;
}
static int _read_dev ( const char * file , dev_t * result )
{
int r ;
FILE * fp ;
if ( ! ( fp = fopen ( file , " r " ) ) ) {
log_sys_error ( " fopen " , file ) ;
return 0 ;
}
r = _parse_dev ( file , fp , result ) ;
2007-01-25 17:37:48 +03:00
if ( fclose ( fp ) )
log_sys_error ( " fclose " , file ) ;
2004-02-13 17:46:04 +03:00
return r ;
}
/*
* Recurse through sysfs directories , inserting any devs found .
*/
2007-10-10 15:31:21 +04:00
static int _read_devs ( struct dev_set * ds , const char * dir , unsigned sysfs_depth )
2004-02-13 17:46:04 +03:00
{
2008-01-30 17:00:02 +03:00
struct dirent * d ;
DIR * dr ;
2004-08-18 22:50:21 +04:00
struct stat info ;
2004-02-13 17:46:04 +03:00
char path [ PATH_MAX ] ;
2007-10-10 15:31:21 +04:00
char file [ PATH_MAX ] ;
2005-03-11 01:34:17 +03:00
dev_t dev = { 0 } ;
2004-02-13 17:46:04 +03:00
int r = 1 ;
2008-01-30 17:00:02 +03:00
if ( ! ( dr = opendir ( dir ) ) ) {
log_sys_error ( " opendir " , dir ) ;
return 0 ;
}
2004-02-13 17:46:04 +03:00
2008-01-30 17:00:02 +03:00
while ( ( d = readdir ( dr ) ) ) {
if ( ! strcmp ( d - > d_name , " . " ) | | ! strcmp ( d - > d_name , " .. " ) )
2004-02-13 17:46:04 +03:00
continue ;
2006-08-21 16:54:53 +04:00
if ( dm_snprintf ( path , sizeof ( path ) , " %s/%s " , dir ,
2004-02-13 17:46:04 +03:00
d - > d_name ) < 0 ) {
log_error ( " sysfs path name too long: %s in %s " ,
d - > d_name , dir ) ;
continue ;
}
2007-10-10 15:31:21 +04:00
/* devices have a "dev" file */
if ( dm_snprintf ( file , sizeof ( file ) , " %s/dev " , path ) < 0 ) {
log_error ( " sysfs path name too long: %s in %s " ,
d - > d_name , dir ) ;
continue ;
2004-08-18 22:50:21 +04:00
}
2007-10-10 15:31:21 +04:00
if ( ! stat ( file , & info ) ) {
/* recurse if we found a device and expect subdirs */
if ( sysfs_depth )
_read_devs ( ds , path , sysfs_depth - 1 ) ;
2004-02-13 17:46:04 +03:00
2007-10-10 15:31:21 +04:00
/* add the device we have found */
if ( _read_dev ( file , & dev ) )
_set_insert ( ds , dev ) ;
}
2004-02-13 17:46:04 +03:00
}
2008-01-30 17:00:02 +03:00
if ( closedir ( dr ) )
log_sys_error ( " closedir " , dir ) ;
2004-02-13 17:46:04 +03:00
return r ;
}
static int _init_devs ( struct dev_set * ds )
{
2007-10-10 15:31:21 +04:00
if ( ! _read_devs ( ds , ds - > sys_block , ds - > sysfs_depth ) ) {
2004-02-13 17:46:04 +03:00
ds - > initialised = - 1 ;
return 0 ;
}
ds - > initialised = 1 ;
return 1 ;
}
static int _accept_p ( struct dev_filter * f , struct device * dev )
{
struct dev_set * ds = ( struct dev_set * ) f - > private ;
if ( ! ds - > initialised )
_init_devs ( ds ) ;
/* Pass through if initialisation failed */
if ( ds - > initialised ! = 1 )
return 1 ;
2004-11-24 23:36:52 +03:00
if ( ! _set_lookup ( ds , dev - > dev ) ) {
2013-01-08 02:30:29 +04:00
log_debug_devs ( " %s: Skipping (sysfs) " , dev_name ( dev ) ) ;
2004-11-24 23:36:52 +03:00
return 0 ;
} else
return 1 ;
2004-02-13 17:46:04 +03:00
}
static void _destroy ( struct dev_filter * f )
{
struct dev_set * ds = ( struct dev_set * ) f - > private ;
2010-09-22 05:36:13 +04:00
if ( f - > use_count )
log_error ( INTERNAL_ERROR " Destroying sysfs filter while in use %u times. " , f - > use_count ) ;
2005-10-17 03:03:59 +04:00
dm_pool_destroy ( ds - > mem ) ;
2004-02-13 17:46:04 +03:00
}
2013-06-12 13:38:48 +04:00
struct dev_filter * sysfs_filter_create ( void )
2004-02-13 17:46:04 +03:00
{
2013-06-12 13:38:48 +04:00
const char * sysfs_dir = dm_sysfs_dir ( ) ;
2004-02-13 17:46:04 +03:00
char sys_block [ PATH_MAX ] ;
2007-10-10 15:31:21 +04:00
unsigned sysfs_depth ;
2005-10-17 03:03:59 +04:00
struct dm_pool * mem ;
2004-02-13 17:46:04 +03:00
struct dev_set * ds ;
struct dev_filter * f ;
2008-09-19 07:42:37 +04:00
if ( ! * sysfs_dir ) {
log_verbose ( " No proc filesystem found: skipping sysfs filter " ) ;
return NULL ;
}
if ( ! _locate_sysfs_blocks ( sysfs_dir , sys_block , sizeof ( sys_block ) , & sysfs_depth ) )
2004-02-13 17:46:04 +03:00
return NULL ;
2005-10-17 03:03:59 +04:00
if ( ! ( mem = dm_pool_create ( " sysfs " , 256 ) ) ) {
2004-02-13 17:46:04 +03:00
log_error ( " sysfs pool creation failed " ) ;
return NULL ;
}
2007-10-10 15:31:21 +04:00
if ( ! ( ds = _dev_set_create ( mem , sys_block , sysfs_depth ) ) ) {
2004-02-13 17:46:04 +03:00
log_error ( " sysfs dev_set creation failed " ) ;
goto bad ;
}
2007-04-26 20:44:59 +04:00
if ( ! ( f = dm_pool_zalloc ( mem , sizeof ( * f ) ) ) )
goto_bad ;
2004-02-13 17:46:04 +03:00
f - > passes_filter = _accept_p ;
f - > destroy = _destroy ;
2010-09-22 05:36:13 +04:00
f - > use_count = 0 ;
2004-02-13 17:46:04 +03:00
f - > private = ds ;
2013-08-14 02:26:58 +04:00
log_debug_devs ( " Sysfs filter initialised. " ) ;
2004-02-13 17:46:04 +03:00
return f ;
bad :
2005-10-17 03:03:59 +04:00
dm_pool_destroy ( mem ) ;
2004-02-13 17:46:04 +03:00
return NULL ;
}
2004-03-26 21:54:55 +03:00
# else
2010-07-09 19:34:40 +04:00
struct dev_filter * sysfs_filter_create ( const char * sysfs_dir __attribute__ ( ( unused ) ) )
2004-03-26 21:54:55 +03:00
{
return NULL ;
}
# endif