view usr/src/lib/lvm/libmeta/common/meta_namespace.c @ 13606:3720d427d9fa

2110 liblvm should avoid extending pointers Reviewed by: Joshua M. Clulow <josh@sysmgr.org> Reviewed by: Jason King <jason.brian.king@gmail.com> Reviewed by: Garrett D'Amore <garrett@damore.org> Approved by: Garrett D'Amore <garrett@damore.org>
author Richard Lowe <richlowe@richlowe.net>
date Mon, 16 May 2011 05:32:51 +0100
parents 74cee1cd404b
children
line wrap: on
line source

/*
 * 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 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * namespace utilities
 */

#include <meta.h>

typedef struct deviceinfo {
	char	*bname;		/* block name of the device */
	char	*dname;		/* driver for the device */
	minor_t	mnum;		/* minor number for the device */
} deviceinfo_t;

static	deviceinfo_t	devlist[MD_MNMAXSIDES];

/*
 * Ask the driver for the device name, driver name, and minor number;
 * which has been stored in the metadevice state database
 * (on behalf of the utilities).
 * (by key)
 */
char *
meta_getnmentbykey(
	set_t		setno,
	side_t		sideno,
	mdkey_t		key,
	char		**drvnm,
	minor_t		*mnum,
	md_dev64_t	*dev,
	md_error_t	*ep
)
{
	struct mdnm_params	nm;
	static char		device_name[MAXPATHLEN];

	(void) memset(&nm, '\0', sizeof (nm));
	nm.setno = setno;
	nm.side = sideno;
	nm.key = key;
	nm.devname = (uintptr_t)device_name;

	if (metaioctl(MD_IOCGET_NM, &nm, &nm.mde, NULL) != 0) {
		(void) mdstealerror(ep, &nm.mde);
		return (NULL);
	}

	if (drvnm != NULL)
		*drvnm = Strdup(nm.drvnm);

	if (mnum != NULL)
		*mnum = nm.mnum;

	if (dev != NULL)
		*dev = meta_expldev(makedevice(nm.major, nm.mnum));

	return (Strdup(device_name));
}

/*
 * Ask the driver for the hsp name, driver name, and minor number;
 * which has been stored in the metadevice state database
 * (on behalf of the utilities).
 * (by key)
 */
char *
meta_gethspnmentbyid(
	set_t		setno,
	side_t		sideno,
	hsp_t		hspid,
	md_error_t	*ep
)
{
	struct mdhspnm_params	nm;
	char			*device_name;

	device_name = Malloc(MAXPATHLEN);
	device_name[0] = '\0';

	(void) memset(&nm, '\0', sizeof (nm));
	nm.setno = setno;
	nm.side = sideno;
	nm.hspid = hspid;
	nm.ret_hspid = MD_HSPID_WILD;
	nm.hspname_len = MAXPATHLEN;
	nm.hspname = (uintptr_t)device_name;

	if (metaioctl(MD_IOCGET_HSP_NM, &nm, &nm.mde, NULL) != 0) {
		(void) mdstealerror(ep, &nm.mde);
		Free(device_name);
		return (NULL);
	}

	return (device_name);
}

/*
 * Ask the driver for the hsp_self_id;
 * which has been stored in the metadevice state database
 * (on behalf of the utilities).
 * (by hsp name)
 */
hsp_t
meta_gethspnmentbyname(
	set_t		setno,
	side_t		sideno,
	char		*hspname,
	md_error_t	*ep
)
{
	struct mdhspnm_params	nm;
	char			*device_name;

	/* must have a hsp name */
	assert(hspname != NULL);

	device_name = Malloc(MAXPATHLEN);
	(void) strcpy(device_name, hspname);

	(void) memset(&nm, '\0', sizeof (nm));
	nm.setno = setno;
	nm.side = sideno;
	nm.hspid = MD_HSPID_WILD;
	nm.ret_hspid = MD_HSPID_WILD;
	nm.hspname_len = strlen(device_name) + 1;
	nm.hspname = (uintptr_t)device_name;

	/*
	 * The ioctl expects the a hsp name and return its hsp_self_id.
	 */
	if (metaioctl(MD_IOCGET_HSP_NM, &nm, &nm.mde, NULL) != 0) {
		(void) mdstealerror(ep, &nm.mde);
		Free(device_name);
		return (MD_HSP_NONE);
	}

	if (nm.ret_hspid == MD_HSPID_WILD) {
		Free(device_name);
		return (MD_HSP_NONE);
	}

	Free(device_name);
	return (nm.ret_hspid);
}


