xref: /csrg-svn/sys/kern/vfs_lookup.c (revision 56517)
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*56517Sbostic  *	@(#)vfs_lookup.c	7.45 (Berkeley) 10/11/92
823401Smckusick  */
930Sbill 
10*56517Sbostic #include <sys/param.h>
11*56517Sbostic #include <sys/syslimits.h>
12*56517Sbostic #include <sys/time.h>
13*56517Sbostic #include <sys/namei.h>
14*56517Sbostic #include <sys/vnode.h>
15*56517Sbostic #include <sys/mount.h>
16*56517Sbostic #include <sys/errno.h>
17*56517Sbostic #include <sys/malloc.h>
18*56517Sbostic #include <sys/filedesc.h>
19*56517Sbostic #include <sys/proc.h>
2037715Smckusick 
2137582Smarc #ifdef KTRACE
22*56517Sbostic #include <sys/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 	 */
25455091Spendry 	cnp->cn_consume = 0;
25552230Sheideman 	cnp->cn_hash = 0;
25652230Sheideman 	for (cp = cnp->cn_nameptr; *cp != 0 && *cp != '/'; cp++)
25752230Sheideman 		cnp->cn_hash += (unsigned char)*cp;
25852230Sheideman 	cnp->cn_namelen = cp - cnp->cn_nameptr;
25952230Sheideman 	if (cnp->cn_namelen >= NAME_MAX) {
26049736Smckusick 		error = ENAMETOOLONG;
26149736Smckusick 		goto bad;
26249736Smckusick 	}
26337743Smckusick #ifdef NAMEI_DIAGNOSTIC
26449736Smckusick 	{ char c = *cp;
26549736Smckusick 	*cp = '\0';
26652230Sheideman 	printf("{%s}: ", cnp->cn_nameptr);
26749736Smckusick 	*cp = c; }
26837743Smckusick #endif
26952230Sheideman 	ndp->ni_pathlen -= cnp->cn_namelen;
27049736Smckusick 	ndp->ni_next = cp;
27152230Sheideman 	cnp->cn_flags |= MAKEENTRY;
27218109Smckusick 	if (*cp == '\0' && docache == 0)
27352230Sheideman 		cnp->cn_flags &= ~MAKEENTRY;
27452230Sheideman 	if (cnp->cn_namelen == 2 &&
27555181Smckusick 	    cnp->cn_nameptr[1] == '.' && cnp->cn_nameptr[0] == '.')
27652230Sheideman 		cnp->cn_flags |= ISDOTDOT;
27755181Smckusick 	else
27855181Smckusick 		cnp->cn_flags &= ~ISDOTDOT;
27952230Sheideman 	if (*ndp->ni_next == 0)
28052230Sheideman 		cnp->cn_flags |= ISLASTCN;
28155181Smckusick 	else
28255181Smckusick 		cnp->cn_flags &= ~ISLASTCN;
2837534Sroot 
28452230Sheideman 
2857534Sroot 	/*
2867534Sroot 	 * Check for degenerate name (e.g. / or "")
2877534Sroot 	 * which is a way of talking about a directory,
2887534Sroot 	 * e.g. like "/." or ".".
2897534Sroot 	 */
29052230Sheideman 	if (cnp->cn_nameptr[0] == '\0') {
29152230Sheideman 		if (cnp->cn_nameiop != LOOKUP || wantparent) {
29237715Smckusick 			error = EISDIR;
2937534Sroot 			goto bad;
2945972Swnj 		}
29549736Smckusick 		if (dp->v_type != VDIR) {
29649736Smckusick 			error = ENOTDIR;
29749736Smckusick 			goto bad;
29849736Smckusick 		}
29952230Sheideman 		if (!(cnp->cn_flags & LOCKLEAF))
30037715Smckusick 			VOP_UNLOCK(dp);
30137715Smckusick 		ndp->ni_vp = dp;
30252230Sheideman 		if (cnp->cn_flags & SAVESTART)
30349736Smckusick 			panic("lookup: SAVESTART");
30452633Sheideman 		return (0);
3055972Swnj 	}
3067534Sroot 
3076571Smckusic 	/*
30837715Smckusick 	 * Handle "..": two special cases.
30937715Smckusick 	 * 1. If at root directory (e.g. after chroot)
31056313Shibler 	 *    or at absolute root directory
31137715Smckusick 	 *    then ignore it so can't get out.
31237715Smckusick 	 * 2. If this vnode is the root of a mounted
31349736Smckusick 	 *    filesystem, then replace it with the
31437715Smckusick 	 *    vnode which was mounted on so we take the
31537715Smckusick 	 *    .. in the other file system.
31636547Smckusick 	 */
31752230Sheideman 	if (cnp->cn_flags & ISDOTDOT) {
31836547Smckusick 		for (;;) {
31956313Shibler 			if (dp == ndp->ni_rootdir || dp == rootdir) {
32037715Smckusick 				ndp->ni_dvp = dp;
32138390Smckusick 				ndp->ni_vp = dp;
32238347Smckusick 				VREF(dp);
32337715Smckusick 				goto nextname;
32436547Smckusick 			}
32541337Smckusick 			if ((dp->v_flag & VROOT) == 0 ||
32652230Sheideman 			    (cnp->cn_flags & NOCROSSMOUNT))
32736547Smckusick 				break;
32837715Smckusick 			tdp = dp;
32941400Smckusick 			dp = dp->v_mount->mnt_vnodecovered;
33037715Smckusick 			vput(tdp);
33138390Smckusick 			VREF(dp);
33237715Smckusick 			VOP_LOCK(dp);
33336547Smckusick 		}
33436547Smckusick 	}
33536547Smckusick 
33636547Smckusick 	/*
33715798Smckusick 	 * We now have a segment name to search for, and a directory to search.
33815798Smckusick 	 */
33955451Spendry unionlookup:
34052230Sheideman 	ndp->ni_dvp = dp;
34152230Sheideman 	if (error = VOP_LOOKUP(dp, &ndp->ni_vp, cnp)) {
34249736Smckusick #ifdef DIAGNOSTIC
34337715Smckusick 		if (ndp->ni_vp != NULL)
34437715Smckusick 			panic("leaf should be empty");
34549736Smckusick #endif
34637582Smarc #ifdef NAMEI_DIAGNOSTIC
34737715Smckusick 		printf("not found\n");
34837582Smarc #endif
34955451Spendry 		if ((error == ENOENT) &&
35055451Spendry 		    (dp->v_flag & VROOT) &&
35155451Spendry 		    (dp->v_mount->mnt_flag & MNT_UNION)) {
35255451Spendry 			tdp = dp;
35355451Spendry 			dp = dp->v_mount->mnt_vnodecovered;
35455451Spendry 			vput(tdp);
35555451Spendry 			VREF(dp);
35655451Spendry 			VOP_LOCK(dp);
35755451Spendry 			goto unionlookup;
35855451Spendry 		}
35955451Spendry 
36052798Smckusick 		if (error != EJUSTRETURN)
36138581Smckusick 			goto bad;
3625972Swnj 		/*
36337715Smckusick 		 * If creating and at end of pathname, then can consider
36437715Smckusick 		 * allowing file to be created.
3655972Swnj 		 */
36652181Smckusick 		if (rdonly || (ndp->ni_dvp->v_mount->mnt_flag & MNT_RDONLY)) {
36737715Smckusick 			error = EROFS;
3687534Sroot 			goto bad;
36938581Smckusick 		}
3705972Swnj 		/*
37137715Smckusick 		 * We return with ni_vp NULL to indicate that the entry
37237715Smckusick 		 * doesn't currently exist, leaving a pointer to the
37337715Smckusick 		 * (possibly locked) directory inode in ndp->ni_dvp.
3745972Swnj 		 */
37552230Sheideman 		if (cnp->cn_flags & SAVESTART) {
37649736Smckusick 			ndp->ni_startdir = ndp->ni_dvp;
37749736Smckusick 			VREF(ndp->ni_startdir);
37849736Smckusick 		}
37952633Sheideman 		return (0);
3807534Sroot 	}
38137582Smarc #ifdef NAMEI_DIAGNOSTIC
38237715Smckusick 	printf("found\n");
38337582Smarc #endif
3847534Sroot 
38555091Spendry 	/*
38655091Spendry 	 * Take into account any additional components consumed by
38755091Spendry 	 * the underlying filesystem.
38855091Spendry 	 */
38955091Spendry 	if (cnp->cn_consume > 0) {
39055091Spendry 		cnp->cn_nameptr += cnp->cn_consume;
39155091Spendry 		ndp->ni_next += cnp->cn_consume;
39255091Spendry 		ndp->ni_pathlen -= cnp->cn_consume;
39355091Spendry 		cnp->cn_consume = 0;
39455091Spendry 	}
39555091Spendry 
39649736Smckusick 	dp = ndp->ni_vp;
3977534Sroot 	/*
39837715Smckusick 	 * Check for symbolic link
3997534Sroot 	 */
40037715Smckusick 	if ((dp->v_type == VLNK) &&
40152230Sheideman 	    ((cnp->cn_flags & FOLLOW) || *ndp->ni_next == '/')) {
40252230Sheideman 		cnp->cn_flags |= ISSYMLINK;
40352633Sheideman 		return (0);
40415798Smckusick 	}
40515798Smckusick 
4067534Sroot 	/*
40737715Smckusick 	 * Check to see if the vnode has been mounted on;
40837715Smckusick 	 * if so find the root of the mounted file system.
4097534Sroot 	 */
41041337Smckusick 	while (dp->v_type == VDIR && (mp = dp->v_mountedhere) &&
41152230Sheideman 	       (cnp->cn_flags & NOCROSSMOUNT) == 0) {
41252116Smarc 		if (mp->mnt_flag & MNT_MLOCK) {
41341400Smckusick 			mp->mnt_flag |= MNT_MWAIT;
41437715Smckusick 			sleep((caddr_t)mp, PVFS);
41552634Smckusick 			continue;
41621014Smckusick 		}
41749736Smckusick 		if (error = VFS_ROOT(dp->v_mountedhere, &tdp))
4187534Sroot 			goto bad2;
41937715Smckusick 		vput(dp);
42037715Smckusick 		ndp->ni_vp = dp = tdp;
42130Sbill 	}
4227534Sroot 
42337715Smckusick nextname:
42430Sbill 	/*
4257534Sroot 	 * Not a symbolic link.  If more pathname,
4267534Sroot 	 * continue at next component, else return.
42730Sbill 	 */
42849736Smckusick 	if (*ndp->ni_next == '/') {
42952230Sheideman 		cnp->cn_nameptr = ndp->ni_next;
43052230Sheideman 		while (*cnp->cn_nameptr == '/') {
43152230Sheideman 			cnp->cn_nameptr++;
43237715Smckusick 			ndp->ni_pathlen--;
43337715Smckusick 		}
43437715Smckusick 		vrele(ndp->ni_dvp);
4357534Sroot 		goto dirloop;
43630Sbill 	}
4377534Sroot 	/*
43838400Smckusick 	 * Check for read-only file systems.
4397534Sroot 	 */
44052230Sheideman 	if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) {
44138267Smckusick 		/*
44238400Smckusick 		 * Disallow directory write attempts on read-only
44338400Smckusick 		 * file systems.
44438267Smckusick 		 */
44552181Smckusick 		if (rdonly || (dp->v_mount->mnt_flag & MNT_RDONLY) ||
44652181Smckusick 		    (wantparent &&
44752181Smckusick 		     (ndp->ni_dvp->v_mount->mnt_flag & MNT_RDONLY))) {
44838267Smckusick 			error = EROFS;
44938267Smckusick 			goto bad2;
45038267Smckusick 		}
45138267Smckusick 	}
45252230Sheideman 	if (cnp->cn_flags & SAVESTART) {
45349736Smckusick 		ndp->ni_startdir = ndp->ni_dvp;
45449736Smckusick 		VREF(ndp->ni_startdir);
45549736Smckusick 	}
45637715Smckusick 	if (!wantparent)
45737715Smckusick 		vrele(ndp->ni_dvp);
45852230Sheideman 	if ((cnp->cn_flags & LOCKLEAF) == 0)
45937715Smckusick 		VOP_UNLOCK(dp);
46052633Sheideman 	return (0);
4617534Sroot 
46237715Smckusick bad2:
46352230Sheideman 	if ((cnp->cn_flags & LOCKPARENT) && *ndp->ni_next == '\0')
46438001Smckusick 		VOP_UNLOCK(ndp->ni_dvp);
46537715Smckusick 	vrele(ndp->ni_dvp);
46637715Smckusick bad:
46737715Smckusick 	vput(dp);
46837715Smckusick 	ndp->ni_vp = NULL;
46952633Sheideman 	return (error);
4705972Swnj }
47152230Sheideman 
47252230Sheideman 
473