diff -ruN 2.6.22-rc3.o/fs/nfs/Makefile 2.6.22-rc3.c/fs/nfs/Makefile
--- 2.6.22-rc3.o/fs/nfs/Makefile	2007-06-11 02:45:49.000000000 -0400
+++ 2.6.22-rc3.c/fs/nfs/Makefile	2007-06-11 02:57:48.000000000 -0400
@@ -13,7 +13,7 @@
 nfs-$(CONFIG_NFS_V4)	+= nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \
 			   delegation.o idmap.o \
 			   callback.o callback_xdr.o callback_proc.o \
-			   nfs4namespace.o
+			   nfs4namespace.o nfs4_crypt.o
 nfs-$(CONFIG_NFS_DIRECTIO) += direct.o
 nfs-$(CONFIG_SYSCTL) += sysctl.o
 nfs-objs		:= $(nfs-y)
diff -ruN 2.6.22-rc3.o/fs/nfs/client.c 2.6.22-rc3.c/fs/nfs/client.c
--- 2.6.22-rc3.o/fs/nfs/client.c	2007-06-11 02:45:49.000000000 -0400
+++ 2.6.22-rc3.c/fs/nfs/client.c	2007-06-11 18:16:32.000000000 -0400
@@ -1020,6 +1020,10 @@
 	spin_unlock(&nfs_client_lock);
 
 	server->mount_time = jiffies;
+
+	memset(server->nfs4_key, 0, NFS4_KEY_SIZE);
+	memset(server->nfs4_cipher, 0, NFS4_MAX_ALG_NAME);
+
 	dprintk("<-- nfs4_create_server() = %p\n", server);
 	return server;
 
diff -ruN 2.6.22-rc3.o/fs/nfs/dir.c 2.6.22-rc3.c/fs/nfs/dir.c
--- 2.6.22-rc3.o/fs/nfs/dir.c	2007-06-11 02:45:49.000000000 -0400
+++ 2.6.22-rc3.c/fs/nfs/dir.c	2007-06-11 18:23:56.000000000 -0400
@@ -63,6 +63,9 @@
 	.open		= nfs_opendir,
 	.release	= nfs_release,
 	.fsync		= nfs_fsync_dir,
+#ifdef CONFIG_NFS_V4  
+	.ioctl   	= nfs4_ioctl, 
+#endif
 };
 
 const struct inode_operations nfs_dir_inode_operations = {
@@ -411,6 +414,12 @@
 	int		loop_count = 0,
 			res;
 
+	if (file && file->f_dentry && file->f_dentry->d_inode) {
+		if(is_nfs4(file->f_dentry->d_inode) && !is_key_set(file->f_dentry->d_inode)) {
+			return -EACCES;
+		}
+	}
+
 	dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling starting @ cookie %Lu\n",
 			(unsigned long long)entry->cookie);
 
@@ -1008,6 +1017,10 @@
 	struct dentry *res = NULL;
 	int error;
 
+	if(is_nfs4(dir) && !is_key_set(dir)) {
+		return  ERR_PTR(-EACCES);
+	}
+
 	dfprintk(VFS, "NFS: atomic_lookup(%s/%ld), %s\n",
 			dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);
 
@@ -1964,6 +1977,12 @@
 
 	if (mask == 0)
 		goto out;
+
+        if(nfs4_deny_access(inode)) {
+            res = -EACCES;
+            goto out;
+        }
+
 	/* Is this sys_access() ? */
 	if (nd != NULL && (nd->flags & LOOKUP_ACCESS))
 		goto force_lookup;
diff -ruN 2.6.22-rc3.o/fs/nfs/file.c 2.6.22-rc3.c/fs/nfs/file.c
--- 2.6.22-rc3.o/fs/nfs/file.c	2007-06-11 02:45:49.000000000 -0400
+++ 2.6.22-rc3.c/fs/nfs/file.c	2007-06-11 18:23:29.000000000 -0400
@@ -32,6 +32,7 @@
 #include <asm/uaccess.h>
 #include <asm/system.h>
 
+#include "nfs4_fs.h"    
 #include "delegation.h"
 #include "iostat.h"
 
@@ -67,6 +68,9 @@
 	.flock		= nfs_flock,
 	.sendfile	= nfs_file_sendfile,
 	.check_flags	= nfs_check_flags,
+#ifdef CONFIG_NFS_V4  
+	.ioctl   	= nfs4_ioctl, 
+#endif
 };
 
 const struct inode_operations nfs_file_inode_operations = {
diff -ruN 2.6.22-rc3.o/fs/nfs/inode.c 2.6.22-rc3.c/fs/nfs/inode.c
--- 2.6.22-rc3.o/fs/nfs/inode.c	2007-06-11 02:45:49.000000000 -0400
+++ 2.6.22-rc3.c/fs/nfs/inode.c	2007-06-11 18:24:33.000000000 -0400
@@ -1141,12 +1141,14 @@
 #endif
 #ifdef CONFIG_NFS_V4
 	nfsi->nfs4_acl = NULL;
+	nfsi->nfs4_nci = NULL;  
 #endif /* CONFIG_NFS_V4 */
 	return &nfsi->vfs_inode;
 }
 
 void nfs_destroy_inode(struct inode *inode)
 {
+	put_nci(inode); 
 	kmem_cache_free(nfs_inode_cachep, NFS_I(inode));
 }
 
