summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/kern/vfs_lookup.c261
-rw-r--r--sys/sys/namei.h3
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.