Viewing: llapi_layout_test.c

// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2016, 2017, Intel Corporation.
 * Copyright (c) 2025, DataDirect Networks, Inc. All rights reserved.
 */
/*
 * These tests exercise the llapi_layout API which abstracts the layout
 * of a Lustre file behind an opaque data type.  They assume a Lustre
 * file system with at least 2 OSTs and a pool containing at least the
 * first 2 OSTs.  For example,
 *
 *  sudo lctl pool_new lustre.testpool
 *  sudo lctl pool_add lustre.testpool OST[0-1]
 *  gcc -Wall -g -Werror -o llapi_layout_test llapi_layout_test.c -llustreapi
 *  sudo ./llapi_layout_test
 */

#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <inttypes.h>
#include <limits.h>
#include <pwd.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include <sys/types.h>

#include <lustre/lustreapi.h>
#include <linux/lustre/lustre_user.h>
#include "llapi_test_utils.h"

static char *poolname = "testpool";
static int num_osts = 2;
static char lustre_dir[PATH_MAX - 5];	/* Lustre test directory */

#define IN_RANGE(value, low, high) ((value >= low) && (value <= high))

/* Layout sanity error codes - should match liblustreapi_layout.c */
#define LSE_NOT_ADJACENT_PREV 12

#define LAYOUT_ASSERTF(cond, rc, fmt, ...)				\
do {									\
	if (!(cond))							\
		llapi_layout_sanity_perror(rc);				\
	ASSERTF(cond, fmt, ## __VA_ARGS__);				\
} while (0)

static void usage(char *prog)
{
	printf("Usage: %s [-d lustre_dir] [-p pool_name] [-o num_osts] "
	       "[-s $n,$m,... (skip tests)] [-t $n,$m,... (run tests)]\n",
	       prog);
	exit(0);
}

#define T0FILE			"t0"
#define T0_STRIPE_COUNT		num_osts
#define T0_STRIPE_SIZE		1048576
#define T0_OST_OFFSET		(num_osts - 1)
#define T0_DESC		"Read/write layout attributes then create a file"
static void test0(void)
{
	int rc;
	int fd;
	uint64_t count;
	uint64_t size;
	struct llapi_layout *layout = llapi_layout_alloc();
	char path[PATH_MAX];
	char mypool[LOV_MAXPOOLNAME + 1] = { '\0' };

	ASSERTF(layout != NULL, "errno %d", errno);

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T0FILE);

	rc = unlink(path);
	ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);

	/* stripe count */
	rc = llapi_layout_stripe_count_set(layout, T0_STRIPE_COUNT);
	ASSERTF(rc == 0, "errno = %d", errno);
	rc = llapi_layout_stripe_count_get(layout, &count);
	ASSERTF(rc == 0 && count == T0_STRIPE_COUNT, "%"PRIu64" != %d", count,
		T0_STRIPE_COUNT);

	/* stripe size */
	rc = llapi_layout_stripe_size_set(layout, T0_STRIPE_SIZE);
	ASSERTF(rc == 0, "errno = %d", errno);
	rc = llapi_layout_stripe_size_get(layout, &size);
	ASSERTF(rc == 0 && size == T0_STRIPE_SIZE, "%"PRIu64" != %d", size,
		T0_STRIPE_SIZE);

	/* pool_name */
	rc = llapi_layout_pool_name_set(layout, poolname);
	ASSERTF(rc == 0, "errno = %d", errno);
	rc = llapi_layout_pool_name_get(layout, mypool, sizeof(mypool));
	ASSERTF(rc == 0, "errno = %d", errno);
	rc = strcmp(mypool, poolname);
	ASSERTF(rc == 0, "%s != %s", mypool, poolname);

	/* ost_index */
	rc = llapi_layout_ost_index_set(layout, 0, T0_OST_OFFSET);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* create */
	fd = llapi_layout_file_create(path, 0, 0660, layout);
	ASSERTF(fd >= 0, "path = %s, errno = %d", path, errno);
	rc = close(fd);
	ASSERTF(rc == 0, "errno = %d", errno);
	llapi_layout_free(layout);
}

static void __test1_helper(struct llapi_layout *layout)
{
	uint64_t ost0;
	uint64_t ost1;
	uint64_t size;
	uint64_t count;
	int rc;
	char mypool[LOV_MAXPOOLNAME + 1] = { '\0' };

	rc = llapi_layout_stripe_count_get(layout, &count);
	ASSERTF(count == T0_STRIPE_COUNT, "%"PRIu64" != %d", count,
		T0_STRIPE_COUNT);

	rc = llapi_layout_stripe_size_get(layout, &size);
	ASSERTF(size == T0_STRIPE_SIZE, "%"PRIu64" != %d", size,
		T0_STRIPE_SIZE);

	rc = llapi_layout_pool_name_get(layout, mypool, sizeof(mypool));
	ASSERTF(rc == 0, "errno = %d", errno);
	rc = strcmp(mypool, poolname);
	ASSERTF(rc == 0, "%s != %s", mypool, poolname);

	rc = llapi_layout_ost_index_get(layout, 0, &ost0);
	ASSERTF(rc == 0, "errno = %d", errno);
	rc = llapi_layout_ost_index_get(layout, 1, &ost1);
	ASSERTF(rc == 0, "errno = %d", errno);
	ASSERTF(ost0 == T0_OST_OFFSET, "%"PRIu64" != %d", ost0, T0_OST_OFFSET);
	ASSERTF(ost1 != ost0, "%"PRIu64" == %"PRIu64, ost0, ost1);
}

#define T1_DESC		"Read test0 file by path and verify attributes"
static void test1(void)
{
	char path[PATH_MAX];

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T0FILE);
	struct llapi_layout *layout = llapi_layout_get_by_path(path, 0);

	ASSERTF(layout != NULL, "errno = %d", errno);
	__test1_helper(layout);
	llapi_layout_free(layout);
}

#define T2_DESC		"Read test0 file by FD and verify attributes"
static void test2(void)
{
	int fd;
	int rc;
	char path[PATH_MAX];

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T0FILE);

	fd = open(path, O_RDONLY);
	ASSERTF(fd >= 0, "open(%s): errno = %d", path, errno);

	struct llapi_layout *layout = llapi_layout_get_by_fd(fd, 0);

	ASSERTF(layout != NULL, "errno = %d", errno);

	rc = close(fd);
	ASSERTF(rc == 0, "close(%s): errno = %d", path, errno);

	__test1_helper(layout);
	llapi_layout_free(layout);
}

#define T3_DESC		"Read test0 file by FID and verify attributes"
static void test3(void)
{
	int rc;
	struct llapi_layout *layout;
	struct lu_fid fid;
	char fidstr[4096];
	char path[PATH_MAX];

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T0FILE);

	rc = llapi_path2fid(path, &fid);
	ASSERTF(rc == 0, "rc = %d, errno = %d", rc, errno);
	snprintf(fidstr, sizeof(fidstr), "0x%"PRIx64":0x%x:0x%x",
		 (uint64_t)fid.f_seq, fid.f_oid, fid.f_ver);
	errno = 0;
	layout = llapi_layout_get_by_fid(path, &fid, 0);
	ASSERTF(layout != NULL, "fidstr = %s, errno = %d", fidstr, errno);

	__test1_helper(layout);
	llapi_layout_free(layout);
}

#define T4FILE			"t4"
#define T4_STRIPE_COUNT		2
#define T4_STRIPE_SIZE		2097152
#define T4_DESC		"Verify compatibility with 'lfs setstripe'"
static void test4(void)
{
	int rc;
	uint64_t ost0;
	uint64_t ost1;
	uint64_t count;
	uint64_t size;
	const char *lfs = getenv("LFS");
	char mypool[LOV_MAXPOOLNAME + 1] = { '\0' };
	char cmd[PATH_MAX + 128];
	char path[PATH_MAX];

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T4FILE);

	if (lfs == NULL)
		lfs = "/usr/bin/lfs";

	rc = unlink(path);
	ASSERTF(rc == 0 || errno == ENOENT, "errno = %d", errno);

	snprintf(cmd, sizeof(cmd), "%s setstripe %s %s -c %d -S %d %s", lfs,
		 strlen(poolname) > 0 ? "-p" : "", poolname, T4_STRIPE_COUNT,
		 T4_STRIPE_SIZE, path);
	rc = system(cmd);
	ASSERTF(rc == 0, "system(%s): exit status %d", cmd, WEXITSTATUS(rc));

	errno = 0;
	struct llapi_layout *layout = llapi_layout_get_by_path(path, 0);

	ASSERTF(layout != NULL, "errno = %d", errno);

	rc = llapi_layout_stripe_count_get(layout, &count);
	ASSERTF(count == T4_STRIPE_COUNT, "%"PRIu64" != %d", count,
		T4_STRIPE_COUNT);

	rc = llapi_layout_stripe_size_get(layout, &size);
	ASSERTF(size == T4_STRIPE_SIZE, "%"PRIu64" != %d", size,
		T4_STRIPE_SIZE);

	rc = llapi_layout_pool_name_get(layout, mypool, sizeof(mypool));
	ASSERTF(rc == 0, "errno = %d", errno);
	rc = strcmp(mypool, poolname);
	ASSERTF(rc == 0, "%s != %s", mypool, poolname);

	rc = llapi_layout_ost_index_get(layout, 0, &ost0);
	ASSERTF(rc == 0, "errno = %d", errno);
	rc = llapi_layout_ost_index_get(layout, 1, &ost1);
	ASSERTF(rc == 0, "errno = %d", errno);
	ASSERTF(ost1 != ost0, "%"PRIu64" == %"PRIu64, ost0, ost1);

	llapi_layout_free(layout);
}

#define T5FILE		"t5"
#define T5_DESC		"llapi_layout_get_by_path ENOENT handling"
static void test5(void)
{
	int rc;
	char path[PATH_MAX];
	struct llapi_layout *layout;

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T5FILE);

	rc = unlink(path);
	ASSERTF(rc == 0 || errno == ENOENT, "errno = %d", errno);

	errno = 0;
	layout = llapi_layout_get_by_path(path, 0);
	ASSERTF(layout == NULL && errno == ENOENT, "errno = %d", errno);
}

#define T6_DESC		"llapi_layout_get_by_fd EBADF handling"
static void test6(void)
{
	errno = 0;
	struct llapi_layout *layout = llapi_layout_get_by_fd(9999, 0);

	ASSERTF(layout == NULL && errno == EBADF, "errno = %d", errno);
}

#define T7FILE		"t7"
#define T7_DESC		"llapi_layout_get_by_path EACCES handling"
static void test7(void)
{
	int fd;
	int rc;
	uid_t myuid = getuid();
	char path[PATH_MAX];
	const char *runas = getenv("RUNAS_ID");
	struct passwd *pw;
	uid_t uid;

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T7FILE);
	ASSERTF(myuid == 0, "myuid = %d", myuid); /* Need root for this test. */

	/* Create file as root */
	rc = unlink(path);
	ASSERTF(rc == 0 || errno == ENOENT, "errno = %d", errno);

	fd = open(path, O_CREAT, 0400);
	ASSERTF(fd > 0, "errno = %d", errno);
	rc = close(fd);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Become unprivileged user */
	if (runas != NULL) {
		uid = atoi(runas);
		ASSERTF(uid != 0, "runas = %s", runas);
	} else {
		pw = getpwnam("nobody");
		ASSERTF(pw != NULL, "errno = %d", errno);
		uid = pw->pw_uid;
	}
	rc = seteuid(uid);
	ASSERTF(rc == 0, "errno = %d", errno);
	errno = 0;
	struct llapi_layout *layout = llapi_layout_get_by_path(path, 0);

	ASSERTF(layout == NULL && errno == EACCES, "errno = %d", errno);
	rc = seteuid(myuid);
	ASSERTF(rc == 0, "errno = %d", errno);
}

/* llapi_layout_get_by_path() returns default layout for file with no
 * striping attributes.
 */
#define T8FILE		"t8"
#define T8_DESC		"llapi_layout_get_by_path ENODATA handling"
static void test8(void)
{
	int fd;
	int rc;
	struct llapi_layout *layout;
	uint64_t count;
	uint64_t size;
	uint64_t pattern;
	char path[PATH_MAX];

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T8FILE);

	rc = unlink(path);
	ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);
	fd = open(path, O_CREAT, 0640);
	ASSERTF(fd >= 0, "errno = %d", errno);
	rc = close(fd);
	ASSERTF(rc == 0, "errno = %d", errno);

	layout = llapi_layout_get_by_path(path, 0);
	ASSERTF(layout != NULL, "errno = %d\n", errno);

	rc = llapi_layout_stripe_count_get(layout, &count);
	ASSERTF(rc == 0, "errno = %d\n", errno);
	ASSERTF(count == LLAPI_LAYOUT_DEFAULT, "count = %"PRIu64"\n", count);

	rc = llapi_layout_stripe_size_get(layout, &size);
	ASSERTF(rc == 0, "errno = %d\n", errno);
	ASSERTF(size == LLAPI_LAYOUT_DEFAULT, "size = %"PRIu64"\n", size);

	rc = llapi_layout_pattern_get(layout, &pattern);
	ASSERTF(rc == 0, "errno = %d\n", errno);
	ASSERTF(pattern == LLAPI_LAYOUT_DEFAULT, "pattern = %"PRIu64"\n",
		pattern);

	llapi_layout_free(layout);
}

/* Verify llapi_layout_patter_set() return values for various inputs. */
#define T9_DESC		"verify llapi_layout_pattern_set() return values"
static void test9(void)
{
	struct llapi_layout *layout;
	int rc;

	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d\n", errno);

	errno = 0;
	rc = llapi_layout_pattern_set(layout, LLAPI_LAYOUT_INVALID);
	ASSERTF(rc == -1 && errno == EOPNOTSUPP, "rc = %d, errno = %d", rc,
		errno);

	errno = 0;
	rc = llapi_layout_pattern_set(NULL, LLAPI_LAYOUT_DEFAULT);
	ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc,
		errno);

	errno = 0;
	rc = llapi_layout_pattern_set(layout, LLAPI_LAYOUT_DEFAULT);
	ASSERTF(rc == 0, "rc = %d, errno = %d", rc, errno);

	errno = 0;
	rc = llapi_layout_pattern_set(layout, LLAPI_LAYOUT_RAID0);
	ASSERTF(rc == 0, "rc = %d, errno = %d", rc, errno);

	llapi_layout_free(layout);
}

/* Verify stripe_count interfaces return errors as expected */
#define T10_DESC	"stripe_count error handling"
static void test10(void)
{
	int rc;
	uint64_t count;
	struct llapi_layout *layout;

	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* invalid stripe count */
	errno = 0;
	rc = llapi_layout_stripe_count_set(layout, LLAPI_LAYOUT_INVALID);
	ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);

	errno = 0;
	rc = llapi_layout_stripe_count_set(layout, -1);
	ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);

	/* NULL layout */
	errno = 0;
	rc = llapi_layout_stripe_count_set(NULL, 2);
	ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);

	/* NULL layout */
	errno = 0;
	rc = llapi_layout_stripe_count_get(NULL, &count);
	ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);

	/* NULL count */
	errno = 0;
	rc = llapi_layout_stripe_count_get(layout, NULL);
	ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);

	/* stripe count too large */
	errno = 0;
	rc = llapi_layout_stripe_count_set(layout, LOV_MAX_STRIPE_COUNT + 1);
	ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);
	llapi_layout_free(layout);
}

/* Verify stripe_size interfaces return errors as expected */
#define T11_DESC	"stripe_size error handling"
static void test11(void)
{
	int rc;
	uint64_t size;
	struct llapi_layout *layout;

	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* negative stripe size */
	errno = 0;
	rc = llapi_layout_stripe_size_set(layout, -1);
	ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);

	/* invalid stripe size */
	errno = 0;
	rc = llapi_layout_stripe_size_set(layout, LLAPI_LAYOUT_INVALID);
	ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);

	/* stripe size too big */
	errno = 0;
	rc = llapi_layout_stripe_size_set(layout, (1ULL << 33));
	ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);

	/* NULL layout */
	errno = 0;
	rc = llapi_layout_stripe_size_set(NULL, 1048576);
	ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);

	errno = 0;
	rc = llapi_layout_stripe_size_get(NULL, &size);
	ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);

	/* NULL size */
	errno = 0;
	rc = llapi_layout_stripe_size_get(layout, NULL);
	ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);

	llapi_layout_free(layout);
}

/* Verify pool_name interfaces return errors as expected */
#define T12_DESC	"pool_name error handling"
static void test12(void)
{
	int rc;
	struct llapi_layout *layout;
	char mypool[LOV_MAXPOOLNAME + 1] = { '\0' };

	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* NULL layout */
	errno = 0;
	rc = llapi_layout_pool_name_set(NULL, "foo");
	ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);

	/* NULL pool name */
	errno = 0;
	rc = llapi_layout_pool_name_set(layout, NULL);
	ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);

	/* NULL layout */
	errno = 0;
	rc = llapi_layout_pool_name_get(NULL, mypool, sizeof(mypool));
	ASSERTF(errno == EINVAL, "poolname = %s, errno = %d", poolname, errno);

	/* NULL buffer */
	errno = 0;
	rc = llapi_layout_pool_name_get(layout, NULL, sizeof(mypool));
	ASSERTF(errno == EINVAL, "poolname = %s, errno = %d", poolname, errno);

	/* Pool name too long*/
	errno = 0;
	rc = llapi_layout_pool_name_set(layout, "0123456789abcdef");
	ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);

	llapi_layout_free(layout);
}

