[LWN Logo]
[Timeline]
Date: Mon, 18 Sep 2000 16:43:00 -0700
From: Dave Higgen <dhiggen@valinux.com>
To: nfs@lists.sourceforge.net
Subject: [NFS] Server-side NFS3 patch for 2.2.18-pre9: take 2.

This is a multi-part message in MIME format.
--------------500E2001E72FE3DE433B11A0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

> Alan,
> 
> I'm attaching the server-side fixes+NFS3 patch resynchronized to
> 2.2.18-pre9.
> 
> Hope we can get this in for 2.2.18 to complete the set, now that the
> client-side stuff is in.   Thanks,
> 
> 
>                 Dave Higgen

Ackk.... I just realized there was some .orig file cruft left in there.

Terribly sorry about that.  Here's the cleaned-up version.



                Dave Higgen
--------------500E2001E72FE3DE433B11A0
Content-Type: text/plain; charset=us-ascii;
 name="dhiggen-over-2.2.18-pre9"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="dhiggen-over-2.2.18-pre9"

diff -Naur pre9/linux/Documentation/Changes test/linux/Documentation/Changes
--- pre9/linux/Documentation/Changes	Mon Sep  4 10:39:15 2000
+++ test/linux/Documentation/Changes	Mon Sep 18 14:35:50 2000
@@ -651,9 +651,9 @@
 ftp://ftp.mathematik.th-darmstadt.de/pub/linux/okir/dontuse/nfs-server-2.2beta40.tar.gz
 ftp://linux.nrao.edu/mirrors/fb0429.mathematik.th-darmstadt.de/pub/linux/okir/dontuse/nfs-server-2.2beta40.tar.gz
 
-The kernel-level 1.4.7 release:
-ftp://ftp.varesearch.com/pub/support/hjl/knfsd/knfsd-1.4.7.tar.gz
-ftp://ftp.kernel.org/pub/linux/devel/gcc/knfsd-1.4.7.tar.gz
+
+The kernel-level nfs-utils-0.1.6 release:
+ftp://nfs.sourceforge.net/pub/nfs/nfs-utils-0.1.6.tar.gz
 
 Net-tools
 =========
diff -Naur pre9/linux/Documentation/Configure.help test/linux/Documentation/Configure.help
--- pre9/linux/Documentation/Configure.help	Mon Sep 18 13:58:31 2000
+++ test/linux/Documentation/Configure.help	Mon Sep 18 14:35:50 2000
@@ -7614,31 +7614,33 @@
 
 NFS server support
 CONFIG_NFSD
-  If you want your Linux box to act as a NFS *server*, so that other
+  If you want your Linux box to act as an NFS *server*, so that other
   computers on your local network which support NFS can access certain
   directories on your box transparently, you have two options: you can
   use the self-contained user space program nfsd, in which case you
-  should say N here, or you can say Y and use this new experimental
-  kernel based NFS server. The advantage of the kernel based solution
-  is that it is faster; it might not be completely stable yet, though.
+  should say N here, or you can say Y and use the kernel based NFS
+  server. The kernel based solution is faster and is now the recommended
+  solution: no further development is occurring on the userspace server and
+  support of it may be discontinued in future.
 
   In either case, you will need support software; the respective
   locations are given in the file Documentation/Changes in the NFS
   section.
 
-  Please read the NFS-HOWTO, available via FTP (user: anonymous) from
-  ftp://metalab.unc.edu/pub/Linux/docs/HOWTO.
+  Please read the NFS-HOWTO, available from
+  http://www.linuxdoc.org/HOWTO/NFS-HOWTO.html .
+
 
   The NFS server is also available as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want).
   The module is called nfsd.o. If you want to compile it as a module,
   say M here and read Documentation/modules.txt. If unsure, say N.
 
-Emulate Sun NFS daemon
-CONFIG_NFSD_SUN
-  If you would like for the server to allow clients to access
-  directories that are mount points on the local filesystem (this is
-  how nfsd behaves on Sun systems), say yes here. If unsure, say N.
+Provide NFSv3 server support (EXPERIMENTAL)
+CONFIG_NFSD_V3
+  If you would like to include the NFSv3 server as well as the NFSv2
+  server, say Y here.  File locking, via the NLMv4 protocol, is now
+  supported. If unsure, say N.
 
 OS/2 HPFS filesystem support (read only)
 CONFIG_HPFS_FS
diff -Naur pre9/linux/fs/Config.in test/linux/fs/Config.in
--- pre9/linux/fs/Config.in	Mon Sep 18 13:58:34 2000
+++ test/linux/fs/Config.in	Mon Sep 18 14:35:50 2000
@@ -69,14 +69,15 @@
 if [ "$CONFIG_INET" = "y" ]; then
   tristate 'Coda filesystem support (advanced network fs)' CONFIG_CODA_FS
   tristate 'NFS filesystem support' CONFIG_NFS_FS
+  if [ "$CONFIG_NFS_FS" != "n" ]; then
+    bool '   NFS Version 3 filesystem support' CONFIG_NFS_V3
+  fi
   if [ "$CONFIG_NFS_FS" = "y" -a "$CONFIG_IP_PNP" = "y" ]; then
     bool '   Root file system on NFS' CONFIG_ROOT_NFS
   fi
-  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-    tristate 'NFS server support' CONFIG_NFSD
-  fi
-  if [ "$CONFIG_EXPERIMENTAL" = "y" -a "$CONFIG_NFSD" != "n" ]; then
-    bool '   Emulate SUN NFS server' CONFIG_NFSD_SUN
+  tristate 'NFS server support' CONFIG_NFSD
+  if [ "$CONFIG_NFSD" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    bool '   NFS Version 3 server support (EXPERIMENTAL)' CONFIG_NFSD_V3
   fi
   if [ "$CONFIG_NFS_FS" = "y" -o "$CONFIG_NFSD" = "y" ]; then
     define_bool CONFIG_SUNRPC y
@@ -89,9 +90,6 @@
       define_bool CONFIG_SUNRPC n
       define_bool CONFIG_LOCKD n
     fi
-  fi
-  if [ "$CONFIG_NFS_FS" != "n" -o "$CONFIG_NFSD" != "n" ]; then
-    bool '   NFS Version 3' CONFIG_NFS_V3
   fi
   tristate 'SMB filesystem support (to mount WfW shares etc.)' CONFIG_SMB_FS
   if [ "$CONFIG_SMB_FS" != "n" ]; then
diff -Naur pre9/linux/fs/ext2/ialloc.c test/linux/fs/ext2/ialloc.c
--- pre9/linux/fs/ext2/ialloc.c	Tue Jan  4 10:12:23 2000
+++ test/linux/fs/ext2/ialloc.c	Mon Sep 18 14:35:50 2000
@@ -478,8 +478,14 @@
 	if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL)
 		inode->i_flags |= MS_SYNCHRONOUS;
 	insert_inode_hash(inode);
+	/* 
+	 *   dhXXX:
+	 *  To be really picky we should set i_generation to one more than
+	 *  whatever's on the disk, to ensure a monotonic advance of 
+	 *  generation for NFS.  But the odds of duplicating the last igen 
+	 *  are only 1 in 2^32...
+	 */
 	inode->i_generation = inode_generation_count++;
-	inode->u.ext2_i.i_version = inode->i_generation;
 	mark_inode_dirty(inode);
 
 	unlock_super (sb);
diff -Naur pre9/linux/fs/ext2/inode.c test/linux/fs/ext2/inode.c
--- pre9/linux/fs/ext2/inode.c	Mon Sep 18 13:58:34 2000
+++ test/linux/fs/ext2/inode.c	Mon Sep 18 14:35:50 2000
@@ -47,14 +47,14 @@
  */
 void ext2_delete_inode (struct inode * inode)
 {
-	if (inode->i_ino == EXT2_ACL_IDX_INO ||
+	if (is_bad_inode(inode) ||
+	    inode->i_ino == EXT2_ACL_IDX_INO ||
 	    inode->i_ino == EXT2_ACL_DATA_INO)
 		return;
 	inode->u.ext2_i.i_dtime	= CURRENT_TIME;
-	/* When we delete an inode, we increment its i_version. If it
-	   is ever read in from disk again, it will have a different
-	   i_version. */
-	inode->u.ext2_i.i_version++;
+	/* When we delete an inode, we increment its i_generation.
+	   If it is read in from disk again, the generation will differ. */
+	inode->i_generation++;
 	mark_inode_dirty(inode);
 	ext2_update_inode(inode, IS_SYNC(inode));
 	inode->i_size = 0;
@@ -511,9 +511,20 @@
 	inode->i_ctime = le32_to_cpu(raw_inode->i_ctime);
 	inode->i_mtime = le32_to_cpu(raw_inode->i_mtime);
 	inode->u.ext2_i.i_dtime = le32_to_cpu(raw_inode->i_dtime);
+	/* We now have enough fields to check if the inode was active or not.
+	 * This is needed because nfsd might try to access dead inodes
+	 * the test is that same one that e2fsck uses
+	 * NeilBrown 1999oct15
+	 */
+	if (inode->i_nlink == 0 && (inode->i_mode == 0 || inode->u.ext2_i.i_dtime)) {
+		/* this inode is deleted */
+		brelse (bh);
+		goto bad_inode;
+	}
 	inode->i_blksize = PAGE_SIZE;	/* This is the optimal IO size (for stat), not the fs block size */
 	inode->i_blocks = le32_to_cpu(raw_inode->i_blocks);
 	inode->i_version = ++global_event;
+	inode->i_generation = le32_to_cpu(raw_inode->i_generation);
 	inode->u.ext2_i.i_new_inode = 0;
 	inode->u.ext2_i.i_flags = le32_to_cpu(raw_inode->i_flags);
 	inode->u.ext2_i.i_faddr = le32_to_cpu(raw_inode->i_faddr);
@@ -535,8 +546,6 @@
 			<< 32;
 #endif
 	}
-	inode->u.ext2_i.i_version = le32_to_cpu(raw_inode->i_version);
-	inode->i_generation = inode->u.ext2_i.i_version;
 	inode->u.ext2_i.i_block_group = block_group;
 	inode->u.ext2_i.i_next_alloc_block = 0;
 	inode->u.ext2_i.i_next_alloc_goal = 0;
@@ -647,6 +656,7 @@
 	raw_inode->i_ctime = cpu_to_le32(inode->i_ctime);
 	raw_inode->i_mtime = cpu_to_le32(inode->i_mtime);
 	raw_inode->i_blocks = cpu_to_le32(inode->i_blocks);
+	raw_inode->i_generation = cpu_to_le32(inode->i_generation);
 	raw_inode->i_dtime = cpu_to_le32(inode->u.ext2_i.i_dtime);
 	raw_inode->i_flags = cpu_to_le32(inode->u.ext2_i.i_flags);
 	raw_inode->i_faddr = cpu_to_le32(inode->u.ext2_i.i_faddr);
@@ -663,7 +673,6 @@
 		raw_inode->i_size_high = cpu_to_le32(inode->i_size >> 32);
 #endif
 	}
-	raw_inode->i_version = cpu_to_le32(inode->u.ext2_i.i_version);
 	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
 		raw_inode->i_block[0] = cpu_to_le32(kdev_t_to_nr(inode->i_rdev));
 	else for (block = 0; block < EXT2_N_BLOCKS; block++)
diff -Naur pre9/linux/fs/ext2/ioctl.c test/linux/fs/ext2/ioctl.c
--- pre9/linux/fs/ext2/ioctl.c	Tue Jan  4 10:12:23 2000
+++ test/linux/fs/ext2/ioctl.c	Mon Sep 18 14:35:50 2000
@@ -68,15 +68,14 @@
 		mark_inode_dirty(inode);
 		return 0;
 	case EXT2_IOC_GETVERSION:
-		return put_user(inode->u.ext2_i.i_version, (int *) arg);
+		return put_user(inode->i_generation, (int *) arg);
 	case EXT2_IOC_SETVERSION:
 		if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
 			return -EPERM;
 		if (IS_RDONLY(inode))
 			return -EROFS;
-		if (get_user(inode->u.ext2_i.i_version, (int *) arg))
+		if (get_user(inode->i_generation, (int *) arg))
 			return -EFAULT;	
-		inode->i_generation = inode->u.ext2_i.i_version;
 		inode->i_ctime = CURRENT_TIME;
 		mark_inode_dirty(inode);
 		return 0;
diff -Naur pre9/linux/fs/lockd/Makefile test/linux/fs/lockd/Makefile
--- pre9/linux/fs/lockd/Makefile	Mon Sep 18 13:58:34 2000
+++ test/linux/fs/lockd/Makefile	Mon Sep 18 14:35:50 2000
@@ -9,11 +9,11 @@
 
 O_TARGET := lockd.o
 O_OBJS   := clntlock.o clntproc.o host.o svc.o svclock.o svcshare.o \
-	    svcproc.o svcsubs.o mon.o xdr.o
+	    svcproc.o svcsubs.o mon.o xdr.o 
 
-ifdef CONFIG_NFS_V3
-  O_OBJS += xdr4.o
-endif
+#ifdef CONFIG_NFS_V3
+  O_OBJS += xdr4.o svc4proc.o
+#endif
 
 OX_OBJS  := lockd_syms.o
 M_OBJS   := $(O_TARGET)
diff -Naur pre9/linux/fs/lockd/lockd_syms.c test/linux/fs/lockd/lockd_syms.c
--- pre9/linux/fs/lockd/lockd_syms.c	Tue Jan  4 10:12:23 2000
+++ test/linux/fs/lockd/lockd_syms.c	Mon Sep 18 14:35:50 2000
@@ -23,6 +23,7 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/svc.h>
 #include <linux/lockd/lockd.h>
+#include <linux/lockd/xdr4.h>
 #include <linux/lockd/syscall.h>
 
 /* Start/stop the daemon */
@@ -40,5 +41,18 @@
 /* Configuration at insmod time */
 EXPORT_SYMBOL(nlmsvc_grace_period);
 EXPORT_SYMBOL(nlmsvc_timeout);
+
+/* NLM4 exported symbols */ 
+EXPORT_SYMBOL(nlm4_lck_denied_grace_period);
+EXPORT_SYMBOL(nlm4_lck_denied);
+EXPORT_SYMBOL(nlm4_lck_blocked);
+EXPORT_SYMBOL(nlm4_rofs);
+EXPORT_SYMBOL(nlm4_stale_fh);
+EXPORT_SYMBOL(nlm4_granted);
+EXPORT_SYMBOL(nlm4_deadlock);
+EXPORT_SYMBOL(nlm4_failed);
+EXPORT_SYMBOL(nlm4_fbig);
+EXPORT_SYMBOL(nlm4_lck_denied_nolocks);
+
 
 #endif /* CONFIG_MODULES */
diff -Naur pre9/linux/fs/lockd/svc.c test/linux/fs/lockd/svc.c
--- pre9/linux/fs/lockd/svc.c	Mon Sep  4 10:39:22 2000
+++ test/linux/fs/lockd/svc.c	Mon Sep 18 14:35:50 2000
@@ -230,6 +230,7 @@
 
 	error = -ENOMEM;
 	serv = svc_create(&nlmsvc_program, 0, NLMSVC_XDRSIZE);
+
 	if (!serv) {
 		printk(KERN_WARNING "lockd_up: create service failed\n");
 		goto out;
@@ -353,7 +354,7 @@
 static struct svc_version	nlmsvc_version3 = {
 	3, 24, nlmsvc_procedures, NULL
 };
-#ifdef CONFIG_NFSD_NFS3
+#if (defined(CONFIG_NFSD) || defined(CONFIG_NFSD_MODULE)) && defined(CONFIG_NFS_V3)
 static struct svc_version	nlmsvc_version4 = {
 	4, 24, nlmsvc_procedures4, NULL
 };
@@ -363,7 +364,7 @@
 	&nlmsvc_version1,
 	NULL,
 	&nlmsvc_version3,
-#ifdef CONFIG_NFSD_NFS3
+#if (defined(CONFIG_NFSD) || defined(CONFIG_NFSD_MODULE)) && defined(CONFIG_NFS_V3)
 	&nlmsvc_version4,
 #endif
 };
@@ -383,6 +384,7 @@
 int
 lockdctl(int cmd, void *opaque_argp, void *opaque_resp)
 {
+#if 0
 	int err;
 
 	MOD_INC_USE_COUNT;
@@ -398,4 +400,17 @@
 	MOD_DEC_USE_COUNT;
 
 	return err;
+#else
+	/*
+	 *  For the moment, unless a real need for locks on NFS root
+	 *  emerges, we revert to automatic lockd start.  But we will leave the
+	 *  manual call machinery in place in case we ever want to go
+	 *  back to it.  I felt a warning was useful here, but many didn't like
+	 *  that, so I'll suppress it.  - dhiggen
+	 */
+#if 0
+	 printk("lockd: note, lockd is automatic in this kernel.  Remove rpc.lockd from any rc scripts.\n"); 
+#endif
+	return (0);
+#endif
 }
