/*
 * Copyright (C) 2014-2015 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 "lib/misc/lib.h"
#include "lib/metadata/metadata.h"
#include "lib/locking/locking.h"
#include "lib/misc/lvm-string.h"
#include "lib/commands/toolcontext.h"
#include "lib/display/display.h"
#include "lib/metadata/segtype.h"
#include "lib/activate/activate.h"
#include "lib/config/defaults.h"

#define DEFAULT_TAG_SIZE 4 /* bytes */
#define DEFAULT_MODE 'J'
#define DEFAULT_INTERNAL_HASH "crc32c"
#define DEFAULT_BLOCK_SIZE 512

#define ONE_MB_IN_BYTES 1048576
#define ONE_GB_IN_BYTES 1073741824

int lv_is_integrity_origin(const struct logical_volume *lv)
{
	struct seg_list *sl;

	dm_list_iterate_items(sl, &lv->segs_using_this_lv) {
		if (!sl->seg || !sl->seg->lv || !sl->seg->origin)
			continue;
		if (lv_is_integrity(sl->seg->lv) && (sl->seg->origin == lv))
			return 1;
	}
	return 0;
}

/*
 * Every 500M of data needs 4M of metadata.
 * (From trial and error testing.)
 *
 * plus some initial space for journals.
 * (again from trial and error testing.)
 */
static uint64_t _lv_size_bytes_to_integrity_meta_bytes(uint64_t lv_size_bytes, uint32_t journal_sectors,
						       uint32_t extent_size)
{
	uint64_t meta_bytes;
	uint64_t initial_bytes;

	/* Every 500M of data needs 4M of metadata. */
	meta_bytes = ((lv_size_bytes / (500 * ONE_MB_IN_BYTES)) + 1) * (4 * ONE_MB_IN_BYTES);

	if (journal_sectors) {
		/* for calculating the metadata LV size for the specified
		   journal size, round the specified journal size up to the
		   nearest extent.  extent_size is in sectors. */
		initial_bytes = dm_round_up(journal_sectors, (int64_t)extent_size) * 512;
		goto out;
	}

	/*
	 * initial space used for journals (when journal size is not specified):
	 * lv_size <= 512M -> 4M
	 * lv_size <= 1G   -> 8M
	 * lv_size <= 4G   -> 32M
	 * lv_size > 4G    -> 64M
	 */
	if (lv_size_bytes <= (512 * ONE_MB_IN_BYTES))
		initial_bytes = 4 * ONE_MB_IN_BYTES;
	else if (lv_size_bytes <= ONE_GB_IN_BYTES)
		initial_bytes = 8 * ONE_MB_IN_BYTES;
	else if (lv_size_bytes <= (4ULL * ONE_GB_IN_BYTES))
		initial_bytes = 32 * ONE_MB_IN_BYTES;
	else if (lv_size_bytes > (4ULL * ONE_GB_IN_BYTES))
		initial_bytes = 64 * ONE_MB_IN_BYTES;
 out:
	log_debug("integrity_meta_bytes %llu from lv_size_bytes %llu meta_bytes %llu initial_bytes %llu journal_sectors %u",
		  (unsigned long long)(meta_bytes+initial_bytes), (unsigned long long)lv_size_bytes,
		  (unsigned long long)meta_bytes, (unsigned long long)initial_bytes, journal_sectors);
	return meta_bytes + initial_bytes;
}

/*
 * The user wants external metadata, but did not specify an existing
 * LV to hold metadata, so create an LV for metadata.
 */
static int _lv_create_integrity_metadata(struct cmd_context *cmd,
				struct volume_group *vg,
				struct lvcreate_params *lp,
				struct integrity_settings *settings,
				struct logical_volume **meta_lv)
{
	char metaname[NAME_LEN] = { 0 };
	uint64_t lv_size_bytes, meta_bytes, meta_sectors;
	struct logical_volume *lv;
	struct lvcreate_params lp_meta = {
		.activate = CHANGE_AN,
		.alloc = ALLOC_INHERIT,
		.major = -1,
		.minor = -1,
		.permission = LVM_READ | LVM_WRITE,
		.pvh = &vg->pvs,
		.read_ahead = DM_READ_AHEAD_NONE,
		.stripes = 1,
		.vg_name = vg->name,
		.temporary = 1,
		.zero = 0,
		.wipe_signatures = 0,
		.suppress_zero_warn = 1,
	};

