xref: /illumos-gate/usr/src/uts/common/fs/nfs/nfs4_srv_ns.c (revision 65a89a64c60f3061bbe2381edaacc81660af9a95)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  *
22  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/systm.h>
29 
30 #include <nfs/nfs.h>
31 #include <nfs/export.h>
32 #include <sys/cmn_err.h>
33 
34 /*
35  * A version of VOP_FID that deals with a remote VOP_FID for nfs.
36  * If vp is an nfs node, nfs4_fid() returns EREMOTE, nfs3_fid() and nfs_fid()
37  * returns the filehandle of vp as its fid. When nfs uses fid to set the
38  * exportinfo filehandle template, a remote nfs filehandle would be too big for
39  * the fid of the exported directory. This routine remaps the value of the
40  * attribute va_nodeid of vp to be the fid of vp, so that the fid can fit.
41  *
42  * We need this fid mainly for setting up NFSv4 server namespace where an
43  * nfs filesystem is also part of it. Thus, need to be able to setup a pseudo
44  * exportinfo for an nfs node.
45  *
46  * e.g. mount an ufs filesystem on an nfs filesystem, then share the ufs
47  *      filesystem. (like exporting a local disk from a "diskless" client)
48  */
49 int
50 vop_fid_pseudo(vnode_t *vp, fid_t *fidp)
51 {
52 	struct vattr va;
53 	int error;
54 
55 	error = VOP_FID(vp, fidp);
56 
57 	/*
58 	 * XXX nfs4_fid() does nothing and returns EREMOTE.
59 	 * XXX nfs3_fid()/nfs_fid() returns nfs filehandle as its fid
60 	 * which has a bigger length than local fid.
61 	 * NFS_FHMAXDATA_EXT is the size of
62 	 * fhandle_ext_t.fh_xdata[NFS_FHMAXDATA_EXT].
63 	 *
64 	 * Note: nfs[2,3,4]_fid() only gets called for diskless clients.
65 	 */
66 	if (error == EREMOTE ||
67 	    (error == 0 && fidp->fid_len > NFS_FHMAXDATA_EXT)) {
68 
69 		va.va_mask = AT_NODEID;
70 		error = VOP_GETATTR(vp, &va, 0, CRED());
71 		if (error)
72 			return (error);
73 
74 		fidp->fid_len = sizeof (va.va_nodeid);
75 		bcopy(&va.va_nodeid, fidp->fid_data, fidp->fid_len);
76 		return (0);
77 	}
78 
79 	return (error);
80 }
81 
82 /*
83  * Get an nfsv4 vnode of the given fid from the visible list of an
84  * nfs filesystem or get the exi_vp if it is the root node.
85  */
86 int
87 nfs4_vget_pseudo(struct exportinfo *exi, vnode_t **vpp, fid_t *fidp)
88 {
89 	fid_t exp_fid;
90 	struct exp_visible *visp;
91 	int error;
92 
93 	/* check if the given fid is in the visible list */
94 
95 	for (visp = exi->exi_visible; visp; visp = visp->vis_next) {
96 		if (EQFID(fidp, &visp->vis_fid)) {
97 			VN_HOLD(visp->vis_vp);
98 			*vpp = visp->vis_vp;
99 			return (0);
100 		}
101 	}
102 
103 	/* check if the given fid is the same as the exported node */
104 
105 	bzero(&exp_fid, sizeof (exp_fid));
106 	exp_fid.fid_len = MAXFIDSZ;
107 	error = vop_fid_pseudo(exi->exi_vp, &exp_fid);
108 	if (error)
109 		return (error);
110 
111 	if (EQFID(fidp, &exp_fid)) {
112 		VN_HOLD(exi->exi_vp);
113 		*vpp = exi->exi_vp;
114 		return (0);
115 	}
116 
117 	return (ENOENT);
118 }
119 
120 /*
121  * Create a pseudo export entry
122  *
123  * This is an export entry that's created as the
124  * side-effect of a "real" export.  As a part of
125  * a real export, the pathname to the export is
126  * checked to see if all the directory components
127  * are accessible via an NFSv4 client, i.e. are
128  * exported.  If tree_climb() finds an unexported
129  * mountpoint along the path, then it calls this
130  * function to export it.
131  *
132  * This pseudo export differs from a real export
133  * in restriction on simple. read-only access,
134  * and the addition of a "visible" list of directories.
135  * A real export may have a visible list if it is a root of
136  * a file system and at least one of its subtree resides in
137  * a different file system is shared.
138  *
139  * A visible list is per file system. It resides in the exportinfo
140  * for the pseudo node (VROOT) and it could reside in a real export
141  * of a VROOT node.
142  */
143 int
144 pseudo_exportfs(vnode_t *vp, struct exp_visible *vis_head,
145 					struct exportdata *exdata)
146 {
147 	struct exportinfo *exi;
148 	struct exportdata *kex;
149 	fid_t fid;
150 	fsid_t fsid;
151 	int error;
152 	char *pseudo;
153 
154 	ASSERT(RW_WRITE_HELD(&exported_lock));
155 
156 	/*
157 	 * Get the vfs id
158 	 */
159 	bzero(&fid, sizeof (fid));
160 	fid.fid_len = MAXFIDSZ;
161 	error = vop_fid_pseudo(vp, &fid);
162 	if (error) {
163 		/*
164 		 * If VOP_FID returns ENOSPC then the fid supplied
165 		 * is too small.  For now we simply return EREMOTE.
166 		 */
167 		if (error == ENOSPC)
168 			error = EREMOTE;
169 		return (error);
170 	}
171 
172 	fsid = vp->v_vfsp->vfs_fsid;
173 	exi = kmem_zalloc(sizeof (*exi), KM_SLEEP);
174 	exi->exi_fsid = fsid;
175 	exi->exi_fid = fid;
176 	exi->exi_vp = vp;
177 	VN_HOLD(exi->exi_vp);
178 	exi->exi_visible = vis_head;
179 	exi->exi_count = 1;
180 	exi->exi_volatile_dev = (vfssw[vp->v_vfsp->vfs_fstype].vsw_flag &
181 				VSW_VOLATILEDEV) ? 1 : 0;
182 	mutex_init(&exi->exi_lock, NULL, MUTEX_DEFAULT, NULL);
183 
184 	/*
185 	 * Build up the template fhandle
186 	 */
187 	exi->exi_fh.fh_fsid = fsid;
188 	ASSERT(exi->exi_fid.fid_len <= sizeof (exi->exi_fh.fh_xdata));
189 	exi->exi_fh.fh_xlen = exi->exi_fid.fid_len;
190 	bcopy(exi->exi_fid.fid_data, exi->exi_fh.fh_xdata,
191 	    exi->exi_fid.fid_len);
192 	exi->exi_fh.fh_len = sizeof (exi->exi_fh.fh_data);
193 
194 	kex = &exi->exi_export;
195 	kex->ex_flags = EX_PSEUDO;
196 
197 	/* Set up a generic pathname */
198 
199 	pseudo = "(pseudo)";
200 	kex->ex_pathlen = strlen(pseudo);
201 	kex->ex_path = kmem_alloc(kex->ex_pathlen + 1, KM_SLEEP);
202 	(void) strcpy(kex->ex_path, pseudo);
203 
204 	/* Transfer the secinfo data from exdata to this new pseudo node */
205 	if (exdata)
206 		srv_secinfo_exp2pseu(&exi->exi_export, exdata);
207 
208 	/*
209 	 * Initialize auth cache lock
210 	 */
211 	rw_init(&exi->exi_cache_lock, NULL, RW_DEFAULT, NULL);
212 
213 	/*
214 	 * Insert the new entry at the front of the export list
215 	 */
216 	export_link(exi);
217 
218 	return (0);
219 }
220 
221 /*
222  * Free a list of visible directories
223  */
224 void
225 free_visible(struct exp_visible *head)
226 {
227 	struct exp_visible *visp, *next;
228 
229 	for (visp = head; visp; visp = next) {
230 		if (visp->vis_vp != NULL)
231 			VN_RELE(visp->vis_vp);
232 		next = visp->vis_next;
233 		kmem_free(visp, sizeof (*visp));
234 	}
235 }
236 
237 /*
238  * Add a list of visible directories to a pseudo exportfs.
239  *
240  * When we export a new directory we need to add a new
241  * path segment through the pseudofs to reach the new
242  * directory. This new path is reflected in a list of
243  * directories added to the "visible" list.
244  *
245  * Here there are two lists of visible fids: one hanging off the
246  * pseudo exportinfo, and the one we want to add.  It's possible
247  * that the two lists share a common path segment
248  * and have some common directories.  We need to combine
249  * the lists so there's no duplicate entries. Where a common
250  * path component is found, the vis_count field is bumped.
251  *
252  * When the addition is complete, the supplied list is freed.
253  */
254 
255 static void
256 more_visible(struct exportinfo *exi, struct exp_visible *vis_head)
257 {
258 	struct exp_visible *vp1, *vp2;
259 	struct exp_visible *tail, *new;
260 	int found;
261 
262 	/*
263 	 * If exportinfo doesn't already have a visible
264 	 * list just assign the entire supplied list.
265 	 */
266 	if (exi->exi_visible == NULL) {
267 		exi->exi_visible = vis_head;
268 		return;
269 	}
270 
271 	/*
272 	 * The outer loop traverses the supplied list.
273 	 */
274 	for (vp1 = vis_head; vp1; vp1 = vp1->vis_next) {
275 
276 		/*
277 		 * Given an element from the list to be added,
278 		 * search the exportinfo visible list looking for a match.
279 		 * If a match is found, increment the reference count.
280 		 */
281 		found = 0;
282 
283 		for (vp2 = exi->exi_visible; vp2; vp2 = vp2->vis_next) {
284 
285 			tail = vp2;
286 
287 			if (EQFID(&vp1->vis_fid, &vp2->vis_fid)) {
288 				found = 1;
289 				vp2->vis_count++;
290 				VN_RELE(vp1->vis_vp);
291 				vp1->vis_vp = NULL;
292 
293 				/*
294 				 * If the visible struct we want to add
295 				 * (vp1) has vis_exported set to 1, then
296 				 * the matching visible struct we just found
297 				 * must also have it's vis_exported field
298 				 * set to 1.
299 				 *
300 				 * For example, if /export/home was shared
301 				 * (and a UFS mountpoint), then "export" and
302 				 * "home" would each have visible structs in
303 				 * the root pseudo exportinfo. The vis_exported
304 				 * for home would be 1, and vis_exported for
305 				 * export would be 0.  Now, if /export was
306 				 * also shared, more_visible would find the
307 				 * existing visible struct for export, and
308 				 * see that vis_exported was 0.  The code
309 				 * below will set it to 1.
310 				 *
311 				 * vp1 is from vis list passed in (vis_head)
312 				 * vp2 is from vis list on pseudo exportinfo
313 				 */
314 				if (vp1->vis_exported && !vp2->vis_exported)
315 					vp2->vis_exported = 1;
316 				break;
317 			}
318 		}
319 
320 		/* If not found - add to the end of the list */
321 		if (! found) {
322 			new = kmem_zalloc(sizeof (*new), KM_SLEEP);
323 			*new = *vp1;
324 			tail->vis_next = new;
325 			new->vis_next = NULL;
326 			vp1->vis_vp = NULL;
327 		}
328 	}
329 
330 	/*
331 	 * Throw away the path list. vis_vp pointers in vis_head list
332 	 * are either VN_RELEed or reassigned, and are set to NULL.
333 	 * There is no need to VN_RELE in free_visible for this vis_head.
334 	 */
335 	free_visible(vis_head);
336 }
337 
338 /*
339  * Remove a list of visible directories from the pseudo exportfs.
340  *
341  * When we unexport a directory, we have to remove path
342  * components from the visible list in the pseudo exportfs
343  * entry.  The supplied visible list contains the fids of the path
344  * to the unexported directory.  The visible list of the export
345  * is checked against this list any matching fids have their
346  * reference count decremented.  If a reference count drops to
347  * zero, then it means no paths now use this directory, so its
348  * fid can be removed from the visible list.
349  *
350  * When the last path is removed, the visible list will be null.
351  */
352 static void
353 less_visible(struct exportinfo *exi, struct exp_visible *vis_head)
354 {
355 	struct exp_visible *vp1, *vp2;
356 	struct exp_visible *prev, *next;
357 
358 	/*
359 	 * The outer loop traverses the supplied list.
360 	 */
361 	for (vp1 = vis_head; vp1; vp1 = vp1->vis_next) {
362 
363 		/*
364 		 * Given an element from the list to be removed,
365 		 * search the exportinfo list looking for a match.
366 		 * If a match is found, decrement the reference
367 		 * count and drop the element if the count drops
368 		 * to zero.
369 		 */
370 		for (vp2 = exi->exi_visible, prev = NULL; vp2; vp2 = next) {
371 
372 			next = vp2->vis_next;
373 
374 			if (EQFID(&vp1->vis_fid, &vp2->vis_fid)) {
375 
376 				/*
377 				 * Decrement the ref count.
378 				 * Remove the entry if it's zero.
379 				 */
380 				if (--vp2->vis_count <= 0) {
381 					if (prev == NULL)
382 						exi->exi_visible = next;
383 					else
384 						prev->vis_next = next;
385 
386 					VN_RELE(vp2->vis_vp);
387 					kmem_free(vp2, sizeof (*vp1));
388 				} else {
389 					/*
390 					 * If we're here, then the vp2 will
391 					 * remain in the vis list.  If the
392 					 * vis entry corresponds to the object
393 					 * being unshared, then vis_exported
394 					 * needs to be set to 0.
395 					 *
396 					 * vp1 is a node from caller's list
397 					 * vp2 is node from exportinfo's list
398 					 *
399 					 * Only 1 node in the caller's list
400 					 * will have vis_exported set to 1,
401 					 * and it corresponds to the obj being
402 					 * unshared.  It should always be the
403 					 * last element of the caller's list.
404 					 */
405 					if (vp1->vis_exported &&
406 					    vp2->vis_exported) {
407 						vp2->vis_exported = 0;
408 					}
409 				}
410 
411 				break;
412 			}
413 
414 			prev = vp2;
415 		}
416 	}
417 
418 	free_visible(vis_head);
419 }
420 
421 /*
422  * This function checks the path to a new export to
423  * check whether all the pathname components are
424  * exported. It works by climbing the file tree one
425  * component at a time via "..", crossing mountpoints
426  * if necessary until an export entry is found, or the
427  * system root is reached.
428  *
429  * If an unexported mountpoint is found, then
430  * a new pseudo export is added and the pathname from
431  * the mountpoint down to the export is added to the
432  * visible list for the new pseudo export.  If an existing
433  * pseudo export is found, then the pathname is added
434  * to its visible list.
435  *
436  * Note that there's some tests for exportdir.
437  * The exportinfo entry that's passed as a parameter
438  * is that of the real export and exportdir is set
439  * for this case.
440  *
441  * Here is an example of a possible setup:
442  *
443  * () - a new fs; fs mount point
444  * EXPORT - a real exported node
445  * PSEUDO - a pseudo node
446  * vis - visible list
447  * f# - security flavor#
448  * (f#) - security flavor# propagated from its decendents
449  * "" - covered vnode
450  *
451  *
452  *                 /
453  *                 |
454  *                 (a) PSEUDO (f1,f2)
455  *                 |   vis: b,b,"c","n"
456  *                 |
457  *                 b
458  *        ---------|------------------
459  *        |                          |
460  *        (c) EXPORT,f1(f2)          (n) PSEUDO (f1,f2)
461  *        |   vis: "e","d"           |   vis: m,m,,p,q,"o"
462  *        |                          |
463  *  ------------------          -------------------
464  *  |        |        |         |                  |
465  *  (d)      (e)      f         m EXPORT,f1(f2)    p
466  *  EXPORT   EXPORT             |                  |
467  *  f1       f2                 |                  |
468  *           |                  |                  |
469  *           j                 (o) EXPORT,f2       q EXPORT f2
470  *
471  */
472 int
473 treeclimb_export(struct exportinfo *exip)
474 {
475 	vnode_t *dvp, *vp;
476 	fid_t fid;
477 	int error;
478 	int exportdir;
479 	struct exportinfo *exi = NULL;
480 	struct exp_visible *visp;
481 	struct exp_visible *vis_head = NULL;
482 	struct vattr va;
483 
484 	ASSERT(RW_WRITE_HELD(&exported_lock));
485 
486 	vp = exip->exi_vp;
487 	VN_HOLD(vp);
488 	exportdir = 1;
489 
490 	for (;;) {
491 
492 		bzero(&fid, sizeof (fid));
493 		fid.fid_len = MAXFIDSZ;
494 		error = vop_fid_pseudo(vp, &fid);
495 		if (error)
496 			break;
497 
498 		if (! exportdir) {
499 			/*
500 			 * Check if this exportroot is a VROOT dir.  If so,
501 			 * then attach the pseudonodes.  If not, then
502 			 * continue .. traversal until we hit a VROOT
503 			 * export (pseudo or real).
504 			 */
505 			exi = checkexport4(&vp->v_vfsp->vfs_fsid, &fid, vp);
506 			if (exi != NULL && vp->v_flag & VROOT) {
507 				/*
508 				 * Found an export info
509 				 *
510 				 * Extend the list of visible
511 				 * directories whether it's a pseudo
512 				 * or a real export.
513 				 */
514 				more_visible(exi, vis_head);
515 				vis_head = NULL;
516 				break;	/* and climb no further */
517 			}
518 		}
519 
520 		/*
521 		 * If at the root of the filesystem, need
522 		 * to traverse across the mountpoint
523 		 * and continue the climb on the mounted-on
524 		 * filesystem.
525 		 */
526 		if (vp->v_flag & VROOT) {
527 
528 			if (! exportdir) {
529 				/*
530 				 * Found the root directory of a filesystem
531 				 * that isn't exported.  Need to export
532 				 * this as a pseudo export so that an NFS v4
533 				 * client can do lookups in it.
534 				 */
535 				error = pseudo_exportfs(vp, vis_head, NULL);
536 				if (error)
537 					break;
538 				vis_head = NULL;
539 			}
540 
541 			if (VN_CMP(vp, rootdir)) {
542 				/* at system root */
543 				break;
544 			}
545 
546 			vp = untraverse(vp);
547 			exportdir = 0;
548 			continue;
549 		}
550 
551 		/*
552 		 * Do a getattr to obtain the nodeid (inode num)
553 		 * for this vnode.
554 		 */
555 		va.va_mask = AT_NODEID;
556 		error = VOP_GETATTR(vp, &va, 0, CRED());
557 		if (error)
558 			break;
559 
560 		/*
561 		 *  Add this directory fid to visible list
562 		 */
563 		visp = kmem_alloc(sizeof (*visp), KM_SLEEP);
564 		VN_HOLD(vp);
565 		visp->vis_vp = vp;
566 		visp->vis_fid = fid;		/* structure copy */
567 		visp->vis_ino = va.va_nodeid;
568 		visp->vis_count = 1;
569 		visp->vis_exported = exportdir;
570 		visp->vis_next = vis_head;
571 		vis_head = visp;
572 
573 		/*
574 		 * Now, do a ".." to find parent dir of vp.
575 		 */
576 		error = VOP_LOOKUP(vp, "..", &dvp, NULL, 0, NULL, CRED());
577 
578 		if (error == ENOTDIR && exportdir) {
579 			dvp = exip->exi_dvp;
580 			ASSERT(dvp != NULL);
581 			VN_HOLD(dvp);
582 			error = 0;
583 		}
584 
585 		if (error)
586 			break;
587 
588 		exportdir = 0;
589 		VN_RELE(vp);
590 		vp = dvp;
591 	}
592 
593 	VN_RELE(vp);
594 	return (error);
595 }
596 
597 /*
598  * Walk up the tree looking for pseudo export entries.
599  *
600  * If a pseudo export is found, remove the path we've
601  * climbed from its visible list. If the visible list
602  * still has entries after the removal, then we can stop.
603  * If it becomes null, then remove the pseudo export entry
604  * and carry on up the tree to see if there's any more.
605  */
606 int
607 treeclimb_unexport(struct exportinfo *exip)
608 {
609 	vnode_t *dvp, *vp;
610 	fid_t fid;
611 	int error = 0;
612 	int exportdir;
613 	struct exportinfo *exi = NULL;
614 	struct exp_visible *vis_head = NULL, *visp;
615 
616 	ASSERT(RW_WRITE_HELD(&exported_lock));
617 
618 	exportdir = 1;
619 	vp = exip->exi_vp;
620 	VN_HOLD(vp);
621 
622 	for (;;) {
623 
624 		bzero(&fid, sizeof (fid));
625 		fid.fid_len = MAXFIDSZ;
626 		error = vop_fid_pseudo(vp, &fid);
627 		if (error)
628 			break;
629 
630 		if (! exportdir) {
631 
632 			/*
633 			 * We need to use checkexport4() here because it
634 			 * doesn't acquire exported_lock and it doesn't
635 			 * manipulate exi_count.
636 			 *
637 			 * Remove directories from the visible
638 			 * list that are unique to the path
639 			 * for this export.  (Only VROOT exportinfos
640 			 * have can have visible entries).
641 			 */
642 			exi = checkexport4(&vp->v_vfsp->vfs_fsid, &fid, vp);
643 			if (exi != NULL && (vp->v_flag & VROOT)) {
644 
645 				less_visible(exi, vis_head);
646 				vis_head = NULL;
647 
648 				/*
649 				 * If the visible list has entries
650 				 * or if it's a real export, then
651 				 * there's no need to keep climbing.
652 				 */
653 				if (exi->exi_visible || ! PSEUDO(exi))
654 					break;
655 
656 				/*
657 				 * Otherwise, we have a pseudo export
658 				 * with an empty list (no exports below
659 				 * it) so we must remove and continue
660 				 * the climb to remove its name from
661 				 * the parent export.
662 				 */
663 				error = export_unlink(&vp->v_vfsp->vfs_fsid,
664 						&fid, vp, NULL);
665 				if (error)
666 					break;
667 
668 				exi_rele(exi);
669 			}
670 		}
671 
672 		/*
673 		 * If at the root of the filesystem, need
674 		 * to traverse across the mountpoint
675 		 * and continue the climb on the mounted-on
676 		 * filesystem.
677 		 */
678 		if (vp->v_flag & VROOT) {
679 			if (VN_CMP(vp, rootdir)) {
680 				/* at system root */
681 				break;
682 			}
683 			vp = untraverse(vp);
684 			exportdir = 0;
685 			continue;
686 		}
687 
688 		/*
689 		 *  Add this directory fid to path list
690 		 */
691 		visp = kmem_alloc(sizeof (*visp), KM_SLEEP);
692 		VN_HOLD(vp);
693 		visp->vis_vp = vp;
694 		visp->vis_fid = fid;		/* structure copy */
695 		visp->vis_ino = 0;
696 		visp->vis_count = 1;
697 		visp->vis_exported = exportdir;
698 		visp->vis_next = vis_head;
699 		vis_head = visp;
700 
701 		/*
702 		 * Do a ".." to find parent dir of vp.
703 		 */
704 		error = VOP_LOOKUP(vp, "..", &dvp, NULL, 0, NULL, CRED());
705 
706 		if (error == ENOTDIR && exportdir) {
707 			dvp = exip->exi_dvp;
708 			ASSERT(dvp != NULL);
709 			VN_HOLD(dvp);
710 			error = 0;
711 		}
712 		if (error)
713 			break;
714 
715 		exportdir = 0;
716 		VN_RELE(vp);
717 		vp = dvp;
718 	}
719 
720 	VN_RELE(vp);
721 	return (error);
722 }
723 
724 
725 /*
726  * Traverse backward across mountpoint from the
727  * root vnode of a filesystem to its mounted-on
728  * vnode.
729  */
730 vnode_t *
731 untraverse(vnode_t *vp)
732 {
733 	vnode_t *tvp, *nextvp;
734 
735 	tvp = vp;
736 	for (;;) {
737 		if (! (tvp->v_flag & VROOT))
738 			break;
739 
740 		/* lock vfs to prevent unmount of this vfs */
741 		vfs_lock_wait(tvp->v_vfsp);
742 
743 		if ((nextvp = tvp->v_vfsp->vfs_vnodecovered) == NULL) {
744 			vfs_unlock(tvp->v_vfsp);
745 			break;
746 		}
747 
748 		/*
749 		 * Hold nextvp to prevent unmount.  After unlock vfs and
750 		 * rele tvp, any number of overlays could be unmounted.
751 		 * Putting a hold on vfs_vnodecovered will only allow
752 		 * tvp's vfs to be unmounted. Of course if caller placed
753 		 * extra hold on vp before calling untraverse, the following
754 		 * hold would not be needed.  Since prev actions of caller
755 		 * are unknown, we need to hold here just to be safe.
756 		 */
757 		VN_HOLD(nextvp);
758 		vfs_unlock(tvp->v_vfsp);
759 		VN_RELE(tvp);
760 		tvp = nextvp;
761 	}
762 
763 	return (tvp);
764 }
765 
766 /*
767  * Given an exportinfo, climb up to find the exportinfo for the VROOT
768  * of the filesystem.
769  *
770  * e.g.         /
771  *              |
772  *              a (VROOT) pseudo-exportinfo
773  *		|
774  *		b
775  *		|
776  *		c  #share /a/b/c
777  *		|
778  *		d
779  *
780  * where c is in the same filesystem as a.
781  * So, get_root_export(*exportinfo_for_c) returns exportinfo_for_a
782  *
783  * If d is shared, then c will be put into a's visible list.
784  * Note: visible list is per filesystem and is attached to the
785  * VROOT exportinfo.
786  */
787 struct exportinfo *
788 get_root_export(struct exportinfo *exip)
789 {
790 	vnode_t *dvp, *vp;
791 	fid_t fid;
792 	struct exportinfo *exi = exip;
793 	int error;
794 
795 	vp = exi->exi_vp;
796 	VN_HOLD(vp);
797 
798 	for (;;) {
799 
800 		if (vp->v_flag & VROOT) {
801 			ASSERT(exi != NULL);
802 			break;
803 		}
804 
805 		/*
806 		 * Now, do a ".." to find parent dir of vp.
807 		 */
808 		error = VOP_LOOKUP(vp, "..", &dvp, NULL, 0, NULL, CRED());
809 
810 		if (error) {
811 			exi = NULL;
812 			break;
813 		}
814 
815 		VN_RELE(vp);
816 		vp = dvp;
817 
818 		bzero(&fid, sizeof (fid));
819 		fid.fid_len = MAXFIDSZ;
820 		error = vop_fid_pseudo(vp, &fid);
821 		if (error) {
822 			exi = NULL;
823 			break;
824 		}
825 
826 		exi = checkexport4(&vp->v_vfsp->vfs_fsid, &fid, vp);
827 	}
828 
829 	VN_RELE(vp);
830 	return (exi);
831 }
832 
833 /*
834  * Return true if the supplied vnode has a sub-directory exported.
835  */
836 int
837 has_visible(struct exportinfo *exi, vnode_t *vp)
838 {
839 	struct exp_visible *visp;
840 	fid_t fid;
841 	bool_t vp_is_exported;
842 
843 	vp_is_exported = VN_CMP(vp,  exi->exi_vp);
844 
845 	/*
846 	 * An exported root vnode has a sub-dir shared if it has a visible list.
847 	 * i.e. if it does not have a visible list, then there is no node in
848 	 * this filesystem leads to any other shared node.
849 	 */
850 	if (vp_is_exported && (vp->v_flag & VROOT))
851 		return (exi->exi_visible ? 1 : 0);
852 
853 	/*
854 	 * Only the exportinfo of a fs root node may have a visible list.
855 	 * Either it is a pseudo root node, or a real exported root node.
856 	 */
857 	if ((exi = get_root_export(exi)) == NULL) {
858 		return (0);
859 	}
860 
861 	if (!exi->exi_visible)
862 		return (0);
863 
864 	/* Get the fid of the vnode */
865 	bzero(&fid, sizeof (fid));
866 	fid.fid_len = MAXFIDSZ;
867 	if (vop_fid_pseudo(vp, &fid) != 0) {
868 		return (0);
869 	}
870 
871 	/*
872 	 * See if vp is in the visible list of the root node exportinfo.
873 	 */
874 	for (visp = exi->exi_visible; visp; visp = visp->vis_next) {
875 		if (EQFID(&fid, &visp->vis_fid)) {
876 			/*
877 			 * If vp is an exported non-root node with only 1 path
878 			 * count (for itself), it indicates no sub-dir shared
879 			 * using this vp as a path.
880 			 */
881 			if (vp_is_exported && visp->vis_count < 2)
882 				break;
883 
884 			return (1);
885 		}
886 	}
887 
888 	return (0);
889 }
890 
891 /*
892  * Returns true if the supplied vnode is visible
893  * in this export.  If vnode is visible, return
894  * vis_exported in expseudo.
895  */
896 int
897 nfs_visible(struct exportinfo *exi, vnode_t *vp, int *expseudo)
898 {
899 	struct exp_visible *visp;
900 	fid_t fid;
901 
902 	/*
903 	 * First check to see if vp is export root.
904 	 *
905 	 * A pseudo export root can never be exported
906 	 * (it would be a real export then); however,
907 	 * it is always visible.  If a pseudo root object
908 	 * was exported by server admin, then the entire
909 	 * pseudo exportinfo (and all visible entries) would
910 	 * be destroyed.  A pseudo exportinfo only exists
911 	 * to provide access to real (descendant) export(s).
912 	 *
913 	 * Previously, rootdir was special cased here; however,
914 	 * the export root special case handles the rootdir
915 	 * case also.
916 	 */
917 	if (VN_CMP(vp, exi->exi_vp)) {
918 		*expseudo = 0;
919 		return (1);
920 	}
921 
922 	/*
923 	 * Only a PSEUDO node has a visible list or an exported VROOT
924 	 * node may have a visible list.
925 	 */
926 	if (! PSEUDO(exi) && (exi = get_root_export(exi)) == NULL) {
927 		*expseudo = 0;
928 		return (0);
929 	}
930 
931 	/* Get the fid of the vnode */
932 
933 	bzero(&fid, sizeof (fid));
934 	fid.fid_len = MAXFIDSZ;
935 	if (vop_fid_pseudo(vp, &fid) != 0) {
936 		*expseudo = 0;
937 		return (0);
938 	}
939 
940 	/*
941 	 * We can't trust VN_CMP() above because of LOFS.
942 	 * Even though VOP_CMP will do the right thing for LOFS
943 	 * objects, VN_CMP will short circuit out early when the
944 	 * vnode ops ptrs are different.  Just in case we're dealing
945 	 * with LOFS, compare exi_fid/fsid here.
946 	 *
947 	 * expseudo is not set because this is not an export
948 	 */
949 	if (EQFID(&exi->exi_fid, &fid) &&
950 	    EQFSID(&exi->exi_fsid, &vp->v_vfsp->vfs_fsid)) {
951 		*expseudo = 0;
952 		return (1);
953 	}
954 
955 
956 	/* See if it matches any fid in the visible list */
957 
958 	for (visp = exi->exi_visible; visp; visp = visp->vis_next) {
959 		if (EQFID(&fid, &visp->vis_fid)) {
960 			*expseudo = visp->vis_exported;
961 			return (1);
962 		}
963 	}
964 
965 	*expseudo = 0;
966 
967 	return (0);
968 }
969 
970 /*
971  * Returns true if the supplied vnode is the
972  * directory of an export point.
973  */
974 int
975 nfs_exported(struct exportinfo *exi, vnode_t *vp)
976 {
977 	struct exp_visible *visp;
978 	fid_t fid;
979 
980 	/*
981 	 * First check to see if vp is the export root
982 	 * This check required for the case of lookup ..
983 	 * where .. is a V_ROOT vnode and a pseudo exportroot.
984 	 * Pseudo export root objects do not have an entry
985 	 * in the visible list even though every V_ROOT
986 	 * pseudonode is visible.  It is safe to compare
987 	 * vp here because pseudo_exportfs put a hold on
988 	 * it when exi_vp was initialized.
989 	 *
990 	 * Note: VN_CMP() won't match for LOFS shares, but they're
991 	 * handled below w/EQFID/EQFSID.
992 	 */
993 	if (VN_CMP(vp, exi->exi_vp))
994 		return (1);
995 
996 	/* Get the fid of the vnode */
997 
998 	bzero(&fid, sizeof (fid));
999 	fid.fid_len = MAXFIDSZ;
1000 	if (vop_fid_pseudo(vp, &fid) != 0)
1001 		return (0);
1002 
1003 	if (EQFID(&fid, &exi->exi_fid) &&
1004 	    EQFSID(&vp->v_vfsp->vfs_fsid, &exi->exi_fsid)) {
1005 		return (1);
1006 	}
1007 
1008 	/* See if it matches any fid in the visible list */
1009 
1010 	for (visp = exi->exi_visible; visp; visp = visp->vis_next) {
1011 		if (EQFID(&fid, &visp->vis_fid))
1012 			return (visp->vis_exported);
1013 	}
1014 
1015 	return (0);
1016 }
1017 
1018 /*
1019  * Returns true if the supplied inode is visible
1020  * in this export.  This function is used by
1021  * readdir which uses inode numbers from the
1022  * directory.
1023  *
1024  * NOTE: this code does not match inode number for ".",
1025  * but it isn't required because NFS4 server rddir
1026  * skips . and .. entries.
1027  */
1028 int
1029 nfs_visible_inode(struct exportinfo *exi, ino64_t ino, int *expseudo)
1030 {
1031 	struct exp_visible *visp;
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) && (exi = get_root_export(exi)) == NULL) {
1038 		*expseudo = 0;
1039 		return (0);
1040 	}
1041 
1042 	for (visp = exi->exi_visible; visp; visp = visp->vis_next)
1043 		if ((u_longlong_t)ino == visp->vis_ino) {
1044 			*expseudo = visp->vis_exported;
1045 			return (1);
1046 		}
1047 
1048 	*expseudo = 0;
1049 	return (0);
1050 }
1051