OK, I've found it. It's caused by changes in d_invalidate().
Details:
After touch a ../b we have the following picture:
- P 3
b H P 0
foo H P 2 <-- cwd
a H P 0
Now we are doing cd /tmp/foo/a
cached_lookup() on foo gives
- P 3
b H P 0
foo H P 3 <-- cwd
a H P 0
d_invalidate() calls shrink_dcache_parent(). Result being
- P 3
b H P 0
foo H P 2 <-- cwd
Now, old d_invalidate() would decide that it can't do d_drop() since d_count
is greater than 1. New one drops it. Rest is trivial. We end up with the
following:
- P 4
b H P 0
foo - P 1 <-- cwd
foo H P 1
a H P 0
Now we do rm a and shit finally hits the fan:
lookup on a gives us
- P 4
b H P 0
foo - P 2 <-- cwd
a H P 1
foo H P 1
a H P 0
... and drop_replace_inodes along with d_delete() results in
- P 4
b H P 0
foo - P 2 <-- cwd
a H N 0
foo H P 1
a - P 0
i.e. it unhashes dentry with zero d_count _and_ leaves it positive
and leaves a new dentry hashed but negative.
cd .. simply does dput() on the unhashed dentry of foo. Nothing interesting -
d_count remains positive. Attempt of rmdir doesn't even touch the sucker -
lookup gets hashed version.
Summary: changes in d_invalidate() made in 2.1.132 are incorrect; we
can't unhash the sucker if it can get children in the future.
drop_replace_inodes() is *badly* broken since it doesn't check d_count on
aliases. Old d_invalidate() somewhat masked it, but attempt to open something
via the short name followed by unlink via long one would leave us with the
interesting situation on hands.
Fix: revert changes in d_invalidate(). I.e.
--- dcache.c Tue Jan 5 16:19:12 1999
+++ dcache.c.new Thu Jan 7 16:10:44 1999
@@ -169,7 +169,7 @@
/* Check whether to do a partial shrink_dcache */
if (!list_empty(&dentry->d_subdirs)) {
shrink_dcache_parent(dentry);
- if (!list_empty(&dentry->d_subdirs))
+ if (dentry->d_count>1)
return -EBUSY;
}
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/