summaryrefslogtreecommitdiffstats
path: root/fs/nfsd
diff options
context:
space:
mode:
authorAndrew Elble <aweits@rit.edu>2017-11-03 14:06:31 -0400
committerJ. Bruce Fields <bfields@redhat.com>2017-11-07 16:44:02 -0500
commit95da1b3a5aded124dd1bda1e3cdb876184813140 (patch)
tree6764bb8c88390cdfc1cdee2733b3be6767e3e9e2 /fs/nfsd
parent77a08867a66796f8316449e030e0bfc84f2a3f66 (diff)
nfsd: deal with revoked delegations appropriately
If a delegation has been revoked by the server, operations using that delegation should error out with NFS4ERR_DELEG_REVOKED in the >4.1 case, and NFS4ERR_BAD_STATEID otherwise. The server needs NFSv4.1 clients to explicitly free revoked delegations. If the server returns NFS4ERR_DELEG_REVOKED, the client will do that; otherwise it may just forget about the delegation and be unable to recover when it later sees SEQ4_STATUS_RECALLABLE_STATE_REVOKED set on a SEQUENCE reply. That can cause the Linux 4.1 client to loop in its stage manager. Signed-off-by: Andrew Elble <aweits@rit.edu> Reviewed-by: Trond Myklebust <trond.myklebust@primarydata.com> Cc: stable@vger.kernel.org Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs/nfsd')
-rw-r--r--fs/nfsd/nfs4state.c25
1 files changed, 24 insertions, 1 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index ecbc7b0dfa4d..b82817767b9d 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4016,7 +4016,8 @@ static struct nfs4_delegation *find_deleg_stateid(struct nfs4_client *cl, statei
4016{ 4016{
4017 struct nfs4_stid *ret; 4017 struct nfs4_stid *ret;
4018 4018
4019 ret = find_stateid_by_type(cl, s, NFS4_DELEG_STID); 4019 ret = find_stateid_by_type(cl, s,
4020 NFS4_DELEG_STID|NFS4_REVOKED_DELEG_STID);
4020 if (!ret) 4021 if (!ret)
4021 return NULL; 4022 return NULL;
4022 return delegstateid(ret); 4023 return delegstateid(ret);
@@ -4039,6 +4040,12 @@ nfs4_check_deleg(struct nfs4_client *cl, struct nfsd4_open *open,
4039 deleg = find_deleg_stateid(cl, &open->op_delegate_stateid); 4040 deleg = find_deleg_stateid(cl, &open->op_delegate_stateid);
4040 if (deleg == NULL) 4041 if (deleg == NULL)
4041 goto out; 4042 goto out;
4043 if (deleg->dl_stid.sc_type == NFS4_REVOKED_DELEG_STID) {
4044 nfs4_put_stid(&deleg->dl_stid);
4045 if (cl->cl_minorversion)
4046 status = nfserr_deleg_revoked;
4047 goto out;
4048 }
4042 flags = share_access_to_flags(open->op_share_access); 4049 flags = share_access_to_flags(open->op_share_access);
4043 status = nfs4_check_delegmode(deleg, flags); 4050 status = nfs4_check_delegmode(deleg, flags);
4044 if (status) { 4051 if (status) {
@@ -4908,6 +4915,16 @@ nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate,
4908 struct nfs4_stid **s, struct nfsd_net *nn) 4915 struct nfs4_stid **s, struct nfsd_net *nn)
4909{ 4916{
4910 __be32 status; 4917 __be32 status;
4918 bool return_revoked = false;
4919
4920 /*
4921 * only return revoked delegations if explicitly asked.
4922 * otherwise we report revoked or bad_stateid status.
4923 */
4924 if (typemask & NFS4_REVOKED_DELEG_STID)
4925 return_revoked = true;
4926 else if (typemask & NFS4_DELEG_STID)
4927 typemask |= NFS4_REVOKED_DELEG_STID;
4911 4928
4912 if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) 4929 if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
4913 return nfserr_bad_stateid; 4930 return nfserr_bad_stateid;
@@ -4922,6 +4939,12 @@ nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate,
4922 *s = find_stateid_by_type(cstate->clp, stateid, typemask); 4939 *s = find_stateid_by_type(cstate->clp, stateid, typemask);
4923 if (!*s) 4940 if (!*s)
4924 return nfserr_bad_stateid; 4941 return nfserr_bad_stateid;
4942 if (((*s)->sc_type == NFS4_REVOKED_DELEG_STID) && !return_revoked) {
4943 nfs4_put_stid(*s);
4944 if (cstate->minorversion)
4945 return nfserr_deleg_revoked;
4946 return nfserr_bad_stateid;
4947 }
4925 return nfs_ok; 4948 return nfs_ok;
4926} 4949}
4927 4950