Viewing: liblustreapi_layout.c

// SPDX-License-Identifier: LGPL-2.1+
/*
 * Copyright (c) 2016, 2017, Intel Corporation.
 */
/*
 * This file is part of Lustre, http://www.lustre.org/
 *
 * lustreapi library for layout calls for interacting with the layout of
 * Lustre files while hiding details of the internal data structures
 * from the user.
 *
 * Author: Ned Bass <bass6@llnl.gov>
 */

#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
#include <assert.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/xattr.h>
#include <time.h>

#include <libcfs/util/list.h>
#include <lustre/lustreapi.h>
#include <linux/lustre/erasure_code.h>
#include <linux/lustre/lustre_idl.h>
#include "lstddef.h"
#include "lustreapi_internal.h"

/**
 * Data structure when computing/verifying parities holding a list
 * of all regions in the parity stripe that depends on data from
 * the data stripes.
 * This allows us to only verify/write the parts of the parity stripes
 * that are actually used for protecting the data stripes and to ignore
 * those regions that do not need parity data.
 * For example regions that are covered by a "hole" in all the corresponding
 * data stripes of are beyond eof for the data mirror.
 */
struct ec_parity_coverage {
	struct ec_parity_coverage *next;
	uint64_t pos;
	uint64_t len;
};


/**
 * Layout component, which contains all attributes of a plain
 * V1/V3/FOREIGN(HSM) layout.
 */
struct llapi_layout_comp {
	uint64_t	llc_pattern;
	union {
		struct { /* For plain layout. */
			uint64_t	llc_stripe_size;
			uint64_t	llc_stripe_count;
			uint64_t	llc_stripe_offset;
			/**
			 * Add 1 so user always gets back a null terminated
			 * string.
			 */
			char		llc_pool_name[LOV_MAXPOOLNAME + 1];
			/**
			 * Number of objects in llc_objects array if was
			 * initialized.
			 */
			uint32_t	llc_objects_count;
			/**
			 * EC parity comp specific fields.
			 */
			uint8_t		llc_dstripe_count;
			uint8_t		llc_cstripe_count;
			struct lov_user_ost_data_v1 *llc_objects;
		};
		struct { /* For FOREIGN/HSM layout. */
			uint32_t	 llc_length;
			uint32_t	 llc_type;
			uint32_t	 llc_hsm_flags;
			union {
				struct lov_hsm_base	 llc_hsm;
				char			*llc_value;
			};
		};
	};

	/* fields used only for composite layouts */
	struct lu_extent	llc_extent;	/* [start, end) of component */
	uint32_t		llc_id;		/* unique ID of component */
	/* mirror ID this component belongs to */
	uint32_t		llc_mirror_id;
	/* mirror link id for data and parity components */
	uint16_t		llc_mirror_link_id;
	uint32_t		llc_flags;	/* LCME_FL_* flags */
	uint64_t		llc_timestamp;	/* snapshot timestamp */
	/* linked to the llapi_layout components list */
	struct list_head	llc_list;
	bool			llc_ondisk;
};

#define llc_archive_id	llc_hsm.lhb_archive_id
#define llc_archive_ver	llc_hsm.lhb_archive_ver
#define llc_uuid	llc_hsm.lhb_uuid

/*
 * An Opaque data type abstracting the layout of a Lustre file.
 */
struct llapi_layout {
	uint32_t	llot_magic; /* LLAPI_LAYOUT_MAGIC */
	uint32_t	llot_gen;
	uint32_t	llot_flags;
	bool		llot_is_composite;
	uint16_t	llot_mirror_count;
	uint16_t	llot_curr_link_id;
	uint32_t	llot_curr_mirror_id;
	/* Cursor pointing to one of the components in llot_comp_list */
	struct llapi_layout_comp *llot_cur_comp;
	struct list_head	  llot_comp_list;
};

/**
 * llapi_layout_objects_in_lum() - Compute the number of elements in the
 * lmm_objects array of @lum with size @lum_size.
 * @lum: the struct lov_user_md to check
 * @lum_size: the number of bytes in @lum
 *
 * Return number of elements in array lum->lmm_objects
 */
static int llapi_layout_objects_in_lum(struct lov_user_md *lum, size_t lum_size)
{
	uint32_t magic;
	size_t base_size;

	if (lum->lmm_magic == __swab32(LOV_MAGIC_FOREIGN))
		return 0;

	if (lum_size < lov_user_md_size(0, LOV_MAGIC_V1))
		return 0;

	if (lum->lmm_magic == __swab32(LOV_MAGIC_V1) ||
	    lum->lmm_magic == __swab32(LOV_MAGIC_V3))
		magic = __swab32(lum->lmm_magic);
	else
		magic = lum->lmm_magic;

	base_size = lov_user_md_size(0, magic);

	if (lum_size <= base_size)
		return 0;
	else
		return (lum_size - base_size) / sizeof(lum->lmm_objects[0]);
}

/*
 * Byte-swap the fields of struct lov_user_md.
 *
 * XXX Rather than duplicating swabbing code here, we should eventually
 * refactor the needed functions in lustre/ptlrpc/pack_generic.c
 * into a library that can be shared between kernel and user code.
 */
static void
llapi_layout_swab_lov_user_md(struct lov_user_md *lum, int lum_size)
{
	int i, j, ent_count, obj_count;
	struct lov_comp_md_v1 *comp_v1 = NULL;
	struct lov_comp_md_entry_v1 *ent;
	struct lov_user_ost_data *lod;

	if (lum->lmm_magic != __swab32(LOV_MAGIC_V1) &&
	    lum->lmm_magic != __swab32(LOV_MAGIC_V3) &&
	    lum->lmm_magic != __swab32(LOV_MAGIC_COMP_V1))
		return;

	if (lum->lmm_magic == __swab32(LOV_MAGIC_COMP_V1))
		comp_v1 = (struct lov_comp_md_v1 *)lum;

	if (comp_v1 != NULL) {
		comp_v1->lcm_magic = __swab32(comp_v1->lcm_magic);
		comp_v1->lcm_size = __swab32(comp_v1->lcm_size);
		comp_v1->lcm_layout_gen = __swab32(comp_v1->lcm_layout_gen);
		comp_v1->lcm_flags = __swab16(comp_v1->lcm_flags);
		comp_v1->lcm_entry_count = __swab16(comp_v1->lcm_entry_count);
		ent_count = comp_v1->lcm_entry_count;
	} else {
		ent_count = 1;
	}

	for (i = 0; i < ent_count; i++) {
		if (comp_v1 != NULL) {
			ent = &comp_v1->lcm_entries[i];
			ent->lcme_id = __swab32(ent->lcme_id);
			ent->lcme_flags = __swab32(ent->lcme_flags);
			ent->lcme_time_and_id = __swab64(ent->lcme_time_and_id);
			ent->lcme_extent.e_start = __swab64(ent->lcme_extent.e_start);
			ent->lcme_extent.e_end = __swab64(ent->lcme_extent.e_end);
			ent->lcme_offset = __swab32(ent->lcme_offset);
			ent->lcme_size = __swab32(ent->lcme_size);

			lum = (struct lov_user_md *)((char *)comp_v1 +
					ent->lcme_offset);
			lum_size = ent->lcme_size;
		}

		lum->lmm_magic = __swab32(lum->lmm_magic);
		if (lum->lmm_magic == LOV_MAGIC_FOREIGN) {
			struct lov_hsm_md *lhm;

			lhm = (struct lov_hsm_md *)lum;
			lhm->lhm_length = __swab32(lhm->lhm_length);
			lhm->lhm_type = __swab32(lhm->lhm_type);
			lhm->lhm_flags = __swab32(lhm->lhm_flags);
			if (!lov_hsm_type_supported(lhm->lhm_type))
				continue;

			lhm->lhm_archive_id = __swab64(lhm->lhm_archive_id);
			lhm->lhm_archive_ver = __swab64(lhm->lhm_archive_ver);
		} else {
			obj_count = llapi_layout_objects_in_lum(lum, lum_size);

			lum->lmm_pattern = __swab32(lum->lmm_pattern);
			lum->lmm_stripe_size = __swab32(lum->lmm_stripe_size);
			lum->lmm_stripe_count = __swab16(lum->lmm_stripe_count);
			lum->lmm_stripe_offset =
				__swab16(lum->lmm_stripe_offset);

			if (lum->lmm_magic != LOV_MAGIC_V1) {
				struct lov_user_md_v3 *v3;

				v3 = (struct lov_user_md_v3 *)lum;
				lod = v3->lmm_objects;
			} else {
				lod = lum->lmm_objects;
			}

			for (j = 0; j < obj_count; j++)
				lod[j].l_ost_idx = __swab32(lod[j].l_ost_idx);
		}
	}
}

/**
 * __llapi_comp_objects_realloc() - copy existing object to new object
 * @comp: existing layout to be modified
 * @new_stripes: number of stripes in new layout
 *
 * (Re-)allocate llc_objects[] to @num_stripes stripes.
 * Copy over existing llc_objects[], if any, to the new llc_objects[].
 *
 * Return:
 * * %0 if the objects are re-allocated successfully
 * * %negative on error with errno set
 */
static int __llapi_comp_objects_realloc(struct llapi_layout_comp *comp,
					unsigned int new_stripes)
{
	struct lov_user_ost_data_v1 *new_objects;
	unsigned int i;

	if (new_stripes > LOV_MAX_STRIPE_COUNT) {
		errno = EINVAL;
		return -1;
	}

	if (new_stripes == comp->llc_objects_count)
		return 0;

	if (new_stripes != 0 && new_stripes <= comp->llc_objects_count)
		return 0;

	new_objects = realloc(comp->llc_objects,
			      sizeof(*new_objects) * new_stripes);
	if (new_objects == NULL && new_stripes != 0) {
		errno = ENOMEM;
		return -1;
	}

	for (i = comp->llc_objects_count; i < new_stripes; i++)
		new_objects[i].l_ost_idx = LLAPI_LAYOUT_IDX_MAX;

	comp->llc_objects = new_objects;
	comp->llc_objects_count = new_stripes;

	return 0;
}

/**
 * __llapi_comp_alloc() - Allocate storage for a llapi_layout_comp with
 * @num_stripes stripes.
 * @num_stripes: number of stripes in new layout
 *
 * Return valid pointer if allocation succeeds or NULL if allocation fails
 */
static struct llapi_layout_comp *__llapi_comp_alloc(unsigned int num_stripes)
{
	struct llapi_layout_comp *comp;

	if (num_stripes > LOV_MAX_STRIPE_COUNT) {
		errno = EINVAL;
		return NULL;
	}

	comp = calloc(1, sizeof(*comp));
	if (comp == NULL) {
		errno = ENOMEM;
		return NULL;
	}

	comp->llc_objects = NULL;
	comp->llc_objects_count = 0;

	if (__llapi_comp_objects_realloc(comp, num_stripes) < 0) {
		free(comp);
		return NULL;
	}

	/* Set defaults. */
	comp->llc_pattern = LLAPI_LAYOUT_DEFAULT;
	comp->llc_stripe_size = LLAPI_LAYOUT_DEFAULT;
	comp->llc_stripe_count = LLAPI_LAYOUT_DEFAULT;
	comp->llc_stripe_offset = LLAPI_LAYOUT_DEFAULT;
	comp->llc_pool_name[0] = '\0';
	comp->llc_extent.e_start = 0;
	comp->llc_extent.e_end = LUSTRE_EOF;
	comp->llc_flags = 0;
	comp->llc_id = 0;
	comp->llc_mirror_id = 0;
	comp->llc_mirror_link_id = LLAPI_MIRROR_LINK_NONE;
	INIT_LIST_HEAD(&comp->llc_list);

	return comp;
}

/**
 * __llapi_comp_hsm_alloc() - Alloc storage for HSM component with @length buffer.
 * @length: size of allocation
 *
 * Return valid pointer if allocation succeeds or NULL if allocate fails
 */
static struct llapi_layout_comp *__llapi_comp_hsm_alloc(uint32_t length)
{
	struct llapi_layout_comp *comp;

	if (lov_foreign_md_size(length) > XATTR_SIZE_MAX) {
		errno = EINVAL;
		return NULL;
	}

	comp = calloc(1, sizeof(*comp));
	if (comp == NULL) {
		errno = ENOMEM;
		return NULL;
	}

	comp->llc_pattern = LLAPI_LAYOUT_FOREIGN;
	comp->llc_length = length;
	comp->llc_type = LU_FOREIGN_TYPE_UNKNOWN;
	comp->llc_hsm_flags = 0;
	comp->llc_archive_id = 0;
	comp->llc_archive_ver = 0;
	comp->llc_extent.e_start = 0;
	comp->llc_extent.e_end = LUSTRE_EOF;
	comp->llc_flags = 0;
	comp->llc_id = 0;
	INIT_LIST_HEAD(&comp->llc_list);

	return comp;
}

/**
 * __llapi_comp_free() - Free memory allocated for @comp
 * @comp: previously allocated by __llapi_comp_alloc()
 */
static void __llapi_comp_free(struct llapi_layout_comp *comp)
{
	if (comp->llc_pattern != LLAPI_LAYOUT_FOREIGN &&
	    comp->llc_objects != NULL) {
		free(comp->llc_objects);
	}
	free(comp);
}

/**
 * llapi_layout_free() - Free memory allocated for @layout.
 * @layout: previously allocated by llapi_layout_alloc()
 */
void llapi_layout_free(struct llapi_layout *layout)
{
	struct llapi_layout_comp *comp, *n;

	if (layout == NULL)
		return;

	list_for_each_entry_safe(comp, n, &layout->llot_comp_list, llc_list) {
		list_del_init(&comp->llc_list);
		__llapi_comp_free(comp);
	}
	free(layout);
}

/**
 * __llapi_layout_alloc() - Allocate and initialize a llapi_layout structure.
 *
 * Returns valid llapi_layout pointer on success or NULL if memory allocation
 * fails
 */
static struct llapi_layout *__llapi_layout_alloc(void)
{
	struct llapi_layout *layout;

	layout = calloc(1, sizeof(*layout));
	if (layout == NULL) {
		errno = ENOMEM;
		return NULL;
	}

	/* Set defaults. */
	layout->llot_magic = LLAPI_LAYOUT_MAGIC;
	layout->llot_gen = 0;
	layout->llot_flags = 0;
	layout->llot_is_composite = false;
	layout->llot_mirror_count = 1;
	layout->llot_curr_link_id = 1;
	layout->llot_curr_mirror_id = 1;
	layout->llot_cur_comp = NULL;
	INIT_LIST_HEAD(&layout->llot_comp_list);

	return layout;
}

/**
 * llapi_layout_alloc() - Allocate and initialize a new plain layout.
 *
 * Return valid llapi_layout pointer on success or NULL if memory allocation
 * fails
 */
struct llapi_layout *llapi_layout_alloc(void)
{
	struct llapi_layout_comp *comp;
	struct llapi_layout *layout;

	layout = __llapi_layout_alloc();
	if (layout == NULL)
		return NULL;

	comp = __llapi_comp_alloc(0);
	if (comp == NULL) {
		free(layout);
		return NULL;
	}

	list_add_tail(&comp->llc_list, &layout->llot_comp_list);
	layout->llot_cur_comp = comp;

	return layout;
}

/**
 * llapi_layout_lum_truncated() - Check if the given @lum_size is large enough
 * to hold the required fields in @lum.
 * @lum: the struct lov_user_md to check
 * @lum_size: the number of bytes in @lum
 *
 * Return:
 * * %true the @lum_size is too small
 * * %false the @lum_size is large enough
 */
static bool llapi_layout_lum_truncated(struct lov_user_md *lum, size_t lum_size)
{
	uint32_t magic;

	if (lum_size < sizeof(lum->lmm_magic))
		return true;

	if (lum->lmm_magic == LOV_MAGIC_V1 ||
	    lum->lmm_magic == __swab32(LOV_MAGIC_V1))
		magic = LOV_MAGIC_V1;
	else if (lum->lmm_magic == LOV_MAGIC_V3 ||
		 lum->lmm_magic == __swab32(LOV_MAGIC_V3))
		magic = LOV_MAGIC_V3;
	else if (lum->lmm_magic == LOV_MAGIC_SPECIFIC ||
		 lum->lmm_magic == __swab32(LOV_MAGIC_SPECIFIC))
		magic = LOV_MAGIC_V3;
	else if (lum->lmm_magic == LOV_MAGIC_COMP_V1 ||
		 lum->lmm_magic == __swab32(LOV_MAGIC_COMP_V1))
		magic = LOV_MAGIC_COMP_V1;
	else
		return true;

	if (magic == LOV_MAGIC_V1 || magic == LOV_MAGIC_V3)
		return lum_size < lov_user_md_size(0, magic);
	else
		return lum_size < sizeof(struct lov_comp_md_v1);
}

/* Verify if the objects count in lum is consistent with the
 * stripe count in lum. It applies to regular file only.
 */
static bool llapi_layout_lum_valid(struct lov_user_md *lum, int lum_size)
{
	struct lov_comp_md_v1 *comp_v1 = NULL;
	int i, ent_count, obj_count;

	if (lum->lmm_magic == LOV_MAGIC_COMP_V1) {
		comp_v1 = (struct lov_comp_md_v1 *)lum;
		ent_count = comp_v1->lcm_entry_count;
	} else if (lum->lmm_magic == LOV_MAGIC_V1 ||
		   lum->lmm_magic == LOV_MAGIC_V3) {
		ent_count = 1;
	} else {
		return false;
	}

	for (i = 0; i < ent_count; i++) {
		if (comp_v1) {
			lum = (struct lov_user_md *)((char *)comp_v1 +
				comp_v1->lcm_entries[i].lcme_offset);
			lum_size = comp_v1->lcm_entries[i].lcme_size;

			if (lum->lmm_magic == LOV_MAGIC_FOREIGN)
				continue;
		}
		obj_count = llapi_layout_objects_in_lum(lum, lum_size);

		if (comp_v1) {
			if (!(comp_v1->lcm_entries[i].lcme_flags &
				 LCME_FL_INIT) && obj_count != 0)
				return false;
		} else if (obj_count != lum->lmm_stripe_count) {
			return false;
		}
	}
	return true;
}

/**
 * llapi_layout_get_by_xattr() - Convert data from  lov_user_md to llapi_layout
 * @lov_xattr: LOV user metadata xattr to copy data from
 * @lov_xattr_size: size the lov_xattr_size passed in
 * @flags: flags to control how layout is retrieved
 *
 * Convert the data from a lov_user_md to a newly allocated llapi_layout. The
 * caller is responsible for freeing the returned pointer.
 *
 * Return valid llapi_layout pointer on success or NULL if memory allocation
 * fails
 */
struct llapi_layout *llapi_layout_get_by_xattr(void *lov_xattr,
					      ssize_t lov_xattr_size,
					      enum llapi_layout_get_flags flags)
{
	struct lov_user_md *lum = lov_xattr;
	struct lov_comp_md_v1 *comp_v1 = NULL;
	struct lov_comp_md_entry_v1 *ent;
	struct lov_user_md *v1;
	struct llapi_layout *layout = NULL;
	struct llapi_layout_comp *comp;
	int i, ent_count = 0, obj_count = 0;

	if (lov_xattr == NULL || lov_xattr_size <= 0) {
		errno = EINVAL;
		return NULL;
	}

	/* Return an error if we got back a partial layout. */
	if (llapi_layout_lum_truncated(lov_xattr, lov_xattr_size)) {
		errno = ERANGE;
		return NULL;
	}

#if __BYTE_ORDER == __BIG_ENDIAN
	if (flags & LLAPI_LAYOUT_GET_COPY) {
		lum = malloc(lov_xattr_size);
		if (lum == NULL) {
			errno = ENOMEM;
			return NULL;
		}
		memcpy(lum, lov_xattr, lov_xattr_size);
	}
#endif

	llapi_layout_swab_lov_user_md(lum, lov_xattr_size);

#if LUSTRE_VERSION_CODE > OBD_OCD_VERSION(2, 16, 53, 0)
#define LLAPI_LXF_CHECK_OLD 0x0001
	if (flags & LLAPI_LXF_CHECK_OLD)
		flags = (flags & ~LLAPI_LXF_CHECK_OLD) | LLAPI_LAYOUT_GET_CHECK;
#endif
	if ((flags & LLAPI_LAYOUT_GET_CHECK) &&
	    !llapi_layout_lum_valid(lum, lov_xattr_size)) {
		errno = EBADSLT;
		goto out;
	}

	layout = __llapi_layout_alloc();
	if (layout == NULL) {
		errno = ENOMEM;
		goto out;
	}

	if (lum->lmm_magic == LOV_MAGIC_COMP_V1) {
		comp_v1 = (struct lov_comp_md_v1 *)lum;
		ent_count = comp_v1->lcm_entry_count;
		layout->llot_gen = comp_v1->lcm_layout_gen;
		layout->llot_is_composite = true;
		layout->llot_mirror_count = comp_v1->lcm_mirror_count + 1;
		layout->llot_gen = comp_v1->lcm_layout_gen;
		layout->llot_flags = comp_v1->lcm_flags;
	} else if (lum->lmm_magic == LOV_MAGIC_V1 ||
		   lum->lmm_magic == LOV_MAGIC_V3 ||
		   lum->lmm_magic == LOV_MAGIC_SPECIFIC) {
		ent_count = 1;
		layout->llot_is_composite = false;

		if (lov_xattr_size <= 0) {
			errno = EINVAL;
			goto out_layout;
		}
	} else {
		errno = EOPNOTSUPP;
		goto out_layout;
	}

	if (ent_count == 0) {
		errno = EINVAL;
		goto out_layout;
	}

