1b819cea2SGordon Ross /*
2b819cea2SGordon Ross * CDDL HEADER START
3b819cea2SGordon Ross *
4b819cea2SGordon Ross * The contents of this file are subject to the terms of the
5b819cea2SGordon Ross * Common Development and Distribution License (the "License").
6b819cea2SGordon Ross * You may not use this file except in compliance with the License.
7b819cea2SGordon Ross *
8b819cea2SGordon Ross * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9b819cea2SGordon Ross * or http://www.opensolaris.org/os/licensing.
10b819cea2SGordon Ross * See the License for the specific language governing permissions
11b819cea2SGordon Ross * and limitations under the License.
12b819cea2SGordon Ross *
13b819cea2SGordon Ross * When distributing Covered Code, include this CDDL HEADER in each
14b819cea2SGordon Ross * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15b819cea2SGordon Ross * If applicable, add the following below this CDDL HEADER, with the
16b819cea2SGordon Ross * fields enclosed by brackets "[]" replaced with your own identifying
17b819cea2SGordon Ross * information: Portions Copyright [yyyy] [name of copyright owner]
18b819cea2SGordon Ross *
19b819cea2SGordon Ross * CDDL HEADER END
20b819cea2SGordon Ross */
21b819cea2SGordon Ross
22b819cea2SGordon Ross /*
23b819cea2SGordon Ross * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
24b819cea2SGordon Ross * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
25268cac54SGordon Ross * Copyright 2022 RackTop Systems, Inc.
26b819cea2SGordon Ross */
27b819cea2SGordon Ross
28b819cea2SGordon Ross /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
29268cac54SGordon Ross /* All Rights Reserved */
30b819cea2SGordon Ross
31b819cea2SGordon Ross /*
32b819cea2SGordon Ross * University Copyright- Copyright (c) 1982, 1986, 1988
33b819cea2SGordon Ross * The Regents of the University of California
34b819cea2SGordon Ross * All Rights Reserved
35b819cea2SGordon Ross *
36b819cea2SGordon Ross * University Acknowledgment- Portions of this document are derived from
37b819cea2SGordon Ross * software developed by the University of California, Berkeley, and its
38b819cea2SGordon Ross * contributors.
39b819cea2SGordon Ross */
40b819cea2SGordon Ross
41b819cea2SGordon Ross #include <sys/types.h>
42b819cea2SGordon Ross #include <sys/param.h>
43b819cea2SGordon Ross #include <sys/systm.h>
44b819cea2SGordon Ross #include <sys/file.h>
45b819cea2SGordon Ross #include <sys/errno.h>
46b819cea2SGordon Ross #include <sys/cred.h>
47b819cea2SGordon Ross #include <sys/user.h>
48b819cea2SGordon Ross #include <sys/uio.h>
49b819cea2SGordon Ross #include <sys/vfs.h>
50b819cea2SGordon Ross #include <sys/vnode.h>
51b819cea2SGordon Ross #include <sys/pathname.h>
52b819cea2SGordon Ross #include <sys/proc.h>
53b819cea2SGordon Ross #include <sys/vtrace.h>
54b819cea2SGordon Ross #include <sys/sysmacros.h>
55b819cea2SGordon Ross #include <sys/debug.h>
56b819cea2SGordon Ross #include <sys/dirent.h>
57b819cea2SGordon Ross #include <sys/zone.h>
58b819cea2SGordon Ross #include <sys/dnlc.h>
59b819cea2SGordon Ross #include <sys/fs/snode.h>
60b819cea2SGordon Ross
61268cac54SGordon Ross int
lookupname(const char * fnamep,enum uio_seg seg,int followlink,vnode_t ** dirvpp,vnode_t ** compvpp)62268cac54SGordon Ross lookupname(
63*5f61829aSRobert Mustacchi const char *fnamep,
64268cac54SGordon Ross enum uio_seg seg,
65268cac54SGordon Ross int followlink,
66268cac54SGordon Ross vnode_t **dirvpp,
67268cac54SGordon Ross vnode_t **compvpp)
68268cac54SGordon Ross {
69268cac54SGordon Ross return (lookupnameatcred(fnamep, seg, followlink, dirvpp, compvpp, NULL,
70268cac54SGordon Ross CRED()));
71268cac54SGordon Ross }
72268cac54SGordon Ross
73268cac54SGordon Ross /*
74268cac54SGordon Ross * Lookup the user file name,
75268cac54SGordon Ross * Handle allocation and freeing of pathname buffer, return error.
76268cac54SGordon Ross */
77268cac54SGordon Ross int
lookupnameatcred(const char * fnamep,enum uio_seg seg,int followlink,vnode_t ** dirvpp,vnode_t ** compvpp,vnode_t * startvp,cred_t * cr)78268cac54SGordon Ross lookupnameatcred(
79*5f61829aSRobert Mustacchi const char *fnamep, /* user pathname */
80268cac54SGordon Ross enum uio_seg seg, /* addr space that name is in */
81268cac54SGordon Ross int followlink, /* follow sym links */
82268cac54SGordon Ross vnode_t **dirvpp, /* ret for ptr to parent dir vnode */
83268cac54SGordon Ross vnode_t **compvpp, /* ret for ptr to component vnode */
84268cac54SGordon Ross vnode_t *startvp, /* start path search from vp */
85268cac54SGordon Ross cred_t *cr) /* credential */
86268cac54SGordon Ross {
87268cac54SGordon Ross char namebuf[TYPICALMAXPATHLEN];
88268cac54SGordon Ross struct pathname lookpn;
89268cac54SGordon Ross int error;
90268cac54SGordon Ross
91268cac54SGordon Ross error = pn_get_buf(fnamep, seg, &lookpn, namebuf, sizeof (namebuf));
92268cac54SGordon Ross if (error == 0) {
93268cac54SGordon Ross error = lookuppnatcred(&lookpn, NULL, followlink,
94268cac54SGordon Ross dirvpp, compvpp, startvp, cr);
95268cac54SGordon Ross }
96268cac54SGordon Ross if (error == ENAMETOOLONG) {
97268cac54SGordon Ross /*
98268cac54SGordon Ross * This thread used a pathname > TYPICALMAXPATHLEN bytes long.
99268cac54SGordon Ross */
100268cac54SGordon Ross if ((error = pn_get(fnamep, seg, &lookpn)) != 0)
101268cac54SGordon Ross return (error);
102268cac54SGordon Ross error = lookuppnatcred(&lookpn, NULL, followlink,
103268cac54SGordon Ross dirvpp, compvpp, startvp, cr);
104268cac54SGordon Ross pn_free(&lookpn);
105268cac54SGordon Ross }
106268cac54SGordon Ross
107268cac54SGordon Ross return (error);
108268cac54SGordon Ross }
109268cac54SGordon Ross
110268cac54SGordon Ross /*
111268cac54SGordon Ross * Lookup the user file name from a given vp, using a specific credential.
112268cac54SGordon Ross */
113268cac54SGordon Ross int
lookuppnatcred(struct pathname * pnp,struct pathname * rpnp,int followlink,vnode_t ** dirvpp,vnode_t ** compvpp,vnode_t * startvp,cred_t * cr)114268cac54SGordon Ross lookuppnatcred(
115268cac54SGordon Ross struct pathname *pnp, /* pathname to lookup */
116268cac54SGordon Ross struct pathname *rpnp, /* if non-NULL, return resolved path */
117268cac54SGordon Ross int followlink, /* (don't) follow sym links */
118268cac54SGordon Ross vnode_t **dirvpp, /* ptr for parent vnode */
119268cac54SGordon Ross vnode_t **compvpp, /* ptr for entry vnode */
120268cac54SGordon Ross vnode_t *startvp, /* start search from this vp */
121268cac54SGordon Ross cred_t *cr) /* user credential */
122268cac54SGordon Ross {
123268cac54SGordon Ross vnode_t *vp; /* current directory vp */
124268cac54SGordon Ross vnode_t *rootvp;
125268cac54SGordon Ross
126268cac54SGordon Ross if (pnp->pn_pathlen == 0)
127268cac54SGordon Ross return (ENOENT);
128268cac54SGordon Ross
129268cac54SGordon Ross /* Simplified for fake_... */
130268cac54SGordon Ross vp = rootvp = rootdir;
131268cac54SGordon Ross
132268cac54SGordon Ross /*
133268cac54SGordon Ross * Skip over leading slashes
134268cac54SGordon Ross */
135268cac54SGordon Ross if (pnp->pn_path[0] == '/') {
136268cac54SGordon Ross do {
137268cac54SGordon Ross pnp->pn_path++;
138268cac54SGordon Ross pnp->pn_pathlen--;
139268cac54SGordon Ross } while (pnp->pn_path[0] == '/');
140268cac54SGordon Ross }
141268cac54SGordon Ross
142268cac54SGordon Ross return (lookuppnvp(pnp, rpnp, followlink, dirvpp,
143268cac54SGordon Ross compvpp, rootvp, vp, cr));
144268cac54SGordon Ross }
145268cac54SGordon Ross
146b819cea2SGordon Ross /*
147b819cea2SGordon Ross * Starting at current directory, translate pathname pnp to end.
148b819cea2SGordon Ross * Leave pathname of final component in pnp, return the vnode
149b819cea2SGordon Ross * for the final component in *compvpp, and return the vnode
150b819cea2SGordon Ross * for the parent of the final component in dirvpp.
151b819cea2SGordon Ross *
152b819cea2SGordon Ross * This is the central routine in pathname translation and handles
153b819cea2SGordon Ross * multiple components in pathnames, separating them at /'s. It also
154b819cea2SGordon Ross * implements mounted file systems and processes symbolic links.
155b819cea2SGordon Ross *
156b819cea2SGordon Ross * vp is the vnode where the directory search should start.
157b819cea2SGordon Ross *
158b819cea2SGordon Ross * Reference counts: vp must be held prior to calling this function. rootvp
159b819cea2SGordon Ross * should only be held if rootvp != rootdir.
160b819cea2SGordon Ross */
161b819cea2SGordon 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)162b819cea2SGordon Ross lookuppnvp(
163b819cea2SGordon Ross struct pathname *pnp, /* pathname to lookup */
164b819cea2SGordon Ross struct pathname *rpnp, /* if non-NULL, return resolved path */
165b819cea2SGordon Ross int flags, /* follow symlinks */
166b819cea2SGordon Ross vnode_t **dirvpp, /* ptr for parent vnode */
167b819cea2SGordon Ross vnode_t **compvpp, /* ptr for entry vnode */
168b819cea2SGordon Ross vnode_t *rootvp, /* rootvp */
169b819cea2SGordon Ross vnode_t *vp, /* directory to start search at */
170b819cea2SGordon Ross cred_t *cr) /* user's credential */
171b819cea2SGordon Ross {
172b819cea2SGordon Ross vnode_t *cvp; /* current component vp */
173b819cea2SGordon Ross vnode_t *tvp; /* addressable temp ptr */
174b819cea2SGordon Ross char component[MAXNAMELEN]; /* buffer for component (incl null) */
175b819cea2SGordon Ross int error;
176b819cea2SGordon Ross int nlink;
177b819cea2SGordon Ross int lookup_flags;
178b819cea2SGordon Ross struct pathname presrvd; /* case preserved name */
179b819cea2SGordon Ross struct pathname *pp = NULL;
180b819cea2SGordon Ross vnode_t *startvp;
181b819cea2SGordon Ross int must_be_directory = 0;
182b819cea2SGordon Ross boolean_t retry_with_kcred;
183b819cea2SGordon Ross
184b819cea2SGordon Ross nlink = 0;
185b819cea2SGordon Ross cvp = NULL;
186b819cea2SGordon Ross if (rpnp)
187b819cea2SGordon Ross rpnp->pn_pathlen = 0;
188b819cea2SGordon Ross
189b819cea2SGordon Ross lookup_flags = dirvpp ? LOOKUP_DIR : 0;
190b819cea2SGordon Ross if (flags & FIGNORECASE) {
191b819cea2SGordon Ross lookup_flags |= FIGNORECASE;
192b819cea2SGordon Ross pn_alloc(&presrvd);
193b819cea2SGordon Ross pp = &presrvd;
194b819cea2SGordon Ross }
195b819cea2SGordon Ross
196b819cea2SGordon Ross /*
197b819cea2SGordon Ross * Eliminate any trailing slashes in the pathname.
198b819cea2SGordon Ross * If there are any, we must follow all symlinks.
199b819cea2SGordon Ross * Also, we must guarantee that the last component is a directory.
200b819cea2SGordon Ross */
201b819cea2SGordon Ross if (pn_fixslash(pnp)) {
202b819cea2SGordon Ross flags |= FOLLOW;
203b819cea2SGordon Ross must_be_directory = 1;
204b819cea2SGordon Ross }
205b819cea2SGordon Ross
206b819cea2SGordon Ross startvp = vp;
207b819cea2SGordon Ross next:
208b819cea2SGordon Ross retry_with_kcred = B_FALSE;
209b819cea2SGordon Ross
210b819cea2SGordon Ross /*
211b819cea2SGordon Ross * Make sure we have a directory.
212b819cea2SGordon Ross */
213b819cea2SGordon Ross if (vp->v_type != VDIR) {
214b819cea2SGordon Ross error = ENOTDIR;
215b819cea2SGordon Ross goto bad;
216b819cea2SGordon Ross }
217b819cea2SGordon Ross
218b819cea2SGordon Ross if (rpnp && VN_CMP(vp, rootvp))
219b819cea2SGordon Ross (void) pn_set(rpnp, "/");
220b819cea2SGordon Ross
221b819cea2SGordon Ross /*
222b819cea2SGordon Ross * Process the next component of the pathname.
223b819cea2SGordon Ross */
22454026d5aSGordon Ross if ((error = pn_getcomponent(pnp, component)) != 0) {
225b819cea2SGordon Ross goto bad;
226b819cea2SGordon Ross }
227b819cea2SGordon Ross
228b819cea2SGordon Ross /*
229b819cea2SGordon Ross * Handle "..": two special cases.
230b819cea2SGordon Ross * 1. If we're at the root directory (e.g. after chroot or
231b819cea2SGordon Ross * zone_enter) then change ".." to "." so we can't get
232b819cea2SGordon Ross * out of this subtree.
233b819cea2SGordon Ross * 2. If this vnode is the root of a mounted file system,
234b819cea2SGordon Ross * then replace it with the vnode that was mounted on
235b819cea2SGordon Ross * so that we take the ".." in the other file system.
236b819cea2SGordon Ross */
237b819cea2SGordon Ross if (component[0] == '.' && component[1] == '.' && component[2] == 0) {
238b819cea2SGordon Ross checkforroot:
239b819cea2SGordon Ross if (VN_CMP(vp, rootvp)) {
240b819cea2SGordon Ross component[1] = '\0';
241b819cea2SGordon Ross } else if (vp->v_flag & VROOT) {
242b819cea2SGordon Ross vfs_t *vfsp;
243b819cea2SGordon Ross cvp = vp;
244b819cea2SGordon Ross
245b819cea2SGordon Ross /*
246b819cea2SGordon Ross * While we deal with the vfs pointer from the vnode
247b819cea2SGordon Ross * the filesystem could have been forcefully unmounted
248b819cea2SGordon Ross * and the vnode's v_vfsp could have been invalidated
249b819cea2SGordon Ross * by VFS_UNMOUNT. Hence, we cache v_vfsp and use it
250b819cea2SGordon Ross * with vfs_rlock_wait/vfs_unlock.
251b819cea2SGordon Ross * It is safe to use the v_vfsp even it is freed by
252b819cea2SGordon Ross * VFS_UNMOUNT because vfs_rlock_wait/vfs_unlock
253b819cea2SGordon Ross * do not dereference v_vfsp. It is just used as a
254b819cea2SGordon Ross * magic cookie.
255b819cea2SGordon Ross * One more corner case here is the memory getting
256b819cea2SGordon Ross * reused for another vfs structure. In this case
257b819cea2SGordon Ross * lookuppnvp's vfs_rlock_wait will succeed, domount's
258b819cea2SGordon Ross * vfs_lock will fail and domount will bail out with an
259b819cea2SGordon Ross * error (EBUSY).
260b819cea2SGordon Ross */
261b819cea2SGordon Ross vfsp = cvp->v_vfsp;
262b819cea2SGordon Ross
263b819cea2SGordon Ross /*
264b819cea2SGordon Ross * This lock is used to synchronize
265b819cea2SGordon Ross * mounts/unmounts and lookups.
266b819cea2SGordon Ross * Threads doing mounts/unmounts hold the
267b819cea2SGordon Ross * writers version vfs_lock_wait().
268b819cea2SGordon Ross */
269b819cea2SGordon Ross
270b819cea2SGordon Ross vfs_rlock_wait(vfsp);
271b819cea2SGordon Ross
272b819cea2SGordon Ross /*
273b819cea2SGordon Ross * If this vnode is on a file system that
274b819cea2SGordon Ross * has been forcibly unmounted,
275b819cea2SGordon Ross * we can't proceed. Cancel this operation
276b819cea2SGordon Ross * and return EIO.
277b819cea2SGordon Ross *
278b819cea2SGordon Ross * vfs_vnodecovered is NULL if unmounted.
279b819cea2SGordon Ross * Currently, nfs uses VFS_UNMOUNTED to
280b819cea2SGordon Ross * check if it's a forced-umount. Keep the
281b819cea2SGordon Ross * same checking here as well even though it
282b819cea2SGordon Ross * may not be needed.
283b819cea2SGordon Ross */
284b819cea2SGordon Ross if (((vp = cvp->v_vfsp->vfs_vnodecovered) == NULL) ||
285b819cea2SGordon Ross (cvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)) {
286b819cea2SGordon Ross vfs_unlock(vfsp);
287b819cea2SGordon Ross VN_RELE(cvp);
288b819cea2SGordon Ross if (pp)
289b819cea2SGordon Ross pn_free(pp);
290b819cea2SGordon Ross return (EIO);
291b819cea2SGordon Ross }
292b819cea2SGordon Ross VN_HOLD(vp);
293b819cea2SGordon Ross vfs_unlock(vfsp);
294b819cea2SGordon Ross VN_RELE(cvp);
295b819cea2SGordon Ross cvp = NULL;
296b819cea2SGordon Ross /*
297b819cea2SGordon Ross * Crossing mount points. For eg: We are doing
298b819cea2SGordon Ross * a lookup of ".." for file systems root vnode
299b819cea2SGordon Ross * mounted here, and VOP_LOOKUP() (with covered vnode)
300b819cea2SGordon Ross * will be on underlying file systems mount point
301b819cea2SGordon Ross * vnode. Set retry_with_kcred flag as we might end
302b819cea2SGordon Ross * up doing VOP_LOOKUP() with kcred if required.
303b819cea2SGordon Ross */
304b819cea2SGordon Ross retry_with_kcred = B_TRUE;
305b819cea2SGordon Ross goto checkforroot;
306b819cea2SGordon Ross }
307b819cea2SGordon Ross }
308b819cea2SGordon Ross
309b819cea2SGordon Ross /*
310b819cea2SGordon Ross * Perform a lookup in the current directory.
311b819cea2SGordon Ross */
312b819cea2SGordon Ross error = VOP_LOOKUP(vp, component, &tvp, pnp, lookup_flags,
313b819cea2SGordon Ross rootvp, cr, NULL, NULL, pp);
314b819cea2SGordon Ross
315b819cea2SGordon Ross /*
316b819cea2SGordon Ross * Retry with kcred - If crossing mount points & error is EACCES.
317b819cea2SGordon Ross *
318b819cea2SGordon Ross * If we are crossing mount points here and doing ".." lookup,
319b819cea2SGordon Ross * VOP_LOOKUP() might fail if the underlying file systems
320b819cea2SGordon Ross * mount point has no execute permission. In cases like these,
321b819cea2SGordon Ross * we retry VOP_LOOKUP() by giving as much privilage as possible
322b819cea2SGordon Ross * by passing kcred credentials.
323b819cea2SGordon Ross *
324b819cea2SGordon Ross * In case of hierarchical file systems, passing kcred still may
325b819cea2SGordon Ross * or may not work.
326b819cea2SGordon Ross * For eg: UFS FS --> Mount NFS FS --> Again mount UFS on some
327b819cea2SGordon Ross * directory inside NFS FS.
328b819cea2SGordon Ross */
329b819cea2SGordon Ross if ((error == EACCES) && retry_with_kcred)
330b819cea2SGordon Ross error = VOP_LOOKUP(vp, component, &tvp, pnp, lookup_flags,
331b819cea2SGordon Ross rootvp, zone_kcred(), NULL, NULL, pp);
332b819cea2SGordon Ross
333b819cea2SGordon Ross cvp = tvp;
334b819cea2SGordon Ross if (error) {
335b819cea2SGordon Ross cvp = NULL;
336b819cea2SGordon Ross /*
337b819cea2SGordon Ross * On error, return hard error if
338b819cea2SGordon Ross * (a) we're not at the end of the pathname yet, or
339b819cea2SGordon Ross * (b) the caller didn't want the parent directory, or
340b819cea2SGordon Ross * (c) we failed for some reason other than a missing entry.
341b819cea2SGordon Ross */
342b819cea2SGordon Ross if (pn_pathleft(pnp) || dirvpp == NULL || error != ENOENT)
343b819cea2SGordon Ross goto bad;
344b819cea2SGordon Ross
345b819cea2SGordon Ross pn_setlast(pnp);
346b819cea2SGordon Ross /*
347b819cea2SGordon Ross * We inform the caller that the desired entry must be
348b819cea2SGordon Ross * a directory by adding a '/' to the component name.
349b819cea2SGordon Ross */
350b819cea2SGordon Ross if (must_be_directory && (error = pn_addslash(pnp)) != 0)
351b819cea2SGordon Ross goto bad;
352b819cea2SGordon Ross *dirvpp = vp;
353b819cea2SGordon Ross if (compvpp != NULL)
354b819cea2SGordon Ross *compvpp = NULL;
355b819cea2SGordon Ross if (rootvp != rootdir)
356b819cea2SGordon Ross VN_RELE(rootvp);
357b819cea2SGordon Ross if (pp)
358b819cea2SGordon Ross pn_free(pp);
359b819cea2SGordon Ross return (0);
360b819cea2SGordon Ross }
361b819cea2SGordon Ross
362b819cea2SGordon Ross /*
363b819cea2SGordon Ross * Traverse mount points.
364b819cea2SGordon Ross */
365b819cea2SGordon Ross if (vn_mountedvfs(cvp) != NULL) {
366b819cea2SGordon Ross tvp = cvp;
367b819cea2SGordon Ross if ((error = traverse(&tvp)) != 0) {
368b819cea2SGordon Ross /*
369b819cea2SGordon Ross * It is required to assign cvp here, because
370b819cea2SGordon Ross * traverse() will return a held vnode which
371b819cea2SGordon Ross * may different than the vnode that was passed
372b819cea2SGordon Ross * in (even in the error case). If traverse()
373b819cea2SGordon Ross * changes the vnode it releases the original,
374b819cea2SGordon Ross * and holds the new one.
375b819cea2SGordon Ross */
376b819cea2SGordon Ross cvp = tvp;
377b819cea2SGordon Ross goto bad;
378b819cea2SGordon Ross }
379b819cea2SGordon Ross cvp = tvp;
380b819cea2SGordon Ross }
381b819cea2SGordon Ross
382b819cea2SGordon Ross /*
383b819cea2SGordon Ross * If we hit a symbolic link and there is more path to be
384b819cea2SGordon Ross * translated or this operation does not wish to apply
385b819cea2SGordon Ross * to a link, then place the contents of the link at the
386b819cea2SGordon Ross * front of the remaining pathname.
387b819cea2SGordon Ross */
388b819cea2SGordon Ross if (cvp->v_type == VLNK && ((flags & FOLLOW) || pn_pathleft(pnp))) {
389b819cea2SGordon Ross struct pathname linkpath;
390b819cea2SGordon Ross
391b819cea2SGordon Ross if (++nlink > MAXSYMLINKS) {
392b819cea2SGordon Ross error = ELOOP;
393b819cea2SGordon Ross goto bad;
394b819cea2SGordon Ross }
395b819cea2SGordon Ross pn_alloc(&linkpath);
39654026d5aSGordon Ross if ((error = pn_getsymlink(cvp, &linkpath, cr)) != 0) {
397b819cea2SGordon Ross pn_free(&linkpath);
398b819cea2SGordon Ross goto bad;
399b819cea2SGordon Ross }
400b819cea2SGordon Ross
401b819cea2SGordon Ross if (pn_pathleft(&linkpath) == 0)
402b819cea2SGordon Ross (void) pn_set(&linkpath, ".");
403b819cea2SGordon Ross error = pn_insert(pnp, &linkpath, strlen(component));
404b819cea2SGordon Ross pn_free(&linkpath);
405b819cea2SGordon Ross if (error)
406b819cea2SGordon Ross goto bad;
407b819cea2SGordon Ross VN_RELE(cvp);
408b819cea2SGordon Ross cvp = NULL;
409b819cea2SGordon Ross if (pnp->pn_pathlen == 0) {
410b819cea2SGordon Ross error = ENOENT;
411b819cea2SGordon Ross goto bad;
412b819cea2SGordon Ross }
413b819cea2SGordon Ross if (pnp->pn_path[0] == '/') {
414b819cea2SGordon Ross do {
415b819cea2SGordon Ross pnp->pn_path++;
416b819cea2SGordon Ross pnp->pn_pathlen--;
417b819cea2SGordon Ross } while (pnp->pn_path[0] == '/');
418b819cea2SGordon Ross VN_RELE(vp);
419b819cea2SGordon Ross vp = rootvp;
420b819cea2SGordon Ross VN_HOLD(vp);
421b819cea2SGordon Ross }
422b819cea2SGordon Ross if (pn_fixslash(pnp)) {
423b819cea2SGordon Ross flags |= FOLLOW;
424b819cea2SGordon Ross must_be_directory = 1;
425b819cea2SGordon Ross }
426b819cea2SGordon Ross goto next;
427b819cea2SGordon Ross }
428b819cea2SGordon Ross
429b819cea2SGordon Ross /*
430b819cea2SGordon Ross * If rpnp is non-NULL, remember the resolved path name therein.
431b819cea2SGordon Ross * Do not include "." components. Collapse occurrences of
432b819cea2SGordon Ross * "previous/..", so long as "previous" is not itself "..".
433b819cea2SGordon Ross * Exhausting rpnp results in error ENAMETOOLONG.
434b819cea2SGordon Ross */
435b819cea2SGordon Ross if (rpnp && strcmp(component, ".") != 0) {
436b819cea2SGordon Ross size_t len;
437b819cea2SGordon Ross
438b819cea2SGordon Ross if (strcmp(component, "..") == 0 &&
439b819cea2SGordon Ross rpnp->pn_pathlen != 0 &&
440b819cea2SGordon Ross !((rpnp->pn_pathlen > 2 &&
441b819cea2SGordon Ross strncmp(rpnp->pn_path+rpnp->pn_pathlen-3, "/..", 3) == 0) ||
442b819cea2SGordon Ross (rpnp->pn_pathlen == 2 &&
443b819cea2SGordon Ross strncmp(rpnp->pn_path, "..", 2) == 0))) {
444b819cea2SGordon Ross while (rpnp->pn_pathlen &&
445b819cea2SGordon Ross rpnp->pn_path[rpnp->pn_pathlen-1] != '/')
446b819cea2SGordon Ross rpnp->pn_pathlen--;
447b819cea2SGordon Ross if (rpnp->pn_pathlen > 1)
448b819cea2SGordon Ross rpnp->pn_pathlen--;
449b819cea2SGordon Ross rpnp->pn_path[rpnp->pn_pathlen] = '\0';
450b819cea2SGordon Ross } else {
451b819cea2SGordon Ross if (rpnp->pn_pathlen != 0 &&
452b819cea2SGordon Ross rpnp->pn_path[rpnp->pn_pathlen-1] != '/')
453b819cea2SGordon Ross rpnp->pn_path[rpnp->pn_pathlen++] = '/';
454b819cea2SGordon Ross if (flags & FIGNORECASE) {
455b819cea2SGordon Ross /*
456b819cea2SGordon Ross * Return the case-preserved name
457b819cea2SGordon Ross * within the resolved path.
458b819cea2SGordon Ross */
459b819cea2SGordon Ross error = copystr(pp->pn_buf,
460b819cea2SGordon Ross rpnp->pn_path + rpnp->pn_pathlen,
461b819cea2SGordon Ross rpnp->pn_bufsize - rpnp->pn_pathlen, &len);
462b819cea2SGordon Ross } else {
463b819cea2SGordon Ross error = copystr(component,
464b819cea2SGordon Ross rpnp->pn_path + rpnp->pn_pathlen,
465b819cea2SGordon Ross rpnp->pn_bufsize - rpnp->pn_pathlen, &len);
466b819cea2SGordon Ross }
467b819cea2SGordon Ross if (error) /* copystr() returns ENAMETOOLONG */
468b819cea2SGordon Ross goto bad;
469b819cea2SGordon Ross rpnp->pn_pathlen += (len - 1);
470b819cea2SGordon Ross ASSERT(rpnp->pn_bufsize > rpnp->pn_pathlen);
471b819cea2SGordon Ross }
472b819cea2SGordon Ross }
473b819cea2SGordon Ross
474b819cea2SGordon Ross /*
475b819cea2SGordon Ross * If no more components, return last directory (if wanted) and
476b819cea2SGordon Ross * last component (if wanted).
477b819cea2SGordon Ross */
478b819cea2SGordon Ross if (pn_pathleft(pnp) == 0) {
479b819cea2SGordon Ross /*
480b819cea2SGordon Ross * If there was a trailing slash in the pathname,
481b819cea2SGordon Ross * make sure the last component is a directory.
482b819cea2SGordon Ross */
483b819cea2SGordon Ross if (must_be_directory && cvp->v_type != VDIR) {
484b819cea2SGordon Ross error = ENOTDIR;
485b819cea2SGordon Ross goto bad;
486b819cea2SGordon Ross }
487b819cea2SGordon Ross if (dirvpp != NULL) {
488b819cea2SGordon Ross /*
489b819cea2SGordon Ross * Check that we have the real parent and not
490b819cea2SGordon Ross * an alias of the last component.
491b819cea2SGordon Ross */
492b819cea2SGordon Ross if (vn_compare(vp, cvp)) {
493b819cea2SGordon Ross pn_setlast(pnp);
494b819cea2SGordon Ross VN_RELE(vp);
495b819cea2SGordon Ross VN_RELE(cvp);
496b819cea2SGordon Ross if (rootvp != rootdir)
497b819cea2SGordon Ross VN_RELE(rootvp);
498b819cea2SGordon Ross if (pp)
499b819cea2SGordon Ross pn_free(pp);
500b819cea2SGordon Ross return (EINVAL);
501b819cea2SGordon Ross }
502b819cea2SGordon Ross *dirvpp = vp;
503b819cea2SGordon Ross } else
504b819cea2SGordon Ross VN_RELE(vp);
505b819cea2SGordon Ross if (pnp->pn_path == pnp->pn_buf)
506b819cea2SGordon Ross (void) pn_set(pnp, ".");
507b819cea2SGordon Ross else
508b819cea2SGordon Ross pn_setlast(pnp);
509b819cea2SGordon Ross if (rpnp) {
510b819cea2SGordon Ross if (VN_CMP(cvp, rootvp))
511b819cea2SGordon Ross (void) pn_set(rpnp, "/");
512b819cea2SGordon Ross else if (rpnp->pn_pathlen == 0)
513b819cea2SGordon Ross (void) pn_set(rpnp, ".");
514b819cea2SGordon Ross }
515b819cea2SGordon Ross
516b819cea2SGordon Ross if (compvpp != NULL)
517b819cea2SGordon Ross *compvpp = cvp;
518b819cea2SGordon Ross else
519b819cea2SGordon Ross VN_RELE(cvp);
520b819cea2SGordon Ross if (rootvp != rootdir)
521b819cea2SGordon Ross VN_RELE(rootvp);
522b819cea2SGordon Ross if (pp)
523b819cea2SGordon Ross pn_free(pp);
524b819cea2SGordon Ross return (0);
525b819cea2SGordon Ross }
526b819cea2SGordon Ross
527b819cea2SGordon Ross /*
528b819cea2SGordon Ross * Skip over slashes from end of last component.
529b819cea2SGordon Ross */
530b819cea2SGordon Ross while (pnp->pn_path[0] == '/') {
531b819cea2SGordon Ross pnp->pn_path++;
532b819cea2SGordon Ross pnp->pn_pathlen--;
533b819cea2SGordon Ross }
534b819cea2SGordon Ross
535b819cea2SGordon Ross /*
536b819cea2SGordon Ross * Searched through another level of directory:
537b819cea2SGordon Ross * release previous directory handle and save new (result
538b819cea2SGordon Ross * of lookup) as current directory.
539b819cea2SGordon Ross */
540b819cea2SGordon Ross VN_RELE(vp);
541b819cea2SGordon Ross vp = cvp;
542b819cea2SGordon Ross cvp = NULL;
543b819cea2SGordon Ross goto next;
544b819cea2SGordon Ross
545b819cea2SGordon Ross bad:
546b819cea2SGordon Ross /*
547b819cea2SGordon Ross * Error. Release vnodes and return.
548b819cea2SGordon Ross */
549b819cea2SGordon Ross if (cvp)
550b819cea2SGordon Ross VN_RELE(cvp);
551b819cea2SGordon Ross /*
552b819cea2SGordon Ross * If the error was ESTALE and the current directory to look in
553b819cea2SGordon Ross * was the root for this lookup, the root for a mounted file
554b819cea2SGordon Ross * system, or the starting directory for lookups, then
555b819cea2SGordon Ross * return ENOENT instead of ESTALE. In this case, no recovery
556b819cea2SGordon Ross * is possible by the higher level. If ESTALE was returned for
557b819cea2SGordon Ross * some intermediate directory along the path, then recovery
558b819cea2SGordon Ross * is potentially possible and retrying from the higher level
559b819cea2SGordon Ross * will either correct the situation by purging stale cache
560b819cea2SGordon Ross * entries or eventually get back to the point where no recovery
561b819cea2SGordon Ross * is possible.
562b819cea2SGordon Ross */
563b819cea2SGordon Ross if (error == ESTALE &&
564b819cea2SGordon Ross (VN_CMP(vp, rootvp) || (vp->v_flag & VROOT) || vp == startvp))
565b819cea2SGordon Ross error = ENOENT;
566b819cea2SGordon Ross VN_RELE(vp);
567b819cea2SGordon Ross if (rootvp != rootdir)
568b819cea2SGordon Ross VN_RELE(rootvp);
569b819cea2SGordon Ross if (pp)
570b819cea2SGordon Ross pn_free(pp);
571b819cea2SGordon Ross return (error);
572b819cea2SGordon Ross }
573b819cea2SGordon Ross
574b819cea2SGordon Ross /*
575b819cea2SGordon Ross * Traverse a mount point. Routine accepts a vnode pointer as a reference
576b819cea2SGordon Ross * parameter and performs the indirection, releasing the original vnode.
577b819cea2SGordon Ross */
578b819cea2SGordon Ross int
traverse(vnode_t ** cvpp)579b819cea2SGordon Ross traverse(vnode_t **cvpp)
580b819cea2SGordon Ross {
581b819cea2SGordon Ross int error = 0;
582b819cea2SGordon Ross vnode_t *cvp;
583b819cea2SGordon Ross vnode_t *tvp;
584b819cea2SGordon Ross vfs_t *vfsp;
585b819cea2SGordon Ross
586b819cea2SGordon Ross cvp = *cvpp;
587b819cea2SGordon Ross
588b819cea2SGordon Ross /*
589b819cea2SGordon Ross * If this vnode is mounted on, then we transparently indirect
590b819cea2SGordon Ross * to the vnode which is the root of the mounted file system.
591b819cea2SGordon Ross * Before we do this we must check that an unmount is not in
592b819cea2SGordon Ross * progress on this vnode.
593b819cea2SGordon Ross */
594b819cea2SGordon Ross
595b819cea2SGordon Ross for (;;) {
596b819cea2SGordon Ross /*
597b819cea2SGordon Ross * Used to try to read lock the vnode here.
598b819cea2SGordon Ross */
599b819cea2SGordon Ross
600b819cea2SGordon Ross /*
601b819cea2SGordon Ross * Reached the end of the mount chain?
602b819cea2SGordon Ross */
603b819cea2SGordon Ross vfsp = vn_mountedvfs(cvp);
604b819cea2SGordon Ross if (vfsp == NULL) {
605b819cea2SGordon Ross break;
606b819cea2SGordon Ross }
607b819cea2SGordon Ross
608b819cea2SGordon Ross /*
609b819cea2SGordon Ross * The read lock must be held across the call to VFS_ROOT() to
610b819cea2SGordon Ross * prevent a concurrent unmount from destroying the vfs.
611b819cea2SGordon Ross */
612b819cea2SGordon Ross error = VFS_ROOT(vfsp, &tvp);
613b819cea2SGordon Ross if (error)
614b819cea2SGordon Ross break;
615b819cea2SGordon Ross
616b819cea2SGordon Ross VN_RELE(cvp);
617b819cea2SGordon Ross
618b819cea2SGordon Ross cvp = tvp;
619b819cea2SGordon Ross }
620b819cea2SGordon Ross
621b819cea2SGordon Ross *cvpp = cvp;
622b819cea2SGordon Ross return (error);
623b819cea2SGordon Ross }
624b819cea2SGordon Ross
625b819cea2SGordon Ross /*
626b819cea2SGordon Ross * Get the vnode path, relative to the passed rootvp.
627b819cea2SGordon Ross * Our vncache always fills in v_path, so this is easy.
628b819cea2SGordon Ross */
629b819cea2SGordon Ross /* ARGSUSED */
630b819cea2SGordon Ross int
vnodetopath(vnode_t * vrootp,vnode_t * vp,char * buf,size_t buflen,cred_t * cr)631b819cea2SGordon Ross vnodetopath(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen, cred_t *cr)
632b819cea2SGordon Ross {
633b819cea2SGordon Ross int len, rvp_len = 0;
634b819cea2SGordon Ross const char *p = vp->v_path;
635b819cea2SGordon Ross
636b819cea2SGordon Ross if (vrootp)
637b819cea2SGordon Ross rvp_len = strlen(vrootp->v_path);
638b819cea2SGordon Ross len = strlen(p);
639b819cea2SGordon Ross if (rvp_len < len)
640b819cea2SGordon Ross p += rvp_len;
641b819cea2SGordon Ross else
642b819cea2SGordon Ross p = "/";
643b819cea2SGordon Ross
644b819cea2SGordon Ross (void) strlcpy(buf, p, buflen);
645b819cea2SGordon Ross return (0);
646b819cea2SGordon Ross }
647