diff options
-rw-r--r-- | lib/metadata/metadata-exported.h | 58 | ||||
-rw-r--r-- | lib/metadata/metadata.c | 94 | ||||
-rw-r--r-- | lib/metadata/pv_manip.c | 20 | ||||
-rw-r--r-- | lib/metadata/vg.c | 1 | ||||
-rw-r--r-- | lib/metadata/vg.h | 7 | ||||
-rw-r--r-- | liblvm/lvm_pv.c | 12 | ||||
-rw-r--r-- | liblvm/lvm_vg.c | 2 | ||||
-rw-r--r-- | tools/commands.h | 6 | ||||
-rw-r--r-- | tools/pvchange.c | 2 | ||||
-rw-r--r-- | tools/pvcreate.c | 143 | ||||
-rw-r--r-- | tools/pvdisplay.c | 5 | ||||
-rw-r--r-- | tools/pvmove.c | 2 | ||||
-rw-r--r-- | tools/pvresize.c | 2 | ||||
-rw-r--r-- | tools/pvscan.c | 2 | ||||
-rw-r--r-- | tools/reporter.c | 6 | ||||
-rw-r--r-- | tools/toollib.c | 1072 | ||||
-rw-r--r-- | tools/toollib.h | 10 | ||||
-rw-r--r-- | tools/tools.h | 2 | ||||
-rw-r--r-- | tools/vgcreate.c | 83 | ||||
-rw-r--r-- | tools/vgextend.c | 89 | ||||
-rw-r--r-- | tools/vgreduce.c | 2 | ||||
-rw-r--r-- | tools/vgsplit.c | 6 |
22 files changed, 1303 insertions, 323 deletions
diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h index 4f25f7d41..1aaa28fce 100644 --- a/lib/metadata/metadata-exported.h +++ b/lib/metadata/metadata-exported.h @@ -538,6 +538,60 @@ struct pvcreate_params { struct pvcreate_restorable_params rp; }; +/* + * FIXME: rename this pvcreate_params once the old pvcreate_params is unused. + * This can probably be put in toollib.h. + */ +struct pvcreate_each_params { + /* + * From argc and argv. + */ + char **pv_names; + uint32_t pv_count; + + /* + * From command line args. + */ + int zero; + uint64_t size; + uint64_t data_alignment; + uint64_t data_alignment_offset; + int pvmetadatacopies; + uint64_t pvmetadatasize; + int64_t labelsector; + force_t force; + unsigned yes; + unsigned metadataignore; + + /* + * From recovery-specific command line args. + */ + const char *restorefile; /* NULL if no --restorefile option */ + const char *uuid_str; /* id in printable format, NULL if no id */ + struct id id; + + /* + * From reading VG backup file. + */ + uint64_t ba_start; + uint64_t ba_size; + uint64_t pe_start; + uint32_t extent_count; + uint32_t extent_size; + + /* + * Used for command processing. + */ + struct dm_list prompts; /* pvcreate_prompt */ + struct dm_list arg_devices; /* pvcreate_device, one for each pv_name */ + struct dm_list arg_confirm; /* pvcreate_device, used for processing */ + struct dm_list arg_create; /* pvcreate_device, used for processing */ + struct dm_list arg_fail; /* pvcreate_device, failed to create */ + struct dm_list pvs; /* pv_list, created and usable for vgcreate/vgextend */ + unsigned preserve_existing : 1; + unsigned check_failed : 1; +}; + struct lvresize_params { const char *vg_name; /* only-used when VG is not yet opened (in /tools) */ const char *lv_name; @@ -639,7 +693,6 @@ int vg_missing_pv_count(const struct volume_group *vg); int vgs_are_compatible(struct cmd_context *cmd, struct volume_group *vg_from, struct volume_group *vg_to); -uint32_t vg_lock_newname(struct cmd_context *cmd, const char *vgname); int lv_resize_prepare(struct cmd_context *cmd, struct logical_volume *lv, struct lvresize_params *lp, struct dm_list *pvh); @@ -691,7 +744,9 @@ uint32_t pv_list_extents_free(const struct dm_list *pvh); int validate_new_vg_name(struct cmd_context *cmd, const char *vg_name); int vg_validate(struct volume_group *vg); +uint32_t vg_lock_newname(struct cmd_context *cmd, const char *vgname); struct volume_group *vg_create(struct cmd_context *cmd, const char *vg_name); +struct volume_group *vg_lock_and_create(struct cmd_context *cmd, const char *vg_name); int vg_remove_mdas(struct volume_group *vg); int vg_remove_check(struct volume_group *vg); void vg_remove_pvs(struct volume_group *vg); @@ -701,6 +756,7 @@ int vg_rename(struct cmd_context *cmd, struct volume_group *vg, const char *new_name); int vg_extend(struct volume_group *vg, int pv_count, const char *const *pv_names, struct pvcreate_params *pp); +int vg_extend_each_pv(struct volume_group *vg, struct pvcreate_each_params *pp); int vg_reduce(struct volume_group *vg, const char *pv_name); int vgreduce_single(struct cmd_context *cmd, struct volume_group *vg, diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c index 0336d5d82..96f502e91 100644 --- a/lib/metadata/metadata.c +++ b/lib/metadata/metadata.c @@ -179,7 +179,6 @@ void del_pvl_from_vgs(struct volume_group *vg, struct pv_list *pvl) int add_pv_to_vg(struct volume_group *vg, const char *pv_name, struct physical_volume *pv, struct pvcreate_params *pp) { - struct pv_to_create *pvc; struct pv_list *pvl; struct format_instance *fid = vg->fid; struct dm_pool *mem = vg->vgmem; @@ -278,16 +277,6 @@ int add_pv_to_vg(struct volume_group *vg, const char *pv_name, break; } - if (pv->status & UNLABELLED_PV) { - if (!(pvc = dm_pool_zalloc(mem, sizeof(*pvc)))) { - log_error("pv_to_create allocation for '%s' failed", pv_name); - return 0; - } - pvc->pv = pv; - pvc->pp = pp; - dm_list_add(&vg->pvs_to_create, &pvc->list); - } - return 1; } @@ -747,6 +736,43 @@ int vg_extend(struct volume_group *vg, int pv_count, const char *const *pv_names return 1; } +int vg_extend_each_pv(struct volume_group *vg, struct pvcreate_each_params *pp) +{ + struct pv_list *pvl; + unsigned int max_phys_block_size = 0; + + log_debug_metadata("Adding PVs to VG %s", vg->name); + + if (_vg_bad_status_bits(vg, RESIZEABLE_VG)) + return_0; + + dm_list_iterate_items(pvl, &pp->pvs) { + log_debug_metadata("Adding PV %s to VG %s", pv_dev_name(pvl->pv), vg->name); + + if (!(check_dev_block_size_for_vg(pvl->pv->dev, + (const struct volume_group *) vg, + &max_phys_block_size))) { + log_error("PV %s has wrong block size", pv_dev_name(pvl->pv)); + return_0; + } + + if (!add_pv_to_vg(vg, pv_dev_name(pvl->pv), pvl->pv, NULL)) { + log_error("PV %s cannot be added to the VG.", pv_dev_name(pvl->pv)); + return_0; + } + + log_verbose("Writing PV data to disk for %s VG %s", + pv_dev_name(pvl->pv), vg->name); + + if (!(pv_write(vg->cmd, pvl->pv, 1))) { + log_error("Failed to write physical volume \"%s\"", pv_dev_name(pvl->pv)); + return_0; + } + } + + return 1; +} + int vg_reduce(struct volume_group *vg, const char *pv_name) { struct physical_volume *pv; @@ -1025,21 +1051,6 @@ struct volume_group *vg_create(struct cmd_context *cmd, const char *vg_name) .context.vg_ref.vg_name = vg_name }; struct format_instance *fid; - uint32_t rc; - - if (!validate_name(vg_name)) { - log_error("Invalid vg name %s", vg_name); - /* FIXME: use _vg_make_handle() w/proper error code */ - return NULL; - } - - rc = vg_lock_newname(cmd, vg_name); - if (rc != SUCCESS) - /* NOTE: let caller decide - this may be check for existence */ - return _vg_make_handle(cmd, NULL, rc); - - /* Strip dev_dir if present */ - vg_name = strip_dir(vg_name, cmd->dev_dir); if (!(vg = alloc_vg("vg_create", cmd, vg_name))) goto_bad; @@ -1081,6 +1092,24 @@ bad: return NULL; } +struct volume_group *vg_lock_and_create(struct cmd_context *cmd, const char *vg_name) +{ + uint32_t rc; + + if (!validate_name(vg_name)) { + log_error("Invalid vg name %s", vg_name); + /* FIXME: use _vg_make_handle() w/proper error code */ + return NULL; + } + + rc = vg_lock_newname(cmd, vg_name); + if (rc != SUCCESS) + /* NOTE: let caller decide - this may be check for existence */ + return _vg_make_handle(cmd, NULL, rc); + + return vg_create(cmd, vg_name); +} + /* Rounds up by default */ uint32_t extents_from_size(struct cmd_context *cmd, uint64_t size, uint32_t extent_size) @@ -1576,7 +1605,6 @@ void pvcreate_params_set_defaults(struct pvcreate_params *pp) pp->rp.extent_size = 0; } - static int _pvcreate_write(struct cmd_context *cmd, struct pv_to_create *pvc) { int zero = pvc->pp->zero; @@ -3016,7 +3044,6 @@ out: int vg_write(struct volume_group *vg) { struct dm_list *mdah; - struct pv_to_create *pv_to_create, *pv_to_create_safe; struct metadata_area *mda; struct lv_list *lvl; int revert = 0, wrote = 0; @@ -3072,12 +3099,6 @@ int vg_write(struct volume_group *vg) memlock_unlock(vg->cmd); vg->seqno++; - dm_list_iterate_items_safe(pv_to_create, pv_to_create_safe, &vg->pvs_to_create) { - if (!_pvcreate_write(vg->cmd, pv_to_create)) - return 0; - dm_list_del(&pv_to_create->list); - } - /* Write to each copy of the metadata area */ dm_list_iterate_items(mda, &vg->fid->metadata_areas_in_use) { if (!mda->ops->vg_write) { @@ -3561,6 +3582,8 @@ static struct volume_group *_vg_read(struct cmd_context *cmd, struct cached_vg_fmtdata *vg_fmtdata = NULL; /* Additional format-specific data about the vg */ unsigned use_previous_vg; + log_very_verbose("Reading VG %s %.32s", vgname ?: "<no name>", vgid ?: "<no vgid>"); + if (is_orphan_vg(vgname)) { if (use_precommitted) { log_error(INTERNAL_ERROR "vg_read_internal requires vgname " @@ -5030,6 +5053,9 @@ static struct volume_group *_vg_lock_and_read(struct cmd_context *cmd, const cha return _vg_make_handle(cmd, vg, FAILED_LOCKING); } + if (already_locked) + log_very_verbose("Locking %s already done", vg_name); + if (is_orphan_vg(vg_name)) status_flags &= ~LVM_WRITE; diff --git a/lib/metadata/pv_manip.c b/lib/metadata/pv_manip.c index e48fe42a1..09000ecde 100644 --- a/lib/metadata/pv_manip.c +++ b/lib/metadata/pv_manip.c @@ -852,23 +852,3 @@ out: return ret; } -int pvcreate_single(struct cmd_context *cmd, const char *pv_name, - struct pvcreate_params *pp) -{ - int r = 0; - - if (!lock_vol(cmd, VG_ORPHANS, LCK_VG_WRITE, NULL)) { - log_error("Can't get lock for orphan PVs"); - return 0; - } - - if (!(pvcreate_vol(cmd, pv_name, pp, 1))) - goto_out; - - r = 1; - -out: - unlock_vg(cmd, VG_ORPHANS); - - return r; -} diff --git a/lib/metadata/vg.c b/lib/metadata/vg.c index 992cf2a9b..dd6afe9a4 100644 --- a/lib/metadata/vg.c +++ b/lib/metadata/vg.c @@ -60,7 +60,6 @@ struct volume_group *alloc_vg(const char *pool_name, struct cmd_context *cmd, } dm_list_init(&vg->pvs); - dm_list_init(&vg->pvs_to_create); dm_list_init(&vg->pvs_outdated); dm_list_init(&vg->lvs); dm_list_init(&vg->tags); diff --git a/lib/metadata/vg.h b/lib/metadata/vg.h index df660a7f2..5603d4fb4 100644 --- a/lib/metadata/vg.h +++ b/lib/metadata/vg.h @@ -86,13 +86,6 @@ struct volume_group { struct dm_list pvs; /* - * List of physical volumes that were used in vgextend but do not carry - * a PV label yet. They need to be pvcreate'd at vg_write time. - */ - - struct dm_list pvs_to_create; - - /* * List of physical volumes that carry outdated metadata that belongs * to this VG. Currently only populated when lvmetad is in use. The PVs * on this list could still belong to the VG (but their MDA carries an diff --git a/liblvm/lvm_pv.c b/liblvm/lvm_pv.c index fee1e8d15..ca9844c67 100644 --- a/liblvm/lvm_pv.c +++ b/liblvm/lvm_pv.c @@ -419,6 +419,7 @@ int lvm_pv_params_set_property(pv_create_params_t params, const char *name, static int _pv_create(pv_create_params_t params) { struct cmd_context *cmd = (struct cmd_context *)params->libh; + int rc = 0; if (params->pv_p.size) { if (params->pv_p.size % SECTOR_SIZE) { @@ -428,9 +429,16 @@ static int _pv_create(pv_create_params_t params) params->pv_p.size = params->pv_p.size >> SECTOR_SHIFT; } - if (!pvcreate_single(cmd, params->pv_name, ¶ms->pv_p)) + if (!lock_vol(cmd, VG_ORPHANS, LCK_VG_WRITE, NULL)) { + log_errno(EINVAL, "Can't get lock for orphan PVs"); return -1; - return 0; + } + + if (!(pvcreate_vol(cmd, params->pv_name, ¶ms->pv_p, 1))) + rc = -1; + + unlock_vg(cmd, VG_ORPHANS); + return rc; } int lvm_pv_create(lvm_t libh, const char *pv_name, uint64_t size) diff --git a/liblvm/lvm_vg.c b/liblvm/lvm_vg.c index 1a36bccb7..4f05816e3 100644 --- a/liblvm/lvm_vg.c +++ b/liblvm/lvm_vg.c @@ -54,7 +54,7 @@ vg_t lvm_vg_create(lvm_t libh, const char *vg_name) struct volume_group *vg = NULL; struct saved_env e = store_user_env((struct cmd_context *)libh); - vg = vg_create((struct cmd_context *)libh, vg_name); + vg = vg_lock_and_create((struct cmd_context *)libh, vg_name); /* FIXME: error handling is still TBD */ if (vg_read_error(vg)) { release_vg(vg); diff --git a/tools/commands.h b/tools/commands.h index a10d8a3b5..5d059d51f 100644 --- a/tools/commands.h +++ b/tools/commands.h @@ -766,7 +766,7 @@ xx(pvck, xx(pvcreate, "Initialize physical volume(s) for use by LVM", - 0, + ENABLE_ALL_DEVS, "pvcreate\n" "\t[--norestorefile]\n" "\t[--restorefile file]\n" @@ -1119,7 +1119,7 @@ xx(vgconvert, xx(vgcreate, "Create a volume group", - 0, + MUST_USE_ALL_ARGS | ENABLE_ALL_DEVS, "vgcreate\n" "\t[-A|--autobackup {y|n}]\n" "\t[--addtag Tag]\n" @@ -1214,7 +1214,7 @@ xx(vgexport, xx(vgextend, "Add physical volumes to a volume group", - 0, + MUST_USE_ALL_ARGS | ENABLE_ALL_DEVS, "vgextend\n" "\t[-A|--autobackup y|n]\n" "\t[--restoremissing]\n" diff --git a/tools/pvchange.c b/tools/pvchange.c index fad28d194..0e3e46f73 100644 --- a/tools/pvchange.c +++ b/tools/pvchange.c @@ -216,7 +216,7 @@ int pvchange(struct cmd_context *cmd, int argc, char **argv) } } - ret = process_each_pv(cmd, argc, argv, NULL, READ_FOR_UPDATE, handle, _pvchange_single); + ret = process_each_pv(cmd, argc, argv, NULL, 0, READ_FOR_UPDATE, handle, _pvchange_single); if (!argc) unlock_vg(cmd, VG_GLOBAL); diff --git a/tools/pvcreate.c b/tools/pvcreate.c index 1f45ad91d..29b2fb7a5 100644 --- a/tools/pvcreate.c +++ b/tools/pvcreate.c @@ -22,13 +22,10 @@ * Output arguments: * pp: structure allocated by caller, fields written / validated here */ -static int pvcreate_restore_params_validate(struct cmd_context *cmd, - int argc, char **argv, - struct pvcreate_params *pp) +static int pvcreate_each_restore_params_from_args(struct cmd_context *cmd, int argc, + struct pvcreate_each_params *pp) { - const char *uuid = NULL; - struct volume_group *vg; - struct pv_list *existing_pvl; + pp->restorefile = arg_str_value(cmd, restorefile_ARG, NULL); if (arg_count(cmd, restorefile_ARG) && !arg_count(cmd, uuidstr_ARG)) { log_error("--uuid is required with --restorefile"); @@ -48,35 +45,10 @@ static int pvcreate_restore_params_validate(struct cmd_context *cmd, return 0; } - if (arg_count(cmd, uuidstr_ARG)) { - uuid = arg_str_value(cmd, uuidstr_ARG, ""); - if (!id_read_format(&pp->rp.id, uuid)) - return 0; - pp->rp.idp = &pp->rp.id; - lvmcache_seed_infos_from_lvmetad(cmd); /* need to check for UUID dups */ - } - - if (arg_count(cmd, restorefile_ARG)) { - pp->rp.restorefile = arg_str_value(cmd, restorefile_ARG, ""); - /* The uuid won't already exist */ - if (!(vg = backup_read_vg(cmd, NULL, pp->rp.restorefile))) { - log_error("Unable to read volume group from %s", - pp->rp.restorefile); + if (arg_count(cmd, uuidstr_ARG)) { + pp->uuid_str = arg_str_value(cmd, uuidstr_ARG, ""); + if (!id_read_format(&pp->id, pp->uuid_str)) return 0; - } - if (!(existing_pvl = find_pv_in_vg_by_uuid(vg, pp->rp.idp))) { - release_vg(vg); - log_error("Can't find uuid %s in backup file %s", - uuid, pp->rp.restorefile); - return 0; - } - pp->rp.ba_start = pv_ba_start(existing_pvl->pv); - pp->rp.ba_size = pv_ba_size(existing_pvl->pv); - pp->rp.pe_start = pv_pe_start(existing_pvl->pv); - pp->rp.extent_size = pv_pe_size(existing_pvl->pv); - pp->rp.extent_count = pv_pe_count(existing_pvl->pv); - - release_vg(vg); } if (arg_sign_value(cmd, physicalvolumesize_ARG, SIGN_NONE) == SIGN_MINUS) { @@ -90,34 +62,103 @@ static int pvcreate_restore_params_validate(struct cmd_context *cmd, return 1; } +static int pvcreate_each_restore_params_from_backup(struct cmd_context *cmd, + struct pvcreate_each_params *pp) +{ + struct volume_group *vg; + struct pv_list *existing_pvl; + + /* + * When restoring a PV, params need to be read from a backup file. + */ + if (!pp->restorefile) + return 1; + + if (!(vg = backup_read_vg(cmd, NULL, pp->restorefile))) { + log_error("Unable to read volume group from %s", pp->restorefile); + return 0; + } + + if (!(existing_pvl = find_pv_in_vg_by_uuid(vg, &pp->id))) { + release_vg(vg); + log_error("Can't find uuid %s in backup file %s", + pp->uuid_str, pp->restorefile); + return 0; + } + + pp->ba_start = pv_ba_start(existing_pvl->pv); + pp->ba_size = pv_ba_size(existing_pvl->pv); + pp->pe_start = pv_pe_start(existing_pvl->pv); + pp->extent_size = pv_pe_size(existing_pvl->pv); + pp->extent_count = pv_pe_count(existing_pvl->pv); + + release_vg(vg); + return 1; +} + int pvcreate(struct cmd_context *cmd, int argc, char **argv) { - int i; - int ret = ECMD_PROCESSED; - struct pvcreate_params pp; + struct processing_handle *handle; + struct pvcreate_each_params pp; + int ret; - /* Needed to change the set of orphan PVs. */ - if (!lockd_gl(cmd, "ex", 0)) - return_ECMD_FAILED; + if (!argc) { + log_error("Please enter a physical volume path."); + return 0; + } - pvcreate_params_set_defaults(&pp); + /* + * Device info needs to be available for reading the VG backup file in + * pvcreate_each_restore_params_from_backup. + */ + lvmcache_seed_infos_from_lvmetad(cmd); + + /* + * Five kinds of pvcreate param values: + * 1. defaults + * 2. recovery-related command line args + * 3. recovery-related args from backup file + * 4. normal command line args + * (this also checks some settings from 2 & 3) + * 5. argc/argv free args specifying devices + */ + + pvcreate_each_params_set_defaults(&pp); + + if (!pvcreate_each_restore_params_from_args(cmd, argc, &pp)) + return EINVALID_CMD_LINE; - if (!pvcreate_restore_params_validate(cmd, argc, argv, &pp)) { + if (!pvcreate_each_restore_params_from_backup(cmd, &pp)) return EINVALID_CMD_LINE; - } - if (!pvcreate_params_validate(cmd, argc, &pp)) { + + if (!pvcreate_each_params_from_args(cmd, &pp)) return EINVALID_CMD_LINE; - } - for (i = 0; i < argc; i++) { - if (sigint_caught()) - return_ECMD_FAILED; + pp.pv_count = argc; + pp.pv_names = argv; - dm_unescape_colons_and_at_signs(argv[i], NULL, NULL); + /* + * Needed to change the set of orphan PVs. + * (disable afterward to prevent process_each_pv from doing + * a shared global lock since it's already acquired it ex.) + */ + if (!lockd_gl(cmd, "ex", 0)) + return_ECMD_FAILED; + cmd->lockd_gl_disable = 1; + + if (!(handle = init_processing_handle(cmd))) { + log_error("Failed to initialize processing handle."); + return ECMD_FAILED; + } - if (!pvcreate_single(cmd, argv[i], &pp)) - ret = ECMD_FAILED; + if (!pvcreate_each_device(cmd, handle, &pp)) + ret = ECMD_FAILED; + else { + /* pvcreate_each_device returns with orphans locked */ + unlock_vg(cmd, VG_ORPHANS); + ret = ECMD_PROCESSED; } + destroy_processing_handle(cmd, handle); return ret; } diff --git a/tools/pvdisplay.c b/tools/pvdisplay.c index 2763889a9..918aba8ea 100644 --- a/tools/pvdisplay.c +++ b/tools/pvdisplay.c @@ -107,8 +107,9 @@ int pvdisplay(struct cmd_context *cmd, int argc, char **argv) } } - ret = process_each_pv(cmd, argc, argv, NULL, 0, NULL, - _pvdisplay_single); + ret = process_each_pv(cmd, argc, argv, NULL, + arg_is_set(cmd, all_ARG), 0, + NULL, _pvdisplay_single); if (lock_global) unlock_vg(cmd, VG_GLOBAL); diff --git a/tools/pvmove.c b/tools/pvmove.c index be2fab5e9..8f21ba728 100644 --- a/tools/pvmove.c +++ b/tools/pvmove.c @@ -895,7 +895,7 @@ int pvmove(struct cmd_context *cmd, int argc, char **argv) handle->custom_handle = &pp; - process_each_pv(cmd, 1, &pv_name, NULL, + process_each_pv(cmd, 1, &pv_name, NULL, 0, is_abort ? 0 : READ_FOR_UPDATE, handle, is_abort ? &_pvmove_read_single : &_pvmove_setup_single); diff --git a/tools/pvresize.c b/tools/pvresize.c index ad59bae2a..5a37d0c7c 100644 --- a/tools/pvresize.c +++ b/tools/pvresize.c @@ -87,7 +87,7 @@ int pvresize(struct cmd_context *cmd, int argc, char **argv) handle->custom_handle = ¶ms; - ret = process_each_pv(cmd, argc, argv, NULL, READ_FOR_UPDATE, handle, + ret = process_each_pv(cmd, argc, argv, NULL, 0, READ_FOR_UPDATE, handle, _pvresize_single); log_print_unless_silent("%d physical volume(s) resized / %d physical volume(s) " diff --git a/tools/pvscan.c b/tools/pvscan.c index e9fb5d56f..e07b553bd 100644 --- a/tools/pvscan.c +++ b/tools/pvscan.c @@ -422,7 +422,7 @@ int pvscan(struct cmd_context *cmd, int argc, char **argv) handle->custom_handle = ¶ms; - ret = process_each_pv(cmd, argc, argv, NULL, 0, handle, _pvscan_single); + ret = process_each_pv(cmd, argc, argv, NULL, 0, 0, handle, _pvscan_single); if (!params.pvs_found) log_print_unless_silent("No matching physical volumes found"); diff --git a/tools/reporter.c b/tools/reporter.c index 416038c85..856cb82ec 100644 --- a/tools/reporter.c +++ b/tools/reporter.c @@ -893,7 +893,8 @@ static int _report(struct cmd_context *cmd, int argc, char **argv, break; case PVS: if (args_are_pvs) - r = process_each_pv(cmd, argc, argv, NULL, 0, + r = process_each_pv(cmd, argc, argv, NULL, + arg_is_set(cmd, all_ARG), 0, &handle, &_pvs_single); else r = process_each_vg(cmd, argc, argv, NULL, 0, @@ -910,7 +911,8 @@ static int _report(struct cmd_context *cmd, int argc, char **argv, break; case PVSEGS: if (args_are_pvs) - r = process_each_pv(cmd, argc, argv, NULL, 0, + r = process_each_pv(cmd, argc, argv, NULL, + arg_is_set(cmd, all_ARG), 0, &handle, lv_info_needed && !lv_segment_status_needed ? &_pvsegs_with_lv_info_single : !lv_info_needed && lv_segment_status_needed ? &_pvsegs_with_lv_status_single : diff --git a/tools/toollib.c b/tools/toollib.c index dec9106a3..f755d0e06 100644 --- a/tools/toollib.c +++ b/tools/toollib.c @@ -14,6 +14,8 @@ */ #include "tools.h" +#include "format1.h" +#include "format-text.h" #include <sys/stat.h> #include <signal.h> @@ -1136,121 +1138,6 @@ void lv_spawn_background_polling(struct cmd_context *cmd, } } -/* - * Intial sanity checking of non-recovery related command-line arguments. - * - * Output arguments: - * pp: structure allocated by caller, fields written / validated here - */ -int pvcreate_params_validate(struct cmd_context *cmd, int argc, - struct pvcreate_params *pp) -{ - if (!argc) { - log_error("Please enter a physical volume path."); - return 0; - } - - pp->yes = arg_count(cmd, yes_ARG); - pp->force = (force_t) arg_count(cmd, force_ARG); - - if (arg_int_value(cmd, labelsector_ARG, 0) >= LABEL_SCAN_SECTORS) { - log_error("labelsector must be less than %lu.", - LABEL_SCAN_SECTORS); - return 0; - } else { - pp->labelsector = arg_int64_value(cmd, labelsector_ARG, - DEFAULT_LABELSECTOR); - } - - if (!(cmd->fmt->features & FMT_MDAS) && - (arg_count(cmd, pvmetadatacopies_ARG) || - arg_count(cmd, metadatasize_ARG) || - arg_count(cmd, dataalignment_ARG) || - arg_count(cmd, dataalignmentoffset_ARG))) { - log_error("Metadata and data alignment parameters only " - "apply to text format."); - return 0; - } - - if (!(cmd->fmt->features & FMT_BAS) && - arg_count(cmd, bootloaderareasize_ARG)) { - log_error("Bootloader area parameters only " - "apply to text format."); - return 0; - } - - if (arg_count(cmd, metadataignore_ARG)) - pp->metadataignore = arg_int_value(cmd, metadataignore_ARG, - DEFAULT_PVMETADATAIGNORE); - else - pp->metadataignore = find_config_tree_bool(cmd, metadata_pvmetadataignore_CFG, NULL); - - if (arg_count(cmd, pvmetadatacopies_ARG) && - !arg_int_value(cmd, pvmetadatacopies_ARG, -1) && - pp->metadataignore) { - log_error("metadataignore only applies to metadatacopies > 0"); - return 0; - } - - pp->zero = arg_int_value(cmd, zero_ARG, 1); - - if (arg_sign_value(cmd, dataalignment_ARG, SIGN_NONE) == SIGN_MINUS) { - log_error("Physical volume data alignment may not be negative."); - return 0; - } - pp->data_alignment = arg_uint64_value(cmd, dataalignment_ARG, UINT64_C(0)); - - if (pp->data_alignment > UINT32_MAX) { - log_error("Physical volume data alignment is too big."); - return 0; - } - - if (arg_sign_value(cmd, dataalignmentoffset_ARG, SIGN_NONE) == SIGN_MINUS) { - log_error("Physical volume data alignment offset may not be negative"); - return 0; - } - pp->data_alignment_offset = arg_uint64_value(cmd, dataalignmentoffset_ARG, UINT64_C(0)); - - if (pp->data_alignment_offset > UINT32_MAX) { - log_error("Physical volume data alignment offset is too big."); - return 0; - } - - if ((pp->data_alignment + pp->data_alignment_offset) && - (pp->rp.pe_start != PV_PE_START_CALC)) { - if ((pp->data_alignment ? pp->rp.pe_start % pp->data_alignment : pp->rp.pe_start) != pp->data_alignment_offset) { - log_warn("WARNING: Ignoring data alignment %s" - " incompatible with restored pe_start value %s)", - display_size(cmd, pp->data_alignment + pp->data_alignment_offset), - display_size(cmd, pp->rp.pe_start)); - pp->data_alignment = 0; - pp->data_alignment_offset = 0; - } - } - - if (arg_sign_value(cmd, metadatasize_ARG, SIGN_NONE) == SIGN_MINUS) { - log_error("Metadata size may not be negative."); - return 0; - } - - if (arg_sign_value(cmd, bootloaderareasize_ARG, SIGN_NONE) == SIGN_MINUS) { - log_error("Bootloader area size may not be negative."); - return 0; - } - - pp->pvmetadatasize = arg_uint64_value(cmd, metadatasize_ARG, UINT64_C(0)); - if (!pp->pvmetadatasize) - pp->pvmetadatasize = find_config_tree_int(cmd, metadata_pvmetadatasize_CFG, NULL); - - pp->pvmetadatacopies = arg_int_value(cmd, pvmetadatacopies_ARG, -1); - if (pp->pvmetadatacopies < 0) - pp->pvmetadatacopies = find_config_tree_int(cmd, metadata_pvmetadatacopies_CFG, NULL); - - pp->rp.ba_size = arg_uint64_value(cmd, bootloaderareasize_ARG, pp->rp.ba_size); - - return 1; -} - int get_activation_monitoring_mode(struct cmd_context *cmd, int *monitoring_mode) { @@ -1737,8 +1624,6 @@ static int _get_arg_vgnames(struct cmd_context *cmd, int ret_max = ECMD_PROCESSED; const char *vg_name; - log_verbose("Using volume group(s) on command line."); - if (one_vgname) { if (!str_list_add(cmd->mem, arg_vgnames, dm_pool_strdup(cmd->mem, one_vgname))) { @@ -1928,6 +1813,7 @@ static int _process_vgnameid_list(struct cmd_context *cmd, uint32_t read_flags, int skip; int notfound; int process_all = 0; + int already_locked; /* * If no VG names or tags were supplied, then process all VGs. @@ -1957,6 +1843,8 @@ static int _process_vgnameid_list(struct cmd_context *cmd, uint32_t read_flags, continue; } + already_locked = lvmcache_vgname_is_locked(vg_name); + vg = vg_read(cmd, vg_name, vg_uuid, read_flags, lockd_state); if (_ignore_vg(vg, vg_name, arg_vgnames, read_flags, &skip, ¬found)) { stack; @@ -1982,7 +1870,7 @@ static int _process_vgnameid_list(struct cmd_context *cmd, uint32_t read_flags, ret_max = ret; } - if (!vg_read_error(vg)) + if (!vg_read_error(vg) && !already_locked) unlock_vg(cmd, vg_name); endvg: release_vg(vg); @@ -2170,6 +2058,8 @@ int process_each_vg(struct cmd_context *cmd, int argc, char **argv, int ret_max = ECMD_PROCESSED; int ret; + log_debug("Processing each VG"); + /* Disable error in vg_read so we can print it from ignore_vg. */ cmd->vg_read_print_access_error = 0; @@ -2220,7 +2110,7 @@ int process_each_vg(struct cmd_context *cmd, int argc, char **argv, * . A VG name is specified which may refer to one * of multiple VGs on the system with that name. */ - log_very_verbose("Get list of VGs on system"); + log_debug("Get list of VGs on system"); if (!get_vgnameids(cmd, &vgnameids_on_system, NULL, 0)) { ret_max = ECMD_FAILED; @@ -2482,8 +2372,6 @@ static int _get_arg_lvnames(struct cmd_context *cmd, const char *vgname_def; unsigned dev_dir_found; - log_verbose("Using logical volume(s) on command line."); - for (; opt < argc; opt++) { lv_name = argv[opt]; dev_dir_found = 0; @@ -2585,6 +2473,7 @@ static int _process_lv_vgnameid_list(struct cmd_context *cmd, uint32_t read_flag int ret; int skip; int notfound; + int already_locked; dm_list_iterate_items(vgnl, vgnameids_to_process) { if (sigint_caught()) @@ -2635,6 +2524,8 @@ static int _process_lv_vgnameid_list(struct cmd_context *cmd, uint32_t read_flag continue; } + already_locked = lvmcache_vgname_is_locked(vg_name); + vg = vg_read(cmd, vg_name, vg_uuid, read_flags, lockd_state); if (_ignore_vg(vg, vg_name, arg_vgnames, read_flags, &skip, ¬found)) { stack; @@ -2651,7 +2542,8 @@ static int _process_lv_vgnameid_list(struct cmd_context *cmd, uint32_t read_flag if (ret > ret_max) ret_max = ret; - unlock_vg(cmd, vg_name); + if (!already_locked) + unlock_vg(cmd, vg_name); endvg: release_vg(vg); if (!lockd_vg(cmd, vg_name, "un", 0, &lockd_state)) @@ -2736,7 +2628,7 @@ int process_each_lv(struct cmd_context *cmd, int argc, char **argv, uint32_t rea * . A VG name is specified which may refer to one * of multiple VGs on the system with that name. */ - log_very_verbose("Get list of VGs on system"); + log_debug("Get list of VGs on system"); if (!get_vgnameids(cmd, &vgnameids_on_system, NULL, 0)) { ret_max = ECMD_FAILED; @@ -2798,8 +2690,6 @@ static int _get_arg_pvnames(struct cmd_context *cmd, char *arg_name; int ret_max = ECMD_PROCESSED; - log_verbose("Using physical volume(s) on command line."); - for (; opt < argc; opt++) { arg_name = argv[opt]; @@ -2864,6 +2754,8 @@ static int _get_all_devices(struct cmd_context *cmd, struct dm_list *all_devices struct device_id_list *dil; int r = ECMD_FAILED; + log_debug("Getting list of all devices"); + lvmcache_seed_infos_from_lvmetad(cmd); if (!(iter = dev_iter_create(cmd->full_filter, 1))) { @@ -2989,6 +2881,8 @@ static int _process_device_list(struct cmd_context *cmd, struct dm_list *all_dev int ret_max = ECMD_PROCESSED; int ret = 0; + log_debug("Processing devices that are not PVs"); + /* * Pretend that each device is a PV with dummy values. * FIXME Formalise this extension or find an alternative. @@ -3226,6 +3120,7 @@ static int _process_pvs_in_vgs(struct cmd_context *cmd, uint32_t read_flags, int ret; int skip; int notfound; + int already_locked; dm_list_iterate_items(vgnl, all_vgnameids) { if (sigint_caught()) @@ -3241,6 +3136,10 @@ static int _process_pvs_in_vgs(struct cmd_context *cmd, uint32_t read_flags, continue; } + log_debug("Processing PVs in VG %s", vg_name); + + already_locked = lvmcache_vgname_is_locked(vg_name); + vg = vg_read(cmd, vg_name, vg_uuid, read_flags, lockd_state); if (_ignore_vg(vg, vg_name, NULL, read_flags, &skip, ¬found)) { stack; @@ -3265,7 +3164,7 @@ static int _process_pvs_in_vgs(struct cmd_context *cmd, uint32_t read_flags, if (ret > ret_max) ret_max = ret; - if (!skip) + if (!skip && !already_locked) unlock_vg(cmd, vg->name); endvg: release_vg(vg); @@ -3281,9 +3180,8 @@ endvg: } int process_each_pv(struct cmd_context *cmd, - int argc, char **argv, - const char *only_this_vgname, - uint32_t read_flags, + int argc, char **argv, const char *only_this_vgname, + int all_is_set, uint32_t read_flags, struct processing_handle *handle, process_single_pv_fn_t process_single_pv) { @@ -3296,9 +3194,12 @@ int process_each_pv(struct cmd_context *cmd, struct device_id_list *dil; int process_all_pvs; int process_all_devices; + int orphans_locked; int ret_max = ECMD_PROCESSED; int ret; + log_debug("Processing each PV"); + /* * When processing a specific VG name, warn if it's inconsistent and * print an error if it's not found. Otherwise we're processing all @@ -3335,10 +3236,11 @@ int process_each_pv(struct cmd_context *cmd, return ret; } + orphans_locked = lvmcache_vgname_is_locked(VG_ORPHANS); + process_all_pvs = dm_list_empty(&arg_pvnames) && dm_list_empty(&arg_tags); - process_all_devices = process_all_pvs && (cmd->command->flags & ENABLE_ALL_DEVS) && - arg_count(cmd, all_ARG); + process_all_devices = process_all_pvs && (cmd->command->flags & ENABLE_ALL_DEVS) && all_is_set; /* Needed for a current listing of the global VG namespace. */ if (!only_this_vgname && !lockd_gl(cmd, "sh", 0)) @@ -3348,10 +3250,13 @@ int process_each_pv(struct cmd_context *cmd, * This full scan would be done by _get_all_devices() if * it were not done here first. It's called here first * so that get_vgnameids() will look at any new devices. + * When orphans is already locked, these steps are done + * before process_each_pv is called. */ - if (!trust_cache()) { - dev_cache_full_scan(cmd->full_filter); + if (!trust_cache() && !orphans_locked) { + log_debug("Scanning for available devices"); lvmcache_destroy(cmd, 1, 0); + dev_cache_full_scan(cmd->full_filter); } /* @@ -3389,6 +3294,14 @@ int process_each_pv(struct cmd_context *cmd, ret_max = ret; /* + * If the orphans lock was held, there shouldn't be missed devices. If + * there were, we cannot clear the cache while holding the orphans lock + * anyway. + */ + if (orphans_locked) + goto skip_missed; + + /* * Some PVs may have been missed by the first search if another command * moved them at the same time. Repeat the search for only the * specific PVs missed. lvmcache needs clearing for a fresh search. @@ -3435,6 +3348,7 @@ int process_each_pv(struct cmd_context *cmd, } } +skip_missed: dm_list_iterate_items(dil, &arg_devices) { log_error("Failed to find physical volume \"%s\".", dev_name(dil->dev)); ret_max = ECMD_FAILED; @@ -3493,3 +3407,895 @@ int lvremove_single(struct cmd_context *cmd, struct logical_volume *lv, return ECMD_PROCESSED; } + +void pvcreate_each_params_set_defaults(struct pvcreate_each_params *pp) +{ + memset(pp, 0, sizeof(*pp)); + + pp->zero = 1; + pp->size = 0; + pp->data_alignment = UINT64_C(0); + pp->data_alignment_offset = UINT64_C(0); + pp->pvmetadatacopies = DEFAULT_PVMETADATACOPIES; + pp->pvmetadatasize = DEFAULT_PVMETADATASIZE; + pp->labelsector = DEFAULT_LABELSECTOR; + pp->force = PROMPT; + pp->yes = 0; + pp->metadataignore = DEFAULT_PVMETADATAIGNORE; + pp->restorefile = NULL; + pp->uuid_str = NULL; + pp->ba_start = 0; + pp->ba_size = 0; + pp->pe_start = PV_PE_START_CALC; + pp->extent_count = 0; + pp->extent_size = 0; + + dm_list_init(&pp->prompts); + dm_list_init(&pp->arg_devices); + dm_list_init(&pp->arg_confirm); + dm_list_init(&pp->arg_create); + dm_list_init(&pp->arg_fail); + dm_list_init(&pp->pvs); +} + +int pvcreate_each_params_from_args(struct cmd_context *cmd, struct pvcreate_each_params *pp) +{ + pp->yes = arg_count(cmd, yes_ARG); + pp->force = (force_t) arg_count(cmd, force_ARG); + + if (arg_int_value(cmd, labelsector_ARG, 0) >= LABEL_SCAN_SECTORS) { + log_error("labelsector must be less than %lu.", + LABEL_SCAN_SECTORS); + return 0; + } else { + pp->labelsector = arg_int64_value(cmd, labelsector_ARG, + DEFAULT_LABELSECTOR); + } + + if (!(cmd->fmt->features & FMT_MDAS) && + (arg_count(cmd, pvmetadatacopies_ARG) || + arg_count(cmd, metadatasize_ARG) || + arg_count(cmd, dataalignment_ARG) || + arg_count(cmd, dataalignmentoffset_ARG))) { + log_error("Metadata and data alignment parameters only " + "apply to text format."); + return 0; + } + + if (!(cmd->fmt->features & FMT_BAS) && + arg_count(cmd, bootloaderareasize_ARG)) { + log_error("Bootloader area parameters only " + "apply to text format."); + return 0; + } + + if (arg_count(cmd, metadataignore_ARG)) + pp->metadataignore = arg_int_value(cmd, metadataignore_ARG, + DEFAULT_PVMETADATAIGNORE); + else + pp->metadataignore = find_config_tree_bool(cmd, metadata_pvmetadataignore_CFG, NULL); + + if (arg_count(cmd, pvmetadatacopies_ARG) && + !arg_int_value(cmd, pvmetadatacopies_ARG, -1) && + pp->metadataignore) { + log_error("metadataignore only applies to metadatacopies > 0"); + return 0; + } + + pp->zero = arg_int_value(cmd, zero_ARG, 1); + + if (arg_sign_value(cmd, dataalignment_ARG, SIGN_NONE) == SIGN_MINUS) { + log_error("Physical volume data alignment may not be negative."); + return 0; + } + pp->data_alignment = arg_uint64_value(cmd, dataalignment_ARG, UINT64_C(0)); + + if (pp->data_alignment > UINT32_MAX) { + log_error("Physical volume data alignment is too big."); + return 0; + } + + if (arg_sign_value(cmd, dataalignmentoffset_ARG, SIGN_NONE) == SIGN_MINUS) { + log_error("Physical volume data alignment offset may not be negative"); + return 0; + } + pp->data_alignment_offset = arg_uint64_value(cmd, dataalignmentoffset_ARG, UINT64_C(0)); + + if (pp->data_alignment_offset > UINT32_MAX) { + log_error("Physical volume data alignment offset is too big."); + return 0; + } + + if ((pp->data_alignment + pp->data_alignment_offset) && + (pp->pe_start != PV_PE_START_CALC)) { + if ((pp->data_alignment ? pp->pe_start % pp->data_alignment : pp->pe_start) != pp->data_alignment_offset) { + log_warn("WARNING: Ignoring data alignment %s" + " incompatible with restored pe_start value %s)", + display_size(cmd, pp->data_alignment + pp->data_alignment_offset), + display_size(cmd, pp->pe_start)); + pp->data_alignment = 0; + pp->data_alignment_offset = 0; + } + } + + if (arg_sign_value(cmd, metadatasize_ARG, SIGN_NONE) == SIGN_MINUS) { + log_error("Metadata size may not be negative."); + return 0; + } + + if (arg_sign_value(cmd, bootloaderareasize_ARG, SIGN_NONE) == SIGN_MINUS) { + log_error("Bootloader area size may not be negative."); + return 0; + } + + pp->pvmetadatasize = arg_uint64_value(cmd, metadatasize_ARG, UINT64_C(0)); + if (!pp->pvmetadatasize) + pp->pvmetadatasize = find_config_tree_int(cmd, metadata_pvmetadatasize_CFG, NULL); + + pp->pvmetadatacopies = arg_int_value(cmd, pvmetadatacopies_ARG, -1); + if (pp->pvmetadatacopies < 0) + pp->pvmetadatacopies = find_config_tree_int(cmd, metadata_pvmetadatacopies_CFG, NULL); + + if (pp->pvmetadatacopies > 2) { + log_error("Metadatacopies may only be 0, 1 or 2"); + return 0; + } + + pp->ba_size = arg_uint64_value(cmd, bootloaderareasize_ARG, pp->ba_size); + + return 1; +} + +enum { + PROMPT_PVCREATE_PV_IN_VG = 1, +}; + +enum { + PROMPT_ANSWER_NO = 1, + PROMPT_ANSWER_YES = 2 +}; + +/* + * When a prompt entry is created, save any strings or info + * in this struct that are needed for the prompt messages. + * The VG/PV structs are not be available when the prompt + * is run. + */ +struct pvcreate_prompt { + struct dm_list list; + uint32_t type; + const char *pv_name; + const char *vg_name; + struct device *dev; + int answer; + unsigned abort_command : 1; +}; + +struct pvcreate_device { + struct dm_list list; + const char *name; + struct device *dev; + char pvid[ID_LEN + 1]; + const char *vg_name; + int wiped; + int orphan_type; + unsigned is_not_pv : 1; /* device is not a PV */ + unsigned is_orphan_pv : 1; /* device is an orphan PV */ + unsigned is_used_pv : 1; /* device is a PV used in a VG */ +}; + +static void _check_pvcreate_prompt(struct cmd_context *cmd, + struct pvcreate_each_params *pp, + struct pvcreate_prompt *prompt, + int ask) +{ + if (prompt->type == PROMPT_PVCREATE_PV_IN_VG) { + if (pp->force != DONT_PROMPT_OVERRIDE) { + prompt->answer = PROMPT_ANSWER_NO; + + /* FIXME: vgcreate/vgextend have different error messages than pvcreate. */ + if (!strcmp(command_name(cmd), "pvcreate")) { + log_error("Can't initialize physical volume \"%s\" of volume group \"%s\" without -ff", + prompt->pv_name, prompt->vg_name); + } else { + log_error("Physical volume '%s' is already in volume group '%s'", + prompt->pv_name, prompt->vg_name); + log_error("Unable to add physical volume '%s' to volume group '%s'", + prompt->pv_name, prompt->vg_name); + } + } else if (pp->yes) { + prompt->answer = PROMPT_ANSWER_YES; + } else if (ask) { + if (yes_no_prompt("Really INITIALIZE physical volume \"%s\" of volume group \"%s\" [y/n]? ", + prompt->pv_name, prompt->vg_name) == 'n') { + prompt->answer = PROMPT_ANSWER_NO; + log_error("%s: physical volume not initialized", prompt->pv_name); + } else { + prompt->answer = PROMPT_ANSWER_YES; + log_warn("WARNING: Forcing physical volume creation on %s of volume group \"%s\"", + prompt->pv_name, prompt->vg_name); + } + } + } +} + +static struct pvcreate_device *_pvcreate_list_find_dev(struct dm_list *devices, struct device *dev) +{ + struct pvcreate_device *pd; + + dm_list_iterate_items(pd, devices) { + if (pd->dev == dev) + return pd; + } + + return NULL; +} + +/* + * If this function decides that a arg_devices entry cannot be used, but the + * command might be able to continue without it, then it moves that entry from + * arg_devices to arg_fail. + * + * If this function decides that an arg_devices entry could be used (possibly + * requiring a prompt), then it moves the entry from arg_devices to arg_create. + * + * Any arg_devices entries that are not moved to arg_fail or arg_create were + * not found. The caller will decide if the command can continue if any + * arg_devices entries were not found, or if any were moved to arg_fail. + * + * This check does not need to look at PVs in foreign, shared or clustered VGs. + * If pvcreate/vgcreate/vgextend specifies a device in a + * foreign/shared/clustered VG, that VG will not be processed by this function, + * and the arg will be reported as not found. + */ + +static int _pvcreate_check1_single(struct cmd_context *cmd, + struct volume_group *vg, + struct physical_volume *pv, + struct processing_handle *handle) +{ + struct pvcreate_each_params *pp = (struct pvcreate_each_params *) handle->custom_handle; + struct pvcreate_device *pd; + struct pvcreate_prompt *prompt; + struct device *dev; + int found = 0; + + if (!pv->dev) + return 1; + + /* + * Check if one of the command args in arg_devices + * matches this device. + * + * (Possible optimization: the first time this _single + * function is called, we could iterate through all + * arg_devices entries, do the name to dev lookup + * with dev_cache_get() and set the pd->dev fields. + * Subsequent _single calls would just compare devs + * and not do any dev_cache_get(). This would avoid + * repeating dev_cache_get() for arg_devices entries.) + */ + dm_list_iterate_items(pd, &pp->arg_devices) { + dev = dev_cache_get(pd->name, cmd->full_filter); + if (dev != pv->dev) + continue; + + pd->dev = pv->dev; + if (pv->dev->pvid[0]) + strncpy(pd->pvid, pv->dev->pvid, ID_LEN); + found = 1; + break; + } + + /* + * Check if the uuid specified for the new PV is used by another PV. + */ + if (!found && pv->dev && pp->uuid_str && id_equal(&pv->id, &pp->id)) { + log_error("uuid %s already in use on \"%s\"", pp->uuid_str, pv_dev_name(pv)); + pp->check_failed = 1; + return 0; + } + + if (!found) + return 1; + + log_debug("Checking device %s for pvcreate %.32s", + pv_dev_name(pv), pv->dev->pvid[0] ? pv->dev->pvid : ""); + + /* + * This test will fail if the device belongs to an MD array. + */ + if (!dev_test_excl(pv->dev)) { + /* FIXME Detect whether device-mapper itself is still using it */ + log_error("Can't open %s exclusively. Mounted filesystem?", + pv_dev_name(pv)); + dm_list_move(&pp->arg_fail, &pd->list); + return 1; + } + + /* + * What kind of device is this: an orphan PV, an uninitialized/unused + * device, a PV used in a VG. + */ + + if (vg && !is_orphan_vg(vg->name)) { + /* Device is a PV used in a VG. */ + log_debug("Found pvcreate arg %s: pv is used in %s", pd->name, vg->name); + pd->is_used_pv = 1; + pd->vg_name = dm_pool_strdup(cmd->mem, vg->name); + } else if (vg && is_orphan_vg(vg->name)) { + /* Device is an orphan PV. */ + log_debug("Found pvcreate arg %s: pv is orphan in %s", pd->name, vg->name); + pd->is_orphan_pv = 1; + if (!strcmp(vg->name, FMT_TEXT_ORPHAN_VG_NAME)) + pd->orphan_type = 2; + else if (!strcmp(vg->name, FMT_LVM1_ORPHAN_VG_NAME)) + pd->orphan_type = 1; + } else { + log_debug("Found pvcreate arg %s: device is not a pv", pd->name); + /* Device is not a PV. */ + pd->is_not_pv = 1; + } + + /* + * pvcreate is being run on this device, and it's not a PV, + * or is an orphan PV. Neither case requires a prompt. + */ + if (pd->is_orphan_pv || pd->is_not_pv) { + pd->dev = pv->dev; + dm_list_move(&pp->arg_create, &pd->list); + return 1; + } + + /* + * pvcreate is being run on this device, but the device is already + * a PV in a VG. A prompt or force option is required to use it. + */ + + if (!(prompt = dm_pool_zalloc(cmd->mem, sizeof(*prompt)))) { + log_error("prompt alloc failed"); + pp->check_failed = 1; + return 0; + } + prompt->dev = pd->dev; + prompt->pv_name = dm_pool_strdup(cmd->mem, pd->name); + prompt->vg_name = dm_pool_strdup(cmd->mem, vg->name); + prompt->type = PROMPT_PVCREATE_PV_IN_VG; + dm_list_add(&pp->prompts, &prompt->list); + + pd->dev = pv->dev; + dm_list_move(&pp->arg_create, &pd->list); + return 1; +} + +/* + * This repeats the first check -- devices should be found, and should not have + * changed since the first check. If they were changed/used while the orphans + * lock was not held (during prompting), then they can't be used any more and + * are moved to arg_fail. If they are not found by this loop, that also + * disqualifies them from being used. Each arg_confirm entry that's found and + * is ok, is moved to arg_create. Those not found will remain in arg_confirm. + * + * This check does not need to look in foreign/shared/clustered VGs. If a + * device from arg_confirm was used in a foreign/shared/clustered VG during the + * prompts, then it will not be found during this check. + */ + +static int _pvcreate_check2_single(struct cmd_context *cmd, + struct volume_group *vg, + struct physical_volume *pv, + struct processing_handle *handle) +{ + struct pvcreate_each_params *pp = (struct pvcreate_each_params *) handle->custom_handle; + struct pvcreate_device *pd; + int found = 0; + + dm_list_iterate_items(pd, &pp->arg_confirm) { + if (pd->dev != pv->dev) + continue; + found = 1; + break; + } + + if (!found) + return 1; + + /* Repeat the same from check1. */ + if (!dev_test_excl(pv->dev)) { + /* FIXME Detect whether device-mapper itself is still using it */ + log_error("Can't open %s exclusively. Mounted filesystem?", + pv_dev_name(pv)); + goto fail; + } + + /* + * What kind of device is this: an orphan PV, an uninitialized/unused + * device, a PV used in a VG. + */ + + if (vg && !is_orphan_vg(vg->name)) { + /* Device is a PV used in a VG. */ + + if (pd->is_orphan_pv || pd->is_not_pv) { + /* In check1 it was an orphan or unused. */ + goto fail; + } + + if (pd->is_used_pv && pd->vg_name && strcmp(pd->vg_name, vg->name)) { + /* In check1 it was in a different VG. */ + goto fail; + } + + } else if (is_orphan(pv)) { + /* Device is an orphan PV. */ + + if (pd->is_not_pv) { + /* In check1 it was not a PV. */ + goto fail; + } + + if (pd->is_used_pv) { + /* In check1 it was in a VG. */ + goto fail; + } + + } else { + /* Device is not a PV. */ + + if (pd->is_orphan_pv) { + /* In check1 it was an orphan PV. */ + goto fail; + } + + if (pd->is_used_pv) { + /* In check1 it was in a VG. */ + goto fail; + } + } + + /* Device is unchanged from check1. */ + dm_list_move(&pp->arg_create, &pd->list); + return 1; + +fail: + log_error("Cannot use device %s: it changed during create.", pd->name); + dm_list_move(&pp->arg_fail, &pd->list); + return 1; +} + +/* + * This can be used by pvcreate, vgcreate and vgextend to create PVs. The + * callers need to set up the pvcreate_each_params structure based on command + * line args. This includes the pv_names field which specifies the devices to + * create PVs on. + * + * This uses process_each_pv() and should be called from a high level in the + * command -- the same level at which other instances of process_each are + * called. + * + * This function returns 0 (failed) if the caller requires all specified + * devices to be created, and any of those devices are not found, or any of + * them cannot be created. + * + * This function returns 1 (success) if the caller requires all specified + * devices to be created, and all are created, or if the caller does not + * require all specified devices to be created and one or more were created. + * + * When this function returns 1 (success), it returns to the caller with the + * VG_ORPHANS write lock held. + */ + +int pvcreate_each_device(struct cmd_context *cmd, + struct processing_handle *handle, + struct pvcreate_each_params *pp) +{ + struct pvcreate_restorable_params rp; + struct pvcreate_device *pd, *pd2; + struct pvcreate_prompt *prompt, *prompt2; + struct physical_volume *pv; + struct volume_group *orphan_vg; + struct pv_list *pvl; + struct pv_list *vgpvl; + const char *pv_name; + int consistent = 0; + int must_use_all = (cmd->command->flags & MUST_USE_ALL_ARGS); + int found; + int i; + + handle->custom_handle = pp; + + /* + * Create a list entry for each name arg. + */ + for (i = 0; i < pp->pv_count; i++) { + dm_unescape_colons_and_at_signs(pp->pv_names[i], NULL, NULL); + + pv_name = pp->pv_names[i]; + + if (!(pd = dm_pool_zalloc(cmd->mem, sizeof(*pd)))) { + log_error("alloc failed"); + return 0; + } + + if (!(pd->name = dm_pool_strdup(cmd->mem, pv_name))) { + log_error("strdup failed"); + return 0; + } + + dm_list_add(&pp->arg_devices, &pd->list); + } + + /* + * This function holds the orphans lock while reading VGs to look for + * devices. This means the orphans lock is held while VG locks are + * acquired, which is against lvmcache lock ordering rules, so disable + * the lvmcache lock ordering checks. + */ + lvmcache_lock_ordering(0); + + /* + * Clear the cache before acquiring the orphan lock. (Clearing the + * cache with locks held is an error.) We want the orphan lock + * acquired before process_each_pv. If the orphan lock is not held + * when process_each_pv is called, then process_each_pv clears the + * cache. + */ + lvmcache_destroy(cmd, 1, 0); + + /* + * If no prompts require a user response, this orphan lock is held + * throughout, and pvcreate_each_device() returns with it held so that + * vgcreate/vgextend use the PVs created here to add to a VG. + */ + if (!lock_vol(cmd, VG_ORPHANS, LCK_VG_WRITE, NULL)) { + log_error("Can't get lock for orphan PVs"); + return 0; + } + + dev_cache_full_scan(cmd->full_filter); + + /* + * Use process_each_pv to search all existing PVs and devices. + * + * This is a slightly different way to use process_each_pv, because the + * command args (arg_devices) are not being processed directly by + * process_each_pv (argc and argv are not passed). Instead, + * process_each_pv is processing all existing PVs and devices, and the + * single function is matching each of those against the command args + * (arg_devices). + * + * If an arg_devices entry is found during process_each_pv, it's moved + * to arg_create if it can be used, or arg_fail if it cannot be used. + * If it's added to arg_create but needs a prompt or force option, then + * a corresponding prompt entry is added to pp->prompts. + */ + process_each_pv(cmd, 0, NULL, NULL, 1, 0, handle, _pvcreate_check1_single); + + /* + * A fatal error was found while checking. + */ + if (pp->check_failed) + goto_bad; + + /* + * Check if all arg_devices were found by process_each_pv. + */ + dm_list_iterate_items(pd, &pp->arg_devices) + log_error("Device %s not found (or ignored by filtering).", pd->name); + + /* + * Can the command continue if some specified devices were not found? + */ + if (!dm_list_empty(&pp->arg_devices) && must_use_all) + goto_bad; + + /* + * Can the command continue if some specified devices cannot be used? + */ + if (!dm_list_empty(&pp->arg_fail) && must_use_all) + goto_bad; + + /* + * The command cannot continue if there are no devices to create. + */ + if (dm_list_empty(&pp->arg_create)) { + log_error("No devices found."); + goto_bad; + } + + /* + * Clear any prompts that have answers without asking the user. + */ + dm_list_iterate_items_safe(prompt, prompt2, &pp->prompts) { + _check_pvcreate_prompt(cmd, pp, prompt, 0); + + switch (prompt->answer) { + case PROMPT_ANSWER_YES: + /* The PV can be used, leave it on arg_create. */ + dm_list_del(&prompt->list); + break; + case PROMPT_ANSWER_NO: + /* The PV cannot be used, remove it from arg_create. */ + if ((pd = _pvcreate_list_find_dev(&pp->arg_create, prompt->dev))) + dm_list_move(&pp->arg_fail, &pd->list); + dm_list_del(&prompt->list); + break; + } + } + + if (!dm_list_empty(&pp->arg_fail) && must_use_all) + goto_bad; + + /* + * If no remaining prompts need a user response, then keep orphans + * locked and go directly to the create steps. + */ + if (dm_list_empty(&pp->prompts)) + goto do_creates; + + /* + * Prompts require asking the user, so release the orphans lock, ask + * the questions, reacquire the orphans lock, verify that the PVs were + * not used during the questions, then do the create steps. + */ + unlock_vg(cmd, VG_ORPHANS); + + /* + * Process prompts that require asking the user. The orphans lock is + * not held, so there's no harm in waiting for a user to respond. + */ + dm_list_iterate_items_safe(prompt, prompt2, &pp->prompts) { + _check_pvcreate_prompt(cmd, pp, prompt, 1); + + switch (prompt->answer) { + case PROMPT_ANSWER_YES: + /* The PV can be used, leave it on arg_create. */ + dm_list_del(&prompt->list); + break; + case PROMPT_ANSWER_NO: + /* The PV cannot be used, remove it from arg_create. */ + if ((pd = _pvcreate_list_find_dev(&pp->arg_create, prompt->dev))) + dm_list_move(&pp->arg_fail, &pd->list); + dm_list_del(&prompt->list); + break; + } + + if (!dm_list_empty(&pp->arg_fail) && must_use_all) + goto_out; + + if (sigint_caught()) + goto_out; + + if (prompt->abort_command) + goto_out; + } + + /* + * Clear the cache, reacquire the orphans write lock, then check again + * that the devices can still be used. If the second loop finds them + * changed, or can't find them any more, then they aren't used. + * Clear the cache here before locking orphans, since it won't be + * done by process_each_pv with orphans already locked. + */ + + lvmcache_destroy(cmd, 1, 0); + + if (!lock_vol(cmd, VG_ORPHANS, LCK_VG_WRITE, NULL)) { + log_error("Can't get lock for orphan PVs"); + goto_out; + } + + /* + * The device args began on the arg_devices list, then the first check + * loop moved those entries to arg_create as they were found. Devices + * not found during the first loop are not being used, and remain on + * arg_devices. + * + * Now, the arg_create entries are moved to arg_confirm, and the second + * check loop moves them back to arg_create as they are found and are + * unchanged. Like the first loop, the second loop moves an entry to + * arg_fail if it cannot be used. After the second loop, any devices + * remaining on arg_confirm were not found and are not used. + */ + + dm_list_splice(&pp->arg_confirm, &pp->arg_create); + + process_each_pv(cmd, 0, NULL, NULL, 1, 0, handle, _pvcreate_check2_single); + + dm_list_iterate_items(pd, &pp->arg_confirm) + log_error("Device %s not found (or ignored by filtering).", pd->name); + + /* Some devices were not found during the second check. */ + if (!dm_list_empty(&pp->arg_confirm) && must_use_all) + goto_bad; + + /* Some devices changed during the second check. */ + if (!dm_list_empty(&pp->arg_fail) && must_use_all) + goto_bad; + + if (dm_list_empty(&pp->arg_create)) { + log_error("No devices found."); + goto_bad; + } + +do_creates: + + /* + * Wipe signatures on devices being created. + */ + dm_list_iterate_items_safe(pd, pd2, &pp->arg_create) { + log_verbose("Wiping signatures on new PV %s", pd->name); + + if (!wipe_known_signatures(cmd, pd->dev, pd->name, TYPE_LVM1_MEMBER | TYPE_LVM2_MEMBER, + 0, pp->yes, pp->force, &pd->wiped)) { + dm_list_move(&pp->arg_fail, &pd->list); + } + + if (sigint_caught()) + goto_bad; + } + + if (!dm_list_empty(&pp->arg_fail) && must_use_all) + goto_bad; + + /* + * Find existing orphan PVs that vgcreate or vgextend want to use. + * "preserve_existing" means that the command wants to use existing PVs + * and not recreate a new PV on top of an existing PV. + */ + if (pp->preserve_existing) { + const char *orphan_vg_name; + + log_debug("Using existing orphan PVs"); + + dm_list_iterate_items(pd, &pp->arg_create) { + if (!pd->is_orphan_pv) + continue; + if (pd->orphan_type == 1) + orphan_vg_name = FMT_LVM1_ORPHAN_VG_NAME; + else if (pd->orphan_type == 2) + orphan_vg_name = FMT_TEXT_ORPHAN_VG_NAME; + log_debug("Using orphan PV %s from %s", pd->name, orphan_vg_name); + } + + if (!(orphan_vg = vg_read_internal(cmd, orphan_vg_name, NULL, 0, &consistent))) { + log_error("Cannot read orphans VG %s", orphan_vg_name); + goto_bad; + } + + dm_list_iterate_items_safe(pd, pd2, &pp->arg_create) { + if (!pd->is_orphan_pv) + continue; + + if (!(pvl = dm_pool_alloc(cmd->mem, sizeof(*pvl)))) { + log_error("alloc pvl failed"); + dm_list_move(&pp->arg_fail, &pd->list); + continue; + } + + found = 0; + dm_list_iterate_items(vgpvl, &orphan_vg->pvs) { + if (vgpvl->pv->dev == pd->dev) { + found = 1; + break; + } + } + + if (found) { + log_debug("Using existing orphan PV %s", pv_dev_name(vgpvl->pv)); + pvl->pv = vgpvl->pv; + dm_list_add(&pp->pvs, &pvl->list); + } else { + log_error("Failed to find PV %s", pd->name); + dm_list_move(&pp->arg_fail, &pd->list); + } + } + } + + /* + * Create PVs on devices. Either create a new PV on top of an existing + * one (e.g. for pvcreate), or create a new PV on a device that is not + * a PV. + */ + dm_list_iterate_items_safe(pd, pd2, &pp->arg_create) { + /* Using existing orphan PVs is covered above. */ + if (pp->preserve_existing && pd->is_orphan_pv) + continue; + + if (!dm_list_empty(&pp->arg_fail) && must_use_all) + break; + + if (!(pvl = dm_pool_alloc(cmd->mem, sizeof(*pvl)))) { + log_error("alloc pvl failed"); + dm_list_move(&pp->arg_fail, &pd->list); + } + + pv_name = pd->name; + + log_debug("Creating a new PV on %s", pv_name); + + /* FIXME: get rid of rp usage in pv_create to avoid this. */ + memset(&rp, 0, sizeof(rp)); + rp.restorefile = pp->restorefile; + if (pp->uuid_str) { + rp.id = pp->id; + rp.idp = &pp->id; + } + rp.ba_start = pp->ba_start; + rp.ba_size = pp->ba_size; + rp.pe_start = pp->pe_start; + rp.extent_count = pp->extent_count; + rp.extent_size = pp->extent_size; + + if (!(pv = pv_create(cmd, pd->dev, pp->size, pp->data_alignment, + pp->data_alignment_offset, pp->labelsector, + pp->pvmetadatacopies, pp->pvmetadatasize, + pp->metadataignore, &rp))) { + log_error("Failed to setup physical volume \"%s\"", pv_name); + dm_list_move(&pp->arg_fail, &pd->list); + continue; + } + + log_verbose("Set up physical volume for \"%s\" with %" PRIu64 + " available sectors", pv_name, pv_size(pv)); + + if (!label_remove(pv->dev)) { + log_error("Failed to wipe existing label on %s", pv_name); + dm_list_move(&pp->arg_fail, &pd->list); + continue; + } + + if (pp->zero) { + log_verbose("Zeroing start of device %s", pv_name); + + if (!dev_open_quiet(pv->dev)) { + log_error("%s not opened: device not zeroed", pv_name); + dm_list_move(&pp->arg_fail, &pd->list); + continue; + } + + if (!dev_set(pv->dev, UINT64_C(0), (size_t) 2048, 0)) { + log_error("%s not wiped: aborting", pv_name); + if (!dev_close(pv->dev)) + stack; + dm_list_move(&pp->arg_fail, &pd->list); + continue; + } + if (!dev_close(pv->dev)) + stack; + } + + log_verbose("Writing physical volume data to disk \"%s\"", pv_name); + + if (!pv_write(cmd, pv, 0)) { + log_error("Failed to write physical volume \"%s\"", pv_name); + dm_list_move(&pp->arg_fail, &pd->list); + } + + log_print_unless_silent("Physical volume \"%s\" successfully created", pv_name); + + pvl->pv = pv; + dm_list_add(&pp->pvs, &pvl->list); + } + + dm_list_iterate_items(pvl, &pp->pvs) + log_debug("pvcreate succeeded for %s", pv_dev_name(pvl->pv)); + + dm_list_iterate_items(pd, &pp->arg_fail) + log_debug("pvcreate failed for %s", pd->name); + + if (!dm_list_empty(&pp->arg_fail)) + goto_bad; + + /* + * Returns with VG_ORPHANS write lock held because vgcreate and + * vgextend want to use the newly created PVs. + */ + return 1; + +bad: + unlock_vg(cmd, VG_ORPHANS); +out: + return 0; +} + diff --git a/tools/toollib.h b/tools/toollib.h index e1ac323c4..32af82164 100644 --- a/tools/toollib.h +++ b/tools/toollib.h @@ -101,8 +101,8 @@ int process_each_vg(struct cmd_context *cmd, int argc, char **argv, struct processing_handle *handle, process_single_vg_fn_t process_single_vg); -int process_each_pv(struct cmd_context *cmd, int argc, char **argv, - const char *vg_name, uint32_t read_flags, +int process_each_pv(struct cmd_context *cmd, int argc, char **argv, const char *vg_name, + int all_is_set, uint32_t read_flags, struct processing_handle *handle, process_single_pv_fn_t process_single_pv); @@ -152,6 +152,10 @@ const char *extract_vgname(struct cmd_context *cmd, const char *lv_name); const char *skip_dev_dir(struct cmd_context *cmd, const char *vg_name, unsigned *dev_dir_found); +void pvcreate_each_params_set_defaults(struct pvcreate_each_params *pp); +int pvcreate_each_params_from_args(struct cmd_context *cmd, struct pvcreate_each_params *pp); +int pvcreate_each_device(struct cmd_context *cmd, struct processing_handle *handle, struct pvcreate_each_params *pp); + /* * Builds a list of pv's from the names in argv. Used in * lvcreate/extend. @@ -173,8 +177,6 @@ int lv_refresh(struct cmd_context *cmd, struct logical_volume *lv); int vg_refresh_visible(struct cmd_context *cmd, struct volume_group *vg); void lv_spawn_background_polling(struct cmd_context *cmd, struct logical_volume *lv); -int pvcreate_params_validate(struct cmd_context *cmd, int argc, - struct pvcreate_params *pp); int get_activation_monitoring_mode(struct cmd_context *cmd, int *monitoring_mode); diff --git a/tools/tools.h b/tools/tools.h index 47dd35e05..85facd6d9 100644 --- a/tools/tools.h +++ b/tools/tools.h @@ -107,6 +107,8 @@ struct arg_value_group_list { #define NO_METADATA_PROCESSING 0x00000040 /* Command wants to scan for new devices and force labels to be read from them all. */ #define REQUIRES_FULL_LABEL_SCAN 0x00000080 +/* Command must use all specified arg names and fail if all cannot be used. */ +#define MUST_USE_ALL_ARGS 0x00000100 /* a register of the lvm commands */ struct command { diff --git a/tools/vgcreate.c b/tools/vgcreate.c index fd7956382..dc6a3cb0d 100644 --- a/tools/vgcreate.c +++ b/tools/vgcreate.c @@ -17,14 +17,16 @@ int vgcreate(struct cmd_context *cmd, int argc, char **argv) { + struct processing_handle *handle; + struct pvcreate_each_params pp; struct vgcreate_params vp_new; struct vgcreate_params vp_def; struct volume_group *vg; const char *tag; const char *clustered_message = ""; char *vg_name; - struct pvcreate_params pp; struct arg_value_group_list *current_group; + uint32_t rc; if (!argc) { log_error("Please provide volume group name and " @@ -36,10 +38,19 @@ int vgcreate(struct cmd_context *cmd, int argc, char **argv) argc--; argv++; - pvcreate_params_set_defaults(&pp); - if (!pvcreate_params_validate(cmd, argc, &pp)) { + pvcreate_each_params_set_defaults(&pp); + + if (!pvcreate_each_params_from_args(cmd, &pp)) return EINVALID_CMD_LINE; - } + + pp.pv_count = argc; + pp.pv_names = argv; + + /* Don't create a new PV on top of an existing PV like pvcreate does. */ + pp.preserve_existing = 1; + + /* pvcreate within vgcreate cannot be forced. */ + pp.force = 0; if (!vgcreate_params_set_defaults(cmd, &vp_def, NULL)) return EINVALID_CMD_LINE; @@ -48,28 +59,64 @@ int vgcreate(struct cmd_context *cmd, int argc, char **argv) return EINVALID_CMD_LINE; if (!vgcreate_params_validate(cmd, &vp_new)) - return EINVALID_CMD_LINE; + return EINVALID_CMD_LINE; /* * Needed to change the global VG namespace, * and to change the set of orphan PVs. */ if (!lockd_gl_create(cmd, "ex", vp_new.lock_type)) - return ECMD_FAILED; + return_ECMD_FAILED; + cmd->lockd_gl_disable = 1; lvmcache_seed_infos_from_lvmetad(cmd); - /* Create the new VG */ - vg = vg_create(cmd, vp_new.vg_name); - if (vg_read_error(vg)) { - if (vg_read_error(vg) == FAILED_EXIST) + /* + * Check if the VG name already exists. This should be done before + * creating PVs on any of the devices. + */ + if ((rc = vg_lock_newname(cmd, vp_new.vg_name)) != SUCCESS) { + if (rc == FAILED_EXIST) log_error("A volume group called %s already exists.", vp_new.vg_name); else log_error("Can't get lock for %s.", vp_new.vg_name); - release_vg(vg); return ECMD_FAILED; } + /* + * FIXME: we have to unlock/relock the new VG name around the pvcreate + * step because pvcreate needs to destroy lvmcache, which doesn't allow + * any locks to be held. There shouldn't be any reason to require this + * VG lock to be released, so the lvmcache destroy rule about locks + * seems to be unwarranted here. + */ + unlock_vg(cmd, vp_new.vg_name); + + if (!(handle = init_processing_handle(cmd))) { + log_error("Failed to initialize processing handle."); + return ECMD_FAILED; + } + + if (!pvcreate_each_device(cmd, handle, &pp)) { + destroy_processing_handle(cmd, handle); + return_ECMD_FAILED; + } + + /* Relock the new VG name, see comment above. */ + if (!lock_vol(cmd, vp_new.vg_name, LCK_VG_WRITE, NULL)) { + destroy_processing_handle(cmd, handle); + return_ECMD_FAILED; + } + + /* + * pvcreate_each_device returns with the VG_ORPHANS write lock held, + * which was used to do pvcreate. Now to create the VG using those + * PVs, the VG lock will be taken (with the orphan lock already held.) + */ + + if (!(vg = vg_create(cmd, vp_new.vg_name))) + goto_bad; + if (vg->fid->fmt->features & FMT_CONFIG_PROFILE) vg->profile = vg->cmd->profile_params->global_metadata_profile; @@ -80,15 +127,10 @@ int vgcreate(struct cmd_context *cmd, int argc, char **argv) !vg_set_clustered(vg, vp_new.clustered) || !vg_set_system_id(vg, vp_new.system_id) || !vg_set_mda_copies(vg, vp_new.vgmetadatacopies)) - goto bad_orphan; - - if (!lock_vol(cmd, VG_ORPHANS, LCK_VG_WRITE, NULL)) { - log_error("Can't get lock for orphan PVs"); - goto bad_orphan; - } + goto_bad; /* attach the pv's */ - if (!vg_extend(vg, argc, (const char* const*)argv, &pp)) + if (!vg_extend_each_pv(vg, &pp)) goto_bad; if (vp_new.max_lv != vg->max_lv) @@ -176,12 +218,13 @@ int vgcreate(struct cmd_context *cmd, int argc, char **argv) } out: release_vg(vg); + destroy_processing_handle(cmd, handle); return ECMD_PROCESSED; bad: + unlock_vg(cmd, vp_new.vg_name); unlock_vg(cmd, VG_ORPHANS); -bad_orphan: release_vg(vg); - unlock_vg(cmd, vp_new.vg_name); + destroy_processing_handle(cmd, handle); return ECMD_FAILED; } diff --git a/tools/vgextend.c b/tools/vgextend.c index 761df7521..b44df72e0 100644 --- a/tools/vgextend.c +++ b/tools/vgextend.c @@ -16,9 +16,7 @@ #include "tools.h" struct vgextend_params { - struct pvcreate_params pp; - int pv_count; - const char *const *pv_names; + struct pvcreate_each_params pp; }; static int _restore_pv(struct volume_group *vg, const char *pv_name) @@ -49,14 +47,15 @@ static int _vgextend_restoremissing(struct cmd_context *cmd __attribute__((unuse struct processing_handle *handle) { struct vgextend_params *vp = (struct vgextend_params *) handle->custom_handle; + struct pvcreate_each_params *pp = &vp->pp; int fixed = 0; int i; if (!archive(vg)) return_0; - for (i = 0; i < vp->pv_count; i++) - if (_restore_pv(vg, vp->pv_names[i])) + for (i = 0; i < pp->pv_count; i++) + if (_restore_pv(vg, pp->pv_names[i])) fixed++; if (!fixed) { @@ -78,7 +77,7 @@ static int _vgextend_single(struct cmd_context *cmd, const char *vg_name, struct volume_group *vg, struct processing_handle *handle) { struct vgextend_params *vp = (struct vgextend_params *) handle->custom_handle; - struct pvcreate_params *pp = &vp->pp; + struct pvcreate_each_params *pp = &vp->pp; uint32_t mda_copies; uint32_t mda_used; int ret = ECMD_FAILED; @@ -94,12 +93,7 @@ static int _vgextend_single(struct cmd_context *cmd, const char *vg_name, if (!archive(vg)) return_ECMD_FAILED; - if (!lock_vol(cmd, VG_ORPHANS, LCK_VG_WRITE, NULL)) { - log_error("Can't get lock for orphan PVs"); - return ECMD_FAILED; - } - - if (!vg_extend(vg, vp->pv_count, vp->pv_names, pp)) + if (!vg_extend_each_pv(vg, pp)) goto_out; if (arg_count(cmd, metadataignore_ARG)) { @@ -114,7 +108,7 @@ static int _vgextend_single(struct cmd_context *cmd, const char *vg_name, } } - log_verbose("Volume group \"%s\" will be extended by %d new physical volumes", vg_name, vp->pv_count); + log_verbose("Volume group \"%s\" will be extended by %d new physical volumes", vg_name, pp->pv_count); if (!vg_write(vg) || !vg_commit(vg)) goto_out; @@ -123,19 +117,17 @@ static int _vgextend_single(struct cmd_context *cmd, const char *vg_name, log_print_unless_silent("Volume group \"%s\" successfully extended", vg_name); ret = ECMD_PROCESSED; - out: - unlock_vg(cmd, VG_ORPHANS); - return ret; } int vgextend(struct cmd_context *cmd, int argc, char **argv) { + struct processing_handle *handle; struct vgextend_params vp; + struct pvcreate_each_params *pp = &vp.pp; unsigned restoremissing = arg_is_set(cmd, restoremissing_ARG); - struct processing_handle *handle; - const char *one_vgname; + const char *vg_name; int ret; if (!argc) { @@ -144,27 +136,56 @@ int vgextend(struct cmd_context *cmd, int argc, char **argv) return EINVALID_CMD_LINE; } - one_vgname = skip_dev_dir(cmd, argv[0], NULL); - if (arg_count(cmd, metadatacopies_ARG)) { log_error("Invalid option --metadatacopies, " "use --pvmetadatacopies instead."); return EINVALID_CMD_LINE; } - pvcreate_params_set_defaults(&vp.pp); - vp.pv_count = argc - 1; - vp.pv_names = (const char* const*)(argv + 1); + vg_name = skip_dev_dir(cmd, argv[0], NULL); + argc--; + argv++; - if (!pvcreate_params_validate(cmd, vp.pv_count, &vp.pp)) - return_EINVALID_CMD_LINE; + pvcreate_each_params_set_defaults(pp); - if (!(handle = init_processing_handle(cmd))) { - log_error("Failed to initialize processing handle."); - return ECMD_FAILED; - } + if (!pvcreate_each_params_from_args(cmd, pp)) + return EINVALID_CMD_LINE; - handle->custom_handle = &vp; + pp->pv_count = argc; + pp->pv_names = argv; + + /* Don't create a new PV on top of an existing PV like pvcreate does. */ + pp->preserve_existing = 1; + + /* pvcreate within vgextend cannot be forced. */ + pp->force = 0; + + /* + * Needed to change the set of orphan PVs. + * (disable afterward to prevent process_each_pv from doing + * a shared global lock since it's already acquired it ex.) + */ + if (!lockd_gl(cmd, "ex", 0)) + return_ECMD_FAILED; + cmd->lockd_gl_disable = 1; + + if (!(handle = init_processing_handle(cmd))) { + log_error("Failed to initialize processing handle."); + return ECMD_FAILED; + } + + if (!restoremissing) { + if (!pvcreate_each_device(cmd, handle, pp)) { + destroy_processing_handle(cmd, handle); + return_ECMD_FAILED; + } + } + + /* + * pvcreate_each_device returns with the VG_ORPHANS write lock held, + * which was used to do pvcreate. Now to create the VG using those + * PVs, the VG lock will be taken (with the orphan lock already held.) + */ /* * It is always ok to add new PVs to a VG - even if there are @@ -174,15 +195,15 @@ int vgextend(struct cmd_context *cmd, int argc, char **argv) */ cmd->handles_missing_pvs = 1; - /* Needed to change the set of orphan PVs. */ - if (!lockd_gl(cmd, "ex", 0)) - return_ECMD_FAILED; + handle->custom_handle = &vp; - ret = process_each_vg(cmd, 0, NULL, one_vgname, + ret = process_each_vg(cmd, 0, NULL, vg_name, READ_FOR_UPDATE, handle, restoremissing ? &_vgextend_restoremissing : &_vgextend_single); destroy_processing_handle(cmd, handle); + if (!restoremissing) + unlock_vg(cmd, VG_ORPHANS); return ret; } diff --git a/tools/vgreduce.c b/tools/vgreduce.c index 5d43c94e1..bc95c3a8d 100644 --- a/tools/vgreduce.c +++ b/tools/vgreduce.c @@ -188,7 +188,7 @@ int vgreduce(struct cmd_context *cmd, int argc, char **argv) /* FIXME: Pass private struct through to all these functions */ /* and update in batch afterwards? */ return process_each_pv(cmd, argc, argv, vg_name, - READ_FOR_UPDATE, NULL, + 0, READ_FOR_UPDATE, NULL, _vgreduce_single); log_verbose("Finding volume group \"%s\"", vg_name); diff --git a/tools/vgsplit.c b/tools/vgsplit.c index 7605bc4b0..f5e7db58d 100644 --- a/tools/vgsplit.c +++ b/tools/vgsplit.c @@ -413,7 +413,7 @@ static struct volume_group *_vgsplit_to(struct cmd_context *cmd, * we obtained a WRITE lock and could not find the vgname in the * system. Thus, the split will be into a new VG. */ - vg_to = vg_create(cmd, vg_name_to); + vg_to = vg_lock_and_create(cmd, vg_name_to); if (vg_read_error(vg_to) == FAILED_LOCKING) { log_error("Can't get lock for %s", vg_name_to); release_vg(vg_to); @@ -526,8 +526,8 @@ int vgsplit(struct cmd_context *cmd, int argc, char **argv) return_ECMD_FAILED; /* * Set metadata format of original VG. - * NOTE: We must set the format before calling vg_create() - * since vg_create() calls the per-format constructor. + * NOTE: We must set the format before calling vg_lock_and_create() + * since vg_lock_and_create() calls the per-format constructor. */ cmd->fmt = vg_from->fid->fmt; |