Module Name: src
Committed By: dholland
Date: Sun Aug 9 07:27:54 UTC 2009
Modified Files:
src/sys/kern: vfs_lookup.c
Log Message:
Begin splitting lookup() into more tractable pieces too.
To generate a diff of this commit:
cvs rdiff -u -r1.117 -r1.118 src/sys/kern/vfs_lookup.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/kern/vfs_lookup.c
diff -u src/sys/kern/vfs_lookup.c:1.117 src/sys/kern/vfs_lookup.c:1.118
--- src/sys/kern/vfs_lookup.c:1.117 Sun Aug 9 03:28:35 2009
+++ src/sys/kern/vfs_lookup.c Sun Aug 9 07:27:54 2009
@@ -1,4 +1,4 @@
-/* $NetBSD: vfs_lookup.c,v 1.117 2009/08/09 03:28:35 dholland Exp $ */
+/* $NetBSD: vfs_lookup.c,v 1.118 2009/08/09 07:27:54 dholland Exp $ */
/*
* Copyright (c) 1982, 1986, 1989, 1993
@@ -37,7 +37,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: vfs_lookup.c,v 1.117 2009/08/09 03:28:35 dholland Exp $");
+__KERNEL_RCSID(0, "$NetBSD: vfs_lookup.c,v 1.118 2009/08/09 07:27:54 dholland Exp $");
#include "opt_magiclinks.h"
@@ -224,9 +224,22 @@
struct nameidata *ndp;
struct componentname *cnp;
+ /* used by the pieces of namei */
+ struct vnode *namei_startdir; /* The directory namei() starts from. */
+
+ /* used by the pieces of lookup */
+ int lookup_alldone;
+
+ int docache; /* == 0 do not cache last component */
+ int rdonly; /* lookup read-only flag bit */
struct vnode *dp; /* the directory we are searching */
+ int slashes;
};
+/* XXX reorder things to make this decl unnecessary */
+static int do_lookup(struct namei_state *state);
+
+
/*
* Initialize the namei working state.
*/
@@ -236,7 +249,14 @@
state->ndp = ndp;
state->cnp = &ndp->ni_cnd;
+ state->namei_startdir = NULL;
+
+ state->lookup_alldone = 0;
+
+ state->docache = 0;
+ state->rdonly = 0;
state->dp = NULL;
+ state->slashes = 0;
}
/*
@@ -248,7 +268,7 @@
{
KASSERT(state->cnp == &state->ndp->ni_cnd);
- //KASSERT(state->dp == NULL); // not yet
+ //KASSERT(state->namei_startdir == NULL); // not yet
/* nothing for now */
(void)state;
@@ -321,10 +341,10 @@
*/
cwdi = self->l_proc->p_cwdi;
rw_enter(&cwdi->cwdi_lock, RW_READER);
- state->dp = cwdi->cwdi_rdir;
- if (state->dp == NULL)
- state->dp = rootvnode;
- ndp->ni_rootdir = state->dp;
+ state->namei_startdir = cwdi->cwdi_rdir;
+ if (state->namei_startdir == NULL)
+ state->namei_startdir = rootvnode;
+ ndp->ni_rootdir = state->namei_startdir;
/*
* Check if starting from root directory or current directory.
@@ -333,7 +353,7 @@
if (cnp->cn_flags & TRYEMULROOT) {
if (cnp->cn_flags & EMULROOTSET) {
/* Called from (eg) emul_find_interp() */
- state->dp = ndp->ni_erootdir;
+ state->namei_startdir = ndp->ni_erootdir;
} else {
if (cwdi->cwdi_edir == NULL
|| (cnp->cn_pnbuf[1] == '.'
@@ -341,20 +361,20 @@
&& cnp->cn_pnbuf[3] == '/')) {
ndp->ni_erootdir = NULL;
} else {
- state->dp = cwdi->cwdi_edir;
- ndp->ni_erootdir = state->dp;
+ state->namei_startdir = cwdi->cwdi_edir;
+ ndp->ni_erootdir = state->namei_startdir;
}
}
} else {
ndp->ni_erootdir = NULL;
if (cnp->cn_flags & NOCHROOT)
- state->dp = ndp->ni_rootdir = rootvnode;
+ state->namei_startdir = ndp->ni_rootdir = rootvnode;
}
} else {
- state->dp = cwdi->cwdi_cdir;
+ state->namei_startdir = cwdi->cwdi_cdir;
ndp->ni_erootdir = NULL;
}
- VREF(state->dp);
+ VREF(state->namei_startdir);
rw_exit(&cwdi->cwdi_lock);
/*
@@ -381,7 +401,7 @@
ktrnamei(cnp->cn_pnbuf, ndp->ni_pathlen);
}
- vn_lock(state->dp, LK_EXCLUSIVE | LK_RETRY);
+ vn_lock(state->namei_startdir, LK_EXCLUSIVE | LK_RETRY);
return 0;
}
@@ -393,7 +413,7 @@
static void
namei_end(struct namei_state *state)
{
- vput(state->dp);
+ vput(state->namei_startdir);
PNBUF_PUT(state->cnp->cn_pnbuf);
//state->cnp->cn_pnbuf = NULL; // not yet (just in case) (XXX)
}
@@ -474,23 +494,23 @@
cnp->cn_pnbuf[linklen] = '\0';
ndp->ni_pathlen += linklen;
vput(ndp->ni_vp);
- state->dp = ndp->ni_dvp;
+ state->namei_startdir = ndp->ni_dvp;
/*
* Check if root directory should replace current directory.
*/
if (cnp->cn_pnbuf[0] == '/') {
- vput(state->dp);
+ vput(state->namei_startdir);
/* Keep absolute symbolic links inside emulation root */
- state->dp = ndp->ni_erootdir;
- if (state->dp == NULL || (cnp->cn_pnbuf[1] == '.'
+ state->namei_startdir = ndp->ni_erootdir;
+ if (state->namei_startdir == NULL || (cnp->cn_pnbuf[1] == '.'
&& cnp->cn_pnbuf[2] == '.'
&& cnp->cn_pnbuf[3] == '/')) {
ndp->ni_erootdir = NULL;
- state->dp = ndp->ni_rootdir;
+ state->namei_startdir = ndp->ni_rootdir;
}
- VREF(state->dp);
- vn_lock(state->dp, LK_EXCLUSIVE | LK_RETRY);
+ VREF(state->namei_startdir);
+ vn_lock(state->namei_startdir, LK_EXCLUSIVE | LK_RETRY);
}
return 0;
@@ -519,13 +539,13 @@
/* Loop through symbolic links */
for (;;) {
- if (!state->dp->v_mount) {
+ if (state->namei_startdir->v_mount == NULL) {
/* Give up if the directory is no longer mounted */
namei_end(state);
return (ENOENT);
}
cnp->cn_nameptr = cnp->cn_pnbuf;
- ndp->ni_startdir = state->dp;
+ ndp->ni_startdir = state->namei_startdir;
error = lookup(ndp);
if (error != 0) {
/* XXX this should use namei_end() */
@@ -660,30 +680,33 @@
* return the answer in ni_vp, locked if LOCKLEAF set
* if LOCKPARENT set, return locked parent in ni_dvp
*/
-int
-lookup(struct nameidata *ndp)
+
+/*
+ * Begin lookup().
+ */
+static int
+lookup_start(struct namei_state *state)
{
const char *cp; /* pointer into pathname argument */
- struct vnode *dp = 0; /* the directory we are searching */
- struct vnode *tdp; /* saved dp */
- struct mount *mp; /* mount table entry */
- int docache; /* == 0 do not cache last component */
- int rdonly; /* lookup read-only flag bit */
- int error = 0;
- int slashes;
- struct componentname *cnp = &ndp->ni_cnd;
- struct lwp *l = curlwp;
+
+ struct componentname *cnp = state->cnp;
+ struct nameidata *ndp = state->ndp;
+
+ KASSERT(cnp == &ndp->ni_cnd);
+
+ state->lookup_alldone = 0;
+ state->dp = NULL;
/*
* Setup: break out flag bits into variables.
*/
- docache = (cnp->cn_flags & NOCACHE) ^ NOCACHE;
+ state->docache = (cnp->cn_flags & NOCACHE) ^ NOCACHE;
if (cnp->cn_nameiop == DELETE)
- docache = 0;
- rdonly = cnp->cn_flags & RDONLY;
+ state->docache = 0;
+ state->rdonly = cnp->cn_flags & RDONLY;
ndp->ni_dvp = NULL;
cnp->cn_flags &= ~ISSYMLINK;
- dp = ndp->ni_startdir;
+ state->dp = ndp->ni_startdir;
ndp->ni_startdir = NULLVP;
/*
@@ -698,10 +721,9 @@
ndp->ni_pathlen -= cp - cnp->cn_nameptr;
cnp->cn_nameptr = cp;
- if (dp->v_type != VDIR) {
- error = ENOTDIR;
- vput(dp);
- goto bad;
+ if (state->dp->v_type != VDIR) {
+ vput(state->dp);
+ return ENOTDIR;
}
/*
@@ -709,13 +731,28 @@
* current node.
*/
if (cnp->cn_nameptr[0] == '\0') {
- ndp->ni_vp = dp;
+ ndp->ni_vp = state->dp;
cnp->cn_flags |= ISLASTCN;
- goto terminal;
+
+ /* bleh */
+ state->lookup_alldone = 1;
+ return 0;
}
}
-dirloop:
+ return 0;
+}
+
+static int
+lookup_parsepath(struct namei_state *state)
+{
+ const char *cp; /* pointer into pathname argument */
+
+ struct componentname *cnp = state->cnp;
+ struct nameidata *ndp = state->ndp;
+
+ KASSERT(cnp == &ndp->ni_cnd);
+
/*
* Search a new directory.
*
@@ -732,10 +769,9 @@
cnp->cn_hash = namei_hash(cnp->cn_nameptr, &cp);
cnp->cn_namelen = cp - cnp->cn_nameptr;
if (cnp->cn_namelen > NAME_MAX) {
- vput(dp);
- error = ENAMETOOLONG;
+ vput(state->dp);
ndp->ni_dvp = NULL;
- goto bad;
+ return ENAMETOOLONG;
}
#ifdef NAMEI_DIAGNOSTIC
{ char c = *cp;
@@ -754,12 +790,12 @@
do {
cp++;
} while (*cp == '/');
- slashes = cp - ndp->ni_next;
- ndp->ni_pathlen -= slashes;
+ state->slashes = cp - ndp->ni_next;
+ ndp->ni_pathlen -= state->slashes;
ndp->ni_next = cp;
cnp->cn_flags |= REQUIREDIR;
} else {
- slashes = 0;
+ state->slashes = 0;
cnp->cn_flags &= ~REQUIREDIR;
}
/*
@@ -767,7 +803,7 @@
* a directory. Cache all intervening lookups, but not the final one.
*/
if (*cp == '\0') {
- if (docache)
+ if (state->docache)
cnp->cn_flags |= MAKEENTRY;
else
cnp->cn_flags &= ~MAKEENTRY;
@@ -782,6 +818,22 @@
else
cnp->cn_flags &= ~ISDOTDOT;
+ return 0;
+}
+
+static int
+lookup_once(struct namei_state *state)
+{
+ struct vnode *tdp; /* saved dp */
+ struct mount *mp; /* mount table entry */
+ struct lwp *l = curlwp;
+ int error;
+
+ struct componentname *cnp = state->cnp;
+ struct nameidata *ndp = state->ndp;
+
+ KASSERT(cnp == &ndp->ni_cnd);
+
/*
* Handle "..": two special cases.
* 1. If at root directory (e.g. after chroot)
@@ -800,18 +852,18 @@
struct proc *p = l->l_proc;
for (;;) {
- if (dp == ndp->ni_rootdir || dp == rootvnode) {
- ndp->ni_dvp = dp;
- ndp->ni_vp = dp;
- VREF(dp);
- goto nextname;
+ if (state->dp == ndp->ni_rootdir || state->dp == rootvnode) {
+ ndp->ni_dvp = state->dp;
+ ndp->ni_vp = state->dp;
+ VREF(state->dp);
+ return 0;
}
if (ndp->ni_rootdir != rootvnode) {
int retval;
- VOP_UNLOCK(dp, 0);
- retval = vn_isunder(dp, ndp->ni_rootdir, l);
- vn_lock(dp, LK_EXCLUSIVE | LK_RETRY);
+ VOP_UNLOCK(state->dp, 0);
+ retval = vn_isunder(state->dp, ndp->ni_rootdir, l);
+ vn_lock(state->dp, LK_EXCLUSIVE | LK_RETRY);
if (!retval) {
/* Oops! We got out of jail! */
log(LOG_WARNING,
@@ -820,24 +872,24 @@
p->p_pid, kauth_cred_geteuid(l->l_cred),
p->p_comm);
/* Put us at the jail root. */
- vput(dp);
- dp = ndp->ni_rootdir;
- ndp->ni_dvp = dp;
- ndp->ni_vp = dp;
- VREF(dp);
- VREF(dp);
- vn_lock(dp, LK_EXCLUSIVE | LK_RETRY);
- goto nextname;
+ vput(state->dp);
+ state->dp = ndp->ni_rootdir;
+ ndp->ni_dvp = state->dp;
+ ndp->ni_vp = state->dp;
+ VREF(state->dp);
+ VREF(state->dp);
+ vn_lock(state->dp, LK_EXCLUSIVE | LK_RETRY);
+ return 0;
}
}
- if ((dp->v_vflag & VV_ROOT) == 0 ||
+ if ((state->dp->v_vflag & VV_ROOT) == 0 ||
(cnp->cn_flags & NOCROSSMOUNT))
break;
- tdp = dp;
- dp = dp->v_mount->mnt_vnodecovered;
+ tdp = state->dp;
+ state->dp = state->dp->v_mount->mnt_vnodecovered;
vput(tdp);
- VREF(dp);
- vn_lock(dp, LK_EXCLUSIVE | LK_RETRY);
+ VREF(state->dp);
+ vn_lock(state->dp, LK_EXCLUSIVE | LK_RETRY);
}
}
@@ -846,9 +898,9 @@
* Again, our only vnode state is that "dp" is held and locked.
*/
unionlookup:
- ndp->ni_dvp = dp;
+ ndp->ni_dvp = state->dp;
ndp->ni_vp = NULL;
- error = VOP_LOOKUP(dp, &ndp->ni_vp, cnp);
+ error = VOP_LOOKUP(state->dp, &ndp->ni_vp, cnp);
if (error != 0) {
#ifdef DIAGNOSTIC
if (ndp->ni_vp != NULL)
@@ -858,18 +910,18 @@
printf("not found\n");
#endif /* NAMEI_DIAGNOSTIC */
if ((error == ENOENT) &&
- (dp->v_vflag & VV_ROOT) &&
- (dp->v_mount->mnt_flag & MNT_UNION)) {
- tdp = dp;
- dp = dp->v_mount->mnt_vnodecovered;
+ (state->dp->v_vflag & VV_ROOT) &&
+ (state->dp->v_mount->mnt_flag & MNT_UNION)) {
+ tdp = state->dp;
+ state->dp = state->dp->v_mount->mnt_vnodecovered;
vput(tdp);
- VREF(dp);
- vn_lock(dp, LK_EXCLUSIVE | LK_RETRY);
+ VREF(state->dp);
+ vn_lock(state->dp, LK_EXCLUSIVE | LK_RETRY);
goto unionlookup;
}
if (error != EJUSTRETURN)
- goto bad;
+ return error;
/*
* If this was not the last component, or there were trailing
@@ -877,17 +929,15 @@
* then the name must exist.
*/
if ((cnp->cn_flags & (REQUIREDIR | CREATEDIR)) == REQUIREDIR) {
- error = ENOENT;
- goto bad;
+ return ENOENT;
}
/*
* If creating and at end of pathname, then can consider
* allowing file to be created.
*/
- if (rdonly) {
- error = EROFS;
- goto bad;
+ if (state->rdonly) {
+ return EROFS;
}
/*
@@ -899,6 +949,7 @@
ndp->ni_startdir = ndp->ni_dvp;
VREF(ndp->ni_startdir);
}
+ state->lookup_alldone = 1;
return (0);
}
#ifdef NAMEI_DIAGNOSTIC
@@ -911,17 +962,17 @@
* the last component consumed.
*/
if (cnp->cn_consume > 0) {
- ndp->ni_pathlen -= cnp->cn_consume - slashes;
- ndp->ni_next += cnp->cn_consume - slashes;
+ ndp->ni_pathlen -= cnp->cn_consume - state->slashes;
+ ndp->ni_next += cnp->cn_consume - state->slashes;
cnp->cn_consume = 0;
if (ndp->ni_next[0] == '\0')
cnp->cn_flags |= ISLASTCN;
}
- dp = ndp->ni_vp;
+ state->dp = ndp->ni_vp;
/*
- * "dp" and "ndp->ni_dvp" are both locked and held,
+ * "state->dp" and "ndp->ni_dvp" are both locked and held,
* and may be the same vnode.
*/
@@ -929,35 +980,74 @@
* Check to see if the vnode has been mounted on;
* if so find the root of the mounted file system.
*/
- while (dp->v_type == VDIR && (mp = dp->v_mountedhere) &&
+ while (state->dp->v_type == VDIR && (mp = state->dp->v_mountedhere) &&
(cnp->cn_flags & NOCROSSMOUNT) == 0) {
error = vfs_busy(mp, NULL);
if (error != 0) {
- vput(dp);
- goto bad;
+ vput(state->dp);
+ return error;
}
- KASSERT(ndp->ni_dvp != dp);
+ KASSERT(ndp->ni_dvp != state->dp);
VOP_UNLOCK(ndp->ni_dvp, 0);
- vput(dp);
+ vput(state->dp);
error = VFS_ROOT(mp, &tdp);
vfs_unbusy(mp, false, NULL);
if (error) {
vn_lock(ndp->ni_dvp, LK_EXCLUSIVE | LK_RETRY);
- goto bad;
+ return error;
}
VOP_UNLOCK(tdp, 0);
- ndp->ni_vp = dp = tdp;
+ ndp->ni_vp = state->dp = tdp;
vn_lock(ndp->ni_dvp, LK_EXCLUSIVE | LK_RETRY);
vn_lock(ndp->ni_vp, LK_EXCLUSIVE | LK_RETRY);
}
+ return 0;
+}
+
+static int
+do_lookup(struct namei_state *state)
+{
+ int error = 0;
+
+ struct componentname *cnp = state->cnp;
+ struct nameidata *ndp = state->ndp;
+
+ KASSERT(cnp == &ndp->ni_cnd);
+
+ error = lookup_start(state);
+ if (error) {
+ goto bad;
+ }
+ // XXX: this case should not be necessary given proper handling
+ // of slashes elsewhere.
+ if (state->lookup_alldone) {
+ goto terminal;
+ }
+
+dirloop:
+ error = lookup_parsepath(state);
+ if (error) {
+ goto bad;
+ }
+
+ error = lookup_once(state);
+ if (error) {
+ goto bad;
+ }
+ // XXX ought to be able to avoid this case too
+ if (state->lookup_alldone) {
+ /* this should NOT be "goto terminal;" */
+ return 0;
+ }
+
/*
* 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|REQUIREDIR))) {
- ndp->ni_pathlen += slashes;
- ndp->ni_next -= slashes;
+ if ((state->dp->v_type == VLNK) && (cnp->cn_flags & (FOLLOW|REQUIREDIR))) {
+ ndp->ni_pathlen += state->slashes;
+ ndp->ni_next -= state->slashes;
cnp->cn_flags |= ISSYMLINK;
return (0);
}
@@ -966,22 +1056,20 @@
* Check for directory, if the component was followed by a series of
* slashes.
*/
- if ((dp->v_type != VDIR) && (cnp->cn_flags & REQUIREDIR)) {
+ if ((state->dp->v_type != VDIR) && (cnp->cn_flags & REQUIREDIR)) {
error = ENOTDIR;
- KASSERT(dp != ndp->ni_dvp);
- vput(dp);
+ KASSERT(state->dp != ndp->ni_dvp);
+ vput(state->dp);
goto bad;
}
-nextname:
-
/*
* Not a symbolic link. If this was not the last component, then
* continue at the next component, else return.
*/
if (!(cnp->cn_flags & ISLASTCN)) {
cnp->cn_nameptr = ndp->ni_next;
- if (ndp->ni_dvp == dp) {
+ if (ndp->ni_dvp == state->dp) {
vrele(ndp->ni_dvp);
} else {
vput(ndp->ni_dvp);
@@ -990,7 +1078,7 @@
}
terminal:
- if (dp == ndp->ni_erootdir) {
+ if (state->dp == ndp->ni_erootdir) {
/*
* We are about to return the emulation root.
* This isn't a good idea because code might repeatedly
@@ -998,17 +1086,17 @@
* for "/" and loop forever.
* So convert it to the real root.
*/
- if (ndp->ni_dvp == dp)
- vrele(dp);
+ if (ndp->ni_dvp == state->dp)
+ vrele(state->dp);
else
if (ndp->ni_dvp != NULL)
vput(ndp->ni_dvp);
ndp->ni_dvp = NULL;
- vput(dp);
- dp = ndp->ni_rootdir;
- VREF(dp);
- vn_lock(dp, LK_EXCLUSIVE | LK_RETRY);
- ndp->ni_vp = dp;
+ vput(state->dp);
+ state->dp = ndp->ni_rootdir;
+ VREF(state->dp);
+ vn_lock(state->dp, LK_EXCLUSIVE | LK_RETRY);
+ ndp->ni_vp = state->dp;
}
/*
@@ -1028,7 +1116,7 @@
default:
KASSERT(0);
}
- vput(dp);
+ vput(state->dp);
goto bad;
}
@@ -1036,11 +1124,11 @@
* Disallow directory write attempts on read-only lookups.
* Prefers EEXIST over EROFS for the CREATE case.
*/
- if (rdonly &&
+ if (state->rdonly &&
(cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) {
error = EROFS;
- if (dp != ndp->ni_dvp) {
- vput(dp);
+ if (state->dp != ndp->ni_dvp) {
+ vput(state->dp);
}
goto bad;
}
@@ -1051,7 +1139,7 @@
}
}
if ((cnp->cn_flags & LOCKLEAF) == 0) {
- VOP_UNLOCK(dp, 0);
+ VOP_UNLOCK(state->dp, 0);
}
return (0);
@@ -1061,6 +1149,23 @@
}
/*
+ * Externally visible interface used by nfsd (bletch, yuk, XXX)
+ */
+int
+lookup(struct nameidata *ndp)
+{
+ struct namei_state state;
+ int error;
+
+ /* For now at least we don't have to frob the state */
+ namei_init(&state, ndp);
+ error = do_lookup(&state);
+ namei_cleanup(&state);
+
+ return error;
+}
+
+/*
* Reacquire a path name component.
* dvp is locked on entry and exit.
* *vpp is locked on exit unless it's NULL.