Beispiel #1
0
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;
}
Beispiel #2
0
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, &params,
			      _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;
}
Beispiel #3
0
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 = &params;

	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;
}
Beispiel #4
0
/*
 * 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;
}
Beispiel #5
0
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;
}
Beispiel #6
0
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;
}
Beispiel #7
0
/*
 * 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;
}
Beispiel #8
0
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;
}
Beispiel #9
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;
}