diff options
author | David Teigland <teigland@redhat.com> | 2020-06-23 13:25:41 -0500 |
---|---|---|
committer | David Teigland <teigland@redhat.com> | 2020-06-23 17:00:35 -0500 |
commit | 024ce344297b1e496512c1dd1e50c646a0a74790 (patch) | |
tree | ad997f12a3479c640fecd717c0e38531ea7391c0 | |
parent | b0787033023fdc213f3fdd3efc488e70f44e1fcd (diff) | |
download | lvm2-dev-dct-deviceid-1.tar.gz |
lvm_devices file with device_id and filteringdev-dct-deviceid-1
-rw-r--r-- | lib/Makefile.in | 2 | ||||
-rw-r--r-- | lib/commands/toolcontext.c | 25 | ||||
-rw-r--r-- | lib/commands/toolcontext.h | 3 | ||||
-rw-r--r-- | lib/config/config_settings.h | 3 | ||||
-rw-r--r-- | lib/config/defaults.h | 2 | ||||
-rw-r--r-- | lib/device/dev-cache.c | 52 | ||||
-rw-r--r-- | lib/device/dev-cache.h | 5 | ||||
-rw-r--r-- | lib/device/device.h | 25 | ||||
-rw-r--r-- | lib/device/device_id.c | 830 | ||||
-rw-r--r-- | lib/device/device_id.h | 38 | ||||
-rw-r--r-- | lib/filters/filter-deviceid.c | 56 | ||||
-rw-r--r-- | lib/filters/filter-regex.c | 6 | ||||
-rw-r--r-- | lib/filters/filter.h | 1 | ||||
-rw-r--r-- | lib/format_text/export.c | 6 | ||||
-rw-r--r-- | lib/format_text/import_vsn1.c | 34 | ||||
-rw-r--r-- | lib/label/label.c | 37 | ||||
-rw-r--r-- | lib/metadata/pv.h | 2 | ||||
-rw-r--r-- | tools/args.h | 9 | ||||
-rw-r--r-- | tools/command-lines.in | 5 | ||||
-rw-r--r-- | tools/lvmcmdline.c | 6 | ||||
-rw-r--r-- | tools/pvscan.c | 9 | ||||
-rw-r--r-- | tools/toollib.c | 19 |
22 files changed, 1141 insertions, 34 deletions
diff --git a/lib/Makefile.in b/lib/Makefile.in index 8e50ec45c..3409cbd8c 100644 --- a/lib/Makefile.in +++ b/lib/Makefile.in @@ -29,6 +29,7 @@ SOURCES =\ device/bcache.c \ device/bcache-utils.c \ device/dev-cache.c \ + device/device_id.c \ device/dev-ext.c \ device/dev-io.c \ device/dev-md.c \ @@ -52,6 +53,7 @@ SOURCES =\ filters/filter-usable.c \ filters/filter-internal.c \ filters/filter-signature.c \ + filters/filter-deviceid.c \ format_text/archive.c \ format_text/archiver.c \ format_text/export.c \ diff --git a/lib/commands/toolcontext.c b/lib/commands/toolcontext.c index 63b6811e5..e4e7b6757 100644 --- a/lib/commands/toolcontext.c +++ b/lib/commands/toolcontext.c @@ -1066,7 +1066,14 @@ static int _init_dev_cache(struct cmd_context *cmd) return 1; } -#define MAX_FILTERS 10 +static int _init_device_ids(struct cmd_context *cmd) +{ + dm_list_init(&cmd->use_device_ids); + cmd->devices_file = find_config_tree_str(cmd, devices_devicesfile_CFG, NULL); + return 1; +} + +#define MAX_FILTERS 11 static struct dev_filter *_init_filter_chain(struct cmd_context *cmd) { @@ -1085,6 +1092,9 @@ static struct dev_filter *_init_filter_chain(struct cmd_context *cmd) * sysfs filter. Only available on 2.6 kernels. Non-critical. * Listed first because it's very efficient at eliminating * unavailable devices. + * + * TODO: I suspect that using the lvm_type and device_id + * filters before this one may be more efficient. */ if (find_config_tree_bool(cmd, devices_sysfs_scan_CFG, NULL)) { if ((filters[nr_filt] = sysfs_filter_create())) @@ -1123,6 +1133,13 @@ static struct dev_filter *_init_filter_chain(struct cmd_context *cmd) } nr_filt++; + /* filter based on the device_ids saved in the lvm_devices file */ + if (!(filters[nr_filt] = deviceid_filter_create(cmd))) { + log_error("Failed to create deviceid device filter"); + goto bad; + } + nr_filt++; + /* usable device filter. Required. */ if (!(filters[nr_filt] = usable_filter_create(cmd, cmd->dev_types, FILTER_MODE_NO_LVMETAD))) { log_error("Failed to create usabled device filter"); @@ -1717,6 +1734,9 @@ struct cmd_context *create_toolcontext(unsigned is_clvmd, if (!_init_dev_cache(cmd)) goto_out; + if (!_init_device_ids(cmd)) + return_0; + memlock_init(cmd); if (!_init_formats(cmd)) @@ -1921,6 +1941,9 @@ int refresh_toolcontext(struct cmd_context *cmd) if (!_init_dev_cache(cmd)) return_0; + if (!_init_device_ids(cmd)) + return_0; + if (!_init_formats(cmd)) return_0; diff --git a/lib/commands/toolcontext.h b/lib/commands/toolcontext.h index c09558a42..f8fc33b31 100644 --- a/lib/commands/toolcontext.h +++ b/lib/commands/toolcontext.h @@ -182,13 +182,16 @@ struct cmd_context { unsigned pvscan_recreate_hints:1; /* enable special case hint handling for pvscan --cache */ unsigned scan_lvs:1; unsigned wipe_outdated_pvs:1; + unsigned enable_device_ids:1; /* * Devices and filtering. */ struct dev_filter *filter; struct dm_list hints; + struct dm_list use_device_ids; const char *md_component_checks; + const char *devices_file; /* * Configuration. diff --git a/lib/config/config_settings.h b/lib/config/config_settings.h index 2bb72ba71..316abc548 100644 --- a/lib/config/config_settings.h +++ b/lib/config/config_settings.h @@ -288,6 +288,9 @@ cfg_array(devices_preferred_names_CFG, "preferred_names", devices_CFG_SECTION, C "preferred_names = [ \"^/dev/mpath/\", \"^/dev/mapper/mpath\", \"^/dev/[hs]d\" ]\n" "#\n") +cfg(devices_devicesfile_CFG, "devicesfile", devices_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_DEVICES_FILE, vsn(2, 3, 10), NULL, 0, NULL, + "The file listing devices LVM should use.\n") + cfg_array(devices_filter_CFG, "filter", devices_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, "#Sa|.*|", vsn(1, 0, 0), NULL, 0, NULL, "Limit the block devices that are used by LVM commands.\n" "This is a list of regular expressions used to accept or reject block\n" diff --git a/lib/config/defaults.h b/lib/config/defaults.h index be4f5ff7f..894fd2d9e 100644 --- a/lib/config/defaults.h +++ b/lib/config/defaults.h @@ -320,4 +320,6 @@ #define DEFAULT_MD_COMPONENT_CHECKS "auto" +#define DEFAULT_DEVICES_FILE "/etc/lvm/lvm_devices.dat" + #endif /* _LVM_DEFAULTS_H */ diff --git a/lib/device/dev-cache.c b/lib/device/dev-cache.c index c3f7c49be..6b99bfc96 100644 --- a/lib/device/dev-cache.c +++ b/lib/device/dev-cache.c @@ -16,6 +16,7 @@ #include "base/memory/zalloc.h" #include "lib/misc/lib.h" #include "lib/device/dev-type.h" +#include "lib/device/device_id.h" #include "lib/datastruct/btree.h" #include "lib/config/config.h" #include "lib/commands/toolcontext.h" @@ -72,6 +73,7 @@ static void _dev_init(struct device *dev) dev->ext.src = DEV_EXT_NONE; dm_list_init(&dev->aliases); + dm_list_init(&dev->ids); } void dev_destroy_file(struct device *dev) @@ -351,7 +353,7 @@ static int _add_alias(struct device *dev, const char *path) return 1; } -static int _get_sysfs_value(const char *path, char *buf, size_t buf_size, int error_if_no_value) +int get_sysfs_value(const char *path, char *buf, size_t buf_size, int error_if_no_value) { FILE *fp; size_t len; @@ -392,7 +394,7 @@ static int _get_dm_uuid_from_sysfs(char *buf, size_t buf_size, int major, int mi return 0; } - return _get_sysfs_value(path, buf, buf_size, 0); + return get_sysfs_value(path, buf, buf_size, 0); } static struct dm_list *_get_or_add_list_by_index_key(struct dm_hash_table *idx, const char *key) @@ -473,7 +475,7 @@ static struct device *_get_device_for_sysfs_dev_name_using_devno(const char *dev return NULL; } - if (!_get_sysfs_value(path, buf, sizeof(buf), 1)) + if (!get_sysfs_value(path, buf, sizeof(buf), 1)) return_NULL; if (sscanf(buf, "%d:%d", &major, &minor) != 2) { @@ -971,7 +973,7 @@ static int _dev_cache_iterate_sysfs_for_index(const char *path) return r; } -int dev_cache_index_devs(void) +static int dev_cache_index_devs(void) { static int sysfs_has_dev_block = -1; char path[PATH_MAX]; @@ -1320,12 +1322,19 @@ int dev_cache_check_for_open_devices(void) int dev_cache_exit(void) { + struct device *dev; + struct dm_hash_node *n; int num_open = 0; if (_cache.names) 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); + dm_hash_iterate(n, _cache.names) { + dev = (struct device *) dm_hash_get_data(_cache.names, n); + free_dids(&dev->ids); + } + if (_cache.mem) dm_pool_destroy(_cache.mem); @@ -1657,3 +1666,38 @@ bool dev_cache_has_md_with_end_superblock(struct dev_types *dt) return false; } +void setup_devices(struct cmd_context *cmd) +{ + /* + * Read the list of device ids that lvm can use. + * Adds a struct dev_id to cmd->use_device_ids for each one. + * + * (Changing enable_device_ids=0 must ensure that nothing + * in the command thus far has been done on the basis of + * enable_device_ids=1.) + */ + if (!device_ids_read(cmd)) { + log_warn("WARNING: disabling use of devices_file, failed to read file."); + cmd->enable_device_ids = 0; + } + + /* + * 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. + * TODO: or from /proc/partitions? + * + * TODO: dev_cache_scan() optimization: start by looking only at + * devnames listed in the devices_file, and if the device_ids for + * those all match we won't need any others. + * Exceptions: the command wants a new device for pvcreate, or + * device_ids don't match the devnames. + */ + dev_cache_scan(); + + /* + * Match entries from cmd->use_device_ids with device structs in dev-cache. + */ + device_ids_match(cmd); +} + diff --git a/lib/device/dev-cache.h b/lib/device/dev-cache.h index 46c86c27a..2cecd6abb 100644 --- a/lib/device/dev-cache.h +++ b/lib/device/dev-cache.h @@ -34,7 +34,6 @@ struct dev_filter { const char *name; }; -int dev_cache_index_devs(void); struct dm_list *dev_cache_get_dev_list_for_vgid(const char *vgid); struct dm_list *dev_cache_get_dev_list_for_lvid(const char *lvid); @@ -74,4 +73,8 @@ void dev_cache_failed_path(struct device *dev, const char *path); bool dev_cache_has_md_with_end_superblock(struct dev_types *dt); +int get_sysfs_value(const char *path, char *buf, size_t buf_size, int error_if_no_value); + +void setup_devices(struct cmd_context *cmd); + #endif diff --git a/lib/device/device.h b/lib/device/device.h index bd3b35557..0ad590ef7 100644 --- a/lib/device/device.h +++ b/lib/device/device.h @@ -38,6 +38,7 @@ #define DEV_SCAN_FOUND_LABEL 0x00010000 /* label scan read dev and found label */ #define DEV_IS_MD_COMPONENT 0x00020000 /* device is an md component */ #define DEV_UDEV_INFO_MISSING 0x00040000 /* we have no udev info for this device */ +#define DEV_MATCHED_USE_ID 0x00080000 /* matched an entry from cmd->use_device_ids */ /* * Support for external device info. @@ -56,12 +57,36 @@ struct dev_ext { void *handle; }; +#define DEV_ID_TYPE_SYS_WWID 0x0001 +#define DEV_ID_TYPE_SYS_SERIAL 0x0002 +#define DEV_ID_TYPE_MPATH_UUID 0x0003 +#define DEV_ID_TYPE_DEVNAME 0x0004 +#define DEV_ID_TYPE_LOOP_FILE 0x0005 + +struct dev_id { + struct dm_list list; + struct device *dev; + uint16_t idtype; + char *idname; +}; + +struct use_id { + struct dm_list list; + struct device *dev; + uint16_t idtype; + char *idname; + char *devname; + char *pvid; +}; + /* * All devices in LVM will be represented by one of these. * pointer comparisons are valid. */ struct device { struct dm_list aliases; /* struct dm_str_list */ + struct dm_list ids; /* struct dev_id */ + struct dev_id *id; /* points to the ids entry being used for this dev */ dev_t dev; /* private */ diff --git a/lib/device/device_id.c b/lib/device/device_id.c new file mode 100644 index 000000000..989d80221 --- /dev/null +++ b/lib/device/device_id.c @@ -0,0 +1,830 @@ +/* + * Copyright (C) 2020 Red Hat, Inc. All rights reserved. + * + * This file is part of LVM2. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU Lesser General Public License v.2.1. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "base/memory/zalloc.h" +#include "lib/misc/lib.h" +#include "lib/commands/toolcontext.h" +#include "lib/device/device.h" +#include "lib/device/device_id.h" +#include "lib/device/dev-type.h" +#include "lib/device/device-types.h" + +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <time.h> +#include <sys/types.h> +#include <sys/file.h> +#include <sys/sysmacros.h> + +void free_uid(struct use_id *uid) +{ + if (uid->idname) + free(uid->idname); + if (uid->devname) + free(uid->devname); + if (uid->pvid) + free(uid->pvid); + free(uid); +} + +void free_uids(struct dm_list *uids) +{ + struct use_id *uid, *safe; + + dm_list_iterate_items_safe(uid, safe, uids) { + dm_list_del(&uid->list); + free_uid(uid); + } +} + +void free_did(struct dev_id *did) +{ + if (did->idname) + free(did->idname); + free(did); +} + +void free_dids(struct dm_list *dids) +{ + struct dev_id *did, *safe; + + dm_list_iterate_items_safe(did, safe, dids) { + dm_list_del(&did->list); + free_did(did); + } +} + +static int _read_sys_wwid(struct cmd_context *cmd, struct device *dev, char **idname) +{ + char path[PATH_MAX]; + char buf[PATH_MAX] = { 0 }; + + if (dm_snprintf(path, sizeof(path), "%sdev/block/%d:%d/device/wwid", + dm_sysfs_dir(), (int)MAJOR(dev->dev), (int)MINOR(dev->dev)) < 0) { + return 0; + } + + get_sysfs_value(path, buf, sizeof(buf), 0); + + if (buf[0]) { + if (!(*idname = strdup(buf))) + return 0; + } else { + *idname = NULL; + } + return 1; +} + +static int _read_sys_serial(struct cmd_context *cmd, struct device *dev, char **idname) +{ + char path[PATH_MAX]; + char buf[PATH_MAX] = { 0 }; + + if (dm_snprintf(path, sizeof(path), "%sdev/block/%d:%d/device/serial", + dm_sysfs_dir(), (int)MAJOR(dev->dev), (int)MINOR(dev->dev)) < 0) { + return 0; + } + + get_sysfs_value(path, buf, sizeof(buf), 0); + + if (buf[0]) { + if (!(*idname = strdup(buf))) + return 0; + } else { + *idname = NULL; + } + return 1; +} + +/* the dm uuid uses the wwid of the underlying dev */ + +static int _read_mpath_uuid(struct cmd_context *cmd, struct device *dev, char **idname) +{ + char path[PATH_MAX]; + char buf[PATH_MAX] = { 0 }; + + if (dm_snprintf(path, sizeof(path), "%sdev/block/%d:%d/dm/uuid", + dm_sysfs_dir(), (int)MAJOR(dev->dev), (int)MINOR(dev->dev)) < 0) { + return 0; + } + + get_sysfs_value(path, buf, sizeof(buf), 0); + + if (buf[0]) { + if (!(*idname = strdup(buf))) + return 0; + } else { + *idname = NULL; + } + return 1; +} + +static int _dev_has_mpath_uuid(struct cmd_context *cmd, struct device *dev, char **idname) +{ + if (MAJOR(dev->dev) != cmd->dev_types->device_mapper_major) + return 0; + + _read_mpath_uuid(cmd, dev, idname); + + if (*idname) + return 1; + return 0; +} + +static int _read_loop_file(struct cmd_context *cmd, struct device *dev, char **idname) +{ + char path[PATH_MAX]; + char buf[PATH_MAX] = { 0 }; + + if (dm_snprintf(path, sizeof(path), "%sdev/block/%d:%d/loop/backing_file", + dm_sysfs_dir(), (int)MAJOR(dev->dev), (int)MINOR(dev->dev)) < 0) { + return 0; + } + + get_sysfs_value(path, buf, sizeof(buf), 0); + + if (buf[0]) { + if (!(*idname = strdup(buf))) + return 0; + } else { + *idname = NULL; + } + return 1; +} + +/* + * TODO: should there be a list like lvm.conf + * device_id_types = [ "sys_wwid", "sys_serial" ] + * that controls which idtype's will be used? + * + * TODO: add a type for md devices, probably have it + * use the uuid from the md dev superblock. This would + * help in case of inconsistent md dev names, but would + * not help in case md components were all cloned. + */ +static char *_device_id_system_read(struct cmd_context *cmd, struct device *dev, uint16_t idtype) +{ + char *idname = NULL; + + if (idtype == DEV_ID_TYPE_SYS_WWID) + _read_sys_wwid(cmd, dev, &idname); + + else if (idtype == DEV_ID_TYPE_SYS_SERIAL) + _read_sys_serial(cmd, dev, &idname); + + else if (idtype == DEV_ID_TYPE_DEVNAME) + idname = strdup(dev_name(dev)); + + else if (idtype == DEV_ID_TYPE_MPATH_UUID) + _read_mpath_uuid(cmd, dev, &idname); + + else if (idtype == DEV_ID_TYPE_LOOP_FILE) + _read_loop_file(cmd, dev, &idname); + + return idname; +} + +const char *idtype_to_str(uint16_t idtype) +{ + if (idtype == DEV_ID_TYPE_SYS_WWID) + return "sys_wwid"; + + if (idtype == DEV_ID_TYPE_SYS_SERIAL) + return "sys_serial"; + + if (idtype == DEV_ID_TYPE_DEVNAME) + return "devname"; + + if (idtype == DEV_ID_TYPE_MPATH_UUID) + return "mpath_uuid"; + + if (idtype == DEV_ID_TYPE_LOOP_FILE) + return "loop_file"; + + return "unknown"; +} + +uint16_t idtype_from_str(const char *str) +{ + if (!strcmp(str, "sys_wwid")) + return DEV_ID_TYPE_SYS_WWID; + if (!strcmp(str, "sys_serial")) + return DEV_ID_TYPE_SYS_SERIAL; + if (!strcmp(str, "devname")) + return DEV_ID_TYPE_DEVNAME; + if (!strcmp(str, "mpath_uuid")) + return DEV_ID_TYPE_MPATH_UUID; + if (!strcmp(str, "loop_file")) + return DEV_ID_TYPE_LOOP_FILE; + return 0; +} + +const char *dev_idtype(struct device *dev) +{ + if (!dev->id) + return NULL; + + return idtype_to_str(dev->id->idtype); +} + +const char *dev_id(struct device *dev) +{ + if (dev->id) + return dev->id->idname; + return NULL; +} + +static void _copy_idline_str(char *src, char *dst, int len) +{ + char *s, *d = dst; + + memset(dst, 0, len); + + if (!(s = strchr(src, '='))) + return; + s++; + while ((*s == ' ') && (s < src + len)) + s++; + while ((*s != ' ') && (*s != '\0') && (*s != '\n') && (s < src + len)) { + *d = *s; + s++; + d++; + } +} + +int device_ids_read(struct cmd_context *cmd) +{ + char line[PATH_MAX]; + char buf[PATH_MAX]; + char *idtype, *idname, *devname, *pvid; + struct use_id *uid; + FILE *fp; + int fl_fd, fl_err = -1; + int ret = 1; + + /* + * TODO: allow the use_device_ids list to come from a + * command line option instead of devices_file? + * If so, add use_id structs to use_device_ids based + * on the reading the command line args here. + */ + + if (!cmd->enable_device_ids) + return 1; + + free_uids(&cmd->use_device_ids); + + if (cmd->nolocking) + goto use_file; + if ((fl_fd = open(cmd->devices_file, O_RDWR)) < 0) { + log_warn("Cannot open devices_file to flock."); + goto use_file; + } + if ((fl_err = flock(fl_fd, LOCK_SH))) { + log_warn("Cannot lock devices_file to read."); + close(fl_fd); + } + +use_file: + if (!(fp = fopen(cmd->devices_file, "r"))) { + log_warn("Cannot open devices_file to read."); + ret = 0; + goto out; + } + + while (fgets(line, sizeof(line), fp)) { + if (line[0] == '#') + continue; + + idtype = strstr(line, "IDTYPE"); + idname = strstr(line, "IDNAME"); + devname = strstr(line, "DEVNAME"); + pvid = strstr(line, "PVID"); + + /* These two are the minimum required. */ + if (!idtype || !idname) + continue; + + if (!(uid = zalloc(sizeof(struct use_id)))) + return 0; + + _copy_idline_str(idtype, buf, PATH_MAX); + if (buf[0]) + uid->idtype = idtype_from_str(buf); + + _copy_idline_str(idname, buf, PATH_MAX); + if (buf[0]) + uid->idname = strdup(buf); + + if (!uid->idtype || !uid->idname) { + log_print("Ignoring device: %s", line); + free_uid(uid); + continue; + } + + if (devname) { + _copy_idline_str(devname, buf, PATH_MAX); + if (buf[0] && (buf[0] != '.')) + uid->devname = strdup(buf); + } + + if (pvid) { + _copy_idline_str(pvid, buf, PATH_MAX); + if (buf[0] && (buf[0] != '.')) + uid->pvid = strdup(buf); + } + + dm_list_add(&cmd->use_device_ids, &uid->list); + } + + if (fclose(fp)) + stack; +out: + if (!cmd->nolocking && !fl_err) { + if (flock(fl_fd, LOCK_UN)) + stack; + if (close(fl_fd)) + stack; + } + return ret; +} + +int device_ids_write(struct cmd_context *cmd) +{ + FILE *fp; + time_t t; + struct use_id *uid; + const char *devname; + const char *pvid; + int fl_fd, fl_err = -1; + int ret = 1; + + if (!cmd->enable_device_ids) + return 1; + + if (cmd->nolocking) + goto use_file; + if ((fl_fd = open(cmd->devices_file, O_RDWR)) < 0) { + log_warn("Cannot open devices_file to flock."); + goto use_file; + } + if ((fl_err = flock(fl_fd, LOCK_EX))) { + log_warn("Cannot lock devices_file to write."); + close(fl_fd); + } + +use_file: + if (!(fp = fopen(cmd->devices_file, "w"))) { + log_warn("Cannot open devices_file to write."); + ret = 0; + goto out; + } + + t = time(NULL); + + log_print("updating devices_file"); + + fprintf(fp, "# LVM will use devices listed in this file.\n"); + fprintf(fp, "# IDTYPE and IDNAME fields are required, the DEVNAME path may change.\n"); + fprintf(fp, "# Created by LVM command %s pid %d at %s\n", cmd->name, getpid(), ctime(&t)); + + dm_list_iterate_items(uid, &cmd->use_device_ids) { + devname = uid->dev ? dev_name(uid->dev) : "."; + if (devname[0] != '/') + devname = "."; + + if (!uid->pvid || !uid->pvid[0] || (uid->pvid[0] == '.')) + pvid = "."; + else + pvid = uid->pvid; + + fprintf(fp, "IDTYPE=%s IDNAME=%s DEVNAME=%s PVID=%s\n", + idtype_to_str(uid->idtype) ?: ".", + uid->idname ?: ".", devname, pvid); + } + + if (fflush(fp)) + stack; + if (fclose(fp)) + stack; + +out: + if (!cmd->nolocking && !fl_err) { + if (flock(fl_fd, LOCK_UN)) + stack; + if (close(fl_fd)) + stack; + } + return ret; +} + +static struct use_id *_get_uid_for_dev(struct cmd_context *cmd, struct device *dev) +{ + struct use_id *uid; + + dm_list_iterate_items(uid, &cmd->use_device_ids) { + if (uid->dev == dev) + return uid; + } + return NULL; +} + +/* + * Add or update entry for this dev. + * IDTYPE=sys_wwid IDNAME=01234566 DEVNAME=/dev/sdb PVID=99393939 [OPTS=xx,yy,zz] + * + * add an entry to dev->ids and point dev->id to it. + * add or update entry in cmd->use_device_ids + */ +int device_id_pvcreate(struct cmd_context *cmd, struct device *dev, const char *pvid, + const char *idtype_arg, const char *id_arg) +{ + uint16_t idtype = 0; + char *idname; + struct use_id *uid; + struct dev_id *did; + int found_did = 0; + + if (!cmd->enable_device_ids) + return 1; + + /* TODO: handle dev with existing match */ + if (dev->id || (dev->flags & DEV_MATCHED_USE_ID)) + return 0; + + if (!(uid = _get_uid_for_dev(cmd, dev))) { + if (!(uid = zalloc(sizeof(struct use_id)))) + return_0; + } + + /* TODO: should deviceidtype command line option work for mpath/loop? */ + /* TODO: add more idtypes for special devs (e.g. MD) that don't have wwid */ + + if (_dev_has_mpath_uuid(cmd, dev, &idname)) { + idtype = DEV_ID_TYPE_MPATH_UUID; + goto id_done; + } + + if (MAJOR(dev->dev) == cmd->dev_types->loop_major) { + idtype = DEV_ID_TYPE_LOOP_FILE; + goto id_name; + } + + /* + * First use type specified by user option, then use a previous + * type, then use the default type. + * TODO: allow lvm.conf device_id_types to control idtypes used here? + */ + + if (idtype_arg) { + if (!(idtype = idtype_from_str(idtype_arg))) + log_warn("WARNING: ignoring unknown device_id type %s.", idtype_arg); + else { + if (id_arg) { + idname = id_arg; + goto id_done; + } + goto id_name; + } + } + + if (!idtype && uid->idtype) { + idtype = uid->idtype; + log_print("Create device_id for %s using previous idtype %u", dev_name(dev), idtype); + goto id_name; + } + + idtype = DEV_ID_TYPE_SYS_WWID; + +id_name: + if (!(idname = _device_id_system_read(cmd, dev, idtype))) { + if (idtype == DEV_ID_TYPE_SYS_WWID) { + idtype = DEV_ID_TYPE_SYS_SERIAL; + goto id_name; + } + idtype = DEV_ID_TYPE_DEVNAME; + goto id_name; + } + +id_done: + dm_list_iterate_items(did, &dev->ids) { + if (did->idtype == idtype) { + /* TODO: verify did->idname matches idname */ + found_did = 1; + break; + } + } + + if (!found_did) { + if (!(did = zalloc(sizeof(struct dev_id)))) + return_0; + did->idtype = idtype; + did->idname = idname; + did->dev = dev; + dm_list_add(&dev->ids, &did->list); + } + dev->id = did; + dev->flags |= DEV_MATCHED_USE_ID; + + if (uid->idtype && (uid->idtype != idtype)) { + log_print("Changing device_id_type from %s to %s for %s", + idtype_to_str(uid->idtype), idtype_to_str(idtype), dev_name(dev)); + } + if (uid->idtype && (uid->idtype == idtype) && + strcmp(uid->idname, idname)) { + log_print("Changing device_id from %s to %s for %s", + uid->idname, idname, dev_name(dev)); + } + + uid->idtype = did->idtype; + uid->idname = strdup(did->idname); + uid->devname = strdup(dev_name(dev)); + uid->dev = dev; + uid->pvid = strdup(pvid); + + if (!uid->idname || !uid->idname || !uid->pvid) { + free_uid(uid); + return 0; + } + + dm_list_add(&cmd->use_device_ids, &uid->list); + + return 1; +} + +/* + * Update entry for this dev. + * Set PVID=. + * update entry in cmd->use_device_ids + */ +void device_id_pvremove(struct cmd_context *cmd, struct device *dev) +{ + struct use_id *uid; + + if (!cmd->enable_device_ids) + return; + + if (!(uid = _get_uid_for_dev(cmd, dev))) { + log_warn("WARNING: use_device_ids does not include %s", dev_name(dev)); + return; + } + + if (uid->pvid) { + free(uid->pvid); + uid->pvid = NULL; + } +} + +/* + * check for dev->ids entry with uid->idtype, if found compare it, + * if not, system_read of this type and add entry to dev->ids, compare it. + * When a match is found, set up links among uid/did/dev. + */ + +int device_id_match(struct cmd_context *cmd, struct use_id *uid, struct device *dev) +{ + struct dev_id *did; + char *idname; + + dm_list_iterate_items(did, &dev->ids) { + if (did->idtype == uid->idtype) { + if (did->idname && !strcmp(did->idname, uid->idname)) { + uid->dev = dev; + dev->id = did; + dev->flags |= DEV_MATCHED_USE_ID; + return 1; + } else { + return_0; + } + } + } + + if (!(did = zalloc(sizeof(struct dev_id)))) + return_0; + + if (!(idname = _device_id_system_read(cmd, dev, uid->idtype))) { + /* Save a new did in dev->ids for this type to indicate no match + to avoid repeated system_read, since this called many times. + Setting idtype and NULL idname means no id of this type. */ + did->idtype = uid->idtype; + did->dev = dev; + dm_list_add(&dev->ids, &did->list); + return 0; + } + + /* Save this id for the device (so it can be quickly checked again), even + if it's not the idtype used to identify the dev in device_id_file. */ + did->idtype = uid->idtype; + did->idname = idname; + did->dev = dev; + dm_list_add(&dev->ids, &did->list); + + if (!strcmp(idname, uid->idname)) { + uid->dev = dev; + dev->id = did; + dev->flags |= DEV_MATCHED_USE_ID; + return 1; + } + + return 0; +} + +/* + +pvid is needed in the device_id_file, and device_id is needed in metadata +in order to handle cases where a device has no wwid/id or the wwid/id +changes. In these cases the correct set of devices can be found and the +devices file can be recreated. (A wwid/id in the metadata will also allow +remove the problem of duplicate pvs.) + +Three identifiers: wwid, dev, pvid +- dev can change, cannot be duplicated, cannot be unknown +- wwid can change, can be duplicated, can be unknown +- pvid can change, can be duplicated, cannot be unknown + +if dev changes +. if wwid exists, lvm corrects dev +. no wwid, new dev out of filter, missing PV, run cmd to find pvid +. no wwid, new dev in filter, lvm corrects filter + +if wwid changes +. lvm sees wrong pvid on wwid +. if the same device is in filter by another means, lvm finds the pvid and corrects wwid +. if the device is not in filter, missing PV, run cmd to find pvid + +if pvid changes +. lvm sees wrong pvid for wwid and corrects filter + +if wwid is duplicated +. lvm can still use the wwid for filtering, + but it won't help if pvid is also duplicated + +if pvid is duplicated +. lvm will use the wwid to choose the right one, + but if the wwid is also duplicated, lvm has to + fall back to picking a prefered devname + +if wwid is unknown +. if dev changes and new dev in filter, lvm fixes devname +. if dev changes and new dev out of filter, pv missing, run cmd to find pvid +. if pvid is duplicated and dev changes, lvm may pick the wrong device + +*/ + +/* + * For each entry on cmd->use_device_ids, find a struct device from dev-cache. + * This must not open or read devices. filters are applied after this, + * and they may open devs in the first filter stage. The second filtering + * stage, done as a part of label_scan, is finally allowed to read devices. + * + * When a device id of a particular type is read for a dev, a did for that + * type is saved in dev->ids in case it needs to be checked again. + * + * When a particular dev_id for a dev (in dev-cache) is matched to a use_dev + * (from use_device_ids), then: + * . uid->dev = dev; + * . dev->id = did; + * . dev->flags |= DEV_MATCHED_USE_ID; + */ + +void device_ids_match(struct cmd_context *cmd) +{ + struct dev_iter *iter; + struct use_id *uid; + struct device *dev; + int update_file = 0; + + if (!cmd->enable_device_ids) + return; + + dm_list_iterate_items(uid, &cmd->use_device_ids) { + /* already matched */ + if (uid->dev && (uid->dev->flags & DEV_MATCHED_USE_ID)) + continue; + + /* + * uid->devname may be incorrect, but it's often correct, so it's the + * most efficient place to begin. + */ + if (uid->devname && + (dev = dev_cache_get(cmd, uid->devname, NULL))) { + /* On success, device_id_match() links the uid, dev, and did. */ + if (device_id_match(cmd, uid, dev)) + continue; + else { + /* uid->devname now belongs to a different device */ + log_print("Device with name %s has changed.", uid->devname); + } + } + + /* At a minimum some devname needs to be added or updated. + device_ids_validate may find other reasons to update the + file. Are there commands where device_ids_validate would + not be run, so we should update the file here? */ + + /* + * Iterate through all devs and try to match uid. + * + * A filter is not used when iterating since this is not selecting + * devs to use. The next dev iteration in label scan does use filters. + * The device_id matches created here are used by filter-deviceid later. + * (Might we apply a couple simple filters here, though, to avoid + * doing some pointless match attempts?) + * + * If a match is made here it means the uid->devname is wrong so the + * device_id file should be udpated with a new devname. + */ + if (!(iter = dev_iter_create(NULL, 0))) + continue; + while ((dev = dev_iter_get(cmd, iter))) { + if (dev->flags & DEV_MATCHED_USE_ID) + continue; + if (device_id_match(cmd, uid, dev)) + break; + } + dev_iter_destroy(iter); + } + + /* + * Look for entries in devices_file for which we found no device. + */ + dm_list_iterate_items(uid, &cmd->use_device_ids) { + if (uid->dev && (uid->dev->flags & DEV_MATCHED_USE_ID)) + continue; + + if (uid->dev && !(uid->dev->flags & DEV_MATCHED_USE_ID)) { + /* is this possible? */ + } + + log_print("Device with previous name %s not found with %s %s PVID %s.", + uid->devname, idtype_to_str(uid->idtype), uid->idname, uid->pvid); + } +} + +/* + * This is called after label_scan() to compare what was found on disks + * vs what's in the devices_file. The devices_file could be outdated + * and need correcting; the authoritative data is what's on disk. + * Now that we have read the device labels in label_scan and have the PVID's + * we can check the pvid's of use_device_ids entries from the device_id_file. + */ +void device_ids_validate(struct cmd_context *cmd) +{ + struct use_id *uid; + int update_file = 0; + + if (!cmd->enable_device_ids) + return; + + dm_list_iterate_items(uid, &cmd->use_device_ids) { + if (!uid->dev) + continue; + + if (uid->dev->pvid[0] && (strcmp(uid->dev->pvid, uid->pvid))) { + log_print("Device %s has updated PVID %s from devices_file (%s)", + dev_name(uid->dev), uid->dev->pvid, uid->pvid); + if (uid->pvid) + free(uid->pvid); + uid->pvid = strdup(uid->dev->pvid); + update_file = 1; + } + + if (!uid->devname || strcmp(dev_name(uid->dev), uid->devname)) { + log_print("Device %s has updated devname from devices_file (%s).", + dev_name(uid->dev), uid->devname ?: "."); + if (uid->devname) + free(uid->devname); + uid->devname = strdup(dev_name(uid->dev)); + update_file = 1; + } + } + + if (update_file) + device_ids_write(cmd); +} + +int devices_file_valid(struct cmd_context *cmd) +{ + struct stat buf; + + if (!cmd->devices_file || !strlen(cmd->devices_file)) + return 0; + + if (stat(cmd->devices_file, &buf)) + return 0; + + return 1; +} + diff --git a/lib/device/device_id.h b/lib/device/device_id.h new file mode 100644 index 000000000..8151474ec --- /dev/null +++ b/lib/device/device_id.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. + * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved. + * + * This file is part of LVM2. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU Lesser General Public License v.2.1. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _LVM_DEVICE_ID_H +#define _LVM_DEVICE_ID_H + +void free_uid(struct use_id *uid); +void free_uids(struct dm_list *list); +void free_did(struct dev_id *did); +void free_dids(struct dm_list *list); +const char *idtype_to_str(uint16_t idtype); +uint16_t idtype_from_str(const char *str); +const char *dev_idtype(struct device *dev); +const char *dev_id(struct device *dev); +int device_ids_read(struct cmd_context *cmd); +int device_ids_write(struct cmd_context *cmd); +int device_id_pvcreate(struct cmd_context *cmd, struct device *dev, const char *pvid, + const char *idtype_arg, const char *id_arg); +void device_id_pvremove(struct cmd_context *cmd, struct device *dev); +int device_id_match(struct cmd_context *cmd, struct use_id *uid, struct device *dev); +void device_ids_match(struct cmd_context *cmd); +void device_ids_validate(struct cmd_context *cmd); + +int devices_file_valid(struct cmd_context *cmd); + +#endif diff --git a/lib/filters/filter-deviceid.c b/lib/filters/filter-deviceid.c new file mode 100644 index 000000000..15b40ea66 --- /dev/null +++ b/lib/filters/filter-deviceid.c @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. + * Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved. + * + * This file is part of LVM2. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU Lesser General Public License v.2.1. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "base/memory/zalloc.h" +#include "lib/misc/lib.h" +#include "lib/filters/filter.h" +#include "lib/commands/toolcontext.h" + +static int _passes_deviceid_filter(struct cmd_context *cmd, struct dev_filter *f, struct device *dev, const char *use_filter_name) +{ + if (!cmd->enable_device_ids) + return 1; + + if (dev->flags & DEV_MATCHED_USE_ID) + return 1; + return 0; +} + +static void _destroy_deviceid_filter(struct dev_filter *f) +{ + if (f->use_count) + log_error(INTERNAL_ERROR "Destroying deviceid filter while in use %u times.", f->use_count); + + free(f); +} + +struct dev_filter *deviceid_filter_create(struct cmd_context *cmd) +{ + struct dev_filter *f; + + if (!(f = zalloc(sizeof(struct dev_filter)))) { + log_error("deviceid filter allocation failed"); + return NULL; + } + + f->passes_filter = _passes_deviceid_filter; + f->destroy = _destroy_deviceid_filter; + f->use_count = 0; + f->name = "deviceid"; + + log_debug_devs("deviceid filter initialised."); + + return f; +} diff --git a/lib/filters/filter-regex.c b/lib/filters/filter-regex.c index e439b36b5..33edc4138 100644 --- a/lib/filters/filter-regex.c +++ b/lib/filters/filter-regex.c @@ -15,6 +15,7 @@ #include "lib/misc/lib.h" #include "lib/filters/filter.h" +#include "lib/commands/toolcontext.h" struct rfilter { struct dm_pool *mem; @@ -151,6 +152,11 @@ static int _accept_p(struct cmd_context *cmd, struct dev_filter *f, struct devic struct rfilter *rf = (struct rfilter *) f->private; struct dm_str_list *sl; + if (cmd->enable_device_ids) { + /* TODO: print a notice if the filter is set to something and we ignore it here. */ + return 1; + } + dm_list_iterate_items(sl, &dev->aliases) { m = dm_regex_match(rf->engine, sl->str); diff --git a/lib/filters/filter.h b/lib/filters/filter.h index 7333cfe3c..af329618f 100644 --- a/lib/filters/filter.h +++ b/lib/filters/filter.h @@ -30,6 +30,7 @@ struct dev_filter *partitioned_filter_create(struct dev_types *dt); struct dev_filter *persistent_filter_create(struct dev_types *dt, struct dev_filter *f); struct dev_filter *sysfs_filter_create(void); struct dev_filter *signature_filter_create(struct dev_types *dt); +struct dev_filter *deviceid_filter_create(struct cmd_context *cmd); struct dev_filter *internal_filter_create(void); int internal_filter_allow(struct dm_pool *mem, struct device *dev); diff --git a/lib/format_text/export.c b/lib/format_text/export.c index b5e987e01..1f620c603 100644 --- a/lib/format_text/export.c +++ b/lib/format_text/export.c @@ -23,6 +23,7 @@ #include "lib/metadata/segtype.h" #include "lib/format_text/text_export.h" #include "lib/commands/toolcontext.h" +#include "lib/device/device_id.h" #include "libdaemon/client/config-util.h" #include <stdarg.h> @@ -555,6 +556,11 @@ static int _print_pvs(struct formatter *f, struct volume_group *vg) dm_escape_double_quotes(buffer, pv_dev_name(pv))); outnl(f); + if (dev_idtype(pv->dev) && dev_id(pv->dev)) { + outf(f, "device_id_type = \"%s\"", dev_idtype(pv->dev)); + outf(f, "device_id = \"%s\"", dev_id(pv->dev)); + } + if (!_print_flag_config(f, pv->status, PV_FLAGS)) return_0; diff --git a/lib/format_text/import_vsn1.c b/lib/format_text/import_vsn1.c index 2bdbfeea9..ee56502e1 100644 --- a/lib/format_text/import_vsn1.c +++ b/lib/format_text/import_vsn1.c @@ -188,7 +188,7 @@ static int _read_pv(struct cmd_context *cmd, struct physical_volume *pv; struct pv_list *pvl; const struct dm_config_value *cv; - const char *device_hint; + const char *str; uint64_t size, ba_start; if (!(pvl = dm_pool_zalloc(mem, sizeof(*pvl))) || @@ -233,11 +233,21 @@ static int _read_pv(struct cmd_context *cmd, return 0; } - if (dm_config_get_str(pvn, "device", &device_hint)) { - if (!(pv->device_hint = dm_pool_strdup(mem, device_hint))) + if (dm_config_get_str(pvn, "device", &str)) { + if (!(pv->device_hint = dm_pool_strdup(mem, str))) log_error("Failed to allocate memory for device hint in read_pv."); } + if (dm_config_get_str(pvn, "device_id", &str)) { + if (!(pv->device_id = dm_pool_strdup(mem, str))) + log_error("Failed to allocate memory for device_id in read_pv."); + } + + if (dm_config_get_str(pvn, "device_id_type", &str)) { + if (!(pv->device_id_type = dm_pool_strdup(mem, str))) + log_error("Failed to allocate memory for device_id_type in read_pv."); + } + if (!_read_uint64(pvn, "pe_start", &pv->pe_start)) { log_error("Couldn't read extent start value (pe_start) " "for physical volume."); @@ -306,7 +316,7 @@ static int _read_pvsummary(struct cmd_context *cmd, { struct physical_volume *pv; struct pv_list *pvl; - const char *device_hint; + const char *str; if (!(pvl = dm_pool_zalloc(mem, sizeof(*pvl))) || !(pvl->pv = dm_pool_zalloc(mem, sizeof(*pvl->pv)))) @@ -326,9 +336,19 @@ static int _read_pvsummary(struct cmd_context *cmd, !_read_uint64(pvn, "dev_size", &pv->size)) log_warn("Couldn't read dev size for physical volume."); - if (dm_config_get_str(pvn, "device", &device_hint)) { - if (!(pv->device_hint = dm_pool_strdup(mem, device_hint))) - log_error("Failed to allocate memory for device hint in read_pv."); + if (dm_config_get_str(pvn, "device", &str)) { + if (!(pv->device_hint = dm_pool_strdup(mem, str))) + log_error("Failed to allocate memory for device hint in read_pv_sum."); + } + + if (dm_config_get_str(pvn, "device_id", &str)) { + if (!(pv->device_id = dm_pool_strdup(mem, str))) + log_error("Failed to allocate memory for device_id in read_pv_sum."); + } + + if (dm_config_get_str(pvn, "device_id_type", &str)) { + if (!(pv->device_id_type = dm_pool_strdup(mem, str))) + log_error("Failed to allocate memory for device_id_type in read_pv_sum."); } dm_list_add(&vgsummary->pvsummaries, &pvl->list); diff --git a/lib/label/label.c b/lib/label/label.c index ed6e0171c..1b15b7a8e 100644 --- a/lib/label/label.c +++ b/lib/label/label.c @@ -25,6 +25,7 @@ #include "lib/label/hints.h" #include "lib/metadata/metadata.h" #include "lib/format_text/layout.h" +#include "lib/device/device_id.h" #include <sys/stat.h> #include <fcntl.h> @@ -780,16 +781,6 @@ static int _scan_list(struct cmd_context *cmd, struct dev_filter *f, } } - /* - * This will search the system's /dev for new path names and - * could help us reopen the device if it finds a new preferred - * path name for this dev's major:minor. It does that by - * inserting a new preferred path name on dev->aliases. open - * uses the first name from that list. - */ - log_debug_devs("Scanning refreshing device paths."); - dev_cache_scan(); - /* Put devs that failed to open back on the original list to retry. */ dm_list_splice(devs, &reopen_devs); goto scan_more; @@ -923,7 +914,17 @@ int label_scan_for_pvid(struct cmd_context *cmd, char *pvid, struct device **dev dm_list_init(&devs); - dev_cache_scan(); + /* + * Creates a list of available devices, does not open or read any, + * and does not filter them. + */ + setup_devices(cmd); + + /* + * Iterating over all available devices with cmd->filter filters + * devices; those returned from dev_iter_get are the devs that + * pass filters, and are those we can use. + */ if (!(iter = dev_iter_create(cmd->filter, 0))) { log_error("Scanning failed to get devices."); @@ -1006,12 +1007,14 @@ int label_scan(struct cmd_context *cmd) dm_list_init(&hints_list); /* - * dev_cache_scan() creates a list of devices on the system - * (saved in in dev-cache) which we can iterate through to - * search for LVM devs. The dev cache list either comes from - * looking at dev nodes under /dev, or from udev. + * Creates a list of available devices, does not open or read any, + * and does not filter them. The list of all available devices + * is kept in "dev-cache", and comes from /dev entries or libudev. + * The list of devs found here needs to be filtered to get the + * list of devs we can use. The dev_iter calls using cmd->filter + * are what filters the devs. */ - dev_cache_scan(); + setup_devices(cmd); /* * If we know that there will be md components with an end @@ -1213,6 +1216,8 @@ int label_scan(struct cmd_context *cmd) if (create_hints) write_hint_file(cmd, create_hints); + device_ids_validate(cmd); + return 1; } diff --git a/lib/metadata/pv.h b/lib/metadata/pv.h index efa13e04b..ec1b21d54 100644 --- a/lib/metadata/pv.h +++ b/lib/metadata/pv.h @@ -27,6 +27,8 @@ struct physical_volume { struct id old_id; /* Set during pvchange -u. */ struct device *dev; const char *device_hint; /* primary name last time metadata was written */ + const char *device_id; + const char *device_id_type; const struct format_type *fmt; struct format_instance *fid; diff --git a/tools/args.h b/tools/args.h index 5fae21abb..66b916667 100644 --- a/tools/args.h +++ b/tools/args.h @@ -199,6 +199,15 @@ arg(detachprofile_ARG, '\0', "detachprofile", 0, 0, 0, "Detaches a metadata profile from a VG or LV.\n" "See \\fBlvm.conf\\fP(5) for more information about profiles.\n") +arg(deviceid_ARG, '\0', "deviceid", string_VAL, 0, 0, + "A device ID with a format determined by --deviceidtype.") + +arg(deviceidtype_ARG, '\0', "deviceidtype", string_VAL, 0, 0, + "A device ID type: sys_wwid, sys_serial, mpath_uuid.") + +arg(devicesfile_ARG, '\0', "devicesfile", string_VAL, 0, 0, + "The file listing device IDs that LVM should use.") + arg(discards_ARG, '\0', "discards", discards_VAL, 0, 0, "Specifies how the device-mapper thin pool layer in the kernel should\n" "handle discards.\n" diff --git a/tools/command-lines.in b/tools/command-lines.in index ed3d0413a..dce0ffec5 100644 --- a/tools/command-lines.in +++ b/tools/command-lines.in @@ -188,7 +188,7 @@ # OO_ALL: --commandprofile String, --config String, --debug, --driverloaded Bool, --help, --nolocking, --lockopt String, --longhelp, --profile String, --quiet, ---verbose, --version, --yes, --test +--verbose, --version, --yes, --test, --devicesfile String # # options for pvs, lvs, vgs, fullreport @@ -1479,7 +1479,8 @@ OO: --dataalignment SizeKB, --dataalignmentoffset SizeKB, --bootloaderareasize S --force, --labelsector Number, --metadatatype MetadataType, --pvmetadatacopies MetadataCopiesPV, --metadatasize SizeMB, --metadataignore Bool, --norestorefile, --setphysicalvolumesize SizeMB, ---reportformat ReportFmt, --restorefile String, --uuidstr String, --zero Bool +--reportformat ReportFmt, --restorefile String, --uuidstr String, --zero Bool, +--deviceidtype String, --deviceid String ID: pvcreate_general RULE: --norestorefile not --restorefile RULE: --bootloaderareasize not --restorefile diff --git a/tools/lvmcmdline.c b/tools/lvmcmdline.c index d87a8f053..eca8b47d9 100644 --- a/tools/lvmcmdline.c +++ b/tools/lvmcmdline.c @@ -17,6 +17,7 @@ #include "lvm2cmdline.h" #include "lib/label/label.h" +#include "lib/device/device_id.h" #include "lvm-version.h" #include "lib/locking/lvmlockd.h" @@ -2477,6 +2478,11 @@ static int _get_current_settings(struct cmd_context *cmd) cmd->record_historical_lvs = find_config_tree_bool(cmd, metadata_record_lvs_history_CFG, NULL) ? (arg_is_set(cmd, nohistory_ARG) ? 0 : 1) : 0; + if (arg_is_set(cmd, devicesfile_ARG)) + cmd->devices_file = arg_str_value(cmd, devicesfile_ARG, ""); + if (devices_file_valid(cmd)) + cmd->enable_device_ids = 1; + /* * This is set to zero by process_each which wants to print errors * itself rather than having them printed in vg_read. diff --git a/tools/pvscan.c b/tools/pvscan.c index 4d811da55..c88b09b42 100644 --- a/tools/pvscan.c +++ b/tools/pvscan.c @@ -820,6 +820,11 @@ static void _online_pvscan_all_devs(struct cmd_context *cmd, struct device *dev; const char *pvid_without_metadata; + /* + * TODO: label_scan() calls setup_devices(), but pvscan --cache is a + * special case in which setup_devices() has already been called. + * So, we could improve things by suppressing the second setup_devices(). + */ lvmcache_label_scan(cmd); if (!(iter = dev_iter_create(cmd->filter, 1))) { @@ -1254,8 +1259,8 @@ int pvscan_cache_cmd(struct cmd_context *cmd, int argc, char **argv) _online_dir_setup(); - /* Creates a list of dev names from /dev, sysfs, etc; does not read any. */ - dev_cache_scan(); + /* Creates a list of available devices, does not open or read any. */ + setup_devices(cmd); if (cmd->md_component_detection && !cmd->use_full_md_check && !strcmp(cmd->md_component_checks, "auto") && diff --git a/tools/toollib.c b/tools/toollib.c index 9640f003e..5ee264f35 100644 --- a/tools/toollib.c +++ b/tools/toollib.c @@ -16,6 +16,7 @@ #include "tools.h" #include "lib/format_text/format-text.h" #include "lib/label/hints.h" +#include "lib/device/device_id.h" #include <sys/stat.h> #include <signal.h> @@ -5102,7 +5103,8 @@ int pvcreate_each_device(struct cmd_context *cmd, * Translate arg names into struct device's. */ dm_list_iterate_items_safe(pd, pd2, &pp->arg_devices) { - pd->dev = dev_cache_get(cmd, pd->name, cmd->filter); + pd->dev = dev_cache_get(cmd, pd->name, + cmd->enable_device_ids ? NULL : cmd->filter); if (!pd->dev) { log_print("No device found for %s", pd->name); dm_list_del(&pd->list); @@ -5411,6 +5413,10 @@ do_command: log_debug("Using existing orphan PV %s.", pv_dev_name(vgpvl->pv)); pvl->pv = vgpvl->pv; dm_list_add(&pp->pvs, &pvl->list); + + device_id_pvcreate(cmd, pd->dev, (const char *)&pvl->pv->id.uuid, + arg_str_value(cmd, deviceidtype_ARG, NULL), + arg_str_value(cmd, deviceid_ARG, NULL)); } else { log_error("Failed to find PV %s", pd->name); dm_list_move(&pp->arg_fail, &pd->list); @@ -5449,6 +5455,10 @@ do_command: continue; } + device_id_pvcreate(cmd, pd->dev, (const char *)&pv->id.uuid, + arg_str_value(cmd, deviceidtype_ARG, NULL), + arg_str_value(cmd, deviceid_ARG, NULL)); + log_verbose("Set up physical volume for \"%s\" with %" PRIu64 " available sectors.", pv_name, pv_size(pv)); @@ -5494,6 +5504,8 @@ do_command: continue; } + device_id_pvremove(cmd, pd->dev); + log_print_unless_silent("Labels on physical volume \"%s\" successfully wiped.", pd->name); } @@ -5510,10 +5522,15 @@ do_command: lvmcache_del_dev_from_duplicates(pd->dev); + device_id_pvremove(cmd, pd->dev); + log_print_unless_silent("Labels on physical volume \"%s\" successfully wiped.", pd->name); } + /* TODO: when vgcreate uses only existing PVs this doesn't change and can be skipped */ + device_ids_write(cmd); + /* * Don't keep devs open excl in bcache because the excl will prevent * using that dev elsewhere. |