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