summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Teigland <teigland@redhat.com>2020-06-23 13:25:41 -0500
committerDavid Teigland <teigland@redhat.com>2020-06-23 17:00:35 -0500
commit024ce344297b1e496512c1dd1e50c646a0a74790 (patch)
treead997f12a3479c640fecd717c0e38531ea7391c0
parentb0787033023fdc213f3fdd3efc488e70f44e1fcd (diff)
downloadlvm2-dev-dct-deviceid-1.tar.gz
lvm_devices file with device_id and filteringdev-dct-deviceid-1
-rw-r--r--lib/Makefile.in2
-rw-r--r--lib/commands/toolcontext.c25
-rw-r--r--lib/commands/toolcontext.h3
-rw-r--r--lib/config/config_settings.h3
-rw-r--r--lib/config/defaults.h2
-rw-r--r--lib/device/dev-cache.c52
-rw-r--r--lib/device/dev-cache.h5
-rw-r--r--lib/device/device.h25
-rw-r--r--lib/device/device_id.c830
-rw-r--r--lib/device/device_id.h38
-rw-r--r--lib/filters/filter-deviceid.c56
-rw-r--r--lib/filters/filter-regex.c6
-rw-r--r--lib/filters/filter.h1
-rw-r--r--lib/format_text/export.c6
-rw-r--r--lib/format_text/import_vsn1.c34
-rw-r--r--lib/label/label.c37
-rw-r--r--lib/metadata/pv.h2
-rw-r--r--tools/args.h9
-rw-r--r--tools/command-lines.in5
-rw-r--r--tools/lvmcmdline.c6
-rw-r--r--tools/pvscan.c9
-rw-r--r--tools/toollib.c19
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.