diff -Naur pre9/linux/fs/lockd/svc4proc.c test/linux/fs/lockd/svc4proc.c
--- pre9/linux/fs/lockd/svc4proc.c	Wed Dec 31 16:00:00 1969
+++ test/linux/fs/lockd/svc4proc.c	Mon Sep 18 14:35:50 2000
@@ -0,0 +1,561 @@
+/*
+ * linux/fs/lockd/svc4proc.c
+ *
+ * Lockd server procedures. We don't implement the NLM_*_RES 
+ * procedures because we don't use the async procedures.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/in.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/lockd/xdr4.h>
+#include <linux/lockd/lockd.h>
+#include <linux/lockd/share.h>
+#include <linux/lockd/sm_inter.h>
+
+
+#define NLMDBG_FACILITY		NLMDBG_CLIENT
+
+static u32	nlm4svc_callback(struct svc_rqst *, u32, struct nlm_res *);
+static void	nlm4svc_callback_exit(struct rpc_task *);
+
+/*
+ * Obtain client and file from arguments
+ */
+static u32
+nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
+			struct nlm_host **hostp, struct nlm_file **filp)
+{
+	struct nlm_host		*host = NULL;
+	struct nlm_file		*file = NULL;
+	struct nlm_lock		*lock = &argp->lock;
+	u32			error = 0;
+
+	/* nfsd callbacks must have been installed for this procedure */
+	if (!nlmsvc_ops)
+		return nlm4_lck_denied_nolocks;
+
+	/* Obtain handle for client host */
+	if (rqstp->rq_client == NULL) {
+		printk(KERN_NOTICE
+			"lockd: unauthenticated request from (%08x:%d)\n",
+			ntohl(rqstp->rq_addr.sin_addr.s_addr),
+			ntohs(rqstp->rq_addr.sin_port));
+		return nlm4_lck_denied_nolocks;
+	}
+
+	/* Obtain host handle */
+	if (!(host = nlmsvc_lookup_host(rqstp))
+	 || (argp->monitor && !host->h_monitored && nsm_monitor(host) < 0))
+		goto no_locks;
+	*hostp = host;
+
+	/* Obtain file pointer. Not used by FREE_ALL call. */
+	if (filp != NULL) {
+		if ((error = nlm_lookup_file(rqstp, &file, &lock->fh)) != 0)
+			goto no_locks;
+		*filp = file;
+
+		/* Set up the missing parts of the file_lock structure */
+		lock->fl.fl_file  = &file->f_file;
+		lock->fl.fl_owner = (fl_owner_t) host;
+	}
+
+	return 0;
+
+no_locks:
+	if (host)
+		nlm_release_host(host);
+	/* check the error to see if its a stale fh error */
+ 	if (error)
+		return error;	
+	return nlm4_lck_denied_nolocks;
+}
+
+/*
+ * NULL: Test for presence of service
+ */
+static int
+nlm4svc_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
+{
+	dprintk("lockd: NULL          called\n");
+	return rpc_success;
+}
+
+/*
+ * TEST: Check for conflicting lock
+ */
+static int
+nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp,
+				         struct nlm_res  *resp)
+{
+	struct nlm_host	*host;
+	struct nlm_file	*file;
+
+	dprintk("lockd: TEST4        called\n");
+	resp->cookie = argp->cookie;
+
+	/* Don't accept test requests during grace period */
+	if (nlmsvc_grace_period) {
+		resp->status = nlm4_lck_denied_grace_period;
+		return rpc_success;
+	}
+
+	/* Obtain client and file */
+	if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
+		return rpc_success;
+
+	/* Now check for conflicting locks */
+	resp->status = nlmsvc_testlock(file, &argp->lock, &resp->lock);
+
+	dprintk("lockd: TEST4          status %d\n", ntohl(resp->status));
+	nlm_release_host(host);
+	nlm_release_file(file);
+	return rpc_success;
+}
+
+static int
+nlm4svc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
+				         struct nlm_res  *resp)
+{
+	struct nlm_host	*host;
+	struct nlm_file	*file;
+
+	dprintk("lockd: LOCK          called\n");
+
+	resp->cookie = argp->cookie;
+
+	/* Don't accept new lock requests during grace period */
+	if (nlmsvc_grace_period && !argp->reclaim) {
+		resp->status = nlm4_lck_denied_grace_period;
+		return rpc_success;
+	}
+
+	/* Obtain client and file */
+	if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
+		return rpc_success;
+
+#if 0
+	/* If supplied state doesn't match current state, we assume it's
+	 * an old request that time-warped somehow. Any error return would
+	 * do in this case because it's irrelevant anyway.
+	 *
+	 * NB: We don't retrieve the remote host's state yet.
+	 */
+	if (host->h_nsmstate && host->h_nsmstate != argp->state) {
+		resp->status = nlm4_lck_denied_nolocks;
+	} else
+#endif
+
+	/* Now try to lock the file */
+	resp->status = nlmsvc_lock(rqstp, file, &argp->lock,
+					argp->block, &argp->cookie);
+
+	dprintk("lockd: LOCK          status %d\n", ntohl(resp->status));
+	nlm_release_host(host);
+	nlm_release_file(file);
+	return rpc_success;
+}
+
+static int
+nlm4svc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp,
+				           struct nlm_res  *resp)
+{
+	struct nlm_host	*host;
+	struct nlm_file	*file;
+
+	dprintk("lockd: CANCEL        called\n");
+
+	resp->cookie = argp->cookie;
+
+	/* Don't accept requests during grace period */
+	if (nlmsvc_grace_period) {
+		resp->status = nlm4_lck_denied_grace_period;
+		return rpc_success;
+	}
+
+	/* Obtain client and file */
+	if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
+		return rpc_success;
+
+	/* Try to cancel request. */
+	resp->status = nlmsvc_cancel_blocked(file, &argp->lock);
+
+	dprintk("lockd: CANCEL        status %d\n", ntohl(resp->status));
+	nlm_release_host(host);
+	nlm_release_file(file);
+	return rpc_success;
+}
+
+/*
+ * UNLOCK: release a lock
+ */
+static int
+nlm4svc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp,
+				           struct nlm_res  *resp)
+{
+	struct nlm_host	*host;
+	struct nlm_file	*file;
+
+	dprintk("lockd: UNLOCK        called\n");
+
+	resp->cookie = argp->cookie;
+
+	/* Don't accept new lock requests during grace period */
+	if (nlmsvc_grace_period) {
+		resp->status = nlm4_lck_denied_grace_period;
+		return rpc_success;
+	}
+
+	/* Obtain client and file */
+	if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
+		return rpc_success;
+
+	/* Now try to remove the lock */
+	resp->status = nlmsvc_unlock(file, &argp->lock);
+
+	dprintk("lockd: UNLOCK        status %d\n", ntohl(resp->status));
+	nlm_release_host(host);
+	nlm_release_file(file);
+	return rpc_success;
+}
+
+/*
+ * GRANTED: A server calls us to tell that a process' lock request
+ * was granted
+ */
+static int
+nlm4svc_proc_granted(struct svc_rqst *rqstp, struct nlm_args *argp,
+				            struct nlm_res  *resp)
+{
+	resp->cookie = argp->cookie;
+
+	dprintk("lockd: GRANTED       called\n");
+	resp->status = nlmclnt_grant(&argp->lock);
+	dprintk("lockd: GRANTED       status %d\n", ntohl(resp->status));
+	return rpc_success;
+}
+
+/*
+ * `Async' versions of the above service routines. They aren't really,
+ * because we send the callback before the reply proper. I hope this
+ * doesn't break any clients.
+ */
+static int
+nlm4svc_proc_test_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
+					     void	     *resp)
+{
+	struct nlm_res	res;
+	u32		stat;
+
+	dprintk("lockd: TEST_MSG      called\n");
+
+	if ((stat = nlm4svc_proc_test(rqstp, argp, &res)) == 0)
+		stat = nlm4svc_callback(rqstp, NLMPROC_TEST_RES, &res);
+	return stat;
+}
+
+static int
+nlm4svc_proc_lock_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
+					     void	     *resp)
+{
+	struct nlm_res	res;
+	u32		stat;
+
+	dprintk("lockd: LOCK_MSG      called\n");
+
+	if ((stat = nlm4svc_proc_lock(rqstp, argp, &res)) == 0)
+		stat = nlm4svc_callback(rqstp, NLMPROC_LOCK_RES, &res);
+	return stat;
+}
+
+static int
+nlm4svc_proc_cancel_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
+					       void	       *resp)
+{
+	struct nlm_res	res;
+	u32		stat;
+
+	dprintk("lockd: CANCEL_MSG    called\n");
+
+	if ((stat = nlm4svc_proc_cancel(rqstp, argp, &res)) == 0)
+		stat = nlm4svc_callback(rqstp, NLMPROC_CANCEL_RES, &res);
+	return stat;
+}
+
+static int
+nlm4svc_proc_unlock_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
+                                               void            *resp)
+{
+	struct nlm_res	res;
+	u32		stat;
+
+	dprintk("lockd: UNLOCK_MSG    called\n");
+
+	if ((stat = nlm4svc_proc_unlock(rqstp, argp, &res)) == 0)
+		stat = nlm4svc_callback(rqstp, NLMPROC_UNLOCK_RES, &res);
+	return stat;
+}
+
+static int
+nlm4svc_proc_granted_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
+                                                void            *resp)
+{
+	struct nlm_res	res;
+	u32		stat;
+
+	dprintk("lockd: GRANTED_MSG   called\n");
+
+	if ((stat = nlm4svc_proc_granted(rqstp, argp, &res)) == 0)
+		stat = nlm4svc_callback(rqstp, NLMPROC_GRANTED_RES, &res);
+	return stat;
+}
+
+/*
+ * SHARE: create a DOS share or alter existing share.
+ */
+static int
+nlm4svc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp,
+				          struct nlm_res  *resp)
+{
+	struct nlm_host	*host;
+	struct nlm_file	*file;
+
+	dprintk("lockd: SHARE         called\n");
+
+	resp->cookie = argp->cookie;
+
+	/* Don't accept new lock requests during grace period */
+	if (nlmsvc_grace_period && !argp->reclaim) {
+		resp->status = nlm4_lck_denied_grace_period;
+		return rpc_success;
+	}
+
+	/* Obtain client and file */
+	if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
+		return rpc_success;
+
+	/* Now try to create the share */
+	resp->status = nlmsvc_share_file(host, file, argp);
+
+	dprintk("lockd: SHARE         status %d\n", ntohl(resp->status));
+	nlm_release_host(host);
+	nlm_release_file(file);
+	return rpc_success;
+}
+
+/*
+ * UNSHARE: Release a DOS share.
+ */
+static int
+nlm4svc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp,
+				            struct nlm_res  *resp)
+{
+	struct nlm_host	*host;
+	struct nlm_file	*file;
+
+	dprintk("lockd: UNSHARE       called\n");
+
+	resp->cookie = argp->cookie;
+
+	/* Don't accept requests during grace period */
+	if (nlmsvc_grace_period) {
+		resp->status = nlm4_lck_denied_grace_period;
+		return rpc_success;
+	}
+
+	/* Obtain client and file */
+	if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
+		return rpc_success;
+
+	/* Now try to lock the file */
+	resp->status = nlmsvc_unshare_file(host, file, argp);
+
+	dprintk("lockd: UNSHARE       status %d\n", ntohl(resp->status));
+	nlm_release_host(host);
+	nlm_release_file(file);
+	return rpc_success;
+}
+
+/*
+ * NM_LOCK: Create an unmonitored lock
+ */
+static int
+nlm4svc_proc_nm_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
+				            struct nlm_res  *resp)
+{
+	dprintk("lockd: NM_LOCK       called\n");
+
+	argp->monitor = 0;		/* just clean the monitor flag */
+	return nlm4svc_proc_lock(rqstp, argp, resp);
+}
+
+/*
+ * FREE_ALL: Release all locks and shares held by client
+ */
+static int
+nlm4svc_proc_free_all(struct svc_rqst *rqstp, struct nlm_args *argp,
+					     void            *resp)
+{
+	struct nlm_host	*host;
+
+	/* Obtain client */
+	if (nlm4svc_retrieve_args(rqstp, argp, &host, NULL))
+		return rpc_success;
+
+	nlmsvc_free_host_resources(host);
+	nlm_release_host(host);
+	return rpc_success;
+}
+
+/*
+ * SM_NOTIFY: private callback from statd (not part of official NLM proto)
+ */
+static int
+nlm4svc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp,
+					      void	        *resp)
+{
+	struct sockaddr_in	saddr = rqstp->rq_addr;
+	struct nlm_host		*host;
+
+	dprintk("lockd: SM_NOTIFY     called\n");
+	if (saddr.sin_addr.s_addr != htonl(INADDR_LOOPBACK)
+	 || ntohs(saddr.sin_port) >= 1024) {
+		printk(KERN_WARNING
+			"lockd: rejected NSM callback from %08x:%d\n",
+			ntohl(rqstp->rq_addr.sin_addr.s_addr),
+			ntohs(rqstp->rq_addr.sin_port));
+		return rpc_system_err;
+	}
+
+	/* Obtain the host pointer for this NFS server and try to
+	 * reclaim all locks we hold on this server.
+	 */
+	saddr.sin_addr.s_addr = argp->addr;	
+	if ((host = nlm_lookup_host(NULL, &saddr, IPPROTO_UDP, 1)) != NULL) {
+		nlmclnt_recovery(host, argp->state);
+		nlm_release_host(host);
+	}
+
+	/* If we run on an NFS server, delete all locks held by the client */
+	if (nlmsvc_ops != NULL) {
+		struct svc_client	*clnt;
+		saddr.sin_addr.s_addr = argp->addr;	
+		if ((clnt = nlmsvc_ops->exp_getclient(&saddr)) != NULL 
+		 && (host = nlm_lookup_host(clnt, &saddr, 0, 0)) != NULL) {
+			nlmsvc_free_host_resources(host);
+		}
+		nlm_release_host(host);
+	}
+
+	return rpc_success;
+}
+
+/*
+ * This is the generic lockd callback for async RPC calls
+ */
+static u32
+nlm4svc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_res *resp)
+{
+	struct nlm_host	*host;
+	struct nlm_rqst	*call;
+
+	if (!(call = nlmclnt_alloc_call()))
+		return rpc_system_err;
+
+	host = nlmclnt_lookup_host(&rqstp->rq_addr,
+				rqstp->rq_prot, rqstp->rq_vers);
+	if (!host) {
+		rpc_free(call);
+		return rpc_system_err;
+	}
+
+	call->a_flags = RPC_TASK_ASYNC;
+	call->a_host  = host;
+	memcpy(&call->a_args, resp, sizeof(*resp));
+
+	if (nlmsvc_async_call(call, proc, nlm4svc_callback_exit) < 0)
+		return rpc_system_err;
+
+	return rpc_success;
+}
+
+static void
+nlm4svc_callback_exit(struct rpc_task *task)
+{
+	struct nlm_rqst	*call = (struct nlm_rqst *) task->tk_calldata;
+
+	if (task->tk_status < 0) {
+		dprintk("lockd: %4d callback failed (errno = %d)\n",
+					task->tk_pid, -task->tk_status);
+	}
+	nlm_release_host(call->a_host);
+	rpc_free(call);
+}
+
+/*
+ * NLM Server procedures.
+ */
+
+#define nlm4svc_encode_norep	nlm4svc_encode_void
+#define nlm4svc_decode_norep	nlm4svc_decode_void
+#define nlm4svc_decode_testres	nlm4svc_decode_void
+#define nlm4svc_decode_lockres	nlm4svc_decode_void
+#define nlm4svc_decode_unlockres	nlm4svc_decode_void
+#define nlm4svc_decode_cancelres	nlm4svc_decode_void
+#define nlm4svc_decode_grantedres	nlm4svc_decode_void
+
+#define nlm4svc_proc_none	nlm4svc_proc_null
+#define nlm4svc_proc_test_res	nlm4svc_proc_null
+#define nlm4svc_proc_lock_res	nlm4svc_proc_null
+#define nlm4svc_proc_cancel_res	nlm4svc_proc_null
+#define nlm4svc_proc_unlock_res	nlm4svc_proc_null
+#define nlm4svc_proc_granted_res	nlm4svc_proc_null
+
+struct nlm_void			{ int dummy; };
+
+#define PROC(name, xargt, xrest, argt, rest)	\
+ { (svc_procfunc) nlm4svc_proc_##name,	\
+   (kxdrproc_t) nlm4svc_decode_##xargt,	\
+   (kxdrproc_t) nlm4svc_encode_##xrest,	\
+   NULL,				\
+   sizeof(struct nlm_##argt),		\
+   sizeof(struct nlm_##rest),		\
+   0,					\
+   0					\
+ }
+struct svc_procedure		nlmsvc_procedures4[] = {
+  PROC(null,		void,		void,		void,	void),
+  PROC(test,		testargs,	testres,	args,	res),
+  PROC(lock,		lockargs,	res,		args,	res),
+  PROC(cancel,		cancargs,	res,		args,	res),
+  PROC(unlock,		unlockargs,	res,		args,	res),
+  PROC(granted,		testargs,	res,		args,	res),
+  PROC(test_msg,	testargs,	norep,		args,	void),
+  PROC(lock_msg,	lockargs,	norep,		args,	void),
+  PROC(cancel_msg,	cancargs,	norep,		args,	void),
+  PROC(unlock_msg,	unlockargs,	norep,		args,	void),
+  PROC(granted_msg,	testargs,	norep,		args,	void),
+  PROC(test_res,	testres,	norep,		res,	void),
+  PROC(lock_res,	lockres,	norep,		res,	void),
+  PROC(cancel_res,	cancelres,	norep,		res,	void),
+  PROC(unlock_res,	unlockres,	norep,		res,	void),
+  PROC(granted_res,	grantedres,	norep,		res,	void),
+  PROC(none,		void,		void,		void,	void),
+  PROC(none,		void,		void,		void,	void),
+  PROC(none,		void,		void,		void,	void),
+  PROC(none,		void,		void,		void,	void),
+  PROC(share,		shareargs,	shareres,	args,	res),
+  PROC(unshare,		shareargs,	shareres,	args,	res),
+  PROC(nm_lock,		lockargs,	res,		args,	res),
+  PROC(free_all,	notify,		void,		args,	void),
+
+  /* statd callback */
+  PROC(sm_notify,	reboot,		void,		reboot,	void),
+};
diff -Naur pre9/linux/fs/lockd/svclock.c test/linux/fs/lockd/svclock.c
--- pre9/linux/fs/lockd/svclock.c	Mon Sep 18 13:58:34 2000
+++ test/linux/fs/lockd/svclock.c	Mon Sep 18 14:35:50 2000
@@ -26,6 +26,7 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/svc.h>
 #include <linux/lockd/nlm.h>
+#include <linux/lockd/xdr4.h>
 #include <linux/lockd/lockd.h>
 
 
@@ -98,9 +99,10 @@
 				lock->fl.fl_end, lock->fl.fl_type);
 	for (head = &nlm_blocked; (block = *head); head = &block->b_next) {
 		fl = &block->b_call.a_args.lock.fl;
-		dprintk("       check f=%p pd=%d %ld-%ld ty=%d\n",
+		dprintk("lockd: check f=%p pd=%d %ld-%ld ty=%d cookie=%x\n",
 				block->b_file, fl->fl_pid, fl->fl_start,
-				fl->fl_end, fl->fl_type);
+				fl->fl_end, fl->fl_type, 
+				*(u32 *)(&block->b_call.a_args.cookie.data));
 		if (block->b_file == file && nlm_compare_locks(fl, &lock->fl)) {
 			if (remove)
 				*head = block->b_next;
@@ -129,6 +131,8 @@
 	struct nlm_block *block;
 
 	for (block = nlm_blocked; block; block = block->b_next) {
+		dprintk("cookie: head of blocked queue %p, block %p\n", 
+			nlm_blocked, block);
 		if (nlm_cookie_match(&block->b_call.a_args.cookie,cookie))
 			break;
 	}
@@ -280,6 +284,7 @@
 {
 	struct file_lock	*conflock;
 	struct nlm_block	*block;
+	struct inode            *inode = file->f_file.f_dentry->d_inode;
 	int			error;
 
 	dprintk("lockd: nlmsvc_lock(%04x/%ld, ty=%d, pi=%d, %ld-%ld, bl=%d)\n",
@@ -289,6 +294,10 @@
 				lock->fl.fl_start,
 				lock->fl.fl_end,
 				wait);
+	
+	/* Checking for read only file system */
+	if (IS_RDONLY(inode))
+		return nlm4_rofs;
 
 	/* Lock file against concurrent access */
 	down(&file->f_sema);
@@ -301,7 +310,7 @@
 again:
 	if (!(conflock = posix_test_lock(&file->f_file, &lock->fl))) {
 		error = posix_lock_file(&file->f_file, &lock->fl, 0);
-
+		
 		if (block)
 			nlmsvc_delete_block(block, 0);
 		up(&file->f_sema);
@@ -309,18 +318,19 @@
 		dprintk("lockd: posix_lock_file returned %d\n", -error);
 		switch(-error) {
 		case 0:
-			return nlm_granted;
-		case EDEADLK:			/* no applicable NLM status */
+			return nlm4_granted;
+		case EDEADLK:		
+			return nlm4_deadlock;
 		case EAGAIN:
-			return nlm_lck_denied;
+			return nlm4_lck_denied;
 		default:			/* includes ENOLCK */
-			return nlm_lck_denied_nolocks;
+			return nlm4_lck_denied_nolocks;
 		}
 	}
 
 	if (!wait) {
 		up(&file->f_sema);
-		return nlm_lck_denied;
+		return nlm4_lck_denied;
 	}
 
 	/* If we don't have a block, create and initialize it. Then
@@ -328,7 +338,7 @@
 	if (block == NULL) {
 		dprintk("lockd: blocking on this lock (allocating).\n");
 		if (!(block = nlmsvc_create_block(rqstp, file, lock, cookie)))
-			return nlm_lck_denied_nolocks;
+			return nlm4_lck_denied_nolocks;
 		goto again;
 	}
 
@@ -343,7 +353,7 @@
 	}
 
 	up(&file->f_sema);
-	return nlm_lck_blocked;
+	return nlm4_lck_blocked;
 }
 
 /*
@@ -364,14 +374,15 @@
 
 	if ((fl = posix_test_lock(&file->f_file, &lock->fl)) != NULL) {
 		dprintk("lockd: conflicting lock(ty=%d, %ld-%ld)\n",
-				fl->fl_type, fl->fl_start, fl->fl_end);
+				fl->fl_type, fl->fl_start, fl->fl_end );
+
 		conflock->caller = "somehost";	/* FIXME */
 		conflock->oh.len = 0;		/* don't return OH info */
 		conflock->fl = *fl;
-		return nlm_lck_denied;
+		return nlm4_lck_denied;
 	}
 
-	return nlm_granted;
+	return nlm4_granted;
 }
 
 /*
@@ -399,7 +410,7 @@
 	lock->fl.fl_type = F_UNLCK;
 	error = posix_lock_file(&file->f_file, &lock->fl, 0);
 
-	return (error < 0)? nlm_lck_denied_nolocks : nlm_granted;
+	return (error < 0)? nlm4_lck_denied_nolocks : nlm4_granted;
 }
 
 /*
@@ -425,7 +436,7 @@
 	if ((block = nlmsvc_lookup_block(file, lock, 1)) != NULL)
 		nlmsvc_delete_block(block, 1);
 	up(&file->f_sema);
-	return nlm_granted;
+	return nlm4_granted;
 }
 
 /*
@@ -541,6 +552,8 @@
 	unsigned long		timeout;
 
 	dprintk("lockd: GRANT_MSG RPC callback\n");
+	dprintk("callback: looking for cookie %x \n", 
+		*(u32 *)(call->a_args.cookie.data));
 	if (!(block = nlmsvc_find_block(&call->a_args.cookie))) {
 		dprintk("lockd: no block for cookie %x\n", *(u32 *)(call->a_args.cookie.data));
 		return;
@@ -616,7 +629,7 @@
 	dprintk("nlmsvc_retry_blocked(%p, when=%ld)\n",
 			nlm_blocked,
 			nlm_blocked? nlm_blocked->b_when : 0);
-	while ((block = nlm_blocked) && block->b_when < jiffies) {
+	while ((block = nlm_blocked) && block->b_when <= jiffies) {
 		dprintk("nlmsvc_retry_blocked(%p, when=%ld, done=%d)\n",
 			block, block->b_when, block->b_done);
 		if (block->b_done)
@@ -627,6 +640,5 @@
 
 	if ((block = nlm_blocked) && block->b_when != NLM_NEVER)
 		return (block->b_when - jiffies);
-
 	return MAX_SCHEDULE_TIMEOUT;
 }
diff -Naur pre9/linux/fs/lockd/svcproc.c test/linux/fs/lockd/svcproc.c
--- pre9/linux/fs/lockd/svcproc.c	Mon Sep 18 13:58:34 2000
+++ test/linux/fs/lockd/svcproc.c	Mon Sep 18 14:35:50 2000
@@ -14,6 +14,8 @@
 #include <linux/sunrpc/svc.h>
 #include <linux/sunrpc/clnt.h>
 #include <linux/nfsd/nfsd.h>
+#include <linux/lockd/xdr.h>
+#include <linux/lockd/xdr4.h>
 #include <linux/lockd/lockd.h>
 #include <linux/lockd/share.h>
 #include <linux/lockd/sm_inter.h>
@@ -23,6 +25,7 @@
 
 static u32	nlmsvc_callback(struct svc_rqst *, u32, struct nlm_res *);
 static void	nlmsvc_callback_exit(struct rpc_task *);
+static u32	cast_to_nlm(u32, u32);
 
 /*
  * Obtain client and file from arguments
@@ -93,6 +96,7 @@
 {
 	struct nlm_host	*host;
 	struct nlm_file	*file;
+	u32 status;
 
 	dprintk("lockd: TEST          called\n");
 	resp->cookie = argp->cookie;
@@ -108,9 +112,12 @@
 		return rpc_success;
 
 	/* Now check for conflicting locks */
-	resp->status = nlmsvc_testlock(file, &argp->lock, &resp->lock);
+	status = nlmsvc_testlock(file, &argp->lock, &resp->lock);
+	dprintk("test: status before %d\n", ntohl(status));
+	resp->status = cast_to_nlm(status, rqstp->rq_vers); 
 
-	dprintk("lockd: TEST          status %d\n", ntohl(resp->status));
+	dprintk("lockd: TEST          status %d vers %d\n", 
+		ntohl(resp->status), rqstp->rq_vers);
 	nlm_release_host(host);
 	nlm_release_file(file);
 	return rpc_success;
@@ -122,6 +129,7 @@
 {
 	struct nlm_host	*host;
 	struct nlm_file	*file;
+	u32 status;
 
 	dprintk("lockd: LOCK          called\n");
 
@@ -150,8 +158,9 @@
 #endif
 
 	/* Now try to lock the file */
-	resp->status = nlmsvc_lock(rqstp, file, &argp->lock,
-					argp->block, &argp->cookie);
+	status = nlmsvc_lock(rqstp, file, &argp->lock, 
+				argp->block, &argp->cookie);
+	resp->status = cast_to_nlm(status, rqstp->rq_vers);
 
 	dprintk("lockd: LOCK          status %d\n", ntohl(resp->status));
 	nlm_release_host(host);
@@ -165,6 +174,7 @@
 {
 	struct nlm_host	*host;
 	struct nlm_file	*file;
+	u32 status;
 
 	dprintk("lockd: CANCEL        called\n");
 
@@ -181,7 +191,8 @@
 		return rpc_success;
 
 	/* Try to cancel request. */
-	resp->status = nlmsvc_cancel_blocked(file, &argp->lock);
+	status = nlmsvc_cancel_blocked(file, &argp->lock);
+	resp->status = cast_to_nlm(status, rqstp->rq_vers);
 
 	dprintk("lockd: CANCEL        status %d\n", ntohl(resp->status));
 	nlm_release_host(host);
@@ -198,6 +209,7 @@
 {
 	struct nlm_host	*host;
 	struct nlm_file	*file;
+	u32 status;
 
 	dprintk("lockd: UNLOCK        called\n");
 
@@ -214,7 +226,8 @@
 		return rpc_success;
 
 	/* Now try to remove the lock */
-	resp->status = nlmsvc_unlock(file, &argp->lock);
+	status = nlmsvc_unlock(file, &argp->lock);
+	resp->status = cast_to_nlm(status, rqstp->rq_vers);	
 
 	dprintk("lockd: UNLOCK        status %d\n", ntohl(resp->status));
 	nlm_release_host(host);
@@ -322,6 +335,7 @@
 {
 	struct nlm_host	*host;
 	struct nlm_file	*file;
+	u32 status;
 
 	dprintk("lockd: SHARE         called\n");
 
@@ -338,7 +352,8 @@
 		return rpc_success;
 
 	/* Now try to create the share */
-	resp->status = nlmsvc_share_file(host, file, argp);
+	status = nlmsvc_share_file(host, file, argp);
+	resp->status = cast_to_nlm(status, rqstp->rq_vers);
 
 	dprintk("lockd: SHARE         status %d\n", ntohl(resp->status));
 	nlm_release_host(host);
@@ -355,6 +370,7 @@
 {
 	struct nlm_host	*host;
 	struct nlm_file	*file;
+	u32 status;
 
 	dprintk("lockd: UNSHARE       called\n");
 
@@ -371,7 +387,8 @@
 		return rpc_success;
 
 	/* Now try to lock the file */
-	resp->status = nlmsvc_unshare_file(host, file, argp);
+	status = nlmsvc_unshare_file(host, file, argp);
+	resp->status = cast_to_nlm(status, rqstp->rq_vers);
 
 	dprintk("lockd: UNSHARE       status %d\n", ntohl(resp->status));
 	nlm_release_host(host);
@@ -495,6 +512,27 @@
 	kfree(call);
 }
 
+static u32
+cast_to_nlm(u32 status, u32 vers)
+{
+
+	if (vers != 4){
+		switch(ntohl(status)){
+		case NLM_LCK_GRANTED:
+		case NLM_LCK_DENIED:
+		case NLM_LCK_DENIED_NOLOCKS:
+		case NLM_LCK_BLOCKED:
+		case NLM_LCK_DENIED_GRACE_PERIOD:
+			break;
+		default:
+			status = NLM_LCK_DENIED_NOLOCKS;
+		}
+	}
+
+	return (status);
+	
+}
+	
 /*
  * NLM Server procedures.
  */
diff -Naur pre9/linux/fs/lockd/svcshare.c test/linux/fs/lockd/svcshare.c
--- pre9/linux/fs/lockd/svcshare.c	Mon Apr  7 11:35:30 1997
+++ test/linux/fs/lockd/svcshare.c	Mon Sep 18 14:35:50 2000
@@ -12,6 +12,7 @@
 
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/svc.h>
+#include <linux/lockd/xdr4.h>
 #include <linux/lockd/lockd.h>
 #include <linux/lockd/share.h>
 
@@ -35,13 +36,13 @@
 			goto update;
 		if ((argp->fsm_access & share->s_mode)
 		 || (argp->fsm_mode   & share->s_access ))
-			return nlm_lck_denied;
+			return nlm4_lck_denied;
 	}
 
 	share = (struct nlm_share *) kmalloc(sizeof(*share) + oh->len,
 						GFP_KERNEL);
 	if (share == NULL)
-		return nlm_lck_denied_nolocks;
+		return nlm4_lck_denied_nolocks;
 
 	/* Copy owner handle */
 	ohdata = (u8 *) (share + 1);
@@ -58,7 +59,7 @@
 update:
 	share->s_access = argp->fsm_access;
 	share->s_mode   = argp->fsm_mode;
-	return nlm_granted;
+	return nlm4_granted;
 }
 
 /*
@@ -75,13 +76,13 @@
 		if (share->s_host == host && nlm_cmp_owner(share, oh)) {
 			*shpp = share->s_next;
 			kfree(share);
-			return nlm_granted;
+			return nlm4_granted;
 		}
 	}
 
 	/* X/Open spec says return success even if there was no
 	 * corresponding share. */
-	return nlm_granted;
+	return nlm4_granted;
 }
 
 /*
diff -Naur pre9/linux/fs/lockd/svcsubs.c test/linux/fs/lockd/svcsubs.c
--- pre9/linux/fs/lockd/svcsubs.c	Mon Sep 18 13:58:34 2000
+++ test/linux/fs/lockd/svcsubs.c	Mon Sep 18 14:35:50 2000
@@ -13,6 +13,7 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/nfsd/nfsfh.h>
 #include <linux/nfsd/export.h>
+#include <linux/lockd/xdr4.h>
 #include <linux/lockd/lockd.h>
 #include <linux/lockd/share.h>
 #include <linux/lockd/sm_inter.h>
@@ -69,7 +70,7 @@
 
 	dprintk("lockd: creating file for %s/%u\n",
 		kdevname(u32_to_kdev_t(fh->fh_dev)), fh->fh_ino);
-	nfserr = nlm_lck_denied_nolocks;
+	nfserr = nlm4_lck_denied_nolocks;
 	file = (struct nlm_file *) kmalloc(sizeof(*file), GFP_KERNEL);
 	if (!file)
 		goto out_unlock;
@@ -104,7 +105,10 @@
 
 out_free:
 	kfree(file);
-	nfserr = nlm_lck_denied;
+        if (nfserr == 1)
+                nfserr = nlm4_stale_fh;
+        else
+		nfserr = nlm4_lck_denied;
 	goto out_unlock;
 }
 
diff -Naur pre9/linux/fs/lockd/xdr.c test/linux/fs/lockd/xdr.c
--- pre9/linux/fs/lockd/xdr.c	Mon Sep 18 13:58:34 2000
+++ test/linux/fs/lockd/xdr.c	Mon Sep 18 14:35:50 2000
@@ -16,8 +16,10 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/svc.h>
 #include <linux/sunrpc/stats.h>
+#include <linux/lockd/xdr4.h>
 #include <linux/lockd/lockd.h>
 #include <linux/lockd/sm_inter.h>
+#include <linux/nfs2.h>
 
 #define NLMDBG_FACILITY		NLMDBG_XDR
 #define NLM_MAXSTRLEN		1024
@@ -51,6 +53,17 @@
 	nlm_lck_blocked = htonl(NLM_LCK_BLOCKED);
 	nlm_lck_denied_grace_period = htonl(NLM_LCK_DENIED_GRACE_PERIOD);
 
+        nlm4_granted = htonl(NLM_LCK_GRANTED);
+        nlm4_lck_denied = htonl(NLM_LCK_DENIED);
+        nlm4_lck_denied_nolocks = htonl(NLM_LCK_DENIED_NOLOCKS);
+        nlm4_lck_blocked = htonl(NLM_LCK_BLOCKED);
+        nlm4_lck_denied_grace_period = htonl(NLM_LCK_DENIED_GRACE_PERIOD);
+	nlm4_deadlock = htonl(NLM_DEADLCK);
+	nlm4_rofs = htonl(NLM_ROFS);
+	nlm4_stale_fh = htonl(NLM_STALE_FH);
+	nlm4_fbig = htonl(NLM_FBIG);
+	nlm4_failed = htonl(NLM_FAILED);
+
 	inited = 1;
 
 	nlm_register_stats();
@@ -559,7 +572,7 @@
       (kxdrproc_t) nlmclt_encode_##argtype,			\
       (kxdrproc_t) nlmclt_decode_##restype,			\
       MAX(NLM_##argtype##_sz, NLM_##restype##_sz) << 2,		\
-      0								\
+      0                                                         \
     }
 
 static struct rpc_procinfo	nlm_procedures[] = {
diff -Naur pre9/linux/fs/lockd/xdr4.c test/linux/fs/lockd/xdr4.c
--- pre9/linux/fs/lockd/xdr4.c	Mon Sep 18 13:58:34 2000
+++ test/linux/fs/lockd/xdr4.c	Mon Sep 18 14:35:50 2000
@@ -1,5 +1,5 @@
 /*
- * linux/fs/lockd/xdr.c
+ * linux/fs/lockd/xdr4.c
  *
  * XDR support for lockd and the lock client.
  *
@@ -18,6 +18,7 @@
 #include <linux/sunrpc/stats.h>
 #include <linux/lockd/lockd.h>
 #include <linux/lockd/sm_inter.h>
+#include <linux/nfs3.h>
 
 #define NLMDBG_FACILITY		NLMDBG_XDR
 #define NLM_MAXSTRLEN		1024
@@ -25,6 +26,10 @@
 
 #define QUADLEN(len)		(((len) + 3) >> 2)
 
+u32      nlm4_granted, nlm4_lck_denied, nlm4_lck_denied_nolocks,
+         nlm4_lck_blocked, nlm4_lck_denied_grace_period, nlm4_deadlock,
+         nlm4_rofs, nlm4_stale_fh, nlm4_fbig, nlm4_failed;
+
 
 typedef struct nlm_args	nlm_args;
 
@@ -170,11 +175,13 @@
 static u32 *
 nlm4_encode_testres(u32 *p, struct nlm_res *resp)
 {
+
+	dprintk("xdr: before encode_testres (p %p resp %p)\n", p, resp);
 	if (!(p = nlm4_encode_cookie(p, &resp->cookie)))
 		return 0;
 	*p++ = resp->status;
 
-	if (resp->status == nlm_lck_denied) {
+	if (resp->status == nlm4_lck_denied) {
 		struct file_lock	*fl = &resp->lock.fl;
 
 		*p++ = (fl->fl_type == F_RDLCK)? xdr_zero : xdr_one;
@@ -189,8 +196,12 @@
 			p = xdr_encode_hyper(p, 0);
 		else
 			p = xdr_encode_hyper(p, fl->fl_end - fl->fl_start + 1);
+		dprintk("xdr: encode_testres (status %d pid %d type %d start %ld end %ld)\n", resp->status, fl->fl_pid, fl->fl_type, fl->fl_start,  fl->fl_end);
+
+
 	}
 
+	dprintk("xdr: after encode_testres (p %p resp %p)\n", p, resp);
 	return p;
 }
 
@@ -540,7 +551,7 @@
       (kxdrproc_t) nlm4clt_encode_##argtype,			\
       (kxdrproc_t) nlm4clt_decode_##restype,			\
       MAX(NLM4_##argtype##_sz, NLM4_##restype##_sz) << 2,	\
-      0								\
+      0                                                         \
     }
 
 static struct rpc_procinfo	nlm4_procedures[] = {
diff -Naur pre9/linux/fs/nfs/inode.c test/linux/fs/nfs/inode.c
--- pre9/linux/fs/nfs/inode.c	Mon Sep 18 13:58:34 2000
+++ test/linux/fs/nfs/inode.c	Mon Sep 18 14:35:50 2000
@@ -177,10 +177,9 @@
 
 	nfs_reqlist_free(server);
 
-#if 0
 	if (!(server->flags & NFS_MOUNT_NONLM))
 		lockd_down();	/* release rpc.lockd */
-#endif
+
 	rpciod_down();		/* release rpciod */
 
 	kfree(server->hostname);
@@ -507,11 +506,10 @@
 	/* We're airborne */
 	unlock_super(sb);
 
-#if 0
 	/* Check whether to start the lockd process */
 	if (!(server->flags & NFS_MOUNT_NONLM))
 		lockd_up();
-#endif
+
 	return sb;
 
 	/* Yargs. It didn't work out. */
diff -Naur pre9/linux/fs/nfs/nfsroot.c test/linux/fs/nfs/nfsroot.c
--- pre9/linux/fs/nfs/nfsroot.c	Mon Sep 18 13:58:34 2000
+++ test/linux/fs/nfs/nfsroot.c	Mon Sep 18 14:35:50 2000
@@ -243,9 +243,12 @@
 	memset(&nfs_data, 0, sizeof(nfs_data));
 	nfs_port          = -1;
 	nfs_data.version  = NFS_MOUNT_VERSION;
-	/* It is ok to have lockd in nfs root since it will be started
-	   later manually in the rc script. */
-	nfs_data.flags    = 0;
+	/*
+	 *  dhiggen: nobody has yet demonstrated a convincing need for NFS root
+	 *  locking, so I am reverting to automatic lockd start and disabling
+	 *  NLM locking on an NFS root.
+	 */
+	nfs_data.flags    = NFS_MOUNT_NONLM;
 	nfs_data.timeo    = 7;
 	nfs_data.retrans  = 3;
 	nfs_data.acregmin = 3;
diff -Naur pre9/linux/fs/nfsd/Makefile test/linux/fs/nfsd/Makefile
--- pre9/linux/fs/nfsd/Makefile	Tue Dec 29 11:42:25 1998
+++ test/linux/fs/nfsd/Makefile	Mon Sep 18 14:35:50 2000
@@ -11,6 +11,9 @@
 O_OBJS   := nfssvc.o nfsctl.o nfsproc.o nfsfh.o vfs.o \
 	    export.o auth.o lockd.o nfscache.o nfsxdr.o \
 	    stats.o
+ifdef CONFIG_NFSD_V3
+  O_OBJS += nfs3proc.o nfs3xdr.o
+endif
 
 M_OBJS   := $(O_TARGET)
 
diff -Naur pre9/linux/fs/nfsd/export.c test/linux/fs/nfsd/export.c
--- pre9/linux/fs/nfsd/export.c	Tue Oct 26 17:53:42 1999
+++ test/linux/fs/nfsd/export.c	Mon Sep 18 14:35:50 2000
@@ -105,20 +105,6 @@
 	return exp;
 }
 
-/*
- * Check whether there are any exports for a device.
- */
-static int
-exp_device_in_use(kdev_t dev)
-{
-	struct svc_client *clp;
-
-	for (clp = clients; clp; clp = clp->cl_next) {
-		if (exp_find(clp, dev))
-			return 1;
-	}
-	return 0;
-}
 
 /*
  * Look up the device of the parent fs.
@@ -168,9 +154,8 @@
 					}
 				} while (NULL != (exp = exp->ex_next));
 		} while (nfsd_parentdev(&xdev));
-		if (xdentry == xdentry->d_parent) {
+		if (IS_ROOT(xdentry))
 			break;
-		}
 	} while ((xdentry = xdentry->d_parent));
 	exp = NULL;
 out:
@@ -204,7 +189,7 @@
 #endif
 						goto out;
 					}
-					if (ndentry == ndentry->d_parent)
+					if (IS_ROOT(ndentry))
 						break;
 				}
 		} while (NULL != (exp = exp->ex_next));
@@ -287,6 +272,12 @@
 		goto finish;
 
 	err = -EINVAL;
+	if (!(inode->i_sb->s_type->fs_flags & FS_REQUIRES_DEV) ||
+	    inode->i_sb->s_op->read_inode == NULL) {
+		dprintk("exp_export: export of invalid fs type.\n");
+		goto finish;
+	}
+
 	if ((parent = exp_child(clp, dev, dentry)) != NULL) {
 		dprintk("exp_export: export not valid (Rule 3).\n");
 		goto finish;
@@ -366,16 +357,6 @@
 				exp->ex_parent = unexp->ex_parent;
 	}
 
-	/*
-	 * Check whether this is the last export for this device,
-	 * and if so flush any cached dentries.
-	 */
-	if (!exp_device_in_use(unexp->ex_dev)) {
-printk("exp_do_unexport: %s last use, flushing cache\n",
-	kdevname(unexp->ex_dev));
-		nfsd_fh_flush(unexp->ex_dev);
-	}
-
 	dentry = unexp->ex_dentry;
 	inode = dentry->d_inode;
 	if (unexp->ex_dev != inode->i_dev || unexp->ex_ino != inode->i_ino)
@@ -628,7 +609,9 @@
 	{ NFSEXP_UIDMAP, {"uidmap", ""}},
 	{ NFSEXP_KERBEROS, { "kerberos", ""}},
 	{ NFSEXP_SUNSECURE, { "sunsecure", ""}},
-	{ NFSEXP_CROSSMNT, {"crossmnt", ""}},
+	{ NFSEXP_CROSSMNT, {"nohide", ""}},
+	{ NFSEXP_NOSUBTREECHECK, {"no_subtree_check", ""}},
+	{ NFSEXP_NOAUTHNLM, {"no_auth_nlm", ""}},
 	{ 0, {"", ""}}
 };
 
diff -Naur pre9/linux/fs/nfsd/lockd.c test/linux/fs/nfsd/lockd.c
--- pre9/linux/fs/nfsd/lockd.c	Mon Aug 31 11:03:38 1998
+++ test/linux/fs/nfsd/lockd.c	Mon Sep 18 14:35:50 2000
@@ -30,11 +30,21 @@
 	fh.fh_handle = *f;
 	fh.fh_export = NULL;
 
-	nfserr = nfsd_open(rqstp, &fh, S_IFREG, 0, filp);
+	nfserr = nfsd_open(rqstp, &fh, S_IFREG, MAY_LOCK, filp);
 	if (!nfserr)
 		dget(filp->f_dentry);
 	fh_put(&fh);
-	return nfserr;
+        /* nlm and nfsd don't share error codes.
+         * we invent: 0 = no error
+         *            1 = stale file handle
+         *            2 = other error
+         */
+        if (nfserr == 0)
+                return 0;
+        else if (nfserr == nfserr_stale)
+                return 1;
+        else return 2;
+
 }
 
 static void
diff -Naur pre9/linux/fs/nfsd/nfs3proc.c test/linux/fs/nfsd/nfs3proc.c
--- pre9/linux/fs/nfsd/nfs3proc.c	Mon Apr 12 10:07:36 1999
+++ test/linux/fs/nfsd/nfs3proc.c	Mon Sep 18 15:03:53 2000
@@ -3,7 +3,7 @@
  *
  * Process version 3 NFS requests.
  *
- * Copyright (C) 1996 Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1996, 1997, 1998 Olaf Kirch <okir@monad.swb.de>
  */
 
 #include <linux/linkage.h>
@@ -18,19 +18,33 @@
 #include <linux/version.h>
 #include <linux/unistd.h>
 #include <linux/malloc.h>
+#include <linux/major.h>
 
 #include <linux/sunrpc/svc.h>
 #include <linux/nfsd/nfsd.h>
 #include <linux/nfsd/cache.h>
 #include <linux/nfsd/xdr3.h>
-
-typedef struct svc_rqst	svc_rqst;
-typedef struct svc_buf	svc_buf;
+#include <linux/nfs3.h>
+#include <linux/ext2_fs.h>
 
 #define NFSDDBG_FACILITY		NFSDDBG_PROC
 
 #define RETURN(st)	{ resp->status = (st); return (st); }
 
+static int	nfs3_ftypes[] = {
+	0,			/* NF3NON */
+	S_IFREG,		/* NF3REG */
+	S_IFDIR,		/* NF3DIR */
+	S_IFBLK,		/* NF3BLK */
+	S_IFCHR,		/* NF3CHR */
+	S_IFLNK,		/* NF3LNK */
+	S_IFSOCK,		/* NF3SOCK */
+	S_IFIFO,		/* NF3FIFO */
+};
+
+/*
+ * Reserve room in the send buffer
+ */
 static void
 svcbuf_reserve(struct svc_buf *buf, u32 **ptr, int *len, int nr)
 {
@@ -38,6 +52,9 @@
 	*len = buf->buflen - buf->len - nr;
 }
 
+/*
+ * NULL call.
+ */
 static int
 nfsd3_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
 {
@@ -46,7 +63,6 @@
 
 /*
  * Get a file's attributes
- * N.B. After this call resp->fh needs an fh_put
  */
 static int
 nfsd3_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle  *argp,
@@ -54,18 +70,17 @@
 {
 	int	nfserr;
 
-	dprintk("nfsd: GETATTR  %x/%ld\n",
+	dprintk("nfsd: GETATTR(3)  %x/%ld\n",
 				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh));
+				(long)SVCFH_INO(&argp->fh));
 
-	resp->fh = argp->fh;
+	fh_copy(&resp->fh, &argp->fh);
 	nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP);
 	RETURN(nfserr);
 }
 
 /*
  * Set a file's attributes
- * N.B. After this call resp->fh needs an fh_put
  */
 static int
 nfsd3_proc_setattr(struct svc_rqst *rqstp, struct nfsd3_sattrargs *argp,
@@ -73,31 +88,30 @@
 {
 	int	nfserr;
 
-	dprintk("nfsd: SETATTR  %x/%ld\n",
+	dprintk("nfsd: SETATTR(3)  %x/%ld\n",
 				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh));
+				(long)SVCFH_INO(&argp->fh));
 
-	resp->fh = argp->fh;
+	fh_copy(&resp->fh, &argp->fh);
 	nfserr = nfsd_setattr(rqstp, &resp->fh, &argp->attrs);
 	RETURN(nfserr);
 }
 
 /*
  * Look up a path name component
- * N.B. After this call _both_ resp->dirfh and resp->fh need an fh_put
  */
 static int
 nfsd3_proc_lookup(struct svc_rqst *rqstp, struct nfsd3_diropargs *argp,
-					  struct nfsd3_lookupres *resp)
+					  struct nfsd3_diropres  *resp)
 {
 	int	nfserr;
 
-	dprintk("nfsd: LOOKUP   %x/%ld %s\n",
+	dprintk("nfsd: LOOKUP(3)   %x/%ld %s\n",
 				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh),
+				(long)SVCFH_INO(&argp->fh),
 				argp->name);
 
-	resp->dirfh = argp->fh;
+	fh_copy(&resp->dirfh, &argp->fh);
 	nfserr = nfsd_lookup(rqstp, &resp->dirfh,
 				    argp->name,
 				    argp->len,
@@ -109,12 +123,20 @@
  * Check file access
  */
 static int
-nfsd3_proc_access(struct svc_rqst *rqstp, struct nfsd_fhandle   *argp,
+nfsd3_proc_access(struct svc_rqst *rqstp, struct nfsd3_accessargs *argp,
 					  struct nfsd3_accessres *resp)
 {
-	/* to be done */
-	resp->fh = argp->fh;
-	return nfserr_notsupp;
+	int	nfserr;
+
+	dprintk("nfsd: ACCESS(3)   %x/%ld 0x%x\n",
+				SVCFH_DEV(&argp->fh),
+				(long)SVCFH_INO(&argp->fh),
+				argp->access);
+
+	fh_copy(&resp->fh, &argp->fh);
+	resp->access = argp->access;
+	nfserr = nfsd_access(rqstp, &resp->fh, &resp->access);
+	RETURN(nfserr);
 }
 
 /*
@@ -127,23 +149,23 @@
 	u32		*path;
 	int		dummy, nfserr;
 
-	dprintk("nfsd: READLINK %x/%ld\n",
+	dprintk("nfsd: READLINK(3) %x/%ld\n",
 				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh));
+				(long)SVCFH_INO(&argp->fh));
 
 	/* Reserve room for status, post_op_attr, and path length */
-	svcbuf_reserve(&rqstp->rq_resbuf, &path, &dummy, 1 + 22 + 1);
+	svcbuf_reserve(&rqstp->rq_resbuf, &path, &dummy,
+				1 + NFS3_POST_OP_ATTR_WORDS + 1);
 
 	/* Read the symlink. */
+	fh_copy(&resp->fh, &argp->fh);
 	resp->len = NFS3_MAXPATHLEN;
-	nfserr = nfsd_readlink(rqstp, &argp->fh, (char *) path, &resp->len);
-	fh_put(&argp->fh);
+	nfserr = nfsd_readlink(rqstp, &resp->fh, (char *) path, &resp->len);
 	RETURN(nfserr);
 }
 
 /*
  * Read a portion of a file.
- * N.B. After this call resp->fh needs an fh_put
  */
 static int
 nfsd3_proc_read(struct svc_rqst *rqstp, struct nfsd3_readargs *argp,
@@ -152,9 +174,9 @@
 	u32 *	buffer;
 	int	nfserr, avail;
 
-	dprintk("nfsd: READ %x/%ld %lu bytes at %lu\n",
+	dprintk("nfsd: READ(3) %x/%ld %lu bytes at %lu\n",
 				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh),
+				(long)SVCFH_INO(&argp->fh),
 				(unsigned long) argp->count,
 				(unsigned long) argp->offset);
 
@@ -162,30 +184,29 @@
 	 * 1 (status) + 22 (post_op_attr) + 1 (count) + 1 (eof)
 	 * + 1 (xdr opaque byte count) = 26
 	 */
-	svcbuf_reserve(&rqstp->rq_resbuf, &buffer, &avail, 26);
-
-	if ((avail << 2) < argp->count) {
-		printk(KERN_NOTICE
-			"oversized read request from %08lx:%d (%d bytes)\n",
-				ntohl(rqstp->rq_addr.sin_addr.s_addr),
-				ntohs(rqstp->rq_addr.sin_port),
-				argp->count);
-		argp->count = avail;
-	}
+	svcbuf_reserve(&rqstp->rq_resbuf, &buffer, &avail,
+			1 + NFS3_POST_OP_ATTR_WORDS + 3);
 
 	resp->count = argp->count;
-	resp->fh    = argp->fh;
+	if ((avail << 2) < resp->count)
+		resp->count = avail << 2;
+
+	fh_copy(&resp->fh, &argp->fh);
 	nfserr = nfsd_read(rqstp, &resp->fh,
 				  argp->offset,
 				  (char *) buffer,
 				  &resp->count);
+	if (nfserr == 0) {
+		struct inode	*inode = resp->fh.fh_dentry->d_inode;
+
+		resp->eof = (argp->offset + resp->count) >= inode->i_size;
+	}
 
 	RETURN(nfserr);
 }
 
 /*
  * Write data to a file
- * N.B. After this call resp->fh needs an fh_put
  */
 static int
 nfsd3_proc_write(struct svc_rqst *rqstp, struct nfsd3_writeargs *argp,
@@ -193,19 +214,21 @@
 {
 	int	nfserr;
 
-	dprintk("nfsd: WRITE    %x/%ld %d bytes at %ld\n",
+	dprintk("nfsd: WRITE(3)    %x/%ld %d bytes at %ld%s\n",
 				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh),
+				(long)SVCFH_INO(&argp->fh),
 				argp->len,
-				(unsigned long) argp->offset);
+				(unsigned long) argp->offset,
+				argp->stable? " stable" : "");
 
-	resp->fh = argp->fh;
+	fh_copy(&resp->fh, &argp->fh);
 	nfserr = nfsd_write(rqstp, &resp->fh,
 				   argp->offset,
 				   argp->data,
 				   argp->len,
 				   argp->stable);
 	resp->committed = argp->stable;
+	resp->count = argp->count;
 	RETURN(nfserr);
 }
 
@@ -213,20 +236,18 @@
  * With NFSv3, CREATE processing is a lot easier than with NFSv2.
  * At least in theory; we'll see how it fares in practice when the
  * first reports about SunOS compatibility problems start to pour in...
- * N.B. After this call _both_ resp->dirfh and resp->fh need an fh_put
  */
 static int
 nfsd3_proc_create(struct svc_rqst *rqstp, struct nfsd3_createargs *argp,
-					  struct nfsd3_createres  *resp)
+					  struct nfsd3_diropres   *resp)
 {
 	svc_fh		*dirfhp, *newfhp = NULL;
 	struct iattr	*attr;
-	int		mode;
 	u32		nfserr;
 
-	dprintk("nfsd: CREATE   %x/%ld %s\n",
+	dprintk("nfsd: CREATE(3)   %x/%ld %s\n",
 				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh),
+				(long)SVCFH_INO(&argp->fh),
 				argp->name);
 
 	dirfhp = fh_copy(&resp->dirfh, &argp->fh);
@@ -243,131 +264,114 @@
 	if (!(attr->ia_valid & ATTR_MODE)) { 
 		attr->ia_valid |= ATTR_MODE;
 		attr->ia_mode = S_IFREG;
+	} else {
+		attr->ia_mode = (attr->ia_mode & ~S_IFMT) | S_IFREG;
 	}
-	mode = attr->ia_mode & ~S_IFMT;
 
 	/* Now create the file and set attributes */
-	nfserr = nfsd_create(rqstp, dirfhp, argp->name, argp->len,
-				attr, S_IFREG, 0, newfhp);
+	nfserr = nfsd_create_v3(rqstp, dirfhp, argp->name, argp->len,
+				attr, newfhp,
+				argp->createmode, argp->verf);
 
 	RETURN(nfserr);
 }
 
-/* N.B. Is nfsd3_attrstat * correct for resp?? table says "void" */
+/*
+ * Make directory. This operation is not idempotent.
+ */
 static int
-nfsd3_proc_remove(struct svc_rqst *rqstp, struct nfsd3_diropargs *argp,
-					  struct nfsd3_attrstat  *resp)
+nfsd3_proc_mkdir(struct svc_rqst *rqstp, struct nfsd3_createargs *argp,
+					 struct nfsd3_diropres   *resp)
 {
 	int	nfserr;
 
-	dprintk("nfsd: REMOVE   %x/%ld %s\n",
+	dprintk("nfsd: MKDIR(3)    %x/%ld %s\n",
 				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh),
+				(long)SVCFH_INO(&argp->fh),
 				argp->name);
 
-	/* Is this correct?? */
-	fh_copy(&resp->fh, &argp->fh);
+	argp->attrs.ia_valid &= ~ATTR_SIZE;
+	fh_copy(&resp->dirfh, &argp->fh);
+	fh_init(&resp->fh);
+	nfserr = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len,
+				    &argp->attrs, S_IFDIR, 0, &resp->fh);
 
-	/* Unlink. -S_IFDIR means file must not be a directory */
-	nfserr = nfsd_unlink(rqstp, &resp->fh, -S_IFDIR, argp->name, argp->len);
-	/* 
-	 * N.B. Should be an fh_put here ... nfsd3_proc_rmdir has one,
-	 * or else as an xdr release function
-	 */
-	fh_put(&resp->fh);
 	RETURN(nfserr);
 }
 
 static int
-nfsd3_proc_rename(struct svc_rqst *rqstp, struct nfsd3_renameargs *argp,
-				  	 void		        *resp)
+nfsd3_proc_symlink(struct svc_rqst *rqstp, struct nfsd3_symlinkargs *argp,
+					   struct nfsd3_diropres    *resp)
 {
 	int	nfserr;
 
-	dprintk("nfsd: RENAME   %x/%ld %s -> %x/%ld %s\n",
+	dprintk("nfsd: SYMLINK(3)  %x/%ld %s -> %s\n",
 				SVCFH_DEV(&argp->ffh),
-				SVCFH_INO(&argp->ffh),
-				argp->fname,
-				SVCFH_DEV(&argp->tfh),
-				SVCFH_INO(&argp->tfh),
-				argp->tname);
+				(long)SVCFH_INO(&argp->ffh),
+				argp->fname, argp->tname);
 
-	nfserr = nfsd_rename(rqstp, &argp->ffh, argp->fname, argp->flen,
-				    &argp->tfh, argp->tname, argp->tlen);
-	fh_put(&argp->ffh);
-	fh_put(&argp->tfh);
+	fh_copy(&resp->dirfh, &argp->ffh);
+	fh_init(&resp->fh);
+	nfserr = nfsd_symlink(rqstp, &resp->dirfh, argp->fname, argp->flen,
+						   argp->tname, argp->tlen,
+						   &resp->fh, &argp->attrs);
 	RETURN(nfserr);
 }
 
