linux-2.2.7-knfsdv3-1.diff

This patch add nfs version 3 to the linux kernel nfs server.

It must be applied after Trond's nfs version 3 patch.

  http://www.fys.uio.no/~trondmy/src/linux-2.2.7-nfsv3-0.8.1.dif.bz2

You may also need to update mount to the one found at:

  http://www.fys.uio.no/~trondmy/src/nfsv3-mount/

  Allen
------------------------------------------------------------------
diff -u --new-file -X exclude linux-2.2.7-nfsv3/fs/nfsd/Makefile linux/fs/nfsd/Makefile
--- linux-2.2.7-nfsv3/fs/nfsd/Makefile	Tue Dec 29 11:42:25 1998
+++ linux/fs/nfsd/Makefile	Fri Apr 30 23:44:22 1999
@@ -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_NFS_V3
+  O_OBJS += nfs3proc.o nfs3xdr.o
+endif
 
 M_OBJS   := $(O_TARGET)
 
diff -u --new-file -X exclude linux-2.2.7-nfsv3/fs/nfsd/nfs3proc.c linux/fs/nfsd/nfs3proc.c
--- linux-2.2.7-nfsv3/fs/nfsd/nfs3proc.c	Mon Apr 19 22:22:02 1999
+++ linux/fs/nfsd/nfs3proc.c	Fri Apr 30 23:44:22 1999
@@ -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,32 @@
 #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>
 
 #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_IFIFO,		/* NF3FIFO */
+	S_IFSOCK,		/* NF3SOCK */
+};
+
+/*
+ * Reserve room in the send buffer
+ */
 static void
 svcbuf_reserve(struct svc_buf *buf, u32 **ptr, int *len, int nr)
 {
@@ -38,6 +51,9 @@
 	*len = buf->buflen - buf->len - nr;
 }
 
+/*
+ * NULL call.
+ */
 static int
 nfsd3_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
 {
@@ -46,7 +62,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 +69,17 @@
 {
 	int	nfserr;
 
-	dprintk("nfsd: GETATTR  %x/%ld\n",
+	dprintk("nfsd: GETATTR(3)  %x/%ld\n",
 				SVCFH_DEV(&argp->fh),
 				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 +87,30 @@
 {
 	int	nfserr;
 
-	dprintk("nfsd: SETATTR  %x/%ld\n",
+	dprintk("nfsd: SETATTR(3)  %x/%ld\n",
 				SVCFH_DEV(&argp->fh),
 				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),
 				argp->name);
 
-	resp->dirfh = argp->fh;
+	fh_copy(&resp->dirfh, &argp->fh);
 	nfserr = nfsd_lookup(rqstp, &resp->dirfh,
 				    argp->name,
 				    argp->len,
@@ -109,12 +122,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),
+				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 +148,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));
 
 	/* 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,7 +173,7 @@
 	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),
 				(unsigned long) argp->count,
@@ -162,30 +183,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 +213,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),
 				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,18 +235,16 @@
  * 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),
 				argp->name);
@@ -243,131 +263,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),
 				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);
+				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;
-
-	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);
+	int	nfserr, type;
+	dev_t	rdev = 0;
 
-	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),
+				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),
 				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 +379,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),
 				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),
+				SVCFH_INO(&argp->ffh),
+				argp->fname,
+				SVCFH_DEV(&argp->tfh),
+				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),
+				SVCFH_INO(&argp->ffh),
+				SVCFH_DEV(&argp->tfh),
+				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,44 +439,83 @@
  */
 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),
+				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);
+				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));
 
