diff options
author | Murphy Zhou <jencce.kernel@gmail.com> | 2019-04-04 02:57:11 -0400 |
---|---|---|
committer | J. Bruce Fields <bfields@redhat.com> | 2019-04-05 19:57:24 -0400 |
commit | 3c86794ac0e6582eea7733619d58ea150198502f (patch) | |
tree | 097ea6e5d63a6fc920c1c0b30f7de874f148b4cf /fs/nfsd | |
parent | d58431eacb226222430940134d97bfd72f292fcd (diff) |
nfsd/nfsd3_proc_readdir: fix buffer count and page pointers
After this commit
f875a79 nfsd: allow nfsv3 readdir request to be larger.
nfsv3 readdir request size can be larger than PAGE_SIZE. So if the
directory been read is large enough, we can use multiple pages
in rq_respages. Update buffer count and page pointers like we do
in readdirplus to make this happen.
Now listing a directory within 3000 files will panic because we
are counting in a wrong way and would write on random page.
Fixes: f875a79 "nfsd: allow nfsv3 readdir request to be larger"
Signed-off-by: Murphy Zhou <jencce.kernel@gmail.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs/nfsd')
-rw-r--r-- | fs/nfsd/nfs3proc.c | 17 | ||||
-rw-r--r-- | fs/nfsd/nfs3xdr.c | 11 |
2 files changed, 24 insertions, 4 deletions
diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c index 8f933e84cec1..9bc32af4e2da 100644 --- a/fs/nfsd/nfs3proc.c +++ b/fs/nfsd/nfs3proc.c | |||
@@ -442,7 +442,9 @@ nfsd3_proc_readdir(struct svc_rqst *rqstp) | |||
442 | struct nfsd3_readdirargs *argp = rqstp->rq_argp; | 442 | struct nfsd3_readdirargs *argp = rqstp->rq_argp; |
443 | struct nfsd3_readdirres *resp = rqstp->rq_resp; | 443 | struct nfsd3_readdirres *resp = rqstp->rq_resp; |
444 | __be32 nfserr; | 444 | __be32 nfserr; |
445 | int count; | 445 | int count = 0; |
446 | struct page **p; | ||
447 | caddr_t page_addr = NULL; | ||
446 | 448 | ||
447 | dprintk("nfsd: READDIR(3) %s %d bytes at %d\n", | 449 | dprintk("nfsd: READDIR(3) %s %d bytes at %d\n", |
448 | SVCFH_fmt(&argp->fh), | 450 | SVCFH_fmt(&argp->fh), |
@@ -462,7 +464,18 @@ nfsd3_proc_readdir(struct svc_rqst *rqstp) | |||
462 | nfserr = nfsd_readdir(rqstp, &resp->fh, (loff_t*) &argp->cookie, | 464 | nfserr = nfsd_readdir(rqstp, &resp->fh, (loff_t*) &argp->cookie, |
463 | &resp->common, nfs3svc_encode_entry); | 465 | &resp->common, nfs3svc_encode_entry); |
464 | memcpy(resp->verf, argp->verf, 8); | 466 | memcpy(resp->verf, argp->verf, 8); |
465 | resp->count = resp->buffer - argp->buffer; | 467 | count = 0; |
468 | for (p = rqstp->rq_respages + 1; p < rqstp->rq_next_page; p++) { | ||
469 | page_addr = page_address(*p); | ||
470 | |||
471 | if (((caddr_t)resp->buffer >= page_addr) && | ||
472 | ((caddr_t)resp->buffer < page_addr + PAGE_SIZE)) { | ||
473 | count += (caddr_t)resp->buffer - page_addr; | ||
474 | break; | ||
475 | } | ||
476 | count += PAGE_SIZE; | ||
477 | } | ||
478 | resp->count = count >> 2; | ||
466 | if (resp->offset) { | 479 | if (resp->offset) { |
467 | loff_t offset = argp->cookie; | 480 | loff_t offset = argp->cookie; |
468 | 481 | ||
diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c index 93fea246f676..8d789124ed3c 100644 --- a/fs/nfsd/nfs3xdr.c +++ b/fs/nfsd/nfs3xdr.c | |||
@@ -573,6 +573,7 @@ int | |||
573 | nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p) | 573 | nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p) |
574 | { | 574 | { |
575 | struct nfsd3_readdirargs *args = rqstp->rq_argp; | 575 | struct nfsd3_readdirargs *args = rqstp->rq_argp; |
576 | int len; | ||
576 | u32 max_blocksize = svc_max_payload(rqstp); | 577 | u32 max_blocksize = svc_max_payload(rqstp); |
577 | 578 | ||
578 | p = decode_fh(p, &args->fh); | 579 | p = decode_fh(p, &args->fh); |
@@ -582,8 +583,14 @@ nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p) | |||
582 | args->verf = p; p += 2; | 583 | args->verf = p; p += 2; |
583 | args->dircount = ~0; | 584 | args->dircount = ~0; |
584 | args->count = ntohl(*p++); | 585 | args->count = ntohl(*p++); |
585 | args->count = min_t(u32, args->count, max_blocksize); | 586 | len = args->count = min_t(u32, args->count, max_blocksize); |
586 | args->buffer = page_address(*(rqstp->rq_next_page++)); | 587 | |
588 | while (len > 0) { | ||
589 | struct page *p = *(rqstp->rq_next_page++); | ||
590 | if (!args->buffer) | ||
591 | args->buffer = page_address(p); | ||
592 | len -= PAGE_SIZE; | ||
593 | } | ||
587 | 594 | ||
588 | return xdr_argsize_check(rqstp, p); | 595 | return xdr_argsize_check(rqstp, p); |
589 | } | 596 | } |