diff -ruN 2.6.22-rc3.o/fs/nfs/nfs4_crypt.c 2.6.22-rc3.c/fs/nfs/nfs4_crypt.c
--- 2.6.22-rc3.o/fs/nfs/nfs4_crypt.c	1969-12-31 19:00:00.000000000 -0500
+++ 2.6.22-rc3.c/fs/nfs/nfs4_crypt.c	2007-06-11 18:26:04.000000000 -0400
@@ -0,0 +1,726 @@
+/*
+ * Crypto functions for rt-privacy.
+ *
+ * Copyright (C) 2007 Avishay Traeger, Kumar Thangavelu, Erez Zadok
+ * Copyright (C) 2007 Stony Brook University (SUNY)
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/nfs_fs.h>
+#include <linux/mount.h>
+#include <linux/nfs_page.h>
+#include <linux/random.h>       /* get_random_bytes */
+#include <linux/scatterlist.h>	
+#include <linux/crypto.h>	/* Crypto APIs*/
+#include "nfs4_crypt.h"
+
+/* This is the global default iv */
+unsigned char global_iv[8] = {
+	0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10
+};
+
+/* Read the encryption cipher from the user space. */
+int nfs4_get_ioctl_data_SETCIPHER_ucipher(void *out, int len, void *arg)
+{
+	int ret;
+	struct _nfs4_ioctl_SETCIPHER this_ioctl;
+	struct crypto_blkcipher *tfm;
+
+	ret = copy_from_user((char *)&this_ioctl, arg, sizeof(this_ioctl));
+	if (ret != 0) {
+		ret = -EFAULT;
+		goto out_SETCIPHER;
+	}
+
+	tfm = crypto_alloc_blkcipher(this_ioctl.ucipher, 0, CRYPTO_ALG_ASYNC);
+	if (tfm == NULL) {
+		printk(KERN_DEBUG "Crypto API unavailable. Failed for %s\n", this_ioctl.ucipher);
+		ret = -EINVAL;
+		goto out_SETCIPHER;
+	}
+
+	memcpy(out, &this_ioctl.ucipher, len);
+
+out_SETCIPHER:
+	return ret;
+}
+
+/* A utility function that calculates a hash from the msg passed. 
+ * The hash is duplicated to be of the appropriate size and stored in 
+ * result. This result is supposed to act as the key for user. 
+ * This funtion is used by ioctl to set the user key.
+ */
+int crypt_set_enc_key(char *result, char *msg, int msg_len) {
+	int len = 0, i = 0;
+
+	if ((len = get_hash(result, msg, msg_len, NFS4_KEY_HASH_ALGO)) < 0) {
+		return len;
+	}
+
+	/* Duplicate the hash generated to fill out the whole of the size of
+	 * result. Just in case. */
+	for(i=len ; i<NFS4_KEY_SIZE; i++) {
+		result[i] = result[i%len];
+	}
+
+	return 0;
+}
+
+/* A utility function used by nfs_ioctl to retrieve the
+ * user password from the user space.
+ */
+int nfs4_get_ioctl_data_SETKEY_ukey(void *out, int len, void *arg)
+{
+	int ret;
+	struct _nfs4_ioctl_SETKEY this_ioctl;
+
+	ret = copy_from_user((char *)&this_ioctl, arg, sizeof(this_ioctl));
+	if (ret != 0)
+		ret = -EFAULT;;
+	if (ret >= 0)
+		memcpy(out, &this_ioctl.ukey, len);
+	return ret;
+}
+
+/*
+ * An ioctl implementation function to read and set the user password.
+ */
+int nfs4_ioctl(struct inode *dir, struct file *filp, unsigned int cmd, unsigned long arg) {
+	int err = 0;		/* don't fail by default */
+
+	/* This function is defined only for nfs v4. For all others should give
+	 * unknown ioctl
+	 */
+	if(!is_nfs4(dir)) {
+		return -ENOTTY;
+	}
+
+	/* check if asked for local commands */
+	switch (cmd) {
+		/* Added a new ioctl command to set the key. */
+		case NFS4_IOCTL_SETKEY:
+			{
+				char temp_buf[16];
+
+				if (nfs4_get_ioctl_data_SETKEY_ukey(temp_buf, sizeof(temp_buf), (void *)arg) < 0) {
+					err = -EFAULT;
+					break;
+				}
+
+				/* If the key is of zero length then the user wants to remove the key */
+				if (temp_buf[0] != 0) {
+					/* calcuate the key from the 16 byte (hardcoded in cryptfs) user password given. */
+					err = crypt_set_enc_key(NFS_SERVER(dir)->nfs4_key, temp_buf, 16);
+				} else {
+					/* User wants to remove the key */
+					memset(NFS_SERVER(dir)->nfs4_key, 0, NFS4_KEY_SIZE);
+				}
+			}
+			break;
+			/* Added a new ioctl command to set the cipher. */
+		case NFS4_IOCTL_SETCIPHER:
+			if ((err = nfs4_get_ioctl_data_SETCIPHER_ucipher((void *) NFS_SERVER(dir)->nfs4_cipher, NFS4_MAX_ALG_NAME, (void *)arg)) < 0)
+				printk("NFS4_IOCTL_SETCIPHER: cipher setting failed\n");
+			else
+				printk("NFS4_IOCTL_SETCIPHER: cipher set to \"%s\"\n", NFS_SERVER(dir)->nfs4_cipher);
+			break;
+		default:
+			err	= -ENOTTY;	/* this is an unknown ioctl */
+	} /* end of outer switch statement */
+
+	return err;
+}
+
+/* Checks if the key is set
+ * Returns 1 if set. Else returns 0
+ */
+int is_key_set(struct inode *this_inode) {
+	int i = 0;
+	char *key = NFS_SERVER(this_inode)->nfs4_key;
+
+	/* If the key is not set then key will contain all 0s */
+	for (i=0 ; i<NFS4_KEY_SIZE; i++) {
+		if (key[i] != 0) {
+			break;
+		}
+	}
+	if (i == NFS4_KEY_SIZE) {
+		/* key has not been set. */
+		return 0;
+	} else {
+		/* key has been set. */
+		return 1;
+	}
+}
+
+/* Checks if the cipher is set
+ * Returns 1 if set. Else returns 0
+ */
+int is_cipher_set(struct inode *this_inode) {
+	int i = 0;
+	char *cipher = NFS_SERVER(this_inode)->nfs4_cipher;
+
+	/* If the cipher is not set then cipher will contain all 0s */
+	for (i=0 ; i<NFS4_MAX_ALG_NAME ; i++) {
+		if (cipher[i] != 0) {
+			break;
+		}
+	}
+	if (i == NFS4_MAX_ALG_NAME) {
+		/* cipher has not been set. */
+		return 0;
+	} else {
+		/* cipher has been set. */
+		return 1;
+	}
+}
+
+/* Checks if the nfs version is nfs4
+ * Returns 1 if nfs4. Else returns 0
+ */
+int is_nfs4(struct inode *this_inode) {
+	if(NFS_PROTO(this_inode)->version != 4) {
+		return 0;
+	} else {
+		return 1;
+	}
+}
+
+/* Checks if the access should be denied for the given 
+ * inode. This will happen if the inode is not that of the 
+ * root and when the key is not set.
+ * Returns 1 for denying access. Else returns 0
+ */
+int nfs4_deny_access(struct inode *this_inode) {
+	int res = 0;
+	if(is_nfs4(this_inode) && !is_key_set(this_inode)) {
+		/* Skip check for mount point. Key can be set after mounting the FS */
+		/* The error conditions should never happen. But checking just in case */
+		if (!this_inode) {
+			printk("ERROR: inode is null\n");
+		} else {
+			struct nfs_open_context *ctx;
+			ctx = nfs_find_open_context(this_inode, NULL, FMODE_READ);
+			if (ctx != NULL) {
+				if (ctx->vfsmnt != NULL) {
+					if (ctx->vfsmnt->mnt_root != NULL) {
+						if (ctx->vfsmnt->mnt_root->d_inode != NULL) {
+							if (this_inode->i_ino == ctx->vfsmnt->mnt_root->d_inode->i_ino) {
+								; /* NO-OP . Mount point is visible even when key is not set. */
+							}
+							else {
+								res = 1;
+							}
+						}
+					}
+				}
+				put_nfs_open_context(ctx);
+			}
+		}
+	}
+	return res;
+}
+
+/* Convert the given parameters to a instance of crypt_vec */
+/* NOTE: No checks are made on the incoming parameters. They may be null or invalid.
+ * This function just converts a long list of arguments to a single argument.
+ * The crypt_vec obtained using this function must be released by a call to 
+ * put_crypt_vec(cv)
+ */
+struct crypt_vec* get_crypt_vec(struct nfs_page *req) {
+	struct crypt_vec * cv = NULL;
+
+	cv = kmalloc(sizeof(struct crypt_vec), GFP_KERNEL); 
+	if (!cv) {
+		return ERR_PTR(-ENOMEM);
+	}
+
+	cv->req = req;
+
+	/* The rest of the fields will be initialized later 
+	 * they need not be supplied by the user
+	 */
+	cv->this_vnode = NULL;
+	cv->this_dentry = NULL;
+	cv->nci = NULL; 
+
+	return cv;
+}
+
+/* Release the given crypt_vec.
+ * NOTE: Do not release the contents that are pointed to by this crypt_vec.
+ */
+void put_crypt_vec(struct crypt_vec *cv) {
+	if (cv && !IS_ERR(cv)) {
+		kfree(cv);
+	}
+}
+
+/* Initialize the fields of the cv from the existing fields 
+ * 'flag' indicates if the operation is NFS4_ENCRYPT/NFS4_DECRYPT
+ */
+int init_cv(struct crypt_vec *cv, int flag) {
+	struct nfs_page *req = cv->req;
+	struct page *pg = req->wb_page;
+
+	if (pg == NULL) {
+		return -EINVAL;
+	}
+
+	cv->this_vnode = pg->mapping->host;
+	cv->nci = NULL; /* Will be initialized later */
+
+	if (flag == NFS4_ENCRYPT) {
+		/* During encryption we need to do a setattr. For that we need the dentry */
+		if (req->wb_context == NULL) {
+			struct nfs_open_context *ctx;
+			printk("WARNING : Unable to get the nfs_open_context from nfs_page for ino %lu\n", 
+					cv->this_vnode->i_ino);
+
+			/* Trying another way to get the nfs_open_context */
+			ctx = nfs_find_open_context(cv->this_vnode, NULL, FMODE_WRITE);
+			if (ctx == NULL) {
+				printk("ERROR : Unable to get the nfs_open_context for ino %lu\n", cv->this_vnode->i_ino);
+				return -EINVAL;
+			}
+			cv->this_dentry = ctx->dentry;
+			put_nfs_open_context(ctx);
+		} else {
+			cv->this_dentry = req->wb_context->dentry;
+		}
+	}
+
+	return 0;
+}
+
+/* This function checks if the set of arguments in struct crypt_vec are sufficient
+ * for ??cryption to be done. 'flag' indicates if the operation is NFS4_ENCRYPT/NFS4_DECRYPT
+ * Returns NFS4_CRYPT_NOT_SUPPORTED if ??cryption cannot be done. Else returns NFS4_CRYPT_SUPPORTED.
+ */
+int is_cryptable(struct crypt_vec *cv, int flag) {
+
+	/* Define on seperate lines as it can be easy to modify later. */
+	if (cv->req == NULL) {
+		printk("ERROR : The input nfs_page sent is null\n");
+		return NFS4_CRYPT_NOT_SUPPORTED;
+	}
+
+	/* From the req populate the other contents of the cv. ie, Initialize the fields. */
+	if(init_cv(cv, flag)) {
+		printk("ERROR : Unable to initialise the input for encryption from the input provided.\n");
+		return NFS4_CRYPT_NOT_SUPPORTED;
+	}
+
+
+	/* Define on seperate lines as it can be easy to modify later. */
+	if (cv->this_vnode == NULL) {
+		printk("ERROR : Unable to get inode from page. \n");
+		return NFS4_CRYPT_NOT_SUPPORTED;
+	}
+
+	if (flag == NFS4_ENCRYPT && cv->this_dentry == NULL) {
+		/* Dentry is required only for ENCRYPT */
+		printk("ERROR : Unable to get dentry from page. \n");
+		return NFS4_CRYPT_NOT_SUPPORTED;
+	}
+
+	if (NFS_PROTO(cv->this_vnode) == NULL) {
+		printk("ERROR : RPC ops not defined for this fs. \n");
+		return NFS4_CRYPT_NOT_SUPPORTED;
+	}
+
+	if (NFS_PROTO(cv->this_vnode)->getattr == NULL) {
+		printk("ERROR : RPC ops -> getattr not defined for this fs. \n");
+		return NFS4_CRYPT_NOT_SUPPORTED;
+	}
+
+	if (NFS_PROTO(cv->this_vnode)->setattr == NULL) {
+		printk("ERROR : RPC ops -> setattr not defined for this fs. \n");
+		return NFS4_CRYPT_NOT_SUPPORTED;
+	}
+
+	if (NFS_SERVER(cv->this_vnode) == NULL) {
+		printk("ERROR : Unable to get the NFS server from the page inode.\n");
+		return NFS4_CRYPT_NOT_SUPPORTED;
+	}
+
+	if (NFS_FH(cv->this_vnode) == NULL) {
+		printk("ERROR : Unable to get the NFS FH from the page inode.\n");
+		return NFS4_CRYPT_NOT_SUPPORTED;
+	}
+
+	return NFS4_CRYPT_SUPPORTED;
+}
+
+/* Process a nci that is going to be written to the server. */
+void prepare_write_nci(struct crypt_vec *cv) {
+	char *key = NFS_SERVER(cv->this_vnode)->nfs4_key;
+	struct nfs4_crypt_info *nci = cv->nci;
+	unsigned char iv[8];
+
+	memcpy(iv, global_iv, 8);
+
+	/* We will always crypt the key in the nci using the global cipher, iv and key */
+	if (crypt(nci->key, NFS4_KEY_SIZE, iv, 8, key, NFS4_ENCRYPT, NFS4_ENC_ALGO) < 0) {
+		printk("ERROR: Unable to encrypt key stored in nci. The keys are now in plain.");
+	}
+}
+
+/* Process a nci that is going has just been read from the server. 
+ * Can also be used to change back the nci after it was written
+ */
+void prepare_read_nci(struct crypt_vec *cv) {
+	char *key = NFS_SERVER(cv->this_vnode)->nfs4_key;
+	struct nfs4_crypt_info *nci = cv->nci;
+	unsigned char iv[8];
+
+	memcpy(iv, global_iv, 8);
+
+	/* We will always crypt the key in the nci using the global cipher, iv and key */
+	if (crypt(nci->key, NFS4_KEY_SIZE, iv, 8, key, NFS4_DECRYPT, NFS4_ENC_ALGO) < 0) {
+		printk("ERROR: Unable to decrypt key stored in lockbox. The key will remain encrypted");
+	}
+}
+
+/* Create a new nci. The nci should already have been allocated. */
+struct nfs4_crypt_info* get_new_nci(struct crypt_vec *cv) {
+	struct nfs4_crypt_info* nci = cv->nci;
+
+	nci->ino = cv->this_vnode->i_ino;
+	get_random_bytes(nci->key, NFS4_KEY_SIZE);
+
+	return nci;
+}
+
+/* Get the crypt info for this file (cv) by reading it from the server.
+ * cv->nci must already have been allocated. The result is populated in 
+ * cv->nci and sent back.
+ */
+int read_nci(struct crypt_vec *cv) {
+	int err = 0;
+	struct nfs4_crypt_info *nci = cv->nci;
+	struct nfs_fattr fattr;
+
+	if (!nci) {
+		/* Should never happen. */
+		printk("ERROR : nci is not initialized during attempt to read from server.\n");
+		return -EINVAL;
+	}
+
+	/* Read from the server */
+	nfs_fattr_init(&fattr);
+
+	err = NFS_PROTO(cv->this_vnode)->getattr(NFS_SERVER(cv->this_vnode), NFS_FH(cv->this_vnode), &fattr);
+
+	if (err) {
+		printk("ERROR : Unable to read nci attr from the server. err = %d\n", err);
+		return -EINVAL;
+	}
+	memcpy(nci, &(fattr.nci), sizeof(struct nfs4_crypt_info));
+
+	/* Process the read nci. Essentially decrypt the key */
+	prepare_read_nci(cv);
+
+	/* TODO: Ignore the fattr. We should ideally update the inode with this info! :-) */
+
+	return err;
+}
+
+/* Write the crypt info for this file (cv) to the server.
+ * cv->nci must already have been allocated and initialized.
+ */
+int write_nci(struct crypt_vec *cv) {
+	int err = 0;
+	struct nfs_fattr fattr;
+	struct iattr attr;
+
+	if (!cv->nci) {
+		/* Should never happen. */
+		printk("ERROR : nci is not initialized during attempt to write to the server.\n");
+		return -EINVAL;
+	}
+
+	/* Initialize the iattr struct that we want to send to the server */
+	attr.ia_valid = 0;
+	attr.ia_valid |= ATTR_CRYPT;  /* We want to send only one attribute for crypt_info */
+
+	/* Encrypt the key. Copy to fattr. Decrypt the key */
+	/* TBD: Can get race condition if someone else is trying to read when file is first written */
+	prepare_write_nci(cv);
+	memcpy(&(attr.ia_nci), cv->nci, sizeof(struct nfs4_crypt_info));
+	prepare_read_nci(cv);
+
+	/* Write to the server. */
+	err = NFS_PROTO(cv->this_vnode)->setattr(cv->this_dentry, &fattr, &attr);
+
+	/* TODO: Ignore the fattr. We should ideally update the inode with this info! :-) */
+
+	if (err) {
+		printk("ERROR : Unable to write nci attr to the server. err = %d\n", err);
+		return -EINVAL;
+	}
+
+	return err;
+}
+
+void put_nci(struct inode * vnode) {
+	struct nfs4_crypt_info *nci = NFS_I(vnode)->nfs4_nci;
+	if (nci && !IS_ERR(nci)) {
+		kfree(nci);
+	}
+}
+
+/* Get the crypt info for this file. 
+ * flag can be NFS4_ENCRYPT or NFS4_DECRYPT
+ * The nci obtained using this function has to be released 
+ * using put_nci.
+ */
+struct nfs4_crypt_info* get_nci(struct crypt_vec *cv, int flag) {
+	int err = 0;
+	struct nfs4_crypt_info *nci = cv->nci;
+
+	/* Check if the nci is cached locally. 
+	 * This will be present if we are in the same
+	 * encrypt or decrypt call.
+	 */
+	if (nci != NULL) {
+		return nci;
+	}
+
+	/* Check if the nci is cached in the inode. 
+	 * This will be present across encrypt and decrypt calls.
+	 */
+	nci = NFS_I(cv->this_vnode)->nfs4_nci;
+	if (nci != NULL) {
+		/* Inititalize the local cache */
+		cv->nci = nci;
+		return nci;
+	}
+
+	/* nci is not cached */ 
+
+	/* Allocate the nci */
+	nci = kmalloc(sizeof(struct nfs4_crypt_info), GFP_KERNEL);
+	if (!nci) {
+		nci = ERR_PTR(-ENOMEM);
+		return nci;
+	}
+	/* Cache the nci for future use. */
+	cv->nci = nci;
+	NFS_I(cv->this_vnode)->nfs4_nci = nci;
+
+	/* Try to get the crypt info (nci) for this inode (cv) from the server */
+	err = read_nci(cv);
+	if (err) {
+		printk("ERROR : Error while retrieving the nci from the server. err = %d\n", err);
+		goto out_get_nci_err;
+	}
+
+	/* Check if the nci sent by the server is valid */
+	if (nci->ino != INVALID_NCI) {
+		if (nci->ino == cv->this_vnode->i_ino) {
+			/* The nci is valid */
+			goto out_get_nci;
+		} else {
+			printk("ERROR : Attrs set in the server side are corrupted! Unable to get nci\n");
+			printk("ERROR : Current ino %lu does not match server side attr set for ino %lu\n",
+					cv->this_vnode->i_ino,
+					nci->ino);
+			goto out_get_nci_err;
+		}
+	}
+
+	/* nci->ino is set to INVALID_NCI. This means that the nci entry was not found 
+	 * on the server. */ 
+
+	if(flag == NFS4_DECRYPT) {
+		/* There is no key set for the file. The file was created directly on
+		 * the server
+		 */
+		err = -ENODATA;
+		goto out_get_nci_err;
+	}
+
+	/* Operation is encryption. Here we are allowed to populate a new nci */
+	get_new_nci(cv);
+
+	/* After creating the nci write it to the server */
+	err = write_nci(cv);
+	if (err) {
+		printk("ERROR : Error while writing the nci to the server.\n");
+		goto out_get_nci_err;
+	}
+
+out_get_nci:
+	/* Do not release the nci. It is cached */
+	return nci;
+
+out_get_nci_err:
+	/* There has been some error. */
+	cv->nci = NULL;
+	NFS_I(cv->this_vnode)->nfs4_nci = NULL;
+	if (nci && !IS_ERR(nci)) {
+		kfree(nci);
+	}
+	nci = ERR_PTR(err);
+	return nci;
+}
+
+/* This function is responsible for getting the cryption key. 
+ * flag can be NFS4_ENCRYPT or NFS4_DECRYPT
+ */
+char* get_key(struct crypt_vec *cv, int flag) {
+	struct nfs4_crypt_info *nci = NULL;
+
+	/* Get the crypt info (nci) for this inode (cv) */
+	nci = get_nci(cv, flag);
+	if (!nci || IS_ERR(nci)) {
+		if (nci && PTR_ERR(nci) == -ENODATA) {
+			/* There is no key set for the file. The file was created directly on
+			 * the server
+			 */
+			return ERR_PTR(-ENODATA);
+		}
+		return ERR_PTR(-EINVAL);
+	}
+
+	return nci->key;
+}
+
+/* Encrypt or Decrypt (flag) result(in) and store the contents in result(out) */
+/* result_length should be of padded length */
+/* result is a in/out parameter */
+int crypt(char* result, int result_length, char *iv, int iv_length, char *key, int flag, char *cipher) {
+	struct scatterlist *sg_enc=NULL;
+	struct crypto_blkcipher *tfm_enc=NULL;
+	struct blkcipher_desc desc;
+	int err=0;
+	char *iv_ext = NULL;
+	int iv_ext_sz = 0;
+
+	/*Initialize the encryption structures */
+	sg_enc = kmalloc(sizeof(struct scatterlist), GFP_KERNEL);
+	if (!sg_enc) {
+		err = -ENOMEM;
+		goto out;
+	}
+	tfm_enc = crypto_alloc_blkcipher(cipher, 0, CRYPTO_ALG_ASYNC);
+	if (tfm_enc == NULL || IS_ERR(tfm_enc)) {
+		printk(KERN_DEBUG "crypt: Crypto API unavailable. Failed for %s\n", cipher);
+		err = -ENOPKG;
+		goto out_sg_enc;
+	}
+	desc.tfm = tfm_enc;
+	desc.flags = 0;
+	crypto_blkcipher_clear_flags(tfm_enc, ~0);
+	err = crypto_blkcipher_setkey(tfm_enc, key, NFS4_KEY_SIZE);
+	if (err) {
+		printk("crypt: ERROR: Key error. crypto_cipher_setkey() failed flags=%x\n", crypto_blkcipher_get_flags(tfm_enc));
+		goto out_tfm_enc;
+	}
+	iv_ext_sz = crypto_blkcipher_ivsize(tfm_enc);
+	iv_ext = kmalloc(iv_ext_sz,GFP_KERNEL);
+	if (!iv_ext) {
+		err = -ENOMEM;
+		goto out_tfm_enc;
+	}
+	memset(iv_ext, 0, iv_ext_sz);
+	memcpy(iv_ext, iv, ((iv_length < iv_ext_sz) ? iv_length : iv_ext_sz));
+
+	sg_set_buf(sg_enc, result, result_length); 
+	crypto_blkcipher_set_iv(tfm_enc, iv_ext, iv_ext_sz);
+	if(flag == NFS4_ENCRYPT) {
+		crypto_blkcipher_encrypt(&desc, sg_enc, sg_enc, sg_enc->length);
+	} else {
+		crypto_blkcipher_decrypt(&desc, sg_enc, sg_enc, sg_enc->length);
+	}
+
+	kfree(iv_ext);
+out_tfm_enc:
+	crypto_free_blkcipher(tfm_enc);
+out_sg_enc:
+	kfree(sg_enc);
+out:
+	return err;
+}
+
+/* Calcuate the hash of the given message. */
+int get_hash(char *result, char *msg, int msg_len, char *hash_algo) {
+	struct scatterlist *sg_hash;
+	struct crypto_hash *tfm_hash;
+	struct hash_desc desc;
+	int hash_size = 0;
+
+	sg_hash = kmalloc(sizeof(struct scatterlist), GFP_KERNEL);
+	if (!sg_hash) {
+		return -ENOMEM;
+	}
+	tfm_hash = crypto_alloc_hash(hash_algo, 0, CRYPTO_ALG_ASYNC);
+	if (tfm_hash == NULL) {
+		printk(KERN_DEBUG "Crypto API unavailable. Failed for %s\n", hash_algo);
+		kfree(sg_hash);
+		return -ENOPKG;
+	}
+	desc.tfm = tfm_hash;
+	desc.flags = 0;
+	crypto_hash_init(&desc);
+	sg_set_buf(&sg_hash[0], msg, msg_len); 
+	crypto_hash_update(&desc, sg_hash, msg_len);
+	crypto_hash_final(&desc, result);
+	hash_size = crypto_hash_digestsize(tfm_hash);
+	crypto_free_hash(tfm_hash);
+	kfree(sg_hash);
+	return hash_size;
+}
+
+/* Best effort to get key. Improves performance */
+char* get_cached_key(struct nfs_page *req, int flag) {
+	struct nfs4_crypt_info *nci = NULL;
+	if (!req || !req->wb_page || !req->wb_page->mapping->host) {
+		return NULL;
+	}
+
+	nci = NFS_I(req->wb_page->mapping->host)->nfs4_nci;
+	if (!nci) {
+		return NULL;
+	}
+	return nci->key;
+}
+
+/* Return the crypt key for encryption or decryption */
+char* get_crypt_key(struct nfs_page *req, int flag) {
+	char *key = NULL;
+	struct crypt_vec* cv = NULL;
+	if(!is_nfs4(req->wb_page->mapping->host)) {
+		return NULL;
+	}
+	if (!S_ISREG(req->wb_page->mapping->host->i_mode)) {
+		return NULL;
+	}
+	key = get_cached_key(req, flag);  
+	if (key && !IS_ERR(key)) {
+		return key;
+	}
+	cv = get_crypt_vec(req);
+	if (!cv || IS_ERR(cv)) {
+		return ERR_PTR(-EINVAL);
+	}
+	if (!is_cryptable(cv, flag)) {
+		/* Based on the input provided. We cannot get the key. */
+		printk("ERROR: Unable to get the encryption key. The arguments passed are insufficient.\n");
+		key = ERR_PTR(-EINVAL);
+		goto out_crypt_vec;
+	}
+	key = get_key(cv, flag);
+	if (flag == NFS4_DECRYPT && IS_ERR(key)) {
+		if (key && PTR_ERR(key) == -ENODATA) {
+			/* There is no key set for the file. The file was created directly on
+			 * the server
+			 */
+			key = NULL;
+			goto out_crypt_vec;
+		}
+	} 
+	if (!key || IS_ERR(key)) {
+		key = ERR_PTR(-EINVAL);
+		printk("ERROR! unable to get key!! ");
+		goto out_crypt_vec;
+	}
+out_crypt_vec:
+	put_crypt_vec(cv);
+	return key;
+}
diff -ruN 2.6.22-rc3.o/fs/nfs/nfs4_crypt.h 2.6.22-rc3.c/fs/nfs/nfs4_crypt.h
--- 2.6.22-rc3.o/fs/nfs/nfs4_crypt.h	1969-12-31 19:00:00.000000000 -0500
+++ 2.6.22-rc3.c/fs/nfs/nfs4_crypt.h	2007-06-11 18:26:35.000000000 -0400
@@ -0,0 +1,52 @@
+/*
+ * Header for rt-privacy.
+ *
+ * Copyright (C) 2007 Avishay Traeger, Kumar Thangavelu, Erez Zadok
+ * Copyright (C) 2007 Stony Brook University (SUNY)
+ *
+ */
+#ifndef __LINUX_NFS4_CRYPT_H_
+#define __LINUX_NFS4_CRYPT_H_
+
+#define NFS4_MAX_ALG_NAME 16 /* constant used by both user and kernel code */
+
+#ifdef __KERNEL__
+
+#define NFS4_KEY_SIZE 16
+#define NFS4_KEY_HASH_ALGO "md5"
+#define NFS4_ENC_ALGO "cbc(aes)"
+#define NFS4_ENCRYPT 00
+#define NFS4_DECRYPT 01
+#define NFS4_CRYPT_NOT_SUPPORTED 00
+#define NFS4_CRYPT_SUPPORTED 01
+
+/* This structure is used as input argument when it needs 
+ * to call encryption or decryption.
+ */
+struct crypt_vec {
+	struct inode *this_vnode;
+	struct dentry *this_dentry;      /* TODO: Is this required ? */
+	struct nfs4_crypt_info *nci; 
+
+	struct nfs_page *req;
+};
+extern int nfs4_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
+extern int nfs4_get_ioctl_data_SETKEY_ukey(void *out, int len, void *arg);
+extern int nfs4_get_ioctl_data_SETCIPHER_ucipher(void *out, int len, void *arg);
+
+extern int crypt_set_enc_key(char *result, char *msg, int msg_len);
+extern int get_hash(char *result, char *msg, int msg_len, char *algo);
+extern int is_key_set(struct inode *this_inode);
+extern int is_nfs4(struct inode *this_inode);
+extern int nfs4_deny_access(struct inode *this_inode);
+extern char* get_crypt_key(struct nfs_page *req, int flag);
+extern int crypt(char* result, int result_length, char *iv, int iv_length, char *key, int flag, char *cipher);
+extern void put_nci(struct inode * vnode);
+
+#endif /* __KERNEL__ */
+
+/* ioctls */
+struct _nfs4_ioctl_SETKEY {
+	char ukey [16];
+};
+#define NFS4_IOCTL_SETKEY	_IOW(0x15, 10, struct _nfs4_ioctl_SETKEY)
+
+struct _nfs4_ioctl_SETCIPHER {
+	char ucipher [NFS4_MAX_ALG_NAME];
+};
+#define NFS4_IOCTL_SETCIPHER	_IOW(0x15, 11, struct _nfs4_ioctl_SETCIPHER)
+
+#endif	/* __LINUX_NFS4_CRYPT_H_*/
diff -ruN 2.6.22-rc3.o/fs/nfs/nfs4_fs.h 2.6.22-rc3.c/fs/nfs/nfs4_fs.h
--- 2.6.22-rc3.o/fs/nfs/nfs4_fs.h	2007-06-11 02:45:49.000000000 -0400
+++ 2.6.22-rc3.c/fs/nfs/nfs4_fs.h	2007-06-11 18:26:49.000000000 -0400
@@ -11,6 +11,8 @@
 
 #ifdef CONFIG_NFS_V4
 
