2001-10-03 16:41:29 +04:00
/*
2004-03-30 23:35:44 +04:00
* Copyright ( C ) 2001 - 2004 Sistina Software , Inc . All rights reserved .
* Copyright ( C ) 2004 Red Hat , Inc . All rights reserved .
2001-10-03 16:41:29 +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
* of the GNU General Public License v .2 .
*
* You should have received a copy of the GNU 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
2001-10-03 16:41:29 +04:00
*/
2002-11-18 17:01:16 +03:00
# include "lib.h"
2001-10-03 16:41:29 +04:00
# include "dev-cache.h"
# include "pool.h"
# include "hash.h"
# include "list.h"
2001-10-25 15:34:55 +04:00
# include "lvm-types.h"
# include "btree.h"
2003-03-24 21:08:53 +03:00
# include "filter.h"
2001-10-03 16:41:29 +04:00
# include <unistd.h>
# include <sys/param.h>
# include <dirent.h>
struct dev_iter {
2001-10-25 15:34:55 +04:00
struct btree_iter * current ;
2001-10-03 16:41:29 +04:00
struct dev_filter * filter ;
} ;
struct dir_list {
2001-10-31 15:47:01 +03:00
struct list list ;
2001-10-03 16:41:29 +04:00
char dir [ 0 ] ;
} ;
static struct {
struct pool * mem ;
2001-10-25 15:34:55 +04:00
struct hash_table * names ;
struct btree * devices ;
2001-10-03 16:41:29 +04:00
int has_scanned ;
2001-10-31 15:47:01 +03:00
struct list dirs ;
2001-10-03 16:41:29 +04:00
} _cache ;
# define _alloc(x) pool_alloc(_cache.mem, (x))
# define _free(x) pool_free(_cache.mem, (x))
2001-10-25 15:34:55 +04:00
static int _insert ( const char * path , int rec ) ;
2003-07-05 02:34:56 +04:00
struct device * dev_create_file ( const char * filename , struct device * dev ,
struct str_list * alias )
{
int allocate = ! dev ;
if ( allocate & & ! ( dev = dbg_malloc ( sizeof ( * dev ) ) ) ) {
log_error ( " struct device allocation failed " ) ;
return NULL ;
}
if ( allocate & & ! ( alias = dbg_malloc ( sizeof ( * alias ) ) ) ) {
log_error ( " struct str_list allocation failed " ) ;
dbg_free ( dev ) ;
return NULL ;
}
if ( ! ( alias - > str = dbg_strdup ( filename ) ) ) {
log_error ( " filename strdup failed " ) ;
if ( allocate ) {
dbg_free ( dev ) ;
dbg_free ( alias ) ;
}
return NULL ;
}
dev - > flags = DEV_REGULAR ;
if ( allocate )
dev - > flags | = DEV_ALLOCED ;
list_init ( & dev - > aliases ) ;
list_add ( & dev - > aliases , & alias - > list ) ;
dev - > end = UINT64_C ( 0 ) ;
dev - > dev = 0 ;
dev - > fd = - 1 ;
dev - > open_count = 0 ;
memset ( dev - > pvid , 0 , sizeof ( dev - > pvid ) ) ;
list_init ( & dev - > open_list ) ;
return dev ;
}
static struct device * _dev_create ( dev_t d )
2001-10-25 15:34:55 +04:00
{
struct device * dev ;
if ( ! ( dev = _alloc ( sizeof ( * dev ) ) ) ) {
2003-01-08 19:41:22 +03:00
log_error ( " struct device allocation failed " ) ;
2001-10-25 18:04:18 +04:00
return NULL ;
2001-10-25 15:34:55 +04:00
}
2003-07-05 02:34:56 +04:00
dev - > flags = 0 ;
2001-10-31 15:47:01 +03:00
list_init ( & dev - > aliases ) ;
2001-10-25 15:34:55 +04:00
dev - > dev = d ;
2001-11-14 13:01:52 +03:00
dev - > fd = - 1 ;
2003-07-05 02:34:56 +04:00
dev - > open_count = 0 ;
dev - > end = UINT64_C ( 0 ) ;
2002-11-18 17:01:16 +03:00
memset ( dev - > pvid , 0 , sizeof ( dev - > pvid ) ) ;
2003-07-05 02:34:56 +04:00
list_init ( & dev - > open_list ) ;
2003-01-08 19:41:22 +03:00
2001-10-25 15:34:55 +04:00
return dev ;
}
2003-01-04 00:11:23 +03:00
/* Return 1 if we prefer path1 else return 0 */
static int _compare_paths ( const char * path0 , const char * path1 )
{
int slash0 = 0 , slash1 = 0 ;
const char * p ;
char p0 [ PATH_MAX ] , p1 [ PATH_MAX ] ;
char * s0 , * s1 ;
struct stat stat0 , stat1 ;
/* Return the path with fewer slashes */
for ( p = path0 ; p + + ; p = ( const char * ) strchr ( p , ' / ' ) )
slash0 + + ;
for ( p = path1 ; p + + ; p = ( const char * ) strchr ( p , ' / ' ) )
slash1 + + ;
if ( slash0 < slash1 )
return 0 ;
if ( slash1 < slash0 )
return 1 ;
strncpy ( p0 , path0 , PATH_MAX ) ;
strncpy ( p1 , path1 , PATH_MAX ) ;
s0 = & p0 [ 0 ] + 1 ;
s1 = & p1 [ 0 ] + 1 ;
/* We prefer symlinks - they exist for a reason!
* So we prefer a shorter path before the first symlink in the name .
* FIXME Configuration option to invert this ? */
while ( s0 ) {
s0 = strchr ( s0 , ' / ' ) ;
s1 = strchr ( s1 , ' / ' ) ;
if ( s0 ) {
* s0 = ' \0 ' ;
* s1 = ' \0 ' ;
}
if ( lstat ( p0 , & stat0 ) ) {
log_sys_error ( " lstat " , p0 ) ;
return 1 ;
}
if ( lstat ( p1 , & stat1 ) ) {
log_sys_error ( " lstat " , p1 ) ;
return 0 ;
}
if ( S_ISLNK ( stat0 . st_mode ) & & ! S_ISLNK ( stat1 . st_mode ) )
return 0 ;
if ( ! S_ISLNK ( stat0 . st_mode ) & & S_ISLNK ( stat1 . st_mode ) )
return 1 ;
if ( s0 ) {
* s0 + + = ' / ' ;
* s1 + + = ' / ' ;
}
}
/* ASCII comparison */
if ( strcmp ( path0 , path1 ) < 0 )
return 0 ;
else
return 1 ;
}
2001-10-25 15:34:55 +04:00
static int _add_alias ( struct device * dev , const char * path )
{
struct str_list * sl = _alloc ( sizeof ( * sl ) ) ;
2003-01-08 19:41:22 +03:00
struct list * ah ;
2003-01-04 00:11:23 +03:00
const char * oldpath ;
int prefer_old = 1 ;
2001-10-25 15:34:55 +04:00
if ( ! sl ) {
stack ;
return 0 ;
}
2003-01-08 19:41:22 +03:00
/* Is name already there? */
list_iterate ( ah , & dev - > aliases ) {
if ( ! strcmp ( list_item ( ah , struct str_list ) - > str , path ) ) {
stack ;
return 1 ;
}
}
2001-10-25 15:34:55 +04:00
if ( ! ( sl - > str = pool_strdup ( _cache . mem , path ) ) ) {
stack ;
return 0 ;
}
2003-01-04 00:11:23 +03:00
if ( ! list_empty ( & dev - > aliases ) ) {
oldpath = list_item ( dev - > aliases . n , struct str_list ) - > str ;
prefer_old = _compare_paths ( path , oldpath ) ;
2003-01-08 19:41:22 +03:00
log_debug ( " %s: Aliased to %s in device cache%s " ,
path , oldpath , prefer_old ? " " : " (preferred name) " ) ;
} else
log_debug ( " %s: Added to device cache " , path ) ;
2003-01-04 00:11:23 +03:00
if ( prefer_old )
list_add ( & dev - > aliases , & sl - > list ) ;
else
list_add_h ( & dev - > aliases , & sl - > list ) ;
2001-10-25 15:34:55 +04:00
return 1 ;
}
/*
* Either creates a new dev , or adds an alias to
* an existing dev .
*/
static int _insert_dev ( const char * path , dev_t d )
{
struct device * dev ;
/* is this device already registered ? */
2002-12-20 02:25:55 +03:00
if ( ! ( dev = ( struct device * ) btree_lookup ( _cache . devices ,
( uint32_t ) d ) ) ) {
2001-10-25 15:34:55 +04:00
/* create new device */
2003-07-05 02:34:56 +04:00
if ( ! ( dev = _dev_create ( d ) ) ) {
2001-10-25 15:34:55 +04:00
stack ;
return 0 ;
}
2002-12-20 02:25:55 +03:00
if ( ! ( btree_insert ( _cache . devices , ( uint32_t ) d , dev ) ) ) {
2001-10-25 15:34:55 +04:00
log_err ( " Couldn't insert device into binary tree. " ) ;
_free ( dev ) ;
return 0 ;
}
}
2001-10-25 18:04:18 +04:00
if ( ! _add_alias ( dev , path ) ) {
2001-12-17 14:07:33 +03:00
log_err ( " Couldn't add alias to dev cache. " ) ;
2001-10-25 15:34:55 +04:00
return 0 ;
}
2001-10-25 18:19:39 +04:00
if ( ! hash_insert ( _cache . names , path , dev ) ) {
2001-12-17 14:07:33 +03:00
log_err ( " Couldn't add name to hash in dev cache. " ) ;
2001-10-25 18:19:39 +04:00
return 0 ;
}
2001-10-25 15:34:55 +04:00
return 1 ;
}
2001-10-08 17:58:52 +04:00
2001-10-25 15:34:55 +04:00
static char * _join ( const char * dir , const char * name )
{
2002-12-20 02:25:55 +03:00
size_t len = strlen ( dir ) + strlen ( name ) + 2 ;
2001-10-25 15:34:55 +04:00
char * r = dbg_malloc ( len ) ;
if ( r )
snprintf ( r , len , " %s/%s " , dir , name ) ;
return r ;
}
2001-10-03 16:41:29 +04:00
/*
* Get rid of extra slashes in the path string .
*/
static void _collapse_slashes ( char * str )
{
char * ptr ;
int was_slash = 0 ;
for ( ptr = str ; * ptr ; ptr + + ) {
if ( * ptr = = ' / ' ) {
if ( was_slash )
continue ;
was_slash = 1 ;
} else
was_slash = 0 ;
* str + + = * ptr ;
}
* str = * ptr ;
}
2001-10-25 15:34:55 +04:00
static int _insert_dir ( const char * dir )
2001-10-03 16:41:29 +04:00
{
2001-10-25 15:34:55 +04:00
int n , dirent_count , r = 1 ;
struct dirent * * dirent ;
char * path ;
2001-10-03 16:41:29 +04:00
2001-10-25 15:34:55 +04:00
dirent_count = scandir ( dir , & dirent , NULL , alphasort ) ;
if ( dirent_count > 0 ) {
for ( n = 0 ; n < dirent_count ; n + + ) {
if ( dirent [ n ] - > d_name [ 0 ] = = ' . ' ) {
free ( dirent [ n ] ) ;
continue ;
}
2001-10-03 16:41:29 +04:00
2001-10-25 15:34:55 +04:00
if ( ! ( path = _join ( dir , dirent [ n ] - > d_name ) ) ) {
stack ;
return 0 ;
}
2001-10-03 16:41:29 +04:00
2001-10-25 15:34:55 +04:00
_collapse_slashes ( path ) ;
r & = _insert ( path , 1 ) ;
dbg_free ( path ) ;
2001-10-03 16:41:29 +04:00
2001-10-25 15:34:55 +04:00
free ( dirent [ n ] ) ;
}
free ( dirent ) ;
}
2001-10-08 17:58:52 +04:00
2001-10-25 15:34:55 +04:00
return r ;
2001-10-08 17:58:52 +04:00
}
2001-10-25 15:34:55 +04:00
static int _insert ( const char * path , int rec )
2001-10-08 17:58:52 +04:00
{
struct stat info ;
2001-10-25 15:34:55 +04:00
int r = 0 ;
2001-10-08 17:58:52 +04:00
if ( stat ( path , & info ) < 0 ) {
2001-10-10 20:36:32 +04:00
log_sys_very_verbose ( " stat " , path ) ;
2001-10-08 17:58:52 +04:00
return 0 ;
}
2002-04-24 22:20:51 +04:00
if ( S_ISDIR ( info . st_mode ) ) { /* add a directory */
2004-02-13 21:55:43 +03:00
/* check it's not a symbolic link */
if ( lstat ( path , & info ) < 0 ) {
log_sys_very_verbose ( " lstat " , path ) ;
return 0 ;
}
if ( S_ISLNK ( info . st_mode ) ) {
log_debug ( " %s: Symbolic link to directory " , path ) ;
return 0 ;
}
2001-10-25 15:34:55 +04:00
if ( rec )
r = _insert_dir ( path ) ;
2001-10-08 17:58:52 +04:00
2001-10-25 15:34:55 +04:00
} else { /* add a device */
if ( ! S_ISBLK ( info . st_mode ) ) {
2002-01-17 19:39:24 +03:00
log_debug ( " %s: Not a block device " , path ) ;
2001-10-25 15:34:55 +04:00
return 0 ;
}
2001-10-08 17:58:52 +04:00
2001-10-25 15:34:55 +04:00
if ( ! _insert_dev ( path , info . st_rdev ) ) {
stack ;
return 0 ;
2001-10-03 16:41:29 +04:00
}
2001-10-25 15:34:55 +04:00
r = 1 ;
2001-10-03 16:41:29 +04:00
}
2001-10-25 15:34:55 +04:00
return r ;
2001-10-03 16:41:29 +04:00
}
static void _full_scan ( void )
{
2001-10-31 15:47:01 +03:00
struct list * dh ;
2001-10-03 16:41:29 +04:00
if ( _cache . has_scanned )
return ;
2001-10-31 15:47:01 +03:00
list_iterate ( dh , & _cache . dirs ) {
struct dir_list * dl = list_item ( dh , struct dir_list ) ;
2001-10-25 15:34:55 +04:00
_insert_dir ( dl - > dir ) ;
2001-10-31 15:47:01 +03:00
} ;
2001-10-03 16:41:29 +04:00
_cache . has_scanned = 1 ;
}
2002-11-18 17:01:16 +03:00
int dev_cache_has_scanned ( void )
{
return _cache . has_scanned ;
}
void dev_cache_scan ( int do_scan )
{
if ( ! do_scan )
_cache . has_scanned = 1 ;
else {
_cache . has_scanned = 0 ;
_full_scan ( ) ;
}
}
2001-10-03 16:41:29 +04:00
int dev_cache_init ( void )
{
2001-10-25 15:34:55 +04:00
_cache . names = NULL ;
2001-10-03 16:41:29 +04:00
if ( ! ( _cache . mem = pool_create ( 10 * 1024 ) ) ) {
stack ;
return 0 ;
}
2001-10-25 15:34:55 +04:00
if ( ! ( _cache . names = hash_create ( 128 ) ) ) {
2001-10-03 16:41:29 +04:00
stack ;
pool_destroy ( _cache . mem ) ;
_cache . mem = 0 ;
return 0 ;
}
2001-10-25 15:34:55 +04:00
if ( ! ( _cache . devices = btree_create ( _cache . mem ) ) ) {
log_err ( " Couldn't create binary tree for dev-cache. " ) ;
goto bad ;
}
2001-10-31 15:47:01 +03:00
list_init ( & _cache . dirs ) ;
2001-10-08 16:11:33 +04:00
2001-10-03 16:41:29 +04:00
return 1 ;
2001-10-25 15:34:55 +04:00
2002-04-24 22:20:51 +04:00
bad :
2001-10-25 15:34:55 +04:00
dev_cache_exit ( ) ;
return 0 ;
2001-10-03 16:41:29 +04:00
}
2002-12-20 02:25:55 +03:00
static void _check_closed ( struct device * dev )
2001-11-14 13:01:52 +03:00
{
2002-01-16 00:28:04 +03:00
if ( dev - > fd > = 0 )
log_err ( " Device '%s' has been left open. " , dev_name ( dev ) ) ;
}
2001-11-14 13:01:52 +03:00
2002-01-16 00:28:04 +03:00
static inline void _check_for_open_devices ( void )
{
2002-04-24 22:20:51 +04:00
hash_iter ( _cache . names , ( iterate_fn ) _check_closed ) ;
2001-11-14 13:01:52 +03:00
}
2001-10-03 16:41:29 +04:00
void dev_cache_exit ( void )
{
2001-10-25 15:34:55 +04:00
if ( _cache . names )
2003-11-14 20:55:39 +03:00
_check_for_open_devices ( ) ;
if ( _cache . mem ) {
pool_destroy ( _cache . mem ) ;
_cache . mem = NULL ;
}
if ( _cache . names ) {
2001-10-25 15:34:55 +04:00
hash_destroy ( _cache . names ) ;
2003-11-14 20:55:39 +03:00
_cache . names = NULL ;
}
_cache . devices = NULL ;
_cache . has_scanned = 0 ;
list_init ( & _cache . dirs ) ;
2001-10-03 16:41:29 +04:00
}
int dev_cache_add_dir ( const char * path )
{
struct dir_list * dl ;
2001-10-23 15:50:49 +04:00
struct stat st ;
if ( stat ( path , & st ) ) {
log_error ( " Ignoring %s: %s " , path , strerror ( errno ) ) ;
/* But don't fail */
return 1 ;
}
if ( ! S_ISDIR ( st . st_mode ) ) {
log_error ( " Ignoring %s: Not a directory " , path ) ;
return 1 ;
}
2001-10-03 16:41:29 +04:00
2003-01-08 19:41:22 +03:00
if ( ! ( dl = _alloc ( sizeof ( * dl ) + strlen ( path ) + 1 ) ) ) {
log_error ( " dir_list allocation failed " ) ;
2001-10-03 16:41:29 +04:00
return 0 ;
2003-01-08 19:41:22 +03:00
}
2001-10-03 16:41:29 +04:00
strcpy ( dl - > dir , path ) ;
2001-10-31 15:47:01 +03:00
list_add ( & _cache . dirs , & dl - > list ) ;
2001-10-03 16:41:29 +04:00
return 1 ;
}
2002-01-25 02:16:19 +03:00
/* Check cached device name is still valid before returning it */
/* This should be a rare occurrence */
2003-07-05 02:34:56 +04:00
/* set quiet if the cache is expected to be out-of-date */
2002-01-25 02:16:19 +03:00
/* FIXME Make rest of code pass/cache struct device instead of dev_name */
2003-07-05 02:34:56 +04:00
const char * dev_name_confirmed ( struct device * dev , int quiet )
2002-01-25 02:16:19 +03:00
{
struct stat buf ;
2003-07-05 02:34:56 +04:00
const char * name ;
2002-01-25 02:16:19 +03:00
int r ;
2002-04-24 22:20:51 +04:00
while ( ( r = stat ( name = list_item ( dev - > aliases . n ,
struct str_list ) - > str , & buf ) ) | |
2002-01-25 02:16:19 +03:00
( buf . st_rdev ! = dev - > dev ) ) {
2003-07-05 02:34:56 +04:00
if ( r < 0 ) {
if ( quiet )
log_sys_debug ( " stat " , name ) ;
else
log_sys_error ( " stat " , name ) ;
}
if ( quiet )
log_debug ( " Path %s no longer valid for device(%d,%d) " ,
2004-02-13 21:55:43 +03:00
name , ( int ) MAJOR ( dev - > dev ) ,
2003-07-05 02:34:56 +04:00
( int ) MINOR ( dev - > dev ) ) ;
else
log_error ( " Path %s no longer valid for device(%d,%d) " ,
name , ( int ) MAJOR ( dev - > dev ) ,
( int ) MINOR ( dev - > dev ) ) ;
2002-01-25 02:16:19 +03:00
/* Remove the incorrect hash entry */
hash_remove ( _cache . names , name ) ;
/* Leave list alone if there isn't an alternative name */
/* so dev_name will always find something to return. */
/* Otherwise add the name to the correct device. */
if ( list_size ( & dev - > aliases ) > 1 ) {
list_del ( dev - > aliases . n ) ;
if ( ! r )
_insert ( name , 0 ) ;
continue ;
}
log_error ( " Aborting - please provide new pathname for what "
" used to be %s " , name ) ;
return NULL ;
}
return dev_name ( dev ) ;
}
2001-10-03 16:41:29 +04:00
struct device * dev_cache_get ( const char * name , struct dev_filter * f )
{
2002-01-25 02:16:19 +03:00
struct stat buf ;
2001-10-25 15:34:55 +04:00
struct device * d = ( struct device * ) hash_lookup ( _cache . names , name ) ;
2001-10-08 16:11:33 +04:00
2002-01-25 02:16:19 +03:00
/* If the entry's wrong, remove it */
if ( d & & ( stat ( name , & buf ) | | ( buf . st_rdev ! = d - > dev ) ) ) {
hash_remove ( _cache . names , name ) ;
d = NULL ;
}
2001-10-08 17:58:52 +04:00
if ( ! d ) {
_insert ( name , 0 ) ;
2001-10-25 15:34:55 +04:00
d = ( struct device * ) hash_lookup ( _cache . names , name ) ;
2001-10-08 17:58:52 +04:00
}
2001-10-08 16:11:33 +04:00
2001-10-03 16:41:29 +04:00
return ( d & & ( ! f | | f - > passes_filter ( f , d ) ) ) ? d : NULL ;
}
struct dev_iter * dev_iter_create ( struct dev_filter * f )
{
struct dev_iter * di = dbg_malloc ( sizeof ( * di ) ) ;
2003-01-08 19:41:22 +03:00
if ( ! di ) {
log_error ( " dev_iter allocation failed " ) ;
2001-10-03 16:41:29 +04:00
return NULL ;
2003-01-08 19:41:22 +03:00
}
2001-10-03 16:41:29 +04:00
_full_scan ( ) ;
2001-10-25 15:34:55 +04:00
di - > current = btree_first ( _cache . devices ) ;
2001-10-03 16:41:29 +04:00
di - > filter = f ;
return di ;
}
void dev_iter_destroy ( struct dev_iter * iter )
{
dbg_free ( iter ) ;
}
static inline struct device * _iter_next ( struct dev_iter * iter )
{
2001-10-25 15:34:55 +04:00
struct device * d = btree_get_data ( iter - > current ) ;
iter - > current = btree_next ( iter - > current ) ;
2001-10-03 16:41:29 +04:00
return d ;
}
struct device * dev_iter_get ( struct dev_iter * iter )
{
while ( iter - > current ) {
struct device * d = _iter_next ( iter ) ;
if ( ! iter - > filter | |
iter - > filter - > passes_filter ( iter - > filter , d ) )
return d ;
}
return NULL ;
}