Snapshot of namei() cleanup:

1) Eliminate all of the null component name special cases; handle runs of
slashes and leading and trailing slashes completely differently.
2) Return ENOENT when doing a lookup through an empty symlink.
3) Enforce that we're doing a lookup through a directory in in chdir() and
lookup() rather than in foo_lookup().

Not yet finished.
This commit is contained in:
mycroft 1997-05-08 14:55:22 +00:00
parent 5482ee2d93
commit 07ba5e5ac3
2 changed files with 140 additions and 125 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: vfs_lookup.c,v 1.22 1997/04/08 16:11:48 kleink Exp $ */
/* $NetBSD: vfs_lookup.c,v 1.23 1997/05/08 14:55:22 mycroft Exp $ */
/*
* Copyright (c) 1982, 1986, 1989, 1993
@ -85,7 +85,7 @@ namei(ndp)
register struct vnode *dp; /* the directory we are searching */
struct iovec aiov; /* uio for reading symbolic links */
struct uio auio;
int error, linklen, forcedir = 0;
int error, linklen;
struct componentname *cnp = &ndp->ni_cnd;
ndp->ni_cnd.cn_cred = ndp->ni_cnd.cn_proc->p_ucred;
@ -125,20 +125,6 @@ namei(ndp)
}
ndp->ni_loopcnt = 0;
/*
* If there are trailing '/'s, strip them off and require the last
* path element to be a directory. This is heavily `2'-based:
* ni_pathlen includes the trailing '\0', and at least the first
* character of cn_pnbuf has to be preserved.
*/
if (ndp->ni_pathlen > 2 && cnp->cn_pnbuf[ndp->ni_pathlen - 2] == '/') {
forcedir = 1;
cnp->cn_flags |= FOLLOW;
while (ndp->ni_pathlen > 2 &&
cnp->cn_pnbuf[ndp->ni_pathlen - 2] == '/')
cnp->cn_pnbuf[ndp->ni_pathlen-- - 2] = '\0';
}
#ifdef KTRACE
if (KTRPOINT(cnp->cn_proc, KTR_NAMEI))
ktrnamei(cnp->cn_proc->p_tracep, cnp->cn_pnbuf);
@ -149,23 +135,18 @@ namei(ndp)
*/
if ((ndp->ni_rootdir = fdp->fd_rdir) == NULL)
ndp->ni_rootdir = rootvnode;
dp = fdp->fd_cdir;
VREF(dp);
/*
* Check if starting from root directory or current directory.
*/
if (cnp->cn_pnbuf[0] == '/') {
dp = ndp->ni_rootdir;
VREF(dp);
} else {
dp = fdp->fd_cdir;
VREF(dp);
}
for (;;) {
/*
* Check if root directory should replace current directory.
* Done at start of translation and after symbolic link.
*/
cnp->cn_nameptr = cnp->cn_pnbuf;
if (*(cnp->cn_nameptr) == '/') {
vrele(dp);
while (*(cnp->cn_nameptr) == '/') {
cnp->cn_nameptr++;
ndp->ni_pathlen--;
}
dp = ndp->ni_rootdir;
VREF(dp);
}
ndp->ni_startdir = dp;
if ((error = lookup(ndp)) != 0) {
FREE(cnp->cn_pnbuf, M_NAMEI);
@ -175,22 +156,6 @@ namei(ndp)
* Check for symbolic link
*/
if ((cnp->cn_flags & ISSYMLINK) == 0) {
/*
* If trailing '/'s implied a directory, verify this.
* Always check ndp->ni_vp: CREATE and RENAME may
* succeed without returning a vnode!
*/
if (forcedir &&
ndp->ni_vp != NULL &&
ndp->ni_vp->v_type != VDIR) {
if ((cnp->cn_flags & LOCKPARENT) &&
ndp->ni_pathlen == 1)
VOP_UNLOCK(ndp->ni_dvp);
vput(ndp->ni_vp);
FREE(cnp->cn_pnbuf, M_NAMEI);
return (ENOTDIR);
}
if ((cnp->cn_flags & (SAVENAME | SAVESTART)) == 0)
FREE(cnp->cn_pnbuf, M_NAMEI);
else
@ -218,16 +183,19 @@ namei(ndp)
auio.uio_resid = MAXPATHLEN;
error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred);
if (error) {
badlink:
if (ndp->ni_pathlen > 1)
free(cp, M_NAMEI);
FREE(cp, M_NAMEI);
break;
}
linklen = MAXPATHLEN - auio.uio_resid;
if (linklen == 0) {
error = ENOENT;
goto badlink;
}
if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
if (ndp->ni_pathlen > 1)
free(cp, M_NAMEI);
error = ENAMETOOLONG;
break;
goto badlink;
}
if (ndp->ni_pathlen > 1) {
bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen);
@ -238,6 +206,14 @@ namei(ndp)
ndp->ni_pathlen += linklen;
vput(ndp->ni_vp);
dp = ndp->ni_dvp;
/*
* Check if root directory should replace current directory.
*/
if (cnp->cn_pnbuf[0] == '/') {
vrele(dp);
dp = ndp->ni_rootdir;
VREF(dp);
}
}
FREE(cnp->cn_pnbuf, M_NAMEI);
vrele(ndp->ni_dvp);
@ -296,6 +272,7 @@ lookup(ndp)
int wantparent; /* 1 => wantparent or lockparent flag */
int rdonly; /* lookup read-only flag bit */
int error = 0;
int slashes;
struct componentname *cnp = &ndp->ni_cnd;
/*
@ -313,6 +290,40 @@ lookup(ndp)
ndp->ni_startdir = NULLVP;
VOP_LOCK(dp);
/*
* If we have a leading string of slashes, remove them, and just make
* sure the current node is a directory.
*/
cp = cnp->cn_nameptr;
if (*cp == '/') {
do {
cp++;
} while (*cp == '/');
ndp->ni_pathlen -= cp - cnp->cn_nameptr;
cnp->cn_nameptr = cp;
if (dp->v_type != VDIR) {
error = ENOTDIR;
goto bad;
}
/*
* If we've exhausted the path name, then just return the
* current node. If the caller requested the parent node (i.e.
* it's a CREATE, DELETE, or RENAME), and we don't have one
* (because this is the root directory), then we must fail.
*/
if (cnp->cn_nameptr[0] == '\0') {
if (ndp->ni_dvp == NULL && wantparent) {
error = EISDIR;
goto bad;
}
ndp->ni_vp = dp;
cnp->cn_flags |= ISLASTCN;
goto terminal;
}
}
dirloop:
/*
* Search a new directory.
@ -325,7 +336,7 @@ dirloop:
*/
cnp->cn_consume = 0;
cnp->cn_hash = 0;
for (cp = cnp->cn_nameptr; *cp != 0 && *cp != '/'; cp++)
for (cp = cnp->cn_nameptr; *cp != '\0' && *cp != '/'; cp++)
cnp->cn_hash += (unsigned char)*cp;
cnp->cn_namelen = cp - cnp->cn_nameptr;
if (cnp->cn_namelen > NAME_MAX) {
@ -340,45 +351,42 @@ dirloop:
#endif
ndp->ni_pathlen -= cnp->cn_namelen;
ndp->ni_next = cp;
cnp->cn_flags |= MAKEENTRY;
if (*cp == '\0' && docache == 0)
cnp->cn_flags &= ~MAKEENTRY;
/*
* If this component is followed by a slash, then move the pointer to
* the next component forward, and remember that this component must be
* a directory.
*/
if (*cp == '/') {
do {
cp++;
} while (*cp == '/');
slashes = cp - ndp->ni_next;
ndp->ni_pathlen -= slashes;
ndp->ni_next = cp;
cnp->cn_flags |= REQUIREDIR;
} else {
slashes = 0;
cnp->cn_flags &= ~REQUIREDIR;
}
/*
* We do special processing on the last component, whether or not it's
* a directory. Cache all intervening lookups, but not the final one.
*/
if (*cp == '\0') {
if (docache)
cnp->cn_flags |= MAKEENTRY;
else
cnp->cn_flags &= ~MAKEENTRY;
cnp->cn_flags |= ISLASTCN;
} else {
cnp->cn_flags |= MAKEENTRY;
cnp->cn_flags &= ~ISLASTCN;
}
if (cnp->cn_namelen == 2 &&
cnp->cn_nameptr[1] == '.' && cnp->cn_nameptr[0] == '.')
cnp->cn_flags |= ISDOTDOT;
else
cnp->cn_flags &= ~ISDOTDOT;
if (*ndp->ni_next == 0)
cnp->cn_flags |= ISLASTCN;
else
cnp->cn_flags &= ~ISLASTCN;
/*
* Check for degenerate name (e.g. / or "")
* which is a way of talking about a directory,
* e.g. like "/." or ".".
*/
if (cnp->cn_nameptr[0] == '\0') {
if (dp->v_type != VDIR) {
error = ENOTDIR;
goto bad;
}
if (cnp->cn_nameiop != LOOKUP) {
error = EISDIR;
goto bad;
}
if (wantparent) {
ndp->ni_dvp = dp;
VREF(dp);
}
ndp->ni_vp = dp;
if (!(cnp->cn_flags & (LOCKPARENT | LOCKLEAF)))
VOP_UNLOCK(dp);
if (cnp->cn_flags & SAVESTART)
panic("lookup: SAVESTART");
return (0);
}
/*
* Handle "..": two special cases.
@ -435,6 +443,14 @@ unionlookup:
if (error != EJUSTRETURN)
goto bad;
/*
* If this was not the last component, or there were trailing
* slashes, then the name must exist.
*/
if (cnp->cn_flags & REQUIREDIR) {
error = ENOENT;
goto bad;
}
/*
* If creating and at end of pathname, then can consider
* allowing file to be created.
@ -459,14 +475,16 @@ unionlookup:
#endif
/*
* Take into account any additional components consumed by
* the underlying filesystem.
* Take into account any additional components consumed by the
* underlying filesystem. This will include any trailing slashes after
* the last component consumed.
*/
if (cnp->cn_consume > 0) {
cnp->cn_nameptr += cnp->cn_consume;
ndp->ni_next += cnp->cn_consume;
ndp->ni_pathlen -= cnp->cn_consume;
ndp->ni_pathlen -= cnp->cn_consume - slashes;
ndp->ni_next += cnp->cn_consume - slashes;
cnp->cn_consume = 0;
if (ndp->ni_next[0] == '\0')
cnp->cn_flags |= ISLASTCN;
}
dp = ndp->ni_vp;
@ -488,28 +506,37 @@ unionlookup:
}
/*
* Check for symbolic link
* Check for symbolic link. Back up over any slashes that we skipped,
* as we will need them again.
*/
if ((dp->v_type == VLNK) &&
((cnp->cn_flags & FOLLOW) || *ndp->ni_next == '/')) {
if ((dp->v_type == VLNK) && (cnp->cn_flags & (FOLLOW|REQUIREDIR))) {
ndp->ni_pathlen += slashes;
ndp->ni_next -= slashes;
cnp->cn_flags |= ISSYMLINK;
return (0);
}
/*
* Check for directory, if the component was followed by a series of
* slashes.
*/
if ((dp->v_type != VDIR) && (cnp->cn_flags & REQUIREDIR)) {
error = ENOTDIR;
goto bad2;
}
nextname:
/*
* Not a symbolic link. If more pathname,
* continue at next component, else return.
* Not a symbolic link. If this was not the last component, then
* continue at the next component, else return.
*/
if (*ndp->ni_next == '/') {
if (!(cnp->cn_flags & ISLASTCN)) {
cnp->cn_nameptr = ndp->ni_next;
while (*cnp->cn_nameptr == '/') {
cnp->cn_nameptr++;
ndp->ni_pathlen--;
}
vrele(ndp->ni_dvp);
goto dirloop;
}
terminal:
/*
* Check for read-only file systems.
*/
@ -525,18 +552,20 @@ nextname:
goto bad2;
}
}
if (cnp->cn_flags & SAVESTART) {
ndp->ni_startdir = ndp->ni_dvp;
VREF(ndp->ni_startdir);
if (ndp->ni_dvp != NULL) {
if (cnp->cn_flags & SAVESTART) {
ndp->ni_startdir = ndp->ni_dvp;
VREF(ndp->ni_startdir);
}
if (!wantparent)
vrele(ndp->ni_dvp);
}
if (!wantparent)
vrele(ndp->ni_dvp);
if ((cnp->cn_flags & LOCKLEAF) == 0)
VOP_UNLOCK(dp);
return (0);
bad2:
if ((cnp->cn_flags & LOCKPARENT) && *ndp->ni_next == '\0')
if ((cnp->cn_flags & LOCKPARENT) && (cnp->cn_flags & ISLASTCN))
VOP_UNLOCK(ndp->ni_dvp);
vrele(ndp->ni_dvp);
bad:
@ -603,22 +632,8 @@ relookup(dvp, vpp, cnp)
* which is a way of talking about a directory,
* e.g. like "/." or ".".
*/
if (cnp->cn_nameptr[0] == '\0') {
if (dp->v_type != VDIR) {
error = ENOTDIR;
goto bad;
}
if (cnp->cn_nameiop != LOOKUP || wantparent) {
error = EISDIR;
goto bad;
}
if (!(cnp->cn_flags & LOCKLEAF))
VOP_UNLOCK(dp);
*vpp = dp;
if (cnp->cn_flags & SAVESTART)
panic("lookup: SAVESTART");
return (0);
}
if (cnp->cn_nameptr[0] == '\0')
panic("relookup: null name");
if (cnp->cn_flags & ISDOTDOT)
panic ("relookup: lookup on dot-dot");
@ -679,7 +694,6 @@ relookup(dvp, vpp, cnp)
/* ASSERT(dvp == ndp->ni_startdir) */
if (cnp->cn_flags & SAVESTART)
VREF(dvp);
if (!wantparent)
vrele(dvp);
if ((cnp->cn_flags & LOCKLEAF) == 0)

View File

@ -1,4 +1,4 @@
/* $NetBSD: namei.h,v 1.12 1996/10/25 23:14:15 cgd Exp $ */
/* $NetBSD: namei.h,v 1.13 1997/05/08 14:55:25 mycroft Exp $ */
/*
* Copyright (c) 1985, 1989, 1991, 1993
@ -137,6 +137,7 @@ struct nameidata {
#define ISSYMLINK 0x10000 /* symlink needs interpretation */
#define ISWHITEOUT 0x20000 /* found whiteout */
#define DOWHITEOUT 0x40000 /* do whiteouts */
#define REQUIREDIR 0x80000 /* must be a directory */
#define PARAMASK 0xfff00 /* mask of parameter descriptors */
/*
* Initialization of an nameidata structure.