12621Sllai1 /*
22621Sllai1 * CDDL HEADER START
32621Sllai1 *
42621Sllai1 * The contents of this file are subject to the terms of the
52621Sllai1 * Common Development and Distribution License (the "License").
62621Sllai1 * You may not use this file except in compliance with the License.
72621Sllai1 *
82621Sllai1 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92621Sllai1 * or http://www.opensolaris.org/os/licensing.
102621Sllai1 * See the License for the specific language governing permissions
112621Sllai1 * and limitations under the License.
122621Sllai1 *
132621Sllai1 * When distributing Covered Code, include this CDDL HEADER in each
142621Sllai1 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152621Sllai1 * If applicable, add the following below this CDDL HEADER, with the
162621Sllai1 * fields enclosed by brackets "[]" replaced with your own identifying
172621Sllai1 * information: Portions Copyright [yyyy] [name of copyright owner]
182621Sllai1 *
192621Sllai1 * CDDL HEADER END
202621Sllai1 */
212621Sllai1
222621Sllai1 /*
23*12633Sjohn.levon@sun.com * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
242621Sllai1 */
252621Sllai1
262621Sllai1 /*
272621Sllai1 * This file implements /dev filesystem operations for non-global
282621Sllai1 * instances. Three major entry points:
292621Sllai1 * devname_profile_update()
302621Sllai1 * Update matching rules determining which names to export
312621Sllai1 * prof_readdir()
322621Sllai1 * Return the list of exported names
332621Sllai1 * prof_lookup()
342621Sllai1 * Implements lookup
352621Sllai1 */
362621Sllai1
372621Sllai1 #include <sys/types.h>
382621Sllai1 #include <sys/param.h>
392621Sllai1 #include <sys/sysmacros.h>
402621Sllai1 #include <sys/vnode.h>
412621Sllai1 #include <sys/uio.h>
422621Sllai1 #include <sys/dirent.h>
432621Sllai1 #include <sys/pathname.h>
442621Sllai1 #include <sys/fs/dv_node.h>
452621Sllai1 #include <sys/fs/sdev_impl.h>
462621Sllai1 #include <sys/sunndi.h>
472621Sllai1 #include <sys/modctl.h>
482621Sllai1
492621Sllai1 enum {
502621Sllai1 PROFILE_TYPE_INCLUDE,
512621Sllai1 PROFILE_TYPE_EXCLUDE,
522621Sllai1 PROFILE_TYPE_MAP,
532621Sllai1 PROFILE_TYPE_SYMLINK
542621Sllai1 };
552621Sllai1
562621Sllai1 enum {
572621Sllai1 WALK_DIR_CONTINUE = 0,
582621Sllai1 WALK_DIR_TERMINATE
592621Sllai1 };
602621Sllai1
612621Sllai1 static const char *sdev_nvp_val_err = "nvpair_value error %d, %s\n";
622621Sllai1
632621Sllai1 static void process_rule(struct sdev_node *, struct sdev_node *,
642621Sllai1 char *, char *, int);
652621Sllai1 static void walk_dir(struct vnode *, void *, int (*)(char *, void *));
662621Sllai1
672621Sllai1 static void
prof_getattr(struct sdev_node * dir,char * name,struct vnode * gdv,struct vattr * vap,struct vnode ** avpp,int * no_fs_perm)682621Sllai1 prof_getattr(struct sdev_node *dir, char *name, struct vnode *gdv,
692621Sllai1 struct vattr *vap, struct vnode **avpp, int *no_fs_perm)
702621Sllai1 {
712621Sllai1 struct vnode *advp;
722621Sllai1
732621Sllai1 /* get attribute from shadow, if present; else get default */
742621Sllai1 advp = dir->sdev_attrvp;
755331Samw if (advp && VOP_LOOKUP(advp, name, avpp, NULL, 0, NULL, kcred,
765331Samw NULL, NULL, NULL) == 0) {
775331Samw (void) VOP_GETATTR(*avpp, vap, 0, kcred, NULL);
782621Sllai1 } else if (gdv == NULL || gdv->v_type == VDIR) {
792621Sllai1 /* always create shadow directory */
802621Sllai1 *vap = sdev_vattr_dir;
815331Samw if (advp && VOP_MKDIR(advp, name, &sdev_vattr_dir,
825331Samw avpp, kcred, NULL, 0, NULL) != 0) {
832621Sllai1 *avpp = NULLVP;
842621Sllai1 sdcmn_err10(("prof_getattr: failed to create "
852621Sllai1 "shadow directory %s/%s\n", dir->sdev_path, name));
862621Sllai1 }
872621Sllai1 } else {
882621Sllai1 /*
892621Sllai1 * get default permission from devfs
902621Sllai1 * Before calling devfs_get_defattr, we need to get
912621Sllai1 * the realvp (the dv_node). If realvp is not a dv_node,
922621Sllai1 * devfs_get_defattr() will return a system-wide default
932621Sllai1 * attr for device nodes.
942621Sllai1 */
952621Sllai1 struct vnode *rvp;
965331Samw if (VOP_REALVP(gdv, &rvp, NULL) != 0)
972621Sllai1 rvp = gdv;
982621Sllai1 devfs_get_defattr(rvp, vap, no_fs_perm);
992621Sllai1 *avpp = NULLVP;
1002621Sllai1 }
1012621Sllai1
1022621Sllai1 /* ignore dev_t and vtype from backing store */
1032621Sllai1 if (gdv) {
1042621Sllai1 vap->va_type = gdv->v_type;
1052621Sllai1 vap->va_rdev = gdv->v_rdev;
1062621Sllai1 }
1072621Sllai1 }
1082621Sllai1
1092621Sllai1 static void
apply_glob_pattern(struct sdev_node * pdir,struct sdev_node * cdir)1102621Sllai1 apply_glob_pattern(struct sdev_node *pdir, struct sdev_node *cdir)
1112621Sllai1 {
1122621Sllai1 char *name;
1132621Sllai1 nvpair_t *nvp = NULL;
1142621Sllai1 nvlist_t *nvl;
1152621Sllai1 struct vnode *vp = SDEVTOV(cdir);
1162621Sllai1 int rv = 0;
1172621Sllai1
1182621Sllai1 if (vp->v_type != VDIR)
1192621Sllai1 return;
1202621Sllai1 name = cdir->sdev_name;
1212621Sllai1 nvl = pdir->sdev_prof.dev_glob_incdir;
1222621Sllai1 while (nvp = nvlist_next_nvpair(nvl, nvp)) {
1232621Sllai1 char *pathleft;
1242621Sllai1 char *expr = nvpair_name(nvp);
1252621Sllai1 if (!gmatch(name, expr))
1262621Sllai1 continue;
1272621Sllai1 rv = nvpair_value_string(nvp, &pathleft);
1282621Sllai1 if (rv != 0) {
1292621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err,
1302621Sllai1 rv, nvpair_name(nvp));
1312621Sllai1 break;
1322621Sllai1 }
1332621Sllai1 process_rule(cdir, cdir->sdev_origin,
1342621Sllai1 pathleft, NULL, PROFILE_TYPE_INCLUDE);
1352621Sllai1 }
1362621Sllai1 }
1372621Sllai1
1382621Sllai1 /*
1392621Sllai1 * Some commonality here with sdev_mknode(), could be simplified.
1402621Sllai1 * NOTE: prof_mknode returns with *newdv held once, if success.
1412621Sllai1 */
1422621Sllai1 static int
prof_mknode(struct sdev_node * dir,char * name,struct sdev_node ** newdv,vattr_t * vap,vnode_t * avp,void * arg,cred_t * cred)1432621Sllai1 prof_mknode(struct sdev_node *dir, char *name, struct sdev_node **newdv,
1442621Sllai1 vattr_t *vap, vnode_t *avp, void *arg, cred_t *cred)
1452621Sllai1 {
1462621Sllai1 struct sdev_node *dv;
1472621Sllai1 int rv;
1482621Sllai1
1492621Sllai1 ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
1502621Sllai1
1512621Sllai1 /* check cache first */
1522621Sllai1 if (dv = sdev_cache_lookup(dir, name)) {
1532621Sllai1 *newdv = dv;
1542621Sllai1 return (0);
1552621Sllai1 }
1562621Sllai1
1572621Sllai1 /* allocate node and insert into cache */
1582621Sllai1 rv = sdev_nodeinit(dir, name, &dv, NULL);
1592621Sllai1 if (rv != 0) {
1602621Sllai1 *newdv = NULL;
1612621Sllai1 return (rv);
1622621Sllai1 }
1632621Sllai1
1642621Sllai1 rv = sdev_cache_update(dir, &dv, name, SDEV_CACHE_ADD);
1652621Sllai1 *newdv = dv;
1662621Sllai1
1672621Sllai1 /* put it in ready state */
1682621Sllai1 rv = sdev_nodeready(*newdv, vap, avp, arg, cred);
1692621Sllai1
1702621Sllai1 /* handle glob pattern in the middle of a path */
1712621Sllai1 if (rv == 0) {
1722621Sllai1 if (SDEVTOV(*newdv)->v_type == VDIR)
1732621Sllai1 sdcmn_err10(("sdev_origin for %s set to 0x%p\n",
1742621Sllai1 name, arg));
1752621Sllai1 apply_glob_pattern(dir, *newdv);
1762621Sllai1 }
1772621Sllai1 return (rv);
1782621Sllai1 }
1792621Sllai1
1802621Sllai1 /*
1812621Sllai1 * Create a directory node in a non-global dev instance.
1822621Sllai1 * Always create shadow vnode. Set sdev_origin to the corresponding
1832621Sllai1 * global directory sdev_node if it exists. This facilitates the
1842621Sllai1 * lookup operation.
1852621Sllai1 */
1862621Sllai1 static int
prof_make_dir(char * name,struct sdev_node ** gdirp,struct sdev_node ** dirp)1872621Sllai1 prof_make_dir(char *name, struct sdev_node **gdirp, struct sdev_node **dirp)
1882621Sllai1 {
1892621Sllai1 struct sdev_node *dir = *dirp;
1902621Sllai1 struct sdev_node *gdir = *gdirp;
1912621Sllai1 struct sdev_node *newdv;
1922621Sllai1 struct vnode *avp, *gnewdir = NULL;
1932621Sllai1 struct vattr vattr;
1942621Sllai1 int error;
1952621Sllai1
1962621Sllai1 /* see if name already exists */
1972621Sllai1 rw_enter(&dir->sdev_contents, RW_READER);
1982621Sllai1 if (newdv = sdev_cache_lookup(dir, name)) {
1992621Sllai1 *dirp = newdv;
2002621Sllai1 *gdirp = newdv->sdev_origin;
2012621Sllai1 SDEV_RELE(dir);
2022621Sllai1 rw_exit(&dir->sdev_contents);
2032621Sllai1 return (0);
2042621Sllai1 }
2052621Sllai1 rw_exit(&dir->sdev_contents);
2062621Sllai1
2072621Sllai1 /* find corresponding dir node in global dev */
2082621Sllai1 if (gdir) {
2092621Sllai1 error = VOP_LOOKUP(SDEVTOV(gdir), name, &gnewdir,
2105331Samw NULL, 0, NULL, kcred, NULL, NULL, NULL);
2112621Sllai1 if (error == 0) {
2122621Sllai1 *gdirp = VTOSDEV(gnewdir);
2132621Sllai1 } else { /* it's ok if there no global dir */
2142621Sllai1 *gdirp = NULL;
2152621Sllai1 }
2162621Sllai1 }
2172621Sllai1
2182621Sllai1 /* get attribute from shadow, also create shadow dir */
2192621Sllai1 prof_getattr(dir, name, gnewdir, &vattr, &avp, NULL);
2202621Sllai1
2212621Sllai1 /* create dev directory vnode */
2222621Sllai1 rw_enter(&dir->sdev_contents, RW_WRITER);
2232621Sllai1 error = prof_mknode(dir, name, &newdv, &vattr, avp, (void *)*gdirp,
2242621Sllai1 kcred);
2252621Sllai1 rw_exit(&dir->sdev_contents);
2262621Sllai1 if (error == 0) {
2272621Sllai1 ASSERT(newdv);
2282621Sllai1 *dirp = newdv;
2292621Sllai1 }
2302621Sllai1 SDEV_RELE(dir);
2312621Sllai1 return (error);
2322621Sllai1 }
2332621Sllai1
2342621Sllai1 /*
2352621Sllai1 * Look up a logical name in the global zone.
2362621Sllai1 * Provides the ability to map the global zone's device name
2372621Sllai1 * to an alternate name within a zone. The primary example
2382621Sllai1 * is the virtual console device /dev/zcons/[zonename]/zconsole
2392621Sllai1 * mapped to /[zonename]/root/dev/zconsole.
2402621Sllai1 */
2412621Sllai1 static void
prof_lookup_globaldev(struct sdev_node * dir,struct sdev_node * gdir,char * name,char * rename)2422621Sllai1 prof_lookup_globaldev(struct sdev_node *dir, struct sdev_node *gdir,
2432621Sllai1 char *name, char *rename)
2442621Sllai1 {
2452621Sllai1 int error;
2462621Sllai1 struct vnode *avp, *gdv, *gddv;
2472621Sllai1 struct sdev_node *newdv;
2482621Sllai1 struct vattr vattr = {0};
2492621Sllai1 struct pathname pn;
2502621Sllai1
2512621Sllai1 /* check if node already exists */
2522621Sllai1 newdv = sdev_cache_lookup(dir, rename);
2532621Sllai1 if (newdv) {
2542621Sllai1 ASSERT(newdv->sdev_state != SDEV_ZOMBIE);
2552621Sllai1 SDEV_SIMPLE_RELE(newdv);
2562621Sllai1 return;
2572621Sllai1 }
2582621Sllai1
2592621Sllai1 /* sanity check arguments */
2602621Sllai1 if (!gdir || pn_get(name, UIO_SYSSPACE, &pn))
2612621Sllai1 return;
2622621Sllai1
2632621Sllai1 /* perform a relative lookup of the global /dev instance */
2642621Sllai1 gddv = SDEVTOV(gdir);
2652621Sllai1 VN_HOLD(gddv);
2662621Sllai1 error = lookuppnvp(&pn, NULL, FOLLOW, NULLVPP, &gdv,
2672621Sllai1 rootdir, gddv, kcred);
2682621Sllai1 pn_free(&pn);
2692621Sllai1 if (error) {
2702621Sllai1 sdcmn_err10(("prof_lookup_globaldev: %s not found\n", name));
2712621Sllai1 return;
2722621Sllai1 }
2732621Sllai1 ASSERT(gdv && gdv->v_type != VLNK);
2742621Sllai1
2752621Sllai1 /*
2762621Sllai1 * Found the entry in global /dev, figure out attributes
2772621Sllai1 * by looking at backing store. Call into devfs for default.
2783024Sllai1 * Note, mapped device is persisted under the new name
2792621Sllai1 */
2803024Sllai1 prof_getattr(dir, rename, gdv, &vattr, &avp, NULL);
2812621Sllai1
2822621Sllai1 if (gdv->v_type != VDIR) {
2832621Sllai1 VN_RELE(gdv);
2842621Sllai1 gdir = NULL;
2852621Sllai1 } else
2862621Sllai1 gdir = VTOSDEV(gdv);
2872621Sllai1
2882621Sllai1 if (prof_mknode(dir, rename, &newdv, &vattr, avp,
2892621Sllai1 (void *)gdir, kcred) == 0) {
2902621Sllai1 ASSERT(newdv->sdev_state != SDEV_ZOMBIE);
2912621Sllai1 SDEV_SIMPLE_RELE(newdv);
2922621Sllai1 }
2932621Sllai1 }
2942621Sllai1
2952621Sllai1 static void
prof_make_sym(struct sdev_node * dir,char * lnm,char * tgt)2962621Sllai1 prof_make_sym(struct sdev_node *dir, char *lnm, char *tgt)
2972621Sllai1 {
2982621Sllai1 struct sdev_node *newdv;
2992621Sllai1
3002621Sllai1 if (prof_mknode(dir, lnm, &newdv, &sdev_vattr_lnk, NULL,
3012621Sllai1 (void *)tgt, kcred) == 0) {
3022621Sllai1 ASSERT(newdv->sdev_state != SDEV_ZOMBIE);
3032621Sllai1 SDEV_SIMPLE_RELE(newdv);
3042621Sllai1 }
3052621Sllai1 }
3062621Sllai1
3072621Sllai1 /*
3082621Sllai1 * Create symlinks in the current directory based on profile
3092621Sllai1 */
3102621Sllai1 static void
prof_make_symlinks(struct sdev_node * dir)3112621Sllai1 prof_make_symlinks(struct sdev_node *dir)
3122621Sllai1 {
3132621Sllai1 char *tgt, *lnm;
3142621Sllai1 nvpair_t *nvp = NULL;
3152621Sllai1 nvlist_t *nvl = dir->sdev_prof.dev_symlink;
3162621Sllai1 int rv;
3172621Sllai1
3182621Sllai1 ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
3192621Sllai1
3202621Sllai1 if (nvl == NULL)
3212621Sllai1 return;
3222621Sllai1
3232621Sllai1 while (nvp = nvlist_next_nvpair(nvl, nvp)) {
3242621Sllai1 lnm = nvpair_name(nvp);
3252621Sllai1 rv = nvpair_value_string(nvp, &tgt);
3262621Sllai1 if (rv != 0) {
3272621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err,
3282621Sllai1 rv, nvpair_name(nvp));
3292621Sllai1 break;
3302621Sllai1 }
3312621Sllai1 prof_make_sym(dir, lnm, tgt);
3322621Sllai1 }
3332621Sllai1 }
3342621Sllai1
3352621Sllai1 static void
prof_make_maps(struct sdev_node * dir)3362621Sllai1 prof_make_maps(struct sdev_node *dir)
3372621Sllai1 {
3382621Sllai1 nvpair_t *nvp = NULL;
3392621Sllai1 nvlist_t *nvl = dir->sdev_prof.dev_map;
3402621Sllai1 int rv;
3412621Sllai1
3422621Sllai1 ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
3432621Sllai1
3442621Sllai1 if (nvl == NULL)
3452621Sllai1 return;
3462621Sllai1
3472621Sllai1 while (nvp = nvlist_next_nvpair(nvl, nvp)) {
3482621Sllai1 char *name;
3492621Sllai1 char *rename = nvpair_name(nvp);
3502621Sllai1 rv = nvpair_value_string(nvp, &name);
3512621Sllai1 if (rv != 0) {
3522621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err,
3532621Sllai1 rv, nvpair_name(nvp));
3542621Sllai1 break;
3552621Sllai1 }
3562621Sllai1 sdcmn_err10(("map %s -> %s\n", name, rename));
3572621Sllai1 (void) prof_lookup_globaldev(dir, sdev_origins->sdev_root,
3582621Sllai1 name, rename);
3592621Sllai1 }
3602621Sllai1 }
3612621Sllai1
3622621Sllai1 struct match_arg {
3632621Sllai1 char *expr;
3642621Sllai1 int match;
3652621Sllai1 };
3662621Sllai1
3672621Sllai1 static int
match_name(char * name,void * arg)3682621Sllai1 match_name(char *name, void *arg)
3692621Sllai1 {
3702621Sllai1 struct match_arg *margp = (struct match_arg *)arg;
3712621Sllai1
3722621Sllai1 if (gmatch(name, margp->expr)) {
3732621Sllai1 margp->match = 1;
3742621Sllai1 return (WALK_DIR_TERMINATE);
3752621Sllai1 }
3762621Sllai1 return (WALK_DIR_CONTINUE);
3772621Sllai1 }
3782621Sllai1
3792621Sllai1 static int
is_nonempty_dir(char * name,char * pathleft,struct sdev_node * dir)3802621Sllai1 is_nonempty_dir(char *name, char *pathleft, struct sdev_node *dir)
3812621Sllai1 {
3822621Sllai1 struct match_arg marg;
3832621Sllai1 struct pathname pn;
3842621Sllai1 struct vnode *gvp;
3852621Sllai1 struct sdev_node *gdir = dir->sdev_origin;
3862621Sllai1
3875331Samw if (VOP_LOOKUP(SDEVTOV(gdir), name, &gvp, NULL, 0, NULL, kcred,
3885331Samw NULL, NULL, NULL) != 0)
3892621Sllai1 return (0);
3902621Sllai1
3912621Sllai1 if (gvp->v_type != VDIR) {
3922621Sllai1 VN_RELE(gvp);
3932621Sllai1 return (0);
3942621Sllai1 }
3952621Sllai1
3962621Sllai1 if (pn_get(pathleft, UIO_SYSSPACE, &pn) != 0) {
3972621Sllai1 VN_RELE(gvp);
3982621Sllai1 return (0);
3992621Sllai1 }
4002621Sllai1
4012621Sllai1 marg.expr = kmem_alloc(MAXNAMELEN, KM_SLEEP);
4022621Sllai1 (void) pn_getcomponent(&pn, marg.expr);
4032621Sllai1 marg.match = 0;
4042621Sllai1
4052621Sllai1 walk_dir(gvp, &marg, match_name);
4062621Sllai1 VN_RELE(gvp);
4072621Sllai1 kmem_free(marg.expr, MAXNAMELEN);
4082621Sllai1 pn_free(&pn);
4092621Sllai1
4102621Sllai1 return (marg.match);
4112621Sllai1 }
4122621Sllai1
4132621Sllai1
4142621Sllai1 /* Check if name passes matching rules */
4152621Sllai1 static int
prof_name_matched(char * name,struct sdev_node * dir)4162621Sllai1 prof_name_matched(char *name, struct sdev_node *dir)
4172621Sllai1 {
4182621Sllai1 int type, match = 0;
4192621Sllai1 char *expr;
4202621Sllai1 nvlist_t *nvl;
4212621Sllai1 nvpair_t *nvp = NULL;
4222621Sllai1 int rv;
4232621Sllai1
4242621Sllai1 /* check against nvlist for leaf include/exclude */
4252621Sllai1 nvl = dir->sdev_prof.dev_name;
4262621Sllai1 while (nvp = nvlist_next_nvpair(nvl, nvp)) {
4272621Sllai1 expr = nvpair_name(nvp);
4282621Sllai1 rv = nvpair_value_int32(nvp, &type);
4292621Sllai1 if (rv != 0) {
4302621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err,
4312621Sllai1 rv, nvpair_name(nvp));
4322621Sllai1 break;
4332621Sllai1 }
4342621Sllai1
4352621Sllai1 if (type == PROFILE_TYPE_EXCLUDE) {
4362621Sllai1 if (gmatch(name, expr))
4372621Sllai1 return (0); /* excluded */
4382621Sllai1 } else if (!match) {
4392621Sllai1 match = gmatch(name, expr);
4402621Sllai1 }
4412621Sllai1 }
4422621Sllai1 if (match) {
4432621Sllai1 sdcmn_err10(("prof_name_matched: %s\n", name));
4442621Sllai1 return (match);
4452621Sllai1 }
4462621Sllai1
4472621Sllai1 /* check for match against directory globbing pattern */
4482621Sllai1 nvl = dir->sdev_prof.dev_glob_incdir;
4492621Sllai1 while (nvp = nvlist_next_nvpair(nvl, nvp)) {
4502621Sllai1 char *pathleft;
4512621Sllai1 expr = nvpair_name(nvp);
4522621Sllai1 if (gmatch(name, expr) == 0)
4532621Sllai1 continue;
4542621Sllai1 rv = nvpair_value_string(nvp, &pathleft);
4552621Sllai1 if (rv != 0) {
4562621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err,
4572621Sllai1 rv, nvpair_name(nvp));
4582621Sllai1 break;
4592621Sllai1 }
4602621Sllai1 if (is_nonempty_dir(name, pathleft, dir)) {
4612621Sllai1 sdcmn_err10(("prof_name_matched: dir %s\n", name));
4622621Sllai1 return (1);
4632621Sllai1 }
4642621Sllai1 }
4652621Sllai1
4662621Sllai1 return (0);
4672621Sllai1 }
4682621Sllai1
4692621Sllai1 static void
walk_dir(struct vnode * dvp,void * arg,int (* callback)(char *,void *))4702621Sllai1 walk_dir(struct vnode *dvp, void *arg, int (*callback)(char *, void *))
4712621Sllai1 {
4722621Sllai1 char *nm;
4732621Sllai1 int eof, error;
4742621Sllai1 struct iovec iov;
4752621Sllai1 struct uio uio;
4762621Sllai1 struct dirent64 *dp;
4772621Sllai1 dirent64_t *dbuf;
4782621Sllai1 size_t dbuflen, dlen;
4792621Sllai1
4802621Sllai1 ASSERT(dvp);
4812621Sllai1
4822621Sllai1 dlen = 4096;
4832621Sllai1 dbuf = kmem_zalloc(dlen, KM_SLEEP);
4842621Sllai1
4852621Sllai1 uio.uio_iov = &iov;
4862621Sllai1 uio.uio_iovcnt = 1;
4872621Sllai1 uio.uio_segflg = UIO_SYSSPACE;
4882621Sllai1 uio.uio_fmode = 0;
4892621Sllai1 uio.uio_extflg = UIO_COPY_CACHED;
4902621Sllai1 uio.uio_loffset = 0;
4912621Sllai1 uio.uio_llimit = MAXOFFSET_T;
4922621Sllai1
4932621Sllai1 eof = 0;
4942621Sllai1 error = 0;
4952621Sllai1 while (!error && !eof) {
4962621Sllai1 uio.uio_resid = dlen;
4972621Sllai1 iov.iov_base = (char *)dbuf;
4982621Sllai1 iov.iov_len = dlen;
4992621Sllai1 (void) VOP_RWLOCK(dvp, V_WRITELOCK_FALSE, NULL);
5005331Samw error = VOP_READDIR(dvp, &uio, kcred, &eof, NULL, 0);
5012621Sllai1 VOP_RWUNLOCK(dvp, V_WRITELOCK_FALSE, NULL);
5022621Sllai1
5032621Sllai1 dbuflen = dlen - uio.uio_resid;
5042621Sllai1 if (error || dbuflen == 0)
5052621Sllai1 break;
5062621Sllai1 for (dp = dbuf; ((intptr_t)dp <
5072621Sllai1 (intptr_t)dbuf + dbuflen);
5082621Sllai1 dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) {
5092621Sllai1 nm = dp->d_name;
5102621Sllai1
5112621Sllai1 if (strcmp(nm, ".") == 0 ||
5122621Sllai1 strcmp(nm, "..") == 0)
5132621Sllai1 continue;
5142621Sllai1
5152621Sllai1 if (callback(nm, arg) == WALK_DIR_TERMINATE)
5162621Sllai1 goto end;
5172621Sllai1 }
5182621Sllai1 }
5192621Sllai1
5202621Sllai1 end:
5212621Sllai1 kmem_free(dbuf, dlen);
5222621Sllai1 }
5232621Sllai1
524*12633Sjohn.levon@sun.com /*
525*12633Sjohn.levon@sun.com * Last chance for a zone to see a node. If our parent dir is
526*12633Sjohn.levon@sun.com * SDEV_ZONED, then we look up the "zone" property for the node. If the
527*12633Sjohn.levon@sun.com * property is found and matches the current zone name, we allow it.
528*12633Sjohn.levon@sun.com * Note that this isn't quite correct for the global zone peeking inside
529*12633Sjohn.levon@sun.com * a zone's /dev - for that to work, we'd have to have a per-dev-mount
530*12633Sjohn.levon@sun.com * zone ref squirreled away.
531*12633Sjohn.levon@sun.com */
5322621Sllai1 static int
prof_zone_matched(char * name,struct sdev_node * dir)533*12633Sjohn.levon@sun.com prof_zone_matched(char *name, struct sdev_node *dir)
534*12633Sjohn.levon@sun.com {
535*12633Sjohn.levon@sun.com vnode_t *gvn = SDEVTOV(dir->sdev_origin);
536*12633Sjohn.levon@sun.com struct pathname pn;
537*12633Sjohn.levon@sun.com vnode_t *vn = NULL;
538*12633Sjohn.levon@sun.com char zonename[ZONENAME_MAX];
539*12633Sjohn.levon@sun.com int znlen = ZONENAME_MAX;
540*12633Sjohn.levon@sun.com int ret;
541*12633Sjohn.levon@sun.com
542*12633Sjohn.levon@sun.com ASSERT((dir->sdev_flags & SDEV_ZONED) != 0);
543*12633Sjohn.levon@sun.com
544*12633Sjohn.levon@sun.com sdcmn_err10(("sdev_node %p is zoned, looking for %s\n",
545*12633Sjohn.levon@sun.com (void *)dir, name));
546*12633Sjohn.levon@sun.com
547*12633Sjohn.levon@sun.com if (pn_get(name, UIO_SYSSPACE, &pn))
548*12633Sjohn.levon@sun.com return (0);
549*12633Sjohn.levon@sun.com
550*12633Sjohn.levon@sun.com VN_HOLD(gvn);
551*12633Sjohn.levon@sun.com
552*12633Sjohn.levon@sun.com ret = lookuppnvp(&pn, NULL, FOLLOW, NULLVPP, &vn, rootdir, gvn, kcred);
553*12633Sjohn.levon@sun.com
554*12633Sjohn.levon@sun.com pn_free(&pn);
555*12633Sjohn.levon@sun.com
556*12633Sjohn.levon@sun.com if (ret != 0) {
557*12633Sjohn.levon@sun.com sdcmn_err10(("prof_zone_matched: %s not found\n", name));
558*12633Sjohn.levon@sun.com return (0);
559*12633Sjohn.levon@sun.com }
560*12633Sjohn.levon@sun.com
561*12633Sjohn.levon@sun.com /*
562*12633Sjohn.levon@sun.com * VBLK doesn't matter, and the property name is in fact treated
563*12633Sjohn.levon@sun.com * as a const char *.
564*12633Sjohn.levon@sun.com */
565*12633Sjohn.levon@sun.com ret = e_ddi_getlongprop_buf(vn->v_rdev, VBLK, (char *)"zone",
566*12633Sjohn.levon@sun.com DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, (caddr_t)zonename, &znlen);
567*12633Sjohn.levon@sun.com
568*12633Sjohn.levon@sun.com VN_RELE(vn);
569*12633Sjohn.levon@sun.com
570*12633Sjohn.levon@sun.com if (ret == DDI_PROP_NOT_FOUND) {
571*12633Sjohn.levon@sun.com sdcmn_err10(("vnode %p: no zone prop\n", (void *)vn));
572*12633Sjohn.levon@sun.com return (0);
573*12633Sjohn.levon@sun.com } else if (ret != DDI_PROP_SUCCESS) {
574*12633Sjohn.levon@sun.com sdcmn_err10(("vnode %p: zone prop error: %d\n",
575*12633Sjohn.levon@sun.com (void *)vn, ret));
576*12633Sjohn.levon@sun.com return (0);
577*12633Sjohn.levon@sun.com }
578*12633Sjohn.levon@sun.com
579*12633Sjohn.levon@sun.com sdcmn_err10(("vnode %p zone prop: %s\n", (void *)vn, zonename));
580*12633Sjohn.levon@sun.com return (strcmp(zonename, curproc->p_zone->zone_name) == 0);
581*12633Sjohn.levon@sun.com }
582*12633Sjohn.levon@sun.com
583*12633Sjohn.levon@sun.com static int
prof_make_name_glob(char * nm,void * arg)584*12633Sjohn.levon@sun.com prof_make_name_glob(char *nm, void *arg)
5852621Sllai1 {
5862621Sllai1 struct sdev_node *ddv = (struct sdev_node *)arg;
5872621Sllai1
5882621Sllai1 if (prof_name_matched(nm, ddv))
5892621Sllai1 prof_lookup_globaldev(ddv, ddv->sdev_origin, nm, nm);
590*12633Sjohn.levon@sun.com
591*12633Sjohn.levon@sun.com return (WALK_DIR_CONTINUE);
592*12633Sjohn.levon@sun.com }
593*12633Sjohn.levon@sun.com
594*12633Sjohn.levon@sun.com static int
prof_make_name_zone(char * nm,void * arg)595*12633Sjohn.levon@sun.com prof_make_name_zone(char *nm, void *arg)
596*12633Sjohn.levon@sun.com {
597*12633Sjohn.levon@sun.com struct sdev_node *ddv = (struct sdev_node *)arg;
598*12633Sjohn.levon@sun.com
599*12633Sjohn.levon@sun.com if (prof_zone_matched(nm, ddv))
600*12633Sjohn.levon@sun.com prof_lookup_globaldev(ddv, ddv->sdev_origin, nm, nm);
601*12633Sjohn.levon@sun.com
6022621Sllai1 return (WALK_DIR_CONTINUE);
6032621Sllai1 }
6042621Sllai1
6052621Sllai1 static void
prof_make_names_walk(struct sdev_node * ddv,int (* cb)(char *,void *))606*12633Sjohn.levon@sun.com prof_make_names_walk(struct sdev_node *ddv, int (*cb)(char *, void *))
6072621Sllai1 {
6082621Sllai1 struct sdev_node *gdir;
6092621Sllai1
6102621Sllai1 gdir = ddv->sdev_origin;
6112621Sllai1 if (gdir == NULL)
6122621Sllai1 return;
613*12633Sjohn.levon@sun.com walk_dir(SDEVTOV(gdir), (void *)ddv, cb);
6142621Sllai1 }
6152621Sllai1
6162621Sllai1 static void
prof_make_names(struct sdev_node * dir)6172621Sllai1 prof_make_names(struct sdev_node *dir)
6182621Sllai1 {
6192621Sllai1 char *name;
6202621Sllai1 nvpair_t *nvp = NULL;
6212621Sllai1 nvlist_t *nvl = dir->sdev_prof.dev_name;
6222621Sllai1 int rv;
6232621Sllai1
6242621Sllai1 ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
6252621Sllai1
626*12633Sjohn.levon@sun.com if ((dir->sdev_flags & SDEV_ZONED) != 0)
627*12633Sjohn.levon@sun.com prof_make_names_walk(dir, prof_make_name_zone);
628*12633Sjohn.levon@sun.com
6292621Sllai1 if (nvl == NULL)
6302621Sllai1 return;
6312621Sllai1
6322621Sllai1 if (dir->sdev_prof.has_glob) {
633*12633Sjohn.levon@sun.com prof_make_names_walk(dir, prof_make_name_glob);
6342621Sllai1 return;
6352621Sllai1 }
6362621Sllai1
6372621Sllai1 /* Walk nvlist and lookup corresponding device in global inst */
6382621Sllai1 while (nvp = nvlist_next_nvpair(nvl, nvp)) {
6392621Sllai1 int type;
6402621Sllai1 rv = nvpair_value_int32(nvp, &type);
6412621Sllai1 if (rv != 0) {
6422621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err,
6432621Sllai1 rv, nvpair_name(nvp));
6442621Sllai1 break;
6452621Sllai1 }
6462621Sllai1 if (type == PROFILE_TYPE_EXCLUDE)
6472621Sllai1 continue;
6482621Sllai1 name = nvpair_name(nvp);
6492621Sllai1 (void) prof_lookup_globaldev(dir, dir->sdev_origin,
6502621Sllai1 name, name);
6512621Sllai1 }
6522621Sllai1 }
6532621Sllai1
6542621Sllai1 /*
6552621Sllai1 * Build directory vnodes based on the profile and the global
6562621Sllai1 * dev instance.
6572621Sllai1 */
6582621Sllai1 void
prof_filldir(struct sdev_node * ddv)6592621Sllai1 prof_filldir(struct sdev_node *ddv)
6602621Sllai1 {
6612621Sllai1 int firsttime = 1;
6622621Sllai1 struct sdev_node *gdir = ddv->sdev_origin;
6632621Sllai1
6642621Sllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents));
6652621Sllai1
6662621Sllai1 /*
6672621Sllai1 * We need to rebuild the directory content if
6682621Sllai1 * - SDEV_BUILD is set
6692621Sllai1 * - The device tree generation number has changed
6702621Sllai1 * - The corresponding /dev namespace has been updated
6712621Sllai1 */
6722621Sllai1 check_build:
6732621Sllai1 if ((ddv->sdev_flags & SDEV_BUILD) == 0 &&
6742621Sllai1 ddv->sdev_devtree_gen == devtree_gen &&
6752621Sllai1 (gdir == NULL || ddv->sdev_ldir_gen
6762621Sllai1 == gdir->sdev_gdir_gen))
6772621Sllai1 return; /* already up to date */
6782621Sllai1
6792621Sllai1 if (firsttime && rw_tryupgrade(&ddv->sdev_contents) == 0) {
6802621Sllai1 rw_exit(&ddv->sdev_contents);
6812621Sllai1 firsttime = 0;
6822621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER);
6832621Sllai1 goto check_build;
6842621Sllai1 }
6852621Sllai1 sdcmn_err10(("devtree_gen (%s): %ld -> %ld\n",
6862621Sllai1 ddv->sdev_path, ddv->sdev_devtree_gen, devtree_gen));
6872621Sllai1 if (gdir)
6882621Sllai1 sdcmn_err10(("sdev_dir_gen (%s): %ld -> %ld\n",
6892621Sllai1 ddv->sdev_path, ddv->sdev_ldir_gen,
6902621Sllai1 gdir->sdev_gdir_gen));
6912621Sllai1
6922621Sllai1 /* update flags and generation number so next filldir is quick */
6932621Sllai1 ddv->sdev_flags &= ~SDEV_BUILD;
6942621Sllai1 ddv->sdev_devtree_gen = devtree_gen;
6952621Sllai1 if (gdir)
6962621Sllai1 ddv->sdev_ldir_gen = gdir->sdev_gdir_gen;
6972621Sllai1
6982621Sllai1 prof_make_symlinks(ddv);
6992621Sllai1 prof_make_maps(ddv);
7002621Sllai1 prof_make_names(ddv);
7012621Sllai1 rw_downgrade(&ddv->sdev_contents);
7022621Sllai1 }
7032621Sllai1
7042621Sllai1 /* apply include/exclude pattern to existing directory content */
7052621Sllai1 static void
apply_dir_pattern(struct sdev_node * dir,char * expr,char * pathleft,int type)7062621Sllai1 apply_dir_pattern(struct sdev_node *dir, char *expr, char *pathleft, int type)
7072621Sllai1 {
7082621Sllai1 struct sdev_node *dv;
7092621Sllai1
7102621Sllai1 /* leaf pattern */
7112621Sllai1 if (pathleft == NULL) {
7122621Sllai1 if (type == PROFILE_TYPE_INCLUDE)
7132621Sllai1 return; /* nothing to do for include */
7142621Sllai1 (void) sdev_cleandir(dir, expr, SDEV_ENFORCE);
7152621Sllai1 return;
7162621Sllai1 }
7172621Sllai1
7182621Sllai1 /* directory pattern */
7192621Sllai1 rw_enter(&dir->sdev_contents, RW_WRITER);
7206260Sjg
7216260Sjg for (dv = SDEV_FIRST_ENTRY(dir); dv; dv = SDEV_NEXT_ENTRY(dir, dv)) {
7222621Sllai1 if (gmatch(dv->sdev_name, expr) == 0 ||
7232621Sllai1 SDEVTOV(dv)->v_type != VDIR)
7242621Sllai1 continue;
7252621Sllai1 process_rule(dv, dv->sdev_origin,
7262621Sllai1 pathleft, NULL, type);
7272621Sllai1 }
7282621Sllai1 rw_exit(&dir->sdev_contents);
7292621Sllai1 }
7302621Sllai1
7312621Sllai1 /*
7322621Sllai1 * Add a profile rule.
7332621Sllai1 * tgt represents a device name matching expression,
7342621Sllai1 * matching device names are to be either included or excluded.
7352621Sllai1 */
7362621Sllai1 static void
prof_add_rule(char * name,char * tgt,struct sdev_node * dir,int type)7372621Sllai1 prof_add_rule(char *name, char *tgt, struct sdev_node *dir, int type)
7382621Sllai1 {
7392621Sllai1 int error;
7402621Sllai1 nvlist_t **nvlp = NULL;
7412621Sllai1 int rv;
7422621Sllai1
7432621Sllai1 ASSERT(SDEVTOV(dir)->v_type == VDIR);
7442621Sllai1
7452621Sllai1 rw_enter(&dir->sdev_contents, RW_WRITER);
7462621Sllai1
7472621Sllai1 switch (type) {
7482621Sllai1 case PROFILE_TYPE_INCLUDE:
7492621Sllai1 if (tgt)
7502621Sllai1 nvlp = &(dir->sdev_prof.dev_glob_incdir);
7512621Sllai1 else
7522621Sllai1 nvlp = &(dir->sdev_prof.dev_name);
7532621Sllai1 break;
7542621Sllai1 case PROFILE_TYPE_EXCLUDE:
7552621Sllai1 if (tgt)
7562621Sllai1 nvlp = &(dir->sdev_prof.dev_glob_excdir);
7572621Sllai1 else
7582621Sllai1 nvlp = &(dir->sdev_prof.dev_name);
7592621Sllai1 break;
7602621Sllai1 case PROFILE_TYPE_MAP:
7612621Sllai1 nvlp = &(dir->sdev_prof.dev_map);
7622621Sllai1 break;
7632621Sllai1 case PROFILE_TYPE_SYMLINK:
7642621Sllai1 nvlp = &(dir->sdev_prof.dev_symlink);
7652621Sllai1 break;
7662621Sllai1 };
7672621Sllai1
7682621Sllai1 /* initialize nvlist */
7692621Sllai1 if (*nvlp == NULL) {
7702621Sllai1 error = nvlist_alloc(nvlp, NV_UNIQUE_NAME, KM_SLEEP);
7712621Sllai1 ASSERT(error == 0);
7722621Sllai1 }
7732621Sllai1
7742621Sllai1 if (tgt) {
7752621Sllai1 rv = nvlist_add_string(*nvlp, name, tgt);
7762621Sllai1 } else {
7772621Sllai1 rv = nvlist_add_int32(*nvlp, name, type);
7782621Sllai1 }
7792621Sllai1 ASSERT(rv == 0);
7802621Sllai1 /* rebuild directory content */
7812621Sllai1 dir->sdev_flags |= SDEV_BUILD;
7822621Sllai1
7832621Sllai1 if ((type == PROFILE_TYPE_INCLUDE) &&
7842621Sllai1 (strpbrk(name, "*?[]") != NULL)) {
7852621Sllai1 dir->sdev_prof.has_glob = 1;
7862621Sllai1 }
7872621Sllai1
7882621Sllai1 rw_exit(&dir->sdev_contents);
7892621Sllai1
7902621Sllai1 /* additional details for glob pattern and exclusion */
7912621Sllai1 switch (type) {
7922621Sllai1 case PROFILE_TYPE_INCLUDE:
7932621Sllai1 case PROFILE_TYPE_EXCLUDE:
7942621Sllai1 apply_dir_pattern(dir, name, tgt, type);
7952621Sllai1 break;
7962621Sllai1 };
7972621Sllai1 }
7982621Sllai1
7992621Sllai1 /*
8002621Sllai1 * Parse path components and apply requested matching rule at
8012621Sllai1 * directory level.
8022621Sllai1 */
8032621Sllai1 static void
process_rule(struct sdev_node * dir,struct sdev_node * gdir,char * path,char * tgt,int type)8042621Sllai1 process_rule(struct sdev_node *dir, struct sdev_node *gdir,
8052621Sllai1 char *path, char *tgt, int type)
8062621Sllai1 {
8072621Sllai1 char *name;
8082621Sllai1 struct pathname pn;
8092621Sllai1 int rv = 0;
8102621Sllai1
8112621Sllai1 if ((strlen(path) > 5) && (strncmp(path, "/dev/", 5) == 0)) {
8122621Sllai1 path += 5;
8132621Sllai1 }
8142621Sllai1
8152621Sllai1 if (pn_get(path, UIO_SYSSPACE, &pn) != 0)
8162621Sllai1 return;
8172621Sllai1
8182621Sllai1 name = kmem_alloc(MAXPATHLEN, KM_SLEEP);
8192621Sllai1 (void) pn_getcomponent(&pn, name);
8202621Sllai1 pn_skipslash(&pn);
8212621Sllai1 SDEV_HOLD(dir);
8222621Sllai1
8232621Sllai1 while (pn_pathleft(&pn)) {
8242621Sllai1 /* If this is pattern, just add the pattern */
8252621Sllai1 if (strpbrk(name, "*?[]") != NULL &&
8262621Sllai1 (type == PROFILE_TYPE_INCLUDE ||
8272621Sllai1 type == PROFILE_TYPE_EXCLUDE)) {
8282621Sllai1 ASSERT(tgt == NULL);
8292621Sllai1 tgt = pn.pn_path;
8302621Sllai1 break;
8312621Sllai1 }
8322621Sllai1 if ((rv = prof_make_dir(name, &gdir, &dir)) != 0) {
8332621Sllai1 cmn_err(CE_CONT, "process_rule: %s error %d\n",
8342621Sllai1 path, rv);
8352621Sllai1 break;
8362621Sllai1 }
8372621Sllai1 (void) pn_getcomponent(&pn, name);
8382621Sllai1 pn_skipslash(&pn);
8392621Sllai1 }
8402621Sllai1
8412621Sllai1 /* process the leaf component */
8422621Sllai1 if (rv == 0) {
8432621Sllai1 prof_add_rule(name, tgt, dir, type);
8442621Sllai1 SDEV_SIMPLE_RELE(dir);
8452621Sllai1 }
8462621Sllai1
8472621Sllai1 kmem_free(name, MAXPATHLEN);
8482621Sllai1 pn_free(&pn);
8492621Sllai1 }
8502621Sllai1
8512621Sllai1 static int
copyin_nvlist(char * packed_usr,size_t packed_sz,nvlist_t ** nvlp)8522621Sllai1 copyin_nvlist(char *packed_usr, size_t packed_sz, nvlist_t **nvlp)
8532621Sllai1 {
8542621Sllai1 int err = 0;
8552621Sllai1 char *packed;
8562621Sllai1 nvlist_t *profile = NULL;
8572621Sllai1
8582621Sllai1 /* simple sanity check */
8592621Sllai1 if (packed_usr == NULL || packed_sz == 0)
8602621Sllai1 return (NULL);
8612621Sllai1
8622621Sllai1 /* copyin packed profile nvlist */
8632621Sllai1 packed = kmem_alloc(packed_sz, KM_NOSLEEP);
8642621Sllai1 if (packed == NULL)
8652621Sllai1 return (ENOMEM);
8662621Sllai1 err = copyin(packed_usr, packed, packed_sz);
8672621Sllai1
8682621Sllai1 /* unpack packed profile nvlist */
8692621Sllai1 if (err)
8702621Sllai1 cmn_err(CE_WARN, "copyin_nvlist: copyin failed with "
8712621Sllai1 "err %d\n", err);
8722621Sllai1 else if (err = nvlist_unpack(packed, packed_sz, &profile, KM_NOSLEEP))
8732621Sllai1 cmn_err(CE_WARN, "copyin_nvlist: nvlist_unpack "
8742621Sllai1 "failed with err %d\n", err);
8752621Sllai1
8762621Sllai1 kmem_free(packed, packed_sz);
8772621Sllai1 if (err == 0)
8782621Sllai1 *nvlp = profile;
8792621Sllai1 return (err);
8802621Sllai1 }
8812621Sllai1
8822621Sllai1 /*
8832621Sllai1 * Process profile passed down from libdevinfo. There are four types
8842621Sllai1 * of matching rules:
8852621Sllai1 * include: export a name or names matching a pattern
8862621Sllai1 * exclude: exclude a name or names matching a pattern
8872621Sllai1 * symlink: create a local symlink
8882621Sllai1 * map: export a device with a name different from the global zone
8892621Sllai1 * Note: We may consider supporting VOP_SYMLINK in non-global instances,
8902621Sllai1 * because it does not present any security risk. For now, the fs
8912621Sllai1 * instance is read only.
8922621Sllai1 */
8932621Sllai1 static void
sdev_process_profile(struct sdev_data * sdev_data,nvlist_t * profile)8942621Sllai1 sdev_process_profile(struct sdev_data *sdev_data, nvlist_t *profile)
8952621Sllai1 {
8962621Sllai1 nvpair_t *nvpair;
8972621Sllai1 char *nvname, *dname;
8982621Sllai1 struct sdev_node *dir, *gdir;
8992621Sllai1 char **pair; /* for symlinks and maps */
9002621Sllai1 uint_t nelem;
9012621Sllai1 int rv;
9022621Sllai1
9032621Sllai1 gdir = sdev_origins->sdev_root; /* root of global /dev */
9042621Sllai1 dir = sdev_data->sdev_root; /* root of current instance */
9052621Sllai1
9062621Sllai1 ASSERT(profile);
9072621Sllai1
9082621Sllai1 /* process nvpairs in the list */
9092621Sllai1 nvpair = NULL;
9102621Sllai1 while (nvpair = nvlist_next_nvpair(profile, nvpair)) {
9112621Sllai1 nvname = nvpair_name(nvpair);
9122621Sllai1 ASSERT(nvname != NULL);
9132621Sllai1
9142621Sllai1 if (strcmp(nvname, SDEV_NVNAME_INCLUDE) == 0) {
9152621Sllai1 rv = nvpair_value_string(nvpair, &dname);
9162621Sllai1 if (rv != 0) {
9172621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err,
9182621Sllai1 rv, nvpair_name(nvpair));
9192621Sllai1 break;
9202621Sllai1 }
9212621Sllai1 process_rule(dir, gdir, dname, NULL,
9222621Sllai1 PROFILE_TYPE_INCLUDE);
9232621Sllai1 } else if (strcmp(nvname, SDEV_NVNAME_EXCLUDE) == 0) {
9242621Sllai1 rv = nvpair_value_string(nvpair, &dname);
9252621Sllai1 if (rv != 0) {
9262621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err,
9272621Sllai1 rv, nvpair_name(nvpair));
9282621Sllai1 break;
9292621Sllai1 }
9302621Sllai1 process_rule(dir, gdir, dname, NULL,
9312621Sllai1 PROFILE_TYPE_EXCLUDE);
9322621Sllai1 } else if (strcmp(nvname, SDEV_NVNAME_SYMLINK) == 0) {
9332621Sllai1 rv = nvpair_value_string_array(nvpair, &pair, &nelem);
9342621Sllai1 if (rv != 0) {
9352621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err,
9362621Sllai1 rv, nvpair_name(nvpair));
9372621Sllai1 break;
9382621Sllai1 }
9392621Sllai1 ASSERT(nelem == 2);
9402621Sllai1 process_rule(dir, gdir, pair[0], pair[1],
9412621Sllai1 PROFILE_TYPE_SYMLINK);
9422621Sllai1 } else if (strcmp(nvname, SDEV_NVNAME_MAP) == 0) {
9432621Sllai1 rv = nvpair_value_string_array(nvpair, &pair, &nelem);
9442621Sllai1 if (rv != 0) {
9452621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err,
9462621Sllai1 rv, nvpair_name(nvpair));
9472621Sllai1 break;
9482621Sllai1 }
9492621Sllai1 process_rule(dir, gdir, pair[1], pair[0],
9502621Sllai1 PROFILE_TYPE_MAP);
9512621Sllai1 } else if (strcmp(nvname, SDEV_NVNAME_MOUNTPT) != 0) {
9522621Sllai1 cmn_err(CE_WARN, "sdev_process_profile: invalid "
9532621Sllai1 "nvpair %s\n", nvname);
9542621Sllai1 }
9552621Sllai1 }
9562621Sllai1 }
9572621Sllai1
9582621Sllai1 /*ARGSUSED*/
9592621Sllai1 int
prof_lookup(vnode_t * dvp,char * nm,struct vnode ** vpp,struct cred * cred)9602621Sllai1 prof_lookup(vnode_t *dvp, char *nm, struct vnode **vpp, struct cred *cred)
9612621Sllai1 {
9622621Sllai1 struct sdev_node *ddv = VTOSDEV(dvp);
9632621Sllai1 struct sdev_node *dv;
9642621Sllai1 int nmlen;
9652621Sllai1
9662621Sllai1 /*
9672621Sllai1 * Empty name or ., return node itself.
9682621Sllai1 */
9692621Sllai1 nmlen = strlen(nm);
9702621Sllai1 if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) {
9712621Sllai1 *vpp = SDEVTOV(ddv);
9722621Sllai1 VN_HOLD(*vpp);
9732621Sllai1 return (0);
9742621Sllai1 }
9752621Sllai1
9762621Sllai1 /*
9772621Sllai1 * .., return the parent directory
9782621Sllai1 */
9792621Sllai1 if ((nmlen == 2) && (strcmp(nm, "..") == 0)) {
9802621Sllai1 *vpp = SDEVTOV(ddv->sdev_dotdot);
9812621Sllai1 VN_HOLD(*vpp);
9822621Sllai1 return (0);
9832621Sllai1 }
9842621Sllai1
9852621Sllai1 rw_enter(&ddv->sdev_contents, RW_READER);
9862621Sllai1 dv = sdev_cache_lookup(ddv, nm);
9872621Sllai1 if (dv == NULL) {
9882621Sllai1 prof_filldir(ddv);
9892621Sllai1 dv = sdev_cache_lookup(ddv, nm);
9902621Sllai1 }
9912621Sllai1 rw_exit(&ddv->sdev_contents);
9922621Sllai1 if (dv == NULL) {
9932621Sllai1 sdcmn_err10(("prof_lookup: %s not found\n", nm));
9942621Sllai1 return (ENOENT);
9952621Sllai1 }
9962621Sllai1
9972621Sllai1 return (sdev_to_vp(dv, vpp));
9982621Sllai1 }
9992621Sllai1
10002621Sllai1 /*
10012621Sllai1 * This is invoked after a new filesystem is mounted to define the
10022621Sllai1 * name space. It is also invoked during normal system operation
10032621Sllai1 * to update the name space.
10042621Sllai1 *
10052621Sllai1 * Applications call di_prof_commit() in libdevinfo, which invokes
10062621Sllai1 * modctl(). modctl calls this function. The input is a packed nvlist.
10072621Sllai1 */
10082621Sllai1 int
devname_profile_update(char * packed,size_t packed_sz)10092621Sllai1 devname_profile_update(char *packed, size_t packed_sz)
10102621Sllai1 {
10112621Sllai1 char *mntpt;
10122621Sllai1 nvlist_t *nvl;
10132621Sllai1 nvpair_t *nvp;
10142621Sllai1 struct sdev_data *mntinfo;
10152621Sllai1 int err;
10162621Sllai1 int rv;
10172621Sllai1
10182621Sllai1 nvl = NULL;
10192621Sllai1 if ((err = copyin_nvlist(packed, packed_sz, &nvl)) != 0)
10202621Sllai1 return (err);
10212621Sllai1 ASSERT(nvl);
10222621Sllai1
10232621Sllai1 /* The first nvpair must be the mount point */
10242621Sllai1 nvp = nvlist_next_nvpair(nvl, NULL);
10252621Sllai1 if (strcmp(nvpair_name(nvp), SDEV_NVNAME_MOUNTPT) != 0) {
10262621Sllai1 cmn_err(CE_NOTE,
10272621Sllai1 "devname_profile_update: mount point not specified");
10282621Sllai1 nvlist_free(nvl);
10292621Sllai1 return (EINVAL);
10302621Sllai1 }
10312621Sllai1
10322621Sllai1 /* find the matching filesystem instance */
10332621Sllai1 rv = nvpair_value_string(nvp, &mntpt);
10342621Sllai1 if (rv != 0) {
10352621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err,
10362621Sllai1 rv, nvpair_name(nvp));
10372621Sllai1 } else {
10382621Sllai1 mntinfo = sdev_find_mntinfo(mntpt);
10392621Sllai1 if (mntinfo == NULL) {
10402621Sllai1 cmn_err(CE_NOTE, "devname_profile_update: "
10412621Sllai1 " mount point %s not found", mntpt);
10422621Sllai1 nvlist_free(nvl);
10432621Sllai1 return (EINVAL);
10442621Sllai1 }
10452621Sllai1
10462621Sllai1 /* now do the hardwork to process the profile */
10472621Sllai1 sdev_process_profile(mntinfo, nvl);
10482621Sllai1
10492621Sllai1 sdev_mntinfo_rele(mntinfo);
10502621Sllai1 }
10512621Sllai1
10522621Sllai1 nvlist_free(nvl);
10532621Sllai1 return (0);
10542621Sllai1 }
1055