Print this page
Revert exi_zone to exi_zoneid, and install exi_ne backpointer
Caution with use after exi_rele()
Ooops exi_zoneid isn't a variable again yet
Be far more judicious in the use of curzone-using macros.
(Merge and extra asserts by danmcd.)
curzone reality check and teardown changes to use the RIGHT zone
Try to remove assumption that zone's root vnode is marked VROOT


 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;