+#include "nfs4_crypt.h"
+
 struct idmap;
 
 /*
diff -ruN 2.6.22-rc3.o/fs/nfs/nfs4proc.c 2.6.22-rc3.c/fs/nfs/nfs4proc.c
--- 2.6.22-rc3.o/fs/nfs/nfs4proc.c	2007-06-11 02:45:49.000000000 -0400
+++ 2.6.22-rc3.c/fs/nfs/nfs4proc.c	2007-06-11 18:27:27.000000000 -0400
@@ -95,6 +95,7 @@
 	| FATTR4_WORD1_TIME_ACCESS
 	| FATTR4_WORD1_TIME_METADATA
 	| FATTR4_WORD1_TIME_MODIFY
+	| FATTR4_WORD1_CRYPT_INFO 
 };
 
 const u32 nfs4_statfs_bitmap[2] = {
@@ -2316,6 +2317,10 @@
 	data->timestamp   = jiffies;
 
 	rpc_call_setup(&data->task, &msg, 0);
+
+	data->task.tk_fsl = 1;       /* Flag to indicate this is a read/write request */
+	data->task.tk_offset_fsl = data->args.offset;
+	data->task.tk_key_fsl = get_crypt_key(data->req, NFS4_DECRYPT);
 }
 
 static int nfs4_write_done(struct rpc_task *task, struct nfs_write_data *data)
@@ -2360,6 +2365,10 @@
 
 	/* Finalize the task. */
 	rpc_call_setup(&data->task, &msg, 0);
+
+	data->task.tk_fsl = 1;       /* Flag to indicate this is a read/write request */
+	data->task.tk_offset_fsl = data->args.offset;
+	data->task.tk_key_fsl = get_crypt_key(data->req, NFS4_ENCRYPT);
 }
 
 static int nfs4_commit_done(struct rpc_task *task, struct nfs_write_data *data)
diff -ruN 2.6.22-rc3.o/fs/nfs/nfs4xdr.c 2.6.22-rc3.c/fs/nfs/nfs4xdr.c
--- 2.6.22-rc3.o/fs/nfs/nfs4xdr.c	2007-06-11 02:45:49.000000000 -0400
+++ 2.6.22-rc3.c/fs/nfs/nfs4xdr.c	2007-06-11 18:28:02.000000000 -0400
@@ -562,6 +562,9 @@
 		len += 16;
 	else if (iap->ia_valid & ATTR_MTIME)
 		len += 4;
