summaryrefslogtreecommitdiffstats
path: root/sys/kern/vfs_lookup.c
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2012-01-01 18:45:59 +0000
committerkib <kib@FreeBSD.org>2012-01-01 18:45:59 +0000
commitc3ff56243b9d716e74544bef440aa812f115faed (patch)
tree3be9dcc8513306b58cd8a6557f5f3ca233d27033 /sys/kern/vfs_lookup.c
parent1e9f485778ee7ae90cbcab204d6fd07a86a924c9 (diff)
downloadFreeBSD-src-c3ff56243b9d716e74544bef440aa812f115faed.zip
FreeBSD-src-c3ff56243b9d716e74544bef440aa812f115faed.tar.gz
Avoid double-unlock or double unreference for ndp->ni_dvp when the vnode dp
lock upgrade right after the 'success' label fails. In collaboration with: pho MFC after: 1 week
Diffstat (limited to 'sys/kern/vfs_lookup.c')
-rw-r--r--sys/kern/vfs_lookup.c21
1 files changed, 15 insertions, 6 deletions
diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c
index e2aad7c..29ad019 100644
--- a/sys/kern/vfs_lookup.c
+++ b/sys/kern/vfs_lookup.c
@@ -508,12 +508,14 @@ lookup(struct nameidata *ndp)
int dvfslocked; /* VFS Giant state for parent */
int tvfslocked;
int lkflags_save;
+ int ni_dvp_unlocked;
/*
* Setup: break out flag bits into variables.
*/
dvfslocked = (ndp->ni_cnd.cn_flags & GIANTHELD) != 0;
vfslocked = 0;
+ ni_dvp_unlocked = 0;
ndp->ni_cnd.cn_flags &= ~GIANTHELD;
wantparent = cnp->cn_flags & (LOCKPARENT | WANTPARENT);
KASSERT(cnp->cn_nameiop == LOOKUP || wantparent,
@@ -861,8 +863,10 @@ unionlookup:
/*
* Symlink code always expects an unlocked dvp.
*/
- if (ndp->ni_dvp != ndp->ni_vp)
+ if (ndp->ni_dvp != ndp->ni_vp) {
VOP_UNLOCK(ndp->ni_dvp, 0);
+ ni_dvp_unlocked = 1;
+ }
goto success;
}
@@ -909,14 +913,17 @@ nextname:
VREF(ndp->ni_startdir);
}
if (!wantparent) {
+ ni_dvp_unlocked = 2;
if (ndp->ni_dvp != dp)
vput(ndp->ni_dvp);
else
vrele(ndp->ni_dvp);
VFS_UNLOCK_GIANT(dvfslocked);
dvfslocked = 0;
- } else if ((cnp->cn_flags & LOCKPARENT) == 0 && ndp->ni_dvp != dp)
+ } else if ((cnp->cn_flags & LOCKPARENT) == 0 && ndp->ni_dvp != dp) {
VOP_UNLOCK(ndp->ni_dvp, 0);
+ ni_dvp_unlocked = 1;
+ }
if (cnp->cn_flags & AUDITVNODE1)
AUDIT_ARG_VNODE1(dp);
@@ -945,10 +952,12 @@ success:
return (0);
bad2:
- if (dp != ndp->ni_dvp)
- vput(ndp->ni_dvp);
- else
- vrele(ndp->ni_dvp);
+ if (ni_dvp_unlocked != 2) {
+ if (dp != ndp->ni_dvp && !ni_dvp_unlocked)
+ vput(ndp->ni_dvp);
+ else
+ vrele(ndp->ni_dvp);
+ }
bad:
if (!dpunlocked)
vput(dp);
OpenPOWER on IntegriCloud