	if (lp->lv_name &&
	    dm_snprintf(metaname, NAME_LEN, "%s_imeta", lp->lv_name) < 0) {
		log_error("Failed to create metadata LV name.");
		return 0;
	}

	lp_meta.lv_name = metaname;
	lp_meta.pvh = lp->pvh;

	lv_size_bytes = (uint64_t)lp->extents * (uint64_t)vg->extent_size * 512;
	meta_bytes = _lv_size_bytes_to_integrity_meta_bytes(lv_size_bytes, settings->journal_sectors, vg->extent_size);
	meta_sectors = meta_bytes / 512;
	lp_meta.extents = meta_sectors / vg->extent_size;

	log_verbose("Creating integrity metadata LV %s with size %s.",
		    metaname, display_size(cmd, meta_sectors));

	dm_list_init(&lp_meta.tags);

	if (!(lp_meta.segtype = get_segtype_from_string(vg->cmd, SEG_TYPE_NAME_STRIPED)))
		return_0;

	if (!(lv = lv_create_single(vg, &lp_meta))) {
		log_error("Failed to create integrity metadata LV");
		return 0;
	}

	if (dm_list_size(&lv->segments) > 1) {
		log_error("Integrity metadata uses more than one segment.");
		return 0;
	}

	*meta_lv = lv;
	return 1;
}

int lv_extend_integrity_in_raid(struct logical_volume *lv, struct dm_list *pvh)
{
	struct cmd_context *cmd = lv->vg->cmd;
	struct volume_group *vg = lv->vg;
	const struct segment_type *segtype;
	struct lv_segment *seg_top, *seg_image;
	struct logical_volume *lv_image;
	struct logical_volume *lv_iorig;
	struct logical_volume *lv_imeta;
	struct dm_list allocatable_pvs;
	struct dm_list *use_pvh;
	uint64_t lv_size_bytes, meta_bytes, meta_sectors, prev_meta_sectors;
	uint32_t meta_extents, prev_meta_extents;
	uint32_t area_count, s;

	if (!lv_is_raid(lv))
		return_0;

	seg_top = first_seg(lv);
		                
	if (!(segtype = get_segtype_from_string(cmd, SEG_TYPE_NAME_STRIPED)))
		return_0;

	area_count = seg_top->area_count;

	for (s = 0; s < area_count; s++) {
		lv_image = seg_lv(seg_top, s);
		seg_image = first_seg(lv_image);

		if (!(lv_imeta = seg_image->integrity_meta_dev)) {
			log_error("LV %s segment has no integrity metadata device.", display_lvname(lv));
			return 0;
		}

		if (!(lv_iorig = seg_lv(seg_image, 0))) {
			log_error("LV %s integrity segment has no origin", display_lvname(lv));
			return 0;
		}

		lv_size_bytes = lv_iorig->size * 512;
		meta_bytes = _lv_size_bytes_to_integrity_meta_bytes(lv_size_bytes, 0, 0);
		meta_sectors = meta_bytes / 512;
		meta_extents = meta_sectors / vg->extent_size;

		prev_meta_sectors = lv_imeta->size;
		prev_meta_extents = prev_meta_sectors / vg->extent_size;

		if (meta_extents <= prev_meta_extents) {
			log_debug("extend not needed for imeta LV %s", lv_imeta->name);
			continue;
		}

		/*
		 * We only allow lv_imeta to exist on a single PV (for now),
		 * so the allocatable_pvs is the one PV currently used by
		 * lv_imeta.
		 */
		dm_list_init(&allocatable_pvs);

		if (!get_pv_list_for_lv(cmd->mem, lv_imeta, &allocatable_pvs)) {
			log_error("Failed to build list of PVs for extending %s.", display_lvname(lv_imeta));
			return 0;
		}

		use_pvh = &allocatable_pvs;

		if (!lv_extend(lv_imeta, segtype, 1, 0, 0, 0,
			       meta_extents - prev_meta_extents,
			       use_pvh, lv_imeta->alloc, 0)) {
			log_error("Failed to extend integrity metadata LV %s", lv_imeta->name);
			return 0;
		}
	}

	return 1;
}