+	if (iap->ia_valid & ATTR_CRYPT) {
+		len += 4 + (XDR_QUADLEN(sizeof(struct nfs4_crypt_info)) << 2);
+	}
 	RESERVE_SPACE(len);
 
 	/*
@@ -612,6 +615,11 @@
 		bmval1 |= FATTR4_WORD1_TIME_MODIFY_SET;
 		WRITE32(NFS4_SET_TO_SERVER_TIME);
 	}
+	if (iap->ia_valid & ATTR_CRYPT) {
+		bmval1 |= FATTR4_WORD1_CRYPT_INFO;
+		WRITE32(sizeof(struct nfs4_crypt_info));
+		WRITEMEM(&(iap->ia_nci), sizeof(struct nfs4_crypt_info));
+	}
 	
 	/*
 	 * Now we backfill the bitmap and the attribute buffer length.
@@ -2355,6 +2363,32 @@
 	return 0;
 }
 
+static int decode_attr_nci(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs4_crypt_info *nci) {
+	uint32_t len;
+	__be32 *p;
+
+	if (unlikely(bitmap[1] & (FATTR4_WORD1_CRYPT_INFO - 1U)))
+		return -EIO;
+	if (likely(bitmap[1] & FATTR4_WORD1_CRYPT_INFO)) {
+		READ_BUF(4);
+		READ32(len);
+		READ_BUF(len);
+		if (len < XDR_MAX_NETOBJ) {
+			if (len != sizeof(struct nfs4_crypt_info)) {
+				printk("ERROR : Server and client seem to be using different \
+						versions of struct nfs4_crypt_info.\n");
+				nci->ino = INVALID_NCI;
+			} else {
+				memcpy(nci, (char *)p, len);
+			}
+		} else
+			printk(KERN_WARNING "%s: name too long (%u)!\n",
+					__FUNCTION__, len);
+		bitmap[1] &= ~FATTR4_WORD1_CRYPT_INFO;
+	}
+	return 0;
+}
+
 static int decode_attr_files_avail(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res)
 {
 	__be32 *p;
@@ -3076,6 +3110,8 @@
 		goto xdr_error;
 	if ((status = decode_attr_mounted_on_fileid(xdr, bitmap, &fileid)) != 0)
 		goto xdr_error;
+	if ((status = decode_attr_nci(xdr, bitmap, &fattr->nci)) != 0)
+		goto xdr_error;
 	if (fattr->fileid == 0 && fileid != 0)
 		fattr->fileid = fileid;
 	if ((status = verify_attr_len(xdr, savep, attrlen)) == 0)
diff -ruN 2.6.22-rc3.o/fs/nfs/super.c 2.6.22-rc3.c/fs/nfs/super.c
--- 2.6.22-rc3.o/fs/nfs/super.c	2007-06-11 02:45:49.000000000 -0400
+++ 2.6.22-rc3.c/fs/nfs/super.c	2007-06-11 02:38:14.000000000 -0400
@@ -257,6 +257,7 @@
 		{ RPC_AUTH_GSS_KRB5, "krb5" },
 		{ RPC_AUTH_GSS_KRB5I, "krb5i" },
 		{ RPC_AUTH_GSS_KRB5P, "krb5p" },
+		{ RPC_AUTH_GSS_KRB5E, "krb5e" },
 		{ RPC_AUTH_GSS_LKEY, "lkey" },
 		{ RPC_AUTH_GSS_LKEYI, "lkeyi" },
 		{ RPC_AUTH_GSS_LKEYP, "lkeyp" },
diff -ruN 2.6.22-rc3.o/fs/nfsd/nfs4proc.c 2.6.22-rc3.c/fs/nfsd/nfs4proc.c
--- 2.6.22-rc3.o/fs/nfsd/nfs4proc.c	2007-06-11 02:45:49.000000000 -0400
+++ 2.6.22-rc3.c/fs/nfsd/nfs4proc.c	2007-06-11 18:29:10.000000000 -0400
@@ -507,6 +507,8 @@
 	if (read->rd_offset >= OFFSET_MAX)
 		return nfserr_inval;
 
+	rqstp->rq_rd_offset = read->rd_offset;
+
 	nfs4_lock_state();
 	/* check stateid */
 	if ((status = nfs4_preprocess_stateid_op(&cstate->current_fh,
@@ -909,6 +911,12 @@
 		if (op->opnum == OP_READ && op->u.read.rd_filp)
 			fput(op->u.read.rd_filp);
 
+		if (op->opnum == OP_READ || op->opnum == OP_WRITE) {
+			rqstp->rq_is_rd_fsl = 1;
+		} else {
+			rqstp->rq_is_rd_fsl = 0;
+		}
+
 		nfsd4_increment_op_stats(op->opnum);
 	}
 
diff -ruN 2.6.22-rc3.o/fs/nfsd/nfs4xdr.c 2.6.22-rc3.c/fs/nfsd/nfs4xdr.c
--- 2.6.22-rc3.o/fs/nfsd/nfs4xdr.c	2007-06-11 02:45:49.000000000 -0400
+++ 2.6.22-rc3.c/fs/nfsd/nfs4xdr.c	2007-06-11 18:30:13.000000000 -0400
@@ -406,6 +406,22 @@
 			goto xdr_error;
 		}
 	}
+	if (bmval[1] & FATTR4_WORD1_CRYPT_INFO) {
+		READ_BUF(4);
+		len += 4;
+		READ32(dummy32);
+		READ_BUF(dummy32);
+		len += (XDR_QUADLEN(dummy32) << 2);
+		READMEM(buf, dummy32);
+		if (sizeof(struct nfs4_crypt_info) != dummy32) {
+			printk ("ERROR : The client and server are using \
+				different versions of struct nfs4_crypt_info");
+			host_err = -EINVAL;
+			goto xdr_error;
+		}
+		memcpy(&(iattr->ia_nci), buf, dummy32);
+		iattr->ia_valid |= ATTR_CRYPT;
+	}
 	if (len != expected_len)
 		goto xdr_error;
 
@@ -1810,6 +1826,39 @@
 		} else
                 	WRITE64((u64) stat.ino);
 	}
+	if (bmval1 & FATTR4_WORD1_CRYPT_INFO) {
+		struct inode *inode = dentry->d_inode;
+		struct iattr attr;
+		int err = 0;
+
+		if (!inode->i_op || !inode->i_op->setxattr) {
+			printk("ERROR : The file system does not support xattrs\n.");
+			status = -EOPNOTSUPP;
+			goto out;
+		}
+		err = inode->i_op->getxattr(dentry, USER_NCI, (char *) &(attr.ia_nci),
+			sizeof(struct nfs4_crypt_info));
+
+		if(err == -ENODATA) {
+			/* The attribute has not been set. Send a dummy nci */
+			attr.ia_nci.ino = INVALID_NCI;
+		} else if (err < 0) {
+			printk("ERROR : Error no %d while reading xattr for ino %lu\n", err, inode->i_ino);
+			/* We still send a dummy nci */
+			attr.ia_nci.ino = INVALID_NCI;
+		} else if (err != sizeof(struct nfs4_crypt_info)) {
+			printk("ERROR : Read error while reading xattr for ino %lu\n", inode->i_ino);
+			/* We still send a dummy nci */
+			attr.ia_nci.ino = INVALID_NCI;
+		}
+
+		/* Encode the nci */
+		buflen -= (XDR_QUADLEN(sizeof(struct nfs4_crypt_info)) << 2) + 4;
+		if (buflen < 0)
+			goto out_resource;
+		WRITE32(sizeof(struct nfs4_crypt_info));
+		WRITEMEM(&(attr.ia_nci), sizeof(struct nfs4_crypt_info));
+	}
 	*attrlenp = htonl((char *)p - (char *)attrlenp - 4);
 	*countp = p - buffer;
 	status = nfs_ok;
diff -ruN 2.6.22-rc3.o/fs/nfsd/vfs.c 2.6.22-rc3.c/fs/nfsd/vfs.c
--- 2.6.22-rc3.o/fs/nfsd/vfs.c	2007-06-11 02:45:49.000000000 -0400
+++ 2.6.22-rc3.c/fs/nfsd/vfs.c	2007-06-11 18:30:35.000000000 -0400
@@ -270,6 +270,26 @@
 	dentry = fhp->fh_dentry;
 	inode = dentry->d_inode;
 
+	if (iap->ia_valid & ATTR_CRYPT) {
+		if (inode->i_ino != iap->ia_nci.ino) {
+			printk("ERROR : Failed to set the xattr. inode numbers dont match.\n");
+			err = -EINVAL;
+			goto out;
+		}
+		if (!inode->i_op || !inode->i_op->setxattr) {
+			printk("ERROR : The file system does not support xattrs\n.");
+			err = -EOPNOTSUPP;
+			goto out;
+		}
+		err = inode->i_op->setxattr(dentry, USER_NCI, (char *) &iap->ia_nci,
+				sizeof(struct nfs4_crypt_info), 0);
+		if(err) {
+			printk("ERROR : Failed to set the xattr %s for ino %lu\n",
+					USER_NCI, inode->i_ino);
+			goto out;
+		}
+	}
+
 	/* Ignore any mode updates on symlinks */
 	if (S_ISLNK(inode->i_mode))
 		iap->ia_valid &= ~ATTR_MODE;
diff -ruN 2.6.22-rc3.o/include/linux/fs.h 2.6.22-rc3.c/include/linux/fs.h
--- 2.6.22-rc3.o/include/linux/fs.h	2007-06-11 02:45:49.000000000 -0400
+++ 2.6.22-rc3.c/include/linux/fs.h	2007-06-11 18:31:06.000000000 -0400
@@ -327,6 +327,25 @@
 #define ATTR_KILL_SUID	2048
 #define ATTR_KILL_SGID	4096
 #define ATTR_FILE	8192
+#define ATTR_CRYPT      16384   /* Added a flag for nfs4_crypt_info */
+
+/* This structure represents cryption information for a inode.
+ * The sizes are hardcoded. But they should be equal to the 
+ * respective constants in the file fs/nfs/nfs4_crypt.h
+ * The size of this structure is 128 bytes.
+ */
+struct nfs4_crypt_info {
+	unsigned long ino;
+	char key[16];       /* NFS4_KEY_SIZE */
+	char padding[12];   /* To make this a multiple of 16 */
+};
+
+/* A constant to indicate if the nfs4_crypt_info is valid or not 
+ * ino in nfs4_crypt_info will be set to this value when the field 
+ * is not valid
+ */
+#define INVALID_NCI -9999
+
 
 /*
  * This is the Inode Attributes structure, used for notify_change().  It
@@ -353,6 +372,8 @@
 	 * check for (ia_valid & ATTR_FILE), and not for (ia_file != NULL).
 	 */
 	struct file	*ia_file;
+
+	struct nfs4_crypt_info ia_nci; 
 };
 
 /*
diff -ruN 2.6.22-rc3.o/include/linux/nfs4.h 2.6.22-rc3.c/include/linux/nfs4.h
--- 2.6.22-rc3.o/include/linux/nfs4.h	2007-06-11 02:45:49.000000000 -0400
+++ 2.6.22-rc3.c/include/linux/nfs4.h	2007-06-11 18:31:34.000000000 -0400
@@ -347,6 +347,9 @@
 #define FATTR4_WORD1_TIME_MODIFY        (1UL << 21)
 #define FATTR4_WORD1_TIME_MODIFY_SET    (1UL << 22)
 #define FATTR4_WORD1_MOUNTED_ON_FILEID  (1UL << 23)
+#define FATTR4_WORD1_CRYPT_INFO         (1UL << 24)
+
+#define USER_NCI "user.nci"   
 
 #define NFSPROC4_NULL 0
 #define NFSPROC4_COMPOUND 1
diff -ruN 2.6.22-rc3.o/include/linux/nfs_fs.h 2.6.22-rc3.c/include/linux/nfs_fs.h
--- 2.6.22-rc3.o/include/linux/nfs_fs.h	2007-06-11 02:45:49.000000000 -0400
+++ 2.6.22-rc3.c/include/linux/nfs_fs.h	2007-06-11 18:31:47.000000000 -0400
@@ -174,6 +174,7 @@
 	struct nfs_delegation	*delegation;
 	int			 delegation_state;
 	struct rw_semaphore	rwsem;
+	struct nfs4_crypt_info *nfs4_nci;
 #endif /* CONFIG_NFS_V4*/
 	struct inode		vfs_inode;
 };
diff -ruN 2.6.22-rc3.o/include/linux/nfs_fs_sb.h 2.6.22-rc3.c/include/linux/nfs_fs_sb.h
--- 2.6.22-rc3.o/include/linux/nfs_fs_sb.h	2007-06-11 02:45:49.000000000 -0400
+++ 2.6.22-rc3.c/include/linux/nfs_fs_sb.h	2007-06-11 18:32:00.000000000 -0400
@@ -110,6 +110,9 @@
 	u32			acl_bitmask;	/* V4 bitmask representing the ACEs
 						   that are supported on this
 						   filesystem */
+
+	char nfs4_key[16]; /* hashed user key. */
+	char nfs4_cipher[16]; /* encryption cipher */
 #endif
 	void (*destroy)(struct nfs_server *);
 };
diff -ruN 2.6.22-rc3.o/include/linux/nfs_xdr.h 2.6.22-rc3.c/include/linux/nfs_xdr.h
--- 2.6.22-rc3.o/include/linux/nfs_xdr.h	2007-06-11 02:45:49.000000000 -0400
+++ 2.6.22-rc3.c/include/linux/nfs_xdr.h	2007-06-11 18:32:19.000000000 -0400
@@ -56,6 +56,8 @@
 	__u64			change_attr;	/* NFSv4 change attribute */
 	__u64			pre_change_attr;/* pre-op NFSv4 change attribute */
 	unsigned long		time_start;
+
+	struct nfs4_crypt_info nci; 
 };
 
 #define NFS_ATTR_WCC		0x0001		/* pre-op WCC data    */
