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, NULL); 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(), NULL); 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 descendents 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(), NULL); 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 NULL, NULL, NULL); 584 585 if (error == ENOTDIR && exportdir) { 586 dvp = exip->exi_dvp; 587 ASSERT(dvp != NULL); 588 VN_HOLD(dvp); 589 error = 0; 590 } 591 592 if (error) 593 break; 594 595 exportdir = 0; 596 VN_RELE(vp); 597 vp = dvp; 598 } 599 600 VN_RELE(vp); 601 return (error); 602 } 603 604 /* 605 * Walk up the tree looking for pseudo export entries. 606 * 607 * If a pseudo export is found, remove the path we've 608 * climbed from its visible list. If the visible list 609 * still has entries after the removal, then we can stop. 610 * If it becomes null, then remove the pseudo export entry 611 * and carry on up the tree to see if there's any more. 612 */ 613 int 614 treeclimb_unexport(struct exportinfo *exip) 615 { 616 vnode_t *dvp, *vp; 617 fid_t fid; 618 int error = 0; 619 int exportdir; 620 struct exportinfo *exi = NULL; 621 struct exp_visible *vis_head = NULL, *visp; 622 623 ASSERT(RW_WRITE_HELD(&exported_lock)); 624 625 exportdir = 1; 626 vp = exip->exi_vp; 627 VN_HOLD(vp); 628 629 for (;;) { 630 631 bzero(&fid, sizeof (fid)); 632 fid.fid_len = MAXFIDSZ; 633 error = vop_fid_pseudo(vp, &fid); 634 if (error) 635 break; 636 637 if (! exportdir) { 638 639 /* 640 * We need to use checkexport4() here because it 641 * doesn't acquire exported_lock and it doesn't 642 * manipulate exi_count. 643 * 644 * Remove directories from the visible 645 * list that are unique to the path 646 * for this export. (Only VROOT exportinfos 647 * have can have visible entries). 648 */ 649 exi = checkexport4(&vp->v_vfsp->vfs_fsid, &fid, vp); 650 if (exi != NULL && (vp->v_flag & VROOT)) { 651 652 less_visible(exi, vis_head); 653 vis_head = NULL; 654 655 /* 656 * If the visible list has entries 657 * or if it's a real export, then 658 * there's no need to keep climbing. 659 */ 660 if (exi->exi_visible || ! PSEUDO(exi)) 661 break; 662 663 /* 664 * Otherwise, we have a pseudo export 665 * with an empty list (no exports below 666 * it) so we must remove and continue 667 * the climb to remove its name from 668 * the parent export. 669 */ 670 error = export_unlink(&vp->v_vfsp->vfs_fsid, 671 &fid, vp, NULL); 672 if (error) 673 break; 674 675 exi_rele(exi); 676 } 677 } 678 679 /* 680 * If at the root of the filesystem, need 681 * to traverse across the mountpoint 682 * and continue the climb on the mounted-on 683 * filesystem. 684 */ 685 if (vp->v_flag & VROOT) { 686 if (VN_CMP(vp, rootdir)) { 687 /* at system root */ 688 break; 689 } 690 vp = untraverse(vp); 691 exportdir = 0; 692 continue; 693 } 694 695 /* 696 * Add this directory fid to path list 697 */ 698 visp = kmem_alloc(sizeof (*visp), KM_SLEEP); 699 VN_HOLD(vp); 700 visp->vis_vp = vp; 701 visp->vis_fid = fid; /* structure copy */ 702 visp->vis_ino = 0; 703 visp->vis_count = 1; 704 visp->vis_exported = exportdir; 705 visp->vis_secinfo = NULL; 706 visp->vis_seccnt = 0; 707 visp->vis_next = vis_head; 708 vis_head = visp; 709 710 /* 711 * Do a ".." to find parent dir of vp. 712 */ 713 error = VOP_LOOKUP(vp, "..", &dvp, NULL, 0, NULL, CRED(), 714 NULL, NULL, NULL); 715 716 if (error == ENOTDIR && exportdir) { 717 dvp = exip->exi_dvp; 718 ASSERT(dvp != NULL); 719 VN_HOLD(dvp); 720 error = 0; 721 } 722 if (error) 723 break; 724 725 exportdir = 0; 726 VN_RELE(vp); 727 vp = dvp; 728 } 729 730 VN_RELE(vp); 731 return (error); 732 } 733 734 735 /* 736 * Traverse backward across mountpoint from the 737 * root vnode of a filesystem to its mounted-on 738 * vnode. 739 */ 740 vnode_t * 741 untraverse(vnode_t *vp) 742 { 743 vnode_t *tvp, *nextvp; 744 745 tvp = vp; 746 for (;;) { 747 if (! (tvp->v_flag & VROOT)) 748 break; 749 750 /* lock vfs to prevent unmount of this vfs */ 751 vfs_lock_wait(tvp->v_vfsp); 752 753 if ((nextvp = tvp->v_vfsp->vfs_vnodecovered) == NULL) { 754 vfs_unlock(tvp->v_vfsp); 755 break; 756 } 757 758 /* 759 * Hold nextvp to prevent unmount. After unlock vfs and 760 * rele tvp, any number of overlays could be unmounted. 761 * Putting a hold on vfs_vnodecovered will only allow 762 * tvp's vfs to be unmounted. Of course if caller placed 763 * extra hold on vp before calling untraverse, the following 764 * hold would not be needed. Since prev actions of caller 765 * are unknown, we need to hold here just to be safe. 766 */ 767 VN_HOLD(nextvp); 768 vfs_unlock(tvp->v_vfsp); 769 VN_RELE(tvp); 770 tvp = nextvp; 771 } 772 773 return (tvp); 774 } 775 776 /* 777 * Given an exportinfo, climb up to find the exportinfo for the VROOT 778 * of the filesystem. 779 * 780 * e.g. / 781 * | 782 * a (VROOT) pseudo-exportinfo 783 * | 784 * b 785 * | 786 * c #share /a/b/c 787 * | 788 * d 789 * 790 * where c is in the same filesystem as a. 791 * So, get_root_export(*exportinfo_for_c) returns exportinfo_for_a 792 * 793 * If d is shared, then c will be put into a's visible list. 794 * Note: visible list is per filesystem and is attached to the 795 * VROOT exportinfo. 796 */ 797 struct exportinfo * 798 get_root_export(struct exportinfo *exip) 799 { 800 vnode_t *dvp, *vp; 801 fid_t fid; 802 struct exportinfo *exi = exip; 803 int error; 804 805 vp = exi->exi_vp; 806 VN_HOLD(vp); 807 808 for (;;) { 809 810 if (vp->v_flag & VROOT) { 811 ASSERT(exi != NULL); 812 break; 813 } 814 815 /* 816 * Now, do a ".." to find parent dir of vp. 817 */ 818 error = VOP_LOOKUP(vp, "..", &dvp, NULL, 0, NULL, CRED(), 819 NULL, NULL, NULL); 820 821 if (error) { 822 exi = NULL; 823 break; 824 } 825 826 VN_RELE(vp); 827 vp = dvp; 828 829 bzero(&fid, sizeof (fid)); 830 fid.fid_len = MAXFIDSZ; 831 error = vop_fid_pseudo(vp, &fid); 832 if (error) { 833 exi = NULL; 834 break; 835 } 836 837 exi = checkexport4(&vp->v_vfsp->vfs_fsid, &fid, vp); 838 } 839 840 VN_RELE(vp); 841 return (exi); 842 } 843 844 /* 845 * Return true if the supplied vnode has a sub-directory exported. 846 */ 847 int 848 has_visible(struct exportinfo *exi, vnode_t *vp) 849 { 850 struct exp_visible *visp; 851 fid_t fid; 852 bool_t vp_is_exported; 853 854 vp_is_exported = VN_CMP(vp, exi->exi_vp); 855 856 /* 857 * An exported root vnode has a sub-dir shared if it has a visible list. 858 * i.e. if it does not have a visible list, then there is no node in 859 * this filesystem leads to any other shared node. 860 */ 861 if (vp_is_exported && (vp->v_flag & VROOT)) 862 return (exi->exi_visible ? 1 : 0); 863 864 /* 865 * Only the exportinfo of a fs root node may have a visible list. 866 * Either it is a pseudo root node, or a real exported root node. 867 */ 868 if ((exi = get_root_export(exi)) == NULL) { 869 return (0); 870 } 871 872 if (!exi->exi_visible) 873 return (0); 874 875 /* Get the fid of the vnode */ 876 bzero(&fid, sizeof (fid)); 877 fid.fid_len = MAXFIDSZ; 878 if (vop_fid_pseudo(vp, &fid) != 0) { 879 return (0); 880 } 881 882 /* 883 * See if vp is in the visible list of the root node exportinfo. 884 */ 885 for (visp = exi->exi_visible; visp; visp = visp->vis_next) { 886 if (EQFID(&fid, &visp->vis_fid)) { 887 /* 888 * If vp is an exported non-root node with only 1 path 889 * count (for itself), it indicates no sub-dir shared 890 * using this vp as a path. 891 */ 892 if (vp_is_exported && visp->vis_count < 2) 893 break; 894 895 return (1); 896 } 897 } 898 899 return (0); 900 } 901 902 /* 903 * Returns true if the supplied vnode is visible 904 * in this export. If vnode is visible, return 905 * vis_exported in expseudo. 906 */ 907 int 908 nfs_visible(struct exportinfo *exi, vnode_t *vp, int *expseudo) 909 { 910 struct exp_visible *visp; 911 fid_t fid; 912 913 /* 914 * First check to see if vp is export root. 915 * 916 * A pseudo export root can never be exported 917 * (it would be a real export then); however, 918 * it is always visible. If a pseudo root object 919 * was exported by server admin, then the entire 920 * pseudo exportinfo (and all visible entries) would 921 * be destroyed. A pseudo exportinfo only exists 922 * to provide access to real (descendant) export(s). 923 * 924 * Previously, rootdir was special cased here; however, 925 * the export root special case handles the rootdir 926 * case also. 927 */ 928 if (VN_CMP(vp, exi->exi_vp)) { 929 *expseudo = 0; 930 return (1); 931 } 932 933 /* 934 * Only a PSEUDO node has a visible list or an exported VROOT 935 * node may have a visible list. 936 */ 937 if (! PSEUDO(exi) && (exi = get_root_export(exi)) == NULL) { 938 *expseudo = 0; 939 return (0); 940 } 941 942 /* Get the fid of the vnode */ 943 944 bzero(&fid, sizeof (fid)); 945 fid.fid_len = MAXFIDSZ; 946 if (vop_fid_pseudo(vp, &fid) != 0) { 947 *expseudo = 0; 948 return (0); 949 } 950 951 /* 952 * We can't trust VN_CMP() above because of LOFS. 953 * Even though VOP_CMP will do the right thing for LOFS 954 * objects, VN_CMP will short circuit out early when the 955 * vnode ops ptrs are different. Just in case we're dealing 956 * with LOFS, compare exi_fid/fsid here. 957 * 958 * expseudo is not set because this is not an export 959 */ 960 if (EQFID(&exi->exi_fid, &fid) && 961 EQFSID(&exi->exi_fsid, &vp->v_vfsp->vfs_fsid)) { 962 *expseudo = 0; 963 return (1); 964 } 965 966 967 /* See if it matches any fid in the visible list */ 968 969 for (visp = exi->exi_visible; visp; visp = visp->vis_next) { 970 if (EQFID(&fid, &visp->vis_fid)) { 971 *expseudo = visp->vis_exported; 972 return (1); 973 } 974 } 975 976 *expseudo = 0; 977 978 return (0); 979 } 980 981 /* 982 * Returns true if the supplied vnode is the 983 * directory of an export point. 984 */ 985 int 986 nfs_exported(struct exportinfo *exi, vnode_t *vp) 987 { 988 struct exp_visible *visp; 989 fid_t fid; 990 991 /* 992 * First check to see if vp is the export root 993 * This check required for the case of lookup .. 994 * where .. is a V_ROOT vnode and a pseudo exportroot. 995 * Pseudo export root objects do not have an entry 996 * in the visible list even though every V_ROOT 997 * pseudonode is visible. It is safe to compare 998 * vp here because pseudo_exportfs put a hold on 999 * it when exi_vp was initialized. 1000 * 1001 * Note: VN_CMP() won't match for LOFS shares, but they're 1002 * handled below w/EQFID/EQFSID. 1003 */ 1004 if (VN_CMP(vp, exi->exi_vp)) 1005 return (1); 1006 1007 /* Get the fid of the vnode */ 1008 1009 bzero(&fid, sizeof (fid)); 1010 fid.fid_len = MAXFIDSZ; 1011 if (vop_fid_pseudo(vp, &fid) != 0) 1012 return (0); 1013 1014 if (EQFID(&fid, &exi->exi_fid) && 1015 EQFSID(&vp->v_vfsp->vfs_fsid, &exi->exi_fsid)) { 1016 return (1); 1017 } 1018 1019 /* See if it matches any fid in the visible list */ 1020 1021 for (visp = exi->exi_visible; visp; visp = visp->vis_next) { 1022 if (EQFID(&fid, &visp->vis_fid)) 1023 return (visp->vis_exported); 1024 } 1025 1026 return (0); 1027 } 1028 1029 /* 1030 * Returns true if the supplied inode is visible 1031 * in this export. This function is used by 1032 * readdir which uses inode numbers from the 1033 * directory. 1034 * 1035 * NOTE: this code does not match inode number for ".", 1036 * but it isn't required because NFS4 server rddir 1037 * skips . and .. entries. 1038 */ 1039 int 1040 nfs_visible_inode(struct exportinfo *exi, ino64_t ino, int *expseudo) 1041 { 1042 struct exp_visible *visp; 1043 1044 /* 1045 * Only a PSEUDO node has a visible list or an exported VROOT 1046 * node may have a visible list. 1047 */ 1048 if (! PSEUDO(exi) && (exi = get_root_export(exi)) == NULL) { 1049 *expseudo = 0; 1050 return (0); 1051 } 1052 1053 for (visp = exi->exi_visible; visp; visp = visp->vis_next) 1054 if ((u_longlong_t)ino == visp->vis_ino) { 1055 *expseudo = visp->vis_exported; 1056 return (1); 1057 } 1058 1059 *expseudo = 0; 1060 return (0); 1061 } 1062