Whamcloud - gitweb
land lustre part of b_hd_sec on HEAD.
authorericm <ericm>
Thu, 31 Mar 2005 22:18:52 +0000 (22:18 +0000)
committerericm <ericm>
Thu, 31 Mar 2005 22:18:52 +0000 (22:18 +0000)
166 files changed:
ldiskfs/kernel_patches/patches/ext3-wantedi-2.6-suse.patch
lustre/Makefile.in
lustre/autoMakefile.am
lustre/autoconf/lustre-core.m4
lustre/cobd/cache_obd.c
lustre/include/liblustre.h
lustre/include/linux/Makefile.am
lustre/include/linux/lustre_acl.h [new file with mode: 0644]
lustre/include/linux/lustre_cfg.h
lustre/include/linux/lustre_compat25.h
lustre/include/linux/lustre_export.h
lustre/include/linux/lustre_idl.h
lustre/include/linux/lustre_import.h
lustre/include/linux/lustre_lite.h
lustre/include/linux/lustre_mds.h
lustre/include/linux/lustre_net.h
lustre/include/linux/lustre_sec.h [new file with mode: 0644]
lustre/include/linux/lustre_smfs.h
lustre/include/linux/lustre_ucache.h [new file with mode: 0644]
lustre/include/linux/lvfs.h
lustre/include/linux/obd.h
lustre/include/linux/obd_class.h
lustre/include/linux/obd_support.h
lustre/kernel_patches/patches/dcache-mds-num-2.6.7.patch
lustre/kernel_patches/patches/export-vanilla-2.6.patch [new file with mode: 0644]
lustre/kernel_patches/patches/ext3-wantedi-2.6-suse.patch
lustre/kernel_patches/patches/header_guards-vanilla-2.6.patch [new file with mode: 0644]
lustre/kernel_patches/patches/iopen-2.6-vanilla.patch
lustre/kernel_patches/patches/linux-2.6.7-CITI_NFS4_ALL-7-lsec.patch [new file with mode: 0644]
lustre/kernel_patches/patches/vfs-dcache_locking-vanilla-2.6.patch [new file with mode: 0644]
lustre/kernel_patches/patches/vfs-dcache_lustre_invalid-vanilla-2.6.patch [new file with mode: 0644]
lustre/kernel_patches/patches/vfs-do_truncate.patch [new file with mode: 0644]
lustre/kernel_patches/patches/vfs-gns_export_doumount.patch [new file with mode: 0644]
lustre/kernel_patches/patches/vfs-intent_api-vanilla-2.6.patch [new file with mode: 0644]
lustre/kernel_patches/patches/vfs-lookup_last-vanilla-2.6.patch [new file with mode: 0644]
lustre/kernel_patches/patches/vfs-pdirops-2.6.7.patch
lustre/kernel_patches/patches/vfs-raw_ops-vanilla-2.6.patch [new file with mode: 0644]
lustre/kernel_patches/patches/vfs_fmode_exec-2.6.patch [new file with mode: 0644]
lustre/kernel_patches/patches/vfs_gns-2.6-vanilla.patch [new file with mode: 0644]
lustre/kernel_patches/patches/vfs_intent-2.6-vanilla.patch
lustre/kernel_patches/series/2.6-vanilla.series
lustre/ldlm/ldlm_lib.c
lustre/ldlm/ldlm_lock.c
lustre/liblustre/Makefile.am
lustre/liblustre/dir.c
lustre/liblustre/file.c
lustre/liblustre/genlib.sh
lustre/liblustre/namei.c
lustre/liblustre/super.c
lustre/llite/dcache.c
lustre/llite/dir.c
lustre/llite/file.c
lustre/llite/llite_gns.c
lustre/llite/llite_internal.h
lustre/llite/llite_lib.c
lustre/llite/llite_nfs.c
lustre/llite/lproc_llite.c
lustre/llite/namei.c
lustre/llite/special.c
lustre/llite/super.c
lustre/llite/super25.c
lustre/llite/symlink.c
lustre/lmv/lmv_intent.c
lustre/lmv/lmv_obd.c
lustre/lmv/lmv_objmgr.c
lustre/lov/lov_obd.c
lustre/lvfs/lvfs_reint.c
lustre/mdc/autoMakefile.am
lustre/mdc/mdc_locks.c
lustre/mdc/mdc_request.c
lustre/mds/Makefile.in
lustre/mds/handler.c
lustre/mds/lproc_mds.c
lustre/mds/mds_fs.c
lustre/mds/mds_groups.c [deleted file]
lustre/mds/mds_internal.h
lustre/mds/mds_lib.c
lustre/mds/mds_lmv.c
lustre/mds/mds_lov.c
lustre/mds/mds_lsd.c [new file with mode: 0644]
lustre/mds/mds_open.c
lustre/mds/mds_reint.c
lustre/obdclass/class_obd.c
lustre/obdclass/genops.c
lustre/obdfilter/filter_log.c
lustre/osc/osc_lib.c
lustre/osc/osc_request.c
lustre/ost/ost_handler.c
lustre/ptlrpc/autoMakefile.am
lustre/ptlrpc/client.c
lustre/ptlrpc/events.c
lustre/ptlrpc/import.c
lustre/ptlrpc/lproc_ptlrpc.c
lustre/ptlrpc/niobuf.c
lustre/ptlrpc/pack_generic.c
lustre/ptlrpc/ptlrpc_internal.h
lustre/ptlrpc/ptlrpc_module.c
lustre/ptlrpc/service.c
lustre/sec/.cvsignore [new file with mode: 0644]
lustre/sec/Makefile.in [new file with mode: 0644]
lustre/sec/Makefile.mk [new file with mode: 0644]
lustre/sec/autoMakefile.am [new file with mode: 0644]
lustre/sec/doc/oss_gss_HLD.lyx [new file with mode: 0644]
lustre/sec/doc/remote_ugid_HLD.lyx [new file with mode: 0644]
lustre/sec/doc/revoke_user_HLD.lyx [new file with mode: 0644]
lustre/sec/gss/.cvsignore [new file with mode: 0644]
lustre/sec/gss/Makefile.in [new file with mode: 0644]
lustre/sec/gss/Makefile.mk [new file with mode: 0644]
lustre/sec/gss/autoMakefile.am [new file with mode: 0644]
lustre/sec/gss/gss_api.h [new file with mode: 0644]
lustre/sec/gss/gss_asn1.h [new file with mode: 0644]
lustre/sec/gss/gss_err.h [new file with mode: 0644]
lustre/sec/gss/gss_generic_token.c [new file with mode: 0644]
lustre/sec/gss/gss_internal.h [new file with mode: 0644]
lustre/sec/gss/gss_krb5.h [new file with mode: 0644]
lustre/sec/gss/gss_krb5_crypto.c [new file with mode: 0644]
lustre/sec/gss/gss_krb5_mech.c [new file with mode: 0644]
lustre/sec/gss/gss_krb5_seal.c [new file with mode: 0644]
lustre/sec/gss/gss_krb5_seqnum.c [new file with mode: 0644]
lustre/sec/gss/gss_krb5_unseal.c [new file with mode: 0644]
lustre/sec/gss/gss_krb5_wrap.c [new file with mode: 0644]
lustre/sec/gss/gss_mech_switch.c [new file with mode: 0644]
lustre/sec/gss/rawobj.c [new file with mode: 0644]
lustre/sec/gss/sec_gss.c [new file with mode: 0644]
lustre/sec/gss/svcsec_gss.c [new file with mode: 0644]
lustre/sec/sec.c [new file with mode: 0644]
lustre/sec/sec_null.c [new file with mode: 0644]
lustre/sec/svcsec.c [new file with mode: 0644]
lustre/sec/svcsec_null.c [new file with mode: 0644]
lustre/sec/upcall_cache.c [new file with mode: 0644]
lustre/smfs/dir.c
lustre/tests/acl_asroot.test [new file with mode: 0644]
lustre/tests/acl_fileutil.test [new file with mode: 0644]
lustre/tests/acl_misc.test [new file with mode: 0644]
lustre/tests/acl_mode [new file with mode: 0755]
lustre/tests/acl_perm.test [new file with mode: 0644]
lustre/tests/conf-sanity.sh
lustre/tests/gns-upcall.sh [new file with mode: 0755]
lustre/tests/insanity.sh
lustre/tests/krb5_env.sh [new file with mode: 0755]
lustre/tests/krb5_refresh_cache.sh [new file with mode: 0755]
lustre/tests/llmount.sh
lustre/tests/llmountcleanup.sh
lustre/tests/llrmount.sh
lustre/tests/lmv.sh
lustre/tests/recovery-small.sh
lustre/tests/replay-dual.sh
lustre/tests/replay-single.sh
lustre/tests/runacltest [new file with mode: 0755]
lustre/tests/sanity-gns.sh [new file with mode: 0644]
lustre/tests/sanity-lmv.sh
lustre/tests/sanity-sec.sh
lustre/tests/sanity.sh
lustre/tests/sanityN.sh
lustre/tests/setfacl.test [new file with mode: 0644]
lustre/tests/test-framework.sh
lustre/utils/.cvsignore
lustre/utils/Makefile.am
lustre/utils/lconf
lustre/utils/lctl.c
lustre/utils/llmount.c
lustre/utils/lrun
lustre/utils/lsd_upcall.c [moved from lustre/utils/l_getgroups.c with 84% similarity]
lustre/utils/lustre_cfg.c
lustre/utils/obd.c
lustre/utils/obdctl.h

index a4867a5..4fd69a5 100644 (file)
@@ -5,10 +5,10 @@
  include/linux/ext3_fs.h |    5 ++++-
  5 files changed, 85 insertions(+), 6 deletions(-)
 
-Index: uml-2.6.3/fs/ext3/ialloc.c
+Index: linux-2.6.7/fs/ext3/ialloc.c
 ===================================================================
---- uml-2.6.3.orig/fs/ext3/ialloc.c    2004-02-20 15:00:48.000000000 +0800
-+++ uml-2.6.3/fs/ext3/ialloc.c 2004-02-21 00:24:45.202693776 +0800
+--- linux-2.6.7.orig/fs/ext3/ialloc.c  2005-03-24 00:27:43.282608616 +0800
++++ linux-2.6.7/fs/ext3/ialloc.c       2005-03-24 00:27:43.888516504 +0800
 @@ -420,7 +420,8 @@
   * For other inodes, search forward from the parent directory's block
   * group to find a free inode.
@@ -58,11 +58,19 @@ Index: uml-2.6.3/fs/ext3/ialloc.c
        if (S_ISDIR(mode)) {
                if (test_opt (sb, OLDALLOC))
                        group = find_group_dir(sb, dir);
-Index: uml-2.6.3/fs/ext3/ioctl.c
+Index: linux-2.6.7/fs/ext3/ioctl.c
 ===================================================================
---- uml-2.6.3.orig/fs/ext3/ioctl.c     2004-01-09 14:59:26.000000000 +0800
-+++ uml-2.6.3/fs/ext3/ioctl.c  2004-02-21 00:21:04.541239416 +0800
-@@ -24,6 +24,31 @@
+--- linux-2.6.7.orig/fs/ext3/ioctl.c   2004-06-16 13:19:13.000000000 +0800
++++ linux-2.6.7/fs/ext3/ioctl.c        2005-03-24 00:31:16.113253440 +0800
+@@ -9,6 +9,7 @@
+ #include <linux/fs.h>
+ #include <linux/jbd.h>
++#include <linux/namei.h>
+ #include <linux/ext3_fs.h>
+ #include <linux/ext3_jbd.h>
+ #include <linux/time.h>
+@@ -24,6 +25,31 @@
        ext3_debug ("cmd = %u, arg = %lu\n", cmd, arg);
  
        switch (cmd) {
@@ -93,12 +101,12 @@ Index: uml-2.6.3/fs/ext3/ioctl.c
 +      }
        case EXT3_IOC_GETFLAGS:
                flags = ei->i_flags & EXT3_FL_USER_VISIBLE;
-               return put_user(flags, (int *) arg);
-Index: uml-2.6.3/fs/ext3/namei.c
+               return put_user(flags, (int __user *) arg);
+Index: linux-2.6.7/fs/ext3/namei.c
 ===================================================================
---- uml-2.6.3.orig/fs/ext3/namei.c     2004-02-20 15:01:27.000000000 +0800
-+++ uml-2.6.3/fs/ext3/namei.c  2004-02-21 00:21:04.611228776 +0800
-@@ -1617,6 +1617,19 @@
+--- linux-2.6.7.orig/fs/ext3/namei.c   2005-03-24 00:27:43.536570008 +0800
++++ linux-2.6.7/fs/ext3/namei.c        2005-03-24 00:27:43.893515744 +0800
+@@ -1939,6 +1939,19 @@
        return err;
  }
  
@@ -118,7 +126,7 @@ Index: uml-2.6.3/fs/ext3/namei.c
  /*
   * By the time this is called, we already have created
   * the directory cache entry for the new file, but it
-@@ -1640,7 +1653,7 @@
+@@ -1963,7 +1976,7 @@
        if (IS_DIRSYNC(dir))
                handle->h_sync = 1;
  
@@ -127,7 +135,7 @@ Index: uml-2.6.3/fs/ext3/namei.c
        err = PTR_ERR(inode);
        if (!IS_ERR(inode)) {
                inode->i_op = &ext3_file_inode_operations;
-@@ -1670,7 +1683,7 @@
+@@ -1994,7 +2007,7 @@
        if (IS_DIRSYNC(dir))
                handle->h_sync = 1;
  
@@ -136,7 +144,7 @@ Index: uml-2.6.3/fs/ext3/namei.c
        err = PTR_ERR(inode);
        if (!IS_ERR(inode)) {
                init_special_inode(inode, inode->i_mode, rdev);
-@@ -1702,7 +1715,7 @@
+@@ -2027,7 +2040,7 @@
        if (IS_DIRSYNC(dir))
                handle->h_sync = 1;
  
@@ -145,7 +153,7 @@ Index: uml-2.6.3/fs/ext3/namei.c
        err = PTR_ERR(inode);
        if (IS_ERR(inode))
                goto out_stop;
-@@ -2094,7 +2107,7 @@
+@@ -2439,7 +2452,7 @@
        if (IS_DIRSYNC(dir))
                handle->h_sync = 1;
  
@@ -154,10 +162,10 @@ Index: uml-2.6.3/fs/ext3/namei.c
        err = PTR_ERR(inode);
        if (IS_ERR(inode))
                goto out_stop;
-Index: uml-2.6.3/include/linux/ext3_fs.h
+Index: linux-2.6.7/include/linux/ext3_fs.h
 ===================================================================
---- uml-2.6.3.orig/include/linux/ext3_fs.h     2004-01-09 14:59:44.000000000 +0800
-+++ uml-2.6.3/include/linux/ext3_fs.h  2004-02-21 00:21:04.613228472 +0800
+--- linux-2.6.7.orig/include/linux/ext3_fs.h   2005-03-24 00:27:43.542569096 +0800
++++ linux-2.6.7/include/linux/ext3_fs.h        2005-03-24 00:27:43.893515744 +0800
 @@ -203,6 +203,7 @@
  #define       EXT3_IOC_SETFLAGS               _IOW('f', 2, long)
  #define       EXT3_IOC_GETVERSION             _IOR('f', 3, long)
@@ -166,7 +174,7 @@ Index: uml-2.6.3/include/linux/ext3_fs.h
  #define       EXT3_IOC_GETVERSION_OLD         _IOR('v', 1, long)
  #define       EXT3_IOC_SETVERSION_OLD         _IOW('v', 2, long)
  #ifdef CONFIG_JBD_DEBUG
-@@ -707,7 +708,8 @@
+@@ -708,7 +709,8 @@
                          dx_hash_info *hinfo);
  
  /* ialloc.c */
@@ -176,7 +184,7 @@ Index: uml-2.6.3/include/linux/ext3_fs.h
  extern void ext3_free_inode (handle_t *, struct inode *);
  extern struct inode * ext3_orphan_get (struct super_block *, unsigned long);
  extern unsigned long ext3_count_free_inodes (struct super_block *);
-@@ -792,4 +794,5 @@
+@@ -793,4 +795,5 @@
  
  #endif        /* __KERNEL__ */
  
index 1907eb1..1a5db43 100644 (file)
@@ -2,6 +2,7 @@
 
 subdir-m += lvfs
 subdir-m += obdclass
+subdir-m += sec
 subdir-m += lov
 subdir-m += lmv
 subdir-m += ptlrpc
index a8197e1..24f80d0 100644 (file)
@@ -5,7 +5,7 @@
 
 AUTOMAKE_OPTIONS = foreign
 
-SUBDIRS = include ldiskfs lvfs obdclass lov ldlm ptlrpc      \
+SUBDIRS = include ldiskfs lvfs obdclass lov ldlm sec ptlrpc      \
        obdecho osc mdc lmv  mds obdfilter ost llite cobd ptlbd smfs snapfs \
        cmobd liblustre doc utils tests conf scripts autoconf
 
index c19132d..dae4f44 100644 (file)
@@ -317,6 +317,23 @@ AC_DEFINE_UNQUOTED(OBD_MAX_IOCTL_BUFFER, $OBD_BUFFER_SIZE, [IOCTL Buffer Size])
 ])
 
 #
+# LC_CONFIG_GSS
+#
+# whether build-in gss/krb5 capability
+#
+AC_DEFUN([LC_CONFIG_GSS],
+[AC_MSG_CHECKING([whether to enable gss/krb5 support])
+AC_ARG_ENABLE([gss],
+       AC_HELP_STRING([--enable-gss],
+                       [enable gss/krb5 support]),
+       [],[enable_gss='yes'])
+AC_MSG_RESULT([$enable_gss])
+if test x$enable_gss != xno ; then
+  AC_DEFINE(ENABLE_GSS, 1, Support GSS/krb5)
+fi
+])
+
+#
 # LC_CONFIG_SNAPFS
 #
 # Whether snapfs is desired
@@ -353,6 +370,7 @@ AC_MSG_RESULT([$enable_smfs])
 AC_DEFUN([LC_PROG_LINUX],
 [LC_CONFIG_BACKINGFS
 LC_CONFIG_PINGER
+LC_CONFIG_GSS
 LC_CONFIG_SNAPFS
 LC_CONFIG_SMFS
 
@@ -423,6 +441,7 @@ AM_CONDITIONAL(USE_QUILT, test x$QUILT != xno)
 AM_CONDITIONAL(MPITESTS, test x$enable_mpitests = xyes, Build MPI Tests)
 AM_CONDITIONAL(SNAPFS, test x$enable_snapfs = xyes)
 AM_CONDITIONAL(SMFS, test x$enable_smfs = xyes)
+AM_CONDITIONAL(GSS, test x$enable_gss = xyes)
 AM_CONDITIONAL(LIBLUSTRE, test x$enable_liblustre = xyes)
 AM_CONDITIONAL(MPITESTS, test x$enable_mpitests = xyes, Build MPI Tests)
 ])
@@ -450,7 +469,6 @@ lustre/ldiskfs/Makefile
 lustre/ldiskfs/autoMakefile
 lustre/ldlm/Makefile
 lustre/liblustre/Makefile
-lustre/liblustre/tests/Makefile
 lustre/llite/Makefile
 lustre/llite/autoMakefile
 lustre/lmv/Makefile
@@ -479,6 +497,10 @@ lustre/ptlrpc/Makefile
 lustre/ptlrpc/autoMakefile
 lustre/scripts/Makefile
 lustre/scripts/version_tag.pl
+lustre/sec/Makefile
+lustre/sec/autoMakefile
+lustre/sec/gss/Makefile
+lustre/sec/gss/autoMakefile
 lustre/smfs/Makefile
 lustre/smfs/autoMakefile
 lustre/snapfs/Makefile
index dd446bd..8a28304 100644 (file)
@@ -351,7 +351,7 @@ static int cobd_precleanup(struct obd_device *obd, int flags)
 }
 
 static int cobd_getattr(struct obd_export *exp, struct obdo *oa,
-                        struct lov_stripe_md *lsm)
+                        struct lov_stripe_md *ea)
 {
         struct obd_device *obd = class_exp2obd(exp);
         struct obd_export *cobd_exp;
@@ -362,7 +362,7 @@ static int cobd_getattr(struct obd_export *exp, struct obdo *oa,
                 return -EINVAL;
         }
         cobd_exp = cobd_get_exp(obd);
-        return obd_getattr(cobd_exp, oa, lsm);
+        return obd_getattr(cobd_exp, oa, ea);
 }
 
 static int cobd_getattr_async(struct obd_export *exp,
@@ -870,8 +870,8 @@ static int  cobd_import_event(struct obd_device *obd,
 }
 
 static int cobd_md_getattr(struct obd_export *exp, struct lustre_id *id,
-                           __u64 valid, unsigned int ea_size,
-                           struct ptlrpc_request **request)
+                          __u64 valid, const char *ea_name, int ea_namelen,
+                           unsigned int ea_size, struct ptlrpc_request **request)
 {
         struct obd_device *obd = class_exp2obd(exp);
         struct obd_export *cobd_exp;
@@ -882,7 +882,7 @@ static int cobd_md_getattr(struct obd_export *exp, struct lustre_id *id,
                 return -EINVAL;
         }
         cobd_exp = cobd_get_exp(obd);
-        return md_getattr(cobd_exp, id, valid, ea_size, request);
+        return md_getattr(cobd_exp, id, valid, NULL, 0, ea_size, request);
 }
 
 static int cobd_md_req2lustre_md (struct obd_export *mdc_exp, 
index 8f925e6..c99e6a5 100644 (file)
@@ -197,16 +197,17 @@ struct module {
         int count;
 };
 
-static inline void MODULE_AUTHOR(char *name)
-{
-        printf("%s\n", name);
-}
-#define MODULE_DESCRIPTION(name) MODULE_AUTHOR(name)
-#define MODULE_LICENSE(name) MODULE_AUTHOR(name)
+#define MODULE_AUTHOR(name)
+#define MODULE_DESCRIPTION(name)
+#define MODULE_LICENSE(name)
+
+#define module_init(init)
+#define module_exit(exit)
 
 #define THIS_MODULE NULL
 #define __init
 #define __exit
+#define __user
 
 /* devices */
 
@@ -275,6 +276,14 @@ static inline void spin_unlock_bh(spinlock_t *l) {}
 static inline void spin_lock_irqsave(spinlock_t *a, unsigned long b) {}
 static inline void spin_unlock_irqrestore(spinlock_t *a, unsigned long b) {}
 
+typedef struct { } rwlock_t;
+#define rwlock_init(x) do {} while(0)
+#define RW_LOCK_UNLOCKED (rwlock_t) {}
+#define read_lock(l)
+#define read_unlock(l)
+#define write_lock(l)
+#define write_unlock(l)
+
 #define min(x,y) ((x)<(y) ? (x) : (y))
 #define max(x,y) ((x)>(y) ? (x) : (y))
 
@@ -287,6 +296,10 @@ static inline void spin_unlock_irqrestore(spinlock_t *a, unsigned long b) {}
        ({ type __x = (x); type __y = (y); __x > __y ? __x: __y; })
 #endif
 
+#define container_of(ptr, type, member) ({                      \
+        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
+        (type *)( (char *)__mptr - offsetof(type,member) );})
+
 /* registering symbols */
 
 #define ERESTARTSYS ERESTART
@@ -313,6 +326,12 @@ static inline int copy_to_user(void *a,void *b, int c)
         return 0;
 }
 