	v1 = (struct lov_user_md *)lum;
	for (i = 0; i < ent_count; i++) {
		if (comp_v1 != NULL) {
			ent = &comp_v1->lcm_entries[i];
			v1 = (struct lov_user_md *)((char *)comp_v1 +
				ent->lcme_offset);
			lov_xattr_size = ent->lcme_size;
		} else {
			ent = NULL;
		}

		if (v1->lmm_magic == LOV_MAGIC_FOREIGN) {
			struct lov_hsm_md *lhm;

			lhm = (struct lov_hsm_md *)v1;
			if (!lov_hsm_type_supported(lhm->lhm_type))
				goto out_layout;

			if (lhm->lhm_length != sizeof(struct lov_hsm_base))
				goto out_layout;

			comp = __llapi_comp_hsm_alloc(lhm->lhm_length);
			if (comp == NULL)
				goto out_layout;

			comp->llc_length = lhm->lhm_length;
			comp->llc_type = lhm->lhm_type;
			comp->llc_hsm_flags = lhm->lhm_flags;
			comp->llc_archive_id = lhm->lhm_archive_id;
			comp->llc_archive_ver = lhm->lhm_archive_ver;
			memcpy(comp->llc_uuid, lhm->lhm_archive_uuid,
			       sizeof(comp->llc_uuid));
		} else {
			obj_count = llapi_layout_objects_in_lum(v1,
								lov_xattr_size);
			comp = __llapi_comp_alloc(obj_count);
			if (comp == NULL)
				goto out_layout;
		}

		if (ent != NULL) {
			comp->llc_extent.e_start = ent->lcme_extent.e_start;
			comp->llc_extent.e_end = ent->lcme_extent.e_end;
			comp->llc_id = ent->lcme_id;
			comp->llc_mirror_id = mirror_id_of(ent->lcme_id);
			comp->llc_flags = ent->lcme_flags;
			comp->llc_timestamp = lcme_timestamp_time_unpack(
				ent->lcme_time_and_id);
			comp->llc_mirror_link_id =
				lcme_timestamp_id_unpack(ent->lcme_time_and_id);
			comp->llc_dstripe_count = ent->lcme_dstripe_count;
			comp->llc_cstripe_count = ent->lcme_cstripe_count;
		} else {
			comp->llc_extent.e_start = 0;
			comp->llc_extent.e_end = LUSTRE_EOF;
			comp->llc_id = 0;
			comp->llc_flags = 0;
		}

		if (v1->lmm_magic == LOV_MAGIC_FOREIGN) {
			comp->llc_pattern = LLAPI_LAYOUT_FOREIGN;
			goto comp_add;
		}

		if (v1->lmm_pattern == LOV_PATTERN_RAID0)
			comp->llc_pattern = LLAPI_LAYOUT_RAID0;
		else if (v1->lmm_pattern == (LOV_PATTERN_RAID0 |
					 LOV_PATTERN_OVERSTRIPING))
			comp->llc_pattern = LLAPI_LAYOUT_OVERSTRIPING;
		else if (v1->lmm_pattern & LOV_PATTERN_MDT)
			comp->llc_pattern = LLAPI_LAYOUT_MDT;
		else
			/* Lustre only supports RAID0, overstripping,
			 * DoM and FOREIGN/HSM for now.
			 */
			comp->llc_pattern = v1->lmm_pattern;

		if (v1->lmm_stripe_size == 0)
			comp->llc_stripe_size = LLAPI_LAYOUT_DEFAULT;
		else
			comp->llc_stripe_size = v1->lmm_stripe_size;

		if (v1->lmm_stripe_count >= LOV_ALL_STRIPES_WIDE &&
		    v1->lmm_stripe_count <= LOV_ALL_STRIPES)
			comp->llc_stripe_count = LLAPI_LAYOUT_WIDE_MIN +
						(LOV_ALL_STRIPES -
						 v1->lmm_stripe_count);
		else if (v1->lmm_stripe_count == 0)
			comp->llc_stripe_count = LLAPI_LAYOUT_DEFAULT;
		else
			comp->llc_stripe_count = v1->lmm_stripe_count;

		if (v1->lmm_stripe_offset ==
		    (typeof(v1->lmm_stripe_offset))-1)
			comp->llc_stripe_offset = LLAPI_LAYOUT_DEFAULT;
		else
			comp->llc_stripe_offset = v1->lmm_stripe_offset;

		if (v1->lmm_magic != LOV_USER_MAGIC_V1) {
			const struct lov_user_md_v3 *lumv3;
			size_t size = sizeof(comp->llc_pool_name);
			int rc;

			lumv3 = (struct lov_user_md_v3 *)v1;
			rc = snprintf(comp->llc_pool_name, size,
				      "%s", lumv3->lmm_pool_name);
			/* Avoid GCC 7 format-truncation warning. */
			if (rc > size)
				comp->llc_pool_name[size - 1] = 0;

			memcpy(comp->llc_objects, lumv3->lmm_objects,
			       obj_count * sizeof(lumv3->lmm_objects[0]));
		} else {
			const struct lov_user_md_v1 *lumv1;
			lumv1 = (struct lov_user_md_v1 *)v1;
			memcpy(comp->llc_objects, lumv1->lmm_objects,
			       obj_count * sizeof(lumv1->lmm_objects[0]));
		}

		if (obj_count != 0)
			comp->llc_stripe_offset =
				comp->llc_objects[0].l_ost_idx;
comp_add:
		comp->llc_ondisk = true;
		list_add_tail(&comp->llc_list, &layout->llot_comp_list);
		layout->llot_cur_comp = comp;
	}

out:
	if (lum != lov_xattr)
		free(lum);
	return layout;
out_layout:
	llapi_layout_free(layout);
	layout = NULL;
	goto out;
}

/**
 * get_lum_size() - Get @lum_size from a lov_user_md @lum
 * @lum: lov_user_md to get lum_size
 *
 * Returns -1 on error and lum_size on success
 */
static size_t get_lum_size(struct lov_user_md *lum)
{
	size_t lum_size;

	if (lum == NULL) {
		errno = EINVAL;
		return -1;
	}

	if (lum->lmm_magic == LOV_USER_MAGIC_COMP_V1)
		lum_size = ((struct lov_comp_md_v1 *)lum)->lcm_size;
	else if (lum->lmm_magic == LOV_USER_MAGIC_SPECIFIC)
		lum_size = lov_user_md_size(lum->lmm_stripe_count,
					    lum->lmm_magic);
	else
		lum_size = lov_user_md_size(0, lum->lmm_magic);

	return lum_size;
}


/**
 * llapi_layout_set_by_xattr() - Set @lum on the file descriptor @fd and write
 * the lum on the file referenced by the file descriptor
 * @fd: open file descriptor
 * @lum: lov_user_md to write on the file
 *
 * Returns -1 on error and set errno
 */
int llapi_layout_set_by_xattr(int fd, struct lov_user_md *lum)
{
	int rc;
	ssize_t lum_size;

	lum_size = get_lum_size(lum);
	if (lum_size < 0) {
		errno = EINVAL;
		return -1;
	}

	rc = fsetxattr(fd, XATTR_LUSTRE_LOV, lum, lum_size, 0);
	return rc;
}

enum lov_pattern llapi_pattern_to_lov(uint64_t llapi_pattern)
{
	enum lov_pattern lov_pattern;

	switch (llapi_pattern) {
	case LLAPI_LAYOUT_DEFAULT:
		lov_pattern = LOV_PATTERN_RAID0;
		break;
	case LLAPI_LAYOUT_RAID0:
		lov_pattern = LOV_PATTERN_RAID0;
		break;
	case LLAPI_LAYOUT_MDT:
		lov_pattern = LOV_PATTERN_MDT;
		break;
	case LLAPI_LAYOUT_FOREIGN:
		lov_pattern = LOV_PATTERN_FOREIGN;
		break;
	case LLAPI_LAYOUT_OVERSTRIPING:
		lov_pattern = LOV_PATTERN_OVERSTRIPING | LOV_PATTERN_RAID0;
		break;
	default:
		lov_pattern = EINVAL;
	}

	return lov_pattern;
}

/**
 * llapi_layout_to_lum() - Convert data from llapi_layout to lov_user_md.
 * @layout: the layout to copy from
 *
 * Convert the data from a llapi_layout to a newly allocated lov_user_md.
 * The caller is responsible for freeing the returned pointer.
 *
 * Return valid lov_user_md pointer on success or NULL if memory allocation
 * fails or the layout is invalid
 */
static struct lov_user_md *
llapi_layout_to_lum(const struct llapi_layout *layout)
{
	struct llapi_layout_comp *comp;
	struct lov_comp_md_v1 *comp_v1 = NULL;
	struct lov_comp_md_entry_v1 *ent;
	struct lov_user_md *lum = NULL;
	size_t lum_size = 0;
	int ent_idx = 0;
	uint32_t offset = 0;

	if (layout == NULL ||
	    list_empty((struct list_head *)&layout->llot_comp_list)) {
		errno = EINVAL;
		return NULL;
	}

	/* Allocate header of lov_comp_md_v1 if necessary */
	if (layout->llot_is_composite) {
		int comp_cnt = 0;

		list_for_each_entry(comp, &layout->llot_comp_list, llc_list)
			comp_cnt++;

		lum_size = sizeof(*comp_v1) + comp_cnt * sizeof(*ent);
		lum = calloc(lum_size, 1);
		if (lum == NULL) {
			errno = ENOMEM;
			return NULL;
		}
		comp_v1 = (struct lov_comp_md_v1 *)lum;
		comp_v1->lcm_magic = LOV_USER_MAGIC_COMP_V1;
		comp_v1->lcm_size = lum_size;
		comp_v1->lcm_layout_gen = 0;
		comp_v1->lcm_flags = layout->llot_flags;
		comp_v1->lcm_entry_count = comp_cnt;
		comp_v1->lcm_mirror_count = layout->llot_mirror_count - 1;
		offset += lum_size;
	}

	list_for_each_entry(comp, &layout->llot_comp_list, llc_list) {
		struct lov_user_md *blob;
		size_t blob_size;
		uint32_t magic;
		int i, obj_count = 0;
		struct lov_user_ost_data *lmm_objects;
		uint64_t pattern = comp->llc_pattern;

		if ((pattern & LLAPI_LAYOUT_SPECIFIC) != 0) {
			if (comp->llc_objects_count <
			    comp->llc_stripe_count) {
				errno = EINVAL;
				goto error;
			}
			magic = LOV_USER_MAGIC_SPECIFIC;
			obj_count = comp->llc_stripe_count;
			pattern &= ~LLAPI_LAYOUT_SPECIFIC;
		} else if (strlen(comp->llc_pool_name) != 0) {
			magic = LOV_USER_MAGIC_V3;
		} else {
			magic = LOV_USER_MAGIC_V1;
		}
		/* All stripes must be specified when the pattern contains
		 * LLAPI_LAYOUT_SPECIFIC */
		for (i = 0; i < obj_count; i++) {
			if (comp->llc_objects[i].l_ost_idx ==
			    LLAPI_LAYOUT_IDX_MAX) {
				errno = EINVAL;
				goto error;
			}
		}

		blob_size = lov_user_md_size(obj_count, magic);
		blob = realloc(lum, lum_size + blob_size);
		if (blob == NULL) {
			errno = ENOMEM;
			goto error;
		} else {
			lum = blob;
			comp_v1 = (struct lov_comp_md_v1 *)lum;
			blob = (struct lov_user_md *)((char *)lum + lum_size);
			lum_size += blob_size;
		}

		blob->lmm_magic = magic;
		blob->lmm_pattern = llapi_pattern_to_lov(pattern);
		if (blob->lmm_pattern == EINVAL) {
			errno = EINVAL;
			goto error;
		}

		if (comp->llc_stripe_size == LLAPI_LAYOUT_DEFAULT)
			blob->lmm_stripe_size = 0;
		else
			blob->lmm_stripe_size = comp->llc_stripe_size;

		if (comp->llc_stripe_count == LLAPI_LAYOUT_DEFAULT)
			blob->lmm_stripe_count = 0;
		else if (comp->llc_stripe_count >= LLAPI_LAYOUT_WIDE_MIN &&
			 comp->llc_stripe_count <= LLAPI_LAYOUT_WIDE_MAX) {
			blob->lmm_stripe_count = LOV_ALL_STRIPES -
				(comp->llc_stripe_count -
				 LLAPI_LAYOUT_WIDE_MIN);
		}
		else
			blob->lmm_stripe_count = comp->llc_stripe_count;

		if (comp->llc_stripe_offset == LLAPI_LAYOUT_DEFAULT)
			blob->lmm_stripe_offset = -1;
		else
			blob->lmm_stripe_offset = comp->llc_stripe_offset;

		if (magic == LOV_USER_MAGIC_V3 ||
		    magic == LOV_USER_MAGIC_SPECIFIC) {
			struct lov_user_md_v3 *lumv3 =
				(struct lov_user_md_v3 *)blob;

			if (comp->llc_pool_name[0] != '\0') {
				snprintf(lumv3->lmm_pool_name,
					 sizeof(lumv3->lmm_pool_name), "%s",
					 comp->llc_pool_name);
			} else {
				memset(lumv3->lmm_pool_name, 0,
				       sizeof(lumv3->lmm_pool_name));
			}
			lmm_objects = lumv3->lmm_objects;
		} else {
			lmm_objects = blob->lmm_objects;
		}

		for (i = 0; i < obj_count; i++)
			lmm_objects[i].l_ost_idx =
				comp->llc_objects[i].l_ost_idx;

		if (layout->llot_is_composite) {
			ent = &comp_v1->lcm_entries[ent_idx];
			ent->lcme_id = comp->llc_id;
			ent->lcme_flags = comp->llc_flags;
			ent->lcme_time_and_id =
				lcme_timestamp_and_id_pack(comp->llc_timestamp,
					     comp->llc_mirror_link_id);
			ent->lcme_dstripe_count = comp->llc_dstripe_count;
			ent->lcme_cstripe_count = comp->llc_cstripe_count;
			ent->lcme_extent.e_start = comp->llc_extent.e_start;
			ent->lcme_extent.e_end = comp->llc_extent.e_end;
			ent->lcme_size = blob_size;
			ent->lcme_offset = offset;
			offset += blob_size;
			comp_v1->lcm_size += blob_size;
			ent_idx++;
		} else {
			break;
		}
	}

	return lum;
error:
	free(lum);
	return NULL;
}

/**
 * get_parent_dir() - Get the parent directory of a path.
 * @path: path to get parent of
 * @buf: buffer in which to store parent path [out]
 * @size: size in bytes of buffer @buf
 */
static void get_parent_dir(const char *path, char *buf, size_t size)
{
	char *p;

	strncpy(buf, path, size - 1);
	p = strrchr(buf, '/');

	if (p != NULL) {
		*p = '\0';
	} else if (size >= 2) {
		strncpy(buf, ".", 2);
		buf[size - 1] = '\0';
	}
}

/**
 * inherit_sys_attributes() - Substitute unspecified attribute values in layout
 * @layout: layout to inherit values from
 * @path: file path of the filesystem
 *
 * Substitute unspecified attribute values in @layout with values
 * from fs global settings. (lov.stripesize, lov.stripecount,
 * lov.stripeoffset)
 */
static void inherit_sys_attributes(struct llapi_layout *layout,
				   const char *path)
{
	struct llapi_layout_comp *comp;
	unsigned int ssize, scount, soffset;
	int rc;

	rc = sattr_cache_get_defaults(NULL, path, &scount, &ssize, &soffset);
	if (rc)
		return;

	list_for_each_entry(comp, &layout->llot_comp_list, llc_list) {
		if (comp->llc_pattern == LLAPI_LAYOUT_DEFAULT)
			comp->llc_pattern = LLAPI_LAYOUT_RAID0;
		if (comp->llc_stripe_size == LLAPI_LAYOUT_DEFAULT)
			comp->llc_stripe_size = ssize;
		if (comp->llc_stripe_count == LLAPI_LAYOUT_DEFAULT)
			comp->llc_stripe_count = scount;
		if (comp->llc_stripe_offset == LLAPI_LAYOUT_DEFAULT)
			comp->llc_stripe_offset = soffset;
	}
}

/**
 * __llapi_layout_cur_comp() - Get the current component of @layout.
 * @layout: layout to get current component
 *
 * Return valid llapi_layout_comp pointer on success or NULL on error
 */
static struct llapi_layout_comp *
__llapi_layout_cur_comp(const struct llapi_layout *layout)
{
	struct llapi_layout_comp *comp;

	if (layout == NULL || layout->llot_magic != LLAPI_LAYOUT_MAGIC) {
		errno = EINVAL;
		return NULL;
	}
	if (layout->llot_cur_comp == NULL) {
		errno = EINVAL;
		return NULL;
	}
	/* Verify data consistency */
	list_for_each_entry(comp, &layout->llot_comp_list, llc_list)
		if (comp == layout->llot_cur_comp)
			return comp;
	errno = EFAULT;
	return NULL;
}

/**
 * is_any_specified() - Test if any attributes of @layout are specified.
 * @layout: the layout to check
 *
 * Return:
 * * %true any attributes are specified
 * * %false all attributes are unspecified
 */
static bool is_any_specified(const struct llapi_layout *layout)
{
	struct llapi_layout_comp *comp;

	comp = __llapi_layout_cur_comp(layout);
	if (comp == NULL)
		return false;

	if (layout->llot_is_composite || layout->llot_mirror_count != 1)
		return true;

	return comp->llc_pattern != LLAPI_LAYOUT_DEFAULT ||
	       comp->llc_stripe_size != LLAPI_LAYOUT_DEFAULT ||
	       comp->llc_stripe_count != LLAPI_LAYOUT_DEFAULT ||
	       comp->llc_stripe_offset != LLAPI_LAYOUT_DEFAULT ||
	       strlen(comp->llc_pool_name);
}

/**
 * llapi_layout_get_by_fd() - Get the striping layout for the file referenced
 * by file descriptor @fd.
 * @fd: open file descriptor
 * @flags: open file descriptor
 *
 * If the filesystem does not support the "lustre." xattr namespace, the
 * file must be on a non-Lustre filesystem, so set errno to ENOTTY per
 * convention.  If the file has no "lustre.lov" data, the file will
 * inherit default values, so return a default layout.
 *
 * If the kernel gives us back less than the expected amount of data,
 * we fail with errno set to EINTR.
 *
 * Return valid llapi_layout pointer on success or NULL if an error occurs
 */
struct llapi_layout *llapi_layout_get_by_fd(int fd,
					    enum llapi_layout_get_flags flags)
{
	size_t lum_len;
	struct lov_user_md *lum;
	struct llapi_layout *layout = NULL;
	ssize_t bytes_read;
	struct stat st;

	lum_len = XATTR_SIZE_MAX;
	lum = malloc(lum_len);
	if (lum == NULL)
		return NULL;

	bytes_read = fgetxattr(fd, XATTR_LUSTRE_LOV, lum, lum_len);
	if (bytes_read < 0) {
		if (errno == EOPNOTSUPP)
			errno = ENOTTY;
		else if (errno == ENODATA)
			layout = llapi_layout_alloc();
		goto out;
	}

	/* Directories may have a positive non-zero lum->lmm_stripe_count
	 * yet have an empty lum->lmm_objects array. For non-directories the
	 * amount of data returned from the kernel must be consistent
	 * with the stripe count. */
	if (fstat(fd, &st) < 0)
		goto out;

	layout = llapi_layout_get_by_xattr(lum, bytes_read,
			S_ISDIR(st.st_mode) ? 0 : LLAPI_LAYOUT_GET_CHECK);
out:
	free(lum);
	return layout;
}

/**
 * llapi_layout_set_by_fd() - Set @layout on the file descriptor @fd
 * @fd: open file descriptor
 * @layout: layout to write on the file
 *
 * Set @layout on the file descriptor @fd and write the layout on the
 * file referenced by the file descriptor
 *
 * Return %0 on success or -1 on error and set errno
 */
int llapi_layout_set_by_fd(int fd, struct llapi_layout *layout)
{
	struct lov_user_md *lum;
	int rc;
	int tmp_errno;

	if (layout == NULL) {
		errno = EINVAL;
		return -1;
	}
	rc = llapi_layout_v2_sanity(layout, false, false, NULL);
	if (rc) {
		errno = EINVAL;
		return -1;
	}
	lum = llapi_layout_to_lum(layout);
	if (!lum) {
		errno = EINVAL;
		return -1;
	}
	rc = llapi_layout_set_by_xattr(fd, lum);
	if (rc < 0) {
		tmp_errno = errno;
		free(lum);
		errno = tmp_errno;
		return rc;
	}

	free(lum);
	return 0;
}

/**
 * llapi_layout_expected() - Get expected striping layout for a file at @path.
 * @path: Path to get striping info
 *
 * Substitute expected inherited attribute values for unspecified
 * attributes.  Unspecified attributes may belong to directories and
 * never-written-to files, and indicate that default values will be
 * assigned when files are created or first written to.  A default value
 * is inherited from the parent directory if the attribute is specified
 * there, otherwise it is inherited from the filesystem root.
 * Unspecified attributes normally have the value LLAPI_LAYOUT_DEFAULT.
 *
 * The complete @path need not refer to an existing file or directory,
 * but some leading portion of it must reside within a lustre filesystem.
 * A use case for this interface would be to obtain the literal striping
 * values that would be assigned to a new file in a given directory.
 *
 * Return valid llapi_layout pointer on success or NULL if an error occurs
 */
static struct llapi_layout *llapi_layout_expected(const char *path)
{
	struct llapi_layout	*path_layout = NULL;
	char			donor_path[PATH_MAX];
	struct stat st;
	int fd;
	int rc;

	fd = open(path, O_RDONLY);
	if (fd < 0 && errno != ENOENT)
		return NULL;

	if (fd >= 0) {
		int tmp;

		path_layout = llapi_layout_get_by_fd(fd, 0);
		tmp = errno;
		close(fd);
		errno = tmp;
	}

	if (path_layout == NULL) {
		if (errno != ENODATA && errno != ENOENT)
			return NULL;

		path_layout = llapi_layout_alloc();
		if (path_layout == NULL)
			return NULL;
	}

	if (is_any_specified(path_layout)) {
		inherit_sys_attributes(path_layout, path);
		return path_layout;
	}

	llapi_layout_free(path_layout);

	rc = stat(path, &st);
	if (rc < 0 && errno != ENOENT)
		return NULL;

	/* If path is a not a directory or doesn't exist, inherit layout
	 * from parent directory. */
	if ((rc == 0 && !S_ISDIR(st.st_mode)) ||
	    (rc < 0 && errno == ENOENT)) {
		get_parent_dir(path, donor_path, sizeof(donor_path));
		path_layout = llapi_layout_get_by_path(donor_path, 0);
		if (path_layout != NULL) {
			if (is_any_specified(path_layout)) {
				inherit_sys_attributes(path_layout, donor_path);
				return path_layout;
			}
			llapi_layout_free(path_layout);
		}
	}

	/* Inherit layout from the filesystem root. */
	rc = llapi_search_mounts(path, 0, donor_path, NULL);
	if (rc < 0)
		return NULL;
	path_layout = llapi_layout_get_by_path(donor_path, 0);
	if (path_layout == NULL)
		return NULL;

	inherit_sys_attributes(path_layout, donor_path);
	return path_layout;
}

/**
 * llapi_layout_get_by_path() - Get the striping layout for the file at @path
 * @path: path for which to get the layout
 * @flags: flags to control how layout is retrieved
 *
 * If @flags contains LLAPI_LAYOUT_GET_EXPECTED, substitute expected inherited
 * attribute values for unspecified attributes. See llapi_layout_expected().
 *
 * Return valid llapi_layout pointer on success or NULL on error
 */
struct llapi_layout *llapi_layout_get_by_path(const char *path,
					      enum llapi_layout_get_flags flags)
{
	struct llapi_layout *layout = NULL;
	bool failed = false;
	int open_flags;
	int fd;
	int tmp;

	if (flags & LLAPI_LAYOUT_GET_EXPECTED)
		return llapi_layout_expected(path);

	/* Always get layout in O_DIRECT */
	/* Allow fetching layout even without the key on encrypted files */
	open_flags = O_RDONLY | O_DIRECT | O_CIPHERTEXT;
do_open:
	fd = open(path, open_flags);
	if (fd < 0) {
		if (errno != EINVAL || failed)
			return layout;
		/* EINVAL is because a directory cannot be opened in O_DIRECT */
		open_flags = O_RDONLY | O_CIPHERTEXT;
		failed = true;
		goto do_open;
	}

	layout = llapi_layout_get_by_fd(fd, flags);
	tmp = errno;
	close(fd);
	errno = tmp;

	return layout;
}

/**
 * llapi_layout_get_by_fid() - Get the layout for the file with @fid in
 * filesystem @lustre_dir.
 * @lustre_dir: path within Lustre filesystem containing @fid
 * @fid: Lustre identifier of file to get layout for
 * @flags: open file descriptor
 *
 * Return valid llapi_layout pointer on success or NULL if an error occurs
 */
struct llapi_layout *llapi_layout_get_by_fid(const char *lustre_dir,
					     const struct lu_fid *fid,
					     enum llapi_layout_get_flags flags)
{
	int fd;
	int tmp;
	int saved_msg_level = llapi_msg_get_level();
	struct llapi_layout *layout = NULL;

	/* Prevent llapi internal routines from writing to console
	 * while executing this function, then restore previous message
	 * level. */
	llapi_msg_set_level(LLAPI_MSG_OFF);
	fd = llapi_open_by_fid(lustre_dir, fid, O_RDONLY);
	llapi_msg_set_level(saved_msg_level);

	if (fd < 0)
		return NULL;

	layout = llapi_layout_get_by_fd(fd, flags);
	tmp = errno;
	close(fd);
	errno = tmp;

	return layout;
}

/**
 * llapi_layout_stripe_count_get() - Get the stripe count of @layout.
 * @layout: layout to get stripe count from
 * @count: integer to store stripe count in [out]
 *
 * Return
 * * %0 on success
 * * %negative if arguments are invalid
 */
int llapi_layout_stripe_count_get(const struct llapi_layout *layout,
				  uint64_t *count)
{
	struct llapi_layout_comp *comp;

	comp = __llapi_layout_cur_comp(layout);
	if (comp == NULL)
		return -1;

	if (count == NULL) {
		errno = EINVAL;
		return -1;
	}

	if (comp->llc_pattern == LLAPI_LAYOUT_FOREIGN)
		*count = 0;
	else
		*count = comp->llc_stripe_count;

	return 0;
}

/*
 * The llapi_layout API functions have these extra validity checks since
 * they use intuitively named macros to denote special behavior, whereas
 * the old API uses 0 and -1.
 */

bool llapi_layout_stripe_count_is_valid(int64_t stripe_count)
{
	return stripe_count == LLAPI_LAYOUT_DEFAULT ||
		(stripe_count >= LLAPI_LAYOUT_WIDE_MIN &&
		 stripe_count <= LLAPI_LAYOUT_WIDE_MAX) ||
		(stripe_count > 0 &&
		 llapi_stripe_count_is_valid(stripe_count));
}

