nfsd: Incoming xdr_bufs may have content in tail buffer
authorChuck Lever <chuck.lever@oracle.com>
Fri, 18 Aug 2017 15:12:27 +0000 (11:12 -0400)
committerJ. Bruce Fields <bfields@redhat.com>
Tue, 5 Sep 2017 19:15:29 +0000 (15:15 -0400)
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>
fs/nfsd/nfs4xdr.c
fs/nfsd/xdr4.h

index 08691fe394b2d89a8477853e47ae0cd5025ac35f..2c61c6b8ae0976209cbb8a443fb5db1a50d5fdad 100644 (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;
index f8a0b6549a88aea36dcf9b1c36310c2c7333c2fc..1e4edbf70052bf5f6c8c0af06e0d7bf1812e80a5 100644 (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;