vgid = vgname;
}
- /* FIXME: remove this, it shouldn't be needed */
- /* If PV without mdas is already in a real VG, don't make it orphan */
- if (is_orphan_vg(vgname) && info->vginfo &&
- mdas_empty_or_ignored(&info->mdas) &&
- !is_orphan_vg(info->vginfo->vgname) && critical_section())
+ /*
+ * This happens when vgremove does pv_write to make a PV
+ * that was previously part of a VG into a new orphan.
+ * FIXME: change pv_write to not use or update lvmcache,
+ * which should only be updated by label_scan.
+ */
+ if (is_orphan_vg(vgname) && info->vginfo && !is_orphan_vg(info->vginfo->vgname)) {
+ log_debug("lvmcache change %s to orphan from previous VG %s.",
+ dev_name(info->dev), info->vginfo->vgname);
return 1;
+ }
/*
* Creates a new vginfo struct for this vgname/vgid if none exists,
* using the 'vg'.
*/
-int lvmcache_update_vg_from_read(struct volume_group *vg, unsigned precommitted)
+int lvmcache_update_vg_from_read(struct volume_group *vg)
{
char pvid[ID_LEN + 1] __attribute__((aligned(8))) = { 0 };
char vgid[ID_LEN + 1] __attribute__((aligned(8))) = { 0 };
return 0;
}
+ log_debug_cache("lvmcache_update_vg %s vginfo from metadata", vg->name);
+
/*
* The label scan doesn't know when a PV with old metadata has been
* removed from the VG. Now with the vg we can tell, so remove the
continue;
}
- log_debug_cache("lvmcache_update_vg %s for info %s",
- vg->name, dev_name(info->dev));
+ /*
+ * If this PV info is already attached to a different VG, don't
+ * override that. The info/vginfo map a PV to a VG based on the
+ * metadata which appears on the PV itself. That has precedence
+ * over a different mapping of PV to another VG (the vg arg here)
+ * which is likely outdated metadata from some other device.
+ */
+ if (info->vginfo && !is_orphan_vg(info->vginfo->vgname) &&
+ (strcmp(info->vginfo->vgname, vg->name) || memcmp(info->vginfo->vgid, &vg->id, ID_LEN))) {
+ log_warn("WARNING: PV %s %s belongs to VG %s, ignoring claim from VG %s.",
+ dev_name(info->dev), pvid, info->vginfo->vgname, vg->name);
+ continue;
+ }
+
+ log_debug_cache("lvmcache_update_vg %s for %s", vg->name, dev_name(info->dev));
/*
* FIXME: use a different function that just attaches info's that
return false;
}
+/*
+ * Metadata is being processed which shows 'vg' containing 'pv'.
+ * Verify that this is consistent with the headers/metadata that
+ * were scanned from PV. The headers/metadata scanned from the
+ * actual PV could be different from what 'vg' metadata claims,
+ * if the 'vg' metadata is old/outdated.
+ */
+
+int lvmcache_verify_info_in_vg(struct volume_group *vg, struct lvmcache_info *info)
+{
+ char vgid[ID_LEN + 1] __attribute__((aligned(8))) = { 0 };
+
+ memcpy(vgid, &vg->id, ID_LEN);
+
+ if (!info->dev) {
+ log_error(INTERNAL_ERROR "Verify PV info in %s: skip, no dev", vg->name);
+ return 1;
+ }
+
+ if (!info->dev->pvid) {
+ log_debug("Verify PV %s in %s: uncertain, no pvid",
+ dev_name(info->dev), vg->name);
+ return 1;
+ }
+
+ if (!info->vginfo) {
+ log_debug("Verify PV %s %s in %s: uncertain, no vginfo",
+ info->dev->pvid, dev_name(info->dev), vg->name);
+ return 1;
+ }
+
+ if (strcmp(vg->name, info->vginfo->vgname)) {
+ log_debug("Verify PV %s %s in %s: fail, other VG %s",
+ info->dev->pvid, dev_name(info->dev), vg->name, info->vginfo->vgname);
+ return 0;
+ }
+
+ if (memcmp(vgid, info->vginfo->vgid, ID_LEN)) {
+ log_debug("Verify PV %s %s in %s: fail, other vgid %s",
+ info->dev->pvid, dev_name(info->dev), vg->name, info->vginfo->vgid);
+ return 0;
+ }
+
+ return 1;
+}
+
+
+int lvmcache_verify_pv_in_vg(struct volume_group *vg, struct physical_volume *pv)
+{
+ struct lvmcache_info *info;
+ char pvid[ID_LEN + 1] __attribute__((aligned(8))) = { 0 };
+
+ memcpy(&pvid, &pv->id.uuid, ID_LEN);
+
+ if (!(info = lvmcache_info_from_pvid(pvid, NULL, 0))) {
+ log_debug("Verify PV %s in %s: skip, no info", pvid, vg->name);
+ return 1;
+ }
+
+ if (pv->dev != info->dev) {
+ log_debug("Verify PV %s in %s: skip, different devs", info->dev->pvid, vg->name);
+ return 1;
+ }
+
+ return lvmcache_verify_info_in_vg(vg, info);
+}
+
const char *dev_filtered_reason(struct device *dev)
{
if (dev->filtered_flags & DEV_FILTERED_REGEX)
/* Update things */
int lvmcache_update_vgname_and_id(struct cmd_context *cmd, struct lvmcache_info *info,
struct lvmcache_vgsummary *vgsummary);
-int lvmcache_update_vg_from_read(struct volume_group *vg, unsigned precommitted);
+int lvmcache_update_vg_from_read(struct volume_group *vg);
void lvmcache_lock_vgname(const char *vgname, int read_only);
void lvmcache_unlock_vgname(const char *vgname);
bool lvmcache_has_old_metadata(struct cmd_context *cmd, const char *vgname, const char *vgid, struct device *dev);
+int lvmcache_verify_pv_in_vg(struct volume_group *vg, struct physical_volume *pv);
+int lvmcache_verify_info_in_vg(struct volume_group *vg, struct lvmcache_info *info);
+
void lvmcache_get_outdated_devs(struct cmd_context *cmd,
const char *vgname, const char *vgid,
struct dm_list *devs);
if (!(info = lvmcache_info_from_pvid(pvl->pv->dev->pvid, pvl->pv->dev, 0)))
continue;
+ /*
+ * If this vg metadata is old/outdated, it may no longer reflect the
+ * actual VG containing this PV, in which case we ignore this check.
+ */
+ if (!lvmcache_verify_info_in_vg(vg, info))
+ continue;
+
ext_version = lvmcache_ext_version(info);
if (ext_version < PV_HEADER_EXTENSION_VSN) {
log_warn("WARNING: PV %s in VG %s is using an old PV header, modify the VG to update.",
return 1;
}
-int pv_write_orphan(struct cmd_context *cmd, struct physical_volume *pv)
-{
- const char *old_vg_name = pv->vg_name;
-
- pv->vg_name = cmd->fmt->orphan_vg_name;
- pv->status = ALLOCATABLE_PV;
- pv->pe_alloc_count = 0;
-
- if (!dev_get_size(pv->dev, &pv->size)) {
- log_error("%s: Couldn't get size.", pv_dev_name(pv));
- return 0;
- }
-
- if (!pv_write(cmd, pv, 0)) {
- log_error("Failed to clear metadata from physical "
- "volume \"%s\" after removal from \"%s\"",
- pv_dev_name(pv), old_vg_name);
- return 0;
- }
-
- return 1;
-}
-
/**
* is_orphan_vg - Determine whether a vg_name is an orphan
* @vg_name: pointer to the vg_name
* Also, outdated PVs that have been removed from the VG were incorrectly
* attached to the vginfo during label_scan, and now need to be detached.
*/
- lvmcache_update_vg_from_read(vg_ret, vg_ret->status & PRECOMMITTED);
+ lvmcache_update_vg_from_read(vg_ret);
/*
* lvmcache_update_vg identified outdated mdas that we read above that
}
}
+ /*
+ * If the VG metadata is referencing PVs that don't belong to it,
+ * then don't allow the VG to be modified or activated, to avoid
+ * damanging those PVs.
+ */
+ if (!is_orphan_vg(vg->name) && (writing || activating)) {
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ if (!lvmcache_verify_pv_in_vg(vg, pvl->pv)) {
+ log_error("Cannot use VG %s referencing incorrect PVs.", vg->name);
+ log_error("(See vgreduce --removemissing with --devices options.)");
+ failure |= FAILED_NOT_ENABLED;
+ goto bad;
+ }
+ }
+ }
+
if (!check_pv_dev_sizes(vg))
log_warn("WARNING: One or more devices used as PVs in VG %s have changed sizes.", vg->name);
void set_pe_align(struct physical_volume *pv, uint64_t data_alignment);
void set_pe_align_offset(struct physical_volume *pv, uint64_t data_alignment_offset);
-int pv_write_orphan(struct cmd_context *cmd, struct physical_volume *pv);
-
int check_dev_block_size_for_vg(struct device *dev, const struct volume_group *vg,
unsigned int *max_logical_block_size_found);
int check_pv_dev_sizes(struct volume_group *vg);
grep $lv2 out
vgremove -ff $vg
+
+# Old metadata is reattached that references PVs
+# which are now used by other VGs. Commands run
+# on the VG from the old metadata should not
+# clobber the other PVs.
+
+vgcreate $SHARED $vg1 "$dev1" "$dev2" "$dev3"
+aux disable_dev "$dev1"
+vgreduce --removemissing $vg1
+vgremove $vg1
+vgcreate $vg2 "$dev2"
+vgcreate $vg3 "$dev3"
+aux enable_dev "$dev1"
+pvs | tee out
+grep "$dev1" out | tee out1
+grep "$dev2" out | tee out2
+grep "$dev3" out | tee out3
+grep $vg1 out1
+grep $vg2 out2
+grep $vg3 out3
+
+# The old VG cannot be used until the invalid PV references
+# are removed from it.
+not lvcreate -l1 $vg1
+not vgextend --restoremissing $vg1 "$dev1"
+not vgextend --restoremissing $vg1 "$dev2"
+not vgmerge $vg1 $vg2
+not vgrename $vg1 foo
+not vgremove $vg1
+not vgremove -ff $vg1
+not vgreduce $vg1 "$dev2"
+not vgreduce $vg1 "$dev3"
+not vgreduce --removemissing $vg1
+not vgreduce --removemissing --force $vg1
+not vgchange -ay $vg1
+
+# To modify the old VG on the reattached device,
+# removemissing together with --devices so the
+# command only sees the old device.
+vgreduce --removemissing --devices "$dev1" $vg1
+pvs | tee out
+grep "$dev1" out | tee out1
+grep "$dev2" out | tee out2
+grep "$dev3" out | tee out3
+grep $vg1 out1
+grep $vg2 out2
+grep $vg3 out3
+
+vgremove $vg1
+pvs | tee out
+not grep $vg1 out
+grep "$dev2" out | tee out2
+grep "$dev3" out | tee out3
+grep $vg2 out2
+grep $vg3 out3
+vgremove $vg2
+vgremove $vg3
+
+# Repeat the previous, but with two reattached/old PVs
+# referencing one other PV that's been reused for something
+# else.
+
+vgcreate $SHARED $vg1 "$dev1" "$dev2" "$dev3"
+aux disable_dev "$dev1"
+aux disable_dev "$dev2"
+vgreduce --removemissing $vg1
+vgremove $vg1
+vgcreate $vg3 "$dev3"
+aux enable_dev "$dev1"
+aux enable_dev "$dev2"
+pvs | tee out
+grep "$dev1" out | tee out1
+grep "$dev2" out | tee out2
+grep "$dev3" out | tee out3
+grep $vg1 out1
+grep $vg1 out2
+grep $vg3 out3
+
+# The old VG cannot be used until the invalid PV references
+# are removed from it.
+not lvcreate -l1 $vg1
+not vgextend --restoremissing $vg1 "$dev3"
+not vgmerge $vg1 $vg3
+not vgrename $vg1 foo
+not vgremove $vg1
+not vgremove -ff $vg1
+not vgreduce $vg1 "$dev2"
+not vgreduce $vg1 "$dev3"
+not vgreduce --removemissing $vg1
+not vgreduce --removemissing --force $vg1
+not vgchange -ay $vg1
+
+# To modify the old VG on the reattached device,
+# removemissing together with the correct PVs
+# specified with --devices.
+vgreduce --removemissing --devices "$dev1","$dev2" $vg1
+pvs | tee out
+grep "$dev1" out | tee out1
+grep "$dev2" out | tee out2
+grep "$dev3" out | tee out3
+grep $vg1 out1
+grep $vg1 out2
+grep $vg3 out3
+
+vgremove $vg1
+pvs | tee out
+not grep $vg1 out
+grep "$dev3" out | tee out3
+grep $vg3 out3
+vgremove $vg3
+
pv = pvl->pv;
pv_name = pv_dev_name(pv);
+ /*
+ * The VG metadata being processed here believes that this PV
+ * is part of the VG, but the headers/metadata on the PV may
+ * say differently. This can happen if the VG metadata being
+ * processed here is outdated, e.g. from an old device that
+ * has been reattached and has outdated metadata that no longer
+ * reflects how other PVs are used.
+ *
+ * So, before processing PV as part of VG, check that the
+ * metadata from PV shows the same. Info about what's on
+ * PV is kept in lvmcache info struct for PV.
+ */
+ if (!is_orphan_vg(vg->name) && !lvmcache_verify_pv_in_vg(vg, pv)) {
+ log_debug("ignoring claim of PV %s by VG %s.", pv_name, vg->name);
+ continue;
+ }
+
log_set_report_object_name_and_id(pv_name, &pv->id);
process_pv = process_all_pvs;