diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-11-26 17:30:30 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-11-26 17:30:30 -0500 |
commit | 19650e8580987c0ffabc2fe2cbc16b944789df8b (patch) | |
tree | cf0cfb390a128e3b2d47daa46f59de0a62a8ff04 | |
parent | 1eb4c6362cb7d6a2f904c555c10dc45caeeefc31 (diff) | |
parent | 0b26a0bf6ff398185546432420bb772bcfdf8d94 (diff) |
Merge branch 'bugfixes' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6
* 'bugfixes' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6:
NFS: Ensure we return the dirent->d_type when it is known
NFS: Correct the array bound calculation in nfs_readdir_add_to_array
NFS: Don't ignore errors from nfs_do_filldir()
NFS: Fix the error handling in "uncached_readdir()"
NFS: Fix a page leak in uncached_readdir()
NFS: Fix a page leak in nfs_do_filldir()
NFS: Assume eof if the server returns no readdir records
NFS: Buffer overflow in ->decode_dirent() should not be fatal
Pure nfs client performance using odirect.
SUNRPC: Fix an infinite loop in call_refresh/call_refreshresult
-rw-r--r-- | fs/nfs/dir.c | 62 | ||||
-rw-r--r-- | fs/nfs/direct.c | 2 | ||||
-rw-r--r-- | fs/nfs/internal.h | 9 | ||||
-rw-r--r-- | fs/nfs/nfs2xdr.c | 4 | ||||
-rw-r--r-- | fs/nfs/nfs3xdr.c | 4 | ||||
-rw-r--r-- | fs/nfs/nfs4xdr.c | 6 | ||||
-rw-r--r-- | include/linux/nfs_xdr.h | 1 | ||||
-rw-r--r-- | net/sunrpc/clnt.c | 24 |
8 files changed, 71 insertions, 41 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 662df2a5fad5..8ea4a4180a87 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
@@ -162,6 +162,7 @@ struct nfs_cache_array_entry { | |||
162 | u64 cookie; | 162 | u64 cookie; |
163 | u64 ino; | 163 | u64 ino; |
164 | struct qstr string; | 164 | struct qstr string; |
165 | unsigned char d_type; | ||
165 | }; | 166 | }; |
166 | 167 | ||
167 | struct nfs_cache_array { | 168 | struct nfs_cache_array { |
@@ -171,8 +172,6 @@ struct nfs_cache_array { | |||
171 | struct nfs_cache_array_entry array[0]; | 172 | struct nfs_cache_array_entry array[0]; |
172 | }; | 173 | }; |
173 | 174 | ||
174 | #define MAX_READDIR_ARRAY ((PAGE_SIZE - sizeof(struct nfs_cache_array)) / sizeof(struct nfs_cache_array_entry)) | ||
175 | |||
176 | typedef __be32 * (*decode_dirent_t)(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int); | 175 | typedef __be32 * (*decode_dirent_t)(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int); |
177 | typedef struct { | 176 | typedef struct { |
178 | struct file *file; | 177 | struct file *file; |
@@ -257,13 +256,17 @@ int nfs_readdir_add_to_array(struct nfs_entry *entry, struct page *page) | |||
257 | 256 | ||
258 | if (IS_ERR(array)) | 257 | if (IS_ERR(array)) |
259 | return PTR_ERR(array); | 258 | return PTR_ERR(array); |
259 | |||
260 | cache_entry = &array->array[array->size]; | ||
261 | |||
262 | /* Check that this entry lies within the page bounds */ | ||
260 | ret = -ENOSPC; | 263 | ret = -ENOSPC; |
261 | if (array->size >= MAX_READDIR_ARRAY) | 264 | if ((char *)&cache_entry[1] - (char *)page_address(page) > PAGE_SIZE) |
262 | goto out; | 265 | goto out; |
263 | 266 | ||
264 | cache_entry = &array->array[array->size]; | ||
265 | cache_entry->cookie = entry->prev_cookie; | 267 | cache_entry->cookie = entry->prev_cookie; |
266 | cache_entry->ino = entry->ino; | 268 | cache_entry->ino = entry->ino; |
269 | cache_entry->d_type = entry->d_type; | ||
267 | ret = nfs_readdir_make_qstr(&cache_entry->string, entry->name, entry->len); | 270 | ret = nfs_readdir_make_qstr(&cache_entry->string, entry->name, entry->len); |
268 | if (ret) | 271 | if (ret) |
269 | goto out; | 272 | goto out; |
@@ -466,8 +469,9 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en | |||
466 | struct xdr_stream stream; | 469 | struct xdr_stream stream; |
467 | struct xdr_buf buf; | 470 | struct xdr_buf buf; |
468 | __be32 *ptr = xdr_page; | 471 | __be32 *ptr = xdr_page; |
469 | int status; | ||
470 | struct nfs_cache_array *array; | 472 | struct nfs_cache_array *array; |
473 | unsigned int count = 0; | ||
474 | int status; | ||
471 | 475 | ||
472 | buf.head->iov_base = xdr_page; | 476 | buf.head->iov_base = xdr_page; |
473 | buf.head->iov_len = buflen; | 477 | buf.head->iov_len = buflen; |
@@ -488,6 +492,8 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en | |||
488 | break; | 492 | break; |
489 | } | 493 | } |
490 | 494 | ||
495 | count++; | ||
496 | |||
491 | if (desc->plus == 1) | 497 | if (desc->plus == 1) |
492 | nfs_prime_dcache(desc->file->f_path.dentry, entry); | 498 | nfs_prime_dcache(desc->file->f_path.dentry, entry); |
493 | 499 | ||
@@ -496,13 +502,14 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en | |||
496 | break; | 502 | break; |
497 | } while (!entry->eof); | 503 | } while (!entry->eof); |
498 | 504 | ||
499 | if (status == -EBADCOOKIE && entry->eof) { | 505 | if (count == 0 || (status == -EBADCOOKIE && entry->eof == 1)) { |
500 | array = nfs_readdir_get_array(page); | 506 | array = nfs_readdir_get_array(page); |
501 | if (!IS_ERR(array)) { | 507 | if (!IS_ERR(array)) { |
502 | array->eof_index = array->size; | 508 | array->eof_index = array->size; |
503 | status = 0; | 509 | status = 0; |
504 | nfs_readdir_release_array(page); | 510 | nfs_readdir_release_array(page); |
505 | } | 511 | } else |
512 | status = PTR_ERR(array); | ||
506 | } | 513 | } |
507 | return status; | 514 | return status; |
508 | } | 515 | } |
@@ -696,21 +703,23 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent, | |||
696 | int i = 0; | 703 | int i = 0; |
697 | int res = 0; | 704 | int res = 0; |
698 | struct nfs_cache_array *array = NULL; | 705 | struct nfs_cache_array *array = NULL; |
699 | unsigned int d_type = DT_UNKNOWN; | ||
700 | struct dentry *dentry = NULL; | ||
701 | 706 | ||
702 | array = nfs_readdir_get_array(desc->page); | 707 | array = nfs_readdir_get_array(desc->page); |
703 | if (IS_ERR(array)) | 708 | if (IS_ERR(array)) { |
704 | return PTR_ERR(array); | 709 | res = PTR_ERR(array); |
710 | goto out; | ||
711 | } | ||
705 | 712 | ||
706 | for (i = desc->cache_entry_index; i < array->size; i++) { | 713 | for (i = desc->cache_entry_index; i < array->size; i++) { |
707 | d_type = DT_UNKNOWN; | 714 | struct nfs_cache_array_entry *ent; |
708 | 715 | ||
709 | res = filldir(dirent, array->array[i].string.name, | 716 | ent = &array->array[i]; |
710 | array->array[i].string.len, file->f_pos, | 717 | if (filldir(dirent, ent->string.name, ent->string.len, |
711 | nfs_compat_user_ino64(array->array[i].ino), d_type); | 718 | file->f_pos, nfs_compat_user_ino64(ent->ino), |
712 | if (res < 0) | 719 | ent->d_type) < 0) { |
720 | desc->eof = 1; | ||
713 | break; | 721 | break; |
722 | } | ||
714 | file->f_pos++; | 723 | file->f_pos++; |
715 | desc->cache_entry_index = i; | 724 | desc->cache_entry_index = i; |
716 | if (i < (array->size-1)) | 725 | if (i < (array->size-1)) |
@@ -722,9 +731,8 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent, | |||
722 | desc->eof = 1; | 731 | desc->eof = 1; |
723 | 732 | ||
724 | nfs_readdir_release_array(desc->page); | 733 | nfs_readdir_release_array(desc->page); |
734 | out: | ||
725 | cache_page_release(desc); | 735 | cache_page_release(desc); |
726 | if (dentry != NULL) | ||
727 | dput(dentry); | ||
728 | dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n", | 736 | dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n", |
729 | (unsigned long long)*desc->dir_cookie, res); | 737 | (unsigned long long)*desc->dir_cookie, res); |
730 | return res; | 738 | return res; |
@@ -759,13 +767,13 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent, | |||
759 | goto out; | 767 | goto out; |
760 | } | 768 | } |
761 | 769 | ||
762 | if (nfs_readdir_xdr_to_array(desc, page, inode) == -1) { | ||
763 | status = -EIO; | ||
764 | goto out_release; | ||
765 | } | ||
766 | |||
767 | desc->page_index = 0; | 770 | desc->page_index = 0; |
768 | desc->page = page; | 771 | desc->page = page; |
772 | |||
773 | status = nfs_readdir_xdr_to_array(desc, page, inode); | ||
774 | if (status < 0) | ||
775 | goto out_release; | ||
776 | |||
769 | status = nfs_do_filldir(desc, dirent, filldir); | 777 | status = nfs_do_filldir(desc, dirent, filldir); |
770 | 778 | ||
771 | out: | 779 | out: |
@@ -816,14 +824,14 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
816 | res = readdir_search_pagecache(desc); | 824 | res = readdir_search_pagecache(desc); |
817 | 825 | ||
818 | if (res == -EBADCOOKIE) { | 826 | if (res == -EBADCOOKIE) { |
827 | res = 0; | ||
819 | /* This means either end of directory */ | 828 | /* This means either end of directory */ |
820 | if (*desc->dir_cookie && desc->eof == 0) { | 829 | if (*desc->dir_cookie && desc->eof == 0) { |
821 | /* Or that the server has 'lost' a cookie */ | 830 | /* Or that the server has 'lost' a cookie */ |
822 | res = uncached_readdir(desc, dirent, filldir); | 831 | res = uncached_readdir(desc, dirent, filldir); |
823 | if (res >= 0) | 832 | if (res == 0) |
824 | continue; | 833 | continue; |
825 | } | 834 | } |
826 | res = 0; | ||
827 | break; | 835 | break; |
828 | } | 836 | } |
829 | if (res == -ETOOSMALL && desc->plus) { | 837 | if (res == -ETOOSMALL && desc->plus) { |
@@ -838,10 +846,8 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
838 | break; | 846 | break; |
839 | 847 | ||
840 | res = nfs_do_filldir(desc, dirent, filldir); | 848 | res = nfs_do_filldir(desc, dirent, filldir); |
841 | if (res < 0) { | 849 | if (res < 0) |
842 | res = 0; | ||
843 | break; | 850 | break; |
844 | } | ||
845 | } | 851 | } |
846 | out: | 852 | out: |
847 | nfs_unblock_sillyrename(dentry); | 853 | nfs_unblock_sillyrename(dentry); |
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 84d3c8b90206..e6ace0d93c71 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c | |||
@@ -867,7 +867,7 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov, | |||
867 | goto out; | 867 | goto out; |
868 | nfs_alloc_commit_data(dreq); | 868 | nfs_alloc_commit_data(dreq); |
869 | 869 | ||
870 | if (dreq->commit_data == NULL || count < wsize) | 870 | if (dreq->commit_data == NULL || count <= wsize) |
871 | sync = NFS_FILE_SYNC; | 871 | sync = NFS_FILE_SYNC; |
872 | 872 | ||
873 | dreq->inode = inode; | 873 | dreq->inode = inode; |
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index db08ff3ff454..e6356b750b77 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h | |||
@@ -362,6 +362,15 @@ unsigned int nfs_page_length(struct page *page) | |||
362 | } | 362 | } |
363 | 363 | ||
364 | /* | 364 | /* |
365 | * Convert a umode to a dirent->d_type | ||
366 | */ | ||
367 | static inline | ||
368 | unsigned char nfs_umode_to_dtype(umode_t mode) | ||
369 | { | ||
370 | return (mode >> 12) & 15; | ||
371 | } | ||
372 | |||
373 | /* | ||
365 | * Determine the number of pages in an array of length 'len' and | 374 | * Determine the number of pages in an array of length 'len' and |
366 | * with a base offset of 'base' | 375 | * with a base offset of 'base' |
367 | */ | 376 | */ |
diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index 2563f765c9b4..5914a1911c95 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c | |||
@@ -485,6 +485,8 @@ nfs_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_se | |||
485 | entry->prev_cookie = entry->cookie; | 485 | entry->prev_cookie = entry->cookie; |
486 | entry->cookie = ntohl(*p++); | 486 | entry->cookie = ntohl(*p++); |
487 | 487 | ||
488 | entry->d_type = DT_UNKNOWN; | ||
489 | |||
488 | p = xdr_inline_peek(xdr, 8); | 490 | p = xdr_inline_peek(xdr, 8); |
489 | if (p != NULL) | 491 | if (p != NULL) |
490 | entry->eof = !p[0] && p[1]; | 492 | entry->eof = !p[0] && p[1]; |
@@ -495,7 +497,7 @@ nfs_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_se | |||
495 | 497 | ||
496 | out_overflow: | 498 | out_overflow: |
497 | print_overflow_msg(__func__, xdr); | 499 | print_overflow_msg(__func__, xdr); |
498 | return ERR_PTR(-EIO); | 500 | return ERR_PTR(-EAGAIN); |
499 | } | 501 | } |
500 | 502 | ||
501 | /* | 503 | /* |
diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c index 748dc91a4a14..f6cc60f06dac 100644 --- a/fs/nfs/nfs3xdr.c +++ b/fs/nfs/nfs3xdr.c | |||
@@ -622,11 +622,13 @@ nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_s | |||
622 | entry->prev_cookie = entry->cookie; | 622 | entry->prev_cookie = entry->cookie; |
623 | p = xdr_decode_hyper(p, &entry->cookie); | 623 | p = xdr_decode_hyper(p, &entry->cookie); |
624 | 624 | ||
625 | entry->d_type = DT_UNKNOWN; | ||
625 | if (plus) { | 626 | if (plus) { |
626 | entry->fattr->valid = 0; | 627 | entry->fattr->valid = 0; |
627 | p = xdr_decode_post_op_attr_stream(xdr, entry->fattr); | 628 | p = xdr_decode_post_op_attr_stream(xdr, entry->fattr); |
628 | if (IS_ERR(p)) | 629 | if (IS_ERR(p)) |
629 | goto out_overflow_exit; | 630 | goto out_overflow_exit; |
631 | entry->d_type = nfs_umode_to_dtype(entry->fattr->mode); | ||
630 | /* In fact, a post_op_fh3: */ | 632 | /* In fact, a post_op_fh3: */ |
631 | p = xdr_inline_decode(xdr, 4); | 633 | p = xdr_inline_decode(xdr, 4); |
632 | if (unlikely(!p)) | 634 | if (unlikely(!p)) |
@@ -656,7 +658,7 @@ nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_s | |||
656 | out_overflow: | 658 | out_overflow: |
657 | print_overflow_msg(__func__, xdr); | 659 | print_overflow_msg(__func__, xdr); |
658 | out_overflow_exit: | 660 | out_overflow_exit: |
659 | return ERR_PTR(-EIO); | 661 | return ERR_PTR(-EAGAIN); |
660 | } | 662 | } |
661 | 663 | ||
662 | /* | 664 | /* |
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index b7a204ff6fe1..9f1826b012e6 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c | |||
@@ -6208,6 +6208,10 @@ __be32 *nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, | |||
6208 | if (entry->fattr->valid & NFS_ATTR_FATTR_FILEID) | 6208 | if (entry->fattr->valid & NFS_ATTR_FATTR_FILEID) |
6209 | entry->ino = entry->fattr->fileid; | 6209 | entry->ino = entry->fattr->fileid; |
6210 | 6210 | ||
6211 | entry->d_type = DT_UNKNOWN; | ||
6212 | if (entry->fattr->valid & NFS_ATTR_FATTR_TYPE) | ||
6213 | entry->d_type = nfs_umode_to_dtype(entry->fattr->mode); | ||
6214 | |||
6211 | if (verify_attr_len(xdr, p, len) < 0) | 6215 | if (verify_attr_len(xdr, p, len) < 0) |
6212 | goto out_overflow; | 6216 | goto out_overflow; |
6213 | 6217 | ||
@@ -6221,7 +6225,7 @@ __be32 *nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, | |||
6221 | 6225 | ||
6222 | out_overflow: | 6226 | out_overflow: |
6223 | print_overflow_msg(__func__, xdr); | 6227 | print_overflow_msg(__func__, xdr); |
6224 | return ERR_PTR(-EIO); | 6228 | return ERR_PTR(-EAGAIN); |
6225 | } | 6229 | } |
6226 | 6230 | ||
6227 | /* | 6231 | /* |
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index ba6cc8f223c9..80f07198a31a 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h | |||
@@ -483,6 +483,7 @@ struct nfs_entry { | |||
483 | int eof; | 483 | int eof; |
484 | struct nfs_fh * fh; | 484 | struct nfs_fh * fh; |
485 | struct nfs_fattr * fattr; | 485 | struct nfs_fattr * fattr; |
486 | unsigned char d_type; | ||
486 | }; | 487 | }; |
487 | 488 | ||
488 | /* | 489 | /* |
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 9dab9573be41..92ce94f5146b 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c | |||
@@ -989,20 +989,26 @@ call_refreshresult(struct rpc_task *task) | |||
989 | dprint_status(task); | 989 | dprint_status(task); |
990 | 990 | ||
991 | task->tk_status = 0; | 991 | task->tk_status = 0; |
992 | task->tk_action = call_allocate; | 992 | task->tk_action = call_refresh; |
993 | if (status >= 0 && rpcauth_uptodatecred(task)) | ||
994 | return; | ||
995 | switch (status) { | 993 | switch (status) { |
996 | case -EACCES: | 994 | case 0: |
997 | rpc_exit(task, -EACCES); | 995 | if (rpcauth_uptodatecred(task)) |
998 | return; | 996 | task->tk_action = call_allocate; |
999 | case -ENOMEM: | ||
1000 | rpc_exit(task, -ENOMEM); | ||
1001 | return; | 997 | return; |
1002 | case -ETIMEDOUT: | 998 | case -ETIMEDOUT: |
1003 | rpc_delay(task, 3*HZ); | 999 | rpc_delay(task, 3*HZ); |
1000 | case -EAGAIN: | ||
1001 | status = -EACCES; | ||
1002 | if (!task->tk_cred_retry) | ||
1003 | break; | ||
1004 | task->tk_cred_retry--; | ||
1005 | dprintk("RPC: %5u %s: retry refresh creds\n", | ||
1006 | task->tk_pid, __func__); | ||
1007 | return; | ||
1004 | } | 1008 | } |
1005 | task->tk_action = call_refresh; | 1009 | dprintk("RPC: %5u %s: refresh creds failed with error %d\n", |
1010 | task->tk_pid, __func__, status); | ||
1011 | rpc_exit(task, status); | ||
1006 | } | 1012 | } |
1007 | 1013 | ||
1008 | /* | 1014 | /* |