/* Verify ost_index interface returns errors as expected */
#define T13FILE			"t13"
#define T13_STRIPE_COUNT	2
#define T13_DESC		"ost_index error handling"
static void test13(void)
{
	int rc;
	int fd;
	uint64_t idx;
	struct llapi_layout *layout;
	char path[PATH_MAX];

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T13FILE);

	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* invalid OST index */
	errno = 0;
	rc = llapi_layout_ost_index_set(layout, 0, LLAPI_LAYOUT_INVALID);
	ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);

	errno = 0;
	rc = llapi_layout_ost_index_set(layout, 0, -1);
	ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);

	/* NULL layout */
	errno = 0;
	rc = llapi_layout_ost_index_set(NULL, 0, 1);
	ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);

	errno = 0;
	rc = llapi_layout_ost_index_get(NULL, 0, &idx);
	ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);

	/* NULL index */
	errno = 0;
	rc = llapi_layout_ost_index_get(layout, 0, NULL);
	ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);

	/* Layout not read from file so has no OST data. */
	errno = 0;
	rc = llapi_layout_stripe_count_set(layout, T13_STRIPE_COUNT);
	ASSERTF(rc == 0, "errno = %d", errno);
	rc = llapi_layout_ost_index_get(layout, 0, &idx);
	ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);

	/* n greater than stripe count*/
	rc = unlink(path);
	ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);
	rc = llapi_layout_stripe_count_set(layout, T13_STRIPE_COUNT);
	ASSERTF(rc == 0, "errno = %d", errno);
	fd = llapi_layout_file_create(path, 0, 0644, layout);
	ASSERTF(fd >= 0, "errno = %d", errno);
	rc = close(fd);
	ASSERTF(rc == 0, "errno = %d", errno);
	llapi_layout_free(layout);

	layout = llapi_layout_get_by_path(path, 0);
	ASSERTF(layout != NULL, "errno = %d", errno);
	errno = 0;
	rc = llapi_layout_ost_index_get(layout, T13_STRIPE_COUNT + 1, &idx);
	ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);

	llapi_layout_free(layout);
}

/* Verify llapi_layout_file_create() returns errors as expected */
#define T14_DESC	"llapi_layout_file_create error handling"
static void test14(void)
{
	int rc;
	struct llapi_layout *layout = llapi_layout_alloc();

	/* NULL path */
	errno = 0;
	rc = llapi_layout_file_create(NULL, 0, 0, layout);
	ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d", rc, errno);

	llapi_layout_free(layout);
}

/* Can't change striping attributes of existing file. */
#define T15FILE			"t15"
#define T15_STRIPE_COUNT	2
#define T15_DESC	"Can't change striping attributes of existing file"
static void test15(void)
{
	int rc;
	int fd;
	uint64_t count;
	struct llapi_layout *layout;
	char path[PATH_MAX];

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T15FILE);

	rc = unlink(path);
	ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);

	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);
	rc = llapi_layout_stripe_count_set(layout, T15_STRIPE_COUNT);
	ASSERTF(rc == 0, "errno = %d", errno);

	errno = 0;
	fd = llapi_layout_file_create(path, 0, 0640, layout);
	ASSERTF(fd >= 0, "fd = %d, errno = %d", fd, errno);
	rc = close(fd);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, T15_STRIPE_COUNT - 1);
	errno = 0;
	fd = llapi_layout_file_open(path, 0, 0640, layout);
	ASSERTF(fd >= 0, "fd = %d, errno = %d", fd, errno);
	rc = close(fd);
	ASSERTF(rc == 0, "errno = %d", errno);
	llapi_layout_free(layout);

	layout = llapi_layout_get_by_path(path, 0);
	ASSERTF(layout != NULL, "errno = %d", errno);
	rc = llapi_layout_stripe_count_get(layout, &count);
	ASSERTF(rc == 0 && count == T15_STRIPE_COUNT,
		"rc = %d, %"PRIu64" != %d", rc, count, T15_STRIPE_COUNT);
	llapi_layout_free(layout);
}

/* Default stripe attributes are applied as expected. */
#define T16FILE		"t16"
#define T16_DESC	"Default stripe attributes are applied as expected"
static void test16(void)
{
	int		rc;
	int		fd;
	struct llapi_layout	*deflayout;
	struct llapi_layout	*filelayout;
	char		path[PATH_MAX];
	uint64_t	fsize;
	uint64_t	fcount;
	uint64_t	dsize;
	uint64_t	dcount;

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T16FILE);

	rc = unlink(path);
	ASSERTF(rc == 0 || errno == ENOENT, "errno = %d", errno);

	deflayout = llapi_layout_get_by_path(lustre_dir, LAYOUT_GET_EXPECTED);
	ASSERTF(deflayout != NULL, "errno = %d", errno);
	rc = llapi_layout_stripe_size_get(deflayout, &dsize);
	ASSERTF(rc == 0, "errno = %d", errno);
	rc = llapi_layout_stripe_count_get(deflayout, &dcount);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* First, with a default struct llapi_layout */
	filelayout = llapi_layout_alloc();
	ASSERTF(filelayout != NULL, "errno = %d", errno);

	fd = llapi_layout_file_create(path, 0, 0640, filelayout);
	ASSERTF(fd >= 0, "errno = %d", errno);

	rc = close(fd);
	ASSERTF(rc == 0, "errno = %d", errno);

	llapi_layout_free(filelayout);

	filelayout = llapi_layout_get_by_path(path, 0);
	ASSERTF(filelayout != NULL, "errno = %d", errno);

	rc = llapi_layout_stripe_count_get(filelayout, &fcount);
	ASSERTF(rc == 0, "errno = %d", errno);
	ASSERTF(fcount == dcount || dcount == LLAPI_LAYOUT_DEFAULT ||
		IN_RANGE(dcount, LLAPI_LAYOUT_WIDE_MIN, LLAPI_LAYOUT_WIDE_MAX),
		"%"PRIu64" != %"PRIu64, fcount, dcount);

	rc = llapi_layout_stripe_size_get(filelayout, &fsize);
	ASSERTF(rc == 0, "errno = %d", errno);
	ASSERTF(fsize == dsize, "%"PRIu64" != %"PRIu64, fsize, dsize);

	/* NULL layout also implies default layout */
	rc = unlink(path);
	ASSERTF(rc == 0 || errno == ENOENT, "errno = %d", errno);

	fd = llapi_layout_file_create(path, 0, 0640, filelayout);
	ASSERTF(fd >= 0, "errno = %d", errno);
	rc = close(fd);
	ASSERTF(rc == 0, "errno = %d", errno);
	filelayout = llapi_layout_get_by_path(path, 0);
	ASSERTF(filelayout != NULL, "errno = %d", errno);

	rc = llapi_layout_stripe_count_get(filelayout, &fcount);
	ASSERTF(rc == 0, "errno = %d", errno);
	rc = llapi_layout_stripe_size_get(filelayout, &fsize);
	ASSERTF(rc == 0, "errno = %d", errno);
	ASSERTF(fcount == dcount || dcount == LLAPI_LAYOUT_DEFAULT ||
		IN_RANGE(dcount, LLAPI_LAYOUT_WIDE_MIN, LLAPI_LAYOUT_WIDE_MAX),
		"%"PRIu64" != %"PRIu64, fcount, dcount);
	ASSERTF(fsize == dsize, "%"PRIu64" != %"PRIu64, fsize, dsize);

	llapi_layout_free(filelayout);
	llapi_layout_free(deflayout);
}

/* Setting stripe count to LLAPI_LAYOUT_WIDE uses all available OSTs. */
#define T17FILE		"t17"
#define T17_DESC	"LLAPI_LAYOUT_WIDE is honored"
static void test17(void)
{
	int rc;
	int fd;
	int osts_all;
	uint64_t osts_layout;
	struct llapi_layout *layout;
	char path[PATH_MAX];

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T17FILE);

	rc = unlink(path);
	ASSERTF(rc == 0 || errno == ENOENT, "errno = %d", errno);
	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);
	rc = llapi_layout_stripe_count_set(layout, LLAPI_LAYOUT_WIDE);
	ASSERTF(rc == 0, "errno = %d", errno);
	fd = llapi_layout_file_create(path, 0, 0640, layout);
	ASSERTF(fd >= 0, "errno = %d", errno);
	rc = close(fd);
	ASSERTF(rc == 0, "errno = %d", errno);
	llapi_layout_free(layout);

	/* Get number of available OSTs */
	fd = open(path, O_RDONLY);
	ASSERTF(fd >= 0, "errno = %d", errno);
	rc = llapi_lov_get_uuids(fd, NULL, &osts_all);
	ASSERTF(rc == 0, "rc = %d, errno = %d", rc, errno);
	rc = close(fd);
	ASSERTF(rc == 0, "errno = %d", errno);

	layout = llapi_layout_get_by_path(path, 0);
	ASSERTF(layout != NULL, "errno = %d", errno);
	rc = llapi_layout_stripe_count_get(layout, &osts_layout);
	ASSERTF(osts_layout == osts_all, "%"PRIu64" != %d", osts_layout,
		osts_all);

	llapi_layout_free(layout);
}

/* Setting pool with "fsname.pool" notation. */
#define T18FILE		"t18"
#define T18_DESC	"Setting pool with fsname.pool notation"
static void test18(void)
{
	int rc;
	int fd;
	struct llapi_layout *layout = llapi_layout_alloc();
	char path[PATH_MAX];
	char pool[LOV_MAXPOOLNAME*2 + 1];
	char mypool[LOV_MAXPOOLNAME + 1] = { '\0' };

	snprintf(pool, sizeof(pool), "lustre.%s", poolname);

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T18FILE);

	ASSERTF(layout != NULL, "errno = %d", errno);

	rc = unlink(path);
	ASSERTF(rc == 0 || errno == ENOENT, "errno = %d", errno);

	rc = llapi_layout_pool_name_set(layout, pool);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_pool_name_get(layout, mypool, sizeof(mypool));
	ASSERTF(rc == 0, "errno = %d", errno);
	rc = strcmp(mypool, poolname);
	ASSERTF(rc == 0, "%s != %s", mypool, poolname);
	fd = llapi_layout_file_create(path, 0, 0640, layout);
	ASSERTF(fd >= 0, "errno = %d", errno);
	rc = close(fd);
	ASSERTF(rc == 0, "errno = %d", errno);

	llapi_layout_free(layout);

	layout = llapi_layout_get_by_path(path, 0);
	ASSERTF(layout != NULL, "errno = %d", errno);
	rc = llapi_layout_pool_name_get(layout, mypool, sizeof(mypool));
	ASSERTF(rc == 0, "errno = %d", errno);
	rc = strcmp(mypool, poolname);
	ASSERTF(rc == 0, "%s != %s", mypool, poolname);
	llapi_layout_free(layout);
}

#define T19_DESC	"Maximum length pool name is NULL-terminated"
static void test19(void)
{
	struct llapi_layout *layout;
	char *name = "0123456789abcde";
	char mypool[LOV_MAXPOOLNAME + 1] = { '\0' };
	int rc;

	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);
	rc = llapi_layout_pool_name_set(layout, name);
	ASSERTF(rc == 0, "errno = %d", errno);
	rc = llapi_layout_pool_name_get(layout, mypool, sizeof(mypool));
	ASSERTF(strlen(name) == strlen(mypool), "name = %s, str = %s", name,
		mypool);
	llapi_layout_free(layout);
}

#define T20FILE		"t20"
#define T20_DESC	"LLAPI_LAYOUT_DEFAULT is honored"
static void test20(void)
{
	int		rc;
	int		fd;
	struct llapi_layout	*deflayout;
	struct llapi_layout	*filelayout;
	char		path[PATH_MAX];
	uint64_t	fsize;
	uint64_t	fcount;
	uint64_t	dsize;
	uint64_t	dcount;

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T20FILE);

	rc = unlink(path);
	ASSERTF(rc == 0 || errno == ENOENT, "errno = %d", errno);

	filelayout = llapi_layout_alloc();
	ASSERTF(filelayout != NULL, "errno = %d", errno);

	rc = llapi_layout_stripe_size_set(filelayout, LLAPI_LAYOUT_DEFAULT);
	ASSERTF(rc == 0, "rc = %d, errno = %d", rc, errno);

	rc = llapi_layout_stripe_count_set(filelayout, LLAPI_LAYOUT_DEFAULT);
	ASSERTF(rc == 0, "rc = %d, errno = %d", rc, errno);

	fd = llapi_layout_file_create(path, 0, 0640, filelayout);
	ASSERTF(fd >= 0, "errno = %d", errno);

	rc = close(fd);
	ASSERTF(rc == 0, "errno = %d", errno);

	llapi_layout_free(filelayout);

	deflayout = llapi_layout_get_by_path(lustre_dir, LAYOUT_GET_EXPECTED);
	ASSERTF(deflayout != NULL, "errno = %d", errno);

	filelayout = llapi_layout_get_by_path(path, 0);
	ASSERTF(filelayout != NULL, "errno = %d", errno);

	rc = llapi_layout_stripe_count_get(filelayout, &fcount);
	ASSERTF(rc == 0, "errno = %d", errno);
	rc = llapi_layout_stripe_count_get(deflayout, &dcount);
	ASSERTF(rc == 0, "errno = %d", errno);
	ASSERTF(fcount == dcount || dcount == LLAPI_LAYOUT_DEFAULT ||
		dcount == LLAPI_LAYOUT_WIDE,
		"%"PRIu64" != %"PRIu64, fcount, dcount);

	rc = llapi_layout_stripe_size_get(filelayout, &fsize);
	ASSERTF(rc == 0, "errno = %d", errno);
	rc = llapi_layout_stripe_size_get(deflayout, &dsize);
	ASSERTF(rc == 0, "errno = %d", errno);
	ASSERTF(fsize == dsize, "%"PRIu64" != %"PRIu64, fsize, dsize);

	llapi_layout_free(filelayout);
	llapi_layout_free(deflayout);
}

#define T21_DESC	"llapi_layout_file_create fails for non-Lustre file"
static void test21(void)
{
	struct llapi_layout *layout;
	char template[PATH_MAX];
	int fd;
	int rc;

	snprintf(template, sizeof(template), "%s/XXXXXX", P_tmpdir);
	fd = mkstemp(template);
	ASSERTF(fd >= 0, "template = %s, errno = %d", template, errno);
	rc = close(fd);
	ASSERTF(rc == 0, "errno = %d", fd);
	rc = unlink(template);
	ASSERTF(rc == 0, "errno = %d", errno);

	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	fd = llapi_layout_file_create(template, 0, 0640, layout);
	ASSERTF(fd == -1 && errno == ENOTTY,
		"fd = %d, errno = %d, template = %s", fd, errno, template);
	llapi_layout_free(layout);
}

#define T22FILE		"t22"
#define T22_DESC	"llapi_layout_file_create applied mode correctly"
static void test22(void)
{
	int		rc;
	int		fd;
	char		path[PATH_MAX];
	struct stat	st;
	mode_t		mode_in = 0640;
	mode_t		mode_out;
	mode_t		umask_orig;

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T22FILE);

	rc = unlink(path);
	ASSERTF(rc == 0 || errno == ENOENT, "errno = %d", errno);

	umask_orig = umask(0022);

	fd = llapi_layout_file_create(path, 0, mode_in, NULL);
	ASSERTF(fd >= 0, "errno = %d", errno);

	(void) umask(umask_orig);

	rc = fstat(fd, &st);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = close(fd);
	ASSERTF(rc == 0, "errno = %d", fd);

	mode_out = st.st_mode & ~S_IFMT;
	ASSERTF(mode_in == mode_out, "%o != %o", mode_in, mode_out);
}

#define T23_DESC	"llapi_layout_get_by_path fails for non-Lustre file"
static void test23(void)
{
	struct llapi_layout *layout;
	char template[PATH_MAX];
	int fd;
	int rc;

	snprintf(template, sizeof(template), "%s/XXXXXX", P_tmpdir);
	fd = mkstemp(template);
	ASSERTF(fd >= 0, "template = %s, errno = %d", template, errno);
	rc = close(fd);
	ASSERTF(rc == 0, "errno = %d", fd);

	layout = llapi_layout_get_by_path(template, 0);
	ASSERTF(layout == NULL && errno == ENOTTY,
		"errno = %d, template = %s", errno, template);

	rc = unlink(template);
	ASSERTF(rc == 0, "errno = %d", errno);
}

/* llapi_layout_get_by_path(path, LAYOUT_GET_EXPECTED) returns expected layout
 * for file with unspecified layout.
 */
#define T24FILE		"t24"
#define T24_DESC	"LAYOUT_GET_EXPECTED works with existing file"
static void test24(void)
{
	int fd;
	int rc;
	struct llapi_layout *layout;
	uint64_t count;
	uint64_t size;
	uint64_t pattern;
	char path[PATH_MAX];

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T24FILE);

	rc = unlink(path);
	ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);
	fd = open(path, O_CREAT, 0640);
	ASSERTF(fd >= 0, "errno = %d", errno);
	rc = close(fd);
	ASSERTF(rc == 0, "errno = %d", errno);

	layout = llapi_layout_get_by_path(path, LAYOUT_GET_EXPECTED);
	ASSERTF(layout != NULL, "errno = %d\n", errno);

	rc = llapi_layout_stripe_count_get(layout, &count);
	ASSERTF(rc == 0, "errno = %d\n", errno);
	ASSERTF(count != LLAPI_LAYOUT_DEFAULT, "expected literal value");

	rc = llapi_layout_stripe_size_get(layout, &size);
	ASSERTF(rc == 0, "errno = %d\n", errno);
	ASSERTF(size != LLAPI_LAYOUT_DEFAULT, "expected literal value");

	rc = llapi_layout_pattern_get(layout, &pattern);
	ASSERTF(rc == 0, "errno = %d\n", errno);
	ASSERTF(pattern != LLAPI_LAYOUT_DEFAULT, "expected literal value");

	llapi_layout_free(layout);
}

/* llapi_layout_get_by_path(path, LAYOUT_GET_EXPECTED) returns expected layout
 * for directory with unspecified layout.
 */
#define T25DIR		"d25"
#define T25_DESC	"LAYOUT_GET_EXPECTED works with directory"
static void test25(void)
{
	int rc;
	struct llapi_layout *layout;
	uint64_t count;
	uint64_t size;
	uint64_t pattern;
	char dir[PATH_MAX];

	snprintf(dir, sizeof(dir), "%s/%s", lustre_dir, T25DIR);

	rc = rmdir(dir);
	ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);
	rc = mkdir(dir, 0750);
	ASSERTF(rc == 0, "errno = %d", errno);

	layout = llapi_layout_get_by_path(dir, LAYOUT_GET_EXPECTED);
	ASSERTF(layout != NULL, "errno = %d\n", errno);

	rc = llapi_layout_stripe_count_get(layout, &count);
	ASSERTF(rc == 0, "errno = %d\n", errno);
	ASSERTF(count != LLAPI_LAYOUT_DEFAULT, "expected literal value");

	rc = llapi_layout_stripe_size_get(layout, &size);
	ASSERTF(rc == 0, "errno = %d\n", errno);
	ASSERTF(size != LLAPI_LAYOUT_DEFAULT, "expected literal value");

	rc = llapi_layout_pattern_get(layout, &pattern);
	ASSERTF(rc == 0, "errno = %d\n", errno);
	ASSERTF(pattern != LLAPI_LAYOUT_DEFAULT, "expected literal value");

	llapi_layout_free(layout);
}

