OpenSolaris_b135/lib/lvm/libmeta/common/meta_mh.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * Just in case we're not in a build environment, make sure that
 * TEXT_DOMAIN gets set to something.
 */
#if !defined(TEXT_DOMAIN)
#define	TEXT_DOMAIN "SYS_TEST"
#endif

/*
 * MH ioctl functions
 */

#include <meta.h>
#include <metamhd.h>
#include <string.h>

#include "meta_runtime.h"

#define	DEFAULTDEV "/dev/rdsk"
/*
 * default timeout values
 */
mhd_mhiargs_t	defmhiargs = {
	1000,			/* failfast */
	{ 6000, 6000, 30000 }	/* take ownership */
};

/* RPC timeouts */
static md_timeval32_t	tk_own_timeout  = { 24 * 60 * 60, 0 };	/* 1 day */
static md_timeval32_t	rel_own_timeout = { 24 * 60 * 60, 0 };	/* 1 day */

/*
 * RPC handle
 */
typedef struct {
	char	*hostname;
	CLIENT	*clientp;
} mhd_handle_t;

/*
 * close RPC connection
 */
static void
close_metamhd(
	mhd_handle_t	*hp
)
{
	assert(hp != NULL);
	if (hp->hostname != NULL) {
		Free(hp->hostname);
	}
	if (hp->clientp != NULL) {
		auth_destroy(hp->clientp->cl_auth);
		clnt_destroy(hp->clientp);
	}
	Free(hp);
}

/*
 * open RPC connection to rpc.metamhd
 */
static mhd_handle_t *
open_metamhd(
	char		*hostname,
	md_error_t	*ep
)
{
	CLIENT		*clientp;
	mhd_handle_t	*hp;

	/* default to local host */
	if ((hostname == NULL) || (*hostname == '\0'))
		hostname = mynode();

	/* open RPC connection */
	assert(hostname != NULL);
	if ((clientp = meta_client_create(hostname, METAMHD, METAMHD_VERSION,
	    "tcp")) == NULL) {
		clnt_pcreateerror(hostname);
		(void) mdrpccreateerror(ep, hostname, "metamhd clnt_create");
		return (NULL);
	} else {
		auth_destroy(clientp->cl_auth);
		clientp->cl_auth = authsys_create_default();
		assert(clientp->cl_auth != NULL);
	}

	/* return connection */
	hp = Zalloc(sizeof (*hp));
	hp->hostname = Strdup(hostname);
	hp->clientp = clientp;
	return (hp);
}

/*
 * steal and convert mherror_t
 */
int
mhstealerror(
	mhd_error_t	*mhep,
	md_error_t	*ep
)
{
	int		rval = -1;

	/* no error */
	if (mhep->errnum == 0) {
		/* assert(mhep->name == NULL); */
		rval = 0;
		goto out;
	}

	/* steal error */
	switch (mhep->errnum) {
	case MHD_E_MAJORITY:
		(void) mderror(ep, MDE_TAKE_OWN, mhep->name);
		break;
	case MHD_E_RESERVED:
		(void) mderror(ep, MDE_RESERVED, mhep->name);
		break;
	default:
		(void) mdsyserror(ep, mhep->errnum, mhep->name);
		break;
	}

	/* cleanup, return success */
out:
	if (mhep->name != NULL)
		Free(mhep->name);
	(void) memset(mhep, 0, sizeof (*mhep));
	return (rval);
}

/*
 * should we do MHIOCTLs ?
 */
static int
do_mhioctl()
{
	if (getenv("MD_NOMHIOCTL") != NULL) {
		(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
		    "NOT doing MH ioctls\n"));
		(void) fflush(stderr);
		return (0);
	}
	return (1);
}

/*
 * take ownership of drives
 */
