static int _wipe_signature(struct device *dev, const char *type, const char *name, int wipe_len, int yes, force_t force, int *wiped, int (*signature_detection_fn)(struct device *dev, uint64_t *offset_found)) { int wipe; uint64_t offset_found; wipe = signature_detection_fn(dev, &offset_found); if (wipe == -1) { log_error("Fatal error while trying to detect %s on %s.", type, name); return 0; } if (wipe == 0) return 1; /* Specifying --yes => do not ask. */ if (!yes && (force == PROMPT) && yes_no_prompt("WARNING: %s detected on %s. Wipe it? [y/n]: ", type, name) == 'n') { log_error("Aborted wiping of %s.", type); return 0; } log_print_unless_silent("Wiping %s on %s.", type, name); if (!dev_set(dev, offset_found, wipe_len, 0)) { log_error("Failed to wipe %s on %s.", type, name); return 0; } (*wiped)++; return 1; }
int pvresize(struct cmd_context *cmd, int argc, char **argv) { struct pvresize_params params; int ret; if (!argc) { log_error("Please supply physical volume(s)"); return EINVALID_CMD_LINE; } if (arg_sign_value(cmd, physicalvolumesize_ARG, SIGN_NONE) == SIGN_MINUS) { log_error("Physical volume size may not be negative"); return 0; } params.new_size = arg_uint64_value(cmd, physicalvolumesize_ARG, UINT64_C(0)); params.done = 0; params.total = 0; ret = process_each_pv(cmd, argc, argv, NULL, READ_FOR_UPDATE, 0, ¶ms, _pvresize_single); log_print_unless_silent("%d physical volume(s) resized / %d physical volume(s) " "not resized", params.done, params.total - params.done); return ret; }
int pvresize(struct cmd_context *cmd, int argc, char **argv) { struct pvresize_params params; struct processing_handle *handle = NULL; int ret; if (!argc) { log_error("Please supply physical volume(s)"); ret = EINVALID_CMD_LINE; goto out; } if (arg_sign_value(cmd, setphysicalvolumesize_ARG, SIGN_NONE) == SIGN_MINUS) { log_error("Physical volume size may not be negative"); ret = EINVALID_CMD_LINE; goto out; } params.new_size = arg_uint64_value(cmd, setphysicalvolumesize_ARG, UINT64_C(0)); params.done = 0; params.total = 0; set_pv_notify(cmd); if (!(handle = init_processing_handle(cmd, NULL))) { log_error("Failed to initialize processing handle."); ret = ECMD_FAILED; goto out; } handle->custom_handle = ¶ms; ret = process_each_pv(cmd, argc, argv, NULL, 0, READ_FOR_UPDATE | READ_ALLOW_EXPORTED, handle, _pvresize_single); log_print_unless_silent("%d physical volume(s) resized or updated / %d physical volume(s) " "not resized", params.done, params.total - params.done); out: destroy_processing_handle(cmd, handle); return ret; }
/* * convert_vdo_pool_lv * @data_lv * @vtp * @virtual_extents * * Convert given data LV and its target parameters into a VDO LV with VDO pool. * * Returns: old data LV on success (passed data LV becomes VDO LV), NULL on failure */ struct logical_volume *convert_vdo_pool_lv(struct logical_volume *data_lv, const struct dm_vdo_target_params *vtp, uint32_t *virtual_extents) { const uint64_t header_size = DEFAULT_VDO_POOL_HEADER_SIZE; const uint32_t extent_size = data_lv->vg->extent_size; struct cmd_context *cmd = data_lv->vg->cmd; struct logical_volume *vdo_pool_lv = data_lv; const struct segment_type *vdo_pool_segtype; struct lv_segment *vdo_pool_seg; uint64_t vdo_logical_size = 0; uint64_t adjust; if (!(vdo_pool_segtype = get_segtype_from_string(cmd, SEG_TYPE_NAME_VDO_POOL))) return_NULL; adjust = (*virtual_extents * (uint64_t) extent_size) % DM_VDO_BLOCK_SIZE; if (adjust) { *virtual_extents += (DM_VDO_BLOCK_SIZE - adjust) / extent_size; log_print_unless_silent("Rounding size up to 4,00 KiB VDO logical extent boundary: %s.", display_size(data_lv->vg->cmd, *virtual_extents * (uint64_t) extent_size)); } if (*virtual_extents) vdo_logical_size = _get_virtual_size(*virtual_extents, extent_size, header_size); if (!dm_vdo_validate_target_params(vtp, vdo_logical_size)) return_0; /* Format data LV as VDO volume */ if (!_format_vdo_pool_data_lv(data_lv, vtp, &vdo_logical_size)) { log_error("Cannot format VDO pool volume %s.", display_lvname(data_lv)); return NULL; } if (!deactivate_lv(data_lv->vg->cmd, data_lv)) { log_error("Aborting. Manual intervention required."); return NULL; } vdo_logical_size -= 2 * header_size; if (vdo_logical_size < extent_size) { if (!*virtual_extents) /* User has not specified size and at least 1 extent is necessary */ log_error("Cannot create fully fitting VDO volume, " "--virtualsize has to be specified."); log_error("Size %s for VDO volume cannot be smaller then extent size %s.", display_size(data_lv->vg->cmd, vdo_logical_size), display_size(data_lv->vg->cmd, extent_size)); return NULL; } *virtual_extents = vdo_logical_size / extent_size; /* Move segments from existing data_lv into LV_vdata */ if (!(data_lv = insert_layer_for_lv(cmd, vdo_pool_lv, 0, "_vdata"))) return_NULL; vdo_pool_seg = first_seg(vdo_pool_lv); vdo_pool_seg->segtype = vdo_pool_segtype; vdo_pool_seg->vdo_params = *vtp; vdo_pool_seg->vdo_pool_header_size = DEFAULT_VDO_POOL_HEADER_SIZE; vdo_pool_seg->vdo_pool_virtual_extents = *virtual_extents; vdo_pool_lv->status |= LV_VDO_POOL; data_lv->status |= LV_VDO_POOL_DATA; return data_lv; }
static int _blkid_wipe(blkid_probe probe, struct device *dev, const char *name, uint32_t types_to_exclude, uint32_t types_no_prompt, int yes, force_t force) { static const char _msg_failed_offset[] = "Failed to get offset of the %s signature on %s."; static const char _msg_failed_length[] = "Failed to get length of the %s signature on %s."; static const char _msg_wiping[] = "Wiping %s signature on %s."; const char *offset = NULL, *type = NULL, *magic = NULL, *usage = NULL, *label = NULL, *uuid = NULL; loff_t offset_value; size_t len; if (!blkid_probe_lookup_value(probe, "TYPE", &type, NULL)) { if (_type_in_flag_list(type, types_to_exclude)) return 2; if (blkid_probe_lookup_value(probe, "SBMAGIC_OFFSET", &offset, NULL)) { log_error(_msg_failed_offset, type, name); return 0; } if (blkid_probe_lookup_value(probe, "SBMAGIC", &magic, &len)) { log_error(_msg_failed_length, type, name); return 0; } } else if (!blkid_probe_lookup_value(probe, "PTTYPE", &type, NULL)) { if (blkid_probe_lookup_value(probe, "PTMAGIC_OFFSET", &offset, NULL)) { log_error(_msg_failed_offset, type, name); return 0; } if (blkid_probe_lookup_value(probe, "PTMAGIC", &magic, &len)) { log_error(_msg_failed_length, type, name); return 0; } usage = "partition table"; } else return_0; offset_value = strtoll(offset, NULL, 10); if (!usage) (void) blkid_probe_lookup_value(probe, "USAGE", &usage, NULL); (void) blkid_probe_lookup_value(probe, "LABEL", &label, NULL); (void) blkid_probe_lookup_value(probe, "UUID", &uuid, NULL); /* Return values ignored here, in the worst case we print NULL */ log_verbose("Found existing signature on %s at offset %s: LABEL=\"%s\" " "UUID=\"%s\" TYPE=\"%s\" USAGE=\"%s\"", name, offset, label, uuid, type, usage); if (!_type_in_flag_list(type, types_no_prompt)) { if (!yes && (force == PROMPT) && yes_no_prompt("WARNING: %s signature detected on %s at offset %s. " "Wipe it? [y/n]: ", type, name, offset) == 'n') { log_error("Aborted wiping of %s.", type); return 0; } log_print_unless_silent(_msg_wiping, type, name); } else log_verbose(_msg_wiping, type, name); if (!dev_set(dev, offset_value, len, 0)) { log_error("Failed to wipe %s signature on %s.", type, 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; }
/* * Update extents parameters based on other parameters which affect the size * calculation. * NOTE: We must do this here because of the dm_percent_t typedef and because we * need the vg. */ static int _update_extents_params(struct volume_group *vg, struct lvcreate_params *lp, struct lvcreate_cmdline_params *lcp) { uint32_t pv_extent_count; struct logical_volume *origin = NULL; uint32_t size_rest; uint32_t stripesize_extents; uint32_t extents; if (lcp->size && !(lp->extents = extents_from_size(vg->cmd, lcp->size, vg->extent_size))) return_0; if (lp->voriginsize && !(lp->voriginextents = extents_from_size(vg->cmd, lp->voriginsize, vg->extent_size))) return_0; /* * Create the pv list before we parse lcp->percent - might be * PERCENT_PVSs */ if (lcp->pv_count) { if (!(lp->pvh = create_pv_list(vg->cmd->mem, vg, lcp->pv_count, lcp->pvs, 1))) return_0; } else lp->pvh = &vg->pvs; switch (lcp->percent) { case PERCENT_VG: extents = percent_of_extents(lp->extents, vg->extent_count, 0); break; case PERCENT_FREE: extents = percent_of_extents(lp->extents, vg->free_count, 0); break; case PERCENT_PVS: if (lcp->pv_count) { pv_extent_count = pv_list_extents_free(lp->pvh); extents = percent_of_extents(lp->extents, pv_extent_count, 0); } else extents = percent_of_extents(lp->extents, vg->extent_count, 0); break; case PERCENT_LV: log_error("Please express size as %s%%VG, %%PVS, " "or %%FREE.", (lp->snapshot) ? "%ORIGIN, " : ""); return 0; case PERCENT_ORIGIN: if (lp->snapshot && lp->origin && !(origin = find_lv(vg, lp->origin))) { log_error("Couldn't find origin volume '%s'.", lp->origin); return 0; } if (!origin) { log_error(INTERNAL_ERROR "Couldn't find origin volume."); return 0; } /* Add whole metadata size estimation */ extents = cow_max_extents(origin, lp->chunk_size) - origin->le_count + percent_of_extents(lp->extents, origin->le_count, 1); break; case PERCENT_NONE: extents = lp->extents; break; default: log_error(INTERNAL_ERROR "Unsupported percent type %u.", lcp->percent); return 0; } if (lcp->percent) { /* FIXME Don't do the adjustment for parallel allocation with PERCENT_ORIGIN! */ lp->approx_alloc = 1; log_verbose("Converted %" PRIu32 "%%%s into %" PRIu32 " extents.", lp->extents, get_percent_string(lcp->percent), extents); lp->extents = extents; } if (lp->snapshot && lp->origin && lp->extents) { if (!lp->chunk_size) { log_error(INTERNAL_ERROR "Missing snapshot chunk size."); return 0; } if (!origin && !(origin = find_lv(vg, lp->origin))) { log_error("Couldn't find origin volume '%s'.", lp->origin); return 0; } extents = cow_max_extents(origin, lp->chunk_size); if (extents < lp->extents) { log_print_unless_silent("Reducing COW size %s down to maximum usable size %s.", display_size(vg->cmd, (uint64_t) vg->extent_size * lp->extents), display_size(vg->cmd, (uint64_t) vg->extent_size * extents)); lp->extents = extents; } } if (!(stripesize_extents = lp->stripe_size / vg->extent_size)) stripesize_extents = 1; if ((lcp->percent != PERCENT_NONE) && lp->stripes && (size_rest = lp->extents % (lp->stripes * stripesize_extents)) && (vg->free_count < lp->extents - size_rest + (lp->stripes * stripesize_extents))) { log_print_unless_silent("Rounding size (%d extents) down to stripe boundary " "size (%d extents)", lp->extents, lp->extents - size_rest); lp->extents = lp->extents - size_rest; } if (lp->create_pool) { if (!_lvcreate_update_pool_params(vg, lp)) return_0; if (!(lp->poolmetadataextents = extents_from_size(vg->cmd, lp->poolmetadatasize, vg->extent_size))) return_0; if (lcp->percent == PERCENT_FREE) { if (lp->extents <= (2 * lp->poolmetadataextents)) { log_error("Not enough space for thin pool creation."); return 0; } /* FIXME: persistent hidden space in VG wanted */ lp->extents -= (2 * lp->poolmetadataextents); } } 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; 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; }