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 2014 Nexenta Systems, Inc.  All rights reserved.
  24  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  25  * Copyright (c) 2015, Joyent, Inc.
  26  */
  27 
  28 #include <sys/systm.h>
  29 
  30 #include <nfs/nfs.h>
  31 #include <nfs/export.h>
  32 #include <sys/cmn_err.h>
  33 #include <sys/avl.h>
  34 
  35 #define PSEUDOFS_SUFFIX         " (pseudo)"
  36 
  37 /*
  38  * A version of VOP_FID that deals with a remote VOP_FID for nfs.
  39  * If vp is an nfs node, nfs4_fid() returns EREMOTE, nfs3_fid() and nfs_fid()
  40  * returns the filehandle of vp as its fid. When nfs uses fid to set the
  41  * exportinfo filehandle template, a remote nfs filehandle would be too big for
  42  * the fid of the exported directory. This routine remaps the value of the
  43  * attribute va_nodeid of vp to be the fid of vp, so that the fid can fit.
  44  *
 
 
 122 
 123 /*
 124  * Create a pseudo export entry
 125  *
 126  * This is an export entry that's created as the
 127  * side-effect of a "real" export.  As a part of
 128  * a real export, the pathname to the export is
 129  * checked to see if all the directory components
 130  * are accessible via an NFSv4 client, i.e. are
 131  * exported.  If treeclimb_export() finds an unexported
 132  * mountpoint along the path, then it calls this
 133  * function to export it.
 134  *
 135  * This pseudo export differs from a real export in that
 136  * it only allows read-only access.  A "visible" list of
 137  * directories is added to filter lookup and readdir results
 138  * to only contain dirnames which lead to descendant shares.
 139  *
 140  * A visible list has a per-file-system scope.  Any exportinfo
 141  * struct (real or pseudo) can have a visible list as long as
 142  * a) its export root is VROOT
 143  * b) a descendant of the export root is shared
 144  */
 145 struct exportinfo *
 146 pseudo_exportfs(vnode_t *vp, fid_t *fid, struct exp_visible *vis_head,
 147     struct exportdata *exdata)
 148 {
 149         struct exportinfo *exi;
 150         struct exportdata *kex;
 151         fsid_t fsid;
 152         int vpathlen;
 153         int i;
 154 
 155         ASSERT(RW_WRITE_HELD(&exported_lock));
 156 
 157         fsid = vp->v_vfsp->vfs_fsid;
 158         exi = kmem_zalloc(sizeof (*exi), KM_SLEEP);
 159         exi->exi_fsid = fsid;
 160         exi->exi_fid = *fid;
 161         exi->exi_vp = vp;
 162         VN_HOLD(exi->exi_vp);
 163         exi->exi_visible = vis_head;
 164         exi->exi_count = 1;
 165         exi->exi_volatile_dev = (vfssw[vp->v_vfsp->vfs_fstype].vsw_flag &
 166             VSW_VOLATILEDEV) ? 1 : 0;
 167         mutex_init(&exi->exi_lock, NULL, MUTEX_DEFAULT, NULL);
 168 
 169         /*
 170          * Build up the template fhandle
 171          */
 172         exi->exi_fh.fh_fsid = fsid;
 173         ASSERT(exi->exi_fid.fid_len <= sizeof (exi->exi_fh.fh_xdata));
 174         exi->exi_fh.fh_xlen = exi->exi_fid.fid_len;
 175         bcopy(exi->exi_fid.fid_data, exi->exi_fh.fh_xdata,
 176             exi->exi_fid.fid_len);
 177         exi->exi_fh.fh_len = sizeof (exi->exi_fh.fh_data);
 178 
 179         kex = &exi->exi_export;
 180         kex->ex_flags = EX_PSEUDO;
 181 
 182         vpathlen = strlen(vp->v_path);
 183         kex->ex_pathlen = vpathlen + strlen(PSEUDOFS_SUFFIX);
 184         kex->ex_path = kmem_alloc(kex->ex_pathlen + 1, KM_SLEEP);
 
 188         (void) strcpy(kex->ex_path + vpathlen, PSEUDOFS_SUFFIX);
 189 
 190         /* Transfer the secinfo data from exdata to this new pseudo node */
 191         if (exdata)
 192                 srv_secinfo_exp2pseu(&exi->exi_export, exdata);
 193 
 194         /*
 195          * Initialize auth cache and auth cache lock
 196          */
 197         for (i = 0; i < AUTH_TABLESIZE; i++) {
 198                 exi->exi_cache[i] = kmem_alloc(sizeof (avl_tree_t), KM_SLEEP);
 199                 avl_create(exi->exi_cache[i], nfsauth_cache_clnt_compar,
 200                     sizeof (struct auth_cache_clnt),
 201                     offsetof(struct auth_cache_clnt, authc_link));
 202         }
 203         rw_init(&exi->exi_cache_lock, NULL, RW_DEFAULT, NULL);
 204 
 205         /*
 206          * Insert the new entry at the front of the export list
 207          */
 208         export_link(exi);
 209 
 210         return (exi);
 211 }
 212 
 213 /*
 214  * Free a list of visible directories
 215  */
 216 void
 217 free_visible(struct exp_visible *head)
 218 {
 219         struct exp_visible *visp, *next;
 220 
 221         for (visp = head; visp; visp = next) {
 222                 if (visp->vis_vp != NULL)
 223                         VN_RELE(visp->vis_vp);
 224 
 225                 next = visp->vis_next;
 226                 srv_secinfo_list_free(visp->vis_secinfo, visp->vis_seccnt);
 227                 kmem_free(visp, sizeof (*visp));
 228         }
 229 }
 
 264         if (n) {
 265                 tnode->tree_child_first = n;
 266                 n->tree_parent = tnode;
 267         }
 268         if (v) {
 269                 tnode->tree_vis = v;
 270         }
 271         if (e) {
 272                 tnode->tree_exi = e;
 273                 e->exi_tree = tnode;
 274         }
 275         return (tnode);
 276 }
 277 
 278 /*
 279  * Removes node from the tree and frees the treenode struct.
 280  * Does not free structures pointed by tree_exi and tree_vis,
 281  * they should be already freed.
 282  */
 283 static void
 284 tree_remove_node(treenode_t *node)
 285 {
 286         treenode_t *parent = node->tree_parent;
 287         treenode_t *s; /* s for sibling */
 288 
 289         if (parent == NULL) {
 290                 kmem_free(node, sizeof (*node));
 291                 ns_root = NULL;
 292                 return;
 293         }
 294         /* This node is first child */
 295         if (parent->tree_child_first == node) {
 296                 parent->tree_child_first = node->tree_sibling;
 297         /* This node is not first child */
 298         } else {
 299                 s = parent->tree_child_first;
 300                 while (s->tree_sibling != node)
 301                         s = s->tree_sibling;
 302                 s->tree_sibling = s->tree_sibling->tree_sibling;
 303         }
 304         kmem_free(node, sizeof (*node));
 305 }
 306 
 307 /*
 308  * When we export a new directory we need to add a new
 309  * path segment through the pseudofs to reach the new
 310  * directory. This new path is reflected in a list of
 311  * directories added to the "visible" list.
 
 420  * tree_head......current head of the NEW treenode chain, in this case it was
 421  *                already moved down to its child - preparation for another loop
 422  *
 423  * What will happen to NEW treenodes N1, N2, N3, N4 in more_visible() later:
 424  *
 425  * N1: is merged - i.e. N1 is kmem_free()d. T0 has a child T1 with the same
 426  *     tree_vis as N1
 427  * N2: is added as a new child of T1
 428  *     Note: not just N2, but the whole chain N2->N3->N4 is added
 429  * N3: not processed separately (it was added together with N2)
 430  *     Even that N3 and T3 have same tree_vis, they are NOT merged, but will
 431  *     become duplicates.
 432  * N4: not processed separately
 433  */
 434 static void
 435 more_visible(struct exportinfo *exi, treenode_t *tree_head)
 436 {
 437         struct exp_visible *vp1, *vp2, *vis_head, *tail, *next;
 438         int found;
 439         treenode_t *child, *curr, *connect_point;
 440 
 441         vis_head = tree_head->tree_vis;
 442         connect_point = exi->exi_tree;
 443 
 444         /*
 445          * If exportinfo doesn't already have a visible
 446          * list just assign the entire supplied list.
 447          */
 448         if (exi->exi_visible == NULL) {
 449                 tree_add_child(connect_point, tree_head);
 450                 exi->exi_visible = vis_head;
 451 
 452                 /* Update the change timestamp */
 453                 tree_update_change(connect_point, &vis_head->vis_change);
 454 
 455                 return;
 456         }
 457 
 458         /* The outer loop traverses the supplied list. */
 459         for (vp1 = vis_head; vp1; vp1 = next) {
 460                 found = 0;
 461                 next = vp1->vis_next;
 462 
 463                 /* The inner loop searches the exportinfo visible list. */
 464                 for (vp2 = exi->exi_visible; vp2; vp2 = vp2->vis_next) {
 465                         tail = vp2;
 466                         if (EQFID(&vp1->vis_fid, &vp2->vis_fid)) {
 467                                 found = 1;
 468                                 vp2->vis_count++;
 469                                 VN_RELE(vp1->vis_vp);
 470                                 /* Transfer vis_exported from vp1 to vp2. */
 471                                 if (vp1->vis_exported && !vp2->vis_exported)
 472                                         vp2->vis_exported = 1;
 473                                 kmem_free(vp1, sizeof (*vp1));
 
 493                  * connect_point for the curr->tree_vis. No need for EQFID.
 494                  */
 495                 child = tree_find_child_by_vis(connect_point, curr->tree_vis);
 496 
 497                 /*
 498                  * Merging cannot be done if a valid child->tree_exi would
 499                  * be overwritten by a new curr->tree_exi.
 500                  */
 501                 if (child &&
 502                     (child->tree_exi == NULL || curr->tree_exi == NULL)) {
 503                         if (curr->tree_exi) { /* Transfer the exportinfo */
 504                                 child->tree_exi = curr->tree_exi;
 505                                 child->tree_exi->exi_tree = child;
 506                         }
 507                         kmem_free(curr, sizeof (treenode_t));
 508                         connect_point = child;
 509                 } else { /* Branching */
 510                         tree_add_child(connect_point, curr);
 511 
 512                         /* Update the change timestamp */
 513                         tree_update_change(connect_point,
 514                             &curr->tree_vis->vis_change);
 515 
 516                         connect_point = NULL;
 517                 }
 518         }
 519 }
 520 
 521 /*
 522  * Remove one visible entry from the pseudo exportfs.
 523  *
 524  * When we unexport a directory, we have to remove path
 525  * components from the visible list in the pseudo exportfs
 526  * entry. The supplied visible contains one fid of one path
 527  * component. The visible list of the export
 528  * is checked against provided visible, matching fid has its
 529  * reference count decremented.  If a reference count drops to
 530  * zero, then it means no paths now use this directory, so its
 531  * fid can be removed from the visible list.
 532  *
 533  * When the last path is removed, the visible list will be null.
 
 610  *  (d)      (e)      f         m EXPORT,f1(f2)    p
 611  *  EXPORT   EXPORT             |                  |
 612  *  f1       f2                 |                  |
 613  *           |                  |                  |
 614  *           j                 (o) EXPORT,f2       q EXPORT f2
 615  *
 616  */
 617 int
 618 treeclimb_export(struct exportinfo *exip)
 619 {
 620         vnode_t *dvp, *vp;
 621         fid_t fid;
 622         int error;
 623         int exportdir;
 624         struct exportinfo *new_exi = exip;
 625         struct exp_visible *visp;
 626         struct exp_visible *vis_head = NULL;
 627         struct vattr va;
 628         treenode_t *tree_head = NULL;
 629         timespec_t now;
 630 
 631         ASSERT(RW_WRITE_HELD(&exported_lock));
 632 
 633         gethrestime(&now);
 634 
 635         vp = exip->exi_vp;
 636         VN_HOLD(vp);
 637         exportdir = 1;
 638 
 639         for (;;) {
 640 
 641                 bzero(&fid, sizeof (fid));
 642                 fid.fid_len = MAXFIDSZ;
 643                 error = vop_fid_pseudo(vp, &fid);
 644                 if (error)
 645                         break;
 646 
 647                 /*
 648                  * The root of the file system needs special handling
 649                  */
 650                 if (vp->v_flag & VROOT) {
 651                         if (! exportdir) {
 652                                 struct exportinfo *exi;
 653 
 654                                 /*
 655                                  * Check if this VROOT dir is already exported.
 656                                  * If so, then attach the pseudonodes.  If not,
 657                                  * then continue .. traversal until we hit a
 658                                  * VROOT export (pseudo or real).
 659                                  */
 660                                 exi = checkexport4(&vp->v_vfsp->vfs_fsid, &fid,
 661                                     vp);
 662                                 if (exi != NULL) {
 663                                         /*
 664                                          * Found an export info
 665                                          *
 666                                          * Extend the list of visible
 667                                          * directories whether it's a pseudo
 668                                          * or a real export.
 669                                          */
 670                                         more_visible(exi, tree_head);
 671                                         break;  /* and climb no further */
 672                                 }
 673 
 674                                 /*
 675                                  * Found the root directory of a filesystem
 676                                  * that isn't exported.  Need to export
 677                                  * this as a pseudo export so that an NFS v4
 678                                  * client can do lookups in it.
 679                                  */
 680                                 new_exi = pseudo_exportfs(vp, &fid, vis_head,
 681                                     NULL);
 682                                 vis_head = NULL;
 683                         }
 684 
 685                         if (VN_CMP(vp, rootdir)) {
 686                                 /* at system root */
 687                                 /*
 688                                  * If sharing "/", new_exi is shared exportinfo
 689                                  * (exip). Otherwise, new_exi is exportinfo
 690                                  * created by pseudo_exportfs() above.
 691                                  */
 692                                 ns_root = tree_prepend_node(tree_head, NULL,
 693                                     new_exi);
 694 
 695                                 /* Update the change timestamp */
 696                                 tree_update_change(ns_root, &now);
 697 
 698                                 break;
 699                         }
 700 
 701                         /*
 702                          * Traverse across the mountpoint and continue the
 703                          * climb on the mounted-on filesystem.
 704                          */
 705                         vp = untraverse(vp);
 706                         exportdir = 0;
 707                         continue;
 708                 }
 709 
 710                 /*
 711                  * Do a getattr to obtain the nodeid (inode num)
 712                  * for this vnode.
 713                  */
 714                 va.va_mask = AT_NODEID;
 715                 error = VOP_GETATTR(vp, &va, 0, CRED(), NULL);
 716                 if (error)
 717                         break;
 718 
 719                 /*
 720                  *  Add this directory fid to visible list
 721                  */
 722                 visp = kmem_alloc(sizeof (*visp), KM_SLEEP);
 723                 VN_HOLD(vp);
 724                 visp->vis_vp = vp;
 725                 visp->vis_fid = fid;         /* structure copy */
 
 771          * 3. VOP_LOOKUP()
 772          * We must free pseudo exportinfos, visibles and treenodes.
 773          * Visibles are referenced from treenode_t::tree_vis and
 774          * exportinfo_t::exi_visible. To avoid double freeing, only
 775          * exi_visible pointer is used, via exi_rele(), for the clean-up.
 776          */
 777         if (error) {
 778                 /* Free unconnected visibles, if there are any. */
 779                 if (vis_head)
 780                         free_visible(vis_head);
 781 
 782                 /* Connect unconnected exportinfo, if there is any. */
 783                 if (new_exi && new_exi != exip)
 784                         tree_head = tree_prepend_node(tree_head, NULL, new_exi);
 785 
 786                 while (tree_head) {
 787                         treenode_t *t2 = tree_head;
 788                         exportinfo_t *e  = tree_head->tree_exi;
 789                         /* exip will be freed in exportfs() */
 790                         if (e && e != exip) {
 791                                 export_unlink(e);
 792                                 exi_rele(e);
 793                         }
 794                         tree_head = tree_head->tree_child_first;
 795                         kmem_free(t2, sizeof (*t2));
 796                 }
 797         }
 798 
 799         return (error);
 800 }
 801 
 802 /*
 803  * Walk up the tree and:
 804  * 1. release pseudo exportinfo if it has no child
 805  * 2. release visible in parent's exportinfo
 806  * 3. delete non-exported leaf nodes from tree
 807  *
 808  * Deleting of nodes will start only if the unshared
 809  * node was a leaf node.
 810  * Deleting of nodes will finish when we reach a node which
 811  * has children or is a real export, then we might still need
 812  * to continue releasing visibles, until we reach VROOT node.
 813  */
 814 void
 815 treeclimb_unexport(struct exportinfo *exip)
 816 {
 817         treenode_t *tnode, *old_nd;
 818         treenode_t *connect_point = NULL;
 819 
 820         ASSERT(RW_WRITE_HELD(&exported_lock));
 821 
 822         tnode = exip->exi_tree;
 823         /*
 824          * The unshared exportinfo was unlinked in unexport().
 825          * Zeroing tree_exi ensures that we will skip it.
 826          */
 827         tnode->tree_exi = NULL;
 828 
 829         if (tnode->tree_vis != NULL) /* system root has tree_vis == NULL */
 830                 tnode->tree_vis->vis_exported = 0;
 831 
 832         while (tnode != NULL) {
 833 
 834                 /* Stop at VROOT node which is exported or has child */
 835                 if (TREE_ROOT(tnode) &&
 836                     (TREE_EXPORTED(tnode) || tnode->tree_child_first != NULL))
 837                         break;
 838 
 839                 /* Release pseudo export if it has no child */
 840                 if (TREE_ROOT(tnode) && !TREE_EXPORTED(tnode) &&
 841                     tnode->tree_child_first == NULL) {
 842                         export_unlink(tnode->tree_exi);
 843                         exi_rele(tnode->tree_exi);
 844                 }
 845 
 846                 /* Release visible in parent's exportinfo */
 847                 if (tnode->tree_vis != NULL)
 848                         less_visible(vis2exi(tnode), tnode->tree_vis);
 849 
 850                 /* Continue with parent */
 851                 old_nd = tnode;
 852                 tnode = tnode->tree_parent;
 853 
 854                 /* Remove itself, if this is a leaf and non-exported node */
 855                 if (old_nd->tree_child_first == NULL &&
 856                     !TREE_EXPORTED(old_nd)) {
 857                         tree_remove_node(old_nd);
 858                         connect_point = tnode;
 859                 }
 860         }
 861 
 862         /* Update the change timestamp */
 863         if (connect_point != NULL)
 864                 tree_update_change(connect_point, NULL);
 865 }
 866 
 867 /*
 868  * Traverse backward across mountpoint from the
 869  * root vnode of a filesystem to its mounted-on
 870  * vnode.
 871  */
 872 vnode_t *
 873 untraverse(vnode_t *vp)
 874 {
 875         vnode_t *tvp, *nextvp;
 876 
 877         tvp = vp;
 878         for (;;) {
 879                 if (! (tvp->v_flag & VROOT))
 880                         break;
 881 
 882                 /* lock vfs to prevent unmount of this vfs */
 883                 vfs_lock_wait(tvp->v_vfsp);
 884 
 885                 if ((nextvp = tvp->v_vfsp->vfs_vnodecovered) == NULL) {
 886                         vfs_unlock(tvp->v_vfsp);
 887                         break;
 888                 }
 889 
 890                 /*
 891                  * Hold nextvp to prevent unmount.  After unlock vfs and
 892                  * rele tvp, any number of overlays could be unmounted.
 893                  * Putting a hold on vfs_vnodecovered will only allow
 894                  * tvp's vfs to be unmounted. Of course if caller placed
 895                  * extra hold on vp before calling untraverse, the following
 896                  * hold would not be needed.  Since prev actions of caller
 897                  * are unknown, we need to hold here just to be safe.
 898                  */
 899                 VN_HOLD(nextvp);
 900                 vfs_unlock(tvp->v_vfsp);
 901                 VN_RELE(tvp);
 902                 tvp = nextvp;
 903         }
 904 
 905         return (tvp);
 906 }
 907 
 908 /*
 909  * Given an exportinfo, climb up to find the exportinfo for the VROOT
 910  * of the filesystem.
 911  *
 912  * e.g.         /
 913  *              |
 914  *              a (VROOT) pseudo-exportinfo
 915  *              |
 916  *              b
 917  *              |
 918  *              c  #share /a/b/c
 919  *              |
 920  *              d
 921  *
 922  * where c is in the same filesystem as a.
 923  * So, get_root_export(*exportinfo_for_c) returns exportinfo_for_a
 924  *
 925  * If d is shared, then c will be put into a's visible list.
 926  * Note: visible list is per filesystem and is attached to the
 927  * VROOT exportinfo.
 928  */
 929 struct exportinfo *
 930 get_root_export(struct exportinfo *exip)
 931 {
 932         treenode_t *tnode = exip->exi_tree;
 933         exportinfo_t *exi = NULL;
 934 
 935         while (tnode) {
 936                 if (TREE_ROOT(tnode)) {
 937                         exi = tnode->tree_exi;
 938                         break;
 939                 }
 940                 tnode = tnode->tree_parent;
 941         }
 942         ASSERT(exi);
 943         return (exi);
 944 }
 945 
 946 /*
 947  * Return true if the supplied vnode has a sub-directory exported.
 948  */
 949 int
 950 has_visible(struct exportinfo *exi, vnode_t *vp)
 951 {
 952         struct exp_visible *visp;
 953         fid_t fid;
 954         bool_t vp_is_exported;
 955 
 956         vp_is_exported = VN_CMP(vp, exi->exi_vp);
 957 
 958         /*
 959          * An exported root vnode has a sub-dir shared if it has a visible list.
 960          * i.e. if it does not have a visible list, then there is no node in
 961          * this filesystem leads to any other shared node.
 962          */
 963         if (vp_is_exported && (vp->v_flag & VROOT))
 964                 return (exi->exi_visible ? 1 : 0);
 965 
 966         /*
 967          * Only the exportinfo of a fs root node may have a visible list.
 968          * Either it is a pseudo root node, or a real exported root node.
 969          */
 970         exi = get_root_export(exi);
 971 
 972         if (!exi->exi_visible)
 973                 return (0);
 974 
 975         /* Get the fid of the vnode */
 976         bzero(&fid, sizeof (fid));
 977         fid.fid_len = MAXFIDSZ;
 978         if (vop_fid_pseudo(vp, &fid) != 0) {
 979                 return (0);
 980         }
 981 
 982         /*
 983          * See if vp is in the visible list of the root node exportinfo.
 984          */
 
1017          * (it would be a real export then); however,
1018          * it is always visible.  If a pseudo root object
1019          * was exported by server admin, then the entire
1020          * pseudo exportinfo (and all visible entries) would
1021          * be destroyed.  A pseudo exportinfo only exists
1022          * to provide access to real (descendant) export(s).
1023          *
1024          * Previously, rootdir was special cased here; however,
1025          * the export root special case handles the rootdir
1026          * case also.
1027          */
1028         if (VN_CMP(vp, exi->exi_vp)) {
1029                 *expseudo = 0;
1030                 return (1);
1031         }
1032 
1033         /*
1034          * Only a PSEUDO node has a visible list or an exported VROOT
1035          * node may have a visible list.
1036          */
1037         if (! PSEUDO(exi))
1038                 exi = get_root_export(exi);
1039 
1040         /* Get the fid of the vnode */
1041 
1042         bzero(&fid, sizeof (fid));
1043         fid.fid_len = MAXFIDSZ;
1044         if (vop_fid_pseudo(vp, &fid) != 0) {
1045                 *expseudo = 0;
1046                 return (0);
1047         }
1048 
1049         /*
1050          * We can't trust VN_CMP() above because of LOFS.
1051          * Even though VOP_CMP will do the right thing for LOFS
1052          * objects, VN_CMP will short circuit out early when the
1053          * vnode ops ptrs are different.  Just in case we're dealing
1054          * with LOFS, compare exi_fid/fsid here.
1055          *
1056          * expseudo is not set because this is not an export
1057          */
 
1125 }
1126 
1127 /*
1128  * Returns true if the supplied inode is visible
1129  * in this export.  This function is used by
1130  * readdir which uses inode numbers from the
1131  * directory.
1132  *
1133  * NOTE: this code does not match inode number for ".",
1134  * but it isn't required because NFS4 server rddir
1135  * skips . and .. entries.
1136  */
1137 int
1138 nfs_visible_inode(struct exportinfo *exi, ino64_t ino,
1139     struct exp_visible **visp)
1140 {
1141         /*
1142          * Only a PSEUDO node has a visible list or an exported VROOT
1143          * node may have a visible list.
1144          */
1145         if (! PSEUDO(exi))
1146                 exi = get_root_export(exi);
1147 
1148         for (*visp = exi->exi_visible; *visp != NULL; *visp = (*visp)->vis_next)
1149                 if ((u_longlong_t)ino == (*visp)->vis_ino) {
1150                         return (1);
1151                 }
1152 
1153         return (0);
1154 }
1155 
1156 /*
1157  * The change attribute value of the root of nfs pseudo namespace.
1158  *
1159  * The ns_root_change is protected by exported_lock because all of the treenode
1160  * operations are protected by exported_lock too.
1161  */
1162 static timespec_t ns_root_change;
1163 
1164 /*
1165  * Get the change attribute from visible and returns TRUE.
1166  * If the change value is not available returns FALSE.
1167  */
1168 bool_t
1169 nfs_visible_change(struct exportinfo *exi, vnode_t *vp, timespec_t *change)
1170 {
1171         struct exp_visible *visp;
1172         fid_t fid;
1173         treenode_t *node;
1174 
1175         /*
1176          * First check to see if vp is export root.
1177          */
1178         if (VN_CMP(vp, exi->exi_vp))
1179                 goto exproot;
1180 
1181         /*
1182          * Only a PSEUDO node has a visible list or an exported VROOT
1183          * node may have a visible list.
1184          */
1185         if (!PSEUDO(exi))
1186                 exi = get_root_export(exi);
1187 
1188         /* Get the fid of the vnode */
1189         bzero(&fid, sizeof (fid));
1190         fid.fid_len = MAXFIDSZ;
1191         if (vop_fid_pseudo(vp, &fid) != 0)
1192                 return (FALSE);
1193 
 
1198          * vnode ops ptrs are different.  Just in case we're dealing
1199          * with LOFS, compare exi_fid/fsid here.
1200          */
1201         if (EQFID(&exi->exi_fid, &fid) &&
1202             EQFSID(&exi->exi_fsid, &vp->v_vfsp->vfs_fsid))
1203                 goto exproot;
1204 
1205         /* See if it matches any fid in the visible list */
1206         for (visp = exi->exi_visible; visp; visp = visp->vis_next) {
1207                 if (EQFID(&fid, &visp->vis_fid)) {
1208                         *change = visp->vis_change;
1209                         return (TRUE);
1210                 }
1211         }
1212 
1213         return (FALSE);
1214 
1215 exproot:
1216         /* The VROOT export have its visible available through treenode */
1217         node = exi->exi_tree;
1218         if (node != ns_root) {
1219                 ASSERT(node->tree_vis != NULL);
1220                 *change = node->tree_vis->vis_change;
1221         } else {
1222                 ASSERT(node->tree_vis == NULL);
1223                 *change = ns_root_change;
1224         }
1225 
1226         return (TRUE);
1227 }
1228 
1229 /*
1230  * Update the change attribute value for a particular treenode.  The change
1231  * attribute value is stored in the visible attached to the treenode, or in the
1232  * ns_root_change.
1233  *
1234  * If the change value is not supplied, the current time is used.
1235  */
1236 void
1237 tree_update_change(treenode_t *tnode, timespec_t *change)
1238 {
1239         timespec_t *vis_change;
1240 
1241         ASSERT(tnode != NULL);
1242         ASSERT((tnode != ns_root && tnode->tree_vis != NULL) ||
1243             (tnode == ns_root && tnode->tree_vis == NULL));
1244 
1245         vis_change = tnode == ns_root ? &ns_root_change
1246             : &tnode->tree_vis->vis_change;
1247 
1248         if (change != NULL)
1249                 *vis_change = *change;
1250         else
1251                 gethrestime(vis_change);
1252 }
 | 
 
 
   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 (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  24  */
  25 
  26 /*
  27  * Copyright 2018 Nexenta Systems, Inc.
  28  * Copyright (c) 2015, Joyent, Inc.
  29  */
  30 
  31 #include <sys/systm.h>
  32 
  33 #include <nfs/nfs.h>
  34 #include <nfs/export.h>
  35 #include <sys/cmn_err.h>
  36 #include <sys/avl.h>
  37 
  38 #define PSEUDOFS_SUFFIX         " (pseudo)"
  39 
  40 /*
  41  * A version of VOP_FID that deals with a remote VOP_FID for nfs.
  42  * If vp is an nfs node, nfs4_fid() returns EREMOTE, nfs3_fid() and nfs_fid()
  43  * returns the filehandle of vp as its fid. When nfs uses fid to set the
  44  * exportinfo filehandle template, a remote nfs filehandle would be too big for
  45  * the fid of the exported directory. This routine remaps the value of the
  46  * attribute va_nodeid of vp to be the fid of vp, so that the fid can fit.
  47  *
 
 
 125 
 126 /*
 127  * Create a pseudo export entry
 128  *
 129  * This is an export entry that's created as the
 130  * side-effect of a "real" export.  As a part of
 131  * a real export, the pathname to the export is
 132  * checked to see if all the directory components
 133  * are accessible via an NFSv4 client, i.e. are
 134  * exported.  If treeclimb_export() finds an unexported
 135  * mountpoint along the path, then it calls this
 136  * function to export it.
 137  *
 138  * This pseudo export differs from a real export in that
 139  * it only allows read-only access.  A "visible" list of
 140  * directories is added to filter lookup and readdir results
 141  * to only contain dirnames which lead to descendant shares.
 142  *
 143  * A visible list has a per-file-system scope.  Any exportinfo
 144  * struct (real or pseudo) can have a visible list as long as
 145  * a) its export root is VROOT, or is the zone's root for in-zone NFS service
 146  * b) a descendant of the export root is shared
 147  */
 148 struct exportinfo *
 149 pseudo_exportfs(nfs_export_t *ne, vnode_t *vp, fid_t *fid,
 150     struct exp_visible *vis_head, struct exportdata *exdata)
 151 {
 152         struct exportinfo *exi;
 153         struct exportdata *kex;
 154         fsid_t fsid;
 155         int vpathlen;
 156         int i;
 157 
 158         ASSERT(RW_WRITE_HELD(&ne->exported_lock));
 159 
 160         fsid = vp->v_vfsp->vfs_fsid;
 161         exi = kmem_zalloc(sizeof (*exi), KM_SLEEP);
 162         exi->exi_fsid = fsid;
 163         exi->exi_fid = *fid;
 164         exi->exi_vp = vp;
 165         VN_HOLD(exi->exi_vp);
 166         exi->exi_visible = vis_head;
 167         exi->exi_count = 1;
 168         exi->exi_zoneid = ne->ne_globals->nfs_zoneid;
 169         exi->exi_volatile_dev = (vfssw[vp->v_vfsp->vfs_fstype].vsw_flag &
 170             VSW_VOLATILEDEV) ? 1 : 0;
 171         mutex_init(&exi->exi_lock, NULL, MUTEX_DEFAULT, NULL);
 172 
 173         /*
 174          * Build up the template fhandle
 175          */
 176         exi->exi_fh.fh_fsid = fsid;
 177         ASSERT(exi->exi_fid.fid_len <= sizeof (exi->exi_fh.fh_xdata));
 178         exi->exi_fh.fh_xlen = exi->exi_fid.fid_len;
 179         bcopy(exi->exi_fid.fid_data, exi->exi_fh.fh_xdata,
 180             exi->exi_fid.fid_len);
 181         exi->exi_fh.fh_len = sizeof (exi->exi_fh.fh_data);
 182 
 183         kex = &exi->exi_export;
 184         kex->ex_flags = EX_PSEUDO;
 185 
 186         vpathlen = strlen(vp->v_path);
 187         kex->ex_pathlen = vpathlen + strlen(PSEUDOFS_SUFFIX);
 188         kex->ex_path = kmem_alloc(kex->ex_pathlen + 1, KM_SLEEP);
 
 192         (void) strcpy(kex->ex_path + vpathlen, PSEUDOFS_SUFFIX);
 193 
 194         /* Transfer the secinfo data from exdata to this new pseudo node */
 195         if (exdata)
 196                 srv_secinfo_exp2pseu(&exi->exi_export, exdata);
 197 
 198         /*
 199          * Initialize auth cache and auth cache lock
 200          */
 201         for (i = 0; i < AUTH_TABLESIZE; i++) {
 202                 exi->exi_cache[i] = kmem_alloc(sizeof (avl_tree_t), KM_SLEEP);
 203                 avl_create(exi->exi_cache[i], nfsauth_cache_clnt_compar,
 204                     sizeof (struct auth_cache_clnt),
 205                     offsetof(struct auth_cache_clnt, authc_link));
 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(ne, exi);
 213 
 214         /*
 215          * Initialize exi_id and exi_kstats
 216          */
 217         mutex_enter(&nfs_exi_id_lock);
 218         exi->exi_id = exi_id_get_next();
 219         avl_add(&exi_id_tree, exi);
 220         mutex_exit(&nfs_exi_id_lock);
 221 
 222         return (exi);
 223 }
 224 
 225 /*
 226  * Free a list of visible directories
 227  */
 228 void
 229 free_visible(struct exp_visible *head)
 230 {
 231         struct exp_visible *visp, *next;
 232 
 233         for (visp = head; visp; visp = next) {
 234                 if (visp->vis_vp != NULL)
 235                         VN_RELE(visp->vis_vp);
 236 
 237                 next = visp->vis_next;
 238                 srv_secinfo_list_free(visp->vis_secinfo, visp->vis_seccnt);
 239                 kmem_free(visp, sizeof (*visp));
 240         }
 241 }
 
 276         if (n) {
 277                 tnode->tree_child_first = n;
 278                 n->tree_parent = tnode;
 279         }
 280         if (v) {
 281                 tnode->tree_vis = v;
 282         }
 283         if (e) {
 284                 tnode->tree_exi = e;
 285                 e->exi_tree = tnode;
 286         }
 287         return (tnode);
 288 }
 289 
 290 /*
 291  * Removes node from the tree and frees the treenode struct.
 292  * Does not free structures pointed by tree_exi and tree_vis,
 293  * they should be already freed.
 294  */
 295 static void
 296 tree_remove_node(nfs_export_t *ne, treenode_t *node)
 297 {
 298         treenode_t *parent = node->tree_parent;
 299         treenode_t *s; /* s for sibling */
 300 
 301         if (parent == NULL) {
 302                 kmem_free(node, sizeof (*node));
 303                 ne->ns_root = NULL;
 304                 return;
 305         }
 306         /* This node is first child */
 307         if (parent->tree_child_first == node) {
 308                 parent->tree_child_first = node->tree_sibling;
 309         /* This node is not first child */
 310         } else {
 311                 s = parent->tree_child_first;
 312                 while (s->tree_sibling != node)
 313                         s = s->tree_sibling;
 314                 s->tree_sibling = s->tree_sibling->tree_sibling;
 315         }
 316         kmem_free(node, sizeof (*node));
 317 }
 318 
 319 /*
 320  * When we export a new directory we need to add a new
 321  * path segment through the pseudofs to reach the new
 322  * directory. This new path is reflected in a list of
 323  * directories added to the "visible" list.
 
 432  * tree_head......current head of the NEW treenode chain, in this case it was
 433  *                already moved down to its child - preparation for another loop
 434  *
 435  * What will happen to NEW treenodes N1, N2, N3, N4 in more_visible() later:
 436  *
 437  * N1: is merged - i.e. N1 is kmem_free()d. T0 has a child T1 with the same
 438  *     tree_vis as N1
 439  * N2: is added as a new child of T1
 440  *     Note: not just N2, but the whole chain N2->N3->N4 is added
 441  * N3: not processed separately (it was added together with N2)
 442  *     Even that N3 and T3 have same tree_vis, they are NOT merged, but will
 443  *     become duplicates.
 444  * N4: not processed separately
 445  */
 446 static void
 447 more_visible(struct exportinfo *exi, treenode_t *tree_head)
 448 {
 449         struct exp_visible *vp1, *vp2, *vis_head, *tail, *next;
 450         int found;
 451         treenode_t *child, *curr, *connect_point;
 452         nfs_export_t *ne = nfs_get_export();
 453 
 454         vis_head = tree_head->tree_vis;
 455         connect_point = exi->exi_tree;
 456 
 457         /*
 458          * If exportinfo doesn't already have a visible
 459          * list just assign the entire supplied list.
 460          */
 461         if (exi->exi_visible == NULL) {
 462                 tree_add_child(connect_point, tree_head);
 463                 exi->exi_visible = vis_head;
 464 
 465                 /* Update the change timestamp */
 466                 tree_update_change(ne, connect_point, &vis_head->vis_change);
 467 
 468                 return;
 469         }
 470 
 471         /* The outer loop traverses the supplied list. */
 472         for (vp1 = vis_head; vp1; vp1 = next) {
 473                 found = 0;
 474                 next = vp1->vis_next;
 475 
 476                 /* The inner loop searches the exportinfo visible list. */
 477                 for (vp2 = exi->exi_visible; vp2; vp2 = vp2->vis_next) {
 478                         tail = vp2;
 479                         if (EQFID(&vp1->vis_fid, &vp2->vis_fid)) {
 480                                 found = 1;
 481                                 vp2->vis_count++;
 482                                 VN_RELE(vp1->vis_vp);
 483                                 /* Transfer vis_exported from vp1 to vp2. */
 484                                 if (vp1->vis_exported && !vp2->vis_exported)
 485                                         vp2->vis_exported = 1;
 486                                 kmem_free(vp1, sizeof (*vp1));
 
 506                  * connect_point for the curr->tree_vis. No need for EQFID.
 507                  */
 508                 child = tree_find_child_by_vis(connect_point, curr->tree_vis);
 509 
 510                 /*
 511                  * Merging cannot be done if a valid child->tree_exi would
 512                  * be overwritten by a new curr->tree_exi.
 513                  */
 514                 if (child &&
 515                     (child->tree_exi == NULL || curr->tree_exi == NULL)) {
 516                         if (curr->tree_exi) { /* Transfer the exportinfo */
 517                                 child->tree_exi = curr->tree_exi;
 518                                 child->tree_exi->exi_tree = child;
 519                         }
 520                         kmem_free(curr, sizeof (treenode_t));
 521                         connect_point = child;
 522                 } else { /* Branching */
 523                         tree_add_child(connect_point, curr);
 524 
 525                         /* Update the change timestamp */
 526                         tree_update_change(ne, connect_point,
 527                             &curr->tree_vis->vis_change);
 528 
 529                         connect_point = NULL;
 530                 }
 531         }
 532 }
 533 
 534 /*
 535  * Remove one visible entry from the pseudo exportfs.
 536  *
 537  * When we unexport a directory, we have to remove path
 538  * components from the visible list in the pseudo exportfs
 539  * entry. The supplied visible contains one fid of one path
 540  * component. The visible list of the export
 541  * is checked against provided visible, matching fid has its
 542  * reference count decremented.  If a reference count drops to
 543  * zero, then it means no paths now use this directory, so its
 544  * fid can be removed from the visible list.
 545  *
 546  * When the last path is removed, the visible list will be null.
 
 623  *  (d)      (e)      f         m EXPORT,f1(f2)    p
 624  *  EXPORT   EXPORT             |                  |
 625  *  f1       f2                 |                  |
 626  *           |                  |                  |
 627  *           j                 (o) EXPORT,f2       q EXPORT f2
 628  *
 629  */
 630 int
 631 treeclimb_export(struct exportinfo *exip)
 632 {
 633         vnode_t *dvp, *vp;
 634         fid_t fid;
 635         int error;
 636         int exportdir;
 637         struct exportinfo *new_exi = exip;
 638         struct exp_visible *visp;
 639         struct exp_visible *vis_head = NULL;
 640         struct vattr va;
 641         treenode_t *tree_head = NULL;
 642         timespec_t now;
 643         nfs_export_t *ne;
 644 
 645         ne = exip->exi_ne;
 646         ASSERT3P(ne, ==, nfs_get_export());     /* curzone reality check */
 647         ASSERT(RW_WRITE_HELD(&ne->exported_lock));
 648 
 649         gethrestime(&now);
 650 
 651         vp = exip->exi_vp;
 652         VN_HOLD(vp);
 653         exportdir = 1;
 654 
 655         for (;;) {
 656 
 657                 bzero(&fid, sizeof (fid));
 658                 fid.fid_len = MAXFIDSZ;
 659                 error = vop_fid_pseudo(vp, &fid);
 660                 if (error)
 661                         break;
 662 
 663                 /* XXX KEBE ASKS DO WE NEED THIS?!? */
 664                 ASSERT3U(exip->exi_zoneid, ==, curzone->zone_id);
 665                 /*
 666                  * The root of the file system, or the zone's root for
 667                  * in-zone NFS service needs special handling
 668                  */
 669                 if (vp->v_flag & VROOT || vp == EXI_TO_ZONEROOTVP(exip)) {
 670                         if (!exportdir) {
 671                                 struct exportinfo *exi;
 672 
 673                                 /*
 674                                  * Check if this VROOT dir is already exported.
 675                                  * If so, then attach the pseudonodes.  If not,
 676                                  * then continue .. traversal until we hit a
 677                                  * VROOT export (pseudo or real).
 678                                  */
 679                                 exi = checkexport4(&vp->v_vfsp->vfs_fsid, &fid,
 680                                     vp);
 681                                 if (exi != NULL) {
 682                                         /*
 683                                          * Found an export info
 684                                          *
 685                                          * Extend the list of visible
 686                                          * directories whether it's a pseudo
 687                                          * or a real export.
 688                                          */
 689                                         more_visible(exi, tree_head);
 690                                         break;  /* and climb no further */
 691                                 }
 692 
 693                                 /*
 694                                  * Found the root directory of a filesystem
 695                                  * that isn't exported.  Need to export
 696                                  * this as a pseudo export so that an NFS v4
 697                                  * client can do lookups in it.
 698                                  */
 699                                 new_exi = pseudo_exportfs(ne, vp, &fid,
 700                                     vis_head, NULL);
 701                                 vis_head = NULL;
 702                         }
 703 
 704                         if (VN_IS_CURZONEROOT(vp)) {
 705                                 /* at system root */
 706                                 /*
 707                                  * If sharing "/", new_exi is shared exportinfo
 708                                  * (exip). Otherwise, new_exi is exportinfo
 709                                  * created by pseudo_exportfs() above.
 710                                  */
 711                                 ne->ns_root = tree_prepend_node(tree_head, NULL,
 712                                     new_exi);
 713 
 714                                 /* Update the change timestamp */
 715                                 tree_update_change(ne, ne->ns_root, &now);
 716 
 717                                 break;
 718                         }
 719 
 720                         /*
 721                          * Traverse across the mountpoint and continue the
 722                          * climb on the mounted-on filesystem.
 723                          */
 724                         vp = untraverse(vp, ne->exi_root->exi_vp);
 725                         exportdir = 0;
 726                         continue;
 727                 }
 728 
 729                 /*
 730                  * Do a getattr to obtain the nodeid (inode num)
 731                  * for this vnode.
 732                  */
 733                 va.va_mask = AT_NODEID;
 734                 error = VOP_GETATTR(vp, &va, 0, CRED(), NULL);
 735                 if (error)
 736                         break;
 737 
 738                 /*
 739                  *  Add this directory fid to visible list
 740                  */
 741                 visp = kmem_alloc(sizeof (*visp), KM_SLEEP);
 742                 VN_HOLD(vp);
 743                 visp->vis_vp = vp;
 744                 visp->vis_fid = fid;         /* structure copy */
 
 790          * 3. VOP_LOOKUP()
 791          * We must free pseudo exportinfos, visibles and treenodes.
 792          * Visibles are referenced from treenode_t::tree_vis and
 793          * exportinfo_t::exi_visible. To avoid double freeing, only
 794          * exi_visible pointer is used, via exi_rele(), for the clean-up.
 795          */
 796         if (error) {
 797                 /* Free unconnected visibles, if there are any. */
 798                 if (vis_head)
 799                         free_visible(vis_head);
 800 
 801                 /* Connect unconnected exportinfo, if there is any. */
 802                 if (new_exi && new_exi != exip)
 803                         tree_head = tree_prepend_node(tree_head, NULL, new_exi);
 804 
 805                 while (tree_head) {
 806                         treenode_t *t2 = tree_head;
 807                         exportinfo_t *e  = tree_head->tree_exi;
 808                         /* exip will be freed in exportfs() */
 809                         if (e && e != exip) {
 810                                 mutex_enter(&nfs_exi_id_lock);
 811                                 avl_remove(&exi_id_tree, e);
 812                                 mutex_exit(&nfs_exi_id_lock);
 813                                 export_unlink(ne, e);
 814                                 exi_rele(e);
 815                         }
 816                         tree_head = tree_head->tree_child_first;
 817                         kmem_free(t2, sizeof (*t2));
 818                 }
 819         }
 820 
 821         return (error);
 822 }
 823 
 824 /*
 825  * Walk up the tree and:
 826  * 1. release pseudo exportinfo if it has no child
 827  * 2. release visible in parent's exportinfo
 828  * 3. delete non-exported leaf nodes from tree
 829  *
 830  * Deleting of nodes will start only if the unshared
 831  * node was a leaf node.
 832  * Deleting of nodes will finish when we reach a node which
 833  * has children or is a real export, then we might still need
 834  * to continue releasing visibles, until we reach VROOT or zone's root node.
 835  */
 836 void
 837 treeclimb_unexport(nfs_export_t *ne, struct exportinfo *exip)
 838 {
 839         treenode_t *tnode, *old_nd;
 840         treenode_t *connect_point = NULL;
 841 
 842         ASSERT(RW_WRITE_HELD(&ne->exported_lock));
 843         ASSERT(curzone->zone_id == exip->exi_zoneid ||
 844             curzone->zone_id == global_zone->zone_id);
 845 
 846         /*
 847          * exi_tree can be null for the zone root
 848          * which means we're already at the "top"
 849          * and there's nothing more to "climb".
 850          */
 851         tnode = exip->exi_tree;
 852         if (tnode == NULL) {
 853                 /* Should only happen for... */
 854                 ASSERT(exip == ne->exi_root);
 855                 return;
 856         }
 857 
 858         /*
 859          * The unshared exportinfo was unlinked in unexport().
 860          * Zeroing tree_exi ensures that we will skip it.
 861          */
 862         tnode->tree_exi = NULL;
 863 
 864         if (tnode->tree_vis != NULL) /* system root has tree_vis == NULL */
 865                 tnode->tree_vis->vis_exported = 0;
 866 
 867         while (tnode != NULL) {
 868 
 869                 /*
 870                  * Stop at VROOT (or zone root) node which is exported or has
 871                  * child.
 872                  */
 873                 if (TREE_ROOT(tnode) &&
 874                     (TREE_EXPORTED(tnode) || tnode->tree_child_first != NULL))
 875                         break;
 876 
 877                 /* Release pseudo export if it has no child */
 878                 if (TREE_ROOT(tnode) && !TREE_EXPORTED(tnode) &&
 879                     tnode->tree_child_first == NULL) {
 880                         mutex_enter(&nfs_exi_id_lock);
 881                         avl_remove(&exi_id_tree, tnode->tree_exi);
 882                         mutex_exit(&nfs_exi_id_lock);
 883                         export_unlink(ne, tnode->tree_exi);
 884                         exi_rele(tnode->tree_exi);
 885                         tnode->tree_exi = NULL;
 886                 }
 887 
 888                 /* Release visible in parent's exportinfo */
 889                 if (tnode->tree_vis != NULL)
 890                         less_visible(vis2exi(tnode), tnode->tree_vis);
 891 
 892                 /* Continue with parent */
 893                 old_nd = tnode;
 894                 tnode = tnode->tree_parent;
 895 
 896                 /* Remove itself, if this is a leaf and non-exported node */
 897                 if (old_nd->tree_child_first == NULL &&
 898                     !TREE_EXPORTED(old_nd)) {
 899                         tree_remove_node(ne, old_nd);
 900                         connect_point = tnode;
 901                 }
 902         }
 903 
 904         /* Update the change timestamp */
 905         if (connect_point != NULL)
 906                 tree_update_change(ne, connect_point, NULL);
 907 }
 908 
 909 /*
 910  * Traverse backward across mountpoint from the
 911  * root vnode of a filesystem to its mounted-on
 912  * vnode.
 913  */
 914 vnode_t *
 915 untraverse(vnode_t *vp, vnode_t *zone_rootvp)
 916 {
 917         vnode_t *tvp, *nextvp;
 918 
 919         tvp = vp;
 920         for (;;) {
 921                 if (!(tvp->v_flag & VROOT) && !VN_CMP(tvp, zone_rootvp))
 922                         break;
 923 
 924                 /* lock vfs to prevent unmount of this vfs */
 925                 vfs_lock_wait(tvp->v_vfsp);
 926 
 927                 if ((nextvp = tvp->v_vfsp->vfs_vnodecovered) == NULL) {
 928                         vfs_unlock(tvp->v_vfsp);
 929                         break;
 930                 }
 931 
 932                 /*
 933                  * Hold nextvp to prevent unmount.  After unlock vfs and
 934                  * rele tvp, any number of overlays could be unmounted.
 935                  * Putting a hold on vfs_vnodecovered will only allow
 936                  * tvp's vfs to be unmounted. Of course if caller placed
 937                  * extra hold on vp before calling untraverse, the following
 938                  * hold would not be needed.  Since prev actions of caller
 939                  * are unknown, we need to hold here just to be safe.
 940                  */
 941                 VN_HOLD(nextvp);
 942                 vfs_unlock(tvp->v_vfsp);
 943                 VN_RELE(tvp);
 944                 tvp = nextvp;
 945         }
 946 
 947         return (tvp);
 948 }
 949 
 950 /*
 951  * Given an exportinfo, climb up to find the exportinfo for the VROOT
 952  * (or zone root) of the filesystem.
 953  *
 954  * e.g.         /
 955  *              |
 956  *              a (VROOT) pseudo-exportinfo
 957  *              |
 958  *              b
 959  *              |
 960  *              c  #share /a/b/c
 961  *              |
 962  *              d
 963  *
 964  * where c is in the same filesystem as a.
 965  * So, get_root_export(*exportinfo_for_c) returns exportinfo_for_a
 966  *
 967  * If d is shared, then c will be put into a's visible list.
 968  * Note: visible list is per filesystem and is attached to the
 969  * VROOT exportinfo.  Returned exi does NOT have a new hold.
 970  */
 971 struct exportinfo *
 972 get_root_export(struct exportinfo *exip)
 973 {
 974         treenode_t *tnode = exip->exi_tree;
 975         exportinfo_t *exi = NULL;
 976 
 977         while (tnode) {
 978                 if (TREE_ROOT(tnode)) {
 979                         exi = tnode->tree_exi;
 980                         break;
 981                 }
 982                 tnode = tnode->tree_parent;
 983         }
 984         ASSERT(exi);
 985         return (exi);
 986 }
 987 
 988 /*
 989  * Return true if the supplied vnode has a sub-directory exported.
 990  */
 991 int
 992 has_visible(struct exportinfo *exi, vnode_t *vp)
 993 {
 994         struct exp_visible *visp;
 995         fid_t fid;
 996         bool_t vp_is_exported;
 997 
 998         vp_is_exported = VN_CMP(vp, exi->exi_vp);
 999 
1000         /*
1001          * An exported root vnode has a sub-dir shared if it has a visible
1002          * list.  i.e. if it does not have a visible list, then there is no
1003          * node in this filesystem leads to any other shared node.
1004          */
1005         ASSERT3P(curzone->zone_id, ==, exi->exi_zoneid);
1006         if (vp_is_exported &&
1007             ((vp->v_flag & VROOT) || VN_IS_CURZONEROOT(vp))) {
1008                 return (exi->exi_visible ? 1 : 0);
1009         }
1010 
1011         /*
1012          * Only the exportinfo of a fs root node may have a visible list.
1013          * Either it is a pseudo root node, or a real exported root node.
1014          */
1015         exi = get_root_export(exi);
1016 
1017         if (!exi->exi_visible)
1018                 return (0);
1019 
1020         /* Get the fid of the vnode */
1021         bzero(&fid, sizeof (fid));
1022         fid.fid_len = MAXFIDSZ;
1023         if (vop_fid_pseudo(vp, &fid) != 0) {
1024                 return (0);
1025         }
1026 
1027         /*
1028          * See if vp is in the visible list of the root node exportinfo.
1029          */
 
1062          * (it would be a real export then); however,
1063          * it is always visible.  If a pseudo root object
1064          * was exported by server admin, then the entire
1065          * pseudo exportinfo (and all visible entries) would
1066          * be destroyed.  A pseudo exportinfo only exists
1067          * to provide access to real (descendant) export(s).
1068          *
1069          * Previously, rootdir was special cased here; however,
1070          * the export root special case handles the rootdir
1071          * case also.
1072          */
1073         if (VN_CMP(vp, exi->exi_vp)) {
1074                 *expseudo = 0;
1075                 return (1);
1076         }
1077 
1078         /*
1079          * Only a PSEUDO node has a visible list or an exported VROOT
1080          * node may have a visible list.
1081          */
1082         if (!PSEUDO(exi))
1083                 exi = get_root_export(exi);
1084 
1085         /* Get the fid of the vnode */
1086 
1087         bzero(&fid, sizeof (fid));
1088         fid.fid_len = MAXFIDSZ;
1089         if (vop_fid_pseudo(vp, &fid) != 0) {
1090                 *expseudo = 0;
1091                 return (0);
1092         }
1093 
1094         /*
1095          * We can't trust VN_CMP() above because of LOFS.
1096          * Even though VOP_CMP will do the right thing for LOFS
1097          * objects, VN_CMP will short circuit out early when the
1098          * vnode ops ptrs are different.  Just in case we're dealing
1099          * with LOFS, compare exi_fid/fsid here.
1100          *
1101          * expseudo is not set because this is not an export
1102          */
 
1170 }
1171 
1172 /*
1173  * Returns true if the supplied inode is visible
1174  * in this export.  This function is used by
1175  * readdir which uses inode numbers from the
1176  * directory.
1177  *
1178  * NOTE: this code does not match inode number for ".",
1179  * but it isn't required because NFS4 server rddir
1180  * skips . and .. entries.
1181  */
1182 int
1183 nfs_visible_inode(struct exportinfo *exi, ino64_t ino,
1184     struct exp_visible **visp)
1185 {
1186         /*
1187          * Only a PSEUDO node has a visible list or an exported VROOT
1188          * node may have a visible list.
1189          */
1190         if (!PSEUDO(exi))
1191                 exi = get_root_export(exi);
1192 
1193         for (*visp = exi->exi_visible; *visp != NULL; *visp = (*visp)->vis_next)
1194                 if ((u_longlong_t)ino == (*visp)->vis_ino) {
1195                         return (1);
1196                 }
1197 
1198         return (0);
1199 }
1200 
1201 /*
1202  * Get the change attribute from visible and returns TRUE.
1203  * If the change value is not available returns FALSE.
1204  */
1205 bool_t
1206 nfs_visible_change(struct exportinfo *exi, vnode_t *vp, timespec_t *change)
1207 {
1208         struct exp_visible *visp;
1209         fid_t fid;
1210         treenode_t *node;
1211         nfs_export_t *ne = nfs_get_export();
1212 
1213         /*
1214          * First check to see if vp is export root.
1215          */
1216         if (VN_CMP(vp, exi->exi_vp))
1217                 goto exproot;
1218 
1219         /*
1220          * Only a PSEUDO node has a visible list or an exported VROOT
1221          * node may have a visible list.
1222          */
1223         if (!PSEUDO(exi))
1224                 exi = get_root_export(exi);
1225 
1226         /* Get the fid of the vnode */
1227         bzero(&fid, sizeof (fid));
1228         fid.fid_len = MAXFIDSZ;
1229         if (vop_fid_pseudo(vp, &fid) != 0)
1230                 return (FALSE);
1231 
 
1236          * vnode ops ptrs are different.  Just in case we're dealing
1237          * with LOFS, compare exi_fid/fsid here.
1238          */
1239         if (EQFID(&exi->exi_fid, &fid) &&
1240             EQFSID(&exi->exi_fsid, &vp->v_vfsp->vfs_fsid))
1241                 goto exproot;
1242 
1243         /* See if it matches any fid in the visible list */
1244         for (visp = exi->exi_visible; visp; visp = visp->vis_next) {
1245                 if (EQFID(&fid, &visp->vis_fid)) {
1246                         *change = visp->vis_change;
1247                         return (TRUE);
1248                 }
1249         }
1250 
1251         return (FALSE);
1252 
1253 exproot:
1254         /* The VROOT export have its visible available through treenode */
1255         node = exi->exi_tree;
1256         if (node != ne->ns_root) {
1257                 ASSERT(node->tree_vis != NULL);
1258                 *change = node->tree_vis->vis_change;
1259         } else {
1260                 ASSERT(node->tree_vis == NULL);
1261                 *change = ne->ns_root_change;
1262         }
1263         return (TRUE);
1264 }
1265 
1266 /*
1267  * Update the change attribute value for a particular treenode.  The change
1268  * attribute value is stored in the visible attached to the treenode, or in the
1269  * ns_root_change.
1270  *
1271  * If the change value is not supplied, the current time is used.
1272  */
1273 void
1274 tree_update_change(nfs_export_t *ne, treenode_t *tnode, timespec_t *change)
1275 {
1276         timespec_t *vis_change;
1277 
1278         ASSERT(tnode != NULL);
1279         ASSERT((tnode != ne->ns_root && tnode->tree_vis != NULL) ||
1280             (tnode == ne->ns_root && tnode->tree_vis == NULL));
1281 
1282         vis_change = tnode == ne->ns_root ? &ne->ns_root_change
1283             : &tnode->tree_vis->vis_change;
1284 
1285         if (change != NULL)
1286                 *vis_change = *change;
1287         else
1288                 gethrestime(vis_change);
1289 }
 |