xref: /illumos-gate/usr/src/lib/smbsrv/libfksmbsrv/common/fake_lookup.c (revision 54026d5ae0b8cbf8da5c14341d51f7a3cab03758)
1b819cea2SGordon Ross /*
2b819cea2SGordon Ross  * CDDL HEADER START
3b819cea2SGordon Ross  *
4b819cea2SGordon Ross  * The contents of this file are subject to the terms of the
5b819cea2SGordon Ross  * Common Development and Distribution License (the "License").
6b819cea2SGordon Ross  * You may not use this file except in compliance with the License.
7b819cea2SGordon Ross  *
8b819cea2SGordon Ross  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9b819cea2SGordon Ross  * or http://www.opensolaris.org/os/licensing.
10b819cea2SGordon Ross  * See the License for the specific language governing permissions
11b819cea2SGordon Ross  * and limitations under the License.
12b819cea2SGordon Ross  *
13b819cea2SGordon Ross  * When distributing Covered Code, include this CDDL HEADER in each
14b819cea2SGordon Ross  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15b819cea2SGordon Ross  * If applicable, add the following below this CDDL HEADER, with the
16b819cea2SGordon Ross  * fields enclosed by brackets "[]" replaced with your own identifying
17b819cea2SGordon Ross  * information: Portions Copyright [yyyy] [name of copyright owner]
18b819cea2SGordon Ross  *
19b819cea2SGordon Ross  * CDDL HEADER END
20b819cea2SGordon Ross  */
21b819cea2SGordon Ross 
22b819cea2SGordon Ross /*
23b819cea2SGordon Ross  * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
24b819cea2SGordon Ross  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
25b819cea2SGordon Ross  */
26b819cea2SGordon Ross 
27b819cea2SGordon Ross /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
28b819cea2SGordon Ross /*	  All Rights Reserved  	*/
29b819cea2SGordon Ross 
30b819cea2SGordon Ross /*
31b819cea2SGordon Ross  * University Copyright- Copyright (c) 1982, 1986, 1988
32b819cea2SGordon Ross  * The Regents of the University of California
33b819cea2SGordon Ross  * All Rights Reserved
34b819cea2SGordon Ross  *
35b819cea2SGordon Ross  * University Acknowledgment- Portions of this document are derived from
36b819cea2SGordon Ross  * software developed by the University of California, Berkeley, and its
37b819cea2SGordon Ross  * contributors.
38b819cea2SGordon Ross  */
39b819cea2SGordon Ross 
40b819cea2SGordon Ross #include <sys/types.h>
41b819cea2SGordon Ross #include <sys/param.h>
42b819cea2SGordon Ross #include <sys/systm.h>
43b819cea2SGordon Ross #include <sys/file.h>
44b819cea2SGordon Ross #include <sys/errno.h>
45b819cea2SGordon Ross #include <sys/cred.h>
46b819cea2SGordon Ross #include <sys/user.h>
47b819cea2SGordon Ross #include <sys/uio.h>
48b819cea2SGordon Ross #include <sys/vfs.h>
49b819cea2SGordon Ross #include <sys/vnode.h>
50b819cea2SGordon Ross #include <sys/pathname.h>
51b819cea2SGordon Ross #include <sys/proc.h>
52b819cea2SGordon Ross #include <sys/vtrace.h>
53b819cea2SGordon Ross #include <sys/sysmacros.h>
54b819cea2SGordon Ross #include <sys/debug.h>
55b819cea2SGordon Ross #include <sys/dirent.h>
56b819cea2SGordon Ross #include <sys/zone.h>
57b819cea2SGordon Ross #include <sys/dnlc.h>
58b819cea2SGordon Ross #include <sys/fs/snode.h>
59b819cea2SGordon Ross 
60b819cea2SGordon Ross /*
61b819cea2SGordon Ross  * Starting at current directory, translate pathname pnp to end.
62b819cea2SGordon Ross  * Leave pathname of final component in pnp, return the vnode
63b819cea2SGordon Ross  * for the final component in *compvpp, and return the vnode
64b819cea2SGordon Ross  * for the parent of the final component in dirvpp.
65b819cea2SGordon Ross  *
66b819cea2SGordon Ross  * This is the central routine in pathname translation and handles
67b819cea2SGordon Ross  * multiple components in pathnames, separating them at /'s.  It also
68b819cea2SGordon Ross  * implements mounted file systems and processes symbolic links.
69b819cea2SGordon Ross  *
70b819cea2SGordon Ross  * vp is the vnode where the directory search should start.
71b819cea2SGordon Ross  *
72b819cea2SGordon Ross  * Reference counts: vp must be held prior to calling this function.  rootvp
73b819cea2SGordon Ross  * should only be held if rootvp != rootdir.
74b819cea2SGordon Ross  */
75b819cea2SGordon Ross int
76b819cea2SGordon Ross lookuppnvp(
77b819cea2SGordon Ross 	struct pathname *pnp,		/* pathname to lookup */
78b819cea2SGordon Ross 	struct pathname *rpnp,		/* if non-NULL, return resolved path */
79b819cea2SGordon Ross 	int flags,			/* follow symlinks */
80b819cea2SGordon Ross 	vnode_t **dirvpp,		/* ptr for parent vnode */
81b819cea2SGordon Ross 	vnode_t **compvpp,		/* ptr for entry vnode */
82b819cea2SGordon Ross 	vnode_t *rootvp,		/* rootvp */
83b819cea2SGordon Ross 	vnode_t *vp,			/* directory to start search at */
84b819cea2SGordon Ross 	cred_t *cr)			/* user's credential */
85b819cea2SGordon Ross {
86b819cea2SGordon Ross 	vnode_t *cvp;	/* current component vp */
87b819cea2SGordon Ross 	vnode_t *tvp;	/* addressable temp ptr */
88b819cea2SGordon Ross 	char component[MAXNAMELEN];	/* buffer for component (incl null) */
89b819cea2SGordon Ross 	int error;
90b819cea2SGordon Ross 	int nlink;
91b819cea2SGordon Ross 	int lookup_flags;
92b819cea2SGordon Ross 	struct pathname presrvd; /* case preserved name */
93b819cea2SGordon Ross 	struct pathname *pp = NULL;
94b819cea2SGordon Ross 	vnode_t *startvp;
95b819cea2SGordon Ross 	int must_be_directory = 0;
96b819cea2SGordon Ross 	boolean_t retry_with_kcred;
97b819cea2SGordon Ross 
98b819cea2SGordon Ross 	nlink = 0;
99b819cea2SGordon Ross 	cvp = NULL;
100b819cea2SGordon Ross 	if (rpnp)
101b819cea2SGordon Ross 		rpnp->pn_pathlen = 0;
102b819cea2SGordon Ross 
103b819cea2SGordon Ross 	lookup_flags = dirvpp ? LOOKUP_DIR : 0;
104b819cea2SGordon Ross 	if (flags & FIGNORECASE) {
105b819cea2SGordon Ross 		lookup_flags |= FIGNORECASE;
106b819cea2SGordon Ross 		pn_alloc(&presrvd);
107b819cea2SGordon Ross 		pp = &presrvd;
108b819cea2SGordon Ross 	}
109b819cea2SGordon Ross 
110b819cea2SGordon Ross 	/*
111b819cea2SGordon Ross 	 * Eliminate any trailing slashes in the pathname.
112b819cea2SGordon Ross 	 * If there are any, we must follow all symlinks.
113b819cea2SGordon Ross 	 * Also, we must guarantee that the last component is a directory.
114b819cea2SGordon Ross 	 */
115b819cea2SGordon Ross 	if (pn_fixslash(pnp)) {
116b819cea2SGordon Ross 		flags |= FOLLOW;
117b819cea2SGordon Ross 		must_be_directory = 1;
118b819cea2SGordon Ross 	}
119b819cea2SGordon Ross 
120b819cea2SGordon Ross 	startvp = vp;
121b819cea2SGordon Ross next:
122b819cea2SGordon Ross 	retry_with_kcred = B_FALSE;
123b819cea2SGordon Ross 
124b819cea2SGordon Ross 	/*
125b819cea2SGordon Ross 	 * Make sure we have a directory.
126b819cea2SGordon Ross 	 */
127b819cea2SGordon Ross 	if (vp->v_type != VDIR) {
128b819cea2SGordon Ross 		error = ENOTDIR;
129b819cea2SGordon Ross 		goto bad;
130b819cea2SGordon Ross 	}
131b819cea2SGordon Ross 
132b819cea2SGordon Ross 	if (rpnp && VN_CMP(vp, rootvp))
133b819cea2SGordon Ross 		(void) pn_set(rpnp, "/");
134b819cea2SGordon Ross 
135b819cea2SGordon Ross 	/*
136b819cea2SGordon Ross 	 * Process the next component of the pathname.
137b819cea2SGordon Ross 	 */
138*54026d5aSGordon Ross 	if ((error = pn_getcomponent(pnp, component)) != 0) {
139b819cea2SGordon Ross 		goto bad;
140b819cea2SGordon Ross 	}
141b819cea2SGordon Ross 
142b819cea2SGordon Ross 	/*
143b819cea2SGordon Ross 	 * Handle "..": two special cases.
144b819cea2SGordon Ross 	 * 1. If we're at the root directory (e.g. after chroot or
145b819cea2SGordon Ross 	 *    zone_enter) then change ".." to "." so we can't get
146b819cea2SGordon Ross 	 *    out of this subtree.
147b819cea2SGordon Ross 	 * 2. If this vnode is the root of a mounted file system,
148b819cea2SGordon Ross 	 *    then replace it with the vnode that was mounted on
149b819cea2SGordon Ross 	 *    so that we take the ".." in the other file system.
150b819cea2SGordon Ross 	 */
151b819cea2SGordon Ross 	if (component[0] == '.' && component[1] == '.' && component[2] == 0) {
152b819cea2SGordon Ross checkforroot:
153b819cea2SGordon Ross 		if (VN_CMP(vp, rootvp)) {
154b819cea2SGordon Ross 			component[1] = '\0';
155b819cea2SGordon Ross 		} else if (vp->v_flag & VROOT) {
156b819cea2SGordon Ross 			vfs_t *vfsp;
157b819cea2SGordon Ross 			cvp = vp;
158b819cea2SGordon Ross 
159b819cea2SGordon Ross 			/*
160b819cea2SGordon Ross 			 * While we deal with the vfs pointer from the vnode
161b819cea2SGordon Ross 			 * the filesystem could have been forcefully unmounted
162b819cea2SGordon Ross 			 * and the vnode's v_vfsp could have been invalidated
163b819cea2SGordon Ross 			 * by VFS_UNMOUNT. Hence, we cache v_vfsp and use it
164b819cea2SGordon Ross 			 * with vfs_rlock_wait/vfs_unlock.
165b819cea2SGordon Ross 			 * It is safe to use the v_vfsp even it is freed by
166b819cea2SGordon Ross 			 * VFS_UNMOUNT because vfs_rlock_wait/vfs_unlock
167b819cea2SGordon Ross 			 * do not dereference v_vfsp. It is just used as a
168b819cea2SGordon Ross 			 * magic cookie.
169b819cea2SGordon Ross 			 * One more corner case here is the memory getting
170b819cea2SGordon Ross 			 * reused for another vfs structure. In this case
171b819cea2SGordon Ross 			 * lookuppnvp's vfs_rlock_wait will succeed, domount's
172b819cea2SGordon Ross 			 * vfs_lock will fail and domount will bail out with an
173b819cea2SGordon Ross 			 * error (EBUSY).
174b819cea2SGordon Ross 			 */
175b819cea2SGordon Ross 			vfsp = cvp->v_vfsp;
176b819cea2SGordon Ross 
177b819cea2SGordon Ross 			/*
178b819cea2SGordon Ross 			 * This lock is used to synchronize
179b819cea2SGordon Ross 			 * mounts/unmounts and lookups.
180b819cea2SGordon Ross 			 * Threads doing mounts/unmounts hold the
181b819cea2SGordon Ross 			 * writers version vfs_lock_wait().
182b819cea2SGordon Ross 			 */
183b819cea2SGordon Ross 
184b819cea2SGordon Ross 			vfs_rlock_wait(vfsp);
185b819cea2SGordon Ross 
186b819cea2SGordon Ross 			/*
187b819cea2SGordon Ross 			 * If this vnode is on a file system that
188b819cea2SGordon Ross 			 * has been forcibly unmounted,
189b819cea2SGordon Ross 			 * we can't proceed. Cancel this operation
190b819cea2SGordon Ross 			 * and return EIO.
191b819cea2SGordon Ross 			 *
192b819cea2SGordon Ross 			 * vfs_vnodecovered is NULL if unmounted.
193b819cea2SGordon Ross 			 * Currently, nfs uses VFS_UNMOUNTED to
194b819cea2SGordon Ross 			 * check if it's a forced-umount. Keep the
195b819cea2SGordon Ross 			 * same checking here as well even though it
196b819cea2SGordon Ross 			 * may not be needed.
197b819cea2SGordon Ross 			 */
198b819cea2SGordon Ross 			if (((vp = cvp->v_vfsp->vfs_vnodecovered) == NULL) ||
199b819cea2SGordon Ross 			    (cvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)) {
200b819cea2SGordon Ross 				vfs_unlock(vfsp);
201b819cea2SGordon Ross 				VN_RELE(cvp);
202b819cea2SGordon Ross 				if (pp)
203b819cea2SGordon Ross 					pn_free(pp);
204b819cea2SGordon Ross 				return (EIO);
205b819cea2SGordon Ross 			}
206b819cea2SGordon Ross 			VN_HOLD(vp);
207b819cea2SGordon Ross 			vfs_unlock(vfsp);
208b819cea2SGordon Ross 			VN_RELE(cvp);
209b819cea2SGordon Ross 			cvp = NULL;
210b819cea2SGordon Ross 			/*
211b819cea2SGordon Ross 			 * Crossing mount points. For eg: We are doing
212b819cea2SGordon Ross 			 * a lookup of ".." for file systems root vnode
213b819cea2SGordon Ross 			 * mounted here, and VOP_LOOKUP() (with covered vnode)
214b819cea2SGordon Ross 			 * will be on underlying file systems mount point
215b819cea2SGordon Ross 			 * vnode. Set retry_with_kcred flag as we might end
216b819cea2SGordon Ross 			 * up doing VOP_LOOKUP() with kcred if required.
217b819cea2SGordon Ross 			 */
218b819cea2SGordon Ross 			retry_with_kcred = B_TRUE;
219b819cea2SGordon Ross 			goto checkforroot;
220b819cea2SGordon Ross 		}
221b819cea2SGordon Ross 	}
222b819cea2SGordon Ross 
223b819cea2SGordon Ross 	/*
224b819cea2SGordon Ross 	 * Perform a lookup in the current directory.
225b819cea2SGordon Ross 	 */
226b819cea2SGordon Ross 	error = VOP_LOOKUP(vp, component, &tvp, pnp, lookup_flags,
227b819cea2SGordon Ross 	    rootvp, cr, NULL, NULL, pp);
228b819cea2SGordon Ross 
229b819cea2SGordon Ross 	/*
230b819cea2SGordon Ross 	 * Retry with kcred - If crossing mount points & error is EACCES.
231b819cea2SGordon Ross 	 *
232b819cea2SGordon Ross 	 * If we are crossing mount points here and doing ".." lookup,
233b819cea2SGordon Ross 	 * VOP_LOOKUP() might fail if the underlying file systems
234b819cea2SGordon Ross 	 * mount point has no execute permission. In cases like these,
235b819cea2SGordon Ross 	 * we retry VOP_LOOKUP() by giving as much privilage as possible
236b819cea2SGordon Ross 	 * by passing kcred credentials.
237b819cea2SGordon Ross 	 *
238b819cea2SGordon Ross 	 * In case of hierarchical file systems, passing kcred still may
239b819cea2SGordon Ross 	 * or may not work.
240b819cea2SGordon Ross 	 * For eg: UFS FS --> Mount NFS FS --> Again mount UFS on some
241b819cea2SGordon Ross 	 *			directory inside NFS FS.
242b819cea2SGordon Ross 	 */
243b819cea2SGordon Ross 	if ((error == EACCES) && retry_with_kcred)
244b819cea2SGordon Ross 		error = VOP_LOOKUP(vp, component, &tvp, pnp, lookup_flags,
245b819cea2SGordon Ross 		    rootvp, zone_kcred(), NULL, NULL, pp);
246b819cea2SGordon Ross 
247b819cea2SGordon Ross 	cvp = tvp;
248b819cea2SGordon Ross 	if (error) {
249b819cea2SGordon Ross 		cvp = NULL;
250b819cea2SGordon Ross 		/*
251b819cea2SGordon Ross 		 * On error, return hard error if
252b819cea2SGordon Ross 		 * (a) we're not at the end of the pathname yet, or
253b819cea2SGordon Ross 		 * (b) the caller didn't want the parent directory, or
254b819cea2SGordon Ross 		 * (c) we failed for some reason other than a missing entry.
255b819cea2SGordon Ross 		 */
256b819cea2SGordon Ross 		if (pn_pathleft(pnp) || dirvpp == NULL || error != ENOENT)
257b819cea2SGordon Ross 			goto bad;
258b819cea2SGordon Ross 
259b819cea2SGordon Ross 		pn_setlast(pnp);
260b819cea2SGordon Ross 		/*
261b819cea2SGordon Ross 		 * We inform the caller that the desired entry must be
262b819cea2SGordon Ross 		 * a directory by adding a '/' to the component name.
263b819cea2SGordon Ross 		 */
264b819cea2SGordon Ross 		if (must_be_directory && (error = pn_addslash(pnp)) != 0)
265b819cea2SGordon Ross 			goto bad;
266b819cea2SGordon Ross 		*dirvpp = vp;
267b819cea2SGordon Ross 		if (compvpp != NULL)
268b819cea2SGordon Ross 			*compvpp = NULL;
269b819cea2SGordon Ross 		if (rootvp != rootdir)
270b819cea2SGordon Ross 			VN_RELE(rootvp);
271b819cea2SGordon Ross 		if (pp)
272b819cea2SGordon Ross 			pn_free(pp);
273b819cea2SGordon Ross 		return (0);
274b819cea2SGordon Ross 	}
275b819cea2SGordon Ross 
276b819cea2SGordon Ross 	/*
277b819cea2SGordon Ross 	 * Traverse mount points.
278b819cea2SGordon Ross 	 */
279b819cea2SGordon Ross 	if (vn_mountedvfs(cvp) != NULL) {
280b819cea2SGordon Ross 		tvp = cvp;
281b819cea2SGordon Ross 		if ((error = traverse(&tvp)) != 0) {
282b819cea2SGordon Ross 			/*
283b819cea2SGordon Ross 			 * It is required to assign cvp here, because
284b819cea2SGordon Ross 			 * traverse() will return a held vnode which
285b819cea2SGordon Ross 			 * may different than the vnode that was passed
286b819cea2SGordon Ross 			 * in (even in the error case).  If traverse()
287b819cea2SGordon Ross 			 * changes the vnode it releases the original,
288b819cea2SGordon Ross 			 * and holds the new one.
289b819cea2SGordon Ross 			 */
290b819cea2SGordon Ross 			cvp = tvp;
291b819cea2SGordon Ross 			goto bad;
292b819cea2SGordon Ross 		}
293b819cea2SGordon Ross 		cvp = tvp;
294b819cea2SGordon Ross 	}
295b819cea2SGordon Ross 
296b819cea2SGordon Ross 	/*
297b819cea2SGordon Ross 	 * If we hit a symbolic link and there is more path to be
298b819cea2SGordon Ross 	 * translated or this operation does not wish to apply
299b819cea2SGordon Ross 	 * to a link, then place the contents of the link at the
300b819cea2SGordon Ross 	 * front of the remaining pathname.
301b819cea2SGordon Ross 	 */
302b819cea2SGordon Ross 	if (cvp->v_type == VLNK && ((flags & FOLLOW) || pn_pathleft(pnp))) {
303b819cea2SGordon Ross 		struct pathname linkpath;
304b819cea2SGordon Ross 
305b819cea2SGordon Ross 		if (++nlink > MAXSYMLINKS) {
306b819cea2SGordon Ross 			error = ELOOP;
307b819cea2SGordon Ross 			goto bad;
308b819cea2SGordon Ross 		}
309b819cea2SGordon Ross 		pn_alloc(&linkpath);
310*54026d5aSGordon Ross 		if ((error = pn_getsymlink(cvp, &linkpath, cr)) != 0) {
311b819cea2SGordon Ross 			pn_free(&linkpath);
312b819cea2SGordon Ross 			goto bad;
313b819cea2SGordon Ross 		}
314b819cea2SGordon Ross 
315b819cea2SGordon Ross 		if (pn_pathleft(&linkpath) == 0)
316b819cea2SGordon Ross 			(void) pn_set(&linkpath, ".");
317b819cea2SGordon Ross 		error = pn_insert(pnp, &linkpath, strlen(component));
318b819cea2SGordon Ross 		pn_free(&linkpath);
319b819cea2SGordon Ross 		if (error)
320b819cea2SGordon Ross 			goto bad;
321b819cea2SGordon Ross 		VN_RELE(cvp);
322b819cea2SGordon Ross 		cvp = NULL;
323b819cea2SGordon Ross 		if (pnp->pn_pathlen == 0) {
324b819cea2SGordon Ross 			error = ENOENT;
325b819cea2SGordon Ross 			goto bad;
326b819cea2SGordon Ross 		}
327b819cea2SGordon Ross 		if (pnp->pn_path[0] == '/') {
328b819cea2SGordon Ross 			do {
329b819cea2SGordon Ross 				pnp->pn_path++;
330b819cea2SGordon Ross 				pnp->pn_pathlen--;
331b819cea2SGordon Ross 			} while (pnp->pn_path[0] == '/');
332b819cea2SGordon Ross 			VN_RELE(vp);
333b819cea2SGordon Ross 			vp = rootvp;
334b819cea2SGordon Ross 			VN_HOLD(vp);
335b819cea2SGordon Ross 		}
336b819cea2SGordon Ross 		if (pn_fixslash(pnp)) {
337b819cea2SGordon Ross 			flags |= FOLLOW;
338b819cea2SGordon Ross 			must_be_directory = 1;
339b819cea2SGordon Ross 		}
340b819cea2SGordon Ross 		goto next;
341b819cea2SGordon Ross 	}
342b819cea2SGordon Ross 
343b819cea2SGordon Ross 	/*
344b819cea2SGordon Ross 	 * If rpnp is non-NULL, remember the resolved path name therein.
345b819cea2SGordon Ross 	 * Do not include "." components.  Collapse occurrences of
346b819cea2SGordon Ross 	 * "previous/..", so long as "previous" is not itself "..".
347b819cea2SGordon Ross 	 * Exhausting rpnp results in error ENAMETOOLONG.
348b819cea2SGordon Ross 	 */
349b819cea2SGordon Ross 	if (rpnp && strcmp(component, ".") != 0) {
350b819cea2SGordon Ross 		size_t len;
351b819cea2SGordon Ross 
352b819cea2SGordon Ross 		if (strcmp(component, "..") == 0 &&
353b819cea2SGordon Ross 		    rpnp->pn_pathlen != 0 &&
354b819cea2SGordon Ross 		    !((rpnp->pn_pathlen > 2 &&
355b819cea2SGordon Ross 		    strncmp(rpnp->pn_path+rpnp->pn_pathlen-3, "/..", 3) == 0) ||
356b819cea2SGordon Ross 		    (rpnp->pn_pathlen == 2 &&
357b819cea2SGordon Ross 		    strncmp(rpnp->pn_path, "..", 2) == 0))) {
358b819cea2SGordon Ross 			while (rpnp->pn_pathlen &&
359b819cea2SGordon Ross 			    rpnp->pn_path[rpnp->pn_pathlen-1] != '/')
360b819cea2SGordon Ross 				rpnp->pn_pathlen--;
361b819cea2SGordon Ross 			if (rpnp->pn_pathlen > 1)
362b819cea2SGordon Ross 				rpnp->pn_pathlen--;
363b819cea2SGordon Ross 			rpnp->pn_path[rpnp->pn_pathlen] = '\0';
364b819cea2SGordon Ross 		} else {
365b819cea2SGordon Ross 			if (rpnp->pn_pathlen != 0 &&
366b819cea2SGordon Ross 			    rpnp->pn_path[rpnp->pn_pathlen-1] != '/')
367b819cea2SGordon Ross 				rpnp->pn_path[rpnp->pn_pathlen++] = '/';
368b819cea2SGordon Ross 			if (flags & FIGNORECASE) {
369b819cea2SGordon Ross 				/*
370b819cea2SGordon Ross 				 * Return the case-preserved name
371b819cea2SGordon Ross 				 * within the resolved path.
372b819cea2SGordon Ross 				 */
373b819cea2SGordon Ross 				error = copystr(pp->pn_buf,
374b819cea2SGordon Ross 				    rpnp->pn_path + rpnp->pn_pathlen,
375b819cea2SGordon Ross 				    rpnp->pn_bufsize - rpnp->pn_pathlen, &len);
376b819cea2SGordon Ross 			} else {
377b819cea2SGordon Ross 				error = copystr(component,
378b819cea2SGordon Ross 				    rpnp->pn_path + rpnp->pn_pathlen,
379b819cea2SGordon Ross 				    rpnp->pn_bufsize - rpnp->pn_pathlen, &len);
380b819cea2SGordon Ross 			}
381b819cea2SGordon Ross 			if (error)	/* copystr() returns ENAMETOOLONG */
382b819cea2SGordon Ross 				goto bad;
383b819cea2SGordon Ross 			rpnp->pn_pathlen += (len - 1);
384b819cea2SGordon Ross 			ASSERT(rpnp->pn_bufsize > rpnp->pn_pathlen);
385b819cea2SGordon Ross 		}
386b819cea2SGordon Ross 	}
387b819cea2SGordon Ross 
388b819cea2SGordon Ross 	/*
389b819cea2SGordon Ross 	 * If no more components, return last directory (if wanted) and
390b819cea2SGordon Ross 	 * last component (if wanted).
391b819cea2SGordon Ross 	 */
392b819cea2SGordon Ross 	if (pn_pathleft(pnp) == 0) {
393b819cea2SGordon Ross 		/*
394b819cea2SGordon Ross 		 * If there was a trailing slash in the pathname,
395b819cea2SGordon Ross 		 * make sure the last component is a directory.
396b819cea2SGordon Ross 		 */
397b819cea2SGordon Ross 		if (must_be_directory && cvp->v_type != VDIR) {
398b819cea2SGordon Ross 			error = ENOTDIR;
399b819cea2SGordon Ross 			goto bad;
400b819cea2SGordon Ross 		}
401b819cea2SGordon Ross 		if (dirvpp != NULL) {
402b819cea2SGordon Ross 			/*
403b819cea2SGordon Ross 			 * Check that we have the real parent and not
404b819cea2SGordon Ross 			 * an alias of the last component.
405b819cea2SGordon Ross 			 */
406b819cea2SGordon Ross 			if (vn_compare(vp, cvp)) {
407b819cea2SGordon Ross 				pn_setlast(pnp);
408b819cea2SGordon Ross 				VN_RELE(vp);
409b819cea2SGordon Ross 				VN_RELE(cvp);
410b819cea2SGordon Ross 				if (rootvp != rootdir)
411b819cea2SGordon Ross 					VN_RELE(rootvp);
412b819cea2SGordon Ross 				if (pp)
413b819cea2SGordon Ross 					pn_free(pp);
414b819cea2SGordon Ross 				return (EINVAL);
415b819cea2SGordon Ross 			}
416b819cea2SGordon Ross 			*dirvpp = vp;
417b819cea2SGordon Ross 		} else
418b819cea2SGordon Ross 			VN_RELE(vp);
419b819cea2SGordon Ross 		if (pnp->pn_path == pnp->pn_buf)
420b819cea2SGordon Ross 			(void) pn_set(pnp, ".");
421b819cea2SGordon Ross 		else
422b819cea2SGordon Ross 			pn_setlast(pnp);
423b819cea2SGordon Ross 		if (rpnp) {
424b819cea2SGordon Ross 			if (VN_CMP(cvp, rootvp))
425b819cea2SGordon Ross 				(void) pn_set(rpnp, "/");
426b819cea2SGordon Ross 			else if (rpnp->pn_pathlen == 0)
427b819cea2SGordon Ross 				(void) pn_set(rpnp, ".");
428b819cea2SGordon Ross 		}
429b819cea2SGordon Ross 
430b819cea2SGordon Ross 		if (compvpp != NULL)
431b819cea2SGordon Ross 			*compvpp = cvp;
432b819cea2SGordon Ross 		else
433b819cea2SGordon Ross 			VN_RELE(cvp);
434b819cea2SGordon Ross 		if (rootvp != rootdir)
435b819cea2SGordon Ross 			VN_RELE(rootvp);
436b819cea2SGordon Ross 		if (pp)
437b819cea2SGordon Ross 			pn_free(pp);
438b819cea2SGordon Ross 		return (0);
439b819cea2SGordon Ross 	}
440b819cea2SGordon Ross 
441b819cea2SGordon Ross 	/*
442b819cea2SGordon Ross 	 * Skip over slashes from end of last component.
443b819cea2SGordon Ross 	 */
444b819cea2SGordon Ross 	while (pnp->pn_path[0] == '/') {
445b819cea2SGordon Ross 		pnp->pn_path++;
446b819cea2SGordon Ross 		pnp->pn_pathlen--;
447b819cea2SGordon Ross 	}
448b819cea2SGordon Ross 
449b819cea2SGordon Ross 	/*
450b819cea2SGordon Ross 	 * Searched through another level of directory:
451b819cea2SGordon Ross 	 * release previous directory handle and save new (result
452b819cea2SGordon Ross 	 * of lookup) as current directory.
453b819cea2SGordon Ross 	 */
454b819cea2SGordon Ross 	VN_RELE(vp);
455b819cea2SGordon Ross 	vp = cvp;
456b819cea2SGordon Ross 	cvp = NULL;
457b819cea2SGordon Ross 	goto next;
458b819cea2SGordon Ross 
459b819cea2SGordon Ross bad:
460b819cea2SGordon Ross 	/*
461b819cea2SGordon Ross 	 * Error.  Release vnodes and return.
462b819cea2SGordon Ross 	 */
463b819cea2SGordon Ross 	if (cvp)
464b819cea2SGordon Ross 		VN_RELE(cvp);
465b819cea2SGordon Ross 	/*
466b819cea2SGordon Ross 	 * If the error was ESTALE and the current directory to look in
467b819cea2SGordon Ross 	 * was the root for this lookup, the root for a mounted file
468b819cea2SGordon Ross 	 * system, or the starting directory for lookups, then
469b819cea2SGordon Ross 	 * return ENOENT instead of ESTALE.  In this case, no recovery
470b819cea2SGordon Ross 	 * is possible by the higher level.  If ESTALE was returned for
471b819cea2SGordon Ross 	 * some intermediate directory along the path, then recovery
472b819cea2SGordon Ross 	 * is potentially possible and retrying from the higher level
473b819cea2SGordon Ross 	 * will either correct the situation by purging stale cache
474b819cea2SGordon Ross 	 * entries or eventually get back to the point where no recovery
475b819cea2SGordon Ross 	 * is possible.
476b819cea2SGordon Ross 	 */
477b819cea2SGordon Ross 	if (error == ESTALE &&
478b819cea2SGordon Ross 	    (VN_CMP(vp, rootvp) || (vp->v_flag & VROOT) || vp == startvp))
479b819cea2SGordon Ross 		error = ENOENT;
480b819cea2SGordon Ross 	VN_RELE(vp);
481b819cea2SGordon Ross 	if (rootvp != rootdir)
482b819cea2SGordon Ross 		VN_RELE(rootvp);
483b819cea2SGordon Ross 	if (pp)
484b819cea2SGordon Ross 		pn_free(pp);
485b819cea2SGordon Ross 	return (error);
486b819cea2SGordon Ross }
487b819cea2SGordon Ross 
488b819cea2SGordon Ross /*
489b819cea2SGordon Ross  * Traverse a mount point.  Routine accepts a vnode pointer as a reference
490b819cea2SGordon Ross  * parameter and performs the indirection, releasing the original vnode.
491b819cea2SGordon Ross  */
492b819cea2SGordon Ross int
493b819cea2SGordon Ross traverse(vnode_t **cvpp)
494b819cea2SGordon Ross {
495b819cea2SGordon Ross 	int error = 0;
496b819cea2SGordon Ross 	vnode_t *cvp;
497b819cea2SGordon Ross 	vnode_t *tvp;
498b819cea2SGordon Ross 	vfs_t *vfsp;
499b819cea2SGordon Ross 
500b819cea2SGordon Ross 	cvp = *cvpp;
501b819cea2SGordon Ross 
502b819cea2SGordon Ross 	/*
503b819cea2SGordon Ross 	 * If this vnode is mounted on, then we transparently indirect
504b819cea2SGordon Ross 	 * to the vnode which is the root of the mounted file system.
505b819cea2SGordon Ross 	 * Before we do this we must check that an unmount is not in
506b819cea2SGordon Ross 	 * progress on this vnode.
507b819cea2SGordon Ross 	 */
508b819cea2SGordon Ross 
509b819cea2SGordon Ross 	for (;;) {
510b819cea2SGordon Ross 		/*
511b819cea2SGordon Ross 		 * Used to try to read lock the vnode here.
512b819cea2SGordon Ross 		 */
513b819cea2SGordon Ross 
514b819cea2SGordon Ross 		/*
515b819cea2SGordon Ross 		 * Reached the end of the mount chain?
516b819cea2SGordon Ross 		 */
517b819cea2SGordon Ross 		vfsp = vn_mountedvfs(cvp);
518b819cea2SGordon Ross 		if (vfsp == NULL) {
519b819cea2SGordon Ross 			break;
520b819cea2SGordon Ross 		}
521b819cea2SGordon Ross 
522b819cea2SGordon Ross 		/*
523b819cea2SGordon Ross 		 * The read lock must be held across the call to VFS_ROOT() to
524b819cea2SGordon Ross 		 * prevent a concurrent unmount from destroying the vfs.
525b819cea2SGordon Ross 		 */
526b819cea2SGordon Ross 		error = VFS_ROOT(vfsp, &tvp);
527b819cea2SGordon Ross 		if (error)
528b819cea2SGordon Ross 			break;
529b819cea2SGordon Ross 
530b819cea2SGordon Ross 		VN_RELE(cvp);
531b819cea2SGordon Ross 
532b819cea2SGordon Ross 		cvp = tvp;
533b819cea2SGordon Ross 	}
534b819cea2SGordon Ross 
535b819cea2SGordon Ross 	*cvpp = cvp;
536b819cea2SGordon Ross 	return (error);
537b819cea2SGordon Ross }
538b819cea2SGordon Ross 
539b819cea2SGordon Ross /*
540b819cea2SGordon Ross  * Get the vnode path, relative to the passed rootvp.
541b819cea2SGordon Ross  * Our vncache always fills in v_path, so this is easy.
542b819cea2SGordon Ross  */
543b819cea2SGordon Ross /* ARGSUSED */
544b819cea2SGordon Ross int
545b819cea2SGordon Ross vnodetopath(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen, cred_t *cr)
546b819cea2SGordon Ross {
547b819cea2SGordon Ross 	int len, rvp_len = 0;
548b819cea2SGordon Ross 	const char *p = vp->v_path;
549b819cea2SGordon Ross 
550b819cea2SGordon Ross 	if (vrootp)
551b819cea2SGordon Ross 		rvp_len = strlen(vrootp->v_path);
552b819cea2SGordon Ross 	len = strlen(p);
553b819cea2SGordon Ross 	if (rvp_len < len)
554b819cea2SGordon Ross 		p += rvp_len;
555b819cea2SGordon Ross 	else
556b819cea2SGordon Ross 		p = "/";
557b819cea2SGordon Ross 
558b819cea2SGordon Ross 	(void) strlcpy(buf, p, buflen);
559b819cea2SGordon Ross 	return (0);
560b819cea2SGordon Ross }
561