aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2005-10-18 17:20:17 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2005-10-18 17:20:17 -0400
commit02a913a73b52071e93f4b76db3e86138d19efffd (patch)
tree1dc1abbd2d8f57a6fd593dd252d6d7ecc7c811c5 /fs
parent834f2a4a1554dc5b2598038b3fe8703defcbe467 (diff)
NFSv4: Eliminate nfsv4 open race...
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>
Diffstat (limited to 'fs')
-rw-r--r--fs/nfs/dir.c29
-rw-r--r--fs/nfs/nfs3proc.c2
-rw-r--r--fs/nfs/nfs4_fs.h4
-rw-r--r--fs/nfs/nfs4proc.c137
-rw-r--r--fs/nfs/proc.c2
5 files changed, 73 insertions, 101 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index c70eabd6d179..a6e251f21fd8 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -914,7 +914,6 @@ static int is_atomic_open(struct inode *dir, struct nameidata *nd)
914static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) 914static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
915{ 915{
916 struct dentry *res = NULL; 916 struct dentry *res = NULL;
917 struct inode *inode = NULL;
918 int error; 917 int error;
919 918
920 /* Check that we are indeed trying to open this file */ 919 /* Check that we are indeed trying to open this file */
@@ -928,8 +927,10 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry
928 dentry->d_op = NFS_PROTO(dir)->dentry_ops; 927 dentry->d_op = NFS_PROTO(dir)->dentry_ops;
929 928
930 /* Let vfs_create() deal with O_EXCL */ 929 /* Let vfs_create() deal with O_EXCL */
931 if (nd->intent.open.flags & O_EXCL) 930 if (nd->intent.open.flags & O_EXCL) {
932 goto no_entry; 931 d_add(dentry, NULL);
932 goto out;
933 }
933 934
934 /* Open the file on the server */ 935 /* Open the file on the server */
935 lock_kernel(); 936 lock_kernel();
@@ -943,18 +944,18 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry
943 944
944 if (nd->intent.open.flags & O_CREAT) { 945 if (nd->intent.open.flags & O_CREAT) {
945 nfs_begin_data_update(dir); 946 nfs_begin_data_update(dir);
946 inode = nfs4_atomic_open(dir, dentry, nd); 947 res = nfs4_atomic_open(dir, dentry, nd);
947 nfs_end_data_update(dir); 948 nfs_end_data_update(dir);
948 } else 949 } else
949 inode = nfs4_atomic_open(dir, dentry, nd); 950 res = nfs4_atomic_open(dir, dentry, nd);
950 unlock_kernel(); 951 unlock_kernel();
951 if (IS_ERR(inode)) { 952 if (IS_ERR(res)) {
952 error = PTR_ERR(inode); 953 error = PTR_ERR(res);
953 switch (error) { 954 switch (error) {
954 /* Make a negative dentry */ 955 /* Make a negative dentry */
955 case -ENOENT: 956 case -ENOENT:
956 inode = NULL; 957 res = NULL;
957 break; 958 goto out;
958 /* This turned out not to be a regular file */ 959 /* This turned out not to be a regular file */
959 case -ELOOP: 960 case -ELOOP:
960 if (!(nd->intent.open.flags & O_NOFOLLOW)) 961 if (!(nd->intent.open.flags & O_NOFOLLOW))
@@ -962,13 +963,9 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry
962 /* case -EISDIR: */ 963 /* case -EISDIR: */
963 /* case -EINVAL: */ 964 /* case -EINVAL: */
964 default: 965 default:
965 res = ERR_PTR(error);
966 goto out; 966 goto out;
967 } 967 }
968 } 968 } else if (res != NULL)
969no_entry:
970 res = d_add_unique(dentry, inode);
971 if (res != NULL)
972 dentry = res; 969 dentry = res;
973 nfs_renew_times(dentry); 970 nfs_renew_times(dentry);
974 nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); 971 nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
@@ -1012,7 +1009,7 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
1012 */ 1009 */
1013 lock_kernel(); 1010 lock_kernel();
1014 verifier = nfs_save_change_attribute(dir); 1011 verifier = nfs_save_change_attribute(dir);
1015 ret = nfs4_open_revalidate(dir, dentry, openflags); 1012 ret = nfs4_open_revalidate(dir, dentry, openflags, nd);
1016 if (!ret) 1013 if (!ret)
1017 nfs_set_verifier(dentry, verifier); 1014 nfs_set_verifier(dentry, verifier);
1018 unlock_kernel(); 1015 unlock_kernel();
@@ -1135,7 +1132,7 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode,
1135 1132
1136 lock_kernel(); 1133 lock_kernel();
1137 nfs_begin_data_update(dir); 1134 nfs_begin_data_update(dir);
1138 error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags); 1135 error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags, nd);
1139 nfs_end_data_update(dir); 1136 nfs_end_data_update(dir);
1140 if (error != 0) 1137 if (error != 0)
1141 goto out_err; 1138 goto out_err;
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
index edc95514046d..df80477c5af4 100644
--- a/fs/nfs/nfs3proc.c
+++ b/fs/nfs/nfs3proc.c
@@ -299,7 +299,7 @@ static int nfs3_proc_commit(struct nfs_write_data *cdata)
299 */ 299 */
300static int 300static int
301nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, 301nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
302 int flags) 302 int flags, struct nameidata *nd)
303{ 303{
304 struct nfs_fh fhandle; 304 struct nfs_fh fhandle;
305 struct nfs_fattr fattr; 305 struct nfs_fattr fattr;
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index 2215cdee43ae..8a3788199052 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -215,8 +215,8 @@ extern int nfs4_proc_setclientid_confirm(struct nfs4_client *);
215extern int nfs4_proc_async_renew(struct nfs4_client *); 215extern int nfs4_proc_async_renew(struct nfs4_client *);
216extern int nfs4_proc_renew(struct nfs4_client *); 216extern int nfs4_proc_renew(struct nfs4_client *);
217extern int nfs4_do_close(struct inode *inode, struct nfs4_state *state, mode_t mode); 217extern int nfs4_do_close(struct inode *inode, struct nfs4_state *state, mode_t mode);
218extern struct inode *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *); 218extern struct dentry *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *);
219extern int nfs4_open_revalidate(struct inode *, struct dentry *, int); 219extern int nfs4_open_revalidate(struct inode *, struct dentry *, int, struct nameidata *);
220 220
221extern struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops; 221extern struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops;
222extern struct nfs4_state_recovery_ops nfs4_network_partition_recovery_ops; 222extern struct nfs4_state_recovery_ops nfs4_network_partition_recovery_ops;
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 35da15342e05..c9ecb8119632 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -47,6 +47,7 @@
47#include <linux/nfs_page.h> 47#include <linux/nfs_page.h>
48#include <linux/smp_lock.h> 48#include <linux/smp_lock.h>
49#include <linux/namei.h> 49#include <linux/namei.h>
50#include <linux/mount.h>
50 51
51#include "nfs4_fs.h" 52#include "nfs4_fs.h"
52#include "delegation.h" 53#include "delegation.h"
@@ -947,12 +948,26 @@ out:
947 return status; 948 return status;
948} 949}
949 950
950struct inode * 951static void nfs4_intent_set_file(struct nameidata *nd, struct dentry *dentry, struct nfs4_state *state)
952{
953 struct file *filp;
954
955 filp = lookup_instantiate_filp(nd, dentry, NULL);
956 if (!IS_ERR(filp)) {
957 struct nfs_open_context *ctx;
958 ctx = (struct nfs_open_context *)filp->private_data;
959 ctx->state = state;
960 } else
961 nfs4_close_state(state, nd->intent.open.flags);
962}
963
964struct dentry *
951nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd) 965nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
952{ 966{
953 struct iattr attr; 967 struct iattr attr;
954 struct rpc_cred *cred; 968 struct rpc_cred *cred;
955 struct nfs4_state *state; 969 struct nfs4_state *state;
970 struct dentry *res;
956 971
957 if (nd->flags & LOOKUP_CREATE) { 972 if (nd->flags & LOOKUP_CREATE) {
958 attr.ia_mode = nd->intent.open.create_mode; 973 attr.ia_mode = nd->intent.open.create_mode;
@@ -966,16 +981,23 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
966 981
967 cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0); 982 cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0);
968 if (IS_ERR(cred)) 983 if (IS_ERR(cred))
969 return (struct inode *)cred; 984 return (struct dentry *)cred;
970 state = nfs4_do_open(dir, dentry, nd->intent.open.flags, &attr, cred); 985 state = nfs4_do_open(dir, dentry, nd->intent.open.flags, &attr, cred);
971 put_rpccred(cred); 986 put_rpccred(cred);
972 if (IS_ERR(state)) 987 if (IS_ERR(state)) {
973 return (struct inode *)state; 988 if (PTR_ERR(state) == -ENOENT)
974 return state->inode; 989 d_add(dentry, NULL);
990 return (struct dentry *)state;
991 }
992 res = d_add_unique(dentry, state->inode);
993 if (res != NULL)
994 dentry = res;
995 nfs4_intent_set_file(nd, dentry, state);
996 return res;
975} 997}
976 998
977int 999int
978nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags) 1000nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags, struct nameidata *nd)
979{ 1001{
980 struct rpc_cred *cred; 1002 struct rpc_cred *cred;
981 struct nfs4_state *state; 1003 struct nfs4_state *state;
@@ -988,18 +1010,30 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags)
988 if (IS_ERR(state)) 1010 if (IS_ERR(state))
989 state = nfs4_do_open(dir, dentry, openflags, NULL, cred); 1011 state = nfs4_do_open(dir, dentry, openflags, NULL, cred);
990 put_rpccred(cred); 1012 put_rpccred(cred);
991 if (state == ERR_PTR(-ENOENT) && dentry->d_inode == 0) 1013 if (IS_ERR(state)) {
992 return 1; 1014 switch (PTR_ERR(state)) {
993 if (IS_ERR(state)) 1015 case -EPERM:
994 return 0; 1016 case -EACCES:
1017 case -EDQUOT:
1018 case -ENOSPC:
1019 case -EROFS:
1020 lookup_instantiate_filp(nd, (struct dentry *)state, NULL);
1021 return 1;
1022 case -ENOENT:
1023 if (dentry->d_inode == NULL)
1024 return 1;
1025 }
1026 goto out_drop;
1027 }
995 inode = state->inode; 1028 inode = state->inode;
1029 iput(inode);
996 if (inode == dentry->d_inode) { 1030 if (inode == dentry->d_inode) {
997 iput(inode); 1031 nfs4_intent_set_file(nd, dentry, state);
998 return 1; 1032 return 1;
999 } 1033 }
1000 d_drop(dentry);
1001 nfs4_close_state(state, openflags); 1034 nfs4_close_state(state, openflags);
1002 iput(inode); 1035out_drop:
1036 d_drop(dentry);
1003 return 0; 1037 return 0;
1004} 1038}
1005 1039
@@ -1500,7 +1534,7 @@ static int nfs4_proc_commit(struct nfs_write_data *cdata)
1500 1534
1501static int 1535static int
1502nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, 1536nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
1503 int flags) 1537 int flags, struct nameidata *nd)
1504{ 1538{
1505 struct nfs4_state *state; 1539 struct nfs4_state *state;
1506 struct rpc_cred *cred; 1540 struct rpc_cred *cred;
@@ -1522,13 +1556,13 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
1522 struct nfs_fattr fattr; 1556 struct nfs_fattr fattr;
1523 status = nfs4_do_setattr(NFS_SERVER(dir), &fattr, 1557 status = nfs4_do_setattr(NFS_SERVER(dir), &fattr,
1524 NFS_FH(state->inode), sattr, state); 1558 NFS_FH(state->inode), sattr, state);
1525 if (status == 0) { 1559 if (status == 0)
1526 nfs_setattr_update_inode(state->inode, sattr); 1560 nfs_setattr_update_inode(state->inode, sattr);
1527 goto out; 1561 }
1528 } 1562 if (status == 0 && nd != NULL && (nd->flags & LOOKUP_OPEN))
1529 } else if (flags != 0) 1563 nfs4_intent_set_file(nd, dentry, state);
1530 goto out; 1564 else
1531 nfs4_close_state(state, flags); 1565 nfs4_close_state(state, flags);
1532out: 1566out:
1533 return status; 1567 return status;
1534} 1568}
@@ -2175,65 +2209,6 @@ nfs4_proc_renew(struct nfs4_client *clp)
2175 return 0; 2209 return 0;
2176} 2210}
2177 2211
2178/*
2179 * We will need to arrange for the VFS layer to provide an atomic open.
2180 * Until then, this open method is prone to inefficiency and race conditions
2181 * due to the lookup, potential create, and open VFS calls from sys_open()
2182 * placed on the wire.
2183 */
2184static int
2185nfs4_proc_file_open(struct inode *inode, struct file *filp)
2186{
2187 struct dentry *dentry = filp->f_dentry;
2188 struct nfs_open_context *ctx;
2189 struct nfs4_state *state = NULL;
2190 struct rpc_cred *cred;
2191 int status = -ENOMEM;
2192
2193 dprintk("nfs4_proc_file_open: starting on (%.*s/%.*s)\n",
2194 (int)dentry->d_parent->d_name.len,
2195 dentry->d_parent->d_name.name,
2196 (int)dentry->d_name.len, dentry->d_name.name);
2197
2198
2199 /* Find our open stateid */
2200 cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0);
2201 if (IS_ERR(cred))
2202 return PTR_ERR(cred);
2203 ctx = alloc_nfs_open_context(dentry, cred);
2204 put_rpccred(cred);
2205 if (unlikely(ctx == NULL))
2206 return -ENOMEM;
2207 status = -EIO; /* ERACE actually */
2208 state = nfs4_find_state(inode, cred, filp->f_mode);
2209 if (unlikely(state == NULL))
2210 goto no_state;
2211 ctx->state = state;
2212 nfs4_close_state(state, filp->f_mode);
2213 ctx->mode = filp->f_mode;
2214 nfs_file_set_open_context(filp, ctx);
2215 put_nfs_open_context(ctx);
2216 if (filp->f_mode & FMODE_WRITE)
2217 nfs_begin_data_update(inode);
2218 return 0;
2219no_state:
2220 printk(KERN_WARNING "NFS: v4 raced in function %s\n", __FUNCTION__);
2221 put_nfs_open_context(ctx);
2222 return status;
2223}
2224
2225/*
2226 * Release our state
2227 */
2228static int
2229nfs4_proc_file_release(struct inode *inode, struct file *filp)
2230{
2231 if (filp->f_mode & FMODE_WRITE)
2232 nfs_end_data_update(inode);
2233 nfs_file_clear_open_context(filp);
2234 return 0;
2235}
2236
2237static inline int nfs4_server_supports_acls(struct nfs_server *server) 2212static inline int nfs4_server_supports_acls(struct nfs_server *server)
2238{ 2213{
2239 return (server->caps & NFS_CAP_ACLS) 2214 return (server->caps & NFS_CAP_ACLS)
@@ -3145,8 +3120,8 @@ struct nfs_rpc_ops nfs_v4_clientops = {
3145 .read_setup = nfs4_proc_read_setup, 3120 .read_setup = nfs4_proc_read_setup,
3146 .write_setup = nfs4_proc_write_setup, 3121 .write_setup = nfs4_proc_write_setup,
3147 .commit_setup = nfs4_proc_commit_setup, 3122 .commit_setup = nfs4_proc_commit_setup,
3148 .file_open = nfs4_proc_file_open, 3123 .file_open = nfs_open,
3149 .file_release = nfs4_proc_file_release, 3124 .file_release = nfs_release,
3150 .lock = nfs4_proc_lock, 3125 .lock = nfs4_proc_lock,
3151 .clear_acl_cache = nfs4_zap_acl_attr, 3126 .clear_acl_cache = nfs4_zap_acl_attr,
3152}; 3127};
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
index be23c3fb9260..8fef86523d7f 100644
--- a/fs/nfs/proc.c
+++ b/fs/nfs/proc.c
@@ -216,7 +216,7 @@ static int nfs_proc_write(struct nfs_write_data *wdata)
216 216
217static int 217static int
218nfs_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, 218nfs_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
219 int flags) 219 int flags, struct nameidata *nd)
220{ 220{
221 struct nfs_fh fhandle; 221 struct nfs_fh fhandle;
222 struct nfs_fattr fattr; 222 struct nfs_fattr fattr;