diff options
author | Artur Grabowski <art@cvs.openbsd.org> | 2003-01-31 17:37:51 +0000 |
---|---|---|
committer | Artur Grabowski <art@cvs.openbsd.org> | 2003-01-31 17:37:51 +0000 |
commit | 1b0b18abe62afb1a61733a6f5891a98bfbea81ec (patch) | |
tree | 29dc896fd65580ea85012ef60aaf813858629e3f /sys/nfs/nfs_vnops.c | |
parent | 2bccdd51809becfe909c271f5fef4d380c663209 (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.c | 173 |
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); } |