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*5331Samw * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 242621Sllai1 * Use is subject to license terms. 252621Sllai1 */ 262621Sllai1 272621Sllai1 #pragma ident "%Z%%M% %I% %E% SMI" 282621Sllai1 292621Sllai1 /* 302621Sllai1 * This file implements /dev filesystem operations for non-global 312621Sllai1 * instances. Three major entry points: 322621Sllai1 * devname_profile_update() 332621Sllai1 * Update matching rules determining which names to export 342621Sllai1 * prof_readdir() 352621Sllai1 * Return the list of exported names 362621Sllai1 * prof_lookup() 372621Sllai1 * Implements lookup 382621Sllai1 */ 392621Sllai1 402621Sllai1 #include <sys/types.h> 412621Sllai1 #include <sys/param.h> 422621Sllai1 #include <sys/sysmacros.h> 432621Sllai1 #include <sys/vnode.h> 442621Sllai1 #include <sys/uio.h> 452621Sllai1 #include <sys/dirent.h> 462621Sllai1 #include <sys/pathname.h> 472621Sllai1 #include <sys/fs/dv_node.h> 482621Sllai1 #include <sys/fs/sdev_impl.h> 492621Sllai1 #include <sys/sunndi.h> 502621Sllai1 #include <sys/modctl.h> 512621Sllai1 522621Sllai1 enum { 532621Sllai1 PROFILE_TYPE_INCLUDE, 542621Sllai1 PROFILE_TYPE_EXCLUDE, 552621Sllai1 PROFILE_TYPE_MAP, 562621Sllai1 PROFILE_TYPE_SYMLINK 572621Sllai1 }; 582621Sllai1 592621Sllai1 enum { 602621Sllai1 WALK_DIR_CONTINUE = 0, 612621Sllai1 WALK_DIR_TERMINATE 622621Sllai1 }; 632621Sllai1 642621Sllai1 static const char *sdev_nvp_val_err = "nvpair_value error %d, %s\n"; 652621Sllai1 662621Sllai1 static void process_rule(struct sdev_node *, struct sdev_node *, 672621Sllai1 char *, char *, int); 682621Sllai1 static void walk_dir(struct vnode *, void *, int (*)(char *, void *)); 692621Sllai1 702621Sllai1 static void 712621Sllai1 prof_getattr(struct sdev_node *dir, char *name, struct vnode *gdv, 722621Sllai1 struct vattr *vap, struct vnode **avpp, int *no_fs_perm) 732621Sllai1 { 742621Sllai1 struct vnode *advp; 752621Sllai1 762621Sllai1 /* get attribute from shadow, if present; else get default */ 772621Sllai1 advp = dir->sdev_attrvp; 78*5331Samw if (advp && VOP_LOOKUP(advp, name, avpp, NULL, 0, NULL, kcred, 79*5331Samw NULL, NULL, NULL) == 0) { 80*5331Samw (void) VOP_GETATTR(*avpp, vap, 0, kcred, NULL); 812621Sllai1 } else if (gdv == NULL || gdv->v_type == VDIR) { 822621Sllai1 /* always create shadow directory */ 832621Sllai1 *vap = sdev_vattr_dir; 84*5331Samw if (advp && VOP_MKDIR(advp, name, &sdev_vattr_dir, 85*5331Samw avpp, kcred, NULL, 0, NULL) != 0) { 862621Sllai1 *avpp = NULLVP; 872621Sllai1 sdcmn_err10(("prof_getattr: failed to create " 882621Sllai1 "shadow directory %s/%s\n", dir->sdev_path, name)); 892621Sllai1 } 902621Sllai1 } else { 912621Sllai1 /* 922621Sllai1 * get default permission from devfs 932621Sllai1 * Before calling devfs_get_defattr, we need to get 942621Sllai1 * the realvp (the dv_node). If realvp is not a dv_node, 952621Sllai1 * devfs_get_defattr() will return a system-wide default 962621Sllai1 * attr for device nodes. 972621Sllai1 */ 982621Sllai1 struct vnode *rvp; 99*5331Samw if (VOP_REALVP(gdv, &rvp, NULL) != 0) 1002621Sllai1 rvp = gdv; 1012621Sllai1 devfs_get_defattr(rvp, vap, no_fs_perm); 1022621Sllai1 *avpp = NULLVP; 1032621Sllai1 } 1042621Sllai1 1052621Sllai1 /* ignore dev_t and vtype from backing store */ 1062621Sllai1 if (gdv) { 1072621Sllai1 vap->va_type = gdv->v_type; 1082621Sllai1 vap->va_rdev = gdv->v_rdev; 1092621Sllai1 } 1102621Sllai1 } 1112621Sllai1 1122621Sllai1 static void 1132621Sllai1 apply_glob_pattern(struct sdev_node *pdir, struct sdev_node *cdir) 1142621Sllai1 { 1152621Sllai1 char *name; 1162621Sllai1 nvpair_t *nvp = NULL; 1172621Sllai1 nvlist_t *nvl; 1182621Sllai1 struct vnode *vp = SDEVTOV(cdir); 1192621Sllai1 int rv = 0; 1202621Sllai1 1212621Sllai1 if (vp->v_type != VDIR) 1222621Sllai1 return; 1232621Sllai1 name = cdir->sdev_name; 1242621Sllai1 nvl = pdir->sdev_prof.dev_glob_incdir; 1252621Sllai1 while (nvp = nvlist_next_nvpair(nvl, nvp)) { 1262621Sllai1 char *pathleft; 1272621Sllai1 char *expr = nvpair_name(nvp); 1282621Sllai1 if (!gmatch(name, expr)) 1292621Sllai1 continue; 1302621Sllai1 rv = nvpair_value_string(nvp, &pathleft); 1312621Sllai1 if (rv != 0) { 1322621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 1332621Sllai1 rv, nvpair_name(nvp)); 1342621Sllai1 break; 1352621Sllai1 } 1362621Sllai1 process_rule(cdir, cdir->sdev_origin, 1372621Sllai1 pathleft, NULL, PROFILE_TYPE_INCLUDE); 1382621Sllai1 } 1392621Sllai1 } 1402621Sllai1 1412621Sllai1 /* 1422621Sllai1 * Some commonality here with sdev_mknode(), could be simplified. 1432621Sllai1 * NOTE: prof_mknode returns with *newdv held once, if success. 1442621Sllai1 */ 1452621Sllai1 static int 1462621Sllai1 prof_mknode(struct sdev_node *dir, char *name, struct sdev_node **newdv, 1472621Sllai1 vattr_t *vap, vnode_t *avp, void *arg, cred_t *cred) 1482621Sllai1 { 1492621Sllai1 struct sdev_node *dv; 1502621Sllai1 int rv; 1512621Sllai1 1522621Sllai1 ASSERT(RW_WRITE_HELD(&dir->sdev_contents)); 1532621Sllai1 1542621Sllai1 /* check cache first */ 1552621Sllai1 if (dv = sdev_cache_lookup(dir, name)) { 1562621Sllai1 *newdv = dv; 1572621Sllai1 return (0); 1582621Sllai1 } 1592621Sllai1 1602621Sllai1 /* allocate node and insert into cache */ 1612621Sllai1 rv = sdev_nodeinit(dir, name, &dv, NULL); 1622621Sllai1 if (rv != 0) { 1632621Sllai1 *newdv = NULL; 1642621Sllai1 return (rv); 1652621Sllai1 } 1662621Sllai1 1672621Sllai1 rv = sdev_cache_update(dir, &dv, name, SDEV_CACHE_ADD); 1682621Sllai1 *newdv = dv; 1692621Sllai1 1702621Sllai1 /* put it in ready state */ 1712621Sllai1 rv = sdev_nodeready(*newdv, vap, avp, arg, cred); 1722621Sllai1 1732621Sllai1 /* handle glob pattern in the middle of a path */ 1742621Sllai1 if (rv == 0) { 1752621Sllai1 if (SDEVTOV(*newdv)->v_type == VDIR) 1762621Sllai1 sdcmn_err10(("sdev_origin for %s set to 0x%p\n", 1772621Sllai1 name, arg)); 1782621Sllai1 apply_glob_pattern(dir, *newdv); 1792621Sllai1 } 1802621Sllai1 return (rv); 1812621Sllai1 } 1822621Sllai1 1832621Sllai1 /* 1842621Sllai1 * Create a directory node in a non-global dev instance. 1852621Sllai1 * Always create shadow vnode. Set sdev_origin to the corresponding 1862621Sllai1 * global directory sdev_node if it exists. This facilitates the 1872621Sllai1 * lookup operation. 1882621Sllai1 */ 1892621Sllai1 static int 1902621Sllai1 prof_make_dir(char *name, struct sdev_node **gdirp, struct sdev_node **dirp) 1912621Sllai1 { 1922621Sllai1 struct sdev_node *dir = *dirp; 1932621Sllai1 struct sdev_node *gdir = *gdirp; 1942621Sllai1 struct sdev_node *newdv; 1952621Sllai1 struct vnode *avp, *gnewdir = NULL; 1962621Sllai1 struct vattr vattr; 1972621Sllai1 int error; 1982621Sllai1 1992621Sllai1 /* see if name already exists */ 2002621Sllai1 rw_enter(&dir->sdev_contents, RW_READER); 2012621Sllai1 if (newdv = sdev_cache_lookup(dir, name)) { 2022621Sllai1 *dirp = newdv; 2032621Sllai1 *gdirp = newdv->sdev_origin; 2042621Sllai1 SDEV_RELE(dir); 2052621Sllai1 rw_exit(&dir->sdev_contents); 2062621Sllai1 return (0); 2072621Sllai1 } 2082621Sllai1 rw_exit(&dir->sdev_contents); 2092621Sllai1 2102621Sllai1 /* find corresponding dir node in global dev */ 2112621Sllai1 if (gdir) { 2122621Sllai1 error = VOP_LOOKUP(SDEVTOV(gdir), name, &gnewdir, 213*5331Samw NULL, 0, NULL, kcred, NULL, NULL, NULL); 2142621Sllai1 if (error == 0) { 2152621Sllai1 *gdirp = VTOSDEV(gnewdir); 2162621Sllai1 } else { /* it's ok if there no global dir */ 2172621Sllai1 *gdirp = NULL; 2182621Sllai1 } 2192621Sllai1 } 2202621Sllai1 2212621Sllai1 /* get attribute from shadow, also create shadow dir */ 2222621Sllai1 prof_getattr(dir, name, gnewdir, &vattr, &avp, NULL); 2232621Sllai1 2242621Sllai1 /* create dev directory vnode */ 2252621Sllai1 rw_enter(&dir->sdev_contents, RW_WRITER); 2262621Sllai1 error = prof_mknode(dir, name, &newdv, &vattr, avp, (void *)*gdirp, 2272621Sllai1 kcred); 2282621Sllai1 rw_exit(&dir->sdev_contents); 2292621Sllai1 if (error == 0) { 2302621Sllai1 ASSERT(newdv); 2312621Sllai1 *dirp = newdv; 2322621Sllai1 } 2332621Sllai1 SDEV_RELE(dir); 2342621Sllai1 return (error); 2352621Sllai1 } 2362621Sllai1 2372621Sllai1 /* 2382621Sllai1 * Look up a logical name in the global zone. 2392621Sllai1 * Provides the ability to map the global zone's device name 2402621Sllai1 * to an alternate name within a zone. The primary example 2412621Sllai1 * is the virtual console device /dev/zcons/[zonename]/zconsole 2422621Sllai1 * mapped to /[zonename]/root/dev/zconsole. 2432621Sllai1 */ 2442621Sllai1 static void 2452621Sllai1 prof_lookup_globaldev(struct sdev_node *dir, struct sdev_node *gdir, 2462621Sllai1 char *name, char *rename) 2472621Sllai1 { 2482621Sllai1 /* global OS rootdir */ 2492621Sllai1 extern vnode_t *rootdir; 2502621Sllai1 2512621Sllai1 int error; 2522621Sllai1 struct vnode *avp, *gdv, *gddv; 2532621Sllai1 struct sdev_node *newdv; 2542621Sllai1 struct vattr vattr = {0}; 2552621Sllai1 struct pathname pn; 2562621Sllai1 2572621Sllai1 /* check if node already exists */ 2582621Sllai1 newdv = sdev_cache_lookup(dir, rename); 2592621Sllai1 if (newdv) { 2602621Sllai1 ASSERT(newdv->sdev_state != SDEV_ZOMBIE); 2612621Sllai1 SDEV_SIMPLE_RELE(newdv); 2622621Sllai1 return; 2632621Sllai1 } 2642621Sllai1 2652621Sllai1 /* sanity check arguments */ 2662621Sllai1 if (!gdir || pn_get(name, UIO_SYSSPACE, &pn)) 2672621Sllai1 return; 2682621Sllai1 2692621Sllai1 /* perform a relative lookup of the global /dev instance */ 2702621Sllai1 gddv = SDEVTOV(gdir); 2712621Sllai1 VN_HOLD(gddv); 2722621Sllai1 VN_HOLD(rootdir); 2732621Sllai1 error = lookuppnvp(&pn, NULL, FOLLOW, NULLVPP, &gdv, 2742621Sllai1 rootdir, gddv, kcred); 2752621Sllai1 pn_free(&pn); 2762621Sllai1 if (error) { 2772621Sllai1 sdcmn_err10(("prof_lookup_globaldev: %s not found\n", name)); 2782621Sllai1 return; 2792621Sllai1 } 2802621Sllai1 ASSERT(gdv && gdv->v_type != VLNK); 2812621Sllai1 2822621Sllai1 /* 2832621Sllai1 * Found the entry in global /dev, figure out attributes 2842621Sllai1 * by looking at backing store. Call into devfs for default. 2853024Sllai1 * Note, mapped device is persisted under the new name 2862621Sllai1 */ 2873024Sllai1 prof_getattr(dir, rename, gdv, &vattr, &avp, NULL); 2882621Sllai1 2892621Sllai1 if (gdv->v_type != VDIR) { 2902621Sllai1 VN_RELE(gdv); 2912621Sllai1 gdir = NULL; 2922621Sllai1 } else 2932621Sllai1 gdir = VTOSDEV(gdv); 2942621Sllai1 2952621Sllai1 if (prof_mknode(dir, rename, &newdv, &vattr, avp, 2962621Sllai1 (void *)gdir, kcred) == 0) { 2972621Sllai1 ASSERT(newdv->sdev_state != SDEV_ZOMBIE); 2982621Sllai1 SDEV_SIMPLE_RELE(newdv); 2992621Sllai1 } 3002621Sllai1 } 3012621Sllai1 3022621Sllai1 static void 3032621Sllai1 prof_make_sym(struct sdev_node *dir, char *lnm, char *tgt) 3042621Sllai1 { 3052621Sllai1 struct sdev_node *newdv; 3062621Sllai1 3072621Sllai1 if (prof_mknode(dir, lnm, &newdv, &sdev_vattr_lnk, NULL, 3082621Sllai1 (void *)tgt, kcred) == 0) { 3092621Sllai1 ASSERT(newdv->sdev_state != SDEV_ZOMBIE); 3102621Sllai1 SDEV_SIMPLE_RELE(newdv); 3112621Sllai1 } 3122621Sllai1 } 3132621Sllai1 3142621Sllai1 /* 3152621Sllai1 * Create symlinks in the current directory based on profile 3162621Sllai1 */ 3172621Sllai1 static void 3182621Sllai1 prof_make_symlinks(struct sdev_node *dir) 3192621Sllai1 { 3202621Sllai1 char *tgt, *lnm; 3212621Sllai1 nvpair_t *nvp = NULL; 3222621Sllai1 nvlist_t *nvl = dir->sdev_prof.dev_symlink; 3232621Sllai1 int rv; 3242621Sllai1 3252621Sllai1 ASSERT(RW_WRITE_HELD(&dir->sdev_contents)); 3262621Sllai1 3272621Sllai1 if (nvl == NULL) 3282621Sllai1 return; 3292621Sllai1 3302621Sllai1 while (nvp = nvlist_next_nvpair(nvl, nvp)) { 3312621Sllai1 lnm = nvpair_name(nvp); 3322621Sllai1 rv = nvpair_value_string(nvp, &tgt); 3332621Sllai1 if (rv != 0) { 3342621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 3352621Sllai1 rv, nvpair_name(nvp)); 3362621Sllai1 break; 3372621Sllai1 } 3382621Sllai1 prof_make_sym(dir, lnm, tgt); 3392621Sllai1 } 3402621Sllai1 } 3412621Sllai1 3422621Sllai1 static void 3432621Sllai1 prof_make_maps(struct sdev_node *dir) 3442621Sllai1 { 3452621Sllai1 nvpair_t *nvp = NULL; 3462621Sllai1 nvlist_t *nvl = dir->sdev_prof.dev_map; 3472621Sllai1 int rv; 3482621Sllai1 3492621Sllai1 ASSERT(RW_WRITE_HELD(&dir->sdev_contents)); 3502621Sllai1 3512621Sllai1 if (nvl == NULL) 3522621Sllai1 return; 3532621Sllai1 3542621Sllai1 while (nvp = nvlist_next_nvpair(nvl, nvp)) { 3552621Sllai1 char *name; 3562621Sllai1 char *rename = nvpair_name(nvp); 3572621Sllai1 rv = nvpair_value_string(nvp, &name); 3582621Sllai1 if (rv != 0) { 3592621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 3602621Sllai1 rv, nvpair_name(nvp)); 3612621Sllai1 break; 3622621Sllai1 } 3632621Sllai1 sdcmn_err10(("map %s -> %s\n", name, rename)); 3642621Sllai1 (void) prof_lookup_globaldev(dir, sdev_origins->sdev_root, 3652621Sllai1 name, rename); 3662621Sllai1 } 3672621Sllai1 } 3682621Sllai1 3692621Sllai1 struct match_arg { 3702621Sllai1 char *expr; 3712621Sllai1 int match; 3722621Sllai1 }; 3732621Sllai1 3742621Sllai1 static int 3752621Sllai1 match_name(char *name, void *arg) 3762621Sllai1 { 3772621Sllai1 struct match_arg *margp = (struct match_arg *)arg; 3782621Sllai1 3792621Sllai1 if (gmatch(name, margp->expr)) { 3802621Sllai1 margp->match = 1; 3812621Sllai1 return (WALK_DIR_TERMINATE); 3822621Sllai1 } 3832621Sllai1 return (WALK_DIR_CONTINUE); 3842621Sllai1 } 3852621Sllai1 3862621Sllai1 static int 3872621Sllai1 is_nonempty_dir(char *name, char *pathleft, struct sdev_node *dir) 3882621Sllai1 { 3892621Sllai1 struct match_arg marg; 3902621Sllai1 struct pathname pn; 3912621Sllai1 struct vnode *gvp; 3922621Sllai1 struct sdev_node *gdir = dir->sdev_origin; 3932621Sllai1 394*5331Samw if (VOP_LOOKUP(SDEVTOV(gdir), name, &gvp, NULL, 0, NULL, kcred, 395*5331Samw NULL, NULL, NULL) != 0) 3962621Sllai1 return (0); 3972621Sllai1 3982621Sllai1 if (gvp->v_type != VDIR) { 3992621Sllai1 VN_RELE(gvp); 4002621Sllai1 return (0); 4012621Sllai1 } 4022621Sllai1 4032621Sllai1 if (pn_get(pathleft, UIO_SYSSPACE, &pn) != 0) { 4042621Sllai1 VN_RELE(gvp); 4052621Sllai1 return (0); 4062621Sllai1 } 4072621Sllai1 4082621Sllai1 marg.expr = kmem_alloc(MAXNAMELEN, KM_SLEEP); 4092621Sllai1 (void) pn_getcomponent(&pn, marg.expr); 4102621Sllai1 marg.match = 0; 4112621Sllai1 4122621Sllai1 walk_dir(gvp, &marg, match_name); 4132621Sllai1 VN_RELE(gvp); 4142621Sllai1 kmem_free(marg.expr, MAXNAMELEN); 4152621Sllai1 pn_free(&pn); 4162621Sllai1 4172621Sllai1 return (marg.match); 4182621Sllai1 } 4192621Sllai1 4202621Sllai1 4212621Sllai1 /* Check if name passes matching rules */ 4222621Sllai1 static int 4232621Sllai1 prof_name_matched(char *name, struct sdev_node *dir) 4242621Sllai1 { 4252621Sllai1 int type, match = 0; 4262621Sllai1 char *expr; 4272621Sllai1 nvlist_t *nvl; 4282621Sllai1 nvpair_t *nvp = NULL; 4292621Sllai1 int rv; 4302621Sllai1 4312621Sllai1 /* check against nvlist for leaf include/exclude */ 4322621Sllai1 nvl = dir->sdev_prof.dev_name; 4332621Sllai1 while (nvp = nvlist_next_nvpair(nvl, nvp)) { 4342621Sllai1 expr = nvpair_name(nvp); 4352621Sllai1 rv = nvpair_value_int32(nvp, &type); 4362621Sllai1 if (rv != 0) { 4372621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 4382621Sllai1 rv, nvpair_name(nvp)); 4392621Sllai1 break; 4402621Sllai1 } 4412621Sllai1 4422621Sllai1 if (type == PROFILE_TYPE_EXCLUDE) { 4432621Sllai1 if (gmatch(name, expr)) 4442621Sllai1 return (0); /* excluded */ 4452621Sllai1 } else if (!match) { 4462621Sllai1 match = gmatch(name, expr); 4472621Sllai1 } 4482621Sllai1 } 4492621Sllai1 if (match) { 4502621Sllai1 sdcmn_err10(("prof_name_matched: %s\n", name)); 4512621Sllai1 return (match); 4522621Sllai1 } 4532621Sllai1 4542621Sllai1 /* check for match against directory globbing pattern */ 4552621Sllai1 nvl = dir->sdev_prof.dev_glob_incdir; 4562621Sllai1 while (nvp = nvlist_next_nvpair(nvl, nvp)) { 4572621Sllai1 char *pathleft; 4582621Sllai1 expr = nvpair_name(nvp); 4592621Sllai1 if (gmatch(name, expr) == 0) 4602621Sllai1 continue; 4612621Sllai1 rv = nvpair_value_string(nvp, &pathleft); 4622621Sllai1 if (rv != 0) { 4632621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 4642621Sllai1 rv, nvpair_name(nvp)); 4652621Sllai1 break; 4662621Sllai1 } 4672621Sllai1 if (is_nonempty_dir(name, pathleft, dir)) { 4682621Sllai1 sdcmn_err10(("prof_name_matched: dir %s\n", name)); 4692621Sllai1 return (1); 4702621Sllai1 } 4712621Sllai1 } 4722621Sllai1 4732621Sllai1 return (0); 4742621Sllai1 } 4752621Sllai1 4762621Sllai1 static void 4772621Sllai1 walk_dir(struct vnode *dvp, void *arg, int (*callback)(char *, void *)) 4782621Sllai1 { 4792621Sllai1 char *nm; 4802621Sllai1 int eof, error; 4812621Sllai1 struct iovec iov; 4822621Sllai1 struct uio uio; 4832621Sllai1 struct dirent64 *dp; 4842621Sllai1 dirent64_t *dbuf; 4852621Sllai1 size_t dbuflen, dlen; 4862621Sllai1 4872621Sllai1 ASSERT(dvp); 4882621Sllai1 4892621Sllai1 dlen = 4096; 4902621Sllai1 dbuf = kmem_zalloc(dlen, KM_SLEEP); 4912621Sllai1 4922621Sllai1 uio.uio_iov = &iov; 4932621Sllai1 uio.uio_iovcnt = 1; 4942621Sllai1 uio.uio_segflg = UIO_SYSSPACE; 4952621Sllai1 uio.uio_fmode = 0; 4962621Sllai1 uio.uio_extflg = UIO_COPY_CACHED; 4972621Sllai1 uio.uio_loffset = 0; 4982621Sllai1 uio.uio_llimit = MAXOFFSET_T; 4992621Sllai1 5002621Sllai1 eof = 0; 5012621Sllai1 error = 0; 5022621Sllai1 while (!error && !eof) { 5032621Sllai1 uio.uio_resid = dlen; 5042621Sllai1 iov.iov_base = (char *)dbuf; 5052621Sllai1 iov.iov_len = dlen; 5062621Sllai1 (void) VOP_RWLOCK(dvp, V_WRITELOCK_FALSE, NULL); 507*5331Samw error = VOP_READDIR(dvp, &uio, kcred, &eof, NULL, 0); 5082621Sllai1 VOP_RWUNLOCK(dvp, V_WRITELOCK_FALSE, NULL); 5092621Sllai1 5102621Sllai1 dbuflen = dlen - uio.uio_resid; 5112621Sllai1 if (error || dbuflen == 0) 5122621Sllai1 break; 5132621Sllai1 for (dp = dbuf; ((intptr_t)dp < 5142621Sllai1 (intptr_t)dbuf + dbuflen); 5152621Sllai1 dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) { 5162621Sllai1 nm = dp->d_name; 5172621Sllai1 5182621Sllai1 if (strcmp(nm, ".") == 0 || 5192621Sllai1 strcmp(nm, "..") == 0) 5202621Sllai1 continue; 5212621Sllai1 5222621Sllai1 if (callback(nm, arg) == WALK_DIR_TERMINATE) 5232621Sllai1 goto end; 5242621Sllai1 } 5252621Sllai1 } 5262621Sllai1 5272621Sllai1 end: 5282621Sllai1 kmem_free(dbuf, dlen); 5292621Sllai1 } 5302621Sllai1 5312621Sllai1 static int 5322621Sllai1 prof_make_name(char *nm, void *arg) 5332621Sllai1 { 5342621Sllai1 struct sdev_node *ddv = (struct sdev_node *)arg; 5352621Sllai1 5362621Sllai1 if (prof_name_matched(nm, ddv)) 5372621Sllai1 prof_lookup_globaldev(ddv, ddv->sdev_origin, nm, nm); 5382621Sllai1 return (WALK_DIR_CONTINUE); 5392621Sllai1 } 5402621Sllai1 5412621Sllai1 static void 5422621Sllai1 prof_make_names_glob(struct sdev_node *ddv) 5432621Sllai1 { 5442621Sllai1 struct sdev_node *gdir; 5452621Sllai1 5462621Sllai1 gdir = ddv->sdev_origin; 5472621Sllai1 if (gdir == NULL) 5482621Sllai1 return; 5492621Sllai1 walk_dir(SDEVTOV(gdir), (void *)ddv, prof_make_name); 5502621Sllai1 } 5512621Sllai1 5522621Sllai1 static void 5532621Sllai1 prof_make_names(struct sdev_node *dir) 5542621Sllai1 { 5552621Sllai1 char *name; 5562621Sllai1 nvpair_t *nvp = NULL; 5572621Sllai1 nvlist_t *nvl = dir->sdev_prof.dev_name; 5582621Sllai1 int rv; 5592621Sllai1 5602621Sllai1 ASSERT(RW_WRITE_HELD(&dir->sdev_contents)); 5612621Sllai1 5622621Sllai1 if (nvl == NULL) 5632621Sllai1 return; 5642621Sllai1 5652621Sllai1 if (dir->sdev_prof.has_glob) { 5662621Sllai1 prof_make_names_glob(dir); 5672621Sllai1 return; 5682621Sllai1 } 5692621Sllai1 5702621Sllai1 /* Walk nvlist and lookup corresponding device in global inst */ 5712621Sllai1 while (nvp = nvlist_next_nvpair(nvl, nvp)) { 5722621Sllai1 int type; 5732621Sllai1 rv = nvpair_value_int32(nvp, &type); 5742621Sllai1 if (rv != 0) { 5752621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 5762621Sllai1 rv, nvpair_name(nvp)); 5772621Sllai1 break; 5782621Sllai1 } 5792621Sllai1 if (type == PROFILE_TYPE_EXCLUDE) 5802621Sllai1 continue; 5812621Sllai1 name = nvpair_name(nvp); 5822621Sllai1 (void) prof_lookup_globaldev(dir, dir->sdev_origin, 5832621Sllai1 name, name); 5842621Sllai1 } 5852621Sllai1 } 5862621Sllai1 5872621Sllai1 /* 5882621Sllai1 * Build directory vnodes based on the profile and the global 5892621Sllai1 * dev instance. 5902621Sllai1 */ 5912621Sllai1 void 5922621Sllai1 prof_filldir(struct sdev_node *ddv) 5932621Sllai1 { 5942621Sllai1 int firsttime = 1; 5952621Sllai1 struct sdev_node *gdir = ddv->sdev_origin; 5962621Sllai1 5972621Sllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 5982621Sllai1 5992621Sllai1 /* 6002621Sllai1 * We need to rebuild the directory content if 6012621Sllai1 * - SDEV_BUILD is set 6022621Sllai1 * - The device tree generation number has changed 6032621Sllai1 * - The corresponding /dev namespace has been updated 6042621Sllai1 */ 6052621Sllai1 check_build: 6062621Sllai1 if ((ddv->sdev_flags & SDEV_BUILD) == 0 && 6072621Sllai1 ddv->sdev_devtree_gen == devtree_gen && 6082621Sllai1 (gdir == NULL || ddv->sdev_ldir_gen 6092621Sllai1 == gdir->sdev_gdir_gen)) 6102621Sllai1 return; /* already up to date */ 6112621Sllai1 6122621Sllai1 if (firsttime && rw_tryupgrade(&ddv->sdev_contents) == 0) { 6132621Sllai1 rw_exit(&ddv->sdev_contents); 6142621Sllai1 firsttime = 0; 6152621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 6162621Sllai1 goto check_build; 6172621Sllai1 } 6182621Sllai1 sdcmn_err10(("devtree_gen (%s): %ld -> %ld\n", 6192621Sllai1 ddv->sdev_path, ddv->sdev_devtree_gen, devtree_gen)); 6202621Sllai1 if (gdir) 6212621Sllai1 sdcmn_err10(("sdev_dir_gen (%s): %ld -> %ld\n", 6222621Sllai1 ddv->sdev_path, ddv->sdev_ldir_gen, 6232621Sllai1 gdir->sdev_gdir_gen)); 6242621Sllai1 6252621Sllai1 /* update flags and generation number so next filldir is quick */ 6262621Sllai1 ddv->sdev_flags &= ~SDEV_BUILD; 6272621Sllai1 ddv->sdev_devtree_gen = devtree_gen; 6282621Sllai1 if (gdir) 6292621Sllai1 ddv->sdev_ldir_gen = gdir->sdev_gdir_gen; 6302621Sllai1 6312621Sllai1 prof_make_symlinks(ddv); 6322621Sllai1 prof_make_maps(ddv); 6332621Sllai1 prof_make_names(ddv); 6342621Sllai1 rw_downgrade(&ddv->sdev_contents); 6352621Sllai1 } 6362621Sllai1 6372621Sllai1 /* apply include/exclude pattern to existing directory content */ 6382621Sllai1 static void 6392621Sllai1 apply_dir_pattern(struct sdev_node *dir, char *expr, char *pathleft, int type) 6402621Sllai1 { 6412621Sllai1 struct sdev_node *dv; 6422621Sllai1 6432621Sllai1 /* leaf pattern */ 6442621Sllai1 if (pathleft == NULL) { 6452621Sllai1 if (type == PROFILE_TYPE_INCLUDE) 6462621Sllai1 return; /* nothing to do for include */ 6472621Sllai1 (void) sdev_cleandir(dir, expr, SDEV_ENFORCE); 6482621Sllai1 return; 6492621Sllai1 } 6502621Sllai1 6512621Sllai1 /* directory pattern */ 6522621Sllai1 rw_enter(&dir->sdev_contents, RW_WRITER); 6532621Sllai1 for (dv = dir->sdev_dot; dv; dv = dv->sdev_next) { 6542621Sllai1 if (gmatch(dv->sdev_name, expr) == 0 || 6552621Sllai1 SDEVTOV(dv)->v_type != VDIR) 6562621Sllai1 continue; 6572621Sllai1 process_rule(dv, dv->sdev_origin, 6582621Sllai1 pathleft, NULL, type); 6592621Sllai1 } 6602621Sllai1 rw_exit(&dir->sdev_contents); 6612621Sllai1 } 6622621Sllai1 6632621Sllai1 /* 6642621Sllai1 * Add a profile rule. 6652621Sllai1 * tgt represents a device name matching expression, 6662621Sllai1 * matching device names are to be either included or excluded. 6672621Sllai1 */ 6682621Sllai1 static void 6692621Sllai1 prof_add_rule(char *name, char *tgt, struct sdev_node *dir, int type) 6702621Sllai1 { 6712621Sllai1 int error; 6722621Sllai1 nvlist_t **nvlp = NULL; 6732621Sllai1 int rv; 6742621Sllai1 6752621Sllai1 ASSERT(SDEVTOV(dir)->v_type == VDIR); 6762621Sllai1 6772621Sllai1 rw_enter(&dir->sdev_contents, RW_WRITER); 6782621Sllai1 6792621Sllai1 switch (type) { 6802621Sllai1 case PROFILE_TYPE_INCLUDE: 6812621Sllai1 if (tgt) 6822621Sllai1 nvlp = &(dir->sdev_prof.dev_glob_incdir); 6832621Sllai1 else 6842621Sllai1 nvlp = &(dir->sdev_prof.dev_name); 6852621Sllai1 break; 6862621Sllai1 case PROFILE_TYPE_EXCLUDE: 6872621Sllai1 if (tgt) 6882621Sllai1 nvlp = &(dir->sdev_prof.dev_glob_excdir); 6892621Sllai1 else 6902621Sllai1 nvlp = &(dir->sdev_prof.dev_name); 6912621Sllai1 break; 6922621Sllai1 case PROFILE_TYPE_MAP: 6932621Sllai1 nvlp = &(dir->sdev_prof.dev_map); 6942621Sllai1 break; 6952621Sllai1 case PROFILE_TYPE_SYMLINK: 6962621Sllai1 nvlp = &(dir->sdev_prof.dev_symlink); 6972621Sllai1 break; 6982621Sllai1 }; 6992621Sllai1 7002621Sllai1 /* initialize nvlist */ 7012621Sllai1 if (*nvlp == NULL) { 7022621Sllai1 error = nvlist_alloc(nvlp, NV_UNIQUE_NAME, KM_SLEEP); 7032621Sllai1 ASSERT(error == 0); 7042621Sllai1 } 7052621Sllai1 7062621Sllai1 if (tgt) { 7072621Sllai1 rv = nvlist_add_string(*nvlp, name, tgt); 7082621Sllai1 } else { 7092621Sllai1 rv = nvlist_add_int32(*nvlp, name, type); 7102621Sllai1 } 7112621Sllai1 ASSERT(rv == 0); 7122621Sllai1 /* rebuild directory content */ 7132621Sllai1 dir->sdev_flags |= SDEV_BUILD; 7142621Sllai1 7152621Sllai1 if ((type == PROFILE_TYPE_INCLUDE) && 7162621Sllai1 (strpbrk(name, "*?[]") != NULL)) { 7172621Sllai1 dir->sdev_prof.has_glob = 1; 7182621Sllai1 } 7192621Sllai1 7202621Sllai1 rw_exit(&dir->sdev_contents); 7212621Sllai1 7222621Sllai1 /* additional details for glob pattern and exclusion */ 7232621Sllai1 switch (type) { 7242621Sllai1 case PROFILE_TYPE_INCLUDE: 7252621Sllai1 case PROFILE_TYPE_EXCLUDE: 7262621Sllai1 apply_dir_pattern(dir, name, tgt, type); 7272621Sllai1 break; 7282621Sllai1 }; 7292621Sllai1 } 7302621Sllai1 7312621Sllai1 /* 7322621Sllai1 * Parse path components and apply requested matching rule at 7332621Sllai1 * directory level. 7342621Sllai1 */ 7352621Sllai1 static void 7362621Sllai1 process_rule(struct sdev_node *dir, struct sdev_node *gdir, 7372621Sllai1 char *path, char *tgt, int type) 7382621Sllai1 { 7392621Sllai1 char *name; 7402621Sllai1 struct pathname pn; 7412621Sllai1 int rv = 0; 7422621Sllai1 7432621Sllai1 if ((strlen(path) > 5) && (strncmp(path, "/dev/", 5) == 0)) { 7442621Sllai1 path += 5; 7452621Sllai1 } 7462621Sllai1 7472621Sllai1 if (pn_get(path, UIO_SYSSPACE, &pn) != 0) 7482621Sllai1 return; 7492621Sllai1 7502621Sllai1 name = kmem_alloc(MAXPATHLEN, KM_SLEEP); 7512621Sllai1 (void) pn_getcomponent(&pn, name); 7522621Sllai1 pn_skipslash(&pn); 7532621Sllai1 SDEV_HOLD(dir); 7542621Sllai1 7552621Sllai1 while (pn_pathleft(&pn)) { 7562621Sllai1 /* If this is pattern, just add the pattern */ 7572621Sllai1 if (strpbrk(name, "*?[]") != NULL && 7582621Sllai1 (type == PROFILE_TYPE_INCLUDE || 7592621Sllai1 type == PROFILE_TYPE_EXCLUDE)) { 7602621Sllai1 ASSERT(tgt == NULL); 7612621Sllai1 tgt = pn.pn_path; 7622621Sllai1 break; 7632621Sllai1 } 7642621Sllai1 if ((rv = prof_make_dir(name, &gdir, &dir)) != 0) { 7652621Sllai1 cmn_err(CE_CONT, "process_rule: %s error %d\n", 7662621Sllai1 path, rv); 7672621Sllai1 break; 7682621Sllai1 } 7692621Sllai1 (void) pn_getcomponent(&pn, name); 7702621Sllai1 pn_skipslash(&pn); 7712621Sllai1 } 7722621Sllai1 7732621Sllai1 /* process the leaf component */ 7742621Sllai1 if (rv == 0) { 7752621Sllai1 prof_add_rule(name, tgt, dir, type); 7762621Sllai1 SDEV_SIMPLE_RELE(dir); 7772621Sllai1 } 7782621Sllai1 7792621Sllai1 kmem_free(name, MAXPATHLEN); 7802621Sllai1 pn_free(&pn); 7812621Sllai1 } 7822621Sllai1 7832621Sllai1 static int 7842621Sllai1 copyin_nvlist(char *packed_usr, size_t packed_sz, nvlist_t **nvlp) 7852621Sllai1 { 7862621Sllai1 int err = 0; 7872621Sllai1 char *packed; 7882621Sllai1 nvlist_t *profile = NULL; 7892621Sllai1 7902621Sllai1 /* simple sanity check */ 7912621Sllai1 if (packed_usr == NULL || packed_sz == 0) 7922621Sllai1 return (NULL); 7932621Sllai1 7942621Sllai1 /* copyin packed profile nvlist */ 7952621Sllai1 packed = kmem_alloc(packed_sz, KM_NOSLEEP); 7962621Sllai1 if (packed == NULL) 7972621Sllai1 return (ENOMEM); 7982621Sllai1 err = copyin(packed_usr, packed, packed_sz); 7992621Sllai1 8002621Sllai1 /* unpack packed profile nvlist */ 8012621Sllai1 if (err) 8022621Sllai1 cmn_err(CE_WARN, "copyin_nvlist: copyin failed with " 8032621Sllai1 "err %d\n", err); 8042621Sllai1 else if (err = nvlist_unpack(packed, packed_sz, &profile, KM_NOSLEEP)) 8052621Sllai1 cmn_err(CE_WARN, "copyin_nvlist: nvlist_unpack " 8062621Sllai1 "failed with err %d\n", err); 8072621Sllai1 8082621Sllai1 kmem_free(packed, packed_sz); 8092621Sllai1 if (err == 0) 8102621Sllai1 *nvlp = profile; 8112621Sllai1 return (err); 8122621Sllai1 } 8132621Sllai1 8142621Sllai1 /* 8152621Sllai1 * Process profile passed down from libdevinfo. There are four types 8162621Sllai1 * of matching rules: 8172621Sllai1 * include: export a name or names matching a pattern 8182621Sllai1 * exclude: exclude a name or names matching a pattern 8192621Sllai1 * symlink: create a local symlink 8202621Sllai1 * map: export a device with a name different from the global zone 8212621Sllai1 * Note: We may consider supporting VOP_SYMLINK in non-global instances, 8222621Sllai1 * because it does not present any security risk. For now, the fs 8232621Sllai1 * instance is read only. 8242621Sllai1 */ 8252621Sllai1 static void 8262621Sllai1 sdev_process_profile(struct sdev_data *sdev_data, nvlist_t *profile) 8272621Sllai1 { 8282621Sllai1 nvpair_t *nvpair; 8292621Sllai1 char *nvname, *dname; 8302621Sllai1 struct sdev_node *dir, *gdir; 8312621Sllai1 char **pair; /* for symlinks and maps */ 8322621Sllai1 uint_t nelem; 8332621Sllai1 int rv; 8342621Sllai1 8352621Sllai1 gdir = sdev_origins->sdev_root; /* root of global /dev */ 8362621Sllai1 dir = sdev_data->sdev_root; /* root of current instance */ 8372621Sllai1 8382621Sllai1 ASSERT(profile); 8392621Sllai1 8402621Sllai1 /* process nvpairs in the list */ 8412621Sllai1 nvpair = NULL; 8422621Sllai1 while (nvpair = nvlist_next_nvpair(profile, nvpair)) { 8432621Sllai1 nvname = nvpair_name(nvpair); 8442621Sllai1 ASSERT(nvname != NULL); 8452621Sllai1 8462621Sllai1 if (strcmp(nvname, SDEV_NVNAME_INCLUDE) == 0) { 8472621Sllai1 rv = nvpair_value_string(nvpair, &dname); 8482621Sllai1 if (rv != 0) { 8492621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 8502621Sllai1 rv, nvpair_name(nvpair)); 8512621Sllai1 break; 8522621Sllai1 } 8532621Sllai1 process_rule(dir, gdir, dname, NULL, 8542621Sllai1 PROFILE_TYPE_INCLUDE); 8552621Sllai1 } else if (strcmp(nvname, SDEV_NVNAME_EXCLUDE) == 0) { 8562621Sllai1 rv = nvpair_value_string(nvpair, &dname); 8572621Sllai1 if (rv != 0) { 8582621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 8592621Sllai1 rv, nvpair_name(nvpair)); 8602621Sllai1 break; 8612621Sllai1 } 8622621Sllai1 process_rule(dir, gdir, dname, NULL, 8632621Sllai1 PROFILE_TYPE_EXCLUDE); 8642621Sllai1 } else if (strcmp(nvname, SDEV_NVNAME_SYMLINK) == 0) { 8652621Sllai1 rv = nvpair_value_string_array(nvpair, &pair, &nelem); 8662621Sllai1 if (rv != 0) { 8672621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 8682621Sllai1 rv, nvpair_name(nvpair)); 8692621Sllai1 break; 8702621Sllai1 } 8712621Sllai1 ASSERT(nelem == 2); 8722621Sllai1 process_rule(dir, gdir, pair[0], pair[1], 8732621Sllai1 PROFILE_TYPE_SYMLINK); 8742621Sllai1 } else if (strcmp(nvname, SDEV_NVNAME_MAP) == 0) { 8752621Sllai1 rv = nvpair_value_string_array(nvpair, &pair, &nelem); 8762621Sllai1 if (rv != 0) { 8772621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 8782621Sllai1 rv, nvpair_name(nvpair)); 8792621Sllai1 break; 8802621Sllai1 } 8812621Sllai1 process_rule(dir, gdir, pair[1], pair[0], 8822621Sllai1 PROFILE_TYPE_MAP); 8832621Sllai1 } else if (strcmp(nvname, SDEV_NVNAME_MOUNTPT) != 0) { 8842621Sllai1 cmn_err(CE_WARN, "sdev_process_profile: invalid " 8852621Sllai1 "nvpair %s\n", nvname); 8862621Sllai1 } 8872621Sllai1 } 8882621Sllai1 } 8892621Sllai1 8902621Sllai1 /*ARGSUSED*/ 8912621Sllai1 int 8922621Sllai1 prof_lookup(vnode_t *dvp, char *nm, struct vnode **vpp, struct cred *cred) 8932621Sllai1 { 8942621Sllai1 struct sdev_node *ddv = VTOSDEV(dvp); 8952621Sllai1 struct sdev_node *dv; 8962621Sllai1 int nmlen; 8972621Sllai1 8982621Sllai1 /* 8992621Sllai1 * Empty name or ., return node itself. 9002621Sllai1 */ 9012621Sllai1 nmlen = strlen(nm); 9022621Sllai1 if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) { 9032621Sllai1 *vpp = SDEVTOV(ddv); 9042621Sllai1 VN_HOLD(*vpp); 9052621Sllai1 return (0); 9062621Sllai1 } 9072621Sllai1 9082621Sllai1 /* 9092621Sllai1 * .., return the parent directory 9102621Sllai1 */ 9112621Sllai1 if ((nmlen == 2) && (strcmp(nm, "..") == 0)) { 9122621Sllai1 *vpp = SDEVTOV(ddv->sdev_dotdot); 9132621Sllai1 VN_HOLD(*vpp); 9142621Sllai1 return (0); 9152621Sllai1 } 9162621Sllai1 9172621Sllai1 rw_enter(&ddv->sdev_contents, RW_READER); 9182621Sllai1 dv = sdev_cache_lookup(ddv, nm); 9192621Sllai1 if (dv == NULL) { 9202621Sllai1 prof_filldir(ddv); 9212621Sllai1 dv = sdev_cache_lookup(ddv, nm); 9222621Sllai1 } 9232621Sllai1 rw_exit(&ddv->sdev_contents); 9242621Sllai1 if (dv == NULL) { 9252621Sllai1 sdcmn_err10(("prof_lookup: %s not found\n", nm)); 9262621Sllai1 return (ENOENT); 9272621Sllai1 } 9282621Sllai1 9292621Sllai1 return (sdev_to_vp(dv, vpp)); 9302621Sllai1 } 9312621Sllai1 9322621Sllai1 /* 9332621Sllai1 * This is invoked after a new filesystem is mounted to define the 9342621Sllai1 * name space. It is also invoked during normal system operation 9352621Sllai1 * to update the name space. 9362621Sllai1 * 9372621Sllai1 * Applications call di_prof_commit() in libdevinfo, which invokes 9382621Sllai1 * modctl(). modctl calls this function. The input is a packed nvlist. 9392621Sllai1 */ 9402621Sllai1 int 9412621Sllai1 devname_profile_update(char *packed, size_t packed_sz) 9422621Sllai1 { 9432621Sllai1 char *mntpt; 9442621Sllai1 nvlist_t *nvl; 9452621Sllai1 nvpair_t *nvp; 9462621Sllai1 struct sdev_data *mntinfo; 9472621Sllai1 int err; 9482621Sllai1 int rv; 9492621Sllai1 9502621Sllai1 nvl = NULL; 9512621Sllai1 if ((err = copyin_nvlist(packed, packed_sz, &nvl)) != 0) 9522621Sllai1 return (err); 9532621Sllai1 ASSERT(nvl); 9542621Sllai1 9552621Sllai1 /* The first nvpair must be the mount point */ 9562621Sllai1 nvp = nvlist_next_nvpair(nvl, NULL); 9572621Sllai1 if (strcmp(nvpair_name(nvp), SDEV_NVNAME_MOUNTPT) != 0) { 9582621Sllai1 cmn_err(CE_NOTE, 9592621Sllai1 "devname_profile_update: mount point not specified"); 9602621Sllai1 nvlist_free(nvl); 9612621Sllai1 return (EINVAL); 9622621Sllai1 } 9632621Sllai1 9642621Sllai1 /* find the matching filesystem instance */ 9652621Sllai1 rv = nvpair_value_string(nvp, &mntpt); 9662621Sllai1 if (rv != 0) { 9672621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 9682621Sllai1 rv, nvpair_name(nvp)); 9692621Sllai1 } else { 9702621Sllai1 mntinfo = sdev_find_mntinfo(mntpt); 9712621Sllai1 if (mntinfo == NULL) { 9722621Sllai1 cmn_err(CE_NOTE, "devname_profile_update: " 9732621Sllai1 " mount point %s not found", mntpt); 9742621Sllai1 nvlist_free(nvl); 9752621Sllai1 return (EINVAL); 9762621Sllai1 } 9772621Sllai1 9782621Sllai1 /* now do the hardwork to process the profile */ 9792621Sllai1 sdev_process_profile(mntinfo, nvl); 9802621Sllai1 9812621Sllai1 sdev_mntinfo_rele(mntinfo); 9822621Sllai1 } 9832621Sllai1 9842621Sllai1 nvlist_free(nvl); 9852621Sllai1 return (0); 9862621Sllai1 } 987