diff -Nru linux-old/fs/nfs/dir.c linux-new/fs/nfs/dir.c
--- linux-old/fs/nfs/dir.c	2002-10-23 21:59:38.000000000 -0400
+++ linux-new/fs/nfs/dir.c	2002-10-23 22:10:25.000000000 -0400
@@ -34,8 +34,11 @@
 #define NFS_PARANOIA 1
 /* #define NFS_DEBUG_VERBOSE 1 */
 
+static loff_t nfs_dir_llseek(struct file *, loff_t, int);
 static int nfs_readdir(struct file *, void *, filldir_t);
 static struct dentry *nfs_lookup(struct inode *, struct dentry *);
+static int nfs_cached_lookup(struct inode *, struct dentry *,
+				struct nfs_fh *, struct nfs_fattr *);
 static int nfs_create(struct inode *, struct dentry *, int);
 static int nfs_mkdir(struct inode *, struct dentry *, int);
 static int nfs_rmdir(struct inode *, struct dentry *);
@@ -48,6 +51,7 @@
 static int nfs_fsync_dir(struct file *, struct dentry *, int);
 
 struct file_operations nfs_dir_operations = {
+	llseek:		nfs_dir_llseek,
 	read:		generic_read_dir,
 	readdir:	nfs_readdir,
 	open:		nfs_open,
@@ -70,6 +74,25 @@
 	setattr:	nfs_notify_change,
 };
 
+static loff_t nfs_dir_llseek(struct file *file, loff_t offset, int origin)
+{
+	switch (origin) {
+		case 1:
+			if (offset == 0) {
+				offset = file->f_pos;
+				break;
+			}
+		case 2:
+			return -EINVAL;
+	}
+	if (offset != file->f_pos) {
+		file->f_pos = offset;
+		file->f_reada = 0;
+		file->f_version = ++event;
+	}
+	return (offset <= 0) ? 0 : offset;
+}
+
 typedef u32 * (*decode_dirent_t)(u32 *, struct nfs_entry *, int);
 typedef struct {
 	struct file	*file;
@@ -109,13 +132,15 @@
 	error = NFS_PROTO(inode)->readdir(inode, cred, desc->entry->cookie, page,
 					  NFS_SERVER(inode)->dtsize, desc->plus);
 	/* We requested READDIRPLUS, but the server doesn't grok it */
-	if (desc->plus && error == -ENOTSUPP) {
-		NFS_FLAGS(inode) &= ~NFS_INO_ADVISE_RDPLUS;
-		desc->plus = 0;
-		goto again;
-	}
-	if (error < 0)
+	if (error < 0) {
+		if (error == -ENOTSUPP && desc->plus) {
+			NFS_SERVER(inode)->caps &= ~NFS_CAP_READDIRPLUS;
+			NFS_FLAGS(inode) &= ~NFS_INO_ADVISE_RDPLUS;
+			desc->plus = 0;
+			goto again;
+		}
 		goto error;
+	}
 	SetPageUptodate(page);
 	/* Ensure consistent page alignment of the data.
 	 * Note: assumes we have exclusive access to this mapping either
@@ -195,8 +220,7 @@
 
 	dfprintk(VFS, "NFS: find_dirent_page() searching directory page %ld\n", desc->page_index);
 
-	desc->plus = NFS_USE_READDIRPLUS(inode);
-	page = read_cache_page(&inode->i_data, desc->page_index,
+	page = read_cache_page(inode->i_mapping, desc->page_index,
 			       (filler_t *)nfs_readdir_filler, desc);
 	if (IS_ERR(page)) {
 		status = PTR_ERR(page);
@@ -247,6 +271,24 @@
 	return res;
 }
 
+static unsigned int nfs_type2dtype[] = {
+	DT_UNKNOWN,
+	DT_REG,
+	DT_DIR,
+	DT_BLK,
+	DT_CHR,
+	DT_LNK,
+	DT_SOCK,
+	DT_UNKNOWN,
+	DT_FIFO
+};
+
+static inline
+unsigned int nfs_type_to_d_type(enum nfs_ftype type)
+{
+	return nfs_type2dtype[type];
+}
+
 /*
  * Once we've found the start of the dirent within a page: fill 'er up...
  */
@@ -263,11 +305,17 @@
 	dfprintk(VFS, "NFS: nfs_do_filldir() filling starting @ cookie %Lu\n", (long long)desc->target);
 
 	for(;;) {
+		unsigned d_type = DT_UNKNOWN;
 		/* Note: entry->prev_cookie contains the cookie for
 		 *	 retrieving the current dirent on the server */
 		fileid = nfs_fileid_to_ino_t(entry->ino);
+
+		/* Use readdirplus info */
+		if (desc->plus && (entry->fattr->valid & NFS_ATTR_FATTR))
+			d_type = nfs_type_to_d_type(entry->fattr->type);
+
 		res = filldir(dirent, entry->name, entry->len, 
-			      entry->prev_cookie, fileid, DT_UNKNOWN);
+			      entry->prev_cookie, fileid, d_type);
 		if (res < 0)
 			break;
 		file->f_pos = desc->target = entry->cookie;
@@ -334,7 +382,8 @@
 	/* Reset read descriptor so it searches the page cache from
 	 * the start upon the next call to readdir_search_pagecache() */
 	desc->page_index = 0;
-	memset(desc->entry, 0, sizeof(*desc->entry));
+	desc->entry->cookie = desc->entry->prev_cookie = 0;
+	desc->entry->eof = 0;
  out:
 	dfprintk(VFS, "NFS: uncached_readdir() returns %d\n", status);
 	return status;
@@ -353,9 +402,11 @@
 	nfs_readdir_descriptor_t my_desc,
 			*desc = &my_desc;
 	struct nfs_entry my_entry;
+	struct nfs_fh	fh;
+	struct nfs_fattr fattr;
 	long		res;
 
-	res = nfs_revalidate(dentry);
+	res = nfs_revalidate_inode(NFS_SERVER(inode), inode);
 	if (res < 0)
 		return res;
 
@@ -366,12 +417,16 @@
 	 * itself.
 	 */
 	memset(desc, 0, sizeof(*desc));
-	memset(&my_entry, 0, sizeof(my_entry));
-
 	desc->file = filp;
 	desc->target = filp->f_pos;
-	desc->entry = &my_entry;
 	desc->decode = NFS_PROTO(inode)->decode_dirent;
+	desc->plus = NFS_USE_READDIRPLUS(inode);
+
+	my_entry.cookie = my_entry.prev_cookie = 0;
+	my_entry.eof = 0;
+	my_entry.fh = &fh;
+	my_entry.fattr = &fattr;
+	desc->entry = &my_entry;
 
 	while(!desc->entry->eof) {
 		res = readdir_search_pagecache(desc);
@@ -435,16 +490,9 @@
 }
 
 static inline
-int nfs_lookup_verify_inode(struct inode *inode, int flags)
+int nfs_lookup_verify_inode(struct inode *inode)
 {
-	struct nfs_server *server = NFS_SERVER(inode);
-	/*
-	 * If we're interested in close-to-open cache consistency,
-	 * then we revalidate the inode upon lookup.
-	 */
-	if (!(server->flags & NFS_MOUNT_NOCTO) && !(flags & (LOOKUP_CONTINUE|LOOKUP_PARENT)))
-		NFS_CACHEINV(inode);
-	return nfs_revalidate_inode(server, inode);
+	return nfs_revalidate_inode(NFS_SERVER(inode), inode);
 }
 
 /*
@@ -498,11 +546,20 @@
 
 	/* Force a full look up iff the parent directory has changed */
 	if (nfs_check_verifier(dir, dentry)) {
-		if (nfs_lookup_verify_inode(inode, flags))
+		if (nfs_lookup_verify_inode(inode))
 			goto out_bad;
 		goto out_valid;
 	}
 
+	error = nfs_cached_lookup(dir, dentry, &fhandle, &fattr);
+	if (!error) {
+		if (memcmp(NFS_FH(inode), &fhandle, sizeof(struct nfs_fh))!= 0)
+			goto out_bad;
+		if (nfs_lookup_verify_inode(inode))
+			goto out_bad;
+		goto out_valid_renew;
+	}
+
 	if (NFS_STALE(inode))
 		goto out_bad;
 
@@ -514,6 +571,7 @@
 	if ((error = nfs_refresh_inode(inode, &fattr)) != 0)
 		goto out_bad;
 
+ out_valid_renew:
 	nfs_renew_times(dentry);
  out_valid:
 	unlock_kernel();
@@ -589,6 +647,18 @@
 	error = -ENOMEM;
 	dentry->d_op = &nfs_dentry_operations;
 
+	error = nfs_cached_lookup(dir, dentry, &fhandle, &fattr);
+	if (!error) {
+		error = -EACCES;
+		inode = nfs_fhget(dentry, &fhandle, &fattr);
+		if (inode) {
+			d_add(dentry, inode);
+			nfs_renew_times(dentry);
+			error = 0;
+		}
+		goto out;
+	}
+
 	error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr);
 	inode = NULL;
 	if (error == -ENOENT)
