2001-10-03 16:41:29 +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-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
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-03 16:41:29 +04:00
*/
2018-06-08 15:40:53 +03:00
# include "base/memory/zalloc.h"
2018-05-14 12:30:20 +03:00
# include "lib/misc/lib.h"
2019-07-09 22:48:31 +03:00
# include "lib/device/dev-type.h"
device usage based on devices file
The LVM devices file lists devices that lvm can use. The default
file is /etc/lvm/devices/system.devices, and the lvmdevices(8)
command is used to add or remove device entries. If the file
does not exist, or if lvm.conf includes use_devicesfile=0, then
lvm will not use a devices file. When the devices file is in use,
the regex filter is not used, and the filter settings in lvm.conf
or on the command line are ignored.
LVM records devices in the devices file using hardware-specific
IDs, such as the WWID, and attempts to use subsystem-specific
IDs for virtual device types. These device IDs are also written
in the VG metadata. When no hardware or virtual ID is available,
lvm falls back using the unstable device name as the device ID.
When devnames are used, lvm performs extra scanning to find
devices if their devname changes, e.g. after reboot.
When proper device IDs are used, an lvm command will not look
at devices outside the devices file, but when devnames are used
as a fallback, lvm will scan devices outside the devices file
to locate PVs on renamed devices. A config setting
search_for_devnames can be used to control the scanning for
renamed devname entries.
Related to the devices file, the new command option
--devices <devnames> allows a list of devices to be specified for
the command to use, overriding the devices file. The listed
devices act as a sort of devices file in terms of limiting which
devices lvm will see and use. Devices that are not listed will
appear to be missing to the lvm command.
Multiple devices files can be kept in /etc/lvm/devices, which
allows lvm to be used with different sets of devices, e.g.
system devices do not need to be exposed to a specific application,
and the application can use lvm on its own set of devices that are
not exposed to the system. The option --devicesfile <filename> is
used to select the devices file to use with the command. Without
the option set, the default system devices file is used.
Setting --devicesfile "" causes lvm to not use a devices file.
An existing, empty devices file means lvm will see no devices.
The new command vgimportdevices adds PVs from a VG to the devices
file and updates the VG metadata to include the device IDs.
vgimportdevices -a will import all VGs into the system devices file.
LVM commands run by dmeventd not use a devices file by default,
and will look at all devices on the system. A devices file can
be created for dmeventd (/etc/lvm/devices/dmeventd.devices) If
this file exists, lvm commands run by dmeventd will use it.
Internal implementaion:
- device_ids_read - read the devices file
. add struct dev_use (du) to cmd->use_devices for each devices file entry
- dev_cache_scan - get /dev entries
. add struct device (dev) to dev_cache for each device on the system
- device_ids_match - match devices file entries to /dev entries
. match each du on cmd->use_devices to a dev in dev_cache, using device ID
. on match, set du->dev, dev->id, dev->flags MATCHED_USE_ID
- label_scan - read lvm headers and metadata from devices
. filters are applied, those that do not need data from the device
. filter-deviceid skips devs without MATCHED_USE_ID, i.e.
skips /dev entries that are not listed in the devices file
. read lvm label from dev
. filters are applied, those that use data from the device
. read lvm metadata from dev
. add info/vginfo structs for PVs/VGs (info is "lvmcache")
- device_ids_find_renamed_devs - handle devices with unstable devname ID
where devname changed
. this step only needed when devs do not have proper device IDs,
and their dev names change, e.g. after reboot sdb becomes sdc.
. detect incorrect match because PVID in the devices file entry
does not match the PVID found when the device was read above
. undo incorrect match between du and dev above
. search system devices for new location of PVID
. update devices file with new devnames for PVIDs on renamed devices
. label_scan the renamed devs
- continue with command processing
2020-06-23 21:25:41 +03:00
# include "lib/device/device_id.h"
2018-05-14 12:30:20 +03:00
# include "lib/datastruct/btree.h"
# include "lib/config/config.h"
# include "lib/commands/toolcontext.h"
2018-05-14 14:16:43 +03:00
# include "device_mapper/misc/dm-ioctl.h"
2018-05-14 12:30:20 +03:00
# include "lib/misc/lvm-string.h"
2001-10-03 16:41:29 +04:00
2014-04-01 17:17:50 +04:00
# ifdef UDEV_SYNC_SUPPORT
# include <libudev.h>
# endif
2001-10-03 16:41:29 +04:00
# include <unistd.h>
# include <dirent.h>
2021-02-27 19:09:38 +03:00
# include <locale.h>
2021-09-10 00:06:45 +03:00
/* coverity[unnecessary_header] needed for MuslC */
2021-08-23 17:24:45 +03:00
# include <sys/file.h>
2001-10-03 16:41:29 +04:00
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 {
2008-11-04 01:14:30 +03:00
struct dm_list list ;
2021-09-22 18:05:09 +03:00
char dir [ 0 ] ;
2001-10-03 16:41:29 +04:00
} ;
static struct {
2005-10-17 03:03:59 +04:00
struct dm_pool * mem ;
struct dm_hash_table * names ;
2016-03-16 16:01:26 +03:00
struct dm_hash_table * vgid_index ;
struct dm_hash_table * lvid_index ;
2016-03-31 16:14:01 +03:00
struct btree * sysfs_only_devices ; /* see comments in _get_device_for_sysfs_dev_name_using_devno */
2001-10-25 15:34:55 +04:00
struct btree * devices ;
2007-04-27 22:52:05 +04:00
struct dm_regex * preferred_names_matcher ;
2010-05-25 02:53:48 +04:00
const char * dev_dir ;
2001-10-03 16:41:29 +04:00
int has_scanned ;
2023-07-13 13:32:44 +03:00
dev_t st_dev ;
2008-11-04 01:14:30 +03:00
struct dm_list dirs ;
struct dm_list files ;
2001-10-03 16:41:29 +04:00
} _cache ;
2011-12-21 17:14:54 +04:00
# define _zalloc(x) dm_pool_zalloc(_cache.mem, (x))
2005-10-17 03:03:59 +04:00
# define _free(x) dm_pool_free(_cache.mem, (x))
# define _strdup(x) dm_pool_strdup(_cache.mem, (x))
2001-10-03 16:41:29 +04:00
2011-11-25 13:01:37 +04:00
static int _insert ( const char * path , const struct stat * info ,
int rec , int check_with_udev_db ) ;
2001-10-25 15:34:55 +04:00
2011-12-21 17:17:54 +04:00
/* Setup non-zero members of passed zeroed 'struct device' */
2018-06-15 22:04:39 +03:00
static void _dev_init ( struct device * dev )
2011-12-21 17:17:54 +04:00
{
dev - > fd = - 1 ;
2020-06-01 20:22:15 +03:00
dev - > bcache_fd = - 1 ;
bcache: use indirection table for fd
Add a "device index" (di) for each device, and use this
in the bcache api to the rest of lvm. This replaces the
file descriptor (fd) in the api. The rest of lvm uses
new functions bcache_set_fd(), bcache_clear_fd(), and
bcache_change_fd() to control which fd bcache uses for
io to a particular device.
. lvm opens a dev and gets and fd.
fd = open(dev);
. lvm passes fd to the bcache layer and gets a di
to use in the bcache api for the dev.
di = bcache_set_fd(fd);
. lvm uses bcache functions, passing di for the dev.
bcache_write_bytes(di, ...), etc.
. bcache translates di to fd to do io.
. lvm closes the device and clears the di/fd bcache state.
close(fd);
bcache_clear_fd(di);
In the bcache layer, a di-to-fd translation table
(int *_fd_table) is added. When bcache needs to
perform io on a di, it uses _fd_table[di].
In the following commit, lvm will make use of the new
bcache_change_fd() function to change the fd that
bcache uses for the dev, without dropping cached blocks.
2020-09-17 17:40:18 +03:00
dev - > bcache_di = - 1 ;
2011-12-21 17:17:54 +04:00
dev - > read_ahead = - 1 ;
device usage based on devices file
The LVM devices file lists devices that lvm can use. The default
file is /etc/lvm/devices/system.devices, and the lvmdevices(8)
command is used to add or remove device entries. If the file
does not exist, or if lvm.conf includes use_devicesfile=0, then
lvm will not use a devices file. When the devices file is in use,
the regex filter is not used, and the filter settings in lvm.conf
or on the command line are ignored.
LVM records devices in the devices file using hardware-specific
IDs, such as the WWID, and attempts to use subsystem-specific
IDs for virtual device types. These device IDs are also written
in the VG metadata. When no hardware or virtual ID is available,
lvm falls back using the unstable device name as the device ID.
When devnames are used, lvm performs extra scanning to find
devices if their devname changes, e.g. after reboot.
When proper device IDs are used, an lvm command will not look
at devices outside the devices file, but when devnames are used
as a fallback, lvm will scan devices outside the devices file
to locate PVs on renamed devices. A config setting
search_for_devnames can be used to control the scanning for
renamed devname entries.
Related to the devices file, the new command option
--devices <devnames> allows a list of devices to be specified for
the command to use, overriding the devices file. The listed
devices act as a sort of devices file in terms of limiting which
devices lvm will see and use. Devices that are not listed will
appear to be missing to the lvm command.
Multiple devices files can be kept in /etc/lvm/devices, which
allows lvm to be used with different sets of devices, e.g.
system devices do not need to be exposed to a specific application,
and the application can use lvm on its own set of devices that are
not exposed to the system. The option --devicesfile <filename> is
used to select the devices file to use with the command. Without
the option set, the default system devices file is used.
Setting --devicesfile "" causes lvm to not use a devices file.
An existing, empty devices file means lvm will see no devices.
The new command vgimportdevices adds PVs from a VG to the devices
file and updates the VG metadata to include the device IDs.
vgimportdevices -a will import all VGs into the system devices file.
LVM commands run by dmeventd not use a devices file by default,
and will look at all devices on the system. A devices file can
be created for dmeventd (/etc/lvm/devices/dmeventd.devices) If
this file exists, lvm commands run by dmeventd will use it.
Internal implementaion:
- device_ids_read - read the devices file
. add struct dev_use (du) to cmd->use_devices for each devices file entry
- dev_cache_scan - get /dev entries
. add struct device (dev) to dev_cache for each device on the system
- device_ids_match - match devices file entries to /dev entries
. match each du on cmd->use_devices to a dev in dev_cache, using device ID
. on match, set du->dev, dev->id, dev->flags MATCHED_USE_ID
- label_scan - read lvm headers and metadata from devices
. filters are applied, those that do not need data from the device
. filter-deviceid skips devs without MATCHED_USE_ID, i.e.
skips /dev entries that are not listed in the devices file
. read lvm label from dev
. filters are applied, those that use data from the device
. read lvm metadata from dev
. add info/vginfo structs for PVs/VGs (info is "lvmcache")
- device_ids_find_renamed_devs - handle devices with unstable devname ID
where devname changed
. this step only needed when devs do not have proper device IDs,
and their dev names change, e.g. after reboot sdb becomes sdc.
. detect incorrect match because PVID in the devices file entry
does not match the PVID found when the device was read above
. undo incorrect match between du and dev above
. search system devices for new location of PVID
. update devices file with new devnames for PVIDs on renamed devices
. label_scan the renamed devs
- continue with command processing
2020-06-23 21:25:41 +03:00
dev - > part = - 1 ;
2011-12-21 17:17:54 +04:00
2014-12-12 14:55:41 +03:00
dev - > ext . enabled = 0 ;
dev - > ext . src = DEV_EXT_NONE ;
2011-12-21 17:17:54 +04:00
dm_list_init ( & dev - > aliases ) ;
device usage based on devices file
The LVM devices file lists devices that lvm can use. The default
file is /etc/lvm/devices/system.devices, and the lvmdevices(8)
command is used to add or remove device entries. If the file
does not exist, or if lvm.conf includes use_devicesfile=0, then
lvm will not use a devices file. When the devices file is in use,
the regex filter is not used, and the filter settings in lvm.conf
or on the command line are ignored.
LVM records devices in the devices file using hardware-specific
IDs, such as the WWID, and attempts to use subsystem-specific
IDs for virtual device types. These device IDs are also written
in the VG metadata. When no hardware or virtual ID is available,
lvm falls back using the unstable device name as the device ID.
When devnames are used, lvm performs extra scanning to find
devices if their devname changes, e.g. after reboot.
When proper device IDs are used, an lvm command will not look
at devices outside the devices file, but when devnames are used
as a fallback, lvm will scan devices outside the devices file
to locate PVs on renamed devices. A config setting
search_for_devnames can be used to control the scanning for
renamed devname entries.
Related to the devices file, the new command option
--devices <devnames> allows a list of devices to be specified for
the command to use, overriding the devices file. The listed
devices act as a sort of devices file in terms of limiting which
devices lvm will see and use. Devices that are not listed will
appear to be missing to the lvm command.
Multiple devices files can be kept in /etc/lvm/devices, which
allows lvm to be used with different sets of devices, e.g.
system devices do not need to be exposed to a specific application,
and the application can use lvm on its own set of devices that are
not exposed to the system. The option --devicesfile <filename> is
used to select the devices file to use with the command. Without
the option set, the default system devices file is used.
Setting --devicesfile "" causes lvm to not use a devices file.
An existing, empty devices file means lvm will see no devices.
The new command vgimportdevices adds PVs from a VG to the devices
file and updates the VG metadata to include the device IDs.
vgimportdevices -a will import all VGs into the system devices file.
LVM commands run by dmeventd not use a devices file by default,
and will look at all devices on the system. A devices file can
be created for dmeventd (/etc/lvm/devices/dmeventd.devices) If
this file exists, lvm commands run by dmeventd will use it.
Internal implementaion:
- device_ids_read - read the devices file
. add struct dev_use (du) to cmd->use_devices for each devices file entry
- dev_cache_scan - get /dev entries
. add struct device (dev) to dev_cache for each device on the system
- device_ids_match - match devices file entries to /dev entries
. match each du on cmd->use_devices to a dev in dev_cache, using device ID
. on match, set du->dev, dev->id, dev->flags MATCHED_USE_ID
- label_scan - read lvm headers and metadata from devices
. filters are applied, those that do not need data from the device
. filter-deviceid skips devs without MATCHED_USE_ID, i.e.
skips /dev entries that are not listed in the devices file
. read lvm label from dev
. filters are applied, those that use data from the device
. read lvm metadata from dev
. add info/vginfo structs for PVs/VGs (info is "lvmcache")
- device_ids_find_renamed_devs - handle devices with unstable devname ID
where devname changed
. this step only needed when devs do not have proper device IDs,
and their dev names change, e.g. after reboot sdb becomes sdc.
. detect incorrect match because PVID in the devices file entry
does not match the PVID found when the device was read above
. undo incorrect match between du and dev above
. search system devices for new location of PVID
. update devices file with new devnames for PVIDs on renamed devices
. label_scan the renamed devs
- continue with command processing
2020-06-23 21:25:41 +03:00
dm_list_init ( & dev - > ids ) ;
2022-06-06 22:04:20 +03:00
dm_list_init ( & dev - > wwids ) ;
2011-12-21 17:17:54 +04:00
}
2015-08-17 14:57:01 +03:00
void dev_destroy_file ( struct device * dev )
{
if ( ! ( dev - > flags & DEV_ALLOCED ) )
return ;
2018-06-08 15:40:53 +03:00
free ( ( void * ) dm_list_item ( dev - > aliases . n , struct dm_str_list ) - > str ) ;
free ( dev - > aliases . n ) ;
free ( dev ) ;
2015-08-17 14:57:01 +03:00
}
2003-07-05 02:34:56 +04:00
struct device * dev_create_file ( const char * filename , struct device * dev ,
2014-05-29 11:41:03 +04:00
struct dm_str_list * alias , int use_malloc )
2003-07-05 02:34:56 +04:00
{
int allocate = ! dev ;
2005-05-03 21:28:23 +04:00
if ( allocate ) {
if ( use_malloc ) {
2018-06-08 15:40:53 +03:00
if ( ! ( dev = zalloc ( sizeof ( * dev ) ) ) ) {
2005-05-03 21:28:23 +04:00
log_error ( " struct device allocation failed " ) ;
return NULL ;
}
2018-06-08 15:40:53 +03:00
if ( ! ( alias = zalloc ( sizeof ( * alias ) ) ) ) {
2014-05-29 11:41:03 +04:00
log_error ( " struct dm_str_list allocation failed " ) ;
2018-06-08 15:40:53 +03:00
free ( dev ) ;
2005-05-03 21:28:23 +04:00
return NULL ;
}
2018-06-08 15:40:53 +03:00
if ( ! ( alias - > str = strdup ( filename ) ) ) {
2005-05-09 21:02:52 +04:00
log_error ( " filename strdup failed " ) ;
2018-06-08 15:40:53 +03:00
free ( dev ) ;
free ( alias ) ;
2005-05-09 21:02:52 +04:00
return NULL ;
}
2005-05-03 21:28:23 +04:00
} else {
2011-12-21 17:14:54 +04:00
if ( ! ( dev = _zalloc ( sizeof ( * dev ) ) ) ) {
2005-05-03 21:28:23 +04:00
log_error ( " struct device allocation failed " ) ;
return NULL ;
}
2011-12-21 17:14:54 +04:00
if ( ! ( alias = _zalloc ( sizeof ( * alias ) ) ) ) {
2014-05-29 11:41:03 +04:00
log_error ( " struct dm_str_list allocation failed " ) ;
2005-05-03 21:31:56 +04:00
_free ( dev ) ;
2005-05-03 21:28:23 +04:00
return NULL ;
}
2005-05-09 21:02:52 +04:00
if ( ! ( alias - > str = _strdup ( filename ) ) ) {
log_error ( " filename strdup failed " ) ;
2014-12-07 01:55:25 +03:00
_free ( dev ) ;
2005-05-09 21:02:52 +04:00
return NULL ;
}
2003-07-05 02:34:56 +04:00
}
2018-06-08 15:40:53 +03:00
} else if ( ! ( alias - > str = strdup ( filename ) ) ) {
2005-05-03 21:31:56 +04:00
log_error ( " filename strdup failed " ) ;
return NULL ;
}
2018-06-15 22:04:39 +03:00
_dev_init ( dev ) ;
2011-12-21 17:17:54 +04:00
dev - > flags = DEV_REGULAR | ( ( use_malloc ) ? DEV_ALLOCED : 0 ) ;
2008-11-04 01:14:30 +03:00
dm_list_add ( & dev - > aliases , & alias - > list ) ;
2003-07-05 02:34:56 +04:00
return dev ;
}
static struct device * _dev_create ( dev_t d )
2001-10-25 15:34:55 +04:00
{
struct device * dev ;
2011-12-21 17:14:54 +04:00
if ( ! ( dev = _zalloc ( 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
}
2011-12-21 17:17:54 +04:00
2018-06-15 22:04:39 +03:00
_dev_init ( dev ) ;
2001-10-25 15:34:55 +04:00
dev - > dev = d ;
2003-01-08 19:41:22 +03:00
2001-10-25 15:34:55 +04:00
return dev ;
}
2014-05-29 11:41:03 +04:00
void dev_set_preferred_name ( struct dm_str_list * sl , struct device * dev )
2007-04-26 20:44:59 +04:00
{
/*
* Don ' t interfere with ordering specified in config file .
*/
if ( _cache . preferred_names_matcher )
return ;
2013-01-08 02:30:29 +04:00
log_debug_devs ( " %s: New preferred name " , sl - > str ) ;
2008-11-04 01:14:30 +03:00
dm_list_del ( & sl - > list ) ;
dm_list_add_h ( & dev - > aliases , & sl - > list ) ;
2007-04-26 20:44:59 +04:00
}
2010-08-03 17:39:27 +04:00
/*
* Check whether path0 or path1 contains the subpath . The path that
* * does not * contain the subpath wins ( return 0 or 1 ) . If both paths
* contain the subpath , return - 1. If none of them contains the subpath ,
* return - 2.
*/
static int _builtin_preference ( const char * path0 , const char * path1 ,
size_t skip_prefix_count , const char * subpath )
{
size_t subpath_len ;
int r0 , r1 ;
subpath_len = strlen ( subpath ) ;
r0 = ! strncmp ( path0 + skip_prefix_count , subpath , subpath_len ) ;
r1 = ! strncmp ( path1 + skip_prefix_count , subpath , subpath_len ) ;
if ( ! r0 & & r1 )
/* path0 does not have the subpath - it wins */
return 0 ;
else if ( r0 & & ! r1 )
/* path1 does not have the subpath - it wins */
return 1 ;
else if ( r0 & & r1 )
/* both of them have the subpath */
return - 1 ;
/* no path has the subpath */
return - 2 ;
}
static int _apply_builtin_path_preference_rules ( const char * path0 , const char * path1 )
{
size_t devdir_len ;
int r ;
devdir_len = strlen ( _cache . dev_dir ) ;
if ( ! strncmp ( path0 , _cache . dev_dir , devdir_len ) & &
! strncmp ( path1 , _cache . dev_dir , devdir_len ) ) {
/*
* We ' re trying to achieve the ordering :
* / dev / block / < / dev / dm - * < / dev / disk / < / dev / mapper / < anything else
*/
/* Prefer any other path over /dev/block/ path. */
if ( ( r = _builtin_preference ( path0 , path1 , devdir_len , " block/ " ) ) > = - 1 )
return r ;
/* Prefer any other path over /dev/dm-* path. */
if ( ( r = _builtin_preference ( path0 , path1 , devdir_len , " dm- " ) ) > = - 1 )
return r ;
/* Prefer any other path over /dev/disk/ path. */
if ( ( r = _builtin_preference ( path0 , path1 , devdir_len , " disk/ " ) ) > = - 1 )
return r ;
/* Prefer any other path over /dev/mapper/ path. */
if ( ( r = _builtin_preference ( path0 , path1 , 0 , dm_dir ( ) ) ) > = - 1 )
return r ;
}
return - 1 ;
}
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 ;
2007-04-26 20:44:59 +04:00
int m0 , m1 ;
2003-01-04 00:11:23 +03:00
const char * p ;
char p0 [ PATH_MAX ] , p1 [ PATH_MAX ] ;
char * s0 , * s1 ;
struct stat stat0 , stat1 ;
2010-08-03 17:39:27 +04:00
int r ;
2003-01-04 00:11:23 +03:00
2007-04-26 21:14:57 +04:00
/*
* FIXME Better to compare patterns one - at - a - time against all names .
*/
2007-04-26 20:44:59 +04:00
if ( _cache . preferred_names_matcher ) {
2007-04-27 22:52:05 +04:00
m0 = dm_regex_match ( _cache . preferred_names_matcher , path0 ) ;
m1 = dm_regex_match ( _cache . preferred_names_matcher , path1 ) ;
2007-04-26 20:44:59 +04:00
if ( m0 ! = m1 ) {
if ( m0 < 0 )
return 1 ;
if ( m1 < 0 )
return 0 ;
if ( m0 < m1 )
return 1 ;
if ( m1 < m0 )
return 0 ;
}
}
2010-08-03 17:39:27 +04:00
/* Apply built-in preference rules first. */
if ( ( r = _apply_builtin_path_preference_rules ( path0 , path1 ) ) > = 0 )
return r ;
2010-05-25 02:53:48 +04:00
2003-01-04 00:11:23 +03:00
/* 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 ;
2018-03-02 18:25:37 +03:00
( void ) dm_strncpy ( p0 , path0 , sizeof ( p0 ) ) ;
( void ) dm_strncpy ( p1 , path1 , sizeof ( p1 ) ) ;
2012-01-20 15:01:56 +04:00
s0 = p0 + 1 ;
s1 = p1 + 1 ;
2003-01-04 00:11:23 +03:00
2011-12-21 17:21:09 +04:00
/*
* If we reach here , both paths are the same length .
* Now skip past identical path components .
*/
while ( * s0 & & * s0 = = * s1 )
s0 + + , s1 + + ;
2003-01-04 00:11:23 +03:00
/* 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 ? */
2021-04-22 16:06:48 +03:00
while ( s0 & & s1 ) {
if ( ( s0 = strchr ( s0 , ' / ' ) ) )
2003-01-04 00:11:23 +03:00
* s0 = ' \0 ' ;
2021-04-22 16:06:48 +03:00
if ( ( s1 = strchr ( s1 , ' / ' ) ) )
2003-01-04 00:11:23 +03:00
* s1 = ' \0 ' ;
2021-04-22 16:06:48 +03:00
2003-01-04 00:11:23 +03:00
if ( lstat ( p0 , & stat0 ) ) {
2007-07-23 14:45:49 +04:00
log_sys_very_verbose ( " lstat " , p0 ) ;
2003-01-04 00:11:23 +03:00
return 1 ;
}
if ( lstat ( p1 , & stat1 ) ) {
2007-07-23 14:45:49 +04:00
log_sys_very_verbose ( " lstat " , p1 ) ;
2003-01-04 00:11:23 +03:00
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 ;
2021-04-22 16:06:48 +03:00
if ( s0 )
2003-01-04 00:11:23 +03:00
* s0 + + = ' / ' ;
2021-04-22 16:06:48 +03:00
if ( s1 )
2003-01-04 00:11:23 +03:00
* s1 + + = ' / ' ;
}
/* ASCII comparison */
if ( strcmp ( path0 , path1 ) < 0 )
return 0 ;
2017-07-19 17:16:12 +03:00
return 1 ;
2003-01-04 00:11:23 +03:00
}
2021-02-07 21:30:26 +03:00
enum add_hash {
NO_HASH ,
HASH ,
REHASH
} ;
static int _add_alias ( struct device * dev , const char * path , enum add_hash hash )
2001-10-25 15:34:55 +04:00
{
2021-02-10 16:33:17 +03:00
struct dm_str_list * sl ;
2014-05-29 11:41:03 +04:00
struct dm_str_list * strl ;
2003-01-04 00:11:23 +03:00
const char * oldpath ;
int prefer_old = 1 ;
2001-10-25 15:34:55 +04:00
2021-02-07 21:30:26 +03:00
if ( hash = = REHASH )
dm_hash_remove ( _cache . names , path ) ;
2001-10-25 15:34:55 +04:00
2003-01-08 19:41:22 +03:00
/* Is name already there? */
2021-02-07 21:30:26 +03:00
dm_list_iterate_items ( strl , & dev - > aliases )
if ( ! strcmp ( strl - > str , path ) ) {
path = strl - > str ;
goto out ;
}
2022-02-25 01:03:21 +03:00
if ( ! ( path = _strdup ( path ) ) | |
2021-02-07 21:30:26 +03:00
! ( sl = _zalloc ( sizeof ( * sl ) ) ) ) {
log_error ( " Failed to add allias to dev cache. " ) ;
return 0 ;
2003-01-08 19:41:22 +03:00
}
2021-02-07 16:04:58 +03:00
if ( ! strncmp ( path , " /dev/nvme " , 9 ) ) {
log_debug ( " Found nvme device %s " , dev_name ( dev ) ) ;
dev - > flags | = DEV_IS_NVME ;
}
2011-04-22 16:05:32 +04:00
sl - > str = path ;
2001-10-25 15:34:55 +04:00
2008-11-04 01:14:30 +03:00
if ( ! dm_list_empty ( & dev - > aliases ) ) {
2014-05-29 11:41:03 +04:00
oldpath = dm_list_item ( dev - > aliases . n , struct dm_str_list ) - > str ;
2003-01-04 00:11:23 +03:00
prefer_old = _compare_paths ( path , oldpath ) ;
2018-05-30 01:02:27 +03:00
}
2003-01-04 00:11:23 +03:00
if ( prefer_old )
2008-11-04 01:14:30 +03:00
dm_list_add ( & dev - > aliases , & sl - > list ) ;
2003-01-04 00:11:23 +03:00
else
2008-11-04 01:14:30 +03:00
dm_list_add_h ( & dev - > aliases , & sl - > list ) ;
2021-02-07 21:30:26 +03:00
out :
if ( ( hash ! = NO_HASH ) & &
! dm_hash_insert ( _cache . names , path , dev ) ) {
log_error ( " Couldn't add name to hash in dev cache. " ) ;
return 0 ;
}
2003-01-04 00:11:23 +03:00
2001-10-25 15:34:55 +04:00
return 1 ;
}
2022-06-06 22:04:20 +03:00
int get_sysfs_binary ( const char * path , char * buf , size_t buf_size , int * retlen )
{
int ret ;
int fd ;
fd = open ( path , O_RDONLY ) ;
if ( fd < 0 )
return 0 ;
ret = read ( fd , buf , buf_size ) ;
close ( fd ) ;
if ( ret < = 0 )
return 0 ;
* retlen = ret ;
return 1 ;
}
device usage based on devices file
The LVM devices file lists devices that lvm can use. The default
file is /etc/lvm/devices/system.devices, and the lvmdevices(8)
command is used to add or remove device entries. If the file
does not exist, or if lvm.conf includes use_devicesfile=0, then
lvm will not use a devices file. When the devices file is in use,
the regex filter is not used, and the filter settings in lvm.conf
or on the command line are ignored.
LVM records devices in the devices file using hardware-specific
IDs, such as the WWID, and attempts to use subsystem-specific
IDs for virtual device types. These device IDs are also written
in the VG metadata. When no hardware or virtual ID is available,
lvm falls back using the unstable device name as the device ID.
When devnames are used, lvm performs extra scanning to find
devices if their devname changes, e.g. after reboot.
When proper device IDs are used, an lvm command will not look
at devices outside the devices file, but when devnames are used
as a fallback, lvm will scan devices outside the devices file
to locate PVs on renamed devices. A config setting
search_for_devnames can be used to control the scanning for
renamed devname entries.
Related to the devices file, the new command option
--devices <devnames> allows a list of devices to be specified for
the command to use, overriding the devices file. The listed
devices act as a sort of devices file in terms of limiting which
devices lvm will see and use. Devices that are not listed will
appear to be missing to the lvm command.
Multiple devices files can be kept in /etc/lvm/devices, which
allows lvm to be used with different sets of devices, e.g.
system devices do not need to be exposed to a specific application,
and the application can use lvm on its own set of devices that are
not exposed to the system. The option --devicesfile <filename> is
used to select the devices file to use with the command. Without
the option set, the default system devices file is used.
Setting --devicesfile "" causes lvm to not use a devices file.
An existing, empty devices file means lvm will see no devices.
The new command vgimportdevices adds PVs from a VG to the devices
file and updates the VG metadata to include the device IDs.
vgimportdevices -a will import all VGs into the system devices file.
LVM commands run by dmeventd not use a devices file by default,
and will look at all devices on the system. A devices file can
be created for dmeventd (/etc/lvm/devices/dmeventd.devices) If
this file exists, lvm commands run by dmeventd will use it.
Internal implementaion:
- device_ids_read - read the devices file
. add struct dev_use (du) to cmd->use_devices for each devices file entry
- dev_cache_scan - get /dev entries
. add struct device (dev) to dev_cache for each device on the system
- device_ids_match - match devices file entries to /dev entries
. match each du on cmd->use_devices to a dev in dev_cache, using device ID
. on match, set du->dev, dev->id, dev->flags MATCHED_USE_ID
- label_scan - read lvm headers and metadata from devices
. filters are applied, those that do not need data from the device
. filter-deviceid skips devs without MATCHED_USE_ID, i.e.
skips /dev entries that are not listed in the devices file
. read lvm label from dev
. filters are applied, those that use data from the device
. read lvm metadata from dev
. add info/vginfo structs for PVs/VGs (info is "lvmcache")
- device_ids_find_renamed_devs - handle devices with unstable devname ID
where devname changed
. this step only needed when devs do not have proper device IDs,
and their dev names change, e.g. after reboot sdb becomes sdc.
. detect incorrect match because PVID in the devices file entry
does not match the PVID found when the device was read above
. undo incorrect match between du and dev above
. search system devices for new location of PVID
. update devices file with new devnames for PVIDs on renamed devices
. label_scan the renamed devs
- continue with command processing
2020-06-23 21:25:41 +03:00
int get_sysfs_value ( const char * path , char * buf , size_t buf_size , int error_if_no_value )
2016-03-16 16:01:26 +03:00
{
FILE * fp ;
2016-03-21 17:48:36 +03:00
size_t len ;
2016-03-22 18:03:51 +03:00
int r = 0 ;
2016-03-16 16:01:26 +03:00
if ( ! ( fp = fopen ( path , " r " ) ) ) {
2020-03-12 18:18:51 +03:00
if ( error_if_no_value )
log_sys_error ( " fopen " , path ) ;
2016-03-16 16:01:26 +03:00
return 0 ;
}
if ( ! fgets ( buf , buf_size , fp ) ) {
2020-03-12 18:18:51 +03:00
if ( error_if_no_value )
log_sys_error ( " fgets " , path ) ;
2016-03-22 18:03:51 +03:00
goto out ;
2016-03-16 16:01:26 +03:00
}
2016-04-21 21:21:59 +03:00
if ( ( len = strlen ( buf ) ) & & buf [ len - 1 ] = = ' \n ' )
buf [ - - len ] = ' \0 ' ;
2016-03-21 17:48:36 +03:00
2016-04-21 21:21:59 +03:00
if ( ! len & & error_if_no_value )
log_error ( " _get_sysfs_value: %s: no value " , path ) ;
else
r = 1 ;
2016-03-22 18:03:51 +03:00
out :
2016-03-16 16:01:26 +03:00
if ( fclose ( fp ) )
2021-02-07 16:06:12 +03:00
log_sys_debug ( " fclose " , path ) ;
2016-03-16 16:01:26 +03:00
2016-03-22 18:03:51 +03:00
return r ;
2016-03-16 16:01:26 +03:00
}
skip indexing devices used by LVs in more commands
expands commit d5a06f9a7df5a43b2e2311db62ff8d3011217d74
"pvscan: skip indexing devices used by LVs"
The dev cache index is expensive and slow, so limit it
to commands that are used to observe the state of lvm.
The index is only used to print warnings about incorrect
device use by active LVs, e.g. if an LV is using a
multipath component device instead of the multipath
device. Commands that continue to use the index and
print the warnings:
fullreport, lvmdiskscan, vgs, lvs, pvs,
vgdisplay, lvdisplay, pvdisplay,
vgscan, lvscan, pvscan (excluding --cache)
A couple other commands were borrowing the DEV_USED_FOR_LV
flag to just check if a device was actively in use by LVs.
These are converted to the new dev_is_used_by_active_lv().
2021-07-08 00:59:56 +03:00
int get_dm_uuid_from_sysfs ( char * buf , size_t buf_size , int major , int minor )
2016-03-16 16:01:26 +03:00
{
char path [ PATH_MAX ] ;
if ( dm_snprintf ( path , sizeof ( path ) , " %sdev/block/%d:%d/dm/uuid " , dm_sysfs_dir ( ) , major , minor ) < 0 ) {
log_error ( " %d:%d: dm_snprintf failed for path to sysfs dm directory. " , major , minor ) ;
return 0 ;
}
device usage based on devices file
The LVM devices file lists devices that lvm can use. The default
file is /etc/lvm/devices/system.devices, and the lvmdevices(8)
command is used to add or remove device entries. If the file
does not exist, or if lvm.conf includes use_devicesfile=0, then
lvm will not use a devices file. When the devices file is in use,
the regex filter is not used, and the filter settings in lvm.conf
or on the command line are ignored.
LVM records devices in the devices file using hardware-specific
IDs, such as the WWID, and attempts to use subsystem-specific
IDs for virtual device types. These device IDs are also written
in the VG metadata. When no hardware or virtual ID is available,
lvm falls back using the unstable device name as the device ID.
When devnames are used, lvm performs extra scanning to find
devices if their devname changes, e.g. after reboot.
When proper device IDs are used, an lvm command will not look
at devices outside the devices file, but when devnames are used
as a fallback, lvm will scan devices outside the devices file
to locate PVs on renamed devices. A config setting
search_for_devnames can be used to control the scanning for
renamed devname entries.
Related to the devices file, the new command option
--devices <devnames> allows a list of devices to be specified for
the command to use, overriding the devices file. The listed
devices act as a sort of devices file in terms of limiting which
devices lvm will see and use. Devices that are not listed will
appear to be missing to the lvm command.
Multiple devices files can be kept in /etc/lvm/devices, which
allows lvm to be used with different sets of devices, e.g.
system devices do not need to be exposed to a specific application,
and the application can use lvm on its own set of devices that are
not exposed to the system. The option --devicesfile <filename> is
used to select the devices file to use with the command. Without
the option set, the default system devices file is used.
Setting --devicesfile "" causes lvm to not use a devices file.
An existing, empty devices file means lvm will see no devices.
The new command vgimportdevices adds PVs from a VG to the devices
file and updates the VG metadata to include the device IDs.
vgimportdevices -a will import all VGs into the system devices file.
LVM commands run by dmeventd not use a devices file by default,
and will look at all devices on the system. A devices file can
be created for dmeventd (/etc/lvm/devices/dmeventd.devices) If
this file exists, lvm commands run by dmeventd will use it.
Internal implementaion:
- device_ids_read - read the devices file
. add struct dev_use (du) to cmd->use_devices for each devices file entry
- dev_cache_scan - get /dev entries
. add struct device (dev) to dev_cache for each device on the system
- device_ids_match - match devices file entries to /dev entries
. match each du on cmd->use_devices to a dev in dev_cache, using device ID
. on match, set du->dev, dev->id, dev->flags MATCHED_USE_ID
- label_scan - read lvm headers and metadata from devices
. filters are applied, those that do not need data from the device
. filter-deviceid skips devs without MATCHED_USE_ID, i.e.
skips /dev entries that are not listed in the devices file
. read lvm label from dev
. filters are applied, those that use data from the device
. read lvm metadata from dev
. add info/vginfo structs for PVs/VGs (info is "lvmcache")
- device_ids_find_renamed_devs - handle devices with unstable devname ID
where devname changed
. this step only needed when devs do not have proper device IDs,
and their dev names change, e.g. after reboot sdb becomes sdc.
. detect incorrect match because PVID in the devices file entry
does not match the PVID found when the device was read above
. undo incorrect match between du and dev above
. search system devices for new location of PVID
. update devices file with new devnames for PVIDs on renamed devices
. label_scan the renamed devs
- continue with command processing
2020-06-23 21:25:41 +03:00
return get_sysfs_value ( path , buf , buf_size , 0 ) ;
2016-03-16 16:01:26 +03:00
}
static struct dm_list * _get_or_add_list_by_index_key ( struct dm_hash_table * idx , const char * key )
{
struct dm_list * list ;
if ( ( list = dm_hash_lookup ( idx , key ) ) )
return list ;
if ( ! ( list = _zalloc ( sizeof ( * list ) ) ) ) {
log_error ( " %s: failed to allocate device list for device cache index. " , key ) ;
return NULL ;
}
dm_list_init ( list ) ;
if ( ! dm_hash_insert ( idx , key , list ) ) {
log_error ( " %s: failed to insert device list to device cache index. " , key ) ;
return NULL ;
}
return list ;
}
2016-04-01 10:41:41 +03:00
static struct device * _insert_sysfs_dev ( dev_t devno , const char * devname )
{
2016-04-11 11:08:16 +03:00
static struct device _fake_dev = { . flags = DEV_USED_FOR_LV } ;
struct stat stat0 ;
2016-04-01 11:09:03 +03:00
char path [ PATH_MAX ] ;
2016-04-01 10:41:41 +03:00
struct device * dev ;
2016-04-01 11:09:03 +03:00
if ( dm_snprintf ( path , sizeof ( path ) , " %s%s " , _cache . dev_dir , devname ) < 0 ) {
log_error ( " _insert_sysfs_dev: %s: dm_snprintf failed " , devname ) ;
return NULL ;
}
2016-04-11 11:08:16 +03:00
if ( lstat ( path , & stat0 ) < 0 ) {
/* When device node does not exist return fake entry.
* This may happen when i . e . lvm2 device dir ! = / dev */
log_debug ( " %s: Not available device node " , path ) ;
return & _fake_dev ;
}
2016-04-01 10:41:41 +03:00
if ( ! ( dev = _dev_create ( devno ) ) )
return_NULL ;
2021-02-07 21:30:26 +03:00
if ( ! _add_alias ( dev , path , NO_HASH ) ) {
2016-04-01 11:09:03 +03:00
_free ( dev ) ;
2021-02-07 21:30:26 +03:00
return_NULL ;
2016-04-01 11:09:03 +03:00
}
2016-04-01 10:41:41 +03:00
if ( ! btree_insert ( _cache . sysfs_only_devices , ( uint32_t ) devno , dev ) ) {
log_error ( " Couldn't add device to binary tree of sysfs-only devices in dev cache. " ) ;
_free ( dev ) ;
return NULL ;
}
return dev ;
}
2016-03-22 18:03:51 +03:00
static struct device * _get_device_for_sysfs_dev_name_using_devno ( const char * devname )
2016-03-16 16:01:26 +03:00
{
char path [ PATH_MAX ] ;
char buf [ PATH_MAX ] ;
int major , minor ;
2016-03-30 11:29:35 +03:00
dev_t devno ;
struct device * dev ;
2016-03-16 16:01:26 +03:00
2016-03-22 18:03:51 +03:00
if ( dm_snprintf ( path , sizeof ( path ) , " %sblock/%s/dev " , dm_sysfs_dir ( ) , devname ) < 0 ) {
2016-04-01 10:41:41 +03:00
log_error ( " _get_device_for_sysfs_dev_name_using_devno: %s: dm_snprintf failed " , devname ) ;
2016-03-16 16:01:26 +03:00
return NULL ;
}
device usage based on devices file
The LVM devices file lists devices that lvm can use. The default
file is /etc/lvm/devices/system.devices, and the lvmdevices(8)
command is used to add or remove device entries. If the file
does not exist, or if lvm.conf includes use_devicesfile=0, then
lvm will not use a devices file. When the devices file is in use,
the regex filter is not used, and the filter settings in lvm.conf
or on the command line are ignored.
LVM records devices in the devices file using hardware-specific
IDs, such as the WWID, and attempts to use subsystem-specific
IDs for virtual device types. These device IDs are also written
in the VG metadata. When no hardware or virtual ID is available,
lvm falls back using the unstable device name as the device ID.
When devnames are used, lvm performs extra scanning to find
devices if their devname changes, e.g. after reboot.
When proper device IDs are used, an lvm command will not look
at devices outside the devices file, but when devnames are used
as a fallback, lvm will scan devices outside the devices file
to locate PVs on renamed devices. A config setting
search_for_devnames can be used to control the scanning for
renamed devname entries.
Related to the devices file, the new command option
--devices <devnames> allows a list of devices to be specified for
the command to use, overriding the devices file. The listed
devices act as a sort of devices file in terms of limiting which
devices lvm will see and use. Devices that are not listed will
appear to be missing to the lvm command.
Multiple devices files can be kept in /etc/lvm/devices, which
allows lvm to be used with different sets of devices, e.g.
system devices do not need to be exposed to a specific application,
and the application can use lvm on its own set of devices that are
not exposed to the system. The option --devicesfile <filename> is
used to select the devices file to use with the command. Without
the option set, the default system devices file is used.
Setting --devicesfile "" causes lvm to not use a devices file.
An existing, empty devices file means lvm will see no devices.
The new command vgimportdevices adds PVs from a VG to the devices
file and updates the VG metadata to include the device IDs.
vgimportdevices -a will import all VGs into the system devices file.
LVM commands run by dmeventd not use a devices file by default,
and will look at all devices on the system. A devices file can
be created for dmeventd (/etc/lvm/devices/dmeventd.devices) If
this file exists, lvm commands run by dmeventd will use it.
Internal implementaion:
- device_ids_read - read the devices file
. add struct dev_use (du) to cmd->use_devices for each devices file entry
- dev_cache_scan - get /dev entries
. add struct device (dev) to dev_cache for each device on the system
- device_ids_match - match devices file entries to /dev entries
. match each du on cmd->use_devices to a dev in dev_cache, using device ID
. on match, set du->dev, dev->id, dev->flags MATCHED_USE_ID
- label_scan - read lvm headers and metadata from devices
. filters are applied, those that do not need data from the device
. filter-deviceid skips devs without MATCHED_USE_ID, i.e.
skips /dev entries that are not listed in the devices file
. read lvm label from dev
. filters are applied, those that use data from the device
. read lvm metadata from dev
. add info/vginfo structs for PVs/VGs (info is "lvmcache")
- device_ids_find_renamed_devs - handle devices with unstable devname ID
where devname changed
. this step only needed when devs do not have proper device IDs,
and their dev names change, e.g. after reboot sdb becomes sdc.
. detect incorrect match because PVID in the devices file entry
does not match the PVID found when the device was read above
. undo incorrect match between du and dev above
. search system devices for new location of PVID
. update devices file with new devnames for PVIDs on renamed devices
. label_scan the renamed devs
- continue with command processing
2020-06-23 21:25:41 +03:00
if ( ! get_sysfs_value ( path , buf , sizeof ( buf ) , 1 ) )
2016-03-16 16:01:26 +03:00
return_NULL ;
if ( sscanf ( buf , " %d:%d " , & major , & minor ) ! = 2 ) {
2016-04-01 10:41:41 +03:00
log_error ( " _get_device_for_sysfs_dev_name_using_devno: %s: failed to get major and minor number " , devname ) ;
2016-03-16 16:01:26 +03:00
return NULL ;
}
2018-11-03 19:13:10 +03:00
devno = MKDEV ( major , minor ) ;
2016-03-30 11:29:35 +03:00
if ( ! ( dev = ( struct device * ) btree_lookup ( _cache . devices , ( uint32_t ) devno ) ) ) {
/*
* If we get here , it means the device is referenced in sysfs , but it ' s not yet in / dev .
* This may happen in some rare cases right after LVs get created - we sync with udev
* ( or alternatively we create / dev content ourselves ) while VG lock is held . However ,
* dev scan is done without VG lock so devices may already be in sysfs , but / dev may
* not be updated yet if we call LVM command right after LV creation . This is not a
* problem with devtmpfs as there ' s at least kernel name for device in / dev as soon
* as the sysfs item exists , but we still support environments without devtmpfs or
* where different directory for dev nodes is used ( e . g . our test suite ) . So track
2016-03-31 16:14:01 +03:00
* such devices in _cache . sysfs_only_devices hash for the vgid / lvid check to work still .
2016-03-30 11:29:35 +03:00
*/
2016-04-01 10:41:41 +03:00
if ( ! ( dev = ( struct device * ) btree_lookup ( _cache . sysfs_only_devices , ( uint32_t ) devno ) ) & &
! ( dev = _insert_sysfs_dev ( devno , devname ) ) )
return_NULL ;
2016-03-30 11:29:35 +03:00
}
return dev ;
2016-03-16 16:01:26 +03:00
}
# define NOT_LVM_UUID "-"
static int _get_vgid_and_lvid_for_dev ( struct device * dev )
{
2016-03-22 12:28:01 +03:00
static size_t lvm_prefix_len = sizeof ( UUID_PREFIX ) - 1 ;
static size_t lvm_uuid_len = sizeof ( UUID_PREFIX ) - 1 + 2 * ID_LEN ;
2016-03-16 16:01:26 +03:00
char uuid [ DM_UUID_LEN ] ;
2016-03-22 12:28:01 +03:00
size_t uuid_len ;
2016-03-16 16:01:26 +03:00
skip indexing devices used by LVs in more commands
expands commit d5a06f9a7df5a43b2e2311db62ff8d3011217d74
"pvscan: skip indexing devices used by LVs"
The dev cache index is expensive and slow, so limit it
to commands that are used to observe the state of lvm.
The index is only used to print warnings about incorrect
device use by active LVs, e.g. if an LV is using a
multipath component device instead of the multipath
device. Commands that continue to use the index and
print the warnings:
fullreport, lvmdiskscan, vgs, lvs, pvs,
vgdisplay, lvdisplay, pvdisplay,
vgscan, lvscan, pvscan (excluding --cache)
A couple other commands were borrowing the DEV_USED_FOR_LV
flag to just check if a device was actively in use by LVs.
These are converted to the new dev_is_used_by_active_lv().
2021-07-08 00:59:56 +03:00
if ( ! get_dm_uuid_from_sysfs ( uuid , sizeof ( uuid ) , ( int ) MAJOR ( dev - > dev ) , ( int ) MINOR ( dev - > dev ) ) )
2016-03-16 16:01:26 +03:00
return_0 ;
2016-03-22 12:28:01 +03:00
uuid_len = strlen ( uuid ) ;
/*
* UUID for LV is either " LVM-<vg_uuid><lv_uuid> " or " LVM-<vg_uuid><lv_uuid>-<suffix> " ,
* where vg_uuid and lv_uuid has length of ID_LEN and suffix len is not restricted
* ( only restricted by whole DM UUID max len ) .
*/
if ( ( ( uuid_len = = lvm_uuid_len ) | |
( ( uuid_len > lvm_uuid_len ) & & ( uuid [ lvm_uuid_len ] = = ' - ' ) ) ) & &
2016-03-16 16:01:26 +03:00
! strncmp ( uuid , UUID_PREFIX , lvm_prefix_len ) ) {
/* Separate VGID and LVID part from DM UUID. */
if ( ! ( dev - > vgid = dm_pool_strndup ( _cache . mem , uuid + lvm_prefix_len , ID_LEN ) ) | |
! ( dev - > lvid = dm_pool_strndup ( _cache . mem , uuid + lvm_prefix_len + ID_LEN , ID_LEN ) ) )
return_0 ;
} else
dev - > vgid = dev - > lvid = NOT_LVM_UUID ;
return 1 ;
}
static int _index_dev_by_vgid_and_lvid ( struct device * dev )
{
const char * devname = dev_name ( dev ) ;
char devpath [ PATH_MAX ] ;
char path [ PATH_MAX ] ;
DIR * d ;
struct dirent * dirent ;
struct device * holder_dev ;
struct dm_list * vgid_list , * lvid_list ;
struct device_list * dl_vgid , * dl_lvid ;
int r = 0 ;
2016-03-30 15:35:06 +03:00
if ( dev - > flags & DEV_USED_FOR_LV )
2016-03-30 11:39:30 +03:00
/* already indexed */
return 1 ;
2016-03-16 16:01:26 +03:00
/* Get holders for device. */
if ( dm_snprintf ( path , sizeof ( path ) , " %sdev/block/%d:%d/holders/ " , dm_sysfs_dir ( ) , ( int ) MAJOR ( dev - > dev ) , ( int ) MINOR ( dev - > dev ) ) < 0 ) {
log_error ( " %s: dm_snprintf failed for path to holders directory. " , devname ) ;
return 0 ;
}
if ( ! ( d = opendir ( path ) ) ) {
if ( errno = = ENOENT ) {
log_debug ( " %s: path does not exist, skipping " , path ) ;
return 1 ;
}
log_sys_error ( " opendir " , path ) ;
return 0 ;
}
/* Iterate over device's holders and look for LVs. */
while ( ( dirent = readdir ( d ) ) ) {
if ( ! strcmp ( " . " , dirent - > d_name ) | |
! strcmp ( " .. " , dirent - > d_name ) )
continue ;
if ( dm_snprintf ( devpath , sizeof ( devpath ) , " %s%s " , _cache . dev_dir , dirent - > d_name ) = = - 1 ) {
log_error ( " %s: dm_snprintf failed for holder %s device path. " , devname , dirent - > d_name ) ;
goto out ;
}
if ( ! ( holder_dev = ( struct device * ) dm_hash_lookup ( _cache . names , devpath ) ) ) {
/*
* Cope with situation where canonical / < dev_dir > / < dirent - > d_name >
* does not exist , but some other node name or symlink exists in
* non - standard environments - someone renaming the nodes or using
* mknod with different dev names than actual kernel names .
* This looks up struct device by major : minor pair which we get
* by looking at / sys / block / < dirent - > d_name > / dev sysfs attribute .
*/
if ( ! ( holder_dev = _get_device_for_sysfs_dev_name_using_devno ( dirent - > d_name ) ) ) {
log_error ( " %s: failed to find associated device structure for holder %s. " , devname , devpath ) ;
goto out ;
}
}
/* We're only interested in a holder which is a DM device. */
if ( ! dm_is_dm_major ( MAJOR ( holder_dev - > dev ) ) )
continue ;
/*
* And if it ' s a DM device , we ' re only interested in a holder which is an LVM device .
* Get the VG UUID and LV UUID if we don ' t have that already .
*/
if ( ! holder_dev - > vgid & & ! _get_vgid_and_lvid_for_dev ( holder_dev ) )
goto_out ;
if ( * holder_dev - > vgid = = * NOT_LVM_UUID )
continue ;
/*
* Do not add internal LV devices to index .
* If a device is internal , the holder has the same VG UUID as the device .
*/
if ( dm_is_dm_major ( MAJOR ( dev - > dev ) ) ) {
if ( ! dev - > vgid & & ! _get_vgid_and_lvid_for_dev ( dev ) )
goto_out ;
if ( * dev - > vgid ! = * NOT_LVM_UUID & & ! strcmp ( holder_dev - > vgid , dev - > vgid ) )
continue ;
}
if ( ! ( vgid_list = _get_or_add_list_by_index_key ( _cache . vgid_index , holder_dev - > vgid ) ) | |
! ( lvid_list = _get_or_add_list_by_index_key ( _cache . lvid_index , holder_dev - > lvid ) ) )
goto_out ;
/* Create dev list items for the holder device. */
if ( ! ( dl_vgid = _zalloc ( sizeof ( * dl_vgid ) ) ) | |
! ( dl_lvid = _zalloc ( sizeof ( * dl_lvid ) ) ) ) {
log_error ( " %s: failed to allocate dev list item. " , devname ) ;
goto out ;
}
dl_vgid - > dev = dl_lvid - > dev = dev ;
/* Add dev list item to VGID device list if it's not there already. */
if ( ! ( dev - > flags & DEV_USED_FOR_LV ) )
dm_list_add ( vgid_list , & dl_vgid - > list ) ;
/* Add dev list item to LVID device list. */
dm_list_add ( lvid_list , & dl_lvid - > list ) ;
/* Mark device as used == also indexed in dev cache by VGID and LVID. */
dev - > flags | = DEV_USED_FOR_LV ;
}
r = 1 ;
out :
if ( closedir ( d ) )
2021-03-09 13:08:47 +03:00
log_sys_debug ( " closedir " , path ) ;
2016-03-16 16:01:26 +03:00
return r ;
}
struct dm_list * dev_cache_get_dev_list_for_vgid ( const char * vgid )
{
return dm_hash_lookup ( _cache . vgid_index , vgid ) ;
}
struct dm_list * dev_cache_get_dev_list_for_lvid ( const char * lvid )
{
return dm_hash_lookup ( _cache . lvid_index , lvid ) ;
}
2018-05-30 01:02:27 +03:00
/*
* Scanning code calls this when it fails to open a device using
* this path . The path is dropped from dev - cache . In the next
* dev_cache_scan it may be added again , but it could be for a
* different device .
*/
void dev_cache_failed_path ( struct device * dev , const char * path )
{
struct dm_str_list * strl ;
2018-11-03 18:48:20 +03:00
if ( dm_hash_lookup ( _cache . names , path ) )
2018-05-30 01:02:27 +03:00
dm_hash_remove ( _cache . names , path ) ;
dm_list_iterate_items ( strl , & dev - > aliases ) {
if ( ! strcmp ( strl - > str , path ) ) {
dm_list_del ( & strl - > list ) ;
break ;
}
}
}
2001-10-25 15:34:55 +04:00
/*
* 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 ;
2018-05-30 01:02:27 +03:00
struct device * dev_by_devt ;
struct device * dev_by_path ;
2005-05-03 21:28:23 +04:00
2018-05-30 01:02:27 +03:00
dev_by_devt = ( struct device * ) btree_lookup ( _cache . devices , ( uint32_t ) d ) ;
dev_by_path = ( struct device * ) dm_hash_lookup ( _cache . names , path ) ;
dev = dev_by_devt ;
/*
* Existing device , existing path points to the same device .
*/
if ( dev_by_devt & & dev_by_path & & ( dev_by_devt = = dev_by_path ) ) {
log_debug_devs ( " Found dev %d:%d %s - exists. %.8s " ,
( int ) MAJOR ( d ) , ( int ) MINOR ( d ) , path , dev - > pvid ) ;
return 1 ;
}
/*
* No device or path found , add devt to cache . devices , add name to cache . names .
*/
if ( ! dev_by_devt & & ! dev_by_path ) {
log_debug_devs ( " Found dev %d:%d %s - new. " ,
( int ) MAJOR ( d ) , ( int ) MINOR ( d ) , path ) ;
2016-04-01 12:34:09 +03:00
if ( ! ( dev = ( struct device * ) btree_lookup ( _cache . sysfs_only_devices , ( uint32_t ) d ) ) ) {
/* create new device */
2018-06-21 17:33:21 +03:00
if ( ! ( dev = _dev_create ( d ) ) )
2008-01-30 16:19:47 +03:00
return_0 ;
2016-04-01 12:34:09 +03:00
}
2001-10-25 15:34:55 +04:00
2002-12-20 02:25:55 +03:00
if ( ! ( btree_insert ( _cache . devices , ( uint32_t ) d , dev ) ) ) {
2009-07-16 00:02:46 +04:00
log_error ( " Couldn't insert device into binary tree. " ) ;
2001-10-25 15:34:55 +04:00
_free ( dev ) ;
return 0 ;
}
2021-02-07 21:30:26 +03:00
if ( ! _add_alias ( dev , path , HASH ) )
return_0 ;
2018-05-30 01:02:27 +03:00
2017-07-16 21:56:58 +03:00
return 1 ;
}
2018-05-30 01:02:27 +03:00
/*
* Existing device , path is new , add path as a new alias for the device .
*/
if ( dev_by_devt & & ! dev_by_path ) {
log_debug_devs ( " Found dev %d:%d %s - new alias. " ,
( int ) MAJOR ( d ) , ( int ) MINOR ( d ) , path ) ;
2021-02-07 21:30:26 +03:00
if ( ! _add_alias ( dev , path , HASH ) )
return_0 ;
2018-05-30 01:02:27 +03:00
return 1 ;
2011-04-22 16:05:32 +04:00
}
2018-05-30 01:02:27 +03:00
/*
* No existing device , but path exists and previously pointed
* to a different device .
*/
if ( ! dev_by_devt & & dev_by_path ) {
log_debug_devs ( " Found dev %d:%d %s - new device, path was previously %d:%d. " ,
( int ) MAJOR ( d ) , ( int ) MINOR ( d ) , path ,
( int ) MAJOR ( dev_by_path - > dev ) , ( int ) MINOR ( dev_by_path - > dev ) ) ;
if ( ! ( dev = ( struct device * ) btree_lookup ( _cache . sysfs_only_devices , ( uint32_t ) d ) ) ) {
/* create new device */
2018-06-21 17:33:21 +03:00
if ( ! ( dev = _dev_create ( d ) ) )
2018-05-30 01:02:27 +03:00
return_0 ;
}
if ( ! ( btree_insert ( _cache . devices , ( uint32_t ) d , dev ) ) ) {
log_error ( " Couldn't insert device into binary tree. " ) ;
_free ( dev ) ;
return 0 ;
}
2021-02-07 21:30:26 +03:00
if ( ! _add_alias ( dev , path , REHASH ) )
return_0 ;
2018-05-30 01:02:27 +03:00
return 1 ;
2001-10-25 15:34:55 +04:00
}
2018-05-30 01:02:27 +03:00
/*
* Existing device , and path exists and previously pointed to
* a different device .
*/
if ( dev_by_devt & & dev_by_path ) {
log_debug_devs ( " Found dev %d:%d %s - existing device, path was previously %d:%d. " ,
( int ) MAJOR ( d ) , ( int ) MINOR ( d ) , path ,
( int ) MAJOR ( dev_by_path - > dev ) , ( int ) MINOR ( dev_by_path - > dev ) ) ;
2021-02-07 21:30:26 +03:00
if ( ! _add_alias ( dev , path , REHASH ) )
return_0 ;
2018-05-30 01:02:27 +03:00
return 1 ;
2001-10-25 18:19:39 +04:00
}
2018-05-30 01:02:27 +03:00
log_error ( " Found dev %d:%d %s - failed to use. " , ( int ) MAJOR ( d ) , ( int ) MINOR ( d ) , path ) ;
return 0 ;
2001-10-25 15:34:55 +04:00
}
2001-10-08 17:58:52 +04:00
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 ;
2021-09-10 23:32:44 +03:00
struct dirent * * dirent = NULL ;
2021-02-27 19:09:38 +03:00
char path [ PATH_MAX ] ;
size_t len ;
if ( ! dm_strncpy ( path , dir , sizeof ( path ) - 1 ) ) {
log_debug_devs ( " Dir path %s is too long " , path ) ;
return 0 ;
}
_collapse_slashes ( path ) ;
len = strlen ( path ) ;
if ( len & & path [ len - 1 ] ! = ' / ' )
path [ len + + ] = ' / ' ;
2001-10-03 16:41:29 +04:00
2021-02-27 19:09:38 +03:00
setlocale ( LC_COLLATE , " C " ) ; /* Avoid sorting by locales */
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 + + ) {
2021-02-27 19:09:38 +03:00
if ( dirent [ n ] - > d_name [ 0 ] = = ' . ' )
2001-10-25 15:34:55 +04:00
continue ;
2001-10-03 16:41:29 +04:00
2021-02-27 19:09:38 +03:00
if ( ! dm_strncpy ( path + len , dirent [ n ] - > d_name , sizeof ( path ) - len ) ) {
log_debug_devs ( " Path %s/%s is too long. " , dir , dirent [ n ] - > d_name ) ;
r = 0 ;
continue ;
}
2001-10-03 16:41:29 +04:00
2011-11-25 13:01:37 +04:00
r & = _insert ( path , NULL , 1 , 0 ) ;
2021-02-27 19:09:38 +03:00
}
2001-10-03 16:41:29 +04:00
2021-02-27 19:09:38 +03:00
for ( n = 0 ; n < dirent_count ; n + + )
2001-10-25 15:34:55 +04:00
free ( dirent [ n ] ) ;
free ( dirent ) ;
}
2021-02-27 19:09:38 +03:00
setlocale ( LC_COLLATE , " " ) ;
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
}
2016-04-01 12:34:09 +03:00
static int _dev_cache_iterate_devs_for_index ( void )
2016-03-30 14:06:12 +03:00
{
struct btree_iter * iter = btree_first ( _cache . devices ) ;
struct device * dev ;
int r = 1 ;
while ( iter ) {
dev = btree_get_data ( iter ) ;
if ( ! _index_dev_by_vgid_and_lvid ( dev ) )
r = 0 ;
iter = btree_next ( iter ) ;
}
return r ;
}
2016-04-01 18:04:11 +03:00
static int _dev_cache_iterate_sysfs_for_index ( const char * path )
2016-04-01 12:34:09 +03:00
{
char devname [ PATH_MAX ] ;
DIR * d ;
struct dirent * dirent ;
int major , minor ;
dev_t devno ;
struct device * dev ;
int partial_failure = 0 ;
int r = 0 ;
if ( ! ( d = opendir ( path ) ) ) {
log_sys_error ( " opendir " , path ) ;
return 0 ;
}
while ( ( dirent = readdir ( d ) ) ) {
if ( ! strcmp ( " . " , dirent - > d_name ) | |
! strcmp ( " .. " , dirent - > d_name ) )
continue ;
if ( sscanf ( dirent - > d_name , " %d:%d " , & major , & minor ) ! = 2 ) {
log_error ( " _dev_cache_iterate_sysfs_for_index: %s: failed "
" to get major and minor number " , dirent - > d_name ) ;
partial_failure = 1 ;
continue ;
}
2018-11-03 19:13:10 +03:00
devno = MKDEV ( major , minor ) ;
2016-04-01 12:34:09 +03:00
if ( ! ( dev = ( struct device * ) btree_lookup ( _cache . devices , ( uint32_t ) devno ) ) & &
! ( dev = ( struct device * ) btree_lookup ( _cache . sysfs_only_devices , ( uint32_t ) devno ) ) ) {
if ( ! dm_device_get_name ( major , minor , 1 , devname , sizeof ( devname ) ) | |
! ( dev = _insert_sysfs_dev ( devno , devname ) ) ) {
partial_failure = 1 ;
continue ;
}
}
if ( ! _index_dev_by_vgid_and_lvid ( dev ) )
partial_failure = 1 ;
}
r = ! partial_failure ;
if ( closedir ( d ) )
2021-02-07 16:06:12 +03:00
log_sys_debug ( " closedir " , path ) ;
2016-04-01 12:34:09 +03:00
return r ;
}
device usage based on devices file
The LVM devices file lists devices that lvm can use. The default
file is /etc/lvm/devices/system.devices, and the lvmdevices(8)
command is used to add or remove device entries. If the file
does not exist, or if lvm.conf includes use_devicesfile=0, then
lvm will not use a devices file. When the devices file is in use,
the regex filter is not used, and the filter settings in lvm.conf
or on the command line are ignored.
LVM records devices in the devices file using hardware-specific
IDs, such as the WWID, and attempts to use subsystem-specific
IDs for virtual device types. These device IDs are also written
in the VG metadata. When no hardware or virtual ID is available,
lvm falls back using the unstable device name as the device ID.
When devnames are used, lvm performs extra scanning to find
devices if their devname changes, e.g. after reboot.
When proper device IDs are used, an lvm command will not look
at devices outside the devices file, but when devnames are used
as a fallback, lvm will scan devices outside the devices file
to locate PVs on renamed devices. A config setting
search_for_devnames can be used to control the scanning for
renamed devname entries.
Related to the devices file, the new command option
--devices <devnames> allows a list of devices to be specified for
the command to use, overriding the devices file. The listed
devices act as a sort of devices file in terms of limiting which
devices lvm will see and use. Devices that are not listed will
appear to be missing to the lvm command.
Multiple devices files can be kept in /etc/lvm/devices, which
allows lvm to be used with different sets of devices, e.g.
system devices do not need to be exposed to a specific application,
and the application can use lvm on its own set of devices that are
not exposed to the system. The option --devicesfile <filename> is
used to select the devices file to use with the command. Without
the option set, the default system devices file is used.
Setting --devicesfile "" causes lvm to not use a devices file.
An existing, empty devices file means lvm will see no devices.
The new command vgimportdevices adds PVs from a VG to the devices
file and updates the VG metadata to include the device IDs.
vgimportdevices -a will import all VGs into the system devices file.
LVM commands run by dmeventd not use a devices file by default,
and will look at all devices on the system. A devices file can
be created for dmeventd (/etc/lvm/devices/dmeventd.devices) If
this file exists, lvm commands run by dmeventd will use it.
Internal implementaion:
- device_ids_read - read the devices file
. add struct dev_use (du) to cmd->use_devices for each devices file entry
- dev_cache_scan - get /dev entries
. add struct device (dev) to dev_cache for each device on the system
- device_ids_match - match devices file entries to /dev entries
. match each du on cmd->use_devices to a dev in dev_cache, using device ID
. on match, set du->dev, dev->id, dev->flags MATCHED_USE_ID
- label_scan - read lvm headers and metadata from devices
. filters are applied, those that do not need data from the device
. filter-deviceid skips devs without MATCHED_USE_ID, i.e.
skips /dev entries that are not listed in the devices file
. read lvm label from dev
. filters are applied, those that use data from the device
. read lvm metadata from dev
. add info/vginfo structs for PVs/VGs (info is "lvmcache")
- device_ids_find_renamed_devs - handle devices with unstable devname ID
where devname changed
. this step only needed when devs do not have proper device IDs,
and their dev names change, e.g. after reboot sdb becomes sdc.
. detect incorrect match because PVID in the devices file entry
does not match the PVID found when the device was read above
. undo incorrect match between du and dev above
. search system devices for new location of PVID
. update devices file with new devnames for PVIDs on renamed devices
. label_scan the renamed devs
- continue with command processing
2020-06-23 21:25:41 +03:00
static int dev_cache_index_devs ( void )
2016-04-01 12:34:09 +03:00
{
2016-04-01 18:04:11 +03:00
static int sysfs_has_dev_block = - 1 ;
char path [ PATH_MAX ] ;
if ( dm_snprintf ( path , sizeof ( path ) , " %sdev/block " , dm_sysfs_dir ( ) ) < 0 ) {
log_error ( " dev_cache_index_devs: dm_snprintf failed. " ) ;
return 0 ;
}
/* Skip indexing if /sys/dev/block is not available.*/
if ( sysfs_has_dev_block = = - 1 ) {
struct stat info ;
if ( stat ( path , & info ) = = 0 )
sysfs_has_dev_block = 1 ;
else {
if ( errno = = ENOENT ) {
sysfs_has_dev_block = 0 ;
return 1 ;
}
2017-07-19 17:16:12 +03:00
2021-02-07 16:06:12 +03:00
log_sys_debug ( " stat " , path ) ;
2017-07-19 17:16:12 +03:00
return 0 ;
2016-04-01 18:04:11 +03:00
}
} else if ( ! sysfs_has_dev_block )
return 1 ;
2016-04-12 12:46:41 +03:00
if ( obtain_device_list_from_udev ( ) & &
udev_get_library_context ( ) )
return _dev_cache_iterate_devs_for_index ( ) ; /* with udev */
2016-04-01 12:34:09 +03:00
2016-04-12 12:46:41 +03:00
return _dev_cache_iterate_sysfs_for_index ( path ) ;
2016-04-01 12:34:09 +03:00
}
2011-04-22 16:05:32 +04:00
# ifdef UDEV_SYNC_SUPPORT
static int _device_in_udev_db ( const dev_t d )
{
struct udev * udev ;
struct udev_device * udev_device ;
if ( ! ( udev = udev_get_library_context ( ) ) )
return_0 ;
if ( ( udev_device = udev_device_new_from_devnum ( udev , ' b ' , d ) ) ) {
udev_device_unref ( udev_device ) ;
return 1 ;
}
return 0 ;
}
static int _insert_udev_dir ( struct udev * udev , const char * dir )
{
struct udev_enumerate * udev_enum = NULL ;
struct udev_list_entry * device_entry , * symlink_entry ;
2012-04-11 13:12:02 +04:00
const char * entry_name , * node_name , * symlink_name ;
2011-04-22 16:05:32 +04:00
struct udev_device * device ;
int r = 1 ;
2017-10-30 15:16:50 +03:00
if ( ! ( udev_enum = udev_enumerate_new ( udev ) ) ) {
log_error ( " Failed to udev_enumerate_new. " ) ;
return 0 ;
}
if ( udev_enumerate_add_match_subsystem ( udev_enum , " block " ) ) {
log_error ( " Failed to udev_enumerate_add_match_subsystem. " ) ;
goto out ;
}
2011-04-22 16:05:32 +04:00
2017-10-30 15:16:50 +03:00
if ( udev_enumerate_scan_devices ( udev_enum ) ) {
log_error ( " Failed to udev_enumerate_scan_devices. " ) ;
goto out ;
}
2011-04-22 16:05:32 +04:00
2012-04-11 13:12:02 +04:00
/*
* Report any missing information as " log_very_verbose " only , do not
* report it as a " warning " or " error " - the record could be removed
* by the time we ask for more info ( node name , symlink name . . . ) .
* Whatever removes * any * block device in the system ( even unrelated
* to our operation ) , we would have a warning / error on output then .
* That could be misleading . If there ' s really any problem with missing
* information from udev db , we can still have a look at the verbose log .
*/
2011-04-22 16:05:32 +04:00
udev_list_entry_foreach ( device_entry , udev_enumerate_get_list_entry ( udev_enum ) ) {
2012-04-11 13:12:02 +04:00
entry_name = udev_list_entry_get_name ( device_entry ) ;
if ( ! ( device = udev_device_new_from_syspath ( udev , entry_name ) ) ) {
log_very_verbose ( " udev failed to return a device for entry %s. " ,
entry_name ) ;
2012-03-04 21:40:59 +04:00
continue ;
}
2011-04-22 16:05:32 +04:00
2012-03-04 21:40:59 +04:00
if ( ! ( node_name = udev_device_get_devnode ( device ) ) )
2012-04-11 13:12:02 +04:00
log_very_verbose ( " udev failed to return a device node for entry %s. " ,
entry_name ) ;
2012-03-04 21:40:59 +04:00
else
2011-11-25 13:01:37 +04:00
r & = _insert ( node_name , NULL , 0 , 0 ) ;
2011-04-22 16:05:32 +04:00
udev_list_entry_foreach ( symlink_entry , udev_device_get_devlinks_list_entry ( device ) ) {
2012-03-04 21:40:59 +04:00
if ( ! ( symlink_name = udev_list_entry_get_name ( symlink_entry ) ) )
2012-04-11 13:12:02 +04:00
log_very_verbose ( " udev failed to return a symlink name for entry %s. " ,
entry_name ) ;
2012-03-04 21:40:59 +04:00
else
2011-11-25 13:01:37 +04:00
r & = _insert ( symlink_name , NULL , 0 , 0 ) ;
2011-04-22 16:05:32 +04:00
}
udev_device_unref ( device ) ;
}
2017-10-30 15:16:50 +03:00
out :
2011-04-22 16:05:32 +04:00
udev_enumerate_unref ( udev_enum ) ;
2017-10-30 15:16:50 +03:00
return r ;
2011-04-22 16:05:32 +04:00
}
static void _insert_dirs ( struct dm_list * dirs )
{
struct dir_list * dl ;
2019-10-22 11:43:11 +03:00
struct udev * udev = NULL ;
2011-04-22 16:05:32 +04:00
int with_udev ;
2021-10-18 22:08:19 +03:00
struct stat tinfo ;
2011-04-22 16:05:32 +04:00
with_udev = obtain_device_list_from_udev ( ) & &
( udev = udev_get_library_context ( ) ) ;
dm_list_iterate_items ( dl , & _cache . dirs ) {
2021-10-18 22:08:19 +03:00
if ( stat ( dl - > dir , & tinfo ) < 0 ) {
log_warn ( " WARNING: Cannot use dir %s, %s. " ,
dl - > dir , strerror ( errno ) ) ;
continue ;
}
_cache . st_dev = tinfo . st_dev ;
2011-04-22 16:05:32 +04:00
if ( with_udev ) {
if ( ! _insert_udev_dir ( udev , dl - > dir ) )
2013-01-08 02:30:29 +04:00
log_debug_devs ( " %s: Failed to insert devices from "
" udev-managed directory to device "
" cache fully " , dl - > dir ) ;
2011-04-22 16:05:32 +04:00
}
else if ( ! _insert_dir ( dl - > dir ) )
2013-01-08 02:30:29 +04:00
log_debug_devs ( " %s: Failed to insert devices to "
" device cache fully " , dl - > dir ) ;
2011-04-22 16:05:32 +04:00
}
}
# else /* UDEV_SYNC_SUPPORT */
static int _device_in_udev_db ( const dev_t d )
{
return 0 ;
}
static void _insert_dirs ( struct dm_list * dirs )
{
struct dir_list * dl ;
2021-10-18 22:50:56 +03:00
struct stat tinfo ;
2011-04-22 16:05:32 +04:00
2021-10-18 22:50:56 +03:00
dm_list_iterate_items ( dl , & _cache . dirs ) {
if ( stat ( dl - > dir , & tinfo ) < 0 ) {
log_warn ( " WARNING: Cannot use dir %s, %s. " ,
dl - > dir , strerror ( errno ) ) ;
continue ;
}
_cache . st_dev = tinfo . st_dev ;
2011-04-22 16:05:32 +04:00
_insert_dir ( dl - > dir ) ;
2021-10-18 22:50:56 +03:00
}
2011-04-22 16:05:32 +04:00
}
# endif /* UDEV_SYNC_SUPPORT */
2011-11-25 13:01:37 +04:00
static int _insert ( const char * path , const struct stat * info ,
int rec , int check_with_udev_db )
2001-10-08 17:58:52 +04:00
{
2011-11-25 13:01:37 +04:00
struct stat tinfo ;
2001-10-08 17:58:52 +04:00
2011-11-25 13:01:37 +04:00
if ( ! info ) {
if ( stat ( path , & tinfo ) < 0 ) {
log_sys_very_verbose ( " stat " , path ) ;
return 0 ;
}
info = & tinfo ;
2001-10-08 17:58:52 +04:00
}
2011-11-25 13:01:37 +04:00
if ( check_with_udev_db & & ! _device_in_udev_db ( info - > st_rdev ) ) {
2011-04-22 16:05:32 +04:00
log_very_verbose ( " %s: Not in udev db " , path ) ;
return 0 ;
}
2011-11-25 13:01:37 +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 */
2011-11-25 13:01:37 +04:00
if ( lstat ( path , & tinfo ) < 0 ) {
2004-02-13 21:55:43 +03:00
log_sys_very_verbose ( " lstat " , path ) ;
return 0 ;
}
2011-11-25 13:01:37 +04:00
if ( S_ISLNK ( tinfo . st_mode ) ) {
2013-01-08 02:30:29 +04:00
log_debug_devs ( " %s: Symbolic link to directory " , path ) ;
2013-12-05 14:11:26 +04:00
return 1 ;
2004-02-13 21:55:43 +03:00
}
2021-10-18 22:08:19 +03:00
if ( info - > st_dev ! = _cache . st_dev ) {
log_debug_devs ( " %s: Different filesystem in directory " , path ) ;
return 1 ;
}
2013-12-05 14:11:26 +04:00
if ( rec & & ! _insert_dir ( path ) )
2020-09-26 15:52:27 +03:00
return 0 ;
2001-10-25 15:34:55 +04:00
} else { /* add a device */
2018-05-30 01:02:27 +03:00
if ( ! S_ISBLK ( info - > st_mode ) )
2013-12-05 14:11:26 +04:00
return 1 ;
2001-10-08 17:58:52 +04:00
2011-11-25 13:01:37 +04:00
if ( ! _insert_dev ( path , info - > st_rdev ) )
2020-09-26 15:52:27 +03:00
return 0 ;
2001-10-03 16:41:29 +04:00
}
2013-12-05 14:11:26 +04:00
return 1 ;
2001-10-03 16:41:29 +04:00
}
2022-02-25 01:03:21 +03:00
static void _drop_all_aliases ( struct device * dev )
{
struct dm_str_list * strl , * strl2 ;
dm_list_iterate_items_safe ( strl , strl2 , & dev - > aliases ) {
log_debug ( " Drop alias for %d:%d %s. " , ( int ) MAJOR ( dev - > dev ) , ( int ) MINOR ( dev - > dev ) , strl - > str ) ;
dm_hash_remove ( _cache . names , strl - > str ) ;
dm_list_del ( & strl - > list ) ;
}
}
2021-07-02 01:25:43 +03:00
void dev_cache_scan ( struct cmd_context * cmd )
2001-10-03 16:41:29 +04:00
{
2018-05-25 19:14:12 +03:00
log_debug_devs ( " Creating list of system devices. " ) ;
2001-10-03 16:41:29 +04:00
2018-04-09 19:13:43 +03:00
_cache . has_scanned = 1 ;
2001-10-03 16:41:29 +04:00
2011-04-22 16:05:32 +04:00
_insert_dirs ( & _cache . dirs ) ;
2001-10-03 16:41:29 +04:00
2021-07-02 01:25:43 +03:00
if ( cmd - > check_devs_used )
( void ) dev_cache_index_devs ( ) ;
2001-10-03 16:41:29 +04:00
}
2002-11-18 17:01:16 +03:00
int dev_cache_has_scanned ( void )
{
return _cache . has_scanned ;
}
2007-04-26 20:44:59 +04:00
static int _init_preferred_names ( struct cmd_context * cmd )
{
2011-08-30 18:55:15 +04:00
const struct dm_config_node * cn ;
const struct dm_config_value * v ;
2007-04-26 20:44:59 +04:00
struct dm_pool * scratch = NULL ;
2010-12-20 16:23:11 +03:00
const char * * regex ;
2007-04-26 20:44:59 +04:00
unsigned count = 0 ;
int i , r = 0 ;
_cache . preferred_names_matcher = NULL ;
2015-07-08 12:22:24 +03:00
if ( ! ( cn = find_config_tree_array ( cmd , devices_preferred_names_CFG , NULL ) ) | |
2011-08-30 18:55:15 +04:00
cn - > v - > type = = DM_CFG_EMPTY_ARRAY ) {
2015-07-15 17:14:07 +03:00
log_very_verbose ( " devices/preferred_names %s: "
" using built-in preferences " ,
cn & & cn - > v - > type = = DM_CFG_EMPTY_ARRAY ? " is empty "
: " not found in config " ) ;
2007-04-26 20:44:59 +04:00
return 1 ;
}
for ( v = cn - > v ; v ; v = v - > next ) {
2011-08-30 18:55:15 +04:00
if ( v - > type ! = DM_CFG_STRING ) {
2007-04-26 20:44:59 +04:00
log_error ( " preferred_names patterns must be enclosed in quotes " ) ;
return 0 ;
2008-01-30 17:00:02 +03:00
}
2007-04-26 20:44:59 +04:00
count + + ;
}
if ( ! ( scratch = dm_pool_create ( " preferred device name matcher " , 1024 ) ) )
return_0 ;
if ( ! ( regex = dm_pool_alloc ( scratch , sizeof ( * regex ) * count ) ) ) {
log_error ( " Failed to allocate preferred device name "
" pattern list. " ) ;
goto out ;
}
for ( v = cn - > v , i = count - 1 ; v ; v = v - > next , i - - ) {
if ( ! ( regex [ i ] = dm_pool_strdup ( scratch , v - > v . str ) ) ) {
log_error ( " Failed to allocate a preferred device name "
" pattern. " ) ;
goto out ;
}
}
if ( ! ( _cache . preferred_names_matcher =
2010-12-20 16:23:11 +03:00
dm_regex_create ( _cache . mem , regex , count ) ) ) {
2007-04-26 20:44:59 +04:00
log_error ( " Preferred device name pattern matcher creation failed. " ) ;
goto out ;
}
r = 1 ;
out :
dm_pool_destroy ( scratch ) ;
return r ;
}
int dev_cache_init ( struct cmd_context * cmd )
2001-10-03 16:41:29 +04:00
{
2001-10-25 15:34:55 +04:00
_cache . names = NULL ;
2008-01-30 16:19:47 +03:00
if ( ! ( _cache . mem = dm_pool_create ( " dev_cache " , 10 * 1024 ) ) )
return_0 ;
2001-10-03 16:41:29 +04:00
2021-12-17 18:36:36 +03:00
if ( ! ( _cache . names = dm_hash_create ( 1020 ) ) | |
2021-03-07 17:33:50 +03:00
! ( _cache . vgid_index = dm_hash_create ( 30 ) ) | |
! ( _cache . lvid_index = dm_hash_create ( 29 ) ) ) {
2005-10-17 03:03:59 +04:00
dm_pool_destroy ( _cache . mem ) ;
2001-10-03 16:41:29 +04:00
_cache . mem = 0 ;
2008-01-30 16:19:47 +03:00
return_0 ;
2001-10-03 16:41:29 +04:00
}
2001-10-25 15:34:55 +04:00
if ( ! ( _cache . devices = btree_create ( _cache . mem ) ) ) {
2009-07-16 00:02:46 +04:00
log_error ( " Couldn't create binary tree for dev-cache. " ) ;
2001-10-25 15:34:55 +04:00
goto bad ;
}
2016-03-31 16:14:01 +03:00
if ( ! ( _cache . sysfs_only_devices = btree_create ( _cache . mem ) ) ) {
log_error ( " Couldn't create binary tree for sysfs-only devices in dev cache. " ) ;
goto bad ;
}
2010-05-25 02:53:48 +04:00
if ( ! ( _cache . dev_dir = _strdup ( cmd - > dev_dir ) ) ) {
log_error ( " strdup dev_dir failed. " ) ;
goto bad ;
}
2008-11-04 01:14:30 +03:00
dm_list_init ( & _cache . dirs ) ;
2001-10-08 16:11:33 +04:00
2007-04-26 20:44:59 +04:00
if ( ! _init_preferred_names ( cmd ) )
goto_bad ;
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
}
2014-04-04 04:39:42 +04:00
/*
* Returns number of devices still open .
*/
2014-03-25 13:53:42 +04:00
static int _check_for_open_devices ( int close_immediate )
2001-11-14 13:01:52 +03:00
{
2014-03-25 13:53:42 +04:00
struct device * dev ;
struct dm_hash_node * n ;
2014-04-04 04:39:42 +04:00
int num_open = 0 ;
2014-03-25 13:53:42 +04:00
dm_hash_iterate ( n , _cache . names ) {
dev = ( struct device * ) dm_hash_get_data ( _cache . names , n ) ;
if ( dev - > fd > = 0 ) {
2014-04-04 04:39:42 +04:00
log_error ( " Device '%s' has been left open (%d remaining references). " ,
2014-03-25 13:53:42 +04:00
dev_name ( dev ) , dev - > open_count ) ;
2014-04-04 04:39:42 +04:00
num_open + + ;
2018-10-15 17:06:35 +03:00
if ( close_immediate & & ! dev_close_immediate ( dev ) )
stack ;
2014-03-25 13:53:42 +04:00
}
}
2014-04-04 04:39:42 +04:00
return num_open ;
2002-01-16 00:28:04 +03:00
}
2001-11-14 13:01:52 +03:00
2014-04-04 04:39:42 +04:00
/*
* Returns number of devices left open .
*/
2014-03-25 13:53:42 +04:00
int dev_cache_check_for_open_devices ( void )
2002-01-16 00:28:04 +03:00
{
2014-03-25 13:53:42 +04:00
return _check_for_open_devices ( 0 ) ;
2001-11-14 13:01:52 +03:00
}
2014-03-25 13:53:42 +04:00
int dev_cache_exit ( void )
2001-10-03 16:41:29 +04:00
{
device usage based on devices file
The LVM devices file lists devices that lvm can use. The default
file is /etc/lvm/devices/system.devices, and the lvmdevices(8)
command is used to add or remove device entries. If the file
does not exist, or if lvm.conf includes use_devicesfile=0, then
lvm will not use a devices file. When the devices file is in use,
the regex filter is not used, and the filter settings in lvm.conf
or on the command line are ignored.
LVM records devices in the devices file using hardware-specific
IDs, such as the WWID, and attempts to use subsystem-specific
IDs for virtual device types. These device IDs are also written
in the VG metadata. When no hardware or virtual ID is available,
lvm falls back using the unstable device name as the device ID.
When devnames are used, lvm performs extra scanning to find
devices if their devname changes, e.g. after reboot.
When proper device IDs are used, an lvm command will not look
at devices outside the devices file, but when devnames are used
as a fallback, lvm will scan devices outside the devices file
to locate PVs on renamed devices. A config setting
search_for_devnames can be used to control the scanning for
renamed devname entries.
Related to the devices file, the new command option
--devices <devnames> allows a list of devices to be specified for
the command to use, overriding the devices file. The listed
devices act as a sort of devices file in terms of limiting which
devices lvm will see and use. Devices that are not listed will
appear to be missing to the lvm command.
Multiple devices files can be kept in /etc/lvm/devices, which
allows lvm to be used with different sets of devices, e.g.
system devices do not need to be exposed to a specific application,
and the application can use lvm on its own set of devices that are
not exposed to the system. The option --devicesfile <filename> is
used to select the devices file to use with the command. Without
the option set, the default system devices file is used.
Setting --devicesfile "" causes lvm to not use a devices file.
An existing, empty devices file means lvm will see no devices.
The new command vgimportdevices adds PVs from a VG to the devices
file and updates the VG metadata to include the device IDs.
vgimportdevices -a will import all VGs into the system devices file.
LVM commands run by dmeventd not use a devices file by default,
and will look at all devices on the system. A devices file can
be created for dmeventd (/etc/lvm/devices/dmeventd.devices) If
this file exists, lvm commands run by dmeventd will use it.
Internal implementaion:
- device_ids_read - read the devices file
. add struct dev_use (du) to cmd->use_devices for each devices file entry
- dev_cache_scan - get /dev entries
. add struct device (dev) to dev_cache for each device on the system
- device_ids_match - match devices file entries to /dev entries
. match each du on cmd->use_devices to a dev in dev_cache, using device ID
. on match, set du->dev, dev->id, dev->flags MATCHED_USE_ID
- label_scan - read lvm headers and metadata from devices
. filters are applied, those that do not need data from the device
. filter-deviceid skips devs without MATCHED_USE_ID, i.e.
skips /dev entries that are not listed in the devices file
. read lvm label from dev
. filters are applied, those that use data from the device
. read lvm metadata from dev
. add info/vginfo structs for PVs/VGs (info is "lvmcache")
- device_ids_find_renamed_devs - handle devices with unstable devname ID
where devname changed
. this step only needed when devs do not have proper device IDs,
and their dev names change, e.g. after reboot sdb becomes sdc.
. detect incorrect match because PVID in the devices file entry
does not match the PVID found when the device was read above
. undo incorrect match between du and dev above
. search system devices for new location of PVID
. update devices file with new devnames for PVIDs on renamed devices
. label_scan the renamed devs
- continue with command processing
2020-06-23 21:25:41 +03:00
struct device * dev ;
struct dm_hash_node * n ;
2014-04-04 04:39:42 +04:00
int num_open = 0 ;
2014-03-25 13:53:42 +04:00
device usage based on devices file
The LVM devices file lists devices that lvm can use. The default
file is /etc/lvm/devices/system.devices, and the lvmdevices(8)
command is used to add or remove device entries. If the file
does not exist, or if lvm.conf includes use_devicesfile=0, then
lvm will not use a devices file. When the devices file is in use,
the regex filter is not used, and the filter settings in lvm.conf
or on the command line are ignored.
LVM records devices in the devices file using hardware-specific
IDs, such as the WWID, and attempts to use subsystem-specific
IDs for virtual device types. These device IDs are also written
in the VG metadata. When no hardware or virtual ID is available,
lvm falls back using the unstable device name as the device ID.
When devnames are used, lvm performs extra scanning to find
devices if their devname changes, e.g. after reboot.
When proper device IDs are used, an lvm command will not look
at devices outside the devices file, but when devnames are used
as a fallback, lvm will scan devices outside the devices file
to locate PVs on renamed devices. A config setting
search_for_devnames can be used to control the scanning for
renamed devname entries.
Related to the devices file, the new command option
--devices <devnames> allows a list of devices to be specified for
the command to use, overriding the devices file. The listed
devices act as a sort of devices file in terms of limiting which
devices lvm will see and use. Devices that are not listed will
appear to be missing to the lvm command.
Multiple devices files can be kept in /etc/lvm/devices, which
allows lvm to be used with different sets of devices, e.g.
system devices do not need to be exposed to a specific application,
and the application can use lvm on its own set of devices that are
not exposed to the system. The option --devicesfile <filename> is
used to select the devices file to use with the command. Without
the option set, the default system devices file is used.
Setting --devicesfile "" causes lvm to not use a devices file.
An existing, empty devices file means lvm will see no devices.
The new command vgimportdevices adds PVs from a VG to the devices
file and updates the VG metadata to include the device IDs.
vgimportdevices -a will import all VGs into the system devices file.
LVM commands run by dmeventd not use a devices file by default,
and will look at all devices on the system. A devices file can
be created for dmeventd (/etc/lvm/devices/dmeventd.devices) If
this file exists, lvm commands run by dmeventd will use it.
Internal implementaion:
- device_ids_read - read the devices file
. add struct dev_use (du) to cmd->use_devices for each devices file entry
- dev_cache_scan - get /dev entries
. add struct device (dev) to dev_cache for each device on the system
- device_ids_match - match devices file entries to /dev entries
. match each du on cmd->use_devices to a dev in dev_cache, using device ID
. on match, set du->dev, dev->id, dev->flags MATCHED_USE_ID
- label_scan - read lvm headers and metadata from devices
. filters are applied, those that do not need data from the device
. filter-deviceid skips devs without MATCHED_USE_ID, i.e.
skips /dev entries that are not listed in the devices file
. read lvm label from dev
. filters are applied, those that use data from the device
. read lvm metadata from dev
. add info/vginfo structs for PVs/VGs (info is "lvmcache")
- device_ids_find_renamed_devs - handle devices with unstable devname ID
where devname changed
. this step only needed when devs do not have proper device IDs,
and their dev names change, e.g. after reboot sdb becomes sdc.
. detect incorrect match because PVID in the devices file entry
does not match the PVID found when the device was read above
. undo incorrect match between du and dev above
. search system devices for new location of PVID
. update devices file with new devnames for PVIDs on renamed devices
. label_scan the renamed devs
- continue with command processing
2020-06-23 21:25:41 +03:00
if ( _cache . names ) {
2014-04-04 04:39:42 +04:00
if ( ( num_open = _check_for_open_devices ( 1 ) ) > 0 )
log_error ( INTERNAL_ERROR " %d device(s) were left open and have been closed. " , num_open ) ;
2003-11-14 20:55:39 +03:00
device usage based on devices file
The LVM devices file lists devices that lvm can use. The default
file is /etc/lvm/devices/system.devices, and the lvmdevices(8)
command is used to add or remove device entries. If the file
does not exist, or if lvm.conf includes use_devicesfile=0, then
lvm will not use a devices file. When the devices file is in use,
the regex filter is not used, and the filter settings in lvm.conf
or on the command line are ignored.
LVM records devices in the devices file using hardware-specific
IDs, such as the WWID, and attempts to use subsystem-specific
IDs for virtual device types. These device IDs are also written
in the VG metadata. When no hardware or virtual ID is available,
lvm falls back using the unstable device name as the device ID.
When devnames are used, lvm performs extra scanning to find
devices if their devname changes, e.g. after reboot.
When proper device IDs are used, an lvm command will not look
at devices outside the devices file, but when devnames are used
as a fallback, lvm will scan devices outside the devices file
to locate PVs on renamed devices. A config setting
search_for_devnames can be used to control the scanning for
renamed devname entries.
Related to the devices file, the new command option
--devices <devnames> allows a list of devices to be specified for
the command to use, overriding the devices file. The listed
devices act as a sort of devices file in terms of limiting which
devices lvm will see and use. Devices that are not listed will
appear to be missing to the lvm command.
Multiple devices files can be kept in /etc/lvm/devices, which
allows lvm to be used with different sets of devices, e.g.
system devices do not need to be exposed to a specific application,
and the application can use lvm on its own set of devices that are
not exposed to the system. The option --devicesfile <filename> is
used to select the devices file to use with the command. Without
the option set, the default system devices file is used.
Setting --devicesfile "" causes lvm to not use a devices file.
An existing, empty devices file means lvm will see no devices.
The new command vgimportdevices adds PVs from a VG to the devices
file and updates the VG metadata to include the device IDs.
vgimportdevices -a will import all VGs into the system devices file.
LVM commands run by dmeventd not use a devices file by default,
and will look at all devices on the system. A devices file can
be created for dmeventd (/etc/lvm/devices/dmeventd.devices) If
this file exists, lvm commands run by dmeventd will use it.
Internal implementaion:
- device_ids_read - read the devices file
. add struct dev_use (du) to cmd->use_devices for each devices file entry
- dev_cache_scan - get /dev entries
. add struct device (dev) to dev_cache for each device on the system
- device_ids_match - match devices file entries to /dev entries
. match each du on cmd->use_devices to a dev in dev_cache, using device ID
. on match, set du->dev, dev->id, dev->flags MATCHED_USE_ID
- label_scan - read lvm headers and metadata from devices
. filters are applied, those that do not need data from the device
. filter-deviceid skips devs without MATCHED_USE_ID, i.e.
skips /dev entries that are not listed in the devices file
. read lvm label from dev
. filters are applied, those that use data from the device
. read lvm metadata from dev
. add info/vginfo structs for PVs/VGs (info is "lvmcache")
- device_ids_find_renamed_devs - handle devices with unstable devname ID
where devname changed
. this step only needed when devs do not have proper device IDs,
and their dev names change, e.g. after reboot sdb becomes sdc.
. detect incorrect match because PVID in the devices file entry
does not match the PVID found when the device was read above
. undo incorrect match between du and dev above
. search system devices for new location of PVID
. update devices file with new devnames for PVIDs on renamed devices
. label_scan the renamed devs
- continue with command processing
2020-06-23 21:25:41 +03:00
dm_hash_iterate ( n , _cache . names ) {
dev = ( struct device * ) dm_hash_get_data ( _cache . names , n ) ;
free_dids ( & dev - > ids ) ;
2022-06-06 22:04:20 +03:00
free_wwids ( & dev - > wwids ) ;
device usage based on devices file
The LVM devices file lists devices that lvm can use. The default
file is /etc/lvm/devices/system.devices, and the lvmdevices(8)
command is used to add or remove device entries. If the file
does not exist, or if lvm.conf includes use_devicesfile=0, then
lvm will not use a devices file. When the devices file is in use,
the regex filter is not used, and the filter settings in lvm.conf
or on the command line are ignored.
LVM records devices in the devices file using hardware-specific
IDs, such as the WWID, and attempts to use subsystem-specific
IDs for virtual device types. These device IDs are also written
in the VG metadata. When no hardware or virtual ID is available,
lvm falls back using the unstable device name as the device ID.
When devnames are used, lvm performs extra scanning to find
devices if their devname changes, e.g. after reboot.
When proper device IDs are used, an lvm command will not look
at devices outside the devices file, but when devnames are used
as a fallback, lvm will scan devices outside the devices file
to locate PVs on renamed devices. A config setting
search_for_devnames can be used to control the scanning for
renamed devname entries.
Related to the devices file, the new command option
--devices <devnames> allows a list of devices to be specified for
the command to use, overriding the devices file. The listed
devices act as a sort of devices file in terms of limiting which
devices lvm will see and use. Devices that are not listed will
appear to be missing to the lvm command.
Multiple devices files can be kept in /etc/lvm/devices, which
allows lvm to be used with different sets of devices, e.g.
system devices do not need to be exposed to a specific application,
and the application can use lvm on its own set of devices that are
not exposed to the system. The option --devicesfile <filename> is
used to select the devices file to use with the command. Without
the option set, the default system devices file is used.
Setting --devicesfile "" causes lvm to not use a devices file.
An existing, empty devices file means lvm will see no devices.
The new command vgimportdevices adds PVs from a VG to the devices
file and updates the VG metadata to include the device IDs.
vgimportdevices -a will import all VGs into the system devices file.
LVM commands run by dmeventd not use a devices file by default,
and will look at all devices on the system. A devices file can
be created for dmeventd (/etc/lvm/devices/dmeventd.devices) If
this file exists, lvm commands run by dmeventd will use it.
Internal implementaion:
- device_ids_read - read the devices file
. add struct dev_use (du) to cmd->use_devices for each devices file entry
- dev_cache_scan - get /dev entries
. add struct device (dev) to dev_cache for each device on the system
- device_ids_match - match devices file entries to /dev entries
. match each du on cmd->use_devices to a dev in dev_cache, using device ID
. on match, set du->dev, dev->id, dev->flags MATCHED_USE_ID
- label_scan - read lvm headers and metadata from devices
. filters are applied, those that do not need data from the device
. filter-deviceid skips devs without MATCHED_USE_ID, i.e.
skips /dev entries that are not listed in the devices file
. read lvm label from dev
. filters are applied, those that use data from the device
. read lvm metadata from dev
. add info/vginfo structs for PVs/VGs (info is "lvmcache")
- device_ids_find_renamed_devs - handle devices with unstable devname ID
where devname changed
. this step only needed when devs do not have proper device IDs,
and their dev names change, e.g. after reboot sdb becomes sdc.
. detect incorrect match because PVID in the devices file entry
does not match the PVID found when the device was read above
. undo incorrect match between du and dev above
. search system devices for new location of PVID
. update devices file with new devnames for PVIDs on renamed devices
. label_scan the renamed devs
- continue with command processing
2020-06-23 21:25:41 +03:00
}
}
2014-12-07 01:56:14 +03:00
if ( _cache . mem )
2005-10-17 03:03:59 +04:00
dm_pool_destroy ( _cache . mem ) ;
2003-11-14 20:55:39 +03:00
2014-12-07 01:56:14 +03:00
if ( _cache . names )
2005-10-17 03:03:59 +04:00
dm_hash_destroy ( _cache . names ) ;
2003-11-14 20:55:39 +03:00
2016-03-16 16:01:26 +03:00
if ( _cache . vgid_index )
dm_hash_destroy ( _cache . vgid_index ) ;
if ( _cache . lvid_index )
dm_hash_destroy ( _cache . lvid_index ) ;
2014-12-07 01:56:14 +03:00
memset ( & _cache , 0 , sizeof ( _cache ) ) ;
2014-03-25 13:53:42 +04:00
2014-04-04 04:39:42 +04:00
return ( ! num_open ) ;
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 ) ) {
2012-10-16 12:14:41 +04:00
log_warn ( " Ignoring %s: %s. " , path , strerror ( errno ) ) ;
2001-10-23 15:50:49 +04:00
/* But don't fail */
return 1 ;
}
if ( ! S_ISDIR ( st . st_mode ) ) {
2012-10-16 12:14:41 +04:00
log_warn ( " Ignoring %s: Not a directory. " , path ) ;
2001-10-23 15:50:49 +04:00
return 1 ;
}
2001-10-03 16:41:29 +04:00
2011-12-21 17:14:54 +04:00
if ( ! ( dl = _zalloc ( sizeof ( * dl ) + strlen ( path ) + 1 ) ) ) {
2003-01-08 19:41:22 +03:00
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 ) ;
2008-11-04 01:14:30 +03:00
dm_list_add ( & _cache . dirs , & dl - > list ) ;
2001-10-03 16:41:29 +04:00
return 1 ;
}
2020-07-20 20:48:36 +03:00
struct device * dev_hash_get ( const char * name )
2017-10-17 04:12:41 +03:00
{
2020-07-20 20:48:36 +03:00
return ( struct device * ) dm_hash_lookup ( _cache . names , name ) ;
2017-10-17 04:12:41 +03:00
}
2021-01-14 22:26:42 +03:00
static void _remove_alias ( struct device * dev , const char * name )
{
struct dm_str_list * strl ;
dm_list_iterate_items ( strl , & dev - > aliases ) {
if ( ! strcmp ( strl - > str , name ) ) {
dm_list_del ( & strl - > list ) ;
return ;
}
}
}
/*
* Check that paths for this dev still refer to the same dev_t . This is known
* to drop invalid paths in the case where lvm deactivates an LV , which causes
* that LV path to go away , but that LV path is not removed from dev - cache ( it
* probably should be ) . Later a new path to a different LV is added to
* dev - cache , where the new LV has the same major : minor as the previously
* deactivated LV . The new LV will find the existing struct dev , and that
* struct dev will have dev - > aliases entries that refer to the name of the old
* deactivated LV . Those old paths are all invalid and are dropped here .
*/
2022-03-01 02:37:12 +03:00
void dev_cache_verify_aliases ( struct device * dev )
2021-01-14 22:26:42 +03:00
{
struct dm_str_list * strl , * strl2 ;
struct stat st ;
dm_list_iterate_items_safe ( strl , strl2 , & dev - > aliases ) {
if ( stat ( strl - > str , & st ) | | ( st . st_rdev ! = dev - > dev ) ) {
2022-02-25 01:03:21 +03:00
log_debug ( " Drop alias for %d:%d invalid path %s %d:%d. " ,
( int ) MAJOR ( dev - > dev ) , ( int ) MINOR ( dev - > dev ) , strl - > str ,
( int ) MAJOR ( st . st_rdev ) , ( int ) MINOR ( st . st_rdev ) ) ;
2021-01-14 22:26:42 +03:00
dm_hash_remove ( _cache . names , strl - > str ) ;
dm_list_del ( & strl - > list ) ;
}
}
}
2022-02-25 01:03:21 +03:00
static struct device * _dev_cache_get ( struct cmd_context * cmd , const char * name , struct dev_filter * f , int existing )
2001-10-03 16:41:29 +04:00
{
2021-01-14 22:26:42 +03:00
struct device * dev = ( struct device * ) dm_hash_lookup ( _cache . names , name ) ;
struct stat st ;
int ret ;
2001-10-08 16:11:33 +04:00
2021-01-14 22:26:42 +03:00
/*
* DEV_REGULAR means that is " dev " is actually a file , not a device .
* FIXME : I don ' t think dev - cache is used for files any more and this
* can be dropped ?
*/
if ( dev & & ( dev - > flags & DEV_REGULAR ) )
return dev ;
2022-02-25 01:03:21 +03:00
if ( dev & & dm_list_empty ( & dev - > aliases ) ) {
/* shouldn't happen */
log_warn ( " Ignoring dev with no valid paths for %s. " , name ) ;
return NULL ;
}
2021-01-14 22:26:42 +03:00
/*
2022-02-25 01:03:21 +03:00
* The requested path is invalid , remove any dev - cache info for it .
2021-01-14 22:26:42 +03:00
*/
if ( stat ( name , & st ) ) {
if ( dev ) {
2022-02-25 01:03:21 +03:00
log_debug ( " Device path %s is invalid for %d:%d %s. " ,
2021-01-14 22:26:42 +03:00
name , ( int ) MAJOR ( dev - > dev ) , ( int ) MINOR ( dev - > dev ) , dev_name ( dev ) ) ;
2005-05-03 21:28:23 +04:00
2011-11-25 13:01:37 +04:00
dm_hash_remove ( _cache . names , name ) ;
2021-01-14 22:26:42 +03:00
_remove_alias ( dev , name ) ;
2002-01-25 02:16:19 +03:00
2021-01-14 22:26:42 +03:00
/* Remove any other names in dev->aliases that are incorrect. */
2022-03-01 02:37:12 +03:00
dev_cache_verify_aliases ( dev ) ;
2005-06-14 21:54:48 +04:00
}
2021-01-14 22:26:42 +03:00
return NULL ;
2001-10-08 17:58:52 +04:00
}
2001-10-08 16:11:33 +04:00
2022-02-25 01:03:21 +03:00
if ( dev & & dm_list_empty ( & dev - > aliases ) ) {
/* shouldn't happen */
log_warn ( " Ignoring dev with no valid paths for %s. " , name ) ;
return NULL ;
}
2021-01-14 22:26:42 +03:00
if ( ! S_ISBLK ( st . st_mode ) ) {
log_debug ( " Not a block device %s. " , name ) ;
2018-05-04 01:12:07 +03:00
return NULL ;
2021-01-14 22:26:42 +03:00
}
/*
* dev - cache has incorrect info for the requested path .
* Remove incorrect info and then add new dev - cache entry .
*/
if ( dev & & ( st . st_rdev ! = dev - > dev ) ) {
2022-02-25 01:03:21 +03:00
struct device * dev_by_devt = ( struct device * ) btree_lookup ( _cache . devices , ( uint32_t ) st . st_rdev ) ;
/*
* lvm commands create this condition when they
* activate / deactivate LVs combined with creating new LVs .
* The command does not purge dev structs when deactivating
* an LV ( which it probably should do ) , but the better
* approach would be not using dev - cache at all for LVs .
*/
log_debug ( " Dropping aliases for device entry %d:%d %s for new device %d:%d %s. " ,
( int ) MAJOR ( dev - > dev ) , ( int ) MINOR ( dev - > dev ) , dev_name ( dev ) ,
( int ) MAJOR ( st . st_rdev ) , ( int ) MINOR ( st . st_rdev ) , name ) ;
_drop_all_aliases ( dev ) ;
if ( dev_by_devt ) {
log_debug ( " Dropping aliases for device entry %d:%d %s for new device %d:%d %s. " ,
( int ) MAJOR ( dev_by_devt - > dev ) , ( int ) MINOR ( dev_by_devt - > dev ) , dev_name ( dev_by_devt ) ,
( int ) MAJOR ( st . st_rdev ) , ( int ) MINOR ( st . st_rdev ) , name ) ;
_drop_all_aliases ( dev_by_devt ) ;
}
2021-01-14 22:26:42 +03:00
2022-02-25 01:03:21 +03:00
#if 0
/*
* I think only lvm ' s own dm devs should be added here , so use
* a warning to look for any other unknown cases .
*/
if ( MAJOR ( st . st_rdev ) ! = cmd - > dev_types - > device_mapper_major ) {
log_warn ( " WARNING: new device appeared %d:%d %s " ,
( int ) MAJOR ( st . st_rdev ) , ( int ) ( MINOR ( st . st_rdev ) ) , name ) ;
}
# endif
2021-01-14 22:26:42 +03:00
2022-02-25 01:03:21 +03:00
if ( ! _insert_dev ( name , st . st_rdev ) )
return_NULL ;
2021-01-14 22:26:42 +03:00
2022-02-25 01:03:21 +03:00
/* Get the struct dev that was just added. */
dev = ( struct device * ) dm_hash_lookup ( _cache . names , name ) ;
2021-01-14 22:26:42 +03:00
2022-02-25 01:03:21 +03:00
if ( ! dev ) {
log_error ( " Failed to get device %s " , name ) ;
return NULL ;
}
goto out ;
2021-01-14 22:26:42 +03:00
}
2018-05-04 01:12:07 +03:00
2022-02-25 01:03:21 +03:00
if ( dev & & dm_list_empty ( & dev - > aliases ) ) {
/* shouldn't happen */
log_warn ( " Ignoring dev with no valid paths for %s. " , name ) ;
return NULL ;
}
if ( ! dev & & existing )
return_NULL ;
2021-01-14 22:26:42 +03:00
/*
2022-02-25 01:03:21 +03:00
* This case should never be hit for a PV . It should only
* happen when the command is opening a new LV it has created .
* Add an arg to all callers indicating when the arg should be
* new ( for an LV ) and not existing .
* FIXME : fix this further by not using dev - cache struct devs
* at all for new dm devs ( LVs ) that lvm uses . Make the
* dev - cache contain only devs for PVs .
* Places to fix that use a dev for LVs include :
* . lv_resize opening lv to discard
* . wipe_lv opening lv to zero it
* . _extend_sanlock_lv opening lv to extend it
* . _write_log_header opening lv to write header
* Also , io to LVs should not go through bcache .
* bcache should contain only labels and metadata
* scanned from PVs .
2021-01-14 22:26:42 +03:00
*/
if ( ! dev ) {
2022-02-25 01:03:21 +03:00
/*
* This case should only be used for new devices created by this
* command ( opening LVs it ' s created ) , so if a dev exists for the
* dev_t referenced by the name , then drop all aliases for before
* _insert_dev adds the new name . lvm commands actually hit this
* fairly often when it uses some LV , deactivates the LV , then
* creates some new LV which ends up with the same major : minor .
* Without dropping the aliases , it ' s plausible that lvm commands
* could end up using the wrong dm device .
*/
struct device * dev_by_devt = ( struct device * ) btree_lookup ( _cache . devices , ( uint32_t ) st . st_rdev ) ;
if ( dev_by_devt ) {
log_debug ( " Dropping aliases for %d:%d before adding new path %s. " ,
( int ) MAJOR ( st . st_rdev ) , ( int ) ( MINOR ( st . st_rdev ) ) , name ) ;
_drop_all_aliases ( dev_by_devt ) ;
}
#if 0
/*
* I think only lvm ' s own dm devs should be added here , so use
* a warning to look for any other unknown cases .
*/
if ( MAJOR ( st . st_rdev ) ! = cmd - > dev_types - > device_mapper_major ) {
log_warn ( " WARNING: new device appeared %d:%d %s " ,
( int ) MAJOR ( st . st_rdev ) , ( int ) ( MINOR ( st . st_rdev ) ) , name ) ;
}
# endif
2021-04-22 13:30:26 +03:00
if ( ! _insert_dev ( name , st . st_rdev ) )
return_NULL ;
2018-05-04 01:12:07 +03:00
2021-01-14 22:26:42 +03:00
/* Get the struct dev that was just added. */
dev = ( struct device * ) dm_hash_lookup ( _cache . names , name ) ;
2018-05-04 01:12:07 +03:00
2021-01-14 22:26:42 +03:00
if ( ! dev ) {
log_error ( " Failed to get device %s " , name ) ;
return NULL ;
2018-05-04 01:12:07 +03:00
}
2021-01-14 22:26:42 +03:00
}
2022-02-25 01:03:21 +03:00
out :
2021-01-14 22:26:42 +03:00
/*
* The caller passed a filter if they only want the dev if it
* passes filters .
*/
if ( ! f )
return dev ;
ret = f - > passes_filter ( cmd , f , dev , NULL ) ;
if ( ! ret ) {
log_debug_devs ( " dev_cache_get filter excludes %s " , dev_name ( dev ) ) ;
2013-08-14 02:26:58 +04:00
return NULL ;
2021-01-14 22:26:42 +03:00
}
2013-08-14 02:26:58 +04:00
2021-01-14 22:26:42 +03:00
return dev ;
2001-10-03 16:41:29 +04:00
}
2022-02-25 01:03:21 +03:00
struct device * dev_cache_get_existing ( struct cmd_context * cmd , const char * name , struct dev_filter * f )
{
return _dev_cache_get ( cmd , name , f , 1 ) ;
}
struct device * dev_cache_get ( struct cmd_context * cmd , const char * name , struct dev_filter * f )
{
return _dev_cache_get ( cmd , name , f , 0 ) ;
}
2022-02-25 00:57:29 +03:00
struct device * dev_cache_get_by_devt ( struct cmd_context * cmd , dev_t devt )
2012-02-23 17:11:07 +04:00
{
2022-02-25 00:57:29 +03:00
struct device * dev = ( struct device * ) btree_lookup ( _cache . devices , ( uint32_t ) devt ) ;
2021-02-27 23:18:48 +03:00
2022-02-25 00:57:29 +03:00
if ( dev )
return dev ;
log_debug_devs ( " No devno %d:%d in dev cache. " , ( int ) MAJOR ( devt ) , ( int ) MINOR ( devt ) ) ;
2018-05-04 01:12:07 +03:00
return NULL ;
2012-02-23 17:11:07 +04:00
}
2018-04-09 19:13:43 +03:00
struct dev_iter * dev_iter_create ( struct dev_filter * f , int unused )
2001-10-03 16:41:29 +04:00
{
2018-06-08 15:40:53 +03:00
struct dev_iter * di = malloc ( sizeof ( * di ) ) ;
2001-10-03 16:41:29 +04:00
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
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 ;
2012-08-19 00:03:46 +04:00
if ( di - > filter )
di - > filter - > use_count + + ;
2001-10-03 16:41:29 +04:00
return di ;
}
void dev_iter_destroy ( struct dev_iter * iter )
{
2012-08-19 00:03:46 +04:00
if ( iter - > filter )
iter - > filter - > use_count - - ;
2018-06-08 15:40:53 +03:00
free ( iter ) ;
2001-10-03 16:41:29 +04:00
}
2006-04-19 19:33:07 +04:00
static struct device * _iter_next ( struct dev_iter * iter )
2001-10-03 16:41:29 +04:00
{
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 ;
}
2018-06-15 19:03:55 +03:00
struct device * dev_iter_get ( struct cmd_context * cmd , struct dev_iter * iter )
2001-10-03 16:41:29 +04:00
{
2018-05-04 01:12:07 +03:00
struct dev_filter * f ;
int ret ;
2001-10-03 16:41:29 +04:00
while ( iter - > current ) {
struct device * d = _iter_next ( iter ) ;
2018-05-04 01:12:07 +03:00
ret = 1 ;
f = iter - > filter ;
2022-04-01 23:06:03 +03:00
if ( f & & ! ( d - > flags & DEV_REGULAR ) )
2018-12-07 23:35:22 +03:00
ret = f - > passes_filter ( cmd , f , d , NULL ) ;
2018-05-04 01:12:07 +03:00
if ( ! f | | ( d - > flags & DEV_REGULAR ) | | ret )
return d ;
2001-10-03 16:41:29 +04:00
}
return NULL ;
}
2006-04-19 19:33:07 +04:00
int dev_fd ( struct device * dev )
{
return dev - > fd ;
}
const char * dev_name ( const struct device * dev )
{
2022-02-25 01:03:21 +03:00
if ( dev & & dev - > aliases . n & & ! dm_list_empty ( & dev - > aliases ) )
return dm_list_item ( dev - > aliases . n , struct dm_str_list ) - > str ;
else
return unknown_device_name ( ) ;
2006-04-19 19:33:07 +04:00
}
2019-07-09 22:48:31 +03:00
bool dev_cache_has_md_with_end_superblock ( struct dev_types * dt )
{
struct btree_iter * iter = btree_first ( _cache . devices ) ;
struct device * dev ;
while ( iter ) {
dev = btree_get_data ( iter ) ;
if ( dev_is_md_with_end_superblock ( dt , dev ) )
return true ;
iter = btree_next ( iter ) ;
}
return false ;
}
device usage based on devices file
The LVM devices file lists devices that lvm can use. The default
file is /etc/lvm/devices/system.devices, and the lvmdevices(8)
command is used to add or remove device entries. If the file
does not exist, or if lvm.conf includes use_devicesfile=0, then
lvm will not use a devices file. When the devices file is in use,
the regex filter is not used, and the filter settings in lvm.conf
or on the command line are ignored.
LVM records devices in the devices file using hardware-specific
IDs, such as the WWID, and attempts to use subsystem-specific
IDs for virtual device types. These device IDs are also written
in the VG metadata. When no hardware or virtual ID is available,
lvm falls back using the unstable device name as the device ID.
When devnames are used, lvm performs extra scanning to find
devices if their devname changes, e.g. after reboot.
When proper device IDs are used, an lvm command will not look
at devices outside the devices file, but when devnames are used
as a fallback, lvm will scan devices outside the devices file
to locate PVs on renamed devices. A config setting
search_for_devnames can be used to control the scanning for
renamed devname entries.
Related to the devices file, the new command option
--devices <devnames> allows a list of devices to be specified for
the command to use, overriding the devices file. The listed
devices act as a sort of devices file in terms of limiting which
devices lvm will see and use. Devices that are not listed will
appear to be missing to the lvm command.
Multiple devices files can be kept in /etc/lvm/devices, which
allows lvm to be used with different sets of devices, e.g.
system devices do not need to be exposed to a specific application,
and the application can use lvm on its own set of devices that are
not exposed to the system. The option --devicesfile <filename> is
used to select the devices file to use with the command. Without
the option set, the default system devices file is used.
Setting --devicesfile "" causes lvm to not use a devices file.
An existing, empty devices file means lvm will see no devices.
The new command vgimportdevices adds PVs from a VG to the devices
file and updates the VG metadata to include the device IDs.
vgimportdevices -a will import all VGs into the system devices file.
LVM commands run by dmeventd not use a devices file by default,
and will look at all devices on the system. A devices file can
be created for dmeventd (/etc/lvm/devices/dmeventd.devices) If
this file exists, lvm commands run by dmeventd will use it.
Internal implementaion:
- device_ids_read - read the devices file
. add struct dev_use (du) to cmd->use_devices for each devices file entry
- dev_cache_scan - get /dev entries
. add struct device (dev) to dev_cache for each device on the system
- device_ids_match - match devices file entries to /dev entries
. match each du on cmd->use_devices to a dev in dev_cache, using device ID
. on match, set du->dev, dev->id, dev->flags MATCHED_USE_ID
- label_scan - read lvm headers and metadata from devices
. filters are applied, those that do not need data from the device
. filter-deviceid skips devs without MATCHED_USE_ID, i.e.
skips /dev entries that are not listed in the devices file
. read lvm label from dev
. filters are applied, those that use data from the device
. read lvm metadata from dev
. add info/vginfo structs for PVs/VGs (info is "lvmcache")
- device_ids_find_renamed_devs - handle devices with unstable devname ID
where devname changed
. this step only needed when devs do not have proper device IDs,
and their dev names change, e.g. after reboot sdb becomes sdc.
. detect incorrect match because PVID in the devices file entry
does not match the PVID found when the device was read above
. undo incorrect match between du and dev above
. search system devices for new location of PVID
. update devices file with new devnames for PVIDs on renamed devices
. label_scan the renamed devs
- continue with command processing
2020-06-23 21:25:41 +03:00
static int _setup_devices_list ( struct cmd_context * cmd )
{
struct dm_str_list * strl ;
struct dev_use * du ;
/*
* For each - - devices arg , add a du to cmd - > use_devices .
* The du has devname is the devices arg value .
*/
dm_list_iterate_items ( strl , & cmd - > deviceslist ) {
2021-04-23 16:03:11 +03:00
if ( ! ( du = dm_pool_zalloc ( cmd - > mem , sizeof ( struct dev_use ) ) ) )
device usage based on devices file
The LVM devices file lists devices that lvm can use. The default
file is /etc/lvm/devices/system.devices, and the lvmdevices(8)
command is used to add or remove device entries. If the file
does not exist, or if lvm.conf includes use_devicesfile=0, then
lvm will not use a devices file. When the devices file is in use,
the regex filter is not used, and the filter settings in lvm.conf
or on the command line are ignored.
LVM records devices in the devices file using hardware-specific
IDs, such as the WWID, and attempts to use subsystem-specific
IDs for virtual device types. These device IDs are also written
in the VG metadata. When no hardware or virtual ID is available,
lvm falls back using the unstable device name as the device ID.
When devnames are used, lvm performs extra scanning to find
devices if their devname changes, e.g. after reboot.
When proper device IDs are used, an lvm command will not look
at devices outside the devices file, but when devnames are used
as a fallback, lvm will scan devices outside the devices file
to locate PVs on renamed devices. A config setting
search_for_devnames can be used to control the scanning for
renamed devname entries.
Related to the devices file, the new command option
--devices <devnames> allows a list of devices to be specified for
the command to use, overriding the devices file. The listed
devices act as a sort of devices file in terms of limiting which
devices lvm will see and use. Devices that are not listed will
appear to be missing to the lvm command.
Multiple devices files can be kept in /etc/lvm/devices, which
allows lvm to be used with different sets of devices, e.g.
system devices do not need to be exposed to a specific application,
and the application can use lvm on its own set of devices that are
not exposed to the system. The option --devicesfile <filename> is
used to select the devices file to use with the command. Without
the option set, the default system devices file is used.
Setting --devicesfile "" causes lvm to not use a devices file.
An existing, empty devices file means lvm will see no devices.
The new command vgimportdevices adds PVs from a VG to the devices
file and updates the VG metadata to include the device IDs.
vgimportdevices -a will import all VGs into the system devices file.
LVM commands run by dmeventd not use a devices file by default,
and will look at all devices on the system. A devices file can
be created for dmeventd (/etc/lvm/devices/dmeventd.devices) If
this file exists, lvm commands run by dmeventd will use it.
Internal implementaion:
- device_ids_read - read the devices file
. add struct dev_use (du) to cmd->use_devices for each devices file entry
- dev_cache_scan - get /dev entries
. add struct device (dev) to dev_cache for each device on the system
- device_ids_match - match devices file entries to /dev entries
. match each du on cmd->use_devices to a dev in dev_cache, using device ID
. on match, set du->dev, dev->id, dev->flags MATCHED_USE_ID
- label_scan - read lvm headers and metadata from devices
. filters are applied, those that do not need data from the device
. filter-deviceid skips devs without MATCHED_USE_ID, i.e.
skips /dev entries that are not listed in the devices file
. read lvm label from dev
. filters are applied, those that use data from the device
. read lvm metadata from dev
. add info/vginfo structs for PVs/VGs (info is "lvmcache")
- device_ids_find_renamed_devs - handle devices with unstable devname ID
where devname changed
. this step only needed when devs do not have proper device IDs,
and their dev names change, e.g. after reboot sdb becomes sdc.
. detect incorrect match because PVID in the devices file entry
does not match the PVID found when the device was read above
. undo incorrect match between du and dev above
. search system devices for new location of PVID
. update devices file with new devnames for PVIDs on renamed devices
. label_scan the renamed devs
- continue with command processing
2020-06-23 21:25:41 +03:00
return_0 ;
2021-04-23 16:03:11 +03:00
if ( ! ( du - > devname = dm_pool_strdup ( cmd - > mem , strl - > str ) ) )
device usage based on devices file
The LVM devices file lists devices that lvm can use. The default
file is /etc/lvm/devices/system.devices, and the lvmdevices(8)
command is used to add or remove device entries. If the file
does not exist, or if lvm.conf includes use_devicesfile=0, then
lvm will not use a devices file. When the devices file is in use,
the regex filter is not used, and the filter settings in lvm.conf
or on the command line are ignored.
LVM records devices in the devices file using hardware-specific
IDs, such as the WWID, and attempts to use subsystem-specific
IDs for virtual device types. These device IDs are also written
in the VG metadata. When no hardware or virtual ID is available,
lvm falls back using the unstable device name as the device ID.
When devnames are used, lvm performs extra scanning to find
devices if their devname changes, e.g. after reboot.
When proper device IDs are used, an lvm command will not look
at devices outside the devices file, but when devnames are used
as a fallback, lvm will scan devices outside the devices file
to locate PVs on renamed devices. A config setting
search_for_devnames can be used to control the scanning for
renamed devname entries.
Related to the devices file, the new command option
--devices <devnames> allows a list of devices to be specified for
the command to use, overriding the devices file. The listed
devices act as a sort of devices file in terms of limiting which
devices lvm will see and use. Devices that are not listed will
appear to be missing to the lvm command.
Multiple devices files can be kept in /etc/lvm/devices, which
allows lvm to be used with different sets of devices, e.g.
system devices do not need to be exposed to a specific application,
and the application can use lvm on its own set of devices that are
not exposed to the system. The option --devicesfile <filename> is
used to select the devices file to use with the command. Without
the option set, the default system devices file is used.
Setting --devicesfile "" causes lvm to not use a devices file.
An existing, empty devices file means lvm will see no devices.
The new command vgimportdevices adds PVs from a VG to the devices
file and updates the VG metadata to include the device IDs.
vgimportdevices -a will import all VGs into the system devices file.
LVM commands run by dmeventd not use a devices file by default,
and will look at all devices on the system. A devices file can
be created for dmeventd (/etc/lvm/devices/dmeventd.devices) If
this file exists, lvm commands run by dmeventd will use it.
Internal implementaion:
- device_ids_read - read the devices file
. add struct dev_use (du) to cmd->use_devices for each devices file entry
- dev_cache_scan - get /dev entries
. add struct device (dev) to dev_cache for each device on the system
- device_ids_match - match devices file entries to /dev entries
. match each du on cmd->use_devices to a dev in dev_cache, using device ID
. on match, set du->dev, dev->id, dev->flags MATCHED_USE_ID
- label_scan - read lvm headers and metadata from devices
. filters are applied, those that do not need data from the device
. filter-deviceid skips devs without MATCHED_USE_ID, i.e.
skips /dev entries that are not listed in the devices file
. read lvm label from dev
. filters are applied, those that use data from the device
. read lvm metadata from dev
. add info/vginfo structs for PVs/VGs (info is "lvmcache")
- device_ids_find_renamed_devs - handle devices with unstable devname ID
where devname changed
. this step only needed when devs do not have proper device IDs,
and their dev names change, e.g. after reboot sdb becomes sdc.
. detect incorrect match because PVID in the devices file entry
does not match the PVID found when the device was read above
. undo incorrect match between du and dev above
. search system devices for new location of PVID
. update devices file with new devnames for PVIDs on renamed devices
. label_scan the renamed devs
- continue with command processing
2020-06-23 21:25:41 +03:00
return_0 ;
dm_list_add ( & cmd - > use_devices , & du - > list ) ;
}
return 1 ;
}
static int _setup_devices_file_dmeventd ( struct cmd_context * cmd )
{
char path [ PATH_MAX ] ;
struct stat st ;
/*
* When command is run by dmeventd there is no - - devicesfile
* option that can enable / disable the use of a devices file .
*/
if ( ! find_config_tree_bool ( cmd , devices_use_devicesfile_CFG , NULL ) ) {
cmd - > enable_devices_file = 0 ;
return 1 ;
}
/*
* If / etc / lvm / devices / dmeventd . devices exists , then use that .
* The optional dmeventd . devices allows the user to control
* which devices dmeventd will look at and use .
* Otherwise , disable the devices file because dmeventd should
* be able to manage LVs in any VG ( i . e . LVs in a non - system
* devices file . )
*/
if ( dm_snprintf ( path , sizeof ( path ) , " %s/devices/dmeventd.devices " , cmd - > system_dir ) < 0 ) {
log_warn ( " Failed to copy devices path " ) ;
cmd - > enable_devices_file = 0 ;
return 1 ;
}
if ( stat ( path , & st ) ) {
/* No dmeventd.devices, so do not use a devices file. */
cmd - > enable_devices_file = 0 ;
return 1 ;
}
cmd - > enable_devices_file = 1 ;
( void ) dm_strncpy ( cmd - > devices_file_path , path , sizeof ( cmd - > devices_file_path ) ) ;
return 1 ;
}
int setup_devices_file ( struct cmd_context * cmd )
{
char dirpath [ PATH_MAX ] ;
const char * filename = NULL ;
struct stat st ;
int rv ;
if ( cmd - > run_by_dmeventd )
return _setup_devices_file_dmeventd ( cmd ) ;
if ( cmd - > devicesfile ) {
/* --devicesfile <filename> or "" has been set which overrides
lvm . conf settings use_devicesfile and devicesfile . */
if ( ! strlen ( cmd - > devicesfile ) )
cmd - > enable_devices_file = 0 ;
else {
cmd - > enable_devices_file = 1 ;
filename = cmd - > devicesfile ;
}
/* TODO: print a warning if --devicesfile system.devices
while lvm . conf use_devicesfile = 0. */
} else {
if ( ! find_config_tree_bool ( cmd , devices_use_devicesfile_CFG , NULL ) )
cmd - > enable_devices_file = 0 ;
else {
cmd - > enable_devices_file = 1 ;
filename = find_config_tree_str ( cmd , devices_devicesfile_CFG , NULL ) ;
if ( ! validate_name ( filename ) ) {
log_error ( " Invalid devices file name from config setting \" %s \" . " , filename ) ;
return 0 ;
}
}
}
if ( ! cmd - > enable_devices_file )
return 1 ;
if ( dm_snprintf ( dirpath , sizeof ( dirpath ) , " %s/devices " , cmd - > system_dir ) < 0 ) {
log_error ( " Failed to copy devices dir path " ) ;
return 0 ;
}
if ( stat ( dirpath , & st ) ) {
log_debug ( " Creating %s. " , dirpath ) ;
dm_prepare_selinux_context ( dirpath , S_IFDIR ) ;
rv = mkdir ( dirpath , 0755 ) ;
dm_prepare_selinux_context ( NULL , 0 ) ;
if ( ( rv < 0 ) & & stat ( dirpath , & st ) ) {
log_error ( " Failed to create %s %d " , dirpath , errno ) ;
return 0 ;
}
}
if ( dm_snprintf ( cmd - > devices_file_path , sizeof ( cmd - > devices_file_path ) ,
" %s/devices/%s " , cmd - > system_dir , filename ) < 0 ) {
log_error ( " Failed to copy devices file path " ) ;
return 0 ;
}
return 1 ;
}
/*
* Add all system devices to dev - cache , and attempt to
* match all devices_file entries to dev - cache entries .
*/
2021-10-19 00:24:24 +03:00
int setup_devices ( struct cmd_context * cmd )
device usage based on devices file
The LVM devices file lists devices that lvm can use. The default
file is /etc/lvm/devices/system.devices, and the lvmdevices(8)
command is used to add or remove device entries. If the file
does not exist, or if lvm.conf includes use_devicesfile=0, then
lvm will not use a devices file. When the devices file is in use,
the regex filter is not used, and the filter settings in lvm.conf
or on the command line are ignored.
LVM records devices in the devices file using hardware-specific
IDs, such as the WWID, and attempts to use subsystem-specific
IDs for virtual device types. These device IDs are also written
in the VG metadata. When no hardware or virtual ID is available,
lvm falls back using the unstable device name as the device ID.
When devnames are used, lvm performs extra scanning to find
devices if their devname changes, e.g. after reboot.
When proper device IDs are used, an lvm command will not look
at devices outside the devices file, but when devnames are used
as a fallback, lvm will scan devices outside the devices file
to locate PVs on renamed devices. A config setting
search_for_devnames can be used to control the scanning for
renamed devname entries.
Related to the devices file, the new command option
--devices <devnames> allows a list of devices to be specified for
the command to use, overriding the devices file. The listed
devices act as a sort of devices file in terms of limiting which
devices lvm will see and use. Devices that are not listed will
appear to be missing to the lvm command.
Multiple devices files can be kept in /etc/lvm/devices, which
allows lvm to be used with different sets of devices, e.g.
system devices do not need to be exposed to a specific application,
and the application can use lvm on its own set of devices that are
not exposed to the system. The option --devicesfile <filename> is
used to select the devices file to use with the command. Without
the option set, the default system devices file is used.
Setting --devicesfile "" causes lvm to not use a devices file.
An existing, empty devices file means lvm will see no devices.
The new command vgimportdevices adds PVs from a VG to the devices
file and updates the VG metadata to include the device IDs.
vgimportdevices -a will import all VGs into the system devices file.
LVM commands run by dmeventd not use a devices file by default,
and will look at all devices on the system. A devices file can
be created for dmeventd (/etc/lvm/devices/dmeventd.devices) If
this file exists, lvm commands run by dmeventd will use it.
Internal implementaion:
- device_ids_read - read the devices file
. add struct dev_use (du) to cmd->use_devices for each devices file entry
- dev_cache_scan - get /dev entries
. add struct device (dev) to dev_cache for each device on the system
- device_ids_match - match devices file entries to /dev entries
. match each du on cmd->use_devices to a dev in dev_cache, using device ID
. on match, set du->dev, dev->id, dev->flags MATCHED_USE_ID
- label_scan - read lvm headers and metadata from devices
. filters are applied, those that do not need data from the device
. filter-deviceid skips devs without MATCHED_USE_ID, i.e.
skips /dev entries that are not listed in the devices file
. read lvm label from dev
. filters are applied, those that use data from the device
. read lvm metadata from dev
. add info/vginfo structs for PVs/VGs (info is "lvmcache")
- device_ids_find_renamed_devs - handle devices with unstable devname ID
where devname changed
. this step only needed when devs do not have proper device IDs,
and their dev names change, e.g. after reboot sdb becomes sdc.
. detect incorrect match because PVID in the devices file entry
does not match the PVID found when the device was read above
. undo incorrect match between du and dev above
. search system devices for new location of PVID
. update devices file with new devnames for PVIDs on renamed devices
. label_scan the renamed devs
- continue with command processing
2020-06-23 21:25:41 +03:00
{
int file_exists ;
int lock_mode = 0 ;
if ( cmd - > enable_devices_list ) {
if ( ! _setup_devices_list ( cmd ) )
return_0 ;
goto scan ;
}
if ( ! setup_devices_file ( cmd ) )
return_0 ;
if ( ! cmd - > enable_devices_file )
goto scan ;
file_exists = devices_file_exists ( cmd ) ;
2022-05-27 22:27:03 +03:00
/*
* Fail if user specifies a file name that doesn ' t exist and
* the command is not creating a new devices file .
*/
if ( ! file_exists & & ! cmd - > create_edit_devices_file & & cmd - > devicesfile & & strlen ( cmd - > devicesfile ) ) {
log_error ( " Devices file not found: %s " , cmd - > devices_file_path ) ;
return 0 ;
}
device usage based on devices file
The LVM devices file lists devices that lvm can use. The default
file is /etc/lvm/devices/system.devices, and the lvmdevices(8)
command is used to add or remove device entries. If the file
does not exist, or if lvm.conf includes use_devicesfile=0, then
lvm will not use a devices file. When the devices file is in use,
the regex filter is not used, and the filter settings in lvm.conf
or on the command line are ignored.
LVM records devices in the devices file using hardware-specific
IDs, such as the WWID, and attempts to use subsystem-specific
IDs for virtual device types. These device IDs are also written
in the VG metadata. When no hardware or virtual ID is available,
lvm falls back using the unstable device name as the device ID.
When devnames are used, lvm performs extra scanning to find
devices if their devname changes, e.g. after reboot.
When proper device IDs are used, an lvm command will not look
at devices outside the devices file, but when devnames are used
as a fallback, lvm will scan devices outside the devices file
to locate PVs on renamed devices. A config setting
search_for_devnames can be used to control the scanning for
renamed devname entries.
Related to the devices file, the new command option
--devices <devnames> allows a list of devices to be specified for
the command to use, overriding the devices file. The listed
devices act as a sort of devices file in terms of limiting which
devices lvm will see and use. Devices that are not listed will
appear to be missing to the lvm command.
Multiple devices files can be kept in /etc/lvm/devices, which
allows lvm to be used with different sets of devices, e.g.
system devices do not need to be exposed to a specific application,
and the application can use lvm on its own set of devices that are
not exposed to the system. The option --devicesfile <filename> is
used to select the devices file to use with the command. Without
the option set, the default system devices file is used.
Setting --devicesfile "" causes lvm to not use a devices file.
An existing, empty devices file means lvm will see no devices.
The new command vgimportdevices adds PVs from a VG to the devices
file and updates the VG metadata to include the device IDs.
vgimportdevices -a will import all VGs into the system devices file.
LVM commands run by dmeventd not use a devices file by default,
and will look at all devices on the system. A devices file can
be created for dmeventd (/etc/lvm/devices/dmeventd.devices) If
this file exists, lvm commands run by dmeventd will use it.
Internal implementaion:
- device_ids_read - read the devices file
. add struct dev_use (du) to cmd->use_devices for each devices file entry
- dev_cache_scan - get /dev entries
. add struct device (dev) to dev_cache for each device on the system
- device_ids_match - match devices file entries to /dev entries
. match each du on cmd->use_devices to a dev in dev_cache, using device ID
. on match, set du->dev, dev->id, dev->flags MATCHED_USE_ID
- label_scan - read lvm headers and metadata from devices
. filters are applied, those that do not need data from the device
. filter-deviceid skips devs without MATCHED_USE_ID, i.e.
skips /dev entries that are not listed in the devices file
. read lvm label from dev
. filters are applied, those that use data from the device
. read lvm metadata from dev
. add info/vginfo structs for PVs/VGs (info is "lvmcache")
- device_ids_find_renamed_devs - handle devices with unstable devname ID
where devname changed
. this step only needed when devs do not have proper device IDs,
and their dev names change, e.g. after reboot sdb becomes sdc.
. detect incorrect match because PVID in the devices file entry
does not match the PVID found when the device was read above
. undo incorrect match between du and dev above
. search system devices for new location of PVID
. update devices file with new devnames for PVIDs on renamed devices
. label_scan the renamed devs
- continue with command processing
2020-06-23 21:25:41 +03:00
/*
* Removing the devices file is another way of disabling the use of
* a devices file , unless the command creates the devices file .
*/
if ( ! file_exists & & ! cmd - > create_edit_devices_file ) {
log_debug ( " Devices file not found, ignoring. " ) ;
cmd - > enable_devices_file = 0 ;
goto scan ;
}
/*
* Don ' t let pvcreate or vgcreate create a new system devices file
* unless it ' s specified explicitly with - - devicesfile . This avoids
* a problem where a system is running with existing PVs , and is
* not using a devices file based on the fact that the system
* devices file doesn ' t exist . If the user simply uses pvcreate
* to create a new PV , they almost certainly do not want that to
* create a new system devices file containing the new PV and none
* of the existing PVs that the system is already using .
* However , if they use the vgimportdevices or lvmdevices command
* then they are clearly intending to use the devices file , so we
* can create it . Or , if they specify a non - system devices file
* with pvcreate / vgcreate , then they clearly want to use a devices
* file and we can create it ( and creating a non - system devices file
* would not cause existing PVs to disappear from the main system . )
*
* An exception is if pvcreate / vgcreate get to device_id_write and
* did not see any existing VGs during label scan . In that case
* they will create a new system devices file , since there will be
* no VGs that the new file would hide .
*/
if ( cmd - > create_edit_devices_file & & ! cmd - > devicesfile & & ! file_exists & &
( ! strncmp ( cmd - > name , " pvcreate " , 8 ) | | ! strncmp ( cmd - > name , " vgcreate " , 8 ) ) ) {
/* The command will decide in device_ids_write whether to create
a new system devices file . */
cmd - > enable_devices_file = 0 ;
cmd - > pending_devices_file = 1 ;
goto scan ;
}
if ( ! file_exists & & cmd - > sysinit ) {
cmd - > enable_devices_file = 0 ;
goto scan ;
}
if ( ! file_exists ) {
/*
2022-08-30 22:40:48 +03:00
* pvcreate / vgcreate create a new devices file here if it
* doesn ' t exist . They have create_edit_devices_file = 1.
* First create / lock - ex the devices file lockfile .
device usage based on devices file
The LVM devices file lists devices that lvm can use. The default
file is /etc/lvm/devices/system.devices, and the lvmdevices(8)
command is used to add or remove device entries. If the file
does not exist, or if lvm.conf includes use_devicesfile=0, then
lvm will not use a devices file. When the devices file is in use,
the regex filter is not used, and the filter settings in lvm.conf
or on the command line are ignored.
LVM records devices in the devices file using hardware-specific
IDs, such as the WWID, and attempts to use subsystem-specific
IDs for virtual device types. These device IDs are also written
in the VG metadata. When no hardware or virtual ID is available,
lvm falls back using the unstable device name as the device ID.
When devnames are used, lvm performs extra scanning to find
devices if their devname changes, e.g. after reboot.
When proper device IDs are used, an lvm command will not look
at devices outside the devices file, but when devnames are used
as a fallback, lvm will scan devices outside the devices file
to locate PVs on renamed devices. A config setting
search_for_devnames can be used to control the scanning for
renamed devname entries.
Related to the devices file, the new command option
--devices <devnames> allows a list of devices to be specified for
the command to use, overriding the devices file. The listed
devices act as a sort of devices file in terms of limiting which
devices lvm will see and use. Devices that are not listed will
appear to be missing to the lvm command.
Multiple devices files can be kept in /etc/lvm/devices, which
allows lvm to be used with different sets of devices, e.g.
system devices do not need to be exposed to a specific application,
and the application can use lvm on its own set of devices that are
not exposed to the system. The option --devicesfile <filename> is
used to select the devices file to use with the command. Without
the option set, the default system devices file is used.
Setting --devicesfile "" causes lvm to not use a devices file.
An existing, empty devices file means lvm will see no devices.
The new command vgimportdevices adds PVs from a VG to the devices
file and updates the VG metadata to include the device IDs.
vgimportdevices -a will import all VGs into the system devices file.
LVM commands run by dmeventd not use a devices file by default,
and will look at all devices on the system. A devices file can
be created for dmeventd (/etc/lvm/devices/dmeventd.devices) If
this file exists, lvm commands run by dmeventd will use it.
Internal implementaion:
- device_ids_read - read the devices file
. add struct dev_use (du) to cmd->use_devices for each devices file entry
- dev_cache_scan - get /dev entries
. add struct device (dev) to dev_cache for each device on the system
- device_ids_match - match devices file entries to /dev entries
. match each du on cmd->use_devices to a dev in dev_cache, using device ID
. on match, set du->dev, dev->id, dev->flags MATCHED_USE_ID
- label_scan - read lvm headers and metadata from devices
. filters are applied, those that do not need data from the device
. filter-deviceid skips devs without MATCHED_USE_ID, i.e.
skips /dev entries that are not listed in the devices file
. read lvm label from dev
. filters are applied, those that use data from the device
. read lvm metadata from dev
. add info/vginfo structs for PVs/VGs (info is "lvmcache")
- device_ids_find_renamed_devs - handle devices with unstable devname ID
where devname changed
. this step only needed when devs do not have proper device IDs,
and their dev names change, e.g. after reboot sdb becomes sdc.
. detect incorrect match because PVID in the devices file entry
does not match the PVID found when the device was read above
. undo incorrect match between du and dev above
. search system devices for new location of PVID
. update devices file with new devnames for PVIDs on renamed devices
. label_scan the renamed devs
- continue with command processing
2020-06-23 21:25:41 +03:00
* Other commands will not use a devices file if none exists .
*/
lock_mode = LOCK_EX ;
if ( ! lock_devices_file ( cmd , lock_mode ) ) {
log_error ( " Failed to lock the devices file to create. " ) ;
return 0 ;
}
/* The file will be created in device_ids_write() */
if ( ! devices_file_exists ( cmd ) )
goto scan ;
} else {
/*
* Commands that intend to edit the devices file have
* edit_devices_file or create_edit_devices_file set ( create if
* they can also create a new devices file ) and lock it ex
* here prior to reading . Other commands that intend to just
* read the devices file lock sh .
*/
lock_mode = ( cmd - > create_edit_devices_file | | cmd - > edit_devices_file ) ? LOCK_EX : LOCK_SH ;
if ( ! lock_devices_file ( cmd , lock_mode ) ) {
log_error ( " Failed to lock the devices file. " ) ;
return 0 ;
}
}
/*
* Read the list of device ids that lvm can use .
* Adds a struct dev_id to cmd - > use_devices for each one .
*/
if ( ! device_ids_read ( cmd ) ) {
log_error ( " Failed to read the devices file. " ) ;
unlock_devices_file ( cmd ) ;
return 0 ;
}
/*
* When the command is editing the devices file , it acquires
* the ex lock above , will later call device_ids_write ( ) , and
* then unlock the lock after writing the file .
* When the command is just reading the devices file , it ' s
* locked sh above just before reading the file , and unlocked
* here after reading .
*/
if ( lock_mode = = LOCK_SH )
unlock_devices_file ( cmd ) ;
scan :
/*
* Add a ' struct device ' to dev - cache for each device available on the system .
* This will not open or read any devices , but may look at sysfs properties .
* This list of devs comes from looking / dev entries , or from asking libudev .
*/
2021-07-02 01:25:43 +03:00
dev_cache_scan ( cmd ) ;
device usage based on devices file
The LVM devices file lists devices that lvm can use. The default
file is /etc/lvm/devices/system.devices, and the lvmdevices(8)
command is used to add or remove device entries. If the file
does not exist, or if lvm.conf includes use_devicesfile=0, then
lvm will not use a devices file. When the devices file is in use,
the regex filter is not used, and the filter settings in lvm.conf
or on the command line are ignored.
LVM records devices in the devices file using hardware-specific
IDs, such as the WWID, and attempts to use subsystem-specific
IDs for virtual device types. These device IDs are also written
in the VG metadata. When no hardware or virtual ID is available,
lvm falls back using the unstable device name as the device ID.
When devnames are used, lvm performs extra scanning to find
devices if their devname changes, e.g. after reboot.
When proper device IDs are used, an lvm command will not look
at devices outside the devices file, but when devnames are used
as a fallback, lvm will scan devices outside the devices file
to locate PVs on renamed devices. A config setting
search_for_devnames can be used to control the scanning for
renamed devname entries.
Related to the devices file, the new command option
--devices <devnames> allows a list of devices to be specified for
the command to use, overriding the devices file. The listed
devices act as a sort of devices file in terms of limiting which
devices lvm will see and use. Devices that are not listed will
appear to be missing to the lvm command.
Multiple devices files can be kept in /etc/lvm/devices, which
allows lvm to be used with different sets of devices, e.g.
system devices do not need to be exposed to a specific application,
and the application can use lvm on its own set of devices that are
not exposed to the system. The option --devicesfile <filename> is
used to select the devices file to use with the command. Without
the option set, the default system devices file is used.
Setting --devicesfile "" causes lvm to not use a devices file.
An existing, empty devices file means lvm will see no devices.
The new command vgimportdevices adds PVs from a VG to the devices
file and updates the VG metadata to include the device IDs.
vgimportdevices -a will import all VGs into the system devices file.
LVM commands run by dmeventd not use a devices file by default,
and will look at all devices on the system. A devices file can
be created for dmeventd (/etc/lvm/devices/dmeventd.devices) If
this file exists, lvm commands run by dmeventd will use it.
Internal implementaion:
- device_ids_read - read the devices file
. add struct dev_use (du) to cmd->use_devices for each devices file entry
- dev_cache_scan - get /dev entries
. add struct device (dev) to dev_cache for each device on the system
- device_ids_match - match devices file entries to /dev entries
. match each du on cmd->use_devices to a dev in dev_cache, using device ID
. on match, set du->dev, dev->id, dev->flags MATCHED_USE_ID
- label_scan - read lvm headers and metadata from devices
. filters are applied, those that do not need data from the device
. filter-deviceid skips devs without MATCHED_USE_ID, i.e.
skips /dev entries that are not listed in the devices file
. read lvm label from dev
. filters are applied, those that use data from the device
. read lvm metadata from dev
. add info/vginfo structs for PVs/VGs (info is "lvmcache")
- device_ids_find_renamed_devs - handle devices with unstable devname ID
where devname changed
. this step only needed when devs do not have proper device IDs,
and their dev names change, e.g. after reboot sdb becomes sdc.
. detect incorrect match because PVID in the devices file entry
does not match the PVID found when the device was read above
. undo incorrect match between du and dev above
. search system devices for new location of PVID
. update devices file with new devnames for PVIDs on renamed devices
. label_scan the renamed devs
- continue with command processing
2020-06-23 21:25:41 +03:00
/*
* Match entries from cmd - > use_devices with device structs in dev - cache .
*/
device_ids_match ( cmd ) ;
return 1 ;
}
/*
* The alternative to setup_devices ( ) when the command is interested
* in using only one PV .
*
* Add one system device to dev - cache , and attempt to
* match its dev - cache entry to a devices_file entry .
*/
int setup_device ( struct cmd_context * cmd , const char * devname )
{
struct stat buf ;
struct device * dev ;
if ( cmd - > enable_devices_list ) {
if ( ! _setup_devices_list ( cmd ) )
return_0 ;
goto scan ;
}
if ( ! setup_devices_file ( cmd ) )
return_0 ;
if ( ! cmd - > enable_devices_file )
goto scan ;
if ( ! devices_file_exists ( cmd ) ) {
log_debug ( " Devices file not found, ignoring. " ) ;
cmd - > enable_devices_file = 0 ;
goto scan ;
}
if ( ! lock_devices_file ( cmd , LOCK_SH ) ) {
log_error ( " Failed to lock the devices file to read. " ) ;
return 0 ;
}
if ( ! device_ids_read ( cmd ) ) {
log_error ( " Failed to read the devices file. " ) ;
unlock_devices_file ( cmd ) ;
return 0 ;
}
unlock_devices_file ( cmd ) ;
scan :
if ( stat ( devname , & buf ) < 0 ) {
log_error ( " Cannot access device %s. " , devname ) ;
return 0 ;
}
if ( ! S_ISBLK ( buf . st_mode ) ) {
log_error ( " Invaild device type %s. " , devname ) ;
return 0 ;
}
if ( ! _insert_dev ( devname , buf . st_rdev ) )
return_0 ;
if ( ! ( dev = ( struct device * ) dm_hash_lookup ( _cache . names , devname ) ) )
return_0 ;
/* Match this device to an entry in devices_file so it will not
be rejected by filter - deviceid . */
if ( cmd - > enable_devices_file )
device_ids_match_dev ( cmd , dev ) ;
return 1 ;
}
2021-10-19 00:24:24 +03:00
/*
2021-11-05 20:19:35 +03:00
* autoactivation is specialized / optimized to look only at command args ,
2021-10-19 00:24:24 +03:00
* so this just sets up the devices file , then individual devices are
2021-11-05 20:19:35 +03:00
* added to dev - cache and matched with device_ids .
2021-10-19 00:24:24 +03:00
*/
2021-11-05 20:19:35 +03:00
int setup_devices_for_online_autoactivation ( struct cmd_context * cmd )
2021-10-19 00:24:24 +03:00
{
if ( cmd - > enable_devices_list ) {
if ( ! _setup_devices_list ( cmd ) )
return_0 ;
return 1 ;
}
if ( ! setup_devices_file ( cmd ) )
return_0 ;
if ( ! cmd - > enable_devices_file )
return 1 ;
if ( ! devices_file_exists ( cmd ) ) {
log_debug ( " Devices file not found, ignoring. " ) ;
cmd - > enable_devices_file = 0 ;
return 1 ;
}
if ( ! lock_devices_file ( cmd , LOCK_SH ) ) {
log_error ( " Failed to lock the devices file to read. " ) ;
return 0 ;
}
if ( ! device_ids_read ( cmd ) ) {
log_error ( " Failed to read the devices file. " ) ;
unlock_devices_file ( cmd ) ;
return 0 ;
}
unlock_devices_file ( cmd ) ;
return 1 ;
}
/* Get a device name from a devno. */
static char * _get_devname_from_devno ( struct cmd_context * cmd , dev_t devno )
{
char path [ PATH_MAX ] ;
2021-12-01 23:36:40 +03:00
char devname [ PATH_MAX ] = { 0 } ;
2021-10-19 00:24:24 +03:00
char namebuf [ NAME_LEN ] ;
char line [ 1024 ] ;
2023-07-13 13:32:44 +03:00
unsigned major = MAJOR ( devno ) ;
unsigned minor = MINOR ( devno ) ;
unsigned line_major ;
unsigned line_minor ;
2021-10-19 00:24:24 +03:00
uint64_t line_blocks ;
DIR * dir ;
struct dirent * dirent ;
FILE * fp ;
2021-12-01 23:36:40 +03:00
if ( ! devno )
return NULL ;
2021-10-19 00:24:24 +03:00
/*
* $ ls / sys / dev / block / 8 : 0 / device / block /
* sda
*/
if ( major_is_scsi_device ( cmd - > dev_types , major ) ) {
if ( dm_snprintf ( path , sizeof ( path ) , " %sdev/block/%d:%d/device/block " ,
dm_sysfs_dir ( ) , major , minor ) < 0 ) {
return NULL ;
}
if ( ! ( dir = opendir ( path ) ) )
2021-11-06 00:21:23 +03:00
goto try_partition ;
2021-10-19 00:24:24 +03:00
while ( ( dirent = readdir ( dir ) ) ) {
if ( dirent - > d_name [ 0 ] = = ' . ' )
continue ;
if ( dm_snprintf ( devname , sizeof ( devname ) , " /dev/%s " , dirent - > d_name ) < 0 ) {
devname [ 0 ] = ' \0 ' ;
stack ;
}
break ;
}
closedir ( dir ) ;
if ( devname [ 0 ] ) {
log_debug ( " Found %s for %d:%d from sys " , devname , major , minor ) ;
return _strdup ( devname ) ;
}
return NULL ;
}
/*
* $ cat / sys / dev / block / 253 : 3 / dm / name
* mpatha
*/
if ( major = = cmd - > dev_types - > device_mapper_major ) {
if ( dm_snprintf ( path , sizeof ( path ) , " %sdev/block/%d:%d/dm/name " ,
dm_sysfs_dir ( ) , major , minor ) < 0 ) {
return NULL ;
}
if ( ! get_sysfs_value ( path , namebuf , sizeof ( namebuf ) , 0 ) )
return NULL ;
if ( dm_snprintf ( devname , sizeof ( devname ) , " /dev/mapper/%s " , namebuf ) < 0 ) {
devname [ 0 ] = ' \0 ' ;
stack ;
}
if ( devname [ 0 ] ) {
2021-11-08 20:30:25 +03:00
log_debug ( " Found %s for %d:%d from sys dm " , devname , major , minor ) ;
2021-10-19 00:24:24 +03:00
return _strdup ( devname ) ;
}
return NULL ;
}
/*
* / proc / partitions lists
* major minor # blocks name
*/
2021-11-06 00:21:23 +03:00
try_partition :
2021-10-19 00:24:24 +03:00
if ( ! ( fp = fopen ( " /proc/partitions " , " r " ) ) )
return NULL ;
while ( fgets ( line , sizeof ( line ) , fp ) ) {
if ( sscanf ( line , " %u %u %llu %s " , & line_major , & line_minor , ( unsigned long long * ) & line_blocks , namebuf ) ! = 4 )
continue ;
if ( line_major ! = major )
continue ;
if ( line_minor ! = minor )
continue ;
if ( dm_snprintf ( devname , sizeof ( devname ) , " /dev/%s " , namebuf ) < 0 ) {
devname [ 0 ] = ' \0 ' ;
stack ;
}
break ;
}
fclose ( fp ) ;
if ( devname [ 0 ] ) {
log_debug ( " Found %s for %d:%d from proc " , devname , major , minor ) ;
return _strdup ( devname ) ;
}
/*
* If necessary , this could continue searching by stat ' ing / dev entries .
*/
return NULL ;
}
int setup_devname_in_dev_cache ( struct cmd_context * cmd , const char * devname )
{
struct stat buf ;
if ( stat ( devname , & buf ) < 0 ) {
log_error ( " Cannot access device %s. " , devname ) ;
return 0 ;
}
if ( ! S_ISBLK ( buf . st_mode ) ) {
log_error ( " Invaild device type %s. " , devname ) ;
return 0 ;
}
if ( ! _insert_dev ( devname , buf . st_rdev ) )
return_0 ;
2023-04-26 17:32:23 +03:00
if ( ! dm_hash_lookup ( _cache . names , devname ) )
2021-10-19 00:24:24 +03:00
return_0 ;
return 1 ;
}
int setup_devno_in_dev_cache ( struct cmd_context * cmd , dev_t devno )
{
const char * devname ;
if ( ! ( devname = _get_devname_from_devno ( cmd , devno ) ) )
return_0 ;
return setup_devname_in_dev_cache ( cmd , devname ) ;
}
2021-11-09 01:02:48 +03:00
struct device * setup_dev_in_dev_cache ( struct cmd_context * cmd , dev_t devno , const char * devname )
{
struct device * dev ;
struct stat buf ;
int major = ( int ) MAJOR ( devno ) ;
int minor = ( int ) MINOR ( devno ) ;
if ( devname ) {
if ( stat ( devname , & buf ) < 0 ) {
log_error ( " Cannot access device %s for %d:%d. " , devname , major , minor ) ;
2021-12-01 23:36:40 +03:00
if ( ! devno )
return_NULL ;
2021-11-09 01:02:48 +03:00
if ( ! ( devname = _get_devname_from_devno ( cmd , devno ) ) ) {
log_error ( " No device name found from %d:%d. " , major , minor ) ;
return_NULL ;
}
if ( stat ( devname , & buf ) < 0 ) {
log_error ( " Cannot access device %s from %d:%d. " , devname , major , minor ) ;
return_NULL ;
}
}
} else {
if ( ! ( devname = _get_devname_from_devno ( cmd , devno ) ) ) {
log_error ( " No device name found from %d:%d. " , major , minor ) ;
return_NULL ;
}
if ( stat ( devname , & buf ) < 0 ) {
log_error ( " Cannot access device %s from %d:%d. " , devname , major , minor ) ;
return_NULL ;
}
}
if ( ! S_ISBLK ( buf . st_mode ) ) {
log_error ( " Invaild device type %s. " , devname ) ;
return_NULL ;
}
if ( devno & & ( buf . st_rdev ! = devno ) ) {
log_warn ( " Found %s devno %d:%d expected %d:%d. " , devname ,
( int ) MAJOR ( buf . st_rdev ) , ( int ) MINOR ( buf . st_rdev ) , major , minor ) ;
}
if ( ! _insert_dev ( devname , buf . st_rdev ) )
return_NULL ;
if ( ! ( dev = ( struct device * ) dm_hash_lookup ( _cache . names , devname ) ) ) {
log_error ( " Device lookup failed for %d:%d %s " , major , minor , devname ) ;
return_NULL ;
}
return dev ;
}