mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-26 13:44:15 +08:00
fuse: readdirplus: fix RCU walk
Doing dput(parent) is not valid in RCU walk mode. In RCU mode it would probably be okay to update the parent flags, but it's actually not necessary most of the time... So only set the FUSE_I_ADVISE_RDPLUS flag on the parent when the entry was recently initialized by READDIRPLUS. This is achieved by setting FUSE_I_INIT_RDPLUS on entries added by READDIRPLUS and only dropping out of RCU mode if this flag is set. FUSE_I_INIT_RDPLUS is cleared once the FUSE_I_ADVISE_RDPLUS flag is set in the parent. Reported-by: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Miklos Szeredi <mszeredi@suse.cz> Cc: stable@vger.kernel.org
This commit is contained in:
parent
3c70b8eeda
commit
6314efee3c
@ -182,6 +182,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
|
||||
struct inode *inode;
|
||||
struct dentry *parent;
|
||||
struct fuse_conn *fc;
|
||||
struct fuse_inode *fi;
|
||||
int ret;
|
||||
|
||||
inode = ACCESS_ONCE(entry->d_inode);
|
||||
@ -228,7 +229,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
|
||||
if (!err && !outarg.nodeid)
|
||||
err = -ENOENT;
|
||||
if (!err) {
|
||||
struct fuse_inode *fi = get_fuse_inode(inode);
|
||||
fi = get_fuse_inode(inode);
|
||||
if (outarg.nodeid != get_node_id(inode)) {
|
||||
fuse_queue_forget(fc, forget, outarg.nodeid, 1);
|
||||
goto invalid;
|
||||
@ -246,8 +247,11 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
|
||||
attr_version);
|
||||
fuse_change_entry_timeout(entry, &outarg);
|
||||
} else if (inode) {
|
||||
fc = get_fuse_conn(inode);
|
||||
if (fc->readdirplus_auto) {
|
||||
fi = get_fuse_inode(inode);
|
||||
if (flags & LOOKUP_RCU) {
|
||||
if (test_bit(FUSE_I_INIT_RDPLUS, &fi->state))
|
||||
return -ECHILD;
|
||||
} else if (test_and_clear_bit(FUSE_I_INIT_RDPLUS, &fi->state)) {
|
||||
parent = dget_parent(entry);
|
||||
fuse_advise_use_readdirplus(parent->d_inode);
|
||||
dput(parent);
|
||||
@ -1292,6 +1296,8 @@ static int fuse_direntplus_link(struct file *file,
|
||||
}
|
||||
|
||||
found:
|
||||
if (fc->readdirplus_auto)
|
||||
set_bit(FUSE_I_INIT_RDPLUS, &get_fuse_inode(inode)->state);
|
||||
fuse_change_entry_timeout(dentry, o);
|
||||
|
||||
err = 0;
|
||||
|
@ -115,6 +115,8 @@ struct fuse_inode {
|
||||
enum {
|
||||
/** Advise readdirplus */
|
||||
FUSE_I_ADVISE_RDPLUS,
|
||||
/** Initialized with readdirplus */
|
||||
FUSE_I_INIT_RDPLUS,
|
||||
/** An operation changing file size is in progress */
|
||||
FUSE_I_SIZE_UNSTABLE,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user