@@ -607,6 +677,79 @@
 	return ERR_PTR(error);
 }
 
+static inline
+int find_dirent_name(nfs_readdir_descriptor_t *desc, struct page *page, struct dentry *dentry)
+{
+	struct nfs_entry *entry = desc->entry;
+	int		 status;
+
+	while((status = dir_decode(desc)) == 0) {
+		if (entry->len != dentry->d_name.len)
+			continue;
+		if (memcmp(entry->name, dentry->d_name.name, entry->len))
+			continue;
+		if (!(entry->fattr->valid & NFS_ATTR_FATTR))
+			continue;
+		break;
+	}
+	return status;
+}
+
+/*
+ * Use the cached Readdirplus results in order to avoid a LOOKUP call
+ * whenever we believe that the parent directory has not changed.
+ *
+ * We assume that any file creation/rename changes the directory mtime.
+ * As this results in a page cache invalidation whenever it occurs,
+ * we don't require any other tests for cache coherency.
+ */
+static
+int nfs_cached_lookup(struct inode *dir, struct dentry *dentry,
+			struct nfs_fh *fh, struct nfs_fattr *fattr)
+{
+	nfs_readdir_descriptor_t desc;
+	struct nfs_server *server;
+	struct nfs_entry entry;
+	struct page *page;
+	unsigned long timestamp = NFS_MTIME_UPDATE(dir);
+	int res;
+
+	if (!NFS_USE_READDIRPLUS(dir))
+		return -ENOENT;
+	server = NFS_SERVER(dir);
+	if (server->flags & NFS_MOUNT_NOAC)
+		return -ENOENT;
+	nfs_revalidate_inode(server, dir);
+
+	entry.fh = fh;
+	entry.fattr = fattr;
+
+	desc.decode = NFS_PROTO(dir)->decode_dirent;
+	desc.entry = &entry;
+	desc.page_index = 0;
+	desc.plus = 1;
+
+	for(;(page = find_get_page(dir->i_mapping, desc.page_index)); desc.page_index++) {
+
+		res = -EIO;
+		if (Page_Uptodate(page)) {
+			desc.ptr = kmap(page);
+			res = find_dirent_name(&desc, page, dentry);
+			kunmap(page);
+		}
+		page_cache_release(page);
+
+		if (res == 0)
+			goto out_found;
+		if (res != -EAGAIN)
+			break;
+	}
+	return -ENOENT;
+ out_found:
+	fattr->timestamp = timestamp;
+	return 0;
+}
+
 /*
  * Code common to create, mkdir, and mknod.
  */