/*
 * Ask the driver for the minor name which has been stored in the
 * metadevice state database.
 * (by key)
 */
char *
meta_getdidminorbykey(
	set_t		setno,
	side_t		sideno,
	mdkey_t		key,
	md_error_t	*ep
)
{
	struct mdnm_params	nm;
	static char		minorname[MAXPATHLEN];

	(void) memset(&nm, '\0', sizeof (nm));
	nm.setno = setno;
	nm.side = sideno;
	nm.key = key;
	nm.minorname = (uintptr_t)minorname;

	if (metaioctl(MD_IOCGET_DIDMIN, &nm, &nm.mde, NULL) != 0) {
		(void) mdstealerror(ep, &nm.mde);
		return (NULL);
	}

	return (Strdup(minorname));
}

/*
 * Ask the driver for the device id string which has been stored in the
 * metadevice state database (on behalf of the utilities).
 * (by key)
 */
ddi_devid_t
meta_getdidbykey(
	set_t		setno,
	side_t		sideno,
	mdkey_t		key,
	md_error_t	*ep
)
{
	struct mdnm_params	nm;

	(void) memset(&nm, '\0', sizeof (nm));
	nm.setno = setno;
	nm.side = sideno;
	nm.key = key;

	/*
	 * First ask the driver for the size of the device id string.  This is
	 * signaled by passing the driver a devid_size of zero.
	 */
	nm.devid_size = 0;
	if (metaioctl(MD_IOCGET_DID, &nm, &nm.mde, NULL) != 0) {
		(void) mdstealerror(ep, &nm.mde);
		return (NULL);
	}

	/*
	 * If the devid_size is still zero then something is wrong.
	 */
	if (nm.devid_size == 0) {
		(void) mdstealerror(ep, &nm.mde);
		return (NULL);
	}

	/*
	 * Now go get the actual device id string.  Caller is responsible for
	 * free'ing device id memory buffer.
	 */
	if ((nm.devid = (uintptr_t)malloc(nm.devid_size)) == NULL) {
		return (NULL);
	}
	if (metaioctl(MD_IOCGET_DID, &nm, &nm.mde, NULL) != 0) {
		(void) mdstealerror(ep, &nm.mde);
		(void) free((void *)(uintptr_t)nm.devid);
		return (NULL);
	}

	return ((void *)(uintptr_t)nm.devid);
}

/*
 * set the devid.
 */
int
meta_setdid(
	set_t		setno,
	side_t		sideno,
	mdkey_t		key,
	md_error_t	*ep
)
{
	struct mdnm_params	nm;
	int			i;

	(void) memset(&nm, '\0', sizeof (nm));
	nm.setno = setno;
	nm.side = sideno;
	nm.key = key;

	if (metaioctl(MD_IOCSET_DID, &nm, &nm.mde, NULL) != 0) {
		(void) mdstealerror(ep, &nm.mde);
		return (-1);
	}

	if (setno == MD_LOCAL_SET) {
		/*
		 * If this is the local set then we are adding in the devids
		 * for the disks in the diskset and so this means adding
		 * a reference count for each side. Need to do this after
		 * the initial add so that the correct devid is picked up.
		 * The key is the key of the drive record and as such this
		 * means the minor number of the device which is used to
		 * get the devid. If the wrong side is used then it would
		 * be possible to get the wrong devid in the namespace, hence
		 * the requirement to process the local side first of all.
		 */
		for (i = 0 + SKEW; i < MD_MAXSIDES; i++) {
			/*
			 * We can just call the ioctl again because it will
			 * fail with ENOENT if the side does not exist, and
			 * more importantly does not increment the usage count
			 * on the devid.
			 */
			nm.side = (side_t)i;
			if (nm.side == sideno)
				continue;
			if (metaioctl(MD_IOCSET_DID, &nm, &nm.mde, NULL) != 0) {
				if (mdissyserror(&nm.mde, ENODEV)) {
					mdclrerror(&nm.mde);
				} else {
					(void) mdstealerror(ep, &nm.mde);
					return (-1);
				}
			}
		}
	}
	return (0);
}
/*
 * Ask the driver for the name, which has been stored in the
 * metadevice state database (on behalf of the utilities).
 * (by key)
 */
