Whamcloud - gitweb
land b_v26 (20040316_1603)
authornic <nic>
Wed, 17 Mar 2004 01:04:43 +0000 (01:04 +0000)
committernic <nic>
Wed, 17 Mar 2004 01:04:43 +0000 (01:04 +0000)
41 files changed:
lnet/Rules.linux
lnet/archdep.m4
lnet/klnds/socklnd/socklnd.c
lnet/klnds/socklnd/socklnd.h
lnet/utils/debug.c
lustre/Rules
lustre/include/linux/lustre_compat25.h
lustre/kernel_patches/patches/export-2.6.3.patch [new file with mode: 0644]
lustre/kernel_patches/patches/fs-intent-2.6.3-mm4.patch [new file with mode: 0644]
lustre/kernel_patches/patches/iopen-2.6.3-mm4.patch [new file with mode: 0644]
lustre/kernel_patches/patches/linux-2.6.3-CITI_NFS4_ALL.patch [new file with mode: 0644]
lustre/kernel_patches/patches/linux-2.6.3-nfs-intent.patch [new file with mode: 0644]
lustre/kernel_patches/patches/nfs-cifs-intent-2.6.3-suse.patch [new file with mode: 0644]
lustre/kernel_patches/patches/uml-fix-2.6.3.patch [new file with mode: 0644]
lustre/kernel_patches/patches/uml-patch-2.6.3-rc2-1.patch [new file with mode: 0644]
lustre/kernel_patches/patches/vfs_intent-2.6.3-suse.patch [new file with mode: 0644]
lustre/kernel_patches/patches/vfs_nointent-2.6.3-mm4.patch [new file with mode: 0644]
lustre/kernel_patches/patches/vfs_nointent_2.6.3-suse.patch [new file with mode: 0644]
lustre/kernel_patches/series/2.6.3-mm4 [new file with mode: 0644]
lustre/kernel_patches/series/suse-2.6.3 [new file with mode: 0644]
lustre/kernel_patches/series/vanilla-2.6.3-nfs4 [new file with mode: 0644]
lustre/llite/Makefile.am
lustre/llite/llite_lib.c
lustre/llite/rw26.c
lustre/llite/super.c
lustre/llite/super25.c
lustre/lvfs/Makefile.am
lustre/lvfs/fsfilt_ext3.c
lustre/lvfs/lvfs_linux.c
lustre/mds/mds_fs.c
lustre/obdclass/Makefile.am
lustre/obdfilter/Makefile.am
lustre/obdfilter/filter_io_24.c
lustre/obdfilter/filter_io_26.c
lustre/ost/ost_handler.c
lustre/portals/Rules.linux
lustre/portals/archdep.m4
lustre/portals/knals/socknal/socknal.c
lustre/portals/knals/socknal/socknal.h
lustre/portals/utils/debug.c
lustre/scripts/merge1.sh

index 93943b7..232a248 100644 (file)
@@ -3,11 +3,15 @@
 
 if LINUX25
 
-basename=$(shell echo $< | sed -e 's/\.c//g' | sed -e 's/-//g' | sed -e 's/\.o//g')
+basename=$(shell echo $< | sed -e 's/\.c//g' | sed -e 's/-//g' | sed -e 's/\.o//g' | sed -e 's/^.*\///g')
 AM_CPPFLAGS= -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -pipe -mpreferred-stack-boundary=2  -DKBUILD_MODNAME=$(MODULE) -DKBUILD_BASENAME=$(basename)
 
-$(MODULE).o: $($(MODULE)_OBJECTS)
-       $(LD) -m $(MOD_LINK) -r -o $(MODULE).o $($(MODULE)_OBJECTS)
+$(MODULE).o: $($(MODULE)_OBJECTS) $($(MODULE)_DEPENDENCIES)
+       $(LD) -m $(MOD_LINK) -r -o $(MODULE)_tmp.o $($(MODULE)_OBJECTS)
+       rm -f $(MODULE)_tmp.c
+       $(LINUX)/scripts/modpost $(LINUX)/vmlinux $(MODULE)_tmp.o
+       $(COMPILE) -UKBUILD_BASENAME -DKBUILD_BASENAME=$(MODULE) -c $(MODULE)_tmp.mod.c
+       $(LD) -m $(MOD_LINK) -r -o $(MODULE).o $(MODULE)_tmp.o $(MODULE)_tmp.mod.o
 
 else
 
index 7801957..65cfaff 100644 (file)
@@ -65,7 +65,7 @@ case ${host_cpu} in
        KCFLAGS='-g -Wall -pipe -Wno-trigraphs -Wstrict-prototypes -fno-strict-aliasing -fno-common '
         case ${linux25} in
                 yes )
-                KCPPFLAGS='-D__KERNEL__ -U__i386__ -Ui386 -DUM_FASTCALL -D__arch_um__ -DSUBARCH="i386" -DNESTING=0 -D_LARGEFILE64_SOURCE  -Derrno=kernel_errno -DPATCHLEVEL=4 -DMODULE -I$(LINUX)/arch/um/include -I$(LINUX)/arch/um/kernel/tt/include -I$(LINUX)/arch/um/kernel/skas/include -O2 -nostdinc -iwithprefix include -DKBUILD_BASENAME=$(MODULE) -DKBUILD_MODNAME=$(MODULE) '
+                KCPPFLAGS='-D__KERNEL__ -U__i386__ -Ui386 -DUM_FASTCALL -D__arch_um__ -DSUBARCH="i386" -DNESTING=0 -D_LARGEFILE64_SOURCE  -Derrno=kernel_errno -DPATCHLEVEL=4 -DMODULE -I$(LINUX)/arch/um/include -I$(LINUX)/arch/um/kernel/tt/include -I$(LINUX)/arch/um/kernel/skas/include -O2 -nostdinc -iwithprefix include'
         ;;
                 * )
                 KCPPFLAGS='-D__KERNEL__ -U__i386__ -Ui386 -DUM_FASTCALL -D__arch_um__ -DSUBARCH="i386" -DNESTING=0 -D_LARGEFILE64_SOURCE  -Derrno=kernel_errno -DPATCHLEVEL=4 -DMODULE -I$(LINUX)/arch/um/kernel/tt/include -I$(LINUX)/arch/um/include '
@@ -206,11 +206,10 @@ if test $host_cpu != "lib" ; then
   AC_MSG_CHECKING(for MODVERSIONS)
   if egrep -e 'MODVERSIONS.*1' $LINUX/include/linux/autoconf.h >/dev/null 2>&1;
   then
-        MFLAGS="-DMODULE -DMODVERSIONS -include $LINUX/include/linux/modversions.h -DEXPORT_SYMTAB"
-        AC_MSG_RESULT(yes)
-  else
-        MFLAGS=
-        AC_MSG_RESULT(no)
+       if test $linux25 != "yes"; then
+                MFLAGS="-DMODULE -DMODVERSIONS -include $LINUX/include/linux/modversions.h -DEXPORT_SYMTAB"
+                AC_MSG_RESULT(yes)
+        fi
   fi
 fi
 
index 2c44b43..0dd5d11 100644 (file)
@@ -196,7 +196,7 @@ ksocknal_bind_irq (unsigned int irq)
         /* FIXME: Find a better method of setting IRQ affinity...
          */
 
-        call_usermodehelper (argv[0], argv, envp);
+        USERMODEHELPER(argv[0], argv, envp);
 #endif
 }
 
index db8c842..17a7e49 100644 (file)
@@ -60,6 +60,7 @@
 #define DEBUG_SUBSYSTEM S_SOCKNAL
 
 #include <linux/kp30.h>
+#include <linux/portals_compat25.h>
 #include <portals/p30.h>
 #include <portals/lib-p30.h>
 #include <portals/socknal.h>
index 01e690f..69880ea 100644 (file)
 #include <sys/stat.h>
 #include <sys/mman.h>
 
-#define BUG()                            /* workaround for module.h includes */
 #include <linux/version.h>
 
 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
+#define BUG()                            /* workaround for module.h includes */
 #include <linux/module.h>
 #endif
 
@@ -524,41 +524,43 @@ int jt_dbg_mark_debug_buf(int argc, char **argv)
         return 0;
 }
 
+static struct mod_paths {
+        char *name, *path;
+} mod_paths[] = {
+        {"portals", "lustre/portals/libcfs"},
+        {"ksocknal", "lustre/portals/knals/socknal"},
+        {"kptlrouter", "lustre/portals/router"},
+        {"lvfs", "lustre/lvfs"},
+        {"obdclass", "lustre/obdclass"},
+        {"llog_test", "lustre/obdclass"},
+        {"ptlrpc", "lustre/ptlrpc"},
+        {"obdext2", "lustre/obdext2"},
+        {"ost", "lustre/ost"},
+        {"osc", "lustre/osc"},
+        {"mds", "lustre/mds"},
+        {"mdc", "lustre/mdc"},
+        {"llite", "lustre/llite"},
+        {"obdecho", "lustre/obdecho"},
+        {"ldlm", "lustre/ldlm"},
+        {"obdfilter", "lustre/obdfilter"},
+        {"extN", "lustre/extN"},
+        {"lov", "lustre/lov"},
+        {"fsfilt_ext3", "lustre/lvfs"},
+        {"fsfilt_extN", "lustre/lvfs"},
+        {"fsfilt_reiserfs", "lustre/lvfs"},
+        {"mds_ext2", "lustre/mds"},
+        {"mds_ext3", "lustre/mds"},
+        {"mds_extN", "lustre/mds"},
+        {"ptlbd", "lustre/ptlbd"},
+        {"mgmt_svc", "lustre/mgmt"},
+        {"mgmt_cli", "lustre/mgmt"},
+        {NULL, NULL}
+};
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
 int jt_dbg_modules(int argc, char **argv)
 {
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
-        struct mod_paths {
-                char *name, *path;
-        } *mp, mod_paths[] = {
-                {"portals", "lustre/portals/libcfs"},
-                {"ksocknal", "lustre/portals/knals/socknal"},
-                {"kptlrouter", "lustre/portals/router"},
-                {"lvfs", "lustre/lvfs"},
-                {"obdclass", "lustre/obdclass"},
-                {"llog_test", "lustre/obdclass"},
-                {"ptlrpc", "lustre/ptlrpc"},
-                {"obdext2", "lustre/obdext2"},
-                {"ost", "lustre/ost"},
-                {"osc", "lustre/osc"},
-                {"mds", "lustre/mds"},
-                {"mdc", "lustre/mdc"},
-                {"llite", "lustre/llite"},
-                {"obdecho", "lustre/obdecho"},
-                {"ldlm", "lustre/ldlm"},
-                {"obdfilter", "lustre/obdfilter"},
-                {"extN", "lustre/extN"},
-                {"lov", "lustre/lov"},
-                {"fsfilt_ext3", "lustre/lvfs"},
-                {"fsfilt_extN", "lustre/lvfs"},
-                {"fsfilt_reiserfs", "lustre/lvfs"},
-                {"mds_ext2", "lustre/mds"},
-                {"mds_ext3", "lustre/mds"},
-                {"mds_extN", "lustre/mds"},
-                {"ptlbd", "lustre/ptlbd"},
-                {"mgmt_svc", "lustre/mgmt"},
-                {"mgmt_cli", "lustre/mgmt"},
-                {NULL, NULL}
-        };
+        struct mod_paths *mp;
         char *path = "..";
         char *kernel = "linux";
 
@@ -592,11 +594,49 @@ int jt_dbg_modules(int argc, char **argv)
         }
 
         return 0;
+}
 #else
-        printf("jt_dbg_module is not yet implemented for Linux 2.5\n");
+int jt_dbg_modules(int argc, char **argv)
+{
+        struct mod_paths *mp;
+        char *path = "..";
+        char *kernel = "linux";
+        const char *proc = "/proc/modules";
+        char modname[128], others[128];
+        long modaddr;
+        int rc;
+        FILE *file;
+
+        if (argc >= 2)
+                path = argv[1];
+        if (argc == 3)
+                kernel = argv[2];
+        if (argc > 3) {
+                printf("%s [path] [kernel]\n", argv[0]);
+                return 0;
+        }
+
+        file = fopen(proc, "r");
+        if (!file) {
+                printf("failed open %s: %s\n", proc, strerror(errno));
+                return 0;
+        }
+
+        while ((rc = fscanf(file, "%s %s %s %s %s %lx\n",
+                modname, others, others, others, others, &modaddr)) == 6) {
+                for (mp = mod_paths; mp->name != NULL; mp++) {
+                        if (!strcmp(mp->name, modname))
+                                break;
+                }
+                if (mp->name) {
+                        printf("add-symbol-file %s/%s/%s.o 0x%0lx\n", path,
+                               mp->path, mp->name, modaddr);
+                }
+        }
+
         return 0;
-#endif /* linux 2.5 */
 }
+#endif /* linux 2.5 */
 
 int jt_dbg_panic(int argc, char **argv)
 {
index b28540a..8846e3b 100644 (file)
 
 if LINUX25
 
-# We still need to link each module with vermagic.o to get rid of "kernel taited" warnings.
-basename=$(shell echo $< | sed -e 's/\.c//g' | sed -e 's/-//g' | sed -e 's/\.o//g')
+# FIXME
+# need to be rewritten:
+# - bad hacking in lvfs/Makefile.am obdclass/Makefile.am
+# - .o -> .ko
+#
+basename=$(shell echo $< | sed -e 's/\.c//g' | sed -e 's/-//g' | sed -e 's/\.o//g' | sed -e 's/^.*\///g')
 AM_CPPFLAGS=-I$(top_builddir)/include -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -pipe -mpreferred-stack-boundary=2  -DKBUILD_MODNAME=$(MODULE) -DKBUILD_BASENAME=$(basename)
 
+$(MODULE).o: $($(MODULE)_OBJECTS) $($(MODULE)_DEPENDENCIES)
+       $(LD) -m $(MOD_LINK) -r -o $(MODULE)_tmp.o $($(MODULE)_OBJECTS)
+       rm -f $(MODULE)_tmp.c
+       $(LINUX)/scripts/modpost $(LINUX)/vmlinux $(MODULE)_tmp.o
+       $(COMPILE) -UKBUILD_BASENAME -DKBUILD_BASENAME=$(MODULE) -c $(MODULE)_tmp.mod.c
+       $(LD) -m $(MOD_LINK) -r -o $(MODULE).o $(MODULE)_tmp.o $(MODULE)_tmp.mod.o
+
 else
 
 AM_CPPFLAGS=-I$(top_builddir)/include
+$(MODULE).o: $($(MODULE)_OBJECTS) $($(MODULE)_DEPENDENCIES)
+       $(LD) -m "`$(LD) --help | awk '/supported emulations/ {print $$4}'`" -r -o $(MODULE).o $($(MODULE)_OBJECTS)
 
 endif
 
-$(MODULE).o: $($(MODULE)_OBJECTS) $($(MODULE)_DEPENDENCIES)
-       $(LD) -m "`$(LD) --help | awk '/supported emulations/ {print $$4}'`" -r -o $(MODULE).o $($(MODULE)_OBJECTS)
 
 tags:
        rm -f $(top_srcdir)/TAGS
index df59db4..4a4e3a0 100644 (file)
@@ -85,6 +85,21 @@ static inline void lustre_daemonize_helper(void)
         current->tty = NULL;
 }
 
+static inline int cleanup_group_info(void)
+{
+        struct group_info *ginfo;
+
+        ginfo = groups_alloc(2);
+        if (!ginfo)
+                return -ENOMEM;
+
+        ginfo->ngroups = 0;
+        set_current_groups(ginfo);
+        put_group_info(ginfo);
+
+        return 0;
+}
+
 #define smp_num_cpus    NR_CPUS
 
 #ifndef conditional_schedule
@@ -160,6 +175,14 @@ static inline void lustre_daemonize_helper(void)
         current->tty = NULL;
 }
 
+static inline int cleanup_group_info(void)
+{
+        /* Get rid of unneeded supplementary groups */
+        current->ngroups = 0;
+        memset(current->groups, 0, sizeof(current->groups));
+        return 0;
+}
+
 #ifndef conditional_schedule
 #define conditional_schedule() if (unlikely(need_resched())) schedule()
 #endif
diff --git a/lustre/kernel_patches/patches/export-2.6.3.patch b/lustre/kernel_patches/patches/export-2.6.3.patch
new file mode 100644 (file)
index 0000000..8821d86
--- /dev/null
@@ -0,0 +1,57 @@
+Index: linux-2.6.3/fs/open.c
+===================================================================
+--- linux-2.6.3.orig/fs/open.c 2004-02-23 14:36:25.000000000 -0800
++++ linux-2.6.3/fs/open.c      2004-02-23 20:09:34.000000000 -0800
+@@ -881,6 +881,7 @@
+       return ERR_PTR(error);
+ }
++EXPORT_SYMBOL(filp_open);
+ struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)
+ {
+Index: linux-2.6.3/fs/jbd/journal.c
+===================================================================
+--- linux-2.6.3.orig/fs/jbd/journal.c  2004-01-08 22:59:10.000000000 -0800
++++ linux-2.6.3/fs/jbd/journal.c       2004-02-23 20:09:34.000000000 -0800
+@@ -71,6 +71,7 @@
+ EXPORT_SYMBOL(journal_errno);
+ EXPORT_SYMBOL(journal_ack_err);
+ EXPORT_SYMBOL(journal_clear_err);
++EXPORT_SYMBOL(log_start_commit);
+ EXPORT_SYMBOL(log_wait_commit);
+ EXPORT_SYMBOL(journal_start_commit);
+ EXPORT_SYMBOL(journal_wipe);
+Index: linux-2.6.3/fs/ext3/super.c
+===================================================================
+--- linux-2.6.3.orig/fs/ext3/super.c   2004-02-23 14:36:26.000000000 -0800
++++ linux-2.6.3/fs/ext3/super.c        2004-02-23 20:24:30.000000000 -0800
+@@ -115,6 +115,8 @@
+               handle->h_err = err;
+ }
++EXPORT_SYMBOL(ext3_journal_abort_handle);
++
+ static char error_buf[1024];
+ /* Deal with the reporting of failure conditions on a filesystem such as
+@@ -1772,6 +1774,8 @@
+       return ret;
+ }
++EXPORT_SYMBOL(ext3_force_commit);
++
+ /*
+  * Ext3 always journals updates to the superblock itself, so we don't
+  * have to propagate any other updates to the superblock on disk at this
+@@ -2059,6 +2063,10 @@
+                         unsigned long *blocks, int *created, int create);
+ EXPORT_SYMBOL(ext3_map_inode_page);
++EXPORT_SYMBOL(ext3_xattr_get);
++EXPORT_SYMBOL(ext3_xattr_set_handle);
++EXPORT_SYMBOL(ext3_bread);
++
+ MODULE_AUTHOR("Remy Card, Stephen Tweedie, Andrew Morton, Andreas Dilger, Theodore Ts'o and others");
+ MODULE_DESCRIPTION("Second Extended Filesystem with journaling extensions");
+ MODULE_LICENSE("GPL");
diff --git a/lustre/kernel_patches/patches/fs-intent-2.6.3-mm4.patch b/lustre/kernel_patches/patches/fs-intent-2.6.3-mm4.patch
new file mode 100644 (file)
index 0000000..a5c8725
--- /dev/null
@@ -0,0 +1,116 @@
+.old..........pc/linux-2.6.3-nfs-intent-suse/fs/nfs/dir.c
+.new.........fs/nfs/dir.c
+.old..........pc/linux-2.6.3-nfs-intent-suse/fs/nfs/dir.c
+.new.........fs/nfs/dir.c
+Index: linux-2.6.3-mm4/fs/nfs/dir.c
+===================================================================
+--- linux-2.6.3-mm4.orig/fs/nfs/dir.c  2004-03-08 17:05:35.000000000 +0800
++++ linux-2.6.3-mm4/fs/nfs/dir.c       2004-03-08 17:38:58.000000000 +0800
+@@ -773,7 +773,7 @@
+       if (nd->flags & LOOKUP_DIRECTORY)
+               return 0;
+       /* Are we trying to write to a read only partition? */
+-      if (IS_RDONLY(dir) && (nd->intent.open.flags & (O_CREAT|O_TRUNC|FMODE_WRITE)))
++      if (IS_RDONLY(dir) && (nd->intent.it_flags & (O_CREAT|O_TRUNC|FMODE_WRITE)))
+               return 0;
+       return 1;
+ }
+@@ -794,7 +794,7 @@
+       dentry->d_op = NFS_PROTO(dir)->dentry_ops;
+       /* Let vfs_create() deal with O_EXCL */
+-      if (nd->intent.open.flags & O_EXCL)
++      if (nd->intent.it_flags & O_EXCL)
+               goto no_entry;
+       /* Open the file on the server */
+@@ -802,7 +802,7 @@
+       /* Revalidate parent directory attribute cache */
+       nfs_revalidate_inode(NFS_SERVER(dir), dir);
+-      if (nd->intent.open.flags & O_CREAT) {
++      if (nd->intent.it_flags & O_CREAT) {
+               nfs_begin_data_update(dir);
+               inode = nfs4_atomic_open(dir, dentry, nd);
+               nfs_end_data_update(dir);
+@@ -818,7 +818,7 @@
+                               break;
+                       /* This turned out not to be a regular file */
+                       case -ELOOP:
+-                              if (!(nd->intent.open.flags & O_NOFOLLOW))
++                              if (!(nd->intent.it_flags & O_NOFOLLOW))
+                                       goto no_open;
+                       /* case -EISDIR: */
+                       /* case -EINVAL: */
+@@ -852,7 +852,7 @@
+       dir = parent->d_inode;
+       if (!is_atomic_open(dir, nd))
+               goto no_open;
+-      openflags = nd->intent.open.flags;
++      openflags = nd->intent.it_flags;
+       if (openflags & O_CREAT) {
+               /* If this is a negative dentry, just drop it */
+               if (!inode)
+Index: linux-2.6.3-mm4/fs/nfs/nfs4proc.c
+===================================================================
+--- linux-2.6.3-mm4.orig/fs/nfs/nfs4proc.c     2004-03-08 17:02:24.000000000 +0800
++++ linux-2.6.3-mm4/fs/nfs/nfs4proc.c  2004-03-08 17:37:59.000000000 +0800
+@@ -778,17 +778,17 @@
+       struct nfs4_state *state;
+       if (nd->flags & LOOKUP_CREATE) {
+-              attr.ia_mode = nd->intent.open.create_mode;
++              attr.ia_mode = nd->intent.it_create_mode;
+               attr.ia_valid = ATTR_MODE;
+               if (!IS_POSIXACL(dir))
+                       attr.ia_mode &= ~current->fs->umask;
+       } else {
+               attr.ia_valid = 0;
+-              BUG_ON(nd->intent.open.flags & O_CREAT);
++              BUG_ON(nd->intent.it_flags & O_CREAT);
+       }
+       cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0);
+-      state = nfs4_do_open(dir, &dentry->d_name, nd->intent.open.flags, &attr, cred);
++      state = nfs4_do_open(dir, &dentry->d_name, nd->intent.it_flags, &attr, cred);
+       put_rpccred(cred);
+       if (IS_ERR(state))
+               return (struct inode *)state;
+Index: linux-2.6.3-mm4/fs/cifs/dir.c
+===================================================================
+--- linux-2.6.3-mm4.orig/fs/cifs/dir.c 2004-02-18 11:58:34.000000000 +0800
++++ linux-2.6.3-mm4/fs/cifs/dir.c      2004-03-08 17:37:59.000000000 +0800
+@@ -146,18 +146,18 @@
+       if(nd) { 
+               cFYI(1,("In create for inode %p dentry->inode %p nd flags = 0x%x for %s",inode, direntry->d_inode, nd->flags,full_path));
+-              if ((nd->intent.open.flags & O_ACCMODE) == O_RDONLY)
++              if ((nd->intent.it_flags & O_ACCMODE) == O_RDONLY)
+                       desiredAccess = GENERIC_READ;
+-              else if ((nd->intent.open.flags & O_ACCMODE) == O_WRONLY)
++              else if ((nd->intent.it_flags & O_ACCMODE) == O_WRONLY)
+                       desiredAccess = GENERIC_WRITE;
+-              else if ((nd->intent.open.flags & O_ACCMODE) == O_RDWR)
++              else if ((nd->intent.it_flags & O_ACCMODE) == O_RDWR)
+                       desiredAccess = GENERIC_ALL;
+-              if((nd->intent.open.flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
++              if((nd->intent.it_flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
+                       disposition = FILE_CREATE;
+-              else if((nd->intent.open.flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
++              else if((nd->intent.it_flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
+                       disposition = FILE_OVERWRITE_IF;
+-              else if((nd->intent.open.flags & O_CREAT) == O_CREAT)
++              else if((nd->intent.it_flags & O_CREAT) == O_CREAT)
+                       disposition = FILE_OPEN_IF;
+               else {
+                       cFYI(1,("Create flag not set in create function"));
+@@ -314,7 +314,7 @@
+             parent_dir_inode, direntry->d_name.name, direntry));
+       if(nd) {  /* BB removeme */
+-              cFYI(1,("In lookup nd flags 0x%x open intent flags 0x%x",nd->flags,nd->intent.open.flags));
++              cFYI(1,("In lookup nd flags 0x%x open intent flags 0x%x",nd->flags,nd->intent.it_flags));
+       } /* BB removeme BB */
+       /* BB Add check of incoming data - e.g. frame not longer than maximum SMB - let server check the namelen BB */
diff --git a/lustre/kernel_patches/patches/iopen-2.6.3-mm4.patch b/lustre/kernel_patches/patches/iopen-2.6.3-mm4.patch
new file mode 100644 (file)
index 0000000..79b1f17
--- /dev/null
@@ -0,0 +1,422 @@
+ Documentation/filesystems/ext2.txt |   16 ++
+ fs/ext3/inode.c                    |    3 
+ fs/ext3/iopen.c                    |  239 +++++++++++++++++++++++++++++++++++++
+ fs/ext3/iopen.h                    |   15 ++
+ fs/ext3/namei.c                    |   13 ++
+ fs/ext3/super.c                    |   17 ++
+ include/linux/ext3_fs.h            |    2 
+ 7 files changed, 304 insertions(+), 1 deletion(-)
+
+Index: linux-2.6.3-mm4/Documentation/filesystems/ext2.txt
+===================================================================
+--- linux-2.6.3-mm4.orig/Documentation/filesystems/ext2.txt    2004-01-09 14:59:18.000000000 +0800
++++ linux-2.6.3-mm4/Documentation/filesystems/ext2.txt 2004-03-08 14:58:44.431196112 +0800
+@@ -35,6 +35,22 @@
+ sb=n                          Use alternate superblock at this location.
++iopen                         Makes an invisible pseudo-directory called 
++                              __iopen__ available in the root directory
++                              of the filesystem.  Allows open-by-inode-
++                              number.  i.e., inode 3145 can be accessed
++                              via /mntpt/__iopen__/3145
++
++iopen_nopriv                  This option makes the iopen directory be
++                              world-readable.  This may be safer since it
++                              allows daemons to run as an unprivileged user,
++                              however it significantly changes the security
++                              model of a Unix filesystem, since previously
++                              all files under a mode 700 directory were not
++                              generally avilable even if the
++                              permissions on the file itself is
++                              world-readable.
++
+ grpquota,noquota,quota,usrquota       Quota options are silently ignored by ext2.
+Index: linux-2.6.3-mm4/fs/ext3/inode.c
+===================================================================
+--- linux-2.6.3-mm4.orig/fs/ext3/inode.c       2004-03-08 14:57:54.969715400 +0800
++++ linux-2.6.3-mm4/fs/ext3/inode.c    2004-03-08 14:58:44.504185016 +0800
+@@ -37,6 +37,7 @@
+ #include <linux/mpage.h>
+ #include <linux/uio.h>
+ #include "xattr.h"
++#include "iopen.h"
+ #include "acl.h"
+ /*
+@@ -2472,6 +2473,8 @@
+       ei->i_acl = EXT3_ACL_NOT_CACHED;
+       ei->i_default_acl = EXT3_ACL_NOT_CACHED;
+ #endif
++      if (ext3_iopen_get_inode(inode))
++              return;
+       if (ext3_get_inode_loc(inode, &iloc, 0))
+               goto bad_inode;
+       bh = iloc.bh;
+Index: linux-2.6.3-mm4/fs/ext3/iopen.c
+===================================================================
+--- linux-2.6.3-mm4.orig/fs/ext3/iopen.c       2004-03-08 14:58:44.413198848 +0800
++++ linux-2.6.3-mm4/fs/ext3/iopen.c    2004-03-08 14:58:44.576174072 +0800
+@@ -0,0 +1,223 @@
++
++
++/*
++ * linux/fs/ext3/iopen.c
++ *
++ * Special support for open by inode number
++ *
++ * Copyright (C) 2001 by Theodore Ts'o (tytso@alum.mit.edu).
++ * 
++ * This file may be redistributed under the terms of the GNU General
++ * Public License.
++ */
++
++#include <linux/sched.h>
++#include <linux/fs.h>
++#include <linux/ext3_jbd.h>
++#include <linux/jbd.h>
++#include <linux/ext3_fs.h>
++#include <linux/smp_lock.h>
++#include "iopen.h"
++
++#ifndef assert
++#define assert(test) J_ASSERT(test)
++#endif
++
++#define IOPEN_NAME_LEN        32
++
++/*
++ * This implements looking up an inode by number.
++ */
++static struct dentry *iopen_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd)
++{
++      struct inode * inode;
++      unsigned long ino;
++        struct list_head *lp;
++        struct dentry *alternate;
++      char buf[IOPEN_NAME_LEN];
++      
++      if (dentry->d_name.len >= IOPEN_NAME_LEN)
++              return ERR_PTR(-ENAMETOOLONG);
++
++      memcpy(buf, dentry->d_name.name, dentry->d_name.len);
++      buf[dentry->d_name.len] = 0;
++
++      if (strcmp(buf, ".") == 0)
++              ino = dir->i_ino;
++      else if (strcmp(buf, "..") == 0)
++              ino = EXT3_ROOT_INO;
++      else
++              ino = simple_strtoul(buf, 0, 0);
++
++      if ((ino != EXT3_ROOT_INO &&
++           //ino != EXT3_ACL_IDX_INO &&
++           //ino != EXT3_ACL_DATA_INO &&
++           ino < EXT3_FIRST_INO(dir->i_sb)) ||
++          ino > le32_to_cpu(EXT3_SB(dir->i_sb)->s_es->s_inodes_count))
++              return ERR_PTR(-ENOENT);
++
++      inode = iget(dir->i_sb, ino);
++      if (!inode)
++              return ERR_PTR(-EACCES);
++      if (is_bad_inode(inode)) {
++              iput(inode);
++              return ERR_PTR(-ENOENT);
++      }
++
++        /* preferrably return a connected dentry */
++        spin_lock(&dcache_lock);
++        list_for_each(lp, &inode->i_dentry) {
++                alternate = list_entry(lp, struct dentry, d_alias);
++                assert(!(alternate->d_flags & DCACHE_DISCONNECTED));
++        }
++
++        if (!list_empty(&inode->i_dentry)) {
++                alternate = list_entry(inode->i_dentry.next, 
++                                       struct dentry, d_alias);
++                dget_locked(alternate);
++                alternate->d_vfs_flags |= DCACHE_REFERENCED;
++                iput(inode);
++                spin_unlock(&dcache_lock);
++                return alternate;
++        }
++        dentry->d_flags |= DCACHE_DISCONNECTED;
++        spin_unlock(&dcache_lock);
++
++      d_add(dentry, inode);
++      return NULL;
++}
++
++#define do_switch(x,y) do { \
++      __typeof__ (x) __tmp = x; \
++      x = y; y = __tmp; } while (0)
++
++static inline void switch_names(struct dentry * dentry, struct dentry * target)
++{
++      const unsigned char *old_name, *new_name;
++
++      memcpy(dentry->d_iname, target->d_iname, DNAME_INLINE_LEN); 
++      old_name = target->d_name.name;
++      new_name = dentry->d_name.name;
++      if (old_name == target->d_iname)
++              old_name = dentry->d_iname;
++      if (new_name == dentry->d_iname)
++              new_name = target->d_iname;
++      target->d_name.name = new_name;
++      dentry->d_name.name = old_name;
++}
++
++
++struct dentry *iopen_connect_dentry(struct dentry *de, struct inode *inode)
++{
++        struct dentry *tmp, *goal = NULL;
++        struct list_head *lp;
++
++        /* preferrably return a connected dentry */
++        spin_lock(&dcache_lock);
++        /* verify this dentry is really new */
++        assert(!de->d_inode);
++        assert(list_empty(&de->d_subdirs));
++        assert(list_empty(&de->d_alias));
++
++
++        list_for_each(lp, &inode->i_dentry) {
++                tmp = list_entry(lp, struct dentry, d_alias);
++                if (tmp->d_flags & DCACHE_DISCONNECTED) {
++                        assert(tmp->d_alias.next == &inode->i_dentry);
++                        assert(tmp->d_alias.prev == &inode->i_dentry);
++                        goal = tmp;
++                        dget_locked(goal);
++                        break;
++                }
++        }
++        spin_unlock(&dcache_lock);
++
++        if (!goal)
++                return NULL; 
++
++        goal->d_flags &= ~DCACHE_DISCONNECTED;
++      d_rehash(de);
++      d_move(goal, de);
++
++        return goal;
++}
++
++/*
++ * These are the special structures for the iopen pseudo directory.
++ */
++
++static struct inode_operations iopen_inode_operations = {
++      lookup:         iopen_lookup,           /* BKL held */
++};
++
++static struct file_operations iopen_file_operations = {
++      read:           generic_read_dir,
++};
++
++static int match_dentry(struct dentry *dentry, const char *name)
++{
++      int     len;
++
++      len = strlen(name);
++      if (dentry->d_name.len != len)
++              return 0;
++      if (strncmp(dentry->d_name.name, name, len))
++              return 0;
++      return 1;
++}
++
++/*
++ * This function is spliced into ext3_lookup and returns 1 the file
++ * name is __iopen__ and dentry has been filled in appropriately.
++ */
++int ext3_check_for_iopen(struct inode * dir, struct dentry *dentry)
++{
++      struct inode * inode;
++
++      if (dir->i_ino != EXT3_ROOT_INO ||
++          !test_opt(dir->i_sb, IOPEN) ||
++          !match_dentry(dentry, "__iopen__"))
++              return 0;
++
++      inode = iget(dir->i_sb, EXT3_BAD_INO);
++
++      if (!inode) 
++              return 0;
++      d_add(dentry, inode);
++      return 1;
++}
++
++/*
++ * This function is spliced into read_inode; it returns 1 if inode
++ * number is the one for /__iopen__, in which case the inode is filled
++ * in appropriately.  Otherwise, this fuction returns 0.
++ */
++int ext3_iopen_get_inode(struct inode * inode)
++{
++      if (inode->i_ino != EXT3_BAD_INO)
++              return 0;
++
++      inode->i_mode = S_IFDIR | S_IRUSR | S_IXUSR;
++      if (test_opt(inode->i_sb, IOPEN_NOPRIV))
++              inode->i_mode |= 0777;
++      inode->i_uid = 0;
++      inode->i_gid = 0;
++      inode->i_nlink = 1;
++      inode->i_size = 4096;
++      inode->i_atime = CURRENT_TIME;
++      inode->i_ctime = CURRENT_TIME;
++      inode->i_mtime = CURRENT_TIME;
++      EXT3_I(inode)->i_dtime = 0;
++      inode->i_blksize = PAGE_SIZE;   /* This is the optimal IO size
++                                       * (for stat), not the fs block
++                                       * size */  
++      inode->i_blocks = 0;
++      inode->i_version = 1;
++      inode->i_generation = 0;
++
++      inode->i_op = &iopen_inode_operations;
++      inode->i_fop = &iopen_file_operations;
++      inode->i_mapping->a_ops = 0;
++
++      return 1;
++}
+Index: linux-2.6.3-mm4/fs/ext3/iopen.h
+===================================================================
+--- linux-2.6.3-mm4.orig/fs/ext3/iopen.h       2004-03-08 14:58:44.413198848 +0800
++++ linux-2.6.3-mm4/fs/ext3/iopen.h    2004-03-08 14:58:44.577173920 +0800
+@@ -0,0 +1,15 @@
++/*
++ * iopen.h
++ *
++ * Special support for opening files by inode number.
++ * 
++ * Copyright (C) 2001 by Theodore Ts'o (tytso@alum.mit.edu).
++ * 
++ * This file may be redistributed under the terms of the GNU General
++ * Public License.
++ */
++
++extern int ext3_check_for_iopen(struct inode * dir, struct dentry *dentry);
++extern int ext3_iopen_get_inode(struct inode * inode);
++
++
+Index: linux-2.6.3-mm4/fs/ext3/namei.c
+===================================================================
+--- linux-2.6.3-mm4.orig/fs/ext3/namei.c       2004-03-08 14:57:52.978018184 +0800
++++ linux-2.6.3-mm4/fs/ext3/namei.c    2004-03-08 14:58:44.648163128 +0800
+@@ -37,6 +37,7 @@
+ #include <linux/buffer_head.h>
+ #include <linux/smp_lock.h>
+ #include "xattr.h"
++#include "iopen.h"
+ #include "acl.h"
+ /*
+@@ -970,15 +971,21 @@
+ }
+ #endif
++struct dentry *iopen_connect_dentry(struct dentry *de, struct inode *inode);
++ 
+ static struct dentry *ext3_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd)
+ {
+       struct inode * inode;
+       struct ext3_dir_entry_2 * de;
+       struct buffer_head * bh;
++      struct dentry *alternate = NULL;
+       if (dentry->d_name.len > EXT3_NAME_LEN)
+               return ERR_PTR(-ENAMETOOLONG);
++      if (ext3_check_for_iopen(dir, dentry))
++              return NULL;
++
+       bh = ext3_find_entry(dentry, &de);
+       inode = NULL;
+       if (bh) {
+@@ -989,8 +996,14 @@
+               if (!inode)
+                       return ERR_PTR(-EACCES);
+       }
++      if (inode && (alternate = iopen_connect_dentry(dentry, inode))) {
++              iput(inode);
++              return alternate;
++      }
++
+       if (inode)
+               return d_splice_alias(inode, dentry);
++
+       d_add(dentry, inode);
+       return NULL;
+ }
+Index: linux-2.6.3-mm4/fs/ext3/super.c
+===================================================================
+--- linux-2.6.3-mm4.orig/fs/ext3/super.c       2004-03-08 14:57:55.049703240 +0800
++++ linux-2.6.3-mm4/fs/ext3/super.c    2004-03-08 15:03:18.310560120 +0800
+@@ -575,7 +575,7 @@
+       Opt_abort, Opt_data_journal, Opt_data_ordered, Opt_data_writeback,
+       Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota,
+       Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0,
+-      Opt_ignore, Opt_err,
++      Opt_ignore, Opt_err, Opt_iopen, Opt_noiopen, Opt_iopen_nopriv,
+ };
+ static match_table_t tokens = {
+@@ -620,6 +620,9 @@
+       {Opt_ignore, "noquota"},
+       {Opt_ignore, "quota"},
+       {Opt_ignore, "usrquota"},
++      {Opt_iopen,  "iopen"},
++      {Opt_noiopen,  "noiopen"},
++      {Opt_iopen_nopriv,  "iopen_nopriv"},
+       {Opt_err, NULL}
+ };
+@@ -869,6 +872,18 @@
+               case Opt_abort:
+                       set_opt(sbi->s_mount_opt, ABORT);
+                       break;
++              case Opt_iopen:
++                      set_opt (sbi->s_mount_opt, IOPEN);
++                      clear_opt (sbi->s_mount_opt, IOPEN_NOPRIV);
++                      break;
++              case Opt_noiopen:
++                      clear_opt (sbi->s_mount_opt, IOPEN);
++                      clear_opt (sbi->s_mount_opt, IOPEN_NOPRIV);
++                      break;
++              case Opt_iopen_nopriv:
++                      set_opt (sbi->s_mount_opt, IOPEN);
++                      set_opt (sbi->s_mount_opt, IOPEN_NOPRIV);
++                      break;
+               case Opt_ignore:
+                       break;
+               default:
+Index: linux-2.6.3-mm4/fs/ext3/Makefile
+===================================================================
+--- linux-2.6.3-mm4.orig/fs/ext3/Makefile      2004-01-09 14:59:08.000000000 +0800
++++ linux-2.6.3-mm4/fs/ext3/Makefile   2004-03-08 14:58:44.794140936 +0800
+@@ -5,7 +5,7 @@
+ obj-$(CONFIG_EXT3_FS) += ext3.o
+ ext3-y        := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \
+-         ioctl.o namei.o super.o symlink.o hash.o
++         ioctl.o namei.o super.o symlink.o hash.o iopen.o
+ ext3-$(CONFIG_EXT3_FS_XATTR)   += xattr.o xattr_user.o xattr_trusted.o
+ ext3-$(CONFIG_EXT3_FS_POSIX_ACL) += acl.o
+Index: linux-2.6.3-mm4/include/linux/ext3_fs.h
+===================================================================
+--- linux-2.6.3-mm4.orig/include/linux/ext3_fs.h       2004-03-08 14:57:53.057006176 +0800
++++ linux-2.6.3-mm4/include/linux/ext3_fs.h    2004-03-08 14:58:44.795140784 +0800
+@@ -325,6 +325,8 @@
+ #define EXT3_MOUNT_NO_UID32           0x2000  /* Disable 32-bit UIDs */
+ #define EXT3_MOUNT_XATTR_USER         0x4000  /* Extended user attributes */
+ #define EXT3_MOUNT_POSIX_ACL          0x8000  /* POSIX Access Control Lists */
++#define EXT3_MOUNT_IOPEN             0x10000  /* Allow access via iopen */
++#define EXT3_MOUNT_IOPEN_NOPRIV              0x20000  /* Make iopen world-readable */
+ /* Compatibility, for having both ext2_fs.h and ext3_fs.h included at once */
+ #ifndef _LINUX_EXT2_FS_H
diff --git a/lustre/kernel_patches/patches/linux-2.6.3-CITI_NFS4_ALL.patch b/lustre/kernel_patches/patches/linux-2.6.3-CITI_NFS4_ALL.patch
new file mode 100644 (file)
index 0000000..16e191b
--- /dev/null
@@ -0,0 +1,14513 @@
+
+The complete series of citi nfsv4 patches in a single patch
+
+
+ Makefile                                |    2 
+ fs/Kconfig                              |   49 
+ fs/Makefile                             |    1 
+ fs/inode.c                              |    2 
+ fs/nfs/dir.c                            |  181 ++
+ fs/nfs/direct.c                         |    3 
+ fs/nfs/file.c                           |   23 
+ fs/nfs/inode.c                          |  586 +++++----
+ fs/nfs/nfs3proc.c                       |   43 
+ fs/nfs/nfs4proc.c                       |  988 +++++++---------
+ fs/nfs/nfs4xdr.c                        | 1931 ++++++++++++++++++++++++--------
+ fs/nfs/pagelist.c                       |    5 
+ fs/nfs/proc.c                           |   51 
+ fs/nfs/read.c                           |    2 
+ fs/nfs/unlink.c                         |    3 
+ fs/nfs/write.c                          |  207 +--
+ fs/nfs4acl/Makefile                     |    3 
+ fs/nfs4acl/acl.c                        |  921 +++++++++++++++
+ fs/nfs4acl/acl_syms.c                   |   51 
+ fs/nfsd/Makefile                        |    2 
+ fs/nfsd/nfs3xdr.c                       |    2 
+ fs/nfsd/nfs4idmap.c                     |  569 +++++++++
+ fs/nfsd/nfs4proc.c                      |  229 ++-
+ fs/nfsd/nfs4state.c                     |  440 +++++--
+ fs/nfsd/nfs4xdr.c                       |  495 +++++---
+ fs/nfsd/nfsctl.c                        |    7 
+ fs/nfsd/nfsproc.c                       |    1 
+ fs/nfsd/nfsxdr.c                        |    2 
+ fs/nfsd/stats.c                         |   67 -
+ fs/nfsd/vfs.c                           |  218 +++
+ include/linux/fs.h                      |    2 
+ include/linux/nfs.h                     |    2 
+ include/linux/nfs4.h                    |   80 +
+ include/linux/nfs4_acl.h                |   68 +
+ include/linux/nfs_fs.h                  |  138 +-
+ include/linux/nfs_page.h                |    2 
+ include/linux/nfs_xdr.h                 |  256 +---
+ include/linux/nfsd/nfsd.h               |   16 
+ include/linux/nfsd/nfsfh.h              |    8 
+ include/linux/nfsd/state.h              |   21 
+ include/linux/nfsd/xdr4.h               |   37 
+ include/linux/nfsd_idmap.h              |   54 
+ include/linux/sunrpc/auth_gss.h         |    2 
+ include/linux/sunrpc/cache.h            |   13 
+ include/linux/sunrpc/gss_api.h          |    3 
+ include/linux/sunrpc/stats.h            |   20 
+ include/linux/sunrpc/svc.h              |    1 
+ include/linux/sunrpc/svcauth.h          |    5 
+ include/linux/sunrpc/svcauth_gss.h      |   35 
+ include/linux/sunrpc/xdr.h              |    3 
+ include/linux/sunrpc/xprt.h             |   15 
+ net/sunrpc/Makefile                     |    2 
+ net/sunrpc/auth_gss/Makefile            |    2 
+ net/sunrpc/auth_gss/auth_gss.c          |  119 +
+ net/sunrpc/auth_gss/gss_krb5_crypto.c   |   18 
+ net/sunrpc/auth_gss/gss_krb5_mech.c     |   14 
+ net/sunrpc/auth_gss/gss_krb5_seal.c     |    9 
+ net/sunrpc/auth_gss/gss_krb5_seqnum.c   |    2 
+ net/sunrpc/auth_gss/gss_mech_switch.c   |   32 
+ net/sunrpc/auth_gss/gss_pseudoflavors.c |   21 
+ net/sunrpc/auth_gss/sunrpcgss_syms.c    |    2 
+ net/sunrpc/auth_gss/svcauth_gss.c       | 1018 ++++++++++++++++
+ net/sunrpc/cache.c                      |   13 
+ net/sunrpc/stats.c                      |  106 -
+ net/sunrpc/sunrpc_syms.c                |    5 
+ net/sunrpc/svc.c                        |    4 
+ net/sunrpc/svcauth.c                    |    5 
+ net/sunrpc/svcauth_unix.c               |   13 
+ net/sunrpc/xdr.c                        |    4 
+ net/sunrpc/xprt.c                       |  210 +--
+ include/linux/sunrpc/name_lookup.h      |   38 
+ 71 files changed, 7194 insertions(+), 2308 deletions(-)
+
+diff -puN Makefile~CITI_NFS4_ALL Makefile
+--- linux-2.6.3/Makefile~CITI_NFS4_ALL 2004-02-19 16:47:02.000000000 -0500
++++ linux-2.6.3-bfields/Makefile       2004-02-19 16:47:16.000000000 -0500
+@@ -1,7 +1,7 @@
+ VERSION = 2
+ PATCHLEVEL = 6
+ SUBLEVEL = 3
+-EXTRAVERSION =
++EXTRAVERSION = -CITI_NFS4_ALL-1
+ NAME=Feisty Dunnart
+ # *DOCUMENTATION*
+diff -puN fs/inode.c~CITI_NFS4_ALL fs/inode.c
+--- linux-2.6.3/fs/inode.c~CITI_NFS4_ALL       2004-02-19 16:47:03.000000000 -0500
++++ linux-2.6.3-bfields/fs/inode.c     2004-02-19 16:47:03.000000000 -0500
+@@ -1178,6 +1178,8 @@ void inode_update_time(struct inode *ino
+       struct timespec now;
+       int sync_it = 0;
++      if (IS_NOCMTIME(inode))
++              return;
+       if (IS_RDONLY(inode))
+               return;
+diff -puN fs/Kconfig~CITI_NFS4_ALL fs/Kconfig
+--- linux-2.6.3/fs/Kconfig~CITI_NFS4_ALL       2004-02-19 16:47:03.000000000 -0500
++++ linux-2.6.3-bfields/fs/Kconfig     2004-02-19 16:47:07.000000000 -0500
+@@ -288,7 +288,7 @@ config FS_POSIX_ACL
+ #     Never use this symbol for ifdefs.
+ #
+       bool
+-      depends on EXT2_FS_POSIX_ACL || EXT3_FS_POSIX_ACL || JFS_POSIX_ACL
++      depends on EXT2_FS_POSIX_ACL || EXT3_FS_POSIX_ACL || JFS_POSIX_ACL || NFS_V4_ACL
+       default y
+ config XFS_FS
+@@ -1314,21 +1314,25 @@ config NFS_V3
+         Say Y here if you want your NFS client to be able to speak the newer
+         version 3 of the NFS protocol.
+-        If unsure, say N.
++        If unsure, say Y.
+ config NFS_V4
+       bool "Provide NFSv4 client support (EXPERIMENTAL)"
+       depends on NFS_FS && EXPERIMENTAL
++      select RPCSEC_GSS_KRB5
+       help
+         Say Y here if you want your NFS client to be able to speak the newer
+-        version 4 of the NFS protocol.  This feature is experimental, and
+-        should only be used if you are interested in helping to test NFSv4.
++        version 4 of the NFS protocol.
++
++        Note: Requires auxiliary userspace daemons which may be found on
++              http://www.citi.umich.edu/projects/nfsv4/
+         If unsure, say N.
+ config NFS_DIRECTIO
+       bool "Allow direct I/O on NFS files (EXPERIMENTAL)"
+       depends on NFS_FS && EXPERIMENTAL
++      select NFS_V4_ACL
+       help
+         This option enables applications to perform uncached I/O on files
+         in NFS file systems using the O_DIRECT open() flag.  When O_DIRECT
+@@ -1388,6 +1392,7 @@ config NFSD_V3
+ config NFSD_V4
+       bool "Provide NFSv4 server support (EXPERIMENTAL)"
+       depends on NFSD_V3 && EXPERIMENTAL
++      select NFS_V4_ACL
+       help
+         If you would like to include the NFSv4 server as well as the NFSv2
+         and NFSv3 servers, say Y here.  This feature is experimental, and
+@@ -1423,6 +1428,12 @@ config LOCKD_V4
+       depends on NFSD_V3 || NFS_V3
+       default y
++config NFS_V4_ACL
++      bool "Provide NFSv4 ACL support"
++      depends on NFSD_V4 || NFS_V4
++      help
++        This allows you to use POSIX ACLs with NFSv4.
++
+ config EXPORTFS
+       tristate
+       default NFSD
+@@ -1431,28 +1442,24 @@ config SUNRPC
+       tristate
+ config SUNRPC_GSS
+-      tristate "Provide RPCSEC_GSS authentication (EXPERIMENTAL)"
++      tristate
++
++config RPCSEC_GSS_KRB5
++      tristate "Secure RPC: Kerberos V mechanism (EXPERIMENTAL)"
+       depends on SUNRPC && EXPERIMENTAL
+-      default SUNRPC if NFS_V4=y
++      select SUNRPC_GSS
++      select CRYPTO
++      select CRYPTO_MD5
++      select CRYPTO_DES
+       help
+-        Provides cryptographic authentication for NFS rpc requests.  To
+-        make this useful, you must also select at least one rpcsec_gss
+-        mechanism.
+-        Note: You should always select this option if you wish to use
++        Provides for secure RPC calls by means of a gss-api
++        mechanism based on Kerberos V5. This is required for
+         NFSv4.
+-config RPCSEC_GSS_KRB5
+-      tristate "Kerberos V mechanism for RPCSEC_GSS (EXPERIMENTAL)"
+-      depends on SUNRPC_GSS && CRYPTO_DES && CRYPTO_MD5
+-      default SUNRPC_GSS if NFS_V4=y
+-      help
+-        Provides a gss-api mechanism based on Kerberos V5 (this is
+-        mandatory for RFC3010-compliant NFSv4 implementations).
+-        Requires a userspace daemon;
+-              see http://www.citi.umich.edu/projects/nfsv4/.
++        Note: Requires an auxiliary userspace daemon which may be found on
++              http://www.citi.umich.edu/projects/nfsv4/
+-        Note: If you select this option, please ensure that you also
+-        enable the MD5 and DES crypto ciphers.
++        If unsure, say N.
+ config SMB_FS
+       tristate "SMB file system support (to mount Windows shares etc.)"
+diff -puN fs/nfs/dir.c~CITI_NFS4_ALL fs/nfs/dir.c
+--- linux-2.6.3/fs/nfs/dir.c~CITI_NFS4_ALL     2004-02-19 16:47:03.000000000 -0500
++++ linux-2.6.3-bfields/fs/nfs/dir.c   2004-02-19 16:47:07.000000000 -0500
+@@ -88,6 +88,10 @@ struct inode_operations nfs4_dir_inode_o
+       .permission     = nfs_permission,
+       .getattr        = nfs_getattr,
+       .setattr        = nfs_setattr,
++#ifdef CONFIG_NFS_V4_ACL
++      .getxattr       = nfs_getxattr,
++      .setxattr       = nfs_setxattr,
++#endif /* CONFIG_NFS_V4_ACL */
+ };
+ #endif /* CONFIG_NFS_V4 */
+@@ -139,11 +143,13 @@ int nfs_readdir_filler(nfs_readdir_descr
+       struct file     *file = desc->file;
+       struct inode    *inode = file->f_dentry->d_inode;
+       struct rpc_cred *cred = nfs_file_cred(file);
++      unsigned long   timestamp;
+       int             error;
+       dfprintk(VFS, "NFS: nfs_readdir_filler() reading cookie %Lu into page %lu.\n", (long long)desc->entry->cookie, page->index);
+  again:
++      timestamp = jiffies;
+       error = NFS_PROTO(inode)->readdir(file->f_dentry, cred, desc->entry->cookie, page,
+                                         NFS_SERVER(inode)->dtsize, desc->plus);
+       if (error < 0) {
+@@ -157,18 +163,21 @@ int nfs_readdir_filler(nfs_readdir_descr
+               goto error;
+       }
+       SetPageUptodate(page);
++      NFS_FLAGS(inode) |= NFS_INO_INVALID_ATIME;
+       /* Ensure consistent page alignment of the data.
+        * Note: assumes we have exclusive access to this mapping either
+        *       throught inode->i_sem or some other mechanism.
+        */
+-      if (page->index == 0)
++      if (page->index == 0) {
+               invalidate_inode_pages(inode->i_mapping);
++              NFS_I(inode)->readdir_timestamp = timestamp;
++      }
+       unlock_page(page);
+       return 0;
+  error:
+       SetPageError(page);
+       unlock_page(page);
+-      invalidate_inode_pages(inode->i_mapping);
++      nfs_zap_caches(inode);
+       desc->error = error;
+       return -EIO;
+ }
+@@ -381,6 +390,7 @@ int uncached_readdir(nfs_readdir_descrip
+                                               page,
+                                               NFS_SERVER(inode)->dtsize,
+                                               desc->plus);
++      NFS_FLAGS(inode) |= NFS_INO_INVALID_ATIME;
+       desc->page = page;
+       desc->ptr = kmap(page);         /* matching kunmap in nfs_do_filldir */
+       if (desc->error >= 0) {
+@@ -459,7 +469,15 @@ static int nfs_readdir(struct file *filp
+                       }
+                       res = 0;
+                       break;
+-              } else if (res < 0)
++              }
++              if (res == -ETOOSMALL && desc->plus) {
++                      NFS_FLAGS(inode) &= ~NFS_INO_ADVISE_RDPLUS;
++                      nfs_zap_caches(inode);
++                      desc->plus = 0;
++                      desc->entry->eof = 0;
++                      continue;
++              }
++              if (res < 0)
+                       break;
+               res = nfs_do_filldir(desc, dirent, filldir);
+@@ -481,14 +499,19 @@ static int nfs_readdir(struct file *filp
+  * In the case it has, we assume that the dentries are untrustworthy
+  * and may need to be looked up again.
+  */
+-static inline
+-int nfs_check_verifier(struct inode *dir, struct dentry *dentry)
++static inline int nfs_check_verifier(struct inode *dir, struct dentry *dentry)
+ {
+       if (IS_ROOT(dentry))
+               return 1;
+-      if (nfs_revalidate_inode(NFS_SERVER(dir), dir))
++      if ((NFS_FLAGS(dir) & NFS_INO_INVALID_ATTR) != 0
++                      || nfs_attribute_timeout(dir))
+               return 0;
+-      return time_after(dentry->d_time, NFS_MTIME_UPDATE(dir));
++      return nfs_verify_change_attribute(dir, (unsigned long)dentry->d_fsdata);
++}
++
++static inline void nfs_set_verifier(struct dentry * dentry, unsigned long verf)
++{
++      dentry->d_fsdata = (void *)verf;
+ }
+ /*
+@@ -528,9 +551,7 @@ int nfs_neg_need_reval(struct inode *dir
+       /* Don't revalidate a negative dentry if we're creating a new file */
+       if ((ndflags & LOOKUP_CREATE) && !(ndflags & LOOKUP_CONTINUE))
+               return 0;
+-      if (!nfs_check_verifier(dir, dentry))
+-              return 1;
+-      return time_after(jiffies, dentry->d_time + NFS_ATTRTIMEO(dir));
++      return !nfs_check_verifier(dir, dentry);
+ }
+ /*
+@@ -552,6 +573,7 @@ static int nfs_lookup_revalidate(struct 
+       int error;
+       struct nfs_fh fhandle;
+       struct nfs_fattr fattr;
++      unsigned long verifier;
+       int isopen = 0;
+       parent = dget_parent(dentry);
+@@ -574,6 +596,9 @@ static int nfs_lookup_revalidate(struct 
+               goto out_bad;
+       }
++      /* Revalidate parent directory attribute cache */
++      nfs_revalidate_inode(NFS_SERVER(dir), dir);
++
+       /* Force a full look up iff the parent directory has changed */
+       if (nfs_check_verifier(dir, dentry)) {
+               if (nfs_lookup_verify_inode(inode, isopen))
+@@ -581,6 +606,12 @@ static int nfs_lookup_revalidate(struct 
+               goto out_valid;
+       }
++      /*
++       * Note: we're not holding inode->i_sem and so may be racing with
++       * operations that change the directory. We therefore save the
++       * change attribute *before* we do the RPC call.
++       */
++      verifier = nfs_save_change_attribute(dir);
+       error = nfs_cached_lookup(dir, dentry, &fhandle, &fattr);
+       if (!error) {
+               if (memcmp(NFS_FH(inode), &fhandle, sizeof(struct nfs_fh))!= 0)
+@@ -603,6 +634,7 @@ static int nfs_lookup_revalidate(struct 
+  out_valid_renew:
+       nfs_renew_times(dentry);
++      nfs_set_verifier(dentry, verifier);
+  out_valid:
+       unlock_kernel();
+       dput(parent);
+@@ -638,6 +670,11 @@ static int nfs_dentry_delete(struct dent
+               /* Unhash it, so that ->d_iput() would be called */
+               return 1;
+       }
++      if (!(dentry->d_sb->s_flags & MS_ACTIVE)) {
++              /* Unhash it, so that ancestors of killed async unlink
++               * files will be cleaned up during umount */
++              return 1;
++      }
+       return 0;
+ }
+@@ -693,6 +730,8 @@ static struct dentry *nfs_lookup(struct 
+       dentry->d_op = NFS_PROTO(dir)->dentry_ops;
+       lock_kernel();
++      /* Revalidate parent directory attribute cache */
++      nfs_revalidate_inode(NFS_SERVER(dir), dir);
+       /* If we're doing an exclusive create, optimize away the lookup */
+       if (nfs_is_exclusive_create(dir, nd))
+@@ -715,6 +754,7 @@ no_entry:
+       error = 0;
+       d_add(dentry, inode);
+       nfs_renew_times(dentry);
++      nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
+ out_unlock:
+       unlock_kernel();
+ out:
+@@ -768,7 +808,15 @@ static struct dentry *nfs_atomic_lookup(
+       /* Open the file on the server */
+       lock_kernel();
+-      inode = nfs4_atomic_open(dir, dentry, nd);
++      /* Revalidate parent directory attribute cache */
++      nfs_revalidate_inode(NFS_SERVER(dir), dir);
++
++      if (nd->intent.open.flags & O_CREAT) {
++              nfs_begin_data_update(dir);
++              inode = nfs4_atomic_open(dir, dentry, nd);
++              nfs_end_data_update(dir);
++      } else
++              inode = nfs4_atomic_open(dir, dentry, nd);
+       unlock_kernel();
+       if (IS_ERR(inode)) {
+               error = PTR_ERR(inode);
+@@ -790,6 +838,7 @@ static struct dentry *nfs_atomic_lookup(
+ no_entry:
+       d_add(dentry, inode);
+       nfs_renew_times(dentry);
++      nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
+ out:
+       BUG_ON(error > 0);
+       return ERR_PTR(error);
+@@ -801,13 +850,16 @@ static int nfs_open_revalidate(struct de
+ {
+       struct dentry *parent = NULL;
+       struct inode *inode = dentry->d_inode;
++      struct inode *dir;
++      unsigned long verifier;
+       int openflags, ret = 0;
+       /* NFS only supports OPEN for regular files */
+       if (inode && !S_ISREG(inode->i_mode))
+               goto no_open;
+       parent = dget_parent(dentry);
+-      if (!is_atomic_open(parent->d_inode, nd))
++      dir = parent->d_inode;
++      if (!is_atomic_open(dir, nd))
+               goto no_open;
+       openflags = nd->intent.open.flags;
+       if (openflags & O_CREAT) {
+@@ -821,8 +873,16 @@ static int nfs_open_revalidate(struct de
+       /* We can't create new files, or truncate existing ones here */
+       openflags &= ~(O_CREAT|O_TRUNC);
++      /*
++       * Note: we're not holding inode->i_sem and so may be racing with
++       * operations that change the directory. We therefore save the
++       * change attribute *before* we do the RPC call.
++       */
+       lock_kernel();
+-      ret = nfs4_open_revalidate(parent->d_inode, dentry, openflags);
++      verifier = nfs_save_change_attribute(dir);
++      ret = nfs4_open_revalidate(dir, dentry, openflags);
++      if (!ret)
++              nfs_set_verifier(dentry, verifier);
+       unlock_kernel();
+ out:
+       dput(parent);
+@@ -869,15 +929,20 @@ int nfs_cached_lookup(struct inode *dir,
+       struct nfs_server *server;
+       struct nfs_entry entry;
+       struct page *page;
+-      unsigned long timestamp = NFS_MTIME_UPDATE(dir);
++      unsigned long timestamp;
+       int res;
+       if (!NFS_USE_READDIRPLUS(dir))
+               return -ENOENT;
+       server = NFS_SERVER(dir);
+-      if (server->flags & NFS_MOUNT_NOAC)
++      /* Don't use readdirplus unless the cache is stable */
++      if ((server->flags & NFS_MOUNT_NOAC) != 0
++                      || nfs_caches_unstable(dir)
++                      || nfs_attribute_timeout(dir))
+               return -ENOENT;
+-      nfs_revalidate_inode(server, dir);
++      if ((NFS_FLAGS(dir) & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA)) != 0)
++              return -ENOENT;
++      timestamp = NFS_I(dir)->readdir_timestamp;
+       entry.fh = fh;
+       entry.fattr = fattr;
+@@ -931,6 +996,7 @@ static int nfs_instantiate(struct dentry
+       if (inode) {
+               d_instantiate(dentry, inode);
+               nfs_renew_times(dentry);
++              nfs_set_verifier(dentry, nfs_save_change_attribute(dentry->d_parent->d_inode));
+               error = 0;
+       }
+       return error;
+@@ -969,11 +1035,13 @@ static int nfs_create(struct inode *dir,
+        * does not pass the create flags.
+        */
+       lock_kernel();
+-      nfs_zap_caches(dir);
++      nfs_begin_data_update(dir);
+       inode = NFS_PROTO(dir)->create(dir, &dentry->d_name, &attr, open_flags);
++      nfs_end_data_update(dir);
+       if (!IS_ERR(inode)) {
+               d_instantiate(dentry, inode);
+               nfs_renew_times(dentry);
++              nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
+               error = 0;
+       } else {
+               error = PTR_ERR(inode);
+@@ -1004,9 +1072,10 @@ nfs_mknod(struct inode *dir, struct dent
+       attr.ia_valid = ATTR_MODE;
+       lock_kernel();
+-      nfs_zap_caches(dir);
++      nfs_begin_data_update(dir);
+       error = NFS_PROTO(dir)->mknod(dir, &dentry->d_name, &attr, rdev,
+                                       &fhandle, &fattr);
++      nfs_end_data_update(dir);
+       if (!error)
+               error = nfs_instantiate(dentry, &fhandle, &fattr);
+       else
+@@ -1041,9 +1110,10 @@ static int nfs_mkdir(struct inode *dir, 
+        */
+       d_drop(dentry);
+ #endif
+-      nfs_zap_caches(dir);
++      nfs_begin_data_update(dir);
+       error = NFS_PROTO(dir)->mkdir(dir, &dentry->d_name, &attr, &fhandle,
+                                       &fattr);
++      nfs_end_data_update(dir);
+       if (!error)
+               error = nfs_instantiate(dentry, &fhandle, &fattr);
+       else
+@@ -1060,10 +1130,12 @@ static int nfs_rmdir(struct inode *dir, 
+               dir->i_ino, dentry->d_name.name);
+       lock_kernel();
+-      nfs_zap_caches(dir);
++      nfs_begin_data_update(dir);
+       error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name);
+-      if (!error)
++      /* Ensure the VFS deletes this inode */
++      if (error == 0 && dentry->d_inode != NULL)
+               dentry->d_inode->i_nlink = 0;
++      nfs_end_data_update(dir);
+       unlock_kernel();
+       return error;
+@@ -1119,12 +1191,21 @@ dentry->d_parent->d_name.name, dentry->d
+                       goto out;
+       } while(sdentry->d_inode != NULL); /* need negative lookup */
+-      nfs_zap_caches(dir);
+       qsilly.name = silly;
+       qsilly.len  = strlen(silly);
+-      error = NFS_PROTO(dir)->rename(dir, &dentry->d_name, dir, &qsilly);
++      nfs_begin_data_update(dir);
++      if (dentry->d_inode) {
++              nfs_begin_data_update(dentry->d_inode);
++              error = NFS_PROTO(dir)->rename(dir, &dentry->d_name,
++                              dir, &qsilly);
++              nfs_end_data_update(dentry->d_inode);
++      } else
++              error = NFS_PROTO(dir)->rename(dir, &dentry->d_name,
++                              dir, &qsilly);
++      nfs_end_data_update(dir);
+       if (!error) {
+               nfs_renew_times(dentry);
++              nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
+               d_move(dentry, sdentry);
+               error = nfs_async_unlink(dentry);
+               /* If we return 0 we don't unlink */
+@@ -1156,14 +1237,17 @@ static int nfs_safe_remove(struct dentry
+               goto out;
+       }
+-      nfs_zap_caches(dir);
+-      if (inode)
+-              NFS_CACHEINV(inode);
+-      error = NFS_PROTO(dir)->remove(dir, &dentry->d_name);
+-      if (error < 0)
+-              goto out;
+-      if (inode)
+-              inode->i_nlink--;
++      nfs_begin_data_update(dir);
++      if (inode != NULL) {
++              nfs_begin_data_update(inode);
++              error = NFS_PROTO(dir)->remove(dir, &dentry->d_name);
++              /* The VFS may want to delete this inode */
++              if (error == 0)
++                      inode->i_nlink--;
++              nfs_end_data_update(inode);
++      } else
++              error = NFS_PROTO(dir)->remove(dir, &dentry->d_name);
++      nfs_end_data_update(dir);
+ out:
+       return error;
+ }
+@@ -1198,9 +1282,10 @@ static int nfs_unlink(struct inode *dir,
+       spin_unlock(&dentry->d_lock);
+       spin_unlock(&dcache_lock);
+       error = nfs_safe_remove(dentry);
+-      if (!error)
++      if (!error) {
+               nfs_renew_times(dentry);
+-      else if (need_rehash)
++              nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
++      } else if (need_rehash)
+               d_rehash(dentry);
+       unlock_kernel();
+       return error;
+@@ -1247,9 +1332,10 @@ dentry->d_parent->d_name.name, dentry->d
+       qsymname.len  = strlen(symname);
+       lock_kernel();
+-      nfs_zap_caches(dir);
++      nfs_begin_data_update(dir);
+       error = NFS_PROTO(dir)->symlink(dir, &dentry->d_name, &qsymname,
+                                         &attr, &sym_fh, &sym_attr);
++      nfs_end_data_update(dir);
+       if (!error) {
+               error = nfs_instantiate(dentry, &sym_fh, &sym_attr);
+       } else {
+@@ -1281,9 +1367,12 @@ nfs_link(struct dentry *old_dentry, stru
+        */
+       lock_kernel();
+       d_drop(dentry);
+-      nfs_zap_caches(dir);
+-      NFS_CACHEINV(inode);
++
++      nfs_begin_data_update(dir);
++      nfs_begin_data_update(inode);
+       error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name);
++      nfs_end_data_update(inode);
++      nfs_end_data_update(dir);
+       unlock_kernel();
+       return error;
+ }
+@@ -1388,16 +1477,23 @@ go_ahead:
+       if (new_inode)
+               d_delete(new_dentry);
+-      nfs_zap_caches(new_dir);
+-      nfs_zap_caches(old_dir);
++      nfs_begin_data_update(old_dir);
++      nfs_begin_data_update(new_dir);
++      nfs_begin_data_update(old_inode);
+       error = NFS_PROTO(old_dir)->rename(old_dir, &old_dentry->d_name,
+                                          new_dir, &new_dentry->d_name);
++      nfs_end_data_update(old_inode);
++      nfs_end_data_update(new_dir);
++      nfs_end_data_update(old_dir);
+ out:
+       if (rehash)
+               d_rehash(rehash);
+-      if (!error && !S_ISDIR(old_inode->i_mode))
+-              d_move(old_dentry, new_dentry);
+-      nfs_renew_times(new_dentry);
++      if (!error) {
++              if (!S_ISDIR(old_inode->i_mode))
++                      d_move(old_dentry, new_dentry);
++              nfs_renew_times(new_dentry);
++              nfs_set_verifier(new_dentry, nfs_save_change_attribute(new_dir));
++      }
+       /* new dentry created? */
+       if (dentry)
+@@ -1451,7 +1547,8 @@ nfs_permission(struct inode *inode, int 
+       cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0);
+       if (cache->cred == cred
+-          && time_before(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode))) {
++          && time_before(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode))
++          && !(NFS_FLAGS(inode) & NFS_INO_INVALID_ATTR)) {
+               if (!(res = cache->err)) {
+                       /* Is the mask a subset of an accepted mask? */
+                       if ((cache->mask & mask) == mask)
+diff -puN fs/nfs/direct.c~CITI_NFS4_ALL fs/nfs/direct.c
+--- linux-2.6.3/fs/nfs/direct.c~CITI_NFS4_ALL  2004-02-19 16:47:03.000000000 -0500
++++ linux-2.6.3-bfields/fs/nfs/direct.c        2004-02-19 16:47:03.000000000 -0500
+@@ -269,6 +269,7 @@ nfs_direct_write_seg(struct inode *inode
+       if (IS_SYNC(inode) || NFS_PROTO(inode)->version == 2 || count <= wsize)
+               wdata.args.stable = NFS_FILE_SYNC;
++      nfs_begin_data_update(inode);
+ retry:
+       need_commit = 0;
+       tot_bytes = 0;
+@@ -334,6 +335,8 @@ retry:
+                                               VERF_SIZE) != 0)
+                       goto sync_retry;
+       }
++      nfs_end_data_update(inode);
++      NFS_FLAGS(inode) |= NFS_INO_INVALID_DATA;
+       return tot_bytes;
+diff -puN fs/nfs/file.c~CITI_NFS4_ALL fs/nfs/file.c
+--- linux-2.6.3/fs/nfs/file.c~CITI_NFS4_ALL    2004-02-19 16:47:03.000000000 -0500
++++ linux-2.6.3-bfields/fs/nfs/file.c  2004-02-19 16:47:07.000000000 -0500
+@@ -63,6 +63,20 @@ struct inode_operations nfs_file_inode_o
+       .setattr        = nfs_setattr,
+ };
++#ifdef CONFIG_NFS_V4
++
++struct inode_operations nfs4_file_inode_operations = {
++      .permission     = nfs_permission,
++      .getattr        = nfs_getattr,
++      .setattr        = nfs_setattr,
++#ifdef CONFIG_NFS_V4_ACL
++      .getxattr       = nfs_getxattr,
++      .setxattr       = nfs_setxattr,
++#endif /* CONFIG_NFS_V4_ACL */
++};
++
++#endif /* CONFIG_NFS_V4 */
++
+ /* Hack for future NFS swap support */
+ #ifndef IS_SWAPFILE
+ # define IS_SWAPFILE(inode)   (0)
+@@ -104,11 +118,16 @@ nfs_file_flush(struct file *file)
+       dfprintk(VFS, "nfs: flush(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino);
++      if ((file->f_mode & FMODE_WRITE) == 0)
++              return 0;
+       lock_kernel();
+-      status = nfs_wb_file(inode, file);
++      /* Ensure that data+attribute caches are up to date after close() */
++      status = nfs_wb_all(inode);
+       if (!status) {
+               status = file->f_error;
+               file->f_error = 0;
++              if (!status)
++                      __nfs_revalidate_inode(NFS_SERVER(inode), inode);
+       }
+       unlock_kernel();
+       return status;
+@@ -179,7 +198,7 @@ nfs_fsync(struct file *file, struct dent
+       dfprintk(VFS, "nfs: fsync(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino);
+       lock_kernel();
+-      status = nfs_wb_file(inode, file);
++      status = nfs_wb_all(inode);
+       if (!status) {
+               status = file->f_error;
+               file->f_error = 0;
+diff -puN fs/nfs/inode.c~CITI_NFS4_ALL fs/nfs/inode.c
+--- linux-2.6.3/fs/nfs/inode.c~CITI_NFS4_ALL   2004-02-19 16:47:03.000000000 -0500
++++ linux-2.6.3-bfields/fs/nfs/inode.c 2004-02-19 16:47:15.000000000 -0500
+@@ -53,8 +53,8 @@
+  */
+ #define NFS_MAX_READAHEAD     RPC_MAXREQS
+-void nfs_zap_caches(struct inode *);
+ static void nfs_invalidate_inode(struct inode *);
++static int nfs_update_inode(struct inode *, struct nfs_fattr *, unsigned long);
+ static struct inode *nfs_alloc_inode(struct super_block *sb);
+ static void nfs_destroy_inode(struct inode *);
+@@ -118,7 +118,7 @@ nfs_write_inode(struct inode *inode, int
+ {
+       int flags = sync ? FLUSH_WAIT : 0;
+-      nfs_commit_file(inode, NULL, 0, 0, flags);
++      nfs_commit_inode(inode, 0, 0, flags);
+ }
+ static void
+@@ -136,21 +136,24 @@ nfs_delete_inode(struct inode * inode)
+       clear_inode(inode);
+ }
+-/*
+- * For the moment, the only task for the NFS clear_inode method is to
+- * release the mmap credential
+- */
+ static void
+ nfs_clear_inode(struct inode *inode)
+ {
+       struct nfs_inode *nfsi = NFS_I(inode);
+       struct rpc_cred *cred = nfsi->mm_cred;
++#ifdef CONFIG_NFS_V4_ACL
++      if (nfsi->acl != NFS4_ACL_NOT_CACHED)
++              posix_acl_release(nfsi->acl);
++      if (nfsi->default_acl != NFS4_ACL_NOT_CACHED)
++              posix_acl_release(nfsi->default_acl);
++#endif /* CONFIG_NFS_V4_ACL */
+       if (cred)
+               put_rpccred(cred);
+       cred = nfsi->cache_access.cred;
+       if (cred)
+               put_rpccred(cred);
++      BUG_ON(atomic_read(&nfsi->data_updates) != 0);
+ }
+ void
+@@ -230,50 +233,23 @@ nfs_block_size(unsigned long bsize, unsi
+ /*
+  * Obtain the root inode of the file system.
+  */
+-static int
+-nfs_get_root(struct inode **rooti, rpc_authflavor_t authflavor, struct super_block *sb, struct nfs_fh *rootfh)
++static struct inode *
++nfs_get_root(struct super_block *sb, struct nfs_fh *rootfh, struct nfs_fsinfo *fsinfo)
+ {
+       struct nfs_server       *server = NFS_SB(sb);
+-      struct nfs_fattr        fattr = { };
++      struct inode *rooti;
+       int                     error;
+-      error = server->rpc_ops->getroot(server, rootfh, &fattr);
+-      if (error == -EACCES && authflavor > RPC_AUTH_MAXFLAVOR) {
+-              /*
+-               * Some authentication types (gss/krb5, most notably)
+-               * are such that root won't be able to present a
+-               * credential for GETATTR (ie, getroot()).
+-               *
+-               * We still want the mount to succeed.
+-               * 
+-               * So we fake the attr values and mark the inode as such.
+-               * On the first succesful traversal, we fix everything.
+-               * The auth type test isn't quite correct, but whatever.
+-               */
+-              dfprintk(VFS, "NFS: faking root inode\n");
+-
+-              fattr.fileid = 1;
+-              fattr.nlink = 2;        /* minimum for a dir */
+-              fattr.type = NFDIR;
+-              fattr.mode = S_IFDIR|S_IRUGO|S_IXUGO;
+-              fattr.size = 4096;
+-              fattr.du.nfs3.used = 1;
+-              fattr.valid = NFS_ATTR_FATTR|NFS_ATTR_FATTR_V3;
+-      } else if (error < 0) {
++      error = server->rpc_ops->getroot(server, rootfh, fsinfo);
++      if (error < 0) {
+               printk(KERN_NOTICE "nfs_get_root: getattr error = %d\n", -error);
+-              *rooti = NULL;  /* superfluous ... but safe */
+-              return error;
++              return ERR_PTR(error);
+       }
+-      *rooti = nfs_fhget(sb, rootfh, &fattr);
+-      if (error == -EACCES && authflavor > RPC_AUTH_MAXFLAVOR) {
+-              if (*rooti) {
+-                      NFS_FLAGS(*rooti) |= NFS_INO_FAKE_ROOT;
+-                      NFS_CACHEINV((*rooti));
+-                      error = 0;
+-              }
+-      }
+-      return error;
++      rooti = nfs_fhget(sb, rootfh, fsinfo->fattr);
++      if (!rooti)
++              return ERR_PTR(-ENOMEM);
++      return rooti;
+ }
+ /*
+@@ -283,7 +259,7 @@ static int
+ nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor)
+ {
+       struct nfs_server       *server;
+-      struct inode            *root_inode = NULL;
++      struct inode            *root_inode;
+       struct nfs_fattr        fattr;
+       struct nfs_fsinfo       fsinfo = {
+                                       .fattr = &fattr,
+@@ -299,8 +275,9 @@ nfs_sb_init(struct super_block *sb, rpc_
+       sb->s_magic      = NFS_SUPER_MAGIC;
++      root_inode = nfs_get_root(sb, &server->fh, &fsinfo);
+       /* Did getting the root inode fail? */
+-      if (nfs_get_root(&root_inode, authflavor, sb, &server->fh) < 0)
++      if (IS_ERR(root_inode))
+               goto out_no_root;
+       sb->s_root = d_alloc_root(root_inode);
+       if (!sb->s_root)
+@@ -309,10 +286,6 @@ nfs_sb_init(struct super_block *sb, rpc_
+       sb->s_root->d_op = server->rpc_ops->dentry_ops;
+       /* Get some general file system info */
+-        if (server->rpc_ops->fsinfo(server, &server->fh, &fsinfo) < 0) {
+-              printk(KERN_NOTICE "NFS: cannot retrieve file system info.\n");
+-              goto out_no_root;
+-        }
+       if (server->namelen == 0 &&
+           server->rpc_ops->pathconf(server, &server->fh, &pathinfo) >= 0)
+               server->namelen = pathinfo.max_namelen;
+@@ -368,13 +341,11 @@ nfs_sb_init(struct super_block *sb, rpc_
+       rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100);
+       return 0;
+       /* Yargs. It didn't work out. */
+-out_free_all:
+-      if (root_inode)
+-              iput(root_inode);
+-      return -EINVAL;
+ out_no_root:
+       printk("nfs_read_super: get root inode failed\n");
+-      goto out_free_all;
++      if (!IS_ERR(root_inode))
++              iput(root_inode);
++      return -EINVAL;
+ }
+ /*
+@@ -627,13 +598,17 @@ static int nfs_show_options(struct seq_f
+ void
+ nfs_zap_caches(struct inode *inode)
+ {
++      struct nfs_inode *nfsi = NFS_I(inode);
++      int mode = inode->i_mode;
++
+       NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
+       NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
+-      invalidate_remote_inode(inode);
+-
+       memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
+-      NFS_CACHEINV(inode);
++      if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))
++              nfsi->flags |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
++      else
++              nfsi->flags |= NFS_INO_INVALID_ATTR;
+ }
+ /*
+@@ -673,9 +648,6 @@ nfs_find_actor(struct inode *inode, void
+               return 0;
+       if (is_bad_inode(inode))
+               return 0;
+-      /* Force an attribute cache update if inode->i_count == 0 */
+-      if (!atomic_read(&inode->i_count))
+-              NFS_CACHEINV(inode);
+       return 1;
+ }
+@@ -729,12 +701,12 @@ nfs_fhget(struct super_block *sb, struct
+               inode->i_ino = hash;
+               /* We can't support update_atime(), since the server will reset it */
+-              inode->i_flags |= S_NOATIME;
++              inode->i_flags |= S_NOATIME|S_NOCMTIME;
+               inode->i_mode = fattr->mode;
+               /* Why so? Because we want revalidate for devices/FIFOs, and
+                * that's precisely what we have in nfs_file_inode_operations.
+                */
+-              inode->i_op = &nfs_file_inode_operations;
++              inode->i_op = NFS_SB(sb)->rpc_ops->file_inode_ops;
+               if (S_ISREG(inode->i_mode)) {
+                       inode->i_fop = &nfs_file_operations;
+                       inode->i_data.a_ops = &nfs_file_aops;
+@@ -754,10 +726,6 @@ nfs_fhget(struct super_block *sb, struct
+               inode->i_atime = fattr->atime;
+               inode->i_mtime = fattr->mtime;
+               inode->i_ctime = fattr->ctime;
+-              nfsi->read_cache_ctime = fattr->ctime;
+-              nfsi->read_cache_mtime = fattr->mtime;
+-              nfsi->cache_mtime_jiffies = fattr->timestamp;
+-              nfsi->read_cache_isize = fattr->size;
+               if (fattr->valid & NFS_ATTR_FATTR_V4)
+                       nfsi->change_attr = fattr->change_attr;
+               inode->i_size = nfs_size_to_loff_t(fattr->size);
+@@ -778,7 +746,6 @@ nfs_fhget(struct super_block *sb, struct
+               nfsi->attrtimeo_timestamp = jiffies;
+               memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf));
+               nfsi->cache_access.cred = NULL;
+-
+               unlock_new_inode(inode);
+       } else
+               nfs_refresh_inode(inode, fattr);
+@@ -804,70 +771,50 @@ nfs_setattr(struct dentry *dentry, struc
+       struct nfs_fattr fattr;
+       int error;
++      if (attr->ia_valid & ATTR_SIZE) {
++              if (!S_ISREG(inode->i_mode) || attr->ia_size == i_size_read(inode))
++                      attr->ia_valid &= ~ATTR_SIZE;
++      }
++
+       /* Optimization: if the end result is no change, don't RPC */
+       attr->ia_valid &= NFS_VALID_ATTRS;
+       if (attr->ia_valid == 0)
+               return 0;
+       lock_kernel();
+-
+-      /*
+-       * Make sure the inode is up-to-date.
+-       */
+-      error = nfs_revalidate_inode(NFS_SERVER(inode),inode);
+-      if (error) {
+-#ifdef NFS_PARANOIA
+-printk("nfs_setattr: revalidate failed, error=%d\n", error);
+-#endif
+-              goto out;
+-      }
+-
+-      if (!S_ISREG(inode->i_mode)) {
+-              attr->ia_valid &= ~ATTR_SIZE;
+-              if (attr->ia_valid == 0)
+-                      goto out;
+-      } else {
+-              filemap_fdatawrite(inode->i_mapping);
+-              error = nfs_wb_all(inode);
+-              filemap_fdatawait(inode->i_mapping);
+-              if (error)
+-                      goto out;
+-              /* Optimize away unnecessary truncates */
+-              if ((attr->ia_valid & ATTR_SIZE) && i_size_read(inode) == attr->ia_size)
+-                      attr->ia_valid &= ~ATTR_SIZE;
++      nfs_begin_data_update(inode);
++      /* Write all dirty data if we're changing file permissions or size */
++      if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_SIZE)) != 0) {
++              if (filemap_fdatawrite(inode->i_mapping) == 0)
++                      filemap_fdatawait(inode->i_mapping);
++              nfs_wb_all(inode);
+       }
+-      if (!attr->ia_valid)
+-              goto out;
+-
+       error = NFS_PROTO(inode)->setattr(dentry, &fattr, attr);
+-      if (error)
+-              goto out;
+-      /*
+-       * If we changed the size or mtime, update the inode
+-       * now to avoid invalidating the page cache.
+-       */
+-      if (attr->ia_valid & ATTR_SIZE) {
+-              if (attr->ia_size != fattr.size)
+-                      printk("nfs_setattr: attr=%Ld, fattr=%Ld??\n",
+-                             (long long) attr->ia_size, (long long)fattr.size);
+-              vmtruncate(inode, attr->ia_size);
++      if (error == 0) {
++              nfs_refresh_inode(inode, &fattr);
++              if ((attr->ia_valid & ATTR_MODE) != 0) {
++                      int mode;
++                      mode = inode->i_mode & ~S_IALLUGO;
++                      mode |= attr->ia_mode & S_IALLUGO;
++                      inode->i_mode = mode;
++              }
++              if ((attr->ia_valid & ATTR_UID) != 0)
++                      inode->i_uid = attr->ia_uid;
++              if ((attr->ia_valid & ATTR_GID) != 0)
++                      inode->i_gid = attr->ia_gid;
++              if ((attr->ia_valid & ATTR_SIZE) != 0) {
++                      i_size_write(inode, attr->ia_size);
++                      vmtruncate(inode, attr->ia_size);
++              }
+       }
+-
+-      /*
+-       * If we changed the size or mtime, update the inode
+-       * now to avoid invalidating the page cache.
+-       */
+-      if (!(fattr.valid & NFS_ATTR_WCC)) {
+-              struct nfs_inode *nfsi = NFS_I(inode);
+-              fattr.pre_size = nfsi->read_cache_isize;
+-              fattr.pre_mtime = nfsi->read_cache_mtime;
+-              fattr.pre_ctime = nfsi->read_cache_ctime;
+-              fattr.valid |= NFS_ATTR_WCC;
+-      }
+-      /* Force an attribute cache update */
+-      NFS_CACHEINV(inode);
+-      error = nfs_refresh_inode(inode, &fattr);
+-out:
++      if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0) {
++              struct rpc_cred **cred = &NFS_I(inode)->cache_access.cred;
++              if (*cred) {
++                      put_rpccred(*cred);
++                      *cred = NULL;
++              }
++      }
++      nfs_end_data_update(inode);
+       unlock_kernel();
+       return error;
+ }
+@@ -895,7 +842,19 @@ nfs_wait_on_inode(struct inode *inode, i
+ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
+ {
+       struct inode *inode = dentry->d_inode;
+-      int err = nfs_revalidate_inode(NFS_SERVER(inode), inode);
++      struct nfs_inode *nfsi = NFS_I(inode);
++      int need_atime = nfsi->flags & NFS_INO_INVALID_ATIME;
++      int err;
++
++      if (__IS_FLG(inode, MS_NOATIME))
++              need_atime = 0;
++      else if (__IS_FLG(inode, MS_NODIRATIME) && S_ISDIR(inode->i_mode))
++              need_atime = 0;
++      /* We may force a getattr if the user cares about atime */
++      if (need_atime)
++              err = __nfs_revalidate_inode(NFS_SERVER(inode), inode);
++      else
++              err = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+       if (!err)
+               generic_fillattr(inode, stat);
+       return err;
+@@ -930,8 +889,10 @@ int nfs_open(struct inode *inode, struct
+       auth = NFS_CLIENT(inode)->cl_auth;
+       cred = rpcauth_lookupcred(auth, 0);
+       filp->private_data = cred;
+-      if (filp->f_mode & FMODE_WRITE)
++      if ((filp->f_mode & FMODE_WRITE) != 0) {
+               nfs_set_mmcred(inode, cred);
++              nfs_begin_data_update(inode);
++      }
+       return 0;
+ }
+@@ -940,6 +901,8 @@ int nfs_release(struct inode *inode, str
+       struct rpc_cred *cred;
+       lock_kernel();
++      if ((filp->f_mode & FMODE_WRITE) != 0)
++              nfs_end_data_update(inode);
+       cred = nfs_file_cred(filp);
+       if (cred)
+               put_rpccred(cred);
+@@ -956,6 +919,9 @@ __nfs_revalidate_inode(struct nfs_server
+ {
+       int              status = -ESTALE;
+       struct nfs_fattr fattr;
++      struct nfs_inode *nfsi = NFS_I(inode);
++      unsigned long verifier;
++      unsigned int flags;
+       dfprintk(PAGECACHE, "NFS: revalidating (%s/%Ld)\n",
+               inode->i_sb->s_id, (long long)NFS_FILEID(inode));
+@@ -965,23 +931,22 @@ __nfs_revalidate_inode(struct nfs_server
+               goto out_nowait;
+       if (NFS_STALE(inode) && inode != inode->i_sb->s_root->d_inode)
+               goto out_nowait;
+-      if (NFS_FAKE_ROOT(inode)) {
+-              dfprintk(VFS, "NFS: not revalidating fake root\n");
+-              status = 0;
+-              goto out_nowait;
+-      }
+       while (NFS_REVALIDATING(inode)) {
+               status = nfs_wait_on_inode(inode, NFS_INO_REVALIDATING);
+               if (status < 0)
+                       goto out_nowait;
+-              if (time_before(jiffies,NFS_READTIME(inode)+NFS_ATTRTIMEO(inode))) {
+-                      status = NFS_STALE(inode) ? -ESTALE : 0;
+-                      goto out_nowait;
+-              }
++              if (NFS_SERVER(inode)->flags & NFS_MOUNT_NOAC)
++                      continue;
++              if (NFS_FLAGS(inode) & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ATIME))
++                      continue;
++              status = NFS_STALE(inode) ? -ESTALE : 0;
++              goto out_nowait;
+       }
+       NFS_FLAGS(inode) |= NFS_INO_REVALIDATING;
++      /* Protect against RPC races by saving the change attribute */
++      verifier = nfs_save_change_attribute(inode);
+       status = NFS_PROTO(inode)->getattr(inode, &fattr);
+       if (status) {
+               dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) getattr failed, error=%d\n",
+@@ -995,13 +960,36 @@ __nfs_revalidate_inode(struct nfs_server
+               goto out;
+       }
+-      status = nfs_refresh_inode(inode, &fattr);
++      status = nfs_update_inode(inode, &fattr, verifier);
+       if (status) {
+               dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) refresh failed, error=%d\n",
+                        inode->i_sb->s_id,
+                        (long long)NFS_FILEID(inode), status);
+               goto out;
+       }
++      flags = nfsi->flags;
++      /*
++       * We may need to keep the attributes marked as invalid if
++       * we raced with nfs_end_attr_update().
++       */
++      if (verifier == nfsi->cache_change_attribute)
++              nfsi->flags &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME);
++      /* Do the page cache invalidation */
++      if (flags & NFS_INO_INVALID_DATA) {
++              if (S_ISREG(inode->i_mode)) {
++                      if (filemap_fdatawrite(inode->i_mapping) == 0)
++                              filemap_fdatawait(inode->i_mapping);
++                      nfs_wb_all(inode);
++              }
++              nfsi->flags &= ~NFS_INO_INVALID_DATA;
++              invalidate_inode_pages2(inode->i_mapping);
++              memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
++              dfprintk(PAGECACHE, "NFS: (%s/%Ld) data cache invalidated\n",
++                              inode->i_sb->s_id,
++                              (long long)NFS_FILEID(inode));
++              /* This ensures we revalidate dentries */
++              nfsi->cache_change_attribute++;
++      }
+       dfprintk(PAGECACHE, "NFS: (%s/%Ld) revalidation complete\n",
+               inode->i_sb->s_id,
+               (long long)NFS_FILEID(inode));
+@@ -1009,41 +997,104 @@ __nfs_revalidate_inode(struct nfs_server
+       NFS_FLAGS(inode) &= ~NFS_INO_STALE;
+ out:
+       NFS_FLAGS(inode) &= ~NFS_INO_REVALIDATING;
+-      wake_up(&NFS_I(inode)->nfs_i_wait);
++      wake_up(&nfsi->nfs_i_wait);
+  out_nowait:
+       unlock_kernel();
+       return status;
+ }
+-/*
+- * nfs_fattr_obsolete - Test if attribute data is newer than cached data
+- * @inode: inode
+- * @fattr: attributes to test
++/**
++ * nfs_begin_data_update
++ * @inode - pointer to inode
++ * Declare that a set of operations will update file data on the server
++ */
++void nfs_begin_data_update(struct inode *inode)
++{
++      atomic_inc(&NFS_I(inode)->data_updates);
++}
++
++/**
++ * nfs_end_data_update
++ * @inode - pointer to inode
++ * Declare end of the operations that will update file data
++ */
++void nfs_end_data_update(struct inode *inode)
++{
++      struct nfs_inode *nfsi = NFS_I(inode);
++
++      if (atomic_dec_and_test(&nfsi->data_updates)) {
++              nfsi->cache_change_attribute ++;
++              /* Mark the attribute cache for revalidation */
++              nfsi->flags |= NFS_INO_INVALID_ATTR;
++              /* Directories and symlinks: invalidate page cache too */
++              if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
++                      nfsi->flags |= NFS_INO_INVALID_DATA;
++      }
++}
++
++/**
++ * nfs_refresh_inode - verify consistency of the inode attribute cache
++ * @inode - pointer to inode
++ * @fattr - updated attributes
+  *
+- * Avoid stuffing the attribute cache with obsolete information.
+- * We always accept updates if the attribute cache timed out, or if
+- * fattr->ctime is newer than our cached value.
+- * If fattr->ctime matches the cached value, we still accept the update
+- * if it increases the file size.
++ * Verifies the attribute cache. If we have just changed the attributes,
++ * so that fattr carries weak cache consistency data, then it may
++ * also update the ctime/mtime/change_attribute.
+  */
+-static inline
+-int nfs_fattr_obsolete(struct inode *inode, struct nfs_fattr *fattr)
++int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
+ {
+       struct nfs_inode *nfsi = NFS_I(inode);
+-      long cdif;
++      loff_t cur_size, new_isize;
++      int data_unstable;
++
++      /* Are we in the process of updating data on the server? */
++      data_unstable = nfs_caches_unstable(inode);
++
++      if (fattr->valid & NFS_ATTR_FATTR_V4) {
++              if ((fattr->valid & NFS_ATTR_PRE_CHANGE) != 0
++                              && nfsi->change_attr == fattr->pre_change_attr)
++                      nfsi->change_attr = fattr->change_attr;
++              if (!data_unstable && nfsi->change_attr != fattr->change_attr)
++                      nfsi->flags |= NFS_INO_INVALID_ATTR;
++      }
++
++      if ((fattr->valid & NFS_ATTR_FATTR) == 0)
++              return 0;
++
++      /* Has the inode gone and changed behind our back? */
++      if (nfsi->fileid != fattr->fileid
++                      || (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT))
++              return -EIO;
+-      if (time_after(jiffies, nfsi->read_cache_jiffies + nfsi->attrtimeo))
+-              goto out_valid;
+-      cdif = fattr->ctime.tv_sec - nfsi->read_cache_ctime.tv_sec;
+-      if (cdif == 0)
+-              cdif = fattr->ctime.tv_nsec - nfsi->read_cache_ctime.tv_nsec;
+-      if (cdif > 0)
+-              goto out_valid;
+-      /* Ugh... */
+-      if (cdif == 0 && fattr->size > nfsi->read_cache_isize)
+-              goto out_valid;
+-      return -1;
+- out_valid:
++      cur_size = i_size_read(inode);
++      new_isize = nfs_size_to_loff_t(fattr->size);
++
++      /* If we have atomic WCC data, we may update some attributes */
++      if ((fattr->valid & NFS_ATTR_WCC) != 0) {
++              if (timespec_equal(&inode->i_ctime, &fattr->pre_ctime))
++                      memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
++              if (timespec_equal(&inode->i_mtime, &fattr->pre_mtime))
++                      memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime));
++      }
++
++      /* Verify a few of the more important attributes */
++      if (!data_unstable) {
++              if (!timespec_equal(&inode->i_mtime, &fattr->mtime)
++                              || cur_size != new_isize)
++                      nfsi->flags |= NFS_INO_INVALID_ATTR;
++      } else if (S_ISREG(inode->i_mode) && new_isize > cur_size)
++                      nfsi->flags |= NFS_INO_INVALID_ATTR;
++
++      /* Have any file permissions changed? */
++      if ((inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO)
++                      || inode->i_uid != fattr->uid
++                      || inode->i_gid != fattr->gid)
++              nfsi->flags |= NFS_INO_INVALID_ATTR;
++
++      if (!timespec_equal(&inode->i_atime, &fattr->atime))
++              nfsi->flags |= NFS_INO_INVALID_ATIME;
++
++      nfsi->read_cache_jiffies = fattr->timestamp;
+       return 0;
+ }
+@@ -1059,20 +1110,22 @@ int nfs_fattr_obsolete(struct inode *ino
+  *
+  * A very similar scenario holds for the dir cache.
+  */
+-int
+-__nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
++static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsigned long verifier)
+ {
+       struct nfs_inode *nfsi = NFS_I(inode);
+       __u64           new_size;
+       loff_t          new_isize;
+-      int             invalid = 0;
+-      int             mtime_update = 0;
++      unsigned int    invalid = 0;
+       loff_t          cur_isize;
++      int data_unstable;
+-      dfprintk(VFS, "NFS: refresh_inode(%s/%ld ct=%d info=0x%x)\n",
+-                      inode->i_sb->s_id, inode->i_ino,
++      dfprintk(VFS, "NFS: %s(%s/%ld ct=%d info=0x%x)\n",
++                      __FUNCTION__, inode->i_sb->s_id, inode->i_ino,
+                       atomic_read(&inode->i_count), fattr->valid);
++      if ((fattr->valid & NFS_ATTR_FATTR) == 0)
++              return 0;
++
+       /* First successful call after mount, fill real data. */
+       if (NFS_FAKE_ROOT(inode)) {
+               dfprintk(VFS, "NFS: updating fake root\n");
+@@ -1081,43 +1134,49 @@ __nfs_refresh_inode(struct inode *inode,
+       }
+       if (nfsi->fileid != fattr->fileid) {
+-              printk(KERN_ERR "nfs_refresh_inode: inode number mismatch\n"
++              printk(KERN_ERR "%s: inode number mismatch\n"
+                      "expected (%s/0x%Lx), got (%s/0x%Lx)\n",
++                     __FUNCTION__,
+                      inode->i_sb->s_id, (long long)nfsi->fileid,
+                      inode->i_sb->s_id, (long long)fattr->fileid);
+               goto out_err;
+       }
+-      /* Throw out obsolete READDIRPLUS attributes */
+-      if (time_before(fattr->timestamp, NFS_READTIME(inode)))
+-              return 0;
+       /*
+        * Make sure the inode's type hasn't changed.
+        */
+       if ((inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT))
+               goto out_changed;
+-      new_size = fattr->size;
+-      new_isize = nfs_size_to_loff_t(fattr->size);
+-
+-      /* Avoid races */
+-      if (nfs_fattr_obsolete(inode, fattr))
+-              goto out_nochange;
+-
+       /*
+        * Update the read time so we don't revalidate too often.
+        */
+       nfsi->read_cache_jiffies = fattr->timestamp;
+-      /*
+-       * Note: NFS_CACHE_ISIZE(inode) reflects the state of the cache.
+-       *       NOT inode->i_size!!!
+-       */
+-      if (nfsi->read_cache_isize != new_size) {
++      /* Are we racing with known updates of the metadata on the server? */
++      data_unstable = ! nfs_verify_change_attribute(inode, verifier);
++
++      /* Check if the file size agrees */
++      new_size = fattr->size;
++      new_isize = nfs_size_to_loff_t(fattr->size);
++      cur_isize = i_size_read(inode);
++      if (cur_isize != new_size) {
+ #ifdef NFS_DEBUG_VERBOSE
+               printk(KERN_DEBUG "NFS: isize change on %s/%ld\n", inode->i_sb->s_id, inode->i_ino);
+ #endif
+-              invalid = 1;
++              /*
++               * If we have pending writebacks, things can get
++               * messy.
++               */
++              if (S_ISREG(inode->i_mode) && data_unstable) {
++                      if (new_isize > cur_isize) {
++                              i_size_write(inode, new_isize);
++                              invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
++                      }
++              } else {
++                      i_size_write(inode, new_isize);
++                      invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
++              }
+       }
+       /*
+@@ -1125,12 +1184,13 @@ __nfs_refresh_inode(struct inode *inode,
+        *       can change this value in VFS without requiring a
+        *       cache revalidation.
+        */
+-      if (!timespec_equal(&nfsi->read_cache_mtime, &fattr->mtime)) {
++      if (!timespec_equal(&inode->i_mtime, &fattr->mtime)) {
++              memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime));
+ #ifdef NFS_DEBUG_VERBOSE
+               printk(KERN_DEBUG "NFS: mtime change on %s/%ld\n", inode->i_sb->s_id, inode->i_ino);
+ #endif
+-              invalid = 1;
+-              mtime_update = 1;
++              if (!data_unstable)
++                      invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
+       }
+       if ((fattr->valid & NFS_ATTR_FATTR_V4)
+@@ -1139,47 +1199,15 @@ __nfs_refresh_inode(struct inode *inode,
+               printk(KERN_DEBUG "NFS: change_attr change on %s/%ld\n",
+                      inode->i_sb->s_id, inode->i_ino);
+ #endif
+-              invalid = 1;
+-      }
+-
+-      /* Check Weak Cache Consistency data.
+-       * If size and mtime match the pre-operation values, we can
+-       * assume that any attribute changes were caused by our NFS
+-         * operation, so there's no need to invalidate the caches.
+-         */
+-      if ((fattr->valid & NFS_ATTR_PRE_CHANGE)
+-          && nfsi->change_attr == fattr->pre_change_attr) {
+-              invalid = 0;
+-      }
+-      else if ((fattr->valid & NFS_ATTR_WCC)
+-          && nfsi->read_cache_isize == fattr->pre_size
+-          && timespec_equal(&nfsi->read_cache_mtime, &fattr->pre_mtime)) {
+-              invalid = 0;
+-      }
+-
+-      /*
+-       * If we have pending writebacks, things can get
+-       * messy.
+-       */
+-      cur_isize = i_size_read(inode);
+-      if (nfs_have_writebacks(inode) && new_isize < cur_isize)
+-              new_isize = cur_isize;
+-
+-      nfsi->read_cache_ctime = fattr->ctime;
+-      inode->i_ctime = fattr->ctime;
+-      inode->i_atime = fattr->atime;
+-
+-      if (mtime_update) {
+-              if (invalid)
+-                      nfsi->cache_mtime_jiffies = fattr->timestamp;
+-              nfsi->read_cache_mtime = fattr->mtime;
+-              inode->i_mtime = fattr->mtime;
++              nfsi->change_attr = fattr->change_attr;
++              if (!data_unstable)
++                      invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
+       }
+-      nfsi->read_cache_isize = new_size;
+-      i_size_write(inode, new_isize);
++      memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
++      memcpy(&inode->i_atime, &fattr->atime, sizeof(inode->i_atime));
+-      if (inode->i_mode != fattr->mode ||
++      if ((inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO) ||
+           inode->i_uid != fattr->uid ||
+           inode->i_gid != fattr->gid) {
+               struct rpc_cred **cred = &NFS_I(inode)->cache_access.cred;
+@@ -1187,15 +1215,17 @@ __nfs_refresh_inode(struct inode *inode,
+                       put_rpccred(*cred);
+                       *cred = NULL;
+               }
++              invalid |= NFS_INO_INVALID_ATTR;
+       }
+-      if (fattr->valid & NFS_ATTR_FATTR_V4)
+-              nfsi->change_attr = fattr->change_attr;
+-
+       inode->i_mode = fattr->mode;
+       inode->i_nlink = fattr->nlink;
+       inode->i_uid = fattr->uid;
+       inode->i_gid = fattr->gid;
++#ifdef CONFIG_NFS_V4_ACL
++      nfs4_izap_acl(inode, &nfsi->acl);
++      nfs4_izap_acl(inode, &nfsi->default_acl);
++#endif /* CONFIG_NFS_V4_ACL */
+       if (fattr->valid & (NFS_ATTR_FATTR_V3 | NFS_ATTR_FATTR_V4)) {
+               /*
+@@ -1207,31 +1237,30 @@ __nfs_refresh_inode(struct inode *inode,
+               inode->i_blocks = fattr->du.nfs2.blocks;
+               inode->i_blksize = fattr->du.nfs2.blocksize;
+       }
+- 
+-      /* Update attrtimeo value */
+-      if (invalid) {
++
++      /* Update attrtimeo value if we're out of the unstable period */
++      if (invalid & NFS_INO_INVALID_ATTR) {
+               nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
+               nfsi->attrtimeo_timestamp = jiffies;
+-              invalidate_remote_inode(inode);
+-              memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
+       } else if (time_after(jiffies, nfsi->attrtimeo_timestamp+nfsi->attrtimeo)) {
+               if ((nfsi->attrtimeo <<= 1) > NFS_MAXATTRTIMEO(inode))
+                       nfsi->attrtimeo = NFS_MAXATTRTIMEO(inode);
+               nfsi->attrtimeo_timestamp = jiffies;
+       }
++      /* Don't invalidate the data if we were to blame */
++      if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)
++                              || S_ISLNK(inode->i_mode)))
++              invalid &= ~NFS_INO_INVALID_DATA;
++      nfsi->flags |= invalid;
+       return 0;
+- out_nochange:
+-      if (!timespec_equal(&fattr->atime, &inode->i_atime))
+-              inode->i_atime = fattr->atime;
+-      return 0;
+  out_changed:
+       /*
+        * Big trouble! The inode has become a different object.
+        */
+ #ifdef NFS_PARANOIA
+-      printk(KERN_DEBUG "nfs_refresh_inode: inode %ld mode changed, %07o to %07o\n",
+-             inode->i_ino, inode->i_mode, fattr->mode);
++      printk(KERN_DEBUG "%s: inode %ld mode changed, %07o to %07o\n",
++                      __FUNCTION__, inode->i_ino, inode->i_mode, fattr->mode);
+ #endif
+       /*
+        * No need to worry about unhashing the dentry, as the
+@@ -1355,6 +1384,82 @@ static struct file_system_type nfs_fs_ty
+       .fs_flags       = FS_ODD_RENAME|FS_REVAL_DOT,
+ };
++#ifdef CONFIG_NFS_V4_ACL
++
++int
++nfs_setxattr(struct dentry *dentry, const char *key, const void *buf,
++    size_t buflen, int flags)
++{
++      struct posix_acl *acl;
++      int type, error;
++      struct inode *inode = dentry->d_inode;
++
++      if (strlen(key) == sizeof(XATTR_NAME_ACL_ACCESS) - 1 &&
++          memcmp(key, XATTR_NAME_ACL_ACCESS,
++              sizeof(XATTR_NAME_ACL_ACCESS) - 1) == 0)
++              type = ACL_TYPE_ACCESS;
++      else if (strlen(key) == sizeof(XATTR_NAME_ACL_DEFAULT) - 1 &&
++          memcmp(key, XATTR_NAME_ACL_DEFAULT,
++              sizeof(XATTR_NAME_ACL_ACCESS) - 1) == 0)
++              type = ACL_TYPE_DEFAULT;
++      else
++              return (-EINVAL);
++
++        if (!S_ISREG(inode->i_mode) &&
++            (!S_ISDIR(inode->i_mode) || inode->i_mode & S_ISVTX))
++                return (-EPERM);
++
++      if (type == ACL_TYPE_DEFAULT && !S_ISDIR(inode->i_mode))
++              return -EACCES;
++
++      acl = posix_acl_from_xattr(buf, buflen);
++      if (IS_ERR(acl))
++              return (PTR_ERR(acl));
++      if (acl == NULL)
++              return (-ENODATA);
++
++      error = posix_acl_valid(acl);
++      if (error)
++              goto out_free;
++
++      error = nfs4_proc_set_posix_acl(inode, type, acl);
++out_free:
++      posix_acl_release(acl);
++      return error;
++}
++
++ssize_t
++nfs_getxattr(struct dentry *dentry, const char *key, void *buf,
++    size_t buflen)
++{
++      int type = 0;
++      struct inode *inode = dentry->d_inode;
++      struct posix_acl *acl;
++      ssize_t ret;
++
++      if (strlen(key) == sizeof(XATTR_NAME_ACL_ACCESS) - 1 &&
++          memcmp(key, XATTR_NAME_ACL_ACCESS,
++              sizeof(XATTR_NAME_ACL_ACCESS) - 1) == 0)
++              type = ACL_TYPE_ACCESS;
++      else if (strlen(key) == sizeof(XATTR_NAME_ACL_DEFAULT) - 1 &&
++          memcmp(key, XATTR_NAME_ACL_DEFAULT,
++              sizeof(XATTR_NAME_ACL_ACCESS) - 1) == 0)
++              type = ACL_TYPE_DEFAULT;
++      else
++              return (-EINVAL);
++
++      acl = nfs4_proc_get_posix_acl(inode, type);
++      if (IS_ERR(acl))
++              return (PTR_ERR(acl));
++
++      ret = posix_acl_to_xattr(acl, buf, buflen);
++
++      posix_acl_release(acl);
++      return ret;
++}
++
++#endif /* CONFIG_NFS_V4_ACL */
++
+ #ifdef CONFIG_NFS_V4
+ static void nfs4_clear_inode(struct inode *);
+@@ -1601,7 +1706,7 @@ static struct super_block *nfs4_get_sb(s
+       if (data->version != NFS4_MOUNT_VERSION) {
+               printk("nfs warning: mount version %s than kernel\n",
+-                      data->version < NFS_MOUNT_VERSION ? "older" : "newer");
++                      data->version < NFS4_MOUNT_VERSION ? "older" : "newer");
+       }
+       p = nfs_copy_user_string(NULL, &data->hostname, 256);
+@@ -1699,6 +1804,10 @@ static struct inode *nfs_alloc_inode(str
+               return NULL;
+       nfsi->flags = 0;
+       nfsi->mm_cred = NULL;
++#ifdef CONFIG_NFS_V4_ACL
++      nfsi->acl = NFS4_ACL_NOT_CACHED;
++      nfsi->default_acl = NFS4_ACL_NOT_CACHED;
++#endif /* CONFIG_NFS_V4_ACL */
+       nfs4_zero_state(nfsi);
+       return &nfsi->vfs_inode;
+ }
+@@ -1718,6 +1827,7 @@ static void init_once(void * foo, kmem_c
+               INIT_LIST_HEAD(&nfsi->dirty);
+               INIT_LIST_HEAD(&nfsi->commit);
+               INIT_RADIX_TREE(&nfsi->nfs_page_tree, GFP_ATOMIC);
++              atomic_set(&nfsi->data_updates, 0);
+               nfsi->ndirty = 0;
+               nfsi->ncommit = 0;
+               nfsi->npages = 0;
+diff -puN fs/nfs/nfs3proc.c~CITI_NFS4_ALL fs/nfs/nfs3proc.c
+--- linux-2.6.3/fs/nfs/nfs3proc.c~CITI_NFS4_ALL        2004-02-19 16:47:03.000000000 -0500
++++ linux-2.6.3-bfields/fs/nfs/nfs3proc.c      2004-02-19 16:47:07.000000000 -0500
+@@ -68,20 +68,6 @@ nfs3_async_handle_jukebox(struct rpc_tas
+       return 1;
+ }
+-static void
+-nfs3_write_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
+-{
+-      if (fattr->valid & NFS_ATTR_FATTR) {
+-              if (!(fattr->valid & NFS_ATTR_WCC)) {
+-                      fattr->pre_size  = NFS_CACHE_ISIZE(inode);
+-                      fattr->pre_mtime = NFS_CACHE_MTIME(inode);
+-                      fattr->pre_ctime = NFS_CACHE_CTIME(inode);
+-                      fattr->valid |= NFS_ATTR_WCC;
+-              }
+-              nfs_refresh_inode(inode, fattr);
+-      }
+-}
+-
+ static struct rpc_cred *
+ nfs_cred(struct inode *inode, struct file *filp)
+ {
+@@ -99,14 +85,18 @@ nfs_cred(struct inode *inode, struct fil
+  */
+ static int
+ nfs3_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
+-                 struct nfs_fattr *fattr)
++                 struct nfs_fsinfo *info)
+ {
+       int     status;
+-      dprintk("NFS call  getroot\n");
+-      fattr->valid = 0;
+-      status = rpc_call(server->client, NFS3PROC_GETATTR, fhandle, fattr, 0);
+-      dprintk("NFS reply getroot\n");
++      dprintk("%s: call  fsinfo\n", __FUNCTION__);
++      info->fattr->valid = 0;
++      status = rpc_call(server->client_sys, NFS3PROC_FSINFO, fhandle, info, 0);
++      dprintk("%s: reply fsinfo %d\n", __FUNCTION__, status);
++      if (!(info->fattr->valid & NFS_ATTR_FATTR)) {
++              status = rpc_call(server->client_sys, NFS3PROC_GETATTR, fhandle, info->fattr, 0);
++              dprintk("%s: reply getattr %d\n", __FUNCTION__, status);
++      }
+       return status;
+ }
+@@ -280,7 +270,7 @@ nfs3_proc_write(struct nfs_write_data *w
+       msg.rpc_cred = nfs_cred(inode, filp);
+       status = rpc_call_sync(NFS_CLIENT(inode), &msg, rpcflags);
+       if (status >= 0)
+-              nfs3_write_refresh_inode(inode, fattr);
++              nfs_refresh_inode(inode, fattr);
+       dprintk("NFS reply write: %d\n", status);
+       return status < 0? status : wdata->res.count;
+ }
+@@ -303,7 +293,7 @@ nfs3_proc_commit(struct nfs_write_data *
+       msg.rpc_cred = nfs_cred(inode, filp);
+       status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
+       if (status >= 0)
+-              nfs3_write_refresh_inode(inode, fattr);
++              nfs_refresh_inode(inode, fattr);
+       dprintk("NFS reply commit: %d\n", status);
+       return status;
+ }
+@@ -777,12 +767,13 @@ nfs3_proc_read_setup(struct nfs_read_dat
+ static void
+ nfs3_write_done(struct rpc_task *task)
+ {
+-      struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata;
++      struct nfs_write_data *data;
+       if (nfs3_async_handle_jukebox(task))
+               return;
++      data = (struct nfs_write_data *)task->tk_calldata;
+       if (task->tk_status >= 0)
+-              nfs3_write_refresh_inode(data->inode, data->res.fattr);
++              nfs_refresh_inode(data->inode, data->res.fattr);
+       nfs_writeback_done(task);
+ }
+@@ -835,12 +826,13 @@ nfs3_proc_write_setup(struct nfs_write_d
+ static void
+ nfs3_commit_done(struct rpc_task *task)
+ {
+-      struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata;
++      struct nfs_write_data *data;
+       if (nfs3_async_handle_jukebox(task))
+               return;
++      data = (struct nfs_write_data *)task->tk_calldata;
+       if (task->tk_status >= 0)
+-              nfs3_write_refresh_inode(data->inode, data->res.fattr);
++              nfs_refresh_inode(data->inode, data->res.fattr);
+       nfs_commit_done(task);
+ }
+@@ -907,6 +899,7 @@ struct nfs_rpc_ops nfs_v3_clientops = {
+       .version        = 3,                    /* protocol version */
+       .dentry_ops     = &nfs_dentry_operations,
+       .dir_inode_ops  = &nfs_dir_inode_operations,
++      .file_inode_ops = &nfs_file_inode_operations,
+       .getroot        = nfs3_proc_get_root,
+       .getattr        = nfs3_proc_getattr,
+       .setattr        = nfs3_proc_setattr,
+diff -puN fs/nfs/nfs4proc.c~CITI_NFS4_ALL fs/nfs/nfs4proc.c
+--- linux-2.6.3/fs/nfs/nfs4proc.c~CITI_NFS4_ALL        2004-02-19 16:47:03.000000000 -0500
++++ linux-2.6.3-bfields/fs/nfs/nfs4proc.c      2004-02-19 16:47:15.000000000 -0500
+@@ -46,112 +46,20 @@
+ #include <linux/nfs_page.h>
+ #include <linux/smp_lock.h>
+ #include <linux/namei.h>
++#include <linux/nfs4_acl.h>
++#include <linux/nfs_idmap.h>
+ #define NFSDBG_FACILITY               NFSDBG_PROC
+ #define NFS4_POLL_RETRY_TIME  (15*HZ)
+-#define GET_OP(cp,name)               &cp->ops[cp->req_nops].u.name
+-#define OPNUM(cp)             cp->ops[cp->req_nops].opnum
+-
++static int nfs4_proc_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
+ static int nfs4_async_handle_error(struct rpc_task *, struct nfs_server *);
+ extern u32 *nfs4_decode_dirent(u32 *p, struct nfs_entry *entry, int plus);
+ extern struct rpc_procinfo nfs4_procedures[];
+ extern nfs4_stateid zero_stateid;
+-static void
+-nfs4_setup_compound(struct nfs4_compound *cp, struct nfs4_op *ops,
+-                  struct nfs_server *server, char *tag)
+-{
+-      memset(cp, 0, sizeof(*cp));
+-      cp->ops = ops;
+-      cp->server = server;
+-}
+-
+-static void
+-nfs4_setup_access(struct nfs4_compound *cp, u32 req_access, u32 *resp_supported, u32 *resp_access)
+-{
+-      struct nfs4_access *access = GET_OP(cp, access);
+-      
+-      access->ac_req_access = req_access;
+-      access->ac_resp_supported = resp_supported;
+-      access->ac_resp_access = resp_access;
+-      
+-      OPNUM(cp) = OP_ACCESS;
+-      cp->req_nops++;
+-}
+-
+-static void
+-nfs4_setup_create_dir(struct nfs4_compound *cp, struct qstr *name,
+-                    struct iattr *sattr, struct nfs4_change_info *info)
+-{
+-      struct nfs4_create *create = GET_OP(cp, create);
+-      
+-      create->cr_ftype = NF4DIR;
+-      create->cr_namelen = name->len;
+-      create->cr_name = name->name;
+-      create->cr_attrs = sattr;
+-      create->cr_cinfo = info;
+-      
+-      OPNUM(cp) = OP_CREATE;
+-      cp->req_nops++;
+-}
+-
+-static void
+-nfs4_setup_create_symlink(struct nfs4_compound *cp, struct qstr *name,
+-                        struct qstr *linktext, struct iattr *sattr,
+-                        struct nfs4_change_info *info)
+-{
+-      struct nfs4_create *create = GET_OP(cp, create);
+-
+-      create->cr_ftype = NF4LNK;
+-      create->cr_textlen = linktext->len;
+-      create->cr_text = linktext->name;
+-      create->cr_namelen = name->len;
+-      create->cr_name = name->name;
+-      create->cr_attrs = sattr;
+-      create->cr_cinfo = info;
+-
+-      OPNUM(cp) = OP_CREATE;
+-      cp->req_nops++;
+-}
+-
+-static void
+-nfs4_setup_create_special(struct nfs4_compound *cp, struct qstr *name,
+-                          dev_t dev, struct iattr *sattr,
+-                          struct nfs4_change_info *info)
+-{
+-      int mode = sattr->ia_mode;
+-      struct nfs4_create *create = GET_OP(cp, create);
+-
+-      BUG_ON(!(sattr->ia_valid & ATTR_MODE));
+-      BUG_ON(!S_ISFIFO(mode) && !S_ISBLK(mode) && !S_ISCHR(mode) && !S_ISSOCK(mode));
+-      
+-      if (S_ISFIFO(mode))
+-              create->cr_ftype = NF4FIFO;
+-      else if (S_ISBLK(mode)) {
+-              create->cr_ftype = NF4BLK;
+-              create->cr_specdata1 = MAJOR(dev);
+-              create->cr_specdata2 = MINOR(dev);
+-      }
+-      else if (S_ISCHR(mode)) {
+-              create->cr_ftype = NF4CHR;
+-              create->cr_specdata1 = MAJOR(dev);
+-              create->cr_specdata2 = MINOR(dev);
+-      }
+-      else
+-              create->cr_ftype = NF4SOCK;
+-      
+-      create->cr_namelen = name->len;
+-      create->cr_name = name->name;
+-      create->cr_attrs = sattr;
+-      create->cr_cinfo = info;
+-
+-      OPNUM(cp) = OP_CREATE;
+-      cp->req_nops++;
+-}
+-
+ /*
+  * This is our standard bitmap for GETATTR requests.
+  */
+@@ -181,126 +89,15 @@ u32 nfs4_statfs_bitmap[2] = {
+       | FATTR4_WORD1_SPACE_TOTAL
+ };
+-u32 nfs4_pathconf_bitmap[2] = {
+-      FATTR4_WORD0_MAXLINK
+-      | FATTR4_WORD0_MAXNAME,
+-      0
+-};
+-
+-static inline void
+-__nfs4_setup_getattr(struct nfs4_compound *cp, u32 *bitmap,
+-                   struct nfs_fattr *fattr,
+-                   struct nfs_fsstat *fsstat,
+-                   struct nfs_pathconf *pathconf)
+-{
+-        struct nfs4_getattr *getattr = GET_OP(cp, getattr);
+-
+-        getattr->gt_bmval = bitmap;
+-        getattr->gt_attrs = fattr;
+-      getattr->gt_fsstat = fsstat;
+-      getattr->gt_pathconf = pathconf;
+-
+-        OPNUM(cp) = OP_GETATTR;
+-        cp->req_nops++;
+-}
+-
+-static void
+-nfs4_setup_getattr(struct nfs4_compound *cp,
+-              struct nfs_fattr *fattr)
+-{
+-      __nfs4_setup_getattr(cp, nfs4_fattr_bitmap, fattr,
+-                      NULL, NULL);
+-}
+-
+-static void
+-nfs4_setup_statfs(struct nfs4_compound *cp,
+-              struct nfs_fsstat *fsstat)
+-{
+-      __nfs4_setup_getattr(cp, nfs4_statfs_bitmap,
+-                      NULL, fsstat, NULL);
+-}
+-
+-static void
+-nfs4_setup_pathconf(struct nfs4_compound *cp,
+-              struct nfs_pathconf *pathconf)
+-{
+-      __nfs4_setup_getattr(cp, nfs4_pathconf_bitmap,
+-                      NULL, NULL, pathconf);
+-}
+-
+-static void
+-nfs4_setup_getfh(struct nfs4_compound *cp, struct nfs_fh *fhandle)
+-{
+-      struct nfs4_getfh *getfh = GET_OP(cp, getfh);
+-
+-      getfh->gf_fhandle = fhandle;
+-
+-      OPNUM(cp) = OP_GETFH;
+-      cp->req_nops++;
+-}
+-
+-static void
+-nfs4_setup_link(struct nfs4_compound *cp, struct qstr *name,
+-              struct nfs4_change_info *info)
+-{
+-      struct nfs4_link *link = GET_OP(cp, link);
+-
+-      link->ln_namelen = name->len;
+-      link->ln_name = name->name;
+-      link->ln_cinfo = info;
+-
+-      OPNUM(cp) = OP_LINK;
+-      cp->req_nops++;
+-}
+-
+ static void
+-nfs4_setup_lookup(struct nfs4_compound *cp, struct qstr *q)
+-{
+-      struct nfs4_lookup *lookup = GET_OP(cp, lookup);
+-
+-      lookup->lo_name = q;
+-
+-      OPNUM(cp) = OP_LOOKUP;
+-      cp->req_nops++;
+-}
+-
+-static void
+-nfs4_setup_putfh(struct nfs4_compound *cp, struct nfs_fh *fhandle)
+-{
+-      struct nfs4_putfh *putfh = GET_OP(cp, putfh);
+-
+-      putfh->pf_fhandle = fhandle;
+-
+-      OPNUM(cp) = OP_PUTFH;
+-      cp->req_nops++;
+-}
+-
+-static void
+-nfs4_setup_putrootfh(struct nfs4_compound *cp)
+-{
+-        OPNUM(cp) = OP_PUTROOTFH;
+-        cp->req_nops++;
+-}
+-
+-static void
+-nfs4_setup_readdir(struct nfs4_compound *cp, u64 cookie, u32 *verifier,
+-                   struct page **pages, unsigned int bufsize, struct dentry *dentry)
++nfs4_setup_readdir(u64 cookie, u32 *verifier, struct dentry *dentry, struct nfs4_readdir_arg *readdir)
+ {
+       u32 *start, *p;
+-      struct nfs4_readdir *readdir = GET_OP(cp, readdir);
+-      BUG_ON(bufsize < 80);
+-      readdir->rd_cookie = (cookie > 2) ? cookie : 0;
+-      memcpy(&readdir->rd_req_verifier, verifier, sizeof(readdir->rd_req_verifier));
+-      readdir->rd_count = bufsize;
+-      readdir->rd_bmval[0] = FATTR4_WORD0_FILEID;
+-      readdir->rd_bmval[1] = 0;
+-      readdir->rd_pages = pages;
+-      readdir->rd_pgbase = 0;
++      BUG_ON(readdir->count < 80);
++      readdir->cookie = (cookie > 2) ? cookie : 0;
++      memcpy(&readdir->req_verifier, verifier, sizeof(readdir->req_verifier));
+       
+-      OPNUM(cp) = OP_READDIR;
+-      cp->req_nops++;
+-
+       if (cookie >= 2)
+               return;
+       
+@@ -311,7 +108,7 @@ nfs4_setup_readdir(struct nfs4_compound 
+        * when talking to the server, we always send cookie 0
+        * instead of 1 or 2.
+        */
+-      start = p = (u32 *)kmap_atomic(*pages, KM_USER0);
++      start = p = (u32 *)kmap_atomic(*readdir->pages, KM_USER0);
+       
+       if (cookie == 0) {
+               *p++ = xdr_one;                                  /* next */
+@@ -337,68 +134,12 @@ nfs4_setup_readdir(struct nfs4_compound 
+       *p++ = htonl(8);              /* attribute buffer length */
+       p = xdr_encode_hyper(p, NFS_FILEID(dentry->d_parent->d_inode));
+-      readdir->rd_pgbase = (char *)p - (char *)start;
+-      readdir->rd_count -= readdir->rd_pgbase;
++      readdir->pgbase = (char *)p - (char *)start;
++      readdir->count -= readdir->pgbase;
+       kunmap_atomic(start, KM_USER0);
+ }
+ static void
+-nfs4_setup_readlink(struct nfs4_compound *cp, int count, struct page **pages)
+-{
+-      struct nfs4_readlink *readlink = GET_OP(cp, readlink);
+-
+-      readlink->rl_count = count;
+-      readlink->rl_pages = pages;
+-
+-      OPNUM(cp) = OP_READLINK;
+-      cp->req_nops++;
+-}
+-
+-static void
+-nfs4_setup_remove(struct nfs4_compound *cp, struct qstr *name, struct nfs4_change_info *cinfo)
+-{
+-      struct nfs4_remove *remove = GET_OP(cp, remove);
+-
+-      remove->rm_namelen = name->len;
+-      remove->rm_name = name->name;
+-      remove->rm_cinfo = cinfo;
+-
+-      OPNUM(cp) = OP_REMOVE;
+-      cp->req_nops++;
+-}
+-
+-static void
+-nfs4_setup_rename(struct nfs4_compound *cp, struct qstr *old, struct qstr *new,
+-                struct nfs4_change_info *old_cinfo, struct nfs4_change_info *new_cinfo)
+-{
+-      struct nfs4_rename *rename = GET_OP(cp, rename);
+-
+-      rename->rn_oldnamelen = old->len;
+-      rename->rn_oldname = old->name;
+-      rename->rn_newnamelen = new->len;
+-      rename->rn_newname = new->name;
+-      rename->rn_src_cinfo = old_cinfo;
+-      rename->rn_dst_cinfo = new_cinfo;
+-
+-      OPNUM(cp) = OP_RENAME;
+-      cp->req_nops++;
+-}
+-
+-static void
+-nfs4_setup_restorefh(struct nfs4_compound *cp)
+-{
+-        OPNUM(cp) = OP_RESTOREFH;
+-        cp->req_nops++;
+-}
+-
+-static void
+-nfs4_setup_savefh(struct nfs4_compound *cp)
+-{
+-        OPNUM(cp) = OP_SAVEFH;
+-        cp->req_nops++;
+-}
+-
+-static void
+ renew_lease(struct nfs_server *server, unsigned long timestamp)
+ {
+       struct nfs4_client *clp = server->nfs4_state;
+@@ -409,47 +150,6 @@ renew_lease(struct nfs_server *server, u
+ }
+ static inline void
+-process_lease(struct nfs4_compound *cp)
+-{
+-        /*
+-         * 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.)
+-       *
+-       * Notes:
+-       *   (1) renewd doesn't acquire the spinlock when messing with
+-       *     server->last_renewal; this is OK since rpciod always runs
+-       *     under the BKL.
+-       *   (2) cp->timestamp was set at the end of XDR encode.
+-         */
+-      if (!cp->renew_index)
+-              return;
+-      if (!cp->toplevel_status || cp->resp_nops > cp->renew_index)
+-              renew_lease(cp->server, cp->timestamp);
+-}
+-
+-static int
+-nfs4_call_compound(struct nfs4_compound *cp, struct rpc_cred *cred, int flags)
+-{
+-      int status;
+-      struct rpc_message msg = {
+-              .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COMPOUND],
+-              .rpc_argp = cp,
+-              .rpc_resp = cp,
+-              .rpc_cred = cred,
+-      };
+-
+-      status = rpc_call_sync(cp->server->client, &msg, flags);
+-      if (!status)
+-              process_lease(cp);
+-      
+-      return status;
+-}
+-
+-static inline void
+ process_cinfo(struct nfs4_change_info *info, struct nfs_fattr *fattr)
+ {
+       BUG_ON((fattr->valid & NFS_ATTR_FATTR) == 0);
+@@ -476,11 +176,6 @@ nfs4_open_reclaim(struct nfs4_state_owne
+               .valid = 0,
+       };
+       struct nfs4_change_info d_cinfo;
+-      struct nfs4_getattr     f_getattr = {
+-              .gt_bmval       = nfs4_fattr_bitmap,
+-              .gt_attrs       = &fattr,
+-      };
+-
+       struct nfs_open_reclaimargs o_arg = {
+               .fh = NFS_FH(inode),
+               .seqid = sp->so_seqid,
+@@ -488,11 +183,10 @@ nfs4_open_reclaim(struct nfs4_state_owne
+               .share_access = state->state,
+               .clientid = server->nfs4_state->cl_clientid,
+               .claim = NFS4_OPEN_CLAIM_PREVIOUS,
+-              .f_getattr = &f_getattr,
+       };
+       struct nfs_openres o_res = {
+-              .cinfo = &d_cinfo,
+-              .f_getattr = &f_getattr,
++              .cinfo  = &d_cinfo,
++              .f_attr = &fattr,
+               .server = server,       /* Grrr */
+       };
+       struct rpc_message msg = {
+@@ -528,28 +222,18 @@ nfs4_do_open(struct inode *dir, struct q
+       struct nfs_fattr        f_attr = {
+               .valid          = 0,
+       };
+-      struct nfs4_getattr     f_getattr = {
+-              .gt_bmval       = nfs4_fattr_bitmap,
+-              .gt_attrs       = &f_attr,
+-      };
+-      struct nfs4_getattr     d_getattr = {
+-              .gt_bmval       = nfs4_fattr_bitmap,
+-              .gt_attrs       = &d_attr,
+-      };
+       struct nfs_openargs o_arg = {
+               .fh             = NFS_FH(dir),
+               .share_access   = flags & (FMODE_READ|FMODE_WRITE),
+               .opentype       = (flags & O_CREAT) ? NFS4_OPEN_CREATE : NFS4_OPEN_NOCREATE,
+               .createmode     = (flags & O_EXCL) ? NFS4_CREATE_EXCLUSIVE : NFS4_CREATE_UNCHECKED,
+               .name           = name,
+-              .f_getattr      = &f_getattr,
+-              .d_getattr      = &d_getattr,
+               .server         = server,
+       };
+       struct nfs_openres o_res = {
+               .cinfo          = &d_cinfo,
+-              .f_getattr      = &f_getattr,
+-              .d_getattr      = &d_getattr,
++              .f_attr         = &f_attr,
++              .d_attr         = &d_attr,
+               .server         = server,
+       };
+       struct rpc_message msg = {
+@@ -665,18 +349,14 @@ nfs4_do_setattr(struct nfs_server *serve
+                 struct nfs_fh *fhandle, struct iattr *sattr,
+                 struct nfs4_state *state)
+ {
+-        struct nfs4_getattr     getattr = {
+-                .gt_bmval       = nfs4_fattr_bitmap,
+-                .gt_attrs       = fattr,
+-        };
+         struct nfs_setattrargs  arg = {
+                 .fh             = fhandle,
+                 .iap            = sattr,
+-                .attr           = &getattr,
++                .fattr          = fattr,
+               .server         = server,
+         };
+         struct nfs_setattrres  res = {
+-                .attr           = &getattr,
++                .fattr          = fattr,
+               .server         = server,
+         };
+         struct rpc_message msg = {
+@@ -822,27 +502,43 @@ nfs4_open_revalidate(struct inode *dir, 
+ static int
+ nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
+-                 struct nfs_fattr *fattr)
++                 struct nfs_fsinfo *info)
+ {
+-      struct nfs4_compound    compound;
+-      struct nfs4_op          ops[4];
++      struct nfs_fattr *      fattr = info->fattr;
+       unsigned char *         p;
+       struct qstr             q;
+       int                     status;
++      struct nfs4_getroot_arg args = {
++              .fhandle = fhandle,
++              .name = &q,
++      };
++      struct nfs4_getroot_res res = {
++              .server = server,
++              .fattr = fattr,
++              .fhandle = fhandle,
++      };
++      struct rpc_message msg_head = {
++              .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETROOT_HEAD],
++              .rpc_argp = NULL,
++              .rpc_resp = &res,
++      };
++      struct rpc_message msg_path = {
++              .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETROOT_PATH],
++              .rpc_argp = &args,
++              .rpc_resp = &res,
++      };
+       /*
+        * Now we do a separate LOOKUP for each component of the mount path.
+        * The LOOKUPs are done separately so that we can conveniently
+        * catch an ERR_WRONGSEC if it occurs along the way...
+        */
+-      p = server->mnt_path;
+       fattr->valid = 0;
+-      nfs4_setup_compound(&compound, ops, server, "getrootfh");
+-      nfs4_setup_putrootfh(&compound);
+-      nfs4_setup_getattr(&compound, fattr);
+-      nfs4_setup_getfh(&compound, fhandle);
+-      if ((status = nfs4_call_compound(&compound, NULL, 0)))
++      status = rpc_call_sync(server->client, &msg_head, 0);
++      if (status)
+               goto out;
++
++      p = server->mnt_path;
+       for (;;) {
+               while (*p == '/')
+                       p++;
+@@ -854,12 +550,7 @@ nfs4_proc_get_root(struct nfs_server *se
+               q.len = p - q.name;
+               fattr->valid = 0;
+-              nfs4_setup_compound(&compound, ops, server, "mount");
+-              nfs4_setup_putfh(&compound, fhandle);
+-              nfs4_setup_lookup(&compound, &q);
+-              nfs4_setup_getattr(&compound, fattr);
+-              nfs4_setup_getfh(&compound, fhandle);
+-              status = nfs4_call_compound(&compound, NULL, 0);
++              status = rpc_call_sync(server->client,&msg_path,0);
+               if (!status)
+                       continue;
+               if (status == -ENOENT) {
+@@ -869,21 +560,27 @@ nfs4_proc_get_root(struct nfs_server *se
+               break;
+       }
+ out:
+-      return status;
++      if (status)
++              return status;
++      return nfs4_proc_fsinfo(server, fhandle, info);
+ }
+ static int
+ nfs4_proc_getattr(struct inode *inode, struct nfs_fattr *fattr)
+ {
+-      struct nfs4_compound compound;
+-      struct nfs4_op ops[2];
+-
++      struct nfs4_getattr_res res = {
++              .fattr = fattr,
++              .server = NFS_SERVER(inode),
++      };
++      struct rpc_message msg = {
++              .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETATTR],
++              .rpc_argp = NFS_FH(inode),
++              .rpc_resp = &res,
++      };
++      
+       fattr->valid = 0;
+-      nfs4_setup_compound(&compound, ops, NFS_SERVER(inode), "getattr");
+-      nfs4_setup_putfh(&compound, NFS_FH(inode));
+-      nfs4_setup_getattr(&compound, fattr);
+-      return nfs4_call_compound(&compound, NULL, 0);
++      return rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
+ }
+ /* 
+@@ -945,26 +642,218 @@ out:
+       return status;
+ }
++#ifdef CONFIG_NFS_V4_ACL
++
++static inline int
++nfs_name_to_uid_wrapper(void *arg, const char *name, size_t len, __u32 *id)
++{
++      return nfs_map_name_to_uid((struct nfs4_client *)arg, name, len, id);
++}
++
++static inline int
++nfs_name_to_gid_wrapper(void *arg, const char *name, size_t len, __u32 *id)
++{
++      return nfs_map_group_to_gid((struct nfs4_client*)arg, name, len, id);
++}
++
++static inline int
++nfs_uid_to_name_wrapper(void *arg, __u32 id, char *name)
++{
++      return nfs_map_uid_to_name((struct nfs4_client *)arg, id, name);
++}
++
++static inline int
++nfs_gid_to_name_wrapper(void *arg, __u32 id, char *name)
++{
++      return nfs_map_gid_to_group((struct nfs4_client *)arg, id, name);
++}
++
++static struct nfs4_acl_idmapper nfs4_idmapper = {
++      .name2uid = nfs_name_to_uid_wrapper,
++      .name2gid = nfs_name_to_gid_wrapper,
++      .uid2name = nfs_uid_to_name_wrapper,
++      .gid2name = nfs_gid_to_name_wrapper,
++};
++
++/* From fs/ext2/acl.c: */
++
++static inline struct posix_acl *
++nfs4_iget_acl(struct inode *inode, struct posix_acl **i_acl)
++{
++      struct posix_acl *acl = NFS4_ACL_NOT_CACHED;
++
++      spin_lock(&inode->i_lock);
++      if (*i_acl != NFS4_ACL_NOT_CACHED)
++              acl = posix_acl_dup(*i_acl);
++      spin_unlock(&inode->i_lock);
++      return acl;
++}
++
++void
++nfs4_iset_acl(struct inode *inode, struct posix_acl **i_acl,
++              struct posix_acl *acl)
++{
++      spin_lock(&inode->i_lock);
++      if (*i_acl != NFS4_ACL_NOT_CACHED)
++              posix_acl_release(*i_acl);
++      *i_acl = posix_acl_dup(acl);
++      spin_unlock(&inode->i_lock);
++}
++
++void
++nfs4_izap_acl(struct inode *inode, struct posix_acl **i_acl)
++{
++      spin_lock(&inode->i_lock);
++      if (*i_acl != NFS4_ACL_NOT_CACHED)
++              posix_acl_release(*i_acl);
++      *i_acl = NFS4_ACL_NOT_CACHED;
++      spin_unlock(&inode->i_lock);
++}
++
++struct posix_acl *
++nfs4_proc_get_posix_acl(struct inode *inode, int type)
++{
++      struct nfs4_acl *acl = NULL;
++      int error;
++      struct posix_acl *pacl, *dpacl, *ret = NULL;
++      struct rpc_message msg = {
++              .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETACL],
++              .rpc_argp = NFS_FH(inode),
++              .rpc_resp = &acl,
++      };
++
++      lock_kernel();
++      error = nfs_revalidate_inode(NFS_SERVER(inode), inode);
++      if (error < 0) {
++              unlock_kernel();
++              return ERR_PTR(error);
++      }
++      if (type == ACL_TYPE_ACCESS)
++              ret = nfs4_iget_acl(inode, &NFS_I(inode)->acl);
++      else
++              ret = nfs4_iget_acl(inode, &NFS_I(inode)->default_acl);
++
++      if (ret != NFS4_ACL_NOT_CACHED) {
++              if (ret == NULL)
++                      ret = ERR_PTR(-ENODATA);
++              unlock_kernel();
++              return ret;
++      }
++
++      error = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
++      unlock_kernel();
++
++      if (error < 0)
++              goto out_free;
++
++      error = -ENODATA;
++      if (acl == NULL)
++              goto out_free;
++
++      error = nfs4_acl_nfsv4_to_posix(&nfs4_idmapper, NFS_SERVER(inode)->nfs4_state, acl, &pacl, &dpacl);
++      if (error < 0)
++              goto out_free;
++
++      error = -ERANGE;
++      if (pacl && pacl->a_count > NFS_ACL_MAX_ENTRIES)
++              goto out_free;
++      if (dpacl && dpacl->a_count > NFS_ACL_MAX_ENTRIES)
++              goto out_free;
++
++      nfs4_iset_acl(inode, &NFS_I(inode)->acl, pacl);
++      nfs4_iset_acl(inode, &NFS_I(inode)->default_acl, dpacl);
++
++      ret = (type == ACL_TYPE_ACCESS) ? pacl : dpacl;
++      error = -ENODATA;
++      if (ret == NULL)
++              goto out_free;
++      error = 0;
++out_free:
++      if (error < 0)
++              ret = ERR_PTR(error);
++      nfs4_acl_free(acl);
++      return ret;
++}
++
++int
++nfs4_proc_set_posix_acl(struct inode *inode, int type, struct posix_acl *pacl)
++{
++      struct iattr ia;
++      struct nfs4_acl *acl;
++      struct nfs_fattr fattr;
++      int error;
++      struct nfs_setaclargs arg = {
++              .fh             = NFS_FH(inode),
++      };
++      struct rpc_message msg = {
++              .rpc_proc       = &nfs4_procedures[NFSPROC4_CLNT_SETACL],
++              .rpc_argp       = &arg,
++              .rpc_resp       = NULL,
++      };
++
++      ia.ia_valid = 0;
++        fattr.valid = 0;
++
++      if (pacl && pacl->a_count > NFS_ACL_MAX_ENTRIES)
++              return -ERANGE;
++
++      if (type == ACL_TYPE_ACCESS)
++              acl = nfs4_acl_posix_to_nfsv4(&nfs4_idmapper, NFS_SERVER(inode)->nfs4_state, pacl, NULL);
++      else
++              acl = nfs4_acl_posix_to_nfsv4(&nfs4_idmapper, NFS_SERVER(inode)->nfs4_state, NULL, pacl);
++      if (IS_ERR(acl))
++              return PTR_ERR(acl);
++      arg.acl = acl;
++
++      lock_kernel();
++      error = rpc_call_sync(NFS_SERVER(inode)->client, &msg, 0);
++      unlock_kernel();
++
++      nfs4_acl_free(acl);
++
++      if (error)
++              return error;
++
++      if (type == ACL_TYPE_ACCESS)
++              nfs4_iset_acl(inode, &NFS_I(inode)->acl, pacl);
++      else
++              nfs4_iset_acl(inode, &NFS_I(inode)->default_acl, pacl);
++
++      if (type == ACL_TYPE_ACCESS)
++              posix_acl_equiv_mode(pacl, &inode->i_mode);
++
++      return error;
++}
++
++#endif /* CONFIG_NFS_V4_ACL */
++
+ static int
+ nfs4_proc_lookup(struct inode *dir, struct qstr *name,
+                struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+ {
+-      struct nfs4_compound    compound;
+-      struct nfs4_op          ops[5];
+-      struct nfs_fattr        dir_attr;
+-      int                     status;
+-
++      struct nfs_fattr       dir_attr;
++      int                    status;
++      struct nfs4_lookupargs args = {
++              .dir_fh   = NFS_FH(dir),
++              .name     = name,
++      };
++      struct nfs4_lookupres  res = {
++              .server   = NFS_SERVER(dir),
++              .dirattr  = &dir_attr,
++              .fattr    = fattr,
++              .fhandle  = fhandle,
++      };
++      struct rpc_message     msg = {
++              .rpc_proc  = &nfs4_procedures[NFSPROC4_CLNT_LOOKUP],
++              .rpc_argp  = &args,
++              .rpc_resp  = &res,
++      };
++      
+       dir_attr.valid = 0;
+       fattr->valid = 0;
+       
+       dprintk("NFS call  lookup %s\n", name->name);
+-      nfs4_setup_compound(&compound, ops, NFS_SERVER(dir), "lookup");
+-      nfs4_setup_putfh(&compound, NFS_FH(dir));
+-      nfs4_setup_getattr(&compound, &dir_attr);
+-      nfs4_setup_lookup(&compound, name);
+-      nfs4_setup_getattr(&compound, fattr);
+-      nfs4_setup_getfh(&compound, fhandle);
+-      status = nfs4_call_compound(&compound, NULL, 0);
++      status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
+       dprintk("NFS reply lookup: %d\n", status);
+       if (status >= 0)
+@@ -975,11 +864,24 @@ nfs4_proc_lookup(struct inode *dir, stru
+ static int
+ nfs4_proc_access(struct inode *inode, struct rpc_cred *cred, int mode)
+ {
+-      struct nfs4_compound    compound;
+-      struct nfs4_op          ops[3];
+       struct nfs_fattr        fattr;
+       u32                     req_access = 0, resp_supported, resp_access;
+       int                     status;
++      struct nfs4_accessargs args = {
++              .fhandle        = NFS_FH(inode),
++      };
++      struct nfs4_accessres res = {
++              .server         = NFS_SERVER(inode),
++              .fattr          = &fattr,
++              .resp_supported  = &resp_supported,
++              .resp_access    = &resp_access,
++      };
++      struct rpc_message msg = {
++              .rpc_proc       = &nfs4_procedures[NFSPROC4_CLNT_ACCESS],
++              .rpc_argp       = &args,
++              .rpc_resp       = &res,
++              .rpc_cred       = cred,
++      };
+       fattr.valid = 0;
+@@ -1000,12 +902,9 @@ nfs4_proc_access(struct inode *inode, st
+               if (mode & MAY_EXEC)
+                       req_access |= NFS4_ACCESS_EXECUTE;
+       }
++      res.req_access = args.req_access = req_access;
+-      nfs4_setup_compound(&compound, ops, NFS_SERVER(inode), "access");
+-      nfs4_setup_putfh(&compound, NFS_FH(inode));
+-      nfs4_setup_getattr(&compound, &fattr);
+-      nfs4_setup_access(&compound, req_access, &resp_supported, &resp_access);
+-      status = nfs4_call_compound(&compound, cred, 0);
++      status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
+       nfs_refresh_inode(inode, &fattr);
+       if (!status) {
+@@ -1046,13 +945,18 @@ nfs4_proc_access(struct inode *inode, st
+ static int
+ nfs4_proc_readlink(struct inode *inode, struct page *page)
+ {
+-      struct nfs4_compound    compound;
+-      struct nfs4_op          ops[2];
++      struct nfs4_readlink args = {
++              .fh       = NFS_FH(inode),
++              .count    = PAGE_CACHE_SIZE,
++              .pages    = &page,
++      };
++      struct rpc_message msg = {
++              .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READLINK],
++              .rpc_argp = &args,
++              .rpc_resp = NULL,
++      };
+-      nfs4_setup_compound(&compound, ops, NFS_SERVER(inode), "readlink");
+-      nfs4_setup_putfh(&compound, NFS_FH(inode));
+-      nfs4_setup_readlink(&compound, PAGE_CACHE_SIZE, &page);
+-      return nfs4_call_compound(&compound, NULL, 0);
++      return rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
+ }
+ static int
+@@ -1088,12 +992,8 @@ nfs4_proc_read(struct nfs_read_data *rda
+       fattr->valid = 0;
+       status = rpc_call_sync(server->client, &msg, flags);
+-      if (!status) {
++      if (!status)
+               renew_lease(server, timestamp);
+-              /* Check cache consistency */
+-              if (fattr->change_attr != NFS_CHANGE_ATTR(inode))
+-                      nfs_zap_caches(inode);
+-      }
+       dprintk("NFS reply read: %d\n", status);
+       return status;
+ }
+@@ -1130,7 +1030,6 @@ nfs4_proc_write(struct nfs_write_data *w
+       fattr->valid = 0;
+       status = rpc_call_sync(server->client, &msg, rpcflags);
+-      NFS_CACHEINV(inode);
+       dprintk("NFS reply write: %d\n", status);
+       return status;
+ }
+@@ -1217,18 +1116,26 @@ nfs4_proc_create(struct inode *dir, stru
+ static int
+ nfs4_proc_remove(struct inode *dir, struct qstr *name)
+ {
+-      struct nfs4_compound    compound;
+-      struct nfs4_op          ops[3];
+       struct nfs4_change_info dir_cinfo;
+       struct nfs_fattr        dir_attr;
+       int                     status;
++      struct nfs4_remove_arg args = {
++              .fhandle = NFS_FH(dir),
++              .name = name,
++      };
++      struct nfs4_remove_res res = {
++              .server = NFS_SERVER(dir),
++              .dir_cinfo = &dir_cinfo,
++              .dir_attr = &dir_attr,
++      };
++      struct rpc_message msg = {
++              .rpc_proc       = &nfs4_procedures[NFSPROC4_CLNT_REMOVE],
++              .rpc_argp       = &args,
++              .rpc_resp       = &res,
++      };
+       dir_attr.valid = 0;
+-      nfs4_setup_compound(&compound, ops, NFS_SERVER(dir), "remove");
+-      nfs4_setup_putfh(&compound, NFS_FH(dir));
+-      nfs4_setup_remove(&compound, name, &dir_cinfo);
+-      nfs4_setup_getattr(&compound, &dir_attr);
+-      status = nfs4_call_compound(&compound, NULL, 0);
++      status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
+       if (!status) {
+               process_cinfo(&dir_cinfo, &dir_attr);
+@@ -1237,32 +1144,22 @@ nfs4_proc_remove(struct inode *dir, stru
+       return status;
+ }
+-struct unlink_desc {
+-      struct nfs4_compound    compound;
+-      struct nfs4_op          ops[3];
+-      struct nfs4_change_info cinfo;
+-      struct nfs_fattr        attrs;
+-};
+-
+ static int
+ nfs4_proc_unlink_setup(struct rpc_message *msg, struct dentry *dir, struct qstr *name)
+ {
+-      struct unlink_desc *    up;
+-      struct nfs4_compound *  cp;
++      struct nfs4_unlink *up;
+-      up = (struct unlink_desc *) kmalloc(sizeof(*up), GFP_KERNEL);
++      up = (struct nfs4_unlink *) kmalloc(sizeof(*up), GFP_KERNEL);
+       if (!up)
+               return -ENOMEM;
+-      cp = &up->compound;
+       
+-      nfs4_setup_compound(cp, up->ops, NFS_SERVER(dir->d_inode), "unlink_setup");
+-      nfs4_setup_putfh(cp, NFS_FH(dir->d_inode));
+-      nfs4_setup_remove(cp, name, &up->cinfo);
+-      nfs4_setup_getattr(cp, &up->attrs);
+-      
+-      msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COMPOUND];
+-      msg->rpc_argp = cp;
+-      msg->rpc_resp = cp;
++      up->server = NFS_SERVER(dir->d_inode);
++      up->fh     = NFS_FH(dir->d_inode);
++      up->name   = name;
++      
++      msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_UNLINK];
++      msg->rpc_argp = up;
++      msg->rpc_resp = up;
+       return 0;
+ }
+@@ -1270,11 +1167,10 @@ static int
+ nfs4_proc_unlink_done(struct dentry *dir, struct rpc_task *task)
+ {
+       struct rpc_message *msg = &task->tk_msg;
+-      struct unlink_desc *up;
++      struct nfs4_unlink *up;
+       
+       if (msg->rpc_argp) {
+-              up = (struct unlink_desc *) msg->rpc_argp;
+-              process_lease(&up->compound);
++              up = (struct nfs4_unlink *) msg->rpc_argp;
+               process_cinfo(&up->cinfo, &up->attrs);
+               nfs_refresh_inode(dir->d_inode, &up->attrs);
+               kfree(up);
+@@ -1287,24 +1183,32 @@ static int
+ nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name,
+                struct inode *new_dir, struct qstr *new_name)
+ {
+-      struct nfs4_compound    compound;
+-      struct nfs4_op          ops[7];
+       struct nfs4_change_info old_cinfo, new_cinfo;
+       struct nfs_fattr        old_dir_attr, new_dir_attr;
+       int                     status;
+-
++      struct nfs4_rename_arg arg = {
++              .old_dir = NFS_FH(old_dir),
++              .new_dir = NFS_FH(new_dir),
++              .old_name = old_name,
++              .new_name = new_name,
++      };
++      struct nfs4_rename_res res = {
++              .server = NFS_SERVER(old_dir),
++              .old_cinfo = &old_cinfo,
++              .new_cinfo = &new_cinfo,
++              .old_fattr = &old_dir_attr,
++              .new_fattr = &new_dir_attr,
++      };
++        struct rpc_message msg = {
++                .rpc_proc       = &nfs4_procedures[NFSPROC4_CLNT_RENAME],
++                .rpc_argp       = &arg,
++                .rpc_resp       = &res,
++        };
++      
+       old_dir_attr.valid = 0;
+       new_dir_attr.valid = 0;
+       
+-      nfs4_setup_compound(&compound, ops, NFS_SERVER(old_dir), "rename");
+-      nfs4_setup_putfh(&compound, NFS_FH(old_dir));
+-      nfs4_setup_savefh(&compound);
+-      nfs4_setup_putfh(&compound, NFS_FH(new_dir));
+-      nfs4_setup_rename(&compound, old_name, new_name, &old_cinfo, &new_cinfo);
+-      nfs4_setup_getattr(&compound, &new_dir_attr);
+-      nfs4_setup_restorefh(&compound);
+-      nfs4_setup_getattr(&compound, &old_dir_attr);
+-      status = nfs4_call_compound(&compound, NULL, 0);
++      status = rpc_call_sync(NFS_CLIENT(old_dir), &msg, 0);
+       if (!status) {
+               process_cinfo(&old_cinfo, &old_dir_attr);
+@@ -1318,24 +1222,30 @@ nfs4_proc_rename(struct inode *old_dir, 
+ static int
+ nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *name)
+ {
+-      struct nfs4_compound    compound;
+-      struct nfs4_op          ops[7];
+       struct nfs4_change_info dir_cinfo;
+       struct nfs_fattr        dir_attr, fattr;
+       int                     status;
+-      
++      struct nfs4_link_arg arg = {
++              .fh     = NFS_FH(inode),
++              .dir_fh = NFS_FH(dir),
++              .name   = name,
++      };
++      struct nfs4_link_res res = {
++              .server    = NFS_SERVER(inode),
++              .fattr     = &fattr,
++              .dir_attr  = &dir_attr,
++              .dir_cinfo = &dir_cinfo,
++      };
++        struct rpc_message msg = {
++                .rpc_proc       = &nfs4_procedures[NFSPROC4_CLNT_LINK],
++                .rpc_argp       = &arg,
++                .rpc_resp       = &res,
++        };
++
+       dir_attr.valid = 0;
+       fattr.valid = 0;
+       
+-      nfs4_setup_compound(&compound, ops, NFS_SERVER(inode), "link");
+-      nfs4_setup_putfh(&compound, NFS_FH(inode));
+-      nfs4_setup_savefh(&compound);
+-      nfs4_setup_putfh(&compound, NFS_FH(dir));
+-      nfs4_setup_link(&compound, name, &dir_cinfo);
+-      nfs4_setup_getattr(&compound, &dir_attr);
+-      nfs4_setup_restorefh(&compound);
+-      nfs4_setup_getattr(&compound, &fattr);
+-      status = nfs4_call_compound(&compound, NULL, 0);
++      status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
+       if (!status) {
+               process_cinfo(&dir_cinfo, &dir_attr);
+@@ -1350,24 +1260,34 @@ nfs4_proc_symlink(struct inode *dir, str
+                 struct iattr *sattr, struct nfs_fh *fhandle,
+                 struct nfs_fattr *fattr)
+ {
+-      struct nfs4_compound    compound;
+-      struct nfs4_op          ops[7];
+       struct nfs_fattr        dir_attr;
+       struct nfs4_change_info dir_cinfo;
+       int                     status;
++      struct nfs4_create_arg arg = {
++              .dir_fh = NFS_FH(dir),
++              .server = NFS_SERVER(dir),
++              .name = name,
++              .u.symlink = path,
++              .attrs = sattr,
++              .ftype = NF4LNK,
++      };
++      struct nfs4_create_res res = {
++              .server = NFS_SERVER(dir),
++              .fhandle = fhandle,
++              .fattr = fattr,
++              .dir_attr = &dir_attr,
++              .dir_cinfo = &dir_cinfo,
++      };
++        struct rpc_message msg = {
++                .rpc_proc       = &nfs4_procedures[NFSPROC4_CLNT_CREATE],
++                .rpc_argp       = &arg,
++                .rpc_resp       = &res,
++        };
+       dir_attr.valid = 0;
+       fattr->valid = 0;
+       
+-      nfs4_setup_compound(&compound, ops, NFS_SERVER(dir), "symlink");
+-      nfs4_setup_putfh(&compound, NFS_FH(dir));
+-      nfs4_setup_savefh(&compound);
+-      nfs4_setup_create_symlink(&compound, name, path, sattr, &dir_cinfo);
+-      nfs4_setup_getattr(&compound, fattr);
+-      nfs4_setup_getfh(&compound, fhandle);
+-      nfs4_setup_restorefh(&compound);
+-      nfs4_setup_getattr(&compound, &dir_attr);
+-      status = nfs4_call_compound(&compound, NULL, 0);
++      status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
+       if (!status) {
+               process_cinfo(&dir_cinfo, &dir_attr);
+@@ -1380,24 +1300,33 @@ static int
+ nfs4_proc_mkdir(struct inode *dir, struct qstr *name, struct iattr *sattr,
+               struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+ {
+-      struct nfs4_compound    compound;
+-      struct nfs4_op          ops[7];
+       struct nfs_fattr        dir_attr;
+       struct nfs4_change_info dir_cinfo;
+       int                     status;
++      struct nfs4_create_arg arg = {
++              .dir_fh = NFS_FH(dir),
++              .server = NFS_SERVER(dir),
++              .name = name,
++              .attrs = sattr,
++              .ftype = NF4DIR,
++      };
++      struct nfs4_create_res res = {
++              .server = NFS_SERVER(dir),
++              .fhandle = fhandle,
++              .fattr = fattr,
++              .dir_attr = &dir_attr,
++              .dir_cinfo = &dir_cinfo,
++      };
++        struct rpc_message msg = {
++                .rpc_proc       = &nfs4_procedures[NFSPROC4_CLNT_CREATE],
++                .rpc_argp       = &arg,
++                .rpc_resp       = &res,
++        };
+       dir_attr.valid = 0;
+       fattr->valid = 0;
+       
+-      nfs4_setup_compound(&compound, ops, NFS_SERVER(dir), "mkdir");
+-      nfs4_setup_putfh(&compound, NFS_FH(dir));
+-      nfs4_setup_savefh(&compound);
+-      nfs4_setup_create_dir(&compound, name, sattr, &dir_cinfo);
+-      nfs4_setup_getattr(&compound, fattr);
+-      nfs4_setup_getfh(&compound, fhandle);
+-      nfs4_setup_restorefh(&compound);
+-      nfs4_setup_getattr(&compound, &dir_attr);
+-      status = nfs4_call_compound(&compound, NULL, 0);
++      status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
+       if (!status) {
+               process_cinfo(&dir_cinfo, &dir_attr);
+@@ -1411,17 +1340,25 @@ nfs4_proc_readdir(struct dentry *dentry,
+                   u64 cookie, struct page *page, unsigned int count, int plus)
+ {
+       struct inode            *dir = dentry->d_inode;
+-      struct nfs4_compound    compound;
+-      struct nfs4_op          ops[2];
+       int                     status;
++      struct nfs4_readdir_arg args = {
++              .fh = NFS_FH(dir),
++              .pages = &page,
++              .pgbase = 0,
++              .count = count,
++      };
++      struct nfs4_readdir_res res;
++      struct rpc_message msg = {
++              .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READDIR],
++              .rpc_argp = &args,
++              .rpc_resp = &res,
++              .rpc_cred = cred,
++      };
+       lock_kernel();
+-
+-      nfs4_setup_compound(&compound, ops, NFS_SERVER(dir), "readdir");
+-      nfs4_setup_putfh(&compound, NFS_FH(dir));
+-      nfs4_setup_readdir(&compound, cookie, NFS_COOKIEVERF(dir), &page, count, dentry);
+-      status = nfs4_call_compound(&compound, cred, 0);
+-
++      nfs4_setup_readdir(cookie, NFS_COOKIEVERF(dir), dentry, &args);
++      res.pgbase = args.pgbase;
++      status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
+       unlock_kernel();
+       return status;
+ }
+@@ -1430,24 +1367,50 @@ static int
+ nfs4_proc_mknod(struct inode *dir, struct qstr *name, struct iattr *sattr,
+               dev_t rdev, struct nfs_fh *fh, struct nfs_fattr *fattr)
+ {
+-      struct nfs4_compound    compound;
+-      struct nfs4_op          ops[7];
+       struct nfs_fattr        dir_attr;
+       struct nfs4_change_info dir_cinfo;
+       int                     status;
++      int                     mode = sattr->ia_mode;
++      struct nfs4_create_arg arg = {
++              .dir_fh = NFS_FH(dir),
++              .server = NFS_SERVER(dir),
++              .name = name,
++              .attrs = sattr,
++      };
++      struct nfs4_create_res res = {
++              .server = NFS_SERVER(dir),
++              .fhandle = fh,
++              .fattr = fattr,
++              .dir_attr = &dir_attr,
++              .dir_cinfo = &dir_cinfo,
++      };
++        struct rpc_message msg = {
++                .rpc_proc       = &nfs4_procedures[NFSPROC4_CLNT_CREATE],
++                .rpc_argp       = &arg,
++                .rpc_resp       = &res,
++        };
+       dir_attr.valid = 0;
+       fattr->valid = 0;
++
++      BUG_ON(!(sattr->ia_valid & ATTR_MODE));
++      BUG_ON(!S_ISFIFO(mode) && !S_ISBLK(mode) && !S_ISCHR(mode) && !S_ISSOCK(mode));
++      if (S_ISFIFO(mode))
++              arg.ftype = NF4FIFO;
++      else if (S_ISBLK(mode)) {
++              arg.ftype = NF4BLK;
++              arg.u.device.specdata1 = MAJOR(rdev);
++              arg.u.device.specdata2 = MINOR(rdev);
++      }
++      else if (S_ISCHR(mode)) {
++              arg.ftype = NF4CHR;
++              arg.u.device.specdata1 = MAJOR(rdev);
++              arg.u.device.specdata2 = MINOR(rdev);
++      }
++      else
++              arg.ftype = NF4SOCK;
+       
+-      nfs4_setup_compound(&compound, ops, NFS_SERVER(dir), "mknod");
+-      nfs4_setup_putfh(&compound, NFS_FH(dir));
+-      nfs4_setup_savefh(&compound);
+-      nfs4_setup_create_special(&compound, name, rdev,sattr, &dir_cinfo);
+-      nfs4_setup_getattr(&compound, fattr);
+-      nfs4_setup_getfh(&compound, fh);
+-      nfs4_setup_restorefh(&compound);
+-      nfs4_setup_getattr(&compound, &dir_attr);
+-      status = nfs4_call_compound(&compound, NULL, 0);
++      status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
+       if (!status) {
+               process_cinfo(&dir_cinfo, &dir_attr);
+@@ -1460,14 +1423,13 @@ static int
+ nfs4_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
+                struct nfs_fsstat *fsstat)
+ {
+-      struct nfs4_compound compound;
+-      struct nfs4_op ops[2];
++      struct rpc_message msg = {
++              .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_STATFS],
++              .rpc_argp = fhandle,
++              .rpc_resp = fsstat,
++      };
+-      memset(fsstat, 0, sizeof(*fsstat));
+-      nfs4_setup_compound(&compound, ops, server, "statfs");
+-      nfs4_setup_putfh(&compound, fhandle);
+-      nfs4_setup_statfs(&compound, fsstat);
+-      return nfs4_call_compound(&compound, NULL, 0);
++      return rpc_call_sync(server->client, &msg, 0);
+ }
+ static int
+@@ -1480,7 +1442,6 @@ nfs4_proc_fsinfo(struct nfs_server *serv
+               .rpc_resp = fsinfo,
+       };
+-      memset(fsinfo, 0, sizeof(*fsinfo));
+       return rpc_call_sync(server->client, &msg, 0);
+ }
+@@ -1488,14 +1449,13 @@ static int
+ nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
+                  struct nfs_pathconf *pathconf)
+ {
+-      struct nfs4_compound compound;
+-      struct nfs4_op ops[2];
++      struct rpc_message msg = {
++              .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_PATHCONF],
++              .rpc_argp = fhandle,
++              .rpc_resp = pathconf,
++      };
+-      memset(pathconf, 0, sizeof(*pathconf));
+-      nfs4_setup_compound(&compound, ops, server, "statfs");
+-      nfs4_setup_putfh(&compound, fhandle);
+-      nfs4_setup_pathconf(&compound, pathconf);
+-      return nfs4_call_compound(&compound, NULL, 0);
++      return rpc_call_sync(server->client, &msg, 0);
+ }
+ static void
+@@ -1517,7 +1477,6 @@ nfs4_read_done(struct rpc_task *task)
+ {
+       struct nfs_read_data *data = (struct nfs_read_data *) task->tk_calldata;
+       struct inode *inode = data->inode;
+-      struct nfs_fattr *fattr = data->res.fattr;
+       if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) {
+               task->tk_action = nfs4_restart_read;
+@@ -1525,11 +1484,6 @@ nfs4_read_done(struct rpc_task *task)
+       }
+       if (task->tk_status > 0)
+               renew_lease(NFS_SERVER(inode), data->timestamp);
+-      /* Check cache consistency */
+-      if (fattr->change_attr != NFS_CHANGE_ATTR(inode))
+-              nfs_zap_caches(inode);
+-      if (fattr->bitmap[1] & FATTR4_WORD1_TIME_ACCESS)
+-              inode->i_atime = fattr->atime;
+       /* Call back common NFS readpage processing */
+       nfs_readpage_result(task);
+ }
+@@ -1577,21 +1531,6 @@ nfs4_proc_read_setup(struct nfs_read_dat
+ }
+ static void
+-nfs4_write_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
+-{
+-      /* Check cache consistency */
+-      if (fattr->pre_change_attr != NFS_CHANGE_ATTR(inode))
+-              nfs_zap_caches(inode);
+-      NFS_CHANGE_ATTR(inode) = fattr->change_attr;
+-      if (fattr->bitmap[1] & FATTR4_WORD1_SPACE_USED)
+-              inode->i_blocks = (fattr->du.nfs3.used + 511) >> 9;
+-      if (fattr->bitmap[1] & FATTR4_WORD1_TIME_METADATA)
+-              inode->i_ctime = fattr->ctime;
+-      if (fattr->bitmap[1] & FATTR4_WORD1_TIME_MODIFY)
+-              inode->i_mtime = fattr->mtime;
+-}
+-
+-static void
+ nfs4_restart_write(struct rpc_task *task)
+ {
+       struct nfs_write_data *data = (struct nfs_write_data *)task->tk_calldata;
+@@ -1617,7 +1556,6 @@ nfs4_write_done(struct rpc_task *task)
+       }
+       if (task->tk_status >= 0)
+               renew_lease(NFS_SERVER(inode), data->timestamp);
+-      nfs4_write_refresh_inode(inode, data->res.fattr);
+       /* Call back common NFS writeback processing */
+       nfs_writeback_done(task);
+ }
+@@ -1684,7 +1622,6 @@ nfs4_commit_done(struct rpc_task *task)
+               task->tk_action = nfs4_restart_write;
+               return;
+       }
+-      nfs4_write_refresh_inode(inode, data->res.fattr);
+       /* Call back common NFS writeback processing */
+       nfs_commit_done(task);
+ }
+@@ -1807,6 +1744,7 @@ nfs4_proc_file_open(struct inode *inode,
+       if (filp->f_mode & FMODE_WRITE) {
+               lock_kernel();
+               nfs_set_mmcred(inode, state->owner->so_cred);
++              nfs_begin_data_update(inode);
+               unlock_kernel();
+       }
+       filp->private_data = state;
+@@ -1823,6 +1761,11 @@ nfs4_proc_file_release(struct inode *ino
+       if (state)
+               nfs4_close_state(state, filp->f_mode);
++      if (filp->f_mode & FMODE_WRITE) {
++              lock_kernel();
++              nfs_end_data_update(inode);
++              unlock_kernel();
++      }
+       return 0;
+ }
+@@ -2294,6 +2237,7 @@ struct nfs_rpc_ops       nfs_v4_clientops = {
+       .version        = 4,                    /* protocol version */
+       .dentry_ops     = &nfs4_dentry_operations,
+       .dir_inode_ops  = &nfs4_dir_inode_operations,
++      .file_inode_ops = &nfs4_file_inode_operations,
+       .getroot        = nfs4_proc_get_root,
+       .getattr        = nfs4_proc_getattr,
+       .setattr        = nfs4_proc_setattr,
+diff -puN fs/nfs/pagelist.c~CITI_NFS4_ALL fs/nfs/pagelist.c
+--- linux-2.6.3/fs/nfs/pagelist.c~CITI_NFS4_ALL        2004-02-19 16:47:03.000000000 -0500
++++ linux-2.6.3-bfields/fs/nfs/pagelist.c      2004-02-19 16:47:03.000000000 -0500
+@@ -246,7 +246,6 @@ nfs_coalesce_requests(struct list_head *
+  * nfs_scan_list - Scan a list for matching requests
+  * @head: One of the NFS inode request lists
+  * @dst: Destination list
+- * @file: if set, ensure we match requests from this file
+  * @idx_start: lower bound of page->index to scan
+  * @npages: idx_start + npages sets the upper bound to scan.
+  *
+@@ -258,7 +257,6 @@ nfs_coalesce_requests(struct list_head *
+  */
+ int
+ nfs_scan_list(struct list_head *head, struct list_head *dst,
+-            struct file *file,
+             unsigned long idx_start, unsigned int npages)
+ {
+       struct list_head        *pos, *tmp;
+@@ -276,9 +274,6 @@ nfs_scan_list(struct list_head *head, st
+               req = nfs_list_entry(pos);
+-              if (file && req->wb_file != file)
+-                      continue;
+-
+               if (req->wb_index < idx_start)
+                       continue;
+               if (req->wb_index > idx_end)
+diff -puN fs/nfs/proc.c~CITI_NFS4_ALL fs/nfs/proc.c
+--- linux-2.6.3/fs/nfs/proc.c~CITI_NFS4_ALL    2004-02-19 16:47:03.000000000 -0500
++++ linux-2.6.3-bfields/fs/nfs/proc.c  2004-02-19 16:47:07.000000000 -0500
+@@ -49,18 +49,6 @@
+ extern struct rpc_procinfo nfs_procedures[];
+-static void
+-nfs_write_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
+-{
+-      if (!(fattr->valid & NFS_ATTR_WCC)) {
+-              fattr->pre_size  = NFS_CACHE_ISIZE(inode);
+-              fattr->pre_mtime = NFS_CACHE_MTIME(inode);
+-              fattr->pre_ctime = NFS_CACHE_CTIME(inode);
+-              fattr->valid |= NFS_ATTR_WCC;
+-      }
+-      nfs_refresh_inode(inode, fattr);
+-}
+-
+ static struct rpc_cred *
+ nfs_cred(struct inode *inode, struct file *filp)
+ {
+@@ -78,15 +66,33 @@ nfs_cred(struct inode *inode, struct fil
+  */
+ static int
+ nfs_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
+-                struct nfs_fattr *fattr)
++                struct nfs_fsinfo *info)
+ {
+-      int             status;
++      struct nfs_fattr *fattr = info->fattr;
++      struct nfs2_fsstat fsinfo;
++      int status;
+-      dprintk("NFS call  getroot\n");
++      dprintk("%s: call getattr\n", __FUNCTION__);
+       fattr->valid = 0;
+-      status = rpc_call(server->client, NFSPROC_GETATTR, fhandle, fattr, 0);
+-      dprintk("NFS reply getroot\n");
+-      return status;
++      status = rpc_call(server->client_sys, NFSPROC_GETATTR, fhandle, fattr, 0);
++      dprintk("%s: reply getattr %d\n", __FUNCTION__, status);
++      if (status)
++              return status;
++      dprintk("%s: call statfs\n", __FUNCTION__);
++      status = rpc_call(server->client_sys, NFSPROC_STATFS, fhandle, &fsinfo, 0);
++      dprintk("%s: reply statfs %d\n", __FUNCTION__, status);
++      if (status)
++              return status;
++      info->rtmax  = NFS_MAXDATA;
++      info->rtpref = fsinfo.tsize;
++      info->rtmult = fsinfo.bsize;
++      info->wtmax  = NFS_MAXDATA;
++      info->wtpref = fsinfo.tsize;
++      info->wtmult = fsinfo.bsize;
++      info->dtpref = fsinfo.tsize;
++      info->maxfilesize = 0x7FFFFFFF;
++      info->lease_time = 0;
++      return 0;
+ }
+ /*
+@@ -205,7 +211,7 @@ nfs_proc_write(struct nfs_write_data *wd
+       msg.rpc_cred = nfs_cred(inode, filp);
+       status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
+       if (status >= 0) {
+-              nfs_write_refresh_inode(inode, fattr);
++              nfs_refresh_inode(inode, fattr);
+               wdata->res.count = wdata->args.count;
+               wdata->verf.committed = NFS_FILE_SYNC;
+       }
+@@ -331,10 +337,8 @@ nfs_proc_unlink_done(struct dentry *dir,
+ {
+       struct rpc_message *msg = &task->tk_msg;
+       
+-      if (msg->rpc_argp) {
+-              NFS_CACHEINV(dir->d_inode);
++      if (msg->rpc_argp)
+               kfree(msg->rpc_argp);
+-      }
+       return 0;
+ }
+@@ -584,7 +588,7 @@ nfs_write_done(struct rpc_task *task)
+       struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata;
+       if (task->tk_status >= 0)
+-              nfs_write_refresh_inode(data->inode, data->res.fattr);
++              nfs_refresh_inode(data->inode, data->res.fattr);
+       nfs_writeback_done(task);
+ }
+@@ -665,6 +669,7 @@ struct nfs_rpc_ops nfs_v2_clientops = {
+       .version        = 2,                   /* protocol version */
+       .dentry_ops     = &nfs_dentry_operations,
+       .dir_inode_ops  = &nfs_dir_inode_operations,
++      .file_inode_ops = &nfs_file_inode_operations,
+       .getroot        = nfs_proc_get_root,
+       .getattr        = nfs_proc_getattr,
+       .setattr        = nfs_proc_setattr,
+diff -puN fs/nfs/read.c~CITI_NFS4_ALL fs/nfs/read.c
+--- linux-2.6.3/fs/nfs/read.c~CITI_NFS4_ALL    2004-02-19 16:47:03.000000000 -0500
++++ linux-2.6.3-bfields/fs/nfs/read.c  2004-02-19 16:47:03.000000000 -0500
+@@ -124,6 +124,7 @@ nfs_readpage_sync(struct file *file, str
+               if (result < rdata.args.count)  /* NFSv2ism */
+                       break;
+       } while (count);
++      NFS_FLAGS(inode) |= NFS_INO_INVALID_ATIME;
+       if (count)
+               memclear_highpage_flush(page, rdata.args.pgbase, count);
+@@ -266,6 +267,7 @@ nfs_readpage_result(struct rpc_task *tas
+       dprintk("NFS: %4d nfs_readpage_result, (status %d)\n",
+               task->tk_pid, task->tk_status);
++      NFS_FLAGS(data->inode) |= NFS_INO_INVALID_ATIME;
+       while (!list_empty(&data->pages)) {
+               struct nfs_page *req = nfs_list_entry(data->pages.next);
+               struct page *page = req->wb_page;
+diff -puN fs/nfs/unlink.c~CITI_NFS4_ALL fs/nfs/unlink.c
+--- linux-2.6.3/fs/nfs/unlink.c~CITI_NFS4_ALL  2004-02-19 16:47:03.000000000 -0500
++++ linux-2.6.3-bfields/fs/nfs/unlink.c        2004-02-19 16:47:03.000000000 -0500
+@@ -104,6 +104,7 @@ nfs_async_unlink_init(struct rpc_task *t
+       status = NFS_PROTO(dir->d_inode)->unlink_setup(&msg, dir, &data->name);
+       if (status < 0)
+               goto out_err;
++      nfs_begin_data_update(dir->d_inode);
+       rpc_call_setup(task, &msg, 0);
+       return;
+  out_err:
+@@ -126,7 +127,7 @@ nfs_async_unlink_done(struct rpc_task *t
+       if (!dir)
+               return;
+       dir_i = dir->d_inode;
+-      nfs_zap_caches(dir_i);
++      nfs_end_data_update(dir_i);
+       if (NFS_PROTO(dir_i)->unlink_done(dir, task))
+               return;
+       put_rpccred(data->cred);
+diff -puN fs/nfs/write.c~CITI_NFS4_ALL fs/nfs/write.c
+--- linux-2.6.3/fs/nfs/write.c~CITI_NFS4_ALL   2004-02-19 16:47:03.000000000 -0500
++++ linux-2.6.3-bfields/fs/nfs/write.c 2004-02-19 16:47:03.000000000 -0500
+@@ -74,7 +74,6 @@
+ static struct nfs_page * nfs_update_request(struct file*, struct inode *,
+                                           struct page *,
+                                           unsigned int, unsigned int);
+-static void   nfs_strategy(struct inode *inode);
+ static kmem_cache_t *nfs_wdata_cachep;
+ static mempool_t *nfs_wdata_mempool;
+@@ -124,6 +123,52 @@ void nfs_commit_release(struct rpc_task 
+       nfs_commit_free(wdata);
+ }
++/* Adjust the file length if we're writing beyond the end */
++static void nfs_grow_file(struct page *page, unsigned int offset, unsigned int count)
++{
++      struct inode *inode = page->mapping->host;
++      loff_t end, i_size = i_size_read(inode);
++      unsigned long end_index = (i_size - 1) >> PAGE_CACHE_SHIFT;
++
++      if (i_size > 0 && page->index < end_index)
++              return;
++      end = ((loff_t)page->index << PAGE_CACHE_SHIFT) + ((loff_t)offset+count);
++      if (i_size >= end)
++              return;
++      i_size_write(inode, end);
++}
++
++/* We can set the PG_uptodate flag if we see that a write request
++ * covers the full page.
++ */
++static void nfs_mark_uptodate(struct page *page, unsigned int base, unsigned int count)
++{
++      loff_t end_offs;
++
++      if (PageUptodate(page))
++              return;
++      if (base != 0)
++              return;
++      if (count == PAGE_CACHE_SIZE) {
++              SetPageUptodate(page);
++              return;
++      }
++
++      end_offs = i_size_read(page->mapping->host) - 1;
++      if (end_offs < 0)
++              return;
++      /* Is this the last page? */
++      if (page->index != (unsigned long)(end_offs >> PAGE_CACHE_SHIFT))
++              return;
++      /* This is the last page: set PG_uptodate if we cover the entire
++       * extent of the data, then zero the rest of the page.
++       */
++      if (count == (unsigned int)(end_offs & (PAGE_CACHE_SIZE - 1)) + 1) {
++              memclear_highpage_flush(page, count, PAGE_CACHE_SIZE - count);
++              SetPageUptodate(page);
++      }
++}
++
+ /*
+  * Write a page synchronously.
+  * Offset is the data offset within the page.
+@@ -157,6 +202,7 @@ nfs_writepage_sync(struct file *file, st
+               (long long)NFS_FILEID(inode),
+               count, (long long)(page_offset(page) + offset));
++      nfs_begin_data_update(inode);
+       do {
+               if (count < wsize && !swapfile)
+                       wdata.args.count = count;
+@@ -177,14 +223,12 @@ nfs_writepage_sync(struct file *file, st
+               wdata.args.pgbase += result;
+               written += result;
+               count -= result;
+-
+-              /*
+-               * If we've extended the file, update the inode
+-               * now so we don't invalidate the cache.
+-               */
+-              if (wdata.args.offset > i_size_read(inode))
+-                      i_size_write(inode, wdata.args.offset);
+       } while (count);
++      /* Update file length */
++      nfs_grow_file(page, offset, written);
++      /* Set the PG_uptodate flag? */
++      nfs_mark_uptodate(page, offset, written);
++      nfs_end_data_update(inode);
+       if (PageError(page))
+               ClearPageError(page);
+@@ -201,18 +245,19 @@ nfs_writepage_async(struct file *file, s
+                   unsigned int offset, unsigned int count)
+ {
+       struct nfs_page *req;
+-      loff_t          end;
+       int             status;
++      nfs_begin_data_update(inode);
+       req = nfs_update_request(file, inode, page, offset, count);
+       status = (IS_ERR(req)) ? PTR_ERR(req) : 0;
+       if (status < 0)
+               goto out;
++      /* Update file length */
++      nfs_grow_file(page, offset, count);
++      /* Set the PG_uptodate flag? */
++      nfs_mark_uptodate(page, offset, count);
+       nfs_unlock_request(req);
+-      nfs_strategy(inode);
+-      end = ((loff_t)page->index<<PAGE_CACHE_SHIFT) + (loff_t)(offset + count);
+-      if (i_size_read(inode) < end)
+-              i_size_write(inode, end);
++      nfs_end_data_update(inode);
+  out:
+       return status;
+@@ -286,7 +331,7 @@ nfs_writepages(struct address_space *map
+       err = generic_writepages(mapping, wbc);
+       if (err)
+               goto out;
+-      err = nfs_flush_file(inode, NULL, 0, 0, 0);
++      err = nfs_flush_inode(inode, 0, 0, 0);
+       if (err < 0)
+               goto out;
+       if (wbc->sync_mode == WB_SYNC_HOLD)
+@@ -294,7 +339,7 @@ nfs_writepages(struct address_space *map
+       if (is_sync && wbc->sync_mode == WB_SYNC_ALL) {
+               err = nfs_wb_all(inode);
+       } else
+-              nfs_commit_file(inode, NULL, 0, 0, 0);
++              nfs_commit_inode(inode, 0, 0, 0);
+ out:
+       return err;
+ }
+@@ -312,8 +357,10 @@ nfs_inode_add_request(struct inode *inod
+       BUG_ON(error == -EEXIST);
+       if (error)
+               return error;
+-      if (!nfsi->npages)
++      if (!nfsi->npages) {
+               igrab(inode);
++              nfs_begin_data_update(inode);
++      }
+       nfsi->npages++;
+       req->wb_count++;
+       return 0;
+@@ -336,6 +383,7 @@ nfs_inode_remove_request(struct nfs_page
+       nfsi->npages--;
+       if (!nfsi->npages) {
+               spin_unlock(&nfs_wreq_lock);
++              nfs_end_data_update(inode);
+               iput(inode);
+       } else
+               spin_unlock(&nfs_wreq_lock);
+@@ -421,7 +469,7 @@ nfs_mark_request_commit(struct nfs_page 
+  * Interruptible by signals only if mounted with intr flag.
+  */
+ static int
+-nfs_wait_on_requests(struct inode *inode, struct file *file, unsigned long idx_start, unsigned int npages)
++nfs_wait_on_requests(struct inode *inode, unsigned long idx_start, unsigned int npages)
+ {
+       struct nfs_inode *nfsi = NFS_I(inode);
+       struct nfs_page *req;
+@@ -441,8 +489,6 @@ nfs_wait_on_requests(struct inode *inode
+                       break;
+               next = req->wb_index + 1;
+-              if (file && req->wb_file != file)
+-                      continue;
+               if (!NFS_WBACK_BUSY(req))
+                       continue;
+@@ -453,7 +499,6 @@ nfs_wait_on_requests(struct inode *inode
+               if (error < 0)
+                       return error;
+               spin_lock(&nfs_wreq_lock);
+-              next = idx_start;
+               res++;
+       }
+       spin_unlock(&nfs_wreq_lock);
+@@ -464,7 +509,6 @@ nfs_wait_on_requests(struct inode *inode
+  * nfs_scan_dirty - Scan an inode for dirty requests
+  * @inode: NFS inode to scan
+  * @dst: destination list
+- * @file: if set, ensure we match requests from this file
+  * @idx_start: lower bound of page->index to scan.
+  * @npages: idx_start + npages sets the upper bound to scan.
+  *
+@@ -472,11 +516,11 @@ nfs_wait_on_requests(struct inode *inode
+  * The requests are *not* checked to ensure that they form a contiguous set.
+  */
+ static int
+-nfs_scan_dirty(struct inode *inode, struct list_head *dst, struct file *file, unsigned long idx_start, unsigned int npages)
++nfs_scan_dirty(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages)
+ {
+       struct nfs_inode *nfsi = NFS_I(inode);
+       int     res;
+-      res = nfs_scan_list(&nfsi->dirty, dst, file, idx_start, npages);
++      res = nfs_scan_list(&nfsi->dirty, dst, idx_start, npages);
+       nfsi->ndirty -= res;
+       sub_page_state(nr_dirty,res);
+       if ((nfsi->ndirty == 0) != list_empty(&nfsi->dirty))
+@@ -489,7 +533,6 @@ nfs_scan_dirty(struct inode *inode, stru
+  * nfs_scan_commit - Scan an inode for commit requests
+  * @inode: NFS inode to scan
+  * @dst: destination list
+- * @file: if set, ensure we collect requests from this file only.
+  * @idx_start: lower bound of page->index to scan.
+  * @npages: idx_start + npages sets the upper bound to scan.
+  *
+@@ -497,11 +540,11 @@ nfs_scan_dirty(struct inode *inode, stru
+  * The requests are *not* checked to ensure that they form a contiguous set.
+  */
+ static int
+-nfs_scan_commit(struct inode *inode, struct list_head *dst, struct file *file, unsigned long idx_start, unsigned int npages)
++nfs_scan_commit(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages)
+ {
+       struct nfs_inode *nfsi = NFS_I(inode);
+       int     res;
+-      res = nfs_scan_list(&nfsi->commit, dst, file, idx_start, npages);
++      res = nfs_scan_list(&nfsi->commit, dst, idx_start, npages);
+       nfsi->ncommit -= res;
+       if ((nfsi->ncommit == 0) != list_empty(&nfsi->commit))
+               printk(KERN_ERR "NFS: desynchronized value of nfs_i.ncommit.\n");
+@@ -600,46 +643,6 @@ nfs_update_request(struct file* file, st
+       return req;
+ }
+-/*
+- * This is the strategy routine for NFS.
+- * It is called by nfs_updatepage whenever the user wrote up to the end
+- * of a page.
+- *
+- * We always try to submit a set of requests in parallel so that the
+- * server's write code can gather writes. This is mainly for the benefit
+- * of NFSv2.
+- *
+- * We never submit more requests than we think the remote can handle.
+- * For UDP sockets, we make sure we don't exceed the congestion window;
+- * for TCP, we limit the number of requests to 8.
+- *
+- * NFS_STRATEGY_PAGES gives the minimum number of requests for NFSv2 that
+- * should be sent out in one go. This is for the benefit of NFSv2 servers
+- * that perform write gathering.
+- *
+- * FIXME: Different servers may have different sweet spots.
+- * Record the average congestion window in server struct?
+- */
+-#define NFS_STRATEGY_PAGES      8
+-static void
+-nfs_strategy(struct inode *inode)
+-{
+-      unsigned int    dirty, wpages;
+-
+-      dirty  = NFS_I(inode)->ndirty;
+-      wpages = NFS_SERVER(inode)->wpages;
+-#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
+-      if (NFS_PROTO(inode)->version == 2) {
+-              if (dirty >= NFS_STRATEGY_PAGES * wpages)
+-                      nfs_flush_file(inode, NULL, 0, 0, 0);
+-      } else if (dirty >= wpages)
+-              nfs_flush_file(inode, NULL, 0, 0, 0);
+-#else
+-      if (dirty >= NFS_STRATEGY_PAGES * wpages)
+-              nfs_flush_file(inode, NULL, 0, 0, 0);
+-#endif
+-}
+-
+ int
+ nfs_flush_incompatible(struct file *file, struct page *page)
+ {
+@@ -675,7 +678,6 @@ nfs_updatepage(struct file *file, struct
+       struct dentry   *dentry = file->f_dentry;
+       struct inode    *inode = page->mapping->host;
+       struct nfs_page *req;
+-      loff_t          end;
+       int             status = 0;
+       dprintk("NFS:      nfs_updatepage(%s/%s %d@%Ld)\n",
+@@ -696,6 +698,30 @@ nfs_updatepage(struct file *file, struct
+               return status;
+       }
++      nfs_begin_data_update(inode);
++
++
++      /* If we're not using byte range locks, and we know the page
++       * is entirely in cache, it may be more efficient to avoid
++       * fragmenting write requests.
++       */
++      if (PageUptodate(page) && inode->i_flock == NULL) {
++              loff_t end_offs = i_size_read(inode) - 1;
++              unsigned long end_index = end_offs >> PAGE_CACHE_SHIFT;
++
++              count += offset;
++              offset = 0;
++              if (end_offs < 0) {
++                      /* Do nothing */
++              } else if (page->index == end_index) {
++                      unsigned int pglen;
++                      pglen = (unsigned int)(end_offs & (PAGE_CACHE_SIZE-1)) + 1;
++                      if (count < pglen)
++                              count = pglen;
++              } else if (page->index < end_index)
++                      count = PAGE_CACHE_SIZE;
++      }
++
+       /*
+        * Try to find an NFS request corresponding to this page
+        * and update it.
+@@ -714,21 +740,14 @@ nfs_updatepage(struct file *file, struct
+               goto done;
+       status = 0;
+-      end = ((loff_t)page->index<<PAGE_CACHE_SHIFT) + (loff_t)(offset + count);
+-      if (i_size_read(inode) < end)
+-              i_size_write(inode, end);
+-
+-      /* If we wrote past the end of the page.
+-       * Call the strategy routine so it can send out a bunch
+-       * of requests.
+-       */
+-      if (req->wb_pgbase == 0 && req->wb_bytes == PAGE_CACHE_SIZE) {
+-              SetPageUptodate(page);
+-              nfs_unlock_request(req);
+-              nfs_strategy(inode);
+-      } else
+-              nfs_unlock_request(req);
++
++      /* Update file length */
++      nfs_grow_file(page, offset, count);
++      /* Set the PG_uptodate flag? */
++      nfs_mark_uptodate(page, req->wb_pgbase, req->wb_bytes);
++      nfs_unlock_request(req);
+ done:
++      nfs_end_data_update(inode);
+         dprintk("NFS:      nfs_updatepage returns %d (isize %Ld)\n",
+                       status, (long long)i_size_read(inode));
+       if (status < 0)
+@@ -891,10 +910,7 @@ nfs_writeback_done(struct rpc_task *task
+ #endif
+       /*
+-       * Update attributes as result of writeback.
+-       * FIXME: There is an inherent race with invalidate_inode_pages and
+-       *        writebacks since the page->count is kept > 1 for as long
+-       *        as the page has a write request pending.
++       * Process the nfs_page list
+        */
+       while (!list_empty(&data->pages)) {
+               req = nfs_list_entry(data->pages.next);
+@@ -1061,7 +1077,7 @@ nfs_commit_done(struct rpc_task *task)
+ }
+ #endif
+-int nfs_flush_file(struct inode *inode, struct file *file, unsigned long idx_start,
++int nfs_flush_inode(struct inode *inode, unsigned long idx_start,
+                  unsigned int npages, int how)
+ {
+       LIST_HEAD(head);
+@@ -1069,7 +1085,7 @@ int nfs_flush_file(struct inode *inode, 
+                               error = 0;
+       spin_lock(&nfs_wreq_lock);
+-      res = nfs_scan_dirty(inode, &head, file, idx_start, npages);
++      res = nfs_scan_dirty(inode, &head, idx_start, npages);
+       spin_unlock(&nfs_wreq_lock);
+       if (res)
+               error = nfs_flush_list(&head, NFS_SERVER(inode)->wpages, how);
+@@ -1079,7 +1095,7 @@ int nfs_flush_file(struct inode *inode, 
+ }
+ #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
+-int nfs_commit_file(struct inode *inode, struct file *file, unsigned long idx_start,
++int nfs_commit_inode(struct inode *inode, unsigned long idx_start,
+                   unsigned int npages, int how)
+ {
+       LIST_HEAD(head);
+@@ -1087,9 +1103,9 @@ int nfs_commit_file(struct inode *inode,
+                               error = 0;
+       spin_lock(&nfs_wreq_lock);
+-      res = nfs_scan_commit(inode, &head, file, idx_start, npages);
++      res = nfs_scan_commit(inode, &head, idx_start, npages);
+       if (res) {
+-              res += nfs_scan_commit(inode, &head, NULL, 0, 0);
++              res += nfs_scan_commit(inode, &head, 0, 0);
+               spin_unlock(&nfs_wreq_lock);
+               error = nfs_commit_list(&head, how);
+       } else
+@@ -1100,7 +1116,7 @@ int nfs_commit_file(struct inode *inode,
+ }
+ #endif
+-int nfs_sync_file(struct inode *inode, struct file *file, unsigned long idx_start,
++int nfs_sync_inode(struct inode *inode, unsigned long idx_start,
+                 unsigned int npages, int how)
+ {
+       int     error,
+@@ -1109,18 +1125,15 @@ int nfs_sync_file(struct inode *inode, s
+       wait = how & FLUSH_WAIT;
+       how &= ~FLUSH_WAIT;
+-      if (!inode && file)
+-              inode = file->f_dentry->d_inode;
+-
+       do {
+               error = 0;
+               if (wait)
+-                      error = nfs_wait_on_requests(inode, file, idx_start, npages);
++                      error = nfs_wait_on_requests(inode, idx_start, npages);
+               if (error == 0)
+-                      error = nfs_flush_file(inode, file, idx_start, npages, how);
++                      error = nfs_flush_inode(inode, idx_start, npages, how);
+ #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
+               if (error == 0)
+-                      error = nfs_commit_file(inode, file, idx_start, npages, how);
++                      error = nfs_commit_inode(inode, idx_start, npages, how);
+ #endif
+       } while (error > 0);
+       return error;
+diff -puN include/linux/fs.h~CITI_NFS4_ALL include/linux/fs.h
+--- linux-2.6.3/include/linux/fs.h~CITI_NFS4_ALL       2004-02-19 16:47:03.000000000 -0500
++++ linux-2.6.3-bfields/include/linux/fs.h     2004-02-19 16:47:03.000000000 -0500
+@@ -137,6 +137,7 @@ extern int leases_enable, dir_notify_ena
+ #define S_DEAD                32      /* removed, but still open directory */
+ #define S_NOQUOTA     64      /* Inode is not counted to quota */
+ #define S_DIRSYNC     128     /* Directory modifications are synchronous */
++#define S_NOCMTIME    256     /* Do not update file c/mtime */
+ /*
+  * Note that nosuid etc flags are inode-specific: setting some file-system
+@@ -170,6 +171,7 @@ extern int leases_enable, dir_notify_ena
+ #define IS_ONE_SECOND(inode)  __IS_FLG(inode, MS_ONE_SECOND)
+ #define IS_DEADDIR(inode)     ((inode)->i_flags & S_DEAD)
++#define IS_NOCMTIME(inode)    ((inode)->i_flags & S_NOCMTIME)
+ /* the read-only stuff doesn't really belong here, but any other place is
+    probably as bad and I don't want to create yet another include file. */
+diff -puN include/linux/nfs_fs.h~CITI_NFS4_ALL include/linux/nfs_fs.h
+--- linux-2.6.3/include/linux/nfs_fs.h~CITI_NFS4_ALL   2004-02-19 16:47:03.000000000 -0500
++++ linux-2.6.3-bfields/include/linux/nfs_fs.h 2004-02-19 16:47:07.000000000 -0500
+@@ -23,6 +23,10 @@
+ #include <linux/sunrpc/auth.h>
+ #include <linux/sunrpc/clnt.h>
++#ifdef CONFIG_NFS_V4
++#include <linux/xattr_acl.h>
++#endif /* CONFIG_NFS_V4 */
++
+ #include <linux/nfs.h>
+ #include <linux/nfs2.h>
+ #include <linux/nfs3.h>
+@@ -99,7 +103,7 @@ struct nfs_inode {
+       /*
+        * Various flags
+        */
+-      unsigned short          flags;
++      unsigned int            flags;
+       /*
+        * read_cache_jiffies is when we started read-caching this inode,
+@@ -118,19 +122,22 @@ struct nfs_inode {
+        *
+        *      mtime != read_cache_mtime
+        */
++      unsigned long           readdir_timestamp;
+       unsigned long           read_cache_jiffies;
+-      struct timespec         read_cache_ctime;
+-      struct timespec         read_cache_mtime;
+-      __u64                   read_cache_isize;
+       unsigned long           attrtimeo;
+       unsigned long           attrtimeo_timestamp;
+       __u64                   change_attr;            /* v4 only */
++      /* "Generation counter" for the attribute cache. This is
++       * bumped whenever we update the metadata on the
++       * server.
++       */
++      unsigned long           cache_change_attribute;
+       /*
+-       * Timestamp that dates the change made to read_cache_mtime.
+-       * This is of use for dentry revalidation
++       * Counter indicating the number of outstanding requests that
++       * will cause a file data update.
+        */
+-      unsigned long           cache_mtime_jiffies;
++      atomic_t                data_updates;
+       struct nfs_access_cache cache_access;
+@@ -160,7 +167,10 @@ struct nfs_inode {
+         /* NFSv4 state */
+       struct list_head        open_states;
+ #endif /* CONFIG_NFS_V4*/
+-
++#ifdef CONFIG_NFS_V4_ACL
++      struct posix_acl        *acl;
++      struct posix_acl        *default_acl;
++#endif /* CONFIG_NFS_V4_ACL */
+       struct inode            vfs_inode;
+ };
+@@ -170,7 +180,9 @@ struct nfs_inode {
+ #define NFS_INO_STALE         0x0001          /* possible stale inode */
+ #define NFS_INO_ADVISE_RDPLUS   0x0002          /* advise readdirplus */
+ #define NFS_INO_REVALIDATING  0x0004          /* revalidating attrs */
+-#define NFS_INO_FLUSH         0x0008          /* inode is due for flushing */
++#define NFS_INO_INVALID_ATTR  0x0008          /* cached attrs are invalid */
++#define NFS_INO_INVALID_DATA  0x0010          /* cached data is invalid */
++#define NFS_INO_INVALID_ATIME 0x0020          /* cached atime is invalid */
+ #define NFS_INO_FAKE_ROOT     0x0080          /* root inode placeholder */
+ static inline struct nfs_inode *NFS_I(struct inode *inode)
+@@ -186,15 +198,7 @@ static inline struct nfs_inode *NFS_I(st
+ #define NFS_ADDR(inode)                       (RPC_PEERADDR(NFS_CLIENT(inode)))
+ #define NFS_COOKIEVERF(inode)         (NFS_I(inode)->cookieverf)
+ #define NFS_READTIME(inode)           (NFS_I(inode)->read_cache_jiffies)
+-#define NFS_MTIME_UPDATE(inode)               (NFS_I(inode)->cache_mtime_jiffies)
+-#define NFS_CACHE_CTIME(inode)                (NFS_I(inode)->read_cache_ctime)
+-#define NFS_CACHE_MTIME(inode)                (NFS_I(inode)->read_cache_mtime)
+-#define NFS_CACHE_ISIZE(inode)                (NFS_I(inode)->read_cache_isize)
+ #define NFS_CHANGE_ATTR(inode)                (NFS_I(inode)->change_attr)
+-#define NFS_CACHEINV(inode) \
+-do { \
+-      NFS_READTIME(inode) = jiffies - NFS_MAXATTRTIMEO(inode) - 1; \
+-} while (0)
+ #define NFS_ATTRTIMEO(inode)          (NFS_I(inode)->attrtimeo)
+ #define NFS_MINATTRTIMEO(inode) \
+       (S_ISDIR(inode->i_mode)? NFS_SERVER(inode)->acdirmin \
+@@ -211,6 +215,17 @@ do { \
+ #define NFS_FILEID(inode)             (NFS_I(inode)->fileid)
++static inline int nfs_caches_unstable(struct inode *inode)
++{
++      return atomic_read(&NFS_I(inode)->data_updates) != 0;
++}
++
++static inline void NFS_CACHEINV(struct inode *inode)
++{
++      if (!nfs_caches_unstable(inode))
++              NFS_FLAGS(inode) |= NFS_INO_INVALID_ATTR;
++}
++
+ static inline int nfs_server_capable(struct inode *inode, int cap)
+ {
+       return NFS_SERVER(inode)->caps & cap;
+@@ -227,13 +242,37 @@ loff_t page_offset(struct page *page)
+       return ((loff_t)page->index) << PAGE_CACHE_SHIFT;
+ }
++/**
++ * nfs_save_change_attribute - Returns the inode attribute change cookie
++ * @inode - pointer to inode
++ * The "change attribute" is updated every time we finish an operation
++ * that will result in a metadata change on the server.
++ */
++static inline long nfs_save_change_attribute(struct inode *inode)
++{
++      return NFS_I(inode)->cache_change_attribute;
++}
++
++/**
++ * nfs_verify_change_attribute - Detects NFS inode cache updates
++ * @inode - pointer to inode
++ * @chattr - previously saved change attribute
++ * Return "false" if metadata has been updated (or is in the process of
++ * being updated) since the change attribute was saved.
++ */
++static inline int nfs_verify_change_attribute(struct inode *inode, unsigned long chattr)
++{
++      return !nfs_caches_unstable(inode)
++              && chattr == NFS_I(inode)->cache_change_attribute;
++}
++
+ /*
+  * linux/fs/nfs/inode.c
+  */
+ extern void nfs_zap_caches(struct inode *);
+ extern struct inode *nfs_fhget(struct super_block *, struct nfs_fh *,
+                               struct nfs_fattr *);
+-extern int __nfs_refresh_inode(struct inode *, struct nfs_fattr *);
++extern int nfs_refresh_inode(struct inode *, struct nfs_fattr *);
+ extern int nfs_getattr(struct vfsmount *, struct dentry *, struct kstat *);
+ extern int nfs_permission(struct inode *, int, struct nameidata *);
+ extern void nfs_set_mmcred(struct inode *, struct rpc_cred *);
+@@ -241,6 +280,10 @@ extern int nfs_open(struct inode *, stru
+ extern int nfs_release(struct inode *, struct file *);
+ extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *);
+ extern int nfs_setattr(struct dentry *, struct iattr *);
++extern void nfs_begin_attr_update(struct inode *);
++extern void nfs_end_attr_update(struct inode *);
++extern void nfs_begin_data_update(struct inode *);
++extern void nfs_end_data_update(struct inode *);
+ /*
+  * linux/fs/nfs/file.c
+@@ -309,11 +352,11 @@ extern void nfs_commit_done(struct rpc_t
+  * Try to write back everything synchronously (but check the
+  * return value!)
+  */
+-extern int  nfs_sync_file(struct inode *, struct file *, unsigned long, unsigned int, int);
+-extern int  nfs_flush_file(struct inode *, struct file *, unsigned long, unsigned int, int);
++extern int  nfs_sync_inode(struct inode *, unsigned long, unsigned int, int);
++extern int  nfs_flush_inode(struct inode *, unsigned long, unsigned int, int);
+ extern int  nfs_flush_list(struct list_head *, int, int);
+ #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
+-extern int  nfs_commit_file(struct inode *, struct file *, unsigned long, unsigned int, int);
++extern int  nfs_commit_inode(struct inode *, unsigned long, unsigned int, int);
+ extern int  nfs_commit_list(struct list_head *, int);
+ #else
+ static inline int
+@@ -333,7 +376,7 @@ nfs_have_writebacks(struct inode *inode)
+ static inline int
+ nfs_wb_all(struct inode *inode)
+ {
+-      int error = nfs_sync_file(inode, 0, 0, 0, FLUSH_WAIT);
++      int error = nfs_sync_inode(inode, 0, 0, FLUSH_WAIT);
+       return (error < 0) ? error : 0;
+ }
+@@ -343,21 +386,11 @@ nfs_wb_all(struct inode *inode)
+ static inline int
+ nfs_wb_page(struct inode *inode, struct page* page)
+ {
+-      int error = nfs_sync_file(inode, 0, page->index, 1,
++      int error = nfs_sync_inode(inode, page->index, 1,
+                                               FLUSH_WAIT | FLUSH_STABLE);
+       return (error < 0) ? error : 0;
+ }
+-/*
+- * Write back all pending writes for one user.. 
+- */
+-static inline int
+-nfs_wb_file(struct inode *inode, struct file *file)
+-{
+-      int error = nfs_sync_file(inode, file, 0, 0, FLUSH_WAIT);
+-      return (error < 0) ? error : 0;
+-}
+-
+ /* Hack for future NFS swap support */
+ #ifndef IS_SWAPFILE
+ # define IS_SWAPFILE(inode)   (0)
+@@ -383,20 +416,27 @@ extern int  nfsroot_mount(struct sockadd
+ /*
+  * inline functions
+  */
+-static inline int
+-nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
++
++static inline int nfs_attribute_timeout(struct inode *inode)
+ {
+-      if (time_before(jiffies, NFS_READTIME(inode)+NFS_ATTRTIMEO(inode)))
+-              return NFS_STALE(inode) ? -ESTALE : 0;
+-      return __nfs_revalidate_inode(server, inode);
++      struct nfs_inode *nfsi = NFS_I(inode);
++
++      return time_after(jiffies, nfsi->read_cache_jiffies+nfsi->attrtimeo);
+ }
+-static inline int
+-nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
++/**
++ * nfs_revalidate_inode - Revalidate the inode attributes
++ * @server - pointer to nfs_server struct
++ * @inode - pointer to inode struct
++ *
++ * Updates inode attribute information by retrieving the data from the server.
++ */
++static inline int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
+ {
+-      if ((fattr->valid & NFS_ATTR_FATTR) == 0)
+-              return 0;
+-      return __nfs_refresh_inode(inode,fattr);
++      if (!(NFS_FLAGS(inode) & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA))
++                      && !nfs_attribute_timeout(inode))
++              return NFS_STALE(inode) ? -ESTALE : 0;
++      return __nfs_revalidate_inode(server, inode);
+ }
+ static inline loff_t
+@@ -590,6 +630,15 @@ struct nfs4_state {
+ extern struct dentry_operations nfs4_dentry_operations;
+ extern struct inode_operations nfs4_dir_inode_operations;
++extern struct inode_operations nfs4_file_inode_operations;
++
++#define NFS_ACL_MAX_ENTRIES   32
++
++/* inode.c */
++extern ssize_t nfs_getxattr(struct dentry *, const char *, void *, size_t);
++extern int nfs_setxattr(struct dentry *, const char *, const void *, size_t, int);
++
++#define NFS4_ACL_NOT_CACHED   ((void *)-1)
+ /* nfs4proc.c */
+ extern int nfs4_proc_setclientid(struct nfs4_client *, u32, unsigned short);
+@@ -602,6 +651,9 @@ int nfs4_do_downgrade(struct inode *inod
+ extern int nfs4_wait_clnt_recover(struct rpc_clnt *, struct nfs4_client *);
+ extern struct inode *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *);
+ extern int nfs4_open_revalidate(struct inode *, struct dentry *, int);
++struct posix_acl * nfs4_proc_get_posix_acl(struct inode *, int);
++extern int nfs4_proc_set_posix_acl(struct inode *, int, struct posix_acl *);
++void nfs4_izap_acl(struct inode *inode, struct posix_acl **i_acl);
+ /* nfs4renewd.c */
+ extern void nfs4_schedule_state_renewal(struct nfs4_client *);
+diff -puN include/linux/nfs_page.h~CITI_NFS4_ALL include/linux/nfs_page.h
+--- linux-2.6.3/include/linux/nfs_page.h~CITI_NFS4_ALL 2004-02-19 16:47:03.000000000 -0500
++++ linux-2.6.3-bfields/include/linux/nfs_page.h       2004-02-19 16:47:03.000000000 -0500
+@@ -53,7 +53,7 @@ extern       void nfs_release_request(struct n
+ extern        void nfs_list_add_request(struct nfs_page *, struct list_head *);
+ extern        int nfs_scan_list(struct list_head *, struct list_head *,
+-                        struct file *, unsigned long, unsigned int);
++                        unsigned long, unsigned int);
+ extern        int nfs_coalesce_requests(struct list_head *, struct list_head *,
+                                 unsigned int);
+ extern  int nfs_wait_on_request(struct nfs_page *);
+diff -puN include/linux/nfs_xdr.h~CITI_NFS4_ALL include/linux/nfs_xdr.h
+--- linux-2.6.3/include/linux/nfs_xdr.h~CITI_NFS4_ALL  2004-02-19 16:47:03.000000000 -0500
++++ linux-2.6.3-bfields/include/linux/nfs_xdr.h        2004-02-19 16:47:15.000000000 -0500
+@@ -39,6 +39,9 @@ struct nfs_fattr {
+       __u64                   change_attr;    /* NFSv4 change attribute */
+       __u64                   pre_change_attr;/* pre-op NFSv4 change attribute */
+       unsigned long           timestamp;
++#ifdef CONFIG_NFS_V4
++      struct nfs4_acl        *acl;            /* NFSv4 ACL */
++#endif /* CONFIG_NFS_V4 */
+ };
+ #define NFS_ATTR_WCC          0x0001          /* pre-op WCC data    */
+@@ -103,8 +106,6 @@ struct nfs_openargs {
+               nfs4_verifier   verifier; /* EXCLUSIVE */
+       } u;
+       struct qstr *           name;
+-      struct nfs4_getattr *   f_getattr;
+-      struct nfs4_getattr *   d_getattr;
+       struct nfs_server *     server;  /* Needed for ID mapping */
+ };
+@@ -113,8 +114,8 @@ struct nfs_openres {
+       struct nfs_fh           fh;
+       struct nfs4_change_info * cinfo;
+       __u32                   rflags;
+-      struct nfs4_getattr *   f_getattr;
+-      struct nfs4_getattr *   d_getattr;
++      struct nfs_fattr *      f_attr;
++      struct nfs_fattr *      d_attr;
+       struct nfs_server *     server;
+ };
+@@ -141,7 +142,6 @@ struct nfs_open_reclaimargs {
+       __u32                   id;
+       __u32                   share_access;
+       __u32                   claim;
+-      struct nfs4_getattr *   f_getattr;
+ };
+ /*
+@@ -319,12 +319,22 @@ struct nfs_setattrargs {
+       struct nfs_fh *                 fh;
+       nfs4_stateid                    stateid;
+       struct iattr *                  iap;
+-      struct nfs4_getattr *           attr;
++      struct nfs_fattr *              fattr;
+       struct nfs_server *             server; /* Needed for name mapping */
++#ifdef CONFIG_NFS_V4
++      struct nfs4_acl *               acl;
++#endif /* CONFIG_NFS_V4 */
+ };
++#ifdef CONFIG_NFS_V4
++struct nfs_setaclargs {
++      struct nfs_fh *                 fh;
++      struct nfs4_acl *               acl;
++};
++#endif /* CONFIG_NFS_V4 */
++
+ struct nfs_setattrres {
+-      struct nfs4_getattr *           attr;
++      struct nfs_fattr *              fattr;
+       struct nfs_server *             server;
+ };
+@@ -482,118 +492,127 @@ struct nfs4_change_info {
+       u64                             after;
+ };
+-struct nfs4_access {
+-      u32                             ac_req_access;     /* request */
+-      u32 *                           ac_resp_supported; /* response */
+-      u32 *                           ac_resp_access;    /* response */
++struct nfs4_accessargs {
++      struct nfs_fh *                 fhandle;
++      u32                             req_access;
+ };
+-struct nfs4_close {
+-      char *                          cl_stateid;        /* request */
+-      u32                             cl_seqid;          /* request */
++struct nfs4_accessres {
++      struct nfs_server *             server;
++      struct nfs_fattr *              fattr;
++      u32                             req_access;
++      u32 *                           resp_supported;
++      u32 *                           resp_access;
+ };
+-struct nfs4_create {
+-      u32                             cr_ftype;          /* request */
+-      union {                                            /* request */
+-              struct {
+-                      u32             textlen;
+-                      const char *    text;
+-              } symlink;   /* NF4LNK */
++struct nfs4_create_arg {
++      u32                             ftype;
++      union {
++              struct qstr *           symlink;    /* NF4LNK */
+               struct {
+                       u32             specdata1;
+                       u32             specdata2;
+               } device;    /* NF4BLK, NF4CHR */
+       } u;
+-      u32                             cr_namelen;        /* request */
+-      const char *                    cr_name;           /* request */
+-      struct iattr *                  cr_attrs;          /* request */
+-      struct nfs4_change_info *       cr_cinfo;          /* response */
++      struct qstr *                   name;
++      struct nfs_server *             server;
++      struct iattr *                  attrs;
++      struct nfs_fh *                 dir_fh;
+ };
+-#define cr_textlen                    u.symlink.textlen
+-#define cr_text                               u.symlink.text
+-#define cr_specdata1                  u.device.specdata1
+-#define cr_specdata2                  u.device.specdata2
+-struct nfs4_getattr {
+-        u32 *                         gt_bmval;          /* request */
+-        struct nfs_fattr *            gt_attrs;          /* response */
+-      struct nfs_fsstat *             gt_fsstat;         /* response */
+-      struct nfs_pathconf *           gt_pathconf;       /* response */
++struct nfs4_create_res {
++      struct nfs_server *             server;
++      struct nfs_fh *                 fhandle;
++      struct nfs_fattr *              fattr;
++      struct nfs_fattr *              dir_attr;
++      struct nfs4_change_info *       dir_cinfo;
+ };
+-struct nfs4_getfh {
+-      struct nfs_fh *                 gf_fhandle;       /* response */
++struct nfs4_getattr_res {
++      struct nfs_server *             server;
++      struct nfs_fattr *              fattr;
+ };
+-struct nfs4_link {
+-      u32                             ln_namelen;       /* request */
+-      const char *                    ln_name;          /* request */
+-      struct nfs4_change_info *       ln_cinfo;         /* response */
++struct nfs4_getroot_res {
++      struct nfs_server *             server;
++      struct nfs_fattr *              fattr;
++      struct nfs_fh *                 fhandle;
+ };
+-struct nfs4_lookup {
+-      struct qstr *                   lo_name;          /* request */
++struct nfs4_getroot_arg {
++      struct nfs_fh *                 fhandle;
++      struct qstr *                   name;
+ };
+-struct nfs4_open {
+-      struct nfs4_client *            op_client_state;  /* request */
+-      u32                             op_share_access;  /* request */
+-      u32                             op_opentype;      /* request */
+-      u32                             op_createmode;    /* request */
+-      union {                                           /* request */
+-              struct iattr *          attrs;    /* UNCHECKED, GUARDED */
+-              nfs4_verifier           verifier; /* EXCLUSIVE */
+-      } u;
+-      struct qstr *                   op_name;          /* request */
+-      char *                          op_stateid;       /* response */
+-      struct nfs4_change_info *       op_cinfo;         /* response */
+-      u32 *                           op_rflags;        /* response */
+-};
+-#define op_attrs     u.attrs
+-#define op_verifier  u.verifier
+-
+-struct nfs4_open_confirm {
+-      char *                          oc_stateid;       /* request */
+-};
+-
+-struct nfs4_putfh {
+-      struct nfs_fh *                 pf_fhandle;       /* request */
+-};
+-
+-struct nfs4_readdir {
+-      u64                             rd_cookie;        /* request */
+-      nfs4_verifier                   rd_req_verifier;  /* request */
+-      u32                             rd_count;         /* request */
+-      u32                             rd_bmval[2];      /* request */ 
+-      nfs4_verifier                   rd_resp_verifier; /* response */
+-      struct page **                  rd_pages;   /* zero-copy data */
+-      unsigned int                    rd_pgbase;  /* zero-copy data */
++struct nfs4_link_arg {
++      struct nfs_fh *                 fh;
++      struct nfs_fh *                 dir_fh;
++      struct qstr *                   name;
++};
++
++struct nfs4_link_res {
++      struct nfs_server *             server;
++      struct nfs_fattr *              fattr;
++      struct nfs_fattr *              dir_attr;
++      struct nfs4_change_info *       dir_cinfo;
++};
++
++struct nfs4_lookupargs {
++      struct nfs_fh *                 dir_fh;
++        struct qstr *                   name;
++};
++
++struct nfs4_lookupres {
++      struct nfs_server *             server;
++      struct nfs_fattr *              dirattr;
++      struct nfs_fattr *              fattr;
++      struct nfs_fh *                 fhandle;
++};
++
++struct nfs4_readdir_arg {
++      struct nfs_fh *                 fh;
++      u64                             cookie;        /* request */
++      nfs4_verifier                   req_verifier;  /* request */
++      u32                             count;         /* request */
++      struct page **                  pages;   /* zero-copy data */
++      unsigned int                    pgbase;  /* zero-copy data */
++};
++
++struct nfs4_readdir_res {
++      nfs4_verifier                   resp_verifier;
++      unsigned int                    pgbase;
+ };
+ struct nfs4_readlink {
+-      u32                             rl_count;   /* zero-copy data */
+-      struct page **                  rl_pages;   /* zero-copy data */
++      struct nfs_fh *                 fh;
++      u32                             count;   /* zero-copy data */
++      struct page **                  pages;   /* zero-copy data */
+ };
+-struct nfs4_remove {
+-      u32                             rm_namelen;       /* request */
+-      const char *                    rm_name;          /* request */
+-      struct nfs4_change_info *       rm_cinfo;         /* response */
++struct nfs4_remove_arg {
++      struct nfs_fh *                 fhandle;
++      struct qstr *                   name;
+ };
+-struct nfs4_rename {
+-      u32                             rn_oldnamelen;    /* request */
+-      const char *                    rn_oldname;       /* request */
+-      u32                             rn_newnamelen;    /* request */
+-      const char *                    rn_newname;       /* request */
+-      struct nfs4_change_info *       rn_src_cinfo;     /* response */
+-      struct nfs4_change_info *       rn_dst_cinfo;     /* response */
++struct nfs4_remove_res {
++      struct nfs_server *             server;
++      struct nfs4_change_info *       dir_cinfo;
++      struct nfs_fattr *              dir_attr;
+ };
+-struct nfs4_setattr {
+-      char *                          st_stateid;       /* request */
+-      struct iattr *                  st_iap;           /* request */
++struct nfs4_rename_arg {
++      struct nfs_fh *                 old_dir;
++        struct nfs_fh *                 new_dir;
++        struct qstr *                   old_name;
++        struct qstr *                   new_name;
++};
++
++struct nfs4_rename_res {
++        struct nfs_server *             server;
++      struct nfs4_change_info *       old_cinfo;
++      struct nfs4_change_info *       new_cinfo;
++        struct nfs_fattr *              old_fattr;
++      struct nfs_fattr *              new_fattr;
+ };
+ struct nfs4_setclientid {
+@@ -606,52 +625,12 @@ struct nfs4_setclientid {
+       struct nfs4_client *            sc_state;         /* response */
+ };
+-struct nfs4_op {
+-      u32                             opnum;
+-      union {
+-              struct nfs4_access      access;
+-              struct nfs4_close       close;
+-              struct nfs4_create      create;
+-              struct nfs4_getattr     getattr;
+-              struct nfs4_getfh       getfh;
+-              struct nfs4_link        link;
+-              struct nfs4_lookup      lookup;
+-              struct nfs4_open        open;
+-              struct nfs4_open_confirm open_confirm;
+-              struct nfs4_putfh       putfh;
+-              struct nfs4_readdir     readdir;
+-              struct nfs4_readlink    readlink;
+-              struct nfs4_remove      remove;
+-              struct nfs4_rename      rename;
+-              struct nfs4_client *    renew;
+-              struct nfs4_setattr     setattr;
+-      } u;
+-};
+-
+-struct nfs4_compound {
+-      unsigned int            flags;   /* defined below */
+-      struct nfs_server *     server;
+-
+-      /* RENEW information */
+-      int                     renew_index;
+-      unsigned long           timestamp;
+-
+-      /* scratch variables for XDR encode/decode */
+-      int                     nops;
+-      u32 *                   p;
+-      u32 *                   end;
+-
+-      /* the individual COMPOUND operations */
+-      struct nfs4_op          *ops;
+-
+-      /* request */
+-      int                     req_nops;
+-      u32                     taglen;
+-      char *                  tag;
+-      
+-      /* response */
+-      int                     resp_nops;
+-      int                     toplevel_status;
++struct nfs4_unlink {
++      struct nfs_server *             server;
++      struct nfs_fh *                 fh;
++      struct qstr *                   name;
++      struct nfs4_change_info         cinfo;  /* NOT a pointer */
++      struct nfs_fattr                attrs;  /* NOT a pointer */
+ };
+ #endif /* CONFIG_NFS_V4 */
+@@ -698,9 +677,10 @@ struct nfs_rpc_ops {
+       int     version;                /* Protocol version */
+       struct dentry_operations *dentry_ops;
+       struct inode_operations *dir_inode_ops;
++      struct inode_operations *file_inode_ops;
+       int     (*getroot) (struct nfs_server *, struct nfs_fh *,
+-                          struct nfs_fattr *);
++                          struct nfs_fsinfo *);
+       int     (*getattr) (struct inode *, struct nfs_fattr *);
+       int     (*setattr) (struct dentry *, struct nfs_fattr *,
+                           struct iattr *);
+diff -puN include/linux/sunrpc/xprt.h~CITI_NFS4_ALL include/linux/sunrpc/xprt.h
+--- linux-2.6.3/include/linux/sunrpc/xprt.h~CITI_NFS4_ALL      2004-02-19 16:47:03.000000000 -0500
++++ linux-2.6.3-bfields/include/linux/sunrpc/xprt.h    2004-02-19 16:47:05.000000000 -0500
+@@ -95,14 +95,15 @@ struct rpc_rqst {
+       struct rpc_rqst *       rq_next;        /* free list */
+       int                     rq_cong;        /* has incremented xprt->cong */
+       int                     rq_received;    /* receive completed */
+-      u32                     rq_seqno;       /* gss seq no. used on req. */
++#define GSS_SEQNO_CACHE               4
++      u32                     rq_seqnos[GSS_SEQNO_CACHE];
++                                              /* gss seq no.s used on req. */
+       struct list_head        rq_list;
+       struct xdr_buf          rq_private_buf;         /* The receive buffer
+                                                        * used in the softirq.
+                                                        */
+-
+       /*
+        * For authentication (e.g. auth_des)
+        */
+@@ -155,6 +156,11 @@ struct rpc_xprt {
+                               stream     : 1; /* TCP */
+       /*
++       * XID
++       */
++      __u32                   xid;            /* Next XID value to use */
++
++      /*
+        * State of TCP reply receive stuff
+        */
+       u32                     tcp_recm,       /* Fragment header */
+@@ -164,6 +170,11 @@ struct rpc_xprt {
+       unsigned long           tcp_copied,     /* copied to request */
+                               tcp_flags;
+       /*
++       * Connection of sockets
++       */
++      struct work_struct      sock_connect;
++      unsigned short          port;
++      /*
+        * Disconnection of idle sockets
+        */
+       struct work_struct      task_cleanup;
+diff -puN net/sunrpc/xprt.c~CITI_NFS4_ALL net/sunrpc/xprt.c
+--- linux-2.6.3/net/sunrpc/xprt.c~CITI_NFS4_ALL        2004-02-19 16:47:03.000000000 -0500
++++ linux-2.6.3-bfields/net/sunrpc/xprt.c      2004-02-19 16:47:05.000000000 -0500
+@@ -60,6 +60,7 @@
+ #include <linux/sunrpc/clnt.h>
+ #include <linux/file.h>
+ #include <linux/workqueue.h>
++#include <linux/random.h>
+ #include <net/sock.h>
+ #include <net/checksum.h>
+@@ -77,6 +78,7 @@
+ #define XPRT_MAX_BACKOFF      (8)
+ #define XPRT_IDLE_TIMEOUT     (5*60*HZ)
++#define XPRT_MAX_RESVPORT     (800)
+ /*
+  * Local functions
+@@ -87,7 +89,7 @@ static void  xprt_disconnect(struct rpc_x
+ static void   xprt_connect_status(struct rpc_task *task);
+ static struct rpc_xprt * xprt_setup(int proto, struct sockaddr_in *ap,
+                                               struct rpc_timeout *to);
+-static struct socket *xprt_create_socket(int, struct rpc_timeout *, int);
++static struct socket *xprt_create_socket(struct rpc_xprt *, int, int);
+ static void   xprt_bind_socket(struct rpc_xprt *, struct socket *);
+ static int      __xprt_get_cong(struct rpc_xprt *, struct rpc_task *);
+@@ -455,6 +457,68 @@ out_abort:
+       spin_unlock(&xprt->sock_lock);
+ }
++static void
++xprt_socket_connect(void *args)
++{
++      struct rpc_xprt *xprt = (struct rpc_xprt *)args;
++      struct socket *sock = xprt->sock;
++      int status = -EIO;
++
++      if (xprt->shutdown) {
++              rpc_wake_up_status(&xprt->pending, -EIO);
++              return;
++      }
++      if (!xprt->addr.sin_port)
++              goto out_err;
++
++      /*
++       * Start by resetting any existing state
++       */
++      xprt_close(xprt);
++      sock = xprt_create_socket(xprt, xprt->prot, xprt->resvport);
++      if (sock == NULL) {
++              /* couldn't create socket or bind to reserved port;
++               * this is likely a permanent error, so cause an abort */
++              goto out_err;
++              return;
++      }
++      xprt_bind_socket(xprt, sock);
++      xprt_sock_setbufsize(xprt);
++
++      if (!xprt->stream)
++              goto out;
++
++      /*
++       * Tell the socket layer to start connecting...
++       */
++      status = sock->ops->connect(sock, (struct sockaddr *) &xprt->addr,
++                      sizeof(xprt->addr), O_NONBLOCK);
++      dprintk("RPC: %p  connect status %d connected %d sock state %d\n",
++                      xprt, -status, xprt_connected(xprt), sock->sk->sk_state);
++      if (status >= 0)
++              goto out;
++      switch (status) {
++              case -EINPROGRESS:
++              case -EALREADY:
++                      break;
++              default:
++                      goto out_err;
++      }
++out:
++      spin_lock_bh(&xprt->sock_lock);
++      if (xprt->snd_task)
++              rpc_wake_up_task(xprt->snd_task);
++      spin_unlock_bh(&xprt->sock_lock);
++      return;
++out_err:
++      spin_lock_bh(&xprt->sock_lock);
++      if (xprt->snd_task) {
++              xprt->snd_task->tk_status = status;
++              rpc_wake_up_task(xprt->snd_task);
++      }
++      spin_unlock_bh(&xprt->sock_lock);
++}
++
+ /*
+  * Attempt to connect a TCP socket.
+  *
+@@ -463,9 +527,6 @@ void
+ xprt_connect(struct rpc_task *task)
+ {
+       struct rpc_xprt *xprt = task->tk_xprt;
+-      struct socket   *sock = xprt->sock;
+-      struct sock     *inet;
+-      int             status;
+       dprintk("RPC: %4d xprt_connect xprt %p %s connected\n", task->tk_pid,
+                       xprt, (xprt_connected(xprt) ? "is" : "is not"));
+@@ -486,79 +547,9 @@ xprt_connect(struct rpc_task *task)
+       if (task->tk_rqstp)
+               task->tk_rqstp->rq_bytes_sent = 0;
+-      /*
+-       * We're here because the xprt was marked disconnected.
+-       * Start by resetting any existing state.
+-       */
+-      xprt_close(xprt);
+-      if (!(sock = xprt_create_socket(xprt->prot, &xprt->timeout, xprt->resvport))) {
+-              /* couldn't create socket or bind to reserved port;
+-               * this is likely a permanent error, so cause an abort */
+-              task->tk_status = -EIO;
+-              goto out_write;
+-      }
+-      xprt_bind_socket(xprt, sock);
+-      xprt_sock_setbufsize(xprt);
+-
+-      if (!xprt->stream)
+-              goto out_write;
+-
+-      inet = sock->sk;
+-
+-      /*
+-       * Tell the socket layer to start connecting...
+-       */
+-      status = sock->ops->connect(sock, (struct sockaddr *) &xprt->addr,
+-                              sizeof(xprt->addr), O_NONBLOCK);
+-      dprintk("RPC: %4d  connect status %d connected %d sock state %d\n",
+-              task->tk_pid, -status, xprt_connected(xprt), inet->sk_state);
+-
+-      if (status >= 0)
+-              return;
+-
+-      switch (status) {
+-      case -EINPROGRESS:
+-      case -EALREADY:
+-              /* Protect against TCP socket state changes */
+-              lock_sock(inet);
+-              if (inet->sk_state != TCP_ESTABLISHED) {
+-                      dprintk("RPC: %4d  waiting for connection\n",
+-                                      task->tk_pid);
+-                      task->tk_timeout = RPC_CONNECT_TIMEOUT;
+-                      /* if the socket is already closing, delay briefly */
+-                      if ((1 << inet->sk_state) &
+-                          ~(TCPF_SYN_SENT | TCPF_SYN_RECV))
+-                              task->tk_timeout = RPC_REESTABLISH_TIMEOUT;
+-                      rpc_sleep_on(&xprt->pending, task, xprt_connect_status,
+-                                                                      NULL);
+-              }
+-              release_sock(inet);
+-              break;
+-      case -ECONNREFUSED:
+-      case -ECONNRESET:
+-      case -ENOTCONN:
+-              if (!RPC_IS_SOFT(task)) {
+-                      rpc_delay(task, RPC_REESTABLISH_TIMEOUT);
+-                      task->tk_status = -ENOTCONN;
+-                      break;
+-              }
+-      default:
+-              /* Report myriad other possible returns.  If this file
+-               * system is soft mounted, just error out, like Solaris.  */
+-              if (RPC_IS_SOFT(task)) {
+-                      printk(KERN_WARNING
+-                      "RPC: error %d connecting to server %s, exiting\n",
+-                                      -status, task->tk_client->cl_server);
+-                      task->tk_status = -EIO;
+-                      goto out_write;
+-              }
+-              printk(KERN_WARNING "RPC: error %d connecting to server %s\n",
+-                              -status, task->tk_client->cl_server);
+-              /* This will prevent anybody else from reconnecting */
+-              rpc_delay(task, RPC_REESTABLISH_TIMEOUT);
+-              task->tk_status = status;
+-              break;
+-      }
++      task->tk_timeout = RPC_CONNECT_TIMEOUT;
++      rpc_sleep_on(&xprt->pending, task, xprt_connect_status, NULL);
++      schedule_work(&xprt->sock_connect);
+       return;
+  out_write:
+       xprt_release_write(xprt, task);
+@@ -583,6 +574,8 @@ xprt_connect_status(struct rpc_task *tas
+               task->tk_status = -EIO;
+       switch (task->tk_status) {
++      case -ECONNREFUSED:
++      case -ECONNRESET:
+       case -ENOTCONN:
+               rpc_delay(task, RPC_REESTABLISH_TIMEOUT);
+               return;
+@@ -1333,22 +1326,14 @@ do_xprt_reserve(struct rpc_task *task)
+ /*
+  * Allocate a 'unique' XID
+  */
+-static u32
+-xprt_alloc_xid(void)
++static inline u32 xprt_alloc_xid(struct rpc_xprt *xprt)
++{
++      return xprt->xid++;
++}
++
++static inline void xprt_init_xid(struct rpc_xprt *xprt)
+ {
+-      static spinlock_t xid_lock = SPIN_LOCK_UNLOCKED;
+-      static int need_init = 1;
+-      static u32 xid;
+-      u32 ret;
+-
+-      spin_lock(&xid_lock);
+-      if (unlikely(need_init)) {
+-              xid = get_seconds() << 12;
+-              need_init = 0;
+-      }
+-      ret = xid++;
+-      spin_unlock(&xid_lock);
+-      return ret;
++      get_random_bytes(&xprt->xid, sizeof(xprt->xid));
+ }
+ /*
+@@ -1362,7 +1347,8 @@ xprt_request_init(struct rpc_task *task,
+       req->rq_timeout = xprt->timeout;
+       req->rq_task    = task;
+       req->rq_xprt    = xprt;
+-      req->rq_xid     = xprt_alloc_xid();
++      req->rq_xid     = xprt_alloc_xid(xprt);
++      memset(req->rq_seqnos, 0, sizeof(req->rq_seqnos));
+       INIT_LIST_HEAD(&req->rq_list);
+       dprintk("RPC: %4d reserved req %p xid %08x\n", task->tk_pid,
+                       req, req->rq_xid);
+@@ -1457,11 +1443,13 @@ xprt_setup(int proto, struct sockaddr_in
+       init_waitqueue_head(&xprt->cong_wait);
+       INIT_LIST_HEAD(&xprt->recv);
++      INIT_WORK(&xprt->sock_connect, xprt_socket_connect, xprt);
+       INIT_WORK(&xprt->task_cleanup, xprt_socket_autoclose, xprt);
+       init_timer(&xprt->timer);
+       xprt->timer.function = xprt_init_autodisconnect;
+       xprt->timer.data = (unsigned long) xprt;
+       xprt->last_used = jiffies;
++      xprt->port = XPRT_MAX_RESVPORT;
+       /* Set timeout parameters */
+       if (to) {
+@@ -1481,6 +1469,8 @@ xprt_setup(int proto, struct sockaddr_in
+       req->rq_next = NULL;
+       xprt->free = xprt->slot;
++      xprt_init_xid(xprt);
++
+       /* Check whether we want to use a reserved port */
+       xprt->resvport = capable(CAP_NET_BIND_SERVICE) ? 1 : 0;
+@@ -1493,30 +1483,28 @@ xprt_setup(int proto, struct sockaddr_in
+  * Bind to a reserved port
+  */
+ static inline int
+-xprt_bindresvport(struct socket *sock)
++xprt_bindresvport(struct rpc_xprt *xprt, struct socket *sock)
+ {
+-      struct sockaddr_in myaddr;
++      struct sockaddr_in myaddr = {
++              .sin_family = AF_INET,
++      };
+       int             err, port;
+-      kernel_cap_t saved_cap = current->cap_effective;
+-      /* Override capabilities.
+-       * They were checked in xprt_create_proto i.e. at mount time
+-       */
+-      cap_raise(current->cap_effective, CAP_NET_BIND_SERVICE);
+-
+-      memset(&myaddr, 0, sizeof(myaddr));
+-      myaddr.sin_family = AF_INET;
+-      port = 800;
++      /* Were we already bound to a given port? Try to reuse it */
++      port = xprt->port;
+       do {
+               myaddr.sin_port = htons(port);
+               err = sock->ops->bind(sock, (struct sockaddr *) &myaddr,
+                                               sizeof(myaddr));
+-      } while (err == -EADDRINUSE && --port > 0);
+-      current->cap_effective = saved_cap;
+-
+-      if (err < 0)
+-              printk("RPC: Can't bind to reserved port (%d).\n", -err);
++              if (err == 0) {
++                      xprt->port = port;
++                      return 0;
++              }
++              if (--port == 0)
++                      port = XPRT_MAX_RESVPORT;
++      } while (err == -EADDRINUSE && port != xprt->port);
++      printk("RPC: Can't bind to reserved port (%d).\n", -err);
+       return err;
+ }
+@@ -1580,7 +1568,7 @@ xprt_sock_setbufsize(struct rpc_xprt *xp
+  * and connect stream sockets.
+  */
+ static struct socket *
+-xprt_create_socket(int proto, struct rpc_timeout *to, int resvport)
++xprt_create_socket(struct rpc_xprt *xprt, int proto, int resvport)
+ {
+       struct socket   *sock;
+       int             type, err;
+@@ -1596,7 +1584,7 @@ xprt_create_socket(int proto, struct rpc
+       }
+       /* If the caller has the capability, bind to a reserved port */
+-      if (resvport && xprt_bindresvport(sock) < 0) {
++      if (resvport && xprt_bindresvport(xprt, sock) < 0) {
+               printk("RPC: can't bind to reserved port.\n");
+               goto failed;
+       }
+diff -puN net/sunrpc/cache.c~CITI_NFS4_ALL net/sunrpc/cache.c
+--- linux-2.6.3/net/sunrpc/cache.c~CITI_NFS4_ALL       2004-02-19 16:47:03.000000000 -0500
++++ linux-2.6.3-bfields/net/sunrpc/cache.c     2004-02-19 16:47:03.000000000 -0500
+@@ -325,6 +325,7 @@ int cache_clean(void)
+       
+       if (current_detail && current_index < current_detail->hash_size) {
+               struct cache_head *ch, **cp;
++              struct cache_detail *d;
+               
+               write_lock(&current_detail->hash_lock);
+@@ -354,12 +355,14 @@ int cache_clean(void)
+                       rv = 1;
+               }
+               write_unlock(&current_detail->hash_lock);
+-              if (ch)
+-                      current_detail->cache_put(ch, current_detail);
+-              else
++              d = current_detail;
++              if (!ch)
+                       current_index ++;
+-      }
+-      spin_unlock(&cache_list_lock);
++              spin_unlock(&cache_list_lock);
++              if (ch)
++                      d->cache_put(ch, d);
++      } else
++              spin_unlock(&cache_list_lock);
+       return rv;
+ }
+diff -puN include/linux/sunrpc/cache.h~CITI_NFS4_ALL include/linux/sunrpc/cache.h
+--- linux-2.6.3/include/linux/sunrpc/cache.h~CITI_NFS4_ALL     2004-02-19 16:47:03.000000000 -0500
++++ linux-2.6.3-bfields/include/linux/sunrpc/cache.h   2004-02-19 16:47:03.000000000 -0500
+@@ -132,12 +132,14 @@ struct cache_deferred_req {
+  * If "set" == 0 :
+  *    If an entry is found, it is returned
+  *    If no entry is found, a new non-VALID entry is created.
+- * If "set" == 1 :
++ * If "set" == 1 and INPLACE == 0 :
+  *    If no entry is found a new one is inserted with data from "template"
+  *    If a non-CACHE_VALID entry is found, it is updated from template using UPDATE
+  *    If a CACHE_VALID entry is found, a new entry is swapped in with data
+  *       from "template"
+- * If set == 2, we UPDATE, but don't swap. i.e. update in place
++ * If set == 1, and INPLACE == 1 :
++ *    As above, except that if a CACHE_VALID entry is found, we UPDATE in place
++ *       instead of swapping in a new entry.
+  *
+  * If the passed handle has the CACHE_NEGATIVE flag set, then UPDATE is not
+  * run but insteead CACHE_NEGATIVE is set in any new item.
+@@ -164,8 +166,8 @@ RTN *FNAME ARGS                                                                            \
+       RTN *tmp, *new=NULL;                                                            \
+       struct cache_head **hp, **head;                                                 \
+       SETUP;                                                                          \
+- retry:                                                                                       \
+       head = &(DETAIL)->hash_table[HASHFN];                                           \
++ retry:                                                                                       \
+       if (set||new) write_lock(&(DETAIL)->hash_lock);                                 \
+       else read_lock(&(DETAIL)->hash_lock);                                           \
+       for(hp=head; *hp != NULL; hp = &tmp->MEMBER.next) {                             \
+@@ -175,6 +177,8 @@ RTN *FNAME ARGS                                                                            \
+                       if (set && !INPLACE && test_bit(CACHE_VALID, &tmp->MEMBER.flags) && !new) \
+                               break;                                                  \
+                                                                                       \
++                      if (new)                                                        \
++                              {INIT;}                                                 \
+                       cache_get(&tmp->MEMBER);                                        \
+                       if (set) {                                                      \
+                               if (!INPLACE && test_bit(CACHE_VALID, &tmp->MEMBER.flags))\
+@@ -203,6 +207,7 @@ RTN *FNAME ARGS                                                                            \
+       }                                                                               \
+       /* Didn't find anything */                                                      \
+       if (new) {                                                                      \
++              INIT;                                                                   \
+               new->MEMBER.next = *head;                                               \
+               *head = &new->MEMBER;                                                   \
+               (DETAIL)->entries ++;                                                   \
+@@ -224,8 +229,6 @@ RTN *FNAME ARGS                                                                            \
+       if (new) {                                                                      \
+               cache_init(&new->MEMBER);                                               \
+               cache_get(&new->MEMBER);                                                \
+-              INIT;                                                                   \
+-              tmp = new;                                                              \
+               goto retry;                                                             \
+       }                                                                               \
+       return NULL;                                                                    \
+diff -puN net/sunrpc/svcauth.c~CITI_NFS4_ALL net/sunrpc/svcauth.c
+--- linux-2.6.3/net/sunrpc/svcauth.c~CITI_NFS4_ALL     2004-02-19 16:47:03.000000000 -0500
++++ linux-2.6.3-bfields/net/sunrpc/svcauth.c   2004-02-19 16:47:04.000000000 -0500
+@@ -150,7 +150,10 @@ DefineCacheLookup(struct auth_domain,
+                 &auth_domain_cache,
+                 auth_domain_hash(item),
+                 auth_domain_match(tmp, item),
+-                kfree(new); if(!set) return NULL;
++                kfree(new); if(!set) {
++                write_unlock(&auth_domain_cache.hash_lock);
++                return NULL;
++                }
+                 new=item; atomic_inc(&new->h.refcnt),
+                 /* no update */,
+                 0 /* no inplace updates */
+diff -puN net/sunrpc/svcauth_unix.c~CITI_NFS4_ALL net/sunrpc/svcauth_unix.c
+--- linux-2.6.3/net/sunrpc/svcauth_unix.c~CITI_NFS4_ALL        2004-02-19 16:47:03.000000000 -0500
++++ linux-2.6.3-bfields/net/sunrpc/svcauth_unix.c      2004-02-19 16:47:03.000000000 -0500
+@@ -119,7 +119,8 @@ static inline int ip_map_match(struct ip
+ }
+ static inline void ip_map_init(struct ip_map *new, struct ip_map *item)
+ {
+-      new->m_class = strdup(item->m_class);
++      new->m_class = item->m_class;
++      item->m_class = NULL;
+       new->m_addr.s_addr = item->m_addr.s_addr;
+ }
+ static inline void ip_map_update(struct ip_map *new, struct ip_map *item)
+@@ -191,7 +192,9 @@ static int ip_map_parse(struct cache_det
+       } else
+               dom = NULL;
+-      ipm.m_class = class;
++      ipm.m_class = strdup(class);
++      if (ipm.m_class == NULL)
++              return -ENOMEM;
+       ipm.m_addr.s_addr =
+               htonl((((((b1<<8)|b2)<<8)|b3)<<8)|b4);
+       ipm.h.flags = 0;
+@@ -207,6 +210,7 @@ static int ip_map_parse(struct cache_det
+               ip_map_put(&ipmp->h, &ip_map_cache);
+       if (dom)
+               auth_domain_put(dom);
++      if (ipm.m_class) kfree(ipm.m_class);
+       if (!ipmp)
+               return -ENOMEM;
+       cache_flush();
+@@ -266,7 +270,9 @@ int auth_unix_add_addr(struct in_addr ad
+       if (dom->flavour != RPC_AUTH_UNIX)
+               return -EINVAL;
+       udom = container_of(dom, struct unix_domain, h);
+-      ip.m_class = "nfsd";
++      ip.m_class = strdup("nfsd");
++      if (!ip.m_class)
++              return -ENOMEM;
+       ip.m_addr = addr;
+       ip.m_client = udom;
+       ip.m_add_change = udom->addr_changes+1;
+@@ -274,6 +280,7 @@ int auth_unix_add_addr(struct in_addr ad
+       ip.h.expiry_time = NEVER;
+       
+       ipmp = ip_map_lookup(&ip, 1);
++      if (ip.m_class) kfree(ip.m_class);
+       if (ipmp) {
+               ip_map_put(&ipmp->h, &ip_map_cache);
+               return 0;
+diff -puN fs/nfsd/stats.c~CITI_NFS4_ALL fs/nfsd/stats.c
+--- linux-2.6.3/fs/nfsd/stats.c~CITI_NFS4_ALL  2004-02-19 16:47:04.000000000 -0500
++++ linux-2.6.3-bfields/fs/nfsd/stats.c        2004-02-19 16:47:04.000000000 -0500
+@@ -26,6 +26,7 @@
+ #include <linux/kernel.h>
+ #include <linux/time.h>
+ #include <linux/proc_fs.h>
++#include <linux/seq_file.h>
+ #include <linux/stat.h>
+ #include <linux/module.h>
+@@ -39,14 +40,11 @@ struct svc_stat            nfsd_svcstats = {
+       .program        = &nfsd_program,
+ };
+-static int
+-nfsd_proc_read(char *buffer, char **start, off_t offset, int count,
+-                              int *eof, void *data)
++static int nfsd_proc_show(struct seq_file *seq, void *v)
+ {
+-      int     len;
+-      int     i;
++      int i;
+-      len = sprintf(buffer, "rc %u %u %u\nfh %u %u %u %u %u\nio %u %u\n",
++      seq_printf(seq, "rc %u %u %u\nfh %u %u %u %u %u\nio %u %u\n",
+                     nfsdstats.rchits,
+                     nfsdstats.rcmisses,
+                     nfsdstats.rcnocache,
+@@ -58,57 +56,42 @@ nfsd_proc_read(char *buffer, char **star
+                     nfsdstats.io_read,
+                     nfsdstats.io_write);
+       /* thread usage: */
+-      len += sprintf(buffer+len, "th %u %u", nfsdstats.th_cnt, nfsdstats.th_fullcnt);
++      seq_printf(seq, "th %u %u", nfsdstats.th_cnt, nfsdstats.th_fullcnt);
+       for (i=0; i<10; i++) {
+               unsigned int jifs = nfsdstats.th_usage[i];
+               unsigned int sec = jifs / HZ, msec = (jifs % HZ)*1000/HZ;
+-              len += sprintf(buffer+len, " %u.%03u", sec, msec);
++              seq_printf(seq, " %u.%03u", sec, msec);
+       }
+       /* newline and ra-cache */
+-      len += sprintf(buffer+len, "\nra %u", nfsdstats.ra_size);
++      seq_printf(seq, "\nra %u", nfsdstats.ra_size);
+       for (i=0; i<11; i++)
+-              len += sprintf(buffer+len, " %u", nfsdstats.ra_depth[i]);
+-      len += sprintf(buffer+len, "\n");
++              seq_printf(seq, " %u", nfsdstats.ra_depth[i]);
++      seq_putc(seq, '\n');
+       
++      /* show my rpc info */
++      svc_seq_show(seq, &nfsd_svcstats);
+-      /* Assume we haven't hit EOF yet. Will be set by svc_proc_read. */
+-      *eof = 0;
+-
+-      /*
+-       * Append generic nfsd RPC statistics if there's room for it.
+-       */
+-      if (len <= offset) {
+-              len = svc_proc_read(buffer, start, offset - len, count,
+-                                  eof, data);
+-              return len;
+-      }
+-
+-      if (len < count) {
+-              len += svc_proc_read(buffer + len, start, 0, count - len,
+-                                   eof, data);
+-      }
+-
+-      if (offset >= len) {
+-              *start = buffer;
+-              return 0;
+-      }
++      return 0;
++}
+-      *start = buffer + offset;
+-      if ((len -= offset) > count)
+-              return count;
+-      return len;
++static int nfsd_proc_open(struct inode *inode, struct file *file)
++{
++      return single_open(file, nfsd_proc_show, NULL);
+ }
++static struct file_operations nfsd_proc_fops = {
++      .owner = THIS_MODULE,
++      .open = nfsd_proc_open,
++      .read  = seq_read,
++      .llseek = seq_lseek,
++      .release = single_release,
++};    
++
+ void
+ nfsd_stat_init(void)
+ {
+-      struct proc_dir_entry   *ent;
+-
+-      if ((ent = svc_proc_register(&nfsd_svcstats)) != 0) {
+-              ent->read_proc = nfsd_proc_read;
+-              ent->owner = THIS_MODULE;
+-      }
++      svc_proc_register(&nfsd_svcstats, &nfsd_proc_fops);
+ }
+ void
+diff -puN include/linux/sunrpc/stats.h~CITI_NFS4_ALL include/linux/sunrpc/stats.h
+--- linux-2.6.3/include/linux/sunrpc/stats.h~CITI_NFS4_ALL     2004-02-19 16:47:04.000000000 -0500
++++ linux-2.6.3-bfields/include/linux/sunrpc/stats.h   2004-02-19 16:47:04.000000000 -0500
+@@ -48,14 +48,13 @@ void                       rpc_modcount(struct inode *, int)
+ #ifdef CONFIG_PROC_FS
+ struct proc_dir_entry *       rpc_proc_register(struct rpc_stat *);
+ void                  rpc_proc_unregister(const char *);
+-int                   rpc_proc_read(char *, char **, off_t, int,
+-                                      int *, void *);
+ void                  rpc_proc_zero(struct rpc_program *);
+-struct proc_dir_entry *       svc_proc_register(struct svc_stat *);
++struct proc_dir_entry *       svc_proc_register(struct svc_stat *, 
++                                        struct file_operations *);
+ void                  svc_proc_unregister(const char *);
+-int                   svc_proc_read(char *, char **, off_t, int,
+-                                      int *, void *);
+-void                  svc_proc_zero(struct svc_program *);
++
++void                  svc_seq_show(struct seq_file *, 
++                                   const struct svc_stat *);
+ extern struct proc_dir_entry  *proc_net_rpc;
+@@ -63,13 +62,14 @@ extern struct proc_dir_entry       *proc_net_r
+ static inline struct proc_dir_entry *rpc_proc_register(struct rpc_stat *s) { return NULL; }
+ static inline void rpc_proc_unregister(const char *p) {}
+-static inline int rpc_proc_read(char *a, char **b, off_t c, int d, int *e, void *f) { return 0; }
+ static inline void rpc_proc_zero(struct rpc_program *p) {}
+-static inline struct proc_dir_entry *svc_proc_register(struct svc_stat *s) { return NULL; }
++static inline struct proc_dir_entry *svc_proc_register(struct svc_stat *s,
++                                                     struct file_operations *f) { return NULL; }
+ static inline void svc_proc_unregister(const char *p) {}
+-static inline int svc_proc_read(char *a, char **b, off_t c, int d, int *e, void *f) { return 0; }
+-static inline void svc_proc_zero(struct svc_program *p) {}
++
++static inline void svc_seq_show(struct seq_file *seq, 
++                              const struct svc_stat *st) {}
+ #define proc_net_rpc NULL
+diff -puN net/sunrpc/stats.c~CITI_NFS4_ALL net/sunrpc/stats.c
+--- linux-2.6.3/net/sunrpc/stats.c~CITI_NFS4_ALL       2004-02-19 16:47:04.000000000 -0500
++++ linux-2.6.3-bfields/net/sunrpc/stats.c     2004-02-19 16:47:04.000000000 -0500
+@@ -18,6 +18,7 @@
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/proc_fs.h>
++#include <linux/seq_file.h>
+ #include <linux/sunrpc/clnt.h>
+ #include <linux/sunrpc/svcsock.h>
+@@ -28,70 +29,66 @@ struct proc_dir_entry      *proc_net_rpc = NU
+ /*
+  * Get RPC client stats
+  */
+-int
+-rpc_proc_read(char *buffer, char **start, off_t offset, int count,
+-                              int *eof, void *data)
+-{
+-      struct rpc_stat *statp = (struct rpc_stat *) data;
+-      struct rpc_program *prog = statp->program;
+-      struct rpc_version *vers;
+-      int             len, i, j;
++static int rpc_proc_show(struct seq_file *seq, void *v) {
++      const struct rpc_stat   *statp = seq->private;
++      const struct rpc_program *prog = statp->program;
++      int             i, j;
+-      len = sprintf(buffer,
++      seq_printf(seq, 
+               "net %d %d %d %d\n",
+                       statp->netcnt,
+                       statp->netudpcnt,
+                       statp->nettcpcnt,
+                       statp->nettcpconn);
+-      len += sprintf(buffer + len,
++      seq_printf(seq, 
+               "rpc %d %d %d\n",
+                       statp->rpccnt,
+                       statp->rpcretrans,
+                       statp->rpcauthrefresh);
+       for (i = 0; i < prog->nrvers; i++) {
+-              if (!(vers = prog->version[i]))
++              const struct rpc_version *vers = prog->version[i];
++              if (!vers)
+                       continue;
+-              len += sprintf(buffer + len, "proc%d %d",
++              seq_printf(seq, "proc%d %d",
+                                       vers->number, vers->nrprocs);
+               for (j = 0; j < vers->nrprocs; j++)
+-                      len += sprintf(buffer + len, " %d",
++                      seq_printf(seq, " %d",
+                                       vers->procs[j].p_count);
+-              buffer[len++] = '\n';
++              seq_putc(seq, '\n');
+       }
++      return 0;
++}
+-      if (offset >= len) {
+-              *start = buffer;
+-              *eof = 1;
+-              return 0;
+-      }
+-      *start = buffer + offset;
+-      if ((len -= offset) > count)
+-              return count;
+-      *eof = 1;
+-      return len;
++static int rpc_proc_open(struct inode *inode, struct file *file)
++{
++      return single_open(file, rpc_proc_show, PDE(inode)->data);
+ }
++static struct file_operations rpc_proc_fops = {
++      .owner = THIS_MODULE,
++      .open = rpc_proc_open,
++      .read  = seq_read,
++      .llseek = seq_lseek,
++      .release = single_release,
++};    
++
+ /*
+  * Get RPC server stats
+  */
+-int
+-svc_proc_read(char *buffer, char **start, off_t offset, int count,
+-                              int *eof, void *data)
+-{
+-      struct svc_stat *statp  = (struct svc_stat *) data;
+-      struct svc_program *prog = statp->program;
+-      struct svc_procedure *proc;
+-      struct svc_version *vers;
+-      int             len, i, j;
++void svc_seq_show(struct seq_file *seq, const struct svc_stat *statp) {
++      const struct svc_program *prog = statp->program;
++      const struct svc_procedure *proc;
++      const struct svc_version *vers;
++      int             i, j;
+-      len = sprintf(buffer,
++      seq_printf(seq, 
+               "net %d %d %d %d\n",
+                       statp->netcnt,
+                       statp->netudpcnt,
+                       statp->nettcpcnt,
+                       statp->nettcpconn);
+-      len += sprintf(buffer + len,
++      seq_printf(seq, 
+               "rpc %d %d %d %d %d\n",
+                       statp->rpccnt,
+                       statp->rpcbadfmt+statp->rpcbadauth+statp->rpcbadclnt,
+@@ -102,41 +99,36 @@ svc_proc_read(char *buffer, char **start
+       for (i = 0; i < prog->pg_nvers; i++) {
+               if (!(vers = prog->pg_vers[i]) || !(proc = vers->vs_proc))
+                       continue;
+-              len += sprintf(buffer + len, "proc%d %d", i, vers->vs_nproc);
++              seq_printf(seq, "proc%d %d", i, vers->vs_nproc);
+               for (j = 0; j < vers->vs_nproc; j++, proc++)
+-                      len += sprintf(buffer + len, " %d", proc->pc_count);
+-              buffer[len++] = '\n';
++                      seq_printf(seq, " %d", proc->pc_count);
++              seq_putc(seq, '\n');
+       }
+-
+-      if (offset >= len) {
+-              *start = buffer;
+-              *eof = 1;
+-              return 0;
+-      }
+-      *start = buffer + offset;
+-      if ((len -= offset) > count)
+-              return count;
+-      *eof = 1;
+-      return len;
+ }
+ /*
+  * Register/unregister RPC proc files
+  */
+ static inline struct proc_dir_entry *
+-do_register(const char *name, void *data, int issvc)
++do_register(const char *name, void *data, struct file_operations *fops)
+ {
++      struct proc_dir_entry *ent;
++
+       rpc_proc_init();
+       dprintk("RPC: registering /proc/net/rpc/%s\n", name);
+-      return create_proc_read_entry(name, 0, proc_net_rpc, 
+-                                    issvc? svc_proc_read : rpc_proc_read,
+-                                    data);
++
++      ent = create_proc_entry(name, 0, proc_net_rpc);
++      if (ent) {
++              ent->proc_fops = fops;
++              ent->data = data;
++      }
++      return ent;
+ }
+ struct proc_dir_entry *
+ rpc_proc_register(struct rpc_stat *statp)
+ {
+-      return do_register(statp->program->name, statp, 0);
++      return do_register(statp->program->name, statp, &rpc_proc_fops);
+ }
+ void
+@@ -146,9 +138,9 @@ rpc_proc_unregister(const char *name)
+ }
+ struct proc_dir_entry *
+-svc_proc_register(struct svc_stat *statp)
++svc_proc_register(struct svc_stat *statp, struct file_operations *fops)
+ {
+-      return do_register(statp->program->pg_name, statp, 1);
++      return do_register(statp->program->pg_name, statp, fops);
+ }
+ void
+@@ -163,7 +155,7 @@ rpc_proc_init(void)
+       dprintk("RPC: registering /proc/net/rpc\n");
+       if (!proc_net_rpc) {
+               struct proc_dir_entry *ent;
+-              ent = proc_mkdir("net/rpc", 0);
++              ent = proc_mkdir("rpc", proc_net);
+               if (ent) {
+                       ent->owner = THIS_MODULE;
+                       proc_net_rpc = ent;
+diff -puN net/sunrpc/sunrpc_syms.c~CITI_NFS4_ALL net/sunrpc/sunrpc_syms.c
+--- linux-2.6.3/net/sunrpc/sunrpc_syms.c~CITI_NFS4_ALL 2004-02-19 16:47:04.000000000 -0500
++++ linux-2.6.3-bfields/net/sunrpc/sunrpc_syms.c       2004-02-19 16:47:04.000000000 -0500
+@@ -85,15 +85,16 @@ EXPORT_SYMBOL(svc_recv);
+ EXPORT_SYMBOL(svc_wake_up);
+ EXPORT_SYMBOL(svc_makesock);
+ EXPORT_SYMBOL(svc_reserve);
++EXPORT_SYMBOL(svc_auth_register);
++EXPORT_SYMBOL(auth_domain_lookup);
+ /* RPC statistics */
+ #ifdef CONFIG_PROC_FS
+ EXPORT_SYMBOL(rpc_proc_register);
+ EXPORT_SYMBOL(rpc_proc_unregister);
+-EXPORT_SYMBOL(rpc_proc_read);
+ EXPORT_SYMBOL(svc_proc_register);
+ EXPORT_SYMBOL(svc_proc_unregister);
+-EXPORT_SYMBOL(svc_proc_read);
++EXPORT_SYMBOL(svc_seq_show);
+ #endif
+ /* caching... */
+diff -puN net/sunrpc/auth_gss/gss_krb5_seal.c~CITI_NFS4_ALL net/sunrpc/auth_gss/gss_krb5_seal.c
+--- linux-2.6.3/net/sunrpc/auth_gss/gss_krb5_seal.c~CITI_NFS4_ALL      2004-02-19 16:47:04.000000000 -0500
++++ linux-2.6.3-bfields/net/sunrpc/auth_gss/gss_krb5_seal.c    2004-02-19 16:47:07.000000000 -0500
+@@ -101,12 +101,12 @@ krb5_make_token(struct krb5_ctx *ctx, in
+                       checksum_type = CKSUMTYPE_RSA_MD5;
+                       break;
+               default:
+-                      dprintk("RPC: gss_krb5_seal: ctx->signalg %d not"
++                      dprintk("RPC:      gss_krb5_seal: ctx->signalg %d not"
+                               " supported\n", ctx->signalg);
+                       goto out_err;
+       }
+       if (ctx->sealalg != SEAL_ALG_NONE && ctx->sealalg != SEAL_ALG_DES) {
+-              dprintk("RPC: gss_krb5_seal: ctx->sealalg %d not supported\n",
++              dprintk("RPC:      gss_krb5_seal: ctx->sealalg %d not supported\n",
+                       ctx->sealalg);
+               goto out_err;
+       }
+@@ -151,7 +151,7 @@ krb5_make_token(struct krb5_ctx *ctx, in
+                      md5cksum.data + md5cksum.len - KRB5_CKSUM_LENGTH,
+                      KRB5_CKSUM_LENGTH);
+-              dprintk("make_seal_token: cksum data: \n");
++              dprintk("RPC:      make_seal_token: cksum data: \n");
+               print_hexl((u32 *) (krb5_hdr + 16), KRB5_CKSUM_LENGTH, 0);
+               break;
+       default:
+@@ -169,8 +169,5 @@ krb5_make_token(struct krb5_ctx *ctx, in
+       return ((ctx->endtime < now) ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE);
+ out_err:
+       if (md5cksum.data) kfree(md5cksum.data);
+-      if (token->data) kfree(token->data);
+-      token->data = 0;
+-      token->len = 0;
+       return GSS_S_FAILURE;
+ }
+diff -puN include/linux/sunrpc/auth_gss.h~CITI_NFS4_ALL include/linux/sunrpc/auth_gss.h
+--- linux-2.6.3/include/linux/sunrpc/auth_gss.h~CITI_NFS4_ALL  2004-02-19 16:47:04.000000000 -0500
++++ linux-2.6.3-bfields/include/linux/sunrpc/auth_gss.h        2004-02-19 16:47:04.000000000 -0500
+@@ -62,8 +62,6 @@ struct rpc_gss_init_res {
+       struct xdr_netobj       gr_token;       /* token */
+ };
+-#define GSS_SEQ_WIN   5
+-
+ /* The gss_cl_ctx struct holds all the information the rpcsec_gss client
+  * code needs to know about a single security context.  In particular,
+  * gc_gss_ctx is the context handle that is used to do gss-api calls, while
+diff -puN include/linux/sunrpc/gss_api.h~CITI_NFS4_ALL include/linux/sunrpc/gss_api.h
+--- linux-2.6.3/include/linux/sunrpc/gss_api.h~CITI_NFS4_ALL   2004-02-19 16:47:04.000000000 -0500
++++ linux-2.6.3-bfields/include/linux/sunrpc/gss_api.h 2004-02-19 16:47:04.000000000 -0500
+@@ -120,6 +120,9 @@ int gss_mech_unregister_all(void);
+  * reference count. */
+ struct gss_api_mech * gss_mech_get_by_OID(struct xdr_netobj *);
++/* Similar, but get by name like "krb5", "spkm", etc., instead of OID. */
++struct gss_api_mech *gss_mech_get_by_name(char *);
++
+ /* Just increments the mechanism's reference count and returns its input: */
+ struct gss_api_mech * gss_mech_get(struct gss_api_mech *);
+diff -puN /dev/null include/linux/sunrpc/svcauth_gss.h
+--- /dev/null  2004-01-26 19:20:21.000000000 -0500
++++ linux-2.6.3-bfields/include/linux/sunrpc/svcauth_gss.h     2004-02-19 16:47:04.000000000 -0500
+@@ -0,0 +1,35 @@
++/*
++ * linux/include/linux/svcauth_gss.h
++ *
++ * Bruce Fields <bfields@umich.edu>
++ * Copyright (c) 2002 The Regents of the Unviersity of Michigan
++ *
++ * $Id: linux-2.6.3-CITI_NFS4_ALL.patch,v 1.2 2004/03/17 01:04:13 nic Exp $
++ *
++ */
++
++#ifndef _LINUX_SUNRPC_SVCAUTH_GSS_H
++#define _LINUX_SUNRPC_SVCAUTH_GSS_H
++
++#ifdef __KERNEL__
++#include <linux/sched.h>
++#include <linux/sunrpc/types.h>
++#include <linux/sunrpc/xdr.h>
++#include <linux/sunrpc/svcauth.h>
++#include <linux/sunrpc/svcsock.h>
++#include <linux/sunrpc/auth_gss.h>
++
++int gss_svc_init(void);
++int svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name);
++
++
++struct gss_svc_data {
++      /* decoded gss client cred: */
++      struct rpc_gss_wire_cred        clcred;
++      /* pointer to the beginning of the procedure-specific results, which
++       * may be encrypted/checksummed in svcauth_gss_release: */
++      u32                             *body_start;
++};
++
++#endif /* __KERNEL__ */
++#endif /* _LINUX_SUNRPC_SVCAUTH_GSS_H */
+diff -puN include/linux/sunrpc/svcauth.h~CITI_NFS4_ALL include/linux/sunrpc/svcauth.h
+--- linux-2.6.3/include/linux/sunrpc/svcauth.h~CITI_NFS4_ALL   2004-02-19 16:47:04.000000000 -0500
++++ linux-2.6.3-bfields/include/linux/sunrpc/svcauth.h 2004-02-19 16:47:04.000000000 -0500
+@@ -65,6 +65,10 @@ struct auth_domain {
+  *      GARBAGE - rpc garbage_args error
+  *      SYSERR - rpc system_err error
+  *      DENIED - authp holds reason for denial.
++ *      COMPLETE - the reply is encoded already and ready to be sent; no
++ *            further processing is necessary.  (This is used for processing
++ *            null procedure calls which are used to set up encryption
++ *            contexts.)
+  *
+  *   accept is passed the proc number so that it can accept NULL rpc requests
+  *   even if it cannot authenticate the client (as is sometimes appropriate).
+@@ -97,6 +101,7 @@ extern struct auth_ops      *authtab[RPC_AUTH
+ #define       SVC_DROP        6
+ #define       SVC_DENIED      7
+ #define       SVC_PENDING     8
++#define       SVC_COMPLETE    9
+ extern int    svc_authenticate(struct svc_rqst *rqstp, u32 *authp);
+diff -puN include/linux/sunrpc/svc.h~CITI_NFS4_ALL include/linux/sunrpc/svc.h
+--- linux-2.6.3/include/linux/sunrpc/svc.h~CITI_NFS4_ALL       2004-02-19 16:47:04.000000000 -0500
++++ linux-2.6.3-bfields/include/linux/sunrpc/svc.h     2004-02-19 16:47:04.000000000 -0500
+@@ -135,6 +135,7 @@ struct svc_rqst {
+       void *                  rq_argp;        /* decoded arguments */
+       void *                  rq_resp;        /* xdr'd results */
++      void *                  rq_auth_data;   /* flavor-specific data */
+       int                     rq_reserved;    /* space on socket outq
+                                                * reserved for this request
+diff -puN net/sunrpc/auth_gss/auth_gss.c~CITI_NFS4_ALL net/sunrpc/auth_gss/auth_gss.c
+--- linux-2.6.3/net/sunrpc/auth_gss/auth_gss.c~CITI_NFS4_ALL   2004-02-19 16:47:04.000000000 -0500
++++ linux-2.6.3-bfields/net/sunrpc/auth_gss/auth_gss.c 2004-02-19 16:47:07.000000000 -0500
+@@ -48,6 +48,7 @@
+ #include <linux/sunrpc/clnt.h>
+ #include <linux/sunrpc/auth.h>
+ #include <linux/sunrpc/auth_gss.h>
++#include <linux/sunrpc/svcauth_gss.h>
+ #include <linux/sunrpc/gss_err.h>
+ #include <linux/workqueue.h>
+ #include <linux/sunrpc/rpc_pipe_fs.h>
+@@ -279,7 +280,7 @@ err_free_ctx:
+       kfree(ctx);
+ err:
+       *gc = NULL;
+-      dprintk("RPC: gss_parse_init_downcall returning %d\n", err);
++      dprintk("RPC:      gss_parse_init_downcall returning %d\n", err);
+       return err;
+ }
+@@ -310,8 +311,10 @@ __gss_find_upcall(struct gss_auth *gss_a
+               if (pos->uid != uid)
+                       continue;
+               atomic_inc(&pos->count);
++              dprintk("RPC:      gss_find_upcall found msg %p\n", pos);
+               return pos;
+       }
++      dprintk("RPC:      gss_find_upcall found nothing\n");
+       return NULL;
+ }
+@@ -349,6 +352,8 @@ gss_upcall(struct rpc_clnt *clnt, struct
+       uid_t uid = cred->cr_uid;
+       int res = 0;
++      dprintk("RPC: %4u gss_upcall for uid %u\n", task->tk_pid, uid);
++
+ retry:
+       spin_lock(&gss_auth->lock);
+       gss_msg = __gss_find_upcall(gss_auth, uid);
+@@ -357,8 +362,10 @@ retry:
+       if (gss_new == NULL) {
+               spin_unlock(&gss_auth->lock);
+               gss_new = kmalloc(sizeof(*gss_new), GFP_KERNEL);
+-              if (!gss_new)
++              if (!gss_new) {
++                      dprintk("RPC: %4u gss_upcall -ENOMEM\n", task->tk_pid);
+                       return -ENOMEM;
++              }
+               goto retry;
+       }
+       gss_msg = gss_new;
+@@ -388,10 +395,12 @@ retry:
+               spin_unlock(&gss_auth->lock);
+       }
+       gss_release_msg(gss_msg);
++      dprintk("RPC: %4u gss_upcall for uid %u result %d", task->tk_pid,
++                      uid, res);
+       return res;
+ out_sleep:
+-      /* Sleep forever */
+-      task->tk_timeout = 0;
++      dprintk("RPC: %4u gss_upcall  sleeping\n", task->tk_pid);
++      task->tk_timeout = 0;           /* Sleep forever */
+       rpc_sleep_on(&gss_msg->waitq, task, NULL, NULL);
+       spin_unlock(&gss_auth->lock);
+       if (gss_new)
+@@ -476,12 +485,13 @@ gss_pipe_downcall(struct file *filp, con
+       } else
+               spin_unlock(&gss_auth->lock);
+       rpc_release_client(clnt);
++      dprintk("RPC:      gss_pipe_downcall returning length %u\n", mlen);
+       return mlen;
+ err:
+       if (ctx)
+               gss_destroy_ctx(ctx);
+       rpc_release_client(clnt);
+-      dprintk("RPC: gss_pipe_downcall returning %d\n", err);
++      dprintk("RPC:      gss_pipe_downcall returning %d\n", err);
+       return err;
+ }
+@@ -519,6 +529,8 @@ gss_pipe_destroy_msg(struct rpc_pipe_msg
+       static unsigned long ratelimit;
+       if (msg->errno < 0) {
++              dprintk("RPC:      gss_pipe_destroy_msg releasing msg %p\n",
++                              gss_msg);
+               atomic_inc(&gss_msg->count);
+               gss_unhash_msg(gss_msg);
+               if (msg->errno == -ETIMEDOUT || msg->errno == -EPIPE) {
+@@ -543,7 +555,8 @@ gss_create(struct rpc_clnt *clnt, rpc_au
+       struct gss_auth *gss_auth;
+       struct rpc_auth * auth;
+-      dprintk("RPC: creating GSS authenticator for client %p\n",clnt);
++      dprintk("RPC:      creating GSS authenticator for client %p\n",clnt);
++
+       if (!(gss_auth = kmalloc(sizeof(*gss_auth), GFP_KERNEL)))
+               goto out_dec;
+       gss_auth->mech = gss_pseudoflavor_to_mech(flavor);
+@@ -581,7 +594,8 @@ static void
+ gss_destroy(struct rpc_auth *auth)
+ {
+       struct gss_auth *gss_auth;
+-      dprintk("RPC: destroying GSS authenticator %p flavor %d\n",
++
++      dprintk("RPC:      destroying GSS authenticator %p flavor %d\n",
+               auth, auth->au_flavor);
+       gss_auth = container_of(auth, struct gss_auth, rpc_auth);
+@@ -596,8 +610,7 @@ gss_destroy(struct rpc_auth *auth)
+ static void
+ gss_destroy_ctx(struct gss_cl_ctx *ctx)
+ {
+-
+-      dprintk("RPC: gss_destroy_ctx\n");
++      dprintk("RPC:      gss_destroy_ctx\n");
+       if (ctx->gc_gss_ctx)
+               gss_delete_sec_context(&ctx->gc_gss_ctx);
+@@ -616,7 +629,7 @@ gss_destroy_cred(struct rpc_cred *rc)
+ {
+       struct gss_cred *cred = (struct gss_cred *)rc;
+-      dprintk("RPC: gss_destroy_cred \n");
++      dprintk("RPC:      gss_destroy_cred \n");
+       if (cred->gc_ctx)
+               gss_put_ctx(cred->gc_ctx);
+@@ -628,7 +641,7 @@ gss_create_cred(struct rpc_auth *auth, s
+ {
+       struct gss_cred *cred = NULL;
+-      dprintk("RPC: gss_create_cred for uid %d, flavor %d\n",
++      dprintk("RPC:      gss_create_cred for uid %d, flavor %d\n",
+               acred->uid, auth->au_flavor);
+       if (!(cred = kmalloc(sizeof(*cred), GFP_KERNEL)))
+@@ -648,7 +661,7 @@ gss_create_cred(struct rpc_auth *auth, s
+       return (struct rpc_cred *) cred;
+ out_err:
+-      dprintk("RPC: gss_create_cred failed\n");
++      dprintk("RPC:      gss_create_cred failed\n");
+       if (cred) gss_destroy_cred((struct rpc_cred *)cred);
+       return NULL;
+ }
+@@ -659,6 +672,15 @@ gss_match(struct auth_cred *acred, struc
+       return (rc->cr_uid == acred->uid);
+ }
++static void
++shift_seqnos(u32 *seqnos)
++{
++      int i;
++
++      for (i=1; i < GSS_SEQNO_CACHE; i++)
++              seqnos[i] = seqnos[i-1];
++}
++
+ /*
+ * Marshal credentials.
+ * Maybe we should keep a cached credential for performance reasons.
+@@ -678,24 +700,25 @@ gss_marshal(struct rpc_task *task, u32 *
+       struct xdr_buf  verf_buf;
+       u32             service;
+-      dprintk("RPC: gss_marshal\n");
++      dprintk("RPC: %4u gss_marshal\n", task->tk_pid);
+       *p++ = htonl(RPC_AUTH_GSS);
+       cred_len = p++;
+       service = gss_pseudoflavor_to_service(gss_cred->gc_flavor);
+       if (service == 0) {
+-              dprintk("Bad pseudoflavor %d in gss_marshal\n",
+-                      gss_cred->gc_flavor);
++              dprintk("RPC: %4u Bad pseudoflavor %d in gss_marshal\n",
++                      task->tk_pid, gss_cred->gc_flavor);
+               goto out_put_ctx;
+       }
++      shift_seqnos(req->rq_seqnos);
+       spin_lock(&ctx->gc_seq_lock);
+-      req->rq_seqno = ctx->gc_seq++;
++      req->rq_seqnos[0] = ctx->gc_seq++;
+       spin_unlock(&ctx->gc_seq_lock);
+       *p++ = htonl((u32) RPC_GSS_VERSION);
+       *p++ = htonl((u32) ctx->gc_proc);
+-      *p++ = htonl((u32) req->rq_seqno);
++      *p++ = htonl((u32) req->rq_seqnos[0]);
+       *p++ = htonl((u32) service);
+       p = xdr_encode_netobj(p, &ctx->gc_wire_ctx);
+       *cred_len = htonl((p - (cred_len + 1)) << 2);
+@@ -745,6 +768,32 @@ gss_refresh(struct rpc_task *task)
+       return 0;
+ }
++static int
++verify_checksum(struct gss_ctx *ctx, struct xdr_netobj *mic, u32 *seqnos)
++{
++      u32             seq, qop_state;
++      struct xdr_buf  verf_buf;
++      struct iovec    iov;
++      int             i;
++
++      for (i=0; i < GSS_SEQNO_CACHE; i++) {
++              if (i && !seqnos[i])
++                      goto fail;
++              seq = htonl(seqnos[i]);
++              iov.iov_base = &seq;
++              iov.iov_len = sizeof(seq);
++              xdr_buf_from_iov(&iov, &verf_buf);
++              if (!gss_verify_mic(ctx, &verf_buf, mic, &qop_state))
++                      goto success;
++      }
++fail:
++      return -1;
++success:
++      /* So unwrap knows which seqno we used: */
++      seqnos[0] = seqnos[i];
++      return 0;
++}
++
+ static u32 *
+ gss_validate(struct rpc_task *task, u32 *p)
+ {
+@@ -752,28 +801,21 @@ gss_validate(struct rpc_task *task, u32 
+       struct gss_cred *gss_cred = container_of(cred, struct gss_cred,
+                                               gc_base);
+       struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred);
+-      u32             seq, qop_state;
+-      struct iovec    iov;
+-      struct xdr_buf  verf_buf;
+       struct xdr_netobj mic;
+       u32             flav,len;
+       u32             service;
+-      dprintk("RPC: gss_validate\n");
++      dprintk("RPC: %4u gss_validate\n", task->tk_pid);
+       flav = ntohl(*p++);
+       if ((len = ntohl(*p++)) > RPC_MAX_AUTH_SIZE)
+                 goto out_bad;
+       if (flav != RPC_AUTH_GSS)
+               goto out_bad;
+-      seq = htonl(task->tk_rqstp->rq_seqno);
+-      iov.iov_base = &seq;
+-      iov.iov_len = sizeof(seq);
+-      xdr_buf_from_iov(&iov, &verf_buf);
++
+       mic.data = (u8 *)p;
+       mic.len = len;
+-
+-      if (gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic, &qop_state))
++      if (verify_checksum(ctx->gc_gss_ctx, &mic, task->tk_rqstp->rq_seqnos))
+                goto out_bad;
+        service = gss_pseudoflavor_to_service(gss_cred->gc_flavor);
+        switch (service) {
+@@ -789,9 +831,12 @@ gss_validate(struct rpc_task *task, u32 
+              goto out_bad;
+        }
+       gss_put_ctx(ctx);
++      dprintk("RPC: %4u GSS gss_validate: gss_verify_mic succeeded.\n",
++                      task->tk_pid);
+       return p + XDR_QUADLEN(len);
+ out_bad:
+       gss_put_ctx(ctx);
++      dprintk("RPC: %4u gss_validate failed.\n", task->tk_pid);
+       return NULL;
+ }
+@@ -814,7 +859,7 @@ gss_wrap_req(struct rpc_task *task,
+       u32             offset, *q;
+       struct iovec    *iov;
+-      dprintk("RPC: gss_wrap_body\n");
++      dprintk("RPC: %4u gss_wrap_req\n", task->tk_pid);
+       BUG_ON(!ctx);
+       if (ctx->gc_proc != RPC_GSS_PROC_DATA) {
+               /* The spec seems a little ambiguous here, but I think that not
+@@ -832,7 +877,7 @@ gss_wrap_req(struct rpc_task *task,
+                       integ_len = p++;
+                       offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base;
+-                      *p++ = htonl(req->rq_seqno);
++                      *p++ = htonl(req->rq_seqnos[0]);
+                       status = encode(rqstp, p, obj);
+                       if (status)
+@@ -871,7 +916,7 @@ gss_wrap_req(struct rpc_task *task,
+       status = 0;
+ out:
+       gss_put_ctx(ctx);
+-      dprintk("RPC: gss_wrap_req returning %d\n", status);
++      dprintk("RPC: %4u gss_wrap_req returning %d\n", task->tk_pid, status);
+       return status;
+ }
+@@ -909,7 +954,7 @@ gss_unwrap_resp(struct rpc_task *task,
+                       mic_offset = integ_len + data_offset;
+                       if (mic_offset > rcv_buf->len)
+                               goto out;
+-                      if (ntohl(*p++) != req->rq_seqno)
++                      if (ntohl(*p++) != req->rq_seqnos[0])
+                               goto out;
+                       if (xdr_buf_subsegment(rcv_buf, &integ_buf, data_offset,
+@@ -932,7 +977,8 @@ out_decode:
+       status = decode(rqstp, p, obj);
+ out:
+       gss_put_ctx(ctx);
+-      dprintk("RPC: gss_unwrap_resp returning %d\n", status);
++      dprintk("RPC: %4u gss_unwrap_resp returning %d\n", task->tk_pid,
++                      status);
+       return status;
+ }
+   
+@@ -972,6 +1018,15 @@ static int __init init_rpcsec_gss(void)
+       int err = 0;
+       err = rpcauth_register(&authgss_ops);
++      if (err)
++              goto out;
++      err = gss_svc_init();
++      if (err)
++              goto out_unregister;
++      return 0;
++out_unregister:
++      rpcauth_unregister(&authgss_ops);
++out:
+       return err;
+ }
+diff -puN net/sunrpc/auth_gss/gss_krb5_mech.c~CITI_NFS4_ALL net/sunrpc/auth_gss/gss_krb5_mech.c
+--- linux-2.6.3/net/sunrpc/auth_gss/gss_krb5_mech.c~CITI_NFS4_ALL      2004-02-19 16:47:04.000000000 -0500
++++ linux-2.6.3-bfields/net/sunrpc/auth_gss/gss_krb5_mech.c    2004-02-19 16:47:15.000000000 -0500
+@@ -39,6 +39,8 @@
+ #include <linux/types.h>
+ #include <linux/slab.h>
+ #include <linux/sunrpc/auth.h>
++#include <linux/in.h>
++#include <linux/sunrpc/svcauth_gss.h>
+ #include <linux/sunrpc/gss_krb5.h>
+ #include <linux/sunrpc/xdr.h>
+ #include <linux/crypto.h>
+@@ -98,7 +100,7 @@ get_key(char **p, char *end, struct cryp
+                       alg_mode = CRYPTO_TFM_MODE_CBC;
+                       break;
+               default:
+-                      dprintk("RPC: get_key: unsupported algorithm %d\n", alg);
++                      dprintk("RPC:      get_key: unsupported algorithm %d\n", alg);
+                       goto out_err_free_key;
+       }
+       if (!(*res = crypto_alloc_tfm(alg_name, alg_mode)))
+@@ -153,7 +155,7 @@ gss_import_sec_context_kerberos(struct x
+               goto out_err_free_key2;
+       ctx_id->internal_ctx_id = ctx;
+-      dprintk("Succesfully imported new context.\n");
++      dprintk("RPC:      Succesfully imported new context.\n");
+       return 0;
+ out_err_free_key2:
+@@ -195,7 +197,7 @@ gss_verify_mic_kerberos(struct gss_ctx             
+       if (!maj_stat && qop_state)
+           *qstate = qop_state;
+-      dprintk("RPC: gss_verify_mic_kerberos returning %d\n", maj_stat);
++      dprintk("RPC:      gss_verify_mic_kerberos returning %d\n", maj_stat);
+       return maj_stat;
+ }
+@@ -209,7 +211,7 @@ gss_get_mic_kerberos(struct gss_ctx        *ctx
+       err = krb5_make_token(kctx, qop, message, mic_token, KG_TOK_MIC_MSG);
+-      dprintk("RPC: gss_get_mic_kerberos returning %d\n",err);
++      dprintk("RPC:      gss_get_mic_kerberos returning %d\n",err);
+       return err;
+ }
+@@ -232,6 +234,10 @@ static int __init init_kerberos_module(v
+       gm = gss_mech_get_by_OID(&gss_mech_krb5_oid);
+       gss_register_triple(RPC_AUTH_GSS_KRB5 , gm, 0, RPC_GSS_SVC_NONE);
+       gss_register_triple(RPC_AUTH_GSS_KRB5I, gm, 0, RPC_GSS_SVC_INTEGRITY);
++      if (svcauth_gss_register_pseudoflavor(RPC_AUTH_GSS_KRB5, "krb5"))
++              printk("Failed to register %s with server!\n", "krb5");
++      if (svcauth_gss_register_pseudoflavor(RPC_AUTH_GSS_KRB5I, "krb5i"))
++              printk("Failed to register %s with server!\n", "krb5i");
+       gss_mech_put(gm);
+       return 0;
+ }
+diff -puN net/sunrpc/auth_gss/gss_mech_switch.c~CITI_NFS4_ALL net/sunrpc/auth_gss/gss_mech_switch.c
+--- linux-2.6.3/net/sunrpc/auth_gss/gss_mech_switch.c~CITI_NFS4_ALL    2004-02-19 16:47:04.000000000 -0500
++++ linux-2.6.3-bfields/net/sunrpc/auth_gss/gss_mech_switch.c  2004-02-19 16:47:07.000000000 -0500
+@@ -43,7 +43,6 @@
+ #include <linux/sunrpc/sched.h>
+ #include <linux/sunrpc/gss_api.h>
+ #include <linux/sunrpc/clnt.h>
+-#include <linux/sunrpc/name_lookup.h>
+ #ifdef RPC_DEBUG
+ # define RPCDBG_FACILITY        RPCDBG_AUTH
+@@ -82,7 +81,7 @@ gss_mech_register(struct xdr_netobj * me
+       spin_lock(&registered_mechs_lock);
+       list_add(&gm->gm_list, &registered_mechs);
+       spin_unlock(&registered_mechs_lock);
+-      dprintk("RPC: gss_mech_register: registered mechanism with oid:\n");
++      dprintk("RPC:      gss_mech_register: registered mechanism with oid:\n");
+       print_hexl((u32 *)mech_type->data, mech_type->len, 0);
+       return 0;
+ }
+@@ -94,11 +93,10 @@ do_gss_mech_unregister(struct gss_api_me
+       list_del(&gm->gm_list);
+-      dprintk("RPC: unregistered mechanism with oid:\n");
++      dprintk("RPC:      unregistered mechanism with oid:\n");
+       print_hexl((u32 *)gm->gm_oid.data, gm->gm_oid.len, 0);
+       if (!gss_mech_put(gm)) {
+-              dprintk("RPC: We just unregistered a gss_mechanism which"
+-                              " someone is still using.\n");
++              dprintk("RPC:      We just unregistered a gss_mechanism which someone is still using.\n");
+               return -1;
+       } else {
+               return 0;
+@@ -146,7 +144,7 @@ gss_mech_get_by_OID(struct xdr_netobj *m
+ {
+       struct gss_api_mech     *pos, *gm = NULL;
+-      dprintk("RPC: gss_mech_get_by_OID searching for mechanism with OID:\n");
++      dprintk("RPC:      gss_mech_get_by_OID searching for mechanism with OID:\n");
+       print_hexl((u32 *)mech_type->data, mech_type->len, 0);
+       spin_lock(&registered_mechs_lock);
+       list_for_each_entry(pos, &registered_mechs, gm_list) {
+@@ -158,10 +156,27 @@ gss_mech_get_by_OID(struct xdr_netobj *m
+               }
+       }
+       spin_unlock(&registered_mechs_lock);
+-      dprintk("RPC: gss_mech_get_by_OID %s it\n", gm ? "found" : "didn't find");
++      dprintk("RPC:      gss_mech_get_by_OID %s it\n", gm ? "found" : "didn't find");
+       return gm;
+ }
++struct gss_api_mech *
++gss_mech_get_by_name(char *name)
++{
++      struct gss_api_mech     *pos, *gm = NULL;
++
++      spin_lock(&registered_mechs_lock);
++      list_for_each_entry(pos, &registered_mechs, gm_list) {
++              if (0 == strcmp(name, pos->gm_ops->name)) {
++                      gm = gss_mech_get(pos);
++                      break;
++              }
++      }
++      spin_unlock(&registered_mechs_lock);
++      return gm;
++
++}
++
+ int
+ gss_mech_put(struct gss_api_mech * gm)
+ {
+@@ -228,7 +243,8 @@ gss_verify_mic(struct gss_ctx              *context_
+ u32
+ gss_delete_sec_context(struct gss_ctx **context_handle)
+ {
+-      dprintk("gss_delete_sec_context deleting %p\n",*context_handle);
++      dprintk("RPC:      gss_delete_sec_context deleting %p\n",
++                      *context_handle);
+       if (!*context_handle)
+               return(GSS_S_NO_CONTEXT);
+diff -puN net/sunrpc/auth_gss/Makefile~CITI_NFS4_ALL net/sunrpc/auth_gss/Makefile
+--- linux-2.6.3/net/sunrpc/auth_gss/Makefile~CITI_NFS4_ALL     2004-02-19 16:47:04.000000000 -0500
++++ linux-2.6.3-bfields/net/sunrpc/auth_gss/Makefile   2004-02-19 16:47:04.000000000 -0500
+@@ -5,7 +5,7 @@
+ obj-$(CONFIG_SUNRPC_GSS) += auth_rpcgss.o
+ auth_rpcgss-objs := auth_gss.o gss_pseudoflavors.o gss_generic_token.o \
+-      sunrpcgss_syms.o gss_mech_switch.o
++      sunrpcgss_syms.o gss_mech_switch.o svcauth_gss.o
+ obj-$(CONFIG_RPCSEC_GSS_KRB5) += rpcsec_gss_krb5.o
+diff -puN net/sunrpc/auth_gss/sunrpcgss_syms.c~CITI_NFS4_ALL net/sunrpc/auth_gss/sunrpcgss_syms.c
+--- linux-2.6.3/net/sunrpc/auth_gss/sunrpcgss_syms.c~CITI_NFS4_ALL     2004-02-19 16:47:04.000000000 -0500
++++ linux-2.6.3-bfields/net/sunrpc/auth_gss/sunrpcgss_syms.c   2004-02-19 16:47:04.000000000 -0500
+@@ -8,6 +8,7 @@
+ #include <linux/unistd.h>
+ #include <linux/sunrpc/auth_gss.h>
++#include <linux/sunrpc/svcauth_gss.h>
+ #include <linux/sunrpc/gss_asn1.h>
+ /* sec_triples: */
+@@ -17,6 +18,7 @@ EXPORT_SYMBOL(gss_cmp_triples);
+ EXPORT_SYMBOL(gss_pseudoflavor_to_mechOID);
+ EXPORT_SYMBOL(gss_pseudoflavor_supported);
+ EXPORT_SYMBOL(gss_pseudoflavor_to_service);
++EXPORT_SYMBOL(svcauth_gss_register_pseudoflavor);
+ /* registering gss mechanisms to the mech switching code: */
+ EXPORT_SYMBOL(gss_mech_register);
+diff -puN /dev/null net/sunrpc/auth_gss/svcauth_gss.c
+--- /dev/null  2004-01-26 19:20:21.000000000 -0500
++++ linux-2.6.3-bfields/net/sunrpc/auth_gss/svcauth_gss.c      2004-02-19 16:47:15.000000000 -0500
+@@ -0,0 +1,1018 @@
++/*
++ * Neil Brown <neilb@cse.unsw.edu.au>
++ * J. Bruce Fields <bfields@umich.edu>
++ * Andy Adamson <andros@umich.edu>
++ * Dug Song <dugsong@monkey.org>
++ * 
++ * RPCSEC_GSS server authentication.
++ * This implements RPCSEC_GSS as defined in rfc2203 (rpcsec_gss) and rfc2078
++ * (gssapi)
++ * 
++ * The RPCSEC_GSS involves three stages:
++ *  1/ context creation
++ *  2/ data exchange
++ *  3/ context destruction
++ *
++ * Context creation is handled largely by upcalls to user-space.
++ *  In particular, GSS_Accept_sec_context is handled by an upcall
++ * Data exchange is handled entirely within the kernel
++ *  In particular, GSS_GetMIC, GSS_VerifyMIC, GSS_Seal, GSS_Unseal are in-kernel.
++ * Context destruction is handled in-kernel
++ *  GSS_Delete_sec_context is in-kernel
++ *
++ * Context creation is initiated by a RPCSEC_GSS_INIT request arriving.
++ * The context handle and gss_token are used as a key into the rpcsec_init cache.
++ * The content of this cache includes some of the outputs of GSS_Accept_sec_context,
++ * being major_status, minor_status, context_handle, reply_token.
++ * These are sent back to the client.
++ * Sequence window management is handled by the kernel.  The window size if currently
++ * a compile time constant.
++ *
++ * When user-space is happy that a context is established, it places an entry
++ * in the rpcsec_context cache. The key for this cache is the context_handle.
++ * The content includes:
++ *   uid/gidlist - for determining access rights
++ *   mechanism type
++ *   mechanism specific information, such as a key
++ *
++ */
++
++#include <linux/types.h>
++#include <linux/module.h>
++#include <linux/pagemap.h>
++
++#include <linux/sunrpc/auth_gss.h>
++#include <linux/sunrpc/svcauth.h>
++#include <linux/sunrpc/gss_err.h>
++#include <linux/sunrpc/svcauth.h>
++#include <linux/sunrpc/svcauth_gss.h>
++#include <linux/sunrpc/cache.h>
++
++#ifdef RPC_DEBUG
++# define RPCDBG_FACILITY      RPCDBG_AUTH
++#endif
++
++/* The rpcsec_init cache is used for mapping RPCSEC_GSS_{,CONT_}INIT requests
++ * into replies.
++ *
++ * Key is context handle (\x if empty) and gss_token.
++ * Content is major_status minor_status (integers) context_handle, reply_token.
++ *
++ */
++
++static int netobj_equal(struct xdr_netobj *a, struct xdr_netobj *b)
++{
++      return a->len == b->len && 0 == memcmp(a->data, b->data, a->len);
++}
++
++#define       RSI_HASHBITS    6
++#define       RSI_HASHMAX     (1<<RSI_HASHBITS)
++#define       RSI_HASHMASK    (RSI_HASHMAX-1)
++
++struct rsi {
++      struct cache_head       h;
++      struct xdr_netobj       in_handle, in_token;
++      struct xdr_netobj       out_handle, out_token;
++      int                     major_status, minor_status;
++};
++
++static struct cache_head *rsi_table[RSI_HASHMAX];
++static struct cache_detail rsi_cache;
++static struct rsi *rsi_lookup(struct rsi *item, int set);
++
++static void rsi_free(struct rsi *rsii)
++{
++      kfree(rsii->in_handle.data);
++      kfree(rsii->in_token.data);
++      kfree(rsii->out_handle.data);
++      kfree(rsii->out_token.data);
++}
++
++static void rsi_put(struct cache_head *item, struct cache_detail *cd)
++{
++      struct rsi *rsii = container_of(item, struct rsi, h);
++      if (cache_put(item, cd)) {
++              rsi_free(rsii);
++              kfree(rsii);
++      }
++}
++
++static inline int rsi_hash(struct rsi *item)
++{
++      return hash_mem(item->in_handle.data, item->in_handle.len, RSI_HASHBITS)
++           ^ hash_mem(item->in_token.data, item->in_token.len, RSI_HASHBITS);
++}
++
++static inline int rsi_match(struct rsi *item, struct rsi *tmp)
++{
++      return netobj_equal(&item->in_handle, &tmp->in_handle)
++              && netobj_equal(&item->in_token, &tmp->in_token);
++}
++
++static int dup_to_netobj(struct xdr_netobj *dst, char *src, int len)
++{
++      dst->len = len;
++      dst->data = (len ? kmalloc(len, GFP_KERNEL) : NULL);
++      if (dst->data)
++              memcpy(dst->data, src, len);
++      if (len && !dst->data)
++              return -ENOMEM;
++      return 0;
++}
++
++static inline int dup_netobj(struct xdr_netobj *dst, struct xdr_netobj *src)
++{
++      return dup_to_netobj(dst, src->data, src->len);
++}
++
++static inline void rsi_init(struct rsi *new, struct rsi *item)
++{
++      new->out_handle.data = NULL;
++      new->out_handle.len = 0;
++      new->out_token.data = NULL;
++      new->out_token.len = 0;
++      new->in_handle.len = item->in_handle.len;
++      new->in_handle.data = item->in_handle.data;
++      item->in_handle.len = 0;
++      item->in_handle.data = NULL;
++      new->in_token.len = item->in_token.len;
++      new->in_token.data = item->in_token.data;
++      item->in_token.len = 0;
++      item->in_token.data = NULL;
++      return;
++}
++
++static inline void rsi_update(struct rsi *new, struct rsi *item)
++{
++      BUG_ON(new->out_handle.data || new->out_token.data);
++      new->out_handle.len = item->out_handle.len;
++      item->out_handle.len = 0;
++      new->out_token.len = item->out_token.len;
++      item->out_token.len = 0;
++      new->out_handle.data = item->out_handle.data;
++      item->out_handle.data = NULL;
++      new->out_token.data = item->out_token.data;
++      item->out_token.data = NULL;
++
++      new->major_status = item->major_status;
++      new->minor_status = item->minor_status;
++}
++
++static void rsi_request(struct cache_detail *cd,
++                       struct cache_head *h,
++                       char **bpp, int *blen)
++{
++      struct rsi *rsii = container_of(h, struct rsi, h);
++
++      qword_addhex(bpp, blen, rsii->in_handle.data, rsii->in_handle.len);
++      qword_addhex(bpp, blen, rsii->in_token.data, rsii->in_token.len);
++      (*bpp)[-1] = '\n';
++}
++
++
++static int rsi_parse(struct cache_detail *cd,
++                    char *mesg, int mlen)
++{
++      /* context token expiry major minor context token */
++      char *buf = mesg;
++      char *ep;
++      int len;
++      struct rsi rsii, *rsip = NULL;
++      time_t expiry;
++      int status = -EINVAL;
++
++      memset(&rsii, 0, sizeof(rsii));
++      /* handle */
++      len = qword_get(&mesg, buf, mlen);
++      if (len < 0)
++              goto out;
++      status = -ENOMEM;
++      if (dup_to_netobj(&rsii.in_handle, buf, len))
++              goto out;
++
++      /* token */
++      len = qword_get(&mesg, buf, mlen);
++      status = -EINVAL;
++      if (len < 0)
++              goto out;;
++      status = -ENOMEM;
++      if (dup_to_netobj(&rsii.in_token, buf, len))
++              goto out;
++
++      rsii.h.flags = 0;
++      /* expiry */
++      expiry = get_expiry(&mesg);
++      status = -EINVAL;
++      if (expiry == 0)
++              goto out;
++
++      /* major/minor */
++      len = qword_get(&mesg, buf, mlen);
++      if (len < 0)
++              goto out;
++      if (len == 0) {
++              goto out;
++      } else {
++              rsii.major_status = simple_strtoul(buf, &ep, 10);
++              if (*ep)
++                      goto out;
++              len = qword_get(&mesg, buf, mlen);
++              if (len <= 0)
++                      goto out;
++              rsii.minor_status = simple_strtoul(buf, &ep, 10);
++              if (*ep)
++                      goto out;
++
++              /* out_handle */
++              len = qword_get(&mesg, buf, mlen);
++              if (len < 0)
++                      goto out;
++              status = -ENOMEM;
++              if (dup_to_netobj(&rsii.out_handle, buf, len))
++                      goto out;
++
++              /* out_token */
++              len = qword_get(&mesg, buf, mlen);
++              status = -EINVAL;
++              if (len < 0)
++                      goto out;
++              status = -ENOMEM;
++              if (dup_to_netobj(&rsii.out_token, buf, len))
++                      goto out;
++      }
++      rsii.h.expiry_time = expiry;
++      rsip = rsi_lookup(&rsii, 1);
++      status = 0;
++out:
++      rsi_free(&rsii);
++      if (rsip)
++              rsi_put(&rsip->h, &rsi_cache);
++      return status;
++}
++
++static struct cache_detail rsi_cache = {
++      .hash_size      = RSI_HASHMAX,
++      .hash_table     = rsi_table,
++      .name           = "auth.rpcsec.init",
++      .cache_put      = rsi_put,
++      .cache_request  = rsi_request,
++      .cache_parse    = rsi_parse,
++};
++
++static DefineSimpleCacheLookup(rsi, 0)
++
++/*
++ * The rpcsec_context cache is used to store a context that is
++ * used in data exchange.
++ * The key is a context handle. The content is:
++ *  uid, gidlist, mechanism, service-set, mech-specific-data
++ */
++
++#define       RSC_HASHBITS    10
++#define       RSC_HASHMAX     (1<<RSC_HASHBITS)
++#define       RSC_HASHMASK    (RSC_HASHMAX-1)
++
++#define GSS_SEQ_WIN   128
++
++struct gss_svc_seq_data {
++      /* highest seq number seen so far: */
++      int                     sd_max;
++      /* for i such that sd_max-GSS_SEQ_WIN < i <= sd_max, the i-th bit of
++       * sd_win is nonzero iff sequence number i has been seen already: */
++      unsigned long           sd_win[GSS_SEQ_WIN/BITS_PER_LONG];
++      spinlock_t              sd_lock;
++};
++
++struct rsc {
++      struct cache_head       h;
++      struct xdr_netobj       handle;
++      struct svc_cred         cred;
++      struct gss_svc_seq_data seqdata;
++      struct gss_ctx          *mechctx;
++};
++
++static struct cache_head *rsc_table[RSC_HASHMAX];
++static struct cache_detail rsc_cache;
++static struct rsc *rsc_lookup(struct rsc *item, int set);
++
++static void rsc_free(struct rsc *rsci)
++{
++      kfree(rsci->handle.data);
++      if (rsci->mechctx)
++              gss_delete_sec_context(&rsci->mechctx);
++}
++
++static void rsc_put(struct cache_head *item, struct cache_detail *cd)
++{
++      struct rsc *rsci = container_of(item, struct rsc, h);
++
++      if (cache_put(item, cd)) {
++              rsc_free(rsci);
++              kfree(rsci);
++      }
++}
++
++static inline int
++rsc_hash(struct rsc *rsci)
++{
++      return hash_mem(rsci->handle.data, rsci->handle.len, RSC_HASHBITS);
++}
++
++static inline int
++rsc_match(struct rsc *new, struct rsc *tmp)
++{
++      return netobj_equal(&new->handle, &tmp->handle);
++}
++
++static inline void
++rsc_init(struct rsc *new, struct rsc *tmp)
++{
++      new->mechctx = NULL;
++      new->handle.len = tmp->handle.len;
++      new->handle.data = tmp->handle.data;
++      tmp->handle.len = 0;
++      tmp->handle.data = NULL;
++}
++
++static inline void
++rsc_update(struct rsc *new, struct rsc *tmp)
++{
++      new->mechctx = tmp->mechctx;
++      tmp->mechctx = NULL;
++      memset(&new->seqdata, 0, sizeof(new->seqdata));
++      spin_lock_init(&new->seqdata.sd_lock);
++      new->cred = tmp->cred;
++}
++
++static int rsc_parse(struct cache_detail *cd,
++                   char *mesg, int mlen)
++{
++      /* contexthandle expiry [ uid gid N <n gids> mechname ...mechdata... ] */
++      char *buf = mesg;
++      int len, rv;
++      struct rsc rsci, *rscp = NULL;
++      time_t expiry;
++      int status = -EINVAL;
++
++      memset(&rsci, 0, sizeof(rsci));
++      /* context handle */
++      len = qword_get(&mesg, buf, mlen);
++      if (len < 0) goto out;
++      status = -ENOMEM;
++      if (dup_to_netobj(&rsci.handle, buf, len))
++              goto out;
++
++      rsci.h.flags = 0;
++      /* expiry */
++      expiry = get_expiry(&mesg);
++      status = -EINVAL;
++      if (expiry == 0)
++              goto out;
++
++      /* uid, or NEGATIVE */
++      rv = get_int(&mesg, &rsci.cred.cr_uid);
++      if (rv == -EINVAL)
++              goto out;
++      if (rv == -ENOENT)
++              set_bit(CACHE_NEGATIVE, &rsci.h.flags);
++      else {
++              int N, i;
++              struct gss_api_mech *gm;
++              struct xdr_netobj tmp_buf;
++
++              /* gid */
++              if (get_int(&mesg, &rsci.cred.cr_gid))
++                      goto out;
++
++              /* number of additional gid's */
++              if (get_int(&mesg, &N))
++                      goto out;
++              if (N > NGROUPS)
++                      goto out;
++
++              /* gid's */
++              for (i=0; i<N; i++) {
++                      if (get_int(&mesg, &rsci.cred.cr_groups[i]))
++                              goto out;
++              }
++              if (N < NGROUPS)
++                      rsci.cred.cr_groups[N] = NOGROUP;
++
++              /* mech name */
++              len = qword_get(&mesg, buf, mlen);
++              if (len < 0)
++                      goto out;
++              gm = gss_mech_get_by_name(buf);
++              status = -EOPNOTSUPP;
++              if (!gm)
++                      goto out;
++
++              status = -EINVAL;
++              /* mech-specific data: */
++              len = qword_get(&mesg, buf, mlen);
++              if (len < 0) {
++                      gss_mech_put(gm);
++                      goto out;
++              }
++              tmp_buf.len = len;
++              tmp_buf.data = buf;
++              if (gss_import_sec_context(&tmp_buf, gm, &rsci.mechctx)) {
++                      gss_mech_put(gm);
++                      goto out;
++              }
++              gss_mech_put(gm);
++      }
++      rsci.h.expiry_time = expiry;
++      rscp = rsc_lookup(&rsci, 1);
++      status = 0;
++out:
++      rsc_free(&rsci);
++      if (rscp)
++              rsc_put(&rscp->h, &rsc_cache);
++      return status;
++}
++
++static struct cache_detail rsc_cache = {
++      .hash_size      = RSC_HASHMAX,
++      .hash_table     = rsc_table,
++      .name           = "auth.rpcsec.context",
++      .cache_put      = rsc_put,
++      .cache_parse    = rsc_parse,
++};
++
++static DefineSimpleCacheLookup(rsc, 0);
++
++struct rsc *
++gss_svc_searchbyctx(struct xdr_netobj *handle)
++{
++      struct rsc rsci;
++      struct rsc *found;
++
++      rsci.handle = *handle;
++      found = rsc_lookup(&rsci, 0);
++      if (!found)
++              return NULL;
++      if (cache_check(&rsc_cache, &found->h, NULL))
++              return NULL;
++      return found;
++}
++
++/* Implements sequence number algorithm as specified in RFC 2203. */
++static int
++gss_check_seq_num(struct rsc *rsci, int seq_num)
++{
++      struct gss_svc_seq_data *sd = &rsci->seqdata;
++
++      spin_lock(&sd->sd_lock);
++      if (seq_num > sd->sd_max) {
++              if (seq_num >= sd->sd_max + GSS_SEQ_WIN) {
++                      memset(sd->sd_win,0,sizeof(sd->sd_win));
++                      sd->sd_max = seq_num;
++              } else while (sd->sd_max < seq_num) {
++                      sd->sd_max++;
++                      __clear_bit(sd->sd_max % GSS_SEQ_WIN, sd->sd_win);
++              }
++              __set_bit(seq_num % GSS_SEQ_WIN, sd->sd_win);
++              goto ok;
++      } else if (seq_num <= sd->sd_max - GSS_SEQ_WIN) {
++              goto drop;
++      }
++      /* sd_max - GSS_SEQ_WIN < seq_num <= sd_max */
++      if (__test_and_set_bit(seq_num % GSS_SEQ_WIN, sd->sd_win))
++              goto drop;
++ok:
++      spin_unlock(&sd->sd_lock);
++      return 1;
++drop:
++      spin_unlock(&sd->sd_lock);
++      return 0;
++}
++
++static inline u32 round_up_to_quad(u32 i)
++{
++      return (i + 3 ) & ~3;
++}
++
++static inline int
++svc_safe_getnetobj(struct iovec *argv, struct xdr_netobj *o)
++{
++      int l;
++
++      if (argv->iov_len < 4)
++              return -1;
++      o->len = ntohl(svc_getu32(argv));
++      l = round_up_to_quad(o->len);
++      if (argv->iov_len < l)
++              return -1;
++      o->data = argv->iov_base;
++      argv->iov_base += l;
++      argv->iov_len -= l;
++      return 0;
++}
++
++static inline int
++svc_safe_putnetobj(struct iovec *resv, struct xdr_netobj *o)
++{
++      u32 *p;
++
++      if (resv->iov_len + 4 > PAGE_SIZE)
++              return -1;
++      svc_putu32(resv, htonl(o->len));
++      p = resv->iov_base + resv->iov_len;
++      resv->iov_len += round_up_to_quad(o->len);
++      if (resv->iov_len > PAGE_SIZE)
++              return -1;
++      memcpy(p, o->data, o->len);
++      memset((u8 *)p + o->len, 0, round_up_to_quad(o->len) - o->len);
++      return 0;
++}
++
++/* Verify the checksum on the header and return SVC_OK on success.
++ * Otherwise, return SVC_DROP (in the case of a bad sequence number)
++ * or return SVC_DENIED and indicate error in authp.
++ */
++static int
++gss_verify_header(struct svc_rqst *rqstp, struct rsc *rsci,
++                u32 *rpcstart, struct rpc_gss_wire_cred *gc, u32 *authp)
++{
++      struct gss_ctx          *ctx_id = rsci->mechctx;
++      struct xdr_buf          rpchdr;
++      struct xdr_netobj       checksum;
++      u32                     flavor = 0;
++      struct iovec            *argv = &rqstp->rq_arg.head[0];
++      struct iovec            iov;
++
++      /* data to compute the checksum over: */
++      iov.iov_base = rpcstart;
++      iov.iov_len = (u8 *)argv->iov_base - (u8 *)rpcstart;
++      xdr_buf_from_iov(&iov, &rpchdr);
++
++      *authp = rpc_autherr_badverf;
++      if (argv->iov_len < 4)
++              return SVC_DENIED;
++      flavor = ntohl(svc_getu32(argv));
++      if (flavor != RPC_AUTH_GSS)
++              return SVC_DENIED;
++      if (svc_safe_getnetobj(argv, &checksum))
++              return SVC_DENIED;
++
++      if (rqstp->rq_deferred) /* skip verification of revisited request */
++              return SVC_OK;
++      if (gss_verify_mic(ctx_id, &rpchdr, &checksum, NULL)
++                                                      != GSS_S_COMPLETE) {
++              *authp = rpcsec_gsserr_credproblem;
++              return SVC_DENIED;
++      }
++
++      if (gc->gc_seq > MAXSEQ) {
++              dprintk("RPC:      svcauth_gss: discarding request with large sequence number %d\n",
++                              gc->gc_seq);
++              *authp = rpcsec_gsserr_ctxproblem;
++              return SVC_DENIED;
++      }
++      if (!gss_check_seq_num(rsci, gc->gc_seq)) {
++              dprintk("RPC:      svcauth_gss: discarding request with old sequence number %d\n",
++                              gc->gc_seq);
++              return SVC_DROP;
++      }
++      return SVC_OK;
++}
++
++static int
++gss_write_verf(struct svc_rqst *rqstp, struct gss_ctx *ctx_id, u32 seq)
++{
++      u32                     xdr_seq;
++      u32                     maj_stat;
++      struct xdr_buf          verf_data;
++      struct xdr_netobj       mic;
++      u32                     *p;
++      struct iovec            iov;
++
++      svc_putu32(rqstp->rq_res.head, htonl(RPC_AUTH_GSS));
++      xdr_seq = htonl(seq);
++
++      iov.iov_base = &xdr_seq;
++      iov.iov_len = sizeof(xdr_seq);
++      xdr_buf_from_iov(&iov, &verf_data);
++      p = rqstp->rq_res.head->iov_base + rqstp->rq_res.head->iov_len;
++      mic.data = (u8 *)(p + 1);
++      maj_stat = gss_get_mic(ctx_id, 0, &verf_data, &mic);
++      if (maj_stat != GSS_S_COMPLETE)
++              return -1;
++      *p++ = htonl(mic.len);
++      memset((u8 *)p + mic.len, 0, round_up_to_quad(mic.len) - mic.len);
++      p += XDR_QUADLEN(mic.len);
++      if (!xdr_ressize_check(rqstp, p))
++              return -1;
++      return 0;
++}
++
++struct gss_domain {
++      struct auth_domain      h;
++      u32                     pseudoflavor;
++};
++
++/* XXX this should be done in gss_pseudoflavors, and shouldn't be hardcoded: */
++static struct auth_domain *
++find_gss_auth_domain(struct gss_ctx *ctx, u32 svc)
++{
++      switch(gss_get_pseudoflavor(ctx, 0, svc)) {
++              case RPC_AUTH_GSS_KRB5:
++                      return auth_domain_find("gss/krb5");
++              case RPC_AUTH_GSS_KRB5I:
++                      return auth_domain_find("gss/krb5i");
++              case RPC_AUTH_GSS_KRB5P:
++                      return auth_domain_find("gss/krb5p");
++      }
++      return NULL;
++}
++
++int
++svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name)
++{
++      struct gss_domain       *new;
++      struct auth_domain      *test;
++      static char             *prefix = "gss/";
++      int                     stat = -1;
++
++      new = kmalloc(sizeof(*new), GFP_KERNEL);
++      if (!new)
++              goto out;
++      cache_init(&new->h.h);
++      atomic_inc(&new->h.h.refcnt);
++      new->h.name = kmalloc(strlen(name) + strlen(prefix) + 1, GFP_KERNEL);
++      if (!new->h.name)
++              goto out_free_dom;
++      strcpy(new->h.name, prefix);
++      strcat(new->h.name, name);
++      new->h.flavour = RPC_AUTH_GSS;
++      new->pseudoflavor = pseudoflavor;
++      new->h.h.expiry_time = NEVER;
++      new->h.h.flags = 0;
++
++      test = auth_domain_lookup(&new->h, 1);
++      if (test == &new->h) {
++              BUG_ON(atomic_dec_and_test(&new->h.h.refcnt));
++      } else { /* XXX Duplicate registration? */
++              auth_domain_put(&new->h);
++              goto out;
++      }
++      return 0;
++
++out_free_dom:
++      kfree(new);
++out:
++      return stat;
++}
++
++/* It would be nice if this bit of code could be shared with the client.
++ * Obstacles:
++ *    The client shouldn't malloc(), would have to pass in own memory.
++ *    The server uses base of head iovec as read pointer, while the
++ *    client uses separate pointer. */
++static int
++unwrap_integ_data(struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx)
++{
++      /* XXX audit u32/int uses, sign/overflow issues */
++      int stat = -EINVAL;
++      u32 integ_len, maj_stat;
++      struct xdr_netobj mic;
++      struct xdr_buf integ_buf;
++
++      integ_len = ntohl(svc_getu32(&buf->head[0]));
++      if (integ_len & 3)
++              goto out;
++      if (integ_len > buf->len)
++              goto out;
++      if (xdr_buf_subsegment(buf, &integ_buf, 0, integ_len))
++              goto out;
++      /* copy out mic... */
++      if (read_u32_from_xdr_buf(buf, integ_len, &mic.len))
++              goto out;
++      if (mic.len > 256) /* XXX: maximum mic length? */
++              goto out;
++      mic.data = kmalloc(mic.len, GFP_KERNEL);
++      if (read_bytes_from_xdr_buf(buf, integ_len + 4, mic.data, mic.len))
++              goto out;
++      maj_stat = gss_verify_mic(ctx, &integ_buf, &mic, NULL);
++      if (maj_stat != GSS_S_COMPLETE)
++              goto out;
++      if (ntohl(svc_getu32(&buf->head[0])) != seq)
++              goto out;
++      stat = 0;
++out:
++      return stat;
++}
++
++/*
++ * Accept an rpcsec packet.
++ * If context establishment, punt to user space
++ * If data exchange, verify/decrypt
++ * If context destruction, handle here
++ * In the context establishment and destruction case we encode
++ * response here and return SVC_COMPLETE.
++ */
++static int
++svcauth_gss_accept(struct svc_rqst *rqstp, u32 *authp)
++{
++      struct iovec    *argv = &rqstp->rq_arg.head[0];
++      struct iovec    *resv = &rqstp->rq_res.head[0];
++      u32             crlen;
++      struct xdr_netobj tmpobj;
++      struct gss_svc_data *svcdata = rqstp->rq_auth_data;
++      struct rpc_gss_wire_cred *gc;
++      struct rsc      *rsci = NULL;
++      struct rsi      *rsip, rsikey;
++      u32             *rpcstart;
++      u32             *reject_stat = resv->iov_base;
++      int             ret;
++
++      dprintk("RPC:      svcauth_gss: argv->iov_len = %d\n", argv->iov_len);
++
++      *authp = rpc_autherr_badcred;
++      if (!svcdata)
++              svcdata = kmalloc(sizeof(*svcdata), GFP_KERNEL);
++      if (!svcdata)
++              goto auth_err;
++      rqstp->rq_auth_data = svcdata;
++      gc = &svcdata->clcred;
++      
++      /* start of rpc packet is 7 u32's back from here:
++       * xid direction rpcversion prog vers proc flavour
++       */
++      rpcstart = argv->iov_base;
++      rpcstart -= 7;
++
++      /* credential is:
++       *   version(==1), proc(0,1,2,3), seq, service (1,2,3), handle
++       * at least 5 u32s, and is preceeded by length, so that makes 6.
++       */
++
++      if (argv->iov_len < 5 * 4)
++              goto auth_err;
++      crlen = ntohl(svc_getu32(argv));
++      if (ntohl(svc_getu32(argv)) != RPC_GSS_VERSION)
++              goto auth_err;
++      gc->gc_proc = ntohl(svc_getu32(argv));
++      gc->gc_seq = ntohl(svc_getu32(argv));
++      gc->gc_svc = ntohl(svc_getu32(argv));
++      if (svc_safe_getnetobj(argv, &gc->gc_ctx))
++              goto auth_err;
++      if (crlen != round_up_to_quad(gc->gc_ctx.len) + 5 * 4)
++              goto auth_err;
++
++      if ((gc->gc_proc != RPC_GSS_PROC_DATA) && (rqstp->rq_proc != 0))
++              goto auth_err;
++
++      /*
++       * We've successfully parsed the credential. Let's check out the
++       * verifier.  An AUTH_NULL verifier is allowed (and required) for
++       * INIT and CONTINUE_INIT requests. AUTH_RPCSEC_GSS is required for
++       * PROC_DATA and PROC_DESTROY.
++       *
++       * AUTH_NULL verifier is 0 (AUTH_NULL), 0 (length).
++       * AUTH_RPCSEC_GSS verifier is:
++       *   6 (AUTH_RPCSEC_GSS), length, checksum.
++       * checksum is calculated over rpcheader from xid up to here.
++       */
++      *authp = rpc_autherr_badverf;
++      switch (gc->gc_proc) {
++      case RPC_GSS_PROC_INIT:
++      case RPC_GSS_PROC_CONTINUE_INIT:
++              if (argv->iov_len < 2 * 4)
++                      goto auth_err;
++              if (ntohl(svc_getu32(argv)) != RPC_AUTH_NULL)
++                      goto auth_err;
++              if (ntohl(svc_getu32(argv)) != 0)
++                      goto auth_err;
++              break;
++      case RPC_GSS_PROC_DATA:
++      case RPC_GSS_PROC_DESTROY:
++              *authp = rpcsec_gsserr_credproblem;
++              rsci = gss_svc_searchbyctx(&gc->gc_ctx);
++              if (!rsci)
++                      goto auth_err;
++              switch (gss_verify_header(rqstp, rsci, rpcstart, gc, authp)) {
++              case SVC_OK:
++                      break;
++              case SVC_DENIED:
++                      goto auth_err;
++              case SVC_DROP:
++                      goto drop;
++              }
++              break;
++      default:
++              *authp = rpc_autherr_rejectedcred;
++              goto auth_err;
++      }
++
++      /* now act upon the command: */
++      switch (gc->gc_proc) {
++      case RPC_GSS_PROC_INIT:
++      case RPC_GSS_PROC_CONTINUE_INIT:
++              *authp = rpc_autherr_badcred;
++              if (gc->gc_proc == RPC_GSS_PROC_INIT && gc->gc_ctx.len != 0)
++                      goto auth_err;
++              memset(&rsikey, 0, sizeof(rsikey));
++              if (dup_netobj(&rsikey.in_handle, &gc->gc_ctx))
++                      goto drop;
++              *authp = rpc_autherr_badverf;
++              if (svc_safe_getnetobj(argv, &tmpobj)) {
++                      kfree(rsikey.in_handle.data);
++                      goto auth_err;
++              }
++              if (dup_netobj(&rsikey.in_token, &tmpobj)) {
++                      kfree(rsikey.in_handle.data);
++                      goto drop;
++              }
++
++              rsip = rsi_lookup(&rsikey, 0);
++              rsi_free(&rsikey);
++              if (!rsip) {
++                      goto drop;
++              }
++              switch(cache_check(&rsi_cache, &rsip->h, &rqstp->rq_chandle)) {
++              case -EAGAIN:
++                      goto drop;
++              case -ENOENT:
++                      goto drop;
++              case 0:
++                      rsci = gss_svc_searchbyctx(&rsip->out_handle);
++                      if (!rsci) {
++                              goto drop;
++                      }
++                      if (gss_write_verf(rqstp, rsci->mechctx, GSS_SEQ_WIN))
++                              goto drop;
++                      if (resv->iov_len + 4 > PAGE_SIZE)
++                              goto drop;
++                      svc_putu32(resv, rpc_success);
++                      if (svc_safe_putnetobj(resv, &rsip->out_handle))
++                              goto drop;
++                      if (resv->iov_len + 3 * 4 > PAGE_SIZE)
++                              goto drop;
++                      svc_putu32(resv, htonl(rsip->major_status));
++                      svc_putu32(resv, htonl(rsip->minor_status));
++                      svc_putu32(resv, htonl(GSS_SEQ_WIN));
++                      if (svc_safe_putnetobj(resv, &rsip->out_token))
++                              goto drop;
++                      rqstp->rq_client = NULL;
++              }
++              goto complete;
++      case RPC_GSS_PROC_DESTROY:
++              set_bit(CACHE_NEGATIVE, &rsci->h.flags);
++              if (resv->iov_len + 4 > PAGE_SIZE)
++                      goto drop;
++              svc_putu32(resv, rpc_success);
++              goto complete;
++      case RPC_GSS_PROC_DATA:
++              rqstp->rq_client =
++                      find_gss_auth_domain(rsci->mechctx, gc->gc_svc);
++              if (rqstp->rq_client == NULL)
++                      goto auth_err;
++              *authp = rpcsec_gsserr_ctxproblem;
++              if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq))
++                      goto auth_err;
++
++              rqstp->rq_cred = rsci->cred;
++
++              *authp = rpc_autherr_badcred;
++              switch (gc->gc_svc) {
++              case RPC_GSS_SVC_NONE:
++                      break;
++              case RPC_GSS_SVC_INTEGRITY:
++                      if (unwrap_integ_data(&rqstp->rq_arg,
++                                      gc->gc_seq, rsci->mechctx))
++                              goto auth_err;
++                      /* placeholders for length and seq. number: */
++                      svcdata->body_start = resv->iov_base + resv->iov_len;
++                      svc_putu32(resv, 0);
++                      svc_putu32(resv, 0);
++                      break;
++              case RPC_GSS_SVC_PRIVACY:
++                      /* currently unsupported */
++              default:
++                      goto auth_err;
++              }
++              ret = SVC_OK;
++              goto out;
++      }
++auth_err:
++      /* Restore write pointer to original value: */
++      xdr_ressize_check(rqstp, reject_stat);
++      ret = SVC_DENIED;
++      goto out;
++complete:
++      ret = SVC_COMPLETE;
++      goto out;
++drop:
++      ret = SVC_DROP;
++out:
++      if (rsci)
++              rsc_put(&rsci->h, &rsc_cache);
++      return ret;
++}
++
++static int
++svcauth_gss_release(struct svc_rqst *rqstp)
++{
++      struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data;
++      struct rpc_gss_wire_cred *gc = &gsd->clcred;
++      struct xdr_buf *resbuf = &rqstp->rq_res;
++      struct xdr_buf integ_buf;
++      struct xdr_netobj mic;
++      struct iovec *resv;
++      u32 *p;
++      int integ_offset, integ_len;
++      struct rsc *rsci;
++      int stat = -EINVAL;
++
++      /* normally not set till svc_send, but we need it here: */
++      resbuf->len = resbuf->head[0].iov_len
++              + resbuf->page_len + resbuf->tail[0].iov_len;
++      switch (gc->gc_svc) {
++      case RPC_GSS_SVC_NONE:
++              break;
++      case RPC_GSS_SVC_INTEGRITY:
++              p = gsd->body_start;
++              /* move accept_stat to right place: */
++              memcpy(p, p + 2, 4);
++              p++;
++              integ_offset = (u8 *)(p + 1) - (u8 *)resbuf->head[0].iov_base;
++              integ_len = resbuf->len - integ_offset;
++              BUG_ON(integ_len % 4);
++              *p++ = htonl(integ_len);
++              *p++ = htonl(gc->gc_seq);
++              if (xdr_buf_subsegment(resbuf, &integ_buf, integ_offset,
++                                      integ_len))
++                      goto out;
++              if (resbuf->page_len == 0) {
++                      BUG_ON(resbuf->tail[0].iov_len);
++                      /* Use head for everything */
++                      resv = &resbuf->head[0];
++              } else if (resbuf->tail[0].iov_base == NULL) {
++                      /* copied from nfsd4_encode_read */
++                      svc_take_page(rqstp);
++                      resbuf->tail[0].iov_base = page_address(rqstp
++                                      ->rq_respages[rqstp->rq_resused-1]);
++                      rqstp->rq_restailpage = rqstp->rq_resused-1;
++                      resbuf->tail[0].iov_len = 0;
++                      resv = &resbuf->tail[0];
++              } else {
++                      resv = &resbuf->tail[0];
++              }
++              /* XXX bounds checking!: */
++              mic.data = (u8 *)resv->iov_base + resv->iov_len + 4;
++              rsci = gss_svc_searchbyctx(&gc->gc_ctx);
++              /* Better error return? Hold count on ctx through
++               * processing instead of looking up again? */
++              if (!rsci)
++                      goto out;
++              /* XXX Whoops, we might overflow here: */
++              if (gss_get_mic(rsci->mechctx, 0, &integ_buf, &mic))
++                      goto out;
++              svc_putu32(resv, htonl(mic.len));
++              resv->iov_len += mic.len;
++              resbuf->len += mic.len; /* not strictly necessary */
++              /* XXX too late, alas: */
++              if (resbuf->len > PAGE_SIZE)
++                      goto out;
++              break;
++      case RPC_GSS_SVC_PRIVACY:
++      default:
++              goto out;
++      }
++
++      stat = 0;
++out:
++      if (rqstp->rq_client)
++              auth_domain_put(rqstp->rq_client);
++      rqstp->rq_client = NULL;
++
++      return stat;
++}
++
++static void
++svcauth_gss_domain_release(struct auth_domain *dom)
++{
++      struct gss_domain *gd = container_of(dom, struct gss_domain, h);
++
++      kfree(dom->name);
++      kfree(gd);
++}
++
++struct auth_ops svcauthops_gss = {
++      .name           = "rpcsec_gss",
++      .flavour        = RPC_AUTH_GSS,
++      .accept         = svcauth_gss_accept,
++      .release        = svcauth_gss_release,
++      .domain_release = svcauth_gss_domain_release,
++};
++
++int
++gss_svc_init(void)
++{
++      cache_register(&rsc_cache);
++      cache_register(&rsi_cache);
++      svc_auth_register(RPC_AUTH_GSS, &svcauthops_gss);
++      return 0;
++}
+diff -puN net/sunrpc/svc.c~CITI_NFS4_ALL net/sunrpc/svc.c
+--- linux-2.6.3/net/sunrpc/svc.c~CITI_NFS4_ALL 2004-02-19 16:47:04.000000000 -0500
++++ linux-2.6.3-bfields/net/sunrpc/svc.c       2004-02-19 16:47:04.000000000 -0500
+@@ -200,6 +200,8 @@ svc_exit_thread(struct svc_rqst *rqstp)
+               kfree(rqstp->rq_resp);
+       if (rqstp->rq_argp)
+               kfree(rqstp->rq_argp);
++      if (rqstp->rq_auth_data)
++              kfree(rqstp->rq_auth_data);
+       kfree(rqstp);
+       /* Release the server */
+@@ -322,6 +324,8 @@ svc_process(struct svc_serv *serv, struc
+               goto err_bad_auth;
+       case SVC_DROP:
+               goto dropit;
++      case SVC_COMPLETE:
++              goto sendit;
+       }
+               
+       progp = serv->sv_program;
+diff -puN net/sunrpc/Makefile~CITI_NFS4_ALL net/sunrpc/Makefile
+--- linux-2.6.3/net/sunrpc/Makefile~CITI_NFS4_ALL      2004-02-19 16:47:05.000000000 -0500
++++ linux-2.6.3-bfields/net/sunrpc/Makefile    2004-02-19 16:47:05.000000000 -0500
+@@ -2,9 +2,9 @@
+ # Makefile for Linux kernel SUN RPC
+ #
+-obj-$(CONFIG_SUNRPC_GSS) += auth_gss/
+ obj-$(CONFIG_SUNRPC) += sunrpc.o
++obj-$(CONFIG_SUNRPC_GSS) += auth_gss/
+ sunrpc-y := clnt.o xprt.o sched.o \
+           auth.o auth_null.o auth_unix.o \
+diff -puN fs/nfsd/nfs4proc.c~CITI_NFS4_ALL fs/nfsd/nfs4proc.c
+--- linux-2.6.3/fs/nfsd/nfs4proc.c~CITI_NFS4_ALL       2004-02-19 16:47:05.000000000 -0500
++++ linux-2.6.3-bfields/fs/nfsd/nfs4proc.c     2004-02-19 16:47:15.000000000 -0500
+@@ -52,15 +52,22 @@
+ #include <linux/nfs4.h>
+ #include <linux/nfsd/state.h>
+ #include <linux/nfsd/xdr4.h>
++#ifdef CONFIG_NFS_V4_ACL
++#include <linux/nfs4_acl.h>
++#endif
+ #define NFSDDBG_FACILITY              NFSDDBG_PROC
+-/* Note: The organization of the OPEN code seems a little strange; it
+- * has been superfluously split into three routines, one of which is named
+- * nfsd4_process_open2() even though there is no nfsd4_process_open1()!
+- * This is because the code has been organized in anticipation of a
+- * subsequent patch which will implement more of the NFSv4 state model.
+- */
++static inline void
++fh_dup2(struct svc_fh *dst, struct svc_fh *src)
++{
++      fh_put(dst);
++      dget(src->fh_dentry);
++      if (src->fh_export)
++              cache_get(&src->fh_export->h);
++      *dst = *src;
++}
++
+ static int
+ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open)
+ {
+@@ -89,12 +96,19 @@ do_open_lookup(struct svc_rqst *rqstp, s
+       if (!status) {
+               set_change_info(&open->op_cinfo, current_fh);
+               fh_dup2(current_fh, &resfh);
++              /* XXXJBF: keep a saved svc_fh struct instead?? */
++              open->op_stateowner->so_replay.rp_openfh_len =
++                      resfh.fh_handle.fh_size;
++              memcpy(open->op_stateowner->so_replay.rp_openfh,
++                              &resfh.fh_handle.fh_base,
++                              resfh.fh_handle.fh_size);
+               accmode = MAY_NOP;
+               if (open->op_share_access & NFS4_SHARE_ACCESS_READ)
+                       accmode = MAY_READ;
+               if (open->op_share_deny & NFS4_SHARE_ACCESS_WRITE)
+                       accmode |= (MAY_WRITE | MAY_TRUNC);
++              accmode |= MAY_OWNER_OVERRIDE;
+               status = fh_verify(rqstp, current_fh, S_IFREG, accmode);
+       }
+@@ -102,19 +116,39 @@ do_open_lookup(struct svc_rqst *rqstp, s
+       return status;
+ }
++/*
++ * nfs4_unlock_state() called in encode
++ */
+ static inline int
+ nfsd4_open(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open)
+ {
+       int status;
+-      dprintk("NFSD: nfsd4_open filename %.*s\n",
+-              (int)open->op_fname.len, open->op_fname.data);
++      dprintk("NFSD: nfsd4_open filename %.*s op_stateowner %p\n",
++              (int)open->op_fname.len, open->op_fname.data,
++              open->op_stateowner);
+       /* This check required by spec. */
+       if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL)
+               return nfserr_inval;
++      open->op_stateowner = NULL;
++      nfs4_lock_state();
++
+       /* check seqid for replay. set nfs4_owner */
+       status = nfsd4_process_open1(open);
++      if (status == NFSERR_REPLAY_ME) {
++              struct nfs4_replay *rp = &open->op_stateowner->so_replay;
++              fh_put(current_fh);
++              current_fh->fh_handle.fh_size = rp->rp_openfh_len;
++              memcpy(&current_fh->fh_handle.fh_base, rp->rp_openfh,
++                              rp->rp_openfh_len);
++              status = fh_verify(rqstp, current_fh, 0, MAY_NOP);
++              if (status)
++                      dprintk("nfsd4_open: replay failed"
++                              " restoring previous filehandle\n");
++              else
++                      status = NFSERR_REPLAY_ME;
++      }
+       if (status)
+               return status;
+       /*
+@@ -172,7 +206,7 @@ static inline int
+ nfsd4_restorefh(struct svc_fh *current_fh, struct svc_fh *save_fh)
+ {
+       if (!save_fh->fh_dentry)
+-              return nfserr_nofilehandle;
++              return nfserr_restorefh;
+       fh_dup2(current_fh, save_fh);
+       return nfs_ok;
+@@ -204,11 +238,16 @@ nfsd4_access(struct svc_rqst *rqstp, str
+ static inline int
+ nfsd4_commit(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_commit *commit)
+ {
++      int status;
++
+       u32 *p = (u32 *)commit->co_verf.data;
+       *p++ = nfssvc_boot.tv_sec;
+       *p++ = nfssvc_boot.tv_usec;
+-      return nfsd_commit(rqstp, current_fh, commit->co_offset, commit->co_count);
++      status = nfsd_commit(rqstp, current_fh, commit->co_offset, commit->co_count);
++      if (status == nfserr_symlink)
++              status = nfserr_inval;
++      return status;
+ }
+ static inline int
+@@ -221,6 +260,8 @@ nfsd4_create(struct svc_rqst *rqstp, str
+       fh_init(&resfh, NFS4_FHSIZE);
+       status = fh_verify(rqstp, current_fh, S_IFDIR, MAY_CREATE);
++      if (status == nfserr_symlink)
++              status = nfserr_notdir;
+       if (status)
+               return status;
+@@ -316,8 +357,10 @@ static inline int
+ nfsd4_link(struct svc_rqst *rqstp, struct svc_fh *current_fh,
+          struct svc_fh *save_fh, struct nfsd4_link *link)
+ {
+-      int status;
++      int status = nfserr_nofilehandle;
++      if (!save_fh->fh_dentry)
++              return status;
+       status = nfsd_link(rqstp, current_fh, link->li_name, link->li_namelen, save_fh);
+       if (!status)
+               set_change_info(&link->li_cinfo, current_fh);
+@@ -327,14 +370,18 @@ nfsd4_link(struct svc_rqst *rqstp, struc
+ static inline int
+ nfsd4_lookupp(struct svc_rqst *rqstp, struct svc_fh *current_fh)
+ {
+-      /*
+-       * XXX: We currently violate the spec in one small respect
+-       * here.  If LOOKUPP is done at the root of the pseudofs,
+-       * the spec requires us to return NFSERR_NOENT.  Personally,
+-       * I think that leaving the filehandle unchanged is more
+-       * logical, but this is an academic question anyway, since
+-       * no clients actually use LOOKUPP.
+-       */
++      struct svc_fh tmp_fh;
++      int ret;
++
++      fh_init(&tmp_fh, NFS4_FHSIZE);
++      if((ret = exp_pseudoroot(rqstp->rq_client, &tmp_fh,
++                            &rqstp->rq_chandle)) != 0)
++              return ret;
++      if (tmp_fh.fh_dentry == current_fh->fh_dentry) {
++              fh_put(&tmp_fh);
++              return nfserr_noent;
++      }
++      fh_put(&tmp_fh);
+       return nfsd_lookup(rqstp, current_fh, "..", 2, current_fh);
+ }
+@@ -345,6 +392,20 @@ nfsd4_lookup(struct svc_rqst *rqstp, str
+ }
+ static inline int
++access_bits_permit_read(unsigned long access_bmap)
++{
++      return test_bit(NFS4_SHARE_ACCESS_READ, &access_bmap) ||
++              test_bit(NFS4_SHARE_ACCESS_BOTH, &access_bmap);
++}
++
++static inline int
++access_bits_permit_write(unsigned long access_bmap)
++{
++      return test_bit(NFS4_SHARE_ACCESS_WRITE, &access_bmap) ||
++              test_bit(NFS4_SHARE_ACCESS_BOTH, &access_bmap);
++}
++
++static inline int
+ nfsd4_read(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_read *read)
+ {
+       struct nfs4_stateid *stp;
+@@ -382,7 +443,7 @@ nfsd4_read(struct svc_rqst *rqstp, struc
+               goto out;
+       }
+       status = nfserr_openmode;
+-      if (!(stp->st_share_access & NFS4_SHARE_ACCESS_READ)) {
++      if (!access_bits_permit_read(stp->st_access_bmap)) {
+               dprintk("NFSD: nfsd4_read: file not opened for read!\n");
+               goto out;
+       }
+@@ -397,6 +458,11 @@ out:
+ static inline int
+ nfsd4_readdir(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_readdir *readdir)
+ {
++      u64 cookie = readdir->rd_cookie;
++      static const nfs4_verifier zeroverf = {
++              .data[0] = 0,
++      };
++
+       /* no need to check permission - this will be done in nfsd_readdir() */
+       if (readdir->rd_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1)
+@@ -405,7 +471,8 @@ nfsd4_readdir(struct svc_rqst *rqstp, st
+       readdir->rd_bmval[0] &= NFSD_SUPPORTED_ATTRS_WORD0;
+       readdir->rd_bmval[1] &= NFSD_SUPPORTED_ATTRS_WORD1;
+-      if (readdir->rd_cookie > ~(u32)0)
++      if ((cookie > ~(u32)0) || (cookie == 1) || (cookie == 2) ||
++          (cookie == 0 && memcmp(readdir->rd_verf.data, zeroverf.data, NFS4_VERIFIER_SIZE)))
+               return nfserr_bad_cookie;
+       readdir->rd_rqstp = rqstp;
+@@ -427,6 +494,8 @@ nfsd4_remove(struct svc_rqst *rqstp, str
+       int status;
+       status = nfsd_unlink(rqstp, current_fh, 0, remove->rm_name, remove->rm_namelen);
++      if (status == nfserr_symlink)
++              return nfserr_notdir;
+       if (!status) {
+               fh_unlock(current_fh);
+               set_change_info(&remove->rm_cinfo, current_fh);
+@@ -438,11 +507,25 @@ static inline int
+ nfsd4_rename(struct svc_rqst *rqstp, struct svc_fh *current_fh,
+            struct svc_fh *save_fh, struct nfsd4_rename *rename)
+ {
+-      int status;
++      int status = nfserr_nofilehandle;
++      if (!save_fh->fh_dentry)
++              return status;
+       status = nfsd_rename(rqstp, save_fh, rename->rn_sname,
+                            rename->rn_snamelen, current_fh,
+                            rename->rn_tname, rename->rn_tnamelen);
++
++      /* the underlying filesystem returns different error's than required
++       * by NFSv4. both save_fh and current_fh have been verified.. */
++      if (status == nfserr_isdir) 
++              status = nfserr_exist;
++      else if ((status == nfserr_notdir) && 
++                  (S_ISDIR(save_fh->fh_dentry->d_inode->i_mode) && 
++                   S_ISDIR(current_fh->fh_dentry->d_inode->i_mode)))
++              status = nfserr_exist;
++      else if (status == nfserr_symlink)
++              status = nfserr_notdir;
++
+       if (!status) {
+               set_change_info(&rename->rn_sinfo, current_fh);
+               set_change_info(&rename->rn_tinfo, save_fh);
+@@ -454,14 +537,18 @@ static inline int
+ nfsd4_setattr(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_setattr *setattr)
+ {
+       struct nfs4_stateid *stp;
+-      int status = nfs_ok;
++      int status = nfserr_nofilehandle;
++
++      if (!current_fh->fh_dentry)
++              goto out;
++      status = nfs_ok;
+       if (setattr->sa_iattr.ia_valid & ATTR_SIZE) {
+               status = nfserr_bad_stateid;
+               if (ZERO_STATEID(&setattr->sa_stateid) || ONE_STATEID(&setattr->sa_stateid)) {
+                       dprintk("NFSD: nfsd4_setattr: magic stateid!\n");
+-                      return status;
++                      goto out;
+               }
+               nfs4_lock_state();
+@@ -469,17 +556,27 @@ nfsd4_setattr(struct svc_rqst *rqstp, st
+                                               &setattr->sa_stateid, 
+                                               CHECK_FH | RDWR_STATE, &stp))) {
+                       dprintk("NFSD: nfsd4_setattr: couldn't process stateid!\n");
+-                      goto out;
++                      goto out_unlock;
+               }
+               status = nfserr_openmode;
+-              if (!(stp->st_share_access & NFS4_SHARE_ACCESS_WRITE)) {
++              if (!access_bits_permit_write(stp->st_access_bmap)) {
+                       dprintk("NFSD: nfsd4_setattr: not opened for write!\n");
+-                      goto out;
++                      goto out_unlock;
+               }
+               nfs4_unlock_state();
+       }
+-      return (nfsd_setattr(rqstp, current_fh, &setattr->sa_iattr, 0, (time_t)0));
++#ifdef CONFIG_NFS_V4_ACL
++      status = nfs_ok;
++      if (setattr->sa_acl != NULL)
++              status = nfsd4_set_nfs4_acl(rqstp, current_fh, setattr->sa_acl);
++      if (status)
++              goto out;
++#endif /* CONFIG_NFS_V4_ACL */
++      status = nfsd_setattr(rqstp, current_fh, &setattr->sa_iattr,
++                              0, (time_t)0);
+ out:
++      return status;
++out_unlock:
+       nfs4_unlock_state();
+       return status;
+ }
+@@ -513,7 +610,7 @@ nfsd4_write(struct svc_rqst *rqstp, stru
+       }
+       status = nfserr_openmode;
+-      if (!(stp->st_share_access & NFS4_SHARE_ACCESS_WRITE)) {
++      if (!access_bits_permit_write(stp->st_access_bmap)) {
+               dprintk("NFSD: nfsd4_write: file not open for write!\n");
+               goto out;
+       }
+@@ -526,9 +623,12 @@ zero_stateid:
+       *p++ = nfssvc_boot.tv_sec;
+       *p++ = nfssvc_boot.tv_usec;
+-      return (nfsd_write(rqstp, current_fh, write->wr_offset,
++      status =  nfsd_write(rqstp, current_fh, write->wr_offset,
+                         write->wr_vec, write->wr_vlen, write->wr_buflen,
+-                        &write->wr_how_written));
++                        &write->wr_how_written);
++      if (status == nfserr_symlink)
++              status = nfserr_inval;
++      return status;
+ out:
+       nfs4_unlock_state();
+       return status;
+@@ -552,8 +652,9 @@ nfsd4_verify(struct svc_rqst *rqstp, str
+       if ((verify->ve_bmval[0] & ~NFSD_SUPPORTED_ATTRS_WORD0)
+           || (verify->ve_bmval[1] & ~NFSD_SUPPORTED_ATTRS_WORD1))
+-              return nfserr_notsupp;
+-      if (verify->ve_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1)
++              return nfserr_attrnotsupp;
++      if ((verify->ve_bmval[0] & FATTR4_WORD0_RDATTR_ERROR)
++          || (verify->ve_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1))
+               return nfserr_inval;
+       if (verify->ve_attrlen & 3)
+               return nfserr_inval;
+@@ -568,7 +669,8 @@ nfsd4_verify(struct svc_rqst *rqstp, str
+       status = nfsd4_encode_fattr(current_fh, current_fh->fh_export,
+                                   current_fh->fh_dentry, buf,
+-                                  &count, verify->ve_bmval);
++                                  &count, verify->ve_bmval,
++                                  rqstp);
+       /* this means that nfsd4_encode_fattr() ran out of space */
+       if (status == nfserr_resource && count == 0)
+@@ -658,13 +760,32 @@ nfsd4_proc_compound(struct svc_rqst *rqs
+                       goto encode_op;
+               }
++              /* All operations except RENEW, SETCLIENTID, RESTOREFH
++              * SETCLIENTID_CONFIRM, PUTFH and PUTROOTFH 
++              * require a valid current filehandle 
++              *
++              * SETATTR NOFILEHANDLE error handled in nfsd4_setattr
++              * due to required returned bitmap argument
++              */ 
++              if ((!current_fh.fh_dentry) && 
++                 !((op->opnum == OP_PUTFH) || (op->opnum == OP_PUTROOTFH) ||
++                 (op->opnum == OP_SETCLIENTID) ||
++                 (op->opnum == OP_SETCLIENTID_CONFIRM) ||
++                 (op->opnum == OP_RENEW) || (op->opnum == OP_RESTOREFH) ||
++                 (op->opnum == OP_RELEASE_LOCKOWNER) || 
++                 (op->opnum == OP_SETATTR))) {
++                      op->status = nfserr_nofilehandle;
++                      goto encode_op;
++              }
+               switch (op->opnum) {
+               case OP_ACCESS:
+                       op->status = nfsd4_access(rqstp, &current_fh, &op->u.access);
+                       break;
+               case OP_CLOSE:
+                       op->status = nfsd4_close(rqstp, &current_fh, &op->u.close);
+-                      op->replay = &op->u.close.cl_stateowner->so_replay;
++                      if (op->u.close.cl_stateowner)
++                              op->replay = 
++                                      &op->u.close.cl_stateowner->so_replay;
+                       break;
+               case OP_COMMIT:
+                       op->status = nfsd4_commit(rqstp, &current_fh, &op->u.commit);
+@@ -683,12 +804,18 @@ nfsd4_proc_compound(struct svc_rqst *rqs
+                       break;
+               case OP_LOCK:
+                       op->status = nfsd4_lock(rqstp, &current_fh, &op->u.lock);
++                      if (op->u.lock.lk_stateowner)
++                              op->replay = 
++                                      &op->u.lock.lk_stateowner->so_replay;
+                       break;
+               case OP_LOCKT:
+                       op->status = nfsd4_lockt(rqstp, &current_fh, &op->u.lockt);
+                       break;
+               case OP_LOCKU:
+                       op->status = nfsd4_locku(rqstp, &current_fh, &op->u.locku);
++                      if (op->u.locku.lu_stateowner)
++                              op->replay = 
++                                      &op->u.locku.lu_stateowner->so_replay;
+                       break;
+               case OP_LOOKUP:
+                       op->status = nfsd4_lookup(rqstp, &current_fh, &op->u.lookup);
+@@ -703,15 +830,21 @@ nfsd4_proc_compound(struct svc_rqst *rqs
+                       break;
+               case OP_OPEN:
+                       op->status = nfsd4_open(rqstp, &current_fh, &op->u.open);
+-                      op->replay = &op->u.open.op_stateowner->so_replay;
++                      if (op->u.open.op_stateowner)
++                              op->replay = 
++                                      &op->u.open.op_stateowner->so_replay;
+                       break;
+               case OP_OPEN_CONFIRM:
+                       op->status = nfsd4_open_confirm(rqstp, &current_fh, &op->u.open_confirm);
+-                      op->replay = &op->u.open_confirm.oc_stateowner->so_replay;
++                      if (op->u.open_confirm.oc_stateowner)
++                              op->replay = 
++                                      &op->u.open_confirm.oc_stateowner->so_replay;
+                       break;
+               case OP_OPEN_DOWNGRADE:
+                       op->status = nfsd4_open_downgrade(rqstp, &current_fh, &op->u.open_downgrade);
+-                      op->replay = &op->u.open_downgrade.od_stateowner->so_replay;
++                      if (op->u.open_downgrade.od_stateowner)
++                              op->replay = 
++                                      &op->u.open_downgrade.od_stateowner->so_replay;