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]
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
32 * Return the list of exported names
37 #include <sys/types.h>
38 #include <sys/param.h>
39 #include <sys/sysmacros.h>
40 #include <sys/vnode.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>
57 WALK_DIR_CONTINUE
= 0,
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
*,
65 static void walk_dir(struct vnode
*, void *, int (*)(char *, void *));
68 prof_getattr(struct sdev_node
*dir
, char *name
, struct vnode
*gdv
,
69 struct vattr
*vap
, struct vnode
**avpp
, int *no_fs_perm
)
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) {
84 sdcmn_err10(("prof_getattr: failed to create "
85 "shadow directory %s/%s\n", dir
->sdev_path
, name
));
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.
96 if (VOP_REALVP(gdv
, &rvp
, NULL
) != 0)
98 devfs_get_defattr(rvp
, vap
, no_fs_perm
);
102 /* ignore dev_t and vtype from backing store */
104 vap
->va_type
= gdv
->v_type
;
105 vap
->va_rdev
= gdv
->v_rdev
;
110 apply_glob_pattern(struct sdev_node
*pdir
, struct sdev_node
*cdir
)
113 nvpair_t
*nvp
= NULL
;
115 struct vnode
*vp
= SDEVTOV(cdir
);
118 if (vp
->v_type
!= VDIR
)
120 name
= cdir
->sdev_name
;
121 nvl
= pdir
->sdev_prof
.dev_glob_incdir
;
122 while (nvp
= nvlist_next_nvpair(nvl
, nvp
)) {
124 char *expr
= nvpair_name(nvp
);
125 if (!gmatch(name
, expr
))
127 rv
= nvpair_value_string(nvp
, &pathleft
);
129 cmn_err(CE_WARN
, sdev_nvp_val_err
,
130 rv
, nvpair_name(nvp
));
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.
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
;
149 ASSERT(RW_WRITE_HELD(&dir
->sdev_contents
));
151 /* check cache first */
152 if (dv
= sdev_cache_lookup(dir
, name
)) {
157 /* allocate node and insert into cache */
158 rv
= sdev_nodeinit(dir
, name
, &dv
, NULL
);
164 rv
= sdev_cache_update(dir
, &dv
, name
, SDEV_CACHE_ADD
);
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 */
172 if (SDEVTOV(*newdv
)->v_type
== VDIR
)
173 sdcmn_err10(("sdev_origin for %s set to 0x%p\n",
175 apply_glob_pattern(dir
, *newdv
);
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
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
;
196 /* see if name already exists */
197 rw_enter(&dir
->sdev_contents
, RW_READER
);
198 if (newdv
= sdev_cache_lookup(dir
, name
)) {
200 *gdirp
= newdv
->sdev_origin
;
202 rw_exit(&dir
->sdev_contents
);
205 rw_exit(&dir
->sdev_contents
);
207 /* find corresponding dir node in global dev */
209 error
= VOP_LOOKUP(SDEVTOV(gdir
), name
, &gnewdir
,
210 NULL
, 0, NULL
, kcred
, NULL
, NULL
, NULL
);
212 *gdirp
= VTOSDEV(gnewdir
);
213 } else { /* it's ok if there no global dir */
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
,
225 rw_exit(&dir
->sdev_contents
);
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.
242 prof_lookup_globaldev(struct sdev_node
*dir
, struct sdev_node
*gdir
,
243 char *name
, char *rename
)
246 struct vnode
*avp
, *gdv
, *gddv
;
247 struct sdev_node
*newdv
;
248 struct vattr vattr
= {0};
251 /* check if node already exists */
252 newdv
= sdev_cache_lookup(dir
, rename
);
254 ASSERT(newdv
->sdev_state
!= SDEV_ZOMBIE
);
255 SDEV_SIMPLE_RELE(newdv
);
259 /* sanity check arguments */
260 if (!gdir
|| pn_get(name
, UIO_SYSSPACE
, &pn
))
263 /* perform a relative lookup of the global /dev instance */
264 gddv
= SDEVTOV(gdir
);
266 error
= lookuppnvp(&pn
, NULL
, FOLLOW
, NULLVPP
, &gdv
,
267 rootdir
, gddv
, kcred
);
270 sdcmn_err10(("prof_lookup_globaldev: %s not found\n", name
));
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
) {
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
);
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
311 prof_make_symlinks(struct sdev_node
*dir
)
314 nvpair_t
*nvp
= NULL
;
315 nvlist_t
*nvl
= dir
->sdev_prof
.dev_symlink
;
318 ASSERT(RW_WRITE_HELD(&dir
->sdev_contents
));
323 while (nvp
= nvlist_next_nvpair(nvl
, nvp
)) {
324 lnm
= nvpair_name(nvp
);
325 rv
= nvpair_value_string(nvp
, &tgt
);
327 cmn_err(CE_WARN
, sdev_nvp_val_err
,
328 rv
, nvpair_name(nvp
));
331 prof_make_sym(dir
, lnm
, tgt
);
336 prof_make_maps(struct sdev_node
*dir
)
338 nvpair_t
*nvp
= NULL
;
339 nvlist_t
*nvl
= dir
->sdev_prof
.dev_map
;
342 ASSERT(RW_WRITE_HELD(&dir
->sdev_contents
));
347 while (nvp
= nvlist_next_nvpair(nvl
, nvp
)) {
349 char *rename
= nvpair_name(nvp
);
350 rv
= nvpair_value_string(nvp
, &name
);
352 cmn_err(CE_WARN
, sdev_nvp_val_err
,
353 rv
, nvpair_name(nvp
));
356 sdcmn_err10(("map %s -> %s\n", name
, rename
));
357 (void) prof_lookup_globaldev(dir
, sdev_origins
->sdev_root
,
368 match_name(char *name
, void *arg
)
370 struct match_arg
*margp
= (struct match_arg
*)arg
;
372 if (gmatch(name
, margp
->expr
)) {
374 return (WALK_DIR_TERMINATE
);
376 return (WALK_DIR_CONTINUE
);
380 is_nonempty_dir(char *name
, char *pathleft
, struct sdev_node
*dir
)
382 struct match_arg marg
;
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)
391 if (gvp
->v_type
!= VDIR
) {
396 if (pn_get(pathleft
, UIO_SYSSPACE
, &pn
) != 0) {
401 marg
.expr
= kmem_alloc(MAXNAMELEN
, KM_SLEEP
);
402 (void) pn_getcomponent(&pn
, marg
.expr
);
405 walk_dir(gvp
, &marg
, match_name
);
407 kmem_free(marg
.expr
, MAXNAMELEN
);
414 /* Check if name passes matching rules */
416 prof_name_matched(char *name
, struct sdev_node
*dir
)
421 nvpair_t
*nvp
= NULL
;
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
);
430 cmn_err(CE_WARN
, sdev_nvp_val_err
,
431 rv
, nvpair_name(nvp
));
435 if (type
== PROFILE_TYPE_EXCLUDE
) {
436 if (gmatch(name
, expr
))
437 return (0); /* excluded */
439 match
= gmatch(name
, expr
);
443 sdcmn_err10(("prof_name_matched: %s\n", name
));
447 /* check for match against directory globbing pattern */
448 nvl
= dir
->sdev_prof
.dev_glob_incdir
;
449 while (nvp
= nvlist_next_nvpair(nvl
, nvp
)) {
451 expr
= nvpair_name(nvp
);
452 if (gmatch(name
, expr
) == 0)
454 rv
= nvpair_value_string(nvp
, &pathleft
);
456 cmn_err(CE_WARN
, sdev_nvp_val_err
,
457 rv
, nvpair_name(nvp
));
460 if (is_nonempty_dir(name
, pathleft
, dir
)) {
461 sdcmn_err10(("prof_name_matched: dir %s\n", name
));
470 walk_dir(struct vnode
*dvp
, void *arg
, int (*callback
)(char *, void *))
478 size_t dbuflen
, dlen
;
483 dbuf
= kmem_zalloc(dlen
, KM_SLEEP
);
487 uio
.uio_segflg
= UIO_SYSSPACE
;
489 uio
.uio_extflg
= UIO_COPY_CACHED
;
491 uio
.uio_llimit
= MAXOFFSET_T
;
495 while (!error
&& !eof
) {
496 uio
.uio_resid
= dlen
;
497 iov
.iov_base
= (char *)dbuf
;
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)
506 for (dp
= dbuf
; ((intptr_t)dp
<
507 (intptr_t)dbuf
+ dbuflen
);
508 dp
= (dirent64_t
*)((intptr_t)dp
+ dp
->d_reclen
)) {
511 if (strcmp(nm
, ".") == 0 ||
512 strcmp(nm
, "..") == 0)
515 if (callback(nm
, arg
) == WALK_DIR_TERMINATE
)
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.
533 prof_zone_matched(char *name
, struct sdev_node
*dir
)
535 vnode_t
*gvn
= SDEVTOV(dir
->sdev_origin
);
538 char zonename
[ZONENAME_MAX
];
539 int znlen
= ZONENAME_MAX
;
542 ASSERT((dir
->sdev_flags
& SDEV_ZONED
) != 0);
544 sdcmn_err10(("sdev_node %p is zoned, looking for %s\n",
547 if (pn_get(name
, UIO_SYSSPACE
, &pn
))
552 ret
= lookuppnvp(&pn
, NULL
, FOLLOW
, NULLVPP
, &vn
, rootdir
, gvn
, kcred
);
557 sdcmn_err10(("prof_zone_matched: %s not found\n", name
));
562 * VBLK doesn't matter, and the property name is in fact treated
565 ret
= e_ddi_getlongprop_buf(vn
->v_rdev
, VBLK
, (char *)"zone",
566 DDI_PROP_NOTPROM
| DDI_PROP_DONTPASS
, (caddr_t
)zonename
, &znlen
);
570 if (ret
== DDI_PROP_NOT_FOUND
) {
571 sdcmn_err10(("vnode %p: no zone prop\n", (void *)vn
));
573 } else if (ret
!= DDI_PROP_SUCCESS
) {
574 sdcmn_err10(("vnode %p: zone prop error: %d\n",
579 sdcmn_err10(("vnode %p zone prop: %s\n", (void *)vn
, zonename
));
580 return (strcmp(zonename
, curproc
->p_zone
->zone_name
) == 0);
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
);
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
);
606 prof_make_names_walk(struct sdev_node
*ddv
, int (*cb
)(char *, void *))
608 struct sdev_node
*gdir
;
610 gdir
= ddv
->sdev_origin
;
613 walk_dir(SDEVTOV(gdir
), (void *)ddv
, cb
);
617 prof_make_names(struct sdev_node
*dir
)
620 nvpair_t
*nvp
= NULL
;
621 nvlist_t
*nvl
= dir
->sdev_prof
.dev_name
;
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
);
632 if (dir
->sdev_prof
.has_glob
) {
633 prof_make_names_walk(dir
, prof_make_name_glob
);
637 /* Walk nvlist and lookup corresponding device in global inst */
638 while (nvp
= nvlist_next_nvpair(nvl
, nvp
)) {
640 rv
= nvpair_value_int32(nvp
, &type
);
642 cmn_err(CE_WARN
, sdev_nvp_val_err
,
643 rv
, nvpair_name(nvp
));
646 if (type
== PROFILE_TYPE_EXCLUDE
)
648 name
= nvpair_name(nvp
);
649 (void) prof_lookup_globaldev(dir
, dir
->sdev_origin
,
655 * Build directory vnodes based on the profile and the global
659 prof_filldir(struct sdev_node
*ddv
)
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
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
);
682 rw_enter(&ddv
->sdev_contents
, RW_WRITER
);
685 sdcmn_err10(("devtree_gen (%s): %ld -> %ld\n",
686 ddv
->sdev_path
, ddv
->sdev_devtree_gen
, devtree_gen
));
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
;
696 ddv
->sdev_ldir_gen
= gdir
->sdev_gdir_gen
;
698 prof_make_symlinks(ddv
);
700 prof_make_names(ddv
);
701 rw_downgrade(&ddv
->sdev_contents
);
704 /* apply include/exclude pattern to existing directory content */
706 apply_dir_pattern(struct sdev_node
*dir
, char *expr
, char *pathleft
, int type
)
708 struct sdev_node
*dv
;
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
);
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
)
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.
737 prof_add_rule(char *name
, char *tgt
, struct sdev_node
*dir
, int type
)
740 nvlist_t
**nvlp
= NULL
;
743 ASSERT(SDEVTOV(dir
)->v_type
== VDIR
);
745 rw_enter(&dir
->sdev_contents
, RW_WRITER
);
748 case PROFILE_TYPE_INCLUDE
:
750 nvlp
= &(dir
->sdev_prof
.dev_glob_incdir
);
752 nvlp
= &(dir
->sdev_prof
.dev_name
);
754 case PROFILE_TYPE_EXCLUDE
:
756 nvlp
= &(dir
->sdev_prof
.dev_glob_excdir
);
758 nvlp
= &(dir
->sdev_prof
.dev_name
);
760 case PROFILE_TYPE_MAP
:
761 nvlp
= &(dir
->sdev_prof
.dev_map
);
763 case PROFILE_TYPE_SYMLINK
:
764 nvlp
= &(dir
->sdev_prof
.dev_symlink
);
768 /* initialize nvlist */
770 error
= nvlist_alloc(nvlp
, NV_UNIQUE_NAME
, KM_SLEEP
);
775 rv
= nvlist_add_string(*nvlp
, name
, tgt
);
777 rv
= nvlist_add_int32(*nvlp
, name
, type
);
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 */
792 case PROFILE_TYPE_INCLUDE
:
793 case PROFILE_TYPE_EXCLUDE
:
794 apply_dir_pattern(dir
, name
, tgt
, type
);
800 * Parse path components and apply requested matching rule at
804 process_rule(struct sdev_node
*dir
, struct sdev_node
*gdir
,
805 char *path
, char *tgt
, int type
)
811 if ((strlen(path
) > 5) && (strncmp(path
, "/dev/", 5) == 0)) {
815 if (pn_get(path
, UIO_SYSSPACE
, &pn
) != 0)
818 name
= kmem_alloc(MAXPATHLEN
, KM_SLEEP
);
819 (void) pn_getcomponent(&pn
, name
);
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
)) {
832 if ((rv
= prof_make_dir(name
, &gdir
, &dir
)) != 0) {
833 cmn_err(CE_CONT
, "process_rule: %s error %d\n",
837 (void) pn_getcomponent(&pn
, name
);
841 /* process the leaf component */
843 prof_add_rule(name
, tgt
, dir
, type
);
844 SDEV_SIMPLE_RELE(dir
);
847 kmem_free(name
, MAXPATHLEN
);
852 copyin_nvlist(char *packed_usr
, size_t packed_sz
, nvlist_t
**nvlp
)
856 nvlist_t
*profile
= NULL
;
858 /* simple sanity check */
859 if (packed_usr
== NULL
|| packed_sz
== 0)
862 /* copyin packed profile nvlist */
863 packed
= kmem_alloc(packed_sz
, KM_NOSLEEP
);
866 err
= copyin(packed_usr
, packed
, packed_sz
);
868 /* unpack packed profile nvlist */
870 cmn_err(CE_WARN
, "copyin_nvlist: copyin failed with "
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
);
883 * Process profile passed down from libdevinfo. There are four types
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.
894 sdev_process_profile(struct sdev_data
*sdev_data
, nvlist_t
*profile
)
897 char *nvname
, *dname
;
898 struct sdev_node
*dir
, *gdir
;
899 char **pair
; /* for symlinks and maps */
903 gdir
= sdev_origins
->sdev_root
; /* root of global /dev */
904 dir
= sdev_data
->sdev_root
; /* root of current instance */
908 /* process nvpairs in the list */
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
);
917 cmn_err(CE_WARN
, sdev_nvp_val_err
,
918 rv
, nvpair_name(nvpair
));
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
);
926 cmn_err(CE_WARN
, sdev_nvp_val_err
,
927 rv
, nvpair_name(nvpair
));
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
);
935 cmn_err(CE_WARN
, sdev_nvp_val_err
,
936 rv
, nvpair_name(nvpair
));
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
);
945 cmn_err(CE_WARN
, sdev_nvp_val_err
,
946 rv
, nvpair_name(nvpair
));
949 process_rule(dir
, gdir
, pair
[1], pair
[0],
951 } else if (strcmp(nvname
, SDEV_NVNAME_MOUNTPT
) != 0) {
952 cmn_err(CE_WARN
, "sdev_process_profile: invalid "
953 "nvpair %s\n", nvname
);
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
;
967 * Empty name or ., return node itself.
970 if ((nmlen
== 0) || ((nmlen
== 1) && (nm
[0] == '.'))) {
977 * .., return the parent directory
979 if ((nmlen
== 2) && (strcmp(nm
, "..") == 0)) {
980 *vpp
= SDEVTOV(ddv
->sdev_dotdot
);
985 rw_enter(&ddv
->sdev_contents
, RW_READER
);
986 dv
= sdev_cache_lookup(ddv
, nm
);
989 dv
= sdev_cache_lookup(ddv
, nm
);
991 rw_exit(&ddv
->sdev_contents
);
993 sdcmn_err10(("prof_lookup: %s not found\n", nm
));
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
)
1014 struct sdev_data
*mntinfo
;
1019 if ((err
= copyin_nvlist(packed
, packed_sz
, &nvl
)) != 0)
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) {
1027 "devname_profile_update: mount point not specified");
1032 /* find the matching filesystem instance */
1033 rv
= nvpair_value_string(nvp
, &mntpt
);
1035 cmn_err(CE_WARN
, sdev_nvp_val_err
,
1036 rv
, nvpair_name(nvp
));
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
);
1046 /* now do the hardwork to process the profile */
1047 sdev_process_profile(mntinfo
, nvl
);
1049 sdev_mntinfo_rele(mntinfo
);