xref: /openbsd-src/sys/kern/vfs_lookup.c (revision e5157e49389faebcb42b7237d55fbf096d9c2523)
1 /*	$OpenBSD: vfs_lookup.c,v 1.47 2014/03/25 04:04:36 guenther Exp $	*/
2 /*	$NetBSD: vfs_lookup.c,v 1.17 1996/02/09 19:00:59 christos Exp $	*/
3 
4 /*
5  * Copyright (c) 1982, 1986, 1989, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  * (c) UNIX System Laboratories, Inc.
8  * All or some portions of this file are derived from material licensed
9  * to the University of California by American Telephone and Telegraph
10  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
11  * the permission of UNIX System Laboratories, Inc.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  *
37  *	@(#)vfs_lookup.c	8.6 (Berkeley) 11/21/94
38  */
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/syslimits.h>
43 #include <sys/time.h>
44 #include <sys/namei.h>
45 #include <sys/vnode.h>
46 #include <sys/mount.h>
47 #include <sys/errno.h>
48 #include <sys/malloc.h>
49 #include <sys/pool.h>
50 #include <sys/filedesc.h>
51 #include <sys/proc.h>
52 #include <sys/hash.h>
53 #include <sys/file.h>
54 #include <sys/fcntl.h>
55 
56 #ifdef KTRACE
57 #include <sys/ktrace.h>
58 #endif
59 
60 #include "systrace.h"
61 #if NSYSTRACE > 0
62 #include <dev/systrace.h>
63 #endif
64 
65 /*
66  * Convert a pathname into a pointer to a vnode.
67  *
68  * The FOLLOW flag is set when symbolic links are to be followed
69  * when they occur at the end of the name translation process.
70  * Symbolic links are always followed for all other pathname
71  * components other than the last.
72  *
73  * If the LOCKLEAF flag is set, a locked vnode is returned.
74  *
75  * The segflg defines whether the name is to be copied from user
76  * space or kernel space.
77  *
78  * Overall outline of namei:
79  *
80  *	copy in name
81  *	get starting directory
82  *	while (!done && !error) {
83  *		call lookup to search path.
84  *		if symbolic link, massage name in buffer and continue
85  *	}
86  */
87 int
88 namei(struct nameidata *ndp)
89 {
90 	struct filedesc *fdp;		/* pointer to file descriptor state */
91 	char *cp;			/* pointer into pathname argument */
92 	struct vnode *dp;		/* the directory we are searching */
93 	struct iovec aiov;		/* uio for reading symbolic links */
94 	struct uio auio;
95 	int error, linklen;
96 	struct componentname *cnp = &ndp->ni_cnd;
97 	struct proc *p = cnp->cn_proc;
98 
99 	ndp->ni_cnd.cn_cred = ndp->ni_cnd.cn_proc->p_ucred;
100 #ifdef DIAGNOSTIC
101 	if (!cnp->cn_cred || !cnp->cn_proc)
102 		panic ("namei: bad cred/proc");
103 	if (cnp->cn_nameiop & (~OPMASK))
104 		panic ("namei: nameiop contaminated with flags");
105 	if (cnp->cn_flags & OPMASK)
106 		panic ("namei: flags contaminated with nameiops");
107 #endif
108 	fdp = cnp->cn_proc->p_fd;
109 
110 	/*
111 	 * Get a buffer for the name to be translated, and copy the
112 	 * name into the buffer.
113 	 */
114 	if ((cnp->cn_flags & HASBUF) == 0)
115 		cnp->cn_pnbuf = pool_get(&namei_pool, PR_WAITOK);
116 	if (ndp->ni_segflg == UIO_SYSSPACE)
117 		error = copystr(ndp->ni_dirp, cnp->cn_pnbuf,
118 			    MAXPATHLEN, &ndp->ni_pathlen);
119 	else
120 		error = copyinstr(ndp->ni_dirp, cnp->cn_pnbuf,
121 			    MAXPATHLEN, &ndp->ni_pathlen);
122 
123 	/*
124 	 * Fail on null pathnames
125 	 */
126 	if (error == 0 && ndp->ni_pathlen == 1)
127 		error = ENOENT;
128 
129 	if (error) {
130 		pool_put(&namei_pool, cnp->cn_pnbuf);
131 		ndp->ni_vp = NULL;
132 		return (error);
133 	}
134 
135 #ifdef KTRACE
136 	if (KTRPOINT(cnp->cn_proc, KTR_NAMEI))
137 		ktrnamei(cnp->cn_proc, cnp->cn_pnbuf);
138 #endif
139 #if NSYSTRACE > 0
140 	if (ISSET(cnp->cn_proc->p_flag, P_SYSTRACE))
141 		systrace_namei(ndp);
142 #endif
143 
144 	/*
145 	 *  Strip trailing slashes, as requested
146 	 */
147 	if (cnp->cn_flags & STRIPSLASHES) {
148 		char *end = cnp->cn_pnbuf + ndp->ni_pathlen - 2;
149 
150 		cp = end;
151 		while (cp >= cnp->cn_pnbuf && (*cp == '/'))
152 			cp--;
153 
154 		/* Still some remaining characters in the buffer */
155 		if (cp >= cnp->cn_pnbuf) {
156 			ndp->ni_pathlen -= (end - cp);
157 			*(cp + 1) = '\0';
158 		}
159 	}
160 
161 	ndp->ni_loopcnt = 0;
162 
163 	/*
164 	 * Get starting point for the translation.
165 	 */
166 	if ((ndp->ni_rootdir = fdp->fd_rdir) == NULL)
167 		ndp->ni_rootdir = rootvnode;
168 	/*
169 	 * Check if starting from root directory or current directory.
170 	 */
171 	if (cnp->cn_pnbuf[0] == '/') {
172 		dp = ndp->ni_rootdir;
173 		vref(dp);
174 	} else if (ndp->ni_dirfd == AT_FDCWD) {
175 		dp = fdp->fd_cdir;
176 		vref(dp);
177 	} else {
178 		struct file *fp = fd_getfile(fdp, ndp->ni_dirfd);
179 		if (fp == NULL) {
180 			pool_put(&namei_pool, cnp->cn_pnbuf);
181 			return (EBADF);
182 		}
183 		dp = (struct vnode *)fp->f_data;
184 		if (fp->f_type != DTYPE_VNODE || dp->v_type != VDIR) {
185 			pool_put(&namei_pool, cnp->cn_pnbuf);
186 			return (ENOTDIR);
187 		}
188 		vref(dp);
189 	}
190 	for (;;) {
191 		if (!dp->v_mount) {
192 			/* Give up if the directory is no longer mounted */
193 			pool_put(&namei_pool, cnp->cn_pnbuf);
194 			return (ENOENT);
195 		}
196 		cnp->cn_nameptr = cnp->cn_pnbuf;
197 		ndp->ni_startdir = dp;
198 		if ((error = vfs_lookup(ndp)) != 0) {
199 			pool_put(&namei_pool, cnp->cn_pnbuf);
200 			return (error);
201 		}
202 		/*
203 		 * If not a symbolic link, return search result.
204 		 */
205 		if ((cnp->cn_flags & ISSYMLINK) == 0) {
206 			if ((cnp->cn_flags & (SAVENAME | SAVESTART)) == 0)
207 				pool_put(&namei_pool, cnp->cn_pnbuf);
208 			else
209 				cnp->cn_flags |= HASBUF;
210 			return (0);
211 		}
212 		if ((cnp->cn_flags & LOCKPARENT) && (cnp->cn_flags & ISLASTCN))
213 			VOP_UNLOCK(ndp->ni_dvp, 0, p);
214 		if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
215 			error = ELOOP;
216 			break;
217 		}
218 		if (ndp->ni_pathlen > 1)
219 			cp = pool_get(&namei_pool, PR_WAITOK);
220 		else
221 			cp = cnp->cn_pnbuf;
222 		aiov.iov_base = cp;
223 		aiov.iov_len = MAXPATHLEN;
224 		auio.uio_iov = &aiov;
225 		auio.uio_iovcnt = 1;
226 		auio.uio_offset = 0;
227 		auio.uio_rw = UIO_READ;
228 		auio.uio_segflg = UIO_SYSSPACE;
229 		auio.uio_procp = cnp->cn_proc;
230 		auio.uio_resid = MAXPATHLEN;
231 		error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred);
232 		if (error) {
233 badlink:
234 			if (ndp->ni_pathlen > 1)
235 				pool_put(&namei_pool, cp);
236 			break;
237 		}
238 		linklen = MAXPATHLEN - auio.uio_resid;
239 		if (linklen == 0) {
240 			error = ENOENT;
241 			goto badlink;
242 		}
243 		if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
244 			error = ENAMETOOLONG;
245 			goto badlink;
246 		}
247 		if (ndp->ni_pathlen > 1) {
248 			bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen);
249 			pool_put(&namei_pool, cnp->cn_pnbuf);
250 			cnp->cn_pnbuf = cp;
251 		} else
252 			cnp->cn_pnbuf[linklen] = '\0';
253 		ndp->ni_pathlen += linklen;
254 		vput(ndp->ni_vp);
255 		dp = ndp->ni_dvp;
256 		/*
257 		 * Check if root directory should replace current directory.
258 		 */
259 		if (cnp->cn_pnbuf[0] == '/') {
260 			vrele(dp);
261 			dp = ndp->ni_rootdir;
262 			vref(dp);
263 		}
264 	}
265 	pool_put(&namei_pool, cnp->cn_pnbuf);
266 	vrele(ndp->ni_dvp);
267 	vput(ndp->ni_vp);
268 	ndp->ni_vp = NULL;
269 	return (error);
270 }
271 
272 /*
273  * Search a pathname.
274  * This is a very central and rather complicated routine.
275  *
276  * The pathname is pointed to by ni_cnd.cn_nameptr and is of length
277  * ni_pathlen.  The starting directory is taken from ni_startdir. The
278  * pathname is descended until done, or a symbolic link is encountered.
279  * If the path is completed the flag ISLASTCN is set in ni_cnd.cn_flags.
280  * If a symbolic link need interpretation is encountered, the flag ISSYMLINK
281  * is set in ni_cnd.cn_flags.
282  *
283  * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
284  * whether the name is to be looked up, created, renamed, or deleted.
285  * When CREATE, RENAME, or DELETE is specified, information usable in
286  * creating, renaming, or deleting a directory entry may be calculated.
287  * If flag has LOCKPARENT or'ed into it, the parent directory is returned
288  * locked. If flag has WANTPARENT or'ed into it, the parent directory is
289  * returned unlocked. Otherwise the parent directory is not returned. If
290  * the target of the pathname exists and LOCKLEAF is or'ed into the flag
291  * the target is returned locked, otherwise it is returned unlocked.
292  * When creating or renaming and LOCKPARENT is specified, the target may not
293  * be ".".  When deleting and LOCKPARENT is specified, the target may be ".".
294  *
295  * Overall outline of lookup:
296  *
297  * dirloop:
298  *	identify next component of name at ndp->ni_ptr
299  *	handle degenerate case where name is null string
300  *	if .. and crossing mount points and on mounted filesys, find parent
301  *	call VOP_LOOKUP routine for next component name
302  *	    directory vnode returned in ni_dvp, unlocked unless LOCKPARENT set
303  *	    component vnode returned in ni_vp (if it exists), locked.
304  *	if result vnode is mounted on and crossing mount points,
305  *	    find mounted on vnode
306  *	if more components of name, do next level at dirloop
307  *	return the answer in ni_vp, locked if LOCKLEAF set
308  *	    if LOCKPARENT set, return locked parent in ni_dvp
309  *	    if WANTPARENT set, return unlocked parent in ni_dvp
310  */
311 int
312 vfs_lookup(struct nameidata *ndp)
313 {
314 	char *cp;			/* pointer into pathname argument */
315 	struct vnode *dp = 0;		/* the directory we are searching */
316 	struct vnode *tdp;		/* saved dp */
317 	struct mount *mp;		/* mount table entry */
318 	int docache;			/* == 0 do not cache last component */
319 	int wantparent;			/* 1 => wantparent or lockparent flag */
320 	int rdonly;			/* lookup read-only flag bit */
321 	int error = 0;
322 	int dpunlocked = 0;		/* dp has already been unlocked */
323 	int slashes;
324 	struct componentname *cnp = &ndp->ni_cnd;
325 	struct proc *p = cnp->cn_proc;
326 	/*
327 	 * Setup: break out flag bits into variables.
328 	 */
329 	wantparent = cnp->cn_flags & (LOCKPARENT | WANTPARENT);
330 	docache = (cnp->cn_flags & NOCACHE) ^ NOCACHE;
331 	if (cnp->cn_nameiop == DELETE ||
332 	    (wantparent && cnp->cn_nameiop != CREATE))
333 		docache = 0;
334 	rdonly = cnp->cn_flags & RDONLY;
335 	ndp->ni_dvp = NULL;
336 	cnp->cn_flags &= ~ISSYMLINK;
337 	dp = ndp->ni_startdir;
338 	ndp->ni_startdir = NULLVP;
339 	vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, p);
340 
341 	/*
342 	 * If we have a leading string of slashes, remove them, and just make
343 	 * sure the current node is a directory.
344 	 */
345 	cp = cnp->cn_nameptr;
346 	if (*cp == '/') {
347 		do {
348 			cp++;
349 		} while (*cp == '/');
350 		ndp->ni_pathlen -= cp - cnp->cn_nameptr;
351 		cnp->cn_nameptr = cp;
352 
353 		if (dp->v_type != VDIR) {
354 			error = ENOTDIR;
355 			goto bad;
356 		}
357 
358 		/*
359 		 * If we've exhausted the path name, then just return the
360 		 * current node.  If the caller requested the parent node (i.e.
361 		 * it's a CREATE, DELETE, or RENAME), and we don't have one
362 		 * (because this is the root directory), then we must fail.
363 		 */
364 		if (cnp->cn_nameptr[0] == '\0') {
365 			if (ndp->ni_dvp == NULL && wantparent) {
366 				error = EISDIR;
367 				goto bad;
368 			}
369 			ndp->ni_vp = dp;
370 			cnp->cn_flags |= ISLASTCN;
371 			goto terminal;
372 		}
373 	}
374 
375 dirloop:
376 	/*
377 	 * Search a new directory.
378 	 *
379 	 * The last component of the filename is left accessible via
380 	 * cnp->cn_nameptr for callers that need the name. Callers needing
381 	 * the name set the SAVENAME flag. When done, they assume
382 	 * responsibility for freeing the pathname buffer.
383 	 */
384 	cnp->cn_consume = 0;
385 
386 	/* XXX: Figure out the length of the last component. */
387 	cp = cnp->cn_nameptr;
388 	while (*cp && (*cp != '/'))
389 		cp++;
390 	cnp->cn_namelen = cp - cnp->cn_nameptr;
391 	if (cnp->cn_namelen > NAME_MAX) {
392 		error = ENAMETOOLONG;
393 		goto bad;
394 	}
395 
396 #ifdef NAMEI_DIAGNOSTIC
397 	{ char c = *cp;
398 	*cp = '\0';
399 	printf("{%s}: ", cnp->cn_nameptr);
400 	*cp = c; }
401 #endif
402 	ndp->ni_pathlen -= cnp->cn_namelen;
403 	ndp->ni_next = cp;
404 	/*
405 	 * If this component is followed by a slash, then move the pointer to
406 	 * the next component forward, and remember that this component must be
407 	 * a directory.
408 	 */
409 	if (*cp == '/') {
410 		do {
411 			cp++;
412 		} while (*cp == '/');
413 		slashes = cp - ndp->ni_next;
414 		ndp->ni_pathlen -= slashes;
415 		ndp->ni_next = cp;
416 		cnp->cn_flags |= REQUIREDIR;
417 	} else {
418 		slashes = 0;
419 		cnp->cn_flags &= ~REQUIREDIR;
420 	}
421 	/*
422 	 * We do special processing on the last component, whether or not it's
423 	 * a directory.  Cache all intervening lookups, but not the final one.
424 	 */
425 	if (*cp == '\0') {
426 		if (docache)
427 			cnp->cn_flags |= MAKEENTRY;
428 		else
429 			cnp->cn_flags &= ~MAKEENTRY;
430 		cnp->cn_flags |= ISLASTCN;
431 	} else {
432 		cnp->cn_flags |= MAKEENTRY;
433 		cnp->cn_flags &= ~ISLASTCN;
434 	}
435 	if (cnp->cn_namelen == 2 &&
436 	    cnp->cn_nameptr[1] == '.' && cnp->cn_nameptr[0] == '.')
437 		cnp->cn_flags |= ISDOTDOT;
438 	else
439 		cnp->cn_flags &= ~ISDOTDOT;
440 
441 	/*
442 	 * Handle "..": two special cases.
443 	 * 1. If at root directory (e.g. after chroot)
444 	 *    or at absolute root directory
445 	 *    then ignore it so can't get out.
446 	 * 2. If this vnode is the root of a mounted
447 	 *    filesystem, then replace it with the
448 	 *    vnode which was mounted on so we take the
449 	 *    .. in the other file system.
450 	 */
451 	if (cnp->cn_flags & ISDOTDOT) {
452 		for (;;) {
453 			if (dp == ndp->ni_rootdir || dp == rootvnode) {
454 				ndp->ni_dvp = dp;
455 				ndp->ni_vp = dp;
456 				vref(dp);
457 				goto nextname;
458 			}
459 			if ((dp->v_flag & VROOT) == 0 ||
460 			    (cnp->cn_flags & NOCROSSMOUNT))
461 				break;
462 			tdp = dp;
463 			dp = dp->v_mount->mnt_vnodecovered;
464 			vput(tdp);
465 			vref(dp);
466 			vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, p);
467 		}
468 	}
469 
470 	/*
471 	 * We now have a segment name to search for, and a directory to search.
472 	 */
473 	ndp->ni_dvp = dp;
474 	ndp->ni_vp = NULL;
475 	cnp->cn_flags &= ~PDIRUNLOCK;
476 
477 	if ((error = VOP_LOOKUP(dp, &ndp->ni_vp, cnp)) != 0) {
478 #ifdef DIAGNOSTIC
479 		if (ndp->ni_vp != NULL)
480 			panic("leaf should be empty");
481 #endif
482 #ifdef NAMEI_DIAGNOSTIC
483 		printf("not found\n");
484 #endif
485 		if (error != EJUSTRETURN)
486 			goto bad;
487 		/*
488 		 * If this was not the last component, or there were trailing
489 		 * slashes, then the name must exist.
490 		 */
491 		if (cnp->cn_flags & REQUIREDIR) {
492 			error = ENOENT;
493 			goto bad;
494 		}
495 		/*
496 		 * If creating and at end of pathname, then can consider
497 		 * allowing file to be created.
498 		 */
499 		if (rdonly || (ndp->ni_dvp->v_mount->mnt_flag & MNT_RDONLY)) {
500 			error = EROFS;
501 			goto bad;
502 		}
503 		/*
504 		 * We return with ni_vp NULL to indicate that the entry
505 		 * doesn't currently exist, leaving a pointer to the
506 		 * (possibly locked) directory inode in ndp->ni_dvp.
507 		 */
508 		if (cnp->cn_flags & SAVESTART) {
509 			ndp->ni_startdir = ndp->ni_dvp;
510 			vref(ndp->ni_startdir);
511 		}
512 		return (0);
513 	}
514 #ifdef NAMEI_DIAGNOSTIC
515 	printf("found\n");
516 #endif
517 
518 	/*
519 	 * Take into account any additional components consumed by the
520 	 * underlying filesystem.  This will include any trailing slashes after
521 	 * the last component consumed.
522 	 */
523 	if (cnp->cn_consume > 0) {
524 		if (cnp->cn_consume >= slashes) {
525 			cnp->cn_flags &= ~REQUIREDIR;
526 		}
527 
528 		ndp->ni_pathlen -= cnp->cn_consume - slashes;
529 		ndp->ni_next += cnp->cn_consume - slashes;
530 		cnp->cn_consume = 0;
531 		if (ndp->ni_next[0] == '\0')
532 			cnp->cn_flags |= ISLASTCN;
533 	}
534 
535 	dp = ndp->ni_vp;
536 	/*
537 	 * Check to see if the vnode has been mounted on;
538 	 * if so find the root of the mounted file system.
539 	 */
540 	while (dp->v_type == VDIR && (mp = dp->v_mountedhere) &&
541 	    (cnp->cn_flags & NOCROSSMOUNT) == 0) {
542 		if (vfs_busy(mp, VB_READ|VB_WAIT))
543 			continue;
544 		VOP_UNLOCK(dp, 0, p);
545 		error = VFS_ROOT(mp, &tdp);
546 		vfs_unbusy(mp);
547 		if (error) {
548 			dpunlocked = 1;
549 			goto bad2;
550 		}
551 		vrele(dp);
552 		ndp->ni_vp = dp = tdp;
553 	}
554 
555 	/*
556 	 * Check for symbolic link.  Back up over any slashes that we skipped,
557 	 * as we will need them again.
558 	 */
559 	if ((dp->v_type == VLNK) && (cnp->cn_flags & (FOLLOW|REQUIREDIR))) {
560 		ndp->ni_pathlen += slashes;
561 		ndp->ni_next -= slashes;
562 		cnp->cn_flags |= ISSYMLINK;
563 		return (0);
564 	}
565 
566 	/*
567 	 * Check for directory, if the component was followed by a series of
568 	 * slashes.
569 	 */
570 	if ((dp->v_type != VDIR) && (cnp->cn_flags & REQUIREDIR)) {
571 		error = ENOTDIR;
572 		goto bad2;
573 	}
574 
575 nextname:
576 	/*
577 	 * Not a symbolic link.  If this was not the last component, then
578 	 * continue at the next component, else return.
579 	 */
580 	if (!(cnp->cn_flags & ISLASTCN)) {
581 		cnp->cn_nameptr = ndp->ni_next;
582 		vrele(ndp->ni_dvp);
583 		goto dirloop;
584 	}
585 
586 terminal:
587 	/*
588 	 * Check for read-only file systems.
589 	 */
590 	if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) {
591 		/*
592 		 * Disallow directory write attempts on read-only
593 		 * file systems.
594 		 */
595 		if (rdonly || (dp->v_mount->mnt_flag & MNT_RDONLY) ||
596 		    (wantparent &&
597 		    (ndp->ni_dvp->v_mount->mnt_flag & MNT_RDONLY))) {
598 			error = EROFS;
599 			goto bad2;
600 		}
601 	}
602 	if (ndp->ni_dvp != NULL) {
603 		if (cnp->cn_flags & SAVESTART) {
604 			ndp->ni_startdir = ndp->ni_dvp;
605 			vref(ndp->ni_startdir);
606 		}
607 		if (!wantparent)
608 			vrele(ndp->ni_dvp);
609 	}
610 	if ((cnp->cn_flags & LOCKLEAF) == 0)
611 		VOP_UNLOCK(dp, 0, p);
612 	return (0);
613 
614 bad2:
615 	if ((cnp->cn_flags & LOCKPARENT) && (cnp->cn_flags & ISLASTCN) &&
616 	    ((cnp->cn_flags & PDIRUNLOCK) == 0))
617 		VOP_UNLOCK(ndp->ni_dvp, 0, p);
618 	vrele(ndp->ni_dvp);
619 bad:
620 	if (dpunlocked)
621 		vrele(dp);
622 	else
623 		vput(dp);
624 	ndp->ni_vp = NULL;
625 	return (error);
626 }
627 
628 /*
629  * Reacquire a path name component.
630  */
631 int
632 vfs_relookup(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp)
633 {
634 	struct proc *p = cnp->cn_proc;
635 	struct vnode *dp = 0;		/* the directory we are searching */
636 	int wantparent;			/* 1 => wantparent or lockparent flag */
637 	int rdonly;			/* lookup read-only flag bit */
638 	int error = 0;
639 #ifdef NAMEI_DIAGNOSTIC
640 	char *cp;			/* DEBUG: check name ptr/len */
641 #endif
642 
643 	/*
644 	 * Setup: break out flag bits into variables.
645 	 */
646 	wantparent = cnp->cn_flags & (LOCKPARENT|WANTPARENT);
647 	rdonly = cnp->cn_flags & RDONLY;
648 	cnp->cn_flags &= ~ISSYMLINK;
649 	dp = dvp;
650 	vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, p);
651 
652 /* dirloop: */
653 	/*
654 	 * Search a new directory.
655 	 *
656 	 * The last component of the filename is left accessible via
657 	 * cnp->cn_nameptr for callers that need the name. Callers needing
658 	 * the name set the SAVENAME flag. When done, they assume
659 	 * responsibility for freeing the pathname buffer.
660 	 */
661 
662 #ifdef NAMEI_DIAGNOSTIC
663 	/* XXX: Figure out the length of the last component. */
664 	cp = cnp->cn_nameptr;
665 	while (*cp && (*cp != '/')) {
666 		*cp++;
667 	}
668 	if (cnp->cn_namelen != cp - cnp->cn_nameptr)
669 		panic("relookup: bad len");
670 	if (*cp != 0)
671 		panic("relookup: not last component");
672 	printf("{%s}: ", cnp->cn_nameptr);
673 #endif
674 
675 	/*
676 	 * Check for degenerate name (e.g. / or "")
677 	 * which is a way of talking about a directory,
678 	 * e.g. like "/." or ".".
679 	 */
680 	if (cnp->cn_nameptr[0] == '\0')
681 		panic("relookup: null name");
682 
683 	if (cnp->cn_flags & ISDOTDOT)
684 		panic ("relookup: lookup on dot-dot");
685 
686 	/*
687 	 * We now have a segment name to search for, and a directory to search.
688 	 */
689 	if ((error = VOP_LOOKUP(dp, vpp, cnp)) != 0) {
690 #ifdef DIAGNOSTIC
691 		if (*vpp != NULL)
692 			panic("leaf should be empty");
693 #endif
694 		if (error != EJUSTRETURN)
695 			goto bad;
696 		/*
697 		 * If creating and at end of pathname, then can consider
698 		 * allowing file to be created.
699 		 */
700 		if (rdonly || (dvp->v_mount->mnt_flag & MNT_RDONLY)) {
701 			error = EROFS;
702 			goto bad;
703 		}
704 		/* ASSERT(dvp == ndp->ni_startdir) */
705 		if (cnp->cn_flags & SAVESTART)
706 			vref(dvp);
707 		/*
708 		 * We return with ni_vp NULL to indicate that the entry
709 		 * doesn't currently exist, leaving a pointer to the
710 		 * (possibly locked) directory inode in ndp->ni_dvp.
711 		 */
712 		return (0);
713 	}
714 	dp = *vpp;
715 
716 #ifdef DIAGNOSTIC
717 	/*
718 	 * Check for symbolic link
719 	 */
720 	if (dp->v_type == VLNK && (cnp->cn_flags & FOLLOW))
721 		panic ("relookup: symlink found.");
722 #endif
723 
724 	/*
725 	 * Check for read-only file systems.
726 	 */
727 	if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) {
728 		/*
729 		 * Disallow directory write attempts on read-only
730 		 * file systems.
731 		 */
732 		if (rdonly || (dp->v_mount->mnt_flag & MNT_RDONLY) ||
733 		    (wantparent &&
734 		    (dvp->v_mount->mnt_flag & MNT_RDONLY))) {
735 			error = EROFS;
736 			goto bad2;
737 		}
738 	}
739 	/* ASSERT(dvp == ndp->ni_startdir) */
740 	if (cnp->cn_flags & SAVESTART)
741 		vref(dvp);
742 	if (!wantparent)
743 		vrele(dvp);
744 	if ((cnp->cn_flags & LOCKLEAF) == 0)
745 		VOP_UNLOCK(dp, 0, p);
746 	return (0);
747 
748 bad2:
749 	if ((cnp->cn_flags & LOCKPARENT) && (cnp->cn_flags & ISLASTCN))
750 		VOP_UNLOCK(dvp, 0, p);
751 	vrele(dvp);
752 bad:
753 	vput(dp);
754 	*vpp = NULL;
755 	return (error);
756 }
757