diff options
author | Chuck Lever <chuck.lever@oracle.com> | 2012-05-29 13:56:37 -0400 |
---|---|---|
committer | J. Bruce Fields <bfields@redhat.com> | 2012-06-20 08:59:40 -0400 |
commit | 7df302f75ee28a6a87436e93b625ef60d37d098e (patch) | |
tree | 11fcb3dd3b30eb6f42167bd7bf671cda7361933d /fs/nfsd | |
parent | 2411967305dbfb8930b9b9c11f55f6c1ef7361e1 (diff) |
NFSD: TEST_STATEID should not return NFS4ERR_STALE_STATEID
According to RFC 5661, the TEST_STATEID operation is not allowed to
return NFS4ERR_STALE_STATEID. In addition, RFC 5661 says:
15.1.16.5. NFS4ERR_STALE_STATEID (Error Code 10023)
A stateid generated by an earlier server instance was used. This
error is moot in NFSv4.1 because all operations that take a stateid
MUST be preceded by the SEQUENCE operation, and the earlier server
instance is detected by the session infrastructure that supports
SEQUENCE.
I triggered NFS4ERR_STALE_STATEID while testing the Linux client's
NOGRACE recovery. Bruce suggested an additional test that could be
useful to client developers.
Lastly, RFC 5661, section 18.48.3 has this:
o Special stateids are always considered invalid (they result in the
error code NFS4ERR_BAD_STATEID).
An explicit check is made for those state IDs to avoid printk noise.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs/nfsd')
-rw-r--r-- | fs/nfsd/nfs4state.c | 22 | ||||
-rw-r--r-- | fs/nfsd/state.h | 1 |
2 files changed, 16 insertions, 7 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 8b80a10d4fc5..59b9efc9d69b 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
@@ -38,6 +38,7 @@ | |||
38 | #include <linux/namei.h> | 38 | #include <linux/namei.h> |
39 | #include <linux/swap.h> | 39 | #include <linux/swap.h> |
40 | #include <linux/pagemap.h> | 40 | #include <linux/pagemap.h> |
41 | #include <linux/ratelimit.h> | ||
41 | #include <linux/sunrpc/svcauth_gss.h> | 42 | #include <linux/sunrpc/svcauth_gss.h> |
42 | #include <linux/sunrpc/clnt.h> | 43 | #include <linux/sunrpc/clnt.h> |
43 | #include "xdr4.h" | 44 | #include "xdr4.h" |
@@ -3338,18 +3339,26 @@ static __be32 check_stateid_generation(stateid_t *in, stateid_t *ref, bool has_s | |||
3338 | return nfserr_old_stateid; | 3339 | return nfserr_old_stateid; |
3339 | } | 3340 | } |
3340 | 3341 | ||
3341 | __be32 nfs4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid) | 3342 | static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid) |
3342 | { | 3343 | { |
3343 | struct nfs4_stid *s; | 3344 | struct nfs4_stid *s; |
3344 | struct nfs4_ol_stateid *ols; | 3345 | struct nfs4_ol_stateid *ols; |
3345 | __be32 status; | 3346 | __be32 status; |
3346 | 3347 | ||
3347 | if (STALE_STATEID(stateid)) | 3348 | if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) |
3348 | return nfserr_stale_stateid; | 3349 | return nfserr_bad_stateid; |
3349 | 3350 | /* Client debugging aid. */ | |
3351 | if (!same_clid(&stateid->si_opaque.so_clid, &cl->cl_clientid)) { | ||
3352 | char addr_str[INET6_ADDRSTRLEN]; | ||
3353 | rpc_ntop((struct sockaddr *)&cl->cl_addr, addr_str, | ||
3354 | sizeof(addr_str)); | ||
3355 | pr_warn_ratelimited("NFSD: client %s testing state ID " | ||
3356 | "with incorrect client ID\n", addr_str); | ||
3357 | return nfserr_bad_stateid; | ||
3358 | } | ||
3350 | s = find_stateid(cl, stateid); | 3359 | s = find_stateid(cl, stateid); |
3351 | if (!s) | 3360 | if (!s) |
3352 | return nfserr_stale_stateid; | 3361 | return nfserr_bad_stateid; |
3353 | status = check_stateid_generation(stateid, &s->sc_stateid, 1); | 3362 | status = check_stateid_generation(stateid, &s->sc_stateid, 1); |
3354 | if (status) | 3363 | if (status) |
3355 | return status; | 3364 | return status; |
@@ -3468,7 +3477,8 @@ nfsd4_test_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
3468 | 3477 | ||
3469 | nfs4_lock_state(); | 3478 | nfs4_lock_state(); |
3470 | list_for_each_entry(stateid, &test_stateid->ts_stateid_list, ts_id_list) | 3479 | list_for_each_entry(stateid, &test_stateid->ts_stateid_list, ts_id_list) |
3471 | stateid->ts_id_status = nfs4_validate_stateid(cl, &stateid->ts_id_stateid); | 3480 | stateid->ts_id_status = |
3481 | nfsd4_validate_stateid(cl, &stateid->ts_id_stateid); | ||
3472 | nfs4_unlock_state(); | 3482 | nfs4_unlock_state(); |
3473 | 3483 | ||
3474 | return nfs_ok; | 3484 | return nfs_ok; |
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 849091e16ea6..495df4e3aa67 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h | |||
@@ -475,7 +475,6 @@ extern __be32 nfs4_make_rec_clidname(char *clidname, struct xdr_netobj *clname); | |||
475 | extern int nfs4_client_to_reclaim(const char *name); | 475 | extern int nfs4_client_to_reclaim(const char *name); |
476 | extern int nfs4_has_reclaimed_state(const char *name, bool use_exchange_id); | 476 | extern int nfs4_has_reclaimed_state(const char *name, bool use_exchange_id); |
477 | extern void release_session_client(struct nfsd4_session *); | 477 | extern void release_session_client(struct nfsd4_session *); |
478 | extern __be32 nfs4_validate_stateid(struct nfs4_client *, stateid_t *); | ||
479 | extern void nfsd4_purge_closed_stateid(struct nfs4_stateowner *); | 478 | extern void nfsd4_purge_closed_stateid(struct nfs4_stateowner *); |
480 | 479 | ||
481 | /* nfs4recover operations */ | 480 | /* nfs4recover operations */ |