/* llapi_layout_get_by_path(path, LAYOUT_GET_EXPECTED) correctly combines
 * specified attributes of parent directory with attributes filesystem root.
 */
#define T26DIR		"d26"
#define T26_DESC	"LAYOUT_GET_EXPECTED partially specified parent"
#define T26_STRIPE_SIZE	(1048576 * 4)
static void test26(void)
{
	int rc;
	struct llapi_layout *layout;
	const char *lfs = getenv("LFS");
	uint64_t count;
	uint64_t size;
	uint64_t pattern;
	char dir[PATH_MAX];
	char cmd[PATH_MAX + 64];

	snprintf(dir, sizeof(dir), "%s/%s", lustre_dir, T26DIR);
	rc = rmdir(dir);
	ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);
	rc = mkdir(dir, 0750);
	ASSERTF(rc == 0, "errno = %d", errno);

	if (lfs == NULL)
		lfs = "/usr/bin/lfs";

	snprintf(cmd, sizeof(cmd), "%s setstripe -S %d %s", lfs,
		 T26_STRIPE_SIZE, dir);
	rc = system(cmd);
	ASSERTF(rc == 0, "system(%s): exit status %d", cmd, WEXITSTATUS(rc));

	layout = llapi_layout_get_by_path(dir, LAYOUT_GET_EXPECTED);
	ASSERTF(layout != NULL, "errno = %d\n", errno);

	rc = llapi_layout_stripe_count_get(layout, &count);
	ASSERTF(rc == 0, "errno = %d\n", errno);
	ASSERTF(count != LLAPI_LAYOUT_DEFAULT, "expected literal value");

	rc = llapi_layout_stripe_size_get(layout, &size);
	ASSERTF(rc == 0, "errno = %d\n", errno);
	ASSERTF(size == T26_STRIPE_SIZE, "size = %"PRIu64, size);

	rc = llapi_layout_pattern_get(layout, &pattern);
	ASSERTF(rc == 0, "errno = %d\n", errno);
	ASSERTF(pattern != LLAPI_LAYOUT_DEFAULT, "expected literal value");

	llapi_layout_free(layout);
}

/* llapi_layout_get_by_path(path, LAYOUT_GET_EXPECTED) work with
 * non existing file.
 */
#define T27DIR		"d27"
#define T27_DESC	"LAYOUT_GET_EXPECTED with non existing file"
#define T27_STRIPE_SIZE	(1048576 * 3)
static void test27(void)
{
	int rc;
	struct llapi_layout *layout;
	const char *lfs = getenv("LFS");
	uint64_t count;
	uint64_t size;
	uint64_t pattern;
	char dirpath[PATH_MAX + 128];
	char filepath[PATH_MAX * 2];
	char cmd[PATH_MAX * 2];

	snprintf(dirpath, sizeof(dirpath) - 1, "%s/%s", lustre_dir, T27DIR);
	snprintf(filepath, sizeof(filepath), "%s/nonesuch", dirpath);

	rc = rmdir(dirpath);
	ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);
	rc = mkdir(dirpath, 0750);
	ASSERTF(rc == 0, "errno = %d", errno);

	if (lfs == NULL)
		lfs = "/usr/bin/lfs";

	snprintf(cmd, sizeof(cmd), "%s setstripe -S %d %s", lfs,
		 T27_STRIPE_SIZE, dirpath);
	rc = system(cmd);
	ASSERTF(rc == 0, "system(%s): exit status %d", cmd, WEXITSTATUS(rc));

	layout = llapi_layout_get_by_path(filepath, LAYOUT_GET_EXPECTED);
	ASSERTF(layout != NULL, "errno = %d\n", errno);

	rc = llapi_layout_stripe_count_get(layout, &count);
	ASSERTF(rc == 0, "errno = %d\n", errno);
	ASSERTF(count != LLAPI_LAYOUT_DEFAULT, "expected literal value");

	rc = llapi_layout_stripe_size_get(layout, &size);
	ASSERTF(rc == 0, "errno = %d\n", errno);
	ASSERTF(size == T27_STRIPE_SIZE, "size = %"PRIu64, size);

	rc = llapi_layout_pattern_get(layout, &pattern);
	ASSERTF(rc == 0, "errno = %d\n", errno);
	ASSERTF(pattern != LLAPI_LAYOUT_DEFAULT, "expected literal value");

	llapi_layout_free(layout);
}

/* llapi_layout_stripe_count_get returns LLAPI_LAYOUT_WIDE for a directory
 * with a stripe_count of -1.
 */
#define T28DIR		"d28"
#define T28_DESC	"LLAPI_LAYOUT_WIDE returned as expected"
static void test28(void)
{
	int rc;
	struct llapi_layout *layout;
	const char *lfs = getenv("LFS");
	uint64_t count;
	char dirpath[PATH_MAX];
	char cmd[PATH_MAX + 64];

	snprintf(dirpath, sizeof(dirpath), "%s/%s", lustre_dir, T28DIR);

	rc = rmdir(dirpath);
	ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);
	rc = mkdir(dirpath, 0750);
	ASSERTF(rc == 0, "errno = %d", errno);

	if (lfs == NULL)
		lfs = "/usr/bin/lfs";

	snprintf(cmd, sizeof(cmd), "%s setstripe -c -1 %s", lfs, dirpath);
	rc = system(cmd);
	ASSERTF(rc == 0, "system(%s): exit status %d", cmd, WEXITSTATUS(rc));

	layout = llapi_layout_get_by_path(dirpath, 0);
	ASSERTF(layout != NULL, "errno = %d\n", errno);

	rc = llapi_layout_stripe_count_get(layout, &count);
	ASSERTF(rc == 0, "errno = %d\n", errno);
	ASSERTF(count == LLAPI_LAYOUT_WIDE, "count = %"PRIu64"\n", count);

	llapi_layout_free(layout);
}

#define T29FILE		"f29"
#define T29_DESC	"set ost index to non-zero stripe number"
static void test29(void)
{
	int rc, fd, i;
	uint64_t ost0, ost1, nost;
	struct llapi_layout *layout;
	char path[PATH_MAX];

	if (num_osts < 2)
		return;

	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno %d", errno);

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T29FILE);

	rc = unlink(path);
	ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);

	/* set ost index to LLAPI_LAYOUT_IDX_MAX should fail */
	rc = llapi_layout_ost_index_set(layout, 1, LLAPI_LAYOUT_IDX_MAX);
	ASSERTF(rc == -1 && errno == EINVAL, "rc = %d, errno = %d\n",
		rc, errno);

	/* specify ost index partially */
	rc = llapi_layout_ost_index_set(layout, 1, 0);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* create a partially specified layout will fail */
	fd = llapi_layout_file_create(path, 0, 0660, layout);
	ASSERTF(fd == -1 && errno == EINVAL, "path = %s, fd = %d, errno = %d",
		path, fd, errno);

	rc = unlink(path);
	ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);

	/* specify all stripes */
	rc = llapi_layout_ost_index_set(layout, 0, 1);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* create */
	fd = llapi_layout_file_create(path, 0, 0660, layout);
	ASSERTF(fd >= 0, "path = %s, fd = %d, errno = %d", path, fd, errno);

	rc = close(fd);
	ASSERTF(rc == 0, "errno = %d", errno);
	llapi_layout_free(layout);

	/* get layout from file */
	layout = llapi_layout_get_by_path(path, 0);
	ASSERTF(layout != NULL, "errno = %d", errno);

	rc = llapi_layout_ost_index_get(layout, 0, &ost0);
	ASSERTF(rc == 0, "errno = %d", errno);
	rc = llapi_layout_ost_index_get(layout, 1, &ost1);
	ASSERTF(rc == 0, "errno = %d", errno);
	ASSERTF(ost0 == 1, "%"PRIu64" != %d", ost0, 1);
	ASSERTF(ost1 == 0, "%"PRIu64" != %d", ost1, 0);
	llapi_layout_free(layout);

	/* specify more ost indexes to test realloc */
	nost = 0;
	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno %d", errno);
	for (i = 0; i < LOV_MAX_STRIPE_COUNT; i++) {
		rc = llapi_layout_ost_index_set(layout, i, nost);
		ASSERTF(rc == 0, "errno = %d", errno);
		rc = llapi_layout_ost_index_get(layout, i, &ost0);
		ASSERTF(rc == 0, "errno = %d", errno);
		nost++;
		if (nost == num_osts)
			nost = 0;
	}

	nost = 0;
	for (i = 0; i < LOV_MAX_STRIPE_COUNT; i++) {
		rc = llapi_layout_ost_index_get(layout, i, &ost0);
		ASSERTF(rc == 0, "errno = %d", errno);
		ASSERTF(ost0 == nost, "ost=%"PRIu64" nost=%"PRIu64"",
			ost0, nost);
		nost++;
		if (nost == num_osts)
			nost = 0;
	}
	llapi_layout_free(layout);

	nost = 0;
	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno %d", errno);
	for (i = LOV_MAX_STRIPE_COUNT-1; i >= 0; i--) {
		rc = llapi_layout_ost_index_set(layout, i, nost);
		ASSERTF(rc == 0, "errno = %d", errno);
		rc = llapi_layout_ost_index_get(layout, i, &ost0);
		ASSERTF(rc == 0, "errno = %d", errno);
		nost++;
		if (nost == num_osts)
			nost = 0;
	}

	nost = 0;
	for (i = LOV_MAX_STRIPE_COUNT-1; i <= 0; i--) {
		rc = llapi_layout_ost_index_get(layout, i, &ost0);
		ASSERTF(rc == 0, "errno = %d", errno);
		ASSERTF(ost0 == nost, "ost=%"PRIu64", nost=%"PRIu64"",
			ost0, nost);
		nost++;
		if (nost == num_osts)
			nost = 0;
	}
	llapi_layout_free(layout);
}

#define T30FILE		"f30"
#define T30_DESC	"create composite file, traverse components"
static void test30(void)
{
	int rc, fd;
	uint64_t start[3], end[3];
	uint64_t s, e;
	struct llapi_layout *layout;
	char path[PATH_MAX];

	start[0] = 0;
	end[0] = 64 * 1024 * 1024; /* 64m */
	start[1] = end[0];
	end[1] = 1 * 1024 * 1024 * 1024; /* 1G */
	start[2] = end[1];
	end[2] = LUSTRE_EOF;

	if (num_osts < 2)
		return;

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T30FILE);

	rc = unlink(path);
	ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);

	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 1);
	ASSERTF(rc == 0, "errno %d", errno);

	/* add component without adjusting previous component's extent
	 * end will fail.
	 */
	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == -1 && errno == EINVAL, "rc %d, errno %d", rc, errno);

	rc = llapi_layout_comp_extent_set(layout, start[0], end[0]);
	ASSERTF(rc == 0, "errno %d", errno);

	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno %d", errno);

	/* set non-contiguous extent will fail */
	rc = llapi_layout_comp_extent_set(layout, start[1] * 2, end[1]);
	ASSERTF(rc == 0, "errno %d", errno);
	rc = llapi_layout_sanity(layout, false, false);
	LAYOUT_ASSERTF(rc == LSE_NOT_ADJACENT_PREV, rc, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, start[1], end[1]);
	ASSERTF(rc == 0, "errno %d", errno);

	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno %d", errno);

	rc = llapi_layout_comp_extent_set(layout, start[2], end[2]);
	ASSERTF(rc == 0, "errno %d", errno);

	/* create composite file */
	fd = llapi_layout_file_create(path, 0, 0660, layout);
	ASSERTF(fd >= 0, "path = %s, fd = %d, errno = %d", path, fd, errno);

	llapi_layout_free(layout);

	/* traverse & verify all components */
	layout = llapi_layout_get_by_path(path, 0);
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* current component should be the tail component */
	rc = llapi_layout_comp_extent_get(layout, &s, &e);
	ASSERTF(rc == 0, "errno %d", errno);
	ASSERTF(s == start[2] && e == end[2],
		"s: %"PRIu64", e: %"PRIu64"", s, e);

	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
	ASSERTF(rc == 0, "rc %d, errno %d", rc, errno);

	/* delete non-tail component will fail */
	rc = llapi_layout_comp_del(layout);
	ASSERTF(rc == -1 && errno == EINVAL, "rc %d, errno %d", rc, errno);

	rc = llapi_layout_comp_extent_get(layout, &s, &e);
	ASSERTF(rc == 0, "errno %d", errno);
	ASSERTF(s == start[0] && e == end[0],
		"s: %"PRIu64", e: %"PRIu64"", s, e);

	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "rc %d, errno %d", rc,  errno);

	rc = llapi_layout_comp_extent_get(layout, &s, &e);
	ASSERTF(rc == 0, "errno %d", errno);
	ASSERTF(s == start[1] && e == end[1],
		"s: %"PRIu64", e: %"PRIu64"", s, e);

	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "rc %d, errno %d", rc,  errno);

	rc = llapi_layout_comp_del(layout);
	ASSERTF(rc == 0, "errno %d", errno);

	llapi_layout_free(layout);
}

#define T31FILE		"f31"
#define T31_DESC	"add/delete component to/from existing file"
static void test31(void)
{
	int rc, fd, i;
	uint64_t start[2], end[2];
	uint64_t s, e;
	uint32_t id[2];
	struct llapi_layout *layout;
	char path[PATH_MAX];

	start[0] = 0;
	end[0] = 64 * 1024 * 1024; /* 64m */
	start[1] = end[0];
	end[1] = LUSTRE_EOF;

	if (num_osts < 2)
		return;

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T31FILE);

	rc = unlink(path);
	ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);

	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 1);
	ASSERTF(rc == 0, "errno %d", errno);

	rc = llapi_layout_comp_extent_set(layout, start[0], end[0]);
	ASSERTF(rc == 0, "errno %d", errno);

	/* create composite file */
	fd = llapi_layout_file_create(path, 0, 0660, layout);
	ASSERTF(fd >= 0, "path = %s, fd = %d, errno = %d", path, fd, errno);
	llapi_layout_free(layout);

	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 2);
	ASSERTF(rc == 0, "errno %d", errno);

	rc = llapi_layout_comp_extent_set(layout, start[1], end[1]);
	ASSERTF(rc == 0, "errno %d", errno);

	/* add comopnent to existing file */
	rc = llapi_layout_file_comp_add(path, layout);
	ASSERTF(rc == 0, "errno %d", errno);
	llapi_layout_free(layout);

	/* verify the composite layout after adding */
	layout = llapi_layout_get_by_path(path, 0);
	ASSERTF(layout != NULL, "errno = %d", errno);

	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
	ASSERTF(rc == 0, "rc %d, errno %d", rc, errno);
	i = 0;
	do {
		rc = llapi_layout_comp_extent_get(layout, &s, &e);
		ASSERTF(rc == 0 && i < 2, "i %d, errno %d", i, errno);
		ASSERTF(s == start[i] && e == end[i],
			"i: %d s: %"PRIu64", e: %"PRIu64"", i, s, e);

		rc = llapi_layout_comp_id_get(layout, &id[i]);
		ASSERTF(rc == 0 && id[i] != 0, "i %d, errno %d, id %d",
			i, errno, id[i]);

		rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
		ASSERTF(rc == 0 || i == 1, "i=%d rc=%d errno=%d", i, rc, errno);
		i++;
	} while (rc == 0);

	/* Verify reverse iteration gives the same IDs as forward iteration */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_LAST);
	ASSERTF(rc == 0, "rc %d, errno %d", rc, errno);
	do {
		__u32 comp_id;

		--i;
		rc = llapi_layout_comp_id_get(layout, &comp_id);
		ASSERTF(rc == 0 && comp_id == id[i],
			"i %d, errno %d, id[] %u/%u", i, errno, id[i], comp_id);

		rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_PREV);
		ASSERTF(rc == 0 || i == 0, "i=%d rc=%d errno=%d", i, rc, errno);
	} while (rc == 0);

	llapi_layout_free(layout);

	/* delete non-tail component will fail */
	rc = llapi_layout_file_comp_del(path, id[0], 0);
	ASSERTF(rc < 0 && errno == EINVAL, "rc %d, errno %d", rc, errno);

	rc = llapi_layout_file_comp_del(path, id[1], 0);
	ASSERTF(rc == 0, "rc %d, errno %d", rc, errno);

	/* verify the composite layout after deleting */
	layout = llapi_layout_get_by_path(path, 0);
	ASSERTF(layout != NULL, "errno = %d", errno);

	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
	ASSERTF(rc == 0, "rc %d, errno %d", rc, errno);

	rc = llapi_layout_comp_extent_get(layout, &s, &e);
	ASSERTF(rc == 0, "errno %d", errno);
	ASSERTF(s == start[0] && e == end[0],
		"s: %"PRIu64", e: %"PRIu64"", s, e);
}

#define T32FILE			"t32"
#define T32_STRIPE_COUNT	(num_osts*2)
#define T32_DESC		"Test overstriping with layout_file_create"
static void test32(void)
{
	int rc;
	int fd;
	uint64_t count;
	struct llapi_layout *layout = llapi_layout_alloc();
	void *lmdbuf = NULL;
	struct lov_user_md *lmd;
	char path[PATH_MAX];

	ASSERTF(layout != NULL, "errno %d", errno);

	/* Maximum possible, to be on the safe side - num_osts could be large */
	lmdbuf = malloc(XATTR_SIZE_MAX);
	ASSERTF(lmdbuf != NULL, "errno %d", errno);
	lmd = lmdbuf;

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T32FILE);

	rc = unlink(path);
	ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);

	/* stripe count */
	rc = llapi_layout_stripe_count_set(layout, T32_STRIPE_COUNT);
	ASSERTF(rc == 0, "errno = %d", errno);
	rc = llapi_layout_stripe_count_get(layout, &count);
	ASSERTF(rc == 0 && count == T32_STRIPE_COUNT, "%"PRIu64" != %d", count,
		T32_STRIPE_COUNT);

	rc = llapi_layout_pattern_set(layout, LLAPI_LAYOUT_OVERSTRIPING);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* create */
	fd = llapi_layout_file_create(path, 0, 0660, layout);
	ASSERTF(fd >= 0, "path = %s, errno = %d", path, errno);

	rc = ioctl(fd, LL_IOC_LOV_GETSTRIPE_NEW, lmdbuf);
	ASSERTF(rc == 0, "errno = %d", errno);

	count = lmd->lmm_stripe_count;
	ASSERTF(count == T32_STRIPE_COUNT,
		"stripe count (%"PRIu64") not equal to expected (%d)",
		count, T32_STRIPE_COUNT);

	rc = close(fd);
	ASSERTF(rc == 0, "errno = %d", errno);
	llapi_layout_free(layout);
	free(lmdbuf);
}

