diff options
author | Chuck Lever <chuck.lever@oracle.com> | 2017-08-18 11:12:27 -0400 |
---|---|---|
committer | J. Bruce Fields <bfields@redhat.com> | 2017-09-05 15:15:29 -0400 |
commit | eae03e2ac80a3476f0652cb0ee451d7b06d30564 (patch) | |
tree | a07c8ea087aec4334faf0c626f2ab5f396685622 /fs/nfsd | |
parent | 0828170f3d6a9d86d2cf5bcd00f1b1ff99f0c15e (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.c | 20 | ||||
-rw-r--r-- | fs/nfsd/xdr4.h | 1 |
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; |