int
meta_take_own(
	char			*sname,
	mddrivenamelist_t	*dnlp,
	mhd_mhiargs_t		*mhiargsp,
	int			partial_set,
	md_error_t		*ep
)
{
	mddrivenamelist_t	*p;
	uint_t			ndev = 0;
	mhd_tkown_args_t	args;
	mhd_error_t		mherror;
	mhd_set_t		*mhsp = &args.set;
	uint_t			i;
	char			*e;
	mhd_handle_t		*hp = NULL;
	int			rval = -1;

	/*
	 * RFE 4126509.  Check the runtime parameters to see if
	 * they're set to disable MHIOCTKOWN ioctl() operations
	 * on the disks.  If so, return immediately without
	 * performing the operations.
	 */

	if (do_owner_ioctls() == B_FALSE) {
		return (0);
	}

	/* count drives, get set */
	for (p = dnlp; (p != NULL); p = p->next)
		++ndev;
	if (ndev == 0)
		return (0);

	/* initialize */
	(void) memset(&args, 0, sizeof (args));
	(void) memset(&mherror, 0, sizeof (mherror));

	/* build arguments */
	mhsp->setname = Strdup(sname);
	mhsp->drives.drives_len = ndev;
	mhsp->drives.drives_val
	    = Calloc(ndev, sizeof (*mhsp->drives.drives_val));
	for (p = dnlp, i = 0; (i < ndev); p = p->next, ++i) {
		mhsp->drives.drives_val[i] = Strdup(p->drivenamep->rname);
	}
	args.timeouts = *mhiargsp;
	args.ff_mode = MHD_FF_DRIVER;
	if (((e = getenv("MD_DEBUG")) != NULL) &&
	    ((e = strstr(e, "FAILFAST=")) != NULL) &&
	    ((e = strchr(e, '=')) != NULL)) {
		++e;
		if (strcmp(e, "NONE") == 0)
			args.ff_mode = MHD_FF_NONE;
		else if (strcmp(e, "DRIVER") == 0)
			args.ff_mode = MHD_FF_DRIVER;
		else if (strcmp(e, "DEBUG") == 0)
			args.ff_mode = MHD_FF_DEBUG;
		else if (strcmp(e, "HALT") == 0)
			args.ff_mode = MHD_FF_HALT;
		else if (strcmp(e, "PANIC") == 0)
			args.ff_mode = MHD_FF_PANIC;
	}
	if (partial_set)
		args.options |= MHD_PARTIAL_SET;
	if (((e = getenv("MD_DEBUG")) != NULL) &&
	    (strstr(e, "NOTHREAD") != NULL)) {
		args.options |= MHD_SERIAL;
	}

	/* open connection */
	if ((hp = open_metamhd(NULL, ep)) == NULL)
		return (-1);
	clnt_control(hp->clientp, CLSET_TIMEOUT, (char *)&tk_own_timeout);

	/* take ownership */
	if (mhd_tkown_1(&args, &mherror, hp->clientp) != RPC_SUCCESS) {
		(void) mdrpcerror(ep, hp->clientp, hp->hostname,
		    "metamhd tkown");
	} else if (mhstealerror(&mherror, ep) == 0) {
		rval = 0;	/* success */
	}

	/* cleanup, return success */
out:
	xdr_free(xdr_mhd_tkown_args_t, (char *)&args);
	xdr_free(xdr_mhd_error_t, (char *)&mherror);
	if (hp != NULL)
		close_metamhd(hp);
	return (rval);
}

/*
 * take ownership of drives
 */
int
tk_own_bydd(
	mdsetname_t		*sp,
	md_drive_desc		*ddlp,
	mhd_mhiargs_t		*mhiargsp,
	int			partial_set,
	md_error_t		*ep
)
{
	mddrivenamelist_t	*dnlp = NULL;
	mddrivenamelist_t	**tailpp = &dnlp;
	md_drive_desc		*p;
	int			rval;

	/*
	 * Add the drivename struct to the end of the
	 * drivenamelist but keep a pointer to the last
	 * element so that we don't incur the overhead
	 * of traversing the list each time
	 */
	for (p = ddlp; (p != NULL); p = p->dd_next)
		tailpp = meta_drivenamelist_append_wrapper(tailpp, p->dd_dnp);

	/* take ownership */
	rval = meta_take_own(sp->setname, dnlp, mhiargsp, partial_set, ep);

	/* cleanup, return success */
	metafreedrivenamelist(dnlp);
	return (rval);
}

/*
 * release ownership of drives
 */
