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
50enum {
51	PROFILE_TYPE_INCLUDE,
52	PROFILE_TYPE_EXCLUDE,
53	PROFILE_TYPE_MAP,
54	PROFILE_TYPE_SYMLINK
55};
56
57enum {
58	WALK_DIR_CONTINUE = 0,
59	WALK_DIR_TERMINATE
60};
61
62static const char *sdev_nvp_val_err = "nvpair_value error %d, %s\n";
63
64static void process_rule(struct sdev_node *, struct sdev_node *,
65    char *, char *, int);
66static void walk_dir(struct vnode *, void *, int (*)(char *, void *));
67
68static void
69prof_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
110static void
111apply_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 */
143static int
144prof_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 */
190static int
191prof_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 */
245static void
246prof_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
299static void
300prof_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 */
314static void
315prof_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
339static void
340prof_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
366struct match_arg {
367	char *expr;
368	int match;
369};
370
371static int
372match_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
383static int
384is_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 */
419int
420prof_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
473static void
474walk_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
524end:
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 */
536static int
537prof_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
587static int
588prof_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
598static int
599prof_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
609static void
610prof_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
620static void
621prof_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 */
661static boolean_t
662prof_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 */
689void
690prof_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 */
745static void
746apply_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 */
776static void
777prof_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 */
843static void
844process_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
891static int
892copyin_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 (err);
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 */
933static void
934sdev_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*/
999int
1000prof_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 */
1048int
1049devname_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