Commit b7342204 authored by Olga Kornievskaia's avatar Olga Kornievskaia Committed by J. Bruce Fields
Browse files

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: default avatarOlga Kornievskaia <kolga@netapp.com>
parent 624322f1
Loading
Loading
Loading
Loading
+66 −8
Original line number Diff line number Diff line
@@ -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;