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;
|