aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Layton <jlayton@redhat.com>2010-08-02 17:43:54 -0400
committerSteve French <sfrench@us.ibm.com>2010-08-05 13:17:50 -0400
commit5acfec2502cf60a91dc1959e476b588ecb3a1b8a (patch)
tree52742d8c6643d534b37d60aad918cbe38c695665
parent67b7626a0512d12e34b38ff45e32c693cf9c79a1 (diff)
cifs: reduce false positives with inode aliasing serverino autodisable
It turns out that not all directory inodes with dentries on the i_dentry list are unusable here. We only consider them unusable if they are still hashed or if they have a root dentry attached. Full disclosure -- this check is inherently racy. There's nothing that stops someone from slapping a new dentry onto this inode just after this check, or hashing an existing one that's already attached. So, this is really a "best effort" thing to work around misbehaving servers. Signed-off-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Steve French <sfrench@us.ibm.com>
-rw-r--r--fs/cifs/inode.c43
1 files changed, 31 insertions, 12 deletions
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index a15b3a9bbff4..dc4c47ab9588 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -732,15 +732,9 @@ cifs_find_inode(struct inode *inode, void *opaque)
732 if ((inode->i_mode & S_IFMT) != (fattr->cf_mode & S_IFMT)) 732 if ((inode->i_mode & S_IFMT) != (fattr->cf_mode & S_IFMT))
733 return 0; 733 return 0;
734 734
735 /* 735 /* if it's not a directory or has no dentries, then flag it */
736 * uh oh -- it's a directory. We can't use it since hardlinked dirs are 736 if (S_ISDIR(inode->i_mode) && !list_empty(&inode->i_dentry))
737 * verboten. Disable serverino and return it as if it were found, the
738 * caller can discard it, generate a uniqueid and retry the find
739 */
740 if (S_ISDIR(inode->i_mode) && !list_empty(&inode->i_dentry)) {
741 fattr->cf_flags |= CIFS_FATTR_INO_COLLISION; 737 fattr->cf_flags |= CIFS_FATTR_INO_COLLISION;
742 cifs_autodisable_serverino(CIFS_SB(inode->i_sb));
743 }
744 738
745 return 1; 739 return 1;
746} 740}
@@ -754,6 +748,27 @@ cifs_init_inode(struct inode *inode, void *opaque)
754 return 0; 748 return 0;
755} 749}
756 750
751/*
752 * walk dentry list for an inode and report whether it has aliases that
753 * are hashed. We use this to determine if a directory inode can actually
754 * be used.
755 */
756static bool
757inode_has_hashed_dentries(struct inode *inode)
758{
759 struct dentry *dentry;
760
761 spin_lock(&dcache_lock);
762 list_for_each_entry(dentry, &inode->i_dentry, d_alias) {
763 if (!d_unhashed(dentry) || IS_ROOT(dentry)) {
764 spin_unlock(&dcache_lock);
765 return true;
766 }
767 }
768 spin_unlock(&dcache_lock);
769 return false;
770}
771
757/* Given fattrs, get a corresponding inode */ 772/* Given fattrs, get a corresponding inode */
758struct inode * 773struct inode *
759cifs_iget(struct super_block *sb, struct cifs_fattr *fattr) 774cifs_iget(struct super_block *sb, struct cifs_fattr *fattr)
@@ -769,12 +784,16 @@ retry_iget5_locked:
769 784
770 inode = iget5_locked(sb, hash, cifs_find_inode, cifs_init_inode, fattr); 785 inode = iget5_locked(sb, hash, cifs_find_inode, cifs_init_inode, fattr);
771 if (inode) { 786 if (inode) {
772 /* was there a problematic inode number collision? */ 787 /* was there a potentially problematic inode collision? */
773 if (fattr->cf_flags & CIFS_FATTR_INO_COLLISION) { 788 if (fattr->cf_flags & CIFS_FATTR_INO_COLLISION) {
774 iput(inode);
775 fattr->cf_uniqueid = iunique(sb, ROOT_I);
776 fattr->cf_flags &= ~CIFS_FATTR_INO_COLLISION; 789 fattr->cf_flags &= ~CIFS_FATTR_INO_COLLISION;
777 goto retry_iget5_locked; 790
791 if (inode_has_hashed_dentries(inode)) {
792 cifs_autodisable_serverino(CIFS_SB(sb));
793 iput(inode);
794 fattr->cf_uniqueid = iunique(sb, ROOT_I);
795 goto retry_iget5_locked;
796 }
778 } 797 }
779 798
780 cifs_fattr_to_inode(inode, fattr); 799 cifs_fattr_to_inode(inode, fattr);