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 * Build directory vnodes based on the profile and the global 659 * dev instance. 660 */ 661 void 662 prof_filldir(struct sdev_node *ddv) 663 { 664 int firsttime = 1; 665 struct sdev_node *gdir = ddv->sdev_origin; 666 667 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 668 669 /* 670 * We need to rebuild the directory content if 671 * - SDEV_BUILD is set 672 * - The device tree generation number has changed 673 * - The corresponding /dev namespace has been updated 674 */ 675 check_build: 676 if ((ddv->sdev_flags & SDEV_BUILD) == 0 && 677 ddv->sdev_devtree_gen == devtree_gen && 678 (gdir == NULL || ddv->sdev_ldir_gen 679 == gdir->sdev_gdir_gen)) 680 return; /* already up to date */ 681 682 /* We may have become a zombie (across a try) */ 683 if (ddv->sdev_state == SDEV_ZOMBIE) 684 return; 685 686 if (firsttime && rw_tryupgrade(&ddv->sdev_contents) == 0) { 687 rw_exit(&ddv->sdev_contents); 688 firsttime = 0; 689 rw_enter(&ddv->sdev_contents, RW_WRITER); 690 goto check_build; 691 } 692 sdcmn_err10(("devtree_gen (%s): %ld -> %ld\n", 693 ddv->sdev_path, ddv->sdev_devtree_gen, devtree_gen)); 694 if (gdir) 695 sdcmn_err10(("sdev_dir_gen (%s): %ld -> %ld\n", 696 ddv->sdev_path, ddv->sdev_ldir_gen, 697 gdir->sdev_gdir_gen)); 698 699 /* update flags and generation number so next filldir is quick */ 700 ddv->sdev_flags &= ~SDEV_BUILD; 701 ddv->sdev_devtree_gen = devtree_gen; 702 if (gdir) 703 ddv->sdev_ldir_gen = gdir->sdev_gdir_gen; 704 705 prof_make_symlinks(ddv); 706 prof_make_maps(ddv); 707 prof_make_names(ddv); 708 rw_downgrade(&ddv->sdev_contents); 709 } 710 711 /* apply include/exclude pattern to existing directory content */ 712 static void 713 apply_dir_pattern(struct sdev_node *dir, char *expr, char *pathleft, int type) 714 { 715 struct sdev_node *dv; 716 717 /* leaf pattern */ 718 if (pathleft == NULL) { 719 if (type == PROFILE_TYPE_INCLUDE) 720 return; /* nothing to do for include */ 721 (void) sdev_cleandir(dir, expr, SDEV_ENFORCE); 722 return; 723 } 724 725 /* directory pattern */ 726 rw_enter(&dir->sdev_contents, RW_WRITER); 727 728 for (dv = SDEV_FIRST_ENTRY(dir); dv; dv = SDEV_NEXT_ENTRY(dir, dv)) { 729 if (gmatch(dv->sdev_name, expr) == 0 || 730 SDEVTOV(dv)->v_type != VDIR) 731 continue; 732 process_rule(dv, dv->sdev_origin, 733 pathleft, NULL, type); 734 } 735 rw_exit(&dir->sdev_contents); 736 } 737 738 /* 739 * Add a profile rule. 740 * tgt represents a device name matching expression, 741 * matching device names are to be either included or excluded. 742 */ 743 static void 744 prof_add_rule(char *name, char *tgt, struct sdev_node *dir, int type) 745 { 746 int error; 747 nvlist_t **nvlp = NULL; 748 int rv; 749 750 ASSERT(SDEVTOV(dir)->v_type == VDIR); 751 752 rw_enter(&dir->sdev_contents, RW_WRITER); 753 754 switch (type) { 755 case PROFILE_TYPE_INCLUDE: 756 if (tgt) 757 nvlp = &(dir->sdev_prof.dev_glob_incdir); 758 else 759 nvlp = &(dir->sdev_prof.dev_name); 760 break; 761 case PROFILE_TYPE_EXCLUDE: 762 if (tgt) 763 nvlp = &(dir->sdev_prof.dev_glob_excdir); 764 else 765 nvlp = &(dir->sdev_prof.dev_name); 766 break; 767 case PROFILE_TYPE_MAP: 768 nvlp = &(dir->sdev_prof.dev_map); 769 break; 770 case PROFILE_TYPE_SYMLINK: 771 nvlp = &(dir->sdev_prof.dev_symlink); 772 break; 773 }; 774 775 /* initialize nvlist */ 776 if (*nvlp == NULL) { 777 error = nvlist_alloc(nvlp, NV_UNIQUE_NAME, KM_SLEEP); 778 ASSERT(error == 0); 779 } 780 781 if (tgt) { 782 rv = nvlist_add_string(*nvlp, name, tgt); 783 } else { 784 rv = nvlist_add_int32(*nvlp, name, type); 785 } 786 ASSERT(rv == 0); 787 /* rebuild directory content */ 788 dir->sdev_flags |= SDEV_BUILD; 789 790 if ((type == PROFILE_TYPE_INCLUDE) && 791 (strpbrk(name, "*?[]") != NULL)) { 792 dir->sdev_prof.has_glob = 1; 793 } 794 795 rw_exit(&dir->sdev_contents); 796 797 /* additional details for glob pattern and exclusion */ 798 switch (type) { 799 case PROFILE_TYPE_INCLUDE: 800 case PROFILE_TYPE_EXCLUDE: 801 apply_dir_pattern(dir, name, tgt, type); 802 break; 803 }; 804 } 805 806 /* 807 * Parse path components and apply requested matching rule at 808 * directory level. 809 */ 810 static void 811 process_rule(struct sdev_node *dir, struct sdev_node *gdir, 812 char *path, char *tgt, int type) 813 { 814 char *name; 815 struct pathname pn; 816 int rv = 0; 817 818 if ((strlen(path) > 5) && (strncmp(path, "/dev/", 5) == 0)) { 819 path += 5; 820 } 821 822 if (pn_get(path, UIO_SYSSPACE, &pn) != 0) 823 return; 824 825 name = kmem_alloc(MAXPATHLEN, KM_SLEEP); 826 (void) pn_getcomponent(&pn, name); 827 pn_skipslash(&pn); 828 SDEV_HOLD(dir); 829 830 while (pn_pathleft(&pn)) { 831 /* If this is pattern, just add the pattern */ 832 if (strpbrk(name, "*?[]") != NULL && 833 (type == PROFILE_TYPE_INCLUDE || 834 type == PROFILE_TYPE_EXCLUDE)) { 835 ASSERT(tgt == NULL); 836 tgt = pn.pn_path; 837 break; 838 } 839 if ((rv = prof_make_dir(name, &gdir, &dir)) != 0) { 840 cmn_err(CE_CONT, "process_rule: %s error %d\n", 841 path, rv); 842 break; 843 } 844 (void) pn_getcomponent(&pn, name); 845 pn_skipslash(&pn); 846 } 847 848 /* process the leaf component */ 849 if (rv == 0) { 850 prof_add_rule(name, tgt, dir, type); 851 SDEV_SIMPLE_RELE(dir); 852 } 853 854 kmem_free(name, MAXPATHLEN); 855 pn_free(&pn); 856 } 857 858 static int 859 copyin_nvlist(char *packed_usr, size_t packed_sz, nvlist_t **nvlp) 860 { 861 int err = 0; 862 char *packed; 863 nvlist_t *profile = NULL; 864 865 /* simple sanity check */ 866 if (packed_usr == NULL || packed_sz == 0) 867 return (NULL); 868 869 /* copyin packed profile nvlist */ 870 packed = kmem_alloc(packed_sz, KM_NOSLEEP); 871 if (packed == NULL) 872 return (ENOMEM); 873 err = copyin(packed_usr, packed, packed_sz); 874 875 /* unpack packed profile nvlist */ 876 if (err) 877 cmn_err(CE_WARN, "copyin_nvlist: copyin failed with " 878 "err %d\n", err); 879 else if (err = nvlist_unpack(packed, packed_sz, &profile, KM_NOSLEEP)) 880 cmn_err(CE_WARN, "copyin_nvlist: nvlist_unpack " 881 "failed with err %d\n", err); 882 883 kmem_free(packed, packed_sz); 884 if (err == 0) 885 *nvlp = profile; 886 return (err); 887 } 888 889 /* 890 * Process profile passed down from libdevinfo. There are four types 891 * of matching rules: 892 * include: export a name or names matching a pattern 893 * exclude: exclude a name or names matching a pattern 894 * symlink: create a local symlink 895 * map: export a device with a name different from the global zone 896 * Note: We may consider supporting VOP_SYMLINK in non-global instances, 897 * because it does not present any security risk. For now, the fs 898 * instance is read only. 899 */ 900 static void 901 sdev_process_profile(struct sdev_data *sdev_data, nvlist_t *profile) 902 { 903 nvpair_t *nvpair; 904 char *nvname, *dname; 905 struct sdev_node *dir, *gdir; 906 char **pair; /* for symlinks and maps */ 907 uint_t nelem; 908 int rv; 909 910 gdir = sdev_origins->sdev_root; /* root of global /dev */ 911 dir = sdev_data->sdev_root; /* root of current instance */ 912 913 ASSERT(profile); 914 915 /* process nvpairs in the list */ 916 nvpair = NULL; 917 while (nvpair = nvlist_next_nvpair(profile, nvpair)) { 918 nvname = nvpair_name(nvpair); 919 ASSERT(nvname != NULL); 920 921 if (strcmp(nvname, SDEV_NVNAME_INCLUDE) == 0) { 922 rv = nvpair_value_string(nvpair, &dname); 923 if (rv != 0) { 924 cmn_err(CE_WARN, sdev_nvp_val_err, 925 rv, nvpair_name(nvpair)); 926 break; 927 } 928 process_rule(dir, gdir, dname, NULL, 929 PROFILE_TYPE_INCLUDE); 930 } else if (strcmp(nvname, SDEV_NVNAME_EXCLUDE) == 0) { 931 rv = nvpair_value_string(nvpair, &dname); 932 if (rv != 0) { 933 cmn_err(CE_WARN, sdev_nvp_val_err, 934 rv, nvpair_name(nvpair)); 935 break; 936 } 937 process_rule(dir, gdir, dname, NULL, 938 PROFILE_TYPE_EXCLUDE); 939 } else if (strcmp(nvname, SDEV_NVNAME_SYMLINK) == 0) { 940 rv = nvpair_value_string_array(nvpair, &pair, &nelem); 941 if (rv != 0) { 942 cmn_err(CE_WARN, sdev_nvp_val_err, 943 rv, nvpair_name(nvpair)); 944 break; 945 } 946 ASSERT(nelem == 2); 947 process_rule(dir, gdir, pair[0], pair[1], 948 PROFILE_TYPE_SYMLINK); 949 } else if (strcmp(nvname, SDEV_NVNAME_MAP) == 0) { 950 rv = nvpair_value_string_array(nvpair, &pair, &nelem); 951 if (rv != 0) { 952 cmn_err(CE_WARN, sdev_nvp_val_err, 953 rv, nvpair_name(nvpair)); 954 break; 955 } 956 process_rule(dir, gdir, pair[1], pair[0], 957 PROFILE_TYPE_MAP); 958 } else if (strcmp(nvname, SDEV_NVNAME_MOUNTPT) != 0) { 959 cmn_err(CE_WARN, "sdev_process_profile: invalid " 960 "nvpair %s\n", nvname); 961 } 962 } 963 } 964 965 /*ARGSUSED*/ 966 int 967 prof_lookup(vnode_t *dvp, char *nm, struct vnode **vpp, struct cred *cred) 968 { 969 struct sdev_node *ddv = VTOSDEV(dvp); 970 struct sdev_node *dv; 971 int nmlen; 972 973 /* 974 * Empty name or ., return node itself. 975 */ 976 nmlen = strlen(nm); 977 if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) { 978 *vpp = SDEVTOV(ddv); 979 VN_HOLD(*vpp); 980 return (0); 981 } 982 983 /* 984 * .., return the parent directory 985 */ 986 if ((nmlen == 2) && (strcmp(nm, "..") == 0)) { 987 *vpp = SDEVTOV(ddv->sdev_dotdot); 988 VN_HOLD(*vpp); 989 return (0); 990 } 991 992 rw_enter(&ddv->sdev_contents, RW_READER); 993 dv = sdev_cache_lookup(ddv, nm); 994 if (dv == NULL) { 995 prof_filldir(ddv); 996 dv = sdev_cache_lookup(ddv, nm); 997 } 998 rw_exit(&ddv->sdev_contents); 999 if (dv == NULL) { 1000 sdcmn_err10(("prof_lookup: %s not found\n", nm)); 1001 return (ENOENT); 1002 } 1003 1004 return (sdev_to_vp(dv, vpp)); 1005 } 1006 1007 /* 1008 * This is invoked after a new filesystem is mounted to define the 1009 * name space. It is also invoked during normal system operation 1010 * to update the name space. 1011 * 1012 * Applications call di_prof_commit() in libdevinfo, which invokes 1013 * modctl(). modctl calls this function. The input is a packed nvlist. 1014 */ 1015 int 1016 devname_profile_update(char *packed, size_t packed_sz) 1017 { 1018 char *mntpt; 1019 nvlist_t *nvl; 1020 nvpair_t *nvp; 1021 struct sdev_data *mntinfo; 1022 int err; 1023 int rv; 1024 1025 nvl = NULL; 1026 if ((err = copyin_nvlist(packed, packed_sz, &nvl)) != 0) 1027 return (err); 1028 ASSERT(nvl); 1029 1030 /* The first nvpair must be the mount point */ 1031 nvp = nvlist_next_nvpair(nvl, NULL); 1032 if (strcmp(nvpair_name(nvp), SDEV_NVNAME_MOUNTPT) != 0) { 1033 cmn_err(CE_NOTE, 1034 "devname_profile_update: mount point not specified"); 1035 nvlist_free(nvl); 1036 return (EINVAL); 1037 } 1038 1039 /* find the matching filesystem instance */ 1040 rv = nvpair_value_string(nvp, &mntpt); 1041 if (rv != 0) { 1042 cmn_err(CE_WARN, sdev_nvp_val_err, 1043 rv, nvpair_name(nvp)); 1044 } else { 1045 mntinfo = sdev_find_mntinfo(mntpt); 1046 if (mntinfo == NULL) { 1047 cmn_err(CE_NOTE, "devname_profile_update: " 1048 " mount point %s not found", mntpt); 1049 nvlist_free(nvl); 1050 return (EINVAL); 1051 } 1052 1053 /* now do the hardwork to process the profile */ 1054 sdev_process_profile(mntinfo, nvl); 1055 1056 sdev_mntinfo_rele(mntinfo); 1057 } 1058 1059 nvlist_free(nvl); 1060 return (0); 1061 } 1062