提交 05f5779c 编写于 作者: A Amir Goldstein 提交者: Zheng Zengkai

fsnotify: invalidate dcache before IN_DELETE event

stable inclusion
from stable-v5.10.96
commit 0b4e82403c84c88fb42972687774ae3a699d047d
bugzilla: https://gitee.com/openeuler/kernel/issues/I55NWB

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=0b4e82403c84c88fb42972687774ae3a699d047d

--------------------------------

commit a37d9a17 upstream.

Apparently, there are some applications that use IN_DELETE event as an
invalidation mechanism and expect that if they try to open a file with
the name reported with the delete event, that it should not contain the
content of the deleted file.

Commit 49246466 ("fsnotify: move fsnotify_nameremove() hook out of
d_delete()") moved the fsnotify delete hook before d_delete() so fsnotify
will have access to a positive dentry.

This allowed a race where opening the deleted file via cached dentry
is now possible after receiving the IN_DELETE event.

To fix the regression, create a new hook fsnotify_delete() that takes
the unlinked inode as an argument and use a helper d_delete_notify() to
pin the inode, so we can pass it to fsnotify_delete() after d_delete().

Backporting hint: this regression is from v5.3. Although patch will
apply with only trivial conflicts to v5.4 and v5.10, it won't build,
because fsnotify_delete() implementation is different in each of those
versions (see fsnotify_link()).

A follow up patch will fix the fsnotify_unlink/rmdir() calls in pseudo
filesystem that do not need to call d_delete().

Link: https://lore.kernel.org/r/20220120215305.282577-1-amir73il@gmail.comReported-by: NIvan Delalande <colona@arista.com>
Link: https://lore.kernel.org/linux-fsdevel/YeNyzoDM5hP5LtGW@visor/
Fixes: 49246466 ("fsnotify: move fsnotify_nameremove() hook out of d_delete()")
Cc: stable@vger.kernel.org # v5.3+
Signed-off-by: NAmir Goldstein <amir73il@gmail.com>
Signed-off-by: NJan Kara <jack@suse.cz>
Signed-off-by: NGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: NYu Liao <liaoyu15@huawei.com>
Reviewed-by: NWei Li <liwei391@huawei.com>
Signed-off-by: NZheng Zengkai <zhengzengkai@huawei.com>
上级 c352253d
...@@ -3103,10 +3103,8 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, ...@@ -3103,10 +3103,8 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
inode_lock(inode); inode_lock(inode);
err = btrfs_delete_subvolume(dir, dentry); err = btrfs_delete_subvolume(dir, dentry);
inode_unlock(inode); inode_unlock(inode);
if (!err) { if (!err)
fsnotify_rmdir(dir, dentry); d_delete_notify(dir, dentry);
d_delete(dentry);
}
out_dput: out_dput:
dput(dentry); dput(dentry);
......
...@@ -3715,13 +3715,12 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry) ...@@ -3715,13 +3715,12 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry)
dentry->d_inode->i_flags |= S_DEAD; dentry->d_inode->i_flags |= S_DEAD;
dont_mount(dentry); dont_mount(dentry);
detach_mounts(dentry); detach_mounts(dentry);
fsnotify_rmdir(dir, dentry);
out: out:
inode_unlock(dentry->d_inode); inode_unlock(dentry->d_inode);
dput(dentry); dput(dentry);
if (!error) if (!error)
d_delete(dentry); d_delete_notify(dir, dentry);
return error; return error;
} }
EXPORT_SYMBOL(vfs_rmdir); EXPORT_SYMBOL(vfs_rmdir);
...@@ -3831,7 +3830,6 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegate ...@@ -3831,7 +3830,6 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegate
if (!error) { if (!error) {
dont_mount(dentry); dont_mount(dentry);
detach_mounts(dentry); detach_mounts(dentry);
fsnotify_unlink(dir, dentry);
} }
} }
} }
...@@ -3839,9 +3837,11 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegate ...@@ -3839,9 +3837,11 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegate
inode_unlock(target); inode_unlock(target);
/* We don't d_delete() NFS sillyrenamed files--they still exist. */ /* We don't d_delete() NFS sillyrenamed files--they still exist. */
if (!error && !(dentry->d_flags & DCACHE_NFSFS_RENAMED)) { if (!error && dentry->d_flags & DCACHE_NFSFS_RENAMED) {
fsnotify_unlink(dir, dentry);
} else if (!error) {
fsnotify_link_count(target); fsnotify_link_count(target);
d_delete(dentry); d_delete_notify(dir, dentry);
} }
return error; return error;
......
...@@ -203,6 +203,42 @@ static inline void fsnotify_link(struct inode *dir, struct inode *inode, ...@@ -203,6 +203,42 @@ static inline void fsnotify_link(struct inode *dir, struct inode *inode,
fsnotify_name(dir, FS_CREATE, inode, &new_dentry->d_name, 0); fsnotify_name(dir, FS_CREATE, inode, &new_dentry->d_name, 0);
} }
/*
* fsnotify_delete - @dentry was unlinked and unhashed
*
* Caller must make sure that dentry->d_name is stable.
*
* Note: unlike fsnotify_unlink(), we have to pass also the unlinked inode
* as this may be called after d_delete() and old_dentry may be negative.
*/
static inline void fsnotify_delete(struct inode *dir, struct inode *inode,
struct dentry *dentry)
{
__u32 mask = FS_DELETE;
if (S_ISDIR(inode->i_mode))
mask |= FS_ISDIR;
fsnotify_name(dir, mask, inode, &dentry->d_name, 0);
}
/**
* d_delete_notify - delete a dentry and call fsnotify_delete()
* @dentry: The dentry to delete
*
* This helper is used to guaranty that the unlinked inode cannot be found
* by lookup of this name after fsnotify_delete() event has been delivered.
*/
static inline void d_delete_notify(struct inode *dir, struct dentry *dentry)
{
struct inode *inode = d_inode(dentry);
ihold(inode);
d_delete(dentry);
fsnotify_delete(dir, inode, dentry);
iput(inode);
}
/* /*
* fsnotify_unlink - 'name' was unlinked * fsnotify_unlink - 'name' was unlinked
* *
...@@ -210,10 +246,10 @@ static inline void fsnotify_link(struct inode *dir, struct inode *inode, ...@@ -210,10 +246,10 @@ static inline void fsnotify_link(struct inode *dir, struct inode *inode,
*/ */
static inline void fsnotify_unlink(struct inode *dir, struct dentry *dentry) static inline void fsnotify_unlink(struct inode *dir, struct dentry *dentry)
{ {
/* Expected to be called before d_delete() */ if (WARN_ON_ONCE(d_is_negative(dentry)))
WARN_ON_ONCE(d_is_negative(dentry)); return;
fsnotify_dirent(dir, dentry, FS_DELETE); fsnotify_delete(dir, d_inode(dentry), dentry);
} }
/* /*
...@@ -233,10 +269,10 @@ static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry) ...@@ -233,10 +269,10 @@ static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry)
*/ */
static inline void fsnotify_rmdir(struct inode *dir, struct dentry *dentry) static inline void fsnotify_rmdir(struct inode *dir, struct dentry *dentry)
{ {
/* Expected to be called before d_delete() */ if (WARN_ON_ONCE(d_is_negative(dentry)))
WARN_ON_ONCE(d_is_negative(dentry)); return;
fsnotify_dirent(dir, dentry, FS_DELETE | FS_ISDIR); fsnotify_delete(dir, d_inode(dentry), dentry);
} }
/* /*
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册