int lv_remove_integrity_from_raid(struct logical_volume *lv)
{
	struct logical_volume *iorig_lvs[DEFAULT_RAID_MAX_IMAGES];
	struct logical_volume *imeta_lvs[DEFAULT_RAID_MAX_IMAGES];
	struct cmd_context *cmd = lv->vg->cmd;
	struct volume_group *vg = lv->vg;
	struct lv_segment *seg_top, *seg_image;
	struct logical_volume *lv_image;
	struct logical_volume *lv_iorig;
	struct logical_volume *lv_imeta;
	uint32_t area_count, s;
	int is_active = lv_is_active(lv);

	seg_top = first_seg(lv);

	if (!seg_is_raid1(seg_top) && !seg_is_raid4(seg_top) &&
	    !seg_is_any_raid5(seg_top) && !seg_is_any_raid6(seg_top) &&
	    !seg_is_any_raid10(seg_top)) {
		log_error("LV %s segment is unsupported raid for integrity.", display_lvname(lv));
		return 0;
	}

	area_count = seg_top->area_count;

	for (s = 0; s < area_count; s++) {
		lv_image = seg_lv(seg_top, s);
		seg_image = first_seg(lv_image);

		if (!(lv_imeta = seg_image->integrity_meta_dev)) {
			log_error("LV %s segment has no integrity metadata device.", display_lvname(lv));
			return 0;
		}

		if (!(lv_iorig = seg_lv(seg_image, 0))) {
			log_error("LV %s integrity segment has no origin", display_lvname(lv));
			return 0;
		}

		if (!remove_seg_from_segs_using_this_lv(seg_image->integrity_meta_dev, seg_image))
			return_0;

		iorig_lvs[s] = lv_iorig;
		imeta_lvs[s] = lv_imeta;

		lv_image->status &= ~INTEGRITY;
		seg_image->integrity_meta_dev = NULL;
		seg_image->integrity_data_sectors = 0;
		memset(&seg_image->integrity_settings, 0, sizeof(seg_image->integrity_settings));

		if (!remove_layer_from_lv(lv_image, lv_iorig))
			return_0;
	}

	if (is_active) {
		/* vg_write(), suspend_lv(), vg_commit(), resume_lv() */
		if (!lv_update_and_reload(lv)) {
			log_error("Failed to update and reload LV after integrity remove.");
			return 0;
                }
	}

	for (s = 0; s < area_count; s++) {
		lv_iorig = iorig_lvs[s];
		lv_imeta = imeta_lvs[s];

		if (is_active) {
			if (!deactivate_lv(cmd, lv_iorig))
				log_error("Failed to deactivate unused iorig LV %s.", lv_iorig->name);

			if (!deactivate_lv(cmd, lv_imeta))
				log_error("Failed to deactivate unused imeta LV %s.", lv_imeta->name);
		}

		lv_imeta->status &= ~INTEGRITY_METADATA;
		lv_set_visible(lv_imeta);

		if (!lv_remove(lv_iorig))
			log_error("Failed to remove unused iorig LV %s.", lv_iorig->name);

		if (!lv_remove(lv_imeta))
			log_error("Failed to remove unused imeta LV %s.", lv_imeta->name);
	}

	if (!vg_write(vg) || !vg_commit(vg))
		return_0;

	return 1;
}

int integrity_mode_set(const char *mode, struct integrity_settings *settings)
{
	if (!mode)
		settings->mode[0] = DEFAULT_MODE;
	else if (!strcmp(mode, "bitmap") || !strcmp(mode, "B"))
		settings->mode[0] = 'B';
	else if (!strcmp(mode, "journal") || !strcmp(mode, "J"))
		settings->mode[0] = 'J';
	else {
		log_error("Invalid raid integrity mode (use \"bitmap\" or \"journal\")");
		return 0;
	}
	return 1;
}

