summaryrefslogtreecommitdiffstats
path: root/fs/nfsd
diff options
context:
space:
mode:
authorMurphy Zhou <jencce.kernel@gmail.com>2019-04-04 02:57:11 -0400
committerJ. Bruce Fields <bfields@redhat.com>2019-04-05 19:57:24 -0400
commit3c86794ac0e6582eea7733619d58ea150198502f (patch)
tree097ea6e5d63a6fc920c1c0b30f7de874f148b4cf /fs/nfsd
parentd58431eacb226222430940134d97bfd72f292fcd (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.c17
-rw-r--r--fs/nfsd/nfs3xdr.c11
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
573nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p) 573nfs3svc_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}