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 *
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,
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) {
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)
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
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 *
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
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,
191 (void) strcpy(kex->ex_path + vpathlen, PSEUDOFS_SUFFIX);
192
193 /* Transfer the secinfo data from exdata to this new pseudo node */
194 if (exdata)
195 srv_secinfo_exp2pseu(&exi->exi_export, exdata);
196
197 /*
198 * Initialize auth cache and auth cache lock
199 */
200 for (i = 0; i < AUTH_TABLESIZE; i++) {
201 exi->exi_cache[i] = kmem_alloc(sizeof (avl_tree_t), KM_SLEEP);
202 avl_create(exi->exi_cache[i], nfsauth_cache_clnt_compar,
203 sizeof (struct auth_cache_clnt),
204 offsetof(struct auth_cache_clnt, authc_link));
205 }
206 rw_init(&exi->exi_cache_lock, NULL, RW_DEFAULT, NULL);
207
208 /*
209 * Insert the new entry at the front of the export list
210 */
211 export_link(ne, exi);
212
213 /*
214 * Initialize exi_id and exi_kstats
215 */
216 mutex_enter(&nfs_exi_id_lock);
217 exi->exi_id = exi_id_get_next();
218 avl_add(&exi_id_tree, exi);
219 mutex_exit(&nfs_exi_id_lock);
220 exi->exi_kstats = exp_kstats_init(getzoneid(), exi->exi_id,
221 kex->ex_path, vpathlen, TRUE);
222
223 return (exi);
224 }
225
226 /*
227 * Free a list of visible directories
228 */
229 void
230 free_visible(struct exp_visible *head)
231 {
232 struct exp_visible *visp, *next;
233
234 for (visp = head; visp; visp = next) {
235 if (visp->vis_vp != NULL)
236 VN_RELE(visp->vis_vp);
237
238 next = visp->vis_next;
239 srv_secinfo_list_free(visp->vis_secinfo, visp->vis_seccnt);
240 kmem_free(visp, sizeof (*visp));
241 }
242 }
277 if (n) {
278 tnode->tree_child_first = n;
279 n->tree_parent = tnode;
280 }
281 if (v) {
282 tnode->tree_vis = v;
283 }
284 if (e) {
285 tnode->tree_exi = e;
286 e->exi_tree = tnode;
287 }
288 return (tnode);
289 }
290
291 /*
292 * Removes node from the tree and frees the treenode struct.
293 * Does not free structures pointed by tree_exi and tree_vis,
294 * they should be already freed.
295 */
296 static void
297 tree_remove_node(nfs_export_t *ne, treenode_t *node)
298 {
299 treenode_t *parent = node->tree_parent;
300 treenode_t *s; /* s for sibling */
301
302 if (parent == NULL) {
303 kmem_free(node, sizeof (*node));
304 ne->ns_root = NULL;
305 return;
306 }
307 /* This node is first child */
308 if (parent->tree_child_first == node) {
309 parent->tree_child_first = node->tree_sibling;
310 /* This node is not first child */
311 } else {
312 s = parent->tree_child_first;
313 while (s->tree_sibling != node)
314 s = s->tree_sibling;
315 s->tree_sibling = s->tree_sibling->tree_sibling;
316 }
317 kmem_free(node, sizeof (*node));
318 }
319
320 /*
321 * When we export a new directory we need to add a new
322 * path segment through the pseudofs to reach the new
323 * directory. This new path is reflected in a list of
324 * directories added to the "visible" list.
433 * tree_head......current head of the NEW treenode chain, in this case it was
434 * already moved down to its child - preparation for another loop
435 *
436 * What will happen to NEW treenodes N1, N2, N3, N4 in more_visible() later:
437 *
438 * N1: is merged - i.e. N1 is kmem_free()d. T0 has a child T1 with the same
439 * tree_vis as N1
440 * N2: is added as a new child of T1
441 * Note: not just N2, but the whole chain N2->N3->N4 is added
442 * N3: not processed separately (it was added together with N2)
443 * Even that N3 and T3 have same tree_vis, they are NOT merged, but will
444 * become duplicates.
445 * N4: not processed separately
446 */
447 static void
448 more_visible(struct exportinfo *exi, treenode_t *tree_head)
449 {
450 struct exp_visible *vp1, *vp2, *vis_head, *tail, *next;
451 int found;
452 treenode_t *child, *curr, *connect_point;
453 nfs_export_t *ne = nfs_get_export();
454
455 vis_head = tree_head->tree_vis;
456 connect_point = exi->exi_tree;
457
458 /*
459 * If exportinfo doesn't already have a visible
460 * list just assign the entire supplied list.
461 */
462 if (exi->exi_visible == NULL) {
463 tree_add_child(connect_point, tree_head);
464 exi->exi_visible = vis_head;
465
466 /* Update the change timestamp */
467 tree_update_change(ne, connect_point, &vis_head->vis_change);
468
469 return;
470 }
471
472 /* The outer loop traverses the supplied list. */
473 for (vp1 = vis_head; vp1; vp1 = next) {
474 found = 0;
475 next = vp1->vis_next;
476
477 /* The inner loop searches the exportinfo visible list. */
478 for (vp2 = exi->exi_visible; vp2; vp2 = vp2->vis_next) {
479 tail = vp2;
480 if (EQFID(&vp1->vis_fid, &vp2->vis_fid)) {
481 found = 1;
482 vp2->vis_count++;
483 VN_RELE(vp1->vis_vp);
484 /* Transfer vis_exported from vp1 to vp2. */
485 if (vp1->vis_exported && !vp2->vis_exported)
486 vp2->vis_exported = 1;
487 kmem_free(vp1, sizeof (*vp1));
507 * connect_point for the curr->tree_vis. No need for EQFID.
508 */
509 child = tree_find_child_by_vis(connect_point, curr->tree_vis);
510
511 /*
512 * Merging cannot be done if a valid child->tree_exi would
513 * be overwritten by a new curr->tree_exi.
514 */
515 if (child &&
516 (child->tree_exi == NULL || curr->tree_exi == NULL)) {
517 if (curr->tree_exi) { /* Transfer the exportinfo */
518 child->tree_exi = curr->tree_exi;
519 child->tree_exi->exi_tree = child;
520 }
521 kmem_free(curr, sizeof (treenode_t));
522 connect_point = child;
523 } else { /* Branching */
524 tree_add_child(connect_point, curr);
525
526 /* Update the change timestamp */
527 tree_update_change(ne, connect_point,
528 &curr->tree_vis->vis_change);
529
530 connect_point = NULL;
531 }
532 }
533 }
534
535 /*
536 * Remove one visible entry from the pseudo exportfs.
537 *
538 * When we unexport a directory, we have to remove path
539 * components from the visible list in the pseudo exportfs
540 * entry. The supplied visible contains one fid of one path
541 * component. The visible list of the export
542 * is checked against provided visible, matching fid has its
543 * reference count decremented. If a reference count drops to
544 * zero, then it means no paths now use this directory, so its
545 * fid can be removed from the visible list.
546 *
547 * When the last path is removed, the visible list will be null.
624 * (d) (e) f m EXPORT,f1(f2) p
625 * EXPORT EXPORT | |
626 * f1 f2 | |
627 * | | |
628 * j (o) EXPORT,f2 q EXPORT f2
629 *
630 */
631 int
632 treeclimb_export(struct exportinfo *exip)
633 {
634 vnode_t *dvp, *vp;
635 fid_t fid;
636 int error;
637 int exportdir;
638 struct exportinfo *new_exi = exip;
639 struct exp_visible *visp;
640 struct exp_visible *vis_head = NULL;
641 struct vattr va;
642 treenode_t *tree_head = NULL;
643 timespec_t now;
644 nfs_export_t *ne = nfs_get_export();
645
646 ASSERT(RW_WRITE_HELD(&ne->exported_lock));
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 needs special handling
664 */
665 if (vp->v_flag & VROOT) {
666 if (! exportdir) {
675 exi = checkexport4(&vp->v_vfsp->vfs_fsid, &fid,
676 vp);
677 if (exi != NULL) {
678 /*
679 * Found an export info
680 *
681 * Extend the list of visible
682 * directories whether it's a pseudo
683 * or a real export.
684 */
685 more_visible(exi, tree_head);
686 break; /* and climb no further */
687 }
688
689 /*
690 * Found the root directory of a filesystem
691 * that isn't exported. Need to export
692 * this as a pseudo export so that an NFS v4
693 * client can do lookups in it.
694 */
695 new_exi = pseudo_exportfs(ne, vp, &fid,
696 vis_head, NULL);
697 vis_head = NULL;
698 }
699
700 if (VN_CMP(vp, ZONE_ROOTVP())) {
701 /* at system root */
702 /*
703 * If sharing "/", new_exi is shared exportinfo
704 * (exip). Otherwise, new_exi is exportinfo
705 * created by pseudo_exportfs() above.
706 */
707 ne->ns_root = tree_prepend_node(tree_head, NULL,
708 new_exi);
709
710 /* Update the change timestamp */
711 tree_update_change(ne, ne->ns_root, &now);
712
713 break;
714 }
715
716 /*
717 * Traverse across the mountpoint and continue the
718 * climb on the mounted-on filesystem.
719 */
720 vp = untraverse(vp);
721 exportdir = 0;
722 continue;
723 }
724
725 /*
726 * Do a getattr to obtain the nodeid (inode num)
727 * for this vnode.
728 */
729 va.va_mask = AT_NODEID;
730 error = VOP_GETATTR(vp, &va, 0, CRED(), NULL);
731 if (error)
786 * 3. VOP_LOOKUP()
787 * We must free pseudo exportinfos, visibles and treenodes.
788 * Visibles are referenced from treenode_t::tree_vis and
789 * exportinfo_t::exi_visible. To avoid double freeing, only
790 * exi_visible pointer is used, via exi_rele(), for the clean-up.
791 */
792 if (error) {
793 /* Free unconnected visibles, if there are any. */
794 if (vis_head)
795 free_visible(vis_head);
796
797 /* Connect unconnected exportinfo, if there is any. */
798 if (new_exi && new_exi != exip)
799 tree_head = tree_prepend_node(tree_head, NULL, new_exi);
800
801 while (tree_head) {
802 treenode_t *t2 = tree_head;
803 exportinfo_t *e = tree_head->tree_exi;
804 /* exip will be freed in exportfs() */
805 if (e && e != exip) {
806 exp_kstats_delete(e->exi_kstats);
807 mutex_enter(&nfs_exi_id_lock);
808 avl_remove(&exi_id_tree, e);
809 mutex_exit(&nfs_exi_id_lock);
810 export_unlink(ne, e);
811 exi_rele(&e);
812 }
813 tree_head = tree_head->tree_child_first;
814 kmem_free(t2, sizeof (*t2));
815 }
816 }
817
818 return (error);
819 }
820
821 /*
822 * Walk up the tree and:
823 * 1. release pseudo exportinfo if it has no child
824 * 2. release visible in parent's exportinfo
825 * 3. delete non-exported leaf nodes from tree
826 *
827 * Deleting of nodes will start only if the unshared
828 * node was a leaf node.
829 * Deleting of nodes will finish when we reach a node which
830 * has children or is a real export, then we might still need
831 * to continue releasing visibles, until we reach VROOT node.
832 */
833 void
834 treeclimb_unexport(nfs_export_t *ne, struct exportinfo *exip)
835 {
836 treenode_t *tnode, *old_nd;
837 treenode_t *connect_point = NULL;
838
839 ASSERT(RW_WRITE_HELD(&ne->exported_lock));
840
841 tnode = exip->exi_tree;
842 /*
843 * The unshared exportinfo was unlinked in unexport().
844 * Zeroing tree_exi ensures that we will skip it.
845 */
846 tnode->tree_exi = NULL;
847
848 if (tnode->tree_vis != NULL) /* system root has tree_vis == NULL */
849 tnode->tree_vis->vis_exported = 0;
850
851 while (tnode != NULL) {
852
853 /* Stop at VROOT node which is exported or has child */
854 if (TREE_ROOT(tnode) &&
855 (TREE_EXPORTED(tnode) || tnode->tree_child_first != NULL))
856 break;
857
858 /* Release pseudo export if it has no child */
859 if (TREE_ROOT(tnode) && !TREE_EXPORTED(tnode) &&
860 tnode->tree_child_first == NULL) {
861 exp_kstats_delete(tnode->tree_exi->exi_kstats);
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;
875 tnode = tnode->tree_parent;
876
877 /* Remove itself, if this is a leaf and non-exported node */
878 if (old_nd->tree_child_first == NULL &&
879 !TREE_EXPORTED(old_nd)) {
880 tree_remove_node(ne, old_nd);
881 connect_point = 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))
903 break;
904
905 /* lock vfs to prevent unmount of this vfs */
906 vfs_lock_wait(tvp->v_vfsp);
907
1160 int
1161 nfs_visible_inode(struct exportinfo *exi, ino64_t ino,
1162 struct exp_visible **visp)
1163 {
1164 /*
1165 * Only a PSEUDO node has a visible list or an exported VROOT
1166 * node may have a visible list.
1167 */
1168 if (! PSEUDO(exi))
1169 exi = get_root_export(exi);
1170
1171 for (*visp = exi->exi_visible; *visp != NULL; *visp = (*visp)->vis_next)
1172 if ((u_longlong_t)ino == (*visp)->vis_ino) {
1173 return (1);
1174 }
1175
1176 return (0);
1177 }
1178
1179 /*
1180 * Get the change attribute from visible and returns TRUE.
1181 * If the change value is not available returns FALSE.
1182 */
1183 bool_t
1184 nfs_visible_change(struct exportinfo *exi, vnode_t *vp, timespec_t *change)
1185 {
1186 struct exp_visible *visp;
1187 fid_t fid;
1188 treenode_t *node;
1189 nfs_export_t *ne = nfs_get_export();
1190
1191 /*
1192 * First check to see if vp is export root.
1193 */
1194 if (VN_CMP(vp, exi->exi_vp))
1195 goto exproot;
1196
1197 /*
1198 * Only a PSEUDO node has a visible list or an exported VROOT
1199 * node may have a visible list.
1200 */
1201 if (!PSEUDO(exi))
1202 exi = get_root_export(exi);
1203
1204 /* Get the fid of the vnode */
1205 bzero(&fid, sizeof (fid));
1206 fid.fid_len = MAXFIDSZ;
1207 if (vop_fid_pseudo(vp, &fid) != 0)
1208 return (FALSE);
1209
1214 * vnode ops ptrs are different. Just in case we're dealing
1215 * with LOFS, compare exi_fid/fsid here.
1216 */
1217 if (EQFID(&exi->exi_fid, &fid) &&
1218 EQFSID(&exi->exi_fsid, &vp->v_vfsp->vfs_fsid))
1219 goto exproot;
1220
1221 /* See if it matches any fid in the visible list */
1222 for (visp = exi->exi_visible; visp; visp = visp->vis_next) {
1223 if (EQFID(&fid, &visp->vis_fid)) {
1224 *change = visp->vis_change;
1225 return (TRUE);
1226 }
1227 }
1228
1229 return (FALSE);
1230
1231 exproot:
1232 /* The VROOT export have its visible available through treenode */
1233 node = exi->exi_tree;
1234 if (node != ne->ns_root) {
1235 ASSERT(node->tree_vis != NULL);
1236 *change = node->tree_vis->vis_change;
1237 } else {
1238 ASSERT(node->tree_vis == NULL);
1239 *change = ne->ns_root_change;
1240 }
1241 return (TRUE);
1242 }
1243
1244 /*
1245 * Update the change attribute value for a particular treenode. The change
1246 * attribute value is stored in the visible attached to the treenode, or in the
1247 * ns_root_change.
1248 *
1249 * If the change value is not supplied, the current time is used.
1250 */
1251 void
1252 tree_update_change(nfs_export_t *ne, treenode_t *tnode, timespec_t *change)
1253 {
1254 timespec_t *vis_change;
1255
1256 ASSERT(tnode != NULL);
1257 ASSERT((tnode != ne->ns_root && tnode->tree_vis != NULL) ||
1258 (tnode == ne->ns_root && tnode->tree_vis == NULL));
1259
1260 vis_change = tnode == ne->ns_root ? &ne->ns_root_change
1261 : &tnode->tree_vis->vis_change;
1262
1263 if (change != NULL)
1264 *vis_change = *change;
1265 else
1266 gethrestime(vis_change);
1267 }
|