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