static bool llapi_layout_extension_size_is_valid(uint64_t ext_size)
{
	return (ext_size != 0 &&
		llapi_stripe_size_is_aligned(ext_size) &&
		!llapi_stripe_size_is_too_big(ext_size));
}

static bool llapi_layout_stripe_size_is_valid(uint64_t stripe_size)
{
	return stripe_size == LLAPI_LAYOUT_DEFAULT ||
		(stripe_size != 0 &&
		 llapi_stripe_size_is_aligned(stripe_size) &&
		 !llapi_stripe_size_is_too_big(stripe_size));
}

static bool llapi_layout_stripe_index_is_valid(int64_t stripe_index)
{
	return stripe_index == LLAPI_LAYOUT_DEFAULT ||
		(stripe_index >= 0 &&
		llapi_stripe_index_is_valid(stripe_index));
}

/**
 * llapi_layout_stripe_count_set() - Set the stripe count of @layout.
 * @layout: layout to set stripe count in
 * @count: value to be set
 *
 * Return:
 * * %0 on success
 * * %negative if arguments are invalid
 */
int llapi_layout_stripe_count_set(struct llapi_layout *layout,
				  uint64_t count)
{
	struct llapi_layout_comp *comp;

	comp = __llapi_layout_cur_comp(layout);
	if (comp == NULL)
		return -1;

	if (comp->llc_flags & LCME_FL_PARITY) {
		errno = EINVAL;
		return -1;
	}

	if (!llapi_layout_stripe_count_is_valid(count)) {
		errno = EINVAL;
		return -1;
	}
	comp->llc_stripe_count = count;

	return 0;
}

/**
 * layout_stripe_size_get() - Get the stripe/extension size of @layout.
 * @layout: layout to get stripe size from
 * @size: integer to store stripe size in [out]
 * @extension: flag if extenion size is requested
 *
 * Return:
 * * %0 on success
 * * %negative if arguments are invalid
 */
static int layout_stripe_size_get(const struct llapi_layout *layout,
				  uint64_t *size, bool extension)
{
	struct llapi_layout_comp *comp;
	int comp_ext;

	comp = __llapi_layout_cur_comp(layout);
	if (comp == NULL)
		return -1;

	if (size == NULL) {
		errno = EINVAL;
		return -1;
	}

	/* FIXME: return a component rather than FOREIGN/HSM component. */
	if (comp->llc_pattern == LLAPI_LAYOUT_FOREIGN) {
		errno = EINVAL;
		return -1;
	}

	comp_ext = comp->llc_flags & LCME_FL_EXTENSION;
	if ((comp_ext && !extension) || (!comp_ext && extension)) {
		errno = EINVAL;
		return -1;
	}

	*size = comp->llc_stripe_size;
	if (comp->llc_flags & LCME_FL_EXTENSION)
		*size *= SEL_UNIT_SIZE;

	return 0;
}

int llapi_layout_stripe_size_get(const struct llapi_layout *layout,
				 uint64_t *size)
{
	return layout_stripe_size_get(layout, size, false);
}

int llapi_layout_extension_size_get(const struct llapi_layout *layout,
				    uint64_t *size)
{
	return layout_stripe_size_get(layout, size, true);
}

/**
 * layout_stripe_size_set() - Set the stripe/extension size of @layout.
 * @layout: layout to set stripe size in
 * @size: value to be set
 * @extension: flag if extenion size is passed
 *
 * Return:
 * * %0 on success
 * * %negative if arguments are invalid
 */
static int layout_stripe_size_set(struct llapi_layout *layout,
				  uint64_t size, bool extension)
{
	struct llapi_layout_comp *comp;
	int comp_ext;

	comp = __llapi_layout_cur_comp(layout);
	if (comp == NULL)
		return -1;

	if (comp->llc_pattern == LLAPI_LAYOUT_FOREIGN ||
	    comp->llc_flags & LCME_FL_PARITY) {
		errno = EINVAL;
		return -1;
	}

	comp_ext = comp->llc_flags & LCME_FL_EXTENSION;
	if ((comp_ext && !extension) || (!comp_ext && extension)) {
		errno = EINVAL;
		return -1;
	}

	if (comp_ext)
		size /= SEL_UNIT_SIZE;

	if ((comp_ext && !llapi_layout_extension_size_is_valid(size)) ||
	    (!comp_ext && !llapi_layout_stripe_size_is_valid(size))) {
		errno = EINVAL;
		return -1;
	}

	comp->llc_stripe_size = size;
	return 0;
}

int llapi_layout_stripe_size_set(struct llapi_layout *layout,
				 uint64_t size)
{
	return layout_stripe_size_set(layout, size, false);
}

int llapi_layout_extension_size_set(struct llapi_layout *layout,
				    uint64_t size)
{
	return layout_stripe_size_set(layout, size, true);
}

/**
 * llapi_layout_pattern_get() - Get the RAID pattern of @layout.
 * @layout: layout to get pattern from
 * @pattern: integer to store pattern in [out]
 *
 * Return:
 * * %0 on success
 * * %negative if arguments are invalid
 */
int llapi_layout_pattern_get(const struct llapi_layout *layout,
			     uint64_t *pattern)
{
	struct llapi_layout_comp *comp;

	comp = __llapi_layout_cur_comp(layout);
	if (comp == NULL)
		return -1;

	if (pattern == NULL) {
		errno = EINVAL;
		return -1;
	}

	*pattern = comp->llc_pattern;

	return 0;
}

/**
 * llapi_layout_pattern_set() - Set the pattern of @layout.
 * @layout: layout to set pattern in
 * @pattern: value to be set
 *
 * Return:
 * * %0 on success
 * * %negative if arguments are invalid or RAID pattern is unsupported
 */
int llapi_layout_pattern_set(struct llapi_layout *layout, uint64_t pattern)
{
	struct llapi_layout_comp *comp;

	comp = __llapi_layout_cur_comp(layout);
	if (comp == NULL)
		return -1;

	if (pattern != LLAPI_LAYOUT_DEFAULT &&
	    pattern != LLAPI_LAYOUT_RAID0 && pattern != LLAPI_LAYOUT_MDT
	    && pattern != LLAPI_LAYOUT_OVERSTRIPING &&
	    pattern != LLAPI_LAYOUT_FOREIGN) {
		errno = EOPNOTSUPP;
		return -1;
	}

	comp->llc_pattern = pattern |
			    (comp->llc_pattern & LLAPI_LAYOUT_SPECIFIC);

	return 0;
}

static inline int stripe_number_roundup(int stripe_number)
{
	unsigned int round_up = (stripe_number + 8) & ~7;
	return round_up > LOV_MAX_STRIPE_COUNT ?
		LOV_MAX_STRIPE_COUNT : round_up;
}

/**
 * llapi_layout_ost_index_set() - Set the OST index of stripe number
 * @stripe_number to @ost_index.
 * @layout: layout to set OST index in
 * @stripe_number: stripe number to set index for
 * @ost_index: the index to set
 *
 * If only the starting stripe's OST index is specified, then this can use
 * the normal LOV_MAGIC_{V1,V3} layout type.  If multiple OST indices are
 * given, then allocate an array to hold the list of indices and ensure that
 * the LOV_USER_MAGIC_SPECIFIC layout is used when creating the file.
 *
 * Return:
 * * %0 on success
 * * %negative if arguments are invalid or an unsupported stripe number was
 *   specified, error returned in errno
 */
int llapi_layout_ost_index_set(struct llapi_layout *layout, int stripe_number,
			       uint64_t ost_index)
{
	struct llapi_layout_comp *comp;

	comp = __llapi_layout_cur_comp(layout);
	if (comp == NULL)
		return -1;

	if (comp->llc_pattern == LLAPI_LAYOUT_FOREIGN) {
		errno = EINVAL;
		return -1;
	}

	if (!llapi_layout_stripe_index_is_valid(ost_index)) {
		errno = EINVAL;
		return -1;
	}

	if (stripe_number == 0 && ost_index == LLAPI_LAYOUT_DEFAULT) {
		comp->llc_stripe_offset = ost_index;
		comp->llc_pattern &= ~LLAPI_LAYOUT_SPECIFIC;
		__llapi_comp_objects_realloc(comp, 0);
	} else if (stripe_number >= 0 &&
		   stripe_number < LOV_MAX_STRIPE_COUNT) {
		if (ost_index >= LLAPI_LAYOUT_IDX_MAX) {
			errno = EINVAL;
			return -1;
		}

		/* Preallocate a few more stripes to avoid realloc() overhead.*/
		if (__llapi_comp_objects_realloc(comp,
				stripe_number_roundup(stripe_number)) < 0)
			return -1;

		comp->llc_objects[stripe_number].l_ost_idx = ost_index;

		if (stripe_number == 0)
			comp->llc_stripe_offset = ost_index;
		else
			comp->llc_pattern |= LLAPI_LAYOUT_SPECIFIC;

		if (comp->llc_stripe_count == LLAPI_LAYOUT_DEFAULT ||
		    comp->llc_stripe_count <= stripe_number)
			comp->llc_stripe_count = stripe_number + 1;
	} else {
		errno = EINVAL;
		return -1;
	}

	return 0;
}

static int reset_index_cb(struct llapi_layout *layout, void *cbdata)
{
	int *save_errno = (int *)cbdata;
	int rc;

	rc = llapi_layout_ost_index_set(layout, 0, LLAPI_LAYOUT_DEFAULT);

	/* save the first error returned, but try to reset all components */
	if (rc && !*save_errno)
		*save_errno = errno;

	return LLAPI_LAYOUT_ITER_CONT;
}

/**
 * llapi_layout_ost_index_reset() - Reset the OST index on all components in
 * @layout to LLAPI_LAYOUT_DEFAULT.
 * @layout: layout to reset OST index in
 *
 * This is useful when reusing a file layout that was copied from an existing
 * file and to be used for a new file (e.g. when mirroring or migrating or
 * copying a file), so the objects are allocated on different OSTs.
 *
 * Return:
 * * %0 Success.
 * * %negative errno Error with errno set to non-zero value.
 */
int llapi_layout_ost_index_reset(struct llapi_layout *layout)
{
	int save_errno = 0;
	int rc;

	rc = llapi_layout_comp_iterate(layout, reset_index_cb, &save_errno);

	if (save_errno)
		errno = save_errno;
	return save_errno ? -save_errno : (rc < 0 ? -errno : 0);
}

/**
 * llapi_layout_ost_index_get() - Get OST index associated with @stripe_number.
 * @layout: layout to get index from
 * @stripe_number: stripe number to get index for
 * @index: integer to store index in [out]
 *
 * Stripes are indexed starting from zero.
 *
 * Return:
 * * %0 on success
 * * %negative if arguments are invalid
 */
int llapi_layout_ost_index_get(const struct llapi_layout *layout,
			       uint64_t stripe_number, uint64_t *index)
{
	struct llapi_layout_comp *comp;

	comp = __llapi_layout_cur_comp(layout);
	if (comp == NULL)
		return -1;

	if (comp->llc_pattern == LLAPI_LAYOUT_FOREIGN) {
		errno = EINVAL;
		return -1;
	}

	if (index == NULL) {
		errno = EINVAL;
		return -1;
	}

	if (stripe_number >= comp->llc_stripe_count ||
	    stripe_number >= comp->llc_objects_count) {
		errno = EINVAL;
		return -1;
	}

	if (comp->llc_stripe_offset == LLAPI_LAYOUT_DEFAULT)
		*index = LLAPI_LAYOUT_DEFAULT;
	else
		*index = comp->llc_objects[stripe_number].l_ost_idx;

	return 0;
}

/**
 * llapi_layout_pool_name_get() - Get the pool name of layout @layout.
 * @layout: layout to get pool name from
 * @dest: buffer to store pool name in [out]
 * @n: size in bytes of buffer @dest
 *
 * Return:
 * * %0 on success
 * * %negative if arguments are invalid
 */
int llapi_layout_pool_name_get(const struct llapi_layout *layout, char *dest,
			       size_t n)
{
	struct llapi_layout_comp *comp;

	comp = __llapi_layout_cur_comp(layout);
	if (comp == NULL)
		return -1;

	if (dest == NULL) {
		errno = EINVAL;
		return -1;
	}

	if (comp->llc_pattern == LLAPI_LAYOUT_FOREIGN) {
		errno = EINVAL;
		return -1;
	}

	snprintf(dest, n, "%s", comp->llc_pool_name);

	return 0;
}

/**
 * llapi_layout_pool_name_set() - Set the name of the pool of layout @layout.
 * @layout: layout to set pool name in
 * @pool_name: pool name to set
 *
 * Return:
 * * %0 on success
 * * %negative if arguments are invalid or pool name is too long
 */
int llapi_layout_pool_name_set(struct llapi_layout *layout,
			       const char *pool_name)
{
	struct llapi_layout_comp *comp;

	comp = __llapi_layout_cur_comp(layout);
	if (comp == NULL)
		return -1;

	if (!llapi_pool_name_is_valid(&pool_name)) {
		errno = EINVAL;
		return -1;
	}

	if (comp->llc_pattern == LLAPI_LAYOUT_FOREIGN) {
		errno = EINVAL;
		return -1;
	}

	snprintf(comp->llc_pool_name, sizeof(comp->llc_pool_name), "%s",
		 pool_name);
	return 0;
}

/**
 * llapi_layout_file_open() - Open and possibly create file with given @layout.
 * @path: name of the file to open
 * @open_flags: open() flags
 * @mode: permissions to create file, filtered by umask
 * @layout: layout to create new file with
 *
 * If @layout is NULL this function acts as a simple wrapper for
 * open(). By convention, ENOTTY is returned in errno if @path
 * refers to a non-Lustre file.
 *
 * Return:
 * * %non-negative file descriptor on successful open
 * * %negative if an error occurred
 */
int llapi_layout_file_open(const char *path, int open_flags, mode_t mode,
			   const struct llapi_layout *layout)
{
	char fsname[MAX_OBD_NAME + 1] = { 0 };
	struct llapi_layout_comp *comp;
	int fd;
	int rc;

	comp = __llapi_layout_cur_comp(layout);

	if (path == NULL ||
	    (layout != NULL && layout->llot_magic != LLAPI_LAYOUT_MAGIC)) {
		errno = EINVAL;
		return -1;
	}

	if (layout) {
		/* Make sure we are on a Lustre file system */
		if (comp != NULL && comp->llc_pool_name[0] != '\0' &&
		    !lov_pool_is_ignored(comp->llc_pool_name)) {
			rc = llapi_search_fsname(path, fsname);
			if (rc) {
				errno = ENOTTY;
				return -1;
			}
		}
		rc = llapi_layout_v2_sanity((struct llapi_layout *)layout,
					    false,
					    !!(layout->llot_mirror_count > 1),
					    fsname);
		if (rc) {
			llapi_layout_sanity_perror(rc);
			return -1;
		}
	}

	/* Object creation must be postponed until after layout
	 * attributes have been applied.
	 */
	if (open_flags & O_CREAT)
		open_flags |= O_LOV_DELAY_CREATE;

	fd = open(path, open_flags, mode);

	if (layout == NULL || fd < 0)
		return fd;

	rc = llapi_layout_set_by_fd(fd, (struct llapi_layout *)layout);
	if (rc < 0) {
		struct lov_user_md *lum;
		size_t lum_size;
		int tmp = errno;

		lum = llapi_layout_to_lum(layout);

		lum_size = get_lum_size(lum);

		/* caller usually prints error, but doesn't know xattr size */
		if (errno == ENOSPC)
			llapi_error(LLAPI_MSG_ERROR, errno,
				    "error setting %zd-byte layout on '%s'\n",
				    lum_size, path);
		free(lum);
		close(fd);
		errno = tmp;
		fd = -1;
	}

	errno = errno == EOPNOTSUPP ? ENOTTY : errno;

	return fd;
}

/**
 * llapi_layout_file_create() - Create a file with a given @layout.
 * @path: name of the file to open
 * @open_flags: open() flags
 * @mode: permissions to create new file with
 * @layout: layout to create new file with
 *
 * Force O_CREAT and O_EXCL flags on so caller is assured that file was
 * created with the given @layout on successful function return.
 *
 * Return:
 * * %non-negative file descriptor on successful open
 * * %negative if an error occurred
 */
int llapi_layout_file_create(const char *path, int open_flags, int mode,
			     const struct llapi_layout *layout)
{
	return llapi_layout_file_open(path, open_flags|O_CREAT|O_EXCL, mode,
				      layout);
}

int llapi_layout_flags_get(struct llapi_layout *layout, uint32_t *flags)
{
	if (layout->llot_magic != LLAPI_LAYOUT_MAGIC) {
		errno = EINVAL;
		return -1;
	}

	*flags = layout->llot_flags;
	return 0;
}

/* Set flags to the header of a component layout */
int llapi_layout_flags_set(struct llapi_layout *layout, uint32_t flags)
{
	if (layout->llot_magic != LLAPI_LAYOUT_MAGIC) {
		errno = EINVAL;
		return -1;
	}

	layout->llot_flags = flags;
	return 0;
}

const char *llapi_layout_flags_string(uint32_t flags)
{
	switch (flags & LCM_FL_FLR_MASK) {
	case LCM_FL_RDONLY:
		return "ro";
	case LCM_FL_WRITE_PENDING:
		return "wp";
	case LCM_FL_SYNC_PENDING:
		return "sp";
	case LCM_FL_RDONLY | LCM_FL_PCC_RDONLY:
		return "ro,pccro";
	case LCM_FL_WRITE_PENDING | LCM_FL_PCC_RDONLY:
		return "wp,pccro";
	case LCM_FL_SYNC_PENDING | LCM_FL_PCC_RDONLY:
		return "sp,pccro";
	}

	return "0";
}

__u16 llapi_layout_string_flags(char *string)
{
	if (strncmp(string, "ro", strlen(string)) == 0)
		return LCM_FL_RDONLY;
	if (strncmp(string, "wp", strlen(string)) == 0)
		return LCM_FL_WRITE_PENDING;
	if (strncmp(string, "sp", strlen(string)) == 0)
		return LCM_FL_SYNC_PENDING;

	return 0;
}

static struct {
	enum lov_pattern	 llpn_pattern;
	const char		*llpn_pattern_name;
} lov_pattern_names[] = {
	{ LOV_PATTERN_BAD,		"bad" },
	{ LOV_PATTERN_RAID0,		"raid0" },
	{ LOV_PATTERN_RAID1,		"raid1" },
	{ LOV_PATTERN_PARITY,		"parity" },
	{ LOV_PATTERN_MDT,		"mdt" },
	{ LOV_PATTERN_OVERSTRIPING,	"overstriped" },  /* getstripe */
	{ LOV_PATTERN_OVERSTRIPING,	"overstriping" }, /* setstripe compat */
	{ LOV_PATTERN_FOREIGN,		"foreign" },
	{ LOV_PATTERN_COMPRESS,		"compress" },
	{ LOV_PATTERN_F_HOLE,		"hole" },
	{ LOV_PATTERN_F_RELEASED,	"released" },
	{ LOV_PATTERN_DEFAULT,		"default" },
	{ 0, NULL }
};

int llapi_lov_string_pattern(const char *string, enum lov_pattern *pattern)
{
	const char *p;

	*pattern = 0;
	for (p = string; p != NULL; p = strchr(p, ',')) {
		int i;

		while (*p == ',')
			p++;
		if (*p == '\0')
			break;
		for (i = 0; lov_pattern_names[i].llpn_pattern != 0; i++) {
			int l;

			l = strlen(lov_pattern_names[i].llpn_pattern_name);
			if (strncmp(p, lov_pattern_names[i].llpn_pattern_name,
				    l))
				continue;
			*pattern |= lov_pattern_names[i].llpn_pattern;
			break;
		}
		if (lov_pattern_names[i].llpn_pattern == 0) {
			errno = EINVAL;
			return -errno;
		}
	}

	return 0;
}

char *llapi_lov_pattern_string(enum lov_pattern pattern, char *buf,
			       size_t buflen)
{
	char *p = buf;
	int l = 0;
	int i;

	p[0] = '\0';
	for (i = 0; lov_pattern_names[i].llpn_pattern && pattern; i++) {
		if ((lov_pattern_names[i].llpn_pattern & pattern) ==
		    lov_pattern_names[i].llpn_pattern) {
			l += snprintf(p, buflen - l, "%s%s", buf[0] ? "," : "",
				      lov_pattern_names[i].llpn_pattern_name);
			pattern &= ~lov_pattern_names[i].llpn_pattern;
			if (l >= buflen) {
				errno = EOVERFLOW;
				return NULL;
			}
			p = buf + l;
		}
	}

	if (pattern)
		snprintf(buf, buflen, "%sunknown_%x", buf[0] ? "," : "",
			pattern);

	return buf;
}

/**
 * llapi_layout_mirror_count_is_valid() - Check the validity of mirror count.
 * @count: Mirror count value to be checked.
 *
 * This function checks the validity of mirror count.
 *
 * Return: true on success or false on failure.
 */
static bool llapi_layout_mirror_count_is_valid(uint16_t count)
{
	return count <= LUSTRE_MIRROR_COUNT_MAX;
}

/**
 * llapi_layout_mirror_count_get() - Get mirror count from header of a layout.
 * @layout: Layout to get mirror count from.
 * @count:  Returned mirror count value.
 *
 * This function gets mirror count from the header of a layout.
 *
 * Return: 0 on success or %negative on failure.
 */
int llapi_layout_mirror_count_get(struct llapi_layout *layout,
				  uint16_t *count)
{
	if (layout->llot_magic != LLAPI_LAYOUT_MAGIC) {
		errno = EINVAL;
		return -1;
	}

	*count = layout->llot_mirror_count;
	return 0;
}

/**
 * llapi_layout_mirror_count_set() - Set mirror count to the header of a layout.
 * @layout: Layout to set mirror count in.
 * @count:  Mirror count value to be set.
 *
 * This function sets mirror count to the header of a layout.
 *
 * Return: 0 on success or %negative on failure.
 */
int llapi_layout_mirror_count_set(struct llapi_layout *layout,
				  uint16_t count)
{
	if (layout->llot_magic != LLAPI_LAYOUT_MAGIC) {
		errno = EINVAL;
		return -1;
	}

	if (!llapi_layout_mirror_count_is_valid(count)) {
		errno = EINVAL;
		return -1;
	}

	layout->llot_mirror_count = count;
	return 0;
}

/**
 * llapi_layout_mirror_count_sync() - Synchronize mirror count from components
 * @layout: layout to synchronize
 *
 * Iterates over all components and counts mirror boundaries (components with
 * extent start == 0). Updates llot_mirror_count and each component's
 * llc_mirror_id accordingly. Note, mirror IDs are decoupled from mirror count
 * and need to be tracked separately.
 *
 * Return:
 * * %0 on success
 * * %negative if error occurs
 */
int llapi_layout_mirror_count_sync(struct llapi_layout *layout)
{
	struct llapi_layout_comp *comp;
	uint16_t mirror_count = 0;
	uint32_t mirror_id = 0;

	if (!layout || layout->llot_magic != LLAPI_LAYOUT_MAGIC) {
		errno = EINVAL;
		return -1;
	}

	list_for_each_entry(comp, &layout->llot_comp_list, llc_list) {
		if (comp->llc_extent.e_start == 0) {
			if (!llapi_layout_mirror_count_is_valid(mirror_count +
								1)) {
				errno = EINVAL;
				return -1;
			}
			mirror_count++;
			mirror_id++;
		}
		/* Only count/assign for components without a mirror ID set */
		if (comp->llc_mirror_id == 0) {
			comp->llc_mirror_id = mirror_id;
		} else {
			/* Track existing mirror IDs */
			if (comp->llc_mirror_id > mirror_id)
				mirror_id = comp->llc_mirror_id;
		}
	}

	layout->llot_curr_mirror_id = mirror_id + 1;
	layout->llot_mirror_count = mirror_count;
	return 0;
}

/**
 * llapi_layout_comp_extent_get() - Fetch the start and end offset of the
 * current layout component.
 * @layout: the layout component
 * @start: extent start, inclusive [out]
 * @end: extent end, exclusive [out]
 *
 * Return:
 * * %0 on success
 * * %negative if error occurs
 */
int llapi_layout_comp_extent_get(const struct llapi_layout *layout,
				 uint64_t *start, uint64_t *end)
{
	struct llapi_layout_comp *comp;

	comp = __llapi_layout_cur_comp(layout);
	if (comp == NULL)
		return -1;

	if (start == NULL || end == NULL) {
		errno = EINVAL;
		return -1;
	}

	*start = comp->llc_extent.e_start;
	*end = comp->llc_extent.e_end;

	return 0;
}