@@ -1083,34 +1226,62 @@
 int
 nfs_permission(struct inode *inode, int mask)
 {
-	int			error = vfs_permission(inode, mask);
-
-	if (!NFS_PROTO(inode)->access)
-		goto out;
-
-	if (error == -EROFS)
-		goto out;
-
-	/*
-	 * Trust UNIX mode bits except:
-	 *
-	 * 1) When override capabilities may have been invoked
-	 * 2) When root squashing may be involved
-	 * 3) When ACLs may overturn a negative answer */
-	if (!capable(CAP_DAC_OVERRIDE) && !capable(CAP_DAC_READ_SEARCH)
-	    && (current->fsuid != 0) && (current->fsgid != 0)
-	    && error != -EACCES)
-		goto out;
+	struct nfs_access_cache *cache = &NFS_I(inode)->cache_access;
+	struct rpc_cred *cred;
+	int mode = inode->i_mode;
+	int error;
 
-	error = NFS_PROTO(inode)->access(inode, mask, 0);
+	if (mask & MAY_WRITE) {
+		/*
+		 *
+		 * Nobody gets write access to a read-only fs.
+		 *
+		 */
+		if (IS_RDONLY(inode) &&
+		    (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
+			return -EROFS;
 
-	if (error == -EACCES && NFS_CLIENT(inode)->cl_droppriv &&
-	    current->uid != 0 && current->gid != 0 &&
-	    (current->fsuid != current->uid || current->fsgid != current->gid))
-		error = NFS_PROTO(inode)->access(inode, mask, 1);
+		/*
+		 *
+		 * Nobody gets write access to an immutable file.
+		 *
+		 */
+		if (IS_IMMUTABLE(inode))
+			return -EACCES;
+	}
 
- out:
-	return error;
+	if (!NFS_PROTO(inode)->access)
+		goto out_notsup;
+	cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0);
+	if (cache->cred == cred
+	    && time_before(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode))) {
+		if (!cache->err) {
+			/* Is the mask a subset of an accepted mask? */
+			if ((cache->mask & mask) == mask)
+				goto out_cached;
+	       	} else {
+			/* ...or is it a superset of a rejected mask? */
+			if ((cache->mask & mask) == cache->mask)
+				goto out_cached;
+		}
+	}
+	error = NFS_PROTO(inode)->access(inode, cred, mask);
+	if (!error || error == -EACCES) {
+		cache->jiffies = jiffies;
+		if (cache->cred)
+			put_rpccred(cache->cred);
+		cache->cred = cred;
+		cache->mask = mask;
+		cache->err = error;
+		return error;
+	}
+	put_rpccred(cred);
+out_notsup:
+	nfs_revalidate_inode(NFS_SERVER(inode), inode);
+	return vfs_permission(inode, mask);
+out_cached:
+	put_rpccred(cred);
+	return cache->err;
 }
 
 /*
diff -Nru linux-old/fs/nfs/inode.c linux-new/fs/nfs/inode.c
--- linux-old/fs/nfs/inode.c	2002-10-23 21:59:38.000000000 -0400
+++ linux-new/fs/nfs/inode.c	2002-10-23 22:10:25.000000000 -0400
@@ -146,10 +146,14 @@
 static void
 nfs_clear_inode(struct inode *inode)
 {
-	struct rpc_cred *cred = NFS_I(inode)->mm_cred;
+	struct nfs_inode_info *nfsi = NFS_I(inode);
+	struct rpc_cred *cred = nfsi->mm_cred;
 
 	if (cred)
 		put_rpccred(cred);
+	cred = nfsi->cache_access.cred;
+	if (cred)
+		put_rpccred(cred);
 }
 
 void
@@ -390,12 +394,14 @@
 	INIT_LIST_HEAD(&server->lru_busy);
 
  nfsv3_try_again:
+	server->caps = 0;
 	/* Check NFS protocol revision and initialize RPC op vector
 	 * and file handle pool. */
 	if (data->flags & NFS_MOUNT_VER3) {
 #ifdef CONFIG_NFS_V3
 		server->rpc_ops = &nfs_v3_clientops;
 		version = 3;
+		server->caps |= NFS_CAP_READDIRPLUS;
 		if (data->version < 4) {
 			printk(KERN_NOTICE "NFS: NFSv3 not supported by mount program.\n");
 			goto out_unlock;
@@ -633,36 +639,35 @@
 	nfs_zap_caches(inode);
 }
 
+/* Don't use READDIRPLUS on directories that we believe are too large */
+#define NFS_LIMIT_READDIRPLUS (8*PAGE_SIZE)
+
 /*
  * Fill in inode information from the fattr.
  */
 static void
 nfs_fill_inode(struct inode *inode, struct nfs_fh *fh, struct nfs_fattr *fattr)
 {
-	/*
-	 * Check whether the mode has been set, as we only want to
-	 * do this once. (We don't allow inodes to change types.)
+	NFS_FILEID(inode) = fattr->fileid;
+	inode->i_mode = fattr->mode;
+	/* Why so? Because we want revalidate for devices/FIFOs, and
+	 * that's precisely what we have in nfs_file_inode_operations.
 	 */
-	if (inode->i_mode == 0) {
-		NFS_FILEID(inode) = fattr->fileid;
-		inode->i_mode = fattr->mode;
-		/* Why so? Because we want revalidate for devices/FIFOs, and
-		 * that's precisely what we have in nfs_file_inode_operations.
-		 */
-		inode->i_op = &nfs_file_inode_operations;
-		if (S_ISREG(inode->i_mode)) {
-			inode->i_fop = &nfs_file_operations;
-			inode->i_data.a_ops = &nfs_file_aops;
-		} else if (S_ISDIR(inode->i_mode)) {
-			inode->i_op = &nfs_dir_inode_operations;
-			inode->i_fop = &nfs_dir_operations;
-		} else if (S_ISLNK(inode->i_mode))
-			inode->i_op = &nfs_symlink_inode_operations;
-		else
-			init_special_inode(inode, inode->i_mode, fattr->rdev);
-		memcpy(&inode->u.nfs_i.fh, fh, sizeof(inode->u.nfs_i.fh));
-	}
-	nfs_refresh_inode(inode, fattr);
+	inode->i_op = &nfs_file_inode_operations;
+	if (S_ISREG(inode->i_mode)) {
+		inode->i_fop = &nfs_file_operations;
+		inode->i_data.a_ops = &nfs_file_aops;
+	} else if (S_ISDIR(inode->i_mode)) {
+		inode->i_op = &nfs_dir_inode_operations;
+		inode->i_fop = &nfs_dir_operations;
+		if (nfs_server_capable(inode, NFS_CAP_READDIRPLUS)
+		    && fattr->size <= NFS_LIMIT_READDIRPLUS)
+			NFS_FLAGS(inode) |= NFS_INO_ADVISE_RDPLUS;
+	} else if (S_ISLNK(inode->i_mode))
+		inode->i_op = &nfs_symlink_inode_operations;
+	else
+		init_special_inode(inode, inode->i_mode, fattr->rdev);
+	memcpy(&inode->u.nfs_i.fh, fh, sizeof(inode->u.nfs_i.fh));
 }
 
 struct nfs_find_desc {
@@ -737,7 +742,14 @@
 	if (!(inode = iget4(sb, ino, nfs_find_actor, &desc)))
 		goto out_no_inode;
 
-	nfs_fill_inode(inode, fh, fattr);
+	/*
+	 * Check whether the mode has been set, as we only want to
+	 * do this once. (We don't allow inodes to change types.)
+	 */
+	if (inode->i_mode == 0)
+		nfs_fill_inode(inode, fh, fattr);
+
+	nfs_refresh_inode(inode, fattr);
 	dprintk("NFS: __nfs_fhget(%x/%Ld ct=%d)\n",
 		inode->i_dev, (long long)NFS_FILEID(inode),
 		atomic_read(&inode->i_count));
@@ -860,15 +872,23 @@
 {
 	struct rpc_auth *auth;
 	struct rpc_cred *cred;
+	int err = 0;
 
 	lock_kernel();
+	/* Ensure that we revalidate the data cache */
+	if (NFS_SERVER(inode)->flags & NFS_MOUNT_NOCTO) {
+		err = __nfs_revalidate_inode(NFS_SERVER(inode),inode);
+		if (err)
+			goto out;
+	}
 	auth = NFS_CLIENT(inode)->cl_auth;
 	cred = rpcauth_lookupcred(auth, 0);
 	filp->private_data = cred;
 	if (filp->f_mode & FMODE_WRITE)
 		nfs_set_mmcred(inode, cred);
+out:
 	unlock_kernel();
-	return 0;
+	return err;
 }
 
 int nfs_release(struct inode *inode, struct file *filp)
@@ -1003,6 +1023,9 @@
 		goto out_err;
 	}
 
+	/* Throw out obsolete READDIRPLUS attributes */
+	if (time_before(fattr->timestamp, NFS_READTIME(inode)))
+		return 0;
 	/*
 	 * Make sure the inode's type hasn't changed.
 	 */
@@ -1021,7 +1044,7 @@
 	/*
 	 * Update the read time so we don't revalidate too often.
 	 */
-	NFS_READTIME(inode) = jiffies;
+	NFS_READTIME(inode) = fattr->timestamp;
 
 	/*
 	 * Note: NFS_CACHE_ISIZE(inode) reflects the state of the cache.
@@ -1071,7 +1094,7 @@
 
 	if (NFS_CACHE_MTIME(inode) != new_mtime) {
 		if (invalid)
-			NFS_MTIME_UPDATE(inode) = jiffies;
+			NFS_MTIME_UPDATE(inode) = fattr->timestamp;
 		NFS_CACHE_MTIME(inode) = new_mtime;
 		inode->i_mtime = nfs_time_to_secs(new_mtime);
 	}
@@ -1079,6 +1102,16 @@
 	NFS_CACHE_ISIZE(inode) = new_size;
 	inode->i_size = new_isize;
 
+	if (inode->i_mode != fattr->mode ||
+	    inode->i_uid != fattr->uid ||
+	    inode->i_gid != fattr->gid) {
+		struct rpc_cred **cred = &NFS_I(inode)->cache_access.cred;
+		if (*cred) {
+			put_rpccred(*cred);
+			*cred = NULL;
+		}
+	}
+
 	inode->i_mode = fattr->mode;
 	inode->i_nlink = fattr->nlink;
 	inode->i_uid = fattr->uid;
diff -Nru linux-old/fs/nfs/nfs2xdr.c linux-new/fs/nfs/nfs2xdr.c
--- linux-old/fs/nfs/nfs2xdr.c	2002-10-23 21:59:38.000000000 -0400
+++ linux-new/fs/nfs/nfs2xdr.c	2002-10-23 22:10:25.000000000 -0400
@@ -118,6 +118,7 @@
 		fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO;
 		fattr->rdev = 0;
 	}
+	fattr->timestamp = jiffies;
 	return p;
 }
 
@@ -360,7 +361,7 @@
 		count = count >> 2;
 
 	p = xdr_encode_fhandle(p, args->fh);
-	*p++ = htonl(args->cookie);
+	*p++ = htonl(args->cookie & 0xFFFFFFFF);
 	*p++ = htonl(count); /* see above */
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 
@@ -443,7 +444,7 @@
 	entry->name	  = (const char *) p;
 	p		 += XDR_QUADLEN(entry->len);
 	entry->prev_cookie	  = entry->cookie;
-	entry->cookie	  = ntohl(*p++);
+	entry->cookie	  = (s64)((off_t)ntohl(*p++));
 	entry->eof	  = !p[0] && p[1];
 
 	return p;
diff -Nru linux-old/fs/nfs/nfs3proc.c linux-new/fs/nfs/nfs3proc.c
--- linux-old/fs/nfs/nfs3proc.c	2002-10-23 21:59:38.000000000 -0400
+++ linux-new/fs/nfs/nfs3proc.c	2002-10-23 22:10:20.000000000 -0400
@@ -117,12 +117,13 @@
 }
 
 static int
