1*2621Sllai1 /* 2*2621Sllai1 * CDDL HEADER START 3*2621Sllai1 * 4*2621Sllai1 * The contents of this file are subject to the terms of the 5*2621Sllai1 * Common Development and Distribution License (the "License"). 6*2621Sllai1 * You may not use this file except in compliance with the License. 7*2621Sllai1 * 8*2621Sllai1 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*2621Sllai1 * or http://www.opensolaris.org/os/licensing. 10*2621Sllai1 * See the License for the specific language governing permissions 11*2621Sllai1 * and limitations under the License. 12*2621Sllai1 * 13*2621Sllai1 * When distributing Covered Code, include this CDDL HEADER in each 14*2621Sllai1 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*2621Sllai1 * If applicable, add the following below this CDDL HEADER, with the 16*2621Sllai1 * fields enclosed by brackets "[]" replaced with your own identifying 17*2621Sllai1 * information: Portions Copyright [yyyy] [name of copyright owner] 18*2621Sllai1 * 19*2621Sllai1 * CDDL HEADER END 20*2621Sllai1 */ 21*2621Sllai1 22*2621Sllai1 /* 23*2621Sllai1 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24*2621Sllai1 * Use is subject to license terms. 25*2621Sllai1 */ 26*2621Sllai1 27*2621Sllai1 #pragma ident "%Z%%M% %I% %E% SMI" 28*2621Sllai1 29*2621Sllai1 /* 30*2621Sllai1 * This file implements /dev filesystem operations for non-global 31*2621Sllai1 * instances. Three major entry points: 32*2621Sllai1 * devname_profile_update() 33*2621Sllai1 * Update matching rules determining which names to export 34*2621Sllai1 * prof_readdir() 35*2621Sllai1 * Return the list of exported names 36*2621Sllai1 * prof_lookup() 37*2621Sllai1 * Implements lookup 38*2621Sllai1 */ 39*2621Sllai1 40*2621Sllai1 #include <sys/types.h> 41*2621Sllai1 #include <sys/param.h> 42*2621Sllai1 #include <sys/sysmacros.h> 43*2621Sllai1 #include <sys/vnode.h> 44*2621Sllai1 #include <sys/uio.h> 45*2621Sllai1 #include <sys/dirent.h> 46*2621Sllai1 #include <sys/pathname.h> 47*2621Sllai1 #include <sys/fs/dv_node.h> 48*2621Sllai1 #include <sys/fs/sdev_impl.h> 49*2621Sllai1 #include <sys/sunndi.h> 50*2621Sllai1 #include <sys/modctl.h> 51*2621Sllai1 52*2621Sllai1 enum { 53*2621Sllai1 PROFILE_TYPE_INCLUDE, 54*2621Sllai1 PROFILE_TYPE_EXCLUDE, 55*2621Sllai1 PROFILE_TYPE_MAP, 56*2621Sllai1 PROFILE_TYPE_SYMLINK 57*2621Sllai1 }; 58*2621Sllai1 59*2621Sllai1 enum { 60*2621Sllai1 WALK_DIR_CONTINUE = 0, 61*2621Sllai1 WALK_DIR_TERMINATE 62*2621Sllai1 }; 63*2621Sllai1 64*2621Sllai1 static const char *sdev_nvp_val_err = "nvpair_value error %d, %s\n"; 65*2621Sllai1 66*2621Sllai1 static void process_rule(struct sdev_node *, struct sdev_node *, 67*2621Sllai1 char *, char *, int); 68*2621Sllai1 static void walk_dir(struct vnode *, void *, int (*)(char *, void *)); 69*2621Sllai1 70*2621Sllai1 static void 71*2621Sllai1 prof_getattr(struct sdev_node *dir, char *name, struct vnode *gdv, 72*2621Sllai1 struct vattr *vap, struct vnode **avpp, int *no_fs_perm) 73*2621Sllai1 { 74*2621Sllai1 struct vnode *advp; 75*2621Sllai1 76*2621Sllai1 /* get attribute from shadow, if present; else get default */ 77*2621Sllai1 advp = dir->sdev_attrvp; 78*2621Sllai1 if (advp && VOP_LOOKUP(advp, name, avpp, NULL, 0, NULL, kcred) == 0) { 79*2621Sllai1 (void) VOP_GETATTR(*avpp, vap, 0, kcred); 80*2621Sllai1 } else if (gdv == NULL || gdv->v_type == VDIR) { 81*2621Sllai1 /* always create shadow directory */ 82*2621Sllai1 *vap = sdev_vattr_dir; 83*2621Sllai1 if (advp && VOP_MKDIR(advp, name, 84*2621Sllai1 &sdev_vattr_dir, avpp, kcred) != 0) { 85*2621Sllai1 *avpp = NULLVP; 86*2621Sllai1 sdcmn_err10(("prof_getattr: failed to create " 87*2621Sllai1 "shadow directory %s/%s\n", dir->sdev_path, name)); 88*2621Sllai1 } 89*2621Sllai1 } else { 90*2621Sllai1 /* 91*2621Sllai1 * get default permission from devfs 92*2621Sllai1 * Before calling devfs_get_defattr, we need to get 93*2621Sllai1 * the realvp (the dv_node). If realvp is not a dv_node, 94*2621Sllai1 * devfs_get_defattr() will return a system-wide default 95*2621Sllai1 * attr for device nodes. 96*2621Sllai1 */ 97*2621Sllai1 struct vnode *rvp; 98*2621Sllai1 if (VOP_REALVP(gdv, &rvp) != 0) 99*2621Sllai1 rvp = gdv; 100*2621Sllai1 devfs_get_defattr(rvp, vap, no_fs_perm); 101*2621Sllai1 *avpp = NULLVP; 102*2621Sllai1 } 103*2621Sllai1 104*2621Sllai1 /* ignore dev_t and vtype from backing store */ 105*2621Sllai1 if (gdv) { 106*2621Sllai1 vap->va_type = gdv->v_type; 107*2621Sllai1 vap->va_rdev = gdv->v_rdev; 108*2621Sllai1 } 109*2621Sllai1 } 110*2621Sllai1 111*2621Sllai1 static void 112*2621Sllai1 apply_glob_pattern(struct sdev_node *pdir, struct sdev_node *cdir) 113*2621Sllai1 { 114*2621Sllai1 char *name; 115*2621Sllai1 nvpair_t *nvp = NULL; 116*2621Sllai1 nvlist_t *nvl; 117*2621Sllai1 struct vnode *vp = SDEVTOV(cdir); 118*2621Sllai1 int rv = 0; 119*2621Sllai1 120*2621Sllai1 if (vp->v_type != VDIR) 121*2621Sllai1 return; 122*2621Sllai1 name = cdir->sdev_name; 123*2621Sllai1 nvl = pdir->sdev_prof.dev_glob_incdir; 124*2621Sllai1 while (nvp = nvlist_next_nvpair(nvl, nvp)) { 125*2621Sllai1 char *pathleft; 126*2621Sllai1 char *expr = nvpair_name(nvp); 127*2621Sllai1 if (!gmatch(name, expr)) 128*2621Sllai1 continue; 129*2621Sllai1 rv = nvpair_value_string(nvp, &pathleft); 130*2621Sllai1 if (rv != 0) { 131*2621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 132*2621Sllai1 rv, nvpair_name(nvp)); 133*2621Sllai1 break; 134*2621Sllai1 } 135*2621Sllai1 process_rule(cdir, cdir->sdev_origin, 136*2621Sllai1 pathleft, NULL, PROFILE_TYPE_INCLUDE); 137*2621Sllai1 } 138*2621Sllai1 } 139*2621Sllai1 140*2621Sllai1 /* 141*2621Sllai1 * Some commonality here with sdev_mknode(), could be simplified. 142*2621Sllai1 * NOTE: prof_mknode returns with *newdv held once, if success. 143*2621Sllai1 */ 144*2621Sllai1 static int 145*2621Sllai1 prof_mknode(struct sdev_node *dir, char *name, struct sdev_node **newdv, 146*2621Sllai1 vattr_t *vap, vnode_t *avp, void *arg, cred_t *cred) 147*2621Sllai1 { 148*2621Sllai1 struct sdev_node *dv; 149*2621Sllai1 int rv; 150*2621Sllai1 151*2621Sllai1 ASSERT(RW_WRITE_HELD(&dir->sdev_contents)); 152*2621Sllai1 153*2621Sllai1 /* check cache first */ 154*2621Sllai1 if (dv = sdev_cache_lookup(dir, name)) { 155*2621Sllai1 *newdv = dv; 156*2621Sllai1 return (0); 157*2621Sllai1 } 158*2621Sllai1 159*2621Sllai1 /* allocate node and insert into cache */ 160*2621Sllai1 rv = sdev_nodeinit(dir, name, &dv, NULL); 161*2621Sllai1 if (rv != 0) { 162*2621Sllai1 *newdv = NULL; 163*2621Sllai1 return (rv); 164*2621Sllai1 } 165*2621Sllai1 166*2621Sllai1 rv = sdev_cache_update(dir, &dv, name, SDEV_CACHE_ADD); 167*2621Sllai1 *newdv = dv; 168*2621Sllai1 169*2621Sllai1 /* put it in ready state */ 170*2621Sllai1 rv = sdev_nodeready(*newdv, vap, avp, arg, cred); 171*2621Sllai1 172*2621Sllai1 /* handle glob pattern in the middle of a path */ 173*2621Sllai1 if (rv == 0) { 174*2621Sllai1 if (SDEVTOV(*newdv)->v_type == VDIR) 175*2621Sllai1 sdcmn_err10(("sdev_origin for %s set to 0x%p\n", 176*2621Sllai1 name, arg)); 177*2621Sllai1 apply_glob_pattern(dir, *newdv); 178*2621Sllai1 } 179*2621Sllai1 return (rv); 180*2621Sllai1 } 181*2621Sllai1 182*2621Sllai1 /* 183*2621Sllai1 * Create a directory node in a non-global dev instance. 184*2621Sllai1 * Always create shadow vnode. Set sdev_origin to the corresponding 185*2621Sllai1 * global directory sdev_node if it exists. This facilitates the 186*2621Sllai1 * lookup operation. 187*2621Sllai1 */ 188*2621Sllai1 static int 189*2621Sllai1 prof_make_dir(char *name, struct sdev_node **gdirp, struct sdev_node **dirp) 190*2621Sllai1 { 191*2621Sllai1 struct sdev_node *dir = *dirp; 192*2621Sllai1 struct sdev_node *gdir = *gdirp; 193*2621Sllai1 struct sdev_node *newdv; 194*2621Sllai1 struct vnode *avp, *gnewdir = NULL; 195*2621Sllai1 struct vattr vattr; 196*2621Sllai1 int error; 197*2621Sllai1 198*2621Sllai1 /* see if name already exists */ 199*2621Sllai1 rw_enter(&dir->sdev_contents, RW_READER); 200*2621Sllai1 if (newdv = sdev_cache_lookup(dir, name)) { 201*2621Sllai1 *dirp = newdv; 202*2621Sllai1 *gdirp = newdv->sdev_origin; 203*2621Sllai1 SDEV_RELE(dir); 204*2621Sllai1 rw_exit(&dir->sdev_contents); 205*2621Sllai1 return (0); 206*2621Sllai1 } 207*2621Sllai1 rw_exit(&dir->sdev_contents); 208*2621Sllai1 209*2621Sllai1 /* find corresponding dir node in global dev */ 210*2621Sllai1 if (gdir) { 211*2621Sllai1 error = VOP_LOOKUP(SDEVTOV(gdir), name, &gnewdir, 212*2621Sllai1 NULL, 0, NULL, kcred); 213*2621Sllai1 if (error == 0) { 214*2621Sllai1 *gdirp = VTOSDEV(gnewdir); 215*2621Sllai1 } else { /* it's ok if there no global dir */ 216*2621Sllai1 *gdirp = NULL; 217*2621Sllai1 } 218*2621Sllai1 } 219*2621Sllai1 220*2621Sllai1 /* get attribute from shadow, also create shadow dir */ 221*2621Sllai1 prof_getattr(dir, name, gnewdir, &vattr, &avp, NULL); 222*2621Sllai1 223*2621Sllai1 /* create dev directory vnode */ 224*2621Sllai1 rw_enter(&dir->sdev_contents, RW_WRITER); 225*2621Sllai1 error = prof_mknode(dir, name, &newdv, &vattr, avp, (void *)*gdirp, 226*2621Sllai1 kcred); 227*2621Sllai1 rw_exit(&dir->sdev_contents); 228*2621Sllai1 if (error == 0) { 229*2621Sllai1 ASSERT(newdv); 230*2621Sllai1 *dirp = newdv; 231*2621Sllai1 } 232*2621Sllai1 SDEV_RELE(dir); 233*2621Sllai1 return (error); 234*2621Sllai1 } 235*2621Sllai1 236*2621Sllai1 /* 237*2621Sllai1 * Look up a logical name in the global zone. 238*2621Sllai1 * Provides the ability to map the global zone's device name 239*2621Sllai1 * to an alternate name within a zone. The primary example 240*2621Sllai1 * is the virtual console device /dev/zcons/[zonename]/zconsole 241*2621Sllai1 * mapped to /[zonename]/root/dev/zconsole. 242*2621Sllai1 */ 243*2621Sllai1 static void 244*2621Sllai1 prof_lookup_globaldev(struct sdev_node *dir, struct sdev_node *gdir, 245*2621Sllai1 char *name, char *rename) 246*2621Sllai1 { 247*2621Sllai1 /* global OS rootdir */ 248*2621Sllai1 extern vnode_t *rootdir; 249*2621Sllai1 250*2621Sllai1 int error; 251*2621Sllai1 struct vnode *avp, *gdv, *gddv; 252*2621Sllai1 struct sdev_node *newdv; 253*2621Sllai1 struct vattr vattr = {0}; 254*2621Sllai1 struct pathname pn; 255*2621Sllai1 256*2621Sllai1 /* check if node already exists */ 257*2621Sllai1 newdv = sdev_cache_lookup(dir, rename); 258*2621Sllai1 if (newdv) { 259*2621Sllai1 ASSERT(newdv->sdev_state != SDEV_ZOMBIE); 260*2621Sllai1 SDEV_SIMPLE_RELE(newdv); 261*2621Sllai1 return; 262*2621Sllai1 } 263*2621Sllai1 264*2621Sllai1 /* sanity check arguments */ 265*2621Sllai1 if (!gdir || pn_get(name, UIO_SYSSPACE, &pn)) 266*2621Sllai1 return; 267*2621Sllai1 268*2621Sllai1 /* perform a relative lookup of the global /dev instance */ 269*2621Sllai1 gddv = SDEVTOV(gdir); 270*2621Sllai1 VN_HOLD(gddv); 271*2621Sllai1 VN_HOLD(rootdir); 272*2621Sllai1 error = lookuppnvp(&pn, NULL, FOLLOW, NULLVPP, &gdv, 273*2621Sllai1 rootdir, gddv, kcred); 274*2621Sllai1 pn_free(&pn); 275*2621Sllai1 if (error) { 276*2621Sllai1 sdcmn_err10(("prof_lookup_globaldev: %s not found\n", name)); 277*2621Sllai1 return; 278*2621Sllai1 } 279*2621Sllai1 ASSERT(gdv && gdv->v_type != VLNK); 280*2621Sllai1 281*2621Sllai1 /* 282*2621Sllai1 * Found the entry in global /dev, figure out attributes 283*2621Sllai1 * by looking at backing store. Call into devfs for default. 284*2621Sllai1 */ 285*2621Sllai1 prof_getattr(dir, name, gdv, &vattr, &avp, NULL); 286*2621Sllai1 287*2621Sllai1 if (gdv->v_type != VDIR) { 288*2621Sllai1 VN_RELE(gdv); 289*2621Sllai1 gdir = NULL; 290*2621Sllai1 } else 291*2621Sllai1 gdir = VTOSDEV(gdv); 292*2621Sllai1 293*2621Sllai1 if (prof_mknode(dir, rename, &newdv, &vattr, avp, 294*2621Sllai1 (void *)gdir, kcred) == 0) { 295*2621Sllai1 ASSERT(newdv->sdev_state != SDEV_ZOMBIE); 296*2621Sllai1 SDEV_SIMPLE_RELE(newdv); 297*2621Sllai1 } 298*2621Sllai1 } 299*2621Sllai1 300*2621Sllai1 static void 301*2621Sllai1 prof_make_sym(struct sdev_node *dir, char *lnm, char *tgt) 302*2621Sllai1 { 303*2621Sllai1 struct sdev_node *newdv; 304*2621Sllai1 305*2621Sllai1 if (prof_mknode(dir, lnm, &newdv, &sdev_vattr_lnk, NULL, 306*2621Sllai1 (void *)tgt, kcred) == 0) { 307*2621Sllai1 ASSERT(newdv->sdev_state != SDEV_ZOMBIE); 308*2621Sllai1 SDEV_SIMPLE_RELE(newdv); 309*2621Sllai1 } 310*2621Sllai1 } 311*2621Sllai1 312*2621Sllai1 /* 313*2621Sllai1 * Create symlinks in the current directory based on profile 314*2621Sllai1 */ 315*2621Sllai1 static void 316*2621Sllai1 prof_make_symlinks(struct sdev_node *dir) 317*2621Sllai1 { 318*2621Sllai1 char *tgt, *lnm; 319*2621Sllai1 nvpair_t *nvp = NULL; 320*2621Sllai1 nvlist_t *nvl = dir->sdev_prof.dev_symlink; 321*2621Sllai1 int rv; 322*2621Sllai1 323*2621Sllai1 ASSERT(RW_WRITE_HELD(&dir->sdev_contents)); 324*2621Sllai1 325*2621Sllai1 if (nvl == NULL) 326*2621Sllai1 return; 327*2621Sllai1 328*2621Sllai1 while (nvp = nvlist_next_nvpair(nvl, nvp)) { 329*2621Sllai1 lnm = nvpair_name(nvp); 330*2621Sllai1 rv = nvpair_value_string(nvp, &tgt); 331*2621Sllai1 if (rv != 0) { 332*2621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 333*2621Sllai1 rv, nvpair_name(nvp)); 334*2621Sllai1 break; 335*2621Sllai1 } 336*2621Sllai1 prof_make_sym(dir, lnm, tgt); 337*2621Sllai1 } 338*2621Sllai1 } 339*2621Sllai1 340*2621Sllai1 static void 341*2621Sllai1 prof_make_maps(struct sdev_node *dir) 342*2621Sllai1 { 343*2621Sllai1 nvpair_t *nvp = NULL; 344*2621Sllai1 nvlist_t *nvl = dir->sdev_prof.dev_map; 345*2621Sllai1 int rv; 346*2621Sllai1 347*2621Sllai1 ASSERT(RW_WRITE_HELD(&dir->sdev_contents)); 348*2621Sllai1 349*2621Sllai1 if (nvl == NULL) 350*2621Sllai1 return; 351*2621Sllai1 352*2621Sllai1 while (nvp = nvlist_next_nvpair(nvl, nvp)) { 353*2621Sllai1 char *name; 354*2621Sllai1 char *rename = nvpair_name(nvp); 355*2621Sllai1 rv = nvpair_value_string(nvp, &name); 356*2621Sllai1 if (rv != 0) { 357*2621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 358*2621Sllai1 rv, nvpair_name(nvp)); 359*2621Sllai1 break; 360*2621Sllai1 } 361*2621Sllai1 sdcmn_err10(("map %s -> %s\n", name, rename)); 362*2621Sllai1 (void) prof_lookup_globaldev(dir, sdev_origins->sdev_root, 363*2621Sllai1 name, rename); 364*2621Sllai1 } 365*2621Sllai1 } 366*2621Sllai1 367*2621Sllai1 struct match_arg { 368*2621Sllai1 char *expr; 369*2621Sllai1 int match; 370*2621Sllai1 }; 371*2621Sllai1 372*2621Sllai1 static int 373*2621Sllai1 match_name(char *name, void *arg) 374*2621Sllai1 { 375*2621Sllai1 struct match_arg *margp = (struct match_arg *)arg; 376*2621Sllai1 377*2621Sllai1 if (gmatch(name, margp->expr)) { 378*2621Sllai1 margp->match = 1; 379*2621Sllai1 return (WALK_DIR_TERMINATE); 380*2621Sllai1 } 381*2621Sllai1 return (WALK_DIR_CONTINUE); 382*2621Sllai1 } 383*2621Sllai1 384*2621Sllai1 static int 385*2621Sllai1 is_nonempty_dir(char *name, char *pathleft, struct sdev_node *dir) 386*2621Sllai1 { 387*2621Sllai1 struct match_arg marg; 388*2621Sllai1 struct pathname pn; 389*2621Sllai1 struct vnode *gvp; 390*2621Sllai1 struct sdev_node *gdir = dir->sdev_origin; 391*2621Sllai1 392*2621Sllai1 if (VOP_LOOKUP(SDEVTOV(gdir), name, &gvp, NULL, 0, NULL, kcred) != 0) 393*2621Sllai1 return (0); 394*2621Sllai1 395*2621Sllai1 if (gvp->v_type != VDIR) { 396*2621Sllai1 VN_RELE(gvp); 397*2621Sllai1 return (0); 398*2621Sllai1 } 399*2621Sllai1 400*2621Sllai1 if (pn_get(pathleft, UIO_SYSSPACE, &pn) != 0) { 401*2621Sllai1 VN_RELE(gvp); 402*2621Sllai1 return (0); 403*2621Sllai1 } 404*2621Sllai1 405*2621Sllai1 marg.expr = kmem_alloc(MAXNAMELEN, KM_SLEEP); 406*2621Sllai1 (void) pn_getcomponent(&pn, marg.expr); 407*2621Sllai1 marg.match = 0; 408*2621Sllai1 409*2621Sllai1 walk_dir(gvp, &marg, match_name); 410*2621Sllai1 VN_RELE(gvp); 411*2621Sllai1 kmem_free(marg.expr, MAXNAMELEN); 412*2621Sllai1 pn_free(&pn); 413*2621Sllai1 414*2621Sllai1 return (marg.match); 415*2621Sllai1 } 416*2621Sllai1 417*2621Sllai1 418*2621Sllai1 /* Check if name passes matching rules */ 419*2621Sllai1 static int 420*2621Sllai1 prof_name_matched(char *name, struct sdev_node *dir) 421*2621Sllai1 { 422*2621Sllai1 int type, match = 0; 423*2621Sllai1 char *expr; 424*2621Sllai1 nvlist_t *nvl; 425*2621Sllai1 nvpair_t *nvp = NULL; 426*2621Sllai1 int rv; 427*2621Sllai1 428*2621Sllai1 /* check against nvlist for leaf include/exclude */ 429*2621Sllai1 nvl = dir->sdev_prof.dev_name; 430*2621Sllai1 while (nvp = nvlist_next_nvpair(nvl, nvp)) { 431*2621Sllai1 expr = nvpair_name(nvp); 432*2621Sllai1 rv = nvpair_value_int32(nvp, &type); 433*2621Sllai1 if (rv != 0) { 434*2621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 435*2621Sllai1 rv, nvpair_name(nvp)); 436*2621Sllai1 break; 437*2621Sllai1 } 438*2621Sllai1 439*2621Sllai1 if (type == PROFILE_TYPE_EXCLUDE) { 440*2621Sllai1 if (gmatch(name, expr)) 441*2621Sllai1 return (0); /* excluded */ 442*2621Sllai1 } else if (!match) { 443*2621Sllai1 match = gmatch(name, expr); 444*2621Sllai1 } 445*2621Sllai1 } 446*2621Sllai1 if (match) { 447*2621Sllai1 sdcmn_err10(("prof_name_matched: %s\n", name)); 448*2621Sllai1 return (match); 449*2621Sllai1 } 450*2621Sllai1 451*2621Sllai1 /* check for match against directory globbing pattern */ 452*2621Sllai1 nvl = dir->sdev_prof.dev_glob_incdir; 453*2621Sllai1 while (nvp = nvlist_next_nvpair(nvl, nvp)) { 454*2621Sllai1 char *pathleft; 455*2621Sllai1 expr = nvpair_name(nvp); 456*2621Sllai1 if (gmatch(name, expr) == 0) 457*2621Sllai1 continue; 458*2621Sllai1 rv = nvpair_value_string(nvp, &pathleft); 459*2621Sllai1 if (rv != 0) { 460*2621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 461*2621Sllai1 rv, nvpair_name(nvp)); 462*2621Sllai1 break; 463*2621Sllai1 } 464*2621Sllai1 if (is_nonempty_dir(name, pathleft, dir)) { 465*2621Sllai1 sdcmn_err10(("prof_name_matched: dir %s\n", name)); 466*2621Sllai1 return (1); 467*2621Sllai1 } 468*2621Sllai1 } 469*2621Sllai1 470*2621Sllai1 return (0); 471*2621Sllai1 } 472*2621Sllai1 473*2621Sllai1 static void 474*2621Sllai1 walk_dir(struct vnode *dvp, void *arg, int (*callback)(char *, void *)) 475*2621Sllai1 { 476*2621Sllai1 char *nm; 477*2621Sllai1 int eof, error; 478*2621Sllai1 struct iovec iov; 479*2621Sllai1 struct uio uio; 480*2621Sllai1 struct dirent64 *dp; 481*2621Sllai1 dirent64_t *dbuf; 482*2621Sllai1 size_t dbuflen, dlen; 483*2621Sllai1 484*2621Sllai1 ASSERT(dvp); 485*2621Sllai1 486*2621Sllai1 dlen = 4096; 487*2621Sllai1 dbuf = kmem_zalloc(dlen, KM_SLEEP); 488*2621Sllai1 489*2621Sllai1 uio.uio_iov = &iov; 490*2621Sllai1 uio.uio_iovcnt = 1; 491*2621Sllai1 uio.uio_segflg = UIO_SYSSPACE; 492*2621Sllai1 uio.uio_fmode = 0; 493*2621Sllai1 uio.uio_extflg = UIO_COPY_CACHED; 494*2621Sllai1 uio.uio_loffset = 0; 495*2621Sllai1 uio.uio_llimit = MAXOFFSET_T; 496*2621Sllai1 497*2621Sllai1 eof = 0; 498*2621Sllai1 error = 0; 499*2621Sllai1 while (!error && !eof) { 500*2621Sllai1 uio.uio_resid = dlen; 501*2621Sllai1 iov.iov_base = (char *)dbuf; 502*2621Sllai1 iov.iov_len = dlen; 503*2621Sllai1 (void) VOP_RWLOCK(dvp, V_WRITELOCK_FALSE, NULL); 504*2621Sllai1 error = VOP_READDIR(dvp, &uio, kcred, &eof); 505*2621Sllai1 VOP_RWUNLOCK(dvp, V_WRITELOCK_FALSE, NULL); 506*2621Sllai1 507*2621Sllai1 dbuflen = dlen - uio.uio_resid; 508*2621Sllai1 if (error || dbuflen == 0) 509*2621Sllai1 break; 510*2621Sllai1 for (dp = dbuf; ((intptr_t)dp < 511*2621Sllai1 (intptr_t)dbuf + dbuflen); 512*2621Sllai1 dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) { 513*2621Sllai1 nm = dp->d_name; 514*2621Sllai1 515*2621Sllai1 if (strcmp(nm, ".") == 0 || 516*2621Sllai1 strcmp(nm, "..") == 0) 517*2621Sllai1 continue; 518*2621Sllai1 519*2621Sllai1 if (callback(nm, arg) == WALK_DIR_TERMINATE) 520*2621Sllai1 goto end; 521*2621Sllai1 } 522*2621Sllai1 } 523*2621Sllai1 524*2621Sllai1 end: 525*2621Sllai1 kmem_free(dbuf, dlen); 526*2621Sllai1 } 527*2621Sllai1 528*2621Sllai1 static int 529*2621Sllai1 prof_make_name(char *nm, void *arg) 530*2621Sllai1 { 531*2621Sllai1 struct sdev_node *ddv = (struct sdev_node *)arg; 532*2621Sllai1 533*2621Sllai1 if (prof_name_matched(nm, ddv)) 534*2621Sllai1 prof_lookup_globaldev(ddv, ddv->sdev_origin, nm, nm); 535*2621Sllai1 return (WALK_DIR_CONTINUE); 536*2621Sllai1 } 537*2621Sllai1 538*2621Sllai1 static void 539*2621Sllai1 prof_make_names_glob(struct sdev_node *ddv) 540*2621Sllai1 { 541*2621Sllai1 struct sdev_node *gdir; 542*2621Sllai1 543*2621Sllai1 gdir = ddv->sdev_origin; 544*2621Sllai1 if (gdir == NULL) 545*2621Sllai1 return; 546*2621Sllai1 walk_dir(SDEVTOV(gdir), (void *)ddv, prof_make_name); 547*2621Sllai1 } 548*2621Sllai1 549*2621Sllai1 static void 550*2621Sllai1 prof_make_names(struct sdev_node *dir) 551*2621Sllai1 { 552*2621Sllai1 char *name; 553*2621Sllai1 nvpair_t *nvp = NULL; 554*2621Sllai1 nvlist_t *nvl = dir->sdev_prof.dev_name; 555*2621Sllai1 int rv; 556*2621Sllai1 557*2621Sllai1 ASSERT(RW_WRITE_HELD(&dir->sdev_contents)); 558*2621Sllai1 559*2621Sllai1 if (nvl == NULL) 560*2621Sllai1 return; 561*2621Sllai1 562*2621Sllai1 if (dir->sdev_prof.has_glob) { 563*2621Sllai1 prof_make_names_glob(dir); 564*2621Sllai1 return; 565*2621Sllai1 } 566*2621Sllai1 567*2621Sllai1 /* Walk nvlist and lookup corresponding device in global inst */ 568*2621Sllai1 while (nvp = nvlist_next_nvpair(nvl, nvp)) { 569*2621Sllai1 int type; 570*2621Sllai1 rv = nvpair_value_int32(nvp, &type); 571*2621Sllai1 if (rv != 0) { 572*2621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 573*2621Sllai1 rv, nvpair_name(nvp)); 574*2621Sllai1 break; 575*2621Sllai1 } 576*2621Sllai1 if (type == PROFILE_TYPE_EXCLUDE) 577*2621Sllai1 continue; 578*2621Sllai1 name = nvpair_name(nvp); 579*2621Sllai1 (void) prof_lookup_globaldev(dir, dir->sdev_origin, 580*2621Sllai1 name, name); 581*2621Sllai1 } 582*2621Sllai1 } 583*2621Sllai1 584*2621Sllai1 /* 585*2621Sllai1 * Build directory vnodes based on the profile and the global 586*2621Sllai1 * dev instance. 587*2621Sllai1 */ 588*2621Sllai1 void 589*2621Sllai1 prof_filldir(struct sdev_node *ddv) 590*2621Sllai1 { 591*2621Sllai1 int firsttime = 1; 592*2621Sllai1 struct sdev_node *gdir = ddv->sdev_origin; 593*2621Sllai1 594*2621Sllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 595*2621Sllai1 596*2621Sllai1 /* 597*2621Sllai1 * We need to rebuild the directory content if 598*2621Sllai1 * - SDEV_BUILD is set 599*2621Sllai1 * - The device tree generation number has changed 600*2621Sllai1 * - The corresponding /dev namespace has been updated 601*2621Sllai1 */ 602*2621Sllai1 check_build: 603*2621Sllai1 if ((ddv->sdev_flags & SDEV_BUILD) == 0 && 604*2621Sllai1 ddv->sdev_devtree_gen == devtree_gen && 605*2621Sllai1 (gdir == NULL || ddv->sdev_ldir_gen 606*2621Sllai1 == gdir->sdev_gdir_gen)) 607*2621Sllai1 return; /* already up to date */ 608*2621Sllai1 609*2621Sllai1 if (firsttime && rw_tryupgrade(&ddv->sdev_contents) == 0) { 610*2621Sllai1 rw_exit(&ddv->sdev_contents); 611*2621Sllai1 firsttime = 0; 612*2621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 613*2621Sllai1 goto check_build; 614*2621Sllai1 } 615*2621Sllai1 sdcmn_err10(("devtree_gen (%s): %ld -> %ld\n", 616*2621Sllai1 ddv->sdev_path, ddv->sdev_devtree_gen, devtree_gen)); 617*2621Sllai1 if (gdir) 618*2621Sllai1 sdcmn_err10(("sdev_dir_gen (%s): %ld -> %ld\n", 619*2621Sllai1 ddv->sdev_path, ddv->sdev_ldir_gen, 620*2621Sllai1 gdir->sdev_gdir_gen)); 621*2621Sllai1 622*2621Sllai1 /* update flags and generation number so next filldir is quick */ 623*2621Sllai1 ddv->sdev_flags &= ~SDEV_BUILD; 624*2621Sllai1 ddv->sdev_devtree_gen = devtree_gen; 625*2621Sllai1 if (gdir) 626*2621Sllai1 ddv->sdev_ldir_gen = gdir->sdev_gdir_gen; 627*2621Sllai1 628*2621Sllai1 prof_make_symlinks(ddv); 629*2621Sllai1 prof_make_maps(ddv); 630*2621Sllai1 prof_make_names(ddv); 631*2621Sllai1 rw_downgrade(&ddv->sdev_contents); 632*2621Sllai1 } 633*2621Sllai1 634*2621Sllai1 /* apply include/exclude pattern to existing directory content */ 635*2621Sllai1 static void 636*2621Sllai1 apply_dir_pattern(struct sdev_node *dir, char *expr, char *pathleft, int type) 637*2621Sllai1 { 638*2621Sllai1 struct sdev_node *dv; 639*2621Sllai1 640*2621Sllai1 /* leaf pattern */ 641*2621Sllai1 if (pathleft == NULL) { 642*2621Sllai1 if (type == PROFILE_TYPE_INCLUDE) 643*2621Sllai1 return; /* nothing to do for include */ 644*2621Sllai1 (void) sdev_cleandir(dir, expr, SDEV_ENFORCE); 645*2621Sllai1 return; 646*2621Sllai1 } 647*2621Sllai1 648*2621Sllai1 /* directory pattern */ 649*2621Sllai1 rw_enter(&dir->sdev_contents, RW_WRITER); 650*2621Sllai1 for (dv = dir->sdev_dot; dv; dv = dv->sdev_next) { 651*2621Sllai1 if (gmatch(dv->sdev_name, expr) == 0 || 652*2621Sllai1 SDEVTOV(dv)->v_type != VDIR) 653*2621Sllai1 continue; 654*2621Sllai1 process_rule(dv, dv->sdev_origin, 655*2621Sllai1 pathleft, NULL, type); 656*2621Sllai1 } 657*2621Sllai1 rw_exit(&dir->sdev_contents); 658*2621Sllai1 } 659*2621Sllai1 660*2621Sllai1 /* 661*2621Sllai1 * Add a profile rule. 662*2621Sllai1 * tgt represents a device name matching expression, 663*2621Sllai1 * matching device names are to be either included or excluded. 664*2621Sllai1 */ 665*2621Sllai1 static void 666*2621Sllai1 prof_add_rule(char *name, char *tgt, struct sdev_node *dir, int type) 667*2621Sllai1 { 668*2621Sllai1 int error; 669*2621Sllai1 nvlist_t **nvlp = NULL; 670*2621Sllai1 int rv; 671*2621Sllai1 672*2621Sllai1 ASSERT(SDEVTOV(dir)->v_type == VDIR); 673*2621Sllai1 674*2621Sllai1 rw_enter(&dir->sdev_contents, RW_WRITER); 675*2621Sllai1 676*2621Sllai1 switch (type) { 677*2621Sllai1 case PROFILE_TYPE_INCLUDE: 678*2621Sllai1 if (tgt) 679*2621Sllai1 nvlp = &(dir->sdev_prof.dev_glob_incdir); 680*2621Sllai1 else 681*2621Sllai1 nvlp = &(dir->sdev_prof.dev_name); 682*2621Sllai1 break; 683*2621Sllai1 case PROFILE_TYPE_EXCLUDE: 684*2621Sllai1 if (tgt) 685*2621Sllai1 nvlp = &(dir->sdev_prof.dev_glob_excdir); 686*2621Sllai1 else 687*2621Sllai1 nvlp = &(dir->sdev_prof.dev_name); 688*2621Sllai1 break; 689*2621Sllai1 case PROFILE_TYPE_MAP: 690*2621Sllai1 nvlp = &(dir->sdev_prof.dev_map); 691*2621Sllai1 break; 692*2621Sllai1 case PROFILE_TYPE_SYMLINK: 693*2621Sllai1 nvlp = &(dir->sdev_prof.dev_symlink); 694*2621Sllai1 break; 695*2621Sllai1 }; 696*2621Sllai1 697*2621Sllai1 /* initialize nvlist */ 698*2621Sllai1 if (*nvlp == NULL) { 699*2621Sllai1 error = nvlist_alloc(nvlp, NV_UNIQUE_NAME, KM_SLEEP); 700*2621Sllai1 ASSERT(error == 0); 701*2621Sllai1 } 702*2621Sllai1 703*2621Sllai1 if (tgt) { 704*2621Sllai1 rv = nvlist_add_string(*nvlp, name, tgt); 705*2621Sllai1 } else { 706*2621Sllai1 rv = nvlist_add_int32(*nvlp, name, type); 707*2621Sllai1 } 708*2621Sllai1 ASSERT(rv == 0); 709*2621Sllai1 /* rebuild directory content */ 710*2621Sllai1 dir->sdev_flags |= SDEV_BUILD; 711*2621Sllai1 712*2621Sllai1 if ((type == PROFILE_TYPE_INCLUDE) && 713*2621Sllai1 (strpbrk(name, "*?[]") != NULL)) { 714*2621Sllai1 dir->sdev_prof.has_glob = 1; 715*2621Sllai1 } 716*2621Sllai1 717*2621Sllai1 rw_exit(&dir->sdev_contents); 718*2621Sllai1 719*2621Sllai1 /* additional details for glob pattern and exclusion */ 720*2621Sllai1 switch (type) { 721*2621Sllai1 case PROFILE_TYPE_INCLUDE: 722*2621Sllai1 case PROFILE_TYPE_EXCLUDE: 723*2621Sllai1 apply_dir_pattern(dir, name, tgt, type); 724*2621Sllai1 break; 725*2621Sllai1 }; 726*2621Sllai1 } 727*2621Sllai1 728*2621Sllai1 /* 729*2621Sllai1 * Parse path components and apply requested matching rule at 730*2621Sllai1 * directory level. 731*2621Sllai1 */ 732*2621Sllai1 static void 733*2621Sllai1 process_rule(struct sdev_node *dir, struct sdev_node *gdir, 734*2621Sllai1 char *path, char *tgt, int type) 735*2621Sllai1 { 736*2621Sllai1 char *name; 737*2621Sllai1 struct pathname pn; 738*2621Sllai1 int rv = 0; 739*2621Sllai1 740*2621Sllai1 if ((strlen(path) > 5) && (strncmp(path, "/dev/", 5) == 0)) { 741*2621Sllai1 path += 5; 742*2621Sllai1 } 743*2621Sllai1 744*2621Sllai1 if (pn_get(path, UIO_SYSSPACE, &pn) != 0) 745*2621Sllai1 return; 746*2621Sllai1 747*2621Sllai1 name = kmem_alloc(MAXPATHLEN, KM_SLEEP); 748*2621Sllai1 (void) pn_getcomponent(&pn, name); 749*2621Sllai1 pn_skipslash(&pn); 750*2621Sllai1 SDEV_HOLD(dir); 751*2621Sllai1 752*2621Sllai1 while (pn_pathleft(&pn)) { 753*2621Sllai1 /* If this is pattern, just add the pattern */ 754*2621Sllai1 if (strpbrk(name, "*?[]") != NULL && 755*2621Sllai1 (type == PROFILE_TYPE_INCLUDE || 756*2621Sllai1 type == PROFILE_TYPE_EXCLUDE)) { 757*2621Sllai1 ASSERT(tgt == NULL); 758*2621Sllai1 tgt = pn.pn_path; 759*2621Sllai1 break; 760*2621Sllai1 } 761*2621Sllai1 if ((rv = prof_make_dir(name, &gdir, &dir)) != 0) { 762*2621Sllai1 cmn_err(CE_CONT, "process_rule: %s error %d\n", 763*2621Sllai1 path, rv); 764*2621Sllai1 break; 765*2621Sllai1 } 766*2621Sllai1 (void) pn_getcomponent(&pn, name); 767*2621Sllai1 pn_skipslash(&pn); 768*2621Sllai1 } 769*2621Sllai1 770*2621Sllai1 /* process the leaf component */ 771*2621Sllai1 if (rv == 0) { 772*2621Sllai1 prof_add_rule(name, tgt, dir, type); 773*2621Sllai1 SDEV_SIMPLE_RELE(dir); 774*2621Sllai1 } 775*2621Sllai1 776*2621Sllai1 kmem_free(name, MAXPATHLEN); 777*2621Sllai1 pn_free(&pn); 778*2621Sllai1 } 779*2621Sllai1 780*2621Sllai1 static int 781*2621Sllai1 copyin_nvlist(char *packed_usr, size_t packed_sz, nvlist_t **nvlp) 782*2621Sllai1 { 783*2621Sllai1 int err = 0; 784*2621Sllai1 char *packed; 785*2621Sllai1 nvlist_t *profile = NULL; 786*2621Sllai1 787*2621Sllai1 /* simple sanity check */ 788*2621Sllai1 if (packed_usr == NULL || packed_sz == 0) 789*2621Sllai1 return (NULL); 790*2621Sllai1 791*2621Sllai1 /* copyin packed profile nvlist */ 792*2621Sllai1 packed = kmem_alloc(packed_sz, KM_NOSLEEP); 793*2621Sllai1 if (packed == NULL) 794*2621Sllai1 return (ENOMEM); 795*2621Sllai1 err = copyin(packed_usr, packed, packed_sz); 796*2621Sllai1 797*2621Sllai1 /* unpack packed profile nvlist */ 798*2621Sllai1 if (err) 799*2621Sllai1 cmn_err(CE_WARN, "copyin_nvlist: copyin failed with " 800*2621Sllai1 "err %d\n", err); 801*2621Sllai1 else if (err = nvlist_unpack(packed, packed_sz, &profile, KM_NOSLEEP)) 802*2621Sllai1 cmn_err(CE_WARN, "copyin_nvlist: nvlist_unpack " 803*2621Sllai1 "failed with err %d\n", err); 804*2621Sllai1 805*2621Sllai1 kmem_free(packed, packed_sz); 806*2621Sllai1 if (err == 0) 807*2621Sllai1 *nvlp = profile; 808*2621Sllai1 return (err); 809*2621Sllai1 } 810*2621Sllai1 811*2621Sllai1 /* 812*2621Sllai1 * Process profile passed down from libdevinfo. There are four types 813*2621Sllai1 * of matching rules: 814*2621Sllai1 * include: export a name or names matching a pattern 815*2621Sllai1 * exclude: exclude a name or names matching a pattern 816*2621Sllai1 * symlink: create a local symlink 817*2621Sllai1 * map: export a device with a name different from the global zone 818*2621Sllai1 * Note: We may consider supporting VOP_SYMLINK in non-global instances, 819*2621Sllai1 * because it does not present any security risk. For now, the fs 820*2621Sllai1 * instance is read only. 821*2621Sllai1 */ 822*2621Sllai1 static void 823*2621Sllai1 sdev_process_profile(struct sdev_data *sdev_data, nvlist_t *profile) 824*2621Sllai1 { 825*2621Sllai1 nvpair_t *nvpair; 826*2621Sllai1 char *nvname, *dname; 827*2621Sllai1 struct sdev_node *dir, *gdir; 828*2621Sllai1 char **pair; /* for symlinks and maps */ 829*2621Sllai1 uint_t nelem; 830*2621Sllai1 int rv; 831*2621Sllai1 832*2621Sllai1 gdir = sdev_origins->sdev_root; /* root of global /dev */ 833*2621Sllai1 dir = sdev_data->sdev_root; /* root of current instance */ 834*2621Sllai1 835*2621Sllai1 ASSERT(profile); 836*2621Sllai1 837*2621Sllai1 /* process nvpairs in the list */ 838*2621Sllai1 nvpair = NULL; 839*2621Sllai1 while (nvpair = nvlist_next_nvpair(profile, nvpair)) { 840*2621Sllai1 nvname = nvpair_name(nvpair); 841*2621Sllai1 ASSERT(nvname != NULL); 842*2621Sllai1 843*2621Sllai1 if (strcmp(nvname, SDEV_NVNAME_INCLUDE) == 0) { 844*2621Sllai1 rv = nvpair_value_string(nvpair, &dname); 845*2621Sllai1 if (rv != 0) { 846*2621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 847*2621Sllai1 rv, nvpair_name(nvpair)); 848*2621Sllai1 break; 849*2621Sllai1 } 850*2621Sllai1 process_rule(dir, gdir, dname, NULL, 851*2621Sllai1 PROFILE_TYPE_INCLUDE); 852*2621Sllai1 } else if (strcmp(nvname, SDEV_NVNAME_EXCLUDE) == 0) { 853*2621Sllai1 rv = nvpair_value_string(nvpair, &dname); 854*2621Sllai1 if (rv != 0) { 855*2621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 856*2621Sllai1 rv, nvpair_name(nvpair)); 857*2621Sllai1 break; 858*2621Sllai1 } 859*2621Sllai1 process_rule(dir, gdir, dname, NULL, 860*2621Sllai1 PROFILE_TYPE_EXCLUDE); 861*2621Sllai1 } else if (strcmp(nvname, SDEV_NVNAME_SYMLINK) == 0) { 862*2621Sllai1 rv = nvpair_value_string_array(nvpair, &pair, &nelem); 863*2621Sllai1 if (rv != 0) { 864*2621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 865*2621Sllai1 rv, nvpair_name(nvpair)); 866*2621Sllai1 break; 867*2621Sllai1 } 868*2621Sllai1 ASSERT(nelem == 2); 869*2621Sllai1 process_rule(dir, gdir, pair[0], pair[1], 870*2621Sllai1 PROFILE_TYPE_SYMLINK); 871*2621Sllai1 } else if (strcmp(nvname, SDEV_NVNAME_MAP) == 0) { 872*2621Sllai1 rv = nvpair_value_string_array(nvpair, &pair, &nelem); 873*2621Sllai1 if (rv != 0) { 874*2621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 875*2621Sllai1 rv, nvpair_name(nvpair)); 876*2621Sllai1 break; 877*2621Sllai1 } 878*2621Sllai1 process_rule(dir, gdir, pair[1], pair[0], 879*2621Sllai1 PROFILE_TYPE_MAP); 880*2621Sllai1 } else if (strcmp(nvname, SDEV_NVNAME_MOUNTPT) != 0) { 881*2621Sllai1 cmn_err(CE_WARN, "sdev_process_profile: invalid " 882*2621Sllai1 "nvpair %s\n", nvname); 883*2621Sllai1 } 884*2621Sllai1 } 885*2621Sllai1 } 886*2621Sllai1 887*2621Sllai1 /*ARGSUSED*/ 888*2621Sllai1 int 889*2621Sllai1 prof_lookup(vnode_t *dvp, char *nm, struct vnode **vpp, struct cred *cred) 890*2621Sllai1 { 891*2621Sllai1 struct sdev_node *ddv = VTOSDEV(dvp); 892*2621Sllai1 struct sdev_node *dv; 893*2621Sllai1 int nmlen; 894*2621Sllai1 895*2621Sllai1 /* 896*2621Sllai1 * Empty name or ., return node itself. 897*2621Sllai1 */ 898*2621Sllai1 nmlen = strlen(nm); 899*2621Sllai1 if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) { 900*2621Sllai1 *vpp = SDEVTOV(ddv); 901*2621Sllai1 VN_HOLD(*vpp); 902*2621Sllai1 return (0); 903*2621Sllai1 } 904*2621Sllai1 905*2621Sllai1 /* 906*2621Sllai1 * .., return the parent directory 907*2621Sllai1 */ 908*2621Sllai1 if ((nmlen == 2) && (strcmp(nm, "..") == 0)) { 909*2621Sllai1 *vpp = SDEVTOV(ddv->sdev_dotdot); 910*2621Sllai1 VN_HOLD(*vpp); 911*2621Sllai1 return (0); 912*2621Sllai1 } 913*2621Sllai1 914*2621Sllai1 rw_enter(&ddv->sdev_contents, RW_READER); 915*2621Sllai1 dv = sdev_cache_lookup(ddv, nm); 916*2621Sllai1 if (dv == NULL) { 917*2621Sllai1 prof_filldir(ddv); 918*2621Sllai1 dv = sdev_cache_lookup(ddv, nm); 919*2621Sllai1 } 920*2621Sllai1 rw_exit(&ddv->sdev_contents); 921*2621Sllai1 if (dv == NULL) { 922*2621Sllai1 sdcmn_err10(("prof_lookup: %s not found\n", nm)); 923*2621Sllai1 return (ENOENT); 924*2621Sllai1 } 925*2621Sllai1 926*2621Sllai1 return (sdev_to_vp(dv, vpp)); 927*2621Sllai1 } 928*2621Sllai1 929*2621Sllai1 /* 930*2621Sllai1 * This is invoked after a new filesystem is mounted to define the 931*2621Sllai1 * name space. It is also invoked during normal system operation 932*2621Sllai1 * to update the name space. 933*2621Sllai1 * 934*2621Sllai1 * Applications call di_prof_commit() in libdevinfo, which invokes 935*2621Sllai1 * modctl(). modctl calls this function. The input is a packed nvlist. 936*2621Sllai1 */ 937*2621Sllai1 int 938*2621Sllai1 devname_profile_update(char *packed, size_t packed_sz) 939*2621Sllai1 { 940*2621Sllai1 char *mntpt; 941*2621Sllai1 nvlist_t *nvl; 942*2621Sllai1 nvpair_t *nvp; 943*2621Sllai1 struct sdev_data *mntinfo; 944*2621Sllai1 int err; 945*2621Sllai1 int rv; 946*2621Sllai1 947*2621Sllai1 nvl = NULL; 948*2621Sllai1 if ((err = copyin_nvlist(packed, packed_sz, &nvl)) != 0) 949*2621Sllai1 return (err); 950*2621Sllai1 ASSERT(nvl); 951*2621Sllai1 952*2621Sllai1 /* The first nvpair must be the mount point */ 953*2621Sllai1 nvp = nvlist_next_nvpair(nvl, NULL); 954*2621Sllai1 if (strcmp(nvpair_name(nvp), SDEV_NVNAME_MOUNTPT) != 0) { 955*2621Sllai1 cmn_err(CE_NOTE, 956*2621Sllai1 "devname_profile_update: mount point not specified"); 957*2621Sllai1 nvlist_free(nvl); 958*2621Sllai1 return (EINVAL); 959*2621Sllai1 } 960*2621Sllai1 961*2621Sllai1 /* find the matching filesystem instance */ 962*2621Sllai1 rv = nvpair_value_string(nvp, &mntpt); 963*2621Sllai1 if (rv != 0) { 964*2621Sllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 965*2621Sllai1 rv, nvpair_name(nvp)); 966*2621Sllai1 } else { 967*2621Sllai1 mntinfo = sdev_find_mntinfo(mntpt); 968*2621Sllai1 if (mntinfo == NULL) { 969*2621Sllai1 cmn_err(CE_NOTE, "devname_profile_update: " 970*2621Sllai1 " mount point %s not found", mntpt); 971*2621Sllai1 nvlist_free(nvl); 972*2621Sllai1 return (EINVAL); 973*2621Sllai1 } 974*2621Sllai1 975*2621Sllai1 /* now do the hardwork to process the profile */ 976*2621Sllai1 sdev_process_profile(mntinfo, nvl); 977*2621Sllai1 978*2621Sllai1 sdev_mntinfo_rele(mntinfo); 979*2621Sllai1 } 980*2621Sllai1 981*2621Sllai1 nvlist_free(nvl); 982*2621Sllai1 return (0); 983*2621Sllai1 } 984