@@ -442,104 +525,166 @@
 }
 
 /*
- * 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),
+				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 = ~(u32) 0;
+	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),
+				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),
+				SVCFH_INO(&argp->fh),
+				argp->count,
+				(unsigned long) argp->offset);
+
+	if (argp->offset > NFS_OFFSET_MAX)
+		return nfserr_inval;
 
-	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]);
+	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_encode_voidres		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,		fhandle,  RC_REPLSTAT),
+  PROC(link,	 link,		link,		fhandle2, RC_REPLSTAT),
+  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_NOCACHE)
+};
diff -u --new-file -X exclude linux-2.2.7-nfsv3/fs/nfsd/nfs3xdr.c linux/fs/nfsd/nfs3xdr.c
--- linux-2.2.7-nfsv3/fs/nfsd/nfs3xdr.c	Mon Apr  7 11:35:31 1997
+++ linux/fs/nfsd/nfs3xdr.c	Fri Apr 30 23:44:22 1999
@@ -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,44 +37,13 @@
 };
 
 /*
- * 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);
+	*p++ = htonl(val >> 32);
+	*p++ = htonl(val & 0xffffffff);
 	return p;
 }
 
@@ -103,13 +72,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,18 +145,24 @@
 		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) {
-		iap->ia_valid |= ATTR_ATIME;
-	} else if (tmp == 2) {
+	if ((tmp = ntohl(*p++)) == 1) {	/* set to server time */
 		iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
+	} else if (tmp == 2) {		/* set to client time */
+		iap->ia_valid |= ATTR_ATIME;
 		iap->ia_atime = ntohl(*p++), p++;
 	}
-	if ((tmp = *p++) != 0) {
+	if ((tmp = ntohl(*p++)) == 1) {	/* set to server time */
 		iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
-	} else if (tmp == 2) {
+	} else if (tmp == 2) {		/* set to client time */
 		iap->ia_valid |= ATTR_MTIME;
 		iap->ia_mtime = ntohl(*p++), p++;
 	}
@@ -198,8 +170,10 @@
 }
 
 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;
@@ -227,19 +201,50 @@
 	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);
+	}
+	p = enc64(p, fhp->fh_post_blksize * fhp->fh_post_blocks);
+	*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->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 +253,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 +309,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 +345,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 +357,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 +378,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 +473,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 +487,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 +499,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);
@@ -501,7 +515,8 @@
 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 +533,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 +549,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 +560,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 +573,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 +601,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 +627,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 +637,107 @@
 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 */
-	memcpy(p, name, namlen);
-	p += slen;
+	*p++ = xdr_one;				   /* mark entry present */
+	p    = enc64(p, ino);			   /* file id */
+	p    = xdr_encode_string(p, name, namlen); /* name length & name */
+
+	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;
@@ -723,8 +772,8 @@
 		*p++ = htonl(resp->f_wtmult);
 		*p++ = htonl(resp->f_dtpref);
 		*p++ = htonl(resp->f_maxfilesize);
+		*p++ = xdr_one;
 		*p++ = xdr_zero;
-		*p++ = htonl(1000000000 / HZ);
 		*p++ = htonl(resp->f_properties);
 	}
 
@@ -741,8 +790,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 +818,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 +826,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 -u --new-file -X exclude linux-2.2.7-nfsv3/fs/nfsd/nfsfh.c linux/fs/nfsd/nfsfh.c
--- linux-2.2.7-nfsv3/fs/nfsd/nfsfh.c	Sun Apr 11 22:03:43 1999
+++ linux/fs/nfsd/nfsfh.c	Fri Apr 30 23:44:22 1999
@@ -1235,6 +1235,30 @@
 	goto out;
 }
 
+void
+__fh_unlock(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;
+	up(&inode->i_sem);
+}
+
 /*
  * 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.
diff -u --new-file -X exclude linux-2.2.7-nfsv3/fs/nfsd/nfsproc.c linux/fs/nfsd/nfsproc.c
--- linux-2.2.7-nfsv3/fs/nfsd/nfsproc.c	Mon Apr 19 22:22:02 1999
+++ linux/fs/nfsd/nfsproc.c	Fri Apr 30 23:44:22 1999
@@ -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>
@@ -87,7 +90,11 @@
 
 	dprintk("nfsd: LOOKUP   %d/%d %s\n",
 		SVCFH_DEV(&argp->fh), SVCFH_INO(&argp->fh), argp->name);
-
+#ifdef NFSD_DEBUG
+	/* XXX: should go in the released version */
+	if (!memcmp(argp->name, "..nfsd.xyzzy..", 14))
+		nfsd_debug = ~nfsd_debug;
+#endif
 	nfserr = nfsd_lookup(rqstp, &argp->fh, argp->name, argp->len,
 				 &resp->fh);
 