-nfs3_proc_access(struct inode *inode, int mode, int ruid)
+nfs3_proc_access(struct inode *inode, struct rpc_cred *cred, int mode)
 {
 	struct nfs_fattr	fattr;
 	struct nfs3_accessargs	arg = { NFS_FH(inode), 0 };
 	struct nfs3_accessres	res = { &fattr, 0 };
-	int	status, flags;
+	struct rpc_message msg = { NFS3PROC_ACCESS, &arg, &res, cred };
+	int	status;
 
 	dprintk("NFS call  access\n");
 	fattr.valid = 0;
@@ -140,8 +141,7 @@
 		if (mode & MAY_EXEC)
 			arg.access |= NFS3_ACCESS_EXECUTE;
 	}
-	flags = (ruid) ? RPC_CALL_REALUID : 0;
-	status = rpc_call(NFS_CLIENT(inode), NFS3PROC_ACCESS, &arg, &res, flags);
+	status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
 	nfs_refresh_inode(inode, &fattr);
 	dprintk("NFS reply access\n");
 
diff -Nru linux-old/fs/nfs/nfs3xdr.c linux-new/fs/nfs/nfs3xdr.c
--- linux-old/fs/nfs/nfs3xdr.c	2002-10-23 21:59:38.000000000 -0400
+++ linux-new/fs/nfs/nfs3xdr.c	2002-10-23 22:10:25.000000000 -0400
@@ -181,6 +181,7 @@
 
 	/* Update the mode bits */
 	fattr->valid |= (NFS_ATTR_FATTR | NFS_ATTR_FATTR_V3);