/**
 * llapi_layout_comp_extent_set() - Set the layout extent of a layout.
 * @layout: the layout to be set
 * @start: extent start, inclusive
 * @end: extent end, exclusive
 *
 * Return:
 * * %0 on success
 * * %negative if error occurs
 */
int llapi_layout_comp_extent_set(struct llapi_layout *layout,
				 uint64_t start, uint64_t end)
{
	struct llapi_layout_comp *comp;

	comp = __llapi_layout_cur_comp(layout);
	if (comp == NULL)
		return -1;

	if (start > end) {
		errno = EINVAL;
		return -1;
	}

	comp->llc_extent.e_start = start;
	comp->llc_extent.e_end = end;
	layout->llot_is_composite = true;

	return 0;
}

/**
 * llapi_layout_comp_flags_get() - Gets the attribute flags of the current
 * component.
 * @layout: the layout component
 * @flags: stored the returned component flags [out]
 *
 * Return:
 * * %0 on success
 * * %negative if error occurs
 */
int llapi_layout_comp_flags_get(const struct llapi_layout *layout,
				uint32_t *flags)
{
	struct llapi_layout_comp *comp;

	comp = __llapi_layout_cur_comp(layout);
	if (comp == NULL)
		return -1;

	if (flags == NULL) {
		errno = EINVAL;
		return -1;
	}

	*flags = comp->llc_flags;

	return 0;
}

/**
 * llapi_layout_comp_flags_set() - Sets the specified flags of the current
 * component leaving other flags as-is.
 * @layout: the layout component
 * @flags: component flags to be set
 *
 * Return:
 * * %0 on success
 * * %negative if error occurs
 */
int llapi_layout_comp_flags_set(struct llapi_layout *layout, uint32_t flags)
{
	struct llapi_layout_comp *comp;

	/* LCME_FL_PARITY cannot be set with this function */
	if (flags & LCME_FL_PARITY) {
		errno = EINVAL;
		return -1;
	}

	comp = __llapi_layout_cur_comp(layout);
	if (comp == NULL)
		return -1;

	comp->llc_flags |= flags;

	return 0;
}

/**
 * llapi_layout_comp_flags_clear() - Clears the flags specified in the flags
 * leaving other flags as-is.
 * @layout:	the layout component
 * @flags:	component flags to be cleared
 *
 * Return:
 * * %0 on success
 * * %negative if error occurs
 */
int llapi_layout_comp_flags_clear(struct llapi_layout *layout,
				  uint32_t flags)
{
	struct llapi_layout_comp *comp;

	comp = __llapi_layout_cur_comp(layout);
	if (comp == NULL)
		return -1;

	comp->llc_flags &= ~flags;

	return 0;
}

/**
 * llapi_layout_comp_id_get() - Fetches the file-unique component ID of the
 * current layout component.
 * @layout: the layout component
 * @id: stored the returned component ID [out]
 *
 * Return:
 * * %0 on success
 * * %negative if error occurs
 */
int llapi_layout_comp_id_get(const struct llapi_layout *layout, uint32_t *id)
{
	struct llapi_layout_comp *comp;

	comp = __llapi_layout_cur_comp(layout);
	if (comp == NULL)
		return -1;

	if (id == NULL) {
		errno = EINVAL;
		return -1;
	}
	*id = comp->llc_id;

	return 0;
}

/**
 * llapi_layout_mirror_id_get() - Return mirror id of current layout component.
 * @layout: the layout component
 * @id: stored the returned mirror ID [out]
 *
 * Return:
 * * %0 on success
 * * %negative if error occurs
 */
int llapi_layout_mirror_id_get(const struct llapi_layout *layout, uint32_t *id)
{
	struct llapi_layout_comp *comp;

	comp = __llapi_layout_cur_comp(layout);
	if (!comp)
		return -1;

	if (!id) {
		errno = EINVAL;
		return -1;
	}

	*id = comp->llc_mirror_id;

	return 0;
}

/**
 * llapi_layout_comp_mirror_link_id_get() - Return mirror link ID of current
 * layout component.
 * @layout: the layout component
 * @id: stored the returned  mirror link ID [out]
 *
 * Return:
 * * %0 on success
 * * %negative if error occurs
 */
int llapi_layout_comp_mirror_link_id_get(const struct llapi_layout *layout,
					 uint16_t *id)
{
	struct llapi_layout_comp *comp;

	comp = __llapi_layout_cur_comp(layout);
	if (!comp)
		return -1;

	if (!id) {
		errno = EINVAL;
		return -1;
	}

	*id = comp->llc_mirror_link_id;

	return 0;
}

/**
 * llapi_layout_comp_add() - Adds a component to @layout
 * @layout: existing composite or plain layout
 *
 * Adds a component to @layout, the new component will be added to
 * the tail of components list and it'll inherit attributes of existing
 * ones. The @layout will change it's current component pointer to
 * the newly added component, and it'll be turned into a composite
 * layout if it was not before the adding.
 *
 * Return:
 * * %0 on success
 * * %negative if error occurs
 */
int llapi_layout_comp_add(struct llapi_layout *layout)
{
	return llapi_layout_comp_add_extent(layout, 0, 0);
}

int llapi_layout_comp_add_extent(struct llapi_layout *layout,
				 uint64_t start, uint64_t end)
{
	struct llapi_layout_comp *last, *comp, *new;
	bool save_composite;

	/* Validate input parameters */
	if (!layout) {
		errno = EINVAL;
		return -1;
	}

	/* extent start should be less than extent end */
	if (start != 0 && start >= end) {
		errno = EINVAL;
		return -1;
	}

	save_composite = layout->llot_is_composite;

	comp = __llapi_layout_cur_comp(layout);
	if (comp == NULL)
		return -1;

	new = __llapi_comp_alloc(0);
	if (new == NULL)
		return -1;

	last = list_last_entry(&layout->llot_comp_list, typeof(*last),
			       llc_list);
	list_add_tail(&new->llc_list, &layout->llot_comp_list);

	layout->llot_is_composite = true;
	layout->llot_cur_comp = new;

	/* If no extent was provided:
	 * We need to set a temporary non-zero value for "end" when we call
	 * comp_extent_set, so we use LUSTRE_EOF-1, which is > all allowed
	 * for the end of the previous component.  (If we're adding this
	 * component, the end of the previous component cannot be EOF.)
	 */
	if (start == 0 && end == 0) {
		start = last->llc_extent.e_end;
		end = LUSTRE_EOF - 1;
	}
	if (llapi_layout_comp_extent_set(layout, start, end)) {
		(void)llapi_layout_comp_del(layout);
		layout->llot_is_composite = save_composite;
		return -1;
	}

	return 0;
}

static int layout_ec_verify_stripes(__u64 stripe_count, __u8 k, __u8 p)
{
	struct ec_split_comp sc;

	/* Validate stripe counts */
	if (p == 0 || k == 0)
		return -EINVAL;

	/*
	 * The total number of parities we will need across all the raid
	 * sets can not exceed the number of stripes in the data comp
	 * it protects.
	 */
	ec_split_stripes(stripe_count, k, &sc);
	if (sc.esc_n0 * sc.esc_k0 + sc.esc_n1 * sc.esc_k1 != stripe_count ||
	    (sc.esc_n0 + sc.esc_n1) * p  > stripe_count)
		return -EINVAL;

	return 0;
}

/**
 * llapi_layout_comp_add_ec() - Adds a EC component to @layout
 * @layout:	existing composite or plain layout
 * @mirror_id:	mirror id of the data component to be protected by the EC
 *		component, a mirror id of 0 is invalid
 * @start:	start offset of the extent
 * @end:	end offset of the extent
 * @dstripe_count: number of data stripes
 * @cstripe_count: number of coding stripes
 *
 * Adds a EC component to @layout at the tail of components list and if
 * @comp_id is specified, the EC component will protect the data component for
 * the specified mirror id. A data component can only protected by one EC
 * component. The @layout will change it's current component pointer to the
 * newly added EC component, and it'll be turned into a composite layout if it
 * was not before the adding.
 *
 * Before (and after) adding the EC component the mirror count and IDs are
 * synced to ensure the EC component is added to the correct mirror.
 *
 * Return:
 * * %0		on success
 * * %negative	if error occurs
 */
int llapi_layout_comp_add_ec(struct llapi_layout *layout, uint32_t mirror_id,
			     uint64_t start, uint64_t end,
			     uint8_t dstripe_count, uint8_t cstripe_count)
{
	struct llapi_layout_comp *parity_comp, *comp;
	bool found = false;
	int rc;

	/* Validate input parameters */
	if (!layout) {
		errno = EINVAL;
		return -1;
	}

	/* Validate extent parameters */
	if (mirror_id == 0 || start >= end) {
		errno = EINVAL;
		return -1;
	}

	/* Validate EC stripe count limits */
	if (dstripe_count > LOV_EC_MAX_DATA_STRIPES) {
		errno = EINVAL;
		return -1;
	}

	if (cstripe_count > LOV_EC_MAX_CODING_STRIPES) {
		errno = EINVAL;
		return -1;
	}

	if (dstripe_count + cstripe_count > LOV_EC_MAX_TOTAL_STRIPES) {
		errno = EINVAL;
		return -1;
	}

	/* Sync mirror count and IDs so that the data component can be found */
	rc = llapi_layout_mirror_count_sync(layout);
	if (rc)
		return rc;

	/*
	 * Find the matching data component. A data component with the same
	 * extent must exist before adding an EC component.
	 */
	list_for_each_entry(comp, &layout->llot_comp_list, llc_list) {
		if (comp->llc_mirror_id != mirror_id)
			continue;
		/* component to protect should not be a parity component */
		if (comp->llc_flags & LCME_FL_PARITY) {
			errno = EINVAL;
			return -1;
		}

		if (comp->llc_extent.e_start == start &&
		    comp->llc_extent.e_end == end) {
			/* Data comp is already protected by a parity comp */
			if (comp->llc_mirror_link_id !=
			    LLAPI_MIRROR_LINK_NONE) {
				errno = EINVAL;
				return -1;
			}
			found = true;
			break;
		}
	}
	if (!found) {
		errno = ENOENT;
		return -1;
	}

	/*
	 * If the data component's stripe count is still LLAPI_LAYOUT_DEFAULT,
	 * set it to dstripe_count (the number of data stripes in the EC layout)
	 */
	if (comp->llc_stripe_count == LLAPI_LAYOUT_DEFAULT ||
	    comp->llc_stripe_count == LLAPI_LAYOUT_WIDE)
		comp->llc_stripe_count = dstripe_count;

	/* Parity components require non-zero cstripe and dstripe */
	if (layout_ec_verify_stripes(comp->llc_stripe_count,
				     dstripe_count, cstripe_count)) {
		errno = EINVAL;
		return -1;
	}

	rc = llapi_layout_comp_add_extent(layout, start, end);
	if (rc)
		return rc;

	/* Sync mirror count and IDs to generate the mirror ID for the new
	 * parity component, which is needed for the bi-directional link.
	 */
	rc = llapi_layout_mirror_count_sync(layout);
	if (rc) {
		(void)llapi_layout_comp_del(layout);
		return rc;
	}

	parity_comp = __llapi_layout_cur_comp(layout);
	if (!parity_comp) {
		(void)llapi_layout_comp_del(layout);
		return -1;
	}

	parity_comp->llc_flags |= LCME_FL_PARITY;
	parity_comp->llc_cstripe_count = cstripe_count;
	parity_comp->llc_dstripe_count = dstripe_count;

	/* mark the data component as protected indirectly */
	comp->llc_cstripe_count = cstripe_count;
	comp->llc_dstripe_count = dstripe_count;

	/*
	 * Copy stripe size from the matching data component.
	 * EC/parity components must have the same stripe size as their
	 * corresponding data components.
	 */
	parity_comp->llc_stripe_size = comp->llc_stripe_size;
	/*
	 * Copy pool name from the matching data component so that
	 * parity components inherit the same pool placement.
	 */
	if (comp->llc_pool_name[0] != '\0')
		snprintf(parity_comp->llc_pool_name,
			 sizeof(parity_comp->llc_pool_name), "%s",
			 comp->llc_pool_name);

	/* bi-directional link for data and parity components */
	parity_comp->llc_mirror_link_id = layout->llot_curr_link_id;
	comp->llc_mirror_link_id = layout->llot_curr_link_id;
	parity_comp->llc_flags |= LCME_FL_IS_LINK_ID;
	comp->llc_flags |= LCME_FL_IS_LINK_ID;
	layout->llot_curr_link_id++;

	return 0;
}

/**
 * Get EC coding stripe count from the current component.
 *
 * \param[in] layout		existing layout
 * \param[out] cstripe_count	EC coding stripe count
 *
 * \retval	0 on success
 * \retval	<0 if error occurs
 */
int llapi_layout_ec_cstripe_count_get(const struct llapi_layout *layout,
				      uint8_t *cstripe_count)
{
	struct llapi_layout_comp *comp;

	comp = __llapi_layout_cur_comp(layout);
	if (!comp)
		return -1;

	if (!cstripe_count) {
		errno = EINVAL;
		return -1;
	}

	if (!(comp->llc_flags & LCME_FL_PARITY)) {
		errno = EINVAL;
		return -1;
	}

	*cstripe_count = comp->llc_cstripe_count;
	return 0;
}

/**
 * Get EC data stripe count from the current component.
 *
 * \param[in] layout		existing layout
 * \param[out] dstripe_count	EC data stripe count
 *
 * \retval	0 on success
 * \retval	<0 if error occurs
 */
int llapi_layout_ec_dstripe_count_get(const struct llapi_layout *layout,
				      uint8_t *dstripe_count)
{
	struct llapi_layout_comp *comp;

	comp = __llapi_layout_cur_comp(layout);
	if (!comp)
		return -1;

	if (!dstripe_count) {
		errno = EINVAL;
		return -1;
	}

	if (!(comp->llc_flags & LCME_FL_PARITY)) {
		errno = EINVAL;
		return -1;
	}

	*dstripe_count = comp->llc_dstripe_count;
	return 0;
}

/**
 * Adds a first component of a mirror to \a layout.
 * The \a layout will change it's current component pointer to
 * the newly added component, and it'll be turned into a composite
 * layout if it was not before the adding.
 *
 * The @layout will change it's current component pointer to the newly added
 * component, and it'll be turned into a composite layout if it was not before
 * the adding.
 *
 * Return:
 * * %0 on success
 * * %negative if error occurs
 */
int llapi_layout_add_first_comp(struct llapi_layout *layout)
{
	struct llapi_layout_comp *comp, *new;

	comp = __llapi_layout_cur_comp(layout);
	if (comp == NULL)
		return -1;

	new = __llapi_comp_alloc(0);
	if (new == NULL)
		return -1;

	new->llc_extent.e_start = 0;

	list_add_tail(&new->llc_list, &layout->llot_comp_list);
	layout->llot_cur_comp = new;
	layout->llot_is_composite = true;

	return 0;
}

/**
 * llapi_layout_comp_del() - Deletes current component from the composite layout
 * @layout: composite layout
 *
 * Deletes current component from the composite layout. The component to be
 * deleted must be the tail of components list, and it can't be the only
 * component in the layout.
 *
 * Return:
 * * %0 on success
 * * %negative if error occurs
 */
int llapi_layout_comp_del(struct llapi_layout *layout)
{
	struct llapi_layout_comp *comp;
	struct llapi_layout_comp *data_comp = NULL;
	uint32_t comp_mirror_link_id = 0;
	bool is_parity_comp = false;
	uint64_t comp_start = 0;
	uint64_t comp_end = 0;

	comp = __llapi_layout_cur_comp(layout);
	if (comp == NULL)
		return -1;

	if (!layout->llot_is_composite) {
		errno = EINVAL;
		return -1;
	}

	if (comp->llc_flags & LCME_FL_PARITY) {
		bool found = false;

		/* Save protected comp info to clear link later on data comp */
		is_parity_comp = true;
		comp_start = comp->llc_extent.e_start;
		comp_end = comp->llc_extent.e_end;
		comp_mirror_link_id = comp->llc_flags & LCME_FL_IS_LINK_ID ?
					      comp->llc_mirror_link_id :
					      comp->llc_mirror_id;

		/* find the protected data component */
		list_for_each_entry(data_comp, &layout->llot_comp_list,
				    llc_list) {
			if (data_comp->llc_mirror_link_id ==
				    comp_mirror_link_id &&
			    data_comp->llc_extent.e_start == comp_start &&
			    data_comp->llc_extent.e_end == comp_end) {
				found = true;
				break;
			}
		}

		/* data component must exist; otherwise layout is invalid */
		if (!found) {
			errno = ENOENT;
			return -1;
		}
	} else {
		/* A data comp that is protected by a parity comp can't be
		 * deleted until the parity comp is deleted.
		 */
		if (comp->llc_mirror_link_id != LLAPI_MIRROR_LINK_NONE) {
			errno = EINVAL;
			return -1;
		}
	}

	/* It must be the tail of the list (for PFL, can be relaxed
	 * once we get mirrored components) */
	if (comp->llc_list.next != &layout->llot_comp_list) {
		errno = EINVAL;
		return -1;
	}
	layout->llot_cur_comp =
		list_last_entry(&comp->llc_list, typeof(*comp), llc_list);
	if (comp->llc_list.prev == &layout->llot_comp_list)
		layout->llot_cur_comp = NULL;

	list_del_init(&comp->llc_list);
	__llapi_comp_free(comp);

	if (!is_parity_comp)
		return 0;

	/* Clear link on protected data_component */
	data_comp->llc_mirror_link_id = LLAPI_MIRROR_LINK_NONE;
	data_comp->llc_flags &= ~LCME_FL_IS_LINK_ID;

	return 0;
}

/**
 * llapi_layout_comp_use_id() - Move the current component pointer to the
 * component with specified component ID.
 * @layout: composite layout
 * @comp_id: component ID
 *
 * Return:
 * * %0 on success
 * * %negative if error occurs
 */
int llapi_layout_comp_use_id(struct llapi_layout *layout, uint32_t comp_id)
{
	struct llapi_layout_comp *comp;

	comp = __llapi_layout_cur_comp(layout);
	if (comp == NULL)
		return -1; /* use previously set errno */

	if (!layout->llot_is_composite) {
		errno = EINVAL;
		return -1;
	}

	if (comp_id == LCME_ID_INVAL) {
		errno = EINVAL;
		return -1;
	}

	list_for_each_entry(comp, &layout->llot_comp_list, llc_list) {
		if (comp->llc_id == comp_id) {
			layout->llot_cur_comp = comp;
			return 0;
		}
	}
	errno = ENOENT;
	return -1;
}

/**
 * __llapi_layout_find_comp_by_id() - Find component by ID
 * @layout: layout structure
 * @comp_id: component ID
 *
 * Return:
 * * pointer to component on success
 * * NULL if not found
 */
static struct llapi_layout_comp *
__llapi_layout_find_comp_by_id(struct llapi_layout *layout, uint32_t comp_id)
{
	struct llapi_layout_comp *comp;

	list_for_each_entry(comp, &layout->llot_comp_list, llc_list)
		if (comp->llc_id == comp_id)
			return comp;

	return NULL;
}

/**
 * llapi_layout_find_data_comp_by_parity() - Find data component for parity comp
 * @layout: layout structure
 * @parity_comp: parity component
 *
 * Return:
 * * pointer to data component on success
 * * NULL if not found
 */
static struct llapi_layout_comp *
__llapi_layout_find_data_comp_by_parity(struct llapi_layout *layout,
					struct llapi_layout_comp *parity_comp)
{
	struct llapi_layout_comp *comp;
	bool is_link_id = parity_comp->llc_flags & LCME_FL_IS_LINK_ID;

	list_for_each_entry(comp, &layout->llot_comp_list, llc_list) {
		uint16_t comp_link_id;

		if (comp->llc_flags & LCME_FL_PARITY)
			continue;

		comp_link_id = is_link_id ? comp->llc_mirror_link_id :
					    comp->llc_mirror_id;

		/* Check if link ids match and extents match */
		if (comp_link_id == parity_comp->llc_mirror_link_id &&
		    comp->llc_extent.e_start ==
			    parity_comp->llc_extent.e_start &&
		    comp->llc_extent.e_end == parity_comp->llc_extent.e_end)
			return comp;
	}

	return NULL;
}

/**
 * llapi_layout_comp_use() - Move the current component pointer to a specified
 * position.
 *
 * @layout: composite layout
 * @pos: the position to be moved, it can be:
 *       LLAPI_LAYOUT_COMP_USE_FIRST: use first component
 *       LLAPI_LAYOUT_COMP_USE_LAST: use last component
 *       LLAPI_LAYOUT_COMP_USE_NEXT: use component after current
 *       LLAPI_LAYOUT_COMP_USE_PREV: use component before current
 *
 * Return:
 * * %0 moved successfully
 * * %1 at last component with NEXT, at first component with PREV
 * * %negative if error occurs
 */
int llapi_layout_comp_use(struct llapi_layout *layout,
			  enum llapi_layout_comp_use pos)
{
	struct llapi_layout_comp *comp, *head, *tail;

	comp = __llapi_layout_cur_comp(layout);
	if (comp == NULL)
		return -1;

	if (!layout->llot_is_composite) {
		if (pos == LLAPI_LAYOUT_COMP_USE_FIRST ||
		    pos == LLAPI_LAYOUT_COMP_USE_LAST)
			return 0;
		errno = ENOENT;
		return 1;
	}

	head = list_first_entry(&layout->llot_comp_list, typeof(*head),
				llc_list);
	tail = list_last_entry(&layout->llot_comp_list, typeof(*tail),
			       llc_list);
	switch (pos) {
	case LLAPI_LAYOUT_COMP_USE_FIRST:
		layout->llot_cur_comp = head;
		break;
	case LLAPI_LAYOUT_COMP_USE_NEXT:
		if (comp == tail) {
			errno = ENOENT;
			return 1;
		}
		layout->llot_cur_comp = list_first_entry(&comp->llc_list,
							 typeof(*comp),
							 llc_list);
		break;
	case LLAPI_LAYOUT_COMP_USE_LAST:
		layout->llot_cur_comp = tail;
		break;
	case LLAPI_LAYOUT_COMP_USE_PREV:
		if (comp == head) {
			errno = ENOENT;
			return 1;
		}
		layout->llot_cur_comp = list_last_entry(&comp->llc_list,
							typeof(*comp),
							llc_list);
		break;
	default:
		errno = EINVAL;
		return -1;
	}

	return 0;
}

/**
 * llapi_layout_file_comp_add() - Add layout component(s) to an existing file.
 * @path: The path name of the file
 * @layout: The layout component(s) to be added
 *
 * Return:
 * * %0 on success
 * * %negative if error occurs
 */
int llapi_layout_file_comp_add(const char *path,
			       const struct llapi_layout *layout)
{
	int rc, fd = -1, lum_size, tmp_errno = 0;
	struct llapi_layout *existing_layout = NULL;
	struct lov_user_md *lum = NULL;
	char fsname[MAX_OBD_NAME + 1] = { 0 };
	struct llapi_layout_comp *comp;

	if (path == NULL || layout == NULL ||
	    layout->llot_magic != LLAPI_LAYOUT_MAGIC) {
		errno = EINVAL;
		return -1;
	}

	fd = open(path, O_RDWR);
	if (fd < 0) {
		tmp_errno = errno;
		rc = -1;
		goto out;
	}

	existing_layout = llapi_layout_get_by_fd(fd, 0);
	if (existing_layout == NULL) {
		tmp_errno = errno;
		rc = -1;
		goto out;
	}

	rc = llapi_layout_merge(&existing_layout, layout);
	if (rc) {
		tmp_errno = errno;
		rc = -1;
		goto out;
	}

	comp = __llapi_layout_cur_comp(layout);

	if (comp != NULL && comp->llc_pool_name[0] != '\0' &&
	    !lov_pool_is_ignored(comp->llc_pool_name)) {
		rc = llapi_search_fsname(path, fsname);
		if (rc) {
			tmp_errno = -rc;
			rc = -1;
			goto out;
		}
	}

	rc = llapi_layout_v2_sanity(existing_layout, false, false, fsname);
	if (rc) {
		tmp_errno = errno;
		llapi_layout_sanity_perror(rc);
		rc = -1;
		goto out;
	}

	lum = llapi_layout_to_lum(layout);
	if (lum == NULL) {
		tmp_errno = errno;
		rc = -1;
		goto out;
	}

	if (lum->lmm_magic != LOV_USER_MAGIC_COMP_V1) {
		tmp_errno = EINVAL;
		rc = -1;
		goto out;
	}
	lum_size = ((struct lov_comp_md_v1 *)lum)->lcm_size;

	rc = fsetxattr(fd, XATTR_LUSTRE_LOV".add", lum, lum_size, 0);
	if (rc < 0) {
		tmp_errno = errno;
		rc = -1;
		goto out;
	}
out:
	if (fd >= 0)
		close(fd);
	free(lum);
	llapi_layout_free(existing_layout);
	errno = tmp_errno;
	return rc;
}

