diff options
Diffstat (limited to 'tools/vgrename.c')
-rw-r--r-- | tools/vgrename.c | 290 |
1 files changed, 156 insertions, 134 deletions
diff --git a/tools/vgrename.c b/tools/vgrename.c index 9e6beec35..e6b808b54 100644 --- a/tools/vgrename.c +++ b/tools/vgrename.c @@ -15,137 +15,107 @@ #include "tools.h" -static struct volume_group *_get_old_vg_for_rename(struct cmd_context *cmd, - const char *vg_name_old, - const char *vgid, - uint32_t lockd_state) -{ - struct volume_group *vg; - - /* FIXME we used to print an error about EXPORTED, but proceeded - nevertheless. */ - vg = vg_read_for_update(cmd, vg_name_old, vgid, READ_ALLOW_EXPORTED, lockd_state); - if (vg_read_error(vg)) { - release_vg(vg); - return_NULL; - } - - return vg; -} +struct vgrename_params { + const char *vg_name_old; + const char *vg_name_new; + unsigned int old_name_is_uuid : 1; + unsigned int lock_vg_old_first : 1; + unsigned int unlock_new_name: 1; +}; static int _lock_new_vg_for_rename(struct cmd_context *cmd, const char *vg_name_new) { - int rc; - - log_verbose("Checking for new volume group \"%s\"", vg_name_new); - - rc = vg_lock_newname(cmd, vg_name_new); - - if (rc == FAILED_LOCKING) { + if (!lock_vol(cmd, vg_name_new, LCK_VG_WRITE, NULL)) { log_error("Can't get lock for %s", vg_name_new); return 0; } - if (rc == FAILED_EXIST) { - log_error("New volume group \"%s\" already exists", - vg_name_new); - return 0; - } return 1; } -static int vg_rename_path(struct cmd_context *cmd, const char *old_vg_path, - const char *new_vg_path) +static int _vgrename_single(struct cmd_context *cmd, const char *vg_name, + struct volume_group *vg, struct processing_handle *handle) { - char *dev_dir; + struct vgrename_params *vp = (struct vgrename_params *) handle->custom_handle; + struct lvmcache_vginfo *vginfo; + char old_path[NAME_LEN]; + char new_path[NAME_LEN]; struct id id; - int match = 0; - int found_id = 0; - struct dm_list *vgids; - struct dm_str_list *sl; - const char *vg_name_new; - const char *vgid = NULL, *vg_name, *vg_name_old; - char old_path[NAME_LEN], new_path[NAME_LEN]; - struct volume_group *vg = NULL; - uint32_t lockd_state = 0; - int lock_vg_old_first = 1; - - vg_name_old = skip_dev_dir(cmd, old_vg_path, NULL); - vg_name_new = skip_dev_dir(cmd, new_vg_path, NULL); - - dev_dir = cmd->dev_dir; - - if (!validate_vg_rename_params(cmd, vg_name_old, vg_name_new)) - return_0; - - log_verbose("Checking for existing volume group \"%s\"", vg_name_old); - - /* populate lvmcache */ - if (!lvmetad_vg_list_to_lvmcache(cmd)) - stack; - - lvmcache_label_scan(cmd, 2); + const char *name; + char *dev_dir; - /* Avoid duplicates */ - if (!(vgids = get_vgids(cmd, 0)) || dm_list_empty(vgids)) { - log_error("No complete volume groups found"); - return 0; + /* + * vg_name_old may be a UUID which process_each_vg + * replaced with the real VG name. In that case, + * vp->vg_name_old will be the UUID and vg_name will be + * the actual VG name. Check again if the old and new + * names match, using the real names. + */ + if (vp->old_name_is_uuid && !strcmp(vp->vg_name_new, vg_name)) { + log_error("New VG name must differ from the old VG name."); + return ECMD_FAILED; } - dm_list_iterate_items(sl, vgids) { - vgid = sl->str; - if (!vgid || !(vg_name = lvmcache_vgname_from_vgid(NULL, vgid))) - continue; - if (!strcmp(vg_name, vg_name_old)) { - if (match) { - log_error("Found more than one VG called %s. " - "Please supply VG uuid.", vg_name_old); - return 0; - } - match = 1; - } + /* + * Check if a VG already exists with the new VG name. + * + * When not using lvmetad, it's essential that a full scan has + * been done to ensure we see all existing VG names, so we + * do not use an existing name. This has been done by + * process_each_vg REQUIRES_FULL_LABEL_SCAN. + * + * (FIXME: We could look for the new name in the list of all + * VGs that process_each_vg created, but we don't have access + * to that list here, so we have to look in lvmcache. + * This requires populating lvmcache when using lvmetad.) + */ + lvmcache_seed_infos_from_lvmetad(cmd); + + if ((vginfo = lvmcache_vginfo_from_vgname(vp->vg_name_new, NULL))) { + log_error("New VG name \"%s\" already exists", vp->vg_name_new); + return ECMD_FAILED; } - found_id = id_read_format_try(&id, vg_name_old); - - if (found_id && (vg_name = lvmcache_vgname_from_vgid(cmd->mem, (char *)id.uuid))) { - if (!strcmp(vg_name, vg_name_new)) { - log_error("New VG name must differ from the old VG name."); - return 0; - } - - vg_name_old = vg_name; - vgid = (char *)id.uuid; - } else - vgid = NULL; - - if (!lockd_vg(cmd, vg_name_old, "ex", 0, &lockd_state)) - return_0; - - if (strcmp(vg_name_new, vg_name_old) < 0) - lock_vg_old_first = 0; - - if (lock_vg_old_first) { - vg = _get_old_vg_for_rename(cmd, vg_name_old, vgid, lockd_state); - if (!vg) - return_0; + if (id_read_format_try(&id, vp->vg_name_new) && + (name = lvmcache_vgname_from_vgid(cmd->mem, (const char *)&id))) { + log_error("New VG name \"%s\" matches the UUID of existing VG %s", vp->vg_name_new, name); + return ECMD_FAILED; + } - if (!_lock_new_vg_for_rename(cmd, vg_name_new)) { - unlock_and_release_vg(cmd, vg, vg_name_old); - return_0; - } - } else { - if (!_lock_new_vg_for_rename(cmd, vg_name_new)) - return_0; - - vg = _get_old_vg_for_rename(cmd, vg_name_old, vgid, lockd_state); - if (!vg) { - unlock_vg(cmd, vg_name_new); - return_0; - } + /* + * Lock the old VG name first: + * . The old VG name has already been locked by process_each_vg. + * . Now lock the new VG name here, second. + * + * Lock the new VG name first: + * . The new VG name has already been pre-locked below, + * before process_each_vg was called. + * . process_each_vg then locked the old VG name second. + * . Nothing to do here. + * + * Special case when the old VG name is a uuid: + * . The old VG's real name wasn't known before process_each_vg, + * so the correct lock ordering wasn't known beforehand, + * so no pre-locking was done. + * . The old VG's real name has been locked by process_each_vg. + * . Now lock the new VG name here, second. + * . Suppress lock ordering checks because the lock order may + * have wanted the new name first, which wasn't possible in + * this uuid-for-name case. + */ + if (vp->lock_vg_old_first || vp->old_name_is_uuid) { + if (vp->old_name_is_uuid) + lvmcache_lock_ordering(0); + + if (!_lock_new_vg_for_rename(cmd, vp->vg_name_new)) + return ECMD_FAILED; + + lvmcache_lock_ordering(1); } + dev_dir = cmd->dev_dir; + if (!archive(vg)) goto error; @@ -159,7 +129,7 @@ static int vg_rename_path(struct cmd_context *cmd, const char *old_vg_path, } /* Change the volume group name */ - vg_rename(cmd, vg, vg_name_new); + vg_rename(cmd, vg, vp->vg_name_new); /* store it on disks */ log_verbose("Writing out updated volume group"); @@ -167,8 +137,8 @@ static int vg_rename_path(struct cmd_context *cmd, const char *old_vg_path, goto error; } - sprintf(old_path, "%s%s", dev_dir, vg_name_old); - sprintf(new_path, "%s%s", dev_dir, vg_name_new); + sprintf(old_path, "%s%s", dev_dir, vg_name); + sprintf(new_path, "%s%s", dev_dir, vp->vg_name_new); if (activation() && dir_exists(old_path)) { log_verbose("Renaming \"%s\" to \"%s\"", old_path, new_path); @@ -189,49 +159,101 @@ static int vg_rename_path(struct cmd_context *cmd, const char *old_vg_path, if (!backup(vg)) stack; - if (!backup_remove(cmd, vg_name_old)) + if (!backup_remove(cmd, vg_name)) stack; - unlock_vg(cmd, vg_name_new); - unlock_and_release_vg(cmd, vg, vg_name_old); + unlock_vg(cmd, vp->vg_name_new); + vp->unlock_new_name = 0; log_print_unless_silent("Volume group \"%s\" successfully renamed to \"%s\"", - vg_name_old, vg_name_new); - - /* FIXME lvmcache corruption - vginfo duplicated instead of renamed */ - if (cmd->filter->wipe) - cmd->filter->wipe(cmd->filter); - lvmcache_destroy(cmd, 1, 0); - + vp->vg_name_old, vp->vg_name_new); return 1; - error: + error: + unlock_vg(cmd, vp->vg_name_new); + vp->unlock_new_name = 0; + lockd_rename_vg_final(cmd, vg, 0); - if (lock_vg_old_first) { - unlock_vg(cmd, vg_name_new); - unlock_and_release_vg(cmd, vg, vg_name_old); - } else { - unlock_and_release_vg(cmd, vg, vg_name_old); - unlock_vg(cmd, vg_name_new); - } return 0; } int vgrename(struct cmd_context *cmd, int argc, char **argv) { + struct vgrename_params vp = { 0 }; + struct processing_handle *handle; + const char *vg_name_new; + const char *vg_name_old; + struct id id; + int ret; + if (argc != 2) { log_error("Old and new volume group names need specifying"); return EINVALID_CMD_LINE; } + vg_name_old = skip_dev_dir(cmd, argv[0], NULL); + vg_name_new = skip_dev_dir(cmd, argv[1], NULL); + + if (!validate_vg_rename_params(cmd, vg_name_old, vg_name_new)) + return_0; + + if (!(vp.vg_name_old = dm_pool_strdup(cmd->mem, vg_name_old))) + return_ECMD_FAILED; + + if (!(vp.vg_name_new = dm_pool_strdup(cmd->mem, vg_name_new))) + return_ECMD_FAILED; + /* Needed change the global VG namespace. */ if (!lockd_gl(cmd, "ex", LDGL_UPDATE_NAMES)) return_ECMD_FAILED; - if (!vg_rename_path(cmd, argv[0], argv[1])) - return_ECMD_FAILED; + /* + * Special case where vg_name_old may be a UUID: + * If vg_name_old is a UUID, then process_each may + * translate it to an actual VG name that we don't + * yet know. The lock ordering, and pre-locking, + * needs to be done based on VG names. When + * vg_name_old is a UUID, do not do any pre-locking + * based on it, since it's likely to be wrong, and + * defer all the locking to the _single function. + * + * When it's not a UUID, we know the two VG names, + * and we can pre-lock the new VG name if the lock + * ordering wants it locked before the old VG name + * which will be locked by process_each. If lock + * ordering wants the old name locked first, then + * the _single function will lock the new VG name. + */ + if (!(vp.old_name_is_uuid = id_read_format_try(&id, vg_name_old))) { + if (strcmp(vg_name_new, vg_name_old) < 0) { + vp.lock_vg_old_first = 0; + vp.unlock_new_name = 1; + + if (!_lock_new_vg_for_rename(cmd, vg_name_new)) + return ECMD_FAILED; + } else { + /* The old VG is locked by process_each_vg. */ + vp.lock_vg_old_first = 1; + } + } + + if (!(handle = init_processing_handle(cmd))) { + log_error("Failed to initialize processing handle."); + return ECMD_FAILED; + } + + handle->custom_handle = &vp; + + ret = process_each_vg(cmd, 0, NULL, vg_name_old, + READ_FOR_UPDATE | READ_ALLOW_EXPORTED, + handle, _vgrename_single); + + /* Needed if process_each_vg returns error before calling _single. */ + if (vp.unlock_new_name) + unlock_vg(cmd, vg_name_new); - return ECMD_PROCESSED; + destroy_processing_handle(cmd, handle); + return ret; } |