summaryrefslogtreecommitdiff
path: root/sys/nfs/nfs_vnops.c
diff options
context:
space:
mode:
authorArtur Grabowski <art@cvs.openbsd.org>2003-01-31 17:37:51 +0000
committerArtur Grabowski <art@cvs.openbsd.org>2003-01-31 17:37:51 +0000
commit1b0b18abe62afb1a61733a6f5891a98bfbea81ec (patch)
tree29dc896fd65580ea85012ef60aaf813858629e3f /sys/nfs/nfs_vnops.c
parent2bccdd51809becfe909c271f5fef4d380c663209 (diff)
File system locking fixups, mostly from NetBSD:
- cache_lookup move common code from various fs's here always return with vnode and parent locked adjust return codes - PDIRUNLOCK - new flag set if lookup couldn't lock parent vnode - kernfs and procfs lock vnode in get_root don't unlock (again) in kernfs_freevp fix memory leak in procfs From tedu@stanford.edu deraadt@ and various other ok
Diffstat (limited to 'sys/nfs/nfs_vnops.c')
-rw-r--r--sys/nfs/nfs_vnops.c173
1 files changed, 139 insertions, 34 deletions
diff --git a/sys/nfs/nfs_vnops.c b/sys/nfs/nfs_vnops.c
index b0875e2b024..bc9f53d8291 100644
--- a/sys/nfs/nfs_vnops.c
+++ b/sys/nfs/nfs_vnops.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: nfs_vnops.c,v 1.52 2002/11/08 04:34:17 art Exp $ */
+/* $OpenBSD: nfs_vnops.c,v 1.53 2003/01/31 17:37:50 art Exp $ */
/* $NetBSD: nfs_vnops.c,v 1.62.4.1 1996/07/08 20:26:52 jtc Exp $ */
/*
@@ -636,7 +636,7 @@ nfs_lookup(v)
struct vnode *dvp = ap->a_dvp;
struct vnode **vpp = ap->a_vpp;
struct proc *p = cnp->cn_proc;
- int flags = cnp->cn_flags;
+ int flags;
struct vnode *newvp;
u_int32_t *tl;
caddr_t cp;
@@ -650,6 +650,9 @@ nfs_lookup(v)
int lockparent, wantparent, error = 0, attrflag, fhsize;
int v3 = NFS_ISV3(dvp);
+ cnp->cn_flags &= ~PDIRUNLOCK;
+ flags = cnp->cn_flags;
+
*vpp = NULLVP;
if ((flags & ISLASTCN) && (dvp->v_mount->mnt_flag & MNT_RDONLY) &&
(cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
@@ -660,38 +663,76 @@ nfs_lookup(v)
wantparent = flags & (LOCKPARENT|WANTPARENT);
nmp = VFSTONFS(dvp->v_mount);
np = VTONFS(dvp);
- if ((error = cache_lookup(dvp, vpp, cnp)) != 0 && error != ENOENT) {
+
+ /*
+ * Before tediously performing a linear scan of the directory,
+ * check the name cache to see if the directory/name pair
+ * we are looking for is known already.
+ * If the directory/name pair is found in the name cache,
+ * we have to ensure the directory has not changed from
+ * the time the cache entry has been created. If it has,
+ * the cache entry has to be ignored.
+ */
+ if ((error = cache_lookup(dvp, vpp, cnp)) >= 0) {
struct vattr vattr;
- int vpid;
+ int err2;
- newvp = *vpp;
- vpid = newvp->v_id;
- /*
- * See the comment starting `Step through' in ufs/ufs_lookup.c
- * for an explanation of the locking protocol
- */
- if (dvp == newvp) {
- VREF(newvp);
- error = 0;
- } else
- error = vget(newvp, LK_EXCLUSIVE, p);
+ if (error && error != ENOENT) {
+ *vpp = NULLVP;
+ return (error);
+ }
- if (!error) {
- if (vpid == newvp->v_id) {
- if (!VOP_GETATTR(newvp, &vattr, cnp->cn_cred, cnp->cn_proc)
- && vattr.va_ctime.tv_sec == VTONFS(newvp)->n_ctime) {
- nfsstats.lookupcache_hits++;
- if (cnp->cn_nameiop != LOOKUP &&
- (flags & ISLASTCN))
- cnp->cn_flags |= SAVENAME;
- return (0);
- }
- cache_purge(newvp);
+ if (cnp->cn_flags & PDIRUNLOCK) {
+ err2 = vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p);
+ if (err2 != 0) {
+ *vpp = NULLVP;
+ return (err2);
}
- vrele(newvp);
+ cnp->cn_flags &= ~PDIRUNLOCK;
+ }
+
+ err2 = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, cnp->cn_proc);
+ if (err2 != 0) {
+ if (error == 0) {
+ if (*vpp != dvp)
+ vput(*vpp);
+ else
+ vrele(*vpp);
+ }
+ *vpp = NULLVP;
+ return (err2);
}
+
+ if (error == ENOENT) {
+ if (!VOP_GETATTR(dvp, &vattr, cnp->cn_cred,
+ cnp->cn_proc) && vattr.va_mtime.tv_sec ==
+ VTONFS(dvp)->n_ctime)
+ return (ENOENT);
+ cache_purge(dvp);
+ np->n_ctime = 0;
+ goto dorpc;
+ }
+
+ newvp = *vpp;
+ if (!VOP_GETATTR(newvp, &vattr, cnp->cn_cred, cnp->cn_proc)
+ && vattr.va_ctime.tv_sec == VTONFS(newvp)->n_ctime)
+ {
+ nfsstats.lookupcache_hits++;
+ if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN))
+ cnp->cn_flags |= SAVENAME;
+ if ((!lockparent || !(flags & ISLASTCN)) &&
+ newvp != dvp)
+ VOP_UNLOCK(dvp, 0, p);
+ return (0);
+ }
+ cache_purge(newvp);
+ if (newvp != dvp)
+ vput(newvp);
+ else
+ vrele(newvp);
*vpp = NULLVP;
}
+dorpc:
error = 0;
newvp = NULLVP;
nfsstats.lookupcache_misses++;
@@ -731,25 +772,72 @@ nfs_lookup(v)
*vpp = newvp;
m_freem(mrep);
cnp->cn_flags |= SAVENAME;
+ if (!lockparent) {
+ VOP_UNLOCK(dvp, 0, p);
+ cnp->cn_flags |= PDIRUNLOCK;
+ }
return (0);
}
+ /*
+ * The postop attr handling is duplicated for each if case,
+ * because it should be done while dvp is locked (unlocking
+ * dvp is different for each case).
+ */
+
if (NFS_CMPFH(np, fhp, fhsize)) {
VREF(dvp);
newvp = dvp;
- } else {
+ if (v3) {
+ nfsm_postop_attr(newvp, attrflag);
+ nfsm_postop_attr(dvp, attrflag);
+ } else
+ nfsm_loadattr(newvp, (struct vattr *)0);
+ } else if (flags & ISDOTDOT) {
+ VOP_UNLOCK(dvp, 0, p);
+ cnp->cn_flags |= PDIRUNLOCK;
+
error = nfs_nget(dvp->v_mount, fhp, fhsize, &np);
if (error) {
+ if (vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p) == 0)
+ cnp->cn_flags &= ~PDIRUNLOCK;
m_freem(mrep);
return (error);
}
newvp = NFSTOV(np);
+
+ if (v3) {
+ nfsm_postop_attr(newvp, attrflag);
+ nfsm_postop_attr(dvp, attrflag);
+ } else
+ nfsm_loadattr(newvp, (struct vattr *)0);
+
+ if (lockparent && (flags & ISLASTCN)) {
+ if ((error = vn_lock(dvp, LK_EXCLUSIVE, p))) {
+ m_freem(mrep);
+ vput(newvp);
+ return error;
+ }
+ cnp->cn_flags &= ~PDIRUNLOCK;
+ }
+
+ } else {
+ error = nfs_nget(dvp->v_mount, fhp, fhsize, &np);
+ if (error) {
+ m_freem(mrep);
+ return error;
+ }
+ newvp = NFSTOV(np);
+ if (v3) {
+ nfsm_postop_attr(newvp, attrflag);
+ nfsm_postop_attr(dvp, attrflag);
+ } else
+ nfsm_loadattr(newvp, (struct vattr *)0);
+ if (!lockparent || !(flags & ISLASTCN)) {
+ VOP_UNLOCK(dvp, 0, p);
+ cnp->cn_flags |= PDIRUNLOCK;
+ }
}
- if (v3) {
- nfsm_postop_attr(newvp, attrflag);
- nfsm_postop_attr(dvp, attrflag);
- } else
- nfsm_loadattr(newvp, (struct vattr *)0);
if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN))
cnp->cn_flags |= SAVENAME;
if ((cnp->cn_flags & MAKEENTRY) &&
@@ -760,8 +848,24 @@ nfs_lookup(v)
*vpp = newvp;
nfsm_reqdone;
if (error) {
- if (newvp != NULLVP)
+ /*
+ * We get here only because of errors returned by
+ * the RPC. Otherwise we'll have returned above
+ * (the nfsm_* macros will jump to nfsm_reqdone
+ * on error).
+ */
+ if (error == ENOENT && (cnp->cn_flags & MAKEENTRY) &&
+ cnp->cn_nameiop != CREATE) {
+ if (VTONFS(dvp)->n_ctime == 0)
+ VTONFS(dvp)->n_ctime =
+ VTONFS(dvp)->n_vattr.va_mtime.tv_sec;
+ cache_enter(dvp, NULL, cnp);
+ }
+ if (newvp != NULLVP) {
vrele(newvp);
+ if (newvp != dvp)
+ VOP_UNLOCK(newvp, 0, p);
+ }
if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) &&
(flags & ISLASTCN) && error == ENOENT) {
if (dvp->v_mount->mnt_flag & MNT_RDONLY)
@@ -771,6 +875,7 @@ nfs_lookup(v)
}
if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN))
cnp->cn_flags |= SAVENAME;
+ *vpp = NULL;
}
return (error);
}