diff options
Diffstat (limited to 'sys')
-rw-r--r-- | sys/kern/vfs_lookup.c | 261 | ||||
-rw-r--r-- | sys/sys/namei.h | 3 |
2 files changed, 138 insertions, 126 deletions
diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c index d36c56cc5c1..6d3e4f8567c 100644 --- a/sys/kern/vfs_lookup.c +++ b/sys/kern/vfs_lookup.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vfs_lookup.c,v 1.7 1997/05/21 14:51:26 tholo Exp $ */ +/* $OpenBSD: vfs_lookup.c,v 1.8 1997/06/18 17:37:38 tholo Exp $ */ /* $NetBSD: vfs_lookup.c,v 1.17 1996/02/09 19:00:59 christos Exp $ */ /* @@ -86,7 +86,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, dironly = 0; + int error, linklen; struct componentname *cnp = &ndp->ni_cnd; ndp->ni_cnd.cn_cred = ndp->ni_cnd.cn_proc->p_ucred; @@ -123,28 +123,12 @@ namei(ndp) */ if (error == 0 && ndp->ni_pathlen == 1) error = ENOENT; + if (error) { - free(cnp->cn_pnbuf, M_NAMEI); + FREE(cnp->cn_pnbuf, M_NAMEI); ndp->ni_vp = NULL; return (error); } - - /* - * Ignore trailing /'s, except require the last path element - * to be a directory - */ - if (ndp->ni_pathlen > 2 && cnp->cn_pnbuf[ndp->ni_pathlen - 2] == '/') { - /* - * Since the last name is no longer the terminal, - * force the FOLLOW flag - */ - cnp->cn_flags |= FOLLOW; - dironly = 1; - while (ndp->ni_pathlen > 2 && - cnp->cn_pnbuf[ndp->ni_pathlen - 2] == '/') - cnp->cn_pnbuf[--ndp->ni_pathlen - 1] = '\0'; - } - ndp->ni_loopcnt = 0; /* @@ -152,23 +136,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); @@ -178,19 +157,6 @@ namei(ndp) * Check for symbolic link */ if ((cnp->cn_flags & ISSYMLINK) == 0) { - /* - * Check for directory if dironly - */ - if (dironly && ndp->ni_vp != NULL && ndp->ni_vp->v_type != VDIR) { - if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1) - VOP_UNLOCK(ndp->ni_dvp); - if (cnp->cn_flags & LOCKLEAF) - vput(ndp->ni_vp); - else - vrele(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 +184,15 @@ 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 + 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 +203,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 +269,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 +287,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 +333,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 +348,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. @@ -436,6 +441,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 +472,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 +503,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 +549,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 +629,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 +691,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) diff --git a/sys/sys/namei.h b/sys/sys/namei.h index d7c691c27d1..8f7288e4721 100644 --- a/sys/sys/namei.h +++ b/sys/sys/namei.h @@ -1,4 +1,4 @@ -/* $OpenBSD: namei.h,v 1.3 1996/12/08 14:25:51 niklas Exp $ */ +/* $OpenBSD: namei.h,v 1.4 1997/06/18 17:37:36 tholo Exp $ */ /* $NetBSD: namei.h,v 1.11 1996/02/09 18:25:20 christos Exp $ */ /* @@ -138,6 +138,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. |