xref: /csrg-svn/sys/kern/vfs_lookup.c (revision 52634)
123401Smckusick /*
237715Smckusick  * Copyright (c) 1982, 1986, 1989 Regents of the University of California.
337715Smckusick  * All rights reserved.
423401Smckusick  *
544457Sbostic  * %sccs.include.redist.c%
637715Smckusick  *
7*52634Smckusick  *	@(#)vfs_lookup.c	7.37 (Berkeley) 02/21/92
823401Smckusick  */
930Sbill 
1017100Sbloom #include "param.h"
1149736Smckusick #include "syslimits.h"
1237715Smckusick #include "time.h"
1337715Smckusick #include "namei.h"
1437715Smckusick #include "vnode.h"
1517100Sbloom #include "mount.h"
1637715Smckusick #include "errno.h"
1731650Smckusick #include "malloc.h"
1845914Smckusick #include "filedesc.h"
1945924Smckusick #include "proc.h"
2037715Smckusick 
2137582Smarc #ifdef KTRACE
2237582Smarc #include "ktrace.h"
2337582Smarc #endif
2430Sbill 
2530Sbill /*
2627268Smckusick  * Convert a pathname into a pointer to a locked inode.
2730Sbill  *
2816688Smckusick  * The FOLLOW flag is set when symbolic links are to be followed
299166Ssam  * when they occur at the end of the name translation process.
3027268Smckusick  * Symbolic links are always followed for all other pathname
3127268Smckusick  * components other than the last.
329166Ssam  *
3327268Smckusick  * The segflg defines whether the name is to be copied from user
3427268Smckusick  * space or kernel space.
3527268Smckusick  *
3615798Smckusick  * Overall outline of namei:
3715798Smckusick  *
387534Sroot  *	copy in name
397534Sroot  *	get starting directory
4049736Smckusick  *	while (!done && !error) {
4149736Smckusick  *		call lookup to search path.
4249736Smckusick  *		if symbolic link, massage name in buffer and continue
4349736Smckusick  *	}
4449736Smckusick  */
4552633Sheideman int
4652633Sheideman namei(ndp)
4749736Smckusick 	register struct nameidata *ndp;
4849736Smckusick {
4949736Smckusick 	register struct filedesc *fdp;	/* pointer to file descriptor state */
5049736Smckusick 	register char *cp;		/* pointer into pathname argument */
5149736Smckusick 	register struct vnode *dp;	/* the directory we are searching */
5249736Smckusick 	struct iovec aiov;		/* uio for reading symbolic links */
5349736Smckusick 	struct uio auio;
5449736Smckusick 	int error, linklen;
5552633Sheideman 	struct componentname *cnp = &ndp->ni_cnd;
5649736Smckusick 
5752633Sheideman 	ndp->ni_cnd.cn_cred = ndp->ni_cnd.cn_proc->p_ucred;
5852633Sheideman #ifdef DIAGNOSTIC
5952633Sheideman 	if (!cnp->cn_cred || !cnp->cn_proc)
6052633Sheideman 		panic ("namei: bad cred/proc");
6152633Sheideman 	if (cnp->cn_nameiop & (~OPMASK))
6252633Sheideman 		panic ("namei: nameiop contaminated with flags");
6352633Sheideman 	if (cnp->cn_flags & OPMASK)
6452633Sheideman 		panic ("namei: flags contaminated with nameiops");
6552633Sheideman #endif
6652633Sheideman 	fdp = cnp->cn_proc->p_fd;
6749736Smckusick 
6849736Smckusick 	/*
6949736Smckusick 	 * Get a buffer for the name to be translated, and copy the
7049736Smckusick 	 * name into the buffer.
7149736Smckusick 	 */
7252633Sheideman 	if ((cnp->cn_flags & HASBUF) == 0)
7352633Sheideman 		MALLOC(cnp->cn_pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
7449736Smckusick 	if (ndp->ni_segflg == UIO_SYSSPACE)
7552633Sheideman 		error = copystr(ndp->ni_dirp, cnp->cn_pnbuf,
7649736Smckusick 			    MAXPATHLEN, &ndp->ni_pathlen);
7749736Smckusick 	else
7852633Sheideman 		error = copyinstr(ndp->ni_dirp, cnp->cn_pnbuf,
7949736Smckusick 			    MAXPATHLEN, &ndp->ni_pathlen);
8049736Smckusick 	if (error) {
8152633Sheideman 		free(cnp->cn_pnbuf, M_NAMEI);
8249736Smckusick 		ndp->ni_vp = NULL;
8349736Smckusick 		return (error);
8449736Smckusick 	}
8549736Smckusick 	ndp->ni_loopcnt = 0;
8649736Smckusick #ifdef KTRACE
8752633Sheideman 	if (KTRPOINT(cnp->cn_proc, KTR_NAMEI))
8852633Sheideman 		ktrnamei(cnp->cn_proc->p_tracep, cnp->cn_pnbuf);
8949736Smckusick #endif
9049736Smckusick 
9149736Smckusick 	/*
9249736Smckusick 	 * Get starting point for the translation.
9349736Smckusick 	 */
9449736Smckusick 	if ((ndp->ni_rootdir = fdp->fd_rdir) == NULL)
9549736Smckusick 		ndp->ni_rootdir = rootdir;
9649736Smckusick 	dp = fdp->fd_cdir;
9749736Smckusick 	VREF(dp);
9849736Smckusick 	for (;;) {
9949736Smckusick 		/*
10049736Smckusick 		 * Check if root directory should replace current directory.
10149736Smckusick 		 * Done at start of translation and after symbolic link.
10249736Smckusick 		 */
10352633Sheideman 		cnp->cn_nameptr = cnp->cn_pnbuf;
10452633Sheideman 		if (*(cnp->cn_nameptr) == '/') {
10549736Smckusick 			vrele(dp);
10652633Sheideman 			while (*(cnp->cn_nameptr) == '/') {
10752633Sheideman 				cnp->cn_nameptr++;
10849736Smckusick 				ndp->ni_pathlen--;
10949736Smckusick 			}
11049736Smckusick 			dp = ndp->ni_rootdir;
11149736Smckusick 			VREF(dp);
11249736Smckusick 		}
11349736Smckusick 		ndp->ni_startdir = dp;
11452633Sheideman 		if (error = lookup(ndp)) {
11552633Sheideman 			FREE(cnp->cn_pnbuf, M_NAMEI);
11649736Smckusick 			return (error);
11749736Smckusick 		}
11849736Smckusick 		/*
11949736Smckusick 		 * Check for symbolic link
12049736Smckusick 		 */
12152633Sheideman 		if ((cnp->cn_flags & ISSYMLINK) == 0) {
12252633Sheideman 			if ((cnp->cn_flags & (SAVENAME | SAVESTART)) == 0)
12352633Sheideman 				FREE(cnp->cn_pnbuf, M_NAMEI);
12449736Smckusick 			else
12552633Sheideman 				cnp->cn_flags |= HASBUF;
12649736Smckusick 			return (0);
12749736Smckusick 		}
12852633Sheideman 		if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1)
12949736Smckusick 			VOP_UNLOCK(ndp->ni_dvp);
13049736Smckusick 		if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
13149736Smckusick 			error = ELOOP;
13249736Smckusick 			break;
13349736Smckusick 		}
13449736Smckusick 		if (ndp->ni_pathlen > 1)
13549736Smckusick 			MALLOC(cp, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
13649736Smckusick 		else
13752633Sheideman 			cp = cnp->cn_pnbuf;
13849736Smckusick 		aiov.iov_base = cp;
13949736Smckusick 		aiov.iov_len = MAXPATHLEN;
14049736Smckusick 		auio.uio_iov = &aiov;
14149736Smckusick 		auio.uio_iovcnt = 1;
14249736Smckusick 		auio.uio_offset = 0;
14349736Smckusick 		auio.uio_rw = UIO_READ;
14449736Smckusick 		auio.uio_segflg = UIO_SYSSPACE;
14549736Smckusick 		auio.uio_procp = (struct proc *)0;
14649736Smckusick 		auio.uio_resid = MAXPATHLEN;
14752633Sheideman 		if (error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred)) {
14849736Smckusick 			if (ndp->ni_pathlen > 1)
14949736Smckusick 				free(cp, M_NAMEI);
15049736Smckusick 			break;
15149736Smckusick 		}
15249736Smckusick 		linklen = MAXPATHLEN - auio.uio_resid;
15349736Smckusick 		if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
15449736Smckusick 			if (ndp->ni_pathlen > 1)
15549736Smckusick 				free(cp, M_NAMEI);
15649736Smckusick 			error = ENAMETOOLONG;
15749736Smckusick 			break;
15849736Smckusick 		}
15949736Smckusick 		if (ndp->ni_pathlen > 1) {
16049736Smckusick 			bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen);
16152633Sheideman 			FREE(cnp->cn_pnbuf, M_NAMEI);
16252633Sheideman 			cnp->cn_pnbuf = cp;
16349736Smckusick 		} else
16452633Sheideman 			cnp->cn_pnbuf[linklen] = '\0';
16549736Smckusick 		ndp->ni_pathlen += linklen;
16649736Smckusick 		vput(ndp->ni_vp);
16749736Smckusick 		dp = ndp->ni_dvp;
16849736Smckusick 	}
16952633Sheideman 	FREE(cnp->cn_pnbuf, M_NAMEI);
17049736Smckusick 	vrele(ndp->ni_dvp);
17149736Smckusick 	vput(ndp->ni_vp);
17249736Smckusick 	ndp->ni_vp = NULL;
17349736Smckusick 	return (error);
17449736Smckusick }
17549736Smckusick 
17649736Smckusick /*
17749736Smckusick  * Search a pathname.
17849736Smckusick  * This is a very central and rather complicated routine.
17949736Smckusick  *
18049736Smckusick  * The pathname is pointed to by ni_ptr and is of length ni_pathlen.
18149736Smckusick  * The starting directory is taken from ni_startdir. The pathname is
18249736Smckusick  * descended until done, or a symbolic link is encountered. The variable
18349736Smckusick  * ni_more is clear if the path is completed; it is set to one if a
18449736Smckusick  * symbolic link needing interpretation is encountered.
18549736Smckusick  *
18649736Smckusick  * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
18749736Smckusick  * whether the name is to be looked up, created, renamed, or deleted.
18849736Smckusick  * When CREATE, RENAME, or DELETE is specified, information usable in
18949736Smckusick  * creating, renaming, or deleting a directory entry may be calculated.
19049736Smckusick  * If flag has LOCKPARENT or'ed into it, the parent directory is returned
19149736Smckusick  * locked. If flag has WANTPARENT or'ed into it, the parent directory is
19249736Smckusick  * returned unlocked. Otherwise the parent directory is not returned. If
19349736Smckusick  * the target of the pathname exists and LOCKLEAF is or'ed into the flag
19449736Smckusick  * the target is returned locked, otherwise it is returned unlocked.
19549736Smckusick  * When creating or renaming and LOCKPARENT is specified, the target may not
19649736Smckusick  * be ".".  When deleting and LOCKPARENT is specified, the target may be ".".
19749736Smckusick  * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent vnode unlocked.
19849736Smckusick  *
19949736Smckusick  * Overall outline of lookup:
20049736Smckusick  *
2017534Sroot  * dirloop:
20249736Smckusick  *	identify next component of name at ndp->ni_ptr
2037534Sroot  *	handle degenerate case where name is null string
20449736Smckusick  *	if .. and crossing mount points and on mounted filesys, find parent
20549736Smckusick  *	call VOP_LOOKUP routine for next component name
20649736Smckusick  *	    directory vnode returned in ni_dvp, unlocked unless LOCKPARENT set
20749736Smckusick  *	    component vnode returned in ni_vp (if it exists), locked.
20849736Smckusick  *	if result vnode is mounted on and crossing mount points,
20949736Smckusick  *	    find mounted on vnode
2107534Sroot  *	if more components of name, do next level at dirloop
21149736Smckusick  *	return the answer in ni_vp, locked if LOCKLEAF set
21249736Smckusick  *	    if LOCKPARENT set, return locked parent in ni_dvp
21349736Smckusick  *	    if WANTPARENT set, return unlocked parent in ni_dvp
21430Sbill  */
21552633Sheideman int
21652633Sheideman lookup(ndp)
21716688Smckusick 	register struct nameidata *ndp;
21830Sbill {
2197534Sroot 	register char *cp;		/* pointer into pathname argument */
22037715Smckusick 	register struct vnode *dp = 0;	/* the directory we are searching */
22137715Smckusick 	struct vnode *tdp;		/* saved dp */
22237715Smckusick 	struct mount *mp;		/* mount table entry */
22318109Smckusick 	int docache;			/* == 0 do not cache last component */
22437715Smckusick 	int wantparent;			/* 1 => wantparent or lockparent flag */
22552181Smckusick 	int rdonly;			/* lookup read-only flag bit */
22637715Smckusick 	int error = 0;
22752230Sheideman 	struct componentname *cnp = &ndp->ni_cnd;
22830Sbill 
22941337Smckusick 	/*
23041337Smckusick 	 * Setup: break out flag bits into variables.
23141337Smckusick 	 */
23252633Sheideman 	wantparent = cnp->cn_flags & (LOCKPARENT | WANTPARENT);
23352230Sheideman 	docache = (cnp->cn_flags & NOCACHE) ^ NOCACHE;
23452633Sheideman 	if (cnp->cn_nameiop == DELETE ||
23552633Sheideman 	    (wantparent && cnp->cn_nameiop != CREATE))
23615798Smckusick 		docache = 0;
23752230Sheideman 	rdonly = cnp->cn_flags & RDONLY;
23849736Smckusick 	ndp->ni_dvp = NULL;
23952230Sheideman 	cnp->cn_flags &= ~ISSYMLINK;
24049736Smckusick 	dp = ndp->ni_startdir;
24149736Smckusick 	ndp->ni_startdir = NULLVP;
24237715Smckusick 	VOP_LOCK(dp);
2437534Sroot 
2446571Smckusic dirloop:
24530Sbill 	/*
24649736Smckusick 	 * Search a new directory.
24749736Smckusick 	 *
24852230Sheideman 	 * The cn_hash value is for use by vfs_cache.
24949736Smckusick 	 * The last component of the filename is left accessible via
25052230Sheideman 	 * cnp->cn_nameptr for callers that need the name. Callers needing
25149736Smckusick 	 * the name set the SAVENAME flag. When done, they assume
25249736Smckusick 	 * responsibility for freeing the pathname buffer.
2537534Sroot 	 */
25452230Sheideman 	cnp->cn_hash = 0;
25552230Sheideman 	for (cp = cnp->cn_nameptr; *cp != 0 && *cp != '/'; cp++)
25652230Sheideman 		cnp->cn_hash += (unsigned char)*cp;
25752230Sheideman 	cnp->cn_namelen = cp - cnp->cn_nameptr;
25852230Sheideman 	if (cnp->cn_namelen >= NAME_MAX) {
25949736Smckusick 		error = ENAMETOOLONG;
26049736Smckusick 		goto bad;
26149736Smckusick 	}
26237743Smckusick #ifdef NAMEI_DIAGNOSTIC
26349736Smckusick 	{ char c = *cp;
26449736Smckusick 	*cp = '\0';
26552230Sheideman 	printf("{%s}: ", cnp->cn_nameptr);
26649736Smckusick 	*cp = c; }
26737743Smckusick #endif
26852230Sheideman 	ndp->ni_pathlen -= cnp->cn_namelen;
26949736Smckusick 	ndp->ni_next = cp;
27052230Sheideman 	cnp->cn_flags |= MAKEENTRY;
27118109Smckusick 	if (*cp == '\0' && docache == 0)
27252230Sheideman 		cnp->cn_flags &= ~MAKEENTRY;
27352230Sheideman 	if (cnp->cn_namelen == 2 &&
27452230Sheideman 			cnp->cn_nameptr[1] == '.' && cnp->cn_nameptr[0] == '.')
27552230Sheideman 		cnp->cn_flags |= ISDOTDOT;
27652230Sheideman 	else cnp->cn_flags &= ~ISDOTDOT;
27752230Sheideman 	if (*ndp->ni_next == 0)
27852230Sheideman 		cnp->cn_flags |= ISLASTCN;
27952230Sheideman 	else cnp->cn_flags &= ~ISLASTCN;
2807534Sroot 
28152230Sheideman 
2827534Sroot 	/*
2837534Sroot 	 * Check for degenerate name (e.g. / or "")
2847534Sroot 	 * which is a way of talking about a directory,
2857534Sroot 	 * e.g. like "/." or ".".
2867534Sroot 	 */
28752230Sheideman 	if (cnp->cn_nameptr[0] == '\0') {
28852230Sheideman 		if (cnp->cn_nameiop != LOOKUP || wantparent) {
28937715Smckusick 			error = EISDIR;
2907534Sroot 			goto bad;
2915972Swnj 		}
29249736Smckusick 		if (dp->v_type != VDIR) {
29349736Smckusick 			error = ENOTDIR;
29449736Smckusick 			goto bad;
29549736Smckusick 		}
29652230Sheideman 		if (!(cnp->cn_flags & LOCKLEAF))
29737715Smckusick 			VOP_UNLOCK(dp);
29837715Smckusick 		ndp->ni_vp = dp;
29952230Sheideman 		if (cnp->cn_flags & SAVESTART)
30049736Smckusick 			panic("lookup: SAVESTART");
30152633Sheideman 		return (0);
3025972Swnj 	}
3037534Sroot 
3046571Smckusic 	/*
30537715Smckusick 	 * Handle "..": two special cases.
30637715Smckusick 	 * 1. If at root directory (e.g. after chroot)
30737715Smckusick 	 *    then ignore it so can't get out.
30837715Smckusick 	 * 2. If this vnode is the root of a mounted
30949736Smckusick 	 *    filesystem, then replace it with the
31037715Smckusick 	 *    vnode which was mounted on so we take the
31137715Smckusick 	 *    .. in the other file system.
31236547Smckusick 	 */
31352230Sheideman 	if (cnp->cn_flags & ISDOTDOT) {
31436547Smckusick 		for (;;) {
31549736Smckusick 			if (dp == ndp->ni_rootdir) {
31637715Smckusick 				ndp->ni_dvp = dp;
31738390Smckusick 				ndp->ni_vp = dp;
31838347Smckusick 				VREF(dp);
31937715Smckusick 				goto nextname;
32036547Smckusick 			}
32141337Smckusick 			if ((dp->v_flag & VROOT) == 0 ||
32252230Sheideman 			    (cnp->cn_flags & NOCROSSMOUNT))
32336547Smckusick 				break;
32437715Smckusick 			tdp = dp;
32541400Smckusick 			dp = dp->v_mount->mnt_vnodecovered;
32637715Smckusick 			vput(tdp);
32738390Smckusick 			VREF(dp);
32837715Smckusick 			VOP_LOCK(dp);
32936547Smckusick 		}
33036547Smckusick 	}
33136547Smckusick 
33236547Smckusick 	/*
33315798Smckusick 	 * We now have a segment name to search for, and a directory to search.
33415798Smckusick 	 */
33552230Sheideman 	ndp->ni_dvp = dp;
33652230Sheideman 	if (error = VOP_LOOKUP(dp, &ndp->ni_vp, cnp)) {
33749736Smckusick #ifdef DIAGNOSTIC
33837715Smckusick 		if (ndp->ni_vp != NULL)
33937715Smckusick 			panic("leaf should be empty");
34049736Smckusick #endif
34137582Smarc #ifdef NAMEI_DIAGNOSTIC
34237715Smckusick 		printf("not found\n");
34337582Smarc #endif
34452230Sheideman 		if (cnp->cn_nameiop == LOOKUP || cnp->cn_nameiop == DELETE ||
34538581Smckusick 		    error != ENOENT || *cp != 0)
34638581Smckusick 			goto bad;
3475972Swnj 		/*
34837715Smckusick 		 * If creating and at end of pathname, then can consider
34937715Smckusick 		 * allowing file to be created.
3505972Swnj 		 */
35152181Smckusick 		if (rdonly || (ndp->ni_dvp->v_mount->mnt_flag & MNT_RDONLY)) {
35237715Smckusick 			error = EROFS;
3537534Sroot 			goto bad;
35438581Smckusick 		}
3555972Swnj 		/*
35637715Smckusick 		 * We return with ni_vp NULL to indicate that the entry
35737715Smckusick 		 * doesn't currently exist, leaving a pointer to the
35837715Smckusick 		 * (possibly locked) directory inode in ndp->ni_dvp.
3595972Swnj 		 */
36052230Sheideman 		if (cnp->cn_flags & SAVESTART) {
36149736Smckusick 			ndp->ni_startdir = ndp->ni_dvp;
36249736Smckusick 			VREF(ndp->ni_startdir);
36349736Smckusick 		}
36452633Sheideman 		return (0);
3657534Sroot 	}
36637582Smarc #ifdef NAMEI_DIAGNOSTIC
36737715Smckusick 	printf("found\n");
36837582Smarc #endif
3697534Sroot 
37049736Smckusick 	dp = ndp->ni_vp;
3717534Sroot 	/*
37237715Smckusick 	 * Check for symbolic link
3737534Sroot 	 */
37437715Smckusick 	if ((dp->v_type == VLNK) &&
37552230Sheideman 	    ((cnp->cn_flags & FOLLOW) || *ndp->ni_next == '/')) {
37652230Sheideman 		cnp->cn_flags |= ISSYMLINK;
37752633Sheideman 		return (0);
37815798Smckusick 	}
37915798Smckusick 
3807534Sroot 	/*
38137715Smckusick 	 * Check to see if the vnode has been mounted on;
38237715Smckusick 	 * if so find the root of the mounted file system.
3837534Sroot 	 */
38441337Smckusick 	while (dp->v_type == VDIR && (mp = dp->v_mountedhere) &&
38552230Sheideman 	       (cnp->cn_flags & NOCROSSMOUNT) == 0) {
38652116Smarc 		if (mp->mnt_flag & MNT_MLOCK) {
38741400Smckusick 			mp->mnt_flag |= MNT_MWAIT;
38837715Smckusick 			sleep((caddr_t)mp, PVFS);
389*52634Smckusick 			continue;
39021014Smckusick 		}
39149736Smckusick 		if (error = VFS_ROOT(dp->v_mountedhere, &tdp))
3927534Sroot 			goto bad2;
39337715Smckusick 		vput(dp);
39437715Smckusick 		ndp->ni_vp = dp = tdp;
39530Sbill 	}
3967534Sroot 
39737715Smckusick nextname:
39830Sbill 	/*
3997534Sroot 	 * Not a symbolic link.  If more pathname,
4007534Sroot 	 * continue at next component, else return.
40130Sbill 	 */
40249736Smckusick 	if (*ndp->ni_next == '/') {
40352230Sheideman 		cnp->cn_nameptr = ndp->ni_next;
40452230Sheideman 		while (*cnp->cn_nameptr == '/') {
40552230Sheideman 			cnp->cn_nameptr++;
40637715Smckusick 			ndp->ni_pathlen--;
40737715Smckusick 		}
40837715Smckusick 		vrele(ndp->ni_dvp);
4097534Sroot 		goto dirloop;
41030Sbill 	}
4117534Sroot 	/*
41238400Smckusick 	 * Check for read-only file systems.
4137534Sroot 	 */
41452230Sheideman 	if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) {
41538267Smckusick 		/*
41638400Smckusick 		 * Disallow directory write attempts on read-only
41738400Smckusick 		 * file systems.
41838267Smckusick 		 */
41952181Smckusick 		if (rdonly || (dp->v_mount->mnt_flag & MNT_RDONLY) ||
42052181Smckusick 		    (wantparent &&
42152181Smckusick 		     (ndp->ni_dvp->v_mount->mnt_flag & MNT_RDONLY))) {
42238267Smckusick 			error = EROFS;
42338267Smckusick 			goto bad2;
42438267Smckusick 		}
42538267Smckusick 	}
42652230Sheideman 	if (cnp->cn_flags & SAVESTART) {
42749736Smckusick 		ndp->ni_startdir = ndp->ni_dvp;
42849736Smckusick 		VREF(ndp->ni_startdir);
42949736Smckusick 	}
43037715Smckusick 	if (!wantparent)
43137715Smckusick 		vrele(ndp->ni_dvp);
43252230Sheideman 	if ((cnp->cn_flags & LOCKLEAF) == 0)
43337715Smckusick 		VOP_UNLOCK(dp);
43452633Sheideman 	return (0);
4357534Sroot 
43637715Smckusick bad2:
43752230Sheideman 	if ((cnp->cn_flags & LOCKPARENT) && *ndp->ni_next == '\0')
43838001Smckusick 		VOP_UNLOCK(ndp->ni_dvp);
43937715Smckusick 	vrele(ndp->ni_dvp);
44037715Smckusick bad:
44137715Smckusick 	vput(dp);
44237715Smckusick 	ndp->ni_vp = NULL;
44352633Sheideman 	return (error);
4445972Swnj }
44552230Sheideman 
44652230Sheideman 
447