3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
24 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
25 * Copyright (c) 2015, Joyent, Inc.
26 */
27
28 #include <sys/systm.h>
29
30 #include <nfs/nfs.h>
31 #include <nfs/export.h>
32 #include <sys/cmn_err.h>
33 #include <sys/avl.h>
34
35 #define PSEUDOFS_SUFFIX " (pseudo)"
36
37 /*
38 * A version of VOP_FID that deals with a remote VOP_FID for nfs.
39 * If vp is an nfs node, nfs4_fid() returns EREMOTE, nfs3_fid() and nfs_fid()
40 * returns the filehandle of vp as its fid. When nfs uses fid to set the
41 * exportinfo filehandle template, a remote nfs filehandle would be too big for
42 * the fid of the exported directory. This routine remaps the value of the
43 * attribute va_nodeid of vp to be the fid of vp, so that the fid can fit.
44 *
122
123 /*
124 * Create a pseudo export entry
125 *
126 * This is an export entry that's created as the
127 * side-effect of a "real" export. As a part of
128 * a real export, the pathname to the export is
129 * checked to see if all the directory components
130 * are accessible via an NFSv4 client, i.e. are
131 * exported. If treeclimb_export() finds an unexported
132 * mountpoint along the path, then it calls this
133 * function to export it.
134 *
135 * This pseudo export differs from a real export in that
136 * it only allows read-only access. A "visible" list of
137 * directories is added to filter lookup and readdir results
138 * to only contain dirnames which lead to descendant shares.
139 *
140 * A visible list has a per-file-system scope. Any exportinfo
141 * struct (real or pseudo) can have a visible list as long as
142 * a) its export root is VROOT
143 * b) a descendant of the export root is shared
144 */
145 struct exportinfo *
146 pseudo_exportfs(vnode_t *vp, fid_t *fid, struct exp_visible *vis_head,
147 struct exportdata *exdata)
148 {
149 struct exportinfo *exi;
150 struct exportdata *kex;
151 fsid_t fsid;
152 int vpathlen;
153 int i;
154
155 ASSERT(RW_WRITE_HELD(&exported_lock));
156
157 fsid = vp->v_vfsp->vfs_fsid;
158 exi = kmem_zalloc(sizeof (*exi), KM_SLEEP);
159 exi->exi_fsid = fsid;
160 exi->exi_fid = *fid;
161 exi->exi_vp = vp;
162 VN_HOLD(exi->exi_vp);
163 exi->exi_visible = vis_head;
164 exi->exi_count = 1;
165 exi->exi_volatile_dev = (vfssw[vp->v_vfsp->vfs_fstype].vsw_flag &
166 VSW_VOLATILEDEV) ? 1 : 0;
167 mutex_init(&exi->exi_lock, NULL, MUTEX_DEFAULT, NULL);
168
169 /*
170 * Build up the template fhandle
171 */
172 exi->exi_fh.fh_fsid = fsid;
173 ASSERT(exi->exi_fid.fid_len <= sizeof (exi->exi_fh.fh_xdata));
174 exi->exi_fh.fh_xlen = exi->exi_fid.fid_len;
175 bcopy(exi->exi_fid.fid_data, exi->exi_fh.fh_xdata,
176 exi->exi_fid.fid_len);
177 exi->exi_fh.fh_len = sizeof (exi->exi_fh.fh_data);
178
179 kex = &exi->exi_export;
180 kex->ex_flags = EX_PSEUDO;
181
182 vpathlen = strlen(vp->v_path);
183 kex->ex_pathlen = vpathlen + strlen(PSEUDOFS_SUFFIX);
184 kex->ex_path = kmem_alloc(kex->ex_pathlen + 1, KM_SLEEP);
188 (void) strcpy(kex->ex_path + vpathlen, PSEUDOFS_SUFFIX);
189
190 /* Transfer the secinfo data from exdata to this new pseudo node */
191 if (exdata)
192 srv_secinfo_exp2pseu(&exi->exi_export, exdata);
193
194 /*
195 * Initialize auth cache and auth cache lock
196 */
197 for (i = 0; i < AUTH_TABLESIZE; i++) {
198 exi->exi_cache[i] = kmem_alloc(sizeof (avl_tree_t), KM_SLEEP);
199 avl_create(exi->exi_cache[i], nfsauth_cache_clnt_compar,
200 sizeof (struct auth_cache_clnt),
201 offsetof(struct auth_cache_clnt, authc_link));
202 }
203 rw_init(&exi->exi_cache_lock, NULL, RW_DEFAULT, NULL);
204
205 /*
206 * Insert the new entry at the front of the export list
207 */
208 export_link(exi);
209
210 return (exi);
211 }
212
213 /*
214 * Free a list of visible directories
215 */
216 void
217 free_visible(struct exp_visible *head)
218 {
219 struct exp_visible *visp, *next;
220
221 for (visp = head; visp; visp = next) {
222 if (visp->vis_vp != NULL)
223 VN_RELE(visp->vis_vp);
224
225 next = visp->vis_next;
226 srv_secinfo_list_free(visp->vis_secinfo, visp->vis_seccnt);
227 kmem_free(visp, sizeof (*visp));
228 }
229 }
264 if (n) {
265 tnode->tree_child_first = n;
266 n->tree_parent = tnode;
267 }
268 if (v) {
269 tnode->tree_vis = v;
270 }
271 if (e) {
272 tnode->tree_exi = e;
273 e->exi_tree = tnode;
274 }
275 return (tnode);
276 }
277
278 /*
279 * Removes node from the tree and frees the treenode struct.
280 * Does not free structures pointed by tree_exi and tree_vis,
281 * they should be already freed.
282 */
283 static void
284 tree_remove_node(treenode_t *node)
285 {
286 treenode_t *parent = node->tree_parent;
287 treenode_t *s; /* s for sibling */
288
289 if (parent == NULL) {
290 kmem_free(node, sizeof (*node));
291 ns_root = NULL;
292 return;
293 }
294 /* This node is first child */
295 if (parent->tree_child_first == node) {
296 parent->tree_child_first = node->tree_sibling;
297 /* This node is not first child */
298 } else {
299 s = parent->tree_child_first;
300 while (s->tree_sibling != node)
301 s = s->tree_sibling;
302 s->tree_sibling = s->tree_sibling->tree_sibling;
303 }
304 kmem_free(node, sizeof (*node));
305 }
306
307 /*
308 * When we export a new directory we need to add a new
309 * path segment through the pseudofs to reach the new
310 * directory. This new path is reflected in a list of
311 * directories added to the "visible" list.
420 * tree_head......current head of the NEW treenode chain, in this case it was
421 * already moved down to its child - preparation for another loop
422 *
423 * What will happen to NEW treenodes N1, N2, N3, N4 in more_visible() later:
424 *
425 * N1: is merged - i.e. N1 is kmem_free()d. T0 has a child T1 with the same
426 * tree_vis as N1
427 * N2: is added as a new child of T1
428 * Note: not just N2, but the whole chain N2->N3->N4 is added
429 * N3: not processed separately (it was added together with N2)
430 * Even that N3 and T3 have same tree_vis, they are NOT merged, but will
431 * become duplicates.
432 * N4: not processed separately
433 */
434 static void
435 more_visible(struct exportinfo *exi, treenode_t *tree_head)
436 {
437 struct exp_visible *vp1, *vp2, *vis_head, *tail, *next;
438 int found;
439 treenode_t *child, *curr, *connect_point;
440
441 vis_head = tree_head->tree_vis;
442 connect_point = exi->exi_tree;
443
444 /*
445 * If exportinfo doesn't already have a visible
446 * list just assign the entire supplied list.
447 */
448 if (exi->exi_visible == NULL) {
449 tree_add_child(connect_point, tree_head);
450 exi->exi_visible = vis_head;
451
452 /* Update the change timestamp */
453 tree_update_change(connect_point, &vis_head->vis_change);
454
455 return;
456 }
457
458 /* The outer loop traverses the supplied list. */
459 for (vp1 = vis_head; vp1; vp1 = next) {
460 found = 0;
461 next = vp1->vis_next;
462
463 /* The inner loop searches the exportinfo visible list. */
464 for (vp2 = exi->exi_visible; vp2; vp2 = vp2->vis_next) {
465 tail = vp2;
466 if (EQFID(&vp1->vis_fid, &vp2->vis_fid)) {
467 found = 1;
468 vp2->vis_count++;
469 VN_RELE(vp1->vis_vp);
470 /* Transfer vis_exported from vp1 to vp2. */
471 if (vp1->vis_exported && !vp2->vis_exported)
472 vp2->vis_exported = 1;
473 kmem_free(vp1, sizeof (*vp1));
493 * connect_point for the curr->tree_vis. No need for EQFID.
494 */
495 child = tree_find_child_by_vis(connect_point, curr->tree_vis);
496
497 /*
498 * Merging cannot be done if a valid child->tree_exi would
499 * be overwritten by a new curr->tree_exi.
500 */
501 if (child &&
502 (child->tree_exi == NULL || curr->tree_exi == NULL)) {
503 if (curr->tree_exi) { /* Transfer the exportinfo */
504 child->tree_exi = curr->tree_exi;
505 child->tree_exi->exi_tree = child;
506 }
507 kmem_free(curr, sizeof (treenode_t));
508 connect_point = child;
509 } else { /* Branching */
510 tree_add_child(connect_point, curr);
511
512 /* Update the change timestamp */
513 tree_update_change(connect_point,
514 &curr->tree_vis->vis_change);
515
516 connect_point = NULL;
517 }
518 }
519 }
520
521 /*
522 * Remove one visible entry from the pseudo exportfs.
523 *
524 * When we unexport a directory, we have to remove path
525 * components from the visible list in the pseudo exportfs
526 * entry. The supplied visible contains one fid of one path
527 * component. The visible list of the export
528 * is checked against provided visible, matching fid has its
529 * reference count decremented. If a reference count drops to
530 * zero, then it means no paths now use this directory, so its
531 * fid can be removed from the visible list.
532 *
533 * When the last path is removed, the visible list will be null.
610 * (d) (e) f m EXPORT,f1(f2) p
611 * EXPORT EXPORT | |
612 * f1 f2 | |
613 * | | |
614 * j (o) EXPORT,f2 q EXPORT f2
615 *
616 */
617 int
618 treeclimb_export(struct exportinfo *exip)
619 {
620 vnode_t *dvp, *vp;
621 fid_t fid;
622 int error;
623 int exportdir;
624 struct exportinfo *new_exi = exip;
625 struct exp_visible *visp;
626 struct exp_visible *vis_head = NULL;
627 struct vattr va;
628 treenode_t *tree_head = NULL;
629 timespec_t now;
630
631 ASSERT(RW_WRITE_HELD(&exported_lock));
632
633 gethrestime(&now);
634
635 vp = exip->exi_vp;
636 VN_HOLD(vp);
637 exportdir = 1;
638
639 for (;;) {
640
641 bzero(&fid, sizeof (fid));
642 fid.fid_len = MAXFIDSZ;
643 error = vop_fid_pseudo(vp, &fid);
644 if (error)
645 break;
646
647 /*
648 * The root of the file system needs special handling
649 */
650 if (vp->v_flag & VROOT) {
651 if (! exportdir) {
652 struct exportinfo *exi;
653
654 /*
655 * Check if this VROOT dir is already exported.
656 * If so, then attach the pseudonodes. If not,
657 * then continue .. traversal until we hit a
658 * VROOT export (pseudo or real).
659 */
660 exi = checkexport4(&vp->v_vfsp->vfs_fsid, &fid,
661 vp);
662 if (exi != NULL) {
663 /*
664 * Found an export info
665 *
666 * Extend the list of visible
667 * directories whether it's a pseudo
668 * or a real export.
669 */
670 more_visible(exi, tree_head);
671 break; /* and climb no further */
672 }
673
674 /*
675 * Found the root directory of a filesystem
676 * that isn't exported. Need to export
677 * this as a pseudo export so that an NFS v4
678 * client can do lookups in it.
679 */
680 new_exi = pseudo_exportfs(vp, &fid, vis_head,
681 NULL);
682 vis_head = NULL;
683 }
684
685 if (VN_CMP(vp, rootdir)) {
686 /* at system root */
687 /*
688 * If sharing "/", new_exi is shared exportinfo
689 * (exip). Otherwise, new_exi is exportinfo
690 * created by pseudo_exportfs() above.
691 */
692 ns_root = tree_prepend_node(tree_head, NULL,
693 new_exi);
694
695 /* Update the change timestamp */
696 tree_update_change(ns_root, &now);
697
698 break;
699 }
700
701 /*
702 * Traverse across the mountpoint and continue the
703 * climb on the mounted-on filesystem.
704 */
705 vp = untraverse(vp);
706 exportdir = 0;
707 continue;
708 }
709
710 /*
711 * Do a getattr to obtain the nodeid (inode num)
712 * for this vnode.
713 */
714 va.va_mask = AT_NODEID;
715 error = VOP_GETATTR(vp, &va, 0, CRED(), NULL);
716 if (error)
717 break;
718
719 /*
720 * Add this directory fid to visible list
721 */
722 visp = kmem_alloc(sizeof (*visp), KM_SLEEP);
723 VN_HOLD(vp);
724 visp->vis_vp = vp;
725 visp->vis_fid = fid; /* structure copy */
771 * 3. VOP_LOOKUP()
772 * We must free pseudo exportinfos, visibles and treenodes.
773 * Visibles are referenced from treenode_t::tree_vis and
774 * exportinfo_t::exi_visible. To avoid double freeing, only
775 * exi_visible pointer is used, via exi_rele(), for the clean-up.
776 */
777 if (error) {
778 /* Free unconnected visibles, if there are any. */
779 if (vis_head)
780 free_visible(vis_head);
781
782 /* Connect unconnected exportinfo, if there is any. */
783 if (new_exi && new_exi != exip)
784 tree_head = tree_prepend_node(tree_head, NULL, new_exi);
785
786 while (tree_head) {
787 treenode_t *t2 = tree_head;
788 exportinfo_t *e = tree_head->tree_exi;
789 /* exip will be freed in exportfs() */
790 if (e && e != exip) {
791 export_unlink(e);
792 exi_rele(e);
793 }
794 tree_head = tree_head->tree_child_first;
795 kmem_free(t2, sizeof (*t2));
796 }
797 }
798
799 return (error);
800 }
801
802 /*
803 * Walk up the tree and:
804 * 1. release pseudo exportinfo if it has no child
805 * 2. release visible in parent's exportinfo
806 * 3. delete non-exported leaf nodes from tree
807 *
808 * Deleting of nodes will start only if the unshared
809 * node was a leaf node.
810 * Deleting of nodes will finish when we reach a node which
811 * has children or is a real export, then we might still need
812 * to continue releasing visibles, until we reach VROOT node.
813 */
814 void
815 treeclimb_unexport(struct exportinfo *exip)
816 {
817 treenode_t *tnode, *old_nd;
818 treenode_t *connect_point = NULL;
819
820 ASSERT(RW_WRITE_HELD(&exported_lock));
821
822 tnode = exip->exi_tree;
823 /*
824 * The unshared exportinfo was unlinked in unexport().
825 * Zeroing tree_exi ensures that we will skip it.
826 */
827 tnode->tree_exi = NULL;
828
829 if (tnode->tree_vis != NULL) /* system root has tree_vis == NULL */
830 tnode->tree_vis->vis_exported = 0;
831
832 while (tnode != NULL) {
833
834 /* Stop at VROOT node which is exported or has child */
835 if (TREE_ROOT(tnode) &&
836 (TREE_EXPORTED(tnode) || tnode->tree_child_first != NULL))
837 break;
838
839 /* Release pseudo export if it has no child */
840 if (TREE_ROOT(tnode) && !TREE_EXPORTED(tnode) &&
841 tnode->tree_child_first == NULL) {
842 export_unlink(tnode->tree_exi);
843 exi_rele(tnode->tree_exi);
844 }
845
846 /* Release visible in parent's exportinfo */
847 if (tnode->tree_vis != NULL)
848 less_visible(vis2exi(tnode), tnode->tree_vis);
849
850 /* Continue with parent */
851 old_nd = tnode;
852 tnode = tnode->tree_parent;
853
854 /* Remove itself, if this is a leaf and non-exported node */
855 if (old_nd->tree_child_first == NULL &&
856 !TREE_EXPORTED(old_nd)) {
857 tree_remove_node(old_nd);
858 connect_point = tnode;
859 }
860 }
861
862 /* Update the change timestamp */
863 if (connect_point != NULL)
864 tree_update_change(connect_point, NULL);
865 }
866
867 /*
868 * Traverse backward across mountpoint from the
869 * root vnode of a filesystem to its mounted-on
870 * vnode.
871 */
872 vnode_t *
873 untraverse(vnode_t *vp)
874 {
875 vnode_t *tvp, *nextvp;
876
877 tvp = vp;
878 for (;;) {
879 if (! (tvp->v_flag & VROOT))
880 break;
881
882 /* lock vfs to prevent unmount of this vfs */
883 vfs_lock_wait(tvp->v_vfsp);
884
885 if ((nextvp = tvp->v_vfsp->vfs_vnodecovered) == NULL) {
886 vfs_unlock(tvp->v_vfsp);
887 break;
888 }
889
890 /*
891 * Hold nextvp to prevent unmount. After unlock vfs and
892 * rele tvp, any number of overlays could be unmounted.
893 * Putting a hold on vfs_vnodecovered will only allow
894 * tvp's vfs to be unmounted. Of course if caller placed
895 * extra hold on vp before calling untraverse, the following
896 * hold would not be needed. Since prev actions of caller
897 * are unknown, we need to hold here just to be safe.
898 */
899 VN_HOLD(nextvp);
900 vfs_unlock(tvp->v_vfsp);
901 VN_RELE(tvp);
902 tvp = nextvp;
903 }
904
905 return (tvp);
906 }
907
908 /*
909 * Given an exportinfo, climb up to find the exportinfo for the VROOT
910 * of the filesystem.
911 *
912 * e.g. /
913 * |
914 * a (VROOT) pseudo-exportinfo
915 * |
916 * b
917 * |
918 * c #share /a/b/c
919 * |
920 * d
921 *
922 * where c is in the same filesystem as a.
923 * So, get_root_export(*exportinfo_for_c) returns exportinfo_for_a
924 *
925 * If d is shared, then c will be put into a's visible list.
926 * Note: visible list is per filesystem and is attached to the
927 * VROOT exportinfo.
928 */
929 struct exportinfo *
930 get_root_export(struct exportinfo *exip)
931 {
932 treenode_t *tnode = exip->exi_tree;
933 exportinfo_t *exi = NULL;
934
935 while (tnode) {
936 if (TREE_ROOT(tnode)) {
937 exi = tnode->tree_exi;
938 break;
939 }
940 tnode = tnode->tree_parent;
941 }
942 ASSERT(exi);
943 return (exi);
944 }
945
946 /*
947 * Return true if the supplied vnode has a sub-directory exported.
948 */
949 int
950 has_visible(struct exportinfo *exi, vnode_t *vp)
951 {
952 struct exp_visible *visp;
953 fid_t fid;
954 bool_t vp_is_exported;
955
956 vp_is_exported = VN_CMP(vp, exi->exi_vp);
957
958 /*
959 * An exported root vnode has a sub-dir shared if it has a visible list.
960 * i.e. if it does not have a visible list, then there is no node in
961 * this filesystem leads to any other shared node.
962 */
963 if (vp_is_exported && (vp->v_flag & VROOT))
964 return (exi->exi_visible ? 1 : 0);
965
966 /*
967 * Only the exportinfo of a fs root node may have a visible list.
968 * Either it is a pseudo root node, or a real exported root node.
969 */
970 exi = get_root_export(exi);
971
972 if (!exi->exi_visible)
973 return (0);
974
975 /* Get the fid of the vnode */
976 bzero(&fid, sizeof (fid));
977 fid.fid_len = MAXFIDSZ;
978 if (vop_fid_pseudo(vp, &fid) != 0) {
979 return (0);
980 }
981
982 /*
983 * See if vp is in the visible list of the root node exportinfo.
984 */
1017 * (it would be a real export then); however,
1018 * it is always visible. If a pseudo root object
1019 * was exported by server admin, then the entire
1020 * pseudo exportinfo (and all visible entries) would
1021 * be destroyed. A pseudo exportinfo only exists
1022 * to provide access to real (descendant) export(s).
1023 *
1024 * Previously, rootdir was special cased here; however,
1025 * the export root special case handles the rootdir
1026 * case also.
1027 */
1028 if (VN_CMP(vp, exi->exi_vp)) {
1029 *expseudo = 0;
1030 return (1);
1031 }
1032
1033 /*
1034 * Only a PSEUDO node has a visible list or an exported VROOT
1035 * node may have a visible list.
1036 */
1037 if (! PSEUDO(exi))
1038 exi = get_root_export(exi);
1039
1040 /* Get the fid of the vnode */
1041
1042 bzero(&fid, sizeof (fid));
1043 fid.fid_len = MAXFIDSZ;
1044 if (vop_fid_pseudo(vp, &fid) != 0) {
1045 *expseudo = 0;
1046 return (0);
1047 }
1048
1049 /*
1050 * We can't trust VN_CMP() above because of LOFS.
1051 * Even though VOP_CMP will do the right thing for LOFS
1052 * objects, VN_CMP will short circuit out early when the
1053 * vnode ops ptrs are different. Just in case we're dealing
1054 * with LOFS, compare exi_fid/fsid here.
1055 *
1056 * expseudo is not set because this is not an export
1057 */
1125 }
1126
1127 /*
1128 * Returns true if the supplied inode is visible
1129 * in this export. This function is used by
1130 * readdir which uses inode numbers from the
1131 * directory.
1132 *
1133 * NOTE: this code does not match inode number for ".",
1134 * but it isn't required because NFS4 server rddir
1135 * skips . and .. entries.
1136 */
1137 int
1138 nfs_visible_inode(struct exportinfo *exi, ino64_t ino,
1139 struct exp_visible **visp)
1140 {
1141 /*
1142 * Only a PSEUDO node has a visible list or an exported VROOT
1143 * node may have a visible list.
1144 */
1145 if (! PSEUDO(exi))
1146 exi = get_root_export(exi);
1147
1148 for (*visp = exi->exi_visible; *visp != NULL; *visp = (*visp)->vis_next)
1149 if ((u_longlong_t)ino == (*visp)->vis_ino) {
1150 return (1);
1151 }
1152
1153 return (0);
1154 }
1155
1156 /*
1157 * The change attribute value of the root of nfs pseudo namespace.
1158 *
1159 * The ns_root_change is protected by exported_lock because all of the treenode
1160 * operations are protected by exported_lock too.
1161 */
1162 static timespec_t ns_root_change;
1163
1164 /*
1165 * Get the change attribute from visible and returns TRUE.
1166 * If the change value is not available returns FALSE.
1167 */
1168 bool_t
1169 nfs_visible_change(struct exportinfo *exi, vnode_t *vp, timespec_t *change)
1170 {
1171 struct exp_visible *visp;
1172 fid_t fid;
1173 treenode_t *node;
1174
1175 /*
1176 * First check to see if vp is export root.
1177 */
1178 if (VN_CMP(vp, exi->exi_vp))
1179 goto exproot;
1180
1181 /*
1182 * Only a PSEUDO node has a visible list or an exported VROOT
1183 * node may have a visible list.
1184 */
1185 if (!PSEUDO(exi))
1186 exi = get_root_export(exi);
1187
1188 /* Get the fid of the vnode */
1189 bzero(&fid, sizeof (fid));
1190 fid.fid_len = MAXFIDSZ;
1191 if (vop_fid_pseudo(vp, &fid) != 0)
1192 return (FALSE);
1193
1198 * vnode ops ptrs are different. Just in case we're dealing
1199 * with LOFS, compare exi_fid/fsid here.
1200 */
1201 if (EQFID(&exi->exi_fid, &fid) &&
1202 EQFSID(&exi->exi_fsid, &vp->v_vfsp->vfs_fsid))
1203 goto exproot;
1204
1205 /* See if it matches any fid in the visible list */
1206 for (visp = exi->exi_visible; visp; visp = visp->vis_next) {
1207 if (EQFID(&fid, &visp->vis_fid)) {
1208 *change = visp->vis_change;
1209 return (TRUE);
1210 }
1211 }
1212
1213 return (FALSE);
1214
1215 exproot:
1216 /* The VROOT export have its visible available through treenode */
1217 node = exi->exi_tree;
1218 if (node != ns_root) {
1219 ASSERT(node->tree_vis != NULL);
1220 *change = node->tree_vis->vis_change;
1221 } else {
1222 ASSERT(node->tree_vis == NULL);
1223 *change = ns_root_change;
1224 }
1225
1226 return (TRUE);
1227 }
1228
1229 /*
1230 * Update the change attribute value for a particular treenode. The change
1231 * attribute value is stored in the visible attached to the treenode, or in the
1232 * ns_root_change.
1233 *
1234 * If the change value is not supplied, the current time is used.
1235 */
1236 void
1237 tree_update_change(treenode_t *tnode, timespec_t *change)
1238 {
1239 timespec_t *vis_change;
1240
1241 ASSERT(tnode != NULL);
1242 ASSERT((tnode != ns_root && tnode->tree_vis != NULL) ||
1243 (tnode == ns_root && tnode->tree_vis == NULL));
1244
1245 vis_change = tnode == ns_root ? &ns_root_change
1246 : &tnode->tree_vis->vis_change;
1247
1248 if (change != NULL)
1249 *vis_change = *change;
1250 else
1251 gethrestime(vis_change);
1252 }
|
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 /*
27 * Copyright 2018 Nexenta Systems, Inc.
28 * Copyright (c) 2015, Joyent, Inc.
29 */
30
31 #include <sys/systm.h>
32
33 #include <nfs/nfs.h>
34 #include <nfs/export.h>
35 #include <sys/cmn_err.h>
36 #include <sys/avl.h>
37
38 #define PSEUDOFS_SUFFIX " (pseudo)"
39
40 /*
41 * A version of VOP_FID that deals with a remote VOP_FID for nfs.
42 * If vp is an nfs node, nfs4_fid() returns EREMOTE, nfs3_fid() and nfs_fid()
43 * returns the filehandle of vp as its fid. When nfs uses fid to set the
44 * exportinfo filehandle template, a remote nfs filehandle would be too big for
45 * the fid of the exported directory. This routine remaps the value of the
46 * attribute va_nodeid of vp to be the fid of vp, so that the fid can fit.
47 *
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);
192 (void) strcpy(kex->ex_path + vpathlen, PSEUDOFS_SUFFIX);
193
194 /* Transfer the secinfo data from exdata to this new pseudo node */
195 if (exdata)
196 srv_secinfo_exp2pseu(&exi->exi_export, exdata);
197
198 /*
199 * Initialize auth cache and auth cache lock
200 */
201 for (i = 0; i < AUTH_TABLESIZE; i++) {
202 exi->exi_cache[i] = kmem_alloc(sizeof (avl_tree_t), KM_SLEEP);
203 avl_create(exi->exi_cache[i], nfsauth_cache_clnt_compar,
204 sizeof (struct auth_cache_clnt),
205 offsetof(struct auth_cache_clnt, authc_link));
206 }
207 rw_init(&exi->exi_cache_lock, NULL, RW_DEFAULT, NULL);
208
209 /*
210 * Insert the new entry at the front of the export list
211 */
212 export_link(ne, exi);
213
214 /*
215 * Initialize exi_id and exi_kstats
216 */
217 mutex_enter(&nfs_exi_id_lock);
218 exi->exi_id = exi_id_get_next();
219 avl_add(&exi_id_tree, exi);
220 mutex_exit(&nfs_exi_id_lock);
221
222 return (exi);
223 }
224
225 /*
226 * Free a list of visible directories
227 */
228 void
229 free_visible(struct exp_visible *head)
230 {
231 struct exp_visible *visp, *next;
232
233 for (visp = head; visp; visp = next) {
234 if (visp->vis_vp != NULL)
235 VN_RELE(visp->vis_vp);
236
237 next = visp->vis_next;
238 srv_secinfo_list_free(visp->vis_secinfo, visp->vis_seccnt);
239 kmem_free(visp, sizeof (*visp));
240 }
241 }
276 if (n) {
277 tnode->tree_child_first = n;
278 n->tree_parent = tnode;
279 }
280 if (v) {
281 tnode->tree_vis = v;
282 }
283 if (e) {
284 tnode->tree_exi = e;
285 e->exi_tree = tnode;
286 }
287 return (tnode);
288 }
289
290 /*
291 * Removes node from the tree and frees the treenode struct.
292 * Does not free structures pointed by tree_exi and tree_vis,
293 * they should be already freed.
294 */
295 static void
296 tree_remove_node(nfs_export_t *ne, treenode_t *node)
297 {
298 treenode_t *parent = node->tree_parent;
299 treenode_t *s; /* s for sibling */
300
301 if (parent == NULL) {
302 kmem_free(node, sizeof (*node));
303 ne->ns_root = NULL;
304 return;
305 }
306 /* This node is first child */
307 if (parent->tree_child_first == node) {
308 parent->tree_child_first = node->tree_sibling;
309 /* This node is not first child */
310 } else {
311 s = parent->tree_child_first;
312 while (s->tree_sibling != node)
313 s = s->tree_sibling;
314 s->tree_sibling = s->tree_sibling->tree_sibling;
315 }
316 kmem_free(node, sizeof (*node));
317 }
318
319 /*
320 * When we export a new directory we need to add a new
321 * path segment through the pseudofs to reach the new
322 * directory. This new path is reflected in a list of
323 * directories added to the "visible" list.
432 * tree_head......current head of the NEW treenode chain, in this case it was
433 * already moved down to its child - preparation for another loop
434 *
435 * What will happen to NEW treenodes N1, N2, N3, N4 in more_visible() later:
436 *
437 * N1: is merged - i.e. N1 is kmem_free()d. T0 has a child T1 with the same
438 * tree_vis as N1
439 * N2: is added as a new child of T1
440 * Note: not just N2, but the whole chain N2->N3->N4 is added
441 * N3: not processed separately (it was added together with N2)
442 * Even that N3 and T3 have same tree_vis, they are NOT merged, but will
443 * become duplicates.
444 * N4: not processed separately
445 */
446 static void
447 more_visible(struct exportinfo *exi, treenode_t *tree_head)
448 {
449 struct exp_visible *vp1, *vp2, *vis_head, *tail, *next;
450 int found;
451 treenode_t *child, *curr, *connect_point;
452 nfs_export_t *ne = nfs_get_export();
453
454 vis_head = tree_head->tree_vis;
455 connect_point = exi->exi_tree;
456
457 /*
458 * If exportinfo doesn't already have a visible
459 * list just assign the entire supplied list.
460 */
461 if (exi->exi_visible == NULL) {
462 tree_add_child(connect_point, tree_head);
463 exi->exi_visible = vis_head;
464
465 /* Update the change timestamp */
466 tree_update_change(ne, connect_point, &vis_head->vis_change);
467
468 return;
469 }
470
471 /* The outer loop traverses the supplied list. */
472 for (vp1 = vis_head; vp1; vp1 = next) {
473 found = 0;
474 next = vp1->vis_next;
475
476 /* The inner loop searches the exportinfo visible list. */
477 for (vp2 = exi->exi_visible; vp2; vp2 = vp2->vis_next) {
478 tail = vp2;
479 if (EQFID(&vp1->vis_fid, &vp2->vis_fid)) {
480 found = 1;
481 vp2->vis_count++;
482 VN_RELE(vp1->vis_vp);
483 /* Transfer vis_exported from vp1 to vp2. */
484 if (vp1->vis_exported && !vp2->vis_exported)
485 vp2->vis_exported = 1;
486 kmem_free(vp1, sizeof (*vp1));
506 * connect_point for the curr->tree_vis. No need for EQFID.
507 */
508 child = tree_find_child_by_vis(connect_point, curr->tree_vis);
509
510 /*
511 * Merging cannot be done if a valid child->tree_exi would
512 * be overwritten by a new curr->tree_exi.
513 */
514 if (child &&
515 (child->tree_exi == NULL || curr->tree_exi == NULL)) {
516 if (curr->tree_exi) { /* Transfer the exportinfo */
517 child->tree_exi = curr->tree_exi;
518 child->tree_exi->exi_tree = child;
519 }
520 kmem_free(curr, sizeof (treenode_t));
521 connect_point = child;
522 } else { /* Branching */
523 tree_add_child(connect_point, curr);
524
525 /* Update the change timestamp */
526 tree_update_change(ne, connect_point,
527 &curr->tree_vis->vis_change);
528
529 connect_point = NULL;
530 }
531 }
532 }
533
534 /*
535 * Remove one visible entry from the pseudo exportfs.
536 *
537 * When we unexport a directory, we have to remove path
538 * components from the visible list in the pseudo exportfs
539 * entry. The supplied visible contains one fid of one path
540 * component. The visible list of the export
541 * is checked against provided visible, matching fid has its
542 * reference count decremented. If a reference count drops to
543 * zero, then it means no paths now use this directory, so its
544 * fid can be removed from the visible list.
545 *
546 * When the last path is removed, the visible list will be null.
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, ne->exi_root->exi_vp);
725 exportdir = 0;
726 continue;
727 }
728
729 /*
730 * Do a getattr to obtain the nodeid (inode num)
731 * for this vnode.
732 */
733 va.va_mask = AT_NODEID;
734 error = VOP_GETATTR(vp, &va, 0, CRED(), NULL);
735 if (error)
736 break;
737
738 /*
739 * Add this directory fid to visible list
740 */
741 visp = kmem_alloc(sizeof (*visp), KM_SLEEP);
742 VN_HOLD(vp);
743 visp->vis_vp = vp;
744 visp->vis_fid = fid; /* structure copy */
790 * 3. VOP_LOOKUP()
791 * We must free pseudo exportinfos, visibles and treenodes.
792 * Visibles are referenced from treenode_t::tree_vis and
793 * exportinfo_t::exi_visible. To avoid double freeing, only
794 * exi_visible pointer is used, via exi_rele(), for the clean-up.
795 */
796 if (error) {
797 /* Free unconnected visibles, if there are any. */
798 if (vis_head)
799 free_visible(vis_head);
800
801 /* Connect unconnected exportinfo, if there is any. */
802 if (new_exi && new_exi != exip)
803 tree_head = tree_prepend_node(tree_head, NULL, new_exi);
804
805 while (tree_head) {
806 treenode_t *t2 = tree_head;
807 exportinfo_t *e = tree_head->tree_exi;
808 /* exip will be freed in exportfs() */
809 if (e && e != exip) {
810 mutex_enter(&nfs_exi_id_lock);
811 avl_remove(&exi_id_tree, e);
812 mutex_exit(&nfs_exi_id_lock);
813 export_unlink(ne, e);
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 vnode_t *
915 untraverse(vnode_t *vp, vnode_t *zone_rootvp)
916 {
917 vnode_t *tvp, *nextvp;
918
919 tvp = vp;
920 for (;;) {
921 if (!(tvp->v_flag & VROOT) && !VN_CMP(tvp, zone_rootvp))
922 break;
923
924 /* lock vfs to prevent unmount of this vfs */
925 vfs_lock_wait(tvp->v_vfsp);
926
927 if ((nextvp = tvp->v_vfsp->vfs_vnodecovered) == NULL) {
928 vfs_unlock(tvp->v_vfsp);
929 break;
930 }
931
932 /*
933 * Hold nextvp to prevent unmount. After unlock vfs and
934 * rele tvp, any number of overlays could be unmounted.
935 * Putting a hold on vfs_vnodecovered will only allow
936 * tvp's vfs to be unmounted. Of course if caller placed
937 * extra hold on vp before calling untraverse, the following
938 * hold would not be needed. Since prev actions of caller
939 * are unknown, we need to hold here just to be safe.
940 */
941 VN_HOLD(nextvp);
942 vfs_unlock(tvp->v_vfsp);
943 VN_RELE(tvp);
944 tvp = nextvp;
945 }
946
947 return (tvp);
948 }
949
950 /*
951 * Given an exportinfo, climb up to find the exportinfo for the VROOT
952 * (or zone root) of the filesystem.
953 *
954 * e.g. /
955 * |
956 * a (VROOT) pseudo-exportinfo
957 * |
958 * b
959 * |
960 * c #share /a/b/c
961 * |
962 * d
963 *
964 * where c is in the same filesystem as a.
965 * So, get_root_export(*exportinfo_for_c) returns exportinfo_for_a
966 *
967 * If d is shared, then c will be put into a's visible list.
968 * Note: visible list is per filesystem and is attached to the
969 * VROOT exportinfo. Returned exi does NOT have a new hold.
970 */
971 struct exportinfo *
972 get_root_export(struct exportinfo *exip)
973 {
974 treenode_t *tnode = exip->exi_tree;
975 exportinfo_t *exi = NULL;
976
977 while (tnode) {
978 if (TREE_ROOT(tnode)) {
979 exi = tnode->tree_exi;
980 break;
981 }
982 tnode = tnode->tree_parent;
983 }
984 ASSERT(exi);
985 return (exi);
986 }
987
988 /*
989 * Return true if the supplied vnode has a sub-directory exported.
990 */
991 int
992 has_visible(struct exportinfo *exi, vnode_t *vp)
993 {
994 struct exp_visible *visp;
995 fid_t fid;
996 bool_t vp_is_exported;
997
998 vp_is_exported = VN_CMP(vp, exi->exi_vp);
999
1000 /*
1001 * An exported root vnode has a sub-dir shared if it has a visible
1002 * list. i.e. if it does not have a visible list, then there is no
1003 * node in this filesystem leads to any other shared node.
1004 */
1005 ASSERT3P(curzone->zone_id, ==, exi->exi_zoneid);
1006 if (vp_is_exported &&
1007 ((vp->v_flag & VROOT) || VN_IS_CURZONEROOT(vp))) {
1008 return (exi->exi_visible ? 1 : 0);
1009 }
1010
1011 /*
1012 * Only the exportinfo of a fs root node may have a visible list.
1013 * Either it is a pseudo root node, or a real exported root node.
1014 */
1015 exi = get_root_export(exi);
1016
1017 if (!exi->exi_visible)
1018 return (0);
1019
1020 /* Get the fid of the vnode */
1021 bzero(&fid, sizeof (fid));
1022 fid.fid_len = MAXFIDSZ;
1023 if (vop_fid_pseudo(vp, &fid) != 0) {
1024 return (0);
1025 }
1026
1027 /*
1028 * See if vp is in the visible list of the root node exportinfo.
1029 */
1062 * (it would be a real export then); however,
1063 * it is always visible. If a pseudo root object
1064 * was exported by server admin, then the entire
1065 * pseudo exportinfo (and all visible entries) would
1066 * be destroyed. A pseudo exportinfo only exists
1067 * to provide access to real (descendant) export(s).
1068 *
1069 * Previously, rootdir was special cased here; however,
1070 * the export root special case handles the rootdir
1071 * case also.
1072 */
1073 if (VN_CMP(vp, exi->exi_vp)) {
1074 *expseudo = 0;
1075 return (1);
1076 }
1077
1078 /*
1079 * Only a PSEUDO node has a visible list or an exported VROOT
1080 * node may have a visible list.
1081 */
1082 if (!PSEUDO(exi))
1083 exi = get_root_export(exi);
1084
1085 /* Get the fid of the vnode */
1086
1087 bzero(&fid, sizeof (fid));
1088 fid.fid_len = MAXFIDSZ;
1089 if (vop_fid_pseudo(vp, &fid) != 0) {
1090 *expseudo = 0;
1091 return (0);
1092 }
1093
1094 /*
1095 * We can't trust VN_CMP() above because of LOFS.
1096 * Even though VOP_CMP will do the right thing for LOFS
1097 * objects, VN_CMP will short circuit out early when the
1098 * vnode ops ptrs are different. Just in case we're dealing
1099 * with LOFS, compare exi_fid/fsid here.
1100 *
1101 * expseudo is not set because this is not an export
1102 */
1170 }
1171
1172 /*
1173 * Returns true if the supplied inode is visible
1174 * in this export. This function is used by
1175 * readdir which uses inode numbers from the
1176 * directory.
1177 *
1178 * NOTE: this code does not match inode number for ".",
1179 * but it isn't required because NFS4 server rddir
1180 * skips . and .. entries.
1181 */
1182 int
1183 nfs_visible_inode(struct exportinfo *exi, ino64_t ino,
1184 struct exp_visible **visp)
1185 {
1186 /*
1187 * Only a PSEUDO node has a visible list or an exported VROOT
1188 * node may have a visible list.
1189 */
1190 if (!PSEUDO(exi))
1191 exi = get_root_export(exi);
1192
1193 for (*visp = exi->exi_visible; *visp != NULL; *visp = (*visp)->vis_next)
1194 if ((u_longlong_t)ino == (*visp)->vis_ino) {
1195 return (1);
1196 }
1197
1198 return (0);
1199 }
1200
1201 /*
1202 * Get the change attribute from visible and returns TRUE.
1203 * If the change value is not available returns FALSE.
1204 */
1205 bool_t
1206 nfs_visible_change(struct exportinfo *exi, vnode_t *vp, timespec_t *change)
1207 {
1208 struct exp_visible *visp;
1209 fid_t fid;
1210 treenode_t *node;
1211 nfs_export_t *ne = nfs_get_export();
1212
1213 /*
1214 * First check to see if vp is export root.
1215 */
1216 if (VN_CMP(vp, exi->exi_vp))
1217 goto exproot;
1218
1219 /*
1220 * Only a PSEUDO node has a visible list or an exported VROOT
1221 * node may have a visible list.
1222 */
1223 if (!PSEUDO(exi))
1224 exi = get_root_export(exi);
1225
1226 /* Get the fid of the vnode */
1227 bzero(&fid, sizeof (fid));
1228 fid.fid_len = MAXFIDSZ;
1229 if (vop_fid_pseudo(vp, &fid) != 0)
1230 return (FALSE);
1231
1236 * vnode ops ptrs are different. Just in case we're dealing
1237 * with LOFS, compare exi_fid/fsid here.
1238 */
1239 if (EQFID(&exi->exi_fid, &fid) &&
1240 EQFSID(&exi->exi_fsid, &vp->v_vfsp->vfs_fsid))
1241 goto exproot;
1242
1243 /* See if it matches any fid in the visible list */
1244 for (visp = exi->exi_visible; visp; visp = visp->vis_next) {
1245 if (EQFID(&fid, &visp->vis_fid)) {
1246 *change = visp->vis_change;
1247 return (TRUE);
1248 }
1249 }
1250
1251 return (FALSE);
1252
1253 exproot:
1254 /* The VROOT export have its visible available through treenode */
1255 node = exi->exi_tree;
1256 if (node != ne->ns_root) {
1257 ASSERT(node->tree_vis != NULL);
1258 *change = node->tree_vis->vis_change;
1259 } else {
1260 ASSERT(node->tree_vis == NULL);
1261 *change = ne->ns_root_change;
1262 }
1263 return (TRUE);
1264 }
1265
1266 /*
1267 * Update the change attribute value for a particular treenode. The change
1268 * attribute value is stored in the visible attached to the treenode, or in the
1269 * ns_root_change.
1270 *
1271 * If the change value is not supplied, the current time is used.
1272 */
1273 void
1274 tree_update_change(nfs_export_t *ne, treenode_t *tnode, timespec_t *change)
1275 {
1276 timespec_t *vis_change;
1277
1278 ASSERT(tnode != NULL);
1279 ASSERT((tnode != ne->ns_root && tnode->tree_vis != NULL) ||
1280 (tnode == ne->ns_root && tnode->tree_vis == NULL));
1281
1282 vis_change = tnode == ne->ns_root ? &ne->ns_root_change
1283 : &tnode->tree_vis->vis_change;
1284
1285 if (change != NULL)
1286 *vis_change = *change;
1287 else
1288 gethrestime(vis_change);
1289 }
|