aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2010-11-26 17:30:30 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2010-11-26 17:30:30 -0500
commit19650e8580987c0ffabc2fe2cbc16b944789df8b (patch)
treecf0cfb390a128e3b2d47daa46f59de0a62a8ff04
parent1eb4c6362cb7d6a2f904c555c10dc45caeeefc31 (diff)
parent0b26a0bf6ff398185546432420bb772bcfdf8d94 (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.c62
-rw-r--r--fs/nfs/direct.c2
-rw-r--r--fs/nfs/internal.h9
-rw-r--r--fs/nfs/nfs2xdr.c4
-rw-r--r--fs/nfs/nfs3xdr.c4
-rw-r--r--fs/nfs/nfs4xdr.c6
-rw-r--r--include/linux/nfs_xdr.h1
-rw-r--r--net/sunrpc/clnt.c24
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
167struct nfs_cache_array { 168struct 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
176typedef __be32 * (*decode_dirent_t)(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int); 175typedef __be32 * (*decode_dirent_t)(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int);
177typedef struct { 176typedef 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);
734out:
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 }
846out: 852out:
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 */
367static inline
368unsigned 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
496out_overflow: 498out_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
656out_overflow: 658out_overflow:
657 print_overflow_msg(__func__, xdr); 659 print_overflow_msg(__func__, xdr);
658out_overflow_exit: 660out_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
6222out_overflow: 6226out_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/*