static int lvchange_alloc(struct cmd_context *cmd, struct logical_volume *lv) { int want_contiguous = 0; alloc_policy_t alloc; want_contiguous = strcmp(arg_str_value(cmd, contiguous_ARG, "n"), "n"); alloc = want_contiguous ? ALLOC_CONTIGUOUS : ALLOC_INHERIT; alloc = (alloc_policy_t) arg_uint_value(cmd, alloc_ARG, alloc); if (alloc == lv->alloc) { log_error("Allocation policy of logical volume \"%s\" is " "already %s", lv->name, get_alloc_string(alloc)); return 0; } lv->alloc = alloc; /* FIXME If contiguous, check existing extents already are */ log_verbose("Setting contiguous allocation policy for \"%s\" to %s", lv->name, get_alloc_string(alloc)); log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name); /* No need to suspend LV for this change */ if (!vg_write(lv->vg) || !vg_commit(lv->vg)) return_0; backup(lv->vg); return 1; }
int vg_remove_snapshot(struct logical_volume *cow) { int preload_origin = 0; struct logical_volume *origin = origin_from_cow(cow); dm_list_del(&cow->snapshot->origin_list); origin->origin_count--; if (find_merging_cow(origin) == find_cow(cow)) { clear_snapshot_merge(origin); /* * preload origin IFF "snapshot-merge" target is active * - IMPORTANT: avoids preload if onactivate merge is pending */ if (lv_has_target_type(origin->vg->cmd->mem, origin, NULL, "snapshot-merge")) { /* * preload origin to: * - allow proper release of -cow * - avoid allocations with other devices suspended * when transitioning from "snapshot-merge" to * "snapshot-origin after a merge completes. */ preload_origin = 1; } } if (!lv_remove(cow->snapshot->lv)) { log_error("Failed to remove internal snapshot LV %s", cow->snapshot->lv->name); return 0; } cow->snapshot = NULL; lv_set_visible(cow); if (preload_origin) { if (!vg_write(origin->vg)) return_0; if (!suspend_lv(origin->vg->cmd, origin)) { log_error("Failed to refresh %s without snapshot.", origin->name); return 0; } if (!vg_commit(origin->vg)) return_0; if (!resume_lv(origin->vg->cmd, origin)) { log_error("Failed to resume %s.", origin->name); return 0; } } return 1; }
static int vgexport_single(struct cmd_context *cmd, // __attribute((unused)), const char *vg_name, struct volume_group *vg, int consistent, void *handle) // __attribute((unused))) { struct pv_list *pvl; struct physical_volume *pv; if (!vg) { log_error("Unable to find volume group \"%s\"", vg_name); goto error; } if (!consistent) { log_error("Volume group %s inconsistent", vg_name); goto error; } if (!vg_check_status(vg, EXPORTED_VG | LVM_WRITE)) { goto error; } if (lvs_in_vg_activated(vg)) { log_error("Volume group \"%s\" has active logical volumes", vg_name); goto error; } if (!archive(vg)) goto error; vg->status |= EXPORTED_VG; list_iterate_items(pvl, struct pv_list, &vg->pvs) { pv = pvl->pv; pv->status |= EXPORTED_VG; } if (!vg_write(vg) || !vg_commit(vg)) goto error; backup(vg); log_print("Volume group \"%s\" successfully exported", vg->name); return ECMD_PROCESSED; error: return ECMD_FAILED; }
static int lvchange_tag(struct cmd_context *cmd, struct logical_volume *lv, int arg) { if (!change_tag(cmd, NULL, lv, NULL, arg)) return_0; log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name); /* No need to suspend LV for this change */ if (!vg_write(lv->vg) || !vg_commit(lv->vg)) return_0; backup(lv->vg); return 1; }
static int lvchange_readahead(struct cmd_context *cmd, struct logical_volume *lv) { unsigned read_ahead = 0; unsigned pagesize = (unsigned) lvm_getpagesize() >> SECTOR_SHIFT; int r = 0; read_ahead = arg_uint_value(cmd, readahead_ARG, 0); if (read_ahead != DM_READ_AHEAD_AUTO && (lv->vg->fid->fmt->features & FMT_RESTRICTED_READAHEAD) && (read_ahead < 2 || read_ahead > 120)) { log_error("Metadata only supports readahead values between 2 and 120."); return 0; } if (read_ahead != DM_READ_AHEAD_AUTO && read_ahead != DM_READ_AHEAD_NONE && read_ahead % pagesize) { if (read_ahead < pagesize) read_ahead = pagesize; else read_ahead = (read_ahead / pagesize) * pagesize; log_warn("WARNING: Overriding readahead to %u sectors, a multiple " "of %uK page size.", read_ahead, pagesize >> 1); } if (lv->read_ahead == read_ahead) { if (read_ahead == DM_READ_AHEAD_AUTO) log_error("Read ahead is already auto for \"%s\"", lv->name); else log_error("Read ahead is already %u for \"%s\"", read_ahead, lv->name); return 0; } lv->read_ahead = read_ahead; log_verbose("Setting read ahead to %u for \"%s\"", read_ahead, lv->name); log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name); if (!vg_write(lv->vg)) return_0; if (!suspend_lv(cmd, lv)) { log_error("Failed to lock %s", lv->name); vg_revert(lv->vg); goto out; } if (!vg_commit(lv->vg)) { if (!resume_lv(cmd, lv)) stack; goto_out; } log_very_verbose("Updating permissions for \"%s\" in kernel", lv->name); if (!resume_lv(cmd, lv)) { log_error("Problem reactivating %s", lv->name); goto out; } r = 1; out: backup(lv->vg); return r; }
static int lvchange_resync(struct cmd_context *cmd, struct logical_volume *lv) { int active = 0; int monitored; struct lvinfo info; struct logical_volume *log_lv; if (!(lv->status & MIRRORED)) { log_error("Unable to resync %s because it is not mirrored.", lv->name); return 1; } if (lv->status & PVMOVE) { log_error("Unable to resync pvmove volume %s", lv->name); return 0; } if (lv->status & LOCKED) { log_error("Unable to resync locked volume %s", lv->name); return 0; } if (lv_info(cmd, lv, 0, &info, 1, 0)) { if (info.open_count) { log_error("Can't resync open logical volume \"%s\"", lv->name); return 0; } if (info.exists) { if (!arg_count(cmd, yes_ARG) && yes_no_prompt("Do you really want to deactivate " "logical volume %s to resync it? [y/n]: ", lv->name) == 'n') { log_error("Logical volume \"%s\" not resynced", lv->name); return 0; } if (sigint_caught()) return 0; active = 1; } } /* Activate exclusively to ensure no nodes still have LV active */ monitored = dmeventd_monitor_mode(); init_dmeventd_monitor(0); if (!deactivate_lv(cmd, lv)) { log_error("Unable to deactivate %s for resync", lv->name); return 0; } if (vg_is_clustered(lv->vg) && lv_is_active(lv)) { log_error("Can't get exclusive access to clustered volume %s", lv->name); return 0; } init_dmeventd_monitor(monitored); log_lv = first_seg(lv)->log_lv; log_very_verbose("Starting resync of %s%s%s mirror \"%s\"", (active) ? "active " : "", vg_is_clustered(lv->vg) ? "clustered " : "", (log_lv) ? "disk-logged" : "core-logged", lv->name); /* * If this mirror has a core log (i.e. !log_lv), * then simply deactivating/activating will cause * it to reset the sync status. We only need to * worry about persistent logs. */ if (!log_lv && !(lv->status & LV_NOTSYNCED)) { if (active && !activate_lv(cmd, lv)) { log_error("Failed to reactivate %s to resynchronize " "mirror", lv->name); return 0; } return 1; } lv->status &= ~LV_NOTSYNCED; if (log_lv) { /* Separate mirror log so we can clear it */ detach_mirror_log(first_seg(lv)); if (!vg_write(lv->vg)) { log_error("Failed to write intermediate VG metadata."); if (!attach_mirror_log(first_seg(lv), log_lv)) stack; if (active && !activate_lv(cmd, lv)) stack; return 0; } if (!vg_commit(lv->vg)) { log_error("Failed to commit intermediate VG metadata."); if (!attach_mirror_log(first_seg(lv), log_lv)) stack; if (active && !activate_lv(cmd, lv)) stack; return 0; } backup(lv->vg); if (!activate_lv(cmd, log_lv)) { log_error("Unable to activate %s for mirror log resync", log_lv->name); return 0; } log_very_verbose("Clearing log device %s", log_lv->name); if (!set_lv(cmd, log_lv, log_lv->size, 0)) { log_error("Unable to reset sync status for %s", lv->name); if (!deactivate_lv(cmd, log_lv)) log_error("Failed to deactivate log LV after " "wiping failed"); return 0; } if (!deactivate_lv(cmd, log_lv)) { log_error("Unable to deactivate log LV %s after wiping " "for resync", log_lv->name); return 0; } /* Put mirror log back in place */ if (!attach_mirror_log(first_seg(lv), log_lv)) stack; } log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name); if (!vg_write(lv->vg) || !vg_commit(lv->vg)) { log_error("Failed to update metadata on disk."); return 0; } if (active && !activate_lv(cmd, lv)) { log_error("Failed to reactivate %s after resync", lv->name); return 0; } return 1; }
static int lvchange_permission(struct cmd_context *cmd, struct logical_volume *lv) { uint32_t lv_access; struct lvinfo info; int r = 0; lv_access = arg_uint_value(cmd, permission_ARG, 0); if ((lv_access & LVM_WRITE) && (lv->status & LVM_WRITE)) { log_error("Logical volume \"%s\" is already writable", lv->name); return 0; } if (!(lv_access & LVM_WRITE) && !(lv->status & LVM_WRITE)) { log_error("Logical volume \"%s\" is already read only", lv->name); return 0; } if ((lv->status & MIRRORED) && (vg_is_clustered(lv->vg)) && lv_info(cmd, lv, 0, &info, 0, 0) && info.exists) { log_error("Cannot change permissions of mirror \"%s\" " "while active.", lv->name); return 0; } /* Not allowed to change permissions on RAID sub-LVs directly */ if ((lv->status & RAID_META) || (lv->status & RAID_IMAGE)) { log_error("Cannot change permissions of RAID %s \"%s\"", (lv->status & RAID_IMAGE) ? "image" : "metadata area", lv->name); return 0; } if (lv_access & LVM_WRITE) { lv->status |= LVM_WRITE; log_verbose("Setting logical volume \"%s\" read/write", lv->name); } else { lv->status &= ~LVM_WRITE; log_verbose("Setting logical volume \"%s\" read-only", lv->name); } log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name); if (!vg_write(lv->vg)) return_0; if (!suspend_lv(cmd, lv)) { log_error("Failed to lock %s", lv->name); vg_revert(lv->vg); goto out; } if (!vg_commit(lv->vg)) { if (!resume_lv(cmd, lv)) stack; goto_out; } log_very_verbose("Updating permissions for \"%s\" in kernel", lv->name); if (!resume_lv(cmd, lv)) { log_error("Problem reactivating %s", lv->name); goto out; } r = 1; out: backup(lv->vg); return r; }
int vg_remove_snapshot(struct logical_volume *cow) { int merging_snapshot = 0; struct logical_volume *origin = origin_from_cow(cow); int is_origin_active = lv_is_active(origin); if (is_origin_active && lv_is_virtual_origin(origin)) { if (!deactivate_lv(origin->vg->cmd, origin)) { log_error("Failed to deactivate logical volume \"%s\"", origin->name); return 0; } is_origin_active = 0; } dm_list_del(&cow->snapshot->origin_list); origin->origin_count--; if (lv_is_merging_origin(origin) && (find_snapshot(origin) == find_snapshot(cow))) { clear_snapshot_merge(origin); /* * preload origin IFF "snapshot-merge" target is active * - IMPORTANT: avoids preload if inactivate merge is pending */ if (lv_has_target_type(origin->vg->vgmem, origin, NULL, TARGET_NAME_SNAPSHOT_MERGE)) { /* * preload origin to: * - allow proper release of -cow * - avoid allocations with other devices suspended * when transitioning from "snapshot-merge" to * "snapshot-origin after a merge completes. */ merging_snapshot = 1; } } if (!lv_remove(cow->snapshot->lv)) { log_error("Failed to remove internal snapshot LV %s", cow->snapshot->lv->name); return 0; } cow->snapshot = NULL; lv_set_visible(cow); /* format1 must do the change in one step, with the commit last. */ if (!(origin->vg->fid->fmt->features & FMT_MDAS)) { /* Get the lock for COW volume */ if (is_origin_active && !activate_lv(cow->vg->cmd, cow)) { log_error("Unable to activate logical volume \"%s\"", cow->name); return 0; } return 1; } if (!vg_write(origin->vg)) return_0; /* Skip call suspend, if device is not active */ if (is_origin_active && !suspend_lv(origin->vg->cmd, origin)) { log_error("Failed to refresh %s without snapshot.", origin->name); vg_revert(origin->vg); return 0; } if (!vg_commit(origin->vg)) return_0; if (is_origin_active) { /* * If the snapshot was active and the COW LV is taken away * the LV lock on cluster has to be grabbed, so use * activate_lv() which resumes suspend cow device. */ if (!merging_snapshot && !activate_lv(cow->vg->cmd, cow)) { log_error("Failed to activate %s.", cow->name); return 0; } if (!resume_lv(origin->vg->cmd, origin)) { log_error("Failed to resume %s.", origin->name); return 0; } /* * For merged snapshot and clustered VG activate cow LV so * the following call to deactivate_lv() can clean-up table * entries. For this clustered lock need to be held. */ if (vg_is_clustered(cow->vg) && merging_snapshot && !activate_lv(cow->vg->cmd, cow)) { log_error("Failed to activate %s.", cow->name); return 0; } } return 1; }
static int _pv_resize_single(struct cmd_context *cmd, struct volume_group *vg, struct physical_volume *pv, const uint64_t new_size) { struct pv_list *pvl; uint64_t size = 0; uint32_t new_pe_count = 0; int r = 0; struct dm_list mdas; const char *pv_name = pv_dev_name(pv); const char *vg_name; struct lvmcache_info *info; int mda_count = 0; struct volume_group *old_vg = vg; dm_list_init(&mdas); if (is_orphan_vg(pv_vg_name(pv))) { vg_name = VG_ORPHANS; if (!lock_vol(cmd, vg_name, LCK_VG_WRITE)) { log_error("Can't get lock for orphans"); return 0; } if (!(pv = pv_read(cmd, pv_name, &mdas, NULL, 1, 0))) { unlock_vg(cmd, vg_name); log_error("Unable to read PV \"%s\"", pv_name); return 0; } mda_count = dm_list_size(&mdas); } else { vg_name = pv_vg_name(pv); vg = vg_read_for_update(cmd, vg_name, NULL, 0); if (vg_read_error(vg)) goto bad; if (!(pvl = find_pv_in_vg(vg, pv_name))) { log_error("Unable to find \"%s\" in volume group \"%s\"", pv_name, vg->name); goto bad; } pv = pvl->pv; if (!(info = info_from_pvid(pv->dev->pvid, 0))) { log_error("Can't get info for PV %s in volume group %s", pv_name, vg->name); goto bad; } mda_count = dm_list_size(&info->mdas); if (!archive(vg)) goto bad; } /* FIXME Create function to test compatibility properly */ if (mda_count > 1) { log_error("%s: too many metadata areas for pvresize", pv_name); goto bad; } if (!(pv->fmt->features & FMT_RESIZE_PV)) { log_error("Physical volume %s format does not support resizing.", pv_name); goto bad; } /* Get new size */ if (!dev_get_size(pv_dev(pv), &size)) { log_error("%s: Couldn't get size.", pv_name); goto bad; } if (new_size) { if (new_size > size) log_warn("WARNING: %s: Overriding real size. " "You could lose data.", pv_name); log_verbose("%s: Pretending size is %" PRIu64 " not %" PRIu64 " sectors.", pv_name, new_size, pv_size(pv)); size = new_size; } if (size < PV_MIN_SIZE) { log_error("%s: Size must exceed minimum of %ld sectors.", pv_name, PV_MIN_SIZE); goto bad; } if (size < pv_pe_start(pv)) { log_error("%s: Size must exceed physical extent start of " "%" PRIu64 " sectors.", pv_name, pv_pe_start(pv)); goto bad; } pv->size = size; if (vg) { pv->size -= pv_pe_start(pv); new_pe_count = pv_size(pv) / vg->extent_size; if (!new_pe_count) { log_error("%s: Size must leave space for at " "least one physical extent of " "%" PRIu32 " sectors.", pv_name, pv_pe_size(pv)); goto bad; } if (!pv_resize(pv, vg, new_pe_count)) goto_bad; } log_verbose("Resizing volume \"%s\" to %" PRIu64 " sectors.", pv_name, pv_size(pv)); log_verbose("Updating physical volume \"%s\"", pv_name); if (!is_orphan_vg(pv_vg_name(pv))) { if (!vg_write(vg) || !vg_commit(vg)) { log_error("Failed to store physical volume \"%s\" in " "volume group \"%s\"", pv_name, vg->name); goto bad; } backup(vg); } else if (!(pv_write(cmd, pv, NULL, INT64_C(-1)))) { log_error("Failed to store physical volume \"%s\"", pv_name); goto bad;; } log_print("Physical volume \"%s\" changed", pv_name); r = 1; bad: unlock_vg(cmd, vg_name); if (!old_vg) vg_release(vg); return r; }
static int lvremove_single(struct cmd_context *cmd, struct logical_volume *lv, void *handle) // __attribute((unused))) { struct volume_group *vg; struct lvinfo info; struct logical_volume *origin = NULL; vg = lv->vg; if (!vg_check_status(vg, LVM_WRITE)) return ECMD_FAILED; if (lv_is_origin(lv)) { log_error("Can't remove logical volume \"%s\" under snapshot", lv->name); return ECMD_FAILED; } if (lv->status & MIRROR_IMAGE) { log_error("Can't remove logical volume %s used by a mirror", lv->name); return ECMD_FAILED; } if (lv->status & MIRROR_LOG) { log_error("Can't remove logical volume %s used as mirror log", lv->name); return ECMD_FAILED; } if (lv->status & LOCKED) { log_error("Can't remove locked LV %s", lv->name); return ECMD_FAILED; } /* FIXME Ensure not referred to by another existing LVs */ if (lv_info(cmd, lv, &info, 1)) { if (info.open_count) { log_error("Can't remove open logical volume \"%s\"", lv->name); return ECMD_FAILED; } if (info.exists && !arg_count(cmd, force_ARG)) { if (yes_no_prompt("Do you really want to remove active " "logical volume \"%s\"? [y/n]: ", lv->name) == 'n') { log_print("Logical volume \"%s\" not removed", lv->name); return ECMD_FAILED; } } } if (!archive(vg)) return ECMD_FAILED; /* If the VG is clustered then make sure no-one else is using the LV we are about to remove */ if (vg_status(vg) & CLUSTERED) { if (!activate_lv_excl(cmd, lv)) { log_error("Can't get exclusive access to volume \"%s\"", lv->name); return ECMD_FAILED; } } /* FIXME Snapshot commit out of sequence if it fails after here? */ if (!deactivate_lv(cmd, lv)) { log_error("Unable to deactivate logical volume \"%s\"", lv->name); return ECMD_FAILED; } if (lv_is_cow(lv)) { origin = origin_from_cow(lv); log_verbose("Removing snapshot %s", lv->name); if (!vg_remove_snapshot(lv)) { stack; return ECMD_FAILED; } } log_verbose("Releasing logical volume \"%s\"", lv->name); if (!lv_remove(lv)) { log_error("Error releasing logical volume \"%s\"", lv->name); return ECMD_FAILED; } /* store it on disks */ if (!vg_write(vg)) return ECMD_FAILED; backup(vg); if (!vg_commit(vg)) return ECMD_FAILED; /* If no snapshots left, reload without -real. */ if (origin && !lv_is_origin(origin)) { if (!suspend_lv(cmd, origin)) log_error("Failed to refresh %s without snapshot.", origin->name); else if (!resume_lv(cmd, origin)) log_error("Failed to resume %s.", origin->name); } log_print("Logical volume \"%s\" successfully removed", lv->name); return ECMD_PROCESSED; }
int vgcreate(struct cmd_context *cmd, int argc, char **argv) { 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; if (!argc) { log_error("Please provide volume group name and " "physical volumes"); return EINVALID_CMD_LINE; } vg_name = argv[0]; argc--; argv++; pvcreate_params_set_defaults(&pp); if (!pvcreate_params_validate(cmd, argc, argv, &pp)) { return EINVALID_CMD_LINE; } vgcreate_params_set_defaults(&vp_def, NULL); vp_def.vg_name = vg_name; if (vgcreate_params_set_from_args(cmd, &vp_new, &vp_def)) return EINVALID_CMD_LINE; if (vgcreate_params_validate(cmd, &vp_new)) return EINVALID_CMD_LINE; /* Create the new VG */ vg = vg_create(cmd, vp_new.vg_name); if (vg_read_error(vg)) { if (vg_read_error(vg) == 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); vg_release(vg); return ECMD_FAILED; } if (!vg_set_extent_size(vg, vp_new.extent_size) || !vg_set_max_lv(vg, vp_new.max_lv) || !vg_set_max_pv(vg, vp_new.max_pv) || !vg_set_alloc_policy(vg, vp_new.alloc) || !vg_set_clustered(vg, vp_new.clustered) || !vg_set_mda_copies(vg, vp_new.vgmetadatacopies)) goto bad_orphan; if (!lock_vol(cmd, VG_ORPHANS, LCK_VG_WRITE)) { log_error("Can't get lock for orphan PVs"); goto bad_orphan; } /* attach the pv's */ if (!vg_extend(vg, argc, argv, &pp)) goto_bad; if (vp_new.max_lv != vg->max_lv) log_warn("WARNING: Setting maxlogicalvolumes to %d " "(0 means unlimited)", vg->max_lv); if (vp_new.max_pv != vg->max_pv) log_warn("WARNING: Setting maxphysicalvolumes to %d " "(0 means unlimited)", vg->max_pv); if (arg_count(cmd, addtag_ARG)) { if (!(tag = arg_str_value(cmd, addtag_ARG, NULL))) { log_error("Failed to get tag"); goto bad; } if (!vg_change_tag(vg, tag, 1)) goto_bad; } if (vg_is_clustered(vg)) { clustered_message = "Clustered "; } else { if (locking_is_clustered()) clustered_message = "Non-clustered "; } if (!archive(vg)) goto_bad; /* Store VG on disk(s) */ if (!vg_write(vg) || !vg_commit(vg)) goto_bad; unlock_vg(cmd, VG_ORPHANS); unlock_vg(cmd, vp_new.vg_name); backup(vg); log_print("%s%colume group \"%s\" successfully created", clustered_message, *clustered_message ? 'v' : 'V', vg->name); vg_release(vg); return ECMD_PROCESSED; bad: unlock_vg(cmd, VG_ORPHANS); bad_orphan: vg_release(vg); unlock_vg(cmd, vp_new.vg_name); return ECMD_FAILED; }
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; struct lvmcache_vginfo *vginfo; 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 ((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; } 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, 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, vp->vg_name_new); vp->unlock_new_name = 0; lockd_rename_vg_final(cmd, vg, 0); return 0; }
/* * lv_cache_remove * @cache_lv * * Given a cache LV, remove the cache layer. This will unlink * the origin and cache_pool, remove the cache LV layer, and promote * the origin to a usable non-cached LV of the same name as the * given cache_lv. * * Returns: 1 on success, 0 on failure */ int lv_cache_remove(struct logical_volume *cache_lv) { struct cmd_context *cmd = cache_lv->vg->cmd; const char *policy_name; uint64_t dirty_blocks; struct lv_segment *cache_seg = first_seg(cache_lv); struct logical_volume *corigin_lv; struct logical_volume *cache_pool_lv; if (!lv_is_cache(cache_lv)) { log_error(INTERNAL_ERROR "LV %s is not cached.", cache_lv->name); return 0; } /* Active volume is needed (writeback only?) */ if (!lv_is_active_locally(cache_lv) && !activate_lv_excl_local(cache_lv->vg->cmd, cache_lv)) { log_error("Failed to active cache locally %s.", cache_lv->name); return 0; } /* * FIXME: * Before the link can be broken, we must ensure that the * cache has been flushed. This may already be the case * if the cache mode is writethrough (or the cleaner * policy is in place from a previous half-finished attempt * to remove the cache_pool). It could take a long time to * flush the cache - it should probably be done in the background. * * Also, if we do perform the flush in the background and we * happen to also be removing the cache/origin LV, then we * could check if the cleaner policy is in place and simply * remove the cache_pool then without waiting for the flush to * complete. */ if (!lv_cache_policy_info(cache_lv, &policy_name, NULL, NULL)) return_0; if (strcmp(policy_name, "cleaner")) { /* We must swap in the cleaner to flush the cache */ log_print_unless_silent("Flushing cache for %s.", cache_lv->name); /* * Is there are clean way to free the memory for the name * and argv when changing the policy? */ cache_seg->policy_name = "cleaner"; cache_seg->policy_argc = 0; cache_seg->policy_argv = NULL; /* update the kernel to put the cleaner policy in place */ if (!vg_write(cache_lv->vg)) return_0; if (!suspend_lv(cmd, cache_lv)) return_0; if (!vg_commit(cache_lv->vg)) return_0; if (!resume_lv(cmd, cache_lv)) return_0; } //FIXME: use polling to do this... do { if (!lv_cache_block_info(cache_lv, NULL, &dirty_blocks, NULL, NULL)) return_0; log_print_unless_silent("%" PRIu64 " blocks must still be flushed.", dirty_blocks); if (dirty_blocks) sleep(1); } while (dirty_blocks); cache_pool_lv = cache_seg->pool_lv; if (!detach_pool_lv(cache_seg)) return_0; /* Regular LV which user may remove if there are problems */ corigin_lv = seg_lv(cache_seg, 0); lv_set_visible(corigin_lv); if (!remove_layer_from_lv(cache_lv, corigin_lv)) return_0; if (!vg_write(cache_lv->vg)) return_0; /* * suspend_lv on this cache LV suspends all components: * - the top-level cache LV * - the origin * - the cache_pool _cdata and _cmeta */ if (!suspend_lv(cmd, cache_lv)) return_0; if (!vg_commit(cache_lv->vg)) return_0; /* resume_lv on this (former) cache LV will resume all */ /* * FIXME: currently we can't easily avoid execution of * blkid on resumed error device */ if (!resume_lv(cmd, cache_lv)) return_0; /* * cleanup orphan devices * * FIXME: * fix _add_dev() to support this case better * since the should be handled interanlly by resume_lv() * which should autoremove any orhpans */ if (!_cleanup_orphan_lv(corigin_lv)) /* _corig */ return_0; if (!_cleanup_orphan_lv(seg_lv(first_seg(cache_pool_lv), 0))) /* _cdata */ return_0; if (!_cleanup_orphan_lv(first_seg(cache_pool_lv)->metadata_lv)) /* _cmeta */ return_0; if (!lv_remove(corigin_lv)) return_0; return 1; }
int vgextend(struct cmd_context *cmd, int argc, char **argv) { char *vg_name; struct volume_group *vg = NULL; int r = ECMD_FAILED; struct pvcreate_params pp; if (!argc) { log_error("Please enter volume group name and " "physical volume(s)"); return EINVALID_CMD_LINE; } vg_name = skip_dev_dir(cmd, argv[0], NULL); argc--; argv++; if (arg_count(cmd, metadatacopies_ARG)) { log_error("Invalid option --metadatacopies, " "use --pvmetadatacopies instead."); return EINVALID_CMD_LINE; } pvcreate_params_set_defaults(&pp); if (!pvcreate_params_validate(cmd, argc, argv, &pp)) { return EINVALID_CMD_LINE; } log_verbose("Checking for volume group \"%s\"", vg_name); vg = vg_read_for_update(cmd, vg_name, NULL, 0); if (vg_read_error(vg)) { vg_release(vg); stack; return ECMD_FAILED; } if (!lock_vol(cmd, VG_ORPHANS, LCK_VG_WRITE)) { log_error("Can't get lock for orphan PVs"); unlock_and_release_vg(cmd, vg, vg_name); return ECMD_FAILED; } if (!archive(vg)) goto_bad; /* extend vg */ if (!vg_extend(vg, argc, argv, &pp)) goto_bad; /* ret > 0 */ log_verbose("Volume group \"%s\" will be extended by %d new " "physical volumes", vg_name, argc); /* store vg on disk(s) */ if (!vg_write(vg) || !vg_commit(vg)) goto_bad; backup(vg); log_print("Volume group \"%s\" successfully extended", vg_name); r = ECMD_PROCESSED; bad: unlock_vg(cmd, VG_ORPHANS); unlock_and_release_vg(cmd, vg, vg_name); return r; }
int vgcreate(struct cmd_context *cmd, int argc, char **argv) { size_t max_lv, max_pv; uint32_t extent_size; char *vg_name; struct volume_group *vg; const char *tag; alloc_policy_t alloc; int clustered; if (!argc) { log_error("Please provide volume group name and " "physical volumes"); return EINVALID_CMD_LINE; } if (argc == 1) { log_error("Please enter physical volume name(s)"); return EINVALID_CMD_LINE; } vg_name = skip_dev_dir(cmd, argv[0], NULL); max_lv = arg_uint_value(cmd, maxlogicalvolumes_ARG, 0); max_pv = arg_uint_value(cmd, maxphysicalvolumes_ARG, 0); alloc = arg_uint_value(cmd, alloc_ARG, ALLOC_NORMAL); if (alloc == ALLOC_INHERIT) { log_error("Volume Group allocation policy cannot inherit " "from anything"); return EINVALID_CMD_LINE; } if (!(cmd->fmt->features & FMT_UNLIMITED_VOLS)) { if (!max_lv) max_lv = 255; if (!max_pv) max_pv = 255; if (max_lv > 255 || max_pv > 255) { log_error("Number of volumes may not exceed 255"); return EINVALID_CMD_LINE; } } if (arg_sign_value(cmd, physicalextentsize_ARG, 0) == SIGN_MINUS) { log_error("Physical extent size may not be negative"); return EINVALID_CMD_LINE; } if (arg_sign_value(cmd, maxlogicalvolumes_ARG, 0) == SIGN_MINUS) { log_error("Max Logical Volumes may not be negative"); return EINVALID_CMD_LINE; } if (arg_sign_value(cmd, maxphysicalvolumes_ARG, 0) == SIGN_MINUS) { log_error("Max Physical Volumes may not be negative"); return EINVALID_CMD_LINE; } /* Units of 512-byte sectors */ extent_size = arg_uint_value(cmd, physicalextentsize_ARG, DEFAULT_EXTENT) * 2; if (!extent_size) { log_error("Physical extent size may not be zero"); return EINVALID_CMD_LINE; } if (!validate_vg_name(cmd, vg_name)) { log_error("New volume group name \"%s\" is invalid", vg_name); return ECMD_FAILED; } /* Create the new VG */ if (!(vg = vg_create(cmd, vg_name, extent_size, max_pv, max_lv, alloc, argc - 1, argv + 1))) return ECMD_FAILED; if (max_lv != vg->max_lv) log_warn("WARNING: Setting maxlogicalvolumes to %d " "(0 means unlimited)", vg->max_lv); if (max_pv != vg->max_pv) log_warn("WARNING: Setting maxphysicalvolumes to %d " "(0 means unlimited)", vg->max_pv); if (arg_count(cmd, addtag_ARG)) { if (!(tag = arg_str_value(cmd, addtag_ARG, NULL))) { log_error("Failed to get tag"); return ECMD_FAILED; } if (!(vg->fid->fmt->features & FMT_TAGS)) { log_error("Volume group format does not support tags"); return ECMD_FAILED; } if (!str_list_add(cmd->mem, &vg->tags, tag)) { log_error("Failed to add tag %s to volume group %s", tag, vg_name); return ECMD_FAILED; } } if (arg_count(cmd, clustered_ARG)) clustered = !strcmp(arg_str_value(cmd, clustered_ARG, "n"), "y"); else /* Default depends on current locking type */ clustered = locking_is_clustered(); if (clustered) vg->status |= CLUSTERED; else vg->status &= ~CLUSTERED; if (!lock_vol(cmd, ORPHAN, LCK_VG_WRITE)) { log_error("Can't get lock for orphan PVs"); return ECMD_FAILED; } if (!lock_vol(cmd, vg_name, LCK_VG_WRITE | LCK_NONBLOCK)) { log_error("Can't get lock for %s", vg_name); unlock_vg(cmd, ORPHAN); return ECMD_FAILED; } if (!archive(vg)) { unlock_vg(cmd, vg_name); unlock_vg(cmd, ORPHAN); return ECMD_FAILED; } /* Store VG on disk(s) */ if (!vg_write(vg) || !vg_commit(vg)) { unlock_vg(cmd, vg_name); unlock_vg(cmd, ORPHAN); return ECMD_FAILED; } unlock_vg(cmd, vg_name); unlock_vg(cmd, ORPHAN); backup(vg); log_print("Volume group \"%s\" successfully created", vg->name); return ECMD_PROCESSED; }
static int lvchange_persistent(struct cmd_context *cmd, struct logical_volume *lv) { struct lvinfo info; int active = 0; if (!strcmp(arg_str_value(cmd, persistent_ARG, "n"), "n")) { if (!(lv->status & FIXED_MINOR)) { log_error("Minor number is already not persistent " "for \"%s\"", lv->name); return 0; } lv->status &= ~FIXED_MINOR; lv->minor = -1; lv->major = -1; log_verbose("Disabling persistent device number for \"%s\"", lv->name); } else { if (!arg_count(cmd, minor_ARG) && lv->minor < 0) { log_error("Minor number must be specified with -My"); return 0; } if (arg_count(cmd, major_ARG) > 1) { log_error("Option -j/--major may not be repeated."); return 0; } if (arg_count(cmd, minor_ARG) > 1) { log_error("Option --minor may not be repeated."); return 0; } if (!arg_count(cmd, major_ARG) && lv->major < 0) { log_error("Major number must be specified with -My"); return 0; } if (lv_info(cmd, lv, 0, &info, 0, 0) && info.exists) active = 1; if (active && !arg_count(cmd, force_ARG) && yes_no_prompt("Logical volume %s will be " "deactivated temporarily. " "Continue? [y/n]: ", lv->name) == 'n') { log_error("%s device number not changed.", lv->name); return 0; } if (sigint_caught()) return 0; log_verbose("Ensuring %s is inactive.", lv->name); if (!deactivate_lv(cmd, lv)) { log_error("%s: deactivation failed", lv->name); return 0; } lv->status |= FIXED_MINOR; lv->minor = arg_int_value(cmd, minor_ARG, lv->minor); lv->major = arg_int_value(cmd, major_ARG, lv->major); log_verbose("Setting persistent device number to (%d, %d) " "for \"%s\"", lv->major, lv->minor, lv->name); } log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name); if (!vg_write(lv->vg) || !vg_commit(lv->vg)) return_0; backup(lv->vg); if (active) { log_verbose("Re-activating logical volume \"%s\"", lv->name); if (!activate_lv(cmd, lv)) { log_error("%s: reactivation failed", lv->name); return 0; } } return 1; }
int vgextend(struct cmd_context *cmd, int argc, char **argv) { const char *vg_name; struct volume_group *vg = NULL; int r = ECMD_FAILED; struct pvcreate_params pp; int fixed = 0, i = 0; if (!argc) { log_error("Please enter volume group name and " "physical volume(s)"); return EINVALID_CMD_LINE; } vg_name = skip_dev_dir(cmd, argv[0], NULL); argc--; argv++; if (arg_count(cmd, metadatacopies_ARG)) { log_error("Invalid option --metadatacopies, " "use --pvmetadatacopies instead."); return EINVALID_CMD_LINE; } pvcreate_params_set_defaults(&pp); if (!pvcreate_params_validate(cmd, argc, argv, &pp)) { return EINVALID_CMD_LINE; } /* * It is always ok to add new PVs to a VG - even if there are * missing PVs. No LVs are affected by this operation, but * repair processes - particularly for RAID segtypes - can * be facilitated. */ cmd->handles_missing_pvs = 1; log_verbose("Checking for volume group \"%s\"", vg_name); vg = vg_read_for_update(cmd, vg_name, NULL, 0); if (vg_read_error(vg)) { release_vg(vg); return_ECMD_FAILED; } if (!archive(vg)) goto_bad; if (arg_count(cmd, restoremissing_ARG)) { for (i = 0; i < argc; ++i) { if (_restore_pv(vg, argv[i])) ++ fixed; } if (!fixed) { log_error("No PV has been restored."); goto bad; } } else { /* no --restore, normal vgextend */ if (!lock_vol(cmd, VG_ORPHANS, LCK_VG_WRITE, NULL)) { log_error("Can't get lock for orphan PVs"); unlock_and_release_vg(cmd, vg, vg_name); return ECMD_FAILED; } if (arg_count(cmd, metadataignore_ARG) && (vg_mda_copies(vg) != VGMETADATACOPIES_UNMANAGED) && (pp.force == PROMPT) && yes_no_prompt("Override preferred number of copies " "of VG %s metadata? [y/n]: ", vg_name) == 'n') { log_error("Volume group %s not changed", vg_name); goto bad; } /* extend vg */ if (!vg_extend(vg, argc, (const char* const*)argv, &pp)) goto_bad; if (arg_count(cmd, metadataignore_ARG) && (vg_mda_copies(vg) != VGMETADATACOPIES_UNMANAGED) && (vg_mda_copies(vg) != vg_mda_used_count(vg))) { log_warn("WARNING: Changing preferred number of copies of VG %s " "metadata from %"PRIu32" to %"PRIu32, vg_name, vg_mda_copies(vg), vg_mda_used_count(vg)); vg_set_mda_copies(vg, vg_mda_used_count(vg)); } /* ret > 0 */ log_verbose("Volume group \"%s\" will be extended by %d new " "physical volumes", vg_name, argc); } /* store vg on disk(s) */ if (!vg_write(vg) || !vg_commit(vg)) goto_bad; backup(vg); log_print_unless_silent("Volume group \"%s\" successfully extended", vg_name); r = ECMD_PROCESSED; bad: if (!arg_count(cmd, restoremissing_ARG)) unlock_vg(cmd, VG_ORPHANS); unlock_and_release_vg(cmd, vg, vg_name); return r; }
static int lvchange_pool_update(struct cmd_context *cmd, struct logical_volume *lv) { int r = 0; int update = 0; unsigned val; thin_discards_t discards; if (!lv_is_thin_pool(lv)) { log_error("Logical volume \"%s\" is not a thin pool.", lv->name); return 0; } if (arg_count(cmd, discards_ARG)) { discards = (thin_discards_t) arg_uint_value(cmd, discards_ARG, THIN_DISCARDS_IGNORE); if (discards != first_seg(lv)->discards) { if ((discards != THIN_DISCARDS_IGNORE) && (first_seg(lv)->chunk_size & (first_seg(lv)->chunk_size - 1))) log_error("Cannot change discards state for " "logical volume \"%s\" " "with non power of 2 chunk size.", lv->name); else if (((discards == THIN_DISCARDS_IGNORE) || (first_seg(lv)->discards == THIN_DISCARDS_IGNORE)) && lv_is_active(lv)) log_error("Cannot change discards state for active " "logical volume \"%s\".", lv->name); else { first_seg(lv)->discards = discards; update++; } } else log_error("Logical volume \"%s\" already uses --discards %s.", lv->name, get_pool_discards_name(discards)); } if (arg_count(cmd, zero_ARG)) { val = arg_uint_value(cmd, zero_ARG, 1); if (val != first_seg(lv)->zero_new_blocks) { first_seg(lv)->zero_new_blocks = val; update++; } else log_error("Logical volume \"%s\" already %szero new blocks.", lv->name, val ? "" : "does not "); } if (!update) return 0; log_very_verbose("Updating logical volume \"%s\" on disk(s).", lv->name); if (!vg_write(lv->vg)) return_0; if (!suspend_lv_origin(cmd, lv)) { log_error("Failed to update active %s/%s (deactivation is needed).", lv->vg->name, lv->name); vg_revert(lv->vg); goto out; } if (!vg_commit(lv->vg)) { if (!resume_lv_origin(cmd, lv)) stack; goto_out; } if (!resume_lv_origin(cmd, lv)) { log_error("Problem reactivating %s.", lv->name); goto out; } r = 1; out: backup(lv->vg); return r; }