+/*
+ * Make socket/fifo/device.
+ */
 static int
-nfsd3_proc_link(struct svc_rqst *rqstp, struct nfsd3_linkargs *argp,
-				void			    *resp)
+nfsd3_proc_mknod(struct svc_rqst *rqstp, struct nfsd3_mknodargs *argp,
+					 struct nfsd3_diropres  *resp)
 {
-	int	nfserr;
+	int	nfserr, type;
+	dev_t	rdev = 0;
 
-	dprintk("nfsd: LINK     %x/%ld -> %x/%ld %s\n",
-				SVCFH_DEV(&argp->ffh),
-				SVCFH_INO(&argp->ffh),
-				SVCFH_DEV(&argp->tfh),
-				SVCFH_INO(&argp->tfh),
-				argp->tname);
-
-	nfserr = nfsd_link(rqstp, &argp->tfh, argp->tname, argp->tlen,
-				  &argp->ffh);
-	fh_put(&argp->ffh);
-	fh_put(&argp->tfh);
-	RETURN(nfserr);
-}
-
-static int
-nfsd3_proc_symlink(struct svc_rqst *rqstp, struct nfsd3_symlinkargs *argp,
-				          void			  *resp)
-{
-	struct svc_fh	newfh;
-	int		nfserr;
+	dprintk("nfsd: MKNOD(3)    %x/%ld %s\n",
+				SVCFH_DEV(&argp->fh),
+				(long)SVCFH_INO(&argp->fh),
+				argp->name);
 
-	dprintk("nfsd: SYMLINK  %x/%ld %s -> %s\n",
-				SVCFH_DEV(&argp->ffh),
-				SVCFH_INO(&argp->ffh),
-				argp->fname, argp->tname);
+	fh_copy(&resp->dirfh, &argp->fh);
+	fh_init(&resp->fh);
 
-	memset(&newfh, 0, sizeof(newfh));
+	if (argp->ftype == 0 || argp->ftype >= NF3BAD)
+		return nfserr_inval;
+	if (argp->ftype == NF3CHR || argp->ftype == NF3BLK) {
+		if ((argp->ftype == NF3CHR && argp->major >= MAX_CHRDEV)
+		    || (argp->ftype == NF3BLK && argp->major >= MAX_BLKDEV)
+		    || argp->minor > 0xFF)
+			return nfserr_inval;
+		rdev = ((argp->major) << 8) | (argp->minor);
+	} else
+		if (argp->ftype != NF3SOCK && argp->ftype != NF3FIFO)
+			return nfserr_inval;
 
-	/*
-	 * Create the link, look up new file and set attrs.
-	 */
-	nfserr = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen,
-						 argp->tname, argp->tlen,
-						 &newfh);
-	if (!nfserr) {
-		argp->attrs.ia_valid &= ~ATTR_SIZE;
-		nfserr = nfsd_setattr(rqstp, &newfh, &argp->attrs);
-	}
+	type = nfs3_ftypes[argp->ftype];
+	nfserr = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len,
+				    &argp->attrs, type, rdev, &resp->fh);
 
-	fh_put(&argp->ffh);
-	fh_put(&newfh);
 	RETURN(nfserr);
 }
 
 /*
- * Make directory. This operation is not idempotent.
- * N.B. After this call resp->fh needs an fh_put
+ * Remove file/fifo/socket etc.
  */
 static int
-nfsd3_proc_mkdir(struct svc_rqst *rqstp, struct nfsd3_createargs *argp,
-					struct nfsd3_diropres   *resp)
+nfsd3_proc_remove(struct svc_rqst *rqstp, struct nfsd3_diropargs *argp,
+					  struct nfsd3_attrstat  *resp)
 {
 	int	nfserr;
 
-	dprintk("nfsd: MKDIR    %x/%ld %s\n",
+	dprintk("nfsd: REMOVE(3)   %x/%ld %s\n",
 				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh),
+				(long)SVCFH_INO(&argp->fh),
 				argp->name);
 
-	argp->attrs.ia_valid &= ~ATTR_SIZE;
-	nfserr = nfsd_create(rqstp, &argp->fh, argp->name, argp->len,
-				    &argp->attrs, S_IFDIR, 0, &resp->fh);
-	fh_put(&argp->fh);
+	/* Unlink. -S_IFDIR means file must not be a directory */
+	fh_copy(&resp->fh, &argp->fh);
+	nfserr = nfsd_unlink(rqstp, &resp->fh, -S_IFDIR, argp->name, argp->len);
 	RETURN(nfserr);
 }
 
@@ -376,17 +380,58 @@
  */
 static int
 nfsd3_proc_rmdir(struct svc_rqst *rqstp, struct nfsd3_diropargs *argp,
-				 	void		      *resp)
+					 struct nfsd3_attrstat  *resp)
 {
 	int	nfserr;
 
-	dprintk("nfsd: RMDIR    %x/%ld %s\n",
+	dprintk("nfsd: RMDIR(3)    %x/%ld %s\n",
 				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh),
+				(long)SVCFH_INO(&argp->fh),
 				argp->name);
 
-	nfserr = nfsd_unlink(rqstp, &argp->fh, S_IFDIR, argp->name, argp->len);
-	fh_put(&argp->fh);
+	fh_copy(&resp->fh, &argp->fh);
+	nfserr = nfsd_unlink(rqstp, &resp->fh, S_IFDIR, argp->name, argp->len);
+	RETURN(nfserr);
+}
+
+static int
+nfsd3_proc_rename(struct svc_rqst *rqstp, struct nfsd3_renameargs *argp,
+					  struct nfsd3_renameres  *resp)
+{
+	int	nfserr;
+
+	dprintk("nfsd: RENAME(3)   %x/%ld %s -> %x/%ld %s\n",
+				SVCFH_DEV(&argp->ffh),
+				(long)SVCFH_INO(&argp->ffh),
+				argp->fname,
+				SVCFH_DEV(&argp->tfh),
+				(long)SVCFH_INO(&argp->tfh),
+				argp->tname);
+
+	fh_copy(&resp->ffh, &argp->ffh);
+	fh_copy(&resp->tfh, &argp->tfh);
+	nfserr = nfsd_rename(rqstp, &resp->ffh, argp->fname, argp->flen,
+				    &resp->tfh, argp->tname, argp->tlen);
+	RETURN(nfserr);
+}
+
+static int
+nfsd3_proc_link(struct svc_rqst *rqstp, struct nfsd3_linkargs *argp,
+					struct nfsd3_linkres  *resp)
+{
+	int	nfserr;
+
+	dprintk("nfsd: LINK(3)     %x/%ld -> %x/%ld %s\n",
+				SVCFH_DEV(&argp->ffh),
+				(long)SVCFH_INO(&argp->ffh),
+				SVCFH_DEV(&argp->tfh),
+				(long)SVCFH_INO(&argp->tfh),
+				argp->tname);
+
+	fh_copy(&resp->fh,  &argp->ffh);
+	fh_copy(&resp->tfh, &argp->tfh);
+	nfserr = nfsd_link(rqstp, &resp->tfh, argp->tname, argp->tlen,
+				  &resp->fh);
 	RETURN(nfserr);
 }
 
@@ -395,46 +440,85 @@
  */
 static int
 nfsd3_proc_readdir(struct svc_rqst *rqstp, struct nfsd3_readdirargs *argp,
-					  struct nfsd3_readdirres  *resp)
+					   struct nfsd3_readdirres  *resp)
+{
+	u32 *		buffer;
+	int		nfserr, count;
+	unsigned int	want;
+
+	dprintk("nfsd: READDIR(3)  %x/%ld %d bytes at %d\n",
+				SVCFH_DEV(&argp->fh),
+				(long)SVCFH_INO(&argp->fh),
+				argp->count, (u32) argp->cookie);
+
+	/* Reserve buffer space for status, attributes and verifier */
+	svcbuf_reserve(&rqstp->rq_resbuf, &buffer, &count,
+				1 + NFS3_POST_OP_ATTR_WORDS + 2);
+
+	/* Make sure we've room for the NULL ptr & eof flag, and shrink to
+	 * client read size */
+	if ((count -= 2) > (want = (argp->count >> 2) - 2))
+		count = want;
+
+	/* Read directory and encode entries on the fly */
+	fh_copy(&resp->fh, &argp->fh);
+	nfserr = nfsd_readdir(rqstp, &resp->fh, (loff_t) argp->cookie, 
+					nfs3svc_encode_entry,
+					buffer, &count, argp->verf);
+	memcpy(resp->verf, argp->verf, 8);
+	resp->count = count;
+
+	RETURN(nfserr);
+}
+
+/*
+ * Read a portion of a directory, including file handles and attrs.
+ * For now, we choose to ignore the dircount parameter.
+ */
+static int
+nfsd3_proc_readdirplus(struct svc_rqst *rqstp, struct nfsd3_readdirargs *argp,
+					       struct nfsd3_readdirres  *resp)
 {
 	u32 *	buffer;
-	int	nfserr, count;
+	int	nfserr, count, want;
 
-	dprintk("nfsd: READDIR  %x/%ld %d bytes at %d\n",
+	dprintk("nfsd: READDIR+(3) %x/%ld %d bytes at %d\n",
 				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh),
-				argp->count, argp->cookie);
+				(long)SVCFH_INO(&argp->fh),
+				argp->count, (u32) argp->cookie);
 
-	/* Reserve buffer space for status */
-	svcbuf_reserve(&rqstp->rq_resbuf, &buffer, &count, 1);
+	/* Reserve buffer space for status, attributes and verifier */
+	svcbuf_reserve(&rqstp->rq_resbuf, &buffer, &count,
+				1 + NFS3_POST_OP_ATTR_WORDS + 2);
 
 	/* Make sure we've room for the NULL ptr & eof flag, and shrink to
 	 * client read size */
-	if ((count -= 8) > argp->count)
-		count = argp->count;
+	if ((count -= 2) > (want = argp->count >> 2))
+		count = want;
 
 	/* Read directory and encode entries on the fly */
-	nfserr = nfsd_readdir(rqstp, &argp->fh, (loff_t) argp->cookie, 
-					nfssvc_encode_entry,
-					buffer, &count);
+	fh_copy(&resp->fh, &argp->fh);
+	nfserr = nfsd_readdir(rqstp, &resp->fh, (loff_t) argp->cookie, 
+					nfs3svc_encode_entry_plus,
+					buffer, &count, argp->verf);
+	memcpy(resp->verf, argp->verf, 8);
 	resp->count = count;
 
-	fh_put(&argp->fh);
 	RETURN(nfserr);
 }
 
 /*
- * Get file system info
+ * Get file system stats
  */
 static int
-nfsd3_proc_statfs(struct svc_rqst * rqstp, struct nfsd_fhandle   *argp,
-					  struct nfsd3_statfsres *resp)
+nfsd3_proc_fsstat(struct svc_rqst * rqstp, struct nfsd_fhandle    *argp,
+					   struct nfsd3_fsstatres *resp)
 {
 	int	nfserr;
 
-	dprintk("nfsd: STATFS   %x/%ld\n",
+	dprintk("nfsd: FSSTAT(3)   %x/%ld\n",
 				SVCFH_DEV(&argp->fh),
-				SVCFH_INO(&argp->fh));
+				(long)SVCFH_INO(&argp->fh));
 
 	nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats);
 	fh_put(&argp->fh);
@@ -442,104 +526,165 @@
 }
 
 /*
- * NFSv2 Server procedures.
- * Only the results of non-idempotent operations are cached.
+ * Get file system info
  */
