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