/**
 * llapi_layout_file_comp_del() - Delete component(s) by component id
 * @path: path name of the file
 * @id: unique component ID
 * @flags: flags: LCME_FL_* or; negative flags: (LCME_FL_NEG|LCME_FL_*)
 *
 * Delete component(s) by the specified component id or component flags
 * from an existing file.
 *
 * Return:
 * * %0 on success
 * * %negative if error occurs
 */
int llapi_layout_file_comp_del(const char *path, uint32_t id, uint32_t flags)
{
	int rc = 0, fd = -1, lum_size, tmp_errno = 0;
	struct llapi_layout *layout;
	struct llapi_layout_comp *comp, *next;
	struct llapi_layout *existing_layout = NULL;
	struct lov_user_md *lum = NULL;

	if (path == NULL || id > LCME_ID_MAX || (flags & ~LCME_KNOWN_FLAGS)) {
		errno = EINVAL;
		return -1;
	}

	/* Can only specify ID or flags, not both, not none. */
	if ((id != LCME_ID_INVAL && flags != 0) ||
	    (id == LCME_ID_INVAL && flags == 0)) {
		errno = EINVAL;
		return -1;
	}

	layout = llapi_layout_alloc();
	if (layout == NULL)
		return -1;

	llapi_layout_comp_extent_set(layout, 0, LUSTRE_EOF);
	comp = __llapi_layout_cur_comp(layout);
	if (comp == NULL) {
		tmp_errno = errno;
		rc = -1;
		goto out;
	}

	comp->llc_id = id;
	comp->llc_flags = flags;

	lum = llapi_layout_to_lum(layout);
	if (lum == NULL) {
		tmp_errno = errno;
		rc = -1;
		goto out;
	}
	lum_size = ((struct lov_comp_md_v1 *)lum)->lcm_size;

	fd = open(path, O_RDWR);
	if (fd < 0) {
		tmp_errno = errno;
		rc = -1;
		goto out;
	}

	existing_layout = llapi_layout_get_by_fd(fd, 0);
	if (existing_layout == NULL) {
		tmp_errno = errno;
		rc = -1;
		goto out;
	}

	comp = NULL;
	next = NULL;
	while (rc == 0 && existing_layout->llot_cur_comp != NULL) {
		rc = llapi_layout_comp_use(existing_layout, comp ?
					   LLAPI_LAYOUT_COMP_USE_PREV :
					   LLAPI_LAYOUT_COMP_USE_LAST);
		if (rc != 0)
			break;

		next = comp;
		comp = __llapi_layout_cur_comp(existing_layout);
		if (comp == NULL) {
			rc = -1;
			break;
		}

		if (id != LCME_ID_INVAL && id != comp->llc_id)
			continue;
		else if ((flags & LCME_FL_NEG) && (flags & comp->llc_flags))
			continue;
		else if (flags && !(flags & comp->llc_flags))
			continue;

		rc = llapi_layout_comp_del(existing_layout);
		/* the layout position is moved to previous one, adjust */
		comp = next;
	}
	if (rc < 0) {
		tmp_errno = errno;
		goto out;
	}

	rc = llapi_layout_sanity(existing_layout, false, false);
	if (rc) {
		tmp_errno = errno;
		llapi_layout_sanity_perror(rc);
		rc = -1;
		goto out;
	}

	rc = fsetxattr(fd, XATTR_LUSTRE_LOV".del", lum, lum_size, 0);
	if (rc < 0) {
		tmp_errno = errno;
		rc = -1;
		goto out;
	}

out:
	if (fd >= 0)
		close(fd);
	free(lum);
	llapi_layout_free(layout);
	llapi_layout_free(existing_layout);
	errno = tmp_errno;

	return rc;
}

/* Internal utility function to apply flags for sanity checking */
static void llapi_layout_comp_apply_flags(struct llapi_layout_comp *comp,
					  uint32_t flags)
{
	if (flags & LCME_FL_NEG)
		comp->llc_flags &= ~flags;
	else
		comp->llc_flags |= flags;
}

struct llapi_layout_apply_flags_args {
	uint32_t *lfa_ids;
	uint32_t *lfa_flags;
	int lfa_count;
	int lfa_rc;
};


static int llapi_layout_apply_flags_cb(struct llapi_layout *layout,
				       void *arg)
{
	struct llapi_layout_apply_flags_args *args = arg;
	struct llapi_layout_comp *comp;
	int i = 0;

	comp = __llapi_layout_cur_comp(layout);
	if (comp == NULL) {
		args->lfa_rc = -1;
		return LLAPI_LAYOUT_ITER_STOP;
	}

	for (i = 0; i < args->lfa_count; i++) {
		if (comp->llc_id == args->lfa_ids[i])
			llapi_layout_comp_apply_flags(comp, args->lfa_flags[i]);
	}

	return LLAPI_LAYOUT_ITER_CONT;
}

/* Apply flags to the layout for sanity checking */
static int llapi_layout_apply_flags(struct llapi_layout *layout, uint32_t *ids,
				    uint32_t *flags, int count)
{
	struct llapi_layout_apply_flags_args args;
	int rc = 0;

	if (!ids || !flags || count == 0) {
		errno = EINVAL;
		return -1;
	}

	args.lfa_ids = ids;
	args.lfa_flags = flags;
	args.lfa_count = count;
	args.lfa_rc = 0;

	rc = llapi_layout_comp_iterate(layout,
				       llapi_layout_apply_flags_cb,
				       &args);
	if (errno == ENOENT)
		errno = 0;

	if (rc != LLAPI_LAYOUT_ITER_CONT)
		rc = args.lfa_rc;

	return rc;
}
/**
 * llapi_layout_file_comp_set() - Change flags by component ID of components
 * @path: path name of the file
 * @ids: An array of component IDs
 * @flags: flags: LCME_FL_* or; negative flags: (LCME_FL_NEG|LCME_FL_*)
 * @count: Number of elements in ids and flags array
 *
 * Change flags by component ID of components of an existing file.
 * The component to be modified is specified by the comp->lcme_id value,
 * which must be a unique component ID.
 *
 * Return:
 * * %0 on success
 * * %negative if error occurs
 */
int llapi_layout_file_comp_set(const char *path, uint32_t *ids, uint32_t *flags,
			       size_t count)
{
	int rc = -1, fd = -1, i, tmp_errno = 0;
	size_t lum_size;
	struct llapi_layout *existing_layout = NULL;
	struct llapi_layout *layout = NULL;
	struct llapi_layout_comp *comp;
	struct lov_user_md *lum = NULL;
	char fsname[MAX_OBD_NAME + 1] = { 0 };

	if (path == NULL) {
		errno = EINVAL;
		return -1;
	}

	if (!count)
		return 0;

	for (i = 0; i < count; i++) {
		if (!ids[i] || !flags[i]) {
			errno = EINVAL;
			return -1;
		}

		if (ids[i] > LCME_ID_MAX || (flags[i] & ~LCME_KNOWN_FLAGS)) {
			errno = EINVAL;
			return -1;
		}

		/* do not allow to set or clear INIT flag */
		if (flags[i] & LCME_FL_INIT) {
			errno = EINVAL;
			return -1;
		}
	}

	fd = open(path, O_RDWR);
	if (fd < 0) {
		tmp_errno = errno;
		rc = -1;
		goto out;
	}

	existing_layout = llapi_layout_get_by_fd(fd, 0);
	if (existing_layout == NULL) {
		tmp_errno = errno;
		rc = -1;
		goto out;
	}

	if (llapi_layout_apply_flags(existing_layout, ids, flags, count)) {
		tmp_errno = errno;
		rc = -1;
		goto out;
	}

	comp = __llapi_layout_cur_comp(layout);

	if (comp && comp->llc_pool_name[0] != '\0' &&
	    !lov_pool_is_ignored(comp->llc_pool_name)) {
		rc = llapi_search_fsname(path, fsname);
		if (rc) {
			tmp_errno = -rc;
			rc = -1;
			goto out;
		}
	}

	rc = llapi_layout_v2_sanity(existing_layout, false, false, fsname);
	if (rc) {
		tmp_errno = errno;
		llapi_layout_sanity_perror(rc);
		rc = -1;
		goto out;
	}

	layout = __llapi_layout_alloc();
	if (layout == NULL) {
		tmp_errno = errno;
		rc = -1;
		goto out;
	}

	layout->llot_is_composite = true;
	for (i = 0; i < count; i++) {
		comp = __llapi_comp_alloc(0);
		if (comp == NULL) {
			tmp_errno = errno;
			rc = -1;
			goto out;
		}

		comp->llc_id = ids[i];
		comp->llc_flags = flags[i];

		list_add_tail(&comp->llc_list, &layout->llot_comp_list);
		layout->llot_cur_comp = comp;
	}

	lum = llapi_layout_to_lum(layout);
	if (lum == NULL) {
		tmp_errno = errno;
		rc = -1;
		goto out;
	}

	lum_size = ((struct lov_comp_md_v1 *)lum)->lcm_size;

	/* flush cached pages from clients */
	rc = llapi_file_flush(fd);
	if (rc) {
		tmp_errno = -rc;
		rc = -1;
		goto out;
	}

	rc = fsetxattr(fd, XATTR_LUSTRE_LOV".set.flags", lum, lum_size, 0);
	if (rc < 0) {
		tmp_errno = errno;
		goto out;
	}

	rc = 0;

out:
	if (fd >= 0)
		close(fd);

	free(lum);
	llapi_layout_free(existing_layout);
	llapi_layout_free(layout);
	errno = tmp_errno;
	return rc;
}

/**
 * llapi_layout_is_composite() - Check if the file layout is composite.
 * @layout: the file layout to check
 *
 * Return:
 * * %true composite
 * * %false not composite
 */
bool llapi_layout_is_composite(struct llapi_layout *layout)
{
	return layout->llot_is_composite;
}

/**
 * llapi_layout_comp_iterate() - Iterate every components in the @layout and
 * call callback function @cb.
 * @layout: component layout list.
 * @cb: callback function called for each component
 * @cbdata: callback data passed to the callback function
 *
 * Return:
 * * %negative error happens during the iteration
 * * %LLAPI_LAYOUT_ITER_CONT finished the iteration w/o error
 * * %LLAPI_LAYOUT_ITER_STOP got something, stop the iteration
 */
int llapi_layout_comp_iterate(struct llapi_layout *layout,
			      llapi_layout_iter_cb cb, void *cbdata)
{
	int rc;

	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
	if (rc < 0)
		return rc;

	/**
	 * make sure on success llapi_layout_comp_use() API returns 0 with
	 * USE_FIRST.
	 */
	assert(rc == 0);

	while (1) {
		rc = cb(layout, cbdata);
		if (rc != LLAPI_LAYOUT_ITER_CONT)
			break;

		rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
		if (rc < 0)
			return rc;
		else if (rc == 1)	/* reached the last comp */
			return LLAPI_LAYOUT_ITER_CONT;
	}

	return rc;
}

/**
 * layout_convert_mirror_to_link_ids() - Convert mirror ID bindings to temporary
 * link IDs
 * @layout: layout to convert
 *
 * When existing layouts are modified, e.g., through llapi_layout_merge(),
 * mirror ids are regenerated by the server. To preserve the data-parity
 * binding, the mirror id bindings are converted to temporary link id bindings
 * that are later converted back when the mirror ids are set by the server.
 */
static void layout_convert_mirror_to_link_ids(struct llapi_layout *layout)
{
	struct llapi_layout_comp *data_comp, *parity_comp;
	uint16_t new_link_id;

	if (!layout)
		return;

	/* find data components with mirror id bindings */
	list_for_each_entry(data_comp, &layout->llot_comp_list, llc_list) {
		uint16_t old_mirror_id;

		if ((data_comp->llc_flags & LCME_FL_PARITY) ||
		    (data_comp->llc_flags & LCME_FL_IS_LINK_ID))
			continue;

		if (data_comp->llc_mirror_link_id == 0)
			continue;

		/* convert mirror id binding to link id */
		old_mirror_id = data_comp->llc_mirror_link_id;
		new_link_id = layout->llot_curr_link_id++;

		data_comp->llc_mirror_link_id = new_link_id;
		data_comp->llc_flags |= LCME_FL_IS_LINK_ID;

		/* find parity components with the same mirror ID */
		list_for_each_entry(parity_comp, &layout->llot_comp_list,
				    llc_list) {
			if (!(parity_comp->llc_flags & LCME_FL_PARITY) ||
			    (parity_comp->llc_flags & LCME_FL_IS_LINK_ID))
				continue;

			if (parity_comp->llc_mirror_link_id != old_mirror_id)
				continue;

			parity_comp->llc_mirror_link_id = new_link_id;
			parity_comp->llc_flags |= LCME_FL_IS_LINK_ID;
		}
	}
}

/**
 * llapi_layout_merge() - Merge a composite layout into another one.
 * @dst_layout: Destination composite layout.
 * @src_layout: Source composite layout.
 *
 * This function copies all of the components from @src_layout and
 * appends them to @dst_layout.
 *
 * Return: 0 on success or %negative on failure.
 */
int llapi_layout_merge(struct llapi_layout **dst_layout,
		       const struct llapi_layout *src_layout)
{
	struct llapi_layout *new_layout = *dst_layout;
	struct llapi_layout_comp *new = NULL;
	struct llapi_layout_comp *comp = NULL;
	uint16_t link_id_offset;
	uint32_t mirror_id_offset;
	int i = 0;

	if (!src_layout ||
	    list_empty((struct list_head *)&src_layout->llot_comp_list))
		return 0;

	if (!new_layout) {
		new_layout = __llapi_layout_alloc();
		if (!new_layout) {
			errno = ENOMEM;
			return -1;
		}
	}

	if (llapi_layout_mirror_count_sync(new_layout))
		goto error;

	link_id_offset = new_layout->llot_curr_link_id - 1;
	mirror_id_offset = new_layout->llot_curr_mirror_id - 1;

	list_for_each_entry(comp, &src_layout->llot_comp_list, llc_list) {
		new = __llapi_comp_alloc(0);
		if (!new) {
			errno = ENOMEM;
			goto error;
		}

		new->llc_pattern = comp->llc_pattern;
		new->llc_stripe_size = comp->llc_stripe_size;
		new->llc_stripe_count = comp->llc_stripe_count;
		new->llc_stripe_offset = comp->llc_stripe_offset;

		if (comp->llc_pool_name[0] != '\0')
			snprintf(new->llc_pool_name, sizeof(new->llc_pool_name),
				 "%s", comp->llc_pool_name);

		for (i = 0; i < comp->llc_objects_count; i++) {
			if (__llapi_comp_objects_realloc(
				    new, stripe_number_roundup(i)) < 0) {
				errno = EINVAL;
				__llapi_comp_free(new);
				goto error;
			}
			new->llc_objects[i].l_ost_idx =
				comp->llc_objects[i].l_ost_idx;
		}

		new->llc_objects_count = comp->llc_objects_count;
		new->llc_extent.e_start = comp->llc_extent.e_start;
		new->llc_extent.e_end = comp->llc_extent.e_end;
		new->llc_id = comp->llc_id;
		new->llc_flags = comp->llc_flags;
		new->llc_dstripe_count = comp->llc_dstripe_count;
		new->llc_cstripe_count = comp->llc_cstripe_count;

		/* offset mirror ids on the src layout */
		new->llc_mirror_id = comp->llc_mirror_id + mirror_id_offset;
		new_layout->llot_curr_mirror_id = new->llc_mirror_id + 1;

		/* offset all mirror link ids on the src layout */
		if (comp->llc_mirror_link_id != 0) {
			if (comp->llc_flags & LCME_FL_IS_LINK_ID) {
				new->llc_mirror_link_id =
					comp->llc_mirror_link_id +
					link_id_offset;
				new->llc_flags |= LCME_FL_IS_LINK_ID;
				new_layout->llot_curr_link_id =
					new->llc_mirror_link_id + 1;
			} else {
				new->llc_mirror_link_id =
					comp->llc_mirror_link_id +
					mirror_id_offset;
			}
		}

		list_add_tail(&new->llc_list, &new_layout->llot_comp_list);
		new_layout->llot_cur_comp = new;
	}
	new_layout->llot_is_composite = true;

	/* convert bindings to link ids since mirror ids are reset by the lod */
	layout_convert_mirror_to_link_ids(new_layout);
	if (llapi_layout_mirror_count_sync(new_layout))
		goto error;

	*dst_layout = new_layout;

	return 0;
error:
	llapi_layout_free(new_layout);
	*dst_layout = NULL;
	return -1;
}

/**
 * llapi_layout_get_last_init_comp() - Get the last initialized component
 * @layout: component layout list.
 *
 * Return:
 * * %0 found
 * * %-EINVAL not found
 * * %-EISDIR directory layout
 */
int llapi_layout_get_last_init_comp(struct llapi_layout *layout)
{
	struct llapi_layout_comp *comp = NULL, *head = NULL;

	if (!layout->llot_is_composite)
		return 0;

	head = list_first_entry(&layout->llot_comp_list, typeof(*comp),
				llc_list);
	if (head == NULL)
		return -EINVAL;
	if (head->llc_id == 0 && !(head->llc_flags & LCME_FL_INIT))
		/* a directory */
		return -EISDIR;

	/* traverse the components from the tail to find the last init one */
	comp = list_last_entry(&layout->llot_comp_list, typeof(*comp),
			       llc_list);
	while (comp != head) {
		if (comp->llc_flags & LCME_FL_INIT)
			break;
		comp = list_last_entry(&comp->llc_list, typeof(*comp),
				       llc_list);
	}

	layout->llot_cur_comp = comp;

	return comp->llc_flags & LCME_FL_INIT ? 0 : -EINVAL;
}

/**
 * llapi_layout_mirror_inherit() - Interit stripe info from the file's component
 * to the mirror
 * @f_layout: file component layout list.
 * @m_layout: mirro component layout list.
 *
 * Return:
 * * %0 on success
 * * %-EINVAL on error
 */
int llapi_layout_mirror_inherit(struct llapi_layout *f_layout,
				struct llapi_layout *m_layout)
{
	struct llapi_layout_comp *m_comp = NULL;
	struct llapi_layout_comp *f_comp = NULL;
	int rc = 0;

	f_comp = __llapi_layout_cur_comp(f_layout);
	if (f_comp == NULL)
		return -EINVAL;
	m_comp = __llapi_layout_cur_comp(m_layout);
	if (m_comp == NULL)
		return -EINVAL;

	/* DoM component does not inherit stripe size */
	if (m_comp->llc_pattern != LLAPI_LAYOUT_MDT)
		m_comp->llc_stripe_size = f_comp->llc_stripe_size;
	m_comp->llc_stripe_count = f_comp->llc_stripe_count;

	return rc;
}

/**
 * llapi_mirror_find_stale() - Find all stale components.
 * @layout: component layout list.
 * @comp: array of stale component info. [out]
 * @comp_size: array size of @comp.
 * @mirror_ids: array of mirror id that only components belonging to these
 *              mirror will be collected.
 * @ids_nr: number of mirror ids array.
 *
 * Return number of component info collected on success or error code on failure
 */
static int _mirror_find_stale(struct llapi_layout *layout,
		struct llapi_resync_comp *comp, size_t comp_size,
		__u16 *mirror_ids, int ids_nr, bool find_ec)
{
	int idx = 0;
	int rc;

	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
	if (rc < 0)
		goto error;

	while (rc == 0) {
		uint32_t id;
		uint32_t mirror_id;
		uint32_t flags;
		uint64_t start, end;

		rc = llapi_layout_comp_flags_get(layout, &flags);
		if (rc < 0)
			goto error;

		if (!(flags & LCME_FL_STALE))
			goto next;
		if (find_ec && !(flags & LCME_FL_PARITY))
			goto next;
		if (!find_ec && (flags & LCME_FL_PARITY))
			goto next;

		rc = llapi_layout_mirror_id_get(layout, &mirror_id);
		if (rc < 0)
			goto error;

		/* the caller only wants stale components from specific
		 * mirrors */
		if (ids_nr > 0) {
			int j;

			for (j = 0; j < ids_nr; j++) {
				if (mirror_ids[j] == mirror_id)
					break;
			}

			/* not in the specified mirror */
			if (j == ids_nr)
				goto next;
		} else if (flags & LCME_FL_NOSYNC) {
			/* if not specified mirrors, do not resync "nosync"
			 * mirrors */
			goto next;
		}

		rc = llapi_layout_comp_id_get(layout, &id);
		if (rc < 0)
			goto error;

		rc = llapi_layout_comp_extent_get(layout, &start, &end);
		if (rc < 0)
			goto error;

		/* pack this component into @comp array */
		comp[idx].lrc_id = id;
		comp[idx].lrc_mirror_id = mirror_id;
		comp[idx].lrc_start = start;
		comp[idx].lrc_end = end;
		comp[idx].lrc_synced = true;
		idx++;

		if (idx >= comp_size) {
			rc = -EINVAL;
			goto error;
		}

	next:
		rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
		if (rc < 0) {
			rc = -EINVAL;
			goto error;
		}
	}
error:
	return rc < 0 ? rc : idx;
}

int llapi_mirror_find_stale(struct llapi_layout *layout,
		struct llapi_resync_comp *comp, size_t comp_size,
		__u16 *mirror_ids, int ids_nr)
{
	return _mirror_find_stale(layout, comp, comp_size,
				  mirror_ids, ids_nr, 0);
}

int llapi_ec_find_stale(struct llapi_layout *layout,
		struct llapi_resync_comp *comp, size_t comp_size,
		__u16 *mirror_ids, int ids_nr)
{
	return _mirror_find_stale(layout, comp, comp_size,
				  mirror_ids, ids_nr, 1);
}

/* locate @layout to a valid component covering file [file_start, file_end) */
int llapi_mirror_find(struct llapi_layout *layout, uint64_t file_start,
		      uint64_t file_end, uint64_t *endp)
{
	uint32_t mirror_id = 0;
	int rc;

	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
	if (rc < 0)
		return rc;

	*endp = 0;
	while (rc == 0) {
		uint64_t start, end;
		uint32_t flags, id, rid;

		rc = llapi_layout_comp_flags_get(layout, &flags);
		if (rc < 0)
			return rc;

		if (flags & LCME_FL_STALE)
			goto next;

		if (flags & LCME_FL_PARITY)
			goto next;

		rc = llapi_layout_mirror_id_get(layout, &rid);
		if (rc < 0)
			return rc;

		rc = llapi_layout_comp_id_get(layout, &id);
		if (rc < 0)
			return rc;

		rc = llapi_layout_comp_extent_get(layout, &start, &end);
		if (rc < 0)
			return rc;

		if (file_start >= start && file_start < end) {
			if (!mirror_id)
				mirror_id = rid;
			else if (mirror_id != rid || *endp != start)
				break;

			file_start = *endp = end;
			if (end >= file_end)
				break;
		}

	next:
		rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
		if (rc < 0)
			return rc;
	}
	if (!mirror_id)
		return -ENOENT;

	return mirror_id;
}

