Print this page
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);


 641         timespec_t now;
 642         nfs_export_t *ne = nfs_get_export();
 643 
 644         ASSERT(RW_WRITE_HELD(&ne->exported_lock));
 645 
 646         gethrestime(&now);
 647 
 648         vp = exip->exi_vp;
 649         VN_HOLD(vp);
 650         exportdir = 1;
 651 
 652         for (;;) {
 653 
 654                 bzero(&fid, sizeof (fid));
 655                 fid.fid_len = MAXFIDSZ;
 656                 error = vop_fid_pseudo(vp, &fid);
 657                 if (error)
 658                         break;
 659 
 660                 /*
 661                  * The root of the file system needs special handling

 662                  */
 663                 if (vp->v_flag & VROOT) {
 664                         if (! exportdir) {
 665                                 struct exportinfo *exi;
 666 
 667                                 /*
 668                                  * Check if this VROOT dir is already exported.
 669                                  * If so, then attach the pseudonodes.  If not,
 670                                  * then continue .. traversal until we hit a
 671                                  * VROOT export (pseudo or real).
 672                                  */
 673                                 exi = checkexport4(&vp->v_vfsp->vfs_fsid, &fid,
 674                                     vp);
 675                                 if (exi != NULL) {
 676                                         /*
 677                                          * Found an export info
 678                                          *
 679                                          * Extend the list of visible
 680                                          * directories whether it's a pseudo
 681                                          * or a real export.
 682                                          */
 683                                         more_visible(exi, tree_head);
 684                                         break;  /* and climb no further */
 685                                 }
 686 
 687                                 /*
 688                                  * Found the root directory of a filesystem
 689                                  * that isn't exported.  Need to export
 690                                  * this as a pseudo export so that an NFS v4
 691                                  * client can do lookups in it.
 692                                  */
 693                                 new_exi = pseudo_exportfs(ne, vp, &fid,
 694                                     vis_head, NULL);
 695                                 vis_head = NULL;
 696                         }
 697 
 698                         if (VN_CMP(vp, ZONE_ROOTVP())) {
 699                                 /* at system root */
 700                                 /*
 701                                  * If sharing "/", new_exi is shared exportinfo
 702                                  * (exip). Otherwise, new_exi is exportinfo
 703                                  * created by pseudo_exportfs() above.
 704                                  */
 705                                 ne->ns_root = tree_prepend_node(tree_head, NULL,
 706                                     new_exi);
 707 
 708                                 /* Update the change timestamp */
 709                                 tree_update_change(ne, ne->ns_root, &now);
 710 
 711                                 break;
 712                         }
 713 
 714                         /*
 715                          * Traverse across the mountpoint and continue the
 716                          * climb on the mounted-on filesystem.
 717                          */
 718                         vp = untraverse(vp);


 808                                 exi_rele(e);
 809                         }
 810                         tree_head = tree_head->tree_child_first;
 811                         kmem_free(t2, sizeof (*t2));
 812                 }
 813         }
 814 
 815         return (error);
 816 }
 817 
 818 /*
 819  * Walk up the tree and:
 820  * 1. release pseudo exportinfo if it has no child
 821  * 2. release visible in parent's exportinfo
 822  * 3. delete non-exported leaf nodes from tree
 823  *
 824  * Deleting of nodes will start only if the unshared
 825  * node was a leaf node.
 826  * Deleting of nodes will finish when we reach a node which
 827  * has children or is a real export, then we might still need
 828  * to continue releasing visibles, until we reach VROOT node.
 829  */
 830 void
 831 treeclimb_unexport(nfs_export_t *ne, struct exportinfo *exip)
 832 {
 833         treenode_t *tnode, *old_nd;
 834         treenode_t *connect_point = NULL;
 835 
 836         ASSERT(RW_WRITE_HELD(&ne->exported_lock));
 837 
 838         tnode = exip->exi_tree;
 839         /*
 840          * The unshared exportinfo was unlinked in unexport().
 841          * Zeroing tree_exi ensures that we will skip it.
 842          */
 843         tnode->tree_exi = NULL;
 844 
 845         if (tnode->tree_vis != NULL) /* system root has tree_vis == NULL */
 846                 tnode->tree_vis->vis_exported = 0;
 847 
 848         while (tnode != NULL) {
 849 
 850                 /* Stop at VROOT node which is exported or has child */



 851                 if (TREE_ROOT(tnode) &&
 852                     (TREE_EXPORTED(tnode) || tnode->tree_child_first != NULL))
 853                         break;
 854 
 855                 /* Release pseudo export if it has no child */
 856                 if (TREE_ROOT(tnode) && !TREE_EXPORTED(tnode) &&
 857                     tnode->tree_child_first == NULL) {
 858                         mutex_enter(&nfs_exi_id_lock);
 859                         avl_remove(&exi_id_tree, tnode->tree_exi);
 860                         mutex_exit(&nfs_exi_id_lock);
 861                         export_unlink(ne, tnode->tree_exi);
 862                         exi_rele(tnode->tree_exi);
 863                 }
 864 
 865                 /* Release visible in parent's exportinfo */
 866                 if (tnode->tree_vis != NULL)
 867                         less_visible(vis2exi(tnode), tnode->tree_vis);
 868 
 869                 /* Continue with parent */
 870                 old_nd = tnode;


 878                 }
 879         }
 880 
 881         /* Update the change timestamp */
 882         if (connect_point != NULL)
 883                 tree_update_change(ne, connect_point, NULL);
 884 }
 885 
 886 /*
 887  * Traverse backward across mountpoint from the
 888  * root vnode of a filesystem to its mounted-on
 889  * vnode.
 890  */
 891 vnode_t *
 892 untraverse(vnode_t *vp)
 893 {
 894         vnode_t *tvp, *nextvp;
 895 
 896         tvp = vp;
 897         for (;;) {
 898                 if (! (tvp->v_flag & VROOT))
 899                         break;
 900 
 901                 /* lock vfs to prevent unmount of this vfs */
 902                 vfs_lock_wait(tvp->v_vfsp);
 903 
 904                 if ((nextvp = tvp->v_vfsp->vfs_vnodecovered) == NULL) {
 905                         vfs_unlock(tvp->v_vfsp);
 906                         break;
 907                 }
 908 
 909                 /*
 910                  * Hold nextvp to prevent unmount.  After unlock vfs and
 911                  * rele tvp, any number of overlays could be unmounted.
 912                  * Putting a hold on vfs_vnodecovered will only allow
 913                  * tvp's vfs to be unmounted. Of course if caller placed
 914                  * extra hold on vp before calling untraverse, the following
 915                  * hold would not be needed.  Since prev actions of caller
 916                  * are unknown, we need to hold here just to be safe.
 917                  */
 918                 VN_HOLD(nextvp);
 919                 vfs_unlock(tvp->v_vfsp);
 920                 VN_RELE(tvp);
 921                 tvp = nextvp;
 922         }
 923 
 924         return (tvp);
 925 }
 926 
 927 /*
 928  * Given an exportinfo, climb up to find the exportinfo for the VROOT
 929  * of the filesystem.
 930  *
 931  * e.g.         /
 932  *              |
 933  *              a (VROOT) pseudo-exportinfo
 934  *              |
 935  *              b
 936  *              |
 937  *              c  #share /a/b/c
 938  *              |
 939  *              d
 940  *
 941  * where c is in the same filesystem as a.
 942  * So, get_root_export(*exportinfo_for_c) returns exportinfo_for_a
 943  *
 944  * If d is shared, then c will be put into a's visible list.
 945  * Note: visible list is per filesystem and is attached to the
 946  * VROOT exportinfo.
 947  */
 948 struct exportinfo *
 949 get_root_export(struct exportinfo *exip)


 958                 }
 959                 tnode = tnode->tree_parent;
 960         }
 961         ASSERT(exi);
 962         return (exi);
 963 }
 964 
 965 /*
 966  * Return true if the supplied vnode has a sub-directory exported.
 967  */
 968 int
 969 has_visible(struct exportinfo *exi, vnode_t *vp)
 970 {
 971         struct exp_visible *visp;
 972         fid_t fid;
 973         bool_t vp_is_exported;
 974 
 975         vp_is_exported = VN_CMP(vp, exi->exi_vp);
 976 
 977         /*
 978          * An exported root vnode has a sub-dir shared if it has a visible list.
 979          * i.e. if it does not have a visible list, then there is no node in
 980          * this filesystem leads to any other shared node.
 981          */
 982         if (vp_is_exported && (vp->v_flag & VROOT))

 983                 return (exi->exi_visible ? 1 : 0);

 984 
 985         /*
 986          * Only the exportinfo of a fs root node may have a visible list.
 987          * Either it is a pseudo root node, or a real exported root node.
 988          */
 989         exi = get_root_export(exi);
 990 
 991         if (!exi->exi_visible)
 992                 return (0);
 993 
 994         /* Get the fid of the vnode */
 995         bzero(&fid, sizeof (fid));
 996         fid.fid_len = MAXFIDSZ;
 997         if (vop_fid_pseudo(vp, &fid) != 0) {
 998                 return (0);
 999         }
1000 
1001         /*
1002          * See if vp is in the visible list of the root node exportinfo.
1003          */


1036          * (it would be a real export then); however,
1037          * it is always visible.  If a pseudo root object
1038          * was exported by server admin, then the entire
1039          * pseudo exportinfo (and all visible entries) would
1040          * be destroyed.  A pseudo exportinfo only exists
1041          * to provide access to real (descendant) export(s).
1042          *
1043          * Previously, rootdir was special cased here; however,
1044          * the export root special case handles the rootdir
1045          * case also.
1046          */
1047         if (VN_CMP(vp, exi->exi_vp)) {
1048                 *expseudo = 0;
1049                 return (1);
1050         }
1051 
1052         /*
1053          * Only a PSEUDO node has a visible list or an exported VROOT
1054          * node may have a visible list.
1055          */
1056         if (! PSEUDO(exi))
1057                 exi = get_root_export(exi);
1058 
1059         /* Get the fid of the vnode */
1060 
1061         bzero(&fid, sizeof (fid));
1062         fid.fid_len = MAXFIDSZ;
1063         if (vop_fid_pseudo(vp, &fid) != 0) {
1064                 *expseudo = 0;
1065                 return (0);
1066         }
1067 
1068         /*
1069          * We can't trust VN_CMP() above because of LOFS.
1070          * Even though VOP_CMP will do the right thing for LOFS
1071          * objects, VN_CMP will short circuit out early when the
1072          * vnode ops ptrs are different.  Just in case we're dealing
1073          * with LOFS, compare exi_fid/fsid here.
1074          *
1075          * expseudo is not set because this is not an export
1076          */


1144 }
1145 
1146 /*
1147  * Returns true if the supplied inode is visible
1148  * in this export.  This function is used by
1149  * readdir which uses inode numbers from the
1150  * directory.
1151  *
1152  * NOTE: this code does not match inode number for ".",
1153  * but it isn't required because NFS4 server rddir
1154  * skips . and .. entries.
1155  */
1156 int
1157 nfs_visible_inode(struct exportinfo *exi, ino64_t ino,
1158     struct exp_visible **visp)
1159 {
1160         /*
1161          * Only a PSEUDO node has a visible list or an exported VROOT
1162          * node may have a visible list.
1163          */
1164         if (! PSEUDO(exi))
1165                 exi = get_root_export(exi);
1166 
1167         for (*visp = exi->exi_visible; *visp != NULL; *visp = (*visp)->vis_next)
1168                 if ((u_longlong_t)ino == (*visp)->vis_ino) {
1169                         return (1);
1170                 }
1171 
1172         return (0);
1173 }
1174 
1175 /*
1176  * Get the change attribute from visible and returns TRUE.
1177  * If the change value is not available returns FALSE.
1178  */
1179 bool_t
1180 nfs_visible_change(struct exportinfo *exi, vnode_t *vp, timespec_t *change)
1181 {
1182         struct exp_visible *visp;
1183         fid_t fid;
1184         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, 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);


 641         timespec_t now;
 642         nfs_export_t *ne = nfs_get_export();
 643 
 644         ASSERT(RW_WRITE_HELD(&ne->exported_lock));
 645 
 646         gethrestime(&now);
 647 
 648         vp = exip->exi_vp;
 649         VN_HOLD(vp);
 650         exportdir = 1;
 651 
 652         for (;;) {
 653 
 654                 bzero(&fid, sizeof (fid));
 655                 fid.fid_len = MAXFIDSZ;
 656                 error = vop_fid_pseudo(vp, &fid);
 657                 if (error)
 658                         break;
 659 
 660                 /*
 661                  * The root of the file system, or the zone's root for
 662                  * in-zone NFS service needs special handling
 663                  */
 664                 if (vp->v_flag & VROOT || VN_IS_CURZONEROOT(vp)) {
 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_IS_CURZONEROOT(vp)) {
 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 or zone's root 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         tnode = exip->exi_tree;
 840         /*
 841          * The unshared exportinfo was unlinked in unexport().
 842          * Zeroing tree_exi ensures that we will skip it.
 843          */
 844         tnode->tree_exi = NULL;
 845 
 846         if (tnode->tree_vis != NULL) /* system root has tree_vis == NULL */
 847                 tnode->tree_vis->vis_exported = 0;
 848 
 849         while (tnode != NULL) {
 850 
 851                 /*
 852                  * Stop at VROOT (or zone root) node which is exported or has
 853                  * child.
 854                  */
 855                 if (TREE_ROOT(tnode) &&
 856                     (TREE_EXPORTED(tnode) || tnode->tree_child_first != NULL))
 857                         break;
 858 
 859                 /* Release pseudo export if it has no child */
 860                 if (TREE_ROOT(tnode) && !TREE_EXPORTED(tnode) &&
 861                     tnode->tree_child_first == NULL) {
 862                         mutex_enter(&nfs_exi_id_lock);
 863                         avl_remove(&exi_id_tree, tnode->tree_exi);
 864                         mutex_exit(&nfs_exi_id_lock);
 865                         export_unlink(ne, tnode->tree_exi);
 866                         exi_rele(tnode->tree_exi);
 867                 }
 868 
 869                 /* Release visible in parent's exportinfo */
 870                 if (tnode->tree_vis != NULL)
 871                         less_visible(vis2exi(tnode), tnode->tree_vis);
 872 
 873                 /* Continue with parent */
 874                 old_nd = tnode;


 882                 }
 883         }
 884 
 885         /* Update the change timestamp */
 886         if (connect_point != NULL)
 887                 tree_update_change(ne, connect_point, NULL);
 888 }
 889 
 890 /*
 891  * Traverse backward across mountpoint from the
 892  * root vnode of a filesystem to its mounted-on
 893  * vnode.
 894  */
 895 vnode_t *
 896 untraverse(vnode_t *vp)
 897 {
 898         vnode_t *tvp, *nextvp;
 899 
 900         tvp = vp;
 901         for (;;) {
 902                 if (!(tvp->v_flag & VROOT) && !VN_IS_CURZONEROOT(tvp))
 903                         break;
 904 
 905                 /* lock vfs to prevent unmount of this vfs */
 906                 vfs_lock_wait(tvp->v_vfsp);
 907 
 908                 if ((nextvp = tvp->v_vfsp->vfs_vnodecovered) == NULL) {
 909                         vfs_unlock(tvp->v_vfsp);
 910                         break;
 911                 }
 912 
 913                 /*
 914                  * Hold nextvp to prevent unmount.  After unlock vfs and
 915                  * rele tvp, any number of overlays could be unmounted.
 916                  * Putting a hold on vfs_vnodecovered will only allow
 917                  * tvp's vfs to be unmounted. Of course if caller placed
 918                  * extra hold on vp before calling untraverse, the following
 919                  * hold would not be needed.  Since prev actions of caller
 920                  * are unknown, we need to hold here just to be safe.
 921                  */
 922                 VN_HOLD(nextvp);
 923                 vfs_unlock(tvp->v_vfsp);
 924                 VN_RELE(tvp);
 925                 tvp = nextvp;
 926         }
 927 
 928         return (tvp);
 929 }
 930 
 931 /*
 932  * Given an exportinfo, climb up to find the exportinfo for the VROOT
 933  * (or zone root) of the filesystem.
 934  *
 935  * e.g.         /
 936  *              |
 937  *              a (VROOT) pseudo-exportinfo
 938  *              |
 939  *              b
 940  *              |
 941  *              c  #share /a/b/c
 942  *              |
 943  *              d
 944  *
 945  * where c is in the same filesystem as a.
 946  * So, get_root_export(*exportinfo_for_c) returns exportinfo_for_a
 947  *
 948  * If d is shared, then c will be put into a's visible list.
 949  * Note: visible list is per filesystem and is attached to the
 950  * VROOT exportinfo.
 951  */
 952 struct exportinfo *
 953 get_root_export(struct exportinfo *exip)


 962                 }
 963                 tnode = tnode->tree_parent;
 964         }
 965         ASSERT(exi);
 966         return (exi);
 967 }
 968 
 969 /*
 970  * Return true if the supplied vnode has a sub-directory exported.
 971  */
 972 int
 973 has_visible(struct exportinfo *exi, vnode_t *vp)
 974 {
 975         struct exp_visible *visp;
 976         fid_t fid;
 977         bool_t vp_is_exported;
 978 
 979         vp_is_exported = VN_CMP(vp, exi->exi_vp);
 980 
 981         /*
 982          * An exported root vnode has a sub-dir shared if it has a visible
 983          * list.  i.e. if it does not have a visible list, then there is no
 984          * node in this filesystem leads to any other shared node.
 985          */
 986         if (vp_is_exported &&
 987             ((vp->v_flag & VROOT) || VN_IS_CURZONEROOT(vp))) {
 988                 return (exi->exi_visible ? 1 : 0);
 989         }
 990 
 991         /*
 992          * Only the exportinfo of a fs root node may have a visible list.
 993          * Either it is a pseudo root node, or a real exported root node.
 994          */
 995         exi = get_root_export(exi);
 996 
 997         if (!exi->exi_visible)
 998                 return (0);
 999 
1000         /* Get the fid of the vnode */
1001         bzero(&fid, sizeof (fid));
1002         fid.fid_len = MAXFIDSZ;
1003         if (vop_fid_pseudo(vp, &fid) != 0) {
1004                 return (0);
1005         }
1006 
1007         /*
1008          * See if vp is in the visible list of the root node exportinfo.
1009          */


1042          * (it would be a real export then); however,
1043          * it is always visible.  If a pseudo root object
1044          * was exported by server admin, then the entire
1045          * pseudo exportinfo (and all visible entries) would
1046          * be destroyed.  A pseudo exportinfo only exists
1047          * to provide access to real (descendant) export(s).
1048          *
1049          * Previously, rootdir was special cased here; however,
1050          * the export root special case handles the rootdir
1051          * case also.
1052          */
1053         if (VN_CMP(vp, exi->exi_vp)) {
1054                 *expseudo = 0;
1055                 return (1);
1056         }
1057 
1058         /*
1059          * Only a PSEUDO node has a visible list or an exported VROOT
1060          * node may have a visible list.
1061          */
1062         if (!PSEUDO(exi))
1063                 exi = get_root_export(exi);
1064 
1065         /* Get the fid of the vnode */
1066 
1067         bzero(&fid, sizeof (fid));
1068         fid.fid_len = MAXFIDSZ;
1069         if (vop_fid_pseudo(vp, &fid) != 0) {
1070                 *expseudo = 0;
1071                 return (0);
1072         }
1073 
1074         /*
1075          * We can't trust VN_CMP() above because of LOFS.
1076          * Even though VOP_CMP will do the right thing for LOFS
1077          * objects, VN_CMP will short circuit out early when the
1078          * vnode ops ptrs are different.  Just in case we're dealing
1079          * with LOFS, compare exi_fid/fsid here.
1080          *
1081          * expseudo is not set because this is not an export
1082          */


1150 }
1151 
1152 /*
1153  * Returns true if the supplied inode is visible
1154  * in this export.  This function is used by
1155  * readdir which uses inode numbers from the
1156  * directory.
1157  *
1158  * NOTE: this code does not match inode number for ".",
1159  * but it isn't required because NFS4 server rddir
1160  * skips . and .. entries.
1161  */
1162 int
1163 nfs_visible_inode(struct exportinfo *exi, ino64_t ino,
1164     struct exp_visible **visp)
1165 {
1166         /*
1167          * Only a PSEUDO node has a visible list or an exported VROOT
1168          * node may have a visible list.
1169          */
1170         if (!PSEUDO(exi))
1171                 exi = get_root_export(exi);
1172 
1173         for (*visp = exi->exi_visible; *visp != NULL; *visp = (*visp)->vis_next)
1174                 if ((u_longlong_t)ino == (*visp)->vis_ino) {
1175                         return (1);
1176                 }
1177 
1178         return (0);
1179 }
1180 
1181 /*
1182  * Get the change attribute from visible and returns TRUE.
1183  * If the change value is not available returns FALSE.
1184  */
1185 bool_t
1186 nfs_visible_change(struct exportinfo *exi, vnode_t *vp, timespec_t *change)
1187 {
1188         struct exp_visible *visp;
1189         fid_t fid;
1190         treenode_t *node;