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 }