aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs/dir.c
diff options
context:
space:
mode:
authorBryan Schumaker <bjschuma@netapp.com>2010-10-20 15:44:29 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2010-10-23 15:27:33 -0400
commitbabddc72a9468884ce1a23db3c3d54b0afa299f0 (patch)
treeb176e5795b47c73c47543acdc546da0c38619ddc /fs/nfs/dir.c
parentba8e452a4fe64a51b74d43761e14d99f0666cc45 (diff)
NFS: decode_dirent should use an xdr_stream
Convert nfs*xdr.c to use an xdr stream in decode_dirent. This will prevent a kernel oops that has been occuring when reading a vmapped page. Signed-off-by: Bryan Schumaker <bjschuma@netapp.com> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs/dir.c')
-rw-r--r--fs/nfs/dir.c29
1 files changed, 21 insertions, 8 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index fd30f185ec01..88cbcda76856 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -171,7 +171,7 @@ struct nfs_cache_array {
171 171
172#define MAX_READDIR_ARRAY ((PAGE_SIZE - sizeof(struct nfs_cache_array)) / sizeof(struct nfs_cache_array_entry)) 172#define MAX_READDIR_ARRAY ((PAGE_SIZE - sizeof(struct nfs_cache_array)) / sizeof(struct nfs_cache_array_entry))
173 173
174typedef __be32 * (*decode_dirent_t)(__be32 *, struct nfs_entry *, int); 174typedef __be32 * (*decode_dirent_t)(struct xdr_stream *, struct nfs_entry *, int);
175typedef struct { 175typedef struct {
176 struct file *file; 176 struct file *file;
177 struct page *page; 177 struct page *page;
@@ -357,13 +357,11 @@ error:
357 357
358/* Fill in an entry based on the xdr code stored in desc->page */ 358/* Fill in an entry based on the xdr code stored in desc->page */
359static 359static
360int xdr_decode(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, __be32 **ptr) 360int xdr_decode(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, struct xdr_stream *stream)
361{ 361{
362 __be32 *p = *ptr; 362 __be32 *p = desc->decode(stream, entry, desc->plus);
363 p = desc->decode(p, entry, desc->plus);
364 if (IS_ERR(p)) 363 if (IS_ERR(p))
365 return PTR_ERR(p); 364 return PTR_ERR(p);
366 *ptr = p;
367 365
368 entry->fattr->time_start = desc->timestamp; 366 entry->fattr->time_start = desc->timestamp;
369 entry->fattr->gencount = desc->gencount; 367 entry->fattr->gencount = desc->gencount;
@@ -438,10 +436,23 @@ out:
438/* Perform conversion from xdr to cache array */ 436/* Perform conversion from xdr to cache array */
439static 437static
440void nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, 438void nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry,
441 struct page *xdr_page, struct page *page) 439 struct page *xdr_page, struct page *page, unsigned int buflen)
442{ 440{
441 struct xdr_stream stream;
442 struct xdr_buf buf;
443 __be32 *ptr = kmap(xdr_page); 443 __be32 *ptr = kmap(xdr_page);
444 while (xdr_decode(desc, entry, &ptr) == 0) { 444
445 buf.head->iov_base = xdr_page;
446 buf.head->iov_len = buflen;
447 buf.tail->iov_len = 0;
448 buf.page_base = 0;
449 buf.page_len = 0;
450 buf.buflen = buf.head->iov_len;
451 buf.len = buf.head->iov_len;
452
453 xdr_init_decode(&stream, &buf, ptr);
454
455 while (xdr_decode(desc, entry, &stream) == 0) {
445 if (nfs_readdir_add_to_array(entry, page) == -1) 456 if (nfs_readdir_add_to_array(entry, page) == -1)
446 break; 457 break;
447 if (desc->plus == 1) 458 if (desc->plus == 1)
@@ -458,6 +469,7 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page,
458 struct file *file = desc->file; 469 struct file *file = desc->file;
459 struct nfs_cache_array *array; 470 struct nfs_cache_array *array;
460 int status = 0; 471 int status = 0;
472 unsigned int array_size = 1;
461 473
462 entry.prev_cookie = 0; 474 entry.prev_cookie = 0;
463 entry.cookie = *desc->dir_cookie; 475 entry.cookie = *desc->dir_cookie;
@@ -476,9 +488,10 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page,
476 goto out_release_array; 488 goto out_release_array;
477 do { 489 do {
478 status = nfs_readdir_xdr_filler(xdr_page, desc, &entry, file, inode); 490 status = nfs_readdir_xdr_filler(xdr_page, desc, &entry, file, inode);
491
479 if (status < 0) 492 if (status < 0)
480 break; 493 break;
481 nfs_readdir_page_filler(desc, &entry, xdr_page, page); 494 nfs_readdir_page_filler(desc, &entry, xdr_page, page, array_size * PAGE_SIZE);
482 } while (array->eof_index < 0 && array->size < MAX_READDIR_ARRAY); 495 } while (array->eof_index < 0 && array->size < MAX_READDIR_ARRAY);
483 496
484 put_page(xdr_page); 497 put_page(xdr_page);