static int _set_integrity_block_size(struct cmd_context *cmd, struct logical_volume *lv, int is_active,
				     struct integrity_settings *settings,
				     int lbs_4k, int lbs_512, int pbs_4k, int pbs_512)
{
	char pathname[PATH_MAX];
	uint32_t fs_block_size = 0;
	int rv;

	if (lbs_4k && lbs_512) {
		log_error("Integrity requires consistent logical block size for LV devices.");
		goto bad;
	}

	if (settings->block_size &&
	    (settings->block_size != 512 && settings->block_size != 1024 &&
	     settings->block_size != 2048 && settings->block_size != 4096)) {
		log_error("Invalid integrity block size, possible values are 512, 1024, 2048, 4096");
		goto bad;
	}

	if (lbs_4k && settings->block_size && (settings->block_size < 4096)) {
		log_error("Integrity block size %u not allowed with device logical block size 4096.",
			  settings->block_size);
		goto bad;
	}

	if (!strcmp(cmd->name, "lvcreate")) {
		if (lbs_4k) {
			settings->block_size = 4096;
		} else if (lbs_512 && pbs_4k && !pbs_512) {
			settings->block_size = 4096;
		} else if (lbs_512) {
			if (!settings->block_size)
				settings->block_size = 512;
		} else if (!lbs_4k && !lbs_512) {
			if (!settings->block_size)
				settings->block_size = 512;
			log_print_unless_silent("Using integrity block size %u with unknown device logical block size.",
						settings->block_size);
		} else {
			goto_bad;
		}

	} else if (!strcmp(cmd->name, "lvconvert")) {
		if (dm_snprintf(pathname, sizeof(pathname), "%s%s/%s", cmd->dev_dir,
				lv->vg->name, lv->name) < 0) {
			log_error("Path name too long to get LV block size %s", display_lvname(lv));
			goto bad;
		}

		/*
		 * fs_block_size_and_type() returns the libblkid BLOCK_SIZE value,
		 * where libblkid has fs-specific code to set BLOCK_SIZE to the
		 * value we need here.
		 *
		 * The term "block size" here may not equate directly to what the fs
		 * calls the block size, e.g. xfs calls this the sector size (and
		 * something different the block size); while ext4 does call this
		 * value the block size, but it's possible values are not the same
		 * as xfs's, and do not seem to relate directly to the device LBS.
		 */
		rv = fs_block_size_and_type(pathname, &fs_block_size, NULL, NULL);
		if (!rv || !fs_block_size) {
			unsigned use_bs;

			if (lbs_4k && pbs_4k) {
				use_bs = 4096;
			} else if (lbs_512 && pbs_512) {
				use_bs = 512;
			} else if (lbs_512 && pbs_4k) {
				if (settings->block_size == 4096)
					use_bs = 4096;
				else
					use_bs = 512;
			} else {
				use_bs = 512;
			}

			if (settings->block_size && (settings->block_size != use_bs)) {
				log_error("Cannot use integrity block size %u with unknown file system block size, logical block size %u, physical block size %u.",
					   settings->block_size, lbs_4k ? 4096 : 512, pbs_4k ? 4096 : 512);
				goto bad;
			}

			settings->block_size = use_bs;

			log_print_unless_silent("Using integrity block size %u for unknown file system block size, logical block size %u, physical block size %u.",
						settings->block_size, lbs_4k ? 4096 : 512, pbs_4k ? 4096 : 512);
			goto out;
		}

		if (!settings->block_size) {
			if (is_active && lbs_512) {
				/* increasing the lbs from 512 to 4k under an active LV could cause problems
				   for an application that expects a given io size/alignment is possible. */
				settings->block_size = 512;
				if (fs_block_size > 512)
					log_print_unless_silent("Limiting integrity block size to 512 because the LV is active.");
			} else if (fs_block_size <= 4096)
				settings->block_size = fs_block_size;
			else
				settings->block_size = 4096; /* dm-integrity max is 4096 */
			log_print_unless_silent("Using integrity block size %u for file system block size %u.",
						settings->block_size, fs_block_size);
		} else {
			/* let user specify integrity block size that is less than fs block size */
			if (settings->block_size > fs_block_size) {
				log_error("Integrity block size %u cannot be larger than file system block size %u.",
					  settings->block_size, fs_block_size);
				goto bad;
			}
			log_print_unless_silent("Using integrity block size %u for file system block size %u.",
						settings->block_size, fs_block_size);
		}
	}
out:
	return 1;
bad:
	return 0;
}