+static inline long strncpy_from_user(char *dest, const char *src, long n)
+{
+        char *s;
+        s = strncpy(dest, src, n);
+        return strnlen(s, n);
+}
 
 /* slabs */
 typedef struct {
@@ -427,7 +446,7 @@ static inline struct page* __grab_cache_page(unsigned long index)
 #define ATTR_ATTR_FLAG  0x0400
 #define ATTR_RAW        0x0800  /* file system, not vfs will massage attrs */
 #define ATTR_FROM_OPEN  0x1000  /* called from open path, ie O_TRUNC */
-#define ATTR_CTIME_SET  0x2000
+/* ATTR_CTIME_SET has been defined in lustre_idl.h */
 
 struct iattr {
         unsigned int    ia_valid;
@@ -457,25 +476,28 @@ struct iattr {
 
 #define INTENT_MAGIC 0x19620323
 
-struct lustre_intent_data {
-        int       it_disposition;
-        int       it_status;
-        __u64     it_lock_handle;
-        void     *it_data;
-        int       it_lock_mode;
-        int it_int_flags;
-};
 struct lookup_intent {
         int     it_magic;
         void    (*it_op_release)(struct lookup_intent *);
         int     it_op;
         int     it_flags;
         int     it_create_mode;
-        union {
-                struct lustre_intent_data lustre;
-        } d;
+       union {
+               void *fs_data; /* FS-specific intent data */
+       } d;
 };
 
+struct lustre_intent_data {
+        int     it_disposition;
+        int     it_status;
+        __u64   it_lock_handle;
+        void    *it_data;
+        int     it_lock_mode;
+        int     it_int_flags;
+};
+
+#define LUSTRE_IT(it) ((struct lustre_intent_data *)((it)->d.fs_data))
+
 static inline void intent_init(struct lookup_intent *it, int op, int flags)
 {
         memset(it, 0, sizeof(*it));
@@ -543,6 +565,8 @@ struct task_struct {
         struct signal pending;
         char comm[32];
         int pid;
+        uid_t uid;
+        gid_t gid;
         int fsuid;
         int fsgid;
         int max_groups;
@@ -625,6 +649,14 @@ static inline int schedule_timeout(signed long t)
 #define time_after(a, b) ((long)(b) - (long)(a) < 0)
 #define time_before(a, b) time_after(b,a)
 
+static inline unsigned long get_seconds(void)
+{
+        struct timeval tv;
+
+        gettimeofday(&tv, NULL);
+        return (tv.tv_sec + tv.tv_usec / 1000000);
+}
+
 struct timer_list {
         struct list_head tl_list;
         void (*function)(unsigned long unused);
index d187775..fc1017d 100644 (file)
@@ -15,4 +15,5 @@ EXTRA_DIST = lprocfs_status.h lustre_debug.h lustre_ha.h lustre_lib.h \
   lustre_export.h lustre_log.h obd_echo.h obd_ptlbd.h obd_trace.h \
   lustre_compat25.h lustre_fsfilt.h lustre_import.h lustre_mds.h obd.h \
   lvfs.h lvfs_linux.h lustre_cfg.h lustre_lite.h  lustre_idl.h lustre_smfs.h \
-  lustre_cmobd.h obd_lmv.h lustre_snap.h
+  lustre_cmobd.h obd_lmv.h lustre_snap.h lustre_sec.h lustre_ucache.h \
+  lustre_acl.h
diff --git a/lustre/include/linux/lustre_acl.h b/lustre/include/linux/lustre_acl.h
new file mode 100644 (file)
index 0000000..2267997
--- /dev/null
@@ -0,0 +1,36 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- 
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ *  Copyright (C) 2002, 2003 Cluster File Systems, Inc.
+ *
+ *   This file is part of Lustre, http://www.lustre.org.
+ *
+ *   Lustre is free software; you can redistribute it and/or
+ *   modify it under the terms of version 2 of the GNU General Public
+ *   License as published by the Free Software Foundation.
+ *
+ *   Lustre is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with Lustre; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */ 
+
+#ifndef _LUSTRE_ACL_H_
+#define _LUSTRE_ACL_H_
+
+#include <linux/xattr_acl.h>
+
+/*
+* the value of LL_ACL_MAX_ENTRIES and LL_ACL_NOT_CACHED should be 
+* kept step with related definition in ext3 (EXT3_ACL_MAX_ENTRIES and
+* EXT3_ACL_NOT_CACHED)
+*/
+#define LL_ACL_MAX_ENTRIES      32      // EXT3_ACL_MAX_ENTRIES
+#define LL_ACL_NOT_CACHED       ((void *)-1) //EXT3_ACL_NOT_CACHED
+
+#endif
index fe446e5..3f2038f 100644 (file)
@@ -40,6 +40,7 @@ enum lcfg_command_type {
         LCFG_LOV_DEL_OBD    = 0x00cf00c,
         LCFG_ADD_CONN       = 0x00cf00d,
         LCFG_DEL_CONN       = 0x00cf00e,
+        LCFG_SET_SECURITY   = 0x00cf00f,
 };
 
 struct lustre_cfg {
@@ -279,6 +280,9 @@ struct lustre_mount_data {
         uint32_t lmd_nal;
         uint32_t lmd_server_ipaddr;
         uint32_t lmd_port;
+        uint32_t lmd_nllu;
+        uint32_t lmd_nllg;
+        char     lmd_security[16];
         char     lmd_mds[64];
         char     lmd_profile[64];
 };
index 711f282..03a88a4 100644 (file)
@@ -99,6 +99,16 @@ static inline int cleanup_group_info(void)
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
 
+/* New (actually old) intent naming */
+#define lookup_intent open_intent
+
+/* And internals */
+#define it_flags flags
+#define it_op op
+#define it_magic magic
+#define it_op_release op_release
+#define it_create_mode create_mode
+
 /*
  * OBD need working random driver, thus all our
  * initialization routines must be called after device
index 525110d..2e4e760 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/lustre_dlm.h>
 
 struct mds_client_data;
+struct mds_idmap_table;
 
 struct mds_export_data {
         struct list_head        med_open_head;
@@ -21,6 +22,12 @@ struct mds_export_data {
         struct mds_client_data *med_mcd;
         loff_t                  med_off;
         int                     med_idx;
+        unsigned int            med_local:1;
+        __u32                   med_nllu;
+        __u32                   med_nllg;
+        /* simple idmapping */
+        spinlock_t              med_idmap_lock;
+        struct mds_idmap_table *med_idmap;
 };
 
 struct osc_creator {
index 198f89c..184572f 100644 (file)
@@ -361,6 +361,7 @@ struct lov_mds_md_v0 {            /* LOV EA mds/wire data (little-endian) */
 #define OBD_MD_FLUID    (0x0000000000000200LL)    /* user ID */
 #define OBD_MD_FLGID    (0x0000000000000400LL)    /* group ID */
 #define OBD_MD_FLFLAGS  (0x0000000000000800LL)    /* flags word */
+#define OBD_MD_FLEA     (0x0000000000001000LL)    /* extended attributes */
 #define OBD_MD_FLNLINK  (0x0000000000002000LL)    /* link count */
 #define OBD_MD_FLGENER  (0x0000000000004000LL)    /* generation number */
 #define OBD_MD_FLINLINE (0x0000000000008000LL)    /* inline data */
@@ -380,12 +381,15 @@ struct lov_mds_md_v0 {            /* LOV EA mds/wire data (little-endian) */
 #define OBD_MD_FLDIREA  (0x0000000020000000LL)    /* dir's extended attribute data */
 #define OBD_MD_REINT    (0x0000000040000000LL)    /* reintegrate oa */
 #define OBD_MD_FID      (0x0000000080000000LL)    /* lustre_id data */
+#define OBD_MD_FLEALIST (0x0000000100000000LL)    /* list extended attributes */
+#define OBD_MD_FLACL_ACCESS (0x0000000200000000LL) /*access acl*/
 
 #define OBD_MD_FLNOTOBD (~(OBD_MD_FLBLOCKS | OBD_MD_LINKNAME |          \
                            OBD_MD_FLEASIZE | OBD_MD_FLHANDLE |          \
                            OBD_MD_FLCKSUM | OBD_MD_FLQOS |              \
                            OBD_MD_FLOSCOPQ | OBD_MD_FLCOOKIE |          \
-                           OBD_MD_MDS))
+                           OBD_MD_FLEA | OBD_MD_FLEALIST |              \
+                           OBD_MD_FLACL_ACCESS | OBD_MD_MDS))
 
 static inline struct lustre_handle *obdo_handle(struct obdo *oa)
 {
@@ -487,10 +491,6 @@ extern void lustre_swab_ost_lvb(struct ost_lvb *);
 
 /* 
  * security descriptor in mds request
- *
- * note gid & cap might need be removed later:
- *  - cap should be obtained on mds
- *  - gid is actually not used.
  */
 struct mds_req_sec_desc {
         __u32           rsd_uid;
@@ -635,6 +635,7 @@ struct lustre_md {
         struct mds_body *body;
         struct lov_stripe_md *lsm;
         struct mea *mea;
+        struct posix_acl *acl_access;
 };
 
 struct mdc_op_data {
@@ -666,11 +667,21 @@ struct mds_rec_setattr {
         __u64            sa_ctime;
 };
 
-/* Remove this once we declare it in include/linux/fs.h (v21 kernel patch?) */
-#ifndef ATTR_CTIME_SET
-#define ATTR_CTIME_SET 0x2000
+/* XXX Following ATTR_XXX should go to vfs patch...  */
+#ifdef ATTR_CTIME_SET
+#error "ATTR_CTIME_SET has been defined somewhere else"
+#endif
+#ifdef ATTR_EA
+#error "ATTR_EA has been defined somewhere else"
+#endif
+#ifdef ATTR_EA_RM
+#error "ATTR_EA_RM has been defined somewhere else"
 #endif
 
+#define ATTR_CTIME_SET  0x00002000
+#define ATTR_EA         0x00040000
+#define ATTR_EA_RM      0x00080000
+
 extern void lustre_swab_mds_rec_setattr (struct mds_rec_setattr *sa);
 
 #ifndef FMODE_READ
@@ -1116,4 +1127,13 @@ static inline struct lustre_id *obdo_id(struct obdo *oa)
         return (struct lustre_id *)raw_id;
 }
 
+/* security negotiate */
+typedef enum {
+        SEC_INIT                = 600,
+        SEC_INIT_CONTINUE       = 601,
+        SEC_FINI                = 602,
+        SEC_LAST_OPC
+} sec_cmd_t;
+#define SEC_FIRST_OPC SEC_INIT
+
 #endif
index e7230d0..d3c182c 100644 (file)
@@ -46,6 +46,8 @@ enum obd_import_event {
         IMP_EVENT_ACTIVE     = 0x808004,
 };
 
+struct ptlrpc_sec;
+
 struct obd_import_conn {
         struct list_head          oic_item;
         struct ptlrpc_connection *oic_conn;
@@ -53,7 +55,6 @@ struct obd_import_conn {
         unsigned long             oic_last_attempt; /* in jiffies */
 };
 
-
 struct obd_import {
         struct portals_handle     imp_handle;
         atomic_t                  imp_refcount;
@@ -70,7 +71,11 @@ struct obd_import {
         struct list_head          imp_sending_list;
         struct list_head          imp_delayed_list;
 
+        /* list of ongoing raw rpcs (only used by gss) */
+        struct list_head          imp_rawrpc_list;
+
         struct obd_device        *imp_obd;
+        struct ptlrpc_sec        *imp_sec;
         wait_queue_head_t         imp_recovery_waitq;
         __u64                     imp_last_replay_transno;
         atomic_t                  imp_inflight;
index bd8341b..866d429 100644 (file)
@@ -36,6 +36,8 @@
 #include <linux/rbtree.h>
 #include <linux/lustre_compat25.h>
 #include <linux/pagemap.h>
+#include <linux/namei.h>
+
 
 /* careful, this is easy to screw up */
 #define PAGE_CACHE_MAXBYTES ((__u64)(~0UL) << PAGE_CACHE_SHIFT)
@@ -45,7 +47,7 @@
 static inline struct lookup_intent *ll_nd2it(struct nameidata *nd)
 {
 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0))
-        return &nd->intent;
+        return &nd->intent.open;
 #else
         return nd->intent;
 #endif
@@ -96,6 +98,7 @@ struct ll_inode_info {
 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0))
         struct inode            lli_vfs_inode;
 #endif
+        struct posix_acl       *lli_acl_access;
 };
 
 // FIXME: replace the name of this with LL_I to conform to kernel stuff
@@ -140,8 +143,19 @@ enum {
 
          LPROC_LL_DIRECT_READ,
          LPROC_LL_DIRECT_WRITE,
-         LPROC_LL_FILE_OPCODES
+         LPROC_LL_SETXATTR,
+         LPROC_LL_GETXATTR,
+         LPROC_LL_FILE_OPCODES,
+};
+
+struct lustre_intent_data {
+        int     it_disposition;
+        int     it_status;
+        __u64   it_lock_handle;
+        void    *it_data;
+        int     it_lock_mode;
 };
+#define LUSTRE_IT(it) ((struct lustre_intent_data *)((it)->d.fs_data))
 
 static inline void
 ll_inode2id(struct lustre_id *id, struct inode *inode)
index d918380..da6aafe 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/lustre_dlm.h>
 #include <linux/lustre_log.h>
 #include <linux/lustre_export.h>
+#include <linux/lustre_ucache.h>
 
 struct ldlm_lock_desc;
 struct mds_obd;
@@ -62,8 +63,10 @@ struct mds_update_record {
         char               *ur_tgt;
         int                 ur_eadatalen;
         void               *ur_eadata;
-        int                 ur_cookielen;
-        struct llog_cookie *ur_logcookies;
+        int                 ur_ea2datalen;
+        void               *ur_ea2data;
+        int                 ur_cookielen;       /* obsolete? */
+        struct llog_cookie *ur_logcookies;      /* obsolete? */
         struct iattr        ur_iattr;
         struct lvfs_ucred   ur_uc;
         __u64               ur_rdev;
@@ -130,6 +133,19 @@ struct mds_client_data {
         __u8 mcd_padding[MDS_LR_CLIENT_SIZE - 64];
 };
 
+/* simple uid/gid mapping hash table */
+struct mds_idmap_item {
+        struct list_head        hash;
+        __u32                   id1;
+        __u32                   id2;
+};
+
+#define MDS_IDMAP_HASHSIZE      (32)
+struct mds_idmap_table {
+        struct list_head uidmap[MDS_IDMAP_HASHSIZE];
+        struct list_head gidmap[MDS_IDMAP_HASHSIZE];
+};
+
 /* file data for open files on MDS */
 struct mds_file_data {
         struct portals_handle mfd_handle; /* must be first */
@@ -166,6 +182,32 @@ struct mds_grp_hash {
         unsigned int            gh_allow_setgroups:1;
 };
 
+/* lustre security descriptor */
+struct lustre_sec_desc {
+        uid_t                   lsd_uid;
+        gid_t                   lsd_gid;
+        struct group_info      *lsd_ginfo;
+        unsigned int            lsd_allow_setuid:1,
+                                lsd_allow_setgid:1,
+                                lsd_allow_setgrp:1;
+};
+
+struct lsd_cache_entry {
+        struct upcall_cache_entry     base;
+        struct lustre_sec_desc        lsd;
+};
+
+struct lsd_downcall_args {
+        int     err;
+        uid_t   uid;
+        gid_t   gid;
+        __u32   ngroups;
+        gid_t  *groups;
+        __u32   allow_setuid;
+        __u32   allow_setgid;
+        __u32   allow_setgrp;
+};
+
 /* mds/mds_reint.c  */
 int mds_reint_rec(struct mds_update_record *r, int offset,
                   struct ptlrpc_request *req, struct lustre_handle *);
@@ -224,8 +266,8 @@ int mdc_req2lustre_md(struct obd_export *exp_lmv, struct ptlrpc_request *req,
                       struct lustre_md *md);
 int mdc_getstatus(struct obd_export *exp, struct lustre_id *rootid);
 int mdc_getattr(struct obd_export *exp, struct lustre_id *id,
-                __u64 valid, unsigned int ea_size,
-                struct ptlrpc_request **request);
+                __u64 valid, const char *ea_name, int ea_namelen,
+                unsigned int ea_size, struct ptlrpc_request **request);
 int mdc_getattr_lock(struct obd_export *exp, struct lustre_id *id,
                      char *filename, int namelen, __u64 valid,
                      unsigned int ea_size, struct ptlrpc_request **request);
index d938260..019e1de 100644 (file)
@@ -241,6 +241,9 @@ struct ptlrpc_cb_id {
         void    *cbid_arg;                      /* additional arg */
 };
 
+struct ptlrpc_cred;
+struct ptlrpc_svcsec;
+
 #define RS_MAX_LOCKS 4
 #define RS_DEBUG     1
 
@@ -259,7 +262,15 @@ struct ptlrpc_reply_state {
         unsigned int          rs_handled:1;     /* been handled yet? */
         unsigned int          rs_on_net:1;      /* reply_out_callback pending? */
 
-        int                   rs_size;
+        struct ptlrpc_svcsec *rs_svcsec;
+        char                 *rs_buf;           /* backend buffer */
+        int                   rs_buf_len;       /* backend buffer length */
+        char                 *rs_repbuf;        /* will be sent on wire */
+        int                   rs_repbuf_len;    /* max on-wire data length */
+        int                   rs_repdata_len;   /* actual on-wire data length */
+        struct lustre_msg    *rs_msg;           /* lustre msg pointer */
+        int                   rs_msg_len;       /* length of lustre msg */
+
         __u64                 rs_transno;
         __u64                 rs_xid;
         struct obd_export    *rs_export;
@@ -271,9 +282,6 @@ struct ptlrpc_reply_state {
         struct lustre_handle  rs_locks[RS_MAX_LOCKS];
         ldlm_mode_t           rs_modes[RS_MAX_LOCKS];
         struct llog_create_locks *rs_llog_locks;
-
-        /* last member: variable sized reply message */
-        struct lustre_msg     rs_msg;
 };
 
 struct ptlrpc_request {
@@ -285,7 +293,8 @@ struct ptlrpc_request {
         unsigned int rq_intr:1, rq_replied:1, rq_err:1,
                 rq_timedout:1, rq_resend:1, rq_restart:1, rq_replay:1,
                 rq_no_resend:1, rq_waiting:1, rq_receiving_reply:1,
-                rq_no_delay:1, rq_net_err:1;
+                rq_no_delay:1, rq_net_err:1, rq_req_wrapped:1,
+                rq_ptlrpcs_restart:1;
         int rq_phase;
         /* client-side refcount for SENT race */
         atomic_t rq_refcount;
@@ -306,6 +315,20 @@ struct ptlrpc_request {
         __u64 rq_xid;
         struct list_head rq_replay_list;
 
+        struct ptlrpc_cred   *rq_cred;        /* client side credit */
+        struct ptlrpc_svcsec *rq_svcsec;      /* server side security */
+        /* XXX temporarily put here XXX */
+        void                 *rq_sec_svcdata; /* server security data */
+        unsigned int          rq_remote;      /* from remote client */
+        uid_t                 rq_auth_uid;
+
+        char *rq_reqbuf;       /* backend request buffer */
+        int   rq_reqbuf_len;   /* backend request buffer length */
+        int   rq_reqdata_len;  /* actual request data length */
+        char *rq_repbuf;       /* backend reply buffer */
+        int   rq_repbuf_len;   /* backend reply buffer length */
+        int   rq_repdata_len;  /* actual reply data length, not used yet */
+
 #if SWAB_PARANOIA
         __u32 rq_req_swab_mask;
         __u32 rq_rep_swab_mask;
@@ -574,6 +597,8 @@ int ptlrpc_error(struct ptlrpc_request *req);
 void ptlrpc_resend_req(struct ptlrpc_request *request);
 int ptl_send_rpc(struct ptlrpc_request *request);
 int ptlrpc_register_rqbd (struct ptlrpc_request_buffer_desc *rqbd);
+int ptlrpc_do_rawrpc(struct obd_import *imp, char *reqbuf, int reqlen,
+                     char *repbuf, int *replenp, int timeout);
 
 /* ptlrpc/client.c */
 void ptlrpc_init_client(int req_portal, int rep_portal, char *name,
diff --git a/lustre/include/linux/lustre_sec.h b/lustre/include/linux/lustre_sec.h
new file mode 100644 (file)
index 0000000..e1e866c
--- /dev/null
@@ -0,0 +1,360 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Copyright (C) 2004 Cluster File Systems, Inc.
+ *
+ *   This file is part of Lustre, http://www.lustre.org.
+ *
+ *   Lustre is free software; you can redistribute it and/or
+ *   modify it under the terms of version 2 of the GNU General Public
+ *   License as published by the Free Software Foundation.
+ *
+ *   Lustre is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with Lustre; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __LINUX_SEC_H_
+#define __LINUX_SEC_H_
+
+/* forward declaration */
+struct obd_import;
+struct ptlrpc_request;
+struct ptlrpc_cred;
+struct ptlrpc_credops;
+struct ptlrpc_sec;
+struct ptlrpc_secops;
+
+#define PTLRPC_SEC_MAX_FLAVORS   (4)
+
+typedef struct ptlrpcs_flavor_s {
+        __u32   flavor;
+        __u32   subflavor;
+} ptlrpcs_flavor_t;
+
+enum ptlrpcs_security_type {
+        PTLRPC_SEC_TYPE_NONE    = 0,    /* no security */
+        PTLRPC_SEC_TYPE_AUTH    = 1,    /* authentication */
+        PTLRPC_SEC_TYPE_PRIV    = 2,    /* privacy */
+};
+
+/*
+ * This header is prepended at any on-wire ptlrpc packets
+ */
+struct ptlrpcs_wire_hdr {
+        __u32   flavor;
+        __u32   sectype;
+        __u32   msg_len;
+        __u32   sec_len;
+};
+
+static inline
+struct ptlrpcs_wire_hdr *buf_to_sec_hdr(void *buf)
+{
+        return (struct ptlrpcs_wire_hdr *) buf;
+}
+
+static inline
+struct lustre_msg *buf_to_lustre_msg(void *buf)
+{
+        return (struct lustre_msg *)
+               ((char *) buf + sizeof(struct ptlrpcs_wire_hdr));
+}
+
+static inline
+__u8 *buf_to_sec_data(void *buf)
+{
+        struct ptlrpcs_wire_hdr *hdr = buf_to_sec_hdr(buf);
+        return (__u8 *) (buf + sizeof(*hdr) + hdr->msg_len);
+}
+
+enum ptlrpcs_flavors {
+        PTLRPC_SEC_NULL = 0,
+        PTLRPC_SEC_GSS  = 1,
+};
+
+#define PTLRPC_SEC_GSS_VERSION (1)
+
+enum ptlrpcs_gss_subflavors {
+        PTLRPC_SEC_GSS_KRB5  = 0,
+        PTLRPC_SEC_GSS_KRB5I = 1,
+        PTLRPC_SEC_GSS_KRB5P = 2,
+};
+
+enum ptlrpcs_gss_proc {
+        PTLRPC_GSS_PROC_DATA =          0,
+        PTLRPC_GSS_PROC_INIT =          1,
+        PTLRPC_GSS_PROC_CONTINUE_INIT = 2,
+        PTLRPC_GSS_PROC_DESTROY =       3,
+        PTLRPC_GSS_PROC_ERR =           4,
+};
+                                                                                                                        
+enum ptlrpcs_gss_svc {
+        PTLRPC_GSS_SVC_NONE =           1,
+        PTLRPC_GSS_SVC_INTEGRITY =      2,
+        PTLRPC_GSS_SVC_PRIVACY =        3,
+};
+
+enum ptlrpcs_error {
+        PTLRPCS_OK =                    0,
+        PTLRPCS_BADCRED =               1,
+        PTLRPCS_REJECTEDCRED =          2,
+        PTLRPCS_BADVERF =               3,
+        PTLRPCS_REJECTEDVERF =          4,
+        PTLRPCS_TOOWEAK =               5,
+        /* GSS errors */
+        PTLRPCS_GSS_CREDPROBLEM =       13,
+        PTLRPCS_GSS_CTXPROBLEM =        14,
+};
+
+struct vfs_cred {
+        __u64   vc_pag;
+        uid_t   vc_uid;
+        gid_t   vc_gid;
+        struct group_info *vc_ginfo;
+};
+
+struct ptlrpc_credops {
+        int     (*refresh)(struct ptlrpc_cred *cred);
+        int     (*match)  (struct ptlrpc_cred *cred,
+                           struct ptlrpc_request *req,
+                           struct vfs_cred *vcred);
+        int     (*sign)   (struct ptlrpc_cred *cred, struct ptlrpc_request *req);
+        int     (*verify) (struct ptlrpc_cred *cred, struct ptlrpc_request *req);
+        int     (*seal)   (struct ptlrpc_cred *cred, struct ptlrpc_request *req);
+        int     (*unseal) (struct ptlrpc_cred *cred, struct ptlrpc_request *req);
+        void    (*destroy)(struct ptlrpc_cred *cred);
+};
+
+#define PTLRPC_CRED_UPTODATE    0x00000001
+#define PTLRPC_CRED_DEAD        0x00000002
+
+struct ptlrpc_cred {
+        struct list_head        pc_hash;   /* linked into hash table */
+        atomic_t                pc_refcount;
+        struct ptlrpc_sec      *pc_sec;
+        struct ptlrpc_credops  *pc_ops;
+        struct ptlrpc_request  *pc_req;
+        unsigned long           pc_expire;
+        int                     pc_flags;
+        /* XXX maybe should not be here */
+        __u64                   pc_pag;
+        uid_t                   pc_uid;
+};
+
+struct ptlrpc_secops {
+        struct ptlrpc_sec *   (*create_sec)    (ptlrpcs_flavor_t *flavor,
+                                                const char *pipe_dir,
+                                                void *pipe_data);
+        void                  (*destroy_sec)   (struct ptlrpc_sec *sec);
+        struct ptlrpc_cred *  (*create_cred)   (struct ptlrpc_sec *sec,
+                                                struct ptlrpc_request *req,
+                                                struct vfs_cred *vcred);
+        /* buffer manipulation */
+        int                   (*alloc_reqbuf)  (struct ptlrpc_sec *sec,
+                                                struct ptlrpc_request *req,
+                                                int lustre_msg_size);
+        int                   (*alloc_repbuf)  (struct ptlrpc_sec *sec,
+                                                struct ptlrpc_request *req,
+                                                int lustre_msg_size);
+        void                  (*free_reqbuf)   (struct ptlrpc_sec *sec,
+                                                struct ptlrpc_request *req);
+        void                  (*free_repbuf)   (struct ptlrpc_sec *sec,
+                                                struct ptlrpc_request *req);
+        /* security payload size estimation */
+        int                   (*est_req_payload)(struct ptlrpc_sec *sec,
+                                                 int msgsize);
+        int                   (*est_rep_payload)(struct ptlrpc_sec *sec,
+                                                 int msgsize);
+};
+
+struct ptlrpc_sec_type {
+        struct module          *pst_owner;
+        char                   *pst_name;
+        atomic_t                pst_inst;       /* instance, debug only */
+        ptlrpcs_flavor_t        pst_flavor;
+        struct ptlrpc_secops   *pst_ops;
+};
+
+#define PTLRPC_CREDCACHE_NR     8
+#define PTLRPC_CREDCACHE_MASK   (PTLRPC_CREDCACHE_NR - 1)
+
+struct ptlrpc_sec {
+        struct ptlrpc_sec_type *ps_type;
+        struct list_head        ps_credcache[PTLRPC_CREDCACHE_NR];
+        spinlock_t              ps_lock;        /* protect cred cache */
+        __u32                   ps_sectype;
+        ptlrpcs_flavor_t        ps_flavor;
+        atomic_t                ps_refcount;
+        atomic_t                ps_credcount;
+        struct obd_import      *ps_import;
+        /* actual security model need initialize following fields */
+        unsigned long           ps_expire;      /* cache expire interval */
+        unsigned long           ps_nextgc;      /* next gc time */
+        unsigned int            ps_flags;
+};
+
+/* sec.c */
+int  ptlrpcs_register(struct ptlrpc_sec_type *type);
+int  ptlrpcs_unregister(struct ptlrpc_sec_type *type);
+
+struct ptlrpc_sec * ptlrpcs_sec_create(ptlrpcs_flavor_t *flavor,
+                                       struct obd_import *import,
+                                       const char *pipe_dir,
+                                       void *pipe_data);
+void ptlrpcs_sec_put(struct ptlrpc_sec *sec);
+void ptlrpcs_sec_invalidate_cache(struct ptlrpc_sec *sec);
+
+struct ptlrpc_cred * ptlrpcs_cred_lookup(struct ptlrpc_sec *sec,
+                                         struct vfs_cred *vcred);
+void ptlrpcs_cred_put(struct ptlrpc_cred *cred, int sync);
+
+static inline void ptlrpcs_cred_get(struct ptlrpc_cred *cred)
+{
+        LASSERT(atomic_read(&cred->pc_refcount));
+        atomic_inc(&cred->pc_refcount);
+}
+
+static inline int ptlrpcs_cred_is_uptodate(struct ptlrpc_cred *cred)
+{
+        LASSERT(cred);
+        LASSERT(atomic_read(&cred->pc_refcount));
+        return (cred->pc_flags & PTLRPC_CRED_UPTODATE);
+}
+static inline int ptlrpcs_cred_refresh(struct ptlrpc_cred *cred)
+{
+        LASSERT(cred);
+        LASSERT(atomic_read(&cred->pc_refcount));
+        LASSERT(cred->pc_ops);
+        LASSERT(cred->pc_ops->refresh);
+        return cred->pc_ops->refresh(cred);
+}
+static inline void ptlrpcs_cred_die(struct ptlrpc_cred *cred)
+{
+        LASSERT(atomic_read(&cred->pc_refcount));
+        LASSERT(cred->pc_sec);
+        if (!(cred->pc_flags & PTLRPC_CRED_DEAD)) {
+                spin_lock(&cred->pc_sec->ps_lock);
+                cred->pc_flags |= PTLRPC_CRED_DEAD;
+                cred->pc_flags &= ~PTLRPC_CRED_UPTODATE;
+                list_del_init(&cred->pc_hash);
+                spin_unlock(&cred->pc_sec->ps_lock);
+        }
+}
+static inline int ptlrpcs_cred_is_dead(struct ptlrpc_cred *cred)
+{
+        return(cred->pc_flags & PTLRPC_CRED_DEAD);
+}
+
+static inline int ptlrpcs_est_req_payload(struct ptlrpc_sec *sec,
+                                          int datasize)
+{
+        struct ptlrpc_secops *ops;
+
+        LASSERT(sec);
+        LASSERT(sec->ps_type);
+        LASSERT(sec->ps_type->pst_ops);
+
+        ops = sec->ps_type->pst_ops;
+        if (ops->est_req_payload)
+                return ops->est_req_payload(sec, datasize);
+        else
+                return 0;
+}
+
+static inline int ptlrpcs_est_rep_payload(struct ptlrpc_sec *sec,
+                                          int datasize)
+{
+        struct ptlrpc_secops *ops;
+
+        LASSERT(sec);
+        LASSERT(sec->ps_type);
+        LASSERT(sec->ps_type->pst_ops);
+
+        ops = sec->ps_type->pst_ops;
+        if (ops->est_rep_payload)
+                return ops->est_rep_payload(sec, datasize);
+        else
+                return 0;
+}
+
+int ptlrpcs_cli_wrap_request(struct ptlrpc_request *req);
+int ptlrpcs_cli_unwrap_reply(struct ptlrpc_request *req);
+int ptlrpcs_cli_alloc_reqbuf(struct ptlrpc_request *req, int msgsize);
+int ptlrpcs_cli_alloc_repbuf(struct ptlrpc_request *req, int msgsize);
+void ptlrpcs_cli_free_reqbuf(struct ptlrpc_request *req);
+void ptlrpcs_cli_free_repbuf(struct ptlrpc_request *req);
+
+/* higher interface */
+int  ptlrpcs_import_get_sec(struct obd_import *imp);
+void ptlrpcs_import_drop_sec(struct obd_import *imp);
+int  ptlrpcs_req_get_cred(struct ptlrpc_request *req);
+void ptlrpcs_req_drop_cred(struct ptlrpc_request *req);
+int  ptlrpcs_req_replace_dead_cred(struct ptlrpc_request *req);
+int  ptlrpcs_req_refresh_cred(struct ptlrpc_request *req);
+
+/* internal helpers */
+int sec_alloc_reqbuf(struct ptlrpc_sec *sec, struct ptlrpc_request *req,
+                     int msgsize, int secsize);
+void sec_free_reqbuf(struct ptlrpc_sec *sec, struct ptlrpc_request *req);
+
+/* sec_null.c */
+int ptlrpcs_null_init(void);
+int ptlrpcs_null_exit(void);
+
+/**********************************************************
+ * Server side stuff
+ **********************************************************/
+
+struct ptlrpc_reply_state;
+
+struct ptlrpc_svcsec {
+        struct module           *pss_owner;
+        char                    *pss_name;
+        ptlrpcs_flavor_t         pss_flavor;
+        int                      pss_sec_size;
+
+        int                    (*accept)      (struct ptlrpc_request *req,
+                                               enum ptlrpcs_error *res);
+        int                    (*authorize)   (struct ptlrpc_request *req);
+        int                    (*alloc_repbuf)(struct ptlrpc_svcsec *svcsec,
+                                               struct ptlrpc_request *req,
+                                               int msgsize);
+        void                   (*free_repbuf) (struct ptlrpc_svcsec *svcsec,
+                                               struct ptlrpc_reply_state *rs);
+        void                   (*cleanup_req) (struct ptlrpc_svcsec *svcsec,
+                                               struct ptlrpc_request *req);
+};
+
+#define SVC_OK          1
+#define SVC_COMPLETE    2
+#define SVC_DROP        3
+#define SVC_LOGIN       4
+#define SVC_LOGOUT      5
+
+int svcsec_register(struct ptlrpc_svcsec *ss);
+int svcsec_unregister(struct ptlrpc_svcsec *ss);
+int svcsec_accept(struct ptlrpc_request *req, enum ptlrpcs_error *res);
+int svcsec_authorize(struct ptlrpc_request *req);
+int svcsec_alloc_repbuf(struct ptlrpc_svcsec *svcsec,
+                        struct ptlrpc_request *req, int msgsize);
+void svcsec_cleanup_req(struct ptlrpc_request *req);
+
+struct ptlrpc_svcsec * svcsec_get(struct ptlrpc_svcsec *sec);
+void svcsec_put(struct ptlrpc_svcsec *sec);
+
+/* internal helpers */
+int svcsec_alloc_reply_state(struct ptlrpc_request *req,
+                             int msgsize, int secsize);
+void svcsec_free_reply_state(struct ptlrpc_reply_state *rs);
+
+/* svcsec_null.c */
+int svcsec_null_init(void);
+int svcsec_null_exit(void);
+
+#endif /* __LINUX_SEC_H_ */
index ee3e43a..7f83f04 100644 (file)
@@ -26,6 +26,7 @@
 #ifndef __LUSTRE_SMFS_H
 #define __LUSTRE_SMFS_H
 
+#include <linux/namei.h>
 struct snap_inode_info {
        int sn_flags;           /*the flags indicated inode type */
        int sn_gen;             /*the inode generation*/
diff --git a/lustre/include/linux/lustre_ucache.h b/lustre/include/linux/lustre_ucache.h
new file mode 100644 (file)
index 0000000..68e37db
--- /dev/null
@@ -0,0 +1,79 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ */
+
+#ifndef _UPCALL_CACHE_H
+#define _UPCALL_CACHE_H
+
+#define UC_CACHE_NEW            0x01
+#define UC_CACHE_ACQUIRING      0x02
+#define UC_CACHE_INVALID        0x04
+#define UC_CACHE_EXPIRED        0x08
+
+#define UC_CACHE_IS_NEW(i)          ((i)->ue_flags & UC_CACHE_NEW)
+#define UC_CACHE_IS_INVALID(i)      ((i)->ue_flags & UC_CACHE_INVALID)
+#define UC_CACHE_IS_ACQUIRING(i)    ((i)->ue_flags & UC_CACHE_ACQUIRING)
+#define UC_CACHE_IS_EXPIRED(i)      ((i)->ue_flags & UC_CACHE_EXPIRED)
+#define UC_CACHE_IS_VALID(i)        ((i)->ue_flags == 0)
+
+#define UC_CACHE_SET_NEW(i)         (i)->ue_flags |= UC_CACHE_NEW
+#define UC_CACHE_SET_INVALID(i)     (i)->ue_flags |= UC_CACHE_INVALID
+#define UC_CACHE_SET_ACQUIRING(i)   (i)->ue_flags |= UC_CACHE_ACQUIRING
+#define UC_CACHE_SET_EXPIRED(i)     (i)->ue_flags |= UC_CACHE_EXPIRED
+#define UC_CACHE_SET_VALID(i)       (i)->ue_flags = 0
+
+#define UC_CACHE_CLEAR_NEW(i)       (i)->ue_flags &= ~UC_CACHE_NEW
+#define UC_CACHE_CLEAR_ACQUIRING(i) (i)->ue_flags &= ~UC_CACHE_ACQUIRING
+#define UC_CACHE_CLEAR_INVALID(i)   (i)->ue_flags &= ~UC_CACHE_INVALID
+#define UC_CACHE_CLEAR_EXPIRED(i)   (i)->ue_flags &= ~UC_CACHE_EXPIRED
+
+struct upcall_cache;
+
+struct upcall_cache_entry {
+        struct list_head        ue_hash;
+        atomic_t                ue_refcount;
+        __u64                   ue_key;
+        struct upcall_cache    *ue_cache;
+        int                     ue_flags;
+        wait_queue_head_t       ue_waitq;
+        unsigned long           ue_acquire_expire;
+        unsigned long           ue_expire;
+};
+
+#define UC_CACHE_UPCALL_MAXPATH (1024)
+
+struct upcall_cache {
+        struct list_head       *uc_hashtable;
+        int                     uc_hashsize;
+        rwlock_t                uc_hashlock;
+
+        char                   *uc_name;
+        char                    uc_upcall[UC_CACHE_UPCALL_MAXPATH];
+        unsigned long           uc_acquire_expire;
+        unsigned long           uc_entry_expire;
+
+        /* functions */
+        unsigned int                (*hash)(struct upcall_cache *, __u64);
+        struct upcall_cache_entry*  (*alloc_entry)(struct upcall_cache *, __u64);
+        void                        (*free_entry)(struct upcall_cache *,
+                                                  struct upcall_cache_entry *);
+        int                         (*make_upcall)(struct upcall_cache *,
+                                                   struct upcall_cache_entry *);
+        int                         (*parse_downcall)(struct upcall_cache *,
+                                                      struct upcall_cache_entry *,
+                                                      void *args);
+};
+
+void upcall_cache_init_entry(struct upcall_cache *cache,
+                             struct upcall_cache_entry *entry,
+                             __u64 key);
+struct upcall_cache_entry *
+upcall_cache_get_entry(struct upcall_cache *cache, __u64 key);
+void upcall_cache_put_entry(struct upcall_cache_entry *entry);
+int upcall_cache_downcall(struct upcall_cache *cache, __u64 key,
+                          int err, void *args);
+void upcall_cache_flush_one(struct upcall_cache *cache, __u64 key);
+void upcall_cache_flush_idle(struct upcall_cache *cache);
+void upcall_cache_flush_all(struct upcall_cache *cache);
+
+#endif /* _UPCALL_CACHE_H */
index 5e3cbd0..96898fd 100644 (file)
@@ -1,3 +1,6 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ */ 
 #ifndef __LVFS_H__
 #define __LVFS_H__
 
@@ -6,6 +9,8 @@
 #define LL_ID_NAMELEN (16 + 1 + 8 + 1)
 
 #if defined __KERNEL__
+#include <linux/dcache.h>
+#include <linux/namei.h>
 #include <linux/lustre_compat25.h>
 #include <linux/lvfs_linux.h>
 #endif 
@@ -18,13 +23,13 @@ struct mds_grp_hash_entry;
 
 /* simple.c */
 struct lvfs_ucred {
-        struct mds_grp_hash_entry *luc_ghash;
-        struct group_info *luc_ginfo;
+        struct lustre_sec_desc *luc_lsd;
+        struct group_info      *luc_ginfo;
         __u32 luc_fsuid;
         __u32 luc_fsgid;
         __u32 luc_cap;
         __u32 luc_uid;
-       __u32 luc_umask;
+        __u32 luc_umask;
 };
 
 struct lvfs_callback_ops {
@@ -100,11 +105,11 @@ ll_lookup_one_len(const char *name, struct dentry *dparent, int namelen)
 {
         struct dentry *dchild;
 #ifdef S_PDIROPS
-       struct qstr qstr;
-       void *lock;
-       qstr.name = name;
-       qstr.len = namelen;
-       lock = lock_dir(dparent->d_inode, &qstr);
+        struct qstr qstr;
+        void *lock;
+        qstr.name = name;
+        qstr.len = namelen;
+        lock = lock_dir(dparent->d_inode, &qstr);
 #else
         down(&dparent->d_inode->i_sem);
 #endif
@@ -112,7 +117,7 @@ ll_lookup_one_len(const char *name, struct dentry *dparent, int namelen)
         dchild = lookup_one_len(name, dparent, namelen);
 
 #ifdef S_PDIROPS
-       unlock_dir(dparent->d_inode, lock);
+        unlock_dir(dparent->d_inode, lock);
 #else
         up(&dparent->d_inode->i_sem);
 #endif
@@ -125,6 +130,18 @@ static inline void ll_sleep(int t)
         schedule_timeout(t * HZ);
         set_current_state(TASK_RUNNING);
 }
+
+static inline struct dentry *
+ll_d_lookup(const char *name,
+           struct dentry *dparent, int len)
+{
+       struct qstr qstr;
+
+       qstr.len = len;
+       qstr.name = name;
+       qstr.hash = full_name_hash(name, len);
+       return d_lookup(dparent, &qstr);
+}
 #endif
 
 static inline int ll_id2str(char *str, __u64 id, __u32 generation)
index 29c77c7..a7f8b5f 100644 (file)
@@ -20,7 +20,6 @@
 #define IOC_MDC_LOOKUP       _IOWR(IOC_MDC_TYPE, 20, struct obd_device *)
 /* Moved to lustre_user.h
 #define IOC_MDC_GETSTRIPE    _IOWR(IOC_MDC_TYPE, 21, struct lov_mds_md *) */
-#define IOC_MDC_FINISH_GNS   _IOWR(IOC_MDC_TYPE, 22, struct obd_device *)
 #define IOC_MDC_MAX_NR       50
 
 #ifdef __KERNEL__
@@ -275,6 +274,12 @@ struct client_obd {
         int                      cl_max_mds_cookiesize;
         kdev_t                   cl_sandev;
 
+        /* security flavors */
+        __u32                    cl_sec_flavor;
+        __u32                    cl_sec_subflavor;
+        __u32                    cl_nllu; /* non lustre local user */
+        __u32                    cl_nllg; /* non lustre local group */
+
         //struct llog_canceld_ctxt *cl_llcd; /* it's included by obd_llog_ctxt */
         void                    *cl_llcd_offset;
 
@@ -386,6 +391,10 @@ struct mds_obd {
         struct dentry                   *mds_id_dir;
         int                              mds_obd_type;
         struct dentry                   *mds_unnamed_dir; /* for mdt_obd_create only */
+
+        /* security related */
+        char                            *mds_mds_sec;
+        char                            *mds_ost_sec;
 };
 
 struct echo_obd {
@@ -850,8 +859,8 @@ struct md_ops {
                          void *, int, ldlm_completion_callback,
                          ldlm_blocking_callback, void *);
         int (*m_getattr)(struct obd_export *, struct lustre_id *,
-                         __u64, unsigned int,
-                         struct ptlrpc_request **);
+                         __u64, const char *, int,
+                         unsigned int, struct ptlrpc_request **);
         int (*m_getattr_lock)(struct obd_export *, struct lustre_id *,
                               char *, int, __u64,
                               unsigned int, struct ptlrpc_request **);
index faba9a6..6bb4dca 100644 (file)
@@ -1245,14 +1245,14 @@ static inline int md_delete_inode(struct obd_export *exp,
 }
 
 static inline int md_getattr(struct obd_export *exp, struct lustre_id *id,
-                             __u64 valid, unsigned int ea_size,
-                             struct ptlrpc_request **request)
+                             __u64 valid, const char *ea_name, int ea_namelen,
+                             unsigned int ea_size, struct ptlrpc_request **request)
 {
         int rc;
         ENTRY;
         EXP_CHECK_MD_OP(exp, getattr);
         MD_COUNTER_INCREMENT(exp->exp_obd, getattr);
-        rc = MDP(exp->exp_obd, getattr)(exp, id, valid, ea_size, request);
+        rc = MDP(exp->exp_obd, getattr)(exp, id, valid, ea_name, ea_namelen, ea_size, request);
         RETURN(rc);
 }
 
index 8783209..64db5f7 100644 (file)
@@ -145,6 +145,14 @@ extern wait_queue_head_t obd_race_waitq;
 #define OBD_FAIL_TGT_REPLY_NET           0x700
 #define OBD_FAIL_TGT_CONN_RACE           0x701
 
+#define OBD_FAIL_SVCSEC_ACCEPT_BEG       0x750
+#define OBD_FAIL_SVCSEC_ACCEPT_END       0x751
+#define OBD_FAIL_SVCSEC_WRAP_BEG         0x752
+#define OBD_FAIL_SVCSEC_WRAP_END         0x753
+#define OBD_FAIL_SVCGSS_ERR_NOTIFY       0x760
+#define OBD_FAIL_SVCGSS_INIT_REQ         0x780
+#define OBD_FAIL_SVCGSS_INIT_REP         0x781
+
 /* preparation for a more advanced failure testbed (not functional yet) */
 #define OBD_FAIL_MASK_SYS    0x0000FF00
 #define OBD_FAIL_MASK_LOC    (0x000000FF | OBD_FAIL_MASK_SYS)
index d86d1b6..466235d 100644 (file)
@@ -1,8 +1,8 @@
 Index: linux-2.6.7/include/linux/dcache.h
 ===================================================================
---- linux-2.6.7.orig/include/linux/dcache.h    2004-08-30 17:20:57.000000000 +0800
-+++ linux-2.6.7/include/linux/dcache.h 2004-08-30 17:39:12.000000000 +0800
-@@ -94,6 +94,9 @@
+--- linux-2.6.7.orig/include/linux/dcache.h    2005-03-23 23:28:49.669799416 +0800
++++ linux-2.6.7/include/linux/dcache.h 2005-03-23 23:38:25.648237384 +0800
+@@ -86,6 +86,9 @@
        spinlock_t d_lock;              /* per dentry lock */
        struct inode *d_inode;          /* Where the name belongs to - NULL is
                                         * negative */
@@ -12,11 +12,12 @@ Index: linux-2.6.7/include/linux/dcache.h
        /*
         * The next three fields are touched by __d_lookup.  Place them here
         * so they all fit in a 16-byte range, with 16-byte alignment.
-@@ -166,6 +169,7 @@
+@@ -158,6 +161,8 @@
  #define DCACHE_UNHASHED               0x0010  
- #define DCACHE_LUSTRE_INVALID     0x0020  /* Lustre invalidated */
+ #define DCACHE_LUSTRE_INVALID 0x0020  /* invalidated by Lustre */
  
 +#define DCACHE_CROSS_REF       0x0040  /* entry points to inode on another MDS */
++
  extern spinlock_t dcache_lock;
  
+ /**
diff --git a/lustre/kernel_patches/patches/export-vanilla-2.6.patch b/lustre/kernel_patches/patches/export-vanilla-2.6.patch
new file mode 100644 (file)
index 0000000..c18a380
--- /dev/null
@@ -0,0 +1,94 @@
+Index: linux-2.6.7/mm/truncate.c
+===================================================================
+--- linux-2.6.7.orig/mm/truncate.c     2004-06-16 13:20:04.000000000 +0800
++++ linux-2.6.7/mm/truncate.c  2005-03-23 23:30:30.676444072 +0800
+@@ -42,7 +42,7 @@
+  * its lock, b) when a concurrent invalidate_inode_pages got there first and
+  * c) when tmpfs swizzles a page between a tmpfs inode and swapper_space.
+  */
+-static void
++void
+ truncate_complete_page(struct address_space *mapping, struct page *page)
+ {
+       if (page->mapping != mapping)
+@@ -58,6 +58,8 @@
+       page_cache_release(page);       /* pagecache ref */
+ }
++EXPORT_SYMBOL(truncate_complete_page);
++
+ /*
+  * This is for invalidate_inode_pages().  That function can be called at
+  * any time, and is not supposed to throw away dirty pages.  But pages can
+Index: linux-2.6.7/fs/super.c
+===================================================================
+--- linux-2.6.7.orig/fs/super.c        2004-06-16 13:19:22.000000000 +0800
++++ linux-2.6.7/fs/super.c     2005-03-23 23:30:30.648448328 +0800
+@@ -804,6 +804,8 @@
+       return (struct vfsmount *)sb;
+ }
++EXPORT_SYMBOL(do_kern_mount);
++
+ struct vfsmount *kern_mount(struct file_system_type *type)
+ {
+       return do_kern_mount(type->name, 0, type->name, NULL);
+Index: linux-2.6.7/fs/jbd/journal.c
+===================================================================
+--- linux-2.6.7.orig/fs/jbd/journal.c  2004-06-16 13:18:59.000000000 +0800
++++ linux-2.6.7/fs/jbd/journal.c       2005-03-23 23:30:30.647448480 +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.7/kernel/exit.c
+===================================================================
+--- linux-2.6.7.orig/kernel/exit.c     2004-06-16 13:19:52.000000000 +0800
++++ linux-2.6.7/kernel/exit.c  2005-03-23 23:34:17.539955576 +0800
+@@ -256,6 +256,8 @@
+       write_unlock_irq(&tasklist_lock);
+ }
++EXPORT_SYMBOL(reparent_to_init);
++
+ void __set_special_pids(pid_t session, pid_t pgrp)
+ {
+       struct task_struct *curr = current;
+@@ -435,6 +437,7 @@
+ {
+       __exit_files(tsk);
+ }
++EXPORT_SYMBOL(exit_files);
+ static inline void __put_fs_struct(struct fs_struct *fs)
+ {
+Index: linux-2.6.7/include/linux/fs.h
+===================================================================
+--- linux-2.6.7.orig/include/linux/fs.h        2005-03-23 23:30:08.535809960 +0800
++++ linux-2.6.7/include/linux/fs.h     2005-03-23 23:30:30.675444224 +0800
+@@ -1133,6 +1133,7 @@
+ extern struct vfsmount *kern_mount(struct file_system_type *);
+ extern int may_umount_tree(struct vfsmount *);
+ extern int may_umount(struct vfsmount *);
++struct vfsmount *do_kern_mount(const char *type, int flags, const char *name, void *data);
+ extern long do_mount(char *, char *, char *, unsigned long, void *);
+ extern int vfs_statfs(struct super_block *, struct kstatfs *);
+Index: linux-2.6.7/include/linux/mm.h
+===================================================================
+--- linux-2.6.7.orig/include/linux/mm.h        2004-06-16 13:18:56.000000000 +0800
++++ linux-2.6.7/include/linux/mm.h     2005-03-23 23:30:30.673444528 +0800
+@@ -653,6 +653,9 @@
+ extern unsigned long do_brk(unsigned long, unsigned long);
++/* truncate.c */
++extern void truncate_complete_page(struct address_space *mapping,struct page *);
++
+ /* filemap.c */
+ extern unsigned long page_unuse(struct page *);
+ extern void truncate_inode_pages(struct address_space *, loff_t);
index a4867a5..4fd69a5 100644 (file)
@@ -5,10 +5,10 @@
  include/linux/ext3_fs.h |    5 ++++-
  5 files changed, 85 insertions(+), 6 deletions(-)
 
-Index: uml-2.6.3/fs/ext3/ialloc.c
+Index: linux-2.6.7/fs/ext3/ialloc.c
 ===================================================================
---- uml-2.6.3.orig/fs/ext3/ialloc.c    2004-02-20 15:00:48.000000000 +0800
-+++ uml-2.6.3/fs/ext3/ialloc.c 2004-02-21 00:24:45.202693776 +0800
+--- linux-2.6.7.orig/fs/ext3/ialloc.c  2005-03-24 00:27:43.282608616 +0800
++++ linux-2.6.7/fs/ext3/ialloc.c       2005-03-24 00:27:43.888516504 +0800
 @@ -420,7 +420,8 @@
   * For other inodes, search forward from the parent directory's block
   * group to find a free inode.
@@ -58,11 +58,19 @@ Index: uml-2.6.3/fs/ext3/ialloc.c
        if (S_ISDIR(mode)) {
                if (test_opt (sb, OLDALLOC))
                        group = find_group_dir(sb, dir);
-Index: uml-2.6.3/fs/ext3/ioctl.c
+Index: linux-2.6.7/fs/ext3/ioctl.c
 ===================================================================
---- uml-2.6.3.orig/fs/ext3/ioctl.c     2004-01-09 14:59:26.000000000 +0800
-+++ uml-2.6.3/fs/ext3/ioctl.c  2004-02-21 00:21:04.541239416 +0800
-@@ -24,6 +24,31 @@
+--- linux-2.6.7.orig/fs/ext3/ioctl.c   2004-06-16 13:19:13.000000000 +0800
++++ linux-2.6.7/fs/ext3/ioctl.c        2005-03-24 00:31:16.113253440 +0800
+@@ -9,6 +9,7 @@
+ #include <linux/fs.h>
+ #include <linux/jbd.h>
++#include <linux/namei.h>
+ #include <linux/ext3_fs.h>
+ #include <linux/ext3_jbd.h>
+ #include <linux/time.h>
+@@ -24,6 +25,31 @@
        ext3_debug ("cmd = %u, arg = %lu\n", cmd, arg);
  
        switch (cmd) {
@@ -93,12 +101,12 @@ Index: uml-2.6.3/fs/ext3/ioctl.c
 +      }
        case EXT3_IOC_GETFLAGS:
                flags = ei->i_flags & EXT3_FL_USER_VISIBLE;
-               return put_user(flags, (int *) arg);
-Index: uml-2.6.3/fs/ext3/namei.c
+               return put_user(flags, (int __user *) arg);
+Index: linux-2.6.7/fs/ext3/namei.c
 ===================================================================
---- uml-2.6.3.orig/fs/ext3/namei.c     2004-02-20 15:01:27.000000000 +0800
-+++ uml-2.6.3/fs/ext3/namei.c  2004-02-21 00:21:04.611228776 +0800
-@@ -1617,6 +1617,19 @@
+--- linux-2.6.7.orig/fs/ext3/namei.c   2005-03-24 00:27:43.536570008 +0800
++++ linux-2.6.7/fs/ext3/namei.c        2005-03-24 00:27:43.893515744 +0800
+@@ -1939,6 +1939,19 @@
        return err;
  }
  
@@ -118,7 +126,7 @@ Index: uml-2.6.3/fs/ext3/namei.c
  /*
   * By the time this is called, we already have created
   * the directory cache entry for the new file, but it
-@@ -1640,7 +1653,7 @@
+@@ -1963,7 +1976,7 @@
        if (IS_DIRSYNC(dir))
                handle->h_sync = 1;
  
@@ -127,7 +135,7 @@ Index: uml-2.6.3/fs/ext3/namei.c
        err = PTR_ERR(inode);
        if (!IS_ERR(inode)) {
                inode->i_op = &ext3_file_inode_operations;
-@@ -1670,7 +1683,7 @@
+@@ -1994,7 +2007,7 @@
        if (IS_DIRSYNC(dir))
                handle->h_sync = 1;
  
@@ -136,7 +144,7 @@ Index: uml-2.6.3/fs/ext3/namei.c
        err = PTR_ERR(inode);
        if (!IS_ERR(inode)) {
                init_special_inode(inode, inode->i_mode, rdev);
-@@ -1702,7 +1715,7 @@
+@@ -2027,7 +2040,7 @@
        if (IS_DIRSYNC(dir))
                handle->h_sync = 1;
  
@@ -145,7 +153,7 @@ Index: uml-2.6.3/fs/ext3/namei.c
        err = PTR_ERR(inode);
        if (IS_ERR(inode))
                goto out_stop;
-@@ -2094,7 +2107,7 @@
+@@ -2439,7 +2452,7 @@
        if (IS_DIRSYNC(dir))
                handle->h_sync = 1;
  
@@ -154,10 +162,10 @@ Index: uml-2.6.3/fs/ext3/namei.c
        err = PTR_ERR(inode);
        if (IS_ERR(inode))
                goto out_stop;
-Index: uml-2.6.3/include/linux/ext3_fs.h
+Index: linux-2.6.7/include/linux/ext3_fs.h
 ===================================================================
---- uml-2.6.3.orig/include/linux/ext3_fs.h     2004-01-09 14:59:44.000000000 +0800
-+++ uml-2.6.3/include/linux/ext3_fs.h  2004-02-21 00:21:04.613228472 +0800
+--- linux-2.6.7.orig/include/linux/ext3_fs.h   2005-03-24 00:27:43.542569096 +0800
++++ linux-2.6.7/include/linux/ext3_fs.h        2005-03-24 00:27:43.893515744 +0800
 @@ -203,6 +203,7 @@
  #define       EXT3_IOC_SETFLAGS               _IOW('f', 2, long)
  #define       EXT3_IOC_GETVERSION             _IOR('f', 3, long)
@@ -166,7 +174,7 @@ Index: uml-2.6.3/include/linux/ext3_fs.h
  #define       EXT3_IOC_GETVERSION_OLD         _IOR('v', 1, long)
  #define       EXT3_IOC_SETVERSION_OLD         _IOW('v', 2, long)
  #ifdef CONFIG_JBD_DEBUG
-@@ -707,7 +708,8 @@
+@@ -708,7 +709,8 @@
                          dx_hash_info *hinfo);
  
  /* ialloc.c */
@@ -176,7 +184,7 @@ Index: uml-2.6.3/include/linux/ext3_fs.h
  extern void ext3_free_inode (handle_t *, struct inode *);
  extern struct inode * ext3_orphan_get (struct super_block *, unsigned long);
  extern unsigned long ext3_count_free_inodes (struct super_block *);
-@@ -792,4 +794,5 @@
+@@ -793,4 +795,5 @@
  
  #endif        /* __KERNEL__ */
  
diff --git a/lustre/kernel_patches/patches/header_guards-vanilla-2.6.patch b/lustre/kernel_patches/patches/header_guards-vanilla-2.6.patch
new file mode 100644 (file)
index 0000000..e8b6abb
--- /dev/null
@@ -0,0 +1,45 @@
+%diffstat
+ blockgroup_lock.h |    4 +++-
+ percpu_counter.h  |    4 ++++
+ 2 files changed, 7 insertions(+), 1 deletion(-)
+
+%patch
+Index: linux-2.6.6/include/linux/percpu_counter.h
+===================================================================
+--- linux-2.6.6.orig/include/linux/percpu_counter.h    2004-04-04 11:37:23.000000000 +0800
++++ linux-2.6.6/include/linux/percpu_counter.h 2004-05-22 16:08:16.000000000 +0800
+@@ -3,6 +3,8 @@
+  *
+  * WARNING: these things are HUGE.  4 kbytes per counter on 32-way P4.
+  */
++#ifndef _LINUX_PERCPU_COUNTER_H
++#define _LINUX_PERCPU_COUNTER_H
+ #include <linux/config.h>
+ #include <linux/spinlock.h>
+@@ -101,3 +103,5 @@ static inline void percpu_counter_dec(st
+ {
+       percpu_counter_mod(fbc, -1);
+ }
++
++#endif /* _LINUX_PERCPU_COUNTER_H */
+Index: linux-2.6.6/include/linux/blockgroup_lock.h
+===================================================================
+--- linux-2.6.6.orig/include/linux/blockgroup_lock.h   2004-04-04 11:36:26.000000000 +0800
++++ linux-2.6.6/include/linux/blockgroup_lock.h        2004-05-22 16:08:45.000000000 +0800
+@@ -3,6 +3,8 @@
+  *
+  * Simple hashed spinlocking.
+  */
++#ifndef _LINUX_BLOCKGROUP_LOCK_H
++#define _LINUX_BLOCKGROUP_LOCK_H
+ #include <linux/config.h>
+ #include <linux/spinlock.h>
+@@ -55,4 +57,4 @@ static inline void bgl_lock_init(struct 
+ #define sb_bgl_lock(sb, block_group) \
+       (&(sb)->s_blockgroup_lock.locks[(block_group) & (NR_BG_LOCKS-1)].lock)
+-
++#endif
+
index cb504d9..88e0843 100644 (file)
@@ -159,7 +159,7 @@ Index: linux-stage/fs/ext3/iopen.c
 +      list_add(&dentry->d_alias, &inode->i_dentry);   /* d_instantiate */
 +      dentry->d_inode = inode;
 +
-+      __d_rehash(dentry, 0);                          /* d_rehash */
++      __d_rehash(dentry);                             /* d_rehash */
 +      spin_unlock(&dcache_lock);
 +
 +      return NULL;
@@ -222,7 +222,7 @@ Index: linux-stage/fs/ext3/iopen.c
 +      /* Move the goal to the de hash queue */
 +      goal->d_flags &= ~ DCACHE_DISCONNECTED;
 +      security_d_instantiate(goal, inode);
-+      __d_rehash(dentry, 0);
++      __d_rehash(dentry);
 +      __d_move(goal, dentry);
 +      spin_unlock(&dcache_lock);
 +      iput(inode);
@@ -235,7 +235,7 @@ Index: linux-stage/fs/ext3/iopen.c
 +      dentry->d_inode = inode;
 +do_rehash:
 +      if (rehash)
-+              __d_rehash(dentry, 0);                  /* d_rehash */
++              __d_rehash(dentry);                     /* d_rehash */
 +      spin_unlock(&dcache_lock);
 +
 +      return NULL;
diff --git a/lustre/kernel_patches/patches/linux-2.6.7-CITI_NFS4_ALL-7-lsec.patch b/lustre/kernel_patches/patches/linux-2.6.7-CITI_NFS4_ALL-7-lsec.patch
new file mode 100644 (file)
index 0000000..f754546
--- /dev/null
@@ -0,0 +1,16246 @@
+--- linux-2.6.7/Documentation/filesystems/00-INDEX.lsec        2004-06-15 23:20:26.000000000 -0600
++++ linux-2.6.7/Documentation/filesystems/00-INDEX     2005-03-23 14:28:24.576313528 -0700
+@@ -28,6 +28,8 @@ jfs.txt
+       - info and mount options for the JFS filesystem.
+ ncpfs.txt
+       - info on Novell Netware(tm) filesystem using NCP protocol.
++nfs4.txt
++      - info and mount options for the nfs4 filesystem.
+ ntfs.txt
+       - info and mount options for the NTFS filesystem (Windows NT).
+ proc.txt
+--- linux-2.6.7/Documentation/filesystems/nfs4.txt.lsec        2005-03-23 14:28:24.576313528 -0700
++++ linux-2.6.7/Documentation/filesystems/nfs4.txt     2005-03-23 14:28:24.576313528 -0700
+@@ -0,0 +1,20 @@
++NFS version 4
++=============
++
++NFS version 4 is specified by RFC3530.  Compared to earlier NFS versions,
++it provides enhanced security and better client caching, among other features.
++
++In addition to basic file operations, the NFS client supports locking, kerberos
++(basic authentication and integrity), and reboot recovery.
++
++As this writing (July 2004), patches to nfs-utils and util-linux are required
++for NFSv4 support; see http://www.citi.umich.edu/projects/nfsv4/linux/ for
++patches and instructions.
++
++The kernel treats NFS version 4 as a separate filesystem type, nfs4, so it is
++mounted using "mount -tnfs4 server:/path /mntpoint", not by mounting the nfs
++filesystem with -onfsver=4.
++
++Mount options:
++
++XXX?
+--- linux-2.6.7/fs/locks.c.lsec        2004-06-15 23:20:03.000000000 -0600
++++ linux-2.6.7/fs/locks.c     2005-03-23 14:28:22.425640480 -0700
+@@ -317,7 +317,7 @@ static int flock_to_posix_lock(struct fi
+       if (l->l_len == 0)
+               fl->fl_end = OFFSET_MAX;
+       
+-      fl->fl_owner = current->files;
++      fl->fl_owner = 0;
+       fl->fl_pid = current->tgid;
+       fl->fl_file = filp;
+       fl->fl_flags = FL_POSIX;
+@@ -357,7 +357,7 @@ static int flock64_to_posix_lock(struct 
+       if (l->l_len == 0)
+               fl->fl_end = OFFSET_MAX;
+       
+-      fl->fl_owner = current->files;
++      fl->fl_owner = 0;
+       fl->fl_pid = current->tgid;
+       fl->fl_file = filp;
+       fl->fl_flags = FL_POSIX;
+@@ -920,7 +920,7 @@ int posix_lock_file(struct file *filp, s
+  */
+ int locks_mandatory_locked(struct inode *inode)
+ {
+-      fl_owner_t owner = current->files;
++      unsigned int pid = current->tgid;
+       struct file_lock *fl;
+       /*
+@@ -930,7 +930,9 @@ int locks_mandatory_locked(struct inode 
+       for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
+               if (!IS_POSIX(fl))
+                       continue;
+-              if (fl->fl_owner != owner)
++              if (fl->fl_owner != 0)
++                      break;
++              if (fl->fl_pid != pid)
+                       break;
+       }
+       unlock_kernel();
+@@ -958,7 +960,7 @@ int locks_mandatory_area(int read_write,
+       int error;
+       locks_init_lock(&fl);
+-      fl.fl_owner = current->files;
++      fl.fl_owner = 0;
+       fl.fl_pid = current->tgid;
+       fl.fl_file = filp;
+       fl.fl_flags = FL_POSIX | FL_ACCESS;
+@@ -1684,7 +1686,7 @@ void locks_remove_posix(struct file *fil
+       lock_kernel();
+       while (*before != NULL) {
+               struct file_lock *fl = *before;
+-              if (IS_POSIX(fl) && (fl->fl_owner == owner)) {
++              if (IS_POSIX(fl) && posix_same_owner(fl, &lock)) {
+                       locks_delete_lock(before);
+                       continue;
+               }
+@@ -1982,18 +1984,6 @@ int lock_may_write(struct inode *inode, 
+ EXPORT_SYMBOL(lock_may_write);
+-static inline void __steal_locks(struct file *file, fl_owner_t from)
+-{
+-      struct inode *inode = file->f_dentry->d_inode;
+-      struct file_lock *fl = inode->i_flock;
+-
+-      while (fl) {
+-              if (fl->fl_file == file && fl->fl_owner == from)
+-                      fl->fl_owner = current->files;
+-              fl = fl->fl_next;
+-      }
+-}
+-
+ /* When getting ready for executing a binary, we make sure that current
+  * has a files_struct on its own. Before dropping the old files_struct,
+  * we take over ownership of all locks for all file descriptors we own.
+@@ -2002,31 +1992,6 @@ static inline void __steal_locks(struct 
+  */
+ void steal_locks(fl_owner_t from)
+ {
+-      struct files_struct *files = current->files;
+-      int i, j;
+-
+-      if (from == files)
+-              return;
+-
+-      lock_kernel();
+-      j = 0;
+-      for (;;) {
+-              unsigned long set;
+-              i = j * __NFDBITS;
+-              if (i >= files->max_fdset || i >= files->max_fds)
+-                      break;
+-              set = files->open_fds->fds_bits[j++];
+-              while (set) {
+-                      if (set & 1) {
+-                              struct file *file = files->fd[i];
+-                              if (file)
+-                                      __steal_locks(file, from);
+-                      }
+-                      i++;
+-                      set >>= 1;
+-              }
+-      }
+-      unlock_kernel();
+ }
+ EXPORT_SYMBOL(steal_locks);
+--- linux-2.6.7/fs/hostfs/hostfs_kern.c.lsec   2005-03-23 14:25:58.982447160 -0700
++++ linux-2.6.7/fs/hostfs/hostfs_kern.c        2005-03-23 14:33:11.946626600 -0700
+@@ -290,7 +290,6 @@ static void hostfs_delete_inode(struct i
+ {
+       if(HOSTFS_I(inode)->fd != -1) {
+               close_file(&HOSTFS_I(inode)->fd);
+-              printk("Closing host fd in .delete_inode\n");
+               HOSTFS_I(inode)->fd = -1;
+       }
+       clear_inode(inode);
+@@ -303,7 +302,6 @@ static void hostfs_destroy_inode(struct 
+       if(HOSTFS_I(inode)->fd != -1) {
+               close_file(&HOSTFS_I(inode)->fd);
+-              printk("Closing host fd in .destroy_inode\n");
+       }
+       kfree(HOSTFS_I(inode));
+--- linux-2.6.7/fs/open.c.lsec 2005-03-23 14:26:01.774022776 -0700
++++ linux-2.6.7/fs/open.c      2005-03-23 14:28:23.226518728 -0700
+@@ -1025,7 +1025,7 @@ int filp_close(struct file *filp, fl_own
+       }
+       dnotify_flush(filp, id);
+-      locks_remove_posix(filp, id);
++      locks_remove_posix(filp, 0);
+       fput(filp);
+       return retval;
+ }
+--- linux-2.6.7/fs/nfsd/export.c.lsec  2004-06-15 23:19:36.000000000 -0600
++++ linux-2.6.7/fs/nfsd/export.c       2005-03-23 14:28:24.686296808 -0700
+@@ -255,7 +255,7 @@ static inline void svc_expkey_update(str
+       new->ek_export = item->ek_export;
+ }
+-static DefineSimpleCacheLookup(svc_expkey,0) /* no inplace updates */
++static DefineSimpleCacheLookup(svc_expkey)
+ #define       EXPORT_HASHBITS         8
+ #define       EXPORT_HASHMAX          (1<< EXPORT_HASHBITS)
+@@ -487,8 +487,72 @@ static inline void svc_export_update(str
+       new->ex_fsid = item->ex_fsid;
+ }
+-static DefineSimpleCacheLookup(svc_export,1) /* allow inplace updates */
++struct svc_export *
++svc_export_lookup(struct svc_export *item, int set)
++{
++      struct svc_export *tmp, *new = NULL;
++      struct cache_head **hp, **head;
++      head = &svc_export_cache.hash_table[svc_export_hash(item)];
++retry:
++      if (set||new)
++              write_lock(&svc_export_cache.hash_lock);
++      else
++              read_lock(&svc_export_cache.hash_lock);
++      for(hp=head; *hp != NULL; hp = &tmp->h.next) {
++              tmp = container_of(*hp, struct svc_export, h);
++              if (svc_export_match(item, tmp)) { /* found a match */
++                      cache_get(&tmp->h);
++                      if (set) {
++                              if (test_bit(CACHE_NEGATIVE,  &item->h.flags))
++                                       set_bit(CACHE_NEGATIVE, &tmp->h.flags);
++                              else {
++                                      clear_bit(CACHE_NEGATIVE, &tmp->h.flags);
++                                      svc_export_update(tmp, item);
++                              }
++                      }
++                      if (set||new)
++                              write_unlock(&svc_export_cache.hash_lock);
++                      else
++                              read_unlock(&svc_export_cache.hash_lock);
++                      if (set)
++                              cache_fresh(&svc_export_cache, &tmp->h,
++                                              item->h.expiry_time);
++                      if (new)
++                              svc_export_put(&new->h, &svc_export_cache);
++                      return tmp;
++              }
++      }
++      /* Didn't find anything */
++      if (new) {
++              svc_export_init(new, item);
++              new->h.next = *head;
++              *head = &new->h;
++              set_bit(CACHE_HASHED, &new->h.flags);
++              svc_export_cache.entries++;
++              if (set) {
++                      tmp = new;
++                      if (test_bit(CACHE_NEGATIVE, &item->h.flags))
++                              set_bit(CACHE_NEGATIVE, &tmp->h.flags);
++                      else
++                              svc_export_update(tmp, item);
++              }
++      }
++      if (set||new)
++              write_unlock(&svc_export_cache.hash_lock);
++      else
++              read_unlock(&svc_export_cache.hash_lock);
++      if (new && set)
++              cache_fresh(&svc_export_cache, &new->h, item->h.expiry_time);
++      if (new)
++              return new;
++      new = kmalloc(sizeof(*new), GFP_KERNEL);
++      if (new) {
++              cache_init(&new->h);
++              goto retry;
++      }
++      return NULL;
++}
+ struct svc_expkey *
+ exp_find_key(svc_client *clp, int fsid_type, u32 *fsidv, struct cache_req *reqp)
+--- linux-2.6.7/fs/nfsd/nfs4callback.c.lsec    2005-03-23 14:28:24.578313224 -0700
++++ linux-2.6.7/fs/nfsd/nfs4callback.c 2005-03-23 14:28:24.578313224 -0700
+@@ -0,0 +1,631 @@
++/*
++ *  linux/fs/nfsd/nfs4callback.c
++ *
++ *  Copyright (c) 2001 The Regents of the University of Michigan.
++ *  All rights reserved.
++ *
++ *  Kendrick Smith <kmsmith@umich.edu>
++ *  Andy Adamson <andros@umich.edu>
++ *
++ *  Redistribution and use in source and binary forms, with or without
++ *  modification, are permitted provided that the following conditions
++ *  are met:
++ *
++ *  1. Redistributions of source code must retain the above copyright
++ *     notice, this list of conditions and the following disclaimer.
++ *  2. Redistributions in binary form must reproduce the above copyright
++ *     notice, this list of conditions and the following disclaimer in the
++ *     documentation and/or other materials provided with the distribution.
++ *  3. Neither the name of the University nor the names of its
++ *     contributors may be used to endorse or promote products derived
++ *     from this software without specific prior written permission.
++ *
++ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
++ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
++ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
++ *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
++ *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
++ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
++ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
++ *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
++ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
++ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/list.h>
++#include <linux/inet.h>
++#include <linux/errno.h>
++#include <linux/sunrpc/xdr.h>
++#include <linux/sunrpc/svc.h>
++#include <linux/sunrpc/clnt.h>
++#include <linux/nfsd/nfsd.h>
++#include <linux/nfsd/state.h>
++#include <linux/sunrpc/sched.h>
++#include <linux/nfs4.h>
++
++#define NFSDDBG_FACILITY                NFSDDBG_PROC
++
++#define NFSPROC4_CB_NULL 0
++#define NFSPROC4_CB_COMPOUND 1
++
++/* forward declarations */
++static void nfs4_cb_null(struct rpc_task *task);
++
++/* Index of predefined Linux callback client operations */
++
++enum {
++        NFSPROC4_CLNT_CB_NULL = 0,
++        NFSPROC4_CLNT_CB_GETATTR,
++        NFSPROC4_CLNT_CB_RECALL,
++};
++
++enum nfs_cb_opnum4 {
++      OP_CB_GETATTR           = 3,
++      OP_CB_RECALL            = 4,
++      OP_CB_ILLEGAL           = 10044
++};
++
++
++#define NFS4_MAXTAGLEN                20
++
++#define cb_compound_enc_hdr_sz                4
++#define cb_compound_dec_hdr_sz                (3 + (NFS4_MAXTAGLEN >> 2))
++#define op_enc_sz                     1
++#define op_dec_sz                     2
++#define enc_nfs4_fh_sz                        (1 + (NFS4_FHSIZE >> 2))
++#define enc_stateid_sz                        16
++
++#define NFS4_enc_cb_getattr_sz                (cb_compound_enc_hdr_sz +       \
++                                      op_enc_sz  +                    \
++                                      enc_nfs4_fh_sz + 4)
++
++#define NFS4_dec_cb_getattr_sz                (cb_compound_dec_hdr_sz +       \
++                                      op_dec_sz          +            \
++                                      11)
++
++#define NFS4_enc_cb_recall_sz         (cb_compound_enc_hdr_sz +       \
++                                      1 + enc_stateid_sz +            \
++                                      enc_nfs4_fh_sz)
++
++#define NFS4_dec_cb_recall_sz         (cb_compound_dec_hdr_sz  +      \
++                                       op_dec_sz)
++
++/*
++* Generic encode routines from fs/nfs/nfs4xdr.c
++*/
++static inline u32 *
++xdr_writemem(u32 *p, const void *ptr, int nbytes)
++{
++      int tmp = XDR_QUADLEN(nbytes);
++      if (!tmp)
++              return p;
++      p[tmp-1] = 0;
++      memcpy(p, ptr, nbytes);
++      return p + tmp;
++}
++
++#define WRITE32(n)               *p++ = htonl(n)
++#define WRITEMEM(ptr,nbytes)     do {                           \
++        p = xdr_writemem(p, ptr, nbytes);                       \
++} while (0)
++#define RESERVE_SPACE(nbytes)   do {                            \
++      p = xdr_reserve_space(xdr, nbytes);                     \
++      if (!p) dprintk("NFSD: RESERVE_SPACE(%d) failed in function %s\n", (int) (nbytes), __FUNCTION__); \
++      BUG_ON(!p);                                             \
++} while (0)
++
++/*
++ * Generic decode routines from fs/nfs/nfs4xdr.c
++ */
++#define DECODE_TAIL                             \
++      status = 0;                             \
++out:                                            \
++      return status;                          \
++xdr_error:                                      \
++      dprintk("NFSD: xdr error! (%s:%d)\n", __FILE__, __LINE__); \
++      status = -EIO;                          \
++      goto out
++
++#define READ32(x)         (x) = ntohl(*p++)
++#define READ64(x)         do {                  \
++      (x) = (u64)ntohl(*p++) << 32;           \
++      (x) |= ntohl(*p++);                     \
++} while (0)
++#define READTIME(x)       do {                  \
++      p++;                                    \
++      (x.tv_sec) = ntohl(*p++);               \
++      (x.tv_nsec) = ntohl(*p++);              \
++} while (0)
++#define READ_BUF(nbytes)  do { \
++      p = xdr_inline_decode(xdr, nbytes); \
++      if (!p) { \
++              dprintk("NFSD: %s: reply buffer overflowed in line %d.", \
++                        __FUNCTION__, __LINE__); \
++              return -EIO; \
++      } \
++} while (0)
++
++struct nfs4_cb_compound_hdr {
++      int    status;
++      u32    ident;
++      u32    nops;
++      u32    taglen;
++      char * tag;
++};
++
++struct nfs4_cb_getattr {
++      struct nfs_fh   fh;
++      u32             bm0;
++      u32             bm1;
++      __u64           change_attr;
++      __u64           size;
++      struct timespec mtime;
++};
++
++struct nfs4_cb_recall {
++      nfs4_stateid  stateid;
++      int           trunc;
++      struct nfs_fh fh;
++};
++
++static struct {
++        int stat;
++        int errno;
++} nfs_cb_errtbl[] = {
++      { NFS4_OK,              0               },
++      { NFS4ERR_PERM,         EPERM           },
++      { NFS4ERR_NOENT,        ENOENT          },
++      { NFS4ERR_IO,           EIO             },
++      { NFS4ERR_NXIO,         ENXIO           },
++      { NFS4ERR_ACCESS,       EACCES          },
++      { NFS4ERR_EXIST,        EEXIST          },
++      { NFS4ERR_XDEV,         EXDEV           },
++      { NFS4ERR_NOTDIR,       ENOTDIR         },
++      { NFS4ERR_ISDIR,        EISDIR          },
++      { NFS4ERR_INVAL,        EINVAL          },
++      { NFS4ERR_FBIG,         EFBIG           },
++      { NFS4ERR_NOSPC,        ENOSPC          },
++      { NFS4ERR_ROFS,         EROFS           },
++      { NFS4ERR_MLINK,        EMLINK          },
++      { NFS4ERR_NAMETOOLONG,  ENAMETOOLONG    },
++      { NFS4ERR_NOTEMPTY,     ENOTEMPTY       },
++      { NFS4ERR_DQUOT,        EDQUOT          },
++      { NFS4ERR_STALE,        ESTALE          },
++      { NFS4ERR_BADHANDLE,    EBADHANDLE      },
++      { NFS4ERR_BAD_COOKIE,   EBADCOOKIE      },
++      { NFS4ERR_NOTSUPP,      ENOTSUPP        },
++      { NFS4ERR_TOOSMALL,     ETOOSMALL       },
++      { NFS4ERR_SERVERFAULT,  ESERVERFAULT    },
++      { NFS4ERR_BADTYPE,      EBADTYPE        },
++      { NFS4ERR_LOCKED,       EAGAIN          },
++      { NFS4ERR_RESOURCE,     EREMOTEIO       },
++      { NFS4ERR_SYMLINK,      ELOOP           },
++      { NFS4ERR_OP_ILLEGAL,   EOPNOTSUPP      },
++      { NFS4ERR_DEADLOCK,     EDEADLK         },
++      { -1,                   EIO             }
++};
++
++static int
++nfs_cb_stat_to_errno(int stat)
++{
++        int i;
++        for (i = 0; nfs_cb_errtbl[i].stat != -1; i++) {
++                if (nfs_cb_errtbl[i].stat == stat)
++                        return nfs_cb_errtbl[i].errno;
++        }
++        /* If we cannot translate the error, the recovery routines should
++         * handle it.
++         * Note: remaining NFSv4 error codes have values > 10000, so should
++         * not conflict with native Linux error codes.
++         */
++        return stat;
++}
++
++/*
++ * XDR encode
++ */
++
++static int
++encode_cb_compound_hdr(struct xdr_stream *xdr, struct nfs4_cb_compound_hdr *hdr)
++{
++      u32 * p;
++
++      RESERVE_SPACE(16);
++      WRITE32(0);            /* tag length is always 0 */
++      WRITE32(NFS4_MINOR_VERSION);
++      WRITE32(hdr->ident);
++      WRITE32(hdr->nops);
++      return 0;
++}
++
++static int
++encode_cb_getattr(struct xdr_stream *xdr, struct nfs4_cb_getattr *cb_get)
++{
++        u32 *p;
++      int len = cb_get->fh.size;
++
++      RESERVE_SPACE(20 + len);
++      WRITE32(OP_CB_GETATTR);
++      WRITE32(len);
++      WRITEMEM(cb_get->fh.data, len);
++      WRITE32(2);
++      WRITE32(cb_get->bm0);
++      WRITE32(cb_get->bm1);
++      return 0;
++}
++
++static int
++encode_cb_recall(struct xdr_stream *xdr, struct nfs4_cb_recall *cb_rec)
++{
++      u32 *p;
++      int len = cb_rec->fh.size;
++
++        RESERVE_SPACE(8+sizeof(cb_rec->stateid.data));
++        WRITE32(OP_CB_RECALL);
++        WRITEMEM(cb_rec->stateid.data, sizeof(cb_rec->stateid.data));
++      WRITE32(cb_rec->trunc);
++      WRITE32(len);
++      WRITEMEM(cb_rec->fh.data, len);
++      return 0;
++}
++
++static int
++nfs4_xdr_enc_cb_getattr(struct rpc_rqst *req, u32 *p, struct nfs4_cb_getattr *args)
++{
++      struct xdr_stream xdr;
++      struct nfs4_cb_compound_hdr hdr = {
++              .nops   = 1,
++      };
++
++        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
++        encode_cb_compound_hdr(&xdr, &hdr);
++        return (encode_cb_getattr(&xdr, args));
++}
++
++static int
++nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, u32 *p, struct nfs4_cb_recall *args)
++{
++      struct xdr_stream xdr;
++      struct nfs4_cb_compound_hdr hdr = {
++              .nops   = 1,
++      };
++
++        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
++        encode_cb_compound_hdr(&xdr, &hdr);
++        return (encode_cb_recall(&xdr, args));
++}
++
++
++static int
++decode_cb_compound_hdr(struct xdr_stream *xdr, struct nfs4_cb_compound_hdr *hdr){
++        u32 *p;
++
++        READ_BUF(8);
++        READ32(hdr->status);
++        READ32(hdr->taglen);
++        READ_BUF(hdr->taglen + 4);
++        hdr->tag = (char *)p;
++        p += XDR_QUADLEN(hdr->taglen);
++        READ32(hdr->nops);
++        return 0;
++}
++
++static int
++decode_cb_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected)
++{
++      u32 *p;
++      u32 op;
++      int32_t nfserr;
++
++      READ_BUF(8);
++      READ32(op);
++      if (op != expected) {
++              dprintk("NFSD: decode_cb_op_hdr: Callback server returned operation"
++                      " %d but we issued a request for %d\n",
++                      op, expected);
++              return -EIO;
++      }
++      READ32(nfserr);
++      if (nfserr != NFS_OK)
++              return -nfs_cb_stat_to_errno(nfserr);
++      return 0;
++}
++
++static int
++decode_cb_getattr(struct xdr_stream *xdr, struct nfs4_cb_getattr *cb_get)
++{
++      int status;
++      u32 bmlen,
++              attrlen =0,
++              bmval0 =0,
++              bmval1 =0,
++              len = 0;
++      u32 *p;
++
++      status = decode_cb_op_hdr(xdr, OP_CB_GETATTR);
++      if (status)
++              return status;
++      READ_BUF(4);
++      READ32(bmlen);
++      if( (bmlen < 1) || (bmlen > 2))
++              goto xdr_error;
++      READ_BUF((bmlen << 2) + 4);
++      READ32(bmval0);
++      if (bmval0 & ~(FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE))
++              goto out_bad_bitmap;
++      if (bmlen == 2) {
++              READ32(bmval1);
++              if (bmval1 & ~ FATTR4_WORD1_TIME_MODIFY)
++                      goto out_bad_bitmap;
++      }
++      READ32(attrlen);
++      if (bmval0 & FATTR4_WORD0_CHANGE) {
++              READ_BUF(8);
++              len += 8;
++              READ64(cb_get->change_attr);
++              dprintk("decode_cb_getattr: changeid=%Ld\n",
++                                   (long long)cb_get->change_attr);
++      }
++      if (bmval0 & FATTR4_WORD0_SIZE) {
++              READ_BUF(8);
++              len += 8;
++              READ64(cb_get->size);
++              dprintk("decode_cb_getattr: size=%Ld\n",
++                                   (long long)cb_get->size);
++      }
++      if (bmval1 & FATTR4_WORD1_TIME_MODIFY) {
++              READ_BUF(12);
++              len += 12;
++              READTIME(cb_get->mtime);
++              dprintk("decode_cb_gatattr: mtime=%ld\n",
++                                    (long)cb_get->mtime.tv_sec);
++      }
++      if (len != attrlen)
++              goto xdr_error;
++
++      DECODE_TAIL;
++
++out_bad_bitmap:
++        dprintk("NFSD: %s Callback server returned bad attribute bitmap\n",
++               __FUNCTION__);
++        return -EIO;
++
++}
++
++static int
++nfs4_xdr_dec_cb_getattr(struct rpc_rqst *rqstp, u32 *p, struct nfs4_cb_getattr *res)
++{
++      struct xdr_stream xdr;
++      struct nfs4_cb_compound_hdr hdr;
++      int status;
++
++      xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
++      status = decode_cb_compound_hdr(&xdr, &hdr);
++      if (status)
++              goto out;
++      status = decode_cb_getattr(&xdr, res);
++out:
++      return status;
++}
++
++static int
++nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp, u32 *p)
++{
++      struct xdr_stream xdr;
++      struct nfs4_cb_compound_hdr hdr;
++      int status;
++
++      xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
++      status = decode_cb_compound_hdr(&xdr, &hdr);
++      if (status)
++              goto out;
++      status = decode_cb_op_hdr(&xdr, OP_CB_RECALL);
++out:
++      return status;
++}
++
++static int
++nfs4_xdr_enc_null(struct rpc_rqst *req, u32 *p)
++{
++      struct xdr_stream xdrs, *xdr = &xdrs;
++
++      xdr_init_encode(&xdrs, &req->rq_snd_buf, p);
++        RESERVE_SPACE(0);
++      return 0;
++}
++
++static int
++nfs4_xdr_dec_null(struct rpc_rqst *req, u32 *p)
++{
++      return 0;
++}
++
++/*
++ * RPC procedure tables
++ */
++#ifndef MAX
++# define MAX(a, b)      (((a) > (b))? (a) : (b))
++#endif
++
++#define PROC(proc, argtype, restype)                                  \
++[NFSPROC4_CLNT_##proc] = {                                            \
++        .p_proc   = NFSPROC4_CB_COMPOUND,                             \
++        .p_encode = (kxdrproc_t) nfs4_xdr_##argtype,                  \
++        .p_decode = (kxdrproc_t) nfs4_xdr_##restype,                  \
++        .p_bufsiz = MAX(NFS4_##argtype##_sz,NFS4_##restype##_sz) << 2,  \
++}
++
++struct rpc_procinfo     nfs4_cb_procedures[] = {
++  PROC(CB_GETATTR,      enc_cb_getattr,     dec_cb_getattr),
++  PROC(CB_RECALL,       enc_cb_recall,      dec_cb_recall),
++};
++
++struct rpc_version              nfs_cb_version4 = {
++        .number                 = 1,
++        .nrprocs                = sizeof(nfs4_cb_procedures)/sizeof(nfs4_cb_procedures[0]),
++        .procs                  = nfs4_cb_procedures
++};
++
++static struct rpc_version *   nfs_cb_version[] = {
++      NULL,
++      &nfs_cb_version4,
++};
++
++struct rpc_procinfo  nfs4_cb_null_proc= {
++      .p_proc = NFSPROC4_CB_NULL,
++      .p_encode = (kxdrproc_t)nfs4_xdr_enc_null,
++        .p_decode = (kxdrproc_t) nfs4_xdr_dec_null,
++        .p_bufsiz = 0,
++};
++
++/*
++ * Use the SETCLIENTID credential
++ */
++struct rpc_cred *
++nfsd4_lookupcred(struct nfs4_client *clp, int taskflags)
++{
++        struct auth_cred acred;
++      struct rpc_clnt *clnt = clp->cl_callback.cb_client;
++        struct rpc_cred *ret = NULL;
++
++      if (!clnt)
++              goto out;
++        get_group_info(clp->cl_cred.cr_group_info);
++        acred.uid = clp->cl_cred.cr_uid;
++        acred.gid = clp->cl_cred.cr_gid;
++        acred.group_info = clp->cl_cred.cr_group_info;
++
++        dprintk("NFSD:     looking up %s cred\n",
++                clnt->cl_auth->au_ops->au_name);
++        ret = rpcauth_lookup_credcache(clnt->cl_auth, &acred, taskflags);
++        put_group_info(clp->cl_cred.cr_group_info);
++out:
++        return ret;
++}
++
++/*
++ * Set up the callback client and put a NFSPROC4_CB_NULL on the wire...
++ */
++void
++nfsd4_probe_callback(struct nfs4_client *clp)
++{
++      struct sockaddr_in      addr;
++      struct nfs4_callback    *cb = &clp->cl_callback;
++      struct rpc_timeout      timeparms;
++      struct rpc_xprt *       xprt;
++      struct rpc_program *    program = &cb->cb_program;
++      struct rpc_stat *       stat = &cb->cb_stat;
++      struct rpc_clnt *       clnt;
++        struct rpc_message msg = {
++                .rpc_proc       = &nfs4_cb_null_proc,
++                .rpc_argp       = clp,
++        };
++      char                    hostname[32];
++      int status;
++
++      dprintk("NFSD: probe_callback. cb_parsed %d cb_set %d 1\n",
++               cb->cb_parsed, cb->cb_set);
++      if (!cb->cb_parsed || cb->cb_set)
++              goto out_err;
++
++      /* Currently, we only support tcp for the callback channel */
++      if (cb->cb_netid.len !=3 || memcmp((char *)cb->cb_netid.data, "tcp", 3))
++              goto out_err;
++
++      /* Initialize address */
++      memset(&addr, 0, sizeof(addr));
++      addr.sin_family = AF_INET;
++      addr.sin_port = htons(cb->cb_port);
++      addr.sin_addr.s_addr = htonl(cb->cb_addr);
++
++      /* Initialize timeout */
++      timeparms.to_initval = HZ;
++      timeparms.to_retries = 5;
++      timeparms.to_maxval = NFSD_LEASE_TIME*HZ;
++      timeparms.to_exponential = 1;
++
++      /* Create RPC transport */
++      if (!(xprt = xprt_create_proto(IPPROTO_TCP, &addr, &timeparms))) {
++              dprintk("NFSD: couldn't create callback transport!\n");
++              goto out_err;
++      }
++
++      /* Initialize rpc_program */
++      program->name = "nfs4_cb";
++      program->number = cb->cb_prog;
++      program->nrvers = sizeof(nfs_cb_version)/sizeof(nfs_cb_version[0]);
++      program->version = nfs_cb_version;
++      program->stats = stat;
++
++      /* Initialize rpc_stat */
++      memset(stat, 0, sizeof(struct rpc_stat));
++      stat->program = program;
++
++      /* Create RPC client
++       *
++       * XXX AUTH_UNIX only - need AUTH_GSS....
++       */
++      sprintf(hostname, "%u.%u.%u.%u", NIPQUAD(addr.sin_addr.s_addr));
++      if (!(clnt = rpc_create_client(xprt, hostname, program, 1, RPC_AUTH_UNIX))) {
++              dprintk("NFSD: couldn't create callback client\n");
++              goto out_xprt;
++      }
++      clnt->cl_intr = 1;
++      clnt->cl_softrtry = 1;
++      clnt->cl_chatty = 1;
++      cb->cb_client = clnt;
++
++      /* Kick rpciod, put the call on the wire. */
++
++      if (rpciod_up() != 0) {
++              dprintk("nfsd: couldn't start rpciod for callbacks!\n");
++              goto out_clnt;
++      }
++
++      /* the task holds a reference to the nfs4_client struct */
++      atomic_inc(&clp->cl_count);
++
++      msg.rpc_cred = nfsd4_lookupcred(clp,0);
++      status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, nfs4_cb_null, 0);
++
++      if (status != 0) {
++              dprintk("NFSD: asynchronous NFSPROC4_CB_NULL failed!\n");
++              goto out_rpciod;
++      }
++      return;
++
++out_rpciod:
++      rpciod_down();
++out_clnt:
++      rpc_shutdown_client(clnt);
++      goto out_err;
++out_xprt:
++      xprt_destroy(xprt);
++out_err:
++      dprintk("NFSD: warning: no callback path to client %.*s\n",
++              clp->cl_name.len, clp->cl_name.data);
++      cb->cb_client = NULL;
++}
++
++static void
++nfs4_cb_null(struct rpc_task *task)
++{
++      struct nfs4_client *clp = (struct nfs4_client *)task->tk_msg.rpc_argp;
++      struct nfs4_callback *cb = &clp->cl_callback;
++      u32 addr = htonl(cb->cb_addr);
++
++      dprintk("NFSD: nfs4_cb_null task->tk_status %d\n", task->tk_status);
++
++      if (task->tk_status < 0) {
++              dprintk("NFSD: callback establishment to client %.*s failed\n",
++                      clp->cl_name.len, clp->cl_name.data);
++              goto out;
++      }
++      cb->cb_set = 1;
++      dprintk("NFSD: callback set to client %u.%u.%u.%u\n", NIPQUAD(addr));
++out:
++      put_nfs4_client(clp);
++}
+--- linux-2.6.7/fs/nfsd/nfs4xdr.c.lsec 2004-06-15 23:19:52.000000000 -0600
++++ linux-2.6.7/fs/nfsd/nfs4xdr.c      2005-03-23 14:28:23.924412632 -0700
+@@ -55,6 +55,8 @@
+ #include <linux/nfsd/state.h>
+ #include <linux/nfsd/xdr4.h>
+ #include <linux/nfsd_idmap.h>
++#include <linux/nfs4.h>
++#include <linux/nfs4_acl.h>
+ #define NFSDDBG_FACILITY              NFSDDBG_XDR
+@@ -287,27 +289,40 @@ u32 *read_buf(struct nfsd4_compoundargs 
+       return p;
+ }
+-char *savemem(struct nfsd4_compoundargs *argp, u32 *p, int nbytes)
++static int
++defer_free(struct nfsd4_compoundargs *argp,
++              void (*release)(const void *), void *p)
+ {
+       struct tmpbuf *tb;
++
++      tb = kmalloc(sizeof(*tb), GFP_KERNEL);
++      if (!tb)
++              return -ENOMEM;
++      tb->buf = p;
++      tb->release = release;
++      tb->next = argp->to_free;
++      argp->to_free = tb;
++      return 0;
++}
++
++char *savemem(struct nfsd4_compoundargs *argp, u32 *p, int nbytes)
++{
++      void *new = NULL;
+       if (p == argp->tmp) {
+-              p = kmalloc(nbytes, GFP_KERNEL);
+-              if (!p) return NULL;
++              new = kmalloc(nbytes, GFP_KERNEL);
++              if (!new) return NULL;
++              p = new;
+               memcpy(p, argp->tmp, nbytes);
+       } else {
+               if (p != argp->tmpp)
+                       BUG();
+               argp->tmpp = NULL;
+       }
+-      tb = kmalloc(sizeof(*tb), GFP_KERNEL);
+-      if (!tb) {
+-              kfree(p);
++      if (defer_free(argp, kfree, p)) {
++              kfree(new);
+               return NULL;
+-      }
+-      tb->buf = p;
+-      tb->next = argp->to_free;
+-      argp->to_free = tb;
+-      return (char*)p;
++      } else
++              return (char *)p;
+ }
+@@ -335,7 +350,8 @@ nfsd4_decode_bitmap(struct nfsd4_compoun
+ }
+ static int
+-nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, struct iattr *iattr)
++nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, struct iattr *iattr,
++    struct nfs4_acl **acl)
+ {
+       int expected_len, len = 0;
+       u32 dummy32;
+@@ -364,6 +380,51 @@ nfsd4_decode_fattr(struct nfsd4_compound
+               READ64(iattr->ia_size);
+               iattr->ia_valid |= ATTR_SIZE;
+       }
++      if (bmval[0] & FATTR4_WORD0_ACL) {
++              int nace, i;
++              struct nfs4_ace ace;
++
++              READ_BUF(4); len += 4;
++              READ32(nace);
++
++              *acl = nfs4_acl_new();
++              if (*acl == NULL) {
++                      status = -ENOMEM;
++                      goto out_nfserr;
++              }
++              defer_free(argp, (void (*)(const void *))nfs4_acl_free, *acl);
++
++              for (i = 0; i < nace; i++) {
++                      READ_BUF(16); len += 16;
++                      READ32(ace.type);
++                      READ32(ace.flag);
++                      READ32(ace.access_mask);
++                      READ32(dummy32);
++                      READ_BUF(dummy32);
++                      len += XDR_QUADLEN(dummy32) << 2;
++                      READMEM(buf, dummy32);
++                      if (check_utf8(buf, dummy32))
++                              return nfserr_inval;
++                      ace.whotype = nfs4_acl_get_whotype(buf, dummy32);
++                      status = 0;
++                      if (ace.whotype != NFS4_ACL_WHO_NAMED)
++                              ace.who = 0;
++                      else if (ace.flag & NFS4_ACE_IDENTIFIER_GROUP)
++                              status = nfsd_map_name_to_gid(argp->rqstp,
++                                              buf, dummy32, &ace.who);
++                      else
++                              status = nfsd_map_name_to_uid(argp->rqstp,
++                                              buf, dummy32, &ace.who);
++                      if (status)
++                              goto out_nfserr;
++                      if (nfs4_acl_add_ace(*acl, ace.type, ace.flag,
++                               ace.access_mask, ace.whotype, ace.who) != 0) {
++                              status = -ENOMEM;
++                              goto out_nfserr;
++                      }
++              }
++      } else
++              *acl = NULL;
+       if (bmval[1] & FATTR4_WORD1_MODE) {
+               READ_BUF(4);
+               len += 4;
+@@ -549,7 +610,7 @@ nfsd4_decode_create(struct nfsd4_compoun
+       if ((status = check_filename(create->cr_name, create->cr_namelen, nfserr_inval)))
+               return status;
+-      if ((status = nfsd4_decode_fattr(argp, create->cr_bmval, &create->cr_iattr)))
++      if ((status = nfsd4_decode_fattr(argp, create->cr_bmval, &create->cr_iattr, &create->cr_acl)))
+               goto out;
+       DECODE_TAIL;
+@@ -698,7 +759,7 @@ nfsd4_decode_open(struct nfsd4_compounda
+               switch (open->op_createmode) {
+               case NFS4_CREATE_UNCHECKED:
+               case NFS4_CREATE_GUARDED:
+-                      if ((status = nfsd4_decode_fattr(argp, open->op_bmval, &open->op_iattr)))
++                      if ((status = nfsd4_decode_fattr(argp, open->op_bmval, &open->op_iattr, &open->op_acl)))
+                               goto out;
+                       break;
+               case NFS4_CREATE_EXCLUSIVE:
+@@ -875,7 +936,7 @@ nfsd4_decode_setattr(struct nfsd4_compou
+       READ_BUF(sizeof(stateid_t));
+       READ32(setattr->sa_stateid.si_generation);
+       COPYMEM(&setattr->sa_stateid.si_opaque, sizeof(stateid_opaque_t));
+-      if ((status = nfsd4_decode_fattr(argp, setattr->sa_bmval, &setattr->sa_iattr)))
++      if ((status = nfsd4_decode_fattr(argp, setattr->sa_bmval, &setattr->sa_iattr, &setattr->sa_acl)))
+               goto out;
+       DECODE_TAIL;
+@@ -1288,32 +1349,24 @@ static u32 nfs4_ftypes[16] = {
+         NF4SOCK, NF4BAD,  NF4LNK, NF4BAD,
+ };
+-static inline int
+-xdr_padding(int l)
+-{
+-       return 3 - ((l - 1) & 3); /* smallest i>=0 such that (l+i)%4 = 0 */
+-}
+-
+ static int
+-nfsd4_encode_name(struct svc_rqst *rqstp, int group, uid_t id,
++nfsd4_encode_name(struct svc_rqst *rqstp, int whotype, uid_t id, int group,
+                       u32 **p, int *buflen)
+ {
+       int status;
+-      u32 len;
+       if (*buflen < (XDR_QUADLEN(IDMAP_NAMESZ) << 2) + 4)
+               return nfserr_resource;
+-      if (group)
++      if (whotype != NFS4_ACL_WHO_NAMED)
++              status = nfs4_acl_write_who(whotype, (u8 *)(*p + 1));
++      else if (group)
+               status = nfsd_map_gid_to_name(rqstp, id, (u8 *)(*p + 1));
+       else
+               status = nfsd_map_uid_to_name(rqstp, id, (u8 *)(*p + 1));
+       if (status < 0)
+               return nfserrno(status);
+-      len = (unsigned)status;
+-      *(*p)++ = htonl(len);
+-      memset((u8 *)*p + len, 0, xdr_padding(len));
+-      *p += XDR_QUADLEN(len);
+-      *buflen -= (XDR_QUADLEN(len) << 2) + 4;
++      *p = xdr_encode_opaque(*p, NULL, status);
++      *buflen -= (XDR_QUADLEN(status) << 2) + 4;
+       BUG_ON(*buflen < 0);
+       return 0;
+ }
+@@ -1321,13 +1374,20 @@ nfsd4_encode_name(struct svc_rqst *rqstp
+ static inline int
+ nfsd4_encode_user(struct svc_rqst *rqstp, uid_t uid, u32 **p, int *buflen)
+ {
+-      return nfsd4_encode_name(rqstp, uid, 0, p, buflen);
++      return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, uid, 0, p, buflen);
+ }
+ static inline int
+ nfsd4_encode_group(struct svc_rqst *rqstp, uid_t gid, u32 **p, int *buflen)
+ {
+-      return nfsd4_encode_name(rqstp, gid, 1, p, buflen);
++      return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, gid, 1, p, buflen);
++}
++
++static inline int
++nfsd4_encode_aclname(struct svc_rqst *rqstp, int whotype, uid_t id, int group,
++              u32 **p, int *buflen)
++{
++      return nfsd4_encode_name(rqstp, whotype, id, group, p, buflen);
+ }
+@@ -1354,6 +1414,8 @@ nfsd4_encode_fattr(struct svc_fh *fhp, s
+       u64 dummy64;
+       u32 *p = buffer;
+       int status;
++      int aclsupport = 0;
++      struct nfs4_acl *acl = NULL;
+       BUG_ON(bmval1 & NFSD_WRITEONLY_ATTRS_WORD1);
+       BUG_ON(bmval0 & ~NFSD_SUPPORTED_ATTRS_WORD0);
+@@ -1376,6 +1438,17 @@ nfsd4_encode_fattr(struct svc_fh *fhp, s
+                       goto out;
+               fhp = &tempfh;
+       }
++      if (bmval0 & (FATTR4_WORD0_ACL | FATTR4_WORD0_ACLSUPPORT
++                      | FATTR4_WORD0_SUPPORTED_ATTRS)) {
++              status = nfsd4_get_nfs4_acl(rqstp, dentry, &acl);
++              aclsupport = (status == 0);
++              if (bmval0 & FATTR4_WORD0_ACL) {
++                      if (status == -EOPNOTSUPP)
++                              bmval0 &= ~FATTR4_WORD0_ACL;
++                      else if (status != 0)
++                              goto out_nfserr;
++              }
++      }
+       if ((buflen -= 16) < 0)
+               goto out_resource;
+@@ -1388,7 +1461,9 @@ nfsd4_encode_fattr(struct svc_fh *fhp, s
+               if ((buflen -= 12) < 0)
+                       goto out_resource;
+               WRITE32(2);
+-              WRITE32(NFSD_SUPPORTED_ATTRS_WORD0);
++              WRITE32(aclsupport ?
++                      NFSD_SUPPORTED_ATTRS_WORD0 :
++                      NFSD_SUPPORTED_ATTRS_WORD0 & ~FATTR4_WORD0_ACL);
+               WRITE32(NFSD_SUPPORTED_ATTRS_WORD1);
+       }
+       if (bmval0 & FATTR4_WORD0_TYPE) {
+@@ -1459,10 +1534,44 @@ nfsd4_encode_fattr(struct svc_fh *fhp, s
+                       goto out_resource;
+               WRITE32(0);
+       }
++      if (bmval0 & FATTR4_WORD0_ACL) {
++              struct nfs4_ace *ace;
++              struct list_head *h;
++
++              if (acl == NULL) {
++                      if ((buflen -= 4) < 0)
++                              goto out_resource;
++
++                      WRITE32(0);
++                      goto out_acl;
++              }
++              if ((buflen -= 4) < 0)
++                      goto out_resource;
++              WRITE32(acl->naces);
++
++              list_for_each(h, &acl->ace_head) {
++                      ace = list_entry(h, struct nfs4_ace, l_ace);
++
++                      if ((buflen -= 4*3) < 0)
++                              goto out_resource;
++                      WRITE32(ace->type);
++                      WRITE32(ace->flag);
++                      WRITE32(ace->access_mask & NFS4_ACE_MASK_ALL);
++                      status = nfsd4_encode_aclname(rqstp, ace->whotype,
++                              ace->who, ace->flag & NFS4_ACE_IDENTIFIER_GROUP,
++                              &p, &buflen);
++                      if (status == nfserr_resource)
++                              goto out_resource;
++                      if (status)
++                              goto out;
++              }
++      }
++out_acl:
+       if (bmval0 & FATTR4_WORD0_ACLSUPPORT) {
+               if ((buflen -= 4) < 0)
+                       goto out_resource;
+-              WRITE32(0);
++              WRITE32(aclsupport ?
++                      ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL : 0);
+       }
+       if (bmval0 & FATTR4_WORD0_CANSETTIME) {
+               if ((buflen -= 4) < 0)
+@@ -1645,6 +1754,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, s
+       status = nfs_ok;
+ out:
++      nfs4_acl_free(acl);
+       if (fhp == &tempfh)
+               fh_put(&tempfh);
+       return status;
+@@ -2471,6 +2581,24 @@ nfs4svc_encode_voidres(struct svc_rqst *
+         return xdr_ressize_check(rqstp, p);
+ }
++void nfsd4_release_compoundargs(struct nfsd4_compoundargs *args)
++{
++      if (args->ops != args->iops) {
++              kfree(args->ops);
++              args->ops = args->iops;
++      }
++      if (args->tmpp) {
++              kfree(args->tmpp);
++              args->tmpp = NULL;
++      }
++      while (args->to_free) {
++              struct tmpbuf *tb = args->to_free;
++              args->to_free = tb->next;
++              tb->release(tb->buf);
++              kfree(tb);
++      }
++}
++
+ int
+ nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, u32 *p, struct nfsd4_compoundargs *args)
+ {
+@@ -2487,20 +2615,7 @@ nfs4svc_decode_compoundargs(struct svc_r
+       status = nfsd4_decode_compound(args);
+       if (status) {
+-              if (args->ops != args->iops) {
+-                      kfree(args->ops);
+-                      args->ops = args->iops;
+-              }
+-              if (args->tmpp) {
+-                      kfree(args->tmpp);
+-                      args->tmpp = NULL;
+-              }
+-              while (args->to_free) {
+-                      struct tmpbuf *tb = args->to_free;
+-                      args->to_free = tb->next;
+-                      kfree(tb->buf);
+-                      kfree(tb);
+-              }
++              nfsd4_release_compoundargs(args);
+       }
+       return !status;
+ }
+--- linux-2.6.7/fs/nfsd/nfs4proc.c.lsec        2004-06-15 23:20:26.000000000 -0600
++++ linux-2.6.7/fs/nfsd/nfs4proc.c     2005-03-23 14:28:24.080388920 -0700
+@@ -52,6 +52,7 @@
+ #include <linux/nfs4.h>
+ #include <linux/nfsd/state.h>
+ #include <linux/nfsd/xdr4.h>
++#include <linux/nfs4_acl.h>
+ #define NFSDDBG_FACILITY              NFSDDBG_PROC
+@@ -135,9 +136,11 @@ do_open_fhandle(struct svc_rqst *rqstp, 
+ {
+       int status;
+-      dprintk("NFSD: do_open_fhandle\n");
++      /* Only reclaims from previously confirmed clients are valid */
++      if ((status = nfs4_check_open_reclaim(&open->op_clientid)))
++              return status;
+-      /* we don't know the target directory, and therefore can not
++      /* We don't know the target directory, and therefore can not
+       * set the change info
+       */
+@@ -172,8 +175,7 @@ nfsd4_open(struct svc_rqst *rqstp, struc
+       if (nfs4_in_grace() && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS)
+               return nfserr_grace;
+-      if (nfs4_in_no_grace() &&
+-                         open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS)
++      if (!nfs4_in_grace() && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS)
+               return nfserr_no_grace;
+       /* This check required by spec. */
+@@ -318,7 +320,7 @@ nfsd4_commit(struct svc_rqst *rqstp, str
+       return status;
+ }
+-static inline int
++static int
+ nfsd4_create(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_create *create)
+ {
+       struct svc_fh resfh;
+@@ -435,7 +437,7 @@ nfsd4_link(struct svc_rqst *rqstp, struc
+       return status;
+ }
+-static inline int
++static int
+ nfsd4_lookupp(struct svc_rqst *rqstp, struct svc_fh *current_fh)
+ {
+       struct svc_fh tmp_fh;
+@@ -619,7 +621,7 @@ nfsd4_setattr(struct svc_rqst *rqstp, st
+               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();
+@@ -627,17 +629,25 @@ 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 (!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));
++      status = nfs_ok;
++      if (setattr->sa_acl != NULL)
++              status = nfsd4_set_nfs4_acl(rqstp, current_fh, setattr->sa_acl);
++      if (status)
++              goto out;
++      status = nfsd_setattr(rqstp, current_fh, &setattr->sa_iattr,
++                              0, (time_t)0);
+ out:
++      return status;
++out_unlock:
+       nfs4_unlock_state();
+       return status;
+ }
+@@ -773,13 +783,20 @@ nfsd4_proc_compound(struct svc_rqst *rqs
+                   struct nfsd4_compoundres *resp)
+ {
+       struct nfsd4_op *op;
+-      struct svc_fh   current_fh;
+-      struct svc_fh   save_fh;
++      struct svc_fh   *current_fh = NULL;
++      struct svc_fh   *save_fh = NULL;
+       int             slack_space;    /* in words, not bytes! */
+       int             status;
+-      fh_init(&current_fh, NFS4_FHSIZE);
+-      fh_init(&save_fh, NFS4_FHSIZE);
++      status = nfserr_resource;
++      current_fh = kmalloc(sizeof(*current_fh), GFP_KERNEL);
++      if (current_fh == NULL)
++              goto out;
++      fh_init(current_fh, NFS4_FHSIZE);
++      save_fh = kmalloc(sizeof(*save_fh), GFP_KERNEL);
++      if (save_fh == NULL)
++              goto out;
++      fh_init(save_fh, NFS4_FHSIZE);
+       resp->xbuf = &rqstp->rq_res;
+       resp->p = rqstp->rq_res.head[0].iov_base + rqstp->rq_res.head[0].iov_len;
+@@ -831,7 +848,7 @@ nfsd4_proc_compound(struct svc_rqst *rqs
+               * SETATTR NOFILEHANDLE error handled in nfsd4_setattr
+               * due to required returned bitmap argument
+               */
+-              if ((!current_fh.fh_dentry) &&
++              if ((!current_fh->fh_dentry) &&
+                  !((op->opnum == OP_PUTFH) || (op->opnum == OP_PUTROOTFH) ||
+                  (op->opnum == OP_SETCLIENTID) ||
+                  (op->opnum == OP_SETCLIENTID_CONFIRM) ||
+@@ -843,105 +860,105 @@ nfsd4_proc_compound(struct svc_rqst *rqs
+               }
+               switch (op->opnum) {
+               case OP_ACCESS:
+-                      op->status = nfsd4_access(rqstp, &current_fh, &op->u.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->status = nfsd4_close(rqstp, current_fh, &op->u.close);
+                       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);
++                      op->status = nfsd4_commit(rqstp, current_fh, &op->u.commit);
+                       break;
+               case OP_CREATE:
+-                      op->status = nfsd4_create(rqstp, &current_fh, &op->u.create);
++                      op->status = nfsd4_create(rqstp, current_fh, &op->u.create);
+                       break;
+               case OP_GETATTR:
+-                      op->status = nfsd4_getattr(rqstp, &current_fh, &op->u.getattr);
++                      op->status = nfsd4_getattr(rqstp, current_fh, &op->u.getattr);
+                       break;
+               case OP_GETFH:
+-                      op->status = nfsd4_getfh(&current_fh, &op->u.getfh);
++                      op->status = nfsd4_getfh(current_fh, &op->u.getfh);
+                       break;
+               case OP_LINK:
+-                      op->status = nfsd4_link(rqstp, &current_fh, &save_fh, &op->u.link);
++                      op->status = nfsd4_link(rqstp, current_fh, save_fh, &op->u.link);
+                       break;
+               case OP_LOCK:
+-                      op->status = nfsd4_lock(rqstp, &current_fh, &op->u.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);
++                      op->status = nfsd4_lockt(rqstp, current_fh, &op->u.lockt);
+                       break;
+               case OP_LOCKU:
+-                      op->status = nfsd4_locku(rqstp, &current_fh, &op->u.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);
++                      op->status = nfsd4_lookup(rqstp, current_fh, &op->u.lookup);
+                       break;
+               case OP_LOOKUPP:
+-                      op->status = nfsd4_lookupp(rqstp, &current_fh);
++                      op->status = nfsd4_lookupp(rqstp, current_fh);
+                       break;
+               case OP_NVERIFY:
+-                      op->status = nfsd4_verify(rqstp, &current_fh, &op->u.nverify);
++                      op->status = nfsd4_verify(rqstp, current_fh, &op->u.nverify);
+                       if (op->status == nfserr_not_same)
+                               op->status = nfs_ok;
+                       break;
+               case OP_OPEN:
+-                      op->status = nfsd4_open(rqstp, &current_fh, &op->u.open);
++                      op->status = nfsd4_open(rqstp, current_fh, &op->u.open);
+                       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->status = nfsd4_open_confirm(rqstp, current_fh, &op->u.open_confirm);
+                       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->status = nfsd4_open_downgrade(rqstp, current_fh, &op->u.open_downgrade);
+                       if (op->u.open_downgrade.od_stateowner)
+                               op->replay =
+                                       &op->u.open_downgrade.od_stateowner->so_replay;
+                       break;
+               case OP_PUTFH:
+-                      op->status = nfsd4_putfh(rqstp, &current_fh, &op->u.putfh);
++                      op->status = nfsd4_putfh(rqstp, current_fh, &op->u.putfh);
+                       break;
+               case OP_PUTROOTFH:
+-                      op->status = nfsd4_putrootfh(rqstp, &current_fh);
++                      op->status = nfsd4_putrootfh(rqstp, current_fh);
+                       break;
+               case OP_READ:
+-                      op->status = nfsd4_read(rqstp, &current_fh, &op->u.read);
++                      op->status = nfsd4_read(rqstp, current_fh, &op->u.read);
+                       break;
+               case OP_READDIR:
+-                      op->status = nfsd4_readdir(rqstp, &current_fh, &op->u.readdir);
++                      op->status = nfsd4_readdir(rqstp, current_fh, &op->u.readdir);
+                       break;
+               case OP_READLINK:
+-                      op->status = nfsd4_readlink(rqstp, &current_fh, &op->u.readlink);
++                      op->status = nfsd4_readlink(rqstp, current_fh, &op->u.readlink);
+                       break;
+               case OP_REMOVE:
+-                      op->status = nfsd4_remove(rqstp, &current_fh, &op->u.remove);
++                      op->status = nfsd4_remove(rqstp, current_fh, &op->u.remove);
+                       break;
+               case OP_RENAME:
+-                      op->status = nfsd4_rename(rqstp, &current_fh, &save_fh, &op->u.rename);
++                      op->status = nfsd4_rename(rqstp, current_fh, save_fh, &op->u.rename);
+                       break;
+               case OP_RENEW:
+                       op->status = nfsd4_renew(&op->u.renew);
+                       break;
+               case OP_RESTOREFH:
+-                      op->status = nfsd4_restorefh(&current_fh, &save_fh);
++                      op->status = nfsd4_restorefh(current_fh, save_fh);
+                       break;
+               case OP_SAVEFH:
+-                      op->status = nfsd4_savefh(&current_fh, &save_fh);
++                      op->status = nfsd4_savefh(current_fh, save_fh);
+                       break;
+               case OP_SETATTR:
+-                      op->status = nfsd4_setattr(rqstp, &current_fh, &op->u.setattr);
++                      op->status = nfsd4_setattr(rqstp, current_fh, &op->u.setattr);
+                       break;
+               case OP_SETCLIENTID:
+                       op->status = nfsd4_setclientid(rqstp, &op->u.setclientid);
+@@ -950,12 +967,12 @@ nfsd4_proc_compound(struct svc_rqst *rqs
+                       op->status = nfsd4_setclientid_confirm(rqstp, &op->u.setclientid_confirm);
+                       break;
+               case OP_VERIFY:
+-                      op->status = nfsd4_verify(rqstp, &current_fh, &op->u.verify);
++                      op->status = nfsd4_verify(rqstp, current_fh, &op->u.verify);
+                       if (op->status == nfserr_same)
+                               op->status = nfs_ok;
+                       break;
+               case OP_WRITE:
+-                      op->status = nfsd4_write(rqstp, &current_fh, &op->u.write);
++                      op->status = nfsd4_write(rqstp, current_fh, &op->u.write);
+                       break;
+               case OP_RELEASE_LOCKOWNER:
+                       op->status = nfsd4_release_lockowner(rqstp, &op->u.release_lockowner);
+@@ -976,22 +993,13 @@ encode_op:
+       }
+ out:
+-      if (args->ops != args->iops) {
+-              kfree(args->ops);
+-              args->ops = args->iops;
+-      }
+-      if (args->tmpp) {
+-              kfree(args->tmpp);
+-              args->tmpp = NULL;
+-      }
+-      while (args->to_free) {
+-              struct tmpbuf *tb = args->to_free;
+-              args->to_free = tb->next;
+-              kfree(tb->buf);
+-              kfree(tb);
+-      }
+-      fh_put(&current_fh);
+-      fh_put(&save_fh);
++      nfsd4_release_compoundargs(args);
++      if (current_fh)
++              fh_put(current_fh);
++      kfree(current_fh);
++      if (save_fh)
++              fh_put(save_fh);
++      kfree(save_fh);
+       return status;
+ }
+--- linux-2.6.7/fs/nfsd/nfs4state.c.lsec       2004-06-15 23:19:43.000000000 -0600
++++ linux-2.6.7/fs/nfsd/nfs4state.c    2005-03-23 14:28:24.028396824 -0700
+@@ -51,6 +51,9 @@
+ #define NFSDDBG_FACILITY                NFSDDBG_PROC
+ /* Globals */
++static time_t lease_time = 90;     /* default lease time */
++static time_t old_lease_time = 90; /* past incarnation lease time */
++static u32 nfs4_reclaim_init = 0;
+ time_t boot_time;
+ static time_t grace_end = 0;
+ static u32 current_clientid = 1;
+@@ -82,7 +85,7 @@ struct nfs4_stateid * find_stateid(state
+  *    protects clientid_hashtbl[], clientstr_hashtbl[],
+  *    unconfstr_hashtbl[], uncofid_hashtbl[].
+  */
+-static struct semaphore client_sema;
++static DECLARE_MUTEX(client_sema);
+ void
+ nfs4_lock_state(void)
+@@ -131,8 +134,11 @@ static void release_file(struct nfs4_fil
+       ((id) & CLIENT_HASH_MASK)
+ #define clientstr_hashval(name, namelen) \
+       (opaque_hashval((name), (namelen)) & CLIENT_HASH_MASK)
+-
+-/* conf_id_hashtbl[], and conf_str_hashtbl[] hold confirmed
++/*
++ * reclaim_str_hashtbl[] holds known client info from previous reset/reboot
++ * used in reboot/reset lease grace period processing
++ *
++ * conf_id_hashtbl[], and conf_str_hashtbl[] hold confirmed
+  * setclientid_confirmed info. 
+  *
+  * unconf_str_hastbl[] and unconf_id_hashtbl[] hold unconfirmed 
+@@ -144,6 +150,8 @@ static void release_file(struct nfs4_fil
+  * close_lru holds (open) stateowner queue ordered by nfs4_stateowner.so_time
+  * for last close replay.
+  */
++static struct list_head       reclaim_str_hashtbl[CLIENT_HASH_SIZE];
++static int reclaim_str_hashtbl_size;
+ static struct list_head       conf_id_hashtbl[CLIENT_HASH_SIZE];
+ static struct list_head       conf_str_hashtbl[CLIENT_HASH_SIZE];
+ static struct list_head       unconf_str_hashtbl[CLIENT_HASH_SIZE];
+@@ -208,12 +216,20 @@ free_client(struct nfs4_client *clp)
+       kfree(clp);
+ }
+-static void
++void
++put_nfs4_client(struct nfs4_client *clp)
++{
++      if (atomic_dec_and_test(&clp->cl_count))
++              free_client(clp);
++}
++
++void
+ expire_client(struct nfs4_client *clp)
+ {
+       struct nfs4_stateowner *sop;
+-      dprintk("NFSD: expire_client\n");
++      dprintk("NFSD: expire_client cl_count %d\n",
++                  atomic_read(&clp->cl_count));
+       list_del(&clp->cl_idhash);
+       list_del(&clp->cl_strhash);
+       list_del(&clp->cl_lru);
+@@ -221,7 +237,7 @@ expire_client(struct nfs4_client *clp)
+               sop = list_entry(clp->cl_perclient.next, struct nfs4_stateowner, so_perclient);
+               release_stateowner(sop);
+       }
+-      free_client(clp);
++      put_nfs4_client(clp);
+ }
+ static struct nfs4_client *
+@@ -230,6 +246,7 @@ create_client(struct xdr_netobj name) {
+       if(!(clp = alloc_client(name)))
+               goto out;
++      atomic_set(&clp->cl_count, 1);
+       INIT_LIST_HEAD(&clp->cl_idhash);
+       INIT_LIST_HEAD(&clp->cl_strhash);
+       INIT_LIST_HEAD(&clp->cl_perclient);
+@@ -339,6 +356,99 @@ move_to_confirmed(struct nfs4_client *cl
+       renew_client(clp);
+ }
++
++/* a helper function for parse_callback */
++static int
++parse_octet(unsigned int *lenp, char **addrp)
++{
++      unsigned int len = *lenp;
++      char *p = *addrp;
++      int n = -1;
++      char c;
++
++      for (;;) {
++              if (!len)
++                      break;
++              len--;
++              c = *p++;
++              if (c == '.')
++                      break;
++              if ((c < '0') || (c > '9')) {
++                      n = -1;
++                      break;
++              }
++              if (n < 0)
++                      n = 0;
++              n = (n * 10) + (c - '0');
++              if (n > 255) {
++                      n = -1;
++                      break;
++              }
++      }
++      *lenp = len;
++      *addrp = p;
++      return n;
++}
++
++/* parse and set the setclientid ipv4 callback address */
++int
++parse_ipv4(unsigned int addr_len, char *addr_val, unsigned int *cbaddrp, unsigned short *cbportp)
++{
++      int temp = 0;
++      u32 cbaddr = 0;
++      u16 cbport = 0;
++      u32 addrlen = addr_len;
++      char *addr = addr_val;
++      int i, shift;
++
++      /* ipaddress */
++      shift = 24;
++      for(i = 4; i > 0  ; i--) {
++              if ((temp = parse_octet(&addrlen, &addr)) < 0) {
++                      return 0;
++              }
++              cbaddr |= (temp << shift);
++              if(shift > 0)
++              shift -= 8;
++      }
++      *cbaddrp = cbaddr;
++
++      /* port */
++      shift = 8;
++      for(i = 2; i > 0  ; i--) {
++              if ((temp = parse_octet(&addrlen, &addr)) < 0) {
++                      return 0;
++              }
++              cbport |= (temp << shift);
++              if(shift > 0)
++                      shift -= 8;
++      }
++      *cbportp = cbport;
++      return 1;
++}
++
++void
++gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se)
++{
++      struct nfs4_callback *cb = &clp->cl_callback;
++
++      if( !(parse_ipv4(se->se_callback_addr_len, se->se_callback_addr_val,
++                       &cb->cb_addr, &cb->cb_port))) {
++              printk(KERN_INFO "NFSD: BAD callback address. client will not receive delegations\n");
++              printk(KERN_INFO "NFSD: this client (clientid %08x/%08x) "
++                      "will not receive delegations\n",
++                      clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id);
++
++              cb->cb_parsed = 0;
++              return;
++      }
++      cb->cb_netid.len = se->se_callback_netid_len;
++      cb->cb_netid.data = se->se_callback_netid_val;
++        cb->cb_prog = se->se_callback_prog;
++        cb->cb_ident = se->se_callback_ident;
++        cb->cb_parsed = 1;
++}
++
+ /*
+  * RFC 3010 has a complex implmentation description of processing a 
+  * SETCLIENTID request consisting of 5 bullets, labeled as 
+@@ -450,6 +560,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp
+               copy_cred(&new->cl_cred,&rqstp->rq_cred);
+               gen_clid(new);
+               gen_confirm(new);
++              gen_callback(new, setclid);
+               add_to_unconfirmed(new, strhashval);
+       } else if (cmp_verf(&conf->cl_verifier, &clverifier)) {
+               /*
+@@ -477,6 +588,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp
+               copy_cred(&new->cl_cred,&rqstp->rq_cred);
+               copy_clid(new, conf);
+               gen_confirm(new);
++              gen_callback(new, setclid);
+               add_to_unconfirmed(new,strhashval);
+       } else if (!unconf) {
+               /*
+@@ -494,6 +606,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp
+               copy_cred(&new->cl_cred,&rqstp->rq_cred);
+               gen_clid(new);
+               gen_confirm(new);
++              gen_callback(new, setclid);
+               add_to_unconfirmed(new, strhashval);
+       } else if (!cmp_verf(&conf->cl_confirm, &unconf->cl_confirm)) {
+               /*      
+@@ -519,6 +632,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp
+               copy_cred(&new->cl_cred,&rqstp->rq_cred);
+               gen_clid(new);
+               gen_confirm(new);
++              gen_callback(new, setclid);
+               add_to_unconfirmed(new, strhashval);
+       } else {
+               /* No cases hit !!! */
+@@ -529,7 +643,6 @@ nfsd4_setclientid(struct svc_rqst *rqstp
+       setclid->se_clientid.cl_boot = new->cl_clientid.cl_boot;
+       setclid->se_clientid.cl_id = new->cl_clientid.cl_id;
+       memcpy(setclid->se_confirm.data, new->cl_confirm.data, sizeof(setclid->se_confirm.data));
+-      printk(KERN_INFO "NFSD: this client will not receive delegations\n");
+       status = nfs_ok;
+ out:
+       nfs4_unlock_state();
+@@ -575,7 +688,7 @@ nfsd4_setclientid_confirm(struct svc_rqs
+                * not been found.
+                */
+               if (clp->cl_addr != ip_addr) { 
+-                      printk("NFSD: setclientid: string in use by client"
++                      dprintk("NFSD: setclientid: string in use by client"
+                       "(clientid %08x/%08x)\n",
+                       clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id);
+                       goto out;
+@@ -588,7 +701,7 @@ nfsd4_setclientid_confirm(struct svc_rqs
+                       continue;
+               status = nfserr_inval;
+               if (clp->cl_addr != ip_addr) { 
+-                      printk("NFSD: setclientid: string in use by client"
++                      dprintk("NFSD: setclientid: string in use by client"
+                       "(clientid %08x/%08x)\n",
+                       clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id);
+                       goto out;
+@@ -610,6 +723,7 @@ nfsd4_setclientid_confirm(struct svc_rqs
+                       status = nfserr_clid_inuse;
+               else {
+                       expire_client(conf);
++                      clp = unconf;
+                       move_to_confirmed(unconf, idhashval);
+                       status = nfs_ok;
+               }
+@@ -627,6 +741,7 @@ nfsd4_setclientid_confirm(struct svc_rqs
+               if (!cmp_creds(&conf->cl_cred,&rqstp->rq_cred)) {
+                       status = nfserr_clid_inuse;
+               } else {
++                      clp = conf;
+                       status = nfs_ok;
+               }
+               goto out;
+@@ -641,6 +756,7 @@ nfsd4_setclientid_confirm(struct svc_rqs
+                       status = nfserr_clid_inuse;
+               } else {
+                       status = nfs_ok;
++                      clp = unconf;
+                       move_to_confirmed(unconf, idhashval);
+               }
+               goto out;
+@@ -660,7 +776,9 @@ nfsd4_setclientid_confirm(struct svc_rqs
+       status = nfserr_inval;
+       goto out;
+ out:
+-      /* XXX if status == nfs_ok, probe callback path */
++      if (!status)
++              nfsd4_probe_callback(clp);
++
+       nfs4_unlock_state();
+       return status;
+ }
+@@ -1510,10 +1628,12 @@ nfs4_preprocess_seqid_op(struct svc_fh *
+       status = nfserr_bad_stateid;
+-      /* for new lock stateowners, check that the lock->v.new.open_stateid
+-       * refers to an open stateowner, and that the lockclid
+-       * (nfs4_lock->v.new.clientid) is the same as the
+-       * open_stateid->st_stateowner->so_client->clientid
++      /* for new lock stateowners: 
++       * check that the lock->v.new.open_stateid
++       * refers to an open stateowner
++       * 
++       * check that the lockclid (nfs4_lock->v.new.clientid) is the same 
++       * as the open_stateid->st_stateowner->so_client->clientid
+        */
+       if (lockclid) {
+               struct nfs4_stateowner *sop = stp->st_stateowner;
+@@ -1599,6 +1719,17 @@ check_replay:
+ }
+ /*
++ * eventually, this will perform an upcall to the 'state daemon' as well as
++ * set the cl_first_state field.
++ */
++void
++first_state(struct nfs4_client *clp)
++{
++      if (!clp->cl_first_state)
++              clp->cl_first_state = get_seconds();
++}
++
++/*
+  * nfs4_unlock_state(); called in encode
+  */
+ int
+@@ -1635,6 +1766,7 @@ nfsd4_open_confirm(struct svc_rqst *rqst
+                        stp->st_stateid.si_fileid,
+                        stp->st_stateid.si_generation);
+       status = nfs_ok;
++      first_state(sop->so_client);
+ out:
+       return status;
+ }
+@@ -1850,6 +1982,21 @@ nfs4_set_lock_denied(struct file_lock *f
+               deny->ld_type = NFS4_WRITE_LT;
+ }
++static struct nfs4_stateowner *
++find_lockstateowner(struct xdr_netobj *owner, clientid_t *clid)
++{
++      struct nfs4_stateowner *local = NULL;
++      int i;
++
++      for (i = 0; i < LOCK_HASH_SIZE; i++) {
++              list_for_each_entry(local, &lock_ownerid_hashtbl[i], so_idhash) {
++                      if(!cmp_owner_str(local, owner, clid))
++                              continue;
++                      return local;
++              }
++      }
++      return NULL;
++}
+ static int
+ find_lockstateowner_str(unsigned int hashval, struct xdr_netobj *owner, clientid_t *clid, struct nfs4_stateowner **op) {
+@@ -1969,7 +2116,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struc
+       if (nfs4_in_grace() && !lock->lk_reclaim)
+               return nfserr_grace;
+-      if (nfs4_in_no_grace() && lock->lk_reclaim)
++      if (!nfs4_in_grace() && lock->lk_reclaim)
+               return nfserr_no_grace;
+       if (check_lock_length(lock->lk_offset, lock->lk_length))
+@@ -1992,7 +2139,11 @@ nfsd4_lock(struct svc_rqst *rqstp, struc
+                       printk("NFSD: nfsd4_lock: clientid is stale!\n");
+                       goto out;
+               }
+-              /* does the clientid in the lock owner own the open stateid? */
++
++              /* is the new lock seqid presented by the client zero? */
++              status = nfserr_bad_seqid;
++              if (lock->v.new.lock_seqid != 0)
++                      goto out;
+               /* validate and update open stateid and open seqid */
+               status = nfs4_preprocess_seqid_op(current_fh, 
+@@ -2011,15 +2162,15 @@ nfsd4_lock(struct svc_rqst *rqstp, struc
+               strhashval = lock_ownerstr_hashval(fp->fi_inode, 
+                               open_sop->so_client->cl_clientid.cl_id, 
+                               lock->v.new.owner);
+-
+               /* 
+                * If we already have this lock owner, the client is in 
+                * error (or our bookeeping is wrong!) 
+                * for asking for a 'new lock'.
+                */
+               status = nfserr_bad_stateid;
+-              if (find_lockstateowner_str(strhashval, &lock->v.new.owner,
+-                                      &lock->v.new.clientid, &lock_sop))
++              lock_sop = find_lockstateowner(&lock->v.new.owner,
++                                              &lock->v.new.clientid);
++              if (lock_sop)
+                       goto out;
+               status = nfserr_resource;
+               if (!(lock->lk_stateowner = alloc_init_lock_stateowner(strhashval, open_sop->so_client, open_stp, lock)))
+@@ -2315,7 +2466,7 @@ nfsd4_release_lockowner(struct svc_rqst 
+       clientid_t *clid = &rlockowner->rl_clientid;
+       struct nfs4_stateowner *local = NULL;
+       struct xdr_netobj *owner = &rlockowner->rl_owner;
+-      int status, i;
++      int status;
+       dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n",
+               clid->cl_boot, clid->cl_id);
+@@ -2330,34 +2481,136 @@ nfsd4_release_lockowner(struct svc_rqst 
+       nfs4_lock_state();
+-      /* find the lockowner */
+         status = nfs_ok;
+-      for (i=0; i < LOCK_HASH_SIZE; i++)
+-              list_for_each_entry(local, &lock_ownerstr_hashtbl[i], so_strhash)
+-                      if(cmp_owner_str(local, owner, clid)) {
+-                              struct nfs4_stateid *stp;
+-
+-                              /* check for any locks held by any stateid
+-                               * associated with the (lock) stateowner */
+-                              status = nfserr_locks_held;
+-                              list_for_each_entry(stp, &local->so_perfilestate,
+-                                                  st_perfilestate) {
+-                                      if(stp->st_vfs_set) {
+-                                              if (check_for_locks(&stp->st_vfs_file,
+-                                                                  local))
+-                                                      goto out;
+-                                      }
+-                              }
+-                              /* no locks held by (lock) stateowner */
+-                              status = nfs_ok;
+-                              release_stateowner(local);
+-                              goto out;
++      local = find_lockstateowner(owner, clid);
++      if (local) {
++              struct nfs4_stateid *stp;
++
++              /* check for any locks held by any stateid
++               * associated with the (lock) stateowner */
++              status = nfserr_locks_held;
++              list_for_each_entry(stp, &local->so_perfilestate,
++                              st_perfilestate) {
++                      if(stp->st_vfs_set) {
++                              if (check_for_locks(&stp->st_vfs_file, local))
++                                      goto out;
+                       }
++              }
++              /* no locks held by (lock) stateowner */
++              status = nfs_ok;
++              release_stateowner(local);
++      }
+ out:
+       nfs4_unlock_state();
+       return status;
+ }
++static inline struct nfs4_client_reclaim *
++alloc_reclaim(int namelen)
++{
++      struct nfs4_client_reclaim *crp = NULL;
++
++      crp = kmalloc(sizeof(struct nfs4_client_reclaim), GFP_KERNEL);
++      if (!crp)
++              return NULL;
++      crp->cr_name.data = kmalloc(namelen, GFP_KERNEL);
++      if (!crp->cr_name.data) {
++              kfree(crp);
++              return NULL;
++      }
++              return crp;
++}
++
++/*
++ * failure => all reset bets are off, nfserr_no_grace...
++ */
++static int
++nfs4_client_to_reclaim(struct nfs4_client *clp)
++{
++      unsigned int strhashval;
++      struct nfs4_client_reclaim *crp = NULL;
++
++      crp = alloc_reclaim(clp->cl_name.len);
++      if (!crp)
++              return 0;
++      strhashval = clientstr_hashval(clp->cl_name.data, clp->cl_name.len);
++      INIT_LIST_HEAD(&crp->cr_strhash);
++      list_add(&crp->cr_strhash, &reclaim_str_hashtbl[strhashval]);
++      memcpy(crp->cr_name.data, clp->cl_name.data, clp->cl_name.len);
++      crp->cr_name.len = clp->cl_name.len;
++      crp->cr_first_state = clp->cl_first_state;
++      crp->cr_expired = 0;
++      return 1;
++}
++
++static void
++nfs4_release_reclaim(void)
++{
++      struct nfs4_client_reclaim *crp = NULL;
++      int i;
++
++      BUG_ON(!nfs4_reclaim_init);
++      for (i = 0; i < CLIENT_HASH_SIZE; i++) {
++              while (!list_empty(&reclaim_str_hashtbl[i])) {
++                      crp = list_entry(reclaim_str_hashtbl[i].next,
++                                      struct nfs4_client_reclaim, cr_strhash);
++                      list_del(&crp->cr_strhash);
++                      kfree(crp->cr_name.data);
++                      kfree(crp);
++                      reclaim_str_hashtbl_size--;
++              }
++      }
++      BUG_ON(reclaim_str_hashtbl_size);
++}
++
++/*
++ * called from OPEN, CLAIM_PREVIOUS with a new clientid. */
++struct nfs4_client_reclaim *
++nfs4_find_reclaim_client(clientid_t *clid)
++{
++      unsigned int idhashval = clientid_hashval(clid->cl_id);
++      unsigned int strhashval;
++      struct nfs4_client *clp, *client = NULL;
++      struct nfs4_client_reclaim *crp = NULL;
++
++
++      /* find clientid in conf_id_hashtbl */
++      list_for_each_entry(clp, &conf_id_hashtbl[idhashval], cl_idhash) {
++              if (cmp_clid(&clp->cl_clientid, clid)) {
++                      client = clp;
++                      break;
++              }
++      }
++      if (!client)
++              return NULL;
++
++      /* find clp->cl_name in reclaim_str_hashtbl */
++      strhashval = clientstr_hashval(client->cl_name.data,
++                                    client->cl_name.len);
++      list_for_each_entry(crp, &reclaim_str_hashtbl[strhashval], cr_strhash) {
++              if(cmp_name(&crp->cr_name, &client->cl_name)) {
++                      return crp;
++              }
++      }
++      return NULL;
++}
++
++/*
++* Called from OPEN. Look for clientid in reclaim list.
++*/
++int
++nfs4_check_open_reclaim(clientid_t *clid)
++{
++      struct nfs4_client_reclaim *crp;
++
++      if ((crp = nfs4_find_reclaim_client(clid)) == NULL)
++              return nfserr_reclaim_bad;
++      if (crp->cr_expired)
++              return nfserr_no_grace;
++      return nfs_ok;
++}
++
++
+ /* 
+  * Start and stop routines
+  */
+@@ -2366,10 +2619,16 @@ void 
+ nfs4_state_init(void)
+ {
+       int i;
+-      time_t start = get_seconds();
++      time_t grace_time;
+       if (nfs4_init)
+               return;
++      if (!nfs4_reclaim_init) {
++              for (i = 0; i < CLIENT_HASH_SIZE; i++)
++                      INIT_LIST_HEAD(&reclaim_str_hashtbl[i]);
++              reclaim_str_hashtbl_size = 0;
++              nfs4_reclaim_init = 1;
++      }
+       for (i = 0; i < CLIENT_HASH_SIZE; i++) {
+               INIT_LIST_HEAD(&conf_id_hashtbl[i]);
+               INIT_LIST_HEAD(&conf_str_hashtbl[i]);
+@@ -2396,27 +2655,36 @@ nfs4_state_init(void)
+       INIT_LIST_HEAD(&close_lru);
+       INIT_LIST_HEAD(&client_lru);
+-      init_MUTEX(&client_sema);
+-      boot_time = start;
+-      grace_end = start + NFSD_LEASE_TIME;
++      boot_time = get_seconds();
++      grace_time = max(old_lease_time, lease_time);
++      if (reclaim_str_hashtbl_size == 0)
++              grace_time = 0;
++      if (grace_time)
++              printk("NFSD: starting %ld-second grace period\n", grace_time);
++      grace_end = boot_time + grace_time;
+       INIT_WORK(&laundromat_work,laundromat_main, NULL);
+       schedule_delayed_work(&laundromat_work, NFSD_LEASE_TIME*HZ);
+       nfs4_init = 1;
+-
+ }
+ int
+ nfs4_in_grace(void)
+ {
+-      return time_before(get_seconds(), (unsigned long)grace_end);
++      return get_seconds() < grace_end;
+ }
+-int
+-nfs4_in_no_grace(void)
++void
++set_no_grace(void)
+ {
+-      return (grace_end < get_seconds());
++      printk("NFSD: ERROR in reboot recovery.  State reclaims will fail.\n");
++      grace_end = get_seconds();
+ }
++time_t
++nfs4_lease_time(void)
++{
++      return lease_time;
++}
+ static void
+ __nfs4_state_shutdown(void)
+@@ -2454,6 +2722,61 @@ void
+ nfs4_state_shutdown(void)
+ {
+       nfs4_lock_state();
++      nfs4_release_reclaim();
+       __nfs4_state_shutdown();
+       nfs4_unlock_state();
+ }
++
++/*
++ * Called when leasetime is changed.
++ *
++ * if nfsd is not started, simply set the global lease.
++ *
++ * if nfsd(s) are running, lease change requires nfsv4 state to be reset.
++ * e.g: boot_time is reset, existing nfs4_client structs are
++ * used to fill reclaim_str_hashtbl, then all state (except for the
++ * reclaim_str_hashtbl) is re-initialized.
++ *
++ * if the old lease time is greater than the new lease time, the grace
++ * period needs to be set to the old lease time to allow clients to reclaim
++ * their state. XXX - we may want to set the grace period == lease time
++ * after an initial grace period == old lease time
++ *
++ * if an error occurs in this process, the new lease is set, but the server
++ * will not honor OPEN or LOCK reclaims, and will return nfserr_no_grace
++ * which means OPEN/LOCK/READ/WRITE will fail during grace period.
++ *
++ * clients will attempt to reset all state with SETCLIENTID/CONFIRM, and
++ * OPEN and LOCK reclaims.
++ */
++void
++nfs4_reset_lease(time_t leasetime)
++{
++      struct nfs4_client *clp;
++      int i;
++
++      printk("NFSD: New leasetime %ld\n",leasetime);
++      if (!nfs4_init)
++              return;
++      nfs4_lock_state();
++      old_lease_time = lease_time;
++      lease_time = leasetime;
++
++      nfs4_release_reclaim();
++
++      /* populate reclaim_str_hashtbl with current confirmed nfs4_clientid */
++      for (i = 0; i < CLIENT_HASH_SIZE; i++) {
++              list_for_each_entry(clp, &conf_id_hashtbl[i], cl_idhash) {
++                      if (!nfs4_client_to_reclaim(clp)) {
++                              nfs4_release_reclaim();
++                              goto init_state;
++                      }
++                      reclaim_str_hashtbl_size++;
++              }
++      }
++init_state:
++      __nfs4_state_shutdown();
++      nfs4_state_init();
++      nfs4_unlock_state();
++}
++
+--- linux-2.6.7/fs/nfsd/vfs.c.lsec     2004-06-15 23:19:13.000000000 -0600
++++ linux-2.6.7/fs/nfsd/vfs.c  2005-03-23 14:28:24.520322040 -0700
+@@ -44,6 +44,16 @@
+ #include <linux/nfsd/nfsfh.h>
+ #include <linux/quotaops.h>
+ #include <linux/dnotify.h>
++#ifdef CONFIG_NFSD_V4
++#include <linux/posix_acl.h>
++#include <linux/posix_acl_xattr.h>
++#include <linux/xattr_acl.h>
++#include <linux/xattr.h>
++#include <linux/nfs4.h>
++#include <linux/nfs4_acl.h>
++#include <linux/nfsd_idmap.h>
++#include <linux/security.h>
++#endif /* CONFIG_NFSD_V4 */
+ #include <asm/uaccess.h>
+@@ -344,6 +354,177 @@ out_nfserr:
+       goto out;
+ }
++#if defined(CONFIG_NFSD_V4)
++
++static int
++set_nfsv4_acl_one(struct dentry *dentry, struct posix_acl *pacl, char *key)
++{
++      int len;
++      size_t buflen;
++      char *buf = NULL;
++      int error = 0;
++      struct inode *inode = dentry->d_inode;
++
++      buflen = posix_acl_xattr_size(pacl->a_count);
++      buf = kmalloc(buflen, GFP_KERNEL);
++      error = -ENOMEM;
++      if (buf == NULL)
++              goto out;
++
++      len = posix_acl_to_xattr(pacl, buf, buflen);
++      if (len < 0) {
++              error = len;
++              goto out;
++      }
++
++      error = -EOPNOTSUPP;
++      if (inode->i_op && inode->i_op->setxattr) {
++              down(&inode->i_sem);
++              security_inode_setxattr(dentry, key, buf, len, 0);
++              error = inode->i_op->setxattr(dentry, key, buf, len, 0);
++              if (!error)
++                      security_inode_post_setxattr(dentry, key, buf, len, 0);
++              up(&inode->i_sem);
++      }
++out:
++      kfree(buf);
++      return (error);
++}
++
++int
++nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
++    struct nfs4_acl *acl)
++{
++      int error;
++      struct dentry *dentry;
++      struct inode *inode;
++      struct posix_acl *pacl = NULL, *dpacl = NULL;
++      unsigned int flags = 0;
++
++      /* Get inode */
++      error = fh_verify(rqstp, fhp, 0 /* S_IFREG */, MAY_SATTR);
++      if (error)
++              goto out;
++
++      dentry = fhp->fh_dentry;
++      inode = dentry->d_inode;
++      if (S_ISDIR(inode->i_mode))
++              flags = NFS4_ACL_DIR;
++
++      error = nfs4_acl_nfsv4_to_posix(acl, &pacl, &dpacl, flags);
++      if (error < 0)
++              goto out_nfserr;
++
++      if (pacl) {
++              error = set_nfsv4_acl_one(dentry, pacl, XATTR_NAME_ACL_ACCESS);
++              if (error < 0)
++                      goto out_nfserr;
++      }
++
++      if (dpacl) {
++              error = set_nfsv4_acl_one(dentry, dpacl, XATTR_NAME_ACL_DEFAULT);
++              if (error < 0)
++                      goto out_nfserr;
++      }
++
++      error = nfs_ok;
++
++out:
++      posix_acl_release(pacl);
++      posix_acl_release(dpacl);
++      return (error);
++out_nfserr:
++      error = nfserrno(error);
++      goto out;
++}
++
++static struct posix_acl *
++_get_posix_acl(struct dentry *dentry, char *key)
++{
++      struct inode *inode = dentry->d_inode;
++      char *buf = NULL;
++      int buflen, error = 0;
++      struct posix_acl *pacl = NULL;
++
++      down(&inode->i_sem);
++
++      buflen = inode->i_op->getxattr(dentry, key, NULL, 0);
++      if (buflen <= 0) {
++              error = buflen < 0 ? buflen : -ENODATA;
++              goto out_sem;
++      }
++
++      buf = kmalloc(buflen, GFP_KERNEL);
++      if (buf == NULL) {
++              error = -ENOMEM;
++              goto out_sem;
++      }
++
++      error = -EOPNOTSUPP;
++      if (inode->i_op && inode->i_op->getxattr) {
++              error = security_inode_getxattr(dentry, key);
++              if (error)
++                      goto out_sem;
++              error = inode->i_op->getxattr(dentry, key, buf, buflen);
++      }
++      if (error < 0)
++              goto out_sem;
++
++      error = 0;
++      up(&inode->i_sem);
++
++      pacl = posix_acl_from_xattr(buf, buflen);
++ out:
++      kfree(buf);
++      return pacl;
++ out_sem:
++      up(&inode->i_sem);
++      pacl = ERR_PTR(error);
++      goto out;
++}
++
++int
++nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct nfs4_acl **acl)
++{
++      struct inode *inode = dentry->d_inode;
++      int error = 0;
++      struct posix_acl *pacl = NULL, *dpacl = NULL;
++      unsigned int flags = 0;
++
++      pacl = _get_posix_acl(dentry, XATTR_NAME_ACL_ACCESS);
++      if (IS_ERR(pacl) && PTR_ERR(pacl) == -ENODATA)
++              pacl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
++      if (IS_ERR(pacl)) {
++              error = PTR_ERR(pacl);
++              pacl = NULL;
++              goto out;
++      }
++
++      if (S_ISDIR(inode->i_mode)) {
++              dpacl = _get_posix_acl(dentry, XATTR_NAME_ACL_DEFAULT);
++              if (IS_ERR(dpacl) && PTR_ERR(dpacl) == -ENODATA)
++                      dpacl = NULL;
++              else if (IS_ERR(dpacl)) {
++                      error = PTR_ERR(dpacl);
++                      dpacl = NULL;
++                      goto out;
++              }
++              flags = NFS4_ACL_DIR;
++      }
++
++      *acl = nfs4_acl_posix_to_nfsv4(pacl, dpacl, flags);
++      if (IS_ERR(*acl)) {
++              error = PTR_ERR(*acl);
++              *acl = NULL;
++      }
++ out:
++      posix_acl_release(pacl);
++      posix_acl_release(dpacl);
++      return error;
++}
++
++#endif /* defined(CONFIG_NFS_V4) */
++
+ #ifdef CONFIG_NFSD_V3
+ /*
+  * Check server access rights to a file system object
+--- linux-2.6.7/fs/nfsd/nfs4idmap.c.lsec       2004-06-15 23:19:43.000000000 -0600
++++ linux-2.6.7/fs/nfsd/nfs4idmap.c    2005-03-23 14:28:24.687296656 -0700
+@@ -78,9 +78,9 @@ struct ent {
+ #define DefineSimpleCacheLookupMap(STRUCT, FUNC)                      \
+         DefineCacheLookup(struct STRUCT, h, FUNC##_lookup,            \
+-        (struct STRUCT *item, int set), /*no setup */,                        \
++        (struct STRUCT *item, int set),                       \
+       & FUNC##_cache, FUNC##_hash(item), FUNC##_match(item, tmp),     \
+-      STRUCT##_init(new, item), STRUCT##_update(tmp, item), 0)
++      STRUCT##_init(new, item), STRUCT##_update(tmp, item))
+ /* Common entry handling */
+--- linux-2.6.7/fs/nfsd/nfs4acl.c.lsec 2005-03-23 14:28:24.463330704 -0700
++++ linux-2.6.7/fs/nfsd/nfs4acl.c      2005-03-23 14:28:24.463330704 -0700
+@@ -0,0 +1,974 @@
++/*
++ *  fs/nfs4acl/acl.c
++ *
++ *  Common NFSv4 ACL handling code.
++ *
++ *  Copyright (c) 2002, 2003 The Regents of the University of Michigan.
++ *  All rights reserved.
++ *
++ *  Marius Aamodt Eriksen <marius@umich.edu>
++ *  Jeff Sedlak <jsedlak@umich.edu>
++ *  J. Bruce Fields <bfields@umich.edu>
++ *
++ *  Redistribution and use in source and binary forms, with or without
++ *  modification, are permitted provided that the following conditions
++ *  are met:
++ *
++ *  1. Redistributions of source code must retain the above copyright
++ *     notice, this list of conditions and the following disclaimer.
++ *  2. Redistributions in binary form must reproduce the above copyright
++ *     notice, this list of conditions and the following disclaimer in the
++ *     documentation and/or other materials provided with the distribution.
++ *  3. Neither the name of the University nor the names of its
++ *     contributors may be used to endorse or promote products derived
++ *     from this software without specific prior written permission.
++ *
++ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
++ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
++ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
++ *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
++ *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
++ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
++ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
++ *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
++ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
++ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#include <linux/string.h>
++#include <linux/slab.h>
++#include <linux/list.h>
++#include <linux/types.h>
++#include <linux/fs.h>
++#include <linux/module.h>
++#include <linux/nfs_fs.h>
++#include <linux/posix_acl.h>
++#include <linux/nfs4.h>
++#include <linux/nfs4_acl.h>
++
++
++/* mode bit translations: */
++#define NFS4_READ_MODE (NFS4_ACE_READ_DATA | NFS4_ACE_READ_NAMED_ATTRS)
++#define NFS4_WRITE_MODE (NFS4_ACE_WRITE_DATA | NFS4_ACE_WRITE_NAMED_ATTRS | NFS4_ACE_APPEND_DATA)
++#define NFS4_EXECUTE_MODE NFS4_ACE_EXECUTE
++#define NFS4_ANYONE_MODE (NFS4_ACE_READ_ATTRIBUTES | NFS4_ACE_READ_ACL | NFS4_ACE_SYNCHRONIZE)
++#define NFS4_OWNER_MODE (NFS4_ACE_WRITE_ATTRIBUTES | NFS4_ACE_WRITE_ACL)
++
++/* flags used to simulate posix default ACLs */
++#define NFS4_INHERITANCE_FLAGS (NFS4_ACE_FILE_INHERIT_ACE \
++              | NFS4_ACE_DIRECTORY_INHERIT_ACE | NFS4_ACE_INHERIT_ONLY_ACE)
++
++#define MASK_EQUAL(mask1, mask2) \
++      ( ((mask1) & NFS4_ACE_MASK_ALL) == ((mask2) & NFS4_ACE_MASK_ALL) )
++
++static u32
++mask_from_posix(unsigned short perm, unsigned int flags)
++{
++      int mask = NFS4_ANYONE_MODE;
++
++      if (flags & NFS4_ACL_OWNER)
++              mask |= NFS4_OWNER_MODE;
++      if (perm & ACL_READ)
++              mask |= NFS4_READ_MODE;
++      if (perm & ACL_WRITE)
++              mask |= NFS4_WRITE_MODE;
++      if ((perm & ACL_WRITE) && (flags & NFS4_ACL_DIR))
++              mask |= NFS4_ACE_DELETE_CHILD;
++      if (perm & ACL_EXECUTE)
++              mask |= NFS4_EXECUTE_MODE;
++      return mask;
++}
++
++static u32
++deny_mask(u32 allow_mask, unsigned int flags)
++{
++      u32 ret = ~allow_mask & ~NFS4_ACE_DELETE;
++      if (!(flags & NFS4_ACL_DIR))
++              ret &= ~NFS4_ACE_DELETE_CHILD;
++      return ret;
++}
++
++static int
++mode_from_nfs4(u32 perm, unsigned short *mode, unsigned int flags)
++{
++      u32 ignore = 0;
++
++      if (!(flags & NFS4_ACL_DIR))
++              ignore |= NFS4_ACE_DELETE_CHILD; /* ignore it */
++      perm |= ignore;
++      *mode = 0;
++      if ((perm & NFS4_READ_MODE) == NFS4_READ_MODE)
++              *mode |= ACL_READ;
++      if ((perm & NFS4_WRITE_MODE) == NFS4_WRITE_MODE)
++              *mode |= ACL_WRITE;
++      if ((perm & NFS4_EXECUTE_MODE) == NFS4_EXECUTE_MODE)
++              *mode |= ACL_EXECUTE;
++      if (!MASK_EQUAL(perm, ignore|mask_from_posix(*mode, flags)))
++              return -EINVAL;
++      return 0;
++}
++
++struct ace_container {
++      struct nfs4_ace  *ace;
++      struct list_head  ace_l;
++};
++
++static short ace2type(struct nfs4_ace *);
++static int _posix_to_nfsv4_one(struct posix_acl *, struct nfs4_acl *, unsigned int);
++static struct posix_acl *_nfsv4_to_posix_one(struct nfs4_acl *, unsigned int);
++int nfs4_acl_add_ace(struct nfs4_acl *, u32, u32, u32, int, uid_t);
++int nfs4_acl_split(struct nfs4_acl *, struct nfs4_acl *);
++
++struct nfs4_acl *
++nfs4_acl_posix_to_nfsv4(struct posix_acl *pacl, struct posix_acl *dpacl,
++                      unsigned int flags)
++{
++      struct nfs4_acl *acl;
++      int error = -EINVAL;
++
++      if ((pacl != NULL &&
++              (posix_acl_valid(pacl) < 0 || pacl->a_count == 0)) ||
++          (dpacl != NULL &&
++              (posix_acl_valid(dpacl) < 0 || dpacl->a_count == 0)))
++              goto out_err;
++
++      acl = nfs4_acl_new();
++      if (acl == NULL) {
++              error = -ENOMEM;
++              goto out_err;
++      }
++
++      if (pacl != NULL) {
++              error = _posix_to_nfsv4_one(pacl, acl,
++                                              flags & ~NFS4_ACL_TYPE_DEFAULT);
++              if (error < 0)
++                      goto out_acl;
++      }
++
++      if (dpacl != NULL) {
++              error = _posix_to_nfsv4_one(dpacl, acl,
++                                              flags | NFS4_ACL_TYPE_DEFAULT);
++              if (error < 0)
++                      goto out_acl;
++      }
++
++      return acl;
++
++out_acl:
++      nfs4_acl_free(acl);
++out_err:
++      acl = ERR_PTR(error);
++
++      return acl;
++}
++
++static int
++nfs4_acl_add_pair(struct nfs4_acl *acl, int eflag, u32 mask, int whotype,
++              uid_t owner, unsigned int flags)
++{
++      int error;
++
++      error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE,
++                               eflag, mask, whotype, owner);
++      if (error < 0)
++              return error;
++      error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_DENIED_ACE_TYPE,
++                              eflag, deny_mask(mask, flags), whotype, owner);
++      return error;
++}
++
++/* We assume the acl has been verified with posix_acl_valid. */
++static int
++_posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl,
++                                              unsigned int flags)
++{
++      struct posix_acl_entry *pa, *pe, *group_owner_entry;
++      int error = -EINVAL;
++      u32 mask, mask_mask;
++      int eflag = ((flags & NFS4_ACL_TYPE_DEFAULT) ?
++                                      NFS4_INHERITANCE_FLAGS : 0);
++
++      BUG_ON(pacl->a_count < 3);
++      pe = pacl->a_entries + pacl->a_count;
++      pa = pe - 2; /* if mask entry exists, it's second from the last. */
++      if (pa->e_tag == ACL_MASK)
++              mask_mask = deny_mask(mask_from_posix(pa->e_perm, flags), flags);
++      else
++              mask_mask = 0;
++
++      pa = pacl->a_entries;
++      BUG_ON(pa->e_tag != ACL_USER_OBJ);
++      mask = mask_from_posix(pa->e_perm, flags | NFS4_ACL_OWNER);
++      error = nfs4_acl_add_pair(acl, eflag, mask, NFS4_ACL_WHO_OWNER, 0, flags);
++      if (error < 0)
++              goto out;
++      pa++;
++
++      while (pa->e_tag == ACL_USER) {
++              mask = mask_from_posix(pa->e_perm, flags);
++              error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_DENIED_ACE_TYPE,
++                              eflag,  mask_mask, NFS4_ACL_WHO_NAMED, pa->e_id);
++              if (error < 0)
++                      goto out;
++
++
++              error = nfs4_acl_add_pair(acl, eflag, mask,
++                              NFS4_ACL_WHO_NAMED, pa->e_id, flags);
++              if (error < 0)
++                      goto out;
++              pa++;
++      }
++
++      /* In the case of groups, we apply allow ACEs first, then deny ACEs,
++       * since a user can be in more than one group.  */
++
++      /* allow ACEs */
++
++      if (pacl->a_count > 3) {
++              BUG_ON(pa->e_tag != ACL_GROUP_OBJ);
++              error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_DENIED_ACE_TYPE,
++                              NFS4_ACE_IDENTIFIER_GROUP | eflag, mask_mask,
++                              NFS4_ACL_WHO_GROUP, 0);
++              if (error < 0)
++                      goto out;
++      }
++      group_owner_entry = pa;
++      mask = mask_from_posix(pa->e_perm, flags);
++      error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE,
++                      NFS4_ACE_IDENTIFIER_GROUP | eflag, mask,
++                      NFS4_ACL_WHO_GROUP, 0);
++      if (error < 0)
++              goto out;
++      pa++;
++
++      while (pa->e_tag == ACL_GROUP) {
++              mask = mask_from_posix(pa->e_perm, flags);
++              error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_DENIED_ACE_TYPE,
++                              NFS4_ACE_IDENTIFIER_GROUP | eflag, mask_mask,
++                              NFS4_ACL_WHO_NAMED, pa->e_id);
++              if (error < 0)
++                      goto out;
++
++              error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE,
++                              NFS4_ACE_IDENTIFIER_GROUP | eflag, mask,
++                              NFS4_ACL_WHO_NAMED, pa->e_id);
++              if (error < 0)
++                      goto out;
++              pa++;
++      }
++
++      /* deny ACEs */
++
++      pa = group_owner_entry;
++      mask = mask_from_posix(pa->e_perm, flags);
++      error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_DENIED_ACE_TYPE,
++                      NFS4_ACE_IDENTIFIER_GROUP | eflag,
++                      deny_mask(mask, flags), NFS4_ACL_WHO_GROUP, 0);
++      if (error < 0)
++              goto out;
++      pa++;
++      while (pa->e_tag == ACL_GROUP) {
++              mask = mask_from_posix(pa->e_perm, flags);
++              error = nfs4_acl_add_ace(acl, NFS4_ACE_ACCESS_DENIED_ACE_TYPE,
++                              NFS4_ACE_IDENTIFIER_GROUP | eflag,
++                              deny_mask(mask, flags), NFS4_ACL_WHO_NAMED, pa->e_id);
++              if (error < 0)
++                      goto out;
++              pa++;
++      }
++
++      if (pa->e_tag == ACL_MASK)
++              pa++;
++      BUG_ON(pa->e_tag != ACL_OTHER);
++      mask = mask_from_posix(pa->e_perm, flags);
++      error = nfs4_acl_add_pair(acl, eflag, mask, NFS4_ACL_WHO_EVERYONE, 0, flags);
++
++out:
++      return error;
++}
++
++static void
++sort_pacl_range(struct posix_acl *pacl, int start, int end) {
++      int sorted = 0, i;
++      struct posix_acl_entry tmp;
++
++      /* We just do a bubble sort; easy to do in place, and we're not
++       * expecting acl's to be long enough to justify anything more. */
++      while (!sorted) {
++              sorted = 1;
++              for (i = start; i < end; i++) {
++                      if (pacl->a_entries[i].e_id
++                                      > pacl->a_entries[i+1].e_id) {
++                              sorted = 0;
++                              tmp = pacl->a_entries[i];
++                              pacl->a_entries[i] = pacl->a_entries[i+1];
++                              pacl->a_entries[i+1] = tmp;
++                      }
++              }
++      }
++}
++
++static void
++sort_pacl(struct posix_acl *pacl)
++{
++      /* posix_acl_valid requires that users and groups be in order
++       * by uid/gid. */
++      int i, j;
++
++      if (pacl->a_count <= 4)
++              return; /* no users or groups */
++      i = 1;
++      while (pacl->a_entries[i].e_tag == ACL_USER)
++              i++;
++      sort_pacl_range(pacl, 1, i-1);
++
++      BUG_ON(pacl->a_entries[i].e_tag != ACL_GROUP_OBJ);
++      j = i++;
++      while (pacl->a_entries[j].e_tag == ACL_GROUP)
++              j++;
++      sort_pacl_range(pacl, i, j-1);
++      return;
++}
++
++static int
++write_pace(struct nfs4_ace *ace, struct posix_acl *pacl,
++              struct posix_acl_entry **pace, short tag, unsigned int flags)
++{
++      struct posix_acl_entry *this = *pace;
++
++      if (*pace == pacl->a_entries + pacl->a_count)
++              return -EINVAL; /* fell off the end */
++      (*pace)++;
++      this->e_tag = tag;
++      if (tag == ACL_USER_OBJ)
++              flags |= NFS4_ACL_OWNER;
++      if (mode_from_nfs4(ace->access_mask, &this->e_perm, flags))
++              return -EINVAL;
++      this->e_id = (tag == ACL_USER || tag == ACL_GROUP ?
++                      ace->who : ACL_UNDEFINED_ID);
++      return 0;
++}
++
++static struct nfs4_ace *
++get_next_v4_ace(struct list_head **p, struct list_head *head)
++{
++      struct nfs4_ace *ace;
++
++      *p = (*p)->next;
++      if (*p == head)
++              return NULL;
++      ace = list_entry(*p, struct nfs4_ace, l_ace);
++
++      return ace;
++}
++
++int
++nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl, struct posix_acl **pacl,
++              struct posix_acl **dpacl, unsigned int flags)
++{
++      struct nfs4_acl *dacl;
++      int error = -ENOMEM;
++
++      *pacl = NULL;
++      *dpacl = NULL;
++
++      dacl = nfs4_acl_new();
++      if (dacl == NULL)
++              goto out;
++
++      error = nfs4_acl_split(acl, dacl);
++      if (error < 0)
++              goto out_acl;
++
++      if (pacl != NULL) {
++              if (acl->naces == 0) {
++                      error = -ENODATA;
++                      goto try_dpacl;
++              }
++
++              *pacl = _nfsv4_to_posix_one(acl, flags);
++              if (IS_ERR(*pacl)) {
++                      error = PTR_ERR(*pacl);
++                      *pacl = NULL;
++                      goto out_acl;
++              }
++      }
++
++try_dpacl:
++      if (dpacl != NULL) {
++              if (dacl->naces == 0) {
++                      if (pacl == NULL || *pacl == NULL)
++                              error = -ENODATA;
++                      goto out_acl;
++              }
++
++              error = 0;
++              *dpacl = _nfsv4_to_posix_one(dacl, flags);
++              if (IS_ERR(*dpacl)) {
++                      error = PTR_ERR(*dpacl);
++                      *dpacl = NULL;
++                      goto out_acl;
++              }
++      }
++
++out_acl:
++      if (error && pacl) {
++              posix_acl_release(*pacl);
++              *pacl = NULL;
++      }
++      nfs4_acl_free(dacl);
++out:
++      return error;
++}
++
++static int
++same_who(struct nfs4_ace *a, struct nfs4_ace *b)
++{
++      return a->whotype == b->whotype &&
++              (a->whotype != NFS4_ACL_WHO_NAMED || a->who == b->who);
++}
++
++static int
++complementary_ace_pair(struct nfs4_ace *allow, struct nfs4_ace *deny,
++              unsigned int flags)
++{
++      int ignore = 0;
++      if (!(flags & NFS4_ACL_DIR))
++              ignore |= NFS4_ACE_DELETE_CHILD;
++      return MASK_EQUAL(ignore|deny_mask(allow->access_mask, flags),
++                        ignore|deny->access_mask) &&
++              allow->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE &&
++              deny->type == NFS4_ACE_ACCESS_DENIED_ACE_TYPE &&
++              allow->flag == deny->flag &&
++              same_who(allow, deny);
++}
++
++static inline int
++user_obj_from_v4(struct nfs4_acl *n4acl, struct list_head **p,
++              struct posix_acl *pacl, struct posix_acl_entry **pace,
++              unsigned int flags)
++{
++      int error = -EINVAL;
++      struct nfs4_ace *ace, *ace2;
++
++      ace = get_next_v4_ace(p, &n4acl->ace_head);
++      if (ace == NULL)
++              goto out;
++      if (ace2type(ace) != ACL_USER_OBJ)
++              goto out;
++      error = write_pace(ace, pacl, pace, ACL_USER_OBJ, flags);
++      if (error < 0)
++              goto out;
++      error = -EINVAL;
++      ace2 = get_next_v4_ace(p, &n4acl->ace_head);
++      if (ace2 == NULL)
++              goto out;
++      if (!complementary_ace_pair(ace, ace2, flags))
++              goto out;
++      error = 0;
++out:
++      return error;
++}
++
++static inline int
++users_from_v4(struct nfs4_acl *n4acl, struct list_head **p,
++              struct nfs4_ace **mask_ace,
++              struct posix_acl *pacl, struct posix_acl_entry **pace,
++              unsigned int flags)
++{
++      int error = -EINVAL;
++      struct nfs4_ace *ace, *ace2;
++
++      ace = get_next_v4_ace(p, &n4acl->ace_head);
++      if (ace == NULL)
++              goto out;
++      while (ace2type(ace) == ACL_USER) {
++              if (ace->type != NFS4_ACE_ACCESS_DENIED_ACE_TYPE)
++                      goto out;
++              if (*mask_ace &&
++                      !MASK_EQUAL(ace->access_mask, (*mask_ace)->access_mask))
++                      goto out;
++              *mask_ace = ace;
++              ace = get_next_v4_ace(p, &n4acl->ace_head);
++              if (ace == NULL)
++                      goto out;
++              if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE)
++                      goto out;
++              error = write_pace(ace, pacl, pace, ACL_USER, flags);
++              if (error < 0)
++                      goto out;
++              error = -EINVAL;
++              ace2 = get_next_v4_ace(p, &n4acl->ace_head);
++              if (ace2 == NULL)
++                      goto out;
++              if (!complementary_ace_pair(ace, ace2, flags))
++                      goto out;
++              if ((*mask_ace)->flag != ace2->flag ||
++                              !same_who(*mask_ace, ace2))
++                      goto out;
++              ace = get_next_v4_ace(p, &n4acl->ace_head);
++              if (ace == NULL)
++                      goto out;
++      }
++      error = 0;
++out:
++      return error;
++}
++
++static inline int
++group_obj_and_groups_from_v4(struct nfs4_acl *n4acl, struct list_head **p,
++              struct nfs4_ace **mask_ace,
++              struct posix_acl *pacl, struct posix_acl_entry **pace,
++              unsigned int flags)
++{
++      int error = -EINVAL;
++      struct nfs4_ace *ace, *ace2;
++      struct ace_container *ac;
++      struct list_head group_l;
++
++      INIT_LIST_HEAD(&group_l);
++      ace = list_entry(*p, struct nfs4_ace, l_ace);
++
++      /* group owner (mask and allow aces) */
++
++      if (pacl->a_count != 3) {
++              /* then the group owner should be preceded by mask */
++              if (ace->type != NFS4_ACE_ACCESS_DENIED_ACE_TYPE)
++                      goto out;
++              if (*mask_ace &&
++                      !MASK_EQUAL(ace->access_mask, (*mask_ace)->access_mask))
++                      goto out;
++              *mask_ace = ace;
++              ace = get_next_v4_ace(p, &n4acl->ace_head);
++              if (ace == NULL)
++                      goto out;
++
++              if ((*mask_ace)->flag != ace->flag || !same_who(*mask_ace, ace))
++                      goto out;
++      }
++
++      if (ace2type(ace) != ACL_GROUP_OBJ)
++              goto out;
++
++      ac = kmalloc(sizeof(*ac), GFP_KERNEL);
++      error = -ENOMEM;
++      if (ac == NULL)
++              goto out;
++      ac->ace = ace;
++      list_add_tail(&ac->ace_l, &group_l);
++
++      error = -EINVAL;
++      if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE)
++              goto out;
++
++      error = write_pace(ace, pacl, pace, ACL_GROUP_OBJ, flags);
++      if (error < 0)
++              goto out;
++
++      error = -EINVAL;
++      ace = get_next_v4_ace(p, &n4acl->ace_head);
++      if (ace == NULL)
++              goto out;
++
++      /* groups (mask and allow aces) */
++
++      while (ace2type(ace) == ACL_GROUP) {
++              if (*mask_ace == NULL)
++                      goto out;
++
++              if (ace->type != NFS4_ACE_ACCESS_DENIED_ACE_TYPE ||
++                      !MASK_EQUAL(ace->access_mask, (*mask_ace)->access_mask))
++                      goto out;
++              *mask_ace = ace;
++
++              ace = get_next_v4_ace(p, &n4acl->ace_head);
++              if (ace == NULL)
++                      goto out;
++              ac = kmalloc(sizeof(*ac), GFP_KERNEL);
++              error = -ENOMEM;
++              if (ac == NULL)
++                      goto out;
++              error = -EINVAL;
++              if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE ||
++                              !same_who(ace, *mask_ace))
++                      goto out;
++
++              ac->ace = ace;
++              list_add_tail(&ac->ace_l, &group_l);
++
++              error = write_pace(ace, pacl, pace, ACL_GROUP, flags);
++              if (error < 0)
++                      goto out;
++              error = -EINVAL;
++              ace = get_next_v4_ace(p, &n4acl->ace_head);
++              if (ace == NULL)
++                      goto out;
++      }
++
++      /* group owner (deny ace) */
++
++      if (ace2type(ace) != ACL_GROUP_OBJ)
++              goto out;
++      ac = list_entry(group_l.next, struct ace_container, ace_l);
++      ace2 = ac->ace;
++      if (!complementary_ace_pair(ace2, ace, flags))
++              goto out;
++      list_del(group_l.next);
++      kfree(ac);
++
++      /* groups (deny aces) */
++
++      while (!list_empty(&group_l)) {
++              ace = get_next_v4_ace(p, &n4acl->ace_head);
++              if (ace == NULL)
++                      goto out;
++              if (ace2type(ace) != ACL_GROUP)
++                      goto out;
++              ac = list_entry(group_l.next, struct ace_container, ace_l);
++              ace2 = ac->ace;
++              if (!complementary_ace_pair(ace2, ace, flags))
++                      goto out;
++              list_del(group_l.next);
++              kfree(ac);
++      }
++
++      ace = get_next_v4_ace(p, &n4acl->ace_head);
++      if (ace == NULL)
++              goto out;
++      if (ace2type(ace) != ACL_OTHER)
++              goto out;
++      error = 0;
++out:
++      while (!list_empty(&group_l)) {
++              ac = list_entry(group_l.next, struct ace_container, ace_l);
++              list_del(group_l.next);
++              kfree(ac);
++      }
++      return error;
++}
++
++static inline int
++mask_from_v4(struct nfs4_acl *n4acl, struct list_head **p,
++              struct nfs4_ace **mask_ace,
++              struct posix_acl *pacl, struct posix_acl_entry **pace,
++              unsigned int flags)
++{
++      int error = -EINVAL;
++      struct nfs4_ace *ace;
++
++      ace = list_entry(*p, struct nfs4_ace, l_ace);
++      if (pacl->a_count != 3) {
++              if (*mask_ace == NULL)
++                      goto out;
++              (*mask_ace)->access_mask = deny_mask((*mask_ace)->access_mask, flags);
++              write_pace(*mask_ace, pacl, pace, ACL_MASK, flags);
++      }
++      error = 0;
++out:
++      return error;
++}
++
++static inline int
++other_from_v4(struct nfs4_acl *n4acl, struct list_head **p,
++              struct posix_acl *pacl, struct posix_acl_entry **pace,
++              unsigned int flags)
++{
++      int error = -EINVAL;
++      struct nfs4_ace *ace, *ace2;
++
++      ace = list_entry(*p, struct nfs4_ace, l_ace);
++      if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE)
++              goto out;
++      error = write_pace(ace, pacl, pace, ACL_OTHER, flags);
++      if (error < 0)
++              goto out;
++      error = -EINVAL;
++      ace2 = get_next_v4_ace(p, &n4acl->ace_head);
++      if (ace2 == NULL)
++              goto out;
++      if (!complementary_ace_pair(ace, ace2, flags))
++              goto out;
++      error = 0;
++out:
++      return error;
++}
++
++static int
++calculate_posix_ace_count(struct nfs4_acl *n4acl)
++{
++      if (n4acl->naces == 6) /* owner, owner group, and other only */
++              return 3;
++      else { /* Otherwise there must be a mask entry. */
++              /* Also, the remaining entries are for named users and
++               * groups, and come in threes (mask, allow, deny): */
++              if (n4acl->naces < 7)
++                      return -1;
++              if ((n4acl->naces - 7) % 3)
++                      return -1;
++              return 4 + (n4acl->naces - 7)/3;
++      }
++}
++
++
++static struct posix_acl *
++_nfsv4_to_posix_one(struct nfs4_acl *n4acl, unsigned int flags)
++{
++      struct posix_acl *pacl;
++      int error = -EINVAL, nace = 0;
++      struct list_head *p;
++      struct nfs4_ace *mask_ace = NULL;
++      struct posix_acl_entry *pace;
++
++      nace = calculate_posix_ace_count(n4acl);
++      if (nace < 0)
++              goto out_err;
++
++      pacl = posix_acl_alloc(nace, GFP_KERNEL);
++      error = -ENOMEM;
++      if (pacl == NULL)
++              goto out_err;
++
++      pace = &pacl->a_entries[0];
++      p = &n4acl->ace_head;
++
++      error = user_obj_from_v4(n4acl, &p, pacl, &pace, flags);
++      if (error)
++              goto out_acl;
++
++      error = users_from_v4(n4acl, &p, &mask_ace, pacl, &pace, flags);
++      if (error)
++              goto out_acl;
++
++      error = group_obj_and_groups_from_v4(n4acl, &p, &mask_ace, pacl, &pace,
++                                              flags);
++      if (error)
++              goto out_acl;
++
++      error = mask_from_v4(n4acl, &p, &mask_ace, pacl, &pace, flags);
++      if (error)
++              goto out_acl;
++      error = other_from_v4(n4acl, &p, pacl, &pace, flags);
++      if (error)
++              goto out_acl;
++
++      error = -EINVAL;
++      if (p->next != &n4acl->ace_head)
++              goto out_acl;
++      if (pace != pacl->a_entries + pacl->a_count)
++              goto out_acl;
++
++      sort_pacl(pacl);
++
++      return pacl;
++out_acl:
++      posix_acl_release(pacl);
++out_err:
++      pacl = ERR_PTR(error);
++      return pacl;
++}
++
++int
++nfs4_acl_split(struct nfs4_acl *acl, struct nfs4_acl *dacl)
++{
++      struct list_head *h, *n;
++      struct nfs4_ace *ace;
++      int error = 0;
++
++      list_for_each_safe(h, n, &acl->ace_head) {
++              ace = list_entry(h, struct nfs4_ace, l_ace);
++
++              if ((ace->flag & NFS4_INHERITANCE_FLAGS)
++                              != NFS4_INHERITANCE_FLAGS)
++                      continue;
++
++              error = nfs4_acl_add_ace(dacl, ace->type, ace->flag,
++                              ace->access_mask, ace->whotype, ace->who) == -1;
++              if (error < 0)
++                      goto out;
++
++              list_del(h);
++              kfree(ace);
++              acl->naces--;
++      }
++
++out:
++      return error;
++}
++
++static short
++ace2type(struct nfs4_ace *ace)
++{
++      switch (ace->whotype) {
++              case NFS4_ACL_WHO_NAMED:
++                      return (ace->flag & NFS4_ACE_IDENTIFIER_GROUP ?
++                                      ACL_GROUP : ACL_USER);
++              case NFS4_ACL_WHO_OWNER:
++                      return ACL_USER_OBJ;
++              case NFS4_ACL_WHO_GROUP:
++                      return ACL_GROUP_OBJ;
++              case NFS4_ACL_WHO_EVERYONE:
++                      return ACL_OTHER;
++      }
++      BUG();
++      return -1;
++}
++
++EXPORT_SYMBOL(nfs4_acl_posix_to_nfsv4);
++EXPORT_SYMBOL(nfs4_acl_nfsv4_to_posix);
++
++struct nfs4_acl *
++nfs4_acl_new(void)
++{
++      struct nfs4_acl *acl;
++
++      if ((acl = kmalloc(sizeof(*acl), GFP_KERNEL)) == NULL)
++              return NULL;
++
++      acl->naces = 0;
++      INIT_LIST_HEAD(&acl->ace_head);
++
++      return acl;
++}
++
++void
++nfs4_acl_free(struct nfs4_acl *acl)
++{
++      struct list_head *h;
++      struct nfs4_ace *ace;
++
++      if (!acl)
++              return;
++
++      while (!list_empty(&acl->ace_head)) {
++              h = acl->ace_head.next;
++              list_del(h);
++              ace = list_entry(h, struct nfs4_ace, l_ace);
++              kfree(ace);
++      }
++
++      kfree(acl);
++
++      return;
++}
++
++int
++nfs4_acl_add_ace(struct nfs4_acl *acl, u32 type, u32 flag, u32 access_mask,
++              int whotype, uid_t who)
++{
++      struct nfs4_ace *ace;
++
++      if ((ace = kmalloc(sizeof(*ace), GFP_KERNEL)) == NULL)
++              return -1;
++
++      ace->type = type;
++      ace->flag = flag;
++      ace->access_mask = access_mask;
++      ace->whotype = whotype;
++      ace->who = who;
++
++      list_add_tail(&ace->l_ace, &acl->ace_head);
++      acl->naces++;
++
++      return 0;
++}
++
++static struct {
++      char *string;
++      int   stringlen;
++      int type;
++} s2t_map[] = {
++      {
++              .string    = "OWNER@",
++              .stringlen = sizeof("OWNER@") - 1,
++              .type      = NFS4_ACL_WHO_OWNER,
++      },
++      {
++              .string    = "GROUP@",
++              .stringlen = sizeof("GROUP@") - 1,
++              .type      = NFS4_ACL_WHO_GROUP,
++      },
++      {
++              .string    = "EVERYONE@",
++              .stringlen = sizeof("EVERYONE@") - 1,
++              .type      = NFS4_ACL_WHO_EVERYONE,
++      },
++};
++
++int
++nfs4_acl_get_whotype(char *p, u32 len)
++{
++      int i;
++
++      for (i=0; i < sizeof(s2t_map) / sizeof(*s2t_map); i++) {
++              if (s2t_map[i].stringlen == len &&
++                              0 == memcmp(s2t_map[i].string, p, len))
++                      return s2t_map[i].type;
++      }
++      return NFS4_ACL_WHO_NAMED;
++}
++
++int
++nfs4_acl_write_who(int who, char *p)
++{
++      int i;
++
++      for (i=0; i < sizeof(s2t_map) / sizeof(*s2t_map); i++) {
++              if (s2t_map[i].type == who) {
++                      memcpy(p, s2t_map[i].string, s2t_map[i].stringlen);
++                      return s2t_map[i].stringlen;
++              }
++      }
++      BUG();
++      return -1;
++}
++
++static inline int
++match_who(struct nfs4_ace *ace, uid_t owner, gid_t group, uid_t who)
++{
++      switch (ace->whotype) {
++              case NFS4_ACL_WHO_NAMED:
++                      return who == ace->who;
++              case NFS4_ACL_WHO_OWNER:
++                      return who == owner;
++              case NFS4_ACL_WHO_GROUP:
++                      return who == group;
++              case NFS4_ACL_WHO_EVERYONE:
++                      return 1;
++              default:
++                      return 0;
++      }
++}
++
++/* 0 = granted, -EACCES = denied; mask is an nfsv4 mask, not mode bits */
++int
++nfs4_acl_permission(struct nfs4_acl *acl, uid_t owner, gid_t group,
++                      uid_t who, u32 mask)
++{
++      struct nfs4_ace *ace;
++      u32 allowed = 0;
++
++      list_for_each_entry(ace, &acl->ace_head, l_ace) {
++              if (!match_who(ace, group, owner, who))
++                      continue;
++              switch (ace->type) {
++                      case NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE:
++                              allowed |= ace->access_mask;
++                              if ((allowed & mask) == mask)
++                                      return 0;
++                              break;
++                      case NFS4_ACE_ACCESS_DENIED_ACE_TYPE:
++                              if (ace->access_mask & mask)
++                                      return -EACCES;
++                              break;
++              }
++      }
++      return -EACCES;
++}
++
++EXPORT_SYMBOL(nfs4_acl_new);
++EXPORT_SYMBOL(nfs4_acl_free);
++EXPORT_SYMBOL(nfs4_acl_add_ace);
++EXPORT_SYMBOL(nfs4_acl_get_whotype);
++EXPORT_SYMBOL(nfs4_acl_write_who);
++EXPORT_SYMBOL(nfs4_acl_permission);
+--- linux-2.6.7/fs/nfsd/Makefile.lsec  2004-06-15 23:19:13.000000000 -0600
++++ linux-2.6.7/fs/nfsd/Makefile       2005-03-23 14:28:24.461331008 -0700
+@@ -7,5 +7,6 @@ obj-$(CONFIG_NFSD)     += nfsd.o
+ nfsd-y                        := nfssvc.o nfsctl.o nfsproc.o nfsfh.o vfs.o \
+                          export.o auth.o lockd.o nfscache.o nfsxdr.o stats.o
+ nfsd-$(CONFIG_NFSD_V3)        += nfs3proc.o nfs3xdr.o
+-nfsd-$(CONFIG_NFSD_V4)        += nfs4proc.o nfs4xdr.o nfs4state.o nfs4idmap.o
++nfsd-$(CONFIG_NFSD_V4)        += nfs4proc.o nfs4xdr.o nfs4state.o nfs4idmap.o \
++                         nfs4acl.o nfs4callback.o
+ nfsd-objs             := $(nfsd-y)
+--- linux-2.6.7/fs/nfsd/nfsctl.c.lsec  2004-06-15 23:19:01.000000000 -0600
++++ linux-2.6.7/fs/nfsd/nfsctl.c       2005-03-23 14:28:24.132381016 -0700
+@@ -36,7 +36,7 @@
+ #include <asm/uaccess.h>
+ /*
+- *    We have a single directory with 8 nodes in it.
++ *    We have a single directory with 9 nodes in it.
+  */
+ enum {
+       NFSD_Root = 1,
+@@ -50,6 +50,7 @@ enum {
+       NFSD_List,
+       NFSD_Fh,
+       NFSD_Threads,
++      NFSD_Leasetime,
+ };
+ /*
+@@ -64,6 +65,7 @@ static ssize_t write_getfd(struct file *
+ static ssize_t write_getfs(struct file *file, char *buf, size_t size);
+ static ssize_t write_filehandle(struct file *file, char *buf, size_t size);
+ static ssize_t write_threads(struct file *file, char *buf, size_t size);
++static ssize_t write_leasetime(struct file *file, char *buf, size_t size);
+ static ssize_t (*write_op[])(struct file *, char *, size_t) = {
+       [NFSD_Svc] = write_svc,
+@@ -75,6 +77,7 @@ static ssize_t (*write_op[])(struct file
+       [NFSD_Getfs] = write_getfs,
+       [NFSD_Fh] = write_filehandle,
+       [NFSD_Threads] = write_threads,
++      [NFSD_Leasetime] = write_leasetime,
+ };
+ /* an argresp is stored in an allocated page and holds the 
+@@ -393,6 +396,29 @@ static ssize_t write_threads(struct file
+       return strlen(buf);
+ }
++extern time_t nfs4_leasetime(void);
++
++static ssize_t write_leasetime(struct file *file, char *buf, size_t size)
++{
++      /* if size > 10 seconds, call
++       * nfs4_reset_lease() then write out the new lease (seconds) as reply
++       */
++      char *mesg = buf;
++      int rv;
++
++      if (size > 0) {
++              int lease;
++              rv = get_int(&mesg, &lease);
++              if (rv)
++                      return rv;
++              if (lease < 10 || lease > 3600)
++                      return -EINVAL;
++              nfs4_reset_lease(lease);
++      }
++      sprintf(buf, "%ld\n", nfs4_lease_time());
++      return strlen(buf);
++}
++
+ /*----------------------------------------------------------------------------*/
+ /*
+  *    populating the filesystem.
+@@ -411,6 +437,7 @@ static int nfsd_fill_super(struct super_
+               [NFSD_List] = {"exports", &exports_operations, S_IRUGO},
+               [NFSD_Fh] = {"filehandle", &transaction_ops, S_IWUSR|S_IRUSR},
+               [NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR},
++              [NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR},
+               /* last one */ {""}
+       };
+       return simple_fill_super(sb, 0x6e667364, nfsd_files);
+--- linux-2.6.7/fs/nfs/callback_proc.c.lsec    2005-03-23 14:28:22.485631360 -0700
++++ linux-2.6.7/fs/nfs/callback_proc.c 2005-03-23 14:28:22.485631360 -0700
+@@ -0,0 +1,85 @@
++/*
++ * linux/fs/nfs/callback_proc.c
++ *
++ * Copyright (C) 2004 Trond Myklebust
++ *
++ * NFSv4 callback procedures
++ */
++#include <linux/config.h>
++#include <linux/nfs4.h>
++#include <linux/nfs_fs.h>
++#include "callback.h"
++#include "delegation.h"
++
++#define NFSDBG_FACILITY NFSDBG_CALLBACK
++ 
++unsigned nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *res)
++{
++      struct nfs4_client *clp;
++      struct nfs_delegation *delegation;
++      struct nfs_inode *nfsi;
++      struct inode *inode;
++      
++      res->bitmap[0] = res->bitmap[1] = 0;
++      res->status = htonl(NFS4ERR_BADHANDLE);
++      clp = nfs4_find_client(&args->addr->sin_addr);
++      if (clp == NULL)
++              goto out;
++      inode = nfs_delegation_find_inode(clp, &args->fh);
++      if (inode == NULL)
++              goto out_putclient;
++      nfsi = NFS_I(inode);
++      down_read(&nfsi->rwsem);
++      delegation = nfsi->delegation;
++      if (delegation == NULL || (delegation->type & FMODE_WRITE) == 0)
++              goto out_iput;
++      res->size = i_size_read(inode);
++      res->change_attr = NFS_CHANGE_ATTR(inode);
++      res->ctime = inode->i_ctime;
++      res->mtime = inode->i_mtime;
++      res->bitmap[0] = (FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE) &
++              args->bitmap[0];
++      res->bitmap[1] = (FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY) &
++              args->bitmap[1];
++      res->status = 0;
++out_iput:
++      up_read(&nfsi->rwsem);
++      iput(inode);
++out_putclient:
++      nfs4_put_client(clp);
++out:
++      dprintk("%s: exit with status = %d\n", __FUNCTION__, ntohl(res->status));
++      return res->status;
++}
++
++unsigned nfs4_callback_recall(struct cb_recallargs *args, void *dummy)
++{
++      struct nfs4_client *clp;
++      struct inode *inode;
++      unsigned res;
++      
++      res = htonl(NFS4ERR_BADHANDLE);
++      clp = nfs4_find_client(&args->addr->sin_addr);
++      if (clp == NULL)
++              goto out;
++      inode = nfs_delegation_find_inode(clp, &args->fh);
++      if (inode == NULL)
++              goto out_putclient;
++      /* Set up a helper thread to actually return the delegation */
++      switch(nfs_async_inode_return_delegation(inode, &args->stateid)) {
++              case 0:
++                      res = 0;
++                      break;
++              case -ENOENT:
++                      res = htonl(NFS4ERR_BAD_STATEID);
++                      break;
++              default:
++                      res = htonl(NFS4ERR_RESOURCE);
++      }
++      iput(inode);
++out_putclient:
++      nfs4_put_client(clp);
++out:
++      dprintk("%s: exit with status = %d\n", __FUNCTION__, ntohl(res));
++      return res;
++}
+--- linux-2.6.7/fs/nfs/delegation.c.lsec       2005-03-23 14:28:22.546622088 -0700
++++ linux-2.6.7/fs/nfs/delegation.c    2005-03-23 14:28:22.545622240 -0700
+@@ -0,0 +1,320 @@
++/*
++ * linux/fs/nfs/delegation.c
++ *
++ * Copyright (C) 2004 Trond Myklebust
++ *
++ * NFS file delegation management
++ *
++ */
++#include <linux/config.h>
++#include <linux/completion.h>
++#include <linux/module.h>
++#include <linux/sched.h>
++#include <linux/spinlock.h>
++
++#include <linux/nfs4.h>
++#include <linux/nfs_fs.h>
++#include <linux/nfs_xdr.h>
++
++#include "delegation.h"
++
++static struct nfs_delegation *nfs_alloc_delegation(void)
++{
++      return (struct nfs_delegation *)kmalloc(sizeof(struct nfs_delegation), GFP_KERNEL);
++}
++
++static void nfs_free_delegation(struct nfs_delegation *delegation)
++{
++      if (delegation->cred)
++              put_rpccred(delegation->cred);
++      kfree(delegation);
++}
++
++static void nfs_delegation_claim_opens(struct inode *inode)
++{
++      struct nfs_inode *nfsi = NFS_I(inode);
++      struct nfs_open_context *ctx;
++      struct nfs4_state *state;
++
++again:
++      spin_lock(&inode->i_lock);
++      list_for_each_entry(ctx, &nfsi->open_files, list) {
++              state = ctx->state;
++              if (state == NULL)
++                      continue;
++              if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
++                      continue;
++              get_nfs_open_context(ctx);
++              spin_unlock(&inode->i_lock);
++              if (nfs4_open_delegation_recall(ctx->dentry, state) < 0)
++                      return;
++              put_nfs_open_context(ctx);
++              goto again;
++      }
++      spin_unlock(&inode->i_lock);
++}
++
++/*
++ * Set up a delegation on an inode
++ */
++void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
++{
++      struct nfs_delegation *delegation = NFS_I(inode)->delegation;
++
++      if (delegation == NULL)
++              return;
++      memcpy(delegation->stateid.data, res->delegation.data,
++                      sizeof(delegation->stateid.data));
++      delegation->type = res->delegation_type;
++      delegation->maxsize = res->maxsize;
++      put_rpccred(cred);
++      delegation->cred = get_rpccred(cred);
++      delegation->flags &= ~NFS_DELEGATION_NEED_RECLAIM;
++      NFS_I(inode)->delegation_state = delegation->type;
++      wmb();
++}
++
++/*
++ * Set up a delegation on an inode
++ */
++int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
++{
++      struct nfs4_client *clp = NFS_SERVER(inode)->nfs4_state;
++      struct nfs_inode *nfsi = NFS_I(inode);
++      struct nfs_delegation *delegation;
++      int status = 0;
++
++      delegation = nfs_alloc_delegation();
++      if (delegation == NULL)
++              return -ENOMEM;
++      memcpy(delegation->stateid.data, res->delegation.data,
++                      sizeof(delegation->stateid.data));
++      delegation->type = res->delegation_type;
++      delegation->maxsize = res->maxsize;
++      delegation->cred = get_rpccred(cred);
++      delegation->inode = inode;
++
++      spin_lock(&clp->cl_lock);
++      if (nfsi->delegation == NULL) {
++              list_add(&delegation->super_list, &clp->cl_delegations);
++              nfsi->delegation = delegation;
++              nfsi->delegation_state = delegation->type;
++              delegation = NULL;
++      } else {
++              if (memcmp(&delegation->stateid, &nfsi->delegation->stateid,
++                                      sizeof(delegation->stateid)) != 0 ||
++                              delegation->type != nfsi->delegation->type) {
++                      printk("%s: server %u.%u.%u.%u, handed out a duplicate delegation!\n",
++                                      __FUNCTION__, NIPQUAD(clp->cl_addr));
++                      status = -EIO;
++              }
++      }
++      spin_unlock(&clp->cl_lock);
++      if (delegation != NULL)
++              kfree(delegation);
++      return status;
++}
++
++static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation)
++{
++      int res = 0;
++
++      __nfs_revalidate_inode(NFS_SERVER(inode), inode);
++
++      res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid);
++      nfs_free_delegation(delegation);
++      return res;
++}
++
++/* Sync all data to disk upon delegation return */
++static void nfs_msync_inode(struct inode *inode)
++{
++      down(&inode->i_sem);
++      filemap_fdatawrite(inode->i_mapping);
++      nfs_wb_all(inode);
++      filemap_fdatawait(inode->i_mapping);
++      up(&inode->i_sem);
++}
++
++/*
++ * Basic procedure for returning a delegation to the server
++ */
++int nfs_inode_return_delegation(struct inode *inode)
++{
++      struct nfs4_client *clp = NFS_SERVER(inode)->nfs4_state;
++      struct nfs_inode *nfsi = NFS_I(inode);
++      struct nfs_delegation *delegation;
++      int res = 0;
++
++      nfs_msync_inode(inode);
++      down_read(&clp->cl_sem);
++      /* Guard against new delegated open calls */
++      down_write(&nfsi->rwsem);
++      spin_lock(&clp->cl_lock);
++      delegation = nfsi->delegation;
++      if (delegation != NULL) {
++              list_del_init(&delegation->super_list);
++              nfsi->delegation = NULL;
++              nfsi->delegation_state = 0;
++      }
++      spin_unlock(&clp->cl_lock);
++      nfs_delegation_claim_opens(inode);
++      up_write(&nfsi->rwsem);
++      up_read(&clp->cl_sem);
++      nfs_msync_inode(inode);
++
++      if (delegation != NULL)
++              res = nfs_do_return_delegation(inode, delegation);
++      return res;
++}
++
++/*
++ * Return all delegations associated to a super block
++ */
++void nfs_return_all_delegations(struct super_block *sb)
++{
++      struct nfs4_client *clp = NFS_SB(sb)->nfs4_state;
++      struct nfs_delegation *delegation;
++      struct inode *inode;
++
++      if (clp == NULL)
++              return;
++restart:
++      spin_lock(&clp->cl_lock);
++      list_for_each_entry(delegation, &clp->cl_delegations, super_list) {
++              if (delegation->inode->i_sb != sb)
++                      continue;
++              inode = igrab(delegation->inode);
++              if (inode == NULL)
++                      continue;
++              spin_unlock(&clp->cl_lock);
++              nfs_inode_return_delegation(inode);
++              iput(inode);
++              goto restart;
++      }
++      spin_unlock(&clp->cl_lock);
++}
++
++struct recall_threadargs {
++      struct inode *inode;
++      struct nfs4_client *clp;
++      const nfs4_stateid *stateid;
++
++      struct completion started;
++      int result;
++};
++
++static int recall_thread(void *data)
++{
++      struct recall_threadargs *args = (struct recall_threadargs *)data;
++      struct inode *inode = igrab(args->inode);
++      struct nfs4_client *clp = NFS_SERVER(inode)->nfs4_state;
++      struct nfs_inode *nfsi = NFS_I(inode);
++      struct nfs_delegation *delegation;
++
++      daemonize("nfsv4-delegreturn");
++
++      nfs_msync_inode(inode);
++      down_read(&clp->cl_sem);
++      down_write(&nfsi->rwsem);
++      spin_lock(&clp->cl_lock);
++      delegation = nfsi->delegation;
++      if (delegation != NULL && memcmp(delegation->stateid.data,
++                              args->stateid->data,
++                              sizeof(delegation->stateid.data)) == 0) {
++              list_del_init(&delegation->super_list);
++              nfsi->delegation = NULL;
++              nfsi->delegation_state = 0;
++              args->result = 0;
++      } else {
++              delegation = NULL;
++              args->result = -ENOENT;
++      }
++      spin_unlock(&clp->cl_lock);
++      complete(&args->started);
++      nfs_delegation_claim_opens(inode);
++      up_write(&nfsi->rwsem);
++      up_read(&clp->cl_sem);
++      nfs_msync_inode(inode);
++
++      if (delegation != NULL)
++              nfs_do_return_delegation(inode, delegation);
++      iput(inode);
++      module_put_and_exit(0);
++}
++
++/*
++ * Asynchronous delegation recall!
++ */
++int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid)
++{
++      struct recall_threadargs data = {
++              .inode = inode,
++              .stateid = stateid,
++      };
++      int status;
++
++      init_completion(&data.started);
++      __module_get(THIS_MODULE);
++      status = kernel_thread(recall_thread, &data, CLONE_KERNEL);
++      if (status < 0)
++              goto out_module_put;
++      wait_for_completion(&data.started);
++      return data.result;
++out_module_put:
++      module_put(THIS_MODULE);
++      return status;
++}
++
++/*
++ * Retrieve the inode associated with a delegation
++ */
++struct inode *nfs_delegation_find_inode(struct nfs4_client *clp, const struct nfs_fh *fhandle)
++{
++      struct nfs_delegation *delegation;
++      struct inode *res = NULL;
++      spin_lock(&clp->cl_lock);
++      list_for_each_entry(delegation, &clp->cl_delegations, super_list) {
++              if (nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) {
++                      res = igrab(delegation->inode);
++                      break;
++              }
++      }
++      spin_unlock(&clp->cl_lock);
++      return res;
++}
++
++/*
++ * Mark all delegations as needing to be reclaimed
++ */
++void nfs_delegation_mark_reclaim(struct nfs4_client *clp)
++{
++      struct nfs_delegation *delegation;
++      spin_lock(&clp->cl_lock);
++      list_for_each_entry(delegation, &clp->cl_delegations, super_list)
++              delegation->flags |= NFS_DELEGATION_NEED_RECLAIM;
++      spin_unlock(&clp->cl_lock);
++}
++
++/*
++ * Reap all unclaimed delegations after reboot recovery is done
++ */
++void nfs_delegation_reap_unclaimed(struct nfs4_client *clp)
++{
++      struct nfs_delegation *delegation, *n;
++      LIST_HEAD(head);
++      spin_lock(&clp->cl_lock);
++      list_for_each_entry_safe(delegation, n, &clp->cl_delegations, super_list) {
++              if ((delegation->flags & NFS_DELEGATION_NEED_RECLAIM) == 0)
++                      continue;
++              list_move(&delegation->super_list, &head);
++              NFS_I(delegation->inode)->delegation = NULL;
++              NFS_I(delegation->inode)->delegation_state = 0;
++      }
++      spin_unlock(&clp->cl_lock);
++      while(!list_empty(&head)) {
++              delegation = list_entry(head.next, struct nfs_delegation, super_list);
++              list_del(&delegation->super_list);
++              nfs_free_delegation(delegation);
++      }
++}
+--- linux-2.6.7/fs/nfs/delegation.h.lsec       2005-03-23 14:28:22.546622088 -0700
++++ linux-2.6.7/fs/nfs/delegation.h    2005-03-23 14:28:22.546622088 -0700
+@@ -0,0 +1,56 @@
++/*
++ * linux/fs/nfs/delegation.h
++ *
++ * Copyright (c) Trond Myklebust
++ *
++ * Definitions pertaining to NFS delegated files
++ */
++#ifndef FS_NFS_DELEGATION_H
++#define FS_NFS_DELEGATION_H
++
++#if defined(CONFIG_NFS_V4)
++/*
++ * NFSv4 delegation
++ */
++struct nfs_delegation {
++      struct list_head super_list;
++      struct rpc_cred *cred;
++      struct inode *inode;
++      nfs4_stateid stateid;
++      int type;
++#define NFS_DELEGATION_NEED_RECLAIM 1
++      long flags;
++      loff_t maxsize;
++};
++
++int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
++void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
++int nfs_inode_return_delegation(struct inode *inode);
++int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid);
++
++struct inode *nfs_delegation_find_inode(struct nfs4_client *clp, const struct nfs_fh *fhandle);
++void nfs_return_all_delegations(struct super_block *sb);
++
++void nfs_delegation_mark_reclaim(struct nfs4_client *clp);
++void nfs_delegation_reap_unclaimed(struct nfs4_client *clp);
++
++/* NFSv4 delegation-related procedures */
++int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid);
++int nfs4_open_delegation_recall(struct dentry *dentry, struct nfs4_state *state);
++
++static inline int nfs_have_delegation(struct inode *inode, int flags)
++{
++      flags &= FMODE_READ|FMODE_WRITE;
++      rmb();
++      if ((NFS_I(inode)->delegation_state & flags) == flags)
++              return 1;
++      return 0;
++}
++#else
++static inline int nfs_have_delegation(struct inode *inode, int flags)
++{
++      return 0;
++}
++#endif
++
++#endif
+--- linux-2.6.7/fs/nfs/nfs3proc.c.lsec 2004-06-15 23:19:23.000000000 -0600
++++ linux-2.6.7/fs/nfs/nfs3proc.c      2005-03-23 14:28:22.820580440 -0700
+@@ -68,18 +68,6 @@ nfs3_async_handle_jukebox(struct rpc_tas
+       return 1;
+ }
+-static struct rpc_cred *
+-nfs_cred(struct inode *inode, struct file *filp)
+-{
+-      struct rpc_cred *cred = NULL;
+-
+-      if (filp)
+-              cred = (struct rpc_cred *)filp->private_data;
+-      if (!cred)
+-              cred = NFS_I(inode)->mm_cred;
+-      return cred;
+-}
+-
+ /*
+  * Bare-bones access to getattr: this is for nfs_read_super.
+  */
+@@ -164,8 +152,7 @@ nfs3_proc_lookup(struct inode *dir, stru
+       return status;
+ }
+-static int
+-nfs3_proc_access(struct inode *inode, struct rpc_cred *cred, int mode)
++static int nfs3_proc_access(struct inode *inode, struct nfs_access_entry *entry)
+ {
+       struct nfs_fattr        fattr;
+       struct nfs3_accessargs  arg = {
+@@ -178,9 +165,10 @@ nfs3_proc_access(struct inode *inode, st
+               .rpc_proc       = &nfs3_procedures[NFS3PROC_ACCESS],
+               .rpc_argp       = &arg,
+               .rpc_resp       = &res,
+-              .rpc_cred       = cred
++              .rpc_cred       = entry->cred
+       };
+-      int     status;
++      int mode = entry->mask;
++      int status;
+       dprintk("NFS call  access\n");
+       fattr.valid = 0;
+@@ -200,10 +188,16 @@ nfs3_proc_access(struct inode *inode, st
+       }
+       status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
+       nfs_refresh_inode(inode, &fattr);
+-      dprintk("NFS reply access\n");
+-
+-      if (status == 0 && (arg.access & res.access) != arg.access)
+-              status = -EACCES;
++      if (status == 0) {
++              entry->mask = 0;
++              if (res.access & NFS3_ACCESS_READ)
++                      entry->mask |= MAY_READ;
++              if (res.access & (NFS3_ACCESS_MODIFY | NFS3_ACCESS_EXTEND | NFS3_ACCESS_DELETE))
++                      entry->mask |= MAY_WRITE;
++              if (res.access & (NFS3_ACCESS_LOOKUP|NFS3_ACCESS_EXECUTE))
++                      entry->mask |= MAY_EXEC;
++      }
++      dprintk("NFS reply access, status = %d\n", status);
+       return status;
+ }
+@@ -227,8 +221,7 @@ nfs3_proc_readlink(struct inode *inode, 
+       return status;
+ }
+-static int
+-nfs3_proc_read(struct nfs_read_data *rdata, struct file *filp)
++static int nfs3_proc_read(struct nfs_read_data *rdata)
+ {
+       int                     flags = rdata->flags;
+       struct inode *          inode = rdata->inode;
+@@ -237,13 +230,13 @@ nfs3_proc_read(struct nfs_read_data *rda
+               .rpc_proc       = &nfs3_procedures[NFS3PROC_READ],
+               .rpc_argp       = &rdata->args,
+               .rpc_resp       = &rdata->res,
++              .rpc_cred       = rdata->cred,
+       };
+       int                     status;
+       dprintk("NFS call  read %d @ %Ld\n", rdata->args.count,
+                       (long long) rdata->args.offset);
+       fattr->valid = 0;
+-      msg.rpc_cred = nfs_cred(inode, filp);
+       status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
+       if (status >= 0)
+               nfs_refresh_inode(inode, fattr);
+@@ -251,8 +244,7 @@ nfs3_proc_read(struct nfs_read_data *rda
+       return status;
+ }
+-static int
+-nfs3_proc_write(struct nfs_write_data *wdata, struct file *filp)
++static int nfs3_proc_write(struct nfs_write_data *wdata)
+ {
+       int                     rpcflags = wdata->flags;
+       struct inode *          inode = wdata->inode;
+@@ -261,13 +253,13 @@ nfs3_proc_write(struct nfs_write_data *w
+               .rpc_proc       = &nfs3_procedures[NFS3PROC_WRITE],
+               .rpc_argp       = &wdata->args,
+               .rpc_resp       = &wdata->res,
++              .rpc_cred       = wdata->cred,
+       };
+       int                     status;
+       dprintk("NFS call  write %d @ %Ld\n", wdata->args.count,
+                       (long long) wdata->args.offset);
+       fattr->valid = 0;
+-      msg.rpc_cred = nfs_cred(inode, filp);
+       status = rpc_call_sync(NFS_CLIENT(inode), &msg, rpcflags);
+       if (status >= 0)
+               nfs_refresh_inode(inode, fattr);
+@@ -275,8 +267,7 @@ nfs3_proc_write(struct nfs_write_data *w
+       return status < 0? status : wdata->res.count;
+ }
+-static int
+-nfs3_proc_commit(struct nfs_write_data *cdata, struct file *filp)
++static int nfs3_proc_commit(struct nfs_write_data *cdata)
+ {
+       struct inode *          inode = cdata->inode;
+       struct nfs_fattr *      fattr = cdata->res.fattr;
+@@ -284,13 +275,13 @@ nfs3_proc_commit(struct nfs_write_data *
+               .rpc_proc       = &nfs3_procedures[NFS3PROC_COMMIT],
+               .rpc_argp       = &cdata->args,
+               .rpc_resp       = &cdata->res,
++              .rpc_cred       = cdata->cred,
+       };
+       int                     status;
+       dprintk("NFS call  commit %d @ %Ld\n", cdata->args.count,
+                       (long long) cdata->args.offset);
+       fattr->valid = 0;
+-      msg.rpc_cred = nfs_cred(inode, filp);
+       status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
+       if (status >= 0)
+               nfs_refresh_inode(inode, fattr);
+@@ -534,6 +525,8 @@ nfs3_proc_symlink(struct inode *dir, str
+       };
+       int                     status;
++      if (path->len > NFS3_MAXPATHLEN)
++              return -ENAMETOOLONG;
+       dprintk("NFS call  symlink %s -> %s\n", name->name, path->name);
+       dir_attr.valid = 0;
+       fattr->valid = 0;
+@@ -832,27 +825,6 @@ nfs3_proc_commit_setup(struct nfs_write_
+       rpc_call_setup(task, &msg, 0);
+ }
+-/*
+- * Set up the nfspage struct with the right credentials
+- */
+-void
+-nfs3_request_init(struct nfs_page *req, struct file *filp)
+-{
+-      req->wb_cred = get_rpccred(nfs_cred(req->wb_inode, filp));
+-}
+-
+-static int
+-nfs3_request_compatible(struct nfs_page *req, struct file *filp, struct page *page)
+-{
+-      if (req->wb_file != filp)
+-              return 0;
+-      if (req->wb_page != page)
+-              return 0;
+-      if (req->wb_cred != nfs_file_cred(filp))
+-              return 0;
+-      return 1;
+-}
+-
+ static int
+ nfs3_proc_lock(struct file *filp, int cmd, struct file_lock *fl)
+ {
+@@ -863,6 +835,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,
+@@ -892,7 +865,5 @@ struct nfs_rpc_ops nfs_v3_clientops = {
+       .commit_setup   = nfs3_proc_commit_setup,
+       .file_open      = nfs_open,
+       .file_release   = nfs_release,
+-      .request_init   = nfs3_request_init,
+-      .request_compatible = nfs3_request_compatible,
+       .lock           = nfs3_proc_lock,
+ };
+--- linux-2.6.7/fs/nfs/proc.c.lsec     2004-06-15 23:20:03.000000000 -0600
++++ linux-2.6.7/fs/nfs/proc.c  2005-03-23 14:28:23.058544264 -0700
+@@ -49,18 +49,6 @@
+ extern struct rpc_procinfo nfs_procedures[];
+-static struct rpc_cred *
+-nfs_cred(struct inode *inode, struct file *filp)
+-{
+-      struct rpc_cred *cred = NULL;
+-
+-      if (filp)
+-              cred = (struct rpc_cred *)filp->private_data;
+-      if (!cred)
+-              cred = NFS_I(inode)->mm_cred;
+-      return cred;
+-}
+-
+ /*
+  * Bare-bones access to getattr: this is for nfs_read_super.
+  */
+@@ -167,8 +155,7 @@ nfs_proc_readlink(struct inode *inode, s
+       return status;
+ }
+-static int
+-nfs_proc_read(struct nfs_read_data *rdata, struct file *filp)
++static int nfs_proc_read(struct nfs_read_data *rdata)
+ {
+       int                     flags = rdata->flags;
+       struct inode *          inode = rdata->inode;
+@@ -177,15 +164,14 @@ nfs_proc_read(struct nfs_read_data *rdat
+               .rpc_proc       = &nfs_procedures[NFSPROC_READ],
+               .rpc_argp       = &rdata->args,
+               .rpc_resp       = &rdata->res,
++              .rpc_resp       = rdata->cred,
+       };
+       int                     status;
+       dprintk("NFS call  read %d @ %Ld\n", rdata->args.count,
+                       (long long) rdata->args.offset);
+       fattr->valid = 0;
+-      msg.rpc_cred = nfs_cred(inode, filp);
+       status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
+-
+       if (status >= 0) {
+               nfs_refresh_inode(inode, fattr);
+               /* Emulate the eof flag, which isn't normally needed in NFSv2
+@@ -198,8 +184,7 @@ nfs_proc_read(struct nfs_read_data *rdat
+       return status;
+ }
+-static int
+-nfs_proc_write(struct nfs_write_data *wdata, struct file *filp)
++static int nfs_proc_write(struct nfs_write_data *wdata)
+ {
+       int                     flags = wdata->flags;
+       struct inode *          inode = wdata->inode;
+@@ -208,13 +193,13 @@ nfs_proc_write(struct nfs_write_data *wd
+               .rpc_proc       = &nfs_procedures[NFSPROC_WRITE],
+               .rpc_argp       = &wdata->args,
+               .rpc_resp       = &wdata->res,
++              .rpc_resp       = wdata->cred,
+       };
+       int                     status;
+       dprintk("NFS call  write %d @ %Ld\n", wdata->args.count,
+                       (long long) wdata->args.offset);
+       fattr->valid = 0;
+-      msg.rpc_cred = nfs_cred(inode, filp);
+       status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
+       if (status >= 0) {
+               nfs_refresh_inode(inode, fattr);
+@@ -400,6 +385,8 @@ nfs_proc_symlink(struct inode *dir, stru
+       };
+       int                     status;
++      if (path->len > NFS2_MAXPATHLEN)
++              return -ENAMETOOLONG;
+       dprintk("NFS call  symlink %s -> %s\n", name->name, path->name);
+       fattr->valid = 0;
+       status = rpc_call(NFS_CLIENT(dir), NFSPROC_SYMLINK, &arg, NULL, 0);
+@@ -619,27 +606,6 @@ nfs_proc_commit_setup(struct nfs_write_d
+       BUG();
+ }
+-/*
+- * Set up the nfspage struct with the right credentials
+- */
+-static void
+-nfs_request_init(struct nfs_page *req, struct file *filp)
+-{
+-      req->wb_cred = get_rpccred(nfs_cred(req->wb_inode, filp));
+-}
+-
+-static int
+-nfs_request_compatible(struct nfs_page *req, struct file *filp, struct page *page)
+-{
+-      if (req->wb_file != filp)
+-              return 0;
+-      if (req->wb_page != page)
+-              return 0;
+-      if (req->wb_cred != nfs_file_cred(filp))
+-              return 0;
+-      return 1;
+-}
+-
+ static int
+ nfs_proc_lock(struct file *filp, int cmd, struct file_lock *fl)
+ {
+@@ -651,6 +617,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,
+@@ -680,7 +647,5 @@ struct nfs_rpc_ops nfs_v2_clientops = {
+       .commit_setup   = nfs_proc_commit_setup,
+       .file_open      = nfs_open,
+       .file_release   = nfs_release,
+-      .request_init   = nfs_request_init,
+-      .request_compatible = nfs_request_compatible,
+       .lock           = nfs_proc_lock,
+ };
+--- linux-2.6.7/fs/nfs/file.c.lsec     2004-06-15 23:19:37.000000000 -0600
++++ linux-2.6.7/fs/nfs/file.c  2005-03-23 14:28:22.760589560 -0700
+@@ -31,6 +31,8 @@
+ #include <asm/uaccess.h>
+ #include <asm/system.h>
++#include "delegation.h"
++
+ #define NFSDBG_FACILITY               NFSDBG_FILE
+ static long nfs_file_fcntl(int fd, unsigned int cmd,
+@@ -66,6 +68,19 @@ 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,
++      .getxattr       = nfs_getxattr,
++      .setxattr       = nfs_setxattr,
++      .listxattr      = nfs_listxattr,
++};
++
++#endif /* CONFIG_NFS_V4 */
++
+ /* Hack for future NFS swap support */
+ #ifndef IS_SWAPFILE
+ # define IS_SWAPFILE(inode)   (0)
+@@ -127,6 +142,7 @@ nfs_file_release(struct inode *inode, st
+ static int
+ nfs_file_flush(struct file *file)
+ {
++      struct nfs_open_context *ctx = (struct nfs_open_context *)file->private_data;
+       struct inode    *inode = file->f_dentry->d_inode;
+       int             status;
+@@ -138,9 +154,9 @@ nfs_file_flush(struct file *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)
++              status = ctx->error;
++              ctx->error = 0;
++              if (!status && !nfs_have_delegation(inode, FMODE_READ))
+                       __nfs_revalidate_inode(NFS_SERVER(inode), inode);
+       }
+       unlock_kernel();
+@@ -211,6 +227,7 @@ nfs_file_mmap(struct file * file, struct
+ static int
+ nfs_fsync(struct file *file, struct dentry *dentry, int datasync)
+ {
++      struct nfs_open_context *ctx = (struct nfs_open_context *)file->private_data;
+       struct inode *inode = dentry->d_inode;
+       int status;
+@@ -219,8 +236,8 @@ nfs_fsync(struct file *file, struct dent
+       lock_kernel();
+       status = nfs_wb_all(inode);
+       if (!status) {
+-              status = file->f_error;
+-              file->f_error = 0;
++              status = ctx->error;
++              ctx->error = 0;
+       }
+       unlock_kernel();
+       return status;
+@@ -302,6 +319,90 @@ out_swapfile:
+       goto out;
+ }
++static int do_getlk(struct file *filp, int cmd, struct file_lock *fl)
++{
++      struct inode *inode = filp->f_mapping->host;
++      int status;
++
++      lock_kernel();
++      status = NFS_PROTO(inode)->lock(filp, cmd, fl);
++      unlock_kernel();
++      return status;
++}
++
++static int do_unlk(struct file *filp, int cmd, struct file_lock *fl)
++{
++      struct inode *inode = filp->f_mapping->host;
++      sigset_t oldset;
++      int status;
++
++      rpc_clnt_sigmask(NFS_CLIENT(inode), &oldset);
++      /*
++       * Flush all pending writes before doing anything
++       * with locks..
++       */
++      filemap_fdatawrite(filp->f_mapping);
++      down(&inode->i_sem);
++      nfs_wb_all(inode);
++      up(&inode->i_sem);
++      filemap_fdatawait(filp->f_mapping);
++
++      /* NOTE: special case
++       *      If we're signalled while cleaning up locks on process exit, we
++       *      still need to complete the unlock.
++       */
++      lock_kernel();
++      status = NFS_PROTO(inode)->lock(filp, cmd, fl);
++      rpc_clnt_sigunmask(NFS_CLIENT(inode), &oldset);
++      return status;
++}
++
++static int do_setlk(struct file *filp, int cmd, struct file_lock *fl)
++{
++      struct inode *inode = filp->f_mapping->host;
++      int status;
++
++      /*
++       * Flush all pending writes before doing anything
++       * with locks..
++       */
++      status = filemap_fdatawrite(filp->f_mapping);
++      if (status == 0) {
++              down(&inode->i_sem);
++              status = nfs_wb_all(inode);
++              up(&inode->i_sem);
++              if (status == 0)
++                      status = filemap_fdatawait(filp->f_mapping);
++      }
++      if (status < 0)
++              return status;
++
++      lock_kernel();
++      status = NFS_PROTO(inode)->lock(filp, cmd, fl);
++      /* If we were signalled we still need to ensure that
++       * we clean up any state on the server. We therefore
++       * record the lock call as having succeeded in order to
++       * ensure that locks_remove_posix() cleans it out when
++       * the process exits.
++       */
++      if (status == -EINTR || status == -ERESTARTSYS)
++              posix_lock_file(filp, fl);
++      unlock_kernel();
++      if (status < 0)
++              return status;
++      /*
++       * Make sure we clear the cache whenever we try to get the lock.
++       * This makes locking act as a cache coherency point.
++       */
++      filemap_fdatawrite(filp->f_mapping);
++      down(&inode->i_sem);
++      nfs_wb_all(inode);      /* we may have slept */
++      up(&inode->i_sem);
++      filemap_fdatawait(filp->f_mapping);
++      nfs_zap_caches(inode);
++      return 0;
++}
++
+ /*
+  * Lock a (portion of) a file
+  */
+@@ -309,8 +410,6 @@ int
+ nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
+ {
+       struct inode * inode = filp->f_mapping->host;
+-      int     status = 0;
+-      int     status2;
+       dprintk("NFS: nfs_lock(f=%s/%ld, t=%x, fl=%x, r=%Ld:%Ld)\n",
+                       inode->i_sb->s_id, inode->i_ino,
+@@ -328,8 +427,8 @@ nfs_lock(struct file *filp, int cmd, str
+               /* Fake OK code if mounted without NLM support */
+               if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM) {
+                       if (IS_GETLK(cmd))
+-                              status = LOCK_USE_CLNT;
+-                      goto out_ok;
++                              return LOCK_USE_CLNT;
++                      return 0;
+               }
+       }
+@@ -340,45 +439,12 @@ nfs_lock(struct file *filp, int cmd, str
+        * Not sure whether that would be unique, though, or whether
+        * that would break in other places.
+        */
+-      if (!fl->fl_owner || !(fl->fl_flags & FL_POSIX))
++      if (!(fl->fl_flags & FL_POSIX))
+               return -ENOLCK;
+-      /*
+-       * Flush all pending writes before doing anything
+-       * with locks..
+-       */
+-      status = filemap_fdatawrite(filp->f_mapping);
+-      down(&inode->i_sem);
+-      status2 = nfs_wb_all(inode);
+-      if (!status)
+-              status = status2;
+-      up(&inode->i_sem);
+-      status2 = filemap_fdatawait(filp->f_mapping);
+-      if (!status)
+-              status = status2;
+-      if (status < 0)
+-              return status;
+-
+-      lock_kernel();
+-      status = NFS_PROTO(inode)->lock(filp, cmd, fl);
+-      unlock_kernel();
+-      if (status < 0)
+-              return status;
+-      
+-      status = 0;
+-
+-      /*
+-       * Make sure we clear the cache whenever we try to get the lock.
+-       * This makes locking act as a cache coherency point.
+-       */
+- out_ok:
+-      if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) {
+-              filemap_fdatawrite(filp->f_mapping);
+-              down(&inode->i_sem);
+-              nfs_wb_all(inode);      /* we may have slept */
+-              up(&inode->i_sem);
+-              filemap_fdatawait(filp->f_mapping);
+-              nfs_zap_caches(inode);
+-      }
+-      return status;
++      if (IS_GETLK(cmd))
++              return do_getlk(filp, cmd, fl);
++      if (fl->fl_type == F_UNLCK)
++              return do_unlk(filp, cmd, fl);
++      return do_setlk(filp, cmd, fl);
+ }
+--- linux-2.6.7/fs/nfs/write.c.lsec    2004-06-15 23:19:43.000000000 -0600
++++ linux-2.6.7/fs/nfs/write.c 2005-03-23 14:28:23.225518880 -0700
+@@ -63,6 +63,8 @@
+ #include <linux/smp_lock.h>
+ #include <linux/mempool.h>
++#include "delegation.h"
++
+ #define NFSDBG_FACILITY               NFSDBG_PAGECACHE
+ #define MIN_POOL_WRITE                (32)
+@@ -71,7 +73,8 @@
+ /*
+  * Local function declarations
+  */
+-static struct nfs_page * nfs_update_request(struct file*, struct inode *,
++static struct nfs_page * nfs_update_request(struct nfs_open_context*,
++                                          struct inode *,
+                                           struct page *,
+                                           unsigned int, unsigned int);
+ static void nfs_writeback_done_partial(struct nfs_write_data *, int);
+@@ -173,7 +176,7 @@ static void nfs_mark_uptodate(struct pag
+  * Write a page synchronously.
+  * Offset is the data offset within the page.
+  */
+-static int nfs_writepage_sync(struct file *file, struct inode *inode,
++static int nfs_writepage_sync(struct nfs_open_context *ctx, struct inode *inode,
+               struct page *page, unsigned int offset, unsigned int count,
+               int how)
+ {
+@@ -187,9 +190,10 @@ static int nfs_writepage_sync(struct fil
+       memset(wdata, 0, sizeof(*wdata));
+       wdata->flags = how;
++      wdata->cred = ctx->cred;
+       wdata->inode = inode;
+       wdata->args.fh = NFS_FH(inode);
+-      wdata->args.lockowner = current->files;
++      wdata->args.context = ctx;
+       wdata->args.pages = &page;
+       wdata->args.stable = NFS_FILE_SYNC;
+       wdata->args.pgbase = offset;
+@@ -208,7 +212,7 @@ static int nfs_writepage_sync(struct fil
+                       wdata->args.count = count;
+               wdata->args.offset = page_offset(page) + wdata->args.pgbase;
+-              result = NFS_PROTO(inode)->write(wdata, file);
++              result = NFS_PROTO(inode)->write(wdata);
+               if (result < 0) {
+                       /* Must mark the page invalid after I/O error */
+@@ -241,13 +245,14 @@ io_error:
+       return written ? written : result;
+ }
+-static int nfs_writepage_async(struct file *file, struct inode *inode,
+-              struct page *page, unsigned int offset, unsigned int count)
++static int nfs_writepage_async(struct nfs_open_context *ctx,
++              struct inode *inode, struct page *page,
++              unsigned int offset, unsigned int count)
+ {
+       struct nfs_page *req;
+       int             status;
+-      req = nfs_update_request(file, inode, page, offset, count);
++      req = nfs_update_request(ctx, inode, page, offset, count);
+       status = (IS_ERR(req)) ? PTR_ERR(req) : 0;
+       if (status < 0)
+               goto out;
+@@ -274,6 +279,7 @@ static int wb_priority(struct writeback_
+  */
+ int nfs_writepage(struct page *page, struct writeback_control *wbc)
+ {
++      struct nfs_open_context *ctx;
+       struct inode *inode = page->mapping->host;
+       unsigned long end_index;
+       unsigned offset = PAGE_CACHE_SIZE;
+@@ -308,16 +314,21 @@ int nfs_writepage(struct page *page, str
+       if (page->index >= end_index+1 || !offset)
+               goto out;
+ do_it:
++      ctx = nfs_find_open_context(inode, FMODE_WRITE);
++      if (ctx == NULL) {
++              err = -EBADF;
++              goto out;
++      }
+       lock_kernel();
+       if (!IS_SYNC(inode) && inode_referenced) {
+-              err = nfs_writepage_async(NULL, inode, page, 0, offset);
++              err = nfs_writepage_async(ctx, inode, page, 0, offset);
+               if (err >= 0) {
+                       err = 0;
+                       if (wbc->for_reclaim)
+                               nfs_flush_inode(inode, 0, 0, FLUSH_STABLE);
+               }
+       } else {
+-              err = nfs_writepage_sync(NULL, inode, page, 0,
++              err = nfs_writepage_sync(ctx, inode, page, 0,
+                                               offset, priority);
+               if (err >= 0) {
+                       if (err != offset)
+@@ -326,6 +337,7 @@ do_it:
+               }
+       }
+       unlock_kernel();
++      put_nfs_open_context(ctx);
+ out:
+       unlock_page(page);
+       if (inode_referenced)
+@@ -374,8 +386,7 @@ out:
+ /*
+  * Insert a write request into an inode
+  */
+-static inline int
+-nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
++static int nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
+ {
+       struct nfs_inode *nfsi = NFS_I(inode);
+       int error;
+@@ -387,6 +398,8 @@ nfs_inode_add_request(struct inode *inod
+       if (!nfsi->npages) {
+               igrab(inode);
+               nfs_begin_data_update(inode);
++              if (nfs_have_delegation(inode, FMODE_WRITE))
++                      nfsi->change_attr++;
+       }
+       nfsi->npages++;
+       req->wb_count++;
+@@ -404,7 +417,7 @@ nfs_inode_remove_request(struct nfs_page
+       BUG_ON (!NFS_WBACK_BUSY(req));
+       spin_lock(&nfs_wreq_lock);
+-      inode = req->wb_inode;
++      inode = req->wb_context->dentry->d_inode;
+       nfsi = NFS_I(inode);
+       radix_tree_delete(&nfsi->nfs_page_tree, req->wb_index);
+       nfsi->npages--;
+@@ -450,7 +463,7 @@ nfs_find_request(struct inode *inode, un
+ static void
+ nfs_mark_request_dirty(struct nfs_page *req)
+ {
+-      struct inode *inode = req->wb_inode;
++      struct inode *inode = req->wb_context->dentry->d_inode;
+       struct nfs_inode *nfsi = NFS_I(inode);
+       spin_lock(&nfs_wreq_lock);
+@@ -467,7 +480,7 @@ nfs_mark_request_dirty(struct nfs_page *
+ static inline int
+ nfs_dirty_request(struct nfs_page *req)
+ {
+-      struct nfs_inode *nfsi = NFS_I(req->wb_inode);
++      struct nfs_inode *nfsi = NFS_I(req->wb_context->dentry->d_inode);
+       return !list_empty(&req->wb_list) && req->wb_list_head == &nfsi->dirty;
+ }
+@@ -478,7 +491,7 @@ nfs_dirty_request(struct nfs_page *req)
+ static void
+ nfs_mark_request_commit(struct nfs_page *req)
+ {
+-      struct inode *inode = req->wb_inode;
++      struct inode *inode = req->wb_context->dentry->d_inode;
+       struct nfs_inode *nfsi = NFS_I(inode);
+       spin_lock(&nfs_wreq_lock);
+@@ -619,9 +632,9 @@ static int nfs_wait_on_write_congestion(
+  *
+  * Note: Should always be called with the Page Lock held!
+  */
+-static struct nfs_page *
+-nfs_update_request(struct file* file, struct inode *inode, struct page *page,
+-                 unsigned int offset, unsigned int bytes)
++static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx,
++              struct inode *inode, struct page *page,
++              unsigned int offset, unsigned int bytes)
+ {
+       struct nfs_server *server = NFS_SERVER(inode);
+       struct nfs_page         *req, *new = NULL;
+@@ -668,13 +681,9 @@ nfs_update_request(struct file* file, st
+               }
+               spin_unlock(&nfs_wreq_lock);
+-              new = nfs_create_request(file, inode, page, offset, bytes);
++              new = nfs_create_request(ctx, inode, page, offset, bytes);
+               if (IS_ERR(new))
+                       return new;
+-              if (file) {
+-                      new->wb_file = file;
+-                      get_file(file);
+-              }
+       }
+       /* We have a request for our page.
+@@ -684,7 +693,7 @@ nfs_update_request(struct file* file, st
+        * request.
+        */
+       rqend = req->wb_offset + req->wb_bytes;
+-      if (req->wb_file != file
++      if (req->wb_context != ctx
+           || req->wb_page != page
+           || !nfs_dirty_request(req)
+           || offset > rqend || end < req->wb_offset) {
+@@ -705,9 +714,9 @@ nfs_update_request(struct file* file, st
+       return req;
+ }
+-int
+-nfs_flush_incompatible(struct file *file, struct page *page)
++int nfs_flush_incompatible(struct file *file, struct page *page)
+ {
++      struct nfs_open_context *ctx = (struct nfs_open_context *)file->private_data;
+       struct inode    *inode = page->mapping->host;
+       struct nfs_page *req;
+       int             status = 0;
+@@ -721,7 +730,7 @@ nfs_flush_incompatible(struct file *file
+        */
+       req = nfs_find_request(inode, page->index);
+       if (req) {
+-              if (!NFS_PROTO(inode)->request_compatible(req, file, page))
++              if (req->wb_page != page || ctx != req->wb_context)
+                       status = nfs_wb_page(inode, page);
+               nfs_release_request(req);
+       }
+@@ -737,6 +746,7 @@ nfs_flush_incompatible(struct file *file
+ int nfs_updatepage(struct file *file, struct page *page,
+               unsigned int offset, unsigned int count)
+ {
++      struct nfs_open_context *ctx = (struct nfs_open_context *)file->private_data;
+       struct dentry   *dentry = file->f_dentry;
+       struct inode    *inode = page->mapping->host;
+       struct nfs_page *req;
+@@ -747,7 +757,7 @@ int nfs_updatepage(struct file *file, st
+               count, (long long)(page_offset(page) +offset));
+       if (IS_SYNC(inode)) {
+-              status = nfs_writepage_sync(file, inode, page, offset, count, 0);
++              status = nfs_writepage_sync(ctx, inode, page, offset, count, 0);
+               if (status > 0) {
+                       if (offset == 0 && status == PAGE_CACHE_SIZE)
+                               SetPageUptodate(page);
+@@ -784,7 +794,7 @@ int nfs_updatepage(struct file *file, st
+        * it out now.
+        */
+       do {
+-              req = nfs_update_request(file, inode, page, offset, count);
++              req = nfs_update_request(ctx, inode, page, offset, count);
+               status = (IS_ERR(req)) ? PTR_ERR(req) : 0;
+               if (status != -EBUSY)
+                       break;
+@@ -860,16 +870,15 @@ static void nfs_write_rpcsetup(struct nf
+        * NB: take care not to mess about with data->commit et al. */
+       data->req = req;
+-      data->inode = inode = req->wb_inode;
+-      data->cred = req->wb_cred;
++      data->inode = inode = req->wb_context->dentry->d_inode;
++      data->cred = req->wb_context->cred;
+       data->args.fh     = NFS_FH(inode);
+       data->args.offset = req_offset(req) + offset;
+       data->args.pgbase = req->wb_pgbase + offset;
+       data->args.pages  = data->pagevec;
+       data->args.count  = count;
+-      data->args.lockowner = req->wb_lockowner;
+-      data->args.state  = req->wb_state;
++      data->args.context = req->wb_context;
+       data->res.fattr   = &data->fattr;
+       data->res.count   = count;
+@@ -1029,7 +1038,7 @@ nfs_flush_list(struct list_head *head, i
+       while (!list_empty(head)) {
+               pages += nfs_coalesce_requests(head, &one_request, wpages);
+               req = nfs_list_entry(one_request.next);
+-              error = nfs_flush_one(&one_request, req->wb_inode, how);
++              error = nfs_flush_one(&one_request, req->wb_context->dentry->d_inode, how);
+               if (error < 0)
+                       break;
+       }
+@@ -1054,16 +1063,15 @@ static void nfs_writeback_done_partial(s
+       struct page             *page = req->wb_page;
+       dprintk("NFS: write (%s/%Ld %d@%Ld)",
+-              req->wb_inode->i_sb->s_id,
+-              (long long)NFS_FILEID(req->wb_inode),
++              req->wb_context->dentry->d_inode->i_sb->s_id,
++              (long long)NFS_FILEID(req->wb_context->dentry->d_inode),
+               req->wb_bytes,
+               (long long)req_offset(req));
+       if (status < 0) {
+               ClearPageUptodate(page);
+               SetPageError(page);
+-              if (req->wb_file)
+-                      req->wb_file->f_error = status;
++              req->wb_context->error = status;
+               dprintk(", error = %d\n", status);
+       } else {
+ #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
+@@ -1104,16 +1112,15 @@ static void nfs_writeback_done_full(stru
+               page = req->wb_page;
+               dprintk("NFS: write (%s/%Ld %d@%Ld)",
+-                      req->wb_inode->i_sb->s_id,
+-                      (long long)NFS_FILEID(req->wb_inode),
++                      req->wb_context->dentry->d_inode->i_sb->s_id,
++                      (long long)NFS_FILEID(req->wb_context->dentry->d_inode),
+                       req->wb_bytes,
+                       (long long)req_offset(req));
+               if (status < 0) {
+                       ClearPageUptodate(page);
+                       SetPageError(page);
+-                      if (req->wb_file)
+-                              req->wb_file->f_error = status;
++                      req->wb_context->error = status;
+                       end_page_writeback(page);
+                       nfs_inode_remove_request(req);
+                       dprintk(", error = %d\n", status);
+@@ -1232,7 +1239,7 @@ static void nfs_commit_rpcsetup(struct l
+       list_splice_init(head, &data->pages);
+       first = nfs_list_entry(data->pages.next);
+       last = nfs_list_entry(data->pages.prev);
+-      inode = first->wb_inode;
++      inode = first->wb_context->dentry->d_inode;
+       /*
+        * Determine the offset range of requests in the COMMIT call.
+@@ -1246,7 +1253,7 @@ static void nfs_commit_rpcsetup(struct l
+               len = 0;
+       data->inode       = inode;
+-      data->cred        = first->wb_cred;
++      data->cred        = first->wb_context->cred;
+       data->args.fh     = NFS_FH(data->inode);
+       data->args.offset = start;
+@@ -1313,13 +1320,12 @@ nfs_commit_done(struct rpc_task *task)
+               nfs_list_remove_request(req);
+               dprintk("NFS: commit (%s/%Ld %d@%Ld)",
+-                      req->wb_inode->i_sb->s_id,
+-                      (long long)NFS_FILEID(req->wb_inode),
++                      req->wb_context->dentry->d_inode->i_sb->s_id,
++                      (long long)NFS_FILEID(req->wb_context->dentry->d_inode),
+                       req->wb_bytes,
+                       (long long)req_offset(req));
+               if (task->tk_status < 0) {
+-                      if (req->wb_file)
+-                              req->wb_file->f_error = task->tk_status;
++                      req->wb_context->error = task->tk_status;
+                       nfs_inode_remove_request(req);
+                       dprintk(", error = %d\n", task->tk_status);
+                       goto next;
+--- linux-2.6.7/fs/nfs/nfs4xdr.c.lsec  2004-06-15 23:20:26.000000000 -0600
++++ linux-2.6.7/fs/nfs/nfs4xdr.c       2005-03-23 14:28:23.056544568 -0700
+@@ -84,9 +84,13 @@ static int nfs_stat_to_errno(int);
+                               ((3+NFS4_FHSIZE) >> 2))
+ #define encode_getattr_maxsz    (op_encode_hdr_maxsz + 3)
+ #define nfs4_name_maxsz               (1 + ((3 + NFS4_MAXNAMLEN) >> 2))
++#define nfs4_path_maxsz               (1 + ((3 + NFS4_MAXPATHLEN) >> 2))
+ #define nfs4_fattr_bitmap_maxsz (36 + 2 * nfs4_name_maxsz)
+ #define decode_getattr_maxsz    (op_decode_hdr_maxsz + 3 + \
+                                 nfs4_fattr_bitmap_maxsz)
++#define encode_setattr_maxsz  (op_decode_hdr_maxsz + 4 + \
++                              nfs4_fattr_bitmap_maxsz)
++#define decode_setattr_maxsz  (op_decode_hdr_maxsz + 3)
+ #define encode_savefh_maxsz     (op_encode_hdr_maxsz)
+ #define decode_savefh_maxsz     (op_decode_hdr_maxsz)
+ #define encode_fsinfo_maxsz   (op_encode_hdr_maxsz + 2)
+@@ -118,10 +122,17 @@ static int nfs_stat_to_errno(int);
+ #define encode_link_maxsz     (op_encode_hdr_maxsz + \
+                               nfs4_name_maxsz)
+ #define decode_link_maxsz     (op_decode_hdr_maxsz + 5)
++#define encode_symlink_maxsz  (op_encode_hdr_maxsz + \
++                              1 + nfs4_name_maxsz + \
++                              nfs4_path_maxsz + \
++                              nfs4_fattr_bitmap_maxsz)
++#define decode_symlink_maxsz  (op_decode_hdr_maxsz + 8)
+ #define encode_create_maxsz   (op_encode_hdr_maxsz + \
+-                              2 + 2 * nfs4_name_maxsz + \
++                              2 + nfs4_name_maxsz + \
+                               nfs4_fattr_bitmap_maxsz)
+ #define decode_create_maxsz   (op_decode_hdr_maxsz + 8)
++#define encode_delegreturn_maxsz (op_encode_hdr_maxsz + 4)
++#define decode_delegreturn_maxsz (op_decode_hdr_maxsz)
+ #define NFS4_enc_compound_sz  (1024)  /* XXX: large enough? */
+ #define NFS4_dec_compound_sz  (1024)  /* XXX: large enough? */
+ #define NFS4_enc_read_sz      (compound_encode_hdr_maxsz + \
+@@ -172,16 +183,14 @@ static int nfs_stat_to_errno(int);
+ #define NFS4_dec_open_confirm_sz        (compound_decode_hdr_maxsz + \
+                                         decode_putfh_maxsz + \
+                                         op_decode_hdr_maxsz + 4)
+-#define NFS4_enc_open_reclaim_sz      (compound_encode_hdr_maxsz + \
++#define NFS4_enc_open_noattr_sz       (compound_encode_hdr_maxsz + \
+                                       encode_putfh_maxsz + \
+                                       op_encode_hdr_maxsz + \
+-                                      11 + \
+-                                      encode_getattr_maxsz)
+-#define NFS4_dec_open_reclaim_sz      (compound_decode_hdr_maxsz + \
++                                      11)
++#define NFS4_dec_open_noattr_sz       (compound_decode_hdr_maxsz + \
+                                       decode_putfh_maxsz + \
+                                       op_decode_hdr_maxsz + \
+-                                      4 + 5 + 2 + 3 + \
+-                                      decode_getattr_maxsz)
++                                      4 + 5 + 2 + 3)
+ #define NFS4_enc_open_downgrade_sz \
+                               (compound_encode_hdr_maxsz + \
+                                 encode_putfh_maxsz + \
+@@ -313,6 +322,16 @@ static int nfs_stat_to_errno(int);
+                               decode_savefh_maxsz + \
+                               decode_putfh_maxsz + \
+                               decode_link_maxsz)
++#define NFS4_enc_symlink_sz   (compound_encode_hdr_maxsz + \
++                              encode_putfh_maxsz + \
++                              encode_symlink_maxsz + \
++                              encode_getattr_maxsz + \
++                              encode_getfh_maxsz)
++#define NFS4_dec_symlink_sz   (compound_decode_hdr_maxsz + \
++                              decode_putfh_maxsz + \
++                              decode_symlink_maxsz + \
++                              decode_getattr_maxsz + \
++                              decode_getfh_maxsz)
+ #define NFS4_enc_create_sz    (compound_encode_hdr_maxsz + \
+                               encode_putfh_maxsz + \
+                               encode_create_maxsz + \
+@@ -339,6 +358,33 @@ static int nfs_stat_to_errno(int);
+                               encode_getattr_maxsz)
+ #define NFS4_dec_server_caps_sz (compound_decode_hdr_maxsz + \
+                               decode_getattr_maxsz)
++#define NFS4_enc_delegreturn_sz       (compound_encode_hdr_maxsz + \
++                              encode_putfh_maxsz + \
++                              encode_delegreturn_maxsz)
++#define NFS4_dec_delegreturn_sz (compound_decode_hdr_maxsz + \
++                              decode_delegreturn_maxsz)
++#define username_maxsz                (1 + ((IDMAP_NAMESZ + 3) >> 2))
++/* XXX: fix ACL bounds */
++#define ace_maxsz             (3 + username_maxsz)
++#define NFS_ACL_MAX_ENTRIES   32
++#define acl_maxentries                ((NFS_ACL_MAX_ENTRIES - 3) * 3 + 6)
++#define acl_maxsz             (1 + acl_maxentries * ace_maxsz)
++#define NFS4_enc_getacl_sz    compound_encode_hdr_maxsz + \
++                              encode_putfh_maxsz + \
++                              encode_getattr_maxsz
++#define username_maxsz                (1 + ((IDMAP_NAMESZ + 3) >> 2))
++#define ace_maxsz             (3 + username_maxsz)
++#define acl_maxentries                ((NFS_ACL_MAX_ENTRIES - 3) * 3 + 6)
++#define acl_maxsz             (1 + acl_maxentries * ace_maxsz)
++#define NFS4_dec_getacl_sz    (compound_decode_hdr_maxsz + \
++                              decode_putfh_maxsz + \
++                              op_decode_hdr_maxsz + 3 + 1 + acl_maxsz)
++#define NFS4_enc_setacl_sz    (compound_encode_hdr_maxsz + \
++                              encode_putfh_maxsz + \
++                              op_encode_hdr_maxsz + 4 + 1 + acl_maxsz)
++#define NFS4_dec_setacl_sz    (compound_decode_hdr_maxsz + \
++                              decode_putfh_maxsz + \
++                              decode_setattr_maxsz)
+ static struct {
+       unsigned int    mode;
+@@ -388,6 +434,15 @@ struct compound_hdr {
+       BUG_ON(!p);                                             \
+ } while (0)
++static void encode_string(struct xdr_stream *xdr, unsigned int len, const char *str)
++{
++      uint32_t *p;
++
++      p = xdr_reserve_space(xdr, 4 + len);
++      BUG_ON(p == NULL);
++      xdr_encode_opaque(p, str, len);
++}
++
+ static int encode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr)
+ {
+       uint32_t *p;
+@@ -402,6 +457,15 @@ static int encode_compound_hdr(struct xd
+       return 0;
+ }
++static void encode_nfs4_verifier(struct xdr_stream *xdr, const nfs4_verifier *verf)
++{
++      uint32_t *p;
++
++      p = xdr_reserve_space(xdr, NFS4_VERIFIER_SIZE);
++      BUG_ON(p == NULL);
++      xdr_encode_opaque_fixed(p, verf->data, NFS4_VERIFIER_SIZE);
++}
++
+ static int encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const struct nfs_server *server)
+ {
+       char owner_name[IDMAP_NAMESZ];
+@@ -420,7 +484,7 @@ static int encode_attrs(struct xdr_strea
+        * In the worst-case, this would be
+        *   12(bitmap) + 4(attrlen) + 8(size) + 4(mode) + 4(atime) + 4(mtime)
+        *          = 36 bytes, plus any contribution from variable-length fields
+-       *            such as owner/group/acl's.
++       *            such as owner/group.
+        */
+       len = 16;
+@@ -742,19 +806,12 @@ static int encode_lookup(struct xdr_stre
+       return 0;
+ }
+-static int encode_open(struct xdr_stream *xdr, const struct nfs_openargs *arg)
++static void encode_share_access(struct xdr_stream *xdr, int open_flags)
+ {
+-      int status;
+       uint32_t *p;
+- /*
+- * opcode 4, seqid 4, share_access 4, share_deny 4, clientid 8, ownerlen 4,
+- * owner 4, opentype 4 = 36
+- */
+-      RESERVE_SPACE(36);
+-      WRITE32(OP_OPEN);
+-      WRITE32(arg->seqid);
+-      switch (arg->share_access) {
++      RESERVE_SPACE(8);
++      switch (open_flags & (FMODE_READ|FMODE_WRITE)) {
+               case FMODE_READ:
+                       WRITE32(NFS4_SHARE_ACCESS_READ);
+                       break;
+@@ -767,84 +824,135 @@ static int encode_open(struct xdr_stream
+               default:
+                       BUG();
+       }
+-      WRITE32(0);                  /* for linux, share_deny = 0 always */
++      WRITE32(0);             /* for linux, share_deny = 0 always */
++}
++
++static inline void encode_openhdr(struct xdr_stream *xdr, const struct nfs_openargs *arg)
++{
++      uint32_t *p;
++ /*
++ * opcode 4, seqid 4, share_access 4, share_deny 4, clientid 8, ownerlen 4,
++ * owner 4 = 32
++ */
++      RESERVE_SPACE(8);
++      WRITE32(OP_OPEN);
++      WRITE32(arg->seqid);
++      encode_share_access(xdr, arg->open_flags);
++      RESERVE_SPACE(16);
+       WRITE64(arg->clientid);
+       WRITE32(4);
+       WRITE32(arg->id);
+-      WRITE32(arg->opentype);
++}
+-      if (arg->opentype == NFS4_OPEN_CREATE) {
+-              if (arg->createmode == NFS4_CREATE_EXCLUSIVE) {
+-                      RESERVE_SPACE(12);
+-                      WRITE32(arg->createmode);
+-                      WRITEMEM(arg->u.verifier.data, sizeof(arg->u.verifier.data));
+-              }
+-              else if (arg->u.attrs) {
+-                      RESERVE_SPACE(4);
+-                      WRITE32(arg->createmode);
+-                      if ((status = encode_attrs(xdr, arg->u.attrs, arg->server)))
+-                              return status;
+-              }
+-              else {
+-                      RESERVE_SPACE(12);
+-                      WRITE32(arg->createmode);
+-                      WRITE32(0);
+-                      WRITE32(0);
+-              }
++static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_openargs *arg)
++{
++      uint32_t *p;
++
++      RESERVE_SPACE(4);
++      switch(arg->open_flags & O_EXCL) {
++              case 0:
++                      WRITE32(NFS4_CREATE_UNCHECKED);
++                      encode_attrs(xdr, arg->u.attrs, arg->server);
++                      break;
++              default:
++                      WRITE32(NFS4_CREATE_EXCLUSIVE);
++                      encode_nfs4_verifier(xdr, &arg->u.verifier);
+       }
++}
+-      RESERVE_SPACE(8 + arg->name->len);
+-      WRITE32(NFS4_OPEN_CLAIM_NULL);
+-      WRITE32(arg->name->len);
+-      WRITEMEM(arg->name->name, arg->name->len);
++static void encode_opentype(struct xdr_stream *xdr, const struct nfs_openargs *arg)
++{
++      uint32_t *p;
+-      return 0;
++      RESERVE_SPACE(4);
++      switch (arg->open_flags & O_CREAT) {
++              case 0:
++                      WRITE32(NFS4_OPEN_NOCREATE);
++                      break;
++              default:
++                      BUG_ON(arg->claim != NFS4_OPEN_CLAIM_NULL);
++                      WRITE32(NFS4_OPEN_CREATE);
++                      encode_createmode(xdr, arg);
++      }
+ }
+-static int encode_open_confirm(struct xdr_stream *xdr, const struct nfs_open_confirmargs *arg)
++static inline void encode_delegation_type(struct xdr_stream *xdr, int delegation_type)
+ {
+       uint32_t *p;
+-      RESERVE_SPACE(8+sizeof(arg->stateid.data));
+-      WRITE32(OP_OPEN_CONFIRM);
+-      WRITEMEM(arg->stateid.data, sizeof(arg->stateid.data));
+-      WRITE32(arg->seqid);
++      RESERVE_SPACE(4);
++      switch (delegation_type) {
++              case 0:
++                      WRITE32(NFS4_OPEN_DELEGATE_NONE);
++                      break;
++              case FMODE_READ:
++                      WRITE32(NFS4_OPEN_DELEGATE_READ);
++                      break;
++              case FMODE_WRITE|FMODE_READ:
++                      WRITE32(NFS4_OPEN_DELEGATE_WRITE);
++                      break;
++              default:
++                      BUG();
++      }
++}
+-      return 0;
++static inline void encode_claim_null(struct xdr_stream *xdr, const struct qstr *name)
++{
++      uint32_t *p;
++
++      RESERVE_SPACE(4);
++      WRITE32(NFS4_OPEN_CLAIM_NULL);
++      encode_string(xdr, name->len, name->name);
+ }
++static inline void encode_claim_previous(struct xdr_stream *xdr, int type)
++{
++      uint32_t *p;
++
++      RESERVE_SPACE(4);
++      WRITE32(NFS4_OPEN_CLAIM_PREVIOUS);
++      encode_delegation_type(xdr, type);
++}
+-static int encode_open_reclaim(struct xdr_stream *xdr, const struct nfs_open_reclaimargs *arg)
++static inline void encode_claim_delegate_cur(struct xdr_stream *xdr, const struct qstr *name, const nfs4_stateid *stateid)
+ {
+       uint32_t *p;
+- /*
+- * opcode 4, seqid 4, share_access 4, share_deny 4, clientid 8, ownerlen 4,
+- * owner 4, opentype 4, claim 4, delegation_type 4 = 44
+- */
+-      RESERVE_SPACE(44);
+-      WRITE32(OP_OPEN);
+-      WRITE32(arg->seqid);
+-      switch (arg->share_access) {
+-              case FMODE_READ:
+-                      WRITE32(NFS4_SHARE_ACCESS_READ);
++      RESERVE_SPACE(4+sizeof(stateid->data));
++      WRITE32(NFS4_OPEN_CLAIM_DELEGATE_CUR);
++      WRITEMEM(stateid->data, sizeof(stateid->data));
++      encode_string(xdr, name->len, name->name);
++}
++
++static int encode_open(struct xdr_stream *xdr, const struct nfs_openargs *arg)
++{
++      encode_openhdr(xdr, arg);
++      encode_opentype(xdr, arg);
++      switch (arg->claim) {
++              case NFS4_OPEN_CLAIM_NULL:
++                      encode_claim_null(xdr, arg->name);
+                       break;
+-              case FMODE_WRITE:
+-                      WRITE32(NFS4_SHARE_ACCESS_WRITE);
++              case NFS4_OPEN_CLAIM_PREVIOUS:
++                      encode_claim_previous(xdr, arg->u.delegation_type);
+                       break;
+-              case FMODE_READ|FMODE_WRITE:
+-                      WRITE32(NFS4_SHARE_ACCESS_BOTH);
++              case NFS4_OPEN_CLAIM_DELEGATE_CUR:
++                      encode_claim_delegate_cur(xdr, arg->name, &arg->u.delegation);
+                       break;
+               default:
+                       BUG();
+       }
+-      WRITE32(0);                  /* for linux, share_deny = 0 always */
+-      WRITE64(arg->clientid);
+-      WRITE32(4);
+-      WRITE32(arg->id);
+-      WRITE32(NFS4_OPEN_NOCREATE);
+-      WRITE32(NFS4_OPEN_CLAIM_PREVIOUS);
+-      WRITE32(NFS4_OPEN_DELEGATE_NONE);
++      return 0;
++}
++
++static int encode_open_confirm(struct xdr_stream *xdr, const struct nfs_open_confirmargs *arg)
++{
++      uint32_t *p;
++
++      RESERVE_SPACE(8+sizeof(arg->stateid.data));
++      WRITE32(OP_OPEN_CONFIRM);
++      WRITEMEM(arg->stateid.data, sizeof(arg->stateid.data));
++      WRITE32(arg->seqid);
++
+       return 0;
+ }
+@@ -852,14 +960,11 @@ static int encode_open_downgrade(struct 
+ {
+       uint32_t *p;
+-      RESERVE_SPACE(16+sizeof(arg->stateid.data));
++      RESERVE_SPACE(8+sizeof(arg->stateid.data));
+       WRITE32(OP_OPEN_DOWNGRADE);
+       WRITEMEM(arg->stateid.data, sizeof(arg->stateid.data));
+       WRITE32(arg->seqid);
+-      WRITE32(arg->share_access);
+-      /* No deny modes */
+-      WRITE32(0);
+-
++      encode_share_access(xdr, arg->open_flags);
+       return 0;
+ }
+@@ -887,15 +992,15 @@ static int encode_putrootfh(struct xdr_s
+         return 0;
+ }
+-static void encode_stateid(struct xdr_stream *xdr, struct nfs4_state *state, fl_owner_t lockowner)
++static void encode_stateid(struct xdr_stream *xdr, const struct nfs_open_context *ctx)
+ {
+       extern nfs4_stateid zero_stateid;
+       nfs4_stateid stateid;
+       uint32_t *p;
+       RESERVE_SPACE(16);
+-      if (state != NULL) {
+-              nfs4_copy_stateid(&stateid, state, lockowner);
++      if (ctx->state != NULL) {
++              nfs4_copy_stateid(&stateid, ctx->state, ctx->pid);
+               WRITEMEM(stateid.data, sizeof(stateid.data));
+       } else
+               WRITEMEM(zero_stateid.data, sizeof(zero_stateid.data));
+@@ -908,7 +1013,7 @@ static int encode_read(struct xdr_stream
+       RESERVE_SPACE(4);
+       WRITE32(OP_READ);
+-      encode_stateid(xdr, args->state, args->lockowner);
++      encode_stateid(xdr, args->context);
+       RESERVE_SPACE(12);
+       WRITE64(args->offset);
+@@ -1003,6 +1108,45 @@ static int encode_renew(struct xdr_strea
+       return 0;
+ }
++extern nfs4_stateid zero_stateid;
++
++static int
++encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg)
++{
++      uint32_t *p;
++      uint32_t *q = (uint32_t *)arg->acl;
++      uint32_t *end = (uint32_t *)(arg->acl + arg->acl_len);
++      uint32_t tmp;
++      int naces, i;
++
++      RESERVE_SPACE(4+sizeof(zero_stateid.data));
++      WRITE32(OP_SETATTR);
++      WRITEMEM(zero_stateid.data, sizeof(zero_stateid.data));
++      RESERVE_SPACE(4*4);
++      WRITE32(1);
++      WRITE32(FATTR4_WORD0_ACL);
++      WRITE32(arg->acl_len);
++      if (q + 1 > end)
++              return -EINVAL;
++      naces = ntohl(*q++);
++      WRITE32(naces);
++      for (i = 0; i < naces; i++) {
++              if (q + 4 > end)
++                      return -EINVAL;
++              RESERVE_SPACE(3*4);
++              memcpy(p, q, 3*4); /* type, flag, access_mask, length */
++              q += 3;
++              tmp = ntohl(*q++); /* length */
++              if (tmp > XDR_MAX_NETOBJ)
++                      return -EINVAL;
++              if (q + XDR_QUADLEN(tmp) > end)
++                      return -EINVAL;
++              RESERVE_SPACE((XDR_QUADLEN(tmp) << 2) + 4);
++              p = xdr_encode_opaque(p, q, tmp);
++      }
++      return 0;
++}
++
+ static int
+ encode_savefh(struct xdr_stream *xdr)
+ {
+@@ -1031,26 +1175,18 @@ static int encode_setattr(struct xdr_str
+ static int encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclientid *setclientid)
+ {
+-      uint32_t total_len;
+-      uint32_t len1, len2, len3;
+       uint32_t *p;
+-      len1 = strlen(setclientid->sc_name);
+-      len2 = strlen(setclientid->sc_netid);
+-      len3 = strlen(setclientid->sc_uaddr);
+-      total_len = XDR_QUADLEN(len1) + XDR_QUADLEN(len2) + XDR_QUADLEN(len3);
+-      total_len = (total_len << 2) + 24 + sizeof(setclientid->sc_verifier.data);
+-
+-      RESERVE_SPACE(total_len);
++      RESERVE_SPACE(4 + sizeof(setclientid->sc_verifier->data));
+       WRITE32(OP_SETCLIENTID);
+-      WRITEMEM(setclientid->sc_verifier.data, sizeof(setclientid->sc_verifier.data));
+-      WRITE32(len1);
+-      WRITEMEM(setclientid->sc_name, len1);
++      WRITEMEM(setclientid->sc_verifier->data, sizeof(setclientid->sc_verifier->data));
++
++      encode_string(xdr, setclientid->sc_name_len, setclientid->sc_name);
++      RESERVE_SPACE(4);
+       WRITE32(setclientid->sc_prog);
+-      WRITE32(len2);
+-      WRITEMEM(setclientid->sc_netid, len2);
+-      WRITE32(len3);
+-      WRITEMEM(setclientid->sc_uaddr, len3);
++      encode_string(xdr, setclientid->sc_netid_len, setclientid->sc_netid);
++      encode_string(xdr, setclientid->sc_uaddr_len, setclientid->sc_uaddr);
++      RESERVE_SPACE(4);
+       WRITE32(setclientid->sc_cb_ident);
+       return 0;
+@@ -1075,7 +1211,7 @@ static int encode_write(struct xdr_strea
+       RESERVE_SPACE(4);
+       WRITE32(OP_WRITE);
+-      encode_stateid(xdr, args->state, args->lockowner);
++      encode_stateid(xdr, args->context);
+       RESERVE_SPACE(16);
+       WRITE64(args->offset);
+@@ -1086,6 +1222,18 @@ static int encode_write(struct xdr_strea
+       return 0;
+ }
++
++static int encode_delegreturn(struct xdr_stream *xdr, const nfs4_stateid *stateid)
++{
++      uint32_t *p;
++
++      RESERVE_SPACE(20);
++
++      WRITE32(OP_DELEGRETURN);
++      WRITEMEM(stateid->data, sizeof(stateid->data));
++      return 0;
++
++}
+ /*
+  * END OF "GENERIC" ENCODE ROUTINES.
+  */
+@@ -1244,6 +1392,14 @@ out:
+ }
+ /*
++ * Encode SYMLINK request
++ */
++static int nfs4_xdr_enc_symlink(struct rpc_rqst *req, uint32_t *p, const struct nfs4_create_arg *args)
++{
++      return nfs4_xdr_enc_create(req, p, args);
++}
++
++/*
+  * Encode GETATTR request
+  */
+ static int nfs4_xdr_enc_getattr(struct rpc_rqst *req, uint32_t *p, const struct nfs4_getattr_arg *args)
+@@ -1331,13 +1487,13 @@ out:
+ }
+ /*
+- * Encode an OPEN request
++ * Encode an OPEN request with no attributes.
+  */
+-static int nfs4_xdr_enc_open_reclaim(struct rpc_rqst *req, uint32_t *p, struct nfs_open_reclaimargs *args)
++static int nfs4_xdr_enc_open_noattr(struct rpc_rqst *req, uint32_t *p, struct nfs_openargs *args)
+ {
+       struct xdr_stream xdr;
+       struct compound_hdr hdr = {
+-              .nops   = 3,
++              .nops   = 2,
+       };
+       int status;
+@@ -1346,10 +1502,7 @@ static int nfs4_xdr_enc_open_reclaim(str
+       status = encode_putfh(&xdr, args->fh);
+       if (status)
+               goto out;
+-      status = encode_open_reclaim(&xdr, args);
+-      if (status)
+-              goto out;
+-      status = encode_getfattr(&xdr, args->bitmask);
++      status = encode_open(&xdr, args);
+ out:
+       return status;
+ }
+@@ -1538,6 +1691,52 @@ out:
+ }
+ /*
++ * Encode an SETACL request
++ */
++static int
++nfs4_xdr_enc_setacl(struct rpc_rqst *req, uint32_t *p, struct nfs_setaclargs *args)
++
++{
++        struct xdr_stream xdr;
++        struct compound_hdr hdr = {
++                .nops   = 2,
++        };
++        int status;
++
++        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
++        encode_compound_hdr(&xdr, &hdr);
++        status = encode_putfh(&xdr, args->fh);
++        if(status)
++                goto out;
++        status = encode_setacl(&xdr, args);
++out:
++        return status;
++}
++
++/*
++ * Encode a GETACL request
++ */
++static int
++nfs4_xdr_enc_getacl(struct rpc_rqst *req, uint32_t *p,struct nfs_fh *fhandle)
++{
++      struct xdr_stream xdr;
++      struct compound_hdr hdr = {
++              .nops   = 2,
++      };
++      int status;
++
++      xdr_init_encode(&xdr, &req->rq_snd_buf, p);
++      encode_compound_hdr(&xdr, &hdr);
++      status = encode_putfh(&xdr, fhandle);
++      if (status)
++              goto out;
++      status = encode_getattr_two(&xdr, FATTR4_WORD0_ACL, 0);
++out:
++      return status;
++
++}
++
++/*
+  * Encode a WRITE request
+  */
+ static int nfs4_xdr_enc_write(struct rpc_rqst *req, uint32_t *p, struct nfs_writeargs *args)
+@@ -1716,6 +1915,24 @@ static int nfs4_xdr_enc_setclientid_conf
+ }
+ /*
++ * DELEGRETURN request
++ */
++static int nfs4_xdr_enc_delegreturn(struct rpc_rqst *req, uint32_t *p, const struct nfs4_delegreturnargs *args)
++{
++      struct xdr_stream xdr;
++      struct compound_hdr hdr = {
++              .nops = 2,
++      };
++      int status;
++
++      xdr_init_encode(&xdr, &req->rq_snd_buf, p);
++      encode_compound_hdr(&xdr, &hdr);
++      if ((status = encode_putfh(&xdr, args->fhandle)) == 0)
++              status = encode_delegreturn(&xdr, args->stateid);
++      return status;
++}
++
++/*
+  * START OF "GENERIC" DECODE ROUTINES.
+  *   These may look a little ugly since they are imported from a "generic"
+  * set of XDR encode/decode routines which are intended to be shared by
+@@ -1749,6 +1966,17 @@ static int nfs4_xdr_enc_setclientid_conf
+       } \
+ } while (0)
++static int decode_opaque_inline(struct xdr_stream *xdr, uint32_t *len, char **string)
++{
++      uint32_t *p;
++
++      READ_BUF(4);
++      READ32(*len);
++      READ_BUF(*len);
++      *string = (char *)p;
++      return 0;
++}
++
+ static int decode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr)
+ {
+       uint32_t *p;
+@@ -1785,6 +2013,17 @@ static int decode_op_hdr(struct xdr_stre
+       return 0;
+ }
++/* Dummy routine */
++static int decode_ace(struct xdr_stream *xdr, void *ace, struct nfs4_client *clp)
++{
++      uint32_t *p;
++      uint32_t strlen;
++      char *str;
++
++      READ_BUF(12);
++      return decode_opaque_inline(xdr, &strlen, &str);
++}
++
+ static int decode_attr_bitmap(struct xdr_stream *xdr, uint32_t *bitmap)
+ {
+       uint32_t bmlen, *p;
+@@ -2717,10 +2956,56 @@ static int decode_lookup(struct xdr_stre
+       return decode_op_hdr(xdr, OP_LOOKUP);
+ }
++/* This is too sick! */
++static int decode_space_limit(struct xdr_stream *xdr, u64 *maxsize)
++{
++        uint32_t *p;
++      uint32_t limit_type, nblocks, blocksize;
++
++      READ_BUF(12);
++      READ32(limit_type);
++      switch (limit_type) {
++              case 1:
++                      READ64(*maxsize);
++                      break;
++              case 2:
++                      READ32(nblocks);
++                      READ32(blocksize);
++                      *maxsize = (uint64_t)nblocks * (uint64_t)blocksize;
++      }
++      return 0;
++}
++
++static int decode_delegation(struct xdr_stream *xdr, struct nfs_openres *res)
++{
++        uint32_t *p;
++        uint32_t delegation_type;
++
++      READ_BUF(4);
++      READ32(delegation_type);
++      if (delegation_type == NFS4_OPEN_DELEGATE_NONE) {
++              res->delegation_type = 0;
++              return 0;
++      }
++      READ_BUF(20);
++      COPYMEM(res->delegation.data, sizeof(res->delegation.data));
++      READ32(res->do_recall);
++      switch (delegation_type) {
++              case NFS4_OPEN_DELEGATE_READ:
++                      res->delegation_type = FMODE_READ;
++                      break;
++              case NFS4_OPEN_DELEGATE_WRITE:
++                      res->delegation_type = FMODE_WRITE|FMODE_READ;
++                      if (decode_space_limit(xdr, &res->maxsize) < 0)
++                              return -EIO;
++      }
++      return decode_ace(xdr, NULL, res->server->nfs4_state);
++}
++
+ static int decode_open(struct xdr_stream *xdr, struct nfs_openres *res)
+ {
+         uint32_t *p;
+-        uint32_t bmlen, delegation_type;
++        uint32_t bmlen;
+         int status;
+         status = decode_op_hdr(xdr, OP_OPEN);
+@@ -2737,11 +3022,9 @@ static int decode_open(struct xdr_stream
+         if (bmlen > 10)
+                 goto xdr_error;
+-        READ_BUF((bmlen << 2) + 4);
++        READ_BUF(bmlen << 2);
+         p += bmlen;
+-        READ32(delegation_type);
+-        if (delegation_type == NFS4_OPEN_DELEGATE_NONE)
+-              return 0;
++      return decode_delegation(xdr, res);
+ xdr_error:
+       printk(KERN_NOTICE "%s: xdr error!\n", __FUNCTION__);
+       return -EIO;
+@@ -2967,6 +3250,72 @@ static int decode_renew(struct xdr_strea
+       return decode_op_hdr(xdr, OP_RENEW);
+ }
++static int decode_attr_acl(struct xdr_stream *xdr, uint32_t *bitmap,
++                              struct nfs_getaclres *res)
++{
++      uint32_t *p;
++
++      if (unlikely(bitmap[0] & (FATTR4_WORD0_ACL - 1U)))
++              return -EIO;
++      if (likely(bitmap[0] & FATTR4_WORD0_ACL)) {
++              ssize_t size = res->acl_len;
++              uint32_t nace, tmp;
++              u32 *start;
++              int i;
++
++              res->acl_len = 0;
++              READ_BUF(4);
++              start = p;
++              READ32(nace);
++              res->acl_len += 4;
++
++              for (i = 0; i < nace; i++) {
++                      READ_BUF(4*4);
++                      res->acl_len += 4*4;
++                      p += 3;
++                      READ32(tmp); /* namelen */
++                      READ_BUF(tmp);
++                      if (tmp > XDR_MAX_NETOBJ) {
++                              printk(KERN_WARNING "%s: name too long (%u)!\n",
++                                      __FUNCTION__, tmp);
++                              return -EIO;
++                      }
++                      res->acl_len += XDR_QUADLEN(tmp) << 2;
++              }
++              if (size && res->acl_len > size)
++                      return -ERANGE;
++              if (size == 0 && res->acl_len <= XATTR_SIZE_MAX)
++                      res->acl = kmalloc(res->acl_len, GFP_KERNEL);
++              if (res->acl)
++                      memcpy(res->acl, start, res->acl_len);
++      }
++      return 0;
++}
++
++static int decode_getacl(struct xdr_stream *xdr, struct nfs_getaclres *res)
++{
++      uint32_t *savep;
++      uint32_t attrlen,
++               bitmap[2] = {0};
++      int status;
++
++      if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0)
++              goto xdr_error;
++      if ((status = decode_attr_bitmap(xdr, bitmap)) != 0)
++              goto xdr_error;
++      if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0)
++              goto xdr_error;
++
++      if ((status = decode_attr_acl(xdr, bitmap, res)) != 0)
++              goto xdr_error;
++
++      status = verify_attr_len(xdr, savep, attrlen);
++xdr_error:
++      if (status != 0)
++              printk(KERN_NOTICE "%s: xdr error %d!\n", __FUNCTION__, -status);
++      return status;
++}
++
+ static int
+ decode_savefh(struct xdr_stream *xdr)
+ {
+@@ -3048,6 +3397,11 @@ static int decode_write(struct xdr_strea
+       return 0;
+ }
++static int decode_delegreturn(struct xdr_stream *xdr)
++{
++      return decode_op_hdr(xdr, OP_DELEGRETURN);
++}
++
+ /*
+  * Decode OPEN_DOWNGRADE response
+  */
+@@ -3222,6 +3576,14 @@ out:
+ }
+ /*
++ * Decode SYMLINK response
++ */
++static int nfs4_xdr_dec_symlink(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_create_res *res)
++{
++      return nfs4_xdr_dec_create(rqstp, p, res);
++}
++
++/*
+  * Decode GETATTR response
+  */
+ static int nfs4_xdr_dec_getattr(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_getattr_res *res)
+@@ -3243,6 +3605,50 @@ out:
+ }
++/*
++ * Decode SETACL response
++ */
++static int
++nfs4_xdr_dec_setacl(struct rpc_rqst *rqstp, uint32_t *p, void *res)
++{
++      struct xdr_stream xdr;
++      struct compound_hdr hdr