diff options
Diffstat (limited to 'fs/cifs/inode.c')
| -rw-r--r-- | fs/cifs/inode.c | 43 |
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 | */ | ||
| 756 | static bool | ||
| 757 | inode_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 */ |
| 758 | struct inode * | 773 | struct inode * |
| 759 | cifs_iget(struct super_block *sb, struct cifs_fattr *fattr) | 774 | cifs_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); |
