| From 95da1b3a5aded124dd1bda1e3cdb876184813140 Mon Sep 17 00:00:00 2001 |
| From: Andrew Elble <aweits@rit.edu> |
| Date: Fri, 3 Nov 2017 14:06:31 -0400 |
| Subject: nfsd: deal with revoked delegations appropriately |
| |
| From: Andrew Elble <aweits@rit.edu> |
| |
| commit 95da1b3a5aded124dd1bda1e3cdb876184813140 upstream. |
| |
| 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> |
| Signed-off-by: J. Bruce Fields <bfields@redhat.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/nfsd/nfs4state.c | 25 ++++++++++++++++++++++++- |
| 1 file changed, 24 insertions(+), 1 deletion(-) |
| |
| --- a/fs/nfsd/nfs4state.c |
| +++ b/fs/nfsd/nfs4state.c |
| @@ -3602,7 +3602,8 @@ static struct nfs4_delegation *find_dele |
| { |
| 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) |
| return NULL; |
| return delegstateid(ret); |
| @@ -3625,6 +3626,12 @@ nfs4_check_deleg(struct nfs4_client *cl, |
| deleg = find_deleg_stateid(cl, &open->op_delegate_stateid); |
| if (deleg == NULL) |
| 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); |
| status = nfs4_check_delegmode(deleg, flags); |
| if (status) { |
| @@ -4451,6 +4458,16 @@ nfsd4_lookup_stateid(struct nfsd4_compou |
| struct nfs4_stid **s, struct nfsd_net *nn) |
| { |
| __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)) |
| return nfserr_bad_stateid; |
| @@ -4465,6 +4482,12 @@ nfsd4_lookup_stateid(struct nfsd4_compou |
| *s = find_stateid_by_type(cstate->clp, stateid, typemask); |
| if (!*s) |
| 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; |
| } |
| |