diff -ruN 2.6.22-rc3.o/include/linux/nfsd/nfsd.h 2.6.22-rc3.c/include/linux/nfsd/nfsd.h
--- 2.6.22-rc3.o/include/linux/nfsd/nfsd.h	2007-06-11 02:45:49.000000000 -0400
+++ 2.6.22-rc3.c/include/linux/nfsd/nfsd.h	2007-06-11 02:38:14.000000000 -0400
@@ -299,12 +299,13 @@
  | FATTR4_WORD0_MAXFILESIZE     | FATTR4_WORD0_MAXLINK      | FATTR4_WORD0_MAXNAME          \
  | FATTR4_WORD0_MAXREAD         | FATTR4_WORD0_MAXWRITE     | FATTR4_WORD0_ACL)
 
+/* added FATTR4_WORD1_CRYPT_INFO to NFSD_SUPPORTED_ATTRS_WORD1 */
 #define NFSD_SUPPORTED_ATTRS_WORD1                                                          \
 (FATTR4_WORD1_MODE              | FATTR4_WORD1_NO_TRUNC     | FATTR4_WORD1_NUMLINKS         \
  | FATTR4_WORD1_OWNER	        | FATTR4_WORD1_OWNER_GROUP  | FATTR4_WORD1_RAWDEV           \
  | FATTR4_WORD1_SPACE_AVAIL     | FATTR4_WORD1_SPACE_FREE   | FATTR4_WORD1_SPACE_TOTAL      \
  | FATTR4_WORD1_SPACE_USED      | FATTR4_WORD1_TIME_ACCESS  | FATTR4_WORD1_TIME_ACCESS_SET  \
- | FATTR4_WORD1_TIME_DELTA   | FATTR4_WORD1_TIME_METADATA    \
+ | FATTR4_WORD1_TIME_DELTA   | FATTR4_WORD1_TIME_METADATA   | FATTR4_WORD1_CRYPT_INFO       \
  | FATTR4_WORD1_TIME_MODIFY     | FATTR4_WORD1_TIME_MODIFY_SET | FATTR4_WORD1_MOUNTED_ON_FILEID)
 
 /* These will return ERR_INVAL if specified in GETATTR or READDIR. */
@@ -316,7 +317,8 @@
 (FATTR4_WORD0_SIZE              | FATTR4_WORD0_ACL                                         )
 #define NFSD_WRITEABLE_ATTRS_WORD1                                                          \
 (FATTR4_WORD1_MODE              | FATTR4_WORD1_OWNER         | FATTR4_WORD1_OWNER_GROUP     \
- | FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_METADATA | FATTR4_WORD1_TIME_MODIFY_SET)
+ | FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_METADATA | FATTR4_WORD1_TIME_MODIFY_SET \
+ | FATTR4_WORD1_CRYPT_INFO)     /* added FATTR4_WORD1_CRYPT_INFO */
 
 #endif /* CONFIG_NFSD_V4 */
 
diff -ruN 2.6.22-rc3.o/include/linux/sunrpc/auth_gss.h 2.6.22-rc3.c/include/linux/sunrpc/auth_gss.h
--- 2.6.22-rc3.o/include/linux/sunrpc/auth_gss.h	2007-06-11 02:45:49.000000000 -0400
+++ 2.6.22-rc3.c/include/linux/sunrpc/auth_gss.h	2007-06-11 18:32:52.000000000 -0400
@@ -33,7 +33,8 @@
 enum rpc_gss_svc {
 	RPC_GSS_SVC_NONE = 1,
 	RPC_GSS_SVC_INTEGRITY = 2,
-	RPC_GSS_SVC_PRIVACY = 3
+	RPC_GSS_SVC_PRIVACY = 3,
+	RPC_GSS_SVC_E2E = 4
 };
 
 /* on-the-wire gss cred: */
diff -ruN 2.6.22-rc3.o/include/linux/sunrpc/gss_krb5.h 2.6.22-rc3.c/include/linux/sunrpc/gss_krb5.h
--- 2.6.22-rc3.o/include/linux/sunrpc/gss_krb5.h	2007-06-11 02:45:49.000000000 -0400
+++ 2.6.22-rc3.c/include/linux/sunrpc/gss_krb5.h	2007-06-11 18:33:14.000000000 -0400
@@ -144,9 +144,17 @@
 		    int offset, struct page **pages);
 
 int
+gss_encrypt_xdr_buf_fsl(struct crypto_blkcipher *tfm, struct xdr_buf *buf,
+		    int offset, struct page **pages, unsigned int len);
+
+int
 gss_decrypt_xdr_buf(struct crypto_blkcipher *tfm, struct xdr_buf *inbuf,
 		    int offset);
 
+int
+gss_decrypt_xdr_buf_fsl(struct crypto_blkcipher *tfm, struct xdr_buf *buf,
+		    int offset, int len);
+
 s32
 krb5_make_seq_num(struct crypto_blkcipher *key,
 		int direction,
@@ -156,3 +164,10 @@
 krb5_get_seq_num(struct crypto_blkcipher *key,
 	       unsigned char *cksum,
 	       unsigned char *buf, int *direction, s32 * seqnum);
+
+/* Values for how */
+#define FSL_ENCRYPT 1
+#define FSL_DECRYPT 0
+int
+gss_crypt_xdr_buf_pages_fsl(struct gss_ctx_fsl *ctx_fsl, struct xdr_buf *buf,
+		    int offset, struct page **pages, unsigned int len, int how);
diff -ruN 2.6.22-rc3.o/include/linux/sunrpc/msg_prot.h 2.6.22-rc3.c/include/linux/sunrpc/msg_prot.h
--- 2.6.22-rc3.o/include/linux/sunrpc/msg_prot.h	2007-06-11 02:45:49.000000000 -0400
+++ 2.6.22-rc3.c/include/linux/sunrpc/msg_prot.h	2007-06-11 02:38:14.000000000 -0400
@@ -35,6 +35,7 @@
 	RPC_AUTH_GSS_SPKM  = 390009,
 	RPC_AUTH_GSS_SPKMI = 390010,
 	RPC_AUTH_GSS_SPKMP = 390011,
+	RPC_AUTH_GSS_KRB5E = 390012,
 };
 
 /* Maximum size (in bytes) of an rpc credential or verifier */
diff -ruN 2.6.22-rc3.o/include/linux/sunrpc/sched.h 2.6.22-rc3.c/include/linux/sunrpc/sched.h
--- 2.6.22-rc3.o/include/linux/sunrpc/sched.h	2007-06-11 02:45:49.000000000 -0400
+++ 2.6.22-rc3.c/include/linux/sunrpc/sched.h	2007-06-11 02:38:14.000000000 -0400
@@ -97,6 +97,9 @@
 #ifdef RPC_DEBUG
 	unsigned short		tk_pid;		/* debugging aid */
 #endif
+	unsigned short		tk_fsl;		/* field to indicate if this is a write/read request */
+	__u64			tk_offset_fsl;  /* field to hold the write/read offset */
+	char*			tk_key_fsl;     /* field to hold the enc/dec key */
 };
 #define tk_auth			tk_client->cl_auth
 #define tk_xprt			tk_client->cl_xprt
diff -ruN 2.6.22-rc3.o/include/linux/sunrpc/svc.h 2.6.22-rc3.c/include/linux/sunrpc/svc.h
--- 2.6.22-rc3.o/include/linux/sunrpc/svc.h	2007-06-11 02:45:49.000000000 -0400
+++ 2.6.22-rc3.c/include/linux/sunrpc/svc.h	2007-06-11 02:38:14.000000000 -0400
@@ -256,6 +256,8 @@
 	int			rq_sendfile_ok; /* turned off in gss privacy
 						 * to prevent encrypting page
 						 * cache pages */
+	int			rq_is_rd_fsl;   /* Added flag to indicate READ call */
+	unsigned int		rq_rd_offset;   /* Added element to indicate read offset */
 	wait_queue_head_t	rq_wait;	/* synchronization */
 	struct task_struct	*rq_task;	/* service thread */
 };
diff -ruN 2.6.22-rc3.o/include/linux/sunrpc/xdr.h 2.6.22-rc3.c/include/linux/sunrpc/xdr.h
--- 2.6.22-rc3.o/include/linux/sunrpc/xdr.h	2007-06-11 02:45:49.000000000 -0400
+++ 2.6.22-rc3.c/include/linux/sunrpc/xdr.h	2007-06-11 18:33:46.000000000 -0400
@@ -59,6 +59,18 @@
 	unsigned int	buflen,		/* Total length of storage buffer */
 			len;		/* Length of XDR encoded message */
 
+	struct gss_ctx_fsl      *ctx_fsl;
+};
+
+struct gss_ctx_fsl {
+	int isE2E;
+	int isClient;
+	unsigned int isRead;
+	__u64 offset;
+	unsigned int page_len;
+	size_t org_head_len;
+	size_t org_tail_len;
+	char *key;
 };
 
 /*
diff -ruN 2.6.22-rc3.o/net/sunrpc/auth_gss/auth_gss.c 2.6.22-rc3.c/net/sunrpc/auth_gss/auth_gss.c
--- 2.6.22-rc3.o/net/sunrpc/auth_gss/auth_gss.c	2007-06-11 02:45:49.000000000 -0400
+++ 2.6.22-rc3.c/net/sunrpc/auth_gss/auth_gss.c	2007-06-11 18:59:27.000000000 -0400
@@ -1055,6 +1055,33 @@
 	return 0;
 }
 
+static inline int
+gss_wrap_req_e2e(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
+		kxdrproc_t encode, struct rpc_rqst *rqstp, __be32 *p, void *obj,
+                struct rpc_task *task)
+{
+	int status = 0;
+	struct xdr_buf *snd_buf = &rqstp->rq_snd_buf;
+	struct gss_ctx_fsl *ctx_fsl = snd_buf->ctx_fsl; 
+
+	if (!ctx_fsl) {
+		printk("ERROR: gss_wrap_req_e2e: ctx_fsl is NULL.\n");
+		return -EIO;
+	}
+
+	ctx_fsl->isE2E = 1;
+	if (task->tk_fsl == 1) {
+		ctx_fsl->isRead = 1;
+	} else {
+		ctx_fsl->isRead = 0;
+	}
+	ctx_fsl->offset = (task->tk_offset_fsl & PAGE_MASK) + snd_buf->page_base;
+	ctx_fsl->key = task->tk_key_fsl;
+	ctx_fsl->isClient = 1;
+	status = gss_wrap_req_priv(cred, ctx, encode, rqstp, p, obj);
+	return status;
+}
+
 static int
 gss_wrap_req(struct rpc_task *task,
 	     kxdrproc_t encode, void *rqstp, __be32 *p, void *obj)
@@ -1064,6 +1091,7 @@
 			gc_base);
 	struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred);
 	int             status = -EIO;
+	struct gss_ctx_fsl *ctx_fsl = NULL;
 
 	dprintk("RPC: %5u gss_wrap_req\n", task->tk_pid);
 	if (ctx->gc_proc != RPC_GSS_PROC_DATA) {
@@ -1073,6 +1101,15 @@
 		status = encode(rqstp, p, obj);
 		goto out;
 	}
+
+	ctx_fsl = kmalloc(sizeof(struct gss_ctx_fsl), GFP_KERNEL);
+	if (!ctx_fsl) {
+		status = -ENOMEM;
+		goto out;
+	}
+	memset(ctx_fsl, 0, sizeof(struct gss_ctx_fsl));
+	((struct rpc_rqst *)rqstp)->rq_snd_buf.ctx_fsl = ctx_fsl;
+
 	switch (gss_cred->gc_service) {
 		case RPC_GSS_SVC_NONE:
 			status = encode(rqstp, p, obj);
@@ -1085,7 +1122,17 @@
 			status = gss_wrap_req_priv(cred, ctx, encode,
 					rqstp, p, obj);
 			break;
+		case RPC_GSS_SVC_E2E:
+			status = gss_wrap_req_e2e(cred, ctx, encode,
+					rqstp, p, obj, task);
+			break;
 	}
+
+	if (ctx_fsl != NULL) {
+		kfree(ctx_fsl);
+		((struct rpc_rqst *)rqstp)->rq_snd_buf.ctx_fsl = NULL;
+	}
+
 out:
 	gss_put_ctx(ctx);
 	dprintk("RPC: %5u gss_wrap_req returning %d\n", task->tk_pid, status);
@@ -1157,6 +1204,31 @@
 	return 0;
 }
 
+static inline int
+gss_unwrap_resp_e2e(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
+		struct rpc_rqst *rqstp, __be32 **p, kxdrproc_t decode,
+		struct rpc_task *task)
+{
+	int status = 0;
+	struct xdr_buf *rcv_buf = &rqstp->rq_rcv_buf;
+	struct gss_ctx_fsl *ctx_fsl = rcv_buf->ctx_fsl; 
+
+	if (!ctx_fsl) {
+		return -EIO;
+	}
+
+	ctx_fsl->isE2E = 1;
+	if (task->tk_fsl == 1) {
+		ctx_fsl->isRead = 1;
+	} else {
+		ctx_fsl->isRead = 0;
+	}
+	ctx_fsl->offset = (task->tk_offset_fsl & PAGE_MASK) + rcv_buf->page_base;
+	ctx_fsl->key = task->tk_key_fsl;
+	ctx_fsl->isClient = 1;
+	status = gss_unwrap_resp_priv(cred, ctx, rqstp, p);
+	return status;
+}
 
 static int
 gss_unwrap_resp(struct rpc_task *task,
@@ -1170,9 +1242,19 @@
 	struct kvec	*head = ((struct rpc_rqst *)rqstp)->rq_rcv_buf.head;
 	int		savedlen = head->iov_len;
 	int             status = -EIO;
+	struct gss_ctx_fsl *ctx_fsl = NULL;
 
 	if (ctx->gc_proc != RPC_GSS_PROC_DATA)
 		goto out_decode;
+
+	ctx_fsl = kmalloc(sizeof(struct gss_ctx_fsl), GFP_KERNEL);
+	if (!ctx_fsl) {
+		status = -ENOMEM;
+		goto out;
+	}
+	memset(ctx_fsl, 0, sizeof(struct gss_ctx_fsl));
+	((struct rpc_rqst *)rqstp)->rq_rcv_buf.ctx_fsl = ctx_fsl;
+
 	switch (gss_cred->gc_service) {
 		case RPC_GSS_SVC_NONE:
 			break;
@@ -1186,6 +1268,11 @@
 			if (status)
 				goto out;
 			break;
+		case RPC_GSS_SVC_E2E:
+			status = gss_unwrap_resp_e2e(cred, ctx, rqstp, &p, decode, task); 
+			if (status)
+				goto out;
+			break;
 	}
 	/* take into account extra slack for integrity and privacy cases: */
 	task->tk_auth->au_rslack = task->tk_auth->au_verfsize + (p - savedp)