+	fattr->timestamp = jiffies;
 	return p;
 }
 
@@ -465,6 +466,13 @@
 	return 0;
 }
 
+/* Hack to sign-extending 32-bit cookies */
+static inline
+u64 nfs_transform_cookie64(u64 cookie)
+{
+	return (cookie & 0x80000000) ? (cookie ^ 0xFFFFFFFF00000000) : cookie;
+}
+
 /*
  * Encode arguments to readdir call
  */
@@ -476,7 +484,7 @@
 	u32 count = args->count;
 
 	p = xdr_encode_fhandle(p, args->fh);
-	p = xdr_encode_hyper(p, args->cookie);
+	p = xdr_encode_hyper(p, nfs_transform_cookie64(args->cookie));
 	*p++ = args->verf[0];
 	*p++ = args->verf[1];
 	if (args->plus) {
@@ -587,6 +595,7 @@
 nfs3_decode_dirent(u32 *p, struct nfs_entry *entry, int plus)
 {
 	struct nfs_entry old = *entry;
+	u64 cookie;
 
 	if (!*p++) {
 		if (!*p)
@@ -600,24 +609,23 @@
 	entry->name = (const char *) p;
 	p += XDR_QUADLEN(entry->len);
 	entry->prev_cookie = entry->cookie;
-	p = xdr_decode_hyper(p, &entry->cookie);
+	p = xdr_decode_hyper(p, &cookie);
+	entry->cookie = nfs_transform_cookie64(cookie);
 
 	if (plus) {
-		p = xdr_decode_post_op_attr(p, &entry->fattr);
+		entry->fattr->valid = 0;
+		p = xdr_decode_post_op_attr(p, entry->fattr);
 		/* In fact, a post_op_fh3: */
 		if (*p++) {
-			p = xdr_decode_fhandle(p, &entry->fh);
+			p = xdr_decode_fhandle(p, entry->fh);
 			/* Ugh -- server reply was truncated */
 			if (p == NULL) {
 				dprintk("NFS: FH truncated\n");
 				*entry = old;
 				return ERR_PTR(-EAGAIN);
 			}
-		} else {
-			/* If we don't get a file handle, the attrs
-			 * aren't worth a lot. */
-			entry->fattr.valid = 0;
-		}
+		} else
+			memset((u8*)(entry->fh), 0, sizeof(*entry->fh));
 	}
 
 	entry->eof = !p[0] && p[1];
diff -Nru linux-old/fs/nfs/write.c linux-new/fs/nfs/write.c
--- linux-old/fs/nfs/write.c	2002-10-23 20:58:12.000000000 -0400
+++ linux-new/fs/nfs/write.c	2002-10-23 22:10:12.000000000 -0400
@@ -795,8 +795,15 @@
 	 * If wsize is smaller than page size, update and write
 	 * page synchronously.
 	 */
-	if (NFS_SERVER(inode)->wsize < PAGE_CACHE_SIZE || IS_SYNC(inode))
-		return nfs_writepage_sync(file, inode, page, offset, count);
+	if (NFS_SERVER(inode)->wsize < PAGE_CACHE_SIZE || IS_SYNC(inode)) {
+		status = nfs_writepage_sync(file, inode, page, offset, count);
+		if (status > 0) {
+			if (offset == 0 && status == PAGE_CACHE_SIZE)
+				SetPageUptodate(page);
+			return 0;
+		}
+		return status;
+	}
 
 	/*
 	 * Try to find an NFS request corresponding to this page
diff -Nru linux-old/include/linux/nfs_fs.h linux-new/include/linux/nfs_fs.h
--- linux-old/include/linux/nfs_fs.h	2002-10-23 20:58:12.000000000 -0400
+++ linux-new/include/linux/nfs_fs.h	2002-10-23 22:10:25.000000000 -0400
@@ -102,8 +102,15 @@
 
 #define NFS_FILEID(inode)		((inode)->u.nfs_i.fileid)
 
-/* Inode Flags */
-#define NFS_USE_READDIRPLUS(inode)	((NFS_FLAGS(inode) & NFS_INO_ADVISE_RDPLUS) ? 1 : 0)
+static inline int nfs_server_capable(struct inode *inode, int cap)
+{
+	return NFS_SERVER(inode)->caps & cap;
+}
+
+static inline int NFS_USE_READDIRPLUS(struct inode *inode)
+{
+	return NFS_FLAGS(inode) & NFS_INO_ADVISE_RDPLUS;
+}
 
 /*
  * These are the default flags for swap requests
diff -Nru linux-old/include/linux/nfs_fs_i.h linux-new/include/linux/nfs_fs_i.h
--- linux-old/include/linux/nfs_fs_i.h	2002-10-23 20:54:40.000000000 -0400
+++ linux-new/include/linux/nfs_fs_i.h	2002-10-23 22:10:20.000000000 -0400
@@ -6,6 +6,16 @@
 #include <linux/nfs.h>
 
 /*
+ * NFSv3 Access mode cache
+ */
+struct nfs_access_cache {
+	unsigned long		jiffies;
+	struct rpc_cred *	cred;
+	int			mask;
+	int			err;
+};
+
+/*
  * nfs fs inode data in memory
  */
 struct nfs_inode_info {
@@ -54,6 +64,8 @@
 	 */
 	unsigned long		cache_mtime_jiffies;
 
+	struct nfs_access_cache	cache_access;
+
 	/*
 	 * This is the cookie verifier used for NFSv3 readdir
 	 * operations
diff -Nru linux-old/include/linux/nfs_fs_sb.h linux-new/include/linux/nfs_fs_sb.h
--- linux-old/include/linux/nfs_fs_sb.h	2002-10-23 20:54:40.000000000 -0400
+++ linux-new/include/linux/nfs_fs_sb.h	2002-10-23 22:10:25.000000000 -0400
@@ -10,6 +10,7 @@
 	struct rpc_clnt *	client;		/* RPC client handle */
 	struct nfs_rpc_ops *	rpc_ops;	/* NFS protocol vector */
 	int			flags;		/* various flags */
+	unsigned int		caps;		/* server capabilities */
 	unsigned int		rsize;		/* read size */
 	unsigned int		rpages;		/* read size (in pages) */
 	unsigned int		wsize;		/* write size */
@@ -36,4 +37,8 @@
 	struct nfs_server	s_server;
 };
 
+/* Server capabilities */
+#define NFS_CAP_READDIRPLUS	1
+
+
 #endif
diff -Nru linux-old/include/linux/nfs_xdr.h linux-new/include/linux/nfs_xdr.h
--- linux-old/include/linux/nfs_xdr.h	2002-10-23 21:59:38.000000000 -0400
+++ linux-new/include/linux/nfs_xdr.h	2002-10-23 22:10:25.000000000 -0400
@@ -27,6 +27,7 @@
 	__u64			atime;
 	__u64			mtime;
 	__u64			ctime;
+	unsigned long		timestamp;
 };
 
 #define NFS_ATTR_WCC		0x0001		/* pre-op WCC data    */
@@ -132,8 +133,8 @@
 	const char *		name;
 	unsigned int		len;
 	int			eof;
-	struct nfs_fh		fh;
-	struct nfs_fattr	fattr;
+	struct nfs_fh		*fh;
+	struct nfs_fattr	*fattr;
 };
 
 /*
@@ -323,7 +324,7 @@
 			    struct iattr *);
 	int	(*lookup)  (struct inode *, struct qstr *,
 			    struct nfs_fh *, struct nfs_fattr *);
-	int	(*access)  (struct inode *, int , int);
+	int	(*access)  (struct inode *, struct rpc_cred *, int);
 	int	(*readlink)(struct inode *, struct page *);
 	int	(*read)    (struct inode *, struct rpc_cred *,
 			    struct nfs_fattr *,
diff -Nru linux-old/include/linux/sunrpc/xprt.h linux-new/include/linux/sunrpc/xprt.h
--- linux-old/include/linux/sunrpc/xprt.h	2002-10-23 20:57:56.000000000 -0400
+++ linux-new/include/linux/sunrpc/xprt.h	2002-10-23 22:10:09.000000000 -0400
@@ -57,8 +57,7 @@
 	unsigned long		to_current,		/* current timeout */
 				to_initval,		/* initial timeout */
 				to_maxval,		/* max timeout */