/*
 * Add integrity to each raid image.
 *
 * for each rimage_N:
 * . create and allocate a new linear LV rimage_N_imeta
 * . move the segments from rimage_N to a new rimage_N_iorig
 * . add an integrity segment to rimage_N with
 *   origin=rimage_N_iorig, meta_dev=rimage_N_imeta
 *
 * Before:
 * rimage_0
 *   segment1: striped: pv0:A
 * rimage_1
 *   segment1: striped: pv1:B
 *
 * After:
 * rimage_0
 *   segment1: integrity: rimage_0_iorig, rimage_0_imeta
 * rimage_1
 *   segment1: integrity: rimage_1_iorig, rimage_1_imeta
 * rimage_0_iorig
 *   segment1: striped: pv0:A
 * rimage_1_iorig
 *   segment1: striped: pv1:B
 * rimage_0_imeta
 *   segment1: striped: pv2:A
 * rimage_1_imeta
 *   segment1: striped: pv2:B
 *    
 */

int lv_add_integrity_to_raid(struct logical_volume *lv, struct integrity_settings *settings,
			     struct dm_list *pvh, struct logical_volume *lv_imeta_0)
{
	char imeta_name[NAME_LEN];
	char *imeta_name_dup;
	struct lvcreate_params lp;
	struct dm_list allocatable_pvs;
	struct logical_volume *imeta_lvs[DEFAULT_RAID_MAX_IMAGES];
	struct cmd_context *cmd = lv->vg->cmd;
	struct volume_group *vg = lv->vg;
	struct logical_volume *lv_image, *lv_imeta;
	struct lv_segment *seg_top, *seg_image;
	struct pv_list *pvl;
	const struct segment_type *segtype;
	struct integrity_settings *set = NULL;
	struct dm_list *use_pvh = NULL;
	uint32_t area_count, s;
	uint32_t revert_meta_lvs = 0;
	int lbs_4k = 0, lbs_512 = 0, lbs_unknown = 0;
	int pbs_4k = 0, pbs_512 = 0, pbs_unknown = 0;
	int is_active;

	memset(imeta_lvs, 0, sizeof(imeta_lvs));

	is_active = lv_is_active(lv);

	if (dm_list_size(&lv->segments) != 1)
		return_0;

	if (!dm_list_empty(&lv->segs_using_this_lv)) {
		log_error("Integrity can only be added to top level raid LV.");
		return 0;
	}

	seg_top = first_seg(lv);
	area_count = seg_top->area_count;

	if (!seg_is_raid1(seg_top) && !seg_is_raid4(seg_top) &&
	    !seg_is_any_raid5(seg_top) && !seg_is_any_raid6(seg_top) &&
	    !seg_is_any_raid10(seg_top)) {
		log_error("Integrity can only be added to raid1,4,5,6,10.");
		return 0;
	}

	/*
	 * For each rimage, create an _imeta LV for integrity metadata.
	 * Each needs to be zeroed.
	 */
	for (s = 0; s < area_count; s++) {
		struct logical_volume *meta_lv;
		struct wipe_params wipe = { .do_zero = 1 };

		if (s >= DEFAULT_RAID_MAX_IMAGES)
			goto_bad;

		lv_image = seg_lv(seg_top, s);

		/*
		 * This function is used to add integrity to new images added
		 * to the raid, in which case old images will already be
		 * integrity.
		 */
		if (seg_is_integrity(first_seg(lv_image)))
			continue;

		if (!seg_is_striped(first_seg(lv_image))) {
			log_error("raid image must be linear to add integrity");
			goto bad;
		}

		/*
		 * Use an existing lv_imeta from previous linear+integrity LV.
		 * FIXME: is it guaranteed that lv_image_0 is the existing?
		 */
		if (!s && lv_imeta_0) {
			if (dm_snprintf(imeta_name, sizeof(imeta_name), "%s_imeta", lv_image->name) > 0) {
				if ((imeta_name_dup = dm_pool_strdup(vg->vgmem, imeta_name)))
					lv_imeta_0->name = imeta_name_dup;
			}
			imeta_lvs[0] = lv_imeta_0;
			continue;
		}

		dm_list_init(&allocatable_pvs);

		if (!get_pv_list_for_lv(cmd->mem, lv_image, &allocatable_pvs)) {
			log_error("Failed to build list of PVs for %s.", display_lvname(lv_image));
			goto bad;
		}

		dm_list_iterate_items(pvl, &allocatable_pvs) {
			unsigned int pbs = 0;
			unsigned int lbs = 0;

			if (!dev_get_direct_block_sizes(pvl->pv->dev, &pbs, &lbs)) {
				lbs_unknown++;
				pbs_unknown++;
				continue;
			}
			if (lbs == 4096)
				lbs_4k++;
			else if (lbs == 512)
				lbs_512++;
			else
				lbs_unknown++;
			if (pbs == 4096)
				pbs_4k++;
			else if (pbs == 512)
				pbs_512++;
			else
				pbs_unknown++;
		}

		use_pvh = &allocatable_pvs;

		/*
		 * allocate a new linear LV NAME_rimage_N_imeta
		 */
		memset(&lp, 0, sizeof(lp));
		lp.lv_name = lv_image->name;
		lp.pvh = use_pvh;
		lp.extents = lv_image->size / vg->extent_size;

		if (!_lv_create_integrity_metadata(cmd, vg, &lp, settings, &meta_lv))
			goto_bad;

		revert_meta_lvs++;

		/* Used below to set up the new integrity segment. */
		imeta_lvs[s] = meta_lv;

		/*
		 * dm-integrity requires the metadata LV header to be zeroed.
		 */

		if (!activate_lv(cmd, meta_lv)) {
			log_error("Failed to activate LV %s to zero", display_lvname(meta_lv));
			goto bad;
		}

		if (!wipe_lv(meta_lv, wipe)) {
			log_error("Failed to zero LV for integrity metadata %s", display_lvname(meta_lv));
			if (deactivate_lv(cmd, meta_lv))
				log_error("Failed to deactivate LV %s after zero", display_lvname(meta_lv));
			goto bad;
		}

		if (!deactivate_lv(cmd, meta_lv)) {
			log_error("Failed to deactivate LV %s after zero", display_lvname(meta_lv));
			goto bad;
		}
	}