#define T33FILE			"t33"
#define T33_STRIPE_COUNT	(num_osts*2)
#define T33_DESC		"Test overstriping with llapi_file_open"
static void test33(void)
{
	int rc;
	int fd;
	uint64_t count;
	void *lmdbuf = NULL;
	struct lov_user_md *lmd;
	char path[PATH_MAX];

	/* Maximum possible, to be on the safe side - num_osts could be large */
	lmdbuf = malloc(XATTR_SIZE_MAX);
	ASSERTF(lmdbuf != NULL, "errno %d", errno);
	lmd = lmdbuf;

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T33FILE);

	rc = unlink(path);
	ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);

	fd = llapi_file_open(path, O_CREAT | O_RDWR, 0660, 0, -1, num_osts*2,
			     LOV_PATTERN_RAID0 | LOV_PATTERN_OVERSTRIPING);
	ASSERTF(fd >= 0, "path = %s, errno = %d", path, errno);

	rc = ioctl(fd, LL_IOC_LOV_GETSTRIPE_NEW, lmdbuf);
	ASSERTF(rc == 0, "errno = %d", errno);

	count = lmd->lmm_stripe_count;
	ASSERTF(count == T33_STRIPE_COUNT,
		"stripe count (%"PRIu64") not equal to expected (%d)",
		count, T33_STRIPE_COUNT);

	rc = close(fd);
	ASSERTF(rc == 0, "errno = %d", errno);
	free(lmdbuf);
}

#define T34FILE		"f34"
#define T34_DESC	"create simple valid & invalid self extending layouts"
static void test34(void)
{
	int rc, fd;
	uint64_t start[4], end[4];
	struct llapi_layout *layout;
	char path[PATH_MAX];

	start[0] = 0;
	end[0] = 10 * 1024 * 1024; /* 10m */
	start[1] = end[0];
	end[1] = 1024 * 1024 * 1024; /* 1G */
	start[2] = end[1];
	end[2] = 10ull * 1024 * 1024 * 1024; /* 10G */
	start[3] = end[2];
	end[3] = LUSTRE_EOF;

	if (num_osts < 2)
		return;

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T34FILE);

	rc = unlink(path);
	ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);

	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 1);
	ASSERTF(rc == 0, "errno %d", errno);

	/* add component without adjusting previous component's extent
	 * end will fail.
	 */
	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == -1 && errno == EINVAL, "rc %d, errno %d", rc, errno);

	rc = llapi_layout_comp_extent_set(layout, start[0], end[0]);
	ASSERTF(rc == 0, "errno %d", errno);

	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno %d", errno);

	rc = llapi_layout_comp_extent_set(layout, start[1], end[1]);
	ASSERTF(rc == 0, "errno %d", errno);

	rc = llapi_layout_comp_flags_set(layout, LCME_FL_EXTENSION);
	ASSERTF(rc == 0, "errno %d", errno);

	/* Invalid size, too small - < 64 MiB */
	rc = llapi_layout_extension_size_set(layout, 32 << 20);
	ASSERTF(rc == -1, "errno %d", errno);

	/* too large - > 4 TiB */
	rc = llapi_layout_extension_size_set(layout, 5ull << 40);
	ASSERTF(rc == -1, "errno %d", errno);

	/* Valid size, 64 MiB */
	rc = llapi_layout_extension_size_set(layout, 64 << 20);
	ASSERTF(rc == 0, "errno %d", errno);

	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno %d", errno);

	rc = llapi_layout_comp_extent_set(layout, start[2], end[2]);
	ASSERTF(rc == 0, "errno %d", errno);

	/* Set extension space flag on adjacent components:
	 * This is invalid, but can't be checked until we create the file.
	 */
	rc = llapi_layout_comp_flags_set(layout, LCME_FL_EXTENSION);
	ASSERTF(rc == 0, "errno %d", errno);

	fd = llapi_layout_file_create(path, 0, 0660, layout);
	ASSERTF(fd = -1, "path = %s, fd = %d, errno = %d", path, fd, errno);

	/* Delete incorrect component */
	rc = llapi_layout_comp_del(layout);
	ASSERTF(rc == 0, "errno %d", errno);

	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno %d", errno);

	/* Convert this comp to 0-len that can be followed by extension space */
	rc = llapi_layout_comp_extent_set(layout, start[2], start[2]);
	ASSERTF(rc == 0, "errno %d", errno);

	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno %d", errno);

	rc = llapi_layout_comp_extent_set(layout, start[2], end[3]);
	ASSERTF(rc == 0, "errno %d", errno);

	rc = llapi_layout_comp_flags_set(layout, LCME_FL_EXTENSION);
	ASSERTF(rc == 0, "errno %d", errno);

	/* create composite file */
	fd = llapi_layout_file_create(path, 0, 0660, layout);
	ASSERTF(fd >= 0, "path = %s, fd = %d, errno = %d", path, fd, errno);

	llapi_layout_free(layout);

	/* traverse & verify all components */
	layout = llapi_layout_get_by_path(path, 0);
	ASSERTF(layout != NULL, "errno = %d", errno);

	rc = llapi_layout_sanity(layout, false, false);
	LAYOUT_ASSERTF(rc == 0, rc, "errno %d", errno);
}

#define T35FILE		"f35"
#define T35_STRIPE_COUNT num_osts
#define T35_STRIPE_SIZE 1048576
#define T35_DESC	"create a file with layout different from default"
static void test35(void)
{
	char mypool[LOV_MAXPOOLNAME + 1] = { '\0'};
	struct llapi_layout *layout;
	char path[PATH_MAX];
	uint64_t count;
	uint64_t size;
	int fd;
	int rc;

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T35FILE);

	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "failed to allocate layout structure");
	llapi_layout_stripe_count_set(layout, T35_STRIPE_COUNT);
	llapi_layout_stripe_size_set(layout, T35_STRIPE_SIZE);
	llapi_layout_pool_name_set(layout, poolname);

	fd = llapi_layout_file_create(path, 0, 0640, layout);
	ASSERTF(fd >= 0, "failed to create " T35FILE);

	llapi_layout_free(layout);

	layout = llapi_layout_get_by_fd(fd, 0);
	ASSERTF(layout != NULL, "failed to get layout");

	rc = llapi_layout_stripe_count_get(layout, &count);
	ASSERTF(rc == 0 && count == T35_STRIPE_COUNT, "invalid stripe count");

	rc = llapi_layout_stripe_size_get(layout, &size);
	ASSERTF(rc == 0 && size == T35_STRIPE_SIZE, "invalid stripe size");

	rc = llapi_layout_pool_name_get(layout, mypool, sizeof(mypool));
	ASSERTF(rc == 0, "error reading pool name");
	ASSERTF(strcmp(mypool, poolname) == 0, "invalid pool name");

	llapi_layout_free(layout);
	close(fd);
}

#define T36_DESC	"verify mirror count is validated"
static void test36(void)
{
	int rc;
	struct llapi_layout *layout;

	/* Create a complete layout first */
	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* Mirror 1 */
	/* COMP1: [0, 1GiB] */
	rc = llapi_layout_stripe_count_set(layout, 4);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, 1024 * 1024 * 1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* COMP2: [1GiB, EOF] */
	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 6);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 1024 * 1024 * 1024ULL,
					  LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Mirror 2 */
	/* COMP1: [0, 1GiB] */
	rc = llapi_layout_add_first_comp(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 4);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, 1024 * 1024 * 1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* COMP2: [1GiB, EOF] */
	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 6);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 1024 * 1024 * 1024ULL,
					  LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Verify it fails with invalid mirror count since it is unset */
	rc = llapi_layout_sanity(layout, false, false);
	LAYOUT_ASSERTF(rc != 0, rc,
		       "Layout should fail with invalid mirror count");

	/* Set the mirror count to 2 and verify it passes */
	rc = llapi_layout_mirror_count_set(layout, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_sanity(layout, false, false);
	LAYOUT_ASSERTF(rc == 0, rc,
		       "Layout should pass with valid mirror count");

	/* Set the mirror count to 3 and verify it fails */
	rc = llapi_layout_mirror_count_set(layout, 3);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_sanity(layout, false, false);
	LAYOUT_ASSERTF(rc != 0, rc,
		       "Layout should fail with invalid mirror count");

	/* Use llapi_layout_mirror_count_sync to sync the mirror count */
	rc = llapi_layout_mirror_count_sync(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_sanity(layout, false, false);
	LAYOUT_ASSERTF(rc == 0, rc,
		       "Layout should pass with valid mirror count");

	/* Set the mirror count to 0 and use llapi_layout_mirror_count_sync */
	rc = llapi_layout_mirror_count_set(layout, 0);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_mirror_count_sync(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_sanity(layout, false, false);
	LAYOUT_ASSERTF(rc == 0, rc,
		       "Layout should pass with valid mirror count");

	llapi_layout_free(layout);
}

#define T37FILE "f37"
#define T37_DESC "verify mirror count and mirror ids for existing files"
static void test37(void)
{
	struct llapi_layout *layout, *filelayout;
	char path[PATH_MAX];
	uint64_t s, e;
	uint16_t mirror_count;
	uint32_t id;
	int rc, fd;

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T37FILE);

	rc = unlink(path);
	ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);

	/* Create a complete layout first */
	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* Mirror 1 */
	/* COMP1: [0, 1GiB] */
	rc = llapi_layout_stripe_count_set(layout, 4);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, 1024 * 1024 * 1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* COMP2: [1GiB, EOF] */
	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 6);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 1024 * 1024 * 1024ULL,
					  LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Mirror 2 */
	/* COMP1: [0, 1GiB] */
	rc = llapi_layout_add_first_comp(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 4);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, 1024 * 1024 * 1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* COMP2: [1GiB, EOF] */
	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 6);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 1024 * 1024 * 1024ULL,
					  LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* sync mirror count and create file*/
	rc = llapi_layout_mirror_count_sync(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	fd = llapi_layout_file_create(path, 0, 0640, layout);
	ASSERTF(fd >= 0, "errno = %d", errno);

	rc = close(fd);
	ASSERTF(rc == 0, "errno = %d", errno);

	llapi_layout_free(layout);

	/* Read the layout back from the file */
	filelayout = llapi_layout_get_by_path(path, 0);
	ASSERTF(filelayout != NULL, "errno = %d", errno);

	rc = llapi_layout_sanity(filelayout, false, false);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_use(filelayout, LLAPI_LAYOUT_COMP_USE_LAST);
	ASSERTF(rc == 0, "rc %d, errno %d", rc, errno);

	/* Verify the mirror ID is set on the last component after file read.
	 * Note, mirror count is not set on llapi_layout_get_by_path() and
	 * requires llapi_layout_mirror_count_sync() to be called.
	 */
	rc = llapi_layout_mirror_id_get(filelayout, &id);
	ASSERTF(rc == 0, "errno = %d", errno);
	ASSERTF(id != 0, "id = %d", id);

	/* Verify the extent of the last component */
	rc = llapi_layout_comp_extent_get(filelayout, &s, &e);
	ASSERTF(rc == 0, "errno = %d", errno);
	ASSERTF(s == 1024 * 1024 * 1024ULL && e == LUSTRE_EOF,
		"s: %" PRIu64 ", e: %" PRIu64 "", s, e);

	/* delete the last 2 components */
	rc = llapi_layout_comp_del(filelayout);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* check the "new" last component */
	rc = llapi_layout_comp_extent_get(filelayout, &s, &e);
	ASSERTF(rc == 0, "errno = %d", errno);
	ASSERTF(s == 0 && e == 1024 * 1024 * 1024ULL,
		"s: %" PRIu64 ", e: %" PRIu64 "", s, e);

	rc = llapi_layout_comp_del(filelayout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_mirror_id_get(filelayout, &id);
	ASSERTF(rc == 0, "errno = %d", errno);
	ASSERTF(id != 0, "id = %d", id);

	/* sync mirror count (only mirror ids are set on get_by_path()) */
	rc = llapi_layout_mirror_count_sync(filelayout);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Verify the mirror count is now 1 */
	rc = llapi_layout_mirror_count_get(filelayout, &mirror_count);
	ASSERTF(rc == 0, "errno = %d", errno);
	ASSERTF(mirror_count == 1, "mirror_count = %d", mirror_count);

	rc = llapi_layout_sanity(filelayout, false, false);
	LAYOUT_ASSERTF(rc == 0, rc,
		       "Layout should pass with valid mirror count");

	/* re-add mirror 2 */
	/* COMP1: [0, 1GiB] */
	rc = llapi_layout_add_first_comp(filelayout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(filelayout, 4);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(filelayout, 0, 1024 * 1024 * 1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* COMP2: [1GiB, EOF] */
	rc = llapi_layout_comp_add(filelayout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(filelayout, 6);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(filelayout, 1024 * 1024 * 1024ULL,
					  LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Verify last component has no mirror id set yet */
	rc = llapi_layout_comp_use(filelayout, LLAPI_LAYOUT_COMP_USE_LAST);
	ASSERTF(rc == 0, "rc %d, errno %d", rc, errno);

	rc = llapi_layout_mirror_id_get(filelayout, &id);
	ASSERTF(rc == 0, "errno = %d", errno);
	ASSERTF(id == 0, "id = %d", id);

	rc = llapi_layout_mirror_count_sync(filelayout);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Verify last component has mirror id set yet now */
	rc = llapi_layout_mirror_id_get(filelayout, &id);
	ASSERTF(rc == 0, "errno = %d", errno);
	ASSERTF(id != 0, "id = %d", id);

	/* Verify the mirror count is 2 */
	rc = llapi_layout_mirror_count_get(filelayout, &mirror_count);
	ASSERTF(rc == 0, "errno = %d", errno);
	ASSERTF(mirror_count == 2, "mirror_count = %d", mirror_count);

	rc = llapi_layout_sanity(filelayout, false, false);
	LAYOUT_ASSERTF(rc == 0, rc,
		       "Layout should pass with valid mirror count");

	llapi_layout_free(filelayout);
}

#define T40_DESC	"verify LCME_FL_PARITY cannot be set with flags_set function"
static void test40(void)
{
	int rc;
	struct llapi_layout *layout;

	/* Create a layout with one data component */
	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 6);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Add a regular component with explicit extent */
	rc = llapi_layout_comp_add_extent(layout, 0, LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Try to set LCME_FL_PARITY flag - this should fail */
	rc = llapi_layout_comp_flags_set(layout, LCME_FL_PARITY);
	ASSERTF(rc != 0,
		"Setting LCME_FL_PARITY with flags_set should fail but succeeded");

	llapi_layout_free(layout);
}

#define T41_DESC	"verify comp_add_opts parameter validation for EC components"
static void test41(void)
{
	int rc;
	struct llapi_layout *layout;

	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 6);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Try to add EC component with zero cstripe/dstripe */
	rc = llapi_layout_comp_add_ec(layout, 1, 0, LUSTRE_EOF, 0, 0);
	ASSERTF(rc != 0,
		"Parity component with zero cstripe/dstripe should fail but succeeded");

	llapi_layout_free(layout);
}

#define T42_DESC	"verify parity component with non-matching extent is refused"
static void test42(void)
{
	int rc;
	struct llapi_layout *layout;

	/* Create layout with data mirror and EC mirror */
	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* Mirror 1: Regular data component [0, EOF] */
	rc = llapi_layout_stripe_count_set(layout, 6);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Add first component of Mirror 2 */
	rc = llapi_layout_add_first_comp(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Attempt to add EC component with different extent [0, 10MiB]
	 * This should fail because it doesn't match Mirror 1 extent [0, EOF]
	 */
	rc = llapi_layout_comp_add_ec(layout, 1, 0, 10*1024*1024, 6, 2);
	ASSERTF(rc != 0, "errno = %d", errno);

	/* Layout is still valid as the extent was refused */
	rc = llapi_layout_sanity(layout, false, false);
	LAYOUT_ASSERTF(rc == 0, rc,
		       "Layout sanity should succeed");

	llapi_layout_free(layout);
}

#define T43_DESC	"verify EC component cannot be added to incomplete layout"
static void test43(void)
{
	int rc;
	struct llapi_layout *layout;

	/* Create layout with incomplete data mirror: [0, 1GiB] (doesn't go
	 * to EOF)
	 */
	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* Mirror 1: Incomplete data component [0, 1GiB] */
	rc = llapi_layout_stripe_count_set(layout, 4);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, 1024*1024*1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Try to add EC parity component - creation should succeed but
	 * validation should fail
	 */
	rc = llapi_layout_comp_add_ec(layout, 1, 0, 1024*1024*1024ULL, 4, 2);
	ASSERTF(rc == 0, "EC component creation should succeed: errno = %d",
		errno);

	/* Validation should catch the incomplete layout */
	rc = llapi_layout_sanity(layout, false, false);
	LAYOUT_ASSERTF(rc != 0, rc,
		       "EC component on incomplete layout should fail validation");

	llapi_layout_free(layout);
}

#define T44_DESC	"verify EC components with zero stripe counts are rejected"
static void test44(void)
{
	int rc;
	struct llapi_layout *layout;

	/* Create a complete layout first */
	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* COMP1: [0, 1GiB] */
	rc = llapi_layout_stripe_count_set(layout, 4);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, 1024*1024*1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* COMP2: [1GiB, EOF] */
	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 6);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 1024*1024*1024ULL,
					   LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Test 1: cstripe_count = 0, dstripe_count > 0 (should fail) */
	rc = llapi_layout_comp_add_ec(layout, 1, 0, 1024*1024*1024ULL, 4, 0);
	ASSERTF(rc != 0, "EC component with zero coding stripes should fail");

	/* Test 2: cstripe_count > 0, dstripe_count = 0 (should fail) */
	rc = llapi_layout_comp_add_ec(layout, 1, 0, 1024*1024*1024ULL, 0, 2);
	ASSERTF(rc != 0, "EC component with zero data stripes should fail");

	/* Test 3: cstripe_count = 0, dstripe_count = 0 (should fail) */
	rc = llapi_layout_comp_add_ec(layout, 1, 0, 1024*1024*1024ULL, 0, 0);
	ASSERTF(rc != 0, "EC component with zero stripe counts should fail");

	llapi_layout_free(layout);
}

#define T45_DESC	"verify overlapping EC extents are rejected"
static void test45(void)
{
	int rc;
	struct llapi_layout *layout;

	/* Create layout: COMP1[0-1GB], COMP2[1GB-2GB], COMP3[2GB-EOF] */
	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* COMP1: [0, 1GiB] */
	rc = llapi_layout_stripe_count_set(layout, 4);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, 1024*1024*1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* COMP2: [1GiB, 2GiB] */
	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 6);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 1024*1024*1024ULL,
					   2*1024*1024*1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* COMP3: [2GiB, EOF] */
	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 8);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 2*1024*1024*1024ULL,
					  LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Add first EC component: [0, 1GiB] */
	rc = llapi_layout_comp_add_ec(layout, 1, 0, 1024*1024*1024ULL, 4, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Try to add overlapping EC component: [512MiB, 1.5GiB], should fail */
	rc = llapi_layout_comp_add_ec(layout, 1, 512*1024*1024ULL,
				      1536*1024*1024ULL, 6, 2);
	ASSERTF(rc != 0, "errno = %d", errno);

	/* Validation should fail since the layout is incomplete due to missing
	 * EC components
	 */
	rc = llapi_layout_sanity(layout, false, false);
	LAYOUT_ASSERTF(rc != 0, rc, "Layout should still be valid");

	llapi_layout_free(layout);
}

#define T46_DESC	"verify invalid EC extent ranges are rejected"
static void test46(void)
{
	int rc;
	struct llapi_layout *layout;

	/* Create a complete layout first */
	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* COMP1: [0, 1GiB] */
	rc = llapi_layout_stripe_count_set(layout, 4);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, 1024*1024*1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* COMP2: [1GiB, EOF] */
	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 6);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 1024*1024*1024ULL,
					  LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Test 1: start > end (should fail) */
	rc = llapi_layout_comp_add_ec(layout, 1, 1024*1024*1024ULL,
				      512*1024*1024ULL, 4, 2);
	ASSERTF(rc != 0, "EC component with start > end should fail");

	/* Test 2: start == end (should fail) */
	rc = llapi_layout_comp_add_ec(layout, 1, 512*1024*1024ULL,
				      512*1024*1024ULL, 4, 2);
	ASSERTF(rc != 0, "EC component with start == end should fail");

	/* Test 3: start == LUSTRE_EOF (should fail) */
	rc = llapi_layout_comp_add_ec(layout, 1, LUSTRE_EOF, LUSTRE_EOF, 4, 2);
	ASSERTF(rc != 0, "EC component with start == LUSTRE_EOF should fail");

	llapi_layout_free(layout);
}

#define T47_DESC	"verify EC parameter constraints are enforced"
static void test47(void)
{
	int rc;
	struct llapi_layout *layout;

	/* Create a complete layout first */
	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* COMP1: [0, 1GiB] - use max data stripe count for validation test */
	rc = llapi_layout_stripe_count_set(layout, 255);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, 1024*1024*1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* COMP2: [1GiB, EOF] */
	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 6);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 1024*1024*1024ULL,
					  LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Test 1: total ec count > data stripe count (should fail) */
	rc = llapi_layout_comp_add_ec(layout, 1, 0, 1024*1024*1024ULL, 3, 4);
	ASSERTF(rc != 0,
		"EC component with total stripe count > data stripe count should fail");

	rc = llapi_layout_comp_add_ec(layout, 1, 0, 1024*1024*1024ULL, 4, 5);
	ASSERTF(rc != 0, "EC component with cstripe > dstripe should fail");

	/* Test 2: cstripe_count exceeds LOV_EC_MAX_CODING_STRIPES (should
	 * fail)
	 */
	rc = llapi_layout_comp_add_ec(layout, 1, 0, 1024*1024*1024ULL, 255, 16);
	ASSERTF(rc != 0, "EC component with cstripe_count > 15 should fail");

	/* Test 3: dstripe + cstripe > 256 (should fail; Cauchy-over-GF(2^8)
	 * theoretical limit, k+m needs k+m distinct field elements and
	 * GF(2^8) has 256)
	 */
	rc = llapi_layout_comp_add_ec(layout, 1, 0, 1024*1024*1024ULL, 255, 15);
	ASSERTF(rc != 0,
		"EC component with dstripe+cstripe > 256 should fail");

	/* Test 4: Valid parameters at maximum coding stripe limit with sum
	 * exactly at LOV_EC_MAX_TOTAL_STRIPES (should succeed)
	 */
	rc = llapi_layout_comp_add_ec(layout, 1, 0, 1024*1024*1024ULL, 241, 15);
	ASSERTF(rc == 0,
		"EC component with dstripe+cstripe == 256 should succeed");

	llapi_layout_free(layout);
}

#define T48_DESC	"verify duplicate EC components are rejected"
static void test48(void)
{
	int rc;
	struct llapi_layout *layout;

	/* Create layout with data mirror and EC mirror */
	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* Mirror 1: Regular data components */
	/* M1 COMP1: [0, 1GiB] */
	rc = llapi_layout_stripe_count_set(layout, 4);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, 1024*1024*1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M1 COMP2: [1GiB, EOF] */
	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 6);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 1024*1024*1024ULL,
					  LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Mirror 2: EC components */
	/* Add first EC component: [0, 1GiB] */
	rc = llapi_layout_comp_add_ec(layout, 1, 0, 1024*1024*1024ULL, 4, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Try to add duplicate EC component: [0, 1GiB] - should fail */
	rc = llapi_layout_comp_add_ec(layout, 1, 0, 1024*1024*1024ULL, 4, 2);
	ASSERTF(rc != 0, "errno = %d", errno);

	/* Layout should be incomplete - the second EC component is missing */
	rc = llapi_layout_sanity(layout, false, false);
	LAYOUT_ASSERTF(rc != 0, rc,
		       "Layout validation should fail due to being incomplete");

	/* Add non-overlapping EC component: [1GiB, EOF] */
	rc = llapi_layout_comp_add_ec(layout, 1, 1024 * 1024 * 1024ULL,
				      LUSTRE_EOF, 6, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_sanity(layout, false, false);
	LAYOUT_ASSERTF(rc == 0, rc, "Layout validation should be complete now");

	llapi_layout_free(layout);
}

#define T49_DESC	"verify API parameter validation and error handling"
static void test49(void)
{
	int rc;
	struct llapi_layout *layout;

	/* Test 1: NULL layout pointer (should fail) */
	rc = llapi_layout_comp_add_ec(NULL, 1, 0, 1024*1024*1024ULL, 4, 2);
	ASSERTF(rc != 0, "NULL layout should fail but succeeded");

	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 4);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, 1024*1024*1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 6);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 1024*1024*1024ULL,
					  LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Test 2: start == end (zero-length extent, should fail) */
	rc = llapi_layout_comp_add_ec(layout, 1, 1024*1024*1024ULL,
				      1024*1024*1024ULL, 4, 2);
	ASSERTF(rc != 0, "Zero-length extent should fail but succeeded");

	llapi_layout_free(layout);
}

