Commit 1ead0e79bfedd4b563b8ea7c585ca3884b0c89a7
Committed by
Linus Torvalds
1 parent
e8577d1f03
Exists in
ti-lsk-linux-4.1.y
and in
10 other branches
fat: fix oops on corrupted vfat fs
a) don't bother with ->d_time for positives - we only check it for negatives anyway. b) make sure to set it at unlink and rmdir time - at *that* point soon-to-be negative dentry matches then-current directory contents c) don't go into renaming of old alias in vfat_lookup() unless it has the same parent (which it will, unless we are seeing corrupted image) [hirofumi@mail.parknet.co.jp: make change minimum, don't call d_move() for dir] Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> Cc: <stable@vger.kernel.org> [3.17.x] Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Showing 1 changed file with 11 additions and 9 deletions Side-by-side Diff
fs/fat/namei_vfat.c
... | ... | @@ -736,7 +736,12 @@ |
736 | 736 | } |
737 | 737 | |
738 | 738 | alias = d_find_alias(inode); |
739 | - if (alias && !vfat_d_anon_disconn(alias)) { | |
739 | + /* | |
740 | + * Checking "alias->d_parent == dentry->d_parent" to make sure | |
741 | + * FS is not corrupted (especially double linked dir). | |
742 | + */ | |
743 | + if (alias && alias->d_parent == dentry->d_parent && | |
744 | + !vfat_d_anon_disconn(alias)) { | |
740 | 745 | /* |
741 | 746 | * This inode has non anonymous-DCACHE_DISCONNECTED |
742 | 747 | * dentry. This means, the user did ->lookup() by an |
... | ... | @@ -755,12 +760,9 @@ |
755 | 760 | |
756 | 761 | out: |
757 | 762 | mutex_unlock(&MSDOS_SB(sb)->s_lock); |
758 | - dentry->d_time = dentry->d_parent->d_inode->i_version; | |
759 | - dentry = d_splice_alias(inode, dentry); | |
760 | - if (dentry) | |
761 | - dentry->d_time = dentry->d_parent->d_inode->i_version; | |
762 | - return dentry; | |
763 | - | |
763 | + if (!inode) | |
764 | + dentry->d_time = dir->i_version; | |
765 | + return d_splice_alias(inode, dentry); | |
764 | 766 | error: |
765 | 767 | mutex_unlock(&MSDOS_SB(sb)->s_lock); |
766 | 768 | return ERR_PTR(err); |
... | ... | @@ -793,7 +795,6 @@ |
793 | 795 | inode->i_mtime = inode->i_atime = inode->i_ctime = ts; |
794 | 796 | /* timestamp is already written, so mark_inode_dirty() is unneeded. */ |
795 | 797 | |
796 | - dentry->d_time = dentry->d_parent->d_inode->i_version; | |
797 | 798 | d_instantiate(dentry, inode); |
798 | 799 | out: |
799 | 800 | mutex_unlock(&MSDOS_SB(sb)->s_lock); |
... | ... | @@ -824,6 +825,7 @@ |
824 | 825 | clear_nlink(inode); |
825 | 826 | inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC; |
826 | 827 | fat_detach(inode); |
828 | + dentry->d_time = dir->i_version; | |
827 | 829 | out: |
828 | 830 | mutex_unlock(&MSDOS_SB(sb)->s_lock); |
829 | 831 | |
... | ... | @@ -849,6 +851,7 @@ |
849 | 851 | clear_nlink(inode); |
850 | 852 | inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC; |
851 | 853 | fat_detach(inode); |
854 | + dentry->d_time = dir->i_version; | |
852 | 855 | out: |
853 | 856 | mutex_unlock(&MSDOS_SB(sb)->s_lock); |
854 | 857 | |
... | ... | @@ -889,7 +892,6 @@ |
889 | 892 | inode->i_mtime = inode->i_atime = inode->i_ctime = ts; |
890 | 893 | /* timestamp is already written, so mark_inode_dirty() is unneeded. */ |
891 | 894 | |
892 | - dentry->d_time = dentry->d_parent->d_inode->i_version; | |
893 | 895 | d_instantiate(dentry, inode); |
894 | 896 | |
895 | 897 | mutex_unlock(&MSDOS_SB(sb)->s_lock); |