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

Commit eae03e2a authored by Chuck Lever's avatar Chuck Lever Committed by J. Bruce Fields
Browse files

nfsd: Incoming xdr_bufs may have content in tail buffer



Since the beginning, svcsock has built a received RPC Call message
by populating the xdr_buf's head, then placing the remaining
message bytes in the xdr_buf's page list. The xdr_buf's tail is
never populated.

This means that an NFSv4 COMPOUND containing an NFS WRITE operation
plus trailing operations has a page list that contains the WRITE
data payload followed by the trailing operations. NFSv4 XDR decoders
will not look in the xdr_buf's tail, ever, because svcsock never put
anything there.

To support transports that can pass the write payload in the
xdr_buf's pagelist and trailing content in the xdr_buf's tail,
introduce logic in READ_BUF that switches to the xdr_buf's tail vec
when the decoder runs out of content in rq_arg.pages.

Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
Signed-off-by: default avatarJ. Bruce Fields <bfields@redhat.com>
parent 0828170f
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -159,6 +159,25 @@ static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes)
	 */
	unsigned int avail = (char *)argp->end - (char *)argp->p;
	__be32 *p;

	if (argp->pagelen == 0) {
		struct kvec *vec = &argp->rqstp->rq_arg.tail[0];

		if (!argp->tail) {
			argp->tail = true;
			avail = vec->iov_len;
			argp->p = vec->iov_base;
			argp->end = vec->iov_base + avail;
		}

		if (avail < nbytes)
			return NULL;

		p = argp->p;
		argp->p += XDR_QUADLEN(nbytes);
		return p;
	}

	if (avail + argp->pagelen < nbytes)
		return NULL;
	if (avail + PAGE_SIZE < nbytes) /* need more than a page !! */
@@ -4471,6 +4490,7 @@ nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, __be32 *p)
	args->end = rqstp->rq_arg.head[0].iov_base + rqstp->rq_arg.head[0].iov_len;
	args->pagelist = rqstp->rq_arg.pages;
	args->pagelen = rqstp->rq_arg.page_len;
	args->tail = false;
	args->tmpp = NULL;
	args->to_free = NULL;
	args->ops = args->iops;
+1 −0
Original line number Diff line number Diff line
@@ -615,6 +615,7 @@ struct nfsd4_compoundargs {
	__be32 *			end;
	struct page **			pagelist;
	int				pagelen;
	bool				tail;
	__be32				tmp[8];
	__be32 *			tmpp;
	struct svcxdr_tmpbuf		*to_free;