nfsd: deal with revoked delegations appropriately
commit 95da1b3a5a
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>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
5756707370
commit
584f0bb568
@ -3966,7 +3966,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);
|
||||||
@ -3989,6 +3990,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) {
|
||||||
@ -4858,6 +4865,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;
|
||||||
@ -4872,6 +4889,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user