Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 57716355 authored by J. Bruce Fields's avatar J. Bruce Fields
Browse files

nfsd4: complete enforcement of 4.1 op ordering



Enforce the rules about compound op ordering.

Motivated by implementing RECLAIM_COMPLETE, for which the client is
implicit in the current session, so it is important to ensure a
succesful SEQUENCE proceeds the RECLAIM_COMPLETE.

Signed-off-by: default avatarJ. Bruce Fields <bfields@citi.umich.edu>
parent 4b21d0de
Loading
Loading
Loading
Loading
+30 −14
Original line number Diff line number Diff line
@@ -968,20 +968,36 @@ static struct nfsd4_operation nfsd4_ops[];
static const char *nfsd4_op_name(unsigned opnum);

/*
 * Enforce NFSv4.1 COMPOUND ordering rules.
 * Enforce NFSv4.1 COMPOUND ordering rules:
 *
 * TODO:
 * - enforce NFS4ERR_NOT_ONLY_OP,
 * - DESTROY_SESSION MUST be the final operation in the COMPOUND request.
 * Also note, enforced elsewhere:
 *	- SEQUENCE other than as first op results in
 *	  NFS4ERR_SEQUENCE_POS. (Enforced in nfsd4_sequence().)
 *	- BIND_CONN_TO_SESSION must be the only op in its compound
 *	  (Will be enforced in nfsd4_bind_conn_to_session().)
 *	- DESTROY_SESSION must be the final operation in a compound, if
 *	  sessionid's in SEQUENCE and DESTROY_SESSION are the same.
 *	  (Enforced in nfsd4_destroy_session().)
 */
static bool nfs41_op_ordering_ok(struct nfsd4_compoundargs *args)
static __be32 nfs41_check_op_ordering(struct nfsd4_compoundargs *args)
{
	if (args->minorversion && args->opcnt > 0) {
	struct nfsd4_op *op = &args->ops[0];
		return (op->status == nfserr_op_illegal) ||
		       (nfsd4_ops[op->opnum].op_flags & ALLOWED_AS_FIRST_OP);
	}
	return true;

	/* These ordering requirements don't apply to NFSv4.0: */
	if (args->minorversion == 0)
		return nfs_ok;
	/* This is weird, but OK, not our problem: */
	if (args->opcnt == 0)
		return nfs_ok;
	if (op->status == nfserr_op_illegal)
		return nfs_ok;
	if (!(nfsd4_ops[op->opnum].op_flags & ALLOWED_AS_FIRST_OP))
		return nfserr_op_not_in_session;
	if (op->opnum == OP_SEQUENCE)
		return nfs_ok;
	if (args->opcnt != 1)
		return nfserr_not_only_op;
	return nfs_ok;
}

/*
@@ -1023,13 +1039,13 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
	if (args->minorversion > nfsd_supported_minorversion)
		goto out;

	if (!nfs41_op_ordering_ok(args)) {
	status = nfs41_check_op_ordering(args);
	if (status) {
		op = &args->ops[0];
		op->status = nfserr_sequence_pos;
		op->status = status;
		goto encode_op;
	}

	status = nfs_ok;
	while (!status && resp->opcnt < args->opcnt) {
		op = &args->ops[resp->opcnt++];

+13 −0
Original line number Diff line number Diff line
@@ -1343,6 +1343,14 @@ nfsd4_create_session(struct svc_rqst *rqstp,
	return status;
}

static bool nfsd4_last_compound_op(struct svc_rqst *rqstp)
{
	struct nfsd4_compoundres *resp = rqstp->rq_resp;
	struct nfsd4_compoundargs *argp = rqstp->rq_argp;

	return argp->opcnt == resp->opcnt;
}

__be32
nfsd4_destroy_session(struct svc_rqst *r,
		      struct nfsd4_compound_state *cstate,
@@ -1358,6 +1366,11 @@ nfsd4_destroy_session(struct svc_rqst *r,
	 * - Do we need to clear any callback info from previous session?
	 */

	if (!memcmp(&sessionid->sessionid, &cstate->session->se_sessionid,
					sizeof(struct nfs4_sessionid))) {
		if (!nfsd4_last_compound_op(r))
			return nfserr_not_only_op;
	}
	dump_sessionid(__func__, &sessionid->sessionid);
	spin_lock(&sessionid_lock);
	ses = find_in_sessionid_hashtbl(&sessionid->sessionid);