mirror of https://github.com/torvalds/linux.git
fs/ntfs3: cancle set bad inode after removing name fails
The reproducer uses a file0 on a ntfs3 file system with a corrupted i_link.
When renaming, the file0's inode is marked as a bad inode because the file
name cannot be deleted.
The underlying bug is that make_bad_inode() is called on a live inode.
In some cases it's "icache lookup finds a normal inode, d_splice_alias()
is called to attach it to dentry, while another thread decides to call
make_bad_inode() on it - that would evict it from icache, but we'd already
found it there earlier".
In some it's outright "we have an inode attached to dentry - that's how we
got it in the first place; let's call make_bad_inode() on it just for shits
and giggles".
Fixes: 78ab59fee0 ("fs/ntfs3: Rework file operations")
Reported-by: syzbot+1aa90f0eb1fc3e77d969@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=1aa90f0eb1fc3e77d969
Signed-off-by: Edward Adam Davis <eadavis@qq.com>
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
This commit is contained in:
parent
e841ecb139
commit
d99208b919
|
|
@ -3003,8 +3003,7 @@ int ni_add_name(struct ntfs_inode *dir_ni, struct ntfs_inode *ni,
|
||||||
* ni_rename - Remove one name and insert new name.
|
* ni_rename - Remove one name and insert new name.
|
||||||
*/
|
*/
|
||||||
int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni,
|
int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni,
|
||||||
struct ntfs_inode *ni, struct NTFS_DE *de, struct NTFS_DE *new_de,
|
struct ntfs_inode *ni, struct NTFS_DE *de, struct NTFS_DE *new_de)
|
||||||
bool *is_bad)
|
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
struct NTFS_DE *de2 = NULL;
|
struct NTFS_DE *de2 = NULL;
|
||||||
|
|
@ -3027,8 +3026,8 @@ int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni,
|
||||||
err = ni_add_name(new_dir_ni, ni, new_de);
|
err = ni_add_name(new_dir_ni, ni, new_de);
|
||||||
if (!err) {
|
if (!err) {
|
||||||
err = ni_remove_name(dir_ni, ni, de, &de2, &undo);
|
err = ni_remove_name(dir_ni, ni, de, &de2, &undo);
|
||||||
if (err && ni_remove_name(new_dir_ni, ni, new_de, &de2, &undo))
|
WARN_ON(err && ni_remove_name(new_dir_ni, ni, new_de, &de2,
|
||||||
*is_bad = true;
|
&undo));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -244,7 +244,7 @@ static int ntfs_rename(struct mnt_idmap *idmap, struct inode *dir,
|
||||||
struct ntfs_inode *ni = ntfs_i(inode);
|
struct ntfs_inode *ni = ntfs_i(inode);
|
||||||
struct inode *new_inode = d_inode(new_dentry);
|
struct inode *new_inode = d_inode(new_dentry);
|
||||||
struct NTFS_DE *de, *new_de;
|
struct NTFS_DE *de, *new_de;
|
||||||
bool is_same, is_bad;
|
bool is_same;
|
||||||
/*
|
/*
|
||||||
* de - memory of PATH_MAX bytes:
|
* de - memory of PATH_MAX bytes:
|
||||||
* [0-1024) - original name (dentry->d_name)
|
* [0-1024) - original name (dentry->d_name)
|
||||||
|
|
@ -313,12 +313,8 @@ static int ntfs_rename(struct mnt_idmap *idmap, struct inode *dir,
|
||||||
if (dir_ni != new_dir_ni)
|
if (dir_ni != new_dir_ni)
|
||||||
ni_lock_dir2(new_dir_ni);
|
ni_lock_dir2(new_dir_ni);
|
||||||
|
|
||||||
is_bad = false;
|
err = ni_rename(dir_ni, new_dir_ni, ni, de, new_de);
|
||||||
err = ni_rename(dir_ni, new_dir_ni, ni, de, new_de, &is_bad);
|
if (!err) {
|
||||||
if (is_bad) {
|
|
||||||
/* Restore after failed rename failed too. */
|
|
||||||
_ntfs_bad_inode(inode);
|
|
||||||
} else if (!err) {
|
|
||||||
simple_rename_timestamp(dir, dentry, new_dir, new_dentry);
|
simple_rename_timestamp(dir, dentry, new_dir, new_dentry);
|
||||||
mark_inode_dirty(inode);
|
mark_inode_dirty(inode);
|
||||||
mark_inode_dirty(dir);
|
mark_inode_dirty(dir);
|
||||||
|
|
|
||||||
|
|
@ -577,8 +577,7 @@ int ni_add_name(struct ntfs_inode *dir_ni, struct ntfs_inode *ni,
|
||||||
struct NTFS_DE *de);
|
struct NTFS_DE *de);
|
||||||
|
|
||||||
int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni,
|
int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni,
|
||||||
struct ntfs_inode *ni, struct NTFS_DE *de, struct NTFS_DE *new_de,
|
struct ntfs_inode *ni, struct NTFS_DE *de, struct NTFS_DE *new_de);
|
||||||
bool *is_bad);
|
|
||||||
|
|
||||||
bool ni_is_dirty(struct inode *inode);
|
bool ni_is_dirty(struct inode *inode);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue