aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-06-08 13:34:03 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-06-08 13:34:03 -0400
commit32ba9c3fcab960f0b0d332c86ebcd2c4870d9bb8 (patch)
treecd3638359e7a635dc15856559ac01b14196c4ff7 /fs
parent0b35d326f813a654f0cd40f513bd95e3935921c1 (diff)
Revert "vfs: stop d_splice_alias creating directory aliases"
This reverts commit 7732a557b1342c6e6966efb5f07effcf99f56167 (and commit 3f50fff4dace23d3cfeb195d5cd4ee813cee68b7, which was a follow-up cleanup). We're chasing an elusive bug that Dave Jones can apparently reproduce using his system call fuzzer tool, and that looks like some kind of locking ordering problem on the directory i_mutex chain. Our i_mutex locking is rather complex, and depends on the topological ordering of the directories, which is why we have been very wary of splicing directory entries around. Of course, we really don't want to ever see aliased unconnected directories anyway, so none of this should ever happen, but this revert aims to basically get us back to a known older state. Bruce points to some of the previous discussion at http://marc.info/?i=<20110310105821.GE22723@ZenIV.linux.org.uk> and in particular a long post from Neil: http://marc.info/?i=<20110311150749.2fa2be66@notabene.brown> It should be noted that it's possible that Dave's problems come from other changes altohgether, including possibly just the fact that Dave constantly is teachning his fuzzer new tricks. So what appears to be a new bug could in fact be an old one that just gets newly triggered, but reverting these patches as "still under heavy discussion" is the right thing regardless. Requested-by: Al Viro <viro@zeniv.linux.org.uk> Acked-by: J. Bruce Fields <bfields@fieldses.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/dcache.c16
1 files changed, 10 insertions, 6 deletions
diff --git a/fs/dcache.c b/fs/dcache.c
index 85c9e2bff8e6..40469044088d 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -683,6 +683,8 @@ EXPORT_SYMBOL(dget_parent);
683/** 683/**
684 * d_find_alias - grab a hashed alias of inode 684 * d_find_alias - grab a hashed alias of inode
685 * @inode: inode in question 685 * @inode: inode in question
686 * @want_discon: flag, used by d_splice_alias, to request
687 * that only a DISCONNECTED alias be returned.
686 * 688 *
687 * If inode has a hashed alias, or is a directory and has any alias, 689 * If inode has a hashed alias, or is a directory and has any alias,
688 * acquire the reference to alias and return it. Otherwise return NULL. 690 * acquire the reference to alias and return it. Otherwise return NULL.
@@ -691,9 +693,10 @@ EXPORT_SYMBOL(dget_parent);
691 * of a filesystem. 693 * of a filesystem.
692 * 694 *
693 * If the inode has an IS_ROOT, DCACHE_DISCONNECTED alias, then prefer 695 * If the inode has an IS_ROOT, DCACHE_DISCONNECTED alias, then prefer
694 * any other hashed alias over that. 696 * any other hashed alias over that one unless @want_discon is set,
697 * in which case only return an IS_ROOT, DCACHE_DISCONNECTED alias.
695 */ 698 */
696static struct dentry *__d_find_alias(struct inode *inode) 699static struct dentry *__d_find_alias(struct inode *inode, int want_discon)
697{ 700{
698 struct dentry *alias, *discon_alias; 701 struct dentry *alias, *discon_alias;
699 702
@@ -705,7 +708,7 @@ again:
705 if (IS_ROOT(alias) && 708 if (IS_ROOT(alias) &&
706 (alias->d_flags & DCACHE_DISCONNECTED)) { 709 (alias->d_flags & DCACHE_DISCONNECTED)) {
707 discon_alias = alias; 710 discon_alias = alias;
708 } else { 711 } else if (!want_discon) {
709 __dget_dlock(alias); 712 __dget_dlock(alias);
710 spin_unlock(&alias->d_lock); 713 spin_unlock(&alias->d_lock);
711 return alias; 714 return alias;
@@ -736,7 +739,7 @@ struct dentry *d_find_alias(struct inode *inode)
736 739
737 if (!list_empty(&inode->i_dentry)) { 740 if (!list_empty(&inode->i_dentry)) {
738 spin_lock(&inode->i_lock); 741 spin_lock(&inode->i_lock);
739 de = __d_find_alias(inode); 742 de = __d_find_alias(inode, 0);
740 spin_unlock(&inode->i_lock); 743 spin_unlock(&inode->i_lock);
741 } 744 }
742 return de; 745 return de;
@@ -1647,8 +1650,9 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry)
1647 1650
1648 if (inode && S_ISDIR(inode->i_mode)) { 1651 if (inode && S_ISDIR(inode->i_mode)) {
1649 spin_lock(&inode->i_lock); 1652 spin_lock(&inode->i_lock);
1650 new = __d_find_any_alias(inode); 1653 new = __d_find_alias(inode, 1);
1651 if (new) { 1654 if (new) {
1655 BUG_ON(!(new->d_flags & DCACHE_DISCONNECTED));
1652 spin_unlock(&inode->i_lock); 1656 spin_unlock(&inode->i_lock);
1653 security_d_instantiate(new, inode); 1657 security_d_instantiate(new, inode);
1654 d_move(new, dentry); 1658 d_move(new, dentry);
@@ -2478,7 +2482,7 @@ struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode)
2478 struct dentry *alias; 2482 struct dentry *alias;
2479 2483
2480 /* Does an aliased dentry already exist? */ 2484 /* Does an aliased dentry already exist? */
2481 alias = __d_find_alias(inode); 2485 alias = __d_find_alias(inode, 0);
2482 if (alias) { 2486 if (alias) {
2483 actual = alias; 2487 actual = alias;
2484 write_seqlock(&rename_lock); 2488 write_seqlock(&rename_lock);