1facf4a8dSllai1 /* 2facf4a8dSllai1 * CDDL HEADER START 3facf4a8dSllai1 * 4facf4a8dSllai1 * The contents of this file are subject to the terms of the 5facf4a8dSllai1 * Common Development and Distribution License (the "License"). 6facf4a8dSllai1 * You may not use this file except in compliance with the License. 7facf4a8dSllai1 * 8facf4a8dSllai1 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9facf4a8dSllai1 * or http://www.opensolaris.org/os/licensing. 10facf4a8dSllai1 * See the License for the specific language governing permissions 11facf4a8dSllai1 * and limitations under the License. 12facf4a8dSllai1 * 13facf4a8dSllai1 * When distributing Covered Code, include this CDDL HEADER in each 14facf4a8dSllai1 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15facf4a8dSllai1 * If applicable, add the following below this CDDL HEADER, with the 16facf4a8dSllai1 * fields enclosed by brackets "[]" replaced with your own identifying 17facf4a8dSllai1 * information: Portions Copyright [yyyy] [name of copyright owner] 18facf4a8dSllai1 * 19facf4a8dSllai1 * CDDL HEADER END 20facf4a8dSllai1 */ 21facf4a8dSllai1 22facf4a8dSllai1 /* 230fbb751dSJohn Levon * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. 24facf4a8dSllai1 */ 25facf4a8dSllai1 26facf4a8dSllai1 /* 27facf4a8dSllai1 * This file implements /dev filesystem operations for non-global 28facf4a8dSllai1 * instances. Three major entry points: 29facf4a8dSllai1 * devname_profile_update() 30facf4a8dSllai1 * Update matching rules determining which names to export 31facf4a8dSllai1 * prof_readdir() 32facf4a8dSllai1 * Return the list of exported names 33facf4a8dSllai1 * prof_lookup() 34facf4a8dSllai1 * Implements lookup 35facf4a8dSllai1 */ 36facf4a8dSllai1 37facf4a8dSllai1 #include <sys/types.h> 38facf4a8dSllai1 #include <sys/param.h> 39facf4a8dSllai1 #include <sys/sysmacros.h> 40facf4a8dSllai1 #include <sys/vnode.h> 41facf4a8dSllai1 #include <sys/uio.h> 42facf4a8dSllai1 #include <sys/dirent.h> 43facf4a8dSllai1 #include <sys/pathname.h> 44facf4a8dSllai1 #include <sys/fs/dv_node.h> 45facf4a8dSllai1 #include <sys/fs/sdev_impl.h> 46facf4a8dSllai1 #include <sys/sunndi.h> 47facf4a8dSllai1 #include <sys/modctl.h> 48facf4a8dSllai1 49facf4a8dSllai1 enum { 50facf4a8dSllai1 PROFILE_TYPE_INCLUDE, 51facf4a8dSllai1 PROFILE_TYPE_EXCLUDE, 52facf4a8dSllai1 PROFILE_TYPE_MAP, 53facf4a8dSllai1 PROFILE_TYPE_SYMLINK 54facf4a8dSllai1 }; 55facf4a8dSllai1 56facf4a8dSllai1 enum { 57facf4a8dSllai1 WALK_DIR_CONTINUE = 0, 58facf4a8dSllai1 WALK_DIR_TERMINATE 59facf4a8dSllai1 }; 60facf4a8dSllai1 61facf4a8dSllai1 static const char *sdev_nvp_val_err = "nvpair_value error %d, %s\n"; 62facf4a8dSllai1 63facf4a8dSllai1 static void process_rule(struct sdev_node *, struct sdev_node *, 64facf4a8dSllai1 char *, char *, int); 65facf4a8dSllai1 static void walk_dir(struct vnode *, void *, int (*)(char *, void *)); 66facf4a8dSllai1 67facf4a8dSllai1 static void 68facf4a8dSllai1 prof_getattr(struct sdev_node *dir, char *name, struct vnode *gdv, 69facf4a8dSllai1 struct vattr *vap, struct vnode **avpp, int *no_fs_perm) 70facf4a8dSllai1 { 71facf4a8dSllai1 struct vnode *advp; 72facf4a8dSllai1 73facf4a8dSllai1 /* get attribute from shadow, if present; else get default */ 74facf4a8dSllai1 advp = dir->sdev_attrvp; 75da6c28aaSamw if (advp && VOP_LOOKUP(advp, name, avpp, NULL, 0, NULL, kcred, 76da6c28aaSamw NULL, NULL, NULL) == 0) { 77da6c28aaSamw (void) VOP_GETATTR(*avpp, vap, 0, kcred, NULL); 78facf4a8dSllai1 } else if (gdv == NULL || gdv->v_type == VDIR) { 79facf4a8dSllai1 /* always create shadow directory */ 80facf4a8dSllai1 *vap = sdev_vattr_dir; 81da6c28aaSamw if (advp && VOP_MKDIR(advp, name, &sdev_vattr_dir, 82da6c28aaSamw avpp, kcred, NULL, 0, NULL) != 0) { 83facf4a8dSllai1 *avpp = NULLVP; 84facf4a8dSllai1 sdcmn_err10(("prof_getattr: failed to create " 85facf4a8dSllai1 "shadow directory %s/%s\n", dir->sdev_path, name)); 86facf4a8dSllai1 } 87facf4a8dSllai1 } else { 88facf4a8dSllai1 /* 89facf4a8dSllai1 * get default permission from devfs 90facf4a8dSllai1 * Before calling devfs_get_defattr, we need to get 91facf4a8dSllai1 * the realvp (the dv_node). If realvp is not a dv_node, 92facf4a8dSllai1 * devfs_get_defattr() will return a system-wide default 93facf4a8dSllai1 * attr for device nodes. 94facf4a8dSllai1 */ 95facf4a8dSllai1 struct vnode *rvp; 96da6c28aaSamw if (VOP_REALVP(gdv, &rvp, NULL) != 0) 97facf4a8dSllai1 rvp = gdv; 98facf4a8dSllai1 devfs_get_defattr(rvp, vap, no_fs_perm); 99facf4a8dSllai1 *avpp = NULLVP; 100facf4a8dSllai1 } 101facf4a8dSllai1 102facf4a8dSllai1 /* ignore dev_t and vtype from backing store */ 103facf4a8dSllai1 if (gdv) { 104facf4a8dSllai1 vap->va_type = gdv->v_type; 105facf4a8dSllai1 vap->va_rdev = gdv->v_rdev; 106facf4a8dSllai1 } 107facf4a8dSllai1 } 108facf4a8dSllai1 109facf4a8dSllai1 static void 110facf4a8dSllai1 apply_glob_pattern(struct sdev_node *pdir, struct sdev_node *cdir) 111facf4a8dSllai1 { 112facf4a8dSllai1 char *name; 113facf4a8dSllai1 nvpair_t *nvp = NULL; 114facf4a8dSllai1 nvlist_t *nvl; 115facf4a8dSllai1 struct vnode *vp = SDEVTOV(cdir); 116facf4a8dSllai1 int rv = 0; 117facf4a8dSllai1 118facf4a8dSllai1 if (vp->v_type != VDIR) 119facf4a8dSllai1 return; 120facf4a8dSllai1 name = cdir->sdev_name; 121facf4a8dSllai1 nvl = pdir->sdev_prof.dev_glob_incdir; 122facf4a8dSllai1 while (nvp = nvlist_next_nvpair(nvl, nvp)) { 123facf4a8dSllai1 char *pathleft; 124facf4a8dSllai1 char *expr = nvpair_name(nvp); 125facf4a8dSllai1 if (!gmatch(name, expr)) 126facf4a8dSllai1 continue; 127facf4a8dSllai1 rv = nvpair_value_string(nvp, &pathleft); 128facf4a8dSllai1 if (rv != 0) { 129facf4a8dSllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 130facf4a8dSllai1 rv, nvpair_name(nvp)); 131facf4a8dSllai1 break; 132facf4a8dSllai1 } 133facf4a8dSllai1 process_rule(cdir, cdir->sdev_origin, 134facf4a8dSllai1 pathleft, NULL, PROFILE_TYPE_INCLUDE); 135facf4a8dSllai1 } 136facf4a8dSllai1 } 137facf4a8dSllai1 138facf4a8dSllai1 /* 139facf4a8dSllai1 * Some commonality here with sdev_mknode(), could be simplified. 140facf4a8dSllai1 * NOTE: prof_mknode returns with *newdv held once, if success. 141facf4a8dSllai1 */ 142facf4a8dSllai1 static int 143facf4a8dSllai1 prof_mknode(struct sdev_node *dir, char *name, struct sdev_node **newdv, 144facf4a8dSllai1 vattr_t *vap, vnode_t *avp, void *arg, cred_t *cred) 145facf4a8dSllai1 { 146facf4a8dSllai1 struct sdev_node *dv; 147facf4a8dSllai1 int rv; 148facf4a8dSllai1 149facf4a8dSllai1 ASSERT(RW_WRITE_HELD(&dir->sdev_contents)); 150facf4a8dSllai1 151facf4a8dSllai1 /* check cache first */ 152facf4a8dSllai1 if (dv = sdev_cache_lookup(dir, name)) { 153facf4a8dSllai1 *newdv = dv; 154facf4a8dSllai1 return (0); 155facf4a8dSllai1 } 156facf4a8dSllai1 157facf4a8dSllai1 /* allocate node and insert into cache */ 158facf4a8dSllai1 rv = sdev_nodeinit(dir, name, &dv, NULL); 159facf4a8dSllai1 if (rv != 0) { 160facf4a8dSllai1 *newdv = NULL; 161facf4a8dSllai1 return (rv); 162facf4a8dSllai1 } 163facf4a8dSllai1 1649e5aa9d8SRobert Mustacchi sdev_cache_update(dir, &dv, name, SDEV_CACHE_ADD); 165facf4a8dSllai1 *newdv = dv; 166facf4a8dSllai1 167facf4a8dSllai1 /* put it in ready state */ 168facf4a8dSllai1 rv = sdev_nodeready(*newdv, vap, avp, arg, cred); 169facf4a8dSllai1 170facf4a8dSllai1 /* handle glob pattern in the middle of a path */ 171facf4a8dSllai1 if (rv == 0) { 172facf4a8dSllai1 if (SDEVTOV(*newdv)->v_type == VDIR) 173facf4a8dSllai1 sdcmn_err10(("sdev_origin for %s set to 0x%p\n", 174facf4a8dSllai1 name, arg)); 175facf4a8dSllai1 apply_glob_pattern(dir, *newdv); 1769e5aa9d8SRobert Mustacchi } else { 1779e5aa9d8SRobert Mustacchi sdev_cache_update(dir, &dv, name, SDEV_CACHE_DELETE); 1789e5aa9d8SRobert Mustacchi SDEV_RELE(dv); 179facf4a8dSllai1 } 180facf4a8dSllai1 return (rv); 181facf4a8dSllai1 } 182facf4a8dSllai1 183facf4a8dSllai1 /* 184facf4a8dSllai1 * Create a directory node in a non-global dev instance. 185facf4a8dSllai1 * Always create shadow vnode. Set sdev_origin to the corresponding 186facf4a8dSllai1 * global directory sdev_node if it exists. This facilitates the 187facf4a8dSllai1 * lookup operation. 188facf4a8dSllai1 */ 189facf4a8dSllai1 static int 190facf4a8dSllai1 prof_make_dir(char *name, struct sdev_node **gdirp, struct sdev_node **dirp) 191facf4a8dSllai1 { 192facf4a8dSllai1 struct sdev_node *dir = *dirp; 193facf4a8dSllai1 struct sdev_node *gdir = *gdirp; 194facf4a8dSllai1 struct sdev_node *newdv; 195facf4a8dSllai1 struct vnode *avp, *gnewdir = NULL; 196facf4a8dSllai1 struct vattr vattr; 197facf4a8dSllai1 int error; 198facf4a8dSllai1 199facf4a8dSllai1 /* see if name already exists */ 200facf4a8dSllai1 rw_enter(&dir->sdev_contents, RW_READER); 201facf4a8dSllai1 if (newdv = sdev_cache_lookup(dir, name)) { 202facf4a8dSllai1 *dirp = newdv; 203facf4a8dSllai1 *gdirp = newdv->sdev_origin; 204facf4a8dSllai1 rw_exit(&dir->sdev_contents); 2059e5aa9d8SRobert Mustacchi SDEV_RELE(dir); 206facf4a8dSllai1 return (0); 207facf4a8dSllai1 } 208facf4a8dSllai1 rw_exit(&dir->sdev_contents); 209facf4a8dSllai1 210facf4a8dSllai1 /* find corresponding dir node in global dev */ 211facf4a8dSllai1 if (gdir) { 212facf4a8dSllai1 error = VOP_LOOKUP(SDEVTOV(gdir), name, &gnewdir, 213da6c28aaSamw NULL, 0, NULL, kcred, NULL, NULL, NULL); 214facf4a8dSllai1 if (error == 0) { 215facf4a8dSllai1 *gdirp = VTOSDEV(gnewdir); 216facf4a8dSllai1 } else { /* it's ok if there no global dir */ 217facf4a8dSllai1 *gdirp = NULL; 218facf4a8dSllai1 } 219facf4a8dSllai1 } 220facf4a8dSllai1 221facf4a8dSllai1 /* get attribute from shadow, also create shadow dir */ 222facf4a8dSllai1 prof_getattr(dir, name, gnewdir, &vattr, &avp, NULL); 223facf4a8dSllai1 224facf4a8dSllai1 /* create dev directory vnode */ 225facf4a8dSllai1 rw_enter(&dir->sdev_contents, RW_WRITER); 226facf4a8dSllai1 error = prof_mknode(dir, name, &newdv, &vattr, avp, (void *)*gdirp, 227facf4a8dSllai1 kcred); 228facf4a8dSllai1 rw_exit(&dir->sdev_contents); 229facf4a8dSllai1 if (error == 0) { 230facf4a8dSllai1 ASSERT(newdv); 231facf4a8dSllai1 *dirp = newdv; 232facf4a8dSllai1 } 233facf4a8dSllai1 SDEV_RELE(dir); 234facf4a8dSllai1 return (error); 235facf4a8dSllai1 } 236facf4a8dSllai1 237facf4a8dSllai1 /* 238facf4a8dSllai1 * Look up a logical name in the global zone. 239facf4a8dSllai1 * Provides the ability to map the global zone's device name 240facf4a8dSllai1 * to an alternate name within a zone. The primary example 241facf4a8dSllai1 * is the virtual console device /dev/zcons/[zonename]/zconsole 242facf4a8dSllai1 * mapped to /[zonename]/root/dev/zconsole. 243facf4a8dSllai1 */ 244facf4a8dSllai1 static void 245facf4a8dSllai1 prof_lookup_globaldev(struct sdev_node *dir, struct sdev_node *gdir, 246facf4a8dSllai1 char *name, char *rename) 247facf4a8dSllai1 { 248facf4a8dSllai1 int error; 249facf4a8dSllai1 struct vnode *avp, *gdv, *gddv; 250facf4a8dSllai1 struct sdev_node *newdv; 251facf4a8dSllai1 struct vattr vattr = {0}; 252facf4a8dSllai1 struct pathname pn; 253facf4a8dSllai1 254facf4a8dSllai1 /* check if node already exists */ 255facf4a8dSllai1 newdv = sdev_cache_lookup(dir, rename); 256facf4a8dSllai1 if (newdv) { 257facf4a8dSllai1 ASSERT(newdv->sdev_state != SDEV_ZOMBIE); 258facf4a8dSllai1 SDEV_SIMPLE_RELE(newdv); 259facf4a8dSllai1 return; 260facf4a8dSllai1 } 261facf4a8dSllai1 262facf4a8dSllai1 /* sanity check arguments */ 263facf4a8dSllai1 if (!gdir || pn_get(name, UIO_SYSSPACE, &pn)) 264facf4a8dSllai1 return; 265facf4a8dSllai1 266facf4a8dSllai1 /* perform a relative lookup of the global /dev instance */ 267facf4a8dSllai1 gddv = SDEVTOV(gdir); 268facf4a8dSllai1 VN_HOLD(gddv); 269facf4a8dSllai1 error = lookuppnvp(&pn, NULL, FOLLOW, NULLVPP, &gdv, 270facf4a8dSllai1 rootdir, gddv, kcred); 271facf4a8dSllai1 pn_free(&pn); 272facf4a8dSllai1 if (error) { 273facf4a8dSllai1 sdcmn_err10(("prof_lookup_globaldev: %s not found\n", name)); 274facf4a8dSllai1 return; 275facf4a8dSllai1 } 276facf4a8dSllai1 ASSERT(gdv && gdv->v_type != VLNK); 277facf4a8dSllai1 278facf4a8dSllai1 /* 279facf4a8dSllai1 * Found the entry in global /dev, figure out attributes 280facf4a8dSllai1 * by looking at backing store. Call into devfs for default. 281a83b1f2cSllai1 * Note, mapped device is persisted under the new name 282facf4a8dSllai1 */ 283a83b1f2cSllai1 prof_getattr(dir, rename, gdv, &vattr, &avp, NULL); 284facf4a8dSllai1 285facf4a8dSllai1 if (gdv->v_type != VDIR) { 286facf4a8dSllai1 VN_RELE(gdv); 287facf4a8dSllai1 gdir = NULL; 288facf4a8dSllai1 } else 289facf4a8dSllai1 gdir = VTOSDEV(gdv); 290facf4a8dSllai1 291facf4a8dSllai1 if (prof_mknode(dir, rename, &newdv, &vattr, avp, 292facf4a8dSllai1 (void *)gdir, kcred) == 0) { 293facf4a8dSllai1 ASSERT(newdv->sdev_state != SDEV_ZOMBIE); 294facf4a8dSllai1 SDEV_SIMPLE_RELE(newdv); 295facf4a8dSllai1 } 296facf4a8dSllai1 } 297facf4a8dSllai1 298facf4a8dSllai1 static void 299facf4a8dSllai1 prof_make_sym(struct sdev_node *dir, char *lnm, char *tgt) 300facf4a8dSllai1 { 301facf4a8dSllai1 struct sdev_node *newdv; 302facf4a8dSllai1 303facf4a8dSllai1 if (prof_mknode(dir, lnm, &newdv, &sdev_vattr_lnk, NULL, 304facf4a8dSllai1 (void *)tgt, kcred) == 0) { 305facf4a8dSllai1 ASSERT(newdv->sdev_state != SDEV_ZOMBIE); 306facf4a8dSllai1 SDEV_SIMPLE_RELE(newdv); 307facf4a8dSllai1 } 308facf4a8dSllai1 } 309facf4a8dSllai1 310facf4a8dSllai1 /* 311facf4a8dSllai1 * Create symlinks in the current directory based on profile 312facf4a8dSllai1 */ 313facf4a8dSllai1 static void 314facf4a8dSllai1 prof_make_symlinks(struct sdev_node *dir) 315facf4a8dSllai1 { 316facf4a8dSllai1 char *tgt, *lnm; 317facf4a8dSllai1 nvpair_t *nvp = NULL; 318facf4a8dSllai1 nvlist_t *nvl = dir->sdev_prof.dev_symlink; 319facf4a8dSllai1 int rv; 320facf4a8dSllai1 321facf4a8dSllai1 ASSERT(RW_WRITE_HELD(&dir->sdev_contents)); 322facf4a8dSllai1 323facf4a8dSllai1 if (nvl == NULL) 324facf4a8dSllai1 return; 325facf4a8dSllai1 326facf4a8dSllai1 while (nvp = nvlist_next_nvpair(nvl, nvp)) { 327facf4a8dSllai1 lnm = nvpair_name(nvp); 328facf4a8dSllai1 rv = nvpair_value_string(nvp, &tgt); 329facf4a8dSllai1 if (rv != 0) { 330facf4a8dSllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 331facf4a8dSllai1 rv, nvpair_name(nvp)); 332facf4a8dSllai1 break; 333facf4a8dSllai1 } 334facf4a8dSllai1 prof_make_sym(dir, lnm, tgt); 335facf4a8dSllai1 } 336facf4a8dSllai1 } 337facf4a8dSllai1 338facf4a8dSllai1 static void 339facf4a8dSllai1 prof_make_maps(struct sdev_node *dir) 340facf4a8dSllai1 { 341facf4a8dSllai1 nvpair_t *nvp = NULL; 342facf4a8dSllai1 nvlist_t *nvl = dir->sdev_prof.dev_map; 343facf4a8dSllai1 int rv; 344facf4a8dSllai1 345facf4a8dSllai1 ASSERT(RW_WRITE_HELD(&dir->sdev_contents)); 346facf4a8dSllai1 347facf4a8dSllai1 if (nvl == NULL) 348facf4a8dSllai1 return; 349facf4a8dSllai1 350facf4a8dSllai1 while (nvp = nvlist_next_nvpair(nvl, nvp)) { 351facf4a8dSllai1 char *name; 352facf4a8dSllai1 char *rename = nvpair_name(nvp); 353facf4a8dSllai1 rv = nvpair_value_string(nvp, &name); 354facf4a8dSllai1 if (rv != 0) { 355facf4a8dSllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 356facf4a8dSllai1 rv, nvpair_name(nvp)); 357facf4a8dSllai1 break; 358facf4a8dSllai1 } 359facf4a8dSllai1 sdcmn_err10(("map %s -> %s\n", name, rename)); 360facf4a8dSllai1 (void) prof_lookup_globaldev(dir, sdev_origins->sdev_root, 361facf4a8dSllai1 name, rename); 362facf4a8dSllai1 } 363facf4a8dSllai1 } 364facf4a8dSllai1 365facf4a8dSllai1 struct match_arg { 366facf4a8dSllai1 char *expr; 367facf4a8dSllai1 int match; 368facf4a8dSllai1 }; 369facf4a8dSllai1 370facf4a8dSllai1 static int 371facf4a8dSllai1 match_name(char *name, void *arg) 372facf4a8dSllai1 { 373facf4a8dSllai1 struct match_arg *margp = (struct match_arg *)arg; 374facf4a8dSllai1 375facf4a8dSllai1 if (gmatch(name, margp->expr)) { 376facf4a8dSllai1 margp->match = 1; 377facf4a8dSllai1 return (WALK_DIR_TERMINATE); 378facf4a8dSllai1 } 379facf4a8dSllai1 return (WALK_DIR_CONTINUE); 380facf4a8dSllai1 } 381facf4a8dSllai1 382facf4a8dSllai1 static int 383facf4a8dSllai1 is_nonempty_dir(char *name, char *pathleft, struct sdev_node *dir) 384facf4a8dSllai1 { 385facf4a8dSllai1 struct match_arg marg; 386facf4a8dSllai1 struct pathname pn; 387facf4a8dSllai1 struct vnode *gvp; 388facf4a8dSllai1 struct sdev_node *gdir = dir->sdev_origin; 389facf4a8dSllai1 390da6c28aaSamw if (VOP_LOOKUP(SDEVTOV(gdir), name, &gvp, NULL, 0, NULL, kcred, 391da6c28aaSamw NULL, NULL, NULL) != 0) 392facf4a8dSllai1 return (0); 393facf4a8dSllai1 394facf4a8dSllai1 if (gvp->v_type != VDIR) { 395facf4a8dSllai1 VN_RELE(gvp); 396facf4a8dSllai1 return (0); 397facf4a8dSllai1 } 398facf4a8dSllai1 399facf4a8dSllai1 if (pn_get(pathleft, UIO_SYSSPACE, &pn) != 0) { 400facf4a8dSllai1 VN_RELE(gvp); 401facf4a8dSllai1 return (0); 402facf4a8dSllai1 } 403facf4a8dSllai1 404facf4a8dSllai1 marg.expr = kmem_alloc(MAXNAMELEN, KM_SLEEP); 405facf4a8dSllai1 (void) pn_getcomponent(&pn, marg.expr); 406facf4a8dSllai1 marg.match = 0; 407facf4a8dSllai1 408facf4a8dSllai1 walk_dir(gvp, &marg, match_name); 409facf4a8dSllai1 VN_RELE(gvp); 410facf4a8dSllai1 kmem_free(marg.expr, MAXNAMELEN); 411facf4a8dSllai1 pn_free(&pn); 412facf4a8dSllai1 413facf4a8dSllai1 return (marg.match); 414facf4a8dSllai1 } 415facf4a8dSllai1 416facf4a8dSllai1 417facf4a8dSllai1 /* Check if name passes matching rules */ 418facf4a8dSllai1 static int 419facf4a8dSllai1 prof_name_matched(char *name, struct sdev_node *dir) 420facf4a8dSllai1 { 421facf4a8dSllai1 int type, match = 0; 422facf4a8dSllai1 char *expr; 423facf4a8dSllai1 nvlist_t *nvl; 424facf4a8dSllai1 nvpair_t *nvp = NULL; 425facf4a8dSllai1 int rv; 426facf4a8dSllai1 427facf4a8dSllai1 /* check against nvlist for leaf include/exclude */ 428facf4a8dSllai1 nvl = dir->sdev_prof.dev_name; 429facf4a8dSllai1 while (nvp = nvlist_next_nvpair(nvl, nvp)) { 430facf4a8dSllai1 expr = nvpair_name(nvp); 431facf4a8dSllai1 rv = nvpair_value_int32(nvp, &type); 432facf4a8dSllai1 if (rv != 0) { 433facf4a8dSllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 434facf4a8dSllai1 rv, nvpair_name(nvp)); 435facf4a8dSllai1 break; 436facf4a8dSllai1 } 437facf4a8dSllai1 438facf4a8dSllai1 if (type == PROFILE_TYPE_EXCLUDE) { 439facf4a8dSllai1 if (gmatch(name, expr)) 440facf4a8dSllai1 return (0); /* excluded */ 441facf4a8dSllai1 } else if (!match) { 442facf4a8dSllai1 match = gmatch(name, expr); 443facf4a8dSllai1 } 444facf4a8dSllai1 } 445facf4a8dSllai1 if (match) { 446facf4a8dSllai1 sdcmn_err10(("prof_name_matched: %s\n", name)); 447facf4a8dSllai1 return (match); 448facf4a8dSllai1 } 449facf4a8dSllai1 450facf4a8dSllai1 /* check for match against directory globbing pattern */ 451facf4a8dSllai1 nvl = dir->sdev_prof.dev_glob_incdir; 452facf4a8dSllai1 while (nvp = nvlist_next_nvpair(nvl, nvp)) { 453facf4a8dSllai1 char *pathleft; 454facf4a8dSllai1 expr = nvpair_name(nvp); 455facf4a8dSllai1 if (gmatch(name, expr) == 0) 456facf4a8dSllai1 continue; 457facf4a8dSllai1 rv = nvpair_value_string(nvp, &pathleft); 458facf4a8dSllai1 if (rv != 0) { 459facf4a8dSllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 460facf4a8dSllai1 rv, nvpair_name(nvp)); 461facf4a8dSllai1 break; 462facf4a8dSllai1 } 463facf4a8dSllai1 if (is_nonempty_dir(name, pathleft, dir)) { 464facf4a8dSllai1 sdcmn_err10(("prof_name_matched: dir %s\n", name)); 465facf4a8dSllai1 return (1); 466facf4a8dSllai1 } 467facf4a8dSllai1 } 468facf4a8dSllai1 469facf4a8dSllai1 return (0); 470facf4a8dSllai1 } 471facf4a8dSllai1 472facf4a8dSllai1 static void 473facf4a8dSllai1 walk_dir(struct vnode *dvp, void *arg, int (*callback)(char *, void *)) 474facf4a8dSllai1 { 475facf4a8dSllai1 char *nm; 476facf4a8dSllai1 int eof, error; 477facf4a8dSllai1 struct iovec iov; 478facf4a8dSllai1 struct uio uio; 479facf4a8dSllai1 struct dirent64 *dp; 480facf4a8dSllai1 dirent64_t *dbuf; 481facf4a8dSllai1 size_t dbuflen, dlen; 482facf4a8dSllai1 483facf4a8dSllai1 ASSERT(dvp); 484facf4a8dSllai1 485facf4a8dSllai1 dlen = 4096; 486facf4a8dSllai1 dbuf = kmem_zalloc(dlen, KM_SLEEP); 487facf4a8dSllai1 488facf4a8dSllai1 uio.uio_iov = &iov; 489facf4a8dSllai1 uio.uio_iovcnt = 1; 490facf4a8dSllai1 uio.uio_segflg = UIO_SYSSPACE; 491facf4a8dSllai1 uio.uio_fmode = 0; 492facf4a8dSllai1 uio.uio_extflg = UIO_COPY_CACHED; 493facf4a8dSllai1 uio.uio_loffset = 0; 494facf4a8dSllai1 uio.uio_llimit = MAXOFFSET_T; 495facf4a8dSllai1 496facf4a8dSllai1 eof = 0; 497facf4a8dSllai1 error = 0; 498facf4a8dSllai1 while (!error && !eof) { 499facf4a8dSllai1 uio.uio_resid = dlen; 500facf4a8dSllai1 iov.iov_base = (char *)dbuf; 501facf4a8dSllai1 iov.iov_len = dlen; 502facf4a8dSllai1 (void) VOP_RWLOCK(dvp, V_WRITELOCK_FALSE, NULL); 503da6c28aaSamw error = VOP_READDIR(dvp, &uio, kcred, &eof, NULL, 0); 504facf4a8dSllai1 VOP_RWUNLOCK(dvp, V_WRITELOCK_FALSE, NULL); 505facf4a8dSllai1 506facf4a8dSllai1 dbuflen = dlen - uio.uio_resid; 507facf4a8dSllai1 if (error || dbuflen == 0) 508facf4a8dSllai1 break; 509facf4a8dSllai1 for (dp = dbuf; ((intptr_t)dp < 510facf4a8dSllai1 (intptr_t)dbuf + dbuflen); 511facf4a8dSllai1 dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) { 512facf4a8dSllai1 nm = dp->d_name; 513facf4a8dSllai1 514facf4a8dSllai1 if (strcmp(nm, ".") == 0 || 515facf4a8dSllai1 strcmp(nm, "..") == 0) 516facf4a8dSllai1 continue; 517facf4a8dSllai1 518facf4a8dSllai1 if (callback(nm, arg) == WALK_DIR_TERMINATE) 519facf4a8dSllai1 goto end; 520facf4a8dSllai1 } 521facf4a8dSllai1 } 522facf4a8dSllai1 523facf4a8dSllai1 end: 524facf4a8dSllai1 kmem_free(dbuf, dlen); 525facf4a8dSllai1 } 526facf4a8dSllai1 5270fbb751dSJohn Levon /* 5280fbb751dSJohn Levon * Last chance for a zone to see a node. If our parent dir is 5290fbb751dSJohn Levon * SDEV_ZONED, then we look up the "zone" property for the node. If the 5300fbb751dSJohn Levon * property is found and matches the current zone name, we allow it. 5310fbb751dSJohn Levon * Note that this isn't quite correct for the global zone peeking inside 5320fbb751dSJohn Levon * a zone's /dev - for that to work, we'd have to have a per-dev-mount 5330fbb751dSJohn Levon * zone ref squirreled away. 5340fbb751dSJohn Levon */ 535facf4a8dSllai1 static int 5360fbb751dSJohn Levon prof_zone_matched(char *name, struct sdev_node *dir) 5370fbb751dSJohn Levon { 5380fbb751dSJohn Levon vnode_t *gvn = SDEVTOV(dir->sdev_origin); 5390fbb751dSJohn Levon struct pathname pn; 5400fbb751dSJohn Levon vnode_t *vn = NULL; 5410fbb751dSJohn Levon char zonename[ZONENAME_MAX]; 5420fbb751dSJohn Levon int znlen = ZONENAME_MAX; 5430fbb751dSJohn Levon int ret; 5440fbb751dSJohn Levon 5450fbb751dSJohn Levon ASSERT((dir->sdev_flags & SDEV_ZONED) != 0); 5460fbb751dSJohn Levon 5470fbb751dSJohn Levon sdcmn_err10(("sdev_node %p is zoned, looking for %s\n", 5480fbb751dSJohn Levon (void *)dir, name)); 5490fbb751dSJohn Levon 5500fbb751dSJohn Levon if (pn_get(name, UIO_SYSSPACE, &pn)) 5510fbb751dSJohn Levon return (0); 5520fbb751dSJohn Levon 5530fbb751dSJohn Levon VN_HOLD(gvn); 5540fbb751dSJohn Levon 5550fbb751dSJohn Levon ret = lookuppnvp(&pn, NULL, FOLLOW, NULLVPP, &vn, rootdir, gvn, kcred); 5560fbb751dSJohn Levon 5570fbb751dSJohn Levon pn_free(&pn); 5580fbb751dSJohn Levon 5590fbb751dSJohn Levon if (ret != 0) { 5600fbb751dSJohn Levon sdcmn_err10(("prof_zone_matched: %s not found\n", name)); 5610fbb751dSJohn Levon return (0); 5620fbb751dSJohn Levon } 5630fbb751dSJohn Levon 5640fbb751dSJohn Levon /* 5650fbb751dSJohn Levon * VBLK doesn't matter, and the property name is in fact treated 5660fbb751dSJohn Levon * as a const char *. 5670fbb751dSJohn Levon */ 5680fbb751dSJohn Levon ret = e_ddi_getlongprop_buf(vn->v_rdev, VBLK, (char *)"zone", 5690fbb751dSJohn Levon DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, (caddr_t)zonename, &znlen); 5700fbb751dSJohn Levon 5710fbb751dSJohn Levon VN_RELE(vn); 5720fbb751dSJohn Levon 5730fbb751dSJohn Levon if (ret == DDI_PROP_NOT_FOUND) { 5740fbb751dSJohn Levon sdcmn_err10(("vnode %p: no zone prop\n", (void *)vn)); 5750fbb751dSJohn Levon return (0); 5760fbb751dSJohn Levon } else if (ret != DDI_PROP_SUCCESS) { 5770fbb751dSJohn Levon sdcmn_err10(("vnode %p: zone prop error: %d\n", 5780fbb751dSJohn Levon (void *)vn, ret)); 5790fbb751dSJohn Levon return (0); 5800fbb751dSJohn Levon } 5810fbb751dSJohn Levon 5820fbb751dSJohn Levon sdcmn_err10(("vnode %p zone prop: %s\n", (void *)vn, zonename)); 5830fbb751dSJohn Levon return (strcmp(zonename, curproc->p_zone->zone_name) == 0); 5840fbb751dSJohn Levon } 5850fbb751dSJohn Levon 5860fbb751dSJohn Levon static int 5870fbb751dSJohn Levon prof_make_name_glob(char *nm, void *arg) 588facf4a8dSllai1 { 589facf4a8dSllai1 struct sdev_node *ddv = (struct sdev_node *)arg; 590facf4a8dSllai1 591facf4a8dSllai1 if (prof_name_matched(nm, ddv)) 592facf4a8dSllai1 prof_lookup_globaldev(ddv, ddv->sdev_origin, nm, nm); 5930fbb751dSJohn Levon 5940fbb751dSJohn Levon return (WALK_DIR_CONTINUE); 5950fbb751dSJohn Levon } 5960fbb751dSJohn Levon 5970fbb751dSJohn Levon static int 5980fbb751dSJohn Levon prof_make_name_zone(char *nm, void *arg) 5990fbb751dSJohn Levon { 6000fbb751dSJohn Levon struct sdev_node *ddv = (struct sdev_node *)arg; 6010fbb751dSJohn Levon 6020fbb751dSJohn Levon if (prof_zone_matched(nm, ddv)) 6030fbb751dSJohn Levon prof_lookup_globaldev(ddv, ddv->sdev_origin, nm, nm); 6040fbb751dSJohn Levon 605facf4a8dSllai1 return (WALK_DIR_CONTINUE); 606facf4a8dSllai1 } 607facf4a8dSllai1 608facf4a8dSllai1 static void 6090fbb751dSJohn Levon prof_make_names_walk(struct sdev_node *ddv, int (*cb)(char *, void *)) 610facf4a8dSllai1 { 611facf4a8dSllai1 struct sdev_node *gdir; 612facf4a8dSllai1 613facf4a8dSllai1 gdir = ddv->sdev_origin; 614facf4a8dSllai1 if (gdir == NULL) 615facf4a8dSllai1 return; 6160fbb751dSJohn Levon walk_dir(SDEVTOV(gdir), (void *)ddv, cb); 617facf4a8dSllai1 } 618facf4a8dSllai1 619facf4a8dSllai1 static void 620facf4a8dSllai1 prof_make_names(struct sdev_node *dir) 621facf4a8dSllai1 { 622facf4a8dSllai1 char *name; 623facf4a8dSllai1 nvpair_t *nvp = NULL; 624facf4a8dSllai1 nvlist_t *nvl = dir->sdev_prof.dev_name; 625facf4a8dSllai1 int rv; 626facf4a8dSllai1 627facf4a8dSllai1 ASSERT(RW_WRITE_HELD(&dir->sdev_contents)); 628facf4a8dSllai1 6290fbb751dSJohn Levon if ((dir->sdev_flags & SDEV_ZONED) != 0) 6300fbb751dSJohn Levon prof_make_names_walk(dir, prof_make_name_zone); 6310fbb751dSJohn Levon 632facf4a8dSllai1 if (nvl == NULL) 633facf4a8dSllai1 return; 634facf4a8dSllai1 635facf4a8dSllai1 if (dir->sdev_prof.has_glob) { 6360fbb751dSJohn Levon prof_make_names_walk(dir, prof_make_name_glob); 637facf4a8dSllai1 return; 638facf4a8dSllai1 } 639facf4a8dSllai1 640facf4a8dSllai1 /* Walk nvlist and lookup corresponding device in global inst */ 641facf4a8dSllai1 while (nvp = nvlist_next_nvpair(nvl, nvp)) { 642facf4a8dSllai1 int type; 643facf4a8dSllai1 rv = nvpair_value_int32(nvp, &type); 644facf4a8dSllai1 if (rv != 0) { 645facf4a8dSllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 646facf4a8dSllai1 rv, nvpair_name(nvp)); 647facf4a8dSllai1 break; 648facf4a8dSllai1 } 649facf4a8dSllai1 if (type == PROFILE_TYPE_EXCLUDE) 650facf4a8dSllai1 continue; 651facf4a8dSllai1 name = nvpair_name(nvp); 652facf4a8dSllai1 (void) prof_lookup_globaldev(dir, dir->sdev_origin, 653facf4a8dSllai1 name, name); 654facf4a8dSllai1 } 655facf4a8dSllai1 } 656facf4a8dSllai1 657facf4a8dSllai1 /* 658*22253b45SAlexander Kolbasov * Return True if directory cache is out of date and should be updated. 659*22253b45SAlexander Kolbasov */ 660*22253b45SAlexander Kolbasov static boolean_t 661*22253b45SAlexander Kolbasov prof_dev_needupdate(sdev_node_t *ddv) 662*22253b45SAlexander Kolbasov { 663*22253b45SAlexander Kolbasov sdev_node_t *gdir = ddv->sdev_origin; 664*22253b45SAlexander Kolbasov 665*22253b45SAlexander Kolbasov /* 666*22253b45SAlexander Kolbasov * Caller can have either reader or writer lock 667*22253b45SAlexander Kolbasov */ 668*22253b45SAlexander Kolbasov ASSERT(RW_LOCK_HELD(&ddv->sdev_contents)); 669*22253b45SAlexander Kolbasov 670*22253b45SAlexander Kolbasov /* 671*22253b45SAlexander Kolbasov * We need to rebuild the directory content if 672*22253b45SAlexander Kolbasov * - ddv is not in a SDEV_ZOMBIE state 673*22253b45SAlexander Kolbasov * - SDEV_BUILD is set OR 674*22253b45SAlexander Kolbasov * - The device tree generation number has changed OR 675*22253b45SAlexander Kolbasov * - The corresponding /dev namespace has been updated 676*22253b45SAlexander Kolbasov */ 677*22253b45SAlexander Kolbasov return ((ddv->sdev_state != SDEV_ZOMBIE) && 678*22253b45SAlexander Kolbasov (((ddv->sdev_flags & SDEV_BUILD) != 0) || 679*22253b45SAlexander Kolbasov (ddv->sdev_devtree_gen != devtree_gen) || 680*22253b45SAlexander Kolbasov ((gdir != NULL) && 681*22253b45SAlexander Kolbasov (ddv->sdev_ldir_gen != gdir->sdev_gdir_gen)))); 682*22253b45SAlexander Kolbasov } 683*22253b45SAlexander Kolbasov 684*22253b45SAlexander Kolbasov /* 685facf4a8dSllai1 * Build directory vnodes based on the profile and the global 686facf4a8dSllai1 * dev instance. 687facf4a8dSllai1 */ 688facf4a8dSllai1 void 689*22253b45SAlexander Kolbasov prof_filldir(sdev_node_t *ddv) 690facf4a8dSllai1 { 691*22253b45SAlexander Kolbasov sdev_node_t *gdir; 692facf4a8dSllai1 693facf4a8dSllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 694facf4a8dSllai1 695*22253b45SAlexander Kolbasov if (!prof_dev_needupdate(ddv)) { 696*22253b45SAlexander Kolbasov ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 6979e5aa9d8SRobert Mustacchi return; 698facf4a8dSllai1 } 699*22253b45SAlexander Kolbasov /* 700*22253b45SAlexander Kolbasov * Upgrade to writer lock 701*22253b45SAlexander Kolbasov */ 702*22253b45SAlexander Kolbasov if (rw_tryupgrade(&ddv->sdev_contents) == 0) { 703*22253b45SAlexander Kolbasov /* 704*22253b45SAlexander Kolbasov * We need to drop the read lock and re-acquire it as a 705*22253b45SAlexander Kolbasov * write lock. While we do this the condition may change so we 706*22253b45SAlexander Kolbasov * need to re-check condition 707*22253b45SAlexander Kolbasov */ 708*22253b45SAlexander Kolbasov rw_exit(&ddv->sdev_contents); 709*22253b45SAlexander Kolbasov rw_enter(&ddv->sdev_contents, RW_WRITER); 710*22253b45SAlexander Kolbasov if (!prof_dev_needupdate(ddv)) { 711*22253b45SAlexander Kolbasov /* Downgrade back to the read lock before returning */ 712*22253b45SAlexander Kolbasov rw_downgrade(&ddv->sdev_contents); 713*22253b45SAlexander Kolbasov return; 714*22253b45SAlexander Kolbasov } 715*22253b45SAlexander Kolbasov } 716*22253b45SAlexander Kolbasov /* At this point we should have a write lock */ 717*22253b45SAlexander Kolbasov ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 718*22253b45SAlexander Kolbasov 719facf4a8dSllai1 sdcmn_err10(("devtree_gen (%s): %ld -> %ld\n", 720facf4a8dSllai1 ddv->sdev_path, ddv->sdev_devtree_gen, devtree_gen)); 721*22253b45SAlexander Kolbasov 722*22253b45SAlexander Kolbasov gdir = ddv->sdev_origin; 723*22253b45SAlexander Kolbasov 724*22253b45SAlexander Kolbasov if (gdir != NULL) 725facf4a8dSllai1 sdcmn_err10(("sdev_dir_gen (%s): %ld -> %ld\n", 726facf4a8dSllai1 ddv->sdev_path, ddv->sdev_ldir_gen, 727facf4a8dSllai1 gdir->sdev_gdir_gen)); 728facf4a8dSllai1 729facf4a8dSllai1 /* update flags and generation number so next filldir is quick */ 730*22253b45SAlexander Kolbasov if ((ddv->sdev_flags & SDEV_BUILD) == SDEV_BUILD) { 731facf4a8dSllai1 ddv->sdev_flags &= ~SDEV_BUILD; 732*22253b45SAlexander Kolbasov } 733facf4a8dSllai1 ddv->sdev_devtree_gen = devtree_gen; 734*22253b45SAlexander Kolbasov if (gdir != NULL) 735facf4a8dSllai1 ddv->sdev_ldir_gen = gdir->sdev_gdir_gen; 736facf4a8dSllai1 737facf4a8dSllai1 prof_make_symlinks(ddv); 738facf4a8dSllai1 prof_make_maps(ddv); 739facf4a8dSllai1 prof_make_names(ddv); 740facf4a8dSllai1 rw_downgrade(&ddv->sdev_contents); 741facf4a8dSllai1 } 742facf4a8dSllai1 743facf4a8dSllai1 /* apply include/exclude pattern to existing directory content */ 744facf4a8dSllai1 static void 745facf4a8dSllai1 apply_dir_pattern(struct sdev_node *dir, char *expr, char *pathleft, int type) 746facf4a8dSllai1 { 747facf4a8dSllai1 struct sdev_node *dv; 748facf4a8dSllai1 749facf4a8dSllai1 /* leaf pattern */ 750facf4a8dSllai1 if (pathleft == NULL) { 751facf4a8dSllai1 if (type == PROFILE_TYPE_INCLUDE) 752facf4a8dSllai1 return; /* nothing to do for include */ 753facf4a8dSllai1 (void) sdev_cleandir(dir, expr, SDEV_ENFORCE); 754facf4a8dSllai1 return; 755facf4a8dSllai1 } 756facf4a8dSllai1 757facf4a8dSllai1 /* directory pattern */ 758facf4a8dSllai1 rw_enter(&dir->sdev_contents, RW_WRITER); 759aac43a5fSjg 760aac43a5fSjg for (dv = SDEV_FIRST_ENTRY(dir); dv; dv = SDEV_NEXT_ENTRY(dir, dv)) { 761facf4a8dSllai1 if (gmatch(dv->sdev_name, expr) == 0 || 762facf4a8dSllai1 SDEVTOV(dv)->v_type != VDIR) 763facf4a8dSllai1 continue; 764facf4a8dSllai1 process_rule(dv, dv->sdev_origin, 765facf4a8dSllai1 pathleft, NULL, type); 766facf4a8dSllai1 } 767facf4a8dSllai1 rw_exit(&dir->sdev_contents); 768facf4a8dSllai1 } 769facf4a8dSllai1 770facf4a8dSllai1 /* 771facf4a8dSllai1 * Add a profile rule. 772facf4a8dSllai1 * tgt represents a device name matching expression, 773facf4a8dSllai1 * matching device names are to be either included or excluded. 774facf4a8dSllai1 */ 775facf4a8dSllai1 static void 776facf4a8dSllai1 prof_add_rule(char *name, char *tgt, struct sdev_node *dir, int type) 777facf4a8dSllai1 { 778facf4a8dSllai1 int error; 779facf4a8dSllai1 nvlist_t **nvlp = NULL; 780facf4a8dSllai1 int rv; 781facf4a8dSllai1 782facf4a8dSllai1 ASSERT(SDEVTOV(dir)->v_type == VDIR); 783facf4a8dSllai1 784facf4a8dSllai1 rw_enter(&dir->sdev_contents, RW_WRITER); 785facf4a8dSllai1 786facf4a8dSllai1 switch (type) { 787facf4a8dSllai1 case PROFILE_TYPE_INCLUDE: 788facf4a8dSllai1 if (tgt) 789facf4a8dSllai1 nvlp = &(dir->sdev_prof.dev_glob_incdir); 790facf4a8dSllai1 else 791facf4a8dSllai1 nvlp = &(dir->sdev_prof.dev_name); 792facf4a8dSllai1 break; 793facf4a8dSllai1 case PROFILE_TYPE_EXCLUDE: 794facf4a8dSllai1 if (tgt) 795facf4a8dSllai1 nvlp = &(dir->sdev_prof.dev_glob_excdir); 796facf4a8dSllai1 else 797facf4a8dSllai1 nvlp = &(dir->sdev_prof.dev_name); 798facf4a8dSllai1 break; 799facf4a8dSllai1 case PROFILE_TYPE_MAP: 800facf4a8dSllai1 nvlp = &(dir->sdev_prof.dev_map); 801facf4a8dSllai1 break; 802facf4a8dSllai1 case PROFILE_TYPE_SYMLINK: 803facf4a8dSllai1 nvlp = &(dir->sdev_prof.dev_symlink); 804facf4a8dSllai1 break; 805facf4a8dSllai1 }; 806facf4a8dSllai1 807facf4a8dSllai1 /* initialize nvlist */ 808facf4a8dSllai1 if (*nvlp == NULL) { 809facf4a8dSllai1 error = nvlist_alloc(nvlp, NV_UNIQUE_NAME, KM_SLEEP); 810facf4a8dSllai1 ASSERT(error == 0); 811facf4a8dSllai1 } 812facf4a8dSllai1 813facf4a8dSllai1 if (tgt) { 814facf4a8dSllai1 rv = nvlist_add_string(*nvlp, name, tgt); 815facf4a8dSllai1 } else { 816facf4a8dSllai1 rv = nvlist_add_int32(*nvlp, name, type); 817facf4a8dSllai1 } 818facf4a8dSllai1 ASSERT(rv == 0); 819facf4a8dSllai1 /* rebuild directory content */ 820facf4a8dSllai1 dir->sdev_flags |= SDEV_BUILD; 821facf4a8dSllai1 822facf4a8dSllai1 if ((type == PROFILE_TYPE_INCLUDE) && 823facf4a8dSllai1 (strpbrk(name, "*?[]") != NULL)) { 824facf4a8dSllai1 dir->sdev_prof.has_glob = 1; 825facf4a8dSllai1 } 826facf4a8dSllai1 827facf4a8dSllai1 rw_exit(&dir->sdev_contents); 828facf4a8dSllai1 829facf4a8dSllai1 /* additional details for glob pattern and exclusion */ 830facf4a8dSllai1 switch (type) { 831facf4a8dSllai1 case PROFILE_TYPE_INCLUDE: 832facf4a8dSllai1 case PROFILE_TYPE_EXCLUDE: 833facf4a8dSllai1 apply_dir_pattern(dir, name, tgt, type); 834facf4a8dSllai1 break; 835facf4a8dSllai1 }; 836facf4a8dSllai1 } 837facf4a8dSllai1 838facf4a8dSllai1 /* 839facf4a8dSllai1 * Parse path components and apply requested matching rule at 840facf4a8dSllai1 * directory level. 841facf4a8dSllai1 */ 842facf4a8dSllai1 static void 843facf4a8dSllai1 process_rule(struct sdev_node *dir, struct sdev_node *gdir, 844facf4a8dSllai1 char *path, char *tgt, int type) 845facf4a8dSllai1 { 846facf4a8dSllai1 char *name; 847facf4a8dSllai1 struct pathname pn; 848facf4a8dSllai1 int rv = 0; 849facf4a8dSllai1 850facf4a8dSllai1 if ((strlen(path) > 5) && (strncmp(path, "/dev/", 5) == 0)) { 851facf4a8dSllai1 path += 5; 852facf4a8dSllai1 } 853facf4a8dSllai1 854facf4a8dSllai1 if (pn_get(path, UIO_SYSSPACE, &pn) != 0) 855facf4a8dSllai1 return; 856facf4a8dSllai1 857facf4a8dSllai1 name = kmem_alloc(MAXPATHLEN, KM_SLEEP); 858facf4a8dSllai1 (void) pn_getcomponent(&pn, name); 859facf4a8dSllai1 pn_skipslash(&pn); 860facf4a8dSllai1 SDEV_HOLD(dir); 861facf4a8dSllai1 862facf4a8dSllai1 while (pn_pathleft(&pn)) { 863facf4a8dSllai1 /* If this is pattern, just add the pattern */ 864facf4a8dSllai1 if (strpbrk(name, "*?[]") != NULL && 865facf4a8dSllai1 (type == PROFILE_TYPE_INCLUDE || 866facf4a8dSllai1 type == PROFILE_TYPE_EXCLUDE)) { 867facf4a8dSllai1 ASSERT(tgt == NULL); 868facf4a8dSllai1 tgt = pn.pn_path; 869facf4a8dSllai1 break; 870facf4a8dSllai1 } 871facf4a8dSllai1 if ((rv = prof_make_dir(name, &gdir, &dir)) != 0) { 872facf4a8dSllai1 cmn_err(CE_CONT, "process_rule: %s error %d\n", 873facf4a8dSllai1 path, rv); 874facf4a8dSllai1 break; 875facf4a8dSllai1 } 876facf4a8dSllai1 (void) pn_getcomponent(&pn, name); 877facf4a8dSllai1 pn_skipslash(&pn); 878facf4a8dSllai1 } 879facf4a8dSllai1 880facf4a8dSllai1 /* process the leaf component */ 881facf4a8dSllai1 if (rv == 0) { 882facf4a8dSllai1 prof_add_rule(name, tgt, dir, type); 883facf4a8dSllai1 SDEV_SIMPLE_RELE(dir); 884facf4a8dSllai1 } 885facf4a8dSllai1 886facf4a8dSllai1 kmem_free(name, MAXPATHLEN); 887facf4a8dSllai1 pn_free(&pn); 888facf4a8dSllai1 } 889facf4a8dSllai1 890facf4a8dSllai1 static int 891facf4a8dSllai1 copyin_nvlist(char *packed_usr, size_t packed_sz, nvlist_t **nvlp) 892facf4a8dSllai1 { 893facf4a8dSllai1 int err = 0; 894facf4a8dSllai1 char *packed; 895facf4a8dSllai1 nvlist_t *profile = NULL; 896facf4a8dSllai1 897facf4a8dSllai1 /* simple sanity check */ 898facf4a8dSllai1 if (packed_usr == NULL || packed_sz == 0) 899facf4a8dSllai1 return (NULL); 900facf4a8dSllai1 901facf4a8dSllai1 /* copyin packed profile nvlist */ 902facf4a8dSllai1 packed = kmem_alloc(packed_sz, KM_NOSLEEP); 903facf4a8dSllai1 if (packed == NULL) 904facf4a8dSllai1 return (ENOMEM); 905facf4a8dSllai1 err = copyin(packed_usr, packed, packed_sz); 906facf4a8dSllai1 907facf4a8dSllai1 /* unpack packed profile nvlist */ 908facf4a8dSllai1 if (err) 909facf4a8dSllai1 cmn_err(CE_WARN, "copyin_nvlist: copyin failed with " 910facf4a8dSllai1 "err %d\n", err); 911facf4a8dSllai1 else if (err = nvlist_unpack(packed, packed_sz, &profile, KM_NOSLEEP)) 912facf4a8dSllai1 cmn_err(CE_WARN, "copyin_nvlist: nvlist_unpack " 913facf4a8dSllai1 "failed with err %d\n", err); 914facf4a8dSllai1 915facf4a8dSllai1 kmem_free(packed, packed_sz); 916facf4a8dSllai1 if (err == 0) 917facf4a8dSllai1 *nvlp = profile; 918facf4a8dSllai1 return (err); 919facf4a8dSllai1 } 920facf4a8dSllai1 921facf4a8dSllai1 /* 922facf4a8dSllai1 * Process profile passed down from libdevinfo. There are four types 923facf4a8dSllai1 * of matching rules: 924facf4a8dSllai1 * include: export a name or names matching a pattern 925facf4a8dSllai1 * exclude: exclude a name or names matching a pattern 926facf4a8dSllai1 * symlink: create a local symlink 927facf4a8dSllai1 * map: export a device with a name different from the global zone 928facf4a8dSllai1 * Note: We may consider supporting VOP_SYMLINK in non-global instances, 929facf4a8dSllai1 * because it does not present any security risk. For now, the fs 930facf4a8dSllai1 * instance is read only. 931facf4a8dSllai1 */ 932facf4a8dSllai1 static void 933facf4a8dSllai1 sdev_process_profile(struct sdev_data *sdev_data, nvlist_t *profile) 934facf4a8dSllai1 { 935facf4a8dSllai1 nvpair_t *nvpair; 936facf4a8dSllai1 char *nvname, *dname; 937facf4a8dSllai1 struct sdev_node *dir, *gdir; 938facf4a8dSllai1 char **pair; /* for symlinks and maps */ 939facf4a8dSllai1 uint_t nelem; 940facf4a8dSllai1 int rv; 941facf4a8dSllai1 942facf4a8dSllai1 gdir = sdev_origins->sdev_root; /* root of global /dev */ 943facf4a8dSllai1 dir = sdev_data->sdev_root; /* root of current instance */ 944facf4a8dSllai1 945facf4a8dSllai1 ASSERT(profile); 946facf4a8dSllai1 947facf4a8dSllai1 /* process nvpairs in the list */ 948facf4a8dSllai1 nvpair = NULL; 949facf4a8dSllai1 while (nvpair = nvlist_next_nvpair(profile, nvpair)) { 950facf4a8dSllai1 nvname = nvpair_name(nvpair); 951facf4a8dSllai1 ASSERT(nvname != NULL); 952facf4a8dSllai1 953facf4a8dSllai1 if (strcmp(nvname, SDEV_NVNAME_INCLUDE) == 0) { 954facf4a8dSllai1 rv = nvpair_value_string(nvpair, &dname); 955facf4a8dSllai1 if (rv != 0) { 956facf4a8dSllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 957facf4a8dSllai1 rv, nvpair_name(nvpair)); 958facf4a8dSllai1 break; 959facf4a8dSllai1 } 960facf4a8dSllai1 process_rule(dir, gdir, dname, NULL, 961facf4a8dSllai1 PROFILE_TYPE_INCLUDE); 962facf4a8dSllai1 } else if (strcmp(nvname, SDEV_NVNAME_EXCLUDE) == 0) { 963facf4a8dSllai1 rv = nvpair_value_string(nvpair, &dname); 964facf4a8dSllai1 if (rv != 0) { 965facf4a8dSllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 966facf4a8dSllai1 rv, nvpair_name(nvpair)); 967facf4a8dSllai1 break; 968facf4a8dSllai1 } 969facf4a8dSllai1 process_rule(dir, gdir, dname, NULL, 970facf4a8dSllai1 PROFILE_TYPE_EXCLUDE); 971facf4a8dSllai1 } else if (strcmp(nvname, SDEV_NVNAME_SYMLINK) == 0) { 972facf4a8dSllai1 rv = nvpair_value_string_array(nvpair, &pair, &nelem); 973facf4a8dSllai1 if (rv != 0) { 974facf4a8dSllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 975facf4a8dSllai1 rv, nvpair_name(nvpair)); 976facf4a8dSllai1 break; 977facf4a8dSllai1 } 978facf4a8dSllai1 ASSERT(nelem == 2); 979facf4a8dSllai1 process_rule(dir, gdir, pair[0], pair[1], 980facf4a8dSllai1 PROFILE_TYPE_SYMLINK); 981facf4a8dSllai1 } else if (strcmp(nvname, SDEV_NVNAME_MAP) == 0) { 982facf4a8dSllai1 rv = nvpair_value_string_array(nvpair, &pair, &nelem); 983facf4a8dSllai1 if (rv != 0) { 984facf4a8dSllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 985facf4a8dSllai1 rv, nvpair_name(nvpair)); 986facf4a8dSllai1 break; 987facf4a8dSllai1 } 988facf4a8dSllai1 process_rule(dir, gdir, pair[1], pair[0], 989facf4a8dSllai1 PROFILE_TYPE_MAP); 990facf4a8dSllai1 } else if (strcmp(nvname, SDEV_NVNAME_MOUNTPT) != 0) { 991facf4a8dSllai1 cmn_err(CE_WARN, "sdev_process_profile: invalid " 992facf4a8dSllai1 "nvpair %s\n", nvname); 993facf4a8dSllai1 } 994facf4a8dSllai1 } 995facf4a8dSllai1 } 996facf4a8dSllai1 997facf4a8dSllai1 /*ARGSUSED*/ 998facf4a8dSllai1 int 999facf4a8dSllai1 prof_lookup(vnode_t *dvp, char *nm, struct vnode **vpp, struct cred *cred) 1000facf4a8dSllai1 { 1001facf4a8dSllai1 struct sdev_node *ddv = VTOSDEV(dvp); 1002facf4a8dSllai1 struct sdev_node *dv; 1003facf4a8dSllai1 int nmlen; 1004facf4a8dSllai1 1005facf4a8dSllai1 /* 1006facf4a8dSllai1 * Empty name or ., return node itself. 1007facf4a8dSllai1 */ 1008facf4a8dSllai1 nmlen = strlen(nm); 1009facf4a8dSllai1 if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) { 1010facf4a8dSllai1 *vpp = SDEVTOV(ddv); 1011facf4a8dSllai1 VN_HOLD(*vpp); 1012facf4a8dSllai1 return (0); 1013facf4a8dSllai1 } 1014facf4a8dSllai1 1015facf4a8dSllai1 /* 1016facf4a8dSllai1 * .., return the parent directory 1017facf4a8dSllai1 */ 1018facf4a8dSllai1 if ((nmlen == 2) && (strcmp(nm, "..") == 0)) { 1019facf4a8dSllai1 *vpp = SDEVTOV(ddv->sdev_dotdot); 1020facf4a8dSllai1 VN_HOLD(*vpp); 1021facf4a8dSllai1 return (0); 1022facf4a8dSllai1 } 1023facf4a8dSllai1 1024facf4a8dSllai1 rw_enter(&ddv->sdev_contents, RW_READER); 1025facf4a8dSllai1 dv = sdev_cache_lookup(ddv, nm); 1026facf4a8dSllai1 if (dv == NULL) { 1027facf4a8dSllai1 prof_filldir(ddv); 1028facf4a8dSllai1 dv = sdev_cache_lookup(ddv, nm); 1029facf4a8dSllai1 } 1030facf4a8dSllai1 rw_exit(&ddv->sdev_contents); 1031facf4a8dSllai1 if (dv == NULL) { 1032facf4a8dSllai1 sdcmn_err10(("prof_lookup: %s not found\n", nm)); 1033facf4a8dSllai1 return (ENOENT); 1034facf4a8dSllai1 } 1035facf4a8dSllai1 1036facf4a8dSllai1 return (sdev_to_vp(dv, vpp)); 1037facf4a8dSllai1 } 1038facf4a8dSllai1 1039facf4a8dSllai1 /* 1040facf4a8dSllai1 * This is invoked after a new filesystem is mounted to define the 1041facf4a8dSllai1 * name space. It is also invoked during normal system operation 1042facf4a8dSllai1 * to update the name space. 1043facf4a8dSllai1 * 1044facf4a8dSllai1 * Applications call di_prof_commit() in libdevinfo, which invokes 1045facf4a8dSllai1 * modctl(). modctl calls this function. The input is a packed nvlist. 1046facf4a8dSllai1 */ 1047facf4a8dSllai1 int 1048facf4a8dSllai1 devname_profile_update(char *packed, size_t packed_sz) 1049facf4a8dSllai1 { 1050facf4a8dSllai1 char *mntpt; 1051facf4a8dSllai1 nvlist_t *nvl; 1052facf4a8dSllai1 nvpair_t *nvp; 1053facf4a8dSllai1 struct sdev_data *mntinfo; 1054facf4a8dSllai1 int err; 1055facf4a8dSllai1 int rv; 1056facf4a8dSllai1 1057facf4a8dSllai1 nvl = NULL; 1058facf4a8dSllai1 if ((err = copyin_nvlist(packed, packed_sz, &nvl)) != 0) 1059facf4a8dSllai1 return (err); 1060facf4a8dSllai1 ASSERT(nvl); 1061facf4a8dSllai1 1062facf4a8dSllai1 /* The first nvpair must be the mount point */ 1063facf4a8dSllai1 nvp = nvlist_next_nvpair(nvl, NULL); 1064facf4a8dSllai1 if (strcmp(nvpair_name(nvp), SDEV_NVNAME_MOUNTPT) != 0) { 1065facf4a8dSllai1 cmn_err(CE_NOTE, 1066facf4a8dSllai1 "devname_profile_update: mount point not specified"); 1067facf4a8dSllai1 nvlist_free(nvl); 1068facf4a8dSllai1 return (EINVAL); 1069facf4a8dSllai1 } 1070facf4a8dSllai1 1071facf4a8dSllai1 /* find the matching filesystem instance */ 1072facf4a8dSllai1 rv = nvpair_value_string(nvp, &mntpt); 1073facf4a8dSllai1 if (rv != 0) { 1074facf4a8dSllai1 cmn_err(CE_WARN, sdev_nvp_val_err, 1075facf4a8dSllai1 rv, nvpair_name(nvp)); 1076facf4a8dSllai1 } else { 1077facf4a8dSllai1 mntinfo = sdev_find_mntinfo(mntpt); 1078facf4a8dSllai1 if (mntinfo == NULL) { 1079facf4a8dSllai1 cmn_err(CE_NOTE, "devname_profile_update: " 1080facf4a8dSllai1 " mount point %s not found", mntpt); 1081facf4a8dSllai1 nvlist_free(nvl); 1082facf4a8dSllai1 return (EINVAL); 1083facf4a8dSllai1 } 1084facf4a8dSllai1 1085facf4a8dSllai1 /* now do the hardwork to process the profile */ 1086facf4a8dSllai1 sdev_process_profile(mntinfo, nvl); 1087facf4a8dSllai1 1088facf4a8dSllai1 sdev_mntinfo_rele(mntinfo); 1089facf4a8dSllai1 } 1090facf4a8dSllai1 1091facf4a8dSllai1 nvlist_free(nvl); 1092facf4a8dSllai1 return (0); 1093facf4a8dSllai1 } 1094