--- clean/fs/Makefile	Fri Mar  2 18:16:59 2001
+++ dirty/fs/Makefile	Tue Feb  5 16:23:25 2002
@@ -40,6 +40,9 @@
 subdir-$(CONFIG_HFS_FS)		+= hfs
 subdir-$(CONFIG_NFS_FS)		+= nfs
 subdir-$(CONFIG_NFSD)		+= nfsd
+subdir-$(CONFIG_NFS4)		+= nfs4
+subdir-$(CONFIG_NFS4_FS)	+= nfs4fs
+subdir-$(CONFIG_NFSD4)		+= nfsd4
 subdir-$(CONFIG_LOCKD)		+= lockd
 subdir-$(CONFIG_NLS)		+= nls
 subdir-$(CONFIG_UMSDOS_FS)	+= umsdos
--- clean/fs/filesystems.c	Wed Apr 18 02:23:12 2001
+++ dirty/fs/filesystems.c	Tue May 15 15:31:46 2001
@@ -13,7 +13,8 @@
 #include <linux/kmod.h>
 #include <linux/nfsd/interface.h>
 
-#if defined(CONFIG_NFSD_MODULE)
+#if defined(CONFIG_NFSD_MODULE) || defined(CONFIG_NFSD4_MODULE)
+
 struct nfsd_linkage *nfsd_linkage = NULL;
 
 long
@@ -32,9 +33,11 @@
 }
 EXPORT_SYMBOL(nfsd_linkage);
 
-#elif ! defined (CONFIG_NFSD)
+#elif !defined (CONFIG_NFSD) && !defined(CONFIG_NFSD4)
+
 asmlinkage int sys_nfsservctl(int cmd, void *argp, void *resp)
 {
 	return -ENOSYS;
 }
+
 #endif /* CONFIG_NFSD */
--- clean/fs/locks.c	Fri Feb  9 14:29:44 2001
+++ dirty/fs/locks.c	Sat May 19 13:55:21 2001
@@ -134,7 +134,7 @@
 static kmem_cache_t *filelock_cache;
 
 /* Allocate an empty lock structure. */
-static struct file_lock *locks_alloc_lock(int account)
+struct file_lock *locks_alloc_lock(int account)
 {
 	struct file_lock *fl;
 	if (account && current->locks >= current->rlim[RLIMIT_LOCKS].rlim_cur)
@@ -146,7 +146,7 @@
 }
 
 /* Free a lock which is not in use. */
-static inline void locks_free_lock(struct file_lock *fl)
+void locks_free_lock(struct file_lock *fl)
 {
 	if (fl == NULL) {
 		BUG();
@@ -461,7 +461,7 @@
 /* Insert file lock fl into an inode's lock list at the position indicated
  * by pos. At the same time add the lock to the global file lock list.
  */
-static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl)
+void locks_insert_lock(struct file_lock **pos, struct file_lock *fl)
 {
 	list_add(&fl->fl_link, &file_lock_list);
 
@@ -478,7 +478,7 @@
  * waiting for this lock, notify the FS that the lock has been cleared and
  * finally free the lock.
  */
-static void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait)
+void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait)
 {
 	struct file_lock *fl = *thisfl_p;
 
@@ -514,7 +514,8 @@
 	if (fl->fl_file->f_op &&
 	    (lock = fl->fl_file->f_op->lock) != NULL) {
 		fl->fl_type = F_UNLCK;
-		lock(fl->fl_file, F_SETLK, fl);
+		if (lock(fl->fl_file, F_SETLK, fl) == LOCK_DONE)
+			return;
 	}
 	locks_delete_lock(thisfl_p, 0);
 }
@@ -614,12 +615,12 @@
 }
 
 struct file_lock *
-posix_test_lock(struct file *filp, struct file_lock *fl)
+__posix_test_lock(struct inode *inode, struct file_lock *fl)
 {
 	struct file_lock *cfl;
 
 	lock_kernel();
-	for (cfl = filp->f_dentry->d_inode->i_flock; cfl; cfl = cfl->fl_next) {
+	for (cfl = inode->i_flock; cfl; cfl = cfl->fl_next) {
 		if (!(cfl->fl_flags & FL_POSIX))
 			continue;
 		if (posix_locks_conflict(cfl, fl))
@@ -1484,6 +1485,10 @@
 		error = filp->f_op->lock(filp, cmd, file_lock);
 		if (error < 0)
 			goto out_putf;
+		if (error == LOCK_DONE) {
+			error = 0;
+			goto out_putf;
+		}
 	}
 	error = posix_lock_file(filp, file_lock, cmd == F_SETLKW);
 
@@ -1620,6 +1625,10 @@
 
 	if (filp->f_op && filp->f_op->lock != NULL) {
 		error = filp->f_op->lock(filp, cmd, file_lock);
+		if (error == LOCK_DONE) {
+			error = 0;
+			goto out_putf;
+		}
 		if (error < 0)
 			goto out_putf;
 	}
--- clean/fs/namei.c	Fri Apr 20 19:04:32 2001
+++ dirty/fs/namei.c	Tue Feb  5 16:08:00 2002
@@ -2,6 +2,8 @@
  *  linux/fs/namei.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  Experimental open_namei() change for NFSv4,  Kendrick Smith <kmsmith@umich.edu>
  */
 
 /*
@@ -936,6 +938,95 @@
  * for symlinks (where the permissions are checked later).
  * SMP-safe
  */
+
+/*
+ * open_namei() was changed significantly as an experimental solution to
+ * a race condition in NFSv4, which exists because of the current VFS
+ * interfaces.  Actually, I believe that this race condition is inherent
+ * to any network file system.
+ *
+ * The race condition is this:  Suppose that an open() is done with O_CREAT
+ * set.  In the stock 2.4.4 kernel, the VFS handled this request by:
+ *    1. Trying a ->lookup() for the filename.  If it succeeds, either fail
+ *       with -EEXIST (if O_EXCL set) or ->open() the file (O_EXCL not set).
+ *       If it fails...
+ *    2. ->create() the file.  The ->create() operation does not know whether
+ *       O_EXCL was set.
+ *    3. ->open() the file.
+ *
+ * The problem is, what if the file is created by a different client between
+ * steps 1 and 2?  The ->create() in step 2 will fail, causing an error to
+ * be returned from the open() syscall, which is not correct (assuming O_EXCL
+ * was not set).  Here is an even more nasty variant: suppose a different
+ * client creates a symlink by the same name between steps 1 and 2?  Then
+ * the whole open() should be restarted, following the symlink...
+ *
+ * This race condition seems inherent to any network file system.  (In fact,
+ * the open(2) manpage already states that O_EXCL is broken over NFSv2/v3.)
+ *  For a local file system, the VFS can acquire semaphores which ensure that
+ * another _process_ cannot create the file between steps 1 and 2?  However,
+ * for a network file system, there is no way to ensure that another _client_
+ * cannot create the file.
+ *
+ * For NFSv4, the situation is even worse than this.  In the NFSv4 protocol,
+ * a file can only be opened by name, not by filehandle.  Therefore, we have
+ * race conditions even if O_CREAT is not set.  The VFS will handle this by
+ * doing a ->lookup() for the filename (acquiring a dentry), then an ->open()
+ * for the file.  It is possible, due to a race against another client, that
+ * the ->open() will open a _different_ file than the one obtained with
+ * ->lookup(), i.e. a different dentry.  At present, there is no way for the
+ * ->open() operation to inform the VFS that this has occured.  There is a
+ * similar race between the ->create() in step 2 and the ->open() in step 3.
+ *
+ * Our experimental solution was to define a ->lookup_open() inode_operation,
+ * which would let the filesystem do steps 1-3 at once.  If the filesystem
+ * defines it, ->lookup_open() is always to be used instead of ->open().
+ * The prototype is
+ *
+ * int lookup_open(struct inode *dir, struct dentry **dentryp, struct nameidata *n);
+ *
+ * The second argument is a (dentry **) instead of a (dentry *) because the
+ * filesystem may change the dentry being opened, this is because NFSv4 always
+ * wants to do open-by-name instead of open-by-filehandle.  The 'nameidata'
+ * argument is used to communicate information about the OPEN to the filesystem;
+ * it was expanded to include extra fields such as the 'flags' and 'mode' parameters
+ * in the open() syscall.  If ->lookup_open() succeeds in opening the file, it
+ * will instantiate a 'struct file' which is communicated back to the VFS through
+ * another field in 'nameidata'.
+ *
+ * ->lookup_open() may return the special value -EAGAIN to tell the VFS to try
+ * the whole open() operation from the beginning.  This deals with the case where
+ * a symlink is instantiated by another client at the wrong time.  Once the
+ * symlink is created, the lookup logic must be restarted so that the symlink
+ * will be followed.
+ *
+ * After living with this solution for some time, I decided that it suffers
+ * from several inadequacies and can probably be improved.  For one thing,
+ * it considerably obfuscates open_namei() for the sake of a single filesystem.
+ * For another, it caters to NFSv4's open-by-filename semantics a little too
+ * much.  If NFSv4 supported open-by-filehandle, it would suffice to solve the
+ * simpler problem of defining a ->lookup_create() operation, which would also
+ * solve the race condition in NFSv2/v3.  Maybe we could do this, then convince
+ * the NFSv4 designers to somehow support open-by-filehandle in the protocol?
+ *
+ * Finally, I believe that putting all this extra logic in the open path is
+ * the wrong way to go.  I now believe that the right place for it is in the
+ * lookup path.  Maybe add lookup flags which indicate what kind of lookup
+ * it is, e.g.
+ *    LOOKUP_OPEN - we are trying to not only lookup the file, but also open it
+ *    LOOKUP_CREATE - we want to create the file if it does not exist
+ *
+ * The ->lookup() operation would need extra information, such as open flags
+ * and creation mode, which could be passed in some way such as adding them
+ * to the nameidata structure, and passing it into ->lookup().
+ *
+ * The upshot of all this is that I am including my ->lookup_open() hacks
+ * for the sake of public perusal, but I don't expect them to last in their
+ * current form, and am hoping to work with the Linux community to find a
+ * way of solving the race condition which will last.
+ *
+ * Big TODO: Sort this whole mess out!
+ */
 int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd)
 {
 	int acc_mode, error = 0;
@@ -943,43 +1034,110 @@
 	struct dentry *dentry;
 	struct dentry *dir;
 	int count = 0;
-
-	acc_mode = ACC_MODE(flag);
-
-	/*
-	 * The simplest case - just a plain lookup.
-	 */
-	if (!(flag & O_CREAT)) {
-		if (path_init(pathname, lookup_flags(flag), nd))
-			error = path_walk(pathname, nd);
-		if (error)
-			return error;
-		dentry = nd->dentry;
-		goto ok;
-	}
-
-	/*
-	 * Create - we need to know the parent.
-	 */
+	int subcount = 0;
+	struct qstr *putname_str = NULL;
+	
+  	acc_mode = ACC_MODE(flag);
+	
 	if (path_init(pathname, LOOKUP_PARENT, nd))
 		error = path_walk(pathname, nd);
 	if (error)
 		return error;
 
-	/*
-	 * We have the parent and last component. First of all, check
-	 * that we are not asked to creat(2) an obvious directory - that
-	 * will not do.
-	 */
+top:
 	error = -EISDIR;
-	if (nd->last_type != LAST_NORM || nd->last.name[nd->last.len])
-		goto exit;
+	if (nd->last_type != LAST_NORM) {
+		if (flag & O_CREAT)
+			goto exit;            /* return -EISDIR */
+		if (nd->last_type == LAST_DOTDOT)
+			follow_dotdot(nd);
+		dentry = nd->dentry;
+		goto ok;
+	}
+	if (nd->last.name[nd->last.len]) {
+		if (flag & O_CREAT)
+			goto exit;            /* return -EISDIR */
+		flag |= O_DIRECTORY;
+	}
 
 	dir = nd->dentry;
-	down(&dir->d_inode->i_sem);
-	dentry = lookup_hash(&nd->last, nd->dentry);
+	
+	if (dir->d_inode->i_op->lookup_open) {
+		if ((error = permission(dir->d_inode, MAY_EXEC)))
+			goto exit;
+		if (dir->d_op && dir->d_op->d_hash) {
+			error = dir->d_op->d_hash(dir, &nd->last);
+			if (error < 0)
+				goto exit;
+		}
+		down(&dir->d_inode->i_sem);
+
+get_dentry:
+		dentry = d_lookup(dir, &nd->last);
+		if (!dentry) {
+			dentry = d_alloc(dir, &nd->last);
+			error = -ENOMEM;
+			if (!dentry) {
+				up(&dir->d_inode->i_sem);
+				goto exit;
+			}
+		}
+		/*
+		 * We trap some cases which would be problematic for
+		 * ->lookup_open(): mountpoints and symlinks.  Then
+		 * we call ->lookup_open().
+		 */
+		else if (d_mountpoint(dentry))
+			/* The branch here duplicates a small amount of work, but
+			 * is cleanest, and opening across mountpoints is an
+			 * uncommon, non-bottleneck case.
+			 */
+			goto already_exists;
+		else if (dentry->d_inode && dentry->d_inode->i_op->follow_link) {
+			if (dentry->d_op && dentry->d_op->d_revalidate &&
+			    !dentry->d_op->d_revalidate(dentry, 0)) {
+				if ((error = d_invalidate(dentry))) {
+					printk(KERN_ERR "d_invalidate() failed, symlink?!\n");
+					up(&dir->d_inode->i_sem);
+					goto exit_dput;
+				}
+				dput(dentry);
+				goto get_dentry;
+			}
+			up(&dir->d_inode->i_sem);
+			goto do_link;
+		}
+		nd->open_flags = flag;
+		nd->mode = mode;
+		error = dir->d_inode->i_op->lookup_open(dir->d_inode, &dentry, nd);
+		up(&dir->d_inode->i_sem);
+		if (!error) {
+			/* These next 2 lines ought to be unnecessary, since the caller
+			   should notice that nd->file has been set and ignore nd->dentry,
+			   but just to be conservative, I've included them... */
+			dput(nd->dentry);
+			nd->dentry = dentry;
+			goto success;
+		}
+		if (error == -ELOOP)
+			goto do_link;
+		dput(dentry);
+		if (error == -EAGAIN) {
+			printk(KERN_INFO "open_namei: ->lookup_open() returned -EAGAIN!\n");
+			if (++subcount < 100)
+				goto get_dentry;
+			printk(KERN_INFO "open_namei: EAGAIN count exceeded!\n");
+			error = -EIO;
+		}
+		goto exit;
+	}
 
-do_last:
+	/*
+	 * If ->lookup_open() is not defined by the fs, use the usual
+	 * lookup-create-open sequence.
+	 */
+	
+	dentry = lookup_hash(&nd->last, nd->dentry);
 	error = PTR_ERR(dentry);
 	if (IS_ERR(dentry)) {
 		up(&dir->d_inode->i_sem);
@@ -988,6 +1146,9 @@
 
 	/* Negative dentry, just create the file */
 	if (!dentry->d_inode) {
+		error = -ENOENT;
+		if (!(flag & O_CREAT))
+			goto exit_dput;
 		error = vfs_create(dir->d_inode, dentry, mode);
 		up(&dir->d_inode->i_sem);
 		dput(nd->dentry);
@@ -1003,10 +1164,11 @@
 	/*
 	 * It already exists.
 	 */
+already_exists:
 	up(&dir->d_inode->i_sem);
 
 	error = -EEXIST;
-	if (flag & O_EXCL)
+	if ((flag & O_EXCL) && (flag & O_CREAT))
 		goto exit_dput;
 
 	if (d_mountpoint(dentry)) {
@@ -1023,9 +1185,7 @@
 
 	dput(nd->dentry);
 	nd->dentry = dentry;
-	error = -EISDIR;
-	if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode))
-		goto exit;
+
 ok:
 	error = -ENOENT;
 	inode = dentry->d_inode;
@@ -1037,7 +1197,10 @@
 		goto exit;
 	
 	error = -EISDIR;
-	if (S_ISDIR(inode->i_mode) && (flag & FMODE_WRITE))
+	if (S_ISDIR(inode->i_mode) && (flag & (FMODE_WRITE|O_CREAT)))
+		goto exit;
+	error = -ENOTDIR;
+	if (!S_ISDIR(inode->i_mode) && (flag & O_DIRECTORY))
 		goto exit;
 
 	error = permission(inode,acc_mode);
@@ -1101,18 +1264,29 @@
 		if (flag & FMODE_WRITE)
 			DQUOT_INIT(inode);
 
+success:
+	if (putname_str)
+		putname(putname_str->name);
 	return 0;
 
 exit_dput:
 	dput(dentry);
 exit:
 	path_release(nd);
+	if (putname_str)
+		putname(putname_str->name);
 	return error;
 
 do_link:
 	error = -ELOOP;
 	if (flag & O_NOFOLLOW)
 		goto exit_dput;
+	
+	if (putname_str) {
+		putname(putname_str->name);
+		putname_str = NULL;
+	}
+
 	/*
 	 * This is subtle. Instead of calling do_follow_link() we do the
 	 * thing by hands. The reason is that this way we have zero link_count
@@ -1132,23 +1306,13 @@
 		dentry = nd->dentry;
 		goto ok;
 	}
-	error = -EISDIR;
-	if (nd->last_type != LAST_NORM)
-		goto exit;
-	if (nd->last.name[nd->last.len]) {
-		putname(nd->last.name);
-		goto exit;
-	}
 	if (count++==32) {
 		dentry = nd->dentry;
 		putname(nd->last.name);
 		goto ok;
 	}
-	dir = nd->dentry;
-	down(&dir->d_inode->i_sem);
-	dentry = lookup_hash(&nd->last, nd->dentry);
-	putname(nd->last.name);
-	goto do_last;
+	putname_str = &nd->last;
+	goto top;
 }
 
 /* SMP-safe */
--- clean/fs/open.c	Fri Feb  9 14:29:44 2001
+++ dirty/fs/open.c	Mon Feb  4 15:16:31 2002
@@ -623,14 +623,26 @@
 	if (namei_flags & O_TRUNC)
 		namei_flags |= 2;
 
+	nd.file = NULL;
 	error = open_namei(filename, namei_flags, mode, &nd);
+	if (nd.file)
+		return nd.file;
 	if (!error)
 		return dentry_open(nd.dentry, nd.mnt, flags);
 
 	return ERR_PTR(error);
 }
 
-struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)
+/*
+ * I split dentry_open() into two parts:
+ *    create_filp() - allocates and initializes a 'struct file'.
+ *    dentry_open() - calls the filesystem's ->open() operation with the newly allocated
+ *                    'struct file', to open the file.
+ *
+ * This was for the sake of the ->lookup_open() hack described above open_namei()
+ * in fs/namei.c
+ */
+struct file *create_filp(struct dentry *dentry, struct vfsmount *mnt, int flags)
 {
 	struct file * f;
 	struct inode *inode;
@@ -656,6 +668,27 @@
 	f->f_op = fops_get(inode->i_fop);
 	if (inode->i_sb)
 		file_move(f, &inode->i_sb->s_files);
+	return f;
+
+cleanup_file:
+	put_filp(f);
+cleanup_dentry:
+	dput(dentry);
+	mntput(mnt);
+	return ERR_PTR(error);
+}
+
+struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)
+{
+	struct file *f;
+	struct inode *inode;
+	int error;
+
+	f = create_filp(dentry, mnt, flags);
+	if (IS_ERR(f))
+		return f;
+
+	inode = dentry->d_inode;
 	if (f->f_op && f->f_op->open) {
 		error = f->f_op->open(inode,f);
 		if (error)
@@ -671,9 +704,7 @@
 		put_write_access(inode);
 	f->f_dentry = NULL;
 	f->f_vfsmnt = NULL;
-cleanup_file:
 	put_filp(f);
-cleanup_dentry:
 	dput(dentry);
 	mntput(mnt);
 	return ERR_PTR(error);
--- clean/include/linux/fs.h	Fri Apr 27 18:48:28 2001
+++ dirty/include/linux/fs.h	Tue Feb  5 16:23:28 2002
@@ -279,6 +279,7 @@
 #include <linux/umsdos_fs_i.h>
 #include <linux/iso_fs_i.h>
 #include <linux/nfs_fs_i.h>
+#include <linux/nfs4_fs_i.h>
 #include <linux/sysv_fs_i.h>
 #include <linux/affs_fs_i.h>
 #include <linux/ufs_fs_i.h>
@@ -443,6 +444,7 @@
 		struct umsdos_inode_info	umsdos_i;
 		struct iso_inode_info		isofs_i;
 		struct nfs_inode_info		nfs_i;
+		struct nfs4_inode_info		nfs4_i;
 		struct sysv_inode_info		sysv_i;
 		struct affs_inode_info		affs_i;
 		struct ufs_inode_info		ufs_i;
@@ -538,6 +540,7 @@
 
 	union {
 		struct nfs_lock_info	nfs_fl;
+		struct nfs4_lock_info	nfs4_fl;
 	} fl_u;
 };
 
@@ -559,11 +562,15 @@
 extern int fcntl_setlk64(unsigned int, unsigned int, struct flock64 *);
 
 /* fs/locks.c */
+extern struct file_lock *locks_alloc_lock(int account);
+extern void locks_free_lock(struct file_lock *fl);
+extern void locks_insert_lock(struct file_lock **ppos, struct file_lock *fl);
+extern void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait);
 extern void locks_init_lock(struct file_lock *);
 extern void locks_copy_lock(struct file_lock *, struct file_lock *);
 extern void locks_remove_posix(struct file *, fl_owner_t);
 extern void locks_remove_flock(struct file *);
-extern struct file_lock *posix_test_lock(struct file *, struct file_lock *);
+extern struct file_lock *__posix_test_lock(struct inode *, struct file_lock *);
 extern int posix_lock_file(struct file *, struct file_lock *, unsigned int);
 extern void posix_block_lock(struct file_lock *, struct file_lock *);
 extern void posix_unblock_lock(struct file_lock *);
@@ -572,6 +579,12 @@
 extern int lock_may_read(struct inode *, loff_t start, unsigned long count);
 extern int lock_may_write(struct inode *, loff_t start, unsigned long count);
 
+static inline struct file_lock *
+posix_test_lock(struct file *filp, struct file_lock *fl)
+{
+	return __posix_test_lock(filp->f_dentry->d_inode, fl);
+}
+
 struct fasync_struct {
 	int	magic;
 	int	fa_fd;
@@ -594,6 +607,11 @@
 	struct qstr last;
 	unsigned int flags;
 	int last_type;
+
+	/* new fields added for nfsv4 */
+	struct file *file;
+	int open_flags;
+	int mode;
 };
 
 #define DQUOT_USR_ENABLED	0x01		/* User diskquotas enabled */
@@ -623,6 +641,7 @@
 #include <linux/msdos_fs_sb.h>
 #include <linux/iso_fs_sb.h>
 #include <linux/nfs_fs_sb.h>
+#include <linux/nfs4_fs_sb.h>
 #include <linux/sysv_fs_sb.h>
 #include <linux/affs_fs_sb.h>
 #include <linux/ufs_fs_sb.h>
@@ -674,6 +693,7 @@
 		struct msdos_sb_info	msdos_sb;
 		struct isofs_sb_info	isofs_sb;
 		struct nfs_sb_info	nfs_sb;
+		struct nfs4_sb_info	nfs4_sb;
 		struct sysv_sb_info	sysv_sb;
 		struct affs_sb_info	affs_sb;
 		struct ufs_sb_info	ufs_sb;
@@ -792,6 +812,7 @@
 	int (*revalidate) (struct dentry *);
 	int (*setattr) (struct dentry *, struct iattr *);
 	int (*getattr) (struct dentry *, struct iattr *);
+	int (*lookup_open) (struct inode *, struct dentry **, struct nameidata *);
 };
 
 /*
@@ -904,6 +925,7 @@
  * REALLY kosha for root NFS and nfs_lock
  */ 
 #define LOCK_USE_CLNT 1
+#define LOCK_DONE     2 /* tells locks.c not to manipulate lock list locally */
 
 #define FLOCK_VERIFY_READ  1
 #define FLOCK_VERIFY_WRITE 2
@@ -962,6 +984,7 @@
 extern int do_truncate(struct dentry *, loff_t start);
 
 extern struct file *filp_open(const char *, int, int);
+struct file *create_filp(struct dentry *, struct vfsmount *, int);
 extern struct file * dentry_open(struct dentry *, struct vfsmount *, int);
 extern int filp_close(struct file *, fl_owner_t id);
 extern char * getname(const char *);
--- clean/include/linux/list.h	Fri Feb 16 19:06:17 2001
+++ dirty/include/linux/list.h	Mon Feb  4 15:16:15 2002
@@ -112,6 +112,16 @@
 }
 
 /**
+ * list_singleton - tests whether a list contains a single element
+ * @head: the list to test.
+ */
+static __inline__ int list_singleton(struct list_head *head)
+{
+	struct list_head *next = head->next;
+	return (next != head) && (next->next == head);
+}
+
+/**
  * list_splice - join two lists
  * @list: the new list to add.
  * @head: the place to add it in the first list.
--- clean/include/linux/nfsd/interface.h	Tue Sep  5 15:56:51 2000
+++ dirty/include/linux/nfsd/interface.h	Tue May 15 15:31:46 2001
@@ -12,7 +12,7 @@
 
 #include <linux/config.h>
 
-#ifdef CONFIG_NFSD_MODULE
+#if defined(CONFIG_NFSD_MODULE) || defined(CONFIG_NFSD4_MODULE)
 
 extern struct nfsd_linkage {
 	long (*do_nfsservctl)(int cmd, void *argp, void *resp);
--- clean/include/linux/sunrpc/msg_prot.h	Mon Apr  7 14:35:32 1997
+++ dirty/include/linux/sunrpc/msg_prot.h	Tue Nov 13 12:15:47 2001
@@ -17,6 +17,7 @@
 	RPC_AUTH_SHORT = 2,
 	RPC_AUTH_DES   = 3,
 	RPC_AUTH_KRB   = 4,
+	RPC_AUTH_GSS   = 6
 };
 
 enum rpc_msg_type {
@@ -49,7 +50,9 @@
 	RPC_AUTH_REJECTEDCRED = 2,
 	RPC_AUTH_BADVERF = 3,
 	RPC_AUTH_REJECTEDVERF = 4,
-	RPC_AUTH_TOOWEAK = 5
+	RPC_AUTH_TOOWEAK = 5,
+	RPCSEC_GSS_CREDPROBLEM = 13,
+	RPCSEC_GSS_CTXPROBLEM = 14
 };
 
 #define RPC_PMAP_PROGRAM	100000
--- clean/kernel/ksyms.c	Fri Apr 27 17:23:25 2001
+++ dirty/kernel/ksyms.c	Tue Feb  5 16:23:50 2002
@@ -211,10 +211,15 @@
 EXPORT_SYMBOL(page_hash_bits);
 EXPORT_SYMBOL(page_hash_table);
 EXPORT_SYMBOL(file_lock_list);
+EXPORT_SYMBOL(locks_alloc_lock);
+EXPORT_SYMBOL(locks_free_lock);
+EXPORT_SYMBOL(locks_insert_lock);
+EXPORT_SYMBOL(locks_delete_lock);
 EXPORT_SYMBOL(locks_init_lock);
 EXPORT_SYMBOL(locks_copy_lock);
 EXPORT_SYMBOL(posix_lock_file);
-EXPORT_SYMBOL(posix_test_lock);
+EXPORT_SYMBOL(__posix_test_lock);
+EXPORT_SYMBOL(locks_remove_posix);
 EXPORT_SYMBOL(posix_block_lock);
 EXPORT_SYMBOL(posix_unblock_lock);
 EXPORT_SYMBOL(locks_mandatory_area);
@@ -259,6 +264,7 @@
 
 /* for stackable file systems (lofs, wrapfs, cryptfs, etc.) */
 EXPORT_SYMBOL(default_llseek);
+EXPORT_SYMBOL(create_filp);
 EXPORT_SYMBOL(dentry_open);
 EXPORT_SYMBOL(filemap_nopage);
 EXPORT_SYMBOL(filemap_sync);
@@ -366,6 +372,7 @@
 #endif
 
 #ifdef CONFIG_SMP
+EXPORT_SYMBOL(atomic_dec_and_lock);
 EXPORT_SYMBOL(del_timer_sync);
 #endif
 EXPORT_SYMBOL(mod_timer);
--- clean/fs/Config.in	Thu Apr 12 15:25:53 2001
+++ dirty/fs/Config.in	Tue Feb  5 16:23:25 2002
@@ -85,21 +85,46 @@
    dep_tristate 'NFS server support' CONFIG_NFSD $CONFIG_INET
    dep_mbool '  Provide NFSv3 server support' CONFIG_NFSD_V3 $CONFIG_NFSD
 
+   dep_tristate 'NFSv4 file system support (EXPERIMENTAL)' CONFIG_NFS4_FS $CONFIG_INET
+   dep_tristate 'NFSv4 server support (EXPERIMENTAL)' CONFIG_NFSD4 $CONFIG_INET
+
+   # CONFIG_LOCKD depends on CONFIG_NFS_FS, CONFIG_NFSD
    if [ "$CONFIG_NFS_FS" = "y" -o "$CONFIG_NFSD" = "y" ]; then
-      define_tristate CONFIG_SUNRPC y
       define_tristate CONFIG_LOCKD y
    else
       if [ "$CONFIG_NFS_FS" = "m" -o "$CONFIG_NFSD" = "m" ]; then
-	 define_tristate CONFIG_SUNRPC m
 	 define_tristate CONFIG_LOCKD m
    else
-	 define_tristate CONFIG_SUNRPC n
 	 define_tristate CONFIG_LOCKD n
       fi
    fi
    if [ "$CONFIG_NFSD_V3" = "y" -o "$CONFIG_NFS_V3" = "y" ]; then
      define_bool CONFIG_LOCKD_V4 y
    fi
+
+   # CONFIG_NFS4 depends on CONFIG_NFS4_FS, CONFIG_NFSD4
+   if [ "$CONFIG_NFS4_FS" = "y" -o "$CONFIG_NFSD4" = "y" ]; then
+      define_tristate CONFIG_NFS4 y
+   else
+      if [ "$CONFIG_NFS4_FS" = "m" -o "$CONFIG_NFSD4" = "m" ]; then
+	 define_tristate CONFIG_NFS4 m
+   else
+	 define_tristate CONFIG_NFS4 n
+      fi
+   fi
+
+   # CONFIG_SUNRPC depends on CONFIG_NFS_FS, CONFIG_NFSD, CONFIG_NFS4_FS, CONFIG_NFSD4
+   if [ "$CONFIG_NFS_FS" = "y" -o "$CONFIG_NFSD" = "y" -o "$CONFIG_NFS4_FS" = "y" -o "$CONFIG_NFSD4" = "y" ]; then
+      define_tristate CONFIG_SUNRPC y
+   else
+      if [ "$CONFIG_NFS_FS" = "m" -o "$CONFIG_NFSD" = "m" -o "$CONFIG_NFS4_FS" = "m" -o "$CONFIG_NFSD4" = "m" ]; then
+	 define_tristate CONFIG_SUNRPC m
+   else
+	 define_tristate CONFIG_SUNRPC n
+      fi
+   fi
+
+   dep_mbool 'RPCSEC_GSS support (EXPERIMENTAL)' CONFIG_SUNRPC_GSS $CONFIG_SUNRPC
 
    dep_tristate 'SMB file system support (to mount Windows shares etc.)' CONFIG_SMB_FS $CONFIG_INET
    if [ "$CONFIG_SMB_FS" != "n" ]; then
--- clean/include/linux/sunrpc/auth.h	Fri Apr 27 18:48:29 2001
+++ dirty/include/linux/sunrpc/auth.h	Mon Feb  4 18:20:46 2002
@@ -51,6 +51,8 @@
 	unsigned int		au_cslack;	/* call cred size estimate */
 	unsigned int		au_rslack;	/* reply verf size guess */
 	unsigned int		au_flags;	/* various flags */
+	uid_t           proxy_uid;
+	uid_t           proxy_gid;
 	struct rpc_authops *	au_ops;		/* operations */
 
 	/* per-flavor data */
@@ -70,10 +72,10 @@
 	struct rpc_auth *	(*create)(struct rpc_clnt *);
 	void			(*destroy)(struct rpc_auth *);
 
-	struct rpc_cred *	(*crcreate)(int);
+	struct rpc_cred *	(*crcreate)(struct rpc_task *);
 	void			(*crdestroy)(struct rpc_cred *);
 
-	int			(*crmatch)(struct rpc_cred *, int);
+	int			(*crmatch)(struct rpc_cred *, int, int, int);
 	u32 *			(*crmarshal)(struct rpc_task *, u32 *, int);
 	int			(*crrefresh)(struct rpc_task *);
 	u32 *			(*crvalidate)(struct rpc_task *, u32 *);
@@ -81,6 +83,7 @@
 
 extern struct rpc_authops	authunix_ops;
 extern struct rpc_authops	authnull_ops;
+extern struct rpc_authops	authgss_ops;
 #ifdef CONFIG_SUNRPC_SECURE
 extern struct rpc_authops	authdes_ops;
 #endif
@@ -89,7 +92,7 @@
 int			rpcauth_unregister(struct rpc_authops *);
 struct rpc_auth *	rpcauth_create(unsigned int, struct rpc_clnt *);
 void			rpcauth_destroy(struct rpc_auth *);
-struct rpc_cred *	rpcauth_lookupcred(struct rpc_auth *, int);
+struct rpc_cred *	rpcauth_lookupcred(struct rpc_task *);
 struct rpc_cred *	rpcauth_bindcred(struct rpc_task *);
 void			rpcauth_holdcred(struct rpc_task *);
 void			rpcauth_releasecred(struct rpc_auth *,
--- clean/include/linux/sunrpc/msg_prot.h	Mon Apr  7 14:35:32 1997
+++ dirty/include/linux/sunrpc/msg_prot.h	Tue Nov 13 12:15:47 2001
@@ -17,6 +17,7 @@
 	RPC_AUTH_SHORT = 2,
 	RPC_AUTH_DES   = 3,
 	RPC_AUTH_KRB   = 4,
+	RPC_AUTH_GSS   = 6
 };
 
 enum rpc_msg_type {
@@ -49,7 +50,9 @@
 	RPC_AUTH_REJECTEDCRED = 2,
 	RPC_AUTH_BADVERF = 3,
 	RPC_AUTH_REJECTEDVERF = 4,
-	RPC_AUTH_TOOWEAK = 5
+	RPC_AUTH_TOOWEAK = 5,
+	RPCSEC_GSS_CREDPROBLEM = 13,
+	RPCSEC_GSS_CTXPROBLEM = 14
 };
 
 #define RPC_PMAP_PROGRAM	100000
--- clean/include/linux/sunrpc/sched.h	Fri Apr 27 18:48:28 2001
+++ dirty/include/linux/sunrpc/sched.h	Mon Feb  4 18:20:46 2002
@@ -102,6 +102,10 @@
 #define RPC_TASK_ROOTCREDS	0x0040		/* force root creds */
 #define RPC_TASK_DYNAMIC	0x0080		/* task was kmalloc'ed */
 #define RPC_TASK_KILLED		0x0100		/* task was killed */
+#define RPC_TASK_ROOT_CREDS	0x0200		/* XXX: really force root creds */
+#define RPC_TASK_DONTZERO	0x0400		/* don't memset buffer to zero */
+/*ANDROS*/
+#define RPC_TASK_PROXYCREDS 0x0800 /* use proxy_uid/gid */
 
 #define RPC_IS_ASYNC(t)		((t)->tk_flags & RPC_TASK_ASYNC)
 #define RPC_IS_SETUID(t)	((t)->tk_flags & RPC_TASK_SETUID)
--- clean/include/linux/sunrpc/xdr.h	Fri Apr 27 18:48:30 2001
+++ dirty/include/linux/sunrpc/xdr.h	Mon Feb  4 18:20:47 2002
@@ -27,6 +27,18 @@
 };
 
 /*
+ * GSSD name/id interface
+ */
+void print_hexl(u32 *p, u_int length, u_int offset);
+
+int gssd_name_to_uid(struct xdr_netobj *name, u32 *uidp);
+int gssd_uid_to_name(u32 *uid, struct xdr_netobj *namep);
+
+int gssd_name_to_gid(struct xdr_netobj *name, u32 *gidp);
+int gssd_gid_to_name(u32 *gid, struct xdr_netobj *namep);
+
+
+/*
  * This is the generic XDR function. rqstp is either a rpc_rqst (client
  * side) or svc_rqst pointer (server side).
  * Encode functions always assume there's enough room in the buffer.
--- clean/net/sunrpc/clnt.c	Thu Apr 19 11:38:50 2001
+++ dirty/net/sunrpc/clnt.c	Mon Feb  4 18:13:40 2002
@@ -323,8 +323,10 @@
 	/* Bind the user cred */
 	if (task->tk_msg.rpc_cred != NULL) {
 		rpcauth_holdcred(task);
-	} else
+	} else {
+    dprintk("RPC: call_setup calling bindcred\n"); 
 		rpcauth_bindcred(task);
+  }
 
 	if (task->tk_status == 0)
 		task->tk_action = call_reserve;
@@ -830,6 +832,12 @@
 			printk(KERN_NOTICE "call_verify: server requires stronger "
 			       "authentication.\n");
 			break;
+		case RPCSEC_GSS_CREDPROBLEM: 
+			printk(KERN_NOTICE "call_verify: RPCSEC_GSS_CREDPROBLEM\n");
+			break;
+		case RPCSEC_GSS_CTXPROBLEM:
+			printk(KERN_NOTICE "call_verify: RPCSEC_GSS_CTXPROBLEM\n");
+			break;
 		default:
 			printk(KERN_WARNING "call_verify: unknown auth error: %x\n", n);
 			error = -EIO;
@@ -859,7 +867,8 @@
 	if (task->tk_garb_retry) {
 		task->tk_garb_retry--;
 		dprintk(KERN_WARNING "RPC: garbage, retrying %4d\n", task->tk_pid);
-		task->tk_action = call_encode;
+		if(task->tk_client->cl_auth->au_ops->au_flavor != RPC_AUTH_GSS)
+			task->tk_action = call_encode;
 		return NULL;
 	}
 	printk(KERN_WARNING "RPC: garbage, exit EIO\n");
--- clean/net/sunrpc/sched.c	Tue Apr  3 16:45:37 2001
+++ dirty/net/sunrpc/sched.c	Mon Feb  4 18:13:40 2002
@@ -718,6 +718,12 @@
 		__rpc_remove_wait_queue(task);
 		spin_unlock_bh(&rpc_queue_lock);
 
+	dprintk("RPC:__rpc_schedule: flavor %d current [%d:%d] proxy [%d:%d]\n ",
+         task->tk_client->cl_auth->au_ops->au_flavor, current->uid, 
+         current->gid,task->tk_client->cl_auth->proxy_uid,
+         task->tk_client->cl_auth->proxy_gid );
+	dprintk("RPC:__rpc_schedule: cred: %p\n",task->tk_msg.rpc_cred);
+
 		__rpc_execute(task);
 
 		if (++count >= 200 || current->need_resched) {
@@ -783,6 +789,7 @@
 rpc_free(void *buffer)
 {
 	if (buffer != swap_buffer) {
+		dprintk("RPC:      freeing buffer %p\n", buffer);
 		kfree(buffer);
 		return;
 	}
@@ -878,7 +885,7 @@
 rpc_release_task(struct rpc_task *task)
 {
 	struct rpc_task	*next, *prev;
-
+	
 	dprintk("RPC: %4d release task\n", task->tk_pid);
 
 #ifdef RPC_DEBUG
--- clean/net/sunrpc/auth.c	Fri Feb  9 14:29:44 2001
+++ dirty/net/sunrpc/auth.c	Tue Feb  5 00:03:03 2002
@@ -5,7 +5,7 @@
  *
  * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
  */
-
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
@@ -20,11 +20,28 @@
 
 #define RPC_MAXFLAVOR	8
 
+#ifdef CONFIG_SUNRPC_GSS
 static struct rpc_authops *	auth_flavors[RPC_MAXFLAVOR] = {
 	&authnull_ops,		/* AUTH_NULL */
 	&authunix_ops,		/* AUTH_UNIX */
-	NULL,			/* others can be loadable modules */
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	&authgss_ops,   /* AUTH_GSS */
+};
+#else /* CONFIG_SUNRPC_GSS */
+static struct rpc_authops * auth_flavors[RPC_MAXFLAVOR] = {
+	&authnull_ops,    /* AUTH_NULL */
+	&authunix_ops,    /* AUTH_UNIX */
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,   /* AUTH_GSS */
 };
+#endif /* CONFIG_SUNRPC_GSS */
+
 
 int
 rpcauth_register(struct rpc_authops *ops)
@@ -170,15 +187,31 @@
 
 /*
  * Look up a process' credentials in the authentication cache
+ *
+ * GSS NOTE: this is the original interface which will not work 
+ * for rpcsec_gss which needs the struct task.
+ * rpcauth_lookup_credcache(struct rpc_auth *auth, int taskflags)
  */
 static struct rpc_cred *
-rpcauth_lookup_credcache(struct rpc_auth *auth, int taskflags)
+rpcauth_lookup_credcache(struct rpc_task *task)
 {
+	struct rpc_auth *auth = task->tk_auth;
+	int taskflags = task->tk_flags;
 	struct rpc_cred	**q, *cred = NULL;
 	int		nr = 0;
 
-	if (!(taskflags & RPC_TASK_ROOTCREDS))
+  dprintk("RPC: auth->proxy_uid/gid [%d:%d]\n",auth->proxy_uid,auth->proxy_gid);
+
+  if (taskflags & RPC_TASK_PROXYCREDS)
+    dprintk("RPC: rpcauth_lookup_credcache found RPC_TASK_PROXYCREDS \n");
+
+  if ((auth->proxy_uid != -1) && (current->uid == 0)) {
+    nr = auth->proxy_uid & RPC_CREDCACHE_MASK;
+    dprintk("RPC: rpcauth_lookup_credcache using PROXY uid nr %d\n",nr);
+  } else if (!(taskflags & RPC_TASK_ROOTCREDS)) {
 		nr = current->uid & RPC_CREDCACHE_MASK;
+    dprintk("RPC: rpcauth_lookup_credcache using current uid nr %d \n",nr);
+  }
 
 	if (time_before(auth->au_nextgc, jiffies))
 		rpcauth_gc_credcache(auth);
@@ -187,7 +220,7 @@
 	q = &auth->au_credcache[nr];
 	while ((cred = *q) != NULL) {
 		if (!(cred->cr_flags & RPCAUTH_CRED_DEAD) &&
-		    auth->au_ops->crmatch(cred, taskflags)) {
+		    auth->au_ops->crmatch(cred, taskflags,auth->proxy_uid,auth->proxy_gid)) {
 			*q = cred->cr_next;
 			break;
 		}
@@ -196,13 +229,14 @@
 	spin_unlock(&rpc_credcache_lock);
 
 	if (!cred) {
-		cred = auth->au_ops->crcreate(taskflags);
+		cred = auth->au_ops->crcreate(task);
 #ifdef RPC_DEBUG
 		if (cred)
 			cred->cr_magic = RPCAUTH_CRED_MAGIC;
 #endif
 	}
 
+	/* XXX: ANDROS: why insert into cred cache if found in above while loop? */
 	if (cred)
 		rpcauth_insert_credcache(auth, cred);
 
@@ -212,7 +246,7 @@
 /*
  * Remove cred handle from cache
  */
-static void
+void
 rpcauth_remove_credcache(struct rpc_auth *auth, struct rpc_cred *cred)
 {
 	struct rpc_cred	**q, *cr;
@@ -232,12 +266,14 @@
 	spin_unlock(&rpc_credcache_lock);
 }
 
+
+
 struct rpc_cred *
-rpcauth_lookupcred(struct rpc_auth *auth, int taskflags)
+rpcauth_lookupcred(struct rpc_task *task)
 {
 	dprintk("RPC:     looking up %s cred\n",
-		auth->au_ops->au_name);
-	return rpcauth_lookup_credcache(auth, taskflags);
+		task->tk_auth->au_ops->au_name);
+	return rpcauth_lookup_credcache(task);
 }
 
 struct rpc_cred *
@@ -247,9 +283,16 @@
 
 	dprintk("RPC: %4d looking up %s cred\n",
 		task->tk_pid, task->tk_auth->au_ops->au_name);
-	task->tk_msg.rpc_cred = rpcauth_lookup_credcache(auth, task->tk_flags);
-	if (task->tk_msg.rpc_cred == 0)
-		task->tk_status = -ENOMEM;
+
+	dprintk("RPC: bindcred calling lookup_credcache\n");
+	task->tk_msg.rpc_cred = rpcauth_lookup_credcache(task);
+
+	dprintk("RPC: bindcred returning. cred %p \n",task->tk_msg.rpc_cred);
+
+	if (task->tk_msg.rpc_cred == 0){
+    if(task->tk_status != -EACCES)
+		  task->tk_status = -ENOMEM;
+  }
 	return task->tk_msg.rpc_cred;
 }
 
@@ -258,7 +301,7 @@
 {
 	dprintk("RPC:     matching %s cred %d\n",
 		auth->au_ops->au_name, taskflags);
-	return auth->au_ops->crmatch(cred, taskflags);
+	return auth->au_ops->crmatch(cred, taskflags,auth->proxy_uid, auth->proxy_gid);
 }
 
 void
--- clean/net/sunrpc/auth_null.c	Fri Feb  9 14:29:44 2001
+++ dirty/net/sunrpc/auth_null.c	Mon Feb  4 18:13:39 2002
@@ -29,6 +29,7 @@
 	auth->au_rslack = 2;
 	auth->au_ops = &authnull_ops;
 	auth->au_expire = 1800 * HZ;
+  auth->proxy_uid = auth->proxy_gid = -1;
 	rpcauth_init_credcache(auth);
 
 	return (struct rpc_auth *) auth;
@@ -46,11 +47,11 @@
  * Create NULL creds for current process
  */
 static struct rpc_cred *
-nul_create_cred(int flags)
+nul_create_cred(struct rpc_task *task)
 {
 	struct rpc_cred	*cred;
 
-	if (!(cred = (struct rpc_cred *) rpc_allocate(flags, sizeof(*cred))))
+	if (!(cred = (struct rpc_cred *) rpc_allocate(task->tk_flags, sizeof(*cred))))
 		return NULL;
 	cred->cr_count = 0;
 	cred->cr_flags = RPCAUTH_CRED_UPTODATE;
@@ -72,7 +73,7 @@
  * Match cred handle against current process
  */
 static int
-nul_match(struct rpc_cred *cred, int taskflags)
+nul_match(struct rpc_cred *cred, int taskflags,u32 proxy_uid,u32 proxy_gid)
 {
 	return 1;
 }
--- clean/net/sunrpc/auth_unix.c	Fri Feb  9 14:29:44 2001
+++ dirty/net/sunrpc/auth_unix.c	Mon Feb  4 18:13:39 2002
@@ -45,6 +45,7 @@
 	auth->au_rslack = 2;	/* assume AUTH_NULL verf */
 	auth->au_expire = UNX_CRED_EXPIRE;
 	auth->au_ops = &authunix_ops;
+  auth->proxy_uid = auth->proxy_gid = -1;
 
 	rpcauth_init_credcache(auth);
 
@@ -60,7 +61,7 @@
 }
 
 static struct rpc_cred *
-unx_create_cred(int flags)
+unx_create_cred(struct rpc_task *task)
 {
 	struct unx_cred	*cred;
 	int		i;
@@ -68,15 +69,20 @@
 	dprintk("RPC:      allocating UNIX cred for uid %d gid %d\n",
 				current->uid, current->gid);
 
-	if (!(cred = (struct unx_cred *) rpc_allocate(flags, sizeof(*cred))))
+	if (!(cred = (struct unx_cred *) rpc_allocate(task->tk_flags, sizeof(*cred))))
 		return NULL;
 
 	cred->uc_count = 0;
 	cred->uc_flags = RPCAUTH_CRED_UPTODATE;
-	if (flags & RPC_TASK_ROOTCREDS) {
+	if (task->tk_flags & RPC_TASK_ROOTCREDS) {
 		cred->uc_uid = cred->uc_fsuid = 0;
 		cred->uc_gid = cred->uc_fsgid = 0;
 		cred->uc_gids[0] = NOGROUP;
+	} else if (task->tk_flags & RPC_TASK_ROOT_CREDS) {
+		cred->uc_uid = cred->uc_uid = 0;
+		cred->uc_uid = cred->uc_gid = 0;
+		cred->uc_uid = cred->uc_fsuid = 0;
+		cred->uc_uid = cred->uc_fsgid = 0;
 	} else {
 		int groups = current->ngroups;
 		if (groups > NFS_NGROUPS)
@@ -92,6 +98,10 @@
 		  cred->uc_gids[i] = NOGROUP;
 	}
 
+	dprintk("RPC:      unx_create_creds : cred->uc_uid %d cred->uc_gid %d "
+		"cred->uc_fsuid %d  cred->uc_fsgid %d\n",
+		cred->uc_uid, cred->uc_gid, cred->uc_fsuid, cred->uc_fsgid);
+
 	return (struct rpc_cred *) cred;
 }
 
@@ -129,7 +139,7 @@
  * request root creds (e.g. for NFS swapping).
  */
 static int
-unx_match(struct rpc_cred *rcred, int taskflags)
+unx_match(struct rpc_cred *rcred, int taskflags,u32 proxy_uid,u32 proxy_gid)
 {
 	struct unx_cred	*cred = (struct unx_cred *) rcred;
 	int		i;
--- clean/net/sunrpc/svcauth.c	Sat Apr 29 01:50:39 2000
+++ dirty/net/sunrpc/svcauth.c	Tue Feb  5 00:03:04 2002
@@ -8,7 +8,7 @@
  * CHANGES
  * 19-Apr-2000 Chris Evans      - Security fix
  */
-
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/sched.h>
 #include <linux/sunrpc/types.h>
@@ -28,6 +28,9 @@
  */
 static void	svcauth_null(struct svc_rqst *rqstp, u32 *statp, u32 *authp);
 static void	svcauth_unix(struct svc_rqst *rqstp, u32 *statp, u32 *authp);
+#ifdef CONFIG_SUNRPC_GSS 
+extern void	svcauth_gss(struct svc_rqst *rqstp, u32 *statp, u32 *authp);
+#endif /* CONFIG_SUNRPC_GSS */
 
 /*
  * Max number of authentication flavors we support
@@ -37,11 +40,27 @@
 /*
  * Table of authenticators
  */
+#ifdef CONFIG_SUNRPC_GSS
 static auth_fn_t	authtab[RPC_SVCAUTH_MAX] = {
 	svcauth_null,
 	svcauth_unix,
 	NULL,
+	NULL,
+	NULL,
+	NULL,
+	svcauth_gss,
+};
+#else /* CONFIG_SUNRPC_GSS */
+static auth_fn_t	authtab[RPC_SVCAUTH_MAX] = {
+	svcauth_null,
+	svcauth_unix,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
 };
+#endif /* CONFIG_SUNRPC_GSS */
 
 void
 svc_authenticate(struct svc_rqst *rqstp, u32 *statp, u32 *authp)
--- clean/net/sunrpc/pmap_clnt.c	Wed Jun 21 15:43:37 2000
+++ dirty/net/sunrpc/pmap_clnt.c	Tue May 15 18:29:30 2001
@@ -86,7 +86,6 @@
 	task->tk_action = NULL;
 }
 
-#ifdef CONFIG_ROOT_NFS
 char *in_ntoa(__u32 in);
 
 int
@@ -114,7 +113,6 @@
 	}
 	return status;
 }
-#endif
 
 static void
 pmap_getport_done(struct rpc_task *task)
--- clean/net/sunrpc/sunrpc_syms.c	Thu Jan 11 18:53:02 2001
+++ dirty/net/sunrpc/sunrpc_syms.c	Tue Feb  5 00:03:04 2002
@@ -21,12 +21,16 @@
 #include <linux/sunrpc/svc.h>
 #include <linux/sunrpc/svcsock.h>
 #include <linux/sunrpc/auth.h>
+#ifdef CONFIG_SUNRPC_GSS
+#include <linux/sunrpc/auth_gss.h>
+#endif /* CONFIG_SUNRPC_GSS */
 
 /* RPC scheduler */
 EXPORT_SYMBOL(rpc_allocate);
 EXPORT_SYMBOL(rpc_free);
 EXPORT_SYMBOL(rpc_execute);
 EXPORT_SYMBOL(rpc_init_task);
+EXPORT_SYMBOL(rpc_remove_wait_queue);
 EXPORT_SYMBOL(rpc_sleep_on);
 EXPORT_SYMBOL(rpc_wake_up_next);
 EXPORT_SYMBOL(rpc_wake_up_task);
@@ -55,6 +59,7 @@
 EXPORT_SYMBOL(xprt_create_proto);
 EXPORT_SYMBOL(xprt_destroy);
 EXPORT_SYMBOL(xprt_set_timeout);
+EXPORT_SYMBOL(xprt_release);
 
 /* Client credential cache */
 EXPORT_SYMBOL(rpcauth_register);
@@ -66,6 +71,22 @@
 EXPORT_SYMBOL(rpcauth_bindcred);
 EXPORT_SYMBOL(rpcauth_matchcred);
 EXPORT_SYMBOL(rpcauth_releasecred);
+/* added for gss */
+EXPORT_SYMBOL(rpcauth_create);
+EXPORT_SYMBOL(rpcauth_destroy);
+
+#ifdef CONFIG_SUNRPC_GSS
+EXPORT_SYMBOL(gss_cmp_triples);
+/* GSSD null proc interface */
+EXPORT_SYMBOL(xdr_encode_rpc_gss_init_res);
+EXPORT_SYMBOL(print_hexl);
+#endif /* CONFIG_SUNRPC_GSS */
+
+/* GSSD upcall interface */
+EXPORT_SYMBOL(gssd_name_to_uid);
+EXPORT_SYMBOL(gssd_name_to_gid);
+EXPORT_SYMBOL(gssd_uid_to_name);
+EXPORT_SYMBOL(gssd_gid_to_name);
 
 /* RPC server stuff */
 EXPORT_SYMBOL(svc_create);
--- clean/net/sunrpc/gssd_clnt.c	Thu Feb  7 18:04:30 2002
+++ dirty/net/sunrpc/gssd_clnt.c	Tue Feb  5 00:03:04 2002
@@ -0,0 +1,778 @@
+/*
+ *  linux/net/sunrpc/gssd_clnt.c
+ *
+ *  GSSD client.
+ *
+ *  Copyright (c) 2000 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  Dug Song       <dugsong@monkey.org>
+ *  Andy Adamson   <andros@umich.edu>
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the University nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: gssd_clnt.c,v 1.6 2002/02/05 05:03:04 kmsmith Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/uio.h>
+#include <linux/in.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/xprt.h>
+#include <linux/sunrpc/sched.h>
+#ifdef CONFIG_SUNRPC_GSS
+#include <linux/sunrpc/auth_gss.h>
+#else /* CONFIG_SUNRPC_GSS */
+typedef struct xdr_netobj    GSS_BUFFER_T;
+#endif /* CONFIG_SUNRPC_GSS */
+
+#ifdef RPC_DEBUG
+# define RPCDBG_FACILITY	RPCDBG_AUTH
+#endif
+
+/*
+ * GSSD gss.x definitions
+ */
+
+#define RPC_GSSD_PROGRAM	100666
+#define RPC_GSSD_VERSION	1
+
+#ifdef CONFIG_SUNRPC_GSS
+
+enum gssd_proc {
+	GSSD_NAME_TO_UID	= 1,
+	GSSD_UID_TO_NAME	= 2,
+	GSSD_NAME_TO_GID	= 3,
+	GSSD_GID_TO_NAME	= 4,
+	GSSD_INIT_SEC_CONTEXT	= 5,
+	GSSD_ACCEPT_SEC_CONTEXT	= 6,
+	GSSD_IMPORT_SEC_CONTEXT	= 7
+};
+
+/* NOTE: these structures mirror those declared in gssd's gss.x */
+struct gssd_init_sec_context_arg {
+	u32		uid;
+	GSS_CTX_ID_T	ctx_id;
+	GSS_BUFFER_T	target_name;
+	GSS_OID		mech;
+	GSS_BUFFER_T	input_token;
+};
+
+struct gssd_init_sec_context_res {
+	GSS_CTX_ID_T	ctx_id;
+	u32		maj_stat;
+	u32		min_stat;
+	GSS_BUFFER_T	output_token;
+	u32		ret_flags;
+};
+
+struct gssd_accept_sec_context_arg {
+	GSS_CTX_ID_T	ctx_id;
+	GSS_BUFFER_T	input_token;
+};
+
+struct gssd_accept_sec_context_res {
+	GSS_CTX_ID_T	ctx_id;
+	u32		maj_stat;
+	u32		min_stat;
+	GSS_BUFFER_T	src_name;
+	GSS_BUFFER_T	output_token;
+	u32		ret_flags;
+};
+
+struct gssd_import_sec_context_arg {
+	GSS_CTX_ID_T	ctx_id;
+
+};
+
+struct gssd_import_sec_context_res {
+	u32		maj_stat;
+	u32		min_stat;
+	GSS_BUFFER_T	ctx_token;
+};
+#else /* CONFIG_SUNRPC_GSS */
+
+enum gssd_proc {
+	GSSD_NAME_TO_UID	= 1,
+	GSSD_UID_TO_NAME	= 2,
+	GSSD_NAME_TO_GID	= 3,
+	GSSD_GID_TO_NAME	= 4
+};
+
+#endif /* CONFIG_SUNRPC_GSS */
+
+struct rpc_program	gssd_program;
+
+static struct rpc_clnt *
+gssd_create(void)
+{
+	struct sockaddr_in	sin;
+	struct rpc_xprt	       *xprt;
+	struct rpc_clnt	       *clnt;
+	int			port;
+	uint   saved_fsuid = current->fsuid;
+	kernel_cap_t saved_cap = current->cap_effective;
+
+	dprintk("RPC: gssd_create()\n");
+	sin.sin_family = AF_INET;
+	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+	
+	if ((port = rpc_getport_external(&sin, RPC_GSSD_PROGRAM,
+					 RPC_GSSD_VERSION, IPPROTO_UDP)) < 0)
+		return NULL;
+	sin.sin_port = ntohs(port);
+
+	/* create rpc socket as root so we get a priv port */
+	current->fsuid = 0;
+	cap_raise(current->cap_effective, CAP_NET_BIND_SERVICE);
+	xprt = xprt_create_proto(IPPROTO_UDP, &sin, NULL);
+	current->fsuid = saved_fsuid;
+	current->cap_effective = saved_cap;
+	if(xprt == NULL)
+		return NULL;
+	
+	clnt = rpc_create_client(xprt, "localhost", &gssd_program,
+				 RPC_GSSD_VERSION, RPC_AUTH_UNIX);
+	if (!clnt) {
+		xprt_destroy(xprt);
+	} else {
+		clnt->cl_softrtry = 1;
+		clnt->cl_chatty   = 1;
+		clnt->cl_oneshot  = 1;
+	}
+	return clnt;
+}
+
+#ifdef CONFIG_SUNRPC_GSS
+/*
+ *  GSSD RPCSEC_GSS test, just for fun.
+ */
+int
+gssd_client_test(struct xdr_netobj *name, u32 *uidp)
+{
+	struct sockaddr_in	sin;
+	struct rpc_xprt	       *xprt;
+	struct rpc_clnt	       *clnt;
+	int			port, error;
+	struct rpc_auth	       *save_auth;
+
+	sin.sin_family = AF_INET;
+	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+	
+	if ((port = rpc_getport_external(&sin, RPC_GSSD_PROGRAM,
+					 RPC_GSSD_VERSION, IPPROTO_UDP)) < 0)
+		return -1;
+	
+	if (!(xprt = xprt_create_proto(IPPROTO_UDP, &sin, NULL)))
+		return -1;
+	xprt->addr.sin_port = htons(port);
+	
+	clnt = rpc_create_client(xprt, "localhost", &gssd_program,
+				 RPC_GSSD_VERSION, RPC_AUTH_UNIX);
+	if (!clnt) {
+		xprt_destroy(xprt);
+		return -1;
+	} else {
+		clnt->cl_softrtry = 1;
+		clnt->cl_chatty   = 1;
+		clnt->cl_oneshot  = 1;
+	}
+	save_auth = clnt->cl_auth;
+	rpcauth_create(RPC_AUTH_GSS, clnt);
+	
+	error = rpc_call(clnt, GSSD_NAME_TO_UID, name, uidp, RPC_TASK_ROOT_CREDS);
+
+	clnt->cl_auth = save_auth;
+	
+	if (error < 0) {
+		printk(KERN_WARNING
+		       "RPC: failed to contact gssd (errno %d).\n", error);
+	}
+	dprintk("RPC: gssd_client_test(%.*s) = %d\n",
+		(int) name->len, name->data, *uidp);
+	
+	/* Client deleted automatically because cl_oneshot == 1 */
+	return error;
+}	
+#endif /* CONFIG_SUNRPC_GSS */
+	
+/*
+ * Convert Unix/GSS username/principal to Unix UID.
+ */
+int
+gssd_name_to_uid(struct xdr_netobj *name, u32 *uidp)
+{
+	struct rpc_clnt		*gssd_clnt;
+	unsigned int		error = 0;
+
+	dprintk("RPC: gssd_name_to_uid \n");
+	if (!(gssd_clnt = gssd_create())) {
+		printk("RPC: couldn't create gssd client\n");
+		return -EACCES;
+	}
+
+	error = rpc_call(gssd_clnt, GSSD_NAME_TO_UID, name, uidp, RPC_TASK_ROOT_CREDS);
+	
+	if (error < 0) {
+		printk(KERN_WARNING
+		       "RPC: failed to contact gssd (errno %d).\n", error);
+	}
+	dprintk("RPC: gssd_name_to_uid(%.*s) = %d\n",
+		(int) name->len, name->data, *uidp);
+	
+	/* Client deleted automatically because cl_oneshot == 1 */
+	return error;
+}
+
+/*
+ * Convert Unix UID to Unix username.
+ */
+int
+gssd_uid_to_name(u32 *uid, struct xdr_netobj *namep)
+{
+	GSS_BUFFER_T		bufout;
+	struct rpc_clnt		*gssd_clnt;
+	unsigned int		error = 0;
+
+	if (!(gssd_clnt = gssd_create())) {
+		printk("RPC: couldn't create gssd client\n");
+		return -EACCES;
+	}
+	error = rpc_call(gssd_clnt, GSSD_UID_TO_NAME, uid, (void *)&bufout, RPC_TASK_ROOT_CREDS);
+	
+	if (error < 0) {
+		printk(KERN_WARNING
+		       "RPC: failed to contact gssd (errno %d).\n", error);
+	}
+	if (bufout.len > 0)
+		namep->data = rpc_allocate(0, bufout.len);
+	memcpy(namep->data, bufout.data, bufout.len);
+	namep->len = bufout.len;
+	
+	dprintk("RPC: gssd_uid_to_name(%d) = %.*s\n",
+		*uid, (int) namep->len, namep->data);
+	
+	/* Client deleted automatically because cl_oneshot == 1 */
+	return error;
+}
+
+/*
+ * Convert Unix group name to Unix GID.
+ */
+int
+gssd_name_to_gid(struct xdr_netobj *name, u32 *gidp)
+{
+	struct rpc_clnt		*gssd_clnt;
+	unsigned int		error = 0;
+
+	if (!(gssd_clnt = gssd_create())) {
+		printk("RPC: couldn't create gssd client\n");
+		return -EACCES;
+	}
+	error = rpc_call(gssd_clnt, GSSD_NAME_TO_GID, name, gidp, RPC_TASK_ROOT_CREDS);
+	
+	if (error < 0) {
+		printk(KERN_WARNING
+		       "RPC: failed to contact gssd (errno %d).\n", error);
+	}
+	dprintk("RPC: gssd_name_to_gid(%.*s) = %d\n",
+		(int) name->len, name->data, *gidp);
+	
+	/* Client deleted automatically because cl_oneshot == 1 */
+	return error;
+}
+
+/*
+ * Convert Unix GID to Unix name.
+ */
+int
+gssd_gid_to_name(u32 *gid, struct xdr_netobj *namep)
+{
+	GSS_BUFFER_T		bufout;
+	struct rpc_clnt		*gssd_clnt;
+	unsigned int		error = 0;
+
+	if (!(gssd_clnt = gssd_create())) {
+		printk("RPC: couldn't create gssd client\n");
+		return -EACCES;
+	}
+	error = rpc_call(gssd_clnt, GSSD_GID_TO_NAME, gid, (void *)&bufout, RPC_TASK_ROOT_CREDS);
+	
+	if (error < 0) {
+		printk(KERN_WARNING
+		       "RPC: failed to contact gssd (errno %d).\n", error);
+	}
+	if (bufout.len > 0)
+		namep->data = rpc_allocate(0, bufout.len);
+	memcpy(namep->data, bufout.data, bufout.len);
+	namep->len = bufout.len;
+	
+	dprintk("RPC: gssd_gid_to_name(%d) = %.*s\n",
+		*gid, (int) namep->len, namep->data);
+	
+	/* Client deleted automatically because cl_oneshot == 1 */
+	return error;
+}
+
+#ifdef CONFIG_SUNRPC_GSS
+/*
+ * Initialize GSS security context.
+ */
+int
+gssd_init_sec_context(u32		uid,
+		      u32	       *maj_statp,
+		      u32	       *min_statp,
+		      GSS_CTX_ID_T     *ctx_id,
+		      GSS_BUFFER_T     *service_name,
+		      GSS_OID	       *mech,
+		      GSS_BUFFER_T     *input_token,
+		      GSS_BUFFER_T     *output_tokenp,
+		      u32	       *ret_flagsp)
+{
+	struct gssd_init_sec_context_arg	arg;
+	struct gssd_init_sec_context_res	res;
+	struct rpc_clnt		*gssd_clnt;
+	unsigned int		error = 0;
+
+	dprintk("RPC: gssd_init_sec_context(): ctx_id %p \n",ctx_id);
+	if (!(gssd_clnt = gssd_create())) {
+		printk("RPC: couldn't create gssd client\n");
+		return -EACCES;
+	}
+	arg.uid = uid;
+	arg.ctx_id = *ctx_id;
+	arg.target_name = *service_name;
+	arg.mech = *mech;
+	arg.input_token = *input_token;
+
+	dprintk("RPC: in gssd_init_sec_context() doing rpc_call()\n");
+
+	error = rpc_call(gssd_clnt, GSSD_INIT_SEC_CONTEXT, &arg, &res, RPC_TASK_ROOT_CREDS);
+
+	if (error < 0) {
+		printk(KERN_WARNING
+		       "RPC: failed to contact gssd (errno %d).\n", error);
+		return error;
+	}
+
+	/* Save our results. */
+	dprintk("RPC: in gssd_init_sec_context() res.ctx_id.len %d\n",res.ctx_id.len);
+
+	if (res.ctx_id.len > 0) {
+		if(!(ctx_id->data = rpc_allocate(0, res.ctx_id.len))){
+			return -ENOMEM;
+		}
+		memcpy(ctx_id->data, res.ctx_id.data, res.ctx_id.len);
+	}
+	ctx_id->len = res.ctx_id.len;
+	
+	*maj_statp = res.maj_stat;
+	*min_statp = res.min_stat;
+
+	dprintk("RPC: in gssd_init_sec_context() res.output_token.len %d\n",
+	   res.output_token.len);
+	if (res.output_token.len > 0) {
+		if(!(output_tokenp->data = rpc_allocate(0, res.output_token.len))){
+			return -ENOMEM;
+		}
+		memcpy(output_tokenp->data, res.output_token.data,
+		       res.output_token.len);
+	}
+	output_tokenp->len = res.output_token.len;
+	
+	*ret_flagsp = res.ret_flags;
+	
+	dprintk("RPC: gssd_init_sec_ctx()\n");
+	
+	/* Client deleted automatically because cl_oneshot == 1 */
+	return error;
+}
+
+/*
+ * Accept GSS security context.
+ */
+int
+gssd_accept_sec_context(u32		*maj_statp,
+			u32		*min_statp,
+			GSS_CTX_ID_T	*ctx_id,
+			GSS_BUFFER_T	*input_token,
+			GSS_BUFFER_T	*client_namep,
+			GSS_BUFFER_T	*output_tokenp,
+			u32		*ret_flagsp)
+{
+	struct gssd_accept_sec_context_arg	arg;
+	struct gssd_accept_sec_context_res	res;
+	struct rpc_clnt		*gssd_clnt;
+	unsigned int		error = 0;
+
+	if (!(gssd_clnt = gssd_create())) {
+		printk("RPC: couldn't create gssd client\n");
+		return -EACCES;
+	}
+	arg.ctx_id = *ctx_id;
+	arg.input_token = *input_token;
+
+	error = rpc_call(gssd_clnt, GSSD_ACCEPT_SEC_CONTEXT, &arg, &res, RPC_TASK_ROOT_CREDS);
+
+	if (error < 0) {
+		printk(KERN_WARNING
+		       "RPC: failed to contact gssd (errno %d).\n", error);
+	}
+	/* Save our results. */
+	if (res.ctx_id.len > 0) {
+		ctx_id->data = rpc_allocate(0, res.ctx_id.len);
+		memcpy(ctx_id->data, res.ctx_id.data, res.ctx_id.len);
+	}
+	ctx_id->len = res.ctx_id.len;
+	
+	*maj_statp = res.maj_stat;
+	*min_statp = res.min_stat;
+
+	if (res.src_name.len > 0) {
+		client_namep->data = rpc_allocate(0, res.src_name.len);
+		memcpy(client_namep->data, res.src_name.data,
+		       res.src_name.len);
+	}
+	client_namep->len = res.src_name.len;
+	
+	if (res.output_token.len > 0) {
+		output_tokenp->data = rpc_allocate(0, res.output_token.len);
+		memcpy(output_tokenp->data, res.output_token.data,
+		       res.output_token.len);
+	}
+	output_tokenp->len = res.output_token.len;
+
+	*ret_flagsp = res.ret_flags;
+	
+	dprintk("RPC: gssd_accept_sec_ctx()\n");
+
+	/* Client deleted automatically because cl_oneshot == 1 */
+	return error;
+}
+
+/*
+ * Import GSS security context.
+ */
+int
+gssd_import_sec_context(u32		*maj_statp,
+			u32		*min_statp,
+			GSS_CTX_ID_T	*ctx_id,
+      GSS_BUFFER_T  *context_token)
+{
+	GSS_BUFFER_T	        ctx_token;
+	struct gssd_import_sec_context_arg	arg;
+	struct gssd_import_sec_context_res	res;
+	struct rpc_clnt		*gssd_clnt;
+	unsigned int		error = 0;
+
+	dprintk("RPC: gssd_import_sec_context: maj_stat %d min_stat %d ctx_id %p\n",
+            *maj_statp,*min_statp,ctx_id);
+	if (!(gssd_clnt = gssd_create())) {
+		printk("RPC: couldn't create gssd client\n");
+		return -EACCES;
+	}
+	arg.ctx_id = *ctx_id;
+
+	error = rpc_call(gssd_clnt, GSSD_IMPORT_SEC_CONTEXT, &arg, &res, RPC_TASK_ROOT_CREDS);
+
+	if (error < 0) {
+		printk(KERN_WARNING
+		       "RPC: failed to contact gssd (errno %d).\n", error);
+	}
+
+	*maj_statp = res.maj_stat;
+	*min_statp = res.min_stat;
+
+	if (res.ctx_token.len > 0) {
+
+	  dprintk("RPC: gssd_import_sec_context results: res.maj_stat %d res.min_stat %d res.ctx_token.len %d res.ctx_token.data %p\n",res.maj_stat, res.min_stat,res.ctx_token.len,res.ctx_token.data); 
+
+		ctx_token.data = rpc_allocate(0, res.ctx_token.len);
+		memcpy(ctx_token.data, res.ctx_token.data,
+		       res.ctx_token.len);
+	}
+	ctx_token.len = res.ctx_token.len;
+
+  *context_token = ctx_token;
+	dprintk("RPC: gssd_import_sec_ctx() context_token %p:%d\n",context_token->data,context_token->len);
+
+
+	/* Client deleted automatically because cl_oneshot == 1 */
+	dprintk("RPC: gssd_import_sec_ctx() returning %d\n",error);
+	return error;
+}
+
+#endif /* CONFIG_SUNRPC_GSS */
+
+/*
+ * XDR encode/decode functions for GSSD
+ */
+static int
+xdr_error(struct rpc_rqst *req, u32 *p, void *dummy)
+{
+	return -EIO;
+}
+
+
+static int
+xdr_decode_name(struct rpc_rqst *req, u32 *p, struct xdr_netobj *name)
+{
+	xdr_decode_netobj(p, name);
+	dprintk("RPC: xdr_decode_name() = (%p:%d)\n", name->data, name->len);
+	return 0;
+}
+
+static int
+xdr_encode_name(struct rpc_rqst *req, u32 *p, struct xdr_netobj *name)
+{
+	dprintk("RPC: xdr_encode_name(%.*s)\n", (int) name->len, name->data);
+	p = xdr_encode_netobj(p, name);
+
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+		
+static int
+xdr_decode_id(struct rpc_rqst *req, u32 *p, u32 *idp)
+{
+	*idp = (u32) ntohl(*p);
+	dprintk("RPC: xdr_decode_id() = (%d)\n", *idp);
+	return 0;
+}
+
+static int
+xdr_encode_id(struct rpc_rqst *req, u32 *p, u32 *idp)
+{
+	dprintk("RPC: xdr_encode_id(%d)\n", *idp);
+	*p++ = (u32) htonl(*idp);
+	
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+#ifdef CONFIG_SUNRPC_GSS
+static int
+xdr_encode_init_sec_context_arg(struct rpc_rqst *req, u32 *p,
+				struct gssd_init_sec_context_arg *argp)
+{
+	dprintk("RPC: xdr_encode_init_sec_context_arg"
+		"(uid %d, ctx %p:%d, name %p:%d, mech %p:%d, token %p:%d)\n",
+		argp->uid, argp->ctx_id.data, argp->ctx_id.len,
+		argp->target_name.data, argp->target_name.len,
+		argp->mech.data, argp->mech.len,
+		argp->input_token.data, argp->input_token.len);
+	
+  	*p++ = (u32) htonl(argp->uid);
+	p = xdr_encode_netobj(p, &argp->ctx_id);
+	p = xdr_encode_netobj(p, &argp->target_name);
+ 	p = xdr_encode_netobj(p, &argp->mech);
+	p = xdr_encode_netobj(p, &argp->input_token);
+	
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+static int
+xdr_decode_init_sec_context_res(struct rpc_rqst *req, u32 *p,
+				struct gssd_init_sec_context_res *resp)
+{
+	p = xdr_decode_netobj(p, &resp->ctx_id);
+	if(!p) return -EIO;
+	resp->maj_stat = (u32) ntohl(*p++);
+	resp->min_stat = (u32) ntohl(*p++);
+	p = xdr_decode_netobj(p, &resp->output_token);
+	if(!p) return -EIO;
+	resp->ret_flags = (u32) ntohl(*p++);
+
+	dprintk("RPC: xdr_decode_init_sec_context_res() = "
+		"(ctx %p:%d, maj_stat %d, min_stat %d, "
+		"token %p:%d, flags %d)\n",
+		resp->ctx_id.data, resp->ctx_id.len,
+		resp->maj_stat, resp->min_stat,
+		resp->output_token.data, resp->output_token.len,
+		resp->ret_flags);
+#if 0	
+	if (!xdr_ressize_check(req, p))
+		return -EIO;
+#endif
+	return 0;
+}	
+
+static int
+xdr_encode_accept_sec_context_arg(struct rpc_rqst *req, u32 *p,
+				  struct gssd_accept_sec_context_arg *argp)
+{
+	dprintk("RPC: xdr_encode_accept_sec_context_arg("
+		"ctx %p:%d, token %p:%d)\n",
+		argp->ctx_id.data, argp->ctx_id.len,
+		argp->input_token.data, argp->input_token.len);
+	
+	p = xdr_encode_netobj(p, &argp->ctx_id);
+	p = xdr_encode_netobj(p, &argp->input_token);
+	
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+static int
+xdr_decode_accept_sec_context_res(struct rpc_rqst *req, u32 *p,
+				  struct gssd_accept_sec_context_res *resp)
+{
+	p = xdr_decode_netobj(p, &resp->ctx_id);
+	if(!p) return -EIO;
+	resp->maj_stat = (u32) ntohl(*p++);
+	resp->min_stat = (u32) ntohl(*p++);
+	p = xdr_decode_netobj(p, &resp->src_name);
+	if(!p) return -EIO;
+	p = xdr_decode_netobj(p, &resp->output_token);
+	if(!p) return -EIO;
+	resp->ret_flags = (u32) ntohl(*p++);
+
+	dprintk("RPC: xdr_decode_accept_sec_context_res() = "
+		"(ctx %p:%d, maj_stat %d, min_stat %d, name %p:%d, "
+		"token %p:%d, flags %d)\n",
+		resp->ctx_id.data, resp->ctx_id.len,
+		resp->maj_stat, resp->min_stat,
+		resp->src_name.data, resp->src_name.len,
+		resp->output_token.data, resp->output_token.len,
+		resp->ret_flags);
+#if 0	
+	if (!xdr_ressize_check(req, p))
+		return -EIO;
+#endif
+	return 0;
+}
+
+static int
+xdr_encode_import_sec_context_arg(struct rpc_rqst *req, u32 *p,
+				  struct gssd_import_sec_context_arg *argp)
+{
+	dprintk("RPC: xdr_encode_import_sec_context_arg: ctx %p:%d\n",
+		argp->ctx_id.data, argp->ctx_id.len);
+	
+	p = xdr_encode_netobj(p, &argp->ctx_id);
+	
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+static int
+xdr_decode_import_sec_context_res(struct rpc_rqst *req, u32 *p,
+				  struct gssd_import_sec_context_res *resp)
+{
+	resp->maj_stat = (u32) ntohl(*p++);
+	resp->min_stat = (u32) ntohl(*p++);
+	p = xdr_decode_netobj(p, &resp->ctx_token);
+	if(!p) return -EIO;
+
+	dprintk("RPC: xdr_decode_import_sec_context_res() = "
+		"maj_stat %d, min_stat %d,"
+		"ctx_token token %p:%d \n",
+		resp->maj_stat, resp->min_stat,
+		resp->ctx_token.data, resp->ctx_token.len);
+#if 0	
+	if (!xdr_ressize_check(req, p))
+		return -EIO;
+#endif
+	return 0;
+}
+
+
+static struct rpc_procinfo	gssd_procedures[] = {
+	{ "gssd_null",
+		(kxdrproc_t) xdr_error,	
+		(kxdrproc_t) xdr_error,	0, 0 },
+	{ "gssd_name_to_uid",
+		(kxdrproc_t) xdr_encode_name,	
+		(kxdrproc_t) xdr_decode_id, RPC_MAXNETNAMELEN >> 2, 1 },
+	{ "gssd_uid_to_name",
+		(kxdrproc_t) xdr_encode_id,	
+		(kxdrproc_t) xdr_decode_name, RPC_MAXNETNAMELEN >> 2, 1 },
+	{ "gssd_name_to_gid",
+		(kxdrproc_t) xdr_encode_name,	
+		(kxdrproc_t) xdr_decode_id, RPC_MAXNETNAMELEN >> 2, 1 },
+	{ "gssd_gid_to_name",
+		(kxdrproc_t) xdr_encode_id,	
+		(kxdrproc_t) xdr_decode_name, RPC_MAXNETNAMELEN >> 2, 1 },
+	{ "gssd_init_sec_context",
+		(kxdrproc_t) xdr_encode_init_sec_context_arg,
+		(kxdrproc_t) xdr_decode_init_sec_context_res,
+	  	XDR_MAX_NETOBJ >> 2, 1 },
+	{ "gssd_accept_sec_context",
+		(kxdrproc_t) xdr_encode_accept_sec_context_arg,
+		(kxdrproc_t) xdr_decode_accept_sec_context_res,
+	  	XDR_MAX_NETOBJ >> 2, 1 },
+	{ "gssd_import_sec_context",
+		(kxdrproc_t) xdr_encode_import_sec_context_arg,
+		(kxdrproc_t) xdr_decode_import_sec_context_res,
+	  	XDR_MAX_NETOBJ >> 2, 1 }
+};
+
+#else /* CONFIG_SUNRPC_GSS */
+
+static struct rpc_procinfo	gssd_procedures[] = {
+	{ "gssd_null",
+		(kxdrproc_t) xdr_error,	
+		(kxdrproc_t) xdr_error,	0, 0 },
+	{ "gssd_name_to_uid",
+		(kxdrproc_t) xdr_encode_name,	
+		(kxdrproc_t) xdr_decode_id, RPC_MAXNETNAMELEN >> 2, 1 },
+	{ "gssd_uid_to_name",
+		(kxdrproc_t) xdr_encode_id,	
+		(kxdrproc_t) xdr_decode_name, RPC_MAXNETNAMELEN >> 2, 1 },
+	{ "gssd_name_to_gid",
+		(kxdrproc_t) xdr_encode_name,	
+		(kxdrproc_t) xdr_decode_id, RPC_MAXNETNAMELEN >> 2, 1 },
+	{ "gssd_gid_to_name",
+		(kxdrproc_t) xdr_encode_id,	
+		(kxdrproc_t) xdr_decode_name, RPC_MAXNETNAMELEN >> 2, 1 }
+};
+
+#endif /* CONFIG_SUNRPC_GSS */
+static struct rpc_version	gssd_version1 = {
+	1,
+	sizeof(gssd_procedures) / sizeof(gssd_procedures[0]),
+	gssd_procedures
+};
+
+static struct rpc_version *	gssd_version[] = {
+	NULL,
+	&gssd_version1
+};
+
+static struct rpc_stat		gssd_stats;
+
+struct rpc_program	gssd_program = {
+	"GSSD",
+	RPC_GSSD_PROGRAM,
+	sizeof(gssd_version) / sizeof(gssd_version[0]),
+	gssd_version,
+	&gssd_stats,
+};
--- clean/net/sunrpc/Makefile	Fri Dec 29 17:07:24 2000
+++ dirty/net/sunrpc/Makefile	Tue Feb  5 10:36:33 2002
@@ -14,10 +14,11 @@
 obj-y    := clnt.o xprt.o sched.o \
 	    auth.o auth_null.o auth_unix.o \
 	    svc.o svcsock.o svcauth.o \
-	    pmap_clnt.o xdr.o sunrpc_syms.o
+	    pmap_clnt.o xdr.o sunrpc_syms.o gssd_clnt.o
 
 obj-$(CONFIG_PROC_FS) += stats.o
 obj-$(CONFIG_SYSCTL) += sysctl.o
+obj-$(CONFIG_SUNRPC_GSS) += auth_gss.o  svcauth_gss.o gss_cache.o gss_svc_cache.o gss_mic.o gss_union.o gss_generic_token.o gss_generic_ordering.o gss_k5serialize.o gss_k5ser.o gss_intern_ctx.o gss_util_crypto.o gss_k5cksum.o gss_k5hash_md5.o gss_k5seal.o gss_k5unseal.o gss_k5encrypt.o gss_k5util_seqnum.o gss_k5decrypt.o
 
 obj-m  := $(O_TARGET)
 
--- clean/include/linux/nfs4_fs_i.h	Thu Feb  7 18:04:30 2002
+++ dirty/include/linux/nfs4_fs_i.h	Mon Feb  4 15:16:14 2002
@@ -0,0 +1,142 @@
+#ifndef _LINUX_NFS4_FS_I_H
+#define _LINUX_NFS4_FS_I_H
+
+/*
+ *  linux/fs/nfs4_fs_i.h
+ *
+ *  Copyright (c) 2001 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  Kendrick Smith <kmsmith@umich.edu>
+ *  Andy Adamson <andros@umich.edu>
+ *  
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *  
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the University nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#define NFS4_FH_INLINE_LEN		32
+
+struct nfs4_inode_info {
+	u64				fsid_major;
+	u64				fsid_minor;
+	u64				fileid;
+	unsigned short			flags;
+
+	/*
+	 * Filehandle - NFSv4 filehandles can be up to 128 bytes, but are
+	 * rarely this long in practice.  To avoid bloating our inodes,
+	 * we keep a small "inline" buffer in the inode, and allocate a
+	 * larger one seperately if needed.
+	 */
+	unsigned short			fh_len;
+	char *				fh_val;   /* normally points to fh_inline_val */
+	char				fh_inline_val[NFS4_FH_INLINE_LEN];
+	
+	/* Size of cached directory snapshot (meaningful only for directories). */
+	unsigned long			directory_size;
+	
+	/*
+	 * The basic algorithm for caching inodes is taken from NFSv3.
+	 *   @cache_jiffies - time we started caching this inode.
+	 *   @cache_changeid - changeid at that time.
+	 *   @cache_timeout - how long we can rely on cached data before needing to revalidate
+	 *   @cache_timeout_jiffies - time of last update to cache_timeout
+	 * If we have a delegation, we never need to revalidate.
+	 * This is all protected by the BKL.
+	 */
+	unsigned long			cache_jiffies;
+	u64				cache_changeid;
+	unsigned long			cache_timeout;
+	unsigned long			cache_timeout_jiffies;
+	struct nfs4fs_delegation *	delegation;
+	
+	/*
+	 * These are lists of file_data structures corresponding to OPENs for read
+	 * (resp. write) on the server.  They are used for mmap'ed or delegated files.
+	 * They are protected by the state_spinlock.
+	 */
+	struct list_head		read_opens;
+	struct list_head		write_opens;
+
+	/*
+	 * If a delegation is held, entries in the access cache are listed here.
+	 * As explained in fs/nfs4fs/access.c, this is protected by the BKL.
+	 */
+	struct list_head		access_cache_entries;
+
+	/* This is needed if strict locking is turned on for this mountpoint. */
+	struct semaphore		locking_sema;
+
+	/* Used by flushd.
+	 *   @flushd_nextscan: wakeup time (for this inode only).
+	 *   @flushd_list: linked into superblock's flushd_inodes list.
+	 *   @flushd_read: head of list of pending read requests
+	 *   @flushd_write: head of list of pending write (=dirty+commit) requests
+	 *   @flushd_dirty: head of list of pending dirty requests
+	 *   @flushd_commit: head of list of pending commit requests
+	 *   @flushd_nread: count of pending read requests
+	 *   @flushd_nread: count of pending dirty requests
+	 *   @flushd_nread: count of pending commit requests
+	 *   @flushd_dirtybytes: combined size of all pending dirty requests.
+	 * All of this is protected by the wreq_spinlock.
+	 */
+	unsigned long			flushd_nextscan;
+	struct list_head		flushd_list;
+	struct list_head		flushd_read;
+	struct list_head		flushd_write;
+	struct list_head		flushd_dirty;
+	struct list_head		flushd_commit;
+	unsigned int			flushd_nread;
+	unsigned int			flushd_ndirty;
+	unsigned int			flushd_ncommit;
+};
+
+/* Inode flags.
+ *   NFS4_INO_REVALIDATING:	attribute revalidation in progress
+ *   NFS4_INO_SNAPSHOT:	do we have snapshot in page cache?  (meaningful only for directories)
+ * We don't use the INO_FLUSH flag from NFSv3.  Instead we just see whether the flushd_list
+ * is empty.
+ *
+ * TODO: Implement STALE flag!
+ */
+#define NFS4_INO_REVALIDATING		0x0001
+#define NFS4_INO_SNAPSHOT		0x0002
+
+/*  These auxiliary lock fields are only used for "strict locking".
+ *  Invariant: current_type is
+ *    a write type   iff   (base_type is a write type || (nr_blockedwrite != 0)
+ *    UNLOCK_LT      iff   (base_type == UNLOCK_LT) && (nr_blockedread == 0) &&
+ *                         (nr_blockedwrite == 0)
+ *    a read type    otherwise
+ */
+struct nfs4_lock_info {
+	int base_type;
+	int current_type;
+	int nr_blockedread;
+	int nr_blockedwrite;
+};
+
+#endif
--- clean/include/linux/nfs4_fs_sb.h	Thu Feb  7 18:04:30 2002
+++ dirty/include/linux/nfs4_fs_sb.h	Mon Feb  4 15:16:15 2002
@@ -0,0 +1,94 @@
+#ifndef _LINUX_NFS4_FS_SB_H
+#define _LINUX_NFS4_FS_SB_H
+
+/*
+ *  linux/fs/nfs4/nfs4_fs_sb.h
+ *
+ *  Copyright (c) 2001 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  Kendrick Smith <kmsmith@umich.edu>
+ *  Andy Adamson <andros@umich.edu>
+ *  
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *  
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the University nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+struct nfs4_sb_info {
+	struct rpc_clnt *	client;		/* RPC client handle */
+	unsigned int		namelen;	/* remote hostname */
+	char *			hostname;	/* remote hostname */
+	char *			mnt_path;	/* remote mount point, as one big string */
+	int			plen;		/* length of pathname array for mountpoint */
+	struct qstr *		pval;		/* pathname array for mountpoint */
+
+	/*
+	 * This leads to all of the state established with SETCLIENTID, OPEN, etc.
+	 * XXX: nfs_client is a terrible name, since we already have a field called client!
+	 */
+	struct nfs4fs_client *	nfs_client;
+	
+	/*
+	 * XXX: For now, keeping the same flags as v3.
+	 * Eventually, strict_locking and silly_rename should become new flags.
+	 * XXX: Need nocallback flag.
+	 */
+	int			flags;
+	unsigned int		strict_locking : 1,
+				silly_rename : 1;
+	
+	/* Transfer sizes for various types of I/O. */
+	unsigned int		rsize;		/* read size */
+	unsigned int		rpages;		/* read size (in pages) */
+	unsigned int		wsize;		/* write size */
+	unsigned int		wpages;		/* write size (in pages) */
+	unsigned int		dtsize;		/* readdir size */
+
+	/* Various timeouts. */
+	unsigned int		acregmin;	/* attr cache timeouts */
+	unsigned int		acregmax;
+	unsigned int		acdirmin;
+	unsigned int		acdirmax;
+	unsigned long		lock_mintimeout;
+	unsigned long		lock_maxtimeout;
+	
+	/* For renewd. */
+	unsigned long		last_renewal;		/* in jiffies */
+	unsigned int		lease_time;		/* in jiffies */
+
+	/* For flushd. */
+	struct rpc_task		*flushd;
+	unsigned long		flushd_timeout;   /* wakeup time for entire superblock */
+	struct list_head	flushd_inodes;    /* list of inodes with pending requests */
+
+	/* For callbacks. */
+	char			ip_addr[16];      /* our own IP address */
+	u32			callback_prog;    /* RPC program # for callback service */
+	unsigned short		callback_port;    /* UDP port for callback service */
+	struct list_head	delegations;
+};
+
+#endif
--- clean/include/linux/nfs4_mount.h	Thu Feb  7 18:04:30 2002
+++ dirty/include/linux/nfs4_mount.h	Wed Jun 13 15:11:22 2001
@@ -0,0 +1,63 @@
+#ifndef _LINUX_NFS4_MOUNT_H
+#define _LINUX_NFS4_MOUNT_H
+
+/*
+ * linux/include/linux/nfs4_mount.h
+ * from linux/include/nfs_mount.h
+ *     Copyright (C) 1992  Rick Sladkey
+ * NFSv4 implementation
+ *    Kendrick Smith  <kmsmith@umich.edu>
+ *    Andy Adamson  <andros@umich.edu>
+ *    Copyright (C) 2001 The Regents of the University of Michigan
+ */
+
+#define KERNEL_NFS_MOUNT_VERSION 5
+#define NFS4_MOUNT_VERSION	5
+#define NFS_MOUNT_VERSION	5
+#define NFS_MOUNT_VER4          0x0400  
+
+/*
+ * WARNING!  Do not delete or change the order of these fields.  If
+ * a new field is required then add it to the end.  The version field
+ * tracks which fields are present.  This will ensure some measure of
+ * mount-to-kernel version compatibility.  Some of these aren't used yet
+ * but here they are anyway.
+ */
+struct nfs4_mount_data {
+	int		version;		/* 1 */
+	int		fd;			/* 1 */
+	int		flags;			/* 1 */
+	int		rsize;			/* 1 */
+	int		wsize;			/* 1 */
+	int		timeo;			/* 1 */
+	int		retrans;		/* 1 */
+	int		acregmin;		/* 1 */
+	int		acregmax;		/* 1 */
+	int		acdirmin;		/* 1 */
+	int		acdirmax;		/* 1 */
+	struct sockaddr_in addr;		/* 1 */
+	char		hostname[256];		/* 1 */
+	int		namlen;			/* 2 */
+	unsigned int	bsize;			/* 3 */
+	char		mnt_path[256];
+	char		ipaddr[16];
+	int		strict_locking;
+	int		silly_rename;
+};
+
+/* bits in the flags field */
+
+#define NFS4_MOUNT_SOFT			0x0001	/* 1 */
+#define NFS4_MOUNT_INTR			0x0002	/* 1 */
+#define NFS4_MOUNT_SECURE		0x0004	/* 1 */
+#define NFS4_MOUNT_POSIX		0x0008	/* 1 */
+#define NFS4_MOUNT_NOCTO		0x0010	/* 1 */
+#define NFS4_MOUNT_NOAC			0x0020	/* 1 */
+#define NFS4_MOUNT_TCP			0x0040	/* 2 */
+#define NFS4_MOUNT_VER3			0x0080	/* 3 */
+#define NFS4_MOUNT_KERBEROS		0x0100	/* 3 */
+#define NFS4_MOUNT_NONLM		0x0200	/* 3 */
+#define NFS4_MOUNT_BROKEN_SUID		0x0400	/* 4 */
+#define NFS4_MOUNT_FLAGMASK		0xFFFF
+
+#endif
--- clean/include/linux/nfs4/nfs4.h	Thu Feb  7 18:04:30 2002
+++ dirty/include/linux/nfs4/nfs4.h	Mon Feb  4 15:16:16 2002
@@ -0,0 +1,381 @@
+/*
+ *  linux/fs/nfs4/nfs4.h
+ *
+ *  Copyright (c) 2001 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  Kendrick Smith <kmsmith@umich.edu>
+ *  Andy Adamson <andros@umich.edu>
+ *  
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *  
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the University nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _LINUX_NFS4_NFS4_H
+#define _LINUX_NFS4_NFS4_H
+
+#define NFS4_FHSIZE 128
+#define NFS4_VERIFIER_SIZE 8
+
+/* XXX: added NF4BAD to enum nfs_ftype4 */
+enum nfs_ftype4 {
+	NF4REG = 1,
+	NF4DIR = 2,
+	NF4BLK = 3,
+	NF4CHR = 4,
+	NF4LNK = 5,
+	NF4SOCK = 6,
+	NF4FIFO = 7,
+	NF4ATTRDIR = 8,
+	NF4NAMEDATTR = 9,
+	NF4BAD = 10
+};
+
+enum nfsstat4 {
+	NFS4_OK = 0,
+	NFS4ERR_PERM = 1,
+	NFS4ERR_NOENT = 2,
+	NFS4ERR_IO = 5,
+	NFS4ERR_NXIO = 6,
+	NFS4ERR_ACCES = 13,
+	NFS4ERR_EXIST = 17,
+	NFS4ERR_XDEV = 18,
+	NFS4ERR_NODEV = 19,
+	NFS4ERR_NOTDIR = 20,
+	NFS4ERR_ISDIR = 21,
+	NFS4ERR_INVAL = 22,
+	NFS4ERR_FBIG = 27,
+	NFS4ERR_NOSPC = 28,
+	NFS4ERR_ROFS = 30,
+	NFS4ERR_MLINK = 31,
+	NFS4ERR_NAMETOOLONG = 63,
+	NFS4ERR_NOTEMPTY = 66,
+	NFS4ERR_DQUOT = 69,
+	NFS4ERR_STALE = 70,
+	NFS4ERR_BADHANDLE = 10001,
+	NFS4ERR_BAD_COOKIE = 10003,
+	NFS4ERR_NOTSUPP = 10004,
+	NFS4ERR_TOOSMALL = 10005,
+	NFS4ERR_SERVERFAULT = 10006,
+	NFS4ERR_BADTYPE = 10007,
+	NFS4ERR_DELAY = 10008,
+	NFS4ERR_SAME = 10009,
+	NFS4ERR_DENIED = 10010,
+	NFS4ERR_EXPIRED = 10011,
+	NFS4ERR_LOCKED = 10012,
+	NFS4ERR_GRACE = 10013,
+	NFS4ERR_FHEXPIRED = 10014,
+	NFS4ERR_SHARE_DENIED = 10015,
+	NFS4ERR_WRONGSEC = 10016,
+	NFS4ERR_CLID_INUSE = 10017,
+	NFS4ERR_RESOURCE = 10018,
+	NFS4ERR_MOVED = 10019,
+	NFS4ERR_NOFILEHANDLE = 10020,
+	NFS4ERR_MINOR_VERS_MISMATCH = 10021,
+	NFS4ERR_STALE_CLIENTID = 10022,
+	NFS4ERR_STALE_STATEID = 10023,
+	NFS4ERR_OLD_STATEID = 10024,
+	NFS4ERR_BAD_STATEID = 10025,
+	NFS4ERR_BAD_SEQID = 10026,
+	NFS4ERR_NOT_SAME = 10027,
+	NFS4ERR_LOCK_RANGE = 10028,
+	NFS4ERR_SYMLINK = 10029,
+	NFS4ERR_READDIR_NOSPC = 10030,
+	NFS4ERR_LEASE_MOVED = 10031,
+	NFS4ERR_ATTRNOTSUPP	= 10032,
+	NFS4ERR_NO_GRACE	= 10033,
+	NFS4ERR_RECLAIM_BAD	= 10034,
+	NFS4ERR_RECLAIM_CONFLICT = 10035,
+	NFS4ERR_BADXDR		= 10036,
+	NFS4ERR_LOCKS_HELD	= 10037,
+#ifdef __linux__
+	NFS4ERR_REPLAY_ME = 10038,
+	NFS4ERR_RETRY_COMPOUND = 10039
+#endif
+};
+
+typedef u64 clientid4;
+typedef u32 seqid4;
+typedef char verifier4[NFS4_VERIFIER_SIZE];
+
+enum time_how4 {
+	SET_TO_SERVER_TIME4 = 0,
+	SET_TO_CLIENT_TIME4 = 1
+};
+
+#define ACL4_SUPPORT_ALLOW_ACL 0x00000001
+#define ACL4_SUPPORT_DENY_ACL 0x00000002
+#define ACL4_SUPPORT_AUDIT_ACL 0x00000004
+#define ACL4_SUPPORT_ALARM_ACL 0x00000008
+
+#define ACE4_ACCESS_ALLOWED_ACE_TYPE 0x00000000
+#define ACE4_ACCESS_DENIED_ACE_TYPE 0x00000001
+#define ACE4_SYSTEM_AUDIT_ACE_TYPE 0x00000002
+#define ACE4_SYSTEM_ALARM_ACE_TYPE 0x00000003
+
+#define ACE4_FILE_INHERIT_ACE 0x00000001
+#define ACE4_DIRECTORY_INHERIT_ACE 0x00000002
+#define ACE4_NO_PROPAGATE_INHERIT_ACE 0x00000004
+#define ACE4_INHERIT_ONLY_ACE 0x00000008
+#define ACE4_SUCCESSFUL_ACCESS_ACE_FLAG 0x00000010
+#define ACE4_FAILED_ACCESS_ACE_FLAG 0x00000020
+#define ACE4_IDENTIFIER_GROUP 0x00000040
+#define ACE4_OWNER 0x00000080
+#define ACE4_GROUP 0x00000100
+#define ACE4_EVERYONE 0x00000200
+
+#define ACE4_READ_DATA 0x00000001
+#define ACE4_LIST_DIRECTORY 0x00000001
+#define ACE4_WRITE_DATA 0x00000002
+#define ACE4_ADD_FILE 0x00000002
+#define ACE4_APPEND_DATA 0x00000004
+#define ACE4_ADD_SUBDIRECTORY 0x00000004
+#define ACE4_READ_NAMED_ATTRS 0x00000008
+#define ACE4_WRITE_NAMED_ATTRS 0x00000010
+#define ACE4_EXECUTE 0x00000020
+#define ACE4_DELETE_CHILD 0x00000040
+#define ACE4_READ_ATTRIBUTES 0x00000080
+#define ACE4_WRITE_ATTRIBUTES 0x00000100
+#define ACE4_DELETE 0x00010000
+#define ACE4_READ_ACL 0x00020000
+#define ACE4_WRITE_ACL 0x00040000
+#define ACE4_WRITE_OWNER 0x00080000
+#define ACE4_SYNCHRONIZE 0x00100000
+#define ACE4_GENERIC_READ 0x00120081
+#define ACE4_GENERIC_WRITE 0x00160106
+#define ACE4_GENERIC_EXECUTE 0x001200A0
+
+#define FH4_PERSISTENT 0x00000000
+#define FH4_NOEXPIRE_WITH_OPEN 0x00000001
+#define FH4_VOLATILE_ANY 0x00000002
+#define FH4_VOL_MIGRATION 0x00000004
+#define FH4_VOL_RENAME 0x00000008
+
+typedef char stateid4[16];
+
+enum nfs_lock_type4 {
+#ifdef __linux__
+	UNLOCK_LT = 0,
+#endif
+	READ_LT = 1,
+	WRITE_LT = 2,
+	READW_LT = 3,
+	WRITEW_LT = 4
+};
+
+#define ACCESS4_READ 0x00000001
+#define ACCESS4_LOOKUP 0x00000002
+#define ACCESS4_MODIFY 0x00000004
+#define ACCESS4_EXTEND 0x00000008
+#define ACCESS4_DELETE 0x00000010
+#define ACCESS4_EXECUTE 0x00000020
+#define ACCESS4_FULL   (ACCESS4_READ | ACCESS4_LOOKUP | ACCESS4_MODIFY | \
+			ACCESS4_EXTEND | ACCESS4_DELETE | ACCESS4_EXECUTE)
+
+enum open_claim_type4 {
+	CLAIM_NULL = 0,
+	CLAIM_PREVIOUS = 1,
+	CLAIM_DELEGATE_CUR = 2,
+	CLAIM_DELEGATE_PREV = 3
+};
+
+enum opentype4 {
+	OPEN4_NOCREATE = 0,
+	OPEN4_CREATE = 1
+};
+
+enum createmode4 {
+	UNCHECKED4 = 0,
+	GUARDED4 = 1,
+	EXCLUSIVE4 = 2
+};
+
+#define OPEN4_SHARE_ACCESS_READ 0x00000001
+#define OPEN4_SHARE_ACCESS_WRITE 0x00000002
+#define OPEN4_SHARE_ACCESS_BOTH 0x00000003
+#define OPEN4_SHARE_DENY_NONE 0x00000000
+#define OPEN4_SHARE_DENY_READ 0x00000001
+#define OPEN4_SHARE_DENY_WRITE 0x00000002
+#define OPEN4_SHARE_DENY_BOTH 0x00000003
+
+#define OPEN4_RESULT_MLOCK 0x00000001
+#define OPEN4_RESULT_CONFIRM 0x00000002
+#define LOCK4_RESULT_CONFIRM 0x00000001
+
+enum limit_by4 {
+	NFS_LIMIT_SIZE = 1,
+	NFS_LIMIT_BLOCKS = 2
+};
+
+enum open_delegation_type4 {
+	OPEN_DELEGATE_NONE = 0,
+	OPEN_DELEGATE_READ = 1,
+	OPEN_DELEGATE_WRITE = 2
+};
+
+enum stable_how4 {
+	UNSTABLE4 = 0,
+	DATA_SYNC4 = 1,
+	FILE_SYNC4 = 2
+};
+
+enum nfs_opnum4 {
+	OP_ACCESS = 3,
+	OP_CLOSE = 4,
+	OP_COMMIT = 5,
+	OP_CREATE = 6,
+	OP_DELEGPURGE = 7,
+	OP_DELEGRETURN = 8,
+	OP_GETATTR = 9,
+	OP_GETFH = 10,
+	OP_LINK = 11,
+	OP_LOCK = 12,
+	OP_LOCKT = 13,
+	OP_LOCKU = 14,
+	OP_LOOKUP = 15,
+	OP_LOOKUPP = 16,
+	OP_NVERIFY = 17,
+	OP_OPEN = 18,
+	OP_OPENATTR = 19,
+	OP_OPEN_CONFIRM = 20,
+	OP_OPEN_DOWNGRADE = 21,
+	OP_PUTFH = 22,
+	OP_PUTPUBFH = 23,
+	OP_PUTROOTFH = 24,
+	OP_READ = 25,
+	OP_READDIR = 26,
+	OP_READLINK = 27,
+	OP_REMOVE = 28,
+	OP_RENAME = 29,
+	OP_RENEW = 30,
+	OP_RESTOREFH = 31,
+	OP_SAVEFH = 32,
+	OP_SECINFO = 33,
+	OP_SETATTR = 34,
+	OP_SETCLIENTID = 35,
+	OP_SETCLIENTID_CONFIRM = 36,
+	OP_VERIFY = 37,
+	OP_WRITE = 38,
+	OP_LOCK_CONFIRM = 39
+};
+
+#define NFSPROC4_NULL 0
+#define NFSPROC4_COMPOUND 1
+
+enum nfs_cb_opnum4 {
+	OP_CB_GETATTR = 3,
+	OP_CB_RECALL = 4
+};
+
+#define CB_NULL 			0
+#define CB_COMPOUND			1
+
+#define NFS4_PROGRAM			100003
+
+#define NFS4_PORT			2049
+#define NFS4ACL				0
+
+/*
+ * In a seqid-mutating op, this macro controls which error return
+ * values trigger incrementation of the seqid.
+ */
+#if 1
+#define seqid_mutating_err(err)	    ((err) == 0)
+#else
+#define seqid_mutating_err(err)		\
+(((err) != NFS4ERR_STALE_CLIENTID) &&	\
+ ((err) != NFS4ERR_BAD_SEQID) &&	\
+ ((err) != NFS4ERR_STALE_STATEID) &&	\
+ ((err) != NFS4ERR_BAD_STATEID))
+#endif
+
+#ifndef XDR_LEN
+#  define XDR_LEN(n)			(((n) + 3) & ~3)
+#endif
+
+static inline u32
+opaque_hashval(const void *ptr, int nbytes)
+{
+	unsigned char *cptr = (unsigned char *) ptr;
+	u32 x = 0;
+
+	while (nbytes--) {
+		x *= 37;
+		x += *cptr++;
+	}
+	return x;
+}
+
+/* nfs4util.c */
+extern int kernerrno(int err);
+extern int nfserrno(int errno);
+extern void hex_dump(void *ptr, int nbytes);
+
+/* nfs4gss.c */
+/* XXX : the gss stuff assumes uid_t == gid_t; is this ok?  (The same assumption is made in
+   in the definition of 'struct nfs4fs_fattr' in fs/nfs4fs/decode.c) */
+#define GSS_OWNER              0
+#define GSS_GROUP              1
+#define GSS_CACHE_INLINE_LEN  32
+#define GSS_NAME_MAX         256
+
+struct gss_cacheent {
+	uid_t id;
+	struct qstr name;
+
+	/* fields after this point are private, for use by the gss cache - don't use */
+	atomic_t refcount;
+	struct list_head name_hash;
+	struct list_head id_hash;
+};
+
+extern void gss_init(void);
+extern int gss_get_name(int type, uid_t id, struct gss_cacheent **cachep);
+extern int gss_get_num(int type, unsigned int len, const unsigned char *name, uid_t *idp);
+extern void gss_free(struct gss_cacheent *p);
+extern void gss_shutdown(void);
+
+static inline void
+gss_put(struct gss_cacheent *p)
+{
+	if (atomic_dec_and_test(&p->refcount))
+		gss_free(p);
+}
+
+/* Test whether start+len >= 2^64, written in a way that doesn't make assumptions about how
+   the underlying hardware deals with arithmetic overflow. */
+#define LOFF_OVERFLOW(start, len)      ((u64)(len) > ~(u64)(start))
+
+/* acl.c */
+#if NFS4ACL
+fattr4_acl * posix_acl_to_nfsv4_acl(posix_acl_t *);
+posix_acl_t * nfsv4_acl_to_posix_acl(nfs4_acl *, int);
+int xdr_fattr4_acl_encode(u32 *p, fattr4_acl * f_acl);
+int xdr_fattr4_acl_decode(u32 *p, char *end, nfs4_acl **acl, u32 *fa_nacl);
+int fattr4_acl_release(fattr4_acl *acl);
+#endif
+
+#endif  /* _LINUX_NFS4_NFS4_H */
--- clean/include/linux/nfs4/nfs4fs_xdr.h	Thu Feb  7 18:04:30 2002
+++ dirty/include/linux/nfs4/nfs4fs_xdr.h	Mon Feb  4 15:16:18 2002
@@ -0,0 +1,565 @@
+/*
+ *  linux/fs/nfs4/nfs4fs_xdr.h
+ *
+ *  Copyright (c) 2001 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  Kendrick Smith <kmsmith@umich.edu>
+ *  
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *  
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the University nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _LINUX_NFS4_NFS4FS_XDR_H
+#define _LINUX_NFS4_NFS4FS_XDR_H
+
+/*
+ * These are all of the definitions used by our XDR encode/decode routines.
+ *
+ * Note!  This file is shared between the Linux and OpenBSD implementations;
+ * for now, Linux-specific regions are seperated with #ifdef __linux__
+ */
+
+#ifdef __linux__
+#define NFS4_MAXIO     4
+#endif
+
+struct nfs4fs_fattr {
+	unsigned int	fa_valid;	/* indicates which other fields are valid,
+					   see flags below */
+	u32		fa_type;
+	loff_t		fa_size;
+	u64		fa_fsid_major;
+	u64		fa_fsid_minor;
+	u64		fa_fileid;
+	umode_t		fa_mode;
+	nlink_t		fa_nlink;
+	uid_t		fa_uid;
+	gid_t		fa_gid;
+	u32		fa_rdev_major;
+	u32		fa_rdev_minor;
+	time_t		fa_atime;
+	time_t		fa_ctime;
+	time_t		fa_mtime;
+	u64		fa_maxread;
+	u64		fa_maxwrite;
+	u64		fa_ffree;
+	u64		fa_ftotal;
+	u32		fa_maxname;
+	u64		fa_savail;
+	u64		fa_sfree;
+	u64		fa_stotal;
+	u64		fa_changeid;
+	u32		fa_lease_time;
+	u64		fa_maxfilesize;
+#if 0	    /* ACL support coming soon! (but absent for now...) */
+	unsigned int 	fa_nacl;	/* number of acl's allocated */
+	nfs4_acl	*fa_acl;	/* array of size fa_nacl */
+#endif
+};
+
+/* flags for fa_valid */
+#define FA_SIZE		0x00000001
+#define FA_FSID		0x00000002
+#define FA_FILEID	0x00000004
+#define FA_MODE		0x00000008
+#define FA_NLINK	0x00000010
+#define FA_UID 		0x00000020
+#define FA_GID 		0x00000040
+#define FA_RDEV		0x00000080
+#define FA_ATIME	0x00000100
+#define FA_CTIME	0x00000200
+#define FA_MTIME	0x00000400
+#define FA_MAXREAD	0x00000800
+#define FA_MAXWRITE	0x00001000
+#define FA_TYPE		0x00002000
+#define FA_FFREE       	0x00004000
+#define FA_FTOTAL       0x00008000
+#define FA_MAXNAME	0x00010000
+#define FA_SAVAIL	0x00020000
+#define FA_SFREE 	0x00040000
+#define FA_STOTAL	0x00080000
+#define FA_CHANGEID	0x00100000
+#define FA_LEASE_TIME	0x00200000
+#define FA_MAXFILESIZE	0x00400000
+#define FA_ACL		0x00800000
+
+/* This is the equivalent of change_info4 in the .x file. */
+struct nfs4fs_change_info {
+	u32				atomic;
+	u64				before;
+	u64				after;
+};
+
+/* This is the equivalent of LOCK4denied in the .x file. */
+struct nfs4fs_lock_denied {
+	u64				offset;
+	u64				length;
+	u32				type;
+	/*
+	 * The actual on-the-wire response contains a lock_owner4 (clientid+lockowner string).
+	 * Our XDR decode routine translates this into:
+	 *    - is_local: a boolean which is set if the clientid matches ours.
+	 *    - id: a 'lockowner id' extracted from the lockowner string, if clientid matches.
+	 */
+	int				is_local;	/* boolean */
+	u32				id;		/* only defined if is_local != 0 */
+};
+
+struct nfs4fs_access {
+	/* request */
+	u32				ac_req_access;
+
+	/* response */
+	u32				ac_resp_supported;
+	u32				ac_resp_access;
+};
+
+/*
+ * For CLOSE, no definition is needed, since the fields of the on-the-wire request:
+ *      seqid
+ *      open_stateid
+ * and the response:
+ *      open_stateid
+ * are encapsulated in the ->seqid_holder and ->stateid_holder fields of the nfs4fs_compound.
+ */
+
+struct nfs4fs_commit {
+	/* request */
+	u64				co_start;
+	u32				co_len;
+
+	/* response */
+	verifier4			co_verifier;
+
+#ifdef __linux__
+	/* List of nfs4_page's associated with the COMMIT request. */
+	struct list_head		co_pages;
+#endif
+};
+
+struct nfs4fs_create {
+	/* request */
+	u32				cr_ftype;
+	union {
+		struct {				/* cr_ftype == NF4LNK */
+			u32		textlen;
+			const char *	text;
+		} symlink;
+		struct {				/* cr_ftype == {NF4BLK, NF4CHR} */
+			u32		specdata1;
+			u32		specdata2;
+		} device;
+	} u;
+	u32				cr_namelen;
+	const char *			cr_name;
+#ifdef __linux__
+	/*
+	 * Next in the on-the-wire request comes a 'fattr4' expressing attributes
+	 * for the newly-created file.  For Linux, it is most convenient to use an 'iattr',
+	 * and translate to a 'fattr4' in the XDR encode...
+	 */
+	struct iattr			cr_attrs;
+#endif
+
+	/* response */
+	struct nfs4fs_change_info	cr_cinfo;
+
+};
+#define cr_textlen			u.symlink.textlen
+#define cr_text				u.symlink.text
+#define cr_specdata1			u.device.specdata1
+#define cr_specdata2			u.device.specdata2
+
+/* DELEGPURGE currently not used */
+
+struct nfs4fs_delegreturn {
+	/* request */
+	stateid4			dr_stateid;
+};
+
+struct nfs4fs_getattr {
+	/* request */
+	/* The bitmap length field, first in the on-the-wire request, will always be 2. */
+	u32				gt_bmval[2];
+
+	/* response */
+	/* The on-the-wire response consists of a fattr4; we translate this into... */
+	struct nfs4fs_fattr		gt_attrs;
+};
+
+struct nfs4fs_getfh {
+	/* request: void */
+
+	/* response */
+	u32				gf_fhlen;
+	char *				gf_fhval;
+
+	/*
+	 * This buffer should normally be large enough to store the filehandle; if
+	 * not, we allocate a larger one...
+	 * XXX: get rid of magic number here!
+	 */
+	char				gf_inline_buf[32];
+};
+
+struct nfs4fs_link {
+	/* request */
+	u32				ln_namelen;
+	const char *			ln_name;
+
+	/* response */
+	struct nfs4fs_change_info	ln_cinfo;
+};
+
+struct nfs4fs_lock {
+	/* request */
+	u32				lk_type;
+	u32				lk_reclaim;        /* boolean */
+	u64				lk_offset;
+	u64				lk_length;
+	/*
+	 * The on-the-wire request now contains a 'locker4', which breaks down as
+	 * follows:  First, there is a boolean field indicating whether this is an
+	 * "open_owner->lock_owner" LOCK request, or "lock_owner->lock_owner".  If
+	 * this field is true, the subsequent fields are
+	 *     openowner's seqid
+	 *     open_stateid
+	 *     lockowner's seqid
+	 *     clientid
+	 *     lockowner name length
+	 *     lockowner name
+	 * If the boolean field is false, the subsequent fields are
+	 *     lock_stateid
+	 *     seqid
+	 * We don't need anything in the struct here, since all this can be filled
+	 * in from ->seqid_holder and ->stateid_holder in nfs4fs_compound.
+	 */
+
+	/* response */
+	u32				lk_rflags;
+};
+
+struct nfs4fs_lockt {
+	/* request */
+	u32				lt_type;
+	u64				lt_offset;
+	u64				lt_length;
+	/*
+	 * On-the-wire, this becomes
+	 *     clientid
+	 *     lockowner name length
+	 *     lockowner name
+	 */
+	struct nfs4fs_lockowner *	lt_owner;
+
+	/* response */
+	struct nfs4fs_lock_denied	lt_denied;
+};
+
+struct nfs4fs_locku {
+	/* request */
+	u32				lu_type;
+	/*
+	 * On-the-wire, the following fields live here:
+	 *      seqid
+	 *      stateid
+	 * We don't need anything in the struct here, since these can be filled
+	 * in from ->seqid_holder and ->stateid_holder in nfs4fs_compound.
+	 */
+	u64				lu_offset;
+	u64				lu_length;
+
+	/* response */
+	stateid4			lu_stateid;
+};
+
+struct nfs4fs_lookup {
+	/* request */
+	u32				lo_namelen;
+	const char *			lo_name;
+
+	/* response: void */
+};
+
+/* LOOKUPP currently not used */
+/* NVERIFY currently not used */
+
+struct nfs4fs_open {
+	/* request */
+	/* On-the-wire, the seqid would comes here... */
+	u32				op_share_access;
+	/*
+	 * On-the-wire, the following fields come here:
+	 *     share deny bits (always 0 for Unix)
+	 *     clientid
+	 *     lockowner name length
+	 *     lockowner name
+	 */
+	u32				op_opentype;
+	u32				op_createmode;  /* OPEN4_CREATE only */
+#ifdef __linux__
+	/* On-the-wire, this will be expanded to a 'fattr4' */
+	struct iattr			op_attrs;       /* OPEN4_CREATE only */
+#endif
+	u32				op_namelen;
+	const char *			op_name;
+
+	/* response */
+	/* On-the-wire, the stateid comes here... */
+	struct nfs4fs_change_info	op_cinfo;
+	u32				op_rflags;
+	/* On-the-wire, we get a bitmap4 here, indicating which attrs were set... */
+	u32				op_delegation_type;
+	stateid4			op_delegation_stateid; /* OPEN_DELEGATE_{READ,WRITE} */
+	u32				op_delegation_recall;  /* OPEN_DELEGATE_{READ,WRITE} */
+	/*
+	 * On-the-wire, we get more delegation info here (currently ignored):
+	 *     space limit      (OPEN_DELEGATE_WRITE)
+	 *     ace              (OPEN_DELEGATE_{READ,WRITE})
+	 */
+};
+
+struct nfs4fs_putfh {
+	/* request */
+	u32				pf_fhlen;
+	const char *			pf_fh;
+
+	/* response: void */
+};
+
+struct nfs4fs_putrootfh {
+	/* request: void */
+	/* response: void */
+
+	/*
+	 * Currently, setup_putrootfh() secretly translates the PUTROOTFH
+	 * into PUTROOTFH LOOKUP LOOKUP ..., to obtain the filehandle for
+	 * the mountpoint (which is different from the server's rootfh if
+	 * a mount path is specified).  This field keeps track of the
+	 * number of LOOKUPs, so that handle_putrootfh() can act accordingly.
+	 *
+	 * TODO: I don't like this "secret translation" anymore; it is
+	 * confusing and serves no purpose.  I would prefer to have
+	 * setup_putrootfh() emit only the PUTROOTFH, and emit the
+	 * LOOKUP's by hand in nfs4_read_super().
+	 */
+	int				pr_nlookups;
+};
+
+struct nfs4fs_read {
+	/* request */
+	/* On-the-wire, stateid comes here... */
+	u64				rd_offset;
+	u32				rd_length;
+
+	/* response */
+	u32				rd_eof;   /* boolean */
+	u32				rd_bytes_read;
+
+#ifdef __linux__
+	/*
+	 * Zero-copy data, and list of nfs4_page's associated with READ request.
+	 */
+	unsigned int			rd_iov_nr;
+	struct iovec			rd_iov[NFS4_MAXIO];
+	struct list_head		rd_pages;
+#endif
+};
+
+struct nfs4fs_readlink {
+	/* request: void */
+	
+	/*
+	 * The _caller_ sets these fields to contain a buffer where the link text will
+	 * be written, and the buffer length.  The XDR decode routine will fill the
+	 * buffer, null-terminate the text, and throw the text length away.  (This is
+	 * because the Linux VFS expects a null-terminated string but does not care
+	 * about the length.)
+	 *
+	 * (On-the-wire, the response consists of the link text length, then the link text.)
+	 */
+	char				*rl_buf;
+	unsigned int			rl_len;
+};
+
+struct nfs4fs_readdir {
+	/* request */
+	u64				rd_cookie;
+	verifier4			rd_req_verifier;
+	/* On-the-wire, there is a meaningless field here.. */
+	u32				rd_count;
+	/* On-the-wire, the bitmap length comes here.. we always use 2. */
+	u32				rd_bmval[2];
+	
+	/* response */
+	verifier4			rd_resp_verifier;
+	/*
+	 * The on-the-wire list of entry4's is read directly into the page cache,
+	 * and corresponding fields do not appear here in the struct.  What does
+	 * appear are fields describing the interaction with the page cache.
+	 */
+#ifdef __linux__
+	/* Summary statistics, filled in by XDR decode routine: */
+	u32				rd_bytes_read;   /* after verifier, before eof */
+	u64				rd_last_cookie;
+	/* Filled in by caller: */
+	unsigned int			rd_iov_nr;
+	struct iovec			rd_iov[NFS4_MAXIO];
+	unsigned int			rd_npages;
+	struct page *			rd_pages[NFS4_MAXIO];
+#endif
+	u32				rd_eof;
+};
+
+struct nfs4fs_remove {
+	/* request */
+	u32				rm_namelen;
+	const char *			rm_name;
+
+	/* response */
+	struct nfs4fs_change_info	rm_cinfo;
+};
+
+struct nfs4fs_rename {
+	/* request */
+	u32				rn_oldnamelen;
+	const char *			rn_oldname;
+	u32				rn_newnamelen;
+	const char *			rn_newname;
+
+	/* response */
+	struct nfs4fs_change_info	rn_src_cinfo;
+	struct nfs4fs_change_info	rn_dst_cinfo;
+};
+
+struct nfsf4s_renew {
+	/* request */
+	u64				rn_clientid;
+
+	/* response: void */
+};
+
+struct nfs4fs_secinfo {
+	/* request */
+	u32				si_namelen;
+	const char *			si_name;
+
+	/* response */
+	u32				num_flavor;
+	struct nfs4fs_sec_flavor *	secflavor;
+};
+
+struct nfs4fs_setattr {
+	/* request */
+	/* On-the-wire, a stateid comes here.. */
+#ifdef __linux__
+	/* On-the-wire, our XDR decode routine will translate this into a fattr4.. */
+	struct iattr *			st_iap;
+#endif
+
+	/* the response consists of a bitmap4, which we currently ignore. */
+};
+
+struct nfs4fs_setclientid {
+	/* request */
+	verifier4			sc_verifier;
+	/*
+	 * XXX: The name has been expanded as part of the "temporary hack"
+	 * described in proc.c:setup_setclientid() -- restore eventually!
+	 */
+	char				sc_name[30];   /* null-terminated */
+	u32				sc_prog;
+	char				sc_netid[10];  /* null-terminated */
+	char				sc_uaddr[30];  /* null-terminated */
+
+	/* response */
+	clientid4			sc_clientid;
+};
+
+struct nfs4fs_setclientid_confirm {
+	/* request */
+	clientid4			sc_clientid;
+
+	/* response: void */
+};
+
+/* VERIFY currently not used */
+
+struct nfs4fs_write {
+	/* request */
+	/* On-the-write stateid goes here.. */
+	u64				wr_offset;
+	u32				wr_stable_how;
+	u32				wr_len;
+
+	/* response */
+	u32				wr_bytes_written;
+	u32				wr_how_written;
+	verifier4			wr_verifier;
+	
+#ifdef __linux__
+	/*
+	 * The caller must set these fields to indicate the location of
+	 * the actual data in the WRITE request.
+	 */
+	unsigned int			wr_iov_nr;
+	struct iovec			wr_iov[NFS4_MAXIO];
+	struct list_head		wr_pages;    /* list of associated nfs4_pages */
+#endif
+};
+
+struct nfs4fs_op {
+	u32					opnum;
+	u32					nfserr;
+	union {
+		struct nfs4fs_access		access;
+		struct nfs4fs_commit		commit;
+		struct nfs4fs_create		create;
+		struct nfs4fs_delegreturn	delegreturn;
+		struct nfs4fs_getattr		getattr;
+		struct nfs4fs_getfh		getfh;
+		struct nfs4fs_link		link;
+		struct nfs4fs_lock		lock;
+		struct nfs4fs_lockt		lockt;
+		struct nfs4fs_locku		locku;
+		struct nfs4fs_lookup		lookup;
+		struct nfs4fs_open		open;
+		struct nfs4fs_putfh		putfh;
+		struct nfs4fs_putrootfh		putrootfh;
+		struct nfs4fs_read		read;
+		struct nfs4fs_readdir		readdir;
+		struct nfs4fs_readlink		readlink;
+		struct nfs4fs_remove		remove;
+		struct nfs4fs_rename		rename;
+		struct nfs4fs_secinfo		secinfo;
+		struct nfs4fs_setattr		setattr;
+		struct nfs4fs_setclientid	setclientid;
+		struct nfs4fs_setclientid_confirm   setclientid_confirm;
+		struct nfs4fs_write		write;
+	} u;
+};
+
+#endif
--- clean/include/linux/nfs4/nfs4_debug.h	Thu Feb  7 18:04:30 2002
+++ dirty/include/linux/nfs4/nfs4_debug.h	Thu Dec  6 02:17:22 2001
@@ -0,0 +1,244 @@
+/*
+ *  linux/fs/nfs4/nfs4_debug.h
+ *
+ *  Copyright (c) 2001 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  Kendrick Smith <kmsmith@umich.edu>
+ *  Andy Adamson <andros@umich.edu>
+ *  
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *  
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the University nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _LINUX_NFS4_NFS4_DEBUG_H
+#define _LINUX_NFS4_NFS4_DEBUG_H
+
+#define NFS4_DEBUG       1
+#define RPCDBG_FACILITY  0
+
+/* XXX : do memory alloction with GFP_NFS instead of GFP_KERNEL?? */
+
+#if NFS4_DEBUG
+# define nfsv4_printk(fac,args...)   do { if (nfsv4_debug_##fac) printk(args); } while (0)
+# define nfsv4_hexdump(fac,ptr,len)  do { if (nfsv4_debug_##fac) hex_dump(ptr,len); } while (0)
+# define nfsv4_print_rpcsec_gss_info(fac,rpcsec_gss_info)      do { if (nfsv4_debug_##fac) print_rpcsec_gss_info(rpcsec_gss_info); } while (0)
+# define nfsv4_print_fh(fac,fh)      do { if (nfsv4_debug_##fac) print_fh(fh); } while (0)
+# define nfsv4_print_bm(fac,bm)      do { if (nfsv4_debug_##fac) print_bm(bm); } while (0)
+# define nfsv4_print_fattr(fac,fp)   do { if (nfsv4_debug_##fac) print_fattr(fp); } while (0)
+# define nfsv4_print_lockowner(fac,lp)  do { if (nfsv4_debug_##fac) \
+	print_lockowner(lp); } while (0)
+# define nfsv4_print_pathname(fac,p)    do { if (nfsv4_debug_##fac) \
+	print_pathname(p); } while (0)
+# define NFS4_ALLOC(nbytes)	__NFS4_ALLOC(nbytes, __FILE__, __LINE__)
+# define NFS4_FREE(ptr)		__NFS4_FREE(ptr, __FILE__, __LINE__)
+# define IGET(s, i) __IGET(s, i, __FILE__, __LINE__)
+# define IPUT(i) __IPUT(i, __FILE__, __LINE__)
+# define IGRAB(i) __IGRAB(i, __FILE__, __LINE__)
+# define DGET(d) __DGET(d, __FILE__, __LINE__)
+# define DGET_LOCKED(d) __DGET_LOCKED(d, __FILE__, __LINE__)
+# define D_ALLOC(d, q) __D_ALLOC(d, q, __FILE__, __LINE__)
+# define D_ALLOC_ROOT(i) __D_ALLOC_ROOT(i, __FILE__, __LINE__)
+# define DPUT(d) __DPUT(d, __FILE__, __LINE__)
+# define D_LOOKUP(d, q) __D_LOOKUP(d, q, __FILE__, __LINE__)
+# define LOOKUP_ONE(n, d) __LOOKUP_ONE(n, d, __FILE__, __LINE__)
+# define FOLLOW_DOWN(m, d) __FOLLOW_DOWN(m, d, __FILE__, __LINE__)
+# define NFS4_ASSERT(condition)		do {	\
+	if (!(condition))			\
+		BUG();				\
+} while (0)
+#else
+# define nfsv4_printk(fac,args...)	do { } while (0)
+# define nfsv4_hexdump(fac,ptr,len)	do { } while (0)
+# define nfsv4_print_fh(fac,fh)		do { } while (0)
+# define nfsv4_print_bm(fac,bm)		do { } while (0)
+# define nfsv4_print_fattr(fac,fp)	do { } while (0)
+# define nfsv4_print_lockowner(fac,lp)	do { } while (0)
+# define nfsv4_print_pathname(fac,p)	do { } while (0)
+# define NFS4_ALLOC(nbytes)	kmalloc(nbytes, GFP_KERNEL)
+# define NFS4_FREE(ptr)		kfree(ptr)
+# define IGET(s, i)		iget(s, i)
+# define IPUT(i)		iput(i)
+# define IGRAB(i)		igrab(i)
+# define DGET(d)		dget(d)
+# define DGET_LOCKED(d)		dget_locked(d)
+# define D_ALLOC(d, q)		d_alloc(d, q)
+# define D_ALLOC_ROOT(i)	d_alloc_root(i)
+# define DPUT(d)		dput(d)
+# define D_LOOKUP(d, q)		d_lookup(d, q)
+# define LOOKUP_ONE(n, d)	lookup_one(n, d)
+# define FOLLOW_DOWN(m, d)	follow_down(m, d)
+# define NFS4_ASSERT(condition) do {} while (0)
+#endif
+
+#if NFS4_DEBUG
+
+extern void *__NFS4_ALLOC(int nbytes, char *file, int line);
+extern void __NFS4_FREE(const void *ptr, char *file, int line);
+extern void NFS4_COMPLAIN(void);
+extern struct inode *__IGET(struct super_block *sb, unsigned long ino, char *file, int line);
+extern void __IPUT(struct inode *inode, char *file, int line);
+extern struct inode * __IGRAB(struct inode *inode, char *file, int line);
+extern void DUP(struct dentry *dentry, char *file, int line);
+extern struct dentry *__DGET(struct dentry *dentry, char *file, int line);
+extern struct dentry *__DGET_LOCKED(struct dentry *dentry, char *file, int line);
+extern struct dentry *__D_ALLOC(struct dentry *dentry, const struct qstr *str,
+				char *file, int line);
+extern struct dentry *__D_ALLOC_ROOT(struct inode *inode, char *file, int line);
+extern void __DPUT(struct dentry *dentry, char *file, int line);
+extern struct dentry *__D_LOOKUP(struct dentry *dentry, struct qstr *str,
+				 char *file, int line);
+extern struct dentry *__LOOKUP_ONE(const char *str, struct dentry *dentry,
+				   char *file, int line);
+extern int __FOLLOW_DOWN(struct vfsmount **, struct dentry **, char *file, int line);
+
+extern int nfsv4_debug_level1;
+extern int nfsv4_debug_level2;
+extern int nfsv4_debug_level3;
+extern int nfsv4_debug_mount;
+extern int nfsv4_debug_ctl;
+extern int nfsv4_debug_dispatcher;
+extern int nfsv4_debug_fh;
+extern int nfsv4_debug_getattr;
+extern int nfsv4_debug_setattr;
+extern int nfsv4_debug_lookup;
+extern int nfsv4_debug_readdir;
+extern int nfsv4_debug_create;
+extern int nfsv4_debug_xdr;
+extern int nfsv4_debug_gss;
+extern int nfsv4_debug_remove;
+extern int nfsv4_debug_link;
+extern int nfsv4_debug_readlink;   /* change name to nfsv4_debug_symlink at some point */
+extern int nfsv4_debug_rename;
+extern int nfsv4_debug_open;
+extern int nfsv4_debug_read;
+extern int nfsv4_debug_write;
+extern int nfsv4_debug_lock;
+extern int nfsv4_debug_seqid;
+extern int nfsv4_debug_lockowner;
+extern int nfsv4_debug_stateid;
+extern int nfsv4_debug_clientid;
+extern int nfsv4_debug_reval;
+extern int nfsv4_debug_auth;
+extern int nfsv4_debug_secinfo;
+extern int nfsv4_debug_pagecache;
+extern int nfsv4_debug_lease;
+extern int nfsv4_debug_async;
+extern int nfsv4_debug_check;
+extern int nfsv4_debug_acl;
+extern int nfsv4_debug_callback;
+
+#define nfsv4_debug_ctl2		(nfsv4_debug_ctl || nfsv4_debug_level2)
+#define nfsv4_debug_mount2		(nfsv4_debug_mount || nfsv4_debug_level2)
+#define nfsv4_debug_fh2			(nfsv4_debug_fh || nfsv4_debug_level2)
+#define nfsv4_debug_getattr2		(nfsv4_debug_getattr || nfsv4_debug_level2)
+#define nfsv4_debug_setattr2		(nfsv4_debug_setattr || nfsv4_debug_level2)
+#define nfsv4_debug_readdir2		(nfsv4_debug_readdir || nfsv4_debug_level2)
+#define nfsv4_debug_create2		(nfsv4_debug_create || nfsv4_debug_level2)
+#define nfsv4_debug_xdr2		(nfsv4_debug_xdr || nfsv4_debug_level2)
+#define nfsv4_debug_lookup2		(nfsv4_debug_lookup || nfsv4_debug_level2)
+#define nfsv4_debug_remove2		(nfsv4_debug_remove || nfsv4_debug_level2)
+#define nfsv4_debug_link2		(nfsv4_debug_link || nfsv4_debug_level2)
+#define nfsv4_debug_readlink2		(nfsv4_debug_readlink || nfsv4_debug_level2)
+#define nfsv4_debug_rename2		(nfsv4_debug_rename || nfsv4_debug_level2)
+#define nfsv4_debug_open2		(nfsv4_debug_open || nfsv4_debug_level2)
+#define nfsv4_debug_read2		(nfsv4_debug_read || nfsv4_debug_level2)
+#define nfsv4_debug_write2		(nfsv4_debug_write || nfsv4_debug_level2)
+#define nfsv4_debug_lock2		(nfsv4_debug_lock || nfsv4_debug_level2)
+#define nfsv4_debug_seqid2		(nfsv4_debug_seqid || nfsv4_debug_level2)
+#define nfsv4_debug_lockowner2		(nfsv4_debug_lockowner || nfsv4_debug_level2)
+#define nfsv4_debug_stateid2		(nfsv4_debug_stateid || nfsv4_debug_level2)
+#define nfsv4_debug_clientid2		(nfsv4_debug_clientid || nfsv4_debug_level2)
+#define nfsv4_debug_reval2		(nfsv4_debug_reval || nfsv4_debug_level2)
+#define nfsv4_debug_gss2		(nfsv4_debug_gss || nfsv4_debug_level2)
+#define nfsv4_debug_async2		(nfsv4_debug_async || nfsv4_debug_level2)
+#define nfsv4_debug_auth2		(nfsv4_debug_auth || nfsv4_debug_level2)
+#define nfsv4_debug_lease2		(nfsv4_debug_lease || nfsv4_debug_level2)
+#define nfsv4_debug_dispatcher2		(nfsv4_debug_dispatcher || nfsv4_debug_level2)
+#define nfsv4_debug_acl2		(nfsv4_debug_acl || nfsv4_debug_level2)
+#define nfsv4_debug_callback2		(nfsv4_debug_callback || nfsv4_debug_level2)
+
+/*
+ * Sysctl interface for RPC debugging
+ */
+void            rpc_register_sysctl(void);
+void            rpc_unregister_sysctl(void);
+
+
+/*
+ * Declarations for the sysctl debug interface, which allows to read or
+ * change the debug flags.
+ */
+
+#define CTL_NFS4      4739    /* arbitrary and hopefully unused */
+
+enum {
+  CTL_DEBUG_LEVEL1 = 1,
+  CTL_DEBUG_LEVEL2,
+  CTL_DEBUG_LEVEL3,
+  CTL_DEBUG_MOUNT,
+  CTL_DEBUG_CTL,
+  CTL_DEBUG_DISPATCHER,
+  CTL_DEBUG_FH,
+  CTL_DEBUG_GETATTR,
+  CTL_DEBUG_SETATTR,
+  CTL_DEBUG_LOOKUP,
+  CTL_DEBUG_READDIR,
+  CTL_DEBUG_CREATE,
+  CTL_DEBUG_XDR,
+  CTL_DEBUG_GSS,
+  CTL_DEBUG_REMOVE,
+  CTL_DEBUG_LINK,
+  CTL_DEBUG_READLINK,
+  CTL_DEBUG_RENAME,
+  CTL_DEBUG_OPEN,
+  CTL_DEBUG_READ,
+  CTL_DEBUG_WRITE,
+  CTL_DEBUG_LOCK,
+  CTL_DEBUG_SEQID,
+  CTL_DEBUG_LOCKOWNER,
+  CTL_DEBUG_STATEID,
+  CTL_DEBUG_CLIENTID,
+  CTL_DEBUG_REVAL,
+  CTL_DEBUG_AUTH,
+  CTL_DEBUG_SECINFO,
+  CTL_DEBUG_PAGECACHE,
+  CTL_DEBUG_LEASE,
+  CTL_DEBUG_ASYNC,
+  CTL_DEBUG_CHECK,
+  CTL_DEBUG_ACL,
+  CTL_DEBUG_CALLBACK
+};
+
+#endif  /* NFS4_DEBUG */
+
+#endif
+
+
+
+
+
+
--- clean/include/linux/nfs4/nfsd4.h	Thu Feb  7 18:04:30 2002
+++ dirty/include/linux/nfs4/nfsd4.h	Tue Feb  5 10:42:19 2002
@@ -0,0 +1,331 @@
+/*
+ *  linux/fs/nfs4/nfsd4.h
+ *
+ *  Copyright (c) 2001 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  Kendrick Smith <kmsmith@umich.edu>
+ *  Andy Adamson <andros@umich.edu>
+ *  
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *  
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the University nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _LINUX_NFS4_NFSD4_H
+#define _LINUX_NFS4_NFSD4_H
+
+#define NFSD4_MAX_BLOCK_SIZE		8192
+#define NFS4_LEASE_TIME			60   /* seconds */
+#define NFS4_LAUNDROMAT_MINTIMEOUT	10   /* seconds */
+
+#define ALLOW_NULL_CLIENTID		1
+#define ALWAYS_OPEN_CONFIRM		0
+#define NFSD4_STRICT_LOCKING		0   /* XXX: do we still use this? */
+#define NFS4_MAXPATHLEN			PATH_MAX  /* defined in limits.h as 4095 */
+#define NFS4_MAXNAMLEN			NAME_MAX
+#define NFS4_MAXLINKLEN			NAME_MAX  /* XXX: ??? */
+
+#define IS_ISMNDLK(i)   (S_ISREG((i)->i_mode) && MANDATORY_LOCK(i))
+
+/* used to communicate mount security options to rpc layer */
+/* XXX: this all belongs in a sunrpc header instead! */
+struct gss_oid {
+	unsigned int len;
+	char *       data;
+};
+
+extern const struct gss_oid * const nfsd4_mech_krb5_oid;
+extern const struct gss_oid * const nfsd4_mech_spkm3_oid;
+
+enum rpc_gss_svc_t {
+  RPC_GSS_SVC_NONE = 1,
+  RPC_GSS_SVC_INTEGRITY = 2,
+  RPC_GSS_SVC_PRIVACY = 3
+};
+
+extern int gss_cmp_triples(u32 oid_len, char *oid_data, u32 qop, u32 service);
+
+struct nfs4_export {
+	/*
+	 * Each export structure contains two sets of device and inode numbers:
+	 * one for the directory in the pseudofs, and one for the actual export
+	 * in the server's filesystem.
+	 */
+	struct list_head	ex_hash, ex_pseudo_hash;
+	kdev_t			ex_dev, ex_pseudo_dev;
+	ino_t			ex_ino, ex_pseudo_ino;
+
+	/*
+	 * These fields keep track of the pseudofs namespace.
+	 */
+	unsigned int		ex_namelen;
+	char *			ex_name;
+	struct nfs4_export *	ex_parent;
+	struct list_head	ex_subdirs;
+	unsigned int		ex_nchild;
+	struct list_head	ex_child;
+
+	/*
+	 * For an "internal" pseudofs entry, these fields are 0.
+	 * For a pseudofs "leaf", they designate the actual export in the
+	 * server's filesystem.
+	 */
+	struct vfsmount	*	ex_mnt;
+	struct dentry *		ex_dentry;
+	unsigned int		ex_exportnamelen;
+	char *			ex_exportname;
+
+	/*
+	 * Security modes.
+	 */
+	unsigned int		ex_readonly	: 1,
+				ex_sec_none	: 1,	/* AUTH_NULL */
+				ex_sec_sys	: 1,	/* AUTH_SYS */
+				ex_sec_dh	: 1,	/* AUTH_DES, old diffie-hellman */
+				ex_sec_krb5	: 1,	/* AUTH_GSS, kerberos 5 authentication */
+				ex_sec_krb5i	: 1,	/* AUTH_GSS, kerberos 5 integrity */
+				ex_sec_krb5p	: 1,	/* AUTH_GSS, kerberos 5 privacy */
+				ex_sec_spkm3	: 1,	/* AUTH_GSS, spkm authentication */
+				ex_sec_spkm3i	: 1,	/* AUTH_GSS, spkm integrity */
+				ex_sec_spkm3p	: 1,	/* AUTH_GSS, spkm privacy */
+				ex_sec_lkey	: 1,	/* AUTH_GSS, lipkey authentication */
+				ex_sec_lkeyi	: 1,	/* AUTH_GSS, lipkey integrity */
+				ex_sec_lkeyp	: 1;	/* AUTH_GSS, lipkey privacy */
+};
+
+/*
+ * Per-(export, client) pair security attributes
+ */
+struct nfs4_secattrs {
+	uid_t			sa_fsuid;
+	gid_t			sa_fsgid;
+	kernel_cap_t		sa_cap_effective;
+	int			sa_access;
+};
+/*
+ * Special flags for sa_access. These must be different from MAY_READ,
+ * MAY_WRITE, and MAY_EXEC.
+ */
+#define MAY_SATTR		8
+#define MAY_TRUNC		16
+#define MAY_LOCK		32
+#define MAY_OWNER_OVERRIDE	64
+#define MAY_CREATE		(MAY_EXEC|MAY_WRITE)
+#define MAY_REMOVE		(MAY_EXEC|MAY_WRITE|MAY_TRUNC)
+
+/*
+ * This is the internal representation of an NFS handle used in knfsd.
+ */
+typedef struct svc_fh {
+	unsigned int		fh_initialized;
+	struct nfs4_export	*fh_export;
+	struct nfs4_secattrs	fh_secattrs;
+	struct dentry		*fh_dentry;   /* NULL if in pseudofs */
+} svc_fh;
+#define fh_fsuid		fh_secattrs.sa_fsuid
+#define fh_fsgid		fh_secattrs.sa_fsgid
+#define fh_cap_effective	fh_secattrs.sa_cap_effective
+#define fh_access		fh_secattrs.sa_access
+
+/*
+ *  This is the context of a single nfsd kernel thread.
+ */
+struct global_state {
+        struct svc_fh			current_fh;
+        struct svc_fh			save_fh;
+	
+#ifdef __linux__
+        struct svc_rqst	*		rqstp;
+#endif
+	
+	/* decode buffer */
+	u32 *				dbuf;      /* current position in decode buffer */
+	u32 *				dend;      /* end of decode buffer */
+	u32				taglen;
+	char *				tag;
+	u32				minorversion;
+	u32				opcnt;     /* total operations in request */
+	
+	/* encode buffer */
+	u32 *				ebuf;      /* current position in encode buffer */
+	u32 *				eend;      /* end of encode buffer */
+	u32 *				statusp;   /* top-level status, backfilled */
+	u32 *				nopsp;     /* num. ops processed, backfilled */
+};
+
+/*
+ * nfs server stats
+ */
+struct nfsd_stats {
+	unsigned int	rchits;		/* repcache hits */
+	unsigned int	rcmisses;	/* repcache hits */
+	unsigned int	rcnocache;	/* uncached reqs */
+	unsigned int	fh_stale;	/* FH stale error */
+	unsigned int	fh_lookup;	/* dentry cached */
+	unsigned int	fh_anon;	/* anon file dentry returned */
+	unsigned int	fh_nocache_dir;	/* filehandle not found in dcache */
+	unsigned int	fh_nocache_nondir;	/* filehandle not found in dcache */
+	unsigned int	io_read;	/* bytes returned to read requests */
+	unsigned int	io_write;	/* bytes passed in write requests */
+	unsigned int	th_cnt;		/* number of available threads */
+	unsigned int	th_usage[10];	/* number of ticks during which n perdeciles
+					 * of available threads were in use */
+	unsigned int	th_fullcnt;	/* number of times last free thread was used */
+	unsigned int	ra_size;	/* size of ra cache */
+	unsigned int	ra_depth[11];	/* number of times ra entry was found that deep
+					 * in the cache (10percentiles). [10] = not found */
+};
+/* thread usage wraps very million seconds (approx one fortnight) */
+/* XXX : what is this?! */
+#define	NFSD_USAGE_WRAP	(HZ*1000000)
+
+/*
+ * Callback arg/result structs.
+ */
+
+struct nfsd4_cb_args {
+	u32			cb_opnum;	/* OP_CB_GETATTR or OP_CB_RECALL */
+	u32			cb_fhlen;
+	u32			cb_fhval[6];
+	delegation_stateid_t	cb_stateid;	/* OP_CB_GETATTR only */
+	u32			*cb_bmval;      /* OP_CB_RECALL only */
+};
+
+struct nfsd4_cb_res {
+	int			cb_toplevel_status;
+	int			cb_opnum;
+
+	/* the remaining fields are for CB_GETATTR only */
+	int			cb_valid;
+	u64			cb_change;
+	u64			cb_size;
+	u64			cb_mtime_sec;
+	u32			cb_mtime_nsec;
+};
+/* flags for cb_valid */
+#define CB_CHANGE		0x00000001
+#define CB_SIZE			0x00000002
+#define CB_MTIME		0x00000004
+
+
+/* ctl.c */
+extern struct rw_semaphore nfs4_ctl_sema;
+
+/* svc.c */
+extern struct svc_program nfsd4_program;
+extern void nfs4_svc_shutdown(void);
+
+/* export.c */
+extern void nfs4_export_init(void);
+extern int nfs4_setexp(int fh_version, kdev_t dev, ino_t ino, struct nfs4_export **expp);
+extern int nfs4_setroot(struct nfs4_export **expp);
+extern int nfs4_setpub(struct nfs4_export **expp);
+extern int nfs4_setsec(struct svc_rqst *rqstp, struct nfs4_export *exp,
+		       struct nfs4_secattrs *attrs);
+extern void nfs4_export_shutdown(void);
+extern int nfs4_exports_read_proc(char *page, char **start, off_t off,
+				         int count, int *eof, void *data);
+extern struct nfs4_export *nfs4_export_follow(struct nfs4_export *ex,
+					      struct vfsmount *mnt, struct dentry *dentry);
+
+/* compound.c */
+extern int nfsd4_proc_compound(struct global_state *stp);
+
+/* vfs.c */
+extern u32 GETATTR_BITMAP[2];
+extern u32 nfs4_ftypes[16];
+extern int do_setattr(struct svc_fh *fhp, struct iattr *iap);
+
+/* fh.c */
+extern int fh_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfsd4_putfh *putfh);
+extern int fh_write(struct nfs4_export *exp, struct dentry *dentry, u32 *p);
+
+/* stats.c */
+extern struct nfsd_stats	nfsdstats;
+extern struct svc_stat		nfsd4_svcstats;
+extern void nfsd_stat_init(void);
+extern void nfsd_stat_shutdown(void);
+
+/* callback.c */
+extern void nfsd4_probe_callback_path(struct nfs4_client *clp);
+extern int nfsd4_cb_recall(struct nfsd4_delegation *dp);
+
+static inline void
+fh_copy(struct svc_fh *dst, struct svc_fh *src)
+{
+	/* just copy field-by-field; the only catch is that we might have to take care of
+	   dentry refcounts.  Caller must ensure that src is initialized. */
+	if (dst->fh_initialized && dst->fh_dentry)
+		DPUT(dst->fh_dentry);
+	memcpy(dst, src, sizeof(struct svc_fh));
+	if (dst->fh_dentry)
+		DGET(dst->fh_dentry);
+}
+
+static inline void
+fh_init(struct svc_fh *fhp)
+{
+	fhp->fh_initialized = 0;
+}
+
+static inline void
+fh_put(struct svc_fh *fhp)
+{
+	if (fhp->fh_initialized)
+		DPUT(fhp->fh_dentry);
+}
+
+#define isdotent(n, l)			(l < 3 && n[0] == '.' && (l == 1 || n[1] == '.'))
+
+static inline int
+bad_filename(char *str, int len)
+{
+        int i;
+
+        if (len == 0) {
+                nfsv4_printk(level2, "bad_filename(%.*s): length==0!\n", len, str);
+                return 1;
+        }
+        if (isdotent(str, len)) {
+                nfsv4_printk(level2, "bad_filename(%.*s): '.' or '..'!\n", len, str);
+                return 1;
+        }
+        for (i = 0; i < len; i++) {
+		if (!str[i]) {
+			nfsv4_printk(level2, "bad_filename(%.*s): contains NULL!\n", len, str);
+			return 1;
+                }
+                if (str[i] == '/') {
+                        nfsv4_printk(level2, "bad_filename(%.*s): contains '/'!\n", len, str);
+                        return 1;
+                }
+        }
+        return 0;
+}
+
+extern time_t boot_time;
+
+#endif
--- clean/include/linux/nfs4/syscall.h	Thu Feb  7 18:04:30 2002
+++ dirty/include/linux/nfs4/syscall.h	Mon Oct  8 14:28:30 2001
@@ -0,0 +1,142 @@
+/*
+ *  linux/fs/nfs4/syscall.h
+ *
+ *  Copyright (c) 2001 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  Kendrick Smith <kmsmith@umich.edu>
+ *  Andy Adamson <andros@umich.edu>
+ *  
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *  
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the University nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ *  Syscall interface to nfsd
+ */
+
+#ifndef NFSD4_SYSCALL_H
+#define NFSD4_SYSCALL_H
+
+#include <asm/types.h>
+#include <linux/posix_types.h>
+
+/*
+ * Version of the syscall interface -- increment whenever this file is modified!
+ */
+#define NFSCTL_VERSION		0x0209
+
+/* Maximum number of allowed kernel threads */
+#define	NFSD_MAXSERVS		128
+
+/*
+ * These are the commands understood by nfsctl().
+ */
+#define NFS4CTL_ADDPSEUDO	100
+#define NFS4CTL_REMOVEPSEUDO	101
+#define NFS4CTL_EXPORT		102
+#define NFS4CTL_UNEXPORT	103
+#define NFS4CTL_START		104
+#define NFS4CTL_REFRESH		105
+
+#define NFSD4_MAX_GRACE		300   /* seconds */
+
+struct nfs4ctl_addpseudo {
+	unsigned int	namlen;
+	char		*name;
+	__u16		major, minor;
+	ino_t		ino;
+	__u16		parent_major, parent_minor;
+	ino_t		parent_ino;
+};
+
+struct nfs4ctl_removepseudo {
+	__u16		major, minor;
+	ino_t		ino;
+};
+
+struct nfs4ctl_export {
+	__u16		major, minor;   /* device numbers of _pseudo_ mountpoint */
+	ino_t		ino;            /* inode number of pseudo mountpoint */
+	unsigned int	pathlen;
+	char		*path;          /* pathname of real exported directory */
+	unsigned int	read_only	: 1,
+			sec_none	: 1,	/* AUTH_NONE RPC security */
+			sec_sys		: 1,	/* AUTH_SYS RPC, default uid/gid lists */
+			sec_dh		: 1,	/* AUTH_DES, old diffe-hellman encryption */
+			sec_krb5	: 1,	/* RPCSEC_GSS Kerberos 5 authenication */
+			sec_krb5i	: 1,	/* RPCSEC_GSS Kerberos 5 integrity */
+			sec_krb5p	: 1,	/* RPCSEC_GSS Kerberso 5 protection */
+			sec_spkm3	: 1,	/* SPKM authenication */
+			sec_spkm3i	: 1,	/* SPKM integrity */
+			sec_spkm3p	: 1,	/* SPKM protection */
+			sec_lkey	: 1,	/* LIPKEY authenication */
+			sec_lkeyi	: 1,	/* LIPKEY integrity */
+			sec_lkeyp	: 1;	/* LIPKEY protection */
+};
+
+struct nfs4ctl_unexport {
+	unsigned int	namelen;
+	char *		name;
+};
+
+struct nfs4ctl_start {
+	int		nthreads;
+	unsigned int	grace_period;    /* boolean */
+};
+
+struct nfsctl_arg {
+	int			ca_version;	/* safeguard */
+	union {
+		struct nfs4ctl_addpseudo	addpseudo;
+		struct nfs4ctl_removepseudo	removepseudo;
+		struct nfs4ctl_export		export;
+		struct nfs4ctl_unexport		unexport;
+		struct nfs4ctl_start		start;
+	} u;
+};
+#define ca_addpseudo	u.addpseudo
+#define ca_removepseudo	u.removepseudo
+#define ca_export	u.export
+#define ca_unexport	u.unexport
+#define ca_start	u.start
+
+union nfsctl_res {
+	unsigned int    nthreads;   /* used by REFRESH */
+};
+
+extern int nfsservctl(int cmd, struct nfsctl_arg *argp, union nfsctl_res *resp);
+
+#ifdef __KERNEL__
+extern int nfs4_start(struct nfs4ctl_start *arg);
+extern int nfs4_refresh(union nfsctl_res *resp);
+extern int nfs4_addpseudo(struct nfs4ctl_addpseudo *arg);
+extern int nfs4_removepseudo(struct nfs4ctl_removepseudo *arg);
+extern int nfs4_export(struct nfs4ctl_export *arg);
+extern int nfs4_unexport(struct nfs4ctl_unexport *arg);
+#endif
+
+#endif /* NFSD4_SYSCALL_H */
--- clean/include/linux/nfs4/nfsd4_state.h	Thu Feb  7 18:04:30 2002
+++ dirty/include/linux/nfs4/nfsd4_state.h	Thu Dec  6 02:17:23 2001
@@ -0,0 +1,443 @@
+#ifndef _NFSD4_STATE_H
+#define _NFSD4_STATE_H
+
+/*
+ * XXX: Thanks to the bakeoff, many comments in this file are now out-of-date; fix!
+ */
+
+#define NFSD4_FH_MAX			28
+
+/*
+ * Note: We currently set NFSD4_LOCKOWNER_MAXNAME to a small value, and keep a static buffer
+ * of size NFSD4_LOCKOWNER_MAXNAME with every lockowner.  This is because lockowner sizes
+ * seem to be small in practice.  However, we may ultimately end up having to increase the
+ * value, and switch from static to dynamic buffering.
+ */
+#define NFSD4_CLIENT_MAXNAME      64
+#define NFSD4_LOCKOWNER_MAXNAME   16
+
+/* A reasonable value for REPLAY_ISIZE was estimated as follows:  The OPEN response, typically
+ * the largest, requires
+ * 4(status) + 8(stateid) + 20(changeinfo) + 4(rflags) + 8(verifier) + 4(deleg. type) +
+ * 8(deleg. stateid) + 4(deleg. recall flag) + 20(deleg. space limit) + ~32(deleg. ace)
+ * = 112 bytes
+ */
+#define NFSD4_REPLAY_ISIZE       112
+
+/*
+ *  Files state.h and state.c implement the NFSv4 state management protocol, written
+ *  with ease of portability in mind.  First, there is a small platform-dependent part
+ *  consisting of the following primitives:
+ *    get_time() - a routine returning the current time in seconds (used for lease timeouts)
+ *    nfs4_ino_desc_t - typedef for a descriptor which represents a unique file on the system.
+ *                      These will be memcmp()'ed to determine whether two files are the same.
+ *    nfs4_file_desc_t - typedef for a descriptor for an open file on the system.  These are
+ *                       opened, file I/O is subsequently done, then they are closed.
+ *  Everything after this should be platform-independent, and should compile once the
+ *  primitives are defined for a given platform.
+ */
+
+struct svc_fh;          /* to be defined in nfsd4.h */
+struct global_state;    /* to be defined in nfsd4.h */
+struct nfsd4_open;      /* to be defined in nfsd4_xdr.h */
+struct nfsd4_lock;      /* to be defined in nfsd4_xdr.h */
+struct nfsd4_lockt;     /* to be defined in nfsd4_xdr.h */
+
+#ifdef __linux__
+
+#define get_time()  CURRENT_TIME
+typedef struct {
+	u32			dev;    /* no kdev_t, to avoid unaligned access */
+	unsigned long		ino;
+	u32			generation;
+} nfs4_ino_desc_t;
+
+#define nfs4_file_desc_t        struct file
+
+#endif
+
+/*
+ *  We are using BSD's list manipulation macros to avoid licensing issues when trying to
+ *  move Linux code into the BSD kernel.  Non-BSD systems will need this snippet from
+ *  BSD's sys/queue.h.
+ */
+#ifndef __OpenBSD__
+
+#define _LIST_HEAD(name, type)                                          \
+struct name {                                                           \
+        struct type *lh_first;  /* first element */                     \
+}
+
+#define LIST_ENTRY(type)                                                \
+struct {                                                                \
+        struct type *le_next;   /* next element */                      \
+        struct type **le_prev;  /* address of previous next element */  \
+}
+#define LIST_FIRST(head)                ((head)->lh_first)
+#define LIST_END(head)                  NULL
+#define LIST_EMPTY(head)                (LIST_FIRST(head) == LIST_END(head))
+#define LIST_NEXT(elm, field)           ((elm)->field.le_next)
+
+#define LIST_FOREACH(var, head, field)                                  \
+        for((var) = LIST_FIRST(head);                                   \
+            (var)!= LIST_END(head);                                     \
+            (var) = LIST_NEXT(var, field))
+
+#define LIST_INIT(head) do {                                            \
+        LIST_FIRST(head) = LIST_END(head);                              \
+} while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do {                         \
+        if (((elm)->field.le_next = (head)->lh_first) != NULL)          \
+                (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+        (head)->lh_first = (elm);                                       \
+        (elm)->field.le_prev = &(head)->lh_first;                       \
+} while (0)
+
+#define LIST_REMOVE(elm, field) do {                                    \
+        if ((elm)->field.le_next != NULL)                               \
+                (elm)->field.le_next->field.le_prev =                   \
+                    (elm)->field.le_prev;                               \
+        *(elm)->field.le_prev = (elm)->field.le_next;                   \
+} while (0)
+
+#define TAILQ_HEAD(name, type)                                          \
+struct name {                                                           \
+        struct type *tqh_first; /* first element */                     \
+        struct type **tqh_last; /* addr of last next element */         \
+}
+
+#define TAILQ_ENTRY(type)                                               \
+struct {                                                                \
+        struct type *tqe_next;  /* next element */                      \
+        struct type **tqe_prev; /* address of previous next element */  \
+}
+
+#define TAILQ_FIRST(head)               ((head)->tqh_first)
+#define TAILQ_END(head)                 NULL
+#define TAILQ_EMPTY(head)		(TAILQ_FIRST(head) == TAILQ_END(head))
+#define TAILQ_NEXT(elm, field)          ((elm)->field.tqe_next)
+
+#define TAILQ_FOREACH(var, head, field)                                 \
+        for((var) = TAILQ_FIRST(head);                                  \
+            (var) != TAILQ_END(head);                                   \
+            (var) = TAILQ_NEXT(var, field))
+
+#define TAILQ_INIT(head) do {                                           \
+        (head)->tqh_first = NULL;                                       \
+        (head)->tqh_last = &(head)->tqh_first;                          \
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do {                        \
+        (elm)->field.tqe_next = NULL;                                   \
+        (elm)->field.tqe_prev = (head)->tqh_last;                       \
+        *(head)->tqh_last = (elm);                                      \
+        (head)->tqh_last = &(elm)->field.tqe_next;                      \
+} while (0)
+
+#define TAILQ_REMOVE(head, elm, field) do {                             \
+        if (((elm)->field.tqe_next) != NULL)                            \
+                (elm)->field.tqe_next->field.tqe_prev =                 \
+                    (elm)->field.tqe_prev;                              \
+        else                                                            \
+                (head)->tqh_last = (elm)->field.tqe_prev;               \
+        *(elm)->field.tqe_prev = (elm)->field.tqe_next;                 \
+} while (0)
+
+#endif     /* end of BSD list macros */
+
+#ifdef __OpenBSD__
+/*
+ *  Argh, it turns out that Linux has a built-in macro called LIST_HEAD.  To avoid the
+ *  collision, I had to replace LIST_HEAD everywhere with _LIST_HEAD.  BSD will need the
+ *  following #define.  Is there a better solution?
+ */
+#define _LIST_HEAD(a,b)   LIST_HEAD(a,b)
+#endif
+
+/*
+ *  I/O operations -- these will need to be defined differently for each platform.
+ *    int nfs4_file_open(nfs4_file_desc_t *, struct svc_fh *, unsigned int share_access)
+ *               opens an nfs4_file_desc_t with specified filehandle + share access.
+ *    int nfs4_file_close(nfs4_file_desc_t *)
+ *               closes an nfs4_file_desc_t
+ *    int nfs4_file_upgrade(nfs4_file_desc_t *, unsigned int share_access)
+ *               called whenever share access is upgraded on an nfs4_file_desc_t.
+ *               the parameter 'share_access' represents the bits actually added.
+ *    int nfs4_file_downgrade(struct nfs4_file_desc_t *, unsigned int share_access)
+ *               called whenever share access is downgraded on an nfs4_file_desc_t.
+ *               the parameter 'share_access' represents the bits actually added.
+ *    int nfs4_truncate(struct svc_fh *fhp)
+ *               truncates a filehandle to size 0
+ *    int nfs4_check_fh(struct svc_fh *fhp, struct nfs4_openfile *ofp)
+ *               returns an error if filehandle fails to match opened file
+ */
+extern int nfs4_file_open(nfs4_file_desc_t *, struct svc_fh *, unsigned int share_access);
+extern void nfs4_file_close(nfs4_file_desc_t *);
+
+/*
+ *    clientid_t            - our internal representation of a clientid
+ *    stateid_t             - our internal representation of a stateid
+ *    delegation_stateid_t  - our internal representation of a delegation stateid
+ *
+ *  Note that stateids are currently defined to be 64 bits, but this supposedly will soon
+ *  be changed to 128 bits.  For this reason, our stateid_t's are defined so that one can
+ *  flip back and forth between 64 and 128 bits using a #define.
+ */
+typedef struct {
+	u32		cl_boot;
+	u32		cl_id;
+} clientid_t;
+
+typedef u32 stateid_boot_t;        /* used to detect stale stateids */
+typedef u32 stateid_lockowner_t;   /* lockowner id - used in various places */
+typedef u32 stateid_file_t;        /* identifies a unique file per lockowner */
+typedef u32 stateid_generation_t;  /* used to update stateids */
+
+typedef struct {
+	stateid_boot_t		so_boot;
+	stateid_lockowner_t	so_lockowner;
+	stateid_file_t		so_file;
+} stateid_other_t;                 /* the opaque part of a stateid: everything except the generation */
+
+typedef struct {
+	stateid_generation_t	st_generation;
+	stateid_other_t		st_other;
+} stateid_t;
+#define st_boot			st_other.so_boot
+#define st_lockowner		st_other.so_lockowner
+#define st_file			st_other.so_file
+
+typedef u32 delegation_zero_t;
+typedef u32 delegation_boot_t;
+typedef u64 delegation_id_t;
+
+typedef struct {
+	delegation_zero_t	ds_zero;
+	delegation_boot_t	ds_boot;
+	delegation_id_t		ds_id;
+} delegation_stateid_t;
+
+extern stateid_t                    zero_stateid;    /* bits all zeros */
+extern stateid_t                    one_stateid;     /* bits all ones  */
+#define ZERO_STATEID(stateid)       (!memcmp((stateid), &zero_stateid, sizeof(stateid_t)))
+#define ONE_STATEID(stateid)        (!memcmp((stateid), &one_stateid, sizeof(stateid_t)))
+
+static inline void
+update_stateid(stateid_t *stateid)
+{
+  	nfsv4_printk(stateid, "stateid = (%08x/%08x/%08x/%08x->%08x)\n",
+		     stateid->st_boot, stateid->st_lockowner, stateid->st_file,
+		     stateid->st_generation, stateid->st_generation + 1);
+	stateid->st_generation++;
+}
+
+/*
+ *    struct nfs4_client - We have one of these for every client.  Clientids live here.
+ *        o Each nfs4_client is hashed by clientid.
+ *        o Each nfs4_clients is also hashed by name (the opaque quantity initially sent by
+ *          the client to identify itself).
+ *        o Each nfs4_client is also the head of a list of nfs4_lockowners which are active
+ *          for the client.
+ *        o Each nfs4_client lives on an LRU list, ordered by time of last lease renewal,
+ *          which will be scanned periodically by the laundromat thread.
+ *  
+ *    struct nfs4_lockowner - We have one of these for every lockowner.  Seqids live here.
+ *        o Each nfs4_lockowner contains a pointer back to its nfs4_client.
+ *        o Each nfs4_lockowner is hashed by name (the opaque quantity sent by the client
+ *          to identify a lockowner), and by lockowner id (lockowner ids are used for
+ *          OPEN_CONFIRM).
+ *        o Each nfs4_lockowner lives on a list of nfs4_lockowners which are active for
+ *          its client.
+ *        o Each nfs4_lockowner is the head of a list of nfs4_openfiles which were opened
+ *          by the lockowner.
+ *        o Each "unconfirmed" lockowner (meaning that an OPEN_CONFIRM is outstanding, or
+ *          the lockowner has no files open) lives on an LRU list, ordered by time of last
+ *          use, which will be scanned periodically by the laundromat thread.
+ *
+ *    struct nfs4_file - We have one of these for every active (i.e., opened by one or more
+ *               clients) file.  Stateids do NOT live here; they live in nfs4_openfiles.
+ *        o Each nfs4_file contains an nfs4_ino_dec_t, and is hashed by it.
+ *        o Each nfs4_file is the head of a list of nfs4_openfiles corresponding to OPENs
+ *          done on the file.
+ *
+ *    struct nfs4_openfile:  We have one of these per open (lockowner,file)-pair.  It
+ *               represents the state of a single OPEN, done by a specific lockowner on a
+ *               specific file.  Stateids live here.
+ *        o Each nfs4_openfile contains pointers back to its nfs4_lockowner and nfs4_file.
+ *        o Each nfs4_openfile is hashed by stateid.
+ *        o Each nfs4_openfile lives on a list of nfs4_openfiles for the same lockowner.
+ *        o Each nfs4_openfile lives on a list of nfs4_openfiles for the same file.
+ */
+struct nfs4_client {
+	LIST_ENTRY(nfs4_client)			cl_idhash;
+	LIST_ENTRY(nfs4_client)			cl_strhash;
+	TAILQ_ENTRY(nfs4_client)		cl_lru;
+  	_LIST_HEAD(dummy1, nfs4_lockowner)	cl_perclient;  /* list of lockowners */
+	_LIST_HEAD(dummyW, nfsd4_delegation)	cl_delegations; /* list of delegations */
+	clientid_t				cl_clientid;
+	int					cl_namelen;
+	char					*cl_name;
+	u32					cl_addr;       /* client's IP address */
+	verifier4				cl_verifier;   /* client's, not server's! */
+	time_t					cl_time;       /* time of last lease renewal */
+	int					cl_count;
+
+	/* callback info -- assumed UDP/IPv4 for now */
+	u32					cl_callback_addr;
+	unsigned short				cl_callback_port;
+	u32					cl_callback_prog;
+	struct rpc_program			cl_callback_program;
+	struct rpc_stat				cl_callback_stat;
+	struct rpc_clnt *			cl_callback_client;
+
+	unsigned int				cl_confirmed : 1,
+						cl_parsed_callback : 1,
+						cl_have_callback : 1;
+};
+
+struct nfs4_lockowner {
+	LIST_ENTRY(nfs4_lockowner)		lo_idhash;
+	LIST_ENTRY(nfs4_lockowner)		lo_strhash;
+	LIST_ENTRY(nfs4_lockowner)		lo_perclient;
+	TAILQ_ENTRY(nfs4_lockowner)		lo_lru;
+	_LIST_HEAD(dummy2, nfs4_openfile)	lo_perlockowner;  /* list of openfiles */
+	stateid_lockowner_t			lo_id;
+	struct nfs4_client			*lo_client;
+	seqid4					lo_seqid;
+	unsigned int				lo_namelen;
+	char					lo_name[NFSD4_LOCKOWNER_MAXNAME];
+	unsigned int				lo_hashval;
+	int					lo_confirmed;       /* boolean */
+	int					lo_is_open_owner;   /* boolean */
+	time_t					lo_time;
+	nfs4_ino_desc_t				lo_ino;  /* XXX: bakeoff hack */
+	
+	/* Replay buffer, where the result of the last seqid-mutating operation is cached */
+	u32					lo_replay_status;
+	unsigned int				lo_replay_buflen;
+	char					*lo_replay_buf;
+	unsigned int				lo_replay_allocated;
+	char					lo_replay_ibuf[NFSD4_REPLAY_ISIZE];
+};
+
+struct nfs4_file {
+	LIST_ENTRY(nfs4_file)			fi_hash;
+	_LIST_HEAD(dummy3, nfs4_openfile)	fi_perfile;   /* list of openfiles */
+	_LIST_HEAD(dummyV, nfsd4_delegation)	fi_delegations;  /* list of delegations */
+	nfs4_ino_desc_t				fi_ino;
+	stateid_file_t				fi_id;
+	/*
+	 * I thought about keeping counts for the different share reservations that had been
+	 * placed on the file here, to make the algorithm for checking share conflicts more
+	 * scalable, but decided against it (I think that, in practice, a single file opened
+	 * by a "large" number of lockowners will be a very rare case.)
+	 */
+};
+
+/*
+ * BAKEOFF HACKING: The following fields are new: of_lock_stateids, of_open_owner.
+ *
+ * The nfs4_openfile can now represent either an open_stateid or lock_stateid.
+ *
+ * For an open_stateid--
+ *   of_open_owner is NULL.  This allows us to distinguish open stateids from lock stateids.
+ *   of_lock_stateids starts a list of lock_stateids rooted at this open_stateid.
+ *
+ * For a lock stateid--
+ *   of_perfile is still defined, but now links through the of_lock_stateids field of the
+ *     associated open stateid, NOT the analagous field of the associated nfs4_file.
+ *     XXX: maybe rename this to of_list?
+ *   of_open_owner points to the associated open stateid (XXX: bad naming)
+ *   of_lock_stateids, of_file, of_vfs_file, of_share_access, of_share_deny are all undefined.
+ *   XXX: rename of_open_owner to of_open_stateid!
+ */
+struct nfs4_openfile {
+	LIST_ENTRY(nfs4_openfile)		of_hash;
+	LIST_ENTRY(nfs4_openfile)		of_perlockowner;
+	LIST_ENTRY(nfs4_openfile)		of_perfile;
+	_LIST_HEAD(dummyF, nfs4_openfile)	of_lock_stateids;
+	struct nfs4_openfile			*of_open_owner;
+	struct nfs4_lockowner			*of_lockowner;
+	struct nfs4_file			*of_file;
+	stateid_t				of_stateid;
+	nfs4_file_desc_t			of_vfs_file;
+	unsigned int				of_share_access;
+	unsigned int				of_share_deny;
+};
+#define of_client				of_lockowner->lo_client
+#define IS_OPEN_STATEID(ofp)			((ofp)->of_open_owner == NULL)
+#define IS_LOCK_STATEID(ofp)			((ofp)->of_open_owner != NULL)
+
+struct nfsd4_delegation {
+	LIST_ENTRY(nfsd4_delegation)	dl_perfile;
+	LIST_ENTRY(nfsd4_delegation)	dl_perclient;
+	LIST_ENTRY(nfsd4_delegation)	dl_hash;
+	TAILQ_ENTRY(nfsd4_delegation)	dl_recalled_lru;
+	struct nfs4_client		*dl_client;
+	struct nfs4_file		*dl_file;
+
+	u32				dl_type;
+	delegation_stateid_t		dl_stateid;
+	u32				dl_fhlen;
+	u32				dl_fhval[6];
+
+	time_t				dl_time;
+	unsigned int			dl_recalled : 1;
+};
+
+/*
+ *  Prototypes for functions defined in state.c.
+ */
+extern void nfs4_state_init(void);
+extern void nfs4_state_shutdown(void);
+extern void nfs4_state_restart(int grace_period);
+extern void nfs4_lock_state(void);
+extern void nfs4_unlock_state(void);
+extern time_t nfs4_laundromat(void);
+extern int nfs4_in_grace_period(void);
+extern time_t boot_time;
+extern void release_client(struct nfs4_client *clp);
+extern void release_openfile(struct nfs4_openfile *ofp);
+
+extern int nfs4_process_open1(struct global_state *stp, struct nfsd4_open *open);
+extern int nfs4_process_open2(struct global_state *stp, struct nfsd4_open *open);
+extern int nfs4_create_lock_stateid(struct global_state *stp, struct nfs4_openfile **ofpp,
+				    struct nfsd4_lock *lock);
+
+/* To process { READ, WRITE, SETATTR(truncating) }, first test for the "magic stateids"
+ * 0 and -1 and handle them accordingly.  Then call nfs4_preprocess_stateid_op() to validate
+ * the stateid, which will set stp->current_openfile.  Then test whether the openfile is
+ * opened with read or write access, as appropriate, and continue processing the operation.
+ */
+extern int nfs4_preprocess_stateid_op(struct global_state *stp, stateid_t *stateid,
+				      int flags, struct nfs4_openfile **ofpp);
+
+/* To process { LOCK, LOCKU }, call nfs4_preprocess_seqid_op(), which sets
+ * stp->current_lockowner and stp->current_openfile.  NFS4ERR_REPLAY_ME is a possible
+ * return value.
+ */
+extern int nfs4_preprocess_seqid_op(struct global_state *stp, u32 seqid,
+				    stateid_t *stateid, int flags,
+				    struct nfs4_lockowner **lpp, struct nfs4_openfile **ofpp);
+
+/* flags for preprocess_seqid_op() */
+#define CHECK_FH		0x00000001
+#define REQUIRE_OPEN_STATEID	0x00000002
+#define REQUIRE_LOCK_STATEID	0x00000004
+#define CONFIRM			0x00000008
+
+/* Miscellanea:
+ *   nfs4_share_conflicts() - reports whether any OPENs on a file deny read/write access.
+ *                            (READ/WRITE with magic stateids need this.)
+ *   nfs4_lookup_lockowner() - looks up a lockowner by name and sets stp->current_lockowner.
+ *                             (LOCKT needs this.)
+ *   nfs4_verify_lockowner() - checks whether an (nfs4_lockowner *) from an "untrusted"
+ *                             source is valid, or a garbage pointer.  (LOCKT needs this.)
+ */
+extern int nfs4_share_conflicts(struct global_state *stp, unsigned int deny_type);
+extern int nfs4_lookup_lockowner(struct global_state *stp, struct nfsd4_lockt *lockt);
+extern int nfs4_verify_lockowner(struct nfs4_lockowner *lp, unsigned int hashval);
+extern int nfsd4_check_delegation(struct inode *inode);
+
+#endif   /* NFSD4_STATE_H */
--- clean/include/linux/nfs4/nfs4fs.h	Thu Feb  7 18:04:30 2002
+++ dirty/include/linux/nfs4/nfs4fs.h	Wed Feb  6 14:06:28 2002
@@ -0,0 +1,1086 @@
+/*
+ *  linux/fs/nfs4/nfs4fs.h
+ *
+ *  Copyright (c) 2001 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  Kendrick Smith <kmsmith@umich.edu>
+ *  Andy Adamson <andros@umich.edu>
+ *  
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *  
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the University nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _LINUX_NFS4_NFS4FS_H
+#define _LINUX_NFS4_NFS4FS_H
+
+#include <linux/nfs4_fs_sb.h>
+#include <linux/nfs4_fs_i.h>
+
+/*
+ * Magic constants..
+ */
+#define NFS4_MINOR_VERSION		0
+#define NFS4_DEF_FILE_IO_BUFFER_SIZE	4096
+#define NFS4_MAX_FILE_IO_BUFFER_SIZE	32768
+#define NFS4_DEF_MAXFILESIZE		0xffffffff
+#define NFS4_SUPER_MAGIC		0xF00BA4
+#define NFS4FS_RETRY_OLD_STATEID	1
+#define NFS4FS_MIN_LEASE                (1 * HZ)
+#define NFS4FS_DEFAULT_LEASE            (30 * HZ)
+#define NFS4FS_MAX_LEASE                (120 * HZ)
+#define NFS4FS_RETRY_MIN_DELAY		(HZ >> 4)
+#define NFS4FS_RETRY_MAX_DELAY		(HZ << 3)
+#define NFS4FS_SEMAPHORE_DELAY		(HZ >> 4)
+#define NFS4FS_GRACE_DELAY		(HZ * 5)
+#define NFS4FS_OLD_STATEID_DELAY	(HZ >> 3)
+#define NFS4FS_OP_MAX			10
+#define NFS4_BUFSIZE			8192    /* XXX: right value? */
+#define NFS4_SETCLIENTID_MAXTRIES	5
+#define NFS4_READDIR_MAXTRIES		5
+#define NFS4_MAX_REQUEST_SOFT		192
+#define NFS4_MAX_REQUEST_HARD		256
+#define NFS4_MAXCOMMIT			64
+#define NFS4_READ_DELAY			(2 * HZ)
+#define NFS4_WRITEBACK_DELAY		(5 * HZ)
+#define NFS4_WRITEBACK_LOCKDELAY	(60 * HZ)
+#define NFS4_COMMIT_DELAY		(5 * HZ)
+
+/*
+ * Macros for digging into the fs-private area of the superblock
+ */
+#define NFS4_CLNT(sb)			((sb)->u.nfs4_sb.client)
+#define NFS4_MOUNT_FLAGS(sb)		((sb)->u.nfs4_sb.flags)
+#define NFS4_RPAGES(sb)			((sb)->u.nfs4_sb.rpages)
+#define NFS4_WPAGES(sb)			((sb)->u.nfs4_sb.wpages)
+#define NFS4_CLIENT(sb)			((sb)->u.nfs4_sb.nfs_client)
+#define NFS4_CLIENTID(sb)		((sb)->u.nfs4_sb.nfs_client->cl_clientid)
+#define NFS4_PLEN(sb)			((sb)->u.nfs4_sb.plen)
+#define NFS4_PVAL(sb)			((sb)->u.nfs4_sb.pval)
+#define NFS4_LOCK_MINTIMEOUT(sb)	((sb)->u.nfs4_sb.lock_mintimeout)
+#define NFS4_LOCK_MAXTIMEOUT(sb)	((sb)->u.nfs4_sb.lock_maxtimeout)
+#define NFS4_STRICT_LOCKING(sb)		((sb)->u.nfs4_sb.strict_locking)
+#define NFS4_SILLY_RENAME(sb)		((sb)->u.nfs4_sb.silly_rename)
+#define NFS4_LEASE_TIME(sb)             ((sb)->u.nfs4_sb.lease_time)
+#define NFS4_LAST_RENEWAL(sb)		((sb)->u.nfs4_sb.last_renewal)
+#define NFS4_FLUSHD(sb)			((sb)->u.nfs4_sb.flushd)
+#define NFS4_FLUSHD_TIMEOUT(sb)		((sb)->u.nfs4_sb.flushd_timeout)
+#define NFS4_FLUSHD_INODES(sb)		((sb)->u.nfs4_sb.flushd_inodes)
+#define NFS4_IP_ADDR(sb)		((sb)->u.nfs4_sb.ip_addr)
+#define NFS4_CALLBACK_PROG(sb)		((sb)->u.nfs4_sb.callback_prog)
+#define NFS4_CALLBACK_PORT(sb)		((sb)->u.nfs4_sb.callback_port)
+#define NFS4_DELEGATIONS(sb)		((sb)->u.nfs4_sb.delegations)
+
+/*
+ * Macros for digging into the fs-private area of the superblock
+ */
+#define NFS4_FSID_MAJOR(inode)			((inode)->u.nfs4_i.fsid_major)
+#define NFS4_FSID_MINOR(inode)			((inode)->u.nfs4_i.fsid_minor)
+#define NFS4_FILEID(inode)			((inode)->u.nfs4_i.fileid)
+#define NFS4_FLAGS(inode)			((inode)->u.nfs4_i.flags)
+#define NFS4_FH_LEN(inode)			((inode)->u.nfs4_i.fh_len)
+#define NFS4_FH_VAL(inode)			((inode)->u.nfs4_i.fh_val)
+#define NFS4_FH_INLINE_VAL(inode)		((inode)->u.nfs4_i.fh_inline_val)
+#define NFS4_DIRECTORY_SIZE(inode)		((inode)->u.nfs4_i.directory_size)
+#define NFS4_CACHE_JIFFIES(inode)		((inode)->u.nfs4_i.cache_jiffies)
+#define NFS4_CACHE_CHANGEID(inode)		((inode)->u.nfs4_i.cache_changeid)
+#define NFS4_CACHE_TIMEOUT(inode)		((inode)->u.nfs4_i.cache_timeout)
+#define NFS4_CACHE_TIMEOUT_JIFFIES(inode)	((inode)->u.nfs4_i.cache_timeout_jiffies)
+#define NFS4_DELEGATION(inode)                  ((inode)->u.nfs4_i.delegation)
+#define NFS4_READ_OPENS(inode)			((inode)->u.nfs4_i.read_opens)
+#define NFS4_WRITE_OPENS(inode)			((inode)->u.nfs4_i.write_opens)
+#define NFS4_ACCESS_CACHE_ENTRIES(inode)	((inode)->u.nfs4_i.access_cache_entries)
+#define NFS4_LOCKING_SEMAPHORE(inode)		((inode)->u.nfs4_i.locking_sema)
+#define NFS4_FLUSHD_NEXTSCAN(inode)		((inode)->u.nfs4_i.flushd_nextscan)
+#define NFS4_FLUSHD_LIST(inode)			((inode)->u.nfs4_i.flushd_list)
+#define NFS4_FLUSHD_READ(inode)			((inode)->u.nfs4_i.flushd_read)
+#define NFS4_FLUSHD_WRITE(inode)		((inode)->u.nfs4_i.flushd_write)
+#define NFS4_FLUSHD_DIRTY(inode)		((inode)->u.nfs4_i.flushd_dirty)
+#define NFS4_FLUSHD_COMMIT(inode)		((inode)->u.nfs4_i.flushd_commit)
+#define NFS4_FLUSHD_NREAD(inode)		((inode)->u.nfs4_i.flushd_nread)
+#define NFS4_FLUSHD_NDIRTY(inode)		((inode)->u.nfs4_i.flushd_ndirty)
+#define NFS4_FLUSHD_NCOMMIT(inode)		((inode)->u.nfs4_i.flushd_ncommit)
+
+#define NFS4_RSIZE(inode)                       ((inode)->i_sb->u.nfs4_sb.rsize)
+#define NFS4_WSIZE(inode)                       ((inode)->i_sb->u.nfs4_sb.wsize)
+#define NFS4_DTSIZE(inode)                      ((inode)->i_sb->u.nfs4_sb.dtsize)
+
+#define NFS4_IS_REVALIDATING(inode)		(NFS4_FLAGS(inode) & NFS4_INO_REVALIDATING)
+#define NFS4_HAS_SNAPSHOT(inode)		(NFS4_FLAGS(inode) & NFS4_INO_SNAPSHOT)
+#define NFS4_FH_IS_INLINE(inode)	(NFS4_FH_VAL(inode) == NFS4_FH_INLINE_VAL(inode))
+
+/*
+ * Macros for digging into the fs-private area of the file_lock
+ */
+#define NFS4_BASE_TYPE(file_lock)		((file_lock)->fl_u.nfs4_fl.base_type)
+#define NFS4_CURRENT_TYPE(file_lock)		((file_lock)->fl_u.nfs4_fl.current_type)
+#define NFS4_NR_BLOCKEDREAD(file_lock)		((file_lock)->fl_u.nfs4_fl.nr_blockedread)
+#define NFS4_NR_BLOCKEDWRITE(file_lock)		((file_lock)->fl_u.nfs4_fl.nr_blockedwrite)
+
+/*
+ *  nfs4_rwlock_t: We need a read-write semaphore which provides trylock() semantics.
+ *  More specifically, we need the following primitives:
+ *    nfs4_down_read() - blocking read lock
+ *    nfs4_down_write() - blocking write lock
+ *    nfs4_up_read() - read unlock
+ *    nfs4_up_write() - write unlock
+ *    nfs4_down_read_trylock() - nonblocking read lock, returns 1 if lock unavailable
+ *    nfs4_down_write_trylock() - nonblocking write lock, returns 1 if lock unavailable
+ *
+ *  At the present time, no such semaphore exists (trylock isn't defined for rw_semaphores),
+ *  so I have provided an egregiously inefficient equivalent below.
+ *
+ *  TODO: Do this right, by adding trylock() routines to include/linux/rwsem.h
+ */
+
+typedef struct {
+	int read_count;       /* negative if there are writers */
+	struct rw_semaphore sem;
+} nfs4_rwlock_t;
+
+static inline void
+nfs4_down_read(nfs4_rwlock_t *lock)
+{
+	lock_kernel();
+	down_read(&lock->sem);
+	if (lock->read_count < 0)
+		BUG();
+	lock->read_count++;
+	unlock_kernel();
+}
+
+static inline void
+nfs4_down_write(nfs4_rwlock_t *lock)
+{
+	lock_kernel();
+	down_write(&lock->sem);
+	if (lock->read_count != 0)
+		BUG();
+	lock->read_count--;
+	unlock_kernel();
+}
+
+static inline void
+nfs4_up_read(nfs4_rwlock_t *lock)
+{
+	lock_kernel();
+	up_read(&lock->sem);
+	if (lock->read_count <= 0)
+		BUG();
+	lock->read_count--;
+	unlock_kernel();
+}
+
+static inline void
+nfs4_up_write(nfs4_rwlock_t *lock)
+{
+	lock_kernel();
+	up_write(&lock->sem);
+	if (lock->read_count >= 0)
+		BUG();
+	lock->read_count++;
+	unlock_kernel();
+}
+
+static inline int
+nfs4_down_read_trylock(nfs4_rwlock_t *lock)
+{
+	lock_kernel();
+	if (lock->read_count < 0) {
+		unlock_kernel();
+		return 1;
+	}
+	down_read(&lock->sem);   /* can't block */
+	lock->read_count++;
+	unlock_kernel();
+	return 0;
+}
+
+static inline int
+nfs4_down_write_trylock(nfs4_rwlock_t *lock)
+{
+	lock_kernel();
+	if (lock->read_count > 0) {
+		unlock_kernel();
+		return 1;
+	}
+	down_write(&lock->sem);  /* can't block */
+	lock->read_count--;
+	unlock_kernel();
+	return 0;
+}
+
+static inline void
+nfs4_init_rwlock(nfs4_rwlock_t *lock)
+{
+	lock->read_count = 0;
+	init_rwsem(&lock->sem);
+}
+
+struct nfs4fs_compound;
+struct nfs4fs_client;
+struct nfs4fs_lockowner;
+struct nfs4fs_file_data;
+typedef void (*nfs4_action)(struct nfs4fs_compound *);
+
+/*
+ * This is the context of a single compound RPC.  It can always be found in the
+ * ->tk_calldata field of our rpc_task's.
+ *
+ * TODO: There is a huge problem here!  This structure ended up being so big that
+ * putting more than one of them on the stack can sometimes eat up all our stack
+ * space and cause a system freeze.  Making sure that only one nfs4fs_compound is
+ * ever on the stack has led me into some awkward convolutions; see
+ *    fs/nfs4fs/inode.c:  do_setattr()
+ *    fs/nfs4fs/dir.c:    do_rename()
+ * and even worse
+ *    fs/nfs4fs/state.c:  do_undelegate_sync()
+ *    fs/nfs4fs/access.c: nfs4fs_try_to_delegate_open()
+ * We need a reasonable way to reduce the size of this struct!
+ */
+struct nfs4fs_compound {
+	struct rpc_task			task;
+	unsigned int			flags;		/* defined below */
+	struct super_block *		sb;
+
+	/* If this is a stateful operation, all of our state hangs here */
+	struct nfs4fs_client *		client;
+	struct nfs4fs_lockowner *	seqid_holder;
+	struct nfs4fs_file_data *	stateid_holder;
+	
+	/*
+	 * These methods are invoked: (1) just after the COMPOUND is received,
+	 *                            (2) just before the nfs4fs_compound is destroyed
+	 */
+	nfs4_action			exit;
+	nfs4_action			release;
+	
+	/* used for synchronization between parent and child */
+	struct nfs4fs_compound *	parent;
+	struct nfs4fs_op *		child_op;
+	struct rpc_wait_queue		parent_queue;
+
+	/* used for SECINFO */
+	struct dentry *			secinfo_dentry;
+	struct rpc_auth *		secinfo_auth;
+	
+	/*
+	 * @ seqid_index: location within the COMPOUND of the seqid-mutating operation
+	 * @ renew_index: location of the operation which renews state
+	 * @ confirm_index: location of the OPEN operation (needed if OPEN_CONFIRM is spawned)
+	 * @ timestamp: time at which RPC was sent, in jiffies (needed by renewd)
+	 * @ retry_delay: timeout before retrying after ERR_DELAY (exponential backoff)
+	 * @ old_stateid_count: safeguard, so we don't retry ERR_OLD_STATEID indefinitely
+	 */
+	int				seqid_index;
+	int				renew_index;
+	int				confirm_index;
+	unsigned long			timestamp;
+	unsigned long			retry_delay;
+#if NFS4FS_RETRY_OLD_STATEID
+	int				old_stateid_count;
+#endif
+
+	/* scratch variables for XDR encode/decode */
+	int				nops;
+	u32 *				p;
+	u32 *				end;
+
+	/* the individual COMPOUND operations */
+	struct nfs4fs_op		ops[NFS4FS_OP_MAX];
+
+	/* request */
+	unsigned int			req_nops;
+	u32				taglen;
+	char *				tag;
+
+	/* response */
+	int				resp_nops;
+	int				toplevel_status;
+	
+	/*
+	 * This "private area" makes life a little easier for a few
+	 * of the async operations...
+	 */
+	union {
+		struct {
+			struct file *		file;
+		} commit;
+		struct {
+			/* XXX: this arm of the union can now be removed, no? */
+			struct dentry *   dentry;
+		} secinfo;
+		struct {
+			struct list_head	list;
+			struct dentry *		dentry;
+			struct inode *		dir_inode;
+			struct qstr		str;
+		} sillyrename;
+		struct {
+			struct rpc_task *	returnd;
+			struct nfs4fs_file_data *fp;
+		} undelegate;
+		struct {
+			struct file *		file;
+			struct inode *		inode;
+		} write;
+	} u;
+};
+
+/*
+ * Bits for the 'flags' field in nfs4fs_compound.
+ * These are needed so that certain "generic" parts of COMPOUND processing can be
+ * implemented in one place.  (See fs/nfs4fs/proc.c)
+ *
+ * CA_SEQID_LOCKED - Are we holding ->seqid_holder->lo_sema (provides seqid serialization)?
+ * CA_STATEID_LOCKED - Are we holding ->stateid_holder->fi_sema (stateid serialization)?
+ *
+ * CA_SEQID_RELEASE - Should the seqid semaphore be released in postprocess_compound(), i.e.
+ *                    immediately upon XDR decoding the response and incrementing the seqid?
+ *                    This bit is always set, except for OPEN requests.  For these, we want
+ *                    to hang on to the ->lo_sema until finishing the OPEN_CONFIRM (the
+ *                    spec requires the OPEN_CONFIRM seqid to be 1 + the OPEN seqid).
+ *
+ * CA_STATEID_RELEASE - Should postprocess_compound() release the the stateid semaphore?
+ *                      This also controls whether a read or write lock is taken on the
+ *                      stateid semaphore.  CA_STATEID_RELEASE => read lock
+ *                                       No CA_STATEID_RELEASE => write lock
+ *                      TODO: This last usage is kind of confusing.  Better to have a bit
+ *                      CA_STATEID_WRITELOCK to explicitly indicate read or write.
+ *
+ * CA_AUXILIARY - This implies that ->stateid_holder is a lock_stateid, and that we also
+ *                have to acquire "auxiliary" locks on the associated open_stateid, i.e.
+ *                    ->stateid_holder->open_stateid->fi_lockowner->lo_sema
+ *                    ->stateid_holder->open_stateid->fi_sema
+ *                This is needed by the LOCK operation, which may need to serialize any of:
+ *                the lock stateid, open stateid, lock seqid, open seqid (ugh).
+ *
+ * CA_AUX_SEQID_LOCKED - If set, CA_AUXILIARY is also set.
+ *                       Are we holding ->stateid_holder->open_stateid->fi_lockowner->lo_sema?
+ * CA_AUX_STATEID_LOCKED - If set, CA_AUXILIARY is also set.
+ *                         Are we holding ->stateid_holder->open_stateid->fi_sema?
+ * CA_AUX_SEQID_MUTATE - If set, CA_AUXILIARY is also set.
+ *                       Should postprocess_compound() increment the "auxilary seqid", i.e.
+ *                           ->stateid_holder->open_stateid->fi_lockowner->lo_sema
+ *                       in addition to the usual seqid
+ *                           ->seqid_holder->lo_sema ?
+ *                       (The LOCK operation will sometimes want this behavior, sometimes not.)
+ */
+#define CA_STATEFUL		0x00000001
+#define	CA_SEQID_LOCKED		0x00000002	/* is seqid currently locked? */
+#define CA_SEQID_RELEASE	0x00000004	/* should handler release seqid? */
+#define CA_STATEID_LOCKED	0x00000008	/* is stateid currently locked? */
+#define CA_STATEID_RELEASE	0x00000010	/* should handler release stateid? */
+#define CA_AUXILIARY		0x00000020
+#define CA_AUX_SEQID_LOCKED	0x00000040
+#define CA_AUX_STATEID_LOCKED	0x00000080
+#define CA_AUX_SEQID_MUTATE	0x00000100      /* should handler increase aux. seqid? */
+
+/*
+ * The next four structures define all of the client-side state:
+ *   clients, lockowners, file_data, delegations
+ */
+struct nfs4fs_client {
+	atomic_t		cl_count;	/* refcount */
+	u64			cl_clientid;	/* constant */
+
+	/*
+	 * Starts a list of lockowners, linked through lo_list.
+	 * This currently isn't used for much, but will be needed once recovery from
+	 * server reboots is implemented.
+	 */
+	struct list_head	cl_lockowners;	/* protected by state_spinlock */ 
+};
+
+struct nfs4fs_lockowner {
+	atomic_t		lo_count;	/* refcount */
+	struct nfs4fs_client	*lo_client;	/* holds reference */
+	struct list_head	lo_stateids; 	/* list of file_data, linked through fi_list */
+	struct list_head	lo_list;	/* links all lockowners on this client */
+	u32			lo_id;		/* 32-bit identifier, unique */
+	
+	/*
+	 * The following fields are used for lock_owners only.
+	 * Note!  We do not claim a reference in ->lo_inode; this is OK because we never
+	 * dereference it.  The pointer itself is used as a hash key.
+	 * The hash lists are protected by the state_spinlock.
+	 */
+	struct list_head	lo_hash;	/* hashed by inode, pid */
+	struct list_head	lo_id_hash;	/* hashed by id */
+	struct inode *		lo_inode;	/* constant */
+	pid_t			lo_pid;		/* constant */
+
+	/*
+	 * The ->lo_sema is held during all seqid-mutating operations.
+	 * Its purpose is to properly serialize the seqid, as mandated by the protocol.
+	 */
+	struct semaphore	lo_sema;
+	u32			lo_seqid;	/* protected by lo_sema */
+};
+
+/*
+ * These are in one-to-one correspondence with stateid's on the server.
+ * They can represent either open_stateid's or lock_stateid's.
+ * An open_stateid can be "delegated"; this means that the OPEN was done locally without
+ * sending anything to the server.
+ *
+ * For open_stateid's:
+ *     ->fi_lockowner points to an open_owner
+ *     ->fi_read (resp. fi_write) links all OPENs for read (resp. write) on this inode
+ *                                or delegation (depending on whether this OPEN is "delegated")
+ *     ->fi_linkage starts a list of all lock_stateids associated with the open_stateid
+ *     ->fi_open_stateid is NULL
+ *     ->fi_delegation is non-NULL iff the OPEN is "delegated", and holds a reference
+ *
+ * For lock_stateid's:
+ *     ->fi_lockowner points to a lock_owner
+ *     ->fi_{read,write} are empty
+ *     ->fi_linkage links all lock_stateids associciated with the same open_stateid
+ *     ->fi_open_stateid points to the associated open_stateid
+ *     ->fi_delegation is always NULL
+ *
+ * Everything here is protected by the state_spinlock, except the fields where
+ * protected by 'fi_sema' is noted.
+ */
+struct nfs4fs_file_data {
+	atomic_t			fi_count;	/* refcount */
+	struct nfs4fs_lockowner	*	fi_lockowner;	/* constant */
+	struct list_head		fi_list;	/* all files on this lockowner */
+
+	/*
+	 * This is subtle!  In the case of an open_stateid, there is a short period when
+	 * ->fi_inode can be NULL, if we are doing open-by-name and have not done a LOOKUP
+	 * to determine the inode.
+	 * During this period, the inode can also change, if we are doing open-by-name
+	 * and open a file different from what we expected.
+	 * This period officially ends upon successful return from nfs4fs_handle_open():
+	 * from this point onward, ->fi_inode will be non-NULL and never change.  To
+	 * be completely safe, I am not putting the file_data on any lists until the
+	 * end of nfs4fs_handle_open().
+	 *
+	 * In the case of a lock_stateid, none of these subtlties arise: the inode is
+	 * known definitively from the beginning.
+	 */
+	struct inode *			fi_inode;	/* holds reference */
+
+	/*
+	 * For open stateids only:
+	 *  These are lists of all OPEN's for read (resp. write).  These are linked
+	 *  either through the inode or the delegation, depending on whether the
+	 *  OPEN is a "real" open or a delegated one.
+	 */
+	struct list_head		fi_read;
+	struct list_head		fi_write;
+
+	/*
+	 * Linkage for open_stateids and lock_stateids.
+	 * There is another subtlety here: it would be nice if the lock_stateid could just
+	 * hold a reference to the associated open_stateid.  However, things are not as
+	 * simple as that.  We want to leave lock_stateids lying around for the duration
+	 * of the open_stateid, and only destroy them when the last reference to the
+	 * open_stateid goes away.  If references are held, this will be impossible.
+	 *
+	 * The solution that I have devised is to have the lock_stateid hold a
+	 * reference to the open_stateid, but only if its own refcount is > 0.
+	 * This means that when new references to the lock_stateid are acquired,
+	 * we must test if a new reference to the open_stateid must be acquired.
+	 * When the last reference to the lock_stateid goes away, the reference
+	 * to the open_stateid must be dropped.
+	 *
+	 * TODO: This solution seems awfully delicate; can someone think of another
+	 * way which might be more robust?
+	 */
+	struct list_head		fi_linkage;
+	struct nfs4fs_file_data *	fi_open_stateid;
+
+	/*
+	 * The ->fi_sema is held during all operations which use the stateid.
+	 * Its main purpose is to properly serialize the stateid, as mandated by the protocol.
+	 * I have also found that it is the "right" way to protect ->fi_flags, ->fi_delegation.
+	 */
+	nfs4_rwlock_t			fi_sema;
+	stateid4			fi_stateid;	/* protected by fi_sema */
+	unsigned int			fi_flags;	/* protected by fi_sema */
+	struct nfs4fs_delegation *	fi_delegation;	/* protected by fi_sema, holds ref */
+};
+#define fi_client			fi_lockowner->lo_client
+#define fi_sb				fi_inode->i_sb
+#define IS_OPEN_STATEID(fp)		(fp->fi_open_stateid == NULL)
+#define IS_LOCK_STATEID(fp)		(fp->fi_open_stateid != NULL)
+
+/*
+ * fi_flags are as follows:
+ *   FI_READ - if this is an open stateid, is the OPEN for read?
+ *   FI_WRITE - if this is an open stateid, is the OPEN for write?
+ *   FI_FAKE - if set, this is a "fake" stateid, meaning that it does not exist
+ *             on the server.  This can happen if:
+ *                o The stateid is freshly instantiated and we are waiting for the
+ *                  OPEN or LOCK response to instantiate it on the server.
+ *                o This is a "delegated" OPEN.
+ *                o Server reboot has been detected, and consequently all stateids
+ *                  have been marked "fake" pending reboot recovery (not yet implemented).
+ */
+#define FI_READ				0x00000001    /* same as OPEN4_SHARE_ACCESS_READ */
+#define FI_WRITE		       	0x00000002    /* same as OPEN4_SHARE_ACCESS_WRITE */
+#define FI_FAKE				0x00000004
+
+/*
+ * This macro converts between the open flags used internally by the VFS and the
+ * FI_{READ,WRITE} flags above.  (In this kernel, we just have to keep the 2 low bits.)
+ */
+#define OPEN_FLAGS_TO_FI_FLAGS(f)	((f) & 3)
+
+/*
+ * Note on the ->dl_{read,write}_placeholder fields:  If we hold a delegation, and
+ * a file descriptor is closed, we might want to keep the file open on the server.
+ * This is so that we can re-use the OPEN if another process opens the file (i.e.,
+ * "delegating" the new OPEN).  This is implemented by holding a "placeholder" reference
+ * to the file_data structure (the CLOSE is not sent until the last reference is dropped).
+ * Actually, we might need two placeholders, one for READ and one for WRITE.  The
+ * key invariant of the placeholders is: If there is a _unique_ open for READ (resp.
+ * write) on the server, ->dl_{read,write}_placeholder is non-NULL and holds a reference.
+ * We always maintain this invariant.  Thus if a new OPEN is instantiated, it may
+ * become a placeholder (if it is the unique OPEN for read or write), or cause a
+ * placeholder to go away (if an old OPEN is no longer the unique one).  When an OPEN
+ * is destroyed, it may cause a placeholder to be set (if destroying it means that there
+ * is now a unique OPEN for read or write).
+ *
+ * Everything here is either constant, or protected by the state_spinlock.
+ */
+struct nfs4fs_delegation {
+	atomic_t		dl_count;
+	struct super_block	*dl_sb;		/* constant */
+	struct inode		*dl_inode;	/* constant */
+	struct list_head	dl_hash;	/* hashed by superblock, filehandle */
+	struct list_head	dl_read_opens;	/* starts a list of delegated read OPENs */
+	struct list_head	dl_write_opens;	/* starts a list of delegated write OPENs */
+	struct list_head	dl_list;	/* links a list of delegations on this sb */
+	struct nfs4fs_file_data *dl_read_placeholder;	/* holds reference */
+	struct nfs4fs_file_data *dl_write_placeholder;	/* holds reference */
+	u32			dl_type;	/* constant */
+	stateid4		dl_stateid;	/* constant */
+
+	/*
+	 * If the delegation is recalled, we might have to "undelegate" OPENs
+	 * which have been delegated to the client.  The current version of the
+	 * nfsv4 protocol requires the name to be specified in the OPEN request.
+	 * (Hopefully, this can be changed.)  For the time being, then, we must
+	 * save the following in the delegation:
+	 *   - the filename
+	 *   - the containing directory's filehandle
+	 * Since I anticipate this being a temporary hack, pending change in
+	 * the protocol, I've set hardcoded limits here.  If the limits are
+	 * exceeded, we refuse to accept the delegation..
+	 *
+	 * These are constant, so don't worry about protecting them with
+	 * a lock.
+	 */
+	unsigned int		dl_fhlen;   		/* parent filehandle length */
+	char			dl_fhval[64];		/* parent filehandle value */
+	char			dl_filename_len;
+	char			dl_filename[64];
+};
+/* end of client-side state definitions */
+
+/*
+ * nfs4_page: This is taken from nfsv2/v3, with minor changes for v4.
+ * It is the key bookkeeper for our read/write paths.
+ */
+struct nfs4_page {
+	struct list_head	wb_list;
+	struct list_head	wb_writelist;
+	wait_queue_head_t	wb_wait;
+	unsigned long		wb_flags;
+	unsigned long		wb_timeout;            /* in jiffies */
+	struct file		*wb_file;              /* holds reference, can be NULL */
+	struct nfs4fs_file_data	*wb_fdata;             /* holds reference */
+	struct page		*wb_page;              /* holds reference */
+	struct inode		*wb_inode;             /* no reference held */
+	unsigned int		wb_offset, wb_bytes;   /* defines byterange in page */
+	unsigned int		wb_count;  /* refcount, protected by nfs4_wreq_lock */
+	char			*wb_buf;
+	u32			wb_how_written;
+	verifier4		wb_verifier;
+	int			wb_safeguard;   /* protects against pathological servers */
+};
+
+/* only the one flag is defined for wb_flags */
+#define PG_BUSY			0
+
+/* despite the name (from nfsv2/v3), this actually protects both read and write requests. */
+extern spinlock_t		nfs4_wreq_lock;
+
+#ifndef FLUSH_AGING
+/*
+ * When flushing a cluster of dirty pages, there can be different
+ * strategies:
+ */
+#define FLUSH_AGING		0	/* only flush old buffers */
+#define FLUSH_SYNC		1	/* file being synced, or contention */
+#define FLUSH_WAIT		2	/* wait for completion */
+#define FLUSH_STABLE		4	/* commit to stable storage */
+#endif
+
+extern void _nfs4_release_request(struct nfs4_page *req);
+
+static inline void
+nfs4_release_request(struct nfs4_page *req)
+{
+	spin_lock(&nfs4_wreq_lock);
+	NFS4_ASSERT(req->wb_count > 0);
+
+	if (!--req->wb_count) {
+		spin_unlock(&nfs4_wreq_lock);
+		_nfs4_release_request(req);
+	}
+	else
+		spin_unlock(&nfs4_wreq_lock);
+}
+
+static inline int
+nfs4_lock_request(struct nfs4_page *req)
+{
+	if (test_and_set_bit(PG_BUSY, &req->wb_flags))
+		return 0;
+	req->wb_count++;
+	return 1;
+}
+
+static inline void
+nfs4_unlock_request(struct nfs4_page *req)
+{
+	NFS4_ASSERT(test_bit(PG_BUSY, &req->wb_flags));
+	NFS4_ASSERT(req->wb_buf == NULL);
+
+	smp_mb__before_clear_bit();
+	clear_bit(PG_BUSY, &req->wb_flags);
+	smp_mb__after_clear_bit();
+	if (waitqueue_active(&req->wb_wait))
+		wake_up(&req->wb_wait);
+	nfs4_release_request(req);
+}
+
+static inline void
+nfs4_kmap_request(struct nfs4_page *req)
+{
+	NFS4_ASSERT(req->wb_buf == NULL);
+	NFS4_ASSERT(test_bit(PG_BUSY, &req->wb_flags));
+	
+	req->wb_buf = kmap(req->wb_page);
+	NFS4_ASSERT(req->wb_buf != NULL);
+}
+
+static inline void
+nfs4_kunmap_request(struct nfs4_page *req)
+{
+	NFS4_ASSERT(req->wb_buf != NULL);
+	NFS4_ASSERT(test_bit(PG_BUSY, &req->wb_flags));
+
+	kunmap(req->wb_page);
+	req->wb_buf = NULL;
+}
+
+extern void nfs4_mark_request_dirty(struct nfs4_page *req);
+extern void nfs4_mark_request_commit(struct nfs4_page *req);
+
+/*
+ * nfs4_inode_{add,remove}_request(): adds an nfs4_page to an inode's writelist
+ *
+ * Warning! Callers of nfs4_inode_add_request() must hold the spinlock.
+ * Callers of nfs4_inode_remove_request() must NOT hold the spinlock.
+ * (Confusing, but this is what nfsv3 does...)
+ */
+static inline void
+nfs4_inode_add_request(struct inode *inode, struct nfs4_page *req)
+{
+	NFS4_ASSERT(list_empty(&req->wb_list));
+	NFS4_ASSERT(list_empty(&req->wb_writelist));
+	NFS4_ASSERT(test_bit(PG_BUSY, &req->wb_flags));
+	NFS4_ASSERT(req->wb_inode == inode);
+
+	/*
+	 * Note: We don't need to increment the inode's refcount, since a
+	 * reference is held through req->wb_fdata->fi_inode.
+	 */
+	req->wb_count++;
+	list_add(&req->wb_writelist, &NFS4_FLUSHD_WRITE(inode));
+}
+
+static inline void
+nfs4_inode_remove_request(struct nfs4_page *req)
+{
+	spin_lock(&nfs4_wreq_lock);
+	NFS4_ASSERT(!list_empty(&req->wb_writelist));
+	NFS4_ASSERT(list_empty(&req->wb_list));
+	NFS4_ASSERT(test_bit(PG_BUSY, &req->wb_flags));
+
+	list_del_init(&req->wb_writelist);
+	spin_unlock(&nfs4_wreq_lock);
+
+	nfs4_release_request(req);
+}
+
+/*
+ * Cache-manipulating operations.
+ */
+#define NFS4_MINTIMEOUT(inode)      (S_ISDIR(inode->i_mode) ? inode->i_sb->u.nfs4_sb.acdirmin \
+                                                            : inode->i_sb->u.nfs4_sb.acregmin)
+#define NFS4_MAXTIMEOUT(inode)      (S_ISDIR(inode->i_mode) ? inode->i_sb->u.nfs4_sb.acdirmax \
+                                                            : inode->i_sb->u.nfs4_sb.acregmax)
+#define NFS4_CACHEINV(inode)	    (NFS4_CACHE_JIFFIES(inode) = jiffies - \
+                                                            NFS4_MAXTIMEOUT(inode) - 1)
+#define NFS4_UPDATE_TIMEOUT(inode)  (jiffies > NFS4_CACHE_TIMEOUT_JIFFIES(inode) + \
+                                               NFS4_CACHE_TIMEOUT(inode))
+
+static inline void
+nfs4_zap_page_cache(struct inode *inode)
+{
+	NFS4_ASSERT(kernel_locked());
+	NFS4_ASSERT(inode != NULL);
+	/* XXX: should we use truncate_inode_pages() here instead? */
+	
+  	nfsv4_printk(reval, "zapping pagecache for ino %ld\n", inode->i_ino);
+	invalidate_inode_pages(inode);
+	NFS4_FLAGS(inode) &= ~NFS4_INO_SNAPSHOT;
+	NFS4_DIRECTORY_SIZE(inode) = 0;
+}
+
+/* This is called whenever we discover that our caches are invalid. */
+static inline void
+nfs4_zap_caches(struct inode *inode)
+{
+	NFS4_ASSERT(kernel_locked());
+	NFS4_ASSERT(inode != NULL);
+
+	nfsv4_printk(reval, "zapping caches for ino %ld\n", inode->i_ino);
+	NFS4_CACHE_JIFFIES(inode) = jiffies - NFS4_MAXTIMEOUT(inode) - 1;
+	NFS4_CACHE_TIMEOUT(inode) = NFS4_MINTIMEOUT(inode);
+	NFS4_CACHE_TIMEOUT_JIFFIES(inode) = jiffies;
+	nfs4_zap_page_cache(inode);
+}
+
+/* This is called whenever we discover that our caches are valid. */
+static inline void
+nfs4_validate_caches(struct inode *inode)
+{
+	NFS4_ASSERT(kernel_locked());
+	NFS4_ASSERT(inode != NULL);
+
+	nfsv4_printk(reval, "validating caches for ino %ld\n", inode->i_ino);
+	NFS4_CACHE_JIFFIES(inode) = jiffies;
+	if (NFS4_UPDATE_TIMEOUT(inode)) {
+		unsigned long timeout = NFS4_CACHE_TIMEOUT(inode);
+		unsigned long maxtimeout = NFS4_MAXTIMEOUT(inode);
+
+		nfsv4_printk(reval, "ino %ld cache_timeout %ld->%ld\n",
+			     inode->i_ino, NFS4_CACHE_TIMEOUT(inode), timeout);
+		
+		timeout <<= 1;
+		if (timeout > maxtimeout)
+			timeout = maxtimeout;
+		NFS4_CACHE_TIMEOUT(inode) = timeout;
+		NFS4_CACHE_TIMEOUT_JIFFIES(inode) = jiffies;
+	}
+}
+
+/* This returns true if revalidation is needed for an inode. */
+static inline int
+nfs4_need_reval(struct inode *inode)
+{
+	NFS4_ASSERT(kernel_locked());
+	NFS4_ASSERT(inode != NULL);
+	
+	if (NFS4_DELEGATION(inode)) {
+		nfsv4_printk(reval, "inode %ld has delegation, no reval\n", inode->i_ino);
+		return 0;
+	}
+	if (NFS4_UPDATE_TIMEOUT(inode)) {
+		nfsv4_printk(reval, "inode %ld needs revalidation\n", inode->i_ino);
+		return 1;
+	}
+	nfsv4_printk(reval, "inode %ld: no revalidation needed\n", inode->i_ino);
+	return 0;
+}
+
+/*
+ * For SECINFO.
+ * XXX: move to nfs4fs_xdr.h
+ */
+struct nfs4fs_flavor_info {
+  u32       oid_len;
+  char *     oid_data;
+  u32       qop;
+  u32       service;
+};
+
+struct nfs4fs_sec_flavor {
+  u32       flavor;
+  struct nfs4fs_flavor_info   *f_info;
+};
+
+
+/* access.c */
+extern void nfs4fs_init_access_cache(void);
+extern void nfs4fs_cleanup_access_cache(void);
+extern void nfs4fs_flush_access_cache(struct inode *inode);
+extern int nfs4fs_try_to_delegate_open(struct inode *inode, struct nfs4fs_file_data *fp,
+				       int flags, struct nfs4fs_compound *cp);
+
+/* callback.c */
+extern void nfs4fs_init_callbacks(void);
+extern int nfs4_create_callback(struct super_block *sb);
+extern void nfs4_destroy_callback(struct super_block *sb);
+
+/* decode.c */
+extern int nfs4fs_decode_compound(struct rpc_rqst *req, u32 *p, struct nfs4fs_compound *cp);
+
+/* dir.c */
+extern struct dentry_operations nfs4_dentry_operations;
+extern struct inode_operations nfs4_dir_inode_operations;
+extern struct file_operations nfs4_dir_operations;
+extern int nfs4_lookup_revalidate(struct dentry *dentry, int flags);
+extern int nfs4fs_do_lookup(struct dentry *dentry);
+
+/* encode.c */
+extern int nfs4fs_encode_compound(struct rpc_rqst *req, u32 *p, struct nfs4fs_compound *cp);
+
+/* file.c */
+extern struct inode_operations nfs4_file_inode_operations;
+extern struct file_operations nfs4_file_operations;
+extern struct address_space_operations nfs4_file_aops;
+extern wait_queue_head_t nfs4_wait;
+extern struct nfs4_page *nfs4_create_request(struct file *file, struct nfs4fs_file_data *fdata,
+					     struct page *page, unsigned int offset,
+					     unsigned int count);
+extern void _nfs4_release_request(struct nfs4_page *req);
+extern void nfs4_list_add_request(struct nfs4_page *req, struct list_head *head);
+extern int nfs4_scan_list(struct list_head *src, struct list_head *dst,
+			  struct file *file, unsigned long idx_start, unsigned int npages);
+extern int nfs4_scan_list_timeout(struct list_head *head,
+				  struct list_head *dst, struct inode *inode);
+extern int nfs4_coalesce_requests(struct list_head *src, struct list_head *dst,
+				  unsigned int maxpages);
+
+/* flushd.c */
+extern int nfs4fs_init_flushd(struct super_block *sb);
+extern void nfs4_wake_flushd(void);
+extern void nfs4_schedule_inode(struct inode *inode, unsigned long time);
+
+/* handle.c */
+extern int nfs4fs_handle_simple_op(struct nfs4fs_compound *cp);
+extern int nfs4fs_handle_access(struct nfs4fs_compound *cp, unsigned int *resp_access);
+extern int nfs4fs_handle_close(struct nfs4fs_compound *cp, struct file *filp);
+extern int nfs4fs_handle_commit(struct nfs4fs_compound *cp);
+extern int nfs4fs_handle_create(struct nfs4fs_compound *cp, struct dentry *dentry);
+extern int nfs4fs_handle_getattr_simple(struct nfs4fs_compound *cp, struct nfs4fs_fattr *fp);
+extern int nfs4fs_handle_getattr_inode(struct nfs4fs_compound *cp, struct inode *inode);
+extern int nfs4fs_handle_getattr_dentry(struct nfs4fs_compound *cp,
+					struct dentry *dentry, struct nfs4fs_fattr *fattr);
+extern int nfs4fs_handle_getfh(struct nfs4fs_compound *cp, struct inode *inode);
+extern int nfs4fs_handle_link(struct nfs4fs_compound *cp,
+			      struct dentry *dentry, struct inode *inode);
+extern int nfs4fs_handle_lock(struct nfs4fs_compound *cp);
+extern int nfs4fs_handle_lockt(struct nfs4fs_compound *cp, struct file_lock *flock);
+extern int nfs4fs_handle_lookup(struct nfs4fs_compound *cp, struct dentry *dentry);
+extern int nfs4fs_handle_open(struct nfs4fs_compound *cp, struct nfs4fs_file_data *fdata,
+			      struct dentry **dentryp);
+extern int nfs4fs_handle_undelegating_open(struct nfs4fs_compound *cp,
+					   struct nfs4fs_file_data *fp);
+extern int nfs4fs_handle_putrootfh(struct nfs4fs_compound *cp);
+extern int nfs4fs_handle_read_simple(struct nfs4fs_compound *cp, u32 *bytes_read, int *eof);
+extern int nfs4fs_handle_read_complex(struct nfs4fs_compound *cp);
+extern int nfs4fs_handle_readdir(struct nfs4fs_compound *cp, struct inode *inode, u64 *cookiep,
+				 verifier4 verf, u32 *bytes_read, int *eof, int *try_again);
+extern int nfs4fs_handle_remove(struct nfs4fs_compound *cp,
+				struct inode *pinode, struct dentry *dentry);
+extern int nfs4fs_handle_rename(struct nfs4fs_compound *cp,
+				struct dentry *src, struct dentry *dst);
+extern int nfs4fs_handle_secinfo(struct nfs4fs_compound *cp);
+extern int nfs4fs_handle_setclientid(struct nfs4fs_compound *cp, struct nfs4fs_client *clp);
+extern int nfs4fs_handle_write_simple(struct nfs4fs_compound *, struct inode *, u32 *);
+extern int nfs4fs_handle_write_complex(struct nfs4fs_compound *, struct inode *);
+
+#define nfs4fs_handle_close          nfs4fs_handle_simple_op
+#define nfs4fs_handle_delegreturn    nfs4fs_handle_simple_op
+#define nfs4fs_handle_locku          nfs4fs_handle_simple_op
+#define nfs4fs_handle_open_confirm   nfs4fs_handle_simple_op
+#define nfs4fs_handle_putfh	     nfs4fs_handle_simple_op
+#define nfs4fs_handle_readlink       nfs4fs_handle_simple_op
+#define nfs4fs_handle_renew          nfs4fs_handle_simple_op
+#define nfs4fs_handle_restorefh      nfs4fs_handle_simple_op
+#define nfs4fs_handle_savefh         nfs4fs_handle_simple_op
+#define nfs4fs_handle_setattr        nfs4fs_handle_simple_op
+#define nfs4fs_handle_setclientid_confirm        nfs4fs_handle_simple_op
+
+/* inode.c */
+extern int nfs4_revalidate_inode(struct inode *inode);
+extern int nfs4_revalidate(struct dentry *dentry);
+extern int nfs4_setattr(struct dentry *dentry, struct iattr *iap);
+extern int nfs4_do_secinfo(struct super_block *sb, struct dentry *dentry);
+extern int nfs4_do_setclientid(struct nfs4fs_compound *cp, struct super_block *sb);
+
+/* lock.c */
+extern int nfs4_lock(struct file *filp, int cmd, struct file_lock *file_lock);
+
+/* open.c */
+extern int nfs4fs_do_open(struct dentry **, int flags, int mode, struct nfs4fs_file_data **);
+extern int nfs4fs_do_open_exact(struct dentry *, int flags,
+				int mode, struct nfs4fs_file_data **);
+extern int nfs4_create(struct inode *dir_inode, struct dentry *dentry, int mode);
+extern int nfs4_lookup_open(struct inode *dir_inode,
+			    struct dentry **dentryp, struct nameidata *nd);
+extern int nfs4_open(struct inode *inode, struct file *filp);
+extern int nfs4_release(struct inode *inode, struct file *filp);
+
+/* proc.c */
+extern int nfs4fs_call_compound(struct nfs4fs_compound *cp);
+extern int nfs4fs_call_async(struct nfs4fs_compound *cp);
+extern void nfs4fs_sleep_async(struct rpc_wait_queue *q, struct nfs4fs_compound *cp);
+extern void nfs4fs_release_compound(struct nfs4fs_compound *cp);
+extern struct rpc_program            nfs4_program;
+extern struct rpc_stat               nfs4_rpcstat;
+
+/* read.c */
+extern int nfs4_readpage(struct file *filp, struct page *page);
+extern int nfs4_pagein_inode(struct inode *inode, unsigned long idx_start,
+			     unsigned int npages);
+extern int nfs4_pagein_timeout(struct inode *inode);
+
+/* renewd.c */
+extern int nfs4fs_init_renewd(struct super_block *sb);
+
+/* setup.c */
+extern void nfs4fs_setup_compound(struct nfs4fs_compound *cp,
+				  struct super_block *sb, char *tag);
+extern int nfs4fs_setup_async(struct nfs4fs_compound **acpp, struct super_block *sb,
+			      char *tag, struct nfs4fs_compound *parent,
+			      nfs4_action exit, nfs4_action release);
+extern void nfs4fs_setup_simple_op(struct nfs4fs_compound *cp, int opnum);
+extern void nfs4fs_setup_access(struct nfs4fs_compound *cp, unsigned int req_access);
+extern void nfs4fs_setup_close(struct nfs4fs_compound *cp, struct nfs4fs_file_data *fp);
+extern void nfs4fs_setup_commit(struct nfs4fs_compound *cp, struct inode *inode,
+				struct list_head *pages);
+extern void nfs4fs_setup_create_dir(struct nfs4fs_compound *cp,
+				    struct dentry *dentry, int mode);
+extern void nfs4fs_setup_create_symlink(struct nfs4fs_compound *cp,
+					struct dentry *dentry, const char *linktext);
+extern void nfs4fs_setup_create_special(struct nfs4fs_compound *cp,
+					struct dentry *dentry, int mode, int dev);
+extern void nfs4fs_setup_delegreturn(struct nfs4fs_compound *cp, stateid4 stateid);
+extern void nfs4fs_setup_getattr(struct nfs4fs_compound *cp, u32 *bitmap);
+extern void nfs4fs_setup_getfh(struct nfs4fs_compound *cp);
+extern void nfs4fs_setup_link(struct nfs4fs_compound *cp, struct dentry *dentry);
+extern void nfs4fs_setup_lock(struct nfs4fs_compound *cp, u32 type, u64 start, u64 end,
+			      struct nfs4fs_file_data *lock_stateid);
+extern void nfs4fs_setup_lockt(struct nfs4fs_compound *cp, struct file_lock *flock,
+			       struct nfs4fs_lockowner *lock_owner);
+extern void nfs4fs_setup_locku(struct nfs4fs_compound *cp, u32 type, u64 start, u64 end,
+			       struct nfs4fs_file_data *lock_stateid);
+extern void nfs4fs_setup_lookup(struct nfs4fs_compound *cp, struct qstr *name);
+extern void nfs4fs_setup_open(struct nfs4fs_compound *cp, struct dentry *dentry,
+			      int flags, int mode, struct nfs4fs_file_data *fp);
+extern void nfs4fs_setup_undelegating_open(struct nfs4fs_compound *cp,
+					   struct nfs4fs_file_data *fp);
+extern void nfs4fs_setup_open_confirm(struct nfs4fs_compound *cp,
+				      struct nfs4fs_compound *parent);
+extern void nfs4fs_setup_putfh(struct nfs4fs_compound *cp, struct inode *inode);
+extern void nfs4fs_setup_putfh_simple(struct nfs4fs_compound *cp, char *val, unsigned int len);
+extern void nfs4fs_setup_putrootfh(struct nfs4fs_compound *cp, struct super_block *sb);
+extern void nfs4fs_setup_read_simple(struct nfs4fs_compound *cp, loff_t start, u32 length,
+				     char *buf, struct nfs4fs_file_data *stateid);
+extern void nfs4fs_setup_read_complex(struct nfs4fs_compound *cp, struct list_head *pages);
+extern void nfs4fs_setup_readdir(struct nfs4fs_compound *cp, u64 cookie, verifier4 verifier,
+				 unsigned int npages, struct page **pages, u32 initial_offset);
+extern void nfs4fs_setup_readlink(struct nfs4fs_compound *cp, char *buf, int len);
+extern void nfs4fs_setup_remove(struct nfs4fs_compound *cp, struct qstr *name);
+extern void nfs4fs_setup_rename(struct nfs4fs_compound *cp,
+				struct dentry *old, struct dentry *new);
+extern void nfs4fs_setup_renew(struct nfs4fs_compound *cp);
+extern void nfs4fs_setup_secinfo(struct nfs4fs_compound *cp,
+				 struct dentry *dentry);
+extern void nfs4fs_setup_setattr(struct nfs4fs_compound *cp, struct iattr *iap,
+				 struct nfs4fs_file_data *fdata);
+extern void nfs4fs_setup_setclientid(struct nfs4fs_compound *cp,
+				     u32 program, unsigned short port);
+extern void nfs4fs_setup_setclientid_confirm(struct nfs4fs_compound *cp,
+					     struct nfs4fs_client *clp);
+extern void nfs4fs_setup_write_simple(struct nfs4fs_compound *cp, loff_t start, u32 length,
+				      char *buf, struct nfs4fs_file_data *stateid);
+extern void nfs4fs_setup_write_complex(struct nfs4fs_compound *, struct list_head *, int how);
+
+#define nfs4fs_setup_restorefh(cp)   nfs4fs_setup_simple_op(cp, OP_RESTOREFH)
+#define nfs4fs_setup_savefh(cp)      nfs4fs_setup_simple_op(cp, OP_SAVEFH)
+
+extern u32 nfs4_standard_bitmap[2];
+
+/* silly.c */
+extern int nfs4_sillyrename(struct inode *dir, struct dentry *dentry);
+extern void nfs4_finish_sillyrename(struct dentry *dentry);
+
+/* state.c */
+extern void nfs4fs_init_state(void);
+extern void nfs4fs_cleanup_state(void);
+extern struct nfs4fs_client *nfs4fs_get_client(void);
+extern void nfs4fs_put_client(struct nfs4fs_client *clp);
+extern struct nfs4fs_lockowner *nfs4fs_get_lock_owner(struct inode *inode);
+extern void nfs4fs_put_lockowner(struct nfs4fs_lockowner *lp);
+extern pid_t nfs4fs_id_to_pid(unsigned int id);
+extern struct nfs4fs_file_data *nfs4fs_get_open_stateid(struct super_block *sb,
+							struct inode *inode, int open_flags);
+extern struct nfs4fs_file_data *nfs4fs_get_read_stateid(struct file *f, struct inode *inode);
+extern struct nfs4fs_file_data *nfs4fs_get_write_stateid(struct file *f, struct inode *inode);
+extern struct nfs4fs_file_data *nfs4fs_get_lock_stateid(struct file *,
+							struct nfs4fs_compound *);
+extern struct nfs4fs_file_data *nfs4fs_get_locku_stateid(struct file *f);
+extern void nfs4fs_instantiate_open(struct nfs4fs_file_data *fp);
+extern void nfs4fs_put_file_data(struct nfs4fs_file_data *fp);
+extern void nfs4fs_accept_delegation(struct dentry *dentry, struct inode *inode,
+				     u32 type, stateid4 new_stateid);
+extern struct nfs4fs_delegation *nfs4fs_find_delegation_by_stateid(struct super_block *sb,
+								   stateid4 stateid);
+extern void nfs4fs_killall_placeholders(struct super_block *sb);
+extern void nfs4fs_put_delegation(struct nfs4fs_delegation *dp);
+extern void nfs4fs_drop_delegation_from_inode(struct inode *inode);
+
+/* symlink.c */
+extern struct inode_operations nfs4_symlink_inode_operations;
+
+/* write.c */
+extern int nfs4_writepage(struct page *page);
+extern int nfs4_updatepage(struct file *file, struct page *page,
+			   unsigned int offset, unsigned int count);
+extern int nfs4_flush_incompatible(struct file *file, struct page *page);
+extern int nfs4_flush_timeout(struct inode *inode, int how);
+extern int nfs4_sync_file(struct inode *inode, struct file *file,
+			  unsigned long idx_start, unsigned int npages, int how);
+extern int nfs4_commit_timeout(struct inode *inode, int how);
+
+/* net/sunrpc/gss_union.c */
+extern int gss_cmp_triples(u32 oid_len, char *oid_data, u32 qop, u32 service);
+
+extern void print_hexl(u32 *p, u_int length, u_int offset);
+
+#endif
--- clean/include/linux/nfs4/nfsd4_xdr.h	Thu Feb  7 18:04:30 2002
+++ dirty/include/linux/nfs4/nfsd4_xdr.h	Thu Dec  6 02:17:23 2001
@@ -0,0 +1,592 @@
+/*
+ *  linux/fs/nfs4/nfsd4_xdr.h
+ *
+ *  Copyright (c) 2001 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  Kendrick Smith <kmsmith@umich.edu>
+ *  
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *  
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the University nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _NFSD4_XDR_H
+#define _NFSD4_XDR_H
+
+#ifdef __linux__
+/*
+ * XDR_POS_T is the type of a "descriptor" which can be used to save/restore
+ * positions in an XDR stream.  For Linux, this is just a pointer, since we
+ * just use a linear buffer.  For details, see encode.c.
+ */
+typedef u32 *XDR_POS_T;
+#endif
+
+#define NFSD4_PATH_ISIZE     1
+
+typedef struct {
+	u64		before;
+	u64		after;
+} change_info4;
+
+struct nfsd4_component {
+	u32		len;
+	char *		name;
+};
+
+/* XXX: This, along with NFSD4_PATH_ISIZE, is vestigual; remove? */
+struct nfsd4_pathname {
+	u32			len;
+	struct nfsd4_component *path;
+	struct nfsd4_component	ipath;
+};
+#define nfsd4_pathname_release(pathname)  do {		\
+	if ((pathname)->path != &(pathname)->ipath) {	\
+		NFS4_FREE((pathname)->path);		\
+		(pathname)->path = &(pathname)->ipath;	\
+	}						\
+} while (0)
+
+struct nfsd4_lock_denied {
+	struct nfs4_lockowner	*lp;
+	u64		start;
+	u64		length;
+	u32		type;
+};
+
+struct nfsd4_secflavor {
+	u32		flavor;   /* AUTH_{SYS,NONE,RPCSEC_GSS} */
+
+	/* remaining fields are used only if flavor==RPCSEC_GSS */
+	u32		oid_len;
+	char *		oid_val;  /* contains ASN.1 OBJECT IDENTIFIER */
+	u32		qop;
+	u32		service;  /* RPC_GSS_SVC_{NONE,INTEGRITY,PRIVACY} */
+};
+
+/*
+ * Here are the structures which are used to represent the request and
+ * response of an individual NFSv4 op.
+ */
+struct nfsd4_access {
+	/* request */
+	u32		ac_req_access;
+	
+	/* response */
+	u32		ac_supported;
+	u32		ac_resp_access;
+};
+
+struct nfsd4_close {
+	/* request */
+	u32		cl_seqid;
+	stateid_t	cl_stateid;    /* also used in response */
+
+	struct nfs4_lockowner *cl_lockowner;
+};
+
+struct nfsd4_commit {
+	/* request */
+	u64		co_offset;
+	u32		co_count;
+
+	/* response */
+	verifier4	co_verf;
+};
+
+struct nfsd4_create {
+	/* request */
+	u32		cr_namelen;
+	char *		cr_name;
+	u32		cr_type;
+	union {
+		struct {
+			u32 namelen;
+			char *name;
+		} link;
+		struct {
+			u32 specdata1;
+			u32 specdata2;
+		} dev;
+	} u;
+	u32		cr_bmval[2];
+	struct iattr	cr_iattr;
+
+	/* response */
+	change_info4	cr_cinfo;
+};
+#define cr_linklen	u.link.namelen
+#define cr_linkname	u.link.name
+#define cr_specdata1	u.dev.specdata1
+#define cr_specdata2	u.dev.specdata2
+
+struct nfsd4_getattr {
+	u32		ga_bmval[2];
+	struct nfs4_export	*ga_export;
+	struct dentry	*ga_dentry;
+};
+
+struct nfsd4_link {
+	/* request */
+	u32		li_namelen;
+	char *		li_name;
+
+	/* response */
+	change_info4	li_cinfo;
+};
+
+struct nfsd4_lock {
+	/* request */
+	u32		lk_type;
+	u32		lk_reclaim;         /* boolean */
+	u64		lk_offset;
+	u64		lk_length;
+	u32		lk_is_new;
+	union {
+		struct {
+			u32		open_seqid;
+			stateid_t	open_stateid;
+			u32		lock_seqid;
+			clientid_t	clientid;
+			u32		ownerlen;
+			char *		owner;
+		} new;
+		struct {
+			stateid_t	lock_stateid;
+			u32		lock_seqid;
+		} old;
+	} v;
+
+	/* response */
+	union {
+		struct {
+			u32			rflags;
+			stateid_t		stateid;
+		} ok;
+		struct nfsd4_lock_denied	denied;
+	} u;
+	
+	struct nfs4_lockowner *lk_owner;
+};
+#define lk_new_open_seqid	v.new.open_seqid
+#define lk_new_open_stateid	v.new.open_stateid
+#define lk_new_lock_seqid	v.new.lock_seqid
+#define lk_new_clientid		v.new.clientid
+#define lk_new_ownerlen		v.new.ownerlen
+#define lk_new_owner		v.new.owner
+#define lk_old_lock_stateid	v.old.lock_stateid
+#define lk_old_lock_seqid	v.old.lock_seqid
+
+#define lk_rflags	u.ok.rflags
+#define lk_resp_stateid	u.ok.stateid
+#define lk_denied	u.denied
+
+struct nfsd4_lockt {
+	u32		lt_type;
+	clientid_t	lt_clientid;
+	u32		lt_ownerlen;
+	char *		lt_ownername;
+	u64		lt_offset;
+	u64		lt_length;
+
+	struct nfs4_lockowner *lt_lockowner;
+	struct nfsd4_lock_denied  lt_denied;
+};
+
+struct nfsd4_locku {
+	u32		lu_type;
+	u32		lu_seqid;
+	stateid_t	lu_stateid;
+	u64		lu_offset;
+	u64		lu_length;
+
+	struct nfs4_lockowner  *lu_lockowner;
+};
+
+struct nfsd4_open {
+	/* request */
+	u32		op_claim_type;
+	struct nfsd4_component	op_pathname;	/* CLAIM_{NULL,DELEGATE_CUR,DELEGATE_PREV} */
+	u32		op_delegate_type;	/* CLAIM_PREVIOUS(?) */
+	delegation_stateid_t	op_delegate_stateid;	/* CLAIM_DELEGATE_CUR */
+	
+	u32		op_create;     		/* boolean */
+	u32		op_createmode;	/* either UNCHECKED4, GUARDED4, or EXCLUSIVE4 */
+	u32		op_bmval[2];
+	union {
+		struct iattr	iattr;		/* UNCHECKED4,GUARDED4 */
+		verifier4	verf;		/* EXCLUSIVE4 */
+	} u;
+
+	clientid_t	op_clientid;
+	u32		op_ownerlen;
+	char *		op_owner;
+	u32		op_seqid;
+	u32		op_share_access;
+	u32		op_share_deny;
+
+	struct nfs4_lockowner	*op_lockowner;
+	int		op_lockowner_old;
+	int		op_truncate;
+	
+	/* response */
+	stateid_t	op_stateid;
+	change_info4	op_cinfo;
+	u32		op_rflags;
+};
+#define op_iattr	u.iattr
+#define op_verf		u.verf
+
+struct nfsd4_open_confirm {
+	/* request */
+	stateid_t		oc_req_stateid;
+	u32			oc_seqid;
+
+	/* response */
+	stateid_t		oc_resp_stateid;
+	struct nfs4_lockowner *	oc_lockowner;
+};
+
+struct nfsd4_open_downgrade {
+	stateid_t	od_stateid;
+	u32		od_seqid;
+	u32		od_share_access;
+	u32		od_share_deny;
+
+	struct nfs4_lockowner *od_lockowner;
+};
+
+struct nfsd4_putfh {
+	u32		pf_fhlen;
+	char		*pf_fhval;
+};
+
+struct nfsd4_read {
+	/* request */
+	stateid_t	rd_stateid;
+	u64		rd_offset;
+	u32		rd_length;
+
+	/* response */
+	struct file	*rd_file;
+};
+
+struct nfsd4_readlink {
+	struct dentry	*rl_dentry;
+	struct inode	*rl_inode;
+};
+
+
+struct nfsd4_readdir {
+	u64		rd_cookie;
+	verifier4	rd_verf;
+	u32		rd_dircount;
+	u32		rd_maxcount;
+	u32		rd_bmval[2];
+
+	u32 *		rd_savep;
+	XDR_POS_T	rd_pos;
+};
+
+
+struct nfsd4_remove {
+	/* request */
+	u32		rm_namelen;
+	char *		rm_name;
+
+	/* response */
+	change_info4	rm_cinfo;
+};
+
+struct nfsd4_rename {
+	/* request */
+	u32		rn_snamelen;
+	char *		rn_sname;
+	u32		rn_tnamelen;
+	char *		rn_tname;
+
+	/* response */
+	change_info4	rn_sinfo;
+	change_info4	rn_tinfo;
+};
+
+struct nfsd4_secinfo {
+	u32		sc_namelen;
+	char *		sc_name;
+
+	u32		sc_nflavors;
+	struct nfsd4_secflavor *sc_flavors;
+};
+
+struct nfsd4_setattr {
+	stateid_t	sa_stateid;
+	u32		sa_bmval[2];
+	struct iattr	sa_iattr;
+};
+
+struct nfsd4_setclientid {
+	verifier4	se_verf;
+	u32		se_namelen;
+	char *		se_name;
+	u32		se_callback_prog;
+	u32		se_callback_netid_len;
+	char *		se_callback_netid_val;
+	u32		se_callback_addr_len;
+	char *		se_callback_addr_val;
+
+	clientid_t	se_clientid;
+};
+
+struct nfsd4_verify {
+	u32		ve_bmval[2];
+	u32		ve_attrlen;
+	char *		ve_attrval;
+};
+
+struct nfsd4_write {
+	stateid_t	wr_stateid;
+	u64		wr_offset;
+	u32		wr_stable_how;
+	u32		wr_buflen;
+	char *		wr_buf;
+
+	u32		wr_bytes_written;
+	u32		wr_how_written;
+	verifier4	wr_verifier;
+};
+
+#if 0
+struct nfsd4_lock_confirm {
+	/* request */
+	stateid_t		lc_req_stateid;
+	u32			lc_seqid;
+
+	/* response */
+	stateid_t		lc_resp_stateid;
+	struct nfs4_lockowner *	lc_lockowner;
+};
+#endif
+
+/*
+ * needed for readdir
+ */
+struct nfsd4_readdir_desc {
+	struct global_state	*stp;
+	struct nfsd4_getattr	getattr;
+	u32			buflen;
+	u32 *			old_cookie;
+	u32			eof;
+	u32			status;
+	u32			flag;
+};
+
+/*
+ * XDR decode routines, defined in decode.c
+ */
+extern int nfsd4_decode_compound(struct global_state *);
+extern int nfsd4_decode_word(struct global_state *, u32 *);
+extern int nfsd4_decode_access(struct global_state *, struct nfsd4_access *);
+extern int nfsd4_decode_close(struct global_state *, struct nfsd4_close *);
+extern int nfsd4_decode_commit(struct global_state *, struct nfsd4_commit *);
+extern int nfsd4_decode_create(struct global_state *, struct nfsd4_create *);
+extern int nfsd4_decode_delegpurge(struct global_state *, clientid_t *);
+extern int nfsd4_decode_delegreturn(struct global_state *, delegation_stateid_t *);
+extern int nfsd4_decode_getattr(struct global_state *, struct nfsd4_getattr *);
+extern int nfsd4_decode_link(struct global_state *, struct nfsd4_link *);
+extern int nfsd4_decode_lock(struct global_state *, struct nfsd4_lock *);
+extern int nfsd4_decode_lockt(struct global_state *, struct nfsd4_lockt *);
+extern int nfsd4_decode_locku(struct global_state *, struct nfsd4_locku *);
+extern int nfsd4_decode_lookup(struct global_state *, struct nfsd4_component *);
+extern int nfsd4_decode_open(struct global_state *, struct nfsd4_open *);
+extern int nfsd4_decode_open_confirm(struct global_state *, struct nfsd4_open_confirm *);
+extern int nfsd4_decode_open_downgrade(struct global_state *, struct nfsd4_open_downgrade *);
+extern int nfsd4_decode_putfh(struct global_state *, struct nfsd4_putfh *);
+extern int nfsd4_decode_read(struct global_state *, struct nfsd4_read *);
+extern int nfsd4_decode_readdir(struct global_state *, struct nfsd4_readdir *);
+extern int nfsd4_decode_remove(struct global_state *, struct nfsd4_remove *);
+extern int nfsd4_decode_rename(struct global_state *, struct nfsd4_rename *);
+extern int nfsd4_decode_renew(struct global_state *, clientid_t *);
+extern int nfsd4_decode_secinfo(struct global_state *, struct nfsd4_secinfo *);
+extern int nfsd4_decode_setattr(struct global_state *, struct nfsd4_setattr *);
+extern int nfsd4_decode_setclientid(struct global_state *, struct nfsd4_setclientid *);
+extern int nfsd4_decode_setclientid_confirm(struct global_state *, clientid_t *);
+extern int nfsd4_decode_verify(struct global_state *, struct nfsd4_verify *);
+extern int nfsd4_decode_write(struct global_state *, struct nfsd4_write *);
+
+#define nfsd4_decode_nverify     nfsd4_decode_verify
+
+/*
+ * Routines to process individual ops, defined in vfs.c or state.c
+ */
+extern int nfsd4_access(struct global_state *, struct nfsd4_access *);
+extern int nfsd4_close(struct global_state *, struct nfsd4_close *);
+extern int nfsd4_commit(struct global_state *, struct nfsd4_commit *);
+extern int nfsd4_create(struct global_state *, struct nfsd4_create *);
+extern int nfsd4_delegpurge(struct global_state *, clientid_t *);
+extern int nfsd4_delegreturn(struct global_state *, delegation_stateid_t *);
+extern int nfsd4_getattr(struct global_state *, struct nfsd4_getattr *);
+extern int nfsd4_getfh(struct global_state *);
+extern int nfsd4_link(struct global_state *, struct nfsd4_link *);
+extern int nfsd4_lock(struct global_state *, struct nfsd4_lock *);
+extern int nfsd4_lockt(struct global_state *, struct nfsd4_lockt *);
+extern int nfsd4_locku(struct global_state *, struct nfsd4_locku *);
+extern int nfsd4_lookup(struct global_state *, struct nfsd4_component *);
+extern int nfsd4_lookupp(struct global_state *);
+extern int nfsd4_nverify(struct global_state *, struct nfsd4_verify *);
+extern int nfsd4_open(struct global_state *, struct nfsd4_open *);
+extern int nfsd4_openattr(struct global_state *);
+extern int nfsd4_open_confirm(struct global_state *, struct nfsd4_open_confirm *);
+extern int nfsd4_open_downgrade(struct global_state *, struct nfsd4_open_downgrade *);
+extern int nfsd4_putfh(struct global_state *, struct nfsd4_putfh *);
+extern int nfsd4_putpubfh(struct global_state *);
+extern int nfsd4_putrootfh(struct global_state *);
+extern int nfsd4_read(struct global_state *, struct nfsd4_read *);
+extern int nfsd4_readdir(struct global_state *, struct nfsd4_readdir *);
+extern int nfsd4_readlink(struct global_state *, struct nfsd4_readlink *);
+extern int nfsd4_remove(struct global_state *, struct nfsd4_remove *);
+extern int nfsd4_rename(struct global_state *, struct nfsd4_rename *);
+extern int nfsd4_renew(struct global_state *, clientid_t *);
+extern int nfsd4_restorefh(struct global_state *);
+extern int nfsd4_savefh(struct global_state *);
+extern int nfsd4_secinfo(struct global_state *, struct nfsd4_secinfo *);
+extern int nfsd4_setattr(struct global_state *, struct nfsd4_setattr *);
+extern int nfsd4_setclientid(struct global_state *, struct nfsd4_setclientid *);
+extern int nfsd4_setclientid_confirm(struct global_state *, clientid_t *);
+extern int nfsd4_verify(struct global_state *, struct nfsd4_verify *);
+extern int nfsd4_write(struct global_state *, struct nfsd4_write *);
+
+/*
+ * XDR encode routines, defined in encode.c
+ * The following ops have null args, and therefore no decode routine:
+ *    GETFH, LOOKUPP, OPENATTR, PUTPUBFH, PUTROOTFH, RESTOREFH, SAVEFH.
+ */
+
+extern int nfsd4_check_space(struct global_state *, unsigned int nbytes);
+extern int nfsd4_encode_dirent(struct nfsd4_readdir_desc *, u32 namelen, const char *name);
+extern int nfsd4_encode_compound(struct global_state *);
+extern int nfsd4_encode_simple_op(struct global_state *, int status, int opnum);
+extern int nfsd4_encode_replay(struct global_state *, int opnum, struct nfs4_lockowner *lp);
+extern void nfsd4_encode_failed_op(struct global_state *, int opnum, int nfserr);
+
+extern int nfsd4_encode_access(struct global_state *, int, struct nfsd4_access *);
+extern int nfsd4_encode_close(struct global_state *, int, struct nfsd4_close *);
+extern int nfsd4_encode_commit(struct global_state *, int, struct nfsd4_commit *);
+extern int nfsd4_encode_create(struct global_state *, int, struct nfsd4_create *);
+extern int nfsd4_encode_getattr(struct global_state *, int, struct nfsd4_getattr *);
+extern int nfsd4_encode_getfh(struct global_state *, int);
+extern int nfsd4_encode_link(struct global_state *, int, struct nfsd4_link *link);
+extern int nfsd4_encode_lock(struct global_state *, int, struct nfsd4_lock *lock);
+extern int nfsd4_encode_lockt(struct global_state *, int, struct nfsd4_lockt *lockt);
+extern int nfsd4_encode_locku(struct global_state *, int, struct nfsd4_locku *locku);
+extern int nfsd4_encode_open(struct global_state *, int, struct nfsd4_open *open);
+extern int nfsd4_encode_open_confirm(struct global_state *, int, struct nfsd4_open_confirm *);
+extern int nfsd4_encode_open_downgrade(struct global_state *, int,
+				       struct nfsd4_open_downgrade *);
+extern int nfsd4_encode_read(struct global_state *, int, struct nfsd4_read *);
+extern int nfsd4_start_encode_readdir(struct global_state *, struct nfsd4_readdir *);
+extern int nfsd4_finish_encode_readdir(struct global_state *, int,
+				       struct nfsd4_readdir *rd, int eof);
+extern int nfsd4_encode_readlink(struct global_state *, int, struct nfsd4_readlink *);
+extern int nfsd4_encode_remove(struct global_state *, int, struct nfsd4_remove *);
+extern int nfsd4_encode_rename(struct global_state *, int, struct nfsd4_rename *);
+extern int nfsd4_encode_secinfo(struct global_state *, int, struct nfsd4_secinfo *);
+extern int nfsd4_encode_setattr(struct global_state *, int, struct nfsd4_setattr *);
+extern int nfsd4_encode_setclientid(struct global_state *, int, struct nfsd4_setclientid *);
+extern int nfsd4_encode_write(struct global_state *, int, struct nfsd4_write *write);
+
+#define nfsd4_encode_delegpurge(stp,status)    \
+	nfsd4_encode_simple_op((stp), (status), OP_DELEGPURGE)
+#define nfsd4_encode_delegreturn(stp,status)    \
+	nfsd4_encode_simple_op((stp), (status), OP_DELEGRETURN)
+#define nfsd4_encode_lookup(stp,status)    \
+	nfsd4_encode_simple_op((stp), (status), OP_LOOKUP)
+#define nfsd4_encode_lookupp(stp,status)   \
+	nfsd4_encode_simple_op((stp), (status), OP_LOOKUPP)
+#define nfsd4_encode_nverify(stp,status)   \
+	nfsd4_encode_simple_op((stp), (status), OP_NVERIFY)
+#define nfsd4_encode_openattr(stp,status)  \
+	nfsd4_encode_simple_op((stp), (status), OP_OPENATTR)
+#define nfsd4_encode_putfh(stp,status)  \
+	nfsd4_encode_simple_op((stp), (status), OP_PUTFH)
+#define nfsd4_encode_putpubfh(stp,status)  \
+	nfsd4_encode_simple_op((stp), (status), OP_PUTPUBFH)
+#define nfsd4_encode_putrootfh(stp,status) \
+	nfsd4_encode_simple_op((stp), (status), OP_PUTROOTFH)
+#define nfsd4_encode_renew(stp,status)  \
+	nfsd4_encode_simple_op((stp), (status), OP_RENEW)
+#define nfsd4_encode_restorefh(stp,status)  \
+	nfsd4_encode_simple_op((stp), (status), OP_RESTOREFH)
+#define nfsd4_encode_savefh(stp,status)  \
+	nfsd4_encode_simple_op((stp), (status), OP_SAVEFH)
+#define nfsd4_encode_setclientid_confirm(stp,status) \
+	nfsd4_encode_simple_op((stp), (status), OP_SETCLIENTID_CONFIRM)
+#define nfsd4_encode_verify(stp,status)    \
+	nfsd4_encode_simple_op((stp), (status), OP_VERIFY)
+
+
+/*
+ * _PADDING constants.
+ * TODO: Write a comment which carefully explains the roles of each of these!
+ */
+
+#define COMPOUND_PADDING	20
+#define READDIR_PADDING		 8
+
+/*
+ * A CLOSE response requires:
+ *   4(nfs_resop) + 4(status) + sizeof(stateid_t) = sizeof(stateid_t) + 8   bytes.
+ * LOCKU, OPEN_CONFIRM, OPEN_DOWNGRADE all have the same space requirement.
+ */
+#define CLOSE_PADDING		(sizeof(stateid_t) + 8)
+#define LOCKU_PADDING		(sizeof(stateid_t) + 8)
+#define OPEN_CONFIRM_PADDING	(sizeof(stateid_t) + 8)
+#define OPEN_DOWNGRADE_PADDING	(sizeof(stateid_t) + 8)
+
+/*
+ * A denied LOCK response requires at least
+ * 4(nfs_resop) + 4(status) + 16(owner) + 8(offset) + 8(length) + 4(type) = 44 bytes.
+ */
+#define LOCK_PADDING		44
+
+/*
+ * An OPEN response requires at least
+ *   4(nfs_resop) + 4(status) + sizeof(stateid_t) + 20(change_info) + 4(flags)
+ *   + 8(verf) + 4(delegation type)      = sizeof(stateid_t) + 44 bytes.
+ */
+#define OPEN_PADDING		(sizeof(stateid_t) + 44)
+
+/*
+ * An OPEN response with a delegation requires at least
+ *   4(nfs_resop) + 4(status) + sizeof(stateid_t) + 20(change_info) + 4(flags) + 8(verf)
+ *   + 4(delegation type) + sizeof(delegation_stateid_t) + 4(recall) + 12(space_limit)
+ *   + 16(ace) = sizeof(stateid_t) + sizeof(delegation_stateid_t) + 76  bytes.
+ */
+#define DELEGATED_OPEN_PADDING  (sizeof(stateid_t) + sizeof(delegation_stateid_t) + 76)
+
+/* For DELEGRETURN, only a status is needed... */
+#define DELEGRETURN_PADDING	4
+
+#endif
--- clean/fs/nfs4/gss.c	Thu Feb  7 18:04:30 2002
+++ dirty/fs/nfs4/gss.c	Mon Feb  4 18:46:42 2002
@@ -0,0 +1,434 @@
+/*
+ *  linux/fs/nfs4/gss.c
+ *
+ *  Copyright (c) 2000 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  Kendrick Smith <kmsmith@umich.edu>
+ *  Andy Adamson   <andros@umich.edu>
+ *  Dug Song       <dugsong@monkey.org>
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the University nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * TODO: Audit this for bugs!  (It has not yet received any auditing at all.)
+ * TODO: Maybe this should be moved somewhere in net/sunrpc so that subsystems
+ *       other than nfsv4 could use it?
+ */
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <linux/nfs4/nfs4.h>
+#include <linux/nfs4/nfs4_debug.h>
+#include <linux/sunrpc/xdr.h>
+
+#define GSS_HASH_BITS	8
+#define GSS_HASH_SIZE	(1 << GSS_HASH_BITS)
+#define GSS_HASH_MASK	(GSS_HASH_SIZE - 1)
+
+/* XXX : the locking here is based on the assumption that kmalloc() and kfree() can both
+   block.. make sure that this is correct! */
+
+struct gss_mapping {
+	/* we have a separate lock for each mapping.  we could even have a seperate lock for
+	   each hash chain, but this seems a little excessive! */
+	rwlock_t		lock;
+	struct list_head	id_hash[GSS_HASH_SIZE];
+	struct list_head	name_hash[GSS_HASH_SIZE];
+};
+
+static struct gss_mapping	owner_map;
+static struct gss_mapping	group_map;
+
+static struct gss_cacheent *gss_alloc(unsigned int namelen);
+
+#if NFS4_DEBUG
+static char *
+gss_opcode(int type)
+{
+	switch (type) {
+	case GSS_OWNER:
+		return "GSS_OWNER";
+	case GSS_GROUP:
+		return "GSS_GROUP";
+	default:
+		return "GSS_BAD";
+	}
+}
+#endif
+
+static int
+__gss_get_num(int type, unsigned int namelen, const unsigned char *name, uid_t *idp)
+{
+	int status;
+	struct xdr_netobj *x;
+
+	nfsv4_printk(gss, "__gss_get_num\n");
+
+	if (!namelen) {
+		nfsv4_printk(gss, "__gss_get_num: empty string!\n");
+#if 0
+		status = -EINVAL;
+#else
+		/* XXX: bakeoff hack, catering to Windows 2000! */
+		*idp = 99;   /* "nobody" */
+		status = 0;
+#endif
+		goto out;
+	}
+
+	nfsv4_printk(gss, "__gss_get_num. name %s, namelen %d\n",name,namelen);
+
+	if (namelen > GSS_NAME_MAX) {
+		nfsv4_printk(gss, "__gss_get_num: name too long!\n");
+		status = -EINVAL;
+		goto out;
+	}
+	/* copy string into rpc buffer */
+	
+	nfsv4_printk(gss, "__gss_get_num alloc x.data\n");
+
+	if (!(x = NFS4_ALLOC(sizeof(struct xdr_netobj)))) {
+		status = -ENOMEM;
+		goto out;	
+	}
+	x->len = namelen;
+	x->data = (char *)name;   /* cast is to get rid of "const" */
+
+	nfsv4_printk(gss, "__gss_get_num making upcall \n");
+	if (type == GSS_OWNER) {
+		nfsv4_printk(gss, "__gss_get_num: gss_name_to_uid\n");
+		if (gssd_name_to_uid(x, idp) < 0)
+			goto fail;
+	}
+	else if (type == GSS_GROUP) {
+		nfsv4_printk(gss, "__gss_get_num: gss_name_to_gid\n");
+		if (gssd_name_to_gid(x, idp) < 0)
+			goto fail;
+	} 
+	nfsv4_printk(gss, "__gss_get_num: returned from gssd\n");
+	/* Once the id has been determined, this function is responsible for setting *idp. */
+	nfsv4_printk(gss, "__gss_get_num: set id=%d\n", *idp);
+	status = 0;
+	NFS4_FREE(x);
+
+out:
+	nfsv4_printk(gss, "__gss_get_num: returning status %d\n", status);
+	return status;
+fail:
+	printk(KERN_INFO "nfsd: gss_get_num() failed!\n");
+	printk(KERN_INFO "nfsd: maybe gssd is not running?\n");
+	status = -EIO;
+	goto out;
+}
+
+static int
+__gss_get_name(int type, uid_t id, struct gss_cacheent **pp)
+{
+	struct gss_cacheent *p = NULL;
+	struct xdr_netobj x;
+	int status = 0;
+
+	nfsv4_printk(gss, "__gss_get_name\n");
+	
+	if (type == GSS_OWNER) {
+		nfsv4_printk(gss, "__gss_get_name: gssd_uid_to_name\n");
+		if (gssd_uid_to_name(&id, &x) < 0)
+			goto fail;
+	}
+	else if (type == GSS_GROUP) {
+		nfsv4_printk(gss, "__gss_get_name: gssd_gid_to_name\n");
+		if (gssd_gid_to_name(&id, &x) < 0)
+			goto fail;
+	}
+	if (!(p = NFS4_ALLOC(sizeof(struct gss_cacheent)))) {
+		status = -ENOMEM;
+		goto out;
+	}
+	p->name.len = x.len;
+	p->name.name = (char *)x.data;
+	*pp = p;
+	nfsv4_printk(gss, "__gss_get_name: set name=\"%.*s\"\n", (int)p->name.len,
+		     p->name.name);
+out:
+	nfsv4_printk(gss, "__gss_get_name: returning status %d\n", status);
+	return status;
+fail:
+	printk(KERN_INFO "nfsd: gss_get_name() failed!\n");
+	printk(KERN_INFO "nfsd: maybe gssd is not running?\n");
+	status = -EIO;
+	goto out;
+}
+
+void
+gss_init(void)
+{
+	int i;
+
+	rwlock_init(&owner_map.lock);
+	rwlock_init(&group_map.lock);
+	for (i = 0; i < GSS_HASH_SIZE; i++) {
+		INIT_LIST_HEAD(&owner_map.id_hash[i]);
+		INIT_LIST_HEAD(&owner_map.name_hash[i]);
+		INIT_LIST_HEAD(&group_map.id_hash[i]);
+		INIT_LIST_HEAD(&group_map.name_hash[i]);
+	}
+}
+
+static struct gss_cacheent *
+gss_alloc(unsigned int len)
+{
+	struct gss_cacheent *p;
+
+	/* caller must not hold spinlock, since this routine can block */
+
+#if NFS4_DEBUG
+	if (len > GSS_NAME_MAX) {
+		nfsv4_printk(gss, "gss_alloc: name length too long!\n");
+		p = NULL;
+		goto out;
+	}
+#endif
+	if (!(p = NFS4_ALLOC(sizeof(struct gss_cacheent))))
+		goto nomem;
+	/* not NFS4_ALLOC(), since name buffers can be allocated externally */
+	if (!(p->name.name = kmalloc(len, GFP_KERNEL))) {
+		NFS4_FREE(p);
+		goto nomem;
+	}
+	nfsv4_printk(gss, "gss_alloc: returning cacheent %p\n", p);
+
+out:
+	return p;
+nomem:
+	nfsv4_printk(gss, "gss_alloc: out of memory!\n");
+	p = NULL;
+	goto out;
+}
+
+void
+gss_free(struct gss_cacheent *p)
+{
+	nfsv4_printk(gss, "gss_free: called on cacheent %p\n", p);
+	
+	/* not NFS4_FREE(), since allocated externally */
+	kfree((char *)p->name.name);  /* cast is to get rid of "const" */
+	NFS4_FREE(p);
+}
+
+static inline void
+gss_insert(struct gss_cacheent *p, struct gss_mapping *map)
+{
+	unsigned int id_hashval = (p->id & GSS_HASH_MASK);
+	unsigned int name_hashval = (p->name.hash & GSS_HASH_MASK);
+
+	/* caller must hold writelock */
+	atomic_set(&p->refcount, 1);    /* refcount==1 indicates in use by the cache */
+	list_add(&p->id_hash, &map->id_hash[id_hashval]);
+	list_add(&p->name_hash, &map->name_hash[name_hashval]);
+}
+
+static void
+gss_clear_map(struct gss_mapping *map)
+{
+	int i;
+	struct list_head *l;
+	struct gss_cacheent *p;
+
+	for (i = 0; i < GSS_HASH_SIZE; i++) {
+		l = &map->id_hash[i];
+		while (!list_empty(l)) {
+			write_lock(&map->lock);
+			p = list_entry(l->next, struct gss_cacheent, id_hash);
+			list_del(&p->id_hash);
+			list_del(&p->name_hash);
+			/* Since gss_put() can block, it can only be called safely if the
+			   spinlock is not held. */
+			write_unlock(&map->lock);
+			gss_put(p);
+		}
+	}
+}
+
+void
+gss_shutdown(void)
+{
+	nfsv4_printk(gss, "gss_shutdown: starting\n");
+	gss_clear_map(&owner_map);
+	gss_clear_map(&group_map);
+	nfsv4_printk(gss, "gss_shutdown: done\n");
+}
+
+static inline struct gss_cacheent *
+gss_searchbyid(struct gss_mapping *map, uid_t id)
+{
+	unsigned int hashval = id & GSS_HASH_MASK;
+	struct list_head *l;
+	struct gss_cacheent *p;
+
+	/* caller must hold readlock */
+	list_for_each(l, &map->id_hash[hashval]) {
+		p = list_entry(l, struct gss_cacheent, id_hash);
+		if (p->id == id)
+			return p;
+	}
+	return NULL;
+}
+
+static inline struct gss_cacheent *
+gss_searchbyname(struct gss_mapping *map, unsigned int len, const unsigned char *name,
+		 unsigned int hash)
+{
+	unsigned int hashval = hash & GSS_HASH_MASK;
+	struct list_head *l;
+	struct gss_cacheent *p;
+
+	/* caller must hold readlock */
+	list_for_each(l, &map->name_hash[hashval]) {
+		p = list_entry(l, struct gss_cacheent, name_hash);
+		if ((p->name.hash == hash) && (p->name.len == len) &&
+		    !memcmp(p->name.name, name, len))
+			return p;
+	}
+	return NULL;
+}
+
+int
+gss_get_name(int type, uid_t id, struct gss_cacheent **pp)
+{
+	struct gss_mapping *map;
+	struct gss_cacheent *p, *q;
+	int status;
+
+	nfsv4_printk(gss, "gss_get_name: called with type %s, id=%d\n", gss_opcode(type),
+		     (int)id);
+	
+#if NFS4_DEBUG
+	if ((type != GSS_OWNER) && (type != GSS_GROUP)) {
+		printk("gss_get_name: bad type!\n");
+		return -EINVAL;
+	}
+#endif
+	map = (type == GSS_OWNER) ? &owner_map : &group_map;
+	read_lock(&map->lock);
+	if ((p = gss_searchbyid(map, id))) {
+		atomic_inc(&p->refcount);         /* return a reference */
+		read_unlock(&map->lock);
+		nfsv4_printk(gss, "gss_get_name: cache hit\n");
+		*pp = p;
+		goto done;
+	}
+	read_unlock(&map->lock);
+	nfsv4_printk(gss, "gss_get_name: cache miss\n");
+	if ((status = __gss_get_name(type, id, &p)))
+		goto out;
+	write_lock(&map->lock);
+	if ((q = gss_searchbyid(map, id))) {      /* retry the search, since we blocked */
+		atomic_inc(&q->refcount);         /* return a reference */
+		write_unlock(&map->lock);
+		nfsv4_printk(gss, "gss_get_name: secondary cache hit (race condition)\n");
+		gss_free(p);
+		*pp = q;
+		goto done;
+	}
+	p->id = id;
+	p->name.hash = opaque_hashval(p->name.name, p->name.len);
+	gss_insert(p, map);
+	atomic_inc(&p->refcount);		  /* return a reference */
+	write_unlock(&map->lock);
+	*pp = p;
+	
+done:
+	status = 0;
+out:
+	nfsv4_printk(gss, "gss_get_name: returning status %d\n", status);
+	if (!status)
+		nfsv4_printk(gss, "gss_get_name: returning id=\"%.*s\"\n",
+			     (int)((*pp)->name.len), (*pp)->name.name);
+	return status;
+}
+
+int
+gss_get_num(int type, unsigned int len, const unsigned char *name, uid_t *pp)
+{
+	struct gss_mapping *map;
+	struct gss_cacheent *p, *q;
+	unsigned int hash = opaque_hashval(name, len);
+	int status;
+
+	nfsv4_printk(gss, "gss_get_num: called with type %s, name=\"%.*s\"\n",
+		     gss_opcode(type), (int)len, name);
+	
+#if NFS4_DEBUG
+	if ((type != GSS_OWNER) && (type != GSS_GROUP)) {
+		printk("gss_get_name: bad type!\n");
+		return -EINVAL;
+	}
+#endif
+	if (len > GSS_NAME_MAX) {
+		nfsv4_printk(gss, "gss_get_num: name too long!\n");
+		status = -ENAMETOOLONG;
+		goto out;
+	}
+	map = (type == GSS_OWNER) ? &owner_map : &group_map;
+	read_lock(&map->lock);
+	if ((p = gss_searchbyname(map, len, name, hash))) {
+		*pp = p->id;
+		read_unlock(&map->lock);
+		nfsv4_printk(gss, "gss_get_num: cache hit\n");
+		goto done;
+	}
+	read_unlock(&map->lock);
+	nfsv4_printk(gss, "gss_get_num: cache miss\n");
+	if (!(p = gss_alloc(len))) {
+		nfsv4_printk(gss, "gss_get_num: gss_alloc failed!\n");
+		status = -ENOMEM;
+		goto out;
+	}
+	if ((status = __gss_get_num(type, len, name, &p->id)))
+		goto out;
+	p->name.len = len;
+	memcpy((char *)p->name.name, name, len);         /* cast is to get rid of "const" */
+	p->name.hash = hash;
+	write_lock(&map->lock);
+	if ((q = gss_searchbyname(map, len, name, hash))) {   /* blocked, so retry search */
+		*pp = q->id;
+		write_unlock(&map->lock);
+		nfsv4_printk(gss, "gss_get_num: secondary cache hit (race condition)\n");
+		gss_free(p);
+		goto done;
+	}
+	gss_insert(p, map);
+	*pp = p->id;
+	write_unlock(&map->lock);
+	
+done:
+	status = 0;
+out:
+	nfsv4_printk(gss, "gss_get_num: returning status %d\n", status);
+	if (!status)
+		nfsv4_printk(gss, "gss_get_num: returning id=%d\n", (int)(*pp));
+	return status;
+}
--- clean/fs/nfs4/nfs4_syms.c	Thu Feb  7 18:04:30 2002
+++ dirty/fs/nfs4/nfs4_syms.c	Mon Feb  4 15:16:18 2002
@@ -0,0 +1,121 @@
+/*
+ *  linux/fs/nfs4/nfs4_syms.c
+ *
+ *  Copyright (c) 2000 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  Kendrick Smith <kmsmith@umich.edu>
+ *  Andy Adamson <andros@umich.edu>
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the University nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+*/
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+
+#include <linux/nfs4/nfs4.h>
+#include <linux/nfs4/nfs4_debug.h>
+
+#if NFS4_DEBUG
+/* Debugging flags */
+EXPORT_SYMBOL(nfsv4_debug_level1);
+EXPORT_SYMBOL(nfsv4_debug_level2);
+EXPORT_SYMBOL(nfsv4_debug_level3);
+EXPORT_SYMBOL(nfsv4_debug_mount);
+EXPORT_SYMBOL(nfsv4_debug_ctl);
+EXPORT_SYMBOL(nfsv4_debug_dispatcher);
+EXPORT_SYMBOL(nfsv4_debug_fh);
+EXPORT_SYMBOL(nfsv4_debug_xdr);
+EXPORT_SYMBOL(nfsv4_debug_getattr);
+EXPORT_SYMBOL(nfsv4_debug_setattr);
+EXPORT_SYMBOL(nfsv4_debug_lookup);
+EXPORT_SYMBOL(nfsv4_debug_readdir);
+EXPORT_SYMBOL(nfsv4_debug_create);
+EXPORT_SYMBOL(nfsv4_debug_remove);
+EXPORT_SYMBOL(nfsv4_debug_rename);
+EXPORT_SYMBOL(nfsv4_debug_link);
+EXPORT_SYMBOL(nfsv4_debug_readlink);
+EXPORT_SYMBOL(nfsv4_debug_open);
+EXPORT_SYMBOL(nfsv4_debug_read);
+EXPORT_SYMBOL(nfsv4_debug_write);
+EXPORT_SYMBOL(nfsv4_debug_lock);
+EXPORT_SYMBOL(nfsv4_debug_seqid);
+EXPORT_SYMBOL(nfsv4_debug_stateid);
+EXPORT_SYMBOL(nfsv4_debug_lockowner);
+EXPORT_SYMBOL(nfsv4_debug_clientid);
+EXPORT_SYMBOL(nfsv4_debug_reval);
+EXPORT_SYMBOL(nfsv4_debug_auth);
+EXPORT_SYMBOL(nfsv4_debug_secinfo);
+EXPORT_SYMBOL(nfsv4_debug_pagecache);
+EXPORT_SYMBOL(nfsv4_debug_lease);
+EXPORT_SYMBOL(nfsv4_debug_async);
+#if NFS4ACL
+EXPORT_SYMBOL(nfsv4_debug_acl);
+#endif /* NFS4ACL */
+EXPORT_SYMBOL(nfsv4_debug_callback);
+
+/* Functions for debugging vnode refcounts, memory allocation */
+EXPORT_SYMBOL(__NFS4_ALLOC);
+EXPORT_SYMBOL(__NFS4_FREE);
+EXPORT_SYMBOL(__IGET);
+EXPORT_SYMBOL(__IPUT);
+EXPORT_SYMBOL(__IGRAB);
+EXPORT_SYMBOL(__DGET);
+EXPORT_SYMBOL(__DGET_LOCKED);
+EXPORT_SYMBOL(__D_ALLOC);
+EXPORT_SYMBOL(__D_ALLOC_ROOT);
+EXPORT_SYMBOL(__DPUT);
+EXPORT_SYMBOL(__D_LOOKUP);
+EXPORT_SYMBOL(__LOOKUP_ONE);
+EXPORT_SYMBOL(__FOLLOW_DOWN);
+EXPORT_SYMBOL(DUP);
+#endif  /* NFS4_DEBUG */
+
+/* Utilities */
+EXPORT_SYMBOL(kernerrno);
+EXPORT_SYMBOL(nfserrno);
+
+/* Debugging print statements -- should be moved within #if NFS4_DEBUG eventually */
+EXPORT_SYMBOL(hex_dump);
+
+/* GSS */
+EXPORT_SYMBOL(gss_init);
+EXPORT_SYMBOL(gss_get_name);
+EXPORT_SYMBOL(gss_get_num);
+EXPORT_SYMBOL(gss_free);
+EXPORT_SYMBOL(gss_shutdown);
+
+/* acl */
+#if NFS4ACL
+EXPORT_SYMBOL(posix_acl_to_nfsv4_acl);
+EXPORT_SYMBOL(nfsv4_acl_to_posix_acl);
+EXPORT_SYMBOL(xdr_fattr4_acl_encode);
+EXPORT_SYMBOL(xdr_fattr4_acl_decode);
+EXPORT_SYMBOL(fattr4_acl_release);
+#endif
--- clean/fs/nfs4/sysctl.c	Thu Feb  7 18:04:30 2002
+++ dirty/fs/nfs4/sysctl.c	Thu Dec  6 02:17:24 2001
@@ -0,0 +1,152 @@
+/*
+ * sysctl.c
+ *
+ *  Copyright (c) 2000 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  Jake Moilanen <jmoilane@umich.edu>
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the University nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#include <linux/sysctl.h>
+#include <linux/fs.h>
+#include <linux/nfs4/nfs4_debug.h>
+
+int nfsv4_debug_level1          = 0;
+int nfsv4_debug_level2          = 0;
+int nfsv4_debug_level3          = 0;
+
+int nfsv4_debug_mount           = 0;
+int nfsv4_debug_ctl             = 0;
+int nfsv4_debug_dispatcher      = 0;
+int nfsv4_debug_fh              = 0;
+int nfsv4_debug_xdr             = 0;
+int nfsv4_debug_gss             = 0;
+
+int nfsv4_debug_getattr         = 0;
+int nfsv4_debug_setattr         = 0;
+int nfsv4_debug_lookup          = 0;
+int nfsv4_debug_readdir         = 0;
+int nfsv4_debug_create          = 0;
+int nfsv4_debug_remove          = 0;
+int nfsv4_debug_rename          = 0;
+int nfsv4_debug_link            = 0;
+int nfsv4_debug_readlink        = 0;
+int nfsv4_debug_open            = 0;
+int nfsv4_debug_read            = 0;
+int nfsv4_debug_write           = 0;
+int nfsv4_debug_lock            = 0;
+
+int nfsv4_debug_seqid           = 0;   /* XXX: phase this out in favor of debug_lockowner */
+int nfsv4_debug_stateid         = 0;
+int nfsv4_debug_lockowner       = 0;
+int nfsv4_debug_clientid        = 0;
+int nfsv4_debug_reval           = 0;
+int nfsv4_debug_auth            = 0;
+int nfsv4_debug_secinfo         = 0;
+int nfsv4_debug_pagecache       = 0;    /* including mmap() */
+int nfsv4_debug_lease           = 0;
+int nfsv4_debug_async		= 0;
+int nfsv4_debug_check           = 0;
+int nfsv4_debug_acl		= 0;
+int nfsv4_debug_callback	= 0;
+
+#if NFS4_DEBUG
+
+static struct ctl_table_header *nfs4_table_header;
+static ctl_table                nfs4_table[];
+
+void
+rpc_register_sysctl(void)
+{
+	if (!nfs4_table_header)
+		nfs4_table_header = register_sysctl_table(nfs4_table, 1);
+}
+
+void
+rpc_unregister_sysctl(void)
+{
+	if (nfs4_table_header) {
+		unregister_sysctl_table(nfs4_table_header);
+		nfs4_table_header = NULL;
+	}
+}
+
+#define DIRENTRY(nam1, nam2, child)     \
+        {CTL_##nam1, #nam2, NULL, 0, 0555, child }
+#define DBGENTRY(nam1, nam2)    \
+        {CTL_DEBUG_##nam1, #nam2, &nfsv4_debug_##nam2, sizeof(int),\
+         0644, NULL, &proc_dointvec}
+
+static ctl_table              debug_table[] = {
+	DBGENTRY(LEVEL1, level1),
+	DBGENTRY(LEVEL2, level2),
+	DBGENTRY(LEVEL3, level3),
+	DBGENTRY(MOUNT, mount),
+	DBGENTRY(CTL, ctl),
+	DBGENTRY(DISPATCHER, dispatcher),
+	DBGENTRY(FH, fh),
+	DBGENTRY(XDR, xdr),
+	DBGENTRY(GSS, gss),
+	DBGENTRY(GETATTR, getattr),
+	DBGENTRY(SETATTR, setattr),
+	DBGENTRY(LOOKUP, lookup),
+	DBGENTRY(READDIR, readdir),
+	DBGENTRY(CREATE, create),
+	DBGENTRY(REMOVE, remove),
+	DBGENTRY(RENAME, rename),
+	DBGENTRY(LINK, link),
+	DBGENTRY(READLINK, readlink),
+	DBGENTRY(OPEN, open),
+	DBGENTRY(READ, read),
+	DBGENTRY(WRITE, write),
+	DBGENTRY(LOCK, lock),
+	DBGENTRY(SEQID, seqid),
+	DBGENTRY(STATEID, stateid),
+	DBGENTRY(LOCKOWNER, lockowner),
+	DBGENTRY(CLIENTID, clientid),
+	DBGENTRY(REVAL, reval),
+	DBGENTRY(AUTH, auth),
+	DBGENTRY(SECINFO, secinfo),
+	DBGENTRY(PAGECACHE, pagecache),
+	DBGENTRY(LEASE, lease),
+	DBGENTRY(ASYNC, async),
+	DBGENTRY(CHECK, check),
+	DBGENTRY(ACL, acl),
+	DBGENTRY(CALLBACK, callback),
+	{0}
+};
+
+static ctl_table             nfs4_table[] = {
+        DIRENTRY(NFS4, nfs4, debug_table),
+        {0}
+};
+
+
+#endif  /* NFS4_DEBUG */
--- clean/fs/nfs4/util.c	Thu Feb  7 18:04:30 2002
+++ dirty/fs/nfs4/util.c	Tue Feb  5 14:38:16 2002
@@ -0,0 +1,644 @@
+/*
+ *  linux/fs/nfs4/util.c
+ *
+ *  Copyright (c) 2000 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  Kendrick Smith <kmsmith@umich.edu>
+ *  Andy Adamson <andros@umich.edu>
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the University nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/dcache.h>
+#include <linux/nfs4/nfs4.h>
+#include <linux/nfs4/nfs4_debug.h>
+
+static struct {
+	int	nfserr;
+	int	syserr;
+} nfs_errtbl[] = {
+	{ NFS4_OK, 0 },
+	{ NFS4ERR_PERM, -EPERM },
+	{ NFS4ERR_NOENT, -ENOENT },
+	{ NFS4ERR_IO, -EIO },
+	{ NFS4ERR_NXIO, -ENXIO },
+	{ NFS4ERR_ACCES, -EACCES },
+	{ NFS4ERR_EXIST, -EEXIST },
+	{ NFS4ERR_XDEV, -EXDEV },
+	{ NFS4ERR_MLINK, -EMLINK },
+	{ NFS4ERR_NODEV, -ENODEV },
+	{ NFS4ERR_NOTDIR, -ENOTDIR },
+	{ NFS4ERR_ISDIR, -EISDIR },
+	{ NFS4ERR_INVAL, -EINVAL },
+	{ NFS4ERR_FBIG, -EFBIG },
+	{ NFS4ERR_NOSPC, -ENOSPC },
+	{ NFS4ERR_ROFS, -EROFS },
+	{ NFS4ERR_MLINK, -EMLINK },
+	{ NFS4ERR_NAMETOOLONG, -ENAMETOOLONG },
+	{ NFS4ERR_NOTEMPTY, -ENOTEMPTY },
+#ifdef EDQUOT
+	{ NFS4ERR_DQUOT, -EDQUOT },
+#endif
+	{ NFS4ERR_STALE, -ESTALE },
+	{ NFS4ERR_DENIED, -EAGAIN },
+	{ NFS4ERR_SYMLINK, -ELOOP },
+	{ -1, -EIO }
+};
+
+int
+kernerrno(int err)
+{
+	int i;
+	
+	/* XXX : not the optimal algorithm, but will do for now! */
+	for (i = 0; nfs_errtbl[i].nfserr != -1; i++) {
+		if (nfs_errtbl[i].nfserr == err)
+			return nfs_errtbl[i].syserr;
+	}
+	nfsv4_printk(level2, "kernerrno: unrecognized nfs err: %d\n", err);
+	/* if no entry exists for this error, use EIO by default */
+	return -EIO;
+}
+
+int
+nfserrno(int errno)
+{
+	int i;
+
+	/* XXX : not the optimal algorithm, but will do for now! */
+	for (i = 0; nfs_errtbl[i].nfserr != -1; i++) {
+		if (nfs_errtbl[i].syserr == errno)
+			return nfs_errtbl[i].nfserr;
+	}
+	nfsv4_printk(level2, "nfserrno: unrecongnized errno: %d\n", errno);
+	/* if no entry exists for this error, use NFS4ERR_IO by default */
+	return NFS4ERR_IO;
+}
+
+#if NFS4_DEBUG
+void
+hex_dump(void *ptr, int nbytes)
+{
+	int i;
+	unsigned char *cptr = (unsigned char *) ptr;
+
+	if (nbytes < 0) {
+		printk("hex_dump: nbytes < 0!\n");
+		return;
+	}
+	for (i = 0; i < nbytes; i++) {
+		printk("%2x", *cptr++);
+		if ((i % 24) == 23)
+			printk("\n");
+		else if ((i % 4) == 3)
+			printk("   ");
+		else
+			printk(" ");
+	}
+	printk("\n");
+}
+
+#if 0
+void
+print_rpcsec_gss_info(rpcsec_gss_info *ptr)
+{
+  int i;
+  char *p;
+
+  printk("NFSV4: print_rpcsec_gss_info\n");
+  /*secinfo4*/
+ 
+	printk("rpcsec_gss_info:\n");
+  printk("     sec_info4 len: %d\n sec_info4 val:",
+	    ptr->oid.sec_oid4_len);
+  p = ptr->oid.sec_oid4_val;
+	printk("     ");
+  for(i=0;i<ptr->oid.sec_oid4_len;i++)
+    printk("%c",*p++);
+  printk("     qop: %d\n",ptr->qop);
+  printk("     service: %d\n",ptr->service);
+}
+#endif
+
+#define HASH_SIZE  256
+
+static u32
+ptr_hval(const void *ptr)
+{
+	return opaque_hashval(&ptr, sizeof(void *)) % HASH_SIZE;
+}
+
+struct mem_hashent {
+	int nbytes;
+	char *file;
+	int line;
+	void *ptr;
+	struct mem_hashent *next;
+};
+static struct mem_hashent *mem_hashtbl[HASH_SIZE];
+
+#define HISTORY_LEN   20
+struct ref_hashent {
+	void *ptr;
+	char *file[HISTORY_LEN];
+	int line[HISTORY_LEN];
+	int change[HISTORY_LEN];
+	int refcount;
+	struct ref_hashent *next;
+};
+static struct ref_hashent *dentry_hashtbl[HASH_SIZE];
+static struct ref_hashent *inode_hashtbl[HASH_SIZE];
+
+/* when a filename is entered into the history tables, it may exist in the static memory of
+   a different module, which can be unloaded seperately from this one.  therefore, we must
+   copy the string.  on the other hand, we want to avoid excessive copying.  we keep a hash
+   table of filenames, so that if a duplicate filename is seen, we can return the old copy
+   instead of making a new one. */
+
+struct filename_hashent {
+	int len;
+	char *name;
+	struct filename_hashent *next;
+};
+static struct filename_hashent *filename_hashtbl[HASH_SIZE];
+
+static struct semaphore check_sema;
+
+static char *
+dup_filename(char *name)
+{
+	char *retval;
+	struct filename_hashent *p;
+	int len = strlen(name);
+	int hashval = opaque_hashval(name, len) % HASH_SIZE;
+
+	down(&check_sema);
+	for (p = filename_hashtbl[hashval]; p; p = p->next)
+		if ((p->len == len) && !memcmp(p->name, name, len)) {
+			up(&check_sema);
+			return p->name;
+		}
+	if (!(p = kmalloc(sizeof(*p), GFP_KERNEL))) {
+		printk("!!! couldn't kmalloc filename_hashent !!!\n");
+		return "unknown";
+	}
+	if (!(retval = kmalloc(len+1, GFP_KERNEL))) {
+		printk("!!! couldn't kmalloc filename !!!\n");
+		kfree(p);
+		return "unknown";
+	}
+	memcpy(retval, name, len+1);
+	p->len = len;
+	p->name = retval;
+	p->next = filename_hashtbl[hashval];
+	filename_hashtbl[hashval] = p;
+	up(&check_sema);
+	return retval;
+}
+
+static void
+adjust_history(struct ref_hashent *p, char *file, int line, int change)
+{
+	int i;
+	for (i = HISTORY_LEN-1; i >= 1; i--) {
+		p->file[i] = p->file[i-1];
+		p->line[i] = p->line[i-1];
+		p->change[i] = p->change[i-1];
+	}
+	p->file[0] = file;
+	p->line[0] = line;
+	p->change[0] = change;
+	p->refcount += change;
+}
+
+static struct ref_hashent *
+find_ref_hashent(void *ptr, struct ref_hashent **headp)
+{
+	struct ref_hashent *p;
+	for (p = *headp; p; p = p->next)
+		if (p->ptr == ptr)
+			break;
+	if (!p && (p = kmalloc(sizeof(*p), GFP_KERNEL))) {
+		int i;
+		for (i = 0; i < HISTORY_LEN; i++)
+			p->change[i] = 0;
+		p->refcount = 0;
+		p->ptr = ptr;
+		p->next = *headp;
+		*headp = p;
+	}
+	return p;
+}
+
+static void
+print_history(struct ref_hashent *p)
+{
+	int i;
+	printk("%p", p->ptr);
+	for (i = 0; (i < HISTORY_LEN) && p->change[i]; i++) {
+		if (p->change[i] == 1)
+			printk(" GET");
+		else if (p->change[i] == -1)
+			printk(" PUT");
+		else
+			printk(" BAD CHANGE: %d?!", p->change[i]);
+		printk(" %s:%d", p->file[i], p->line[i]);
+	}
+}
+
+static void
+INITIALIZE(void)
+{
+	int i;
+	init_MUTEX(&check_sema);
+	for (i = 0; i < HASH_SIZE; i++) {
+		mem_hashtbl[i] = NULL;
+		dentry_hashtbl[i] = NULL;
+		inode_hashtbl[i] = NULL;
+		filename_hashtbl[i] = NULL;
+	}
+}
+
+void *
+__NFS4_ALLOC(int nbytes, char *file, int line)
+{
+	struct mem_hashent *p;
+	void *ptr;
+	u32 hashval;
+
+	if (nbytes == 0) {
+		printk("ZERO SIZE REQUESTED in NFS4_ALLOC (source %s:%d)!\n",
+		       file, line);
+		return NULL;
+	}
+	if (nbytes < 0) {
+		printk("NEGATIVE SIZE REQUESTED in NFS4_ALLOC (source %s:%d)!\n",
+		       file, line);
+		return NULL;
+	}
+	if (nbytes > 20000) {
+		printk("%d BYTES REQUESTED in NFS4_ALLOC (source %s:%d)!\n",
+		       nbytes, file, line);
+		return NULL;
+	}
+		
+	file = dup_filename(file);
+	if (!(p = kmalloc(sizeof(*p), GFP_KERNEL))) {
+		printk("HASH KMALLOC FAILED in NFS4_ALLOC!\n");
+		return NULL;
+	}
+	if (!(ptr = kmalloc(nbytes, GFP_KERNEL))) {
+		printk("MAIN KMALLOC FAILED in NFS4_ALLOC!\n");
+		printk("%s:%d, %d bytes\n", file, line, nbytes);
+		kfree(p);
+		return NULL;
+	}
+	hashval = ptr_hval(ptr);
+	p->nbytes = nbytes;
+	p->file = file;
+	p->line = line;
+	p->ptr = ptr;
+	down(&check_sema);
+	p->next = mem_hashtbl[hashval];
+	mem_hashtbl[hashval] = p;
+	up(&check_sema);
+	return ptr;
+}
+
+void
+__NFS4_FREE(const void *ptr, char *file, int line)
+{
+	u32 hashval = ptr_hval(ptr);
+	struct mem_hashent *p, **pp;
+
+	file = dup_filename(file);
+	down(&check_sema);
+	pp = &mem_hashtbl[hashval];
+	for (p = mem_hashtbl[hashval]; p; p = p->next) {
+		if (p->ptr == ptr)
+			break;
+		pp = &p->next;
+	}
+	if (!p) {
+		up(&check_sema);
+		printk("BAD KFREE of ptr %p! (source: %s:%d)\n", ptr, file, line);
+	}
+	else {
+		*pp = p->next;
+		up(&check_sema);
+		kfree(p);
+		kfree(ptr);
+	}
+}
+
+void
+DUP(struct dentry *dentry, char *file, int line)
+{
+	struct ref_hashent *p;
+
+	file = dup_filename(file);
+	down(&check_sema);
+	if (!(p = find_ref_hashent(dentry, &dentry_hashtbl[ptr_hval(dentry)]))) {
+		printk("FAILED TO ALLOC REF HASHENT!\n");
+		return;
+	}
+	adjust_history(p, file, line, 1);
+	if (p->refcount > 5) {
+		printk("BAD DGET? ");
+		print_history(p);
+	}
+	up(&check_sema);
+}
+
+static void
+DDOWN(struct dentry *dentry, char *file, int line)
+{
+	struct ref_hashent *p;
+
+	file = dup_filename(file);
+	down(&check_sema);
+	if (!(p = find_ref_hashent(dentry, &dentry_hashtbl[ptr_hval(dentry)]))) {
+		printk("FAILED TO ALLOC REF HASHENT!\n");
+		return;
+	}
+	adjust_history(p, file, line, -1);
+	if (p->refcount < 0) {
+		printk("BAD DPUT! ");
+		print_history(p);
+	}
+	up(&check_sema);
+}
+
+static void
+IUP(struct inode *inode, char *file, int line)
+{
+	struct ref_hashent *p;
+
+	file = dup_filename(file);
+	down(&check_sema);
+	if (!(p = find_ref_hashent(inode, &inode_hashtbl[ptr_hval(inode)]))) {
+		printk("FAILED TO ALLOC REF HASHENT!\n");
+		return;
+	}
+	adjust_history(p, file, line, 1);
+	if (p->refcount > 5) {
+		printk("BAD IGET? ");
+		print_history(p);
+	}
+	up(&check_sema);
+}
+
+static void
+IDOWN(struct inode *inode, char *file, int line)
+{
+	struct ref_hashent *p;
+
+	file = dup_filename(file);
+	down(&check_sema);
+	if (!(p = find_ref_hashent(inode, &inode_hashtbl[ptr_hval(inode)]))) {
+		printk("FAILED TO ALLOC REF HASHENT!\n");
+		return;
+	}
+	adjust_history(p, file, line, -1);
+	if (p->refcount < 0) {
+		printk("BAD IPUT! ");
+		print_history(p);
+	}
+	up(&check_sema);
+}
+
+struct inode *__IGET(struct super_block *sb, unsigned long ino, char *file, int line)
+{
+	struct inode *i;
+	nfsv4_printk(check, "          iget: %s:%d\n", file, line);
+	i = iget(sb, ino);
+	if (i)
+		IUP(i, file, line);
+	return i;
+}
+
+void __IPUT(struct inode *inode, char *file, int line)
+{
+	IDOWN(inode, file, line);
+	iput(inode);
+}
+
+struct inode * __IGRAB(struct inode *inode, char *file, int line)
+{
+	struct inode *i;
+	nfsv4_printk(check, "          igrab: %s:%d\n", file, line);
+	i = igrab(inode);
+	if (i == inode)
+		IUP(i, file, line);
+	else if (i)
+		printk("IGRAB RETURN MISMATCH %s:%d?!\n", file, line);
+	return i;
+}
+
+struct dentry *__DGET(struct dentry *dentry, char *file, int line)
+{
+	struct dentry *d;
+	nfsv4_printk(check, "          dget: %s:%d\n", file, line);
+	d = dget(dentry);
+	if (dentry) {
+		if (d == dentry)
+			DUP(d, file, line);
+		else
+			printk("DGET RETURN MISMATCH %s:%d?!\n", file, line);
+	}
+	return d;
+}
+
+struct dentry *__DGET_LOCKED(struct dentry *dentry, char *file, int line)
+{
+	struct dentry *d;
+	nfsv4_printk(check, "          dget_locked: %s:%d\n", file, line);
+	d = dget_locked(dentry);
+	if (d == dentry)
+		DUP(d, file, line);
+	else
+		printk("DGET_LOCKED RETURN MISMATCH %s:%d?!\n", file, line);
+	return d;
+}
+
+struct dentry *__D_ALLOC(struct dentry *dentry, const struct qstr *str, char *file, int line)
+{
+	struct dentry *d;
+	nfsv4_printk(check, "          d_alloc: %s:%d\n", file, line);
+	d = d_alloc(dentry, str);
+	if (d == dentry)
+		DUP(d, file, line);
+	else
+		printk("D_ALLOC RETURN MISMATCH %s:%d?!\n", file, line);
+	return d;
+}
+
+struct dentry *__D_ALLOC_ROOT(struct inode *inode, char *file, int line)
+{
+	struct dentry *d;
+	nfsv4_printk(check, "          d_alloc_root: %s:%d\n", file, line);
+	d = d_alloc_root(inode);
+	if (d)
+		DUP(d, file, line);
+	return d;
+}
+
+void __DPUT(struct dentry *dentry, char *file, int line)
+{
+	if (dentry) {
+		dput(dentry);
+		DDOWN(dentry, file, line);
+	}
+}
+
+struct dentry *__D_LOOKUP(struct dentry *dentry, struct qstr *str, char *file, int line)
+{
+	struct dentry *d;
+	nfsv4_printk(check, "          d_lookup: %s:%d\n", file, line);
+	d = d_lookup(dentry, str);
+	if (d)
+		DUP(d, file, line);
+	return d;
+}
+
+struct dentry *__LOOKUP_ONE(const char *str, struct dentry *dentry, char *file, int line)
+{
+	struct dentry *d;
+	nfsv4_printk(check, "          lookup_one: %s:%d\n", file, line);
+	d = lookup_one(str, dentry);
+	if (d)
+		DUP(d, file, line);
+	return d;
+}
+
+int __FOLLOW_DOWN(struct vfsmount **mntp, struct dentry **dentryp, char *file, int line)
+{
+	struct dentry *d = *dentryp;
+	int res;
+	nfsv4_printk(check, "          follow_down: %s:%d\n", file, line);
+	if ((res = follow_down(mntp, dentryp))) {
+		DDOWN(d, file, line);
+		DUP(*dentryp, file, line);
+	}
+	return res;
+}
+
+static void
+COMPLAIN(void)
+{
+	int i;
+	struct mem_hashent *p, *P;
+	struct ref_hashent *q, *Q;
+	struct filename_hashent *r, *R;
+	down(&check_sema);
+	printk("Checking for memory leaks...\n");
+	for (i = 0; i < HASH_SIZE; i++) {
+		for (p = mem_hashtbl[i]; p; p = P) {
+			printk("MEMORY LEAK %d bytes at %p (source: %s:%d)\n",
+			       p->nbytes, p->ptr, p->file, p->line);
+			kfree(p->ptr);
+			P = p->next;
+			kfree(p);
+		}
+	}
+
+	printk("Checking dentry refcounts...\n");
+	for (i = 0; i < HASH_SIZE; i++) {
+		for (q = dentry_hashtbl[i]; q; q = Q) {
+			if (q->refcount > 0) {
+				printk("DCOUNT LEAK ");
+				print_history(q);
+			}
+			Q = q->next;
+			kfree(q);
+		}
+	}
+
+	printk("Checking inode refcounts...\n");
+	for (i = 0; i < HASH_SIZE; i++) {
+		for (q = inode_hashtbl[i]; q; q = Q) {
+			if (q->refcount > 0) {
+				printk("ICOUNT LEAK ");
+				print_history(q);
+			}
+			Q = q->next;
+			kfree(q);
+		}
+	}
+
+	for (i = 0; i < HASH_SIZE; i++)
+		for (r = filename_hashtbl[i]; r; r = R) {
+			R = r->next;
+			kfree(r->name);
+			kfree(r);
+		}
+
+	up(&check_sema);
+}
+
+#endif  /* NFS4_DEBUG */
+
+/*
+ *  Kernel module setup
+ */
+
+#ifdef MODULE
+
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR("UM-CITI");
+
+int
+init_module(void)
+{
+#if NFS4_DEBUG
+	INITIALIZE();
+	rpc_register_sysctl();
+#endif
+	gss_init();
+	printk(KERN_INFO "Installing nfs4\n");
+	return 0;
+}
+
+void
+cleanup_module(void)
+{
+	gss_shutdown();
+#if NFS4_DEBUG
+	rpc_unregister_sysctl();
+	COMPLAIN();
+#endif
+	printk(KERN_INFO "Uninstalling nfs4\n");
+}
+
+#endif  /* MODULE */
--- clean/fs/nfs4/Makefile	Thu Feb  7 18:04:30 2002
+++ dirty/fs/nfs4/Makefile	Mon Feb  4 15:16:19 2002
@@ -0,0 +1,9 @@
+O_TARGET := nfs4.o
+
+export-objs := nfs4_syms.o
+
+obj-y    := nfs4_syms.o gss.o sysctl.o util.o
+
+obj-m  := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
--- clean/fs/nfs4fs/access.c	Thu Feb  7 18:04:30 2002
+++ dirty/fs/nfs4fs/access.c	Mon Feb  4 16:32:18 2002
@@ -0,0 +1,380 @@
+/*
+ *  fs/nfs4fs/access.c
+ *
+ *  Copyright (c) 2002 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  Kendrick Smith <kmsmith@umich.edu>
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the University nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This file implements a straightforward hashtable for caching the results
+ * of ACCESS calls.  At present, it is only used in the case where a file
+ * delegation is held, and we are trying to decide whether an OPEN can
+ * be delegated to the client.  Over the lifetime of the delegation, the
+ * cache will fill with entries asserting the file access of various
+ * uid's and gid's.  The cache entries for a particular file are flushed
+ * when (1) the delegation is returned, or (2) we do a SETATTR which
+ * might influence the file access.  (Note: nfsv4 delegation semantics
+ * ensure that the cache remains valid until one of these events occurs.)
+ */
+
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/list.h>
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include <linux/sunrpc/sched.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+
+#include <linux/nfs4/nfs4.h>
+#include <linux/nfs4/nfs4_debug.h>
+#include <linux/nfs4/nfs4fs_xdr.h>
+#include <linux/nfs4/nfs4fs.h>
+
+/*
+ * These are the uid and gid which are used for deciding file access.  The
+ * macros can be changed arbitrarily as long as they do not sleep -- they are
+ * called while holding a spinlock.
+ */
+#define CURRENT_UID()		(current->fsuid)
+#define CURRENT_GID()		(current->fsgid)
+
+/*
+ * Hashtable declarations.  I initially protected the hashtable with the
+ * state_spinlock, but it turned out that all users held the BKL anyway
+ * (for other reasons), so I ended up adopting the convention that the
+ * table is protected with the BKL.
+ */
+#define ACCESS_HASH_BITS	6
+#define ACCESS_HASH_SIZE	(1 << ACCESS_HASH_BITS)
+#define ACCESS_HASH_MASK	(ACCESS_HASH_SIZE - 1)
+static struct list_head		access_hashtbl[ACCESS_HASH_SIZE];
+
+struct access_hashtbl_entry {
+	struct list_head	hash;    /* by inode, uid, gid */
+	struct list_head	list;    /* linkage for per-inode list */
+	struct inode *		inode;
+	uid_t			uid;
+	gid_t			gid;
+	unsigned int		access;
+};
+
+/*
+ * hash function -- hash by inode pointer, CURRENT_UID(), CURRENT_GID()
+ * XXX: could be improved...
+ */
+static inline unsigned int
+access_hashval(struct inode *inode)
+{
+	unsigned int x;
+
+	x = opaque_hashval(&inode, sizeof(struct inode *));
+	x += CURRENT_UID() * 37;
+	x += CURRENT_GID() * 23;
+	return (x & ACCESS_HASH_MASK);
+}
+
+/* routine for hashtable search... */
+static inline struct access_hashtbl_entry *
+__search_hashtbl(struct inode *inode, unsigned int hashval)
+{
+	struct list_head *l;
+	struct access_hashtbl_entry *ap;
+	uid_t uid = CURRENT_UID();
+	gid_t gid = CURRENT_GID();
+
+	NFS4_ASSERT(kernel_locked());
+
+	list_for_each(l, &access_hashtbl[hashval]) {
+		ap = list_entry(l, struct access_hashtbl_entry, hash);
+		if ((ap->inode == inode) && (ap->uid == uid) && (ap->gid == gid))
+			return ap;
+	}
+	return NULL;
+}
+
+void
+nfs4fs_init_access_cache(void)
+{
+	int i;
+	for (i = 0; i < ACCESS_HASH_SIZE; i++)
+		INIT_LIST_HEAD(&access_hashtbl[i]);
+}
+
+void
+nfs4fs_cleanup_access_cache(void)
+{
+	int i;
+
+	for (i = 0; i < ACCESS_HASH_SIZE; i++)
+		if (!list_empty(&access_hashtbl[i]))
+			printk(KERN_ERR "nfs4: stray access cache entry!\n");
+}
+
+/*
+ * Externally-visible entry points:
+ *   nfs4fs_flush_access_cache() - flushes access cache for a given inode
+ *   nfs4fs_try_to_delegate_open() - called in OPEN path to see whether an open
+ *                                   can be "delegated" to client
+ */
+void
+nfs4fs_flush_access_cache(struct inode *inode)
+{
+	LIST_HEAD(private);
+	struct list_head *l;
+	struct access_hashtbl_entry *ap;
+
+	NFS4_ASSERT(kernel_locked());
+	NFS4_ASSERT(inode != NULL);
+	nfsv4_printk(open, "flushing access cache for inode %p/%ld\n", inode, inode->i_ino);
+	
+	/*
+	 * To avoid race conditions, we pull the inode's access cache entries into
+	 * our own private list.  This part is atomic as it runs under the BKL.
+	 * Then we walk the private list freeing each member.
+	 */
+
+	list_splice(&NFS4_ACCESS_CACHE_ENTRIES(inode), &private);
+	INIT_LIST_HEAD(&NFS4_ACCESS_CACHE_ENTRIES(inode));
+	
+	while (!list_empty(&private)) {
+		l = private.next;
+		ap = list_entry(l, struct access_hashtbl_entry, list);
+		list_del_init(&ap->hash);
+		list_del_init(&ap->list);
+		NFS4_FREE(ap);
+	}
+}
+
+/*
+ * This routine does most of the checks (i.e., everything except sending an
+ * ACCESS call to the server) for deciding whether an OPEN can be "delegated"
+ * to the client:
+ *     - do we hold a delegation?
+ *     - do we already hold "placeholder" OPEN's needed for reads and writes?
+ *     - if opening for write, is the delegation a write delegation?
+ *
+ * This is atomic: it cannot sleep, and is run under the BKL.
+ */
+static inline int
+do_checks(struct inode *inode, struct nfs4fs_file_data *fp)
+{
+	struct nfs4fs_delegation *dp;
+	int status;
+
+	NFS4_ASSERT(kernel_locked());
+	
+	status = -EIO;
+	if (!(dp = NFS4_DELEGATION(inode))) {
+		nfsv4_printk(open, "delegate_open: no delegation held, sorry\n");
+		goto out;
+	}
+	if (fp->fi_flags & FI_READ) {
+		if (list_empty(&NFS4_READ_OPENS(inode))) {
+			nfsv4_printk(open, "delegate_open: no read OPEN yet, sorry\n");
+			goto out;
+		}
+	}
+	if (fp->fi_flags & FI_WRITE) {
+		if (list_empty(&NFS4_WRITE_OPENS(inode))) {
+			nfsv4_printk(open, "delegate_open: no write OPEN yet, sorry\n");
+			goto out;
+		}
+		if (dp->dl_type != OPEN_DELEGATE_WRITE) {
+			nfsv4_printk(open, "delegate_open: not write delegation, sorry\n");
+			goto out;
+		}
+	}
+
+	status = 0;
+out:
+	return status;
+}
+
+/*
+ * This routine "delegates" an OPEN to the client if possible.  Return value: 0
+ * if OPEN has been successfully delegated, -EPERM if access denied on server.
+ * Anything else: couldn't delegate open, continue on normal open path...
+ *
+ * One particularly ugly feature of this function is the presence of the
+ * 'nfs4fs_compound *' argument -- this is because the caller has already
+ * placed an nfs4fs_compound on its stack, and we want to avoid placing
+ * another one.  This "huge problem" is listed as a TODO in nfs4fs.h.
+ * Temporary solution: Hijack the nfs4fs_compound in the caller's stack
+ * frame, by having the caller pass in a pointer...
+ * Hopefully, the need for such contortions will go away soon!
+ */
+int
+nfs4fs_try_to_delegate_open(struct inode *inode, struct nfs4fs_file_data *fp,
+			    int flags, struct nfs4fs_compound *cp)
+{
+	unsigned int req_access, access;
+	unsigned int hashval;
+	struct access_hashtbl_entry *ap;
+	struct nfs4fs_delegation *dp;
+	int status;
+
+	NFS4_ASSERT(kernel_locked());
+	
+	status = -EIO;
+	if (flags & O_EXCL) {
+		/* This case should have been caught already, but just in case... */
+		nfsv4_printk(level1, "try_do_delegate_open: O_EXCL?!\n");
+		goto out;
+	}
+	if (flags & O_TRUNC) {
+		/*
+		 * XXX: In this case, we can't just delegate the OPEN, since it has
+		 * side effects (truncating the file).  The right way to handle this
+		 * condition is probably to send a SETATTR, but at present we can't
+		 * do this because it will result in recall of the delegation
+		 * (protocol bug).  So for now we handle it by continuing on the
+		 * normal OPEN path...
+		 */
+		nfsv4_printk(open, "try_to_delegate_open: O_TRUNC specified, sorry\n");
+		goto out;
+	}
+	if (flags & O_DIRECT) {
+		/*
+		 * XXX: Temporary hack: O_DIRECT means "do not delegate"
+		 */
+		nfsv4_printk(open, "try_to_delegate_open: O_DIRECT specified, sorry\n");
+		goto out;
+	}
+
+	req_access = 0;
+	if (fp->fi_flags & FI_READ)
+		req_access |= ACCESS4_READ;
+	if (fp->fi_flags & FI_WRITE)
+		req_access |= (ACCESS4_MODIFY | ACCESS4_EXTEND);
+	
+	/* First, do the "obvious" checks... */
+	if ((status = do_checks(inode, fp)))
+		goto out;
+
+	/*
+	 * If the "obvious" checks succeed, all that remains is to check file
+	 * access.  First, see if this information is cached...
+	 */
+	nfsv4_printk(open, "try_to_delegate_open: checking access %p/%ld (uid %d, gid %d)\n",
+		     inode, inode->i_ino, CURRENT_UID(), CURRENT_GID());
+	hashval = access_hashval(inode);
+	ap = __search_hashtbl(inode, hashval);
+	if (ap) {
+		nfsv4_printk(open, "try_to_delegate_open: cached access=0x%x\n", ap->access);
+		status = -EPERM;
+		if (req_access & ~ap->access) {
+			nfsv4_printk(open2, "delegate_open: access denied! [cached]\n");
+			goto out;
+		}
+		ap = NULL;
+		goto success;
+	}
+
+	/*
+	 * No entry cached, so send ACCESS call to server...
+	 */
+	nfsv4_printk(open, "try_to_delegate_open: uncached, sending ACCESS to server...\n");
+	nfs4fs_setup_compound(cp, inode->i_sb, "access");
+	nfs4fs_setup_putfh(cp, inode);
+	nfs4fs_setup_access(cp, ACCESS4_READ | ACCESS4_MODIFY | ACCESS4_EXTEND);
+
+	if ((status = nfs4fs_call_compound(cp)))
+		goto out_release;
+	if ((status = nfs4fs_handle_putfh(cp)))
+		goto out_release;
+	if ((status = nfs4fs_handle_access(cp, &access)))
+		goto out_release;
+	nfs4fs_release_compound(cp);
+
+	/*
+	 * Now that we have received the response, prepare to save this
+	 * information in the hashtable, by allocating a hashtable entry.
+	 */
+	nfsv4_printk(open, "try_to_delegate_open: ACCESS succeeds, response=0x%x\n", access);
+	if (!(ap = NFS4_ALLOC(sizeof(*ap)))) {
+		nfsv4_printk(level1, "try_to_delegate_open: couldn't allocate memory!\n");
+		goto recheck;
+	}
+	ap->inode = inode;
+	ap->uid = CURRENT_UID();
+	ap->gid = CURRENT_GID();
+	ap->access = access;
+
+	/*
+	 * We only update the hashtable if (1) a delegation is held for this inode,
+	 * (2) no hashtable entry already exists for this (inode,uid,gid) triple.
+	 *
+	 * (We know that these conditions held earlier in this routine, but we must
+	 *  retry them now, since we slept.  For the same reason, we must retry the
+	 *  "obvious" checks..)
+	 */
+	if (NFS4_DELEGATION(inode) && !__search_hashtbl(inode, hashval)) {
+		nfsv4_printk(open, "try_to_delegate_open: inserting into hashtable\n");
+		list_add(&ap->hash, &access_hashtbl[hashval]);
+		list_add(&ap->list, &NFS4_ACCESS_CACHE_ENTRIES(inode));
+		ap = NULL;
+	}
+
+recheck:
+	status = -EPERM;
+	if (req_access & ~access) {
+		nfsv4_printk(open2, "delegate_open: access denied! [cached]\n");
+		goto out_free;
+	}
+	if ((status = do_checks(inode, fp)))   /* Retry "obvious" checks */
+		goto out_free;
+
+success:
+	/*
+	 * Success!  This OPEN can be delegated...
+	 */
+	nfsv4_printk(open, "try_to_delegate_open: success, OPEN will be delegated\n");
+	dp = NFS4_DELEGATION(inode);
+	NFS4_ASSERT(dp != NULL);
+	
+	if (fp->fi_flags & FI_READ)
+		list_add(&fp->fi_read, &dp->dl_read_opens);
+	if (fp->fi_flags & FI_WRITE)
+		list_add(&fp->fi_write, &dp->dl_write_opens);
+	fp->fi_delegation = dp;
+	atomic_inc(&dp->dl_count);
+	status = 0;
+
+out_free:
+	if (ap) {
+		NFS4_ASSERT(&ap->hash);
+		NFS4_ASSERT(&ap->list);
+		NFS4_FREE(ap);
+	}
+out:
+	return status;
+out_release:
+	nfs4fs_release_compound(cp);
+	goto out;
+}
--- clean/fs/nfs4fs/dir.c	Thu Feb  7 18:04:30 2002
+++ dirty/fs/nfs4fs/dir.c	Mon Feb  4 15:16:20 2002
@@ -0,0 +1,1168 @@
+/*
+ * linux/fs/nfs4fs/dir.c
+ * from linux/fs/nfs/dir.c
+ *    Copyright (C) 1992  Rick Sladkey
+ *
+ * Kendrick Smith  <kmsmith@umich.edu>
+ * Andy Adamson  <andros@umich.edu>
+ * Copyright (C) 2001 The Regents of the University of Michigan
+ *
+ */
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/fs.h>
+#include <linux/sunrpc/sched.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+
+#include <linux/nfs4/nfs4.h>
+#include <linux/nfs4/nfs4_debug.h>
+#include <linux/nfs4/nfs4fs_xdr.h>
+#include <linux/nfs4/nfs4fs.h>
+
+/*
+ * Dentry operations.
+ */
+static int nfs4_dentry_delete(struct dentry *dentry);
+static void nfs4_silly_dentry_iput(struct dentry *dentry, struct inode *inode);
+
+struct dentry_operations nfs4_dentry_operations = {
+	d_revalidate: nfs4_lookup_revalidate,
+	d_delete: nfs4_dentry_delete,
+	d_iput: nfs4_silly_dentry_iput
+};
+
+/*
+ * Inode operations.
+ */
+static struct dentry *nfs4_lookup(struct inode *dir_inode, struct dentry *dentry);
+static int nfs4_readdir(struct file *filp, void *buf, filldir_t filler);
+static int nfs4_link(struct dentry *, struct inode *, struct dentry *);
+static int nfs4_symlink(struct inode *dir_inode, struct dentry *dentry, const char *name);
+static int nfs4_unlink(struct inode *dir_inode, struct dentry *dentry);
+static int nfs4_mkdir(struct inode *dir_i, struct dentry *dentry, int mode);
+static int nfs4_remove(struct inode *dir_inode, struct dentry *dentry);
+static int nfs4_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);
+static int nfs4_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev);
+
+struct inode_operations nfs4_dir_inode_operations = {
+	create: nfs4_create,
+	lookup: nfs4_lookup,
+	revalidate: nfs4_revalidate,
+	link: nfs4_link,
+	unlink: nfs4_unlink,
+	symlink: nfs4_symlink,
+	mkdir: nfs4_mkdir,
+	rmdir: nfs4_remove,
+	mknod: nfs4_mknod,
+	rename: nfs4_rename,
+	setattr: nfs4_setattr,
+	lookup_open: nfs4_lookup_open,
+#if NFS4ACL
+	permission: nfs4_posix_acl_permission,
+	get_posix_acl: nfs4_acl_get_inode,
+	set_posix_acl: nfs4_acl_set_inode,
+	get_ext_attr: nfs4_get_ext_attr,
+	set_ext_attr: nfs4_set_ext_attr
+#endif
+};
+
+struct file_operations nfs4_dir_operations = {
+	read: generic_read_dir,
+	readdir: nfs4_readdir,
+};
+
+/*
+ * Code common to nfs4_lookup_revalidate(), nfs4_lookup().
+ */
+int
+nfs4fs_do_lookup(struct dentry *dentry)
+{
+	struct inode *parent_inode = dentry->d_parent->d_inode;
+	struct nfs4fs_compound compound;
+	int do_reval;
+	int status;
+
+	NFS4_ASSERT(kernel_locked());
+
+	do_reval = nfs4_need_reval(parent_inode);	
+	compound.u.secinfo.dentry = dentry;
+
+	nfs4fs_setup_compound(&compound, dentry->d_sb, "do_lookup()");
+	nfs4fs_setup_putfh(&compound, parent_inode);
+	if (do_reval)
+		nfs4fs_setup_getattr(&compound, nfs4_standard_bitmap);	
+	nfs4fs_setup_lookup(&compound, &dentry->d_name);
+	nfs4fs_setup_getattr(&compound, nfs4_standard_bitmap);
+	nfs4fs_setup_getfh(&compound);
+
+	if ((status = nfs4fs_call_compound(&compound)))
+		goto out;
+	if ((status = nfs4fs_handle_putfh(&compound)))
+		goto out;
+	if (do_reval) {
+		if ((status = nfs4fs_handle_getattr_inode(&compound, parent_inode)))
+			goto out;
+	}
+	if ((status = nfs4fs_handle_lookup(&compound, dentry)))
+		goto out;
+	if ((status = nfs4fs_handle_getattr_dentry(&compound, dentry, NULL)))
+		goto out;
+	if ((status = nfs4fs_handle_getfh(&compound, dentry->d_inode)))
+		goto out;
+	
+out:
+	nfs4fs_release_compound(&compound);
+	return status;
+}
+
+/*
+ * The next two routines are from nfsv3 and test whether dentry revalidation
+ * is necessary for positive and negative dentries, respectively.
+ */
+static inline int nfs4_dentry_force_reval(struct dentry *dentry, int flags)
+{
+	struct inode *inode = dentry->d_inode;
+	unsigned long timeout;
+
+	NFS4_ASSERT(kernel_locked());
+	NFS4_ASSERT(inode != NULL);
+	
+	timeout = NFS4_CACHE_TIMEOUT(inode);
+
+	/*
+	 * If it's the last lookup in a series, we use a stricter
+	 * cache consistency check by looking at the parent mtime.
+	 *
+	 * If it's been modified in the last hour, be really strict.
+	 * (This still means that we can avoid doing unnecessary
+	 * work on directories like /usr/share/bin etc which basically
+	 * never change).
+	 */
+	if (!(flags & LOOKUP_CONTINUE)) {
+		long diff = CURRENT_TIME - dentry->d_parent->d_inode->i_mtime;
+
+		if (diff < 15*60)
+			timeout = 0;
+	}
+	
+	return time_after(jiffies,dentry->d_time + timeout);
+}
+
+/*
+ * We judge how long we want to trust negative
+ * dentries by looking at the parent inode mtime.
+ *
+ * If mtime is close to present time, we revalidate
+ * more often.
+ */
+#define NFS_REVALIDATE_NEGATIVE (1 * HZ)
+static inline int nfs4_neg_need_reval(struct dentry *dentry)
+{
+	struct inode *dir = dentry->d_parent->d_inode;
+	unsigned long timeout = NFS4_CACHE_TIMEOUT(dir);
+	long diff = CURRENT_TIME - dir->i_mtime;
+
+	NFS4_ASSERT(kernel_locked());
+	NFS4_ASSERT(dentry->d_inode == NULL);
+
+	if (diff < 5*60 && timeout > NFS_REVALIDATE_NEGATIVE)
+		timeout = NFS_REVALIDATE_NEGATIVE;
+
+	return time_after(jiffies, dentry->d_time + timeout);
+}
+
+/*
+ * d_op->revalidate(): called by the VFS when doing a pathname lookup, to revalidate each
+ * component of the pathname.  Dentry will be hashed, but may be positive or negative.
+ *   The "flags" parameter consists of flags of the form LOOKUP_FOLLOW, etc.
+ *   Locks held: none.
+ */
+int
+nfs4_lookup_revalidate(struct dentry *dentry, int flags)
+{
+	struct inode *dir, *inode;
+	int status;
+
+	nfsv4_printk(level2, "nfs4_lookup_revalidate: starting with dentry=\"%.*s\", "
+		     "flags=%d\n", (int)dentry->d_name.len, dentry->d_name.name, flags);
+	nfsv4_printk(reval, "nfs4_lookup_revalidate: jiffies=%ld, dtime=%ld, timeout=%ld\n",
+		     jiffies, dentry->d_time, NFS4_CACHE_TIMEOUT(dentry->d_parent->d_inode));
+	
+	lock_kernel();
+	dir = dentry->d_parent->d_inode;
+	inode = dentry->d_inode;
+	
+	/*
+	 * If we don't have an inode, let's look at the parent
+	 * directory mtime to get a hint about how often we
+	 * should validate things..
+	 */
+	if (!inode) {
+		if (!nfs4_neg_need_reval(dentry)) {
+			nfsv4_printk(reval, "neg_need_reval: revalidation not needed\n");
+			goto out_valid;
+		}
+		/*
+		 * XXX: nfsv3 does something odd here.  Instead of revalidating
+		 * the negative dentry, it returns failure, which means that the
+		 * VFS will drop the dentry, allocate a new one, and do a fresh
+		 * ->lookup().  What is the reason for doing this?
+		 *
+		 * We just fall through and let the dentry be revalidated.
+		 */
+	}
+	else if (is_bad_inode(inode)) {
+		nfsv4_printk(level1, "nfs_lookup_validate: %s/%s has dud inode\n",
+			     dentry->d_parent->d_name.name, dentry->d_name.name);
+		goto out_bad;
+	}
+	else if (!nfs4_dentry_force_reval(dentry, flags)) {
+		nfsv4_printk(reval, "dentry_force_reval: revalidation not needed\n");
+		goto out_valid;
+	}
+
+	if (IS_ROOT(dentry)) {
+		/*
+		 * Special behavior in this case; revalidate without lookup.
+		 */
+		NFS4_ASSERT(inode != NULL);
+		nfsv4_printk(reval, "nfs4_lookup_revalidate: revalidating root\n");
+		if (nfs4_revalidate_inode(inode))
+			goto out_bad;
+		goto out_valid;
+	}
+
+	nfsv4_printk(reval, "nfs4_lookup_revalidate: dentry will be revalidated\n");
+
+	/*
+	 * If the dentry is negative and the lookup returns success, we just
+	 * instantiate an inode and return success.  On the other hand, if the
+	 * dentry is positive and the lookup returns NOENT, we cannot in general
+	 * delete the inode: we must return failure and let the VFS call
+	 * d_invalidate().
+	 */
+	if ((status = nfs4fs_do_lookup(dentry)))
+		goto out_lookup_failed;
+	
+out_valid:
+	status = 1;  /* success */	
+out:
+	unlock_kernel();
+	nfsv4_printk(level2, "nfs4_lookup_revalidate: returning %s\n\n", status? "ok" : "err");
+	return status;
+out_lookup_failed:
+	/* We use dentry->d_inode instead of inode here, since we may have slept... */
+	if (status == -ENOENT && !dentry->d_inode)
+		goto out_valid;
+out_bad:
+	status = 0;  /* failure */
+	goto out;
+}
+
+/*
+ * d_op->d_delete(): called from dput() when d_count goes to 0.
+ * This cannot block -- caller holds the dcache spinlock.
+ * If return value is nonzero, dentry will be unhashed and killed immediately.
+ * Otherwise, the dentry can be saved for later use.
+ */
+static int
+nfs4_dentry_delete(struct dentry *dentry)
+{
+	/* Ensure that sillyrenamed files are cleaned up... */
+	return (dentry->d_flags & DCACHE_NFSFS_RENAMED);
+}
+
+/*
+ * d_op->d_iput(): called when the dentry loses inode.
+ * We use it to clean up silly-renamed files.
+ *
+ * NB: The default ->d_iput() method is just iput().
+ * In other words, this routine is responsible for decreasing
+ * the inode's refcount, in addition to anything else it does.
+ */
+static void
+nfs4_silly_dentry_iput(struct dentry *dentry, struct inode *inode)
+{
+	nfsv4_printk(remove, "nfs4_silly_dentry_iput: starting %p\n", dentry);
+
+	if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
+		nfsv4_printk(remove, "nfs4_silly_dentry_iput: calling finish_unlink\n");
+		lock_kernel();
+		nfs4_finish_sillyrename(dentry);
+		unlock_kernel();
+	}
+	iput(inode);
+	
+	nfsv4_printk(remove, "nfs4_silly_dentry_iput: done\n\n");
+}
+
+/*
+ * i_op->lookup(): called just after the VFS creates a new dentry, to instantiate an inode.
+ *   The dentry parameter will be negative and unhashed, but its parent will be set.
+ *   This routine is also responsible for hashing the dentry, and instantiating the d_ops.
+ *   Locks held: BKL, dir_inode->i_sem
+ */
+static struct dentry *
+nfs4_lookup(struct inode *dir_inode, struct dentry *dentry)
+{
+	int status;
+
+	nfsv4_printk(level2, "nfs4_lookup(%.*s/%.*s): starting\n",
+		     (int)dentry->d_parent->d_name.len, dentry->d_parent->d_name.name,
+		     (int)dentry->d_name.len, dentry->d_name.name);
+
+	status = nfs4fs_do_lookup(dentry);
+	if (!status || status == -ENOENT) {
+		if (d_unhashed(dentry))       /* paranoid */
+			d_rehash(dentry);
+		dentry->d_op = &nfs4_dentry_operations;
+		status = 0;
+	}
+	
+	nfsv4_printk(level2, "nfs4_lookup: returning status %d\n\n", status);
+	return ERR_PTR(status);
+}
+
+/*
+ * i_op->readdir()
+ *   Note: a filldir_t looks like
+ * int filler(void *buf, const char *name, int namlen, off_t offset, ino_t ino,
+ *            unsigned int d_type).
+ * d_type can be one of DT_DIR, etc.  XXX : should we bother setting this?  we can do it
+ * without much extra overhead by requesting a type attribute in the readdir.. on the other
+ * hand, the v3 code just uses DT_UNKNOWN for all entries!
+ *   Contrary to what it says in the v3 nfsd code, i_op->readdir() does _not_ have to call
+ * the 'filldir' function with a null string to indicate that the last entry has been read!
+ *   The page cache stores entries in the form
+ *      next(1 byte), namelen, name (no XDR "baggage"), type (in form DT_*), ino (64 bit)
+ *   next==0 if EOF, next==1 if more entries on next page, next==2 if more entries on same
+ * page.
+ *   Locks held: BKL, dir_inode->i_sem, dir_inode->i_zombie
+ *
+ * TODO: At the present time, we have a brain-damaged readdir implementation which
+ * needs considerable improvement.  This urgently needs to be fixed!
+ *
+ * One problem: client always caches entire directories at a time, this may be a problem
+ * for pathologically large, or rapidly changing, directories
+ *
+ * Another problem: we don't cope with the case where a page is cached and then gets flushed
+ * from the cache under memory pressure!
+ */
+static int
+readdir_prepare_pagecache(struct file *filp)
+{
+	struct dentry *dentry = filp->f_dentry;
+	struct inode *inode = dentry->d_inode;
+	struct dentry *parent_dentry;
+	struct nfs4fs_compound compound;
+	struct page *page[2];
+	u32 *start, *p;
+	unsigned int offset, index, pos;
+	u64 cookie;
+	verifier4 verifier;
+	u32 bytes_read;
+	int eof, try_again;
+	int try = 0;
+	int status = 0;
+
+	/* caller is responsible for revalidating inode */
+	NFS4_ASSERT(kernel_locked());
+	
+	nfsv4_printk(readdir, "nfs4_readdir: prepare_pagecache starting\n");
+	
+	if (NFS4_HAS_SNAPSHOT(inode)) {
+		nfsv4_printk(readdir, "nfs4_readdir: snapshot already cached\n");
+		goto out;
+	}
+
+	nfsv4_printk(readdir, "nfs4_readdir: reading directory snapshot into page cache\n");
+	
+	/* Get a dentry for the parent directory.  A bit tricky if we're at the mountpoint of
+	   the NFS filesystem.  XXX: should we actually traverse the mountpoint, or consider
+	   our root to be its own parent?  What do other filesystems do? */
+	
+	parent_dentry = dentry->d_parent;
+	if (parent_dentry == dentry) {
+		struct vfsmount *mnt = mntget(filp->f_vfsmnt);
+		struct dentry *d = dget(dentry);
+		do {
+			if (!follow_up(&mnt, &d))
+				break;
+		} while (d->d_parent == d);
+		parent_dentry = d->d_parent;
+		dput(d);
+		mntput(mnt);
+	}
+
+top:
+	
+	/* Read directory contents into page cache, using several READDIR ops if
+	   necessary.  According to the NFSv4 protocol, the server will not return entries
+	   for '.' and '..', so we write those entries into the page cache "by hand"
+	   before starting the READDIR loop. */
+
+	/* XXX: think about what bits might be set for this page.  PG_error? */
+	status = -ENOMEM;
+	if (!(page[0] = grab_cache_page(inode->i_mapping, 0))) {
+		nfsv4_printk(readdir2, "nfs4_readdir: couldn't grab initial page!\n");
+		goto out;
+	}
+        p = start = kmap(page[0]);
+
+	/* Write entry '.' into the page cache */
+	*p++ = 1;					/* next */
+	*p++ = 0;					/* fake cookie */
+	*p++ = 0;					/* fake cookie */
+	*p++ = 1;                                	/* namelen */
+	memcpy(p, ".", 1);				/* name */
+	p++;
+	*p++ = 2;					/* bmlen */
+	*p++ = 0x00100002;				/* bmval[0] */
+	*p++ = 0;					/* bmval[1] */
+	*p++ = 12;					/* attrlen */
+	*p++ = DT_DIR;					/* attrval */
+	*p++ = 0;					/* ino */
+	*p++ = (u32)(inode->i_ino);			/* ino */
+
+	/* Write entry '..' into the page cache */
+	*p++ = 1;					/* next */
+	*p++ = 0;					/* fake cookie */
+	*p++ = 0;					/* fake cookie */
+	*p++ = 2;                                	/* namelen */
+	memcpy(p, "..", 2);				/* name */
+	p++;
+	*p++ = 2;					/* bmlen */
+	*p++ = 0x00100002;				/* bmval[0] */
+	*p++ = 0;					/* bmval[1] */
+	*p++ = 12;					/* attrlen */
+	*p++ = DT_DIR;					/* attrval */
+	*p++ = 0;					/* ino */
+	*p++ = (u32)(parent_dentry->d_inode->i_ino);	/* ino */
+
+	cookie = 0;
+	memset(verifier, 0, sizeof(verifier4));
+	offset = (char *)p - (char *)start;
+	status = 0;
+	
+	*p++ = 0;               /* add an extra NULL pointer, just to be paranoid... */
+	kunmap(page[0]);
+	UnlockPage(page[0]);
+	put_page(page[0]);
+
+	do {
+		status = -EIO;
+		if (++try > 100) {
+			printk("DOH!\n");
+			goto out;
+		}
+		
+		index = offset >> PAGE_CACHE_SHIFT;
+		pos = offset & (PAGE_CACHE_SIZE - 1);
+		NFS4_ASSERT((pos & 3) == 0);
+		
+		status = -ENOMEM;
+		if (!(page[0] = grab_cache_page(inode->i_mapping, index)))
+			goto out;
+		if (!(page[1] = grab_cache_page(inode->i_mapping, index + 1)))
+			goto out_put_page;
+
+		nfsv4_printk(readdir, "filling pagecache(%d:%d) cookie=%Ld\n",
+			     index, pos, cookie);
+		
+		nfs4fs_setup_compound(&compound, inode->i_sb, "nfs4_readdir()");
+		nfs4fs_setup_putfh(&compound, inode);
+		nfs4fs_setup_readdir(&compound, cookie, verifier, 2, page, pos);
+
+		if ((status = nfs4fs_call_compound(&compound)))
+			goto out_release;
+		if ((status = nfs4fs_handle_putfh(&compound)))
+			goto out_release;
+		if ((status = nfs4fs_handle_readdir(&compound, inode, &cookie,
+						    verifier, &bytes_read, &eof, &try_again)))
+			goto out_release;
+		nfs4fs_release_compound(&compound);
+
+		if (try_again) {
+			status = -EIO;
+			if (++try > 10)
+				goto out;
+			goto top;
+		}
+		offset += (bytes_read - 8);   /* supress final 'next' pointer, eof */
+	} while (!eof);
+
+	NFS4_FLAGS(inode) |= NFS4_INO_SNAPSHOT;
+	NFS4_DIRECTORY_SIZE(inode) = offset;
+
+out:
+	nfsv4_printk(readdir, "nfs4_readdir: prepare_pagecache returning %d\n", status);
+	return status;
+out_release:
+	nfs4fs_release_compound(&compound);
+	goto out;
+out_put_page:
+	UnlockPage(page[0]);
+	put_page(page[0]);
+	goto out;
+}
+
+static int
+nfs4_readdir(struct file *filp, void *opaque, filldir_t filler)
+{
+	struct dentry *dentry = filp->f_dentry;
+	struct inode *inode = dentry->d_inode;
+	loff_t fpos = filp->f_pos;
+	unsigned int index, last_index;
+	unsigned int offset, last_offset;
+	u32 *start, *p, *end;
+	struct page *page = NULL;
+	u32 more, namelen, type, bmlen, hi, lo;
+	u64 fileid;
+	unsigned int nwords;
+	char name[100];    /* XXX: fix! */
+	int status;
+
+	nfsv4_printk(level2, "nfs4_readdir(%.*s/%.*s): starting, fpos=%Ld\n",
+		     (int)dentry->d_parent->d_name.len, dentry->d_parent->d_name.name,
+		     (int)dentry->d_name.len, dentry->d_name.name, fpos);
+
+	status = -EIO;
+	if (fpos > OFFT_OFFSET_MAX) {
+		nfsv4_printk(readdir2, "nfs4_readdir: fpos > OFFT_OFFSET_MAX\n");
+		goto out;
+	}
+	if (fpos & 3) {
+		nfsv4_printk(readdir2, "nfs4_readdir: fpos not divisible by 3!\n");
+		goto out;
+	}
+	
+	if ((status = nfs4_revalidate_inode(inode))) {
+		nfsv4_printk(readdir2, "nfs4_readdir: revalidate_inode() failed!\n");
+		goto out;
+	}
+	if ((status = readdir_prepare_pagecache(filp))) {
+		nfsv4_printk(readdir2, "nfs4_readdir: prepare_pagecache() failed!\n");
+		goto out;
+	}
+	NFS4_ASSERT((NFS4_DIRECTORY_SIZE(inode) & 3) == 0);
+	if (fpos >= NFS4_DIRECTORY_SIZE(inode))
+		goto out;
+	
+	/*
+	 * readdir_prepare_pagecache() has already done the hard part, populating
+	 * the page cache with directory entries.  We just read through the pages
+	 * and call the filldir function.
+	 */
+	
+	last_index = NFS4_DIRECTORY_SIZE(inode) >> PAGE_CACHE_SHIFT;
+	last_offset = NFS4_DIRECTORY_SIZE(inode) & (PAGE_CACHE_SIZE - 1);
+	index = fpos >> PAGE_CACHE_SHIFT;
+	offset = fpos & (PAGE_CACHE_SIZE - 1);
+	
+	status = -ENOMEM;
+	if (!(page = find_lock_page(inode->i_mapping, index))) {  /* read past EOF */
+		nfsv4_printk(readdir2, "nfs4_readdir: page not found\n");
+		goto out;
+	}
+	start = kmap(page);
+	p = start + (offset >> 2);
+	if (index == last_index)
+		end = start + (last_offset >> 2);
+	else
+		end = start + (PAGE_CACHE_SIZE >> 2);
+
+#if 0
+	printk("PAGE:\n");
+	hex_dump(p, (end - p) << 2);
+#endif
+
+#define ADJUST(label)  do {							\
+	if (p >= end) {								\
+		kunmap(page);							\
+		UnlockPage(page);						\
+		put_page(page);							\
+		if (index++ == last_index) {					\
+			nfsv4_printk(readdir, "nfs4_readdir: breaking loop(%d)\n", __LINE__); \
+			goto label;						\
+		}								\
+		status = -ENOMEM;						\
+		if (!(page = find_lock_page(inode->i_mapping, index)))		\
+			goto out;						\
+		start = kmap(page);						\
+		p = start + ((char *)p - (char *)end);				\
+		if (index == last_index)					\
+			end = start + (last_offset >> 2);			\
+		else								\
+			end = start + (PAGE_CACHE_SIZE >> 2);			\
+	}									\
+} while (0)
+
+	/*
+	 * Invariant: at top,
+	 *    page == pointer to page, kmapped, locked, reference claimed
+	 *    index == page index
+	 *    start == start of page
+	 *    p == current position in page
+	 *    end == end of page
+	 */
+top:
+	more = *p++;
+	if (more > 1) {
+		nfsv4_printk(readdir2, "more pointer not 0 or 1!\n");
+		goto page_garbage_unlock;
+	}
+	if (more == 0) {
+		status = 0;
+		ADJUST(out);
+		nfsv4_printk(readdir2, "more pointer 0, more data in snapshot!\n");
+		goto page_garbage_unlock;
+	}
+	
+	p += 2;        /* skip cookie */
+	
+	ADJUST(page_garbage);
+	namelen = *p++;
+	if (namelen > sizeof(name)) {
+		nfsv4_printk(readdir2, "name too long!\n");
+		goto page_garbage_unlock;
+	}
+	
+	ADJUST(page_garbage);
+	nwords = end - p;
+	if (XDR_QUADLEN(namelen) <= nwords) {
+		memcpy(name, p, namelen);
+		p += XDR_QUADLEN(namelen);
+	}
+	else {
+		memcpy(name, p, nwords << 2);
+		p = end;
+		ADJUST(page_garbage);
+		memcpy(name + (nwords << 2), p, namelen - (nwords << 2));
+		p += XDR_QUADLEN(namelen) - nwords;
+	}
+
+	ADJUST(page_garbage);
+	bmlen = *p++;
+	if (bmlen != 1 && bmlen != 2) {
+		nfsv4_printk(readdir2, "bad bmlen (%d)!\n", bmlen);
+		goto page_garbage_unlock;
+	}
+
+	ADJUST(page_garbage);
+	if (*p++ != 0x00100002) {
+		nfsv4_printk(readdir2, "bad bmval[0]!\n");
+		goto page_garbage_unlock;
+	}
+
+	ADJUST(page_garbage);
+	if (bmlen == 2 && *p++ != 0) {
+		nfsv4_printk(readdir2, "bad bmval[1]!!\n");
+		goto page_garbage_unlock;
+	}
+
+	ADJUST(page_garbage);
+	if (*p++ != 12) {
+		nfsv4_printk(readdir2, "bad attrlen!\n");
+		goto page_garbage_unlock;
+	}
+
+	ADJUST(page_garbage);
+	type = *p++;
+
+	ADJUST(page_garbage);
+	hi = *p++;
+
+	ADJUST(page_garbage);
+	lo = *p++;
+	fileid = ((u64)hi << 32) | (u64)lo;
+
+	if (!fileid) {     /* XXX: linux glibc gets confused by zero inode number */
+		nfsv4_printk(level1, "nfs4: warning: inode number hack!\n");
+		fileid = 1;
+	}
+
+	status = 0;
+	nfsv4_printk(readdir, "filling \"%.*s\", fpos=%Ld, fileid=%Ld\n",
+		     (int)namelen, name, fpos, fileid);
+	if (filler(opaque, name, namelen, fpos, fileid, type)) {
+		nfsv4_printk(readdir, "nfs4_readdir: filler returned error\n");
+		goto out_unlock;
+	}
+	fpos = ((loff_t)index << PAGE_CACHE_SHIFT) + ((char *)p - (char *)start);
+	filp->f_pos = fpos;
+	goto top;
+
+out_unlock:
+	kunmap(page);
+	UnlockPage(page);
+	put_page(page);
+out:
+	nfsv4_printk(level2, "nfs4_readdir: returning status %d\n\n", status);
+	return status;
+	
+page_garbage_unlock:
+	kunmap(page);
+	UnlockPage(page);
+	put_page(page);
+page_garbage:
+	printk(KERN_WARNING "nfs4: garbage page in nfs4_readdir!\n");
+	nfs4_zap_caches(inode);
+	status = -EIO;
+	goto out;
+}
+
+/*
+ * i_op->link()
+ *   The new dentry can be either positive or negative, but will always be hashed.
+ *   We no longer try to coalesce a LOOKUP into the COMPOUND call to get the new
+ * filehandle; instead we just drop the new dentry to force a new lookup, just like
+ * nfsv3 does.  This is the only way to avoid race conditions with other clients.
+ *   Locks held: BKL, dir_inode->i_sem, dir_inode->i_zombie
+ */
+static int
+nfs4_link(struct dentry *old_dentry, struct inode *dir_inode, struct dentry *dentry)
+{
+	struct inode *inode = old_dentry->d_inode;
+	struct nfs4fs_compound compound;
+	int status;
+
+	nfsv4_printk(level2, "nfs4_link: starting on %.*s->%.*s/%.*s\n",
+		     (int)old_dentry->d_name.len, old_dentry->d_name.name,
+		     (int)dentry->d_parent->d_name.len, dentry->d_parent->d_name.name,
+		     (int)dentry->d_name.len, dentry->d_name.name);
+
+	nfs4fs_setup_compound(&compound, dir_inode->i_sb, "nfs4_link()");
+	nfs4fs_setup_putfh(&compound, inode);
+	nfs4fs_setup_savefh(&compound);
+	nfs4fs_setup_putfh(&compound, dir_inode);
+	nfs4fs_setup_link(&compound, dentry);
+
+	/*
+	 * We drop the existing dentry in advance to force a new lookup.
+	 */
+	d_drop(dentry);
+
+	/*
+	 * Since creating the link will add a directory entry, we destroy
+	 * any cached READDIR result in advance.  Note that the directory
+	 * semaphores prevent us from racing against ->readdir().
+	 *
+	 * XXX: nfsv3 calls zap_caches() here, which seems like overkill
+	 * and hurts performance.  Same comment applies to ->symlink(),
+	 * ->mkdir(), ->remove(), ->rename(), and ->lookup_open().
+	 */
+	nfs4_zap_page_cache(dir_inode);
+	
+	if ((status = nfs4fs_call_compound(&compound)))
+		goto out_release;
+	if ((status = nfs4fs_handle_putfh(&compound)))
+		goto out_release;
+	if ((status = nfs4fs_handle_savefh(&compound)))
+		goto out_release;
+	if ((status = nfs4fs_handle_putfh(&compound)))
+		goto out_release;
+	if ((status = nfs4fs_handle_link(&compound, dentry, inode)))
+		goto out_release;
+
+out_release:
+	nfs4fs_release_compound(&compound);
+	nfsv4_printk(link, "nfs4_link: returning status %d\n\n", status);
+	return status;
+}
+
+/*
+ * i_op->symlink()
+ *   Extremely similar to i_op->link().  Note that no attributes need to be set for symlinks.
+ *   Locks held: BKL, dir_inode->i_sem, dir_inode->i_zombie
+ */
+static int
+nfs4_symlink(struct inode *dir_inode, struct dentry *dentry, const char *name)
+{
+	struct nfs4fs_compound compound;
+	int status;
+
+	nfsv4_printk(level2, "nfs4_symlink: starting on %.*s/%.*s -> \"%s\"\n",
+		     (int)dentry->d_parent->d_name.len, dentry->d_parent->d_name.name,
+		     (int)dentry->d_name.len, dentry->d_name.name, name);
+	
+	nfs4fs_setup_compound(&compound, dentry->d_sb, "nfs4_symlink()");
+	nfs4fs_setup_putfh(&compound, dir_inode);
+	nfs4fs_setup_create_symlink(&compound, dentry, name);
+	nfs4fs_setup_getattr(&compound, nfs4_standard_bitmap);
+	nfs4fs_setup_getfh(&compound);
+
+	/* Invalidate any cached READDIR response... */
+	nfs4_zap_page_cache(dir_inode);
+	
+	if ((status = nfs4fs_call_compound(&compound)))
+		goto out_release;
+	if ((status = nfs4fs_handle_putfh(&compound)))
+		goto out_release;
+	if ((status = nfs4fs_handle_create(&compound, dentry)))
+		goto out_release;
+	if ((status = nfs4fs_handle_getattr_dentry(&compound, dentry, NULL)))
+		goto out_release;
+	if ((status = nfs4fs_handle_getfh(&compound, dentry->d_inode)))
+		goto out_release;
+
+out_release:
+	nfs4fs_release_compound(&compound);
+	nfsv4_printk(level2, "nfs4_symlink: returning status %d\n\n", status);
+	return status;
+}
+
+/*
+ * i_op->mkdir(): essentially identical to i_op->symlink().
+ */
+static int
+nfs4_mkdir(struct inode *dir_inode, struct dentry *dentry, int mode)
+{
+	struct nfs4fs_compound compound;
+	int status;
+	
+	nfsv4_printk(level2, "nfs4_mkdir: starting on %.*s/%.*s, mode=0%o\n",
+		     (int)dentry->d_parent->d_name.len, dentry->d_parent->d_name.name,
+		     (int)dentry->d_name.len, dentry->d_name.name, mode);
+
+	/* Probably not necessary... */
+	mode = (mode & S_IALLUGO) | S_IFDIR;
+
+	nfs4fs_setup_compound(&compound, dentry->d_sb, "nfs4_mkdir()");
+	nfs4fs_setup_putfh(&compound, dir_inode);
+	nfs4fs_setup_create_dir(&compound, dentry, mode);
+	nfs4fs_setup_getattr(&compound, nfs4_standard_bitmap);
+	nfs4fs_setup_getfh(&compound);
+	
+	/* Invalidate any cached READDIR response... */
+	nfs4_zap_page_cache(dir_inode);
+	
+	if ((status = nfs4fs_call_compound(&compound)))
+		goto out_release;
+	if ((status = nfs4fs_handle_putfh(&compound)))
+		goto out_release;
+	if ((status = nfs4fs_handle_create(&compound, dentry)))
+		goto out_release;
+	if ((status = nfs4fs_handle_getattr_dentry(&compound, dentry, NULL)))
+		goto out_release;
+	if ((status = nfs4fs_handle_getfh(&compound, dentry->d_inode)))
+		goto out_release;
+
+out_release:
+	nfs4fs_release_compound(&compound);
+	nfsv4_printk(level2, "nfs4_mkdir: returning status %d\n\n", status);
+	return status;
+}
+
+/* 
+ * i_op->unlink()
+ *   This routine doesn't have to manipulate the dentry at all, just return success or failure.
+ * If it returns success, the VFS will call d_delete(), which takes care of everything; if it
+ * returns failure, the VFS does nothing.
+ *   We now sillyrename the file if d_count > 1.
+ *   Locks held: BKL, dir_inode->i_sem, dir_inode->i_zombie
+ */
+static int
+nfs4_unlink(struct inode *dir_inode, struct dentry *dentry)
+{
+	int rehash;
+	int status = 0;
+	
+	nfsv4_printk(level2, "nfs4_unlink(%.*s/%.*s): starting\n",
+		     (int)dentry->d_parent->d_name.len, dentry->d_parent->d_name.name,
+		     (int)dentry->d_name.len, dentry->d_name.name);
+	
+	if (!NFS4_SILLY_RENAME(dir_inode->i_sb)) {
+		nfsv4_printk(remove, "nfs4_unlink: silly rename disabled, removing file\n");
+		goto do_remove;
+	}
+	if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
+		nfsv4_printk(remove, "nfs4_unlink: already sillyrenamed, no-op\n");
+		goto out;
+	}
+	if (atomic_read(&dentry->d_count) > 1) {
+		nfsv4_printk(remove, "nfs4_silly_unlink: silly renaming file\n");
+		status = nfs4_sillyrename(dir_inode, dentry);
+		goto out;
+	}
+	
+do_remove:
+	/*
+	 * Unhash the dentry while we remove the file.
+	 * This prevents anyone else from acquiring references to the dentry, which
+	 * would defeat our policy of "remove if d_count==1, sillyrename otherwise".
+	 */
+	rehash = 0;
+	if (!d_unhashed(dentry)) {
+		d_drop(dentry);
+		rehash = 1;
+	}
+
+	status = nfs4_remove(dir_inode, dentry);
+
+	/*
+	 * Note: While we slept, no new dentry by the same name could have been
+	 * hashed.  This is because we hold dir_inode->i_sem.
+	 */
+	if (rehash)
+		d_rehash(dentry);
+	
+out:
+	nfsv4_printk(level2, "nfs4_unlink(): returning status %d\n", status);
+	return status;
+}
+
+/*
+ * i_op->rmdir()
+ *   The semantics of this routine are identical to those of i_op->unlink()
+ *   Note: the VFS does not check to see whether the directory is empty; that is the
+ * responsibility of the lower-level filesystem.
+ *   Locks held: BKL, dir_inode->i_sem, dir_inode->i_zombie, dentry->d_inode->i_zombie
+ */
+static int
+nfs4_remove(struct inode *dir_inode, struct dentry *dentry)
+{
+	struct nfs4fs_compound compound;
+	int status;
+
+	nfsv4_printk(level2, "nfs4_remove: starting on %.*s/%.*s\n",
+		     (int)dentry->d_parent->d_name.len, dentry->d_parent->d_name.name,
+		     (int)dentry->d_name.len, dentry->d_name.name);
+	
+	nfs4fs_setup_compound(&compound, dentry->d_sb, "nfs4_remove()");
+	nfs4fs_setup_putfh(&compound, dir_inode);
+	nfs4fs_setup_remove(&compound, &dentry->d_name);
+	
+	nfs4_zap_page_cache(dir_inode);
+
+	if ((status = nfs4fs_call_compound(&compound)))
+		goto out_release;
+	if ((status = nfs4fs_handle_putfh(&compound)))
+		goto out_release;
+	if ((status = nfs4fs_handle_remove(&compound, dir_inode, dentry)))
+		goto out_release;
+
+out_release:
+	nfs4fs_release_compound(&compound);
+	nfsv4_printk(level2, "nfs4_remove: returning status %d\n\n", status);
+	return status;
+}
+
+/*
+ * i_op->mknod(): essentially identical to i_op->symlink().
+ */
+static int
+nfs4_mknod(struct inode *dir_inode, struct dentry *dentry, int mode, int rdev)
+{
+	struct nfs4fs_compound compound;
+	int status;
+
+	nfsv4_printk(level2, "nfs4_mknod: starting on %.*s/%.*s, mode=0%o, dev=%d %d\n",
+		     (int)dentry->d_parent->d_name.len, dentry->d_parent->d_name.name,
+		     (int)dentry->d_name.len, dentry->d_name.name, mode,
+		     MAJOR(rdev), MINOR(rdev));
+
+	NFS4_ASSERT(S_ISFIFO(mode) || S_ISBLK(mode) || S_ISCHR(mode) || S_ISSOCK(mode));
+	
+	nfs4fs_setup_compound(&compound, dentry->d_sb, "nfs4_mknod()");
+	nfs4fs_setup_putfh(&compound, dir_inode);
+	nfs4fs_setup_create_special(&compound, dentry, mode, rdev);
+	nfs4fs_setup_getattr(&compound, nfs4_standard_bitmap);
+	nfs4fs_setup_getfh(&compound);
+
+	/* Invalidate any cached READDIR response... */
+	nfs4_zap_page_cache(dir_inode);
+	
+	if ((status = nfs4fs_call_compound(&compound)))
+		goto out_release;
+	if ((status = nfs4fs_handle_putfh(&compound)))
+		goto out_release;
+	if ((status = nfs4fs_handle_create(&compound, dentry)))
+		goto out_release;
+	if ((status = nfs4fs_handle_getattr_dentry(&compound, dentry, NULL)))
+		goto out_release;
+	if ((status = nfs4fs_handle_getfh(&compound, dentry->d_inode)))
+		goto out_release;
+
+out_release:
+	nfs4fs_release_compound(&compound);
+	nfsv4_printk(level2, "nfs4_mknod: returning status %d\n\n", status);
+	return status;
+}
+
+/*
+ * This was moved to its own function so that only one nfs4fs_compound would be on
+ * the stack at one time -- these are huge and can really eat up stack space.
+ */
+static int
+do_rename(struct inode *old_dir_inode, struct dentry *old_dentry,
+	  struct inode *new_dir_inode, struct dentry *new_dentry)
+{
+	struct nfs4fs_compound compound;
+	int status;
+
+	nfs4fs_setup_compound(&compound, old_dir_inode->i_sb, "nfs4_rename()");
+	nfs4fs_setup_putfh(&compound, old_dir_inode);
+	nfs4fs_setup_savefh(&compound);
+	nfs4fs_setup_putfh(&compound, new_dir_inode);
+	nfs4fs_setup_rename(&compound, old_dentry, new_dentry);
+
+	/*
+	 * Analagously to nfs4_link(), we drop the existing dentry in advance
+	 * to force a new lookup.  XXX: nfsv3 doesn't do this; why?
+	 */
+	d_drop(old_dentry);
+	
+	/* Flush any cached READDIR response.. */
+	nfs4_zap_page_cache(old_dir_inode);
+	nfs4_zap_page_cache(new_dir_inode);
+	
+	if ((status = nfs4fs_call_compound(&compound)))
+		goto out_release;
+	if ((status = nfs4fs_handle_putfh(&compound)))
+		goto out_release;
+	if ((status = nfs4fs_handle_savefh(&compound)))
+		goto out_release;
+	if ((status = nfs4fs_handle_putfh(&compound)))
+		goto out_release;
+	if ((status = nfs4fs_handle_rename(&compound, old_dentry, new_dentry)))
+		goto out_release;
+
+out_release:
+	nfs4fs_release_compound(&compound);
+	return status;
+}
+
+/* XXX - temporary, to experiment with effect of rehashing vs. not */
+#define REHASH 1
+
+/*
+ * i_op->rename() : the mother of all vnode ops!
+ *   Locks held: BKL, old_dir_inode->i_sem, new_dir_inode->i_sem, old_dir_inode->i_zombie,
+ * new_dir_inode->i_zombie.  also, if new_dentry is an existing directory,
+ * new_dentry->d_inode->i_zombie.
+ */
+static int
+nfs4_rename(struct inode *old_dir_inode, struct dentry *old_dentry,
+	    struct inode *new_dir_inode, struct dentry *new_dentry)
+{
+	struct dentry *old_dir_dentry = old_dentry->d_parent;
+	struct dentry *new_dir_dentry = new_dentry->d_parent;
+	struct dentry *temp_dentry = NULL;
+#if REHASH
+	struct dentry *rehash = NULL;
+#endif
+	int status;
+
+	nfsv4_printk(level2, "nfs4_rename: starting on %.*s/%.*s->%.*s/%.*s\n",
+		     (int)old_dir_dentry->d_name.len, old_dir_dentry->d_name.name,
+		     (int)old_dentry->d_name.len, old_dentry->d_name.name,
+		     (int)new_dir_dentry->d_name.len, new_dir_dentry->d_name.name,
+		     (int)new_dentry->d_name.len, new_dentry->d_name.name);
+
+	/*
+	 * To prevent any new references to the target during the rename,
+	 * we unhash the dentry and free the inode in advance.
+	 */
+	if (!d_unhashed(new_dentry)) {
+		d_drop(new_dentry);
+#if REHASH
+		rehash = new_dentry;
+#endif
+	}
+	/*
+	 * XXX: Does nfsv3 adequately handle this case?
+	 */
+	status = -EIO;
+	if (new_dentry->d_flags & DCACHE_NFSFS_RENAMED) {
+		printk(KERN_WARNING "nfs4: rename over silly-renamed file?!\n");
+		goto out;
+	}
+
+	/*
+	 * If any of the following conditions applies, we don't have to sillyrename
+	 * the target: (a) sillyrename disabled for this mountpoint (-o nosilly),
+	 *             (b) target does not exist, i.e. negative dentry,
+	 *             (c) target is not in use, i.e. d_count==1
+	 */
+	if (!NFS4_SILLY_RENAME(old_dir_inode->i_sb)) {
+		nfsv4_printk(remove, "nfs4_unlink: silly rename disabled, doing rename\n");
+		goto do_rename;
+	}
+	if (!new_dentry->d_inode) {
+		nfsv4_printk(rename, "nfs4_rename: target dentry negative, doing rename\n");
+		goto do_rename;
+	}
+	status = -EBUSY;
+	if (S_ISDIR(new_dentry->d_inode->i_mode)) {
+		/*
+		 * XXX - is this actually possible?  nfsv3 seems to think so...
+		 */
+		nfsv4_printk(rename2, "nfs4_rename: target is directory?!\n");
+		goto out;
+	}
+	if (atomic_read(&new_dentry->d_count) == 1) {
+		nfsv4_printk(rename, "nfs4_rename: target not in use, doing rename\n");
+		goto do_rename;
+	}
+	
+	/*
+	 * If we get here, the target will be sillyrenamed.
+	 *
+	 * XXX - If the sillyrename returns an error, we fail the whole rename operation.
+	 *       This seems like the right choice, but nfsv3 seems to do otherwise (?!)
+	 *
+	 *       Similarly, if the new dentry is busy after the sillyrename, we return
+	 *       -EBUSY.  nfsv3's handling of this condition seems really brain-damaged.
+	 *       Maybe the right way to handle this is: sillyrename in a loop until the
+	 *       target is no longer busy?
+	 *
+	 * XXX - nfsv3 calls d_instantiate(temp_dentry, NULL); isn't this a no-op?
+	 */
+	nfsv4_printk(rename, "nfs4_rename: trying to silly rename target...\n");
+	status = -ENOMEM;               /* XXX: nfsv3 returns -EBUSY? */
+	if (!(temp_dentry = d_alloc(new_dentry->d_parent, &new_dentry->d_name))) {
+		nfsv4_printk(rename2, "nfs4_rename: couldn't alloc new dentry!\n");
+		goto out;
+	}
+	temp_dentry->d_op = &nfs4_dentry_operations;  /* paranoid */
+	if ((status = nfs4_sillyrename(new_dir_inode, new_dentry))) {
+		nfsv4_printk(rename2, "nfs4_rename: nfs4_sillyrename failed!\n");
+		goto out;
+	}
+	status = -EBUSY;
+	if (atomic_read(&temp_dentry->d_count) > 1) {
+		nfsv4_printk(rename2, "nfs4_rename: temp dentry is busy?!\n");
+		goto out;
+	}
+
+	new_dentry = temp_dentry;
+#if REHASH
+	rehash = temp_dentry;
+#endif
+	nfsv4_printk(rename, "nfs4_rename: done with silly rename, now doing real rename..\n");
+
+do_rename:
+	/*
+	 * ... prune child dentries and writebacks if needed
+	 */
+	if (atomic_read(&old_dentry->d_count) > 1) {
+		nfs4_sync_file(old_dentry->d_inode, NULL, 0, 0, FLUSH_WAIT);
+		shrink_dcache_parent(old_dentry);
+	}
+
+	status = do_rename(old_dir_inode, old_dentry, new_dir_inode, new_dentry);
+	
+out:
+	/* Note: No-one else could have rehashed it, since we have the i_sem... */
+#if REHASH
+	if (rehash)
+		d_rehash(rehash);
+#endif
+	if (temp_dentry)
+		dput(temp_dentry);
+	nfsv4_printk(level2, "nfs4_rename: returning status %d\n\n", status);
+	return status;
+}
--- clean/fs/nfs4fs/handle.c	Thu Feb  7 18:04:30 2002
+++ dirty/fs/nfs4fs/handle.c	Thu Feb  7 14:09:58 2002
@@ -0,0 +1,1257 @@
+/*
+ *  linux/fs/nfs4fs/handle.c
+ *
+ *  Copyright (c) 2002 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  Kendrick Smith <kmsmith@umich.edu>
+ *  Andy Adamson   <andros@umich.edu>
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the University nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This file implements "handler" routines for COMPOUND responses.
+ * These are called after the response has already been XDR decoded,
+ * and the on-the-wire response translated into a sequence of nfs4fs_op
+ * structures.  Each handler routine is responsible for taking the
+ * appropriate action based on the contents of one such structure.
+ * Here, the meaning of "appropriate" depends on the operation in
+ * question.  Example: handle_getattr() uses the attributes returned
+ * in the GETATTR response to refresh an inode.
+ */
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/fs.h>
+#include <linux/sunrpc/sched.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+
+#include <linux/nfs4/nfs4.h>
+#include <linux/nfs4/nfs4_debug.h>
+#include <linux/nfs4/nfs4fs_xdr.h>
+#include <linux/nfs4/nfs4fs.h>
+
+/*
+ * Convenient shorthand for handle functions...
+ */
+#define HANDLE_HEAD(name)						\
+	struct nfs4fs_##name *name = &cp->ops[cp->nops].u.name;		\
+	int status = cp->ops[cp->nops++].nfserr;			\
+	if (status)							\
+		goto out_nfserr
+
+#define HANDLE_TAIL							\
+out:									\
+	return status;							\
+out_nfserr:								\
+	status = kernerrno(status);					\
+	goto out
+
+/*
+ * Actual handle functions start here...
+ */
+static void
+handle_change_info(struct nfs4fs_change_info *cinfo, struct inode *inode)
+{
+	NFS4_ASSERT(kernel_locked());
+	NFS4_ASSERT(inode != NULL);
+	
+	/*
+	 * XXX: for now, the 'atomic' field is ignored.
+	 * Eventually, this should become a mount option.
+	 */
+	if (cinfo->before == NFS4_CACHE_CHANGEID(inode)) {
+		NFS4_CACHE_CHANGEID(inode) = cinfo->after;
+		nfs4_validate_caches(inode);
+	}
+	else
+		nfs4_zap_caches(inode);
+}
+
+int
+nfs4fs_handle_simple_op(struct nfs4fs_compound *cp)
+{
+	int status = cp->ops[cp->nops++].nfserr;
+	if (status)
+		status = kernerrno(status);
+	return status;
+}
+
+int
+nfs4fs_handle_access(struct nfs4fs_compound *cp, unsigned int *resp_access)
+{
+	HANDLE_HEAD(access);
+
+	status = -EIO;
+	if (access->ac_resp_supported != access->ac_req_access) {
+		nfsv4_printk(level1, "handle_access: server doesn't support all bits!\n");
+		goto out;
+	}
+	
+	*resp_access = access->ac_resp_access;
+	status = 0;
+	
+	HANDLE_TAIL;
+}
+
+int
+nfs4fs_handle_commit(struct nfs4fs_compound *cp)
+{
+	struct list_head *head;
+	struct nfs4_page *req;
+	HANDLE_HEAD(commit);
+
+	head = &commit->co_pages;
+
+	status = 0;
+	while (!list_empty(head)) {
+		req = list_entry(head->next, struct nfs4_page, wb_list);
+		list_del_init(&req->wb_list);
+		if (!memcmp(req->wb_verifier, commit->co_verifier, sizeof(verifier4))) {
+			nfsv4_printk(write, "__handle_commit: verifiers agree, commit ok\n");
+			nfs4_inode_remove_request(req);
+			nfs4_unlock_request(req);
+		}
+		else {
+			static unsigned long complain = 0;
+			nfsv4_printk(level1, "nfs4: verifier mismatch, server rebooted!!\n");
+			if (time_before(complain, jiffies)) {
+				printk(KERN_INFO
+				       "nfs4: server reboot detected, rewriting data\n");
+				complain = jiffies + 300 * HZ;
+			}
+			if (++req->wb_safeguard < 10) {
+				nfs4_mark_request_dirty(req);
+				nfs4_unlock_request(req);
+			}
+			else {
+				printk(KERN_WARNING "nfs4: warning: pathological server, data "
+				       "may have been lost!\n");
+				nfs4_inode_remove_request(req);
+				nfs4_unlock_request(req);
+				status = -EIO;
+			}
+		}
+	}
+
+	HANDLE_TAIL;
+}
+
+int
+nfs4fs_handle_create(struct nfs4fs_compound *cp, struct dentry *dentry)
+{
+	HANDLE_HEAD(create);
+
+	handle_change_info(&create->cr_cinfo, dentry->d_parent->d_inode);
+	status = 0;
+	
+out:
+	return status;
+out_nfserr:
+	if (status == NFS4ERR_EXIST)
+		d_invalidate(dentry);
+	status = kernerrno(status);
+	goto out;
+}
+
+/*
+ * The next few definitions are for use by nfs4fs_handle_getattr().
+ *
+ * INODE_MASK - attributes which must be returned in order to meaningfully
+ * refresh an existing inode.
+ * DENTRY_MASK - attributes which must be returned in order to meaningfully
+ * validate a (possibly negative) dentry.
+ */
+
+#define INODE_MASK	(FA_CHANGEID | FA_TYPE | FA_FSID | FA_FILEID | FA_MODE)
+#define DENTRY_MASK	(FA_CHANGEID | FA_TYPE | FA_FSID | FA_FILEID | FA_SIZE | FA_MODE | \
+			 FA_NLINK | FA_UID | FA_GID | FA_RDEV | FA_ATIME |  FA_MTIME)
+
+static void
+nfs4_invalidate_inode(struct inode *inode)
+{
+	umode_t save_mode = inode->i_mode;
+	
+	/* Invalidate, but do not unhash, the inode */
+	make_bad_inode(inode);
+	inode->i_mode = save_mode;
+	nfs4_zap_caches(inode);
+}
+
+/* This cannot sleep -- it is called with the icache spinlock held. */
+static int
+nfs4_find_actor(struct inode *inode, unsigned long ino, void *opaque)
+{
+	struct nfs4fs_fattr *fattr = (struct nfs4fs_fattr *)opaque;
+
+	NFS4_ASSERT(fattr->fa_valid & FA_FSID);
+	NFS4_ASSERT(fattr->fa_valid & FA_FILEID);
+	
+	/*
+	 * TODO: NFSv3 compared for equality of filehandles as well.
+	 * I thought that this would be unnecessary in NFSv4, with
+	 * its 64-bit inode numbers, but things are not as simple
+	 * as that.  We can get into trouble with generation numbers:
+	 * If a server reuses inode numbers, we can confuse a new
+	 * inode with a new one, which can cause type mismatches etc.
+	 * Therefore comparing filehandles seems to be necessary
+	 * after all!  How can we arrange for this to happen?  We
+	 * might have to start coalescing GETFH's with our GETATTR's.
+	 */
+	return	(NFS4_FSID_MAJOR(inode) == fattr->fa_fsid_major) &&
+		(NFS4_FSID_MINOR(inode) == fattr->fa_fsid_minor) &&
+		(NFS4_FILEID(inode) == fattr->fa_fileid);
+}
+
+static int
+nfs4_refresh_inode(struct nfs4fs_fattr *fattr, struct inode *inode)
+{
+	unsigned int valid = fattr->fa_valid;
+	
+	NFS4_ASSERT(kernel_locked());
+	NFS4_ASSERT(inode && fattr);
+	NFS4_ASSERT(inode->i_mode != 0);
+	NFS4_ASSERT((~valid & INODE_MASK) == 0);
+
+	if (is_bad_inode(inode))
+		goto out_bad;
+
+	if (NFS4_FSID_MAJOR(inode) != fattr->fa_fsid_major ||
+	    NFS4_FSID_MINOR(inode) != fattr->fa_fsid_minor ||
+	    NFS4_FILEID(inode) != fattr->fa_fileid)
+		goto out_mismatch;
+
+	if ((inode->i_mode & S_IFMT) != (fattr->fa_mode & S_IFMT))
+		goto out_type_change;
+	
+	if (NFS4_CACHE_CHANGEID(inode) == fattr->fa_changeid)
+		nfs4_validate_caches(inode);
+	else {
+		NFS4_CACHE_CHANGEID(inode) = fattr->fa_changeid;
+		nfs4_zap_caches(inode);
+	}
+
+	/*
+	 * XXX: NFSv3 messes with CACHE_CTIME etc., but we don't yet.
+	 * Understand why this is done!
+	 */
+	if (valid & FA_CTIME)
+		inode->i_ctime = fattr->fa_ctime;
+	if (valid & FA_MODE)
+		inode->i_mode = fattr->fa_mode;
+	if (valid & FA_UID)
+		inode->i_uid = fattr->fa_uid;
+	if (valid & FA_GID)
+		inode->i_gid = fattr->fa_gid;
+	if (valid & FA_RDEV) {
+		inode->i_rdev = 0;
+		if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+			inode->i_rdev = MKDEV(fattr->fa_rdev_major, fattr->fa_rdev_minor);
+	}
+	if (valid & FA_ATIME)
+		inode->i_atime = fattr->fa_atime;
+	if (valid & FA_MTIME)
+		inode->i_mtime = fattr->fa_mtime;
+	if (valid & FA_NLINK)
+		inode->i_nlink = fattr->fa_nlink;
+	if (valid & FA_SIZE) {
+		/*
+		 * We avoid updating the i_size if there are pending writes
+		 * which might extend it.
+		 */
+		if (list_empty(&NFS4_FLUSHD_WRITE(inode)) || fattr->fa_size > inode->i_size)
+			inode->i_size = fattr->fa_size;
+	}
+
+	/*
+	 * XXX: NFSv3 messes with ->i_blocks, ->i_blksize... we should too!
+	 */
+
+	return 0;
+
+out_bad:
+	return -EIO;
+out_mismatch:
+	printk(KERN_WARNING "nfs4: inode number mismatch (%Ld:%Ld:%Ld / %Ld:%Ld:%Ld!)\n",
+	       fattr->fa_fsid_major, fattr->fa_fsid_minor, fattr->fa_fileid,
+	       NFS4_FSID_MAJOR(inode), NFS4_FSID_MINOR(inode), NFS4_FILEID(inode));
+	return -EBUSY;
+out_type_change:
+	printk(KERN_WARNING "nfs4: type change %o -> %o\n", inode->i_mode, fattr->fa_mode);
+	nfs4_invalidate_inode(inode);
+	return -EIO;
+}
+
+static int
+nfs4_refresh_dentry(struct nfs4fs_fattr *fattr, struct dentry *dentry)
+{
+	struct inode *inode;
+	ino_t ino;
+	int status;
+	
+	NFS4_ASSERT(kernel_locked());
+	NFS4_ASSERT(dentry && fattr);
+	NFS4_ASSERT((~fattr->fa_valid & DENTRY_MASK) == 0);
+
+	inode = dentry->d_inode;
+	if (!inode) {
+		status = -ENOMEM;
+		ino = (ino_t)fattr->fa_fileid;
+		if (!(inode = iget4(dentry->d_sb, ino, nfs4_find_actor, fattr))) {
+			nfsv4_printk(level1, "iget4() failed!\n");
+			goto out;
+		}
+
+		if (!dentry->d_inode) {
+			d_instantiate(dentry, inode);
+			if (inode->i_mode == 0) {
+				/*
+				 * This is a new inode; some of the fields have
+				 * already been initialized in nfs4_read_inode(),
+				 * but the rest must be initialized here.
+				 */
+				NFS4_FSID_MAJOR(inode) = fattr->fa_fsid_major;
+				NFS4_FSID_MINOR(inode) = fattr->fa_fsid_minor;
+				NFS4_FILEID(inode) = fattr->fa_fileid;
+				
+				inode->i_mode = fattr->fa_mode;
+				inode->i_uid = fattr->fa_uid;
+				inode->i_gid = fattr->fa_gid;
+				inode->i_rdev = 0;
+				if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+					inode->i_rdev = MKDEV(fattr->fa_rdev_major,
+							      fattr->fa_rdev_minor);
+				inode->i_atime = fattr->fa_atime;
+				inode->i_mtime = fattr->fa_mtime;
+				inode->i_ctime = fattr->fa_ctime;
+				inode->i_nlink = fattr->fa_nlink;
+				inode->i_size = fattr->fa_size;
+
+				if (S_ISREG(inode->i_mode)) {
+					inode->i_op = &nfs4_file_inode_operations;
+					inode->i_fop = &nfs4_file_operations;
+					inode->i_data.a_ops = &nfs4_file_aops;
+				}
+				else if (S_ISDIR(inode->i_mode)) {
+					inode->i_op = &nfs4_dir_inode_operations;
+					inode->i_fop = &nfs4_dir_operations;
+				}
+				else if (S_ISLNK(inode->i_mode))
+					inode->i_op = &nfs4_symlink_inode_operations;
+				else {
+					inode->i_op = &nfs4_file_inode_operations;
+					init_special_inode(inode, inode->i_mode,
+							   MKDEV(fattr->fa_rdev_major,
+								 fattr->fa_rdev_minor));
+				}
+				dentry->d_time = jiffies;
+				return 0;
+			}
+		}
+		else {
+			/*
+			 * Someone else has instantiated a new inode, while we slept
+			 * in iget4()...
+			 */
+			iput(inode);
+			inode = dentry->d_inode;
+		}
+	}
+
+	NFS4_ASSERT(inode != NULL);
+	NFS4_ASSERT(inode == dentry->d_inode);
+
+	status = nfs4_refresh_inode(fattr, inode);
+	if (status == -EBUSY)
+		d_invalidate(dentry);
+	if (!status)
+		dentry->d_time = jiffies;
+out:
+	return status;
+}
+
+static int
+fixup_fattr(struct nfs4fs_fattr *fattr)
+{
+	static umode_t typemode[] = {
+		0,           /* bad */
+		S_IFREG,     /* NF4REG */
+		S_IFDIR,     /* NF4DIR */
+		S_IFBLK,     /* NF4BLK */
+		S_IFCHR,     /* NF4CHR */
+		S_IFLNK,     /* NF4LNK */
+		S_IFSOCK,    /* NF4SOCK */
+		S_IFIFO      /* NF4FIFO */
+	};
+	umode_t m;
+	int status;
+	
+	/*
+	 * Many servers (e.g, Netapp) don't return type bits in the mode,
+	 * so we fill them in here.  If type bits are returned in the mode,
+	 * we instead check for consistency with the type.
+	 */
+	if (fattr->fa_valid & FA_MODE) {
+		status = -EIO;
+		if (!(fattr->fa_valid & FA_TYPE)) {
+			nfsv4_printk(level1, "handle_getattr: mode, no type!\n");
+			goto out;
+		}
+
+		/* These checks should have been taken care of during XDR decode. */
+		/* XXX: NAMEDATTR will screw us up here... */
+		NFS4_ASSERT(fattr->fa_type >= 0);
+		NFS4_ASSERT(fattr->fa_type < sizeof(typemode)/sizeof(typemode[0]));
+		NFS4_ASSERT(typemode[fattr->fa_type] != 0);
+		
+		m = typemode[fattr->fa_type];
+		if (!(fattr->fa_mode & S_IFMT))
+			fattr->fa_mode |= m;
+		else if ((fattr->fa_mode & S_IFMT) != m) {
+			nfsv4_printk(level1, "handle_getattr: type-mode mismatch (%d/0%o)!\n",
+				     fattr->fa_type, fattr->fa_mode);
+			goto out;          /* will return -EIO */
+		}
+	}
+	status = 0;
+
+out:
+	return status;
+}
+
+int
+nfs4fs_handle_getattr_simple(struct nfs4fs_compound *cp, struct nfs4fs_fattr *fp)
+{
+	struct nfs4fs_fattr *fattr;
+	HANDLE_HEAD(getattr);
+
+	NFS4_ASSERT(fp != NULL);
+	
+	fattr = &getattr->gt_attrs;
+	if ((status = fixup_fattr(fattr)))
+		goto out;
+	*fp = *fattr;
+
+	HANDLE_TAIL;
+}
+
+int
+nfs4fs_handle_getattr_inode(struct nfs4fs_compound *cp, struct inode *inode)
+{
+	struct nfs4fs_fattr *fattr;
+	HANDLE_HEAD(getattr);
+
+	NFS4_ASSERT(inode != NULL);
+
+	fattr = &getattr->gt_attrs;
+	if ((status = fixup_fattr(fattr)))
+		goto out;
+	
+	if (~fattr->fa_valid & INODE_MASK)
+		goto out_missing;
+	
+	/*
+	 * If nfs4_refresh_inode() returns -EBUSY, replace with -EIO.
+	 */
+	lock_kernel();
+	if (nfs4_refresh_inode(fattr, inode))
+		status = -EIO;
+	unlock_kernel();
+
+	HANDLE_TAIL;	
+out_missing:
+	printk("missing attr! [1] (0x%x/0x%x)\n", fattr->fa_valid & INODE_MASK, INODE_MASK);
+	status = -EIO;
+	goto out;
+}
+
+int
+nfs4fs_handle_getattr_dentry(struct nfs4fs_compound *cp,
+			   struct dentry *dentry, struct nfs4fs_fattr *fp)
+{
+	struct nfs4fs_fattr *fattr;
+	HANDLE_HEAD(getattr);
+
+	NFS4_ASSERT(dentry != NULL);
+
+	fattr = &getattr->gt_attrs;
+	if ((status = fixup_fattr(fattr)))
+		goto out;
+	if (fp)
+		*fp = *fattr;
+
+	if (~fattr->fa_valid & DENTRY_MASK)
+		goto out_missing;
+
+	lock_kernel();
+	status = nfs4_refresh_dentry(fattr, dentry);
+	unlock_kernel();
+
+	HANDLE_TAIL;
+out_missing:
+	printk("missing attr! [2] (0x%x/0x%x)\n", fattr->fa_valid & DENTRY_MASK, DENTRY_MASK);
+	status = -EIO;
+	goto out;
+}
+
+static inline int
+__nfs4fs_handle_getfh(struct inode *inode, struct nfs4fs_getfh *getfh)
+{
+	char *buf;
+	int status;
+
+	NFS4_ASSERT(inode != NULL);
+	NFS4_ASSERT(getfh->gf_fhlen > 0);
+	NFS4_ASSERT(getfh->gf_fhlen <= NFS4_FHSIZE);
+
+	/*
+	 * TODO: We need to fix a race condition here.
+	 * The race is against setup_putfh(), which saves the fhlen and fhval
+	 * for later use by encode_putfh().
+	 * Problem: In the meantime, this routine may change the fhlen, and
+	 * write new data into the fhval buffer!
+	 *
+	 * Possible solution: this routine no-ops if fh is already obtained.
+	 * This might cause problems with the Linux server because its filehandles
+	 * will change if files are moved between directories.  We might _need_ to
+	 * get the new filehandle after a rename!  Actually, the NFSv4 spec allows
+	 * the server to change its filehandles at any time (not only on rename);
+	 * search for "volatile filehandles" in RFC 3010 to read all about it.
+	 * Fixing this race condition is therefore closely related to the problem
+	 * of supporting volatile filehandles on the server (maybe this should be
+	 * its own TODO!)
+	 */
+	
+	if ((getfh->gf_fhlen > NFS4_FH_INLINE_LEN) && NFS4_FH_IS_INLINE(inode)) {
+		buf = NFS4_ALLOC(NFS4_FHSIZE);
+		status = -ENOMEM;
+		if (!buf) {
+			nfsv4_printk(level1, "handle_getfh: couldn't allocate memory!\n");
+			goto out;
+		}
+		NFS4_FH_VAL(inode) = buf;   /* XXX: protect with lock! */
+	}
+
+	NFS4_FH_LEN(inode) = getfh->gf_fhlen;
+	memcpy(NFS4_FH_VAL(inode), getfh->gf_fhval, getfh->gf_fhlen);
+	status = 0;
+
+out:
+	return status;
+}
+
+int
+nfs4fs_handle_getfh(struct nfs4fs_compound *cp, struct inode *inode)
+{
+	HANDLE_HEAD(getfh);
+	status = __nfs4fs_handle_getfh(inode, getfh);
+	HANDLE_TAIL;
+}
+
+int
+nfs4fs_handle_link(struct nfs4fs_compound *cp, struct dentry *dentry, struct inode *inode)
+{
+	HANDLE_HEAD(link);
+	
+	handle_change_info(&link->ln_cinfo, dentry->d_parent->d_inode);
+	d_invalidate(dentry);
+	inode->i_nlink++;
+
+	HANDLE_TAIL;
+}
+
+int
+nfs4fs_handle_lock(struct nfs4fs_compound *cp)
+{
+	int status = cp->ops[cp->nops++].nfserr;
+	if (status == NFS4ERR_LOCK_RANGE)
+		printk(KERN_WARNING "nfs4: warning: server couldn't process lock request!\n");
+	if (status)
+		status = kernerrno(status);
+	return status;
+}
+
+int
+nfs4fs_handle_lockt(struct nfs4fs_compound *cp, struct file_lock *flock)
+{
+	u64 start, length;
+	struct nfs4fs_lockt *lockt = &cp->ops[cp->nops].u.lockt;
+	int status = cp->ops[cp->nops++].nfserr;
+
+	if (!status)
+		flock->fl_type = F_UNLCK;
+	else if (status == NFS4ERR_DENIED) {
+		start = lockt->lt_denied.offset;
+		length = lockt->lt_denied.length;
+		
+		flock->fl_start = start;
+		if (LOFF_OVERFLOW(start, length))
+			flock->fl_end = ~(u64) 0;
+		else
+			flock->fl_end = start + length - 1;
+		
+		flock->fl_type = F_WRLCK;
+		if (lockt->lt_denied.type & 1)    /* READ_LT, READW_LT */
+			flock->fl_type = F_RDLCK;
+
+		if (lockt->lt_denied.is_local)
+			flock->fl_pid = nfs4fs_id_to_pid(lockt->lt_denied.id);
+		else
+			flock->fl_pid = 1;
+	}
+	else
+		status = kernerrno(status);
+
+	return status;
+}
+
+int
+nfs4fs_handle_lookup(struct nfs4fs_compound *cp, struct dentry *dentry)
+{
+	int status = cp->ops[cp->nops++].nfserr;
+
+	if (!status)
+		return 0;
+	if (status == NFS4ERR_NOENT) {
+		nfs4_zap_caches(dentry->d_parent->d_inode);
+		d_delete(dentry);
+		return -ENOENT;
+	}
+	return kernerrno(status);
+}
+
+int
+nfs4fs_handle_open(struct nfs4fs_compound *cp, struct nfs4fs_file_data *fdata,
+		   struct dentry **dentryp)
+{
+	struct dentry *dentry = *dentryp;
+	struct inode *inode;
+	struct nfs4fs_open *open;
+	struct nfs4fs_getattr *getattr;
+	struct nfs4fs_getfh *getfh;
+	struct nfs4fs_fattr *fattr;
+	int status;
+
+	NFS4_ASSERT(kernel_locked());
+
+	/*
+	 * OPEN is complicated by the fact that we might see a "mismatch":
+	 * the OPEN might succeed, but not on the file we were expecting.
+	 * This can happen e.g. if another client renames the file out from
+	 * under us (since OPEN is always by name in nfsv4).
+	 *
+	 * First, we look at the OPEN GETATTR GETFH response to determine
+	 * whether the OPEN succeeded, and if so, which file was opened.
+	 * If any of these operations fail, we fail the entire OPEN
+	 * operation.  In particular, we will "leak" state if the OPEN
+	 * succeeds but the GETATTR or GETFH fails.
+	 *
+	 * XXX - it would be nice to recover from this condition as
+	 * gracefully as possible, although this seems low-priority as
+	 * I just don't see this being a problem in real life.
+	 */
+
+	/* OPEN */
+	open = &cp->ops[cp->nops].u.open;
+	status = cp->ops[cp->nops++].nfserr;
+	if (status)
+		goto out_nfserr;
+	
+	/* GETATTR */
+	getattr = &cp->ops[cp->nops].u.getattr;
+	status = cp->ops[cp->nops++].nfserr;
+	if (status)
+		goto out_leak;
+	fattr = &getattr->gt_attrs;
+	
+	status = NFS4ERR_IO;    /* kernerrno() will translate to -EIO */
+	if (~fattr->fa_valid & DENTRY_MASK) {
+		printk("missing attr! [3] (0x%x/0x%x)\n",
+		       fattr->fa_valid & DENTRY_MASK, DENTRY_MASK);
+		goto out_leak;
+	}
+	if (fixup_fattr(fattr))
+		goto out_leak;
+
+	/* GETFH */
+	getfh = &cp->ops[cp->nops].u.getfh;
+	status = cp->ops[cp->nops++].nfserr;
+	if (status)
+		goto out_leak;
+
+	/*
+	 * Now we process the actual OPEN response.
+	 */
+	handle_change_info(&open->op_cinfo, dentry->d_parent->d_inode);
+	
+	for (;;) {
+		status = nfs4_refresh_dentry(fattr, dentry);
+		if (!status) {
+			NFS4_ASSERT(dentry->d_inode != NULL);
+			if (dentry->d_inode != fdata->fi_inode) {
+				iput(fdata->fi_inode);
+				fdata->fi_inode = igrab(dentry->d_inode);
+				NFS4_ASSERT(fdata->fi_inode != NULL);
+			}
+			break;
+		}
+		else if (status != -EBUSY)
+			goto out;
+		
+		/*
+		 * If nfs4_refresh_dentry returns -EBUSY,
+		 * it means that we have to replace the dentry with one
+		 * which points to another inode, presumably because of
+		 * another client mucking with the namespace.
+		 *
+		 * First see whether nfs4_refresh_{positive,negative}
+		 * was able to invalidate the dentry...
+		 */
+		if (!d_unhashed(dentry)) {
+			/*
+			 * For the time being, this a fatal error.
+			 */
+			printk(KERN_ERR "nfs4: couldn't invalidate dentry in OPEN!\n");
+			status = -EIO;
+			goto out_leak;
+		}
+		
+		/*
+		 * Looks good, now try to create a new dentry to
+		 * replace the old one.
+		 */
+		status = -ENOMEM;
+		if (!(dentry = d_alloc(dentry->d_parent, &dentry->d_name))) {
+			nfsv4_printk(level1, "handle_open: couldn't alloc dentry!\n");
+			goto out_leak;
+		}
+		dentry->d_op = &nfs4_dentry_operations;
+		nfsv4_printk(open, "handle_open: switching dentry...\n");
+
+		/*
+		 * We replace the caller's dentry with the new dentry.
+		 * Our convention is that the caller's reference to the
+		 * old dentry is also replaced with a reference to the
+		 * new dentry.
+		 */
+		dput(*dentryp);
+		*dentryp = dentry;
+		d_rehash(dentry);   /* XXX: what about dir semaphore? */
+	}
+	
+	NFS4_ASSERT(dentry->d_inode != NULL);
+	NFS4_ASSERT(dentry->d_inode == fdata->fi_inode);
+	inode = dentry->d_inode;
+
+	/* XXX - what to do if __nfs4fs_handle_getfh() fails? */
+	__nfs4fs_handle_getfh(inode, getfh);
+
+	/*
+	 * Success!  We now have an inode and are ready to instantiate
+	 * the file_data.
+	 */
+	status = 0;
+	nfs4fs_instantiate_open(fdata);
+
+	/*
+	 * Process delegation.
+	 */
+	if (open->op_delegation_type != OPEN_DELEGATE_NONE) {
+		nfsv4_printk(callback, "handle_open: accepting delegation %p\n", inode);
+		nfs4fs_accept_delegation(dentry, inode, open->op_delegation_type,
+					 open->op_delegation_stateid);
+	}
+
+out:
+	return status;
+out_leak:
+	printk(KERN_ERR "nfs4: warning: stray OPEN on server!\n");
+out_nfserr:
+	status = kernerrno(status);
+	goto out;
+}
+
+int
+nfs4fs_handle_undelegating_open(struct nfs4fs_compound *cp, struct nfs4fs_file_data *fp)
+{
+	struct nfs4fs_open *open;
+	struct nfs4fs_getattr *getattr;
+	struct nfs4fs_fattr *fattr;
+	int status;
+	
+	/*
+	 * XXX: For now, we are coalescing a GETATTR GETFH into the OPEN.  (See
+	 * "temporary hack" in setup_undelegating_open().)  We may as well use
+	 * the GETATTR response to refresh the inode, although the GETFH is
+	 * ignored entirely.
+	 */
+
+	/* OPEN */
+	open = &cp->ops[cp->nops].u.open;
+	status = cp->ops[cp->nops++].nfserr;
+	if (status)
+		goto out_nfserr;
+
+	/* GETATTR */
+	getattr = &cp->ops[cp->nops].u.getattr;
+	status = cp->ops[cp->nops++].nfserr;
+	if (status)
+		goto out_leak_nfserr;
+	fattr = &getattr->gt_attrs;
+	
+	status = -EIO;
+	if (~fattr->fa_valid & INODE_MASK) {
+		printk("missing attr! [4] (0x%x/0x%x)\n",
+		       fattr->fa_valid & INODE_MASK, INODE_MASK);
+		goto out_leak;
+	}
+	if (fixup_fattr(fattr))
+		goto out_leak;
+	
+	/* GETFH */
+	status = cp->ops[cp->nops++].nfserr;
+	if (status)
+		goto out_leak;
+	
+	/*
+	 * Ignore the change_info in the response: we don't have the parent inode anyway.
+	 */
+
+	status = -EIO;
+	if (nfs4_refresh_inode(fattr, fp->fi_inode)) {
+		nfsv4_printk(open2, "handle_undelegating_open: refresh_inode failed!\n");
+		goto out_leak;
+	}
+
+	/*
+	 * Success!  Instantiate the file_data.
+	 */
+	status = 0;
+	nfs4fs_instantiate_open(fp);
+	
+	/*
+	 * If the server returns a delegation while we are trying to undelegate, just
+	 * return it.  This avoids complexities, and shouldn't happen in practice anyway.
+	 * (The NULL argument in nfs4_accept_delegation() means that the delegation will
+	 * be returned immediately.)
+	 */
+	if (open->op_delegation_type != OPEN_DELEGATE_NONE) {
+		nfsv4_printk(callback, "handle_undelegating_open: got delegation?!\n");
+		nfs4fs_accept_delegation(NULL, fp->fi_inode, open->op_delegation_type,
+					 open->op_delegation_stateid);
+	}
+
+out:
+	return status;
+out_leak:
+	printk(KERN_ERR "nfs4: warning: stray OPEN on server!\n");
+	goto out;
+out_leak_nfserr:
+	printk(KERN_ERR "nfs4: warning: stray OPEN on server!\n");
+out_nfserr:
+	status = kernerrno(status);
+	goto out;
+}
+
+int
+nfs4fs_handle_putrootfh(struct nfs4fs_compound *cp)
+{
+	int i;
+	HANDLE_HEAD(putrootfh);
+	
+	for (i = 0; i < putrootfh->pr_nlookups; i++) {
+		/*
+		 * We can't call nfs4fs_handle_lookup(), since these
+		 * LOOKUPs aren't actually backed by dentries.
+		 */
+		if ((status = nfs4fs_handle_simple_op(cp)))
+			goto out;
+	}
+
+	HANDLE_TAIL;
+}
+
+int
+nfs4fs_handle_read_simple(struct nfs4fs_compound *cp, u32 *bytes_read, int *eof)
+{
+	HANDLE_HEAD(read);
+
+	*bytes_read = read->rd_bytes_read;
+	*eof = read->rd_eof;
+	
+	HANDLE_TAIL;
+}
+
+int
+nfs4fs_handle_read_complex(struct nfs4fs_compound *cp)
+{
+	struct nfs4_page *req;
+	struct page *page;
+	struct list_head *head;
+	int count;
+	HANDLE_HEAD(read);
+
+#if 0
+	/* XXX: really? */
+	NFS4_ASSERT(kernel_locked());
+#endif
+
+	head = &read->rd_pages;
+	count = read->rd_bytes_read;
+	while (!list_empty(head)) {
+		req = list_entry(head->next, struct nfs4_page, wb_list);
+		page = req->wb_page;
+
+		list_del_init(&req->wb_list);
+		if (count >= 0) {
+			SetPageUptodate(page);
+			count -= PAGE_CACHE_SIZE;
+		}
+		else
+			SetPageError(page);
+		flush_dcache_page(page);
+		UnlockPage(page);
+		nfs4_kunmap_request(req);
+		nfs4_unlock_request(req);
+		nfs4_release_request(req);
+	}
+
+	HANDLE_TAIL;
+}
+
+int
+nfs4fs_handle_readdir(struct nfs4fs_compound *cp, struct inode *inode, u64 *cookiep,
+		      verifier4 verifier, u32 *bytes_read, int *eof, int *try_again)
+{
+	struct nfs4fs_readdir *readdir = &cp->ops[cp->nops].u.readdir;
+	int status = cp->ops[cp->nops++].nfserr;
+
+	if (!status) {
+		*cookiep = readdir->rd_last_cookie;
+		memcpy(verifier, readdir->rd_resp_verifier, sizeof(verifier4));
+		*bytes_read = readdir->rd_bytes_read;
+		*eof = readdir->rd_eof;
+		*try_again = 0;
+		return 0;
+	}
+	else if (status == NFS4ERR_STALE) {
+		*cookiep = 0;
+		memset(verifier, 0, sizeof(verifier4));
+		*bytes_read = readdir->rd_bytes_read;
+		*eof = 0;
+		*try_again = 1;
+		return 0;
+	}
+	else {
+		nfs4_zap_caches(inode);
+		return kernerrno(status);
+	}
+}
+
+int
+nfs4fs_handle_remove(struct nfs4fs_compound *cp, struct inode *pinode, struct dentry *dentry)
+{
+	HANDLE_HEAD(remove);
+
+	/*
+	 * There is one case where 'dentry' will be NULL: finish_silly_unlink() can't
+	 * hang onto it, because it is only invoked when the last reference to the
+	 * dentry goes away.  Normally, 'dentry' will be non-NULL.  In this case, we
+	 * check for consistency with the parent inode.
+	 */
+	NFS4_ASSERT(!dentry || dentry->d_parent);
+	NFS4_ASSERT(!dentry || (dentry->d_parent->d_inode == pinode));
+	
+	handle_change_info(&remove->rm_cinfo, pinode);
+
+	/*
+	 * XXX: This looks weird... better compare with v3.
+	 * XXX: Do we need the BKL?
+	 */
+	if (dentry && dentry->d_inode)
+		dentry->d_inode->i_nlink--;
+	
+	HANDLE_TAIL;
+}
+
+int
+nfs4fs_handle_rename(struct nfs4fs_compound *cp, struct dentry *src, struct dentry *dst)
+{
+	struct inode *dir1 = src->d_parent->d_inode;
+	struct inode *dir2 = dst->d_parent->d_inode;
+	HANDLE_HEAD(rename);
+
+	NFS4_ASSERT(src->d_inode != NULL);
+	
+	handle_change_info(&rename->rn_src_cinfo, dir1);
+	if (dir1 != dir2)
+		handle_change_info(&rename->rn_dst_cinfo, dir2);
+	if (!S_ISDIR(src->d_inode->i_mode))
+		d_move(src, dst);
+
+	HANDLE_TAIL;
+}
+
+/* compare returned security flavors and possibley triples to client 
+ * supported security triples,
+ * both lists preference ordered top down. 
+ * where to remember the secinfo negotiated results?
+ * 1) store first matched triple 
+ *    in nfs4fs_client which hangs off the super block. this reflects
+ *    the current linux, network appliance, and sun ports that allow
+ *    different security triples per export 
+ * 2) the spec says that different security triples are allowed 
+ *    per file - maybe we should store the security triple 
+ *    in nfs4fs_file_data.
+ * 
+ * another issus: how to pass the qop and service to the rpc layer. 
+ * the curret interface in via rpcauth_create which only takes a flavor.
+ * i'm considering adding to the rpc_auth struct.
+ */
+
+int
+nfs4fs_handle_secinfo(struct nfs4fs_compound *cp)
+{
+	int i,flavor = -1;
+	HANDLE_HEAD(secinfo);
+
+	nfsv4_printk(secinfo,"nfs4fs_handle_secinfo:secinfo->num_flavor %d\n",
+	            secinfo->num_flavor); 
+	for(i=0;i<secinfo->num_flavor;i++){
+		flavor = secinfo->secflavor[i].flavor;
+
+		/* XXX do we really support RPC_AUTH_NULL */
+		if((flavor == RPC_AUTH_UNIX) || (flavor == RPC_AUTH_NULL)){
+			/* The server supports RPC_AUTH_UNIX/NONE so 
+			 * fall throught and simply change auth */
+			break;	
+		}
+		if(flavor == RPC_AUTH_GSS){
+#ifndef CONFIG_SUNRPC_GSS
+    status = NFS4ERR_NOTSUPP;
+#else /* SUNRPC_GSS */
+			struct nfs4fs_flavor_info *fi;
+			/* compare the server security triple with client supported triples */
+			status = NFS4ERR_WRONGSEC;
+			fi = secinfo->secflavor[i].f_info;
+
+#if 0
+			nfsv4_printk(secinfo,"oid_len %d oid_data:\n",fi->oid_len);
+			print_hexl((u32 *)fi->oid_data,fi->oid_len,0);
+			nfsv4_printk(secinfo,"qop %d, service %d \n",
+			               fi->qop,fi->service);
+#endif /*0*/
+
+			if(gss_cmp_triples(fi->oid_len,fi->oid_data,fi->qop,fi->service)){
+
+				nfsv4_printk(secinfo,"nfs4fs_handle_secinfo: sec triple match \n");
+				/* XXX - remember for the next time? should i save in superblock? 
+			   * or should i save the resultant auth?*/ 
+
+				status = NFS4_OK;
+				break;
+			}
+#endif /* CONFIG_SUNRPC_GSS */
+		}
+		/* Client does not support the required security triple.
+		* return error -?? XXX NFS4ERR_WRONGSEC XXX ??? 
+		*/
+		goto out_nfserr;
+	}
+	/* change the rpc auth */
+	if(NFS4_CLNT(cp->sb)->cl_auth->au_ops->au_flavor != flavor) {
+		nfsv4_printk(secinfo, "nfs4_handle_secinfo: changing to flavor %d\n",
+		              flavor);
+		/* Set the mech, service, QOP, based on negotiated secinfo */
+		rpcauth_destroy(NFS4_CLNT(cp->sb)->cl_auth);
+		rpcauth_create(flavor, NFS4_CLNT(cp->sb));
+	}
+	HANDLE_TAIL;
+}
+
+int
+nfs4fs_handle_setclientid(struct nfs4fs_compound *cp, struct nfs4fs_client *clp)
+{
+	HANDLE_HEAD(setclientid);
+
+	clp->cl_clientid = setclientid->sc_clientid;
+	
+	HANDLE_TAIL;
+}
+
+int
+nfs4fs_handle_write_simple(struct nfs4fs_compound *cp, struct inode *inode, u32 *bytes_written)
+{
+	struct nfs4fs_getattr *getattr;
+	struct nfs4fs_write *write;
+	struct nfs4fs_change_info cinfo;
+	int status;
+
+	NFS4_ASSERT(kernel_locked());
+	
+	/*
+	 * Retrieve first GETATTR result.
+	 * We use the changeid to partially construct a fake change_info4.
+	 */
+	getattr = &cp->ops[cp->nops].u.getattr;
+	status = cp->ops[cp->nops++].nfserr;
+	if (status)
+		goto out_nfserr;
+	if (!(getattr->gt_attrs.fa_valid & FA_CHANGEID))
+		goto out_missing_attr;
+	cinfo.before = getattr->gt_attrs.fa_changeid;
+	
+	/*
+	 * Retrieve the WRITE result.
+	 */
+	write = &cp->ops[cp->nops].u.write;
+	status = cp->ops[cp->nops++].nfserr;
+	if (status)
+		goto out_nfserr;
+	*bytes_written = write->wr_bytes_written;	
+	if (write->wr_how_written == UNSTABLE4) {
+		static unsigned long  complain;
+		if (time_before(complain, jiffies)) {
+			printk(KERN_WARNING "nfs4: server failed to commit data!\n");
+			complain = jiffies + 300 * HZ;
+		}
+	}
+
+	/*
+	 * Retrieve second GETATTR result.
+	 * We finish off the change_info4 and also update the mtime.
+	 */
+	getattr = &cp->ops[cp->nops].u.getattr;
+	status = cp->ops[cp->nops++].nfserr;
+	if (status)
+		goto out_nfserr;
+	
+	if ((getattr->gt_attrs.fa_valid & (FA_CHANGEID|FA_MTIME)) != (FA_CHANGEID|FA_MTIME))
+		goto out_missing_attr;
+	cinfo.after = getattr->gt_attrs.fa_changeid;
+	cinfo.atomic = 0;
+	handle_change_info(&cinfo, inode);
+	inode->i_mtime = getattr->gt_attrs.fa_mtime;
+
+out:
+	return status;
+out_nfserr:
+	status = kernerrno(status);
+	goto out;
+out_missing_attr:
+	nfsv4_printk(write2, "handle_write_simple: missing attr!\n");
+	status = -EIO;
+	goto out;
+}
+
+int
+nfs4fs_handle_write_complex(struct nfs4fs_compound *cp, struct inode *inode)
+{
+	struct nfs4fs_getattr *getattr;
+	struct nfs4fs_write *write;
+	struct nfs4fs_change_info cinfo;
+	struct nfs4_page *req;
+	int status;
+
+	NFS4_ASSERT(kernel_locked());
+	
+	/*
+	 * Retrieve first GETATTR result.
+	 * We use the changeid to partially construct a fake change_info4.
+	 */
+	getattr = &cp->ops[cp->nops].u.getattr;
+	status = cp->ops[cp->nops++].nfserr;
+	if (status)
+		goto out_nfserr;
+	if (!(getattr->gt_attrs.fa_valid & FA_CHANGEID))
+		goto out_missing_attr;
+	cinfo.before = getattr->gt_attrs.fa_changeid;
+	
+	/*
+	 * Retrieve the WRITE result.
+	 * XXX: Following v3, we just throw an error if the server didn't
+	 * write all the data!
+	 */
+	write = &cp->ops[cp->nops].u.write;
+	status = cp->ops[cp->nops++].nfserr;
+	if (status)
+		goto out_nfserr;
+	
+	status = -EIO;
+	if (write->wr_bytes_written != write->wr_len) {
+		nfsv4_printk(level1, "server wrote less than expected!\n");
+		goto out;
+	}
+	
+	while (!list_empty(&write->wr_pages)) {
+		req = list_entry(write->wr_pages.next, struct nfs4_page, wb_list);
+		list_del_init(&req->wb_list);
+
+		memcpy(req->wb_verifier, write->wr_verifier, 8);
+		if (req->wb_how_written != UNSTABLE4) {
+			nfsv4_printk(write, "handle_write: page written stably, ok\n");
+			nfs4_inode_remove_request(req);
+			nfs4_kunmap_request(req);
+			nfs4_unlock_request(req);
+		}
+		else {
+			nfsv4_printk(write, "handle_write: page written unstably, "
+				     "marking for commit\n");
+			req->wb_timeout = jiffies + NFS4_COMMIT_DELAY;
+			nfs4_mark_request_commit(req);
+			nfs4_kunmap_request(req);
+			nfs4_unlock_request(req);
+		}
+	}
+
+	/*
+	 * Retrieve second GETATTR result.
+	 * We finish off the change_info4 and also update the mtime.
+	 */
+	getattr = &cp->ops[cp->nops].u.getattr;
+	status = cp->ops[cp->nops++].nfserr;
+	if (status)
+		goto out_nfserr;
+	
+	if ((getattr->gt_attrs.fa_valid & (FA_CHANGEID|FA_MTIME)) != (FA_CHANGEID|FA_MTIME))
+		goto out_missing_attr;
+	cinfo.after = getattr->gt_attrs.fa_changeid;
+	cinfo.atomic = 0;
+	handle_change_info(&cinfo, inode);
+	inode->i_mtime = getattr->gt_attrs.fa_mtime;
+
+out:
+	return status;
+out_nfserr:
+	status = kernerrno(status);
+	goto out;
+out_missing_attr:
+	nfsv4_printk(write2, "handle_write_simple: missing attr!\n");
+	status = -EIO;
+	goto out;
+}
--- clean/fs/nfs4fs/read.c	Thu Feb  7 18:04:30 2002
+++ dirty/fs/nfs4fs/read.c	Mon Feb  4 15:16:23 2002
@@ -0,0 +1,441 @@
+/*
+ * linux/fs/nfs4fs/read.c
+ * from linux/fs/nfs/read.c
+ * 
+ * NFSv4 implementation
+ *    Kendrick Smith  <kmsmith@umich.edu>
+ *    Andy Adamson  <andros@umich.edu>
+ *    Copyright (C) 2001 The Regents of the University of Michigan
+ */
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <asm/uaccess.h>
+#include <linux/sunrpc/sched.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+
+#include <linux/nfs4/nfs4.h>
+#include <linux/nfs4/nfs4_debug.h>
+#include <linux/nfs4/nfs4fs_xdr.h>
+#include <linux/nfs4/nfs4fs.h>
+
+#ifndef IS_SWAPFILE
+# define IS_SWAPFILE(inode)        0
+#endif
+
+static int
+nfs4_readpage_sync(struct nfs4fs_file_data *fdata, struct page *page)
+{
+	struct nfs4fs_compound	compound;
+	struct inode *		inode = fdata->fi_inode;
+	loff_t 			offset;
+	u32 			bytes_read;
+	char *			buffer;
+	int 			eof;
+	int 			count;
+	int 			rsize = NFS4_RSIZE(inode);
+	int			do_reval;
+	int 			status;
+
+	nfsv4_printk(read, "nfs4_readpage_sync(%ld): starting, index=%d\n",
+		     inode->i_ino, (int)page->index);
+
+	count = PAGE_CACHE_SIZE;
+	offset = (loff_t)page->index << PAGE_CACHE_SHIFT;
+	buffer = kmap(page);
+
+	do {
+		if (count < rsize)
+			rsize = count;
+		lock_kernel();
+		do_reval = nfs4_need_reval(inode);
+		unlock_kernel();
+		
+		nfsv4_printk(read, "nfs4_readpage_sync: reading %d bytes\n", rsize);
+
+		nfs4fs_setup_compound(&compound, inode->i_sb, "nfs4_readpage_sync()");
+		nfs4fs_setup_putfh(&compound, inode);
+		if (do_reval)
+			nfs4fs_setup_getattr(&compound, nfs4_standard_bitmap);
+		nfs4fs_setup_read_simple(&compound, offset, rsize, buffer, fdata);
+
+		/*
+		 * XXX: In v3, the lock_kernel() surrounds the entire block here.
+		 * I don't see why it's needed except in handle_getattr().
+		 */
+		lock_kernel();
+		status = nfs4fs_call_compound(&compound);
+		if (status)
+			goto out_release;
+		if ((status = nfs4fs_handle_putfh(&compound)))
+			goto out_release;
+		if (do_reval) {
+			if ((status = nfs4fs_handle_getattr_inode(&compound, inode)))
+				goto out_release;
+		}
+		if ((status = nfs4fs_handle_read_simple(&compound, &bytes_read, &eof)))
+			goto out_release;
+		unlock_kernel();
+		
+		nfs4fs_release_compound(&compound);
+		
+		nfsv4_printk(read, "nfs4_read: read %d bytes, eof=%d\n", bytes_read, eof);
+		if (!bytes_read) {
+			nfsv4_printk(read, "nfs4_read: no data read\n");
+			status = -EIO;
+			goto io_error;
+		}
+
+		count -= bytes_read;
+		offset += bytes_read;
+		buffer += bytes_read;
+	} while (count && !eof);
+
+	memset(buffer, 0, count);
+
+	flush_dcache_page(page);   /* XXX : what does this do?  it's a null macro on intel */
+	SetPageUptodate(page);
+	if (PageError(page))
+		ClearPageError(page);
+	status = 0;
+	
+io_error:
+	kunmap(page);
+	UnlockPage(page);
+	nfsv4_printk(read, "nfs4_readpage_sync: returning status %d\n", status);
+	return status;
+out_release:
+	unlock_kernel();
+	nfs4fs_release_compound(&compound);
+	goto io_error;
+}
+
+static inline struct nfs4_page *
+_nfs4_find_read(struct inode *inode, struct page *page)
+{
+	struct list_head *head, *next;
+	struct nfs4_page *req;
+
+	head = &NFS4_FLUSHD_READ(inode);
+	next = head->next;
+	while (next != head) {
+		req = list_entry(next, struct nfs4_page, wb_list);
+		next = next->next;
+		if (req->wb_page->index != page->index)
+			continue;
+		req->wb_count++;
+		goto out;
+	}
+	req = NULL;
+
+out:
+	return req;
+}
+
+static inline struct nfs4_page *
+nfs4_find_read(struct inode *inode, struct page *page)
+{
+	struct nfs4_page *req;
+	spin_lock(&nfs4_wreq_lock);
+	req = _nfs4_find_read(inode, page);
+	spin_unlock(&nfs4_wreq_lock);
+	return req;
+}
+
+/* called with spinlock held, drops it. */
+static inline void
+nfs4_mark_request_read(struct nfs4_page *req)
+{
+	struct inode *inode = req->wb_inode;
+	
+	if (list_empty(&req->wb_list)) {
+		nfs4_list_add_request(req, &NFS4_FLUSHD_READ(inode));
+		NFS4_FLUSHD_NREAD(inode)++;
+	}
+	
+	/*
+	 * NB: The call to schedule_inode() can lie inside the spinlock,
+	 * since the v4 version cannot block...
+	 */
+	nfs4_schedule_inode(inode, req->wb_timeout);
+	spin_unlock(&nfs4_wreq_lock);
+}
+
+static int
+nfs4_readpage_async(struct file *file, struct nfs4fs_file_data *fdata, struct page *page)
+{
+	struct inode *inode;
+	struct nfs4_page  *req, *new = NULL;
+	int result;
+
+	NFS4_ASSERT(fdata != NULL);
+	NFS4_ASSERT(fdata->fi_inode != NULL);
+
+	inode = fdata->fi_inode;
+	
+	for (;;) {
+		result = 0;
+		if (Page_Uptodate(page))
+			break;
+
+		nfsv4_printk(read, "nfs4_readpage_async() calling find_read\n");
+
+		/*
+		 * XXX: This is slightly different from v3, but I think that
+		 * my version avoids a race condition!  In the v3 code, there
+		 * is a small window between nfs_find_read() and
+		 * nfs_mark_request_read() where another process can insert
+		 * a request...
+		 */
+		spin_lock(&nfs4_wreq_lock);
+		req = _nfs4_find_read(inode, page);
+		if (req) {
+			spin_unlock(&nfs4_wreq_lock);
+			nfsv4_printk(read, "nfs4_readpage_async() found page\n");
+			if (page != req->wb_page) {
+				nfsv4_printk(read2, "nfs4_readpage_async: page mismatch?!\n");
+				nfs4_release_request(req);
+				nfs4_pagein_inode(inode, page->index, 0);
+				continue;
+			}
+			nfs4_release_request(req);
+			break;
+		}
+
+		if (new) {
+			nfsv4_printk(read, "nfs4_readpage_async: scheduling new page...\n");
+			nfs4_mark_request_read(new);    /* drops spinlock */
+			new = NULL;
+			break;
+		}
+		spin_unlock(&nfs4_wreq_lock);
+		
+		result = -ENOMEM;
+		nfsv4_printk(read, "nfs4_readpage_async: creating new page...\n");
+		new = nfs4_create_request(file, fdata, page, 0, PAGE_CACHE_SIZE);
+		if (!new)
+			break;
+		new->wb_timeout = jiffies + NFS4_READ_DELAY;
+	}
+
+	if (NFS4_FLUSHD_NREAD(inode) >= NFS4_RPAGES(inode->i_sb) ||
+	    page->index == (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT) {
+		nfsv4_printk(read, "nfs4_readpage_async: paging in inode now!\n");
+		nfs4_pagein_inode(inode, 0, 0);
+	}
+	
+	if (new)
+		nfs4_release_request(new);
+
+	return result;
+}
+
+int
+nfs4_readpage(struct file *file, struct page *page)
+{
+	struct nfs4fs_file_data *fdata;
+	struct inode *inode;
+	int error;
+
+	nfsv4_printk(pagecache, "nfs4_readpage %d: pagecount=%d flags=0x%x\n",
+	       (int)page->index, (int)atomic_read(&page->count),
+	       (int)page->flags);
+	nfsv4_printk(read, "nfs4_readpage: starting, index=%ld\n", page->index);
+
+	inode = page->mapping->host;
+	fdata = nfs4fs_get_read_stateid(file, inode);
+
+	error = -EIO;
+	if (!fdata) {
+		nfsv4_printk(read2, "nfs4_readpage: couldn't get stateid!\n");
+		goto out_error;
+	}
+	
+	/*
+	 * Try to flush any pending writes to the file..
+	 *
+	 * NOTE! Because we own the page lock, there cannot
+	 * be any new pending writes generated at this point
+	 * for this page (other pages can be written to).
+	 *
+	 * XXX: why FLUSH_STABLE here?
+	 */
+	error = nfs4_sync_file(inode, NULL, page->index, 1, FLUSH_WAIT | FLUSH_STABLE);
+	if (error)
+		goto out_error;
+	
+	error = -1;
+	if (!PageError(page) && NFS4_RSIZE(inode) >= PAGE_CACHE_SIZE)
+		error = nfs4_readpage_async(file, fdata, page);
+	if (error >= 0)
+		goto out;
+	
+	error = nfs4_readpage_sync(fdata, page);   /* will release page lock */
+	if ((error < 0) && IS_SWAPFILE(inode))
+		printk("Aiee.. nfs swap-in of page failed!\n");
+
+out:
+	nfs4fs_put_file_data(fdata);
+	nfsv4_printk(read, "nfs4_readpage: returning status %d\n", error);
+	return error;
+out_error:
+	UnlockPage(page);
+	goto out;
+}
+
+static void
+nfs4_async_read_error(struct list_head *head)
+{
+	struct nfs4_page *req;
+	struct page *page;
+
+	while (!list_empty(head)) {
+		req = list_entry(head->next, struct nfs4_page, wb_list);
+		page = req->wb_page;
+
+		/*
+		 * XXX: do we need to add flush_dcache_page() here?
+		 */
+		list_del_init(&req->wb_list);
+		SetPageError(page);
+		UnlockPage(page);
+		nfs4_unlock_request(req);
+		nfs4_release_request(req);
+	}
+}
+
+static void
+read_handler(struct nfs4fs_compound *cp)
+{
+	if (nfs4fs_handle_putfh(cp))
+		goto err;
+	if (nfs4fs_handle_read_complex(cp))
+		goto err;
+	return;
+
+err:
+	nfsv4_printk(read2, "read_handler: READ failed!\n");
+}
+
+static int
+nfs4_pagein_one(struct list_head *head)
+{
+	struct nfs4fs_compound *cp;
+	struct nfs4_page *req;
+	struct inode *inode;
+	int status;
+
+	NFS4_ASSERT(!list_empty(head));
+	req = list_entry(head->next, struct nfs4_page, wb_list);
+	inode = req->wb_fdata->fi_inode;
+	
+	if ((status = nfs4fs_setup_async(&cp, inode->i_sb, "read", NULL, read_handler, NULL)))
+		goto out_bad;
+	nfs4fs_setup_putfh(cp, inode);
+	nfs4fs_setup_read_complex(cp, head);
+	status = nfs4fs_call_async(cp);
+	
+out:
+	return status;
+out_bad:
+	nfsv4_printk(read2, "pagein_one: error, failed to schedule task!\n");
+	nfs4_async_read_error(head);
+	goto out;
+}
+
+static int
+nfs4_pagein_list(struct inode *inode, struct list_head *head)
+{
+	struct list_head	one_request;
+	int			error = 0;
+	unsigned int		pages = 0;
+	unsigned int		rpages = NFS4_RPAGES(inode->i_sb);
+
+	NFS4_ASSERT(!list_empty(head));
+	
+	while (!list_empty(head)) {
+		INIT_LIST_HEAD(&one_request);
+		pages += nfs4_coalesce_requests(head, &one_request, rpages);
+		NFS4_ASSERT(!list_empty(&one_request));
+
+		/*
+		 * If nfs4_pagein_one() fails, it will release pages
+		 * on &one_request.
+		 */
+		error = nfs4_pagein_one(&one_request);
+		if (error < 0)
+			break;
+	}
+	if (error >= 0)
+		return pages;
+
+	nfs4_async_read_error(head);
+	return error;
+}
+
+static inline int
+nfs4_scan_read(struct inode *inode, struct list_head *dst,
+	      unsigned long idx_start, unsigned int npages)
+{
+	int	res;
+	
+	spin_lock(&nfs4_wreq_lock);
+	res = nfs4_scan_list(&NFS4_FLUSHD_READ(inode), dst, NULL, idx_start, npages);
+	NFS4_FLUSHD_NREAD(inode) -= res;
+#if 0
+	NFS4_ASSERT(NFS4_FLUSHD_NREAD(inode) >= 0);
+	NFS4_ASSERT((NFS4_FLUSHD_NREAD(inode) == 0) == list_empty(&NFS4_FLUSHD_READ(inode)));
+#endif
+	spin_unlock(&nfs4_wreq_lock);
+	
+	return res;
+}
+
+static inline int
+nfs4_scan_read_timeout(struct inode *inode, struct list_head *dst)
+{
+	int	pages;
+	
+	spin_lock(&nfs4_wreq_lock);
+	pages = nfs4_scan_list_timeout(&NFS4_FLUSHD_READ(inode), dst, inode);
+	NFS4_FLUSHD_NREAD(inode) -= pages;
+#if 0
+	NFS4_ASSERT(NFS4_FLUSHD_NREAD(inode) >= 0);
+	NFS4_ASSERT((NFS4_FLUSHD_NREAD(inode) == 0) == list_empty(&NFS4_FLUSHD_READ(inode)));
+#endif
+	spin_unlock(&nfs4_wreq_lock);
+	
+	return pages;
+}
+
+int
+nfs4_pagein_inode(struct inode *inode, unsigned long idx_start, unsigned int npages)
+{
+	LIST_HEAD(head);
+	int	res,
+		error = 0;
+
+	res = nfs4_scan_read(inode, &head, idx_start, npages);
+	if (res)
+		error = nfs4_pagein_list(inode, &head);
+	if (error < 0)
+		return error;
+	return res;
+}
+
+int
+nfs4_pagein_timeout(struct inode *inode)
+{
+	LIST_HEAD(head);
+	int	pages,
+		error = 0;
+
+	pages = nfs4_scan_read_timeout(inode, &head);
+	if (pages)
+		error = nfs4_pagein_list(inode, &head);
+	if (error < 0)
+		return error;
+	return pages;
+}
--- clean/fs/nfs4fs/state.c	Thu Feb  7 18:04:30 2002
+++ dirty/fs/nfs4fs/state.c	Mon Feb  4 15:16:24 2002
@@ -0,0 +1,1438 @@
+/*
+ *  linux/fs/nfs4fs/state.c
+ *
+ *  Copyright (c) 2002 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  Kendrick Smith <kmsmith@umich.edu>
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the University nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This file contains our client-side implementation of the NFSv4 state model.
+ *
+ * TODO: A outstanding problem: If we are running low on memory and want
+ *       to purge some inodes, we can't do it if a delegation is holding
+ *       "placeholder" OPENs which in turn claim a reference to the inode.
+ *       Possible solutions:
+ *        (1) Expire delegations, or perhaps just placeholders, after a timeout.
+ *        (2) Set a hard limit on the number of delegations, or perhaps the
+ *          number of placeholders; expire in LRU fashion if new ones are added.
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/quotaops.h>
+#include <linux/sunrpc/sched.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+
+#include <linux/nfs4/nfs4.h>
+#include <linux/nfs4/nfs4_debug.h>
+#include <linux/nfs4/nfs4fs_xdr.h>
+#include <linux/nfs4/nfs4fs.h>
+
+static void close_handler(struct nfs4fs_compound *cp);
+static void delegreturn_handler(struct nfs4fs_compound *cp);
+static void delegreturn_panic(struct nfs4fs_delegation *dp);
+static void __delegreturn_panic(struct nfs4fs_delegation *dp, struct nfs4fs_file_data *fp);
+static void returnd(struct rpc_task *task);
+static void returnd_exit(struct rpc_task *task);
+static int do_undelegate_sync(struct nfs4fs_file_data *fp, struct nfs4fs_compound *cp);
+static int do_undelegate_async(struct rpc_task *returnd, struct nfs4fs_file_data *fp);
+
+/* This protects most of the client-side state. */
+static spinlock_t		state_spinlock = SPIN_LOCK_UNLOCKED;
+
+/*
+ * Lockowners live in two hashtables:
+ *   lockowner_tbl - hashed by inode+pid
+ *   lockowner_id_tbl - hashed by ->lo_id
+ * TODO: worry about wraparound in global_lockowner_id.  Maybe just make 64-bit and
+ * forget about it?
+ */
+#define LOCKOWNER_HASH_BITS	8
+#define LOCKOWNER_HASH_SIZE	(1 << LOCKOWNER_HASH_BITS)
+#define LOCKOWNER_HASH_MASK	(LOCKOWNER_HASH_SIZE - 1)
+
+static u32 global_lockowner_id = 0;	/* protected by state_spinlock */
+static struct list_head lockowner_tbl[LOCKOWNER_HASH_SIZE];
+static struct list_head lockowner_id_tbl[LOCKOWNER_HASH_SIZE];
+
+/* TODO: better hash functions here, also below in delgation_hashval() */
+#define lockowner_hashval(inode)						\
+	(((opaque_hashval(&inode, sizeof(inode))) +			       	\
+	  (opaque_hashval(&current->pid, sizeof(current->pid)))) & LOCKOWNER_HASH_MASK)
+#define lockowner_id_hashval(id)		((id) & LOCKOWNER_HASH_MASK)
+
+/*
+ * Delegations are hashed by sb+stateid
+ */
+#define DELEG_HASH_BITS			8
+#define DELEG_HASH_SIZE			(1 << DELEG_HASH_BITS)
+#define DELEG_HASH_MASK			(DELEG_HASH_SIZE - 1)
+
+static struct list_head			delegation_hashtbl[DELEG_HASH_SIZE];
+
+#define delegation_hashval(sb, stateid)					\
+(( opaque_hashval((sb), sizeof(struct super_block *)) +			\
+   opaque_hashval((stateid), sizeof(stateid4)) ) & DELEG_HASH_MASK)
+
+/* RPC tasks wait here while waiting for an OPEN to be "undelegated". */
+static struct rpc_wait_queue	returnd_queue = RPC_INIT_WAITQ("nfs4_returnd");
+
+void
+nfs4fs_init_state(void)
+{
+	int i;
+
+	for (i = 0; i < LOCKOWNER_HASH_SIZE; i++) {
+		INIT_LIST_HEAD(&lockowner_tbl[i]);
+		INIT_LIST_HEAD(&lockowner_id_tbl[i]);
+	}
+	for (i = 0; i < DELEG_HASH_SIZE; i++)
+		INIT_LIST_HEAD(&delegation_hashtbl[i]);
+}
+
+void
+nfs4fs_cleanup_state(void)
+{
+	/* empty for now */
+}
+
+/*
+ * nfs4fs_get_client(): returns an empty client structure
+ * nfs4fs_put_client(): drops reference to client structure
+ */
+struct nfs4fs_client *
+nfs4fs_get_client(void)
+{
+	struct nfs4fs_client *clp;
+
+	if ((clp = NFS4_ALLOC(sizeof(*clp)))) {
+		atomic_set(&clp->cl_count, 1);
+		clp->cl_clientid = 0;
+		INIT_LIST_HEAD(&clp->cl_lockowners);
+	}
+	return clp;
+}
+
+void
+nfs4fs_put_client(struct nfs4fs_client *clp)
+{
+	NFS4_ASSERT(clp != NULL);
+	NFS4_ASSERT(atomic_read(&clp->cl_count) != 0);
+	
+	if (atomic_dec_and_test(&clp->cl_count)) {
+		NFS4_ASSERT(list_empty(&clp->cl_lockowners));
+		NFS4_FREE(clp);
+	}
+}
+
+/*
+ * nfs4fs_get_open_owner(): this is called on the OPEN path to obtain a new open_owner.
+ * nfs4fs_get_lock_owner(): called whenever we need a lock_owner corresponding to the given
+ *                          inode+pid.  (Currently used on the LOCK and LOCKT paths.)
+ * nfs4fs_put_lock_owner(): drops reference to either open_owner or lock_owner.
+ */
+static struct nfs4fs_lockowner *
+nfs4fs_get_open_owner(struct super_block *sb)
+{
+	struct nfs4fs_client *clp;
+	struct nfs4fs_lockowner *lp;
+
+	if ((lp = NFS4_ALLOC(sizeof(*lp)))) {
+		clp = NFS4_CLIENT(sb);
+		NFS4_ASSERT(clp != NULL);
+		atomic_inc(&clp->cl_count);
+		
+		atomic_set(&lp->lo_count, 1);
+		lp->lo_client = clp;
+		INIT_LIST_HEAD(&lp->lo_stateids);
+		INIT_LIST_HEAD(&lp->lo_hash);     /* open_owners are not hashed */
+		INIT_LIST_HEAD(&lp->lo_id_hash);  /* open_owners are not hashed */
+		lp->lo_inode = NULL;              /* no inode for open_owner */
+		lp->lo_pid = 0;                   /* no pid for open_owner */
+		init_MUTEX(&lp->lo_sema);
+		lp->lo_seqid = 0;                 /* arbitrary */
+		
+		spin_lock(&state_spinlock);
+		list_add(&lp->lo_list, &clp->cl_lockowners);
+		lp->lo_id = global_lockowner_id++;
+		spin_unlock(&state_spinlock);
+	}
+	return lp;
+}
+
+/*
+ * Hashtable lookup: find a lockowner by inode+pid.
+ * Returns a reference; call with spinlock held.
+ */
+static inline struct nfs4fs_lockowner *
+__find_lock_owner_by_inode(unsigned int hashval, struct inode *inode)
+{
+	struct list_head *l;
+	struct nfs4fs_lockowner *lp;
+
+	NFS4_ASSERT(hashval == lockowner_hashval(inode));
+	
+	list_for_each(l, &lockowner_tbl[hashval]) {
+		lp = list_entry(l, struct nfs4fs_lockowner, lo_hash);
+		if ((lp->lo_inode == inode) &&
+		    (lp->lo_pid == current->pid)) {
+			atomic_inc(&lp->lo_count);
+			return lp;
+		}
+	}
+	return NULL;
+}
+
+struct nfs4fs_lockowner *
+nfs4fs_get_lock_owner(struct inode *inode)
+{
+	struct nfs4fs_client *clp;
+	struct nfs4fs_lockowner *lp, *lp2;
+	u32 id;
+	unsigned int hashval = lockowner_hashval(inode);
+
+	spin_lock(&state_spinlock);
+	lp = __find_lock_owner_by_inode(hashval, inode);
+	spin_unlock(&state_spinlock);
+	if (lp)
+		return lp;
+
+	if (!(lp = NFS4_ALLOC(sizeof(*lp))))
+		return NULL;
+
+	/* retry the search, since we may have slept. */
+	spin_lock(&state_spinlock);
+	lp2 = __find_lock_owner_by_inode(hashval, inode);
+	if (lp2) {
+		spin_unlock(&state_spinlock);
+		NFS4_FREE(lp);
+		return lp2;
+	}
+
+	/*
+	 * Retried search failed, so initialize the new lock_owner.
+	 * Note: we never dereference lp->lo_inode, so we don't need a reference.
+	 * (The pointer is used strictly as a hash key.)
+	 */
+	id = global_lockowner_id++;
+	clp = NFS4_CLIENT(inode->i_sb);
+	NFS4_ASSERT(clp != NULL);
+	atomic_inc(&clp->cl_count);
+	
+	atomic_set(&lp->lo_count, 1);
+	lp->lo_client = clp;
+	INIT_LIST_HEAD(&lp->lo_stateids);
+	list_add(&lp->lo_list, &clp->cl_lockowners);
+	lp->lo_id = id;
+	list_add(&lp->lo_hash, &lockowner_tbl[hashval]);
+	list_add(&lp->lo_id_hash, &lockowner_id_tbl[lockowner_id_hashval(id)]);
+	lp->lo_inode = inode;
+	lp->lo_pid = current->pid;
+	init_MUTEX(&lp->lo_sema);
+	lp->lo_seqid = 0;     /* arbitrary */
+	
+	spin_unlock(&state_spinlock);
+	return lp;
+}
+
+void
+nfs4fs_put_lockowner(struct nfs4fs_lockowner *lp)
+{
+	struct nfs4fs_client *clp;
+
+	NFS4_ASSERT(lp != NULL);
+	NFS4_ASSERT(atomic_read(&lp->lo_count) != 0);
+
+	spin_lock(&state_spinlock);
+	if (!atomic_dec_and_test(&lp->lo_count)) {
+		spin_unlock(&state_spinlock);
+		return;
+	}
+	
+	list_del_init(&lp->lo_hash);
+	list_del_init(&lp->lo_id_hash);
+	list_del_init(&lp->lo_list);
+	clp = lp->lo_client;
+	spin_unlock(&state_spinlock);
+
+	NFS4_ASSERT(list_empty(&lp->lo_stateids));
+	NFS4_FREE(lp);
+	nfs4fs_put_client(clp);
+}
+
+/*
+ * Hashtable lookup: find a lockowner by ->lo_id, return the corresponding pid.
+ * If none is found, we return a default pid.  Currently, we use the pid of rpciod.
+ * XXX: Maybe the pid of init is better?
+ *
+ * This is used when processing the LOCKT response from the server.
+ */
+pid_t
+nfs4fs_id_to_pid(unsigned int id)
+{
+	struct list_head *p;
+	struct nfs4fs_lockowner *lockowner;
+
+	spin_lock(&state_spinlock);
+	list_for_each(p, &lockowner_id_tbl[lockowner_id_hashval(id)]) {
+		lockowner = list_entry(p, struct nfs4fs_lockowner, lo_id_hash);
+		if (lockowner->lo_id == id) {
+			spin_unlock(&state_spinlock);
+			return lockowner->lo_pid;
+		}
+	}
+	spin_unlock(&state_spinlock);
+	return 1;
+}
+
+/*
+ * Now we come to the routines for manipulating the file_data structure.
+ * These are the most complex part of the client-side state.
+ *
+ * nfs4fs_get_open_stateid(): returns a stateid suitable for OPEN.
+ * nfs4fs_get_read_stateid():    "    "    "       "      "  READ.
+ * nfs4fs_get_write_stateid():   "    "    "       "      "  WRITE.
+ * nfs4fs_get_lock_stateid():    "    "    "       "      "  LOCK.
+ * nfs4fs_get_locku_stateid():   "    "    "       "      "  LOCKU.
+ * nfs4fs_instantiate_open(): does all of the work necessary to turn a "fake" open
+ *                     (i.e. one whose FI_FAKE flag is set) into a full productive
+ *                     member of society.  It is called by handle_open() and friends.
+ *
+ * nfs4fs_put_file_data():    drops reference to file_data, initiates CLOSE
+ *                            if this reference is the last one.
+ */
+
+/* Note: 'inode' can be NULL here... */
+struct nfs4fs_file_data *
+nfs4fs_get_open_stateid(struct super_block *sb, struct inode *inode, int open_flags)
+{
+	struct nfs4fs_lockowner *lp;
+	struct nfs4fs_file_data *fp;
+
+	if (!(fp = NFS4_ALLOC(sizeof(*fp))))
+		return NULL;
+	/*
+	 * We currently create a new open_owner every time a file is opened.
+	 * Eventual TODO: Analyze whether this is optimal.  The advantage of
+	 * such highly granulated OPENs is minimal serialization.  The disadvantage
+	 * is extra state overhead and more OPEN_CONFIRMs.
+	 */
+	if (!(lp = nfs4fs_get_open_owner(sb))) {
+		NFS4_FREE(fp);
+		return NULL;
+	}
+	
+	if (inode) {
+		inode = igrab(inode);
+		NFS4_ASSERT(inode != NULL);
+	}
+
+	/* state_spinlock not needed here... */
+	atomic_set(&fp->fi_count, 1);
+	fp->fi_lockowner = lp;
+	fp->fi_inode = inode;
+	INIT_LIST_HEAD(&fp->fi_read);
+	INIT_LIST_HEAD(&fp->fi_write);
+	INIT_LIST_HEAD(&fp->fi_linkage);
+	fp->fi_open_stateid = NULL;
+	nfs4_init_rwlock(&fp->fi_sema);
+	fp->fi_flags = OPEN_FLAGS_TO_FI_FLAGS(open_flags) | FI_FAKE;
+	fp->fi_delegation = NULL;
+
+	/* XXX: actually, don't we have the BKL here? */
+	spin_lock(&state_spinlock);
+	list_add(&fp->fi_list, &lp->lo_stateids);
+	spin_unlock(&state_spinlock);
+
+	return fp;
+}
+
+/*
+ * Finds a lock_stateid associated with the current pid and specified open_stateid, or
+ * NULL if none exists.  Returns a reference; call with spinlock held.
+ *
+ * XXX: Currently based on linear search through lock stateids.  I don't expect
+ * this to be a bottleneck in practice, but it could be implemented as a hash
+ * lookup by open_stateid+pid.
+ */
+static inline struct nfs4fs_file_data *
+__try_to_get_lock_stateid(struct nfs4fs_file_data *ofp)
+{
+	struct list_head *l;
+	struct nfs4fs_file_data *fp;
+
+	list_for_each(l, &ofp->fi_linkage) {
+		fp = list_entry(l, struct nfs4fs_file_data, fi_linkage);
+		if (fp->fi_lockowner->lo_pid == current->pid) {
+			/*
+			 * This is the refcounting subtletly described in nfs4fs.h
+			 * near 'fi_linkage'.
+			 */
+			atomic_inc(&fp->fi_count);
+			if (atomic_read(&fp->fi_count) == 1)
+				atomic_inc(&ofp->fi_count);
+			return fp;
+		}
+	}
+	return NULL;
+}
+
+/* 'filp' can be NULL in the case of an mmap-ed file. */
+struct nfs4fs_file_data *
+nfs4fs_get_read_stateid(struct file *filp, struct inode *inode)
+{
+	struct nfs4fs_file_data *ofp;
+	struct nfs4fs_file_data *fp;
+	struct list_head *l;
+
+	NFS4_ASSERT(inode != NULL);
+	NFS4_ASSERT(!filp || (filp->f_dentry->d_inode == inode));
+	
+	spin_lock(&state_spinlock);
+	if (filp) {
+		ofp = (struct nfs4fs_file_data *) filp->private_data;
+		NFS4_ASSERT(ofp != NULL);
+		if (!(ofp->fi_flags & FI_FAKE)) {
+			atomic_inc(&ofp->fi_count);
+			goto got_ofp;
+		}
+		nfsv4_printk(read, "get_read_stateid: open_stateid is fake...\n");
+	}
+
+	/*
+	 * No open_stateid available (mmap), or open_stateid is fake (probably because
+	 * this OPEN has been delegated).  Because of placeholder opens, we should have
+	 * one on the list...
+	 */
+	if (list_empty(&NFS4_READ_OPENS(inode))) {
+		spin_unlock(&state_spinlock);
+		nfsv4_printk(level1, "get_read_stateid: none found?!\n");
+		return NULL;
+	}
+	l = NFS4_READ_OPENS(inode).next;
+	ofp = list_entry(l, struct nfs4fs_file_data, fi_read);
+	atomic_inc(&ofp->fi_count);
+
+got_ofp:
+	/*
+	 * If we get here, we have located an open stateid corresponding to
+	 * an OPEN for read on the server.  Now see if a subordinate lock
+	 * stateid can be found (if available, a lock stateid is to be preferred
+	 * to an open stateid).
+	 */
+	fp = __try_to_get_lock_stateid(ofp);
+	spin_unlock(&state_spinlock);
+	
+	if (fp) {
+		if (!(fp->fi_flags & FI_FAKE)) {
+			nfs4fs_put_file_data(ofp);
+			return fp;
+		}
+		nfs4fs_put_file_data(fp);
+	}
+	return ofp;	
+}
+
+/*
+ * Essentially identical to get_read_stateid(); consequently some comments
+ * have been suppressed.
+ */
+struct nfs4fs_file_data *
+nfs4fs_get_write_stateid(struct file *filp, struct inode *inode)
+{
+	struct nfs4fs_file_data *ofp;
+	struct nfs4fs_file_data *fp;
+	struct list_head *l;
+
+	NFS4_ASSERT(inode != NULL);
+	NFS4_ASSERT(!filp || (filp->f_dentry->d_inode == inode));
+	
+	spin_lock(&state_spinlock);
+	if (filp) {
+		ofp = (struct nfs4fs_file_data *) filp->private_data;
+		NFS4_ASSERT(ofp != NULL);
+		if (!(ofp->fi_flags & FI_FAKE)) {
+			atomic_inc(&ofp->fi_count);
+			goto got_ofp;
+		}
+		nfsv4_printk(write, "get_write_stateid: open_stateid is fake...\n");
+	}
+
+	if (list_empty(&NFS4_WRITE_OPENS(inode))) {
+		spin_unlock(&state_spinlock);
+		nfsv4_printk(level1, "get_write_stateid: none found?!\n");
+		return NULL;
+	}
+	l = NFS4_WRITE_OPENS(inode).next;
+	ofp = list_entry(l, struct nfs4fs_file_data, fi_write);
+	atomic_inc(&ofp->fi_count);
+
+got_ofp:
+	/*
+	 * If we get here, we have located an open stateid corresponding to
+	 * an OPEN for write on the server.  Now see if a subordinate lock
+	 * stateid can be found (if available, a lock stateid is to be preferred
+	 * to an open stateid).
+	 */
+	fp = __try_to_get_lock_stateid(ofp);
+	spin_unlock(&state_spinlock);
+
+	if (fp) {
+		if (!(fp->fi_flags & FI_FAKE)) {
+			nfs4fs_put_file_data(ofp);
+			return fp;
+		}
+		nfs4fs_put_file_data(fp);
+	}
+	return ofp;	
+}
+
+struct nfs4fs_file_data *
+nfs4fs_get_lock_stateid(struct file *filp, struct nfs4fs_compound *cp)
+{
+	struct inode *inode = filp->f_dentry->d_inode;
+	struct nfs4fs_file_data *ofp = (struct nfs4fs_file_data *) filp->private_data;
+	struct nfs4fs_lockowner *lp;
+	struct nfs4fs_file_data *fp, *fp2;
+
+	NFS4_ASSERT(kernel_locked());
+	NFS4_ASSERT(ofp != NULL);
+	NFS4_ASSERT(inode != NULL);
+
+	/*
+	 * If the open_stateid is currently "delegated", we undelegate it.
+	 */
+	if (ofp->fi_delegation) {
+		if (do_undelegate_sync(ofp, cp)) {
+			nfsv4_printk(lock2, "nfs4_lock: couldn't undelegate OPEN!\n");
+			return NULL;
+		}
+		NFS4_ASSERT(ofp->fi_delegation == NULL);
+	}
+	
+	/* the spinlock would normally be needed here, but caller holds BKL... */
+	fp = __try_to_get_lock_stateid(ofp);
+	if (fp)
+		return fp;
+	
+	if (!(fp = NFS4_ALLOC(sizeof(*fp))))
+		return NULL;
+	if (!(lp = nfs4fs_get_lock_owner(inode))) {
+		NFS4_FREE(fp);
+		return NULL;
+	}
+	if (inode) {
+		inode = igrab(inode);
+		NFS4_ASSERT(inode != NULL);
+	}
+
+	/* retry the search, since we may have slept. */
+	/* the spinlock would normally be needed here, but caller holds BKL... */
+	fp2 = __try_to_get_lock_stateid(ofp);
+	if (fp2) {
+		iput(inode);
+		nfs4fs_put_lockowner(lp);
+		NFS4_FREE(fp);
+		return fp2;
+	}
+
+	/*
+	 * The retried search failed, so initialize the new lock_stateid.
+	 * The spinlock would normally be needed here, but caller holds BKL...
+	 */
+
+	/* This is the refcounting subtlety described in nfs4fs.h near "fi_linkage" */
+	atomic_set(&fp->fi_count, 1);
+	atomic_inc(&ofp->fi_count);
+	
+	fp->fi_lockowner = lp;
+	list_add(&fp->fi_list, &lp->lo_stateids);
+	fp->fi_inode = inode;
+	INIT_LIST_HEAD(&fp->fi_read);
+	INIT_LIST_HEAD(&fp->fi_write);
+	list_add(&fp->fi_linkage, &ofp->fi_linkage);
+	fp->fi_open_stateid = ofp;
+	nfs4_init_rwlock(&fp->fi_sema);	
+	fp->fi_flags = FI_FAKE;
+	fp->fi_delegation = NULL;
+	
+	return fp;
+}
+
+/*
+ * We try to find a lock_stateid suitable for use in a LOCKU request.
+ * First, we see if any of the lock_stateids hanging off our file descriptor will
+ * work (i.e., have the right pid).  This will always be the case if it is the
+ * same file descriptor originally used to lock the file.
+ * Second, we see if there any active lock_stateids, associated with any file
+ * descriptor at all, with the right inode+pid.
+ *
+ * If both of these fail, we have no choice but to return NULL.
+ * TODO: What is the "right" way for the LOCKU path to handle this condition?
+ */
+struct nfs4fs_file_data *
+nfs4fs_get_locku_stateid(struct file *filp)
+{
+	struct nfs4fs_file_data *ofp = (struct nfs4fs_file_data *) filp->private_data;
+	struct nfs4fs_file_data *fp;
+	struct inode *inode;
+	unsigned int hashval;
+	struct nfs4fs_lockowner *lp;
+	struct list_head *l;
+
+	NFS4_ASSERT(kernel_locked());
+
+	/* the spinlock would normally be needed here, but caller holds BKL... */
+	fp = __try_to_get_lock_stateid(ofp);
+	if (fp) {
+		if (!(fp->fi_flags & FI_FAKE))
+			return fp;
+		nfs4fs_put_file_data(fp);
+	}
+
+	inode = filp->f_dentry->d_inode;
+	hashval = lockowner_hashval(inode);
+	if (!(lp = __find_lock_owner_by_inode(hashval, inode)))
+		goto out;
+	list_for_each(l, &lp->lo_stateids) {
+		fp = list_entry(l, struct nfs4fs_file_data, fi_list);
+		
+		/* XXX: Aren't these checks superfluous? */
+		if (IS_OPEN_STATEID(fp))
+			continue;
+		if (fp->fi_inode != inode)
+			continue;
+
+		/*
+		 * This is the refcounting subtlety described in nfs4fs.h
+		 * near "fi_linkage".
+		 */
+		atomic_inc(&fp->fi_count);
+		if (atomic_read(&fp->fi_count) == 1)
+			atomic_inc(&fp->fi_open_stateid->fi_count);
+		return fp;
+	}
+	
+out:
+	return NULL;
+}
+
+/*
+ * Note: this is protected by the BKL and fp->fi_sema
+ */
+void
+nfs4fs_instantiate_open(struct nfs4fs_file_data *fp)
+{
+	struct inode *inode = fp->fi_inode;
+	struct nfs4fs_delegation *dp;
+	int flags = fp->fi_flags;
+	struct nfs4fs_file_data *dropme = NULL;
+	struct nfs4fs_file_data *dropme2 = NULL;
+
+	nfsv4_printk(open, "instantiate_open: starting\n");
+	NFS4_ASSERT(IS_OPEN_STATEID(fp));
+	NFS4_ASSERT(fp->fi_flags & FI_FAKE);
+	NFS4_ASSERT(inode != NULL);
+	NFS4_ASSERT(kernel_locked());
+
+	/*
+	 * First: If we are already on read/write lists, remove ourselves...
+	 * Note!  Here and in several other places, the spinlock would normally
+	 * be required, but it is redundant since we hold the BKL...
+	 */
+	list_del_init(&fp->fi_read);
+	list_del_init(&fp->fi_write);
+
+	/*
+	 * Second: announce to the world that we are now a non-fake, undelegated OPEN...
+	 */
+	fp->fi_flags &= ~FI_FAKE;
+	dp = fp->fi_delegation;
+	if (dp) {
+		fp->fi_delegation = NULL;
+		nfs4fs_put_delegation(dp);
+	}
+
+	/*
+	 * Third: put ourselves on the inode's read/write lists.
+	 * Delegation placeholder pointers must be updated consistently.
+	 */
+	dp = NFS4_DELEGATION(inode);
+	if (flags & FI_READ) {
+		if (dp) {
+			if (list_empty(&NFS4_READ_OPENS(inode))) {
+				NFS4_ASSERT(dp->dl_read_placeholder == NULL);
+				dp->dl_read_placeholder = fp;
+				atomic_inc(&fp->fi_count);
+			}
+			else {
+				dropme = dp->dl_read_placeholder;
+				dp->dl_read_placeholder = NULL;
+			}
+		}
+		list_add(&fp->fi_read, &NFS4_READ_OPENS(inode));
+	}
+	if (flags & FI_WRITE) {
+		if (dp) {
+			if (list_empty(&NFS4_WRITE_OPENS(inode))) {
+				NFS4_ASSERT(dp->dl_write_placeholder == NULL);
+				dp->dl_write_placeholder = fp;
+				atomic_inc(&fp->fi_count);
+			}
+			else {
+				dropme2 = dp->dl_write_placeholder;
+				dp->dl_write_placeholder = NULL;
+			}
+		}
+		list_add(&fp->fi_write, &NFS4_WRITE_OPENS(inode));
+	}
+	
+	/*
+	 * This is done last so that the part labeled 'Third:' will
+	 * be atomic.  (It is atomic because we have the BKL.)
+	 */
+	if (dropme)
+		nfs4fs_put_file_data(dropme);
+	if (dropme2)
+		nfs4fs_put_file_data(dropme2);
+
+	nfsv4_printk(open, "instantiate_new_open: done\n");
+}
+
+void
+nfs4fs_put_file_data(struct nfs4fs_file_data *fp)
+{
+	struct nfs4fs_file_data *ofp;
+	struct nfs4fs_delegation *dp;
+	struct list_head *l;
+	struct inode *inode;
+	struct nfs4fs_compound *cp;
+
+	NFS4_ASSERT(fp != NULL);
+	NFS4_ASSERT(atomic_read(&fp->fi_count) != 0);
+
+	spin_lock(&state_spinlock);
+	if (!atomic_dec_and_test(&fp->fi_count)) {
+		spin_unlock(&state_spinlock);
+		return;
+	}
+	ofp = fp->fi_open_stateid;
+
+	if (ofp) {
+		/*
+		 * This is a lock stateid.  We just leave it intact for future use,
+		 * but drop the reference to the open_stateid.  This is the refcount
+		 * subtlety described in nfs4fs.h near "fi_linkage".
+		 */
+		spin_unlock(&state_spinlock);
+		nfs4fs_put_file_data(ofp);
+		return;
+	}
+
+	/*
+	 * While holding the spinlock, remove stateids rooted at
+	 * this one from all lists.  This ensures that no new
+	 * references can be acquired.
+	 */
+	list_del_init(&fp->fi_read);
+	list_del_init(&fp->fi_write);
+	list_del_init(&fp->fi_list);
+	list_for_each(l, &fp->fi_linkage) {
+		ofp = list_entry(l, struct nfs4fs_file_data, fi_linkage);
+		NFS4_ASSERT(list_empty(&ofp->fi_read));
+		NFS4_ASSERT(list_empty(&ofp->fi_write));
+		list_del_init(&ofp->fi_list);
+	}
+
+	/*
+	 * Since we updated the read/write lists, we must update the
+	 * delegation placeholder pointers consistently.
+	 */
+	inode = fp->fi_inode;
+	if (inode && ((dp = NFS4_DELEGATION(inode)))) {
+		if (list_singleton(&NFS4_READ_OPENS(inode)) && !dp->dl_read_placeholder) {
+			l = NFS4_READ_OPENS(inode).next;
+			ofp = list_entry(l, struct nfs4fs_file_data, fi_read);
+			dp->dl_read_placeholder = ofp;
+			atomic_inc(&ofp->fi_count);
+		}
+		if (list_singleton(&NFS4_WRITE_OPENS(inode)) && !dp->dl_write_placeholder) {
+			l = NFS4_WRITE_OPENS(inode).next;
+			ofp = list_entry(l, struct nfs4fs_file_data, fi_write);
+			dp->dl_write_placeholder = ofp;
+			atomic_inc(&ofp->fi_count);
+		}
+	
+	}
+	spin_unlock(&state_spinlock);
+
+	/*
+	 * If this OPEN has been delegated, drop the reference to the delegation.
+	 */
+	dp = fp->fi_delegation;
+	if (dp) {
+		fp->fi_delegation = NULL;
+		nfs4fs_put_delegation(dp);
+	}
+
+	/*
+	 * Free all lock stateids now.
+	 */
+	while (!list_empty(&fp->fi_linkage)) {
+		ofp = list_entry(fp->fi_linkage.next, struct nfs4fs_file_data, fi_linkage);
+		NFS4_ASSERT(atomic_read(&ofp->fi_count) == 0);
+		NFS4_ASSERT(list_empty(&ofp->fi_list));
+		NFS4_ASSERT(list_empty(&ofp->fi_read));
+		NFS4_ASSERT(list_empty(&ofp->fi_write));
+		NFS4_ASSERT(ofp->fi_open_stateid == fp);
+		NFS4_ASSERT(ofp->fi_delegation == NULL);
+
+		list_del_init(&ofp->fi_linkage);
+		iput(ofp->fi_inode);
+		nfs4fs_put_lockowner(ofp->fi_lockowner);
+		ofp->fi_inode = NULL;		/* paranoid */
+		ofp->fi_lockowner = NULL;	/* paranoid */
+		NFS4_FREE(ofp);
+	}
+	
+	/*
+	 * To completely finish freeing the file_data, we have to call
+	 *   - iput(fp->fi_inode)
+	 *   - nfs4fs_release_lockowner(fp->fi_lockowner)
+	 *   - NFS4_FREE(fp).
+	 *
+	 * If we dispatch a CLOSE, this will be taken care of in nfs4fs_release_compound().
+	 * If no CLOSE is dispatched, we have to do it here...
+	 *
+	 * Note: We have to hang on to the inode until the CLOSE operation is finished.
+	 * This is because the PUTFH xdr encode gets its filehandle from the inode, so
+	 * we need to keep a reference...
+	 */
+	if (fp->fi_flags & FI_FAKE) {
+		/*
+		 * This file_data is not backed by an OPEN on the server, probably
+		 * because it was a delegated OPEN.  No need to dispatch CLOSE...
+		 */
+		iput(fp->fi_inode);
+		nfs4fs_put_lockowner(fp->fi_lockowner);
+		fp->fi_inode = NULL;		/* paranoid */
+		fp->fi_lockowner = NULL;	/* paranoid */
+		NFS4_FREE(fp);
+		return;
+	}
+
+	NFS4_ASSERT(fp->fi_inode != NULL);
+	if (nfs4fs_setup_async(&cp, fp->fi_inode->i_sb, "close", NULL, close_handler, NULL))
+		goto fail_finish;
+	nfs4fs_setup_putfh(cp, fp->fi_inode);
+	nfs4fs_setup_close(cp, fp);
+
+	if (nfs4fs_call_async(cp))
+		goto fail;    /* XXX: check to make sure ->tk_release() is always called */
+	return;
+	
+fail_finish:
+	iput(fp->fi_inode);
+	nfs4fs_put_lockowner(fp->fi_lockowner);
+	fp->fi_inode = NULL;		/* paranoid */
+	fp->fi_lockowner = NULL;	/* paranoid */
+	NFS4_FREE(fp);
+fail:
+	printk(KERN_WARNING "nfs4: couldn't close file on server!\n");
+}
+
+/* exit routine for async CLOSE */
+static void
+close_handler(struct nfs4fs_compound *cp)
+{
+	if (nfs4fs_handle_putfh(cp))
+		goto err;
+	if (nfs4fs_handle_close(cp))
+		goto err;
+	return;
+
+err:
+	printk(KERN_WARNING "nfs4: warning: couldn't close file on server!\n");
+}
+
+/*
+ * Next are the routines for manipulating delegations.
+ * This includes handling the delegation return process, and the "undelegation" process.
+ *   nfs4fs_accept_delegation():
+ *         called during OPEN path when a delegation is received, instantiates the
+ *         delegation in NFS4_DELEGATION(inode).
+ *   nfs4fs_find_delegation_by_stateid():
+ *         returns reference to the delegation with given stateid (called from CB_RECALL path).
+ *   nfs4fs_killall_placeholders():
+ *         removes all placeholder OPENs on this superblock (called from unmount path).
+ *   nfs4fs_put_delegation():
+ *         drops reference to delegation, initiates DELEGRETURN if this reference is
+ *         the last one.
+ *   nfs4fs_drop_delegation_from_inode():
+ *         drops inode's reference to the delegation.  This means that no new delegated
+ *         OPENs can be generated.  Consequently, we also flush the inode's access cache.
+ */
+
+void
+nfs4fs_accept_delegation(struct dentry *dentry, struct inode *inode,
+			 u32 type, stateid4 new_stateid)
+{
+	struct super_block *sb;
+	unsigned int hashval;
+	struct nfs4fs_delegation *dp, *new_dp;
+	struct list_head *l;
+	struct nfs4fs_file_data *fp;
+
+	NFS4_ASSERT(kernel_locked());
+	NFS4_ASSERT(inode != NULL);
+	NFS4_ASSERT(type == OPEN_DELEGATE_READ || type == OPEN_DELEGATE_WRITE);
+	
+	sb = inode->i_sb;
+	
+	/*
+	 * TODO: The space_limit and ACE returned with the delegation
+	 * are currently ignored (in fact, we skip them in XDR decode).
+	 * Handle them correctly!
+	 */
+
+	if (!(new_dp = NFS4_ALLOC(sizeof(*new_dp))))
+		goto nomem;
+	atomic_set(&new_dp->dl_count, 1);
+	new_dp->dl_sb = sb;
+	new_dp->dl_inode = NULL;
+	INIT_LIST_HEAD(&new_dp->dl_hash);
+	INIT_LIST_HEAD(&new_dp->dl_read_opens);
+	INIT_LIST_HEAD(&new_dp->dl_write_opens);
+	INIT_LIST_HEAD(&new_dp->dl_list);
+	new_dp->dl_read_placeholder = NULL;
+	new_dp->dl_write_placeholder = NULL;
+	new_dp->dl_type = type;
+	memcpy(new_dp->dl_stateid, new_stateid, sizeof(stateid4));
+	new_dp->dl_fhlen = 0;		/* paranoid */
+	new_dp->dl_filename_len = 0;	/* paranoid */
+
+	/*
+	 * If the dentry is NULL, this means that the caller wants us to
+	 * return the delegation immediately.
+	 */
+	if (!dentry)
+		goto return_it;
+
+	nfsv4_printk(callback, "accept_delegation: starting %p\n", inode);
+
+	/*
+	 * Fill in the information needed for the undelegation process:
+	 * filename and parent filehandle.
+	 * XXX - this is a temporary hack, it looks like the need for it will
+	 * soon disappear.  Consequently I put in some hardcoded limits for
+	 * convenience...
+	 */
+	if (NFS4_FH_LEN(dentry->d_parent->d_inode) > sizeof(new_dp->dl_fhval)) {
+		printk("accept_delegation: broke hardcoded limit on fhlen\n");
+		goto return_it;
+	}
+	if (dentry->d_name.len > sizeof(new_dp->dl_filename)) {
+		printk("accept_delegation: broke hardcoded limit on filename len\n");
+		goto return_it;
+	}
+	new_dp->dl_fhlen = NFS4_FH_LEN(dentry->d_parent->d_inode);
+	memcpy(new_dp->dl_fhval, NFS4_FH_VAL(dentry->d_parent->d_inode),
+	       NFS4_FH_LEN(dentry->d_parent->d_inode));
+	new_dp->dl_filename_len = dentry->d_name.len;
+	memcpy(new_dp->dl_filename, dentry->d_name.name, dentry->d_name.len);
+	
+	/*
+	 * We refuse to hold multiple delegations for the same inode.
+	 * If a delegation is already held, we either return it or return
+	 * the current one, depending on which has the stronger delegation
+	 * type.  Note that if we sleep, we have to retry the check on the
+	 * inode's delegation.
+	 */
+	for (;;) {
+		dp = NFS4_DELEGATION(inode);
+		if (!dp) {                      /* normal case */
+			/*
+			 * Success!  Instantiate this delegation..
+			 *
+			 * Note: We do _not_ claim a reference to the inode in dp->dl_inode!
+			 * Instead, we return the delegation in the inode's ->clear_inode()
+			 * method.  This has the advantage that extra logic for returning
+			 * infrequently-used delegations is not needed: it is encapsulated
+			 * in the logic for purging infrequently-used inodes.
+			 *
+			 * We cannot race against clear_inode(), since our caller holds a
+			 * reference to the inode.
+			 */
+			hashval = delegation_hashval(sb, new_stateid);
+			list_add(&new_dp->dl_hash, &delegation_hashtbl[hashval]);
+			list_add(&new_dp->dl_list, &NFS4_DELEGATIONS(sb));
+			new_dp->dl_inode = inode;
+			NFS4_DELEGATION(inode) = new_dp;
+
+			/*
+			 * When we instantiate the delegation, we must update placeholder
+			 * pointers consistently.
+			 */
+			if (list_singleton(&NFS4_READ_OPENS(inode))) {
+				l = NFS4_READ_OPENS(inode).next;
+				fp = list_entry(l, struct nfs4fs_file_data, fi_read);
+				new_dp->dl_read_placeholder = fp;
+				atomic_inc(&fp->fi_count);
+			}
+			if (list_singleton(&NFS4_WRITE_OPENS(inode))) {
+				l = NFS4_WRITE_OPENS(inode).next;
+				fp = list_entry(l, struct nfs4fs_file_data, fi_write);
+				new_dp->dl_write_placeholder = fp;
+				atomic_inc(&fp->fi_count);
+			}
+			
+			nfsv4_printk(callback, "accept_delegation: success %p\n", inode);
+			return;
+		}
+		
+		if ((type != OPEN_DELEGATE_WRITE) || (dp->dl_type == OPEN_DELEGATE_WRITE))
+			break;
+
+		/*
+		 * May as well take advantage of the chance to replace a read delegation
+		 * by a write delegation...
+		 */
+		nfsv4_printk(callback, "accept_delegation: read->write?!\n");
+		nfs4fs_drop_delegation_from_inode(inode);
+	}
+
+return_it:
+	nfsv4_printk(callback, "accept_delegation: this delegation will be returned\n");
+	nfs4fs_put_delegation(new_dp);   /* this will return the delegation */
+	return;
+nomem:
+	/* TODO - recover better */
+	printk(KERN_WARNING "nfs4: insufficient memory to accept delegation!\n");
+}
+
+struct nfs4fs_delegation *
+nfs4fs_find_delegation_by_stateid(struct super_block *sb, stateid4 stateid)
+{
+	unsigned int hashval = delegation_hashval(sb, stateid);
+	struct list_head *l;
+	struct nfs4fs_delegation *dp;
+
+	NFS4_ASSERT(kernel_locked());
+
+	/* normally the spinlock would be needed here, but we have the BKL... */
+	list_for_each(l, &delegation_hashtbl[hashval]) {
+		dp = list_entry(l, struct nfs4fs_delegation, dl_hash);
+		if (dp->dl_sb == sb && !memcmp(dp->dl_stateid, stateid, sizeof(stateid4))) {
+			NFS4_ASSERT(dp->dl_inode != NULL);
+			NFS4_ASSERT(NFS4_DELEGATION(dp->dl_inode) == dp);
+			atomic_inc(&dp->dl_count);
+			return dp;
+		}
+	}
+	return NULL;
+}
+
+void
+nfs4fs_killall_placeholders(struct super_block *sb)
+{
+	struct list_head *l;
+	struct nfs4fs_delegation *dp;
+	struct nfs4fs_file_data *fp;
+
+	nfsv4_printk(open, "nfs4fs_killall_placeholders: starting\n");
+	lock_kernel();
+
+	/*
+	 * TODO: This is inefficient (quadratic in the number of delegations held),
+	 * but since we only pay the cost at unmount time, I haven't tried to
+	 * solve the problem of finding anything better.
+	 */
+top:
+	list_for_each(l, &NFS4_DELEGATIONS(sb)) {
+		dp = list_entry(l, struct nfs4fs_delegation, dl_list);
+		
+		fp = dp->dl_read_placeholder;
+		if (fp) {
+			dp->dl_read_placeholder = NULL;
+			nfs4fs_put_file_data(fp);
+			goto top;
+		}
+		
+		fp = dp->dl_write_placeholder;
+		if (fp) {
+			dp->dl_write_placeholder = NULL;
+			nfs4fs_put_file_data(fp);
+			goto top;
+		}
+	}	
+
+	unlock_kernel();
+	nfsv4_printk(open, "nfs4fs_killall_placeholders: done\n");
+}
+
+void
+nfs4fs_put_delegation(struct nfs4fs_delegation *dp)
+{
+	struct nfs4fs_compound *cp;
+
+	NFS4_ASSERT(dp != NULL);
+	NFS4_ASSERT(atomic_read(&dp->dl_count) != 0);
+
+	spin_lock(&state_spinlock);
+	if (!atomic_dec_and_test(&dp->dl_count)) {
+		spin_unlock(&state_spinlock);
+		return;
+	}
+	list_del_init(&dp->dl_list);
+	spin_unlock(&state_spinlock);
+	
+	NFS4_ASSERT(list_empty(&dp->dl_hash));
+	NFS4_ASSERT(list_empty(&dp->dl_read_opens));
+	NFS4_ASSERT(list_empty(&dp->dl_write_opens));
+	NFS4_ASSERT(dp->dl_inode == NULL);
+
+	if (dp->dl_read_placeholder) {
+		nfs4fs_put_file_data(dp->dl_read_placeholder);
+		dp->dl_read_placeholder = NULL;
+	}
+	if (dp->dl_write_placeholder) {
+		nfs4fs_put_file_data(dp->dl_write_placeholder);
+		dp->dl_write_placeholder = NULL;
+	}
+
+	nfsv4_printk(callback, "put_delegation: attempting to queue async DELEGRETURN...\n");
+	if (nfs4fs_setup_async(&cp, dp->dl_sb, "delegreturn", NULL, delegreturn_handler, NULL))
+		goto err;
+	nfs4fs_setup_delegreturn(cp, dp->dl_stateid);
+
+	if (nfs4fs_call_async(cp))
+		goto err;	
+	nfsv4_printk(callback, "put_delegation: dispatched async DELEGRETURN\n");
+
+out:
+	/*
+	 * Now that we have extracted the stateid for use in the DELEGRETURN request,
+	 * we have no further use for the delegation.
+	 */
+	NFS4_FREE(dp);	
+	return;
+err:
+	printk(KERN_WARNING "nfs4: warning: couldn't return delegation!\n");
+	goto out;
+}
+
+/* exit routine for async DELEGRETURN */
+static void
+delegreturn_handler(struct nfs4fs_compound *cp)
+{
+	if (nfs4fs_handle_delegreturn(cp))
+		goto err;
+	nfsv4_printk(callback, "delegreturn_handler: success\n");
+	return;
+
+err:
+	printk(KERN_WARNING "nfs4: warning: couldn't return delegation!\n");
+	return;
+}
+
+void
+nfs4fs_drop_delegation_from_inode(struct inode *inode)
+{
+	struct nfs4fs_delegation *dp;
+	struct rpc_task *task;
+	int status;
+
+	/*
+	 * XXX: audit callers, see if they all hold the BKL already.
+	 * Maybe the lock_kernel() here is superfluous.
+	 */
+	nfsv4_printk(open, "nfs4fs_drop_delegation: starting, inode=%p\n", inode);
+	lock_kernel();
+	dp = NFS4_DELEGATION(inode);
+	if (!dp)
+		goto out;
+	
+	NFS4_ASSERT(!list_empty(&dp->dl_hash));
+	NFS4_ASSERT(dp->dl_inode == inode);
+		
+	list_del_init(&dp->dl_hash);
+	dp->dl_inode = NULL;
+	NFS4_DELEGATION(inode) = NULL;
+	nfs4fs_flush_access_cache(inode);
+
+	/*
+	 * At this point, no new delegated OPEN's can be generated.
+	 *
+	 * Optimization: If no OPEN's need to be "undelegated", just finish off
+	 * by dropping the inode's reference to the delegation.
+	 */
+	if (list_empty(&dp->dl_read_opens) && list_empty(&dp->dl_write_opens)) {
+		nfsv4_printk(callback, "drop_delegation: no undelegation needed...\n");
+		goto out_put;
+	}
+	
+	/*
+	 * Create an async RPC task to undelegate the OPENs.
+	 * This task is responsible for dropping the reference to the delegation.
+	 */
+	nfsv4_printk(callback, "drop_delegation: creating returnd...\n");
+	status = NFS4ERR_RESOURCE;
+	if (!(task = rpc_new_task(NFS4_CLNT(dp->dl_sb), returnd_exit, RPC_TASK_ASYNC))) {
+		nfsv4_printk(level1, "drop_delegation: couldn't create RPC task!\n");
+		goto out_panic;
+	}
+	task->tk_calldata = dp;
+	task->tk_action = returnd;
+	if (rpc_execute(task)) {
+		nfsv4_printk(level1, "drop_delegation: couldn't execute RPC task!\n");
+		goto out_panic;
+	}
+
+	status = NFS4_OK;
+
+out:
+	unlock_kernel();
+	nfsv4_printk(open, "nfs4fs_drop_delegation: done\n");
+	return;
+out_panic:
+	delegreturn_panic(dp);
+out_put:
+	nfs4fs_put_delegation(dp);
+	goto out;
+}
+
+/*
+ * This is the last-ditch error handler, if we encounter an error trying to
+ * undelegate some OPEN's.  It just stamps out all record of the delegation's
+ * existence.
+ */
+static void
+delegreturn_panic(struct nfs4fs_delegation *dp)
+{
+	struct nfs4fs_file_data *fp;
+	
+	NFS4_ASSERT(kernel_locked());
+	NFS4_ASSERT(dp->dl_inode == NULL);
+
+	printk(KERN_WARNING "nfs4: fatal error during delegation return!\n");
+	printk(KERN_WARNING "nfs4: some state will be lost\n");
+	
+	while (!list_empty(&dp->dl_read_opens)) {
+		fp = list_entry(dp->dl_read_opens.next, struct nfs4fs_file_data, fi_read);
+		__delegreturn_panic(dp, fp);
+	}
+	while (!list_empty(&dp->dl_write_opens)) {
+		fp = list_entry(dp->dl_write_opens.next, struct nfs4fs_file_data, fi_write);
+		__delegreturn_panic(dp, fp);
+	}
+}
+
+static void
+__delegreturn_panic(struct nfs4fs_delegation *dp, struct nfs4fs_file_data *fp)
+{
+	NFS4_ASSERT(kernel_locked());
+	NFS4_ASSERT(fp->fi_delegation == dp);
+
+	list_del_init(&fp->fi_read);
+	list_del_init(&fp->fi_write);
+	fp->fi_delegation = NULL;
+	nfs4fs_put_delegation(dp);
+}
+
+/*
+ * We are passed a delegation in ->tk_calldata, and are responsible for dropping the reference.
+ */
+static void
+returnd(struct rpc_task *task)
+{
+	struct nfs4fs_delegation *dp = (struct nfs4fs_delegation *) task->tk_calldata;
+	struct nfs4fs_file_data *fp;
+	int status;
+
+	NFS4_ASSERT(kernel_locked());
+	NFS4_ASSERT(list_empty(&dp->dl_hash));
+	NFS4_ASSERT(dp->dl_inode == NULL);
+
+	nfsv4_printk(callback, "returnd: woke up\n");
+
+	if ((status = task->tk_status))
+		goto out_panic;
+	
+	if (!list_empty(&dp->dl_read_opens)) {
+		fp = list_entry(dp->dl_read_opens.next, struct nfs4fs_file_data, fi_read);
+		goto undelegate;
+	}
+	if (!list_empty(&dp->dl_write_opens)) {
+		fp = list_entry(dp->dl_write_opens.next, struct nfs4fs_file_data, fi_write);
+		goto undelegate;
+	}
+	nfsv4_printk(callback, "returnd: all done\n");
+	task->tk_action = NULL;
+
+out:
+	return;
+out_panic:
+	rpc_wake_up_task(task);      /* will no-op if task already running */
+	delegreturn_panic(dp);
+	task->tk_action = NULL;
+	goto out;
+	
+undelegate:
+	/*
+	 * TODO: Check that nothing here can sleep (this is necessary since we
+	 * wait until do_undelegate_async() before claiming a reference to fp).
+	 */
+	nfsv4_printk(callback, "returnd: calling do_undelegate, back to sleep...\n");
+	task->tk_timeout = 60 * HZ;    /* XXX */
+	task->tk_action = returnd;
+	rpc_sleep_on(&returnd_queue, task, NULL, NULL);
+	if (do_undelegate_async(task, fp))
+		goto out_panic;
+	return;   /* we will wake up after the OPEN has completed. */
+}	
+
+/*
+ * TODO: Confirm that ->tk_exit() is always called.  Should this be ->tk_release() instead?
+ */
+static void
+returnd_exit(struct rpc_task *task)
+{
+	struct nfs4fs_delegation *dp = (struct nfs4fs_delegation *) task->tk_calldata;
+	nfsv4_printk(callback, "returnd_exit: dropping reference to delegation\n");
+	nfs4fs_put_delegation(dp);
+}
+
+/*
+ * XXX: This routine avoids declaring a 'struct nfs4fs_compound' on its own
+ * stack; instead the caller passes in a pointer to an nfs4fs_compound further
+ * down in the stack.  This ugliness is necessary because of the "huge problem"
+ * listed in nfs4fs.h as a TODO.
+ */
+static int
+do_undelegate_sync(struct nfs4fs_file_data *fp, struct nfs4fs_compound *cp)
+{
+	struct nfs4fs_delegation *dp = fp->fi_delegation;
+	int status;
+	
+	NFS4_ASSERT(kernel_locked());
+	NFS4_ASSERT(IS_OPEN_STATEID(fp));
+	NFS4_ASSERT(fp->fi_flags & FI_FAKE);
+	NFS4_ASSERT(dp != NULL);
+	NFS4_ASSERT(list_empty(&fp->fi_linkage));
+
+	nfs4fs_setup_compound(cp, fp->fi_sb, "undelegate_sync");
+	nfs4fs_setup_putfh_simple(cp, dp->dl_fhval, dp->dl_fhlen);
+	nfs4fs_setup_undelegating_open(cp, fp);
+	
+	if ((status = nfs4fs_call_compound(cp)))
+		goto out_release;
+	if ((status = nfs4fs_handle_putfh(cp)))
+		goto out_release;
+	if ((status = nfs4fs_handle_undelegating_open(cp, fp)))
+		goto out_release;
+
+out_release:
+	nfs4fs_release_compound(cp);
+	return status;
+}
+
+static void
+undelegate_handler(struct nfs4fs_compound *cp)
+{
+	struct nfs4fs_file_data *fp = cp->u.undelegate.fp;
+	int status;
+	
+	if ((status = nfs4fs_handle_putfh(cp)))
+		goto out;
+	if ((status = nfs4fs_handle_undelegating_open(cp, fp)))
+		goto out;
+
+out:
+	cp->task.tk_status = status;
+}
+
+static void
+undelegate_release(struct nfs4fs_compound *cp)
+{
+	struct rpc_task *returnd = cp->u.undelegate.returnd;
+	struct nfs4fs_file_data *fp = cp->u.undelegate.fp;
+
+	if (returnd) {
+		cp->u.undelegate.returnd = NULL;
+		returnd->tk_status = cp->task.tk_status;
+		rpc_wake_up_task(returnd);
+	}
+	if (fp) {
+		cp->u.undelegate.fp = NULL;
+		nfs4fs_put_file_data(fp);
+	}
+}
+
+static int
+do_undelegate_async(struct rpc_task *returnd, struct nfs4fs_file_data *fp)
+{
+	struct nfs4fs_delegation *dp = fp->fi_delegation;
+	struct nfs4fs_compound *cp;
+	int status;
+	
+	NFS4_ASSERT(kernel_locked());
+	NFS4_ASSERT(IS_OPEN_STATEID(fp));
+	NFS4_ASSERT(fp->fi_flags & FI_FAKE);
+	NFS4_ASSERT(dp != NULL);
+	NFS4_ASSERT(list_empty(&fp->fi_linkage));
+
+	/*
+	 * Claim a reference for the duration of the "undelegation" operation..
+	 * This will be released in undelegate_release().
+	 */
+	atomic_inc(&fp->fi_count);
+	
+	if ((status = nfs4fs_setup_async(&cp, fp->fi_sb, "undelegate_async", NULL,
+					 undelegate_handler, undelegate_release)))
+		goto out_err;
+	cp->u.undelegate.returnd = returnd;
+	cp->u.undelegate.fp = fp;
+
+	nfs4fs_setup_putfh_simple(cp, dp->dl_fhval, dp->dl_fhlen);
+	nfs4fs_setup_undelegating_open(cp, fp);
+	if ((status = nfs4fs_call_async(cp)))
+		goto out_err;
+
+out:
+	return status;
+out_err:
+	nfs4fs_put_file_data(fp);
+	goto out;
+}
--- clean/fs/nfs4fs/callback.c	Thu Feb  7 18:04:30 2002
+++ dirty/fs/nfs4fs/callback.c	Mon Feb  4 15:16:19 2002
@@ -0,0 +1,772 @@
+/*
+ *  fs/nfs4fs/callback.c
+ *
+ *  Copyright (c) 2002 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  Kendrick Smith <kmsmith@umich.edu>
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the University nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This file contains an implementation of the NFSv4 callback service,
+ * which allows the server to issue "delegations" to the client.  An
+ * NFSv4 delegation is a piece of state issued by the server, which
+ * provides a guarantee of exclusive access to the client, so that the
+ * client can optimize certain operations such as revalidating the file,
+ * opening, locking, etc.  Delegations on directories or other non-regular
+ * file types are not supported in NFSv4.  Delegation is strictly a
+ * performance-enhancing feature, and is not necessary for functional
+ * completeness of the protocol.
+ *
+ * Big TODO: The CB_GETATTR operation, which allows the server to request attributes
+ * on a file delegated to the client, has yet to be implemented.
+ *
+ * Small TODO: How should we handle the 'truncate' flag in CB_RECALL?
+ *
+ * TODO: Set up a replay cache, a la nfsv3, for CB_RECALL requests.
+ * This is very important since CB_RECALL is non-idempotent, and the protocol
+ * defines no serialization mechanisms.  Implementing it should just be a matter
+ * of passing the right switches down into the RPC layer...
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/fs.h>
+#include <linux/sunrpc/sched.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/svcsock.h>
+#include <linux/nfsd/cache.h>	/* need RC_NOCACHE */
+
+#include <linux/nfs4/nfs4.h>
+#include <linux/nfs4/nfs4_debug.h>
+#include <linux/nfs4/nfs4fs_xdr.h>
+#include <linux/nfs4/nfs4fs.h>
+
+#define CB_BASE_PROGRAM		0x0400beef
+#define CB_VERSION		1
+#define CB_BASE_PORT		31337
+#define CB_BUFSIZE		1024  /* XXX: total overkill for now */
+#define CB_XDRSIZE		1024  /* XXX: total overkill for now */
+#define CB_MAX			16
+
+#define ALLOWED_SIGS    (sigmask(SIGKILL))
+#define SHUTDOWN_SIGS   (sigmask(SIGKILL) | sigmask(SIGINT) | sigmask(SIGQUIT))
+
+#define CB_ISIZE	1
+#define CB_MAXTAGLEN	64
+#define CB_MAXOP	256
+
+/*
+ * These are the definitions for the callback XDR.
+ */
+struct cb_arg {
+	u32		opnum;
+	union {
+		struct {
+			
+		} getattr;
+		struct {
+			stateid4	stateid;
+			int		truncate;
+		} recall;
+	} u;
+};
+#define recall_stateid		u.recall.stateid
+#define recall_truncate		u.recall.truncate
+
+struct cb_args {
+#if NFS4_DEBUG
+	u32		taglen;
+	char		tag[CB_MAXTAGLEN];
+#endif
+	u32		minorversion;
+	u32		nops;
+	struct cb_arg	*args;
+	struct cb_arg	iargs[CB_ISIZE];
+};
+
+struct cb_rep {
+	u32		opnum;
+	u32		status;
+};
+
+struct cb_reps {
+#if NFS4_DEBUG
+	u32		taglen;
+	char		tag[CB_MAXTAGLEN + 1];
+#endif
+	u32		status;
+	u32		nops;
+	struct cb_rep	*reps;
+	struct cb_rep	ireps[CB_ISIZE];
+};
+
+/*
+ * TODO: This silly table reserves 16 (CB_MAX) ports for callbacks, starting
+ * at port 31337 (CB_BASE_PORT).  When we need to create a callback service,
+ * a slot in this table is reserved.  (We use a seperate port for each NFSv4
+ * mountpoint.)  This is a stupid way of doing things!  We should just bind
+ * an unused port whenever we need one, and dispense with this table.  Fix it...
+ *
+ * XXX: Will the RPC layer let us use the same port for all of the callback
+ * services, and differentiate them by program number?  This might actually
+ * be the way to go...
+ */
+static spinlock_t 		callback_lock = SPIN_LOCK_UNLOCKED;
+static u32			current_program = CB_BASE_PROGRAM;
+
+static struct {
+	struct super_block	*sb;
+	struct svc_serv 	*serv;
+	struct svc_program	program;
+	struct svc_stat		stat;
+	struct semaphore	start;
+	wait_queue_head_t	wait;
+	pid_t			pid;
+	unsigned int		reserved : 1;
+} callback_vector[CB_MAX];
+
+static void	cb_main(struct svc_rqst *);
+static int	cb_decode_null(struct svc_rqst *, u32 *, void *);
+static int	cb_proc_null(struct svc_rqst *, void *, void *);
+static int	cb_encode_null(struct svc_rqst *, u32 *, void *);
+static int	cb_decode_compound(struct svc_rqst *, u32 *, struct cb_args *);
+static int	cb_proc_compound(struct svc_rqst *, struct cb_args *, struct cb_reps *);
+static int	cb_encode_compound(struct svc_rqst *, u32 *, struct cb_reps *);
+
+/*
+ * RPC state tables etc., for the callback service.
+ *
+ * XXX: The 1024's here are total overkill; fix later...
+ */
+#define PROC(proc)			\
+{	(svc_procfunc) cb_proc_##proc,	\
+	(kxdrproc_t) cb_decode_##proc,	\
+	(kxdrproc_t) cb_encode_##proc,	\
+	NULL,				\
+	1024,			       	\
+	1024,				\
+	0,				\
+	RC_NOCACHE			\
+}
+
+static struct svc_procedure cb_v1_procedures[] = {
+	PROC(null),
+	PROC(compound)
+};
+
+static struct svc_version nfs4_cb_version1 = {
+	1,							/* version number */
+	sizeof(cb_v1_procedures) / sizeof(cb_v1_procedures[0]),	/* no. of procedures */
+	cb_v1_procedures,					/* procedure table */
+	NULL							/* dispatcher */
+};
+
+static struct svc_version *cb_version[] = {
+	NULL,
+	&nfs4_cb_version1,
+};
+
+/*
+ * Perform all needed initializations in this file...
+ */
+void
+nfs4fs_init_callbacks(void)
+{
+	int i;
+	
+	memset(callback_vector, 0, sizeof(callback_vector));
+	for (i = 0; i < CB_MAX; i++) {
+		init_MUTEX_LOCKED(&callback_vector[i].start);
+		init_waitqueue_head(&callback_vector[i].wait);
+	}
+}
+
+/*
+ * This is called during the mount process to create a callback service for
+ * the mountpoint.
+ * It sets NFS4_CALLBACK_PROG(sb) and NFS4_CALLBACK_PORT(sb) accordingly.
+ */
+int
+nfs4_create_callback(struct super_block *sb)
+{
+	struct svc_program *program;
+	struct svc_stat *stat;
+	struct svc_serv *serv;
+	unsigned short port;
+	int i;
+	int status;
+
+	NFS4_CALLBACK_PROG(sb) = 0;
+	NFS4_CALLBACK_PORT(sb) = 0;
+
+	/*
+	 * Try to reserve a callback slot for the callback on this superblock.
+	 */
+	spin_lock(&callback_lock);
+	for (i = 0; i < CB_MAX; i++)
+		if (!callback_vector[i].reserved) {
+			if (callback_vector[i].sb || callback_vector[i].serv)
+				BUG();
+			callback_vector[i].reserved = 1;
+			break;
+		}
+	spin_unlock(&callback_lock);
+	
+	status = -EBUSY;
+	if (i == CB_MAX) {
+		printk(KERN_WARNING "nfs4: couldn't reserve a callback slot\n");
+		goto out;
+	}
+	port = CB_BASE_PORT + i;
+
+	/*
+	 * Try to create an RPC service for the callback.
+	 */
+	program = &callback_vector[i].program;
+	stat = &callback_vector[i].stat;
+	program->pg_prog = current_program++;
+	program->pg_lovers = CB_VERSION;
+	program->pg_hivers = CB_VERSION;
+	program->pg_nvers = sizeof(cb_version) / sizeof(cb_version[0]);
+	program->pg_vers = cb_version;
+	program->pg_name = "nfs4_cb";
+	program->pg_stats = stat;
+	memset(stat, 0, sizeof(*stat));
+	stat->program = program;
+
+	status = -ENOMEM;
+	if (!(serv = svc_create(program, CB_BUFSIZE, CB_XDRSIZE))) {
+		printk(KERN_WARNING "nfs4: couldn't create callback service\n");
+		goto out_slot;
+	}
+	if ((status = svc_makesock(serv, IPPROTO_UDP, port))) {
+		printk(KERN_WARNING "nfs4: couldn't create callback socket\n");
+		goto out_serv;
+	}
+
+	/*
+	 * Fork the callback thread, and wait for it to start.
+	 * TODO: At present we allow only one callback thread.  Add a mechanism
+	 * for specifying more?
+	 */
+	if ((status = svc_create_thread(cb_main, serv))) {
+		printk(KERN_WARNING "nfs4: couldn't create callback thread\n");
+		goto out_serv;
+	}
+	down(&callback_vector[i].start);
+
+	if (!callback_vector[i].pid)
+		BUG();
+	
+	/*
+	 * Success!  Update callback table and exit..
+	 */
+	spin_lock(&callback_lock);
+	callback_vector[i].sb = sb;
+	callback_vector[i].serv = serv;
+	spin_unlock(&callback_lock);
+
+	NFS4_CALLBACK_PROG(sb) = program->pg_prog;
+	NFS4_CALLBACK_PORT(sb) = CB_BASE_PORT + i;
+	status = 0;
+
+out:
+	return status;
+out_serv:
+	svc_destroy(serv);
+out_slot:
+	spin_lock(&callback_lock);
+	callback_vector[i].reserved = 0;
+	spin_unlock(&callback_lock);
+	goto out;
+}
+
+/*
+ * This is called during the umount process to destroy the callback service.
+ */
+void
+nfs4_destroy_callback(struct super_block *sb)
+{
+	struct svc_serv *serv;
+	unsigned short port;
+	int i;
+
+	port = NFS4_CALLBACK_PORT(sb);
+	if (!port)
+		return;
+	if (port < CB_BASE_PORT || port >= CB_BASE_PORT + CB_MAX)
+		BUG();
+	i = port - CB_BASE_PORT;
+
+	spin_lock(&callback_lock);
+	if ((!callback_vector[i].reserved) ||
+	    (!callback_vector[i].serv) ||
+	    (callback_vector[i].sb != sb))
+		BUG();
+	serv = callback_vector[i].serv;
+	callback_vector[i].serv = NULL;
+	callback_vector[i].sb = NULL;
+	spin_unlock(&callback_lock);
+
+	kill_proc(callback_vector[i].pid, SIGKILL, 1);
+	current->sigpending = 0;
+	interruptible_sleep_on_timeout(&callback_vector[i].wait, 5 * HZ);
+	
+	spin_lock(&callback_lock);
+	if (!callback_vector[i].pid)
+		callback_vector[i].reserved = 0;
+	else
+		printk(KERN_WARNING "nfs4: warning: callback thread failed to exit!\n");
+	spin_unlock(&callback_lock);
+	
+	svc_destroy(serv);
+}
+
+/*
+ * This is the callback kernel thread.
+ */
+static void
+cb_main(struct svc_rqst *rqstp)
+{
+	struct svc_serv *serv = rqstp->rq_server;
+	u32 prog = serv->sv_program->pg_prog;
+	unsigned int signo;
+	int status;
+	int i;
+	
+	MOD_INC_USE_COUNT;
+	lock_kernel();
+	
+	spin_lock(&callback_lock);
+	for (i = 0; i < CB_MAX; i++) {
+		if (callback_vector[i].program.pg_prog == prog)
+			break;
+	}
+	spin_unlock(&callback_lock);
+
+	if (i == CB_MAX)
+		BUG();
+	callback_vector[i].pid = current->pid;
+	up(&callback_vector[i].start);
+
+	daemonize();
+	sprintf(current->comm, "nfs4_cb");
+
+	for (;;) {
+		spin_lock_irq(&current->sigmask_lock);
+		siginitsetinv(&current->blocked, SHUTDOWN_SIGS);
+		recalc_sigpending(current);
+		spin_unlock_irq(&current->sigmask_lock);
+
+		do {
+			status = svc_recv(serv, rqstp, MAX_SCHEDULE_TIMEOUT);
+		} while (status == -EAGAIN);
+		
+		if (status < 0)
+			break;
+
+		/*
+		 * Process requests with signals blocked.
+		 */
+		spin_lock_irq(&current->sigmask_lock);
+		siginitsetinv(&current->blocked, ALLOWED_SIGS);
+		recalc_sigpending(current);
+		spin_unlock_irq(&current->sigmask_lock);
+		svc_process(serv, rqstp);
+	}
+
+	if (status != -EINTR)
+		printk(KERN_WARNING "nfs4: callback thread terminating on error %d\n", status);
+	else {
+		for (signo = 1; signo <= _NSIG; signo++)
+			if (sigismember(&current->pending.signal, signo) &&
+			    !sigismember(&current->blocked, signo))
+				break;
+		printk(KERN_WARNING "nfs4: callback thread terminating on signal %d\n", signo);
+	}
+
+	callback_vector[i].pid = 0;
+	wake_up(&callback_vector[i].wait);
+	
+	svc_exit_thread(rqstp);
+	MOD_DEC_USE_COUNT;
+}
+
+/* finds the superblock matching a given callback request. */
+static int
+__find_callback_sb(struct svc_rqst *rqstp, struct super_block **sbp)
+{
+	struct svc_serv *serv = rqstp->rq_server;
+	u32 prog = rqstp->rq_prog;
+	int i;
+	int status;
+
+	/*
+	 * XXX: For now, we just do a linear search through the
+	 * table of callbacks.  This is not a problem as long as
+	 * the number of callbacks is not too large.  Later, we
+	 * might consider adding a hash lookup here...
+	 */
+
+	status = NFS4ERR_INVAL;
+	spin_lock(&callback_lock);
+	for (i = 0; i < CB_MAX; i++)
+		if ((callback_vector[i].reserved) &&
+		    (callback_vector[i].program.pg_prog == prog) &&
+		    (callback_vector[i].serv == serv)) {
+			*sbp = callback_vector[i].sb;
+			status = NFS4_OK;
+			break;
+		}
+	spin_unlock(&callback_lock);
+
+	return status;
+}
+
+/*
+ * This does complete processing of a CB_RECALL request.
+ *
+ * Note: The return value is an NFS status (e.g. NFS4ERR_PERM), not an
+ * "internal" status (e.g. -EPERM)
+ */
+static int
+proc_cb_recall(struct super_block *sb, stateid4 stateid)
+{
+	struct nfs4fs_delegation *dp;
+	struct inode *inode;
+	int status;
+	
+	nfsv4_printk(callback, "nfs4: processing CB_RECALL...\n");
+	lock_kernel();
+	
+	status = NFS4ERR_BAD_STATEID;
+	if (!(dp = nfs4fs_find_delegation_by_stateid(sb, stateid))) {
+		nfsv4_printk(callback2, "cb_recall: couldn't find delegation!\n");
+		goto out;
+	}
+
+	/*
+	 * If we cannot get a reference to the inode, it is because the inode is
+	 * about to be cleared, which will return the delegation, so just return
+	 * NFS4_OK.
+	 */
+	status = NFS4_OK;
+	if (!(inode = igrab(dp->dl_inode))) {
+		nfsv4_printk(callback, "cb_recall: couldn't get reference to inode...\n");
+		goto out_put;
+	}
+
+	/*
+	 * Drop the delegation from the inode.  After this step,
+	 * the delegation return has been initiated...
+	 */
+	nfs4fs_drop_delegation_from_inode(inode);
+	iput(inode);
+
+out_put:
+	nfs4fs_put_delegation(dp);
+out:
+	unlock_kernel();
+	return status;
+}
+
+/*
+ * Finally, the routines to do XDR encode/decode, and top-level
+ * processing, for CB_NULL and CB_COMPOUND.
+ */
+static int
+cb_decode_null(struct svc_rqst *rqstp, u32 *p, void *dummy)
+{
+	return 1;
+}
+
+static int
+cb_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
+{
+	struct super_block *sb;
+	return __find_callback_sb(rqstp, &sb);
+}
+
+static int
+cb_encode_null(struct svc_rqst *rqstp, u32 *p, void *dummy)
+{
+	struct svc_buf *buf = &rqstp->rq_resbuf;
+
+	buf->len = p - buf->base;
+	return 1;
+}
+
+/* convenient shorthand for the XDR routines... */
+#define CHECK(condition)			\
+	do {					\
+		if (!(condition)) {		\
+			nfsv4_printk(level1, "xdr error in callback: line %d\n", __LINE__); \
+			goto xdr_error;		\
+		}				\
+	} while (0)
+
+static int
+cb_decode_compound(struct svc_rqst *rqstp, u32 *p, struct cb_args *argp)
+{
+	struct svc_buf *buf = &rqstp->rq_argbuf;
+	u32 *end = buf->base + buf->buflen;
+	u32 len, xdrlen;
+	struct cb_arg *arg;
+	int i;
+
+	nfsv4_printk(callback, "cb_decode_compound: starting\n");
+	argp->args = argp->iargs;
+	
+	/*
+	 * Get tag length.
+	 */
+	CHECK(p + 1 <= end);
+	len = ntohl(*p++);
+	xdrlen = XDR_QUADLEN(len);
+	CHECK(len <= CB_MAXTAGLEN);
+
+	/*
+	 * Read tag, minor version, array length
+	 */
+	CHECK(p + xdrlen + 2 <= end);
+#if NFS4_DEBUG
+	argp->taglen = len;
+	memcpy(argp->tag, p, len);
+#endif
+	p += xdrlen;
+	argp->minorversion = ntohl(*p++);
+	argp->nops = ntohl(*p++);
+
+	if (argp->nops > CB_ISIZE) {
+		argp->args = NFS4_ALLOC(argp->nops * sizeof(struct cb_arg));
+		CHECK(argp->args);
+	}
+
+	/*
+	 * Read the individual ops.
+	 */
+	for (i = 0; i < argp->nops; i++) {
+		arg = &argp->args[i];
+		CHECK(p + 1 <= end);
+		arg->opnum = ntohl(*p++);
+		
+		switch (arg->opnum) {
+		case OP_CB_GETATTR:
+			/*
+			 * XXX: not yet!
+			 */
+			CHECK(0);
+			
+		case OP_CB_RECALL:
+			/*
+			 * Get stateid, truncate flag, fhlen.
+			 */
+			CHECK(p + XDR_QUADLEN(sizeof(stateid4)) + 2 <= end);
+			memcpy(arg->recall_stateid, p, sizeof(stateid4));
+			p += XDR_QUADLEN(sizeof(stateid4));
+			arg->recall_truncate = ntohl(*p++);
+			len = ntohl(*p++);
+			xdrlen = XDR_QUADLEN(len);
+			CHECK(len <= NFS4_FHSIZE);
+
+			/*
+			 * Just ignore the filehandle for now.
+			 */
+			CHECK(p + xdrlen <= end);
+			p += xdrlen;
+			
+			break;
+			
+		default:
+			CHECK(0);
+		}
+	}
+
+	nfsv4_printk(callback, "cb_decode_compound: done\n");
+	return 1;
+
+xdr_error:
+	if (argp->args != argp->iargs && argp->args) {
+		NFS4_FREE(argp->args);
+		argp->args = NULL;
+	}
+	return 0;
+}
+
+static int
+cb_proc_compound(struct svc_rqst *rqstp, struct cb_args *argp, struct cb_reps *repp)
+{
+	struct super_block *sb;
+	struct cb_arg *arg;
+	struct cb_rep *rep;
+	int i;
+	int rpc_status, nfs_status;
+
+	nfsv4_printk(callback, "nfs4: cb_proc_compound starting\n");
+	repp->reps = repp->ireps;
+	
+	rpc_status = htonl(RPC_PROG_UNAVAIL);
+	if (__find_callback_sb(rqstp, &sb))
+		goto out;
+
+#if NFS4_DEBUG
+	repp->taglen = argp->taglen;
+	memcpy(repp->tag, argp->tag, argp->taglen);
+#endif
+	
+	rpc_status = rpc_success;
+	nfs_status = NFS4_OK;
+	
+	if (argp->nops > CB_ISIZE) {
+		repp->reps = NFS4_ALLOC(argp->nops * sizeof(struct cb_rep));
+		if (!repp->reps) {
+			/*
+			 * Failing this allocation is kind of awkward...
+			 * We have to fake the entire response.
+			 */
+			repp->reps = repp->ireps;
+			rep = &repp->ireps[0];
+			rep->opnum = argp->args[0].opnum;
+			rep->status = NFS4ERR_RESOURCE;
+			repp->nops = 1;
+			repp->status = NFS4ERR_RESOURCE;
+			goto out;
+		}
+	}
+	
+	for (i = 0; i < argp->nops && !nfs_status; i++) {
+		arg = &argp->args[i];
+		rep = &repp->reps[i];
+		
+		rep->opnum = arg->opnum;
+		switch (arg->opnum) {
+			
+		case OP_CB_GETATTR:
+			/*
+			 * XXX: not implemented yet...
+			 */
+			BUG();
+			
+		case OP_CB_RECALL:
+			/*
+			 * XXX: 'truncate' arg currently ignored...
+			 */
+			nfs_status = proc_cb_recall(sb, arg->recall_stateid);
+			break;
+			
+		default:
+			BUG();
+		}
+
+		nfsv4_printk(callback, "nfs4: cb_proc_compound done; status %d\n", nfs_status);
+		rep->status = nfs_status;
+	}
+
+	repp->status = nfs_status;
+	repp->nops = i;
+	
+out:
+	if (argp->args != argp->iargs && argp->args) {
+		NFS4_FREE(argp->args);
+		argp->args = NULL;
+	}
+	return rpc_status;
+}
+
+static int
+cb_encode_compound(struct svc_rqst *rqstp, u32 *p, struct cb_reps *repp)
+{
+	struct svc_buf *buf = &rqstp->rq_resbuf;
+	u32 *end = buf->base + buf->buflen;
+	u32 xdrlen;
+	struct cb_rep *rep;
+	int i;
+	int status;
+		
+	nfsv4_printk(callback, "cb_encode_compound: starting\n");
+	buf->len = p - buf->base;   /* paranoid */
+
+	/*
+	 * Encode status, tag, resarray length.
+	 */
+#if NFS4_DEBUG
+	xdrlen = XDR_QUADLEN(repp->taglen);
+	CHECK(p + xdrlen + 3 <= end);
+	*p++ = htonl(repp->status);
+	*p++ = htonl(repp->taglen);
+	memcpy(p, repp->tag, repp->taglen);
+	p += xdrlen;
+	*p++ = htonl(repp->nops);
+#else
+	CHECK(p + 3 <= end);
+	*p++ = htonl(repp->status);
+	*p++ = htonl(repp->taglen);
+	*p++ = htonl(repp->nops);
+#endif
+
+	/*
+	 * Encode individual ops.
+	 */
+	for (i = 0; i < repp->nops; i++) {
+		rep = &repp->reps[i];
+		/*
+		 * Encode opnum, status.
+		 */
+		NFS4_ASSERT(rep->opnum == OP_CB_RECALL);
+		CHECK(p + 2 <= end);
+		*p++ = htonl(rep->opnum);
+		*p++ = htonl(rep->status);
+
+		/*
+		 * When we implement CB_GETATTR, there will
+		 * be more stuff here...
+		 */
+	}
+	
+	buf->len = p - buf->base;   /* paranoid */
+	status = 1;
+	
+out:
+	if (repp->reps != repp->ireps && repp->reps) {
+		NFS4_FREE(repp->reps);
+		repp->reps = NULL;
+	}
+	nfsv4_printk(callback, "cb_encode_compound: done\n");
+	return status;
+
+xdr_error:
+	nfsv4_printk(level1, "cb_encode_compound: failed to XDR response!\n");
+	status = 0;
+	goto out;
+}
--- clean/fs/nfs4fs/encode.c	Thu Feb  7 18:04:30 2002
+++ dirty/fs/nfs4fs/encode.c	Mon Feb  4 15:16:20 2002
@@ -0,0 +1,1004 @@
+/*
+ *  fs/nfs4fs/encode.c
+ *
+ *  Copyright (c) 2002 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  Kendrick Smith <kmsmith@umich.edu>
+ *  Andy Adamson   <andros@umich.edu>
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the University nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This implements all of the client-side XDR encoding for NFSv4.
+ */
+
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/fs.h>
+#include <linux/sunrpc/sched.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+
+#include <linux/nfs4/nfs4.h>
+#include <linux/nfs4/nfs4_debug.h>
+#include <linux/nfs4/nfs4fs_xdr.h>
+#include <linux/nfs4/nfs4fs.h>
+
+/* XXX: move this into fs/nfs4/util.c */
+#if NFS4_DEBUG
+static char *
+opcode_name(int op)
+{
+	static char *name_map[] = {
+		"BAD OP",
+		"BAD OP",
+		"BAD OP",
+		"ACCESS",
+		"CLOSE",
+		"COMMIT",
+		"CREATE",
+		"DELEGPURGE",
+		"DELEGRETURN",
+		"GETATTR",
+		"GETFH",
+		"LINK",
+		"LOCK",
+		"LOCKT",
+		"LOCKU",
+		"LOOKUP",
+		"LOOKUPP",
+		"NVERIFY",
+		"OPEN",
+		"OPENATTR",
+		"OPEN_CONFIRM",
+		"OPEN_DOWNGRADE",
+		"PUTFH",
+		"PUTPUBFH",
+		"PUTROOTFH",
+		"READ",
+		"READDIR",
+		"READLINK",
+		"REMOVE",
+		"RENAME",
+		"RENEW",
+		"RESTOREFH",
+		"SAVEFH",
+		"SECINFO",
+		"SETATTR",
+		"SETCLIENTID",
+		"SETCLIENTID_CONFIRM",
+		"VERIFY",
+		"WRITE",
+		"BAD OP"
+	};
+	static int lastentry = sizeof(name_map) / sizeof(name_map[0]) - 1;
+
+	if (op < 0)
+		op = 0;
+	if (op > lastentry)
+		op = lastentry;
+	return name_map[op];
+}
+#endif
+
+/*
+ * Note! This file is shared between Linux and OpenBSD implementations.
+ * It is designed to be portable to any platform, as long as the following
+ * primitives are defined:
+ *    reserve_space() - returns a pointer to 'nbytes' worth of space in the
+ *                      request buffer, or NULL if this would overflow the buffer.
+ *    adjust_args() - this sets the current position in the request buffer to
+ *                    a specified value.
+ */
+#ifdef __linux__
+#define reserve_space(cp,nbytes)  ((cp->p + XDR_QUADLEN(nbytes) <= cp->end) ? cp->p : NULL)
+#define adjust_args(cp,p)         cp->p = p
+#else
+#error "sorry, you will need to define reserve_space() and adjust_args() for your platform."
+#endif
+
+#ifndef NFS4_CLIENT
+#error "sorry, you will need to define NFS4_CLIENT() for your platform."
+#endif
+
+#ifndef NFS4_CLIENTID
+#error "sorry, you will need to define NFS4_CLIENTID() for your platform."
+#endif
+
+/*
+ * Convenient shorthand for XDR decode routines...
+ */
+#define ENCODE_HEAD						\
+	u32 *p;							\
+	int status
+
+#define ENCODE_TAIL						\
+	status = 0;						\
+out:								\
+	return status;						\
+nomem:								\
+	nfsv4_printk(level1, "xdr: out of memory! (%s:%d)\n", __FILE__, __LINE__); \
+	status = -ENOMEM;					\
+	goto out
+	
+#define RESERVE_SPACE(nbytes)    do {				\
+	if (!(p = reserve_space(cp, nbytes)))			\
+		goto nomem;					\
+} while (0)
+#define WRITE32(n)               *p++ = htonl(n)
+#define WRITE64(n)               do {				\
+	*p++ = htonl((u32)((n) >> 32));				\
+	*p++ = htonl((u32)(n));					\
+} while (0)
+#define WRITEMEM(ptr,nbytes)     do {				\
+	memcpy(p, ptr, nbytes);					\
+	p += XDR_QUADLEN(nbytes);				\
+} while (0)
+#define ADJUST_ARGS()            adjust_args(cp,p)
+
+/*
+ * The actual XDR encode routines start here...
+ */
+#ifdef __linux__
+static int
+encode_attrs(struct nfs4fs_compound *cp, struct iattr *iap, void *f_acl)
+{
+	u32 *p, *q;
+	int len;
+	u32 bmval0 = 0;
+	u32 bmval1 = 0;
+	struct gss_cacheent *owner = NULL;
+	struct gss_cacheent *group = NULL;
+	int status;
+	
+	NFS4_ASSERT(f_acl == NULL);    /* until I import Jake's ACL code */
+
+	/*
+	 * We reserve enough space to write the entire attribute buffer at once.
+	 * In the worst-case, this would be
+	 *   12(bitmap) + 4(attrlen) + 8(size) + 4(mode) + 4(atime) + 4(mtime)
+	 *          = 36 bytes, plus any contribution from variable-length fields
+	 *            such as owner/group/acl's.
+	 */
+	len = 36;
+	if (iap->ia_valid & ATTR_UID) {
+		if ((status = gss_get_name(GSS_OWNER, iap->ia_uid, &owner))) {
+			printk("nfs4: couldn't resolve uid %d to string\n", iap->ia_uid);
+			goto out;
+		}
+		len += (XDR_LEN(owner->name.len) + 4);
+	}
+	if (iap->ia_valid & ATTR_GID) {
+		if ((status = gss_get_name(GSS_GROUP, iap->ia_gid, &group))) {
+			printk("nfs4: couldn't resolve uid %d to string\n", iap->ia_gid);
+			goto out;
+		}
+		len += (XDR_LEN(group->name.len) + 4);
+	}
+	RESERVE_SPACE(len);
+
+	/*
+	 * We write the bitmap length now, but leave the bitmap and the attribute
+	 * buffer length to be backfilled at the end of this routine.
+	 */
+	WRITE32(2);
+	q = p;
+	p += 3;
+
+	if (iap->ia_valid & ATTR_SIZE) {
+		bmval0 |= 0x00000010;
+		WRITE64(iap->ia_size);
+	}
+	if (iap->ia_valid & ATTR_MODE) {
+		bmval1 |= 0x00000002;
+		WRITE32(iap->ia_mode);
+	}
+	if (iap->ia_valid & ATTR_UID) {
+		bmval1 |= 0x00000010;
+		WRITE32(owner->name.len);
+		WRITEMEM(owner->name.name, owner->name.len);
+	}
+	if (iap->ia_valid & ATTR_GID) {
+		bmval1 |= 0x00000020;
+		WRITE32(group->name.len);
+		WRITEMEM(group->name.name, group->name.len);
+	}
+	if (iap->ia_valid & ATTR_ATIME) {
+		/* XXX: check for correctness.  what about ATTR_*_SET? */
+		bmval1 |= 0x00010000;
+		WRITE32(SET_TO_SERVER_TIME4);
+	}
+	if (iap->ia_valid & ATTR_MTIME) {
+		bmval1 |= 0x00400000;
+		WRITE32(SET_TO_SERVER_TIME4);
+	}
+	ADJUST_ARGS();
+	
+	/*
+	 * Now we backfill the bitmap and the attribute buffer length.
+	 */
+	len = (char *)p - (char *)q - 12;
+	*q++ = htonl(bmval0);
+	*q++ = htonl(bmval1);
+	*q++ = htonl(len);
+
+	status = 0;
+out:
+	if (owner)
+		gss_put(owner);
+	if (group)
+		gss_put(group);
+	return status;
+nomem:
+	status = -ENOMEM;
+	goto out;
+}
+#else
+#error "sorry, you will need to define encode_attrs() for your platform."
+#endif
+
+static int
+encode_simple_op(struct nfs4fs_compound *cp, int opnum)
+{
+	ENCODE_HEAD;
+	
+	RESERVE_SPACE(4);
+	WRITE32(opnum);
+	ADJUST_ARGS();
+
+	ENCODE_TAIL;
+}
+
+static int
+encode_access(struct nfs4fs_compound *cp, struct nfs4fs_access *access)
+{
+	ENCODE_HEAD;
+
+	RESERVE_SPACE(8);
+	WRITE32(OP_ACCESS);
+	WRITE32(access->ac_req_access);
+	ADJUST_ARGS();
+	
+	ENCODE_TAIL;
+}
+
+static int
+encode_close(struct nfs4fs_compound *cp)
+{
+	struct nfs4fs_file_data *open_stateid = cp->stateid_holder;
+	struct nfs4fs_lockowner *open_owner = cp->seqid_holder;
+	ENCODE_HEAD;
+
+	NFS4_ASSERT(open_stateid != NULL);
+	NFS4_ASSERT(open_owner != NULL);
+	NFS4_ASSERT(cp->flags & CA_SEQID_LOCKED);
+	NFS4_ASSERT(cp->flags & CA_STATEID_LOCKED);
+	cp->client = open_stateid->fi_client;
+	
+	RESERVE_SPACE(24);
+	WRITE32(OP_CLOSE);
+	WRITE32(open_owner->lo_seqid);
+	WRITEMEM(open_stateid->fi_stateid, sizeof(stateid4));
+	ADJUST_ARGS();
+	
+	ENCODE_TAIL;
+}
+
+static int
+encode_commit(struct nfs4fs_compound *cp, struct nfs4fs_commit *commit)
+{
+	ENCODE_HEAD;
+	
+	RESERVE_SPACE(16);
+	WRITE32(OP_COMMIT);
+	WRITE64(commit->co_start);
+	WRITE32(commit->co_len);
+	ADJUST_ARGS();
+
+	ENCODE_TAIL;
+}
+
+static int
+encode_create(struct nfs4fs_compound *cp, struct nfs4fs_create *create)
+{
+	ENCODE_HEAD;
+	
+	RESERVE_SPACE(8);
+	WRITE32(OP_CREATE);
+	WRITE32(create->cr_ftype);
+	ADJUST_ARGS();
+
+	switch (create->cr_ftype) {
+	case NF4LNK:
+		RESERVE_SPACE(4 + create->cr_textlen);
+		WRITE32(create->cr_textlen);
+		WRITEMEM(create->cr_text, create->cr_textlen);
+		ADJUST_ARGS();
+		break;
+
+	case NF4BLK: case NF4CHR:
+		RESERVE_SPACE(8);
+		WRITE32(create->cr_specdata1);
+		WRITE32(create->cr_specdata2);
+		ADJUST_ARGS();
+		break;
+
+	default:
+		break;
+	}
+
+	RESERVE_SPACE(4 + create->cr_namelen);
+	WRITE32(create->cr_namelen);
+	WRITEMEM(create->cr_name, create->cr_namelen);
+	ADJUST_ARGS();
+
+	if (encode_attrs(cp, &create->cr_attrs, NULL))
+		goto nomem;
+	
+	ENCODE_TAIL;
+}
+
+static int
+encode_delegreturn(struct nfs4fs_compound *cp, struct nfs4fs_delegreturn *delegreturn)
+{
+	ENCODE_HEAD;
+
+	RESERVE_SPACE(20);
+	WRITE32(OP_DELEGRETURN);
+	WRITEMEM(&delegreturn->dr_stateid, sizeof(stateid4));
+	ADJUST_ARGS();
+
+	ENCODE_TAIL;
+}
+
+static int
+encode_getattr(struct nfs4fs_compound *cp, struct nfs4fs_getattr *getattr)
+{
+	ENCODE_HEAD;
+
+	RESERVE_SPACE(16);
+	WRITE32(OP_GETATTR);
+	WRITE32(2);
+	WRITE32(getattr->gt_bmval[0]);
+	WRITE32(getattr->gt_bmval[1]);
+	ADJUST_ARGS();
+
+	ENCODE_TAIL;
+}
+
+#define encode_getfh(cp)	encode_simple_op(cp, OP_GETFH)
+
+static int
+encode_link(struct nfs4fs_compound *cp, struct nfs4fs_link *link)
+{
+	ENCODE_HEAD;
+
+	RESERVE_SPACE(8 + link->ln_namelen);
+	WRITE32(OP_LINK);
+	WRITE32(link->ln_namelen);
+	WRITEMEM(link->ln_name, link->ln_namelen);
+	ADJUST_ARGS();
+	
+	ENCODE_TAIL;
+}
+
+static int
+encode_lock(struct nfs4fs_compound *cp, struct nfs4fs_lock *lock)
+{
+	struct nfs4fs_lockowner *lock_owner = cp->seqid_holder;
+	struct nfs4fs_file_data *lock_stateid = cp->stateid_holder;
+	struct nfs4fs_lockowner *open_owner;
+	struct nfs4fs_file_data *open_stateid;
+	int is_transition;
+	ENCODE_HEAD;
+
+	NFS4_ASSERT(lock_owner != NULL);
+	NFS4_ASSERT(lock_stateid != NULL);
+	NFS4_ASSERT(lock_stateid->fi_lockowner == lock_owner);
+	NFS4_ASSERT(lock_stateid->fi_open_stateid != NULL);
+	NFS4_ASSERT(lock_stateid->fi_open_stateid->fi_lockowner != NULL);
+	NFS4_ASSERT(cp->flags & CA_SEQID_LOCKED);
+	NFS4_ASSERT(cp->flags & CA_STATEID_LOCKED);
+	
+	cp->client = lock_stateid->fi_client;
+	is_transition = lock_stateid->fi_flags & FI_FAKE;
+
+	RESERVE_SPACE(72);
+	WRITE32(OP_LOCK);
+	WRITE32(lock->lk_type);
+	WRITE32(lock->lk_reclaim);
+	WRITE64(lock->lk_offset);
+	WRITE64(lock->lk_length);
+	WRITE32(is_transition);
+	if (is_transition) {		
+		NFS4_ASSERT(cp->flags & CA_AUX_SEQID_LOCKED);
+		NFS4_ASSERT(cp->flags & CA_AUX_STATEID_LOCKED);
+		cp->flags |= CA_AUX_SEQID_MUTATE;
+		open_stateid = lock_stateid->fi_open_stateid;
+		open_owner = open_stateid->fi_lockowner;
+		
+		WRITE32(open_owner->lo_seqid);
+		WRITEMEM(open_stateid->fi_stateid, sizeof(stateid4));
+		WRITE32(lock_owner->lo_seqid);
+		WRITE64(lock_owner->lo_client->cl_clientid);
+		WRITE32(4);
+		WRITE32(lock_owner->lo_id);
+	}
+	else {
+		WRITEMEM(lock_stateid->fi_stateid, sizeof(stateid4));
+		WRITE32(lock_owner->lo_seqid);
+	}
+	ADJUST_ARGS();
+
+	ENCODE_TAIL;
+}
+
+/* XXX: Should cp->client be set for LOCKT? */
+static int
+encode_lockt(struct nfs4fs_compound *cp, struct nfs4fs_lockt *lockt)
+{
+	struct nfs4fs_lockowner *owner = lockt->lt_owner;
+	ENCODE_HEAD;
+
+	RESERVE_SPACE(40);
+	WRITE32(OP_LOCKT);
+	WRITE32(lockt->lt_type);
+	WRITE64(lockt->lt_offset);
+	WRITE64(lockt->lt_length);
+	WRITE64(NFS4_CLIENTID(cp->sb));
+	WRITE32(4);
+	WRITE32(owner->lo_id);
+	ADJUST_ARGS();
+
+	ENCODE_TAIL;
+}
+
+static int
+encode_locku(struct nfs4fs_compound *cp, struct nfs4fs_locku *locku)
+{
+	struct nfs4fs_lockowner *lock_owner = cp->seqid_holder;
+	struct nfs4fs_file_data *lock_stateid = cp->stateid_holder;
+	ENCODE_HEAD;
+
+	NFS4_ASSERT(lock_stateid != NULL);
+	NFS4_ASSERT(lock_owner != NULL);
+	NFS4_ASSERT(IS_LOCK_STATEID(lock_stateid));
+	NFS4_ASSERT(cp->flags & CA_SEQID_LOCKED);
+	NFS4_ASSERT(cp->flags & CA_STATEID_LOCKED);
+	cp->client = lock_stateid->fi_client;
+	
+	RESERVE_SPACE(44);
+	WRITE32(OP_LOCKU);
+	WRITE32(locku->lu_type);
+	WRITE32(lock_owner->lo_seqid);
+	WRITEMEM(lock_stateid->fi_stateid, sizeof(stateid4));
+	WRITE64(locku->lu_offset);
+	WRITE64(locku->lu_length);
+	ADJUST_ARGS();
+
+	ENCODE_TAIL;
+}
+
+static int
+encode_lookup(struct nfs4fs_compound *cp, struct nfs4fs_lookup *lookup)
+{
+	ENCODE_HEAD;
+
+	RESERVE_SPACE(8 + lookup->lo_namelen);
+	WRITE32(OP_LOOKUP);
+	WRITE32(lookup->lo_namelen);
+	WRITEMEM(lookup->lo_name, lookup->lo_namelen);
+	ADJUST_ARGS();
+
+	ENCODE_TAIL;
+}
+
+static int
+encode_open(struct nfs4fs_compound *cp, struct nfs4fs_open *open)
+{
+	struct nfs4fs_lockowner *open_owner = cp->seqid_holder;
+	ENCODE_HEAD;
+
+	NFS4_ASSERT(open_owner != NULL);
+	NFS4_ASSERT(cp->flags & CA_SEQID_LOCKED);
+	NFS4_ASSERT(cp->stateid_holder != NULL);
+	/*
+	 * TODO: This next NFS4_ASSERT() is wrong.  It is unlikely, but possible,
+	 * that the stateid_holder will be non-fake when we come to this point.
+	 * (This can occur e.g. if a locking request happens at about the same
+	 * time as a delegation recall.  In general, it can happen if two threads
+	 * are in the "undelegation" path for the same file_data.  It cannot happen
+	 * except in the presence of delegations.)  What is the best way to fix this
+	 * race condition?
+	 *
+	 * One idea: Add another FI_* flag which indicates that the undelegation
+	 * process is already underway in the context of another thread.  Also a wait
+	 * queue where a thread can wait for another thread to finish the undelegation
+	 * process...
+	 */
+	NFS4_ASSERT(cp->stateid_holder->fi_flags & FI_FAKE);
+	
+	cp->client = NFS4_CLIENT(cp->sb);
+	
+	/* seqid, share_access, share_deny, clientid, ownerlen, owner, opentype */
+	RESERVE_SPACE(40);
+	WRITE32(OP_OPEN);
+	WRITE32(open_owner->lo_seqid);
+	WRITE32(open->op_share_access);
+	WRITE32(0);                       /* for us, share_deny== 0 always */
+	WRITE64(NFS4_CLIENTID(cp->sb));
+	WRITE32(4);
+	WRITE32(open_owner->lo_id);
+	WRITE32(open->op_opentype);
+	ADJUST_ARGS();
+
+	if (open->op_opentype == OPEN4_CREATE) {
+		/*
+		 * TODO: At the present time, we use GUARDED4
+		 * instead of EXCLUSIVE4...
+		 */
+		NFS4_ASSERT(open->op_createmode != EXCLUSIVE4);
+		
+		RESERVE_SPACE(4);
+		WRITE32(open->op_createmode);
+		ADJUST_ARGS();
+
+		if ((status = encode_attrs(cp, &open->op_attrs, NULL)))
+			goto out;
+	}
+
+	RESERVE_SPACE(8 + open->op_namelen);
+	WRITE32(CLAIM_NULL);
+	WRITE32(open->op_namelen);
+	WRITEMEM(open->op_name, open->op_namelen);
+	ADJUST_ARGS();
+	
+	ENCODE_TAIL;
+}
+
+/*
+ * This routine is for OPEN_CONFIRM only, now that LOCK_CONFIRM has
+ * been removed from the protocol for good.
+ */
+static int
+encode_open_confirm(struct nfs4fs_compound *cp)
+{
+	struct nfs4fs_lockowner *lp = cp->seqid_holder;
+	struct nfs4fs_file_data *fp = cp->stateid_holder;
+	ENCODE_HEAD;
+
+	NFS4_ASSERT(lp != NULL);
+	NFS4_ASSERT(cp->flags & CA_SEQID_LOCKED);
+	NFS4_ASSERT(cp->flags & CA_STATEID_LOCKED);
+	cp->client = fp->fi_client;
+
+	RESERVE_SPACE(24);
+	WRITE32(OP_OPEN_CONFIRM);
+	WRITEMEM(fp->fi_stateid, sizeof(stateid4));
+	WRITE32(lp->lo_seqid);
+	ADJUST_ARGS();
+	
+	ENCODE_TAIL;
+}
+
+static int
+encode_putfh(struct nfs4fs_compound *cp, struct nfs4fs_putfh *putfh)
+{
+	ENCODE_HEAD;
+
+	NFS4_ASSERT(putfh->pf_fhlen <= NFS4_FHSIZE);
+	NFS4_ASSERT(putfh->pf_fh != NULL);
+	
+	RESERVE_SPACE(8 + putfh->pf_fhlen);
+	WRITE32(OP_PUTFH);
+	WRITE32(putfh->pf_fhlen);
+	WRITEMEM(putfh->pf_fh, putfh->pf_fhlen);
+	ADJUST_ARGS();
+
+	ENCODE_TAIL;
+}
+
+#define encode_putrootfh(cp)	encode_simple_op(cp, OP_PUTROOTFH)
+
+/* XXX: zero-copy not implemented yet! */
+static int
+encode_read(struct nfs4fs_compound *cp, struct nfs4fs_read *read)
+{
+	struct nfs4fs_file_data *fp = cp->stateid_holder;
+	ENCODE_HEAD;
+
+	NFS4_ASSERT(fp != NULL);
+	NFS4_ASSERT(cp->flags & CA_STATEID_LOCKED);
+	cp->client = fp->fi_client;
+
+	RESERVE_SPACE(32);
+	WRITE32(OP_READ);
+	WRITEMEM(fp->fi_stateid, sizeof(stateid4));
+	WRITE64(read->rd_offset);
+	WRITE32(read->rd_length);
+	ADJUST_ARGS();
+
+	ENCODE_TAIL;
+}
+
+static int
+encode_readdir(struct nfs4fs_compound *cp, struct nfs4fs_readdir *readdir)
+{
+	ENCODE_HEAD;
+
+	RESERVE_SPACE(40);
+	WRITE32(OP_READDIR);
+	WRITE64(readdir->rd_cookie);
+	WRITEMEM(readdir->rd_req_verifier, sizeof(verifier4));
+	WRITE32(readdir->rd_count >> 4);  /* meaningless "dircount" field */
+	WRITE32(readdir->rd_count);
+	WRITE32(2);
+	WRITE32(readdir->rd_bmval[0]);
+	WRITE32(readdir->rd_bmval[1]);
+	ADJUST_ARGS();
+
+	ENCODE_TAIL;
+}
+
+#define encode_readlink(cp)	encode_simple_op(cp, OP_READLINK)
+
+static int
+encode_remove(struct nfs4fs_compound *cp, struct nfs4fs_remove *remove)
+{
+	ENCODE_HEAD;
+
+	nfsv4_printk(remove, "encode_remove: removing \"%.*s\"\n",
+		     (int)remove->rm_namelen, remove->rm_name);
+	RESERVE_SPACE(8 + remove->rm_namelen);
+	WRITE32(OP_REMOVE);
+	WRITE32(remove->rm_namelen);
+	WRITEMEM(remove->rm_name, remove->rm_namelen);
+	ADJUST_ARGS();
+
+	ENCODE_TAIL;
+}
+
+static int
+encode_rename(struct nfs4fs_compound *cp, struct nfs4fs_rename *rename)
+{
+	ENCODE_HEAD;
+
+	RESERVE_SPACE(8 + rename->rn_oldnamelen);
+	WRITE32(OP_RENAME);
+	WRITE32(rename->rn_oldnamelen);
+	WRITEMEM(rename->rn_oldname, rename->rn_oldnamelen);
+	ADJUST_ARGS();
+	
+	RESERVE_SPACE(8 + rename->rn_newnamelen);
+	WRITE32(rename->rn_newnamelen);
+	WRITEMEM(rename->rn_newname, rename->rn_newnamelen);
+	ADJUST_ARGS();
+
+	ENCODE_TAIL;
+}
+
+static int
+encode_renew(struct nfs4fs_compound *cp)
+{
+	ENCODE_HEAD;
+
+	NFS4_ASSERT(cp->flags & CA_STATEFUL);
+	cp->client = NFS4_CLIENT(cp->sb);
+	
+	RESERVE_SPACE(12);
+	WRITE32(OP_RENEW);
+	WRITE64(NFS4_CLIENTID(cp->sb));
+	ADJUST_ARGS();
+
+	ENCODE_TAIL;
+}
+
+#define encode_restorefh(cp)	encode_simple_op(cp, OP_RESTOREFH)
+#define encode_savefh(cp)	encode_simple_op(cp, OP_SAVEFH)
+
+static int
+encode_secinfo(struct nfs4fs_compound *cp, struct nfs4fs_secinfo *si)
+{
+	ENCODE_HEAD;
+
+  RESERVE_SPACE(8 + si->si_namelen);
+  WRITE32(OP_SECINFO);
+  WRITE32(si->si_namelen);
+  WRITEMEM(si->si_name, si->si_namelen);
+  ADJUST_ARGS();
+
+	ENCODE_TAIL;
+}
+
+static int
+encode_setattr(struct nfs4fs_compound *cp, struct nfs4fs_setattr *setattr)
+{
+	struct nfs4fs_file_data *fp = cp->stateid_holder;
+	ENCODE_HEAD;
+
+	if (fp) {
+		NFS4_ASSERT(cp->flags & CA_STATEID_LOCKED);
+		cp->client = fp->fi_client;
+	}
+	
+	RESERVE_SPACE(20);
+	WRITE32(OP_SETATTR);
+	if (fp)
+		WRITEMEM(fp->fi_stateid, sizeof(stateid4));
+	else {
+		WRITE32(0);
+		WRITE32(0);
+		WRITE32(0);
+		WRITE32(0);
+	}
+	ADJUST_ARGS();
+
+	if ((status = encode_attrs(cp, setattr->st_iap, NULL)))
+		goto out;
+
+	ENCODE_TAIL;
+}
+
+static int
+encode_setclientid(struct nfs4fs_compound *cp, struct nfs4fs_setclientid *setclientid)
+{
+	u32 len1, len2, len3;
+	ENCODE_HEAD;
+
+	len1 = strlen(setclientid->sc_name);
+	len2 = strlen(setclientid->sc_netid);
+	len3 = strlen(setclientid->sc_uaddr);
+
+	RESERVE_SPACE(XDR_LEN(len1) + XDR_LEN(len2) + XDR_LEN(len3) + 28);
+	WRITE32(OP_SETCLIENTID);
+	WRITEMEM(setclientid->sc_verifier, sizeof(verifier4));
+	WRITE32(len1);
+	WRITEMEM(setclientid->sc_name, len1);
+	WRITE32(setclientid->sc_prog);
+	WRITE32(len2);
+	WRITEMEM(setclientid->sc_netid, len2);
+	WRITE32(len3);
+	WRITEMEM(setclientid->sc_uaddr, len3);
+	ADJUST_ARGS();
+
+	ENCODE_TAIL;
+}
+
+static int
+encode_setclientid_confirm(struct nfs4fs_compound *cp, 
+			   struct nfs4fs_setclientid_confirm *setclientid_confirm)
+{
+	ENCODE_HEAD;
+
+	cp->client = NFS4_CLIENT(cp->sb);
+
+	RESERVE_SPACE(12);
+	WRITE32(OP_SETCLIENTID_CONFIRM);
+	WRITE64(setclientid_confirm->sc_clientid);
+	ADJUST_ARGS();
+
+	ENCODE_TAIL;
+}
+
+/*
+ * TODO: Implement zero-copy WRITE.
+ * This should be relatively easy as the hooks are already in place.
+ * Currently, 'struct nfs4fs_write' contains an iovec array pointing to the data
+ * which is going to be written.  We want to arrange things so that the RPC layer
+ * will push this data directly out onto the wire.  Right now, we fake it by having
+ * encode_write() copy it into the request buffer.
+ */
+static int
+encode_write(struct nfs4fs_compound *cp, struct nfs4fs_write *write)
+{
+	struct nfs4fs_file_data *fp = cp->stateid_holder;
+	int i, total = 0;
+	char *buf;
+	ENCODE_HEAD;
+
+	NFS4_ASSERT(fp);
+	NFS4_ASSERT(cp->flags & CA_STATEID_LOCKED);
+	for (i = 0; i < write->wr_iov_nr; i++)
+		total += write->wr_iov[i].iov_len;
+	NFS4_ASSERT(total == write->wr_len);
+	cp->client = fp->fi_client;
+	
+	RESERVE_SPACE(36 + write->wr_len);
+	WRITE32(OP_WRITE);
+	WRITEMEM(fp->fi_stateid, sizeof(stateid4));
+	WRITE64(write->wr_offset);
+	WRITE32(write->wr_stable_how);
+	WRITE32(write->wr_len);
+	buf = (char *)p;
+	for (i = 0; i < write->wr_iov_nr; i++) {
+		memcpy(buf, write->wr_iov[i].iov_base, write->wr_iov[i].iov_len);
+		buf += write->wr_iov[i].iov_len;
+	}
+	p += XDR_QUADLEN(write->wr_len);
+	ADJUST_ARGS();
+
+	ENCODE_TAIL;
+}
+
+static int
+encode_compound(struct nfs4fs_compound *cp)
+{
+	int i;
+	ENCODE_HEAD;
+
+#ifdef __linux__
+	for (i = 0; i < cp->req_nops - 1; i++) {
+		NFS4_ASSERT(cp->ops[i].opnum != OP_READ);
+		NFS4_ASSERT(cp->ops[i].opnum != OP_READDIR);
+	}
+#endif
+	
+	RESERVE_SPACE(12 + cp->taglen);
+	WRITE32(cp->taglen);
+	WRITEMEM(cp->tag, cp->taglen);
+	WRITE32(NFS4_MINOR_VERSION);
+	WRITE32(cp->req_nops);
+	ADJUST_ARGS();
+
+	status = 0;
+	for (i = 0; i < cp->req_nops; i++) {
+		nfsv4_printk(level2, "   encoding op %d [%s]\n", i,
+			     opcode_name(cp->ops[i].opnum));
+		switch (cp->ops[i].opnum) {
+		case OP_ACCESS:
+			status = encode_access(cp, &cp->ops[i].u.access);
+			break;
+		case OP_CLOSE:
+			status = encode_close(cp);
+			break;
+		case OP_CREATE:
+			status = encode_create(cp, &cp->ops[i].u.create);
+			break;
+		case OP_COMMIT:
+			status = encode_commit(cp, &cp->ops[i].u.commit);
+			break;
+		case OP_DELEGRETURN:
+			status = encode_delegreturn(cp, &cp->ops[i].u.delegreturn);
+			break;
+		case OP_GETATTR:
+			status = encode_getattr(cp, &cp->ops[i].u.getattr);
+			break;
+		case OP_GETFH:
+			status = encode_getfh(cp);
+			break;
+		case OP_LINK:
+			status = encode_link(cp, &cp->ops[i].u.link);
+			break;
+		case OP_LOCK:
+			status = encode_lock(cp, &cp->ops[i].u.lock);
+			break;
+		case OP_LOCKT:
+			status = encode_lockt(cp, &cp->ops[i].u.lockt);
+			break;
+		case OP_LOCKU:
+			status = encode_locku(cp, &cp->ops[i].u.locku);
+			break;
+		case OP_LOOKUP:
+			status = encode_lookup(cp, &cp->ops[i].u.lookup);
+			break;
+		case OP_OPEN:
+			status = encode_open(cp, &cp->ops[i].u.open);
+			break;
+		case OP_OPEN_CONFIRM:
+			status = encode_open_confirm(cp);
+			break;
+		case OP_PUTFH:
+			status = encode_putfh(cp, &cp->ops[i].u.putfh);
+			break;
+		case OP_PUTROOTFH:
+			status = encode_putrootfh(cp);
+			break;
+		case OP_READ:
+			status = encode_read(cp, &cp->ops[i].u.read);
+			break;
+		case OP_READDIR:
+			status = encode_readdir(cp, &cp->ops[i].u.readdir);
+			break;
+		case OP_READLINK:
+			status = encode_readlink(cp);
+			break;
+		case OP_REMOVE:
+			status = encode_remove(cp, &cp->ops[i].u.remove);
+			break;
+		case OP_RENAME:
+			status = encode_rename(cp, &cp->ops[i].u.rename);
+			break;
+		case OP_RENEW:
+			status = encode_renew(cp);
+			break;
+		case OP_RESTOREFH:
+			status = encode_restorefh(cp);
+			break;
+		case OP_SAVEFH:
+			status = encode_savefh(cp);
+			break;
+		case OP_SECINFO:
+			status = encode_secinfo(cp, &cp->ops[i].u.secinfo);
+			break;
+		case OP_SETATTR:
+			status = encode_setattr(cp, &cp->ops[i].u.setattr);
+			break;
+		case OP_SETCLIENTID:
+			status = encode_setclientid(cp, &cp->ops[i].u.setclientid);
+			break;
+		case OP_SETCLIENTID_CONFIRM:
+			status = encode_setclientid_confirm(cp, &cp->ops[i].u.setclientid_confirm);
+			break;
+		case OP_WRITE:
+			status = encode_write(cp, &cp->ops[i].u.write);
+			break;
+		default:
+			BUG();
+		}
+		if (status)
+			goto out;
+	}
+	
+	ENCODE_TAIL;
+}
+
+#ifdef __linux__
+/*
+ * Linux only:
+ * This is the externally-visible entry point to the decoding routines,
+ * as required by the Linux RPC layer.
+ */
+
+int
+nfs4fs_encode_compound(struct rpc_rqst *req, u32 *p, struct nfs4fs_compound *cp)
+{
+	int status;
+	
+	NFS4_ASSERT(req->rq_snr == 1);
+	NFS4_ASSERT((req->rq_svec[0].iov_len & 3) == 0);
+	NFS4_ASSERT((((char *)p - (char *)req->rq_svec[0].iov_base) & 3) == 0);
+
+	cp->p = p;
+	cp->end = (u32 *) ((char *)req->rq_svec[0].iov_base + req->rq_svec[0].iov_len);
+	
+	status = encode_compound(cp);
+	if (!status) {
+		req->rq_slen = xdr_adjust_iovec(req->rq_svec, cp->p);
+		/*
+		 * We sometimes turn this on in debugging when things go so horribly
+		 * awry that we want a raw dump of the entire response.
+		 */
+#if 0
+		printk("    ========= CLIENT REQUEST ==========\n");
+		hex_dump(p, req->rq_slen - ((char *)p - (char *)req->rq_svec[0].iov_base));
+		printk("    ===================================\n");
+#endif
+	}
+	return status;
+}
+#endif
--- clean/fs/nfs4fs/inode.c	Thu Feb  7 18:04:30 2002
+++ dirty/fs/nfs4fs/inode.c	Wed Feb  6 14:06:29 2002
@@ -0,0 +1,967 @@
+/*
+ * linux/fs/nfs4fs/inode.c
+ * from linux/fs/nfs/inode.c
+ *    Copyright (C) 1992  Rick Sladkey
+ *
+ * NFSv4 implementation
+ *    Kendrick Smith  <kmsmith@umich.edu>
+ *    Andy Adamson  <andros@umich.edu>
+ *    Copyright (C) 2001 The Regents of the University of Michigan
+ *
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/pagemap.h>
+#include <linux/net.h>
+#include <linux/inet.h>
+#include <linux/uio.h>
+#include <linux/in.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/sched.h>
+#include <linux/sunrpc/svc.h>
+
+#include <linux/nfs4/nfs4.h>
+#include <linux/nfs4/nfs4_debug.h>
+#include <linux/nfs4/nfs4fs_xdr.h>
+#include <linux/nfs4/nfs4fs.h>
+#include <linux/nfs4_mount.h>
+
+/*
+ * Superblock operations.
+ */
+static void nfs4_read_inode(struct inode *inode);
+static void nfs4_clear_inode(struct inode *inode);
+static void nfs4_put_super(struct super_block *sb);
+static void nfs4_umount_begin(struct super_block *sb);
+static int nfs4_statfs(struct super_block *sb, struct statfs *fs);
+
+static struct super_operations nfs4_sops = {
+	read_inode: nfs4_read_inode,
+	put_inode: force_delete,         /* called on every iput(), before decreasing count */
+	clear_inode: nfs4_clear_inode,   /* called when inode is freed */
+	put_super: nfs4_put_super,
+	statfs: nfs4_statfs,
+	umount_begin: nfs4_umount_begin,
+};
+
+static void
+initialize(void)
+{
+	static int initialized = 0;
+	if (!initialized) {
+		nfs4fs_init_callbacks();
+		nfs4fs_init_state();
+		nfs4fs_init_access_cache();
+		initialized = 1;
+	}
+}
+
+/* an unforunately named function, ripped from the v3 code.. */
+static inline unsigned long
+nfs_block_bits(unsigned long bsize, unsigned char *nrbitsp)
+{
+	/* make sure blocksize is a power of two */
+	if ((bsize & (bsize - 1)) || nrbitsp) {
+		unsigned char	nrbits;
+
+		for (nrbits = 31; nrbits && !(bsize & (1 << nrbits)); nrbits--)
+			;
+		bsize = 1 << nrbits;
+		if (nrbitsp)
+			*nrbitsp = nrbits;
+	}
+
+	return bsize;
+}
+
+static inline unsigned long
+nfs_block_size(unsigned long bsize, unsigned char *nrbitsp)
+{
+	if (bsize < 1024)
+		bsize = NFS4_DEF_FILE_IO_BUFFER_SIZE;
+	else if (bsize >= NFS4_MAX_FILE_IO_BUFFER_SIZE)
+		bsize = NFS4_MAX_FILE_IO_BUFFER_SIZE;
+
+	return nfs_block_bits(bsize, nrbitsp);
+}
+
+static u32 mount_bitmap[2] = { 0xc810051a, 0x20823a };
+
+static struct super_block *
+nfs4_read_super(struct super_block *sb, void *raw_data, int silent)
+{
+	struct nfs4_mount_data	*data = (struct nfs4_mount_data *) raw_data;
+	struct nfs4_sb_info	*sbinfo = &sb->u.nfs4_sb;
+	int			len, plen;
+	char			*cptr, *placeholder;
+	struct nfs4fs_compound	compound;
+	struct nfs4fs_fattr     fattr;
+	int			tcp, authflavor;
+	struct rpc_timeout	timeparms;
+	struct rpc_xprt		*xprt = NULL;
+	struct rpc_clnt		*clnt = NULL;
+	struct dentry		*root_dentry = NULL;
+	int			raised_rpciod = 0;
+	int			i;
+
+	nfsv4_printk(level2, "nfs4_read_super: starting\n");
+	initialize();
+	sb->s_op = &nfs4_sops;
+	sb->s_magic = NFS4_SUPER_MAGIC;
+	
+	/* ACL Temp code */
+#if NFS4ACL
+	sb->s_ext_attr_flags = 0;
+	sb->s_ext_attr_flags |= EXT_ATTR_FLAG_EXT_ATTR;
+	sb->s_ext_attr_flags |= EXT_ATTR_FLAG_EXT_ATTR_USER;
+        sb->s_ext_attr_flags |= EXT_ATTR_FLAG_POSIX_ACL;
+#endif
+	/* end of ACL Temp code */
+	
+	memset(sbinfo, 0, sizeof(*sbinfo));
+	sbinfo->flags = data->flags & NFS4_MOUNT_FLAGMASK;
+	sbinfo->strict_locking = data->strict_locking;
+	sbinfo->silly_rename = data->silly_rename;
+	sbinfo->last_renewal = jiffies;
+	sbinfo->flushd_timeout = jiffies;
+	INIT_LIST_HEAD(&sbinfo->flushd_inodes);
+	
+	nfsv4_printk(mount, "nfs4_read_super: silly_rename=%s strict_locking=%s\n",
+		     sbinfo->silly_rename ? "yes" : "no",
+		     sbinfo->strict_locking ? "yes" : "no");
+	
+	/*
+	 * 1. Check the verson #, hostname, remote address, and mount path passed down by
+	 *    the mount program
+	 *
+	 * XXX: data->namlen is _not_ strlen(data->hostname).  What is it?
+	 */
+	if (!data) {
+		printk("nfs4_read_super: no data from mount program!\n");
+		goto out_fail;
+	}
+	if (data->version != NFS4_MOUNT_VERSION) {
+		printk("nfs4: mount version %s than kernel\n",
+			data->version < NFS4_MOUNT_VERSION ? "older" : "newer");
+		printk("nfs4: maybe you need to bring your mount program up-to-date?\n");
+		if (data->version < NFS4_MOUNT_VERSION)
+			goto out_fail;
+	}
+	if (data->addr.sin_addr.s_addr == INADDR_ANY) {
+		printk("nfs4_read_super: mount program didn't pass remote address!\n");
+		goto out_fail;
+	}
+	nfsv4_printk(mount, "nfs4_read_super: remote addr = %s\n",
+		     in_ntoa(data->addr.sin_addr.s_addr));
+
+	/* Remote hostname */
+	for (len = 0; len < sizeof(data->hostname); len++)
+		if (!data->hostname[len])
+			break;
+	sbinfo->namelen = len;
+	if (!(sbinfo->hostname = NFS4_ALLOC(len+1))) {
+		printk("nfs4: couldn't allocate hostname buffer?!\n");
+		goto out_fail;
+	}
+	memcpy(sbinfo->hostname, data->hostname, len);
+	sbinfo->hostname[len] = 0;
+	nfsv4_printk(mount, "nfs4_read_super: hostname=\"%s\"\n", sbinfo->hostname);
+
+	/* Remote pathname */
+	plen = 1;
+	for (len = 0; (len < sizeof(data->mnt_path)) && data->mnt_path[len]; len++)
+		if (data->mnt_path[len] == '/')
+			plen++;
+	if (len == sizeof(data->mnt_path)) {
+		printk("nfs4_read_super: mount passed non null-terminated mount path!\n");
+		goto out_fail;
+	}
+	if (!(sbinfo->mnt_path = NFS4_ALLOC(len+1))) {
+		printk("nfs4_read_super: couldn't alloc mount path!\n");
+		goto out_fail;
+	}	
+	memcpy(sbinfo->mnt_path, data->mnt_path, len);
+	sbinfo->mnt_path[len] = 0;
+	if (!(sbinfo->pval = NFS4_ALLOC(plen * sizeof(struct qstr)))) {
+		printk("nfs4_read_super: couldn't alloc mount path!\n");
+		goto out_fail;
+	}
+	
+	sbinfo->plen = 0;
+	cptr = sbinfo->mnt_path;
+	for (;;) {
+		while (*cptr == '/')
+			cptr++;
+		if (!*cptr)
+			break;
+		NFS4_ASSERT(sbinfo->plen < plen);
+		sbinfo->pval[sbinfo->plen].name = placeholder = cptr;
+		while (*cptr && (*cptr != '/'))
+			cptr++;
+		sbinfo->pval[sbinfo->plen].len = cptr - placeholder;
+		sbinfo->plen++;
+	}
+	nfsv4_printk(mount, "nfs4_read_super: mount_path=/");
+	for (i = 0; i < sbinfo->plen; i++)
+		nfsv4_printk(mount, "%.*s/", (int)sbinfo->pval[i].len, sbinfo->pval[i].name);
+	nfsv4_printk(mount, "\n");
+
+
+	/*
+	 * 2. Set up state for the compound RPC which will get the root filehandle
+	 *    and attributes
+	 *
+	 * XXX: For now, ignore the NFS4_MOUNT_TCP flag and always use UDP.
+	 * XXX: For now, always AUTH_UNIX authentication.
+	 */
+	
+	tcp = 0;
+	authflavor = RPC_AUTH_UNIX;
+
+	/* Initialize timeout values */
+	timeparms.to_initval = (data->timeo * HZ / 10);
+	timeparms.to_retries = data->retrans ?: 5;
+	timeparms.to_maxval  = tcp? RPC_MAX_TCP_TIMEOUT : RPC_MAX_UDP_TIMEOUT;
+	timeparms.to_exponential = 1;
+	if (!timeparms.to_initval)
+		timeparms.to_initval = (tcp ? 600 : 11) * HZ / 10;
+
+	/* create RPC transport */
+	xprt = xprt_create_proto(tcp? IPPROTO_TCP : IPPROTO_UDP, &data->addr, &timeparms);
+	if (!xprt) {
+		nfsv4_printk(mount2, "nfs4_read_super: couldn't create RPC xprt!\n");
+		goto out_fail;
+	}
+	nfsv4_printk(mount, "nfs4_read_super: RPC transport created successfully\n");
+
+	/* create RPC client state */
+	clnt = rpc_create_client(xprt, sbinfo->hostname, &nfs4_program, 4, authflavor);
+	if (!clnt) {
+		nfsv4_printk(mount2, "nfs4_read_super: couldn't create RPC clnt!\n");
+		xprt_destroy(xprt);
+		goto out_fail;
+	}
+	clnt->cl_intr     = (data->flags & NFS4_MOUNT_INTR)? 1 : 0;
+	clnt->cl_softrtry = (data->flags & NFS4_MOUNT_SOFT)? 1 : 0;
+	clnt->cl_chatty   = 1;
+	sbinfo->client = clnt;
+	nfsv4_printk(mount, "nfs4_read_super: RPC client state created successfully\n");
+
+	if (rpciod_up() != 0) {
+		nfsv4_printk(mount2, "nfs4_read_super: couldn't start rpciod!\n");
+		goto out_fail;
+	}
+	raised_rpciod = 1;
+	nfsv4_printk(mount, "nfs4_read_super: rpciod running\n");
+	
+	/*
+	 *  3.  instantiate the root of the filesystem; make the COMPOUND call
+	 */
+	
+	nfsv4_printk(mount, "read_super: reading root fh\n");
+	if (!(root_dentry = d_alloc(NULL, &(const struct qstr) { "/", 1, 0 }))) {
+		printk("nfs4_read_super: couldn't alloc root dentry!\n");
+		goto out_fail;
+	}
+	root_dentry->d_sb = sb;
+	root_dentry->d_parent = root_dentry;
+	root_dentry->d_op = &nfs4_dentry_operations;
+	sb->s_root = root_dentry;
+
+	nfs4fs_setup_compound(&compound, sb, "nfs4_read_super()");
+	nfs4fs_setup_putrootfh(&compound, sb);
+	nfs4fs_setup_getattr(&compound, mount_bitmap);
+	nfs4fs_setup_getfh(&compound);
+	
+	if (nfs4fs_call_compound(&compound))
+		goto out_release;
+	if (nfs4fs_handle_putrootfh(&compound))
+		goto out_release;
+	if (nfs4fs_handle_getattr_dentry(&compound, root_dentry, &fattr))
+		goto out_release;
+	if (nfs4fs_handle_getfh(&compound, root_dentry->d_inode))
+		goto out_release;
+	
+	nfs4fs_release_compound(&compound);
+	nfsv4_printk(mount, "nfs4_read_super: root of filesystem instantiated\n");
+	
+	sbinfo->rsize = nfs_block_size(data->rsize, NULL);
+	sbinfo->wsize = nfs_block_size(data->wsize, NULL);
+	if ((fattr.fa_valid & FA_MAXREAD) && (sbinfo->rsize > fattr.fa_maxread)) {
+		printk(KERN_INFO "nfs4: rsize=%d too large for server\n", sbinfo->rsize);
+		sbinfo->rsize = nfs_block_bits(fattr.fa_maxread, NULL);
+		printk(KERN_INFO "nfs4: setting rsize=%d\n", sbinfo->rsize);
+	}
+	if ((fattr.fa_valid & FA_MAXWRITE) && (sbinfo->wsize > fattr.fa_maxwrite)) {
+		printk(KERN_INFO "nfs4: wsize=%d too large for server\n", sbinfo->wsize);
+		sbinfo->wsize = nfs_block_bits(fattr.fa_maxwrite, NULL);
+		printk(KERN_INFO "nfs4: setting wsize=%d\n", sbinfo->wsize);
+	}
+	sbinfo->rpages = (sbinfo->rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+	sbinfo->wpages = (sbinfo->wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+	if (sbinfo->rpages > NFS4_MAXIO) {
+		printk(KERN_INFO "nfs4: rpages=%d too large; setting rpages=%d\n",
+		       sbinfo->rpages, NFS4_MAXIO);
+		printk(KERN_INFO "nfs4: to use a larger rpages, increase NFS4_MAXIO\n");
+		sbinfo->rpages = NFS4_MAXIO;
+	}
+	if (sbinfo->wpages > NFS4_MAXIO) {
+		printk(KERN_INFO "nfs4: wpages=%d too large; setting wpages=%d\n",
+		       sbinfo->wpages, NFS4_MAXIO);
+		printk(KERN_INFO "nfs4: to use a larger rpages, increase NFS4_MAXIO\n");
+		sbinfo->wpages = NFS4_MAXIO;
+	}
+	sbinfo->dtsize = (sbinfo->rsize < PAGE_CACHE_SIZE) ? sbinfo->rsize : PAGE_CACHE_SIZE;
+	
+	nfsv4_printk(mount, "nfs4_read_super: rsize=%d wsize=%d dtsize=%d\n",
+		     sbinfo->rsize, sbinfo->wsize, sbinfo->dtsize);
+	nfsv4_printk(mount, "nfs4_read_super: rpages=%d wpages=%d\n",
+		     sbinfo->rpages, sbinfo->wpages);
+	
+	if (data->flags & NFS4_MOUNT_NOAC) {
+		data->acregmin = data->acregmax = 0;
+		data->acdirmin = data->acdirmax = 0;
+	}
+	sbinfo->acregmin = data->acregmin*HZ;
+	sbinfo->acregmax = data->acregmax*HZ;
+	sbinfo->acdirmin = data->acdirmin*HZ;
+	sbinfo->acdirmax = data->acdirmax*HZ;
+	sbinfo->lock_mintimeout = 1 * HZ;
+	sbinfo->lock_maxtimeout = 30 * HZ;
+	
+	nfsv4_printk(mount, "nfs4_read_super: regmin=%d regmax=%d dirmin=%d dirmax=%d\n",
+		     sbinfo->acregmin, sbinfo->acregmax, sbinfo->acdirmin, sbinfo->acdirmax);
+	nfsv4_printk(mount, "nfs4_read_super: lock_mintimeout=%ld lock_maxtimeout=%ld\n",
+		     sbinfo->lock_mintimeout, sbinfo->lock_maxtimeout);
+	
+	if (fattr.fa_valid & FA_LEASE_TIME) {
+		sbinfo->lease_time = fattr.fa_lease_time * HZ;
+		if (sbinfo->lease_time < NFS4FS_MIN_LEASE)
+			sbinfo->lease_time = NFS4FS_MIN_LEASE;
+		if (sbinfo->lease_time > NFS4FS_MAX_LEASE)
+			sbinfo->lease_time = NFS4FS_MAX_LEASE;
+	}
+	else
+		sbinfo->lease_time = NFS4FS_DEFAULT_LEASE;
+	
+	nfsv4_printk(mount, "nfs4_read_super: lease time=%d/%d\n", sbinfo->lease_time, HZ);
+
+	/*
+	 * XXX : ugh! setting sb->s_blocksize is kind of a pain.. what is this value even
+	 * used for, anyway?  for the time being, do the following: if data->bsize is
+	 * specified, use it.  otherwise, if the server returns both a maxread and a maxwrite
+	 * attr, use the smaller.  otherwise, use NFS4_DEF_FILE_IO_BUFFER_SIZE
+	 */
+	if (fattr.fa_valid & FA_MAXFILESIZE)
+		sb->s_maxbytes = fattr.fa_maxfilesize;
+	else {
+		printk(KERN_INFO "nfs4: warning: couldn't read max. file size from server\n");
+		sb->s_maxbytes = NFS4_DEF_MAXFILESIZE;
+	}
+	if (!data->bsize) {
+		if ((fattr.fa_valid & FA_MAXREAD) &&
+		    (fattr.fa_valid & FA_MAXWRITE))
+			data->bsize = (fattr.fa_maxread < fattr.fa_maxwrite) ?
+				fattr.fa_maxread : fattr.fa_maxwrite;
+		else
+			data->bsize = NFS4_DEF_FILE_IO_BUFFER_SIZE;
+	}
+	sb->s_blocksize = nfs_block_bits(data->bsize, &sb->s_blocksize_bits);
+	
+	nfsv4_printk(mount, "nfs4_read_super: s_blocksize=%ld\n", sb->s_blocksize);
+	
+	/*
+	 * 4. Start renewd for this superblock.
+	 */	
+
+	nfsv4_printk(mount, "read_super: bringing up renewd\n");
+	if (nfs4fs_init_renewd(sb)) {
+		printk(KERN_INFO "nfs4: couldn't start renewd for this mountpoint!\n");
+		goto out_fail;
+	}
+	nfsv4_printk(mount, "read_super: started renewd\n");
+
+	/*
+	 * 5. Start flushd for this superblock.
+	 */
+	
+	nfsv4_printk(mount, "read_super: bringing up flushd\n");
+	if (nfs4fs_init_flushd(sb)) {
+		printk(KERN_INFO "nfs4: couldn't start flushd for this mountpoint!\n");
+		goto out_fail;
+	}
+	nfsv4_printk(mount, "read_super: started flushd\n");
+
+	/*
+	 * 6. Start the callback service for this superblock.
+	 */
+	
+	nfsv4_printk(mount, "read_super: inititializing callback service\n");
+	INIT_LIST_HEAD(&sbinfo->delegations);
+	if (nfs4_create_callback(sb)) {
+		printk(KERN_WARNING "nfs4: warning: couldn't start callback service\n"
+		       "nfs4: warning: delegations will be unavailable for this mountpoint\n");
+	}
+	else
+		nfsv4_printk(mount, "read_super: callback service running\n");
+
+	/*
+	 * 7. SETCLIENTID.  We now do this at mount time.
+	 */
+	
+	memcpy(NFS4_IP_ADDR(sb), data->ipaddr, 16);
+	nfsv4_printk(mount, "read_super: doing SETCLIENTID\n");
+	if (nfs4_do_setclientid(&compound, sb)) {
+		printk("nfs4_read_super: couldn't do SETCLIENTID!\n");
+		goto out_fail;
+	}
+	nfsv4_printk(mount, "read_super: SETLCIENTID finished\n");
+
+	/*
+	 * TODO: To be a good citizen, we should issue a DELEGPURGE here, to advise
+	 * the server that we won't be reclaiming any delegations which were issued
+	 * to the previous boot instance of our client (this would be possible if
+	 * we could commit delegations to stable storage).
+	 */
+	
+out:
+	nfsv4_printk(level2, "nfs4_read_super: returning %s\n\n", sb ? "success" : "failure");
+	return sb;
+
+out_release:
+	nfs4fs_release_compound(&compound);	
+out_fail:
+	if (sbinfo->nfs_client) {
+		nfs4fs_put_client(sbinfo->nfs_client);   /* un-SETCLIENTID */
+		sbinfo->nfs_client = NULL;
+	}
+	nfs4_destroy_callback(sb);
+	if (sb->s_root) {
+		dput(sb->s_root);
+		sb->s_root = NULL;
+	}
+	if (sbinfo->client) {
+		/*
+		 * XXX: This question applies both to here and the
+		 * umount_begin()..put_super() sequence: when rpc_killall_tasks()
+		 * returns, can we guarantee that there are no pending FSM steps
+		 * which will dereference the superblock?
+		 *
+		 * Likewise, when rpc_killall_tasks() returns, is it safe to call
+		 * rpciod_down() immediately, or do we need the daemon to finish
+		 * off some FSM steps?
+		 */
+		rpc_killall_tasks(sbinfo->client);
+		rpc_shutdown_client(sbinfo->client);
+		sbinfo->client = NULL;
+	}
+	if (raised_rpciod)
+		rpciod_down();
+	if (sbinfo->pval) {
+		NFS4_FREE(sbinfo->pval);
+		sbinfo->pval = NULL;
+	}
+	if (sbinfo->mnt_path) {
+		NFS4_FREE(sbinfo->mnt_path);
+		sbinfo->mnt_path = NULL;
+	}
+	if (sbinfo->hostname) {
+		NFS4_FREE(sbinfo->hostname);
+		sbinfo->hostname = NULL;
+	}
+	sb = NULL;
+	goto out;
+}
+
+/*
+ * s_op->umount_begin().
+ */
+static void
+nfs4_umount_begin(struct super_block *sb)
+{
+	struct rpc_clnt *clnt = sb->u.nfs4_sb.client;
+
+	/*
+	 * This will also kill renewd and flushd.
+	 * XXX: should we set ->cl_dead as well?
+	 */
+	if (clnt)
+		rpc_killall_tasks(clnt);
+}
+
+/*
+ * s_op->put_super().  This is called during the unmount process, after
+ * clearing the dcache but before clearing the icache.  The upshot of
+ * this is that, if no files are open, no references can be held to
+ * dentries, ever.  If references are held to inodes, that is OK as long
+ * as they are disposed of before ->put_super() returns.
+ *
+ * Some code here is shared with the tail end of nfs4_read_super(),
+ * but it didn't seem worth creating a common function...
+ */
+static void
+nfs4_put_super(struct super_block *sb)
+{
+	struct nfs4_sb_info *sbinfo = &sb->u.nfs4_sb;
+
+	/*
+	 * If we have any "placeholder" OPENs, they are holding references
+	 * to inodes, so we must kill them now.
+	 */
+	nfs4fs_killall_placeholders(sb);
+	  
+	if (sbinfo->nfs_client) {
+		nfs4fs_put_client(sbinfo->nfs_client);
+		sbinfo->nfs_client = NULL;
+	}
+	nfs4_destroy_callback(sb);
+	if (sbinfo->client) {
+		rpc_shutdown_client(sbinfo->client);
+		sbinfo->client = NULL;
+	}
+	rpciod_down();
+	if (sbinfo->pval) {
+		NFS4_FREE(sbinfo->pval);
+		sbinfo->pval = NULL;
+	}
+	if (sbinfo->mnt_path) {
+		NFS4_FREE(sbinfo->mnt_path);
+		sbinfo->mnt_path = NULL;
+	}
+	if (sbinfo->hostname) {
+		NFS4_FREE(sbinfo->hostname);
+		sbinfo->hostname = NULL;
+	}
+
+	/*
+	 * TODO: As a sanity check, search through all of our state tables to
+	 * ensure that all state associated with this superblock has been
+	 * cleaned up?
+	 */
+}
+
+/*
+ * s_op->read_inode(): This is called whenever a new inode is allocated.
+ * We use it to initialize the fields to some nominal values.
+ */
+static void
+nfs4_read_inode(struct inode *inode)
+{
+	inode->i_blksize = inode->i_sb->s_blocksize;
+	inode->i_mode = 0;
+	inode->i_rdev = 0;
+	NFS4_FSID_MAJOR(inode) = 0;
+	NFS4_FSID_MINOR(inode) = 0;
+	NFS4_FILEID(inode) = 0;
+	NFS4_FLAGS(inode) = 0;
+	NFS4_FH_LEN(inode) = 0;
+	NFS4_FH_VAL(inode) = NFS4_FH_INLINE_VAL(inode);
+	NFS4_DIRECTORY_SIZE(inode) = 0;
+	NFS4_CACHEINV(inode);
+	NFS4_CACHE_TIMEOUT(inode) = NFS4_MINTIMEOUT(inode);
+	NFS4_CACHE_TIMEOUT_JIFFIES(inode) = jiffies;
+	NFS4_DELEGATION(inode) = NULL;
+	INIT_LIST_HEAD(&NFS4_ACCESS_CACHE_ENTRIES(inode));
+	INIT_LIST_HEAD(&NFS4_READ_OPENS(inode));
+	INIT_LIST_HEAD(&NFS4_WRITE_OPENS(inode));
+	init_MUTEX(&NFS4_LOCKING_SEMAPHORE(inode));
+	NFS4_FLUSHD_NEXTSCAN(inode) = jiffies + NFS4_WRITEBACK_LOCKDELAY;	
+	INIT_LIST_HEAD(&NFS4_FLUSHD_LIST(inode));
+	INIT_LIST_HEAD(&NFS4_FLUSHD_READ(inode));
+	INIT_LIST_HEAD(&NFS4_FLUSHD_WRITE(inode));
+	INIT_LIST_HEAD(&NFS4_FLUSHD_DIRTY(inode));
+	INIT_LIST_HEAD(&NFS4_FLUSHD_COMMIT(inode));
+	NFS4_FLUSHD_NREAD(inode) = 0;
+	NFS4_FLUSHD_NDIRTY(inode) = 0;
+	NFS4_FLUSHD_NCOMMIT(inode) = 0;
+}
+
+/*
+ * s_op->clear_inode(): This is the inverse of the routine above: it is called
+ * just before an inode is deallocated.
+ */
+static void
+nfs4_clear_inode(struct inode *inode)
+{
+	NFS4_ASSERT(list_empty(&NFS4_READ_OPENS(inode)));
+	NFS4_ASSERT(list_empty(&NFS4_WRITE_OPENS(inode)));
+	NFS4_ASSERT(list_empty(&NFS4_FLUSHD_LIST(inode)));
+	NFS4_ASSERT(list_empty(&NFS4_FLUSHD_READ(inode)));
+	NFS4_ASSERT(list_empty(&NFS4_FLUSHD_WRITE(inode)));
+	NFS4_ASSERT(list_empty(&NFS4_FLUSHD_DIRTY(inode)));
+	NFS4_ASSERT(list_empty(&NFS4_FLUSHD_COMMIT(inode)));
+
+	nfs4fs_drop_delegation_from_inode(inode);
+	
+	if (!NFS4_FH_IS_INLINE(inode)) {
+		NFS4_FREE(NFS4_FH_VAL(inode));
+		NFS4_FH_VAL(inode) = NFS4_FH_INLINE_VAL(inode);
+	}
+}
+
+/*
+ * s_op->statfs().
+ */
+#define STATFS_MASK	(FA_STOTAL | FA_SAVAIL | FA_FTOTAL | FA_FFREE | FA_MAXNAME)
+static u32 statfs_bitmap[2] =	{ 0x20c00000, 0x00001400};
+
+static int
+nfs4_statfs(struct super_block *sb, struct statfs *buf)
+{
+	struct nfs4fs_compound compound;
+	struct nfs4fs_fattr fattr;
+	unsigned char blockbits;
+	unsigned long blockres;
+	int status;
+
+	nfsv4_printk(level2, "nfs4_statfs: starting\n");
+
+	buf->f_type = NFS4_SUPER_MAGIC;
+	nfs4fs_setup_compound(&compound, sb, "nfs4_statfs()");
+	nfs4fs_setup_putfh(&compound, sb->s_root->d_inode);
+	nfs4fs_setup_getattr(&compound, statfs_bitmap);
+
+	if ((status = nfs4fs_call_compound(&compound)))
+		goto out_err;
+	if ((status = nfs4fs_handle_putfh(&compound)))
+		goto out_err;
+	if ((status = nfs4fs_handle_getattr_simple(&compound, &fattr)))
+		goto out_err;
+
+	status = -EIO;
+	if ((fattr.fa_valid & STATFS_MASK) != STATFS_MASK)
+		goto out_missing_attr;
+
+	/*
+	 * XXX: What about buf->f_fsid?  nfsv3 does not set this...
+	 */
+	buf->f_bsize = nfs_block_bits(sb->s_blocksize, &blockbits);
+	blockres = (1 << blockbits) - 1;
+	buf->f_blocks = (fattr.fa_stotal + blockres) >> blockbits;
+	buf->f_bavail = (fattr.fa_savail + blockres) >> blockbits;
+	buf->f_files = (fattr.fa_ftotal + blockres) >> blockbits;
+	buf->f_ffree = (fattr.fa_ffree + blockres) >> blockbits;
+	buf->f_namelen = fattr.fa_maxname;
+	status = 0;
+	
+out_release:
+	nfs4fs_release_compound(&compound);
+	nfsv4_printk(level2, "nfs4_statfs: returning status %d\n\n", status);
+	return status;
+out_missing_attr:
+	printk("nfs4_statfs: missing attr (0x%x/0x%x)!\n",
+	       fattr.fa_valid & STATFS_MASK, STATFS_MASK);
+out_err:
+	/*
+	 * XXX: nfsv3 returns 0 even if there is an error; why?
+	 */
+	buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1;
+	goto out_release;
+}
+
+static inline int
+nfs4_wait_on_inode(struct inode *inode, int flag)
+{
+	struct rpc_clnt *clnt = NFS4_CLNT(inode->i_sb);
+	sigset_t oldmask;
+	int status;
+
+	NFS4_ASSERT(kernel_locked());
+	
+	if (!(NFS4_FLAGS(inode) & flag))
+		return 0;
+	
+	atomic_inc(&inode->i_count);
+	if (clnt->cl_intr) {
+		rpc_clnt_sigmask(clnt, &oldmask);
+		status = wait_event_interruptible(inode->i_wait, !(NFS4_FLAGS(inode) & flag));
+		rpc_clnt_sigunmask(clnt, &oldmask);
+	}
+	else {
+		wait_event_interruptible(inode->i_wait, !(NFS4_FLAGS(inode) & flag));
+		status = 0;
+	}
+	iput(inode);
+	
+	return status;
+}
+
+int
+nfs4_revalidate_inode(struct inode *inode)
+{
+	struct nfs4fs_compound compound;
+	int status = 0;
+
+	NFS4_ASSERT(inode != NULL);
+	
+	nfsv4_printk(level2, "nfs4_revalidate_inode: starting on ino %ld\n", inode->i_ino);
+	lock_kernel();
+	
+	while (NFS4_IS_REVALIDATING(inode)) {
+		status = nfs4_wait_on_inode(inode, NFS4_INO_REVALIDATING);
+		if (status < 0)
+			goto out;
+	}
+
+	if (!nfs4_need_reval(inode))
+		goto out;
+	
+	nfs4fs_setup_compound(&compound, inode->i_sb, "revalidate_inode");
+	nfs4fs_setup_putfh(&compound, inode);
+	nfs4fs_setup_getattr(&compound, nfs4_standard_bitmap);
+
+	if ((status = nfs4fs_call_compound(&compound)))
+		goto out_release;
+	if ((status = nfs4fs_handle_putfh(&compound)))
+		goto out_release;
+	if ((status = nfs4fs_handle_getattr_inode(&compound, inode)))
+		goto out_release;
+
+out_release:
+	nfs4fs_release_compound(&compound);
+out:
+	unlock_kernel();
+	nfsv4_printk(level2, "nfs4_revalidate_inode: returning status %d\n\n", status);
+	return status;
+}
+
+int
+nfs4_revalidate(struct dentry *dentry)
+{
+	return nfs4_revalidate_inode(dentry->d_inode);
+}
+
+/*
+ * XXX: This was moved into its own function to avoid having two nfs4fs_compound's on
+ * the stack at once -- this would eat up our stack space.  This is the "huge problem"
+ * listed as a TODO in include/linux/nfs4/nfs4fs.h.  Once this TODO is solved, it can
+ * be moved back into nfs4_setattr().
+ */
+static int
+nfs4_do_setattr(struct nfs4fs_file_data *fp, struct inode *inode, struct iattr *iap)
+{
+	struct nfs4fs_compound compound;
+	int status;
+
+	NFS4_ASSERT(inode != NULL);
+	
+	nfs4fs_setup_compound(&compound, inode->i_sb, "nfs4_setattr()");
+	nfs4fs_setup_putfh(&compound, inode);
+	nfs4fs_setup_setattr(&compound, iap, fp);
+	nfs4fs_setup_getattr(&compound, nfs4_standard_bitmap);
+
+	if ((status = nfs4fs_call_compound(&compound)))
+		goto out;
+	if ((status = nfs4fs_handle_putfh(&compound)))
+		goto out;
+	if ((status = nfs4fs_handle_setattr(&compound)))
+		goto out;
+
+	if (iap->ia_valid & ATTR_SIZE) {
+		nfsv4_printk(setattr2, "nfs4_do_setattr: truncating file\n");
+		vmtruncate(inode, iap->ia_size);
+	}
+	
+	if ((status = nfs4fs_handle_getattr_inode(&compound, inode)))
+		goto out;
+
+out:
+	return status;
+}
+
+/*
+ * i_op->setattr().
+ * Locks held: BKL, also inode->i_sem if size change requested
+ *
+ * TODO: There is a big problem remaining here!  If this is a size-changing
+ * setattr, we will have to open the file.  It is possible (since OPEN is
+ * always by name) that we will open the wrong file ("wrong" in the sense
+ * that it doesn't match the dentry that the caller just looked up with
+ * LOOKUP and passed in as a parameter).  We don't have any way to deal
+ * with this, so we just return an error for the time being.  (This logic
+ * can actually be found in nfs4fs_do_open_exact(), not here.)
+ *
+ * One solution would be to allow this routine to return a special error
+ * code which informs the caller to redo the lookup, and try again.  Another
+ * would be to design a new interface which does the lookup for the final
+ * component, and the setattr, in one go.  In other words, we could have
+ * a ->lookup_setattr() analagous to ->lookup_open() in open.c.
+ *
+ * TODO: Implement an optimization: If we already have the file opened
+ * for write, piggyback onto the old stateid, instead of opening it again?
+ * Is there any reason why this might not be a good idea?
+ */
+int
+nfs4_setattr(struct dentry *dentry, struct iattr *iap)
+{
+	struct nfs4fs_file_data *fp = NULL;
+	int status;
+	
+	nfsv4_printk(level2, "nfs4_setattr: starting on \"%.*s\", iap->ia_valid=%d\n",
+		     (int)dentry->d_name.len, dentry->d_name.name, iap->ia_valid);
+
+	NFS4_ASSERT(dentry->d_inode != NULL);
+
+	/* probably not necessary, but... */
+	if (!S_ISREG(dentry->d_inode->i_mode))
+		iap->ia_valid &= ~ATTR_SIZE;
+
+	if ((status = nfs4_sync_file(dentry->d_inode, NULL, 0, 0, FLUSH_WAIT)))
+		goto out;
+
+	if (iap->ia_valid & ATTR_SIZE) {
+		/*
+		 * XXX: Temporary hack: O_DIRECT means "do not delegate"
+		 */
+		if ((status = nfs4fs_do_open_exact(dentry, FI_WRITE | O_DIRECT, 0, &fp))) {
+			nfsv4_printk(open2, "nfs4_setattr: do_open failed!\n");
+			goto out;
+		}
+		status = nfs4_sync_file(dentry->d_inode, 0, 0, 0, FLUSH_WAIT|FLUSH_STABLE);
+		if (status) {
+			nfsv4_printk(open2, "nfs4_setattr: nfs4_sync_file failed!\n");
+			goto out;
+		}
+	}
+
+	status = nfs4_do_setattr(fp, dentry->d_inode, iap);
+	
+out:
+	if (fp)
+		nfs4fs_put_file_data(fp);
+	nfsv4_printk(level2, "nfs4_setattr: returning status %d\n\n", status);
+	return status;
+}
+
+/*
+ * XXX: needs to be done asynchronously...
+ */
+int
+nfs4_do_secinfo(struct super_block *sb,struct dentry *dentry)
+{
+  struct nfs4fs_compound compound;
+  int status;
+  struct rpc_auth *save_auth = NULL;
+
+  nfsv4_printk(level2, "nfs4_do_secinfo: starting\n");
+
+  /* Save old auth handle, replace with AUTH_GSS. */
+  if(sb->u.nfs4_sb.client && sb->u.nfs4_sb.client->cl_auth){
+    save_auth = sb->u.nfs4_sb.client->cl_auth;
+  }
+  rpcauth_create(RPC_AUTH_GSS, sb->u.nfs4_sb.client);
+
+	nfs4fs_setup_compound(&compound, sb, "do_secinfo()");
+	nfs4fs_setup_putfh(&compound,dentry->d_inode);
+	nfs4fs_setup_secinfo(&compound,dentry);
+
+  if ((status = nfs4fs_call_compound(&compound))) {
+    goto out_auth;
+	}
+  if ((status = nfs4fs_handle_putfh(&compound))) {
+    goto out_auth;
+	}
+	if ((status = nfs4fs_handle_secinfo(&compound))) {
+		goto out_auth;
+	}
+
+out:
+	nfs4fs_release_compound(&compound);
+  nfsv4_printk(level2, "nfs4_do_secinfo: returning status %d\n", status);
+  return status;
+out_auth:
+  /* restore old auth */
+  printk("nfs4_do_secinfo: 9\n");
+  sb->u.nfs4_sb.client->cl_auth = save_auth;
+	goto out;
+}
+
+int
+nfs4_do_setclientid(struct nfs4fs_compound *cp, struct super_block *sb)
+{
+	struct nfs4fs_client *clp;
+	u32 prog;
+	unsigned short port;
+	int status;
+
+	nfsv4_printk(level2, "nfs4_do_setclientid: starting\n");
+	
+	NFS4_ASSERT(kernel_locked());
+
+	status = -ENOMEM;
+	if (!(clp = nfs4fs_get_client())) {
+		nfsv4_printk(level1, "nfs4: couldn't allocate client!\n");
+		goto out;
+	}
+	prog = NFS4_CALLBACK_PROG(sb);
+	port = NFS4_CALLBACK_PORT(sb);
+
+	/*
+	 * SETCLIENTID
+	 */
+	nfs4fs_setup_compound(cp, sb, "setclientid()");
+	nfs4fs_setup_setclientid(cp, prog, port);
+	if ((status = nfs4fs_call_compound(cp)))
+		goto out_release;
+	if ((status = nfs4fs_handle_setclientid(cp, clp)))
+		goto out_release;
+	nfs4fs_release_compound(cp);
+
+	/*
+	 * SETCLIENTID_CONFIRM
+	 */
+	nfs4fs_setup_compound(cp, sb, "setclientid_confirm()");
+	nfs4fs_setup_setclientid_confirm(cp, clp);
+	if ((status = nfs4fs_call_compound(cp)))
+		goto out_release;
+	if ((status = nfs4fs_handle_setclientid_confirm(cp)))
+		goto out_release;
+	nfs4fs_release_compound(cp);
+
+	/* success! */
+	if (NFS4_CLIENT(sb))
+		nfs4fs_put_client(NFS4_CLIENT(sb));
+	NFS4_CLIENT(sb) = clp;
+
+out:
+	return status;
+out_release:
+	nfs4fs_release_compound(cp);
+	nfs4fs_put_client(clp);
+	goto out;
+}	
+
+static DECLARE_FSTYPE(nfs4_fstype, "nfs4", nfs4_read_super, FS_ODD_RENAME);
+
+#ifdef MODULE
+
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR("UM-CITI");
+
+int
+init_module(void)
+{
+#ifdef CONFIG_PROC_FS
+	rpc_proc_register(&nfs4_rpcstat);
+#endif
+
+	initialize();
+
+	printk(KERN_INFO "Installing nfs4fs\n");
+        return register_filesystem(&nfs4_fstype);
+}
+
+void
+cleanup_module(void)
+{
+#ifdef CONFIG_PROC_FS
+	rpc_proc_unregister("nfs4");
+#endif
+	printk(KERN_INFO "Uninstalling nfs4fs\n");
+	nfs4fs_cleanup_state();
+	nfs4fs_cleanup_access_cache();
+	unregister_filesystem(&nfs4_fstype);
+}
+#endif
--- clean/fs/nfs4fs/renewd.c	Thu Feb  7 18:04:30 2002
+++ dirty/fs/nfs4fs/renewd.c	Tue Feb  5 10:44:12 2002
@@ -0,0 +1,154 @@
+/*
+ *  linux/fs/nfs4fs/renewd.c
+ *
+ *  Copyright (c) 2001 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  Kendrick Smith <kmsmith@umich.edu>
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the University nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/sunrpc/sched.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+
+#include <linux/nfs4/nfs4.h>
+#include <linux/nfs4/nfs4_debug.h>
+#include <linux/nfs4/nfs4fs_xdr.h>
+#include <linux/nfs4/nfs4fs.h>
+
+/*
+ * Implementation of the NFSv4 "renew daemon", which wakes up periodically to
+ * send a RENEW, to keep state alive on the server.  The daemon is implemented
+ * as an rpc_task, not a real kernel thread, so it always runs in rpciod's
+ * context.  There is one renewd per superblock.
+ */
+
+static void renewd(struct rpc_task *task);
+static void renewd_exit(struct rpc_task *task);
+static void renew_handler(struct nfs4fs_compound *cp);
+
+static struct rpc_wait_queue   renewd_queue = RPC_INIT_WAITQ("nfs4_renewd");
+
+/* Note: Before calling nfs4fs_init_renewd(), make sure that the lease period and
+ * last renewal time are correctly set in the superblock!
+ */
+int
+nfs4fs_init_renewd(struct super_block *sb)
+{
+	struct rpc_task *task;
+	int status;
+	
+	lock_kernel();	
+	status = -ENOMEM;
+	if (!(task = rpc_new_task(NFS4_CLNT(sb), renewd_exit, RPC_TASK_ASYNC)))
+		goto out;
+	task->tk_calldata = sb;
+	task->tk_action = renewd;
+	status = rpc_execute(task);
+	if (!status)
+		nfsv4_printk(level1, "RENEWD: created!\n");
+	
+out:
+	unlock_kernel();
+	return status;
+}
+
+static void
+renewd(struct rpc_task *task)
+{
+	struct super_block *sb = (struct super_block *)task->tk_calldata;
+	struct nfs4fs_compound *cp;
+	unsigned long timeout;
+
+	/* NB: renewd runs with the BKL held, so no need for lock_kernel()... */
+	
+	nfsv4_printk(lease2, "RENEWD: woke up\n");
+	if (!NFS4_CLIENT(sb)) {
+		timeout = (2 * NFS4_LEASE_TIME(sb)) / 3;
+		goto out;
+	}
+	if (jiffies < NFS4_LAST_RENEWAL(sb) + NFS4_LEASE_TIME(sb)/3) {
+		timeout = (2 * NFS4_LEASE_TIME(sb)) / 3 + NFS4_LAST_RENEWAL(sb) - jiffies;
+		goto out;
+	}
+	
+	nfsv4_printk(lease2, "RENEWD: making RENEW call\n");
+
+#ifdef CONFIG_SUNRPC_GSS
+ /* for AUTH_GSS tell rpciod which creds to use  - 
+  * AUTH_SYS and AUTH_NONE, renewd will run as root */
+  printk("HARDCODE HACK! will soon set to machine creds. for now, pick a user with kerberos5 credentials! renewd setting proxy_uid/gid [%d:%d]\n",23975,100);
+  task->tk_client->cl_auth->proxy_uid = 23975;
+  task->tk_client->cl_auth->proxy_gid = 100;
+  task->tk_flags |= RPC_TASK_PROXYCREDS;
+#endif /* CONFIG_SUNRPC_GSS */
+
+	/* Set up the RENEW call... */
+	if (nfs4fs_setup_async(&cp, sb, "renewd", NULL, renew_handler, NULL))
+		goto fail;
+	nfs4fs_setup_renew(cp);
+	if (nfs4fs_call_async(cp))
+		goto fail;
+
+	timeout = 2 * NFS4_LEASE_TIME(sb) / 3;
+
+out:
+	if (timeout < 1 * HZ)
+		timeout = 1 * HZ;
+	task->tk_timeout = timeout;
+	task->tk_action = renewd;
+	task->tk_exit = NULL;
+	rpc_sleep_on(&renewd_queue, task, NULL, NULL);
+	nfsv4_printk(lease2, "RENEWD: back to sleep for %ld %ld/%d seconds\n",
+		     timeout / HZ, timeout % HZ, HZ);
+	return;
+
+fail:
+	timeout = 10 * HZ;      /* hardcoded */
+	printk(KERN_WARNING "nfs4: failed to renew lease!\n");
+	goto out;
+}
+
+static void
+renewd_exit(struct rpc_task *task)
+{
+	nfsv4_printk(level1, "RENEWD: destroyed!\n");
+}
+
+static void
+renew_handler(struct nfs4fs_compound *cp)
+{
+	nfsv4_printk(lease, "RENEWD: in handler\n");
+	if (nfs4fs_handle_renew(cp))
+		printk(KERN_WARNING "nfs4: couldn't renew lease!\n");
+}
--- clean/fs/nfs4fs/symlink.c	Thu Feb  7 18:04:30 2002
+++ dirty/fs/nfs4fs/symlink.c	Mon Feb  4 15:16:25 2002
@@ -0,0 +1,152 @@
+/*
+ * linux/fs/nfs4fs/symlink.c
+ * from linux/fs/nfs/symlink.c
+ *    Copyright (C) 1992  Rick Sladkey
+ *    Optimization changes Copyright (C) 1994 Florian La Roche
+ *
+ * NFSv4 implementation
+ *    Kendrick Smith  <kmsmith@umich.edu>
+ *    Andy Adamson  <andros@umich.edu>
+ *    Copyright (C) 2001 The Regents of the University of Michigan
+ *
+ */
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/sunrpc/sched.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+
+#include <linux/nfs4/nfs4.h>
+#include <linux/nfs4/nfs4_debug.h>
+#include <linux/nfs4/nfs4fs_xdr.h>
+#include <linux/nfs4/nfs4fs.h>
+
+static int nfs4_readlink(struct dentry *dentry, char *buffer, int buflen);
+static int nfs4_follow_link(struct dentry *dentry, struct nameidata *nd);
+	
+struct inode_operations nfs4_symlink_inode_operations = {
+	readlink:	nfs4_readlink,
+	follow_link:	nfs4_follow_link,
+	revalidate:	nfs4_revalidate,
+	setattr:	nfs4_setattr,
+#if NFS4ACL
+        permission: nfs4_posix_acl_permission,
+        get_posix_acl: nfs4_acl_get_inode,
+        set_posix_acl: nfs4_acl_set_inode,
+        get_ext_attr: nfs4_get_ext_attr,         
+        set_ext_attr: nfs4_set_ext_attr
+#endif
+};
+
+static int
+nfs4_getlink(struct dentry *dentry, struct page **ppage, char **bufp)
+{
+	struct inode *inode = dentry->d_inode;
+	struct page *page;
+	char *buf;
+	struct nfs4fs_compound compound;
+	int status;
+
+	lock_kernel();
+	
+	/* Caller revalidated the directory inode already. */
+	page = grab_cache_page(inode->i_mapping, 0);
+	nfsv4_printk(readlink, "nfs4_getlink: starting, page=%p\n", page);
+	status = -ENOMEM;
+	if (!page)
+		goto out_buf;
+	buf = kmap(page);
+	status = 0;
+	if (Page_Uptodate(page))
+		goto out;
+	nfsv4_printk(readlink, "nfs4_getlink: buf=%p, locked=%d, uptodate=%d, count=%d\n",
+		     buf, (int)PageLocked(page), (int)Page_Uptodate(page),
+		     (int)atomic_read(&page->count));
+
+	nfs4fs_setup_compound(&compound, dentry->d_sb, "nfs4_getlink()");
+	nfs4fs_setup_putfh(&compound, inode);
+	nfs4fs_setup_getattr(&compound, nfs4_standard_bitmap);
+	nfs4fs_setup_readlink(&compound, buf, PAGE_CACHE_SIZE - 1);
+
+	if ((status = nfs4fs_call_compound(&compound)))
+		goto out_release;
+	if ((status = nfs4fs_handle_putfh(&compound)))
+		goto out_release;
+	if ((status = nfs4fs_handle_getattr_inode(&compound, inode)))
+		goto out_release;
+	if ((status = nfs4fs_handle_readlink(&compound)))
+		goto out_release;
+
+	nfs4fs_release_compound(&compound);
+	SetPageUptodate(page);
+	
+out:
+	unlock_kernel();
+	*ppage = page;
+	*bufp = buf;
+	nfsv4_printk(readlink, "nfs4_getlink: returning status %d\n", status);
+	return status;
+out_release:
+	nfs4fs_release_compound(&compound);
+	kunmap(page);
+	UnlockPage(page);
+	page_cache_release(page);
+	page = NULL;
+out_buf:
+	buf = NULL;
+	goto out;
+}
+
+static int
+nfs4_readlink(struct dentry *dentry, char *buf, int buflen)
+{
+	struct page *page;
+	char *link;
+	int status;
+	
+	/* By calling vfs_readlink(), we compute strlen() for a string whose length we
+	   already know. probably not worth optimizing out, though.. */
+
+	nfsv4_printk(level2, "nfs4_readlink(%.*s/%.*s): starting\n",
+		     (int)dentry->d_parent->d_name.len, dentry->d_parent->d_name.name,
+		     (int)dentry->d_name.len, dentry->d_name.name);
+	
+	if ((status = nfs4_getlink(dentry, &page, &link)))
+		goto out;
+	status = vfs_readlink(dentry, buf, buflen, link);
+	kunmap(page);
+	UnlockPage(page);
+	page_cache_release(page);
+
+out:
+	nfsv4_printk(level2, "nfs4_readlink: returning status %d\n\n", status);
+	return status;
+}
+
+static int
+nfs4_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+	struct page *page;
+	char *buf;
+	int status;
+
+	nfsv4_printk(level2, "nfs4_follow_link(%.*s/%.*s): starting\n",
+		     (int)dentry->d_parent->d_name.len, dentry->d_parent->d_name.name,
+		     (int)dentry->d_name.len, dentry->d_name.name);
+	
+	if ((status = nfs4_getlink(dentry, &page, &buf)))
+		goto out_release;
+	status = vfs_follow_link(nd, buf);
+	kunmap(page);
+	UnlockPage(page);
+	page_cache_release(page);
+
+out:
+	nfsv4_printk(level2, "nfs4_follow_link: returning status %d\n\n", status);
+	return status;
+out_release:
+	path_release(nd);
+	goto out;
+}
--- clean/fs/nfs4fs/proc.c	Thu Feb  7 18:04:30 2002
+++ dirty/fs/nfs4fs/proc.c	Tue Feb  5 00:10:46 2002
@@ -0,0 +1,897 @@
+/*
+ *  fs/nfs4fs/proc.c
+ *
+ *  Copyright (c) 2002 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  Kendrick Smith <kmsmith@umich.edu>
+ *  Andy Adamson   <andros@umich.edu>
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the University nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This file implements "central processing" for COMPOUND requests.
+ *
+ * While reading this file, it may be helpful to refer to the following
+ * flow of events associated with a COMPOUND request:
+ *
+ *   1. Request is initialized with nfs4fs_setup_compound()
+ *      or nfs4fs_setup_async()
+ *   2. A series of nfs4fs_setup_*() routines are called, to
+ *      build up the COMPOUND operation-by-operation.
+ *   3. nfs4fs_call_compound() or nfs4fs_call_async() is called.
+ *      These call rpc_execute() and we descend into the RPC layer:
+ *         4. preprocess_compound() is called from the RPC layer
+ *            (through the ->tk_action pointer).
+ *         5. RPC layer calls routines in fs/nfs4fs/encode.c to
+ *            XDR encode the request.
+ *         6. The request is sent; response is received from server.
+ *         7. RPC layer calls routines in fs/nfs4fs/decode.c to
+ *            XDR decode the response.
+ *         8. postprocess_compound() is called from the RPC layer
+ *            (through the ->tk_exit pointer)
+ *
+ * If the request is synchronous, successive steps are as follows:
+ *    9. A series of nfs4fs_handle_*() routines are called, to
+ *       process the COMPOUND operation-by-operation.  E.g. if
+ *       if the COMPOUND contained a GETATTR, nfs4fs_handle_getattr()
+ *       will be called and will refresh the inode with the new
+ *       attributes.
+ *   10. nfs4fs_release_compound() is called, to release all
+ *       resources associated with the COMPOUND.
+ *
+ * If the request is asynchronous, the steps are similar, but the
+ * calling mechanism is different:
+ *             9. postprocess_compound() calls the function in cp->exit,
+ *                which calls a series of nfs4fs_handle_*() routines,
+ *                to process the COMPOUND operation-by-operation.
+ *            10. free_async() is called from the RPC layer (through
+ *                the ->tk_release pointer).  This routine calls
+ *                nfs4fs_release_compound() as in the sync case, but
+ *                also takes a few additional "releasing" steps which
+ *                are specific to the async case.
+ */
+
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/fs.h>
+#include <linux/sunrpc/sched.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+
+#include <linux/nfs4/nfs4.h>
+#include <linux/nfs4/nfs4_debug.h>
+#include <linux/nfs4/nfs4fs_xdr.h>
+#include <linux/nfs4/nfs4fs.h>
+
+static void preprocess_compound(struct rpc_task *task);
+static void postprocess_compound(struct rpc_task *task);
+static void postprocess_child(struct rpc_task *task);
+static void free_async(struct rpc_task *task);
+static int spawn_open_confirm(struct nfs4fs_compound *cp);
+static void open_confirm_handler(struct nfs4fs_compound *cp);
+static int spawn_secinfo(struct nfs4fs_compound *cp);
+static void secinfo_handler(struct nfs4fs_compound *cp);
+
+static struct rpc_wait_queue	parent_queue = RPC_INIT_WAITQ("nfs4_parent_queue");
+
+static inline void
+release_semaphores(struct nfs4fs_compound *cp)
+{
+	if (cp->flags & CA_SEQID_LOCKED) {
+		NFS4_ASSERT(cp->seqid_holder != NULL);
+		up(&cp->seqid_holder->lo_sema);
+		cp->flags &= ~CA_SEQID_LOCKED;
+	}
+
+	if (cp->flags & CA_AUX_SEQID_LOCKED) {
+		NFS4_ASSERT(cp->stateid_holder != NULL);
+		NFS4_ASSERT(cp->stateid_holder->fi_open_stateid != NULL);
+		up(&cp->stateid_holder->fi_open_stateid->fi_lockowner->lo_sema);
+		cp->flags &= ~CA_AUX_SEQID_LOCKED;
+	}
+	
+	if (cp->flags & CA_AUX_STATEID_LOCKED) {
+		NFS4_ASSERT(cp->stateid_holder != NULL);
+		NFS4_ASSERT(cp->stateid_holder->fi_open_stateid != NULL);
+		nfs4_up_read(&cp->stateid_holder->fi_open_stateid->fi_sema);
+		cp->flags &= ~CA_AUX_STATEID_LOCKED;
+	}
+	
+	if (cp->flags & CA_STATEID_LOCKED) {
+		/*
+		 * Note: CA_STATEID_RELEASE controls whether this is a read or write lock.
+		 * (See comment in nfs4fs.h)
+		 */
+		NFS4_ASSERT(cp->stateid_holder != NULL);
+		if (cp->flags & CA_STATEID_RELEASE)
+			nfs4_up_read(&cp->stateid_holder->fi_sema);
+		else
+			nfs4_up_write(&cp->stateid_holder->fi_sema);
+		cp->flags &= ~CA_STATEID_LOCKED;
+	}
+}
+
+/*
+ * This is the initial step of our RPC state machine.  It acquires necessary
+ * semaphores just before putting the request on the wire.  This is done for
+ * two reasons: (1) to minimize the window when the semaphores are held, and
+ * (2) as part of implementing reboot recovery.  If we get ERR_STALE, a later
+ * stage of our RPC state machine will release the semaphores, dispatch the
+ * recovery thread, and put the rpc_task to sleep.  When the recovery thread
+ * wakes us up, we automatically reacquire the semaphores as the first step
+ * in the RPC state machine.
+ *
+ * Under normal circumstances, this function just acquires the semaphores and
+ * enters into the "usual" RPC state machine.
+ */
+static void
+preprocess_compound(struct rpc_task *task)
+{
+	struct nfs4fs_compound *cp = (struct nfs4fs_compound *)task->tk_calldata;
+	struct nfs4fs_file_data *fp = (struct nfs4fs_file_data *)cp->stateid_holder;
+	struct nfs4fs_lockowner *lp = (struct nfs4fs_lockowner *)cp->seqid_holder;
+	struct rpc_message msg = { NFSPROC4_COMPOUND, cp, cp, NULL };
+	int status;
+
+	/*
+	 * Note: We never wait on a semaphore if this is an async operation.
+	 * Instead we do a trylock(), and if we fail, sleep and retry later.
+	 * This allows other async tasks to continue in the meantime, and
+	 * avoids potential deadlocks.
+	 */
+	NFS4_ASSERT(cp->sb != NULL);
+	NFS4_ASSERT(cp->seqid_index || !cp->seqid_holder);
+
+	/* Grab stateid lock, if we need it and don't already have it... */
+	if (fp && !(cp->flags & CA_STATEID_LOCKED)) {
+		/*
+		 * Note: CA_STATEID_RELEASE controls whether a read or write lock
+		 * is taken here (see comment in nfs4fs.h).
+		 */
+		if (!RPC_IS_ASYNC(task)) {
+			if (cp->flags & CA_STATEID_RELEASE)
+				nfs4_down_read(&fp->fi_sema);
+			else
+				nfs4_down_write(&fp->fi_sema);
+		}
+		else {
+			if (cp->flags & CA_STATEID_RELEASE)
+				status = nfs4_down_read_trylock(&fp->fi_sema);
+			else
+				status = nfs4_down_write_trylock(&fp->fi_sema);
+			if (status)
+				goto sleep;
+		}
+		cp->flags |= CA_STATEID_LOCKED;
+	}
+
+	/* Same thing for auxiliary stateid lock... */
+	if ((cp->flags & CA_AUXILIARY) && !(cp->flags & CA_AUX_STATEID_LOCKED)) {
+		NFS4_ASSERT(fp != NULL);
+		NFS4_ASSERT(IS_LOCK_STATEID(fp));
+
+		/*
+		 * The auxiliary stateid lock is always a read lock.
+		 */
+		if (!RPC_IS_ASYNC(task))
+			nfs4_down_read(&fp->fi_open_stateid->fi_sema);
+		else if (nfs4_down_read_trylock(&fp->fi_open_stateid->fi_sema))
+			goto sleep;
+		cp->flags |= CA_AUX_STATEID_LOCKED;
+	}
+
+	/* Same thing for seqid lock... */
+	if (lp && !(cp->flags & CA_SEQID_LOCKED)) {
+		if (!RPC_IS_ASYNC(task))
+			down(&lp->lo_sema);
+		else if (down_trylock(&lp->lo_sema))
+			goto sleep;
+		cp->flags |= CA_SEQID_LOCKED;
+	}
+
+	/* Same thing for auxiliary seqid lock... */
+	if ((cp->flags & CA_AUXILIARY) && !(cp->flags & CA_AUX_SEQID_LOCKED)) {
+		NFS4_ASSERT(fp != NULL);
+		NFS4_ASSERT(IS_LOCK_STATEID(fp));
+		
+		if (!RPC_IS_ASYNC(task))
+			down(&fp->fi_open_stateid->fi_lockowner->lo_sema);
+		else if (down_trylock(&fp->fi_open_stateid->fi_lockowner->lo_sema))
+			goto sleep;
+		cp->flags |= CA_AUX_SEQID_LOCKED;
+	}
+
+	/*
+	 * Success; our request will now be XDR encoded and put on the wire.
+	 */
+	cp->timestamp = jiffies;
+	rpc_call_setup(task, &msg, 0);
+	return;
+
+sleep:
+	/*
+	 * TODO: Implement exponential backoff for semaphore contention?
+	 * Currently, we just use a fixed delay...
+	 */
+	rpc_delay(task, NFS4FS_SEMAPHORE_DELAY);
+	return;
+}
+
+/*
+ * This is the final step in our RPC state machine.
+ *
+ * It implements generic postprocessing for COMPOUNDs, such as releasing semaphores
+ * which may have been acquired in preprocess_compound(), adjusting seqids, updating
+ * the RENEW timeout, handling of special errors such as ERR_DELAY, etc.
+ */
+static void
+postprocess_compound(struct rpc_task *task)
+{
+	struct nfs4fs_compound *cp = (struct nfs4fs_compound *) task->tk_calldata;
+	struct nfs4fs_file_data *fp = (struct nfs4fs_file_data *)cp->stateid_holder;
+	struct nfs4fs_lockowner *lp = (struct nfs4fs_lockowner *)cp->seqid_holder;
+	struct nfs4fs_file_data *wfp;
+	struct nfs4fs_op *op;
+	unsigned long delay;
+	u32 nfserr;
+
+	NFS4_ASSERT(task == &cp->task);
+	
+	if (task->tk_status < 0)
+		return;
+	
+	nfserr = cp->toplevel_status;
+
+	/*
+	 * See if the seqid needs to be updated.  Also, if the caller wants us to release
+	 * the seqid semaphore, do so now...
+	 */
+	if (lp) {
+		NFS4_ASSERT(cp->flags & CA_SEQID_LOCKED);
+		if ((cp->resp_nops > cp->seqid_index) ||
+		    (cp->resp_nops == cp->seqid_index && seqid_mutating_err(nfserr)))
+			lp->lo_seqid++;
+		if (cp->flags & CA_SEQID_RELEASE) {
+			up(&lp->lo_sema);
+			cp->flags &= ~CA_SEQID_LOCKED;
+		}
+	}
+
+	/* Same thing for auxiliary seqid... */
+	if (cp->flags & CA_AUX_SEQID_MUTATE) {
+		NFS4_ASSERT(cp->flags & CA_AUX_SEQID_LOCKED);
+		NFS4_ASSERT(cp->flags & CA_AUX_STATEID_LOCKED);
+		NFS4_ASSERT(cp->stateid_holder != NULL);
+		NFS4_ASSERT(cp->stateid_holder->fi_open_stateid != NULL);
+			
+		wfp = cp->stateid_holder->fi_open_stateid;
+		if ((cp->resp_nops > cp->seqid_index) ||
+		    (cp->resp_nops == cp->seqid_index && seqid_mutating_err(nfserr)))
+			wfp->fi_lockowner->lo_seqid++;
+	}
+	if (cp->flags & CA_AUX_SEQID_LOCKED) {
+		NFS4_ASSERT(cp->stateid_holder != NULL);
+		NFS4_ASSERT(cp->stateid_holder->fi_open_stateid != NULL);
+		wfp = cp->stateid_holder->fi_open_stateid;
+		up(&wfp->fi_lockowner->lo_sema);
+		cp->flags &= ~CA_AUX_SEQID_LOCKED;
+	}
+
+	/* If the caller wants us to release the auxiliary stateid semaphore, do so now... */
+	if (cp->flags & CA_AUX_STATEID_LOCKED) {
+		NFS4_ASSERT(cp->stateid_holder != NULL);
+		NFS4_ASSERT(cp->stateid_holder->fi_open_stateid != NULL);
+		wfp = cp->stateid_holder->fi_open_stateid;
+		nfs4_up_read(&wfp->fi_sema);
+		cp->flags &= ~CA_AUX_STATEID_LOCKED;
+	}
+	
+	/* Same thing for the stateid... */
+	if (cp->flags & CA_STATEID_RELEASE) {
+		NFS4_ASSERT(cp->stateid_holder != NULL);
+		NFS4_ASSERT(cp->flags & CA_STATEID_LOCKED);
+		nfs4_up_read(&fp->fi_sema);
+		cp->flags &= ~CA_STATEID_LOCKED;
+	}
+
+	/*
+	 * Generic lease processing: If this operation contains a lease-renewing operation,
+	 * and it succeeded, update the RENEW time in the superblock.  Instead of the current
+	 * time, we use the time when the request was sent out.  (All we know is that the
+	 * lease was renewed sometime between then and now, and we have to assume the worst
+	 * case.)
+	 */
+	if (cp->renew_index && (!nfserr || cp->resp_nops > cp->renew_index)) {
+		/*
+		 * TODO: oops, looks like we are missing a lock to protect
+		 * NFS4_LAST_RENEWAL(sb)!
+		 */
+		if (NFS4_LAST_RENEWAL(cp->sb) < cp->timestamp)
+			NFS4_LAST_RENEWAL(cp->sb) = cp->timestamp;
+	}
+
+	/*
+	 * Generic handling of special errors (e.g. NFS4ERR_DELAY).
+	 */
+	switch (nfserr) {
+	case NFS4_OK:
+	default:
+		break;
+	case NFS4ERR_DELAY:
+		nfsv4_printk(level1, "nfs4: got NFS4ERR_DELAY; retrying...\n");
+		delay = cp->retry_delay;
+		cp->retry_delay <<= 1;
+		if (cp->retry_delay > NFS4FS_RETRY_MAX_DELAY)
+			cp->retry_delay = NFS4FS_RETRY_MAX_DELAY;
+		goto delay;
+
+	case NFS4ERR_GRACE:
+		/*
+		 * TODO: Implement recovery from server reboots!
+		 */
+		nfsv4_printk(level1, "nfs4: got NFS4ERR_GRACE; retrying...\n");
+		delay = NFS4FS_GRACE_DELAY;
+		goto delay;
+
+#if 0
+	case NFS4ERR_STALE_CLIENTID:
+	case NFS4ERR_STALE_STATEID:
+		/* When reboot recovery is implemented... */
+		break;
+#endif
+		
+#if NFS4FS_RETRY_OLD_STATEID
+	case NFS4ERR_OLD_STATEID:
+		if (++cp->old_stateid_count >=5)   /* safeguard */
+			break;
+		nfsv4_printk(level1, "nfs4: got NFS4ERR_OLD_STATEID; retrying...\n");
+		goto retry;
+#endif
+		
+	case NFS4ERR_WRONGSEC:		
+	{
+	/*
+	* Spawn a child task to negotiate rpc_clnt auth handle
+	*/
+
+printk("ANDROS: calling ALLOC before spawn_secinfo\n");
+
+	if((op = NFS4_ALLOC(sizeof(struct nfs4fs_op))) == NULL)
+		return; /* XXX is this right? */
+	memset(op, 0, sizeof(struct nfs4fs_op));
+	/* XXX any other op fields needed? */
+
+	/* XXX need to free the op...*/
+	op->nfserr = spawn_secinfo(cp);
+	if(!op->nfserr)
+		goto wait_for_child;
+	break;
+	}
+	}
+
+	if (cp->confirm_index) {
+		/*
+		 * Spawn a child task to take care of any needed OPEN_CONFIRM.
+		 */
+		op = &cp->ops[cp->confirm_index];
+		
+		NFS4_ASSERT(op->opnum == OP_OPEN);
+		op->nfserr = spawn_open_confirm(cp);
+		if (!op->nfserr)
+			goto wait_for_child;
+	}
+
+	/* Normal termination. */
+	if (cp->exit)
+		cp->exit(cp);
+	return;
+	
+delay:
+	NFS4_ASSERT(delay > 0);
+	nfsv4_printk(level1, "nfs4: request will be retried in %ld %ld/%d seconds\n",
+		     delay / HZ, delay % HZ, HZ);
+	task->tk_action = preprocess_compound;
+	xprt_release(task);
+	rpc_delay(task, delay);
+	return;
+	
+retry:
+	xprt_release(task);     /* XXX: Do we need this?  Maybe we should put it at the top*/
+	task->tk_action = preprocess_compound;
+	return;
+
+wait_for_child:
+	NFS4_ASSERT(!(cp->flags & CA_SEQID_LOCKED));
+	NFS4_ASSERT(!(cp->flags & CA_STATEID_LOCKED));
+	
+	op->nfserr = NFS4ERR_IO;
+	cp->child_op = op;
+	task->tk_timeout = 30 * HZ;
+	task->tk_action = postprocess_child;
+	task->tk_exit = NULL;
+	xprt_release(task);
+	rpc_sleep_on(&parent_queue, task, NULL, NULL);
+	return;
+}
+
+/*
+ * If we went to sleep waiting for an asynchronous OPEN_CONFIRM to finish,
+ * we will wake up here...
+ */
+static void
+postprocess_child(struct rpc_task *task)
+{
+	struct nfs4fs_compound *cp = (struct nfs4fs_compound *) task->tk_calldata;
+
+	NFS4_ASSERT(task == &cp->task);
+	NFS4_ASSERT(cp->child_op != NULL);
+
+	task->tk_action = NULL;
+	if (task->tk_status < 0)
+		return;
+	
+	if (cp->child_op->nfserr == NFS4ERR_RETRY_COMPOUND) {
+		struct rpc_message msg = { NFSPROC4_COMPOUND, cp, cp, NULL };
+		rpc_call_setup(task, &msg, 0);
+		task->tk_exit = postprocess_compound;
+		return;
+	}
+
+	if (cp->exit)
+		cp->exit(cp);
+}
+
+/*
+ * This is the externally-visible entry point for sending a synchronous
+ * COMPOUND to the server.
+ */
+int
+nfs4fs_call_compound(struct nfs4fs_compound *cp)
+{
+	struct rpc_task *task = &cp->task;
+
+#if 0
+	nfsv4_printk(level2, "compound %p: queued (sync)!\n", task);
+#endif
+	rpc_init_task(task, NFS4_CLNT(cp->sb), postprocess_compound, 0);
+	task->tk_calldata = cp;
+	task->tk_action = preprocess_compound;
+	return rpc_execute(task);
+}
+
+/*
+ * This is the externally-visible entry point for initiating an
+ * asynchronous COMPOUND.
+ */
+int
+nfs4fs_call_async(struct nfs4fs_compound *cp)
+{
+	struct rpc_task *task = &cp->task;
+
+#if 0
+	nfsv4_printk(level2, "compound %p: queued (async)!\n", task);
+#endif
+	rpc_init_task(task, NFS4_CLNT(cp->sb), postprocess_compound, RPC_TASK_ASYNC);
+	task->tk_calldata = cp;
+	task->tk_action = preprocess_compound;
+	task->tk_release = free_async;
+	return rpc_execute(task);
+}
+
+/*
+ * This externally-visible routine is used in situations where we create an
+ * async COMPOUND, but don't want to execute it right away.  It sets things
+ * up so that the task goes to sleep on a queue, and will send the COMPOUND
+ * once it wakes up.
+ */
+void
+nfs4fs_sleep_async(struct rpc_wait_queue *q, struct nfs4fs_compound *cp)
+{
+	struct rpc_task *task = &cp->task;
+
+	rpc_init_task(task, NFS4_CLNT(cp->sb), postprocess_compound, RPC_TASK_ASYNC);
+	task->tk_calldata = cp;
+	task->tk_action = preprocess_compound;
+	task->tk_release = free_async;
+	rpc_sleep_on(q, task, NULL, NULL);
+}
+
+/*
+ * This releases all resources associated with an nfs4fs_compound: semaphores,
+ * inode refcounts, page refcounts, etc.
+ *
+ * Exception: For async COMPOUNDs, which are dynamically allocated, the memory
+ * is freed in free_async(), not here.
+ *
+ * For asynchronous COMPOUND's, this is called automatically from free_async(),
+ * which is the ->tk_release for such tasks.
+ * For synchronous COMPOUND's, it must be called explicitly.  (Otherwise, we
+ * would have to do all of the COMPOUND postprocessing in ->tk_action, which
+ * seemed inconvenient to me.  For async operations, doing this is unavoidable..)
+ */
+void
+nfs4fs_release_compound(struct nfs4fs_compound *cp)
+{
+	struct nfs4fs_commit *commit;
+	struct nfs4fs_getfh *getfh;
+	struct nfs4fs_read *read;
+	struct nfs4fs_readdir *readdir;
+	struct nfs4fs_write *write;
+	struct nfs4fs_secinfo *secinfo;
+	struct list_head *head;
+	struct nfs4_page *req;
+	struct nfs4fs_file_data *fp;
+	int i, j;
+
+	release_semaphores(cp);
+
+	for (i = 0; i < cp->req_nops; i++) {
+		switch (cp->ops[i].opnum) {
+		case OP_CLOSE:
+			NFS4_ASSERT(cp->stateid_holder != NULL);
+			fp = cp->stateid_holder;
+			iput(fp->fi_inode);
+			nfs4fs_put_lockowner(fp->fi_lockowner);
+			cp->stateid_holder = NULL;	/* paranoid */
+			fp->fi_inode = NULL;		/* paranoid */
+			fp->fi_lockowner = NULL;	/* paranoid */
+			NFS4_FREE(fp);
+			break;
+			
+		case OP_COMMIT:
+			commit = &cp->ops[i].u.commit;
+			head = &commit->co_pages;
+			while (!list_empty(head)) {
+				req = list_entry(head->next, struct nfs4_page, wb_list);
+				list_del_init(&req->wb_list);
+				nfs4_inode_remove_request(req);
+				nfs4_unlock_request(req);
+			}
+			break;
+
+		case OP_GETFH:
+			getfh = &cp->ops[i].u.getfh;
+			if (getfh->gf_fhval != getfh->gf_inline_buf) {
+				NFS4_FREE(getfh->gf_fhval);
+				getfh->gf_fhval = 0;
+			}
+			break;
+
+		case OP_READ:
+			/* XXX: do we need the BKL here? */
+			read = &cp->ops[i].u.read;
+			head = &read->rd_pages;
+			while (!list_empty(head)) {
+				req = list_entry(head->next, struct nfs4_page, wb_list);
+				list_del_init(&req->wb_list);
+				SetPageError(req->wb_page);
+				flush_dcache_page(req->wb_page);
+				UnlockPage(req->wb_page);
+				nfs4_kunmap_request(req);
+				nfs4_unlock_request(req);
+				nfs4_release_request(req);
+			}
+			break;
+
+		case OP_READDIR:
+			readdir = &cp->ops[i].u.readdir;
+			for (j = 0; j < readdir->rd_npages; j++) {
+				kunmap(readdir->rd_pages[j]);
+				UnlockPage(readdir->rd_pages[j]);
+				put_page(readdir->rd_pages[j]);
+			}
+			readdir->rd_npages = 0;
+			break;
+
+		case OP_WRITE:
+			write = &cp->ops[i].u.write;
+			head = &write->wr_pages;
+			while (!list_empty(head)) {
+				req = list_entry(head->next, struct nfs4_page, wb_list);
+				list_del_init(&req->wb_list);
+				ClearPageUptodate(req->wb_page);
+				SetPageError(req->wb_page);
+				nfs4_inode_remove_request(req);
+				nfs4_kunmap_request(req);
+				nfs4_unlock_request(req);
+			}
+			break;
+
+		case OP_SECINFO:
+			secinfo = &cp->ops[i].u.secinfo;
+			for (j = 0; j < secinfo->num_flavor; j++) {
+				if (secinfo->secflavor[j].flavor == RPC_AUTH_GSS) {
+					NFS4_FREE(secinfo->secflavor[j].f_info->oid_data);
+					NFS4_FREE(secinfo->secflavor[j].f_info);
+				}
+			}
+			if(secinfo->num_flavor > 0)
+				NFS4_FREE(secinfo->secflavor);
+			break;
+		}
+	}
+}
+
+/*
+ * This lives in the ->tk_release field of our async COMPOUNDs.
+ */
+static void
+free_async(struct rpc_task *task)
+{
+	struct nfs4fs_compound *cp = (struct nfs4fs_compound *) task->tk_calldata;
+
+	NFS4_ASSERT(&cp->task == task);
+	NFS4_ASSERT(RPC_IS_ASYNC(task));
+	
+	if (cp->release)
+		cp->release(cp);
+	if (cp->parent) {
+		rpc_wake_up_task(&cp->parent->task);
+		cp->parent = NULL;
+	}
+	nfs4fs_release_compound(cp);
+	NFS4_FREE(cp);
+}
+
+/*
+ * TODO: The current mechanism for OPEN_CONFIRM is ugly and I would like to see it done
+ * better.  For one thing, it means that every OPEN has to be followed by GETATTR GETFH
+ * to work.  This hurts us in the "undelegation" path because these secondary operations
+ * are unnecessary.  I am open to suggestions for improvement here...
+ *
+ * I just noticed this during pre-release auditing but don't have time to fix it yet:
+ * The OPEN_CONFIRM mechanism is more than just ugly, it's actually broken!  The problem
+ * is that nfs4fs_handle_open() assumes that the stateid semaphore is still held.  As a
+ * by-product of the way semaphores are handled in OPEN_CONFIRM, this will not be true
+ * if an OPEN_CONFIRM was done in the OPEN!
+ *
+ * (By the way, nfs4fs_handle_open() only needs to hold the stateid semaphore in the
+ * case of an "undelegating" OPEN, so there is only a race condition here in the presence
+ * of delegations.)
+ */
+static int
+spawn_open_confirm(struct nfs4fs_compound *cp)
+{
+	struct nfs4fs_getfh *getfh;
+	struct nfs4fs_compound *child;
+	int nfserr;
+	
+	NFS4_ASSERT(cp->confirm_index != 0);
+	NFS4_ASSERT(cp->ops[cp->confirm_index].opnum == OP_OPEN);
+	NFS4_ASSERT(cp->ops[cp->confirm_index + 2].opnum == OP_GETFH);
+
+	/*
+	 * This is a little ugly.  We have to dig the filehandle out of the
+	 * GETFH response to replay to the server.
+	 */
+	if (cp->toplevel_status && cp->resp_nops <= cp->confirm_index + 3)
+		goto out_getfh_failed;
+	getfh = &cp->ops[cp->confirm_index + 2].u.getfh;
+	
+	if (nfs4fs_setup_async(&child, cp->sb, "open_confirm", cp, open_confirm_handler, NULL))
+		goto out_nomem;
+	nfs4fs_setup_putfh_simple(child, getfh->gf_fhval, getfh->gf_fhlen);
+	nfs4fs_setup_open_confirm(child, cp);
+
+	if (nfs4fs_call_async(child))
+		goto out_nomem;   /* XXX: need nfserrno() */
+	
+	nfserr = NFS4_OK;
+	
+out:
+	return nfserr;
+out_getfh_failed:
+	nfsv4_printk(level1, "GETFH failed on OPEN!\n");
+	nfserr = cp->toplevel_status;
+	goto out;
+out_nomem:
+	nfserr = NFS4ERR_RESOURCE;
+	goto out;
+}
+
+static void
+open_confirm_handler(struct nfs4fs_compound *cp)
+{
+	NFS4_ASSERT(cp->task.tk_status >= 0);
+	NFS4_ASSERT(cp->parent != NULL);
+	
+	if (cp->toplevel_status) {
+		cp->parent->child_op->nfserr = cp->toplevel_status;
+		return;
+	}
+	
+	if (nfs4fs_handle_putfh(cp))
+		return;
+	if (nfs4fs_handle_open_confirm(cp))
+		return;
+	cp->parent->child_op->nfserr = NFS4_OK;
+}
+
+static int
+spawn_secinfo(struct nfs4fs_compound *cp)
+{
+	struct nfs4fs_compound *child;
+	struct dentry *parent = NULL;
+	int nfserr;
+
+	nfsv4_printk(level1,"ANDROS: spawn_secinfo:\n");
+	if(!cp){
+		printk("ANDROS: spawn_secinfo cp is NULL\n");
+		goto out_nomem;
+	}
+	if(!cp->secinfo_dentry){
+		printk("ANDROS: spawn_secinfo cp->secinfo_dentry is NULL\n");
+		goto out_nomem;
+	}
+	if(!cp->sb->u.nfs4_sb.client){
+		printk("ANDROS: spawn_secinfo cp->sb->u.nfs4_sb.client is NULL\n");
+		goto out_nomem;
+	}
+	if(!cp->sb->u.nfs4_sb.client->cl_auth){
+		printk("ANDROS: spawn_secinfo cp->sb->u.nfs4_sb.client->cl_auth is NULL\n");
+		goto out_nomem;
+	}
+	parent = cp->secinfo_dentry->d_parent;
+	NFS4_ASSERT(parent->d_inode);
+
+#if 0
+	NFS4_ASSERT(cp != NULL);
+	NFS4_ASSERT(cp->secinfo_dentry != NULL);
+	NFS4_ASSERT(cp->sb->u.nfs4_sb.client != NULL);
+	NFS4_ASSERT(cp->sb->u.nfs4_sb.client->cl_auth != NULL);
+#endif /*0*/
+
+	printk("ANDROS: spawn_secinfo 1\n");
+	/* save the old auth handle, and replace with AUTH_GSS
+	* the spawned rpc and the origional rpc share the same rpc_clnt struct...*/
+	cp->secinfo_auth = cp->sb->u.nfs4_sb.client->cl_auth;
+
+#if 0
+	if (nfs4fs_setup_async(&child, cp->sb, "secinfo", cp, secinfo_handler, secinfo_release))
+#endif
+
+	if (nfs4fs_setup_async(&child, cp->sb, "secinfo", cp, secinfo_handler, NULL))
+		goto out_nomem;
+
+	printk("ANDROS: spawn_secinfo 2\n");
+
+	nfs4fs_setup_putfh(child, parent->d_inode);
+
+	printk("ANDROS: spawn_secinfo 3\n");
+	nfs4fs_setup_secinfo(child, cp->secinfo_dentry);
+	printk("ANDROS: spawn_secinfo 4\n");
+
+	nfsv4_printk(level1,"spawn_secinfo: calling nfs4fs_call_async\n");
+	if ((nfserr = nfs4fs_call_async(child)) !=NFS4_OK ){
+		nfsv4_printk(level1,"spawn_secinfo: nfs4fs_call_async returns %d\n",nfserr);
+		goto out_nomem;   /* XXX: need nfserrno() */
+	}
+	nfserr = NFS4_OK;
+
+	printk("ANDROS: spawn_secinfo 5\n");
+out:
+	nfsv4_printk(level1,"spawn_secinfo: returning %d\n",nfserr);
+	return nfserr;
+out_nomem:
+	printk("ANDROS: spawn_secinfo 6\n");
+	nfserr = NFS4ERR_RESOURCE;
+goto out;
+}
+
+static void
+secinfo_handler(struct nfs4fs_compound *cp)
+{
+	printk("ANDROS: secinfo_handler\n");
+	NFS4_ASSERT(cp->parent != NULL);
+	NFS4_ASSERT(cp->task.tk_status >= 0);
+
+	printk("ANDROS: secinfo_handler 1\n");
+	if (cp->toplevel_status) {
+		cp->parent->child_op->nfserr = cp->toplevel_status;
+		goto out_auth;
+	}
+
+	printk("ANDROS: secinfo_handler 2\n");
+	if (nfs4fs_handle_putfh(cp))
+		goto out_auth;
+	printk("ANDROS: secinfo_handler 3\n");
+	if (nfs4fs_handle_secinfo(cp))
+		goto out_auth;
+
+	printk("ANDROS: secinfo_handler 4\n");
+	cp->parent->child_op->nfserr = NFS4_OK;
+
+	printk("ANDROS: secinfo_handler 5\n");
+	return;
+out_auth:
+	/* call failed, restore rpc_clnt auth handle */
+	cp->sb->u.nfs4_sb.client->cl_auth = cp->secinfo_auth;
+	nfsv4_printk(level1,"secinfo_handler: call failed, restore auth handle\n");
+	return;
+}
+
+#if 0   /* XXX */
+static void
+secinfo_release(struct nfs4fs_compound *cp)
+{
+	nfsv4_printk(level1,"secinfo_release\n");
+	NFS4_ASSERT(cp->parent != NULL);
+	NFS4_ASSERT(cp->parent->child_op != NULL);
+
+	NFS4_FREE(cp->parent->child_op);
+
+	printk("ANDROS: secinfo_release returns after NFS4_FREE\n");
+	return;
+}
+#endif
+
+/*
+ * RPC procedure tables, etc.
+ */
+static int
+null_encode(struct rpc_rqst *req, u32 *p, void *dummy)
+{
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+static int
+null_decode(struct rpc_rqst *req, u32 *p, void *dummy)
+{
+	return 0;
+}
+
+static struct rpc_procinfo nfs4_procedures[] = {
+	{ "nfs4_null", (kxdrproc_t)null_encode, (kxdrproc_t)null_decode, 0, 0 },
+	{ "nfs4_compound", (kxdrproc_t)nfs4fs_encode_compound,
+	  (kxdrproc_t)nfs4fs_decode_compound, NFS4_BUFSIZE, 0 }
+};
+
+static struct rpc_version	 nfs_version4 = {
+	4,
+	2,
+	nfs4_procedures
+};
+
+static struct rpc_version *	nfs4_version[] = {
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	&nfs_version4
+};
+
+struct rpc_stat			nfs4_rpcstat = { &nfs4_program };
+
+struct rpc_program		nfs4_program = {
+	"nfs4",
+	NFS4_PROGRAM,
+	sizeof(nfs4_version) / sizeof(nfs4_version[0]),
+	nfs4_version,
+	&nfs4_rpcstat,
+};
--- clean/fs/nfs4fs/file.c	Thu Feb  7 18:04:30 2002
+++ dirty/fs/nfs4fs/file.c	Mon Feb  4 15:16:21 2002
@@ -0,0 +1,561 @@
+/*
+ * linux/fs/nfs4fs/file.c
+ *
+ * from linux/fs/nfs/file.c
+ *    Copyright (C) 1992  Rick Sladkey
+ *    Changes Copyright (C) 1994 by Florian La Roche
+ *
+ * NFSv4 implementation 
+ *    Kendrick Smith <kmsmith@umich.edu>
+ *    Andy Adamson   <andros@umich.edu>
+ *    Copyright (C) 2001 The Regents of the University of Michigan
+ */
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/file.h>
+#include <asm/uaccess.h>
+#include <linux/sunrpc/sched.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+
+#include <linux/nfs4/nfs4.h>
+#include <linux/nfs4/nfs4_debug.h>
+#include <linux/nfs4/nfs4fs_xdr.h>
+#include <linux/nfs4/nfs4fs.h>
+#include <linux/nfs4_mount.h>
+
+struct inode_operations nfs4_file_inode_operations = {
+	revalidate:	nfs4_revalidate,
+	setattr:	nfs4_setattr,
+#if NFS4ACL
+       	permission: nfs4_posix_acl_permission,
+       	get_posix_acl: nfs4_acl_get_inode,
+       	set_posix_acl: nfs4_acl_set_inode,
+       	get_ext_attr: nfs4_get_ext_attr,
+       	set_ext_attr: nfs4_set_ext_attr
+#endif
+};
+
+/*
+ * File operations.
+ */
+static ssize_t nfs4_read(struct file *filp, char *buf, size_t count, loff_t *ppos);
+static ssize_t nfs4_write(struct file *filp, const char *buf, size_t count, loff_t *ppos);
+static int nfs4_mmap(struct file *filp, struct vm_area_struct *vma);
+static int nfs4_flush(struct file *filp);
+static int nfs4_fsync(struct file *file, struct dentry *dentry, int datasync);
+
+struct file_operations nfs4_file_operations = {
+	read: nfs4_read,
+	write: nfs4_write,
+	mmap: nfs4_mmap,
+	open: nfs4_open,
+	flush: nfs4_flush,
+	release: nfs4_release,
+	fsync: nfs4_fsync,
+	lock: nfs4_lock
+};
+
+/*
+ * Address space operations.
+ */
+static int nfs4_sync_page(struct page *page);
+static int nfs4_prepare_write(struct file *, struct page *, unsigned int, unsigned int);
+static int nfs4_commit_write(struct file *, struct page *, unsigned int, unsigned int);
+
+struct address_space_operations nfs4_file_aops = {
+	readpage: nfs4_readpage,
+	writepage: nfs4_writepage,
+	sync_page: nfs4_sync_page,
+	prepare_write: nfs4_prepare_write,
+	commit_write: nfs4_commit_write
+};
+
+/*
+ * a_ops->sync_page(): "initiates the completion of any page readahead operations" --NFSv3
+ */
+static int
+nfs4_sync_page(struct page *page)
+{
+	struct address_space *mapping;
+	struct inode *inode;
+	int status;
+
+	/*
+	 * XXX: These checks are taken from nfsv3, but can they really happen?
+	 */
+	status = 0;
+	mapping = page->mapping;
+	if (!mapping) {
+		nfsv4_printk(level1, "nfs4_sync_page: mapping == NULL!\n");
+		goto out;
+	}
+	inode = mapping->host;
+	if (!inode) {
+		nfsv4_printk(level1, "nfs4_sync_page: inode == NULL!\n");
+		goto out;
+	}
+
+	nfsv4_printk(read, "nfs4_sync_page: calling pagein_inode\n");
+	status = nfs4_pagein_inode(inode, page->index, NFS4_RPAGES(inode->i_sb));
+	if (status > 0)
+		status = 0;
+
+out:
+	return status;
+}
+
+/*
+ * f_op->mmap().
+ */
+static int
+nfs4_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	struct dentry *dentry = filp->f_dentry;
+	struct inode *inode = dentry->d_inode;
+	struct nfs4fs_file_data *fdata = (struct nfs4fs_file_data *)filp->private_data;
+	int status;
+
+	NFS4_ASSERT(inode != NULL);
+	NFS4_ASSERT(S_ISREG(inode->i_mode));    /* XXX: is this done in the vfs? */
+	NFS4_ASSERT(fdata != NULL);
+	
+	nfsv4_printk(level2, "nfs4_mmap(): starting\n");
+	lock_kernel();
+
+	if ((status = nfs4_revalidate_inode(inode))) {
+		nfsv4_printk(level2, "nfs4_mmap: revalidate_inode failed!\n");
+		goto out;
+	}
+	
+	status = generic_file_mmap(filp, vma);
+
+out:
+	unlock_kernel();
+	nfsv4_printk(level2, "nfs4_mmap(): returning status %d\n\n", status);
+	return status;
+}
+
+/*
+ * a_ops->prepare_write(): called just before generic_file_write() copies data into the
+ * page from userspace.  page is already locked.  offset=starting offset within page,
+ * to=final offset within page.
+ */
+static int
+nfs4_prepare_write(struct file *filp, struct page *page, unsigned int offset, unsigned int to)
+{
+	nfsv4_printk(level2, "nfs4_prepare_write: %d:[%d,%d]\n", (int)page->index, offset, to);
+	kmap(page);
+	return nfs4_flush_incompatible(filp, page);
+}
+
+/*
+ * a_ops->commit_write(): called just after generic_file_write() copies data into the page
+ * from userspace.  we still have the page lock and kmap(); we will kunmap() after the write
+ * is finished, but keep the lock.  the return value from this function is the number of
+ * bytes written successfully, or a negative error status.  a return value of 0 is treated
+ * as a successful write of all the data, so be careful!
+ */
+static int
+nfs4_commit_write(struct file *filp, struct page *page, unsigned int offset, unsigned int to)
+{
+	loff_t pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to;
+	struct dentry *dentry = filp->f_dentry;
+	struct inode *inode = dentry->d_inode;
+	int status;
+
+	nfsv4_printk(level2, "nfs4_commit_write %d:[%d,%d]\n", (int)page->index, offset, to);
+	kunmap(page);
+	
+	lock_kernel();
+	status = nfs4_updatepage(filp, page, offset, to-offset);
+	/* XXX: not sure whether this is necessary; listed as a CHECKME in v2/v3 */
+	if (!status && pos > inode->i_size)
+		inode->i_size = pos;
+	unlock_kernel();
+	
+	return status;
+}
+
+/*
+ * f_op->read().
+ */
+static ssize_t
+nfs4_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
+{
+	unsigned long start_idx, end_idx;
+	ssize_t result;
+
+	nfsv4_printk(level2, "nfs4_read(%.*s/%.*s): starting, reading %Ld-%Ld\n",
+		     (int)filp->f_dentry->d_parent->d_name.len,
+		     filp->f_dentry->d_parent->d_name.name,
+		     (int)filp->f_dentry->d_name.len, filp->f_dentry->d_name.name,
+		     *ppos, *ppos + count);
+
+	if ((result = nfs4_revalidate_inode(filp->f_dentry->d_inode))) {
+		nfsv4_printk(read2, "nfs4_read: revalidate_inode() failed!\n");
+		goto out;
+	}
+
+	/*
+	 * TODO: Do we really need this?!  I seemed to think it was necessary
+	 * when I wrote this routine, but NFSv3 doesn't do it...
+	 */
+	start_idx = *ppos >> PAGE_CACHE_SHIFT;
+	end_idx = (*ppos + count) >> PAGE_CACHE_SHIFT;
+	result = nfs4_sync_file(filp->f_dentry->d_inode, NULL, 0, 0, FLUSH_WAIT|FLUSH_STABLE);
+	if (result) {
+		nfsv4_printk(read2, "nfs4_read: nfs4_sync_file failed!\n");
+		goto out;
+	}
+	
+	result = generic_file_read(filp, buf, count, ppos);
+
+out:
+	nfsv4_printk(level2, "nfs4_read: returning status %d\n", result);
+	return result;
+}
+
+/*
+ * f_op->write().
+ */
+static ssize_t
+nfs4_write(struct file *filp, const char *buf, size_t count, loff_t *ppos)
+{
+	ssize_t result = 0;
+
+	nfsv4_printk(level2, "nfs4_write(%.*s/%.*s): starting, writing %Ld-%Ld\n",
+		     (int)filp->f_dentry->d_parent->d_name.len,
+		     filp->f_dentry->d_parent->d_name.name,
+		     (int)filp->f_dentry->d_name.len, filp->f_dentry->d_name.name,
+		     *ppos, *ppos + count);
+	
+	if ((result = nfs4_revalidate_inode(filp->f_dentry->d_inode))) {
+		nfsv4_printk(write2, "nfs4_write: revalidate_inode failed!\n");
+		goto out;
+	}
+	if (!count) {
+		nfsv4_printk(write2, "nfs4_write: count == 0\n");
+		goto out;
+	}
+	result = generic_file_write(filp, buf, count, ppos);
+	
+out:
+	nfsv4_printk(level2, "nfs4_write: returning status %d\n\n", result);
+	return result;
+}
+
+/*
+ * f_op->flush(): called when a file descriptor is closed.  the return value here is the
+ * value that will be returned from the close() system call.  locks held: BKL.
+ */
+static int
+nfs4_flush(struct file *filp)
+{
+	struct nfs4fs_file_data *fp = (struct nfs4fs_file_data *)filp->private_data;
+	struct inode *inode = filp->f_dentry->d_inode;
+	int status;
+	
+	if (!fp)
+		return 0;
+	
+	/*
+	 * Make sure all async reads have been sent off. We don't bother
+	 * waiting on them though...
+	 */
+	if (filp->f_mode & FMODE_READ) {
+		nfsv4_printk(read, "nfs4_flush: calling nfs4_pagein_inode()\n");
+		nfs4_pagein_inode(inode, 0, 0);
+	}
+
+	status = nfs4_sync_file(inode, filp, 0, 0, FLUSH_WAIT);
+	if (!status) {
+		status = filp->f_error;
+		filp->f_error = 0;
+	}
+	
+	return status;
+}
+
+/*
+ * f_op->fsync(): called on fsync() or fdatasync().  last arg is 0 or 1, respectively, for
+ * these cases.  locks held: inode->i_sem.
+ */
+static int
+nfs4_fsync(struct file *file, struct dentry *dentry, int datasync)
+{
+	int status;
+
+	nfsv4_printk(level2, "nfs4_fsync(): starting on file \"%.*s/%.*s\"\n",
+		     (int)dentry->d_parent->d_name.len, dentry->d_parent->d_name.name,
+		     (int)dentry->d_name.len, dentry->d_name.name);
+
+	lock_kernel();
+	status = nfs4_sync_file(dentry->d_inode, file, 0, 0, FLUSH_WAIT|FLUSH_STABLE);
+	if (!status) {
+		status = file->f_error;
+		file->f_error = 0;
+	}
+	unlock_kernel();
+
+	nfsv4_printk(level2, "nfs4_fsync(): returning status %d\n", status);
+	return status;
+}
+
+/*
+ * The remainder of this file is devoted to "generic" (i.e., not specific to
+ * either the read or write case) helper routines for I/O.
+ */
+spinlock_t nfs4_wreq_lock = SPIN_LOCK_UNLOCKED;
+DECLARE_WAIT_QUEUE_HEAD(nfs4_wait);     /* wait here to allocate a request.. */
+static atomic_t nfs4_nr_requests = ATOMIC_INIT(0);
+	
+/*
+ * Caller is responsible for initializing wb_timeout, but this routine will
+ * initialize all other fields.
+ */
+struct nfs4_page *
+nfs4_create_request(struct file *file, struct nfs4fs_file_data *fdata,
+		    struct page *page, unsigned int offset, unsigned int count)
+{
+	struct nfs4_page *req = NULL;
+	unsigned long timeout;
+
+	NFS4_ASSERT(fdata != NULL);
+	NFS4_ASSERT(fdata->fi_inode != NULL);
+	NFS4_ASSERT(page != NULL);
+
+	/* Deal with hard/soft limits. */
+	do {
+		/* If we're over the global soft limit, wake up all requests */
+		if (atomic_read(&nfs4_nr_requests) >= NFS4_MAX_REQUEST_SOFT) {
+			nfsv4_printk(async, "ASYNC: hit soft limit (%d requests)\n",
+			       atomic_read(&nfs4_nr_requests));
+			nfs4_wake_flushd();
+		}
+
+		/* If we haven't reached the local hard limit yet,
+		 * try to allocate the request struct */
+		if (atomic_read(&nfs4_nr_requests) < NFS4_MAX_REQUEST_HARD) {
+			req = NFS4_ALLOC(sizeof(*req));
+			if (req != NULL)
+				break;
+		}
+
+		/* We're over the hard limit. Wait for better times */
+		nfsv4_printk(async, "ASYNC: hit hard limit (%d requests)\n",
+		       atomic_read(&nfs4_nr_requests));
+		
+		timeout = 1 * HZ;
+		if (NFS4_MOUNT_FLAGS(fdata->fi_inode->i_sb) & NFS4_MOUNT_INTR) {
+			interruptible_sleep_on_timeout(&nfs4_wait, timeout);
+			
+			if (signalled())
+				break;
+		}
+		else
+			sleep_on_timeout(&nfs4_wait, timeout);
+		
+	} while (!req);
+
+	if (req) {
+		nfsv4_printk(async, "ASYNC: allocating page\n");
+		atomic_inc(&nfs4_nr_requests);
+		atomic_inc(&fdata->fi_count);
+		page_cache_get(page);
+		if (file)
+			get_file(file);
+			
+		INIT_LIST_HEAD(&req->wb_list);
+		INIT_LIST_HEAD(&req->wb_writelist);
+		init_waitqueue_head(&req->wb_wait);
+		req->wb_flags = 0;
+		req->wb_file = file;
+		req->wb_fdata = fdata;
+		req->wb_page = page;
+		req->wb_inode = fdata->fi_inode;
+		req->wb_offset = offset;
+		req->wb_bytes = count;
+		req->wb_count = 1;
+		req->wb_buf = NULL;
+		req->wb_safeguard = 0;
+	}
+	return req;
+}
+
+void
+_nfs4_release_request(struct nfs4_page *req)
+{
+	NFS4_ASSERT(list_empty(&req->wb_list));
+	NFS4_ASSERT(list_empty(&req->wb_writelist));
+	NFS4_ASSERT(!test_bit(PG_BUSY, &req->wb_flags));
+	NFS4_ASSERT(req->wb_buf == NULL);
+	
+	nfsv4_printk(async, "ASYNC: freeing page\n");
+	if (req->wb_file)
+		fput(req->wb_file);
+	page_cache_release(req->wb_page);
+	nfs4fs_put_file_data(req->wb_fdata);
+	atomic_dec(&nfs4_nr_requests);	
+	NFS4_FREE(req);
+	wake_up(&nfs4_wait);
+}
+
+void
+nfs4_list_add_request(struct nfs4_page *req, struct list_head *head)
+{
+	struct list_head *prev;
+
+	/* caller must hold the wreq spinlock */
+
+	NFS4_ASSERT(list_empty(&req->wb_list));
+	
+	prev = head->prev;
+	while (prev != head) {
+		struct nfs4_page *p = list_entry(prev, struct nfs4_page, wb_list);
+		if (p->wb_page->index < req->wb_page->index)
+			break;
+		prev = prev->prev;
+	}
+	list_add(&req->wb_list, prev);
+}
+
+void
+nfs4_list_remove_request(struct nfs4_page *req)
+{
+	/* caller must hold the wreq spinlock */
+	
+	NFS4_ASSERT(!list_empty(&req->wb_list));
+	NFS4_ASSERT(test_bit(PG_BUSY, &req->wb_flags));
+	
+	list_del_init(&req->wb_list);
+}
+
+int
+nfs4_scan_list(struct list_head *src, struct list_head *dst,
+	       struct file *file, unsigned long idx_start, unsigned int npages)
+{
+	struct list_head	*p;
+	struct nfs4_page	*req;
+	unsigned long		idx_end;
+	int			res;
+
+	/* caller must hold the wreq spinlock */
+
+	NFS4_ASSERT(list_empty(dst));
+	
+	res = 0;
+	if (npages == 0)
+		idx_end = ~0;
+	else
+		idx_end = idx_start + npages - 1;
+	
+	p = src->next;
+	while (p != src) {
+		unsigned long pg_idx;
+
+		req = list_entry(p, struct nfs4_page, wb_list);
+		p = p->next;
+
+		if (file && req->wb_file != file)
+			continue;
+
+		pg_idx = req->wb_page->index;
+		if (pg_idx < idx_start || pg_idx > idx_end)
+			continue;
+		if (!nfs4_lock_request(req))
+			continue;
+
+		/*
+		 * We put the page at the tail of the dst list.  This keeps the list in
+		 * sorted order because the dst list is assumed to be initially empty
+		 * (hence the assertion at the top).
+		 */
+		list_del(&req->wb_list);
+		list_add_tail(&req->wb_list, dst);
+		res++;
+	}
+	nfsv4_printk(async, "ASYNC: scan_list() grabbed list of %d pages\n", res);
+	return res;
+}
+
+int
+nfs4_scan_list_timeout(struct list_head *head, struct list_head *dst, struct inode *inode)
+{
+	struct list_head	*p;
+        struct nfs4_page	*req;
+        int			pages = 0;
+	unsigned long		now = jiffies;
+
+	/* caller must hold wreq spinlock */
+
+	NFS4_ASSERT(list_empty(dst));
+	
+	p = head->next;
+        while (p != head) {
+		req = list_entry(p, struct nfs4_page, wb_list);
+		p = p->next;
+		
+		if (time_after(req->wb_timeout, now)) {
+			if (time_after(NFS4_FLUSHD_NEXTSCAN(inode), req->wb_timeout))
+				NFS4_FLUSHD_NEXTSCAN(inode) = req->wb_timeout;
+			continue;
+		}
+		if (!nfs4_lock_request(req))
+			continue;
+		
+		/*
+		 * We put the page at the tail of the dst list.  This keeps the list in
+		 * sorted order because the dst list is assumed to be initially empty
+		 * (hence the assertion at the top).
+		 */
+		list_del(&req->wb_list);
+		list_add_tail(&req->wb_list, dst);
+		pages++;
+	}
+	nfsv4_printk(async, "ASYNC: scan_list_timeout() grabbed list of %d pages\n", pages);
+	return pages;
+}
+
+int
+nfs4_coalesce_requests(struct list_head *src, struct list_head *dst, unsigned int maxpages)
+{
+	struct nfs4_page	*prev;
+	struct nfs4_page	*req = NULL;
+	unsigned int		pages = 0;
+
+	NFS4_ASSERT(!list_empty(src));
+	NFS4_ASSERT(list_empty(dst));
+
+	while (!list_empty(src)) {
+		prev = req;
+		req = list_entry(src->next, struct nfs4_page, wb_list);
+
+		if (prev) {
+			if (req->wb_file != prev->wb_file)
+				break;
+			if (req->wb_fdata != prev->wb_fdata)
+				break;
+			if (req->wb_page->index != prev->wb_page->index + 1)
+				break;
+			if (req->wb_offset != 0)
+				break;
+		}
+		
+		NFS4_ASSERT(test_bit(PG_BUSY, &req->wb_flags));
+
+		list_del_init(&req->wb_list);
+		list_add_tail(&req->wb_list, dst);
+		pages++;
+
+		if (req->wb_offset + req->wb_bytes != PAGE_CACHE_SIZE)
+			break;
+		if (pages >= maxpages)
+			break;
+	}
+
+	nfsv4_printk(async, "ASYNC: coalesced one %d-page read request\n", pages);
+	return pages;
+}
--- clean/fs/nfs4fs/lock.c	Thu Feb  7 18:04:30 2002
+++ dirty/fs/nfs4fs/lock.c	Mon Feb  4 15:16:22 2002
@@ -0,0 +1,1173 @@
+/*
+ *  linux/fs/nfs4fs/lock.c
+ *
+ *  Copyright (c) 2001 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  Kendrick Smith <kmsmith@umich.edu>
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the University nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This file implements POSIX byte-range file locking over NFSv4.
+ *
+ * The Big Problem is, according to the NFSv4 spec, servers are not required to
+ * support subranges in locking requests.  If a server does not support them, a
+ * Unix client is supposed to "factor" its subrange requests into an equivalent
+ * sequence of "strict" (i.e., non-subrange) requests.  As you might expect, this
+ * renders the whole locking subsystem prone to race conditions with other clients
+ * (largely defeating the whole purpose of locking).  This is such a dangerous
+ * solution that we don't want to give it to the user unless they ask for it
+ * specifically.  Therefore, by default, we assume that the server can handle
+ * subrange requests, and don't try to factor.  If the NFSv4 filesystem is
+ * mounted '-o strictlock', we do factor.
+ *
+ * Executive summary:
+ *   default              server supports subranges => everything works.
+ *                        server does not => simple locking requests work.
+ *                                           more complex ones fail outright (-EIO).
+ *
+ *   -o strictlock        for any server => simple locking requests work.
+ *                                          more complex ones "work" most of the time but
+ *                                          are subject to inherent race conditions which
+ *                                          sometimes cause the locks to have no effect!
+ *
+ * In order to implement 'strict locking', I had to make one minor VFS change.
+ * In the stock kernel, upon successful return from the ->lock() operation, the VFS
+ * proceeds to insert the new lock into the inode's lock list, coalescing
+ * locks in the process.  However, for strict locking to work, it is critical that
+ * locks not be coalesced: we need to remember the individual pieces.  My solution
+ * was to define a special return value from ->lock(), LOCK_DONE, which indicates
+ * to the VFS that the filesystem has already done all the work of modifying the
+ * inode's lock list.
+ *
+ * Warning!  In my pre-release code audit, I didn't have time to audit this file, which
+ * is particularly large and complex, due to the difficulty of solving the "lock factoring"
+ * problem in general.  Therefore, it has not been subjected to the level of code cleanup
+ * of the other client-side files, and the documentation is quite weak.  Nonetheless I
+ * believe that it should be possible to follow the logic in this file.  There may be also
+ * be a greater density of bugs here than in other places.  Caveat emptor.  If someone
+ * is willing to do code cleanup / auditing before I get around to it, I would be quite
+ * happy to receive patches here...
+ */
+
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/sunrpc/sched.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+
+#include <linux/nfs4/nfs4.h>
+#include <linux/nfs4/nfs4_debug.h>
+#include <linux/nfs4/nfs4fs_xdr.h>
+#include <linux/nfs4/nfs4fs.h>
+
+static int nfs4_lock_strict(struct file *filp, struct file_lock *lock, int is_blocking);
+
+/* nfs4_testlock(): If a conflicting lock exists, write data for the conflicting lock into
+   the file_lock structure.  If no such lock exists, set the 'fl_type' field to FL_UNLCK. */
+static int
+nfs4_testlock(struct file *filp, struct file_lock *file_lock)
+{
+	struct dentry *dentry = filp->f_dentry;
+	struct nfs4fs_lockowner *lp;
+	struct nfs4fs_compound compound;
+	int status;
+
+	nfsv4_printk(lock, "nfs4_testlock: testing byterange %Ld-%Ld\n",
+		     file_lock->fl_start, file_lock->fl_end);
+	
+#if NFS4_DEBUG
+	if ((file_lock->fl_type != F_RDLCK) && (file_lock->fl_type != F_WRLCK)) {
+		printk("!!! unrecognized lock type in nfs4_testlock !!!\n");
+		file_lock->fl_type = F_UNLCK;
+		return 0;
+	}
+#endif
+
+	status = -ENOMEM;
+	if (!(lp = nfs4fs_get_lock_owner(dentry->d_inode)))
+		goto out_no_lp;
+	
+	nfs4fs_setup_compound(&compound, dentry->d_sb, "nfs4_testlock()");
+	nfs4fs_setup_putfh(&compound, dentry->d_inode);
+	nfs4fs_setup_getattr(&compound, nfs4_standard_bitmap);
+	nfs4fs_setup_lockt(&compound, file_lock, lp);
+
+	if ((status = nfs4fs_call_compound(&compound)))
+		goto out_release;
+	if ((status = nfs4fs_handle_putfh(&compound)))
+		goto out_release;
+	if ((status = nfs4fs_handle_getattr_inode(&compound, dentry->d_inode)))
+		goto out