int
meta_rel_own(
	char			*sname,
	mddrivenamelist_t	*dnlp,
	int			partial_set,
	md_error_t		*ep
)
{
	mddrivenamelist_t	*p;
	uint_t			ndev = 0;
	mhd_relown_args_t	args;
	mhd_error_t		mherror;
	mhd_set_t		*mhsp = &args.set;
	uint_t			i;
	char			*e;
	mhd_handle_t		*hp = NULL;
	int			rval = -1;

	/*
	 * RFE 4126509.  Check the runtime parameters to see if
	 * they're set to disable MHIOCRELEASE and MHIOCENFAILFAST
	 * ioctl() operations on the disks.  If so, return
	 * immediately without performing the operations.
	 */

	if (do_owner_ioctls() == B_FALSE) {
		return (0);
	}

	/*
	 * if not doing ioctls (HK 98/10/28: the following code tests
	 * an environment variable, and was apparently inserted to
	 * make testing easier.)
	 */

	if (! do_mhioctl())
		return (0);

	/* count drives, get set */
	for (p = dnlp; (p != NULL); p = p->next)
		++ndev;
	if (ndev == 0)
		return (0);

	/* initialize */
	(void) memset(&args, 0, sizeof (args));
	(void) memset(&mherror, 0, sizeof (mherror));

	/* build arguments */
	mhsp->setname = Strdup(sname);
	mhsp->drives.drives_len = ndev;
	mhsp->drives.drives_val
	    = Calloc(ndev, sizeof (*mhsp->drives.drives_val));
	for (p = dnlp, i = 0; (i < ndev); p = p->next, ++i) {
		mhsp->drives.drives_val[i] = Strdup(p->drivenamep->rname);
	}
	if (partial_set)
		args.options |= MHD_PARTIAL_SET;
	if (((e = getenv("MD_DEBUG")) != NULL) &&
	    (strstr(e, "NOTHREAD") != NULL)) {
		args.options |= MHD_SERIAL;
	}

	/* open connection */
	if ((hp = open_metamhd(NULL, ep)) == NULL)
		return (-1);
	clnt_control(hp->clientp, CLSET_TIMEOUT, (char *)&rel_own_timeout);

	/* take ownership */
	if (mhd_relown_1(&args, &mherror, hp->clientp) != RPC_SUCCESS) {
		(void) mdrpcerror(ep, hp->clientp, hp->hostname,
		    "metamhd relown");
	} else if (mhstealerror(&mherror, ep) == 0) {
		rval = 0;	/* success */
	}

	/* cleanup, return success */
out:
	xdr_free(xdr_mhd_relown_args_t, (char *)&args);
	xdr_free(xdr_mhd_error_t, (char *)&mherror);
	if (hp != NULL)
		close_metamhd(hp);
	return (rval);
}

/*
 * release ownership of drives
 */
int
rel_own_bydd(
	mdsetname_t		*sp,
	md_drive_desc		*ddlp,
	int			partial_set,
	md_error_t		*ep
)
{
	mddrivenamelist_t	*dnlp = NULL;
	mddrivenamelist_t	**tailpp = &dnlp;
	md_drive_desc		*p;
	int			rval;

	/*
	 * Add the drivename struct to the end of the
	 * drivenamelist but keep a pointer to the last
	 * element so that we don't incur the overhead
	 * of traversing the list each time
	 */
	for (p = ddlp; (p != NULL); p = p->dd_next)
		tailpp = meta_drivenamelist_append_wrapper(tailpp, p->dd_dnp);

	/* release ownership */
	rval = meta_rel_own(sp->setname, dnlp, partial_set, ep);

	/* cleanup, return success */
	metafreedrivenamelist(dnlp);
	return (rval);
}

/*
 * get status of drives
 */