@@ -144,7 +151,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;
@@ -276,7 +283,7 @@
 			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 */
@@ -367,7 +374,7 @@
 
 static int
 nfsd_proc_symlink(struct svc_rqst *rqstp, struct nfsd_symlinkargs *argp,
-				          void			  *resp)
+					  struct nfsd_fhandle *resp)
 {
 	struct svc_fh	newfh;
 	int		nfserr;
@@ -381,7 +388,8 @@
 	 */
 	nfserr = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen,
 						 argp->tname, argp->tlen,
-						 &newfh);
+				 		 &newfh, &argp->attrs);
+
 	if (!nfserr) {
 		argp->attrs.ia_valid &= ~ATTR_SIZE;
 		nfserr = nfsd_setattr(rqstp, &newfh, &argp->attrs);
@@ -439,8 +447,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),
@@ -460,7 +468,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);
@@ -540,6 +549,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 },
@@ -547,39 +558,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 -u --new-file -X exclude linux-2.2.7-nfsv3/fs/nfsd/nfssvc.c linux/fs/nfsd/nfssvc.c
--- linux-2.2.7-nfsv3/fs/nfsd/nfssvc.c	Sun Apr 11 22:03:43 1999
+++ linux/fs/nfsd/nfssvc.c	Fri Apr 30 23:44:22 1999
@@ -121,6 +121,8 @@
 	}
 	lockd_up();				/* start lockd */
 
+/* nfsd_debug = -1; */
+/* rpc_debug = -1; */
 	/*
 	 * The main request loop
 	 */
@@ -202,7 +204,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. */
@@ -231,16 +234,17 @@
 		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.
 	 */
-	xdr = proc->pc_encode;
-	if (!nfserr && 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;
+	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;
+		}
 	}
 
 	/* Store reply in cache. */
@@ -251,16 +255,16 @@
 static struct svc_version	nfsd_version2 = {
 	2, 18, nfsd_procedures2, nfsd_dispatch
 };
-#ifdef CONFIG_NFSD_NFS3
+#ifdef CONFIG_NFS_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_NFS_V3
 	&nfsd_version3,
 #endif
 };
diff -u --new-file -X exclude linux-2.2.7-nfsv3/fs/nfsd/nfsxdr.c linux/fs/nfsd/nfsxdr.c
--- linux-2.2.7-nfsv3/fs/nfsd/nfsxdr.c	Wed Nov 26 13:08:38 1997
+++ linux/fs/nfsd/nfsxdr.c	Fri Apr 30 23:44:22 1999
@@ -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;
 }
@@ -438,6 +457,12 @@
 		return -EINVAL;
 	if (cd->offset)
 		*cd->offset = htonl(offset);
+
+	/* nfsd_readdir calls us with name == 0 if it wants us to
+	 * set the last entry's offset. */
+	if (name == 0)
+		return 0;
+
 	if (namlen > NFS2_MAXNAMLEN)
 		namlen = NFS2_MAXNAMLEN;/* truncate filename */
 
diff -u --new-file -X exclude linux-2.2.7-nfsv3/fs/nfsd/vfs.c linux/fs/nfsd/vfs.c
--- linux-2.2.7-nfsv3/fs/nfsd/vfs.c	Mon Apr 19 22:22:02 1999
+++ linux/fs/nfsd/vfs.c	Fri Apr 30 23:44:22 1999
@@ -32,6 +32,7 @@
 
 #include <linux/sunrpc/svc.h>
 #include <linux/nfsd/nfsd.h>
+#include <linux/nfs3.h>
 #include <linux/nfsd/nfsfh.h>
 #include <linux/quotaops.h>
 
@@ -239,6 +240,9 @@
 	if (err)
 		goto out_nfserr;
 
