mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-21 13:34:40 +03:00
f3f7f5db89
TODO: not sure if here we should possibly exit from function.
1022 lines
30 KiB
C
1022 lines
30 KiB
C
/*
|
|
* 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 "tools.h"
|
|
#include "lib/cache/lvmcache.h"
|
|
#include "lib/device/device_id.h"
|
|
#include "lib/device/dev-type.h"
|
|
#include "lib/filters/filter.h"
|
|
|
|
/* coverity[unnecessary_header] needed for MuslC */
|
|
#include <sys/file.h>
|
|
|
|
extern char devices_file_hostname_orig[PATH_MAX];
|
|
extern char devices_file_product_uuid_orig[PATH_MAX];
|
|
|
|
static void _search_devs_for_pvids(struct cmd_context *cmd, struct dm_list *search_pvids, struct dm_list *found_devs)
|
|
{
|
|
struct dev_iter *iter;
|
|
struct device *dev;
|
|
struct device_list *devl, *devl2;
|
|
struct device_id_list *dil, *dil2;
|
|
struct dm_list devs;
|
|
int found;
|
|
|
|
dm_list_init(&devs);
|
|
|
|
/*
|
|
* Create a list of all devices on the system, without applying
|
|
* any filters, since we do not want filters to read any of the
|
|
* devices yet.
|
|
*/
|
|
if (!(iter = dev_iter_create(NULL, 0)))
|
|
return;
|
|
while ((dev = dev_iter_get(cmd, iter))) {
|
|
/* Skip devs with a valid match to a du. */
|
|
if (get_du_for_dev(cmd, dev))
|
|
continue;
|
|
|
|
if (!(devl = dm_pool_zalloc(cmd->mem, sizeof(*devl))))
|
|
continue;
|
|
devl->dev = dev;
|
|
dm_list_add(&devs, &devl->list);
|
|
}
|
|
dev_iter_destroy(iter);
|
|
|
|
/*
|
|
* Apply the filters that do not require reading the devices
|
|
*/
|
|
log_debug("Filtering devices (no data) for pvid search");
|
|
cmd->filter_nodata_only = 1;
|
|
cmd->filter_deviceid_skip = 1;
|
|
dm_list_iterate_items_safe(devl, devl2, &devs) {
|
|
if (!cmd->filter->passes_filter(cmd, cmd->filter, devl->dev, NULL))
|
|
dm_list_del(&devl->list);
|
|
}
|
|
|
|
/*
|
|
* Read header from each dev to see if it has one of the pvids we're
|
|
* searching for.
|
|
*/
|
|
dm_list_iterate_items_safe(devl, devl2, &devs) {
|
|
int has_pvid;
|
|
|
|
/* sets dev->pvid if an lvm label with pvid is found */
|
|
if (!label_read_pvid(devl->dev, &has_pvid))
|
|
continue;
|
|
if (!has_pvid)
|
|
continue;
|
|
|
|
found = 0;
|
|
dm_list_iterate_items_safe(dil, dil2, search_pvids) {
|
|
if (!strcmp(devl->dev->pvid, dil->pvid)) {
|
|
dm_list_del(&devl->list);
|
|
dm_list_del(&dil->list);
|
|
dm_list_add(found_devs, &devl->list);
|
|
log_print("Found PVID %s on %s.", dil->pvid, dev_name(devl->dev));
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (!found)
|
|
label_scan_invalidate(devl->dev);
|
|
|
|
/*
|
|
* FIXME: search all devs in case pvid is duplicated on multiple devs.
|
|
*/
|
|
if (dm_list_empty(search_pvids))
|
|
break;
|
|
}
|
|
|
|
dm_list_iterate_items(dil, search_pvids)
|
|
log_error("PVID %s not found on any devices.", dil->pvid);
|
|
|
|
/*
|
|
* Now that the device has been read, apply the filters again
|
|
* which will now include filters that read data from the device.
|
|
* N.B. we've already skipped devs that were excluded by the
|
|
* no-data filters, so if the PVID exists on one of those devices
|
|
* no warning is printed.
|
|
*/
|
|
log_debug("Filtering devices (with data) for pvid search");
|
|
cmd->filter_nodata_only = 0;
|
|
cmd->filter_deviceid_skip = 1;
|
|
dm_list_iterate_items_safe(devl, devl2, found_devs) {
|
|
dev = devl->dev;
|
|
cmd->filter->wipe(cmd, cmd->filter, dev, NULL);
|
|
if (!cmd->filter->passes_filter(cmd, cmd->filter, dev, NULL)) {
|
|
log_warn("WARNING: PVID %s found on %s which is excluded: %s",
|
|
dev->pvid, dev_name(dev), dev_filtered_reason(dev));
|
|
dm_list_del(&devl->list);
|
|
}
|
|
}
|
|
}
|
|
|
|
static char *_part_str(struct dev_use *du)
|
|
{
|
|
static char _part_str_buf[64];
|
|
|
|
if (du->part)
|
|
snprintf(_part_str_buf, 63, " PART=%d", du->part);
|
|
else
|
|
_part_str_buf[0] = '\0';
|
|
|
|
return _part_str_buf;
|
|
}
|
|
|
|
static void _print_check(struct cmd_context *cmd)
|
|
{
|
|
struct dm_list use_old;
|
|
struct dm_list use_new;
|
|
struct dm_list done_old;
|
|
struct dm_list done_new;
|
|
struct dev_use *du_old, *du_new;
|
|
int pvid_same;
|
|
int idname_same;
|
|
int idtype_same;
|
|
int devname_same;
|
|
int part_same;
|
|
char *part;
|
|
|
|
dm_list_init(&use_old);
|
|
dm_list_init(&use_new);
|
|
dm_list_init(&done_old);
|
|
dm_list_init(&done_new);
|
|
|
|
/*
|
|
* Move the entries that have been processed out of the way so
|
|
* original entries can be added to use_devices by device_ids_read().
|
|
* The processed entries are moved back to cmd->use_devices at the
|
|
* end of this function.
|
|
*/
|
|
dm_list_splice(&use_new, &cmd->use_devices);
|
|
if (!device_ids_read(cmd))
|
|
log_debug("Failed to read the devices file.");
|
|
dm_list_splice(&use_old, &cmd->use_devices);
|
|
dm_list_init(&cmd->use_devices);
|
|
|
|
/*
|
|
* Check if system identifier is changed.
|
|
*/
|
|
|
|
if (cmd->device_ids_refresh_trigger) {
|
|
int include_product_uuid = 0;
|
|
int include_hostname = 0;
|
|
|
|
if (cmd->product_uuid && cmd->device_ids_check_product_uuid) {
|
|
include_product_uuid = 1;
|
|
if (devices_file_product_uuid_orig[0] &&
|
|
strcmp(cmd->product_uuid, devices_file_product_uuid_orig))
|
|
log_print_unless_silent("PRODUCT_UUID=%s (old %s): update",
|
|
cmd->product_uuid, devices_file_product_uuid_orig);
|
|
else if (!devices_file_product_uuid_orig[0])
|
|
log_print_unless_silent("PRODUCT_UUID=%s: add", cmd->product_uuid);
|
|
}
|
|
if (!include_product_uuid && devices_file_product_uuid_orig[0])
|
|
log_print_unless_silent("PRODUCT_UUID=%s: remove", devices_file_product_uuid_orig);
|
|
|
|
/* hostname is only updated or added if product_uuid is not included */
|
|
if (cmd->hostname && cmd->device_ids_check_hostname && !include_product_uuid) {
|
|
include_hostname = 1;
|
|
if (devices_file_hostname_orig[0] &&
|
|
strcmp(cmd->hostname, devices_file_hostname_orig))
|
|
log_print_unless_silent("HOSTNAME=%s (old %s): update",
|
|
cmd->hostname, devices_file_hostname_orig);
|
|
else if (!devices_file_hostname_orig[0])
|
|
log_print_unless_silent("HOSTNAME=%s: add", cmd->hostname);
|
|
}
|
|
if (!include_hostname && devices_file_hostname_orig[0])
|
|
log_print_unless_silent("HOSTNAME=%s: remove", devices_file_hostname_orig);
|
|
}
|
|
|
|
/*
|
|
* Check entries with proper id types.
|
|
*/
|
|
restart1:
|
|
dm_list_iterate_items(du_old, &use_old) {
|
|
if (du_old->idtype == DEV_ID_TYPE_DEVNAME)
|
|
continue;
|
|
dm_list_iterate_items(du_new, &use_new) {
|
|
if (du_new->idtype == DEV_ID_TYPE_DEVNAME)
|
|
continue;
|
|
|
|
if (du_old->idtype != du_new->idtype)
|
|
continue;
|
|
|
|
if (!du_old->idname || !du_new->idname)
|
|
continue;
|
|
|
|
if (du_old->part != du_new->part)
|
|
continue;
|
|
|
|
if (!strcmp(du_old->idname, du_new->idname)) {
|
|
part = _part_str(du_old);
|
|
|
|
/*
|
|
* Old and new entries match based on device id.
|
|
* Possible differences between old and new:
|
|
* DEVNAME mismatch can be common.
|
|
* PVID mismatch is not common, but can
|
|
* happen from something like dd of one
|
|
* PV to another.
|
|
*/
|
|
|
|
if (!du_new->dev) {
|
|
/* We can't know the new pvid and devname without a device. */
|
|
log_print_unless_silent("IDTYPE=%s IDNAME=%s DEVNAME=%s PVID=%s%s: device not found",
|
|
idtype_to_str(du_old->idtype),
|
|
du_old->idname,
|
|
du_old->devname ?: "none",
|
|
du_old->pvid ?: "none",
|
|
part);
|
|
goto next1;
|
|
}
|
|
|
|
pvid_same = (du_old->pvid && du_new->pvid && !strcmp(du_old->pvid, du_new->pvid)) || (!du_old->pvid && !du_new->pvid);
|
|
devname_same = (du_old->devname && du_new->devname && !strcmp(du_old->devname, du_new->devname)) || (!du_old->devname && !du_new->devname);
|
|
|
|
if (pvid_same && devname_same) {
|
|
log_verbose("IDTYPE=%s IDNAME=%s DEVNAME=%s PVID=%s%s: no change",
|
|
idtype_to_str(du_new->idtype),
|
|
du_new->idname,
|
|
du_new->devname ?: "none",
|
|
du_new->pvid ?: "none",
|
|
part);
|
|
|
|
} else if (!pvid_same && !devname_same) {
|
|
log_print_unless_silent("IDTYPE=%s IDNAME=%s DEVNAME=%s (old %s) PVID=%s (old %s)%s: update",
|
|
idtype_to_str(du_new->idtype),
|
|
du_new->idname,
|
|
du_new->devname ?: "none",
|
|
du_old->devname ?: "none",
|
|
du_new->pvid ?: "none",
|
|
du_old->pvid ?: "none",
|
|
part);
|
|
|
|
} else if (pvid_same && !devname_same) {
|
|
log_print_unless_silent("IDTYPE=%s IDNAME=%s DEVNAME=%s (old %s) PVID=%s%s: update",
|
|
idtype_to_str(du_new->idtype),
|
|
du_new->idname,
|
|
du_new->devname ?: "none",
|
|
du_old->devname ?: "none",
|
|
du_new->pvid ?: "none",
|
|
part);
|
|
|
|
} else if (!pvid_same && devname_same) {
|
|
log_print_unless_silent("IDTYPE=%s IDNAME=%s DEVNAME=%s PVID=%s (old %s)%s: update",
|
|
idtype_to_str(du_new->idtype),
|
|
du_new->idname,
|
|
du_new->devname ?: "none",
|
|
du_new->pvid ?: "none",
|
|
du_old->pvid ?: "none",
|
|
part);
|
|
}
|
|
next1:
|
|
dm_list_del(&du_old->list);
|
|
dm_list_del(&du_new->list);
|
|
dm_list_add(&done_old, &du_old->list);
|
|
dm_list_add(&done_new, &du_new->list);
|
|
goto restart1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check entries with devname id type.
|
|
*/
|
|
restart2:
|
|
dm_list_iterate_items(du_old, &use_old) {
|
|
if (du_old->idtype != DEV_ID_TYPE_DEVNAME)
|
|
continue;
|
|
dm_list_iterate_items(du_new, &use_new) {
|
|
if (du_new->idtype != DEV_ID_TYPE_DEVNAME)
|
|
continue;
|
|
|
|
if (!du_old->pvid || !du_new->pvid)
|
|
continue;
|
|
|
|
if (du_old->part != du_new->part)
|
|
continue;
|
|
|
|
if (!memcmp(du_old->pvid, du_new->pvid, strlen(du_old->pvid))) {
|
|
part = _part_str(du_old);
|
|
|
|
/*
|
|
* Old and new entries match based on PVID.
|
|
* IDNAME and DEVNAME might not match.
|
|
*/
|
|
|
|
if (!du_new->dev) {
|
|
/* We can't know the new idname and devname without a device. */
|
|
log_print_unless_silent("IDTYPE=%s IDNAME=%s DEVNAME=%s PVID=%s%s: device not found",
|
|
idtype_to_str(du_old->idtype),
|
|
du_old->idname ?: "none",
|
|
du_old->devname ?: "none",
|
|
du_old->pvid,
|
|
part);
|
|
goto next2;
|
|
}
|
|
|
|
idname_same = (du_old->idname && du_new->idname && !strcmp(du_old->idname, du_new->idname)) || (!du_old->idname && !du_new->idname);
|
|
devname_same = (du_old->devname && du_new->devname && !strcmp(du_old->devname, du_new->devname)) || (!du_old->devname && !du_new->devname);
|
|
|
|
if (idname_same && devname_same) {
|
|
log_verbose("IDTYPE=%s IDNAME=%s DEVNAME=%s PVID=%s%s: no change",
|
|
idtype_to_str(du_new->idtype),
|
|
du_new->idname ?: "none",
|
|
du_new->devname ?: "none",
|
|
du_new->pvid,
|
|
part);
|
|
|
|
} else if (!idname_same && !devname_same) {
|
|
log_print_unless_silent("IDTYPE=%s IDNAME=%s (old %s) DEVNAME=%s (old %s) PVID=%s%s: update",
|
|
idtype_to_str(du_new->idtype),
|
|
du_new->idname ?: "none",
|
|
du_old->idname ?: "none",
|
|
du_new->devname ?: "none",
|
|
du_old->devname ?: "none",
|
|
du_new->pvid,
|
|
part);
|
|
|
|
} else if (idname_same && !devname_same) {
|
|
log_print_unless_silent("IDTYPE=%s IDNAME=%s DEVNAME=%s (old %s) PVID=%s%s: update",
|
|
idtype_to_str(du_new->idtype),
|
|
du_new->idname ?: "none",
|
|
du_new->devname ?: "none",
|
|
du_old->devname ?: "none",
|
|
du_new->pvid,
|
|
part);
|
|
|
|
} else if (!idname_same && devname_same) {
|
|
log_print_unless_silent("IDTYPE=%s IDNAME=%s (old %s) DEVNAME=%s PVID=%s%s: update",
|
|
idtype_to_str(du_new->idtype),
|
|
du_new->idname ?: "none",
|
|
du_old->idname ?: "none",
|
|
du_new->devname ?: "none",
|
|
du_new->pvid,
|
|
part);
|
|
}
|
|
next2:
|
|
dm_list_del(&du_old->list);
|
|
dm_list_del(&du_new->list);
|
|
dm_list_add(&done_old, &du_old->list);
|
|
dm_list_add(&done_new, &du_new->list);
|
|
goto restart2;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check entries with new IDTYPE (refresh can do this)
|
|
* Compare PVIDs.
|
|
*/
|
|
restart3:
|
|
dm_list_iterate_items(du_old, &use_old) {
|
|
dm_list_iterate_items(du_new, &use_new) {
|
|
|
|
if (!du_old->pvid || !du_new->pvid)
|
|
continue;
|
|
|
|
if (du_old->part != du_new->part)
|
|
continue;
|
|
|
|
if (!memcmp(du_old->pvid, du_new->pvid, strlen(du_old->pvid))) {
|
|
part = _part_str(du_old);
|
|
|
|
/*
|
|
* Old and new entries match based on PVID.
|
|
* IDTYPE, IDNAME, DEVNAME might not match.
|
|
*/
|
|
|
|
if (!du_new->dev) {
|
|
/* could this happen? */
|
|
log_print_unless_silent("IDTYPE=%s (%s) IDNAME=%s (%s) DEVNAME=%s (%s) PVID=%s%s: device not found",
|
|
idtype_to_str(du_new->idtype),
|
|
idtype_to_str(du_old->idtype),
|
|
du_new->idname ?: "none",
|
|
du_old->idname ?: "none",
|
|
du_new->devname ?: "none",
|
|
du_old->devname ?: "none",
|
|
du_new->pvid,
|
|
part);
|
|
goto next3;
|
|
}
|
|
|
|
idtype_same = (du_old->idtype == du_new->idtype);
|
|
idname_same = (du_old->idname && du_new->idname && !strcmp(du_old->idname, du_new->idname)) || (!du_old->idname && !du_new->idname);
|
|
devname_same = (du_old->devname && du_new->devname && !strcmp(du_old->devname, du_new->devname)) || (!du_old->devname && !du_new->devname);
|
|
|
|
if (idtype_same && idname_same && devname_same) {
|
|
/* this case will probably be caught earlier */
|
|
log_verbose("IDTYPE=%s IDNAME=%s DEVNAME=%s PVID=%s%s: no change",
|
|
idtype_to_str(du_new->idtype),
|
|
du_new->idname ?: "none",
|
|
du_new->devname ?: "none",
|
|
du_new->pvid,
|
|
part);
|
|
|
|
} else if (!idtype_same && !idname_same && !devname_same) {
|
|
log_print_unless_silent("IDTYPE=%s (old %s) IDNAME=%s (old %s) DEVNAME=%s (old %s) PVID=%s%s: update",
|
|
idtype_to_str(du_new->idtype),
|
|
idtype_to_str(du_old->idtype),
|
|
du_new->idname ?: "none",
|
|
du_old->idname ?: "none",
|
|
du_new->devname ?: "none",
|
|
du_old->devname ?: "none",
|
|
du_new->pvid,
|
|
part);
|
|
|
|
} else if (!idtype_same && !idname_same && devname_same) {
|
|
log_print_unless_silent("IDTYPE=%s (old %s) IDNAME=%s (old %s) DEVNAME=%s PVID=%s%s: update",
|
|
idtype_to_str(du_new->idtype),
|
|
idtype_to_str(du_old->idtype),
|
|
du_new->idname ?: "none",
|
|
du_old->idname ?: "none",
|
|
du_new->devname ?: "none",
|
|
du_new->pvid,
|
|
part);
|
|
|
|
} else if (idtype_same && !idname_same && !devname_same) {
|
|
log_print_unless_silent("IDTYPE=%s IDNAME=%s (old %s) DEVNAME=%s (old %s) PVID=%s%s: update",
|
|
idtype_to_str(du_new->idtype),
|
|
du_new->idname ?: "none",
|
|
du_old->idname ?: "none",
|
|
du_new->devname ?: "none",
|
|
du_old->devname ?: "none",
|
|
du_new->pvid,
|
|
part);
|
|
|
|
} else if (idtype_same && !idname_same && devname_same) {
|
|
log_print_unless_silent("IDTYPE=%s IDNAME=%s (old %s) DEVNAME=%s PVID=%s%s: update",
|
|
idtype_to_str(du_new->idtype),
|
|
du_new->idname ?: "none",
|
|
du_old->idname ?: "none",
|
|
du_new->devname ?: "none",
|
|
du_new->pvid,
|
|
part);
|
|
|
|
} else if (idtype_same && idname_same && !devname_same) {
|
|
log_print_unless_silent("IDTYPE=%s IDNAME=%s DEVNAME=%s (old %s) PVID=%s%s: update",
|
|
idtype_to_str(du_new->idtype),
|
|
du_new->idname ?: "none",
|
|
du_new->devname ?: "none",
|
|
du_old->devname ?: "none",
|
|
du_new->pvid,
|
|
part);
|
|
}
|
|
next3:
|
|
dm_list_del(&du_old->list);
|
|
dm_list_del(&du_new->list);
|
|
dm_list_add(&done_old, &du_old->list);
|
|
dm_list_add(&done_new, &du_new->list);
|
|
goto restart3;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Handle old and new entries that remain and are identical.
|
|
* This covers entries that do not have enough valid fields
|
|
* set to definitively identify a PV.
|
|
*/
|
|
restart4:
|
|
dm_list_iterate_items(du_old, &use_old) {
|
|
dm_list_iterate_items(du_new, &use_new) {
|
|
idtype_same = (du_old->idtype == du_new->idtype);
|
|
idname_same = (du_old->idname && du_new->idname && !strcmp(du_old->idname, du_new->idname)) || (!du_old->idname && !du_new->idname);
|
|
devname_same = (du_old->devname && du_new->devname && !strcmp(du_old->devname, du_new->devname)) || (!du_old->devname && !du_new->devname);
|
|
pvid_same = (du_old->pvid && du_new->pvid && !strcmp(du_old->pvid, du_new->pvid)) || (!du_old->pvid && !du_new->pvid);
|
|
part_same = (du_old->part == du_new->part);
|
|
|
|
if (idtype_same && idname_same && devname_same && pvid_same && part_same) {
|
|
part = _part_str(du_old);
|
|
|
|
log_print_unless_silent("IDTYPE=%s IDNAME=%s DEVNAME=%s PVID=%s%s: indeterminate",
|
|
idtype_to_str(du_new->idtype),
|
|
du_new->idname ?: "none",
|
|
du_new->devname ?: "none",
|
|
du_new->pvid ?: "none",
|
|
part);
|
|
|
|
dm_list_del(&du_old->list);
|
|
dm_list_del(&du_new->list);
|
|
dm_list_add(&done_old, &du_old->list);
|
|
dm_list_add(&done_new, &du_new->list);
|
|
goto restart4;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Entries remaining on old/new lists can't be directly
|
|
* correlated by loops above.
|
|
* Just print remaining old entries as being removed and
|
|
* remaing new entries as being added.
|
|
* If we find specific cases that reach here, we may
|
|
* want to add loops above to detect and print them
|
|
* more specifically.
|
|
*/
|
|
|
|
dm_list_iterate_items(du_old, &use_old) {
|
|
part = _part_str(du_old);
|
|
|
|
log_print_unless_silent("IDTYPE=%s IDNAME=%s DEVNAME=%s PVID=%s%s: old entry",
|
|
idtype_to_str(du_old->idtype),
|
|
du_old->idname ?: "none",
|
|
du_old->devname ?: "none",
|
|
du_old->pvid ?: "none",
|
|
part);
|
|
}
|
|
|
|
dm_list_iterate_items(du_new, &use_new) {
|
|
part = _part_str(du_new);
|
|
|
|
log_print_unless_silent("IDTYPE=%s IDNAME=%s DEVNAME=%s PVID=%s%s: new entry",
|
|
idtype_to_str(du_new->idtype),
|
|
du_new->idname ?: "none",
|
|
du_new->devname ?: "none",
|
|
du_new->pvid ?: "none",
|
|
part);
|
|
}
|
|
|
|
/* Restore cmd->use_devices list */
|
|
|
|
dm_list_splice(&cmd->use_devices, &use_new);
|
|
dm_list_splice(&cmd->use_devices, &done_new);
|
|
}
|
|
|
|
int lvmdevices(struct cmd_context *cmd, int argc, char **argv)
|
|
{
|
|
struct dm_list search_pvids;
|
|
struct dm_list found_devs;
|
|
struct dm_list scan_devs;
|
|
struct device_id_list *dil;
|
|
struct device_list *devl;
|
|
struct device *dev;
|
|
struct dev_use *du, *du2;
|
|
const char *deviceidtype;
|
|
|
|
dm_list_init(&search_pvids);
|
|
dm_list_init(&found_devs);
|
|
dm_list_init(&scan_devs);
|
|
|
|
if (!setup_devices_file(cmd))
|
|
return ECMD_FAILED;
|
|
|
|
if (!cmd->enable_devices_file) {
|
|
log_error("Devices file not enabled.");
|
|
return ECMD_FAILED;
|
|
}
|
|
|
|
if (arg_is_set(cmd, update_ARG) ||
|
|
arg_is_set(cmd, adddev_ARG) || arg_is_set(cmd, deldev_ARG) ||
|
|
arg_is_set(cmd, addpvid_ARG) || arg_is_set(cmd, delpvid_ARG)) {
|
|
if (!lock_devices_file(cmd, LOCK_EX)) {
|
|
log_error("Failed to lock the devices file to create.");
|
|
return ECMD_FAILED;
|
|
}
|
|
if (!devices_file_exists(cmd)) {
|
|
if (!devices_file_touch(cmd)) {
|
|
log_error("Failed to create the devices file.");
|
|
return ECMD_FAILED;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* The hint file is associated with the default/system devices file,
|
|
* so don't clear hints when using a different --devicesfile.
|
|
*/
|
|
if (!cmd->devicesfile)
|
|
clear_hint_file(cmd);
|
|
} else {
|
|
if (!lock_devices_file(cmd, LOCK_SH)) {
|
|
log_error("Failed to lock the devices file.");
|
|
return ECMD_FAILED;
|
|
}
|
|
if (!devices_file_exists(cmd)) {
|
|
log_error("Devices file does not exist.");
|
|
return ECMD_FAILED;
|
|
}
|
|
}
|
|
|
|
if (!device_ids_read(cmd)) {
|
|
log_error("Failed to read the devices file.");
|
|
return ECMD_FAILED;
|
|
}
|
|
|
|
prepare_open_file_limit(cmd, dm_list_size(&cmd->use_devices));
|
|
|
|
dev_cache_scan(cmd);
|
|
device_ids_match(cmd);
|
|
|
|
if (arg_is_set(cmd, check_ARG) || arg_is_set(cmd, update_ARG)) {
|
|
int update_set = arg_is_set(cmd, update_ARG);
|
|
int update_needed = 0;
|
|
|
|
unlink_searched_devnames(cmd);
|
|
|
|
label_scan_setup_bcache();
|
|
|
|
dm_list_iterate_items(du, &cmd->use_devices) {
|
|
if (!du->dev)
|
|
continue;
|
|
dev = du->dev;
|
|
|
|
if (!label_read_pvid(dev, NULL))
|
|
continue;
|
|
|
|
/*
|
|
* label_read_pvid has read the first 4K of the device
|
|
* so these filters should not for the most part need
|
|
* to do any further reading of the device.
|
|
*
|
|
* We run the filters here for the first time in the
|
|
* check|update command. device_ids_validate() then
|
|
* checks the result of this filtering (by checking the
|
|
* "persistent" filter explicitly), and prints a warning
|
|
* if a devices file entry does not pass the filters.
|
|
* The !passes_filter here is log_debug instead of log_warn
|
|
* to avoid repeating the same message as device_ids_validate.
|
|
* (We could also print the warning here and then pass a
|
|
* parameter to suppress the warning in device_ids_validate.)
|
|
*/
|
|
log_debug("Checking filters with data for %s", dev_name(dev));
|
|
if (!cmd->filter->passes_filter(cmd, cmd->filter, dev, NULL)) {
|
|
log_debug("filter result: %s in devices file is excluded by filter: %s.",
|
|
dev_name(dev), dev_filtered_reason(dev));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check that the pvid read from the lvm label matches the pvid
|
|
* for this devices file entry.
|
|
*/
|
|
device_ids_validate(cmd, NULL, 0, 1, &update_needed);
|
|
|
|
/*
|
|
* Remove multipath components.
|
|
* Add multipath devs that had components listed.
|
|
*/
|
|
dm_list_iterate_items_safe(du, du2, &cmd->use_devices) {
|
|
dev_t mpath_devno;
|
|
struct device *mpath_dev;
|
|
|
|
if (!du->dev)
|
|
continue;
|
|
dev = du->dev;
|
|
|
|
if (!(dev->filtered_flags & DEV_FILTERED_MPATH_COMPONENT))
|
|
continue;
|
|
|
|
/* redundant given the flag check, but used to get devno */
|
|
if (!dev_is_mpath_component(cmd, dev, &mpath_devno))
|
|
continue;
|
|
|
|
log_print_unless_silent("IDTYPE=%s IDNAME=%s DEVNAME=%s PVID=%s%s: remove multipath component",
|
|
idtype_to_str(du->idtype),
|
|
du->idname ?: "none",
|
|
du->devname ?: "none",
|
|
du->pvid ?: "none",
|
|
_part_str(du));
|
|
|
|
update_needed = 1;
|
|
|
|
if (update_set)
|
|
dm_list_del(&du->list);
|
|
|
|
if (!(mpath_dev = dev_cache_get_by_devt(cmd, mpath_devno)))
|
|
continue;
|
|
|
|
if (!get_du_for_dev(cmd, mpath_dev)) {
|
|
if (update_set) {
|
|
log_print("Adding multipath device %s for multipath component %s.",
|
|
dev_name(mpath_dev), dev_name(du->dev));
|
|
if (!device_id_add(cmd, mpath_dev, dev->pvid, NULL, NULL, 0))
|
|
stack;
|
|
} else {
|
|
log_print_unless_silent("Missing multipath device %s for multipath component %s.",
|
|
dev_name(mpath_dev), dev_name(du->dev));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!dm_list_empty(&cmd->device_ids_check_serial)) {
|
|
device_ids_check_serial(cmd, &scan_devs, 1, &update_needed);
|
|
/* device_ids_check_serial has done label_read_pvid on the scan_devs. */
|
|
}
|
|
|
|
/*
|
|
* Find devname entries that have moved to a renamed device.
|
|
* If --refresh is set, then also look for missing PVIDs on
|
|
* devices with new device ids of any type, e.g. a PVID that's
|
|
* moved to a new WWID.
|
|
*/
|
|
cmd->search_for_devnames = "all";
|
|
device_ids_search(cmd, &found_devs, arg_is_set(cmd, refresh_ARG), 1, &update_needed);
|
|
|
|
_print_check(cmd);
|
|
|
|
dm_list_iterate_items(du, &cmd->use_devices) {
|
|
if (du->dev)
|
|
label_scan_invalidate(du->dev);
|
|
}
|
|
|
|
if (arg_is_set(cmd, delnotfound_ARG)) {
|
|
dm_list_iterate_items_safe(du, du2, &cmd->use_devices) {
|
|
if (!du->dev) {
|
|
log_print_unless_silent("IDTYPE=%s IDNAME=%s DEVNAME=%s PVID=%s%s: delete",
|
|
idtype_to_str(du->idtype),
|
|
du->idname ?: "none",
|
|
du->devname ?: "none",
|
|
du->pvid ?: "none",
|
|
_part_str(du));
|
|
dm_list_del(&du->list);
|
|
free_du(du);
|
|
update_needed = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (arg_is_set(cmd, update_ARG)) {
|
|
if (update_needed || !dm_list_empty(&found_devs)) {
|
|
if (!device_ids_write(cmd))
|
|
goto_bad;
|
|
log_print("Updated devices file to version %s", devices_file_version());
|
|
} else {
|
|
log_print("No update for devices file is needed.");
|
|
}
|
|
} else {
|
|
/*
|
|
* --check exits with an error if the devices file
|
|
* needs updates, i.e. running --update would make
|
|
* changes.
|
|
*/
|
|
if (update_needed) {
|
|
log_error("Updates needed for devices file.");
|
|
goto bad;
|
|
}
|
|
}
|
|
goto out;
|
|
}
|
|
|
|
if (arg_is_set(cmd, adddev_ARG)) {
|
|
const char *devname;
|
|
|
|
if (!(devname = arg_str_value(cmd, adddev_ARG, NULL)))
|
|
goto_bad;
|
|
|
|
/*
|
|
* addev will add a device to devices_file even if that device
|
|
* is excluded by filters.
|
|
*/
|
|
|
|
/*
|
|
* No filter applied here (only the non-data filters would
|
|
* be applied since we haven't read the device yet.
|
|
*/
|
|
if (!(dev = dev_cache_get(cmd, devname, NULL))) {
|
|
log_error("No device found for %s.", devname);
|
|
goto bad;
|
|
}
|
|
|
|
/*
|
|
* reads pvid from dev header, sets dev->pvid.
|
|
* (it's ok if the device is not a PV and has no PVID)
|
|
*/
|
|
label_scan_setup_bcache();
|
|
if (!label_read_pvid(dev, NULL)) {
|
|
log_error("Failed to read %s.", devname);
|
|
goto bad;
|
|
}
|
|
|
|
/*
|
|
* Allow filtered devices to be added to devices_file, but
|
|
* check if it's excluded by filters to print a warning.
|
|
* Since label_read_pvid has read the first 4K of the device,
|
|
* the filters should not for the most part need to do any further
|
|
* reading of the device.
|
|
*
|
|
* (This is the first time filters are being run, so we do
|
|
* not need to wipe filters of any previous result that was
|
|
* based on filter_deviceid_skip=0.)
|
|
*/
|
|
cmd->filter_deviceid_skip = 1;
|
|
|
|
if (!cmd->filter->passes_filter(cmd, cmd->filter, dev, NULL)) {
|
|
log_warn("WARNING: adding device %s that is excluded: %s.",
|
|
dev_name(dev), dev_filtered_reason(dev));
|
|
}
|
|
|
|
/* also allow deviceid_ARG ? */
|
|
deviceidtype = arg_str_value(cmd, deviceidtype_ARG, NULL);
|
|
|
|
if (!device_id_add(cmd, dev, dev->pvid, deviceidtype, NULL, 1))
|
|
goto_bad;
|
|
if (!device_ids_write(cmd))
|
|
goto_bad;
|
|
goto out;
|
|
}
|
|
|
|
if (arg_is_set(cmd, addpvid_ARG)) {
|
|
struct id id;
|
|
char pvid[ID_LEN+1] = { 0 };
|
|
const char *pvid_arg;
|
|
|
|
label_scan_setup_bcache();
|
|
|
|
/*
|
|
* Iterate through all devs on the system, reading the
|
|
* pvid of each to check if it has this pvid.
|
|
* Devices that are excluded by no-data filters will not
|
|
* be checked for the PVID.
|
|
* addpvid will not add a device to devices_file if it's
|
|
* excluded by filters.
|
|
*/
|
|
|
|
pvid_arg = arg_str_value(cmd, addpvid_ARG, NULL);
|
|
if (!id_read_format_try(&id, pvid_arg)) {
|
|
log_error("Invalid PVID.");
|
|
goto bad;
|
|
}
|
|
memcpy(pvid, &id.uuid, ID_LEN);
|
|
|
|
if ((du = get_du_for_pvid(cmd, pvid))) {
|
|
log_error("PVID already exists in devices file for %s.", dev_name(du->dev));
|
|
goto bad;
|
|
}
|
|
|
|
if (!(dil = dm_pool_zalloc(cmd->mem, sizeof(*dil))))
|
|
goto_bad;
|
|
memcpy(dil->pvid, &pvid, ID_LEN);
|
|
dm_list_add(&search_pvids, &dil->list);
|
|
|
|
_search_devs_for_pvids(cmd, &search_pvids, &found_devs);
|
|
|
|
if (dm_list_empty(&found_devs)) {
|
|
log_error("PVID %s not found on any devices.", pvid);
|
|
goto bad;
|
|
}
|
|
dm_list_iterate_items(devl, &found_devs) {
|
|
deviceidtype = arg_str_value(cmd, deviceidtype_ARG, NULL);
|
|
if (!device_id_add(cmd, devl->dev, devl->dev->pvid, deviceidtype, NULL, 1))
|
|
goto_bad;
|
|
}
|
|
if (!device_ids_write(cmd))
|
|
goto_bad;
|
|
goto out;
|
|
}
|
|
|
|
if (arg_is_set(cmd, deldev_ARG) && !arg_is_set(cmd, deviceidtype_ARG)) {
|
|
const char *devname;
|
|
|
|
if (!(devname = arg_str_value(cmd, deldev_ARG, NULL)))
|
|
goto_bad;
|
|
|
|
if (strncmp(devname, "/dev/", 5))
|
|
log_warn("WARNING: to remove a device by device id, include --deviceidtype.");
|
|
|
|
/*
|
|
* No filter because we always want to allow removing a device
|
|
* by name from the devices file.
|
|
*/
|
|
if ((dev = dev_cache_get(cmd, devname, NULL))) {
|
|
/*
|
|
* dev_cache_scan uses sysfs to check if an LV is using each dev
|
|
* and sets this flag is so.
|
|
*/
|
|
if (dev_is_used_by_active_lv(cmd, dev, NULL, NULL, NULL, NULL)) {
|
|
if (!arg_count(cmd, yes_ARG) &&
|
|
yes_no_prompt("Device %s is used by an active LV, continue to remove? ", devname) == 'n') {
|
|
log_error("Device not removed.");
|
|
goto bad;
|
|
}
|
|
}
|
|
if ((du = get_du_for_dev(cmd, dev)))
|
|
goto dev_del;
|
|
}
|
|
|
|
if (!(du = get_du_for_devname(cmd, devname))) {
|
|
log_error("No devices file entry for %s.", devname);
|
|
goto bad;
|
|
}
|
|
dev_del:
|
|
dm_list_del(&du->list);
|
|
free_du(du);
|
|
device_ids_write(cmd);
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* By itself, --deldev <devname> specifies a device name to remove.
|
|
* With an id type specified, --deldev specifies a device id to remove:
|
|
* --deldev <idname> --deviceidtype <idtype>
|
|
*/
|
|
if (arg_is_set(cmd, deldev_ARG) && arg_is_set(cmd, deviceidtype_ARG)) {
|
|
const char *idtype_str = arg_str_value(cmd, deviceidtype_ARG, NULL);
|
|
const char *idname = arg_str_value(cmd, deldev_ARG, NULL);
|
|
int idtype;
|
|
|
|
if (!idtype_str || !idname || !strlen(idname) || !strlen(idtype_str))
|
|
goto_bad;
|
|
|
|
if (!(idtype = idtype_from_str(idtype_str))) {
|
|
log_error("Unknown device_id type.");
|
|
goto_bad;
|
|
}
|
|
|
|
if (!strncmp(idname, "/dev/", 5))
|
|
log_warn("WARNING: to remove a device by name, do not include --deviceidtype.");
|
|
|
|
if (!(du = get_du_for_device_id(cmd, idtype, idname))) {
|
|
log_error("No devices file entry with device id %s %s.", idtype_str, idname);
|
|
goto_bad;
|
|
}
|
|
|
|
dev = du->dev;
|
|
|
|
if (dev && dev_is_used_by_active_lv(cmd, dev, NULL, NULL, NULL, NULL)) {
|
|
if (!arg_count(cmd, yes_ARG) &&
|
|
yes_no_prompt("Device %s is used by an active LV, continue to remove? ", dev_name(dev)) == 'n') {
|
|
log_error("Device not removed.");
|
|
goto bad;
|
|
}
|
|
}
|
|
|
|
dm_list_del(&du->list);
|
|
free_du(du);
|
|
device_ids_write(cmd);
|
|
goto out;
|
|
}
|
|
|
|
if (arg_is_set(cmd, delpvid_ARG)) {
|
|
struct id id;
|
|
char pvid[ID_LEN+1] = { 0 };
|
|
const char *pvid_arg;
|
|
|
|
pvid_arg = arg_str_value(cmd, delpvid_ARG, NULL);
|
|
if (!id_read_format_try(&id, pvid_arg)) {
|
|
log_error("Invalid PVID.");
|
|
goto bad;
|
|
}
|
|
memcpy(pvid, &id.uuid, ID_LEN);
|
|
|
|
if (!(du = get_du_for_pvid(cmd, pvid))) {
|
|
log_error("PVID not found in devices file.");
|
|
goto bad;
|
|
}
|
|
|
|
dm_list_del(&du->list);
|
|
|
|
if ((du2 = get_du_for_pvid(cmd, pvid))) {
|
|
log_error("Multiple devices file entries for PVID %s (%s %s), remove by device name.",
|
|
pvid, du->devname, du2->devname);
|
|
goto bad;
|
|
}
|
|
|
|
if (du->devname && (du->devname[0] != '.')) {
|
|
if ((dev = dev_cache_get(cmd, du->devname, NULL)) &&
|
|
dev_is_used_by_active_lv(cmd, dev, NULL, NULL, NULL, NULL)) {
|
|
if (!arg_count(cmd, yes_ARG) &&
|
|
yes_no_prompt("Device %s is used by an active LV, continue to remove? ", du->devname) == 'n') {
|
|
log_error("Device not removed.");
|
|
goto bad;
|
|
}
|
|
}
|
|
}
|
|
|
|
free_du(du);
|
|
device_ids_write(cmd);
|
|
goto out;
|
|
}
|
|
|
|
/* If no options, print use_devices list */
|
|
|
|
dm_list_iterate_items(du, &cmd->use_devices) {
|
|
log_print("Device %s IDTYPE=%s IDNAME=%s DEVNAME=%s PVID=%s%s",
|
|
du->dev ? dev_name(du->dev) : "none",
|
|
du->idtype ? idtype_to_str(du->idtype) : "none",
|
|
du->idname ? du->idname : "none",
|
|
du->devname ? du->devname : "none",
|
|
du->pvid ? (char *)du->pvid : "none",
|
|
_part_str(du));
|
|
}
|
|
|
|
out:
|
|
return ECMD_PROCESSED;
|
|
|
|
bad:
|
|
return ECMD_FAILED;
|
|
}
|
|
|