int
meta_status_own(
	char			*sname,
	md_disk_status_list_t	*dslp,
	int			partial_set,
	md_error_t		*ep
)
{
	md_disk_status_list_t	*p;
	uint_t			ndev = 0;
	mhd_status_args_t	args;
	mhd_status_res_t	results;
	mhd_error_t		*mhep = &results.status;
	mhd_set_t		*mhsp = &args.set;
	uint_t			i;
	char			*e;
	mhd_handle_t		*hp = NULL;
	int			rval = -1;

	/* if not doing ioctls */
	if (! do_mhioctl())
		return (0);

	/* count drives, get set */
	for (p = dslp; (p != NULL); p = p->next)
		++ndev;
	if (ndev == 0)
		return (0);

	/* initialize */
	(void) memset(&args, 0, sizeof (args));
	(void) memset(&results, 0, sizeof (results));

	/* build arguments */
	mhsp->setname = Strdup(sname);
	mhsp->drives.drives_len = ndev;
	mhsp->drives.drives_val
	    = Calloc(ndev, sizeof (*mhsp->drives.drives_val));
	for (p = dslp, i = 0; (i < ndev); p = p->next, ++i) {
		mhsp->drives.drives_val[i] = Strdup(p->drivenamep->rname);
	}
	if (partial_set)
		args.options |= MHD_PARTIAL_SET;
	if (((e = getenv("MD_DEBUG")) != NULL) &&
	    (strstr(e, "NOTHREAD") != NULL)) {
		args.options |= MHD_SERIAL;
	}

	/* open connection */
	if ((hp = open_metamhd(NULL, ep)) == NULL)
		return (-1);
	clnt_control(hp->clientp, CLSET_TIMEOUT, (char *)&tk_own_timeout);

	/* get status */
	if (mhd_status_1(&args, &results, hp->clientp) != RPC_SUCCESS) {
		(void) mdrpcerror(ep, hp->clientp, hp->hostname,
		    dgettext(TEXT_DOMAIN, "metamhd status"));
		goto out;
	} else if (mhstealerror(mhep, ep) != 0) {
		goto out;
	}

	/* do something with it */
	assert(results.results.results_len == ndev);
	for (p = dslp, i = 0; (i < ndev); p = p->next, ++i) {
		mhd_drive_status_t	*resp = &results.results.results_val[i];
		mddrivename_t		*dp = p->drivenamep;
		mhd_error_t		mherror;

		/* make sure we have the right drive */
		assert(strcmp(dp->rname, resp->drive) == 0);

		/* copy status */
		if (resp->errnum != 0) {
			(void) memset(&mherror, 0, sizeof (mherror));
			mherror.errnum = resp->errnum;
			mherror.name = Strdup(resp->drive);
			(void) mhstealerror(&mherror, &p->status);
		}
	}
	rval = 0;		/* success */

	/* cleanup, return success */
out:
	xdr_free(xdr_mhd_status_args_t, (char *)&args);
	xdr_free(xdr_mhd_status_res_t, (char *)&results);
	if (hp != NULL)
		close_metamhd(hp);
	return (rval);
}

/*
 * build disk status list from drivename list
 */
md_disk_status_list_t *
meta_drive_to_disk_status_list(
	mddrivenamelist_t	*dnlp
)
{
	md_disk_status_list_t	*head = NULL;
	md_disk_status_list_t	**tailp = &head;
	mddrivenamelist_t	*p;

	/* copy list */
	for (p = dnlp; (p != NULL); p = p->next) {
		md_disk_status_list_t	*dsp;

		dsp = *tailp = Zalloc(sizeof (*dsp));
		tailp = &dsp->next;
		dsp->drivenamep = p->drivenamep;
	}

	/* return list */
	return (head);
}

/*
 * free disk status list
 */
void
meta_free_disk_status_list(
	md_disk_status_list_t	*dslp
)
{
	md_disk_status_list_t	*next = NULL;

	for (/* void */; (dslp != NULL); dslp = next) {
		next = dslp->next;
		mdclrerror(&dslp->status);
		Free(dslp);
	}
}

/*
 * free drive info list
 */
void
meta_free_drive_info_list(
	mhd_drive_info_list_t	*listp
)
{
	xdr_free(xdr_mhd_drive_info_list_t, (char *)listp);
	(void) memset(listp, 0, sizeof (*listp));
}

/*
 * sort drive info list
 */
