summaryrefslogtreecommitdiffstats
path: root/fs/nfsd
diff options
context:
space:
mode:
authorChuck Lever <chuck.lever@oracle.com>2017-08-18 11:12:27 -0400
committerJ. Bruce Fields <bfields@redhat.com>2017-09-05 15:15:29 -0400
commiteae03e2ac80a3476f0652cb0ee451d7b06d30564 (patch)
treea07c8ea087aec4334faf0c626f2ab5f396685622 /fs/nfsd
parent0828170f3d6a9d86d2cf5bcd00f1b1ff99f0c15e (diff)
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>
Diffstat (limited to 'fs/nfsd')
-rw-r--r--fs/nfsd/nfs4xdr.c20
-rw-r--r--fs/nfsd/xdr4.h1
2 files changed, 21 insertions, 0 deletions
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 08691fe394b2..2c61c6b8ae09 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -159,6 +159,25 @@ static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes)
159 */ 159 */
160 unsigned int avail = (char *)argp->end - (char *)argp->p; 160 unsigned int avail = (char *)argp->end - (char *)argp->p;
161 __be32 *p; 161 __be32 *p;
162
163 if (argp->pagelen == 0) {
164 struct kvec *vec = &argp->rqstp->rq_arg.tail[0];
165
166 if (!argp->tail) {
167 argp->tail = true;
168 avail = vec->iov_len;
169 argp->p = vec->iov_base;
170 argp->end = vec->iov_base + avail;
171 }
172
173 if (avail < nbytes)
174 return NULL;
175
176 p = argp->p;
177 argp->p += XDR_QUADLEN(nbytes);
178 return p;
179 }
180
162 if (avail + argp->pagelen < nbytes) 181 if (avail + argp->pagelen < nbytes)
163 return NULL; 182 return NULL;
164 if (avail + PAGE_SIZE < nbytes) /* need more than a page !! */ 183 if (avail + PAGE_SIZE < nbytes) /* need more than a page !! */
@@ -4471,6 +4490,7 @@ nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, __be32 *p)
4471 args->end = rqstp->rq_arg.head[0].iov_base + rqstp->rq_arg.head[0].iov_len; 4490 args->end = rqstp->rq_arg.head[0].iov_base + rqstp->rq_arg.head[0].iov_len;
4472 args->pagelist = rqstp->rq_arg.pages; 4491 args->pagelist = rqstp->rq_arg.pages;
4473 args->pagelen = rqstp->rq_arg.page_len; 4492 args->pagelen = rqstp->rq_arg.page_len;
4493 args->tail = false;
4474 args->tmpp = NULL; 4494 args->tmpp = NULL;
4475 args->to_free = NULL; 4495 args->to_free = NULL;
4476 args->ops = args->iops; 4496 args->ops = args->iops;
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index f8a0b6549a88..1e4edbf70052 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -615,6 +615,7 @@ struct nfsd4_compoundargs {
615 __be32 * end; 615 __be32 * end;
616 struct page ** pagelist; 616 struct page ** pagelist;
617 int pagelen; 617 int pagelen;
618 bool tail;
618 __be32 tmp[8]; 619 __be32 tmp[8];
619 __be32 * tmpp; 620 __be32 * tmpp;
620 struct svcxdr_tmpbuf *to_free; 621 struct svcxdr_tmpbuf *to_free;