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 }