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: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
This commit is contained in:
Chuck Lever 2017-08-18 11:12:27 -04:00 committed by J. Bruce Fields
parent 0828170f3d
commit eae03e2ac8
2 changed files with 21 additions and 0 deletions

View File

@ -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;

View File

@ -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;