int llapi_mirror_resync_many_params(int fd, struct llapi_layout *layout,
				    struct llapi_resync_comp *comp_array,
				    int comp_size, uint64_t start, uint64_t end,
				    unsigned long stats_interval_sec,
				    uint64_t bandwidth_bytes_sec)
{
	struct stat stbuf;
	size_t buflen = 64 << 20; /* 64M */
	ssize_t page_size;
	void *buf;
	uint64_t pos = start;
	uint64_t data_off = pos, data_end = pos;
	uint64_t mirror_end = LUSTRE_EOF;
	uint32_t src = 0;
	int i;
	int rc;
	int rc2 = 0;
	struct timespec start_time;
	struct timespec now;
	struct timespec last_bw_print;
	uint64_t total_bytes_read = 0;
	uint64_t total_bytes_written = 0;
	uint64_t write_estimation_bytes = 0;

	rc = fstat(fd, &stbuf);
	if (rc < 0)
		return -errno;

	/* estimate is too big for sparse file, but good enough for % done */
	if (bandwidth_bytes_sec > 0 || stats_interval_sec) {
		for (i = 0; i < comp_size; i++) {
			write_estimation_bytes +=
				min_t(uint64_t, comp_array[i].lrc_end,
				      stbuf.st_size) - comp_array[i].lrc_start;
		}
	}

	/* limit transfer size to what can be sent in one second */
	if (bandwidth_bytes_sec && bandwidth_bytes_sec < buflen)
		buflen = (bandwidth_bytes_sec + ONE_MB - 1) & ~(ONE_MB - 1);

	page_size = sysconf(_SC_PAGESIZE);
	if (page_size < 0) {
		rc = -errno;
		return rc;
	}

	rc = posix_memalign(&buf, page_size, buflen);
	if (rc)
		return -rc;
	(void)mlock(buf, buflen);

	clock_gettime(CLOCK_MONOTONIC, &start_time);
	now = last_bw_print = start_time;

	while (pos < end) {
		ssize_t bytes_read;
		size_t to_read;
		size_t to_write;
		size_t data_size;

		if (pos >= data_end) {
			off_t tmp_off;

			if (pos >= mirror_end || !src) {
				rc = llapi_mirror_find(layout, pos, end,
							&mirror_end);
				if (rc < 0) {
					llapi_error(LLAPI_MSG_ERROR, rc,
						    "cannot find source mirror");
					goto out_free;
				}
				src = rc;
				/* restrict mirror end by resync end */
				mirror_end = MIN(end, mirror_end);
			}

			tmp_off = llapi_mirror_data_seek(fd, src, pos,
							 &data_size);
			if (tmp_off < 0) {
				/* switch to full copy */
				to_read = mirror_end - pos;
				goto do_read;
			}
			data_off = tmp_off;
			data_end = data_off + data_size;

			data_off = MIN(data_off, mirror_end);
			data_end = MIN(data_end, mirror_end);

			/* align by page, if there is data block to copy */
			if (data_size)
				data_off &= ~(page_size - 1);
		}

		if (pos < data_off) {
			for (i = 0; i < comp_size; i++) {
				uint64_t cur_pos;
				size_t to_punch;
				uint32_t mid = comp_array[i].lrc_mirror_id;

				/* skip non-overlapped component */
				if (pos >= comp_array[i].lrc_end ||
				    data_off <= comp_array[i].lrc_start)
					continue;

				if (pos < comp_array[i].lrc_start)
					cur_pos = comp_array[i].lrc_start;
				else
					cur_pos = pos;

				if (data_off > comp_array[i].lrc_end)
					to_punch = comp_array[i].lrc_end -
						   cur_pos;
				else
					to_punch = data_off - cur_pos;

				if (comp_array[i].lrc_end == OBD_OBJECT_EOF)
					/* the last component can be truncated
					 * safely
					 */
					rc = llapi_mirror_truncate(fd, mid,
								   cur_pos);
				else if (to_punch)
					rc = llapi_mirror_punch(fd, mid,
							cur_pos, to_punch);
				/**
				 * hole at the end of file, so just truncate up
				 * to set size.
				 */
				if (!rc && data_off == data_end && !data_size)
					rc = llapi_mirror_truncate(fd,
								mid, data_end);
				/* if failed then read failed hole range */
				if (rc < 0) {
					rc = 0;
					pos = cur_pos;
					if (pos + to_punch == data_off)
						to_read = data_end - pos;
					else
						to_read = to_punch;
					goto do_read;
				}
			}
			pos = data_off;
		}
		if (pos == mirror_end)
			continue;
		to_read = data_end - pos;
do_read:
		if (!to_read)
			break;

		assert(data_end <= mirror_end);

		to_read = MIN(buflen, to_read);
		to_read = ((to_read - 1) | (page_size - 1)) + 1;
		bytes_read = llapi_mirror_read(fd, src, buf, to_read, pos);
		if (bytes_read == 0) {
			/* end of file */
			break;
		}
		if (bytes_read < 0) {
			rc = bytes_read;
			llapi_error(LLAPI_MSG_ERROR, rc,
				    "error reading bytes %ld-%ld of mirror %u",
				    pos, to_read, src);
			break;
		}
		total_bytes_read += bytes_read;

		/* round up to page align to make direct IO happy. */
		to_write = ((bytes_read - 1) | (page_size - 1)) + 1;

		for (i = 0; i < comp_size; i++) {
			ssize_t written;
			off_t pos2 = pos;
			size_t to_write2 = to_write;

			/* skip non-overlapped component */
			if (pos >= comp_array[i].lrc_end ||
			    pos + to_write <= comp_array[i].lrc_start)
				continue;

			if (pos < comp_array[i].lrc_start)
				pos2 = comp_array[i].lrc_start;

			to_write2 -= pos2 - pos;

			if ((pos + to_write) > comp_array[i].lrc_end)
				to_write2 -= pos + to_write -
					     comp_array[i].lrc_end;

			written = llapi_mirror_write(fd,
					comp_array[i].lrc_mirror_id,
					buf + pos2 - pos,
					to_write2, pos2);
			if (written < 0) {
				comp_array[i].lrc_synced = false;
				llapi_error(LLAPI_MSG_ERROR, written,
					    "component %u not synced",
					    comp_array[i].lrc_id);
				if (rc2 == 0)
					rc2 = (int)written;
				continue;
			}
			assert(written == to_write2);
			total_bytes_written += written;

			if (!bandwidth_bytes_sec && !stats_interval_sec)
				continue;

			clock_gettime(CLOCK_MONOTONIC, &now);
			llapi_bandwidth_throttle(&now, &start_time,
						 bandwidth_bytes_sec,
						 total_bytes_written);

			if (stats_interval_sec &&
			    total_bytes_written != write_estimation_bytes)
				llapi_stats_log(&now, &start_time,
					  &last_bw_print, stats_interval_sec,
					  total_bytes_read, total_bytes_written,
					  total_bytes_written,
					  write_estimation_bytes);
		}
		pos += bytes_read;
	}
out_free:
	(void)munlock(buf, buflen);
	free(buf);

	if (rc < 0) {
		/* fatal error happens */
		for (i = 0; i < comp_size; i++)
			comp_array[i].lrc_synced = false;
		return rc;
	}

	/* Output at least one log, regardless of stats_interval */
	if (stats_interval_sec) {
		clock_gettime(CLOCK_MONOTONIC, &now);
		llapi_stats_log(&now, &start_time,
				&last_bw_print, stats_interval_sec,
				total_bytes_read, total_bytes_written,
				write_estimation_bytes, write_estimation_bytes);
	}

	/**
	 * no fatal error happens, each lrc_synced tells whether the component
	 * has been resync successfully.
	 */
	for (i = 0; i < comp_size; i++) {
		struct llapi_resync_comp *comp = comp_array + i;

		if (!comp->lrc_synced)
			continue;
		if (pos < comp->lrc_start || pos >= comp->lrc_end)
			continue;

		if (pos < stbuf.st_size) {
			rc = llapi_mirror_punch(fd, comp->lrc_mirror_id, pos,
						comp->lrc_end - pos);
		} else {
			rc = llapi_mirror_truncate(fd, comp->lrc_mirror_id,
						   pos);
		}

		/* Ignore truncate error on encrypted file without the
		 * key if tried on LUSTRE_ENCRYPTION_UNIT_SIZE boundary.
		 */
		if (rc < 0 && (rc != -ENOKEY || pos & ~LUSTRE_ENCRYPTION_MASK))
			comp->lrc_synced = false;
	}

	/**
	 * returns the first error code for partially successful resync if
	 * possible.
	 */
	return rc2;
}

/* Total maximum number of data stripes and parities in a raid set */
#define MAX_STRIPE_POINTERS 512

/**
 * llapi_ec_compute_parities() - Compute and update parities for a single RAID
 * set
 * @fd: file descriptor to read data from
 * @layout: layout structure containing EC configuration
 * @data_pos: starting position in file to read data stripes
 * @num_data_stripes: number of data stripes (k)
 * @ec_pos: starting position for EC parity stripes
 * @num_ec_stripes: number of EC parity stripes (p)
 * @data_id: mirror id of the data component to read from
 * @stripe_size: size of each stripe in bytes
 * @end_pos: end position in file (exclusive)
 * @stripe_ptrs: array of pointers to stripe buffers (data + parity)
 * @encode_matrix: encoding matrix for erasure coding
 * @g_tbls: Galois field tables for EC computation
 *
 * This function reads data stripes from the file starting at @data_pos and
 * computes the corresponding parity stripes using erasure coding. For a single
 * data stripe (num_data_stripes == 1), it performs a simple copy. For multiple
 * data stripes, it generates a Cauchy matrix and uses Intel ISA-L library
 * functions to compute the parities.
 *
 * The function reads up to @stripe_size bytes per data stripe, stopping at
 * @end_pos. If the file size changes during the operation (detected by
 * llapi_mirror_read() returning 0 before reaching end_pos), the operation
 * fails with -EBUSY.
 *
 * Return:
 * * %0 on success
 * * %-EBUSY if file size changed during operation
 * * %negative error code from llapi_mirror_read() on read failure
 */
static int llapi_ec_compute_parities(int fd, struct llapi_layout *layout,
		uint64_t data_pos, int num_data_stripes,
		uint64_t ec_pos, int num_ec_stripes,
		int data_id, uint64_t stripe_size, uint64_t end_pos,
		uint8_t *stripe_ptrs[],
		uint8_t *encode_matrix, uint8_t *g_tbls)
{
	uint64_t bytes_left;
	int rc, i, k, p, m;

	k = num_data_stripes;
	p = num_ec_stripes;
	m = k + p;

	/*
	 * The buffer is reused across raid sets. Only zero the portions beyond
	 * end_pos because bytes before it are overwritten by pread(), including
	 * sparse holes that read back as zeroes.
	 */
	bytes_left = end_pos > data_pos ? end_pos - data_pos : 0;
	for (i = 0; i < k; i++) {
		if (bytes_left >= stripe_size) {
			bytes_left -= stripe_size;
			continue;
		}

		memset(stripe_ptrs[i] + bytes_left, 0,
		       stripe_size - bytes_left);
		bytes_left = 0;
	}

	for (i = 0; i < k; i++) {
		size_t to_read;
		ssize_t bytes_read;
		uint8_t *read_buf = stripe_ptrs[i];

		/* End of file or end of extent reached so no more data */
		if (data_pos >= end_pos)
			break;

		to_read = end_pos - data_pos;
		if (to_read > stripe_size)
			to_read = stripe_size;
		while (to_read) {
			bytes_read = llapi_mirror_read(fd, data_id, read_buf,
						       to_read, data_pos);
			/*
			 * We are careful to not read beyond eof so we can treat
			 * ==0 as an unrecoverable error.
			 * File size must have changed while we were resyncing.
			 */
			if (bytes_read == 0)
				bytes_read = -EBUSY;

			if (bytes_read < 0) {
				llapi_error(LLAPI_MSG_ERROR, bytes_read,
				      "could not read data to compute parities");
				rc = bytes_read;
				goto out;
			}
			read_buf += bytes_read;
			data_pos += bytes_read;
			to_read -= bytes_read;
		}
	}

	if (num_data_stripes == 1) {
		memcpy(stripe_ptrs[1], stripe_ptrs[0], stripe_size);
	} else {
		gf_gen_cauchy1_matrix(encode_matrix, m, k);

		ec_init_tables(k, p, &encode_matrix[k * k], g_tbls);
		ec_encode_data(stripe_size, k, p, g_tbls, &stripe_ptrs[0],
			       &stripe_ptrs[k]);
	}

	rc = 0;
 out:
	return rc;
}

static int
llapi_ec_write_parities(int fd, uint64_t stripe_size, int k, int p,
			uint64_t ec_pos, int ec_id, uint8_t *stripe_ptrs[],
			struct ec_parity_coverage *cov)
{
	int rc, i;
	int m = k + p;
	struct ec_parity_coverage *c;

	for (i = k; i < m; i++) {
		size_t to_write;
		ssize_t bytes_written;
		uint8_t *write_buf;

		write_buf = stripe_ptrs[i];
		for (c = cov; c; c = c->next) {
			uint64_t off = ec_pos + (i - k) * stripe_size + c->pos;

			to_write = c->len;
			bytes_written = llapi_mirror_write(fd,
							   mirror_id_of(ec_id),
							   &write_buf[c->pos],
				   to_write, off);
			if (bytes_written < 0) {
				llapi_error(LLAPI_MSG_ERROR, bytes_written,
					    "could not write ec parities");
				rc = bytes_written;
				goto out;
			}
			assert(bytes_written == to_write);
			llapi_printf(LLAPI_MSG_NORMAL,
				     "Wrote parity #%d range=0x%llx-0x%llx\n",
				     i - k,
				     (unsigned long long)off,
				     (unsigned long long)(off + c->len));
		}
	}

	rc = 0;
 out:
	return rc;
}

/**
 * llapi_ec_verify_parities() - Verify on-disk EC parities match the in-memory
 * parities computed from the data stripes
 * @fd: file descriptor of the file to verify
 * @stripe_size: size of a single stripe in bytes
 * @k: number of data stripes in the RAID set
 * @p: number of parity stripes in the RAID set
 * @ec_pos: byte offset within the EC mirror to start reading parities from
 * @ec_id: mirror id of the EC parity component
 * @stripe_ptrs: array of stripe buffers; stripe_ptrs[k..k+p-1] hold the
 *	expected (in-memory) parities to compare against
 *
 * Reads each on-disk parity stripe and compares it against the corresponding
 * computed parity in stripe_ptrs[k + i].
 *
 * Note, stripe_ptrs[0] is reused as the read buffer and its contents
 * are overwritten by this function. The caller must not rely on stripe_ptrs[0]
 * holding the original data stripe after this call returns.
 *
 * Return:
 * * %0 on success (all parities match)
 * * %-EINVAL on a short read or parity mismatch
 * * %negative error code from llapi_mirror_read() on read failure
 */
static int llapi_ec_verify_parities(int fd, uint64_t stripe_size, int k, int p,
				    uint64_t ec_pos, int ec_id,
				    uint8_t *stripe_ptrs[],
				    struct ec_parity_coverage *cov)
{
	struct ec_parity_coverage *c;
	int rc;
	int i;

	for (i = 0; i < p; i++) {
		uint8_t *read_buf = stripe_ptrs[0];

		for (c = cov; c; c = c->next) {
			uint64_t off = ec_pos + i * stripe_size + c->pos;
			size_t to_read = c->len;
			ssize_t bytes_read;

			bytes_read = llapi_mirror_read(fd, mirror_id_of(ec_id),
						       &read_buf[c->pos],
						       to_read, off);
			if (bytes_read < 0) {
				llapi_error(LLAPI_MSG_ERROR, bytes_read,
					    "could not read ec parities");
				rc = bytes_read;
				goto out;
			}
			/*
			 * Short read from parity component indicates the
			 * parity data was not written for this region.
			 */
			if (bytes_read != to_read) {
				rc = -EINVAL;
				llapi_error(
					LLAPI_MSG_ERROR, rc,
					"parity %d short read: got %zd expected %zu",
					i, bytes_read, to_read);
				goto out;
			}
			if (memcmp(&read_buf[c->pos],
				   &stripe_ptrs[k + i][c->pos],
				   c->len)) {
				rc = -EINVAL;
				llapi_error(LLAPI_MSG_ERROR, rc,
					    "parity mismatch at ec_pos=%llu",
					    (unsigned long long)off);
				goto out;
			}
			llapi_printf(
				LLAPI_MSG_NORMAL,
				"Verified parity #%d range=0x%llx-0x%llx\n", i,
				(unsigned long long)off,
				(unsigned long long)(off + c->len));
		}
	}

	rc = 0;
 out:
	return rc;
}

/**
 * Compute the parity coverage for a single SEEK_DATA region
 */
static int ec_compute_parity_coverage_single(int fd, uint64_t stripe_start_pos,
					     uint64_t data_pos, uint64_t len,
					     struct ec_parity_coverage **cov)
{
	size_t data_len;
	off_t data_off;
	struct ec_parity_coverage *c;

	data_off = llapi_data_seek(fd, data_pos, &data_len);
	if (data_off < 0) {
		llapi_error(LLAPI_MSG_ERROR, data_off,
			    "failed to SEEK_DATA");
		return data_off;
	}
	/* normalize data_off and data_pos to start of stripe */
	data_off -= stripe_start_pos;
	data_pos -= stripe_start_pos;

	if (data_len == 0 || data_off >= data_pos + len)
		return len;

	/*
	 * Clamp data_off+data_len so that it does not extend beyond
	 * data_pos+len
	 */
	if (data_off + data_len > data_pos + len)
		data_len = len - (data_off - data_pos);

	/* If this will be the first region in the list */
	if (*cov == NULL || data_off < (*cov)->pos) {
		c = malloc(sizeof(struct ec_parity_coverage));
		if (c == NULL)
			return -ENOMEM;
		c->next = *cov;
		c->pos = data_off;
		c->len = data_len;

		*cov = c;

		/* merge if the new head overlaps with the previous head */
		c = (*cov)->next;
		while (c &&
		    c->pos <= (*cov)->pos + (*cov)->len) {
			if (c->pos + c->len > (*cov)->pos + (*cov)->len)
				(*cov)->len = c->pos + c->len - (*cov)->pos;
			(*cov)->next = c->next;
			free(c);
			c = (*cov)->next;
		}

		return data_off - data_pos + data_len;
	}

	/* Skip forward until we find which cov to insert this range behind */
	while ((*cov)->next && data_off > (*cov)->next->pos)
		cov = &(*cov)->next;

	if (data_off <= (*cov)->pos + (*cov)->len) {
		/*
		 * This range starts inside or immediately after the current
		 * cov so we can just extend its length.
		 */
		(*cov)->len = data_off - (*cov)->pos + data_len;
	} else {
		/*
		 * This range starts beyond the end of the current cov.
		 * Create a new entry. If it overlaps with the next entry
		 * we can merge them below.
		 */
		c = malloc(sizeof(struct ec_parity_coverage));
		if (c == NULL)
			return -ENOMEM;
		c->next = (*cov)->next;
		c->pos = data_off;
		c->len = data_len;
		(*cov)->next = c;
		cov = &(*cov)->next;
	}

	/* merge if the new head overlaps with the previous head */
	c = (*cov)->next;
	while (c &&
	       c->pos <= (*cov)->pos + (*cov)->len) {
		if (c->pos + c->len > (*cov)->pos + (*cov)->len)
			(*cov)->len = c->pos + c->len - (*cov)->pos;
		(*cov)->next = c->next;
		free(c);
		c = (*cov)->next;
	}

	return data_off - data_pos + data_len;
}

/**
 * Compute the parity coverage for a single stripe
 */
static int
ec_compute_parity_coverage_stripe(int fd, uint64_t data_pos,
				  uint64_t stripe_size,
				  struct ec_parity_coverage **cov)
{
	int rc;
	uint64_t len = stripe_size;
	uint64_t stripe_start_pos = data_pos;

	/* If we already cover the whole stripe there is nothing more to do. */
	if (*cov && (*cov)->pos == 0 && (*cov)->len == stripe_size)
		return 0;

	while (len) {
		rc = ec_compute_parity_coverage_single(fd, stripe_start_pos,
				       data_pos, len, cov);
		if (rc <= 0)
			return rc;
		if (rc > len)
			return 0;
		data_pos += rc;
		len -= rc;
	}

	return 0;
}

/**
 * Compute the parity coverage for an entire raid set.
 *
 * The parity coverate is the set of ranges in the parity stripe that are
 * computed from actual file data in the corresponding data stripes.
 * This takes into account both EOF as well as holes in a sparse file.
 *
 * All the examples are for a file with --ec 2+1. I.e. Two data stripes
 * and one parity stripe.
 * Stripe size is 64kb.
 *
 * In the examples below we also assume byte level granularity
 * on the data and hole sections. This is for illustration purposes
 * but is unlikely in real world as the data regions reported from
 * SEEK_DATA/SEEK_HOLE will depend on the underlying filesystem of
 * the OST hosting the data stripes and will most often be
 * aligned to full 4kb blocks.
 *
 *
 * Example:
 * A File containing 6 bytes of data at offset 0. EOF is at offset 6.
 * +--------------------------+
 * | data |                   | Stripe #0, data in range 0-6
 * +--------------------------+
 *        ^ EOF at offset 6
 *
 * +--------------------------+
 * |                          | Stripe #1, no data.
 * +--------------------------+
 *
 * In this case the parity stripe will contain parity data in the range 0 - 6.
 * The rest of the parity stripe is undefined, a hole, as there is no data
 * for the range 6 - end_of_stripe
 *
 *
 * A File containing 6 bytes of data at offset 0. EOF is at offset 48.
 * +--------------------------+
 * | data | hole |            | Stripe #0, data in range 0-6
 * +--------------------------+
 *               ^ EOF at offset 48
 *
 * +--------------------------+
 * |                          | Stripe #1, no data.
 * +--------------------------+
 *
 * In this case the parity stripe will contain parity data in the range 0 - 6.
 * The rest of the parity stripe is undefined, a hole, as there is no data
 * for the range 6 - end_of_stripe
 * While EOF is at offset 48, there range 6 - 48 is a hole and contains no data
 * and thus do not contribute to partity data.
 *
 *
 * Example:
 * A File containing data in the ranged 0 - 6 and 70000 - 70006
 * +--------------------------+
 * | data |       hole        | Stripe #0, data in range 0-6
 * +--------------------------+
 * ^ start of stripe #0 at offset 0
 *
 * +--------------------------+
 * |         |data|           | Stripe #1, data in the range 70000 - 70006
 * +--------------------------+
 * ^              ^ EOF at offset 70006
 * | start of stripe #0 at offset 65536
 *
 * In this case the parity stripe will contain parity data in the two ranges
 * 0 - 6  due to the data in stripe #0 range 0 - 6
 * 4464 - 4470 due to the data in stripe #1, range 70000 - 70006
 *
 *
 * Example:
 * A File containing data in the ranged 10 - 2048 and 66536 - 70000
 * +--------------------------+
 * | | data |      hole       | Stripe #0, data in range 10 - 2048
 * +--------------------------+
 * ^ start of stripe #0 at offset 0
 *
 * +--------------------------+
 * |    |  data  |            | Stripe #1, data in the range 66536 - 70000
 * +--------------------------+
 * ^ start of stripe #1 at offset 66536 - 70000
 *
 * In this case the parity stripe will contain parity in a single range
 * that is the result of merging the two ranges of data in stripe #0 and #1
 * 10 - 4464  Due to the merger of the two ranges 10 - 2048 and 1024 - 4464
 *
 */
static int ec_compute_parity_coverage_raidset(int fd, uint64_t data_pos, int k,
					      uint64_t stripe_size,
					      struct ec_parity_coverage **cov)
{
	int i, rc;

	for (i = 0; i < k; i++) {
		rc = ec_compute_parity_coverage_stripe(fd, data_pos,
						       stripe_size, cov);
		if (rc)
			goto out_free;
		data_pos += stripe_size;
	}

	return 0;
 out_free:
	while (*cov) {
		struct ec_parity_coverage *tmp = (*cov)->next;

		free(*cov);
		*cov = tmp;
	}
	return rc;
}

