diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2007-07-05 19:02:21 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2007-07-10 23:40:43 -0400 |
commit | aac00a8d0a53097063da532cbdf0b8775a4dcd53 (patch) | |
tree | 4a83d1a25dd23d63cbcb0fe5c40b2f857ebcc19e /fs | |
parent | 3e309914a15333a5493058e4927e979c7434ae44 (diff) |
NFSv4: Check for the existence of a delegation in nfs4_open_prepare()
We should not be calling open() on an inode that has a delegation unless
we're doing a reclaim.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/nfs/nfs4proc.c | 195 |
1 files changed, 107 insertions, 88 deletions
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 05afb7ba3bc4..ea332e831d75 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
@@ -65,6 +65,7 @@ static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *) | |||
65 | static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry); | 65 | static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry); |
66 | static int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception); | 66 | static int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception); |
67 | static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs_client *clp); | 67 | static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs_client *clp); |
68 | static int _nfs4_do_access(struct inode *inode, struct rpc_cred *cred, int openflags); | ||
68 | 69 | ||
69 | /* Prevent leaks of NFSv4 errors into userland */ | 70 | /* Prevent leaks of NFSv4 errors into userland */ |
70 | int nfs4_map_errors(int err) | 71 | int nfs4_map_errors(int err) |
@@ -224,6 +225,7 @@ struct nfs4_opendata { | |||
224 | struct path path; | 225 | struct path path; |
225 | struct dentry *dir; | 226 | struct dentry *dir; |
226 | struct nfs4_state_owner *owner; | 227 | struct nfs4_state_owner *owner; |
228 | struct nfs4_state *state; | ||
227 | struct iattr attrs; | 229 | struct iattr attrs; |
228 | unsigned long timestamp; | 230 | unsigned long timestamp; |
229 | unsigned int rpc_done : 1; | 231 | unsigned int rpc_done : 1; |
@@ -296,6 +298,8 @@ static void nfs4_opendata_free(struct kref *kref) | |||
296 | struct nfs4_opendata, kref); | 298 | struct nfs4_opendata, kref); |
297 | 299 | ||
298 | nfs_free_seqid(p->o_arg.seqid); | 300 | nfs_free_seqid(p->o_arg.seqid); |
301 | if (p->state != NULL) | ||
302 | nfs4_put_open_state(p->state); | ||
299 | nfs4_put_state_owner(p->owner); | 303 | nfs4_put_state_owner(p->owner); |
300 | dput(p->dir); | 304 | dput(p->dir); |
301 | dput(p->path.dentry); | 305 | dput(p->path.dentry); |
@@ -320,6 +324,15 @@ static int nfs4_wait_for_completion_rpc_task(struct rpc_task *task) | |||
320 | return ret; | 324 | return ret; |
321 | } | 325 | } |
322 | 326 | ||
327 | static int can_open_delegated(struct nfs_delegation *delegation, mode_t open_flags) | ||
328 | { | ||
329 | if ((delegation->type & open_flags) != open_flags) | ||
330 | return 0; | ||
331 | if (delegation->flags & NFS_DELEGATION_NEED_RECLAIM) | ||
332 | return 0; | ||
333 | return 1; | ||
334 | } | ||
335 | |||
323 | static void update_open_stateflags(struct nfs4_state *state, mode_t open_flags) | 336 | static void update_open_stateflags(struct nfs4_state *state, mode_t open_flags) |
324 | { | 337 | { |
325 | switch (open_flags) { | 338 | switch (open_flags) { |
@@ -380,6 +393,65 @@ static void update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_sta | |||
380 | spin_unlock(&state->owner->so_lock); | 393 | spin_unlock(&state->owner->so_lock); |
381 | } | 394 | } |
382 | 395 | ||
396 | static void nfs4_return_incompatible_delegation(struct inode *inode, mode_t open_flags) | ||
397 | { | ||
398 | struct nfs_delegation *delegation; | ||
399 | |||
400 | rcu_read_lock(); | ||
401 | delegation = rcu_dereference(NFS_I(inode)->delegation); | ||
402 | if (delegation == NULL || (delegation->type & open_flags) == open_flags) { | ||
403 | rcu_read_unlock(); | ||
404 | return; | ||
405 | } | ||
406 | rcu_read_unlock(); | ||
407 | nfs_inode_return_delegation(inode); | ||
408 | } | ||
409 | |||
410 | static struct nfs4_state *nfs4_try_open_delegated(struct nfs4_opendata *opendata) | ||
411 | { | ||
412 | struct nfs4_state *state = opendata->state; | ||
413 | struct nfs_inode *nfsi = NFS_I(state->inode); | ||
414 | struct nfs_delegation *delegation; | ||
415 | int open_mode = opendata->o_arg.open_flags & (FMODE_READ|FMODE_WRITE|O_EXCL); | ||
416 | nfs4_stateid stateid; | ||
417 | int ret = -EAGAIN; | ||
418 | |||
419 | rcu_read_lock(); | ||
420 | delegation = rcu_dereference(nfsi->delegation); | ||
421 | if (delegation == NULL) | ||
422 | goto out_unlock; | ||
423 | for (;;) { | ||
424 | if (!can_open_delegated(delegation, open_mode)) | ||
425 | break; | ||
426 | /* Save the delegation */ | ||
427 | memcpy(stateid.data, delegation->stateid.data, sizeof(stateid.data)); | ||
428 | rcu_read_unlock(); | ||
429 | lock_kernel(); | ||
430 | ret = _nfs4_do_access(state->inode, state->owner->so_cred, open_mode); | ||
431 | unlock_kernel(); | ||
432 | if (ret != 0) | ||
433 | goto out; | ||
434 | ret = -EAGAIN; | ||
435 | rcu_read_lock(); | ||
436 | delegation = rcu_dereference(nfsi->delegation); | ||
437 | if (delegation == NULL) | ||
438 | break; | ||
439 | /* Is the delegation still valid? */ | ||
440 | if (memcmp(stateid.data, delegation->stateid.data, sizeof(stateid.data)) != 0) | ||
441 | continue; | ||
442 | rcu_read_unlock(); | ||
443 | update_open_stateid(state, NULL, &stateid, open_mode); | ||
444 | goto out_return_state; | ||
445 | } | ||
446 | out_unlock: | ||
447 | rcu_read_unlock(); | ||
448 | out: | ||
449 | return ERR_PTR(ret); | ||
450 | out_return_state: | ||
451 | atomic_inc(&state->count); | ||
452 | return state; | ||
453 | } | ||
454 | |||
383 | static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data) | 455 | static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data) |
384 | { | 456 | { |
385 | struct inode *inode; | 457 | struct inode *inode; |
@@ -388,6 +460,11 @@ static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data | |||
388 | nfs4_stateid *deleg_stateid = NULL; | 460 | nfs4_stateid *deleg_stateid = NULL; |
389 | int ret; | 461 | int ret; |
390 | 462 | ||
463 | if (!data->rpc_done) { | ||
464 | state = nfs4_try_open_delegated(data); | ||
465 | goto out; | ||
466 | } | ||
467 | |||
391 | ret = -EAGAIN; | 468 | ret = -EAGAIN; |
392 | if (!(data->f_attr.valid & NFS_ATTR_FATTR)) | 469 | if (!(data->f_attr.valid & NFS_ATTR_FATTR)) |
393 | goto err; | 470 | goto err; |
@@ -423,6 +500,7 @@ static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data | |||
423 | update_open_stateid(state, &data->o_res.stateid, deleg_stateid, data->o_arg.open_flags); | 500 | update_open_stateid(state, &data->o_res.stateid, deleg_stateid, data->o_arg.open_flags); |
424 | rcu_read_unlock(); | 501 | rcu_read_unlock(); |
425 | iput(inode); | 502 | iput(inode); |
503 | out: | ||
426 | return state; | 504 | return state; |
427 | err_put_inode: | 505 | err_put_inode: |
428 | iput(inode); | 506 | iput(inode); |
@@ -690,6 +768,23 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata) | |||
690 | 768 | ||
691 | if (nfs_wait_on_sequence(data->o_arg.seqid, task) != 0) | 769 | if (nfs_wait_on_sequence(data->o_arg.seqid, task) != 0) |
692 | return; | 770 | return; |
771 | /* | ||
772 | * Check if we still need to send an OPEN call, or if we can use | ||
773 | * a delegation instead. | ||
774 | */ | ||
775 | if (data->state != NULL) { | ||
776 | struct nfs_delegation *delegation; | ||
777 | |||
778 | rcu_read_lock(); | ||
779 | delegation = rcu_dereference(NFS_I(data->state->inode)->delegation); | ||
780 | if (delegation != NULL && | ||
781 | (delegation->flags & NFS_DELEGATION_NEED_RECLAIM) == 0) { | ||
782 | rcu_read_unlock(); | ||
783 | task->tk_action = NULL; | ||
784 | return; | ||
785 | } | ||
786 | rcu_read_unlock(); | ||
787 | } | ||
693 | /* Update sequence id. */ | 788 | /* Update sequence id. */ |
694 | data->o_arg.id = sp->so_owner_id.id; | 789 | data->o_arg.id = sp->so_owner_id.id; |
695 | data->o_arg.clientid = sp->so_client->cl_clientid; | 790 | data->o_arg.clientid = sp->so_client->cl_clientid; |
@@ -907,90 +1002,6 @@ static int nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *sta | |||
907 | } | 1002 | } |
908 | 1003 | ||
909 | /* | 1004 | /* |
910 | * Returns a referenced nfs4_state if there is an open delegation on the file | ||
911 | */ | ||
912 | static int _nfs4_open_delegated(struct inode *inode, int flags, struct rpc_cred *cred, struct nfs4_state **res) | ||
913 | { | ||
914 | struct nfs_delegation *delegation; | ||
915 | struct nfs_server *server = NFS_SERVER(inode); | ||
916 | struct nfs_client *clp = server->nfs_client; | ||
917 | struct nfs_inode *nfsi = NFS_I(inode); | ||
918 | struct nfs4_state_owner *sp = NULL; | ||
919 | struct nfs4_state *state = NULL; | ||
920 | int open_flags = flags & (FMODE_READ|FMODE_WRITE); | ||
921 | int err; | ||
922 | |||
923 | err = -ENOMEM; | ||
924 | if (!(sp = nfs4_get_state_owner(server, cred))) { | ||
925 | dprintk("%s: nfs4_get_state_owner failed!\n", __FUNCTION__); | ||
926 | return err; | ||
927 | } | ||
928 | err = nfs4_recover_expired_lease(server); | ||
929 | if (err != 0) | ||
930 | goto out_put_state_owner; | ||
931 | /* Protect against reboot recovery - NOTE ORDER! */ | ||
932 | down_read(&clp->cl_sem); | ||
933 | /* Protect against delegation recall */ | ||
934 | down_read(&nfsi->rwsem); | ||
935 | delegation = NFS_I(inode)->delegation; | ||
936 | err = -ENOENT; | ||
937 | if (delegation == NULL || (delegation->type & open_flags) != open_flags) | ||
938 | goto out_err; | ||
939 | err = -ENOMEM; | ||
940 | state = nfs4_get_open_state(inode, sp); | ||
941 | if (state == NULL) | ||
942 | goto out_err; | ||
943 | |||
944 | err = -ENOENT; | ||
945 | if ((state->state & open_flags) == open_flags) { | ||
946 | spin_lock(&inode->i_lock); | ||
947 | update_open_stateflags(state, open_flags); | ||
948 | spin_unlock(&inode->i_lock); | ||
949 | goto out_ok; | ||
950 | } else if (state->state != 0) | ||
951 | goto out_put_open_state; | ||
952 | |||
953 | lock_kernel(); | ||
954 | err = _nfs4_do_access(inode, cred, open_flags); | ||
955 | unlock_kernel(); | ||
956 | if (err != 0) | ||
957 | goto out_put_open_state; | ||
958 | update_open_stateid(state, NULL, &delegation->stateid, open_flags); | ||
959 | out_ok: | ||
960 | nfs4_put_state_owner(sp); | ||
961 | up_read(&nfsi->rwsem); | ||
962 | up_read(&clp->cl_sem); | ||
963 | *res = state; | ||
964 | return 0; | ||
965 | out_put_open_state: | ||
966 | nfs4_put_open_state(state); | ||
967 | out_err: | ||
968 | up_read(&nfsi->rwsem); | ||
969 | up_read(&clp->cl_sem); | ||
970 | if (err != -EACCES) | ||
971 | nfs_inode_return_delegation(inode); | ||
972 | out_put_state_owner: | ||
973 | nfs4_put_state_owner(sp); | ||
974 | return err; | ||
975 | } | ||
976 | |||
977 | static struct nfs4_state *nfs4_open_delegated(struct inode *inode, int flags, struct rpc_cred *cred) | ||
978 | { | ||
979 | struct nfs4_exception exception = { }; | ||
980 | struct nfs4_state *res = ERR_PTR(-EIO); | ||
981 | int err; | ||
982 | |||
983 | do { | ||
984 | err = _nfs4_open_delegated(inode, flags, cred, &res); | ||
985 | if (err == 0) | ||
986 | break; | ||
987 | res = ERR_PTR(nfs4_handle_exception(NFS_SERVER(inode), | ||
988 | err, &exception)); | ||
989 | } while (exception.retry); | ||
990 | return res; | ||
991 | } | ||
992 | |||
993 | /* | ||
994 | * on an EXCLUSIVE create, the server should send back a bitmask with FATTR4-* | 1005 | * on an EXCLUSIVE create, the server should send back a bitmask with FATTR4-* |
995 | * fields corresponding to attributes that were used to store the verifier. | 1006 | * fields corresponding to attributes that were used to store the verifier. |
996 | * Make sure we clobber those fields in the later setattr call | 1007 | * Make sure we clobber those fields in the later setattr call |
@@ -1016,7 +1027,7 @@ static int _nfs4_do_open(struct inode *dir, struct path *path, int flags, struct | |||
1016 | struct nfs_server *server = NFS_SERVER(dir); | 1027 | struct nfs_server *server = NFS_SERVER(dir); |
1017 | struct nfs_client *clp = server->nfs_client; | 1028 | struct nfs_client *clp = server->nfs_client; |
1018 | struct nfs4_opendata *opendata; | 1029 | struct nfs4_opendata *opendata; |
1019 | int status; | 1030 | int status; |
1020 | 1031 | ||
1021 | /* Protect against reboot recovery conflicts */ | 1032 | /* Protect against reboot recovery conflicts */ |
1022 | status = -ENOMEM; | 1033 | status = -ENOMEM; |
@@ -1027,12 +1038,17 @@ static int _nfs4_do_open(struct inode *dir, struct path *path, int flags, struct | |||
1027 | status = nfs4_recover_expired_lease(server); | 1038 | status = nfs4_recover_expired_lease(server); |
1028 | if (status != 0) | 1039 | if (status != 0) |
1029 | goto err_put_state_owner; | 1040 | goto err_put_state_owner; |
1041 | if (path->dentry->d_inode != NULL) | ||
1042 | nfs4_return_incompatible_delegation(path->dentry->d_inode, flags & (FMODE_READ|FMODE_WRITE)); | ||
1030 | down_read(&clp->cl_sem); | 1043 | down_read(&clp->cl_sem); |
1031 | status = -ENOMEM; | 1044 | status = -ENOMEM; |
1032 | opendata = nfs4_opendata_alloc(path, sp, flags, sattr); | 1045 | opendata = nfs4_opendata_alloc(path, sp, flags, sattr); |
1033 | if (opendata == NULL) | 1046 | if (opendata == NULL) |
1034 | goto err_release_rwsem; | 1047 | goto err_release_rwsem; |
1035 | 1048 | ||
1049 | if (path->dentry->d_inode != NULL) | ||
1050 | opendata->state = nfs4_get_open_state(path->dentry->d_inode, sp); | ||
1051 | |||
1036 | status = _nfs4_proc_open(opendata); | 1052 | status = _nfs4_proc_open(opendata); |
1037 | if (status != 0) | 1053 | if (status != 0) |
1038 | goto err_opendata_put; | 1054 | goto err_opendata_put; |
@@ -1099,6 +1115,11 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir, struct path *path, int | |||
1099 | exception.retry = 1; | 1115 | exception.retry = 1; |
1100 | continue; | 1116 | continue; |
1101 | } | 1117 | } |
1118 | if (status == -EAGAIN) { | ||
1119 | /* We must have found a delegation */ | ||
1120 | exception.retry = 1; | ||
1121 | continue; | ||
1122 | } | ||
1102 | res = ERR_PTR(nfs4_handle_exception(NFS_SERVER(dir), | 1123 | res = ERR_PTR(nfs4_handle_exception(NFS_SERVER(dir), |
1103 | status, &exception)); | 1124 | status, &exception)); |
1104 | } while (exception.retry); | 1125 | } while (exception.retry); |
@@ -1390,9 +1411,7 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags, st | |||
1390 | cred = rpcauth_lookupcred(NFS_CLIENT(dir)->cl_auth, 0); | 1411 | cred = rpcauth_lookupcred(NFS_CLIENT(dir)->cl_auth, 0); |
1391 | if (IS_ERR(cred)) | 1412 | if (IS_ERR(cred)) |
1392 | return PTR_ERR(cred); | 1413 | return PTR_ERR(cred); |
1393 | state = nfs4_open_delegated(dentry->d_inode, openflags, cred); | 1414 | state = nfs4_do_open(dir, &path, openflags, NULL, cred); |
1394 | if (IS_ERR(state)) | ||
1395 | state = nfs4_do_open(dir, &path, openflags, NULL, cred); | ||
1396 | put_rpccred(cred); | 1415 | put_rpccred(cred); |
1397 | if (IS_ERR(state)) { | 1416 | if (IS_ERR(state)) { |
1398 | switch (PTR_ERR(state)) { | 1417 | switch (PTR_ERR(state)) { |