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);
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
172 /*
173 * Build up the template fhandle
174 */
175 exi->exi_fh.fh_fsid = fsid;
176 ASSERT(exi->exi_fid.fid_len <= sizeof (exi->exi_fh.fh_xdata));
177 exi->exi_fh.fh_xlen = exi->exi_fid.fid_len;
178 bcopy(exi->exi_fid.fid_data, exi->exi_fh.fh_xdata,
179 exi->exi_fid.fid_len);
180 exi->exi_fh.fh_len = sizeof (exi->exi_fh.fh_data);
181
182 kex = &exi->exi_export;
183 kex->ex_flags = EX_PSEUDO;
184
185 vpathlen = strlen(vp->v_path);
186 kex->ex_pathlen = vpathlen + strlen(PSEUDOFS_SUFFIX);
187 kex->ex_path = kmem_alloc(kex->ex_pathlen + 1, KM_SLEEP);
625 * | | |
626 * j (o) EXPORT,f2 q EXPORT f2
627 *
628 */
629 int
630 treeclimb_export(struct exportinfo *exip)
631 {
632 vnode_t *dvp, *vp;
633 fid_t fid;
634 int error;
635 int exportdir;
636 struct exportinfo *new_exi = exip;
637 struct exp_visible *visp;
638 struct exp_visible *vis_head = NULL;
639 struct vattr va;
640 treenode_t *tree_head = NULL;
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)) {
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 /*
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;
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 }
|
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 /* Caller will set exi_zone... */
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);
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 ASSERT3P(curzone, ==, exip->exi_zone);
647
648 gethrestime(&now);
649
650 vp = exip->exi_vp;
651 VN_HOLD(vp);
652 exportdir = 1;
653
654 for (;;) {
655
656 bzero(&fid, sizeof (fid));
657 fid.fid_len = MAXFIDSZ;
658 error = vop_fid_pseudo(vp, &fid);
659 if (error)
660 break;
661
662 /*
663 * The root of the file system, or the zone's root for
664 * in-zone NFS service needs special handling
665 */
666 if (vp->v_flag & VROOT || VN_IS_CURZONEROOT(vp)) {
678 if (exi != NULL) {
679 /*
680 * Found an export info
681 *
682 * Extend the list of visible
683 * directories whether it's a pseudo
684 * or a real export.
685 */
686 more_visible(exi, tree_head);
687 break; /* and climb no further */
688 }
689
690 /*
691 * Found the root directory of a filesystem
692 * that isn't exported. Need to export
693 * this as a pseudo export so that an NFS v4
694 * client can do lookups in it.
695 */
696 new_exi = pseudo_exportfs(ne, vp, &fid,
697 vis_head, NULL);
698 new_exi->exi_zone = exip->exi_zone;
699 vis_head = NULL;
700 }
701
702 if (VN_IS_CURZONEROOT(vp)) {
703 /* at system root */
704 /*
705 * If sharing "/", new_exi is shared exportinfo
706 * (exip). Otherwise, new_exi is exportinfo
707 * created by pseudo_exportfs() above.
708 */
709 ne->ns_root = tree_prepend_node(tree_head, NULL,
710 new_exi);
711
712 /* Update the change timestamp */
713 tree_update_change(ne, ne->ns_root, &now);
714
715 break;
716 }
717
718 /*
821
822 /*
823 * Walk up the tree and:
824 * 1. release pseudo exportinfo if it has no child
825 * 2. release visible in parent's exportinfo
826 * 3. delete non-exported leaf nodes from tree
827 *
828 * Deleting of nodes will start only if the unshared
829 * node was a leaf node.
830 * Deleting of nodes will finish when we reach a node which
831 * has children or is a real export, then we might still need
832 * to continue releasing visibles, until we reach VROOT or zone's root node.
833 */
834 void
835 treeclimb_unexport(nfs_export_t *ne, struct exportinfo *exip)
836 {
837 treenode_t *tnode, *old_nd;
838 treenode_t *connect_point = NULL;
839
840 ASSERT(RW_WRITE_HELD(&ne->exported_lock));
841 ASSERT(curzone == exip->exi_zone || curzone == global_zone);
842
843 tnode = exip->exi_tree;
844 /*
845 * The unshared exportinfo was unlinked in unexport().
846 * Zeroing tree_exi ensures that we will skip it.
847 */
848 tnode->tree_exi = NULL;
849
850 if (tnode->tree_vis != NULL) /* system root has tree_vis == NULL */
851 tnode->tree_vis->vis_exported = 0;
852
853 while (tnode != NULL) {
854
855 /*
856 * Stop at VROOT (or zone root) node which is exported or has
857 * child.
858 */
859 if (TREE_ROOT(tnode) &&
860 (TREE_EXPORTED(tnode) || tnode->tree_child_first != NULL))
861 break;
970 return (exi);
971 }
972
973 /*
974 * Return true if the supplied vnode has a sub-directory exported.
975 */
976 int
977 has_visible(struct exportinfo *exi, vnode_t *vp)
978 {
979 struct exp_visible *visp;
980 fid_t fid;
981 bool_t vp_is_exported;
982
983 vp_is_exported = VN_CMP(vp, exi->exi_vp);
984
985 /*
986 * An exported root vnode has a sub-dir shared if it has a visible
987 * list. i.e. if it does not have a visible list, then there is no
988 * node in this filesystem leads to any other shared node.
989 */
990 ASSERT3P(curzone, ==, exi->exi_zone);
991 if (vp_is_exported &&
992 ((vp->v_flag & VROOT) || VN_IS_CURZONEROOT(vp))) {
993 return (exi->exi_visible ? 1 : 0);
994 }
995
996 /*
997 * Only the exportinfo of a fs root node may have a visible list.
998 * Either it is a pseudo root node, or a real exported root node.
999 */
1000 exi = get_root_export(exi);
1001
1002 if (!exi->exi_visible)
1003 return (0);
1004
1005 /* Get the fid of the vnode */
1006 bzero(&fid, sizeof (fid));
1007 fid.fid_len = MAXFIDSZ;
1008 if (vop_fid_pseudo(vp, &fid) != 0) {
1009 return (0);
1010 }
|