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