@@ -1193,6 +1280,11 @@
 out_decode:
 	status = decode(rqstp, p, obj);
 out:
+	if (ctx_fsl != NULL) {
+		kfree(ctx_fsl);
+		((struct rpc_rqst *)rqstp)->rq_rcv_buf.ctx_fsl = NULL;
+	}
+
 	gss_put_ctx(ctx);
 	dprintk("RPC: %5u gss_unwrap_resp returning %d\n", task->tk_pid,
 			status);
diff -ruN 2.6.22-rc3.o/net/sunrpc/auth_gss/gss_krb5_crypto.c 2.6.22-rc3.c/net/sunrpc/auth_gss/gss_krb5_crypto.c
--- 2.6.22-rc3.o/net/sunrpc/auth_gss/gss_krb5_crypto.c	2007-06-11 02:45:50.000000000 -0400
+++ 2.6.22-rc3.c/net/sunrpc/auth_gss/gss_krb5_crypto.c	2007-06-11 18:42:07.000000000 -0400
@@ -175,6 +175,105 @@
 	int fraglen;
 };
 
+struct encryptor_desc_fsl {
+	__u64 offset;
+	char *key;
+	int how;
+	struct blkcipher_desc desc;
+};
+
+#define KEY_SIZE (16)   /* Must be same as NFS4_KEY_SIZE */
+#define NONCE_SIZE (8)
+
+static char def_key[] = {
+	5, 0, 6, 5, 0, 6, 5, 0, 6, 5, 0, 6, 5, 0, 6, 5
+};
+
+static char def_nonce[] = {
+	2, 6, 19, 2, 1, 19, 2, 6
+};
+
+
+/* Xor the characters in out with the characters in in, storing the result in
+ * out. */
+static void xor(char *out, char *in, ssize_t len)
+{
+	ssize_t i;
+	for (i = 0 ; i < len ; i++)
+		out[i] ^= in[i];
+}
+
+int encrypt_ctr_mode(char *key, char *nonce, char *text, ssize_t len,
+		     ssize_t offset, int how, struct blkcipher_desc *desc)
+{
+	struct crypto_blkcipher *cipher;
+	int blocksize;
+	char keystream[16];
+	struct scatterlist sg;
+	int block_num;
+	int block_start, block_end;
+	int pos = 0;
+
+        if (nonce == NULL) {
+            nonce = def_nonce;
+        }
+
+	cipher = desc->tfm;
+	desc->flags = 0;
+
+	blocksize = crypto_blkcipher_blocksize(cipher);
+
+	/* Init the scatterlist to encrypt the keystream buffer. */
+	sg_set_buf(&sg, keystream, blocksize);
+
+	block_num = offset / blocksize;
+	block_start = block_num * blocksize;
+	block_end = block_start + blocksize;
+
+	/* Loop through each block. */
+	while (len > 0) {
+		int block_len;
+
+		/* Concatenate the nonce and the block number into the
+		 * keystream buffer. */
+		memset(keystream, 0, blocksize);
+
+		memcpy(keystream, nonce, NONCE_SIZE);
+
+		keystream[blocksize - 4] = (char)(block_num >> 24);
+		keystream[blocksize - 3] = (char)(block_num >> 16);
+		keystream[blocksize - 2] = (char)(block_num >> 8);
+		keystream[blocksize - 1] = (char)block_num;
+
+		/* Encrypting the keystream buffer gives us key material. */
+		crypto_blkcipher_encrypt(desc, &sg, &sg, blocksize);
+
+		/* Calculate how many bytes we should encrypt.
+		 * We want to encrypt to the end of the block. */
+		block_len = block_end - offset;
+
+		/* But not if the amount of data left to encryt is less than
+		 * the size of the block. */
+		if (len < block_len)
+			block_len = len;
+
+
+		xor (text + pos, keystream + (offset - block_start),
+		     block_len);
+
+		/* Update the position in the buffer, current offset,
+		 * block number and amount of data left to encrypt. */
+		pos += block_len;
+		offset += block_len;
+		len -= block_len;
+		block_num++;
+		block_start += blocksize;
+		block_end += blocksize;
+	}
+
+	return 0;
+}
+
 static int
 encryptor(struct scatterlist *sg, void *data)
 {
@@ -229,6 +328,27 @@
 	return 0;
 }
 
+static int
+encryptor_fsl(struct scatterlist *sg, void *data)
+{
+	struct encryptor_desc_fsl *desc = data;
+	char *to;
+	int err = 0;
+
+	to = kmap(sg->page);
+	to += sg->offset;
+	err = encrypt_ctr_mode(desc->key, NULL, to, sg->length, desc->offset, desc->how, &(desc->desc));
+	kunmap(sg->page);
+
+	if (!err) {
+		desc->offset += sg->length;
+	} else {
+		printk("ERROR: encryptor_fsl: encrypt_ctr_mode returned %d\n", err);
+	}
+
+	return err;
+}
+
 int
 gss_encrypt_xdr_buf(struct crypto_blkcipher *tfm, struct xdr_buf *buf,
 		    int offset, struct page **pages)
@@ -254,6 +374,74 @@
 
 EXPORT_SYMBOL(gss_encrypt_xdr_buf);
 
+int
+gss_encrypt_xdr_buf_fsl(struct crypto_blkcipher *tfm, struct xdr_buf *buf,
+		    int offset, struct page **pages, unsigned int len)
+{
+	int ret;
+	struct encryptor_desc desc;
+
+	BUG_ON(len % crypto_blkcipher_blocksize(tfm) != 0);
+
+	memset(desc.iv, 0, sizeof(desc.iv));
+	desc.desc.tfm = tfm;
+	desc.desc.info = desc.iv;
+	desc.desc.flags = 0;
+	desc.pos = offset;
+	desc.outbuf = buf;
+	desc.pages = pages;
+	desc.fragno = 0;
+	desc.fraglen = 0;
+
+	ret = xdr_process_buf(buf, offset, len, encryptor, &desc);
+	return ret;
+}
+
+EXPORT_SYMBOL(gss_encrypt_xdr_buf_fsl);
+
+int
+gss_crypt_xdr_buf_pages_fsl(struct gss_ctx_fsl *ctx_fsl, struct xdr_buf *buf,
+		int offset, struct page **pages, unsigned int len, int how)
+{
+	int ret=0;
+	struct encryptor_desc_fsl desc;
+	struct crypto_blkcipher *cipher;
+	int blocksize;
+
+	cipher = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
+	if (IS_ERR(cipher)) {
+		printk("Unsupported cipher for NFS encryption.\n");
+		ret = PTR_ERR(cipher);
+		return ret;
+	}
+	if (ctx_fsl->key == NULL || IS_ERR(ctx_fsl->key)) {
+		ctx_fsl->key = def_key;
+	}
+	if (crypto_blkcipher_setkey(cipher, ctx_fsl->key, KEY_SIZE) != 0) {
+		printk("Set key error.\n");
+		return -ENOTSUPP;
+	}
+	blocksize = crypto_blkcipher_blocksize(cipher);
+	/* Oops, we didn't allocate enough stack space to hold a block. */
+	BUG_ON(blocksize > 16);
+	/* Oops, our nonce is too big! */
+	BUG_ON(NONCE_SIZE + 4 > blocksize);
+
+	desc.offset = ctx_fsl->offset;
+	desc.key = ctx_fsl->key;
+	desc.how = how;
+	desc.desc.tfm = cipher;
+	desc.desc.flags = 0;
+
+	ret = xdr_process_buf(buf, offset, len, encryptor_fsl, &desc);
+	if (cipher != NULL)
+		crypto_free_blkcipher(cipher);
+
+	return ret;
+}
+
+EXPORT_SYMBOL(gss_crypt_xdr_buf_pages_fsl);
+
 struct decryptor_desc {
 	u8 iv[8]; /* XXX hard-coded blocksize */
 	struct blkcipher_desc desc;
@@ -318,3 +506,22 @@
 }
 
 EXPORT_SYMBOL(gss_decrypt_xdr_buf);
+
+int
+gss_decrypt_xdr_buf_fsl(struct crypto_blkcipher *tfm, struct xdr_buf *buf,
+		    int offset, int len)
+{
+	struct decryptor_desc desc;
+
+	BUG_ON(len % crypto_blkcipher_blocksize(tfm) != 0);
+
+	memset(desc.iv, 0, sizeof(desc.iv));
+	desc.desc.tfm = tfm;
+	desc.desc.info = desc.iv;
+	desc.desc.flags = 0;
+	desc.fragno = 0;
+	desc.fraglen = 0;
+	return xdr_process_buf(buf, offset, len, decryptor, &desc);
+}
+
+EXPORT_SYMBOL(gss_decrypt_xdr_buf_fsl);
diff -ruN 2.6.22-rc3.o/net/sunrpc/auth_gss/gss_krb5_mech.c 2.6.22-rc3.c/net/sunrpc/auth_gss/gss_krb5_mech.c
--- 2.6.22-rc3.o/net/sunrpc/auth_gss/gss_krb5_mech.c	2007-06-11 02:45:50.000000000 -0400
+++ 2.6.22-rc3.c/net/sunrpc/auth_gss/gss_krb5_mech.c	2007-06-11 02:38:14.000000000 -0400
@@ -226,6 +226,11 @@
 		.service = RPC_GSS_SVC_PRIVACY,
 		.name = "krb5p",
 	},
+	[3] = {
+		.pseudoflavor = RPC_AUTH_GSS_KRB5E,
+		.service = RPC_GSS_SVC_E2E,
+		.name = "krb5e",
+	},
 };
 
 static struct gss_api_mech gss_kerberos_mech = {
diff -ruN 2.6.22-rc3.o/net/sunrpc/auth_gss/gss_krb5_wrap.c 2.6.22-rc3.c/net/sunrpc/auth_gss/gss_krb5_wrap.c
--- 2.6.22-rc3.o/net/sunrpc/auth_gss/gss_krb5_wrap.c	2007-06-11 02:45:50.000000000 -0400
+++ 2.6.22-rc3.c/net/sunrpc/auth_gss/gss_krb5_wrap.c	2007-06-11 18:53:00.000000000 -0400
@@ -107,6 +107,416 @@
 	*q = i++;
 }
 