-				to_increment,		/* if !exponential */
-				to_resrvval;		/* reserve timeout */
+				to_increment;		/* if !exponential */
 	short			to_retries;		/* max # of retries */
 	unsigned char		to_exponential;
 };
@@ -173,7 +172,7 @@
 void			xprt_set_timeout(struct rpc_timeout *, unsigned int,
 					unsigned long);
 
-int			xprt_reserve(struct rpc_task *);
+void			xprt_reserve(struct rpc_task *);
 void			xprt_transmit(struct rpc_task *);
 void			xprt_receive(struct rpc_task *);
 int			xprt_adjust_timeout(struct rpc_timeout *);
diff -Nru linux-old/net/sunrpc/clnt.c linux-new/net/sunrpc/clnt.c
--- linux-old/net/sunrpc/clnt.c	2002-10-23 20:57:56.000000000 -0400
+++ linux-new/net/sunrpc/clnt.c	2002-10-23 22:10:09.000000000 -0400
@@ -43,6 +43,7 @@
 static DECLARE_WAIT_QUEUE_HEAD(destroy_wait);
 
 
+static void	call_start(struct rpc_task *task);
 static void	call_reserve(struct rpc_task *task);
 static void	call_reserveresult(struct rpc_task *task);
 static void	call_allocate(struct rpc_task *task);
@@ -326,13 +327,9 @@
 		rpcauth_bindcred(task);
 
 	if (task->tk_status == 0)
-		task->tk_action = call_reserve;
+		task->tk_action = call_start;
 	else
 		task->tk_action = NULL;
-
-	/* Increment call count */
-	if (task->tk_msg.rpc_proc < task->tk_client->cl_maxproc)
-		rpcproc_count(task->tk_client, task->tk_msg.rpc_proc)++;
 }
 
 void
@@ -359,26 +356,46 @@
 	if (RPC_ASSASSINATED(task))
 		return;
 
-	task->tk_action = call_reserve;
-	rpcproc_count(task->tk_client, task->tk_msg.rpc_proc)++;
+	task->tk_action = call_start;
 }
 
 /*
- * 1.	Reserve an RPC call slot
+ * 0.  Initial state
+ *
+ *     Other FSM states can be visited zero or more times, but
+ *     this state is visited exactly once for each RPC.
  */
 static void
