Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Murphy Zhou and committed by
J. Bruce Fields
3c86794a d58431ea

+24 -4
+15 -2
fs/nfsd/nfs3proc.c
··· 442 442 struct nfsd3_readdirargs *argp = rqstp->rq_argp; 443 443 struct nfsd3_readdirres *resp = rqstp->rq_resp; 444 444 __be32 nfserr; 445 - int count; 445 + int count = 0; 446 + struct page **p; 447 + caddr_t page_addr = NULL; 446 448 447 449 dprintk("nfsd: READDIR(3) %s %d bytes at %d\n", 448 450 SVCFH_fmt(&argp->fh), ··· 464 462 nfserr = nfsd_readdir(rqstp, &resp->fh, (loff_t*) &argp->cookie, 465 463 &resp->common, nfs3svc_encode_entry); 466 464 memcpy(resp->verf, argp->verf, 8); 467 - resp->count = resp->buffer - argp->buffer; 465 + count = 0; 466 + for (p = rqstp->rq_respages + 1; p < rqstp->rq_next_page; p++) { 467 + page_addr = page_address(*p); 468 + 469 + if (((caddr_t)resp->buffer >= page_addr) && 470 + ((caddr_t)resp->buffer < page_addr + PAGE_SIZE)) { 471 + count += (caddr_t)resp->buffer - page_addr; 472 + break; 473 + } 474 + count += PAGE_SIZE; 475 + } 476 + resp->count = count >> 2; 468 477 if (resp->offset) { 469 478 loff_t offset = argp->cookie; 470 479
+9 -2
fs/nfsd/nfs3xdr.c
··· 573 573 nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p) 574 574 { 575 575 struct nfsd3_readdirargs *args = rqstp->rq_argp; 576 + int len; 576 577 u32 max_blocksize = svc_max_payload(rqstp); 577 578 578 579 p = decode_fh(p, &args->fh); ··· 583 582 args->verf = p; p += 2; 584 583 args->dircount = ~0; 585 584 args->count = ntohl(*p++); 586 - args->count = min_t(u32, args->count, max_blocksize); 587 - args->buffer = page_address(*(rqstp->rq_next_page++)); 585 + len = args->count = min_t(u32, args->count, max_blocksize); 586 + 587 + while (len > 0) { 588 + struct page *p = *(rqstp->rq_next_page++); 589 + if (!args->buffer) 590 + args->buffer = page_address(p); 591 + len -= PAGE_SIZE; 592 + } 588 593 589 594 return xdr_argsize_check(rqstp, p); 590 595 }