	if (!is_active) {
		/* checking block size of fs on the lv requires the lv to be active */
		if (!activate_lv(cmd, lv)) {
			log_error("Failed to activate LV to check block size %s", display_lvname(lv));
			goto bad;
		}
		if (!sync_local_dev_names(cmd))
			stack;
	}

	/*
	 * Set settings->block_size which will be copied to segment settings below.
	 * integrity block size chosen based on device logical block size and
	 * file system block size.
	 */
	if (!_set_integrity_block_size(cmd, lv, is_active, settings, lbs_4k, lbs_512, pbs_4k, pbs_512)) {
		if (!is_active && !deactivate_lv(cmd, lv))
			stack;
		goto_bad;
	}

	if (!is_active) {
		if (!deactivate_lv(cmd, lv)) {
			log_error("Failed to deactivate LV after checking block size %s", display_lvname(lv));
			goto bad;
		}
	}

	/*
	 * For each rimage, move its segments to a new rimage_iorig and give
	 * the rimage a new integrity segment.
	 */
	for (s = 0; s < area_count; s++) {
		lv_image = seg_lv(seg_top, s);

		/* Not adding integrity to this image. */
		if (!imeta_lvs[s])
			continue;

		if (!(segtype = get_segtype_from_string(cmd, SEG_TYPE_NAME_INTEGRITY)))
			goto_bad;

		log_debug("Adding integrity to raid image %s", lv_image->name);

		/*
		 * lv_iorig returned by insert_layer_for_lv() (but unused here)
		 * "lv_iorig" is a new LV with new id, but with the segments
		 * from "lv_image". "lv_image" keeps the existing name and id,
		 * but gets a new integrity segment, in place of the segments
		 * that were moved to lv_iorig.
		 */
		/* coverity[format_string_injection] lv name is already validated */
		if (!insert_layer_for_lv(cmd, lv_image, 0, "_iorig"))
			goto_bad;

		lv_image->status |= INTEGRITY;

		/*
		 * Set up the new first segment of lv_image as integrity.
		 */
		seg_image = first_seg(lv_image);
		seg_image->segtype = segtype;

		lv_imeta = imeta_lvs[s];
		lv_imeta->status |= INTEGRITY_METADATA;
		lv_set_hidden(lv_imeta);
		seg_image->integrity_data_sectors = lv_image->size;
		seg_image->integrity_meta_dev = lv_imeta;
		seg_image->integrity_recalculate = 1;

		memcpy(&seg_image->integrity_settings, settings, sizeof(struct integrity_settings));
		set = &seg_image->integrity_settings;

		if (!set->mode[0])
			set->mode[0] = DEFAULT_MODE;

		if (!set->tag_size)
			set->tag_size = DEFAULT_TAG_SIZE;

		if (!set->block_size)
			set->block_size = DEFAULT_BLOCK_SIZE;

		if (!set->internal_hash)
			set->internal_hash = DEFAULT_INTERNAL_HASH;
	}

