--- linux-2.2.1/include/linux/nfsd/nfsfh.h	Thu Oct  8 21:32:12 1998
+++ linux/include/linux/nfsd/nfsfh.h	Thu Feb 18 13:49:05 1999
@@ -33,6 +33,7 @@
 	__u32		fb_dev;		/* our device */
 	__u32		fb_xdev;
 	__u32		fb_xino;
+	__u32		fb_version;
 };
 
 #define NFS_FH_PADDING		(NFS_FHSIZE - sizeof(struct nfs_fhbase))
@@ -47,6 +48,7 @@
 #define fh_dev			fh_base.fb_dev
 #define fh_xdev			fh_base.fb_xdev
 #define fh_xino			fh_base.fb_xino
+#define fh_version		fh_base.fb_version
 
 #ifdef __KERNEL__
 
@@ -106,6 +108,9 @@
 void	nfsd_fh_flush(kdev_t);
 void	nfsd_fh_init(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)
@@ -176,34 +181,6 @@
 		up(&inode->i_sem);
 	}
 }
-
-/*
- * Release an inode
- */
-#if 0
-#define fh_put(fhp)	__fh_put(fhp, __FILE__, __LINE__)
-
-static inline void
-__fh_put(struct svc_fh *fhp, char *file, int line)
-{
-	struct dentry	*dentry;
-
-	if (!fhp->fh_dverified)
-		return;
-
-	dentry = fhp->fh_dentry;
-	if (!dentry->d_count) {
-		printk("nfsd: trying to free free dentry in %s:%d\n"
-		       "      file %s/%s\n",
-		       file, line,
-		       dentry->d_parent->d_name.name, dentry->d_name.name);
-	} else {
-		fh_unlock(fhp);
-		fhp->fh_dverified = 0;
-		dput(dentry);
-	}
-}
-#endif
 
 #endif /* __KERNEL__ */
 
diff -u -X exclude linux-2.2.1/fs/nfs/dir.c linux/fs/nfs/dir.c
--- linux-2.2.1/fs/nfs/dir.c	Tue Jan 19 11:22:18 1999
+++ linux/fs/nfs/dir.c	Wed Feb 17 16:05:20 1999
@@ -1158,8 +1158,9 @@
 	 */
 	if (old_dentry->d_count > 1) {
 		nfs_wb_all(old_inode);
-		shrink_dcache_parent(old_dentry);
+/*		shrink_dcache_parent(old_dentry); FIXME */
 	}
+	goto do_rename;
 
 	/*
 	 * Now check the use counts ... we can't safely do the
Only in linux/fs/nfs: nfs3proc.c
diff -u -X exclude linux-2.2.1/fs/nfsd/nfsfh.c linux/fs/nfsd/nfsfh.c
--- linux-2.2.1/fs/nfsd/nfsfh.c	Tue Jan 19 11:22:18 1999
+++ linux/fs/nfsd/nfsfh.c	Fri Feb 19 00:00:09 1999
@@ -210,9 +210,9 @@
 	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);	
+		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);
@@ -299,7 +333,7 @@
 	int result = 0;
 
 	buf->sequence++;
-#ifdef NFSD_DEBUG_VERBOSE
+#ifdef NFSD_DEBUG_VERY_VERBOSE
 printk("filldir_one: seq=%d, ino=%ld, name=%s\n", buf->sequence, ino, name);
 #endif
 	if (buf->sequence == 2) {
@@ -309,7 +343,7 @@
 	if (dirent->ino == ino) {
 		dirent->len = len;
 		memcpy(dirent->name, name, len);
-		dirent->name[len] = 0;
+		dirent->name[len] = '\0';
 		buf->found = 1;
 		result = -1;
 	}
@@ -519,7 +553,8 @@
 	struct dentry *dentry = empty->dentry;
 
 #ifdef NFSD_DEBUG_VERBOSE
-printk("expire_fhe: expiring %s/%s, d_count=%d, ino=%ld\n",
+printk("expire_fhe: expiring %s %s/%s, d_count=%d, ino=%ld\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 */
@@ -578,7 +613,7 @@
 	struct fh_entry *fhe;
 	int i;
 
-#ifdef NFSD_DEBUG_VERBOSE
+#ifdef NFSD_DEBUG_VERY_VERBOSE
 printk("expire_old: expiring %s older than %d\n",
 (cache == NFSD_FILE_CACHE) ? "file" : "dir", age);
 #endif
@@ -749,6 +784,21 @@
 	return dentry;
 }
 