char *
meta_getnmbykey(
	set_t		setno,
	side_t		sideno,
	mdkey_t		key,
	md_error_t	*ep
)
{
	return (meta_getnmentbykey(setno, sideno, key, NULL, NULL, NULL, ep));
}

/*
 * Ask the driver for the device name, driver name, minor number, and key;
 * which has been stored in the metadevice state database
 * (on behalf of the utilities).
 * (by md_dev64_t)
 */
char *
meta_getnmentbydev(
	set_t		setno,
	side_t		sideno,
	md_dev64_t	dev,
	char		**drvnm,
	minor_t		*mnum,
	mdkey_t		*key,
	md_error_t	*ep
)
{
	struct mdnm_params	nm;
	static char		device_name[MAXPATHLEN];

	/* must have a dev */
	assert(dev != NODEV64);

	(void) memset(&nm, '\0', sizeof (nm));
	nm.setno = setno;
	nm.side = sideno;
	nm.key = MD_KEYWILD;
	nm.major = meta_getmajor(dev);
	nm.mnum = meta_getminor(dev);
	nm.devname = (uintptr_t)device_name;

	if (metaioctl(MD_IOCGET_NM, &nm, &nm.mde, NULL) != 0) {
		(void) mdstealerror(ep, &nm.mde);
		return (NULL);
	}

	/*
	 * With the friendly name work, each metadevice will have
	 * an NM entry. However, to allow backward compatibility,
	 * systems upgraded to a friendly name release won't have
	 * NM entries for the pre-existing top level metadevices. This
	 * implementation allows users to downgrade to a pre-friendly
	 * name release since the configuration information (mddb) is
	 * not modified.
	 *
	 * meta_getnmentbydev is called to get nm entry for all metadevices
	 * and expects the minor and major number and returns a key and
	 * name. For upgraded systems with pre-existing metadevices,
	 * the only returning value will be the name since there's no nm
	 * entry for pre-friendly name top level metadevices. So a return
	 * key for the device will not be available and will be NULL.
	 * Thus, the caller is responsible for making sure the returned key
	 * is valid, not NULL.
	 */
	if (drvnm != NULL)
		*drvnm = Strdup(nm.drvnm);
	if (mnum != NULL)
		*mnum = nm.mnum;

	if (key != NULL)
		*key = nm.retkey;

	return (Strdup(device_name));
}

/*
 * The arguments, minorname and devid, are only used with the partial
 * import code and should be NULL otherwise.
 */
int
add_name(
	mdsetname_t	*sp,
	side_t		sideno,
	mdkey_t		key,
	char		*dname,
	minor_t		mnum,
	char		*bname,
	char		*minorname,	/* only used with a partial import */
	ddi_devid_t	devid,		/* only used with a partial import */
	md_error_t	*ep
)
{
	struct mdnm_params	nm;

	(void) memset(&nm, '\0', sizeof (nm));
	nm.setno = sp->setno;
	nm.side = sideno;
	nm.key = key;
	nm.mnum = mnum;
	(void) strncpy(nm.drvnm, dname, sizeof (nm.drvnm));
	nm.devname_len = strlen(bname) + 1;
	nm.devname = (uintptr_t)bname;
	if (devid && minorname) {
		nm.minorname_len = strlen(minorname) + 1;
		nm.minorname = (uintptr_t)minorname;
		nm.devid_size = devid_sizeof(devid);
		nm.devid = (uintptr_t)devid;
		nm.imp_flag = MDDB_C_IMPORT;
	}
	if (metaioctl(MD_IOCSET_NM, &nm, &nm.mde, bname) < 0)
		return (mdstealerror(ep, &nm.mde));

	return (nm.key);
}