static int
llapi_ec_resync_or_verify_raidset(int fd, struct llapi_layout *layout,
				  struct llapi_layout_comp *data_comp,
				  uint64_t data_pos, int k,
				  struct llapi_layout_comp *ec_comp,
				  uint64_t ec_pos, int p,
				  uint8_t *stripe_ptrs[],
				  uint8_t *encode_matrix,
				  uint8_t *g_tbls,
				  uint64_t end_pos, int is_verify)
{
	int rc = 0;
	size_t data_len;
	off_t data_off;
	struct ec_parity_coverage *cov = NULL;

	if (data_pos >= end_pos)
		goto out_free;

	data_off = llapi_data_seek(fd, data_pos, &data_len);
	if (data_off < 0) {
		rc = data_off;
		llapi_error(LLAPI_MSG_ERROR, rc,
			    "failed to SEEK_DATA");
		goto out_free;
	}

	if (data_off >= data_pos + k * data_comp->llc_stripe_size)
		goto out_free;

	if (!data_len)
		goto out_free;

	llapi_printf(LLAPI_MSG_NORMAL,
		     "Compute/verify raidset range=%llu-%llu\n",
		     (unsigned long long)data_pos,
		     (unsigned long long)(data_pos +
					  k * data_comp->llc_stripe_size));

	rc = ec_compute_parity_coverage_raidset(fd, data_pos, k,
		      data_comp->llc_stripe_size, &cov);
	if (rc)
		goto out_free;

	rc = llapi_ec_compute_parities(fd, layout,
				       data_pos, k,
				       ec_pos, p,
				       data_comp->llc_mirror_id,
				       data_comp->llc_stripe_size, end_pos,
				       stripe_ptrs, encode_matrix, g_tbls);
	if (rc)
		goto out_free;
	if (is_verify) {
		rc = llapi_ec_verify_parities(fd,
			      data_comp->llc_stripe_size, k, p, ec_pos,
					      ec_comp->llc_id, stripe_ptrs, cov);
		if (rc)
			goto out_free;
	} else {
		rc = llapi_ec_write_parities(fd,
			     data_comp->llc_stripe_size, k, p, ec_pos,
					     ec_comp->llc_id, stripe_ptrs, cov);
		if (rc)
			goto out_free;
	}

 out_free:
	while (cov) {
		struct ec_parity_coverage *tmp = cov->next;

		free(cov);
		cov = tmp;
	}
	return rc;
}

/**
 * llapi_ec_resync_or_verify_comp() - Resync EC parities for a single component
 * @fd: file descriptor of the file to resync
 * @layout: layout structure containing the file layout
 * @data_comp: data component to read from
 * @ec_comp: EC parity component to update
 * @is_verify: if true, compare the computed parities against the stored ones
 *             (verify) instead of writing them back (resync)
 *
 * This function resyncs the erasure coding parities for a single component
 * by splitting the stripe set into smaller RAID sets and computing parities
 * for each set. The data component's stripes are divided into RAID sets of
 * approximately ec_comp->llc_dstripe_count stripes each, then
 * llapi_ec_compute_parities() computes the parities for each RAID set, which
 * are then written back (resync) or, if @is_verify is set, compared against
 * the stored parities (verify).
 *
 * The function handles the splitting of stripes into two groups:
 * - c0 RAID sets with k stripes each
 * - c1 RAID sets with k-1 stripes each (c1 may be 0)
 *
 * Processing stops at the end of the data component extent or EOF, whichever
 * comes first. If the extent starts beyond EOF, the function returns success
 * without processing.
 *
 * Return:
 * * %0 on success
 * * %-errno from fstat() if file stat fails
 * * %-EINVAL if total stripes exceed MAX_STRIPE_POINTERS
 * * %-ENOMEM if memory allocation fails
 * * %negative error code from llapi_ec_compute_parities() or
 *   llapi_ec_write_parities() on failure
 */
static int
llapi_ec_resync_or_verify_comp(int fd, struct llapi_layout *layout,
			       struct llapi_layout_comp *data_comp,
			       struct llapi_layout_comp *ec_comp,
			       int is_verify)
{
	int rc = 0, i, k, p, m;
	struct stat stbuf;
	struct ec_split_comp sc;
	uint64_t data_pos, ec_pos, end_pos;
	uint64_t stripe_set_size, ec_size;
	uint64_t num_hole_stripe_sets;
	uint32_t ec_mirror_id = mirror_id_of(ec_comp->llc_id);
	uint8_t *buf = NULL;
	uint8_t *encode_matrix = NULL;
	uint8_t *g_tbls = NULL;
	uint8_t *stripe_ptrs[MAX_STRIPE_POINTERS];
	size_t data_len, ec_len;
	off_t data_off, ec_off;
	ssize_t page_size;

	rc = fstat(fd, &stbuf);
	if (rc < 0)
		return -errno;

	/* This extent is past the end of the file so we can just skip it */
	if (stbuf.st_size < data_comp->llc_extent.e_start)
		return 0;

	data_pos = data_comp->llc_extent.e_start;
	data_off = data_pos;
	ec_pos = ec_comp->llc_extent.e_start;

	/* We only use data until end of extent or eof */
	end_pos = stbuf.st_size;
	if (end_pos > data_comp->llc_extent.e_end)
		end_pos = data_comp->llc_extent.e_end;

	/*
	 * We have data_comp->llc_stripe_count number of stripes in the
	 * in the data comp we want to split this into raid sets of
	 * approximately ec_comp->llc_dstripe_count stripes each.
	 * Call ec_split_stripes and find a mapping into smaller raidsets.
	 */
	ec_split_stripes(data_comp->llc_stripe_count,
			 ec_comp->llc_dstripe_count, &sc);

	/*
	 * We have now split the total number of data stripes into
	 * c0 number of raidsets with k stripes each and
	 * c1 number of raidsets with k-1 stripes each.
	 * c1 may be 0.
	 * Compute and update the parities one raid set at a time.
	 */
	k = sc.esc_k0;
	p = ec_comp->llc_cstripe_count;
	m = k + p;
	if (m > MAX_STRIPE_POINTERS) {
		rc = -EINVAL;
		goto out_free;
	}
	page_size = sysconf(_SC_PAGESIZE);
	if (page_size < 0) {
		rc = -errno;
		goto out_free;
	}
	rc = posix_memalign((void **)&buf, page_size,
			    m * data_comp->llc_stripe_size);
	if (rc) {
		rc = -rc;
		goto out_free;
	}
	memset(buf, 0, m * data_comp->llc_stripe_size);

	for (i = 0; i < m; i++)
		stripe_ptrs[i] = &buf[i * data_comp->llc_stripe_size];

	encode_matrix = malloc((k + p) * k);
	if (encode_matrix == NULL) {
		rc = -ENOMEM;
		goto out_free;
	}

	g_tbls = malloc(k * p * 32);
	if (g_tbls == NULL) {
		rc = -ENOMEM;
		goto out_free;
	}

	if (data_pos >= end_pos) {
		rc = 0;
		goto out_free;
	}

	stripe_set_size = data_comp->llc_stripe_count *
			  data_comp->llc_stripe_size;
	ec_size = (uint64_t)(sc.esc_n0 + sc.esc_n1) *
		  ec_comp->llc_cstripe_count *
		  ec_comp->llc_stripe_size;
 one_more_stripeset:
	data_off = llapi_mirror_data_seek(fd, data_comp->llc_mirror_id,
					  data_pos, &data_len);
	if (data_off < 0) {
		rc = data_off;
		llapi_error(LLAPI_MSG_ERROR, rc, "failed to SEEK_DATA");
		goto out_free;
	}
	if (data_off == data_pos)
		goto stripeset_with_data;

	/*
	 * Punch or verify holes for whole stripe sets only. Anything not
	 * covered here is left to stripeset_with_data, e.g. partial stripe set.
	 *
	 * Floor division counts full stripe sets only. For data_len == 0, it
	 * means a hole extends to the @end_pos; otherwise @data_off marks
	 * the next data and we jump to stripeset_with_data if it is still
	 * inside this stripe set.
	 */
	data_off = min_t(uint64_t, (uint64_t)data_off, end_pos);
	if (data_len == 0) {
		if (end_pos < data_pos + stripe_set_size)
			goto stripeset_with_data;
		num_hole_stripe_sets = (end_pos - data_pos) / stripe_set_size;
	} else {
		if (data_off < data_pos + stripe_set_size)
			goto stripeset_with_data;
		num_hole_stripe_sets = (data_off - data_pos) / stripe_set_size;
	}
	if (is_verify) {
		/* skip over the same number of stripe sets as the data part */
		ec_off = llapi_mirror_data_seek(fd, ec_mirror_id, ec_pos,
						&ec_len);
		if (ec_off < 0) {
			rc = ec_off;
			llapi_error(LLAPI_MSG_ERROR, rc, "failed to SEEK_EC");
			goto out_free;
		}
		if (ec_off < ec_pos + num_hole_stripe_sets * ec_size) {
			rc = -EINVAL;
			llapi_error(LLAPI_MSG_ERROR, rc, "parity %d mismatch",
				    ec_mirror_id);
			goto out_free;
		}
	} else {
		/* punch on EC parity for the data part that is not written */
		size_t punch_len = num_hole_stripe_sets * ec_size;

		rc = llapi_mirror_punch(fd, ec_mirror_id, ec_pos, punch_len);
		if (rc < 0)
			goto out_free;
	}

	data_pos += num_hole_stripe_sets * stripe_set_size;
	ec_pos += num_hole_stripe_sets * ec_size;
	if (data_pos >= end_pos) {
		rc = 0;
		goto out_free;
	}

stripeset_with_data:
	for (i = 0, k = sc.esc_k0; i < sc.esc_n0 + sc.esc_n1; i++) {
		if (i == sc.esc_n0)
			k = sc.esc_k1;

		/* Skip raidsets that are covered by an initial hole */
		if (data_off > data_pos + k * data_comp->llc_stripe_size)
			goto skip;

		rc = llapi_ec_resync_or_verify_raidset(
			fd, layout, data_comp, data_pos, k, ec_comp, ec_pos, p,
			stripe_ptrs, encode_matrix, g_tbls, end_pos, is_verify);
		if (rc)
			goto out_free;

skip:
		data_pos += k * data_comp->llc_stripe_size;
		ec_pos += ec_comp->llc_cstripe_count * ec_comp->llc_stripe_size;
		if (data_pos >= end_pos)
			goto out_free;
	}
	if (data_pos < end_pos)
		goto one_more_stripeset;

 out_free:
	free(g_tbls);
	free(encode_matrix);
	free(buf);
	return rc;
}

/**
 * llapi_ec_check_comp_match() - Verify an EC parity component is compatible
 * with its data component
 * @data_comp: data component the parities are computed from
 * @ec_comp: EC parity component to validate against @data_comp
 *
 * The data and parity components must agree on stripe size and extent, and the
 * data component must have enough stripes to hold the requested parities,
 * otherwise the erasure coding would not fit in the extent.
 *
 * Return: 0 if the pair is consistent, -EINVAL otherwise
 */
static int llapi_ec_check_comp_match(struct llapi_layout_comp *data_comp,
				     struct llapi_layout_comp *ec_comp)
{
	struct ec_split_comp sc;

	/* data_comp and ec_comp must match in stripe size and region */
	if (data_comp->llc_stripe_size != ec_comp->llc_stripe_size) {
		llapi_error(LLAPI_MSG_ERROR, -EINVAL,
			    "data and ec stripe mismatch");
		return -EINVAL;
	}
	if ((data_comp->llc_extent.e_start != ec_comp->llc_extent.e_start) ||
	    (data_comp->llc_extent.e_end != ec_comp->llc_extent.e_end)) {
		llapi_error(LLAPI_MSG_ERROR, -EINVAL,
			    "data and ec range mismatch");
		return -EINVAL;
	}
	/*
	 * If there are more parities than there are data stripes then the
	 * resulting erasure coding will no longer fit in the extent.
	 */
	ec_split_stripes(data_comp->llc_stripe_count,
			 ec_comp->llc_dstripe_count, &sc);
	if (data_comp->llc_stripe_count <
	    (uint64_t)(sc.esc_n0 + sc.esc_n1) * ec_comp->llc_cstripe_count) {
		llapi_error(LLAPI_MSG_ERROR, -EINVAL, "too many parities");
		return -EINVAL;
	}

	return 0;
}

/*
 * Definitions:
 * Stripe set:
 *     These are the stripes defined for the data comp.
 *     The number of stripes in a stripe set is data_comp->llc_stripe_count.
 *     Stripe sets are repeated, one after the other, until the end of the
 *     comp.
 *     Each repetition of a stripe set is a row.
 *
 * Raid set:
 *     The stripe set is split into smaller groups over which ec parities
 *     are computed.
 *     This is the raid set.
 *     The raid set covers a specific range of each row.
 *     A raid set covers the same range for every row.
 *
 *     The stripe set is split into raidsets so that there will be
 *       c0 number of raidsets with k0 stripes each and
 *       c1 number of raidsets with k1 stripes each.
 *       k1 == k0 - 1
 *       c1 may be 0.
 *
 * Raid stride:
 *     For a raid set, this is the number of stripes from one row in a raid
 *     set until the next row for that raid set.
 *     The raid stride is the same for all raid sets in a comp.
 *     The raid stride is data_comp->llc_stripe_count.
 *
 * Parity stride:
 *     This is the number of stripes for a specific parity for one row
 *     stripes to the same parity for the next row.
 *     It is the same as the number of parities.
 *
 *
 * EC parity comps match to a single instance of a data mirror so that
 * we can guarantee that the set of OSTs used in the data mirror will not
 * overlap with the set of OSTs in the ec mirror.
 * Thus if you delete a data mirror the EC mirror will not longer be useful
 * and should be deleted as well.
 *
 * An EC comp must span the same region of the file as its associated data
 * comp and thus the llc_extent.e_start and llc_extent.e_end must match
 * between the two. Additionally the EC comp and its data comp must also have
 * the same stripe size.
 *
 * The number of stripes in the EC comp must be equal or less than the number
 * of stripes in the data comp, or else the parity data will not fit.
 * I.e. there would be more parity data than would fit in the range
 * llc_extent.e_start to llc_extent.e_start + llc_extent.e_end.
 * Thus for example we can not do 2+8 encoding as the parities would
 * take up 4 times more data than the actual data and the corresponding range
 * of the file.
 *
 * As a special case, if there is only a single data stripe then we just store
 * a copy of the data as the first (and only) parity instead of computing it
 * just as if it was a normal mirror component of a single stripe.
 * In this case there can only be a single "parity" stripe for the same reason
 * as above.
 *
 * The data stripe consists of llc_stripe_count number of stripes.
 * This might be a large number, much larger than what we want to compute the
 * erasure code data over, so we need to split it into smaller raid sets.
 *
 * Example: we have a stripe set 11 data stripes:
 *
 * +-----------------+  \  <- mirror offset: llc_extent.e_start
 * | Data stripe #0  |  |
 * +-----------------+  |
 * | Data stripe #1  |  |
 * +-----------------+  |
 * | Data stripe #2  |  |
 * +-----------------+  |
 * | Data stripe #3  |  |
 * +-----------------+  |
 * | Data stripe #4  |  |
 * +-----------------+  |  Row #0 of the stripe set.
 * | Data stripe #5  |  |
 * +-----------------+  |
 * | Data stripe #6  |  |
 * +-----------------+  |
 * | Data stripe #7  |  |
 * +-----------------+  |
 * | Data stripe #8  |  |
 * +-----------------+  |
 * | Data stripe #9  |  |
 * +-----------------+  |
 * | Data stripe #10 |  |
 * +-----------------+  /
 * ... Repeated until llc_extent.e_end.
 *
 * Assume we want to use 4+2 erasure coding.
 * I.e. 2 parities for raid sets of at most 4 stripes each.
 *
 * Each row is then split into smaller raid sets using the
 * function ec_split_stripes().
 * This splits into 2 x 4 stripes + 1 x 3 stripes, like this:
 * +-----------------+  \  <- mirror offset: llc_extent.e_start
 * | Data stripe #0  |  |
 * +-----------------+  |
 * | Data stripe #1  |  |
 * +-----------------+  | RAID set #0, row #0
 * | Data stripe #2  |  |
 * +-----------------+  |
 * | Data stripe #3  |  |
 * +-----------------+  /
 *
 * +-----------------+  \
 * | Data stripe #4  |  |
 * +-----------------+  |
 * | Data stripe #5  |  |
 * +-----------------+  | RAID set #1, row #0
 * | Data stripe #6  |  |
 * +-----------------+  |
 * | Data stripe #7  |  |
 * +-----------------+  /
 *
 * +-----------------+  \
 * | Data stripe #8  |  |
 * +-----------------+  |
 * | Data stripe #9  |  | RAID set #2, row #0
 * +-----------------+  |
 * | Data stripe #10 |  |
 * +-----------------+  /
 *
 * +-----------------+  \  <- mirror offset: llc_extent.e_start
 * | Data stripe #11 |  |     + the raid stride * stripe size
 * +-----------------+  |
 * | Data stripe #12 |  |
 * +-----------------+  | RAID set #0, row #1
 * | Data stripe #13 |  |
 * +-----------------+  |
 * | Data stripe #14 |  |
 * +-----------------+  /
 * ...
 *
 * For a given data stripe ds, the row it belongs to is:
 *   row = ds / data_comp->llc_stripe_count
 *
 * and which raid set rs it belongs to is given by:
 *
 *   _o = ds % data_comp->llc_stripe_count
 *   if (_o <= c0 * k0)
 *      rs = _o / k0
 *   else
 *      rs = c0 + (_o - c0 * k0) / k1
 *
 *
 * For each RAID set 2 parities will be computed and they will be laid out
 * sequentially in the ec comp as this:
 *
 * +---------------------------+ \  <- mirror offset: llc_extent.e_start
 * | Parity #0 for RAID set #0 | |
 * +---------------------------+ | Parity set #0, row #0
 * | Parity #1 for RAID set #0 | |
 * +---------------------------+ X
 * | Parity #0 for RAID set #1 | |
 * +---------------------------+ | Parity set #1, row #0
 * | Parity #1 for RAID set #1 | |
 * +---------------------------+ X
 * | Parity #0 for RAID set #2 | |
 * +---------------------------+ | Parity set #2, row #0
 * | Parity #1 for RAID set #2 | |
 * +---------------------------+ X  <- mirror offset: llc_extent.e_start
 * | Parity #0 for RAID set #0 | |     + parity_stride * stripe size
 * +---------------------------+ | Parity set #0, row #1
 * | Parity #1 for RAID set #0 | |
 * +---------------------------+ X
 * ...
 *
 * For a given row, raid set and parity, the parity will be stored in the
 * parity stripe:
 *
 * parity-stripe = row * parity-stride
 *                 + raid-set * number-of-parities
 *                 + parity
 *
 *
 * If there is a hole that spans the entire raidset then we can skip
 * computing the parities and leave it as a hole in the ec comp as well.
 * Example, assume there is a hole spanning the entire RAID set #1 above, this
 * will result in a parity layout as:
 * +---------------------------+      <- file offset: start of stripe-set
 * | Parity #0 for RAID set #0 |
 * +-----------------+---------+
 * | Parity #1 for RAID set #0 |
 * +---------------------------+
 * |           hole            |
 * +---------------------------+ No data in RAID set #1 so
 * |           hole            | no need to store the parities either.
 * +-----------------+---------+
 * | Parity #0 for RAID set #2 |
 * +-----------------+---------+
 * | Parity #1 for RAID set #2 |
 * +-----------------+---------+
 * |    hole until e_end       |
 * ...
 *
 * The extent offsets matches between the EC comp and the data comp
 * it protects.
 *
 * Computations are done on whole stripes at a time.
 * We compute the parities for a full raid set at a time.
 *
 * There is no guarantee that the raid set will be fully populated.
 * We could for example reach the end of the extent (llc_extent.e_end)
 * partially through the raid set, or we could reach EOF.
 * In both cases we pad the remainder of the raid set with 0 when we compute
 * the parity.
 *
 * Example: the extent ends partway through the second stripe in raidset #2:
 * The raid set is padded with 0 so that we have a full set of stripes to
 * compute the parities over.
 * +------------------------+  \
 * |     Data stripe #8     |  |
 * +------------------------+  |
 * | Data stripe #9 | 00000 |  | RAID set #2
 * +------------------------+  |
 * | 0000000000000000000000 |  |
 * +------------------------+  /
 *
 */
int llapi_ec_resync_many_params(int fd, struct llapi_layout *layout,
				struct llapi_resync_comp *comp_array,
				int comp_size,
				unsigned long stats_interval_sec,
				uint64_t bandwidth_bytes_sec)
{
	int rc, i;
	struct llapi_layout_comp *ec_comp, *data_comp;

	for (i = 0; i < comp_size; i++) {
		/* Find the stale ec comp */
		ec_comp = __llapi_layout_find_comp_by_id(layout,
				       comp_array[i].lrc_id);
		if (!ec_comp) {
			rc = -ENOENT;
			llapi_error(LLAPI_MSG_ERROR, rc,
			      "cannot find ec comp");
			goto out;
		}

		/* Find the data comp that matches the same region */
		data_comp = __llapi_layout_find_data_comp_by_parity(layout,
								    ec_comp);
		if (!data_comp) {
			rc = -ENOENT;
			llapi_error(LLAPI_MSG_ERROR, rc,
			      "cannot find data comp");
			goto out;
		}

		/* Skip resyncing parity if data component has NOSYNC flag */
		if (data_comp->llc_flags & LCME_FL_NOSYNC)
			continue;

		rc = llapi_ec_check_comp_match(data_comp, ec_comp);
		if (rc)
			goto out;

		rc = llapi_ec_resync_or_verify_comp(fd, layout, data_comp,
						    ec_comp, false);
		if (rc) {
			llapi_error(LLAPI_MSG_ERROR, rc,
			      "failed to sync ec comp");
			goto out;
		}
	}
	return 0;

out:
	return rc;
}

/**
 * llapi_ec_verify_comps() - Verify EC parity components against their data
 * @fd:          File descriptor of the mirrored file.
 * @layout:      Mirror component list.
 * @ecs:         Array of EC parity component ids to verify.
 * @ec_count:    Number of entries in @ecs.
 *
 * For each EC parity component id in @ecs, locate the matching data component
 * in @layout, recompute the parities from the data, and compare them against
 * the on-disk parities. Returns the first failure encountered; remaining
 * components in @ecs are not checked.
 *
 * Return: 0 on success, negative errno on failure.
 */
int llapi_ec_verify_comps(int fd, struct llapi_layout *layout, __u32 *ecs,
			  int ec_count)
{
	struct llapi_layout_comp *data_comp;
	struct llapi_layout_comp *ec_comp;
	int rc = 0;
	int i;

	for (i = 0; i < ec_count; i++) {
		ec_comp = __llapi_layout_find_comp_by_id(layout, ecs[i]);
		if (!ec_comp) {
			rc = -ENOENT;
			llapi_error(LLAPI_MSG_ERROR, rc,
				    "ec component is missing");
			goto out;
		}
		data_comp = __llapi_layout_find_data_comp_by_parity(layout,
								    ec_comp);
		if (!data_comp) {
			rc = -ENOENT;
			llapi_error(
				LLAPI_MSG_ERROR, rc,
				"ec component does not have a matching data component");
			goto out;
		}

		rc = llapi_ec_check_comp_match(data_comp, ec_comp);
		if (rc)
			goto out;

		rc = llapi_ec_resync_or_verify_comp(fd, layout, data_comp,
						    ec_comp, true);
		if (rc) {
			llapi_error(LLAPI_MSG_ERROR, rc,
				    "ec verify failed for comp 0x%08x",
				    ec_comp->llc_id);
			goto out;
		}
	}

out:
	return rc;
}

int llapi_mirror_resync_many(int fd, struct llapi_layout *layout,
			     struct llapi_resync_comp *comp_array,
			     int comp_size,  uint64_t start, uint64_t end)
{
	return llapi_mirror_resync_many_params(fd, layout, comp_array,
					       comp_size, start, end, 0, 0);
}

enum llapi_layout_comp_sanity_error {
	LSE_OK,
	LSE_INCOMPLETE_MIRROR,
	LSE_ADJACENT_EXTENSION,
	LSE_INIT_EXTENSION,
	LSE_FLAGS,
	LSE_DOM_EXTENSION,
	LSE_DOM_EXTENSION_FOLLOWING,
	LSE_DOM_FIRST,
	LSE_SET_COMP_START,
	LSE_NOT_ZERO_LENGTH_EXTENDABLE,
	LSE_END_NOT_GREATER,
	LSE_ZERO_LENGTH_NORMAL,
	LSE_NOT_ADJACENT_PREV,
	LSE_START_GT_END,
	LSE_ALIGN_END,
	LSE_ALIGN_EXT,
	LSE_FOREIGN_EXTENSION,
	LSE_MIRROR_COUNT_INVALID,
	LSE_MIRROR_COUNT_MISMATCH,
	LSE_EC_PARAM,
	LSE_EC_MATCH_DATA,
	LSE_EC_DUP,
	LSE_EC_OVERLAP,
	LSE_EC_MIXED_MIRROR,
	LSE_EC_DATA_COMP_UNSET_LINK_ID,
	LSE_EC_UNPROTECTED_DATA,
	LSE_LAST,
};