+	/* Lock the inode */
+	fh_lock(fhp);
+
 	/* The size case is special... */
 	if (iap->ia_valid & ATTR_SIZE) {
 if (!S_ISREG(inode->i_mode))
@@ -258,8 +262,8 @@
 		mark_inode_dirty(inode);
 		put_write_access(inode);
 		iap->ia_valid &= ~ATTR_SIZE;
-		iap->ia_valid |= ATTR_MTIME;
-		iap->ia_mtime = CURRENT_TIME;
+		iap->ia_valid |= ATTR_CTIME;
+		iap->ia_ctime = CURRENT_TIME;
 	}
 
 	imode = inode->i_mode;
@@ -299,6 +303,9 @@
 			write_inode_now(inode);
 	}
 	err = 0;
+
+	/* Don't unlock inode; the nfssvc_release functions are supposed
+	 * to do this. */
 out:
 	return err;
 
@@ -307,6 +314,83 @@
 	goto out;
 }
 
+#ifdef CONFIG_NFS_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		},
+
+    {	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 < 0)
+		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);
+			if (error == 0)
+				result |= map->access;
+			else if (error != -EPERM)
+				goto out;
+		}
+		map++;
+	}
+	*access = result;
+
+out:
+	return error;
+}
+#endif
+
+
+
 /*
  * Open an existing file or directory.
  * The wflag argument indicates write access.
@@ -396,8 +480,9 @@
  * Sync a file
  */
 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);
 	filp->f_op->fsync(filp, filp->f_dentry);
 }
 
@@ -529,10 +614,15 @@
 	 * 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.
 	 */
-	if ((stable || (stable = EX_ISSYNC(exp))) && !EX_WGATHER(exp))
+	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;
 
 	fh_lock(fhp);			/* lock inode */
@@ -554,7 +644,7 @@
 	/* clear setuid/setgid flag after write */
 	if (err >= 0 && (inode->i_mode & (S_ISUID | S_ISGID))) {
 		struct iattr	ia;
-		kernel_cap_t	saved_cap;
+		kernel_cap_t	saved_cap = 0;
 
 		ia.ia_valid = ATTR_MODE;
 		ia.ia_mode  = inode->i_mode & ~(S_ISUID | S_ISGID);
@@ -591,6 +681,7 @@
 			interruptible_sleep_on_timeout(&inode->i_wait, 10 * HZ / 1000);
 #else
 			dprintk("nfsd: write defer %d\n", current->pid);
+/* FIXME: Olaf commented this out [gam3] */
 			schedule_timeout((HZ+99)/100);
 			dprintk("nfsd: write resume %d\n", current->pid);
 #endif
@@ -598,15 +689,16 @@
 
 		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 (EX_ISSYNC(exp))
+				write_inode_now(inode);
 		}
 		wake_up(&inode->i_wait);
 		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 
@@ -617,6 +709,40 @@
 	return err;
 }
 
