Skip to content

Commit

Permalink
NFSv4: Eliminate nfsv4 open race...
Browse files Browse the repository at this point in the history
 Make NFSv4 return the fully initialized file pointer with the
 stateid that it created in the lookup w/intent.

 Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
  • Loading branch information
Trond Myklebust authored and Trond Myklebust committed Oct 18, 2005
1 parent 834f2a4 commit 02a913a
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 102 deletions.
29 changes: 13 additions & 16 deletions fs/nfs/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -914,7 +914,6 @@ static int is_atomic_open(struct inode *dir, struct nameidata *nd)
static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
{
struct dentry *res = NULL;
struct inode *inode = NULL;
int error;

/* Check that we are indeed trying to open this file */
Expand All @@ -928,8 +927,10 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry
dentry->d_op = NFS_PROTO(dir)->dentry_ops;

/* Let vfs_create() deal with O_EXCL */
if (nd->intent.open.flags & O_EXCL)
goto no_entry;
if (nd->intent.open.flags & O_EXCL) {
d_add(dentry, NULL);
goto out;
}

/* Open the file on the server */
lock_kernel();
Expand All @@ -943,32 +944,28 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry

if (nd->intent.open.flags & O_CREAT) {
nfs_begin_data_update(dir);
inode = nfs4_atomic_open(dir, dentry, nd);
res = nfs4_atomic_open(dir, dentry, nd);
nfs_end_data_update(dir);
} else
inode = nfs4_atomic_open(dir, dentry, nd);
res = nfs4_atomic_open(dir, dentry, nd);
unlock_kernel();
if (IS_ERR(inode)) {
error = PTR_ERR(inode);
if (IS_ERR(res)) {
error = PTR_ERR(res);
switch (error) {
/* Make a negative dentry */
case -ENOENT:
inode = NULL;
break;
res = NULL;
goto out;
/* This turned out not to be a regular file */
case -ELOOP:
if (!(nd->intent.open.flags & O_NOFOLLOW))
goto no_open;
/* case -EISDIR: */
/* case -EINVAL: */
default:
res = ERR_PTR(error);
goto out;
}
}
no_entry:
res = d_add_unique(dentry, inode);
if (res != NULL)
} else if (res != NULL)
dentry = res;
nfs_renew_times(dentry);
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
Expand Down Expand Up @@ -1012,7 +1009,7 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
*/
lock_kernel();
verifier = nfs_save_change_attribute(dir);
ret = nfs4_open_revalidate(dir, dentry, openflags);
ret = nfs4_open_revalidate(dir, dentry, openflags, nd);
if (!ret)
nfs_set_verifier(dentry, verifier);
unlock_kernel();
Expand Down Expand Up @@ -1135,7 +1132,7 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode,

lock_kernel();
nfs_begin_data_update(dir);
error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags);
error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags, nd);
nfs_end_data_update(dir);
if (error != 0)
goto out_err;
Expand Down
2 changes: 1 addition & 1 deletion fs/nfs/nfs3proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ static int nfs3_proc_commit(struct nfs_write_data *cdata)
*/
static int
nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
int flags)
int flags, struct nameidata *nd)
{
struct nfs_fh fhandle;
struct nfs_fattr fattr;
Expand Down
4 changes: 2 additions & 2 deletions fs/nfs/nfs4_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,8 @@ extern int nfs4_proc_setclientid_confirm(struct nfs4_client *);
extern int nfs4_proc_async_renew(struct nfs4_client *);
extern int nfs4_proc_renew(struct nfs4_client *);
extern int nfs4_do_close(struct inode *inode, struct nfs4_state *state, mode_t mode);
extern struct inode *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *);
extern int nfs4_open_revalidate(struct inode *, struct dentry *, int);
extern struct dentry *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *);
extern int nfs4_open_revalidate(struct inode *, struct dentry *, int, struct nameidata *);

extern struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops;
extern struct nfs4_state_recovery_ops nfs4_network_partition_recovery_ops;
Expand Down
137 changes: 56 additions & 81 deletions fs/nfs/nfs4proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include <linux/nfs_page.h>
#include <linux/smp_lock.h>
#include <linux/namei.h>
#include <linux/mount.h>

#include "nfs4_fs.h"
#include "delegation.h"
Expand Down Expand Up @@ -947,12 +948,26 @@ int nfs4_do_close(struct inode *inode, struct nfs4_state *state, mode_t mode)
return status;
}

struct inode *
static void nfs4_intent_set_file(struct nameidata *nd, struct dentry *dentry, struct nfs4_state *state)
{
struct file *filp;

filp = lookup_instantiate_filp(nd, dentry, NULL);
if (!IS_ERR(filp)) {
struct nfs_open_context *ctx;
ctx = (struct nfs_open_context *)filp->private_data;
ctx->state = state;
} else
nfs4_close_state(state, nd->intent.open.flags);
}

struct dentry *
nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
{
struct iattr attr;
struct rpc_cred *cred;
struct nfs4_state *state;
struct dentry *res;

if (nd->flags & LOOKUP_CREATE) {
attr.ia_mode = nd->intent.open.create_mode;
Expand All @@ -966,16 +981,23 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)

cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0);
if (IS_ERR(cred))
return (struct inode *)cred;
return (struct dentry *)cred;
state = nfs4_do_open(dir, dentry, nd->intent.open.flags, &attr, cred);
put_rpccred(cred);
if (IS_ERR(state))
return (struct inode *)state;
return state->inode;
if (IS_ERR(state)) {
if (PTR_ERR(state) == -ENOENT)
d_add(dentry, NULL);
return (struct dentry *)state;
}
res = d_add_unique(dentry, state->inode);
if (res != NULL)
dentry = res;
nfs4_intent_set_file(nd, dentry, state);
return res;
}