const char *const llapi_layout_strerror[] =
{
	[LSE_OK] = "",
	[LSE_INCOMPLETE_MIRROR] =
		"Incomplete mirror - must go to EOF",
	[LSE_ADJACENT_EXTENSION] =
		"No adjacent extension space components",
	[LSE_INIT_EXTENSION] =
		"Cannot apply extension flag to init components",
	[LSE_FLAGS] =
		"Wrong flags",
	[LSE_DOM_EXTENSION] =
		"DoM components can't be extension space",
	[LSE_DOM_EXTENSION_FOLLOWING] =
		"DoM components cannot be followed by extension space",
	[LSE_DOM_FIRST] =
		"DoM component should be the first one in a file/mirror",
	[LSE_SET_COMP_START] =
		"Must set previous component extent before adding next",
	[LSE_NOT_ZERO_LENGTH_EXTENDABLE] =
		"Extendable component must start out zero-length",
	[LSE_END_NOT_GREATER] =
		"Component end is before end of previous component",
	[LSE_ZERO_LENGTH_NORMAL] =
		"Zero length components must be followed by extension",
	[LSE_NOT_ADJACENT_PREV] =
		"Components not adjacent (end != next->start",
	[LSE_START_GT_END] =
		"Component start is > end",
	[LSE_ALIGN_END] =
		"The component end must be aligned by the stripe size",
	[LSE_ALIGN_EXT] =
		"The extension size must be aligned by the stripe size",
	[LSE_FOREIGN_EXTENSION] =
		"FOREIGN components can't be extension space",
	[LSE_MIRROR_COUNT_INVALID] =
		"Mirror count is invalid",
	[LSE_MIRROR_COUNT_MISMATCH] =
		"Mirror count doesn't match component structure",
	[LSE_EC_PARAM] =
		"EC component should have valid parameters",
	[LSE_EC_MATCH_DATA] =
		"EC component should have matching data component",
	[LSE_EC_DUP] =
		"Parity component should not protect multiple data components",
	[LSE_EC_OVERLAP] =
		"EC components should not overlap in the same mirror",
	[LSE_EC_MIXED_MIRROR] =
		"Mirror contains both PARITY and non-PARITY components",
	[LSE_EC_DATA_COMP_UNSET_LINK_ID] =
		"Data component should link to parity component",
	[LSE_EC_UNPROTECTED_DATA] =
		"All data components in the mirror must be protected by parity",
};

struct llapi_layout_sanity_args {
	bool lsa_incomplete;
	bool lsa_flr;
	bool lsa_ondisk;
	int lsa_rc;
	char *fsname;
	uint16_t lsa_mirror_count;
	/* Track parity state for mixed mirror detection */
	bool lsa_in_parity;
	uint64_t lsa_last_parity_end;
};

/* Inline function to verify the pool name */
static inline int verify_pool_name(char *fsname, struct llapi_layout *layout)
{
	struct llapi_layout_comp *comp;

	if (!fsname || fsname[0] == '\0')
		return 0;

	comp = __llapi_layout_cur_comp(layout);
	if (!comp)
		return 0;
	if (comp->llc_pool_name[0] == '\0' ||
	    lov_pool_is_ignored(comp->llc_pool_name))
		return 0;
	/* check if the pool name exist */
	if (llapi_search_ost(fsname, comp->llc_pool_name, NULL) < 0)
		return -1;
	return 0;
}

/*
 * When modified, adjust llapi_stripe_param_verify() if needed as well.
 */
static int llapi_layout_sanity_cb(struct llapi_layout *layout,
				  void *arg)
{
	struct llapi_layout_comp *comp, *next, *prev;
	struct llapi_layout_sanity_args *args = arg;
	bool first_comp = false;

	comp = __llapi_layout_cur_comp(layout);
	if (comp == NULL) {
		args->lsa_rc = -1;
		goto out_err;
	}

	if (verify_pool_name(args->fsname, layout) != 0) {
		args->lsa_rc = -1;
		goto out_err;
	}

	if (comp->llc_list.prev != &layout->llot_comp_list)
		prev = list_last_entry(&comp->llc_list, typeof(*prev),
				       llc_list);
	else
		prev = NULL;

	if (comp->llc_list.next != &layout->llot_comp_list)
		next = list_first_entry(&comp->llc_list, typeof(*next),
					llc_list);
	else
		next = NULL;

	/*
	 * Start of zero implies a new mirror.
	 * With parity-first allowed, any component at e_start == 0 marks
	 * the start of a new mirror, regardless of PARITY flag.
	 */
	if (comp->llc_extent.e_start == 0) {
		first_comp = true;
		args->lsa_mirror_count++;
		/* Most checks apply only within one mirror, this is an
		 * exception. */
		if (prev && prev->llc_extent.e_end != LUSTRE_EOF) {
			args->lsa_rc = LSE_INCOMPLETE_MIRROR;
			goto out_err;
		}

		/* Reset prev at mirror boundary */
		prev = NULL;
	}

	if (next && next->llc_extent.e_start == 0)
		next = NULL;

	/* Flag sanity checks */
	/* No adjacent extension components */
	if ((comp->llc_flags & LCME_FL_EXTENSION) && next &&
	    (next->llc_flags & LCME_FL_EXTENSION)) {
		args->lsa_rc = LSE_ADJACENT_EXTENSION;
		goto out_err;
	}

	/* Extension flag cannot be applied to init components and the first
	 * component of each mirror is automatically init */
	if ((comp->llc_flags & LCME_FL_EXTENSION) &&
	    (comp->llc_flags & LCME_FL_INIT || first_comp)) {
		args->lsa_rc = LSE_INIT_EXTENSION;
		goto out_err;
	}

	if (comp->llc_ondisk) {
		if (comp->llc_flags & LCME_FL_NEG)
			args->lsa_rc = LSE_FLAGS;
	} else if (!args->lsa_incomplete) {
		if (args->lsa_flr) {
			if (comp->llc_flags &
			    ~(LCME_USER_COMP_FLAGS | LCME_FL_IS_LINK_ID))
				args->lsa_rc = LSE_FLAGS;
		} else {
			if (comp->llc_flags &
			    ~(LCME_FL_EXTENSION | LCME_FL_PREF_RW |
			      LCME_FL_NOCOMPR | LCME_FL_PARITY |
			      LCME_FL_IS_LINK_ID)) {
				args->lsa_rc = LSE_FLAGS;
			}
		}
	}
	if (args->lsa_rc)
		goto out_err;

	/* DoM sanity checks */
	if (!(comp->llc_pattern & LLAPI_LAYOUT_INVALID) &&
	    (comp->llc_pattern & (LLAPI_LAYOUT_MDT | LOV_PATTERN_MDT))) {
		/* DoM components can't be extension components */
		if (comp->llc_flags & LCME_FL_EXTENSION) {
			args->lsa_rc = LSE_DOM_EXTENSION;
			goto out_err;
		}
		/* DoM components cannot be followed by an extension comp */
		if (next && (next->llc_flags & LCME_FL_EXTENSION)) {
			args->lsa_rc = LSE_DOM_EXTENSION_FOLLOWING;
			goto out_err;
		}

		/* DoM should be the first component in a mirror */
		if (!first_comp) {
			args->lsa_rc = LSE_DOM_FIRST;
			errno = EINVAL;
			goto out_err;
		}
	}

	if (comp->llc_pattern == LLAPI_LAYOUT_FOREIGN ||
	    comp->llc_pattern == LOV_PATTERN_FOREIGN) {
		/* FOREING/HSM components can't be extension components */
		if (comp->llc_flags & LCME_FL_EXTENSION) {
			args->lsa_rc = LSE_FOREIGN_EXTENSION;
			goto out_err;
		}
	}

	/* Extent sanity checks */
	/* Must set previous component extent before adding another */
	if (prev && prev->llc_extent.e_start == 0 &&
	    prev->llc_extent.e_end == 0) {
		args->lsa_rc = LSE_SET_COMP_START;
		goto out_err;
	}

	if (!args->lsa_incomplete) {
		/* Components followed by extension space (extendable
		 * components) must be zero length before initialization.
		 * (Except for first comp, which will be initialized on
		 * creation). */
		if (next && (next->llc_flags & LCME_FL_EXTENSION) &&
		    !first_comp && !(comp->llc_flags & LCME_FL_INIT) &&
		    comp->llc_extent.e_start != comp->llc_extent.e_end) {
			args->lsa_rc = LSE_NOT_ZERO_LENGTH_EXTENDABLE;
			goto out_err;
		}

		/* End must come after end of previous comp */
		if (prev && comp->llc_extent.e_end < prev->llc_extent.e_end) {
			args->lsa_rc = LSE_END_NOT_GREATER;
			goto out_err;
		}

		/* Components not followed by ext space must have length > 0. */
		if (comp->llc_extent.e_start == comp->llc_extent.e_end &&
		    (next == NULL || !(next->llc_flags & LCME_FL_EXTENSION))) {
			args->lsa_rc = LSE_ZERO_LENGTH_NORMAL;
			goto out_err;
		}

		/* The component end must be aligned by the stripe size */
		if ((comp->llc_flags & LCME_FL_EXTENSION) &&
		    (prev->llc_stripe_size != LLAPI_LAYOUT_DEFAULT)) {
			if (comp->llc_extent.e_end != LUSTRE_EOF &&
			    comp->llc_extent.e_end % prev->llc_stripe_size) {
				args->lsa_rc = LSE_ALIGN_END;
				goto out_err;
			}
			if ((comp->llc_stripe_size * SEL_UNIT_SIZE) %
			    prev->llc_stripe_size) {
				args->lsa_rc = LSE_ALIGN_EXT;
				goto out_err;
			}
		} else if (!(comp->llc_flags & LCME_FL_EXTENSION) &&
			   (comp->llc_stripe_size != LLAPI_LAYOUT_DEFAULT)) {
			if (comp->llc_extent.e_end != LUSTRE_EOF &&
			    comp->llc_extent.e_end !=
			    comp->llc_extent.e_start &&
			    comp->llc_extent.e_end % comp->llc_stripe_size) {
				args->lsa_rc = LSE_ALIGN_END;
				goto out_err;
			}
		}
	}

	/* Components must have start == prev->end */
	if (prev && comp->llc_extent.e_start != 0 &&
	    comp->llc_extent.e_start != prev->llc_extent.e_end) {
		args->lsa_rc = LSE_NOT_ADJACENT_PREV;
		goto out_err;
	}

	/* Components must have start <= end */
	if (comp->llc_extent.e_start > comp->llc_extent.e_end) {
		args->lsa_rc = LSE_START_GT_END;
		goto out_err;
	}

	/* Any component should not have LCME_FL_IS_LINK_ID flag and
	 * llc_ondisk flag set since the former flag is transient.
	 */
	if (comp->llc_ondisk && (comp->llc_flags & LCME_FL_IS_LINK_ID)) {
		args->lsa_rc = LSE_FLAGS;
		goto out_err;
	}

	/* EC parity component specific validation */
	if (comp->llc_flags & LCME_FL_PARITY) {
		struct llapi_layout_comp *data_comp = NULL;
		struct llapi_layout_comp *search_comp;
		bool is_link_id = comp->llc_flags & LCME_FL_IS_LINK_ID;

		/*
		 * Skip EC validation for components that are already on disk.
		 * They were validated when created, and we may not have all
		 * the necessary information (like stripe counts) when just
		 * modifying component flags.
		 */
		if (comp->llc_ondisk)
			goto skip_ec_validation;

		list_for_each_entry(search_comp, &layout->llot_comp_list,
				    llc_list) {
			__u16 search_comp_mirror_link_id =
				is_link_id ? search_comp->llc_mirror_link_id :
					     search_comp->llc_mirror_id;
			__u16 comp_mirror_link_id =
				is_link_id ? comp->llc_mirror_link_id :
					     comp->llc_mirror_id;

			/* Skip parity components */
			if (search_comp->llc_flags & LCME_FL_PARITY)
				continue;

			/* Comp should link to data comp to protect */
			if (comp->llc_mirror_link_id !=
			    search_comp_mirror_link_id)
				continue;

			/* Check if extents match */
			if (search_comp->llc_extent.e_start !=
				    comp->llc_extent.e_start ||
			    search_comp->llc_extent.e_end !=
				    comp->llc_extent.e_end)
				continue;

			/* Data comp and parity comp should have same flag */
			if ((search_comp->llc_flags & LCME_FL_IS_LINK_ID) !=
			    (comp->llc_flags & LCME_FL_IS_LINK_ID)) {
				args->lsa_rc = LSE_EC_PARAM;
				goto out_err;
			}

			/* Data comp should link to parity component */
			if (search_comp->llc_mirror_link_id !=
			    comp_mirror_link_id) {
				args->lsa_rc = LSE_EC_DATA_COMP_UNSET_LINK_ID;
				goto out_err;
			}

			/* Parity comp should not have multiple data comps */
			if (data_comp) {
				args->lsa_rc = LSE_EC_DUP;
				goto out_err;
			}
			data_comp = search_comp;
		}

		if (!data_comp) {
			args->lsa_rc = LSE_EC_MATCH_DATA;
			goto out_err;
		}

		/* EC parameter validation */
		if (layout_ec_verify_stripes(data_comp->llc_stripe_count,
					     comp->llc_dstripe_count,
					     comp->llc_cstripe_count)) {
			args->lsa_rc = LSE_EC_PARAM;
			goto out_err;
		}

		/*
		 * EC/parity components must have the same stripe size as
		 * their matching data components.
		 */
		if (comp->llc_stripe_size != data_comp->llc_stripe_size) {
			args->lsa_rc = LSE_EC_PARAM;
			goto out_err;
		}

		/*
		 * Check that the protected data mirror is complete, i.e., the
		 * extents covers [0, EOF], and all its data components are
		 * protected by parity.
		 */
		uint64_t min_data_start = LUSTRE_EOF;
		uint64_t max_data_end = 0;
		uint16_t data_mirror_id = data_comp->llc_mirror_id;

		list_for_each_entry(search_comp, &layout->llot_comp_list,
				    llc_list) {
			if (search_comp->llc_flags & LCME_FL_PARITY)
				continue;
			if (search_comp->llc_mirror_id != data_mirror_id)
				continue;

			/* Check extent coverage is complete */
			if (search_comp->llc_extent.e_start < min_data_start)
				min_data_start =
					search_comp->llc_extent.e_start;
			if (search_comp->llc_extent.e_end > max_data_end)
				max_data_end = search_comp->llc_extent.e_end;

			/* Every data component in the mirror must be protected
			 * by a parity component
			 */
			if (search_comp->llc_mirror_link_id ==
			    LLAPI_MIRROR_LINK_NONE) {
				args->lsa_rc = LSE_EC_UNPROTECTED_DATA;
				goto out_err;
			}
		}

		/* Data mirror must be complete: extents cover [0, EOF] */
		if (min_data_start != 0 || max_data_end != LUSTRE_EOF) {
			args->lsa_rc = LSE_EC_PARAM;
			goto out_err;
		}

skip_ec_validation:
		/* Continue with other validation checks */
		;
	}

	/*
	 * Detect mixed parity mirrors: once we see a parity component,
	 * all subsequent components in that mirror must be parity until
	 * we hit a new mirror boundary (e_start == 0).
	 */
	if (comp->llc_flags & LCME_FL_PARITY) {
		/* Entering or continuing parity mode */
		if (!args->lsa_in_parity) {
			/* First parity component in this mirror */
			if (comp->llc_extent.e_start != 0) {
				/* Parity can only start at mirror boundary */
				args->lsa_rc = LSE_EC_MIXED_MIRROR;
				goto out_err;
			}
			args->lsa_in_parity = true;
		}
		args->lsa_last_parity_end = comp->llc_extent.e_end;
	} else {
		/* Non-parity component */
		if (args->lsa_in_parity) {
			/*
			 * Switching from parity to non-parity within a
			 * mirror is not allowed. Non-parity after parity
			 * must start a new mirror (e_start == 0).
			 */
			if (comp->llc_extent.e_start != 0) {
				args->lsa_rc = LSE_EC_MIXED_MIRROR;
				goto out_err;
			}
			/* New mirror started, exit parity mode */
			args->lsa_in_parity = false;
		}
	}

	return LLAPI_LAYOUT_ITER_CONT;

out_err:
	errno = errno ? errno : EINVAL;
	return LLAPI_LAYOUT_ITER_STOP;
}

/* Print explanation of layout error */
void llapi_layout_sanity_perror(int error)
{
	if (error >= LSE_LAST || error < 0)
		llapi_err_noerrno(LLAPI_MSG_ERROR,
				  "Invalid layout, unrecognized error: %d\n",
				  error);
	else
		llapi_err_noerrno(LLAPI_MSG_ERROR, "Invalid layout: %s\n",
				  llapi_layout_strerror[error]);
}

/**
 * llapi_layout_sanity() - Enforce sanity checks
 * @layout: component layout list.
 * @incomplete: if layout is complete or not - some checks can only be done on
 * complete layouts.
 * @flr: set when this is called from FLR mirror create
 *
 * Walk a layout and enforce sanity checks that apply to > 1 component
 *
 * The core idea here is that of sanity checking individual tokens vs semantic
 * checking.
 * We cannot check everything at the individual component level ('token'),
 * instead we must check whether or not the full layout has a valid meaning.
 *
 * An example of a component level check is "is stripe size valid?".  That is
 * handled when setting stripe size.
 *
 * An example of a layout level check is "are the extents of these components
 * valid when adjacent to one another", or "can we set these flags on adjacent
 * components"?
 *
 * Return:
 * * %0 on success
 * * %negative on failure (see llapi_layout_sanity_perror)
 */

int llapi_layout_sanity(struct llapi_layout *layout, bool incomplete, bool flr)
{
	return llapi_layout_v2_sanity(layout, incomplete, flr, NULL);
}

/**
 * llapi_layout_v2_sanity() - Do pool name checking
 * @layout: component layout list.
 * @incomplete: if layout is complete or not - some checks can
 *              only be done on complete layouts.
 * @flr: set when this is called from FLR mirror create
 * @fsname: filesystem name is used to check pool name, if
 *          NULL no pool name check is performed
 *
 * This function has been introduced to do pool name checking
 * on top of llapi_layout_sanity, the file name passed in this
 * function is used later to verify if pool exist. The older version
 * of the sanity function is passing NULL for the filename
 *
 * Return:
 * * %0 on success
 * * %negative on failure (see llapi_layout_sanity_perror)
 */

int llapi_layout_v2_sanity(struct llapi_layout *layout,
			bool incomplete, bool flr, char *fsname)
{
	struct llapi_layout_sanity_args args = { 0 };
	struct llapi_layout_comp *curr;
	int rc = 0;

	if (!layout)
		return 0;

	curr = layout->llot_cur_comp;
	if (!curr)
		return 0;

	/* Set up args */
	args.lsa_rc = 0;
	args.lsa_flr = flr;
	args.lsa_incomplete = incomplete;
	args.fsname = fsname;
	args.lsa_mirror_count = 0; /* tracks actual mirror count for each cb */
	args.lsa_in_parity = false;
	args.lsa_last_parity_end = 0;

	/* When we modify an existing layout, this tells us if it's FLR */
	if (curr->llc_mirror_id > 0)
		args.lsa_flr = true;

	errno = 0;
	rc = llapi_layout_comp_iterate(layout,
				       llapi_layout_sanity_cb,
				       &args);
	if (errno == ENOENT)
		errno = 0;

	if (rc != LLAPI_LAYOUT_ITER_CONT)
		rc = args.lsa_rc;

	/* Verify mirror count is valid and matches component structure */
	if (rc == 0 && layout->llot_is_composite) {
		if (!llapi_layout_mirror_count_is_valid(args.lsa_mirror_count))
			rc = LSE_MIRROR_COUNT_INVALID;
		else if (args.lsa_mirror_count != layout->llot_mirror_count)
			rc = LSE_MIRROR_COUNT_MISMATCH;
	}

	layout->llot_cur_comp = curr;

	return rc;
}

int llapi_layout_dom_size(struct llapi_layout *layout, uint64_t *size)
{
	uint64_t pattern, start;
	int rc;

	if (!layout || !llapi_layout_is_composite(layout)) {
		*size = 0;
		return 0;
	}

	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
	if (rc)
		return -errno;

	rc = llapi_layout_pattern_get(layout, &pattern);
	if (rc)
		return -errno;

	if ((pattern & LLAPI_LAYOUT_INVALID) ||
	    !(pattern & (LOV_PATTERN_MDT | LLAPI_LAYOUT_MDT))) {
		*size = 0;
		return 0;
	}

	rc = llapi_layout_comp_extent_get(layout, &start, size);
	if (rc)
		return -errno;
	if (start)
		return -ERANGE;
	return 0;
}

int lov_comp_md_size(struct lov_comp_md_v1 *lcm)
{
	if (lcm->lcm_magic == LOV_MAGIC_V1 || lcm->lcm_magic == LOV_MAGIC_V3) {
		struct lov_user_md *lum = (void *)lcm;

		return lov_user_md_size(lum->lmm_stripe_count, lum->lmm_magic);
	}

	if (lcm->lcm_magic == LOV_MAGIC_FOREIGN) {
		struct lov_foreign_md *lfm = (void *)lcm;

		return lfm->lfm_length;
	}

	if (lcm->lcm_magic != LOV_MAGIC_COMP_V1)
		return -EOPNOTSUPP;

	return lcm->lcm_size;
}

int llapi_get_lum_file_fd(int dir_fd, const char *fname, __u64 *valid,
			  lstatx_t *statx, struct lov_user_md *lum,
			  size_t lumsize)
{
	struct lov_user_mds_data *lmd;
	char buf[65536 + offsetof(typeof(*lmd), lmd_lmm)];
	int parent_fd = -1;
	int rc;

	if (lum && lumsize < sizeof(*lum))
		return -EINVAL;

	/* If a file name is provided, it is relative to the parent directory */
	if (fname) {
		parent_fd = dir_fd;
		dir_fd = -1;
	}

	lmd = (struct lov_user_mds_data *)buf;
	rc = get_lmd_info_fd(fname, parent_fd, dir_fd, buf, sizeof(buf),
			     GET_LMD_INFO);
	if (rc)
		return rc;

	if (valid)
		*valid = lmd->lmd_flags;

	if (statx)
		memcpy(statx, &lmd->lmd_stx, sizeof(*statx));

	if (lum) {
		if (lmd->lmd_lmmsize > lumsize)
			return -EOVERFLOW;
		memcpy(lum, &lmd->lmd_lmm, lmd->lmd_lmmsize);
	}

	return 0;
}

int llapi_get_lum_dir_fd(int dir_fd, __u64 *valid, lstatx_t *statx,
			 struct lov_user_md *lum, size_t lumsize)
{
	return llapi_get_lum_file_fd(dir_fd, NULL, valid, statx, lum, lumsize);
}

int llapi_get_lum_file(const char *path, __u64 *valid, lstatx_t *statx,
		       struct lov_user_md *lum, size_t lumsize)
{
	char parent[PATH_MAX];
	const char *fname;
	const char *tmp;
	int offset;
	int dir_fd;
	int rc;

	tmp = strrchr(path, '/');
	if (!tmp) {
		strncpy(parent, ".", sizeof(parent) - 1);
		offset = -1;
	} else {
		strncpy(parent, path, tmp - path);
		offset = tmp - path - 1;
		parent[tmp - path] = 0;
	}

	fname = path;
	if (offset >= 0)
		fname += offset + 2;

	dir_fd = open(parent, O_RDONLY);
	if (dir_fd < 0) {
		rc = -errno;
		llapi_error(LLAPI_MSG_ERROR, rc, "cannot open '%s'", path);
		return rc;
	}

	rc = llapi_get_lum_file_fd(dir_fd, fname, valid, statx, lum, lumsize);
	close(dir_fd);
	return rc;
}

int llapi_get_lum_dir(const char *path, __u64 *valid, lstatx_t *statx,
		      struct lov_user_md *lum, size_t lumsize)
{
	int dir_fd;
	int rc;

	dir_fd = open(path, O_RDONLY);
	if (dir_fd < 0) {
		rc = -errno;
		llapi_error(LLAPI_MSG_ERROR, rc, "cannot open '%s'", path);
		return rc;
	}

	rc = llapi_get_lum_dir_fd(dir_fd, valid, statx, lum, lumsize);
	close(dir_fd);
	return rc;
}