Commit 95da1b3a authored by Andrew Elble's avatar Andrew Elble Committed by J. Bruce Fields

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: default avatarAndrew Elble <aweits@rit.edu>
Reviewed-by: default avatarTrond Myklebust <trond.myklebust@primarydata.com>
Cc: stable@vger.kernel.org
Signed-off-by: default avatarJ. Bruce Fields <bfields@redhat.com>
parent 77a08867
...@@ -4016,7 +4016,8 @@ static struct nfs4_delegation *find_deleg_stateid(struct nfs4_client *cl, statei ...@@ -4016,7 +4016,8 @@ static struct nfs4_delegation *find_deleg_stateid(struct nfs4_client *cl, statei
{ {
struct nfs4_stid *ret; struct nfs4_stid *ret;
ret = find_stateid_by_type(cl, s, NFS4_DELEG_STID); ret = find_stateid_by_type(cl, s,
NFS4_DELEG_STID|NFS4_REVOKED_DELEG_STID);
if (!ret) if (!ret)
return NULL; return NULL;
return delegstateid(ret); return delegstateid(ret);
...@@ -4039,6 +4040,12 @@ nfs4_check_deleg(struct nfs4_client *cl, struct nfsd4_open *open, ...@@ -4039,6 +4040,12 @@ nfs4_check_deleg(struct nfs4_client *cl, struct nfsd4_open *open,
deleg = find_deleg_stateid(cl, &open->op_delegate_stateid); deleg = find_deleg_stateid(cl, &open->op_delegate_stateid);
if (deleg == NULL) if (deleg == NULL)
goto out; goto out;
if (deleg->dl_stid.sc_type == NFS4_REVOKED_DELEG_STID) {
nfs4_put_stid(&deleg->dl_stid);
if (cl->cl_minorversion)
status = nfserr_deleg_revoked;
goto out;
}
flags = share_access_to_flags(open->op_share_access); flags = share_access_to_flags(open->op_share_access);
status = nfs4_check_delegmode(deleg, flags); status = nfs4_check_delegmode(deleg, flags);
if (status) { if (status) {
...@@ -4908,6 +4915,16 @@ nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate, ...@@ -4908,6 +4915,16 @@ nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate,
struct nfs4_stid **s, struct nfsd_net *nn) struct nfs4_stid **s, struct nfsd_net *nn)
{ {
__be32 status; __be32 status;
bool return_revoked = false;
/*
* only return revoked delegations if explicitly asked.
* otherwise we report revoked or bad_stateid status.
*/
if (typemask & NFS4_REVOKED_DELEG_STID)
return_revoked = true;
else if (typemask & NFS4_DELEG_STID)
typemask |= NFS4_REVOKED_DELEG_STID;
if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
return nfserr_bad_stateid; return nfserr_bad_stateid;
...@@ -4922,6 +4939,12 @@ nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate, ...@@ -4922,6 +4939,12 @@ nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate,
*s = find_stateid_by_type(cstate->clp, stateid, typemask); *s = find_stateid_by_type(cstate->clp, stateid, typemask);
if (!*s) if (!*s)
return nfserr_bad_stateid; return nfserr_bad_stateid;
if (((*s)->sc_type == NFS4_REVOKED_DELEG_STID) && !return_revoked) {
nfs4_put_stid(*s);
if (cstate->minorversion)
return nfserr_deleg_revoked;
return nfserr_bad_stateid;
}
return nfs_ok; return nfs_ok;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment