OpenSolaris_b135/lib/lvm/libmeta/common/meta_name.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 (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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <meta.h>
#include <metad.h>

#include <ctype.h>
#include <string.h>
#include <sys/fs/ufs_fsdir.h>

/*
 * 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

/*
 *	Macros to produce a quoted string containing the value of a
 *	preprocessor macro. For example, if SIZE is defined to be 256,
 *	VAL2STR(SIZE) is "256". This is used to construct format
 *	strings for scanf-family functions below.
 */
#define	QUOTE(x)	#x
#define	VAL2STR(x)	QUOTE(x)

extern	char	*getfullblkname();

/*
 * caches
 */
static	mdsetnamelist_t		*setlistp = NULL;
static	mddrivenamelist_t	*drivelistp = NULL;
static	mdnamelist_t		*fastnmlp = NULL;
static	mdhspnamelist_t		*hsplistp = NULL;

/*
 * Static definitions
 */
static int chksetname(mdsetname_t **spp, char *sname, md_error_t *ep);

/*
 * FUNCTION:	meta_dsk_to_rdsk()
 * INPUT:	str - Fully qualified pathname of a block or character device
 * RETURNS:	char * - The pathname of the raw device
 * PURPOSE:	Allocation of a new string representing the character device
 *		associated with the input string.  Note that no checking is
 *		done to verify the existence of this device file.
 */
static char *
meta_dsk_to_rdsk(char *str)
{
	char	*dp = NULL;
	char	*rdskp = NULL;

	assert(*str == '/');

	if ((dp = strstr(str, "/rdsk/")) != NULL)
		return (Strdup(str));

	/*
	 * If this is a malformed string, (i.e. containing neither
	 * "/rdsk/" nor "/dsk/") then check to see if the caller
	 * is passing old school device names like "/dev/[r]sd" or
	 * exotic hardware presenting "/dev/[r]dc" names.
	 */
	if ((dp = strstr(str, "/dsk/")) == NULL) {
		if (strncmp(str, "/dev/r", 6) == 0) {
			return (Strdup(str));
		} else if (strncmp(str, "/dev/", 5) == 0) {
			dp = str + 4;
		} else {
			return (NULL);
		}
	}

	dp++;
	if (*dp == '\0')
		return (NULL);

	rdskp = Zalloc(strlen(str) + 2);
	(void) strncpy(rdskp, str, dp - str);
	rdskp[dp - str] = 'r';
	(void) strcpy(rdskp + (dp - str) + 1, dp);

	return (rdskp);
}

/*
 * FUNCTION:	rawname()
 * INPUT:	uname - Fully qualified pathname of a block or character device
 * RETURNS:	char * - The fully qualified character device pathname
 * PURPOSE:	Return the fully qualified pathname of the character device
 *		corresponding to the block or character device passed in.
 */
static char *
rawname(char *uname)
{
	char		*new_path = NULL;
	int		ret = -1;
	struct stat	statbuf;

	if (*uname != '/')
		return (NULL);

	if ((new_path = meta_dsk_to_rdsk(uname)) == NULL)
		return (NULL);

	if (strncmp("/dev/", new_path, 5) == 0) {
		ret = stat(new_path, &statbuf);
		if (ret != 0 || (! S_ISCHR(statbuf.st_mode))) {
			Free(new_path);
			return (NULL);
		}
	}

	return (new_path);
}

char *
blkname(
	char	*uname
)
{
	char	*p;

	if ((p = getfullblkname(uname)) == NULL) {
		return (NULL);
	} else if (*p == '\0') {
		Free(p);
		return (NULL);
	} else {
		return (p);
	}
}

/*
 * FUNCTION:	parse_device()
 * INPUT:	sp - pointer to setname struct
 *		uname - Name of either a hotspare pool or metadevice
 *			This can either be a fully qualified path or
 *			in the form [set name/]device
 * OUTPUT:	snamep - name of the set that uname is in
 *		fnamep - metadevice or hsp with path and set name info stripped
 *		    This parameter is dynamically allocated and must be
 *		    freed by the calling function.
 * PURPOSE:	Parse uname and sp into the set name and device name strings.
 *		If the set name is specified as part of uname then use that
 *		otherwise attempt to get the set name from sp.
 */
void
parse_device(
	mdsetname_t	*sp,
	char		*uname,
	char		**fnamep, /* dynamically alloced - caller must free */
	char		**snamep  /* dynamically alloced - caller must free */
)
{
	char		setname[FILENAME_MAX+1];
	char		devname[FILENAME_MAX+1];
	char		*tname = Malloc(strlen(uname) + 1);

	int		len;
	char *up;
	char *tp;
	int lcws;	/* last character was slash */

	/* Now copy uname to tname by throwing away any duplicate '/' */
	for (lcws = 0, tp = tname, up = uname; *up; up++) {
		if (lcws) {
			if (*up == '/') {
				continue;
			} else {
				lcws = 0;
			}
		}
		if (*up == '/') {
			lcws = 1;
		}
		*tp++ = *up; /* ++ is done by for loop */
	}
	*tp = '\0';

	/* fully-qualified  - local set */
	if (((sscanf(tname, "/dev/md/dsk/%" VAL2STR(FILENAME_MAX) "s%n",
	    devname, &len) == 1) && (strlen(tname) == len)) ||
	    ((sscanf(tname, "/dev/md/rdsk/%" VAL2STR(FILENAME_MAX) "s%n",
	    devname, &len) == 1) && (strlen(tname) == len))) {
		*snamep = Strdup(MD_LOCAL_NAME);
		*fnamep = Strdup(devname);
		Free(tname);
		return;
	}

	/* with setname specified - either fully qualified and relative spec */
	if (((sscanf(tname, "%[^/]/%" VAL2STR(FILENAME_MAX) "s%n",
	    setname, devname, &len) == 2) && (strlen(tname) == len)) ||
	    ((sscanf(tname, "/dev/md/%[^/]/dsk/%" VAL2STR(FILENAME_MAX) "s%n",
	    setname, devname, &len) == 2) && (strlen(tname) == len)) ||
	    ((sscanf(tname, "/dev/md/%[^/]/rdsk/%" VAL2STR(FILENAME_MAX) "s%n",
	    setname, devname, &len) == 2) && (strlen(tname) == len))) {

		*snamep = Strdup(setname);
		*fnamep = Strdup(devname);
		Free(tname);
		return;
	}

	/* without setname specified */
	*fnamep = tname;
	if (sp != NULL && !metaislocalset(sp))
		*snamep = Strdup(sp->setname);
	else
		*snamep = NULL;
}

/*
 * check for "all"
 */
int
meta_is_all(char *s)
{
	if ((strcoll(s, gettext("all")) == 0) ||
	    (strcoll(s, gettext("ALL")) == 0))
		return (1);
	return (0);
}

/*
 * check for "none"
 */
int
meta_is_none(char *s)
{
	if ((strcoll(s, gettext("none")) == 0) ||
	    (strcoll(s, gettext("NONE")) == 0))
		return (1);
	return (0);
}

static int
valid_name_syntax(char *uname)
{
	int	i;
	int	uname_len;

	if (uname == NULL || !isalpha(uname[0]))
		return (0);

	uname_len = strlen(uname);
	if (uname_len > MAXNAMLEN)
		return (0);

	/* 'all' and 'none' are reserved */
	if (meta_is_all(uname) || meta_is_none(uname))
		return (0);

	for (i = 1; i < uname_len; i++) {
		if ((isalnum(uname[i]) || uname[i] == '-' ||
		    uname[i] == '_' || uname[i] == '.'))
			continue;
		break;
	}

	if (i < uname_len)
		return (0);

	return (1);

}

/*
 * canonicalize name
 */
char *
meta_canonicalize(
	mdsetname_t	*sp,
	char		*uname
)
{
	char	*sname = NULL;
	char	*tname = NULL;
	char	*cname;

	/* return the dev name and set name */
	parse_device(sp, uname, &tname, &sname);

	if (!valid_name_syntax(tname)) {
		Free(tname);
		if (sname != NULL)
			Free(sname);
		return (NULL);
	}

	if ((sname == NULL) || (strcmp(sname, MD_LOCAL_NAME) == 0))
		cname = tname;
	else {
		size_t	cname_len;

		cname_len = strlen(tname) + strlen(sname) + 2;
		cname = Malloc(cname_len);
		(void) snprintf(
		    cname, cname_len, "%s/%s", sname, tname);
		Free(tname);
	}

	if (sname != NULL)
		Free(sname);

	return (cname);
}

/*
 * canonicalize name and check the set
 */
char *
meta_canonicalize_check_set(
	mdsetname_t	**spp,
	char		*uname,
	md_error_t	*ep
)
{
	char		*sname = NULL;
	char		*tname = NULL;
	char		*cname;

	/* return the dev name and set name */
	parse_device(*spp, uname, &tname, &sname);

	if (!valid_name_syntax(tname)) {
		(void) mderror(ep, MDE_NAME_ILLEGAL, tname);
		if (sname != NULL)
			Free(sname);
		Free(tname);
		return (NULL);
	}

	/* check the set name returned from the name for validity */
	if (chksetname(spp, sname, ep) != 0) {
		Free(tname);
		if (sname != NULL)
			Free(sname);
		return (NULL);
	}

	if ((sname == NULL) || (strcmp(sname, MD_LOCAL_NAME) == 0))
		cname = tname;
	else {
		size_t	cname_len;

		cname_len = strlen(tname) + strlen(sname) + 2;
		cname = Malloc(cname_len);
		(void) snprintf(
		    cname, cname_len, "%s/%s", sname, tname);
		Free(tname);
	}

	if (sname != NULL)
		Free(sname);

	return (cname);
}

/*
 * Verify that the name is a valid hsp/metadevice name
 */
static int
parse_meta_hsp_name(char *uname)
{
	char	*sname = NULL;
	char	*tname = NULL;
	int	ret;

	/* return the dev name and set name */
	parse_device(NULL, uname, &tname, &sname);

	ret = valid_name_syntax(tname);
	if (sname != NULL)
		Free(sname);
	Free(tname);
	return (ret);
}

/*
 * check that name is a metadevice
 */
int
is_metaname(
	char	*uname
)
{
	return (parse_meta_hsp_name(uname));
}

/*
 * check that name is a hotspare pool
 */
int
is_hspname(
	char	*uname
)
{
	return (parse_meta_hsp_name(uname));
}

/*
 * check to verify that name is an existing metadevice
 */
int
is_existing_metadevice(
	mdsetname_t	*sp,
	char		*uname
)
{
	char		*raw_name;
	char		*set_name;
	char		*full_path;
	char		*fname = NULL;
	int		pathlen;
	int		retval = 0;

	assert(uname != NULL);
	/*
	 * If it is an absolute name of a metadevice, then just call rawname
	 * on the input
	 */
	if (uname[0] == '/') {
		if (strncmp("/dev/md", uname, strlen("/dev/md")) == 0 &&
		    (raw_name = rawname(uname)) != NULL) {
			Free(raw_name);
			return (1);
		}
		return (0);
	}

	/* create a fully specified path from the parsed string */
	parse_device(sp, uname, &fname, &set_name);

	if ((set_name == NULL) || (strcmp(set_name, MD_LOCAL_NAME) == 0)) {
		pathlen = strlen("/dev/md/rdsk/") + strlen(fname) + 1;
		full_path = Zalloc(pathlen);
		(void) snprintf(full_path, pathlen, "/dev/md/rdsk/%s", fname);
	} else {
		pathlen = strlen("/dev/md//rdsk/") + strlen(fname) +
		    strlen(set_name) + 1;
		full_path = Zalloc(pathlen);
		(void) snprintf(full_path, pathlen, "/dev/md/%s/rdsk/%s",
		    set_name, fname);
	}

	if ((raw_name = rawname(full_path)) != NULL) {
		Free(raw_name);
		retval = 1;
	}

	if (set_name != NULL)
		Free(set_name);

	Free(fname);
	Free(full_path);
	return (retval);
}

/*
 * check to verify that name is an existing hsp
 */
int
is_existing_hsp(
	mdsetname_t	*sp,
	char		*uname
)
{
	md_error_t	status = mdnullerror;
	hsp_t		hsp;
	set_t		cur_set;

	if (sp != NULL)
		cur_set = sp->setno;
	else
		cur_set = 0;

	hsp = meta_gethspnmentbyname(cur_set, MD_SIDEWILD, uname, &status);

	if (hsp == MD_HSP_NONE) {
		mdclrerror(&status);
		return (0);
	}
	return (1);
}

/*
 * check to verify that name is an existing metadevice or hotspare pool
 */
int
is_existing_meta_hsp(
	mdsetname_t	*sp,
	char		*uname
)
{
	if (is_existing_metadevice(sp, uname) ||
	    is_existing_hsp(sp, uname))
		return (1);

	return (0);
}

/*
 *	mdsetname_t stuff
 */

/*
 * initialize setname
 */
static void
metainitsetname(
	mdsetname_t	*sp
)
{
	(void) memset(sp, '\0', sizeof (*sp));
}

static void
metafreesetdesc(md_set_desc *sd)
{
	md_mnnode_desc	*nd;

	if (MD_MNSET_DESC(sd)) {
		nd = sd->sd_nodelist;
		while (nd) {
			sd->sd_nodelist = nd->nd_next;
			Free(nd);
			nd = sd->sd_nodelist;
		}
	}
	metafreedrivedesc(&sd->sd_drvs);
	Free(sd);
}

/*
 * free allocated setname
 */
static void
metafreesetname(
	mdsetname_t	*sp
)
{
	if (sp->setname != NULL)
		Free(sp->setname);
	if (sp->setdesc != NULL)
		metafreesetdesc(sp->setdesc);
	metainitsetname(sp);
}

/*
 * flush the setname cache
 */
static void
metaflushsetnames()
{
	mdsetnamelist_t		*p, *n;

	for (p = setlistp, n = NULL; (p != NULL); p = n) {
		n = p->next;
		metafreesetname(p->sp);
		Free(p->sp);
		Free(p);
	}
	setlistp = NULL;
}

/*
 * get set number
 */
static int
getsetno(
	char		*sname,
	set_t		*setnop,
	md_error_t	*ep
)
{
	md_set_record	*sr;
	size_t		len;

	/* local set */
	if ((sname == NULL) || (strcmp(sname, MD_LOCAL_NAME) == 0)) {
		*setnop = 0;
		return (0);
	}

	/* shared set */
	if ((sr = getsetbyname(sname, ep)) == NULL) {
		if (mdisrpcerror(ep, RPC_PROGNOTREGISTERED)) {
			char	*p;

			len = strlen(sname) + 30;
			p = Malloc(len);

			(void) snprintf(p, len, "setname \"%s\"", sname);
			(void) mderror(ep, MDE_NO_SET, p);
			Free(p);
		}
		return (-1);
	}
	*setnop = sr->sr_setno;
	free_sr(sr);
	return (0);
}

/*
 * find setname from name
 */
mdsetname_t *
metasetname(
	char		*sname,
	md_error_t	*ep
)
{
	mdsetnamelist_t	**tail;
	set_t		setno;
	mdsetname_t	*sp;

	/* look for cached value first */
	assert(sname != NULL);
	for (tail = &setlistp; (*tail != NULL); tail = &(*tail)->next) {
		sp = (*tail)->sp;
		if (strcmp(sp->setname, sname) == 0) {
			return (sp);
		}
	}

	/* setup set */
	if (getsetno(sname, &setno, ep) != 0)
		return (NULL);

	/* allocate new list element and setname */
	*tail = Zalloc(sizeof (**tail));
	sp = (*tail)->sp = Zalloc(sizeof (*sp));

	sp->setname = Strdup(sname);
	sp->setno = setno;
	sp->lockfd = MD_NO_LOCK;

	return (sp);
}

/*
 * find setname from setno
 */
mdsetname_t *
metasetnosetname(
	set_t		setno,
	md_error_t	*ep
)
{
	mdsetnamelist_t	*slp;
	mdsetname_t	*sp;
	md_set_record	*sr;

	/* look for cached value first */
	for (slp = setlistp; (slp != NULL); slp = slp->next) {
		sp = slp->sp;
		if (sp->setno == setno)
			return (sp);
	}

	/* local set */
	if (setno == MD_LOCAL_SET)
		return (metasetname(MD_LOCAL_NAME, ep));

	/* shared set */
	if ((sr = getsetbynum(setno, ep)) == NULL)
		return (NULL);
	sp = metasetname(sr->sr_setname, ep);
	free_sr(sr);
	return (sp);
}

mdsetname_t *
metafakesetname(
	set_t		setno,
	char		*sname
)
{
	mdsetnamelist_t	**tail;
	mdsetname_t	*sp;

	/* look for cached value first */
	for (tail = &setlistp; (*tail != NULL); tail = &(*tail)->next) {
		sp = (*tail)->sp;
		if (sp->setno == setno) {
			if ((sp->setname == NULL) && (sname != NULL))
				sp->setname = Strdup(sname);
			return (sp);
		}
	}

	/* allocate new list element and setname */
	*tail = Zalloc(sizeof (**tail));
	sp = (*tail)->sp = Zalloc(sizeof (*sp));

	if (sname != NULL)
		sp->setname = Strdup(sname);
	sp->setno = setno;
	sp->lockfd = MD_NO_LOCK;

	return (sp);
}


/*
 * setup set record (sr) and cache it in the mdsetname_t struct
 */
md_set_desc *
sr2setdesc(
	md_set_record	*sr
)
{
	md_set_desc	*sd;
	int		i;
	md_mnset_record	*mnsr;
	md_mnnode_desc	*nd, *nd_prev = 0;
	md_mnnode_record	*nr;
	md_error_t	status = mdnullerror;
	md_error_t	*ep = &status;
	int		nodecnt, nrcnt;
	mndiskset_membershiplist_t *nl, *nl2;

	sd = Zalloc(sizeof (*sd));
	sd->sd_ctime = sr->sr_ctime;
	sd->sd_genid = sr->sr_genid;
	sd->sd_setno = sr->sr_setno;
	sd->sd_flags = sr->sr_flags;

	if (MD_MNSET_DESC(sd)) {
		mnsr = (md_mnset_record *)sr;
		(void) strlcpy(sd->sd_mn_master_nodenm,
		    mnsr->sr_master_nodenm, sizeof (sd->sd_mn_master_nodenm));
		sd->sd_mn_master_nodeid = mnsr->sr_master_nodeid;
		if (strcmp(mnsr->sr_master_nodenm, mynode()) == 0) {
			sd->sd_mn_am_i_master = 1;
		}

		/*
		 * Get membershiplist from API routine.  If there's
		 * an error, just use a NULL nodelist.
		 */
		if (meta_read_nodelist(&nodecnt, &nl, ep) == -1) {
			nodecnt = 0;  /* no nodes are alive */
			nl = NULL;
		}
		nr = mnsr->sr_nodechain;
		nrcnt = 0;
		/*
		 * Node descriptor node list must be built in
		 * ascending order of nodeid.  The nodechain
		 * in the mnset record is in ascending order,
		 * so just make them the same.
		 */
		while (nr) {
			nd = Zalloc(sizeof (*nd));
			if (nd_prev) {
				nd_prev->nd_next = nd;
			} else {
				sd->sd_nodelist = nd;
			}
			nd->nd_ctime = nr->nr_ctime;
			nd->nd_genid = nr->nr_genid;
			nd->nd_flags = nr->nr_flags;

			(void) strlcpy(nd->nd_nodename, nr->nr_nodename,
			    sizeof (nd->nd_nodename));
			nd->nd_nodeid = nr->nr_nodeid;
			if (strcmp(nd->nd_nodename, mynode()) == 0) {
				sd->sd_mn_mynode = nd;
			}
			if (nd->nd_nodeid == sd->sd_mn_master_nodeid) {
				sd->sd_mn_masternode = nd;
			}

			/*
			 * If node is marked ALIVE, then set priv_ic
			 * from membership list.  During the early part
			 * of a reconfig cycle, the membership list may
			 * have been changed, (a node entering or leaving
			 * the cluster), but rpc.metad hasn't flushed
			 * its data yet.  So, if node is marked alive, but
			 * is no longer in the membership list (node has
			 * left the cluster) then just leave priv_ic to NULL.
			 */
			if (nd->nd_flags & MD_MN_NODE_ALIVE) {
				nl2 = nl;
				while (nl2) {
					if (nl2->msl_node_id == nd->nd_nodeid) {
						(void) strlcpy(nd->nd_priv_ic,
						    nl2->msl_node_addr,
						    sizeof (nd->nd_priv_ic));
						break;
					}
					nl2 = nl2->next;
				}
			}

			nr = nr->nr_next;
			nrcnt++;
			nd_prev = nd;
		}
		sd->sd_mn_numnodes = nrcnt;
		if (nodecnt)
			meta_free_nodelist(nl);

		/* Just copying to keep consistent view between sr & sd */
		(void) strlcpy(sd->sd_nodes[0], mnsr->sr_nodes_bw_compat[0],
		    sizeof (sd->sd_nodes[0]));
	} else {
		for (i = 0; i < MD_MAXSIDES; i++)
			(void) strlcpy(sd->sd_nodes[i], sr->sr_nodes[i],
			    sizeof (sd->sd_nodes[i]));
	}

	sd->sd_med = sr->sr_med;		/* structure assignment */

	return (sd);
}

md_set_desc *
metaget_setdesc(
	mdsetname_t	*sp,
	md_error_t	*ep
)
{
	md_set_record	*sr;

	if (sp->setdesc != NULL)
		return (sp->setdesc);

	if (sp->setname != NULL) {
		if ((sr = getsetbyname(sp->setname, ep)) != NULL) {
			sp->setdesc = sr2setdesc(sr);
			free_sr(sr);
			return (sp->setdesc);
		}
	}

	if (sp->setno > 0) {
		if ((sr = getsetbynum(sp->setno, ep)) != NULL) {
			sp->setdesc = sr2setdesc(sr);
			free_sr(sr);
			return (sp->setdesc);
		}
	}

	return (NULL);
}

void
metaflushsetname(mdsetname_t *sp)
{
	if (sp == NULL)
		return;

	if (sp->setdesc == NULL)
		return;

	metafreesetdesc(sp->setdesc);
	sp->setdesc = NULL;
}

/*
 * check for local set
 */
int
metaislocalset(
	mdsetname_t	*sp
)
{
	assert(sp->setname != NULL);
	if (strcmp(sp->setname, MD_LOCAL_NAME) == 0) {
		assert(sp->setno == MD_LOCAL_SET);
		return (1);
	} else {
		assert(sp->setno != MD_LOCAL_SET);
		return (0);
	}
}

/*
 * check for same set
 */
int
metaissameset(
	mdsetname_t	*sp1,
	mdsetname_t	*sp2
)
{
	if (strcmp(sp1->setname, sp2->setname) == 0) {
		assert(sp1->setno == sp2->setno);
		return (1);
	} else {
		assert(sp1->setno != sp2->setno);
		return (0);
	}
}

/*
 * check to see if set changed
 */
static int
chkset(
	mdsetname_t	**spp,
	char		*sname,
	md_error_t	*ep
)
{
	/* if we already have a set, make sure it's the same */
	if (*spp != NULL && !metaislocalset(*spp)) {
		if ((*spp)->setname != sname &&
		    strcmp((*spp)->setname, sname) != 0) {
			return (mderror(ep, MDE_SET_DIFF, sname));
		}
		return (0);
	}

	/* otherwise store new set name and number */
	if ((*spp = metasetname(sname, ep)) == NULL) {
		return (-1);
	}

	/* return success */
	return (0);
}

/*
 * check to see if set changed from default
 */
static int
chksetname(
	mdsetname_t	**spp,
	char		*sname,
	md_error_t	*ep
)
{
	/* default to *spp's setname, or if that is NULL to MD_LOCAL_NAME */
	if (sname == NULL) {
		if (*spp) {
			return (0);
		} else {
			sname = MD_LOCAL_NAME;
		}
	}

	/* see if changed */
	return (chkset(spp, sname, ep));
}

/*
 * check setname from setno
 */
static int
chksetno(
	mdsetname_t	**spp,
	set_t		setno,
	md_error_t	*ep
)
{
	md_set_record	*sr;
	int		rval;

	/* local set */
	if (setno == 0)
		return (chkset(spp, MD_LOCAL_NAME, ep));

	/* shared set */
	if ((sr = getsetbynum(setno, ep)) == NULL)
		return (-1);
	rval = chkset(spp, sr->sr_setname, ep);
	free_sr(sr);
	return (rval);
}

/*
 *	mddrivename_t stuff
 */

/*
 * initialize name
 */
static void
metainitname(
	mdname_t	*np
)
{
	(void) memset(np, 0, sizeof (*np));
	np->dev = NODEV64;
	np->key = MD_KEYBAD;
	np->end_blk = -1;
	np->start_blk = -1;
}

/*
 * free allocated name
 */
static void
metafreename(
	mdname_t	*np
)
{
	if (np->cname != NULL)
		Free(np->cname);
	if (np->bname != NULL)
		Free(np->bname);
	if (np->rname != NULL)
		Free(np->rname);
	if (np->devicesname != NULL)
		Free(np->devicesname);
	metainitname(np);
}

/*
 * initialize drive name
 */
static void
metainitdrivename(
	mddrivename_t	*dnp
)
{
	(void) memset(dnp, 0, sizeof (*dnp));
	dnp->side_names_key = MD_KEYBAD;
}

/*
 * flush side names
 */
void
metaflushsidenames(
	mddrivename_t	*dnp
)
{
	mdsidenames_t	*p, *n;

	for (p = dnp->side_names, n = NULL; (p != NULL); p = n) {
		n = p->next;
		if (p->dname != NULL)
			Free(p->dname);
		if (p->cname != NULL)
			Free(p->cname);
		Free(p);
	}
	dnp->side_names = NULL;
}

/*
 * free drive name
 */
void
metafreedrivename(
	mddrivename_t	*dnp
)
{
	uint_t		slice;

	if (dnp->cname != NULL)
		Free(dnp->cname);
	if (dnp->rname != NULL)
		Free(dnp->rname);
	metafreevtoc(&dnp->vtoc);
	for (slice = 0; (slice < dnp->parts.parts_len); ++slice)
		metafreename(&dnp->parts.parts_val[slice]);
	if (dnp->parts.parts_val != NULL)
		Free(dnp->parts.parts_val);
	metaflushsidenames(dnp);
	if (dnp->miscname != NULL)
		Free(dnp->miscname);
	meta_free_unit(dnp);
	metainitdrivename(dnp);
}

/*
 * flush the drive name cache
 */
void
metaflushdrivenames()
{
	mddrivenamelist_t	*p, *n;

	for (p = drivelistp, n = NULL; (p != NULL); p = n) {
		n = p->next;
		metafreedrivename(p->drivenamep);
		Free(p->drivenamep);
		Free(p);
	}
	drivelistp = NULL;
}

/*
 * peel off s%u from name
 */
char *
metadiskname(
	char	*name
)
{
	char	*p, *e;
	char	onmb[BUFSIZ+1], cnmb[BUFSIZ];
	uint_t	d = 0;
	int	l = 0;
	int	cl = strlen(name);

	/*
	 * Handle old style names, which are of the form /dev/rXXNN[a-h].
	 */
	if (sscanf(name, "/dev/r%" VAL2STR(BUFSIZ) "[^0-9/]%u%*[a-h]%n",
	    onmb, &d, &l) == 2 && l == cl) {
		(void) snprintf(cnmb, sizeof (cnmb), "/dev/r%s%u", onmb, d);
		return (Strdup(cnmb));
	}

	/*
	 * Handle old style names, which are of the form /dev/XXNN[a-h].
	 */
	if (sscanf(name, "/dev/%" VAL2STR(BUFSIZ) "[^0-9/]%u%*[a-h]%n",
	    onmb, &d, &l) == 2 && l == cl) {
		(void) snprintf(cnmb, sizeof (cnmb), "/dev/%s%u", onmb, d);
		return (Strdup(cnmb));
	}

	/* gobble number and 's' */
	p = e = name + strlen(name) - 1;
	for (; (p > name); --p) {
		if (!isdigit(*p))
			break;
	}
	if ((p == e) || (p <= name))
		return (Strdup(name));

	if (*p != 's' && strchr("dt", *p) == NULL)
		return (Strdup(name));
	else if (strchr("dt", *p) != NULL)
		return (Strdup(name));
	p--;

	if ((p <= name) || (!isdigit(*p)))
		return (Strdup(name));

	*(++p) = '\0';
	e = Strdup(name);
	*p = 's';

	return (e);
}

/*
 * free list of drivenames
 */
void
metafreedrivenamelist(
	mddrivenamelist_t	*dnlp
)
{
	mddrivenamelist_t	*next = NULL;

	for (/* void */; (dnlp != NULL); dnlp = next) {
		next = dnlp->next;
		Free(dnlp);
	}
}

/*
 * build list of drivenames
 */
int
metadrivenamelist(
	mdsetname_t		**spp,
	mddrivenamelist_t	**dnlpp,
	int			argc,
	char			*argv[],
	md_error_t		*ep
)
{
	mddrivenamelist_t	**tailpp = dnlpp;
	int			count = 0;

	for (*dnlpp = NULL; (argc > 0); ++count, --argc, ++argv) {
		mddrivenamelist_t	*dnlp = Zalloc(sizeof (*dnlp));

		if ((dnlp->drivenamep = metadrivename(spp, argv[0],
		    ep)) == NULL) {
			metafreedrivenamelist(*dnlpp);
			*dnlpp = NULL;
			return (-1);
		}
		*tailpp = dnlp;
		tailpp = &dnlp->next;
	}
	return (count);
}

/*
 * append to end of drivename list
 */
mddrivename_t *
metadrivenamelist_append(
	mddrivenamelist_t	**dnlpp,
	mddrivename_t		*dnp
)
{
	mddrivenamelist_t	*dnlp;

	/* run to end of list */
	for (; (*dnlpp != NULL); dnlpp = &(*dnlpp)->next)
		;

	/* allocate new list element */
	dnlp = *dnlpp = Zalloc(sizeof (*dnlp));

	/* append drivename */
	dnlp->drivenamep = dnp;
	return (dnp);
}

/*
 * FUNCTION:	meta_drivenamelist_append_wrapper()
 * INPUT:	tailpp	- pointer to the list tail pointer
 *		dnp	- name node to be appended to list
 * OUTPUT:	none
 * RETURNS:	mddrivenamelist_t * - new tail of the list.
 * PURPOSE:	wrapper to meta_namelist_append for performance.
 *		metanamelist_append finds the tail each time which slows
 *		down long lists.  By keeping track of the tail ourselves
 *		we can change metadrivenamelist_append into a
 *		constant time operation.
 */
mddrivenamelist_t **
meta_drivenamelist_append_wrapper(
	mddrivenamelist_t	**tailpp,
	mddrivename_t	*dnp
)
{
	(void) metadrivenamelist_append(tailpp, dnp);

	/* If it's the first item in the list, return it instead of the next */
	if ((*tailpp)->next == NULL)
		return (tailpp);

	return (&(*tailpp)->next);
}


/*
 *	mdname_t stuff
 */

/*
 * check set and get comparison name
 *
 * NOTE: This function has a side effect of setting *spp if the setname
 * has been specified and *spp is not already set.
 */
char *
meta_name_getname(
	mdsetname_t		**spp,
	char			*uname,
	meta_device_type_t	uname_type,
	md_error_t		*ep
)
{
	if (uname_type == META_DEVICE || uname_type == HSP_DEVICE ||
	    (uname_type == UNKNOWN && is_existing_metadevice(*spp, uname))) {

		/*
		 * if the setname is specified in uname, *spp is set,
		 * and the set names don't agree then canonical name will be
		 * returned as NULL
		 */
		return (meta_canonicalize_check_set(spp, uname, ep));
	}

	/* if it is not a meta/hsp and *spp is not set then set it to local */
	if (chksetname(spp, NULL, ep) != 0)
		return (NULL);

	/* if it is not a meta/hsp name then just return uname */
	return (Strdup(uname));
}

/*
 * FUNCTION:	getrname()
 * INPUT:	spp	- the setname struct
 *		uname	- the possibly unqualified device name
 *		type 	- ptr to the device type of uname
 * OUTPUT:	ep	- return error pointer
 * RETURNS:	char*	- character string containing the fully
 *			qualified raw device name
 * PURPOSE:	Create the fully qualified raw name for the possibly
 *		unqualified device name.  If uname is an absolute
 *		path the raw name is derived from the input string.
 *		Otherwise, an attempt is made to get the rawname by
 *		catting "/dev/md/rdsk" and "/dev/rdsk". If the input
 *		value of type is UNKNOWN and it can be successfully
 *		determined then update type to the correct value.
 */
static	char *
getrname(mdsetname_t **spp, char *uname,
    meta_device_type_t *type, md_error_t *ep)
{
	char			*rname;
	char			*fname;
	int			i;
	int 			rname_cnt = 0;
	char			*rname_list[3];
	meta_device_type_t	tmp_type;

	assert(uname != NULL);
	/* if it is an absolute name then just call rawname on the input */
	if (uname[0] == '/') {
		if ((rname = rawname(uname)) != NULL) {
			/*
			 * If the returned rname does not match with
			 * the specified uname type, we'll return null.
			 */
			if (strncmp(rname, "/dev/md", strlen("/dev/md")) == 0) {
				if (*type == LOGICAL_DEVICE) {
					(void) mdsyserror(ep, ENOENT, uname);
					return (NULL);
				}
				*type = META_DEVICE;
			} else {
				if (*type == META_DEVICE) {
					(void) mdsyserror(ep, ENOENT, uname);
					return (NULL);
				}
				*type = LOGICAL_DEVICE;
			}
			return (rname);
		}

		/* out of luck */
		(void) mdsyserror(ep, ENOENT, uname);
		return (NULL);
	}

	/*
	 * Get device that matches the requested type. If
	 * a match is found, return immediately. If type is
	 * UNKNOWN, save all the found devices in rname_list
	 * so we can determine later whether the input uname
	 * is ambiguous.
	 *
	 * Check for metadevice before physical device.
	 * With the introduction of softpartitions it is more
	 * likely to be a metadevice.
	 */

	/* metadevice short form */
	if (*type == META_DEVICE || *type == UNKNOWN) {
		if (metaislocalset(*spp)) {
			fname = Malloc(strlen(uname) +
			    strlen("/dev/md/rdsk/") + 1);
			(void) strcpy(fname, "/dev/md/rdsk/");
			(void) strcat(fname, uname);
		} else {
			char	*p;
			size_t	len;

			if ((p = strchr(uname, '/')) != NULL) {
				++p;
			} else {
				p = uname;
			}
			len = strlen((*spp)->setname) + strlen(p) +
			    strlen("/dev/md//rdsk/") + 1;
			fname = Malloc(len);
			(void) snprintf(fname, len, "/dev/md/%s/rdsk/%s",
			    (*spp)->setname, p);
		}
		rname = rawname(fname);

		if (*type == META_DEVICE) {
			/*
			 * Handle the case where we have a new metadevice
			 * that does not yet exist in the name-space(e.g
			 * metarecover in MN sets where /dev/md entry is
			 * not yet created in the non-master nodes). In
			 * this case we return the constructed metadevice
			 * name as that will exist after the metainit call
			 * has created it.
			 */
			if (rname == NULL) {
				rname = Strdup(fname);
			}

			Free(fname);
			return (rname);
		}

		Free(fname);
		if ((rname != NULL) && (*type == UNKNOWN)) {
			/* Save this result */
			rname_list[rname_cnt] = rname;
			rname_cnt ++;
		}
	}

	if (*type == LOGICAL_DEVICE || *type == UNKNOWN) {
		fname = Malloc(strlen(uname) + strlen("/dev/rdsk/") + 1);
		(void) strcpy(fname, "/dev/rdsk/");
		(void) strcat(fname, uname);
		rname = rawname(fname);

		Free(fname);
		if (rname != NULL) {
			/* Simply return if a logical device was requested */
			if (*type == LOGICAL_DEVICE) {
				return (rname);
			} else {
				rname_list[rname_cnt] = rname;
				rname_cnt ++;
			}
		}
	}

	/*
	 * At this point, we've searched /dev/md/rdsk, /dev/rdsk and
	 * ./ for the specified device. rname_list contains all
	 * the matches we've found and rname_cnt is the number of
	 * matches.
	 *
	 * We know that either we don't have a match if a specific
	 * type was given, in which case we simply return NULL or
	 * we have an UNKNOWN device with 1-3 entries in rname_list.
	 *
	 * If we get 3 entries, rname_cnt == 3, it's ambiguous.
	 * If we only get 1 entry, rname_cnt == 1, return rname_list[0].
	 * If we get 2 entries that are not the same, it's ambigous.
	 */
	rname = NULL;
	if (rname_cnt == 0 || *type != UNKNOWN) {
		/* out of luck */
		(void) mdsyserror(ep, ENOENT, uname);
		return (NULL);
	} else {
		if (rname_cnt == 3) {
			(void) mderror(ep, MDE_AMBIGUOUS_DEV, uname);
			(void) printf(dgettext(TEXT_DOMAIN,
			    "Error: ambiguous device name.\n%s %s %s\n\n"),
			    rname_list[0], rname_list[1], rname_list[2]);
			rname = NULL;
		}

		/* grab the type in case it is not ambiguous */
		if (strncmp(rname_list[0], "/dev/md", strlen("/dev/md")) == 0)
			tmp_type =  META_DEVICE;
		else
			tmp_type =  LOGICAL_DEVICE;

		if (rname_cnt == 1) {
			rname = Strdup(rname_list[0]);
			*type = tmp_type;
		} else {
			/*
			 * Prevent the case where the command is run in
			 * either /dev/md/rdsk or /dev/rdsk so the both
			 * rname_list[0] and rname_list[1] are the same.
			 */
			if (strcmp(rname_list[0], rname_list[1]) != 0) {
				(void) mderror(ep, MDE_AMBIGUOUS_DEV, uname);
				if (rname_cnt != 3) {
					/*
					 * For the rname_cnt == 3 case, the
					 * error was printed above.
					 */
					(void) printf(dgettext(TEXT_DOMAIN,
					    "Error: ambiguous device "
					    "name.\n%s %s\n\n"),
					    rname_list[0], rname_list[1]);
				}
				rname = NULL;
			} else {
				rname = Strdup(rname_list[0]);
				*type = tmp_type;
			}
		}
		for (i = 0; i < rname_cnt; i++)
			Free(rname_list[i]);
		return (rname);
	}
}

/*
 * get raw slice and drive names
 */
static char *
getrawnames(
	mdsetname_t		**spp,
	char			*uname,
	char			**dnamep,
	meta_device_type_t	*uname_type,
	md_error_t		*ep
)
{
	char		*rname = NULL;
	size_t		len;

	/*
	 * Incorrect code path if type is HSP_DEVICE
	 */
	assert(*uname_type != HSP_DEVICE);

	/* initialize */
	*dnamep = NULL;

	/* get slice name */
	if ((rname = getrname(spp, uname, uname_type, ep)) != NULL) {
		*dnamep = metadiskname(rname);
		return (rname);
	}

	/*
	 * If name cannot be found, if may be because is is not accessible.
	 * If it is an absolute name, try all possible disk name formats and
	 * if it is device name, assume it is /dev/rdsk/..
	 * Since the code below assumes logical devices, if the given
	 * uname_type is META_DEVICE, there's nothing to do.
	 */
	if (mdissyserror(ep, ENOENT) && *uname_type != META_DEVICE) {
		if (uname[0] == '/') {
			/* Absolute name */
			uint_t			d = 0;
			int			l = 0;
			char			onmb[BUFSIZ+1], snm[BUFSIZ+1];

			/*
			 * Handle old style raw names
			 */
			if (sscanf(uname,
			    "/dev/r%" VAL2STR(BUFSIZ) "[^0-9/]%u"
			    "%" VAL2STR(BUFSIZ) "[a-h]%n",
			    onmb, &d, snm, &l) == 3 && l == strlen(uname)) {
				mdclrerror(ep);
				rname = Strdup(uname);
				*dnamep = metadiskname(rname);
				*uname_type = LOGICAL_DEVICE;
				return (rname);
			}

			/*
			 * Handle old style block names
			 */
			if (sscanf(uname,
			    "/dev/%" VAL2STR(BUFSIZ) "[^0-9/]%u"
			    "%" VAL2STR(BUFSIZ) "[a-h]%n",
			    onmb, &d, snm, &l) == 3 && l == strlen(uname)) {
				len = strlen(uname) + 1 + 1;
				rname = Malloc(len);
				(void) snprintf(rname, len, "/dev/r%s%u%s",
				    onmb, d, snm);
				*dnamep = metadiskname(rname);
				*uname_type = LOGICAL_DEVICE;
				return (rname);
			}

			/* /.../dsk/... or /.../rdsk/... */
			if ((rname = meta_dsk_to_rdsk(uname)) != NULL) {
				mdclrerror(ep);
				*dnamep = metadiskname(rname);
				*uname_type = LOGICAL_DEVICE;
				return (rname);
			}
		} else {
			/*
			 * If it's not an absolute name but is a valid ctd name,
			 * guess at /dev/rdsk/...
			 */
			uint_t	s;
			if (parse_ctd(uname, &s) == 0) {
				len = strlen(uname) + strlen("/dev/rdsk/") + 1;
				rname = Malloc(len);
				(void) snprintf(rname, len, "/dev/rdsk/%s",
				    uname);
				*dnamep = metadiskname(rname);
				*uname_type = LOGICAL_DEVICE;
				return (rname);
			}
		}
	}

	/* out of luck */
	if (!mdiserror(ep, MDE_AMBIGUOUS_DEV))
		(void) mderror(ep, MDE_UNIT_NOT_FOUND, uname);
	return (NULL);
}

/*
 * get number of slices for name
 */
static int
getnslice(
	char		*rname,
	char		*dname,
	uint_t		*slicep
)
{
	char		*srname;
	uint_t		nslice;
	size_t		dl = strlen(dname);
	size_t		rl = strlen(rname);
	size_t		l = 0;
	size_t		len;

	/*
	 * get our slice number - works only with names that end in s%u -
	 * all others return -1.
	 */
	if (dl >= rl ||
	    sscanf(&rname[dl], "s%u%n", slicep, &l) != 1 || l != rl ||
	    (int)*slicep < 0) {
		return (-1);
	}

	/*
	 * go find how many slices there really are
	 */
	len = strlen(dname) + 20 + 1;
	srname = Malloc(len);
	for (nslice = 0; /* void */; ++nslice) {
		struct stat	statbuf;

		/* build slice name */
		(void) snprintf(srname, len, "%ss%u", dname, nslice);

		/* see if it's there */
		if ((meta_stat(srname, &statbuf) != 0) ||
		    (! S_ISCHR(statbuf.st_mode))) {
			break;
		}
	}
	Free(srname);

	/* Need to make sure that we at least have V_NUMPAR */
	nslice = max(nslice, V_NUMPAR);

	/* make sure we have at least our slice */
	if (nslice < *slicep)
		return (-1);

	/* return number of slices */
	return (nslice);
}

/*
 * Attempt to parse the input string as a c[t]ds specifier
 * The target can either be a SCSI target id or if the device
 * is in a fabric configuration in a fibre channel setup then
 * the target is a standard WWN (world wide name).
 *
 * if successful	return 0
 * if c[t]dp name	return 1
 * otherwise		return -1
 */
int
parse_ctd(
	char	*uname,
	uint_t	*slice)
{
	uint_t	channel;
	uint_t	target;
	uint_t	device;
	int	has_target = 1;
	uint_t	cl;
	uint_t	target_str_len;
	char	*partial_ctd_str;
	char	*target_str;
	char	*device_start_pos;
	int	l = -1;

	/* pull off the channel spec and the 't' for the target */
	if (sscanf(uname, "c%ut%n", &channel, &l) != 1 || l == -1) {
		/* check for cds style name */
		if (sscanf(uname, "c%ud%n", &channel, &l) != 1 || l == -1) {
			return (-1);
		} else {
			l--;	/* we want to be on the 'd' */
			has_target = 0;
		}
	}
	partial_ctd_str = uname + l;

	/* find the beginning of the device specifier */
	device_start_pos = strrchr(partial_ctd_str, 'd');
	if (device_start_pos == NULL) {
		return (-1);
	}

	/* check to see if it is a ctd with a WWN or SCSI target */
	if (has_target) {
		/* pull off the target and see if it is a WWN */
		target_str_len = device_start_pos - partial_ctd_str + 2;
		target_str = (char *)Malloc(target_str_len+1);
		(void) strcpy(target_str, "0X");
		(void) strncpy(target_str+2, partial_ctd_str,
		    target_str_len - 2);
		target_str[target_str_len] = '\0';
		if (sscanf(target_str, "%x%n", &target, &l) != 1 ||
		    l != target_str_len) {
			Free(target_str);
			return (-1);
		}
		Free(target_str);
	}

	/* check the device and slice */
	cl = strlen(device_start_pos);
	if (sscanf(device_start_pos, "d%us%u%n", &device, slice, &l) != 2 ||
	    l != cl) {
		/* check the device and partition */
		if (sscanf(device_start_pos, "d%up%u%n", &device, slice, &l)
		    == 2 && l == cl) {
			return (1);
		}
		return (-1);
	}

	return (0);
}


/*
 * get number of slices for name
 */
static int
uname2sliceno(
	char			*uname,
	meta_device_type_t	uname_type,
	uint_t			*slicep,
	md_error_t		*ep
)
{
	uint_t			c = 0, t = 0, d = 0;
	int			l = 0, cl = 0;
	int			fd;
	struct dk_cinfo		cinfo;
	char			*p;
	char			*rname = NULL;


	if (uname_type == META_DEVICE)
		return (*slicep = 0);

	if ((p = strrchr(uname, '/')) != NULL)
		p++;
	else
		p = uname;

	cl = strlen(p);

	if (parse_ctd(p, slicep) == 0)
		return (*slicep);
	else if (sscanf(p, "mc%ut%ud%us%u%n", &c, &t, &d, slicep, &l) == 4 &&
	    l == cl)
		return (*slicep);
	else if (sscanf(p, "d%us%u%n", &d, slicep, &l) == 2 && l == cl)
		return (*slicep);

	/*
	 * If we can't get the slice from the name, then we have to do it the
	 * hard and expensive way.
	 */
	if ((rname = rawname(uname)) == NULL)
		return (-1);

	/* get controller info */
	if ((fd = open(rname, (O_RDONLY|O_NDELAY), 0)) < 0) {
		Free(rname);
		return (-1);
	}

	if (ioctl(fd, DKIOCINFO, &cinfo) != 0) {
		int	save = errno;

		if (save == ENOTTY)
			(void) mddeverror(ep, MDE_NOT_DISK, NODEV64, rname);
		else
			(void) mdsyserror(ep, save, rname);

		Free(rname);
		(void) close(fd);
		return (-1);
	}
	(void) close(fd);	/* sd/ssd bug */

	if (cinfo.dki_partition < V_NUMPAR) {
		Free(rname);
		return (*slicep = cinfo.dki_partition);
	}

	return (mddeverror(ep, MDE_NOT_DISK, NODEV64, rname));
}

/*
 * get partition info
 */
static int
getparts(
	mddrivename_t		*dnp,
	char			*rname,
	char			*dname,
	meta_device_type_t	uname_type,
	uint_t			*npartsp,
	uint_t			*partnop,
	md_error_t		*ep
)
{
	int		nparts;
	uint_t		partno;
	mdname_t	name;
	mdvtoc_t	*vtocp;

	/* metadevice */
	if (uname_type == META_DEVICE) {
		dnp->type = MDT_META;
		nparts = 1;
		partno = 0;
		goto gotit;
	}

	/* see how many partitions in drive, this is really tricky */
	metainitname(&name);
	name.rname = rname;
	name.drivenamep = dnp;
	if ((vtocp = metagetvtoc(&name, TRUE, &partno, ep)) != NULL) {
		dnp->type = MDT_COMP;
		nparts = vtocp->nparts;
		/* partno already setup */
		/* dname already setup */
		goto gotit;
	}

	if ((ep->info.errclass == MDEC_DEV) &&
	    (ep->info.md_error_info_t_u.dev_error.errnum == MDE_TOO_MANY_PARTS))
		return (-1);

	/* fallback and try and guess (used to check for just EACCES here) */
	if ((dname != NULL) &&
	    ((nparts = getnslice(rname, dname, &partno)) > 0)) {
		dnp->type = MDT_ACCES;
		if (mdanysyserror(ep)) {
			dnp->errnum =
			    ep->info.md_error_info_t_u.sys_error.errnum;
		} else {
			dnp->errnum = ENOENT;
		}
		mdclrerror(ep);
		/* nparts already setup */
		/* partno already setup */
		/* dname already setup */
		nparts = roundup(nparts, V_NUMPAR);
		goto gotit;
	}

	/* nothing worked */
	dnp->type = MDT_UNKNOWN;
	if (mdissyserror(ep, EACCES))
		dnp->type = MDT_ACCES;

	if (mdanysyserror(ep)) {
		dnp->errnum = ep->info.md_error_info_t_u.sys_error.errnum;
	} else {
		dnp->errnum = ENOENT;
	}

	mdclrerror(ep);
	nparts = V_NUMPAR;
	if (uname2sliceno(rname, uname_type, &partno, ep) < 0) {
		mdclrerror(ep);
		partno = 0;
	}

	/* return success */
gotit:
	assert(nparts > 0);

	if (partno >= nparts)
		return (mdsyserror(ep, ENOENT, rname));

	*npartsp = nparts;
	*partnop = partno;
	return (0);
}

/*
 * get block name
 */
static int
getbname(
	mdname_t	*np,
	md_error_t	*ep
)
{
	char		*rname = np->rname;
	char		*bname;

	/* fully qualified */
	assert(rname != NULL);
	if ((bname = blkname(rname)) != NULL) {
		if (np->bname)
			Free(np->bname);
		np->bname = bname;
		return (0);
	}

	/* out of luck */
	return (mdsyserror(ep, ENOENT, rname));
}

static void
getcname(
	mdsetname_t	*sp,
	mdname_t	*np
)
{
	char		*sname = sp->setname;
	char		*bname = np->bname;
	char		*p;
	size_t		len;

	assert(sname != NULL);
	assert(bname != NULL);
	assert(np->drivenamep->type != MDT_FAST_COMP &&
	    np->drivenamep->type != MDT_FAST_META);

	/* regular device */
	if ((strncmp(bname, "/dev/dsk/", strlen("/dev/dsk/")) == 0) &&
	    (strchr((p = bname + strlen("/dev/dsk/")), '/') == NULL)) {
		if (np->cname)
			Free(np->cname);
		np->cname = Strdup(p);
		return;
	}

	if ((strncmp(bname, "/dev/ap/dsk/", strlen("/dev/ap/dsk/")) == 0) &&
	    (strchr((p = bname + strlen("/dev/ap/dsk/")), '/') == NULL)) {
		if (np->cname)
			Free(np->cname);
		np->cname = Strdup(p);
		return;
	}

	if ((strncmp(bname, "/dev/did/dsk/", strlen("/dev/did/dsk/")) == 0) &&
	    (strchr((p = bname + strlen("/dev/did/dsk/")), '/') == NULL)) {
		if (np->cname)
			Free(np->cname);
		np->cname = Strdup(p);
		return;
	}

	/* anything else but metadevice */
	if (np->drivenamep->type != MDT_META) {
		if (np->cname)
			Free(np->cname);
		np->cname = Strdup(bname);
		return;
	}

	/* metadevice */
	p = strrchr(bname, '/');
	assert(p != NULL);
	++p;
	if (metaislocalset(sp)) {
		if (np->cname)
			Free(np->cname);
		np->cname = Strdup(p);
	} else {
		assert(sname[0] != '\0');
		if (np->cname)
			Free(np->cname);
		len = strlen(sname) + 1 + strlen(p) + 1;
		np->cname = Malloc(len);
		(void) snprintf(np->cname, len, "%s/%s", sname, p);
	}
}

/*
 * get dev
 */
int
meta_getdev(
	mdsetname_t	*sp,
	mdname_t	*np,
	md_error_t	*ep
)
{
	struct stat	statbuf;

	/* get dev */
	if (meta_stat(np->rname, &statbuf) != 0)
		return (mdsyserror(ep, errno, np->rname));
	else if (! S_ISCHR(statbuf.st_mode))
		return (mddeverror(ep, MDE_NOT_DISK, NODEV64, np->rname));
	np->dev = meta_expldev(statbuf.st_rdev);

	assert(np->drivenamep->type != MDT_FAST_META &&
	    np->drivenamep->type != MDT_FAST_COMP);

	/* check set */
	assert((np->drivenamep->type == MDT_META) ?
	    (sp->setno == MD_MIN2SET(meta_getminor(np->dev))) : 1);

	/* return sucess */
	return (0);
}

/*
 * set up names for a slice
 */
static int
getnames(
	mdsetname_t	*sp,
	mdname_t	*np,
	char		*rname,
	md_error_t	*ep
)
{
	/* get names */
	if (np->rname)
		Free(np->rname);
	np->rname = Strdup(rname);
	if (getbname(np, ep) != 0)
		return (-1);
	getcname(sp, np);
	if (meta_getdev(sp, np, ep) != 0)
		return (-1);

	/* return success */
	return (0);
}

/*
 * fake up names for a slice
 */
static void
getfakenames(
	mdsetname_t	*sp,
	mdname_t	*np,
	char		*rname
)
{
	char		*p;
	char		onmb[BUFSIZ+1], snm[BUFSIZ+1];
	uint_t		d = 0;
	int		l = 0;

	/* fake names */
	if (np->rname != NULL)
		Free(np->rname);
	np->rname = Strdup(rname);

	if (np->bname != NULL)
		Free(np->bname);
	np->bname = Strdup(rname);

	/*
	 * Fixup old style names
	 */
	if (sscanf(rname, "/dev/r%" VAL2STR(BUFSIZ) "[^0-9/]%u"
	    "%" VAL2STR(BUFSIZ) "[a-h]%n",
	    onmb, &d, snm, &l) == 3 && l == strlen(rname))
		(void) snprintf(np->bname, l, "/dev/%s%u%s", onmb, d, snm);

	/*
	 * Fixup new style names
	 */
	if ((p = strstr(np->bname, "/rdsk/")) != NULL) {
		for (++p; (*(p + 1) != '\0'); ++p)
			*p = *(p + 1);
		*p = '\0';
	}

	if (np->cname != NULL)
		Free(np->cname);
	getcname(sp, np);
}

static mdname_t *
setup_slice(
	mdsetname_t		*sp,
	meta_device_type_t	uname_type,
	mddrivename_t		*dnp,
	char			*uname,
	char			*rname,
	char			*dname,
	uint_t			partno,
	md_error_t		*ep
)
{
	char			*srname = NULL;
	mdname_t		*np;

	/* must have a set */
	assert(sp != NULL);
	assert(partno < dnp->parts.parts_len);
	assert(dname != NULL);

	np = &dnp->parts.parts_val[partno];

	if (rname)
		srname = rname;
	else if (uname_type == META_DEVICE)
		srname = dname;
	else {
		char	onmb[BUFSIZ+1];
		uint_t	d = 0;
		int	l = 0, cl = strlen(dname);
		size_t	len;

		len = cl + 20 + 1;
		srname = Malloc(len);

		/*
		 * Handle /dev/rXXNN.
		 */
		if (sscanf(dname, "/dev/r%" VAL2STR(BUFSIZ) "[^0-9/]%u%n",
		    onmb, &d, &l) == 2 && l == cl) {
			(void) snprintf(srname, len, "/dev/r%s%u%c", onmb, d,
			    'a' + partno);
		} else if (sscanf(dname, "/dev/%" VAL2STR(BUFSIZ) "[^0-9/]%u%n",
		    onmb, &d, &l) == 2 && l == cl) {
			(void) snprintf(srname, len, "/dev/%s%u%c", onmb, d,
			    'a' + partno);
		} else {
			/* build the slice that is wanted */
			(void) snprintf(srname, len, "%ss%u", dname, partno);
		}
	}

	if (getnames(sp, np, srname, ep) != 0) {
		if (dnp->type == MDT_UNKNOWN) {
			mdclrerror(ep);
			getfakenames(sp, np, srname);
		} else if (dnp->type == MDT_COMP && mdissyserror(ep, ENOENT)) {
			dnp->type = MDT_UNKNOWN;
			if (mdanysyserror(ep)) {
				dnp->errnum =
				    ep->info.md_error_info_t_u.sys_error.errnum;
			} else {
				dnp->errnum = ENOENT;
			}
			mdclrerror(ep);
			getfakenames(sp, np, srname);
		} else {
			mdclrerror(ep);
			if (getnames(sp, np, dname, ep) != 0) {
				np = NULL;
				goto fixup;
			}
		}
	}

out:
	if ((srname != rname) && (srname != dname))
		Free(srname);

	/* return name */
	return (np);

fixup:
	if (mdanysyserror(ep)) {
		char	*p;
		int	errnum = ep->info.md_error_info_t_u.sys_error.errnum;

		mdclrerror(ep);
		if (uname && *uname) {
			if ((p = strrchr(uname, '/')) != NULL)
				(void) mdsyserror(ep, errnum, ++p);
			else
				(void) mdsyserror(ep, errnum, uname);
		} else {
			if ((p = strrchr(srname, '/')) != NULL)
				(void) mdsyserror(ep, errnum, ++p);
			else
				(void) mdsyserror(ep, errnum, srname);
		}
	}
	goto out;
}

/*
 * flush the fast name cache
 */
static void
metafreefastnm(mdname_t **np)
{
	mddrivename_t	*dnp;

	assert(np != NULL && *np != NULL);

	if ((dnp = (*np)->drivenamep) != NULL) {
		if (dnp->cname != NULL)
			Free(dnp->cname);
		if (dnp->rname != NULL)
			Free(dnp->rname);
		if (dnp->miscname != NULL)
			Free(dnp->miscname);
		meta_free_unit(dnp);
		Free(dnp);
	}
	if ((*np)->cname != NULL)
		Free((*np)->cname);
	if ((*np)->bname != NULL)
		Free((*np)->bname);
	if ((*np)->rname != NULL)
		Free((*np)->rname);
	if ((*np)->devicesname != NULL)
		Free((*np)->devicesname);
	Free(*np);
	*np = NULL;
}

/*
 * flush the fast name cache
 */
static void
metaflushfastnames()
{
	mdnamelist_t	*p, *n;

	for (p = fastnmlp, n = NULL; (p != NULL); p = n) {
		n = p->next;
		metafreefastnm(&p->namep);
		Free(p);
	}
	fastnmlp = NULL;
}

static char *
getrname_fast(char *unm, meta_device_type_t uname_type, md_error_t *ep)
{
	uint_t			d = 0;
	int			l = 0;
	int			cl = strlen(unm);
	char			onmb[BUFSIZ+1], snm[BUFSIZ+1], cnmb[BUFSIZ];
	char			*rnm;
	size_t			len;

	if (uname_type == META_DEVICE) {
		/* fully qualified  - local set */
		if (((sscanf(unm, "/dev/md/dsk/%" VAL2STR(BUFSIZ) "s%n",
		    onmb, &len) == 1) && (cl == len)) || ((sscanf(unm,
		    "/dev/md/rdsk/%" VAL2STR(BUFSIZ) "s%n",
		    onmb, &len) == 1) && (cl == len))) {
			len = strlen("/dev/md/rdsk/") +	strlen(onmb) + 1;
			rnm = Zalloc(len);
			(void) snprintf(rnm, len, "/dev/md/rdsk/%s", onmb);
			return (rnm);
		}

		/* fully qualified - setname specified */
		if (((sscanf(unm, "/dev/md/%[^/]/dsk/%"
		    VAL2STR(BUFSIZ) "s%n", snm, onmb, &len) == 2) &&
		    (cl == len)) || ((sscanf(unm, "/dev/md/%[^/]/rdsk/%"
		    VAL2STR(BUFSIZ) "s%n", snm, onmb, &len) == 2) &&
		    (cl == len))) {

			len = strlen("/dev/md//rdsk/") + strlen(snm) +
			    strlen(onmb) + 1;
			rnm = Zalloc(len);
			(void) snprintf(rnm, len, "/dev/md/%s/rdsk/%s",
			    snm, onmb);
			return (rnm);
		}

		/* Fully qualified path - error */
		if (unm[0] == '/') {
			(void) mdsyserror(ep, EINVAL, unm);
			return (NULL);
		}

		/* setname specified <setname>/<metadev> */
		if (((sscanf(unm, "%[^/]/%" VAL2STR(BUFSIZ) "s%n",
		    snm, onmb, &len) == 2) && (cl == len))) {
			/* Not <setname>/<metadev>  - error */
			if (strchr(onmb, '/') != NULL) {
				(void) mdsyserror(ep, EINVAL, unm);
				return (NULL);
			}

			len = strlen("/dev/md//rdsk/") + strlen(snm) +
			    strlen(onmb) + 1;
			rnm = Zalloc(len);
			(void) snprintf(rnm, len, "/dev/md/%s/rdsk/%s",
			    snm, onmb);
			return (rnm);
		}

		/* Must be simple metaname/hsp pool name */
		len = strlen("/dev/md/rdsk/") + strlen(unm) + 1;
		rnm = Zalloc(len);
		(void) snprintf(rnm, len, "/dev/md/rdsk/%s", unm);
		return (rnm);
	}

	/* NOT Fully qualified path, done */
	if (unm[0] != '/') {
		(void) mdsyserror(ep, EINVAL, unm);
		return (NULL);
	}

	/*
	 * Get slice information from old style names of the form
	 * /dev/rXXNN[a-h] or /dev/XXNN[a-h], must be done before regular
	 * devices, but after metadevices.
	 */
	if ((sscanf(unm, "/dev/r%" VAL2STR(BUFSIZ) "[^0-9/]%u"
	    "%" VAL2STR(BUFSIZ) "[a-h]%n",
	    onmb, &d, snm, &l) == 3 ||
	    sscanf(unm, "/dev/%" VAL2STR(BUFSIZ) "[^0-9/]%u"
	    "%" VAL2STR(BUFSIZ) "[a-h]%n",
	    onmb, &d, snm, &l) == 3) && l == cl) {
		if (strchr("abcdefgh", snm[0]) != NULL) {
			(void) snprintf(cnmb, sizeof (cnmb), "/dev/r%s%u%s",
			    onmb, d, snm);
			return (Strdup(cnmb));
		}
	}

	/* /.../dsk/... or /.../rdsk/... */
	if ((rnm = meta_dsk_to_rdsk(unm)) != NULL)
		return (rnm);

	/*
	 * Shouldn't get here but if we do then we have an unrecognized
	 * fully qualified path - error
	 */
	(void) mdsyserror(ep, EINVAL, unm);
	return (NULL);
}

static mdname_t *
metainitfastname(
	mdsetname_t		*sp,
	char			*uname,
	meta_device_type_t	uname_type,
	md_error_t		*ep
)
{
	uint_t			c = 0, t = 0, d = 0, s = 0;
	int			l = 0;
	mddrivename_t		*dnp;
	mdname_t		*np;
	mdnamelist_t		**fnlpp;
	char			*cname;

	for (fnlpp = &fastnmlp; (*fnlpp != NULL); fnlpp = &(*fnlpp)->next) {
		np = (*fnlpp)->namep;

		if (strcmp(np->bname, uname) == 0)
			return (np);
	}

	*fnlpp = Zalloc(sizeof (**fnlpp));
	np = (*fnlpp)->namep = Zalloc(sizeof (mdname_t));
	metainitname(np);
	dnp = np->drivenamep = Zalloc(sizeof (mddrivename_t));
	metainitdrivename(dnp);


	/* Metadevices */
	if (uname_type == META_DEVICE &&
	    (cname = meta_canonicalize(sp, uname)) != NULL) {

		np->cname = cname;
		dnp->type = MDT_FAST_META;
		goto done;
	}

	/* Others */
	dnp->type = MDT_FAST_COMP;

	if (((sscanf(uname, "/dev/rdsk/c%ut%ud%us%u%n", &c, &t, &d,
	    &s, &l) == 4 ||
	    sscanf(uname, "/dev/dsk/c%ut%ud%us%u%n", &c, &t, &d,
	    &s, &l) == 4 ||
	    sscanf(uname, "/dev/ap/rdsk/mc%ut%ud%us%u%n", &c, &t, &d,
	    &s, &l) == 4 ||
	    sscanf(uname, "/dev/ap/dsk/mc%ut%ud%us%u%n", &c, &t, &d,
	    &s, &l) == 4 ||
	    sscanf(uname, "/dev/did/rdsk/d%us%u%n", &t, &s, &l) == 2 ||
	    sscanf(uname, "/dev/did/dsk/d%us%u%n", &t, &s, &l) == 2||
	    sscanf(uname, "/dev/rdsk/c%ud%us%u%n", &c, &d, &s, &l) == 3 ||
	    sscanf(uname, "/dev/dsk/c%ud%us%u%n", &c, &d, &s, &l) == 3 ||
	    sscanf(uname, "/dev/rdsk/c%ut%ud%u%n", &c, &t, &d, &l) == 3 ||
	    sscanf(uname, "/dev/dsk/c%ut%ud%u%n", &c, &t, &d, &l) == 3 ||
	    sscanf(uname, "/dev/ap/rdsk/mc%ut%ud%u%n", &c, &t, &d, &l) == 3 ||
	    sscanf(uname, "/dev/ap/dsk/mc%ut%ud%u%n", &c, &t, &d, &l) == 3 ||
	    sscanf(uname, "/dev/did/rdsk/d%u%n", &t, &l) == 1 ||
	    sscanf(uname, "/dev/did/dsk/d%u%n", &t, &l) == 1 ||
	    sscanf(uname, "/dev/rdsk/c%ud%u%n", &c, &d, &l) == 2 ||
	    sscanf(uname, "/dev/dsk/c%ud%u%n", &c, &d, &l) == 2) &&
	    l == strlen(uname))) {
		if ((np->cname = strrchr(uname, '/')) == NULL)
			np->cname = Strdup(uname);
		else
			np->cname = Strdup(++np->cname);
	} else {
		np->cname = Strdup(uname);
	}

done:
	/* Driver always gives us block names */
	np->bname = Strdup(uname);

	/* canonical disk name */
	if ((dnp->cname = metadiskname(np->cname)) == NULL)
		dnp->cname = Strdup(np->cname);

	if ((np->rname = getrname_fast(uname, uname_type, ep)) != NULL) {
		if ((dnp->rname = metadiskname(np->rname)) == NULL)
			dnp->rname = Strdup(np->rname);
	} else {
		metafreefastnm(&(*fnlpp)->namep);
		Free(*fnlpp);
		*fnlpp = NULL;
		return (NULL);
	}

	/* cleanup, return success */
	return (np);
}

/*
 * set up names for a device
 */
static mdname_t *
metaname_common(
	mdsetname_t	**spp,
	char		*uname,
	int		fast,
	meta_device_type_t	uname_type,
	md_error_t	*ep
)
{
	mddrivenamelist_t	**tail;
	mddrivename_t		*dnp;
	uint_t			slice;
	mdname_t		*np;
	char			*rname = NULL;
	char			*dname = NULL;
	char			*cname = NULL;
	uint_t			nparts, partno;

	assert(uname != NULL);

	/* check setname */
	if ((cname = meta_name_getname(spp, uname, uname_type, ep)) == NULL)
		return (NULL);

	assert(*spp != NULL);
	Free(cname);

	/* get raw name (rname) of the slice and drive (dname) we have */
	if ((rname = getrawnames(spp, uname,
	    &dname, &uname_type, ep)) == NULL) {
		return (NULL);
	}

	assert(uname_type != UNKNOWN);

	/* look in cache first */
	for (tail = &drivelistp; (*tail != NULL); tail = &(*tail)->next) {
		dnp = (*tail)->drivenamep;

		/* check to see if the drive name is already in the cache */
		if ((dnp->rname != NULL) && strcmp(dnp->rname, dname) == 0) {

			Free(rname);
			if (dname != NULL)
				Free(dname);

			if (uname2sliceno(uname, uname_type, &partno, ep) < 0)
				return (NULL);

			return (metaslicename(dnp, partno, ep));
		}
	}

	/*
	 * If a fast names is OK, then get one, and be done.
	 */
	if (fast) {
		Free(rname);
		if (dname != NULL)
			Free(dname);

		return (metainitfastname(*spp, uname, uname_type, ep));
	}

	/* allocate new list element and drive */
	*tail = Zalloc(sizeof (**tail));
	dnp = (*tail)->drivenamep = Zalloc(sizeof (*dnp));

	metainitdrivename(dnp);

	/* get parts info */
	if (getparts(dnp, rname, dname, uname_type, &nparts, &partno, ep) != 0)
		goto out;

	/*
	 * libmeta needs at least V_NUMPAR partitions.
	 * If we have an EFI partition with less than V_NUMPAR slices,
	 * we nevertheless reserve space for V_NUMPAR
	 */
	if (nparts < V_NUMPAR) {
		nparts = V_NUMPAR;
	}

	/* allocate and link in parts */
	dnp->parts.parts_len = nparts;
	dnp->parts.parts_val = Zalloc((sizeof (*dnp->parts.parts_val)) *
	    dnp->parts.parts_len);
	for (slice = 0; (slice < nparts); ++slice) {
		np = &dnp->parts.parts_val[slice];
		metainitname(np);
		np->drivenamep = dnp;
	}

	/* setup name_t (or slice) wanted */
	if ((np = setup_slice(*spp, uname_type, dnp, uname, rname,
	    dname, partno, ep)) == NULL)
		goto out;

	/* canonical disk name */
	if ((dnp->cname = metadiskname(np->cname)) == NULL)
		dnp->cname = Strdup(np->cname);
	if ((dnp->rname = metadiskname(np->rname)) == NULL)
		dnp->rname = Strdup(np->rname);

	/* cleanup, return success */
	if (dname != NULL)
		Free(dname);
	Free(rname);
	return (np);

	/* cleanup, return error */
out:
	if (dname != NULL)
		Free(dname);
	if (rname != NULL)
		Free(rname);

	metafreedrivename(dnp);
	Free(dnp);
	Free(*tail);
	*tail = NULL;
	return (NULL);
}

/*
 * metaname()
 *
 * Wrapper function for metaname_common()
 * If the second arg is a metadevice name then it is important that this should
 * be a canonical name (eg d30 rather than /dev/md/dsk/d30). If this is not the
 * case then a bad entry may be placed into the drivelistp cache.
 */
mdname_t *
metaname(
	mdsetname_t	**spp,
	char		*uname,
	meta_device_type_t	uname_type,
	md_error_t	*ep
)
{
	return (metaname_common(spp, uname, 0, uname_type, ep));
}

mdname_t *
metaname_fast(
	mdsetname_t	**spp,
	char		*uname,
	meta_device_type_t	uname_type,
	md_error_t	*ep
)
{
	return (metaname_common(spp, uname, 1, uname_type, ep));
}
/*
 * Get the dnp using the device id.
 *
 * We have the potential to have more than 1 dnp with the same disk name but
 * have different device ids. This would happen in the case of a partial
 * diskset. The unavailable disk name is relative to the prior host and could
 * possibly be the same as a disk on this system. The only way to tell which
 * dnp belongs with this disk is by searching by device id. We have the
 * potential to have the case where 1) the disk who's device id we pass in is
 * in the system. In this case the name and the device id are both valid for
 * the disk. 2) The disk whose device id we've been passed is not in the
 * system and no disk with the same name has a dnp on the list. And 3) The
 * disk whose device id we've been passed is not on the system but there is
 * a disk with the same name (different devid) that is on the system. Here's
 * what we return for each of those cases:
 * 1) If disk is in system:
 * 	disk is found on drivelistp or we create a new drivename and it's
 * 	fully populated as expected.
 * 2) If disk not in system, no collision
 *	Disk with the same devid is not found on drivelistp, we create a new
 *	drivename structure and the dnp->devid is filled in not from getparts
 *	but from the devidp passed in. No other disk in the system has the
 *	same "name" or devid.
 *	This situation would be caused by the import of a partial diskset.
 * 3) If disk not in system, collision
 *	Disk with the same devid is not found on the drivelistp, we create a
 *	new drivename struct but getparts will use the information from the
 *	name which is actually in reference to another disk of the same name
 *	in the system. getparts will fill in the dnp->devid with the value
 *	from the other disk and	we overwrite this with the value of this disk.
 *	To get into this situation one of the disks is actually unavailable
 *	as in the case of a partial import.
 */
mddrivename_t *
meta_getdnp_bydevid(
	mdsetname_t	*sp,
	side_t		sideno,
	ddi_devid_t	devidp,
	mdkey_t		key,
	md_error_t	*ep
)
{
	ddi_devid_t		dnp_devidp;
	char			*nm;
	mddrivenamelist_t	**tail;
	mddrivename_t		*dnp;
	uint_t			slice;
	mdname_t		*np;
	char			*rname = NULL;
	char			*dname = NULL;
	uint_t			nparts, partno;
	int			ret;
	md_set_desc		*sd = NULL;
	meta_device_type_t	uname_type = LOGICAL_DEVICE;

	/* look in the cache first */
	for (tail = &drivelistp; (*tail != NULL); tail = &(*tail)->next) {
		dnp = (*tail)->drivenamep;
		if (dnp->type != MDT_COMP)
			continue;
		ret = devid_str_decode(dnp->devid, &dnp_devidp, NULL);
		if (ret != 0) {
			/* unable to decode the devid */
			return (NULL);
		}
		/* compare with the devid passed in. */
		if (devid_compare(devidp, dnp_devidp) == 0) {
			/* match! We have the same disk */
			devid_free(dnp_devidp);
			return (dnp);
		}
		devid_free(dnp_devidp);
	}

	/* drive not in the cache */

	if ((sd = metaget_setdesc(sp, ep)) == NULL) {
		return (NULL);
	}
	/* get namespace info */
	if (MD_MNSET_DESC(sd)) {
		if ((nm = meta_getnmbykey(MD_LOCAL_SET, sideno,
		    key, ep)) == NULL)
			return (NULL);
	} else {
		if ((nm = meta_getnmbykey(MD_LOCAL_SET,
		    sideno+SKEW, key, ep)) == NULL)
			return (NULL);
	}

	/* get raw name (rname) of the slice and drive name (dname) */
	if ((rname = getrawnames(&sp, nm, &dname, &uname_type, ep)) == NULL) {
		return (NULL);
	}

	/* allocate new list element and drive */
	*tail = Zalloc(sizeof (**tail));
	dnp = (*tail)->drivenamep = Zalloc(sizeof (*dnp));
	metainitdrivename(dnp);

	/* get parts info */
	/*
	 * Note that if the disk is unavailable this name will point to
	 * either a nonexistent disk and thus the part info and devid will
	 * be empty or the name will point to the wrong disk and this
	 * information will be invalid. Because of this, we overwrite the
	 * dnp->devid with the correct one after getparts returns.
	 */
	if (getparts(dnp, rname, dname, uname_type, &nparts, &partno, ep) != 0)
		goto out;

	dnp->devid = devid_str_encode(devidp, NULL);

	/*
	 * libmeta needs at least V_NUMPAR partitions.
	 * If we have an EFI partition with less than V_NUMPAR slices,
	 * we nevertheless reserve space for V_NUMPAR
	 */
	if (nparts < V_NUMPAR) {
		nparts = V_NUMPAR;
	}

	/* allocate and link in parts */
	dnp->parts.parts_len = nparts;
	dnp->parts.parts_val = Zalloc((sizeof (*dnp->parts.parts_val)) *
	    dnp->parts.parts_len);

	for (slice = 0; (slice < nparts); ++slice) {
		np = &dnp->parts.parts_val[slice];
		metainitname(np);
		np->drivenamep = dnp;
	}

	/* setup name_t (or slice) wanted */
	if ((np = setup_slice(sp, uname_type, dnp, nm, rname,
	    dname, partno, ep)) == NULL)
		goto out;

	/* canonical disk name */
	if ((dnp->cname = metadiskname(np->cname)) == NULL)
		dnp->cname = Strdup(np->cname);
	if ((dnp->rname = metadiskname(np->rname)) == NULL)
		dnp->rname = Strdup(np->rname);

	if (dname != NULL)
		Free(dname);
	Free(rname);
	return (dnp);

out:
	if (dname != NULL)
		Free(dname);

	if (rname != NULL)
		Free(rname);

	metafreedrivename(dnp);
	Free(dnp);
	Free(*tail);
	*tail = NULL;
	return (NULL);
}

/*
 * Search the drivename list by devid instead of name. If you don't find
 * an entry with the same device id, create one for the uname passed in.
 */
mddrivename_t *
metadrivenamebydevid(
	mdsetname_t		**spp,
	char			*devid,
	char			*uname,
	md_error_t		*ep
)
{
	ddi_devid_t		dnp_devidp, in_devidp;
	mdname_t		*np;
	mddrivenamelist_t	**tail;
	char			*rname = NULL;
	mddrivename_t		*dnp;
	char			*dname;
	int			ret;
	uint_t			nparts, partno;
	uint_t			slice;
	meta_device_type_t	uname_type = LOGICAL_DEVICE;

	/* look in the cache first */
	for (tail = &drivelistp; (*tail != NULL); tail = &(*tail)->next) {
		dnp = (*tail)->drivenamep;
		if (dnp->type != MDT_COMP)
			continue;

		/* decode the dnp devid */
		ret = devid_str_decode(dnp->devid, &dnp_devidp, NULL);
		if (ret != 0) {
			/* unable to decode the devid */
			return (NULL);
		}
		/* decode the passed in devid */
		ret = devid_str_decode(devid, &in_devidp, NULL);
		if (ret != 0) {
			/* unable to decode the devid */
			devid_free(dnp_devidp);
			return (NULL);
		}
		/* compare with the devids */
		if (devid_compare(in_devidp, dnp_devidp) == 0) {
			/* match! We have the same disk */
			devid_free(dnp_devidp);
			devid_free(in_devidp);
			return (dnp);
		}
	}
	devid_free(dnp_devidp);
	devid_free(in_devidp);

	/* not in the cache */

	/* get raw name (rname) of the slice and drive (dname) we have */
	if ((rname = getrawnames(spp, uname, &dname, &uname_type,
	    ep)) == NULL) {
		return (NULL);
	}

	/* allocate new list element and drive */
	*tail = Zalloc(sizeof (**tail));
	dnp = (*tail)->drivenamep = Zalloc(sizeof (*dnp));

	metainitdrivename(dnp);

	/* get parts info */
	if (getparts(dnp, rname, dname, uname_type, &nparts, &partno, ep) != 0)
		goto out;

	/*
	 * libmeta needs at least V_NUMPAR partitions.
	 * If we have an EFI partition with less than V_NUMPAR slices,
	 * we nevertheless reserve space for V_NUMPAR
	 */
	if (nparts < V_NUMPAR) {
		nparts = V_NUMPAR;
	}

	/* allocate and link in parts */
	dnp->parts.parts_len = nparts;
	dnp->parts.parts_val = Zalloc((sizeof (*dnp->parts.parts_val)) *
	    dnp->parts.parts_len);
	for (slice = 0; (slice < nparts); ++slice) {
		np = &dnp->parts.parts_val[slice];
		metainitname(np);
		np->drivenamep = dnp;
	}

	/* setup name_t (or slice) wanted */
	if ((np = setup_slice(*spp, uname_type, dnp, uname, rname,
	    dname, partno, ep)) == NULL)
		goto out;

	/* canonical disk name */
	if ((dnp->cname = metadiskname(np->cname)) == NULL)
		dnp->cname = Strdup(np->cname);
	if ((dnp->rname = metadiskname(np->rname)) == NULL)
		dnp->rname = Strdup(np->rname);

	/* cleanup, return success */
	if (dname != NULL)
		Free(dname);
	Free(rname);
	return (dnp);

	/* cleanup, return error */
out:
	if (dname != NULL)
		Free(dname);
	if (rname != NULL)
		Free(rname);

	metafreedrivename(dnp);
	Free(dnp);
	Free(*tail);
	*tail = NULL;
	return (NULL);
}
/*
 * set up names for a drive
 */
mddrivename_t *
metadrivename(
	mdsetname_t		**spp,
	char			*uname,
	md_error_t		*ep
)
{
	char		*slicename;
	mdname_t	*np;

	mddrivenamelist_t **tail;
	mddrivename_t	*dnp;
	char		*dname;
	int		i;
	int		mplen;
	size_t		len;

	assert(uname != NULL);

	if ((dname = metadiskname(uname)) == NULL) {
		(void) mdsyserror(ep, ENOENT, uname);
		return (NULL);
	}

	/* look in cache first */
	for (tail = &drivelistp; (*tail != NULL); tail = &(*tail)->next) {
		dnp = (*tail)->drivenamep;
		if ((dnp->cname != NULL &&
		    (strcmp(dnp->cname, dname) == 0)) ||
		    (dnp->rname != NULL &&
		    (strcmp(dnp->rname, dname) == 0))) {
			Free(dname);
			return (dnp);
		}
	}
	Free(dname);

	/* Check each possible slice name based on MD_MAX_PARTS. */

	/*
	 * Figure out how much string space to reserve to fit
	 * (MD_MAX_PARTS - 1) into the name string; the loop will
	 * increment the mplen counter once for each decimal digit in
	 * (MD_MAX_PARTS - 1).
	 */
	for (i = MD_MAX_PARTS - 1, mplen = 0; i; i /= 10, ++mplen)
		;
	len = strlen(uname) + mplen + 2;
	slicename = Malloc(len);

	/* Check for each slice in turn until we find one */
	for (np = NULL, i = 0; ((np == NULL) && (i < MD_MAX_PARTS)); ++i) {
		(void) snprintf(slicename, len, "%ss%d", uname, i);
		np = metaname(spp, slicename, LOGICAL_DEVICE, ep);
	}
	Free(slicename);

	if (np == NULL) {
		if ((mdiserror(ep, MDE_UNIT_NOT_FOUND)) &&
		    ((dname = metadiskname(uname)) != NULL)) {
			Free(dname);
			(void) mderror(ep, MDE_NOT_DRIVENAME, uname);
		}
		return (NULL);
	}
	return (np->drivenamep);
}

/*
 * FUNCTION:	metaslicename_type()
 * INPUT:	dnp	- the drivename structure
 *		sliceno	- the slice on the drive to return
 *		type - LOGICAL_DEVICE or META_DEVICE
 * OUTPUT:	ep	- return error pointer
 * RETURNS:	mdname_t- pointer the the slice name structure
 * PURPOSE:	interface to the parts struct in the drive name struct
 *		Since there is no guarantee that the slice name
 *		structures are populated users should call this
 *		function rather than accessing the structure directly
 *		since it will populate the structure values if they
 *		haven't already been populated before returning.
 */
mdname_t *
metaslicename_type(
	mddrivename_t		*dnp,
	uint_t			sliceno,
	meta_device_type_t	uname_type,
	md_error_t		*ep
)
{
	mdsetname_t	*sp = NULL;
	char		*namep = NULL;
	mdname_t	*np;

	assert(dnp->type != MDT_FAST_COMP && dnp->type != MDT_FAST_META);

	if (sliceno >= dnp->parts.parts_len) {
		(void) mderror(ep, MDE_NOSLICE, dnp->cname);
		return (NULL);
	}

	np = &dnp->parts.parts_val[sliceno];

	/* check to see if the struct is already populated */
	if (np->cname) {
		return (np);
	}

	if ((namep = meta_name_getname(&sp, dnp->cname,
	    uname_type, ep)) == NULL)
		return (NULL);

	np = setup_slice(sp, uname_type, dnp, NULL, NULL, dnp->rname,
	    sliceno, ep);

	Free(namep);

	return (np);
}

/*
 * FUNCTION:	metaslicename()
 * INPUT:	dnp	- the drivename structure
 *		sliceno	- the slice on the drive to return
 * OUTPUT:	ep	- return error pointer
 * RETURNS:	mdname_t- pointer the the slice name structure
 * PURPOSE:	interface to the parts struct in the drive name struct
 *		Since there is no guarantee that the slice name
 *		structures are populated users should call this
 *		function rather than accessing the structure directly
 *		since it will populate the structure values if they
 *		haven't already been populated before returning.
 */
mdname_t *
metaslicename(
	mddrivename_t	*dnp,
	uint_t		sliceno,
	md_error_t	*ep
)
{
	return (metaslicename_type(dnp, sliceno, LOGICAL_DEVICE, ep));
}

/*
 * set up metadevice name from id
 */
mdname_t *
metamnumname(
	mdsetname_t	**spp,
	minor_t		mnum,
	int		fast,
	md_error_t	*ep
)
{
	set_t		setno = MD_MIN2SET(mnum);
	mdsetname_t	*sp = NULL;
	char		*uname;
	mdname_t	*np;
	md_dev64_t	dev;
	mdkey_t		key;

	/* check set first */
	if (spp == NULL)
		spp = &sp;
	if (chksetno(spp, setno, ep) != 0)
		return (NULL);
	assert(*spp != NULL);
	sp = *spp;

	/* get corresponding device name */
	dev = metamakedev(mnum);
	if ((uname = meta_getnmentbydev(sp->setno, MD_SIDEWILD, dev,
	    NULL, NULL, &key, ep)) == NULL)
		return (NULL);

	/* setup name */
	if (fast) {
		np = metaname_fast(spp, uname, META_DEVICE, ep);
		if (np) {
			np->dev = dev;
			np->key = key;
		}
	} else
		np = metaname(spp, uname, META_DEVICE, ep);

	Free(uname);
	return (np);
}

/*
 * return metadevice name
 */
char *
get_mdname(
	mdsetname_t	*sp,
	minor_t		mnum
)
{
	mdname_t	*np;
	md_error_t	status = mdnullerror;
	mdsetname_t	**spp = NULL;

	if (sp != NULL)
		spp = &sp;

	/* get name */
	if ((np = metamnumname(spp, mnum, 0, &status)) == NULL) {
		return (NULL);
	}
	assert(meta_getminor(np->dev) == mnum);

	/* return name */
	return (np->cname);
}

/*
 * check for device type
 */
int
metaismeta(
	mdname_t	*np
)
{
	return (np->drivenamep->type == MDT_META ||
	    np->drivenamep->type == MDT_FAST_META);
}

int
metachkmeta(
	mdname_t	*np,
	md_error_t	*ep
)
{
	if (! metaismeta(np)) {
		return (mddeverror(ep, MDE_NOT_META, np->dev,
		    np->cname));
	}
	return (0);
}

int
metachkdisk(
	mdname_t	*np,
	md_error_t	*ep
)
{
	mddrivename_t	*dnp = np->drivenamep;

	assert(dnp->type != MDT_FAST_COMP && dnp->type != MDT_FAST_META);

	if ((! metaismeta(np)) && (dnp->type != MDT_COMP)) {
		switch (dnp->type) {
			case MDT_ACCES:
			case MDT_UNKNOWN:
				return (mdsyserror(ep, dnp->errnum, np->bname));
			default:
				assert(0);
				return (mddeverror(ep, MDE_NOT_DISK, np->dev,
				    np->cname));
		}
	}
	return (0);
}

int
metachkcomp(
	mdname_t	*np,
	md_error_t	*ep
)
{
	if (metaismeta(np)) {
		return (mddeverror(ep, MDE_IS_META, np->dev,
		    np->cname));
	}
	return (metachkdisk(np, ep));
}

/*
 * free list of names
 */
void
metafreenamelist(
	mdnamelist_t	*nlp
)
{
	mdnamelist_t	*next = NULL;

	for (/* void */; (nlp != NULL); nlp = next) {
		next = nlp->next;
		Free(nlp);
	}
}

/*
 * build list of names
 */
int
metanamelist(
	mdsetname_t	**spp,
	mdnamelist_t	**nlpp,
	int		argc,
	char		*argv[],
	meta_device_type_t	type,
	md_error_t	*ep
)
{
	mdnamelist_t	**tailpp = nlpp;
	int		count = 0;

	for (*nlpp = NULL; (argc > 0); ++count, --argc, ++argv) {
		mdnamelist_t	*nlp = Zalloc(sizeof (*nlp));

		if ((nlp->namep = metaname(spp, argv[0],
		    type, ep)) == NULL) {
			metafreenamelist(*nlpp);
			*nlpp = NULL;
			return (-1);
		}
		*tailpp = nlp;
		tailpp = &nlp->next;
	}
	return (count);
}

/*
 * append to end of name list
 */
mdname_t *
metanamelist_append(
	mdnamelist_t	**nlpp,
	mdname_t	*np
)
{
	mdnamelist_t	*nlp;

	/* run to end of list */
	for (; (*nlpp != NULL); nlpp = &(*nlpp)->next)
		;

	/* allocate new list element */
	nlp = *nlpp = Zalloc(sizeof (*nlp));

	/* append name */
	nlp->namep = np;
	return (np);
}

/*
 * FUNCTION:	meta_namelist_append_wrapper()
 * INPUT:	tailpp	- pointer to the list tail pointer
 *		np	- name node to be appended to list
 * OUTPUT:	none
 * RETURNS:	mdnamelist_t * - new tail of the list.
 * PURPOSE:	wrapper to meta_namelist_append for performance.
 *		metanamelist_append finds the tail each time which slows
 *		down long lists.  By keeping track of the tail ourselves
 *		we can change metanamelist_append into a constant time
 *		operation.
 */
mdnamelist_t **
meta_namelist_append_wrapper(
	mdnamelist_t	**tailpp,
	mdname_t	*np
)
{
	(void) metanamelist_append(tailpp, np);

	/* If it's the first item in the list, return it instead of the next */
	if ((*tailpp)->next == NULL)
		return (tailpp);

	return (&(*tailpp)->next);
}


/*
 *	mdhspname_t stuff
 */

/*
 * initialize hspname
 */
static void
metainithspname(
	mdhspname_t	*hspnamep
)
{
	(void) memset(hspnamep, '\0', sizeof (*hspnamep));
	hspnamep->hsp = MD_HSP_NONE;
}

/*
 * free allocated hspname
 */
static void
metafreehspname(
	mdhspname_t	*hspnamep
)
{
	if (hspnamep->hspname != NULL)
		Free(hspnamep->hspname);
	if (hspnamep->unitp != NULL)
		meta_invalidate_hsp(hspnamep);
	metainithspname(hspnamep);
}

/*
 * clear the hspname cache
 */
static void
metaflushhspnames()
{
	mdhspnamelist_t		*p, *n;

	for (p = hsplistp, n = NULL; (p != NULL); p = n) {
		n = p->next;
		metafreehspname(p->hspnamep);
		Free(p->hspnamep);
		Free(p);
	}
	hsplistp = NULL;
}

/*
 * check set and get comparison name
 */
static char *
gethspname(
	mdsetname_t	**spp,
	char		*uname,
	md_error_t	*ep
)
{
	char		*cname = NULL;

	cname = meta_canonicalize(*spp, uname);
	/* if it is not a meta/hsp name then flag an error */
	if (cname == NULL) {
		(void) mdsyserror(ep, ENOENT, uname);
		return (NULL);
	}
	return (cname);
}

/*
 * set up a hotspare pool name structure using both the name
 * and the self id
 */
static mdhspname_t *
metahspname_hsp(
	mdsetname_t	**spp,
	char		*uname,
	hsp_t		hsp,
	md_error_t	*ep
)
{
	char		*cname;
	mdhspnamelist_t	**tail;
	mdhspname_t	*hspnp;

	/* check setname */
	assert(uname != NULL);
	if ((cname = gethspname(spp, uname, ep)) == NULL)
		return (NULL);
	assert(*spp != NULL);

	/* look in cache first */
	for (tail = &hsplistp; (*tail != NULL); tail = &(*tail)->next) {
		hspnp = (*tail)->hspnamep;
		if (strcmp(hspnp->hspname, cname) == 0) {
			Free(cname);
			/* if the hsp value has not been set then set it now */
			if (hspnp->hsp == MD_HSP_NONE)
				hspnp->hsp = hsp;
			return (hspnp);
		}
	}

	/* if the hsp number isn't specified then attempt to get it */
	if (hsp == MD_HSP_NONE && (hsp = meta_gethspnmentbyname((*spp)->setno,
	    MD_SIDEWILD, cname, ep)) == MD_HSP_NONE) {
		if (! mdisok(ep)) {
			/*
			 * If the error is ENOENT, then we will continue on,
			 * because the device does not yet exist.
			 * For other types of errors, however, we'll bail out.
			 */
			if (! mdissyserror(ep, ENOENT)) {
				Free(cname);
				return (NULL);
			}
			mdclrerror(ep);
		}
	}

	/* allocate new list element and hspname */
	*tail = Zalloc(sizeof (**tail));
	hspnp = (*tail)->hspnamep = Zalloc(sizeof (*hspnp));
	metainithspname(hspnp);

	/* save hspname and number */
	hspnp->hspname = cname;
	hspnp->hsp = hsp;

	/* success */
	return (hspnp);
}

/*
 * set up names for a hotspare pool
 */
mdhspname_t *
metahspname(
	mdsetname_t	**spp,
	char		*uname,
	md_error_t	*ep
)
{
	return (metahspname_hsp(spp, uname, MD_HSP_NONE, ep));
}

/*
 * set up hotspare pool name from key
 */
mdhspname_t *
metahsphspname(
	mdsetname_t	**spp,
	hsp_t		hsp,
	md_error_t	*ep
)
{
	set_t		setno = HSP_SET(hsp);
	mdsetname_t	*sp = NULL;
	char		*uname;
	mdhspname_t	*hspnp;

	/* check set first */
	if (spp == NULL)
		spp = &sp;
	if (chksetno(spp, setno, ep) != 0)
		return (NULL);
	assert(*spp != NULL);
	sp = *spp;

	/* get corresponding hotspare pool name */
	if ((uname = meta_gethspnmentbyid(sp->setno,
	    MD_SIDEWILD, hsp, ep)) == NULL)
		return (NULL);

	/* setup name */
	hspnp = metahspname_hsp(spp, uname, hsp, ep);
	Free(uname);
	return (hspnp);
}

/*
 * return hotspare pool name
 */
char *
get_hspname(mdsetname_t *sp, hsp_t hsp)
{
	mdhspname_t	*hspnp;
	md_error_t	status = mdnullerror;
	mdsetname_t	**spp = NULL;

	if (sp != NULL)
		spp = &sp;

	/* get name */
	if ((hspnp = metahsphspname(spp, hsp, &status)) == NULL) {
		mdclrerror(&status);
		return (NULL);
	}

	/* return name */
	return (hspnp->hspname);
}

/*
 * free hotspare pool list
 */
void
metafreehspnamelist(mdhspnamelist_t *hspnlp)
{
	mdhspnamelist_t	*next = NULL;

	for (/* void */; (hspnlp != NULL); hspnlp = next) {
		next = hspnlp->next;
		Free(hspnlp);
	}
}

/*
 * build list of hotspare pool names
 */
int
metahspnamelist(
	mdsetname_t	**spp,
	mdhspnamelist_t	**hspnlpp,
	int		argc,
	char		*argv[],
	md_error_t	*ep
)
{
	mdhspnamelist_t	**tailpp = hspnlpp;
	int		count = 0;

	for (*hspnlpp = NULL; (argc > 0); ++count, --argc, ++argv) {
		mdhspnamelist_t	*hspnlp = Zalloc(sizeof (*hspnlp));

		if ((hspnlp->hspnamep = metahspname(spp, argv[0],
		    ep)) == NULL) {
			metafreehspnamelist(*hspnlpp);
			*hspnlpp = NULL;
			return (-1);
		}
		*tailpp = hspnlp;
		tailpp = &hspnlp->next;
	}
	return (count);
}

/*
 * append to end of hotspare pool list
 */
mdhspname_t *
metahspnamelist_append(mdhspnamelist_t **hspnlpp, mdhspname_t *hspnp)
{
	mdhspnamelist_t	*hspnlp;

	/* run to end of list */
	for (; (*hspnlpp != NULL); hspnlpp = &(*hspnlpp)->next)
		;

	/* allocate new list element */
	hspnlp = *hspnlpp = Zalloc(sizeof (*hspnlp));

	/* append hotspare pool name */
	hspnlp->hspnamep = hspnp;
	return (hspnp);
}

/*
 * get name from dev
 */
mdname_t *
metadevname(
	mdsetname_t **spp,
	md_dev64_t dev,
	md_error_t *ep)
{
	char		*device_name;
	mdname_t	*namep;
	mdkey_t		key;

	/* short circuit metadevices */
	assert(dev != NODEV64);
	if (meta_dev_ismeta(dev))
		return (metamnumname(spp, meta_getminor(dev), 0, ep));

	/* create local set, if necessary */
	if (*spp == NULL) {
		if ((*spp = metasetname(MD_LOCAL_NAME, ep)) == NULL)
			return (NULL);
	}

	/* get name from namespace */
	if ((device_name = meta_getnmentbydev((*spp)->setno, MD_SIDEWILD,
	    dev, NULL, NULL, &key, ep)) == NULL) {
		return (NULL);
	}
	namep = metaname_fast(spp, device_name, LOGICAL_DEVICE, ep);
	if (namep != NULL)
		namep->key = key;

	Free(device_name);
	return (namep);
}

/*
 * return cached name from md_dev64_t
 */
static char *
metadevtocachename(md_dev64_t dev)
{
	mddrivenamelist_t	*dnlp;

	/* look in cache */
	for (dnlp = drivelistp; (dnlp != NULL); dnlp = dnlp->next) {
		mddrivename_t	*dnp = dnlp->drivenamep;
		uint_t		i;

		for (i = 0; (i < dnp->parts.parts_len); ++i) {
			mdname_t	*np = &dnp->parts.parts_val[i];

			if (np->dev == dev)
				return (np->cname);
		}
	}

	/* not found */
	return (NULL);
}

/*
 * Ask the driver for the name, which has been stored in the
 * metadevice state database (on behalf of the utilities).
 * (by devno)
 */
char *
get_devname(
	set_t setno,
	md_dev64_t dev)
{
	mdsetname_t	*sp;
	mdname_t	*np;
	md_error_t	status = mdnullerror;

	/* get name */
	if ((setno == MD_SET_BAD) ||
	    ((sp = metasetnosetname(setno, &status)) == NULL) ||
	    ((np = metadevname(&sp, dev, &status)) == NULL)) {
		mdclrerror(&status);
		return (metadevtocachename(dev));
	}

	/* return name */
	return (np->cname);
}

/*
 * get name from key
 */
mdname_t *
metakeyname(
	mdsetname_t	**spp,
	mdkey_t		key,
	int		fast,
	md_error_t	*ep
)
{
	char		*device_name;
	md_dev64_t	dev = NODEV64;
	mdname_t	*namep;

	/* create local set, if necessary */
	if (*spp == NULL) {
		if ((*spp = metasetname(MD_LOCAL_NAME, ep)) == NULL)
			return (NULL);
	}

	/* get name from namespace */
	if ((device_name = meta_getnmentbykey((*spp)->setno, MD_SIDEWILD,
	    key, NULL, NULL, &dev, ep)) == NULL) {
		return (NULL);
	}
	if (fast)
		namep = metaname_fast(spp, device_name, UNKNOWN, ep);
	else
		namep = metaname(spp, device_name, UNKNOWN, ep);

	assert(dev != NODEV64);
	if (namep)
		namep->dev = dev;
	Free(device_name);
	return (namep);
}

/*
 * completely flush metadev/hsp caches
 */
void
metaflushmetanames()
{
	metaflushhspnames();
	metaflushdrivenames();
	metaflushfastnames();
	metaflushstatcache();
}

/*
 * completely flush the caches
 */
void
metaflushnames(int flush_sr_cache)
{
	metaflushhspnames();
	metaflushdrivenames();
	metaflushsetnames();
	metaflushctlrcache();
	metaflushfastnames();
	metaflushstatcache();
	if (flush_sr_cache)
		sr_cache_flush(0);
}

/*
 * meta_get_hotspare_names
 *  returns an mdnamelist_t of hot spare names
 */

int
meta_get_hotspare_names(
	mdsetname_t	*sp,
	mdnamelist_t	**nlpp,
	int		options,
	md_error_t	*ep
)
{
	mdhspnamelist_t		*hspnlp	= NULL;
	mdhspnamelist_t		*hspp;
	int			cnt = 0;

	assert(nlpp != NULL);

	/* get hotspare names */
	if (meta_get_hsp_names(sp, &hspnlp, options, ep) < 0) {
		cnt = -1;
		goto out;
	}

	/* build name list */
	for (hspp = hspnlp; (hspp != NULL); hspp = hspp->next) {
		md_hsp_t	*hsp;
		int		i;

		if ((hsp = meta_get_hsp(sp, hspp->hspnamep, ep)) == NULL) {
			cnt = -1;
			goto out;
		}
		for (i = 0; (i < hsp->hotspares.hotspares_len); i++) {
			md_hs_t	*hs = &hsp->hotspares.hotspares_val[i];

			(void) metanamelist_append(nlpp, hs->hsnamep);
			++cnt;
		}
	}

	/* cleanup and return count or error */
out:
	metafreehspnamelist(hspnlp);
	if ((cnt == -1) && mdisok(ep)) {
		/*
		 * At least try to give some sort of meaningful error
		 */
		(void) mderror(ep, MDE_NO_HSPS, "Generic Hotspare Error");
	}

	return (cnt);
}
/*
 * meta_create_non_dup_list
 *    INPUT: mdnp mdname_t pointer to add to the list if a new name
 *           ldevidp list of non-duplicate names.
 *    OUTPUT: ldevidp list of non-duplicate names.
 * meta_create_non_dup_list will take a mdname_t pointer and if the device
 *    is not in the list (ldevidp) will add it to the list.
 *    User needs to free allocated memory.
 */
void
meta_create_non_dup_list(
	mdname_t	*mdnp,
	mddevid_t	**ldevidpp
)
{
	char		*lcname;
	mddevid_t	*tmp;
	mddevid_t	*lastdevidp;
	mddevid_t	*lldevidp;
	char		*ctd, *slice;
	mddevid_t	*ldevidp;

	if (mdnp == NULL)
		return;

	ldevidp = *ldevidpp;
	/*
	 * Grab the name of the device and strip off slice information
	 */
	lcname = Strdup(mdnp->cname);
	if (lcname == NULL) {
		return;
	}
	ctd = strrchr(lcname, '/');
	if (ctd != NULL)
		slice = strrchr(ctd, 's');
	else
		slice = strrchr(lcname, 's');

	if (slice != NULL)
		*slice = '\0';

	if (ldevidp == NULL) {
		/* first item in list */
		ldevidp = Zalloc(sizeof (mddevid_t));
		ldevidp->ctdname = lcname;
		ldevidp->key = mdnp->key;
		*ldevidpp = ldevidp;
	} else {
		for (tmp = ldevidp; (tmp != NULL); tmp = tmp->next) {
			if (strcmp(tmp->ctdname, lcname) == 0) {
				/* already there so just return */
				Free(lcname);
				return;
			}
			lastdevidp = tmp;
		}
		lldevidp = Zalloc(sizeof (mddevid_t));
		lldevidp->ctdname = lcname;
		lldevidp->key = mdnp->key;
		lastdevidp->next = lldevidp;
	}
}