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
 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, struct exp_visible *vis_head,
 150     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_volatile_dev = (vfssw[vp->v_vfsp->vfs_fstype].vsw_flag &
 169             VSW_VOLATILEDEV) ? 1 : 0;
 170         mutex_init(&exi->exi_lock, NULL, MUTEX_DEFAULT, NULL);
 171         exi->exi_zoneid = ne->ne_globals->nfs_zoneid;
 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);
 189 
 190         if (vpathlen)
 191                 (void) strncpy(kex->ex_path, vp->v_path, vpathlen);
 
 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 = nfs_get_export();
 644 
 645         ASSERT(RW_WRITE_HELD(&ne->exported_lock));
 646 
 647         gethrestime(&now);
 648 
 649         vp = exip->exi_vp;
 650         VN_HOLD(vp);
 651         exportdir = 1;
 652 
 653         for (;;) {
 654 
 655                 bzero(&fid, sizeof (fid));
 656                 fid.fid_len = MAXFIDSZ;
 657                 error = vop_fid_pseudo(vp, &fid);
 658                 if (error)
 659                         break;
 660 
 661                 /*
 662                  * The root of the file system needs special handling
 663                  */
 664                 if (vp->v_flag & VROOT) {
 665                         if (! exportdir) {
 666                                 struct exportinfo *exi;
 667 
 668                                 /*
 669                                  * Check if this VROOT dir is already exported.
 670                                  * If so, then attach the pseudonodes.  If not,
 671                                  * then continue .. traversal until we hit a
 672                                  * VROOT export (pseudo or real).
 673                                  */
 674                                 exi = checkexport4(&vp->v_vfsp->vfs_fsid, &fid,
 675                                     vp);
 676                                 if (exi != NULL) {
 677                                         /*
 678                                          * Found an export info
 679                                          *
 680                                          * Extend the list of visible
 681                                          * directories whether it's a pseudo
 682                                          * or a real export.
 683                                          */
 684                                         more_visible(exi, tree_head);
 685                                         break;  /* and climb no further */
 686                                 }
 687 
 688                                 /*
 689                                  * Found the root directory of a filesystem
 690                                  * that isn't exported.  Need to export
 691                                  * this as a pseudo export so that an NFS v4
 692                                  * client can do lookups in it.
 693                                  */
 694                                 new_exi = pseudo_exportfs(ne, vp, &fid,
 695                                     vis_head, NULL);
 696                                 vis_head = NULL;
 697                         }
 698 
 699                         if (VN_CMP(vp, ZONE_ROOTVP())) {
 700                                 /* at system root */
 701                                 /*
 702                                  * If sharing "/", new_exi is shared exportinfo
 703                                  * (exip). Otherwise, new_exi is exportinfo
 704                                  * created by pseudo_exportfs() above.
 705                                  */
 706                                 ne->ns_root = tree_prepend_node(tree_head, NULL,
 707                                     new_exi);
 708 
 709                                 /* Update the change timestamp */
 710                                 tree_update_change(ne, ne->ns_root, &now);
 711 
 712                                 break;
 713                         }
 714 
 715                         /*
 716                          * Traverse across the mountpoint and continue the
 717                          * climb on the mounted-on filesystem.
 718                          */
 719                         vp = untraverse(vp);
 
 809                                 exi_rele(e);
 810                         }
 811                         tree_head = tree_head->tree_child_first;
 812                         kmem_free(t2, sizeof (*t2));
 813                 }
 814         }
 815 
 816         return (error);
 817 }
 818 
 819 /*
 820  * Walk up the tree and:
 821  * 1. release pseudo exportinfo if it has no child
 822  * 2. release visible in parent's exportinfo
 823  * 3. delete non-exported leaf nodes from tree
 824  *
 825  * Deleting of nodes will start only if the unshared
 826  * node was a leaf node.
 827  * Deleting of nodes will finish when we reach a node which
 828  * has children or is a real export, then we might still need
 829  * to continue releasing visibles, until we reach VROOT node.
 830  */
 831 void
 832 treeclimb_unexport(nfs_export_t *ne, struct exportinfo *exip)
 833 {
 834         treenode_t *tnode, *old_nd;
 835         treenode_t *connect_point = NULL;
 836 
 837         ASSERT(RW_WRITE_HELD(&ne->exported_lock));
 838 
 839         /*
 840          * exi_tree can be null for the zone root
 841          * which means we're already at the "top"
 842          * and there's nothing more to "climb".
 843          */
 844         tnode = exip->exi_tree;
 845         if (tnode == NULL) {
 846                 /* Should only happen for... */
 847                 ASSERT(exip == ne->exi_root);
 848                 return;
 849         }
 850 
 851         /*
 852          * The unshared exportinfo was unlinked in unexport().
 853          * Zeroing tree_exi ensures that we will skip it.
 854          */
 855         tnode->tree_exi = NULL;
 856 
 857         if (tnode->tree_vis != NULL) /* system root has tree_vis == NULL */
 858                 tnode->tree_vis->vis_exported = 0;
 859 
 860         while (tnode != NULL) {
 861 
 862                 /* Stop at VROOT node which is exported or has child */
 863                 if (TREE_ROOT(tnode) &&
 864                     (TREE_EXPORTED(tnode) || tnode->tree_child_first != NULL))
 865                         break;
 866 
 867                 /* Release pseudo export if it has no child */
 868                 if (TREE_ROOT(tnode) && !TREE_EXPORTED(tnode) &&
 869                     tnode->tree_child_first == NULL) {
 870                         mutex_enter(&nfs_exi_id_lock);
 871                         avl_remove(&exi_id_tree, tnode->tree_exi);
 872                         mutex_exit(&nfs_exi_id_lock);
 873                         export_unlink(ne, tnode->tree_exi);
 874                         exi_rele(tnode->tree_exi);
 875                 }
 876 
 877                 /* Release visible in parent's exportinfo */
 878                 if (tnode->tree_vis != NULL)
 879                         less_visible(vis2exi(tnode), tnode->tree_vis);
 880 
 881                 /* Continue with parent */
 882                 old_nd = tnode;
 883                 tnode = tnode->tree_parent;
 884 
 885                 /* Remove itself, if this is a leaf and non-exported node */
 886                 if (old_nd->tree_child_first == NULL &&
 887                     !TREE_EXPORTED(old_nd)) {
 888                         tree_remove_node(ne, old_nd);
 889                         connect_point = tnode;
 890                 }
 891         }
 892 
 893         /* Update the change timestamp */
 894         if (connect_point != NULL)
 895                 tree_update_change(ne, connect_point, NULL);
 896 }
 897 
 898 /*
 899  * Traverse backward across mountpoint from the
 900  * root vnode of a filesystem to its mounted-on
 901  * vnode.
 902  */
 903 vnode_t *
 904 untraverse(vnode_t *vp)
 905 {
 906         vnode_t *tvp, *nextvp;
 907 
 908         tvp = vp;
 909         for (;;) {
 910                 if (! (tvp->v_flag & VROOT))
 911                         break;
 912 
 913                 /* lock vfs to prevent unmount of this vfs */
 914                 vfs_lock_wait(tvp->v_vfsp);
 915 
 916                 if ((nextvp = tvp->v_vfsp->vfs_vnodecovered) == NULL) {
 917                         vfs_unlock(tvp->v_vfsp);
 918                         break;
 919                 }
 920 
 921                 /*
 922                  * Hold nextvp to prevent unmount.  After unlock vfs and
 923                  * rele tvp, any number of overlays could be unmounted.
 924                  * Putting a hold on vfs_vnodecovered will only allow
 925                  * tvp's vfs to be unmounted. Of course if caller placed
 926                  * extra hold on vp before calling untraverse, the following
 927                  * hold would not be needed.  Since prev actions of caller
 928                  * are unknown, we need to hold here just to be safe.
 929                  */
 930                 VN_HOLD(nextvp);
 931                 vfs_unlock(tvp->v_vfsp);
 932                 VN_RELE(tvp);
 933                 tvp = nextvp;
 934         }
 935 
 936         return (tvp);
 937 }
 938 
 939 /*
 940  * Given an exportinfo, climb up to find the exportinfo for the VROOT
 941  * of the filesystem.
 942  *
 943  * e.g.         /
 944  *              |
 945  *              a (VROOT) pseudo-exportinfo
 946  *              |
 947  *              b
 948  *              |
 949  *              c  #share /a/b/c
 950  *              |
 951  *              d
 952  *
 953  * where c is in the same filesystem as a.
 954  * So, get_root_export(*exportinfo_for_c) returns exportinfo_for_a
 955  *
 956  * If d is shared, then c will be put into a's visible list.
 957  * Note: visible list is per filesystem and is attached to the
 958  * VROOT exportinfo.
 959  */
 960 struct exportinfo *
 961 get_root_export(struct exportinfo *exip)
 962 {
 963         treenode_t *tnode = exip->exi_tree;
 964         exportinfo_t *exi = NULL;
 965 
 966         while (tnode) {
 967                 if (TREE_ROOT(tnode)) {
 968                         exi = tnode->tree_exi;
 969                         break;
 970                 }
 971                 tnode = tnode->tree_parent;
 972         }
 973         ASSERT(exi);
 974         return (exi);
 975 }
 976 
 977 /*
 978  * Return true if the supplied vnode has a sub-directory exported.
 979  */
 980 int
 981 has_visible(struct exportinfo *exi, vnode_t *vp)
 982 {
 983         struct exp_visible *visp;
 984         fid_t fid;
 985         bool_t vp_is_exported;
 986 
 987         vp_is_exported = VN_CMP(vp, exi->exi_vp);
 988 
 989         /*
 990          * An exported root vnode has a sub-dir shared if it has a visible list.
 991          * i.e. if it does not have a visible list, then there is no node in
 992          * this filesystem leads to any other shared node.
 993          */
 994         if (vp_is_exported && (vp->v_flag & VROOT))
 995                 return (exi->exi_visible ? 1 : 0);
 996 
 997         /*
 998          * Only the exportinfo of a fs root node may have a visible list.
 999          * Either it is a pseudo root node, or a real exported root node.
1000          */
1001         exi = get_root_export(exi);
1002 
1003         if (!exi->exi_visible)
1004                 return (0);
1005 
1006         /* Get the fid of the vnode */
1007         bzero(&fid, sizeof (fid));
1008         fid.fid_len = MAXFIDSZ;
1009         if (vop_fid_pseudo(vp, &fid) != 0) {
1010                 return (0);
1011         }
1012 
1013         /*
1014          * See if vp is in the visible list of the root node exportinfo.
1015          */
 
1048          * (it would be a real export then); however,
1049          * it is always visible.  If a pseudo root object
1050          * was exported by server admin, then the entire
1051          * pseudo exportinfo (and all visible entries) would
1052          * be destroyed.  A pseudo exportinfo only exists
1053          * to provide access to real (descendant) export(s).
1054          *
1055          * Previously, rootdir was special cased here; however,
1056          * the export root special case handles the rootdir
1057          * case also.
1058          */
1059         if (VN_CMP(vp, exi->exi_vp)) {
1060                 *expseudo = 0;
1061                 return (1);
1062         }
1063 
1064         /*
1065          * Only a PSEUDO node has a visible list or an exported VROOT
1066          * node may have a visible list.
1067          */
1068         if (! PSEUDO(exi))
1069                 exi = get_root_export(exi);
1070 
1071         /* Get the fid of the vnode */
1072 
1073         bzero(&fid, sizeof (fid));
1074         fid.fid_len = MAXFIDSZ;
1075         if (vop_fid_pseudo(vp, &fid) != 0) {
1076                 *expseudo = 0;
1077                 return (0);
1078         }
1079 
1080         /*
1081          * We can't trust VN_CMP() above because of LOFS.
1082          * Even though VOP_CMP will do the right thing for LOFS
1083          * objects, VN_CMP will short circuit out early when the
1084          * vnode ops ptrs are different.  Just in case we're dealing
1085          * with LOFS, compare exi_fid/fsid here.
1086          *
1087          * expseudo is not set because this is not an export
1088          */
 
1156 }
1157 
1158 /*
1159  * Returns true if the supplied inode is visible
1160  * in this export.  This function is used by
1161  * readdir which uses inode numbers from the
1162  * directory.
1163  *
1164  * NOTE: this code does not match inode number for ".",
1165  * but it isn't required because NFS4 server rddir
1166  * skips . and .. entries.
1167  */
1168 int
1169 nfs_visible_inode(struct exportinfo *exi, ino64_t ino,
1170     struct exp_visible **visp)
1171 {
1172         /*
1173          * Only a PSEUDO node has a visible list or an exported VROOT
1174          * node may have a visible list.
1175          */
1176         if (! PSEUDO(exi))
1177                 exi = get_root_export(exi);
1178 
1179         for (*visp = exi->exi_visible; *visp != NULL; *visp = (*visp)->vis_next)
1180                 if ((u_longlong_t)ino == (*visp)->vis_ino) {
1181                         return (1);
1182                 }
1183 
1184         return (0);
1185 }
1186 
1187 /*
1188  * Get the change attribute from visible and returns TRUE.
1189  * If the change value is not available returns FALSE.
1190  */
1191 bool_t
1192 nfs_visible_change(struct exportinfo *exi, vnode_t *vp, timespec_t *change)
1193 {
1194         struct exp_visible *visp;
1195         fid_t fid;
1196         treenode_t *node;
 
 | 
 
 
 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);
 189 
 190         if (vpathlen)
 191                 (void) strncpy(kex->ex_path, vp->v_path, vpathlen);
 
 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);
 
 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  * Callers to this function have confirmed the use of curzone is safe here.
 915  */
 916 vnode_t *
 917 untraverse(vnode_t *vp)
 918 {
 919         vnode_t *tvp, *nextvp;
 920 
 921         tvp = vp;
 922         for (;;) {
 923                 if (!(tvp->v_flag & VROOT) && !VN_IS_CURZONEROOT(tvp))
 924                         break;
 925 
 926                 /* lock vfs to prevent unmount of this vfs */
 927                 vfs_lock_wait(tvp->v_vfsp);
 928 
 929                 if ((nextvp = tvp->v_vfsp->vfs_vnodecovered) == NULL) {
 930                         vfs_unlock(tvp->v_vfsp);
 931                         break;
 932                 }
 933 
 934                 /*
 935                  * Hold nextvp to prevent unmount.  After unlock vfs and
 936                  * rele tvp, any number of overlays could be unmounted.
 937                  * Putting a hold on vfs_vnodecovered will only allow
 938                  * tvp's vfs to be unmounted. Of course if caller placed
 939                  * extra hold on vp before calling untraverse, the following
 940                  * hold would not be needed.  Since prev actions of caller
 941                  * are unknown, we need to hold here just to be safe.
 942                  */
 943                 VN_HOLD(nextvp);
 944                 vfs_unlock(tvp->v_vfsp);
 945                 VN_RELE(tvp);
 946                 tvp = nextvp;
 947         }
 948 
 949         return (tvp);
 950 }
 951 
 952 /*
 953  * Given an exportinfo, climb up to find the exportinfo for the VROOT
 954  * (or zone root) of the filesystem.
 955  *
 956  * e.g.         /
 957  *              |
 958  *              a (VROOT) pseudo-exportinfo
 959  *              |
 960  *              b
 961  *              |
 962  *              c  #share /a/b/c
 963  *              |
 964  *              d
 965  *
 966  * where c is in the same filesystem as a.
 967  * So, get_root_export(*exportinfo_for_c) returns exportinfo_for_a
 968  *
 969  * If d is shared, then c will be put into a's visible list.
 970  * Note: visible list is per filesystem and is attached to the
 971  * VROOT exportinfo.  Returned exi does NOT have a new hold.
 972  */
 973 struct exportinfo *
 974 get_root_export(struct exportinfo *exip)
 975 {
 976         treenode_t *tnode = exip->exi_tree;
 977         exportinfo_t *exi = NULL;
 978 
 979         while (tnode) {
 980                 if (TREE_ROOT(tnode)) {
 981                         exi = tnode->tree_exi;
 982                         break;
 983                 }
 984                 tnode = tnode->tree_parent;
 985         }
 986         ASSERT(exi);
 987         return (exi);
 988 }
 989 
 990 /*
 991  * Return true if the supplied vnode has a sub-directory exported.
 992  */
 993 int
 994 has_visible(struct exportinfo *exi, vnode_t *vp)
 995 {
 996         struct exp_visible *visp;
 997         fid_t fid;
 998         bool_t vp_is_exported;
 999 
1000         vp_is_exported = VN_CMP(vp, exi->exi_vp);
1001 
1002         /*
1003          * An exported root vnode has a sub-dir shared if it has a visible
1004          * list.  i.e. if it does not have a visible list, then there is no
1005          * node in this filesystem leads to any other shared node.
1006          */
1007         ASSERT3P(curzone->zone_id, ==, exi->exi_zoneid);
1008         if (vp_is_exported &&
1009             ((vp->v_flag & VROOT) || VN_IS_CURZONEROOT(vp))) {
1010                 return (exi->exi_visible ? 1 : 0);
1011         }
1012 
1013         /*
1014          * Only the exportinfo of a fs root node may have a visible list.
1015          * Either it is a pseudo root node, or a real exported root node.
1016          */
1017         exi = get_root_export(exi);
1018 
1019         if (!exi->exi_visible)
1020                 return (0);
1021 
1022         /* Get the fid of the vnode */
1023         bzero(&fid, sizeof (fid));
1024         fid.fid_len = MAXFIDSZ;
1025         if (vop_fid_pseudo(vp, &fid) != 0) {
1026                 return (0);
1027         }
1028 
1029         /*
1030          * See if vp is in the visible list of the root node exportinfo.
1031          */
 
1064          * (it would be a real export then); however,
1065          * it is always visible.  If a pseudo root object
1066          * was exported by server admin, then the entire
1067          * pseudo exportinfo (and all visible entries) would
1068          * be destroyed.  A pseudo exportinfo only exists
1069          * to provide access to real (descendant) export(s).
1070          *
1071          * Previously, rootdir was special cased here; however,
1072          * the export root special case handles the rootdir
1073          * case also.
1074          */
1075         if (VN_CMP(vp, exi->exi_vp)) {
1076                 *expseudo = 0;
1077                 return (1);
1078         }
1079 
1080         /*
1081          * Only a PSEUDO node has a visible list or an exported VROOT
1082          * node may have a visible list.
1083          */
1084         if (!PSEUDO(exi))
1085                 exi = get_root_export(exi);
1086 
1087         /* Get the fid of the vnode */
1088 
1089         bzero(&fid, sizeof (fid));
1090         fid.fid_len = MAXFIDSZ;
1091         if (vop_fid_pseudo(vp, &fid) != 0) {
1092                 *expseudo = 0;
1093                 return (0);
1094         }
1095 
1096         /*
1097          * We can't trust VN_CMP() above because of LOFS.
1098          * Even though VOP_CMP will do the right thing for LOFS
1099          * objects, VN_CMP will short circuit out early when the
1100          * vnode ops ptrs are different.  Just in case we're dealing
1101          * with LOFS, compare exi_fid/fsid here.
1102          *
1103          * expseudo is not set because this is not an export
1104          */
 
1172 }
1173 
1174 /*
1175  * Returns true if the supplied inode is visible
1176  * in this export.  This function is used by
1177  * readdir which uses inode numbers from the
1178  * directory.
1179  *
1180  * NOTE: this code does not match inode number for ".",
1181  * but it isn't required because NFS4 server rddir
1182  * skips . and .. entries.
1183  */
1184 int
1185 nfs_visible_inode(struct exportinfo *exi, ino64_t ino,
1186     struct exp_visible **visp)
1187 {
1188         /*
1189          * Only a PSEUDO node has a visible list or an exported VROOT
1190          * node may have a visible list.
1191          */
1192         if (!PSEUDO(exi))
1193                 exi = get_root_export(exi);
1194 
1195         for (*visp = exi->exi_visible; *visp != NULL; *visp = (*visp)->vis_next)
1196                 if ((u_longlong_t)ino == (*visp)->vis_ino) {
1197                         return (1);
1198                 }
1199 
1200         return (0);
1201 }
1202 
1203 /*
1204  * Get the change attribute from visible and returns TRUE.
1205  * If the change value is not available returns FALSE.
1206  */
1207 bool_t
1208 nfs_visible_change(struct exportinfo *exi, vnode_t *vp, timespec_t *change)
1209 {
1210         struct exp_visible *visp;
1211         fid_t fid;
1212         treenode_t *node;
 
 |