	if (is_active) {
		log_debug("Writing VG and updating LV with new integrity LV %s", lv->name);

		/* vg_write(), suspend_lv(), vg_commit(), resume_lv() */
		if (!lv_update_and_reload(lv)) {
			log_error("LV update and reload failed");
			goto bad;
		}
		revert_meta_lvs = 0;

	} else {
		log_debug("Writing VG with new integrity LV %s", lv->name);

		if (!vg_write(vg) || !vg_commit(vg))
			goto_bad;

		revert_meta_lvs = 0;

		/*
		 * This first activation includes "recalculate" which starts the
		 * kernel's recalculating (initialization) process.
		 */

		log_debug("Activating to start integrity initialization for LV %s", lv->name);

		if (!activate_lv(cmd, lv)) {
			log_error("Failed to activate integrity LV to initialize.");
			goto bad;
		}
	}

	/*
	 * Now that the device is being initialized, update the VG to clear
	 * integrity_recalculate so that subsequent activations will not
	 * include "recalculate" and restart initialization.
	 */

	log_debug("Writing VG with initialized integrity LV %s", lv->name);

	for (s = 0; s < area_count; s++) {
		lv_image = seg_lv(seg_top, s);
		seg_image = first_seg(lv_image);
		seg_image->integrity_recalculate = 0;
	}

	if (!vg_write(vg) || !vg_commit(vg))
		goto_bad;

	return 1;

bad:
	log_error("Failed to add integrity.");

	if (revert_meta_lvs) {
		for (s = 0; s < DEFAULT_RAID_MAX_IMAGES; s++) {
			if (!imeta_lvs[s])
				continue;
			if (!lv_remove(imeta_lvs[s]))
				log_error("New integrity metadata LV may require manual removal.");
		}
	}
			       
	if (!vg_write(vg) || !vg_commit(vg))
		log_error("New integrity metadata LV may require manual removal.");

	return 0;
}

/*
 * This should rarely if ever be used.  A command that adds integrity
 * to an LV will activate and then clear the flag.  If it fails before
 * clearing the flag, then this function will be used by a subsequent
 * activation to clear the flag.
 */
void lv_clear_integrity_recalculate_metadata(struct logical_volume *lv)
{
	struct volume_group *vg = lv->vg;
	struct logical_volume *lv_image;
	struct lv_segment *seg, *seg_image;
	uint32_t s;

	if (!lv_is_raid(lv) && !lv_is_integrity(lv)) {
		log_error("Invalid LV type for clearing integrity");
		return;
	}

	seg = first_seg(lv);

	if (seg_is_raid(seg)) {
		for (s = 0; s < seg->area_count; s++) {
			lv_image = seg_lv(seg, s);
			seg_image = first_seg(lv_image);
			seg_image->integrity_recalculate = 0;
		}
	} else if (seg_is_integrity(seg)) {
		seg->integrity_recalculate = 0;
	}

	if (!vg_write(vg) || !vg_commit(vg)) {
		log_warn("WARNING: failed to clear integrity recalculate flag for %s",
			 display_lvname(lv));
	}
}

int lv_has_integrity_recalculate_metadata(struct logical_volume *lv)
{
	struct logical_volume *lv_image;
	struct lv_segment *seg, *seg_image;
	uint32_t s;
	int ret = 0;

	if (!lv_is_raid(lv) && !lv_is_integrity(lv))
		return 0;

	seg = first_seg(lv);

	if (seg_is_raid(seg)) {
		for (s = 0; s < seg->area_count; s++) {
			lv_image = seg_lv(seg, s);
			seg_image = first_seg(lv_image);

			if (!seg_is_integrity(seg_image))
				continue;
			if (seg_image->integrity_recalculate)
				ret = 1;
		}
	} else if (seg_is_integrity(seg)) {
		ret = seg->integrity_recalculate;
	}

	return ret;
}