/*
 * Remove the device name which corresponds to the given device number.
 */
int
del_name(
	mdsetname_t	*sp,
	side_t		sideno,
	mdkey_t		key,
	md_error_t	*ep
)
{
	struct mdnm_params	nm;

	(void) memset(&nm, '\0', sizeof (nm));
	nm.setno = sp->setno;
	nm.side = sideno;
	nm.key = key;

	if (metaioctl(MD_IOCREM_NM, &nm, &nm.mde, NULL) != 0)
		return (mdstealerror(ep, &nm.mde));

	return (0);
}

static void
empty_devicelist()
{
	side_t	sideno;

	for (sideno = 0; sideno < MD_MNMAXSIDES; sideno++) {
		if (devlist[sideno].bname != (char *)NULL) {
			Free(devlist[sideno].bname);
			Free(devlist[sideno].dname);
			devlist[sideno].mnum = NODEV;
		}
	}
}

static void
add_to_devicelist(
	side_t		sideno,
	char		*bname,
	char		*dname,
	minor_t		mnum
)
{
	devlist[sideno].bname = Strdup(bname);
	devlist[sideno].dname = Strdup(dname);

	devlist[sideno].mnum = mnum;
}

/*
 * Build a list of the names on the systems, if this fails the caller
 * will tidy up the entries in the devlist.
 */
static int
build_sidenamelist(
	mdsetname_t	*sp,
	mdname_t	*np,
	md_error_t	*ep
)
{
	side_t		sideno = MD_SIDEWILD;
	minor_t		mnum = NODEV;
	char		*bname = NULL;
	char		*dname = NULL;
	int		err;

	/*CONSTCOND*/
	while (1) {

		if ((err = meta_getnextside_devinfo(sp, np->bname, &sideno,
		    &bname, &dname, &mnum, ep)) == -1)
			return (-1);

		if (err == 0)
			break;

		/* the sideno gives us the index into the array */
		add_to_devicelist(sideno, bname, dname, mnum);
	}
	return (0);
}

/*
 * add name key
 * the meta_create* functions should be the only ones using this. The
 * adding of a name to the namespace must be done in a particular order
 * to devid support for the disksets. The order is: add the 'local' side
 * first of all, so the devid lookup in the kernel will use the correct
 * device information and then add in the other sides.
 */
