1facf4a8dSllai /*
2facf4a8dSllai  * CDDL HEADER START
3facf4a8dSllai  *
4facf4a8dSllai  * The contents of this file are subject to the terms of the
5facf4a8dSllai  * Common Development and Distribution License (the "License").
6facf4a8dSllai  * You may not use this file except in compliance with the License.
7facf4a8dSllai  *
8facf4a8dSllai  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9facf4a8dSllai  * or http://www.opensolaris.org/os/licensing.
10facf4a8dSllai  * See the License for the specific language governing permissions
11facf4a8dSllai  * and limitations under the License.
12facf4a8dSllai  *
13facf4a8dSllai  * When distributing Covered Code, include this CDDL HEADER in each
14facf4a8dSllai  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15facf4a8dSllai  * If applicable, add the following below this CDDL HEADER, with the
16facf4a8dSllai  * fields enclosed by brackets "[]" replaced with your own identifying
17facf4a8dSllai  * information: Portions Copyright [yyyy] [name of copyright owner]
18facf4a8dSllai  *
19facf4a8dSllai  * CDDL HEADER END
20facf4a8dSllai  */
21facf4a8dSllai 
22facf4a8dSllai /*
23facf4a8dSllai  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24facf4a8dSllai  * Use is subject to license terms.
25facf4a8dSllai  */
26facf4a8dSllai 
27facf4a8dSllai #pragma ident	"%Z%%M%	%I%	%E% SMI"
28facf4a8dSllai 
29facf4a8dSllai /*
30facf4a8dSllai  * This file implements /dev filesystem operations for non-global
31facf4a8dSllai  * instances. Three major entry points:
32facf4a8dSllai  * devname_profile_update()
33facf4a8dSllai  *   Update matching rules determining which names to export
34facf4a8dSllai  * prof_readdir()
35facf4a8dSllai  *   Return the list of exported names
36facf4a8dSllai  * prof_lookup()
37facf4a8dSllai  *   Implements lookup
38facf4a8dSllai  */
39facf4a8dSllai 
40facf4a8dSllai #include <sys/types.h>
41facf4a8dSllai #include <sys/param.h>
42facf4a8dSllai #include <sys/sysmacros.h>
43facf4a8dSllai #include <sys/vnode.h>
44facf4a8dSllai #include <sys/uio.h>
45facf4a8dSllai #include <sys/dirent.h>
46facf4a8dSllai #include <sys/pathname.h>
47facf4a8dSllai #include <sys/fs/dv_node.h>
48facf4a8dSllai #include <sys/fs/sdev_impl.h>
49facf4a8dSllai #include <sys/sunndi.h>
50facf4a8dSllai #include <sys/modctl.h>
51facf4a8dSllai 
52facf4a8dSllai enum {
53facf4a8dSllai 	PROFILE_TYPE_INCLUDE,
54facf4a8dSllai 	PROFILE_TYPE_EXCLUDE,
55facf4a8dSllai 	PROFILE_TYPE_MAP,
56facf4a8dSllai 	PROFILE_TYPE_SYMLINK
57facf4a8dSllai };
58facf4a8dSllai 
59facf4a8dSllai enum {
60facf4a8dSllai 	WALK_DIR_CONTINUE = 0,
61facf4a8dSllai 	WALK_DIR_TERMINATE
62facf4a8dSllai };
63facf4a8dSllai 
64facf4a8dSllai static const char *sdev_nvp_val_err = "nvpair_value error %d, %s\n";
65facf4a8dSllai 
66facf4a8dSllai static void process_rule(struct sdev_node *, struct sdev_node *,
67facf4a8dSllai     char *, char *, int);
68facf4a8dSllai static void walk_dir(struct vnode *, void *, int (*)(char *, void *));
69facf4a8dSllai 
70facf4a8dSllai static void
71facf4a8dSllai prof_getattr(struct sdev_node *dir, char *name, struct vnode *gdv,
72facf4a8dSllai     struct vattr *vap, struct vnode **avpp, int *no_fs_perm)
73facf4a8dSllai {
74facf4a8dSllai 	struct vnode *advp;
75facf4a8dSllai 
76facf4a8dSllai 	/* get attribute from shadow, if present; else get default */
77facf4a8dSllai 	advp = dir->sdev_attrvp;
78facf4a8dSllai 	if (advp && VOP_LOOKUP(advp, name, avpp, NULL, 0, NULL, kcred) == 0) {
79facf4a8dSllai 		(void) VOP_GETATTR(*avpp, vap, 0, kcred);
80facf4a8dSllai 	} else if (gdv == NULL || gdv->v_type == VDIR) {
81facf4a8dSllai 		/* always create shadow directory */
82facf4a8dSllai 		*vap = sdev_vattr_dir;
83facf4a8dSllai 		if (advp && VOP_MKDIR(advp, name,
84facf4a8dSllai 		    &sdev_vattr_dir, avpp, kcred) != 0) {
85facf4a8dSllai 			*avpp = NULLVP;
86facf4a8dSllai 			sdcmn_err10(("prof_getattr: failed to create "
87facf4a8dSllai 			    "shadow directory %s/%s\n", dir->sdev_path, name));
88facf4a8dSllai 		}
89facf4a8dSllai 	} else {
90facf4a8dSllai 		/*
91facf4a8dSllai 		 * get default permission from devfs
92facf4a8dSllai 		 * Before calling devfs_get_defattr, we need to get
93facf4a8dSllai 		 * the realvp (the dv_node). If realvp is not a dv_node,
94facf4a8dSllai 		 * devfs_get_defattr() will return a system-wide default
95facf4a8dSllai 		 * attr for device nodes.
96facf4a8dSllai 		 */
97facf4a8dSllai 		struct vnode *rvp;
98facf4a8dSllai 		if (VOP_REALVP(gdv, &rvp) != 0)
99facf4a8dSllai 			rvp = gdv;
100facf4a8dSllai 		devfs_get_defattr(rvp, vap, no_fs_perm);
101facf4a8dSllai 		*avpp = NULLVP;
102facf4a8dSllai 	}
103facf4a8dSllai 
104facf4a8dSllai 	/* ignore dev_t and vtype from backing store */
105facf4a8dSllai 	if (gdv) {
106facf4a8dSllai 		vap->va_type = gdv->v_type;
107facf4a8dSllai 		vap->va_rdev = gdv->v_rdev;
108facf4a8dSllai 	}
109facf4a8dSllai }
110facf4a8dSllai 
111facf4a8dSllai static void
112facf4a8dSllai apply_glob_pattern(struct sdev_node *pdir, struct sdev_node *cdir)
113facf4a8dSllai {
114facf4a8dSllai 	char *name;
115facf4a8dSllai 	nvpair_t *nvp = NULL;
116facf4a8dSllai 	nvlist_t *nvl;
117facf4a8dSllai 	struct vnode *vp = SDEVTOV(cdir);
118facf4a8dSllai 	int rv = 0;
119facf4a8dSllai 
120facf4a8dSllai 	if (vp->v_type != VDIR)
121facf4a8dSllai 		return;
122facf4a8dSllai 	name = cdir->sdev_name;
123facf4a8dSllai 	nvl = pdir->sdev_prof.dev_glob_incdir;
124facf4a8dSllai 	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
125facf4a8dSllai 		char *pathleft;
126facf4a8dSllai 		char *expr = nvpair_name(nvp);
127facf4a8dSllai 		if (!gmatch(name, expr))
128facf4a8dSllai 			continue;
129facf4a8dSllai 		rv = nvpair_value_string(nvp, &pathleft);
130facf4a8dSllai 		if (rv != 0) {
131facf4a8dSllai 			cmn_err(CE_WARN, sdev_nvp_val_err,
132facf4a8dSllai 			    rv, nvpair_name(nvp));
133facf4a8dSllai 			break;
134facf4a8dSllai 		}
135facf4a8dSllai 		process_rule(cdir, cdir->sdev_origin,
136facf4a8dSllai 		    pathleft, NULL, PROFILE_TYPE_INCLUDE);
137facf4a8dSllai 	}
138facf4a8dSllai }
139facf4a8dSllai 
140facf4a8dSllai /*
141facf4a8dSllai  * Some commonality here with sdev_mknode(), could be simplified.
142facf4a8dSllai  * NOTE: prof_mknode returns with *newdv held once, if success.
143facf4a8dSllai  */
144facf4a8dSllai static int
145facf4a8dSllai prof_mknode(struct sdev_node *dir, char *name, struct sdev_node **newdv,
146facf4a8dSllai     vattr_t *vap, vnode_t *avp, void *arg, cred_t *cred)
147facf4a8dSllai {
148facf4a8dSllai 	struct sdev_node *dv;
149facf4a8dSllai 	int rv;
150facf4a8dSllai 
151facf4a8dSllai 	ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
152facf4a8dSllai 
153facf4a8dSllai 	/* check cache first */
154facf4a8dSllai 	if (dv = sdev_cache_lookup(dir, name)) {
155facf4a8dSllai 		*newdv = dv;
156facf4a8dSllai 		return (0);
157facf4a8dSllai 	}
158facf4a8dSllai 
159facf4a8dSllai 	/* allocate node and insert into cache */
160facf4a8dSllai 	rv = sdev_nodeinit(dir, name, &dv, NULL);
161facf4a8dSllai 	if (rv != 0) {
162facf4a8dSllai 		*newdv = NULL;
163facf4a8dSllai 		return (rv);
164facf4a8dSllai 	}
165facf4a8dSllai 
166facf4a8dSllai 	rv = sdev_cache_update(dir, &dv, name, SDEV_CACHE_ADD);
167facf4a8dSllai 	*newdv = dv;
168facf4a8dSllai 
169facf4a8dSllai 	/* put it in ready state */
170facf4a8dSllai 	rv = sdev_nodeready(*newdv, vap, avp, arg, cred);
171facf4a8dSllai 
172facf4a8dSllai 	/* handle glob pattern in the middle of a path */
173facf4a8dSllai 	if (rv == 0) {
174facf4a8dSllai 		if (SDEVTOV(*newdv)->v_type == VDIR)
175facf4a8dSllai 			sdcmn_err10(("sdev_origin for %s set to 0x%p\n",
176facf4a8dSllai 			    name, arg));
177facf4a8dSllai 		apply_glob_pattern(dir, *newdv);
178facf4a8dSllai 	}
179facf4a8dSllai 	return (rv);
180facf4a8dSllai }
181facf4a8dSllai 
182facf4a8dSllai /*
183facf4a8dSllai  * Create a directory node in a non-global dev instance.
184facf4a8dSllai  * Always create shadow vnode. Set sdev_origin to the corresponding
185facf4a8dSllai  * global directory sdev_node if it exists. This facilitates the
186facf4a8dSllai  * lookup operation.
187facf4a8dSllai  */
188facf4a8dSllai static int
189facf4a8dSllai prof_make_dir(char *name, struct sdev_node **gdirp, struct sdev_node **dirp)
190facf4a8dSllai {
191facf4a8dSllai 	struct sdev_node *dir = *dirp;
192facf4a8dSllai 	struct sdev_node *gdir = *gdirp;
193facf4a8dSllai 	struct sdev_node *newdv;
194facf4a8dSllai 	struct vnode *avp, *gnewdir = NULL;
195facf4a8dSllai 	struct vattr vattr;
196facf4a8dSllai 	int error;
197facf4a8dSllai 
198facf4a8dSllai 	/* see if name already exists */
199facf4a8dSllai 	rw_enter(&dir->sdev_contents, RW_READER);
200facf4a8dSllai 	if (newdv = sdev_cache_lookup(dir, name)) {
201facf4a8dSllai 		*dirp = newdv;
202facf4a8dSllai 		*gdirp = newdv->sdev_origin;
203facf4a8dSllai 		SDEV_RELE(dir);
204facf4a8dSllai 		rw_exit(&dir->sdev_contents);
205facf4a8dSllai 		return (0);
206facf4a8dSllai 	}
207facf4a8dSllai 	rw_exit(&dir->sdev_contents);
208facf4a8dSllai 
209facf4a8dSllai 	/* find corresponding dir node in global dev */
210facf4a8dSllai 	if (gdir) {
211facf4a8dSllai 		error = VOP_LOOKUP(SDEVTOV(gdir), name, &gnewdir,
212facf4a8dSllai 		    NULL, 0, NULL, kcred);
213facf4a8dSllai 		if (error == 0) {
214facf4a8dSllai 			*gdirp = VTOSDEV(gnewdir);
215facf4a8dSllai 		} else { 	/* it's ok if there no global dir */
216facf4a8dSllai 			*gdirp = NULL;
217facf4a8dSllai 		}
218facf4a8dSllai 	}
219facf4a8dSllai 
220facf4a8dSllai 	/* get attribute from shadow, also create shadow dir */
221facf4a8dSllai 	prof_getattr(dir, name, gnewdir, &vattr, &avp, NULL);
222facf4a8dSllai 
223facf4a8dSllai 	/* create dev directory vnode */
224facf4a8dSllai 	rw_enter(&dir->sdev_contents, RW_WRITER);
225facf4a8dSllai 	error = prof_mknode(dir, name, &newdv, &vattr, avp, (void *)*gdirp,
226facf4a8dSllai 	    kcred);
227facf4a8dSllai 	rw_exit(&dir->sdev_contents);
228facf4a8dSllai 	if (error == 0) {
229facf4a8dSllai 		ASSERT(newdv);
230facf4a8dSllai 		*dirp = newdv;
231facf4a8dSllai 	}
232facf4a8dSllai 	SDEV_RELE(dir);
233facf4a8dSllai 	return (error);
234facf4a8dSllai }
235facf4a8dSllai 
236facf4a8dSllai /*
237facf4a8dSllai  * Look up a logical name in the global zone.
238facf4a8dSllai  * Provides the ability to map the global zone's device name
239facf4a8dSllai  * to an alternate name within a zone.  The primary example
240facf4a8dSllai  * is the virtual console device /dev/zcons/[zonename]/zconsole
241facf4a8dSllai  * mapped to /[zonename]/root/dev/zconsole.
242facf4a8dSllai  */
243facf4a8dSllai static void
244facf4a8dSllai prof_lookup_globaldev(struct sdev_node *dir, struct sdev_node *gdir,
245facf4a8dSllai     char *name, char *rename)
246facf4a8dSllai {
247facf4a8dSllai 	/* global OS rootdir */
248facf4a8dSllai 	extern vnode_t *rootdir;
249facf4a8dSllai 
250facf4a8dSllai 	int error;
251facf4a8dSllai 	struct vnode *avp, *gdv, *gddv;
252facf4a8dSllai 	struct sdev_node *newdv;
253facf4a8dSllai 	struct vattr vattr = {0};
254facf4a8dSllai 	struct pathname pn;
255facf4a8dSllai 
256facf4a8dSllai 	/* check if node already exists */
257facf4a8dSllai 	newdv = sdev_cache_lookup(dir, rename);
258facf4a8dSllai 	if (newdv) {
259facf4a8dSllai 		ASSERT(newdv->sdev_state != SDEV_ZOMBIE);
260facf4a8dSllai 		SDEV_SIMPLE_RELE(newdv);
261facf4a8dSllai 		return;
262facf4a8dSllai 	}
263facf4a8dSllai 
264facf4a8dSllai 	/* sanity check arguments */
265facf4a8dSllai 	if (!gdir || pn_get(name, UIO_SYSSPACE, &pn))
266facf4a8dSllai 		return;
267facf4a8dSllai 
268facf4a8dSllai 	/* perform a relative lookup of the global /dev instance */
269facf4a8dSllai 	gddv = SDEVTOV(gdir);
270facf4a8dSllai 	VN_HOLD(gddv);
271facf4a8dSllai 	VN_HOLD(rootdir);
272facf4a8dSllai 	error = lookuppnvp(&pn, NULL, FOLLOW, NULLVPP, &gdv,
273facf4a8dSllai 	    rootdir, gddv, kcred);
274facf4a8dSllai 	pn_free(&pn);
275facf4a8dSllai 	if (error) {
276facf4a8dSllai 		sdcmn_err10(("prof_lookup_globaldev: %s not found\n", name));
277facf4a8dSllai 		return;
278facf4a8dSllai 	}
279facf4a8dSllai 	ASSERT(gdv && gdv->v_type != VLNK);
280facf4a8dSllai 
281facf4a8dSllai 	/*
282facf4a8dSllai 	 * Found the entry in global /dev, figure out attributes
283facf4a8dSllai 	 * by looking at backing store. Call into devfs for default.
284*a83b1f2cSllai 	 * Note, mapped device is persisted under the new name
285facf4a8dSllai 	 */
286*a83b1f2cSllai 	prof_getattr(dir, rename, gdv, &vattr, &avp, NULL);
287facf4a8dSllai 
288facf4a8dSllai 	if (gdv->v_type != VDIR) {
289facf4a8dSllai 		VN_RELE(gdv);
290facf4a8dSllai 		gdir = NULL;
291facf4a8dSllai 	} else
292facf4a8dSllai 		gdir = VTOSDEV(gdv);
293facf4a8dSllai 
294facf4a8dSllai 	if (prof_mknode(dir, rename, &newdv, &vattr, avp,
295facf4a8dSllai 	    (void *)gdir, kcred) == 0) {
296facf4a8dSllai 		ASSERT(newdv->sdev_state != SDEV_ZOMBIE);
297facf4a8dSllai 		SDEV_SIMPLE_RELE(newdv);
298facf4a8dSllai 	}
299facf4a8dSllai }
300facf4a8dSllai 
301facf4a8dSllai static void
302facf4a8dSllai prof_make_sym(struct sdev_node *dir, char *lnm, char *tgt)
303facf4a8dSllai {
304facf4a8dSllai 	struct sdev_node *newdv;
305facf4a8dSllai 
306facf4a8dSllai 	if (prof_mknode(dir, lnm, &newdv, &sdev_vattr_lnk, NULL,
307facf4a8dSllai 	    (void *)tgt, kcred) == 0) {
308facf4a8dSllai 		ASSERT(newdv->sdev_state != SDEV_ZOMBIE);
309facf4a8dSllai 		SDEV_SIMPLE_RELE(newdv);
310facf4a8dSllai 	}
311facf4a8dSllai }
312facf4a8dSllai 
313facf4a8dSllai /*
314facf4a8dSllai  * Create symlinks in the current directory based on profile
315facf4a8dSllai  */
316facf4a8dSllai static void
317facf4a8dSllai prof_make_symlinks(struct sdev_node *dir)
318facf4a8dSllai {
319facf4a8dSllai 	char *tgt, *lnm;
320facf4a8dSllai 	nvpair_t *nvp = NULL;
321facf4a8dSllai 	nvlist_t *nvl = dir->sdev_prof.dev_symlink;
322facf4a8dSllai 	int rv;
323facf4a8dSllai 
324facf4a8dSllai 	ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
325facf4a8dSllai 
326facf4a8dSllai 	if (nvl == NULL)
327facf4a8dSllai 		return;
328facf4a8dSllai 
329facf4a8dSllai 	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
330facf4a8dSllai 		lnm = nvpair_name(nvp);
331facf4a8dSllai 		rv = nvpair_value_string(nvp, &tgt);
332facf4a8dSllai 		if (rv != 0) {
333facf4a8dSllai 			cmn_err(CE_WARN, sdev_nvp_val_err,
334facf4a8dSllai 			    rv, nvpair_name(nvp));
335facf4a8dSllai 			break;
336facf4a8dSllai 		}
337facf4a8dSllai 		prof_make_sym(dir, lnm, tgt);
338facf4a8dSllai 	}
339facf4a8dSllai }
340facf4a8dSllai 
341facf4a8dSllai static void
342facf4a8dSllai prof_make_maps(struct sdev_node *dir)
343facf4a8dSllai {
344facf4a8dSllai 	nvpair_t *nvp = NULL;
345facf4a8dSllai 	nvlist_t *nvl = dir->sdev_prof.dev_map;
346facf4a8dSllai 	int rv;
347facf4a8dSllai 
348facf4a8dSllai 	ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
349facf4a8dSllai 
350facf4a8dSllai 	if (nvl == NULL)
351facf4a8dSllai 		return;
352facf4a8dSllai 
353facf4a8dSllai 	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
354facf4a8dSllai 		char *name;
355facf4a8dSllai 		char *rename = nvpair_name(nvp);
356facf4a8dSllai 		rv = nvpair_value_string(nvp, &name);
357facf4a8dSllai 		if (rv != 0) {
358facf4a8dSllai 			cmn_err(CE_WARN, sdev_nvp_val_err,
359facf4a8dSllai 			    rv, nvpair_name(nvp));
360facf4a8dSllai 			break;
361facf4a8dSllai 		}
362facf4a8dSllai 		sdcmn_err10(("map %s -> %s\n", name, rename));
363facf4a8dSllai 		(void) prof_lookup_globaldev(dir, sdev_origins->sdev_root,
364facf4a8dSllai 		    name, rename);
365facf4a8dSllai 	}
366facf4a8dSllai }
367facf4a8dSllai 
368facf4a8dSllai struct match_arg {
369facf4a8dSllai 	char *expr;
370facf4a8dSllai 	int match;
371facf4a8dSllai };
372facf4a8dSllai 
373facf4a8dSllai static int
374facf4a8dSllai match_name(char *name, void *arg)
375facf4a8dSllai {
376facf4a8dSllai 	struct match_arg *margp = (struct match_arg *)arg;
377facf4a8dSllai 
378facf4a8dSllai 	if (gmatch(name, margp->expr)) {
379facf4a8dSllai 		margp->match = 1;
380facf4a8dSllai 		return (WALK_DIR_TERMINATE);
381facf4a8dSllai 	}
382facf4a8dSllai 	return (WALK_DIR_CONTINUE);
383facf4a8dSllai }
384facf4a8dSllai 
385facf4a8dSllai static int
386facf4a8dSllai is_nonempty_dir(char *name, char *pathleft, struct sdev_node *dir)
387facf4a8dSllai {
388facf4a8dSllai 	struct match_arg marg;
389facf4a8dSllai 	struct pathname pn;
390facf4a8dSllai 	struct vnode *gvp;
391facf4a8dSllai 	struct sdev_node *gdir = dir->sdev_origin;
392facf4a8dSllai 
393facf4a8dSllai 	if (VOP_LOOKUP(SDEVTOV(gdir), name, &gvp, NULL, 0, NULL, kcred) != 0)
394facf4a8dSllai 		return (0);
395facf4a8dSllai 
396facf4a8dSllai 	if (gvp->v_type != VDIR) {
397facf4a8dSllai 		VN_RELE(gvp);
398facf4a8dSllai 		return (0);
399facf4a8dSllai 	}
400facf4a8dSllai 
401facf4a8dSllai 	if (pn_get(pathleft, UIO_SYSSPACE, &pn) != 0) {
402facf4a8dSllai 		VN_RELE(gvp);
403facf4a8dSllai 		return (0);
404facf4a8dSllai 	}
405facf4a8dSllai 
406facf4a8dSllai 	marg.expr = kmem_alloc(MAXNAMELEN, KM_SLEEP);
407facf4a8dSllai 	(void) pn_getcomponent(&pn, marg.expr);
408facf4a8dSllai 	marg.match = 0;
409facf4a8dSllai 
410facf4a8dSllai 	walk_dir(gvp, &marg, match_name);
411facf4a8dSllai 	VN_RELE(gvp);
412facf4a8dSllai 	kmem_free(marg.expr, MAXNAMELEN);
413facf4a8dSllai 	pn_free(&pn);
414facf4a8dSllai 
415facf4a8dSllai 	return (marg.match);
416facf4a8dSllai }
417facf4a8dSllai 
418facf4a8dSllai 
419facf4a8dSllai /* Check if name passes matching rules */
420facf4a8dSllai static int
421facf4a8dSllai prof_name_matched(char *name, struct sdev_node *dir)
422facf4a8dSllai {
423facf4a8dSllai 	int type, match = 0;
424facf4a8dSllai 	char *expr;
425facf4a8dSllai 	nvlist_t *nvl;
426facf4a8dSllai 	nvpair_t *nvp = NULL;
427facf4a8dSllai 	int rv;
428facf4a8dSllai 
429facf4a8dSllai 	/* check against nvlist for leaf include/exclude */
430facf4a8dSllai 	nvl = dir->sdev_prof.dev_name;
431facf4a8dSllai 	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
432facf4a8dSllai 		expr = nvpair_name(nvp);
433facf4a8dSllai 		rv = nvpair_value_int32(nvp, &type);
434facf4a8dSllai 		if (rv != 0) {
435facf4a8dSllai 			cmn_err(CE_WARN, sdev_nvp_val_err,
436facf4a8dSllai 			    rv, nvpair_name(nvp));
437facf4a8dSllai 			break;
438facf4a8dSllai 		}
439facf4a8dSllai 
440facf4a8dSllai 		if (type == PROFILE_TYPE_EXCLUDE) {
441facf4a8dSllai 			if (gmatch(name, expr))
442facf4a8dSllai 				return (0);	/* excluded */
443facf4a8dSllai 		} else if (!match) {
444facf4a8dSllai 			match = gmatch(name, expr);
445facf4a8dSllai 		}
446facf4a8dSllai 	}
447facf4a8dSllai 	if (match) {
448facf4a8dSllai 		sdcmn_err10(("prof_name_matched: %s\n", name));
449facf4a8dSllai 		return (match);
450facf4a8dSllai 	}
451facf4a8dSllai 
452facf4a8dSllai 	/* check for match against directory globbing pattern */
453facf4a8dSllai 	nvl = dir->sdev_prof.dev_glob_incdir;
454facf4a8dSllai 	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
455facf4a8dSllai 		char *pathleft;
456facf4a8dSllai 		expr = nvpair_name(nvp);
457facf4a8dSllai 		if (gmatch(name, expr) == 0)
458facf4a8dSllai 			continue;
459facf4a8dSllai 		rv = nvpair_value_string(nvp, &pathleft);
460facf4a8dSllai 		if (rv != 0) {
461facf4a8dSllai 			cmn_err(CE_WARN, sdev_nvp_val_err,
462facf4a8dSllai 			    rv, nvpair_name(nvp));
463facf4a8dSllai 			break;
464facf4a8dSllai 		}
465facf4a8dSllai 		if (is_nonempty_dir(name, pathleft, dir)) {
466facf4a8dSllai 			sdcmn_err10(("prof_name_matched: dir %s\n", name));
467facf4a8dSllai 			return (1);
468facf4a8dSllai 		}
469facf4a8dSllai 	}
470facf4a8dSllai 
471facf4a8dSllai 	return (0);
472facf4a8dSllai }
473facf4a8dSllai 
474facf4a8dSllai static void
475facf4a8dSllai walk_dir(struct vnode *dvp, void *arg, int (*callback)(char *, void *))
476facf4a8dSllai {
477facf4a8dSllai 	char    *nm;
478facf4a8dSllai 	int eof, error;
479facf4a8dSllai 	struct iovec iov;
480facf4a8dSllai 	struct uio uio;
481facf4a8dSllai 	struct dirent64 *dp;
482facf4a8dSllai 	dirent64_t *dbuf;
483facf4a8dSllai 	size_t dbuflen, dlen;
484facf4a8dSllai 
485facf4a8dSllai 	ASSERT(dvp);
486facf4a8dSllai 
487facf4a8dSllai 	dlen = 4096;
488facf4a8dSllai 	dbuf = kmem_zalloc(dlen, KM_SLEEP);
489facf4a8dSllai 
490facf4a8dSllai 	uio.uio_iov = &iov;
491facf4a8dSllai 	uio.uio_iovcnt = 1;
492facf4a8dSllai 	uio.uio_segflg = UIO_SYSSPACE;
493facf4a8dSllai 	uio.uio_fmode = 0;
494facf4a8dSllai 	uio.uio_extflg = UIO_COPY_CACHED;
495facf4a8dSllai 	uio.uio_loffset = 0;
496facf4a8dSllai 	uio.uio_llimit = MAXOFFSET_T;
497facf4a8dSllai 
498facf4a8dSllai 	eof = 0;
499facf4a8dSllai 	error = 0;
500facf4a8dSllai 	while (!error && !eof) {
501facf4a8dSllai 		uio.uio_resid = dlen;
502facf4a8dSllai 		iov.iov_base = (char *)dbuf;
503facf4a8dSllai 		iov.iov_len = dlen;
504facf4a8dSllai 		(void) VOP_RWLOCK(dvp, V_WRITELOCK_FALSE, NULL);
505facf4a8dSllai 		error = VOP_READDIR(dvp, &uio, kcred, &eof);
506facf4a8dSllai 		VOP_RWUNLOCK(dvp, V_WRITELOCK_FALSE, NULL);
507facf4a8dSllai 
508facf4a8dSllai 		dbuflen = dlen - uio.uio_resid;
509facf4a8dSllai 		if (error || dbuflen == 0)
510facf4a8dSllai 			break;
511facf4a8dSllai 		for (dp = dbuf; ((intptr_t)dp <
512facf4a8dSllai 		    (intptr_t)dbuf + dbuflen);
513facf4a8dSllai 		    dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) {
514facf4a8dSllai 			nm = dp->d_name;
515facf4a8dSllai 
516facf4a8dSllai 			if (strcmp(nm, ".") == 0 ||
517facf4a8dSllai 			    strcmp(nm, "..") == 0)
518facf4a8dSllai 				continue;
519facf4a8dSllai 
520facf4a8dSllai 			if (callback(nm, arg) == WALK_DIR_TERMINATE)
521facf4a8dSllai 				goto end;
522facf4a8dSllai 		}
523facf4a8dSllai 	}
524facf4a8dSllai 
525facf4a8dSllai end:
526facf4a8dSllai 	kmem_free(dbuf, dlen);
527facf4a8dSllai }
528facf4a8dSllai 
529facf4a8dSllai static int
530facf4a8dSllai prof_make_name(char *nm, void *arg)
531facf4a8dSllai {
532facf4a8dSllai 	struct sdev_node *ddv = (struct sdev_node *)arg;
533facf4a8dSllai 
534facf4a8dSllai 	if (prof_name_matched(nm, ddv))
535facf4a8dSllai 		prof_lookup_globaldev(ddv, ddv->sdev_origin, nm, nm);
536facf4a8dSllai 	return (WALK_DIR_CONTINUE);
537facf4a8dSllai }
538facf4a8dSllai 
539facf4a8dSllai static void
540facf4a8dSllai prof_make_names_glob(struct sdev_node *ddv)
541facf4a8dSllai {
542facf4a8dSllai 	struct sdev_node *gdir;
543facf4a8dSllai 
544facf4a8dSllai 	gdir = ddv->sdev_origin;
545facf4a8dSllai 	if (gdir == NULL)
546facf4a8dSllai 		return;
547facf4a8dSllai 	walk_dir(SDEVTOV(gdir), (void *)ddv, prof_make_name);
548facf4a8dSllai }
549facf4a8dSllai 
550facf4a8dSllai static void
551facf4a8dSllai prof_make_names(struct sdev_node *dir)
552facf4a8dSllai {
553facf4a8dSllai 	char *name;
554facf4a8dSllai 	nvpair_t *nvp = NULL;
555facf4a8dSllai 	nvlist_t *nvl = dir->sdev_prof.dev_name;
556facf4a8dSllai 	int rv;
557facf4a8dSllai 
558facf4a8dSllai 	ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
559facf4a8dSllai 
560facf4a8dSllai 	if (nvl == NULL)
561facf4a8dSllai 		return;
562facf4a8dSllai 
563facf4a8dSllai 	if (dir->sdev_prof.has_glob) {
564facf4a8dSllai 		prof_make_names_glob(dir);
565facf4a8dSllai 		return;
566facf4a8dSllai 	}
567facf4a8dSllai 
568facf4a8dSllai 	/* Walk nvlist and lookup corresponding device in global inst */
569facf4a8dSllai 	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
570facf4a8dSllai 		int type;
571facf4a8dSllai 		rv = nvpair_value_int32(nvp, &type);
572facf4a8dSllai 		if (rv != 0) {
573facf4a8dSllai 			cmn_err(CE_WARN, sdev_nvp_val_err,
574facf4a8dSllai 			    rv, nvpair_name(nvp));
575facf4a8dSllai 			break;
576facf4a8dSllai 		}
577facf4a8dSllai 		if (type == PROFILE_TYPE_EXCLUDE)
578facf4a8dSllai 			continue;
579facf4a8dSllai 		name = nvpair_name(nvp);
580facf4a8dSllai 		(void) prof_lookup_globaldev(dir, dir->sdev_origin,
581facf4a8dSllai 		    name, name);
582facf4a8dSllai 	}
583facf4a8dSllai }
584facf4a8dSllai 
585facf4a8dSllai /*
586facf4a8dSllai  * Build directory vnodes based on the profile and the global
587facf4a8dSllai  * dev instance.
588facf4a8dSllai  */
589facf4a8dSllai void
590facf4a8dSllai prof_filldir(struct sdev_node *ddv)
591facf4a8dSllai {
592facf4a8dSllai 	int firsttime = 1;
593facf4a8dSllai 	struct sdev_node *gdir = ddv->sdev_origin;
594facf4a8dSllai 
595facf4a8dSllai 	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
596facf4a8dSllai 
597facf4a8dSllai 	/*
598facf4a8dSllai 	 * We need to rebuild the directory content if
599facf4a8dSllai 	 * - SDEV_BUILD is set
600facf4a8dSllai 	 * - The device tree generation number has changed
601facf4a8dSllai 	 * - The corresponding /dev namespace has been updated
602facf4a8dSllai 	 */
603facf4a8dSllai check_build:
604facf4a8dSllai 	if ((ddv->sdev_flags & SDEV_BUILD) == 0 &&
605facf4a8dSllai 	    ddv->sdev_devtree_gen == devtree_gen &&
606facf4a8dSllai 	    (gdir == NULL || ddv->sdev_ldir_gen
607facf4a8dSllai 	    == gdir->sdev_gdir_gen))
608facf4a8dSllai 		return;		/* already up to date */
609facf4a8dSllai 
610facf4a8dSllai 	if (firsttime && rw_tryupgrade(&ddv->sdev_contents) == 0) {
611facf4a8dSllai 		rw_exit(&ddv->sdev_contents);
612facf4a8dSllai 		firsttime = 0;
613facf4a8dSllai 		rw_enter(&ddv->sdev_contents, RW_WRITER);
614facf4a8dSllai 		goto check_build;
615facf4a8dSllai 	}
616facf4a8dSllai 	sdcmn_err10(("devtree_gen (%s): %ld -> %ld\n",
617facf4a8dSllai 	    ddv->sdev_path, ddv->sdev_devtree_gen, devtree_gen));
618facf4a8dSllai 	if (gdir)
619facf4a8dSllai 		sdcmn_err10(("sdev_dir_gen (%s): %ld -> %ld\n",
620facf4a8dSllai 		    ddv->sdev_path, ddv->sdev_ldir_gen,
621facf4a8dSllai 		    gdir->sdev_gdir_gen));
622facf4a8dSllai 
623facf4a8dSllai 	/* update flags and generation number so next filldir is quick */
624facf4a8dSllai 	ddv->sdev_flags &= ~SDEV_BUILD;
625facf4a8dSllai 	ddv->sdev_devtree_gen = devtree_gen;
626facf4a8dSllai 	if (gdir)
627facf4a8dSllai 		ddv->sdev_ldir_gen = gdir->sdev_gdir_gen;
628facf4a8dSllai 
629facf4a8dSllai 	prof_make_symlinks(ddv);
630facf4a8dSllai 	prof_make_maps(ddv);
631facf4a8dSllai 	prof_make_names(ddv);
632facf4a8dSllai 	rw_downgrade(&ddv->sdev_contents);
633facf4a8dSllai }
634facf4a8dSllai 
635facf4a8dSllai /* apply include/exclude pattern to existing directory content */
636facf4a8dSllai static void
637facf4a8dSllai apply_dir_pattern(struct sdev_node *dir, char *expr, char *pathleft, int type)
638facf4a8dSllai {
639facf4a8dSllai 	struct sdev_node *dv;
640facf4a8dSllai 
641facf4a8dSllai 	/* leaf pattern */
642facf4a8dSllai 	if (pathleft == NULL) {
643facf4a8dSllai 		if (type == PROFILE_TYPE_INCLUDE)
644facf4a8dSllai 			return;	/* nothing to do for include */
645facf4a8dSllai 		(void) sdev_cleandir(dir, expr, SDEV_ENFORCE);
646facf4a8dSllai 		return;
647facf4a8dSllai 	}
648facf4a8dSllai 
649facf4a8dSllai 	/* directory pattern */
650facf4a8dSllai 	rw_enter(&dir->sdev_contents, RW_WRITER);
651facf4a8dSllai 	for (dv = dir->sdev_dot; dv; dv = dv->sdev_next) {
652facf4a8dSllai 		if (gmatch(dv->sdev_name, expr) == 0 ||
653facf4a8dSllai 		    SDEVTOV(dv)->v_type != VDIR)
654facf4a8dSllai 			continue;
655facf4a8dSllai 		process_rule(dv, dv->sdev_origin,
656facf4a8dSllai 		    pathleft, NULL, type);
657facf4a8dSllai 	}
658facf4a8dSllai 	rw_exit(&dir->sdev_contents);
659facf4a8dSllai }
660facf4a8dSllai 
661facf4a8dSllai /*
662facf4a8dSllai  * Add a profile rule.
663facf4a8dSllai  * tgt represents a device name matching expression,
664facf4a8dSllai  * matching device names are to be either included or excluded.
665facf4a8dSllai  */
666facf4a8dSllai static void
667facf4a8dSllai prof_add_rule(char *name, char *tgt, struct sdev_node *dir, int type)
668facf4a8dSllai {
669facf4a8dSllai 	int error;
670facf4a8dSllai 	nvlist_t **nvlp = NULL;
671facf4a8dSllai 	int rv;
672facf4a8dSllai 
673facf4a8dSllai 	ASSERT(SDEVTOV(dir)->v_type == VDIR);
674facf4a8dSllai 
675facf4a8dSllai 	rw_enter(&dir->sdev_contents, RW_WRITER);
676facf4a8dSllai 
677facf4a8dSllai 	switch (type) {
678facf4a8dSllai 	case PROFILE_TYPE_INCLUDE:
679facf4a8dSllai 		if (tgt)
680facf4a8dSllai 			nvlp = &(dir->sdev_prof.dev_glob_incdir);
681facf4a8dSllai 		else
682facf4a8dSllai 			nvlp = &(dir->sdev_prof.dev_name);
683facf4a8dSllai 		break;
684facf4a8dSllai 	case PROFILE_TYPE_EXCLUDE:
685facf4a8dSllai 		if (tgt)
686facf4a8dSllai 			nvlp = &(dir->sdev_prof.dev_glob_excdir);
687facf4a8dSllai 		else
688facf4a8dSllai 			nvlp = &(dir->sdev_prof.dev_name);
689facf4a8dSllai 		break;
690facf4a8dSllai 	case PROFILE_TYPE_MAP:
691facf4a8dSllai 		nvlp = &(dir->sdev_prof.dev_map);
692facf4a8dSllai 		break;
693facf4a8dSllai 	case PROFILE_TYPE_SYMLINK:
694facf4a8dSllai 		nvlp = &(dir->sdev_prof.dev_symlink);
695facf4a8dSllai 		break;
696facf4a8dSllai 	};
697facf4a8dSllai 
698facf4a8dSllai 	/* initialize nvlist */
699facf4a8dSllai 	if (*nvlp == NULL) {
700facf4a8dSllai 		error = nvlist_alloc(nvlp, NV_UNIQUE_NAME, KM_SLEEP);
701facf4a8dSllai 		ASSERT(error == 0);
702facf4a8dSllai 	}
703facf4a8dSllai 
704facf4a8dSllai 	if (tgt) {
705facf4a8dSllai 		rv = nvlist_add_string(*nvlp, name, tgt);
706facf4a8dSllai 	} else {
707facf4a8dSllai 		rv = nvlist_add_int32(*nvlp, name, type);
708facf4a8dSllai 	}
709facf4a8dSllai 	ASSERT(rv == 0);
710facf4a8dSllai 	/* rebuild directory content */
711facf4a8dSllai 	dir->sdev_flags |= SDEV_BUILD;
712facf4a8dSllai 
713facf4a8dSllai 	if ((type == PROFILE_TYPE_INCLUDE) &&
714facf4a8dSllai 	    (strpbrk(name, "*?[]") != NULL)) {
715facf4a8dSllai 			dir->sdev_prof.has_glob = 1;
716facf4a8dSllai 	}
717facf4a8dSllai 
718facf4a8dSllai 	rw_exit(&dir->sdev_contents);
719facf4a8dSllai 
720facf4a8dSllai 	/* additional details for glob pattern and exclusion */
721facf4a8dSllai 	switch (type) {
722facf4a8dSllai 	case PROFILE_TYPE_INCLUDE:
723facf4a8dSllai 	case PROFILE_TYPE_EXCLUDE:
724facf4a8dSllai 		apply_dir_pattern(dir, name, tgt, type);
725facf4a8dSllai 		break;
726facf4a8dSllai 	};
727facf4a8dSllai }
728facf4a8dSllai 
729facf4a8dSllai /*
730facf4a8dSllai  * Parse path components and apply requested matching rule at
731facf4a8dSllai  * directory level.
732facf4a8dSllai  */
733facf4a8dSllai static void
734facf4a8dSllai process_rule(struct sdev_node *dir, struct sdev_node *gdir,
735facf4a8dSllai     char *path, char *tgt, int type)
736facf4a8dSllai {
737facf4a8dSllai 	char *name;
738facf4a8dSllai 	struct pathname	pn;
739facf4a8dSllai 	int rv = 0;
740facf4a8dSllai 
741facf4a8dSllai 	if ((strlen(path) > 5) && (strncmp(path, "/dev/", 5) == 0)) {
742facf4a8dSllai 		path += 5;
743facf4a8dSllai 	}
744facf4a8dSllai 
745facf4a8dSllai 	if (pn_get(path, UIO_SYSSPACE, &pn) != 0)
746facf4a8dSllai 		return;
747facf4a8dSllai 
748facf4a8dSllai 	name = kmem_alloc(MAXPATHLEN, KM_SLEEP);
749facf4a8dSllai 	(void) pn_getcomponent(&pn, name);
750facf4a8dSllai 	pn_skipslash(&pn);
751facf4a8dSllai 	SDEV_HOLD(dir);
752facf4a8dSllai 
753facf4a8dSllai 	while (pn_pathleft(&pn)) {
754facf4a8dSllai 		/* If this is pattern, just add the pattern */
755facf4a8dSllai 		if (strpbrk(name, "*?[]") != NULL &&
756facf4a8dSllai 		    (type == PROFILE_TYPE_INCLUDE ||
757facf4a8dSllai 		    type == PROFILE_TYPE_EXCLUDE)) {
758facf4a8dSllai 			ASSERT(tgt == NULL);
759facf4a8dSllai 			tgt = pn.pn_path;
760facf4a8dSllai 			break;
761facf4a8dSllai 		}
762facf4a8dSllai 		if ((rv = prof_make_dir(name, &gdir, &dir)) != 0) {
763facf4a8dSllai 			cmn_err(CE_CONT, "process_rule: %s error %d\n",
764facf4a8dSllai 			    path, rv);
765facf4a8dSllai 			break;
766facf4a8dSllai 		}
767facf4a8dSllai 		(void) pn_getcomponent(&pn, name);
768facf4a8dSllai 		pn_skipslash(&pn);
769facf4a8dSllai 	}
770facf4a8dSllai 
771facf4a8dSllai 	/* process the leaf component */
772facf4a8dSllai 	if (rv == 0) {
773facf4a8dSllai 		prof_add_rule(name, tgt, dir, type);
774facf4a8dSllai 		SDEV_SIMPLE_RELE(dir);
775facf4a8dSllai 	}
776facf4a8dSllai 
777facf4a8dSllai 	kmem_free(name, MAXPATHLEN);
778facf4a8dSllai 	pn_free(&pn);
779facf4a8dSllai }
780facf4a8dSllai 
781facf4a8dSllai static int
782facf4a8dSllai copyin_nvlist(char *packed_usr, size_t packed_sz, nvlist_t **nvlp)
783facf4a8dSllai {
784facf4a8dSllai 	int err = 0;
785facf4a8dSllai 	char *packed;
786facf4a8dSllai 	nvlist_t *profile = NULL;
787facf4a8dSllai 
788facf4a8dSllai 	/* simple sanity check */
789facf4a8dSllai 	if (packed_usr == NULL || packed_sz == 0)
790facf4a8dSllai 		return (NULL);
791facf4a8dSllai 
792facf4a8dSllai 	/* copyin packed profile nvlist */
793facf4a8dSllai 	packed = kmem_alloc(packed_sz, KM_NOSLEEP);
794facf4a8dSllai 	if (packed == NULL)
795facf4a8dSllai 		return (ENOMEM);
796facf4a8dSllai 	err = copyin(packed_usr, packed, packed_sz);
797facf4a8dSllai 
798facf4a8dSllai 	/* unpack packed profile nvlist */
799facf4a8dSllai 	if (err)
800facf4a8dSllai 		cmn_err(CE_WARN, "copyin_nvlist: copyin failed with "
801facf4a8dSllai 		    "err %d\n", err);
802facf4a8dSllai 	else if (err = nvlist_unpack(packed, packed_sz, &profile, KM_NOSLEEP))
803facf4a8dSllai 		cmn_err(CE_WARN, "copyin_nvlist: nvlist_unpack "
804facf4a8dSllai 		    "failed with err %d\n", err);
805facf4a8dSllai 
806facf4a8dSllai 	kmem_free(packed, packed_sz);
807facf4a8dSllai 	if (err == 0)
808facf4a8dSllai 		*nvlp = profile;
809facf4a8dSllai 	return (err);
810facf4a8dSllai }
811facf4a8dSllai 
812facf4a8dSllai /*
813facf4a8dSllai  * Process profile passed down from libdevinfo. There are four types
814facf4a8dSllai  * of matching rules:
815facf4a8dSllai  *  include: export a name or names matching a pattern
816facf4a8dSllai  *  exclude: exclude a name or names matching a pattern
817facf4a8dSllai  *  symlink: create a local symlink
818facf4a8dSllai  *  map:     export a device with a name different from the global zone
819facf4a8dSllai  * Note: We may consider supporting VOP_SYMLINK in non-global instances,
820facf4a8dSllai  *	because it does not present any security risk. For now, the fs
821facf4a8dSllai  *	instance is read only.
822facf4a8dSllai  */
823facf4a8dSllai static void
824facf4a8dSllai sdev_process_profile(struct sdev_data *sdev_data, nvlist_t *profile)
825facf4a8dSllai {
826facf4a8dSllai 	nvpair_t *nvpair;
827facf4a8dSllai 	char *nvname, *dname;
828facf4a8dSllai 	struct sdev_node *dir, *gdir;
829facf4a8dSllai 	char **pair;				/* for symlinks and maps */
830facf4a8dSllai 	uint_t nelem;
831facf4a8dSllai 	int rv;
832facf4a8dSllai 
833facf4a8dSllai 	gdir = sdev_origins->sdev_root;	/* root of global /dev */
834facf4a8dSllai 	dir = sdev_data->sdev_root;	/* root of current instance */
835facf4a8dSllai 
836facf4a8dSllai 	ASSERT(profile);
837facf4a8dSllai 
838facf4a8dSllai 	/* process nvpairs in the list */
839facf4a8dSllai 	nvpair = NULL;
840facf4a8dSllai 	while (nvpair = nvlist_next_nvpair(profile, nvpair)) {
841facf4a8dSllai 		nvname = nvpair_name(nvpair);
842facf4a8dSllai 		ASSERT(nvname != NULL);
843facf4a8dSllai 
844facf4a8dSllai 		if (strcmp(nvname, SDEV_NVNAME_INCLUDE) == 0) {
845facf4a8dSllai 			rv = nvpair_value_string(nvpair, &dname);
846facf4a8dSllai 			if (rv != 0) {
847facf4a8dSllai 				cmn_err(CE_WARN, sdev_nvp_val_err,
848facf4a8dSllai 				    rv, nvpair_name(nvpair));
849facf4a8dSllai 				break;
850facf4a8dSllai 			}
851facf4a8dSllai 			process_rule(dir, gdir, dname, NULL,
852facf4a8dSllai 			    PROFILE_TYPE_INCLUDE);
853facf4a8dSllai 		} else if (strcmp(nvname, SDEV_NVNAME_EXCLUDE) == 0) {
854facf4a8dSllai 			rv = nvpair_value_string(nvpair, &dname);
855facf4a8dSllai 			if (rv != 0) {
856facf4a8dSllai 				cmn_err(CE_WARN, sdev_nvp_val_err,
857facf4a8dSllai 				    rv, nvpair_name(nvpair));
858facf4a8dSllai 				break;
859facf4a8dSllai 			}
860facf4a8dSllai 			process_rule(dir, gdir, dname, NULL,
861facf4a8dSllai 			    PROFILE_TYPE_EXCLUDE);
862facf4a8dSllai 		} else if (strcmp(nvname, SDEV_NVNAME_SYMLINK) == 0) {
863facf4a8dSllai 			rv = nvpair_value_string_array(nvpair, &pair, &nelem);
864facf4a8dSllai 			if (rv != 0) {
865facf4a8dSllai 				cmn_err(CE_WARN, sdev_nvp_val_err,
866facf4a8dSllai 				    rv, nvpair_name(nvpair));
867facf4a8dSllai 				break;
868facf4a8dSllai 			}
869facf4a8dSllai 			ASSERT(nelem == 2);
870facf4a8dSllai 			process_rule(dir, gdir, pair[0], pair[1],
871facf4a8dSllai 			    PROFILE_TYPE_SYMLINK);
872facf4a8dSllai 		} else if (strcmp(nvname, SDEV_NVNAME_MAP) == 0) {
873facf4a8dSllai 			rv = nvpair_value_string_array(nvpair, &pair, &nelem);
874facf4a8dSllai 			if (rv != 0) {
875facf4a8dSllai 				cmn_err(CE_WARN, sdev_nvp_val_err,
876facf4a8dSllai 				    rv, nvpair_name(nvpair));
877facf4a8dSllai 				break;
878facf4a8dSllai 			}
879facf4a8dSllai 			process_rule(dir, gdir, pair[1], pair[0],
880facf4a8dSllai 			    PROFILE_TYPE_MAP);
881facf4a8dSllai 		} else if (strcmp(nvname, SDEV_NVNAME_MOUNTPT) != 0) {
882facf4a8dSllai 			cmn_err(CE_WARN, "sdev_process_profile: invalid "
883facf4a8dSllai 			    "nvpair %s\n", nvname);
884facf4a8dSllai 		}
885facf4a8dSllai 	}
886facf4a8dSllai }
887facf4a8dSllai 
888facf4a8dSllai /*ARGSUSED*/
889facf4a8dSllai int
890facf4a8dSllai prof_lookup(vnode_t *dvp, char *nm, struct vnode **vpp, struct cred *cred)
891facf4a8dSllai {
892facf4a8dSllai 	struct sdev_node *ddv = VTOSDEV(dvp);
893facf4a8dSllai 	struct sdev_node *dv;
894facf4a8dSllai 	int nmlen;
895facf4a8dSllai 
896facf4a8dSllai 	/*
897facf4a8dSllai 	 * Empty name or ., return node itself.
898facf4a8dSllai 	 */
899facf4a8dSllai 	nmlen = strlen(nm);
900facf4a8dSllai 	if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) {
901facf4a8dSllai 		*vpp = SDEVTOV(ddv);
902facf4a8dSllai 		VN_HOLD(*vpp);
903facf4a8dSllai 		return (0);
904facf4a8dSllai 	}
905facf4a8dSllai 
906facf4a8dSllai 	/*
907facf4a8dSllai 	 * .., return the parent directory
908facf4a8dSllai 	 */
909facf4a8dSllai 	if ((nmlen == 2) && (strcmp(nm, "..") == 0)) {
910facf4a8dSllai 		*vpp = SDEVTOV(ddv->sdev_dotdot);
911facf4a8dSllai 		VN_HOLD(*vpp);
912facf4a8dSllai 		return (0);
913facf4a8dSllai 	}
914facf4a8dSllai 
915facf4a8dSllai 	rw_enter(&ddv->sdev_contents, RW_READER);
916facf4a8dSllai 	dv = sdev_cache_lookup(ddv, nm);
917facf4a8dSllai 	if (dv == NULL) {
918facf4a8dSllai 		prof_filldir(ddv);
919facf4a8dSllai 		dv = sdev_cache_lookup(ddv, nm);
920facf4a8dSllai 	}
921facf4a8dSllai 	rw_exit(&ddv->sdev_contents);
922facf4a8dSllai 	if (dv == NULL) {
923facf4a8dSllai 		sdcmn_err10(("prof_lookup: %s not found\n", nm));
924facf4a8dSllai 		return (ENOENT);
925facf4a8dSllai 	}
926facf4a8dSllai 
927facf4a8dSllai 	return (sdev_to_vp(dv, vpp));
928facf4a8dSllai }
929facf4a8dSllai 
930facf4a8dSllai /*
931facf4a8dSllai  * This is invoked after a new filesystem is mounted to define the
932facf4a8dSllai  * name space. It is also invoked during normal system operation
933facf4a8dSllai  * to update the name space.
934facf4a8dSllai  *
935facf4a8dSllai  * Applications call di_prof_commit() in libdevinfo, which invokes
936facf4a8dSllai  * modctl(). modctl calls this function. The input is a packed nvlist.
937facf4a8dSllai  */
938facf4a8dSllai int
939facf4a8dSllai devname_profile_update(char *packed, size_t packed_sz)
940facf4a8dSllai {
941facf4a8dSllai 	char *mntpt;
942facf4a8dSllai 	nvlist_t *nvl;
943facf4a8dSllai 	nvpair_t *nvp;
944facf4a8dSllai 	struct sdev_data *mntinfo;
945facf4a8dSllai 	int err;
946facf4a8dSllai 	int rv;
947facf4a8dSllai 
948facf4a8dSllai 	nvl = NULL;
949facf4a8dSllai 	if ((err = copyin_nvlist(packed, packed_sz, &nvl)) != 0)
950facf4a8dSllai 		return (err);
951facf4a8dSllai 	ASSERT(nvl);
952facf4a8dSllai 
953facf4a8dSllai 	/* The first nvpair must be the mount point */
954facf4a8dSllai 	nvp = nvlist_next_nvpair(nvl, NULL);
955facf4a8dSllai 	if (strcmp(nvpair_name(nvp), SDEV_NVNAME_MOUNTPT) != 0) {
956facf4a8dSllai 		cmn_err(CE_NOTE,
957facf4a8dSllai 		    "devname_profile_update: mount point not specified");
958facf4a8dSllai 		nvlist_free(nvl);
959facf4a8dSllai 		return (EINVAL);
960facf4a8dSllai 	}
961facf4a8dSllai 
962facf4a8dSllai 	/* find the matching filesystem instance */
963facf4a8dSllai 	rv = nvpair_value_string(nvp, &mntpt);
964facf4a8dSllai 	if (rv != 0) {
965facf4a8dSllai 		cmn_err(CE_WARN, sdev_nvp_val_err,
966facf4a8dSllai 		    rv, nvpair_name(nvp));
967facf4a8dSllai 	} else {
968facf4a8dSllai 		mntinfo = sdev_find_mntinfo(mntpt);
969facf4a8dSllai 		if (mntinfo == NULL) {
970facf4a8dSllai 			cmn_err(CE_NOTE, "devname_profile_update: "
971facf4a8dSllai 			    " mount point %s not found", mntpt);
972facf4a8dSllai 			nvlist_free(nvl);
973facf4a8dSllai 			return (EINVAL);
974facf4a8dSllai 		}
975facf4a8dSllai 
976facf4a8dSllai 		/* now do the hardwork to process the profile */
977facf4a8dSllai 		sdev_process_profile(mntinfo, nvl);
978facf4a8dSllai 
979facf4a8dSllai 		sdev_mntinfo_rele(mntinfo);
980facf4a8dSllai 	}
981facf4a8dSllai 
982facf4a8dSllai 	nvlist_free(nvl);
983facf4a8dSllai 	return (0);
984facf4a8dSllai }
985