Commit c94c0953 authored by Al Viro's avatar Al Viro

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

Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 00699ad8
...@@ -1485,11 +1485,13 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry, ...@@ -1485,11 +1485,13 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
struct file *file, unsigned open_flags, struct file *file, unsigned open_flags,
umode_t mode, int *opened) umode_t mode, int *opened)
{ {
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
struct nfs_open_context *ctx; struct nfs_open_context *ctx;
struct dentry *res; struct dentry *res;
struct iattr attr = { .ia_valid = ATTR_OPEN }; struct iattr attr = { .ia_valid = ATTR_OPEN };
struct inode *inode; struct inode *inode;
unsigned int lookup_flags = 0; unsigned int lookup_flags = 0;
bool switched = false;
int err; int err;
/* Expect a negative dentry */ /* Expect a negative dentry */
...@@ -1528,6 +1530,17 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry, ...@@ -1528,6 +1530,17 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
attr.ia_size = 0; 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); ctx = create_nfs_open_context(dentry, open_flags);
err = PTR_ERR(ctx); err = PTR_ERR(ctx);
if (IS_ERR(ctx)) if (IS_ERR(ctx))
...@@ -1563,14 +1576,23 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry, ...@@ -1563,14 +1576,23 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
trace_nfs_atomic_open_exit(dir, ctx, open_flags, err); trace_nfs_atomic_open_exit(dir, ctx, open_flags, err);
put_nfs_open_context(ctx); put_nfs_open_context(ctx);
out: out:
if (unlikely(switched)) {
d_lookup_done(dentry);
dput(dentry);
}
return err; return err;
no_open: no_open:
res = nfs_lookup(dir, dentry, lookup_flags); 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)) if (IS_ERR(res))
goto out; return PTR_ERR(res);
return finish_no_open(file, res); return finish_no_open(file, res);
} }
EXPORT_SYMBOL_GPL(nfs_atomic_open); EXPORT_SYMBOL_GPL(nfs_atomic_open);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment