summaryrefslogtreecommitdiffstats
path: root/sys/kern/vfs_cache.c
diff options
context:
space:
mode:
authorjeff <jeff@FreeBSD.org>2005-06-17 01:05:13 +0000
committerjeff <jeff@FreeBSD.org>2005-06-17 01:05:13 +0000
commit9e1f35189b68835695794b672a198843d829907c (patch)
tree72784ea2a3ca62c1cc6546096eca973cfdf0aa82 /sys/kern/vfs_cache.c
parent6e375597a819dc30ff241ef5a9ffa73cf0abb741 (diff)
downloadFreeBSD-src-9e1f35189b68835695794b672a198843d829907c.zip
FreeBSD-src-9e1f35189b68835695794b672a198843d829907c.tar.gz
- Fix a leaked reference to a vnode via v_dd. We rely on cache_purge() and
cache_zap() to clear the v_dd pointers when a directory vnode is forcibly discarded. For this to work, all vnodes with v_dd pointers to a directory must also have name cache entries linked via v_cache_dst to that dvp otherwise we could not find them at cache_purge() time. The following code snipit could break this guarantee by unlinking a directory before fetching it's dotdot. The dotdot lookup would initialize the v_dd field of the unlinked directory which could never be cleared. To fix this we don't initialize v_dd for orphaned vnodes. printf("rmdir: %d\n", rmdir("../foo")); /* foo is cwd */ printf("chdir: %d\n", chdir("..")); printf("%s\n", getwd(NULL)); Sponsored by: Isilon Systems, Inc. Discovered by: kkenn Approved by: re (blanket vfs)
Diffstat (limited to 'sys/kern/vfs_cache.c')
-rw-r--r--sys/kern/vfs_cache.c12
1 files changed, 11 insertions, 1 deletions
diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c
index 88ecc35..4dfbe93 100644
--- a/sys/kern/vfs_cache.c
+++ b/sys/kern/vfs_cache.c
@@ -493,8 +493,18 @@ cache_enter(dvp, vp, cnp)
if (cnp->cn_namelen == 1) {
return;
}
+ /*
+ * For dotdot lookups only cache the v_dd pointer if the
+ * directory has a link back to its parent via v_cache_dst.
+ * Without this an unlinked directory would keep a soft
+ * reference to its parent which could not be NULLd at
+ * cache_purge() time.
+ */
if (cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.') {
- dvp->v_dd = vp;
+ CACHE_LOCK();
+ if (!TAILQ_EMPTY(&dvp->v_cache_dst))
+ dvp->v_dd = vp;
+ CACHE_UNLOCK();
return;
}
}
OpenPOWER on IntegriCloud