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 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 22 * Use is subject to license terms. 23 */ 24 25 #pragma ident "%Z%%M% %I% %E% SMI" 26 27 #include <sys/systm.h> 28 29 #include <nfs/nfs.h> 30 #include <nfs/export.h> 31 #include <sys/cmn_err.h> 32 33 #define PSEUDOFS_SUFFIX " (pseudo)" 34 35 /* 36 * A version of VOP_FID that deals with a remote VOP_FID for nfs. 37 * If vp is an nfs node, nfs4_fid() returns EREMOTE, nfs3_fid() and nfs_fid() 38 * returns the filehandle of vp as its fid. When nfs uses fid to set the 39 * exportinfo filehandle template, a remote nfs filehandle would be too big for 40 * the fid of the exported directory. This routine remaps the value of the 41 * attribute va_nodeid of vp to be the fid of vp, so that the fid can fit. 42 * 43 * We need this fid mainly for setting up NFSv4 server namespace where an 44 * nfs filesystem is also part of it. Thus, need to be able to setup a pseudo 45 * exportinfo for an nfs node. 46 * 47 * e.g. mount a filesystem on top of a nfs dir, and then share the new mount 48 * (like exporting a local disk from a "diskless" client) 49 */ 50 int 51 vop_fid_pseudo(vnode_t *vp, fid_t *fidp) 52 { 53 struct vattr va; 54 int error; 55 56 error = VOP_FID(vp, fidp); 57 58 /* 59 * XXX nfs4_fid() does nothing and returns EREMOTE. 60 * XXX nfs3_fid()/nfs_fid() returns nfs filehandle as its fid 61 * which has a bigger length than local fid. 62 * NFS_FH4MAXDATA is the size of 63 * fhandle4_t.fh_xdata[NFS_FH4MAXDATA]. 64 * 65 * Note: nfs[2,3,4]_fid() only gets called for diskless clients. 66 */ 67 if (error == EREMOTE || 68 (error == 0 && fidp->fid_len > NFS_FH4MAXDATA)) { 69 70 va.va_mask = AT_NODEID; 71 error = VOP_GETATTR(vp, &va, 0, CRED()); 72 if (error) 73 return (error); 74 75 fidp->fid_len = sizeof (va.va_nodeid); 76 bcopy(&va.va_nodeid, fidp->fid_data, fidp->fid_len); 77 return (0); 78 } 79 80 return (error); 81 } 82 83 /* 84 * Get an nfsv4 vnode of the given fid from the visible list of an 85 * nfs filesystem or get the exi_vp if it is the root node. 86 */ 87 int 88 nfs4_vget_pseudo(struct exportinfo *exi, vnode_t **vpp, fid_t *fidp) 89 { 90 fid_t exp_fid; 91 struct exp_visible *visp; 92 int error; 93 94 /* check if the given fid is in the visible list */ 95 96 for (visp = exi->exi_visible; visp; visp = visp->vis_next) { 97 if (EQFID(fidp, &visp->vis_fid)) { 98 VN_HOLD(visp->vis_vp); 99 *vpp = visp->vis_vp; 100 return (0); 101 } 102 } 103 104 /* check if the given fid is the same as the exported node */ 105 106 bzero(&exp_fid, sizeof (exp_fid)); 107 exp_fid.fid_len = MAXFIDSZ; 108 error = vop_fid_pseudo(exi->exi_vp, &exp_fid); 109 if (error) 110 return (error); 111 112 if (EQFID(fidp, &exp_fid)) { 113 VN_HOLD(exi->exi_vp); 114 *vpp = exi->exi_vp; 115 return (0); 116 } 117 118 return (ENOENT); 119 } 120 121 /* 122 * Create a pseudo export entry 123 * 124 * This is an export entry that's created as the 125 * side-effect of a "real" export. As a part of 126 * a real export, the pathname to the export is 127 * checked to see if all the directory components 128 * are accessible via an NFSv4 client, i.e. are 129 * exported. If treeclimb_export() finds an unexported 130 * mountpoint along the path, then it calls this 131 * function to export it. 132 * 133 * This pseudo export differs from a real export in that 134 * it only allows read-only access. A "visible" list of 135 * directories is added to filter lookup and readdir results 136 * to only contain dirnames which lead to descendant shares. 137 * 138 * A visible list has a per-file-system scope. Any exportinfo 139 * struct (real or pseudo) can have a visible list as long as 140 * a) its export root is VROOT 141 * b) a descendant of the export root is shared 142 */ 143 int 144 pseudo_exportfs(vnode_t *vp, struct exp_visible *vis_head, 145 struct exportdata *exdata) 146 { 147 struct exportinfo *exi; 148 struct exportdata *kex; 149 fid_t fid; 150 fsid_t fsid; 151 int error, vpathlen; 152 153 ASSERT(RW_WRITE_HELD(&exported_lock)); 154 155 /* 156 * Get the vfs id 157 */ 158 bzero(&fid, sizeof (fid)); 159 fid.fid_len = MAXFIDSZ; 160 error = vop_fid_pseudo(vp, &fid); 161 if (error) { 162 /* 163 * If VOP_FID returns ENOSPC then the fid supplied 164 * is too small. For now we simply return EREMOTE. 165 */ 166 if (error == ENOSPC) 167 error = EREMOTE; 168 return (error); 169 } 170 171 fsid = vp->v_vfsp->vfs_fsid; 172 exi = kmem_zalloc(sizeof (*exi), KM_SLEEP); 173 exi->exi_fsid = fsid; 174 exi->exi_fid = fid; 175 exi->exi_vp = vp; 176 VN_HOLD(exi->exi_vp); 177 exi->exi_visible = vis_head; 178 exi->exi_count = 1; 179 exi->exi_volatile_dev = (vfssw[vp->v_vfsp->vfs_fstype].vsw_flag & 180 VSW_VOLATILEDEV) ? 1 : 0; 181 mutex_init(&exi->exi_lock, NULL, MUTEX_DEFAULT, NULL); 182 183 /* 184 * Build up the template fhandle 185 */ 186 exi->exi_fh.fh_fsid = fsid; 187 ASSERT(exi->exi_fid.fid_len <= sizeof (exi->exi_fh.fh_xdata)); 188 exi->exi_fh.fh_xlen = exi->exi_fid.fid_len; 189 bcopy(exi->exi_fid.fid_data, exi->exi_fh.fh_xdata, 190 exi->exi_fid.fid_len); 191 exi->exi_fh.fh_len = sizeof (exi->exi_fh.fh_data); 192 193 kex = &exi->exi_export; 194 kex->ex_flags = EX_PSEUDO; 195 196 vpathlen = vp->v_path ? strlen(vp->v_path) : 0; 197 kex->ex_pathlen = vpathlen + strlen(PSEUDOFS_SUFFIX); 198 kex->ex_path = kmem_alloc(kex->ex_pathlen + 1, KM_SLEEP); 199 200 if (vpathlen) 201 (void) strcpy(kex->ex_path, vp->v_path); 202 (void) strcpy(kex->ex_path + vpathlen, PSEUDOFS_SUFFIX); 203 204 /* Transfer the secinfo data from exdata to this new pseudo node */ 205 if (exdata) 206 srv_secinfo_exp2pseu(&exi->exi_export, exdata); 207 208 /* 209 * Initialize auth cache lock 210 */ 211 rw_init(&exi->exi_cache_lock, NULL, RW_DEFAULT, NULL); 212 213 /* 214 * Insert the new entry at the front of the export list 215 */ 216 export_link(exi); 217 218 return (0); 219 } 220 221 /* 222 * Free a list of visible directories 223 */ 224 void 225 free_visible(struct exp_visible *head) 226 { 227 struct exp_visible *visp, *next; 228 229 for (visp = head; visp; visp = next) { 230 if (visp->vis_vp != NULL) 231 VN_RELE(visp->vis_vp); 232 233 next = visp->vis_next; 234 srv_secinfo_list_free(visp->vis_secinfo, visp->vis_seccnt); 235 kmem_free(visp, sizeof (*visp)); 236 } 237 } 238 239 /* 240 * Add a list of visible directories to a pseudo exportfs. 241 * 242 * When we export a new directory we need to add a new 243 * path segment through the pseudofs to reach the new 244 * directory. This new path is reflected in a list of 245 * directories added to the "visible" list. 246 * 247 * Here there are two lists of visible fids: one hanging off the 248 * pseudo exportinfo, and the one we want to add. It's possible 249 * that the two lists share a common path segment 250 * and have some common directories. We need to combine 251 * the lists so there's no duplicate entries. Where a common 252 * path component is found, the vis_count field is bumped. 253 * 254 * When the addition is complete, the supplied list is freed. 255 */ 256 257 static void 258 more_visible(struct exportinfo *exi, struct exp_visible *vis_head) 259 { 260 struct exp_visible *vp1, *vp2; 261 struct exp_visible *tail, *new; 262 int found; 263 264 /* 265 * If exportinfo doesn't already have a visible 266 * list just assign the entire supplied list. 267 */ 268 if (exi->exi_visible == NULL) { 269 exi->exi_visible = vis_head; 270 return; 271 } 272 273 /* 274 * The outer loop traverses the supplied list. 275 */ 276 for (vp1 = vis_head; vp1; vp1 = vp1->vis_next) { 277 278 /* 279 * Given an element from the list to be added, 280 * search the exportinfo visible list looking for a match. 281 * If a match is found, increment the reference count. 282 */ 283 found = 0; 284 285 for (vp2 = exi->exi_visible; vp2; vp2 = vp2->vis_next) { 286 287 tail = vp2; 288 289 if (EQFID(&vp1->vis_fid, &vp2->vis_fid)) { 290 found = 1; 291 vp2->vis_count++; 292 VN_RELE(vp1->vis_vp); 293 vp1->vis_vp = NULL; 294 295 /* 296 * If the visible struct we want to add 297 * (vp1) has vis_exported set to 1, then 298 * the matching visible struct we just found 299 * must also have it's vis_exported field 300 * set to 1. 301 * 302 * For example, if /export/home was shared 303 * (and a mountpoint), then "export" and 304 * "home" would each have visible structs in 305 * the root pseudo exportinfo. The vis_exported 306 * for home would be 1, and vis_exported for 307 * export would be 0. Now, if /export was 308 * also shared, more_visible would find the 309 * existing visible struct for export, and 310 * see that vis_exported was 0. The code 311 * below will set it to 1. 312 * 313 * vp1 is from vis list passed in (vis_head) 314 * vp2 is from vis list on pseudo exportinfo 315 */ 316 if (vp1->vis_exported && !vp2->vis_exported) 317 vp2->vis_exported = 1; 318 break; 319 } 320 } 321 322 /* If not found - add to the end of the list */ 323 if (! found) { 324 new = kmem_zalloc(sizeof (*new), KM_SLEEP); 325 *new = *vp1; 326 tail->vis_next = new; 327 new->vis_next = NULL; 328 vp1->vis_vp = NULL; 329 } 330 } 331 332 /* 333 * Throw away the path list. vis_vp pointers in vis_head list 334 * are either VN_RELEed or reassigned, and are set to NULL. 335 * There is no need to VN_RELE in free_visible for this vis_head. 336 */ 337 free_visible(vis_head); 338 } 339 340 /* 341 * Remove a list of visible directories from the pseudo exportfs. 342 * 343 * When we unexport a directory, we have to remove path 344 * components from the visible list in the pseudo exportfs 345 * entry. The supplied visible list contains the fids of the path 346 * to the unexported directory. The visible list of the export 347 * is checked against this list any matching fids have their 348 * reference count decremented. If a reference count drops to 349 * zero, then it means no paths now use this directory, so its 350 * fid can be removed from the visible list. 351 * 352 * When the last path is removed, the visible list will be null. 353 */ 354 static void 355 less_visible(struct exportinfo *exi, struct exp_visible *vis_head) 356 { 357 struct exp_visible *vp1, *vp2; 358 struct exp_visible *prev, *next; 359 360 /* 361 * The outer loop traverses the supplied list. 362 */ 363 for (vp1 = vis_head; vp1; vp1 = vp1->vis_next) { 364 365 /* 366 * Given an element from the list to be removed, 367 * search the exportinfo list looking for a match. 368 * If a match is found, decrement the reference 369 * count and drop the element if the count drops 370 * to zero. 371 */ 372 for (vp2 = exi->exi_visible, prev = NULL; vp2; vp2 = next) { 373 374 next = vp2->vis_next; 375 376 if (EQFID(&vp1->vis_fid, &vp2->vis_fid)) { 377 378 /* 379 * Decrement the ref count. 380 * Remove the entry if it's zero. 381 */ 382 if (--vp2->vis_count <= 0) { 383 if (prev == NULL) 384 exi->exi_visible = next; 385 else 386 prev->vis_next = next; 387 388 VN_RELE(vp2->vis_vp); 389 srv_secinfo_list_free(vp2->vis_secinfo, 390 vp2->vis_seccnt); 391 kmem_free(vp2, sizeof (*vp1)); 392 } else { 393 /* 394 * If we're here, then the vp2 will 395 * remain in the vis list. If the 396 * vis entry corresponds to the object 397 * being unshared, then vis_exported 398 * needs to be set to 0. 399 * 400 * vp1 is a node from caller's list 401 * vp2 is node from exportinfo's list 402 * 403 * Only 1 node in the caller's list 404 * will have vis_exported set to 1, 405 * and it corresponds to the obj being 406 * unshared. It should always be the 407 * last element of the caller's list. 408 */ 409 if (vp1->vis_exported && 410 vp2->vis_exported) { 411 vp2->vis_exported = 0; 412 } 413 } 414 415 break; 416 } 417 418 prev = vp2; 419 } 420 } 421 422 free_visible(vis_head); 423 } 424 425 /* 426 * This function checks the path to a new export to 427 * check whether all the pathname components are 428 * exported. It works by climbing the file tree one 429 * component at a time via "..", crossing mountpoints 430 * if necessary until an export entry is found, or the 431 * system root is reached. 432 * 433 * If an unexported mountpoint is found, then 434 * a new pseudo export is added and the pathname from 435 * the mountpoint down to the export is added to the 436 * visible list for the new pseudo export. If an existing 437 * pseudo export is found, then the pathname is added 438 * to its visible list. 439 * 440 * Note that there's some tests for exportdir. 441 * The exportinfo entry that's passed as a parameter 442 * is that of the real export and exportdir is set 443 * for this case. 444 * 445 * Here is an example of a possible setup: 446 * 447 * () - a new fs; fs mount point 448 * EXPORT - a real exported node 449 * PSEUDO - a pseudo node 450 * vis - visible list 451 * f# - security flavor# 452 * (f#) - security flavor# propagated from its decendents 453 * "" - covered vnode 454 * 455 * 456 * / 457 * | 458 * (a) PSEUDO (f1,f2) 459 * | vis: b,b,"c","n" 460 * | 461 * b 462 * ---------|------------------ 463 * | | 464 * (c) EXPORT,f1(f2) (n) PSEUDO (f1,f2) 465 * | vis: "e","d" | vis: m,m,,p,q,"o" 466 * | | 467 * ------------------ ------------------- 468 * | | | | | 469 * (d) (e) f m EXPORT,f1(f2) p 470 * EXPORT EXPORT | | 471 * f1 f2 | | 472 * | | | 473 * j (o) EXPORT,f2 q EXPORT f2 474 * 475 */ 476 int 477 treeclimb_export(struct exportinfo *exip) 478 { 479 vnode_t *dvp, *vp; 480 fid_t fid; 481 int error; 482 int exportdir; 483 struct exportinfo *exi = NULL; 484 struct exp_visible *visp; 485 struct exp_visible *vis_head = NULL; 486 struct vattr va; 487 488 ASSERT(RW_WRITE_HELD(&exported_lock)); 489 490 vp = exip->exi_vp; 491 VN_HOLD(vp); 492 exportdir = 1; 493 494 for (;;) { 495 496 bzero(&fid, sizeof (fid)); 497 fid.fid_len = MAXFIDSZ; 498 error = vop_fid_pseudo(vp, &fid); 499 if (error) 500 break; 501 502 if (! exportdir) { 503 /* 504 * Check if this exportroot is a VROOT dir. If so, 505 * then attach the pseudonodes. If not, then 506 * continue .. traversal until we hit a VROOT 507 * export (pseudo or real). 508 */ 509 exi = checkexport4(&vp->v_vfsp->vfs_fsid, &fid, vp); 510 if (exi != NULL && vp->v_flag & VROOT) { 511 /* 512 * Found an export info 513 * 514 * Extend the list of visible 515 * directories whether it's a pseudo 516 * or a real export. 517 */ 518 more_visible(exi, vis_head); 519 vis_head = NULL; 520 break; /* and climb no further */ 521 } 522 } 523 524 /* 525 * If at the root of the filesystem, need 526 * to traverse across the mountpoint 527 * and continue the climb on the mounted-on 528 * filesystem. 529 */ 530 if (vp->v_flag & VROOT) { 531 532 if (! exportdir) { 533 /* 534 * Found the root directory of a filesystem 535 * that isn't exported. Need to export 536 * this as a pseudo export so that an NFS v4 537 * client can do lookups in it. 538 */ 539 error = pseudo_exportfs(vp, vis_head, NULL); 540 if (error) 541 break; 542 vis_head = NULL; 543 } 544 545 if (VN_CMP(vp, rootdir)) { 546 /* at system root */ 547 break; 548 } 549 550 vp = untraverse(vp); 551 exportdir = 0; 552 continue; 553 } 554 555 /* 556 * Do a getattr to obtain the nodeid (inode num) 557 * for this vnode. 558 */ 559 va.va_mask = AT_NODEID; 560 error = VOP_GETATTR(vp, &va, 0, CRED()); 561 if (error) 562 break; 563 564 /* 565 * Add this directory fid to visible list 566 */ 567 visp = kmem_alloc(sizeof (*visp), KM_SLEEP); 568 VN_HOLD(vp); 569 visp->vis_vp = vp; 570 visp->vis_fid = fid; /* structure copy */ 571 visp->vis_ino = va.va_nodeid; 572 visp->vis_count = 1; 573 visp->vis_exported = exportdir; 574 visp->vis_secinfo = NULL; 575 visp->vis_seccnt = 0; 576 visp->vis_next = vis_head; 577 vis_head = visp; 578 579 /* 580 * Now, do a ".." to find parent dir of vp. 581 */ 582 error = VOP_LOOKUP(vp, "..", &dvp, NULL, 0, NULL, CRED()); 583 584 if (error == ENOTDIR && exportdir) { 585 dvp = exip->exi_dvp; 586 ASSERT(dvp != NULL); 587 VN_HOLD(dvp); 588 error = 0; 589 } 590 591 if (error) 592 break; 593 594 exportdir = 0; 595 VN_RELE(vp); 596 vp = dvp; 597 } 598 599 VN_RELE(vp); 600 return (error); 601 } 602 603 /* 604 * Walk up the tree looking for pseudo export entries. 605 * 606 * If a pseudo export is found, remove the path we've 607 * climbed from its visible list. If the visible list 608 * still has entries after the removal, then we can stop. 609 * If it becomes null, then remove the pseudo export entry 610 * and carry on up the tree to see if there's any more. 611 */ 612 int 613 treeclimb_unexport(struct exportinfo *exip) 614 { 615 vnode_t *dvp, *vp; 616 fid_t fid; 617 int error = 0; 618 int exportdir; 619 struct exportinfo *exi = NULL; 620 struct exp_visible *vis_head = NULL, *visp; 621 622 ASSERT(RW_WRITE_HELD(&exported_lock)); 623 624 exportdir = 1; 625 vp = exip->exi_vp; 626 VN_HOLD(vp); 627 628 for (;;) { 629 630 bzero(&fid, sizeof (fid)); 631 fid.fid_len = MAXFIDSZ; 632 error = vop_fid_pseudo(vp, &fid); 633 if (error) 634 break; 635 636 if (! exportdir) { 637 638 /* 639 * We need to use checkexport4() here because it 640 * doesn't acquire exported_lock and it doesn't 641 * manipulate exi_count. 642 * 643 * Remove directories from the visible 644 * list that are unique to the path 645 * for this export. (Only VROOT exportinfos 646 * have can have visible entries). 647 */ 648 exi = checkexport4(&vp->v_vfsp->vfs_fsid, &fid, vp); 649 if (exi != NULL && (vp->v_flag & VROOT)) { 650 651 less_visible(exi, vis_head); 652 vis_head = NULL; 653 654 /* 655 * If the visible list has entries 656 * or if it's a real export, then 657 * there's no need to keep climbing. 658 */ 659 if (exi->exi_visible || ! PSEUDO(exi)) 660 break; 661 662 /* 663 * Otherwise, we have a pseudo export 664 * with an empty list (no exports below 665 * it) so we must remove and continue 666 * the climb to remove its name from 667 * the parent export. 668 */ 669 error = export_unlink(&vp->v_vfsp->vfs_fsid, 670 &fid, vp, NULL); 671 if (error) 672 break; 673 674 exi_rele(exi); 675 } 676 } 677 678 /* 679 * If at the root of the filesystem, need 680 * to traverse across the mountpoint 681 * and continue the climb on the mounted-on 682 * filesystem. 683 */ 684 if (vp->v_flag & VROOT) { 685 if (VN_CMP(vp, rootdir)) { 686 /* at system root */ 687 break; 688 } 689 vp = untraverse(vp); 690 exportdir = 0; 691 continue; 692 } 693 694 /* 695 * Add this directory fid to path list 696 */ 697 visp = kmem_alloc(sizeof (*visp), KM_SLEEP); 698 VN_HOLD(vp); 699 visp->vis_vp = vp; 700 visp->vis_fid = fid; /* structure copy */ 701 visp->vis_ino = 0; 702 visp->vis_count = 1; 703 visp->vis_exported = exportdir; 704 visp->vis_secinfo = NULL; 705 visp->vis_seccnt = 0; 706 visp->vis_next = vis_head; 707 vis_head = visp; 708 709 /* 710 * Do a ".." to find parent dir of vp. 711 */ 712 error = VOP_LOOKUP(vp, "..", &dvp, NULL, 0, NULL, CRED()); 713 714 if (error == ENOTDIR && exportdir) { 715 dvp = exip->exi_dvp; 716 ASSERT(dvp != NULL); 717 VN_HOLD(dvp); 718 error = 0; 719 } 720 if (error) 721 break; 722 723 exportdir = 0; 724 VN_RELE(vp); 725 vp = dvp; 726 } 727 728 VN_RELE(vp); 729 return (error); 730 } 731 732 733 /* 734 * Traverse backward across mountpoint from the 735 * root vnode of a filesystem to its mounted-on 736 * vnode. 737 */ 738 vnode_t * 739 untraverse(vnode_t *vp) 740 { 741 vnode_t *tvp, *nextvp; 742 743 tvp = vp; 744 for (;;) { 745 if (! (tvp->v_flag & VROOT)) 746 break; 747 748 /* lock vfs to prevent unmount of this vfs */ 749 vfs_lock_wait(tvp->v_vfsp); 750 751 if ((nextvp = tvp->v_vfsp->vfs_vnodecovered) == NULL) { 752 vfs_unlock(tvp->v_vfsp); 753 break; 754 } 755 756 /* 757 * Hold nextvp to prevent unmount. After unlock vfs and 758 * rele tvp, any number of overlays could be unmounted. 759 * Putting a hold on vfs_vnodecovered will only allow 760 * tvp's vfs to be unmounted. Of course if caller placed 761 * extra hold on vp before calling untraverse, the following 762 * hold would not be needed. Since prev actions of caller 763 * are unknown, we need to hold here just to be safe. 764 */ 765 VN_HOLD(nextvp); 766 vfs_unlock(tvp->v_vfsp); 767 VN_RELE(tvp); 768 tvp = nextvp; 769 } 770 771 return (tvp); 772 } 773 774 /* 775 * Given an exportinfo, climb up to find the exportinfo for the VROOT 776 * of the filesystem. 777 * 778 * e.g. / 779 * | 780 * a (VROOT) pseudo-exportinfo 781 * | 782 * b 783 * | 784 * c #share /a/b/c 785 * | 786 * d 787 * 788 * where c is in the same filesystem as a. 789 * So, get_root_export(*exportinfo_for_c) returns exportinfo_for_a 790 * 791 * If d is shared, then c will be put into a's visible list. 792 * Note: visible list is per filesystem and is attached to the 793 * VROOT exportinfo. 794 */ 795 struct exportinfo * 796 get_root_export(struct exportinfo *exip) 797 { 798 vnode_t *dvp, *vp; 799 fid_t fid; 800 struct exportinfo *exi = exip; 801 int error; 802 803 vp = exi->exi_vp; 804 VN_HOLD(vp); 805 806 for (;;) { 807 808 if (vp->v_flag & VROOT) { 809 ASSERT(exi != NULL); 810 break; 811 } 812 813 /* 814 * Now, do a ".." to find parent dir of vp. 815 */ 816 error = VOP_LOOKUP(vp, "..", &dvp, NULL, 0, NULL, CRED()); 817 818 if (error) { 819 exi = NULL; 820 break; 821 } 822 823 VN_RELE(vp); 824 vp = dvp; 825 826 bzero(&fid, sizeof (fid)); 827 fid.fid_len = MAXFIDSZ; 828 error = vop_fid_pseudo(vp, &fid); 829 if (error) { 830 exi = NULL; 831 break; 832 } 833 834 exi = checkexport4(&vp->v_vfsp->vfs_fsid, &fid, vp); 835 } 836 837 VN_RELE(vp); 838 return (exi); 839 } 840 841 /* 842 * Return true if the supplied vnode has a sub-directory exported. 843 */ 844 int 845 has_visible(struct exportinfo *exi, vnode_t *vp) 846 { 847 struct exp_visible *visp; 848 fid_t fid; 849 bool_t vp_is_exported; 850 851 vp_is_exported = VN_CMP(vp, exi->exi_vp); 852 853 /* 854 * An exported root vnode has a sub-dir shared if it has a visible list. 855 * i.e. if it does not have a visible list, then there is no node in 856 * this filesystem leads to any other shared node. 857 */ 858 if (vp_is_exported && (vp->v_flag & VROOT)) 859 return (exi->exi_visible ? 1 : 0); 860 861 /* 862 * Only the exportinfo of a fs root node may have a visible list. 863 * Either it is a pseudo root node, or a real exported root node. 864 */ 865 if ((exi = get_root_export(exi)) == NULL) { 866 return (0); 867 } 868 869 if (!exi->exi_visible) 870 return (0); 871 872 /* Get the fid of the vnode */ 873 bzero(&fid, sizeof (fid)); 874 fid.fid_len = MAXFIDSZ; 875 if (vop_fid_pseudo(vp, &fid) != 0) { 876 return (0); 877 } 878 879 /* 880 * See if vp is in the visible list of the root node exportinfo. 881 */ 882 for (visp = exi->exi_visible; visp; visp = visp->vis_next) { 883 if (EQFID(&fid, &visp->vis_fid)) { 884 /* 885 * If vp is an exported non-root node with only 1 path 886 * count (for itself), it indicates no sub-dir shared 887 * using this vp as a path. 888 */ 889 if (vp_is_exported && visp->vis_count < 2) 890 break; 891 892 return (1); 893 } 894 } 895 896 return (0); 897 } 898 899 /* 900 * Returns true if the supplied vnode is visible 901 * in this export. If vnode is visible, return 902 * vis_exported in expseudo. 903 */ 904 int 905 nfs_visible(struct exportinfo *exi, vnode_t *vp, int *expseudo) 906 { 907 struct exp_visible *visp; 908 fid_t fid; 909 910 /* 911 * First check to see if vp is export root. 912 * 913 * A pseudo export root can never be exported 914 * (it would be a real export then); however, 915 * it is always visible. If a pseudo root object 916 * was exported by server admin, then the entire 917 * pseudo exportinfo (and all visible entries) would 918 * be destroyed. A pseudo exportinfo only exists 919 * to provide access to real (descendant) export(s). 920 * 921 * Previously, rootdir was special cased here; however, 922 * the export root special case handles the rootdir 923 * case also. 924 */ 925 if (VN_CMP(vp, exi->exi_vp)) { 926 *expseudo = 0; 927 return (1); 928 } 929 930 /* 931 * Only a PSEUDO node has a visible list or an exported VROOT 932 * node may have a visible list. 933 */ 934 if (! PSEUDO(exi) && (exi = get_root_export(exi)) == NULL) { 935 *expseudo = 0; 936 return (0); 937 } 938 939 /* Get the fid of the vnode */ 940 941 bzero(&fid, sizeof (fid)); 942 fid.fid_len = MAXFIDSZ; 943 if (vop_fid_pseudo(vp, &fid) != 0) { 944 *expseudo = 0; 945 return (0); 946 } 947 948 /* 949 * We can't trust VN_CMP() above because of LOFS. 950 * Even though VOP_CMP will do the right thing for LOFS 951 * objects, VN_CMP will short circuit out early when the 952 * vnode ops ptrs are different. Just in case we're dealing 953 * with LOFS, compare exi_fid/fsid here. 954 * 955 * expseudo is not set because this is not an export 956 */ 957 if (EQFID(&exi->exi_fid, &fid) && 958 EQFSID(&exi->exi_fsid, &vp->v_vfsp->vfs_fsid)) { 959 *expseudo = 0; 960 return (1); 961 } 962 963 964 /* See if it matches any fid in the visible list */ 965 966 for (visp = exi->exi_visible; visp; visp = visp->vis_next) { 967 if (EQFID(&fid, &visp->vis_fid)) { 968 *expseudo = visp->vis_exported; 969 return (1); 970 } 971 } 972 973 *expseudo = 0; 974 975 return (0); 976 } 977 978 /* 979 * Returns true if the supplied vnode is the 980 * directory of an export point. 981 */ 982 int 983 nfs_exported(struct exportinfo *exi, vnode_t *vp) 984 { 985 struct exp_visible *visp; 986 fid_t fid; 987 988 /* 989 * First check to see if vp is the export root 990 * This check required for the case of lookup .. 991 * where .. is a V_ROOT vnode and a pseudo exportroot. 992 * Pseudo export root objects do not have an entry 993 * in the visible list even though every V_ROOT 994 * pseudonode is visible. It is safe to compare 995 * vp here because pseudo_exportfs put a hold on 996 * it when exi_vp was initialized. 997 * 998 * Note: VN_CMP() won't match for LOFS shares, but they're 999 * handled below w/EQFID/EQFSID. 1000 */ 1001 if (VN_CMP(vp, exi->exi_vp)) 1002 return (1); 1003 1004 /* Get the fid of the vnode */ 1005 1006 bzero(&fid, sizeof (fid)); 1007 fid.fid_len = MAXFIDSZ; 1008 if (vop_fid_pseudo(vp, &fid) != 0) 1009 return (0); 1010 1011 if (EQFID(&fid, &exi->exi_fid) && 1012 EQFSID(&vp->v_vfsp->vfs_fsid, &exi->exi_fsid)) { 1013 return (1); 1014 } 1015 1016 /* See if it matches any fid in the visible list */ 1017 1018 for (visp = exi->exi_visible; visp; visp = visp->vis_next) { 1019 if (EQFID(&fid, &visp->vis_fid)) 1020 return (visp->vis_exported); 1021 } 1022 1023 return (0); 1024 } 1025 1026 /* 1027 * Returns true if the supplied inode is visible 1028 * in this export. This function is used by 1029 * readdir which uses inode numbers from the 1030 * directory. 1031 * 1032 * NOTE: this code does not match inode number for ".", 1033 * but it isn't required because NFS4 server rddir 1034 * skips . and .. entries. 1035 */ 1036 int 1037 nfs_visible_inode(struct exportinfo *exi, ino64_t ino, int *expseudo) 1038 { 1039 struct exp_visible *visp; 1040 1041 /* 1042 * Only a PSEUDO node has a visible list or an exported VROOT 1043 * node may have a visible list. 1044 */ 1045 if (! PSEUDO(exi) && (exi = get_root_export(exi)) == NULL) { 1046 *expseudo = 0; 1047 return (0); 1048 } 1049 1050 for (visp = exi->exi_visible; visp; visp = visp->vis_next) 1051 if ((u_longlong_t)ino == visp->vis_ino) { 1052 *expseudo = visp->vis_exported; 1053 return (1); 1054 } 1055 1056 *expseudo = 0; 1057 return (0); 1058 } 1059