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>
This commit is contained in:
parent
e8577d1f03
commit
1ead0e79bf
|
@ -736,7 +736,12 @@ static struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry,
|
||||||
}
|
}
|
||||||
|
|
||||||
alias = d_find_alias(inode);
|
alias = d_find_alias(inode);
|
||||||
if (alias && !vfat_d_anon_disconn(alias)) {
|
/*
|
||||||
|
* Checking "alias->d_parent == dentry->d_parent" to make sure
|
||||||
|
* FS is not corrupted (especially double linked dir).
|
||||||
|
*/
|
||||||
|
if (alias && alias->d_parent == dentry->d_parent &&
|
||||||
|
!vfat_d_anon_disconn(alias)) {
|
||||||
/*
|
/*
|
||||||
* This inode has non anonymous-DCACHE_DISCONNECTED
|
* This inode has non anonymous-DCACHE_DISCONNECTED
|
||||||
* dentry. This means, the user did ->lookup() by an
|
* dentry. This means, the user did ->lookup() by an
|
||||||
|
@ -755,12 +760,9 @@ static struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry,
|
||||||
|
|
||||||
out:
|
out:
|
||||||
mutex_unlock(&MSDOS_SB(sb)->s_lock);
|
mutex_unlock(&MSDOS_SB(sb)->s_lock);
|
||||||
dentry->d_time = dentry->d_parent->d_inode->i_version;
|
if (!inode)
|
||||||
dentry = d_splice_alias(inode, dentry);
|
dentry->d_time = dir->i_version;
|
||||||
if (dentry)
|
return d_splice_alias(inode, dentry);
|
||||||
dentry->d_time = dentry->d_parent->d_inode->i_version;
|
|
||||||
return dentry;
|
|
||||||
|
|
||||||
error:
|
error:
|
||||||
mutex_unlock(&MSDOS_SB(sb)->s_lock);
|
mutex_unlock(&MSDOS_SB(sb)->s_lock);
|
||||||
return ERR_PTR(err);
|
return ERR_PTR(err);
|
||||||
|
@ -793,7 +795,6 @@ static int vfat_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||||
inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
|
inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
|
||||||
/* timestamp is already written, so mark_inode_dirty() is unneeded. */
|
/* timestamp is already written, so mark_inode_dirty() is unneeded. */
|
||||||
|
|
||||||
dentry->d_time = dentry->d_parent->d_inode->i_version;
|
|
||||||
d_instantiate(dentry, inode);
|
d_instantiate(dentry, inode);
|
||||||
out:
|
out:
|
||||||
mutex_unlock(&MSDOS_SB(sb)->s_lock);
|
mutex_unlock(&MSDOS_SB(sb)->s_lock);
|
||||||
|
@ -824,6 +825,7 @@ static int vfat_rmdir(struct inode *dir, struct dentry *dentry)
|
||||||
clear_nlink(inode);
|
clear_nlink(inode);
|
||||||
inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC;
|
inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC;
|
||||||
fat_detach(inode);
|
fat_detach(inode);
|
||||||
|
dentry->d_time = dir->i_version;
|
||||||
out:
|
out:
|
||||||
mutex_unlock(&MSDOS_SB(sb)->s_lock);
|
mutex_unlock(&MSDOS_SB(sb)->s_lock);
|
||||||
|
|
||||||
|
@ -849,6 +851,7 @@ static int vfat_unlink(struct inode *dir, struct dentry *dentry)
|
||||||
clear_nlink(inode);
|
clear_nlink(inode);
|
||||||
inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC;
|
inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC;
|
||||||
fat_detach(inode);
|
fat_detach(inode);
|
||||||
|
dentry->d_time = dir->i_version;
|
||||||
out:
|
out:
|
||||||
mutex_unlock(&MSDOS_SB(sb)->s_lock);
|
mutex_unlock(&MSDOS_SB(sb)->s_lock);
|
||||||
|
|
||||||
|
@ -889,7 +892,6 @@ static int vfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||||
inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
|
inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
|
||||||
/* timestamp is already written, so mark_inode_dirty() is unneeded. */
|
/* timestamp is already written, so mark_inode_dirty() is unneeded. */
|
||||||
|
|
||||||
dentry->d_time = dentry->d_parent->d_inode->i_version;
|
|
||||||
d_instantiate(dentry, inode);
|
d_instantiate(dentry, inode);
|
||||||
|
|
||||||
mutex_unlock(&MSDOS_SB(sb)->s_lock);
|
mutex_unlock(&MSDOS_SB(sb)->s_lock);
|
||||||
|
|
Loading…
Reference in New Issue