diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2005-10-18 17:20:17 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2005-10-18 17:20:17 -0400 |
commit | 02a913a73b52071e93f4b76db3e86138d19efffd (patch) | |
tree | 1dc1abbd2d8f57a6fd593dd252d6d7ecc7c811c5 | |
parent | 834f2a4a1554dc5b2598038b3fe8703defcbe467 (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>
-rw-r--r-- | fs/nfs/dir.c | 29 | ||||
-rw-r--r-- | fs/nfs/nfs3proc.c | 2 | ||||
-rw-r--r-- | fs/nfs/nfs4_fs.h | 4 | ||||
-rw-r--r-- | fs/nfs/nfs4proc.c | 137 | ||||
-rw-r--r-- | fs/nfs/proc.c | 2 | ||||
-rw-r--r-- | include/linux/nfs_xdr.h | 2 |
6 files changed, 74 insertions, 102 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) | |||
914 | static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) | 914 | static 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) |
969 | no_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 | */ |
300 | static int | 300 | static int |
301 | nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, | 301 | nfs3_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 *); | |||
215 | extern int nfs4_proc_async_renew(struct nfs4_client *); | 215 | extern int nfs4_proc_async_renew(struct nfs4_client *); |
216 | extern int nfs4_proc_renew(struct nfs4_client *); | 216 | extern int nfs4_proc_renew(struct nfs4_client *); |
217 | extern int nfs4_do_close(struct inode *inode, struct nfs4_state *state, mode_t mode); | 217 | extern int nfs4_do_close(struct inode *inode, struct nfs4_state *state, mode_t mode); |
218 | extern struct inode *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *); | 218 | extern struct dentry *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *); |
219 | extern int nfs4_open_revalidate(struct inode *, struct dentry *, int); | 219 | extern int nfs4_open_revalidate(struct inode *, struct dentry *, int, struct nameidata *); |
220 | 220 | ||
221 | extern struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops; | 221 | extern struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops; |
222 | extern struct nfs4_state_recovery_ops nfs4_network_partition_recovery_ops; | 222 | extern 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 | ||
950 | struct inode * | 951 | static 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 | |||
964 | struct dentry * | ||
951 | nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd) | 965 | nfs4_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 | ||
977 | int | 999 | int |
978 | nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags) | 1000 | nfs4_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); | 1035 | out_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 | ||
1501 | static int | 1535 | static int |
1502 | nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, | 1536 | nfs4_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); |
1532 | out: | 1566 | out: |
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 | */ | ||
2184 | static int | ||
2185 | nfs4_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; | ||
2219 | no_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 | */ | ||
2228 | static int | ||
2229 | nfs4_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 | |||
2237 | static inline int nfs4_server_supports_acls(struct nfs_server *server) | 2212 | static 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 | ||
217 | static int | 217 | static int |
218 | nfs_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, | 218 | nfs_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; |
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 57efcc27f20b..60086dac11d5 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h | |||
@@ -712,7 +712,7 @@ struct nfs_rpc_ops { | |||
712 | int (*write) (struct nfs_write_data *); | 712 | int (*write) (struct nfs_write_data *); |
713 | int (*commit) (struct nfs_write_data *); | 713 | int (*commit) (struct nfs_write_data *); |
714 | int (*create) (struct inode *, struct dentry *, | 714 | int (*create) (struct inode *, struct dentry *, |
715 | struct iattr *, int); | 715 | struct iattr *, int, struct nameidata *); |
716 | int (*remove) (struct inode *, struct qstr *); | 716 | int (*remove) (struct inode *, struct qstr *); |
717 | int (*unlink_setup) (struct rpc_message *, | 717 | int (*unlink_setup) (struct rpc_message *, |
718 | struct dentry *, struct qstr *); | 718 | struct dentry *, struct qstr *); |