+static u32
+get_version(struct inode *inode)
+{
+	u32 ret = 0;
+
+/* FIXME: there should be a flag s_type */
+	if (inode && !strcmp("ext2", inode->i_sb->s_type->name)) {
+		ret = ino_t_to_u32(inode->u.ext2_i.i_version);
+#if 0
+		printk("ext2 %u %u\n", inode->i_ino, inode->u.ext2_i.i_version);
+#endif
+	}
+	return ret;
+}
+
 /*
  * Look for an entry in the file cache matching the dentry pointer,
  * and verify that the (dev, inode) numbers are correct. If found,
@@ -777,6 +827,11 @@
 			goto out;
  		if (inode->i_dev != u32_to_kdev_t(fh->fh_dev))
 			goto out;
+		if (get_version(inode) != fh->fh_version) {
+printk("find_dentry_in_fhcache: Bad version %u %u\n",
+	get_version(inode), fh->fh_version);
+/* 			goto out; */
+		}
 
 		fhe->dentry = NULL;
 		fhe->ino = 0;
@@ -841,7 +896,6 @@
 	dentry = NULL;
 out:
 	return dentry;
-
 }
 
 /*
@@ -999,14 +1066,34 @@
 
 	/*
 	 * Perform any needed housekeeping ...
-	 * N.B. move this into one of the daemons ...
 	 */
+	expire_all();
+	return dentry;
+}
+
+void
+expire_all(void)
+{
 	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;
 	}
-	return dentry;
+}
+
+/* 
+ * Free cache after unlink.
+ * FIXME: (gam3) only do files now, should directories also be done?
+ */
+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);
+	}
 }
 
 /*
@@ -1096,7 +1183,7 @@
 	error = 0;
 	if (fh->fh_dev != fh->fh_xdev) {
 		printk("fh_verify: Security: export on other device"
-		       " (%d, %d).\n", fh->fh_dev, fh->fh_xdev);
+			   " (%d, %d).\n", fh->fh_dev, fh->fh_xdev);
 		error = nfserr_stale;
 	} else if (exp->ex_dentry != dentry) {
 		struct dentry *tdentry = dentry;
@@ -1106,13 +1193,13 @@
 			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 (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)) {
 				error = nfserr_stale;
 dprintk("fh_verify: no root_squashed access.\n");
 			}
@@ -1171,6 +1258,7 @@
 	fhp->fh_handle.fh_dcookie = dentry;
 	if (inode) {
 		fhp->fh_handle.fh_ino = ino_t_to_u32(inode->i_ino);
+		fhp->fh_handle.fh_version = get_version(inode);
 	}
 	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);
@@ -1202,6 +1290,7 @@
 	if (!inode)
 		goto out_negative;
 	fhp->fh_handle.fh_ino = ino_t_to_u32(inode->i_ino);
+	fhp->fh_handle.fh_version = get_version(inode);
 out:
 	return;
 
diff -u -X exclude linux-2.2.1/fs/nfsd/nfsproc.c linux/fs/nfsd/nfsproc.c
--- linux-2.2.1/fs/nfsd/nfsproc.c	Wed Jul 22 14:37:36 1998
+++ linux/fs/nfsd/nfsproc.c	Wed Feb 17 16:05:20 1999
@@ -515,7 +515,7 @@
   PROC(symlink,	 symlinkargs,	void,		none,		RC_REPLSTAT),
   PROC(mkdir,	 createargs,	diropres,	fhandle,	RC_REPLBUFF),
   PROC(rmdir,	 diropargs,	void,		none,		RC_REPLSTAT),
-  PROC(readdir,	 readdirargs,	readdirres,	none,		RC_REPLSTAT),
+  PROC(readdir,	 readdirargs,	readdirres,	none,		RC_REPLBUFF),
   PROC(statfs,	 fhandle,	statfsres,	none,		RC_NOCACHE),
 };
 
diff -u -X exclude linux-2.2.1/fs/nfsd/nfssvc.c linux/fs/nfsd/nfssvc.c
--- linux-2.2.1/fs/nfsd/nfssvc.c	Fri Jan  1 23:03:09 1999
+++ linux/fs/nfsd/nfssvc.c	Fri Feb 19 12:26:03 1999
@@ -5,7 +5,7 @@
  *
  * Authors:	Olaf Kirch (okir@monad.swb.de)
  *
- * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1995-1999 Olaf Kirch <okir@monad.swb.de>
  */
 
 #define __NO_VERSION__
