| /* |
| * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. |
| * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved. |
| * |
| * This file is part of LVM2. |
| * |
| * This copyrighted material is made available to anyone wishing to use, |
| * modify, copy, or redistribute it subject to the terms and conditions |
| * of the GNU Lesser General Public License v.2.1. |
| * |
| * You should have received a copy of the GNU Lesser General Public License |
| * along with this program; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include "tools.h" |
| |
| 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) |
| { |
| if (!lock_vol(cmd, vg_name_new, LCK_VG_WRITE, NULL)) { |
| log_error("Can't get lock for %s", vg_name_new); |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| static int _vgrename_single(struct cmd_context *cmd, const char *vg_name, |
| struct volume_group *vg, struct processing_handle *handle) |
| { |
| struct vgrename_params *vp = (struct vgrename_params *) handle->custom_handle; |
| char old_path[NAME_LEN]; |
| char new_path[NAME_LEN]; |
| struct id id; |
| const char *name; |
| char *dev_dir; |
| |
| /* |
| * 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; |
| } |
| |
| /* |
| * 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 (lvmcache_vginfo_from_vgname(vp->vg_name_new, NULL)) { |
| log_error("New VG name \"%s\" already exists", vp->vg_name_new); |
| return ECMD_FAILED; |
| } |
| |
| 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; |
| } |
| |
| /* |
| * 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; |
| |
| /* Remove references based on old name */ |
| if (!drop_cached_metadata(vg)) |
| stack; |
| |
| if (!lockd_rename_vg_before(cmd, vg)) { |
| stack; |
| goto error; |
| } |
| |
| /* Change the volume group name */ |
| vg_rename(cmd, vg, vp->vg_name_new); |
| |
| /* store it on disks */ |
| log_verbose("Writing out updated volume group"); |
| if (!vg_write(vg) || !vg_commit(vg)) { |
| goto error; |
| } |
| |
| 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); |
| |
| if (test_mode()) |
| log_verbose("Test mode: Skipping rename."); |
| |
| else if (lvs_in_vg_activated(vg)) { |
| if (!vg_refresh_visible(cmd, vg)) { |
| log_error("Renaming \"%s\" to \"%s\" failed", |
| old_path, new_path); |
| goto error; |
| } |
| } |
| } |
| |
| lockd_rename_vg_final(cmd, vg, 1); |
| |
| if (!backup(vg)) |
| stack; |
| if (!backup_remove(cmd, vg_name)) |
| stack; |
| |
| unlock_vg(cmd, vg, vp->vg_name_new); |
| vp->unlock_new_name = 0; |
| |
| log_print_unless_silent("Volume group \"%s\" successfully renamed to \"%s\"", |
| vp->vg_name_old, vp->vg_name_new); |
| return 1; |
| |
| error: |
| unlock_vg(cmd, vg, vp->vg_name_new); |
| vp->unlock_new_name = 0; |
| |
| lockd_rename_vg_final(cmd, vg, 0); |
| |
| 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; |
| |
| /* |
| * 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, NULL))) { |
| log_error("Failed to initialize processing handle."); |
| return ECMD_FAILED; |
| } |
| |
| handle->custom_handle = &vp; |
| |
| ret = process_each_vg(cmd, 0, NULL, vg_name_old, NULL, |
| READ_FOR_UPDATE | READ_ALLOW_EXPORTED, |
| 0, handle, _vgrename_single); |
| |
| /* Needed if process_each_vg returns error before calling _single. */ |
| if (vp.unlock_new_name) |
| unlock_vg(cmd, NULL, vg_name_new); |
| |
| destroy_processing_handle(cmd, handle); |
| return ret; |
| } |
| |