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