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