-#define nfsd3_proc_none		NULL
-#define nfssvc_encode_void	NULL
-#define nfssvc_decode_void	NULL
-#define nfssvc_release_void	NULL
-struct nfsd3_void { int dummy; };
+static int
+nfsd3_proc_fsinfo(struct svc_rqst * rqstp, struct nfsd_fhandle    *argp,
+					   struct nfsd3_fsinfores *resp)
+{
+	int	nfserr;
 
-#define PROC(name, argt, rest, relt, cache)	\
- { (svc_procfunc) nfsd3_proc_##name,	\
-   (kxdrproc_t) nfssvc_decode_##argt,	\
-   (kxdrproc_t) nfssvc_encode_##rest,	\
-   (kxdrproc_t) nfssvc_release_##relt,	\
-   sizeof(struct nfsd3_##argt),		\
-   sizeof(struct nfsd3_##rest),		\
-   0,					\
-   cache				\
- }
-struct svc_procedure		nfsd3_procedures2[18] = {
-  PROC(null,	 void,		void,		void,	 RC_NOCACHE),
-  PROC(getattr,	 fhandle,	attrstat,	fhandle, RC_NOCACHE),
-  PROC(setattr,  sattrargs,	attrstat,	fhandle, RC_REPLBUFF),
-  PROC(none,	 void,		void,		void,	 RC_NOCACHE),
-  PROC(lookup,	 diropargs,	diropres,	fhandle2,RC_NOCACHE),
-  PROC(readlink, fhandle,	readlinkres,	void,	 RC_NOCACHE),
-  PROC(read,	 readargs,	readres,	fhandle, RC_NOCACHE),
-  PROC(none,	 void,		void,		void,	 RC_NOCACHE),
-  PROC(write,	 writeargs,	attrstat,	fhandle, RC_REPLBUFF),
-  PROC(create,	 createargs,	diropres,	fhandle2,RC_REPLBUFF),
-  PROC(remove,	 diropargs,	void,/* ??*/	void,	 RC_REPLSTAT),
-  PROC(rename,	 renameargs,	void,		void,	 RC_REPLSTAT),
-  PROC(link,	 linkargs,	void,		void,	 RC_REPLSTAT),
-  PROC(symlink,	 symlinkargs,	void,		void,	 RC_REPLSTAT),
-  PROC(mkdir,	 createargs,	diropres,	fhandle, RC_REPLBUFF),
-  PROC(rmdir,	 diropargs,	void,		void,	 RC_REPLSTAT),
-  PROC(readdir,	 readdirargs,	readdirres,	void,	 RC_REPLSTAT),
-  PROC(statfs,	 fhandle,	statfsres,	void,	 RC_NOCACHE),
-};
+	dprintk("nfsd: FSINFO(3)   %x/%ld\n",
+				SVCFH_DEV(&argp->fh),
+				(long)SVCFH_INO(&argp->fh));
 
+	resp->f_rtmax  = NFSSVC_MAXBLKSIZE;
+	resp->f_rtpref = NFSSVC_MAXBLKSIZE;
+	resp->f_rtmult = PAGE_SIZE;
+	resp->f_wtmax  = NFSSVC_MAXBLKSIZE;
+	resp->f_wtpref = NFSSVC_MAXBLKSIZE;
+	resp->f_wtmult = PAGE_SIZE;
+	resp->f_dtpref = PAGE_SIZE;
+	resp->f_maxfilesize = LONG_MAX;
+	resp->f_properties = NFS3_FSF_DEFAULT;
+
+	nfserr = fh_verify(rqstp, &argp->fh, 0, MAY_NOP);
+
+	/* Check special features of the file system. May request
+	 * different read/write sizes for file systems known to have
+	 * problems with large blocks */
+	if (nfserr == 0) {
+		struct super_block *sb = argp->fh.fh_dentry->d_inode->i_sb;
+
+		/* Note that we don't care for remote fs's here */
+		if (sb->s_magic == 0x4d44 /* MSDOS_SUPER_MAGIC */) {
+			resp->f_properties = NFS3_FSF_BILLYBOY;
+		}
+	}
+
+	fh_put(&argp->fh);
+	RETURN(nfserr);
+}
 
 /*
- * Map errnos to NFS errnos.
+ * Get pathconf info for the specified file
  */
-int
-nfserrno (int errno)
+static int
+nfsd3_proc_pathconf(struct svc_rqst * rqstp, struct nfsd_fhandle      *argp,
+					     struct nfsd3_pathconfres *resp)
 {
-	static struct {
-		int	nfserr;
-		int	syserr;
-	} nfs_errtbl[] = {
-		{ NFS_OK, 0 },
-		{ NFSERR_PERM, EPERM },
-		{ NFSERR_NOENT, ENOENT },
-		{ NFSERR_IO, EIO },
-		{ NFSERR_NXIO, ENXIO },
-		{ NFSERR_ACCES, EACCES },
-		{ NFSERR_EXIST, EEXIST },
-		{ NFSERR_NODEV, ENODEV },
-		{ NFSERR_NOTDIR, ENOTDIR },
-		{ NFSERR_ISDIR, EISDIR },
-		{ NFSERR_INVAL, EINVAL },
-		{ NFSERR_FBIG, EFBIG },
-		{ NFSERR_NOSPC, ENOSPC },
-		{ NFSERR_ROFS, EROFS },
-		{ NFSERR_NAMETOOLONG, ENAMETOOLONG },
-		{ NFSERR_NOTEMPTY, ENOTEMPTY },
-#ifdef EDQUOT
-		{ NFSERR_DQUOT, EDQUOT },
-#endif
-		{ NFSERR_STALE, ESTALE },
-		{ NFSERR_WFLUSH, EIO },
-		{ -1, EIO }
-	};
-	int	i;
-
-	for (i = 0; nfs_errtbl[i].nfserr != -1; i++) {
-		if (nfs_errtbl[i].syserr == errno)
-			return htonl (nfs_errtbl[i].nfserr);
+	int	nfserr;
+
+	dprintk("nfsd: PATHCONF(3) %x/%ld\n",
+				SVCFH_DEV(&argp->fh),
+				(long)SVCFH_INO(&argp->fh));
+
+	/* Set default pathconf */
+	resp->p_link_max = 255;		/* at least */
+	resp->p_name_max = 255;		/* at least */
+	resp->p_no_trunc = 0;
+	resp->p_chown_restricted = 1;
+	resp->p_case_insensitive = 0;
+	resp->p_case_preserving = 1;
+
+	nfserr = fh_verify(rqstp, &argp->fh, 0, MAY_NOP);
+
+	if (nfserr == 0) {
+		struct super_block *sb = argp->fh.fh_dentry->d_inode->i_sb;
+
+		/* Note that we don't care for remote fs's here */
+		switch (sb->s_magic) {
+		case EXT2_SUPER_MAGIC:
+			resp->p_link_max = EXT2_LINK_MAX;
+			resp->p_name_max = EXT2_NAME_LEN;
+			break;
+		case 0x4d44:	/* MSDOS_SUPER_MAGIC */
+			resp->p_case_insensitive = 1;
+			resp->p_case_preserving  = 0;
+			break;
+		}
 	}
-	printk (KERN_INFO "nfsd: non-standard errno: %d\n", errno);
-	return nfserr_io;
+
+	fh_put(&argp->fh);
+	RETURN(nfserr);
 }
 
-#if 0
-static void
-nfsd3_dump(char *tag, u32 *buf, int len)
+
+/*
+ * Commit a file (range) to stable storage.
+ */
+static int
+nfsd3_proc_commit(struct svc_rqst * rqstp, struct nfsd3_commitargs *argp,
+					   struct nfsd3_commitres  *resp)
 {
-	int	i;
+	int	nfserr;
 
-	printk(KERN_NOTICE
-		"nfsd: %s (%d words)\n", tag, len);
+	dprintk("nfsd: COMMIT(3)   %x/%ld %d@%ld\n",
+				SVCFH_DEV(&argp->fh),
+				(long)SVCFH_INO(&argp->fh),
+				argp->count,
+				(unsigned long) argp->offset);
 
-	for (i = 0; i < len && i < 32; i += 8)
-		printk(KERN_NOTICE
-			" %08lx %08lx %08lx %08lx"
-			" %08lx %08lx %08lx %08lx\n",
-			buf[i],   buf[i+1], buf[i+2], buf[i+3],
-			buf[i+4], buf[i+5], buf[i+6], buf[i+7]);
+	if (argp->offset > NFS_OFFSET_MAX)
+		return nfserr_inval;
+
+	fh_copy(&resp->fh, &argp->fh);
+	nfserr = nfsd_commit(rqstp, &resp->fh, argp->offset, argp->count);
+
+	RETURN(nfserr);
 }
-#endif
+
+
+/*
+ * NFSv3 Server procedures.
+ * Only the results of non-idempotent operations are cached.
+ */
+#define nfs3svc_decode_voidargs		NULL
+#define nfs3svc_release_void		NULL
+#define nfs3svc_decode_fhandleargs	nfs3svc_decode_fhandle
+#define nfs3svc_encode_attrstatres	nfs3svc_encode_attrstat
+#define nfs3svc_encode_wccstatres	nfs3svc_encode_wccstat
+#define nfsd3_mkdirargs			nfsd3_createargs
+#define nfsd3_readdirplusargs		nfsd3_readdirargs
+#define nfsd3_fhandleargs		nfsd_fhandle
+#define nfsd3_fhandleres		nfsd3_attrstat
+#define nfsd3_attrstatres		nfsd3_attrstat
+#define nfsd3_wccstatres		nfsd3_attrstat
+#define nfsd3_createres			nfsd3_diropres
+#define nfsd3_voidres			nfsd3_voidargs
+struct nfsd3_voidargs { int dummy; };
+
+#define PROC(name, argt, rest, relt, cache)	\
+ { (svc_procfunc) nfsd3_proc_##name,		\
+   (kxdrproc_t) nfs3svc_decode_##argt##args,	\
+   (kxdrproc_t) nfs3svc_encode_##rest##res,	\
+   (kxdrproc_t) nfs3svc_release_##relt,		\
+   sizeof(struct nfsd3_##argt##args),		\
+   sizeof(struct nfsd3_##rest##res),		\
+   0,						\
+   cache					\
+ }
+struct svc_procedure		nfsd_procedures3[22] = {
+  PROC(null,	 void,		void,		void,	 RC_NOCACHE),
+  PROC(getattr,	 fhandle,	attrstat,	fhandle, RC_NOCACHE),
+  PROC(setattr,  sattr,		wccstat,	fhandle,  RC_REPLBUFF),
+  PROC(lookup,	 dirop,		dirop,		fhandle2, RC_NOCACHE),
+  PROC(access,	 access,	access,		fhandle,  RC_NOCACHE),
+  PROC(readlink, fhandle,	readlink,	fhandle,  RC_NOCACHE),
+  PROC(read,	 read,		read,		fhandle, RC_NOCACHE),
+  PROC(write,	 write,		write,		fhandle,  RC_REPLBUFF),
+  PROC(create,	 create,	create,		fhandle2, RC_REPLBUFF),
+  PROC(mkdir,	 mkdir,		create,		fhandle2, RC_REPLBUFF),
+  PROC(symlink,	 symlink,	create,		fhandle2, RC_REPLBUFF),
+  PROC(mknod,	 mknod,		create,		fhandle2, RC_REPLBUFF),
+  PROC(remove,	 dirop,		wccstat,	fhandle,  RC_REPLBUFF),
+  PROC(rmdir,	 dirop,		wccstat,	fhandle,  RC_REPLBUFF),
+  PROC(rename,	 rename,	rename,		fhandle2, RC_REPLBUFF),
+  PROC(link,	 link,		link,		fhandle2, RC_REPLBUFF),
+  PROC(readdir,	 readdir,	readdir,	fhandle,  RC_NOCACHE),
+  PROC(readdirplus,readdirplus,	readdir,	fhandle,  RC_NOCACHE),
+  PROC(fsstat,	 fhandle,	fsstat,		void,     RC_NOCACHE),
+  PROC(fsinfo,   fhandle,	fsinfo,		void,     RC_NOCACHE),
+  PROC(pathconf, fhandle,	pathconf,	void,     RC_NOCACHE),
+  PROC(commit,	 commit,	commit,		fhandle,  RC_REPLBUFF)
+};
diff -Naur pre9/linux/fs/nfsd/nfs3xdr.c test/linux/fs/nfsd/nfs3xdr.c
--- pre9/linux/fs/nfsd/nfs3xdr.c	Mon Apr  7 11:35:31 1997
+++ test/linux/fs/nfsd/nfs3xdr.c	Mon Sep 18 14:35:50 2000
@@ -3,7 +3,7 @@
  *
  * XDR support for nfsd/protocol version 3.
  *
- * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
  */
 
 #include <linux/types.h>
@@ -17,16 +17,16 @@
 
 #define NFSDDBG_FACILITY		NFSDDBG_XDR
 
-u32	nfs_ok, nfserr_perm, nfserr_noent, nfserr_io, nfserr_nxio,
-	nfserr_acces, nfserr_exist, nfserr_nodev, nfserr_notdir,
-	nfserr_isdir, nfserr_fbig, nfserr_nospc, nfserr_rofs,
-	nfserr_nametoolong, nfserr_dquot, nfserr_stale;
-
 #ifdef NFSD_OPTIMIZE_SPACE
 # define inline
 #endif
 
 /*
+ * Size of encoded NFS3 file handle, in words
+ */
+#define NFS3_FHANDLE_WORDS	(1 + XDR_QUADLEN(sizeof(struct knfs_fh)))
+
+/*
  * Mapping of S_IF* types to NFS file types
  */
 static u32	nfs3_ftypes[] = {
@@ -37,48 +37,9 @@
 };
 
 /*
- * Initialization of NFS status variables
- */
-void
-nfs3xdr_init(void)
-{
-	static int	inited = 0;
-
-	if (inited)
-		return;
-
-	nfs_ok = htonl(NFS_OK);
-	nfserr_perm = htonl(NFSERR_PERM);
-	nfserr_noent = htonl(NFSERR_NOENT);
-	nfserr_io = htonl(NFSERR_IO);
-	nfserr_nxio = htonl(NFSERR_NXIO);
-	nfserr_acces = htonl(NFSERR_ACCES);
-	nfserr_exist = htonl(NFSERR_EXIST);
-	nfserr_nodev = htonl(NFSERR_NODEV);
-	nfserr_notdir = htonl(NFSERR_NOTDIR);
-	nfserr_isdir = htonl(NFSERR_ISDIR);
-	nfserr_fbig = htonl(NFSERR_FBIG);
-	nfserr_nospc = htonl(NFSERR_NOSPC);
-	nfserr_rofs = htonl(NFSERR_ROFS);
-	nfserr_nametoolong = htonl(NFSERR_NAMETOOLONG);
-	nfserr_dquot = htonl(NFSERR_DQUOT);
-	nfserr_stale = htonl(NFSERR_STALE);
-
-	inited = 1;
-}
-
-/*
  * XDR functions for basic NFS types
  */
 static inline u32 *
-enc64(u32 *p, u64 val)
-{
-	*p++ = (val >> 32);
-	*p++ = (val & 0xffffffff);
-	return p;
-}
-
-static inline u32 *
 dec64(u32 *p, u64 *valp)
 {
 	*valp  = ((u64) ntohl(*p++)) << 32;
@@ -103,13 +64,10 @@
 static inline u32 *
 decode_fh(u32 *p, struct svc_fh *fhp)
 {
-	if (*p++ != sizeof(struct knfs_fh))
+	if (ntohl(*p++) != sizeof(struct knfs_fh))
 		return NULL;
 
 	memcpy(&fhp->fh_handle, p, sizeof(struct knfs_fh));
-	fhp->fh_inode  = NULL;
-	fhp->fh_export = NULL;
-
 	return p + (sizeof(struct knfs_fh) >> 2);
 }
 
@@ -179,27 +137,35 @@
 		iap->ia_gid = ntohl(*p++);
 	}
 	if (*p++) {
+		u64	newsize;
+
 		iap->ia_valid |= ATTR_SIZE;
-		iap->ia_size = ntohl(*p++);
+		p = dec64(p, &newsize);
+		if (newsize <= NFS_OFFSET_MAX)
+			iap->ia_size = (u32) newsize;
+		else
+			iap->ia_size = ~(size_t) 0;
 	}
-	if ((tmp = *p++) == 1) {
+	if ((tmp = ntohl(*p++)) == 1) {	/* set to server time */
 		iap->ia_valid |= ATTR_ATIME;
-	} else if (tmp == 2) {
+	} else if (tmp == 2) {		/* set to client time */
 		iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
 		iap->ia_atime = ntohl(*p++), p++;
 	}
-	if ((tmp = *p++) != 0) {
-		iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
-	} else if (tmp == 2) {
+	if ((tmp = ntohl(*p++)) == 1) {	/* set to server time */
 		iap->ia_valid |= ATTR_MTIME;
+	} else if (tmp == 2) {		/* set to client time */
+		iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
 		iap->ia_mtime = ntohl(*p++), p++;
 	}
 	return p;
 }
 
 static inline u32 *
-encode_fattr3(struct svc_rqst *rqstp, u32 *p, struct inode *inode)
+encode_fattr3(struct svc_rqst *rqstp, u32 *p, struct dentry *dentry)
 {
+	struct inode	*inode = dentry->d_inode;
+
 	if (!inode) {
 		printk("nfsd: NULL inode in %s:%d", __FILE__, __LINE__);
 		return NULL;
@@ -215,7 +181,16 @@
 	} else {
 		p = enc64(p, (u64) inode->i_size);
 	}
-	p = enc64(p, inode->i_blksize * inode->i_blocks);
+	/*
+	 *  For the 'used' member, we take i_blocks if set; assuming 512-byte
+	 *  units.  Some FSs don't set this, so all we can do then is
+	 *  use the size.
+	 */
+	if (inode->i_blocks) {
+		p = enc64(p,  ((u64)inode->i_blocks)<<9 );
+	} else {
+		p = enc64(p, (u64) inode->i_size);
+	}
 	*p++ = htonl((u32) MAJOR(inode->i_rdev));
 	*p++ = htonl((u32) MINOR(inode->i_rdev));
 	p = enc64(p, (u64) inode->i_dev);
@@ -227,19 +202,54 @@
 	return p;
 }
 
+static inline u32 *
+encode_saved_post_attr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
+{
+	struct inode	*inode = fhp->fh_dentry->d_inode;
+
+	/* Attributes to follow */
+	*p++ = xdr_one;
+
+	*p++ = htonl(nfs3_ftypes[(fhp->fh_post_mode & S_IFMT) >> 12]);
+	*p++ = htonl((u32) fhp->fh_post_mode);
+	*p++ = htonl((u32) fhp->fh_post_nlink);
+	*p++ = htonl((u32) nfsd_ruid(rqstp, fhp->fh_post_uid));
+	*p++ = htonl((u32) nfsd_rgid(rqstp, fhp->fh_post_gid));
+	if (S_ISLNK(fhp->fh_post_mode) && fhp->fh_post_size > NFS3_MAXPATHLEN) {
+		p = enc64(p, (u64) NFS3_MAXPATHLEN);
+	} else {
+		p = enc64(p, (u64) fhp->fh_post_size);
+	}
+	if (fhp->fh_post_blocks) {
+		p = enc64(p, ((u64)fhp->fh_post_blocks)<<9);
+	} else {
+		p = enc64(p, (u64) fhp->fh_post_size);
+	}
+	*p++ = htonl((u32) MAJOR(fhp->fh_post_rdev));
+	*p++ = htonl((u32) MINOR(fhp->fh_post_rdev));
+	p = enc64(p, (u64) inode->i_dev);
+	p = enc64(p, (u64) inode->i_ino);
+	p = encode_time3(p, fhp->fh_post_atime);
+	p = encode_time3(p, fhp->fh_post_mtime);
+	p = encode_time3(p, fhp->fh_post_ctime);
+
+	return p;
+}
+
 /*
  * Encode post-operation attributes.
  * The inode may be NULL if the call failed because of a stale file
  * handle. In this case, no attributes are returned.
  */
 static u32 *
-encode_post_op_attr(struct svc_rqst *rqstp, u32 *p, struct inode *inode)
+encode_post_op_attr(struct svc_rqst *rqstp, u32 *p, struct dentry *dentry)
 {
-	if (inode == NULL) {
-		*p++ = xdr_zero;
-		return p;
+	if (dentry && dentry->d_inode != NULL) {
+		*p++ = xdr_one;		/* attributes follow */
+		return encode_fattr3(rqstp, p, dentry);
 	}
-	return encode_fattr3(rqstp, p, inode);
+	*p++ = xdr_zero;
+	return p;
 }
 
 /*
@@ -248,17 +258,22 @@
 static u32 *
 encode_wcc_data(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
 {
-	struct inode	*inode = fhp->fh_inode;
+	struct dentry	*dentry = fhp->fh_dentry;
 
-	if (fhp->fh_post_version == inode->i_version) {
-		*p++ = xdr_one;
-		p = enc64(p, (u64) fhp->fh_pre_size);
-		p = encode_time3(p, fhp->fh_pre_mtime);
-		p = encode_time3(p, fhp->fh_pre_ctime);
-	} else {
-		*p++ = xdr_zero;
+	if (dentry && dentry->d_inode && fhp->fh_post_saved) {
+		if (fhp->fh_pre_saved) {
+			*p++ = xdr_one;
+			p = enc64(p, (u64) fhp->fh_pre_size);
+			p = encode_time3(p, fhp->fh_pre_mtime);
+			p = encode_time3(p, fhp->fh_pre_ctime);
+		} else {
+			*p++ = xdr_zero;
+		}
+		return encode_saved_post_attr(rqstp, p, fhp);
 	}
-	return encode_post_op_attr(rqstp, p, inode);
+	/* no pre- or post-attrs */
+	*p++ = xdr_zero;
+	return encode_post_op_attr(rqstp, p, dentry);
 }
 
 /*
@@ -299,10 +314,12 @@
 					struct nfsd3_sattrargs *args)
 {
 	if (!(p = decode_fh(p, &args->fh))
-	 || !(p = decode_sattr3(p, &args->attrs))
-	 || (*p++ && !(p = decode_time3(p, &args->guardtime))))
+	 || !(p = decode_sattr3(p, &args->attrs)))
 		return 0;
 
+	if ((args->check_guard = ntohl(*p++)) != 0)
+		p = decode_time3(p, &args->guardtime);
+
 	return xdr_argsize_check(rqstp, p);
 }
 
@@ -333,10 +350,10 @@
 					struct nfsd3_readargs *args)
 {
 	if (!(p = decode_fh(p, &args->fh))
-	 || !(p = dec64(p, &args->offset))
-	 || !(p = dec64(p, &args->count)))
+	 || !(p = dec64(p, &args->offset)))
 		return 0;
 
+	args->count = ntohl(*p++);
 	return xdr_argsize_check(rqstp, p);
 }
 
@@ -345,14 +362,14 @@
 					struct nfsd3_writeargs *args)
 {
 	if (!(p = decode_fh(p, &args->fh))
-	 || !(p = dec64(p, &args->offset))
-	 || !(p = dec64(p, &args->count)))
+	 || !(p = dec64(p, &args->offset)))
 		return 0;
 
+	args->count = ntohl(*p++);
 	args->stable = ntohl(*p++);
 	args->len = ntohl(*p++);
 	args->data = (char *) p;
-	p += (args->len + 3) >> 2;
+	p += XDR_QUADLEN(args->len);
 
 	return xdr_argsize_check(rqstp, p);
 }
@@ -366,11 +383,12 @@
 		return 0;
 
 	switch (args->createmode = ntohl(*p++)) {
-	case 0: case 1:
+	case NFS3_CREATE_UNCHECKED:
+	case NFS3_CREATE_GUARDED:
 		if (!(p = decode_sattr3(p, &args->attrs)))
 			return 0;
 		break;
-	case 2:
+	case NFS3_CREATE_EXCLUSIVE:
 		args->verf = p;
 		p += 2;
 		break;
@@ -460,8 +478,9 @@
 {
 	if (!(p = decode_fh(p, &args->fh)))
 		return 0;
-	args->cookie = ntohl(*p++);
+	p = dec64(p, &args->cookie);
 	args->verf   = p; p += 2;
+	args->dircount = ~0;
 	args->count  = ntohl(*p++);
 
 	return xdr_argsize_check(rqstp, p);
@@ -473,7 +492,7 @@
 {
 	if (!(p = decode_fh(p, &args->fh)))
 		return 0;
-	args->cookie   = ntohl(*p++);
+	p = dec64(p, &args->cookie);
 	args->verf     = p; p += 2;
 	args->dircount = ntohl(*p++);
 	args->count    = ntohl(*p++);
@@ -485,9 +504,9 @@
 nfs3svc_decode_commitargs(struct svc_rqst *rqstp, u32 *p,
 					struct nfsd3_commitargs *args)
 {
-	if (!(p = decode_fh(p, &args->fh))
-	 || !(p = dec64(p, &args->offset)))
+	if (!(p = decode_fh(p, &args->fh)))
 		return 0;
+	p = dec64(p, &args->offset);
 	args->count = ntohl(*p++);
 
 	return xdr_argsize_check(rqstp, p);
@@ -496,12 +515,23 @@
 /*
  * XDR encode functions
  */
+/*
+ * There must be an encoding function for void results so svc_process
+ * will work properly.
+ */
+int
+nfs3svc_encode_voidres(struct svc_rqst *rqstp, u32 *p, void *dummy)
+{
+	return xdr_ressize_check(rqstp, p);
+}
+
 /* GETATTR */
 int
 nfs3svc_encode_attrstat(struct svc_rqst *rqstp, u32 *p,
 					struct nfsd3_attrstat *resp)
 {
-	if (!(p = encode_fattr3(rqstp, p, resp->fh.fh_inode)))
+	if (resp->status == 0
+	 && !(p = encode_fattr3(rqstp, p, resp->fh.fh_dentry)))
 		return 0;
 	return xdr_ressize_check(rqstp, p);
 }
@@ -518,15 +548,14 @@
 
 /* LOOKUP */
 int
-nfs3svc_encode_lookupres(struct svc_rqst *rqstp, u32 *p,
-					struct nfsd3_lookupres *resp)
+nfs3svc_encode_diropres(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_diropres *resp)
 {
 	if (resp->status == 0) {
 		p = encode_fh(p, &resp->fh);
-		if (!(p = encode_fattr3(rqstp, p, resp->fh.fh_inode)))
-			return 0;
+		p = encode_post_op_attr(rqstp, p, resp->fh.fh_dentry);
 	}
-	p = encode_post_op_attr(rqstp, p, resp->dirfh.fh_inode);
+	p = encode_post_op_attr(rqstp, p, resp->dirfh.fh_dentry);
 	return xdr_ressize_check(rqstp, p);
 }
 
@@ -535,7 +564,7 @@
 nfs3svc_encode_accessres(struct svc_rqst *rqstp, u32 *p,
 					struct nfsd3_accessres *resp)
 {
-	p = encode_post_op_attr(rqstp, p, resp->fh.fh_inode);
+	p = encode_post_op_attr(rqstp, p, resp->fh.fh_dentry);
 	if (resp->status == 0)
 		*p++ = htonl(resp->access);
 	return xdr_ressize_check(rqstp, p);
@@ -546,7 +575,7 @@
 nfs3svc_encode_readlinkres(struct svc_rqst *rqstp, u32 *p,
 					struct nfsd3_readlinkres *resp)
 {
-	p = encode_post_op_attr(rqstp, p, resp->fh.fh_inode);
+	p = encode_post_op_attr(rqstp, p, resp->fh.fh_dentry);
 	if (resp->status == 0) {
 		*p++ = htonl(resp->len);
 		p += XDR_QUADLEN(resp->len);
@@ -559,7 +588,7 @@
 nfs3svc_encode_readres(struct svc_rqst *rqstp, u32 *p,
 					struct nfsd3_readres *resp)
 {
-	p = encode_post_op_attr(rqstp, p, resp->fh.fh_inode);
+	p = encode_post_op_attr(rqstp, p, resp->fh.fh_dentry);
 	if (resp->status == 0) {
 		*p++ = htonl(resp->count);
 		*p++ = htonl(resp->eof);
@@ -587,11 +616,12 @@
 /* CREATE, MKDIR, SYMLINK, MKNOD */
 int
 nfs3svc_encode_createres(struct svc_rqst *rqstp, u32 *p,
-					struct nfsd3_createres *resp)
+					struct nfsd3_diropres *resp)
 {
 	if (resp->status == 0) {
+		*p++ = xdr_one;
 		p = encode_fh(p, &resp->fh);
-		p = encode_post_op_attr(rqstp, p, resp->fh.fh_inode);
+		p = encode_post_op_attr(rqstp, p, resp->fh.fh_dentry);
 	}
 	p = encode_wcc_data(rqstp, p, &resp->dirfh);
 	return xdr_ressize_check(rqstp, p);
@@ -612,7 +642,7 @@
 nfs3svc_encode_linkres(struct svc_rqst *rqstp, u32 *p,
 					struct nfsd3_linkres *resp)
 {
-	p = encode_post_op_attr(rqstp, p, resp->fh.fh_inode);
+	p = encode_post_op_attr(rqstp, p, resp->fh.fh_dentry);
 	p = encode_wcc_data(rqstp, p, &resp->tfh);
 	return xdr_ressize_check(rqstp, p);
 }
@@ -622,73 +652,116 @@
 nfs3svc_encode_readdirres(struct svc_rqst *rqstp, u32 *p,
 					struct nfsd3_readdirres *resp)
 {
-	p = encode_post_op_attr(rqstp, p, resp->fh.fh_inode);
+	p = encode_post_op_attr(rqstp, p, resp->fh.fh_dentry);
 	if (resp->status == 0) {
 		/* stupid readdir cookie */
-		*p++ = ntohl(resp->fh.fh_inode->i_mtime);
-		*p++ = xdr_zero;
-		p = resp->list_end;
+		memcpy(p, resp->verf, 8); p += 2;
+		p += XDR_QUADLEN(resp->count);
 	}
 
 	return xdr_ressize_check(rqstp, p);
 }
 
-#define NFS3_ENTRYPLUS_BAGGAGE	((1 + 20 + 1 + NFS3_FHSIZE) << 2)
-int
-nfs3svc_encode_entry(struct readdir_cd *cd, const char *name,
-				int namlen, unsigned long offset, ino_t ino)
+/*
+ * Encode a directory entry. This one works for both normal readdir
+ * and readdirplus.
+ * The normal readdir reply requires 2 (fileid) + 1 (stringlen)
+ * + string + 2 (cookie) + 1 (next) words, i.e. 6 + strlen.
+ * 
+ * The readdirplus baggage is 1+21 words for post_op_attr, plus the
+ * file handle.
+ */
+
+#define NFS3_ENTRY_BAGGAGE	(2 + 1 + 2 + 1)
+#define NFS3_ENTRYPLUS_BAGGAGE	(1 + 21 + 1 + (NFS3_FHSIZE >> 2))
+static int
+encode_entry(struct readdir_cd *cd, const char *name,
+			int namlen, off_t offset, ino_t ino, int plus)
 {
 	u32		*p = cd->buffer;
 	int		buflen, slen, elen;
-	struct svc_fh	fh;
 
-	if (offset > ~((u64) 0))
-		return -EINVAL;
 	if (cd->offset)
-		*cd->offset = htonl(offset);
+		enc64(cd->offset, (u64) offset);
 
-	/* For readdirplus, look up the inode */
-	if (cd->plus && nfsd_lookup(cd->rqstp, cd->dirfh, name, namlen, &fh))
+	/* nfsd_readdir calls us with name == 0 when it wants us to
+	 * set the last offset entry. */
+	if (name == 0)
 		return 0;
 
+	/*
+	dprintk("encode_entry(%.*s @%ld%s)\n",
+		namlen, name, (long) offset, plus? " plus" : "");
+	 */
+
 	/* truncate filename if too long */
 	if (namlen > NFS3_MAXNAMLEN)
 		namlen = NFS3_MAXNAMLEN;
 
 	slen = XDR_QUADLEN(namlen);
-	elen = slen + (cd->plus? NFS3_ENTRYPLUS_BAGGAGE : 0);
-	if ((buflen = cd->buflen - elen - 4) < 0) {
+	elen = slen + NFS3_ENTRY_BAGGAGE
+		+ (plus? NFS3_ENTRYPLUS_BAGGAGE : 0);
+	if ((buflen = cd->buflen - elen) < 0) {
 		cd->eob = 1;
-		if (cd->plus)
-			fh_put(&fh);
 		return -EINVAL;
 	}
-	*p++ = xdr_one;			/* mark entry present */
-	*p++ = xdr_zero;		/* file id (64 bit) */
-	*p++ = htonl((u32) ino);
-	*p++ = htonl((u32) namlen);	/* name length & name */
+	*p++ = xdr_one;				   /* mark entry present */
+	p    = enc64(p, ino);			   /* file id */
+#ifdef XDR_ENCODE_STRING_TAKES_LENGTH
+	p    = xdr_encode_string(p, name, namlen); /* name length & name */
+#else
+	/* just like nfsproc.c */
+	*p++ = htonl((u32) namlen);
+	p[slen - 1] = 0;		/* don't leak kernel data */
 	memcpy(p, name, namlen);
 	p += slen;
+#endif
+
+	cd->offset = p;			/* remember pointer */
+	p = enc64(p, NFS_OFFSET_MAX);	/* offset of next entry */
 
 	/* throw in readdirplus baggage */
-	if (cd->plus) {
-		p = encode_post_op_attr(cd->rqstp, p, fh.fh_inode);
-		p = encode_fh(p, &fh);
-		fh_put(&fh);
-	}
+	if (plus) {
+		struct svc_fh	fh;
 
-	cd->offset = p;			/* remember pointer */
-	p = enc64(p, ~(u64) 0);	/* offset of next entry */
+		fh_init(&fh);
+		/* Disabled for now because of lock-up */
+		if (0 && nfsd_lookup(cd->rqstp, cd->dirfh, name, namlen, &fh) == 0) {
+			p = encode_post_op_attr(cd->rqstp, p, fh.fh_dentry);
+			p = encode_fh(p, &fh);
+			fh_put(&fh);
+		} else {
+			/* Didn't find this entry... weird.
+			 * Proceed without the attrs anf fh anyway.
+			 */
+			*p++ = 0;
+			*p++ = 0;
+		}
+	}
 
 	cd->buflen = buflen;
 	cd->buffer = p;
 	return 0;
 }
 
+int
+nfs3svc_encode_entry(struct readdir_cd *cd, const char *name,
+				int namlen, off_t offset, ino_t ino)
+{
+	return encode_entry(cd, name, namlen, offset, ino, 0);
+}
+
+int
+nfs3svc_encode_entry_plus(struct readdir_cd *cd, const char *name,
+				int namlen, off_t offset, ino_t ino)
+{
+	return encode_entry(cd, name, namlen, offset, ino, 1);
+}
+
 /* FSSTAT */
 int
-nfs3svc_encode_statfsres(struct svc_rqst *rqstp, u32 *p,
-					struct nfsd3_statfsres *resp)
+nfs3svc_encode_fsstatres(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_fsstatres *resp)
 {
 	struct statfs	*s = &resp->stats;
 	u64		bs = s->f_bsize;
@@ -722,9 +795,9 @@
 		*p++ = htonl(resp->f_wtpref);
 		*p++ = htonl(resp->f_wtmult);
 		*p++ = htonl(resp->f_dtpref);
-		*p++ = htonl(resp->f_maxfilesize);
+		p = enc64(p, resp->f_maxfilesize);
+		*p++ = xdr_one;
 		*p++ = xdr_zero;
-		*p++ = htonl(1000000000 / HZ);
 		*p++ = htonl(resp->f_properties);
 	}
 
@@ -741,8 +814,8 @@
 	if (resp->status == 0) {
 		*p++ = htonl(resp->p_link_max);
 		*p++ = htonl(resp->p_name_max);
-		*p++ = xdr_one;	/* always reject long file names */
-		*p++ = xdr_one;	/* chown restricted */
+		*p++ = htonl(resp->p_no_trunc);
+		*p++ = htonl(resp->p_chown_restricted);
 		*p++ = htonl(resp->p_case_insensitive);
 		*p++ = htonl(resp->p_case_preserving);
 	}
@@ -769,7 +842,7 @@
  */
 int
 nfs3svc_release_fhandle(struct svc_rqst *rqstp, u32 *p,
-					struct nfsd_fhandle *resp)
+					struct nfsd3_attrstat *resp)
 {
 	fh_put(&resp->fh);
 	return 1;
@@ -777,7 +850,7 @@
 
 int
 nfs3svc_release_fhandle2(struct svc_rqst *rqstp, u32 *p,
-					struct nfsd3_fhandle2 *resp)
+					struct nfsd3_fhandle_pair *resp)
 {
 	fh_put(&resp->fh1);
 	fh_put(&resp->fh2);
diff -Naur pre9/linux/fs/nfsd/nfscache.c test/linux/fs/nfsd/nfscache.c
--- pre9/linux/fs/nfsd/nfscache.c	Sun Jan 24 21:54:35 1999
+++ test/linux/fs/nfsd/nfscache.c	Mon Sep 18 14:35:50 2000
@@ -143,8 +143,9 @@
 nfsd_cache_lookup(struct svc_rqst *rqstp, int type)
 {
 	struct svc_cacherep	*rh, *rp;
-	struct svc_client	*clp = rqstp->rq_client;
 	u32			xid = rqstp->rq_xid,
+				proto = rqstp->rq_prot,
+				vers = rqstp->rq_vers,
 				proc = rqstp->rq_proc;
 	unsigned long		age;
 
@@ -158,7 +159,9 @@
 	while ((rp = rp->c_hash_next) != rh) {
 		if (rp->c_state != RC_UNUSED &&
 		    xid == rp->c_xid && proc == rp->c_proc &&
-		    exp_checkaddr(clp, rp->c_client)) {
+		    proto == rp->c_prot && vers == rp->c_vers &&
+		    time_before(jiffies, rp->c_timestamp + 120*HZ) &&
+		    memcmp((char*)&rqstp->rq_addr, (char*)&rp->c_addr, rqstp->rq_addrlen)==0) {
 			nfsdstats.rchits++;
 			goto found_entry;
 		}
@@ -195,7 +198,11 @@
 	rp->c_state = RC_INPROG;
 	rp->c_xid = xid;
 	rp->c_proc = proc;
-	rp->c_client = rqstp->rq_addr.sin_addr;
+	memcpy(&rp->c_addr, &rqstp->rq_addr, sizeof(rp->c_addr));
+	rp->c_prot = proto;
+	rp->c_vers = vers;
+	rp->c_timestamp = jiffies;
+
 	hash_refile(rp);
 
 	/* release any buffer */
@@ -210,12 +217,13 @@
 found_entry:
 	/* We found a matching entry which is either in progress or done. */
 	age = jiffies - rp->c_timestamp;
-	rp->c_timestamp = jiffies;
-	lru_put_front(rp);
 
 	/* Request being processed or excessive rexmits */
 	if (rp->c_state == RC_INPROG || age < RC_DELAY)
 		return RC_DROPIT;
+
+	rp->c_timestamp = jiffies;
+	lru_put_front(rp);
 
 	/* From the hall of fame of impractical attacks:
 	 * Is this a user who tries to snoop on the cache? */
diff -Naur pre9/linux/fs/nfsd/nfsctl.c test/linux/fs/nfsd/nfsctl.c
--- pre9/linux/fs/nfsd/nfsctl.c	Wed May  3 17:16:46 2000
+++ test/linux/fs/nfsd/nfsctl.c	Mon Sep 18 14:35:50 2000
@@ -18,7 +18,6 @@
 #include <linux/fcntl.h>
 #include <linux/net.h>
 #include <linux/in.h>
-#include <linux/version.h>
 #include <linux/unistd.h>
 #include <linux/malloc.h>
 #include <linux/proc_fs.h>
@@ -363,7 +362,6 @@
 	do_nfsservctl = NULL;
 	nfsd_export_shutdown();
 	nfsd_cache_shutdown();
-	nfsd_fh_free();
 	remove_proc_entry("fs/nfs/time-diff-margin", NULL);
 	remove_proc_entry("fs/nfs/exports", NULL);
 	remove_proc_entry("fs/nfs", NULL);
diff -Naur pre9/linux/fs/nfsd/nfsfh.c test/linux/fs/nfsd/nfsfh.c
--- pre9/linux/fs/nfsd/nfsfh.c	Wed May  3 17:16:46 2000
+++ test/linux/fs/nfsd/nfsfh.c	Mon Sep 18 14:35:50 2000
@@ -5,6 +5,7 @@
  *
  * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
  * Portions Copyright (C) 1999 G. Allen Morris III <gam3@acm.org>
+ * Extensive rewrite by Neil Brown <neilb@cse.unsw.edu.au> Southern-Spring 1999
  */
 
 #include <linux/sched.h>
@@ -22,334 +23,50 @@
 #define NFSDDBG_FACILITY		NFSDDBG_FH
 #define NFSD_PARANOIA 1
 /* #define NFSD_DEBUG_VERBOSE 1 */
-/* #define NFSD_DEBUG_VERY_VERBOSE 1 */
 
-extern unsigned long max_mapnr;
-
-#define NFSD_FILE_CACHE 0
-#define NFSD_DIR_CACHE  1
-struct fh_entry {
-	struct dentry * dentry;
-	unsigned long reftime;
-	ino_t	ino;
-	kdev_t	dev;
-};
-
-#define NFSD_MAXFH \
-  (((nfsd_nservers + 1) >> 1) * PAGE_SIZE/sizeof(struct fh_entry))
-static struct fh_entry *filetable = NULL;
-static struct fh_entry *dirstable = NULL;
 
 static int nfsd_nr_verified = 0;
 static int nfsd_nr_put = 0;
-static unsigned long nfsd_next_expire = 0;
-
-static int add_to_fhcache(struct dentry *, int);
-struct dentry * lookup_inode(kdev_t, ino_t, ino_t);
-
-static LIST_HEAD(fixup_head);
-static LIST_HEAD(path_inuse);
-static int nfsd_nr_fixups = 0;
-static int nfsd_nr_paths = 0;
-#define NFSD_MAX_PATHS 500
-#define NFSD_MAX_FIXUPS 500
-#define NFSD_MAX_FIXUP_AGE 30*HZ
-
-struct nfsd_fixup {
-	struct list_head lru;
-	unsigned long reftime;
-	ino_t	dirino;
-	ino_t	ino;
-	kdev_t	dev;
-	ino_t	new_dirino;
-};
-
-struct nfsd_path {
-	struct list_head lru;
-	unsigned long reftime;
-	int	users;
-	ino_t	ino;
-	kdev_t	dev;
-	char	name[1];
-};
-
-static struct nfsd_fixup *
-find_cached_lookup(kdev_t dev, ino_t dirino, ino_t ino)
-{
-	struct list_head *tmp = fixup_head.next;
-
-	for (; tmp != &fixup_head; tmp = tmp->next) {
-		struct nfsd_fixup *fp;
-
-		fp = list_entry(tmp, struct nfsd_fixup, lru);
-#ifdef NFSD_DEBUG_VERY_VERBOSE
-printk("fixup %lu %lu, %lu %lu %s %s\n",
-        fp->ino, ino,
-	fp->dirino, dirino,
-	kdevname(fp->dev), kdevname(dev));
-#endif
-		if (fp->ino != ino)
-			continue;
-		if (fp->dirino != dirino)
-			continue;
-		if (fp->dev != dev)
-			continue;
-		fp->reftime = jiffies;	
-		list_del(tmp);
-		list_add(tmp, &fixup_head);
-		return fp;
-	}
-	return NULL;
-}
-
-/*
- * Save the dirino from a rename.
- */
-void
-add_to_rename_cache(ino_t new_dirino,
-                    kdev_t dev, ino_t dirino, ino_t ino)
-{
-	struct nfsd_fixup *fp;
-
-	if (dirino == new_dirino)
-		return;
-
-	fp = find_cached_lookup(dev, 
-				dirino,
-				ino);
-	if (fp) {
-		fp->new_dirino = new_dirino;
-		return;
-	}
-
-	/*
-	 * Add a new entry. The small race here is unimportant:
-	 * if another task adds the same lookup, both entries
-	 * will be consistent.
-	 */
-	fp = kmalloc(sizeof(struct nfsd_fixup), GFP_KERNEL);
-	if (fp) {
-		fp->dirino = dirino;
-		fp->ino = ino;
-		fp->dev = dev;
-		fp->new_dirino = new_dirino;
-		list_add(&fp->lru, &fixup_head);
-		nfsd_nr_fixups++;
-	}
-}
-
-/*
- * Save the dentry pointer from a successful lookup.
- */
-
-static void free_fixup_entry(struct nfsd_fixup *fp)
-{
-	list_del(&fp->lru);
-#ifdef NFSD_DEBUG_VERY_VERBOSE
-printk("free_rename_entry: %lu->%lu %lu/%s\n",
-		fp->dirino,
-		fp->new_dirino,
-		fp->ino,
-		kdevname(fp->dev),
-		(jiffies - fp->reftime));
-#endif
-	kfree(fp);
-	nfsd_nr_fixups--;
-}
-
-/*
- * Copy a dentry's path into the specified buffer.
- */
-static int copy_path(char *buffer, struct dentry *dentry, int namelen)
-{
-	char *p, *b = buffer;
-	int result = 0, totlen = 0, len; 
-
-	while (1) {
-		struct dentry *parent;
-		dentry = dentry->d_covers;
-		parent = dentry->d_parent;
-		len = dentry->d_name.len;
-		p = (char *) dentry->d_name.name + len;
-		totlen += len;
-		if (totlen > namelen)
-			goto out;
-		while (len--)
-			*b++ = *(--p);
-		if (dentry == parent)
-			break;
-		dentry = parent;
-		totlen++;
-		if (totlen > namelen)
-			goto out;
-		*b++ = '/';
-	}
-	*b = 0;
-
-	/*
-	 * Now reverse in place ...
-	 */
-	p = buffer;
-	while (p < b) {
-		char c = *(--b);
-		*b = *p;
-		*p++ = c;
-	} 
-	result = 1;
-out:
-	return result;
-}
-
-/*
- * Add a dentry's path to the path cache.
- */
-static int add_to_path_cache(struct dentry *dentry)
-{
-	struct inode *inode = dentry->d_inode;
-	struct dentry *this;
-	struct nfsd_path *new;
-	int len, result = 0;
-
-#ifdef NFSD_DEBUG_VERBOSE
-printk("add_to_path_cache: caching %s/%s\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
-#endif
-	/*
-	 * Get the length of the full pathname.
-	 */
-restart:
-	len = 0;
-	this = dentry;
-	while (1) {
-		struct dentry *parent;
-		this = this->d_covers;
-		parent = this->d_parent;
-		len += this->d_name.len;
-		if (this == parent)
-			break;
-		this = parent;
-		len++;
-	}
-	/*
-	 * Allocate a structure to hold the path.
-	 */
-	new = kmalloc(sizeof(struct nfsd_path) + len, GFP_KERNEL);
-	if (new) {
-		new->users = 0;	
-		new->reftime = jiffies;	
-		new->ino = inode->i_ino;
-		new->dev = inode->i_dev;
-		result = copy_path(new->name, dentry, len);
-		if (!result)
-			goto retry;
-		list_add(&new->lru, &path_inuse);
-		nfsd_nr_paths++;
-#ifdef NFSD_DEBUG_VERBOSE
-printk("add_to_path_cache: added %s, paths=%d\n", new->name, nfsd_nr_paths);
-#endif
-	}
-	return result;
-
-	/*
-	 * If the dentry's path length changed, just try again.
-	 */
-retry:
-	kfree(new);
-	printk(KERN_DEBUG "add_to_path_cache: path length changed, retrying\n");
-	goto restart;
-}
-
-/*
- * Search for a path entry for the specified (dev, inode).
- */
-static struct nfsd_path *get_path_entry(kdev_t dev, ino_t ino)
-{
-	struct nfsd_path *pe;
-	struct list_head *tmp;
 
-	for (tmp = path_inuse.next; tmp != &path_inuse; tmp = tmp->next) {
-		pe = list_entry(tmp, struct nfsd_path, lru);
-		if (pe->ino != ino)
-			continue;
-		if (pe->dev != dev)
-			continue;
-		list_del(tmp);
-		list_add(tmp, &path_inuse);
-		pe->users++;
-		pe->reftime = jiffies;
-#ifdef NFSD_PARANOIA
-printk("get_path_entry: found %s for %s/%ld\n", pe->name, kdevname(dev), ino);
-#endif
-		return pe;
-	}
-	return NULL;
-}
-
-static void put_path(struct nfsd_path *pe)
-{
-	pe->users--;
-}
-
-static void free_path_entry(struct nfsd_path *pe)
-{
-	if (pe->users)
-		printk(KERN_DEBUG "free_path_entry: %s in use, users=%d\n",
-			pe->name, pe->users);
-	list_del(&pe->lru);
-	kfree(pe);
-	nfsd_nr_paths--;
-}
 
 struct nfsd_getdents_callback {
-	struct nfsd_dirent *dirent;
-	ino_t dirino;		/* parent inode number */
-	int found;		/* dirent inode matched? */
+	struct qstr *name;	/* name that was found. name->name already points to a buffer */
+	unsigned long ino;	/* the inum we are looking for */
+	int found;		/* inode matched? */
 	int sequence;		/* sequence counter */
 };
 
-struct nfsd_dirent {
-	ino_t ino;		/* preset to desired entry */
-	int len;
-	char name[256];
-};
-
 /*
- * A rather strange filldir function to capture the inode number
- * for the second entry (the parent inode) and the name matching
- * the specified inode number.
+ * A rather strange filldir function to capture
+ * the name matching the specified inode number.
  */
-static int filldir_one(void * __buf, const char * name, int len, 
+static int filldir_one(void * __buf, const char * name, int len,
 			off_t pos, ino_t ino)
 {
 	struct nfsd_getdents_callback *buf = __buf;
-	struct nfsd_dirent *dirent = buf->dirent;
+	struct qstr *qs = buf->name;
+	char *nbuf = (char*)qs->name; /* cast is to get rid of "const" */
 	int result = 0;
 
 	buf->sequence++;
-#ifdef NFSD_DEBUG_VERY_VERBOSE
-printk("filldir_one: seq=%d, ino=%lu, name=%s\n", buf->sequence, ino, name);
+#ifdef NFSD_DEBUG_VERBOSE
+dprintk("filldir_one: seq=%d, ino=%ld, name=%s\n", buf->sequence, ino, name);
 #endif
-	if (buf->sequence == 2) {
-		buf->dirino = ino;
-		goto out;
-	}
-	if (dirent->ino == ino) {
-		dirent->len = len;
-		memcpy(dirent->name, name, len);
-		dirent->name[len] = '\0';
+	if (buf->ino == ino) {
+		qs->len = len;
+		memcpy(nbuf, name, len);
+		nbuf[len] = '\0';
 		buf->found = 1;
 		result = -1;
 	}
-out:
 	return result;
 }
 
 /*
- * Read a directory and return the parent inode number and the name
- * of the specified entry. The dirent must be initialized with the
- * inode number of the desired entry.
+ * Read a directory and return the name of the specified entry.  
+ * i_sem is already down().
  */
-static int get_parent_ino(struct dentry *dentry, struct nfsd_dirent *dirent)
+static int get_ino_name(struct dentry *dentry, struct qstr *name, unsigned long ino)
 {
 	struct inode *dir = dentry->d_inode;
 	int error;
@@ -372,15 +89,13 @@
 	if (!file.f_op->readdir)
 		goto out_close;
 
-	buffer.dirent = dirent;
-	buffer.dirino = 0;
+	buffer.name = name;
+	buffer.ino = ino;
 	buffer.found = 0;
 	buffer.sequence = 0;
 	while (1) {
 		int old_seq = buffer.sequence;
-		down(&dir->i_sem);
 		error = file.f_op->readdir(&file, &buffer, filldir_one);
-		up(&dir->i_sem);
 		if (error < 0)
 			break;
 
@@ -391,7 +106,6 @@
 		if (old_seq == buffer.sequence)
 			break;
 	}
-	dirent->ino = buffer.dirino;
 
 out_close:
 	if (file.f_op->release)
@@ -400,716 +114,380 @@
 	return error;
 }
 
-/*
- * Look up a dentry given inode and parent inode numbers.
- *
- * This relies on the ability of a Unix-like filesystem to return
- * the parent inode of a directory as the ".." (second) entry.
- *
- * This could be further optimized if we had an efficient way of
- * searching for a dentry given the inode: as we walk up the tree,
- * it's likely that a dentry exists before we reach the root.
+/* this should be provided by each filesystem in an nfsd_operations interface as
+ * iget isn't really the right interface
  */
-struct dentry * lookup_inode(kdev_t dev, ino_t dirino, ino_t ino)
+static struct dentry *nfsd_iget(struct super_block *sb, unsigned long ino, __u32 generation)
 {
-	struct super_block *sb;
-	struct dentry *root, *dentry, *result;
-	struct inode *dir;
-	char *name;
-	unsigned long page;
-	ino_t root_ino;
-	int error;
-	struct nfsd_dirent dirent;
 
-	result = ERR_PTR(-ENOMEM);
-	page = __get_free_page(GFP_KERNEL);
-	if (!page)
-		goto out;
-
-	/*
-	 * Get the root dentry for the device.
-	 */
-	result = ERR_PTR(-ENOENT);
-	sb = get_super(dev);
-	if (!sb)
-		goto out_page;
-	root = dget(sb->s_root);
-	root_ino = root->d_inode->i_ino; /* usually 2 */
-
-	name = (char *) page + PAGE_SIZE;
-	*(--name) = 0;
-
-	/*
-	 * Walk up the tree to construct the name string.
-	 * When we reach the root inode, look up the name
-	 * relative to the root dentry.
+	/* 
+	 * ext2fs' read_inode has been strengthed to return a bad_inode if 
+	 * the inode had been deleted.
+	 *
+	 * Currently we don't know the generation for parent directory, 
+	 * so a generation of 0 means "accept any"
 	 */
-	while (1) {
-		if (ino == root_ino) {
-			if (*name == '/')
-				name++;
-			/*
-			 * Note: this dput()s the root dentry.
-			 */
-			result = lookup_dentry(name, root, 0);
-			break;
-		}
-
-		/*
-		 *  Fix for /// bad export bug: if dirino is the root,
-		 *  get the real root dentry rather than creating a temporary
-		 *  "root" dentry.  XXX We could extend this to use
-		 *  any existing dentry for the located 'dir', but all
-		 *  of this code is going to be completely rewritten soon,
-		 *  so I won't bother. 
-		 */
-
-		if (dirino == root_ino) {
-			dentry = dget(root);
-		}
-		else {
-			result = ERR_PTR(-ENOENT);
-			dir = iget_in_use(sb, dirino);
-			if (!dir)
-				goto out_root;
-			dentry = d_alloc_root(dir, NULL);
-			if (!dentry)
-				goto out_iput;
-		}
-
-		/*
-		 * Get the name for this inode and the next parent inode.
-		 */
-		dirent.ino = ino;
-		error = get_parent_ino(dentry, &dirent);
-		result = ERR_PTR(error);
-		dput(dentry);
-		if (error)
-			goto out_root;
-		/*
-		 * Prepend the name to the buffer.
-		 */
-		result = ERR_PTR(-ENAMETOOLONG);
-		name -= (dirent.len + 1);
-		if ((unsigned long) name <= page)
-			goto out_root;
-		memcpy(name + 1, dirent.name, dirent.len);
-		*name = '/';
-
-		/*
-		 * Make sure we can't get caught in a loop ...
-		 */
-		if (dirino == dirent.ino && dirino != root_ino) {
-			printk(KERN_DEBUG 
-			       "lookup_inode: looping?? (ino=%ld, path=%s)\n",
-				dirino, name);	
-			goto out_root;
-		}
-		ino = dirino;
-		dirino = dirent.ino;
-	}
-
-out_page:
-	free_page(page);
-out:
-	return result;
-
-	/*
-	 * Error exits ...
-	 */
-out_iput:
-	result = ERR_PTR(-ENOMEM);
-	iput(dir);
-out_root:
-	dput(root);
-	goto out_page;
-}
-
-/*
- * Find an entry in the cache matching the given dentry pointer.
- */
-static struct fh_entry *find_fhe(struct dentry *dentry, int cache,
-				struct fh_entry **empty)
-{
-	struct fh_entry *fhe;
-	int i, found = (empty == NULL) ? 1 : 0;
-
-	if (!dentry)
-		goto out;
-
-	fhe = (cache == NFSD_FILE_CACHE) ? &filetable[0] : &dirstable[0];
-	for (i = 0; i < NFSD_MAXFH; i++, fhe++) {
-		if (fhe->dentry == dentry) {
-			fhe->reftime = jiffies;
-			return fhe;
-		}
-		if (!found && !fhe->dentry) {
-			found = 1;
-			*empty = fhe;
-		}
-	}
-out:
-	return NULL;
-}
-
-/*
- * Expire a cache entry.
- */
-static void expire_fhe(struct fh_entry *empty, int cache)
-{
-	struct dentry *dentry = empty->dentry;
-
-#ifdef NFSD_DEBUG_VERBOSE
-printk("expire_fhe: expiring %s %s/%s, d_count=%d, ino=%lu\n",
-(cache == NFSD_FILE_CACHE) ? "file" : "dir",
-dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count,empty->ino);
-#endif
-	empty->dentry = NULL;	/* no dentry */
-	/*
-	 * Add the parent to the dir cache before releasing the dentry,
-	 * and check whether to save a copy of the dentry's path.
-	 */
-	if (dentry != dentry->d_parent) {
-		struct dentry *parent = dget(dentry->d_parent);
-		if (add_to_fhcache(parent, NFSD_DIR_CACHE))
-			nfsd_nr_verified++;
-		else
-			dput(parent);
-		/*
-		 * If we're expiring a directory, copy its path.
-		 */
-		if (cache == NFSD_DIR_CACHE) {
-			add_to_path_cache(dentry);
-		}
-	}
-	dput(dentry);
-	nfsd_nr_put++;
-}
-
-/*
- * Look for an empty slot, or select one to expire.
- */
-static void expire_slot(int cache)
-{
-	struct fh_entry *fhe, *empty = NULL;
-	unsigned long oldest = -1;
-	int i;
-
-	fhe = (cache == NFSD_FILE_CACHE) ? &filetable[0] : &dirstable[0];
-	for (i = 0; i < NFSD_MAXFH; i++, fhe++) {
-		if (!fhe->dentry)
-			goto out;
-		if (fhe->reftime < oldest) {
-			oldest = fhe->reftime;
-			empty = fhe;
-		}
-	}
-	if (empty)
-		expire_fhe(empty, cache);
-
-out:
-	return;
-}
-
-/*
- * Expire any cache entries older than a certain age.
- */
-static void expire_old(int cache, int age)
-{
-	struct fh_entry *fhe;
-	int i;
-
-#ifdef NFSD_DEBUG_VERY_VERBOSE
-printk("expire_old: expiring %s older than %d\n",
-(cache == NFSD_FILE_CACHE) ? "file" : "dir", age);
-#endif
-	fhe = (cache == NFSD_FILE_CACHE) ? &filetable[0] : &dirstable[0];
-	for (i = 0; i < NFSD_MAXFH; i++, fhe++) {
-		if (!fhe->dentry)
-			continue;
-		if ((jiffies - fhe->reftime) > age)
-			expire_fhe(fhe, cache);
-	}
-
-	/*
-	 * Trim the fixup cache ...
-	 */
-	while (nfsd_nr_fixups > NFSD_MAX_FIXUPS) {
-		struct nfsd_fixup *fp;
-		fp = list_entry(fixup_head.prev, struct nfsd_fixup, lru);
-		if ((jiffies - fp->reftime) < NFSD_MAX_FIXUP_AGE)
-			break;
-		free_fixup_entry(fp);
-	}
-
-	/*
-	 * Trim the path cache ...
-	 */
-	while (nfsd_nr_paths > NFSD_MAX_PATHS) {
-		struct nfsd_path *pe;
-		pe = list_entry(path_inuse.prev, struct nfsd_path, lru);
-		if (pe->users)
-			break;
-		free_path_entry(pe);
-	}
-}
-
-/*
- * Add a dentry to the file or dir cache.
- *
- * Note: As NFS file handles must have an inode, we don't accept
- * negative dentries.
- */
-static int add_to_fhcache(struct dentry *dentry, int cache)
-{
-	struct fh_entry *fhe, *empty = NULL;
-	struct inode *inode = dentry->d_inode;
-
+	struct inode *inode;
+	struct list_head *lp;
+	struct dentry *result;
+	inode = iget_in_use(sb, ino);
 	if (!inode) {
-#ifdef NFSD_PARANOIA
-printk("add_to_fhcache: %s/%s rejected, no inode!\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
-#endif
-		return 0;
-	}
+		dprintk("nfsd_iget: failed to find ino: %lu on %s\n",
+			ino, bdevname(sb->s_dev));
+		return ERR_PTR(-ESTALE);
+	}
+	if (is_bad_inode(inode)
+	    || (generation && inode->i_generation != generation)
+		) {
+		/* we didn't find the right inode.. */
+		dprintk("fh_verify: Inode %lu, Bad count: %d %d or version  %u %u\n",
+			inode->i_ino,
+			inode->i_nlink, inode->i_count,
+			inode->i_generation,
+			generation);
 
-repeat:
-	fhe = find_fhe(dentry, cache, &empty);
-	if (fhe) {
-		return 0;
+		iput(inode);
+		return ERR_PTR(-ESTALE);
 	}
-
-	/*
-	 * Not found ... make a new entry.
+	/* now to find a dentry.
+	 * If possible, get a well-connected one
 	 */
-	if (empty) {
-		empty->dentry = dentry;
-		empty->reftime = jiffies;
-		empty->ino = inode->i_ino;
-		empty->dev = inode->i_dev;
-		return 1;
-	}
-
-	/* if nfsd_server is zero, NFSD_MAXFH will be zero too, so
-	 * find_fhe() will NEVER find the file handle NOR an empty space,
-	 * and expire_slot will not be able to expire any file handle,
-	 * because NFSD_MAXFH is zero ... */
-
-	if (nfsd_nservers <= 0) {
-		return 0;
-	}
-
-	expire_slot(cache);
-	goto repeat;
-}
-
-/*
- * Find an entry in the dir cache for the specified inode number.
- */
-static struct fh_entry *find_fhe_by_ino(kdev_t dev, ino_t ino)
-{
-	struct fh_entry * fhe = &dirstable[0];
-	int i;
-
-	for (i = 0; i < NFSD_MAXFH; i++, fhe++) {
-		if (fhe->ino == ino && fhe->dev == dev) {
-			fhe->reftime = jiffies;
-			return fhe;
+	for (lp = inode->i_dentry.next; lp != &inode->i_dentry ; lp=lp->next) {
+		result = list_entry(lp,struct dentry, d_alias);
+		if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) {
+			dget(result);
+			iput(inode);
+			return result;
 		}
 	}
-	return NULL;
+	result = d_alloc_root(inode, NULL);
+	if (result == NULL) {
+		iput(inode);
+		return ERR_PTR(-ENOMEM);
+	}
+	result->d_flags |= DCACHE_NFSD_DISCONNECTED;
+	d_rehash(result); /* so a dput won't loose it */
+	return result;
 }
 
-/*
- * Find the (directory) dentry with the specified (dev, inode) number.
- * Note: this leaves the dentry in the cache.
+/* this routine links an IS_ROOT dentry into the dcache tree.  It gains "parent"
+ * as a parent and "name" as a name
+ * It should possibly go in dcache.c
  */
-static struct dentry *find_dentry_by_ino(kdev_t dev, ino_t ino)
+int d_splice(struct dentry *target, struct dentry *parent, struct qstr *name)
 {
-	struct fh_entry *fhe;
-	struct nfsd_path *pe;
-	struct dentry * dentry;
-
-#ifdef NFSD_DEBUG_VERBOSE
-printk("find_dentry_by_ino: looking for inode %ld\n", ino);
-#endif
-	/*
-	 * Special case: inode number 2 is the root inode,
-	 * so we can use the root dentry for the device.
-	 */
-	if (ino == 2) {
-		struct super_block *sb = get_super(dev);
-		if (sb) {
+	struct dentry *tdentry;
 #ifdef NFSD_PARANOIA
-printk("find_dentry_by_ino: getting root dentry for %s\n", kdevname(dev));
-#endif
-			if (sb->s_root) {
-				dentry = dget(sb->s_root);
-				goto out;
-			} else {
+	if (!IS_ROOT(target))
+		printk("nfsd: d_splice with no-root target: %s/%s\n", parent->d_name.name, name->name);
+	if (!(target->d_flags & DCACHE_NFSD_DISCONNECTED))
+		printk("nfsd: d_splice with non-DISCONNECTED target: %s/%s\n", parent->d_name.name, name->name);
+#endif
+	name->hash = full_name_hash(name->name, name->len);
+	tdentry = d_alloc(parent, name);
+	if (tdentry == NULL)
+		return -ENOMEM;
+	d_move(target, tdentry);
+
+	/* tdentry will have been made a "child" of target (the parent of target)
+	 * make it an IS_ROOT instead
+	 */
+	list_del(&tdentry->d_child);
+	tdentry->d_parent = tdentry;
+	d_rehash(target);
+	dput(tdentry);
+
+	/* if parent is properly connected, then we can assert that
+	 * the children are connected, but it must be a singluar (non-forking)
+	 * branch
+	 */
+	if (!(parent->d_flags & DCACHE_NFSD_DISCONNECTED)) {
+		while (target) {
+			target->d_flags &= ~DCACHE_NFSD_DISCONNECTED;
+			parent = target;
+			if (list_empty(&parent->d_subdirs))
+				target = NULL;
+			else {
+				target = list_entry(parent->d_subdirs.next, struct dentry, d_child);
 #ifdef NFSD_PARANOIA
-				printk("find_dentry_by_ino: %s has no root??\n",
-					kdevname(dev));
+				/* must be only child */
+				if (target->d_child.next != &parent->d_subdirs
+				    || target->d_child.prev != &parent->d_subdirs)
+					printk("nfsd: d_splice found non-singular disconnected branch: %s/%s\n",
+					       parent->d_name.name, target->d_name.name);
 #endif
 			}
 		}
 	}
+	return 0;
+}
 
-	/*
-	 * Search the dentry cache ...
-	 */
-	fhe = find_fhe_by_ino(dev, ino);
-	if (fhe) {
-		dentry = dget(fhe->dentry);
-		goto out;
-	}
-	/*
-	 * Search the path cache ...
-	 */
-	dentry = NULL;
-	pe = get_path_entry(dev, ino);
-	if (pe) {
-		struct dentry *res;
-		res = lookup_dentry(pe->name, NULL, 0);
-		if (!IS_ERR(res)) {
-			struct inode *inode = res->d_inode;
-			if (inode && inode->i_ino == ino &&
-				     inode->i_dev == dev) {
-				dentry = res;
-#ifdef NFSD_PARANOIA
-printk("find_dentry_by_ino: found %s/%s, ino=%ld\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, ino);
-#endif
-				if (add_to_fhcache(dentry, NFSD_DIR_CACHE)) {
-					dget(dentry);
-					nfsd_nr_verified++;
-				}
-				put_path(pe);
-			} else {
-				dput(res);
-				put_path(pe);
-				/* We should delete it from the cache. */
-				free_path_entry(pe);
+/* this routine finds the dentry of the parent of a given directory
+ * it should be in the filesystem accessed by nfsd_operations
+ * it assumes lookup("..") works.
+ */
+struct dentry *nfsd_findparent(struct dentry *child)
+{
+	struct dentry *tdentry, *pdentry;
+	tdentry = d_alloc(child, &(const struct qstr) {"..", 2, 0});
+	if (!tdentry)
+		return ERR_PTR(-ENOMEM);
+
+	/* I'm going to assume that if the returned dentry is different, then
+	 * it is well connected.  But nobody returns different dentrys do they?
+	 */
+	pdentry = child->d_inode->i_op->lookup(child->d_inode, tdentry);
+	d_drop(tdentry); /* we never want ".." hashed */
+	if (!pdentry) {
+		/* I don't want to return a ".." dentry.
+		 * I would prefer to return an unconnected "IS_ROOT" dentry,
+		 * though a properly connected dentry is even better
+		 */
+		/* if first or last of alias list is not tdentry, use that
+		 * else make a root dentry
+		 */
+		struct list_head *aliases = &tdentry->d_inode->i_dentry;
+		if (aliases->next != aliases) {
+			pdentry = list_entry(aliases->next, struct dentry, d_alias);
+			if (pdentry == tdentry)
+				pdentry = list_entry(aliases->prev, struct dentry, d_alias);
+			if (pdentry == tdentry)
+				pdentry = NULL;
+			if (pdentry) dget(pdentry);
+		}
+		if (pdentry == NULL) {
+			pdentry = d_alloc_root(igrab(tdentry->d_inode), NULL);
+			if (pdentry) {
+				pdentry->d_flags |= DCACHE_NFSD_DISCONNECTED;
+				d_rehash(pdentry);
 			}
-		} else {
-#ifdef NFSD_PARANOIA
-printk("find_dentry_by_ino: %s lookup failed\n", pe->name);
-#endif
-			put_path(pe);
-			/* We should delete it from the cache. */
-			free_path_entry(pe);
 		}
+		if (pdentry == NULL)
+			pdentry = ERR_PTR(-ENOMEM);
 	}
-out:
-	return dentry;
+	dput(tdentry); /* it is not hashed, it will be discarded */
+	return pdentry;
 }
 
-/*
- * Look for an entry in the file cache matching the dentry pointer,
- * and verify that the (dev, inode) numbers are correct. If found,
- * the entry is removed from the cache.
- */
-static struct dentry *find_dentry_in_fhcache(struct knfs_fh *fh)
+static struct dentry *splice(struct dentry *child, struct dentry *parent)
 {
-/* FIXME: this must use the dev/ino/dir_ino triple. */ 
-#if 0
-	struct fh_entry * fhe;
-
-	fhe = find_fhe(fh->fh_dcookie, NFSD_FILE_CACHE, NULL);
-	if (fhe) {
-		struct dentry *parent, *dentry;
-		struct inode *inode;
-
-		dentry = fhe->dentry;
-		inode = dentry->d_inode;
+	int err = 0;
+	struct qstr qs;
+	char namebuf[256];
+	struct list_head *lp;
+	struct dentry *tmp;
+	/* child is an IS_ROOT (anonymous) dentry, but it is hypothesised that
+	 * it should be a child of parent.
+	 * We see if we can find a name and, if we can - splice it in.
+	 * We hold the i_sem on the parent the whole time to try to follow 
+	 * locking protocols.
+	 */
+	qs.name = namebuf;
+	down(&parent->d_inode->i_sem);
 
-		if (!inode) {
-#ifdef NFSD_PARANOIA
-printk("find_dentry_in_fhcache: %s/%s has no inode!\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
-#endif
+	/* Now, things might have changed while we waited.
+	 * Possibly a friendly filesystem found child and spliced it in in 
+	 * response to a lookup (though nobody does this yet).  
+	 * In this case, just succeed.
+	 */
+	if (child->d_parent == parent) goto out;
+	/* Possibly a new dentry has been made for this child->d_inode in 
+	 * parent by a lookup.  In this case return that dentry. 
+	 * caller must notice and act accordingly
+	 */
+	for (lp = child->d_inode->i_dentry.next; lp != &child->d_inode->i_dentry ; lp=lp->next) {
+		tmp = list_entry(lp,struct dentry, d_alias);
+		if (tmp->d_parent == parent) {
+			child = dget(tmp);
 			goto out;
 		}
-		if (inode->i_ino != u32_to_ino_t(fh->fh_ino))
-			goto out;
- 		if (inode->i_dev != u32_to_kdev_t(fh->fh_dev))
-			goto out;
-
-		fhe->dentry = NULL;
-		fhe->ino = 0;
-		fhe->dev = 0;
-		nfsd_nr_put++;
-		/*
-		 * Make sure the parent is in the dir cache ...
-		 */
-		parent = dget(dentry->d_parent);
-		if (add_to_fhcache(parent, NFSD_DIR_CACHE))
-			nfsd_nr_verified++;
-		else
-			dput(parent);
-		return dentry;
 	}
-out:
-#endif
-	return NULL;
-}
-
-/*
- * Look for an entry in the parent directory with the specified
- * inode number.
- */
-static struct dentry *lookup_by_inode(struct dentry *parent, ino_t ino)
-{
-	struct dentry *dentry;
-	int error;
-	struct nfsd_dirent dirent;
-
-	/*
-	 * Search the directory for the inode number.
+	/* well, if we can find a name for child in parent, it should be 
+	 * safe to splice it in 
 	 */
-	dirent.ino = ino;
-	error = get_parent_ino(parent, &dirent);
-	if (error) {
-#ifdef NFSD_PARANOIA_EXTREME
-printk("lookup_by_inode: ino %ld not found in %s\n", ino, parent->d_name.name);
-#endif
-		goto no_entry;
-	}
-#ifdef NFSD_PARANOIA_EXTREME
-printk("lookup_by_inode: found %s\n", dirent.name);
-#endif
-
-	dentry = lookup_dentry(dirent.name, parent, 0);
-	if (!IS_ERR(dentry)) {
-		if (dentry->d_inode && dentry->d_inode->i_ino == ino)
-			goto out;
-#ifdef NFSD_PARANOIA_EXTREME
-printk("lookup_by_inode: %s/%s inode mismatch??\n",
-parent->d_name.name, dentry->d_name.name);
-#endif
-		dput(dentry);
-	} else {
-#ifdef NFSD_PARANOIA_EXTREME
-printk("lookup_by_inode: %s lookup failed, error=%ld\n",
-dirent.name, PTR_ERR(dentry));
-#endif
+	err = get_ino_name(parent, &qs, child->d_inode->i_ino);
+	if (err)
+		goto out;
+	tmp = d_lookup(parent, &qs);
+	if (tmp) {
+		/* Now that IS odd.  I wonder what it means... */
+		err = -EEXIST;
+		printk("nfsd-fh: found a name that I didn't expect: %s/%s\n", parent->d_name.name, qs.name);
+		dput(tmp);
+		goto out;
 	}
-
-no_entry:
-	dentry = NULL;
-out:
-	return dentry;
+	err = d_splice(child, parent, &qs);
+	dprintk("nfsd_fh: found name %s for ino %ld\n", child->d_name.name, child->d_inode->i_ino);
+ out:
+	up(&parent->d_inode->i_sem);
+	if (err)
+		return ERR_PTR(err);
+	else
+		return child;
 }
 
 /*
- * Search the fix-up list for a dentry from a prior lookup.
+ * This is the basic lookup mechanism for turning an NFS file handle
+ * into a dentry.
+ * We use nfsd_iget and if that doesn't return a suitably connected dentry,
+ * we try to find the parent, and the parent of that and so-on until a
+ * connection if made.
  */
-static ino_t nfsd_cached_lookup(struct knfs_fh *fh)
-{
-	struct nfsd_fixup *fp;
-
-	fp = find_cached_lookup(u32_to_kdev_t(fh->fh_dev),
-				u32_to_ino_t(fh->fh_dirino),
-				u32_to_ino_t(fh->fh_ino));
-	if (fp)
-		return fp->new_dirino;
-	return 0;
-}
-
-void
-expire_all(void)
+static struct dentry *
+find_fh_dentry(struct super_block *sb, struct knfs_fh *fh, int needpath)
 {
- 	if (time_after_eq(jiffies, nfsd_next_expire)) {
- 		expire_old(NFSD_FILE_CACHE,  5*HZ);
- 		expire_old(NFSD_DIR_CACHE , 60*HZ);
- 		nfsd_next_expire = jiffies + 5*HZ;
- 	}
-}
+	struct dentry *dentry, *result = NULL;
+	struct dentry *tmp;
+	int  found =0;
+	int err;
+	/* the sb->s_nfsd_free_path_sem semaphore is needed to make sure that only one unconnected (free)
+	 * dcache path ever exists, as otherwise two partial paths might get
+	 * joined together, which would be very confusing.
+	 * If there is ever an unconnected non-root directory, then this lock
+	 * must be held.
+	 */
 
-/* 
- * Free cache after unlink/rmdir.
- */
-void
-expire_by_dentry(struct dentry *dentry)
-{
-	struct fh_entry *fhe;
 
-	fhe = find_fhe(dentry, NFSD_FILE_CACHE, NULL);
-	if (fhe) {
-		expire_fhe(fhe, NFSD_FILE_CACHE);
-	}
-	fhe = find_fhe(dentry, NFSD_DIR_CACHE, NULL);
-	if (fhe) {
-		expire_fhe(fhe, NFSD_DIR_CACHE);
+	nfsdstats.fh_lookup++;
+	/*
+	 * Attempt to find the inode.
+	 */
+ retry:
+	result = nfsd_iget(sb, fh->fh_ino, fh->fh_generation);
+	err = PTR_ERR(result);
+	if (IS_ERR(result))
+		goto err_out;
+	err = -ESTALE;
+	if (!result) {
+		dprintk("find_fh_dentry: No inode found.\n");
+		goto err_out;
 	}
-}
+	if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED))
+		return result;
 
-/*
- * The is the basic lookup mechanism for turning an NFS file handle 
- * into a dentry. There are several levels to the search:
- * (1) Look for the dentry pointer the short-term fhcache,
- *     and verify that it has the correct inode number.
- *
- * (2) Try to validate the dentry pointer in the file handle,
- *     and verify that it has the correct inode number. If this
- *     fails, check for a cached lookup in the fix-up list and
- *     repeat step (2) using the new dentry pointer.
- *
- * (3) Look up the dentry by using the inode and parent inode numbers
- *     to build the name string. This should succeed for any Unix-like
- *     filesystem.
- *
- * (4) Search for the parent dentry in the dir cache, and then
- *     look for the name matching the inode number.
- *
- * (5) The most general case ... search the whole volume for the inode.
- *
- * If successful, we return a dentry with the use count incremented.
- *
- * Note: steps (4) and (5) above are probably unnecessary now that (3)
- * is working. Remove the code once this is verified ...
- */
-static struct dentry *
-find_fh_dentry(struct knfs_fh *fh)
-{
-	struct super_block *sb;
-	struct dentry *dentry, *parent;
-	struct inode * inode;
-	struct list_head *lst;
-	int looked_up = 0, retry = 0;
-	ino_t dirino;
+	/* result is now an anonymous dentry, which may be adequate as it 
+	 * stands, or else will get spliced into the dcache tree */
 
-	/*
-	 * Stage 1: Look for the dentry in the short-term fhcache.
-	 */
-	dentry = find_dentry_in_fhcache(fh);
-	if (dentry) {
-		nfsdstats.fh_cached++;
-		goto out;
+	if (!S_ISDIR(result->d_inode->i_mode) && ! needpath) {
+		nfsdstats.fh_anon++;
+		return result;
 	}
-	/*
-	 * Stage 2: Attempt to find the inode.
+
+	/* It's a directory, or we are required to confirm the file's
+	 * location in the tree.
 	 */
-	sb = get_super(fh->fh_dev);
-	if (NULL == sb) {
-		printk("find_fh_dentry: No SuperBlock for device %s.",
-		       kdevname(fh->fh_dev));
-		dentry = NULL;
-		goto out;
-	}
+	dprintk("nfs_fh: need to look harder for %d/%d\n",sb->s_dev,fh->fh_ino);
+	down(&sb->s_nfsd_free_path_sem);
 
-	dirino = u32_to_ino_t(fh->fh_dirino);
-	inode = iget_in_use(sb, fh->fh_ino);
-	if (!inode) {
-		dprintk("find_fh_dentry: No inode found.\n");
-		goto out_five;
-	}
-	goto check;
-recheck:
-	if (!inode) {
-		dprintk("find_fh_dentry: No inode found.\n");
-		goto out_three;
+	/* claiming the semaphore might have allow things to get fixed up */
+	if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) {
+		up(&sb->s_nfsd_free_path_sem);
+		return result;
 	}
-check:
-	for (lst = inode->i_dentry.next;
-	     lst != &inode->i_dentry;
-	     lst = lst->next) {
-		dentry = list_entry(lst, struct dentry, d_alias);
-
-/* if we are looking up a directory then we don't need the parent! */
-		if (!dentry ||
-		    !dentry->d_parent ||
-		    !dentry->d_parent->d_inode) {
-printk("find_fh_dentry: Found a useless inode %lu\n", inode->i_ino);
-			continue;
-		}
-		if (dentry->d_parent->d_inode->i_ino != dirino)
-			continue;
 
-		dget(dentry);
-		iput(inode);
-#ifdef NFSD_DEBUG_VERBOSE
-		printk("find_fh_dentry: Found%s %s/%s filehandle dirino = %lu, %lu\n",
-		       retry ? " Renamed" : "",
-		       dentry->d_parent->d_name.name,
-		       dentry->d_name.name,
-		       dentry->d_parent->d_inode->i_ino,
-		       dirino);
-#endif
-		goto out;
-	} /* for inode->i_dentry */
 
-	/*
-	 * Before proceeding to a lookup, check for a rename
-	 */
-	if (!retry && (dirino = nfsd_cached_lookup(fh))) {
-		dprintk("find_fh_dentry: retry with %lu\n", dirino);
-		retry = 1;
-		goto recheck;
+	found = 0;
+	if (!S_ISDIR(result->d_inode->i_mode)) {
+		nfsdstats.fh_nocache_nondir++;
+		if (fh->fh_dirino == 0)
+			goto err_result; /* don't know how to find parent */
+		else {
+			/* need to iget fh->fh_dirino and make sure this 
+			 * inode is in that directory 
+			 */
+			dentry = nfsd_iget(sb, fh->fh_dirino, 0);
+			err = PTR_ERR(dentry);
+			if (IS_ERR(dentry))
+				goto err_result;
+			err = -ESTALE;
+			if (!dentry->d_inode
+			    || !S_ISDIR(dentry->d_inode->i_mode)) {
+				goto err_dentry;
+			}
+			if ((!dentry->d_flags & DCACHE_NFSD_DISCONNECTED))
+				found = 1;
+			tmp = splice(result, dentry);
+			err = PTR_ERR(tmp);
+			if (IS_ERR(tmp))
+				goto err_dentry;
+			if (tmp != result) {
+				/* it is safe to just use tmp instead, but 
+				 * we must discard result first 
+				 */
+				d_drop(result);
+				dput(result);
+				result = tmp;
+				/* If !found, then this is really wierd, 
+				 * but it shouldn't hurt */
+			}
+		}
+	} else {
+		nfsdstats.fh_nocache_dir++;
+		dentry = dget(result);
 	}
 
-	iput(inode);
+	while(!found) {
+		/* LOOP INVARIANT */
+		/* haven't found a place in the tree yet, but we do have a 
+		 * free path from dentry down to result, and dentry is a 
+		 * directory.  Have a hold on dentry and result 
+		 */
+		struct dentry *pdentry;
+		struct inode *parent;
 
-	dprintk("find_fh_dentry: dirino not found %lu\n", dirino);
+		pdentry = nfsd_findparent(dentry);
+		err = PTR_ERR(pdentry);
+		if (IS_ERR(pdentry))
+			goto err_dentry;
+		parent = pdentry->d_inode;
+		err = -EACCES;
+		if (!parent) {
+			dput(pdentry);
+			goto err_dentry;
+		}
 
-out_three:
+		if (!(dentry->d_flags & DCACHE_NFSD_DISCONNECTED))
+			found = 1;
 
-	/*
-	 * Stage 3: Look up the dentry based on the inode and parent inode
-	 * numbers. This should work for all Unix-like filesystems.
-	 */
-	looked_up = 1;
-	dentry = lookup_inode(u32_to_kdev_t(fh->fh_dev),
-			      u32_to_ino_t(fh->fh_dirino),
-			      u32_to_ino_t(fh->fh_ino));
-	if (!IS_ERR(dentry)) {
-		struct inode * inode = dentry->d_inode;
-#ifdef NFSD_DEBUG_VERBOSE
-printk("find_fh_dentry: looked up %s/%s\n",
-       dentry->d_parent->d_name.name, dentry->d_name.name);
-#endif
-		if (inode && inode->i_ino == u32_to_ino_t(fh->fh_ino)) {
-			nfsdstats.fh_lookup++;
-			goto out;
+		tmp = splice(dentry, pdentry);
+		if (tmp != dentry) {
+			/* Something wrong.  We need to drop the whole 
+			 * dentry->result path whatever it was
+			 */
+			struct dentry *d;
+			for (d=result ; d ; d=(d->d_parent == d)?NULL:d->d_parent)
+				d_drop(d);
+		}
+		if (IS_ERR(tmp)) {
+			err = PTR_ERR(tmp);
+			dput(pdentry);
+			goto err_dentry;
+		}
+		if (tmp != dentry) {
+			/* we lost a race,  try again
+			 */
+			dput(tmp);
+			dput(dentry);
+			dput(result);	/* this will discard the whole free path, so we can up the semaphore */
+			up(&sb->s_nfsd_free_path_sem);
+			goto retry;
 		}
-#ifdef NFSD_PARANOIA
-printk("find_fh_dentry: %s/%s lookup mismatch!\n",
-       dentry->d_parent->d_name.name, dentry->d_name.name);
-#endif
 		dput(dentry);
+		dentry = pdentry;
 	}
+	dput(dentry);
+	up(&sb->s_nfsd_free_path_sem);
+	return result;
 
-	/*
-	 * Stage 4: Look for the parent dentry in the fhcache ...
-	 */
-	parent = find_dentry_by_ino(u32_to_kdev_t(fh->fh_dev),
-				    u32_to_ino_t(fh->fh_dirino));
-	if (parent) {
-		/*
-		 * ... then search for the inode in the parent directory.
-		 */
-		dget(parent);
-		dentry = lookup_by_inode(parent, u32_to_ino_t(fh->fh_ino));
-		dput(parent);
-		if (dentry)
-			goto out;
-	}
-
-out_five:
-
-	/*
-	 * Stage 5: Search the whole volume, Yea Right.
-	 */
-#ifdef NFSD_PARANOIA_EXTREME
-printk("find_fh_dentry: %s/%u dir/%u not found!\n",
-       kdevname(u32_to_kdev_t(fh->fh_dev)), fh->fh_ino, fh->fh_dirino);
-#endif
-	dentry = NULL;
-	nfsdstats.fh_stale++;
-	
-out:
-	expire_all();
-	return dentry;
+err_dentry:
+	dput(dentry);
+err_result:
+	dput(result);
+	up(&sb->s_nfsd_free_path_sem);
+err_out:
+	if (err == -ESTALE)
+		nfsdstats.fh_stale++;
+	return ERR_PTR(err);
 }
 
 /*
@@ -1117,6 +495,9 @@
  *
  * Note that the file handle dentry may need to be freed even after
  * an error return.
+ *
+ * This is only called at the start of an nfsproc call, so fhp points to
+ * a svc_fh which is all 0 except for the over-the-wire file handle.
  */
 u32
 fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
@@ -1134,57 +515,79 @@
 		fh->fh_ino,
 		fh->fh_dirino);
 
-	if (fhp->fh_dverified)
-		goto check_type;
-	/*
-	 * Look up the export entry.
-	 */
-	error = nfserr_stale;
-	exp = exp_get(rqstp->rq_client,
-		      u32_to_kdev_t(fh->fh_xdev),
-		      u32_to_ino_t(fh->fh_xino));
-	if (!exp) {
-		/* export entry revoked */
-		nfsdstats.fh_stale++;
-		goto out;
-	}
+	if (!fhp->fh_dentry) {
+		/*
+		 * Security: Check that the fh is internally consistant 
+		 * (from <gam3@acm.org>)
+		 */
+		if (fh->fh_dev != fh->fh_xdev) {
+			printk("fh_verify: Security: export on other device (%s, %s).\n",
+			       kdevname(fh->fh_dev), kdevname(fh->fh_xdev));
+			error = nfserr_stale;
+			nfsdstats.fh_stale++;
+			goto out;
+		}
 
-	/* Check if the request originated from a secure port. */
-	error = nfserr_perm;
-	if (!rqstp->rq_secure && EX_SECURE(exp)) {
-		printk(KERN_WARNING
-			"nfsd: request from insecure port (%08x:%d)!\n",
-				ntohl(rqstp->rq_addr.sin_addr.s_addr),
-				ntohs(rqstp->rq_addr.sin_port));
-		goto out;
-	}
+		/*
+		 * Look up the export entry.
+		 */
+		error = nfserr_stale; 
+		exp = exp_get(rqstp->rq_client,
+			      u32_to_kdev_t(fh->fh_xdev),
+			      u32_to_ino_t(fh->fh_xino));
+		if (!exp) {
+			/* export entry revoked */
+			nfsdstats.fh_stale++;
+			goto out;
+		}
+
+		/* Check if the request originated from a secure port. */
+		error = nfserr_perm;
+		if (!rqstp->rq_secure && EX_SECURE(exp)) {
+			printk(KERN_WARNING
+			       "nfsd: request from insecure port (%08x:%d)!\n",
+			       ntohl(rqstp->rq_addr.sin_addr.s_addr),
+			       ntohs(rqstp->rq_addr.sin_port));
+			goto out;
+		}
 
-	/* Set user creds if we haven't done so already. */
-	nfsd_setuser(rqstp, exp);
+		/* Set user creds if we haven't done so already. */
+		nfsd_setuser(rqstp, exp);
 
-	/*
-	 * Look up the dentry using the NFS file handle.
-	 */
-	error = nfserr_noent;
-	dentry = find_fh_dentry(fh);
-	if (!dentry) {
-		goto out;
-	}
-	if (IS_ERR(dentry)) {
-		error = nfserrno(-PTR_ERR(dentry));
-		goto out;
+		/*
+		 * Look up the dentry using the NFS file handle.
+		 */
+
+		dentry = find_fh_dentry(exp->ex_dentry->d_inode->i_sb,
+					fh,
+					!(exp->ex_flags & NFSEXP_NOSUBTREECHECK));
+
+		if (IS_ERR(dentry)) {
+			error = nfserrno(-PTR_ERR(dentry));
+			goto out;
+		}
+#ifdef NFSD_PARANOIA
+		if (S_ISDIR(dentry->d_inode->i_mode) &&
+		    (dentry->d_flags & DCACHE_NFSD_DISCONNECTED)) {
+			printk("nfsd: find_fh_dentry returned a DISCONNECTED directory: %s/%s\n",
+			       dentry->d_parent->d_name.name, dentry->d_name.name);
+		}
+#endif
+
+		fhp->fh_dentry = dentry;
+		fhp->fh_export = exp;
+		nfsd_nr_verified++;
+	} else {
+		/* just rechecking permissions
+		 * (e.g. nfsproc_create calls fh_verify, then nfsd_create 
+		 * does as well)
+		 */
+		dprintk("nfsd: fh_verify - just checking\n");
+		dentry = fhp->fh_dentry;
+		exp = fhp->fh_export;
 	}
 
-	/*
-	 * Note:  it's possible the returned dentry won't be the one in the
-	 * file handle.  We can correct the file handle for our use, but
-	 * unfortunately the client will keep sending the broken one.  Let's
-	 * hope the lookup will keep patching things up.
-	 */
-	fhp->fh_dentry = dentry;
-	fhp->fh_export = exp;
-	fhp->fh_dverified = 1;
-	nfsd_nr_verified++;
+	inode = dentry->d_inode;
 
 	/* Type check. The correct error return for type mismatches
 	 * does not seem to be generally agreed upon. SunOS seems to
@@ -1192,39 +595,7 @@
 	 * spec says this is incorrect (implementation notes for the
 	 * write call).
 	 */
-check_type:
-	dentry = fhp->fh_dentry;
-	inode = dentry->d_inode;
-	error = nfserr_stale;
-	/* On a heavily loaded SMP machine, more than one identical
-	   requests may run at the same time on different processors.
-	   One thread may get here with unfinished fh after another
-	   thread just fetched the inode. It doesn't make any senses
-	   to check fh->fh_generation here since it has not been set
-	   yet. In that case, we shouldn't send back the stale
-	   filehandle to the client. We use fh->fh_dcookie to indicate
-	   if fh->fh_generation is set or not. If fh->fh_dcookie is
-	   not set, don't return stale filehandle. */
-	if (inode->i_generation != fh->fh_generation) {
-		if (fh->fh_dcookie) {
-			dprintk("fh_verify: Bad version %lu %u %u: 0x%x, 0x%x\n",
-				inode->i_ino,
-				inode->i_generation,
-				fh->fh_generation,
-				type, access);
-			nfsdstats.fh_stale++;
-			goto out;
-		}
-		else {
-			/* We get here when inode is fetched by other
-			   threads. We just use what is in there. */
-			fh->fh_ino = ino_t_to_u32(inode->i_ino);
-			fh->fh_generation = inode->i_generation;
-			fh->fh_dcookie = (struct dentry *)0xfeebbaca;
-			nfsdstats.fh_concurrent++;
-		}
-	}
-	exp = fhp->fh_export;
+
 	if (type > 0 && (inode->i_mode & S_IFMT) != type) {
 		error = (type == S_IFDIR)? nfserr_notdir : nfserr_isdir;
 		goto out;
@@ -1238,38 +609,37 @@
 	 * Security: Check that the export is valid for dentry <gam3@acm.org>
 	 */
 	error = 0;
-	if (fh->fh_dev != fh->fh_xdev) {
-		printk("fh_verify: Security: export on other device (%s, %s).\n",
-		       kdevname(fh->fh_dev), kdevname(fh->fh_xdev));
-		error = nfserr_stale;
-		nfsdstats.fh_stale++;
-	} else if (exp->ex_dentry != dentry) {
-		struct dentry *tdentry = dentry;
 
-		do {
-			tdentry = tdentry->d_parent;
-			if (exp->ex_dentry == tdentry)
-				break;
-			/* executable only by root and we can't be root */
-			if (current->fsuid
-			    && !(tdentry->d_inode->i_uid
-			         && (tdentry->d_inode->i_mode & S_IXUSR))
-			    && !(tdentry->d_inode->i_gid
-				 && (tdentry->d_inode->i_mode & S_IXGRP))
-			    && !(tdentry->d_inode->i_mode & S_IXOTH)
-			    && (exp->ex_flags & NFSEXP_ROOTSQUASH)) {
+	if (!(exp->ex_flags & NFSEXP_NOSUBTREECHECK)) {
+		if (exp->ex_dentry != dentry) {
+			struct dentry *tdentry = dentry;
+
+			do {
+				tdentry = tdentry->d_parent;
+				if (exp->ex_dentry == tdentry)
+					break;
+				/* executable only by root and we can't be root */
+				if (current->fsuid
+				    && (exp->ex_flags & NFSEXP_ROOTSQUASH)
+				    && !(tdentry->d_inode->i_uid
+					 && (tdentry->d_inode->i_mode & S_IXUSR))
+				    && !(tdentry->d_inode->i_gid
+					 && (tdentry->d_inode->i_mode & S_IXGRP))
+				    && !(tdentry->d_inode->i_mode & S_IXOTH)
+					) {
+					error = nfserr_stale;
+					nfsdstats.fh_stale++;
+					dprintk("fh_verify: no root_squashed access.\n");
+				}
+			} while ((tdentry != tdentry->d_parent));
+			if (exp->ex_dentry != tdentry) {
 				error = nfserr_stale;
 				nfsdstats.fh_stale++;
-dprintk("fh_verify: no root_squashed access.\n");
+				printk("nfsd Security: %s/%s bad export.\n",
+				       dentry->d_parent->d_name.name,
+				       dentry->d_name.name);
+				goto out;
 			}
-		} while ((tdentry != tdentry->d_parent));
-		if (exp->ex_dentry != tdentry) {
-			error = nfserr_stale;
-			nfsdstats.fh_stale++;
-			printk("nfsd Security: %s/%s bad export.\n",
-			       dentry->d_parent->d_name.name,
-			       dentry->d_name.name);
-			goto out;
 		}
 	}
 
@@ -1277,10 +647,11 @@
 	if (!error) {
 		error = nfsd_permission(exp, dentry, access);
 	}
-#ifdef NFSD_PARANOIA
-if (error)
-printk("fh_verify: %s/%s permission failure, acc=%x, error=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, access, (error >> 24));
+#ifdef NFSD_PARANOIA_EXTREME
+	if (error) {
+		printk("fh_verify: %s/%s permission failure, acc=%x, error=%d\n",
+		       dentry->d_parent->d_name.name, dentry->d_name.name, access, (error >> 24));
+	}
 #endif
 out:
 	return error;
@@ -1300,7 +671,7 @@
 	struct dentry *parent = dentry->d_parent;
 
 	dprintk("nfsd: fh_compose(exp %x/%ld %s/%s, ino=%ld)\n",
-		exp->ex_dev, exp->ex_ino,
+		exp->ex_dev, (long) exp->ex_ino,
 		parent->d_name.name, dentry->d_name.name,
 		(inode ? inode->i_ino : 0));
 
@@ -1309,33 +680,33 @@
 	 * may not be done on error paths, but the cleanup must call fh_put.
 	 * Fix this soon!
 	 */
-	if (fhp->fh_dverified || fhp->fh_locked || fhp->fh_dentry) {
+	if (fhp->fh_dentry || fhp->fh_locked) {
 		printk(KERN_ERR "fh_compose: fh %s/%s not initialized!\n",
 			parent->d_name.name, dentry->d_name.name);
 	}
 	fh_init(fhp);
 
-	fhp->fh_handle.fh_dcookie = dentry;
+	fhp->fh_handle.fh_dirino = ino_t_to_u32(parent->d_inode->i_ino);
+	fhp->fh_handle.fh_dev    = kdev_t_to_u32(parent->d_inode->i_dev);
+	fhp->fh_handle.fh_xdev   = kdev_t_to_u32(exp->ex_dev);
+	fhp->fh_handle.fh_xino   = ino_t_to_u32(exp->ex_ino);
+	fhp->fh_handle.fh_dcookie = (struct dentry *)0xfeebbaca;
 	if (inode) {
 		fhp->fh_handle.fh_ino = ino_t_to_u32(inode->i_ino);
 		fhp->fh_handle.fh_generation = inode->i_generation;
-		fhp->fh_handle.fh_dcookie = (struct dentry *)0xfeebbaca;
+		if (S_ISDIR(inode->i_mode) || (exp->ex_flags & NFSEXP_NOSUBTREECHECK))
+			fhp->fh_handle.fh_dirino = 0;
 	}
-	fhp->fh_handle.fh_dirino = ino_t_to_u32(parent->d_inode->i_ino);
-	fhp->fh_handle.fh_dev	 = kdev_t_to_u32(parent->d_inode->i_dev);
-	fhp->fh_handle.fh_xdev	 = kdev_t_to_u32(exp->ex_dev);
-	fhp->fh_handle.fh_xino	 = ino_t_to_u32(exp->ex_ino);
 
 	fhp->fh_dentry = dentry; /* our internal copy */
 	fhp->fh_export = exp;
 
-	/* We stuck it there, we know it's good. */
-	fhp->fh_dverified = 1;
 	nfsd_nr_verified++;
 }
 
 /*
  * Update file handle information after changing a dentry.
+ * This is only called by nfsd_create
  */
 void
 fh_update(struct svc_fh *fhp)
@@ -1343,7 +714,7 @@
 	struct dentry *dentry;
 	struct inode *inode;
 
-	if (!fhp->fh_dverified)
+	if (!fhp->fh_dentry)
 		goto out_bad;
 
 	dentry = fhp->fh_dentry;
@@ -1352,7 +723,9 @@
 		goto out_negative;
 	fhp->fh_handle.fh_ino = ino_t_to_u32(inode->i_ino);
 	fhp->fh_handle.fh_generation = inode->i_generation;
-	fhp->fh_handle.fh_dcookie = (struct dentry *)0xfeebbaca;
+	if (S_ISDIR(inode->i_mode) || (fhp->fh_export->ex_flags & NFSEXP_NOSUBTREECHECK))
+		fhp->fh_handle.fh_dirino = 0;
+
 out:
 	return;
 
@@ -1366,22 +739,19 @@
 }
 
 /*
- * Release a file handle.  If the file handle carries a dentry count,
- * we add the dentry to the short-term cache rather than release it.
+ * Release a file handle.
  */
 void
 fh_put(struct svc_fh *fhp)
 {
 	struct dentry * dentry = fhp->fh_dentry;
-	if (fhp->fh_dverified) {
+	if (dentry) {
 		fh_unlock(fhp);
-		fhp->fh_dverified = 0;
+		fhp->fh_dentry = NULL;
 		if (!dentry->d_count)
 			goto out_bad;
-		if (!dentry->d_inode || !add_to_fhcache(dentry, 0)) {
-			dput(dentry);
-			nfsd_nr_put++;
-		}
+		dput(dentry);
+		nfsd_nr_put++;
 	}
 	return;
 
@@ -1389,118 +759,4 @@
 	printk(KERN_ERR "fh_put: %s/%s has d_count 0!\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name);
 	return;
-}
-
-/*
- * Flush any cached dentries for the specified device
- * or for all devices.
- *
- * This is called when revoking the last export for a
- * device, so that it can be unmounted cleanly.
- */
-void nfsd_fh_flush(kdev_t dev)
-{
-	struct fh_entry *fhe;
-	int i, pass = 2;
-
-	fhe = &filetable[0];
-	while (pass--) {
-		for (i = 0; i < NFSD_MAXFH; i++, fhe++) {
-			struct dentry *dentry = fhe->dentry;
-			if (!dentry)
-				continue;
-			if (dev && dentry->d_inode->i_dev != dev)
-				continue;
-			fhe->dentry = NULL;
-			dput(dentry);
-			nfsd_nr_put++;
-		}
-		fhe = &dirstable[0];
-	}
-}
-
-/*
- * Free the rename and path caches.
- */
-void nfsd_fh_free(void)
-{
-	struct list_head *tmp;
-	int i;
-
-	/* Flush dentries for all devices */
-	nfsd_fh_flush(0);
-
-	/*
-	 * N.B. write a destructor for these lists ...
-	 */
-	i = 0;
-	while ((tmp = fixup_head.next) != &fixup_head) {
-		struct nfsd_fixup *fp;
-		fp = list_entry(tmp, struct nfsd_fixup, lru);
-		free_fixup_entry(fp);
-		i++;
-	}
-	printk(KERN_DEBUG "nfsd_fh_free: %d fixups freed\n", i);
-
-	i = 0;
-	while ((tmp = path_inuse.next) != &path_inuse) {
-		struct nfsd_path *pe;
-		pe = list_entry(tmp, struct nfsd_path, lru);
-		free_path_entry(pe);
-		i++;
-	}
-	printk(KERN_DEBUG "nfsd_fh_free: %d paths freed\n", i);
-
-	printk(KERN_DEBUG "nfsd_fh_free: verified %d, put %d\n",
-		nfsd_nr_verified, nfsd_nr_put);
-}
-
-void nfsd_fh_init(void)
-{
-	extern void __my_nfsfh_is_too_big(void); 
-
-	if (filetable)
-		return;
-
-	/* Sanity check */ 
-	if (sizeof(struct nfs_fhbase) > 32) 
-		__my_nfsfh_is_too_big(); 
-
-	filetable = kmalloc(sizeof(struct fh_entry) * NFSD_MAXFH,
-			    GFP_KERNEL);
-	dirstable = kmalloc(sizeof(struct fh_entry) * NFSD_MAXFH,
-			    GFP_KERNEL);
-
-	if (filetable == NULL || dirstable == NULL) {
-		printk(KERN_WARNING "nfsd_fh_init : Could not allocate fhcache\n");
-		nfsd_nservers = 0;
-		return;
-	}
-
-	memset(filetable, 0, NFSD_MAXFH*sizeof(struct fh_entry));
-	memset(dirstable, 0, NFSD_MAXFH*sizeof(struct fh_entry));
-	INIT_LIST_HEAD(&path_inuse);
-	INIT_LIST_HEAD(&fixup_head);
-
-	printk(KERN_DEBUG 
-		"nfsd_fh_init : initialized fhcache, entries=%lu\n", NFSD_MAXFH);
-	/*
-	 * Display a warning if the ino_t is larger than 32 bits.
-	 */
-	if (sizeof(ino_t) > sizeof(__u32))
-		printk(KERN_INFO 
-			"NFSD: ino_t is %d bytes, using lower 4 bytes\n",
-			sizeof(ino_t));
-}
-
-void
-nfsd_fh_shutdown(void)
-{
-	if (!filetable)
-		return;
-	printk(KERN_DEBUG 
-		"nfsd_fh_shutdown : freeing %ld fhcache entries.\n", NFSD_MAXFH);
-	kfree(filetable);
-	kfree(dirstable);
-	filetable = dirstable = NULL;
 }
diff -Naur pre9/linux/fs/nfsd/nfsproc.c test/linux/fs/nfsd/nfsproc.c
--- pre9/linux/fs/nfsd/nfsproc.c	Wed May  3 17:16:46 2000
+++ test/linux/fs/nfsd/nfsproc.c	Mon Sep 18 14:35:50 2000
@@ -1,7 +1,10 @@
 /*
  * nfsproc2.c	Process version 2 NFS requests.
+ * linux/fs/nfsd/nfs2proc.c
+ * 
+ * Process version 2 NFS requests.
  *
- * Copyright (C) 1995 Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1995-1997 Olaf Kirch <okir@monad.swb.de>
  */
 
 #include <linux/linkage.h>
@@ -27,6 +30,9 @@
 
 #define NFSDDBG_FACILITY		NFSDDBG_PROC
 
+/* Check for dir entries '.' and '..' */
+#define isdotent(n, l)	(l < 3 && n[0] == '.' && (l == 1 || n[1] == '.'))
+
 #define RETURN(st)	return st
 
 static void
@@ -144,7 +150,7 @@
 				ntohl(rqstp->rq_addr.sin_addr.s_addr),
 				ntohs(rqstp->rq_addr.sin_port),
 				argp->count);
-		argp->count = avail;
+		argp->count = avail << 2;
 	}
 
 	resp->count = argp->count;
@@ -211,6 +217,12 @@
 	} else if (nfserr)
 		goto done;
 
+	nfserr = nfserr_acces;
+	if (!argp->len)
+		goto done;
+	nfserr = nfserr_exist;
+	if (isdotent(argp->name, argp->len))
+		goto done;
 	/*
 	 * Do a lookup to verify the new file handle.
 	 */
@@ -223,7 +235,7 @@
 		 * whether the file exists or not. Time to bail ...
 		 */
 		nfserr = nfserr_acces;
-		if (!newfhp->fh_dverified) {
+		if (!newfhp->fh_dentry) {
 			printk(KERN_WARNING 
 				"nfsd_proc_create: file handle not verified\n");
 			goto done;
@@ -261,7 +273,7 @@
 		goto out_unlock;
 
 	attr->ia_valid |= ATTR_MODE;
-	attr->ia_mode = type | mode;
+	attr->ia_mode = mode;
 
 	/* Special treatment for non-regular files according to the
 	 * gospel of sun micro
@@ -279,22 +291,21 @@
 			type = S_IFIFO;
 		} else if (size != rdev) {
 			/* dev got truncated because of 16bit Linux dev_t */
-			nfserr = nfserr_io;	/* or nfserr_inval? */
+			nfserr = nfserr_inval;
 			goto out_unlock;
 		} else {
 			/* Okay, char or block special */
 			is_borc = 1;
 		}
 
+		/* we've used the SIZE information, so discard it */
+		attr->ia_valid &= ~ATTR_SIZE;
+
 		/* Make sure the type and device matches */
 		nfserr = nfserr_exist;
 		if (inode && (type != (inode->i_mode & S_IFMT) || 
 		    (is_borc && inode->i_rdev != rdev)))
 			goto out_unlock;
-
-		/* invalidate size because only (type == S_IFREG) has
-		   size. */
-		attr->ia_valid &= ~ATTR_SIZE;
 	}
 	
 	nfserr = 0;
@@ -388,11 +399,8 @@
 	 */
 	nfserr = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen,
 						 argp->tname, argp->tlen,
-						 &newfh);
-	if (!nfserr) {
-		argp->attrs.ia_valid &= ~ATTR_SIZE;
-		nfserr = nfsd_setattr(rqstp, &newfh, &argp->attrs);
-	}
+				 		 &newfh, &argp->attrs);
+
 
 	fh_put(&argp->ffh);
 	fh_put(&newfh);
@@ -411,7 +419,7 @@
 
 	dprintk("nfsd: MKDIR    %p %s\n", SVCFH_DENTRY(&argp->fh), argp->name);
 
-	if (resp->fh.fh_dverified) {
+	if (resp->fh.fh_dentry) {
 		printk(KERN_WARNING
 			"nfsd_proc_mkdir: response already verified??\n");
 	}
@@ -446,8 +454,8 @@
 nfsd_proc_readdir(struct svc_rqst *rqstp, struct nfsd_readdirargs *argp,
 					  struct nfsd_readdirres  *resp)
 {
-	u32 *	buffer;
-	int	nfserr, count;
+	u32 *		buffer;
+	int		nfserr, count;
 
 	dprintk("nfsd: READDIR  %d/%d %d bytes at %d\n",
 		SVCFH_DEV(&argp->fh), SVCFH_INO(&argp->fh),
@@ -467,7 +475,8 @@
 
 	/* Read directory and encode entries on the fly */
 	nfserr = nfsd_readdir(rqstp, &argp->fh, (loff_t) argp->cookie, 
-				nfssvc_encode_entry, buffer, &count);
+			      nfssvc_encode_entry,
+			      buffer, &count, NULL);
 	resp->count = count;
 
 	fh_put(&argp->fh);
@@ -547,6 +556,8 @@
 		{ NFSERR_NXIO, ENXIO },
 		{ NFSERR_ACCES, EACCES },
 		{ NFSERR_EXIST, EEXIST },
+		{ NFSERR_XDEV, EXDEV },
+		{ NFSERR_MLINK, EMLINK },
 		{ NFSERR_NODEV, ENODEV },
 		{ NFSERR_NOTDIR, ENOTDIR },
 		{ NFSERR_ISDIR, EISDIR },
@@ -554,39 +565,22 @@
 		{ NFSERR_FBIG, EFBIG },
 		{ NFSERR_NOSPC, ENOSPC },
 		{ NFSERR_ROFS, EROFS },
+		{ NFSERR_MLINK, EMLINK },
 		{ NFSERR_NAMETOOLONG, ENAMETOOLONG },
 		{ NFSERR_NOTEMPTY, ENOTEMPTY },
 #ifdef EDQUOT
 		{ NFSERR_DQUOT, EDQUOT },
 #endif
 		{ NFSERR_STALE, ESTALE },
-		{ NFSERR_WFLUSH, EIO },
 		{ -1, EIO }
 	};
 	int	i;
 
 	for (i = 0; nfs_errtbl[i].nfserr != -1; i++) {
 		if (nfs_errtbl[i].syserr == errno)
-			return htonl (nfs_errtbl[i].nfserr);
+			return htonl(nfs_errtbl[i].nfserr);
 	}
 	printk (KERN_INFO "nfsd: non-standard errno: %d\n", errno);
 	return nfserr_io;
 }
 
-#if 0
-static void
-nfsd_dump(char *tag, u32 *buf, int len)
-{
-	int	i;
-
-	printk(KERN_NOTICE
-		"nfsd: %s (%d words)\n", tag, len);
-
-	for (i = 0; i < len && i < 32; i += 8)
-		printk(KERN_NOTICE
-			" %08lx %08lx %08lx %08lx"
-			" %08lx %08lx %08lx %08lx\n",
-			buf[i],   buf[i+1], buf[i+2], buf[i+3],
-			buf[i+4], buf[i+5], buf[i+6], buf[i+7]);
-}
-#endif
diff -Naur pre9/linux/fs/nfsd/nfssvc.c test/linux/fs/nfsd/nfssvc.c
--- pre9/linux/fs/nfsd/nfssvc.c	Tue Jan  4 10:12:23 2000
+++ test/linux/fs/nfsd/nfssvc.c	Mon Sep 18 14:35:50 2000
@@ -42,7 +42,7 @@
 extern struct svc_program	nfsd_program;
 static void			nfsd(struct svc_rqst *rqstp);
 struct timeval			nfssvc_boot = { 0, 0 };
-static int			nfsd_active = 0;
+static atomic_t			nfsd_active = ATOMIC_INIT(0);
 
 /*
  * Maximum number of nfsd processes
@@ -55,24 +55,19 @@
 	struct svc_serv *	serv;
 	int			error;
 
-	dprintk("nfsd: creating service\n");
 	error = -EINVAL;
+	if (atomic_read(&nfsd_active))
+		goto out;	/* already running */
 	if (nrservs <= 0)
 		goto out;
 	if (nrservs > NFSD_MAXSERVS)
 		nrservs = NFSD_MAXSERVS;
+
+	dprintk("nfsd: creating service (%d)\n", nrservs);
 	nfsd_nservers = nrservs;
+	nfssvc_boot = xtime;
 
 	error = -ENOMEM;
-	nfsd_fh_init();		/* NFS dentry cache */
-	if (nfsd_nservers == 0)
-		goto out;
-	  
-	error = -ENOMEM;
-	nfsd_racache_init();     /* Readahead param cache */
-	if (nfsd_nservers == 0)
-		goto out;
-	  
 	serv = svc_create(&nfsd_program, NFSD_BUFSIZE, NFSSVC_XDRSIZE);
 	if (serv == NULL)
 		goto out;
@@ -87,6 +82,8 @@
 		goto failure;
 #endif
 
+	nfsd_racache_init();	/* Readahead param cache */
+
 	while (nrservs--) {
 		error = svc_create_thread(nfsd, serv);
 		if (error < 0)
@@ -106,27 +103,27 @@
 nfsd(struct svc_rqst *rqstp)
 {
 	struct svc_serv	*serv = rqstp->rq_server;
-	int		oldumask, err, first = 0;
+	int		err;
 
-	/* Lock module and set up kernel thread */
+	/* Lock module */
 	MOD_INC_USE_COUNT;
+
+	/* Set up kernel thread */
 	lock_kernel();
 	exit_mm(current);
+	sprintf(current->comm, "nfsd");
 	current->session = 1;
 	current->pgrp = 1;
+	current->fs->umask = 0;
+
+	/* Count active threads */
+	atomic_inc(&nfsd_active);
+
+	/* Start lockd */
+	lockd_up();
+
 	/* Let svc_process check client's authentication. */
 	rqstp->rq_auth = 1;
-	sprintf(current->comm, "nfsd");
-
-	oldumask = current->fs->umask;		/* Set umask to 0.  */
-	current->fs->umask = 0;
-	if (!nfsd_active++) {
-		nfssvc_boot = xtime;		/* record boot time */
-		first = 1;
-	}
-#if 0
-	lockd_up();				/* start lockd */
-#endif
 
 	/*
 	 * The main request loop
@@ -143,13 +140,8 @@
 		 * recvfrom routine.
 		 */
 		while ((err = svc_recv(serv, rqstp,
-		        first?5*HZ:MAX_SCHEDULE_TIMEOUT)) == -EAGAIN) {
-			if (first && 1) {
-				exp_readlock();
-				expire_all();
-				exp_unlock();
-			}
-		}
+				       MAX_SCHEDULE_TIMEOUT)) == -EAGAIN)
+		    ;
 		if (err < 0)
 			break;
 
@@ -184,23 +176,17 @@
 		printk(KERN_WARNING "nfsd: terminating on signal %d\n", signo);
 	}
 
-#if 0
+	/* Count active threads */
+	if (atomic_dec_and_test(&nfsd_active)) {
+		nfsd_export_shutdown();		/* revoke all exports */
+	        nfsd_racache_shutdown();	/* release read-ahead cache */
+	}
+
 	/* Release lockd */
 	lockd_down();
-#endif
-	if (!--nfsd_active) {
-		printk("nfsd: last server exiting\n");
-		/* revoke all exports */
-		nfsd_export_shutdown();
-		/* release fhcache */
-		nfsd_fh_shutdown ();
-		/* release read-ahead cache */
-	        nfsd_racache_shutdown();
-	}
 
-	/* Destroy the thread */
+	/* Destroy the thread's resources */
 	svc_exit_thread(rqstp);
-	current->fs->umask = oldumask;
 
 	/* Release module */
 	MOD_DEC_USE_COUNT;
@@ -213,7 +199,8 @@
 	kxdrproc_t		xdr;
 	u32			nfserr;
 
-	dprintk("nfsd_dispatch: proc %d\n", rqstp->rq_proc);
+	dprintk("nfsd_dispatch: vers %d proc %d\n",
+				rqstp->rq_vers, rqstp->rq_proc);
 	proc = rqstp->rq_procinfo;
 
 	/* Check whether we have this call in the cache. */
@@ -242,8 +229,20 @@
 		svc_putlong(&rqstp->rq_resbuf, nfserr);
 
 	/* Encode result.
-	 * FIXME: Most NFSv3 calls return wcc data even when the call failed
+	 * For NFSv2, additional info is never returned in case of an error.
 	 */
+#ifdef CONFIG_NFSD_V3
+	if (!(nfserr && rqstp->rq_vers == 2)) {
+		xdr = proc->pc_encode;
+		if (xdr && !xdr(rqstp, rqstp->rq_resbuf.buf, rqstp->rq_resp)) {
+			/* Failed to encode result. Release cache entry */
+			dprintk("nfsd: failed to encode result!\n");
+			nfsd_cache_update(rqstp, RC_NOCACHE, NULL);
+			*statp = rpc_system_err;
+			return 1;
+		}
+	}
+#else
 	xdr = proc->pc_encode;
 	if (!nfserr && xdr
 	 && !xdr(rqstp, rqstp->rq_resbuf.buf, rqstp->rq_resp)) {
@@ -253,6 +252,7 @@
 		*statp = rpc_system_err;
 		return 1;
 	}
+#endif /* CONFIG_NFSD_V3 */
 
 	/* Store reply in cache. */
 	nfsd_cache_update(rqstp, proc->pc_cachetype, statp + 1);
@@ -262,16 +262,16 @@
 static struct svc_version	nfsd_version2 = {
 	2, 18, nfsd_procedures2, nfsd_dispatch
 };
-#ifdef CONFIG_NFSD_NFS3
+#ifdef CONFIG_NFSD_V3
 static struct svc_version	nfsd_version3 = {
-	3, 23, nfsd_procedures3, nfsd_dispatch
+	3, 22, nfsd_procedures3, nfsd_dispatch
 };
 #endif
 static struct svc_version *	nfsd_version[] = {
 	NULL,
 	NULL,
 	&nfsd_version2,
-#ifdef CONFIG_NFSD_NFS3
+#ifdef CONFIG_NFSD_V3
 	&nfsd_version3,
 #endif
 };
diff -Naur pre9/linux/fs/nfsd/nfsxdr.c test/linux/fs/nfsd/nfsxdr.c
--- pre9/linux/fs/nfsd/nfsxdr.c	Wed Nov 26 13:08:38 1997
+++ test/linux/fs/nfsd/nfsxdr.c	Mon Sep 18 14:35:50 2000
@@ -18,9 +18,14 @@
 #define NFSDDBG_FACILITY		NFSDDBG_XDR
 
 u32	nfs_ok, nfserr_perm, nfserr_noent, nfserr_io, nfserr_nxio,
-	nfserr_inval, nfserr_acces, nfserr_exist, nfserr_nodev, nfserr_notdir,
-	nfserr_isdir, nfserr_fbig, nfserr_nospc, nfserr_rofs,
-	nfserr_nametoolong, nfserr_dquot, nfserr_stale;
+	nfserr_acces, nfserr_exist, nfserr_xdev, nfserr_nodev,
+	nfserr_notdir, nfserr_isdir, nfserr_inval, nfserr_fbig,
+	nfserr_nospc, nfserr_rofs, nfserr_mlink,
+	nfserr_nametoolong, nfserr_notempty, nfserr_dquot, nfserr_stale,
+	nfserr_remote, nfserr_badhandle, nfserr_notsync,
+	nfserr_badcookie, nfserr_notsupp, nfserr_toosmall,
+	nfserr_serverfault, nfserr_badtype, nfserr_jukebox;
+
 
 #ifdef NFSD_OPTIMIZE_SPACE
 # define inline
@@ -52,18 +57,32 @@
 	nfserr_noent	= htonl(NFSERR_NOENT);
 	nfserr_io	= htonl(NFSERR_IO);
 	nfserr_inval	= htonl(NFSERR_INVAL);
-	nfserr_nxio = htonl(NFSERR_NXIO);
-	nfserr_acces = htonl(NFSERR_ACCES);
-	nfserr_exist = htonl(NFSERR_EXIST);
-	nfserr_nodev = htonl(NFSERR_NODEV);
-	nfserr_notdir = htonl(NFSERR_NOTDIR);
-	nfserr_isdir = htonl(NFSERR_ISDIR);
-	nfserr_fbig = htonl(NFSERR_FBIG);
-	nfserr_nospc = htonl(NFSERR_NOSPC);
-	nfserr_rofs = htonl(NFSERR_ROFS);
+	nfserr_nxio	= htonl(NFSERR_NXIO);
+	nfserr_acces	= htonl(NFSERR_ACCES);
+	nfserr_exist	= htonl(NFSERR_EXIST);
+	nfserr_xdev	= htonl(NFSERR_XDEV);
+	nfserr_nodev	= htonl(NFSERR_NODEV);
+	nfserr_notdir	= htonl(NFSERR_NOTDIR);
+	nfserr_isdir	= htonl(NFSERR_ISDIR);
+	nfserr_inval	= htonl(NFSERR_INVAL);
+	nfserr_fbig	= htonl(NFSERR_FBIG);
+	nfserr_nospc	= htonl(NFSERR_NOSPC);
+	nfserr_rofs	= htonl(NFSERR_ROFS);
+	nfserr_mlink	= htonl(NFSERR_MLINK);
 	nfserr_nametoolong = htonl(NFSERR_NAMETOOLONG);
-	nfserr_dquot = htonl(NFSERR_DQUOT);
-	nfserr_stale = htonl(NFSERR_STALE);
+	nfserr_notempty	= htonl(NFSERR_NOTEMPTY);
+	nfserr_dquot	= htonl(NFSERR_DQUOT);
+	nfserr_stale	= htonl(NFSERR_STALE);
+	nfserr_remote	= htonl(NFSERR_REMOTE);
+	nfserr_badhandle = htonl(NFSERR_BADHANDLE);
+	nfserr_notsync	= htonl(NFSERR_NOT_SYNC);
+	nfserr_badcookie = htonl(NFSERR_BAD_COOKIE);
+	nfserr_notsupp	= htonl(NFSERR_NOTSUPP);
+	nfserr_toosmall	= htonl(NFSERR_TOOSMALL);
+	nfserr_serverfault = htonl(NFSERR_SERVERFAULT);
+	nfserr_badtype	= htonl(NFSERR_BADTYPE);
+	nfserr_jukebox	= htonl(NFSERR_JUKEBOX);
+
 
 	inited = 1;
 }
diff -Naur pre9/linux/fs/nfsd/stats.c test/linux/fs/nfsd/stats.c
--- pre9/linux/fs/nfsd/stats.c	Tue Jan  4 10:12:23 2000
+++ test/linux/fs/nfsd/stats.c	Mon Sep 18 14:35:50 2000
@@ -32,16 +32,15 @@
 {
 	int	len;
 
-	len = sprintf(buffer, "rc %d %d %d  %d %d %d %d %d %d\n",
+	len = sprintf(buffer, "rc %d %d %d  %d %d %d %d %d\n",
 			nfsdstats.rchits,
 			nfsdstats.rcmisses,
 			nfsdstats.rcnocache,
-			nfsdstats.fh_cached,
-			nfsdstats.fh_valid,
-			nfsdstats.fh_fixup,
 			nfsdstats.fh_lookup,
-			nfsdstats.fh_stale,
-			nfsdstats.fh_concurrent);
+			nfsdstats.fh_anon,
+			nfsdstats.fh_nocache_nondir,
+			nfsdstats.fh_nocache_dir,
+			nfsdstats.fh_stale);
 
 	/* Assume we haven't hit EOF yet. Will be set by svc_proc_read. */
 	*eof = 0;
diff -Naur pre9/linux/fs/nfsd/vfs.c test/linux/fs/nfsd/vfs.c
--- pre9/linux/fs/nfsd/vfs.c	Tue Jan  4 10:12:23 2000
+++ test/linux/fs/nfsd/vfs.c	Mon Sep 18 14:35:50 2000
@@ -31,6 +31,10 @@
 
 #include <linux/sunrpc/svc.h>
 #include <linux/nfsd/nfsd.h>
+#ifdef CONFIG_NFSD_V3
+#include <linux/nfs3.h>
+#include <linux/nfsd/xdr3.h>
+#endif /* CONFIG_NFSD_V3 */
 #include <linux/nfsd/nfsfh.h>
 #include <linux/quotaops.h>
 
@@ -41,16 +45,15 @@
 #define NFSDDBG_FACILITY		NFSDDBG_FILEOP
 #define NFSD_PARANOIA
 
-/* Open mode for nfsd_open */
-#define OPEN_READ	0
-#define OPEN_WRITE	1
-
-/* Hack until we have a macro check for mandatory locks. */
-#ifndef IS_ISMNDLK
-#define IS_ISMNDLK(i)	(((i)->i_mode & (S_ISGID|S_IXGRP|S_IFMT)) \
-			 == (S_ISGID|S_IFREG))
-#endif
 
+/* We must ignore files (but only files) which might have mandatory
+ * locks on them because there is no way to know if the accesser has
+ * the lock.
+ */
+/* MANDATORY_LOCK taken from 2.3 */
+#define MANDATORY_LOCK(inode) \
+	(IS_MANDLOCK(inode) && ((inode)->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
+#define IS_ISMNDLK(i)	(S_ISREG((i)->i_mode) && MANDATORY_LOCK(i))
 /* Time difference margin in seconds for comparison. It is a
    dynamically-tunable parameter via /proc/fs/nfs/time-diff-margin.
  */
@@ -83,65 +86,58 @@
 static struct raparms *		raparml = NULL;
 static struct raparms *		raparm_cache = NULL;
 
+
+/*
+ * We need to do a check-parent every time
+ * after we have locked the parent - to verify
+ * that the parent is still our parent and
+ * that we are still hashed onto it..
+ *
+ * This is required in case two processes race
+ * on removing (or moving) the same entry: the
+ * parent lock will serialize them, but the
+ * other process will be too late..
+ *
+ * Note that this nfsd_check_parent is identical
+ * the check_parent in linux/fs/namei.c.
+ */
+#define nfsd_check_parent(dir, dentry) \
+	((dir) == (dentry)->d_parent && !list_empty(&dentry->d_hash))
+
 /*
  * Lock a parent directory following the VFS locking protocol.
  */
 int
 fh_lock_parent(struct svc_fh *parent_fh, struct dentry *dchild)
 {
-	int	nfserr = 0;
-
 	fh_lock(parent_fh);
 	/*
 	 * Make sure the parent->child relationship still holds,
 	 * and that the child is still hashed.
 	 */
-	if (dchild->d_parent != parent_fh->fh_dentry) 
-		goto out_not_parent;
-	if (list_empty(&dchild->d_hash))
-		goto out_not_hashed; 
-out:
-	return nfserr;
+	if (nfsd_check_parent(parent_fh->fh_dentry, dchild))
+	    return 0;
 
-out_not_parent:
 	printk(KERN_WARNING
-		"fh_lock_parent: %s/%s parent changed\n",
+		"fh_lock_parent: %s/%s parent changed or child unhashed\n",
 		dchild->d_parent->d_name.name, dchild->d_name.name);
-	goto out_unlock;
-out_not_hashed:
-	printk(KERN_WARNING
-		"fh_lock_parent: %s/%s unhashed\n",
-		dchild->d_parent->d_name.name, dchild->d_name.name);
-out_unlock:
-	nfserr = nfserr_noent;
-	fh_unlock(parent_fh);
-	goto out;
-}
 
-/*
- * Deny access to certain file systems
- */
-static inline int
-fs_off_limits(struct super_block *sb)
-{
-	return !sb || sb->s_magic == NFS_SUPER_MAGIC
-	           || sb->s_magic == PROC_SUPER_MAGIC;
+	fh_unlock(parent_fh);
+	return nfserr_noent;
 }
 
-/*
- * Check whether directory is a mount point, but it is all right if
- * this is precisely the local mount point being exported.
- */
-static inline int
-nfsd_iscovered(struct dentry *dentry, struct svc_export *exp)
-{
-	return (dentry != dentry->d_covers &&
-		dentry != exp->ex_dentry);
-}
 
 /*
  * Look up one component of a pathname.
  * N.B. After this call _both_ fhp and resfh need an fh_put
+ *
+ * If the lookup would cross a mountpoint, and the mounted filesystem
+ * is exported to the client with NFSEXP_CROSSMNT, then the lookup is
+ * accepted as it stands and the mounted directory is
+ * returned. Otherwise the covered directory is returned.
+ * NOTE: this mountpoint crossing is not supported properly by all
+ *   clients and is explicitly disallowed for NFSv3
+ *      NeilBrown <neilb@cse.unsw.edu.au>
  */
 int
 nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
@@ -166,33 +162,62 @@
 	if (err)
 		goto out;
 #endif
-	err = nfserr_noent;
-	if (fs_off_limits(dparent->d_sb))
-		goto out;
 	err = nfserr_acces;
-	if (nfsd_iscovered(dparent, exp))
-		goto out;
 
 	/* Lookup the name, but don't follow links */
-	dchild = lookup_dentry(name, dget(dparent), 0);
-	if (IS_ERR(dchild))
-		goto out_nfserr;
-	/*
-	 * check if we have crossed a mount point ...
-	 */
-	if (dchild->d_sb != dparent->d_sb) {
-		struct dentry *tdentry;
-		tdentry = dchild->d_covers;
-		if (tdentry == dchild)
-			goto out_dput;
-	        dput(dchild);
-		dchild = dget(tdentry);
-	        if (dchild->d_sb != dparent->d_sb) {
-printk("nfsd_lookup: %s/%s crossed mount point!\n", dparent->d_name.name, dchild->d_name.name);
-			goto out_dput;
+	if (strcmp(name, "..")==0) {
+		/* checking mountpoint crossing is very different when stepping up */
+		if (dparent == exp->ex_dentry) {
+			if (!EX_CROSSMNT(exp))
+				dchild = dget(dparent); /* .. == . just like at / */
+			else
+			{
+				struct svc_export *exp2 = NULL;
+				struct dentry *dp;
+				dchild = dparent->d_covers->d_parent;
+				for (dp=dchild;
+				     exp2 == NULL && dp->d_covers->d_parent != dp;
+				     dp=dp->d_covers->d_parent)
+					exp2 = exp_get(exp->ex_client, dp->d_inode->i_dev, dp->d_inode->i_ino);
+				if (exp2==NULL || dchild->d_sb != exp2->ex_dentry->d_sb) {
+					dchild = dget(dparent);
+				} else {
+					dget(dchild);
+					exp = exp2;
+				}
+			}
+		} else
+			dchild = dget(dparent->d_parent);
+	} else {
+		dchild = lookup_dentry(name, dget(dparent), 0);
+		if (IS_ERR(dchild))
+			goto out_nfserr;
+		/*
+		 * check if we have crossed a mount point ...
+		 */
+		if (dchild->d_sb != dparent->d_sb) {
+			struct svc_export *exp2 = NULL;
+			exp2 = exp_get(rqstp->rq_client,
+				       dchild->d_inode->i_dev,
+				       dchild->d_inode->i_ino);
+			if (exp2 && EX_CROSSMNT(exp2))
+				/* successfully crossed mount point */
+				exp = exp2;
+			else if (dchild->d_covers->d_sb == dparent->d_sb) {
+				/* stay in the original filesystem */
+				struct dentry *tdentry = dget(dchild->d_covers);
+				dput(dchild);
+				dchild = tdentry;
+			} else {
+				/* This cannot possibly happen */
+				printk("nfsd_lookup: %s/%s impossible mount point!\n", dparent->d_name.name, dchild->d_name.name);
+				dput(dchild);
+				err = nfserr_acces;
+				goto out;
+
+			}
 		}
 	}
-
 	/*
 	 * Note: we compose the file handle now, but as the
 	 * dentry may be negative, it may need to be updated.
@@ -207,10 +232,6 @@
 out_nfserr:
 	err = nfserrno(-PTR_ERR(dchild));
 	goto out;
-out_dput:
-	dput(dchild);
-	err = nfserr_acces;
-	goto out;
 }
 
 /*
@@ -226,6 +247,8 @@
 	int		ftype = 0;
 	int		imode;
 	int		err;
+	kernel_cap_t	saved_cap = 0;
+	int		size_change = 0;
 
 	if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE))
 		accmode |= MAY_WRITE;
@@ -234,39 +257,43 @@
 
 	/* Get inode */
 	err = fh_verify(rqstp, fhp, ftype, accmode);
-	if (err)
+	if (err || !iap->ia_valid)
 		goto out;
 
 	dentry = fhp->fh_dentry;
 	inode = dentry->d_inode;
 
 	err = inode_change_ok(inode, iap);
-	if (err) {
-		/* It is very tricky. When you are not the file owner,
-		   but have the write permission, you should be allowed
-		   to set atime and mtime to the current time on the
-		   server. However, the NFS V2 protocol doesn't support
-		   it. It has been fixed in V3. Here we do this: if the
-		   current server time and atime/mtime are close enough,
-		   we use the current server time. */
-#define CURRENT_TIME_SET	(ATTR_ATIME_SET | ATTR_MTIME_SET)
-		if (iap->ia_mtime == iap->ia_atime
-		    && ((iap->ia_valid & (CURRENT_TIME_SET))
-			== CURRENT_TIME_SET)) {
-			time_t now = CURRENT_TIME;
-			time_t delta = iap->ia_atime - now;
-			if (delta < 0) delta = -delta;
-			if (delta <= nfsd_time_diff_margin) {
-				iap->ia_valid &= ~CURRENT_TIME_SET;
-				goto current_time_ok;
-			}
+	/* could be a "touch" (utimes) request where the user is not the owner but does
+	 * have write permission. In this case the user should be allowed to set
+	 * both times to the current time.  We could just assume any such SETATTR
+	 * is intended to set the times to "now", but we do a couple of simple tests
+	 * to increase our confidence.
+	 */
+#define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET)
+#define	MAX_TOUCH_TIME_ERROR (30*60)
+	if (err
+	    && (iap->ia_valid & BOTH_TIME_SET) == BOTH_TIME_SET
+	    && iap->ia_mtime == iap->ia_ctime
+	    ) {
+	    /* looks good.  now just make sure time is in the right ballpark.
+	     * solaris, at least, doesn't seem to care what the time request is
+	     */
+	    time_t delta = iap->ia_atime - CURRENT_TIME;
+	    if (delta<0) delta = -delta;
+	    if (delta < MAX_TOUCH_TIME_ERROR) {
+		/* turn off ATTR_[AM]TIME_SET but leave ATTR_[AM]TIME
+		 * this will cause notify_change to setthese times to "now"
+		 */
+		iap->ia_valid &= ~BOTH_TIME_SET;
+		err = inode_change_ok(inode, iap);
 		}
-		goto out_nfserr;
 	}
 
-current_time_ok:
+	if (err)
+		goto out_nfserr;
 
-	/* The size case is special... */
+	/* The size case is special. It changes the file as well as the attributes.  */
 	if (iap->ia_valid & ATTR_SIZE) {
 if (!S_ISREG(inode->i_mode))
 printk("nfsd_setattr: size change??\n");
@@ -275,22 +302,12 @@
 			if (err)
 				goto out;
 		}
-		DQUOT_INIT(inode);
 		err = get_write_access(inode);
-		if (err) {
-			DQUOT_DROP(inode);
+		if (err)
 			goto out_nfserr;
-		}
-		/* N.B. Should we update the inode cache here? */
-		inode->i_size = iap->ia_size;
-		if (inode->i_op && inode->i_op->truncate)
-			inode->i_op->truncate(inode);
-		mark_inode_dirty(inode);
-		put_write_access(inode);
-		DQUOT_DROP(inode);
-		iap->ia_valid &= ~ATTR_SIZE;
-		iap->ia_valid |= ATTR_MTIME;
-		iap->ia_mtime = CURRENT_TIME;
+		size_change = 1;
+
+		DQUOT_INIT(inode);
 	}
 
 	imode = inode->i_mode;
@@ -312,24 +329,53 @@
 	}
 
 	/* Change the attributes. */
-	if (iap->ia_valid) {
-		kernel_cap_t	saved_cap = 0;
 
-		iap->ia_valid |= ATTR_CTIME;
-		iap->ia_ctime = CURRENT_TIME;
-		if (current->fsuid != 0) {
-			saved_cap = current->cap_effective;
-			cap_clear(current->cap_effective);
-		}
-		err = notify_change(dentry, iap);
-		if (current->fsuid != 0)
-			current->cap_effective = saved_cap;
-		if (err)
-			goto out_nfserr;
-		if (EX_ISSYNC(fhp->fh_export))
-			write_inode_now(inode);
+
+	iap->ia_valid |= ATTR_CTIME;
+	if (current->fsuid != 0) {
+		saved_cap = current->cap_effective;
+		cap_clear(current->cap_effective);
+	}
+#ifdef CONFIG_QUOTA
+	/* DQUOT_TRANSFER needs both ia_uid and ia_gid defined */
+	if (iap->ia_valid & (ATTR_UID|ATTR_GID)) {
+		if (! (iap->ia_valid & ATTR_UID))
+			iap->ia_uid = inode->i_uid;
+		if (! (iap->ia_valid & ATTR_GID))
+			iap->ia_gid = inode->i_gid;
+		iap->ia_valid |= ATTR_UID|ATTR_GID;
 	}
+#endif /* CONFIG_QUOTA */
+
+	fh_lock(fhp);
+#ifdef CONFIG_QUOTA
+	if (iap->ia_valid & (ATTR_UID|ATTR_GID)) 
+		err = DQUOT_TRANSFER(dentry, iap);
+	else
+#endif
+		err = notify_change(dentry, iap);
+
+	if (size_change) {
+		if (!err) {
+			vmtruncate(inode,iap->ia_size);		
+			if (inode->i_op && inode->i_op->truncate)
+				inode->i_op->truncate(inode);
+		}
+		fh_unlock(fhp);
+		put_write_access(inode);
+	} else
+		fh_unlock(fhp);
+
+	if (current->fsuid != 0)
+		current->cap_effective = saved_cap;
+	if (err)
+		goto out_nfserr;
+	if (EX_ISSYNC(fhp->fh_export))
+		write_inode_now(inode);
 	err = 0;
+
+	/* Don't unlock inode; the nfssvc_release functions are supposed
+	 * to do this. */
 out:
 	return err;
 
@@ -338,20 +384,107 @@
 	goto out;
 }
 
+#ifdef CONFIG_NFSD_V3
+/*
+ * Check server access rights to a file system object
+ */
+struct accessmap {
+	u32		access;
+	int		how;
+};
+static struct accessmap	nfs3_regaccess[] = {
+    {	NFS3_ACCESS_READ,	MAY_READ			},
+    {	NFS3_ACCESS_EXECUTE,	MAY_EXEC			},
+    {	NFS3_ACCESS_MODIFY,	MAY_WRITE|MAY_TRUNC		},
+    {	NFS3_ACCESS_EXTEND,	MAY_WRITE			},
+
+    {	0,			0				}
+};
+
+static struct accessmap	nfs3_diraccess[] = {
+    {	NFS3_ACCESS_READ,	MAY_READ			},
+    {	NFS3_ACCESS_LOOKUP,	MAY_EXEC			},
+    {	NFS3_ACCESS_MODIFY,	MAY_EXEC|MAY_WRITE|MAY_TRUNC	},
+    {	NFS3_ACCESS_EXTEND,	MAY_EXEC|MAY_WRITE		},
+    {	NFS3_ACCESS_DELETE,	MAY_REMOVE			},
+
+    {	0,			0				}
+};
+
+static struct accessmap	nfs3_anyaccess[] = {
+    /* XXX: should we try to cover read/write here for clients that
+     * rely on us to do their access checking for special files? */
+
+    {	0,			0				}
+};
+
+int
+nfsd_access(struct svc_rqst *rqstp, struct svc_fh *fhp, u32 *access)
+{
+	struct accessmap	*map;
+	struct svc_export	*export;
+	struct dentry		*dentry;
+	u32			query, result = 0;
+	int			error;
+
+	error = fh_verify(rqstp, fhp, 0, MAY_NOP);
+	if (error)
+		goto out;
+
+	export = fhp->fh_export;
+	dentry = fhp->fh_dentry;
+
+	if (S_ISREG(dentry->d_inode->i_mode)) {
+		map = nfs3_regaccess;
+	} else if (S_ISDIR(dentry->d_inode->i_mode)) {
+		map = nfs3_diraccess;
+	} else {
+		map = nfs3_anyaccess;
+	}
+
+	query = *access;
+	while (map->access) {
+		if (map->access & query) {
+			error = nfsd_permission(export, dentry, (map->how | NO_OWNER_OVERRIDE));
+			if (error == 0)
+				result |= map->access;
+			else if ((error == nfserr_perm) || (error == nfserr_acces)) {
+				/*
+				 *  This access type is denyed; but the 
+				 *  access query itself succeeds.
+				 */
+				error = 0;
+			} else {
+				/*
+				 *  Some fatal error.  Fail the query.
+				 */
+				goto out;
+			}
+		}
+		map++;
+	}
+	*access = result;
+
+out:
+	return error;
+}
+#endif
+
+
+
 /*
  * Open an existing file or directory.
- * The wflag argument indicates write access.
+ * The access argument indicates the type of open (read/write/lock)
  * N.B. After this call fhp needs an fh_put
  */
 int
 nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
-			int wflag, struct file *filp)
+			int access, struct file *filp)
 {
 	struct dentry	*dentry;
 	struct inode	*inode;
-	int		access, err;
+	int		err;
 
-	access = wflag? MAY_WRITE : MAY_READ;
 	err = fh_verify(rqstp, fhp, type, access);
 	if (err)
 		goto out;
@@ -368,24 +501,27 @@
 	if (!inode->i_op || !inode->i_op->default_file_ops)
 		goto out;
 
-	if (wflag && (err = get_write_access(inode)) != 0)
+	if ((access & MAY_WRITE) && (err = get_write_access(inode)) != 0)
 		goto out_nfserr;
 
 	memset(filp, 0, sizeof(*filp));
 	filp->f_op    = inode->i_op->default_file_ops;
 	filp->f_count = 1;
-	filp->f_flags = wflag? O_WRONLY : O_RDONLY;
-	filp->f_mode  = wflag? FMODE_WRITE : FMODE_READ;
 	filp->f_dentry = dentry;
-
-	if (wflag)
+	if (access & MAY_WRITE) {
+		filp->f_flags = O_WRONLY;
+		filp->f_mode  = FMODE_WRITE;
 		DQUOT_INIT(inode);
+	} else {
+		filp->f_flags = O_RDONLY;
+		filp->f_mode  = FMODE_READ;
+	}
 
 	err = 0;
 	if (filp->f_op && filp->f_op->open) {
 		err = filp->f_op->open(inode, filp);
 		if (err) {
-			if (wflag)
+			if (access & MAY_WRITE)
 				put_write_access(inode);
 
 			/* I nearly added put_filp() call here, but this filp
@@ -419,17 +555,33 @@
 		filp->f_op->release(inode, filp);
 	if (filp->f_mode & FMODE_WRITE) {
 		put_write_access(inode);
-		DQUOT_DROP(inode);
 	}
 }
 
 /*
  * Sync a file
+ * As this calls fsync (not fdatasync) there is no need for a write_inode
+ * after it.
  */
 void
-nfsd_sync(struct inode *inode, struct file *filp)
+nfsd_sync(struct file *filp)
 {
+	dprintk("nfsd: sync file %s\n", filp->f_dentry->d_name.name);
+	down(&filp->f_dentry->d_inode->i_sem);
 	filp->f_op->fsync(filp, filp->f_dentry);
+	up(&filp->f_dentry->d_inode->i_sem);
+}
+
+void
+nfsd_sync_dir(struct dentry *dp)
+{
+	struct inode *inode = dp->d_inode;
+	int (*fsync) (struct file *, struct dentry *);
+	
+	if (inode->i_op->default_file_ops
+	    && (fsync = inode->i_op->default_file_ops->fsync)) {
+		fsync(NULL, dp);
+	}
 }
 
 /*
@@ -478,7 +630,7 @@
 	int		err;
 	struct file	file;
 
-	err = nfsd_open(rqstp, fhp, S_IFREG, OPEN_READ, &file);
+	err = nfsd_open(rqstp, fhp, S_IFREG, MAY_READ, &file);
 	if (err)
 		goto out;
 	err = nfserr_perm;
@@ -543,11 +695,11 @@
 	uid_t			saved_euid;
 #endif
 
-	if (!cnt)
-		goto out;
-	err = nfsd_open(rqstp, fhp, S_IFREG, OPEN_WRITE, &file);
+	err = nfsd_open(rqstp, fhp, S_IFREG, MAY_WRITE, &file);
 	if (err)
 		goto out;
+	if (!cnt)
+		goto out_close;
 	err = nfserr_perm;
 	if (!file.f_op->write)
 		goto out_close;
@@ -560,11 +712,21 @@
 	 * Request sync writes if
 	 *  -	the sync export option has been set, or
 	 *  -	the client requested O_SYNC behavior (NFSv3 feature).
+	 *  -   The file system doesn't support fsync().
 	 * When gathered writes have been configured for this volume,
 	 * flushing the data to disk is handled separately below.
 	 */
+#ifdef CONFIG_NFSD_V3
+	if (rqstp->rq_vers == 2)
+		stable = EX_ISSYNC(exp);
+	else if (file.f_op->fsync == 0)
+	       stable = 1;
+	if (stable && !EX_WGATHER(exp))
+		file.f_flags |= O_SYNC;
+#else
 	if ((stable || (stable = EX_ISSYNC(exp))) && !EX_WGATHER(exp))
 		file.f_flags |= O_SYNC;
+#endif /* CONFIG_NFSD_V3 */
 
 	fh_lock(fhp);			/* lock inode */
 	file.f_pos = offset;		/* set write offset */
@@ -618,26 +780,25 @@
 		 */
 		if (EX_WGATHER(exp) && (inode->i_writecount > 1
 		 || (last_ino == inode->i_ino && last_dev == inode->i_dev))) {
-#if 0
-			interruptible_sleep_on_timeout(&inode->i_wait, 10 * HZ / 1000);
-#else
 			dprintk("nfsd: write defer %d\n", current->pid);
+			current->state = TASK_UNINTERRUPTIBLE;
 			schedule_timeout((HZ+99)/100);
+			current->state = TASK_RUNNING;
 			dprintk("nfsd: write resume %d\n", current->pid);
-#endif
 		}
 
 		if (inode->i_state & I_DIRTY) {
 			dprintk("nfsd: write sync %d\n", current->pid);
-			nfsd_sync(inode, &file);
-			write_inode_now(inode);
+			nfsd_sync(&file);
 		}
+#if 0
 		wake_up(&inode->i_wait);
+#endif
 		last_ino = inode->i_ino;
 		last_dev = inode->i_dev;
 	}
 
-	dprintk("nfsd: write complete\n");
+	dprintk("nfsd: write complete err=%d\n", err);
 	if (err >= 0)
 		err = 0;
 	else 
@@ -648,6 +809,38 @@
 	return err;
 }
 
+
+#ifdef CONFIG_NFSD_V3
+/*
+ * Commit all pendig writes to stable storage.
+ * Strictly speaking, we could sync just indicated the file region here,
+ * but there's currently no way we can ask the VFS to do so.
+ *
+ * We lock the file to make sure we return full WCC data to the client.
+ */
+int
+nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp,
+               off_t offset, unsigned long count)
+{
+	struct file	file;
+	int		err;
+
+	if ((err = nfsd_open(rqstp, fhp, S_IFREG, MAY_WRITE, &file)) != 0)
+		return err;
+
+	fh_lock(fhp);
+	if (file.f_op && file.f_op->fsync) {
+		file.f_op->fsync(&file, file.f_dentry);
+	} else {
+		err = nfserr_notsupp;
+	}
+	fh_unlock(fhp);
+
+	nfsd_close(&file);
+	return err;
+}
+#endif /* CONFIG_NFSD_V3 */
+
 /*
  * Create a file (regular, directory, device, fifo); UNIX sockets 
  * not yet implemented.
@@ -669,6 +862,7 @@
 	err = nfserr_perm;
 	if (!flen)
 		goto out;
+
 	err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE);
 	if (err)
 		goto out;
@@ -679,40 +873,42 @@
 	err = nfserr_notdir;
 	if(!dirp->i_op || !dirp->i_op->lookup)
 		goto out;
+
+	err = nfserr_exist;
+	if (isdotent(fname, flen))
+		goto out;
 	/*
 	 * Check whether the response file handle has been verified yet.
 	 * If it has, the parent directory should already be locked.
 	 */
-	if (!resfhp->fh_dverified) {
+	if (!resfhp->fh_dentry) {
 		dchild = lookup_dentry(fname, dget(dentry), 0);
 		err = PTR_ERR(dchild);
 		if (IS_ERR(dchild))
 			goto out_nfserr;
 		fh_compose(resfhp, fhp->fh_export, dchild);
+		/* Lock the parent and check for errors ... */
+		err = fh_lock_parent(fhp, dchild);
+		if (err)
+			goto out;
 	} else {
 		dchild = resfhp->fh_dentry;
-		if (!fhp->fh_locked)
+		if (!fhp->fh_locked) {
+			/* not actually possible */
 			printk(KERN_ERR
 				"nfsd_create: parent %s/%s not locked!\n",
 				dentry->d_parent->d_name.name,
 				dentry->d_name.name);
-	}
-	err = nfserr_exist;
-	if (dchild->d_inode)
-		goto out;
-	if (!fhp->fh_locked) {
-		/* Lock the parent and check for errors ... */
-		err = fh_lock_parent(fhp, dchild);
-		if (err)
+			err = -EIO;
 			goto out;
 	}
+	}
 	/*
 	 * Make sure the child dentry is still negative ...
 	 */
 	err = nfserr_exist;
 	if (dchild->d_inode) {
-		printk(KERN_WARNING
-			"nfsd_create: dentry %s/%s not negative!\n",
+		dprintk("nfsd_create: dentry %s/%s not negative!\n",
 			dentry->d_name.name, dchild->d_name.name);
 		goto out; 
 	}
@@ -739,24 +935,29 @@
 	case S_IFSOCK:
 		opfunc = dirp->i_op->mknod;
 		break;
+	default:
+	        printk("nfsd: bad file type %o in nfsd_create\n", type);
+		err = nfserr_inval;
 	}
 	if (!opfunc)
 		goto out;
 
 	if (!(iap->ia_valid & ATTR_MODE))
 		iap->ia_mode = 0;
+	iap->ia_mode = (iap->ia_mode & S_IALLUGO) | type;
 
 	/*
 	 * Call the dir op function to create the object.
 	 */
 	DQUOT_INIT(dirp);
 	err = opfunc(dirp, dchild, iap->ia_mode, rdev);
-	DQUOT_DROP(dirp);
 	if (err < 0)
 		goto out_nfserr;
 
-	if (EX_ISSYNC(fhp->fh_export))
-		write_inode_now(dirp);
+	if (EX_ISSYNC(fhp->fh_export)) {
+		nfsd_sync_dir(dentry);
+		write_inode_now(dchild->d_inode);
+	}
 
 	/*
 	 * Update the file handle to get the new inode info.
@@ -779,6 +980,129 @@
 	goto out;
 }
 
+#ifdef CONFIG_NFSD_V3
+/*
+ * NFSv3 version of nfsd_create
+ */
+int
+nfsd_create_v3(struct svc_rqst *rqstp, struct svc_fh *fhp,
+		char *fname, int flen, struct iattr *iap,
+		struct svc_fh *resfhp, int createmode, u32 *verifier)
+{
+	struct dentry	*dentry, *dchild;
+	struct inode	*dirp;
+	int		err;
+
+	err = nfserr_perm;
+	if (!flen)
+		goto out;
+	if (!(iap->ia_valid & ATTR_MODE))
+		iap->ia_mode = 0;
+	err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE);
+	if (err)
+		goto out;
+
+	dentry = fhp->fh_dentry;
+	dirp = dentry->d_inode;
+
+	/* Get all the sanity checks out of the way before
+	 * we lock the parent. */
+	err = nfserr_notdir;
+	if(!dirp->i_op || !dirp->i_op->lookup)
+		goto out;
+	err = nfserr_perm;
+	if(!dirp->i_op->create)
+		goto out;
+
+	err = nfserr_exist;
+	if (isdotent(fname, flen))
+		goto out;
+	/*
+	 * Compose the response file handle.
+	 */
+	dchild = lookup_dentry(fname, dget(dentry), 0);
+	err = PTR_ERR(dchild);
+	if(IS_ERR(dchild))
+		goto out_nfserr;
+	fh_compose(resfhp, fhp->fh_export, dchild);
+
+	/*
+	 * We must lock the directory before we check for the inode.
+	 */
+	err = fh_lock_parent(fhp, dchild);
+	if (err)
+	    goto out;
+
+	if (dchild->d_inode) {
+		err = 0;
+
+		if (resfhp->fh_handle.fh_ino == 0)
+		     /* inode might have been instantiated while we slept */
+		    fh_update(resfhp);
+		
+		switch (createmode) {
+		case NFS3_CREATE_UNCHECKED:
+			if (! S_ISREG(dchild->d_inode->i_mode))
+				err = nfserr_exist;
+			else {
+				iap->ia_valid &= ATTR_SIZE;
+				goto set_attr;
+			}
+			break;
+		case NFS3_CREATE_EXCLUSIVE:
+			if (   dchild->d_inode->i_mtime == verifier[0]
+			    && dchild->d_inode->i_atime == verifier[1]
+			    && dchild->d_inode->i_mode == S_IFREG
+			    && dchild->d_inode->i_size == 0 )
+				break;
+			 /* fallthru */
+		case NFS3_CREATE_GUARDED:
+			err = nfserr_exist;
+		}
+		goto out;
+	}
+
+	err = dirp->i_op->create(dirp, dchild, iap->ia_mode);
+	if (err < 0)
+		goto out_nfserr;
+
+	if (EX_ISSYNC(fhp->fh_export)) {
+		nfsd_sync_dir(dentry);
+		/* setattr will sync the child (or not) */
+	}
+
+	/*
+	 * Update the filehandle to get the new inode info.
+	 */
+	fh_update(resfhp);
+	err = 0;
+
+	if (createmode == NFS3_CREATE_EXCLUSIVE) {
+		/* Cram the verifier into atime/mtime */
+		iap->ia_valid = ATTR_MTIME|ATTR_ATIME|ATTR_MTIME_SET|ATTR_ATIME_SET;
+		iap->ia_mtime = verifier[0];
+		iap->ia_atime = verifier[1];
+	}
+
+	/* Set file attributes. Mode has already been set and
+	 * setting uid/gid works only for root. Irix appears to
+	 * send along the gid when it tries to implement setgid
+	 * directories via NFS. Clear out all that cruft.
+	 */
+ set_attr:
+	if ((iap->ia_valid &= ~(ATTR_UID|ATTR_GID|ATTR_MODE)) != 0)
+ 		err = nfsd_setattr(rqstp, resfhp, iap);
+
+ out:
+	fh_unlock(fhp);
+ 	return err;
+ 
+ out_nfserr:
+	err = nfserrno(-err);
+	goto out;
+}
+#endif /* CONFIG_NFSD_V3 */
+
 /*
  * Truncate a file.
  * The calling routines must make sure to update the ctime
@@ -817,15 +1141,16 @@
 		cap_clear(current->cap_effective);
 	}
 	err = notify_change(dentry, &newattrs);
-	if (current->fsuid != 0)
-		current->cap_effective = saved_cap;
 	if (!err) {
 		vmtruncate(inode, size);
 		if (inode->i_op && inode->i_op->truncate)
 			inode->i_op->truncate(inode);
 	}
+	if (current->fsuid != 0)
+		current->cap_effective = saved_cap;
 	put_write_access(inode);
-	DQUOT_DROP(inode);
+	if (EX_ISSYNC(fhp->fh_export))
+		nfsd_sync_dir(dentry);
 	fh_unlock(fhp);
 out_nfserr:
 	if (err)
@@ -859,7 +1184,10 @@
 		goto out;
 
 	UPDATE_ATIME(inode);
-	/* N.B. Why does this call need a get_fs()?? */
+	/* N.B. Why does this call need a get_fs()??
+	 * Remove the set_fs and watch the fireworks:-) --okir
+	 */
+
 	oldfs = get_fs(); set_fs(KERNEL_DS);
 	err = inode->i_op->readlink(dentry, buf, *lenp);
 	set_fs(oldfs);
@@ -884,7 +1212,8 @@
 nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
 				char *fname, int flen,
 				char *path,  int plen,
-				struct svc_fh *resfhp)
+				struct svc_fh *resfhp,
+				struct iattr *iap)
 {
 	struct dentry	*dentry, *dnew;
 	struct inode	*dirp;
@@ -899,9 +1228,11 @@
 		goto out;
 	dentry = fhp->fh_dentry;
 
-	err = nfserr_perm;
-	if (nfsd_iscovered(dentry, fhp->fh_export))
+	err = nfserr_exist;
+	if (isdotent(fname, flen))
 		goto out;
+
+	err = nfserr_perm;
 	dirp = dentry->d_inode;
 	if (!dirp->i_op	|| !dirp->i_op->symlink)
 		goto out;
@@ -922,10 +1253,20 @@
 	if (!dnew->d_inode) {
 		DQUOT_INIT(dirp);
 		err = dirp->i_op->symlink(dirp, dnew, path);
-		DQUOT_DROP(dirp);
 		if (!err) {
 			if (EX_ISSYNC(fhp->fh_export))
-				write_inode_now(dirp);
+				nfsd_sync_dir(dentry);
+			if (iap) {
+				iap->ia_valid &= ATTR_MODE /* ~(ATTR_MODE|ATTR_UID|ATTR_GID)*/;
+				if (iap->ia_valid) {
+					iap->ia_valid |= ATTR_CTIME;
+					iap->ia_mode = (iap->ia_mode&S_IALLUGO)
+						| S_IFLNK;
+					err = notify_change(dnew, iap);
+					if (!err && EX_ISSYNC(fhp->fh_export))
+						write_inode_now(dentry->d_inode);
+			       }
+			}
 		} else
 			err = nfserrno(-err);
 	}
@@ -949,7 +1290,7 @@
  */
 int
 nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
-				char *fname, int len, struct svc_fh *tfhp)
+				char *fname, int flen, struct svc_fh *tfhp)
 {
 	struct dentry	*ddir, *dnew, *dold;
 	struct inode	*dirp, *dest;
@@ -958,12 +1299,16 @@
 	err = fh_verify(rqstp, ffhp, S_IFDIR, MAY_CREATE);
 	if (err)
 		goto out;
-	err = fh_verify(rqstp, tfhp, S_IFREG, MAY_NOP);
+	err = fh_verify(rqstp, tfhp, -S_IFDIR, MAY_NOP);
 	if (err)
 		goto out;
 
 	err = nfserr_perm;
-	if (!len)
+	if (!flen)
+		goto out;
+
+	err = nfserr_exist;
+	if (isdotent(fname, flen))
 		goto out;
 
 	ddir = ffhp->fh_dentry;
@@ -987,10 +1332,7 @@
 	dold = tfhp->fh_dentry;
 	dest = dold->d_inode;
 
-	err = nfserr_acces;
-	if (nfsd_iscovered(ddir, ffhp->fh_export))
-		goto out_unlock;
-	/* FIXME: nxdev for NFSv3 */
+	err = (rqstp->rq_vers == 2) ? nfserr_acces : nfserr_xdev;
 	if (dirp->i_dev != dest->i_dev)
 		goto out_unlock;
 
@@ -1002,10 +1344,9 @@
 
 	DQUOT_INIT(dirp);
 	err = dirp->i_op->link(dold, dirp, dnew);
-	DQUOT_DROP(dirp);
 	if (!err) {
 		if (EX_ISSYNC(ffhp->fh_export)) {
-			write_inode_now(dirp);
+			nfsd_sync_dir(ddir);
 			write_inode_now(dest);
 		}
 	} else
@@ -1024,26 +1365,12 @@
 }
 
 /*
- * We need to do a check-parent every time
- * after we have locked the parent - to verify
- * that the parent is still our parent and
- * that we are still hashed onto it..
- *
- * This is requied in case two processes race
- * on removing (or moving) the same entry: the
- * parent lock will serialize them, but the
- * other process will be too late..
- */
-#define check_parent(dir, dentry) \
-	((dir) == (dentry)->d_parent->d_inode && !list_empty(&dentry->d_hash))
-
-/*
  * This follows the model of double_lock() in the VFS.
  */
 static inline void nfsd_double_down(struct semaphore *s1, struct semaphore *s2)
 {
 	if (s1 != s2) {
-		if ((unsigned long) s1 > (unsigned long) s2) {
+		if ((unsigned long) s1 < (unsigned long) s2) {
 			struct semaphore *tmp = s1;
 			s1 = s2;
 			s2 = tmp;
@@ -1085,12 +1412,12 @@
 	tdentry = tfhp->fh_dentry;
 	tdir = tdentry->d_inode;
 
-	/* N.B. We shouldn't need this ... dentry layer handles it */
+	err = (rqstp->rq_vers == 2) ? nfserr_acces : nfserr_xdev;
+	if (fdir->i_dev != tdir->i_dev)
+		goto out;
+
 	err = nfserr_perm;
-	if (!flen || (fname[0] == '.' && 
-	    (flen == 1 || (flen == 2 && fname[1] == '.'))) ||
-	    !tlen || (tname[0] == '.' && 
-	    (tlen == 1 || (tlen == 2 && tname[1] == '.'))))
+	if (!flen || isdotent(fname, flen) || !tlen || isdotent(tname, tlen))
 		goto out;
 
 	odentry = lookup_dentry(fname, dget(fdentry), 0);
@@ -1111,31 +1438,36 @@
 	 * Lock the parent directories.
 	 */
 	nfsd_double_down(&tdir->i_sem, &fdir->i_sem);
+
+#ifdef CONFIG_NFSD_V3
+	/* Fill in the pre-op attr for the wcc data for both 
+	 * tdir and fdir
+	 */ 
+	fill_pre_wcc(ffhp);
+	fill_pre_wcc(tfhp);
+#endif /* CONFIG_NFSD_V3 */
+
 	err = -ENOENT;
 	/* GAM3 check for parent changes after locking. */
-	if (check_parent(fdir, odentry) &&
-	    check_parent(tdir, ndentry)) {
+	if (nfsd_check_parent(fdentry, odentry) &&
+	    nfsd_check_parent(tdentry, ndentry)) {
 
 		err = vfs_rename(fdir, odentry, tdir, ndentry);
 		if (!err && EX_ISSYNC(tfhp->fh_export)) {
-			write_inode_now(fdir);
-			write_inode_now(tdir);
+			nfsd_sync_dir(tdentry);
+			nfsd_sync_dir(fdentry);
 		}
 	} else
 		dprintk("nfsd: Caught race in nfsd_rename");
-	DQUOT_DROP(fdir);
-	DQUOT_DROP(tdir);
 
+#ifdef CONFIG_NFSD_V3
+        /* Fill in the post-op attr for the wcc data for both 
+         * tdir and fdir
+         */
+	fill_post_wcc(ffhp);
+	fill_post_wcc(tfhp);
+#endif /* CONFIG_NFSD_V3 */
 	nfsd_double_up(&tdir->i_sem, &fdir->i_sem);
-
-	if (!err && odentry->d_inode) {
-		add_to_rename_cache(tdir->i_ino,
-				    odentry->d_inode->i_dev,
-				    fdir->i_ino,
-				    odentry->d_inode->i_ino);
-	} else {
-		printk(": no inode in rename or err: %d.\n", err);
-	}
 	dput(ndentry);
 
 out_dput_old:
@@ -1162,13 +1494,12 @@
 	struct inode	*dirp;
 	int		err;
 
-	/* N.B. We shouldn't need this test ... handled by dentry layer */
-	err = nfserr_acces;
-	if (!flen || isdotent(fname, flen))
-		goto out;
 	err = fh_verify(rqstp, fhp, S_IFDIR, MAY_REMOVE);
 	if (err)
 		goto out;
+	err = nfserr_acces;
+	if (!flen || isdotent(fname, flen))
+		goto out;
 
 	dentry = fhp->fh_dentry;
 	dirp = dentry->d_inode;
@@ -1177,13 +1508,13 @@
 	err = PTR_ERR(rdentry);
 	if (IS_ERR(rdentry))
 		goto out_nfserr;
+
 	if (!rdentry->d_inode) {
 		dput(rdentry);
 		err = nfserr_noent;
 		goto out;
 	}
 
-	expire_by_dentry(rdentry);
 
 	if (type != S_IFDIR) {
 		/* It's UNLINK */
@@ -1194,30 +1525,33 @@
 
 		err = vfs_unlink(dirp, rdentry);
 
-		DQUOT_DROP(dirp);
 		fh_unlock(fhp);
 
 		dput(rdentry);
-		expire_by_dentry(rdentry);
+
 	} else {
 		/* It's RMDIR */
 		/* See comments in fs/namei.c:do_rmdir */
 
 		rdentry->d_count++;
 		nfsd_double_down(&dirp->i_sem, &rdentry->d_inode->i_sem);
-		if (!fhp->fh_pre_mtime)
-			fhp->fh_pre_mtime = dirp->i_mtime;
+
+#ifdef CONFIG_NFSD_V3
+		fill_pre_wcc(fhp);
+#else
 		fhp->fh_locked = 1;
+#endif /* CONFIG_NFSD_V3 */
 
 		err = -ENOENT;
-		if (check_parent(dirp, rdentry))
+		if (nfsd_check_parent(dentry, rdentry))
 			err = vfs_rmdir(dirp, rdentry);
 
 		rdentry->d_count--;
-		DQUOT_DROP(dirp);
-		if (!fhp->fh_post_version)
-			fhp->fh_post_version = dirp->i_version;
+#ifdef CONFIG_NFSD_V3
+		fill_post_wcc(fhp);
+#else
 		fhp->fh_locked = 0;
+#endif /* CONFIG_NFSD_V3 */
 		nfsd_double_up(&dirp->i_sem, &rdentry->d_inode->i_sem);
 
 		dput(rdentry);
@@ -1225,9 +1559,11 @@
 
 	if (err)
 		goto out_nfserr;
-	if (EX_ISSYNC(fhp->fh_export))
-		write_inode_now(dirp);
-
+	if (EX_ISSYNC(fhp->fh_export)) {
+		down(&dentry->d_inode->i_sem);
+		nfsd_sync_dir(dentry);
+		up(&dentry->d_inode->i_sem);
+	}
 out:
 	return err;
 
@@ -1238,10 +1574,11 @@
 
 /*
  * Read entries from a directory.
+ * The verifier is an NFSv3 thing we ignore for now.
  */
 int
 nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, 
-			encode_dent_fn func, u32 *buffer, int *countp)
+             encode_dent_fn func, u32 *buffer, int *countp, u32 *verf)
 {
 	struct inode	*inode;
 	u32		*p;
@@ -1249,13 +1586,11 @@
 	struct file	file;
 	struct readdir_cd cd;
 
-	err = 0;
-	if (offset > ~(u32) 0)
-		goto out;
-
-	err = nfsd_open(rqstp, fhp, S_IFDIR, OPEN_READ, &file);
+	err = nfsd_open(rqstp, fhp, S_IFDIR, MAY_READ, &file);
 	if (err)
 		goto out;
+	if (offset > ~(u32) 0)
+		goto out_close;
 
 	err = nfserr_notdir;
 	if (!file.f_op->readdir)
@@ -1267,6 +1602,7 @@
 	cd.rqstp  = rqstp;
 	cd.buffer = buffer;
 	cd.buflen = *countp; /* count of words */
+	cd.dirfh  = fhp;
 
 	/*
 	 * Read the directory entries. This silly loop is necessary because
@@ -1296,8 +1632,14 @@
 	/* If we didn't fill the buffer completely, we're at EOF */
 	eof = !cd.eob;
 
-	if (cd.offset)
-		*cd.offset = htonl(file.f_pos);
+	if (cd.offset) {
+#ifdef CONFIG_NFSD_V3
+		if (rqstp->rq_vers == 3)
+			(void)enc64(cd.offset, file.f_pos);
+		else
+#endif /* CONFIG_NFSD_V3 */
+			*cd.offset = htonl(file.f_pos);
+	}
 
 	p = cd.buffer;
 	*p++ = 0;			/* no more entries */
@@ -1360,17 +1702,33 @@
 	struct inode	*inode = dentry->d_inode;
 	int		err;
 	kernel_cap_t	saved_cap = 0;
+	int		owneraccess;
+
+	/*
+	 *  Check if we are to use "owner may always access" semantics,
+	 *  then clean out the flag bit which controls this. It might be
+	 *  clearer to reverse the logic of this flag, but I didn't
+	 *  want to change a lot of code in a stable kernel - dhiggen.
+	 */
+
+	if (acc & NO_OWNER_OVERRIDE) {
+		owneraccess = 0;
+		acc &= ~NO_OWNER_OVERRIDE;
+	} else {
+		owneraccess = 1;
+	}
 
 	if (acc == MAY_NOP)
 		return 0;
 #if 0
-	dprintk("nfsd: permission 0x%x%s%s%s%s%s mode 0%o%s%s%s\n",
+	dprintk("nfsd: permission 0x%x%s%s%s%s%s%s mode 0%o%s%s%s\n",
 		acc,
 		(acc & MAY_READ)?	" read"  : "",
 		(acc & MAY_WRITE)?	" write" : "",
 		(acc & MAY_EXEC)?	" exec"  : "",
 		(acc & MAY_SATTR)?	" sattr" : "",
 		(acc & MAY_TRUNC)?	" trunc" : "",
+		(acc & MAY_LOCK)?	" lock"  : "",
 		inode->i_mode,
 		IS_IMMUTABLE(inode)?	" immut" : "",
 		IS_APPEND(inode)?	" append" : "",
@@ -1378,33 +1736,40 @@
 	dprintk("      owner %d/%d user %d/%d\n",
 		inode->i_uid, inode->i_gid, current->fsuid, current->fsgid);
 #endif
-#ifndef CONFIG_NFSD_SUN
-	if (dentry->d_mounts != dentry) {
-		return nfserr_perm;
-	}
-#endif
 
 	if (acc & (MAY_WRITE | MAY_SATTR | MAY_TRUNC)) {
 		if (EX_RDONLY(exp) || IS_RDONLY(inode))
 			return nfserr_rofs;
-		if (S_ISDIR(inode->i_mode) && nfsd_iscovered(dentry, exp))
-			return nfserr_perm;
 		if (/* (acc & MAY_WRITE) && */ IS_IMMUTABLE(inode))
 			return nfserr_perm;
 	}
 	if ((acc & MAY_TRUNC) && IS_APPEND(inode))
 		return nfserr_perm;
 
+	if (acc & MAY_LOCK) {
+		/* If we cannot rely on authentication in NLM requests,
+		 * just allow locks, others require read permission
+		 */
+		if (exp->ex_flags & NFSEXP_NOAUTHNLM)
+			return 0;
+		else
+			acc = MAY_READ;
+	}
 	/*
-	 * The file owner always gets access permission. This is to make
-	 * file access work even when the client has done a fchmod(fd, 0).
+	 * The file owner always gets access permission (except in the 
+	 * special case of a V3 ACCESS call, which is used for checking at 
+	 * open() time). This is to make file access work even when the 
+	 * client has done a fchmod(fd, 0).
 	 *
 	 * However, `cp foo bar' should fail nevertheless when bar is
 	 * readonly. A sensible way to do this might be to reject all
 	 * attempts to truncate a read-only file, because a creat() call
 	 * always implies file truncation.
+	 *  dhXXX we are not currently setting MAY_TRUNC from a SETATTR which
+	 *  changes the size, so this check is not enforced.  It probably
+	 *  should be?
 	 */
-	if (inode->i_uid == current->fsuid /* && !(acc & MAY_TRUNC) */)
+	if (owneraccess && inode->i_uid == current->fsuid /* && !(acc & MAY_TRUNC) */)
 		return 0;
 
 	if (current->fsuid != 0) {
diff -Naur pre9/linux/fs/super.c test/linux/fs/super.c
--- pre9/linux/fs/super.c	Mon Sep 18 13:58:34 2000
+++ test/linux/fs/super.c	Mon Sep 18 14:35:50 2000
@@ -566,6 +566,7 @@
 	s->s_flags = flags;
 	s->s_dirt = 0;
 	sema_init(&s->s_vfs_rename_sem,1);
+	sema_init(&s->s_nfsd_free_path_sem,1);
 	/* N.B. Should lock superblock now ... */
 	if (!type->read_super(s, data, silent))
 		goto out_fail;
@@ -1154,6 +1155,7 @@
 			sb->s_dev = get_unnamed_dev();
 			sb->s_flags = root_mountflags;
 			sema_init(&sb->s_vfs_rename_sem,1);
+			sema_init(&sb->s_nfsd_free_path_sem,1);
 			vfsmnt = add_vfsmnt(sb, "/dev/root", "/");
 			if (vfsmnt) {
 				if (nfs_root_mount(sb) >= 0) {
diff -Naur pre9/linux/include/linux/dcache.h test/linux/include/linux/dcache.h
--- pre9/linux/include/linux/dcache.h	Mon Sep 18 13:58:34 2000
+++ test/linux/include/linux/dcache.h	Mon Sep 18 14:35:50 2000
@@ -100,6 +100,12 @@
 					 * renamed" and has to be
 					 * deleted on the last dput()
 					 */
+#define	DCACHE_NFSD_DISCONNECTED 0x0004	/* This dentry is not currently connected to the
+					 * dcache tree. Its parent will either be itself,
+					 * or will have this flag as well.
+					 * If this dentry points to a directory, then
+					 * s_nfsd_free_path semaphore will be down
+					 */
 
 /*
  * d_drop() unhashes the entry from the parent
diff -Naur pre9/linux/include/linux/ext2_fs.h test/linux/include/linux/ext2_fs.h
--- pre9/linux/include/linux/ext2_fs.h	Mon Sep 18 13:58:34 2000
+++ test/linux/include/linux/ext2_fs.h	Mon Sep 18 14:48:28 2000
@@ -238,7 +238,7 @@
 		} masix1;
 	} osd1;				/* OS dependent 1 */
 	__u32	i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
-	__u32	i_version;	/* File version (for NFS) */
+	__u32	i_generation;	/* File version (for NFS) */
 	__u32	i_file_acl;	/* File ACL */
 	__u32	i_dir_acl;	/* Directory ACL */
 	__u32	i_faddr;	/* Fragment address */
diff -Naur pre9/linux/include/linux/ext2_fs_i.h test/linux/include/linux/ext2_fs_i.h
--- pre9/linux/include/linux/ext2_fs_i.h	Thu Apr  2 13:39:51 1998
+++ test/linux/include/linux/ext2_fs_i.h	Mon Sep 18 14:35:50 2000
@@ -29,7 +29,7 @@
 	__u32	i_file_acl;
 	__u32	i_dir_acl;
 	__u32	i_dtime;
-	__u32	i_version;
+	__u32	not_used_1;	/* FIX: not used/ 2.2 placeholder */
 	__u32	i_block_group;
 	__u32	i_next_alloc_block;
 	__u32	i_next_alloc_goal;
diff -Naur pre9/linux/include/linux/fs.h test/linux/include/linux/fs.h
--- pre9/linux/include/linux/fs.h	Mon Sep 18 13:58:34 2000
+++ test/linux/include/linux/fs.h	Mon Sep 18 14:46:59 2000
@@ -571,6 +571,15 @@
 	 * even looking at it. You had been warned.
 	 */
 	struct semaphore s_vfs_rename_sem;	/* Kludge */
+
+	/* The next field is used by knfsd when converting a (inode number based)
+	 * file handle into a dentry. As it builds a path in the dcache tree from
+	 * the bottom up, there may for a time be a subpath of dentrys which is not
+	 * connected to the main tree.  This semaphore ensure that there is only ever
+	 * one such free path per filesystem.  Note that unconnected files (or other
+	 * non-directories) are allowed, but not unconnected diretories.
+	 */
+	struct semaphore s_nfsd_free_path_sem;
 };
 
 /*
diff -Naur pre9/linux/include/linux/lockd/debug.h test/linux/include/linux/lockd/debug.h
--- pre9/linux/include/linux/lockd/debug.h	Tue May 11 10:36:15 1999
+++ test/linux/include/linux/lockd/debug.h	Mon Sep 18 15:04:07 2000
@@ -45,6 +45,7 @@
 #define NLMDBG_CLNTSUBS		0x0020
 #define NLMDBG_SVCSUBS		0x0040
 #define NLMDBG_HOSTCACHE	0x0080
+#define NLMDBG_XDR		0x0100
 #define NLMDBG_ALL		0x7fff
 
 
diff -Naur pre9/linux/include/linux/lockd/lockd.h test/linux/include/linux/lockd/lockd.h
--- pre9/linux/include/linux/lockd/lockd.h	Mon Sep 18 13:58:34 2000
+++ test/linux/include/linux/lockd/lockd.h	Mon Sep 18 15:04:07 2000
@@ -17,6 +17,7 @@
 #include <linux/nfsd/nfsfh.h>
 #include <linux/lockd/bind.h>
 #include <linux/lockd/xdr.h>
+#include <linux/lockd/xdr4.h>
 #include <linux/lockd/debug.h>
 
 /*
@@ -112,6 +113,7 @@
  */
 extern struct rpc_program	nlm_program;
 extern struct svc_procedure	nlmsvc_procedures[];
+extern struct svc_procedure     nlmsvc_procedures4[];
 extern unsigned long		nlmsvc_grace_period;
 extern unsigned long		nlmsvc_timeout;
 
diff -Naur pre9/linux/include/linux/lockd/xdr4.h test/linux/include/linux/lockd/xdr4.h
--- pre9/linux/include/linux/lockd/xdr4.h	Wed Dec 31 16:00:00 1969
+++ test/linux/include/linux/lockd/xdr4.h	Mon Sep 18 15:04:07 2000
@@ -0,0 +1,43 @@
+/*
+ * linux/include/linux/lockd/xdr.h
+ *
+ * XDR types for the NLM protocol
+ *
+ * Copyright (C) 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef LOCKD_XDR4_H
+#define LOCKD_XDR4_H
+
+#include <linux/fs.h>
+#include <linux/nfs.h>
+#include <linux/sunrpc/xdr.h>
+#include <linux/lockd/xdr.h>
+
+extern u32	nlm4_granted, nlm4_lck_denied, nlm4_lck_denied_nolocks,
+		nlm4_lck_blocked, nlm4_lck_denied_grace_period, nlm4_deadlock,
+		nlm4_rofs, nlm4_stale_fh, nlm4_fbig, nlm4_failed;
+
+#define NLMSVC_XDRSIZE		sizeof(struct nlm_args)
+
+int	nlm4svc_decode_testargs(struct svc_rqst *, u32 *, struct nlm_args *);
+int	nlm4svc_encode_testres(struct svc_rqst *, u32 *, struct nlm_res *);
+int	nlm4svc_decode_lockargs(struct svc_rqst *, u32 *, struct nlm_args *);
+int	nlm4svc_decode_cancargs(struct svc_rqst *, u32 *, struct nlm_args *);
+int	nlm4svc_decode_unlockargs(struct svc_rqst *, u32 *, struct nlm_args *);
+int	nlm4svc_encode_res(struct svc_rqst *, u32 *, struct nlm_res *);
+int	nlm4svc_decode_res(struct svc_rqst *, u32 *, struct nlm_res *);
+int	nlm4svc_encode_void(struct svc_rqst *, u32 *, void *);
+int	nlm4svc_decode_void(struct svc_rqst *, u32 *, void *);
+int	nlm4svc_decode_shareargs(struct svc_rqst *, u32 *, struct nlm_args *);
+int	nlm4svc_encode_shareres(struct svc_rqst *, u32 *, struct nlm_res *);
+int	nlm4svc_decode_notify(struct svc_rqst *, u32 *, struct nlm_args *);
+int	nlm4svc_decode_reboot(struct svc_rqst *, u32 *, struct nlm_reboot *);
+/*
+int	nlmclt_encode_testargs(struct rpc_rqst *, u32 *, struct nlm_args *);
+int	nlmclt_encode_lockargs(struct rpc_rqst *, u32 *, struct nlm_args *);
+int	nlmclt_encode_cancargs(struct rpc_rqst *, u32 *, struct nlm_args *);
+int	nlmclt_encode_unlockargs(struct rpc_rqst *, u32 *, struct nlm_args *);
+ */
+
+#endif /* LOCKD_XDR4_H */
diff -Naur pre9/linux/include/linux/nfsd/cache.h test/linux/include/linux/nfsd/cache.h
--- pre9/linux/include/linux/nfsd/cache.h	Mon Dec 28 14:09:59 1998
+++ test/linux/include/linux/nfsd/cache.h	Mon Sep 18 14:49:01 2000
@@ -25,9 +25,11 @@
 	unsigned char		c_state,	/* unused, inprog, done */
 				c_type,		/* status, buffer */
 				c_secure : 1;	/* req came from port < 1024 */
-	struct in_addr		c_client;
+	struct sockaddr_in	c_addr;
 	u32			c_xid;
+	u32			c_prot;
 	u32			c_proc;
+	u32			c_vers;
 	unsigned long		c_timestamp;
 	union {
 		struct svc_buf	u_buffer;
diff -Naur pre9/linux/include/linux/nfsd/const.h test/linux/include/linux/nfsd/const.h
--- pre9/linux/include/linux/nfsd/const.h	Mon Sep 18 13:58:34 2000
+++ test/linux/include/linux/nfsd/const.h	Mon Sep 18 14:35:50 2000
@@ -1,19 +1,14 @@
 /*
- * include/linux/nfsd/nfsconst.h
+ * include/linux/nfsd/const.h
  *
  * Various constants related to NFS.
  *
- * Copyright (C) 1995 Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1995-1997 Olaf Kirch <okir@monad.swb.de>
  */
 
-#ifndef __NFSCONST_H__
-#define __NFSCONST_H__
+#ifndef _LINUX_NFSD_CONST_H
+#define _LINUX_NFSD_CONST_H
 
-#include <linux/limits.h>
-#include <linux/types.h>
-#include <linux/unistd.h>
-#include <linux/dirent.h>
-#include <linux/fs.h>
 #include <linux/nfs.h>
 
 #define NFS_FHSIZE		32
@@ -32,12 +27,10 @@
 
 #define NFS2_MAXPATHLEN		1024
 #define NFS2_MAXNAMLEN		255
-#define NFS2_FHSIZE		NFS_FHSIZE
 #define NFS2_COOKIESIZE		4
 
 #define NFS3_MAXPATHLEN		PATH_MAX
 #define NFS3_MAXNAMLEN		NAME_MAX
-#define NFS3_FHSIZE		NFS_FHSIZE
 #define NFS3_COOKIEVERFSIZE	8
 #define NFS3_CREATEVERFSIZE	8
 #define NFS3_WRITEVERFSIZE	8
@@ -48,43 +41,6 @@
 # define NFS_SUPER_MAGIC	0x6969
 #endif
 
-/*
- * NFS stats. The good thing with these values is that NFSv3 errors are
- * a superset of NFSv2 errors (with the exception of NFSERR_WFLUSH which
- * no-one uses anyway), so we can happily mix code as long as we make sure
- * no NFSv3 errors are returned to NFSv2 clients.
- */
-#define NFS_OK			0		/* v2 v3 */
-#define NFSERR_PERM		1		/* v2 v3 */
-#define NFSERR_NOENT		2		/* v2 v3 */
-#define NFSERR_IO		5		/* v2 v3 */
-#define NFSERR_NXIO		6		/* v2 v3 */
-#define NFSERR_ACCES		13		/* v2 v3 */
-#define NFSERR_EXIST		17		/* v2 v3 */
-#define NFSERR_XDEV		18		/*    v3 */
-#define NFSERR_NODEV		19		/* v2 v3 */
-#define NFSERR_NOTDIR		20		/* v2 v3 */
-#define NFSERR_ISDIR		21		/* v2 v3 */
-#define NFSERR_INVAL		22		/*    v3 */
-#define NFSERR_FBIG		27		/* v2 v3 */
-#define NFSERR_NOSPC		28		/* v2 v3 */
-#define NFSERR_ROFS		30		/* v2 v3 */
-#define NFSERR_MLINK		31		/*    v3 */
-#define NFSERR_NAMETOOLONG	63		/* v2 v3 */
-#define NFSERR_NOTEMPTY		66		/* v2 v3 */
-#define NFSERR_DQUOT		69		/* v2 v3 */
-#define NFSERR_STALE		70		/* v2 v3 */
-#define NFSERR_REMOTE		71		/*    v3 */
-#define NFSERR_WFLUSH		99		/* v2    */
-#define NFSERR_BADHANDLE	10001		/*    v3 */
-#define NFSERR_NOT_SYNC		10002		/*    v3 */
-#define NFSERR_BAD_COOKIE	10003		/*    v3 */
-#define NFSERR_NOTSUPP		10004		/*    v3 */
-#define NFSERR_TOOSMALL		10005		/*    v3 */
-#define NFSERR_SERVERFAULT	10006		/*    v3 */
-#define NFSERR_BADTYPE		10007		/*    v3 */
-#define NFSERR_JUKEBOX		10008		/*    v3 */
-
 #endif /* __KERNEL__ */
 
-#endif /* __NFSCONST_H__ */
+#endif /* _LINUX_NFSD_CONST_H */
diff -Naur pre9/linux/include/linux/nfsd/export.h test/linux/include/linux/nfsd/export.h
--- pre9/linux/include/linux/nfsd/export.h	Tue May 11 10:36:17 1999
+++ test/linux/include/linux/nfsd/export.h	Mon Sep 18 14:49:01 2000
@@ -4,16 +4,17 @@
  * Public declarations for NFS exports. The definitions for the
  * syscall interface are in nfsctl.h
  *
- * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1995-1997 Olaf Kirch <okir@monad.swb.de>
  */
 
 #ifndef NFSD_EXPORT_H
 #define NFSD_EXPORT_H
 
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/fs.h>
+#include <asm/types.h>
+#ifdef __KERNEL__
+# include <linux/types.h>
+# include <linux/in.h>
+#endif
 
 /*
  * Important limits for the exports stuff.
@@ -34,8 +35,10 @@
 #define NFSEXP_UIDMAP		0x0040
 #define NFSEXP_KERBEROS		0x0080		/* not available */
 #define NFSEXP_SUNSECURE	0x0100
-#define NFSEXP_CROSSMNT		0x0200		/* not available */
-#define NFSEXP_ALLFLAGS		0x03FF
+#define NFSEXP_CROSSMNT		0x0200
+#define NFSEXP_NOSUBTREECHECK	0x0400
+#define	NFSEXP_NOAUTHNLM	0x0800		/* Don't authenticate NLM requests - just trust */
+#define NFSEXP_ALLFLAGS		0x0FFF
 
 
 #ifdef __KERNEL__
diff -Naur pre9/linux/include/linux/nfsd/nfsd.h test/linux/include/linux/nfsd/nfsd.h
--- pre9/linux/include/linux/nfsd/nfsd.h	Tue May 11 10:36:17 1999
+++ test/linux/include/linux/nfsd/nfsd.h	Mon Sep 18 14:49:01 2000
@@ -4,7 +4,7 @@
  * Hodge-podge collection of knfsd-related stuff.
  * I will sort this out later.
  *
- * Copyright (C) 1995 Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1995-1997 Olaf Kirch <okir@monad.swb.de>
  */
 
 #ifndef LINUX_NFSD_NFSD_H
@@ -24,18 +24,21 @@
 /*
  * nfsd version
  */
-#define NFSD_VERSION		"0.4"
+#define NFSD_VERSION		"0.5"
 
 #ifdef __KERNEL__
 /*
  * Special flags for nfsd_permission. These must be different from MAY_READ,
  * MAY_WRITE, and MAY_EXEC.
  */
-#define MAY_NOP			0
-#define MAY_SATTR		8
-#define MAY_TRUNC		16
-#if (MAY_SATTR | MAY_TRUNC) & (MAY_READ | MAY_WRITE | MAY_EXEC)
-# error "please use a different value for MAY_SATTR or MAY_TRUNC."
+#define MAY_NOP			0x00000000
+#define MAY_SATTR		0x00000008
+#define MAY_TRUNC		0x00000010
+#define MAY_LOCK		0x00000020
+#define NO_OWNER_OVERRIDE	0x00000040
+
+#if (MAY_SATTR | MAY_TRUNC | MAY_LOCK | NO_OWNER_OVERRIDE) & (MAY_READ | MAY_WRITE | MAY_EXEC)
+# error "please use a different value for MAY_SATTR or MAY_TRUNC or MAY_LOCK or NO_OWNER_OVERRIDE."
 #endif
 #define MAY_CREATE		(MAY_EXEC|MAY_WRITE)
 #define MAY_REMOVE		(MAY_EXEC|MAY_WRITE|MAY_TRUNC)
@@ -61,6 +64,9 @@
  * Procedure table for NFSv2
  */
 extern struct svc_procedure	nfsd_procedures2[];
+#ifdef CONFIG_NFSD_V3
+extern struct svc_procedure	nfsd_procedures3[];
+#endif /* CONFIG_NFSD_V3 */
 extern struct svc_program	nfsd_program;
 
 /*
@@ -74,11 +80,20 @@
 void		nfsd_racache_shutdown(void);
 int		nfsd_lookup(struct svc_rqst *, struct svc_fh *,
 				const char *, int, struct svc_fh *);
+#ifdef CONFIG_NFSD_V3
+int		nfsd_access(struct svc_rqst *, struct svc_fh *, u32 *);
+#endif /* CONFIG_NFSD_V3 */
 int		nfsd_setattr(struct svc_rqst *, struct svc_fh *,
 				struct iattr *);
 int		nfsd_create(struct svc_rqst *, struct svc_fh *,
 				char *name, int len, struct iattr *attrs,
 				int type, dev_t rdev, struct svc_fh *res);
+#ifdef CONFIG_NFSD_V3
+int		nfsd_create_v3(struct svc_rqst *, struct svc_fh *,
+				char *name, int len, struct iattr *attrs,
+				struct svc_fh *res, int createmode,
+				u32 *verifier);
+#endif /* CONFIG_NFSD_V3 */
 int		nfsd_open(struct svc_rqst *, struct svc_fh *, int,
 				int, struct file *);
 void		nfsd_close(struct file *);
@@ -90,7 +105,7 @@
 				char *, int *);
 int		nfsd_symlink(struct svc_rqst *, struct svc_fh *,
 				char *name, int len, char *path, int plen,
-				struct svc_fh *res);
+				struct svc_fh *res, struct iattr *);
 int		nfsd_link(struct svc_rqst *, struct svc_fh *,
 				char *, int, struct svc_fh *);
 int		nfsd_rename(struct svc_rqst *,
@@ -104,9 +119,13 @@
 				unsigned long size);
 int		nfsd_readdir(struct svc_rqst *, struct svc_fh *,
 				loff_t, encode_dent_fn,
-				u32 *buffer, int *countp);
+				u32 *buffer, int *countp, u32 *verf);
 int		nfsd_statfs(struct svc_rqst *, struct svc_fh *,
 				struct statfs *);
+#ifdef CONFIG_NFSD_V3
+int		nfsd_commit(struct svc_rqst *, struct svc_fh *,
+				off_t, unsigned long);
+#endif /* CONFIG_NFSD_V3 */
 int		nfsd_notify_change(struct inode *, struct iattr *);
 int		nfsd_permission(struct svc_export *, struct dentry *, int);
 
@@ -146,6 +165,7 @@
 		nfserr_rofs,
 		nfserr_mlink,
 		nfserr_nametoolong,
+		nfserr_notempty,
 		nfserr_dquot,
 		nfserr_stale,
 		nfserr_remote,
diff -Naur pre9/linux/include/linux/nfsd/nfsfh.h test/linux/include/linux/nfsd/nfsfh.h
--- pre9/linux/include/linux/nfsd/nfsfh.h	Tue Jan  4 10:12:25 2000
+++ test/linux/include/linux/nfsd/nfsfh.h	Mon Sep 18 14:49:01 2000
@@ -11,12 +11,15 @@
  * Copyright (C) 1995-1999 Olaf Kirch <okir@monad.swb.de>
  */
 
-#ifndef NFSD_FH_H
-#define NFSD_FH_H
+#ifndef _LINUX_NFSD_FH_H
+#define _LINUX_NFSD_FH_H
 
-#include <linux/types.h>
-#include <linux/string.h>
-#include <linux/fs.h>
+#include <asm/types.h>
+#ifdef __KERNEL__
+# include <linux/types.h>
+# include <linux/string.h>
+# include <linux/fs.h>
+#endif
 #include <linux/nfsd/const.h>
 #include <linux/nfsd/debug.h>
 
@@ -83,12 +86,32 @@
 	struct knfs_fh		fh_handle;	/* FH data */
 	struct dentry *		fh_dentry;	/* validated dentry */
 	struct svc_export *	fh_export;	/* export pointer */
-	size_t			fh_pre_size;	/* size before operation */
+#ifdef CONFIG_NFSD_V3
+	unsigned char		fh_post_saved;	/* post-op attrs saved */
+	unsigned char		fh_pre_saved;	/* pre-op attrs saved */
+#endif /* CONFIG_NFSD_V3 */
+	unsigned char		fh_locked;	/* inode locked by us */
+
+#ifdef CONFIG_NFSD_V3
+	/* Pre-op attributes saved during fh_lock */
+	__u64			fh_pre_size;	/* size before operation */
 	time_t			fh_pre_mtime;	/* mtime before oper */
 	time_t			fh_pre_ctime;	/* ctime before oper */
-	unsigned long		fh_post_version;/* inode version after oper */
-	unsigned char		fh_locked;	/* inode locked by us */
-	unsigned char		fh_dverified;	/* dentry has been checked */
+
+	/* Post-op attributes saved in fh_unlock */
+	umode_t			fh_post_mode;	/* i_mode */
+	nlink_t			fh_post_nlink;	/* i_nlink */
+	uid_t			fh_post_uid;	/* i_uid */
+	gid_t			fh_post_gid;	/* i_gid */
+	__u64			fh_post_size;	/* i_size */
+	unsigned long		fh_post_blocks; /* i_blocks */
+	unsigned long		fh_post_blksize;/* i_blksize */
+	kdev_t			fh_post_rdev;	/* i_rdev */
+	time_t			fh_post_atime;	/* i_atime */
+	time_t			fh_post_mtime;	/* i_mtime */
+	time_t			fh_post_ctime;	/* i_ctime */
+#endif /* CONFIG_NFSD_V3 */
+
 } svc_fh;
 
 /*
@@ -105,18 +128,11 @@
 void	fh_compose(struct svc_fh *, struct svc_export *, struct dentry *);
 void	fh_update(struct svc_fh *);
 void	fh_put(struct svc_fh *);
-void	nfsd_fh_flush(kdev_t);
-void	nfsd_fh_init(void);
-void	nfsd_fh_shutdown(void);
-void	nfsd_fh_free(void);
-
-void	expire_all(void);
-void	expire_by_dentry(struct dentry *);
 
 static __inline__ struct svc_fh *
 fh_copy(struct svc_fh *dst, struct svc_fh *src)
 {
-	if (src->fh_dverified || src->fh_locked) {
+	if (src->fh_dentry || src->fh_locked) {
 		struct dentry *dentry = src->fh_dentry;
 		printk(KERN_ERR "fh_copy: copying %s/%s, already verified!\n",
 			dentry->d_parent->d_name.name, dentry->d_name.name);
@@ -133,6 +149,53 @@
 	return fhp;
 }
 
+#ifdef CONFIG_NFSD_V3
+/*
+ * Fill in the pre_op attr for the wcc data
+ */
+static inline void
+fill_pre_wcc(struct svc_fh *fhp)
+{
+        struct inode    *inode;
+
+        inode = fhp->fh_dentry->d_inode;
+        if (!fhp->fh_pre_saved) {
+                fhp->fh_pre_mtime = inode->i_mtime;
+                        fhp->fh_pre_ctime = inode->i_ctime;
+                        fhp->fh_pre_size  = inode->i_size;
+                        fhp->fh_pre_saved = 1;
+        }
+        fhp->fh_locked = 1;
+}
+
+/*
+ * Fill in the post_op attr for the wcc data
+ */
+static inline void
+fill_post_wcc(struct svc_fh *fhp)
+{
+        struct inode    *inode = fhp->fh_dentry->d_inode;
+
+        if (fhp->fh_post_saved)
+                printk("nfsd: inode locked twice during operation.\n");
+
+        fhp->fh_post_mode       = inode->i_mode;
+        fhp->fh_post_nlink      = inode->i_nlink;
+        fhp->fh_post_uid        = inode->i_uid;
+        fhp->fh_post_gid        = inode->i_gid;
+        fhp->fh_post_size       = inode->i_size;
+        fhp->fh_post_blksize    = inode->i_blksize;
+        fhp->fh_post_blocks     = inode->i_blocks;
+        fhp->fh_post_rdev       = inode->i_rdev;
+        fhp->fh_post_atime      = inode->i_atime;
+        fhp->fh_post_mtime      = inode->i_mtime;
+        fhp->fh_post_ctime      = inode->i_ctime;
+        fhp->fh_post_saved      = 1;
+        fhp->fh_locked          = 0;
+}
+#endif /* CONFIG_NFSD_V3 */
+
+
 /*
  * Lock a file handle/inode
  */
@@ -142,11 +205,10 @@
 	struct dentry	*dentry = fhp->fh_dentry;
 	struct inode	*inode;
 
-	/*
 	dfprintk(FILEOP, "nfsd: fh_lock(%x/%ld) locked = %d\n",
-			SVCFH_DEV(fhp), SVCFH_INO(fhp), fhp->fh_locked);
-	 */
-	if (!fhp->fh_dverified) {
+			SVCFH_DEV(fhp), (long)SVCFH_INO(fhp), fhp->fh_locked);
+
+	if (!fhp->fh_dentry) {
 		printk(KERN_ERR "fh_lock: fh not verified!\n");
 		return;
 	}
@@ -158,9 +220,11 @@
 
 	inode = dentry->d_inode;
 	down(&inode->i_sem);
-	if (!fhp->fh_pre_mtime)
-		fhp->fh_pre_mtime = inode->i_mtime;
+#ifdef CONFIG_NFSD_V3
+	fill_pre_wcc(fhp);
+#else
 	fhp->fh_locked = 1;
+#endif /* CONFIG_NFSD_V3 */
 }
 
 /*
@@ -169,25 +233,23 @@
 static inline void
 fh_unlock(struct svc_fh *fhp)
 {
-	if (!fhp->fh_dverified)
+	if (!fhp->fh_dentry)
 		printk(KERN_ERR "fh_unlock: fh not verified!\n");
 
 	if (fhp->fh_locked) {
+#ifdef CONFIG_NFSD_V3
+		fill_post_wcc(fhp);
+		up(&fhp->fh_dentry->d_inode->i_sem);
+#else
 		struct dentry *dentry = fhp->fh_dentry;
 		struct inode *inode = dentry->d_inode;
 
-		if (!fhp->fh_post_version)
-			fhp->fh_post_version = inode->i_version;
 		fhp->fh_locked = 0;
 		up(&inode->i_sem);
+#endif /* CONFIG_NFSD_V3 */
 	}
 }
-
-/*
- * This is a long term cache to help find renamed files.
- */
-void add_to_rename_cache(ino_t new_dirino, kdev_t dev, ino_t dirino, ino_t ino);
-
 #endif /* __KERNEL__ */
 
-#endif /* NFSD_FH_H */
+
+#endif /* _LINUX_NFSD_FH_H */
diff -Naur pre9/linux/include/linux/nfsd/stats.h test/linux/include/linux/nfsd/stats.h
--- pre9/linux/include/linux/nfsd/stats.h	Tue Jan  4 10:12:25 2000
+++ test/linux/include/linux/nfsd/stats.h	Mon Sep 18 14:35:50 2000
@@ -13,12 +13,11 @@
 	unsigned int	rchits;		/* repcache hits */
 	unsigned int	rcmisses;	/* repcache hits */
 	unsigned int	rcnocache;	/* uncached reqs */
-	unsigned int	fh_cached;	/* dentry cached */
-	unsigned int	fh_valid;	/* dentry validated */
-	unsigned int	fh_fixup;	/* dentry fixup validated */
 	unsigned int	fh_lookup;	/* new lookup required */
+	unsigned int	fh_anon;	
+	unsigned int	fh_nocache_nondir;	
+	unsigned int	fh_nocache_dir;	
 	unsigned int	fh_stale;	/* FH stale error */
-	unsigned int    fh_concurrent;	/* concurrent request */
 };
 
 #ifdef __KERNEL__
diff -Naur pre9/linux/include/linux/nfsd/syscall.h test/linux/include/linux/nfsd/syscall.h
--- pre9/linux/include/linux/nfsd/syscall.h	Tue Jan  4 10:12:25 2000
+++ test/linux/include/linux/nfsd/syscall.h	Mon Sep 18 14:49:02 2000
@@ -3,15 +3,18 @@
  *
  * This file holds all declarations for the knfsd syscall interface.
  *
- * Copyright (C) 1995 Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1995-1997 Olaf Kirch <okir@monad.swb.de>
  */
 
 #ifndef NFSD_SYSCALL_H
 #define NFSD_SYSCALL_H
 
-#include <linux/config.h>
-#include <linux/types.h>
-#include <linux/socket.h>
+#include <asm/types.h>
+#ifdef __KERNEL__
+# include <linux/config.h>
+# include <linux/types.h>
+# include <linux/in.h>
+#endif 
 #include <linux/posix_types.h>
 #include <linux/nfsd/const.h>
 #include <linux/nfsd/export.h>
diff -Naur pre9/linux/include/linux/nfsd/xdr3.h test/linux/include/linux/nfsd/xdr3.h
--- pre9/linux/include/linux/nfsd/xdr3.h	Mon Apr  7 11:35:32 1997
+++ test/linux/include/linux/nfsd/xdr3.h	Mon Sep 18 14:49:04 2000
@@ -3,17 +3,18 @@
  *
  * XDR types for NFSv3 in nfsd.
  *
- * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1996-1998, Olaf Kirch <okir@monad.swb.de>
  */
 
-#ifndef LINUX_NFSD_XDR3_H
-#define LINUX_NFSD_XDR3_H
+#ifndef _LINUX_NFSD_XDR3_H
+#define _LINUX_NFSD_XDR3_H
 
 #include <linux/nfsd/xdr.h>
 
 struct nfsd3_sattrargs {
 	struct svc_fh		fh;
 	struct iattr		attrs;
+	int			check_guard;
 	time_t			guardtime;
 };
 
@@ -88,7 +89,7 @@
 
 struct nfsd3_readdirargs {
 	struct svc_fh		fh;
-	__u32			cookie;
+	__u64			cookie;
 	__u32			dircount;
 	__u32			count;
 	__u32 *			verf;
@@ -97,7 +98,7 @@
 struct nfsd3_commitargs {
 	struct svc_fh		fh;
 	__u64			offset;
-	__u64			count;
+	__u32			count;
 };
 
 struct nfsd3_attrstat {
@@ -105,7 +106,8 @@
 	struct svc_fh		fh;
 };
 
-struct nfsd3_lookupres  {
+/* LOOKUP, CREATE, MKDIR, SYMLINK, MKNOD */
+struct nfsd3_diropres  {
 	__u32			status;
 	struct svc_fh		dirfh;
 	struct svc_fh		fh;
@@ -137,12 +139,6 @@
 	int			committed;
 };
 
-struct nfsd3_createres {
-	__u32			status;
-	struct svc_fh		dirfh;
-	struct svc_fh		fh;
-};
-
 struct nfsd3_renameres {
 	__u32			status;
 	struct svc_fh		ffh;
@@ -158,10 +154,11 @@
 struct nfsd3_readdirres {
 	__u32			status;
 	struct svc_fh		fh;
-	__u32 *			list_end;
+	int			count;
+	__u32			verf[2];
 };
 
-struct nfsd3_statfsres {
+struct nfsd3_fsstatres {
 	__u32			status;
 	struct statfs		stats;
 	__u32			invarsec;
@@ -184,6 +181,8 @@
 	__u32			status;
 	__u32			p_link_max;
 	__u32			p_name_max;
+	__u32			p_no_trunc;
+	__u32			p_chown_restricted;
 	__u32			p_case_insensitive;
 	__u32			p_case_preserving;
 };
@@ -194,7 +193,7 @@
 };
 
 /* dummy type for release */
-struct nfsd3_fhandle2 {
+struct nfsd3_fhandle_pair {
 	__u32			dummy;
 	struct svc_fh		fh1;
 	struct svc_fh		fh2;
@@ -213,16 +212,15 @@
 	struct nfsd3_linkargs		linkargs;
 	struct nfsd3_symlinkargs	symlinkargs;
 	struct nfsd3_readdirargs	readdirargs;
-	struct nfsd3_lookupres 		lookupres;
+	struct nfsd3_diropres 		diropres;
 	struct nfsd3_accessres		accessres;
 	struct nfsd3_readlinkres	readlinkres;
 	struct nfsd3_readres		readres;
 	struct nfsd3_writeres		writeres;
-	struct nfsd3_createres		createres;
 	struct nfsd3_renameres		renameres;
 	struct nfsd3_linkres		linkres;
 	struct nfsd3_readdirres		readdirres;
-	struct nfsd3_statfsres		statfsres;
+	struct nfsd3_fsstatres		fsstatres;
 	struct nfsd3_fsinfores		fsinfores;
 	struct nfsd3_pathconfres	pathconfres;
 	struct nfsd3_commitres		commitres;
@@ -230,39 +228,87 @@
 
 #define NFS3_SVC_XDRSIZE		sizeof(union nfsd3_xdrstore)
 
-void nfsxdr_init(void);
-
 int nfs3svc_decode_fhandle(struct svc_rqst *, u32 *, struct svc_fh *);
-int nfs3svc_decode_sattr3args(struct svc_rqst *, u32 *,
+int nfs3svc_decode_sattrargs(struct svc_rqst *, u32 *,
 				struct nfsd3_sattrargs *);
-int nfs3svc_decode_dirop3args(struct svc_rqst *, u32 *,
+int nfs3svc_decode_diropargs(struct svc_rqst *, u32 *,
 				struct nfsd3_diropargs *);
-int nfs3svc_decode_read3args(struct svc_rqst *, u32 *,
+int nfs3svc_decode_accessargs(struct svc_rqst *, u32 *,
+				struct nfsd3_accessargs *);
+int nfs3svc_decode_readargs(struct svc_rqst *, u32 *,
 				struct nfsd3_readargs *);
-int nfs3svc_decode_write3args(struct svc_rqst *, u32 *,
+int nfs3svc_decode_writeargs(struct svc_rqst *, u32 *,
 				struct nfsd3_writeargs *);
-int nfs3svc_decode_create3args(struct svc_rqst *, u32 *,
+int nfs3svc_decode_createargs(struct svc_rqst *, u32 *,
+				struct nfsd3_createargs *);
+int nfs3svc_decode_mkdirargs(struct svc_rqst *, u32 *,
 				struct nfsd3_createargs *);
-int nfs3svc_decode_rename3args(struct svc_rqst *, u32 *,
+int nfs3svc_decode_mknodargs(struct svc_rqst *, u32 *,
+				struct nfsd3_mknodargs *);
+int nfs3svc_decode_renameargs(struct svc_rqst *, u32 *,
 				struct nfsd3_renameargs *);
-int nfs3svc_decode_link3args(struct svc_rqst *, u32 *,
+int nfs3svc_decode_linkargs(struct svc_rqst *, u32 *,
 				struct nfsd3_linkargs *);
-int nfs3svc_decode_symlink3args(struct svc_rqst *, u32 *,
+int nfs3svc_decode_symlinkargs(struct svc_rqst *, u32 *,
 				struct nfsd3_symlinkargs *);
-int nfs3svc_decode_readdir3args(struct svc_rqst *, u32 *,
+int nfs3svc_decode_readdirargs(struct svc_rqst *, u32 *,
 				struct nfsd3_readdirargs *);
+int nfs3svc_decode_readdirplusargs(struct svc_rqst *, u32 *,
+				struct nfsd3_readdirargs *);
+int nfs3svc_decode_commitargs(struct svc_rqst *, u32 *,
+				struct nfsd3_commitargs *);
+int nfs3svc_encode_voidres(struct svc_rqst *, u32 *, void *);
+int nfs3svc_encode_attrstat(struct svc_rqst *, u32 *,
+				struct nfsd3_attrstat *);
+int nfs3svc_encode_wccstat(struct svc_rqst *, u32 *,
+				struct nfsd3_attrstat *);
+int nfs3svc_encode_diropres(struct svc_rqst *, u32 *,
+				struct nfsd3_diropres *);
+int nfs3svc_encode_accessres(struct svc_rqst *, u32 *,
+				struct nfsd3_accessres *);
 int nfs3svc_encode_readlinkres(struct svc_rqst *, u32 *,
 				struct nfsd3_readlinkres *);
 int nfs3svc_encode_readres(struct svc_rqst *, u32 *, struct nfsd3_readres *);
-int nfs3svc_encode_statfsres(struct svc_rqst *, u32 *,
-				struct nfsd3_statfsres *);
+int nfs3svc_encode_writeres(struct svc_rqst *, u32 *, struct nfsd3_writeres *);
+int nfs3svc_encode_createres(struct svc_rqst *, u32 *,
+				struct nfsd3_diropres *);
+int nfs3svc_encode_renameres(struct svc_rqst *, u32 *,
+				struct nfsd3_renameres *);
+int nfs3svc_encode_linkres(struct svc_rqst *, u32 *,
+				struct nfsd3_linkres *);
 int nfs3svc_encode_readdirres(struct svc_rqst *, u32 *,
 				struct nfsd3_readdirres *);
+int nfs3svc_encode_fsstatres(struct svc_rqst *, u32 *,
+				struct nfsd3_fsstatres *);
+int nfs3svc_encode_fsinfores(struct svc_rqst *, u32 *,
+				struct nfsd3_fsinfores *);
+int nfs3svc_encode_pathconfres(struct svc_rqst *, u32 *,
+				struct nfsd3_pathconfres *);
+int nfs3svc_encode_commitres(struct svc_rqst *, u32 *,
+				struct nfsd3_commitres *);
+
 int nfs3svc_release_fhandle(struct svc_rqst *, u32 *,
-				struct nfsd_fhandle *);
+				struct nfsd3_attrstat *);
 int nfs3svc_release_fhandle2(struct svc_rqst *, u32 *,
-				struct nfsd3_fhandle2 *);
+				struct nfsd3_fhandle_pair *);
 int nfs3svc_encode_entry(struct readdir_cd *, const char *name,
-				int namlen, unsigned long offset, ino_t ino);
+				int namlen, off_t offset, ino_t ino);
+int nfs3svc_encode_entry_plus(struct readdir_cd *, const char *name,
+				int namlen, off_t offset, ino_t ino);
+
+#ifdef __KERNEL__
+
+/*
+ * This is needed in nfs_readdir for encoding NFS3 directory cookies.
+ */
+static inline u32 *
+enc64(u32 *p, u64 val)
+{
+	*p++ = htonl(val >> 32);
+	*p++ = htonl(val & 0xffffffff);
+	return p;
+}
+
+#endif /* __KERNEL__ */
 
-#endif /* LINUX_NFSD_XDR3_H */
+#endif /* _LINUX_NFSD_XDR3_H */
diff -Naur pre9/linux/net/sunrpc/svc.c test/linux/net/sunrpc/svc.c
--- pre9/linux/net/sunrpc/svc.c	Tue Jan  4 10:12:27 2000
+++ test/linux/net/sunrpc/svc.c	Mon Sep 18 14:35:50 2000
@@ -32,6 +32,9 @@
 	struct svc_serv	*serv;
 
 	xdr_init();
+#ifdef RPC_DEBUG
+	rpc_register_sysctl();
+#endif
 
 	if (!(serv = (struct svc_serv *) kmalloc(sizeof(*serv), GFP_KERNEL)))
 		return NULL;
@@ -267,8 +270,8 @@
 	if (prog != progp->pg_prog)
 		goto err_bad_prog;
 
-	versp = progp->pg_vers[vers];
-	if (!versp || vers >= progp->pg_nvers)
+	if (vers >= progp->pg_nvers ||
+	  !(versp = progp->pg_vers[vers]))
 		goto err_bad_vers;
 
 	procp = versp->vs_proc + proc;

--------------500E2001E72FE3DE433B11A0--

_______________________________________________
NFS maillist  -  NFS@lists.sourceforge.net
http://lists.sourceforge.net/mailman/listinfo/nfs