xref: /csrg-svn/sys/kern/vfs_lookup.c (revision 66128)
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*66128Spendry  *	@(#)vfs_lookup.c	8.4 (Berkeley) 02/16/94
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
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;
6149736Smckusick 
6252633Sheideman 	ndp->ni_cnd.cn_cred = ndp->ni_cnd.cn_proc->p_ucred;
6352633Sheideman #ifdef DIAGNOSTIC
6452633Sheideman 	if (!cnp->cn_cred || !cnp->cn_proc)
6552633Sheideman 		panic ("namei: bad cred/proc");
6652633Sheideman 	if (cnp->cn_nameiop & (~OPMASK))
6752633Sheideman 		panic ("namei: nameiop contaminated with flags");
6852633Sheideman 	if (cnp->cn_flags & OPMASK)
6952633Sheideman 		panic ("namei: flags contaminated with nameiops");
7052633Sheideman #endif
7152633Sheideman 	fdp = cnp->cn_proc->p_fd;
7249736Smckusick 
7349736Smckusick 	/*
7449736Smckusick 	 * Get a buffer for the name to be translated, and copy the
7549736Smckusick 	 * name into the buffer.
7649736Smckusick 	 */
7752633Sheideman 	if ((cnp->cn_flags & HASBUF) == 0)
7852633Sheideman 		MALLOC(cnp->cn_pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
7949736Smckusick 	if (ndp->ni_segflg == UIO_SYSSPACE)
8052633Sheideman 		error = copystr(ndp->ni_dirp, cnp->cn_pnbuf,
8149736Smckusick 			    MAXPATHLEN, &ndp->ni_pathlen);
8249736Smckusick 	else
8352633Sheideman 		error = copyinstr(ndp->ni_dirp, cnp->cn_pnbuf,
8449736Smckusick 			    MAXPATHLEN, &ndp->ni_pathlen);
8549736Smckusick 	if (error) {
8652633Sheideman 		free(cnp->cn_pnbuf, M_NAMEI);
8749736Smckusick 		ndp->ni_vp = NULL;
8849736Smckusick 		return (error);
8949736Smckusick 	}
9049736Smckusick 	ndp->ni_loopcnt = 0;
9149736Smckusick #ifdef KTRACE
9252633Sheideman 	if (KTRPOINT(cnp->cn_proc, KTR_NAMEI))
9352633Sheideman 		ktrnamei(cnp->cn_proc->p_tracep, cnp->cn_pnbuf);
9449736Smckusick #endif
9549736Smckusick 
9649736Smckusick 	/*
9749736Smckusick 	 * Get starting point for the translation.
9849736Smckusick 	 */
9949736Smckusick 	if ((ndp->ni_rootdir = fdp->fd_rdir) == NULL)
10064414Sbostic 		ndp->ni_rootdir = rootvnode;
10149736Smckusick 	dp = fdp->fd_cdir;
10249736Smckusick 	VREF(dp);
10349736Smckusick 	for (;;) {
10449736Smckusick 		/*
10549736Smckusick 		 * Check if root directory should replace current directory.
10649736Smckusick 		 * Done at start of translation and after symbolic link.
10749736Smckusick 		 */
10852633Sheideman 		cnp->cn_nameptr = cnp->cn_pnbuf;
10952633Sheideman 		if (*(cnp->cn_nameptr) == '/') {
11049736Smckusick 			vrele(dp);
11152633Sheideman 			while (*(cnp->cn_nameptr) == '/') {
11252633Sheideman 				cnp->cn_nameptr++;
11349736Smckusick 				ndp->ni_pathlen--;
11449736Smckusick 			}
11549736Smckusick 			dp = ndp->ni_rootdir;
11649736Smckusick 			VREF(dp);
11749736Smckusick 		}
11849736Smckusick 		ndp->ni_startdir = dp;
11952633Sheideman 		if (error = lookup(ndp)) {
12052633Sheideman 			FREE(cnp->cn_pnbuf, M_NAMEI);
12149736Smckusick 			return (error);
12249736Smckusick 		}
12349736Smckusick 		/*
12449736Smckusick 		 * Check for symbolic link
12549736Smckusick 		 */
12652633Sheideman 		if ((cnp->cn_flags & ISSYMLINK) == 0) {
12752633Sheideman 			if ((cnp->cn_flags & (SAVENAME | SAVESTART)) == 0)
12852633Sheideman 				FREE(cnp->cn_pnbuf, M_NAMEI);
12949736Smckusick 			else
13052633Sheideman 				cnp->cn_flags |= HASBUF;
13149736Smckusick 			return (0);
13249736Smckusick 		}
13352633Sheideman 		if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1)
13449736Smckusick 			VOP_UNLOCK(ndp->ni_dvp);
13549736Smckusick 		if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
13649736Smckusick 			error = ELOOP;
13749736Smckusick 			break;
13849736Smckusick 		}
13949736Smckusick 		if (ndp->ni_pathlen > 1)
14049736Smckusick 			MALLOC(cp, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
14149736Smckusick 		else
14252633Sheideman 			cp = cnp->cn_pnbuf;
14349736Smckusick 		aiov.iov_base = cp;
14449736Smckusick 		aiov.iov_len = MAXPATHLEN;
14549736Smckusick 		auio.uio_iov = &aiov;
14649736Smckusick 		auio.uio_iovcnt = 1;
14749736Smckusick 		auio.uio_offset = 0;
14849736Smckusick 		auio.uio_rw = UIO_READ;
14949736Smckusick 		auio.uio_segflg = UIO_SYSSPACE;
15049736Smckusick 		auio.uio_procp = (struct proc *)0;
15149736Smckusick 		auio.uio_resid = MAXPATHLEN;
15252633Sheideman 		if (error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred)) {
15349736Smckusick 			if (ndp->ni_pathlen > 1)
15449736Smckusick 				free(cp, M_NAMEI);
15549736Smckusick 			break;
15649736Smckusick 		}
15749736Smckusick 		linklen = MAXPATHLEN - auio.uio_resid;
15849736Smckusick 		if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
15949736Smckusick 			if (ndp->ni_pathlen > 1)
16049736Smckusick 				free(cp, M_NAMEI);
16149736Smckusick 			error = ENAMETOOLONG;
16249736Smckusick 			break;
16349736Smckusick 		}
16449736Smckusick 		if (ndp->ni_pathlen > 1) {
16549736Smckusick 			bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen);
16652633Sheideman 			FREE(cnp->cn_pnbuf, M_NAMEI);
16752633Sheideman 			cnp->cn_pnbuf = cp;
16849736Smckusick 		} else
16952633Sheideman 			cnp->cn_pnbuf[linklen] = '\0';
17049736Smckusick 		ndp->ni_pathlen += linklen;
17149736Smckusick 		vput(ndp->ni_vp);
17249736Smckusick 		dp = ndp->ni_dvp;
17349736Smckusick 	}
17452633Sheideman 	FREE(cnp->cn_pnbuf, M_NAMEI);
17549736Smckusick 	vrele(ndp->ni_dvp);
17649736Smckusick 	vput(ndp->ni_vp);
17749736Smckusick 	ndp->ni_vp = NULL;
17849736Smckusick 	return (error);
17949736Smckusick }
18049736Smckusick 
18149736Smckusick /*
18249736Smckusick  * Search a pathname.
18349736Smckusick  * This is a very central and rather complicated routine.
18449736Smckusick  *
18549736Smckusick  * The pathname is pointed to by ni_ptr and is of length ni_pathlen.
18649736Smckusick  * The starting directory is taken from ni_startdir. The pathname is
18749736Smckusick  * descended until done, or a symbolic link is encountered. The variable
18849736Smckusick  * ni_more is clear if the path is completed; it is set to one if a
18949736Smckusick  * symbolic link needing interpretation is encountered.
19049736Smckusick  *
19149736Smckusick  * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
19249736Smckusick  * whether the name is to be looked up, created, renamed, or deleted.
19349736Smckusick  * When CREATE, RENAME, or DELETE is specified, information usable in
19449736Smckusick  * creating, renaming, or deleting a directory entry may be calculated.
19549736Smckusick  * If flag has LOCKPARENT or'ed into it, the parent directory is returned
19649736Smckusick  * locked. If flag has WANTPARENT or'ed into it, the parent directory is
19749736Smckusick  * returned unlocked. Otherwise the parent directory is not returned. If
19849736Smckusick  * the target of the pathname exists and LOCKLEAF is or'ed into the flag
19949736Smckusick  * the target is returned locked, otherwise it is returned unlocked.
20049736Smckusick  * When creating or renaming and LOCKPARENT is specified, the target may not
20149736Smckusick  * be ".".  When deleting and LOCKPARENT is specified, the target may be ".".
20249736Smckusick  *
20349736Smckusick  * Overall outline of lookup:
20449736Smckusick  *
2057534Sroot  * dirloop:
20649736Smckusick  *	identify next component of name at ndp->ni_ptr
2077534Sroot  *	handle degenerate case where name is null string
20849736Smckusick  *	if .. and crossing mount points and on mounted filesys, find parent
20949736Smckusick  *	call VOP_LOOKUP routine for next component name
21049736Smckusick  *	    directory vnode returned in ni_dvp, unlocked unless LOCKPARENT set
21149736Smckusick  *	    component vnode returned in ni_vp (if it exists), locked.
21249736Smckusick  *	if result vnode is mounted on and crossing mount points,
21349736Smckusick  *	    find mounted on vnode
2147534Sroot  *	if more components of name, do next level at dirloop
21549736Smckusick  *	return the answer in ni_vp, locked if LOCKLEAF set
21649736Smckusick  *	    if LOCKPARENT set, return locked parent in ni_dvp
21749736Smckusick  *	    if WANTPARENT set, return unlocked parent in ni_dvp
21830Sbill  */
21952633Sheideman int
22052633Sheideman lookup(ndp)
22116688Smckusick 	register struct nameidata *ndp;
22230Sbill {
2237534Sroot 	register char *cp;		/* pointer into pathname argument */
22437715Smckusick 	register struct vnode *dp = 0;	/* the directory we are searching */
22537715Smckusick 	struct vnode *tdp;		/* saved dp */
22637715Smckusick 	struct mount *mp;		/* mount table entry */
22718109Smckusick 	int docache;			/* == 0 do not cache last component */
22837715Smckusick 	int wantparent;			/* 1 => wantparent or lockparent flag */
22952181Smckusick 	int rdonly;			/* lookup read-only flag bit */
23037715Smckusick 	int error = 0;
23152230Sheideman 	struct componentname *cnp = &ndp->ni_cnd;
23230Sbill 
23341337Smckusick 	/*
23441337Smckusick 	 * Setup: break out flag bits into variables.
23541337Smckusick 	 */
23652633Sheideman 	wantparent = cnp->cn_flags & (LOCKPARENT | WANTPARENT);
23752230Sheideman 	docache = (cnp->cn_flags & NOCACHE) ^ NOCACHE;
23852633Sheideman 	if (cnp->cn_nameiop == DELETE ||
23952633Sheideman 	    (wantparent && cnp->cn_nameiop != CREATE))
24015798Smckusick 		docache = 0;
24152230Sheideman 	rdonly = cnp->cn_flags & RDONLY;
24249736Smckusick 	ndp->ni_dvp = NULL;
24352230Sheideman 	cnp->cn_flags &= ~ISSYMLINK;
24449736Smckusick 	dp = ndp->ni_startdir;
24549736Smckusick 	ndp->ni_startdir = NULLVP;
24637715Smckusick 	VOP_LOCK(dp);
2477534Sroot 
2486571Smckusic dirloop:
24930Sbill 	/*
25049736Smckusick 	 * Search a new directory.
25149736Smckusick 	 *
25252230Sheideman 	 * The cn_hash value is for use by vfs_cache.
25349736Smckusick 	 * The last component of the filename is left accessible via
25452230Sheideman 	 * cnp->cn_nameptr for callers that need the name. Callers needing
25549736Smckusick 	 * the name set the SAVENAME flag. When done, they assume
25649736Smckusick 	 * responsibility for freeing the pathname buffer.
2577534Sroot 	 */
25855091Spendry 	cnp->cn_consume = 0;
25952230Sheideman 	cnp->cn_hash = 0;
26052230Sheideman 	for (cp = cnp->cn_nameptr; *cp != 0 && *cp != '/'; cp++)
26152230Sheideman 		cnp->cn_hash += (unsigned char)*cp;
26252230Sheideman 	cnp->cn_namelen = cp - cnp->cn_nameptr;
26357653Smckusick 	if (cnp->cn_namelen > NAME_MAX) {
26449736Smckusick 		error = ENAMETOOLONG;
26549736Smckusick 		goto bad;
26649736Smckusick 	}
26737743Smckusick #ifdef NAMEI_DIAGNOSTIC
26849736Smckusick 	{ char c = *cp;
26949736Smckusick 	*cp = '\0';
27052230Sheideman 	printf("{%s}: ", cnp->cn_nameptr);
27149736Smckusick 	*cp = c; }
27237743Smckusick #endif
27352230Sheideman 	ndp->ni_pathlen -= cnp->cn_namelen;
27449736Smckusick 	ndp->ni_next = cp;
27552230Sheideman 	cnp->cn_flags |= MAKEENTRY;
27618109Smckusick 	if (*cp == '\0' && docache == 0)
27752230Sheideman 		cnp->cn_flags &= ~MAKEENTRY;
27852230Sheideman 	if (cnp->cn_namelen == 2 &&
27955181Smckusick 	    cnp->cn_nameptr[1] == '.' && cnp->cn_nameptr[0] == '.')
28052230Sheideman 		cnp->cn_flags |= ISDOTDOT;
28155181Smckusick 	else
28255181Smckusick 		cnp->cn_flags &= ~ISDOTDOT;
28352230Sheideman 	if (*ndp->ni_next == 0)
28452230Sheideman 		cnp->cn_flags |= ISLASTCN;
28555181Smckusick 	else
28655181Smckusick 		cnp->cn_flags &= ~ISLASTCN;
2877534Sroot 
28852230Sheideman 
2897534Sroot 	/*
2907534Sroot 	 * Check for degenerate name (e.g. / or "")
2917534Sroot 	 * which is a way of talking about a directory,
2927534Sroot 	 * e.g. like "/." or ".".
2937534Sroot 	 */
29452230Sheideman 	if (cnp->cn_nameptr[0] == '\0') {
29559371Smckusick 		if (cnp->cn_nameiop != LOOKUP) {
29637715Smckusick 			error = EISDIR;
2977534Sroot 			goto bad;
2985972Swnj 		}
29949736Smckusick 		if (dp->v_type != VDIR) {
30049736Smckusick 			error = ENOTDIR;
30149736Smckusick 			goto bad;
30249736Smckusick 		}
30359371Smckusick 		if (wantparent) {
30459371Smckusick 			ndp->ni_dvp = dp;
305*66128Spendry 			VREF(dp);
30659371Smckusick 		}
30759371Smckusick 		ndp->ni_vp = dp;
30859371Smckusick 		if (!(cnp->cn_flags & (LOCKPARENT | LOCKLEAF)))
30937715Smckusick 			VOP_UNLOCK(dp);
31052230Sheideman 		if (cnp->cn_flags & SAVESTART)
31149736Smckusick 			panic("lookup: SAVESTART");
31252633Sheideman 		return (0);
3135972Swnj 	}
3147534Sroot 
3156571Smckusic 	/*
31637715Smckusick 	 * Handle "..": two special cases.
31737715Smckusick 	 * 1. If at root directory (e.g. after chroot)
31856313Shibler 	 *    or at absolute root directory
31937715Smckusick 	 *    then ignore it so can't get out.
32037715Smckusick 	 * 2. If this vnode is the root of a mounted
32149736Smckusick 	 *    filesystem, then replace it with the
32237715Smckusick 	 *    vnode which was mounted on so we take the
32337715Smckusick 	 *    .. in the other file system.
32436547Smckusick 	 */
32552230Sheideman 	if (cnp->cn_flags & ISDOTDOT) {
32636547Smckusick 		for (;;) {
32764414Sbostic 			if (dp == ndp->ni_rootdir || dp == rootvnode) {
32837715Smckusick 				ndp->ni_dvp = dp;
32938390Smckusick 				ndp->ni_vp = dp;
33038347Smckusick 				VREF(dp);
33137715Smckusick 				goto nextname;
33236547Smckusick 			}
33341337Smckusick 			if ((dp->v_flag & VROOT) == 0 ||
33452230Sheideman 			    (cnp->cn_flags & NOCROSSMOUNT))
33536547Smckusick 				break;
33637715Smckusick 			tdp = dp;
33741400Smckusick 			dp = dp->v_mount->mnt_vnodecovered;
33837715Smckusick 			vput(tdp);
33938390Smckusick 			VREF(dp);
34037715Smckusick 			VOP_LOCK(dp);
34136547Smckusick 		}
34236547Smckusick 	}
34336547Smckusick 
34436547Smckusick 	/*
34515798Smckusick 	 * We now have a segment name to search for, and a directory to search.
34615798Smckusick 	 */
34755451Spendry unionlookup:
34852230Sheideman 	ndp->ni_dvp = dp;
34952230Sheideman 	if (error = VOP_LOOKUP(dp, &ndp->ni_vp, cnp)) {
35049736Smckusick #ifdef DIAGNOSTIC
35137715Smckusick 		if (ndp->ni_vp != NULL)
35237715Smckusick 			panic("leaf should be empty");
35349736Smckusick #endif
35437582Smarc #ifdef NAMEI_DIAGNOSTIC
35537715Smckusick 		printf("not found\n");
35637582Smarc #endif
35755451Spendry 		if ((error == ENOENT) &&
35855451Spendry 		    (dp->v_flag & VROOT) &&
35955451Spendry 		    (dp->v_mount->mnt_flag & MNT_UNION)) {
36055451Spendry 			tdp = dp;
36155451Spendry 			dp = dp->v_mount->mnt_vnodecovered;
36255451Spendry 			vput(tdp);
36355451Spendry 			VREF(dp);
36455451Spendry 			VOP_LOCK(dp);
36555451Spendry 			goto unionlookup;
36655451Spendry 		}
36755451Spendry 
36852798Smckusick 		if (error != EJUSTRETURN)
36938581Smckusick 			goto bad;
3705972Swnj 		/*
37137715Smckusick 		 * If creating and at end of pathname, then can consider
37237715Smckusick 		 * allowing file to be created.
3735972Swnj 		 */
37452181Smckusick 		if (rdonly || (ndp->ni_dvp->v_mount->mnt_flag & MNT_RDONLY)) {
37537715Smckusick 			error = EROFS;
3767534Sroot 			goto bad;
37738581Smckusick 		}
3785972Swnj 		/*
37937715Smckusick 		 * We return with ni_vp NULL to indicate that the entry
38037715Smckusick 		 * doesn't currently exist, leaving a pointer to the
38137715Smckusick 		 * (possibly locked) directory inode in ndp->ni_dvp.
3825972Swnj 		 */
38352230Sheideman 		if (cnp->cn_flags & SAVESTART) {
38449736Smckusick 			ndp->ni_startdir = ndp->ni_dvp;
38549736Smckusick 			VREF(ndp->ni_startdir);
38649736Smckusick 		}
38752633Sheideman 		return (0);
3887534Sroot 	}
38937582Smarc #ifdef NAMEI_DIAGNOSTIC
39037715Smckusick 	printf("found\n");
39137582Smarc #endif
3927534Sroot 
39355091Spendry 	/*
39455091Spendry 	 * Take into account any additional components consumed by
39555091Spendry 	 * the underlying filesystem.
39655091Spendry 	 */
39755091Spendry 	if (cnp->cn_consume > 0) {
39855091Spendry 		cnp->cn_nameptr += cnp->cn_consume;
39955091Spendry 		ndp->ni_next += cnp->cn_consume;
40055091Spendry 		ndp->ni_pathlen -= cnp->cn_consume;
40155091Spendry 		cnp->cn_consume = 0;
40255091Spendry 	}
40355091Spendry 
40449736Smckusick 	dp = ndp->ni_vp;
4057534Sroot 	/*
40637715Smckusick 	 * Check for symbolic link
4077534Sroot 	 */
40837715Smckusick 	if ((dp->v_type == VLNK) &&
40952230Sheideman 	    ((cnp->cn_flags & FOLLOW) || *ndp->ni_next == '/')) {
41052230Sheideman 		cnp->cn_flags |= ISSYMLINK;
41152633Sheideman 		return (0);
41215798Smckusick 	}
41315798Smckusick 
4147534Sroot 	/*
41537715Smckusick 	 * Check to see if the vnode has been mounted on;
41637715Smckusick 	 * if so find the root of the mounted file system.
4177534Sroot 	 */
41841337Smckusick 	while (dp->v_type == VDIR && (mp = dp->v_mountedhere) &&
41952230Sheideman 	       (cnp->cn_flags & NOCROSSMOUNT) == 0) {
42052116Smarc 		if (mp->mnt_flag & MNT_MLOCK) {
42141400Smckusick 			mp->mnt_flag |= MNT_MWAIT;
42237715Smckusick 			sleep((caddr_t)mp, PVFS);
42352634Smckusick 			continue;
42421014Smckusick 		}
42549736Smckusick 		if (error = VFS_ROOT(dp->v_mountedhere, &tdp))
4267534Sroot 			goto bad2;
42737715Smckusick 		vput(dp);
42837715Smckusick 		ndp->ni_vp = dp = tdp;
42930Sbill 	}
4307534Sroot 
43137715Smckusick nextname:
43230Sbill 	/*
4337534Sroot 	 * Not a symbolic link.  If more pathname,
4347534Sroot 	 * continue at next component, else return.
43530Sbill 	 */
43649736Smckusick 	if (*ndp->ni_next == '/') {
43752230Sheideman 		cnp->cn_nameptr = ndp->ni_next;
43852230Sheideman 		while (*cnp->cn_nameptr == '/') {
43952230Sheideman 			cnp->cn_nameptr++;
44037715Smckusick 			ndp->ni_pathlen--;
44137715Smckusick 		}
44237715Smckusick 		vrele(ndp->ni_dvp);
4437534Sroot 		goto dirloop;
44430Sbill 	}
4457534Sroot 	/*
44638400Smckusick 	 * Check for read-only file systems.
4477534Sroot 	 */
44852230Sheideman 	if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) {
44938267Smckusick 		/*
45038400Smckusick 		 * Disallow directory write attempts on read-only
45138400Smckusick 		 * file systems.
45238267Smckusick 		 */
45352181Smckusick 		if (rdonly || (dp->v_mount->mnt_flag & MNT_RDONLY) ||
45452181Smckusick 		    (wantparent &&
45552181Smckusick 		     (ndp->ni_dvp->v_mount->mnt_flag & MNT_RDONLY))) {
45638267Smckusick 			error = EROFS;
45738267Smckusick 			goto bad2;
45838267Smckusick 		}
45938267Smckusick 	}
46052230Sheideman 	if (cnp->cn_flags & SAVESTART) {
46149736Smckusick 		ndp->ni_startdir = ndp->ni_dvp;
46249736Smckusick 		VREF(ndp->ni_startdir);
46349736Smckusick 	}
46437715Smckusick 	if (!wantparent)
46537715Smckusick 		vrele(ndp->ni_dvp);
46652230Sheideman 	if ((cnp->cn_flags & LOCKLEAF) == 0)
46737715Smckusick 		VOP_UNLOCK(dp);
46852633Sheideman 	return (0);
4697534Sroot 
47037715Smckusick bad2:
47152230Sheideman 	if ((cnp->cn_flags & LOCKPARENT) && *ndp->ni_next == '\0')
47238001Smckusick 		VOP_UNLOCK(ndp->ni_dvp);
47337715Smckusick 	vrele(ndp->ni_dvp);
47437715Smckusick bad:
47537715Smckusick 	vput(dp);
47637715Smckusick 	ndp->ni_vp = NULL;
47752633Sheideman 	return (error);
4785972Swnj }
47952230Sheideman 
48052230Sheideman 
481