int
add_key_name(
	mdsetname_t	*sp,
	mdname_t	*np,
	mdnamelist_t	**nlpp,
	md_error_t	*ep
)
{
	int		err;
	side_t		sideno = MD_SIDEWILD;
	side_t		thisside;
	mdkey_t		key = MD_KEYWILD;
	md_set_desc	*sd;
	int		maxsides;

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

	if (! metaislocalset(sp)) {
		if ((sd = metaget_setdesc(sp, ep)) == NULL) {
			return (-1);
		}
	}

	if (build_sidenamelist(sp, np, ep) == -1) {
		empty_devicelist();
		return (-1);
	}

	/*
	 * When a disk is added into the namespace the local information for
	 * that disk is added in first of all. For the local set this is not
	 * a concern and for the host that owns the diskset it is not a concern
	 * but when a disk is added in the remote namespace we *must* use the
	 * local information for that disk first of all. This is because when
	 * in the kernel (md_setdevname) the passed in dev_t is used to find
	 * the devid of the disk. This means we have to cater for the following:
	 *
	 * - a disk on the remote host having the dev_t that has been passed
	 *   into the kernel and this disk is not actually the disk that is
	 *   being added into the diskset.
	 * - the dev_t does not exist on this node
	 *
	 * So putting in the local information first of all makes sure that the
	 * dev_t passed into the kernel is correct with respect to that node
	 * and then any further additions for that name match on the key
	 * passed back.
	 */
	thisside = getmyside(sp, ep);

	if (devlist[thisside].dname == NULL ||
	    strlen(devlist[thisside].dname) == 0) {
		/*
		 * Did not find the disk information for the disk. This can
		 * be because of an inconsistancy in the namespace: that is the
		 * devid we have in the namespace does not exist on the
		 * system and thus when looking up the disk information
		 * using this devid we fail to find anything.
		 */
		(void) mdcomperror(ep, MDE_SP_COMP_OPEN_ERR, 0, np->dev,
		    np->cname);
		empty_devicelist();
		return (-1);
	}

	if ((err = add_name(sp, thisside, key, devlist[thisside].dname,
	    devlist[thisside].mnum, devlist[thisside].bname, NULL,
	    NULL, ep)) == -1) {
		empty_devicelist();
		return (-1);
	}

	/* We now have a 'key' so add in the other sides */
	key = (mdkey_t)err;

	if (metaislocalset(sp))
		goto done;

	if (MD_MNSET_DESC(sd))
		maxsides = MD_MNMAXSIDES;
	else
		maxsides = MD_MAXSIDES;

	for (sideno = 0; sideno < maxsides; sideno++) {
		/* ignore thisside, as it has been added above */
		if (sideno == thisside)
			continue;

		if (devlist[sideno].dname != NULL) {
			err = add_name(sp, sideno, key, devlist[sideno].dname,
			    devlist[sideno].mnum, devlist[sideno].bname,
			    NULL, NULL, ep);
			if (err == -1) {
				empty_devicelist();
				return (-1);
			}
		}
	}

done:
	empty_devicelist();
	/* save key, return success */
	np->key = key;
	if (nlpp != NULL)
		(void) metanamelist_append(nlpp, np);
	return (0);
}

/*
 * delete name key
 * the meta_create* functions should be the only ones using this. The
 * removal of the names must be done in a particular order: remove the
 * non-local entries first of all and then finally the local entry.
 */
int
del_key_name(
	mdsetname_t	*sp,
	mdname_t	*np,
	md_error_t	*ep
)
{
	side_t		sideno = MD_SIDEWILD;
	int		err;
	int		retval = 0;
	side_t		thisside;

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

	/* should have a key */
	assert((np->key != MD_KEYWILD) && (np->key != MD_KEYBAD));

	thisside = getmyside(sp, ep);

	/* remove the remote sides first of all */
	for (;;) {
		if ((err = meta_getnextside_devinfo(sp, np->bname, &sideno,
		    NULL, NULL, NULL, ep)) == -1)
			return (-1);

		if (err == 0)
			break;

		/* ignore thisside */
		if (thisside == sideno) {
			continue;
		}
		if ((err = del_name(sp, sideno, np->key, ep)) == -1)
			retval = -1;
	}

	/* now remove this side */
	if (retval == 0)
		if ((err = del_name(sp, thisside, np->key, ep)) == -1)
			retval = -1;

	np->key = MD_KEYBAD;
	return (retval);
}

/*
 * delete namelist keys
 * the meta_create* functions should be the only ones using this
 */
int
del_key_names(
	mdsetname_t	*sp,
	mdnamelist_t	*nlp,
	md_error_t	*ep
)
{
	mdnamelist_t	*p;
	md_error_t	status = mdnullerror;
	int		rval = 0;

	/* if ignoring errors */
	if (ep == NULL)
		ep = &status;

	/* delete names */
	for (p = nlp; (p != NULL); p = p->next) {
		mdname_t	*np = p->namep;

		if (del_key_name(sp, np, ep) != 0)
			rval = -1;
	}

	/* cleanup, return success */
	if (ep == &status)
		mdclrerror(&status);
	return (rval);
}


/*
 * This routine when is called will store the metadevice name
 * when it is first created
 */
