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 /fs/nfs/nfs4proc.c | |
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>
Diffstat (limited to 'fs/nfs/nfs4proc.c')
-rw-r--r-- | fs/nfs/nfs4proc.c | 137 |
1 files changed, 56 insertions, 81 deletions
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 | }; |