mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-22 17:35:59 +03:00
96b777167c
pvid and vgid are sometimes a null-terminated string, and other times a 'struct id', and the two types were often cast between each other. When a struct id was cast to a char pointer, the resulting string would not necessarily be null terminated. Casting a null-terminated string id to a struct id is fine, but is still avoided when possible. A struct id is: int8_t uuid[ID_LEN] A string id is: char pvid[ID_LEN + 1] A convention is introduced to help distinguish them: - variables and struct fields named "pvid" or "vgid" should be null-terminated strings. - variables and struct fields named "pv_id" or "vg_id" should be struct id's. - examples: char pvid[ID_LEN + 1]; char vgid[ID_LEN + 1]; struct id pv_id; struct id vg_id; Function names also attempt to follow this convention. Avoid casting between the two types as much as possible, with limited exceptions when known to be safe and clearly commented. Avoid using variations of strcpy and strcmp, and instead use memcpy/memcmp with ID_LEN (with similar limited exceptions possible.)
208 lines
5.8 KiB
C
208 lines
5.8 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"
|
|
|
|
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;
|
|
char pvid[ID_LEN + 1] __attribute__((aligned(8))) = { 0 };
|
|
struct pv_list *pvl;
|
|
struct physical_volume *pv;
|
|
int update_vg = 1;
|
|
int updated_pvs = 0;
|
|
const char *idtypestr = NULL; /* deviceidtype_ARG ? */
|
|
|
|
dm_list_iterate_items(pvl, &vg->pvs) {
|
|
if (is_missing_pv(pvl->pv) || !pvl->pv->dev) {
|
|
memcpy(pvid, &pvl->pv->id.uuid, ID_LEN);
|
|
log_error("Not importing devices for VG %s with missing PV %s.",
|
|
vg->name, pvid);
|
|
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;
|
|
|
|
dm_list_iterate_items(pvl, &vg->pvs) {
|
|
pv = pvl->pv;
|
|
|
|
if (!idtypestr && pv->device_id_type)
|
|
idtypestr = pv->device_id_type;
|
|
|
|
memcpy(pvid, &pvl->pv->id.uuid, ID_LEN);
|
|
device_id_add(cmd, pv->dev, pvid, 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;
|
|
}
|
|
|
|
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_FAILED;
|
|
|
|
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;
|
|
|
|
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 (!cmd->enable_devices_file) {
|
|
log_error("Devices file not enabled.");
|
|
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.");
|
|
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_devices 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)
|
|
*
|
|
* process_each_vg->label_scan->setup_devices
|
|
* setup_devices sees create_edit_devices_file is 1,
|
|
* so it does lock_devices_file(EX), then it creates/reads
|
|
* the devices file, then each device_id_add happens
|
|
* above, and then device_ids_write happens below.
|
|
*/
|
|
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;
|
|
}
|
|
|