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