+
+#ifdef CONFIG_NFS_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, OPEN_WRITE, &file)) != 0)
+		return err;
+
+	if (file.f_op && file.f_op->fsync) {
+		fh_lock(fhp);
+		nfsd_sync(&file);
+		fh_unlock(fhp);
+		if (EX_ISSYNC(fhp->fh_export))
+			write_inode_now(fhp->fh_dentry->d_inode);
+	} else {
+		err = nfserr_notsupp;
+	}
+
+	nfsd_close(&file);
+	return err;
+}
+#endif
+
 /*
  * Create a file (regular, directory, device, fifo); UNIX sockets 
  * not yet implemented.
@@ -704,6 +830,9 @@
 	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;
@@ -744,6 +873,109 @@
 	goto out;
 }
 
+#ifdef CONFIG_NFS_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;
+
+	/*
+	 * 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.
+	 */
+	fh_lock(fhp);
+
+	if (dchild->d_inode) {
+		err = 0;
+
+		switch (createmode) {
+		case NFS3_CREATE_UNCHECKED:
+			break;
+		case NFS3_CREATE_EXCLUSIVE:
+			if (dchild->d_inode->i_mtime == verifier[0]
+			 && dchild->d_inode->i_atime == verifier[1])
+				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))
+		write_inode_now(dirp);
+
+	/*
+	 * 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;
+		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.
+	 */
+	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_NFS_V3 */
+
 /*
  * Truncate a file.
  * The calling routines must make sure to update the ctime
@@ -759,7 +991,7 @@
 	struct inode	*inode;
 	struct iattr	newattrs;
 	int		err;
-	kernel_cap_t	saved_cap;
+	kernel_cap_t	saved_cap = 0;
 
 	err = fh_verify(rqstp, fhp, S_IFREG, MAY_WRITE | MAY_TRUNC);
 	if (err)
@@ -824,7 +1056,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);
@@ -849,7 +1084,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;
@@ -891,6 +1127,16 @@
 		if (!err) {
 			if (EX_ISSYNC(fhp->fh_export))
 				write_inode_now(dirp);
+			if (iap) {
+				iap->ia_valid &= ~(ATTR_MODE|ATTR_UID|ATTR_GID);
+				if (iap->ia_valid) {
+					iap->ia_valid |= ATTR_CTIME;
+					iap->ia_ctime = CURRENT_TIME;
+					err = notify_change(dnew, iap);
+					if (!err && EX_ISSYNC(fhp->fh_export))
+						write_inode_now(dnew->d_inode);
+			       }
+			}
 		} else
 			err = nfserrno(-err);
 	}
@@ -1171,8 +1417,10 @@
 
 		rdentry->d_count--;
 		DQUOT_DROP(dirp);
+#ifdef FIXTHIS
 		if (!fhp->fh_post_version)
 			fhp->fh_post_version = dirp->i_version;
+#endif
 		fhp->fh_locked = 0;
 		nfsd_double_up(&dirp->i_sem, &rdentry->d_inode->i_sem);
 
@@ -1193,10 +1441,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;
@@ -1222,6 +1471,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
@@ -1314,7 +1564,7 @@
 {
 	struct inode	*inode = dentry->d_inode;
 	int		err;
-	kernel_cap_t	saved_cap;
+	kernel_cap_t	saved_cap = 0;
 
 	if (acc == MAY_NOP)
 		return 0;
diff -u --new-file -X exclude linux-2.2.7-nfsv3/include/linux/nfsd/const.h linux/include/linux/nfsd/const.h
--- linux-2.2.7-nfsv3/include/linux/nfsd/const.h	Fri Apr 30 16:27:42 1999
+++ linux/include/linux/nfsd/const.h	Sat May  1 22:13:04 1999
@@ -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
@@ -48,43 +43,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 -u --new-file -X exclude linux-2.2.7-nfsv3/include/linux/nfsd/export.h linux/include/linux/nfsd/export.h
--- linux-2.2.7-nfsv3/include/linux/nfsd/export.h	Mon Feb 22 14:50:55 1999
+++ linux/include/linux/nfsd/export.h	Sat May  1 01:34:32 1999
@@ -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.
diff -u --new-file -X exclude linux-2.2.7-nfsv3/include/linux/nfsd/nfsd.h linux/include/linux/nfsd/nfsd.h
--- linux-2.2.7-nfsv3/include/linux/nfsd/nfsd.h	Mon Feb 22 14:50:57 1999
+++ linux/include/linux/nfsd/nfsd.h	Sat May  1 22:14:27 1999
@@ -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,7 +24,7 @@
 /*
  * nfsd version
  */
-#define NFSD_VERSION		"0.4"
+#define NFSD_VERSION		"0.5"
 
 #ifdef __KERNEL__
 /*
@@ -61,6 +61,7 @@
  * Procedure table for NFSv2
  */
 extern struct svc_procedure	nfsd_procedures2[];