static int
compare_drives(
	const void		*p1,
	const void		*p2
)
{
	const mhd_drive_info_t	*di1 = p1;
	const mhd_drive_info_t	*di2 = p2;
	const char		*n1 = di1->dif_name;
	const char		*n2 = di2->dif_name;
	uint_t			c1 = 0, t1 = 0, d1 = 0, s1 = 0;
	uint_t			c2 = 0, t2 = 0, d2 = 0, s2 = 0;
	uint_t			l, cl;

	if (n1 == NULL)
		n1 = "";
	if (n2 == NULL)
		n2 = "";

	/* attempt to sort correctly for c0t1d0s0 .vs. c0t18d0s0 */
	if ((n1 = strrchr(n1, '/')) == NULL)
		goto u;
	n1 += (n1[1] != 'c') ? 2 : 1;
	cl = strlen(n1);
	if ((sscanf(n1, "c%ut%ud%us%u%n", &c1, &t1, &d1, &s1, &l) != 4 &&
	    sscanf(n1, "c%ud%us%u%n", &c1, &d1, &s1, &l) != 3 &&
	    sscanf(n1, "c%ut%ud%u%n", &c1, &t1, &d1, &l) != 3 &&
	    sscanf(n1, "c%ud%u%n", &c1, &d1, &l) != 2) || (l != cl))
		goto u;

	if ((n2 = strrchr(n2, '/')) == NULL)
		goto u;
	n2 += (n2[1] != 'c') ? 2 : 1;
	cl = strlen(n2);
	if ((sscanf(n2, "c%ut%ud%us%u%n", &c2, &t2, &d2, &s2, &l) != 4 &&
	    sscanf(n2, "c%ud%us%u%n", &c2, &d2, &s2, &l) != 3 &&
	    sscanf(n2, "c%ut%ud%u%n", &c2, &t2, &d2, &l) != 3 &&
	    sscanf(n2, "c%ud%u%n", &c2, &d2, &l) != 2) || (l != cl))
		goto u;
	if (c1 != c2)
		return ((c1 > c2) ? 1 : -1);
	if (t1 != t2)
		return ((t1 > t2) ? 1 : -1);
	if (d1 != d2)
		return ((d1 > d2) ? 1 : -1);
	if (s1 != s2)
		return ((s1 > s2) ? 1 : -1);
	return (0);

u:	return (strcmp(di1->dif_name, di2->dif_name));
}

static void
sort_drives(
	mhd_drive_info_list_t	*listp
)
{
	qsort(listp->mhd_drive_info_list_t_val,
	    listp->mhd_drive_info_list_t_len,
	    sizeof (*listp->mhd_drive_info_list_t_val),
	    compare_drives);
}

/*
 * return list of all drives
 */
int
meta_list_drives(
	char			*hostname,
	char			*path,
	mhd_did_flags_t		flags,
	mhd_drive_info_list_t	*listp,
	md_error_t		*ep
)
{
	mhd_list_args_t		args;
	mhd_list_res_t		results;
	mhd_error_t		*mhep = &results.status;
	mhd_handle_t		*hp = NULL;
	int			rval = -1;

	/* if not doing ioctls */
	if (! do_mhioctl())
		return (0);

	/* initialize */
	(void) memset(&args, 0, sizeof (args));
	(void) memset(&results, 0, sizeof (results));

	/* build arguments */
	if (path == NULL)
		path = getenv("MD_DRIVE_ROOT");
	if ((path != NULL) && (*path != '\0'))
		args.path = Strdup(path);
	args.flags = flags;

	/* open connection */
	if ((hp = open_metamhd(hostname, ep)) == NULL)
		return (-1);
	clnt_control(hp->clientp, CLSET_TIMEOUT, (char *)&tk_own_timeout);

	/* get list */
	if (mhd_list_1(&args, &results, hp->clientp) != RPC_SUCCESS) {
		(void) mdrpcerror(ep, hp->clientp, hp->hostname,
		    dgettext(TEXT_DOMAIN, "metamhd list"));
		goto out;
	} else if (mhstealerror(mhep, ep) != 0) {
		goto out;
	}

	/* sort list */
	sort_drives(&results.results);

	/* steal list */
	*listp = results.results;
	results.results.mhd_drive_info_list_t_len = 0;
	results.results.mhd_drive_info_list_t_val = NULL;
	rval = listp->mhd_drive_info_list_t_len;	/* success */

	/* cleanup, return success */
out:
	xdr_free(xdr_mhd_list_args_t, (char *)&args);
	xdr_free(xdr_mhd_list_res_t, (char *)&results);
	if (hp != NULL)
		close_metamhd(hp);
	return (rval);
}