/**
 * __verify_parity_comp() - verify a component's EC parameters and tight binding
 * @layout: layout to verify
 * @expected_dstripe: expected dstripe count
 * @expected_cstripe: expected cstripe count
 * @expected_mirror_link_id: expected mirror link ID this parity binds to
 * @comp_desc: description for error messages
 *
 * Verifies that the given layout component has the expected EC (Erasure Coding)
 * parameters and is properly bound to the expected data mirror. Only parity
 * components have EC parameters set and the LCME_FL_PARITY flag.
 *
 * Note, this should only be called for layouts retrieved from disk to retrieve
 * the permanent binding.
 *
 * Return:
 * * void - function asserts on verification failures
 */
static void __verify_parity_comp(struct llapi_layout *layout,
				 uint8_t expected_dstripe,
				 uint8_t expected_cstripe,
				 uint16_t expected_mirror_link_id,
				 const char *comp_desc)
{
	uint32_t flags;
	uint8_t cstripe, dstripe;
	uint64_t pattern;
	uint16_t mirror_link_id;
	uint32_t comp_id;
	int rc;

	rc = llapi_layout_comp_flags_get(layout, &flags);
	ASSERTF(rc == 0, "%s: failed to get flags, errno = %d", comp_desc,
		errno);

	/* Verify that the component has an ID - meaning it comes from disk */
	rc = llapi_layout_comp_id_get(layout, &comp_id);
	ASSERTF(rc == 0, "%s: failed to get id, errno = %d", comp_desc, errno);
	ASSERTF(comp_id != LCME_ID_INVAL, "%s: comp id is invalid", comp_desc);

	/* EC parameters are only set on parity components */
	ASSERTF(flags & LCME_FL_PARITY,
		"%s: PARITY flag not set on EC component", comp_desc);

	/* Link id should not be set on parity components */
	ASSERTF(!(flags & LCME_FL_IS_LINK_ID),
		"%s: IS_LINK_ID flag set on EC component", comp_desc);

	/* Verify pattern has LOV_PATTERN_PARITY bit set */
	rc = llapi_layout_pattern_get(layout, &pattern);
	ASSERTF(rc == 0, "%s: failed to get pattern, errno = %d", comp_desc,
		errno);
	ASSERTF(pattern & LOV_PATTERN_PARITY,
		"%s: LOV_PATTERN_PARITY not set in pattern 0x%lx", comp_desc,
		pattern);

	rc = llapi_layout_ec_dstripe_count_get(layout, &dstripe);
	ASSERTF(rc == 0, "%s: failed to get dstripe, errno = %d", comp_desc,
		errno);
	ASSERTF(dstripe == expected_dstripe, "%s: dstripe %u != expected %u",
		comp_desc, dstripe, expected_dstripe);

	rc = llapi_layout_ec_cstripe_count_get(layout, &cstripe);
	ASSERTF(rc == 0, "%s: failed to get cstripe, errno = %d", comp_desc,
		errno);
	ASSERTF(cstripe == expected_cstripe, "%s: cstripe %u != expected %u",
		comp_desc, cstripe, expected_cstripe);

	/* Verify tight binding to expected data mirror */
	rc = llapi_layout_comp_mirror_link_id_get(layout, &mirror_link_id);
	ASSERTF(rc == 0, "%s: failed to get mirror_link_id, errno = %d",
		comp_desc, errno);
	ASSERTF(mirror_link_id == expected_mirror_link_id,
		"%s: mirror_link_id %u != expected data mirror ID %u",
		comp_desc, mirror_link_id, expected_mirror_link_id);
}

/**
 * __verify_ec_data_comp() - verify a data component's EC binding
 * @layout: layout to verify
 * @expected_mirror_link_id: expected mirror link ID this data comp binds to
 * @comp_desc: description for error messages
 *
 * Verifies that the given layout component is properly bound to the expected
 * parity mirror.
 *
 * Note, this should only be called for layouts retrieved from disk to retrieve
 * the permanent binding.
 *
 * Return:
 * * void - function asserts on verification failures
 */
static void __verify_ec_data_comp(struct llapi_layout *layout,
				  uint16_t expected_mirror_link_id,
				  const char *comp_desc)
{
	uint32_t flags;
	uint64_t pattern;
	uint16_t mirror_link_id;
	uint32_t comp_id;
	int rc;

	rc = llapi_layout_comp_flags_get(layout, &flags);
	ASSERTF(rc == 0, "%s: failed to get flags, errno = %d", comp_desc,
		errno);

	/* Verify that the component has an ID - meaning it comes from disk */
	rc = llapi_layout_comp_id_get(layout, &comp_id);
	ASSERTF(rc == 0, "%s: failed to get id, errno = %d", comp_desc, errno);
	ASSERTF(comp_id != LCME_ID_INVAL, "%s: comp id is invalid", comp_desc);

	/* EC parameters should not be set on data components */
	ASSERTF(!(flags & LCME_FL_PARITY),
		"%s: PARITY flag set on data component", comp_desc);

	/* Verify pattern does not have LOV_PATTERN_PARITY bit set */
	rc = llapi_layout_pattern_get(layout, &pattern);
	ASSERTF(rc == 0, "%s: failed to get pattern, errno = %d", comp_desc,
		errno);
	ASSERTF(!(pattern & LOV_PATTERN_PARITY),
		"%s: LOV_PATTERN_PARITY set in pattern 0x%lx", comp_desc,
		pattern);

	/* Verify tight binding to expected parity mirror */
	rc = llapi_layout_comp_mirror_link_id_get(layout, &mirror_link_id);
	ASSERTF(rc == 0, "%s: failed to get mirror_link_id, errno = %d",
		comp_desc, errno);
	ASSERTF(mirror_link_id == expected_mirror_link_id,
		"%s: mirror_link_id %u != expected data mirror ID %u",
		comp_desc, mirror_link_id, expected_mirror_link_id);
}

/**
 * __verify_comp_extent() - verify a component's extent
 * @layout: layout to verify
 * @expected_start: expected extent start
 * @expected_end: expected extent end
 * @comp_desc: description for error messages
 *
 * Verifies that the given layout component has the expected extent range
 * by comparing the actual start and end values against the expected values.
 *
 * Return:
 * * void - function asserts on verification failures
 */
static void __verify_comp_extent(struct llapi_layout *layout,
				 uint64_t expected_start, uint64_t expected_end,
				 const char *comp_desc)
{
	uint64_t start, end;
	int rc;

	rc = llapi_layout_comp_extent_get(layout, &start, &end);
	ASSERTF(rc == 0, "%s: failed to get extent, errno = %d", comp_desc,
		errno);
	ASSERTF(start == expected_start,
		"%s: extent start %" PRIu64 " != expected %" PRIu64, comp_desc,
		start, expected_start);
	ASSERTF(end == expected_end,
		"%s: extent end %" PRIu64 " != expected %" PRIu64, comp_desc,
		end, expected_end);
}

#define T50FILE		"f50"
#define T50_DESC	"verify mixed parity mirror is rejected"
static void test50(void)
{
	int rc;
	struct llapi_layout *layout;

	/*
	 * Create layout with data mirror and partial EC mirror.
	 * This should now fail because Mirror 2 mixes non-PARITY
	 * and PARITY components.
	 */
	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* Mirror 1: Regular data components (3 components) */
	/* M1 COMP1: [0, 512MiB] */
	rc = llapi_layout_stripe_count_set(layout, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, 512*1024*1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M1 COMP2: [512MiB, 1GiB] */
	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 4);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 512*1024*1024ULL,
					  1024*1024*1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M1 COMP3: [1GiB, EOF] */
	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 6);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 1024*1024*1024ULL,
					  LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Mirror 2: Mixed mirror - data + EC (should fail validation) */
	/* M2 COMP1: [0, 512MiB] - regular data component */
	rc = llapi_layout_add_first_comp(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, 512*1024*1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M2 EC1: [512MiB, 1GiB] with EC(2,4) */
	rc = llapi_layout_comp_add_ec(layout, 1, 512*1024*1024ULL,
				      1024*1024*1024ULL, 4, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M2 EC2: [1GiB, EOF] with EC(2,6) */
	rc = llapi_layout_comp_add_ec(layout, 1, 1024*1024*1024ULL, LUSTRE_EOF,
				      6, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Set mirror count for the layout (2 mirrors total) */
	rc = llapi_layout_mirror_count_sync(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	/*
	 * Validate the layout - should fail with LSE_EC_MIXED_MIRROR
	 * because Mirror 2 contains both non-PARITY and PARITY components
	 */
	rc = llapi_layout_sanity(layout, false, false);
	LAYOUT_ASSERTF(rc != 0, rc,
		       "Mixed parity mirror should fail validation");

	llapi_layout_free(layout);
}

#define T51FILE		"f51"
#define T51_DESC	"verify parity-first mixed mirror is rejected"
static void test51(void)
{
	int rc;
	struct llapi_layout *layout;

	/*
	 * Test that a mirror starting with PARITY components but also
	 * containing non-PARITY components is rejected. This validates
	 * the parity-agnostic mirror boundary detection and mixed
	 * mirror rejection.
	 */
	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* Mirror 1: Regular data component [0, EOF] */
	rc = llapi_layout_stripe_count_set(layout, 4);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/*
	 * Mirror 2: Create a mixed mirror with both EC and non-EC components.
	 * Start by creating data component, then add EC for same extent,
	 * then add another data component. This creates a mirror where
	 * EC components come before non-EC components in the list.
	 * This should fail validation as a mixed mirror.
	 */
	/* M2 COMP1: [0, 1GiB] - data component */
	rc = llapi_layout_add_first_comp(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 4);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, 1024*1024*1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M3 EC1: [0, 1GiB] with EC(4,2) - EC component for same extent */
	rc = llapi_layout_comp_add_ec(layout, 2, 0, 1024*1024*1024ULL, 4, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M3 COMP2: [1GiB, EOF] - regular data component */
	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 6);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 1024*1024*1024ULL,
					  LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Set mirror count for the layout (3 mirrors total) */
	rc = llapi_layout_mirror_count_sync(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	/*
	 * Validate the layout - should fail with LSE_EC_MIXED_MIRROR
	 * because Mirror 3 starts with PARITY but also has non-PARITY
	 */
	rc = llapi_layout_sanity(layout, false, false);
	LAYOUT_ASSERTF(rc != 0, rc,
		       "Parity-first mixed mirror should fail validation");

	llapi_layout_free(layout);
}

#define T52FILE		"f52"
#define T52_DESC	"create simple EC layout with data and parity components"
static void test52(void)
{
	struct llapi_layout *layout;
	char path[PATH_MAX];
	int fd;
	int rc;

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T52FILE);

	rc = unlink(path);
	ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);

	/* Create a layout with one data mirror and one EC mirror */
	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* Mirror 1: Regular data component [0, EOF] with 6 stripes */
	rc = llapi_layout_stripe_count_set(layout, 6);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Mirror 2: EC component [0, EOF] with EC(2,6) */
	rc = llapi_layout_comp_add_ec(layout, 1, 0, LUSTRE_EOF, 6, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Set mirror count for the layout (2 mirrors total) */
	rc = llapi_layout_mirror_count_sync(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Validate the layout */
	rc = llapi_layout_sanity(layout, false, false);
	LAYOUT_ASSERTF(rc == 0, rc, "errno = %d", errno);

	fd = llapi_layout_file_create(path, 0, 0640, layout);
	ASSERTF(fd >= 0, "errno = %d", errno);

	rc = close(fd);
	ASSERTF(rc == 0, "errno = %d", errno);

	llapi_layout_free(layout);

	/* Verify the created file has the correct layout */
	layout = llapi_layout_get_by_path(path, 0);
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* Move to first component (Mirror 1, data component) */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
	ASSERTF(rc == 0, "errno = %d", errno);

	__verify_comp_extent(layout, 0, LUSTRE_EOF, "M1 data comp");
	/* Note: stripe count may be capped by number of OSTs */

	/* Move to second component (Mirror 2, EC parity component) */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);

	__verify_comp_extent(layout, 0, LUSTRE_EOF, "M2 EC parity");
	/* EC(6,2) - dstripe=6, cstripe=2, binds to M1 */
	__verify_parity_comp(layout, 6, 2, 1, "M2 EC parity");

	llapi_layout_free(layout);
}

#define T53FILE		"f53"
#define T53_DESC	"verify multiple data + multiple parity components layout"
static void test53(void)
{
	int fd;
	int rc;
	struct llapi_layout *layout;
	char path[PATH_MAX];

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T53FILE);

	rc = unlink(path);
	ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);

	/* Create layout with data mirror and EC mirror, each with 2
	 * components
	 */
	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* Mirror 1: Regular data components */
	/* M1 COMP1: [0, 1GiB] with 4 stripes */
	rc = llapi_layout_stripe_count_set(layout, 4);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, 1024*1024*1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M1 COMP2: [1GiB, EOF] with 6 stripes */
	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 6);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 1024*1024*1024ULL,
					  LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Mirror 2: EC components matching Mirror 1 extents */
	/* M2 EC1: [0, 1GiB] with EC(2,4) */
	rc = llapi_layout_comp_add_ec(layout, 1, 0, 1024*1024*1024ULL, 4, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M2 EC2: [1GiB, EOF] with EC(2,6) */
	rc = llapi_layout_comp_add_ec(layout, 1, 1024*1024*1024ULL, LUSTRE_EOF,
				      6, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Set mirror count for the layout (2 mirrors total) */
	rc = llapi_layout_mirror_count_sync(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Validate the layout */
	rc = llapi_layout_sanity(layout, false, false);
	LAYOUT_ASSERTF(rc == 0, rc, "errno = %d", errno);

	fd = llapi_layout_file_create(path, 0, 0640, layout);
	ASSERTF(fd >= 0, "errno = %d", errno);

	rc = close(fd);
	ASSERTF(rc == 0, "errno = %d", errno);

	llapi_layout_free(layout);

	/* Verify the created file has the correct layout */
	layout = llapi_layout_get_by_path(path, 0);
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* M1 COMP1: [0, 1GiB] - data component */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 0, 1024*1024*1024ULL, "M1 COMP1");

	/* M1 COMP2: [1GiB, EOF] - data component */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 1024*1024*1024ULL, LUSTRE_EOF, "M1 COMP2");

	/* M2 EC1: [0, 1GiB] with EC(4,2) - dstripe=4, cstripe=2,
	 * binds to M1 COMP1
	 */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 0, 1024*1024*1024ULL, "M2 EC1");
	__verify_parity_comp(layout, 4, 2, 1, "M2 EC1");

	/* M2 EC2: [1GiB, EOF] with EC(6,2) - dstripe=6, cstripe=2,
	 * binds to M1 COMP2
	 */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 1024*1024*1024ULL, LUSTRE_EOF, "M2 EC2");
	__verify_parity_comp(layout, 6, 2, 1, "M2 EC2");

	llapi_layout_free(layout);
}

