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