xref: /titanic_44/usr/src/uts/common/fs/dev/sdev_profile.c (revision 22253b45e469decdb988b799c90598f2652597cd)
1facf4a8dSllai1 /*
2facf4a8dSllai1  * CDDL HEADER START
3facf4a8dSllai1  *
4facf4a8dSllai1  * The contents of this file are subject to the terms of the
5facf4a8dSllai1  * Common Development and Distribution License (the "License").
6facf4a8dSllai1  * You may not use this file except in compliance with the License.
7facf4a8dSllai1  *
8facf4a8dSllai1  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9facf4a8dSllai1  * or http://www.opensolaris.org/os/licensing.
10facf4a8dSllai1  * See the License for the specific language governing permissions
11facf4a8dSllai1  * and limitations under the License.
12facf4a8dSllai1  *
13facf4a8dSllai1  * When distributing Covered Code, include this CDDL HEADER in each
14facf4a8dSllai1  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15facf4a8dSllai1  * If applicable, add the following below this CDDL HEADER, with the
16facf4a8dSllai1  * fields enclosed by brackets "[]" replaced with your own identifying
17facf4a8dSllai1  * information: Portions Copyright [yyyy] [name of copyright owner]
18facf4a8dSllai1  *
19facf4a8dSllai1  * CDDL HEADER END
20facf4a8dSllai1  */
21facf4a8dSllai1 
22facf4a8dSllai1 /*
230fbb751dSJohn Levon  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
24facf4a8dSllai1  */
25facf4a8dSllai1 
26facf4a8dSllai1 /*
27facf4a8dSllai1  * This file implements /dev filesystem operations for non-global
28facf4a8dSllai1  * instances. Three major entry points:
29facf4a8dSllai1  * devname_profile_update()
30facf4a8dSllai1  *   Update matching rules determining which names to export
31facf4a8dSllai1  * prof_readdir()
32facf4a8dSllai1  *   Return the list of exported names
33facf4a8dSllai1  * prof_lookup()
34facf4a8dSllai1  *   Implements lookup
35facf4a8dSllai1  */
36facf4a8dSllai1 
37facf4a8dSllai1 #include <sys/types.h>
38facf4a8dSllai1 #include <sys/param.h>
39facf4a8dSllai1 #include <sys/sysmacros.h>
40facf4a8dSllai1 #include <sys/vnode.h>
41facf4a8dSllai1 #include <sys/uio.h>
42facf4a8dSllai1 #include <sys/dirent.h>
43facf4a8dSllai1 #include <sys/pathname.h>
44facf4a8dSllai1 #include <sys/fs/dv_node.h>
45facf4a8dSllai1 #include <sys/fs/sdev_impl.h>
46facf4a8dSllai1 #include <sys/sunndi.h>
47facf4a8dSllai1 #include <sys/modctl.h>
48facf4a8dSllai1 
49facf4a8dSllai1 enum {
50facf4a8dSllai1 	PROFILE_TYPE_INCLUDE,
51facf4a8dSllai1 	PROFILE_TYPE_EXCLUDE,
52facf4a8dSllai1 	PROFILE_TYPE_MAP,
53facf4a8dSllai1 	PROFILE_TYPE_SYMLINK
54facf4a8dSllai1 };
55facf4a8dSllai1 
56facf4a8dSllai1 enum {
57facf4a8dSllai1 	WALK_DIR_CONTINUE = 0,
58facf4a8dSllai1 	WALK_DIR_TERMINATE
59facf4a8dSllai1 };
60facf4a8dSllai1 
61facf4a8dSllai1 static const char *sdev_nvp_val_err = "nvpair_value error %d, %s\n";
62facf4a8dSllai1 
63facf4a8dSllai1 static void process_rule(struct sdev_node *, struct sdev_node *,
64facf4a8dSllai1     char *, char *, int);
65facf4a8dSllai1 static void walk_dir(struct vnode *, void *, int (*)(char *, void *));
66facf4a8dSllai1 
67facf4a8dSllai1 static void
prof_getattr(struct sdev_node * dir,char * name,struct vnode * gdv,struct vattr * vap,struct vnode ** avpp,int * no_fs_perm)68facf4a8dSllai1 prof_getattr(struct sdev_node *dir, char *name, struct vnode *gdv,
69facf4a8dSllai1     struct vattr *vap, struct vnode **avpp, int *no_fs_perm)
70facf4a8dSllai1 {
71facf4a8dSllai1 	struct vnode *advp;
72facf4a8dSllai1 
73facf4a8dSllai1 	/* get attribute from shadow, if present; else get default */
74facf4a8dSllai1 	advp = dir->sdev_attrvp;
75da6c28aaSamw 	if (advp && VOP_LOOKUP(advp, name, avpp, NULL, 0, NULL, kcred,
76da6c28aaSamw 	    NULL, NULL, NULL) == 0) {
77da6c28aaSamw 		(void) VOP_GETATTR(*avpp, vap, 0, kcred, NULL);
78facf4a8dSllai1 	} else if (gdv == NULL || gdv->v_type == VDIR) {
79facf4a8dSllai1 		/* always create shadow directory */
80facf4a8dSllai1 		*vap = sdev_vattr_dir;
81da6c28aaSamw 		if (advp && VOP_MKDIR(advp, name, &sdev_vattr_dir,
82da6c28aaSamw 		    avpp, kcred, NULL, 0, NULL) != 0) {
83facf4a8dSllai1 			*avpp = NULLVP;
84facf4a8dSllai1 			sdcmn_err10(("prof_getattr: failed to create "
85facf4a8dSllai1 			    "shadow directory %s/%s\n", dir->sdev_path, name));
86facf4a8dSllai1 		}
87facf4a8dSllai1 	} else {
88facf4a8dSllai1 		/*
89facf4a8dSllai1 		 * get default permission from devfs
90facf4a8dSllai1 		 * Before calling devfs_get_defattr, we need to get
91facf4a8dSllai1 		 * the realvp (the dv_node). If realvp is not a dv_node,
92facf4a8dSllai1 		 * devfs_get_defattr() will return a system-wide default
93facf4a8dSllai1 		 * attr for device nodes.
94facf4a8dSllai1 		 */
95facf4a8dSllai1 		struct vnode *rvp;
96da6c28aaSamw 		if (VOP_REALVP(gdv, &rvp, NULL) != 0)
97facf4a8dSllai1 			rvp = gdv;
98facf4a8dSllai1 		devfs_get_defattr(rvp, vap, no_fs_perm);
99facf4a8dSllai1 		*avpp = NULLVP;
100facf4a8dSllai1 	}
101facf4a8dSllai1 
102facf4a8dSllai1 	/* ignore dev_t and vtype from backing store */
103facf4a8dSllai1 	if (gdv) {
104facf4a8dSllai1 		vap->va_type = gdv->v_type;
105facf4a8dSllai1 		vap->va_rdev = gdv->v_rdev;
106facf4a8dSllai1 	}
107facf4a8dSllai1 }
108facf4a8dSllai1 
109facf4a8dSllai1 static void
apply_glob_pattern(struct sdev_node * pdir,struct sdev_node * cdir)110facf4a8dSllai1 apply_glob_pattern(struct sdev_node *pdir, struct sdev_node *cdir)
111facf4a8dSllai1 {
112facf4a8dSllai1 	char *name;
113facf4a8dSllai1 	nvpair_t *nvp = NULL;
114facf4a8dSllai1 	nvlist_t *nvl;
115facf4a8dSllai1 	struct vnode *vp = SDEVTOV(cdir);
116facf4a8dSllai1 	int rv = 0;
117facf4a8dSllai1 
118facf4a8dSllai1 	if (vp->v_type != VDIR)
119facf4a8dSllai1 		return;
120facf4a8dSllai1 	name = cdir->sdev_name;
121facf4a8dSllai1 	nvl = pdir->sdev_prof.dev_glob_incdir;
122facf4a8dSllai1 	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
123facf4a8dSllai1 		char *pathleft;
124facf4a8dSllai1 		char *expr = nvpair_name(nvp);
125facf4a8dSllai1 		if (!gmatch(name, expr))
126facf4a8dSllai1 			continue;
127facf4a8dSllai1 		rv = nvpair_value_string(nvp, &pathleft);
128facf4a8dSllai1 		if (rv != 0) {
129facf4a8dSllai1 			cmn_err(CE_WARN, sdev_nvp_val_err,
130facf4a8dSllai1 			    rv, nvpair_name(nvp));
131facf4a8dSllai1 			break;
132facf4a8dSllai1 		}
133facf4a8dSllai1 		process_rule(cdir, cdir->sdev_origin,
134facf4a8dSllai1 		    pathleft, NULL, PROFILE_TYPE_INCLUDE);
135facf4a8dSllai1 	}
136facf4a8dSllai1 }
137facf4a8dSllai1 
138facf4a8dSllai1 /*
139facf4a8dSllai1  * Some commonality here with sdev_mknode(), could be simplified.
140facf4a8dSllai1  * NOTE: prof_mknode returns with *newdv held once, if success.
141facf4a8dSllai1  */
142facf4a8dSllai1 static int
prof_mknode(struct sdev_node * dir,char * name,struct sdev_node ** newdv,vattr_t * vap,vnode_t * avp,void * arg,cred_t * cred)143facf4a8dSllai1 prof_mknode(struct sdev_node *dir, char *name, struct sdev_node **newdv,
144facf4a8dSllai1     vattr_t *vap, vnode_t *avp, void *arg, cred_t *cred)
145facf4a8dSllai1 {
146facf4a8dSllai1 	struct sdev_node *dv;
147facf4a8dSllai1 	int rv;
148facf4a8dSllai1 
149facf4a8dSllai1 	ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
150facf4a8dSllai1 
151facf4a8dSllai1 	/* check cache first */
152facf4a8dSllai1 	if (dv = sdev_cache_lookup(dir, name)) {
153facf4a8dSllai1 		*newdv = dv;
154facf4a8dSllai1 		return (0);
155facf4a8dSllai1 	}
156facf4a8dSllai1 
157facf4a8dSllai1 	/* allocate node and insert into cache */
158facf4a8dSllai1 	rv = sdev_nodeinit(dir, name, &dv, NULL);
159facf4a8dSllai1 	if (rv != 0) {
160facf4a8dSllai1 		*newdv = NULL;
161facf4a8dSllai1 		return (rv);
162facf4a8dSllai1 	}
163facf4a8dSllai1 
1649e5aa9d8SRobert Mustacchi 	sdev_cache_update(dir, &dv, name, SDEV_CACHE_ADD);
165facf4a8dSllai1 	*newdv = dv;
166facf4a8dSllai1 
167facf4a8dSllai1 	/* put it in ready state */
168facf4a8dSllai1 	rv = sdev_nodeready(*newdv, vap, avp, arg, cred);
169facf4a8dSllai1 
170facf4a8dSllai1 	/* handle glob pattern in the middle of a path */
171facf4a8dSllai1 	if (rv == 0) {
172facf4a8dSllai1 		if (SDEVTOV(*newdv)->v_type == VDIR)
173facf4a8dSllai1 			sdcmn_err10(("sdev_origin for %s set to 0x%p\n",
174facf4a8dSllai1 			    name, arg));
175facf4a8dSllai1 		apply_glob_pattern(dir, *newdv);
1769e5aa9d8SRobert Mustacchi 	} else {
1779e5aa9d8SRobert Mustacchi 		sdev_cache_update(dir, &dv, name, SDEV_CACHE_DELETE);
1789e5aa9d8SRobert Mustacchi 		SDEV_RELE(dv);
179facf4a8dSllai1 	}
180facf4a8dSllai1 	return (rv);
181facf4a8dSllai1 }
182facf4a8dSllai1 
183facf4a8dSllai1 /*
184facf4a8dSllai1  * Create a directory node in a non-global dev instance.
185facf4a8dSllai1  * Always create shadow vnode. Set sdev_origin to the corresponding
186facf4a8dSllai1  * global directory sdev_node if it exists. This facilitates the
187facf4a8dSllai1  * lookup operation.
188facf4a8dSllai1  */
189facf4a8dSllai1 static int
prof_make_dir(char * name,struct sdev_node ** gdirp,struct sdev_node ** dirp)190facf4a8dSllai1 prof_make_dir(char *name, struct sdev_node **gdirp, struct sdev_node **dirp)
191facf4a8dSllai1 {
192facf4a8dSllai1 	struct sdev_node *dir = *dirp;
193facf4a8dSllai1 	struct sdev_node *gdir = *gdirp;
194facf4a8dSllai1 	struct sdev_node *newdv;
195facf4a8dSllai1 	struct vnode *avp, *gnewdir = NULL;
196facf4a8dSllai1 	struct vattr vattr;
197facf4a8dSllai1 	int error;
198facf4a8dSllai1 
199facf4a8dSllai1 	/* see if name already exists */
200facf4a8dSllai1 	rw_enter(&dir->sdev_contents, RW_READER);
201facf4a8dSllai1 	if (newdv = sdev_cache_lookup(dir, name)) {
202facf4a8dSllai1 		*dirp = newdv;
203facf4a8dSllai1 		*gdirp = newdv->sdev_origin;
204facf4a8dSllai1 		rw_exit(&dir->sdev_contents);
2059e5aa9d8SRobert Mustacchi 		SDEV_RELE(dir);
206facf4a8dSllai1 		return (0);
207facf4a8dSllai1 	}
208facf4a8dSllai1 	rw_exit(&dir->sdev_contents);
209facf4a8dSllai1 
210facf4a8dSllai1 	/* find corresponding dir node in global dev */
211facf4a8dSllai1 	if (gdir) {
212facf4a8dSllai1 		error = VOP_LOOKUP(SDEVTOV(gdir), name, &gnewdir,
213da6c28aaSamw 		    NULL, 0, NULL, kcred, NULL, NULL, NULL);
214facf4a8dSllai1 		if (error == 0) {
215facf4a8dSllai1 			*gdirp = VTOSDEV(gnewdir);
216facf4a8dSllai1 		} else { 	/* it's ok if there no global dir */
217facf4a8dSllai1 			*gdirp = NULL;
218facf4a8dSllai1 		}
219facf4a8dSllai1 	}
220facf4a8dSllai1 
221facf4a8dSllai1 	/* get attribute from shadow, also create shadow dir */
222facf4a8dSllai1 	prof_getattr(dir, name, gnewdir, &vattr, &avp, NULL);
223facf4a8dSllai1 
224facf4a8dSllai1 	/* create dev directory vnode */
225facf4a8dSllai1 	rw_enter(&dir->sdev_contents, RW_WRITER);
226facf4a8dSllai1 	error = prof_mknode(dir, name, &newdv, &vattr, avp, (void *)*gdirp,
227facf4a8dSllai1 	    kcred);
228facf4a8dSllai1 	rw_exit(&dir->sdev_contents);
229facf4a8dSllai1 	if (error == 0) {
230facf4a8dSllai1 		ASSERT(newdv);
231facf4a8dSllai1 		*dirp = newdv;
232facf4a8dSllai1 	}
233facf4a8dSllai1 	SDEV_RELE(dir);
234facf4a8dSllai1 	return (error);
235facf4a8dSllai1 }
236facf4a8dSllai1 
237facf4a8dSllai1 /*
238facf4a8dSllai1  * Look up a logical name in the global zone.
239facf4a8dSllai1  * Provides the ability to map the global zone's device name
240facf4a8dSllai1  * to an alternate name within a zone.  The primary example
241facf4a8dSllai1  * is the virtual console device /dev/zcons/[zonename]/zconsole
242facf4a8dSllai1  * mapped to /[zonename]/root/dev/zconsole.
243facf4a8dSllai1  */
244facf4a8dSllai1 static void
prof_lookup_globaldev(struct sdev_node * dir,struct sdev_node * gdir,char * name,char * rename)245facf4a8dSllai1 prof_lookup_globaldev(struct sdev_node *dir, struct sdev_node *gdir,
246facf4a8dSllai1     char *name, char *rename)
247facf4a8dSllai1 {
248facf4a8dSllai1 	int error;
249facf4a8dSllai1 	struct vnode *avp, *gdv, *gddv;
250facf4a8dSllai1 	struct sdev_node *newdv;
251facf4a8dSllai1 	struct vattr vattr = {0};
252facf4a8dSllai1 	struct pathname pn;
253facf4a8dSllai1 
254facf4a8dSllai1 	/* check if node already exists */
255facf4a8dSllai1 	newdv = sdev_cache_lookup(dir, rename);
256facf4a8dSllai1 	if (newdv) {
257facf4a8dSllai1 		ASSERT(newdv->sdev_state != SDEV_ZOMBIE);
258facf4a8dSllai1 		SDEV_SIMPLE_RELE(newdv);
259facf4a8dSllai1 		return;
260facf4a8dSllai1 	}
261facf4a8dSllai1 
262facf4a8dSllai1 	/* sanity check arguments */
263facf4a8dSllai1 	if (!gdir || pn_get(name, UIO_SYSSPACE, &pn))
264facf4a8dSllai1 		return;
265facf4a8dSllai1 
266facf4a8dSllai1 	/* perform a relative lookup of the global /dev instance */
267facf4a8dSllai1 	gddv = SDEVTOV(gdir);
268facf4a8dSllai1 	VN_HOLD(gddv);
269facf4a8dSllai1 	error = lookuppnvp(&pn, NULL, FOLLOW, NULLVPP, &gdv,
270facf4a8dSllai1 	    rootdir, gddv, kcred);
271facf4a8dSllai1 	pn_free(&pn);
272facf4a8dSllai1 	if (error) {
273facf4a8dSllai1 		sdcmn_err10(("prof_lookup_globaldev: %s not found\n", name));
274facf4a8dSllai1 		return;
275facf4a8dSllai1 	}
276facf4a8dSllai1 	ASSERT(gdv && gdv->v_type != VLNK);
277facf4a8dSllai1 
278facf4a8dSllai1 	/*
279facf4a8dSllai1 	 * Found the entry in global /dev, figure out attributes
280facf4a8dSllai1 	 * by looking at backing store. Call into devfs for default.
281a83b1f2cSllai1 	 * Note, mapped device is persisted under the new name
282facf4a8dSllai1 	 */
283a83b1f2cSllai1 	prof_getattr(dir, rename, gdv, &vattr, &avp, NULL);
284facf4a8dSllai1 
285facf4a8dSllai1 	if (gdv->v_type != VDIR) {
286facf4a8dSllai1 		VN_RELE(gdv);
287facf4a8dSllai1 		gdir = NULL;
288facf4a8dSllai1 	} else
289facf4a8dSllai1 		gdir = VTOSDEV(gdv);
290facf4a8dSllai1 
291facf4a8dSllai1 	if (prof_mknode(dir, rename, &newdv, &vattr, avp,
292facf4a8dSllai1 	    (void *)gdir, kcred) == 0) {
293facf4a8dSllai1 		ASSERT(newdv->sdev_state != SDEV_ZOMBIE);
294facf4a8dSllai1 		SDEV_SIMPLE_RELE(newdv);
295facf4a8dSllai1 	}
296facf4a8dSllai1 }
297facf4a8dSllai1 
298facf4a8dSllai1 static void
prof_make_sym(struct sdev_node * dir,char * lnm,char * tgt)299facf4a8dSllai1 prof_make_sym(struct sdev_node *dir, char *lnm, char *tgt)
300facf4a8dSllai1 {
301facf4a8dSllai1 	struct sdev_node *newdv;
302facf4a8dSllai1 
303facf4a8dSllai1 	if (prof_mknode(dir, lnm, &newdv, &sdev_vattr_lnk, NULL,
304facf4a8dSllai1 	    (void *)tgt, kcred) == 0) {
305facf4a8dSllai1 		ASSERT(newdv->sdev_state != SDEV_ZOMBIE);
306facf4a8dSllai1 		SDEV_SIMPLE_RELE(newdv);
307facf4a8dSllai1 	}
308facf4a8dSllai1 }
309facf4a8dSllai1 
310facf4a8dSllai1 /*
311facf4a8dSllai1  * Create symlinks in the current directory based on profile
312facf4a8dSllai1  */
313facf4a8dSllai1 static void
prof_make_symlinks(struct sdev_node * dir)314facf4a8dSllai1 prof_make_symlinks(struct sdev_node *dir)
315facf4a8dSllai1 {
316facf4a8dSllai1 	char *tgt, *lnm;
317facf4a8dSllai1 	nvpair_t *nvp = NULL;
318facf4a8dSllai1 	nvlist_t *nvl = dir->sdev_prof.dev_symlink;
319facf4a8dSllai1 	int rv;
320facf4a8dSllai1 
321facf4a8dSllai1 	ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
322facf4a8dSllai1 
323facf4a8dSllai1 	if (nvl == NULL)
324facf4a8dSllai1 		return;
325facf4a8dSllai1 
326facf4a8dSllai1 	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
327facf4a8dSllai1 		lnm = nvpair_name(nvp);
328facf4a8dSllai1 		rv = nvpair_value_string(nvp, &tgt);
329facf4a8dSllai1 		if (rv != 0) {
330facf4a8dSllai1 			cmn_err(CE_WARN, sdev_nvp_val_err,
331facf4a8dSllai1 			    rv, nvpair_name(nvp));
332facf4a8dSllai1 			break;
333facf4a8dSllai1 		}
334facf4a8dSllai1 		prof_make_sym(dir, lnm, tgt);
335facf4a8dSllai1 	}
336facf4a8dSllai1 }
337facf4a8dSllai1 
338facf4a8dSllai1 static void
prof_make_maps(struct sdev_node * dir)339facf4a8dSllai1 prof_make_maps(struct sdev_node *dir)
340facf4a8dSllai1 {
341facf4a8dSllai1 	nvpair_t *nvp = NULL;
342facf4a8dSllai1 	nvlist_t *nvl = dir->sdev_prof.dev_map;
343facf4a8dSllai1 	int rv;
344facf4a8dSllai1 
345facf4a8dSllai1 	ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
346facf4a8dSllai1 
347facf4a8dSllai1 	if (nvl == NULL)
348facf4a8dSllai1 		return;
349facf4a8dSllai1 
350facf4a8dSllai1 	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
351facf4a8dSllai1 		char *name;
352facf4a8dSllai1 		char *rename = nvpair_name(nvp);
353facf4a8dSllai1 		rv = nvpair_value_string(nvp, &name);
354facf4a8dSllai1 		if (rv != 0) {
355facf4a8dSllai1 			cmn_err(CE_WARN, sdev_nvp_val_err,
356facf4a8dSllai1 			    rv, nvpair_name(nvp));
357facf4a8dSllai1 			break;
358facf4a8dSllai1 		}
359facf4a8dSllai1 		sdcmn_err10(("map %s -> %s\n", name, rename));
360facf4a8dSllai1 		(void) prof_lookup_globaldev(dir, sdev_origins->sdev_root,
361facf4a8dSllai1 		    name, rename);
362facf4a8dSllai1 	}
363facf4a8dSllai1 }
364facf4a8dSllai1 
365facf4a8dSllai1 struct match_arg {
366facf4a8dSllai1 	char *expr;
367facf4a8dSllai1 	int match;
368facf4a8dSllai1 };
369facf4a8dSllai1 
370facf4a8dSllai1 static int
match_name(char * name,void * arg)371facf4a8dSllai1 match_name(char *name, void *arg)
372facf4a8dSllai1 {
373facf4a8dSllai1 	struct match_arg *margp = (struct match_arg *)arg;
374facf4a8dSllai1 
375facf4a8dSllai1 	if (gmatch(name, margp->expr)) {
376facf4a8dSllai1 		margp->match = 1;
377facf4a8dSllai1 		return (WALK_DIR_TERMINATE);
378facf4a8dSllai1 	}
379facf4a8dSllai1 	return (WALK_DIR_CONTINUE);
380facf4a8dSllai1 }
381facf4a8dSllai1 
382facf4a8dSllai1 static int
is_nonempty_dir(char * name,char * pathleft,struct sdev_node * dir)383facf4a8dSllai1 is_nonempty_dir(char *name, char *pathleft, struct sdev_node *dir)
384facf4a8dSllai1 {
385facf4a8dSllai1 	struct match_arg marg;
386facf4a8dSllai1 	struct pathname pn;
387facf4a8dSllai1 	struct vnode *gvp;
388facf4a8dSllai1 	struct sdev_node *gdir = dir->sdev_origin;
389facf4a8dSllai1 
390da6c28aaSamw 	if (VOP_LOOKUP(SDEVTOV(gdir), name, &gvp, NULL, 0, NULL, kcred,
391da6c28aaSamw 	    NULL, NULL, NULL) != 0)
392facf4a8dSllai1 		return (0);
393facf4a8dSllai1 
394facf4a8dSllai1 	if (gvp->v_type != VDIR) {
395facf4a8dSllai1 		VN_RELE(gvp);
396facf4a8dSllai1 		return (0);
397facf4a8dSllai1 	}
398facf4a8dSllai1 
399facf4a8dSllai1 	if (pn_get(pathleft, UIO_SYSSPACE, &pn) != 0) {
400facf4a8dSllai1 		VN_RELE(gvp);
401facf4a8dSllai1 		return (0);
402facf4a8dSllai1 	}
403facf4a8dSllai1 
404facf4a8dSllai1 	marg.expr = kmem_alloc(MAXNAMELEN, KM_SLEEP);
405facf4a8dSllai1 	(void) pn_getcomponent(&pn, marg.expr);
406facf4a8dSllai1 	marg.match = 0;
407facf4a8dSllai1 
408facf4a8dSllai1 	walk_dir(gvp, &marg, match_name);
409facf4a8dSllai1 	VN_RELE(gvp);
410facf4a8dSllai1 	kmem_free(marg.expr, MAXNAMELEN);
411facf4a8dSllai1 	pn_free(&pn);
412facf4a8dSllai1 
413facf4a8dSllai1 	return (marg.match);
414facf4a8dSllai1 }
415facf4a8dSllai1 
416facf4a8dSllai1 
417facf4a8dSllai1 /* Check if name passes matching rules */
418facf4a8dSllai1 static int
prof_name_matched(char * name,struct sdev_node * dir)419facf4a8dSllai1 prof_name_matched(char *name, struct sdev_node *dir)
420facf4a8dSllai1 {
421facf4a8dSllai1 	int type, match = 0;
422facf4a8dSllai1 	char *expr;
423facf4a8dSllai1 	nvlist_t *nvl;
424facf4a8dSllai1 	nvpair_t *nvp = NULL;
425facf4a8dSllai1 	int rv;
426facf4a8dSllai1 
427facf4a8dSllai1 	/* check against nvlist for leaf include/exclude */
428facf4a8dSllai1 	nvl = dir->sdev_prof.dev_name;
429facf4a8dSllai1 	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
430facf4a8dSllai1 		expr = nvpair_name(nvp);
431facf4a8dSllai1 		rv = nvpair_value_int32(nvp, &type);
432facf4a8dSllai1 		if (rv != 0) {
433facf4a8dSllai1 			cmn_err(CE_WARN, sdev_nvp_val_err,
434facf4a8dSllai1 			    rv, nvpair_name(nvp));
435facf4a8dSllai1 			break;
436facf4a8dSllai1 		}
437facf4a8dSllai1 
438facf4a8dSllai1 		if (type == PROFILE_TYPE_EXCLUDE) {
439facf4a8dSllai1 			if (gmatch(name, expr))
440facf4a8dSllai1 				return (0);	/* excluded */
441facf4a8dSllai1 		} else if (!match) {
442facf4a8dSllai1 			match = gmatch(name, expr);
443facf4a8dSllai1 		}
444facf4a8dSllai1 	}
445facf4a8dSllai1 	if (match) {
446facf4a8dSllai1 		sdcmn_err10(("prof_name_matched: %s\n", name));
447facf4a8dSllai1 		return (match);
448facf4a8dSllai1 	}
449facf4a8dSllai1 
450facf4a8dSllai1 	/* check for match against directory globbing pattern */
451facf4a8dSllai1 	nvl = dir->sdev_prof.dev_glob_incdir;
452facf4a8dSllai1 	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
453facf4a8dSllai1 		char *pathleft;
454facf4a8dSllai1 		expr = nvpair_name(nvp);
455facf4a8dSllai1 		if (gmatch(name, expr) == 0)
456facf4a8dSllai1 			continue;
457facf4a8dSllai1 		rv = nvpair_value_string(nvp, &pathleft);
458facf4a8dSllai1 		if (rv != 0) {
459facf4a8dSllai1 			cmn_err(CE_WARN, sdev_nvp_val_err,
460facf4a8dSllai1 			    rv, nvpair_name(nvp));
461facf4a8dSllai1 			break;
462facf4a8dSllai1 		}
463facf4a8dSllai1 		if (is_nonempty_dir(name, pathleft, dir)) {
464facf4a8dSllai1 			sdcmn_err10(("prof_name_matched: dir %s\n", name));
465facf4a8dSllai1 			return (1);
466facf4a8dSllai1 		}
467facf4a8dSllai1 	}
468facf4a8dSllai1 
469facf4a8dSllai1 	return (0);
470facf4a8dSllai1 }
471facf4a8dSllai1 
472facf4a8dSllai1 static void
walk_dir(struct vnode * dvp,void * arg,int (* callback)(char *,void *))473facf4a8dSllai1 walk_dir(struct vnode *dvp, void *arg, int (*callback)(char *, void *))
474facf4a8dSllai1 {
475facf4a8dSllai1 	char    *nm;
476facf4a8dSllai1 	int eof, error;
477facf4a8dSllai1 	struct iovec iov;
478facf4a8dSllai1 	struct uio uio;
479facf4a8dSllai1 	struct dirent64 *dp;
480facf4a8dSllai1 	dirent64_t *dbuf;
481facf4a8dSllai1 	size_t dbuflen, dlen;
482facf4a8dSllai1 
483facf4a8dSllai1 	ASSERT(dvp);
484facf4a8dSllai1 
485facf4a8dSllai1 	dlen = 4096;
486facf4a8dSllai1 	dbuf = kmem_zalloc(dlen, KM_SLEEP);
487facf4a8dSllai1 
488facf4a8dSllai1 	uio.uio_iov = &iov;
489facf4a8dSllai1 	uio.uio_iovcnt = 1;
490facf4a8dSllai1 	uio.uio_segflg = UIO_SYSSPACE;
491facf4a8dSllai1 	uio.uio_fmode = 0;
492facf4a8dSllai1 	uio.uio_extflg = UIO_COPY_CACHED;
493facf4a8dSllai1 	uio.uio_loffset = 0;
494facf4a8dSllai1 	uio.uio_llimit = MAXOFFSET_T;
495facf4a8dSllai1 
496facf4a8dSllai1 	eof = 0;
497facf4a8dSllai1 	error = 0;
498facf4a8dSllai1 	while (!error && !eof) {
499facf4a8dSllai1 		uio.uio_resid = dlen;
500facf4a8dSllai1 		iov.iov_base = (char *)dbuf;
501facf4a8dSllai1 		iov.iov_len = dlen;
502facf4a8dSllai1 		(void) VOP_RWLOCK(dvp, V_WRITELOCK_FALSE, NULL);
503da6c28aaSamw 		error = VOP_READDIR(dvp, &uio, kcred, &eof, NULL, 0);
504facf4a8dSllai1 		VOP_RWUNLOCK(dvp, V_WRITELOCK_FALSE, NULL);
505facf4a8dSllai1 
506facf4a8dSllai1 		dbuflen = dlen - uio.uio_resid;
507facf4a8dSllai1 		if (error || dbuflen == 0)
508facf4a8dSllai1 			break;
509facf4a8dSllai1 		for (dp = dbuf; ((intptr_t)dp <
510facf4a8dSllai1 		    (intptr_t)dbuf + dbuflen);
511facf4a8dSllai1 		    dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) {
512facf4a8dSllai1 			nm = dp->d_name;
513facf4a8dSllai1 
514facf4a8dSllai1 			if (strcmp(nm, ".") == 0 ||
515facf4a8dSllai1 			    strcmp(nm, "..") == 0)
516facf4a8dSllai1 				continue;
517facf4a8dSllai1 
518facf4a8dSllai1 			if (callback(nm, arg) == WALK_DIR_TERMINATE)
519facf4a8dSllai1 				goto end;
520facf4a8dSllai1 		}
521facf4a8dSllai1 	}
522facf4a8dSllai1 
523facf4a8dSllai1 end:
524facf4a8dSllai1 	kmem_free(dbuf, dlen);
525facf4a8dSllai1 }
526facf4a8dSllai1 
5270fbb751dSJohn Levon /*
5280fbb751dSJohn Levon  * Last chance for a zone to see a node.  If our parent dir is
5290fbb751dSJohn Levon  * SDEV_ZONED, then we look up the "zone" property for the node.  If the
5300fbb751dSJohn Levon  * property is found and matches the current zone name, we allow it.
5310fbb751dSJohn Levon  * Note that this isn't quite correct for the global zone peeking inside
5320fbb751dSJohn Levon  * a zone's /dev - for that to work, we'd have to have a per-dev-mount
5330fbb751dSJohn Levon  * zone ref squirreled away.
5340fbb751dSJohn Levon  */
535facf4a8dSllai1 static int
prof_zone_matched(char * name,struct sdev_node * dir)5360fbb751dSJohn Levon prof_zone_matched(char *name, struct sdev_node *dir)
5370fbb751dSJohn Levon {
5380fbb751dSJohn Levon 	vnode_t *gvn = SDEVTOV(dir->sdev_origin);
5390fbb751dSJohn Levon 	struct pathname pn;
5400fbb751dSJohn Levon 	vnode_t *vn = NULL;
5410fbb751dSJohn Levon 	char zonename[ZONENAME_MAX];
5420fbb751dSJohn Levon 	int znlen = ZONENAME_MAX;
5430fbb751dSJohn Levon 	int ret;
5440fbb751dSJohn Levon 
5450fbb751dSJohn Levon 	ASSERT((dir->sdev_flags & SDEV_ZONED) != 0);
5460fbb751dSJohn Levon 
5470fbb751dSJohn Levon 	sdcmn_err10(("sdev_node %p is zoned, looking for %s\n",
5480fbb751dSJohn Levon 	    (void *)dir, name));
5490fbb751dSJohn Levon 
5500fbb751dSJohn Levon 	if (pn_get(name, UIO_SYSSPACE, &pn))
5510fbb751dSJohn Levon 		return (0);
5520fbb751dSJohn Levon 
5530fbb751dSJohn Levon 	VN_HOLD(gvn);
5540fbb751dSJohn Levon 
5550fbb751dSJohn Levon 	ret = lookuppnvp(&pn, NULL, FOLLOW, NULLVPP, &vn, rootdir, gvn, kcred);
5560fbb751dSJohn Levon 
5570fbb751dSJohn Levon 	pn_free(&pn);
5580fbb751dSJohn Levon 
5590fbb751dSJohn Levon 	if (ret != 0) {
5600fbb751dSJohn Levon 		sdcmn_err10(("prof_zone_matched: %s not found\n", name));
5610fbb751dSJohn Levon 		return (0);
5620fbb751dSJohn Levon 	}
5630fbb751dSJohn Levon 
5640fbb751dSJohn Levon 	/*
5650fbb751dSJohn Levon 	 * VBLK doesn't matter, and the property name is in fact treated
5660fbb751dSJohn Levon 	 * as a const char *.
5670fbb751dSJohn Levon 	 */
5680fbb751dSJohn Levon 	ret = e_ddi_getlongprop_buf(vn->v_rdev, VBLK, (char *)"zone",
5690fbb751dSJohn Levon 	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, (caddr_t)zonename, &znlen);
5700fbb751dSJohn Levon 
5710fbb751dSJohn Levon 	VN_RELE(vn);
5720fbb751dSJohn Levon 
5730fbb751dSJohn Levon 	if (ret == DDI_PROP_NOT_FOUND) {
5740fbb751dSJohn Levon 		sdcmn_err10(("vnode %p: no zone prop\n", (void *)vn));
5750fbb751dSJohn Levon 		return (0);
5760fbb751dSJohn Levon 	} else if (ret != DDI_PROP_SUCCESS) {
5770fbb751dSJohn Levon 		sdcmn_err10(("vnode %p: zone prop error: %d\n",
5780fbb751dSJohn Levon 		    (void *)vn, ret));
5790fbb751dSJohn Levon 		return (0);
5800fbb751dSJohn Levon 	}
5810fbb751dSJohn Levon 
5820fbb751dSJohn Levon 	sdcmn_err10(("vnode %p zone prop: %s\n", (void *)vn, zonename));
5830fbb751dSJohn Levon 	return (strcmp(zonename, curproc->p_zone->zone_name) == 0);
5840fbb751dSJohn Levon }
5850fbb751dSJohn Levon 
5860fbb751dSJohn Levon static int
prof_make_name_glob(char * nm,void * arg)5870fbb751dSJohn Levon prof_make_name_glob(char *nm, void *arg)
588facf4a8dSllai1 {
589facf4a8dSllai1 	struct sdev_node *ddv = (struct sdev_node *)arg;
590facf4a8dSllai1 
591facf4a8dSllai1 	if (prof_name_matched(nm, ddv))
592facf4a8dSllai1 		prof_lookup_globaldev(ddv, ddv->sdev_origin, nm, nm);
5930fbb751dSJohn Levon 
5940fbb751dSJohn Levon 	return (WALK_DIR_CONTINUE);
5950fbb751dSJohn Levon }
5960fbb751dSJohn Levon 
5970fbb751dSJohn Levon static int
prof_make_name_zone(char * nm,void * arg)5980fbb751dSJohn Levon prof_make_name_zone(char *nm, void *arg)
5990fbb751dSJohn Levon {
6000fbb751dSJohn Levon 	struct sdev_node *ddv = (struct sdev_node *)arg;
6010fbb751dSJohn Levon 
6020fbb751dSJohn Levon 	if (prof_zone_matched(nm, ddv))
6030fbb751dSJohn Levon 		prof_lookup_globaldev(ddv, ddv->sdev_origin, nm, nm);
6040fbb751dSJohn Levon 
605facf4a8dSllai1 	return (WALK_DIR_CONTINUE);
606facf4a8dSllai1 }
607facf4a8dSllai1 
608facf4a8dSllai1 static void
prof_make_names_walk(struct sdev_node * ddv,int (* cb)(char *,void *))6090fbb751dSJohn Levon prof_make_names_walk(struct sdev_node *ddv, int (*cb)(char *, void *))
610facf4a8dSllai1 {
611facf4a8dSllai1 	struct sdev_node *gdir;
612facf4a8dSllai1 
613facf4a8dSllai1 	gdir = ddv->sdev_origin;
614facf4a8dSllai1 	if (gdir == NULL)
615facf4a8dSllai1 		return;
6160fbb751dSJohn Levon 	walk_dir(SDEVTOV(gdir), (void *)ddv, cb);
617facf4a8dSllai1 }
618facf4a8dSllai1 
619facf4a8dSllai1 static void
prof_make_names(struct sdev_node * dir)620facf4a8dSllai1 prof_make_names(struct sdev_node *dir)
621facf4a8dSllai1 {
622facf4a8dSllai1 	char *name;
623facf4a8dSllai1 	nvpair_t *nvp = NULL;
624facf4a8dSllai1 	nvlist_t *nvl = dir->sdev_prof.dev_name;
625facf4a8dSllai1 	int rv;
626facf4a8dSllai1 
627facf4a8dSllai1 	ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
628facf4a8dSllai1 
6290fbb751dSJohn Levon 	if ((dir->sdev_flags & SDEV_ZONED) != 0)
6300fbb751dSJohn Levon 		prof_make_names_walk(dir, prof_make_name_zone);
6310fbb751dSJohn Levon 
632facf4a8dSllai1 	if (nvl == NULL)
633facf4a8dSllai1 		return;
634facf4a8dSllai1 
635facf4a8dSllai1 	if (dir->sdev_prof.has_glob) {
6360fbb751dSJohn Levon 		prof_make_names_walk(dir, prof_make_name_glob);
637facf4a8dSllai1 		return;
638facf4a8dSllai1 	}
639facf4a8dSllai1 
640facf4a8dSllai1 	/* Walk nvlist and lookup corresponding device in global inst */
641facf4a8dSllai1 	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
642facf4a8dSllai1 		int type;
643facf4a8dSllai1 		rv = nvpair_value_int32(nvp, &type);
644facf4a8dSllai1 		if (rv != 0) {
645facf4a8dSllai1 			cmn_err(CE_WARN, sdev_nvp_val_err,
646facf4a8dSllai1 			    rv, nvpair_name(nvp));
647facf4a8dSllai1 			break;
648facf4a8dSllai1 		}
649facf4a8dSllai1 		if (type == PROFILE_TYPE_EXCLUDE)
650facf4a8dSllai1 			continue;
651facf4a8dSllai1 		name = nvpair_name(nvp);
652facf4a8dSllai1 		(void) prof_lookup_globaldev(dir, dir->sdev_origin,
653facf4a8dSllai1 		    name, name);
654facf4a8dSllai1 	}
655facf4a8dSllai1 }
656facf4a8dSllai1 
657facf4a8dSllai1 /*
658*22253b45SAlexander Kolbasov  * Return True if directory cache is out of date and should be updated.
659*22253b45SAlexander Kolbasov  */
660*22253b45SAlexander Kolbasov static boolean_t
prof_dev_needupdate(sdev_node_t * ddv)661*22253b45SAlexander Kolbasov prof_dev_needupdate(sdev_node_t *ddv)
662*22253b45SAlexander Kolbasov {
663*22253b45SAlexander Kolbasov 	sdev_node_t *gdir = ddv->sdev_origin;
664*22253b45SAlexander Kolbasov 
665*22253b45SAlexander Kolbasov 	/*
666*22253b45SAlexander Kolbasov 	 * Caller can have either reader or writer lock
667*22253b45SAlexander Kolbasov 	 */
668*22253b45SAlexander Kolbasov 	ASSERT(RW_LOCK_HELD(&ddv->sdev_contents));
669*22253b45SAlexander Kolbasov 
670*22253b45SAlexander Kolbasov 	/*
671*22253b45SAlexander Kolbasov 	 * We need to rebuild the directory content if
672*22253b45SAlexander Kolbasov 	 * - ddv is not in a SDEV_ZOMBIE state
673*22253b45SAlexander Kolbasov 	 * - SDEV_BUILD is set OR
674*22253b45SAlexander Kolbasov 	 * - The device tree generation number has changed OR
675*22253b45SAlexander Kolbasov 	 * - The corresponding /dev namespace has been updated
676*22253b45SAlexander Kolbasov 	 */
677*22253b45SAlexander Kolbasov 	return ((ddv->sdev_state != SDEV_ZOMBIE) &&
678*22253b45SAlexander Kolbasov 	    (((ddv->sdev_flags & SDEV_BUILD) != 0) ||
679*22253b45SAlexander Kolbasov 	    (ddv->sdev_devtree_gen != devtree_gen) ||
680*22253b45SAlexander Kolbasov 	    ((gdir != NULL) &&
681*22253b45SAlexander Kolbasov 	    (ddv->sdev_ldir_gen != gdir->sdev_gdir_gen))));
682*22253b45SAlexander Kolbasov }
683*22253b45SAlexander Kolbasov 
684*22253b45SAlexander Kolbasov /*
685facf4a8dSllai1  * Build directory vnodes based on the profile and the global
686facf4a8dSllai1  * dev instance.
687facf4a8dSllai1  */
688facf4a8dSllai1 void
prof_filldir(sdev_node_t * ddv)689*22253b45SAlexander Kolbasov prof_filldir(sdev_node_t *ddv)
690facf4a8dSllai1 {
691*22253b45SAlexander Kolbasov 	sdev_node_t *gdir;
692facf4a8dSllai1 
693facf4a8dSllai1 	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
694facf4a8dSllai1 
695*22253b45SAlexander Kolbasov 	if (!prof_dev_needupdate(ddv)) {
696*22253b45SAlexander Kolbasov 		ASSERT(RW_READ_HELD(&ddv->sdev_contents));
6979e5aa9d8SRobert Mustacchi 		return;
698facf4a8dSllai1 	}
699*22253b45SAlexander Kolbasov 	/*
700*22253b45SAlexander Kolbasov 	 * Upgrade to writer lock
701*22253b45SAlexander Kolbasov 	 */
702*22253b45SAlexander Kolbasov 	if (rw_tryupgrade(&ddv->sdev_contents) == 0) {
703*22253b45SAlexander Kolbasov 		/*
704*22253b45SAlexander Kolbasov 		 * We need to drop the read lock and re-acquire it as a
705*22253b45SAlexander Kolbasov 		 * write lock. While we do this the condition may change so we
706*22253b45SAlexander Kolbasov 		 * need to re-check condition
707*22253b45SAlexander Kolbasov 		 */
708*22253b45SAlexander Kolbasov 		rw_exit(&ddv->sdev_contents);
709*22253b45SAlexander Kolbasov 		rw_enter(&ddv->sdev_contents, RW_WRITER);
710*22253b45SAlexander Kolbasov 		if (!prof_dev_needupdate(ddv)) {
711*22253b45SAlexander Kolbasov 			/* Downgrade back to the read lock before returning */
712*22253b45SAlexander Kolbasov 			rw_downgrade(&ddv->sdev_contents);
713*22253b45SAlexander Kolbasov 			return;
714*22253b45SAlexander Kolbasov 		}
715*22253b45SAlexander Kolbasov 	}
716*22253b45SAlexander Kolbasov 	/* At this point we should have a write lock */
717*22253b45SAlexander Kolbasov 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
718*22253b45SAlexander Kolbasov 
719facf4a8dSllai1 	sdcmn_err10(("devtree_gen (%s): %ld -> %ld\n",
720facf4a8dSllai1 	    ddv->sdev_path, ddv->sdev_devtree_gen, devtree_gen));
721*22253b45SAlexander Kolbasov 
722*22253b45SAlexander Kolbasov 	gdir = ddv->sdev_origin;
723*22253b45SAlexander Kolbasov 
724*22253b45SAlexander Kolbasov 	if (gdir != NULL)
725facf4a8dSllai1 		sdcmn_err10(("sdev_dir_gen (%s): %ld -> %ld\n",
726facf4a8dSllai1 		    ddv->sdev_path, ddv->sdev_ldir_gen,
727facf4a8dSllai1 		    gdir->sdev_gdir_gen));
728facf4a8dSllai1 
729facf4a8dSllai1 	/* update flags and generation number so next filldir is quick */
730*22253b45SAlexander Kolbasov 	if ((ddv->sdev_flags & SDEV_BUILD) == SDEV_BUILD) {
731facf4a8dSllai1 		ddv->sdev_flags &= ~SDEV_BUILD;
732*22253b45SAlexander Kolbasov 	}
733facf4a8dSllai1 	ddv->sdev_devtree_gen = devtree_gen;
734*22253b45SAlexander Kolbasov 	if (gdir != NULL)
735facf4a8dSllai1 		ddv->sdev_ldir_gen = gdir->sdev_gdir_gen;
736facf4a8dSllai1 
737facf4a8dSllai1 	prof_make_symlinks(ddv);
738facf4a8dSllai1 	prof_make_maps(ddv);
739facf4a8dSllai1 	prof_make_names(ddv);
740facf4a8dSllai1 	rw_downgrade(&ddv->sdev_contents);
741facf4a8dSllai1 }
742facf4a8dSllai1 
743facf4a8dSllai1 /* apply include/exclude pattern to existing directory content */
744facf4a8dSllai1 static void
apply_dir_pattern(struct sdev_node * dir,char * expr,char * pathleft,int type)745facf4a8dSllai1 apply_dir_pattern(struct sdev_node *dir, char *expr, char *pathleft, int type)
746facf4a8dSllai1 {
747facf4a8dSllai1 	struct sdev_node *dv;
748facf4a8dSllai1 
749facf4a8dSllai1 	/* leaf pattern */
750facf4a8dSllai1 	if (pathleft == NULL) {
751facf4a8dSllai1 		if (type == PROFILE_TYPE_INCLUDE)
752facf4a8dSllai1 			return;	/* nothing to do for include */
753facf4a8dSllai1 		(void) sdev_cleandir(dir, expr, SDEV_ENFORCE);
754facf4a8dSllai1 		return;
755facf4a8dSllai1 	}
756facf4a8dSllai1 
757facf4a8dSllai1 	/* directory pattern */
758facf4a8dSllai1 	rw_enter(&dir->sdev_contents, RW_WRITER);
759aac43a5fSjg 
760aac43a5fSjg 	for (dv = SDEV_FIRST_ENTRY(dir); dv; dv = SDEV_NEXT_ENTRY(dir, dv)) {
761facf4a8dSllai1 		if (gmatch(dv->sdev_name, expr) == 0 ||
762facf4a8dSllai1 		    SDEVTOV(dv)->v_type != VDIR)
763facf4a8dSllai1 			continue;
764facf4a8dSllai1 		process_rule(dv, dv->sdev_origin,
765facf4a8dSllai1 		    pathleft, NULL, type);
766facf4a8dSllai1 	}
767facf4a8dSllai1 	rw_exit(&dir->sdev_contents);
768facf4a8dSllai1 }
769facf4a8dSllai1 
770facf4a8dSllai1 /*
771facf4a8dSllai1  * Add a profile rule.
772facf4a8dSllai1  * tgt represents a device name matching expression,
773facf4a8dSllai1  * matching device names are to be either included or excluded.
774facf4a8dSllai1  */
775facf4a8dSllai1 static void
prof_add_rule(char * name,char * tgt,struct sdev_node * dir,int type)776facf4a8dSllai1 prof_add_rule(char *name, char *tgt, struct sdev_node *dir, int type)
777facf4a8dSllai1 {
778facf4a8dSllai1 	int error;
779facf4a8dSllai1 	nvlist_t **nvlp = NULL;
780facf4a8dSllai1 	int rv;
781facf4a8dSllai1 
782facf4a8dSllai1 	ASSERT(SDEVTOV(dir)->v_type == VDIR);
783facf4a8dSllai1 
784facf4a8dSllai1 	rw_enter(&dir->sdev_contents, RW_WRITER);
785facf4a8dSllai1 
786facf4a8dSllai1 	switch (type) {
787facf4a8dSllai1 	case PROFILE_TYPE_INCLUDE:
788facf4a8dSllai1 		if (tgt)
789facf4a8dSllai1 			nvlp = &(dir->sdev_prof.dev_glob_incdir);
790facf4a8dSllai1 		else
791facf4a8dSllai1 			nvlp = &(dir->sdev_prof.dev_name);
792facf4a8dSllai1 		break;
793facf4a8dSllai1 	case PROFILE_TYPE_EXCLUDE:
794facf4a8dSllai1 		if (tgt)
795facf4a8dSllai1 			nvlp = &(dir->sdev_prof.dev_glob_excdir);
796facf4a8dSllai1 		else
797facf4a8dSllai1 			nvlp = &(dir->sdev_prof.dev_name);
798facf4a8dSllai1 		break;
799facf4a8dSllai1 	case PROFILE_TYPE_MAP:
800facf4a8dSllai1 		nvlp = &(dir->sdev_prof.dev_map);
801facf4a8dSllai1 		break;
802facf4a8dSllai1 	case PROFILE_TYPE_SYMLINK:
803facf4a8dSllai1 		nvlp = &(dir->sdev_prof.dev_symlink);
804facf4a8dSllai1 		break;
805facf4a8dSllai1 	};
806facf4a8dSllai1 
807facf4a8dSllai1 	/* initialize nvlist */
808facf4a8dSllai1 	if (*nvlp == NULL) {
809facf4a8dSllai1 		error = nvlist_alloc(nvlp, NV_UNIQUE_NAME, KM_SLEEP);
810facf4a8dSllai1 		ASSERT(error == 0);
811facf4a8dSllai1 	}
812facf4a8dSllai1 
813facf4a8dSllai1 	if (tgt) {
814facf4a8dSllai1 		rv = nvlist_add_string(*nvlp, name, tgt);
815facf4a8dSllai1 	} else {
816facf4a8dSllai1 		rv = nvlist_add_int32(*nvlp, name, type);
817facf4a8dSllai1 	}
818facf4a8dSllai1 	ASSERT(rv == 0);
819facf4a8dSllai1 	/* rebuild directory content */
820facf4a8dSllai1 	dir->sdev_flags |= SDEV_BUILD;
821facf4a8dSllai1 
822facf4a8dSllai1 	if ((type == PROFILE_TYPE_INCLUDE) &&
823facf4a8dSllai1 	    (strpbrk(name, "*?[]") != NULL)) {
824facf4a8dSllai1 			dir->sdev_prof.has_glob = 1;
825facf4a8dSllai1 	}
826facf4a8dSllai1 
827facf4a8dSllai1 	rw_exit(&dir->sdev_contents);
828facf4a8dSllai1 
829facf4a8dSllai1 	/* additional details for glob pattern and exclusion */
830facf4a8dSllai1 	switch (type) {
831facf4a8dSllai1 	case PROFILE_TYPE_INCLUDE:
832facf4a8dSllai1 	case PROFILE_TYPE_EXCLUDE:
833facf4a8dSllai1 		apply_dir_pattern(dir, name, tgt, type);
834facf4a8dSllai1 		break;
835facf4a8dSllai1 	};
836facf4a8dSllai1 }
837facf4a8dSllai1 
838facf4a8dSllai1 /*
839facf4a8dSllai1  * Parse path components and apply requested matching rule at
840facf4a8dSllai1  * directory level.
841facf4a8dSllai1  */
842facf4a8dSllai1 static void
process_rule(struct sdev_node * dir,struct sdev_node * gdir,char * path,char * tgt,int type)843facf4a8dSllai1 process_rule(struct sdev_node *dir, struct sdev_node *gdir,
844facf4a8dSllai1     char *path, char *tgt, int type)
845facf4a8dSllai1 {
846facf4a8dSllai1 	char *name;
847facf4a8dSllai1 	struct pathname	pn;
848facf4a8dSllai1 	int rv = 0;
849facf4a8dSllai1 
850facf4a8dSllai1 	if ((strlen(path) > 5) && (strncmp(path, "/dev/", 5) == 0)) {
851facf4a8dSllai1 		path += 5;
852facf4a8dSllai1 	}
853facf4a8dSllai1 
854facf4a8dSllai1 	if (pn_get(path, UIO_SYSSPACE, &pn) != 0)
855facf4a8dSllai1 		return;
856facf4a8dSllai1 
857facf4a8dSllai1 	name = kmem_alloc(MAXPATHLEN, KM_SLEEP);
858facf4a8dSllai1 	(void) pn_getcomponent(&pn, name);
859facf4a8dSllai1 	pn_skipslash(&pn);
860facf4a8dSllai1 	SDEV_HOLD(dir);
861facf4a8dSllai1 
862facf4a8dSllai1 	while (pn_pathleft(&pn)) {
863facf4a8dSllai1 		/* If this is pattern, just add the pattern */
864facf4a8dSllai1 		if (strpbrk(name, "*?[]") != NULL &&
865facf4a8dSllai1 		    (type == PROFILE_TYPE_INCLUDE ||
866facf4a8dSllai1 		    type == PROFILE_TYPE_EXCLUDE)) {
867facf4a8dSllai1 			ASSERT(tgt == NULL);
868facf4a8dSllai1 			tgt = pn.pn_path;
869facf4a8dSllai1 			break;
870facf4a8dSllai1 		}
871facf4a8dSllai1 		if ((rv = prof_make_dir(name, &gdir, &dir)) != 0) {
872facf4a8dSllai1 			cmn_err(CE_CONT, "process_rule: %s error %d\n",
873facf4a8dSllai1 			    path, rv);
874facf4a8dSllai1 			break;
875facf4a8dSllai1 		}
876facf4a8dSllai1 		(void) pn_getcomponent(&pn, name);
877facf4a8dSllai1 		pn_skipslash(&pn);
878facf4a8dSllai1 	}
879facf4a8dSllai1 
880facf4a8dSllai1 	/* process the leaf component */
881facf4a8dSllai1 	if (rv == 0) {
882facf4a8dSllai1 		prof_add_rule(name, tgt, dir, type);
883facf4a8dSllai1 		SDEV_SIMPLE_RELE(dir);
884facf4a8dSllai1 	}
885facf4a8dSllai1 
886facf4a8dSllai1 	kmem_free(name, MAXPATHLEN);
887facf4a8dSllai1 	pn_free(&pn);
888facf4a8dSllai1 }
889facf4a8dSllai1 
890facf4a8dSllai1 static int
copyin_nvlist(char * packed_usr,size_t packed_sz,nvlist_t ** nvlp)891facf4a8dSllai1 copyin_nvlist(char *packed_usr, size_t packed_sz, nvlist_t **nvlp)
892facf4a8dSllai1 {
893facf4a8dSllai1 	int err = 0;
894facf4a8dSllai1 	char *packed;
895facf4a8dSllai1 	nvlist_t *profile = NULL;
896facf4a8dSllai1 
897facf4a8dSllai1 	/* simple sanity check */
898facf4a8dSllai1 	if (packed_usr == NULL || packed_sz == 0)
899facf4a8dSllai1 		return (NULL);
900facf4a8dSllai1 
901facf4a8dSllai1 	/* copyin packed profile nvlist */
902facf4a8dSllai1 	packed = kmem_alloc(packed_sz, KM_NOSLEEP);
903facf4a8dSllai1 	if (packed == NULL)
904facf4a8dSllai1 		return (ENOMEM);
905facf4a8dSllai1 	err = copyin(packed_usr, packed, packed_sz);
906facf4a8dSllai1 
907facf4a8dSllai1 	/* unpack packed profile nvlist */
908facf4a8dSllai1 	if (err)
909facf4a8dSllai1 		cmn_err(CE_WARN, "copyin_nvlist: copyin failed with "
910facf4a8dSllai1 		    "err %d\n", err);
911facf4a8dSllai1 	else if (err = nvlist_unpack(packed, packed_sz, &profile, KM_NOSLEEP))
912facf4a8dSllai1 		cmn_err(CE_WARN, "copyin_nvlist: nvlist_unpack "
913facf4a8dSllai1 		    "failed with err %d\n", err);
914facf4a8dSllai1 
915facf4a8dSllai1 	kmem_free(packed, packed_sz);
916facf4a8dSllai1 	if (err == 0)
917facf4a8dSllai1 		*nvlp = profile;
918facf4a8dSllai1 	return (err);
919facf4a8dSllai1 }
920facf4a8dSllai1 
921facf4a8dSllai1 /*
922facf4a8dSllai1  * Process profile passed down from libdevinfo. There are four types
923facf4a8dSllai1  * of matching rules:
924facf4a8dSllai1  *  include: export a name or names matching a pattern
925facf4a8dSllai1  *  exclude: exclude a name or names matching a pattern
926facf4a8dSllai1  *  symlink: create a local symlink
927facf4a8dSllai1  *  map:     export a device with a name different from the global zone
928facf4a8dSllai1  * Note: We may consider supporting VOP_SYMLINK in non-global instances,
929facf4a8dSllai1  *	because it does not present any security risk. For now, the fs
930facf4a8dSllai1  *	instance is read only.
931facf4a8dSllai1  */
932facf4a8dSllai1 static void
sdev_process_profile(struct sdev_data * sdev_data,nvlist_t * profile)933facf4a8dSllai1 sdev_process_profile(struct sdev_data *sdev_data, nvlist_t *profile)
934facf4a8dSllai1 {
935facf4a8dSllai1 	nvpair_t *nvpair;
936facf4a8dSllai1 	char *nvname, *dname;
937facf4a8dSllai1 	struct sdev_node *dir, *gdir;
938facf4a8dSllai1 	char **pair;				/* for symlinks and maps */
939facf4a8dSllai1 	uint_t nelem;
940facf4a8dSllai1 	int rv;
941facf4a8dSllai1 
942facf4a8dSllai1 	gdir = sdev_origins->sdev_root;	/* root of global /dev */
943facf4a8dSllai1 	dir = sdev_data->sdev_root;	/* root of current instance */
944facf4a8dSllai1 
945facf4a8dSllai1 	ASSERT(profile);
946facf4a8dSllai1 
947facf4a8dSllai1 	/* process nvpairs in the list */
948facf4a8dSllai1 	nvpair = NULL;
949facf4a8dSllai1 	while (nvpair = nvlist_next_nvpair(profile, nvpair)) {
950facf4a8dSllai1 		nvname = nvpair_name(nvpair);
951facf4a8dSllai1 		ASSERT(nvname != NULL);
952facf4a8dSllai1 
953facf4a8dSllai1 		if (strcmp(nvname, SDEV_NVNAME_INCLUDE) == 0) {
954facf4a8dSllai1 			rv = nvpair_value_string(nvpair, &dname);
955facf4a8dSllai1 			if (rv != 0) {
956facf4a8dSllai1 				cmn_err(CE_WARN, sdev_nvp_val_err,
957facf4a8dSllai1 				    rv, nvpair_name(nvpair));
958facf4a8dSllai1 				break;
959facf4a8dSllai1 			}
960facf4a8dSllai1 			process_rule(dir, gdir, dname, NULL,
961facf4a8dSllai1 			    PROFILE_TYPE_INCLUDE);
962facf4a8dSllai1 		} else if (strcmp(nvname, SDEV_NVNAME_EXCLUDE) == 0) {
963facf4a8dSllai1 			rv = nvpair_value_string(nvpair, &dname);
964facf4a8dSllai1 			if (rv != 0) {
965facf4a8dSllai1 				cmn_err(CE_WARN, sdev_nvp_val_err,
966facf4a8dSllai1 				    rv, nvpair_name(nvpair));
967facf4a8dSllai1 				break;
968facf4a8dSllai1 			}
969facf4a8dSllai1 			process_rule(dir, gdir, dname, NULL,
970facf4a8dSllai1 			    PROFILE_TYPE_EXCLUDE);
971facf4a8dSllai1 		} else if (strcmp(nvname, SDEV_NVNAME_SYMLINK) == 0) {
972facf4a8dSllai1 			rv = nvpair_value_string_array(nvpair, &pair, &nelem);
973facf4a8dSllai1 			if (rv != 0) {
974facf4a8dSllai1 				cmn_err(CE_WARN, sdev_nvp_val_err,
975facf4a8dSllai1 				    rv, nvpair_name(nvpair));
976facf4a8dSllai1 				break;
977facf4a8dSllai1 			}
978facf4a8dSllai1 			ASSERT(nelem == 2);
979facf4a8dSllai1 			process_rule(dir, gdir, pair[0], pair[1],
980facf4a8dSllai1 			    PROFILE_TYPE_SYMLINK);
981facf4a8dSllai1 		} else if (strcmp(nvname, SDEV_NVNAME_MAP) == 0) {
982facf4a8dSllai1 			rv = nvpair_value_string_array(nvpair, &pair, &nelem);
983facf4a8dSllai1 			if (rv != 0) {
984facf4a8dSllai1 				cmn_err(CE_WARN, sdev_nvp_val_err,
985facf4a8dSllai1 				    rv, nvpair_name(nvpair));
986facf4a8dSllai1 				break;
987facf4a8dSllai1 			}
988facf4a8dSllai1 			process_rule(dir, gdir, pair[1], pair[0],
989facf4a8dSllai1 			    PROFILE_TYPE_MAP);
990facf4a8dSllai1 		} else if (strcmp(nvname, SDEV_NVNAME_MOUNTPT) != 0) {
991facf4a8dSllai1 			cmn_err(CE_WARN, "sdev_process_profile: invalid "
992facf4a8dSllai1 			    "nvpair %s\n", nvname);
993facf4a8dSllai1 		}
994facf4a8dSllai1 	}
995facf4a8dSllai1 }
996facf4a8dSllai1 
997facf4a8dSllai1 /*ARGSUSED*/
998facf4a8dSllai1 int
prof_lookup(vnode_t * dvp,char * nm,struct vnode ** vpp,struct cred * cred)999facf4a8dSllai1 prof_lookup(vnode_t *dvp, char *nm, struct vnode **vpp, struct cred *cred)
1000facf4a8dSllai1 {
1001facf4a8dSllai1 	struct sdev_node *ddv = VTOSDEV(dvp);
1002facf4a8dSllai1 	struct sdev_node *dv;
1003facf4a8dSllai1 	int nmlen;
1004facf4a8dSllai1 
1005facf4a8dSllai1 	/*
1006facf4a8dSllai1 	 * Empty name or ., return node itself.
1007facf4a8dSllai1 	 */
1008facf4a8dSllai1 	nmlen = strlen(nm);
1009facf4a8dSllai1 	if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) {
1010facf4a8dSllai1 		*vpp = SDEVTOV(ddv);
1011facf4a8dSllai1 		VN_HOLD(*vpp);
1012facf4a8dSllai1 		return (0);
1013facf4a8dSllai1 	}
1014facf4a8dSllai1 
1015facf4a8dSllai1 	/*
1016facf4a8dSllai1 	 * .., return the parent directory
1017facf4a8dSllai1 	 */
1018facf4a8dSllai1 	if ((nmlen == 2) && (strcmp(nm, "..") == 0)) {
1019facf4a8dSllai1 		*vpp = SDEVTOV(ddv->sdev_dotdot);
1020facf4a8dSllai1 		VN_HOLD(*vpp);
1021facf4a8dSllai1 		return (0);
1022facf4a8dSllai1 	}
1023facf4a8dSllai1 
1024facf4a8dSllai1 	rw_enter(&ddv->sdev_contents, RW_READER);
1025facf4a8dSllai1 	dv = sdev_cache_lookup(ddv, nm);
1026facf4a8dSllai1 	if (dv == NULL) {
1027facf4a8dSllai1 		prof_filldir(ddv);
1028facf4a8dSllai1 		dv = sdev_cache_lookup(ddv, nm);
1029facf4a8dSllai1 	}
1030facf4a8dSllai1 	rw_exit(&ddv->sdev_contents);
1031facf4a8dSllai1 	if (dv == NULL) {
1032facf4a8dSllai1 		sdcmn_err10(("prof_lookup: %s not found\n", nm));
1033facf4a8dSllai1 		return (ENOENT);
1034facf4a8dSllai1 	}
1035facf4a8dSllai1 
1036facf4a8dSllai1 	return (sdev_to_vp(dv, vpp));
1037facf4a8dSllai1 }
1038facf4a8dSllai1 
1039facf4a8dSllai1 /*
1040facf4a8dSllai1  * This is invoked after a new filesystem is mounted to define the
1041facf4a8dSllai1  * name space. It is also invoked during normal system operation
1042facf4a8dSllai1  * to update the name space.
1043facf4a8dSllai1  *
1044facf4a8dSllai1  * Applications call di_prof_commit() in libdevinfo, which invokes
1045facf4a8dSllai1  * modctl(). modctl calls this function. The input is a packed nvlist.
1046facf4a8dSllai1  */
1047facf4a8dSllai1 int
devname_profile_update(char * packed,size_t packed_sz)1048facf4a8dSllai1 devname_profile_update(char *packed, size_t packed_sz)
1049facf4a8dSllai1 {
1050facf4a8dSllai1 	char *mntpt;
1051facf4a8dSllai1 	nvlist_t *nvl;
1052facf4a8dSllai1 	nvpair_t *nvp;
1053facf4a8dSllai1 	struct sdev_data *mntinfo;
1054facf4a8dSllai1 	int err;
1055facf4a8dSllai1 	int rv;
1056facf4a8dSllai1 
1057facf4a8dSllai1 	nvl = NULL;
1058facf4a8dSllai1 	if ((err = copyin_nvlist(packed, packed_sz, &nvl)) != 0)
1059facf4a8dSllai1 		return (err);
1060facf4a8dSllai1 	ASSERT(nvl);
1061facf4a8dSllai1 
1062facf4a8dSllai1 	/* The first nvpair must be the mount point */
1063facf4a8dSllai1 	nvp = nvlist_next_nvpair(nvl, NULL);
1064facf4a8dSllai1 	if (strcmp(nvpair_name(nvp), SDEV_NVNAME_MOUNTPT) != 0) {
1065facf4a8dSllai1 		cmn_err(CE_NOTE,
1066facf4a8dSllai1 		    "devname_profile_update: mount point not specified");
1067facf4a8dSllai1 		nvlist_free(nvl);
1068facf4a8dSllai1 		return (EINVAL);
1069facf4a8dSllai1 	}
1070facf4a8dSllai1 
1071facf4a8dSllai1 	/* find the matching filesystem instance */
1072facf4a8dSllai1 	rv = nvpair_value_string(nvp, &mntpt);
1073facf4a8dSllai1 	if (rv != 0) {
1074facf4a8dSllai1 		cmn_err(CE_WARN, sdev_nvp_val_err,
1075facf4a8dSllai1 		    rv, nvpair_name(nvp));
1076facf4a8dSllai1 	} else {
1077facf4a8dSllai1 		mntinfo = sdev_find_mntinfo(mntpt);
1078facf4a8dSllai1 		if (mntinfo == NULL) {
1079facf4a8dSllai1 			cmn_err(CE_NOTE, "devname_profile_update: "
1080facf4a8dSllai1 			    " mount point %s not found", mntpt);
1081facf4a8dSllai1 			nvlist_free(nvl);
1082facf4a8dSllai1 			return (EINVAL);
1083facf4a8dSllai1 		}
1084facf4a8dSllai1 
1085facf4a8dSllai1 		/* now do the hardwork to process the profile */
1086facf4a8dSllai1 		sdev_process_profile(mntinfo, nvl);
1087facf4a8dSllai1 
1088facf4a8dSllai1 		sdev_mntinfo_rele(mntinfo);
1089facf4a8dSllai1 	}
1090facf4a8dSllai1 
1091facf4a8dSllai1 	nvlist_free(nvl);
1092facf4a8dSllai1 	return (0);
1093facf4a8dSllai1 }
1094