#define T54FILE		"f54"
#define T54_DESC	"verify complex multi-component EC layout"
static void test54(void)
{
	int fd;
	int rc;
	struct llapi_layout *layout;
	char path[PATH_MAX];

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T54FILE);

	rc = unlink(path);
	ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);

	/* Create layout with data mirror and EC mirror, each with 4
	 * components
	 */
	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* Mirror 1: Regular data components */
	/* M1 COMP1: [0, 256MiB] with 2 stripes */
	rc = llapi_layout_stripe_count_set(layout, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, 256*1024*1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M1 COMP2: [256MiB, 512MiB] with 4 stripes */
	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 4);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 256*1024*1024ULL,
					  512*1024*1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M1 COMP3: [512MiB, 1GiB] with 6 stripes */
	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 6);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 512*1024*1024ULL,
					  1024*1024*1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M1 COMP4: [1GiB, EOF] with 8 stripes */
	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 8);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 1024*1024*1024ULL,
					  LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Mirror 2: EC components matching Mirror 1 extents */
	/* M2 EC1: [0, 256MiB] with EC(1,2) */
	rc = llapi_layout_comp_add_ec(layout, 1, 0, 256*1024*1024ULL, 2, 1);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M2 EC2: [256MiB, 512MiB] with EC(2,4) */
	rc = llapi_layout_comp_add_ec(layout, 1, 256*1024*1024ULL,
				      512*1024*1024ULL, 4, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M2 EC3: [512MiB, 1GiB] with EC(2,6) */
	rc = llapi_layout_comp_add_ec(layout, 1, 512*1024*1024ULL,
				      1024*1024*1024ULL, 6, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M2 EC4: [1GiB, EOF] with EC(3,8) */
	rc = llapi_layout_comp_add_ec(layout, 1, 1024*1024*1024ULL, LUSTRE_EOF,
				      8, 3);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Set mirror count for the layout (2 mirrors total) */
	rc = llapi_layout_mirror_count_sync(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Validate the layout */
	rc = llapi_layout_sanity(layout, false, false);
	LAYOUT_ASSERTF(rc == 0, rc, "errno = %d", errno);

	fd = llapi_layout_file_create(path, 0, 0640, layout);
	ASSERTF(fd >= 0, "errno = %d", errno);

	rc = close(fd);
	ASSERTF(rc == 0, "errno = %d", errno);

	llapi_layout_free(layout);

	/* Verify the created file has the correct layout */
	layout = llapi_layout_get_by_path(path, 0);
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* M1 COMP1: [0, 256MiB] - data component */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 0, 256*1024*1024ULL, "M1 COMP1");

	/* M1 COMP2: [256MiB, 512MiB] - data component */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 256*1024*1024ULL, 512*1024*1024ULL,
			     "M1 COMP2");

	/* M1 COMP3: [512MiB, 1GiB] - data component */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 512*1024*1024ULL, 1024*1024*1024ULL,
			     "M1 COMP3");

	/* M1 COMP4: [1GiB, EOF] - data component */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 1024*1024*1024ULL, LUSTRE_EOF, "M1 COMP4");

	/* M2 EC1: [0, 256MiB] with EC(2,1) - dstripe=2, cstripe=1,
	 * binds to M1 COMP1
	 */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 0, 256*1024*1024ULL, "M2 EC1");
	__verify_parity_comp(layout, 2, 1, 1, "M2 EC1");

	/* M2 EC2: [256MiB, 512MiB] with EC(4,2) - dstripe=4, cstripe=2,
	 * binds to M1 COMP2
	 */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 256*1024*1024ULL, 512*1024*1024ULL,
			     "M2 EC2");
	__verify_parity_comp(layout, 4, 2, 1, "M2 EC2");

	/* M2 EC3: [512MiB, 1GiB] with EC(6,2) - dstripe=6, cstripe=2,
	 * binds to M1 COMP3
	 */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 512*1024*1024ULL, 1024*1024*1024ULL,
			     "M2 EC3");
	__verify_parity_comp(layout, 6, 2, 1, "M2 EC3");

	/* M2 EC4: [1GiB, EOF] with EC(8,3) - dstripe=8, cstripe=3,
	 * binds to M1 COMP4
	 */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 1024*1024*1024ULL, LUSTRE_EOF, "M2 EC4");
	__verify_parity_comp(layout, 8, 3, 1, "M2 EC4");

	llapi_layout_free(layout);
}

#define T55FILE		"f55"
#define T55_DESC	"verify additional EC component ordering scenarios"
static void test55(void)
{
	int fd;
	int rc;
	struct llapi_layout *layout;
	char path[PATH_MAX];

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T55FILE);

	rc = unlink(path);
	ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);

	/* Create layout: COMP1 COMP2 COMP3 (complete to EOF) */
	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* COMP1: [0, 256MiB] */
	rc = llapi_layout_stripe_count_set(layout, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, 256*1024*1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* COMP2: [256MiB, 512MiB] */
	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 4);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 256*1024*1024ULL,
					  512*1024*1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* COMP3: [512MiB, EOF] */
	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 6);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 512*1024*1024ULL,
					  LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Add EC for all components in order - should work */
	rc = llapi_layout_comp_add_ec(layout, 1, 0, 256*1024*1024ULL, 2, 1);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_add_ec(layout, 1, 256*1024*1024ULL,
				      512*1024*1024ULL, 4, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_add_ec(layout, 1, 512*1024*1024ULL, LUSTRE_EOF,
				      6, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Set mirror count for the layout (2 mirrors total) */
	rc = llapi_layout_mirror_count_sync(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Validate the layout */
	rc = llapi_layout_sanity(layout, false, false);
	LAYOUT_ASSERTF(rc == 0, rc, "errno = %d", errno);

	fd = llapi_layout_file_create(path, 0, 0640, layout);
	ASSERTF(fd >= 0, "errno = %d", errno);

	rc = close(fd);
	ASSERTF(rc == 0, "errno = %d", errno);

	llapi_layout_free(layout);
}

#define T56FILE		"f56"
#define T56_DESC	"verify EC layout with two mirrors - EC only first mirror"
static void test56(void)
{
	int fd;
	int rc;
	struct llapi_layout *layout;
	char path[PATH_MAX];

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T56FILE);

	rc = unlink(path);
	ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);

	/* Create layout with 3 mirrors: data, EC, data */
	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* Mirror 1: Regular data components */
	/* M1 COMP1: [0, 1GiB] with 4 stripes */
	rc = llapi_layout_stripe_count_set(layout, 4);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, 1024*1024*1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M1 COMP2: [1GiB, EOF] with 6 stripes */
	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 6);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 1024*1024*1024ULL,
					  LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Mirror 2: EC components matching Mirror 1 extents */
	/* M2 EC1: [0, 1GiB] with EC(2,4) */
	rc = llapi_layout_comp_add_ec(layout, 1, 0, 1024*1024*1024ULL, 4, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M2 EC2: [1GiB, EOF] with EC(2,6) */
	rc = llapi_layout_comp_add_ec(layout, 1, 1024*1024*1024ULL, LUSTRE_EOF,
				      6, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Mirror 3: Regular data components with different extents */
	rc = llapi_layout_add_first_comp(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M3 COMP1: [0, 2GiB] with 8 stripes */
	rc = llapi_layout_stripe_count_set(layout, 8);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, 2*1024*1024*1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M3 COMP2: [2GiB, EOF] with 10 stripes */
	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 10);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 2*1024*1024*1024ULL,
					  LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Set mirror count for the layout (3 mirrors total) */
	rc = llapi_layout_mirror_count_sync(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Validate the layout */
	rc = llapi_layout_sanity(layout, false, false);
	LAYOUT_ASSERTF(rc == 0, rc, "errno = %d", errno);

	fd = llapi_layout_file_create(path, 0, 0640, layout);
	ASSERTF(fd >= 0, "errno = %d", errno);

	rc = close(fd);
	ASSERTF(rc == 0, "errno = %d", errno);

	llapi_layout_free(layout);

	/* Verify the created file has the correct layout */
	layout = llapi_layout_get_by_path(path, 0);
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* M1 COMP1: [0, 1GiB] - data component */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 0, 1024*1024*1024ULL, "M1 COMP1");

	/* M1 COMP2: [1GiB, EOF] - data component */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 1024*1024*1024ULL, LUSTRE_EOF, "M1 COMP2");

	/* M2 EC1: [0, 1GiB] with EC(4,2) - dstripe=4, cstripe=2,
	 * binds to M1 COMP1
	 */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 0, 1024*1024*1024ULL, "M2 EC1");
	__verify_parity_comp(layout, 4, 2, 1, "M2 EC1");

	/* M2 EC2: [1GiB, EOF] with EC(6,2) - dstripe=6, cstripe=2,
	 * binds to M1 COMP2
	 */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 1024*1024*1024ULL, LUSTRE_EOF, "M2 EC2");
	__verify_parity_comp(layout, 6, 2, 1, "M2 EC2");

	/* M3 COMP1: [0, 2GiB] - regular data component (no EC) */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 0, 2*1024*1024*1024ULL, "M3 COMP1");

	/* M3 COMP2: [2GiB, EOF] - regular data component (no EC) */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 2*1024*1024*1024ULL, LUSTRE_EOF,
			     "M3 COMP2");

	llapi_layout_free(layout);
}

#define T57FILE		"f57"
#define T57_DESC	"verify EC layout with two mirrors - EC both mirrors"
static void test57(void)
{
	int fd;
	int rc;
	struct llapi_layout *layout;
	char path[PATH_MAX];
	__u32 mirror_id;

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T57FILE);

	rc = unlink(path);
	ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);

	/* Create layout with 4 mirrors: data, EC, data, EC */
	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* Mirror 1: Regular data components */
	/* M1 COMP1: [0, 1GiB] with 4 stripes */
	rc = llapi_layout_stripe_count_set(layout, 4);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, 1024*1024*1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M1 COMP2: [1GiB, EOF] with 6 stripes */
	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 6);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 1024*1024*1024ULL,
					  LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_mirror_count_sync(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_mirror_id_get(layout, &mirror_id);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Mirror 2: EC components matching Mirror 1 */
	/* M2 EC1: [0, 1GiB] with EC(2,4) */
	rc = llapi_layout_comp_add_ec(layout, mirror_id, 0,
				      1024 * 1024 * 1024ULL, 4, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M2 EC2: [1GiB, EOF] with EC(2,6) */
	rc = llapi_layout_comp_add_ec(layout, mirror_id, 1024 * 1024 * 1024ULL,
				      LUSTRE_EOF, 6, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Mirror 3: Regular data components (different extents from M1) */
	rc = llapi_layout_add_first_comp(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M3 COMP1: [0, 2GiB] with 8 stripes */
	rc = llapi_layout_stripe_count_set(layout, 8);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, 2*1024*1024*1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M3 COMP2: [2GiB, EOF] with 10 stripes */
	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 10);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 2*1024*1024*1024ULL,
					  LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Mirror 4: EC components matching Mirror 1 (not Mirror 3).
	 * This should fail since a data component can only be protected
	 * by one parity component.
	 *
	 * M4 EC1: [0, 1GiB] with EC(2,4) - matches M1 COMP1
	 */
	rc = llapi_layout_comp_add_ec(layout, mirror_id, 0, 1024*1024*1024ULL,
				      4, 2);
	ASSERTF(rc != 0, "errno = %d", errno);

	/* M4 EC2: [1GiB, EOF] with EC(2,6) - matches M1 COMP2 - should fail */
	rc = llapi_layout_comp_add_ec(layout, mirror_id, 1024*1024*1024ULL,
				      LUSTRE_EOF, 6, 2);
	ASSERTF(rc != 0, "errno = %d", errno);

	/* Set mirror count for the layout (3 mirrors total) */
	rc = llapi_layout_mirror_count_sync(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Validate the layout */
	rc = llapi_layout_sanity(layout, false, false);
	LAYOUT_ASSERTF(rc == 0, rc, "errno = %d", errno);

	fd = llapi_layout_file_create(path, 0, 0640, layout);
	ASSERTF(fd >= 0, "errno = %d", errno);

	rc = close(fd);
	ASSERTF(rc == 0, "errno = %d", errno);

	llapi_layout_free(layout);

	/* Verify the created file has the correct layout */
	layout = llapi_layout_get_by_path(path, 0);
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* M1 COMP1: [0, 1GiB] - data component */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 0, 1024*1024*1024ULL, "M1 COMP1");

	/* M1 COMP2: [1GiB, EOF] - data component */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 1024*1024*1024ULL, LUSTRE_EOF, "M1 COMP2");

	/* M2 EC1: [0, 1GiB] with EC(4,2) - dstripe=4, cstripe=2,
	 * binds to M1 COMP1
	 */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 0, 1024*1024*1024ULL, "M2 EC1");
	__verify_parity_comp(layout, 4, 2, 1, "M2 EC1");

	/* M2 EC2: [1GiB, EOF] with EC(6,2) - dstripe=6, cstripe=2,
	 * binds to M1 COMP2
	 */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 1024*1024*1024ULL, LUSTRE_EOF, "M2 EC2");
	__verify_parity_comp(layout, 6, 2, 1, "M2 EC2");

	/* M3 COMP1: [0, 2GiB] - regular data component (no EC) */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 0, 2*1024*1024*1024ULL, "M3 COMP1");

	/* M3 COMP2: [2GiB, EOF] - regular data component (no EC) */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 2*1024*1024*1024ULL, LUSTRE_EOF,
			     "M3 COMP2");

	llapi_layout_free(layout);
}

