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