-call_reserve(struct rpc_task *task)
+call_start(struct rpc_task *task)
 {
 	struct rpc_clnt	*clnt = task->tk_client;
 
 	if (task->tk_msg.rpc_proc > clnt->cl_maxproc) {
-		printk(KERN_WARNING "%s (vers %d): bad procedure number %d\n",
-			clnt->cl_protname, clnt->cl_vers, task->tk_msg.rpc_proc);
+		printk(KERN_ERR "%s (vers %d): bad procedure number %d\n",
+				clnt->cl_protname, clnt->cl_vers,
+				task->tk_msg.rpc_proc);
 		rpc_exit(task, -EIO);
 		return;
 	}
 
+	dprintk("RPC: %4d call_start %s%d proc %d (%s)\n", task->tk_pid,
+		clnt->cl_protname, clnt->cl_vers, task->tk_msg.rpc_proc,
+		(RPC_IS_ASYNC(task) ? "async" : "sync"));
+
+	/* Increment call count */
+	rpcproc_count(clnt, task->tk_msg.rpc_proc)++;
+	clnt->cl_stats->rpccnt++;
+	task->tk_action = call_reserve;
+}
+
+/*
+ * 1.	Reserve an RPC call slot
+ */
+static void
+call_reserve(struct rpc_task *task)
+{
 	dprintk("RPC: %4d call_reserve\n", task->tk_pid);
+
 	if (!rpcauth_uptodatecred(task)) {
 		task->tk_action = call_refresh;
 		return;
@@ -386,8 +403,6 @@
 
 	task->tk_status  = 0;
 	task->tk_action  = call_reserveresult;
-	task->tk_timeout = clnt->cl_timeout.to_resrvval;
-	clnt->cl_stats->rpccnt++;
 	xprt_reserve(task);
 }
 
@@ -401,38 +416,46 @@
 
 	dprintk("RPC: %4d call_reserveresult (status %d)\n",
 				task->tk_pid, task->tk_status);
+
 	/*
 	 * After a call to xprt_reserve(), we must have either
 	 * a request slot or else an error status.
 	 */
-	if ((task->tk_status >= 0 && !task->tk_rqstp) ||
-	    (task->tk_status < 0 && task->tk_rqstp))
-		printk(KERN_ERR "call_reserveresult: status=%d, request=%p??\n",
-		 task->tk_status, task->tk_rqstp);
+	task->tk_status = 0;
+	if (status >= 0) {
+		if (task->tk_rqstp) {
+			task->tk_action = call_allocate;
+			return;
+		}
 
-	if (task->tk_status >= 0) {
-		task->tk_action = call_allocate;
+		printk(KERN_ERR "%s: status=%d, but no request slot, exiting\n",
+				__FUNCTION__, status);
+		rpc_exit(task, -EIO);
 		return;
 	}
 
-	task->tk_status = 0;
+	/*
+	 * Even though there was an error, we may have acquired
+	 * a request slot somehow.  Make sure not to leak it.
+	 */
+	if (task->tk_rqstp) {
+		printk(KERN_ERR "%s: status=%d, request allocated anyway\n",
+				__FUNCTION__, status);
+		xprt_release(task);
+	}
+
 	switch (status) {
-	case -EAGAIN:
-	case -ENOBUFS:
-		task->tk_timeout = task->tk_client->cl_timeout.to_resrvval;
+	case -EAGAIN:	/* woken up; retry */
 		task->tk_action = call_reserve;
-		break;
-	case -ETIMEDOUT:
-		dprintk("RPC: task timed out\n");
-		task->tk_action = call_timeout;
+		return;
+	case -EIO:	/* probably a shutdown */
 		break;
 	default:
-		if (!task->tk_rqstp) {
-			printk(KERN_INFO "RPC: task has no request, exit EIO\n");
-			rpc_exit(task, -EIO);
-		} else
-			rpc_exit(task, status);
+		printk(KERN_ERR "%s: unrecognized error %d, exiting\n",
+				__FUNCTION__, status);
+		break;
 	}
+	rpc_exit(task, status);
 }
 
 /*
@@ -527,6 +550,9 @@
 	struct rpc_clnt	*clnt = task->tk_client;
 	struct rpc_xprt *xprt = clnt->cl_xprt;
 
+	dprintk("RPC: %4d call_bind xprt %p %s connected\n", task->tk_pid,
+			xprt, (xprt_connected(xprt) ? "is" : "is not"));
+
 	task->tk_action = (xprt_connected(xprt)) ? call_transmit : call_reconnect;
 
 	if (!clnt->cl_port) {
@@ -645,7 +671,6 @@
 	case -ENOMEM:
 	case -EAGAIN:
 		task->tk_action = call_transmit;
-		clnt->cl_stats->rpcretrans++;
 		break;
 	default:
 		if (clnt->cl_chatty)
@@ -664,20 +689,15 @@
 call_timeout(struct rpc_task *task)
 {
 	struct rpc_clnt	*clnt = task->tk_client;
-	struct rpc_rqst	*req = task->tk_rqstp;
-
-	if (req) {
-		struct rpc_timeout *to = &req->rq_timeout;
+	struct rpc_timeout *to = &task->tk_rqstp->rq_timeout;
 
-		if (xprt_adjust_timeout(to)) {
-			dprintk("RPC: %4d call_timeout (minor timeo)\n",
-				task->tk_pid);
-			goto minor_timeout;
-		}
-		to->to_retries = clnt->cl_timeout.to_retries;
+	if (xprt_adjust_timeout(to)) {
+		dprintk("RPC: %4d call_timeout (minor)\n", task->tk_pid);
+		goto retry;
 	}
+	to->to_retries = clnt->cl_timeout.to_retries;
 
-	dprintk("RPC: %4d call_timeout (major timeo)\n", task->tk_pid);
+	dprintk("RPC: %4d call_timeout (major)\n", task->tk_pid);
 	if (clnt->cl_softrtry) {
 		if (clnt->cl_chatty && !task->tk_exit)
 			printk(KERN_NOTICE "%s: server %s not responding, timed out\n",
@@ -685,33 +705,18 @@
 		rpc_exit(task, -EIO);
 		return;
 	}
+
 	if (clnt->cl_chatty && !(task->tk_flags & RPC_CALL_MAJORSEEN) && rpc_ntimeo(&clnt->cl_rtt) > 7) {
 		task->tk_flags |= RPC_CALL_MAJORSEEN;
-		if (req)
-			printk(KERN_NOTICE "%s: server %s not responding, still trying\n",
-				clnt->cl_protname, clnt->cl_server);
-#ifdef RPC_DEBUG				
-		else
-			printk(KERN_NOTICE "%s: task %d can't get a request slot\n",
-				clnt->cl_protname, task->tk_pid);
-#endif				
+		printk(KERN_NOTICE "%s: server %s not responding, still trying\n",
+			clnt->cl_protname, clnt->cl_server);
 	}
 	if (clnt->cl_autobind)
 		clnt->cl_port = 0;
 
-minor_timeout:
-	if (!req)
-		task->tk_action = call_reserve;
-	else if (!clnt->cl_port) {
-		task->tk_action = call_bind;
-		clnt->cl_stats->rpcretrans++;
-	} else if (!xprt_connected(clnt->cl_xprt)) {
-		task->tk_action = call_reconnect;
-		clnt->cl_stats->rpcretrans++;
-	} else {
-		task->tk_action = call_transmit;
-		clnt->cl_stats->rpcretrans++;
-	}
+retry:
+	clnt->cl_stats->rpcretrans++;
+	task->tk_action = call_bind;
 	task->tk_status = 0;
 }
 
diff -Nru linux-old/net/sunrpc/xprt.c linux-new/net/sunrpc/xprt.c
--- linux-old/net/sunrpc/xprt.c	2002-10-23 20:58:12.000000000 -0400
+++ linux-new/net/sunrpc/xprt.c	2002-10-23 22:10:09.000000000 -0400
@@ -83,7 +83,7 @@
  */
 static void	xprt_request_init(struct rpc_task *, struct rpc_xprt *);
 static void	do_xprt_transmit(struct rpc_task *);
-static void	xprt_reserve_status(struct rpc_task *task);
+static inline void	do_xprt_reserve(struct rpc_task *);
 static void	xprt_disconnect(struct rpc_xprt *);
 static void	xprt_reconn_status(struct rpc_task *task);
 static struct socket *xprt_create_socket(int, struct rpc_timeout *);
@@ -1181,61 +1181,39 @@
 /*
  * Reserve an RPC call slot.
  */
-int
+void
 xprt_reserve(struct rpc_task *task)
 {
 	struct rpc_xprt	*xprt = task->tk_xprt;
 
-	/* We already have an initialized request. */
-	if (task->tk_rqstp)
-		return 0;
-
-	spin_lock(&xprt->xprt_lock);
-	xprt_reserve_status(task);
-	if (task->tk_rqstp) {
-		task->tk_timeout = 0;
-	} else if (!task->tk_timeout) {
-		task->tk_status = -ENOBUFS;
-	} else {
-		dprintk("RPC:      xprt_reserve waiting on backlog\n");
-		task->tk_status = -EAGAIN;
-		rpc_sleep_on(&xprt->backlog, task, NULL, NULL);
+	task->tk_status = -EIO;
+	if (!xprt->shutdown) {
+		spin_lock(&xprt->xprt_lock);
+		do_xprt_reserve(task);
+		spin_unlock(&xprt->xprt_lock);
 	}
-	spin_unlock(&xprt->xprt_lock);
-	dprintk("RPC: %4d xprt_reserve returns %d\n",
-				task->tk_pid, task->tk_status);
-	return task->tk_status;
 }
 
-/*
- * Reservation callback
- */
-static void
-xprt_reserve_status(struct rpc_task *task)
+static inline void
+do_xprt_reserve(struct rpc_task *task)
 {
 	struct rpc_xprt	*xprt = task->tk_xprt;
-	struct rpc_rqst	*req;
 
-	if (xprt->shutdown) {
-		task->tk_status = -EIO;
-	} else if (task->tk_status < 0) {
-		/* NOP */
-	} else if (task->tk_rqstp) {
-		/* We've already been given a request slot: NOP */
-	} else {
-		if (!(req = xprt->free))
-			goto out_nofree;
-		/* OK: There's room for us. Grab a free slot */
-		xprt->free     = req->rq_next;
-		req->rq_next   = NULL;
+	task->tk_status = 0;
+	if (task->tk_rqstp)
+		return;
+	if (xprt->free) {
+		struct rpc_rqst	*req = xprt->free;
+		xprt->free = req->rq_next;
+		req->rq_next = NULL;
 		task->tk_rqstp = req;
 		xprt_request_init(task, xprt);
+		return;
 	}
-
-	return;
-
-out_nofree:
+	dprintk("RPC:      waiting for request slot\n");
 	task->tk_status = -EAGAIN;
+	task->tk_timeout = 0;
+	rpc_sleep_on(&xprt->backlog, task, NULL, NULL);
 }
 
 /*
@@ -1251,7 +1229,6 @@
 		xid = CURRENT_TIME << 12;
 
 	dprintk("RPC: %4d reserved req %p xid %08x\n", task->tk_pid, req, xid);
-	task->tk_status = 0;
 	req->rq_timeout = xprt->timeout;
 	req->rq_task	= task;
 	req->rq_xprt    = xprt;
@@ -1313,7 +1290,6 @@
 	to->to_initval   = 
 	to->to_increment = incr;
 	to->to_maxval    = incr * retr;
-	to->to_resrvval  = incr * retr;
 	to->to_retries   = retr;
 	to->to_exponential = 0;
 }
@@ -1354,7 +1330,6 @@
 	if (to) {
 		xprt->timeout = *to;
 		xprt->timeout.to_current = to->to_initval;
-		xprt->timeout.to_resrvval = to->to_maxval << 1;
 	} else
 		xprt_default_timeout(&xprt->timeout, xprt->prot);
 
