xref: /csrg-svn/sys/kern/vfs_lookup.c (revision 69736)
123401Smckusick /*
263180Sbostic  * Copyright (c) 1982, 1986, 1989, 1993
363180Sbostic  *	The Regents of the University of California.  All rights reserved.
465771Sbostic  * (c) UNIX System Laboratories, Inc.
565771Sbostic  * All or some portions of this file are derived from material licensed
665771Sbostic  * to the University of California by American Telephone and Telegraph
765771Sbostic  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
865771Sbostic  * the permission of UNIX System Laboratories, Inc.
923401Smckusick  *
1044457Sbostic  * %sccs.include.redist.c%
1137715Smckusick  *
12*69736Smckusick  *	@(#)vfs_lookup.c	8.10 (Berkeley) 05/27/95
1323401Smckusick  */
1430Sbill 
1556517Sbostic #include <sys/param.h>
1656517Sbostic #include <sys/syslimits.h>
1756517Sbostic #include <sys/time.h>
1856517Sbostic #include <sys/namei.h>
1956517Sbostic #include <sys/vnode.h>
2056517Sbostic #include <sys/mount.h>
2156517Sbostic #include <sys/errno.h>
2256517Sbostic #include <sys/malloc.h>
2356517Sbostic #include <sys/filedesc.h>
2456517Sbostic #include <sys/proc.h>
2537715Smckusick 
2637582Smarc #ifdef KTRACE
2756517Sbostic #include <sys/ktrace.h>
2837582Smarc #endif
2930Sbill 
3030Sbill /*
3127268Smckusick  * Convert a pathname into a pointer to a locked inode.
3230Sbill  *
3316688Smckusick  * The FOLLOW flag is set when symbolic links are to be followed
349166Ssam  * when they occur at the end of the name translation process.
3527268Smckusick  * Symbolic links are always followed for all other pathname
3627268Smckusick  * components other than the last.
379166Ssam  *
3827268Smckusick  * The segflg defines whether the name is to be copied from user
3927268Smckusick  * space or kernel space.
4027268Smckusick  *
4115798Smckusick  * Overall outline of namei:
4215798Smckusick  *
437534Sroot  *	copy in name
447534Sroot  *	get starting directory
4549736Smckusick  *	while (!done && !error) {
4649736Smckusick  *		call lookup to search path.
4749736Smckusick  *		if symbolic link, massage name in buffer and continue
4849736Smckusick  *	}
4949736Smckusick  */
5052633Sheideman int
namei(ndp)5152633Sheideman namei(ndp)
5249736Smckusick 	register struct nameidata *ndp;
5349736Smckusick {
5449736Smckusick 	register struct filedesc *fdp;	/* pointer to file descriptor state */
5549736Smckusick 	register char *cp;		/* pointer into pathname argument */
5649736Smckusick 	register struct vnode *dp;	/* the directory we are searching */
5749736Smckusick 	struct iovec aiov;		/* uio for reading symbolic links */
5849736Smckusick 	struct uio auio;
5949736Smckusick 	int error, linklen;
6052633Sheideman 	struct componentname *cnp = &ndp->ni_cnd;
6169409Smckusick 	struct proc *p = cnp->cn_proc;
6249736Smckusick 
6352633Sheideman 	ndp->ni_cnd.cn_cred = ndp->ni_cnd.cn_proc->p_ucred;
6452633Sheideman #ifdef DIAGNOSTIC
6552633Sheideman 	if (!cnp->cn_cred || !cnp->cn_proc)
6652633Sheideman 		panic ("namei: bad cred/proc");
6752633Sheideman 	if (cnp->cn_nameiop & (~OPMASK))
6852633Sheideman 		panic ("namei: nameiop contaminated with flags");
6952633Sheideman 	if (cnp->cn_flags & OPMASK)
7052633Sheideman 		panic ("namei: flags contaminated with nameiops");
7152633Sheideman #endif
7252633Sheideman 	fdp = cnp->cn_proc->p_fd;
7349736Smckusick 
7449736Smckusick 	/*
7549736Smckusick 	 * Get a buffer for the name to be translated, and copy the
7649736Smckusick 	 * name into the buffer.
7749736Smckusick 	 */
7852633Sheideman 	if ((cnp->cn_flags & HASBUF) == 0)
7952633Sheideman 		MALLOC(cnp->cn_pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
8049736Smckusick 	if (ndp->ni_segflg == UIO_SYSSPACE)
8152633Sheideman 		error = copystr(ndp->ni_dirp, cnp->cn_pnbuf,
8249736Smckusick 			    MAXPATHLEN, &ndp->ni_pathlen);
8349736Smckusick 	else
8452633Sheideman 		error = copyinstr(ndp->ni_dirp, cnp->cn_pnbuf,
8549736Smckusick 			    MAXPATHLEN, &ndp->ni_pathlen);
8649736Smckusick 	if (error) {
8752633Sheideman 		free(cnp->cn_pnbuf, M_NAMEI);
8849736Smckusick 		ndp->ni_vp = NULL;
8949736Smckusick 		return (error);
9049736Smckusick 	}
9149736Smckusick 	ndp->ni_loopcnt = 0;
9249736Smckusick #ifdef KTRACE
9352633Sheideman 	if (KTRPOINT(cnp->cn_proc, KTR_NAMEI))
9452633Sheideman 		ktrnamei(cnp->cn_proc->p_tracep, cnp->cn_pnbuf);
9549736Smckusick #endif
9649736Smckusick 
9749736Smckusick 	/*
9849736Smckusick 	 * Get starting point for the translation.
9949736Smckusick 	 */
10049736Smckusick 	if ((ndp->ni_rootdir = fdp->fd_rdir) == NULL)
10164414Sbostic 		ndp->ni_rootdir = rootvnode;
10249736Smckusick 	dp = fdp->fd_cdir;
10349736Smckusick 	VREF(dp);
10449736Smckusick 	for (;;) {
10549736Smckusick 		/*
10649736Smckusick 		 * Check if root directory should replace current directory.
10749736Smckusick 		 * Done at start of translation and after symbolic link.
10849736Smckusick 		 */
10952633Sheideman 		cnp->cn_nameptr = cnp->cn_pnbuf;
11052633Sheideman 		if (*(cnp->cn_nameptr) == '/') {
11149736Smckusick 			vrele(dp);
11252633Sheideman 			while (*(cnp->cn_nameptr) == '/') {
11352633Sheideman 				cnp->cn_nameptr++;
11449736Smckusick 				ndp->ni_pathlen--;
11549736Smckusick 			}
11649736Smckusick 			dp = ndp->ni_rootdir;
11749736Smckusick 			VREF(dp);
11849736Smckusick 		}
11949736Smckusick 		ndp->ni_startdir = dp;
12052633Sheideman 		if (error = lookup(ndp)) {
12152633Sheideman 			FREE(cnp->cn_pnbuf, M_NAMEI);
12249736Smckusick 			return (error);
12349736Smckusick 		}
12449736Smckusick 		/*
12549736Smckusick 		 * Check for symbolic link
12649736Smckusick 		 */
12752633Sheideman 		if ((cnp->cn_flags & ISSYMLINK) == 0) {
12852633Sheideman 			if ((cnp->cn_flags & (SAVENAME | SAVESTART)) == 0)
12952633Sheideman 				FREE(cnp->cn_pnbuf, M_NAMEI);
13049736Smckusick 			else
13152633Sheideman 				cnp->cn_flags |= HASBUF;
13249736Smckusick 			return (0);
13349736Smckusick 		}
13452633Sheideman 		if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1)
13569409Smckusick 			VOP_UNLOCK(ndp->ni_dvp, 0, p);
13649736Smckusick 		if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
13749736Smckusick 			error = ELOOP;
13849736Smckusick 			break;
13949736Smckusick 		}
14049736Smckusick 		if (ndp->ni_pathlen > 1)
14149736Smckusick 			MALLOC(cp, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
14249736Smckusick 		else
14352633Sheideman 			cp = cnp->cn_pnbuf;
14449736Smckusick 		aiov.iov_base = cp;
14549736Smckusick 		aiov.iov_len = MAXPATHLEN;
14649736Smckusick 		auio.uio_iov = &aiov;
14749736Smckusick 		auio.uio_iovcnt = 1;
14849736Smckusick 		auio.uio_offset = 0;
14949736Smckusick 		auio.uio_rw = UIO_READ;
15049736Smckusick 		auio.uio_segflg = UIO_SYSSPACE;
15149736Smckusick 		auio.uio_procp = (struct proc *)0;
15249736Smckusick 		auio.uio_resid = MAXPATHLEN;
15352633Sheideman 		if (error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred)) {
15449736Smckusick 			if (ndp->ni_pathlen > 1)
15549736Smckusick 				free(cp, M_NAMEI);
15649736Smckusick 			break;
15749736Smckusick 		}
15849736Smckusick 		linklen = MAXPATHLEN - auio.uio_resid;
15949736Smckusick 		if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
16049736Smckusick 			if (ndp->ni_pathlen > 1)
16149736Smckusick 				free(cp, M_NAMEI);
16249736Smckusick 			error = ENAMETOOLONG;
16349736Smckusick 			break;
16449736Smckusick 		}
16549736Smckusick 		if (ndp->ni_pathlen > 1) {
16649736Smckusick 			bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen);
16752633Sheideman 			FREE(cnp->cn_pnbuf, M_NAMEI);
16852633Sheideman 			cnp->cn_pnbuf = cp;
16949736Smckusick 		} else
17052633Sheideman 			cnp->cn_pnbuf[linklen] = '\0';
17149736Smckusick 		ndp->ni_pathlen += linklen;
17249736Smckusick 		vput(ndp->ni_vp);
17349736Smckusick 		dp = ndp->ni_dvp;
17449736Smckusick 	}
17552633Sheideman 	FREE(cnp->cn_pnbuf, M_NAMEI);
17649736Smckusick 	vrele(ndp->ni_dvp);
17749736Smckusick 	vput(ndp->ni_vp);
17849736Smckusick 	ndp->ni_vp = NULL;
17949736Smckusick 	return (error);
18049736Smckusick }
18149736Smckusick 
18249736Smckusick /*
18349736Smckusick  * Search a pathname.
18449736Smckusick  * This is a very central and rather complicated routine.
18549736Smckusick  *
18649736Smckusick  * The pathname is pointed to by ni_ptr and is of length ni_pathlen.
18749736Smckusick  * The starting directory is taken from ni_startdir. The pathname is
18849736Smckusick  * descended until done, or a symbolic link is encountered. The variable
18949736Smckusick  * ni_more is clear if the path is completed; it is set to one if a
19049736Smckusick  * symbolic link needing interpretation is encountered.
19149736Smckusick  *
19249736Smckusick  * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
19349736Smckusick  * whether the name is to be looked up, created, renamed, or deleted.
19449736Smckusick  * When CREATE, RENAME, or DELETE is specified, information usable in
19549736Smckusick  * creating, renaming, or deleting a directory entry may be calculated.
19649736Smckusick  * If flag has LOCKPARENT or'ed into it, the parent directory is returned
19749736Smckusick  * locked. If flag has WANTPARENT or'ed into it, the parent directory is
19849736Smckusick  * returned unlocked. Otherwise the parent directory is not returned. If
19949736Smckusick  * the target of the pathname exists and LOCKLEAF is or'ed into the flag
20049736Smckusick  * the target is returned locked, otherwise it is returned unlocked.
20149736Smckusick  * When creating or renaming and LOCKPARENT is specified, the target may not
20249736Smckusick  * be ".".  When deleting and LOCKPARENT is specified, the target may be ".".
20349736Smckusick  *
20449736Smckusick  * Overall outline of lookup:
20549736Smckusick  *
2067534Sroot  * dirloop:
20749736Smckusick  *	identify next component of name at ndp->ni_ptr
2087534Sroot  *	handle degenerate case where name is null string
20949736Smckusick  *	if .. and crossing mount points and on mounted filesys, find parent
21049736Smckusick  *	call VOP_LOOKUP routine for next component name
21149736Smckusick  *	    directory vnode returned in ni_dvp, unlocked unless LOCKPARENT set
21249736Smckusick  *	    component vnode returned in ni_vp (if it exists), locked.
21349736Smckusick  *	if result vnode is mounted on and crossing mount points,
21449736Smckusick  *	    find mounted on vnode
2157534Sroot  *	if more components of name, do next level at dirloop
21649736Smckusick  *	return the answer in ni_vp, locked if LOCKLEAF set
21749736Smckusick  *	    if LOCKPARENT set, return locked parent in ni_dvp
21849736Smckusick  *	    if WANTPARENT set, return unlocked parent in ni_dvp
21930Sbill  */
22052633Sheideman int
lookup(ndp)22152633Sheideman lookup(ndp)
22216688Smckusick 	register struct nameidata *ndp;
22330Sbill {
2247534Sroot 	register char *cp;		/* pointer into pathname argument */
22537715Smckusick 	register struct vnode *dp = 0;	/* the directory we are searching */
22637715Smckusick 	struct vnode *tdp;		/* saved dp */
22737715Smckusick 	struct mount *mp;		/* mount table entry */
22818109Smckusick 	int docache;			/* == 0 do not cache last component */
22937715Smckusick 	int wantparent;			/* 1 => wantparent or lockparent flag */
23052181Smckusick 	int rdonly;			/* lookup read-only flag bit */
23137715Smckusick 	int error = 0;
23252230Sheideman 	struct componentname *cnp = &ndp->ni_cnd;
23369409Smckusick 	struct proc *p = cnp->cn_proc;
23430Sbill 
23541337Smckusick 	/*
23641337Smckusick 	 * Setup: break out flag bits into variables.
23741337Smckusick 	 */
23852633Sheideman 	wantparent = cnp->cn_flags & (LOCKPARENT | WANTPARENT);
23952230Sheideman 	docache = (cnp->cn_flags & NOCACHE) ^ NOCACHE;
24052633Sheideman 	if (cnp->cn_nameiop == DELETE ||
24152633Sheideman 	    (wantparent && cnp->cn_nameiop != CREATE))
24215798Smckusick 		docache = 0;
24352230Sheideman 	rdonly = cnp->cn_flags & RDONLY;
24449736Smckusick 	ndp->ni_dvp = NULL;
24552230Sheideman 	cnp->cn_flags &= ~ISSYMLINK;
24649736Smckusick 	dp = ndp->ni_startdir;
24749736Smckusick 	ndp->ni_startdir = NULLVP;
24869409Smckusick 	vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, p);
2497534Sroot 
2506571Smckusic dirloop:
25130Sbill 	/*
25249736Smckusick 	 * Search a new directory.
25349736Smckusick 	 *
25452230Sheideman 	 * The cn_hash value is for use by vfs_cache.
25549736Smckusick 	 * The last component of the filename is left accessible via
25652230Sheideman 	 * cnp->cn_nameptr for callers that need the name. Callers needing
25749736Smckusick 	 * the name set the SAVENAME flag. When done, they assume
25849736Smckusick 	 * responsibility for freeing the pathname buffer.
2597534Sroot 	 */
26055091Spendry 	cnp->cn_consume = 0;
26152230Sheideman 	cnp->cn_hash = 0;
26252230Sheideman 	for (cp = cnp->cn_nameptr; *cp != 0 && *cp != '/'; cp++)
26352230Sheideman 		cnp->cn_hash += (unsigned char)*cp;
26452230Sheideman 	cnp->cn_namelen = cp - cnp->cn_nameptr;
26557653Smckusick 	if (cnp->cn_namelen > NAME_MAX) {
26649736Smckusick 		error = ENAMETOOLONG;
26749736Smckusick 		goto bad;
26849736Smckusick 	}
26937743Smckusick #ifdef NAMEI_DIAGNOSTIC
27049736Smckusick 	{ char c = *cp;
27149736Smckusick 	*cp = '\0';
27252230Sheideman 	printf("{%s}: ", cnp->cn_nameptr);
27349736Smckusick 	*cp = c; }
27437743Smckusick #endif
27552230Sheideman 	ndp->ni_pathlen -= cnp->cn_namelen;
27649736Smckusick 	ndp->ni_next = cp;
27752230Sheideman 	cnp->cn_flags |= MAKEENTRY;
27818109Smckusick 	if (*cp == '\0' && docache == 0)
27952230Sheideman 		cnp->cn_flags &= ~MAKEENTRY;
28052230Sheideman 	if (cnp->cn_namelen == 2 &&
28155181Smckusick 	    cnp->cn_nameptr[1] == '.' && cnp->cn_nameptr[0] == '.')
28252230Sheideman 		cnp->cn_flags |= ISDOTDOT;
28355181Smckusick 	else
28455181Smckusick 		cnp->cn_flags &= ~ISDOTDOT;
28552230Sheideman 	if (*ndp->ni_next == 0)
28652230Sheideman 		cnp->cn_flags |= ISLASTCN;
28755181Smckusick 	else
28855181Smckusick 		cnp->cn_flags &= ~ISLASTCN;
2897534Sroot 
29052230Sheideman 
2917534Sroot 	/*
2927534Sroot 	 * Check for degenerate name (e.g. / or "")
2937534Sroot 	 * which is a way of talking about a directory,
2947534Sroot 	 * e.g. like "/." or ".".
2957534Sroot 	 */
29652230Sheideman 	if (cnp->cn_nameptr[0] == '\0') {
29767207Smckusick 		if (dp->v_type != VDIR) {
29867207Smckusick 			error = ENOTDIR;
29967207Smckusick 			goto bad;
30067207Smckusick 		}
30159371Smckusick 		if (cnp->cn_nameiop != LOOKUP) {
30237715Smckusick 			error = EISDIR;
3037534Sroot 			goto bad;
3045972Swnj 		}
30559371Smckusick 		if (wantparent) {
30659371Smckusick 			ndp->ni_dvp = dp;
30766128Spendry 			VREF(dp);
30859371Smckusick 		}
30959371Smckusick 		ndp->ni_vp = dp;
31059371Smckusick 		if (!(cnp->cn_flags & (LOCKPARENT | LOCKLEAF)))
31169409Smckusick 			VOP_UNLOCK(dp, 0, p);
31252230Sheideman 		if (cnp->cn_flags & SAVESTART)
31349736Smckusick 			panic("lookup: SAVESTART");
31452633Sheideman 		return (0);
3155972Swnj 	}
3167534Sroot 
3176571Smckusic 	/*
31837715Smckusick 	 * Handle "..": two special cases.
31937715Smckusick 	 * 1. If at root directory (e.g. after chroot)
32056313Shibler 	 *    or at absolute root directory
32137715Smckusick 	 *    then ignore it so can't get out.
32237715Smckusick 	 * 2. If this vnode is the root of a mounted
32349736Smckusick 	 *    filesystem, then replace it with the
32437715Smckusick 	 *    vnode which was mounted on so we take the
32537715Smckusick 	 *    .. in the other file system.
32636547Smckusick 	 */
32752230Sheideman 	if (cnp->cn_flags & ISDOTDOT) {
32836547Smckusick 		for (;;) {
32964414Sbostic 			if (dp == ndp->ni_rootdir || dp == rootvnode) {
33037715Smckusick 				ndp->ni_dvp = dp;
33138390Smckusick 				ndp->ni_vp = dp;
33238347Smckusick 				VREF(dp);
33337715Smckusick 				goto nextname;
33436547Smckusick 			}
33541337Smckusick 			if ((dp->v_flag & VROOT) == 0 ||
33652230Sheideman 			    (cnp->cn_flags & NOCROSSMOUNT))
33736547Smckusick 				break;
33837715Smckusick 			tdp = dp;
33941400Smckusick 			dp = dp->v_mount->mnt_vnodecovered;
34037715Smckusick 			vput(tdp);
34138390Smckusick 			VREF(dp);
34269409Smckusick 			vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, p);
34336547Smckusick 		}
34436547Smckusick 	}
34536547Smckusick 
34636547Smckusick 	/*
34715798Smckusick 	 * We now have a segment name to search for, and a directory to search.
34815798Smckusick 	 */
34955451Spendry unionlookup:
35052230Sheideman 	ndp->ni_dvp = dp;
351*69736Smckusick 	ndp->ni_vp = NULL;
35252230Sheideman 	if (error = VOP_LOOKUP(dp, &ndp->ni_vp, cnp)) {
35349736Smckusick #ifdef DIAGNOSTIC
35437715Smckusick 		if (ndp->ni_vp != NULL)
35537715Smckusick 			panic("leaf should be empty");
35649736Smckusick #endif
35737582Smarc #ifdef NAMEI_DIAGNOSTIC
35837715Smckusick 		printf("not found\n");
35937582Smarc #endif
36055451Spendry 		if ((error == ENOENT) &&
36155451Spendry 		    (dp->v_flag & VROOT) &&
36255451Spendry 		    (dp->v_mount->mnt_flag & MNT_UNION)) {
36355451Spendry 			tdp = dp;
36455451Spendry 			dp = dp->v_mount->mnt_vnodecovered;
36555451Spendry 			vput(tdp);
36655451Spendry 			VREF(dp);
36769409Smckusick 			vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, p);
36855451Spendry 			goto unionlookup;
36955451Spendry 		}
37055451Spendry 
37152798Smckusick 		if (error != EJUSTRETURN)
37238581Smckusick 			goto bad;
3735972Swnj 		/*
37437715Smckusick 		 * If creating and at end of pathname, then can consider
37537715Smckusick 		 * allowing file to be created.
3765972Swnj 		 */
377*69736Smckusick 		if (rdonly) {
37837715Smckusick 			error = EROFS;
3797534Sroot 			goto bad;
38038581Smckusick 		}
3815972Swnj 		/*
38237715Smckusick 		 * We return with ni_vp NULL to indicate that the entry
38337715Smckusick 		 * doesn't currently exist, leaving a pointer to the
38437715Smckusick 		 * (possibly locked) directory inode in ndp->ni_dvp.
3855972Swnj 		 */
38652230Sheideman 		if (cnp->cn_flags & SAVESTART) {
38749736Smckusick 			ndp->ni_startdir = ndp->ni_dvp;
38849736Smckusick 			VREF(ndp->ni_startdir);
38949736Smckusick 		}
39052633Sheideman 		return (0);
3917534Sroot 	}
39237582Smarc #ifdef NAMEI_DIAGNOSTIC
39337715Smckusick 	printf("found\n");
39437582Smarc #endif
3957534Sroot 
39655091Spendry 	/*
39755091Spendry 	 * Take into account any additional components consumed by
39855091Spendry 	 * the underlying filesystem.
39955091Spendry 	 */
40055091Spendry 	if (cnp->cn_consume > 0) {
40155091Spendry 		cnp->cn_nameptr += cnp->cn_consume;
40255091Spendry 		ndp->ni_next += cnp->cn_consume;
40355091Spendry 		ndp->ni_pathlen -= cnp->cn_consume;
40455091Spendry 		cnp->cn_consume = 0;
40555091Spendry 	}
40655091Spendry 
40749736Smckusick 	dp = ndp->ni_vp;
4087534Sroot 	/*
40937715Smckusick 	 * Check to see if the vnode has been mounted on;
41037715Smckusick 	 * if so find the root of the mounted file system.
4117534Sroot 	 */
41241337Smckusick 	while (dp->v_type == VDIR && (mp = dp->v_mountedhere) &&
41352230Sheideman 	       (cnp->cn_flags & NOCROSSMOUNT) == 0) {
41469578Smckusick 		if (vfs_busy(mp, 0, 0, p))
41552634Smckusick 			continue;
41669578Smckusick 		error = VFS_ROOT(mp, &tdp);
41769578Smckusick 		vfs_unbusy(mp, p);
41869578Smckusick 		if (error)
4197534Sroot 			goto bad2;
42037715Smckusick 		vput(dp);
42137715Smckusick 		ndp->ni_vp = dp = tdp;
42230Sbill 	}
4237534Sroot 
42467959Smckusick 	/*
42567959Smckusick 	 * Check for symbolic link
42667959Smckusick 	 */
42767959Smckusick 	if ((dp->v_type == VLNK) &&
42867959Smckusick 	    ((cnp->cn_flags & FOLLOW) || *ndp->ni_next == '/')) {
42967959Smckusick 		cnp->cn_flags |= ISSYMLINK;
43067959Smckusick 		return (0);
43167959Smckusick 	}
43267959Smckusick 
43337715Smckusick nextname:
43430Sbill 	/*
4357534Sroot 	 * Not a symbolic link.  If more pathname,
4367534Sroot 	 * continue at next component, else return.
43730Sbill 	 */
43849736Smckusick 	if (*ndp->ni_next == '/') {
43952230Sheideman 		cnp->cn_nameptr = ndp->ni_next;
44052230Sheideman 		while (*cnp->cn_nameptr == '/') {
44152230Sheideman 			cnp->cn_nameptr++;
44237715Smckusick 			ndp->ni_pathlen--;
44337715Smckusick 		}
44437715Smckusick 		vrele(ndp->ni_dvp);
4457534Sroot 		goto dirloop;
44630Sbill 	}
4477534Sroot 	/*
448*69736Smckusick 	 * Disallow directory write attempts on read-only file systems.
4497534Sroot 	 */
450*69736Smckusick 	if (rdonly &&
451*69736Smckusick 	    (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) {
452*69736Smckusick 		error = EROFS;
453*69736Smckusick 		goto bad2;
45438267Smckusick 	}
45552230Sheideman 	if (cnp->cn_flags & SAVESTART) {
45649736Smckusick 		ndp->ni_startdir = ndp->ni_dvp;
45749736Smckusick 		VREF(ndp->ni_startdir);
45849736Smckusick 	}
45937715Smckusick 	if (!wantparent)
46037715Smckusick 		vrele(ndp->ni_dvp);
46152230Sheideman 	if ((cnp->cn_flags & LOCKLEAF) == 0)
46269409Smckusick 		VOP_UNLOCK(dp, 0, p);
46352633Sheideman 	return (0);
4647534Sroot 
46537715Smckusick bad2:
46652230Sheideman 	if ((cnp->cn_flags & LOCKPARENT) && *ndp->ni_next == '\0')
46769409Smckusick 		VOP_UNLOCK(ndp->ni_dvp, 0, p);
46837715Smckusick 	vrele(ndp->ni_dvp);
46937715Smckusick bad:
47037715Smckusick 	vput(dp);
47137715Smckusick 	ndp->ni_vp = NULL;
47252633Sheideman 	return (error);
4735972Swnj }
47452230Sheideman 
47569083Smckusick /*
47669083Smckusick  * relookup - lookup a path name component
47769083Smckusick  *    Used by lookup to re-aquire things.
47869083Smckusick  */
47969083Smckusick int
relookup(dvp,vpp,cnp)48069083Smckusick relookup(dvp, vpp, cnp)
48169083Smckusick 	struct vnode *dvp, **vpp;
48269083Smckusick 	struct componentname *cnp;
48369083Smckusick {
48469409Smckusick 	struct proc *p = cnp->cn_proc;
48569409Smckusick 	struct vnode *dp = 0;		/* the directory we are searching */
48669083Smckusick 	int docache;			/* == 0 do not cache last component */
48769083Smckusick 	int wantparent;			/* 1 => wantparent or lockparent flag */
48869083Smckusick 	int rdonly;			/* lookup read-only flag bit */
48969083Smckusick 	int error = 0;
49069083Smckusick #ifdef NAMEI_DIAGNOSTIC
49169083Smckusick 	int newhash;			/* DEBUG: check name hash */
49269083Smckusick 	char *cp;			/* DEBUG: check name ptr/len */
49369083Smckusick #endif
49452230Sheideman 
49569083Smckusick 	/*
49669083Smckusick 	 * Setup: break out flag bits into variables.
49769083Smckusick 	 */
49869083Smckusick 	wantparent = cnp->cn_flags & (LOCKPARENT|WANTPARENT);
49969083Smckusick 	docache = (cnp->cn_flags & NOCACHE) ^ NOCACHE;
50069083Smckusick 	if (cnp->cn_nameiop == DELETE ||
50169083Smckusick 	    (wantparent && cnp->cn_nameiop != CREATE))
50269083Smckusick 		docache = 0;
50369083Smckusick 	rdonly = cnp->cn_flags & RDONLY;
50469083Smckusick 	cnp->cn_flags &= ~ISSYMLINK;
50569083Smckusick 	dp = dvp;
50669409Smckusick 	vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, p);
50769083Smckusick 
50869083Smckusick /* dirloop: */
50969083Smckusick 	/*
51069083Smckusick 	 * Search a new directory.
51169083Smckusick 	 *
51269083Smckusick 	 * The cn_hash value is for use by vfs_cache.
51369083Smckusick 	 * The last component of the filename is left accessible via
51469083Smckusick 	 * cnp->cn_nameptr for callers that need the name. Callers needing
51569083Smckusick 	 * the name set the SAVENAME flag. When done, they assume
51669083Smckusick 	 * responsibility for freeing the pathname buffer.
51769083Smckusick 	 */
51869083Smckusick #ifdef NAMEI_DIAGNOSTIC
51969083Smckusick 	for (newhash = 0, cp = cnp->cn_nameptr; *cp != 0 && *cp != '/'; cp++)
52069083Smckusick 		newhash += (unsigned char)*cp;
52169083Smckusick 	if (newhash != cnp->cn_hash)
52269083Smckusick 		panic("relookup: bad hash");
52369083Smckusick 	if (cnp->cn_namelen != cp - cnp->cn_nameptr)
52469083Smckusick 		panic ("relookup: bad len");
52569083Smckusick 	if (*cp != 0)
52669083Smckusick 		panic("relookup: not last component");
52769083Smckusick 	printf("{%s}: ", cnp->cn_nameptr);
52869083Smckusick #endif
52969083Smckusick 
53069083Smckusick 	/*
53169083Smckusick 	 * Check for degenerate name (e.g. / or "")
53269083Smckusick 	 * which is a way of talking about a directory,
53369083Smckusick 	 * e.g. like "/." or ".".
53469083Smckusick 	 */
53569083Smckusick 	if (cnp->cn_nameptr[0] == '\0') {
53669083Smckusick 		if (cnp->cn_nameiop != LOOKUP || wantparent) {
53769083Smckusick 			error = EISDIR;
53869083Smckusick 			goto bad;
53969083Smckusick 		}
54069083Smckusick 		if (dp->v_type != VDIR) {
54169083Smckusick 			error = ENOTDIR;
54269083Smckusick 			goto bad;
54369083Smckusick 		}
54469083Smckusick 		if (!(cnp->cn_flags & LOCKLEAF))
54569409Smckusick 			VOP_UNLOCK(dp, 0, p);
54669083Smckusick 		*vpp = dp;
54769083Smckusick 		if (cnp->cn_flags & SAVESTART)
54869083Smckusick 			panic("lookup: SAVESTART");
54969083Smckusick 		return (0);
55069083Smckusick 	}
55169083Smckusick 
55269083Smckusick 	if (cnp->cn_flags & ISDOTDOT)
55369083Smckusick 		panic ("relookup: lookup on dot-dot");
55469083Smckusick 
55569083Smckusick 	/*
55669083Smckusick 	 * We now have a segment name to search for, and a directory to search.
55769083Smckusick 	 */
55869083Smckusick 	if (error = VOP_LOOKUP(dp, vpp, cnp)) {
55969083Smckusick #ifdef DIAGNOSTIC
56069083Smckusick 		if (*vpp != NULL)
56169083Smckusick 			panic("leaf should be empty");
56269083Smckusick #endif
56369083Smckusick 		if (error != EJUSTRETURN)
56469083Smckusick 			goto bad;
56569083Smckusick 		/*
56669083Smckusick 		 * If creating and at end of pathname, then can consider
56769083Smckusick 		 * allowing file to be created.
56869083Smckusick 		 */
569*69736Smckusick 		if (rdonly) {
57069083Smckusick 			error = EROFS;
57169083Smckusick 			goto bad;
57269083Smckusick 		}
57369083Smckusick 		/* ASSERT(dvp == ndp->ni_startdir) */
57469083Smckusick 		if (cnp->cn_flags & SAVESTART)
57569083Smckusick 			VREF(dvp);
57669083Smckusick 		/*
57769083Smckusick 		 * We return with ni_vp NULL to indicate that the entry
57869083Smckusick 		 * doesn't currently exist, leaving a pointer to the
57969083Smckusick 		 * (possibly locked) directory inode in ndp->ni_dvp.
58069083Smckusick 		 */
58169083Smckusick 		return (0);
58269083Smckusick 	}
58369083Smckusick 	dp = *vpp;
58469083Smckusick 
58569083Smckusick #ifdef DIAGNOSTIC
58669083Smckusick 	/*
58769083Smckusick 	 * Check for symbolic link
58869083Smckusick 	 */
58969083Smckusick 	if (dp->v_type == VLNK && (cnp->cn_flags & FOLLOW))
59069083Smckusick 		panic ("relookup: symlink found.\n");
59169083Smckusick #endif
59269083Smckusick 
59369083Smckusick 	/*
594*69736Smckusick 	 * Disallow directory write attempts on read-only file systems.
59569083Smckusick 	 */
596*69736Smckusick 	if (rdonly &&
597*69736Smckusick 	    (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) {
598*69736Smckusick 		error = EROFS;
599*69736Smckusick 		goto bad2;
60069083Smckusick 	}
60169083Smckusick 	/* ASSERT(dvp == ndp->ni_startdir) */
60269083Smckusick 	if (cnp->cn_flags & SAVESTART)
60369083Smckusick 		VREF(dvp);
60469083Smckusick 
60569083Smckusick 	if (!wantparent)
60669083Smckusick 		vrele(dvp);
60769083Smckusick 	if ((cnp->cn_flags & LOCKLEAF) == 0)
60869409Smckusick 		VOP_UNLOCK(dp, 0, p);
60969083Smckusick 	return (0);
61069083Smckusick 
61169083Smckusick bad2:
61269083Smckusick 	if ((cnp->cn_flags & LOCKPARENT) && (cnp->cn_flags & ISLASTCN))
61369409Smckusick 		VOP_UNLOCK(dvp, 0, p);
61469083Smckusick 	vrele(dvp);
61569083Smckusick bad:
61669083Smckusick 	vput(dp);
61769083Smckusick 	*vpp = NULL;
61869083Smckusick 	return (error);
61969083Smckusick }
620