#define T58FILE		"f58"
#define T58_DESC	"verify three mirrors with different extents - EC first only"
static void test58(void)
{
	int fd;
	int rc;
	struct llapi_layout *layout;
	char path[PATH_MAX];

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T58FILE);

	rc = unlink(path);
	ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);

	/* Create layout with 4 mirrors with different extent structures */
	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* Mirror 1: Regular data components with 3 components */
	/* M1 COMP1: [0, 512MiB] with 2 stripes */
	rc = llapi_layout_stripe_count_set(layout, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, 512*1024*1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M1 COMP2: [512MiB, 1GiB] with 4 stripes */
	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 4);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 512*1024*1024ULL,
					  1024*1024*1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M1 COMP3: [1GiB, EOF] with 6 stripes */
	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 6);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 1024*1024*1024ULL,
					  LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Mirror 2: EC components matching Mirror 1 extents */
	/* M2 EC1: [0, 512MiB] with EC(1,2) */
	rc = llapi_layout_comp_add_ec(layout, 1, 0, 512*1024*1024ULL, 2, 1);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M2 EC2: [512MiB, 1GiB] with EC(2,4) */
	rc = llapi_layout_comp_add_ec(layout, 1, 512*1024*1024ULL,
				      1024*1024*1024ULL, 4, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M2 EC3: [1GiB, EOF] with EC(2,6) */
	rc = llapi_layout_comp_add_ec(layout, 1, 1024*1024*1024ULL, LUSTRE_EOF,
				      6, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Mirror 3: Regular data with different extents (2 components) */
	rc = llapi_layout_add_first_comp(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M3 COMP1: [0, 2GiB] with 8 stripes */
	rc = llapi_layout_stripe_count_set(layout, 8);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, 2*1024*1024*1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M3 COMP2: [2GiB, EOF] with 10 stripes */
	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 10);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 2*1024*1024*1024ULL,
					   LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Mirror 4: Regular data with single component */
	rc = llapi_layout_add_first_comp(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M4 COMP1: [0, EOF] with 12 stripes (single component) */
	rc = llapi_layout_stripe_count_set(layout, 12);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Set mirror count for the layout (4 mirrors total) */
	rc = llapi_layout_mirror_count_sync(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Validate the layout */
	rc = llapi_layout_sanity(layout, false, false);
	LAYOUT_ASSERTF(rc == 0, rc, "errno = %d", errno);

	fd = llapi_layout_file_create(path, 0, 0640, layout);
	ASSERTF(fd >= 0, "errno = %d", errno);

	rc = close(fd);
	ASSERTF(rc == 0, "errno = %d", errno);

	llapi_layout_free(layout);

	/* Verify the created file has the correct layout */
	layout = llapi_layout_get_by_path(path, 0);
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* M1 COMP1: [0, 512MiB] - data component */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 0, 512*1024*1024ULL, "M1 COMP1");

	/* M1 COMP2: [512MiB, 1GiB] - data component */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 512*1024*1024ULL, 1024*1024*1024ULL,
			     "M1 COMP2");

	/* M1 COMP3: [1GiB, EOF] - data component */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 1024*1024*1024ULL, LUSTRE_EOF,
			     "M1 COMP3");

	/* M2 EC1: [0, 512MiB] with EC(2,1) - dstripe=2, cstripe=1,
	 * binds to M1 COMP1
	 */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 0, 512*1024*1024ULL, "M2 EC1");
	__verify_parity_comp(layout, 2, 1, 1, "M2 EC1");

	/* M2 EC2: [512MiB, 1GiB] with EC(4,2) - dstripe=4, cstripe=2,
	 * binds to M1 COMP2
	 */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 512*1024*1024ULL, 1024*1024*1024ULL,
			     "M2 EC2");
	__verify_parity_comp(layout, 4, 2, 1, "M2 EC2");

	/* M2 EC3: [1GiB, EOF] with EC(6,2) - dstripe=6, cstripe=2,
	 * binds to M1 COMP3
	 */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 1024*1024*1024ULL, LUSTRE_EOF, "M2 EC3");
	__verify_parity_comp(layout, 6, 2, 1, "M2 EC3");

	/* M3 COMP1: [0, 2GiB] - regular data component (no EC) */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 0, 2*1024*1024*1024ULL, "M3 COMP1");

	/* M3 COMP2: [2GiB, EOF] - regular data component (no EC) */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 2*1024*1024*1024ULL, LUSTRE_EOF,
			     "M3 COMP2");

	/* M4 COMP1: [0, EOF] - regular data component (no EC) */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 0, LUSTRE_EOF, "M4 COMP1");

	llapi_layout_free(layout);
}

#define T59FILE		"f59"
#define T59_DESC	"EC verify mirror count and data-parity comp linkage"
static void test59(void)
{
	struct llapi_layout *layout;
	uint16_t link_id;
	uint32_t mirror_id;
	int rc;

	/*
	 * Create 2 data mirrors and 1 parity mirrors protecting the first
	 * data mirror. The linkage between the data and parity components will
	 * be verified by the test. In addition, this test verifies that
	 * removing EC components also removes the linkage and affects the
	 * mirror count correctly.
	 */
	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* Mirror 1: Regular data components */
	/* M1 COMP1: [0, 1GiB] with 4 stripes */
	rc = llapi_layout_stripe_count_set(layout, 4);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, 1024 * 1024 * 1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M1 COMP2: [1GiB, EOF] with 8 stripes */
	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 8);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 1024 * 1024 * 1024ULL,
					  LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Mirror 2: Regular data components */
	rc = llapi_layout_add_first_comp(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M2 COMP1: [0, 1GiB] with 4 stripes */
	rc = llapi_layout_stripe_count_set(layout, 4);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, 1024 * 1024 * 1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M2 COMP2: [1GiB, EOF] with 8 stripes */
	rc = llapi_layout_comp_add(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 8);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 1024 * 1024 * 1024ULL,
					  LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Get mirror id from first component and first mirror */
	rc = llapi_layout_mirror_count_sync(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_mirror_id_get(layout, &mirror_id);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Mirror 3: Parity components */
	/* M3 EC1: [0, 1GiB] with EC(2,1) */
	rc = llapi_layout_comp_add_ec(layout, mirror_id, 0,
				      1024 * 1024 * 1024ULL, 2, 1);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Check link id is set on first data component */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_mirror_link_id_get(layout, &link_id);
	ASSERTF(rc == 0, "errno = %d", errno);
	ASSERTF(link_id != LLAPI_MIRROR_LINK_NONE, "link_id = %d", link_id);

	/* M3 EC2: [1GiB, EOF] with EC(2,1) */
	rc = llapi_layout_comp_add_ec(layout, mirror_id, 1024 * 1024 * 1024ULL,
				      LUSTRE_EOF, 2, 1);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Check link id is set on second data component */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_mirror_link_id_get(layout, &link_id);
	ASSERTF(rc == 0, "errno = %d", errno);
	ASSERTF(link_id != LLAPI_MIRROR_LINK_NONE, "link_id = %d", link_id);

	/* Validate the layout */
	rc = llapi_layout_sanity(layout, false, false);
	LAYOUT_ASSERTF(rc == 0, rc, "errno = %d", errno);

	/* Move current component to last */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_LAST);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Delete last EC component */
	rc = llapi_layout_comp_del(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* check link_id on M1 COMP2 was cleared */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_mirror_link_id_get(layout, &link_id);
	ASSERTF(rc == 0, "errno = %d", errno);
	ASSERTF(link_id == LLAPI_MIRROR_LINK_NONE, "link_id = %d", link_id);

	/* Verifying the layout fails due to being incomplete */
	rc = llapi_layout_sanity(layout, false, false);
	LAYOUT_ASSERTF(rc != 0, rc, "Layout should be incomplete");

	/* Delete next last EC component */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_LAST);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_del(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* check link_id on first component was cleared */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_mirror_link_id_get(layout, &link_id);
	ASSERTF(rc == 0, "errno = %d", errno);
	ASSERTF(link_id == LLAPI_MIRROR_LINK_NONE, "link_id = %d", link_id);

	/* Verifying the layout fails due to wrong mirror count */
	rc = llapi_layout_sanity(layout, false, false);
	LAYOUT_ASSERTF(rc != 0, rc, "Layout should have wrong mirror count");

	/* Set correct mirror count */
	rc = llapi_layout_mirror_count_sync(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_sanity(layout, false, false);
	LAYOUT_ASSERTF(rc == 0, rc, "errno = %d", errno);

	llapi_layout_free(layout);
}

#define T60FILE		"f60"
#define T60_DESC	"verify EC component stripe size matches data component"
static void test60(void)
{
	int fd;
	int rc;
	struct llapi_layout *layout, *filelayout;
	uint64_t data_stripe_size, ec_stripe_size;
	char path[PATH_MAX];

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T60FILE);

	rc = unlink(path);
	ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);

	/* Create a layout with one data component with specific stripe size */
	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* Set stripe count and size for data component */
	rc = llapi_layout_stripe_count_set(layout, 6);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_size_set(layout, 4*1024*1024);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Add EC component with matching extent */
	rc = llapi_layout_comp_add_ec(layout, 1, 0, LUSTRE_EOF, 6, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Navigate to first component and get its stripe size */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_size_get(layout, &data_stripe_size);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Navigate to second component (EC) and get its stripe size */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_size_get(layout, &ec_stripe_size);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Verify stripe sizes match */
	ASSERTF(data_stripe_size == ec_stripe_size,
		"EC stripe size %lu should match data stripe size %lu",
		ec_stripe_size, data_stripe_size);

	/* Try to set a different stripe size on EC component - should fail */
	rc = llapi_layout_stripe_size_set(layout, 1*1024*1024);
	ASSERTF(rc != 0 && errno == EINVAL,
		"Setting stripe size on EC component should fail with EINVAL");

	/* Set mirror count for the layout (2 mirrors total) */
	rc = llapi_layout_mirror_count_sync(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Validate the layout */
	rc = llapi_layout_sanity(layout, false, false);
	LAYOUT_ASSERTF(rc == 0, rc, "errno = %d", errno);

	/* Create the file with this layout */
	fd = llapi_layout_file_create(path, 0, 0640, layout);
	ASSERTF(fd >= 0, "errno = %d", errno);

	rc = close(fd);
	ASSERTF(rc == 0, "errno = %d", errno);

	llapi_layout_free(layout);

	/* Read the layout back from the file and verify stripe sizes match */
	filelayout = llapi_layout_get_by_path(path, 0);
	ASSERTF(filelayout != NULL, "errno = %d", errno);

	/* Navigate to first component and get its stripe size */
	rc = llapi_layout_comp_use(filelayout, LLAPI_LAYOUT_COMP_USE_FIRST);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_size_get(filelayout, &data_stripe_size);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Navigate to second component (EC) and get its stripe size */
	rc = llapi_layout_comp_use(filelayout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_size_get(filelayout, &ec_stripe_size);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Verify stripe sizes match in the file layout */
	ASSERTF(data_stripe_size == ec_stripe_size,
		"File EC stripe size %lu should match data stripe size %lu",
		ec_stripe_size, data_stripe_size);

	ASSERTF(data_stripe_size == 4*1024*1024,
		"File data stripe size %lu should be 4MiB",
		data_stripe_size);

	llapi_layout_free(filelayout);
}

#define T61FILE		"f61"
#define T61_DESC	"verify a protected data component can't be deleted"
static void test61(void)
{
	struct llapi_layout *layout;
	int rc;

	/*
	 * Create a layout with a data component and an EC parity component
	 * protecting it. Then verify that attempting to delete the protected
	 * data component fails.
	 */
	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* M1: Create data component [0, EOF] with 6 stripes */
	rc = llapi_layout_stripe_count_set(layout, 6);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M2: Add EC parity component to protect the data component */
	rc = llapi_layout_comp_add_ec(layout, 1, 0, LUSTRE_EOF, 6, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Attempt to delete the protected data component - should fail */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_del(layout);
	ASSERTF(rc != 0,
		"Deleting protected data component should fail but succeeded");
	ASSERTF(errno == EINVAL,
		"Expected EINVAL when deleting protected data component, got %d",
		errno);

	/* Delete parity component first - should succeed*/
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_LAST);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_del(layout);
	ASSERTF(rc == 0, "Deleting parity component should succeed, errno = %d",
		errno);

	/* Now deleting data component - should succeed */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
	ASSERTF(rc == 0, "errno = %d", errno);

	llapi_layout_free(layout);
}

#define T62FILE		"f62"
#define T62_DESC	"verify multiple parity mirrors for one data mirror is rejected"
static void test62(void)
{
	struct llapi_layout *layout;
	int rc;

	/* Attempt to create a layout with 5 mirrors:
	 * DATA1, PARITY1, DATA2, PARITY2, PARITY1.2
	 * Creating PARITY1.2 components should fail because a data component
	 * can only be protected by one parity component.
	 */
	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* Mirror 1 (DATA1): [0, EOF] with 4 stripes */
	rc = llapi_layout_stripe_count_set(layout, 4);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Mirror 2 (PARITY1): [0, EOF] with EC(4,2) - bind to M1 */
	rc = llapi_layout_comp_add_ec(layout, 1, 0, LUSTRE_EOF, 4, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Mirror 3 (DATA2): [0, EOF] with 6 stripes */
	rc = llapi_layout_add_first_comp(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 6);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Mirror 4 (PARITY2): [0, EOF] with EC(6,2) - bind to M3 */
	rc = llapi_layout_comp_add_ec(layout, 3, 0, LUSTRE_EOF, 6, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Mirror 5 (PARITY1.2): [0, EOF] with EC(4,3) - bind to M1 */
	/* this should fail because M1 is already bound to M2 */
	rc = llapi_layout_comp_add_ec(layout, 3, 0, LUSTRE_EOF, 4, 3);
	ASSERTF(rc != 0, "errno = %d", errno);

	/* Validate the layout */
	rc = llapi_layout_sanity(layout, false, false);
	LAYOUT_ASSERTF(rc == 0, rc, "errno = %d", errno);

	llapi_layout_free(layout);
}

#define T63FILE		"f63"
#define T63_DESC	"verify DATA1, PARITY1, DATA2, PARITY2 binding"
static void test63(void)
{
	struct llapi_layout *layout;
	char path[PATH_MAX];
	int fd;
	int rc;

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T63FILE);

	rc = unlink(path);
	ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);

	/* Create layout with 4 mirrors: DATA1, PARITY1, DATA2, PARITY2 */
	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* Mirror 1 (DATA1): [0, EOF] with 4 stripes */
	rc = llapi_layout_stripe_count_set(layout, 4);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Mirror 2 (PARITY1): [0, EOF] with EC(4,2) - bind to M1 */
	rc = llapi_layout_comp_add_ec(layout, 1, 0, LUSTRE_EOF, 4, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Mirror 3 (DATA2): [0, EOF] with 6 stripes */
	rc = llapi_layout_add_first_comp(layout);
	ASSERTF(rc == 0, "llapi_layout_add_first_comp failed: errno = %d",
		errno);

	rc = llapi_layout_stripe_count_set(layout, 6);
	ASSERTF(rc == 0, "llapi_layout_stripe_count_set failed: errno = %d",
		errno);

	rc = llapi_layout_comp_extent_set(layout, 0, LUSTRE_EOF);
	ASSERTF(rc == 0, "llapi_layout_comp_extent_set failed: errno = %d",
		errno);

	/* Mirror 4 (PARITY2): [0, EOF] with EC(6,2) - bind to M3 */
	rc = llapi_layout_comp_add_ec(layout, 3, 0, LUSTRE_EOF, 6, 2);
	ASSERTF(rc == 0, "llapi_layout_comp_add_ec failed: errno = %d", errno);

	/* Set mirror count for the layout (4 mirrors total) */
	rc = llapi_layout_mirror_count_sync(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Validate the layout */
	rc = llapi_layout_sanity(layout, false, false);
	LAYOUT_ASSERTF(rc == 0, rc, "errno = %d", errno);

	fd = llapi_layout_file_create(path, 0, 0640, layout);
	ASSERTF(fd >= 0, "errno = %d", errno);

	rc = close(fd);
	ASSERTF(rc == 0, "errno = %d", errno);

	llapi_layout_free(layout);

	/* Verify the created file has the correct layout and bindings */
	layout = llapi_layout_get_by_path(path, 0);
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* M1 DATA1: [0, EOF] - regular data component */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 0, LUSTRE_EOF, "M1 DATA1");
	__verify_ec_data_comp(layout, 2, "M1 DATA1");

	/* M2 PARITY1: [0, EOF] with EC(4,2) - binds to M1 */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 0, LUSTRE_EOF, "M2 PARITY1");
	__verify_parity_comp(layout, 4, 2, 1, "M2 PARITY1");

	/* M3 DATA2: [0, EOF] - regular data component */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 0, LUSTRE_EOF, "M3 DATA2");
	__verify_ec_data_comp(layout, 4, "M3 DATA2");

	/* M4 PARITY2: [0, EOF] with EC(6,2) - binds to M3 */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 0, LUSTRE_EOF, "M4 PARITY2");
	__verify_parity_comp(layout, 6, 2, 3, "M4 PARITY2");

	llapi_layout_free(layout);
}

#define T64FILE		"f64"
#define T64_DESC	"verify DATA1, DATA2, PARITY1, PARITY2 binding"
static void test64(void)
{
	int fd;
	int rc;
	struct llapi_layout *layout;
	char path[PATH_MAX];

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T64FILE);

	rc = unlink(path);
	ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);

	/* Create layout with 4 mirrors: DATA1, DATA2, PARITY1, PARITY2 */
	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* Mirror 1 (DATA1): [0, EOF] with 4 stripes */
	rc = llapi_layout_stripe_count_set(layout, 4);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Mirror 2 (DATA2): [0, EOF] with 6 stripes */
	rc = llapi_layout_add_first_comp(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout, 6);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Mirror 3 (PARITY1): [0, EOF] with EC(4,2) - bind to M1 */
	rc = llapi_layout_comp_add_ec(layout, 1, 0, LUSTRE_EOF, 4, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Mirror 4 (PARITY2): [0, EOF] with EC(6,2) - bind to M2 */
	rc = llapi_layout_comp_add_ec(layout, 2, 0, LUSTRE_EOF, 6, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Set mirror count for the layout (5 mirrors total) */
	rc = llapi_layout_mirror_count_sync(layout);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Validate the layout */
	rc = llapi_layout_sanity(layout, false, false);
	LAYOUT_ASSERTF(rc == 0, rc, "errno = %d", errno);

	fd = llapi_layout_file_create(path, 0, 0640, layout);
	ASSERTF(fd >= 0, "errno = %d", errno);

	rc = close(fd);
	ASSERTF(rc == 0, "errno = %d", errno);

	llapi_layout_free(layout);

	/* Verify the created file has the correct layout and bindings */
	layout = llapi_layout_get_by_path(path, 0);
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* M1 DATA1: [0, EOF] - regular data component */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 0, LUSTRE_EOF, "M1 DATA1");
	__verify_ec_data_comp(layout, 3, "M1 DATA1");

	/* M2 DATA2: [0, EOF] - regular data component */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 0, LUSTRE_EOF, "M3 DATA2");
	__verify_ec_data_comp(layout, 4, "M2 DATA2");

	/* M3 PARITY1: [0, EOF] with EC(4,2) - binds to M1 */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 0, LUSTRE_EOF, "M2 PARITY1");
	__verify_parity_comp(layout, 4, 2, 1, "M2 PARITY1");

	/* M4 PARITY2: [0, EOF] with EC(6,2) - binds to M3 */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(layout, 0, LUSTRE_EOF, "M4 PARITY2");
	__verify_parity_comp(layout, 6, 2, 2, "M4 PARITY2");

	llapi_layout_free(layout);
}