+extern struct svc_procedure	nfsd_procedures3[];
 extern struct svc_program	nfsd_program;
 
 /*
@@ -74,11 +75,16 @@
 void		nfsd_racache_shutdown(void);
 int		nfsd_lookup(struct svc_rqst *, struct svc_fh *,
 				const char *, int, struct svc_fh *);
+int		nfsd_access(struct svc_rqst *, struct svc_fh *, u32 *);
 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);
+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);
 int		nfsd_open(struct svc_rqst *, struct svc_fh *, int,
 				int, struct file *);
 void		nfsd_close(struct file *);
@@ -90,7 +96,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 +110,11 @@
 				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 *);
+int		nfsd_commit(struct svc_rqst *, struct svc_fh *,
+				off_t, unsigned long);
 int		nfsd_notify_change(struct inode *, struct iattr *);
 int		nfsd_permission(struct svc_export *, struct dentry *, int);
 
@@ -146,6 +154,7 @@
 		nfserr_rofs,
 		nfserr_mlink,
 		nfserr_nametoolong,
+		nfserr_notempty,
 		nfserr_dquot,
 		nfserr_stale,
 		nfserr_remote,
diff -u --new-file -X exclude linux-2.2.7-nfsv3/include/linux/nfsd/nfsfh.h linux/include/linux/nfsd/nfsfh.h
--- linux-2.2.7-nfsv3/include/linux/nfsd/nfsfh.h	Sun Apr 11 22:03:59 1999
+++ linux/include/linux/nfsd/nfsfh.h	Sat May  1 22:13:36 1999
@@ -11,12 +11,15 @@
  * Copyright (C) 1995, 1996, 1997 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>
 
@@ -81,20 +84,38 @@
 	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 */
-	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_post_saved;	/* post-op attrs saved */
+	unsigned char		fh_pre_saved;	/* pre-op attrs saved */
 	unsigned char		fh_locked;	/* inode locked by us */
 	unsigned char		fh_dverified;	/* dentry has been checked */
+
+	/* 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 */
+
+	/* 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 */
+
 } svc_fh;
 
 /*
  * Shorthand for dprintk()'s
  */
 #define SVCFH_DENTRY(f)		((f)->fh_dentry)
-#define SVCFH_INO(f)		((f)->fh_handle.fh_ino)
 #define SVCFH_DEV(f)		((f)->fh_handle.fh_dev)
+#define SVCFH_INO(f)		((f)->fh_handle.fh_ino)
 
 /*
  * Function prototypes
@@ -103,6 +124,7 @@
 void	fh_compose(struct svc_fh *, struct svc_export *, struct dentry *);
 void	fh_update(struct svc_fh *);
 void	fh_put(struct svc_fh *);
+void	__fh_unlock(struct svc_fh *);
 void	nfsd_fh_flush(kdev_t);
 void	nfsd_fh_init(void);
 void	nfsd_fh_free(void);
@@ -139,10 +161,9 @@
 	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) {
 		printk(KERN_ERR "fh_lock: fh not verified!\n");
 		return;
@@ -155,8 +176,12 @@
 
 	inode = dentry->d_inode;
 	down(&inode->i_sem);
-	if (!fhp->fh_pre_mtime)
+	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;
 }
 
@@ -170,13 +195,7 @@
 		printk(KERN_ERR "fh_unlock: fh not verified!\n");
 
 	if (fhp->fh_locked) {
-		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);
+		__fh_unlock(fhp);
 	}
 }
 
@@ -210,4 +229,4 @@
 
 #endif /* __KERNEL__ */
 
-#endif /* NFSD_FH_H */
+#endif /* _LINUX_NFSD_FH_H */
diff -u --new-file -X exclude linux-2.2.7-nfsv3/include/linux/nfsd/syscall.h linux/include/linux/nfsd/syscall.h
--- linux-2.2.7-nfsv3/include/linux/nfsd/syscall.h	Mon Dec 28 14:10:01 1998
+++ linux/include/linux/nfsd/syscall.h	Sat May  1 22:15:32 1999
@@ -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 -u --new-file -X exclude linux-2.2.7-nfsv3/include/linux/nfsd/xdr3.h linux/include/linux/nfsd/xdr3.h
--- linux-2.2.7-nfsv3/include/linux/nfsd/xdr3.h	Mon Apr  7 11:35:32 1997
+++ linux/include/linux/nfsd/xdr3.h	Sat May  1 01:38:58 1999
@@ -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,71 @@
 
 #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_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);
 
-#endif /* LINUX_NFSD_XDR3_H */
+#endif /* _LINUX_NFSD_XDR3_H */