@@ -101,7 +101,7 @@
 nfsd(struct svc_rqst *rqstp)
 {
 	struct svc_serv	*serv = rqstp->rq_server;
-	int		oldumask, err;
+	int		oldumask, err, first = 0;
 
 	/* Lock module and set up kernel thread */
 	MOD_INC_USE_COUNT;
@@ -115,8 +115,10 @@
 
 	oldumask = current->fs->umask;		/* Set umask to 0.  */
 	current->fs->umask = 0;
-	if (!nfsd_active++)
+	if (!nfsd_active++) {
 		nfssvc_boot = xtime;		/* record boot time */
+		first = 1;
+	}
 	lockd_up();				/* start lockd */
 
 	/*
@@ -133,8 +135,14 @@
 		 * Find a socket with data available and call its
 		 * recvfrom routine.
 		 */
-		while ((err = svc_recv(serv, rqstp, MAX_SCHEDULE_TIMEOUT)) == -EAGAIN)
-			;
+		while ((err = svc_recv(serv, rqstp,
+		        first?5*HZ:MAX_SCHEDULE_TIMEOUT)) == -EAGAIN) {
+			if (first && 1) {
+				exp_readlock();
+				expire_all();
+				exp_unlock();
+			}
+		}
 		if (err < 0)
 			break;
 
diff -u -X exclude linux-2.2.1/fs/nfsd/vfs.c linux/fs/nfsd/vfs.c
--- linux-2.2.1/fs/nfsd/vfs.c	Tue Feb  2 09:53:31 1999
+++ linux/fs/nfsd/vfs.c	Fri Feb 19 12:23:16 1999
@@ -658,10 +658,6 @@
 		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)
@@ -670,6 +666,15 @@
 				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)
+			goto out;
+	}
 	/*
 	 * Make sure the child dentry is still negative ...
 	 */
@@ -695,8 +700,7 @@
 	case S_IFCHR:
 	case S_IFBLK:
 		/* The client is _NOT_ required to do security enforcement */
-		if(!capable(CAP_SYS_ADMIN))
-		{
+		if(!capable(CAP_SYS_ADMIN)) {
 			err = -EPERM;
 			goto out;
 		}
@@ -734,7 +738,7 @@
 	 * directories via NFS.
 	 */
 	err = 0;
-	if ((iap->ia_valid &= (ATTR_UID|ATTR_GID|ATTR_MODE)) != 0)
+	if ((iap->ia_valid &= ~(ATTR_UID|ATTR_GID|ATTR_MODE)) != 0)
 		err = nfsd_setattr(rqstp, resfhp, iap);
 out:
 	return err;
@@ -899,6 +903,7 @@
 	/* Compose the fh so the dentry will be freed ... */
 out_compose:
 	fh_compose(resfhp, fhp->fh_export, dnew);
+
 out:
 	return err;
 
@@ -1132,7 +1137,6 @@
 	err = PTR_ERR(rdentry);
 	if (IS_ERR(rdentry))
 		goto out_nfserr;
-
 	if (!rdentry->d_inode) {
 		dput(rdentry);
 		err = nfserr_noent;
@@ -1151,7 +1155,7 @@
 		fh_unlock(fhp);
 
 		dput(rdentry);
-
+		expire_by_dentry(rdentry);
 	} else {
 		/* It's RMDIR */
 		/* See comments in fs/namei.c:do_rmdir */
@@ -1179,6 +1183,7 @@
 		goto out_nfserr;
 	if (EX_ISSYNC(fhp->fh_export))
 		write_inode_now(dirp);
+
 out:
 	return err;
 
@@ -1330,7 +1335,7 @@
 		inode->i_uid, inode->i_gid, current->fsuid, current->fsgid);
 #endif
 #ifndef CONFIG_NFSD_SUN
-        if (dentry->d_mounts != dentry) {
+	if (dentry->d_mounts != dentry) {
 		return nfserr_perm;
 	}
 #endif
diff -u -X exclude linux-2.2.1/fs/ext2/ialloc.c linux/fs/ext2/ialloc.c
--- linux-2.2.1/fs/ext2/ialloc.c	Thu Nov 12 08:32:58 1998
+++ linux/fs/ext2/ialloc.c	Thu Feb 18 15:37:11 1999
@@ -268,15 +268,16 @@
 }
 
 /*
- * This function increments the inode version number
+ * This function sets the inode version number
  *
- * This may be used one day by the NFS server
+ * This is used by the NFS server
  */
-static void inc_inode_version (struct inode * inode,
+static void get_inode_version(struct inode * inode,
 			       struct ext2_group_desc *gdp,
 			       int mode)
 {
-	inode->u.ext2_i.i_version++;
+    get_random_bytes(&inode->u.ext2_i.i_version,
+					 sizeof(inode->u.ext2_i.i_version));
 	mark_inode_dirty(inode);
 
 	return;
@@ -494,10 +495,10 @@
 		inode->i_flags |= MS_SYNCHRONOUS;
 	insert_inode_hash(inode);
 	mark_inode_dirty(inode);
-	inc_inode_version (inode, gdp, mode);
+	get_inode_version(inode, gdp, mode);
 
 	unlock_super (sb);
-	if(DQUOT_ALLOC_INODE(sb, inode)) {
+	if (DQUOT_ALLOC_INODE(sb, inode)) {
 		sb->dq_op->drop(inode);
 		inode->i_nlink = 0;
 		iput(inode);
