diff options
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/Makefile.in | 2 | ||||
| -rw-r--r-- | tools/args.h | 27 | ||||
| -rw-r--r-- | tools/command-lines.in | 44 | ||||
| -rw-r--r-- | tools/commands.h | 8 | ||||
| -rw-r--r-- | tools/lvmcmdline.c | 20 | ||||
| -rw-r--r-- | tools/lvmdevices.c | 339 | ||||
| -rw-r--r-- | tools/polldaemon.c | 9 | ||||
| -rw-r--r-- | tools/pvck.c | 11 | ||||
| -rw-r--r-- | tools/pvcreate.c | 2 | ||||
| -rw-r--r-- | tools/pvscan.c | 13 | ||||
| -rw-r--r-- | tools/toollib.c | 64 | ||||
| -rw-r--r-- | tools/tools.h | 1 | ||||
| -rw-r--r-- | tools/vgcreate.c | 4 | ||||
| -rw-r--r-- | tools/vgextend.c | 4 | ||||
| -rw-r--r-- | tools/vgimportclone.c | 404 | ||||
| -rw-r--r-- | tools/vgimportdevices.c | 210 |
16 files changed, 1026 insertions, 136 deletions
diff --git a/tools/Makefile.in b/tools/Makefile.in index 2620daa17..54d67262d 100644 --- a/tools/Makefile.in +++ b/tools/Makefile.in @@ -27,6 +27,7 @@ SOURCES =\ lvdisplay.c \ lvextend.c \ lvmcmdline.c \ + lvmdevices.c \ lvmdiskscan.c \ lvpoll.c \ lvreduce.c \ @@ -58,6 +59,7 @@ SOURCES =\ vgextend.c \ vgimport.c \ vgimportclone.c \ + vgimportdevices.c \ vgmerge.c \ vgmknodes.c \ vgreduce.c \ diff --git a/tools/args.h b/tools/args.h index 5fae21abb..6948c5e27 100644 --- a/tools/args.h +++ b/tools/args.h @@ -44,6 +44,15 @@ arg(addtag_ARG, '\0', "addtag", tag_VAL, ARG_GROUPABLE, 0, "Adds a tag to a PV, VG or LV. This option can be repeated to add\n" "multiple tags at once. See \\fBlvm\\fP(8) for information about tags.\n") +arg(adddev_ARG, '\0', "adddev", pv_VAL, 0, 0, + "Add a device to the devices file.\n") +arg(deldev_ARG, '\0', "deldev", pv_VAL, 0, 0, + "Remove a device from the devices file.\n") +arg(addpvid_ARG, '\0', "addpvid", string_VAL, 0, 0, + "Find a device with the PVID and add the device to the devices file.\n") +arg(delpvid_ARG, '\0', "delpvid", string_VAL, 0, 0, + "Remove a device with the PVID from the devices file.\n") + arg(aligned_ARG, '\0', "aligned", 0, 0, 0, "Use with --separator to align the output columns\n") @@ -126,6 +135,9 @@ arg(cachepool_ARG, '\0', "cachepool", lv_VAL, 0, 0, arg(cachevol_ARG, '\0', "cachevol", lv_VAL, 0, 0, "The name of a cache volume.\n") +arg(check_ARG, '\0', "check", 0, 0, 0, + "Check the content of the devices file.\n") + arg(commandprofile_ARG, '\0', "commandprofile", string_VAL, 0, 0, "The command profile to use for command configuration.\n" "See \\fBlvm.conf\\fP(5) for more information about profiles.\n") @@ -199,6 +211,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" @@ -274,6 +295,9 @@ arg(ignoreunsupported_ARG, '\0', "ignoreunsupported", 0, 0, 0, "and \\fBdiff\\fP types include unsupported settings in their output by default,\n" "all the other types ignore unsupported settings.\n") +arg(importdevices_ARG, '\0', "importdevices", 0, 0, 0, + "Add devices to the devices file.\n") + arg(labelsector_ARG, '\0', "labelsector", number_VAL, 0, 0, "By default the PV is labelled with an LVM2 identifier in its second\n" "sector (sector 1). This lets you use a different sector near the\n" @@ -769,6 +793,9 @@ arg(uncache_ARG, '\0', "uncache", 0, 0, 0, "Separates a cache pool from a cache LV, and deletes the unused cache pool LV.\n" "Before the separation, the cache is flushed. Also see --splitcache.\n") +arg(update_ARG, '\0', "update", 0, 0, 0, + "Update the content of the devices file.\n") + arg(cachepolicy_ARG, '\0', "cachepolicy", string_VAL, 0, 0, "Specifies the cache policy for a cache LV.\n" "See \\fBlvmcache\\fP(7) for more information.\n") diff --git a/tools/command-lines.in b/tools/command-lines.in index ed3d0413a..d0621d711 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 @@ -1349,6 +1349,31 @@ ID: lvmconfig_general --- +lvmdevices +ID: lvmdevices_list + +lvmdevices --check +ID: lvmdevices_check + +lvmdevices --update +ID: lvmdevices_update + +lvmdevices --adddev PV +OO: --deviceidtype String, --deviceid String +ID: lvmdevices_edit + +lvmdevices --deldev PV +ID: lvmdevices_edit + +lvmdevices --addpvid String +OO: --deviceidtype String, --deviceid String +ID: lvmdevices_edit + +lvmdevices --delpvid String +ID: lvmdevices_edit + +--- + lvreduce --size NSizeMB LV OO: --autobackup Bool, --force, --nofsck, --noudevsync, --reportformat ReportFmt, --resizefs @@ -1479,7 +1504,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 @@ -1729,11 +1755,23 @@ DESC: Import all VGs. --- vgimportclone PV ... -OO: --basevgname VG, --import +OO: --basevgname VG, --import, --importdevices ID: vgimportclone_general --- +vgimportdevices VG|Tag|Select ... +OO: --deviceidtype String, --select String, --foreign, --reportformat ReportFmt +ID: vgimportdevices_some +DESC: Add devices from specific VGs to the devices file. + +vgimportdevices --all +OO: --deviceidtype String, --foreign, --reportformat ReportFmt +ID: vgimportdevices_all +DESC: Add devices from all accessible VGs to the devices file. + +--- + vgmerge VG VG OO: --autobackup Bool, --list ID: vgmerge_general diff --git a/tools/commands.h b/tools/commands.h index c1670ae66..fefab193a 100644 --- a/tools/commands.h +++ b/tools/commands.h @@ -69,6 +69,10 @@ xx(lvmconfig, "Display and manipulate configuration information", PERMITTED_READ_ONLY | NO_METADATA_PROCESSING) +xx(lvmdevices, + "Manage the devices file listing devices for LVM to use", + 0) + xx(lvmdiskscan, "List devices that may be used as physical volumes", PERMITTED_READ_ONLY | ENABLE_ALL_DEVS | ALLOW_EXPORTED) @@ -207,6 +211,10 @@ xx(vgimportclone, "Import a VG from cloned PVs", ALLOW_EXPORTED) +xx(vgimportdevices, + "Add devices for a VG to the devices file.", + ALL_VGS_IS_DEFAULT | ALLOW_EXPORTED) + xx(vgmerge, "Merge volume groups", 0) diff --git a/tools/lvmcmdline.c b/tools/lvmcmdline.c index d87a8f053..eab1405e8 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" @@ -2420,7 +2421,7 @@ static int _get_current_settings(struct cmd_context *cmd) /* * enable_hints is set to 1 if any commands are using hints. - * use_hints is set to 1 if this command doesn't use the hints. + * use_hints is set to 0 if this command doesn't use the hints. * enable_hints=1 and use_hints=0 means that this command won't * use the hints, but it may invalidate the hints that are used * by other commands. @@ -2436,6 +2437,10 @@ static int _get_current_settings(struct cmd_context *cmd) else cmd->use_hints = 0; + /* The hints file is associated with the default/system devices file. */ + if (arg_is_set(cmd, devicesfile_ARG)) + cmd->use_hints = 0; + if ((hint_mode = find_config_tree_str(cmd, devices_hints_CFG, NULL))) { if (!strcmp(hint_mode, "none")) cmd->enable_hints = 0; @@ -2477,6 +2482,19 @@ 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)) { + const char *devices_file = arg_str_value(cmd, devicesfile_ARG, NULL); + if (devices_file && !strlen(devices_file)) { + cmd->devicesfile = ""; + } else if (!devices_file || !validate_name(devices_file)) { + log_error("Invalid devices file name."); + return EINVALID_CMD_LINE; + } else if (!(cmd->devicesfile = dm_pool_strdup(cmd->libmem, devices_file))) { + log_error("Failed to copy devices file name."); + return EINVALID_CMD_LINE; + } + } + /* * 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/lvmdevices.c b/tools/lvmdevices.c new file mode 100644 index 000000000..a82f4d990 --- /dev/null +++ b/tools/lvmdevices.c @@ -0,0 +1,339 @@ +/* + * 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/filters/filter.h" +#include "lib/device/device_id.h" + +int lvmdevices(struct cmd_context *cmd, int argc, char **argv) +{ + struct device *dev; + struct use_id *uid; + + 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; + } + dev_cache_scan(); + device_ids_match(cmd); + + if (arg_is_set(cmd, check_ARG)) { + /* For each cmd->use_device_ids: + reads pvid from header, sets dev->pvid and uid->pvid */ + label_scan_setup_bcache(); + device_ids_read_pvids(cmd); + goto out; + } + + if (arg_is_set(cmd, update_ARG)) { + /* For each cmd->use_device_ids: + reads pvid from header, sets dev->pvid and uid->pvid */ + label_scan_setup_bcache(); + device_ids_read_pvids(cmd); + + /* Any uid fields found/set/fixed will be written. */ + if (!device_ids_write(cmd)) + 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(); + device_id_read_pvid(cmd, dev); + + /* + * Allow filtered devices to be added to devices_file, but + * check if it's excluded by filters to print a warning. + * Since device_id_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; + cmd->filter_regex_with_devices_file = 1; + + if (!cmd->filter->passes_filter(cmd, cmd->filter, dev, NULL)) { + /* FIXME: print which filters */ + log_warn("WARNING: %s is currently excluded by filters.", dev_name(dev)); + } + + if (!device_id_add(cmd, dev, dev->pvid, + arg_str_value(cmd, deviceidtype_ARG, NULL), + arg_str_value(cmd, deviceid_ARG, NULL))) + goto_bad; + if (!device_ids_write(cmd)) + goto_bad; + goto out; + } + + if (arg_is_set(cmd, addpvid_ARG)) { + struct dev_iter *iter; + struct id id; + char pvid[ID_LEN+1] = { 0 }; + const char *pvid_arg; + struct device_list *devl, *safe; + struct dm_list devs; + + dm_list_init(&devs); + + /* + * 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 ((uid = get_uid_for_pvid(cmd, pvid))) { + log_error("PVID already exists in devices_file for %s.", dev_name(uid->dev)); + goto bad; + } + + /* + * 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))) + goto_bad; + while ((dev = dev_iter_get(cmd, iter))) { + if (!(devl = zalloc(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 + * (bcache is not set up yet, so any filter that requires + * reading the device will return EAGAIN.) The regex filter + * will be used and filter-deviceid not used. + */ + log_debug("Filtering devices without data"); + cmd->filter_deviceid_skip = 1; + cmd->filter_regex_with_devices_file = 1; + dm_list_iterate_items_safe(devl, safe, &devs) { + if (!cmd->filter->passes_filter(cmd, cmd->filter, devl->dev, NULL)) + dm_list_del(&devl->list); + } + + label_scan_setup_bcache(); + dev = NULL; + dm_list_iterate_items(devl, &devs) { + if (!device_id_read_pvid(cmd, devl->dev)) + continue; + if (!strcmp(devl->dev->pvid, pvid)) { + dev = devl->dev; + break; + } + } + + if (!dev) { + log_error("PVID %s not found on any devices.", pvid); + goto bad; + } + + /* + * 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 device with data"); + cmd->filter_deviceid_skip = 1; + cmd->filter_regex_with_devices_file = 1; + cmd->filter->wipe(cmd, cmd->filter, dev, NULL); + if (!cmd->filter->passes_filter(cmd, cmd->filter, dev, NULL)) { + /* FIXME: print which filters */ + log_error("PVID %s found on %s which is excluded by filters", + pvid, dev_name(dev)); + goto_bad; + } + + if (!device_id_add(cmd, dev, dev->pvid, NULL, NULL)) + goto_bad; + if (!device_ids_write(cmd)) + goto_bad; + goto out; + } + + if (arg_is_set(cmd, deldev_ARG)) { + const char *devname; + + if (!(devname = arg_str_value(cmd, deldev_ARG, NULL))) + goto_bad; + + /* we don't need to filter_deviceid_skip since we're + removing a dev from devices_file, that dev should + be in the devices_file and pass the filter */ + if (!(dev = dev_cache_get(cmd, devname, cmd->filter))) { + log_error("No device found for %s.", devname); + goto bad; + } + + /* dev_cache_scan uses sysfs to check if an LV is using each dev + and sets this flag is so. */ + if (dev->flags & DEV_USED_FOR_LV) { + 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 (!(uid = get_uid_for_dev(cmd, dev))) { + log_error("Device not found in devices_file."); + goto bad; + } + + dm_list_del(&uid->list); + free_uid(uid); + 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 (!(uid = get_uid_for_pvid(cmd, pvid))) { + log_error("PVID not found in devices_file."); + goto_bad; + } + + if (uid->devname && (uid->devname[0] != '.')) { + if ((dev = dev_cache_get(cmd, uid->devname, cmd->filter)) && + (dev->flags & DEV_USED_FOR_LV)) { + if (!arg_count(cmd, yes_ARG) && + yes_no_prompt("Device %s is used by an active LV, continue to remove? ", uid->devname) == 'n') { + log_error("Device not removed."); + goto bad; + } + } + } + + dm_list_del(&uid->list); + free_uid(uid); + device_ids_write(cmd); + goto out; + } + + /* If no options, print use_device_ids list */ + + dm_list_iterate_items(uid, &cmd->use_device_ids) { + char part_buf[64] = { 0 }; + + if (uid->part) + snprintf(part_buf, 63, " PART=%d", uid->part); + + log_print("Device %s IDTYPE=%s IDNAME=%s DEVNAME=%s PVID=%s%s", + uid->dev ? dev_name(uid->dev) : ".", + uid->idtype ? idtype_to_str(uid->idtype) : ".", + uid->idname ? uid->idname : ".", + uid->devname ? uid->devname : ".", + uid->pvid ? (char *)uid->pvid : ".", + part_buf); + } + +out: + return ECMD_PROCESSED; + +bad: + return ECMD_FAILED; +} + diff --git a/tools/polldaemon.c b/tools/polldaemon.c index b0294ebb3..d4e0be13e 100644 --- a/tools/polldaemon.c +++ b/tools/polldaemon.c @@ -670,6 +670,15 @@ static int _daemon_parms_init(struct cmd_context *cmd, struct daemon_parms *parm parms->progress_display = parms->interval ? 1 : 0; + memset(parms->devicesfile, 0, sizeof(parms->devicesfile)); + if (cmd->devicesfile) { + if (strlen(cmd->devicesfile) >= sizeof(parms->devicesfile)) { + log_error("devicefile name too long for lvmpolld"); + return 0; + } + strcpy(parms->devicesfile, cmd->devicesfile); + } + return 1; } diff --git a/tools/pvck.c b/tools/pvck.c index a0f567eeb..6178e3f5b 100644 --- a/tools/pvck.c +++ b/tools/pvck.c @@ -19,6 +19,7 @@ #include "lib/format_text/layout.h" #include "lib/mm/xlate.h" #include "lib/misc/crc.h" +#include "lib/device/device_id.h" #define ONE_MB_IN_BYTES 1048576 @@ -3032,6 +3033,11 @@ int pvck(struct cmd_context *cmd, int argc, char **argv) if (arg_is_set(cmd, repairtype_ARG) || arg_is_set(cmd, repair_ARG)) { pv_name = argv[0]; + if (!setup_device(cmd, pv_name)) { + log_error("Failed to set up device %s.", pv_name); + return ECMD_FAILED; + } + if (!(dev = dev_cache_get(cmd, pv_name, cmd->filter))) { log_error("No device found for %s %s.", pv_name, dev_cache_filtered_reason(pv_name)); return ECMD_FAILED; @@ -3041,6 +3047,11 @@ int pvck(struct cmd_context *cmd, int argc, char **argv) if (arg_is_set(cmd, dump_ARG)) { pv_name = argv[0]; + if (!setup_device(cmd, pv_name)) { + log_error("Failed to set up device %s.", pv_name); + return ECMD_FAILED; + } + dev = dev_cache_get(cmd, pv_name, cmd->filter); if (!dev) diff --git a/tools/pvcreate.c b/tools/pvcreate.c index 29ae0fa2e..71eb060a3 100644 --- a/tools/pvcreate.c +++ b/tools/pvcreate.c @@ -142,6 +142,8 @@ int pvcreate(struct cmd_context *cmd, int argc, char **argv) clear_hint_file(cmd); + cmd->create_edit_devices_file = 1; + lvmcache_label_scan(cmd); if (!(handle = init_processing_handle(cmd, NULL))) { diff --git a/tools/pvscan.c b/tools/pvscan.c index 4d811da55..a1e827bed 100644 --- a/tools/pvscan.c +++ b/tools/pvscan.c @@ -1254,8 +1254,17 @@ 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(); + /* + * Set up device ids. Does not open or read any devices. + * Matches devs in dev-cache to devices file entries. + * Devs that don't match an entry rejected by filter-deviceid, + * in dev_cache_get below which applies filters and returns NULL + * if the requested device name doesn't pass filters. + */ + if (!setup_devices(cmd)) { + log_error("Failed to set up devices."); + return ECMD_FAILED; + } 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..f6a4ebda1 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> @@ -5101,15 +5102,57 @@ int pvcreate_each_device(struct cmd_context *cmd, /* * Translate arg names into struct device's. */ + + /* + * We allow pvcreate to look outside devices file here to find + * the target device, in case the user has not added the device + * being pvcreated to the devices file. + * + * First, wipe the filter to remove the previous result of filtering + * the target device that was done during label_scan. The persistent + * filter is storing the result of filtering the device when we + * were not skipping filter-deviceid. + * Then look up the device in dev-cache which will rerun the filters + * against the target device, skipping filter-deviceid. The target + * device could be excluded by a filter other than deviceid. + * + * TODO: do we want to add a config setting that would disable this + * ability of pvcreate to use devs outside of the devices_file? + * i.e. disable the ability to skip filter-deviceid. + * If devices_file is to be more strict in allowing access to devs, + * e.g. applied to pvcreate, then a user would need to add the new + * device to devices_file prior to running pvcreate on it. + */ + cmd->filter_deviceid_skip = 1; + dm_list_iterate_items_safe(pd, pd2, &pp->arg_devices) { - pd->dev = dev_cache_get(cmd, pd->name, cmd->filter); - if (!pd->dev) { - log_print("No device found for %s", pd->name); + struct device *new_dev; + + /* + * No filter applied here because we first need to wipe the + * previous result that was based on using filter-deviceid. + */ + if (!(new_dev = dev_cache_get(cmd, pd->name, NULL))) { + log_error("No device found for %s", pd->name); dm_list_del(&pd->list); dm_list_add(&pp->arg_fail, &pd->list); + continue; } + + cmd->filter->wipe(cmd, cmd->filter, new_dev, NULL); + + if (!cmd->filter->passes_filter(cmd, cmd->filter, new_dev, NULL)) { + log_error("Device %s is excluded by filter.", pd->name); + dm_list_del(&pd->list); + dm_list_add(&pp->arg_fail, &pd->list); + continue; + } + + pd->dev = new_dev; } + cmd->filter_deviceid_skip = 0; + /* * Can the command continue if some specified devices were not found? */ @@ -5411,6 +5454,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_add(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 +5496,10 @@ do_command: continue; } + device_id_add(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 +5545,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 +5563,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. diff --git a/tools/tools.h b/tools/tools.h index 7f2434d06..aa7a03ba2 100644 --- a/tools/tools.h +++ b/tools/tools.h @@ -29,6 +29,7 @@ #include "lib/config/defaults.h" #include "lib/device/dev-cache.h" #include "lib/device/device.h" +#include "lib/device/device_id.h" #include "lib/display/display.h" #include "errors.h" #include "lib/metadata/metadata-exported.h" diff --git a/tools/vgcreate.c b/tools/vgcreate.c index 09b6a6c95..73066e9a4 100644 --- a/tools/vgcreate.c +++ b/tools/vgcreate.c @@ -82,6 +82,8 @@ int vgcreate(struct cmd_context *cmd, int argc, char **argv) return ECMD_FAILED; } + cmd->create_edit_devices_file = 1; + lvmcache_label_scan(cmd); if (lvmcache_vginfo_from_vgname(vp_new.vg_name, NULL)) { @@ -100,6 +102,8 @@ int vgcreate(struct cmd_context *cmd, int argc, char **argv) return_ECMD_FAILED; } + unlock_devices_file(cmd); + if (!(vg = vg_create(cmd, vp_new.vg_name))) goto_bad; diff --git a/tools/vgextend.c b/tools/vgextend.c index da768981d..73e2e632a 100644 --- a/tools/vgextend.c +++ b/tools/vgextend.c @@ -168,6 +168,8 @@ int vgextend(struct cmd_context *cmd, int argc, char **argv) clear_hint_file(cmd); + cmd->edit_devices_file = 1; + lvmcache_label_scan(cmd); if (!(handle = init_processing_handle(cmd, NULL))) { @@ -182,6 +184,8 @@ int vgextend(struct cmd_context *cmd, int argc, char **argv) } } + unlock_devices_file(cmd); + /* * It is always ok to add new PVs to a VG - even if there are * missing PVs. No LVs are affected by this operation, but diff --git a/tools/vgimportclone.c b/tools/vgimportclone.c index ee1c28fae..7c961c4bb 100644 --- a/tools/vgimportclone.c +++ b/tools/vgimportclone.c @@ -15,62 +15,39 @@ #include "tools.h" #include "lib/cache/lvmcache.h" #include "lib/filters/filter.h" +#include "lib/device/device_id.h" struct vgimportclone_params { - unsigned done; - unsigned total; - - int import_vg; - int found_args; - struct dm_list arg_import; + struct dm_list new_devs; const char *base_vgname; const char *old_vgname; const char *new_vgname; + unsigned import_devices:1; + unsigned import_vg:1; }; -struct vgimportclone_device { - struct dm_list list; - struct device *dev; - unsigned found_in_vg : 1; -}; - -static int _vgimportclone_pv_single(struct cmd_context *cmd, struct volume_group *vg, - struct physical_volume *pv, struct processing_handle *handle) +static struct device_list *_get_device_list(struct dm_list *list, struct device *dev) { - struct vgimportclone_params *vp = (struct vgimportclone_params *) handle->custom_handle; - struct vgimportclone_device *vd; + struct device_list *devl; - if (vg && is_orphan_vg(vg->name)) { - log_error("Cannot import clone of orphan PV %s.", dev_name(pv->dev)); - return ECMD_FAILED; - } - - if (!(vd = dm_pool_zalloc(cmd->mem, sizeof(*vd)))) { - log_error("alloc failed."); - return ECMD_FAILED; + dm_list_iterate_items(devl, list) { + if (devl->dev == dev) + return devl; } - - vd->dev = pv->dev; - dm_list_add(&vp->arg_import, &vd->list); - - log_debug("vgimportclone dev %s VG %s found to import", - dev_name(vd->dev), vg ? vg->name : "<none>"); - - vp->found_args++; - - return ECMD_PROCESSED; + return NULL; } -static int _vgimportclone_vg_single(struct cmd_context *cmd, const char *vg_name, - struct volume_group *vg, struct processing_handle *handle) +static int _update_vg(struct cmd_context *cmd, struct volume_group *vg, + struct vgimportclone_params *vp) { char uuid[64] __attribute__((aligned(8))); - struct vgimportclone_params *vp = (struct vgimportclone_params *) handle->custom_handle; - struct vgimportclone_device *vd; struct pv_list *pvl, *new_pvl; struct lv_list *lvl; + struct device_list *devl; + struct dm_list tmp_devs; int devs_used_for_lv = 0; - int found; + + dm_list_init(&tmp_devs); if (vg_is_exported(vg) && !vp->import_vg) { log_error("VG %s is exported, use the --import option.", vg->name); @@ -88,6 +65,10 @@ static int _vgimportclone_vg_single(struct cmd_context *cmd, const char *vg_name * that's being imported, so check DEV_USED_FOR_LV. */ dm_list_iterate_items(pvl, &vg->pvs) { + if (is_missing_pv(pvl->pv) || !pvl->pv->dev) { + log_error("VG is missing a device."); + goto bad; + } if (pvl->pv->dev->flags & DEV_USED_FOR_LV) { log_error("Device %s has active LVs, deactivate first.", dev_name(pvl->pv->dev)); devs_used_for_lv++; @@ -98,21 +79,14 @@ static int _vgimportclone_vg_single(struct cmd_context *cmd, const char *vg_name goto_bad; /* - * The arg_import list must match the PVs in VG. + * The new_devs list must match the PVs in VG. */ dm_list_iterate_items(pvl, &vg->pvs) { - found = 0; - - dm_list_iterate_items(vd, &vp->arg_import) { - if (pvl->pv->dev != vd->dev) - continue; - vd->found_in_vg = 1; - found = 1; - break; - } - - if (!found) { + if ((devl = _get_device_list(&vp->new_devs, pvl->pv->dev))) { + dm_list_del(&devl->list); + dm_list_add(&tmp_devs, &devl->list); + } else { if (!id_write_format(&pvl->pv->id, uuid, sizeof(uuid))) goto_bad; @@ -124,21 +98,21 @@ static int _vgimportclone_vg_single(struct cmd_context *cmd, const char *vg_name } } - dm_list_iterate_items(vd, &vp->arg_import) { - if (!vd->found_in_vg) { - /* device arg is not in the VG. */ - log_error("Device %s was not found in VG %s.", dev_name(vd->dev), vg->name); - log_error("The devices to import must match the devices in the VG."); - goto bad; - } + dm_list_iterate_items(devl, &vp->new_devs) { + /* device arg is not in the VG. */ + log_error("Device %s was not found in VG %s.", dev_name(devl->dev), vg->name); + log_error("The devices to import must match the devices in the VG."); + goto bad; } + dm_list_splice(&vp->new_devs, &tmp_devs); + /* * Write changes. */ if (!archive(vg)) - return_ECMD_FAILED; + goto_bad; if (vp->import_vg) vg->status &= ~EXPORTED_VG; @@ -175,6 +149,8 @@ static int _vgimportclone_vg_single(struct cmd_context *cmd, const char *vg_name if (!id_create(&new_pvl->pv->id)) goto_bad; + memcpy(&pvl->pv->dev->pvid, &new_pvl->pv->id.uuid, ID_LEN); + dm_list_add(&vg->pv_write_list, &new_pvl->list); } @@ -183,97 +159,255 @@ static int _vgimportclone_vg_single(struct cmd_context *cmd, const char *vg_name lvl->lv->lock_args = NULL; } + /* + * Add the device id before writing the vg so that the device id + * will be included in the metadata. The device file is written + * (with these additions) at the end of the command. + */ + if (vp->import_devices) { + dm_list_iterate_items(devl, &vp->new_devs) { + if (!device_id_add(cmd, devl->dev, devl->dev->pvid, NULL, NULL)) { + log_error("Failed to add device id for %s.", dev_name(devl->dev)); + goto bad; + } + } + } + if (!vg_write(vg) || !vg_commit(vg)) goto_bad; - return ECMD_PROCESSED; + return 1; bad: - return ECMD_FAILED; + return 0; +} + +/* + * Create a list of devices that label_scan would scan excluding devs in + * new_devs. + */ +static int _get_other_devs(struct cmd_context *cmd, struct dm_list *new_devs, struct dm_list *other_devs) +{ + struct dev_iter *iter; + struct device *dev; + struct device_list *devl; + + if (!(iter = dev_iter_create(cmd->filter, 0))) + return_0; + + while ((dev = dev_iter_get(cmd, iter))) { + if (_get_device_list(new_devs, dev)) + continue; + if (!(devl = malloc(sizeof(*devl)))) + return_0; + devl->dev = dev; + dm_list_add(other_devs, &devl->list); + } + + dev_iter_destroy(iter); + return 1; } int vgimportclone(struct cmd_context *cmd, int argc, char **argv) { struct vgimportclone_params vp = { 0 }; - struct processing_handle *handle = NULL; - struct dm_list vgnameids_on_system; /* vgnameid_list */ + struct dm_list vgnames; struct vgnameid_list *vgnl; - struct vgimportclone_device *vd; - struct lvmcache_info *info; + struct device *dev; + struct device_list *devl; + struct dm_list other_devs; + struct volume_group *vg; const char *vgname; char base_vgname[NAME_LEN] = { 0 }; char tmp_vgname[NAME_LEN] = { 0 }; + uint32_t lockd_state = 0; + uint32_t error_flags = 0; unsigned int vgname_count; int ret = ECMD_FAILED; + int i; - if (!argc) { - log_error("PV names required."); - return EINVALID_CMD_LINE; - } - - dm_list_init(&vgnameids_on_system); - dm_list_init(&vp.arg_import); + dm_list_init(&vgnames); + dm_list_init(&other_devs); + dm_list_init(&vp.new_devs); set_pv_notify(cmd); + vp.import_devices = arg_is_set(cmd, importdevices_ARG); vp.import_vg = arg_is_set(cmd, import_ARG); - if (!(handle = init_processing_handle(cmd, NULL))) { - log_error("Failed to initialize processing handle."); + if (!lock_global(cmd, "ex")) return ECMD_FAILED; - } - handle->custom_handle = &vp; - if (!lock_global(cmd, "ex")) { - destroy_processing_handle(cmd, handle); + clear_hint_file(cmd); + + cmd->edit_devices_file = 1; + + if (!setup_devices(cmd)) { + log_error("Failed to set up devices."); return ECMD_FAILED; } /* - * Find the devices being imported which are named on the command line. - * They may be in the list of unchosen duplicates. + * When importing devices not in the devices file + * we cannot use the device id filter when looking + * for the devs. */ + if (vp.import_devices) { + if (!cmd->enable_devices_file) { + log_print("Devices file not enabled, ignoring importdevices."); + vp.import_devices = 0; + } else if (!devices_file_exists(cmd)) { + log_print("Devices file does not exist, ignoring importdevices."); + vp.import_devices = 0; + } else { + cmd->filter_deviceid_skip = 1; + } + } - log_debug("Finding devices to import."); - cmd->cname->flags |= ENABLE_DUPLICATE_DEVS; - process_each_pv(cmd, argc, argv, NULL, 0, 0, handle, _vgimportclone_pv_single); + /* + * For each device arg, get the dev from dev-cache. + * Only apply nodata filters when getting the devs + * from dev cache. The data filters will be applied + * next when label scan is done on them. + */ + cmd->filter_nodata_only = 1; - if (vp.found_args != argc) { - log_error("Failed to find all devices."); - goto out; + for (i = 0; i < argc; i++) { + if (!(dev = dev_cache_get(cmd, argv[i], cmd->filter))) { + /* FIXME: if filtered print which */ + log_error("Failed to find device %s.", argv[i]); + goto out; + } + + if (!(devl = malloc(sizeof(*devl)))) + goto_out; + + devl->dev = dev; + dm_list_add(&vp.new_devs, &devl->list); + } + + /* + * Clear the result of nodata filtering so all + * filters will be applied in label_scan. + */ + dm_list_iterate_items(devl, &vp.new_devs) + cmd->filter->wipe(cmd, cmd->filter, devl->dev, NULL); + + /* + * Scan lvm info from each new dev, and apply the filters + * again, this time applying filters that use data. + */ + log_debug("scan new devs"); + + label_scan_setup_bcache(); + + cmd->filter_nodata_only = 0; + + label_scan_devs(cmd, cmd->filter, &vp.new_devs); + + /* + * Check if any new devs were excluded by filters + * in label scan, where all filters were applied. + * (incl those that need data.) + */ + dm_list_iterate_items(devl, &vp.new_devs) { + if (!cmd->filter->passes_filter(cmd, cmd->filter, devl->dev, "persistent")) { + /* FIXME: print which filter */ + log_error("Device %s was excluded by filters.", dev_name(devl->dev)); + goto out; + } } /* - * Find the VG name of the PVs being imported, save as old_vgname. - * N.B. If vd->dev is a duplicate, then it may not match info->dev. + * Look up vg info in lvmcache for each new_devs entry. This info was + * found by label scan. Verify all the new devs are from the same vg. + * The lvmcache at this point only reflects a label scan, not a vg_read + * which would assign PV info's for PVs without metadata. So this + * check is incomplete, and the same vg for devs is verified again + * later. */ + dm_list_iterate_items(devl, &vp.new_devs) { + struct lvmcache_info *info; - dm_list_iterate_items(vd, &vp.arg_import) { - if (!(info = lvmcache_info_from_pvid(vd->dev->pvid, NULL, 0))) { - log_error("Failed to find PVID for device %s in lvmcache.", dev_name(vd->dev)); + if (!(info = lvmcache_info_from_pvid(devl->dev->pvid, devl->dev, 0))) { + log_error("Failed to find PVID for device %s.", dev_name(devl->dev)); goto out; } if (!(vgname = lvmcache_vgname_from_info(info))) { - log_error("Failed to find VG name for device %s in lvmcache.", dev_name(vd->dev)); - goto out; + /* The PV may not have metadata, this will be resolved in + the process_each_vg/vg_read at the end. */ + continue; } if (!vp.old_vgname) { if (!(vp.old_vgname = dm_pool_strdup(cmd->mem, vgname))) goto_out; - } else { - if (strcmp(vp.old_vgname, vgname)) { - log_error("Devices must be from the same VG."); - goto out; - } + } else if (strcmp(vp.old_vgname, vgname)) { + log_error("Devices must be from the same VG."); + goto out; } } + if (!vp.old_vgname) { + log_error("No VG found on devices."); + goto out; + } + + /* + * Get rid of lvmcache info from the new devs because we are going to + * read the other devs next (which conflict with the new devs because + * of the duplicated info.) + */ + lvmcache_destroy(cmd, 1, 0); + + /* + * Now processing other devs instead of new devs, so return to using + * the deviceid filter. (wiping filters not needed since these other + * devs have not been filtered yet.) + */ + cmd->filter_deviceid_skip = 0; + + /* + * Scan all other devs (devs that would normally be seen excluding new + * devs). This is necessary to check if the new vgname conflicts with + * an existing vgname on other devices. We don't need to actually + * process any existing VGs, we only process the VG on the new devs + * being imported after this. + * + * This only requires a label_scan of the other devs which is enough to + * see what the other vgnames are. + * + * Only apply nodata filters when creating the other_devs list. + * Then apply all filters when label_scan_devs processes the label. + */ + + log_debug("get other devices"); + + cmd->filter_nodata_only = 1; + + if (!_get_other_devs(cmd, &vp.new_devs, &other_devs)) + goto_out; + + log_debug("scan other devices"); + + cmd->filter_nodata_only = 0; + + /* + * Clear the result of nodata filtering so all + * filters will be applied in label_scan. + */ + dm_list_iterate_items(devl, &other_devs) + cmd->filter->wipe(cmd, cmd->filter, devl->dev, NULL); + + label_scan_devs(cmd, cmd->filter, &other_devs); + + if (!lvmcache_get_vgnameids(cmd, &vgnames, NULL, 0)) + goto_out; + /* * Pick a new VG name, save as new_vgname. The new name begins with * the basevgname or old_vgname, plus a $i suffix, if necessary, to - * make it unique. This requires comparing the old_vgname with all the - * VG names on the system. + * make it unique. */ if (arg_is_set(cmd, basevgname_ARG)) { @@ -296,11 +430,8 @@ int vgimportclone(struct cmd_context *cmd, int argc, char **argv) vgname_count = 1; } - if (!lvmcache_get_vgnameids(cmd, &vgnameids_on_system, NULL, 0)) - goto_out; - retry_name: - dm_list_iterate_items(vgnl, &vgnameids_on_system) { + dm_list_iterate_items(vgnl, &vgnames) { if (!strcmp(vgnl->vg_name, tmp_vgname)) { vgname_count++; if (dm_snprintf(tmp_vgname, sizeof(tmp_vgname), "%s%u", base_vgname, vgname_count) < 0) { @@ -315,42 +446,61 @@ retry_name: goto_out; log_debug("Using new VG name %s.", vp.new_vgname); - lvmcache_destroy(cmd, 1, 0); - /* - * Create a device filter so that we are only working with the devices - * in arg_import. With the original devs hidden (that arg_import were - * cloned from), we can read and write the cloned PVs and VG without - * touching the original PVs/VG. + * Get rid of lvmcache info from the other devs because we are going to + * read the new devs again, now to update them. */ + lvmcache_destroy(cmd, 1, 0); - init_internal_filtering(1); - dm_list_iterate_items(vd, &vp.arg_import) - internal_filter_allow(cmd->mem, vd->dev); - refresh_filters(cmd); - - log_debug("Changing VG %s to %s.", vp.old_vgname, vp.new_vgname); + log_debug("import vg on new devices"); if (!lock_vol(cmd, vp.new_vgname, LCK_VG_WRITE, NULL)) { log_error("Can't get lock for new VG name %s", vp.new_vgname); goto out; } - /* - * Trying to lock the duplicated VG would conflict with the original, - * and it's not needed because the new VG will be imported as a local VG. - */ - cmd->lockd_vg_disable = 1; + /* No filter used since these devs have already been filtered above. */ + label_scan_devs_rw(cmd, NULL, &vp.new_devs); - clear_hint_file(cmd); + cmd->can_use_one_scan = 1; + cmd->include_exported_vgs = 1; - ret = process_each_vg(cmd, 0, NULL, vp.old_vgname, NULL, READ_FOR_UPDATE, 0, handle, _vgimportclone_vg_single); + vg = vg_read(cmd, vp.old_vgname, NULL, READ_WITHOUT_LOCK | READ_FOR_UPDATE, lockd_state, &error_flags, NULL); + if (!vg) { + log_error("Failed to read VG %s from devices being imported.", vp.old_vgname); + unlock_vg(cmd, NULL, vp.new_vgname); + goto out; + } + + if (error_flags) { + log_error("Error reading VG %s from devices being imported.", vp.old_vgname); + release_vg(vg); + unlock_vg(cmd, NULL, vp.new_vgname); + goto out; + } + + if (!_update_vg(cmd, vg, &vp)) { + log_error("Failed to update VG on devices being imported."); + release_vg(vg); + unlock_vg(cmd, NULL, vp.new_vgname); + goto out; + } + release_vg(vg); unlock_vg(cmd, NULL, vp.new_vgname); -out: - internal_filter_clear(); - init_internal_filtering(0); - destroy_processing_handle(cmd, handle); + /* + * Should we be using device_ids_validate to check/fix other + * devs in the devices file? + */ + if (vp.import_devices) { + if (!device_ids_write(cmd)) { + log_error("Failed to write devices file."); + goto out; + } + } + ret = ECMD_PROCESSED; +out: + unlock_devices_file(cmd); return ret; } diff --git a/tools/vgimportdevices.c b/tools/vgimportdevices.c new file mode 100644 index 000000000..6da1c84b8 --- /dev/null +++ b/tools/vgimportdevices.c @@ -0,0 +1,210 @@ +/* + * 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/filters/filter.h" +#include "lib/device/device_id.h" + +struct vgimportdevices_params { + uint32_t added_devices; +}; + +static int _vgimportdevices_single(struct cmd_context *cmd, + const char *vg_name, + struct volume_group *vg, + struct processing_handle *handle) +{ + struct vgimportdevices_params *vp = (struct vgimportdevices_params *) handle->custom_handle; + struct pv_list *pvl; + struct physical_volume *pv; + int update_vg = 1; + int updated_pvs = 0; + const char *idtypestr; + + dm_list_iterate_items(pvl, &vg->pvs) { + if (is_missing_pv(pvl->pv) || !pvl->pv->dev) { + log_error("Not importing devices for VG %s with missing PV %s.", + vg->name, (const char *)&pvl->pv->id.uuid); + goto bad; + } + } + + /* + * We want to allow importing devices of foreign and shared + * VGs, but we do not want to update device_ids in those VGs. + * + * If --foreign is set, then foreign VGs will be passed + * to this function; add devices but don't update vg. + * shared VGs are passed to this function; add devices + * and do not update. + */ + if (vg_is_foreign(vg) || vg_is_shared(vg)) + update_vg = 0; + + /* + * TODO: let users import devices without updating VG device_ids. + * if --nodeviceidupdate; update_vg = 0; + */ + + /* + * User can select the idtype to use when importing. + */ + idtypestr = arg_str_value(cmd, deviceidtype_ARG, NULL); + + dm_list_iterate_items(pvl, &vg->pvs) { + pv = pvl->pv; + + if (!idtypestr && pv->device_id_type) + idtypestr = pv->device_id_type; + + device_id_add(cmd, pv->dev, (const char *)&pvl->pv->id.uuid, idtypestr, NULL); + vp->added_devices++; + + /* We could skip update if the device_id has not changed. */ + + if (!update_vg) + continue; + + updated_pvs++; + } + + if (updated_pvs) { + if (!vg_write(vg) || !vg_commit(vg)) + goto_bad; + backup(vg); + } + + return ECMD_PROCESSED; +bad: + return ECMD_FAILED; +} + +/* + * This command always scans all devices on the system, + * any pre-existing devices_file does not limit the scope. + * + * This command adds the VG's devices to whichever + * devices_file is set in config or command line. + * If devices_file doesn't exist, it's created. + * + * If devices_file is "" then this file will scan all devices + * and show the devices that it would otherwise have added to + * the devices_file. The VG is not updated with device_ids. + * + * This command updates the VG metadata to add device_ids + * (if the metadata is missing them), unless an option is + * set to skip that, e.g. --nodeviceidupdate? + * + * If the VG found has a foreign system ID then an error + * will be printed. To import devices from a foreign VG: + * vgimportdevices --foreign -a + * vgimportdevices --foreign VG + * + * If there are duplicate VG names it will do nothing. + * + * If there are duplicate PVIDs related to VG it will do nothing, + * the user would need to add the PVs they want with lvmdevices --add. + * + * vgimportdevices -a (no vg arg) will import all accesible VGs. + */ + +int vgimportdevices(struct cmd_context *cmd, int argc, char **argv) +{ + struct vgimportdevices_params vp = { 0 }; + struct processing_handle *handle; + int ret = ECMD_PROCESSED; + + if (arg_is_set(cmd, foreign_ARG)) + cmd->include_foreign_vgs = 1; + + cmd->include_shared_vgs = 1; + + /* So that we can warn about this. */ + cmd->handles_missing_pvs = 1; + + /* Print a notice if a regex filter is being applied? + Possibly offer an option to ignore a regex filter? */ + + if (!lock_global(cmd, "ex")) + return ECMD_FAILED; + + /* + * Prepare devices file preemptively because the error path for this + * case from process_each is not as clean. + */ + if (!setup_devices_file(cmd)) { + log_error("Failed to set up devices file."); + return ECMD_FAILED; + } + if (!devices_file_exists(cmd) && !devices_file_touch(cmd)) { + log_error("Failed to create 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); + + if (!(handle = init_processing_handle(cmd, NULL))) { + log_error("Failed to initialize processing handle."); + ret = ECMD_FAILED; + goto out; + } + handle->custom_handle = &vp; + + /* + * import is a case where we do not want to be limited by an existing + * devices file because we want to search outside the devices file for + * new devs to add to it, but we do want devices file entries on + * use_device_ids so we can update and write out that list. + * + * Ususally when devices file is enabled, we use filter-deviceid and + * skip filter-regex. In this import case it's reversed, and we skip + * filter-deviceid and use filter-regex. + */ + cmd->filter_deviceid_skip = 1; + cmd->filter_regex_with_devices_file = 1; + cmd->create_edit_devices_file = 1; + + /* + * For each VG: + * device_id_add() each PV in the VG + * update device_ids in the VG (potentially) + */ + ret = process_each_vg(cmd, argc, argv, NULL, NULL, READ_FOR_UPDATE, + 0, handle, _vgimportdevices_single); + if (ret == ECMD_FAILED) + goto out; + + if (!vp.added_devices) { + log_print("No devices to add."); + goto out; + } + + if (!device_ids_write(cmd)) { + log_print("Failed to update devices file."); + ret = ECMD_FAILED; + goto out; + } + + log_print("Added %u devices to devices file.", vp.added_devices); +out: + destroy_processing_handle(cmd, handle); + return ret; +} + |
