fuse: fix initial parallel dirops
If parallel dirops are enabled in FUSE_INIT reply, then first operation may
leave fi->mutex held.
Reported-by: syzbot <syzbot+3f7b29af1baa9d0a55be@syzkaller.appspotmail.com>
Fixes: 5c672ab3f0
("fuse: serialize dirops by default")
Cc: <stable@vger.kernel.org> # v4.7
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
This commit is contained in:
parent
e8f3bd773d
commit
63576c13bd
|
@ -355,11 +355,12 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
|
|||
struct inode *inode;
|
||||
struct dentry *newent;
|
||||
bool outarg_valid = true;
|
||||
bool locked;
|
||||
|
||||
fuse_lock_inode(dir);
|
||||
locked = fuse_lock_inode(dir);
|
||||
err = fuse_lookup_name(dir->i_sb, get_node_id(dir), &entry->d_name,
|
||||
&outarg, &inode);
|
||||
fuse_unlock_inode(dir);
|
||||
fuse_unlock_inode(dir, locked);
|
||||
if (err == -ENOENT) {
|
||||
outarg_valid = false;
|
||||
err = 0;
|
||||
|
@ -1340,6 +1341,7 @@ static int fuse_readdir(struct file *file, struct dir_context *ctx)
|
|||
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||
struct fuse_req *req;
|
||||
u64 attr_version = 0;
|
||||
bool locked;
|
||||
|
||||
if (is_bad_inode(inode))
|
||||
return -EIO;
|
||||
|
@ -1367,9 +1369,9 @@ static int fuse_readdir(struct file *file, struct dir_context *ctx)
|
|||
fuse_read_fill(req, file, ctx->pos, PAGE_SIZE,
|
||||
FUSE_READDIR);
|
||||
}
|
||||
fuse_lock_inode(inode);
|
||||
locked = fuse_lock_inode(inode);
|
||||
fuse_request_send(fc, req);
|
||||
fuse_unlock_inode(inode);
|
||||
fuse_unlock_inode(inode, locked);
|
||||
nbytes = req->out.args[0].size;
|
||||
err = req->out.h.error;
|
||||
fuse_put_request(fc, req);
|
||||
|
|
|
@ -975,8 +975,8 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
|
|||
|
||||
void fuse_set_initialized(struct fuse_conn *fc);
|
||||
|
||||
void fuse_unlock_inode(struct inode *inode);
|
||||
void fuse_lock_inode(struct inode *inode);
|
||||
void fuse_unlock_inode(struct inode *inode, bool locked);
|
||||
bool fuse_lock_inode(struct inode *inode);
|
||||
|
||||
int fuse_setxattr(struct inode *inode, const char *name, const void *value,
|
||||
size_t size, int flags);
|
||||
|
|
|
@ -357,15 +357,21 @@ int fuse_reverse_inval_inode(struct super_block *sb, u64 nodeid,
|
|||
return 0;
|
||||
}
|
||||
|
||||
void fuse_lock_inode(struct inode *inode)
|
||||
bool fuse_lock_inode(struct inode *inode)
|
||||
{
|
||||
if (!get_fuse_conn(inode)->parallel_dirops)
|
||||
bool locked = false;
|
||||
|
||||
if (!get_fuse_conn(inode)->parallel_dirops) {
|
||||
mutex_lock(&get_fuse_inode(inode)->mutex);
|
||||
locked = true;
|
||||
}
|
||||
|
||||
return locked;
|
||||
}
|
||||
|
||||
void fuse_unlock_inode(struct inode *inode)
|
||||
void fuse_unlock_inode(struct inode *inode, bool locked)
|
||||
{
|
||||
if (!get_fuse_conn(inode)->parallel_dirops)
|
||||
if (locked)
|
||||
mutex_unlock(&get_fuse_inode(inode)->mutex);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue