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 /*
230fbb751dSJohn Levon  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
2445b17475SAlex Wilson  * Copyright 2016, Joyent Inc.
25facf4a8dSllai  */
26facf4a8dSllai 
27facf4a8dSllai /*
28facf4a8dSllai  * This file implements /dev filesystem operations for non-global
29facf4a8dSllai  * instances. Three major entry points:
30facf4a8dSllai  * devname_profile_update()
31facf4a8dSllai  *   Update matching rules determining which names to export
32facf4a8dSllai  * prof_readdir()
33facf4a8dSllai  *   Return the list of exported names
34facf4a8dSllai  * prof_lookup()
35facf4a8dSllai  *   Implements lookup
36facf4a8dSllai  */
37facf4a8dSllai 
38facf4a8dSllai #include <sys/types.h>
39facf4a8dSllai #include <sys/param.h>
40facf4a8dSllai #include <sys/sysmacros.h>
41facf4a8dSllai #include <sys/vnode.h>
42facf4a8dSllai #include <sys/uio.h>
43facf4a8dSllai #include <sys/dirent.h>
44facf4a8dSllai #include <sys/pathname.h>
45facf4a8dSllai #include <sys/fs/dv_node.h>
46facf4a8dSllai #include <sys/fs/sdev_impl.h>
47facf4a8dSllai #include <sys/sunndi.h>
48facf4a8dSllai #include <sys/modctl.h>
49facf4a8dSllai 
50facf4a8dSllai enum {
51facf4a8dSllai 	PROFILE_TYPE_INCLUDE,
52facf4a8dSllai 	PROFILE_TYPE_EXCLUDE,
53facf4a8dSllai 	PROFILE_TYPE_MAP,
54facf4a8dSllai 	PROFILE_TYPE_SYMLINK
55facf4a8dSllai };
56facf4a8dSllai 
57facf4a8dSllai enum {
58facf4a8dSllai 	WALK_DIR_CONTINUE = 0,
59facf4a8dSllai 	WALK_DIR_TERMINATE
60facf4a8dSllai };
61facf4a8dSllai 
62facf4a8dSllai static const char *sdev_nvp_val_err = "nvpair_value error %d, %s\n";
63facf4a8dSllai 
64facf4a8dSllai static void process_rule(struct sdev_node *, struct sdev_node *,
65facf4a8dSllai     char *, char *, int);
66facf4a8dSllai static void walk_dir(struct vnode *, void *, int (*)(char *, void *));
67facf4a8dSllai 
68facf4a8dSllai static void
prof_getattr(struct sdev_node * dir,char * name,struct vnode * gdv,struct vattr * vap,struct vnode ** avpp,int * no_fs_perm)69facf4a8dSllai prof_getattr(struct sdev_node *dir, char *name, struct vnode *gdv,
70facf4a8dSllai     struct vattr *vap, struct vnode **avpp, int *no_fs_perm)
71facf4a8dSllai {
72facf4a8dSllai 	struct vnode *advp;
73facf4a8dSllai 
74facf4a8dSllai 	/* get attribute from shadow, if present; else get default */
75facf4a8dSllai 	advp = dir->sdev_attrvp;
76da6c28aaSamw 	if (advp && VOP_LOOKUP(advp, name, avpp, NULL, 0, NULL, kcred,
77da6c28aaSamw 	    NULL, NULL, NULL) == 0) {
78da6c28aaSamw 		(void) VOP_GETATTR(*avpp, vap, 0, kcred, NULL);
79facf4a8dSllai 	} else if (gdv == NULL || gdv->v_type == VDIR) {
80facf4a8dSllai 		/* always create shadow directory */
81facf4a8dSllai 		*vap = sdev_vattr_dir;
82da6c28aaSamw 		if (advp && VOP_MKDIR(advp, name, &sdev_vattr_dir,
83da6c28aaSamw 		    avpp, kcred, NULL, 0, NULL) != 0) {
84facf4a8dSllai 			*avpp = NULLVP;
85facf4a8dSllai 			sdcmn_err10(("prof_getattr: failed to create "
86facf4a8dSllai 			    "shadow directory %s/%s\n", dir->sdev_path, name));
87facf4a8dSllai 		}
88facf4a8dSllai 	} else {
89facf4a8dSllai 		/*
90facf4a8dSllai 		 * get default permission from devfs
91facf4a8dSllai 		 * Before calling devfs_get_defattr, we need to get
92facf4a8dSllai 		 * the realvp (the dv_node). If realvp is not a dv_node,
93facf4a8dSllai 		 * devfs_get_defattr() will return a system-wide default
94facf4a8dSllai 		 * attr for device nodes.
95facf4a8dSllai 		 */
96facf4a8dSllai 		struct vnode *rvp;
97da6c28aaSamw 		if (VOP_REALVP(gdv, &rvp, NULL) != 0)
98facf4a8dSllai 			rvp = gdv;
99facf4a8dSllai 		devfs_get_defattr(rvp, vap, no_fs_perm);
100facf4a8dSllai 		*avpp = NULLVP;
101facf4a8dSllai 	}
102facf4a8dSllai 
103facf4a8dSllai 	/* ignore dev_t and vtype from backing store */
104facf4a8dSllai 	if (gdv) {
105facf4a8dSllai 		vap->va_type = gdv->v_type;
106facf4a8dSllai 		vap->va_rdev = gdv->v_rdev;
107facf4a8dSllai 	}
108facf4a8dSllai }
109facf4a8dSllai 
110facf4a8dSllai static void
apply_glob_pattern(struct sdev_node * pdir,struct sdev_node * cdir)111facf4a8dSllai apply_glob_pattern(struct sdev_node *pdir, struct sdev_node *cdir)
112facf4a8dSllai {
113facf4a8dSllai 	char *name;
114facf4a8dSllai 	nvpair_t *nvp = NULL;
115facf4a8dSllai 	nvlist_t *nvl;
116facf4a8dSllai 	struct vnode *vp = SDEVTOV(cdir);
117facf4a8dSllai 	int rv = 0;
118facf4a8dSllai 
119facf4a8dSllai 	if (vp->v_type != VDIR)
120facf4a8dSllai 		return;
121facf4a8dSllai 	name = cdir->sdev_name;
122facf4a8dSllai 	nvl = pdir->sdev_prof.dev_glob_incdir;
123facf4a8dSllai 	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
124facf4a8dSllai 		char *pathleft;
125facf4a8dSllai 		char *expr = nvpair_name(nvp);
126facf4a8dSllai 		if (!gmatch(name, expr))
127facf4a8dSllai 			continue;
128facf4a8dSllai 		rv = nvpair_value_string(nvp, &pathleft);
129facf4a8dSllai 		if (rv != 0) {
130facf4a8dSllai 			cmn_err(CE_WARN, sdev_nvp_val_err,
131facf4a8dSllai 			    rv, nvpair_name(nvp));
132facf4a8dSllai 			break;
133facf4a8dSllai 		}
134facf4a8dSllai 		process_rule(cdir, cdir->sdev_origin,
135facf4a8dSllai 		    pathleft, NULL, PROFILE_TYPE_INCLUDE);
136facf4a8dSllai 	}
137facf4a8dSllai }
138facf4a8dSllai 
139facf4a8dSllai /*
140facf4a8dSllai  * Some commonality here with sdev_mknode(), could be simplified.
141facf4a8dSllai  * NOTE: prof_mknode returns with *newdv held once, if success.
142facf4a8dSllai  */
143facf4a8dSllai 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)144facf4a8dSllai prof_mknode(struct sdev_node *dir, char *name, struct sdev_node **newdv,
145facf4a8dSllai     vattr_t *vap, vnode_t *avp, void *arg, cred_t *cred)
146facf4a8dSllai {
147facf4a8dSllai 	struct sdev_node *dv;
148facf4a8dSllai 	int rv;
149facf4a8dSllai 
150facf4a8dSllai 	ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
151facf4a8dSllai 
152facf4a8dSllai 	/* check cache first */
153facf4a8dSllai 	if (dv = sdev_cache_lookup(dir, name)) {
154facf4a8dSllai 		*newdv = dv;
155facf4a8dSllai 		return (0);
156facf4a8dSllai 	}
157facf4a8dSllai 
158facf4a8dSllai 	/* allocate node and insert into cache */
159facf4a8dSllai 	rv = sdev_nodeinit(dir, name, &dv, NULL);
160facf4a8dSllai 	if (rv != 0) {
161facf4a8dSllai 		*newdv = NULL;
162facf4a8dSllai 		return (rv);
163facf4a8dSllai 	}
164facf4a8dSllai 
1659e5aa9d8SRobert Mustacchi 	sdev_cache_update(dir, &dv, name, SDEV_CACHE_ADD);
166facf4a8dSllai 	*newdv = dv;
167facf4a8dSllai 
168facf4a8dSllai 	/* put it in ready state */
169facf4a8dSllai 	rv = sdev_nodeready(*newdv, vap, avp, arg, cred);
170facf4a8dSllai 
171facf4a8dSllai 	/* handle glob pattern in the middle of a path */
172facf4a8dSllai 	if (rv == 0) {
173facf4a8dSllai 		if (SDEVTOV(*newdv)->v_type == VDIR)
174facf4a8dSllai 			sdcmn_err10(("sdev_origin for %s set to 0x%p\n",
175facf4a8dSllai 			    name, arg));
176facf4a8dSllai 		apply_glob_pattern(dir, *newdv);
1779e5aa9d8SRobert Mustacchi 	} else {
1789e5aa9d8SRobert Mustacchi 		sdev_cache_update(dir, &dv, name, SDEV_CACHE_DELETE);
1799e5aa9d8SRobert Mustacchi 		SDEV_RELE(dv);
180facf4a8dSllai 	}
181facf4a8dSllai 	return (rv);
182facf4a8dSllai }
183facf4a8dSllai 
184facf4a8dSllai /*
185facf4a8dSllai  * Create a directory node in a non-global dev instance.
186facf4a8dSllai  * Always create shadow vnode. Set sdev_origin to the corresponding
187facf4a8dSllai  * global directory sdev_node if it exists. This facilitates the
188facf4a8dSllai  * lookup operation.
189facf4a8dSllai  */
190facf4a8dSllai static int
prof_make_dir(char * name,struct sdev_node ** gdirp,struct sdev_node ** dirp)191facf4a8dSllai prof_make_dir(char *name, struct sdev_node **gdirp, struct sdev_node **dirp)
192facf4a8dSllai {
193facf4a8dSllai 	struct sdev_node *dir = *dirp;
194facf4a8dSllai 	struct sdev_node *gdir = *gdirp;
195facf4a8dSllai 	struct sdev_node *newdv;
196facf4a8dSllai 	struct vnode *avp, *gnewdir = NULL;
197facf4a8dSllai 	struct vattr vattr;
198facf4a8dSllai 	int error;
199facf4a8dSllai 
200facf4a8dSllai 	/* see if name already exists */
201facf4a8dSllai 	rw_enter(&dir->sdev_contents, RW_READER);
202facf4a8dSllai 	if (newdv = sdev_cache_lookup(dir, name)) {
203facf4a8dSllai 		*dirp = newdv;
204facf4a8dSllai 		*gdirp = newdv->sdev_origin;
205facf4a8dSllai 		rw_exit(&dir->sdev_contents);
2069e5aa9d8SRobert Mustacchi 		SDEV_RELE(dir);
207facf4a8dSllai 		return (0);
208facf4a8dSllai 	}
209facf4a8dSllai 	rw_exit(&dir->sdev_contents);
210facf4a8dSllai 
211facf4a8dSllai 	/* find corresponding dir node in global dev */
212facf4a8dSllai 	if (gdir) {
213facf4a8dSllai 		error = VOP_LOOKUP(SDEVTOV(gdir), name, &gnewdir,
214da6c28aaSamw 		    NULL, 0, NULL, kcred, NULL, NULL, NULL);
215facf4a8dSllai 		if (error == 0) {
216facf4a8dSllai 			*gdirp = VTOSDEV(gnewdir);
217*814e7298SToomas Soome 		} else {	/* it's ok if there no global dir */
218facf4a8dSllai 			*gdirp = NULL;
219facf4a8dSllai 		}
220facf4a8dSllai 	}
221facf4a8dSllai 
222facf4a8dSllai 	/* get attribute from shadow, also create shadow dir */
223facf4a8dSllai 	prof_getattr(dir, name, gnewdir, &vattr, &avp, NULL);
224facf4a8dSllai 
225facf4a8dSllai 	/* create dev directory vnode */
226facf4a8dSllai 	rw_enter(&dir->sdev_contents, RW_WRITER);
227facf4a8dSllai 	error = prof_mknode(dir, name, &newdv, &vattr, avp, (void *)*gdirp,
228facf4a8dSllai 	    kcred);
229facf4a8dSllai 	rw_exit(&dir->sdev_contents);
230facf4a8dSllai 	if (error == 0) {
231facf4a8dSllai 		ASSERT(newdv);
232facf4a8dSllai 		*dirp = newdv;
233facf4a8dSllai 	}
234facf4a8dSllai 	SDEV_RELE(dir);
235facf4a8dSllai 	return (error);
236facf4a8dSllai }
237facf4a8dSllai 
238facf4a8dSllai /*
239facf4a8dSllai  * Look up a logical name in the global zone.
240facf4a8dSllai  * Provides the ability to map the global zone's device name
241facf4a8dSllai  * to an alternate name within a zone.  The primary example
242facf4a8dSllai  * is the virtual console device /dev/zcons/[zonename]/zconsole
243facf4a8dSllai  * mapped to /[zonename]/root/dev/zconsole.
244facf4a8dSllai  */
245facf4a8dSllai static void
prof_lookup_globaldev(struct sdev_node * dir,struct sdev_node * gdir,char * name,char * rename)246facf4a8dSllai prof_lookup_globaldev(struct sdev_node *dir, struct sdev_node *gdir,
247facf4a8dSllai     char *name, char *rename)
248facf4a8dSllai {
249facf4a8dSllai 	int error;
250facf4a8dSllai 	struct vnode *avp, *gdv, *gddv;
251facf4a8dSllai 	struct sdev_node *newdv;
252facf4a8dSllai 	struct vattr vattr = {0};
253facf4a8dSllai 	struct pathname pn;
254facf4a8dSllai 
255facf4a8dSllai 	/* check if node already exists */
256facf4a8dSllai 	newdv = sdev_cache_lookup(dir, rename);
257facf4a8dSllai 	if (newdv) {
258facf4a8dSllai 		ASSERT(newdv->sdev_state != SDEV_ZOMBIE);
259facf4a8dSllai 		SDEV_SIMPLE_RELE(newdv);
260facf4a8dSllai 		return;
261facf4a8dSllai 	}
262facf4a8dSllai 
263facf4a8dSllai 	/* sanity check arguments */
264facf4a8dSllai 	if (!gdir || pn_get(name, UIO_SYSSPACE, &pn))
265facf4a8dSllai 		return;
266facf4a8dSllai 
267facf4a8dSllai 	/* perform a relative lookup of the global /dev instance */
268facf4a8dSllai 	gddv = SDEVTOV(gdir);
269facf4a8dSllai 	VN_HOLD(gddv);
270facf4a8dSllai 	error = lookuppnvp(&pn, NULL, FOLLOW, NULLVPP, &gdv,
271facf4a8dSllai 	    rootdir, gddv, kcred);
272facf4a8dSllai 	pn_free(&pn);
273facf4a8dSllai 	if (error) {
274facf4a8dSllai 		sdcmn_err10(("prof_lookup_globaldev: %s not found\n", name));
275facf4a8dSllai 		return;
276facf4a8dSllai 	}
277facf4a8dSllai 	ASSERT(gdv && gdv->v_type != VLNK);
278facf4a8dSllai 
279facf4a8dSllai 	/*
280facf4a8dSllai 	 * Found the entry in global /dev, figure out attributes
281facf4a8dSllai 	 * by looking at backing store. Call into devfs for default.
282a83b1f2cSllai 	 * Note, mapped device is persisted under the new name
283facf4a8dSllai 	 */
284a83b1f2cSllai 	prof_getattr(dir, rename, gdv, &vattr, &avp, NULL);
285facf4a8dSllai 
286facf4a8dSllai 	if (gdv->v_type != VDIR) {
287facf4a8dSllai 		VN_RELE(gdv);
288facf4a8dSllai 		gdir = NULL;
289facf4a8dSllai 	} else
290facf4a8dSllai 		gdir = VTOSDEV(gdv);
291facf4a8dSllai 
292facf4a8dSllai 	if (prof_mknode(dir, rename, &newdv, &vattr, avp,
293facf4a8dSllai 	    (void *)gdir, kcred) == 0) {
294facf4a8dSllai 		ASSERT(newdv->sdev_state != SDEV_ZOMBIE);
295facf4a8dSllai 		SDEV_SIMPLE_RELE(newdv);
296facf4a8dSllai 	}
297facf4a8dSllai }
298facf4a8dSllai 
299facf4a8dSllai static void
prof_make_sym(struct sdev_node * dir,char * lnm,char * tgt)300facf4a8dSllai prof_make_sym(struct sdev_node *dir, char *lnm, char *tgt)
301facf4a8dSllai {
302facf4a8dSllai 	struct sdev_node *newdv;
303facf4a8dSllai 
304facf4a8dSllai 	if (prof_mknode(dir, lnm, &newdv, &sdev_vattr_lnk, NULL,
305facf4a8dSllai 	    (void *)tgt, kcred) == 0) {
306facf4a8dSllai 		ASSERT(newdv->sdev_state != SDEV_ZOMBIE);
307facf4a8dSllai 		SDEV_SIMPLE_RELE(newdv);
308facf4a8dSllai 	}
309facf4a8dSllai }
310facf4a8dSllai 
311facf4a8dSllai /*
312facf4a8dSllai  * Create symlinks in the current directory based on profile
313facf4a8dSllai  */
314facf4a8dSllai static void
prof_make_symlinks(struct sdev_node * dir)315facf4a8dSllai prof_make_symlinks(struct sdev_node *dir)
316facf4a8dSllai {
317facf4a8dSllai 	char *tgt, *lnm;
318facf4a8dSllai 	nvpair_t *nvp = NULL;
319facf4a8dSllai 	nvlist_t *nvl = dir->sdev_prof.dev_symlink;
320facf4a8dSllai 	int rv;
321facf4a8dSllai 
322facf4a8dSllai 	ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
323facf4a8dSllai 
324facf4a8dSllai 	if (nvl == NULL)
325facf4a8dSllai 		return;
326facf4a8dSllai 
327facf4a8dSllai 	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
328facf4a8dSllai 		lnm = nvpair_name(nvp);
329facf4a8dSllai 		rv = nvpair_value_string(nvp, &tgt);
330facf4a8dSllai 		if (rv != 0) {
331facf4a8dSllai 			cmn_err(CE_WARN, sdev_nvp_val_err,
332facf4a8dSllai 			    rv, nvpair_name(nvp));
333facf4a8dSllai 			break;
334facf4a8dSllai 		}
335facf4a8dSllai 		prof_make_sym(dir, lnm, tgt);
336facf4a8dSllai 	}
337facf4a8dSllai }
338facf4a8dSllai 
339facf4a8dSllai static void
prof_make_maps(struct sdev_node * dir)340facf4a8dSllai prof_make_maps(struct sdev_node *dir)
341facf4a8dSllai {
342facf4a8dSllai 	nvpair_t *nvp = NULL;
343facf4a8dSllai 	nvlist_t *nvl = dir->sdev_prof.dev_map;
344facf4a8dSllai 	int rv;
345facf4a8dSllai 
346facf4a8dSllai 	ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
347facf4a8dSllai 
348facf4a8dSllai 	if (nvl == NULL)
349facf4a8dSllai 		return;
350facf4a8dSllai 
351facf4a8dSllai 	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
352facf4a8dSllai 		char *name;
353facf4a8dSllai 		char *rename = nvpair_name(nvp);
354facf4a8dSllai 		rv = nvpair_value_string(nvp, &name);
355facf4a8dSllai 		if (rv != 0) {
356facf4a8dSllai 			cmn_err(CE_WARN, sdev_nvp_val_err,
357facf4a8dSllai 			    rv, nvpair_name(nvp));
358facf4a8dSllai 			break;
359facf4a8dSllai 		}
360facf4a8dSllai 		sdcmn_err10(("map %s -> %s\n", name, rename));
361facf4a8dSllai 		(void) prof_lookup_globaldev(dir, sdev_origins->sdev_root,
362facf4a8dSllai 		    name, rename);
363facf4a8dSllai 	}
364facf4a8dSllai }
365facf4a8dSllai 
366facf4a8dSllai struct match_arg {
367facf4a8dSllai 	char *expr;
368facf4a8dSllai 	int match;
369facf4a8dSllai };
370facf4a8dSllai 
371facf4a8dSllai static int
match_name(char * name,void * arg)372facf4a8dSllai match_name(char *name, void *arg)
373facf4a8dSllai {
374facf4a8dSllai 	struct match_arg *margp = (struct match_arg *)arg;
375facf4a8dSllai 
376facf4a8dSllai 	if (gmatch(name, margp->expr)) {
377facf4a8dSllai 		margp->match = 1;
378facf4a8dSllai 		return (WALK_DIR_TERMINATE);
379facf4a8dSllai 	}
380facf4a8dSllai 	return (WALK_DIR_CONTINUE);
381facf4a8dSllai }
382facf4a8dSllai 
383facf4a8dSllai static int
is_nonempty_dir(char * name,char * pathleft,struct sdev_node * dir)384facf4a8dSllai is_nonempty_dir(char *name, char *pathleft, struct sdev_node *dir)
385facf4a8dSllai {
386facf4a8dSllai 	struct match_arg marg;
387facf4a8dSllai 	struct pathname pn;
388facf4a8dSllai 	struct vnode *gvp;
389facf4a8dSllai 	struct sdev_node *gdir = dir->sdev_origin;
390facf4a8dSllai 
391da6c28aaSamw 	if (VOP_LOOKUP(SDEVTOV(gdir), name, &gvp, NULL, 0, NULL, kcred,
392da6c28aaSamw 	    NULL, NULL, NULL) != 0)
393facf4a8dSllai 		return (0);
394facf4a8dSllai 
395facf4a8dSllai 	if (gvp->v_type != VDIR) {
396facf4a8dSllai 		VN_RELE(gvp);
397facf4a8dSllai 		return (0);
398facf4a8dSllai 	}
399facf4a8dSllai 
400facf4a8dSllai 	if (pn_get(pathleft, UIO_SYSSPACE, &pn) != 0) {
401facf4a8dSllai 		VN_RELE(gvp);
402facf4a8dSllai 		return (0);
403facf4a8dSllai 	}
404facf4a8dSllai 
405facf4a8dSllai 	marg.expr = kmem_alloc(MAXNAMELEN, KM_SLEEP);
406facf4a8dSllai 	(void) pn_getcomponent(&pn, marg.expr);
407facf4a8dSllai 	marg.match = 0;
408facf4a8dSllai 
409facf4a8dSllai 	walk_dir(gvp, &marg, match_name);
410facf4a8dSllai 	VN_RELE(gvp);
411facf4a8dSllai 	kmem_free(marg.expr, MAXNAMELEN);
412facf4a8dSllai 	pn_free(&pn);
413facf4a8dSllai 
414facf4a8dSllai 	return (marg.match);
415facf4a8dSllai }
416facf4a8dSllai 
417facf4a8dSllai 
418facf4a8dSllai /* Check if name passes matching rules */
41945b17475SAlex Wilson int
prof_name_matched(char * name,struct sdev_node * dir)420facf4a8dSllai prof_name_matched(char *name, struct sdev_node *dir)
421facf4a8dSllai {
422facf4a8dSllai 	int type, match = 0;
423facf4a8dSllai 	char *expr;
424facf4a8dSllai 	nvlist_t *nvl;
425facf4a8dSllai 	nvpair_t *nvp = NULL;
426facf4a8dSllai 	int rv;
427facf4a8dSllai 
428facf4a8dSllai 	/* check against nvlist for leaf include/exclude */
429facf4a8dSllai 	nvl = dir->sdev_prof.dev_name;
430facf4a8dSllai 	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
431facf4a8dSllai 		expr = nvpair_name(nvp);
432facf4a8dSllai 		rv = nvpair_value_int32(nvp, &type);
433facf4a8dSllai 		if (rv != 0) {
434facf4a8dSllai 			cmn_err(CE_WARN, sdev_nvp_val_err,
435facf4a8dSllai 			    rv, nvpair_name(nvp));
436facf4a8dSllai 			break;
437facf4a8dSllai 		}
438facf4a8dSllai 
439facf4a8dSllai 		if (type == PROFILE_TYPE_EXCLUDE) {
440facf4a8dSllai 			if (gmatch(name, expr))
441facf4a8dSllai 				return (0);	/* excluded */
442facf4a8dSllai 		} else if (!match) {
443facf4a8dSllai 			match = gmatch(name, expr);
444facf4a8dSllai 		}
445facf4a8dSllai 	}
446facf4a8dSllai 	if (match) {
447facf4a8dSllai 		sdcmn_err10(("prof_name_matched: %s\n", name));
448facf4a8dSllai 		return (match);
449facf4a8dSllai 	}
450facf4a8dSllai 
451facf4a8dSllai 	/* check for match against directory globbing pattern */
452facf4a8dSllai 	nvl = dir->sdev_prof.dev_glob_incdir;
453facf4a8dSllai 	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
454facf4a8dSllai 		char *pathleft;
455facf4a8dSllai 		expr = nvpair_name(nvp);
456facf4a8dSllai 		if (gmatch(name, expr) == 0)
457facf4a8dSllai 			continue;
458facf4a8dSllai 		rv = nvpair_value_string(nvp, &pathleft);
459facf4a8dSllai 		if (rv != 0) {
460facf4a8dSllai 			cmn_err(CE_WARN, sdev_nvp_val_err,
461facf4a8dSllai 			    rv, nvpair_name(nvp));
462facf4a8dSllai 			break;
463facf4a8dSllai 		}
464facf4a8dSllai 		if (is_nonempty_dir(name, pathleft, dir)) {
465facf4a8dSllai 			sdcmn_err10(("prof_name_matched: dir %s\n", name));
466facf4a8dSllai 			return (1);
467facf4a8dSllai 		}
468facf4a8dSllai 	}
469facf4a8dSllai 
470facf4a8dSllai 	return (0);
471facf4a8dSllai }
472facf4a8dSllai 
473facf4a8dSllai static void
walk_dir(struct vnode * dvp,void * arg,int (* callback)(char *,void *))474facf4a8dSllai walk_dir(struct vnode *dvp, void *arg, int (*callback)(char *, void *))
475facf4a8dSllai {
476facf4a8dSllai 	char    *nm;
477facf4a8dSllai 	int eof, error;
478facf4a8dSllai 	struct iovec iov;
479facf4a8dSllai 	struct uio uio;
480facf4a8dSllai 	struct dirent64 *dp;
481facf4a8dSllai 	dirent64_t *dbuf;
482facf4a8dSllai 	size_t dbuflen, dlen;
483facf4a8dSllai 
484facf4a8dSllai 	ASSERT(dvp);
485facf4a8dSllai 
486facf4a8dSllai 	dlen = 4096;
487facf4a8dSllai 	dbuf = kmem_zalloc(dlen, KM_SLEEP);
488facf4a8dSllai 
489facf4a8dSllai 	uio.uio_iov = &iov;
490facf4a8dSllai 	uio.uio_iovcnt = 1;
491facf4a8dSllai 	uio.uio_segflg = UIO_SYSSPACE;
492facf4a8dSllai 	uio.uio_fmode = 0;
493facf4a8dSllai 	uio.uio_extflg = UIO_COPY_CACHED;
494facf4a8dSllai 	uio.uio_loffset = 0;
495facf4a8dSllai 	uio.uio_llimit = MAXOFFSET_T;
496facf4a8dSllai 
497facf4a8dSllai 	eof = 0;
498facf4a8dSllai 	error = 0;
499facf4a8dSllai 	while (!error && !eof) {
500facf4a8dSllai 		uio.uio_resid = dlen;
501facf4a8dSllai 		iov.iov_base = (char *)dbuf;
502facf4a8dSllai 		iov.iov_len = dlen;
503facf4a8dSllai 		(void) VOP_RWLOCK(dvp, V_WRITELOCK_FALSE, NULL);
504da6c28aaSamw 		error = VOP_READDIR(dvp, &uio, kcred, &eof, NULL, 0);
505facf4a8dSllai 		VOP_RWUNLOCK(dvp, V_WRITELOCK_FALSE, NULL);
506facf4a8dSllai 
507facf4a8dSllai 		dbuflen = dlen - uio.uio_resid;
508facf4a8dSllai 		if (error || dbuflen == 0)
509facf4a8dSllai 			break;
510facf4a8dSllai 		for (dp = dbuf; ((intptr_t)dp <
511facf4a8dSllai 		    (intptr_t)dbuf + dbuflen);
512facf4a8dSllai 		    dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) {
513facf4a8dSllai 			nm = dp->d_name;
514facf4a8dSllai 
515facf4a8dSllai 			if (strcmp(nm, ".") == 0 ||
516facf4a8dSllai 			    strcmp(nm, "..") == 0)
517facf4a8dSllai 				continue;
518facf4a8dSllai 
519facf4a8dSllai 			if (callback(nm, arg) == WALK_DIR_TERMINATE)
520facf4a8dSllai 				goto end;
521facf4a8dSllai 		}
522facf4a8dSllai 	}
523facf4a8dSllai 
524facf4a8dSllai end:
525facf4a8dSllai 	kmem_free(dbuf, dlen);
526facf4a8dSllai }
527facf4a8dSllai 
5280fbb751dSJohn Levon /*
5290fbb751dSJohn Levon  * Last chance for a zone to see a node.  If our parent dir is
5300fbb751dSJohn Levon  * SDEV