mdkey_t
add_self_name(
	mdsetname_t	*sp,
	char 		*uname,
	md_mkdev_params_t	*params,
	md_error_t	*ep
)
{
	char		*p, *devname;
	side_t		myside, side;
	mdkey_t		key;
	md_set_desc	*sd;
	int		len;
	char		*drvname = params->md_driver.md_drivername;
	minor_t		minor = MD_MKMIN(sp->setno, params->un);
	md_mnnode_desc	*mnside;

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

	/*
	 * The valid qualified name
	 */
	if (metaislocalset(sp)) {
		len = strlen(p) + strlen("/dev/md/dsk/") + 1;
		devname = Malloc(len);
		(void) strcpy(devname, "/dev/md/dsk/");
		(void) strcat(devname, p);
	} else {
		len = strlen(sp->setname) + strlen(p) +
		    strlen("/dev/md//dsk/") + 1;
		devname = Malloc(len);
		(void) snprintf(devname, len, "/dev/md/%s/dsk/%s",
		    sp->setname, p);
	}

	/*
	 * Add self to the namespace
	 */
	if ((myside = getmyside(sp, ep)) == MD_SIDEWILD) {
		Free(devname);
		return (-1);
	}

	if (metaislocalset(sp)) {
		if ((key = add_name(sp, myside, MD_KEYWILD, drvname,
		    minor, devname, NULL, NULL, ep)) == MD_KEYBAD) {
			Free(devname);
			return (-1);
		}
	} else {
		/*
		 * Add myside first and use the returned key to add other sides
		 */
		if ((key = add_name(sp, myside, MD_KEYWILD, drvname,
		    minor, devname, NULL, NULL, ep)) == MD_KEYBAD) {
			Free(devname);
			return (-1);
		}

		/*
		 * Add for all other sides
		 */
		if ((sd = metaget_setdesc(sp, ep)) == NULL) {
			Free(devname);
			return (-1);
		}

		if (MD_MNSET_DESC(sd)) {
			for (mnside = sd->sd_nodelist; mnside != NULL;
			    mnside = mnside->nd_next) {
				if (mnside->nd_nodeid == myside)
					continue;
				if (add_name(sp, mnside->nd_nodeid, key,
				    drvname, minor, devname, NULL, NULL,
				    ep) == -1) {
					Free(devname);
					return (-1);
				}
			}
		} else {
			for (side = 0; side < MD_MAXSIDES; side++) {
				if (sd->sd_nodes[side][0] == '\0')
					continue;
				if (side == myside)
					continue;
				if (add_name(sp, side, key, drvname, minor,
				    devname, NULL, NULL, ep) == -1) {
					Free(devname);
					return (-1);
				}
			}
		}
	}

	Free(devname);
	return (key);
}


/*
 * This routine when is called will remove the metadevice name
 * from the namespace and it is the last thing to do in the
 * metaclear operation
 */
int
del_self_name(
	mdsetname_t	*sp,
	mdkey_t		key,
	md_error_t	*ep
)
{
	side_t		myside;
	int		rval = 0;
	side_t		side;
	md_set_desc	*sd;
	md_mnnode_desc	*mnside;

	assert(key != MD_KEYBAD);

	if ((myside = getmyside(sp, ep)) == MD_SIDEWILD)
		return (-1);

	if (metaislocalset(sp)) {
		rval = del_name(sp, myside, key, ep);
	} else {
		/*
		 * Remove all other sides first
		 */
		if ((sd = metaget_setdesc(sp, ep)) == NULL) {
			return (-1);
		}

		if (MD_MNSET_DESC(sd)) {
			for (mnside = sd->sd_nodelist; mnside != NULL;
			    mnside = mnside->nd_next) {
				if (mnside->nd_nodeid == myside)
					continue;
				if ((rval = del_name(sp, mnside->nd_nodeid, key,
				    ep)) == -1) {
					goto out;
				}
			}
		} else {
			for (side = 0; side < MD_MAXSIDES; side++) {
				if (sd->sd_nodes[side][0] == '\0')
					continue;
				if (side == myside)
					continue;
				if ((rval = del_name(sp, side, key,
				    ep)) == -1) {
					goto out;
				}
			}
		}

		/*
		 * del myside
		 */
		rval = del_name(sp, myside, key, ep);
	}

out:
	return (rval);
}