int
nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags)
nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags, struct nameidata *nd)
{
struct rpc_cred *cred;
struct nfs4_state *state;
Expand All @@ -988,18 +1010,30 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags)
if (IS_ERR(state))
state = nfs4_do_open(dir, dentry, openflags, NULL, cred);
put_rpccred(cred);
if (state == ERR_PTR(-ENOENT) && dentry->d_inode == 0)
return 1;
if (IS_ERR(state))
return 0;
if (IS_ERR(state)) {
switch (PTR_ERR(state)) {
case -EPERM:
case -EACCES:
case -EDQUOT:
case -ENOSPC:
case -EROFS:
lookup_instantiate_filp(nd, (struct dentry *)state, NULL);
return 1;
case -ENOENT:
if (dentry->d_inode == NULL)
return 1;
}
goto out_drop;
}
inode = state->inode;
iput(inode);
if (inode == dentry->d_inode) {
iput(inode);
nfs4_intent_set_file(nd, dentry, state);
return 1;
}
d_drop(dentry);
nfs4_close_state(state, openflags);
iput(inode);
out_drop:
d_drop(dentry);
return 0;
}

Expand Down Expand Up @@ -1500,7 +1534,7 @@ static int nfs4_proc_commit(struct nfs_write_data *cdata)

static int
nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
int flags)
int flags, struct nameidata *nd)
{
struct nfs4_state *state;
struct rpc_cred *cred;
Expand All @@ -1522,13 +1556,13 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
struct nfs_fattr fattr;
status = nfs4_do_setattr(NFS_SERVER(dir), &fattr,
NFS_FH(state->inode), sattr, state);
if (status == 0) {
if (status == 0)
nfs_setattr_update_inode(state->inode, sattr);
goto out;
}
} else if (flags != 0)
goto out;
nfs4_close_state(state, flags);
}
if (status == 0 && nd != NULL && (nd->flags & LOOKUP_OPEN))
nfs4_intent_set_file(nd, dentry, state);
else
nfs4_close_state(state, flags);
out:
return status;
}
Expand Down Expand Up @@ -2175,65 +2209,6 @@ nfs4_proc_renew(struct nfs4_client *clp)
return 0;
}

/*
* We will need to arrange for the VFS layer to provide an atomic open.
* Until then, this open method is prone to inefficiency and race conditions
* due to the lookup, potential create, and open VFS calls from sys_open()
* placed on the wire.
*/
static int
nfs4_proc_file_open(struct inode *inode, struct file *filp)
{
struct dentry *dentry = filp->f_dentry;
struct nfs_open_context *ctx;
struct nfs4_state *state = NULL;
struct rpc_cred *cred;
int status = -ENOMEM;

dprintk("nfs4_proc_file_open: starting on (%.*s/%.*s)\n",
(int)dentry->d_parent->d_name.len,
dentry->d_parent->d_name.name,
(int)dentry->d_name.len, dentry->d_name.name);


/* Find our open stateid */
cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0);
if (IS_ERR(cred))
return PTR_ERR(cred);
ctx = alloc_nfs_open_context(dentry, cred);
put_rpccred(cred);
if (unlikely(ctx == NULL))
return -ENOMEM;
status = -EIO; /* ERACE actually */
state = nfs4_find_state(inode, cred, filp->f_mode);
if (unlikely(state == NULL))
goto no_state;
ctx->state = state;
nfs4_close_state(state, filp->f_mode);
ctx->mode = filp->f_mode;
nfs_file_set_open_context(filp, ctx);
put_nfs_open_context(ctx);
if (filp->f_mode & FMODE_WRITE)
nfs_begin_data_update(inode);
return 0;
no_state:
printk(KERN_WARNING "NFS: v4 raced in function %s\n", __FUNCTION__);
put_nfs_open_context(ctx);
return status;
}

/*
* Release our state
*/
static int
nfs4_proc_file_release(struct inode *inode, struct file *filp)
{
if (filp->f_mode & FMODE_WRITE)
nfs_end_data_update(inode);
nfs_file_clear_open_context(filp);
return 0;
}

static inline int nfs4_server_supports_acls(struct nfs_server *server)
{
return (server->caps & NFS_CAP_ACLS)
Expand Down Expand Up @@ -3145,8 +3120,8 @@ struct nfs_rpc_ops nfs_v4_clientops = {
.read_setup = nfs4_proc_read_setup,
.write_setup = nfs4_proc_write_setup,
.commit_setup = nfs4_proc_commit_setup,
.file_open = nfs4_proc_file_open,
.file_release = nfs4_proc_file_release,
.file_open = nfs_open,
.file_release = nfs_release,
.lock = nfs4_proc_lock,
.clear_acl_cache = nfs4_zap_acl_attr,
};
Expand Down
2 changes: 1 addition & 1 deletion fs/nfs/proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ static int nfs_proc_write(struct nfs_write_data *wdata)

static int
nfs_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
int flags)
int flags, struct nameidata *nd)
{
struct nfs_fh fhandle;
struct nfs_fattr fattr;
Expand Down
2 changes: 1 addition & 1 deletion include/linux/nfs_xdr.h
Original file line number Diff line number Diff line change
Expand Up @@ -712,7 +712,7 @@ struct nfs_rpc_ops {
int (*write) (struct nfs_write_data *);
int (*commit) (struct nfs_write_data *);
int (*create) (struct inode *, struct dentry *,
struct iattr *, int);
struct iattr *, int, struct nameidata *);
int (*remove) (struct inode *, struct qstr *);
int (*unlink_setup) (struct rpc_message *,
struct dentry *, struct qstr *);
Expand Down

0 comments on commit 02a913a

Please sign in to comment.