1
0
Fork 0

nfs_atomic_open(): prevent parallel nfs_lookup() on a negative hashed

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
steinar/wifi_calib_4_9_kernel
Al Viro 2016-07-05 09:49:21 -04:00
parent 00699ad857
commit c94c09535c
1 changed files with 25 additions and 3 deletions

View File

@ -1485,11 +1485,13 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
struct file *file, unsigned open_flags,
umode_t mode, int *opened)
{
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
struct nfs_open_context *ctx;
struct dentry *res;
struct iattr attr = { .ia_valid = ATTR_OPEN };
struct inode *inode;
unsigned int lookup_flags = 0;
bool switched = false;
int err;
/* Expect a negative dentry */
@ -1528,6 +1530,17 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
attr.ia_size = 0;
}
if (!(open_flags & O_CREAT) && !d_in_lookup(dentry)) {
d_drop(dentry);
switched = true;
dentry = d_alloc_parallel(dentry->d_parent,
&dentry->d_name, &wq);
if (IS_ERR(dentry))
return PTR_ERR(dentry);
if (unlikely(!d_in_lookup(dentry)))
return finish_no_open(file, dentry);
}
ctx = create_nfs_open_context(dentry, open_flags);
err = PTR_ERR(ctx);
if (IS_ERR(ctx))
@ -1563,14 +1576,23 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
trace_nfs_atomic_open_exit(dir, ctx, open_flags, err);
put_nfs_open_context(ctx);
out:
if (unlikely(switched)) {
d_lookup_done(dentry);
dput(dentry);
}
return err;
no_open:
res = nfs_lookup(dir, dentry, lookup_flags);
err = PTR_ERR(res);
if (switched) {
d_lookup_done(dentry);
if (!res)
res = dentry;
else
dput(dentry);
}
if (IS_ERR(res))
goto out;
return PTR_ERR(res);
return finish_no_open(file, res);
}
EXPORT_SYMBOL_GPL(nfs_atomic_open);