diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2010-11-15 20:26:22 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2010-11-15 20:44:28 -0500 |
commit | 8cd51a0ccd1beda4482507769887c0be9d70f8c1 (patch) | |
tree | 3fe5c05dcc6e532641d1be4e797ab869c71c36ad /fs/nfs/dir.c | |
parent | 23ebbd9acf5756b6eb783df84403e3ab668a6bce (diff) |
NFS: Fix a couple of regressions in readdir.
Fix up the issue that array->eof_index needs to be able to be set
even if array->size == 0.
Ensure that we catch all important memory allocation error conditions
and/or kmap() failures.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs/dir.c')
-rw-r--r-- | fs/nfs/dir.c | 90 |
1 files changed, 56 insertions, 34 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 635ff65d3092..c6ce8af266ed 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
@@ -194,9 +194,13 @@ typedef struct { | |||
194 | static | 194 | static |
195 | struct nfs_cache_array *nfs_readdir_get_array(struct page *page) | 195 | struct nfs_cache_array *nfs_readdir_get_array(struct page *page) |
196 | { | 196 | { |
197 | void *ptr; | ||
197 | if (page == NULL) | 198 | if (page == NULL) |
198 | return ERR_PTR(-EIO); | 199 | return ERR_PTR(-EIO); |
199 | return (struct nfs_cache_array *)kmap(page); | 200 | ptr = kmap(page); |
201 | if (ptr == NULL) | ||
202 | return ERR_PTR(-ENOMEM); | ||
203 | return ptr; | ||
200 | } | 204 | } |
201 | 205 | ||
202 | static | 206 | static |
@@ -213,6 +217,9 @@ int nfs_readdir_clear_array(struct page *page, gfp_t mask) | |||
213 | { | 217 | { |
214 | struct nfs_cache_array *array = nfs_readdir_get_array(page); | 218 | struct nfs_cache_array *array = nfs_readdir_get_array(page); |
215 | int i; | 219 | int i; |
220 | |||
221 | if (IS_ERR(array)) | ||
222 | return PTR_ERR(array); | ||
216 | for (i = 0; i < array->size; i++) | 223 | for (i = 0; i < array->size; i++) |
217 | kfree(array->array[i].string.name); | 224 | kfree(array->array[i].string.name); |
218 | nfs_readdir_release_array(page); | 225 | nfs_readdir_release_array(page); |
@@ -244,7 +251,7 @@ int nfs_readdir_add_to_array(struct nfs_entry *entry, struct page *page) | |||
244 | 251 | ||
245 | if (IS_ERR(array)) | 252 | if (IS_ERR(array)) |
246 | return PTR_ERR(array); | 253 | return PTR_ERR(array); |
247 | ret = -EIO; | 254 | ret = -ENOSPC; |
248 | if (array->size >= MAX_READDIR_ARRAY) | 255 | if (array->size >= MAX_READDIR_ARRAY) |
249 | goto out; | 256 | goto out; |
250 | 257 | ||
@@ -255,9 +262,9 @@ int nfs_readdir_add_to_array(struct nfs_entry *entry, struct page *page) | |||
255 | if (ret) | 262 | if (ret) |
256 | goto out; | 263 | goto out; |
257 | array->last_cookie = entry->cookie; | 264 | array->last_cookie = entry->cookie; |
265 | array->size++; | ||
258 | if (entry->eof == 1) | 266 | if (entry->eof == 1) |
259 | array->eof_index = array->size; | 267 | array->eof_index = array->size; |
260 | array->size++; | ||
261 | out: | 268 | out: |
262 | nfs_readdir_release_array(page); | 269 | nfs_readdir_release_array(page); |
263 | return ret; | 270 | return ret; |
@@ -272,7 +279,7 @@ int nfs_readdir_search_for_pos(struct nfs_cache_array *array, nfs_readdir_descri | |||
272 | if (diff < 0) | 279 | if (diff < 0) |
273 | goto out_eof; | 280 | goto out_eof; |
274 | if (diff >= array->size) { | 281 | if (diff >= array->size) { |
275 | if (array->eof_index > 0) | 282 | if (array->eof_index >= 0) |
276 | goto out_eof; | 283 | goto out_eof; |
277 | desc->current_index += array->size; | 284 | desc->current_index += array->size; |
278 | return -EAGAIN; | 285 | return -EAGAIN; |
@@ -281,8 +288,6 @@ int nfs_readdir_search_for_pos(struct nfs_cache_array *array, nfs_readdir_descri | |||
281 | index = (unsigned int)diff; | 288 | index = (unsigned int)diff; |
282 | *desc->dir_cookie = array->array[index].cookie; | 289 | *desc->dir_cookie = array->array[index].cookie; |
283 | desc->cache_entry_index = index; | 290 | desc->cache_entry_index = index; |
284 | if (index == array->eof_index) | ||
285 | desc->eof = 1; | ||
286 | return 0; | 291 | return 0; |
287 | out_eof: | 292 | out_eof: |
288 | desc->eof = 1; | 293 | desc->eof = 1; |
@@ -296,17 +301,17 @@ int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_des | |||
296 | int status = -EAGAIN; | 301 | int status = -EAGAIN; |
297 | 302 | ||
298 | for (i = 0; i < array->size; i++) { | 303 | for (i = 0; i < array->size; i++) { |
299 | if (i == array->eof_index) { | ||
300 | desc->eof = 1; | ||
301 | status = -EBADCOOKIE; | ||
302 | } | ||
303 | if (array->array[i].cookie == *desc->dir_cookie) { | 304 | if (array->array[i].cookie == *desc->dir_cookie) { |
304 | desc->cache_entry_index = i; | 305 | desc->cache_entry_index = i; |
305 | status = 0; | 306 | status = 0; |
306 | break; | 307 | goto out; |
307 | } | 308 | } |
308 | } | 309 | } |
309 | 310 | if (i == array->eof_index) { | |
311 | desc->eof = 1; | ||
312 | status = -EBADCOOKIE; | ||
313 | } | ||
314 | out: | ||
310 | return status; | 315 | return status; |
311 | } | 316 | } |
312 | 317 | ||
@@ -449,7 +454,7 @@ out: | |||
449 | 454 | ||
450 | /* Perform conversion from xdr to cache array */ | 455 | /* Perform conversion from xdr to cache array */ |
451 | static | 456 | static |
452 | void nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, | 457 | int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, |
453 | void *xdr_page, struct page *page, unsigned int buflen) | 458 | void *xdr_page, struct page *page, unsigned int buflen) |
454 | { | 459 | { |
455 | struct xdr_stream stream; | 460 | struct xdr_stream stream; |
@@ -471,21 +476,29 @@ void nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *e | |||
471 | 476 | ||
472 | do { | 477 | do { |
473 | status = xdr_decode(desc, entry, &stream); | 478 | status = xdr_decode(desc, entry, &stream); |
474 | if (status != 0) | 479 | if (status != 0) { |
480 | if (status == -EAGAIN) | ||
481 | status = 0; | ||
475 | break; | 482 | break; |
483 | } | ||
476 | 484 | ||
477 | if (nfs_readdir_add_to_array(entry, page) == -1) | ||
478 | break; | ||
479 | if (desc->plus == 1) | 485 | if (desc->plus == 1) |
480 | nfs_prime_dcache(desc->file->f_path.dentry, entry); | 486 | nfs_prime_dcache(desc->file->f_path.dentry, entry); |
487 | |||
488 | status = nfs_readdir_add_to_array(entry, page); | ||
489 | if (status != 0) | ||
490 | break; | ||
481 | } while (!entry->eof); | 491 | } while (!entry->eof); |
482 | 492 | ||
483 | if (status == -EBADCOOKIE && entry->eof) { | 493 | if (status == -EBADCOOKIE && entry->eof) { |
484 | array = nfs_readdir_get_array(page); | 494 | array = nfs_readdir_get_array(page); |
485 | array->eof_index = array->size - 1; | 495 | if (!IS_ERR(array)) { |
486 | status = 0; | 496 | array->eof_index = array->size; |
487 | nfs_readdir_release_array(page); | 497 | status = 0; |
498 | nfs_readdir_release_array(page); | ||
499 | } | ||
488 | } | 500 | } |
501 | return status; | ||
489 | } | 502 | } |
490 | 503 | ||
491 | static | 504 | static |
@@ -537,7 +550,7 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page, | |||
537 | struct nfs_entry entry; | 550 | struct nfs_entry entry; |
538 | struct file *file = desc->file; | 551 | struct file *file = desc->file; |
539 | struct nfs_cache_array *array; | 552 | struct nfs_cache_array *array; |
540 | int status = 0; | 553 | int status = -ENOMEM; |
541 | unsigned int array_size = ARRAY_SIZE(pages); | 554 | unsigned int array_size = ARRAY_SIZE(pages); |
542 | 555 | ||
543 | entry.prev_cookie = 0; | 556 | entry.prev_cookie = 0; |
@@ -549,6 +562,10 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page, | |||
549 | goto out; | 562 | goto out; |
550 | 563 | ||
551 | array = nfs_readdir_get_array(page); | 564 | array = nfs_readdir_get_array(page); |
565 | if (IS_ERR(array)) { | ||
566 | status = PTR_ERR(array); | ||
567 | goto out; | ||
568 | } | ||
552 | memset(array, 0, sizeof(struct nfs_cache_array)); | 569 | memset(array, 0, sizeof(struct nfs_cache_array)); |
553 | array->eof_index = -1; | 570 | array->eof_index = -1; |
554 | 571 | ||
@@ -560,8 +577,13 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page, | |||
560 | 577 | ||
561 | if (status < 0) | 578 | if (status < 0) |
562 | break; | 579 | break; |
563 | nfs_readdir_page_filler(desc, &entry, pages_ptr, page, array_size * PAGE_SIZE); | 580 | status = nfs_readdir_page_filler(desc, &entry, pages_ptr, page, array_size * PAGE_SIZE); |
564 | } while (array->eof_index < 0 && array->size < MAX_READDIR_ARRAY); | 581 | if (status < 0) { |
582 | if (status == -ENOSPC) | ||
583 | status = 0; | ||
584 | break; | ||
585 | } | ||
586 | } while (array->eof_index < 0); | ||
565 | 587 | ||
566 | nfs_readdir_free_large_page(pages_ptr, pages, array_size); | 588 | nfs_readdir_free_large_page(pages_ptr, pages, array_size); |
567 | out_release_array: | 589 | out_release_array: |
@@ -582,8 +604,10 @@ static | |||
582 | int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page* page) | 604 | int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page* page) |
583 | { | 605 | { |
584 | struct inode *inode = desc->file->f_path.dentry->d_inode; | 606 | struct inode *inode = desc->file->f_path.dentry->d_inode; |
607 | int ret; | ||
585 | 608 | ||
586 | if (nfs_readdir_xdr_to_array(desc, page, inode) < 0) | 609 | ret = nfs_readdir_xdr_to_array(desc, page, inode); |
610 | if (ret < 0) | ||
587 | goto error; | 611 | goto error; |
588 | SetPageUptodate(page); | 612 | SetPageUptodate(page); |
589 | 613 | ||
@@ -595,7 +619,7 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page* page) | |||
595 | return 0; | 619 | return 0; |
596 | error: | 620 | error: |
597 | unlock_page(page); | 621 | unlock_page(page); |
598 | return -EIO; | 622 | return ret; |
599 | } | 623 | } |
600 | 624 | ||
601 | static | 625 | static |
@@ -608,12 +632,8 @@ void cache_page_release(nfs_readdir_descriptor_t *desc) | |||
608 | static | 632 | static |
609 | struct page *get_cache_page(nfs_readdir_descriptor_t *desc) | 633 | struct page *get_cache_page(nfs_readdir_descriptor_t *desc) |
610 | { | 634 | { |
611 | struct page *page; | 635 | return read_cache_page(desc->file->f_path.dentry->d_inode->i_mapping, |
612 | page = read_cache_page(desc->file->f_path.dentry->d_inode->i_mapping, | ||
613 | desc->page_index, (filler_t *)nfs_readdir_filler, desc); | 636 | desc->page_index, (filler_t *)nfs_readdir_filler, desc); |
614 | if (IS_ERR(page)) | ||
615 | desc->eof = 1; | ||
616 | return page; | ||
617 | } | 637 | } |
618 | 638 | ||
619 | /* | 639 | /* |
@@ -639,8 +659,10 @@ int find_cache_page(nfs_readdir_descriptor_t *desc) | |||
639 | static inline | 659 | static inline |
640 | int readdir_search_pagecache(nfs_readdir_descriptor_t *desc) | 660 | int readdir_search_pagecache(nfs_readdir_descriptor_t *desc) |
641 | { | 661 | { |
642 | int res = -EAGAIN; | 662 | int res; |
643 | 663 | ||
664 | if (desc->page_index == 0) | ||
665 | desc->current_index = 0; | ||
644 | while (1) { | 666 | while (1) { |
645 | res = find_cache_page(desc); | 667 | res = find_cache_page(desc); |
646 | if (res != -EAGAIN) | 668 | if (res != -EAGAIN) |
@@ -670,6 +692,8 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent, | |||
670 | struct dentry *dentry = NULL; | 692 | struct dentry *dentry = NULL; |
671 | 693 | ||
672 | array = nfs_readdir_get_array(desc->page); | 694 | array = nfs_readdir_get_array(desc->page); |
695 | if (IS_ERR(array)) | ||
696 | return PTR_ERR(array); | ||
673 | 697 | ||
674 | for (i = desc->cache_entry_index; i < array->size; i++) { | 698 | for (i = desc->cache_entry_index; i < array->size; i++) { |
675 | d_type = DT_UNKNOWN; | 699 | d_type = DT_UNKNOWN; |
@@ -685,11 +709,9 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent, | |||
685 | *desc->dir_cookie = array->array[i+1].cookie; | 709 | *desc->dir_cookie = array->array[i+1].cookie; |
686 | else | 710 | else |
687 | *desc->dir_cookie = array->last_cookie; | 711 | *desc->dir_cookie = array->last_cookie; |
688 | if (i == array->eof_index) { | ||
689 | desc->eof = 1; | ||
690 | break; | ||
691 | } | ||
692 | } | 712 | } |
713 | if (i == array->eof_index) | ||
714 | desc->eof = 1; | ||
693 | 715 | ||
694 | nfs_readdir_release_array(desc->page); | 716 | nfs_readdir_release_array(desc->page); |
695 | cache_page_release(desc); | 717 | cache_page_release(desc); |