diff options
author | Bryan Schumaker <bjschuma@netapp.com> | 2010-10-20 15:44:29 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2010-10-23 15:27:33 -0400 |
commit | babddc72a9468884ce1a23db3c3d54b0afa299f0 (patch) | |
tree | b176e5795b47c73c47543acdc546da0c38619ddc /fs/nfs/dir.c | |
parent | ba8e452a4fe64a51b74d43761e14d99f0666cc45 (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.c | 29 |
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 | ||
174 | typedef __be32 * (*decode_dirent_t)(__be32 *, struct nfs_entry *, int); | 174 | typedef __be32 * (*decode_dirent_t)(struct xdr_stream *, struct nfs_entry *, int); |
175 | typedef struct { | 175 | typedef 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 */ |
359 | static | 359 | static |
360 | int xdr_decode(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, __be32 **ptr) | 360 | int 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 */ |
439 | static | 437 | static |
440 | void nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, | 438 | void 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); |