+struct header_fsl_t {
+	size_t org_head_len;
+	size_t org_tail_len;
+	unsigned int isReadWrite;
+	unsigned int page_len;
+};
+
+/* Function that returns the size of the fsl header */
+size_t get_header_len_fsl(int blocksize) {
+	int header_len_fsl = 0;
+
+	header_len_fsl = sizeof(struct header_fsl_t);
+	if (header_len_fsl % blocksize) {
+		header_len_fsl += blocksize - (header_len_fsl % blocksize);
+	}
+	BUG_ON(header_len_fsl % blocksize);
+
+	return header_len_fsl;
+}
+
+/* Check the incoming argument */
+int check_ctx_fsl(struct gss_ctx_fsl *ctx_fsl) {
+	if (!ctx_fsl) {
+		return GSS_S_FAILURE;
+	} else {
+	}
+	return 0;
+}
+
+/* Determinies if the thread is being run in the client or in the server.
+ * Return 1 if client. Else returns 0
+ */
+static int isClient(struct gss_ctx_fsl *ctx_fsl) {
+	if (!ctx_fsl) {
+		return 0;
+	}
+	if (ctx_fsl->isClient) {
+		return 1;
+	} 
+	return 0;
+}
+
+/* Determine if the mount is using krb5e mode. */
+static int isE2E(struct gss_ctx_fsl *ctx_fsl) {
+	if (!ctx_fsl) {
+		dump_stack();
+		return 0;
+	}
+	if (ctx_fsl->isE2E) {
+		return 1;
+	}
+	return 0;
+}
+
+/* Determine if this this is a read/write call */
+static int isReadWrite(struct gss_ctx_fsl *ctx_fsl) {
+	if (!ctx_fsl) {
+		return 0;
+	}
+	if (ctx_fsl->isRead) {
+		return 1;
+	} 
+	return 0;
+}
+
+static int write_fsl_header(struct xdr_buf *buf, int offset, int blocksize, struct gss_ctx_fsl *ctx_fsl) {
+	struct header_fsl_t *header_fsl = NULL;
+	size_t org_head_len=0, org_tail_len=0;
+	int header_len_fsl = 0;
+	unsigned char *ptr;
+
+	/* Store the starting location and data for the fsl header */
+	header_fsl = buf->head[0].iov_base + offset;
+	org_head_len = buf->head[0].iov_len - offset;
+	org_tail_len = buf->tail[0].iov_len;
+
+	/* Calculate the size of the header for fsl related data */
+	header_len_fsl = get_header_len_fsl(blocksize);
+
+	/* shift data to make room for header. */
+	ptr = buf->head[0].iov_base + offset;
+	memmove(ptr + header_len_fsl, ptr, buf->head[0].iov_len - offset);
+	buf->head[0].iov_len += header_len_fsl;
+	buf->len += header_len_fsl;
+
+	/* Populate the fsl header */
+	header_fsl->org_head_len = org_head_len;
+	header_fsl->org_tail_len = org_tail_len;
+	header_fsl->isReadWrite = (unsigned int) isReadWrite(ctx_fsl);
+	header_fsl->page_len = buf->page_len;
+
+	return 0;
+}
+
+/* Add seperate padding for tail and head */
+static int gss_krb5e_add_padding_fsl(struct xdr_buf *buf, int offset, int blocksize, struct gss_ctx_fsl *ctx_fsl) {
+	int padding = 0;
+	char *p;
+	struct kvec *iov;
+	struct header_fsl_t *header_fsl = NULL;
+	size_t org_head_len=0, org_tail_len=0;
+	int header_len_fsl = 0;
+
+	/* Must already be initialized */
+	header_len_fsl = get_header_len_fsl(blocksize);
+	header_fsl = buf->head[0].iov_base + offset;
+	org_head_len = header_fsl->org_head_len;
+	org_tail_len = header_fsl->org_tail_len;
+
+	if (!isReadWrite(ctx_fsl)) {
+		gss_krb5_add_padding(buf, offset + header_len_fsl, blocksize);
+		return 0;
+	}
+
+
+	/* Perform the padding for the head */
+	iov = &buf->head[0];
+	if (iov->iov_len - offset - header_len_fsl != org_head_len) {
+		return GSS_S_FAILURE;
+	}
+	padding = gss_krb5_padding(blocksize, iov->iov_len - offset - header_len_fsl);
+	p = iov->iov_base + iov->iov_len;
+	iov->iov_len += padding;
+	buf->len += padding;
+	memset(p, padding, padding);
+
+	/* Perform the padding for the tail */
+	iov = &buf->tail[0];
+	if (iov->iov_len) {
+		padding = gss_krb5_padding(blocksize, iov->iov_len);
+		p = iov->iov_base + iov->iov_len;
+		iov->iov_len += padding;
+		buf->len += padding;
+		memset(p, padding, padding);
+	} else {
+	}
+
+	BUG_ON((buf->head[0].iov_len - offset - header_len_fsl) % blocksize);
+	BUG_ON(buf->tail[0].iov_len % blocksize);
+	if((buf->head[0].iov_base + offset + header_len_fsl) != 
+			(char*) header_fsl + sizeof(struct header_fsl_t)) {
+		return GSS_S_FAILURE;
+	} 
+
+	return 0;
+}
+
+static int gss_krb5e_encypt_fsl(struct krb5_ctx *kctx, struct xdr_buf *buf, int offset_header_fsl, 
+	int blocksize, struct page **pages, struct gss_ctx_fsl *ctx_fsl) {
+	char *p;
+	struct header_fsl_t *header_fsl;
+	unsigned thislen;
+	size_t org_head_len=0, org_tail_len=0;
+	int header_len_fsl = 0;
+
+
+	header_fsl = buf->head[0].iov_base + offset_header_fsl;
+
+	/* Must already be initialized */
+	org_head_len = header_fsl->org_head_len;
+	org_tail_len = header_fsl->org_tail_len;
+	header_len_fsl = get_header_len_fsl(blocksize);
+
+	if (!isReadWrite(ctx_fsl)) {
+		return gss_encrypt_xdr_buf(kctx->enc, buf, offset_header_fsl + header_len_fsl, pages);
+	}
+
+	/* Encrypt the head */
+	p = buf->head[0].iov_base + offset_header_fsl + header_len_fsl;
+	thislen = buf->head[0].iov_len - offset_header_fsl - header_len_fsl;
+	if (thislen != org_head_len + gss_krb5_padding(blocksize, org_head_len)) {
+		return GSS_S_FAILURE;
+	}
+
+	if (thislen % crypto_blkcipher_blocksize(kctx->enc) != 0) {
+		return GSS_S_FAILURE;
+	}
+
+	if (gss_encrypt_xdr_buf_fsl(kctx->enc, buf, offset_header_fsl + header_len_fsl, pages, thislen)) {
+		return GSS_S_FAILURE;
+	}
+
+	/* Encrypt the tail */
+	p = buf->tail[0].iov_base;
+	thislen = buf->tail[0].iov_len;
+	if (thislen && thislen != org_tail_len + gss_krb5_padding(blocksize, org_tail_len)) {
+		return GSS_S_FAILURE;
+	}
+	if (thislen % crypto_blkcipher_blocksize(kctx->enc) != 0) {
+		return GSS_S_FAILURE; /* Delete this */
+	}
+
+	if (gss_encrypt_xdr_buf_fsl(kctx->enc, buf, buf->len - buf->tail[0].iov_len, pages, thislen)) {
+		return GSS_S_FAILURE;
+	}
+
+	return 0;
+}
+
+static int encrypt_pages_fsl(struct xdr_buf *buf, int offset, int blocksize, 
+	struct page **pages, struct gss_ctx_fsl *ctx_fsl) {
+	pgoff_t start_index, end_index, index;
+	unsigned long nrpages, start, end, thislen;
+	char *to, *from;
+	int err = 0;
+
+	if (buf->page_len == 0) { 
+		return 0;
+	}
+
+	if (!isReadWrite(ctx_fsl) || !isClient(ctx_fsl)) {
+		return 0;
+	}
+
+	start_index = buf->page_base >> PAGE_CACHE_SHIFT;
+	index = start_index;
+	end_index = (buf->page_base + buf->page_len) >> PAGE_CACHE_SHIFT;
+	nrpages = end_index - start_index + 1;
+
+	/* Encryption has to happen before writing the header because of line below */
+	if (isClient(ctx_fsl)) {
+		if (ctx_fsl->offset != page_offset(pages[start_index]) + (buf->page_base & ~PAGE_CACHE_MASK)) {
+			/* Only in client this can be done */
+			ctx_fsl->offset = page_offset(pages[start_index]) + (buf->page_base & ~PAGE_CACHE_MASK);
+		} else {
+		}
+	}
+
+	while (index <= end_index) {
+		if (index == start_index) {
+			start = buf->page_base & ~PAGE_CACHE_MASK;
+		} else {
+			start = 0;
+		}
+		if (index == end_index) {
+			end = (buf->page_base + buf->page_len) & ~PAGE_CACHE_MASK;
+		} else {
+			end = PAGE_CACHE_SIZE;
+		}
+		thislen = end - start;
+		if (thislen) {
+			to = kmap(buf->pages[index]) + start;
+			from = kmap(pages[index]) + start;
+			memcpy(to, from, thislen);
+			kunmap(buf->pages[index]);
+			kunmap(pages[index]);
+		}
+		index++;
+	}
+
+	if (isClient(ctx_fsl)) {
+		err = gss_crypt_xdr_buf_pages_fsl(ctx_fsl, buf, buf->head[0].iov_len, NULL, buf->page_len, FSL_ENCRYPT);
+	} 
+
+
+	return err;
+}
+
+static int decrypt_pages_fsl(struct xdr_buf *buf, int offset, int blocksize, 
+	struct gss_ctx_fsl *ctx_fsl) {
+	int err = 0;
+
+
+	if (isClient(ctx_fsl)) {
+		pgoff_t start_index = buf->page_base >> PAGE_CACHE_SHIFT;
+		if (ctx_fsl->offset != page_offset(buf->pages[start_index]) + (buf->page_base & ~PAGE_CACHE_MASK)) {
+			ctx_fsl->offset = page_offset(buf->pages[start_index]) + (buf->page_base & ~PAGE_CACHE_MASK);
+		}
+	}
+
+	err = gss_crypt_xdr_buf_pages_fsl(ctx_fsl, buf, offset + ctx_fsl->org_head_len, NULL, ctx_fsl->page_len, FSL_DECRYPT);
+
+
+	return err;
+}
+
+static int gss_krb5e_decypt_fsl(struct krb5_ctx *kctx, struct xdr_buf *buf, int offset_header_fsl, 
+	int blocksize, struct gss_ctx_fsl *ctx_fsl) {
+	struct header_fsl_t *header_fsl;
+	unsigned thislen, offset_tail;
+	int header_len_fsl = 0;
+	size_t org_head_len=0, org_tail_len=0;
+
+	/* Calculate the size of the header for fsl related data */
+	header_len_fsl = get_header_len_fsl(blocksize);
+
+	if (buf->head[0].iov_len - offset_header_fsl < header_len_fsl) {
+		return GSS_S_DEFECTIVE_TOKEN;
+	}
+
+	header_fsl = buf->head[0].iov_base + offset_header_fsl;
+
+	org_head_len = header_fsl->org_head_len;
+	org_tail_len = header_fsl->org_tail_len;
+	ctx_fsl->org_head_len = org_head_len;
+	ctx_fsl->org_tail_len = org_tail_len;
+	ctx_fsl->isRead = header_fsl->isReadWrite;
+	ctx_fsl->page_len = header_fsl->page_len;
+
+	if (!isReadWrite(ctx_fsl)) {
+		if (gss_decrypt_xdr_buf(kctx->enc, buf, offset_header_fsl + header_len_fsl)) {
+				return GSS_S_DEFECTIVE_TOKEN;
+		}
+		return 0;
+	}
+
+	/* Decrypt the head */
+	thislen = org_head_len + gss_krb5_padding(blocksize, org_head_len);
+	if(thislen > buf->head[0].iov_len - offset_header_fsl - header_len_fsl) {
+
+		return GSS_S_BAD_SIG;
+	}
+	if (gss_decrypt_xdr_buf_fsl(kctx->enc, buf, offset_header_fsl + header_len_fsl, thislen))
+		return GSS_S_DEFECTIVE_TOKEN;
+
+	/* Decrypt the tail */
+	if (org_tail_len) {
+		thislen = org_tail_len + gss_krb5_padding(blocksize, org_tail_len);
+		/* Replacing below line with another method for calculating tail offset 
+		offset_tail = buf->len - thislen;
+		*/
+		offset_tail = offset_header_fsl + header_len_fsl + org_head_len +
+					  gss_krb5_padding(blocksize, org_head_len) + ctx_fsl->page_len;
+		if (offset_tail > buf->len) {
+			return GSS_S_BAD_SIG;
+		}
+		if (gss_decrypt_xdr_buf_fsl(kctx->enc, buf, offset_tail, thislen)) {
+			return GSS_S_DEFECTIVE_TOKEN;
+		}
+	}
+
+	return 0;
+}
+
+static int gss_krb5e_remove_padding(struct krb5_ctx *kctx, struct xdr_buf *buf, int offset, 
+	int blocksize, struct gss_ctx_fsl *ctx_fsl) {
+	struct header_fsl_t *header_fsl;
+	int header_len_fsl = 0, padding=0;
+	size_t org_head_len=0, org_tail_len=0;
+	void *data_start, *orig_start;
+	int	data_len;
+
+	header_fsl = buf->head[0].iov_base + offset;
+	/* TBD: This info is already present in ctx_fsl */
+	org_head_len = header_fsl->org_head_len;
+	org_tail_len = header_fsl->org_tail_len;
+	ctx_fsl->isRead = (unsigned int) header_fsl->isReadWrite;
+
+	if (!isReadWrite(ctx_fsl)) {
+		if (gss_krb5_remove_padding(buf, blocksize)) {
+				return GSS_S_DEFECTIVE_TOKEN;
+		}
+		return 0;
+	}
+
+	/* Calculate the size of the header for fsl related data */
+	header_len_fsl = get_header_len_fsl(blocksize);
+
+	/* Remove the padding from the head */
+	padding = gss_krb5_padding(blocksize, org_head_len);
+	data_start = buf->head[0].iov_base + offset + header_len_fsl + org_head_len + padding;
+	orig_start = buf->head[0].iov_base + offset + header_len_fsl + org_head_len;
+	data_len = (buf->head[0].iov_base + buf->head[0].iov_len) - data_start;
+	if(offset + header_len_fsl + org_head_len + padding > buf->head[0].iov_len) {
+		return GSS_S_DEFECTIVE_TOKEN;
+	}
+	memmove(orig_start, data_start, data_len);
+	buf->head[0].iov_len -= padding;
+	buf->len -= padding;
+
+	/* Remove the padding from the tail */
+	if (org_tail_len) {
+		padding = gss_krb5_padding(blocksize, org_tail_len);
+		if(padding > buf->len) {
+			return GSS_S_DEFECTIVE_TOKEN;
+		}
+		/* buf->tail[0].iov_len -= padding; */
+		if (buf->len <= buf->head[0].iov_len) {
+			buf->head[0].iov_len -= padding;
+		}
+		buf->len -= padding;
+	}
+
+	return 0;
+}
+
+int remove_fsl_header(struct krb5_ctx *kctx, struct xdr_buf *buf, int offset, 
+	int blocksize, struct gss_ctx_fsl *ctx_fsl) {
+	int header_len_fsl = 0;
+	void *data_start, *orig_start;
+	int	data_len;
+
+
+	header_len_fsl = get_header_len_fsl(blocksize);
+
+	/* Remove the fsl header */
+	data_start = buf->head[0].iov_base + offset + header_len_fsl;
+	orig_start = buf->head[0].iov_base + offset;
+	data_len = (buf->head[0].iov_base + buf->head[0].iov_len) - data_start;
+	if(offset + header_len_fsl > buf->head[0].iov_len) {
+		return GSS_S_DEFECTIVE_TOKEN;
+	}
+	memmove(orig_start, data_start, data_len);
+	buf->head[0].iov_len -= header_len_fsl;
+	buf->len -= header_len_fsl;
+
+	return 0;
+}
+
 /* Assumptions: the head and tail of inbuf are ours to play with.
  * The pages, however, may be real pages in the page cache and we replace
  * them with scratch pages from **pages before writing to them. */