int lv_raid_has_integrity(struct logical_volume *lv)
{
	struct logical_volume *lv_image;
	struct lv_segment *seg, *seg_image;
	uint32_t s;

	if (!lv_is_raid(lv))
		return 0;

	seg = first_seg(lv);

	for (s = 0; s < seg->area_count; s++) {
		lv_image = seg_lv(seg, s);
		seg_image = first_seg(lv_image);

		if (seg_is_integrity(seg_image))
			return 1;
	}

	return 0;
}

int lv_get_raid_integrity_settings(struct logical_volume *lv, struct integrity_settings **isettings)
{
	struct logical_volume *lv_image;
	struct lv_segment *seg, *seg_image;
	uint32_t s;

	if (!lv_is_raid(lv))
		return_0;

	seg = first_seg(lv);

	for (s = 0; s < seg->area_count; s++) {
		lv_image = seg_lv(seg, s);
		seg_image = first_seg(lv_image);

		if (seg_is_integrity(seg_image)) {
			*isettings = &seg_image->integrity_settings;
			return 1;
		}
	}

	return 0;
}

int lv_raid_integrity_total_mismatches(struct cmd_context *cmd,
			    const struct logical_volume *lv,
			    uint64_t *mismatches)
{
	struct logical_volume *lv_image;
	struct lv_segment *seg, *seg_image;
	uint32_t s;
	uint64_t mismatches_image;
	uint64_t total = 0;
	int errors = 0;

	if (!lv_is_raid(lv))
		return 0;

	seg = first_seg(lv);

	for (s = 0; s < seg->area_count; s++) {
		lv_image = seg_lv(seg, s);
		seg_image = first_seg(lv_image);

		if (!seg_is_integrity(seg_image))
			continue;

		mismatches_image = 0;

		if (!lv_integrity_mismatches(cmd, lv_image, &mismatches_image))
			errors++;

		total += mismatches_image;
	}
	*mismatches = total;

	if (errors)
		return 0;
	return 1;
}

int lv_integrity_mismatches(struct cmd_context *cmd,
			    const struct logical_volume *lv,
			    uint64_t *mismatches)
{
	struct lv_with_info_and_seg_status status = {
		.seg_status.type = SEG_STATUS_NONE,
	};

	if (lv_is_raid(lv) && lv_raid_has_integrity((struct logical_volume *)lv))
		return lv_raid_integrity_total_mismatches(cmd, lv, mismatches);

	if (!lv_is_integrity(lv))
		return_0;

	status.seg_status.seg = first_seg(lv);

	/* FIXME: why reporter_pool? */
	if (!(status.seg_status.mem = dm_pool_create("reporter_pool", 1024))) {
		log_error("Failed to get mem for LV status.");
		return 0;
	}

	if (!lv_info_with_seg_status(cmd, first_seg(lv), &status, 1, 1)) {
		log_error("Failed to get device mapper status for %s", display_lvname(lv));
		goto fail;
	}

	if (!status.info.exists)
		goto fail;

	if (status.seg_status.type != SEG_STATUS_INTEGRITY) {
		log_error("Invalid device mapper status type (%d) for %s",
			  (uint32_t)status.seg_status.type, display_lvname(lv));
		goto fail;
	}

	*mismatches = status.seg_status.integrity->number_of_mismatches;

	dm_pool_destroy(status.seg_status.mem);
	return 1;

fail:
	dm_pool_destroy(status.seg_status.mem);
	return 0;
}

int integrity_settings_to_str_list(struct integrity_settings *settings, struct dm_list *result, struct dm_pool *mem)
{
	int errors = 0;

	if (settings->journal_watermark_set)
		if (!setting_str_list_add("journal_watermark", settings->journal_watermark, NULL, result, mem))
                        errors++;

	if (settings->commit_time_set)
		if (!setting_str_list_add("commit_time", settings->commit_time, NULL, result, mem))
			errors++;

	if (settings->bitmap_flush_interval_set)
		if (!setting_str_list_add("bitmap_flush_interval", settings->bitmap_flush_interval, NULL, result, mem))
			errors++;

	if (settings->allow_discards_set)
		if (!setting_str_list_add("allow_discards", settings->allow_discards, NULL, result, mem))
			errors++;
	if (errors)
		log_warn("Failed to create list of integrity settings.");

	return 1;
}


