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