NFSD check stateids against copy stateids

Incoming stateid (used by a READ) could be a saved copy stateid.
Using the provided stateid, look it up in the list of copy_notify
stateids. If found, use the parent's stateid and parent's clid
to look up the parent's stid to do the appropriate checks.

Update the copy notify timestamp (cpntf_time) with current time
this making it 'active' so that laundromat thread will not delete
copy notify state.

Signed-off-by: Olga Kornievskaia <kolga@netapp.com>
This commit is contained in:
Olga Kornievskaia 2019-09-06 15:17:21 -04:00 коммит произвёл J. Bruce Fields
Родитель 624322f1ad
Коммит b734220425
1 изменённых файлов: 66 добавлений и 8 удалений

Просмотреть файл

@ -4539,7 +4539,8 @@ static __be32 nfsd4_check_seqid(struct nfsd4_compound_state *cstate, struct nfs4
static __be32 lookup_clientid(clientid_t *clid,
struct nfsd4_compound_state *cstate,
struct nfsd_net *nn)
struct nfsd_net *nn,
bool sessions)
{
struct nfs4_client *found;
@ -4560,7 +4561,7 @@ static __be32 lookup_clientid(clientid_t *clid,
*/
WARN_ON_ONCE(cstate->session);
spin_lock(&nn->client_lock);
found = find_confirmed_client(clid, false, nn);
found = find_confirmed_client(clid, sessions, nn);
if (!found) {
spin_unlock(&nn->client_lock);
return nfserr_expired;
@ -4593,7 +4594,7 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate,
if (open->op_file == NULL)
return nfserr_jukebox;
status = lookup_clientid(clientid, cstate, nn);
status = lookup_clientid(clientid, cstate, nn, false);
if (status)
return status;
clp = cstate->clp;
@ -5182,7 +5183,7 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
dprintk("process_renew(%08x/%08x): starting\n",
clid->cl_boot, clid->cl_id);
status = lookup_clientid(clid, cstate, nn);
status = lookup_clientid(clid, cstate, nn, false);
if (status)
goto out;
clp = cstate->clp;
@ -5584,7 +5585,8 @@ nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate,
if (ZERO_STATEID(stateid) || ONE_STATEID(stateid) ||
CLOSE_STATEID(stateid))
return nfserr_bad_stateid;
status = lookup_clientid(&stateid->si_opaque.so_clid, cstate, nn);
status = lookup_clientid(&stateid->si_opaque.so_clid, cstate, nn,
false);
if (status == nfserr_stale_clientid) {
if (cstate->session)
return nfserr_bad_stateid;
@ -5674,6 +5676,59 @@ _free_cpntf_state_locked(struct nfsd_net *nn, struct nfs4_cpntf_state *cps)
cps->cp_stateid.stid.si_opaque.so_id);
kfree(cps);
}
/*
* A READ from an inter server to server COPY will have a
* copy stateid. Look up the copy notify stateid from the
* idr structure and take a reference on it.
*/
static __be32 _find_cpntf_state(struct nfsd_net *nn, stateid_t *st,
struct nfs4_cpntf_state **cps)
{
copy_stateid_t *cps_t;
struct nfs4_cpntf_state *state = NULL;
if (st->si_opaque.so_clid.cl_id != nn->s2s_cp_cl_id)
return nfserr_bad_stateid;
spin_lock(&nn->s2s_cp_lock);
cps_t = idr_find(&nn->s2s_cp_stateids, st->si_opaque.so_id);
if (cps_t) {
state = container_of(cps_t, struct nfs4_cpntf_state,
cp_stateid);
if (state->cp_stateid.sc_type != NFS4_COPYNOTIFY_STID)
return nfserr_bad_stateid;
refcount_inc(&state->cp_stateid.sc_count);
}
spin_unlock(&nn->s2s_cp_lock);
if (!state)
return nfserr_bad_stateid;
*cps = state;
return 0;
}
static __be32 find_cpntf_state(struct nfsd_net *nn, stateid_t *st,
struct nfs4_stid **stid)
{
__be32 status;
struct nfs4_cpntf_state *cps = NULL;
struct nfsd4_compound_state cstate;
status = _find_cpntf_state(nn, st, &cps);
if (status)
return status;
cps->cpntf_time = get_seconds();
memset(&cstate, 0, sizeof(cstate));
status = lookup_clientid(&cps->cp_p_clid, &cstate, nn, true);
if (status)
goto out;
status = nfsd4_lookup_stateid(&cstate, &cps->cp_p_stateid,
NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID,
stid, nn);
put_client_renew(cstate.clp);
out:
nfs4_put_cpntf_state(nn, cps);
return status;
}
void nfs4_put_cpntf_state(struct nfsd_net *nn, struct nfs4_cpntf_state *cps)
{
@ -5711,6 +5766,8 @@ nfs4_preprocess_stateid_op(struct svc_rqst *rqstp,
status = nfsd4_lookup_stateid(cstate, stateid,
NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID,
&s, nn);
if (status == nfserr_bad_stateid)
status = find_cpntf_state(nn, stateid, &s);
if (status)
return status;
status = nfsd4_stid_check_stateid_generation(stateid, s,
@ -6743,7 +6800,8 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
return nfserr_inval;
if (!nfsd4_has_session(cstate)) {
status = lookup_clientid(&lockt->lt_clientid, cstate, nn);
status = lookup_clientid(&lockt->lt_clientid, cstate, nn,
false);
if (status)
goto out;
}
@ -6927,7 +6985,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n",
clid->cl_boot, clid->cl_id);
status = lookup_clientid(clid, cstate, nn);
status = lookup_clientid(clid, cstate, nn, false);
if (status)
return status;
@ -7074,7 +7132,7 @@ nfs4_check_open_reclaim(clientid_t *clid,
__be32 status;
/* find clientid in conf_id_hashtbl */
status = lookup_clientid(clid, cstate, nn);
status = lookup_clientid(clid, cstate, nn, false);
if (status)
return nfserr_reclaim_bad;