2001-10-22 18:14:00 +04:00
/*
2008-01-30 17:00:02 +03:00
* Copyright ( C ) 2001 - 2004 Sistina Software , Inc . All rights reserved .
2007-08-21 00:55:30 +04:00
* Copyright ( C ) 2004 - 2007 Red Hat , Inc . All rights reserved .
2001-10-22 18:14:00 +04: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 ,
2016-01-21 13:49:46 +03:00
* Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
2001-10-22 18:14:00 +04:00
*/
2002-11-18 17:01:16 +03:00
# include "lib.h"
2013-08-14 02:26:58 +04:00
# include "filter.h"
2013-06-12 14:08:56 +04:00
# include "config.h"
2006-11-04 06:34:10 +03:00
# include "lvm-file.h"
2001-10-22 18:14:00 +04:00
struct pfilter {
char * file ;
2005-10-17 03:03:59 +04:00
struct dm_hash_table * devices ;
2001-10-23 16:24:55 +04:00
struct dev_filter * real ;
2015-03-18 12:59:41 +03:00
struct timespec ctime ;
2013-06-12 14:08:56 +04:00
struct dev_types * dt ;
2001-10-22 18:14:00 +04:00
} ;
2018-05-04 01:12:07 +03:00
/*
* The persistent filter is filter layer that sits above the other filters and
* caches the final result of those other filters . When a device is first
* checked against filters , it will not be in this cache , so this filter will
* pass the device down to the other filters to check it . The other filters
* will run and either include the device ( good / pass ) or exclude the device
* ( bad / fail ) . That good or bad result propagates up through this filter which
* saves the result . The next time some code checks the filters against the
* device , this persistent / cache filter is checked first . This filter finds
* the previous result in its cache and returns it without reevaluating the
* other real filters .
*
* FIXME : a cache like this should not be needed . The fact it ' s needed is a
* symptom of code that should be fixed to not reevaluate filters multiple
* times . A device should be checked against the filter once , and then not
* need to be checked again . With scanning now controlled , we could probably
* do this .
*
* FIXME : " persistent " isn ' t a great name for this caching filter . This filter
* at one time saved its cache results to a file , which is how it got the name .
* That . cache file does not work well , causes problems , and is no longer used
* by default . The old code for it should be removed .
*/
2001-10-22 18:14:00 +04:00
/*
2006-11-04 06:34:10 +03:00
* The hash table holds one of these two states
* against each entry .
2001-10-22 18:14:00 +04:00
*/
2001-10-23 16:24:55 +04:00
# define PF_BAD_DEVICE ((void *) 1)
# define PF_GOOD_DEVICE ((void *) 2)
2001-10-22 18:14:00 +04:00
2001-10-22 18:39:12 +04:00
static int _init_hash ( struct pfilter * pf )
2001-10-22 18:14:00 +04:00
{
if ( pf - > devices )
2005-10-17 03:03:59 +04:00
dm_hash_destroy ( pf - > devices ) ;
2001-10-22 18:14:00 +04:00
2008-01-30 16:19:47 +03:00
if ( ! ( pf - > devices = dm_hash_create ( 128 ) ) )
return_0 ;
2003-01-08 19:41:22 +03:00
return 1 ;
2001-10-22 18:14:00 +04:00
}
2012-08-13 21:44:10 +04:00
static void _persistent_filter_wipe ( struct dev_filter * f )
2001-10-22 18:14:00 +04:00
{
2001-10-23 16:24:55 +04:00
struct pfilter * pf = ( struct pfilter * ) f - > private ;
2001-10-22 18:14:00 +04:00
2005-10-17 03:03:59 +04:00
dm_hash_wipe ( pf - > devices ) ;
2001-10-23 16:24:55 +04:00
}
2001-10-22 18:14:00 +04:00
2011-08-30 18:55:15 +04:00
static int _read_array ( struct pfilter * pf , struct dm_config_tree * cft ,
2001-10-23 16:24:55 +04:00
const char * path , void * data )
{
2011-08-30 18:55:15 +04:00
const struct dm_config_node * cn ;
const struct dm_config_value * cv ;
2001-10-22 18:14:00 +04:00
2011-08-30 18:55:15 +04:00
if ( ! ( cn = dm_config_find_node ( cft - > root , path ) ) ) {
2001-10-24 21:53:50 +04:00
log_very_verbose ( " Couldn't find %s array in '%s' " ,
2002-04-24 22:20:51 +04:00
path , pf - > file ) ;
2001-10-23 16:24:55 +04:00
return 0 ;
2001-10-22 18:14:00 +04:00
}
/*
* iterate through the array , adding
* devices as we go .
*/
for ( cv = cn - > v ; cv ; cv = cv - > next ) {
2011-08-30 18:55:15 +04:00
if ( cv - > type ! = DM_CFG_STRING ) {
2001-10-24 21:53:50 +04:00
log_verbose ( " Devices array contains a value "
2002-04-24 22:20:51 +04:00
" which is not a string ... ignoring " ) ;
2001-10-22 18:14:00 +04:00
continue ;
}
2005-10-17 03:03:59 +04:00
if ( ! dm_hash_insert ( pf - > devices , cv - > v . str , data ) )
2001-10-23 22:20:27 +04:00
log_verbose ( " Couldn't add '%s' to filter ... ignoring " ,
2002-04-24 22:20:51 +04:00
cv - > v . str ) ;
2002-11-18 17:01:16 +03:00
/* Populate dev_cache ourselves */
dev_cache_get ( cv - > v . str , NULL ) ;
2001-10-22 18:14:00 +04:00
}
2001-10-23 16:24:55 +04:00
return 1 ;
}
2018-04-20 18:43:50 +03:00
int persistent_filter_load ( struct dev_filter * f , struct dm_config_tree * * cft_out )
2001-10-23 16:24:55 +04:00
{
struct pfilter * pf = ( struct pfilter * ) f - > private ;
2011-08-30 18:55:15 +04:00
struct dm_config_tree * cft ;
2008-01-30 17:00:02 +03:00
struct stat info ;
2006-11-04 06:34:10 +03:00
int r = 0 ;
2001-10-23 16:24:55 +04:00
2011-04-22 16:05:32 +04:00
if ( obtain_device_list_from_udev ( ) ) {
if ( ! stat ( pf - > file , & info ) ) {
2013-04-23 13:58:50 +04:00
log_very_verbose ( " Obtaining device list from udev. "
" Removing obsolete %s. " ,
2011-04-22 16:05:32 +04:00
pf - > file ) ;
2011-05-12 16:42:47 +04:00
if ( unlink ( pf - > file ) < 0 & & errno ! = EROFS )
2011-04-22 16:05:32 +04:00
log_sys_error ( " unlink " , pf - > file ) ;
}
return 1 ;
}
2008-01-30 17:00:02 +03:00
if ( ! stat ( pf - > file , & info ) )
2015-03-18 12:59:41 +03:00
lvm_stat_ctim ( & pf - > ctime , & info ) ;
2006-11-04 06:34:10 +03:00
else {
2008-01-30 17:00:02 +03:00
log_very_verbose ( " %s: stat failed: %s " , pf - > file ,
2006-11-04 06:34:10 +03:00
strerror ( errno ) ) ;
return_0 ;
2001-10-23 16:24:55 +04:00
}
2014-05-19 12:58:28 +04:00
if ( ! ( cft = config_open ( CONFIG_FILE_SPECIAL , pf - > file , 1 ) ) )
2006-11-04 06:34:10 +03:00
return_0 ;
2018-04-20 18:43:50 +03:00
if ( ! config_file_read ( cft ) )
2006-11-04 06:34:10 +03:00
goto_out ;
2001-10-23 16:24:55 +04:00
2017-11-13 17:43:32 +03:00
log_debug_devs ( " Loading persistent filter cache from %s " , pf - > file ) ;
2004-03-08 21:28:45 +03:00
_read_array ( pf , cft , " persistent_filter_cache/valid_devices " ,
2001-10-25 18:41:28 +04:00
PF_GOOD_DEVICE ) ;
2002-11-18 17:01:16 +03:00
/* We don't gain anything by holding invalid devices */
2004-03-08 21:28:45 +03:00
/* _read_array(pf, cft, "persistent_filter_cache/invalid_devices",
2002-11-18 17:01:16 +03:00
PF_BAD_DEVICE ) ; */
2003-01-07 00:09:04 +03:00
log_very_verbose ( " Loaded persistent filter cache from %s " , pf - > file ) ;
2002-04-24 22:20:51 +04:00
out :
2006-11-04 06:34:10 +03:00
if ( r & & cft_out )
* cft_out = cft ;
else
2013-06-25 14:25:43 +04:00
config_destroy ( cft ) ;
2001-10-22 18:14:00 +04:00
return r ;
}
2002-11-18 17:01:16 +03:00
static void _write_array ( struct pfilter * pf , FILE * fp , const char * path ,
2001-10-23 16:24:55 +04:00
void * data )
2001-10-22 18:14:00 +04:00
{
2001-10-23 16:24:55 +04:00
void * d ;
2001-10-22 18:14:00 +04:00
int first = 1 ;
2010-01-06 16:25:36 +03:00
char buf [ 2 * PATH_MAX ] ;
2005-10-17 03:03:59 +04:00
struct dm_hash_node * n ;
2001-10-22 18:14:00 +04:00
2005-10-17 03:03:59 +04:00
for ( n = dm_hash_get_first ( pf - > devices ) ; n ;
n = dm_hash_get_next ( pf - > devices , n ) ) {
d = dm_hash_get_data ( pf - > devices , n ) ;
2001-10-23 16:24:55 +04:00
if ( d ! = data )
continue ;
2001-10-22 18:14:00 +04:00
if ( ! first )
fprintf ( fp , " , \n " ) ;
2001-10-24 21:53:50 +04:00
else {
fprintf ( fp , " \t %s=[ \n " , path ) ;
2001-10-22 18:14:00 +04:00
first = 0 ;
2001-10-24 21:53:50 +04:00
}
2001-10-22 18:14:00 +04:00
2011-08-30 18:55:15 +04:00
dm_escape_double_quotes ( buf , dm_hash_get_key ( pf - > devices , n ) ) ;
2010-01-06 16:25:36 +03:00
fprintf ( fp , " \t \t \" %s \" " , buf ) ;
2001-10-22 18:14:00 +04:00
}
2001-10-24 21:53:50 +04:00
if ( ! first )
fprintf ( fp , " \n \t ] \n " ) ;
2001-10-22 18:14:00 +04:00
}
2018-04-20 18:43:50 +03:00
static int _persistent_filter_dump ( struct dev_filter * f , int merge_existing )
2001-10-22 18:14:00 +04:00
{
2009-07-08 22:13:32 +04:00
struct pfilter * pf ;
2006-11-04 06:34:10 +03:00
char * tmp_file ;
struct stat info , info2 ;
2015-03-18 12:59:41 +03:00
struct timespec ts ;
2011-08-30 18:55:15 +04:00
struct dm_config_tree * cft = NULL ;
2001-10-25 19:07:26 +04:00
FILE * fp ;
2006-11-04 06:34:10 +03:00
int lockfd ;
int r = 0 ;
2002-01-25 02:17:16 +03:00
2011-04-22 16:05:32 +04:00
if ( obtain_device_list_from_udev ( ) )
return 1 ;
2009-07-08 22:13:32 +04:00
if ( ! f )
return_0 ;
pf = ( struct pfilter * ) f - > private ;
2005-10-17 03:03:59 +04:00
if ( ! dm_hash_get_num_entries ( pf - > devices ) ) {
2002-01-25 02:17:16 +03:00
log_very_verbose ( " Internal persistent device cache empty "
" - not writing to %s " , pf - > file ) ;
2014-10-31 22:25:25 +03:00
return 1 ;
2002-01-25 02:17:16 +03:00
}
2002-11-18 17:01:16 +03:00
if ( ! dev_cache_has_scanned ( ) ) {
log_very_verbose ( " Device cache incomplete - not writing "
" to %s " , pf - > file ) ;
return 0 ;
}
2001-10-23 16:24:55 +04:00
log_very_verbose ( " Dumping persistent device cache to %s " , pf - > file ) ;
2001-10-22 18:14:00 +04:00
2006-11-04 06:34:10 +03:00
while ( 1 ) {
if ( ( lockfd = fcntl_lock_file ( pf - > file , F_WRLCK , 0 ) ) < 0 )
return_0 ;
/*
* Ensure we locked the file we expected
*/
if ( fstat ( lockfd , & info ) ) {
log_sys_error ( " fstat " , pf - > file ) ;
goto out ;
}
if ( stat ( pf - > file , & info2 ) ) {
log_sys_error ( " stat " , pf - > file ) ;
goto out ;
}
2007-07-20 19:22:46 +04:00
if ( is_same_inode ( info , info2 ) )
2006-11-04 06:34:10 +03:00
break ;
fcntl_unlock_file ( lockfd ) ;
}
/*
* If file contents changed since we loaded it , merge new contents
*/
2015-03-18 12:59:41 +03:00
lvm_stat_ctim ( & ts , & info ) ;
if ( merge_existing & & timespeccmp ( & ts , & pf - > ctime , ! = ) )
2006-11-04 06:34:10 +03:00
/* Keep cft open to avoid losing lock */
2018-04-20 18:43:50 +03:00
persistent_filter_load ( f , & cft ) ;
2006-11-04 06:34:10 +03:00
tmp_file = alloca ( strlen ( pf - > file ) + 5 ) ;
sprintf ( tmp_file , " %s.tmp " , pf - > file ) ;
if ( ! ( fp = fopen ( tmp_file , " w " ) ) ) {
/* EACCES has been reported over NFS */
if ( errno ! = EROFS & & errno ! = EACCES )
log_sys_error ( " fopen " , tmp_file ) ;
goto out ;
2001-10-23 16:24:55 +04:00
}
2001-10-22 18:14:00 +04:00
2001-10-23 16:24:55 +04:00
fprintf ( fp , " # This file is automatically maintained by lvm. \n \n " ) ;
2001-10-24 21:53:50 +04:00
fprintf ( fp , " persistent_filter_cache { \n " ) ;
2001-10-22 18:14:00 +04:00
2001-10-23 16:24:55 +04:00
_write_array ( pf , fp , " valid_devices " , PF_GOOD_DEVICE ) ;
2002-11-18 17:01:16 +03:00
/* We don't gain anything by remembering invalid devices */
/* _write_array(pf, fp, "invalid_devices", PF_BAD_DEVICE); */
2001-10-22 18:14:00 +04:00
2001-10-24 21:53:50 +04:00
fprintf ( fp , " } \n " ) ;
2007-07-24 21:48:08 +04:00
if ( lvm_fclose ( fp , tmp_file ) )
goto_out ;
2006-11-04 06:34:10 +03:00
if ( rename ( tmp_file , pf - > file ) )
log_error ( " %s: rename to %s failed: %s " , tmp_file , pf - > file ,
strerror ( errno ) ) ;
r = 1 ;
out :
fcntl_unlock_file ( lockfd ) ;
if ( cft )
2013-06-25 14:25:43 +04:00
config_destroy ( cft ) ;
2006-11-04 06:34:10 +03:00
return r ;
2001-10-22 18:14:00 +04:00
}
2001-10-23 16:24:55 +04:00
static int _lookup_p ( struct dev_filter * f , struct device * dev )
2001-10-22 18:14:00 +04:00
{
struct pfilter * pf = ( struct pfilter * ) f - > private ;
2005-10-17 03:03:59 +04:00
void * l = dm_hash_lookup ( pf - > devices , dev_name ( dev ) ) ;
2014-05-29 11:41:03 +04:00
struct dm_str_list * sl ;
2018-05-04 01:12:07 +03:00
int pass = 1 ;
2001-10-22 18:14:00 +04:00
2018-05-04 01:12:07 +03:00
/* Cached bad, skip dev */
2010-07-02 06:09:57 +04:00
if ( l = = PF_BAD_DEVICE ) {
2018-05-04 01:12:07 +03:00
log_debug_devs ( " %s: filter cache skipping (cached bad) " , dev_name ( dev ) ) ;
2010-07-02 06:09:57 +04:00
return 0 ;
}
2018-05-04 01:12:07 +03:00
/* Cached good, use dev */
if ( l = = PF_GOOD_DEVICE ) {
log_debug_devs ( " %s: filter cache using (cached good) " , dev_name ( dev ) ) ;
return 1 ;
2010-07-02 06:09:57 +04:00
}
2018-05-04 01:12:07 +03:00
/* Uncached, check filters and cache the result */
2001-10-23 16:24:55 +04:00
if ( ! l ) {
2018-05-04 01:12:07 +03:00
dev - > flags & = ~ DEV_FILTER_AFTER_SCAN ;
pass = pf - > real - > passes_filter ( pf - > real , dev ) ;
if ( ! pass ) {
/*
* A device that does not pass one filter is excluded
* even if the result of another filter is deferred ,
* because the deferred result won ' t change the exclude .
*/
l = PF_BAD_DEVICE ;
} else if ( ( pass = = - EAGAIN ) | | ( dev - > flags & DEV_FILTER_AFTER_SCAN ) ) {
/*
* When the filter result is deferred , we let the device
* pass for now , but do not cache the result . We need to
* rerun the filters later . At that point the final result
* will be cached .
*/
log_debug_devs ( " filter cache deferred %s " , dev_name ( dev ) ) ;
dev - > flags | = DEV_FILTER_AFTER_SCAN ;
pass = 1 ;
goto out ;
} else if ( pass ) {
l = PF_GOOD_DEVICE ;
}
log_debug_devs ( " filter caching %s %s " , pass ? " good " : " bad " , dev_name ( dev ) ) ;
2001-10-22 18:14:00 +04:00
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( sl , & dev - > aliases )
2012-02-28 15:12:58 +04:00
if ( ! dm_hash_insert ( pf - > devices , sl - > str , l ) ) {
log_error ( " Failed to hash alias to filter. " ) ;
return 0 ;
}
2010-07-02 06:09:57 +04:00
}
2018-05-04 01:12:07 +03:00
out :
return pass ;
2001-10-22 18:14:00 +04:00
}
2006-04-19 19:33:07 +04:00
static void _persistent_destroy ( struct dev_filter * f )
2001-10-22 18:14:00 +04:00
{
struct pfilter * pf = ( struct pfilter * ) f - > private ;
2010-09-22 05:36:13 +04:00
if ( f - > use_count )
log_error ( INTERNAL_ERROR " Destroying persistent filter while in use %u times. " , f - > use_count ) ;
2005-10-17 03:03:59 +04:00
dm_hash_destroy ( pf - > devices ) ;
dm_free ( pf - > file ) ;
2001-10-23 16:24:55 +04:00
pf - > real - > destroy ( pf - > real ) ;
2005-10-17 03:03:59 +04:00
dm_free ( pf ) ;
dm_free ( f ) ;
2001-10-22 18:14:00 +04:00
}
2013-06-12 14:08:56 +04:00
struct dev_filter * persistent_filter_create ( struct dev_types * dt ,
struct dev_filter * real ,
2001-10-23 16:24:55 +04:00
const char * file )
2001-10-22 18:14:00 +04:00
{
struct pfilter * pf ;
struct dev_filter * f = NULL ;
2010-06-01 23:02:12 +04:00
struct stat info ;
2001-10-22 18:14:00 +04:00
2011-01-06 18:29:24 +03:00
if ( ! ( pf = dm_zalloc ( sizeof ( * pf ) ) ) ) {
log_error ( " Allocation of persistent filter failed. " ) ;
return NULL ;
}
2001-10-22 18:14:00 +04:00
2013-06-12 14:08:56 +04:00
pf - > dt = dt ;
2011-01-06 18:29:24 +03:00
if ( ! ( pf - > file = dm_strdup ( file ) ) ) {
log_error ( " Filename duplication for persistent filter failed. " ) ;
goto bad ;
}
2007-04-26 20:44:59 +04:00
2001-10-23 16:24:55 +04:00
pf - > real = real ;
2001-10-22 18:14:00 +04:00
if ( ! ( _init_hash ( pf ) ) ) {
2001-10-23 22:20:27 +04:00
log_error ( " Couldn't create hash table for persistent filter. " ) ;
2001-10-22 18:14:00 +04:00
goto bad ;
}
2012-08-18 20:59:07 +04:00
if ( ! ( f = dm_zalloc ( sizeof ( * f ) ) ) ) {
2011-01-06 18:29:24 +03:00
log_error ( " Allocation of device filter for persistent filter failed. " ) ;
goto bad ;
}
2001-10-22 18:14:00 +04:00
2010-06-01 23:02:12 +04:00
/* Only merge cache file before dumping it if it changed externally. */
if ( ! stat ( pf - > file , & info ) )
2015-03-18 12:59:41 +03:00
lvm_stat_ctim ( & pf - > ctime , & info ) ;
2010-06-01 23:02:12 +04:00
2001-10-23 16:24:55 +04:00
f - > passes_filter = _lookup_p ;
2006-04-19 19:33:07 +04:00
f - > destroy = _persistent_destroy ;
2010-09-22 05:36:13 +04:00
f - > use_count = 0 ;
2001-10-22 18:14:00 +04:00
f - > private = pf ;
2012-08-13 21:44:10 +04:00
f - > wipe = _persistent_filter_wipe ;
2013-06-02 23:59:57 +04:00
f - > dump = _persistent_filter_dump ;
2001-10-22 18:14:00 +04:00
2013-08-14 02:26:58 +04:00
log_debug_devs ( " Persistent filter initialised. " ) ;
2001-10-22 18:14:00 +04:00
return f ;
2002-04-24 22:20:51 +04:00
bad :
2005-10-17 03:03:59 +04:00
dm_free ( pf - > file ) ;
2001-10-22 18:14:00 +04:00
if ( pf - > devices )
2005-10-17 03:03:59 +04:00
dm_hash_destroy ( pf - > devices ) ;
dm_free ( pf ) ;
dm_free ( f ) ;
2001-10-22 18:14:00 +04:00
return NULL ;
}