@@ -128,14 +538,27 @@
 	int			headlen;
 	struct page		**tmp_pages;
 	u32			seq_send;
+	struct gss_ctx_fsl *ctx_fsl = buf->ctx_fsl;
 
-	dprintk("RPC:       gss_wrap_kerberos\n");
+
+	if (check_ctx_fsl(ctx_fsl)) {
+		return GSS_S_FAILURE;
+	}
 
 	now = get_seconds();
 
-	blocksize = crypto_blkcipher_blocksize(kctx->enc);
-	gss_krb5_add_padding(buf, offset, blocksize);
-	BUG_ON((buf->len - offset) % blocksize);
+	blocksize = crypto_blkcipher_blocksize(kctx->enc);
+	if (isE2E(ctx_fsl)) {   /* Using mode krb5e */
+		if (encrypt_pages_fsl(buf, offset, blocksize, pages, ctx_fsl)) {
+		return GSS_S_FAILURE;
+		}
+		write_fsl_header(buf, offset, blocksize, ctx_fsl);
+		gss_krb5e_add_padding_fsl(buf, offset, blocksize, ctx_fsl);
+	} else {
+		gss_krb5_add_padding(buf, offset, blocksize);
+		BUG_ON((buf->len - offset) % blocksize);
+	}
+
 	plainlen = blocksize + buf->len - offset;
 
 	headlen = g_token_size(&kctx->mech_used, 22 + plainlen) -
@@ -148,7 +571,10 @@
 	memmove(ptr + headlen, ptr, buf->head[0].iov_len - offset);
 	buf->head[0].iov_len += headlen;
 	buf->len += headlen;
-	BUG_ON((buf->len - offset - headlen) % blocksize);
+
+	if (!isE2E(ctx_fsl)) { /* Original code. */
+		BUG_ON((buf->len - offset - headlen) % blocksize);
+	}
 
 	g_make_token_header(&kctx->mech_used, 22 + plainlen, &ptr);
 
@@ -167,8 +593,10 @@
 	make_confounder(msg_start, blocksize);
 
 	/* XXXJBF: UGH!: */
-	tmp_pages = buf->pages;
-	buf->pages = pages;
+	tmp_pages = buf->pages;
+	if (!isE2E(ctx_fsl) || !isReadWrite(ctx_fsl)) {
+		buf->pages = pages;
+	}
 	if (make_checksum("md5", krb5_hdr, 8, buf,
 				offset + headlen - blocksize, &md5cksum))
 		return GSS_S_FAILURE;
@@ -191,9 +619,15 @@
 			       seq_send, krb5_hdr + 16, krb5_hdr + 8)))
 		return GSS_S_FAILURE;
 
-	if (gss_encrypt_xdr_buf(kctx->enc, buf, offset + headlen - blocksize,
-									pages))
-		return GSS_S_FAILURE;
+	if (isE2E(ctx_fsl)) {   /* Using mode krb5e */
+		if (gss_krb5e_encypt_fsl(kctx, buf, offset + headlen, blocksize, pages, ctx_fsl)) {
+		return GSS_S_FAILURE;
+		}
+	} else {
+		if (gss_encrypt_xdr_buf(kctx->enc, buf, offset + headlen - blocksize,
+										pages))
+			return GSS_S_FAILURE;
+	}
 
 	return (kctx->endtime < now) ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE;
 }
@@ -214,8 +648,14 @@
 	void			*data_start, *orig_start;
 	int			data_len;
 	int			blocksize;
+	struct gss_ctx_fsl *ctx_fsl = buf->ctx_fsl;
+	int offset_header_fsl=0;
+
 
-	dprintk("RPC:       gss_unwrap_kerberos\n");
+
+	if (check_ctx_fsl(ctx_fsl)) {
+		return GSS_S_FAILURE;
+	}
 
 	ptr = (u8 *)buf->head[0].iov_base + offset;
 	if (g_verify_token_header(&kctx->mech_used, &bodysize, &ptr,
@@ -241,9 +681,20 @@
 	if ((ptr[4] != 0xff) || (ptr[5] != 0xff))
 		return GSS_S_DEFECTIVE_TOKEN;
 
-	if (gss_decrypt_xdr_buf(kctx->enc, buf,
-			ptr + 22 - (unsigned char *)buf->head[0].iov_base))
-		return GSS_S_DEFECTIVE_TOKEN;
+	if (isE2E(ctx_fsl)) {
+		int ret = 0;
+		blocksize = crypto_blkcipher_blocksize(kctx->enc);
+		offset_header_fsl = ptr + 22 + blocksize - (unsigned char *)buf->head[0].iov_base;
+		if ((ret = gss_krb5e_decypt_fsl(kctx, buf, offset_header_fsl, blocksize, ctx_fsl))) {
+		return ret;
+		}
+	} else {
+		/* The original statements. */
+		if (gss_decrypt_xdr_buf(kctx->enc, buf,
+				ptr + 22 - (unsigned char *)buf->head[0].iov_base)) {
+			return GSS_S_DEFECTIVE_TOKEN;
+		}
+	}
 
 	if (make_checksum("md5", ptr - 2, 8, buf,
 		 ptr + 22 - (unsigned char *)buf->head[0].iov_base, &md5cksum))
@@ -284,8 +735,22 @@
 	buf->head[0].iov_len -= (data_start - orig_start);
 	buf->len -= (data_start - orig_start);
 
-	if (gss_krb5_remove_padding(buf, blocksize))
-		return GSS_S_DEFECTIVE_TOKEN;
+	if (isE2E(ctx_fsl)) {
+		if (gss_krb5e_remove_padding(kctx, buf, offset, blocksize, ctx_fsl)) {
+		return GSS_S_DEFECTIVE_TOKEN;
+		}
+		if (remove_fsl_header(kctx, buf, offset, blocksize, ctx_fsl))  {
+		return GSS_S_DEFECTIVE_TOKEN;
+		}
+		if (isReadWrite(ctx_fsl) && isClient(ctx_fsl)) {
+		if (decrypt_pages_fsl(buf, offset, blocksize, ctx_fsl)) {
+			return GSS_S_DEFECTIVE_TOKEN;
+		}
+		}
+	} else {
+		if (gss_krb5_remove_padding(buf, blocksize))
+			return GSS_S_DEFECTIVE_TOKEN;
+	}
 
 	return GSS_S_COMPLETE;
 }
diff -ruN 2.6.22-rc3.o/net/sunrpc/auth_gss/svcauth_gss.c 2.6.22-rc3.c/net/sunrpc/auth_gss/svcauth_gss.c
--- 2.6.22-rc3.o/net/sunrpc/auth_gss/svcauth_gss.c	2007-06-11 02:45:50.000000000 -0400
+++ 2.6.22-rc3.c/net/sunrpc/auth_gss/svcauth_gss.c	2007-06-11 18:54:07.000000000 -0400
@@ -960,6 +960,14 @@
 	__be32		*rpcstart;
 	__be32		*reject_stat = resv->iov_base + resv->iov_len;
 	int		ret;
+	struct gss_ctx_fsl *ctx_fsl = NULL;
+
+	ctx_fsl = kmalloc(sizeof(struct gss_ctx_fsl), GFP_KERNEL);
+	if (!ctx_fsl)
+		goto auth_err;
+	memset(ctx_fsl, 0, sizeof(struct gss_ctx_fsl));
+	rqstp->rq_arg.ctx_fsl = ctx_fsl;
+
 
 	dprintk("RPC:       svcauth_gss: argv->iov_len = %zd\n",
 			argv->iov_len);
@@ -1126,6 +1134,19 @@
 			svc_putnl(resv, 0);
 			svc_putnl(resv, 0);
 			break;
+		case RPC_GSS_SVC_E2E:
+			ctx_fsl->isE2E = 1;
+			ctx_fsl->isRead = rqstp->rq_is_rd_fsl;
+			ctx_fsl->isClient = 0;
+			if (unwrap_priv_data(rqstp, &rqstp->rq_arg,
+					gc->gc_seq, rsci->mechctx)) {
+				printk("ERROR: unwrap_priv_data returned error\n");
+				goto auth_err;
+			}
+			/* placeholders for length and seq. number: */
+			svc_putnl(resv, 0);
+			svc_putnl(resv, 0);
+			break;
 		default:
 			goto auth_err;
 		}
@@ -1145,6 +1166,10 @@
 drop:
 	ret = SVC_DROP;
 out:
+	if (ctx_fsl != NULL) {
+		kfree(ctx_fsl);
+	}
+	rqstp->rq_arg.ctx_fsl = NULL;
 	if (rsci)
 		cache_put(&rsci->h, &rsc_cache);
 	return ret;
@@ -1284,6 +1309,13 @@
 	struct rpc_gss_wire_cred *gc = &gsd->clcred;
 	struct xdr_buf *resbuf = &rqstp->rq_res;
 	int stat = -EINVAL;
+	struct gss_ctx_fsl *ctx_fsl = NULL;
+
+	ctx_fsl = kmalloc(sizeof(struct gss_ctx_fsl), GFP_KERNEL);
+	if (!ctx_fsl)
+		goto out_err;
+	memset(ctx_fsl, 0, sizeof(struct gss_ctx_fsl));
+	resbuf->ctx_fsl = ctx_fsl;
 
 	if (gc->gc_proc != RPC_GSS_PROC_DATA)
 		goto out;
@@ -1307,6 +1339,17 @@
 		if (stat)
 			goto out_err;
 		break;
+	case RPC_GSS_SVC_E2E:
+		ctx_fsl->isE2E = 1;
+		ctx_fsl->isClient = 0;
+		ctx_fsl->isRead = rqstp->rq_is_rd_fsl;
+		ctx_fsl->offset = rqstp->rq_rd_offset;
+		stat = svcauth_gss_wrap_resp_priv(rqstp);
+		if (stat) {
+			printk("ERROR: svcauth_gss_wrap_resp_priv returned %d\n", stat);
+			goto out_err;
+		}
+		break;
 	default:
 		goto out_err;
 	}
@@ -1314,6 +1357,11 @@
 out:
 	stat = 0;
 out_err:
+	if (ctx_fsl != NULL) {
+		kfree(ctx_fsl);
+	}
+	resbuf->ctx_fsl = NULL;
+
 	if (rqstp->rq_client)
 		auth_domain_put(rqstp->rq_client);
 	rqstp->rq_client = NULL;
diff -ruN 2.6.22-rc3.o/net/sunrpc/sched.c 2.6.22-rc3.c/net/sunrpc/sched.c
--- 2.6.22-rc3.o/net/sunrpc/sched.c	2007-06-11 02:45:50.000000000 -0400
+++ 2.6.22-rc3.c/net/sunrpc/sched.c	2007-06-11 18:54:41.000000000 -0400
@@ -842,6 +842,8 @@
 	/* starting timestamp */
 	task->tk_start = jiffies;
 
+	task->tk_fsl = 0;
+
 	dprintk("RPC:       new task initialized, procpid %u\n",
 				current->pid);
 }
diff -ruN 2.6.22-rc3.o/net/sunrpc/sunrpc_syms.c 2.6.22-rc3.c/net/sunrpc/sunrpc_syms.c
--- 2.6.22-rc3.o/net/sunrpc/sunrpc_syms.c	2007-06-11 02:45:50.000000000 -0400
+++ 2.6.22-rc3.c/net/sunrpc/sunrpc_syms.c	2007-06-11 02:38:14.000000000 -0400
@@ -178,5 +178,6 @@
 #endif
 }
 MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("KRB5E");
 module_init(init_sunrpc);
 module_exit(cleanup_sunrpc);
diff -ruN 2.6.22-rc3.o/net/sunrpc/svc.c 2.6.22-rc3.c/net/sunrpc/svc.c
--- 2.6.22-rc3.o/net/sunrpc/svc.c	2007-06-11 02:45:50.000000000 -0400
+++ 2.6.22-rc3.c/net/sunrpc/svc.c	2007-06-11 18:55:06.000000000 -0400
@@ -815,6 +815,7 @@
 	rqstp->rq_res.tail[0].iov_len = 0;
 	/* Will be turned off only in gss privacy case: */
 	rqstp->rq_sendfile_ok = 1;
+	rqstp->rq_is_rd_fsl = 0;
 	/* tcp needs a space for the record length... */
 	if (rqstp->rq_prot == IPPROTO_TCP)
 		svc_putnl(resv, 0);