#define T65FILE		"f65"
#define T65_DESC	"verify layout merge preserves EC data-parity bindings"
static void test65(void)
{
	int fd;
	int rc;
	struct llapi_layout *layout1, *layout2, *merged_layout, *filelayout;
	char path[PATH_MAX];

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T65FILE);

	rc = unlink(path);
	ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);

	/*
	 * Create layout1: DATA1 (2 comps) + PARITY1 (2 comps)
	 *   M1 (DATA1):   [0, 1GiB] + [1GiB, EOF]
	 *   M2 (PARITY1): [0, 1GiB] EC(4,2) + [1GiB, EOF] EC(4,2): binds to M1
	 */
	layout1 = llapi_layout_alloc();
	ASSERTF(layout1 != NULL, "errno = %d", errno);

	/* M1 DATA1 comp1: [0, 1GiB] with 4 stripes */
	rc = llapi_layout_stripe_count_set(layout1, 4);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout1, 0, 1024 * 1024 * 1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M1 DATA1 comp2: [1GiB, EOF] with 4 stripes */
	rc = llapi_layout_comp_add(layout1);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout1, 4);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout1, 1024 * 1024 * 1024ULL,
					  LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M2 PARITY1 comp1: [0, 1GiB] with EC(4,2) - bind to M1 */
	rc = llapi_layout_comp_add_ec(layout1, 1, 0, 1024 * 1024 * 1024ULL, 4,
				      2);
	ASSERTF(rc == 0, "llapi_layout_comp_add_ec failed: errno = %d", errno);

	/* M2 PARITY1 comp2: [1GiB, EOF] with EC(4,2) - bind to M1 */
	rc = llapi_layout_comp_add_ec(layout1, 1, 1024 * 1024 * 1024ULL,
				      LUSTRE_EOF, 4, 2);
	ASSERTF(rc == 0, "llapi_layout_comp_add_ec failed: errno = %d", errno);

	/*
	 * Create layout2: DATA1 (2 comps) + DATA2 (2 comps) + PARITY1 (2 comps)
	 *   M1 (DATA1):   [0, 1GiB] + [1GiB, EOF]
	 *   M2 (DATA2):   [0, 512MiB] + [512MiB, EOF]
	 *   M3 (PARITY1): [0, 1GiB] EC(6,2) + [1GiB, EOF] EC(6,2): binds to M1
	 */
	layout2 = llapi_layout_alloc();
	ASSERTF(layout2 != NULL, "errno = %d", errno);

	/* M1 DATA1 comp1: [0, 1GiB] with 6 stripes */
	rc = llapi_layout_stripe_count_set(layout2, 6);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout2, 0, 1024 * 1024 * 1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M1 DATA1 comp2: [1GiB, EOF] with 6 stripes */
	rc = llapi_layout_comp_add(layout2);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout2, 6);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout2, 1024 * 1024 * 1024ULL,
					  LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M2 DATA2 comp1: [0, 512MiB] with 8 stripes */
	rc = llapi_layout_add_first_comp(layout2);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout2, 8);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout2, 0, 512 * 1024 * 1024ULL);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M2 DATA2 comp2: [512MiB, EOF] with 8 stripes */
	rc = llapi_layout_comp_add(layout2);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_stripe_count_set(layout2, 8);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout2, 512 * 1024 * 1024ULL,
					  LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* M3 PARITY1 comp1: [0, 1GiB] with EC(6,2) - bind to M1 DATA1 */
	rc = llapi_layout_comp_add_ec(layout2, 1, 0, 1024 * 1024 * 1024ULL, 6,
				      2);
	ASSERTF(rc == 0, "llapi_layout_comp_add_ec failed: errno = %d", errno);

	/* M3 PARITY1 comp2: [1GiB, EOF] with EC(6,2) - bind to M1 DATA1 */
	rc = llapi_layout_comp_add_ec(layout2, 1, 1024 * 1024 * 1024ULL,
				      LUSTRE_EOF, 6, 2);
	ASSERTF(rc == 0, "llapi_layout_comp_add_ec failed: errno = %d", errno);

	/* Merge layout2 into layout1 */
	merged_layout = layout1;
	rc = llapi_layout_merge(&merged_layout, layout2);
	ASSERTF(rc == 0, "llapi_layout_merge failed: errno = %d", errno);

	/* Validate the merged layout */
	rc = llapi_layout_sanity(merged_layout, false, false);
	LAYOUT_ASSERTF(rc == 0, rc, "llapi_layout_sanity failed: errno = %d",
		       errno);

	/* Create file with merged layout */
	fd = llapi_layout_file_create(path, 0, 0640, merged_layout);
	ASSERTF(fd >= 0, "llapi_layout_file_create failed: errno = %d", errno);

	rc = close(fd);
	ASSERTF(rc == 0, "errno = %d", errno);

	llapi_layout_free(merged_layout);
	llapi_layout_free(layout2);

	/* Verify the created file has the correct layout and bindings */
	filelayout = llapi_layout_get_by_path(path, 0);
	ASSERTF(filelayout != NULL,
		"llapi_layout_get_by_path failed: errno = %d", errno);

	/*
	 * Expected layout after merge (10 components, 5 mirrors):
	 *   From layout1:
	 *     M1 (DATA1):   [0, 1GiB] + [1GiB, EOF] -> binds to M2
	 *     M2 (PARITY1): [0, 1GiB] + [1GiB, EOF] EC(4,2) -> binds to M1
	 *   From layout2 (mirror IDs offset by 2):
	 *     M3 (DATA1):   [0, 1GiB] + [1GiB, EOF] -> binds to M5
	 *     M4 (DATA2):   [0, 512MiB] + [512MiB, EOF] -> no EC binding
	 *     M5 (PARITY1): [0, 1GiB] + [1GiB, EOF] EC(6,2) -> binds to M3
	 */

	/* M1 DATA1 comp1: [0, 1GiB] */
	rc = llapi_layout_comp_use(filelayout, LLAPI_LAYOUT_COMP_USE_FIRST);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(filelayout, 0, 1024 * 1024 * 1024ULL,
			     "M1 DATA1 comp1");
	__verify_ec_data_comp(filelayout, 2, "M1 DATA1 comp1");

	/* M1 DATA1 comp2: [1GiB, EOF] */
	rc = llapi_layout_comp_use(filelayout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(filelayout, 1024 * 1024 * 1024ULL, LUSTRE_EOF,
			     "M1 DATA1 comp2");
	__verify_ec_data_comp(filelayout, 2, "M1 DATA1 comp2");

	/* M2 PARITY1 comp1: [0, 1GiB] with EC(4,2) - binds to M1 */
	rc = llapi_layout_comp_use(filelayout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(filelayout, 0, 1024 * 1024 * 1024ULL,
			     "M2 PARITY1 comp1");
	__verify_parity_comp(filelayout, 4, 2, 1, "M2 PARITY1 comp1");

	/* M2 PARITY1 comp2: [1GiB, EOF] with EC(4,2) - binds to M1 */
	rc = llapi_layout_comp_use(filelayout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(filelayout, 1024 * 1024 * 1024ULL, LUSTRE_EOF,
			     "M2 PARITY1 comp2");
	__verify_parity_comp(filelayout, 4, 2, 1, "M2 PARITY1 comp2");

	/* M3 DATA1 comp1: [0, 1GiB] - from layout2 */
	rc = llapi_layout_comp_use(filelayout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(filelayout, 0, 1024 * 1024 * 1024ULL,
			     "M3 DATA1 comp1");
	__verify_ec_data_comp(filelayout, 5, "M3 DATA1 comp1");

	/* M3 DATA1 comp2: [1GiB, EOF] - from layout2 */
	rc = llapi_layout_comp_use(filelayout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(filelayout, 1024 * 1024 * 1024ULL, LUSTRE_EOF,
			     "M3 DATA1 comp2");
	__verify_ec_data_comp(filelayout, 5, "M3 DATA1 comp2");

	/* M4 DATA2 comp1: [0, 512MiB] - from layout2, no EC binding */
	rc = llapi_layout_comp_use(filelayout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(filelayout, 0, 512 * 1024 * 1024ULL,
			     "M4 DATA2 comp1");
	__verify_ec_data_comp(filelayout, 0, "M4 DATA2 comp1");

	/* M4 DATA2 comp2: [512MiB, EOF] - from layout2, no EC binding */
	rc = llapi_layout_comp_use(filelayout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(filelayout, 512 * 1024 * 1024ULL, LUSTRE_EOF,
			     "M4 DATA2 comp2");
	__verify_ec_data_comp(filelayout, 0, "M4 DATA2 comp2");

	/* M5 PARITY1 comp1: [0, 1GiB] with EC(6,2) - binds to M3 */
	rc = llapi_layout_comp_use(filelayout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(filelayout, 0, 1024 * 1024 * 1024ULL,
			     "M5 PARITY1 comp1");
	__verify_parity_comp(filelayout, 6, 2, 3, "M5 PARITY1 comp1");

	/* M5 PARITY1 comp2: [1GiB, EOF] with EC(6,2) - binds to M3 */
	rc = llapi_layout_comp_use(filelayout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);
	__verify_comp_extent(filelayout, 1024 * 1024 * 1024ULL, LUSTRE_EOF,
			     "M5 PARITY1 comp2");
	__verify_parity_comp(filelayout, 6, 2, 3, "M5 PARITY1 comp2");

	llapi_layout_free(filelayout);
}

#define T66_DESC	"EC mirror can override copied pool via llapi"
static void test66(void)
{
	struct llapi_layout *layout;
	char data_pool[LOV_MAXPOOLNAME + 1] = { '\0' };
	char ec_pool[LOV_MAXPOOLNAME + 1] = { '\0' };
	char pool_override[LOV_MAXPOOLNAME * 2 + 1];
	int rc;

	/* Use the actual pool name with fsname prefix for override */
	snprintf(pool_override, sizeof(pool_override), "lustre.%s", poolname);

	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* Mirror 1: data mirror on pool "poolname" */
	rc = llapi_layout_stripe_count_set(layout, 4);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_pool_name_set(layout, poolname);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Mirror 2: EC mirror initially copies data mirror pool */
	rc = llapi_layout_comp_add_ec(layout, 1, 0, LUSTRE_EOF, 4, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_mirror_count_set(layout, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Verify data mirror pool */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_pool_name_get(layout, data_pool,
					  sizeof(data_pool));
	ASSERTF(rc == 0, "errno = %d", errno);
	ASSERTF(strcmp(data_pool, poolname) == 0,
		"data mirror pool %s != %s", data_pool, poolname);

	/* Verify EC mirror initially has copied pool */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_pool_name_get(layout, ec_pool, sizeof(ec_pool));
	ASSERTF(rc == 0, "errno = %d", errno);
	ASSERTF(strcmp(ec_pool, poolname) == 0,
		"EC mirror initial pool %s != %s", ec_pool, poolname);

	/* Override pool only on EC mirror using fsname.pool format */
	rc = llapi_layout_pool_name_set(layout, pool_override);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_pool_name_get(layout, ec_pool, sizeof(ec_pool));
	ASSERTF(rc == 0, "errno = %d", errno);
	ASSERTF(strcmp(ec_pool, poolname) == 0,
		"EC mirror pool %s != %s", ec_pool, poolname);

	/* Data mirror pool must remain unchanged */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_pool_name_get(layout, data_pool,
					  sizeof(data_pool));
	ASSERTF(rc == 0, "errno = %d", errno);
	ASSERTF(strcmp(data_pool, poolname) == 0,
		"data mirror pool changed to %s", data_pool);

	llapi_layout_free(layout);
}

#define T67FILE		"f67"
#define T67_DESC	"verify server blocks prefer flags on parity components"
static void test67(void)
{
	struct llapi_layout *layout;
	char path[PATH_MAX];
	uint32_t flags;
	uint32_t comp_id;
	uint32_t ids[1];
	uint32_t flags_array[1];
	int fd;
	int rc;

	/* EC requires at least 3 OSTs (2 data + 1 parity minimum) */
	if (num_osts < 3) {
		fprintf(stderr, "test64 requires at least 3 OSTs, skipping\n");
		return;
	}

	snprintf(path, sizeof(path), "%s/%s", lustre_dir, T67FILE);

	rc = unlink(path);
	ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno);

	/* Create a layout with data mirror and EC parity mirror */
	layout = llapi_layout_alloc();
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* Mirror 1: Regular data component [0, EOF] */
	rc = llapi_layout_stripe_count_set(layout, 2);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_extent_set(layout, 0, LUSTRE_EOF);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Mirror 2: EC parity component [0, EOF] with EC(2,1) */
	rc = llapi_layout_comp_add_ec(layout, 1, 0, LUSTRE_EOF, 2, 1);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Create the file */
	fd = llapi_layout_file_create(path, 0, 0644, layout);
	ASSERTF(fd >= 0, "errno = %d", errno);
	close(fd);

	llapi_layout_free(layout);

	/* Now try to set prefer flags on the parity component using llapi.
	 * This bypasses the lfs.c client-side validation and tests the
	 * server-side validation in lod_object.c
	 */

	/* Get the layout from the file */
	layout = llapi_layout_get_by_path(path, 0);
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* Move to the second component (parity mirror) */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Get component ID and verify this is a parity component */
	rc = llapi_layout_comp_id_get(layout, &comp_id);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_flags_get(layout, &flags);
	ASSERTF(rc == 0, "errno = %d", errno);
	ASSERTF(flags & LCME_FL_PARITY, "Component should have PARITY flag");

	llapi_layout_free(layout);

	/* Try to set LCME_FL_PREF_RD flag on parity component
	 * Server should reject it
	 */
	ids[0] = comp_id;
	flags_array[0] = LCME_FL_PREF_RD;

	rc = llapi_layout_file_comp_set(path, ids, flags_array, 1);
	ASSERTF(rc != 0 && errno == EINVAL,
		"Server should reject PREF_RD on parity component: rc=%d, errno=%d",
		rc, errno);

	/* Try LCME_FL_PREF_WR */
	flags_array[0] = LCME_FL_PREF_WR;

	rc = llapi_layout_file_comp_set(path, ids, flags_array, 1);
	ASSERTF(rc != 0 && errno == EINVAL,
		"Server should reject PREF_WR on parity component: rc=%d, errno=%d",
		rc, errno);

	/* Verify that prefer flags CAN be set on data components */
	layout = llapi_layout_get_by_path(path, 0);
	ASSERTF(layout != NULL, "errno = %d", errno);

	/* Move to first component (data mirror) */
	rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
	ASSERTF(rc == 0, "errno = %d", errno);

	/* Get component ID and verify this is NOT a parity component */
	rc = llapi_layout_comp_id_get(layout, &comp_id);
	ASSERTF(rc == 0, "errno = %d", errno);

	rc = llapi_layout_comp_flags_get(layout, &flags);
	ASSERTF(rc == 0, "errno = %d", errno);
	ASSERTF(!(flags & LCME_FL_PARITY),
		"First component should not have PARITY flag");

	llapi_layout_free(layout);

	/* Set LCME_FL_PREF_RW on data component - should succeed */
	ids[0] = comp_id;
	flags_array[0] = LCME_FL_PREF_RW;

	rc = llapi_layout_file_comp_set(path, ids, flags_array, 1);
	ASSERTF(rc == 0,
		"Server should allow PREF_RW on data component: rc=%d, errno=%d",
		rc, errno);
}

static struct test_tbl_entry test_tbl[] = {
	TEST_REGISTER(0),
	TEST_REGISTER(1),
	TEST_REGISTER(2),
	TEST_REGISTER(3),
	TEST_REGISTER(4),
	TEST_REGISTER(5),
	TEST_REGISTER(6),
	TEST_REGISTER(7),
	TEST_REGISTER(8),
	TEST_REGISTER(9),
	TEST_REGISTER(10),
	TEST_REGISTER(11),
	TEST_REGISTER(12),
	TEST_REGISTER(13),
	TEST_REGISTER(14),
	TEST_REGISTER(15),
	TEST_REGISTER(16),
	TEST_REGISTER(17),
	TEST_REGISTER(18),
	TEST_REGISTER(19),
	TEST_REGISTER(20),
	TEST_REGISTER(21),
	TEST_REGISTER(22),
	TEST_REGISTER(23),
	TEST_REGISTER(24),
	TEST_REGISTER(25),
	TEST_REGISTER(26),
	TEST_REGISTER(27),
	TEST_REGISTER(28),
	TEST_REGISTER(29),
	TEST_REGISTER(30),
	TEST_REGISTER(31),
	TEST_REGISTER(32),
	TEST_REGISTER(33),
	TEST_REGISTER(34),
	TEST_REGISTER(35),
	TEST_REGISTER(36),
	TEST_REGISTER(37),
	TEST_REGISTER(40),
	TEST_REGISTER(41),
	TEST_REGISTER(42),
	TEST_REGISTER(43),
	TEST_REGISTER(44),
	TEST_REGISTER(45),
	TEST_REGISTER(46),
	TEST_REGISTER(47),
	TEST_REGISTER(48),
	TEST_REGISTER(49),
	TEST_REGISTER(50),
	TEST_REGISTER(51),
	TEST_REGISTER(52),
	TEST_REGISTER(53),
	TEST_REGISTER(54),
	TEST_REGISTER(55),
	TEST_REGISTER(56),
	TEST_REGISTER(57),
	TEST_REGISTER(58),
	TEST_REGISTER(59),
	TEST_REGISTER(60),
	TEST_REGISTER(61),
	TEST_REGISTER(62),
	TEST_REGISTER(63),
	TEST_REGISTER(64),
	TEST_REGISTER(65),
	TEST_REGISTER(66),
	TEST_REGISTER(67),
	TEST_REGISTER_END
};

static void process_args(int argc, char *argv[])
{
	int c;

	while ((c = getopt(argc, argv, "d:p:o:s:t:")) != -1) {
		switch (c) {
		case 'd':
			if (snprintf(lustre_dir, sizeof(lustre_dir), "%s",
				     optarg) >= sizeof(lustre_dir))
				DIE("Error: test directory name too long\n");
			break;
		case 'p':
			poolname = optarg;
			break;
		case 'o':
			num_osts = atoi(optarg);
			if (num_osts < 2)
				DIE("Error: at least 2 OSTS are required\n");
			break;
		case 's':
			set_tests_to_skip(optarg, test_tbl);
			break;
		case 't':
			set_tests_to_run(optarg, test_tbl);
			break;
		case '?':
			fprintf(stderr, "Unknown option '%c'\n", optopt);
			usage(argv[0]);
		}
	}
}

int main(int argc, char *argv[])
{
	process_args(argc, argv);

	return run_tests(lustre_dir, test_tbl);
}