xref: /illumos-gate/usr/src/uts/common/fs/lookup.c (revision 7c478bd9)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
28*7c478bd9Sstevel@tonic-gate /*	  All Rights Reserved  	*/
29*7c478bd9Sstevel@tonic-gate 
30*7c478bd9Sstevel@tonic-gate /*
31*7c478bd9Sstevel@tonic-gate  * University Copyright- Copyright (c) 1982, 1986, 1988
32*7c478bd9Sstevel@tonic-gate  * The Regents of the University of California
33*7c478bd9Sstevel@tonic-gate  * All Rights Reserved
34*7c478bd9Sstevel@tonic-gate  *
35*7c478bd9Sstevel@tonic-gate  * University Acknowledgment- Portions of this document are derived from
36*7c478bd9Sstevel@tonic-gate  * software developed by the University of California, Berkeley, and its
37*7c478bd9Sstevel@tonic-gate  * contributors.
38*7c478bd9Sstevel@tonic-gate  */
39*7c478bd9Sstevel@tonic-gate 
40*7c478bd9Sstevel@tonic-gate 
41*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
42*7c478bd9Sstevel@tonic-gate 
43*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
44*7c478bd9Sstevel@tonic-gate #include <sys/param.h>
45*7c478bd9Sstevel@tonic-gate #include <sys/systm.h>
46*7c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h>
47*7c478bd9Sstevel@tonic-gate #include <sys/errno.h>
48*7c478bd9Sstevel@tonic-gate #include <sys/cred.h>
49*7c478bd9Sstevel@tonic-gate #include <sys/user.h>
50*7c478bd9Sstevel@tonic-gate #include <sys/uio.h>
51*7c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
52*7c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
53*7c478bd9Sstevel@tonic-gate #include <sys/pathname.h>
54*7c478bd9Sstevel@tonic-gate #include <sys/proc.h>
55*7c478bd9Sstevel@tonic-gate #include <sys/vtrace.h>
56*7c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
57*7c478bd9Sstevel@tonic-gate #include <sys/debug.h>
58*7c478bd9Sstevel@tonic-gate #include <sys/dirent.h>
59*7c478bd9Sstevel@tonic-gate #include <c2/audit.h>
60*7c478bd9Sstevel@tonic-gate #include <sys/zone.h>
61*7c478bd9Sstevel@tonic-gate #include <sys/dnlc.h>
62*7c478bd9Sstevel@tonic-gate #include <sys/fs/snode.h>
63*7c478bd9Sstevel@tonic-gate 
64*7c478bd9Sstevel@tonic-gate /* Controls whether paths are stored with vnodes. */
65*7c478bd9Sstevel@tonic-gate int vfs_vnode_path = 1;
66*7c478bd9Sstevel@tonic-gate 
67*7c478bd9Sstevel@tonic-gate int
68*7c478bd9Sstevel@tonic-gate lookupname(
69*7c478bd9Sstevel@tonic-gate 	char *fnamep,
70*7c478bd9Sstevel@tonic-gate 	enum uio_seg seg,
71*7c478bd9Sstevel@tonic-gate 	enum symfollow followlink,
72*7c478bd9Sstevel@tonic-gate 	vnode_t **dirvpp,
73*7c478bd9Sstevel@tonic-gate 	vnode_t **compvpp)
74*7c478bd9Sstevel@tonic-gate {
75*7c478bd9Sstevel@tonic-gate 	return (lookupnameat(fnamep, seg, followlink, dirvpp, compvpp, NULL));
76*7c478bd9Sstevel@tonic-gate }
77*7c478bd9Sstevel@tonic-gate 
78*7c478bd9Sstevel@tonic-gate 
79*7c478bd9Sstevel@tonic-gate /*
80*7c478bd9Sstevel@tonic-gate  * Lookup the user file name,
81*7c478bd9Sstevel@tonic-gate  * Handle allocation and freeing of pathname buffer, return error.
82*7c478bd9Sstevel@tonic-gate  */
83*7c478bd9Sstevel@tonic-gate int
84*7c478bd9Sstevel@tonic-gate lookupnameat(
85*7c478bd9Sstevel@tonic-gate 	char *fnamep,			/* user pathname */
86*7c478bd9Sstevel@tonic-gate 	enum uio_seg seg,		/* addr space that name is in */
87*7c478bd9Sstevel@tonic-gate 	enum symfollow followlink,	/* follow sym links */
88*7c478bd9Sstevel@tonic-gate 	vnode_t **dirvpp,		/* ret for ptr to parent dir vnode */
89*7c478bd9Sstevel@tonic-gate 	vnode_t **compvpp,		/* ret for ptr to component vnode */
90*7c478bd9Sstevel@tonic-gate 	vnode_t *startvp)		/* start path search from vp */
91*7c478bd9Sstevel@tonic-gate {
92*7c478bd9Sstevel@tonic-gate 	char namebuf[TYPICALMAXPATHLEN];
93*7c478bd9Sstevel@tonic-gate 	struct pathname lookpn;
94*7c478bd9Sstevel@tonic-gate 	int error;
95*7c478bd9Sstevel@tonic-gate 
96*7c478bd9Sstevel@tonic-gate 	error = pn_get_buf(fnamep, seg, &lookpn, namebuf, sizeof (namebuf));
97*7c478bd9Sstevel@tonic-gate 	if (error == 0) {
98*7c478bd9Sstevel@tonic-gate #ifdef C2_AUDIT
99*7c478bd9Sstevel@tonic-gate 		if (audit_active)
100*7c478bd9Sstevel@tonic-gate 			audit_lookupname();
101*7c478bd9Sstevel@tonic-gate #endif
102*7c478bd9Sstevel@tonic-gate 		error = lookuppnat(&lookpn, NULL, followlink,
103*7c478bd9Sstevel@tonic-gate 		    dirvpp, compvpp, startvp);
104*7c478bd9Sstevel@tonic-gate 	}
105*7c478bd9Sstevel@tonic-gate 	if (error == ENAMETOOLONG) {
106*7c478bd9Sstevel@tonic-gate 		/*
107*7c478bd9Sstevel@tonic-gate 		 * This thread used a pathname > TYPICALMAXPATHLEN bytes long.
108*7c478bd9Sstevel@tonic-gate 		 */
109*7c478bd9Sstevel@tonic-gate 		if (error = pn_get(fnamep, seg, &lookpn))
110*7c478bd9Sstevel@tonic-gate 			return (error);
111*7c478bd9Sstevel@tonic-gate 		error = lookuppnat(&lookpn, NULL, followlink,
112*7c478bd9Sstevel@tonic-gate 		    dirvpp, compvpp, startvp);
113*7c478bd9Sstevel@tonic-gate 		pn_free(&lookpn);
114*7c478bd9Sstevel@tonic-gate 	}
115*7c478bd9Sstevel@tonic-gate 
116*7c478bd9Sstevel@tonic-gate 	return (error);
117*7c478bd9Sstevel@tonic-gate }
118*7c478bd9Sstevel@tonic-gate 
119*7c478bd9Sstevel@tonic-gate /*
120*7c478bd9Sstevel@tonic-gate  * Lookup the user file name from a given vp,
121*7c478bd9Sstevel@tonic-gate  */
122*7c478bd9Sstevel@tonic-gate int
123*7c478bd9Sstevel@tonic-gate lookuppn(
124*7c478bd9Sstevel@tonic-gate 	struct pathname *pnp,
125*7c478bd9Sstevel@tonic-gate 	struct pathname *rpnp,
126*7c478bd9Sstevel@tonic-gate 	enum symfollow followlink,
127*7c478bd9Sstevel@tonic-gate 	vnode_t **dirvpp,
128*7c478bd9Sstevel@tonic-gate 	vnode_t **compvpp)
129*7c478bd9Sstevel@tonic-gate {
130*7c478bd9Sstevel@tonic-gate 	return (lookuppnat(pnp, rpnp, followlink, dirvpp, compvpp, NULL));
131*7c478bd9Sstevel@tonic-gate }
132*7c478bd9Sstevel@tonic-gate 
133*7c478bd9Sstevel@tonic-gate int
134*7c478bd9Sstevel@tonic-gate lookuppnat(
135*7c478bd9Sstevel@tonic-gate 	struct pathname *pnp,		/* pathname to lookup */
136*7c478bd9Sstevel@tonic-gate 	struct pathname *rpnp,		/* if non-NULL, return resolved path */
137*7c478bd9Sstevel@tonic-gate 	enum symfollow followlink,	/* (don't) follow sym links */
138*7c478bd9Sstevel@tonic-gate 	vnode_t **dirvpp,		/* ptr for parent vnode */
139*7c478bd9Sstevel@tonic-gate 	vnode_t **compvpp,		/* ptr for entry vnode */
140*7c478bd9Sstevel@tonic-gate 	vnode_t *startvp)		/* start search from this vp */
141*7c478bd9Sstevel@tonic-gate {
142*7c478bd9Sstevel@tonic-gate 	vnode_t *vp;	/* current directory vp */
143*7c478bd9Sstevel@tonic-gate 	vnode_t *rootvp;
144*7c478bd9Sstevel@tonic-gate 	proc_t *p = curproc;
145*7c478bd9Sstevel@tonic-gate 
146*7c478bd9Sstevel@tonic-gate 	if (pnp->pn_pathlen == 0)
147*7c478bd9Sstevel@tonic-gate 		return (ENOENT);
148*7c478bd9Sstevel@tonic-gate 
149*7c478bd9Sstevel@tonic-gate 	mutex_enter(&p->p_lock);	/* for u_rdir and u_cdir */
150*7c478bd9Sstevel@tonic-gate 	if ((rootvp = PTOU(p)->u_rdir) == NULL)
151*7c478bd9Sstevel@tonic-gate 		rootvp = rootdir;
152*7c478bd9Sstevel@tonic-gate 	else if (rootvp != rootdir)	/* no need to VN_HOLD rootdir */
153*7c478bd9Sstevel@tonic-gate 		VN_HOLD(rootvp);
154*7c478bd9Sstevel@tonic-gate 
155*7c478bd9Sstevel@tonic-gate 	if (pnp->pn_path[0] == '/') {
156*7c478bd9Sstevel@tonic-gate 		vp = rootvp;
157*7c478bd9Sstevel@tonic-gate 	} else {
158*7c478bd9Sstevel@tonic-gate 		vp = (startvp == NULL) ? PTOU(p)->u_cdir : startvp;
159*7c478bd9Sstevel@tonic-gate 	}
160*7c478bd9Sstevel@tonic-gate 	VN_HOLD(vp);
161*7c478bd9Sstevel@tonic-gate 	mutex_exit(&p->p_lock);
162*7c478bd9Sstevel@tonic-gate 
163*7c478bd9Sstevel@tonic-gate 	/*
164*7c478bd9Sstevel@tonic-gate 	 * Skip over leading slashes
165*7c478bd9Sstevel@tonic-gate 	 */
166*7c478bd9Sstevel@tonic-gate 	if (pnp->pn_path[0] == '/') {
167*7c478bd9Sstevel@tonic-gate 		do {
168*7c478bd9Sstevel@tonic-gate 			pnp->pn_path++;
169*7c478bd9Sstevel@tonic-gate 			pnp->pn_pathlen--;
170*7c478bd9Sstevel@tonic-gate 		} while (pnp->pn_path[0] == '/');
171*7c478bd9Sstevel@tonic-gate 	}
172*7c478bd9Sstevel@tonic-gate 
173*7c478bd9Sstevel@tonic-gate 	return (lookuppnvp(pnp, rpnp, followlink, dirvpp,
174*7c478bd9Sstevel@tonic-gate 	    compvpp, rootvp, vp, CRED()));
175*7c478bd9Sstevel@tonic-gate }
176*7c478bd9Sstevel@tonic-gate 
177*7c478bd9Sstevel@tonic-gate /* Private flag to do our getcwd() dirty work */
178*7c478bd9Sstevel@tonic-gate #define	LOOKUP_CHECKREAD	0x10
179*7c478bd9Sstevel@tonic-gate #define	LOOKUP_MASK		(~LOOKUP_CHECKREAD)
180*7c478bd9Sstevel@tonic-gate 
181*7c478bd9Sstevel@tonic-gate /*
182*7c478bd9Sstevel@tonic-gate  * Starting at current directory, translate pathname pnp to end.
183*7c478bd9Sstevel@tonic-gate  * Leave pathname of final component in pnp, return the vnode
184*7c478bd9Sstevel@tonic-gate  * for the final component in *compvpp, and return the vnode
185*7c478bd9Sstevel@tonic-gate  * for the parent of the final component in dirvpp.
186*7c478bd9Sstevel@tonic-gate  *
187*7c478bd9Sstevel@tonic-gate  * This is the central routine in pathname translation and handles
188*7c478bd9Sstevel@tonic-gate  * multiple components in pathnames, separating them at /'s.  It also
189*7c478bd9Sstevel@tonic-gate  * implements mounted file systems and processes symbolic links.
190*7c478bd9Sstevel@tonic-gate  *
191*7c478bd9Sstevel@tonic-gate  * vp is the vnode where the directory search should start.
192*7c478bd9Sstevel@tonic-gate  *
193*7c478bd9Sstevel@tonic-gate  * Reference counts: vp must be held prior to calling this function.  rootvp
194*7c478bd9Sstevel@tonic-gate  * should only be held if rootvp != rootdir.
195*7c478bd9Sstevel@tonic-gate  */
196*7c478bd9Sstevel@tonic-gate int
197*7c478bd9Sstevel@tonic-gate lookuppnvp(
198*7c478bd9Sstevel@tonic-gate 	struct pathname *pnp,		/* pathname to lookup */
199*7c478bd9Sstevel@tonic-gate 	struct pathname *rpnp,		/* if non-NULL, return resolved path */
200*7c478bd9Sstevel@tonic-gate 	int flags,			/* follow symlinks */
201*7c478bd9Sstevel@tonic-gate 	vnode_t **dirvpp,		/* ptr for parent vnode */
202*7c478bd9Sstevel@tonic-gate 	vnode_t **compvpp,		/* ptr for entry vnode */
203*7c478bd9Sstevel@tonic-gate 	vnode_t *rootvp,		/* rootvp */
204*7c478bd9Sstevel@tonic-gate 	vnode_t *vp,			/* directory to start search at */
205*7c478bd9Sstevel@tonic-gate 	cred_t *cr)			/* user's credential */
206*7c478bd9Sstevel@tonic-gate {
207*7c478bd9Sstevel@tonic-gate 	vnode_t *cvp;	/* current component vp */
208*7c478bd9Sstevel@tonic-gate 	vnode_t *tvp;	/* addressable temp ptr */
209*7c478bd9Sstevel@tonic-gate 	char component[MAXNAMELEN];	/* buffer for component (incl null) */
210*7c478bd9Sstevel@tonic-gate 	int error;
211*7c478bd9Sstevel@tonic-gate 	int nlink;
212*7c478bd9Sstevel@tonic-gate 	int lookup_flags;
213*7c478bd9Sstevel@tonic-gate 	vnode_t *startvp;
214*7c478bd9Sstevel@tonic-gate 	vnode_t *zonevp = curproc->p_zone->zone_rootvp;		/* zone root */
215*7c478bd9Sstevel@tonic-gate 	int must_be_directory = 0;
216*7c478bd9Sstevel@tonic-gate 	size_t plen;
217*7c478bd9Sstevel@tonic-gate 
218*7c478bd9Sstevel@tonic-gate 	CPU_STATS_ADDQ(CPU, sys, namei, 1);
219*7c478bd9Sstevel@tonic-gate 	nlink = 0;
220*7c478bd9Sstevel@tonic-gate 	cvp = NULL;
221*7c478bd9Sstevel@tonic-gate 	if (rpnp)
222*7c478bd9Sstevel@tonic-gate 		rpnp->pn_pathlen = 0;
223*7c478bd9Sstevel@tonic-gate 	lookup_flags = dirvpp ? LOOKUP_DIR : 0;
224*7c478bd9Sstevel@tonic-gate #ifdef C2_AUDIT
225*7c478bd9Sstevel@tonic-gate 	if (audit_active)
226*7c478bd9Sstevel@tonic-gate 		audit_anchorpath(pnp, vp == rootvp);
227*7c478bd9Sstevel@tonic-gate #endif
228*7c478bd9Sstevel@tonic-gate 
229*7c478bd9Sstevel@tonic-gate 	/*
230*7c478bd9Sstevel@tonic-gate 	 * Eliminate any trailing slashes in the pathname.
231*7c478bd9Sstevel@tonic-gate 	 * If there are any, we must follow all symlinks.
232*7c478bd9Sstevel@tonic-gate 	 * Also, we must guarantee that the last component is a directory.
233*7c478bd9Sstevel@tonic-gate 	 */
234*7c478bd9Sstevel@tonic-gate 	if (pn_fixslash(pnp)) {
235*7c478bd9Sstevel@tonic-gate 		flags |= FOLLOW;
236*7c478bd9Sstevel@tonic-gate 		must_be_directory = 1;
237*7c478bd9Sstevel@tonic-gate 	}
238*7c478bd9Sstevel@tonic-gate 
239*7c478bd9Sstevel@tonic-gate 	startvp = vp;
240*7c478bd9Sstevel@tonic-gate next:
241*7c478bd9Sstevel@tonic-gate 	/*
242*7c478bd9Sstevel@tonic-gate 	 * Make sure we have a directory.
243*7c478bd9Sstevel@tonic-gate 	 */
244*7c478bd9Sstevel@tonic-gate 	if (vp->v_type != VDIR) {
245*7c478bd9Sstevel@tonic-gate 		error = ENOTDIR;
246*7c478bd9Sstevel@tonic-gate 		goto bad;
247*7c478bd9Sstevel@tonic-gate 	}
248*7c478bd9Sstevel@tonic-gate 
249*7c478bd9Sstevel@tonic-gate 	if (rpnp && VN_CMP(vp, rootvp))
250*7c478bd9Sstevel@tonic-gate 		(void) pn_set(rpnp, "/");
251*7c478bd9Sstevel@tonic-gate 
252*7c478bd9Sstevel@tonic-gate 	/*
253*7c478bd9Sstevel@tonic-gate 	 * Process the next component of the pathname.
254*7c478bd9Sstevel@tonic-gate 	 */
255*7c478bd9Sstevel@tonic-gate 	if (error = pn_getcomponent(pnp, component)) {
256*7c478bd9Sstevel@tonic-gate #ifdef C2_AUDIT
257*7c478bd9Sstevel@tonic-gate 		if (audit_active)
258*7c478bd9Sstevel@tonic-gate 			audit_addcomponent(pnp);
259*7c478bd9Sstevel@tonic-gate #endif
260*7c478bd9Sstevel@tonic-gate 		goto bad;
261*7c478bd9Sstevel@tonic-gate 	}
262*7c478bd9Sstevel@tonic-gate 
263*7c478bd9Sstevel@tonic-gate 	/*
264*7c478bd9Sstevel@tonic-gate 	 * Handle "..": two special cases.
265*7c478bd9Sstevel@tonic-gate 	 * 1. If we're at the root directory (e.g. after chroot or
266*7c478bd9Sstevel@tonic-gate 	 *    zone_enter) then change ".." to "." so we can't get
267*7c478bd9Sstevel@tonic-gate 	 *    out of this subtree.
268*7c478bd9Sstevel@tonic-gate 	 * 2. If this vnode is the root of a mounted file system,
269*7c478bd9Sstevel@tonic-gate 	 *    then replace it with the vnode that was mounted on
270*7c478bd9Sstevel@tonic-gate 	 *    so that we take the ".." in the other file system.
271*7c478bd9Sstevel@tonic-gate 	 */
272*7c478bd9Sstevel@tonic-gate 	if (component[0] == '.' && component[1] == '.' && component[2] == 0) {
273*7c478bd9Sstevel@tonic-gate checkforroot:
274*7c478bd9Sstevel@tonic-gate 		if (VN_CMP(vp, rootvp) || VN_CMP(vp, zonevp)) {
275*7c478bd9Sstevel@tonic-gate 			component[1] = '\0';
276*7c478bd9Sstevel@tonic-gate 		} else if (vp->v_flag & VROOT) {
277*7c478bd9Sstevel@tonic-gate 			vfs_t *vfsp;
278*7c478bd9Sstevel@tonic-gate 			cvp = vp;
279*7c478bd9Sstevel@tonic-gate 
280*7c478bd9Sstevel@tonic-gate 			/*
281*7c478bd9Sstevel@tonic-gate 			 * While we deal with the vfs pointer from the vnode
282*7c478bd9Sstevel@tonic-gate 			 * the filesystem could have been forcefully unmounted
283*7c478bd9Sstevel@tonic-gate 			 * and the vnode's v_vfsp could have been invalidated
284*7c478bd9Sstevel@tonic-gate 			 * by VFS_UNMOUNT. Hence, we cache v_vfsp and use it
285*7c478bd9Sstevel@tonic-gate 			 * with vfs_rlock_wait/vfs_unlock.
286*7c478bd9Sstevel@tonic-gate 			 * It is safe to use the v_vfsp even it is freed by
287*7c478bd9Sstevel@tonic-gate 			 * VFS_UNMOUNT because vfs_rlock_wait/vfs_unlock
288*7c478bd9Sstevel@tonic-gate 			 * do not dereference v_vfsp. It is just used as a
289*7c478bd9Sstevel@tonic-gate 			 * magic cookie.
290*7c478bd9Sstevel@tonic-gate 			 * One more corner case here is the memory getting
291*7c478bd9Sstevel@tonic-gate 			 * reused for another vfs structure. In this case
292*7c478bd9Sstevel@tonic-gate 			 * lookuppnvp's vfs_rlock_wait will succeed, domount's
293*7c478bd9Sstevel@tonic-gate 			 * vfs_lock will fail and domount will bail out with an
294*7c478bd9Sstevel@tonic-gate 			 * error (EBUSY).
295*7c478bd9Sstevel@tonic-gate 			 */
296*7c478bd9Sstevel@tonic-gate 			vfsp = cvp->v_vfsp;
297*7c478bd9Sstevel@tonic-gate 
298*7c478bd9Sstevel@tonic-gate 			/*
299*7c478bd9Sstevel@tonic-gate 			 * This lock is used to synchronize
300*7c478bd9Sstevel@tonic-gate 			 * mounts/unmounts and lookups.
301*7c478bd9Sstevel@tonic-gate 			 * Threads doing mounts/unmounts hold the
302*7c478bd9Sstevel@tonic-gate 			 * writers version vfs_lock_wait().
303*7c478bd9Sstevel@tonic-gate 			 */
304*7c478bd9Sstevel@tonic-gate 
305*7c478bd9Sstevel@tonic-gate 			vfs_rlock_wait(vfsp);
306*7c478bd9Sstevel@tonic-gate 
307*7c478bd9Sstevel@tonic-gate 			/*
308*7c478bd9Sstevel@tonic-gate 			 * If this vnode is on a file system that
309*7c478bd9Sstevel@tonic-gate 			 * has been forcibly unmounted,
310*7c478bd9Sstevel@tonic-gate 			 * we can't proceed. Cancel this operation
311*7c478bd9Sstevel@tonic-gate 			 * and return EIO.
312*7c478bd9Sstevel@tonic-gate 			 *
313*7c478bd9Sstevel@tonic-gate 			 * vfs_vnodecovered is NULL if unmounted.
314*7c478bd9Sstevel@tonic-gate 			 * Currently, nfs uses VFS_UNMOUNTED to
315*7c478bd9Sstevel@tonic-gate 			 * check if it's a forced-umount. Keep the
316*7c478bd9Sstevel@tonic-gate 			 * same checking here as well even though it
317*7c478bd9Sstevel@tonic-gate 			 * may not be needed.
318*7c478bd9Sstevel@tonic-gate 			 */
319*7c478bd9Sstevel@tonic-gate 			if (((vp = cvp->v_vfsp->vfs_vnodecovered) == NULL) ||
320*7c478bd9Sstevel@tonic-gate 			    (cvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)) {
321*7c478bd9Sstevel@tonic-gate 				vfs_unlock(vfsp);
322*7c478bd9Sstevel@tonic-gate 				VN_RELE(cvp);
323*7c478bd9Sstevel@tonic-gate 				return (EIO);
324*7c478bd9Sstevel@tonic-gate 			}
325*7c478bd9Sstevel@tonic-gate 			VN_HOLD(vp);
326*7c478bd9Sstevel@tonic-gate 			vfs_unlock(vfsp);
327*7c478bd9Sstevel@tonic-gate 			VN_RELE(cvp);
328*7c478bd9Sstevel@tonic-gate 			cvp = NULL;
329*7c478bd9Sstevel@tonic-gate 			goto checkforroot;
330*7c478bd9Sstevel@tonic-gate 		}
331*7c478bd9Sstevel@tonic-gate 	}
332*7c478bd9Sstevel@tonic-gate 
333*7c478bd9Sstevel@tonic-gate 	/*
334*7c478bd9Sstevel@tonic-gate 	 * LOOKUP_CHECKREAD is a private flag used by vnodetopath() to indicate
335*7c478bd9Sstevel@tonic-gate 	 * that we need to have read permission on every directory in the entire
336*7c478bd9Sstevel@tonic-gate 	 * path.  This is used to ensure that a forward-lookup of a cached value
337*7c478bd9Sstevel@tonic-gate 	 * has the same effect as a reverse-lookup when the cached value cannot
338*7c478bd9Sstevel@tonic-gate 	 * be found.
339*7c478bd9Sstevel@tonic-gate 	 */
340*7c478bd9Sstevel@tonic-gate 	if ((flags & LOOKUP_CHECKREAD) &&
341*7c478bd9Sstevel@tonic-gate 	    (error = VOP_ACCESS(vp, VREAD, 0, cr)) != 0)
342*7c478bd9Sstevel@tonic-gate 		goto bad;
343*7c478bd9Sstevel@tonic-gate 
344*7c478bd9Sstevel@tonic-gate 	/*
345*7c478bd9Sstevel@tonic-gate 	 * Perform a lookup in the current directory.
346*7c478bd9Sstevel@tonic-gate 	 */
347*7c478bd9Sstevel@tonic-gate 	error = VOP_LOOKUP(vp, component, &tvp, pnp, lookup_flags,
348*7c478bd9Sstevel@tonic-gate 		rootvp, cr);
349*7c478bd9Sstevel@tonic-gate 	cvp = tvp;
350*7c478bd9Sstevel@tonic-gate 	if (error) {
351*7c478bd9Sstevel@tonic-gate 		cvp = NULL;
352*7c478bd9Sstevel@tonic-gate 		/*
353*7c478bd9Sstevel@tonic-gate 		 * On error, return hard error if
354*7c478bd9Sstevel@tonic-gate 		 * (a) we're not at the end of the pathname yet, or
355*7c478bd9Sstevel@tonic-gate 		 * (b) the caller didn't want the parent directory, or
356*7c478bd9Sstevel@tonic-gate 		 * (c) we failed for some reason other than a missing entry.
357*7c478bd9Sstevel@tonic-gate 		 */
358*7c478bd9Sstevel@tonic-gate 		if (pn_pathleft(pnp) || dirvpp == NULL || error != ENOENT)
359*7c478bd9Sstevel@tonic-gate 			goto bad;
360*7c478bd9Sstevel@tonic-gate #ifdef C2_AUDIT
361*7c478bd9Sstevel@tonic-gate 		if (audit_active) {	/* directory access */
362*7c478bd9Sstevel@tonic-gate 			if (error = audit_savepath(pnp, vp, error, cr))
363*7c478bd9Sstevel@tonic-gate 				goto bad_noaudit;
364*7c478bd9Sstevel@tonic-gate 		}
365*7c478bd9Sstevel@tonic-gate #endif
366*7c478bd9Sstevel@tonic-gate 		pn_setlast(pnp);
367*7c478bd9Sstevel@tonic-gate 		/*
368*7c478bd9Sstevel@tonic-gate 		 * We inform the caller that the desired entry must be
369*7c478bd9Sstevel@tonic-gate 		 * a directory by adding a '/' to the component name.
370*7c478bd9Sstevel@tonic-gate 		 */
371*7c478bd9Sstevel@tonic-gate 		if (must_be_directory && (error = pn_addslash(pnp)) != 0)
372*7c478bd9Sstevel@tonic-gate 			goto bad;
373*7c478bd9Sstevel@tonic-gate 		*dirvpp = vp;
374*7c478bd9Sstevel@tonic-gate 		/*
375*7c478bd9Sstevel@tonic-gate 		 * We cache the path of everything up to right before this
376*7c478bd9Sstevel@tonic-gate 		 * component and store that in the parent directory.
377*7c478bd9Sstevel@tonic-gate 		 */
378*7c478bd9Sstevel@tonic-gate 		if (vfs_vnode_path && pnp->pn_path != pnp->pn_buf) {
379*7c478bd9Sstevel@tonic-gate 			VN_SETPATH(rootvp, startvp, vp, pnp->pn_buf,
380*7c478bd9Sstevel@tonic-gate 			    pnp->pn_path - pnp->pn_buf);
381*7c478bd9Sstevel@tonic-gate 		}
382*7c478bd9Sstevel@tonic-gate 		if (compvpp != NULL)
383*7c478bd9Sstevel@tonic-gate 			*compvpp = NULL;
384*7c478bd9Sstevel@tonic-gate 		if (rootvp != rootdir)
385*7c478bd9Sstevel@tonic-gate 			VN_RELE(rootvp);
386*7c478bd9Sstevel@tonic-gate 		return (0);
387*7c478bd9Sstevel@tonic-gate 	}
388*7c478bd9Sstevel@tonic-gate 
389*7c478bd9Sstevel@tonic-gate 	/*
390*7c478bd9Sstevel@tonic-gate 	 * Traverse mount points.
391*7c478bd9Sstevel@tonic-gate 	 * XXX why don't we need to hold a read lock here (call vn_vfsrlock)?
392*7c478bd9Sstevel@tonic-gate 	 * What prevents a concurrent update to v_vfsmountedhere?
393*7c478bd9Sstevel@tonic-gate 	 * 	Possible answer: if mounting, we might not see the mount
394*7c478bd9Sstevel@tonic-gate 	 *	if it is concurrently coming into existence, but that's
395*7c478bd9Sstevel@tonic-gate 	 *	really not much different from the thread running a bit slower.
396*7c478bd9Sstevel@tonic-gate 	 *	If unmounting, we may get into traverse() when we shouldn't,
397*7c478bd9Sstevel@tonic-gate 	 *	but traverse() will catch this case for us.
398*7c478bd9Sstevel@tonic-gate 	 *	(For this to work, fetching v_vfsmountedhere had better
399*7c478bd9Sstevel@tonic-gate 	 *	be atomic!)
400*7c478bd9Sstevel@tonic-gate 	 */
401*7c478bd9Sstevel@tonic-gate 	if (vn_mountedvfs(cvp) != NULL) {
402*7c478bd9Sstevel@tonic-gate 		tvp = cvp;
403*7c478bd9Sstevel@tonic-gate 		if ((error = traverse(&tvp)) != 0) {
404*7c478bd9Sstevel@tonic-gate 			/*
405*7c478bd9Sstevel@tonic-gate 			 * It is required to assign cvp here, because
406*7c478bd9Sstevel@tonic-gate 			 * traverse() will return a held vnode which
407*7c478bd9Sstevel@tonic-gate 			 * may different than the vnode that was passed
408*7c478bd9Sstevel@tonic-gate 			 * in (even in the error case).  If traverse()
409*7c478bd9Sstevel@tonic-gate 			 * changes the vnode it releases the original,
410*7c478bd9Sstevel@tonic-gate 			 * and holds the new one.
411*7c478bd9Sstevel@tonic-gate 			 */
412*7c478bd9Sstevel@tonic-gate 			cvp = tvp;
413*7c478bd9Sstevel@tonic-gate 			goto bad;
414*7c478bd9Sstevel@tonic-gate 		}
415*7c478bd9Sstevel@tonic-gate 		cvp = tvp;
416*7c478bd9Sstevel@tonic-gate 	}
417*7c478bd9Sstevel@tonic-gate 
418*7c478bd9Sstevel@tonic-gate 	/*
419*7c478bd9Sstevel@tonic-gate 	 * If we hit a symbolic link and there is more path to be
420*7c478bd9Sstevel@tonic-gate 	 * translated or this operation does not wish to apply
421*7c478bd9Sstevel@tonic-gate 	 * to a link, then place the contents of the link at the
422*7c478bd9Sstevel@tonic-gate 	 * front of the remaining pathname.
423*7c478bd9Sstevel@tonic-gate 	 */
424*7c478bd9Sstevel@tonic-gate 	if (cvp->v_type == VLNK && ((flags & FOLLOW) || pn_pathleft(pnp))) {
425*7c478bd9Sstevel@tonic-gate 		struct pathname linkpath;
426*7c478bd9Sstevel@tonic-gate #ifdef C2_AUDIT
427*7c478bd9Sstevel@tonic-gate 		if (audit_active) {
428*7c478bd9Sstevel@tonic-gate 			if (error = audit_pathcomp(pnp, cvp, cr))
429*7c478bd9Sstevel@tonic-gate 				goto bad;
430*7c478bd9Sstevel@tonic-gate 		}
431*7c478bd9Sstevel@tonic-gate #endif
432*7c478bd9Sstevel@tonic-gate 
433*7c478bd9Sstevel@tonic-gate 		if (++nlink > MAXSYMLINKS) {
434*7c478bd9Sstevel@tonic-gate 			error = ELOOP;
435*7c478bd9Sstevel@tonic-gate 			goto bad;
436*7c478bd9Sstevel@tonic-gate 		}
437*7c478bd9Sstevel@tonic-gate 		pn_alloc(&linkpath);
438*7c478bd9Sstevel@tonic-gate 		if (error = pn_getsymlink(cvp, &linkpath, cr)) {
439*7c478bd9Sstevel@tonic-gate 			pn_free(&linkpath);
440*7c478bd9Sstevel@tonic-gate 			goto bad;
441*7c478bd9Sstevel@tonic-gate 		}
442*7c478bd9Sstevel@tonic-gate 
443*7c478bd9Sstevel@tonic-gate #ifdef C2_AUDIT
444*7c478bd9Sstevel@tonic-gate 		if (audit_active)
445*7c478bd9Sstevel@tonic-gate 			audit_symlink(pnp, &linkpath);
446*7c478bd9Sstevel@tonic-gate #endif /* C2_AUDIT */
447*7c478bd9Sstevel@tonic-gate 
448*7c478bd9Sstevel@tonic-gate 		if (pn_pathleft(&linkpath) == 0)
449*7c478bd9Sstevel@tonic-gate 			(void) pn_set(&linkpath, ".");
450*7c478bd9Sstevel@tonic-gate 		error = pn_insert(pnp, &linkpath, strlen(component));
451*7c478bd9Sstevel@tonic-gate 		pn_free(&linkpath);
452*7c478bd9Sstevel@tonic-gate 		if (error)
453*7c478bd9Sstevel@tonic-gate 			goto bad;
454*7c478bd9Sstevel@tonic-gate 		VN_RELE(cvp);
455*7c478bd9Sstevel@tonic-gate 		cvp = NULL;
456*7c478bd9Sstevel@tonic-gate 		if (pnp->pn_pathlen == 0) {
457*7c478bd9Sstevel@tonic-gate 			error = ENOENT;
458*7c478bd9Sstevel@tonic-gate 			goto bad;
459*7c478bd9Sstevel@tonic-gate 		}
460*7c478bd9Sstevel@tonic-gate 		if (pnp->pn_path[0] == '/') {
461*7c478bd9Sstevel@tonic-gate 			do {
462*7c478bd9Sstevel@tonic-gate 				pnp->pn_path++;
463*7c478bd9Sstevel@tonic-gate 				pnp->pn_pathlen--;
464*7c478bd9Sstevel@tonic-gate 			} while (pnp->pn_path[0] == '/');
465*7c478bd9Sstevel@tonic-gate 			VN_RELE(vp);
466*7c478bd9Sstevel@tonic-gate 			vp = rootvp;
467*7c478bd9Sstevel@tonic-gate 			VN_HOLD(vp);
468*7c478bd9Sstevel@tonic-gate 		}
469*7c478bd9Sstevel@tonic-gate #ifdef C2_AUDIT
470*7c478bd9Sstevel@tonic-gate 		if (audit_active)
471*7c478bd9Sstevel@tonic-gate 			audit_anchorpath(pnp, vp == rootvp);
472*7c478bd9Sstevel@tonic-gate #endif
473*7c478bd9Sstevel@tonic-gate 		if (pn_fixslash(pnp)) {
474*7c478bd9Sstevel@tonic-gate 			flags |= FOLLOW;
475*7c478bd9Sstevel@tonic-gate 			must_be_directory = 1;
476*7c478bd9Sstevel@tonic-gate 		}
477*7c478bd9Sstevel@tonic-gate 		goto next;
478*7c478bd9Sstevel@tonic-gate 	}
479*7c478bd9Sstevel@tonic-gate 
480*7c478bd9Sstevel@tonic-gate 	/*
481*7c478bd9Sstevel@tonic-gate 	 * If rpnp is non-NULL, remember the resolved path name therein.
482*7c478bd9Sstevel@tonic-gate 	 * Do not include "." components.  Collapse occurrences of
483*7c478bd9Sstevel@tonic-gate 	 * "previous/..", so long as "previous" is not itself "..".
484*7c478bd9Sstevel@tonic-gate 	 * Exhausting rpnp results in error ENAMETOOLONG.
485*7c478bd9Sstevel@tonic-gate 	 */
486*7c478bd9Sstevel@tonic-gate 	if (rpnp && strcmp(component, ".") != 0) {
487*7c478bd9Sstevel@tonic-gate 		size_t len;
488*7c478bd9Sstevel@tonic-gate 
489*7c478bd9Sstevel@tonic-gate 		if (strcmp(component, "..") == 0 &&
490*7c478bd9Sstevel@tonic-gate 		    rpnp->pn_pathlen != 0 &&
491*7c478bd9Sstevel@tonic-gate 		    !((rpnp->pn_pathlen > 2 &&
492*7c478bd9Sstevel@tonic-gate 		    strncmp(rpnp->pn_path+rpnp->pn_pathlen-3, "/..", 3) == 0) ||
493*7c478bd9Sstevel@tonic-gate 		    (rpnp->pn_pathlen == 2 &&
494*7c478bd9Sstevel@tonic-gate 		    strncmp(rpnp->pn_path, "..", 2) == 0))) {
495*7c478bd9Sstevel@tonic-gate 			while (rpnp->pn_pathlen &&
496*7c478bd9Sstevel@tonic-gate 			    rpnp->pn_path[rpnp->pn_pathlen-1] != '/')
497*7c478bd9Sstevel@tonic-gate 				rpnp->pn_pathlen--;
498*7c478bd9Sstevel@tonic-gate 			if (rpnp->pn_pathlen > 1)
499*7c478bd9Sstevel@tonic-gate 				rpnp->pn_pathlen--;
500*7c478bd9Sstevel@tonic-gate 			rpnp->pn_path[rpnp->pn_pathlen] = '\0';
501*7c478bd9Sstevel@tonic-gate 		} else {
502*7c478bd9Sstevel@tonic-gate 			if (rpnp->pn_pathlen != 0 &&
503*7c478bd9Sstevel@tonic-gate 			    rpnp->pn_path[rpnp->pn_pathlen-1] != '/')
504*7c478bd9Sstevel@tonic-gate 				rpnp->pn_path[rpnp->pn_pathlen++] = '/';
505*7c478bd9Sstevel@tonic-gate 			error = copystr(component,
506*7c478bd9Sstevel@tonic-gate 			    rpnp->pn_path + rpnp->pn_pathlen,
507*7c478bd9Sstevel@tonic-gate 			    rpnp->pn_bufsize - rpnp->pn_pathlen, &len);
508*7c478bd9Sstevel@tonic-gate 			if (error)	/* copystr() returns ENAMETOOLONG */
509*7c478bd9Sstevel@tonic-gate 				goto bad;
510*7c478bd9Sstevel@tonic-gate 			rpnp->pn_pathlen += (len - 1);
511*7c478bd9Sstevel@tonic-gate 			ASSERT(rpnp->pn_bufsize > rpnp->pn_pathlen);
512*7c478bd9Sstevel@tonic-gate 		}
513*7c478bd9Sstevel@tonic-gate 	}
514*7c478bd9Sstevel@tonic-gate 
515*7c478bd9Sstevel@tonic-gate 	/*
516*7c478bd9Sstevel@tonic-gate 	 * If no more components, return last directory (if wanted) and
517*7c478bd9Sstevel@tonic-gate 	 * last component (if wanted).
518*7c478bd9Sstevel@tonic-gate 	 */
519*7c478bd9Sstevel@tonic-gate 	if (pn_pathleft(pnp) == 0) {
520*7c478bd9Sstevel@tonic-gate 		/*
521*7c478bd9Sstevel@tonic-gate 		 * If there was a trailing slash in the pathname,
522*7c478bd9Sstevel@tonic-gate 		 * make sure the last component is a directory.
523*7c478bd9Sstevel@tonic-gate 		 */
524*7c478bd9Sstevel@tonic-gate 		if (must_be_directory && cvp->v_type != VDIR) {
525*7c478bd9Sstevel@tonic-gate 			error = ENOTDIR;
526*7c478bd9Sstevel@tonic-gate 			goto bad;
527*7c478bd9Sstevel@tonic-gate 		}
528*7c478bd9Sstevel@tonic-gate 		if (dirvpp != NULL) {
529*7c478bd9Sstevel@tonic-gate 			/*
530*7c478bd9Sstevel@tonic-gate 			 * Check that we have the real parent and not
531*7c478bd9Sstevel@tonic-gate 			 * an alias of the last component.
532*7c478bd9Sstevel@tonic-gate 			 */
533*7c478bd9Sstevel@tonic-gate 			if (vn_compare(vp, cvp)) {
534*7c478bd9Sstevel@tonic-gate #ifdef C2_AUDIT
535*7c478bd9Sstevel@tonic-gate 				if (audit_active)
536*7c478bd9Sstevel@tonic-gate 					(void) audit_savepath(pnp, cvp,
537*7c478bd9Sstevel@tonic-gate 						EINVAL, cr);
538*7c478bd9Sstevel@tonic-gate #endif
539*7c478bd9Sstevel@tonic-gate 				pn_setlast(pnp);
540*7c478bd9Sstevel@tonic-gate 				VN_RELE(vp);
541*7c478bd9Sstevel@tonic-gate 				VN_RELE(cvp);
542*7c478bd9Sstevel@tonic-gate 				if (rootvp != rootdir)
543*7c478bd9Sstevel@tonic-gate 					VN_RELE(rootvp);
544*7c478bd9Sstevel@tonic-gate 				return (EINVAL);
545*7c478bd9Sstevel@tonic-gate 			}
546*7c478bd9Sstevel@tonic-gate #ifdef C2_AUDIT
547*7c478bd9Sstevel@tonic-gate 			if (audit_active) {
548*7c478bd9Sstevel@tonic-gate 				if (error = audit_pathcomp(pnp, vp, cr))
549*7c478bd9Sstevel@tonic-gate 					goto bad;
550*7c478bd9Sstevel@tonic-gate 			}
551*7c478bd9Sstevel@tonic-gate #endif
552*7c478bd9Sstevel@tonic-gate 			*dirvpp = vp;
553*7c478bd9Sstevel@tonic-gate 		} else
554*7c478bd9Sstevel@tonic-gate 			VN_RELE(vp);
555*7c478bd9Sstevel@tonic-gate #ifdef C2_AUDIT
556*7c478bd9Sstevel@tonic-gate 		if (audit_active)
557*7c478bd9Sstevel@tonic-gate 			(void) audit_savepath(pnp, cvp, 0, cr);
558*7c478bd9Sstevel@tonic-gate #endif
559*7c478bd9Sstevel@tonic-gate 		if (pnp->pn_path == pnp->pn_buf)
560*7c478bd9Sstevel@tonic-gate 			(void) pn_set(pnp, ".");
561*7c478bd9Sstevel@tonic-gate 		else
562*7c478bd9Sstevel@tonic-gate 			pn_setlast(pnp);
563*7c478bd9Sstevel@tonic-gate 		if (rpnp) {
564*7c478bd9Sstevel@tonic-gate 			if (VN_CMP(cvp, rootvp))
565*7c478bd9Sstevel@tonic-gate 				(void) pn_set(rpnp, "/");
566*7c478bd9Sstevel@tonic-gate 			else if (rpnp->pn_pathlen == 0)
567*7c478bd9Sstevel@tonic-gate 				(void) pn_set(rpnp, ".");
568*7c478bd9Sstevel@tonic-gate 		}
569*7c478bd9Sstevel@tonic-gate 
570*7c478bd9Sstevel@tonic-gate 		/*
571*7c478bd9Sstevel@tonic-gate 		 * Store the path for this vnode and/or its parent.
572*7c478bd9Sstevel@tonic-gate 		 */
573*7c478bd9Sstevel@tonic-gate 		if (vfs_vnode_path) {
574*7c478bd9Sstevel@tonic-gate 			plen = pnp->pn_path - pnp->pn_buf;
575*7c478bd9Sstevel@tonic-gate 			if (dirvpp != NULL && plen != 0)
576*7c478bd9Sstevel@tonic-gate 				VN_SETPATH(rootvp, startvp, *dirvpp,
577*7c478bd9Sstevel@tonic-gate 				    pnp->pn_buf, plen);
578*7c478bd9Sstevel@tonic-gate 			VN_SETPATH(rootvp, startvp, cvp, pnp->pn_buf,
579*7c478bd9Sstevel@tonic-gate 			    plen + pnp->pn_pathlen);
580*7c478bd9Sstevel@tonic-gate 		}
581*7c478bd9Sstevel@tonic-gate 
582*7c478bd9Sstevel@tonic-gate 		if (compvpp != NULL)
583*7c478bd9Sstevel@tonic-gate 			*compvpp = cvp;
584*7c478bd9Sstevel@tonic-gate 		else
585*7c478bd9Sstevel@tonic-gate 			VN_RELE(cvp);
586*7c478bd9Sstevel@tonic-gate 		if (rootvp != rootdir)
587*7c478bd9Sstevel@tonic-gate 			VN_RELE(rootvp);
588*7c478bd9Sstevel@tonic-gate 		return (0);
589*7c478bd9Sstevel@tonic-gate 	}
590*7c478bd9Sstevel@tonic-gate 
591*7c478bd9Sstevel@tonic-gate #ifdef C2_AUDIT
592*7c478bd9Sstevel@tonic-gate 	if (audit_active) {
593*7c478bd9Sstevel@tonic-gate 		if (error = audit_pathcomp(pnp, cvp, cr))
594*7c478bd9Sstevel@tonic-gate 			goto bad;
595*7c478bd9Sstevel@tonic-gate 	}
596*7c478bd9Sstevel@tonic-gate #endif
597*7c478bd9Sstevel@tonic-gate 
598*7c478bd9Sstevel@tonic-gate 	/*
599*7c478bd9Sstevel@tonic-gate 	 * Skip over slashes from end of last component.
600*7c478bd9Sstevel@tonic-gate 	 */
601*7c478bd9Sstevel@tonic-gate 	while (pnp->pn_path[0] == '/') {
602*7c478bd9Sstevel@tonic-gate 		pnp->pn_path++;
603*7c478bd9Sstevel@tonic-gate 		pnp->pn_pathlen--;
604*7c478bd9Sstevel@tonic-gate 	}
605*7c478bd9Sstevel@tonic-gate 
606*7c478bd9Sstevel@tonic-gate 	/*
607*7c478bd9Sstevel@tonic-gate 	 * Searched through another level of directory:
608*7c478bd9Sstevel@tonic-gate 	 * release previous directory handle and save new (result
609*7c478bd9Sstevel@tonic-gate 	 * of lookup) as current directory.
610*7c478bd9Sstevel@tonic-gate 	 */
611*7c478bd9Sstevel@tonic-gate 	VN_RELE(vp);
612*7c478bd9Sstevel@tonic-gate 	vp = cvp;
613*7c478bd9Sstevel@tonic-gate 	cvp = NULL;
614*7c478bd9Sstevel@tonic-gate 	goto next;
615*7c478bd9Sstevel@tonic-gate 
616*7c478bd9Sstevel@tonic-gate bad:
617*7c478bd9Sstevel@tonic-gate #ifdef C2_AUDIT
618*7c478bd9Sstevel@tonic-gate 	if (audit_active)	/* reached end of path */
619*7c478bd9Sstevel@tonic-gate 		(void) audit_savepath(pnp, cvp, error, cr);
620*7c478bd9Sstevel@tonic-gate bad_noaudit:
621*7c478bd9Sstevel@tonic-gate #endif
622*7c478bd9Sstevel@tonic-gate 	/*
623*7c478bd9Sstevel@tonic-gate 	 * Error.  Release vnodes and return.
624*7c478bd9Sstevel@tonic-gate 	 */
625*7c478bd9Sstevel@tonic-gate 	if (cvp)
626*7c478bd9Sstevel@tonic-gate 		VN_RELE(cvp);
627*7c478bd9Sstevel@tonic-gate 	/*
628*7c478bd9Sstevel@tonic-gate 	 * If the error was ESTALE and the current directory to look in
629*7c478bd9Sstevel@tonic-gate 	 * was the root for this lookup, the root for a mounted file
630*7c478bd9Sstevel@tonic-gate 	 * system, or the starting directory for lookups, then
631*7c478bd9Sstevel@tonic-gate 	 * return ENOENT instead of ESTALE.  In this case, no recovery
632*7c478bd9Sstevel@tonic-gate 	 * is possible by the higher level.  If ESTALE was returned for
633*7c478bd9Sstevel@tonic-gate 	 * some intermediate directory along the path, then recovery
634*7c478bd9Sstevel@tonic-gate 	 * is potentially possible and retrying from the higher level
635*7c478bd9Sstevel@tonic-gate 	 * will either correct the situation by purging stale cache
636*7c478bd9Sstevel@tonic-gate 	 * entries or eventually get back to the point where no recovery
637*7c478bd9Sstevel@tonic-gate 	 * is possible.
638*7c478bd9Sstevel@tonic-gate 	 */
639*7c478bd9Sstevel@tonic-gate 	if (error == ESTALE &&
640*7c478bd9Sstevel@tonic-gate 	    (VN_CMP(vp, rootvp) || (vp->v_flag & VROOT) || vp == startvp))
641*7c478bd9Sstevel@tonic-gate 		error = ENOENT;
642*7c478bd9Sstevel@tonic-gate 	VN_RELE(vp);
643*7c478bd9Sstevel@tonic-gate 	if (rootvp != rootdir)
644*7c478bd9Sstevel@tonic-gate 		VN_RELE(rootvp);
645*7c478bd9Sstevel@tonic-gate 	return (error);
646*7c478bd9Sstevel@tonic-gate }
647*7c478bd9Sstevel@tonic-gate 
648*7c478bd9Sstevel@tonic-gate /*
649*7c478bd9Sstevel@tonic-gate  * Traverse a mount point.  Routine accepts a vnode pointer as a reference
650*7c478bd9Sstevel@tonic-gate  * parameter and performs the indirection, releasing the original vnode.
651*7c478bd9Sstevel@tonic-gate  */
652*7c478bd9Sstevel@tonic-gate int
653*7c478bd9Sstevel@tonic-gate traverse(vnode_t **cvpp)
654*7c478bd9Sstevel@tonic-gate {
655*7c478bd9Sstevel@tonic-gate 	int error = 0;
656*7c478bd9Sstevel@tonic-gate 	vnode_t *cvp;
657*7c478bd9Sstevel@tonic-gate 	vnode_t *tvp;
658*7c478bd9Sstevel@tonic-gate 	vfs_t *vfsp;
659*7c478bd9Sstevel@tonic-gate 
660*7c478bd9Sstevel@tonic-gate 	cvp = *cvpp;
661*7c478bd9Sstevel@tonic-gate 
662*7c478bd9Sstevel@tonic-gate 	/*
663*7c478bd9Sstevel@tonic-gate 	 * If this vnode is mounted on, then we transparently indirect
664*7c478bd9Sstevel@tonic-gate 	 * to the vnode which is the root of the mounted file system.
665*7c478bd9Sstevel@tonic-gate 	 * Before we do this we must check that an unmount is not in
666*7c478bd9Sstevel@tonic-gate 	 * progress on this vnode.
667*7c478bd9Sstevel@tonic-gate 	 */
668*7c478bd9Sstevel@tonic-gate 
669*7c478bd9Sstevel@tonic-gate 	for (;;) {
670*7c478bd9Sstevel@tonic-gate 		/*
671*7c478bd9Sstevel@tonic-gate 		 * Try to read lock the vnode.  If this fails because
672*7c478bd9Sstevel@tonic-gate 		 * the vnode is already write locked, then check to
673*7c478bd9Sstevel@tonic-gate 		 * see whether it is the current thread which locked
674*7c478bd9Sstevel@tonic-gate 		 * the vnode.  If it is not, then read lock the vnode
675*7c478bd9Sstevel@tonic-gate 		 * by waiting to acquire the lock.
676*7c478bd9Sstevel@tonic-gate 		 *
677*7c478bd9Sstevel@tonic-gate 		 * The code path in domount() is an example of support
678*7c478bd9Sstevel@tonic-gate 		 * which needs to look up two pathnames and locks one
679*7c478bd9Sstevel@tonic-gate 		 * of them in between the two lookups.
680*7c478bd9Sstevel@tonic-gate 		 */
681*7c478bd9Sstevel@tonic-gate 		error = vn_vfsrlock(cvp);
682*7c478bd9Sstevel@tonic-gate 		if (error) {
683*7c478bd9Sstevel@tonic-gate 			if (!vn_vfswlock_held(cvp))
684*7c478bd9Sstevel@tonic-gate 				error = vn_vfsrlock_wait(cvp);
685*7c478bd9Sstevel@tonic-gate 			if (error != 0) {
686*7c478bd9Sstevel@tonic-gate 				/*
687*7c478bd9Sstevel@tonic-gate 				 * lookuppn() expects a held vnode to be
688*7c478bd9Sstevel@tonic-gate 				 * returned because it promptly calls
689*7c478bd9Sstevel@tonic-gate 				 * VN_RELE after the error return
690*7c478bd9Sstevel@tonic-gate 				 */
691*7c478bd9Sstevel@tonic-gate 				*cvpp = cvp;
692*7c478bd9Sstevel@tonic-gate 				return (error);
693*7c478bd9Sstevel@tonic-gate 			}
694*7c478bd9Sstevel@tonic-gate 		}
695*7c478bd9Sstevel@tonic-gate 
696*7c478bd9Sstevel@tonic-gate 		/*
697*7c478bd9Sstevel@tonic-gate 		 * Reached the end of the mount chain?
698*7c478bd9Sstevel@tonic-gate 		 */
699*7c478bd9Sstevel@tonic-gate 		vfsp = vn_mountedvfs(cvp);
700*7c478bd9Sstevel@tonic-gate 		if (vfsp == NULL) {
701*7c478bd9Sstevel@tonic-gate 			vn_vfsunlock(cvp);
702*7c478bd9Sstevel@tonic-gate 			break;
703*7c478bd9Sstevel@tonic-gate 		}
704*7c478bd9Sstevel@tonic-gate 
705*7c478bd9Sstevel@tonic-gate 		/*
706*7c478bd9Sstevel@tonic-gate 		 * The read lock must be held across the call to VFS_ROOT() to
707*7c478bd9Sstevel@tonic-gate 		 * prevent a concurrent unmount from destroying the vfs.
708*7c478bd9Sstevel@tonic-gate 		 */
709*7c478bd9Sstevel@tonic-gate 		error = VFS_ROOT(vfsp, &tvp);
710*7c478bd9Sstevel@tonic-gate 		vn_vfsunlock(cvp);
711*7c478bd9Sstevel@tonic-gate 
712*7c478bd9Sstevel@tonic-gate 		if (error)
713*7c478bd9Sstevel@tonic-gate 			break;
714*7c478bd9Sstevel@tonic-gate 
715*7c478bd9Sstevel@tonic-gate 		VN_RELE(cvp);
716*7c478bd9Sstevel@tonic-gate 
717*7c478bd9Sstevel@tonic-gate 		cvp = tvp;
718*7c478bd9Sstevel@tonic-gate 	}
719*7c478bd9Sstevel@tonic-gate 
720*7c478bd9Sstevel@tonic-gate 	*cvpp = cvp;
721*7c478bd9Sstevel@tonic-gate 	return (error);
722*7c478bd9Sstevel@tonic-gate }
723*7c478bd9Sstevel@tonic-gate 
724*7c478bd9Sstevel@tonic-gate /*
725*7c478bd9Sstevel@tonic-gate  * Return the lowermost vnode if this is a mountpoint.
726*7c478bd9Sstevel@tonic-gate  */
727*7c478bd9Sstevel@tonic-gate static vnode_t *
728*7c478bd9Sstevel@tonic-gate vn_under(vnode_t *vp)
729*7c478bd9Sstevel@tonic-gate {
730*7c478bd9Sstevel@tonic-gate 	vnode_t *uvp;
731*7c478bd9Sstevel@tonic-gate 	vfs_t *vfsp;
732*7c478bd9Sstevel@tonic-gate 
733*7c478bd9Sstevel@tonic-gate 	while (vp->v_flag & VROOT) {
734*7c478bd9Sstevel@tonic-gate 
735*7c478bd9Sstevel@tonic-gate 		vfsp = vp->v_vfsp;
736*7c478bd9Sstevel@tonic-gate 		vfs_rlock_wait(vfsp);
737*7c478bd9Sstevel@tonic-gate 		if ((uvp = vfsp->vfs_vnodecovered) == NULL ||
738*7c478bd9Sstevel@tonic-gate 		    (vfsp->vfs_flag & VFS_UNMOUNTED)) {
739*7c478bd9Sstevel@tonic-gate 			vfs_unlock(vfsp);
740*7c478bd9Sstevel@tonic-gate 			break;
741*7c478bd9Sstevel@tonic-gate 		}
742*7c478bd9Sstevel@tonic-gate 		VN_HOLD(uvp);
743*7c478bd9Sstevel@tonic-gate 		vfs_unlock(vfsp);
744*7c478bd9Sstevel@tonic-gate 		VN_RELE(vp);
745*7c478bd9Sstevel@tonic-gate 		vp = uvp;
746*7c478bd9Sstevel@tonic-gate 	}
747*7c478bd9Sstevel@tonic-gate 
748*7c478bd9Sstevel@tonic-gate 	return (vp);
749*7c478bd9Sstevel@tonic-gate }
750*7c478bd9Sstevel@tonic-gate 
751*7c478bd9Sstevel@tonic-gate static int
752*7c478bd9Sstevel@tonic-gate vnode_match(vnode_t *v1, vnode_t *v2, cred_t *cr)
753*7c478bd9Sstevel@tonic-gate {
754*7c478bd9Sstevel@tonic-gate 	vattr_t	v1attr, v2attr;
755*7c478bd9Sstevel@tonic-gate 
756*7c478bd9Sstevel@tonic-gate 	/*
757*7c478bd9Sstevel@tonic-gate 	 * If we have a device file, check to see if is a cloned open of the
758*7c478bd9Sstevel@tonic-gate 	 * same device.  For self-cloning devices, the major numbers will match.
759*7c478bd9Sstevel@tonic-gate 	 * For devices cloned through the 'clone' driver, the minor number of
760*7c478bd9Sstevel@tonic-gate 	 * the source device will be the same as the major number of the cloned
761*7c478bd9Sstevel@tonic-gate 	 * device.
762*7c478bd9Sstevel@tonic-gate 	 */
763*7c478bd9Sstevel@tonic-gate 	if ((v1->v_type == VCHR || v1->v_type == VBLK) &&
764*7c478bd9Sstevel@tonic-gate 	    v1->v_type == v2->v_type) {
765*7c478bd9Sstevel@tonic-gate 		if ((spec_is_selfclone(v1) || spec_is_selfclone(v2)) &&
766*7c478bd9Sstevel@tonic-gate 		    getmajor(v1->v_rdev) == getmajor(v2->v_rdev))
767*7c478bd9Sstevel@tonic-gate 			return (1);
768*7c478bd9Sstevel@tonic-gate 
769*7c478bd9Sstevel@tonic-gate 		if (spec_is_clone(v1) &&
770*7c478bd9Sstevel@tonic-gate 		    getmajor(v1->v_rdev) == getminor(v2->v_rdev))
771*7c478bd9Sstevel@tonic-gate 			return (1);
772*7c478bd9Sstevel@tonic-gate 
773*7c478bd9Sstevel@tonic-gate 		if (spec_is_clone(v2) &&
774*7c478bd9Sstevel@tonic-gate 		    getmajor(v2->v_rdev) == getminor(v1->v_rdev))
775*7c478bd9Sstevel@tonic-gate 			return (1);
776*7c478bd9Sstevel@tonic-gate 	}
777*7c478bd9Sstevel@tonic-gate 
778*7c478bd9Sstevel@tonic-gate 	v1attr.va_mask = v2attr.va_mask = AT_TYPE;
779*7c478bd9Sstevel@tonic-gate 
780*7c478bd9Sstevel@tonic-gate 	/*
781*7c478bd9Sstevel@tonic-gate 	 * This check for symbolic links handles the pseudo-symlinks in procfs.
782*7c478bd9Sstevel@tonic-gate 	 * These particular links have v_type of VDIR, but the attributes have a
783*7c478bd9Sstevel@tonic-gate 	 * type of VLNK.  We need to avoid these links because otherwise if we
784*7c478bd9Sstevel@tonic-gate 	 * are currently in '/proc/self/fd', then '/proc/self/cwd' will compare
785*7c478bd9Sstevel@tonic-gate 	 * as the same vnode.
786*7c478bd9Sstevel@tonic-gate 	 */
787*7c478bd9Sstevel@tonic-gate 	if (VOP_GETATTR(v1, &v1attr, 0, cr) != 0 ||
788*7c478bd9Sstevel@tonic-gate 	    VOP_GETATTR(v2, &v2attr, 0, cr) != 0 ||
789*7c478bd9Sstevel@tonic-gate 	    v1attr.va_type == VLNK || v2attr.va_type == VLNK)
790*7c478bd9Sstevel@tonic-gate 		return (0);
791*7c478bd9Sstevel@tonic-gate 
792*7c478bd9Sstevel@tonic-gate 	v1attr.va_mask = v2attr.va_mask = AT_TYPE | AT_FSID | AT_NODEID;
793*7c478bd9Sstevel@tonic-gate 
794*7c478bd9Sstevel@tonic-gate 	if (VOP_GETATTR(v1, &v1attr, ATTR_REAL, cr) != 0 ||
795*7c478bd9Sstevel@tonic-gate 	    VOP_GETATTR(v2, &v2attr, ATTR_REAL, cr) != 0)
796*7c478bd9Sstevel@tonic-gate 		return (0);
797*7c478bd9Sstevel@tonic-gate 
798*7c478bd9Sstevel@tonic-gate 	return (v1attr.va_fsid == v2attr.va_fsid &&
799*7c478bd9Sstevel@tonic-gate 	    v1attr.va_nodeid == v2attr.va_nodeid);
800*7c478bd9Sstevel@tonic-gate }
801*7c478bd9Sstevel@tonic-gate 
802*7c478bd9Sstevel@tonic-gate 
803*7c478bd9Sstevel@tonic-gate /*
804*7c478bd9Sstevel@tonic-gate  * Find the entry in the directory corresponding to the target vnode.
805*7c478bd9Sstevel@tonic-gate  */
806*7c478bd9Sstevel@tonic-gate int
807*7c478bd9Sstevel@tonic-gate dirfindvp(vnode_t *vrootp, vnode_t *dvp, vnode_t *tvp, cred_t *cr, char *dbuf,
808*7c478bd9Sstevel@tonic-gate     size_t dlen, dirent64_t **rdp)
809*7c478bd9Sstevel@tonic-gate {
810*7c478bd9Sstevel@tonic-gate 	size_t dbuflen;
811*7c478bd9Sstevel@tonic-gate 	struct iovec iov;
812*7c478bd9Sstevel@tonic-gate 	struct uio uio;
813*7c478bd9Sstevel@tonic-gate 	int err;
814*7c478bd9Sstevel@tonic-gate 	int eof;
815*7c478bd9Sstevel@tonic-gate 	vnode_t *cmpvp;
816*7c478bd9Sstevel@tonic-gate 	struct dirent64 *dp;
817*7c478bd9Sstevel@tonic-gate 	pathname_t pnp;
818*7c478bd9Sstevel@tonic-gate 
819*7c478bd9Sstevel@tonic-gate 	ASSERT(dvp->v_type == VDIR);
820*7c478bd9Sstevel@tonic-gate 
821*7c478bd9Sstevel@tonic-gate 	/*
822*7c478bd9Sstevel@tonic-gate 	 * This is necessary because of the strange semantics of VOP_LOOKUP().
823*7c478bd9Sstevel@tonic-gate 	 */
824*7c478bd9Sstevel@tonic-gate 	bzero(&pnp, sizeof (pnp));
825*7c478bd9Sstevel@tonic-gate 
826*7c478bd9Sstevel@tonic-gate 	eof = 0;
827*7c478bd9Sstevel@tonic-gate 
828*7c478bd9Sstevel@tonic-gate 	uio.uio_iov = &iov;
829*7c478bd9Sstevel@tonic-gate 	uio.uio_iovcnt = 1;
830*7c478bd9Sstevel@tonic-gate 	uio.uio_segflg = UIO_SYSSPACE;
831*7c478bd9Sstevel@tonic-gate 	uio.uio_fmode = 0;
832*7c478bd9Sstevel@tonic-gate 	uio.uio_extflg = UIO_COPY_CACHED;
833*7c478bd9Sstevel@tonic-gate 	uio.uio_loffset = 0;
834*7c478bd9Sstevel@tonic-gate 
835*7c478bd9Sstevel@tonic-gate 	if ((err = VOP_ACCESS(dvp, VREAD, 0, cr)) != 0)
836*7c478bd9Sstevel@tonic-gate 		return (err);
837*7c478bd9Sstevel@tonic-gate 
838*7c478bd9Sstevel@tonic-gate 	while (!eof) {
839*7c478bd9Sstevel@tonic-gate 		uio.uio_resid = dlen;
840*7c478bd9Sstevel@tonic-gate 		iov.iov_base = dbuf;
841*7c478bd9Sstevel@tonic-gate 		iov.iov_len = dlen;
842*7c478bd9Sstevel@tonic-gate 
843*7c478bd9Sstevel@tonic-gate 		(void) VOP_RWLOCK(dvp, V_WRITELOCK_FALSE, NULL);
844*7c478bd9Sstevel@tonic-gate 		err = VOP_READDIR(dvp, &uio, cr, &eof);
845*7c478bd9Sstevel@tonic-gate 		VOP_RWUNLOCK(dvp, V_WRITELOCK_FALSE, NULL);
846*7c478bd9Sstevel@tonic-gate 
847*7c478bd9Sstevel@tonic-gate 		dbuflen = dlen - uio.uio_resid;
848*7c478bd9Sstevel@tonic-gate 
849*7c478bd9Sstevel@tonic-gate 		if (err || dbuflen == 0)
850*7c478bd9Sstevel@tonic-gate 			break;
851*7c478bd9Sstevel@tonic-gate 
852*7c478bd9Sstevel@tonic-gate 		dp = (dirent64_t *)dbuf;
853*7c478bd9Sstevel@tonic-gate 		while ((intptr_t)dp < (intptr_t)dbuf + dbuflen) {
854*7c478bd9Sstevel@tonic-gate 			/*
855*7c478bd9Sstevel@tonic-gate 			 * Ignore '.' and '..' entries
856*7c478bd9Sstevel@tonic-gate 			 */
857*7c478bd9Sstevel@tonic-gate 			if (strcmp(dp->d_name, ".") == 0 ||
858*7c478bd9Sstevel@tonic-gate 			    strcmp(dp->d_name, "..") == 0) {
859*7c478bd9Sstevel@tonic-gate 				dp = (dirent64_t *)((intptr_t)dp +
860*7c478bd9Sstevel@tonic-gate 				    dp->d_reclen);
861*7c478bd9Sstevel@tonic-gate 				continue;
862*7c478bd9Sstevel@tonic-gate 			}
863*7c478bd9Sstevel@tonic-gate 
864*7c478bd9Sstevel@tonic-gate 			err = VOP_LOOKUP(dvp, dp->d_name, &cmpvp, &pnp, 0,
865*7c478bd9Sstevel@tonic-gate 			    vrootp, cr);
866*7c478bd9Sstevel@tonic-gate 
867*7c478bd9Sstevel@tonic-gate 			/*
868*7c478bd9Sstevel@tonic-gate 			 * We only want to bail out if there was an error other
869*7c478bd9Sstevel@tonic-gate 			 * than ENOENT.  Otherwise, it could be that someone
870*7c478bd9Sstevel@tonic-gate 			 * just removed an entry since the readdir() call, and
871*7c478bd9Sstevel@tonic-gate 			 * the entry we want is further on in the directory.
872*7c478bd9Sstevel@tonic-gate 			 */
873*7c478bd9Sstevel@tonic-gate 			if (err == 0) {
874*7c478bd9Sstevel@tonic-gate 				if (vnode_match(tvp, cmpvp, cr)) {
875*7c478bd9Sstevel@tonic-gate 					VN_RELE(cmpvp);
876*7c478bd9Sstevel@tonic-gate 					*rdp = dp;
877*7c478bd9Sstevel@tonic-gate 					return (0);
878*7c478bd9Sstevel@tonic-gate 				}
879*7c478bd9Sstevel@tonic-gate 
880*7c478bd9Sstevel@tonic-gate 				VN_RELE(cmpvp);
881*7c478bd9Sstevel@tonic-gate 			} else if (err != ENOENT) {
882*7c478bd9Sstevel@tonic-gate 				return (err);
883*7c478bd9Sstevel@tonic-gate 			}
884*7c478bd9Sstevel@tonic-gate 
885*7c478bd9Sstevel@tonic-gate 			dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen);
886*7c478bd9Sstevel@tonic-gate 		}
887*7c478bd9Sstevel@tonic-gate 	}
888*7c478bd9Sstevel@tonic-gate 
889*7c478bd9Sstevel@tonic-gate 	/*
890*7c478bd9Sstevel@tonic-gate 	 * Something strange has happened, this directory does not contain the
891*7c478bd9Sstevel@tonic-gate 	 * specified vnode.  This should never happen in the normal case, since
892*7c478bd9Sstevel@tonic-gate 	 * we ensured that dvp is the parent of vp.  This may be possible in
893*7c478bd9Sstevel@tonic-gate 	 * some race conditions, so fail gracefully.
894*7c478bd9Sstevel@tonic-gate 	 */
895*7c478bd9Sstevel@tonic-gate 	if (err == 0)
896*7c478bd9Sstevel@tonic-gate 		err = ENOENT;
897*7c478bd9Sstevel@tonic-gate 
898*7c478bd9Sstevel@tonic-gate 	return (err);
899*7c478bd9Sstevel@tonic-gate }
900*7c478bd9Sstevel@tonic-gate 
901*7c478bd9Sstevel@tonic-gate /*
902*7c478bd9Sstevel@tonic-gate  * Given a global path (from rootdir), and a vnode that is the current root,
903*7c478bd9Sstevel@tonic-gate  * return the portion of the path that is beneath the current root or NULL on
904*7c478bd9Sstevel@tonic-gate  * failure.  The path MUST be a resolved path (no '..' entries or symlinks),
905*7c478bd9Sstevel@tonic-gate  * otherwise this function will fail.
906*7c478bd9Sstevel@tonic-gate  */
907*7c478bd9Sstevel@tonic-gate static char *
908*7c478bd9Sstevel@tonic-gate localpath(char *path, struct vnode *vrootp, cred_t *cr)
909*7c478bd9Sstevel@tonic-gate {
910*7c478bd9Sstevel@tonic-gate 	vnode_t *vp;
911*7c478bd9Sstevel@tonic-gate 	vnode_t *cvp;
912*7c478bd9Sstevel@tonic-gate 	char component[MAXNAMELEN];
913*7c478bd9Sstevel@tonic-gate 	char *ret = NULL;
914*7c478bd9Sstevel@tonic-gate 	pathname_t pn;
915*7c478bd9Sstevel@tonic-gate 
916*7c478bd9Sstevel@tonic-gate 	/*
917*7c478bd9Sstevel@tonic-gate 	 * We use vn_compare() instead of VN_CMP() in order to detect lofs
918*7c478bd9Sstevel@tonic-gate 	 * mounts and stacked vnodes.
919*7c478bd9Sstevel@tonic-gate 	 */
920*7c478bd9Sstevel@tonic-gate 	if (vn_compare(vrootp, rootdir))
921*7c478bd9Sstevel@tonic-gate 		return (path);
922*7c478bd9Sstevel@tonic-gate 
923*7c478bd9Sstevel@tonic-gate 	if (pn_get(path, UIO_SYSSPACE, &pn) != 0)
924*7c478bd9Sstevel@tonic-gate 		return (NULL);
925*7c478bd9Sstevel@tonic-gate 
926*7c478bd9Sstevel@tonic-gate 	vp = rootdir;
927*7c478bd9Sstevel@tonic-gate 	VN_HOLD(vp);
928*7c478bd9Sstevel@tonic-gate 
929*7c478bd9Sstevel@tonic-gate 	while (pn_pathleft(&pn)) {
930*7c478bd9Sstevel@tonic-gate 		pn_skipslash(&pn);
931*7c478bd9Sstevel@tonic-gate 
932*7c478bd9Sstevel@tonic-gate 		if (pn_getcomponent(&pn, component) != 0)
933*7c478bd9Sstevel@tonic-gate 			break;
934*7c478bd9Sstevel@tonic-gate 
935*7c478bd9Sstevel@tonic-gate 		if (vn_ismntpt(vp) && traverse(&vp) != 0)
936*7c478bd9Sstevel@tonic-gate 			break;
937*7c478bd9Sstevel@tonic-gate 
938*7c478bd9Sstevel@tonic-gate 		if (VOP_LOOKUP(vp, component, &cvp, &pn, 0, rootdir, cr) != 0)
939*7c478bd9Sstevel@tonic-gate 			break;
940*7c478bd9Sstevel@tonic-gate 
941*7c478bd9Sstevel@tonic-gate 		VN_RELE(vp);
942*7c478bd9Sstevel@tonic-gate 		vp = cvp;
943*7c478bd9Sstevel@tonic-gate 
944*7c478bd9Sstevel@tonic-gate 		if (vn_compare(vp, vrootp)) {
945*7c478bd9Sstevel@tonic-gate 			ret = path + (pn.pn_path - pn.pn_buf);
946*7c478bd9Sstevel@tonic-gate 			break;
947*7c478bd9Sstevel@tonic-gate 		}
948*7c478bd9Sstevel@tonic-gate 	}
949*7c478bd9Sstevel@tonic-gate 
950*7c478bd9Sstevel@tonic-gate 	VN_RELE(vp);
951*7c478bd9Sstevel@tonic-gate 	pn_free(&pn);
952*7c478bd9Sstevel@tonic-gate 
953*7c478bd9Sstevel@tonic-gate 	return (ret);
954*7c478bd9Sstevel@tonic-gate }
955*7c478bd9Sstevel@tonic-gate 
956*7c478bd9Sstevel@tonic-gate /*
957*7c478bd9Sstevel@tonic-gate  * Given a directory, return the full, resolved path.  This looks up "..",
958*7c478bd9Sstevel@tonic-gate  * searches for the given vnode in the parent, appends the component, etc.  It
959*7c478bd9Sstevel@tonic-gate  * is used to implement vnodetopath() and getcwd() when the cached path fails
960*7c478bd9Sstevel@tonic-gate  * (or vfs_vnode_path is not set).
961*7c478bd9Sstevel@tonic-gate  */
962*7c478bd9Sstevel@tonic-gate static int
963*7c478bd9Sstevel@tonic-gate dirtopath(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen, cred_t *cr)
964*7c478bd9Sstevel@tonic-gate {
965*7c478bd9Sstevel@tonic-gate 	pathname_t pn, rpn, emptypn;
966*7c478bd9Sstevel@tonic-gate 	vnode_t *cmpvp, *pvp = NULL;
967*7c478bd9Sstevel@tonic-gate 	vnode_t *startvp = vp;
968*7c478bd9Sstevel@tonic-gate 	int err = 0;
969*7c478bd9Sstevel@tonic-gate 	size_t complen;
970*7c478bd9Sstevel@tonic-gate 	char *dbuf;
971*7c478bd9Sstevel@tonic-gate 	dirent64_t *dp;
972*7c478bd9Sstevel@tonic-gate 	char		*bufloc;
973*7c478bd9Sstevel@tonic-gate 	size_t		dlen = DIRENT64_RECLEN(MAXPATHLEN);
974*7c478bd9Sstevel@tonic-gate 	refstr_t	*mntpt;
975*7c478bd9Sstevel@tonic-gate 
976*7c478bd9Sstevel@tonic-gate 	/* Operation only allowed on directories */
977*7c478bd9Sstevel@tonic-gate 	ASSERT(vp->v_type == VDIR);
978*7c478bd9Sstevel@tonic-gate 
979*7c478bd9Sstevel@tonic-gate 	/* We must have at least enough space for "/" */
980*7c478bd9Sstevel@tonic-gate 	if (buflen < 2)
981*7c478bd9Sstevel@tonic-gate 		return (ENAMETOOLONG);
982*7c478bd9Sstevel@tonic-gate 
983*7c478bd9Sstevel@tonic-gate 	/* Start at end of string with terminating null */
984*7c478bd9Sstevel@tonic-gate 	bufloc = &buf[buflen - 1];
985*7c478bd9Sstevel@tonic-gate 	*bufloc = '\0';
986*7c478bd9Sstevel@tonic-gate 
987*7c478bd9Sstevel@tonic-gate 	pn_alloc(&pn);
988*7c478bd9Sstevel@tonic-gate 	pn_alloc(&rpn);
989*7c478bd9Sstevel@tonic-gate 	dbuf = kmem_alloc(dlen, KM_SLEEP);
990*7c478bd9Sstevel@tonic-gate 	bzero(&emptypn, sizeof (emptypn));
991*7c478bd9Sstevel@tonic-gate 
992*7c478bd9Sstevel@tonic-gate 	/*
993*7c478bd9Sstevel@tonic-gate 	 * Begin with an additional reference on vp.  This will be decremented
994*7c478bd9Sstevel@tonic-gate 	 * during the loop.
995*7c478bd9Sstevel@tonic-gate 	 */
996*7c478bd9Sstevel@tonic-gate 	VN_HOLD(vp);
997*7c478bd9Sstevel@tonic-gate 
998*7c478bd9Sstevel@tonic-gate 	for (;;) {
999*7c478bd9Sstevel@tonic-gate 		/*
1000*7c478bd9Sstevel@tonic-gate 		 * Return if we've reached the root.  If the buffer is empty,
1001*7c478bd9Sstevel@tonic-gate 		 * return '/'.  We explicitly don't use vn_compare(), since it
1002*7c478bd9Sstevel@tonic-gate 		 * compares the real vnodes.  A lofs mount of '/' would produce
1003*7c478bd9Sstevel@tonic-gate 		 * incorrect results otherwise.
1004*7c478bd9Sstevel@tonic-gate 		 */
1005*7c478bd9Sstevel@tonic-gate 		if (VN_CMP(vrootp, vp)) {
1006*7c478bd9Sstevel@tonic-gate 			if (*bufloc == '\0')
1007*7c478bd9Sstevel@tonic-gate 				*--bufloc = '/';
1008*7c478bd9Sstevel@tonic-gate 			break;
1009*7c478bd9Sstevel@tonic-gate 		}
1010*7c478bd9Sstevel@tonic-gate 
1011*7c478bd9Sstevel@tonic-gate 		/*
1012*7c478bd9Sstevel@tonic-gate 		 * If we've reached the VFS root, something has gone wrong.  We
1013*7c478bd9Sstevel@tonic-gate 		 * should have reached the root in the above check.  The only
1014*7c478bd9Sstevel@tonic-gate 		 * explantation is that 'vp' is not contained withing the given
1015*7c478bd9Sstevel@tonic-gate 		 * root, in which case we return EPERM.
1016*7c478bd9Sstevel@tonic-gate 		 */
1017*7c478bd9Sstevel@tonic-gate 		if (VN_CMP(rootdir, vp)) {
1018*7c478bd9Sstevel@tonic-gate 			err = EPERM;
1019*7c478bd9Sstevel@tonic-gate 			goto out;
1020*7c478bd9Sstevel@tonic-gate 		}
1021*7c478bd9Sstevel@tonic-gate 
1022*7c478bd9Sstevel@tonic-gate 		/*
1023*7c478bd9Sstevel@tonic-gate 		 * Shortcut: see if this vnode is a mountpoint.  If so,
1024*7c478bd9Sstevel@tonic-gate 		 * grab the path information from the vfs_t.
1025*7c478bd9Sstevel@tonic-gate 		 */
1026*7c478bd9Sstevel@tonic-gate 		if (vp->v_flag & VROOT) {
1027*7c478bd9Sstevel@tonic-gate 
1028*7c478bd9Sstevel@tonic-gate 			mntpt = vfs_getmntpoint(vp->v_vfsp);
1029*7c478bd9Sstevel@tonic-gate 			if ((err = pn_set(&pn, (char *)refstr_value(mntpt)))
1030*7c478bd9Sstevel@tonic-gate 			    == 0) {
1031*7c478bd9Sstevel@tonic-gate 				refstr_rele(mntpt);
1032*7c478bd9Sstevel@tonic-gate 				rpn.pn_path = rpn.pn_buf;
1033*7c478bd9Sstevel@tonic-gate 
1034*7c478bd9Sstevel@tonic-gate 				/*
1035*7c478bd9Sstevel@tonic-gate 				 * Ensure the mointpoint still exists.
1036*7c478bd9Sstevel@tonic-gate 				 */
1037*7c478bd9Sstevel@tonic-gate 				VN_HOLD(vrootp);
1038*7c478bd9Sstevel@tonic-gate 				if (vrootp != rootdir)
1039*7c478bd9Sstevel@tonic-gate 					VN_HOLD(vrootp);
1040*7c478bd9Sstevel@tonic-gate 				if (lookuppnvp(&pn, &rpn, 0, NULL,
1041*7c478bd9Sstevel@tonic-gate 				    &cmpvp, vrootp, vrootp, cr) == 0) {
1042*7c478bd9Sstevel@tonic-gate 
1043*7c478bd9Sstevel@tonic-gate 					if (VN_CMP(vp, cmpvp)) {
1044*7c478bd9Sstevel@tonic-gate 						VN_RELE(cmpvp);
1045*7c478bd9Sstevel@tonic-gate 
1046*7c478bd9Sstevel@tonic-gate 						complen = strlen(rpn.pn_path);
1047*7c478bd9Sstevel@tonic-gate 						bufloc -= complen;
1048*7c478bd9Sstevel@tonic-gate 						if (bufloc < buf) {
1049*7c478bd9Sstevel@tonic-gate 							err = ERANGE;
1050*7c478bd9Sstevel@tonic-gate 							goto out;
1051*7c478bd9Sstevel@tonic-gate 						}
1052*7c478bd9Sstevel@tonic-gate 						bcopy(rpn.pn_path, bufloc,
1053*7c478bd9Sstevel@tonic-gate 						    complen);
1054*7c478bd9Sstevel@tonic-gate 						break;
1055*7c478bd9Sstevel@tonic-gate 					} else {
1056*7c478bd9Sstevel@tonic-gate 						VN_RELE(cmpvp);
1057*7c478bd9Sstevel@tonic-gate 					}
1058*7c478bd9Sstevel@tonic-gate 				}
1059*7c478bd9Sstevel@tonic-gate 			} else {
1060*7c478bd9Sstevel@tonic-gate 				refstr_rele(mntpt);
1061*7c478bd9Sstevel@tonic-gate 			}
1062*7c478bd9Sstevel@tonic-gate 		}
1063*7c478bd9Sstevel@tonic-gate 
1064*7c478bd9Sstevel@tonic-gate 		/*
1065*7c478bd9Sstevel@tonic-gate 		 * Shortcuts failed, search for this vnode in its parent.  If
1066*7c478bd9Sstevel@tonic-gate 		 * this is a mountpoint, then get the vnode underneath.
1067*7c478bd9Sstevel@tonic-gate 		 */
1068*7c478bd9Sstevel@tonic-gate 		if (vp->v_flag & VROOT)
1069*7c478bd9Sstevel@tonic-gate 			vp = vn_under(vp);
1070*7c478bd9Sstevel@tonic-gate 		if ((err = VOP_LOOKUP(vp, "..", &pvp, &emptypn, 0, vrootp, cr))
1071*7c478bd9Sstevel@tonic-gate 		    != 0)
1072*7c478bd9Sstevel@tonic-gate 			goto out;
1073*7c478bd9Sstevel@tonic-gate 
1074*7c478bd9Sstevel@tonic-gate 		/*
1075*7c478bd9Sstevel@tonic-gate 		 * With extended attributes, it's possible for a directory to
1076*7c478bd9Sstevel@tonic-gate 		 * have a parent that is a regular file.  Check for that here.
1077*7c478bd9Sstevel@tonic-gate 		 */
1078*7c478bd9Sstevel@tonic-gate 		if (pvp->v_type != VDIR) {
1079*7c478bd9Sstevel@tonic-gate 			err = ENOTDIR;
1080*7c478bd9Sstevel@tonic-gate 			goto out;
1081*7c478bd9Sstevel@tonic-gate 		}
1082*7c478bd9Sstevel@tonic-gate 
1083*7c478bd9Sstevel@tonic-gate 		/*
1084*7c478bd9Sstevel@tonic-gate 		 * If this is true, something strange has happened.  This is
1085*7c478bd9Sstevel@tonic-gate 		 * only true if we are the root of a filesystem, which should
1086*7c478bd9Sstevel@tonic-gate 		 * have been caught by the check above.
1087*7c478bd9Sstevel@tonic-gate 		 */
1088*7c478bd9Sstevel@tonic-gate 		if (VN_CMP(pvp, vp)) {
1089*7c478bd9Sstevel@tonic-gate 			err = ENOENT;
1090*7c478bd9Sstevel@tonic-gate 			goto out;
1091*7c478bd9Sstevel@tonic-gate 		}
1092*7c478bd9Sstevel@tonic-gate 
1093*7c478bd9Sstevel@tonic-gate 		/*
1094*7c478bd9Sstevel@tonic-gate 		 * Search the parent directory for the entry corresponding to
1095*7c478bd9Sstevel@tonic-gate 		 * this vnode.
1096*7c478bd9Sstevel@tonic-gate 		 */
1097*7c478bd9Sstevel@tonic-gate 		if ((err = dirfindvp(vrootp, pvp, vp, cr, dbuf, dlen, &dp))
1098*7c478bd9Sstevel@tonic-gate 		    != 0)
1099*7c478bd9Sstevel@tonic-gate 			goto out;
1100*7c478bd9Sstevel@tonic-gate 		complen = strlen(dp->d_name);
1101*7c478bd9Sstevel@tonic-gate 		bufloc -= complen;
1102*7c478bd9Sstevel@tonic-gate 		if (bufloc <= buf) {
1103*7c478bd9Sstevel@tonic-gate 			err = ENAMETOOLONG;
1104*7c478bd9Sstevel@tonic-gate 			goto out;
1105*7c478bd9Sstevel@tonic-gate 		}
1106*7c478bd9Sstevel@tonic-gate 		bcopy(dp->d_name, bufloc, complen);
1107*7c478bd9Sstevel@tonic-gate 
1108*7c478bd9Sstevel@tonic-gate 		/* Prepend a slash to the current path.  */
1109*7c478bd9Sstevel@tonic-gate 		*--bufloc = '/';
1110*7c478bd9Sstevel@tonic-gate 
1111*7c478bd9Sstevel@tonic-gate 		/* And continue with the next component */
1112*7c478bd9Sstevel@tonic-gate 		VN_RELE(vp);
1113*7c478bd9Sstevel@tonic-gate 		vp = pvp;
1114*7c478bd9Sstevel@tonic-gate 		pvp = NULL;
1115*7c478bd9Sstevel@tonic-gate 	}
1116*7c478bd9Sstevel@tonic-gate 
1117*7c478bd9Sstevel@tonic-gate 	/*
1118*7c478bd9Sstevel@tonic-gate 	 * Place the path at the beginning of the buffer.
1119*7c478bd9Sstevel@tonic-gate 	 */
1120*7c478bd9Sstevel@tonic-gate 	if (bufloc != buf)
1121*7c478bd9Sstevel@tonic-gate 		ovbcopy(bufloc, buf, buflen - (bufloc - buf));
1122*7c478bd9Sstevel@tonic-gate 
1123*7c478bd9Sstevel@tonic-gate out:
1124*7c478bd9Sstevel@tonic-gate 	/*
1125*7c478bd9Sstevel@tonic-gate 	 * If the error was ESTALE and the current directory to look in
1126*7c478bd9Sstevel@tonic-gate 	 * was the root for this lookup, the root for a mounted file
1127*7c478bd9Sstevel@tonic-gate 	 * system, or the starting directory for lookups, then
1128*7c478bd9Sstevel@tonic-gate 	 * return ENOENT instead of ESTALE.  In this case, no recovery
1129*7c478bd9Sstevel@tonic-gate 	 * is possible by the higher level.  If ESTALE was returned for
1130*7c478bd9Sstevel@tonic-gate 	 * some intermediate directory along the path, then recovery
1131*7c478bd9Sstevel@tonic-gate 	 * is potentially possible and retrying from the higher level
1132*7c478bd9Sstevel@tonic-gate 	 * will either correct the situation by purging stale cache
1133*7c478bd9Sstevel@tonic-gate 	 * entries or eventually get back to the point where no recovery
1134*7c478bd9Sstevel@tonic-gate 	 * is possible.
1135*7c478bd9Sstevel@tonic-gate 	 */
1136*7c478bd9Sstevel@tonic-gate 	if (err == ESTALE &&
1137*7c478bd9Sstevel@tonic-gate 	    (VN_CMP(vp, vrootp) || (vp->v_flag & VROOT) || vp == startvp))
1138*7c478bd9Sstevel@tonic-gate 		err = ENOENT;
1139*7c478bd9Sstevel@tonic-gate 
1140*7c478bd9Sstevel@tonic-gate 	kmem_free(dbuf, dlen);
1141*7c478bd9Sstevel@tonic-gate 	VN_RELE(vp);
1142*7c478bd9Sstevel@tonic-gate 	if (pvp)
1143*7c478bd9Sstevel@tonic-gate 		VN_RELE(pvp);
1144*7c478bd9Sstevel@tonic-gate 	pn_free(&pn);
1145*7c478bd9Sstevel@tonic-gate 	pn_free(&rpn);
1146*7c478bd9Sstevel@tonic-gate 
1147*7c478bd9Sstevel@tonic-gate 	return (err);
1148*7c478bd9Sstevel@tonic-gate }
1149*7c478bd9Sstevel@tonic-gate 
1150*7c478bd9Sstevel@tonic-gate /*
1151*7c478bd9Sstevel@tonic-gate  * The additional flag, LOOKUP_CHECKREAD, is ued to enforce artificial
1152*7c478bd9Sstevel@tonic-gate  * constraints in order to be standards compliant.  For example, if we have
1153*7c478bd9Sstevel@tonic-gate  * the cached path of '/foo/bar', and '/foo' has permissions 100 (execute
1154*7c478bd9Sstevel@tonic-gate  * only), then we can legitimately look up the path to the current working
1155*7c478bd9Sstevel@tonic-gate  * directory without needing read permission.  Existing standards tests,
1156*7c478bd9Sstevel@tonic-gate  * however, assume that we are determining the path by repeatedly looking up
1157*7c478bd9Sstevel@tonic-gate  * "..".  We need to keep this behavior in order to maintain backwards
1158*7c478bd9Sstevel@tonic-gate  * compatibility.
1159*7c478bd9Sstevel@tonic-gate  */
1160*7c478bd9Sstevel@tonic-gate static int
1161*7c478bd9Sstevel@tonic-gate vnodetopath_common(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen,
1162*7c478bd9Sstevel@tonic-gate     cred_t *cr, int flags)
1163*7c478bd9Sstevel@tonic-gate {
1164*7c478bd9Sstevel@tonic-gate 	pathname_t pn, rpn;
1165*7c478bd9Sstevel@tonic-gate 	int ret, len;
1166*7c478bd9Sstevel@tonic-gate 	vnode_t *compvp, *pvp, *realvp;
1167*7c478bd9Sstevel@tonic-gate 	proc_t *p = curproc;
1168*7c478bd9Sstevel@tonic-gate 	char path[MAXNAMELEN];
1169*7c478bd9Sstevel@tonic-gate 	int doclose = 0;
1170*7c478bd9Sstevel@tonic-gate 
1171*7c478bd9Sstevel@tonic-gate 	/*
1172*7c478bd9Sstevel@tonic-gate 	 * If vrootp is NULL, get the root for curproc.  Callers with any other
1173*7c478bd9Sstevel@tonic-gate 	 * requirements should pass in a different vrootp.
1174*7c478bd9Sstevel@tonic-gate 	 */
1175*7c478bd9Sstevel@tonic-gate 	if (vrootp == NULL) {
1176*7c478bd9Sstevel@tonic-gate 		mutex_enter(&p->p_lock);
1177*7c478bd9Sstevel@tonic-gate 		if ((vrootp = PTOU(p)->u_rdir) == NULL)
1178*7c478bd9Sstevel@tonic-gate 			vrootp = rootdir;
1179*7c478bd9Sstevel@tonic-gate 		VN_HOLD(vrootp);
1180*7c478bd9Sstevel@tonic-gate 		mutex_exit(&p->p_lock);
1181*7c478bd9Sstevel@tonic-gate 	} else {
1182*7c478bd9Sstevel@tonic-gate 		VN_HOLD(vrootp);
1183*7c478bd9Sstevel@tonic-gate 	}
1184*7c478bd9Sstevel@tonic-gate 
1185*7c478bd9Sstevel@tonic-gate 	/*
1186*7c478bd9Sstevel@tonic-gate 	 * This is to get around an annoying artifact of the /proc filesystem,
1187*7c478bd9Sstevel@tonic-gate 	 * which is the behavior of {cwd/root}.  Trying to resolve this path
1188*7c478bd9Sstevel@tonic-gate 	 * will result in /proc/pid/cwd instead of whatever the real working
1189*7c478bd9Sstevel@tonic-gate 	 * directory is.  We can't rely on VOP_REALVP(), since that will break
1190*7c478bd9Sstevel@tonic-gate 	 * lofs.  The only difference between procfs and lofs is that opening
1191*7c478bd9Sstevel@tonic-gate 	 * the file will return the underling vnode in the case of procfs.
1192*7c478bd9Sstevel@tonic-gate 	 */
1193*7c478bd9Sstevel@tonic-gate 	if (vp->v_type == VDIR && VOP_REALVP(vp, &realvp) == 0 &&
1194*7c478bd9Sstevel@tonic-gate 	    realvp != vp) {
1195*7c478bd9Sstevel@tonic-gate 		VN_HOLD(vp);
1196*7c478bd9Sstevel@tonic-gate 		if (VOP_OPEN(&vp, FREAD, cr) == 0)
1197*7c478bd9Sstevel@tonic-gate 			doclose = 1;
1198*7c478bd9Sstevel@tonic-gate 		else
1199*7c478bd9Sstevel@tonic-gate 			VN_RELE(vp);
1200*7c478bd9Sstevel@tonic-gate 	}
1201*7c478bd9Sstevel@tonic-gate 
1202*7c478bd9Sstevel@tonic-gate 	pn_alloc(&pn);
1203*7c478bd9Sstevel@tonic-gate 
1204*7c478bd9Sstevel@tonic-gate 	/*
1205*7c478bd9Sstevel@tonic-gate 	 * Check to see if we have a cached path in the vnode.
1206*7c478bd9Sstevel@tonic-gate 	 */
1207*7c478bd9Sstevel@tonic-gate 	mutex_enter(&vp->v_lock);
1208*7c478bd9Sstevel@tonic-gate 	if (vn_path(vp) != NULL) {
1209*7c478bd9Sstevel@tonic-gate 		(void) pn_set(&pn, vn_path(vp));
1210*7c478bd9Sstevel@tonic-gate 		mutex_exit(&vp->v_lock);
1211*7c478bd9Sstevel@tonic-gate 
1212*7c478bd9Sstevel@tonic-gate 		pn_alloc(&rpn);
1213*7c478bd9Sstevel@tonic-gate 
1214*7c478bd9Sstevel@tonic-gate 		/* We should only cache absolute paths */
1215*7c478bd9Sstevel@tonic-gate 		ASSERT(pn.pn_buf[0] == '/');
1216*7c478bd9Sstevel@tonic-gate 
1217*7c478bd9Sstevel@tonic-gate 		/*
1218*7c478bd9Sstevel@tonic-gate 		 * If we are in a zone or a chroot environment, then we have to
1219*7c478bd9Sstevel@tonic-gate 		 * take additional steps, since the path to the root might not
1220*7c478bd9Sstevel@tonic-gate 		 * be readable with the current credentials, even though the
1221*7c478bd9Sstevel@tonic-gate 		 * process can legitmately access the file.  In this case, we
1222*7c478bd9Sstevel@tonic-gate 		 * do the following:
1223*7c478bd9Sstevel@tonic-gate 		 *
1224*7c478bd9Sstevel@tonic-gate 		 * lookuppnvp() with all privileges to get the resolved path.
1225*7c478bd9Sstevel@tonic-gate 		 * call localpath() to get the local portion of the path, and
1226*7c478bd9Sstevel@tonic-gate 		 * continue as normal.
1227*7c478bd9Sstevel@tonic-gate 		 *
1228*7c478bd9Sstevel@tonic-gate 		 * If the the conversion to a local path fails, then we continue
1229*7c478bd9Sstevel@tonic-gate 		 * as normal.  This is a heuristic to make process object file
1230*7c478bd9Sstevel@tonic-gate 		 * paths available from within a zone.  Because lofs doesn't
1231*7c478bd9Sstevel@tonic-gate 		 * support page operations, the vnode stored in the seg_t is
1232*7c478bd9Sstevel@tonic-gate 		 * actually the underlying real vnode, not the lofs node itself.
1233*7c478bd9Sstevel@tonic-gate 		 * Most of the time, the lofs path is the same as the underlying
1234*7c478bd9Sstevel@tonic-gate 		 * vnode (for example, /usr/lib/libc.so.1).
1235*7c478bd9Sstevel@tonic-gate 		 */
1236*7c478bd9Sstevel@tonic-gate 		if (vrootp != rootdir) {
1237*7c478bd9Sstevel@tonic-gate 			char *local = NULL;
1238*7c478bd9Sstevel@tonic-gate 			VN_HOLD(rootdir);
1239*7c478bd9Sstevel@tonic-gate 			if (lookuppnvp(&pn, &rpn, FOLLOW,
1240*7c478bd9Sstevel@tonic-gate 			    NULL, &compvp, rootdir, rootdir, kcred) == 0) {
1241*7c478bd9Sstevel@tonic-gate 				local = localpath(rpn.pn_path, vrootp,
1242*7c478bd9Sstevel@tonic-gate 				    kcred);
1243*7c478bd9Sstevel@tonic-gate 				VN_RELE(compvp);
1244*7c478bd9Sstevel@tonic-gate 			}
1245*7c478bd9Sstevel@tonic-gate 
1246*7c478bd9Sstevel@tonic-gate 			/*
1247*7c478bd9Sstevel@tonic-gate 			 * The original pn was changed through lookuppnvp(), so
1248*7c478bd9Sstevel@tonic-gate 			 * reset it.
1249*7c478bd9Sstevel@tonic-gate 			 */
1250*7c478bd9Sstevel@tonic-gate 			if (local) {
1251*7c478bd9Sstevel@tonic-gate 				(void) pn_set(&pn, local);
1252*7c478bd9Sstevel@tonic-gate 			} else {
1253*7c478bd9Sstevel@tonic-gate 				mutex_enter(&vp->v_lock);
1254*7c478bd9Sstevel@tonic-gate 				if (vn_path(vp) != NULL) {
1255*7c478bd9Sstevel@tonic-gate 					(void) pn_set(&pn, vn_path(vp));
1256*7c478bd9Sstevel@tonic-gate 					mutex_exit(&vp->v_lock);
1257*7c478bd9Sstevel@tonic-gate 				} else {
1258*7c478bd9Sstevel@tonic-gate 					mutex_exit(&vp->v_lock);
1259*7c478bd9Sstevel@tonic-gate 					goto notcached;
1260*7c478bd9Sstevel@tonic-gate 				}
1261*7c478bd9Sstevel@tonic-gate 			}
1262*7c478bd9Sstevel@tonic-gate 		}
1263*7c478bd9Sstevel@tonic-gate 
1264*7c478bd9Sstevel@tonic-gate 		/*
1265*7c478bd9Sstevel@tonic-gate 		 * We should have a local path at this point, so start the
1266*7c478bd9Sstevel@tonic-gate 		 * search from the root of the current process.
1267*7c478bd9Sstevel@tonic-gate 		 */
1268*7c478bd9Sstevel@tonic-gate 		VN_HOLD(vrootp);
1269*7c478bd9Sstevel@tonic-gate 		if (vrootp != rootdir)
1270*7c478bd9Sstevel@tonic-gate 			VN_HOLD(vrootp);
1271*7c478bd9Sstevel@tonic-gate 		ret = lookuppnvp(&pn, &rpn, FOLLOW | flags, NULL,
1272*7c478bd9Sstevel@tonic-gate 		    &compvp, vrootp, vrootp, cr);
1273*7c478bd9Sstevel@tonic-gate 		if (ret == 0) {
1274*7c478bd9Sstevel@tonic-gate 			/*
1275*7c478bd9Sstevel@tonic-gate 			 * Check to see if the returned vnode is the same as
1276*7c478bd9Sstevel@tonic-gate 			 * the one we expect.  If not, give up.
1277*7c478bd9Sstevel@tonic-gate 			 */
1278*7c478bd9Sstevel@tonic-gate 			if (!vn_compare(vp, compvp) &&
1279*7c478bd9Sstevel@tonic-gate 			    !vnode_match(vp, compvp, cr)) {
1280*7c478bd9Sstevel@tonic-gate 				VN_RELE(compvp);
1281*7c478bd9Sstevel@tonic-gate 				goto notcached;
1282*7c478bd9Sstevel@tonic-gate 			}
1283*7c478bd9Sstevel@tonic-gate 
1284*7c478bd9Sstevel@tonic-gate 			VN_RELE(compvp);
1285*7c478bd9Sstevel@tonic-gate 
1286*7c478bd9Sstevel@tonic-gate 			/*
1287*7c478bd9Sstevel@tonic-gate 			 * Return the result.
1288*7c478bd9Sstevel@tonic-gate 			 */
1289*7c478bd9Sstevel@tonic-gate 			if (buflen <= rpn.pn_pathlen)
1290*7c478bd9Sstevel@tonic-gate 				goto notcached;
1291*7c478bd9Sstevel@tonic-gate 
1292*7c478bd9Sstevel@tonic-gate 			bcopy(rpn.pn_path, buf, rpn.pn_pathlen + 1);
1293*7c478bd9Sstevel@tonic-gate 			pn_free(&pn);
1294*7c478bd9Sstevel@tonic-gate 			pn_free(&rpn);
1295*7c478bd9Sstevel@tonic-gate 			VN_RELE(vrootp);
1296*7c478bd9Sstevel@tonic-gate 			if (doclose) {
1297*7c478bd9Sstevel@tonic-gate 				(void) VOP_CLOSE(vp, FREAD, 1, 0, cr);
1298*7c478bd9Sstevel@tonic-gate 				VN_RELE(vp);
1299*7c478bd9Sstevel@tonic-gate 			}
1300*7c478bd9Sstevel@tonic-gate 			return (0);
1301*7c478bd9Sstevel@tonic-gate 		}
1302*7c478bd9Sstevel@tonic-gate 
1303*7c478bd9Sstevel@tonic-gate notcached:
1304*7c478bd9Sstevel@tonic-gate 		pn_free(&rpn);
1305*7c478bd9Sstevel@tonic-gate 	} else {
1306*7c478bd9Sstevel@tonic-gate 		mutex_exit(&vp->v_lock);
1307*7c478bd9Sstevel@tonic-gate 	}
1308*7c478bd9Sstevel@tonic-gate 
1309*7c478bd9Sstevel@tonic-gate 	pn_free(&pn);
1310*7c478bd9Sstevel@tonic-gate 
1311*7c478bd9Sstevel@tonic-gate 	if (vp->v_type != VDIR) {
1312*7c478bd9Sstevel@tonic-gate 		/*
1313*7c478bd9Sstevel@tonic-gate 		 * If we don't have a directory, try to find it in the dnlc via
1314*7c478bd9Sstevel@tonic-gate 		 * reverse lookup.  Once this is found, we can use the regular
1315*7c478bd9Sstevel@tonic-gate 		 * directory search to find the full path.
1316*7c478bd9Sstevel@tonic-gate 		 */
1317*7c478bd9Sstevel@tonic-gate 		if ((pvp = dnlc_reverse_lookup(vp, path, MAXNAMELEN)) != NULL) {
1318*7c478bd9Sstevel@tonic-gate 			ret = dirtopath(vrootp, pvp, buf, buflen, cr);
1319*7c478bd9Sstevel@tonic-gate 			if (ret == 0) {
1320*7c478bd9Sstevel@tonic-gate 				len = strlen(buf);
1321*7c478bd9Sstevel@tonic-gate 				if (len + strlen(path) + 1 >= buflen) {
1322*7c478bd9Sstevel@tonic-gate 					ret = ENAMETOOLONG;
1323*7c478bd9Sstevel@tonic-gate 				} else {
1324*7c478bd9Sstevel@tonic-gate 					if (buf[len - 1] != '/')
1325*7c478bd9Sstevel@tonic-gate 						buf[len++] = '/';
1326*7c478bd9Sstevel@tonic-gate 					bcopy(path, buf + len,
1327*7c478bd9Sstevel@tonic-gate 					    strlen(path) + 1);
1328*7c478bd9Sstevel@tonic-gate 				}
1329*7c478bd9Sstevel@tonic-gate 			}
1330*7c478bd9Sstevel@tonic-gate 
1331*7c478bd9Sstevel@tonic-gate 			VN_RELE(pvp);
1332*7c478bd9Sstevel@tonic-gate 		} else
1333*7c478bd9Sstevel@tonic-gate 			ret = ENOENT;
1334*7c478bd9Sstevel@tonic-gate 	} else
1335*7c478bd9Sstevel@tonic-gate 		ret = dirtopath(vrootp, vp, buf, buflen, cr);
1336*7c478bd9Sstevel@tonic-gate 
1337*7c478bd9Sstevel@tonic-gate 	VN_RELE(vrootp);
1338*7c478bd9Sstevel@tonic-gate 	if (doclose) {
1339*7c478bd9Sstevel@tonic-gate 		(void) VOP_CLOSE(vp, FREAD, 1, 0, cr);
1340*7c478bd9Sstevel@tonic-gate 		VN_RELE(vp);
1341*7c478bd9Sstevel@tonic-gate 	}
1342*7c478bd9Sstevel@tonic-gate 
1343*7c478bd9Sstevel@tonic-gate 	return (ret);
1344*7c478bd9Sstevel@tonic-gate }
1345*7c478bd9Sstevel@tonic-gate 
1346*7c478bd9Sstevel@tonic-gate int
1347*7c478bd9Sstevel@tonic-gate vnodetopath(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen, cred_t *cr)
1348*7c478bd9Sstevel@tonic-gate {
1349*7c478bd9Sstevel@tonic-gate 	return (vnodetopath_common(vrootp, vp, buf, buflen, cr, 0));
1350*7c478bd9Sstevel@tonic-gate }
1351*7c478bd9Sstevel@tonic-gate 
1352*7c478bd9Sstevel@tonic-gate int
1353*7c478bd9Sstevel@tonic-gate dogetcwd(char *buf, size_t buflen)
1354*7c478bd9Sstevel@tonic-gate {
1355*7c478bd9Sstevel@tonic-gate 	int ret;
1356*7c478bd9Sstevel@tonic-gate 	vnode_t *vp;
1357*7c478bd9Sstevel@tonic-gate 	vnode_t *compvp;
1358*7c478bd9Sstevel@tonic-gate 	refstr_t *cwd, *oldcwd;
1359*7c478bd9Sstevel@tonic-gate 	const char *value;
1360*7c478bd9Sstevel@tonic-gate 	pathname_t rpnp, pnp;
1361*7c478bd9Sstevel@tonic-gate 	proc_t *p = curproc;
1362*7c478bd9Sstevel@tonic-gate 
1363*7c478bd9Sstevel@tonic-gate 	/*
1364*7c478bd9Sstevel@tonic-gate 	 * Check to see if there is a cached version of the cwd.  If so, lookup
1365*7c478bd9Sstevel@tonic-gate 	 * the cached value and make sure it is the same vnode.
1366*7c478bd9Sstevel@tonic-gate 	 */
1367*7c478bd9Sstevel@tonic-gate 	mutex_enter(&p->p_lock);
1368*7c478bd9Sstevel@tonic-gate 	if ((cwd = PTOU(p)->u_cwd) != NULL)
1369*7c478bd9Sstevel@tonic-gate 		refstr_hold(cwd);
1370*7c478bd9Sstevel@tonic-gate 	vp = PTOU(p)->u_cdir;
1371*7c478bd9Sstevel@tonic-gate 	VN_HOLD(vp);
1372*7c478bd9Sstevel@tonic-gate 	mutex_exit(&p->p_lock);
1373*7c478bd9Sstevel@tonic-gate 
1374*7c478bd9Sstevel@tonic-gate 	/*
1375*7c478bd9Sstevel@tonic-gate 	 * Make sure we have permission to access the current directory.
1376*7c478bd9Sstevel@tonic-gate 	 */
1377*7c478bd9Sstevel@tonic-gate 	if ((ret = VOP_ACCESS(vp, VEXEC, 0, CRED())) != 0) {
1378*7c478bd9Sstevel@tonic-gate 		if (cwd != NULL)
1379*7c478bd9Sstevel@tonic-gate 			refstr_rele(cwd);
1380*7c478bd9Sstevel@tonic-gate 		VN_RELE(vp);
1381*7c478bd9Sstevel@tonic-gate 		return (ret);
1382*7c478bd9Sstevel@tonic-gate 	}
1383*7c478bd9Sstevel@tonic-gate 
1384*7c478bd9Sstevel@tonic-gate 	if (cwd) {
1385*7c478bd9Sstevel@tonic-gate 		value = refstr_value(cwd);
1386*7c478bd9Sstevel@tonic-gate 		if ((ret = pn_get((char *)value, UIO_SYSSPACE, &pnp)) != 0) {
1387*7c478bd9Sstevel@tonic-gate 			refstr_rele(cwd);
1388*7c478bd9Sstevel@tonic-gate 			VN_RELE(vp);
1389*7c478bd9Sstevel@tonic-gate 			return (ret);
1390*7c478bd9Sstevel@tonic-gate 		}
1391*7c478bd9Sstevel@tonic-gate 
1392*7c478bd9Sstevel@tonic-gate 		pn_alloc(&rpnp);
1393*7c478bd9Sstevel@tonic-gate 
1394*7c478bd9Sstevel@tonic-gate 		if (lookuppn(&pnp, &rpnp, NO_FOLLOW, NULL, &compvp) == 0) {
1395*7c478bd9Sstevel@tonic-gate 
1396*7c478bd9Sstevel@tonic-gate 			if (VN_CMP(vp, compvp) &&
1397*7c478bd9Sstevel@tonic-gate 			    strcmp(value, rpnp.pn_path) == 0) {
1398*7c478bd9Sstevel@tonic-gate 				VN_RELE(compvp);
1399*7c478bd9Sstevel@tonic-gate 				VN_RELE(vp);
1400*7c478bd9Sstevel@tonic-gate 				pn_free(&pnp);
1401*7c478bd9Sstevel@tonic-gate 				pn_free(&rpnp);
1402*7c478bd9Sstevel@tonic-gate 				if (strlen(value) + 1 > buflen) {
1403*7c478bd9Sstevel@tonic-gate 					refstr_rele(cwd);
1404*7c478bd9Sstevel@tonic-gate 					return (ENAMETOOLONG);
1405*7c478bd9Sstevel@tonic-gate 				}
1406*7c478bd9Sstevel@tonic-gate 				bcopy(value, buf, strlen(value) + 1);
1407*7c478bd9Sstevel@tonic-gate 				refstr_rele(cwd);
1408*7c478bd9Sstevel@tonic-gate 				return (0);
1409*7c478bd9Sstevel@tonic-gate 			}
1410*7c478bd9Sstevel@tonic-gate 
1411*7c478bd9Sstevel@tonic-gate 			VN_RELE(compvp);
1412*7c478bd9Sstevel@tonic-gate 		}
1413*7c478bd9Sstevel@tonic-gate 
1414*7c478bd9Sstevel@tonic-gate 		pn_free(&rpnp);
1415*7c478bd9Sstevel@tonic-gate 		pn_free(&pnp);
1416*7c478bd9Sstevel@tonic-gate 
1417*7c478bd9Sstevel@tonic-gate 		refstr_rele(cwd);
1418*7c478bd9Sstevel@tonic-gate 	}
1419*7c478bd9Sstevel@tonic-gate 
1420*7c478bd9Sstevel@tonic-gate 	ret = vnodetopath_common(NULL, vp, buf, buflen, CRED(),
1421*7c478bd9Sstevel@tonic-gate 	    LOOKUP_CHECKREAD);
1422*7c478bd9Sstevel@tonic-gate 
1423*7c478bd9Sstevel@tonic-gate 	VN_RELE(vp);
1424*7c478bd9Sstevel@tonic-gate 
1425*7c478bd9Sstevel@tonic-gate 	/*
1426*7c478bd9Sstevel@tonic-gate 	 * Store the new cwd and replace the existing cached copy.
1427*7c478bd9Sstevel@tonic-gate 	 */
1428*7c478bd9Sstevel@tonic-gate 	if (ret == 0)
1429*7c478bd9Sstevel@tonic-gate 		cwd = refstr_alloc(buf);
1430*7c478bd9Sstevel@tonic-gate 	else
1431*7c478bd9Sstevel@tonic-gate 		cwd = NULL;
1432*7c478bd9Sstevel@tonic-gate 
1433*7c478bd9Sstevel@tonic-gate 	mutex_enter(&p->p_lock);
1434*7c478bd9Sstevel@tonic-gate 	oldcwd = PTOU(p)->u_cwd;
1435*7c478bd9Sstevel@tonic-gate 	PTOU(p)->u_cwd = cwd;
1436*7c478bd9Sstevel@tonic-gate 	mutex_exit(&p->p_lock);
1437*7c478bd9Sstevel@tonic-gate 
1438*7c478bd9Sstevel@tonic-gate 	if (oldcwd)
1439*7c478bd9Sstevel@tonic-gate 		refstr_rele(oldcwd);
1440*7c478bd9Sstevel@tonic-gate 
1441*7c478bd9Sstevel@tonic-gate 	return (ret);
1442*7c478bd9Sstevel@tonic-gate }
1443