static int _using_devices_file;
static int _devices_file_locked;
static char _devices_lockfile[PATH_MAX];
-static char _devices_file_systemid[PATH_MAX];
static char _devices_file_version[VERSION_LINE_MAX];
static const char *_searched_file = DEFAULT_RUN_DIR "/searched_devnames";
return 0;
}
+/*
+ * Check if the device_id saved in the VG metadata matches the actual device_id
+ * on the device used for the PV.
+ */
+int pv_device_id_is_stale(const struct physical_volume *pv)
+{
+ struct dev_use *du;
+
+ if (!pv->vg || !pv->vg->cmd)
+ return 0;
+ if (!pv->device_id || !pv->device_id_type)
+ return 0;
+ if (!(du = get_du_for_dev(pv->vg->cmd, pv->dev)))
+ return 0;
+ if (!du->idname)
+ return 0;
+
+ if (du->idtype != idtype_from_str(pv->device_id_type))
+ return 1;
+ if (strcmp(du->idname, pv->device_id))
+ return 1;
+ return 0;
+}
+
/*
* How the devices file and device IDs are used by an ordinary command:
*
return NULL;
}
+static int device_id_system_read_preferred(struct cmd_context *cmd, struct device *dev,
+ uint16_t *new_idtype, const char **new_idname)
+{
+ const char *idname = NULL;
+ uint16_t idtype;
+
+ if (MAJOR(dev->dev) == cmd->dev_types->device_mapper_major) {
+ if (dev_has_mpath_uuid(cmd, dev, &idname)) {
+ idtype = DEV_ID_TYPE_MPATH_UUID;
+ goto id_done;
+ }
+
+ if (_dev_has_crypt_uuid(cmd, dev, &idname)) {
+ idtype = DEV_ID_TYPE_CRYPT_UUID;
+ goto id_done;
+ }
+
+ if (_dev_has_lvmlv_uuid(cmd, dev, &idname)) {
+ idtype = DEV_ID_TYPE_LVMLV_UUID;
+ goto id_done;
+ }
+ }
+
+ /* TODO: kpartx partitions on loop devs. */
+ if (MAJOR(dev->dev) == cmd->dev_types->loop_major) {
+ idtype = DEV_ID_TYPE_LOOP_FILE;
+ if ((idname = device_id_system_read(cmd, dev, idtype)))
+ goto id_done;
+ goto id_last;
+ }
+
+ if (MAJOR(dev->dev) == cmd->dev_types->md_major) {
+ idtype = DEV_ID_TYPE_MD_UUID;
+ if ((idname = device_id_system_read(cmd, dev, idtype)))
+ goto id_done;
+ goto id_last;
+ }
+
+ if (MAJOR(dev->dev) == cmd->dev_types->drbd_major) {
+ /* TODO */
+ log_warn("Missing support for DRBD idtype");
+ goto id_last;
+ }
+
+ idtype = DEV_ID_TYPE_SYS_WWID;
+ if ((idname = device_id_system_read(cmd, dev, idtype)))
+ goto id_done;
+
+ idtype = DEV_ID_TYPE_WWID_NAA;
+ if ((idname = device_id_system_read(cmd, dev, idtype)))
+ goto id_done;
+
+ idtype = DEV_ID_TYPE_WWID_EUI;
+ if ((idname = device_id_system_read(cmd, dev, idtype)))
+ goto id_done;
+
+ idtype = DEV_ID_TYPE_WWID_T10;
+ if ((idname = device_id_system_read(cmd, dev, idtype)))
+ goto id_done;
+
+ idtype = DEV_ID_TYPE_SYS_SERIAL;
+ if ((idname = device_id_system_read(cmd, dev, idtype)))
+ goto id_done;
+id_last:
+ idtype = DEV_ID_TYPE_DEVNAME;
+ if ((idname = device_id_system_read(cmd, dev, idtype)))
+ goto id_done;
+
+ return 0;
+
+id_done:
+ *new_idtype = idtype;
+ *new_idname = idname;
+ return 1;
+}
+
/*
* Check if this dev would use a stable idtype or if it
* would use DEV_ID_TYPE_DEVNAME.
{
char line[PATH_MAX];
char buf[PATH_MAX];
+ char check_id[PATH_MAX];
char *idtype, *idname, *devname, *pvid, *part;
struct dev_use *du;
FILE *fp;
int line_error;
+ int product_uuid_found = 0;
+ int hostname_found = 0;
int ret = 1;
if (!cmd->enable_devices_file)
if (line[0] == '#')
continue;
- if (!strncmp(line, "SYSTEMID", 8)) {
- _copy_idline_str(line, _devices_file_systemid, sizeof(_devices_file_systemid));
- log_debug("read devices file systemid %s", _devices_file_systemid);
- if ((!cmd->system_id && _devices_file_systemid[0]) ||
- (cmd->system_id && strcmp(cmd->system_id, _devices_file_systemid))) {
- log_warn("WARNING: devices file has unmatching system id %s vs local %s.",
- _devices_file_systemid[0] ? _devices_file_systemid : "none", cmd->system_id ?: "none");
+ /* Old version wrote this but it's not used. */
+ if (!strncmp(line, "SYSTEMID", 8))
+ continue;
+
+ if (!strncmp(line, "HOSTNAME", 8)) {
+ if (!cmd->device_ids_check_hostname)
+ continue;
+ hostname_found = 1;
+ _copy_idline_str(line, check_id, sizeof(check_id));
+ log_debug("read devices file hostname %s", check_id);
+ if (cmd->hostname && strcmp(cmd->hostname, check_id)) {
+ log_debug("Devices file hostname %s vs local %s.",
+ check_id[0] ? check_id : "none", cmd->hostname ?: "none");
+ cmd->device_ids_refresh_trigger = 1;
+ }
+ continue;
+ }
+
+ if (!strncmp(line, "PRODUCT_UUID", 12)) {
+ if (!cmd->device_ids_check_product_uuid)
+ continue;
+ product_uuid_found = 1;
+ _copy_idline_str(line, check_id, sizeof(check_id));
+ log_debug("read devices file product_uuid %s", check_id);
+ if ((!cmd->product_uuid && check_id[0]) ||
+ (cmd->product_uuid && strcmp(cmd->product_uuid, check_id))) {
+ log_debug("Devices file product_uuid %s vs local %s.",
+ check_id[0] ? check_id : "none", cmd->product_uuid ?: "none");
+ cmd->device_ids_refresh_trigger = 1;
}
continue;
}
+
if (!strncmp(line, "VERSION", 7)) {
_copy_idline_str(line, _devices_file_version, sizeof(_devices_file_version));
log_debug("read devices file version %s", _devices_file_version);
}
if (fclose(fp))
stack;
+
+ if (!product_uuid_found && !hostname_found &&
+ (cmd->device_ids_check_product_uuid || cmd->device_ids_check_hostname)) {
+ cmd->device_ids_refresh_trigger = 1;
+ log_debug("Devices file refresh due to no product_uuid or hostname.");
+ }
return ret;
}
(!strncmp(cmd->name, "pvcreate", 8) || !strncmp(cmd->name, "vgcreate", 8))) {
/* If any PVs were seen during scan then don't create a new devices file. */
if (lvmcache_vg_info_count()) {
- log_warn("Not creating system devices file due to existing VGs.");
+ log_print_unless_silent("Not creating system devices file due to existing VGs.");
free_dus(&cmd->use_devices);
return 1;
}
- log_warn("Creating devices file %s", cmd->devices_file_path);
+ log_print_unless_silent("Creating devices file %s", cmd->devices_file_path);
cmd->enable_devices_file = 1;
}
fprintf(fp, "# LVM uses devices listed in this file.\n");
fprintf(fp, "# Created by LVM command %s pid %d at %s", cmd->name, getpid(), ctime(&t));
- /*
- * It's useful to ensure that this devices file is associated to a
- * single system because this file can be used to control access to
- * shared devices. If this file is copied/cloned to another system,
- * that new system should not automatically gain access to the devices
- * that the original system is using.
- */
- if (cmd->system_id)
- fprintf(fp, "SYSTEMID=%s\n", cmd->system_id);
+ if (cmd->product_uuid && cmd->device_ids_check_product_uuid)
+ fprintf(fp, "PRODUCT_UUID=%s\n", cmd->product_uuid);
+ if (cmd->hostname && cmd->device_ids_check_hostname)
+ fprintf(fp, "HOSTNAME=%s\n", cmd->hostname);
if (dm_snprintf(version_buf, VERSION_LINE_MAX, "VERSION=%u.%u.%u", DEVICES_FILE_MAJOR, DEVICES_FILE_MINOR, df_counter+1) < 0)
stack;
}
}
- if (MAJOR(dev->dev) == cmd->dev_types->device_mapper_major) {
- if (dev_has_mpath_uuid(cmd, dev, &idname)) {
- idtype = DEV_ID_TYPE_MPATH_UUID;
- goto id_done;
- }
-
- if (_dev_has_crypt_uuid(cmd, dev, &idname)) {
- idtype = DEV_ID_TYPE_CRYPT_UUID;
- goto id_done;
- }
-
- if (_dev_has_lvmlv_uuid(cmd, dev, &idname)) {
- idtype = DEV_ID_TYPE_LVMLV_UUID;
- goto id_done;
- }
- }
-
- /* TODO: kpartx partitions on loop devs. */
- if (MAJOR(dev->dev) == cmd->dev_types->loop_major) {
- idtype = DEV_ID_TYPE_LOOP_FILE;
- if ((idname = device_id_system_read(cmd, dev, idtype)))
- goto id_done;
- goto id_last;
- }
-
- if (MAJOR(dev->dev) == cmd->dev_types->md_major) {
- idtype = DEV_ID_TYPE_MD_UUID;
- if ((idname = device_id_system_read(cmd, dev, idtype)))
- goto id_done;
- goto id_last;
- }
-
- if (MAJOR(dev->dev) == cmd->dev_types->drbd_major) {
- /* TODO */
- log_warn("Missing support for DRBD idtype");
- goto id_last;
- }
-
- /*
- * No device-specific, existing, or user-specified idtypes,
- * so use first available of sys_wwid, wwid_naa, wwid_eui,
- * wwid_t10, sys_serial, devname.
- */
-
- idtype = DEV_ID_TYPE_SYS_WWID;
- if ((idname = device_id_system_read(cmd, dev, idtype)))
- goto id_done;
-
- idtype = DEV_ID_TYPE_WWID_NAA;
- if ((idname = device_id_system_read(cmd, dev, idtype)))
- goto id_done;
-
- idtype = DEV_ID_TYPE_WWID_EUI;
- if ((idname = device_id_system_read(cmd, dev, idtype)))
- goto id_done;
-
- idtype = DEV_ID_TYPE_WWID_T10;
- if ((idname = device_id_system_read(cmd, dev, idtype)))
- goto id_done;
-
- idtype = DEV_ID_TYPE_SYS_SERIAL;
- if ((idname = device_id_system_read(cmd, dev, idtype)))
- goto id_done;
-id_last:
- idtype = DEV_ID_TYPE_DEVNAME;
- if ((idname = device_id_system_read(cmd, dev, idtype)))
- goto id_done;
-
-id_done:
+ if (!device_id_system_read_preferred(cmd, dev, &idtype, &idname))
+ return_0;
if (!idname)
return_0;
+id_done:
/*
* Create a dev_id struct for the new idtype on dev->ids.
}
}
- /* just copying the no-data filters in similar device_ids_find_renamed_devs */
+ /* just copying the no-data filters in similar device_ids_refresh */
if (!cmd->filter->passes_filter(cmd, cmd->filter, dev, "sysfs"))
continue;
if (!cmd->filter->passes_filter(cmd, cmd->filter, dev, "type"))
if (!cmd->enable_devices_file)
return;
- log_debug("validating devices file entries");
+ log_debug("Validating devices file entries");
/*
* Validate entries with proper device id types.
* scanned_devs are the devices that have been scanned,
* so they are the only devs we can verify PVID for.
*/
- if (scanned_devs && !device_list_find_dev(scanned_devs, dev))
+ if (scanned_devs && !device_list_find_dev(scanned_devs, dev)) {
+ log_debug("Validate %s %s PVID %s on %s: not scanned",
+ idtype_to_str(du->idtype), du->idname ?: ".", du->pvid ?: ".", dev_name(dev));
continue;
+ }
/*
* The matched device could not be read so we do not have
* the PVID from disk and cannot verify the devices file entry.
*/
- if (dev->flags & DEV_SCAN_NOT_READ)
+ if (dev->flags & DEV_SCAN_NOT_READ) {
+ log_debug("Validate %s %s PVID %s on %s: not read",
+ idtype_to_str(du->idtype), du->idname ?: ".", du->pvid ?: ".", dev_name(dev));
continue;
+ }
/*
* du and dev may have been matched, but the dev could still
* probably wants to do something about it.
*/
if (!cmd->filter->passes_filter(cmd, cmd->filter, dev, "persistent")) {
+ log_debug("Validate %s %s PVID %s on %s: filtered",
+ idtype_to_str(du->idtype), du->idname ?: ".", du->pvid ?: ".", dev_name(dev));
log_warn("Devices file %s is excluded: %s.",
dev_name(dev), dev_filtered_reason(dev));
continue;
*/
if ((du->idtype == DEV_ID_TYPE_SYS_SERIAL) && du->pvid &&
memcmp(dev->pvid, du->pvid, ID_LEN)) {
+ log_debug("Validate %s %s PVID %s on %s: wrong PVID %s",
+ idtype_to_str(du->idtype), du->idname ?: ".", du->pvid ?: ".",
+ dev_name(dev), dev->pvid);
log_debug("suspect device id serial %s for %s", du->idname, dev_name(dev));
if (!str_list_add(cmd->mem, &cmd->device_ids_check_serial, dm_pool_strdup(cmd->mem, du->idname)))
stack;
*/
if (dev->pvid[0]) {
if (!du->pvid || memcmp(dev->pvid, du->pvid, ID_LEN)) {
+ log_debug("Validate %s %s PVID %s on %s: wrong PVID %s",
+ idtype_to_str(du->idtype), du->idname ?: ".", du->pvid ?: ".",
+ dev_name(dev), dev->pvid);
log_warn("Device %s has PVID %s (devices file %s)",
dev_name(dev), dev->pvid, du->pvid ?: "none");
if (!(tmpdup = strdup(dev->pvid)))
}
} else {
if (du->pvid && (du->pvid[0] != '.')) {
+ log_debug("Validate %s %s PVID %s on %s: wrong PVID %s",
+ idtype_to_str(du->idtype), du->idname ?: ".", du->pvid ?: ".",
+ dev_name(dev), dev->pvid);
log_warn("Device %s has no PVID (devices file %s)",
dev_name(dev), du->pvid);
free(du->pvid);
}
}
+ log_debug("Validate %s %s PVID %s on %s: correct",
+ idtype_to_str(du->idtype), du->idname ?: ".", du->pvid ?: ".",
+ dev_name(dev));
+
/*
* Avoid thrashing changes to the devices file during
* startup due to device names that are still being
devname = dev_name(dev);
- log_print("Devices file PVID %s is now on %s.", du->pvid, devname);
+ log_debug("Devices file PVID %s is now on %s.", du->pvid, devname);
dup_devname1 = strdup(devname);
dup_devname2 = strdup(devname);
/*
* Each remaining du that's not matched to a dev (no du->dev set) is
- * subject to device_ids_find_renamed_devs which will look for
- * unmatched pvids on devs that have not been scanned yet.
+ * subject to device_ids_refresh which will look for unmatched pvids on
+ * devs that have not been scanned yet.
*/
dm_list_iterate_items(du, &cmd->use_devices) {
- if (du->idtype != DEV_ID_TYPE_DEVNAME)
+ /*
+ * Only search for devname type entries unless the refresh
+ * trigger is set due to a machine change, in which case
+ * we look for missing PVIDs on new devs with real idtypes.
+ */
+ if ((du->idtype != DEV_ID_TYPE_DEVNAME) && !cmd->device_ids_refresh_trigger)
continue;
if (!du->pvid)
continue;
if (du->dev)
continue;
- log_debug("Search needed to find device with PVID %s.", du->pvid);
+ log_debug("Search needed to locate PVID %s %s %s.",
+ du->pvid, idtype_to_str(du->idtype), du->idname ?: ".");
}
/*
/*
* Check for other problems for which we want to set *device_ids_invalid,
* even if we don't have a way to fix them right here. In particular,
- * issues that may be fixed shortly by device_ids_find_renamed_devs.
+ * issues that may be fixed shortly by device_ids_refresh.
*
* The device_ids_invalid flag is only used to tell the caller not
* to write hints, which could be based on invalid device info.
if (update_file)
unlink_searched_devnames(cmd);
- /* FIXME: for wrong devname cases, wait to write new until device_ids_find_renamed_devs? */
+ /* FIXME: for wrong devname cases, wait to write new until device_ids_refresh? */
/*
* try lock and device_ids_write(), the update is not required and will
* is using a non-system devices file?
*/
-void device_ids_find_renamed_devs(struct cmd_context *cmd, struct dm_list *dev_list,
- int *search_count, int noupdate)
+void device_ids_refresh(struct cmd_context *cmd, struct dm_list *dev_list,
+ int *search_count, int noupdate)
{
struct device *dev;
struct dev_use *du;
struct dev_iter *iter;
struct device_list *devl; /* holds struct device */
struct device_id_list *dil, *dil2; /* holds struct device + pvid */
- struct dm_list search_pvids; /* list of device_id_list */
- struct dm_list search_devs ; /* list of device_list */
+ struct dm_list search_list_pvids; /* list of device_id_list */
+ struct dm_list search_list_devs ; /* list of device_list */
const char *devname;
int update_file = 0;
int other_idtype = 0;
int no_pvid = 0;
int found = 0;
int not_found = 0;
- int search_none;
- int search_auto;
+ int search_mode_none;
+ int search_mode_auto;
+ int search_mode_all;
- dm_list_init(&search_pvids);
- dm_list_init(&search_devs);
+ dm_list_init(&search_list_pvids);
+ dm_list_init(&search_list_devs);
if (!cmd->enable_devices_file)
return;
- search_none = !strcmp(cmd->search_for_devnames, "none");
- search_auto = !strcmp(cmd->search_for_devnames, "auto");
+ if (cmd->device_ids_refresh_trigger) {
+ search_mode_all = 1;
+ search_mode_none = 0;
+ search_mode_auto = 0;
+ } else {
+ search_mode_all = !strcmp(cmd->search_for_devnames, "all");
+ search_mode_none = !strcmp(cmd->search_for_devnames, "none");
+ search_mode_auto = !strcmp(cmd->search_for_devnames, "auto");
+ }
+ /*
+ * Create search_list_pvids which is a list of PVIDs that
+ * we want to locate on some device.
+ */
dm_list_iterate_items(du, &cmd->use_devices) {
- if (du->idtype != DEV_ID_TYPE_DEVNAME)
- continue;
if (!du->pvid)
continue;
if (du->dev)
continue;
- if (!(dil = dm_pool_zalloc(cmd->mem, sizeof(*dil))))
+ /*
+ * When device_ids_refresh_trigger is set, it means
+ * that a PVID may be shifted to a new device even when
+ * the entry uses a stable id type, like wwid.
+ * Otherwise, we assume that only entries using the
+ * devname id type can move to new devices.
+ */
+ if (!cmd->device_ids_refresh_trigger &&
+ (du->idtype != DEV_ID_TYPE_DEVNAME))
continue;
- if (!search_none) {
- memcpy(dil->pvid, du->pvid, ID_LEN);
- dm_list_add(&search_pvids, &dil->list);
- }
log_debug("Search for PVID %s.", du->pvid);
+
if (search_count)
(*search_count)++;
+
+ if (search_mode_none)
+ continue;
+
+ if (!(dil = dm_pool_zalloc(cmd->mem, sizeof(*dil))))
+ continue;
+ memcpy(dil->pvid, du->pvid, ID_LEN);
+ dm_list_add(&search_list_pvids, &dil->list);
}
- if (dm_list_empty(&search_pvids))
+ /* No unmatched PVIDs to search for, and no system id to update. */
+ if (dm_list_empty(&search_list_pvids) && !cmd->device_ids_refresh_trigger)
return;
+ log_debug("device ids refresh search_pvids %d trigger %d search all %d auto %d none %d",
+ dm_list_size(&search_list_pvids), cmd->device_ids_refresh_trigger,
+ search_mode_all, search_mode_auto, search_mode_none);
+
+ if (dm_list_empty(&search_list_pvids) && cmd->device_ids_refresh_trigger) {
+ update_file = 1;
+ goto out;
+ }
+
/*
* A previous command searched for devnames and found nothing, so it
* created the searched file to tell us not to bother. Without this, a
* If hints are enabled, the hints invalidation could also remove the
* searched file.
*/
- if (_searched_devnames_exists(cmd)) {
+ if (!cmd->device_ids_refresh_trigger && _searched_devnames_exists(cmd)) {
log_debug("Search for PVIDs skipped for %s", _searched_file);
return;
}
if (!(devl = dm_pool_zalloc(cmd->mem, sizeof(*devl))))
continue;
devl->dev = dev;
- dm_list_add(&search_devs, &devl->list);
+ dm_list_add(&search_list_devs, &devl->list);
}
dev_iter_destroy(iter);
- log_debug("Search for PVIDs reading labels on %d devs.", dm_list_size(&search_devs));
+ log_debug("Search for PVIDs reading labels on %d devs.", dm_list_size(&search_list_devs));
/*
* Read the dev to get the pvid, and run the filters that will use the
* to modify the command's existing filter chain or the persistent
* filter values.
*/
- dm_list_iterate_items(devl, &search_devs) {
+ dm_list_iterate_items(devl, &search_list_devs) {
int has_pvid;
dev = devl->dev;
* themselves as alternatives to the missing ID_TYPE_DEVNAME
* entry. i.e. a ID_TYPE_DEVNAME entry would not appear on a
* device that has a wwid and would use ID_TYPE_SYS_WWID. So,
- * if a dev in the search_devs list has a proper/stable device
+ * if a dev in the search_list_devs list has a proper/stable device
* id (e.g. wwid, serial, loop, mpath), then we don't need to
* read it to check for missing PVIDs.
*
* user forces a devname id, then they should probably also
* set search_for_devnames=all.
*/
- if (search_auto && _dev_has_stable_id(cmd, dev)) {
+ if (search_mode_auto && _dev_has_stable_id(cmd, dev)) {
other_idtype++;
continue;
}
goto next;
/*
- * Check if the the PVID is one we are searching for.
- * Loop below looks at search_pvid entries that have dil->dev set.
- * This continues checking after all search_pvids entries have been
+ * Check if the the PVID returned from label_read is one we are looking for.
+ * The loop below looks at search_list_pvids entries that have dil->dev set.
+ * This loop continues checking after all search_list_pvids entries have been
* matched in order to check if the PVID is on duplicate devs.
*/
- dm_list_iterate_items_safe(dil, dil2, &search_pvids) {
+ dm_list_iterate_items_safe(dil, dil2, &search_list_pvids) {
if (!memcmp(dil->pvid, dev->pvid, ID_LEN)) {
if (dil->dev) {
log_warn("WARNING: found PVID %s on multiple devices %s %s.",
/*
* The use_devices entries (repesenting the devices file) are
* updated for the new devices on which the PVs reside. The new
- * correct devs are set as dil->dev on search_pvids entries.
+ * correct devs are set as dil->dev on search_list_pvids entries.
*
* The du/dev/id are set up and linked for the new devs.
*
* The command's full filter chain is updated for the new devs now that
* filter-deviceid will pass.
*/
- dm_list_iterate_items(dil, &search_pvids) {
- char *dup_devname1, *dup_devname2, *dup_devname3;
+ dm_list_iterate_items(dil, &search_list_pvids) {
+ char *new_idname, *new_idname2, *new_devname;
+ uint16_t new_idtype;
if (!dil->dev || dm_list_empty(&dil->dev->aliases)) {
not_found++;
/* shouldn't happen */
continue;
}
- if (du->idtype != DEV_ID_TYPE_DEVNAME) {
- /* shouldn't happen */
- continue;
+
+ new_idtype = 0;
+ new_idname = NULL;
+ new_idname2 = NULL;
+ new_devname = NULL;
+
+ if (cmd->device_ids_refresh_trigger) {
+ if (!device_id_system_read_preferred(cmd, dev, &new_idtype, (const char **)&new_idname))
+ continue;
+ new_idname2 = strdup(new_idname);
+ new_devname = strdup(devname);
+ log_print_unless_silent("Devices file PVID %s has new device ID %s %s from %s.",
+ du->pvid ?: "", idtype_to_str(new_idtype), new_idname ?: "", devname);
+ } else {
+ /* Use the new device name as the new idname. */
+ new_idtype = DEV_ID_TYPE_DEVNAME;
+ new_idname = strdup(devname);
+ new_idname2 = strdup(devname);
+ new_devname = strdup(devname);
+ log_print_unless_silent("Found new device name %s for PVID %s.", devname, du->pvid ?: "");
}
- dup_devname1 = strdup(devname);
- dup_devname2 = strdup(devname);
- dup_devname3 = strdup(devname);
id = zalloc(sizeof(struct dev_id));
- if (!dup_devname1 || !dup_devname2 || !dup_devname3 || !id) {
- free(dup_devname1);
- free(dup_devname2);
- free(dup_devname3);
+
+ if (!id || !new_devname || !new_idname || !new_idname2) {
free(id);
+ free(new_idname);
+ free(new_idname2);
+ free(new_devname);
stack;
continue;
}
- if (!noupdate)
- log_warn("Devices file PVID %s updating IDNAME to %s.", dev->pvid, devname);
-
free(du->idname);
free(du->devname);
free_dids(&dev->ids);
- du->idname = dup_devname1;
- du->devname = dup_devname2;
- id->idtype = DEV_ID_TYPE_DEVNAME;
- id->idname = dup_devname3;
- id->dev = dev;
+ du->idtype = new_idtype;
+ du->idname = new_idname;
+ du->devname = new_devname;
du->dev = dev;
+ id->idtype = new_idtype;
+ id->idname = new_idname2;
+ id->dev = dev;
dev->id = id;
dev->flags |= DEV_MATCHED_USE_ID;
dm_list_add(&dev->ids, &id->list);
update_file = 1;
}
- dm_list_iterate_items(dil, &search_pvids) {
+ dm_list_iterate_items(dil, &search_list_pvids) {
if (!dil->dev)
continue;
dev = dil->dev;
}
}
+ out:
/*
* try lock and device_ids_write(), the update is not required and will
* be done by a subsequent command if it's not done here.
}
/*
- * The entries in search_pvids with a dev set are the new devs found
+ * The entries in search_list_pvids with a dev set are the new devs found
* for the PVIDs that we want to return to the caller in a device_list
* format.
*/
- dm_list_iterate_items(dil, &search_pvids) {
+ dm_list_iterate_items(dil, &search_list_pvids) {
if (!dil->dev)
continue;
dev = dil->dev;
* pvids not found were from devices that are permanently detached.
* If a new PV appears, pvscan will run and do unlink_searched_file.
*/
- if (not_found && !found)
+ if (!cmd->device_ids_refresh_trigger && not_found && !found)
_touch_searched_devnames(cmd);
}
--- /dev/null
+#!/usr/bin/env bash
+
+# Copyright (C) 2020-23 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+test_description='refresh device ids if system changes'
+
+SKIP_WITH_LVMPOLLD=1
+
+. lib/inittest
+
+test -d /sys/block/ram0 && skip "Ramdisk already loaded"
+
+test "$DM_DEV_DIR" = "/dev" || skip "Only works with /dev access -> make check LVM_TEST_DEVDIR=/dev"
+
+# requires trailing / to match dm
+SYS_DIR="$PWD/test/sys"
+aux lvmconf "devices/use_devicesfile = 1" \
+ "devices/device_id_sysfs_dir = \"$SYS_DIR/\""
+
+aux lvmconf 'devices/global_filter = [ "a|.*|" ]'
+
+SERIAL1="S111"
+SERIAL2="S222"
+SERIAL3="S333"
+SERIAL4="S444"
+
+PRODUCT_UUID1="11111111-2222-3333-4444-555555555555"
+PRODUCT_UUID2="11111111-2222-3333-4444-666666666666"
+
+create_sysfs() {
+ mkdir -p "$SYS_DIR/dev/block/$MAJOR1:$MINOR1/device"
+ mkdir -p "$SYS_DIR/dev/block/$MAJOR2:$MINOR2/device"
+ mkdir -p "$SYS_DIR/dev/block/$MAJOR3:$MINOR3/device"
+ mkdir -p "$SYS_DIR/dev/block/$MAJOR4:$MINOR4/device"
+
+ echo "$SERIAL1" > "$SYS_DIR/dev/block/$MAJOR1:$MINOR1/device/serial"
+ echo "$SERIAL2" > "$SYS_DIR/dev/block/$MAJOR2:$MINOR2/device/serial"
+ echo "$SERIAL3" > "$SYS_DIR/dev/block/$MAJOR3:$MINOR3/device/serial"
+ echo "$SERIAL4" > "$SYS_DIR/dev/block/$MAJOR4:$MINOR4/device/serial"
+
+ mkdir -p "$SYS_DIR/devices/virtual/dmi/id/"
+ echo "$PRODUCT_UUID1" > "$SYS_DIR/devices/virtual/dmi/id/product_uuid"
+}
+
+remove_sysfs() {
+ rm -rf "$SYS_DIR"
+}
+
+cleanup_and_teardown()
+{
+ remove_sysfs
+ rmmod brd
+
+ aux teardown
+}
+
+trap 'cleanup_and_teardown' EXIT
+
+modprobe brd rd_nr=4 || skip
+sleep 1
+remove_sysfs
+
+dev1="/dev/ram0"
+dev2="/dev/ram1"
+dev3="/dev/ram2"
+dev4="/dev/ram3"
+
+DFDIR="$LVM_SYSTEM_DIR/devices"
+mkdir -p "$DFDIR" || true
+DF="$DFDIR/system.devices"
+ORIG="$DFDIR/orig.devices"
+touch "$DF"
+
+aux wipefs_a "$dev1"
+aux wipefs_a "$dev2"
+aux wipefs_a "$dev3"
+aux wipefs_a "$dev4"
+
+vgcreate $vg1 "$dev1"
+eval "$(pvs --noheading --nameprefixes -o major,minor,uuid "$dev1")"
+MAJOR1=$LVM2_PV_MAJOR
+MINOR1=$LVM2_PV_MINOR
+OPVID1=$LVM2_PV_UUID
+PVID1=${OPVID1//-/}
+
+vgcreate $vg2 "$dev2"
+eval "$(pvs --noheading --nameprefixes -o major,minor,uuid "$dev2")"
+MAJOR2=$LVM2_PV_MAJOR
+MINOR2=$LVM2_PV_MINOR
+OPVID2=$LVM2_PV_UUID
+PVID2=${OPVID2//-/}
+
+# just using pvcreate/pvs to get MAJOR MINOR
+
+pvcreate "$dev3"
+eval "$(pvs --noheading --nameprefixes -o major,minor,uuid "$dev3")"
+MAJOR3=$LVM2_PV_MAJOR
+MINOR3=$LVM2_PV_MINOR
+
+pvcreate "$dev4"
+eval "$(pvs --noheading --nameprefixes -o major,minor,uuid "$dev4")"
+MAJOR4=$LVM2_PV_MAJOR
+MINOR4=$LVM2_PV_MINOR
+
+pvremove "$dev3"
+pvremove "$dev4"
+aux wipefs_a "$dev3"
+aux wipefs_a "$dev4"
+
+create_sysfs
+
+rm "$DF"
+
+vgimportdevices $vg1
+vgimportdevices $vg2
+
+cat "$DF"
+
+grep $PRODUCT_UUID1 "$DF"
+grep "$dev1" "$DF"
+grep "$dev2" "$DF"
+not grep "$dev3" "$DF"
+not grep "$dev4" "$DF"
+grep $SERIAL1 "$DF"
+grep $SERIAL2 "$DF"
+not grep $SERIAL3 "$DF"
+not grep $SERIAL4 "$DF"
+
+pvs |tee out
+grep "$dev1" out
+grep "$dev2" out
+not grep "$dev3" out
+not grep "$dev4" out
+
+# Prints the deviceid that's saved in metadata.
+pvs -o uuid,deviceid "$dev1" | tee out
+grep $OPVID1 out
+grep $SERIAL1 out
+
+# PV1 moves from dev1 to dev3 (and dev1 goes away)
+# lvm does not find PV1 until the product_uuid changes which
+# triggers the command to look at devs outside the DF.
+
+# PV1 moves to new dev
+dd if="$dev1" of="$dev3" bs=1M count=1
+aux wipefs_a "$dev1"
+rm "$SYS_DIR/dev/block/$MAJOR1:$MINOR1/device/serial"
+
+# PV1 not found
+pvs |tee out
+not grep "$dev1" out
+grep "$dev2" out
+not grep "$dev3" out
+not grep "$dev4" out
+
+# DF unchanged
+grep $PRODUCT_UUID1 "$DF"
+grep "$dev1" "$DF"
+grep "$dev2" "$DF"
+not grep "$dev3" "$DF"
+not grep "$dev4" "$DF"
+grep $SERIAL1 "$DF"
+grep $SERIAL2 "$DF"
+not grep $SERIAL3 "$DF"
+not grep $SERIAL4 "$DF"
+
+# product_uuid changes
+echo "$PRODUCT_UUID2" > "$SYS_DIR/devices/virtual/dmi/id/product_uuid"
+
+# PV1 found on new dev
+pvs |tee out
+not grep "$dev1" out
+grep "$dev2" out
+grep "$dev3" out
+not grep "$dev4" out
+
+# DF updated replacing old dev with new dev
+grep $PRODUCT_UUID2 "$DF"
+not grep "$dev1" "$DF"
+grep "$dev2" "$DF"
+grep "$dev3" "$DF"
+not grep "$dev4" "$DF"
+not grep $SERIAL1 "$DF"
+grep $SERIAL2 "$DF"
+grep $SERIAL3 "$DF"
+not grep $SERIAL4 "$DF"
+
+# PV1 was originally written to dev1 but has not
+# moved to dev3. The deviceid in the metadata is
+# S111 from dev1, but the PV is now on dev3 which
+# has deviceid S333. Since the deviceid of the dev
+# doesn't match the deviceid savedin metadata,
+# "invalid" is printed when displaying the outdated
+# deviceid from the metadata.
+pvs -o uuid,deviceid "$dev3" | tee out
+grep $OPVID1 out
+grep invalid out
+
+# bring back dev1
+echo "$SERIAL1" > "$SYS_DIR/dev/block/$MAJOR1:$MINOR1/device/serial"
+
+
+# No product_uuid so hostname is used
+
+rm "$SYS_DIR/devices/virtual/dmi/id/product_uuid"
+
+rm "$DF"
+vgimportdevices $vg1
+vgimportdevices $vg2
+
+grep HOSTNAME "$DF"
+not grep PRODUCT_UUID "$DF"
+
+pvs |tee out
+not grep "$dev1" out
+grep "$dev2" out
+grep "$dev3" out
+not grep "$dev4" out
+
+not grep "$dev1" "$DF"
+grep "$dev2" "$DF"
+grep "$dev3" "$DF"
+not grep "$dev4" "$DF"
+
+# PV1 moves from dev3 back to dev1
+# lvm does not find PV1 until the hostname changes which
+# triggers the command to look at devs outside the DF.
+
+# PV1 moves to new dev
+dd if="$dev3" of="$dev1" bs=1M count=1
+aux wipefs_a "$dev3"
+rm "$SYS_DIR/dev/block/$MAJOR3:$MINOR3/device/serial"
+
+# PV1 not found
+pvs |tee out
+not grep "$dev1" out
+grep "$dev2" out
+not grep "$dev3" out
+not grep "$dev4" out
+
+# we can't change the hostname to trigger lvm refresh,
+# but removing the HOSTNAME line from system.devices
+# will be a trigger.
+sed -e "s|HOSTNAME=.||" "$DF" > tmpdf
+cp tmpdf "$DF"
+
+# PV1 found on new dev
+pvs |tee out
+grep "$dev1" out
+grep "$dev2" out
+not grep "$dev3" out
+not grep "$dev4" out
+
+# DF updated replacing old dev with new dev
+not grep PRODUCT_UUID "$DF"
+grep HOSTNAME "$DF"
+grep "$dev1" "$DF"
+grep "$dev2" "$DF"
+not grep "$dev3" "$DF"
+not grep "$dev4" "$DF"
+grep $SERIAL1 "$DF"
+grep $SERIAL2 "$DF"
+not grep $SERIAL3 "$DF"
+not grep $SERIAL4 "$DF"
+
+# bring back dev3
+echo "$SERIAL3" > "$SYS_DIR/dev/block/$MAJOR3:$MINOR3/device/serial"
+
+# DF has no PRODUCT_UUID or HOSTNAME, lvm command adds one
+
+rm "$DF"
+vgimportdevices $vg1
+vgimportdevices $vg2
+
+sed -e "s|HOSTNAME=.||" "$DF" > tmpdf
+cp tmpdf "$DF"
+sed -e "s|PRODUCT_UUID=.||" "$DF" > tmpdf
+cp tmpdf "$DF"
+
+not grep HOSTNAME "$DF"
+not grep PRODUCT_UUID "$DF"
+
+pvs
+grep HOSTNAME "$DF"
+grep "$dev1" "$DF"
+grep "$dev2" "$DF"
+not grep "$dev3" "$DF"
+not grep "$dev4" "$DF"
+grep $SERIAL1 "$DF"
+grep $SERIAL2 "$DF"
+not grep $SERIAL3 "$DF"
+not grep $SERIAL4 "$DF"
+
+
+# DF has PRODUCT_UUID but system only has hostname,
+# and PV1 moves to different device
+
+echo "$PRODUCT_UUID1" > "$SYS_DIR/devices/virtual/dmi/id/product_uuid"
+rm "$DF"
+vgimportdevices $vg1
+vgimportdevices $vg2
+rm "$SYS_DIR/devices/virtual/dmi/id/product_uuid"
+
+# PV1 moves from dev1 to dev3
+dd if="$dev1" of="$dev3" bs=1M count=1
+aux wipefs_a "$dev1"
+rm "$SYS_DIR/dev/block/$MAJOR1:$MINOR1/device/serial"
+
+pvs
+grep HOSTNAME "$DF"
+not grep "$dev1" "$DF"
+grep "$dev2" "$DF"
+grep "$dev3" "$DF"
+not grep "$dev4" "$DF"
+not grep $SERIAL1 "$DF"
+grep $SERIAL2 "$DF"
+grep $SERIAL3 "$DF"
+not grep $SERIAL4 "$DF"
+
+# bring back dev1
+echo "$SERIAL1" > "$SYS_DIR/dev/block/$MAJOR1:$MINOR1/device/serial"
+
+# DF has HOSTNAME but system has product_uuid, lvm command updates it
+
+rm "$DF"
+vgimportdevices $vg1
+vgimportdevices $vg2
+
+grep HOSTNAME "$DF"
+echo "$PRODUCT_UUID1" > "$SYS_DIR/devices/virtual/dmi/id/product_uuid"
+pvs
+grep "$PRODUCT_UUID1" "$DF"
+not grep HOSTNAME "$DF"
+
+
+# DF has PRODUCT_UUID, system product_uuid changes, lvm command upates it
+
+rm "$DF"
+vgimportdevices $vg1
+vgimportdevices $vg2
+
+grep "$PRODUCT_UUID1" "$DF"
+echo "$PRODUCT_UUID2" > "$SYS_DIR/devices/virtual/dmi/id/product_uuid"
+pvs
+grep "$PRODUCT_UUID2" "$DF"
+
+# PV1 moves from dev3 back to dev1
+dd if="$dev3" of="$dev1" bs=1M count=1
+aux wipefs_a "$dev3"
+
+
+#
+# pvscan --cache and vgchange -aay work when refresh is triggered and
+# the device ids are wrong on the PVs that need to be autoactivated.
+#
+
+RUNDIR="/run"
+test -d "$RUNDIR" || RUNDIR="/var/run"
+PVS_ONLINE_DIR="$RUNDIR/lvm/pvs_online"
+VGS_ONLINE_DIR="$RUNDIR/lvm/vgs_online"
+PVS_LOOKUP_DIR="$RUNDIR/lvm/pvs_lookup"
+
+_clear_online_files() {
+ # wait till udev is finished
+ aux udev_wait
+ rm -f "$PVS_ONLINE_DIR"/*
+ rm -f "$VGS_ONLINE_DIR"/*
+ rm -f "$PVS_LOOKUP_DIR"/*
+}
+
+echo "$PRODUCT_UUID1" > "$SYS_DIR/devices/virtual/dmi/id/product_uuid"
+rm "$DF"
+vgimportdevices $vg1
+vgimportdevices $vg2
+grep "$PRODUCT_UUID1" "$DF"
+grep "$dev1" "$DF"
+grep "$dev2" "$DF"
+grep $SERIAL1 "$DF"
+grep $SERIAL2 "$DF"
+lvcreate -l1 -an -n $lv1 $vg1
+lvcreate -l1 -an -n $lv1 $vg2
+pvs -o+deviceid
+
+# PV1 moves from dev1 to dev3
+dd if="$dev1" of="$dev3" bs=1M count=1
+aux wipefs_a "$dev1"
+rm "$SYS_DIR/dev/block/$MAJOR1:$MINOR1/device/serial"
+
+_clear_online_files
+
+# One PV in VG to autoactivate when system.devices has the wrong device ID
+# PV1 is listed in system.devices as being from dev1 with SERIAL1,
+# but PV1 is actually appearing from dev3 with SERIAL3. PRODUCT_UUID is
+# wrong, so refresh is triggered and PV1 will be used from dev3.
+
+echo "$PRODUCT_UUID2" > "$SYS_DIR/devices/virtual/dmi/id/product_uuid"
+
+pvscan --cache --listvg --checkcomplete --vgonline --autoactivation event "$dev3"
+ls "$RUNDIR/lvm/pvs_online/$PVID1"
+ls "$RUNDIR/lvm/vgs_online/$vg1"
+vgchange -aay --autoactivation event $vg1
+
+# DF should be unchanged and have old info since the event based pvscan
+# and vgchange are special/optimized for auto activation and don't update DF
+grep "$PRODUCT_UUID1" "$DF"
+grep "$dev1" "$DF"
+grep "$dev2" "$DF"
+not grep "$dev3" "$DF"
+not grep "$dev4" "$DF"
+grep $SERIAL1 "$DF"
+grep $SERIAL2 "$DF"
+not grep $SERIAL3 "$DF"
+not grep $SERIAL4 "$DF"
+
+# check that pvs will update DF PV1 to have SERIAL3
+pvs
+grep "$PRODUCT_UUID2" "$DF"
+not grep "$dev1" "$DF"
+grep "$dev2" "$DF"
+grep "$dev3" "$DF"
+not grep "$dev4" "$DF"
+not grep $SERIAL1 "$DF"
+grep $SERIAL2 "$DF"
+grep $SERIAL3 "$DF"
+not grep $SERIAL4 "$DF"
+
+# check that the vgchange aay above actually activated the LV
+lvs -o active $vg1/$lv1 | grep active
+
+vgchange -an $vg1
+
+# bring back dev1
+echo "$SERIAL1" > "$SYS_DIR/dev/block/$MAJOR1:$MINOR1/device/serial"
+
+# Two PVs in VG to autoactivate when system.devices has the wrong device ID
+
+# PV1 moves from dev3 back to dev1
+dd if="$dev3" of="$dev1" bs=1M count=1
+aux wipefs_a "$dev3"
+
+rm "$DF"
+vgremove -ff $vg1
+vgremove -ff $vg2
+pvs
+
+echo "$PRODUCT_UUID1" > "$SYS_DIR/devices/virtual/dmi/id/product_uuid"
+
+vgcreate $vg1 "$dev1" "$dev2"
+vgimportdevices $vg1
+lvcreate -l1 -n $lv1 $vg1 "$dev1"
+lvcreate -l1 -n $lv2 $vg1 "$dev2"
+lvcreate -l4 -i2 -n $lv3 $vg1 "$dev1" "$dev2"
+vgchange -an $vg1
+
+grep "$PRODUCT_UUID1" "$DF"
+grep "$dev1" "$DF"
+grep "$dev2" "$DF"
+not grep "$dev3" "$DF"
+not grep "$dev4" "$DF"
+grep $SERIAL1 "$DF"
+grep $SERIAL2 "$DF"
+not grep $SERIAL3 "$DF"
+not grep $SERIAL4 "$DF"
+
+# PV1 moves from dev1 to dev3
+dd if="$dev1" of="$dev3" bs=1M count=1
+aux wipefs_a "$dev1"
+rm "$SYS_DIR/dev/block/$MAJOR1:$MINOR1/device/serial"
+
+# PV2 moves from dev2 to dev4
+dd if="$dev2" of="$dev4" bs=1M count=1
+aux wipefs_a "$dev2"
+rm "$SYS_DIR/dev/block/$MAJOR2:$MINOR2/device/serial"
+
+_clear_online_files
+
+# Two PVs in VG to autoactivate when system.devices has the wrong device ID
+# system.devices says PV1 has SERIAL1 and PV2 has SERIAL2, but the new
+# system has PV1 on SERIAL3 and PV2 on SERIAL4.
+# PRODUCT_UUID is wrong, so refresh finds PV1/PV2 on SERIAL3/SERIAL4
+
+echo "$PRODUCT_UUID2" > "$SYS_DIR/devices/virtual/dmi/id/product_uuid"
+
+pvscan --cache --listvg --checkcomplete --vgonline --autoactivation event "$dev3"
+pvscan --cache --listvg --checkcomplete --vgonline --autoactivation event "$dev4"
+ls "$RUNDIR/lvm/pvs_online/$PVID1"
+ls "$RUNDIR/lvm/pvs_online/$PVID2"
+ls "$RUNDIR/lvm/vgs_online/$vg1"
+ls "$RUNDIR/lvm/pvs_lookup/$vg1"
+vgchange -aay --autoactivation event $vg1
+
+# DF not yet updated by pvscan/vgchange
+
+grep "$PRODUCT_UUID1" "$DF"
+grep "$dev1" "$DF"
+grep "$dev2" "$DF"
+not grep "$dev3" "$DF"
+not grep "$dev4" "$DF"
+grep $SERIAL1 "$DF"
+grep $SERIAL2 "$DF"
+not grep $SERIAL3 "$DF"
+not grep $SERIAL4 "$DF"
+
+# check that lvmdevices will update DF
+lvmdevices --update
+grep "$PRODUCT_UUID2" "$DF"
+not grep "$dev1" "$DF"
+not grep "$dev2" "$DF"
+grep "$dev3" "$DF"
+grep "$dev4" "$DF"
+not grep $SERIAL1 "$DF"
+not grep $SERIAL2 "$DF"
+grep $SERIAL3 "$DF"
+grep $SERIAL4 "$DF"
+
+# check that the vgchange actually activated LVs
+lvs $vg1
+lvs -o active $vg1/$lv1 | grep active
+lvs -o active $vg1/$lv2 | grep active
+lvs -o active $vg1/$lv3 | grep active
+
+vgchange -an $vg1
+vgremove -ff $vg1
+