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 */ 285 prof_getattr(dir, name, gdv, &vattr, &avp, NULL); 286 287 if (gdv->v_type != VDIR) { 288 VN_RELE(gdv); 289 gdir = NULL; 290 } else 291 gdir = VTOSDEV(gdv); 292 293 if (prof_mknode(dir, rename, &newdv, &vattr, avp, 294 (void *)gdir, kcred) == 0) { 295 ASSERT(newdv->sdev_state != SDEV_ZOMBIE); 296 SDEV_SIMPLE_RELE(newdv); 297 } 298 } 299 300 static void 301 prof_make_sym(struct sdev_node *dir, char *lnm, char *tgt) 302 { 303 struct sdev_node *newdv; 304 305 if (prof_mknode(dir, lnm, &newdv, &sdev_vattr_lnk, NULL, 306 (void *)tgt, kcred) == 0) { 307 ASSERT(newdv->sdev_state != SDEV_ZOMBIE); 308 SDEV_SIMPLE_RELE(newdv); 309 } 310 } 311 312 /* 313 * Create symlinks in the current directory based on profile 314 */ 315 static void 316 prof_make_symlinks(struct sdev_node *dir) 317 { 318 char *tgt, *lnm; 319 nvpair_t *nvp = NULL; 320 nvlist_t *nvl = dir->sdev_prof.dev_symlink; 321 int rv; 322 323 ASSERT(RW_WRITE_HELD(&dir->sdev_contents)); 324 325 if (nvl == NULL) 326 return; 327 328 while (nvp = nvlist_next_nvpair(nvl, nvp)) { 329 lnm = nvpair_name(nvp); 330 rv = nvpair_value_string(nvp, &tgt); 331 if (rv != 0) { 332 cmn_err(CE_WARN, sdev_nvp_val_err, 333 rv, nvpair_name(nvp)); 334 break; 335 } 336 prof_make_sym(dir, lnm, tgt); 337 } 338 } 339 340 static void 341 prof_make_maps(struct sdev_node *dir) 342 { 343 nvpair_t *nvp = NULL; 344 nvlist_t *nvl = dir->sdev_prof.dev_map; 345 int rv; 346 347 ASSERT(RW_WRITE_HELD(&dir->sdev_contents)); 348 349 if (nvl == NULL) 350 return; 351 352 while (nvp = nvlist_next_nvpair(nvl, nvp)) { 353 char *name; 354 char *rename = nvpair_name(nvp); 355 rv = nvpair_value_string(nvp, &name); 356 if (rv != 0) { 357 cmn_err(CE_WARN, sdev_nvp_val_err, 358 rv, nvpair_name(nvp)); 359 break; 360 } 361 sdcmn_err10(("map %s -> %s\n", name, rename)); 362 (void) prof_lookup_globaldev(dir, sdev_origins->sdev_root, 363 name, rename); 364 } 365 } 366 367 struct match_arg { 368 char *expr; 369 int match; 370 }; 371 372 static int 373 match_name(char *name, void *arg) 374 { 375 struct match_arg *margp = (struct match_arg *)arg; 376 377 if (gmatch(name, margp->expr)) { 378 margp->match = 1; 379 return (WALK_DIR_TERMINATE); 380 } 381 return (WALK_DIR_CONTINUE); 382 } 383 384 static int 385 is_nonempty_dir(char *name, char *pathleft, struct sdev_node *dir) 386 { 387 struct match_arg marg; 388 struct pathname pn; 389 struct vnode *gvp; 390 struct sdev_node *gdir = dir->sdev_origin; 391 392 if (VOP_LOOKUP(SDEVTOV(gdir), name, &gvp, NULL, 0, NULL, kcred) != 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); 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 static int 529 prof_make_name(char *nm, void *arg) 530 { 531 struct sdev_node *ddv = (struct sdev_node *)arg; 532 533 if (prof_name_matched(nm, ddv)) 534 prof_lookup_globaldev(ddv, ddv->sdev_origin, nm, nm); 535 return (WALK_DIR_CONTINUE); 536 } 537 538 static void 539 prof_make_names_glob(struct sdev_node *ddv) 540 { 541 struct sdev_node *gdir; 542 543 gdir = ddv->sdev_origin; 544 if (gdir == NULL) 545 return; 546 walk_dir(SDEVTOV(gdir), (void *)ddv, prof_make_name); 547 } 548 549 static void 550 prof_make_names(struct sdev_node *dir) 551 { 552 char *name; 553 nvpair_t *nvp = NULL; 554 nvlist_t *nvl = dir->sdev_prof.dev_name; 555 int rv; 556 557 ASSERT(RW_WRITE_HELD(&dir->sdev_contents)); 558 559 if (nvl == NULL) 560 return; 561 562 if (dir->sdev_prof.has_glob) { 563 prof_make_names_glob(dir); 564 return; 565 } 566 567 /* Walk nvlist and lookup corresponding device in global inst */ 568 while (nvp = nvlist_next_nvpair(nvl, nvp)) { 569 int type; 570 rv = nvpair_value_int32(nvp, &type); 571 if (rv != 0) { 572 cmn_err(CE_WARN, sdev_nvp_val_err, 573 rv, nvpair_name(nvp)); 574 break; 575 } 576 if (type == PROFILE_TYPE_EXCLUDE) 577 continue; 578 name = nvpair_name(nvp); 579 (void) prof_lookup_globaldev(dir, dir->sdev_origin, 580 name, name); 581 } 582 } 583 584 /* 585 * Build directory vnodes based on the profile and the global 586 * dev instance. 587 */ 588 void 589 prof_filldir(struct sdev_node *ddv) 590 { 591 int firsttime = 1; 592 struct sdev_node *gdir = ddv->sdev_origin; 593 594 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 595 596 /* 597 * We need to rebuild the directory content if 598 * - SDEV_BUILD is set 599 * - The device tree generation number has changed 600 * - The corresponding /dev namespace has been updated 601 */ 602 check_build: 603 if ((ddv->sdev_flags & SDEV_BUILD) == 0 && 604 ddv->sdev_devtree_gen == devtree_gen && 605 (gdir == NULL || ddv->sdev_ldir_gen 606 == gdir->sdev_gdir_gen)) 607 return; /* already up to date */ 608 609 if (firsttime && rw_tryupgrade(&ddv->sdev_contents) == 0) { 610 rw_exit(&ddv->sdev_contents); 611 firsttime = 0; 612 rw_enter(&ddv->sdev_contents, RW_WRITER); 613 goto check_build; 614 } 615 sdcmn_err10(("devtree_gen (%s): %ld -> %ld\n", 616 ddv->sdev_path, ddv->sdev_devtree_gen, devtree_gen)); 617 if (gdir) 618 sdcmn_err10(("sdev_dir_gen (%s): %ld -> %ld\n", 619 ddv->sdev_path, ddv->sdev_ldir_gen, 620 gdir->sdev_gdir_gen)); 621 622 /* update flags and generation number so next filldir is quick */ 623 ddv->sdev_flags &= ~SDEV_BUILD; 624 ddv->sdev_devtree_gen = devtree_gen; 625 if (gdir) 626 ddv->sdev_ldir_gen = gdir->sdev_gdir_gen; 627 628 prof_make_symlinks(ddv); 629 prof_make_maps(ddv); 630 prof_make_names(ddv); 631 rw_downgrade(&ddv->sdev_contents); 632 } 633 634 /* apply include/exclude pattern to existing directory content */ 635 static void 636 apply_dir_pattern(struct sdev_node *dir, char *expr, char *pathleft, int type) 637 { 638 struct sdev_node *dv; 639 640 /* leaf pattern */ 641 if (pathleft == NULL) { 642 if (type == PROFILE_TYPE_INCLUDE) 643 return; /* nothing to do for include */ 644 (void) sdev_cleandir(dir, expr, SDEV_ENFORCE); 645 return; 646 } 647 648 /* directory pattern */ 649 rw_enter(&dir->sdev_contents, RW_WRITER); 650 for (dv = dir->sdev_dot; dv; dv = dv->sdev_next) { 651 if (gmatch(dv->sdev_name, expr) == 0 || 652 SDEVTOV(dv)->v_type != VDIR) 653 continue; 654 process_rule(dv, dv->sdev_origin, 655 pathleft, NULL, type); 656 } 657 rw_exit(&dir->sdev_contents); 658 } 659 660 /* 661 * Add a profile rule. 662 * tgt represents a device name matching expression, 663 * matching device names are to be either included or excluded. 664 */ 665 static void 666 prof_add_rule(char *name, char *tgt, struct sdev_node *dir, int type) 667 { 668 int error; 669 nvlist_t **nvlp = NULL; 670 int rv; 671 672 ASSERT(SDEVTOV(dir)->v_type == VDIR); 673 674 rw_enter(&dir->sdev_contents, RW_WRITER); 675 676 switch (type) { 677 case PROFILE_TYPE_INCLUDE: 678 if (tgt) 679 nvlp = &(dir->sdev_prof.dev_glob_incdir); 680 else 681 nvlp = &(dir->sdev_prof.dev_name); 682 break; 683 case PROFILE_TYPE_EXCLUDE: 684 if (tgt) 685 nvlp = &(dir->sdev_prof.dev_glob_excdir); 686 else 687 nvlp = &(dir->sdev_prof.dev_name); 688 break; 689 case PROFILE_TYPE_MAP: 690 nvlp = &(dir->sdev_prof.dev_map); 691 break; 692 case PROFILE_TYPE_SYMLINK: 693 nvlp = &(dir->sdev_prof.dev_symlink); 694 break; 695 }; 696 697 /* initialize nvlist */ 698 if (*nvlp == NULL) { 699 error = nvlist_alloc(nvlp, NV_UNIQUE_NAME, KM_SLEEP); 700 ASSERT(error == 0); 701 } 702 703 if (tgt) { 704 rv = nvlist_add_string(*nvlp, name, tgt); 705 } else { 706 rv = nvlist_add_int32(*nvlp, name, type); 707 } 708 ASSERT(rv == 0); 709 /* rebuild directory content */ 710 dir->sdev_flags |= SDEV_BUILD; 711 712 if ((type == PROFILE_TYPE_INCLUDE) && 713 (strpbrk(name, "*?[]") != NULL)) { 714 dir->sdev_prof.has_glob = 1; 715 } 716 717 rw_exit(&dir->sdev_contents); 718 719 /* additional details for glob pattern and exclusion */ 720 switch (type) { 721 case PROFILE_TYPE_INCLUDE: 722 case PROFILE_TYPE_EXCLUDE: 723 apply_dir_pattern(dir, name, tgt, type); 724 break; 725 }; 726 } 727 728 /* 729 * Parse path components and apply requested matching rule at 730 * directory level. 731 */ 732 static void 733 process_rule(struct sdev_node *dir, struct sdev_node *gdir, 734 char *path, char *tgt, int type) 735 { 736 char *name; 737 struct pathname pn; 738 int rv = 0; 739 740 if ((strlen(path) > 5) && (strncmp(path, "/dev/", 5) == 0)) { 741 path += 5; 742 } 743 744 if (pn_get(path, UIO_SYSSPACE, &pn) != 0) 745 return; 746 747 name = kmem_alloc(MAXPATHLEN, KM_SLEEP); 748 (void) pn_getcomponent(&pn, name); 749 pn_skipslash(&pn); 750 SDEV_HOLD(dir); 751 752 while (pn_pathleft(&pn)) { 753 /* If this is pattern, just add the pattern */ 754 if (strpbrk(name, "*?[]") != NULL && 755 (type == PROFILE_TYPE_INCLUDE || 756 type == PROFILE_TYPE_EXCLUDE)) { 757 ASSERT(tgt == NULL); 758 tgt = pn.pn_path; 759 break; 760 } 761 if ((rv = prof_make_dir(name, &gdir, &dir)) != 0) { 762 cmn_err(CE_CONT, "process_rule: %s error %d\n", 763 path, rv); 764 break; 765 } 766 (void) pn_getcomponent(&pn, name); 767 pn_skipslash(&pn); 768 } 769 770 /* process the leaf component */ 771 if (rv == 0) { 772 prof_add_rule(name, tgt, dir, type); 773 SDEV_SIMPLE_RELE(dir); 774 } 775 776 kmem_free(name, MAXPATHLEN); 777 pn_free(&pn); 778 } 779 780 static int 781 copyin_nvlist(char *packed_usr, size_t packed_sz, nvlist_t **nvlp) 782 { 783 int err = 0; 784 char *packed; 785 nvlist_t *profile = NULL; 786 787 /* simple sanity check */ 788 if (packed_usr == NULL || packed_sz == 0) 789 return (NULL); 790 791 /* copyin packed profile nvlist */ 792 packed = kmem_alloc(packed_sz, KM_NOSLEEP); 793 if (packed == NULL) 794 return (ENOMEM); 795 err = copyin(packed_usr, packed, packed_sz); 796 797 /* unpack packed profile nvlist */ 798 if (err) 799 cmn_err(CE_WARN, "copyin_nvlist: copyin failed with " 800 "err %d\n", err); 801 else if (err = nvlist_unpack(packed, packed_sz, &profile, KM_NOSLEEP)) 802 cmn_err(CE_WARN, "copyin_nvlist: nvlist_unpack " 803 "failed with err %d\n", err); 804 805 kmem_free(packed, packed_sz); 806 if (err == 0) 807 *nvlp = profile; 808 return (err); 809 } 810 811 /* 812 * Process profile passed down from libdevinfo. There are four types 813 * of matching rules: 814 * include: export a name or names matching a pattern 815 * exclude: exclude a name or names matching a pattern 816 * symlink: create a local symlink 817 * map: export a device with a name different from the global zone 818 * Note: We may consider supporting VOP_SYMLINK in non-global instances, 819 * because it does not present any security risk. For now, the fs 820 * instance is read only. 821 */ 822 static void 823 sdev_process_profile(struct sdev_data *sdev_data, nvlist_t *profile) 824 { 825 nvpair_t *nvpair; 826 char *nvname, *dname; 827 struct sdev_node *dir, *gdir; 828 char **pair; /* for symlinks and maps */ 829 uint_t nelem; 830 int rv; 831 832 gdir = sdev_origins->sdev_root; /* root of global /dev */ 833 dir = sdev_data->sdev_root; /* root of current instance */ 834 835 ASSERT(profile); 836 837 /* process nvpairs in the list */ 838 nvpair = NULL; 839 while (nvpair = nvlist_next_nvpair(profile, nvpair)) { 840 nvname = nvpair_name(nvpair); 841 ASSERT(nvname != NULL); 842 843 if (strcmp(nvname, SDEV_NVNAME_INCLUDE) == 0) { 844 rv = nvpair_value_string(nvpair, &dname); 845 if (rv != 0) { 846 cmn_err(CE_WARN, sdev_nvp_val_err, 847 rv, nvpair_name(nvpair)); 848 break; 849 } 850 process_rule(dir, gdir, dname, NULL, 851 PROFILE_TYPE_INCLUDE); 852 } else if (strcmp(nvname, SDEV_NVNAME_EXCLUDE) == 0) { 853 rv = nvpair_value_string(nvpair, &dname); 854 if (rv != 0) { 855 cmn_err(CE_WARN, sdev_nvp_val_err, 856 rv, nvpair_name(nvpair)); 857 break; 858 } 859 process_rule(dir, gdir, dname, NULL, 860 PROFILE_TYPE_EXCLUDE); 861 } else if (strcmp(nvname, SDEV_NVNAME_SYMLINK) == 0) { 862 rv = nvpair_value_string_array(nvpair, &pair, &nelem); 863 if (rv != 0) { 864 cmn_err(CE_WARN, sdev_nvp_val_err, 865 rv, nvpair_name(nvpair)); 866 break; 867 } 868 ASSERT(nelem == 2); 869 process_rule(dir, gdir, pair[0], pair[1], 870 PROFILE_TYPE_SYMLINK); 871 } else if (strcmp(nvname, SDEV_NVNAME_MAP) == 0) { 872 rv = nvpair_value_string_array(nvpair, &pair, &nelem); 873 if (rv != 0) { 874 cmn_err(CE_WARN, sdev_nvp_val_err, 875 rv, nvpair_name(nvpair)); 876 break; 877 } 878 process_rule(dir, gdir, pair[1], pair[0], 879 PROFILE_TYPE_MAP); 880 } else if (strcmp(nvname, SDEV_NVNAME_MOUNTPT) != 0) { 881 cmn_err(CE_WARN, "sdev_process_profile: invalid " 882 "nvpair %s\n", nvname); 883 } 884 } 885 } 886 887 /*ARGSUSED*/ 888 int 889 prof_lookup(vnode_t *dvp, char *nm, struct vnode **vpp, struct cred *cred) 890 { 891 struct sdev_node *ddv = VTOSDEV(dvp); 892 struct sdev_node *dv; 893 int nmlen; 894 895 /* 896 * Empty name or ., return node itself. 897 */ 898 nmlen = strlen(nm); 899 if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) { 900 *vpp = SDEVTOV(ddv); 901 VN_HOLD(*vpp); 902 return (0); 903 } 904 905 /* 906 * .., return the parent directory 907 */ 908 if ((nmlen == 2) && (strcmp(nm, "..") == 0)) { 909 *vpp = SDEVTOV(ddv->sdev_dotdot); 910 VN_HOLD(*vpp); 911 return (0); 912 } 913 914 rw_enter(&ddv->sdev_contents, RW_READER); 915 dv = sdev_cache_lookup(ddv, nm); 916 if (dv == NULL) { 917 prof_filldir(ddv); 918 dv = sdev_cache_lookup(ddv, nm); 919 } 920 rw_exit(&ddv->sdev_contents); 921 if (dv == NULL) { 922 sdcmn_err10(("prof_lookup: %s not found\n", nm)); 923 return (ENOENT); 924 } 925 926 return (sdev_to_vp(dv, vpp)); 927 } 928 929 /* 930 * This is invoked after a new filesystem is mounted to define the 931 * name space. It is also invoked during normal system operation 932 * to update the name space. 933 * 934 * Applications call di_prof_commit() in libdevinfo, which invokes 935 * modctl(). modctl calls this function. The input is a packed nvlist. 936 */ 937 int 938 devname_profile_update(char *packed, size_t packed_sz) 939 { 940 char *mntpt; 941 nvlist_t *nvl; 942 nvpair_t *nvp; 943 struct sdev_data *mntinfo; 944 int err; 945 int rv; 946 947 nvl = NULL; 948 if ((err = copyin_nvlist(packed, packed_sz, &nvl)) != 0) 949 return (err); 950 ASSERT(nvl); 951 952 /* The first nvpair must be the mount point */ 953 nvp = nvlist_next_nvpair(nvl, NULL); 954 if (strcmp(nvpair_name(nvp), SDEV_NVNAME_MOUNTPT) != 0) { 955 cmn_err(CE_NOTE, 956 "devname_profile_update: mount point not specified"); 957 nvlist_free(nvl); 958 return (EINVAL); 959 } 960 961 /* find the matching filesystem instance */ 962 rv = nvpair_value_string(nvp, &mntpt); 963 if (rv != 0) { 964 cmn_err(CE_WARN, sdev_nvp_val_err, 965 rv, nvpair_name(nvp)); 966 } else { 967 mntinfo = sdev_find_mntinfo(mntpt); 968 if (mntinfo == NULL) { 969 cmn_err(CE_NOTE, "devname_profile_update: " 970 " mount point %s not found", mntpt); 971 nvlist_free(nvl); 972 return (EINVAL); 973 } 974 975 /* now do the hardwork to process the profile */ 976 sdev_process_profile(mntinfo, nvl); 977 978 sdev_mntinfo_rele(mntinfo); 979 } 980 981 nvlist_free(nvl); 982 return (0); 983 } 984