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