diff options
Diffstat (limited to 'fs/nfs')
-rw-r--r-- | fs/nfs/nfs2xdr.c | 37 |
1 files changed, 28 insertions, 9 deletions
diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index 1f7ea675e0c5..86a80b33ec82 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c | |||
@@ -428,7 +428,7 @@ nfs_xdr_readdirres(struct rpc_rqst *req, __be32 *p, void *dummy) | |||
428 | size_t hdrlen; | 428 | size_t hdrlen; |
429 | unsigned int pglen, recvd; | 429 | unsigned int pglen, recvd; |
430 | u32 len; | 430 | u32 len; |
431 | int status, nr; | 431 | int status, nr = 0; |
432 | __be32 *end, *entry, *kaddr; | 432 | __be32 *end, *entry, *kaddr; |
433 | 433 | ||
434 | if ((status = ntohl(*p++))) | 434 | if ((status = ntohl(*p++))) |
@@ -452,7 +452,12 @@ nfs_xdr_readdirres(struct rpc_rqst *req, __be32 *p, void *dummy) | |||
452 | kaddr = p = kmap_atomic(*page, KM_USER0); | 452 | kaddr = p = kmap_atomic(*page, KM_USER0); |
453 | end = (__be32 *)((char *)p + pglen); | 453 | end = (__be32 *)((char *)p + pglen); |
454 | entry = p; | 454 | entry = p; |
455 | for (nr = 0; *p++; nr++) { | 455 | |
456 | /* Make sure the packet actually has a value_follows and EOF entry */ | ||
457 | if ((entry + 1) > end) | ||
458 | goto short_pkt; | ||
459 | |||
460 | for (; *p++; nr++) { | ||
456 | if (p + 2 > end) | 461 | if (p + 2 > end) |
457 | goto short_pkt; | 462 | goto short_pkt; |
458 | p++; /* fileid */ | 463 | p++; /* fileid */ |
@@ -467,18 +472,32 @@ nfs_xdr_readdirres(struct rpc_rqst *req, __be32 *p, void *dummy) | |||
467 | goto short_pkt; | 472 | goto short_pkt; |
468 | entry = p; | 473 | entry = p; |
469 | } | 474 | } |
470 | if (!nr && (entry[0] != 0 || entry[1] == 0)) | 475 | |
471 | goto short_pkt; | 476 | /* |
477 | * Apparently some server sends responses that are a valid size, but | ||
478 | * contain no entries, and have value_follows==0 and EOF==0. For | ||
479 | * those, just set the EOF marker. | ||
480 | */ | ||
481 | if (!nr && entry[1] == 0) { | ||
482 | dprintk("NFS: readdir reply truncated!\n"); | ||
483 | entry[1] = 1; | ||
484 | } | ||
472 | out: | 485 | out: |
473 | kunmap_atomic(kaddr, KM_USER0); | 486 | kunmap_atomic(kaddr, KM_USER0); |
474 | return nr; | 487 | return nr; |
475 | short_pkt: | 488 | short_pkt: |
489 | /* | ||
490 | * When we get a short packet there are 2 possibilities. We can | ||
491 | * return an error, or fix up the response to look like a valid | ||
492 | * response and return what we have so far. If there are no | ||
493 | * entries and the packet was short, then return -EIO. If there | ||
494 | * are valid entries in the response, return them and pretend that | ||
495 | * the call was successful, but incomplete. The caller can retry the | ||
496 | * readdir starting at the last cookie. | ||
497 | */ | ||
476 | entry[0] = entry[1] = 0; | 498 | entry[0] = entry[1] = 0; |
477 | /* truncate listing ? */ | 499 | if (!nr) |
478 | if (!nr) { | 500 | nr = -errno_NFSERR_IO; |
479 | dprintk("NFS: readdir reply truncated!\n"); | ||
480 | entry[1] = 1; | ||
481 | } | ||
482 | goto out; | 501 | goto out; |
483 | err_unmap: | 502 | err_unmap: |
484 | nr = -errno_NFSERR_IO; | 503 | nr = -errno_NFSERR_IO; |