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
1 changed files with 11 additions and 9 deletions
|
@ -736,7 +736,12 @@ static struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry,
|
|||
}
|
||||
|
||||
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
|
||||
* 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:
|
||||
mutex_unlock(&MSDOS_SB(sb)->s_lock);
|
||||
dentry->d_time = dentry->d_parent->d_inode->i_version;
|
||||
dentry = d_splice_alias(inode, dentry);
|
||||
if (dentry)
|
||||
dentry->d_time = dentry->d_parent->d_inode->i_version;
|
||||
return dentry;
|
||||
|
||||
if (!inode)
|
||||
dentry->d_time = dir->i_version;
|
||||
return d_splice_alias(inode, dentry);
|
||||
error:
|
||||
mutex_unlock(&MSDOS_SB(sb)->s_lock);
|
||||
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;
|
||||
/* timestamp is already written, so mark_inode_dirty() is unneeded. */
|
||||
|
||||
dentry->d_time = dentry->d_parent->d_inode->i_version;
|
||||
d_instantiate(dentry, inode);
|
||||
out:
|
||||
mutex_unlock(&MSDOS_SB(sb)->s_lock);
|
||||
|
@ -824,6 +825,7 @@ static int vfat_rmdir(struct inode *dir, struct dentry *dentry)
|
|||
clear_nlink(inode);
|
||||
inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC;
|
||||
fat_detach(inode);
|
||||
dentry->d_time = dir->i_version;
|
||||
out:
|
||||
mutex_unlock(&MSDOS_SB(sb)->s_lock);
|
||||
|
||||
|
@ -849,6 +851,7 @@ static int vfat_unlink(struct inode *dir, struct dentry *dentry)
|
|||
clear_nlink(inode);
|
||||
inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC;
|
||||
fat_detach(inode);
|
||||
dentry->d_time = dir->i_version;
|
||||
out:
|
||||
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;
|
||||
/* timestamp is already written, so mark_inode_dirty() is unneeded. */
|
||||
|
||||
dentry->d_time = dentry->d_parent->d_inode->i_version;
|
||||
d_instantiate(dentry, inode);
|
||||
|
||||
mutex_unlock(&MSDOS_SB(sb)->s_lock);
|
||||
|
|
Loading…
Reference in a new issue