summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Teigland <teigland@redhat.com>2015-12-15 12:55:48 -0600
committerDavid Teigland <teigland@redhat.com>2016-01-07 13:39:25 -0600
commit0b1526eae1d5803549720f403d5324fdd8f758ad (patch)
tree4dedf17839a64fa8f3d2fbfac3386eebfa794d77
parent4304a95dfdb4791e23dbd4b0280c667eba5cb664 (diff)
downloadlvm2-dev-dct-pvcreate-8.tar.gz
pvcreate, vgcreate, vgextend: restructuring to use toollibdev-dct-pvcreate-8
- Pull out the hidden equivalent of process_each_pv into an actual top level process_each_pv. - Pull the prompts to the top level, and do not run any prompts while locks are held. The orphan lock is reacquired after the prompts are done, and the devices being created are checked for any change made while the lock was not held. Previously, pvcreate_vol() was the shared function for creating a PV for pvcreate, vgcreate, vgextend. Now, it will be toollib function pvcreate_each_device(). pvcreate_vol() was called effectively as a helper, from within vgcreate and vgextend code paths. pvcreate_each_device() will be called at the same level as other process_each functions. One of the main problems with pvcreate_vol() is that it included a hidden equivalent of process_each_pv for each device being created: pvcreate_vol() -> _pvcreate_check() -> find_pv_by_name() -> get_pvs() -> get_pvs_internal() -> _get_pvs() -> get_vgids() -> /* equivalent to process_each_pv */ dm_list_iterate_items(vgids) vg = vg_read_internal() dm_list_iterate_items(&vg->pvs) pvcreate_each_device() reorganizes the code so that each-VG-each-PV loop is done once, and uses the standard process_each_pv function at the top level of the function.
-rw-r--r--lib/metadata/metadata-exported.h55
-rw-r--r--lib/metadata/metadata.c60
-rw-r--r--lib/metadata/pv_manip.c20
-rw-r--r--lib/metadata/vg.c1
-rw-r--r--lib/metadata/vg.h7
-rw-r--r--liblvm/lvm_pv.c12
-rw-r--r--tools/commands.h4
-rw-r--r--tools/pvchange.c2
-rw-r--r--tools/pvcreate.c138
-rw-r--r--tools/pvdisplay.c5
-rw-r--r--tools/pvresize.c2
-rw-r--r--tools/pvscan.c2
-rw-r--r--tools/reporter.c6
-rw-r--r--tools/toollib.c854
-rw-r--r--tools/toollib.h10
-rw-r--r--tools/tools.h2
-rw-r--r--tools/vgcreate.c54
-rw-r--r--tools/vgextend.c83
-rw-r--r--tools/vgreduce.c2
19 files changed, 1029 insertions, 290 deletions
diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h
index 8242db17d..fb61bbf77 100644
--- a/lib/metadata/metadata-exported.h
+++ b/lib/metadata/metadata-exported.h
@@ -535,6 +535,59 @@ 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_create; /* pvcreate_device, processing from arg_devices */
+ struct dm_list arg_fail; /* pvcreate_device, cannot process */
+ struct dm_list pvs; /* pv_list */
+ 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;
@@ -637,7 +690,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);
@@ -699,6 +751,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 a1d2e2b49..aeb56a4bf 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;
@@ -274,16 +273,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;
}
@@ -743,6 +732,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;
@@ -1005,6 +1031,8 @@ int vg_has_unknown_segments(const struct volume_group *vg)
return 0;
}
+static uint32_t vg_lock_newname(struct cmd_context *cmd, const char *vgname);
+
/*
* Create a VG with default parameters.
* Returns:
@@ -1572,7 +1600,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;
@@ -3012,7 +3039,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;
@@ -3068,12 +3094,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) {
@@ -5174,7 +5194,7 @@ uint32_t vg_read_error(struct volume_group *vg_handle)
* FAILED_EXIST - VG name already exists - cannot reserve
* SUCCESS - VG name does not exist in system and WRITE lock held
*/
-uint32_t vg_lock_newname(struct cmd_context *cmd, const char *vgname)
+static uint32_t vg_lock_newname(struct cmd_context *cmd, const char *vgname)
{
if (!lock_vol(cmd, vgname, LCK_VG_WRITE, NULL)) {
return FAILED_LOCKING;
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, &params->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, &params->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/tools/commands.h b/tools/commands.h
index a10d8a3b5..59c84c8e0 100644
--- a/tools/commands.h
+++ b/tools/commands.h
@@ -1119,7 +1119,7 @@ xx(vgconvert,
xx(vgcreate,
"Create a volume group",
- 0,
+ MUST_USE_ALL_ARGS,
"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,
"vgextend\n"
"\t[-A|--autobackup y|n]\n"
"\t[--restoremissing]\n"
diff --git a/tools/pvchange.c b/tools/pvchange.c
index 91e93c1e7..da5a4a86a 100644
--- a/tools/pvchange.c
+++ b/tools/pvchange.c
@@ -213,7 +213,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..94323db11 100644
--- a/tools/pvcreate.c
+++ b/tools/pvcreate.c
@@ -22,13 +22,12 @@
* 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 +47,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, uuid))
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 +64,98 @@ 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;
+ const char *uuid;
+
+ /*
+ * When restoring a PV, params need to be read from a backup file.
+ */
+ if (!pp->restorefile)
+ return 1;
+
+ uuid = arg_str_value(cmd, uuidstr_ARG, "");
+
+ 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",
+ uuid, 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);
+ /*
+ * Five kinds of pvcreate param values:
+ * 1. defaults
+ * 2. normal command line args
+ * 3. recovery-related command line args
+ * 4. recovery-related args from backup file
+ * 5. argc/argv free args specifying devices
+ */
- if (!pvcreate_restore_params_validate(cmd, argc, argv, &pp)) {
+ pvcreate_each_params_set_defaults(&pp);
+
+ if (!pvcreate_each_params_from_args(cmd, &pp))
return EINVALID_CMD_LINE;
- }
- if (!pvcreate_params_validate(cmd, argc, &pp)) {
+
+ if (!pvcreate_each_restore_params_from_args(cmd, argc, &pp))
+ return EINVALID_CMD_LINE;
+
+ if (!pvcreate_each_restore_params_from_backup(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 {
+ ret = ECMD_PROCESSED;
+ unlock_vg(cmd, VG_ORPHANS);
}
+ 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/pvresize.c b/tools/pvresize.c
index 0b055e6ef..3ee08b954 100644
--- a/tools/pvresize.c
+++ b/tools/pvresize.c
@@ -84,7 +84,7 @@ int pvresize(struct cmd_context *cmd, int argc, char **argv)
handle->custom_handle = &params;
- 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 68a074e02..b8e8a75d7 100644
--- a/tools/pvscan.c
+++ b/tools/pvscan.c
@@ -413,7 +413,7 @@ int pvscan(struct cmd_context *cmd, int argc, char **argv)
handle->custom_handle = &params;
- 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 728e61d4d..8dab3c612 100644
--- a/tools/toollib.c
+++ b/tools/toollib.c
@@ -1136,121 +1136,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 +1622,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))) {
@@ -2482,8 +2365,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;
@@ -2798,8 +2679,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 +2743,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_very_verbose("Getting list of all devices");
+
lvmcache_seed_infos_from_lvmetad(cmd);
if (!(iter = dev_iter_create(cmd->full_filter, 1))) {
@@ -2989,6 +2870,8 @@ static int _process_device_list(struct cmd_context *cmd, struct dm_list *all_dev
int ret_max = ECMD_PROCESSED;
int ret = 0;
+ log_verbose("Processing devices that are not PVs");
+
/*
* Pretend that each device is a PV with dummy values.
* FIXME Formalise this extension or find an alternative.
@@ -3246,6 +3129,8 @@ static int _process_pvs_in_vgs(struct cmd_context *cmd, uint32_t read_flags,
continue;
}
+ log_verbose("Processing PVs in VG %s", vg_name);
+
vg = vg_read(cmd, vg_name, vg_uuid, read_flags, lockd_state);
if (_ignore_vg(vg, vg_name, NULL, read_flags, &skip, &notfound)) {
stack;
@@ -3286,9 +3171,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)
{
@@ -3342,8 +3226,7 @@ int process_each_pv(struct cmd_context *cmd,
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))
@@ -3355,6 +3238,7 @@ int process_each_pv(struct cmd_context *cmd,
* so that get_vgnameids() will look at any new devices.
*/
if (!trust_cache()) {
+ log_verbose("Scanning for available devices");
dev_cache_full_scan(cmd->full_filter);
lvmcache_destroy(cmd, 1, 0);
}
@@ -3498,3 +3382,721 @@ 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_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,
+};
+
+/*
+ * 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;
+ unsigned answer : 1;
+ unsigned abort_command : 1;
+};
+
+struct pvcreate_device {
+ struct dm_list list;
+ const char *name;
+ struct device *dev;
+ char pvid[ID_LEN + 1];
+ int wiped;
+ int found_existing_label;
+};
+
+static void _run_pvcreate_prompt(struct cmd_context *cmd,
+ struct pvcreate_each_params *pp,
+ struct pvcreate_prompt *prompt)
+{
+ if (prompt->type == PROMPT_PVCREATE_PV_IN_VG) {
+ if (pp->force != DONT_PROMPT_OVERRIDE) {
+ prompt->answer = 0;
+
+ /* 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 = 1;
+ } else {
+ if (yes_no_prompt("Really INITIALIZE physical volume \"%s\" of volume group \"%s\" [y/n]? ",
+ prompt->pv_name, prompt->vg_name) == 'n') {
+ prompt->answer = 0;
+ log_error("%s: physical volume not initialized", prompt->pv_name);
+ } else {
+ prompt->answer = 1;
+ 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 and returns 1 (success).
+ *
+ * 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
+ * and returns 1 (success).
+ *
+ * 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.
+ */
+
+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;
+
+ /*
+ * Check if pv uuid matches a uuid specified by the command.
+ */
+ if (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;
+ }
+
+ /*
+ * 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;
+ }
+
+ /*
+ * The command is not interested in this device.
+ */
+ if (!found)
+ return 1;
+
+ log_verbose("Checking device %s for pvcreate %s",
+ 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;
+ }
+
+ if (is_orphan(pv))
+ pd->found_existing_label = 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 (!vg || is_orphan(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;
+}
+
+static int _pvcreate_check2_single(struct cmd_context *cmd,
+ const char *vg_name,
+ struct volume_group *vg,
+ struct processing_handle *handle)
+{
+ struct pvcreate_each_params *pp = (struct pvcreate_each_params *) handle->custom_handle;
+ struct pvcreate_device *pd, *pd2;
+ struct pv_list *pvl;
+
+ /*
+ * Ensure arg_create entries were not used in a real VG since the first
+ * check.
+ */
+ dm_list_iterate_items_safe(pd, pd2, &pp->arg_create) {
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ if (pvl->pv->dev != pd->dev)
+ continue;
+
+ log_error("Cannot use device %s: PV was changed during command.", pd->name);
+ dm_list_move(&pp->arg_fail, &pd->list);
+ break;
+ }
+ }
+ 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);
+ }
+
+ /*
+ * process all existing PVs and devices (not just args).
+ *
+ * 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, 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.
+ */
+ cmd->command->flags |= ENABLE_ALL_DEVS;
+
+ /*
+ * FIXME: when checking against existing PVs, we don't want to
+ * skip foreign, clustered or shared VGs.
+ */
+ process_each_pv(cmd, 0, NULL, NULL, 1, 0, handle, _pvcreate_check1_single);
+
+ log_debug("Checking devices for pvcreate found %u to use", dm_list_size(&pp->arg_create));
+
+ /*
+ * A fatal error was found while checking.
+ */
+ if (pp->check_failed)
+ goto_out;
+
+ /*
+ * 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_out;
+
+ /*
+ * Can the command continue if some specified devices cannot be used?
+ */
+ if (!dm_list_empty(&pp->arg_fail) && must_use_all)
+ goto_out;
+
+ /*
+ * The command cannot continue if there are no devices to create.
+ */
+ if (dm_list_empty(&pp->arg_create)) {
+ log_error("No devices found.");
+ goto_out;
+ }
+
+ /*
+ * Process any prompts that were gathered during the process_each_pv
+ * checks. Options can override/force/skip prompts.
+ */
+ dm_list_iterate_items_safe(prompt, prompt2, &pp->prompts) {
+ _run_pvcreate_prompt(cmd, pp, prompt);
+
+ if (!prompt->answer) {
+ /* Remove the corresponding entry from arg_create. */
+
+ if ((pd = _pvcreate_list_find_dev(&pp->arg_create, prompt->dev))) {
+ dm_list_move(&pp->arg_fail, &pd->list);
+ } else {
+ /* should never happen */
+ goto_out;
+ }
+ }
+
+ if (!dm_list_empty(&pp->arg_fail) && must_use_all)
+ goto_out;
+
+ if (sigint_caught())
+ goto_out;
+
+ if (prompt->abort_command)
+ goto_out;
+ }
+
+ /*
+ * The orphans lock was acquired and released during the
+ * process_each_pv read/check, and is now reacquired to write. The
+ * devices being created could have been changed while the lock was not
+ * held (during the prompts). So, each device in arg_create should now
+ * be checked again to verify it was not changed while the lock was not
+ * held. lvmcache is destroyed after the prompts, and orphans are
+ * reread here. The pvids from the rescanned info are compared with
+ * the pvids seen during the check.
+ *
+ * FIXME: a possible optimization might be to take the VG_ORPHANS write
+ * lock before the check1 loop, then if no prompts are collected during
+ * check1, skip the check2 loop. If prompts were found, then the
+ * orphans lock would be released, prompts processed, the orphans lock
+ * reacquired, and the check2 loop run. This would especially help
+ * vgcreate/vgextend which never use pvcreate prompts.
+ */
+
+ lvmcache_destroy(cmd, 1, 0);
+
+ /*
+ * The lock ordering rules do not want the orphans lock held while VG
+ * locks are taken, so the ordering check is disabled. With the
+ * orphans lock held, process_each_vg locks/unlocks each VG, and then
+ * vgcreate/vgextend will also lock the VG being extended to write it.
+ */
+ lvmcache_lock_ordering(0);
+
+ if (!lock_vol(cmd, VG_ORPHANS, LCK_VG_WRITE, NULL)) {
+ log_error("Can't get lock for orphan PVs");
+ goto_out;
+ }
+
+ /*
+ * Check if the arg_create entries were used during the prompts while
+ * the orphans lock was not held. A PV with no MDAs could have been
+ * used in a VG during prompts, and we wouldn't be able to tell without
+ * reading all VGs.
+ */
+ cmd->command->flags |= ALL_VGS_IS_DEFAULT;
+
+ /*
+ * FIXME: when checking if a PV is used, we don't want to skip
+ * foreign or cluster or lockd VGs.
+ */
+ process_each_vg(cmd, 0, NULL, NULL, 0, handle, _pvcreate_check2_single);
+
+ if (!dm_list_empty(&pp->arg_fail) && must_use_all)
+ goto_bad;
+
+ /*
+ * 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 (!dm_list_empty(&pp->arg_fail) && must_use_all)
+ goto_bad;
+
+ if (sigint_caught())
+ goto_bad;
+ }
+
+ /*
+ * pvcreate does not preserve existing PVs, and always writes a new PV
+ * over an existing orphan.
+ */
+ if (!pp->preserve_existing)
+ goto do_create;
+
+ /*
+ * 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.
+ */
+ log_verbose("Using existing orphan PVs");
+
+ if (!(orphan_vg = vg_read_internal(cmd, cmd->fmt->orphan_vg_name, NULL, 0, &consistent))) {
+ log_error("Cannot read orphans");
+ goto_bad;
+ }
+
+ dm_list_iterate_items_safe(pd, pd2, &pp->arg_create) {
+ if (!pd->found_existing_label)
+ 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_verbose("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);
+ }
+ }
+
+do_create:
+ /*
+ * 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) {
+ if (pp->preserve_existing && pd->found_existing_label)
+ 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_verbose("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);
+ }
+
+ if (!dm_list_empty(&pp->arg_fail) && must_use_all)
+ goto_bad;
+
+ dm_list_iterate_items(pvl, &pp->pvs) {
+ log_verbose("pvcreate complete for %s", pv_dev_name(pvl->pv));
+ }
+
+ /*
+ * 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 400bac520..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 lock_type,
+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 4179cc8f4..de7064970 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..2bbb87cb2 100644
--- a/tools/vgcreate.c
+++ b/tools/vgcreate.c
@@ -17,13 +17,14 @@
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;
if (!argc) {
@@ -36,10 +37,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,14 +58,30 @@ 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;
+
+ 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;
+ }
+
+ /*
+ * 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.)
+ */
lvmcache_seed_infos_from_lvmetad(cmd);
@@ -66,8 +92,7 @@ int vgcreate(struct cmd_context *cmd, int argc, char **argv)
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;
+ goto bad_read;
}
if (vg->fid->fmt->features & FMT_CONFIG_PROFILE)
@@ -80,15 +105,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 +196,14 @@ 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);
+bad_read:
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..eda326edd 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,54 @@ 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++;
+
+ 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;
- if (!pvcreate_params_validate(cmd, vp.pv_count, &vp.pp))
- return_EINVALID_CMD_LINE;
+ /* pvcreate within vgextend cannot be forced. */
+ pp->force = 0;
- if (!(handle = init_processing_handle(cmd))) {
+ /*
+ * 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;
+ return ECMD_FAILED;
}
- handle->custom_handle = &vp;
+ 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 +193,13 @@ 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);
-
+ 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);