PSARC 2010/144 lofi(7D) in non global zones
[unleashed.git] / usr / src / uts / common / fs / dev / sdev_profile.c
blob01d4aab1ae9bfae9e14eb654d7d92422040c60db
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
27 * This file implements /dev filesystem operations for non-global
28 * instances. Three major entry points:
29 * devname_profile_update()
30 * Update matching rules determining which names to export
31 * prof_readdir()
32 * Return the list of exported names
33 * prof_lookup()
34 * Implements lookup
37 #include <sys/types.h>
38 #include <sys/param.h>
39 #include <sys/sysmacros.h>
40 #include <sys/vnode.h>
41 #include <sys/uio.h>
42 #include <sys/dirent.h>
43 #include <sys/pathname.h>
44 #include <sys/fs/dv_node.h>
45 #include <sys/fs/sdev_impl.h>
46 #include <sys/sunndi.h>
47 #include <sys/modctl.h>
49 enum {
50 PROFILE_TYPE_INCLUDE,
51 PROFILE_TYPE_EXCLUDE,
52 PROFILE_TYPE_MAP,
53 PROFILE_TYPE_SYMLINK
56 enum {
57 WALK_DIR_CONTINUE = 0,
58 WALK_DIR_TERMINATE
61 static const char *sdev_nvp_val_err = "nvpair_value error %d, %s\n";
63 static void process_rule(struct sdev_node *, struct sdev_node *,
64 char *, char *, int);
65 static void walk_dir(struct vnode *, void *, int (*)(char *, void *));
67 static void
68 prof_getattr(struct sdev_node *dir, char *name, struct vnode *gdv,
69 struct vattr *vap, struct vnode **avpp, int *no_fs_perm)
71 struct vnode *advp;
73 /* get attribute from shadow, if present; else get default */
74 advp = dir->sdev_attrvp;
75 if (advp && VOP_LOOKUP(advp, name, avpp, NULL, 0, NULL, kcred,
76 NULL, NULL, NULL) == 0) {
77 (void) VOP_GETATTR(*avpp, vap, 0, kcred, NULL);
78 } else if (gdv == NULL || gdv->v_type == VDIR) {
79 /* always create shadow directory */
80 *vap = sdev_vattr_dir;
81 if (advp && VOP_MKDIR(advp, name, &sdev_vattr_dir,
82 avpp, kcred, NULL, 0, NULL) != 0) {
83 *avpp = NULLVP;
84 sdcmn_err10(("prof_getattr: failed to create "
85 "shadow directory %s/%s\n", dir->sdev_path, name));
87 } else {
89 * get default permission from devfs
90 * Before calling devfs_get_defattr, we need to get
91 * the realvp (the dv_node). If realvp is not a dv_node,
92 * devfs_get_defattr() will return a system-wide default
93 * attr for device nodes.
95 struct vnode *rvp;
96 if (VOP_REALVP(gdv, &rvp, NULL) != 0)
97 rvp = gdv;
98 devfs_get_defattr(rvp, vap, no_fs_perm);
99 *avpp = NULLVP;
102 /* ignore dev_t and vtype from backing store */
103 if (gdv) {
104 vap->va_type = gdv->v_type;
105 vap->va_rdev = gdv->v_rdev;
109 static void
110 apply_glob_pattern(struct sdev_node *pdir, struct sdev_node *cdir)
112 char *name;
113 nvpair_t *nvp = NULL;
114 nvlist_t *nvl;
115 struct vnode *vp = SDEVTOV(cdir);
116 int rv = 0;
118 if (vp->v_type != VDIR)
119 return;
120 name = cdir->sdev_name;
121 nvl = pdir->sdev_prof.dev_glob_incdir;
122 while (nvp = nvlist_next_nvpair(nvl, nvp)) {
123 char *pathleft;
124 char *expr = nvpair_name(nvp);
125 if (!gmatch(name, expr))
126 continue;
127 rv = nvpair_value_string(nvp, &pathleft);
128 if (rv != 0) {
129 cmn_err(CE_WARN, sdev_nvp_val_err,
130 rv, nvpair_name(nvp));
131 break;
133 process_rule(cdir, cdir->sdev_origin,
134 pathleft, NULL, PROFILE_TYPE_INCLUDE);
139 * Some commonality here with sdev_mknode(), could be simplified.
140 * NOTE: prof_mknode returns with *newdv held once, if success.
142 static int
143 prof_mknode(struct sdev_node *dir, char *name, struct sdev_node **newdv,
144 vattr_t *vap, vnode_t *avp, void *arg, cred_t *cred)
146 struct sdev_node *dv;
147 int rv;
149 ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
151 /* check cache first */
152 if (dv = sdev_cache_lookup(dir, name)) {
153 *newdv = dv;
154 return (0);
157 /* allocate node and insert into cache */
158 rv = sdev_nodeinit(dir, name, &dv, NULL);
159 if (rv != 0) {
160 *newdv = NULL;
161 return (rv);
164 rv = sdev_cache_update(dir, &dv, name, SDEV_CACHE_ADD);
165 *newdv = dv;
167 /* put it in ready state */
168 rv = sdev_nodeready(*newdv, vap, avp, arg, cred);
170 /* handle glob pattern in the middle of a path */
171 if (rv == 0) {
172 if (SDEVTOV(*newdv)->v_type == VDIR)
173 sdcmn_err10(("sdev_origin for %s set to 0x%p\n",
174 name, arg));
175 apply_glob_pattern(dir, *newdv);
177 return (rv);
181 * Create a directory node in a non-global dev instance.
182 * Always create shadow vnode. Set sdev_origin to the corresponding
183 * global directory sdev_node if it exists. This facilitates the
184 * lookup operation.
186 static int
187 prof_make_dir(char *name, struct sdev_node **gdirp, struct sdev_node **dirp)
189 struct sdev_node *dir = *dirp;
190 struct sdev_node *gdir = *gdirp;
191 struct sdev_node *newdv;
192 struct vnode *avp, *gnewdir = NULL;
193 struct vattr vattr;
194 int error;
196 /* see if name already exists */
197 rw_enter(&dir->sdev_contents, RW_READER);
198 if (newdv = sdev_cache_lookup(dir, name)) {
199 *dirp = newdv;
200 *gdirp = newdv->sdev_origin;
201 SDEV_RELE(dir);
202 rw_exit(&dir->sdev_contents);
203 return (0);
205 rw_exit(&dir->sdev_contents);
207 /* find corresponding dir node in global dev */
208 if (gdir) {
209 error = VOP_LOOKUP(SDEVTOV(gdir), name, &gnewdir,
210 NULL, 0, NULL, kcred, NULL, NULL, NULL);
211 if (error == 0) {
212 *gdirp = VTOSDEV(gnewdir);
213 } else { /* it's ok if there no global dir */
214 *gdirp = NULL;
218 /* get attribute from shadow, also create shadow dir */
219 prof_getattr(dir, name, gnewdir, &vattr, &avp, NULL);
221 /* create dev directory vnode */
222 rw_enter(&dir->sdev_contents, RW_WRITER);
223 error = prof_mknode(dir, name, &newdv, &vattr, avp, (void *)*gdirp,
224 kcred);
225 rw_exit(&dir->sdev_contents);
226 if (error == 0) {
227 ASSERT(newdv);
228 *dirp = newdv;
230 SDEV_RELE(dir);
231 return (error);
235 * Look up a logical name in the global zone.
236 * Provides the ability to map the global zone's device name
237 * to an alternate name within a zone. The primary example
238 * is the virtual console device /dev/zcons/[zonename]/zconsole
239 * mapped to /[zonename]/root/dev/zconsole.
241 static void
242 prof_lookup_globaldev(struct sdev_node *dir, struct sdev_node *gdir,
243 char *name, char *rename)
245 int error;
246 struct vnode *avp, *gdv, *gddv;
247 struct sdev_node *newdv;
248 struct vattr vattr = {0};
249 struct pathname pn;
251 /* check if node already exists */
252 newdv = sdev_cache_lookup(dir, rename);
253 if (newdv) {
254 ASSERT(newdv->sdev_state != SDEV_ZOMBIE);
255 SDEV_SIMPLE_RELE(newdv);
256 return;
259 /* sanity check arguments */
260 if (!gdir || pn_get(name, UIO_SYSSPACE, &pn))
261 return;
263 /* perform a relative lookup of the global /dev instance */
264 gddv = SDEVTOV(gdir);
265 VN_HOLD(gddv);
266 error = lookuppnvp(&pn, NULL, FOLLOW, NULLVPP, &gdv,
267 rootdir, gddv, kcred);
268 pn_free(&pn);
269 if (error) {
270 sdcmn_err10(("prof_lookup_globaldev: %s not found\n", name));
271 return;
273 ASSERT(gdv && gdv->v_type != VLNK);
276 * Found the entry in global /dev, figure out attributes
277 * by looking at backing store. Call into devfs for default.
278 * Note, mapped device is persisted under the new name
280 prof_getattr(dir, rename, gdv, &vattr, &avp, NULL);
282 if (gdv->v_type != VDIR) {
283 VN_RELE(gdv);
284 gdir = NULL;
285 } else
286 gdir = VTOSDEV(gdv);
288 if (prof_mknode(dir, rename, &newdv, &vattr, avp,
289 (void *)gdir, kcred) == 0) {
290 ASSERT(newdv->sdev_state != SDEV_ZOMBIE);
291 SDEV_SIMPLE_RELE(newdv);
295 static void
296 prof_make_sym(struct sdev_node *dir, char *lnm, char *tgt)
298 struct sdev_node *newdv;
300 if (prof_mknode(dir, lnm, &newdv, &sdev_vattr_lnk, NULL,
301 (void *)tgt, kcred) == 0) {
302 ASSERT(newdv->sdev_state != SDEV_ZOMBIE);
303 SDEV_SIMPLE_RELE(newdv);
308 * Create symlinks in the current directory based on profile
310 static void
311 prof_make_symlinks(struct sdev_node *dir)
313 char *tgt, *lnm;
314 nvpair_t *nvp = NULL;
315 nvlist_t *nvl = dir->sdev_prof.dev_symlink;
316 int rv;
318 ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
320 if (nvl == NULL)
321 return;
323 while (nvp = nvlist_next_nvpair(nvl, nvp)) {
324 lnm = nvpair_name(nvp);
325 rv = nvpair_value_string(nvp, &tgt);
326 if (rv != 0) {
327 cmn_err(CE_WARN, sdev_nvp_val_err,
328 rv, nvpair_name(nvp));
329 break;
331 prof_make_sym(dir, lnm, tgt);
335 static void
336 prof_make_maps(struct sdev_node *dir)
338 nvpair_t *nvp = NULL;
339 nvlist_t *nvl = dir->sdev_prof.dev_map;
340 int rv;
342 ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
344 if (nvl == NULL)
345 return;
347 while (nvp = nvlist_next_nvpair(nvl, nvp)) {
348 char *name;
349 char *rename = nvpair_name(nvp);
350 rv = nvpair_value_string(nvp, &name);
351 if (rv != 0) {
352 cmn_err(CE_WARN, sdev_nvp_val_err,
353 rv, nvpair_name(nvp));
354 break;
356 sdcmn_err10(("map %s -> %s\n", name, rename));
357 (void) prof_lookup_globaldev(dir, sdev_origins->sdev_root,
358 name, rename);
362 struct match_arg {
363 char *expr;
364 int match;
367 static int
368 match_name(char *name, void *arg)
370 struct match_arg *margp = (struct match_arg *)arg;
372 if (gmatch(name, margp->expr)) {
373 margp->match = 1;
374 return (WALK_DIR_TERMINATE);
376 return (WALK_DIR_CONTINUE);
379 static int
380 is_nonempty_dir(char *name, char *pathleft, struct sdev_node *dir)
382 struct match_arg marg;
383 struct pathname pn;
384 struct vnode *gvp;
385 struct sdev_node *gdir = dir->sdev_origin;
387 if (VOP_LOOKUP(SDEVTOV(gdir), name, &gvp, NULL, 0, NULL, kcred,
388 NULL, NULL, NULL) != 0)
389 return (0);
391 if (gvp->v_type != VDIR) {
392 VN_RELE(gvp);
393 return (0);
396 if (pn_get(pathleft, UIO_SYSSPACE, &pn) != 0) {
397 VN_RELE(gvp);
398 return (0);
401 marg.expr = kmem_alloc(MAXNAMELEN, KM_SLEEP);
402 (void) pn_getcomponent(&pn, marg.expr);
403 marg.match = 0;
405 walk_dir(gvp, &marg, match_name);
406 VN_RELE(gvp);
407 kmem_free(marg.expr, MAXNAMELEN);
408 pn_free(&pn);
410 return (marg.match);
414 /* Check if name passes matching rules */
415 static int
416 prof_name_matched(char *name, struct sdev_node *dir)
418 int type, match = 0;
419 char *expr;
420 nvlist_t *nvl;
421 nvpair_t *nvp = NULL;
422 int rv;
424 /* check against nvlist for leaf include/exclude */
425 nvl = dir->sdev_prof.dev_name;
426 while (nvp = nvlist_next_nvpair(nvl, nvp)) {
427 expr = nvpair_name(nvp);
428 rv = nvpair_value_int32(nvp, &type);
429 if (rv != 0) {
430 cmn_err(CE_WARN, sdev_nvp_val_err,
431 rv, nvpair_name(nvp));
432 break;
435 if (type == PROFILE_TYPE_EXCLUDE) {
436 if (gmatch(name, expr))
437 return (0); /* excluded */
438 } else if (!match) {
439 match = gmatch(name, expr);
442 if (match) {
443 sdcmn_err10(("prof_name_matched: %s\n", name));
444 return (match);
447 /* check for match against directory globbing pattern */
448 nvl = dir->sdev_prof.dev_glob_incdir;
449 while (nvp = nvlist_next_nvpair(nvl, nvp)) {
450 char *pathleft;
451 expr = nvpair_name(nvp);
452 if (gmatch(name, expr) == 0)
453 continue;
454 rv = nvpair_value_string(nvp, &pathleft);
455 if (rv != 0) {
456 cmn_err(CE_WARN, sdev_nvp_val_err,
457 rv, nvpair_name(nvp));
458 break;
460 if (is_nonempty_dir(name, pathleft, dir)) {
461 sdcmn_err10(("prof_name_matched: dir %s\n", name));
462 return (1);
466 return (0);
469 static void
470 walk_dir(struct vnode *dvp, void *arg, int (*callback)(char *, void *))
472 char *nm;
473 int eof, error;
474 struct iovec iov;
475 struct uio uio;
476 struct dirent64 *dp;
477 dirent64_t *dbuf;
478 size_t dbuflen, dlen;
480 ASSERT(dvp);
482 dlen = 4096;
483 dbuf = kmem_zalloc(dlen, KM_SLEEP);
485 uio.uio_iov = &iov;
486 uio.uio_iovcnt = 1;
487 uio.uio_segflg = UIO_SYSSPACE;
488 uio.uio_fmode = 0;
489 uio.uio_extflg = UIO_COPY_CACHED;
490 uio.uio_loffset = 0;
491 uio.uio_llimit = MAXOFFSET_T;
493 eof = 0;
494 error = 0;
495 while (!error && !eof) {
496 uio.uio_resid = dlen;
497 iov.iov_base = (char *)dbuf;
498 iov.iov_len = dlen;
499 (void) VOP_RWLOCK(dvp, V_WRITELOCK_FALSE, NULL);
500 error = VOP_READDIR(dvp, &uio, kcred, &eof, NULL, 0);
501 VOP_RWUNLOCK(dvp, V_WRITELOCK_FALSE, NULL);
503 dbuflen = dlen - uio.uio_resid;
504 if (error || dbuflen == 0)
505 break;
506 for (dp = dbuf; ((intptr_t)dp <
507 (intptr_t)dbuf + dbuflen);
508 dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) {
509 nm = dp->d_name;
511 if (strcmp(nm, ".") == 0 ||
512 strcmp(nm, "..") == 0)
513 continue;
515 if (callback(nm, arg) == WALK_DIR_TERMINATE)
516 goto end;
520 end:
521 kmem_free(dbuf, dlen);
525 * Last chance for a zone to see a node. If our parent dir is
526 * SDEV_ZONED, then we look up the "zone" property for the node. If the
527 * property is found and matches the current zone name, we allow it.
528 * Note that this isn't quite correct for the global zone peeking inside
529 * a zone's /dev - for that to work, we'd have to have a per-dev-mount
530 * zone ref squirreled away.
532 static int
533 prof_zone_matched(char *name, struct sdev_node *dir)
535 vnode_t *gvn = SDEVTOV(dir->sdev_origin);
536 struct pathname pn;
537 vnode_t *vn = NULL;
538 char zonename[ZONENAME_MAX];
539 int znlen = ZONENAME_MAX;
540 int ret;
542 ASSERT((dir->sdev_flags & SDEV_ZONED) != 0);
544 sdcmn_err10(("sdev_node %p is zoned, looking for %s\n",
545 (void *)dir, name));
547 if (pn_get(name, UIO_SYSSPACE, &pn))
548 return (0);
550 VN_HOLD(gvn);
552 ret = lookuppnvp(&pn, NULL, FOLLOW, NULLVPP, &vn, rootdir, gvn, kcred);
554 pn_free(&pn);
556 if (ret != 0) {
557 sdcmn_err10(("prof_zone_matched: %s not found\n", name));
558 return (0);
562 * VBLK doesn't matter, and the property name is in fact treated
563 * as a const char *.
565 ret = e_ddi_getlongprop_buf(vn->v_rdev, VBLK, (char *)"zone",
566 DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, (caddr_t)zonename, &znlen);
568 VN_RELE(vn);
570 if (ret == DDI_PROP_NOT_FOUND) {
571 sdcmn_err10(("vnode %p: no zone prop\n", (void *)vn));
572 return (0);
573 } else if (ret != DDI_PROP_SUCCESS) {
574 sdcmn_err10(("vnode %p: zone prop error: %d\n",
575 (void *)vn, ret));
576 return (0);
579 sdcmn_err10(("vnode %p zone prop: %s\n", (void *)vn, zonename));
580 return (strcmp(zonename, curproc->p_zone->zone_name) == 0);
583 static int
584 prof_make_name_glob(char *nm, void *arg)
586 struct sdev_node *ddv = (struct sdev_node *)arg;
588 if (prof_name_matched(nm, ddv))
589 prof_lookup_globaldev(ddv, ddv->sdev_origin, nm, nm);
591 return (WALK_DIR_CONTINUE);
594 static int
595 prof_make_name_zone(char *nm, void *arg)
597 struct sdev_node *ddv = (struct sdev_node *)arg;
599 if (prof_zone_matched(nm, ddv))
600 prof_lookup_globaldev(ddv, ddv->sdev_origin, nm, nm);
602 return (WALK_DIR_CONTINUE);
605 static void
606 prof_make_names_walk(struct sdev_node *ddv, int (*cb)(char *, void *))
608 struct sdev_node *gdir;
610 gdir = ddv->sdev_origin;
611 if (gdir == NULL)
612 return;
613 walk_dir(SDEVTOV(gdir), (void *)ddv, cb);
616 static void
617 prof_make_names(struct sdev_node *dir)
619 char *name;
620 nvpair_t *nvp = NULL;
621 nvlist_t *nvl = dir->sdev_prof.dev_name;
622 int rv;
624 ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
626 if ((dir->sdev_flags & SDEV_ZONED) != 0)
627 prof_make_names_walk(dir, prof_make_name_zone);
629 if (nvl == NULL)
630 return;
632 if (dir->sdev_prof.has_glob) {
633 prof_make_names_walk(dir, prof_make_name_glob);
634 return;
637 /* Walk nvlist and lookup corresponding device in global inst */
638 while (nvp = nvlist_next_nvpair(nvl, nvp)) {
639 int type;
640 rv = nvpair_value_int32(nvp, &type);
641 if (rv != 0) {
642 cmn_err(CE_WARN, sdev_nvp_val_err,
643 rv, nvpair_name(nvp));
644 break;
646 if (type == PROFILE_TYPE_EXCLUDE)
647 continue;
648 name = nvpair_name(nvp);
649 (void) prof_lookup_globaldev(dir, dir->sdev_origin,
650 name, name);
655 * Build directory vnodes based on the profile and the global
656 * dev instance.
658 void
659 prof_filldir(struct sdev_node *ddv)
661 int firsttime = 1;
662 struct sdev_node *gdir = ddv->sdev_origin;
664 ASSERT(RW_READ_HELD(&ddv->sdev_contents));
667 * We need to rebuild the directory content if
668 * - SDEV_BUILD is set
669 * - The device tree generation number has changed
670 * - The corresponding /dev namespace has been updated
672 check_build:
673 if ((ddv->sdev_flags & SDEV_BUILD) == 0 &&
674 ddv->sdev_devtree_gen == devtree_gen &&
675 (gdir == NULL || ddv->sdev_ldir_gen
676 == gdir->sdev_gdir_gen))
677 return; /* already up to date */
679 if (firsttime && rw_tryupgrade(&ddv->sdev_contents) == 0) {
680 rw_exit(&ddv->sdev_contents);
681 firsttime = 0;
682 rw_enter(&ddv->sdev_contents, RW_WRITER);
683 goto check_build;
685 sdcmn_err10(("devtree_gen (%s): %ld -> %ld\n",
686 ddv->sdev_path, ddv->sdev_devtree_gen, devtree_gen));
687 if (gdir)
688 sdcmn_err10(("sdev_dir_gen (%s): %ld -> %ld\n",
689 ddv->sdev_path, ddv->sdev_ldir_gen,
690 gdir->sdev_gdir_gen));
692 /* update flags and generation number so next filldir is quick */
693 ddv->sdev_flags &= ~SDEV_BUILD;
694 ddv->sdev_devtree_gen = devtree_gen;
695 if (gdir)
696 ddv->sdev_ldir_gen = gdir->sdev_gdir_gen;
698 prof_make_symlinks(ddv);
699 prof_make_maps(ddv);
700 prof_make_names(ddv);
701 rw_downgrade(&ddv->sdev_contents);
704 /* apply include/exclude pattern to existing directory content */
705 static void
706 apply_dir_pattern(struct sdev_node *dir, char *expr, char *pathleft, int type)
708 struct sdev_node *dv;
710 /* leaf pattern */
711 if (pathleft == NULL) {
712 if (type == PROFILE_TYPE_INCLUDE)
713 return; /* nothing to do for include */
714 (void) sdev_cleandir(dir, expr, SDEV_ENFORCE);
715 return;
718 /* directory pattern */
719 rw_enter(&dir->sdev_contents, RW_WRITER);
721 for (dv = SDEV_FIRST_ENTRY(dir); dv; dv = SDEV_NEXT_ENTRY(dir, dv)) {
722 if (gmatch(dv->sdev_name, expr) == 0 ||
723 SDEVTOV(dv)->v_type != VDIR)
724 continue;
725 process_rule(dv, dv->sdev_origin,
726 pathleft, NULL, type);
728 rw_exit(&dir->sdev_contents);
732 * Add a profile rule.
733 * tgt represents a device name matching expression,
734 * matching device names are to be either included or excluded.
736 static void
737 prof_add_rule(char *name, char *tgt, struct sdev_node *dir, int type)
739 int error;
740 nvlist_t **nvlp = NULL;
741 int rv;
743 ASSERT(SDEVTOV(dir)->v_type == VDIR);
745 rw_enter(&dir->sdev_contents, RW_WRITER);
747 switch (type) {
748 case PROFILE_TYPE_INCLUDE:
749 if (tgt)
750 nvlp = &(dir->sdev_prof.dev_glob_incdir);
751 else
752 nvlp = &(dir->sdev_prof.dev_name);
753 break;
754 case PROFILE_TYPE_EXCLUDE:
755 if (tgt)
756 nvlp = &(dir->sdev_prof.dev_glob_excdir);
757 else
758 nvlp = &(dir->sdev_prof.dev_name);
759 break;
760 case PROFILE_TYPE_MAP:
761 nvlp = &(dir->sdev_prof.dev_map);
762 break;
763 case PROFILE_TYPE_SYMLINK:
764 nvlp = &(dir->sdev_prof.dev_symlink);
765 break;
768 /* initialize nvlist */
769 if (*nvlp == NULL) {
770 error = nvlist_alloc(nvlp, NV_UNIQUE_NAME, KM_SLEEP);
771 ASSERT(error == 0);
774 if (tgt) {
775 rv = nvlist_add_string(*nvlp, name, tgt);
776 } else {
777 rv = nvlist_add_int32(*nvlp, name, type);
779 ASSERT(rv == 0);
780 /* rebuild directory content */
781 dir->sdev_flags |= SDEV_BUILD;
783 if ((type == PROFILE_TYPE_INCLUDE) &&
784 (strpbrk(name, "*?[]") != NULL)) {
785 dir->sdev_prof.has_glob = 1;
788 rw_exit(&dir->sdev_contents);
790 /* additional details for glob pattern and exclusion */
791 switch (type) {
792 case PROFILE_TYPE_INCLUDE:
793 case PROFILE_TYPE_EXCLUDE:
794 apply_dir_pattern(dir, name, tgt, type);
795 break;
800 * Parse path components and apply requested matching rule at
801 * directory level.
803 static void
804 process_rule(struct sdev_node *dir, struct sdev_node *gdir,
805 char *path, char *tgt, int type)
807 char *name;
808 struct pathname pn;
809 int rv = 0;
811 if ((strlen(path) > 5) && (strncmp(path, "/dev/", 5) == 0)) {
812 path += 5;
815 if (pn_get(path, UIO_SYSSPACE, &pn) != 0)
816 return;
818 name = kmem_alloc(MAXPATHLEN, KM_SLEEP);
819 (void) pn_getcomponent(&pn, name);
820 pn_skipslash(&pn);
821 SDEV_HOLD(dir);
823 while (pn_pathleft(&pn)) {
824 /* If this is pattern, just add the pattern */
825 if (strpbrk(name, "*?[]") != NULL &&
826 (type == PROFILE_TYPE_INCLUDE ||
827 type == PROFILE_TYPE_EXCLUDE)) {
828 ASSERT(tgt == NULL);
829 tgt = pn.pn_path;
830 break;
832 if ((rv = prof_make_dir(name, &gdir, &dir)) != 0) {
833 cmn_err(CE_CONT, "process_rule: %s error %d\n",
834 path, rv);
835 break;
837 (void) pn_getcomponent(&pn, name);
838 pn_skipslash(&pn);
841 /* process the leaf component */
842 if (rv == 0) {
843 prof_add_rule(name, tgt, dir, type);
844 SDEV_SIMPLE_RELE(dir);
847 kmem_free(name, MAXPATHLEN);
848 pn_free(&pn);
851 static int
852 copyin_nvlist(char *packed_usr, size_t packed_sz, nvlist_t **nvlp)
854 int err = 0;
855 char *packed;
856 nvlist_t *profile = NULL;
858 /* simple sanity check */
859 if (packed_usr == NULL || packed_sz == 0)
860 return (NULL);
862 /* copyin packed profile nvlist */
863 packed = kmem_alloc(packed_sz, KM_NOSLEEP);
864 if (packed == NULL)
865 return (ENOMEM);
866 err = copyin(packed_usr, packed, packed_sz);
868 /* unpack packed profile nvlist */
869 if (err)
870 cmn_err(CE_WARN, "copyin_nvlist: copyin failed with "
871 "err %d\n", err);
872 else if (err = nvlist_unpack(packed, packed_sz, &profile, KM_NOSLEEP))
873 cmn_err(CE_WARN, "copyin_nvlist: nvlist_unpack "
874 "failed with err %d\n", err);
876 kmem_free(packed, packed_sz);
877 if (err == 0)
878 *nvlp = profile;
879 return (err);
883 * Process profile passed down from libdevinfo. There are four types
884 * of matching rules:
885 * include: export a name or names matching a pattern
886 * exclude: exclude a name or names matching a pattern
887 * symlink: create a local symlink
888 * map: export a device with a name different from the global zone
889 * Note: We may consider supporting VOP_SYMLINK in non-global instances,
890 * because it does not present any security risk. For now, the fs
891 * instance is read only.
893 static void
894 sdev_process_profile(struct sdev_data *sdev_data, nvlist_t *profile)
896 nvpair_t *nvpair;
897 char *nvname, *dname;
898 struct sdev_node *dir, *gdir;
899 char **pair; /* for symlinks and maps */
900 uint_t nelem;
901 int rv;
903 gdir = sdev_origins->sdev_root; /* root of global /dev */
904 dir = sdev_data->sdev_root; /* root of current instance */
906 ASSERT(profile);
908 /* process nvpairs in the list */
909 nvpair = NULL;
910 while (nvpair = nvlist_next_nvpair(profile, nvpair)) {
911 nvname = nvpair_name(nvpair);
912 ASSERT(nvname != NULL);
914 if (strcmp(nvname, SDEV_NVNAME_INCLUDE) == 0) {
915 rv = nvpair_value_string(nvpair, &dname);
916 if (rv != 0) {
917 cmn_err(CE_WARN, sdev_nvp_val_err,
918 rv, nvpair_name(nvpair));
919 break;
921 process_rule(dir, gdir, dname, NULL,
922 PROFILE_TYPE_INCLUDE);
923 } else if (strcmp(nvname, SDEV_NVNAME_EXCLUDE) == 0) {
924 rv = nvpair_value_string(nvpair, &dname);
925 if (rv != 0) {
926 cmn_err(CE_WARN, sdev_nvp_val_err,
927 rv, nvpair_name(nvpair));
928 break;
930 process_rule(dir, gdir, dname, NULL,
931 PROFILE_TYPE_EXCLUDE);
932 } else if (strcmp(nvname, SDEV_NVNAME_SYMLINK) == 0) {
933 rv = nvpair_value_string_array(nvpair, &pair, &nelem);
934 if (rv != 0) {
935 cmn_err(CE_WARN, sdev_nvp_val_err,
936 rv, nvpair_name(nvpair));
937 break;
939 ASSERT(nelem == 2);
940 process_rule(dir, gdir, pair[0], pair[1],
941 PROFILE_TYPE_SYMLINK);
942 } else if (strcmp(nvname, SDEV_NVNAME_MAP) == 0) {
943 rv = nvpair_value_string_array(nvpair, &pair, &nelem);
944 if (rv != 0) {
945 cmn_err(CE_WARN, sdev_nvp_val_err,
946 rv, nvpair_name(nvpair));
947 break;
949 process_rule(dir, gdir, pair[1], pair[0],
950 PROFILE_TYPE_MAP);
951 } else if (strcmp(nvname, SDEV_NVNAME_MOUNTPT) != 0) {
952 cmn_err(CE_WARN, "sdev_process_profile: invalid "
953 "nvpair %s\n", nvname);
958 /*ARGSUSED*/
960 prof_lookup(vnode_t *dvp, char *nm, struct vnode **vpp, struct cred *cred)
962 struct sdev_node *ddv = VTOSDEV(dvp);
963 struct sdev_node *dv;
964 int nmlen;
967 * Empty name or ., return node itself.
969 nmlen = strlen(nm);
970 if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) {
971 *vpp = SDEVTOV(ddv);
972 VN_HOLD(*vpp);
973 return (0);
977 * .., return the parent directory
979 if ((nmlen == 2) && (strcmp(nm, "..") == 0)) {
980 *vpp = SDEVTOV(ddv->sdev_dotdot);
981 VN_HOLD(*vpp);
982 return (0);
985 rw_enter(&ddv->sdev_contents, RW_READER);
986 dv = sdev_cache_lookup(ddv, nm);
987 if (dv == NULL) {
988 prof_filldir(ddv);
989 dv = sdev_cache_lookup(ddv, nm);
991 rw_exit(&ddv->sdev_contents);
992 if (dv == NULL) {
993 sdcmn_err10(("prof_lookup: %s not found\n", nm));
994 return (ENOENT);
997 return (sdev_to_vp(dv, vpp));
1001 * This is invoked after a new filesystem is mounted to define the
1002 * name space. It is also invoked during normal system operation
1003 * to update the name space.
1005 * Applications call di_prof_commit() in libdevinfo, which invokes
1006 * modctl(). modctl calls this function. The input is a packed nvlist.
1009 devname_profile_update(char *packed, size_t packed_sz)
1011 char *mntpt;
1012 nvlist_t *nvl;
1013 nvpair_t *nvp;
1014 struct sdev_data *mntinfo;
1015 int err;
1016 int rv;
1018 nvl = NULL;
1019 if ((err = copyin_nvlist(packed, packed_sz, &nvl)) != 0)
1020 return (err);
1021 ASSERT(nvl);
1023 /* The first nvpair must be the mount point */
1024 nvp = nvlist_next_nvpair(nvl, NULL);
1025 if (strcmp(nvpair_name(nvp), SDEV_NVNAME_MOUNTPT) != 0) {
1026 cmn_err(CE_NOTE,
1027 "devname_profile_update: mount point not specified");
1028 nvlist_free(nvl);
1029 return (EINVAL);
1032 /* find the matching filesystem instance */
1033 rv = nvpair_value_string(nvp, &mntpt);
1034 if (rv != 0) {
1035 cmn_err(CE_WARN, sdev_nvp_val_err,
1036 rv, nvpair_name(nvp));
1037 } else {
1038 mntinfo = sdev_find_mntinfo(mntpt);
1039 if (mntinfo == NULL) {
1040 cmn_err(CE_NOTE, "devname_profile_update: "
1041 " mount point %s not found", mntpt);
1042 nvlist_free(nvl);
1043 return (EINVAL);
1046 /* now do the hardwork to process the profile */
1047 sdev_process_profile(mntinfo, nvl);
1049 sdev_mntinfo_rele(mntinfo);
1052 nvlist_free(nvl);
1053 return (0);