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

+72 -42
+35 -29
fs/nfs/dir.c
··· 162 162 u64 cookie; 163 163 u64 ino; 164 164 struct qstr string; 165 + unsigned char d_type; 165 166 }; 166 167 167 168 struct nfs_cache_array { ··· 171 170 u64 last_cookie; 172 171 struct nfs_cache_array_entry array[0]; 173 172 }; 174 - 175 - #define MAX_READDIR_ARRAY ((PAGE_SIZE - sizeof(struct nfs_cache_array)) / sizeof(struct nfs_cache_array_entry)) 176 173 177 174 typedef __be32 * (*decode_dirent_t)(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int); 178 175 typedef struct { ··· 256 257 257 258 if (IS_ERR(array)) 258 259 return PTR_ERR(array); 259 - ret = -ENOSPC; 260 - if (array->size >= MAX_READDIR_ARRAY) 261 - goto out; 262 260 263 261 cache_entry = &array->array[array->size]; 262 + 263 + /* Check that this entry lies within the page bounds */ 264 + ret = -ENOSPC; 265 + if ((char *)&cache_entry[1] - (char *)page_address(page) > PAGE_SIZE) 266 + goto out; 267 + 264 268 cache_entry->cookie = entry->prev_cookie; 265 269 cache_entry->ino = entry->ino; 270 + cache_entry->d_type = entry->d_type; 266 271 ret = nfs_readdir_make_qstr(&cache_entry->string, entry->name, entry->len); 267 272 if (ret) 268 273 goto out; ··· 469 466 struct xdr_stream stream; 470 467 struct xdr_buf buf; 471 468 __be32 *ptr = xdr_page; 472 - int status; 473 469 struct nfs_cache_array *array; 470 + unsigned int count = 0; 471 + int status; 474 472 475 473 buf.head->iov_base = xdr_page; 476 474 buf.head->iov_len = buflen; ··· 492 488 break; 493 489 } 494 490 491 + count++; 492 + 495 493 if (desc->plus == 1) 496 494 nfs_prime_dcache(desc->file->f_path.dentry, entry); 497 495 ··· 502 496 break; 503 497 } while (!entry->eof); 504 498 505 - if (status == -EBADCOOKIE && entry->eof) { 499 + if (count == 0 || (status == -EBADCOOKIE && entry->eof == 1)) { 506 500 array = nfs_readdir_get_array(page); 507 501 if (!IS_ERR(array)) { 508 502 array->eof_index = array->size; 509 503 status = 0; 510 504 nfs_readdir_release_array(page); 511 - } 505 + } else 506 + status = PTR_ERR(array); 512 507 } 513 508 return status; 514 509 } ··· 703 696 int i = 0; 704 697 int res = 0; 705 698 struct nfs_cache_array *array = NULL; 706 - unsigned int d_type = DT_UNKNOWN; 707 - struct dentry *dentry = NULL; 708 699 709 700 array = nfs_readdir_get_array(desc->page); 710 - if (IS_ERR(array)) 711 - return PTR_ERR(array); 701 + if (IS_ERR(array)) { 702 + res = PTR_ERR(array); 703 + goto out; 704 + } 712 705 713 706 for (i = desc->cache_entry_index; i < array->size; i++) { 714 - d_type = DT_UNKNOWN; 707 + struct nfs_cache_array_entry *ent; 715 708 716 - res = filldir(dirent, array->array[i].string.name, 717 - array->array[i].string.len, file->f_pos, 718 - nfs_compat_user_ino64(array->array[i].ino), d_type); 719 - if (res < 0) 709 + ent = &array->array[i]; 710 + if (filldir(dirent, ent->string.name, ent->string.len, 711 + file->f_pos, nfs_compat_user_ino64(ent->ino), 712 + ent->d_type) < 0) { 713 + desc->eof = 1; 720 714 break; 715 + } 721 716 file->f_pos++; 722 717 desc->cache_entry_index = i; 723 718 if (i < (array->size-1)) ··· 731 722 desc->eof = 1; 732 723 733 724 nfs_readdir_release_array(desc->page); 725 + out: 734 726 cache_page_release(desc); 735 - if (dentry != NULL) 736 - dput(dentry); 737 727 dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n", 738 728 (unsigned long long)*desc->dir_cookie, res); 739 729 return res; ··· 767 759 goto out; 768 760 } 769 761 770 - if (nfs_readdir_xdr_to_array(desc, page, inode) == -1) { 771 - status = -EIO; 772 - goto out_release; 773 - } 774 - 775 762 desc->page_index = 0; 776 763 desc->page = page; 764 + 765 + status = nfs_readdir_xdr_to_array(desc, page, inode); 766 + if (status < 0) 767 + goto out_release; 768 + 777 769 status = nfs_do_filldir(desc, dirent, filldir); 778 770 779 771 out: ··· 824 816 res = readdir_search_pagecache(desc); 825 817 826 818 if (res == -EBADCOOKIE) { 819 + res = 0; 827 820 /* This means either end of directory */ 828 821 if (*desc->dir_cookie && desc->eof == 0) { 829 822 /* Or that the server has 'lost' a cookie */ 830 823 res = uncached_readdir(desc, dirent, filldir); 831 - if (res >= 0) 824 + if (res == 0) 832 825 continue; 833 826 } 834 - res = 0; 835 827 break; 836 828 } 837 829 if (res == -ETOOSMALL && desc->plus) { ··· 846 838 break; 847 839 848 840 res = nfs_do_filldir(desc, dirent, filldir); 849 - if (res < 0) { 850 - res = 0; 841 + if (res < 0) 851 842 break; 852 - } 853 843 } 854 844 out: 855 845 nfs_unblock_sillyrename(dentry);
+1 -1
fs/nfs/direct.c
··· 867 867 goto out; 868 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 871 sync = NFS_FILE_SYNC; 872 872 873 873 dreq->inode = inode;
+9
fs/nfs/internal.h
··· 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 374 * Determine the number of pages in an array of length 'len' and 366 375 * with a base offset of 'base' 367 376 */
+3 -1
fs/nfs/nfs2xdr.c
··· 485 485 entry->prev_cookie = entry->cookie; 486 486 entry->cookie = ntohl(*p++); 487 487 488 + entry->d_type = DT_UNKNOWN; 489 + 488 490 p = xdr_inline_peek(xdr, 8); 489 491 if (p != NULL) 490 492 entry->eof = !p[0] && p[1]; ··· 497 495 498 496 out_overflow: 499 497 print_overflow_msg(__func__, xdr); 500 - return ERR_PTR(-EIO); 498 + return ERR_PTR(-EAGAIN); 501 499 } 502 500 503 501 /*
+3 -1
fs/nfs/nfs3xdr.c
··· 622 622 entry->prev_cookie = entry->cookie; 623 623 p = xdr_decode_hyper(p, &entry->cookie); 624 624 625 + entry->d_type = DT_UNKNOWN; 625 626 if (plus) { 626 627 entry->fattr->valid = 0; 627 628 p = xdr_decode_post_op_attr_stream(xdr, entry->fattr); 628 629 if (IS_ERR(p)) 629 630 goto out_overflow_exit; 631 + entry->d_type = nfs_umode_to_dtype(entry->fattr->mode); 630 632 /* In fact, a post_op_fh3: */ 631 633 p = xdr_inline_decode(xdr, 4); 632 634 if (unlikely(!p)) ··· 658 656 out_overflow: 659 657 print_overflow_msg(__func__, xdr); 660 658 out_overflow_exit: 661 - return ERR_PTR(-EIO); 659 + return ERR_PTR(-EAGAIN); 662 660 } 663 661 664 662 /*
+5 -1
fs/nfs/nfs4xdr.c
··· 6208 6208 if (entry->fattr->valid & NFS_ATTR_FATTR_FILEID) 6209 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 6215 if (verify_attr_len(xdr, p, len) < 0) 6212 6216 goto out_overflow; 6213 6217 ··· 6225 6221 6226 6222 out_overflow: 6227 6223 print_overflow_msg(__func__, xdr); 6228 - return ERR_PTR(-EIO); 6224 + return ERR_PTR(-EAGAIN); 6229 6225 } 6230 6226 6231 6227 /*
+1
include/linux/nfs_xdr.h
··· 483 483 int eof; 484 484 struct nfs_fh * fh; 485 485 struct nfs_fattr * fattr; 486 + unsigned char d_type; 486 487 }; 487 488 488 489 /*
+15 -9
net/sunrpc/clnt.c
··· 989 989 dprint_status(task); 990 990 991 991 task->tk_status = 0; 992 - task->tk_action = call_allocate; 993 - if (status >= 0 && rpcauth_uptodatecred(task)) 994 - return; 992 + task->tk_action = call_refresh; 995 993 switch (status) { 996 - case -EACCES: 997 - rpc_exit(task, -EACCES); 998 - return; 999 - case -ENOMEM: 1000 - rpc_exit(task, -ENOMEM); 994 + case 0: 995 + if (rpcauth_uptodatecred(task)) 996 + task->tk_action = call_allocate; 1001 997 return; 1002 998 case -ETIMEDOUT: 1003 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 /*