static void
load_paths_to_metamhd()
{
	FILE			*cfp;		/* config file pointer */
	char			buf[BUFSIZ],
				*p,
				*x;
	mhd_drive_info_list_t	list;
	md_error_t		ep;
	mhd_did_flags_t		flags = MHD_DID_SERIAL;

	if ((cfp = fopen(METADEVPATH, "r")) != NULL) {
		/*
		 * Read each line from the file. Lines will be either
		 * comments or path names to pass to rpc.metamhd. If
		 * path names check to see if their a colon seperate
		 * list of names which must be processed one at a time.
		 */

		while (fgets(buf, BUFSIZ, cfp) != NULL) {
			if (buf[0] == '#') {
				/*
				 * Ignore comment lines
				 */
				continue;

			} else if (strchr(buf, ':') != NULL) {
				p = buf;
				while ((x = strchr(p, ':')) != NULL) {
					*x = '\0';
					(void) memset(&ep, '\0', sizeof (ep));
					(void) meta_list_drives(NULL, p, 0,
					    &list, &ep);
					meta_free_drive_info_list(&list);
					p = x + 1;
				}
				/*
				 * We won't pick up the last path name
				 * because the line ends with a newline
				 * not a ':'. So p will still point to
				 * a valid path in this case. Copy the
				 * data that p points to to the beginning
				 * of the buf and let the default case
				 * handle this buffer.
				 * NOTE:
				 * If the file does end with a ":\n", p at
				 * will point to the newline. The default
				 * cause would then set the newline to a
				 * NULL which is okay because meta_list_drives
				 * interprets a null string as /dev/rdsk.
				 */
				(void) memcpy(buf, p, strlen(p));
			}
			/*
			 * Remove any newlines in the buffer.
			 */
			if ((p = strchr(buf, '\n')) != NULL)
				*p = '\0';
			(void) memset(&ep, '\0', sizeof (ep));
			(void) memset(&list, '\0', sizeof (list));
			(void) meta_list_drives(NULL, buf, flags, &list, &ep);
			meta_free_drive_info_list(&list);
		}
		(void) fclose(cfp);
	}
}

/*
 * build list of all drives in set
 */
/*ARGSUSED*/
int
meta_get_drive_names(
	mdsetname_t		*sp,
	mddrivenamelist_t	**dnlpp,
	int			options,
	md_error_t		*ep
)
{
	mhd_did_flags_t		flags = MHD_DID_SERIAL;
	mhd_drive_info_list_t	list;
	mhd_drive_info_t	*mp;
	uint_t			i;
	unsigned		cnt = 0;
	int			rval = -1;
	mddrivenamelist_t	**tailpp = dnlpp;

	/* must have a set */
	assert(sp != NULL);

	load_paths_to_metamhd();
	(void) memset(&list, 0, sizeof (list));
	if ((meta_list_drives(NULL, NULL, flags, &list, ep)) < 0)
		return (-1);

	/* find drives in set */
	for (i = 0; (i < list.mhd_drive_info_list_t_len); ++i) {
		mddrivename_t		*dnp;
		mdname_t		*np;

		mp = &list.mhd_drive_info_list_t_val[i];

		if (mp->dif_id.did_flags & MHD_DID_DUPLICATE)
			continue;

		/* quietly skip drives which don't conform */
		if ((dnp = metadrivename(&sp, mp->dif_name, ep)) == NULL) {
			mdclrerror(ep);
			continue;
		}

		/* check in set */
		if ((np = metaslicename(dnp, MD_SLICE0, ep)) == NULL)
			goto out;
		if (meta_check_inset(sp, np, ep) != 0) {
			mdclrerror(ep);
			continue;
		}

		/*
		 * Add the drivename struct to the end of the
		 * drivenamelist but keep a pointer to the last
		 * element so that we don't incur the overhead
		 * of traversing the list each time
		 */
		tailpp = meta_drivenamelist_append_wrapper(tailpp, dnp);
		++cnt;
	}
	rval = cnt;

	/* cleanup, return error */
out:
	meta_free_drive_info_list(&list);
	return (rval);
}