xref: /onnv-gate/usr/src/uts/common/fs/dev/sdev_profile.c (revision 5331:3047ad28a67b)
12621Sllai1 /*
22621Sllai1  * CDDL HEADER START
32621Sllai1  *
42621Sllai1  * The contents of this file are subject to the terms of the
52621Sllai1  * Common Development and Distribution License (the "License").
62621Sllai1  * You may not use this file except in compliance with the License.
72621Sllai1  *
82621Sllai1  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92621Sllai1  * or http://www.opensolaris.org/os/licensing.
102621Sllai1  * See the License for the specific language governing permissions
112621Sllai1  * and limitations under the License.
122621Sllai1  *
132621Sllai1  * When distributing Covered Code, include this CDDL HEADER in each
142621Sllai1  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152621Sllai1  * If applicable, add the following below this CDDL HEADER, with the
162621Sllai1  * fields enclosed by brackets "[]" replaced with your own identifying
172621Sllai1  * information: Portions Copyright [yyyy] [name of copyright owner]
182621Sllai1  *
192621Sllai1  * CDDL HEADER END
202621Sllai1  */
212621Sllai1 
222621Sllai1 /*
23*5331Samw  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
242621Sllai1  * Use is subject to license terms.
252621Sllai1  */
262621Sllai1 
272621Sllai1 #pragma ident	"%Z%%M%	%I%	%E% SMI"
282621Sllai1 
292621Sllai1 /*
302621Sllai1  * This file implements /dev filesystem operations for non-global
312621Sllai1  * instances. Three major entry points:
322621Sllai1  * devname_profile_update()
332621Sllai1  *   Update matching rules determining which names to export
342621Sllai1  * prof_readdir()
352621Sllai1  *   Return the list of exported names
362621Sllai1  * prof_lookup()
372621Sllai1  *   Implements lookup
382621Sllai1  */
392621Sllai1 
402621Sllai1 #include <sys/types.h>
412621Sllai1 #include <sys/param.h>
422621Sllai1 #include <sys/sysmacros.h>
432621Sllai1 #include <sys/vnode.h>
442621Sllai1 #include <sys/uio.h>
452621Sllai1 #include <sys/dirent.h>
462621Sllai1 #include <sys/pathname.h>
472621Sllai1 #include <sys/fs/dv_node.h>
482621Sllai1 #include <sys/fs/sdev_impl.h>
492621Sllai1 #include <sys/sunndi.h>
502621Sllai1 #include <sys/modctl.h>
512621Sllai1 
522621Sllai1 enum {
532621Sllai1 	PROFILE_TYPE_INCLUDE,
542621Sllai1 	PROFILE_TYPE_EXCLUDE,
552621Sllai1 	PROFILE_TYPE_MAP,
562621Sllai1 	PROFILE_TYPE_SYMLINK
572621Sllai1 };
582621Sllai1 
592621Sllai1 enum {
602621Sllai1 	WALK_DIR_CONTINUE = 0,
612621Sllai1 	WALK_DIR_TERMINATE
622621Sllai1 };
632621Sllai1 
642621Sllai1 static const char *sdev_nvp_val_err = "nvpair_value error %d, %s\n";
652621Sllai1 
662621Sllai1 static void process_rule(struct sdev_node *, struct sdev_node *,
672621Sllai1     char *, char *, int);
682621Sllai1 static void walk_dir(struct vnode *, void *, int (*)(char *, void *));
692621Sllai1 
702621Sllai1 static void
712621Sllai1 prof_getattr(struct sdev_node *dir, char *name, struct vnode *gdv,
722621Sllai1     struct vattr *vap, struct vnode **avpp, int *no_fs_perm)
732621Sllai1 {
742621Sllai1 	struct vnode *advp;
752621Sllai1 
762621Sllai1 	/* get attribute from shadow, if present; else get default */
772621Sllai1 	advp = dir->sdev_attrvp;
78*5331Samw 	if (advp && VOP_LOOKUP(advp, name, avpp, NULL, 0, NULL, kcred,
79*5331Samw 	    NULL, NULL, NULL) == 0) {
80*5331Samw 		(void) VOP_GETATTR(*avpp, vap, 0, kcred, NULL);
812621Sllai1 	} else if (gdv == NULL || gdv->v_type == VDIR) {
822621Sllai1 		/* always create shadow directory */
832621Sllai1 		*vap = sdev_vattr_dir;
84*5331Samw 		if (advp && VOP_MKDIR(advp, name, &sdev_vattr_dir,
85*5331Samw 		    avpp, kcred, NULL, 0, NULL) != 0) {
862621Sllai1 			*avpp = NULLVP;
872621Sllai1 			sdcmn_err10(("prof_getattr: failed to create "
882621Sllai1 			    "shadow directory %s/%s\n", dir->sdev_path, name));
892621Sllai1 		}
902621Sllai1 	} else {
912621Sllai1 		/*
922621Sllai1 		 * get default permission from devfs
932621Sllai1 		 * Before calling devfs_get_defattr, we need to get
942621Sllai1 		 * the realvp (the dv_node). If realvp is not a dv_node,
952621Sllai1 		 * devfs_get_defattr() will return a system-wide default
962621Sllai1 		 * attr for device nodes.
972621Sllai1 		 */
982621Sllai1 		struct vnode *rvp;
99*5331Samw 		if (VOP_REALVP(gdv, &rvp, NULL) != 0)
1002621Sllai1 			rvp = gdv;
1012621Sllai1 		devfs_get_defattr(rvp, vap, no_fs_perm);
1022621Sllai1 		*avpp = NULLVP;
1032621Sllai1 	}
1042621Sllai1 
1052621Sllai1 	/* ignore dev_t and vtype from backing store */
1062621Sllai1 	if (gdv) {
1072621Sllai1 		vap->va_type = gdv->v_type;
1082621Sllai1 		vap->va_rdev = gdv->v_rdev;
1092621Sllai1 	}
1102621Sllai1 }
1112621Sllai1 
1122621Sllai1 static void
1132621Sllai1 apply_glob_pattern(struct sdev_node *pdir, struct sdev_node *cdir)
1142621Sllai1 {
1152621Sllai1 	char *name;
1162621Sllai1 	nvpair_t *nvp = NULL;
1172621Sllai1 	nvlist_t *nvl;
1182621Sllai1 	struct vnode *vp = SDEVTOV(cdir);
1192621Sllai1 	int rv = 0;
1202621Sllai1 
1212621Sllai1 	if (vp->v_type != VDIR)
1222621Sllai1 		return;
1232621Sllai1 	name = cdir->sdev_name;
1242621Sllai1 	nvl = pdir->sdev_prof.dev_glob_incdir;
1252621Sllai1 	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
1262621Sllai1 		char *pathleft;
1272621Sllai1 		char *expr = nvpair_name(nvp);
1282621Sllai1 		if (!gmatch(name, expr))
1292621Sllai1 			continue;
1302621Sllai1 		rv = nvpair_value_string(nvp, &pathleft);
1312621Sllai1 		if (rv != 0) {
1322621Sllai1 			cmn_err(CE_WARN, sdev_nvp_val_err,
1332621Sllai1 			    rv, nvpair_name(nvp));
1342621Sllai1 			break;
1352621Sllai1 		}
1362621Sllai1 		process_rule(cdir, cdir->sdev_origin,
1372621Sllai1 		    pathleft, NULL, PROFILE_TYPE_INCLUDE);
1382621Sllai1 	}
1392621Sllai1 }
1402621Sllai1 
1412621Sllai1 /*
1422621Sllai1  * Some commonality here with sdev_mknode(), could be simplified.
1432621Sllai1  * NOTE: prof_mknode returns with *newdv held once, if success.
1442621Sllai1  */
1452621Sllai1 static int
1462621Sllai1 prof_mknode(struct sdev_node *dir, char *name, struct sdev_node **newdv,
1472621Sllai1     vattr_t *vap, vnode_t *avp, void *arg, cred_t *cred)
1482621Sllai1 {
1492621Sllai1 	struct sdev_node *dv;
1502621Sllai1 	int rv;
1512621Sllai1 
1522621Sllai1 	ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
1532621Sllai1 
1542621Sllai1 	/* check cache first */
1552621Sllai1 	if (dv = sdev_cache_lookup(dir, name)) {
1562621Sllai1 		*newdv = dv;
1572621Sllai1 		return (0);
1582621Sllai1 	}
1592621Sllai1 
1602621Sllai1 	/* allocate node and insert into cache */
1612621Sllai1 	rv = sdev_nodeinit(dir, name, &dv, NULL);
1622621Sllai1 	if (rv != 0) {
1632621Sllai1 		*newdv = NULL;
1642621Sllai1 		return (rv);
1652621Sllai1 	}
1662621Sllai1 
1672621Sllai1 	rv = sdev_cache_update(dir, &dv, name, SDEV_CACHE_ADD);
1682621Sllai1 	*newdv = dv;
1692621Sllai1 
1702621Sllai1 	/* put it in ready state */
1712621Sllai1 	rv = sdev_nodeready(*newdv, vap, avp, arg, cred);
1722621Sllai1 
1732621Sllai1 	/* handle glob pattern in the middle of a path */
1742621Sllai1 	if (rv == 0) {
1752621Sllai1 		if (SDEVTOV(*newdv)->v_type == VDIR)
1762621Sllai1 			sdcmn_err10(("sdev_origin for %s set to 0x%p\n",
1772621Sllai1 			    name, arg));
1782621Sllai1 		apply_glob_pattern(dir, *newdv);
1792621Sllai1 	}
1802621Sllai1 	return (rv);
1812621Sllai1 }
1822621Sllai1 
1832621Sllai1 /*
1842621Sllai1  * Create a directory node in a non-global dev instance.
1852621Sllai1  * Always create shadow vnode. Set sdev_origin to the corresponding
1862621Sllai1  * global directory sdev_node if it exists. This facilitates the
1872621Sllai1  * lookup operation.
1882621Sllai1  */
1892621Sllai1 static int
1902621Sllai1 prof_make_dir(char *name, struct sdev_node **gdirp, struct sdev_node **dirp)
1912621Sllai1 {
1922621Sllai1 	struct sdev_node *dir = *dirp;
1932621Sllai1 	struct sdev_node *gdir = *gdirp;
1942621Sllai1 	struct sdev_node *newdv;
1952621Sllai1 	struct vnode *avp, *gnewdir = NULL;
1962621Sllai1 	struct vattr vattr;
1972621Sllai1 	int error;
1982621Sllai1 
1992621Sllai1 	/* see if name already exists */
2002621Sllai1 	rw_enter(&dir->sdev_contents, RW_READER);
2012621Sllai1 	if (newdv = sdev_cache_lookup(dir, name)) {
2022621Sllai1 		*dirp = newdv;
2032621Sllai1 		*gdirp = newdv->sdev_origin;
2042621Sllai1 		SDEV_RELE(dir);
2052621Sllai1 		rw_exit(&dir->sdev_contents);
2062621Sllai1 		return (0);
2072621Sllai1 	}
2082621Sllai1 	rw_exit(&dir->sdev_contents);
2092621Sllai1 
2102621Sllai1 	/* find corresponding dir node in global dev */
2112621Sllai1 	if (gdir) {
2122621Sllai1 		error = VOP_LOOKUP(SDEVTOV(gdir), name, &gnewdir,
213*5331Samw 		    NULL, 0, NULL, kcred, NULL, NULL, NULL);
2142621Sllai1 		if (error == 0) {
2152621Sllai1 			*gdirp = VTOSDEV(gnewdir);
2162621Sllai1 		} else { 	/* it's ok if there no global dir */
2172621Sllai1 			*gdirp = NULL;
2182621Sllai1 		}
2192621Sllai1 	}
2202621Sllai1 
2212621Sllai1 	/* get attribute from shadow, also create shadow dir */
2222621Sllai1 	prof_getattr(dir, name, gnewdir, &vattr, &avp, NULL);
2232621Sllai1 
2242621Sllai1 	/* create dev directory vnode */
2252621Sllai1 	rw_enter(&dir->sdev_contents, RW_WRITER);
2262621Sllai1 	error = prof_mknode(dir, name, &newdv, &vattr, avp, (void *)*gdirp,
2272621Sllai1 	    kcred);
2282621Sllai1 	rw_exit(&dir->sdev_contents);
2292621Sllai1 	if (error == 0) {
2302621Sllai1 		ASSERT(newdv);
2312621Sllai1 		*dirp = newdv;
2322621Sllai1 	}
2332621Sllai1 	SDEV_RELE(dir);
2342621Sllai1 	return (error);
2352621Sllai1 }
2362621Sllai1 
2372621Sllai1 /*
2382621Sllai1  * Look up a logical name in the global zone.
2392621Sllai1  * Provides the ability to map the global zone's device name
2402621Sllai1  * to an alternate name within a zone.  The primary example
2412621Sllai1  * is the virtual console device /dev/zcons/[zonename]/zconsole
2422621Sllai1  * mapped to /[zonename]/root/dev/zconsole.
2432621Sllai1  */
2442621Sllai1 static void
2452621Sllai1 prof_lookup_globaldev(struct sdev_node *dir, struct sdev_node *gdir,
2462621Sllai1     char *name, char *rename)
2472621Sllai1 {
2482621Sllai1 	/* global OS rootdir */
2492621Sllai1 	extern vnode_t *rootdir;
2502621Sllai1 
2512621Sllai1 	int error;
2522621Sllai1 	struct vnode *avp, *gdv, *gddv;
2532621Sllai1 	struct sdev_node *newdv;
2542621Sllai1 	struct vattr vattr = {0};
2552621Sllai1 	struct pathname pn;
2562621Sllai1 
2572621Sllai1 	/* check if node already exists */
2582621Sllai1 	newdv = sdev_cache_lookup(dir, rename);
2592621Sllai1 	if (newdv) {
2602621Sllai1 		ASSERT(newdv->sdev_state != SDEV_ZOMBIE);
2612621Sllai1 		SDEV_SIMPLE_RELE(newdv);
2622621Sllai1 		return;
2632621Sllai1 	}
2642621Sllai1 
2652621Sllai1 	/* sanity check arguments */
2662621Sllai1 	if (!gdir || pn_get(name, UIO_SYSSPACE, &pn))
2672621Sllai1 		return;
2682621Sllai1 
2692621Sllai1 	/* perform a relative lookup of the global /dev instance */
2702621Sllai1 	gddv = SDEVTOV(gdir);
2712621Sllai1 	VN_HOLD(gddv);
2722621Sllai1 	VN_HOLD(rootdir);
2732621Sllai1 	error = lookuppnvp(&pn, NULL, FOLLOW, NULLVPP, &gdv,
2742621Sllai1 	    rootdir, gddv, kcred);
2752621Sllai1 	pn_free(&pn);
2762621Sllai1 	if (error) {
2772621Sllai1 		sdcmn_err10(("prof_lookup_globaldev: %s not found\n", name));
2782621Sllai1 		return;
2792621Sllai1 	}
2802621Sllai1 	ASSERT(gdv && gdv->v_type != VLNK);
2812621Sllai1 
2822621Sllai1 	/*
2832621Sllai1 	 * Found the entry in global /dev, figure out attributes
2842621Sllai1 	 * by looking at backing store. Call into devfs for default.
2853024Sllai1 	 * Note, mapped device is persisted under the new name
2862621Sllai1 	 */
2873024Sllai1 	prof_getattr(dir, rename, gdv, &vattr, &avp, NULL);
2882621Sllai1 
2892621Sllai1 	if (gdv->v_type != VDIR) {
2902621Sllai1 		VN_RELE(gdv);
2912621Sllai1 		gdir = NULL;
2922621Sllai1 	} else
2932621Sllai1 		gdir = VTOSDEV(gdv);
2942621Sllai1 
2952621Sllai1 	if (prof_mknode(dir, rename, &newdv, &vattr, avp,
2962621Sllai1 	    (void *)gdir, kcred) == 0) {
2972621Sllai1 		ASSERT(newdv->sdev_state != SDEV_ZOMBIE);
2982621Sllai1 		SDEV_SIMPLE_RELE(newdv);
2992621Sllai1 	}
3002621Sllai1 }
3012621Sllai1 
3022621Sllai1 static void
3032621Sllai1 prof_make_sym(struct sdev_node *dir, char *lnm, char *tgt)
3042621Sllai1 {
3052621Sllai1 	struct sdev_node *newdv;
3062621Sllai1 
3072621Sllai1 	if (prof_mknode(dir, lnm, &newdv, &sdev_vattr_lnk, NULL,
3082621Sllai1 	    (void *)tgt, kcred) == 0) {
3092621Sllai1 		ASSERT(newdv->sdev_state != SDEV_ZOMBIE);
3102621Sllai1 		SDEV_SIMPLE_RELE(newdv);
3112621Sllai1 	}
3122621Sllai1 }
3132621Sllai1 
3142621Sllai1 /*
3152621Sllai1  * Create symlinks in the current directory based on profile
3162621Sllai1  */
3172621Sllai1 static void
3182621Sllai1 prof_make_symlinks(struct sdev_node *dir)
3192621Sllai1 {
3202621Sllai1 	char *tgt, *lnm;
3212621Sllai1 	nvpair_t *nvp = NULL;
3222621Sllai1 	nvlist_t *nvl = dir->sdev_prof.dev_symlink;
3232621Sllai1 	int rv;
3242621Sllai1 
3252621Sllai1 	ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
3262621Sllai1 
3272621Sllai1 	if (nvl == NULL)
3282621Sllai1 		return;
3292621Sllai1 
3302621Sllai1 	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
3312621Sllai1 		lnm = nvpair_name(nvp);
3322621Sllai1 		rv = nvpair_value_string(nvp, &tgt);
3332621Sllai1 		if (rv != 0) {
3342621Sllai1 			cmn_err(CE_WARN, sdev_nvp_val_err,
3352621Sllai1 			    rv, nvpair_name(nvp));
3362621Sllai1 			break;
3372621Sllai1 		}
3382621Sllai1 		prof_make_sym(dir, lnm, tgt);
3392621Sllai1 	}
3402621Sllai1 }
3412621Sllai1 
3422621Sllai1 static void
3432621Sllai1 prof_make_maps(struct sdev_node *dir)
3442621Sllai1 {
3452621Sllai1 	nvpair_t *nvp = NULL;
3462621Sllai1 	nvlist_t *nvl = dir->sdev_prof.dev_map;
3472621Sllai1 	int rv;
3482621Sllai1 
3492621Sllai1 	ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
3502621Sllai1 
3512621Sllai1 	if (nvl == NULL)
3522621Sllai1 		return;
3532621Sllai1 
3542621Sllai1 	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
3552621Sllai1 		char *name;
3562621Sllai1 		char *rename = nvpair_name(nvp);
3572621Sllai1 		rv = nvpair_value_string(nvp, &name);
3582621Sllai1 		if (rv != 0) {
3592621Sllai1 			cmn_err(CE_WARN, sdev_nvp_val_err,
3602621Sllai1 			    rv, nvpair_name(nvp));
3612621Sllai1 			break;
3622621Sllai1 		}
3632621Sllai1 		sdcmn_err10(("map %s -> %s\n", name, rename));
3642621Sllai1 		(void) prof_lookup_globaldev(dir, sdev_origins->sdev_root,
3652621Sllai1 		    name, rename);
3662621Sllai1 	}
3672621Sllai1 }
3682621Sllai1 
3692621Sllai1 struct match_arg {
3702621Sllai1 	char *expr;
3712621Sllai1 	int match;
3722621Sllai1 };
3732621Sllai1 
3742621Sllai1 static int
3752621Sllai1 match_name(char *name, void *arg)
3762621Sllai1 {
3772621Sllai1 	struct match_arg *margp = (struct match_arg *)arg;
3782621Sllai1 
3792621Sllai1 	if (gmatch(name, margp->expr)) {
3802621Sllai1 		margp->match = 1;
3812621Sllai1 		return (WALK_DIR_TERMINATE);
3822621Sllai1 	}
3832621Sllai1 	return (WALK_DIR_CONTINUE);
3842621Sllai1 }
3852621Sllai1 
3862621Sllai1 static int
3872621Sllai1 is_nonempty_dir(char *name, char *pathleft, struct sdev_node *dir)
3882621Sllai1 {
3892621Sllai1 	struct match_arg marg;
3902621Sllai1 	struct pathname pn;
3912621Sllai1 	struct vnode *gvp;
3922621Sllai1 	struct sdev_node *gdir = dir->sdev_origin;
3932621Sllai1 
394*5331Samw 	if (VOP_LOOKUP(SDEVTOV(gdir), name, &gvp, NULL, 0, NULL, kcred,
395*5331Samw 	    NULL, NULL, NULL) != 0)
3962621Sllai1 		return (0);
3972621Sllai1 
3982621Sllai1 	if (gvp->v_type != VDIR) {
3992621Sllai1 		VN_RELE(gvp);
4002621Sllai1 		return (0);
4012621Sllai1 	}
4022621Sllai1 
4032621Sllai1 	if (pn_get(pathleft, UIO_SYSSPACE, &pn) != 0) {
4042621Sllai1 		VN_RELE(gvp);
4052621Sllai1 		return (0);
4062621Sllai1 	}
4072621Sllai1 
4082621Sllai1 	marg.expr = kmem_alloc(MAXNAMELEN, KM_SLEEP);
4092621Sllai1 	(void) pn_getcomponent(&pn, marg.expr);
4102621Sllai1 	marg.match = 0;
4112621Sllai1 
4122621Sllai1 	walk_dir(gvp, &marg, match_name);
4132621Sllai1 	VN_RELE(gvp);
4142621Sllai1 	kmem_free(marg.expr, MAXNAMELEN);
4152621Sllai1 	pn_free(&pn);
4162621Sllai1 
4172621Sllai1 	return (marg.match);
4182621Sllai1 }
4192621Sllai1 
4202621Sllai1 
4212621Sllai1 /* Check if name passes matching rules */
4222621Sllai1 static int
4232621Sllai1 prof_name_matched(char *name, struct sdev_node *dir)
4242621Sllai1 {
4252621Sllai1 	int type, match = 0;
4262621Sllai1 	char *expr;
4272621Sllai1 	nvlist_t *nvl;
4282621Sllai1 	nvpair_t *nvp = NULL;
4292621Sllai1 	int rv;
4302621Sllai1 
4312621Sllai1 	/* check against nvlist for leaf include/exclude */
4322621Sllai1 	nvl = dir->sdev_prof.dev_name;
4332621Sllai1 	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
4342621Sllai1 		expr = nvpair_name(nvp);
4352621Sllai1 		rv = nvpair_value_int32(nvp, &type);
4362621Sllai1 		if (rv != 0) {
4372621Sllai1 			cmn_err(CE_WARN, sdev_nvp_val_err,
4382621Sllai1 			    rv, nvpair_name(nvp));
4392621Sllai1 			break;
4402621Sllai1 		}
4412621Sllai1 
4422621Sllai1 		if (type == PROFILE_TYPE_EXCLUDE) {
4432621Sllai1 			if (gmatch(name, expr))
4442621Sllai1 				return (0);	/* excluded */
4452621Sllai1 		} else if (!match) {
4462621Sllai1 			match = gmatch(name, expr);
4472621Sllai1 		}
4482621Sllai1 	}
4492621Sllai1 	if (match) {
4502621Sllai1 		sdcmn_err10(("prof_name_matched: %s\n", name));
4512621Sllai1 		return (match);
4522621Sllai1 	}
4532621Sllai1 
4542621Sllai1 	/* check for match against directory globbing pattern */
4552621Sllai1 	nvl = dir->sdev_prof.dev_glob_incdir;
4562621Sllai1 	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
4572621Sllai1 		char *pathleft;
4582621Sllai1 		expr = nvpair_name(nvp);
4592621Sllai1 		if (gmatch(name, expr) == 0)
4602621Sllai1 			continue;
4612621Sllai1 		rv = nvpair_value_string(nvp, &pathleft);
4622621Sllai1 		if (rv != 0) {
4632621Sllai1 			cmn_err(CE_WARN, sdev_nvp_val_err,
4642621Sllai1 			    rv, nvpair_name(nvp));
4652621Sllai1 			break;
4662621Sllai1 		}
4672621Sllai1 		if (is_nonempty_dir(name, pathleft, dir)) {
4682621Sllai1 			sdcmn_err10(("prof_name_matched: dir %s\n", name));
4692621Sllai1 			return (1);
4702621Sllai1 		}
4712621Sllai1 	}
4722621Sllai1 
4732621Sllai1 	return (0);
4742621Sllai1 }
4752621Sllai1 
4762621Sllai1 static void
4772621Sllai1 walk_dir(struct vnode *dvp, void *arg, int (*callback)(char *, void *))
4782621Sllai1 {
4792621Sllai1 	char    *nm;
4802621Sllai1 	int eof, error;
4812621Sllai1 	struct iovec iov;
4822621Sllai1 	struct uio uio;
4832621Sllai1 	struct dirent64 *dp;
4842621Sllai1 	dirent64_t *dbuf;
4852621Sllai1 	size_t dbuflen, dlen;
4862621Sllai1 
4872621Sllai1 	ASSERT(dvp);
4882621Sllai1 
4892621Sllai1 	dlen = 4096;
4902621Sllai1 	dbuf = kmem_zalloc(dlen, KM_SLEEP);
4912621Sllai1 
4922621Sllai1 	uio.uio_iov = &iov;
4932621Sllai1 	uio.uio_iovcnt = 1;
4942621Sllai1 	uio.uio_segflg = UIO_SYSSPACE;
4952621Sllai1 	uio.uio_fmode = 0;
4962621Sllai1 	uio.uio_extflg = UIO_COPY_CACHED;
4972621Sllai1 	uio.uio_loffset = 0;
4982621Sllai1 	uio.uio_llimit = MAXOFFSET_T;
4992621Sllai1 
5002621Sllai1 	eof = 0;
5012621Sllai1 	error = 0;
5022621Sllai1 	while (!error && !eof) {
5032621Sllai1 		uio.uio_resid = dlen;
5042621Sllai1 		iov.iov_base = (char *)dbuf;
5052621Sllai1 		iov.iov_len = dlen;
5062621Sllai1 		(void) VOP_RWLOCK(dvp, V_WRITELOCK_FALSE, NULL);
507*5331Samw 		error = VOP_READDIR(dvp, &uio, kcred, &eof, NULL, 0);
5082621Sllai1 		VOP_RWUNLOCK(dvp, V_WRITELOCK_FALSE, NULL);
5092621Sllai1 
5102621Sllai1 		dbuflen = dlen - uio.uio_resid;
5112621Sllai1 		if (error || dbuflen == 0)
5122621Sllai1 			break;
5132621Sllai1 		for (dp = dbuf; ((intptr_t)dp <
5142621Sllai1 		    (intptr_t)dbuf + dbuflen);
5152621Sllai1 		    dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) {
5162621Sllai1 			nm = dp->d_name;
5172621Sllai1 
5182621Sllai1 			if (strcmp(nm, ".") == 0 ||
5192621Sllai1 			    strcmp(nm, "..") == 0)
5202621Sllai1 				continue;
5212621Sllai1 
5222621Sllai1 			if (callback(nm, arg) == WALK_DIR_TERMINATE)
5232621Sllai1 				goto end;
5242621Sllai1 		}
5252621Sllai1 	}
5262621Sllai1 
5272621Sllai1 end:
5282621Sllai1 	kmem_free(dbuf, dlen);
5292621Sllai1 }
5302621Sllai1 
5312621Sllai1 static int
5322621Sllai1 prof_make_name(char *nm, void *arg)
5332621Sllai1 {
5342621Sllai1 	struct sdev_node *ddv = (struct sdev_node *)arg;
5352621Sllai1 
5362621Sllai1 	if (prof_name_matched(nm, ddv))
5372621Sllai1 		prof_lookup_globaldev(ddv, ddv->sdev_origin, nm, nm);
5382621Sllai1 	return (WALK_DIR_CONTINUE);
5392621Sllai1 }
5402621Sllai1 
5412621Sllai1 static void
5422621Sllai1 prof_make_names_glob(struct sdev_node *ddv)
5432621Sllai1 {
5442621Sllai1 	struct sdev_node *gdir;
5452621Sllai1 
5462621Sllai1 	gdir = ddv->sdev_origin;
5472621Sllai1 	if (gdir == NULL)
5482621Sllai1 		return;
5492621Sllai1 	walk_dir(SDEVTOV(gdir), (void *)ddv, prof_make_name);
5502621Sllai1 }
5512621Sllai1 
5522621Sllai1 static void
5532621Sllai1 prof_make_names(struct sdev_node *dir)
5542621Sllai1 {
5552621Sllai1 	char *name;
5562621Sllai1 	nvpair_t *nvp = NULL;
5572621Sllai1 	nvlist_t *nvl = dir->sdev_prof.dev_name;
5582621Sllai1 	int rv;
5592621Sllai1 
5602621Sllai1 	ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
5612621Sllai1 
5622621Sllai1 	if (nvl == NULL)
5632621Sllai1 		return;
5642621Sllai1 
5652621Sllai1 	if (dir->sdev_prof.has_glob) {
5662621Sllai1 		prof_make_names_glob(dir);
5672621Sllai1 		return;
5682621Sllai1 	}
5692621Sllai1 
5702621Sllai1 	/* Walk nvlist and lookup corresponding device in global inst */
5712621Sllai1 	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
5722621Sllai1 		int type;
5732621Sllai1 		rv = nvpair_value_int32(nvp, &type);
5742621Sllai1 		if (rv != 0) {
5752621Sllai1 			cmn_err(CE_WARN, sdev_nvp_val_err,
5762621Sllai1 			    rv, nvpair_name(nvp));
5772621Sllai1 			break;
5782621Sllai1 		}
5792621Sllai1 		if (type == PROFILE_TYPE_EXCLUDE)
5802621Sllai1 			continue;
5812621Sllai1 		name = nvpair_name(nvp);
5822621Sllai1 		(void) prof_lookup_globaldev(dir, dir->sdev_origin,
5832621Sllai1 		    name, name);
5842621Sllai1 	}
5852621Sllai1 }
5862621Sllai1 
5872621Sllai1 /*
5882621Sllai1  * Build directory vnodes based on the profile and the global
5892621Sllai1  * dev instance.
5902621Sllai1  */
5912621Sllai1 void
5922621Sllai1 prof_filldir(struct sdev_node *ddv)
5932621Sllai1 {
5942621Sllai1 	int firsttime = 1;
5952621Sllai1 	struct sdev_node *gdir = ddv->sdev_origin;
5962621Sllai1 
5972621Sllai1 	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
5982621Sllai1 
5992621Sllai1 	/*
6002621Sllai1 	 * We need to rebuild the directory content if
6012621Sllai1 	 * - SDEV_BUILD is set
6022621Sllai1 	 * - The device tree generation number has changed
6032621Sllai1 	 * - The corresponding /dev namespace has been updated
6042621Sllai1 	 */
6052621Sllai1 check_build:
6062621Sllai1 	if ((ddv->sdev_flags & SDEV_BUILD) == 0 &&
6072621Sllai1 	    ddv->sdev_devtree_gen == devtree_gen &&
6082621Sllai1 	    (gdir == NULL || ddv->sdev_ldir_gen
6092621Sllai1 	    == gdir->sdev_gdir_gen))
6102621Sllai1 		return;		/* already up to date */
6112621Sllai1 
6122621Sllai1 	if (firsttime && rw_tryupgrade(&ddv->sdev_contents) == 0) {
6132621Sllai1 		rw_exit(&ddv->sdev_contents);
6142621Sllai1 		firsttime = 0;
6152621Sllai1 		rw_enter(&ddv->sdev_contents, RW_WRITER);
6162621Sllai1 		goto check_build;
6172621Sllai1 	}
6182621Sllai1 	sdcmn_err10(("devtree_gen (%s): %ld -> %ld\n",
6192621Sllai1 	    ddv->sdev_path, ddv->sdev_devtree_gen, devtree_gen));
6202621Sllai1 	if (gdir)
6212621Sllai1 		sdcmn_err10(("sdev_dir_gen (%s): %ld -> %ld\n",
6222621Sllai1 		    ddv->sdev_path, ddv->sdev_ldir_gen,
6232621Sllai1 		    gdir->sdev_gdir_gen));
6242621Sllai1 
6252621Sllai1 	/* update flags and generation number so next filldir is quick */
6262621Sllai1 	ddv->sdev_flags &= ~SDEV_BUILD;
6272621Sllai1 	ddv->sdev_devtree_gen = devtree_gen;
6282621Sllai1 	if (gdir)
6292621Sllai1 		ddv->sdev_ldir_gen = gdir->sdev_gdir_gen;
6302621Sllai1 
6312621Sllai1 	prof_make_symlinks(ddv);
6322621Sllai1 	prof_make_maps(ddv);
6332621Sllai1 	prof_make_names(ddv);
6342621Sllai1 	rw_downgrade(&ddv->sdev_contents);
6352621Sllai1 }
6362621Sllai1 
6372621Sllai1 /* apply include/exclude pattern to existing directory content */
6382621Sllai1 static void
6392621Sllai1 apply_dir_pattern(struct sdev_node *dir, char *expr, char *pathleft, int type)
6402621Sllai1 {
6412621Sllai1 	struct sdev_node *dv;
6422621Sllai1 
6432621Sllai1 	/* leaf pattern */
6442621Sllai1 	if (pathleft == NULL) {
6452621Sllai1 		if (type == PROFILE_TYPE_INCLUDE)
6462621Sllai1 			return;	/* nothing to do for include */
6472621Sllai1 		(void) sdev_cleandir(dir, expr, SDEV_ENFORCE);
6482621Sllai1 		return;
6492621Sllai1 	}
6502621Sllai1 
6512621Sllai1 	/* directory pattern */
6522621Sllai1 	rw_enter(&dir->sdev_contents, RW_WRITER);
6532621Sllai1 	for (dv = dir->sdev_dot; dv; dv = dv->sdev_next) {
6542621Sllai1 		if (gmatch(dv->sdev_name, expr) == 0 ||
6552621Sllai1 		    SDEVTOV(dv)->v_type != VDIR)
6562621Sllai1 			continue;
6572621Sllai1 		process_rule(dv, dv->sdev_origin,
6582621Sllai1 		    pathleft, NULL, type);
6592621Sllai1 	}
6602621Sllai1 	rw_exit(&dir->sdev_contents);
6612621Sllai1 }
6622621Sllai1 
6632621Sllai1 /*
6642621Sllai1  * Add a profile rule.
6652621Sllai1  * tgt represents a device name matching expression,
6662621Sllai1  * matching device names are to be either included or excluded.
6672621Sllai1  */
6682621Sllai1 static void
6692621Sllai1 prof_add_rule(char *name, char *tgt, struct sdev_node *dir, int type)
6702621Sllai1 {
6712621Sllai1 	int error;
6722621Sllai1 	nvlist_t **nvlp = NULL;
6732621Sllai1 	int rv;
6742621Sllai1 
6752621Sllai1 	ASSERT(SDEVTOV(dir)->v_type == VDIR);
6762621Sllai1 
6772621Sllai1 	rw_enter(&dir->sdev_contents, RW_WRITER);
6782621Sllai1 
6792621Sllai1 	switch (type) {
6802621Sllai1 	case PROFILE_TYPE_INCLUDE:
6812621Sllai1 		if (tgt)
6822621Sllai1 			nvlp = &(dir->sdev_prof.dev_glob_incdir);
6832621Sllai1 		else
6842621Sllai1 			nvlp = &(dir->sdev_prof.dev_name);
6852621Sllai1 		break;
6862621Sllai1 	case PROFILE_TYPE_EXCLUDE:
6872621Sllai1 		if (tgt)
6882621Sllai1 			nvlp = &(dir->sdev_prof.dev_glob_excdir);
6892621Sllai1 		else
6902621Sllai1 			nvlp = &(dir->sdev_prof.dev_name);
6912621Sllai1 		break;
6922621Sllai1 	case PROFILE_TYPE_MAP:
6932621Sllai1 		nvlp = &(dir->sdev_prof.dev_map);
6942621Sllai1 		break;
6952621Sllai1 	case PROFILE_TYPE_SYMLINK:
6962621Sllai1 		nvlp = &(dir->sdev_prof.dev_symlink);
6972621Sllai1 		break;
6982621Sllai1 	};
6992621Sllai1 
7002621Sllai1 	/* initialize nvlist */
7012621Sllai1 	if (*nvlp == NULL) {
7022621Sllai1 		error = nvlist_alloc(nvlp, NV_UNIQUE_NAME, KM_SLEEP);
7032621Sllai1 		ASSERT(error == 0);
7042621Sllai1 	}
7052621Sllai1 
7062621Sllai1 	if (tgt) {
7072621Sllai1 		rv = nvlist_add_string(*nvlp, name, tgt);
7082621Sllai1 	} else {
7092621Sllai1 		rv = nvlist_add_int32(*nvlp, name, type);
7102621Sllai1 	}
7112621Sllai1 	ASSERT(rv == 0);
7122621Sllai1 	/* rebuild directory content */
7132621Sllai1 	dir->sdev_flags |= SDEV_BUILD;
7142621Sllai1 
7152621Sllai1 	if ((type == PROFILE_TYPE_INCLUDE) &&
7162621Sllai1 	    (strpbrk(name, "*?[]") != NULL)) {
7172621Sllai1 			dir->sdev_prof.has_glob = 1;
7182621Sllai1 	}
7192621Sllai1 
7202621Sllai1 	rw_exit(&dir->sdev_contents);
7212621Sllai1 
7222621Sllai1 	/* additional details for glob pattern and exclusion */
7232621Sllai1 	switch (type) {
7242621Sllai1 	case PROFILE_TYPE_INCLUDE:
7252621Sllai1 	case PROFILE_TYPE_EXCLUDE:
7262621Sllai1 		apply_dir_pattern(dir, name, tgt, type);
7272621Sllai1 		break;
7282621Sllai1 	};
7292621Sllai1 }
7302621Sllai1 
7312621Sllai1 /*
7322621Sllai1  * Parse path components and apply requested matching rule at
7332621Sllai1  * directory level.
7342621Sllai1  */
7352621Sllai1 static void
7362621Sllai1 process_rule(struct sdev_node *dir, struct sdev_node *gdir,
7372621Sllai1     char *path, char *tgt, int type)
7382621Sllai1 {
7392621Sllai1 	char *name;
7402621Sllai1 	struct pathname	pn;
7412621Sllai1 	int rv = 0;
7422621Sllai1 
7432621Sllai1 	if ((strlen(path) > 5) && (strncmp(path, "/dev/", 5) == 0)) {
7442621Sllai1 		path += 5;
7452621Sllai1 	}
7462621Sllai1 
7472621Sllai1 	if (pn_get(path, UIO_SYSSPACE, &pn) != 0)
7482621Sllai1 		return;
7492621Sllai1 
7502621Sllai1 	name = kmem_alloc(MAXPATHLEN, KM_SLEEP);
7512621Sllai1 	(void) pn_getcomponent(&pn, name);
7522621Sllai1 	pn_skipslash(&pn);
7532621Sllai1 	SDEV_HOLD(dir);
7542621Sllai1 
7552621Sllai1 	while (pn_pathleft(&pn)) {
7562621Sllai1 		/* If this is pattern, just add the pattern */
7572621Sllai1 		if (strpbrk(name, "*?[]") != NULL &&
7582621Sllai1 		    (type == PROFILE_TYPE_INCLUDE ||
7592621Sllai1 		    type == PROFILE_TYPE_EXCLUDE)) {
7602621Sllai1 			ASSERT(tgt == NULL);
7612621Sllai1 			tgt = pn.pn_path;
7622621Sllai1 			break;
7632621Sllai1 		}
7642621Sllai1 		if ((rv = prof_make_dir(name, &gdir, &dir)) != 0) {
7652621Sllai1 			cmn_err(CE_CONT, "process_rule: %s error %d\n",
7662621Sllai1 			    path, rv);
7672621Sllai1 			break;
7682621Sllai1 		}
7692621Sllai1 		(void) pn_getcomponent(&pn, name);
7702621Sllai1 		pn_skipslash(&pn);
7712621Sllai1 	}
7722621Sllai1 
7732621Sllai1 	/* process the leaf component */
7742621Sllai1 	if (rv == 0) {
7752621Sllai1 		prof_add_rule(name, tgt, dir, type);
7762621Sllai1 		SDEV_SIMPLE_RELE(dir);
7772621Sllai1 	}
7782621Sllai1 
7792621Sllai1 	kmem_free(name, MAXPATHLEN);
7802621Sllai1 	pn_free(&pn);
7812621Sllai1 }
7822621Sllai1 
7832621Sllai1 static int
7842621Sllai1 copyin_nvlist(char *packed_usr, size_t packed_sz, nvlist_t **nvlp)
7852621Sllai1 {
7862621Sllai1 	int err = 0;
7872621Sllai1 	char *packed;
7882621Sllai1 	nvlist_t *profile = NULL;
7892621Sllai1 
7902621Sllai1 	/* simple sanity check */
7912621Sllai1 	if (packed_usr == NULL || packed_sz == 0)
7922621Sllai1 		return (NULL);
7932621Sllai1 
7942621Sllai1 	/* copyin packed profile nvlist */
7952621Sllai1 	packed = kmem_alloc(packed_sz, KM_NOSLEEP);
7962621Sllai1 	if (packed == NULL)
7972621Sllai1 		return (ENOMEM);
7982621Sllai1 	err = copyin(packed_usr, packed, packed_sz);
7992621Sllai1 
8002621Sllai1 	/* unpack packed profile nvlist */
8012621Sllai1 	if (err)
8022621Sllai1 		cmn_err(CE_WARN, "copyin_nvlist: copyin failed with "
8032621Sllai1 		    "err %d\n", err);
8042621Sllai1 	else if (err = nvlist_unpack(packed, packed_sz, &profile, KM_NOSLEEP))
8052621Sllai1 		cmn_err(CE_WARN, "copyin_nvlist: nvlist_unpack "
8062621Sllai1 		    "failed with err %d\n", err);
8072621Sllai1 
8082621Sllai1 	kmem_free(packed, packed_sz);
8092621Sllai1 	if (err == 0)
8102621Sllai1 		*nvlp = profile;
8112621Sllai1 	return (err);
8122621Sllai1 }
8132621Sllai1 
8142621Sllai1 /*
8152621Sllai1  * Process profile passed down from libdevinfo. There are four types
8162621Sllai1  * of matching rules:
8172621Sllai1  *  include: export a name or names matching a pattern
8182621Sllai1  *  exclude: exclude a name or names matching a pattern
8192621Sllai1  *  symlink: create a local symlink
8202621Sllai1  *  map:     export a device with a name different from the global zone
8212621Sllai1  * Note: We may consider supporting VOP_SYMLINK in non-global instances,
8222621Sllai1  *	because it does not present any security risk. For now, the fs
8232621Sllai1  *	instance is read only.
8242621Sllai1  */
8252621Sllai1 static void
8262621Sllai1 sdev_process_profile(struct sdev_data *sdev_data, nvlist_t *profile)
8272621Sllai1 {
8282621Sllai1 	nvpair_t *nvpair;
8292621Sllai1 	char *nvname, *dname;
8302621Sllai1 	struct sdev_node *dir, *gdir;
8312621Sllai1 	char **pair;				/* for symlinks and maps */
8322621Sllai1 	uint_t nelem;
8332621Sllai1 	int rv;
8342621Sllai1 
8352621Sllai1 	gdir = sdev_origins->sdev_root;	/* root of global /dev */
8362621Sllai1 	dir = sdev_data->sdev_root;	/* root of current instance */
8372621Sllai1 
8382621Sllai1 	ASSERT(profile);
8392621Sllai1 
8402621Sllai1 	/* process nvpairs in the list */
8412621Sllai1 	nvpair = NULL;
8422621Sllai1 	while (nvpair = nvlist_next_nvpair(profile, nvpair)) {
8432621Sllai1 		nvname = nvpair_name(nvpair);
8442621Sllai1 		ASSERT(nvname != NULL);
8452621Sllai1 
8462621Sllai1 		if (strcmp(nvname, SDEV_NVNAME_INCLUDE) == 0) {
8472621Sllai1 			rv = nvpair_value_string(nvpair, &dname);
8482621Sllai1 			if (rv != 0) {
8492621Sllai1 				cmn_err(CE_WARN, sdev_nvp_val_err,
8502621Sllai1 				    rv, nvpair_name(nvpair));
8512621Sllai1 				break;
8522621Sllai1 			}
8532621Sllai1 			process_rule(dir, gdir, dname, NULL,
8542621Sllai1 			    PROFILE_TYPE_INCLUDE);
8552621Sllai1 		} else if (strcmp(nvname, SDEV_NVNAME_EXCLUDE) == 0) {
8562621Sllai1 			rv = nvpair_value_string(nvpair, &dname);
8572621Sllai1 			if (rv != 0) {
8582621Sllai1 				cmn_err(CE_WARN, sdev_nvp_val_err,
8592621Sllai1 				    rv, nvpair_name(nvpair));
8602621Sllai1 				break;
8612621Sllai1 			}
8622621Sllai1 			process_rule(dir, gdir, dname, NULL,
8632621Sllai1 			    PROFILE_TYPE_EXCLUDE);
8642621Sllai1 		} else if (strcmp(nvname, SDEV_NVNAME_SYMLINK) == 0) {
8652621Sllai1 			rv = nvpair_value_string_array(nvpair, &pair, &nelem);
8662621Sllai1 			if (rv != 0) {
8672621Sllai1 				cmn_err(CE_WARN, sdev_nvp_val_err,
8682621Sllai1 				    rv, nvpair_name(nvpair));
8692621Sllai1 				break;
8702621Sllai1 			}
8712621Sllai1 			ASSERT(nelem == 2);
8722621Sllai1 			process_rule(dir, gdir, pair[0], pair[1],
8732621Sllai1 			    PROFILE_TYPE_SYMLINK);
8742621Sllai1 		} else if (strcmp(nvname, SDEV_NVNAME_MAP) == 0) {
8752621Sllai1 			rv = nvpair_value_string_array(nvpair, &pair, &nelem);
8762621Sllai1 			if (rv != 0) {
8772621Sllai1 				cmn_err(CE_WARN, sdev_nvp_val_err,
8782621Sllai1 				    rv, nvpair_name(nvpair));
8792621Sllai1 				break;
8802621Sllai1 			}
8812621Sllai1 			process_rule(dir, gdir, pair[1], pair[0],
8822621Sllai1 			    PROFILE_TYPE_MAP);
8832621Sllai1 		} else if (strcmp(nvname, SDEV_NVNAME_MOUNTPT) != 0) {
8842621Sllai1 			cmn_err(CE_WARN, "sdev_process_profile: invalid "
8852621Sllai1 			    "nvpair %s\n", nvname);
8862621Sllai1 		}
8872621Sllai1 	}
8882621Sllai1 }
8892621Sllai1 
8902621Sllai1 /*ARGSUSED*/
8912621Sllai1 int
8922621Sllai1 prof_lookup(vnode_t *dvp, char *nm, struct vnode **vpp, struct cred *cred)
8932621Sllai1 {
8942621Sllai1 	struct sdev_node *ddv = VTOSDEV(dvp);
8952621Sllai1 	struct sdev_node *dv;
8962621Sllai1 	int nmlen;
8972621Sllai1 
8982621Sllai1 	/*
8992621Sllai1 	 * Empty name or ., return node itself.
9002621Sllai1 	 */
9012621Sllai1 	nmlen = strlen(nm);
9022621Sllai1 	if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) {
9032621Sllai1 		*vpp = SDEVTOV(ddv);
9042621Sllai1 		VN_HOLD(*vpp);
9052621Sllai1 		return (0);
9062621Sllai1 	}
9072621Sllai1 
9082621Sllai1 	/*
9092621Sllai1 	 * .., return the parent directory
9102621Sllai1 	 */
9112621Sllai1 	if ((nmlen == 2) && (strcmp(nm, "..") == 0)) {
9122621Sllai1 		*vpp = SDEVTOV(ddv->sdev_dotdot);
9132621Sllai1 		VN_HOLD(*vpp);
9142621Sllai1 		return (0);
9152621Sllai1 	}
9162621Sllai1 
9172621Sllai1 	rw_enter(&ddv->sdev_contents, RW_READER);
9182621Sllai1 	dv = sdev_cache_lookup(ddv, nm);
9192621Sllai1 	if (dv == NULL) {
9202621Sllai1 		prof_filldir(ddv);
9212621Sllai1 		dv = sdev_cache_lookup(ddv, nm);
9222621Sllai1 	}
9232621Sllai1 	rw_exit(&ddv->sdev_contents);
9242621Sllai1 	if (dv == NULL) {
9252621Sllai1 		sdcmn_err10(("prof_lookup: %s not found\n", nm));
9262621Sllai1 		return (ENOENT);
9272621Sllai1 	}
9282621Sllai1 
9292621Sllai1 	return (sdev_to_vp(dv, vpp));
9302621Sllai1 }
9312621Sllai1 
9322621Sllai1 /*
9332621Sllai1  * This is invoked after a new filesystem is mounted to define the
9342621Sllai1  * name space. It is also invoked during normal system operation
9352621Sllai1  * to update the name space.
9362621Sllai1  *
9372621Sllai1  * Applications call di_prof_commit() in libdevinfo, which invokes
9382621Sllai1  * modctl(). modctl calls this function. The input is a packed nvlist.
9392621Sllai1  */
9402621Sllai1 int
9412621Sllai1 devname_profile_update(char *packed, size_t packed_sz)
9422621Sllai1 {
9432621Sllai1 	char *mntpt;
9442621Sllai1 	nvlist_t *nvl;
9452621Sllai1 	nvpair_t *nvp;
9462621Sllai1 	struct sdev_data *mntinfo;
9472621Sllai1 	int err;
9482621Sllai1 	int rv;
9492621Sllai1 
9502621Sllai1 	nvl = NULL;
9512621Sllai1 	if ((err = copyin_nvlist(packed, packed_sz, &nvl)) != 0)
9522621Sllai1 		return (err);
9532621Sllai1 	ASSERT(nvl);
9542621Sllai1 
9552621Sllai1 	/* The first nvpair must be the mount point */
9562621Sllai1 	nvp = nvlist_next_nvpair(nvl, NULL);
9572621Sllai1 	if (strcmp(nvpair_name(nvp), SDEV_NVNAME_MOUNTPT) != 0) {
9582621Sllai1 		cmn_err(CE_NOTE,
9592621Sllai1 		    "devname_profile_update: mount point not specified");
9602621Sllai1 		nvlist_free(nvl);
9612621Sllai1 		return (EINVAL);
9622621Sllai1 	}
9632621Sllai1 
9642621Sllai1 	/* find the matching filesystem instance */
9652621Sllai1 	rv = nvpair_value_string(nvp, &mntpt);
9662621Sllai1 	if (rv != 0) {
9672621Sllai1 		cmn_err(CE_WARN, sdev_nvp_val_err,
9682621Sllai1 		    rv, nvpair_name(nvp));
9692621Sllai1 	} else {
9702621Sllai1 		mntinfo = sdev_find_mntinfo(mntpt);
9712621Sllai1 		if (mntinfo == NULL) {
9722621Sllai1 			cmn_err(CE_NOTE, "devname_profile_update: "
9732621Sllai1 			    " mount point %s not found", mntpt);
9742621Sllai1 			nvlist_free(nvl);
9752621Sllai1 			return (EINVAL);
9762621Sllai1 		}
9772621Sllai1 
9782621Sllai1 		/* now do the hardwork to process the profile */
9792621Sllai1 		sdev_process_profile(mntinfo, nvl);
9802621Sllai1 
9812621Sllai1 		sdev_mntinfo_rele(mntinfo);
9822621Sllai1 	}
9832621Sllai1 
9842621Sllai1 	nvlist_free(nvl);
9852621Sllai1 	return (0);
9862621Sllai1 }
987