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