Skip to contentSkip to conversation

eCryptfs version 0.1

PATCH In review Phillip Hellewell 7 participants 33 messages Nov 19, 2005 – Nov 21, 2005

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

cover eCryptfs version 0.1

This is a follow-up set of patches in response to comments made after our first submission to the LKML at the beginning of this month. We were able to track down and resolve several bugs, and we feel that eCryptfs is now ready to be merged into the -mm tree as an experimental filesystem.

We have successfully run a series of tests, including FSX, Connectathon, and Bonnie. In addition, we are able to successfully compile the Linux kernel under eCryptfs, both with and without multiple threads on a multi-processor PPC64 machine.

We are able to invoke a bug that terminates a process when we stress the filesystem with multiple concurrent operations (FSX, multiple Connectathon jobs, and a kernel compile running simultaneously), but we cannot oops the kernel with any of our test cases.

eCryptfs utilizes David Howells' keyring; at mount, eCryptfs version 0.1 expects an existing authentication token in the user's session keyring. The tarball containing the code to do this is available from the eCryptfs SourceForge site:

http://sourceforge.net/projects/ecryptfs/

Several features demonstrated in prototypes of eCryptfs at OLS in the past have been left out of this release until they can be thoroughly tested; the reduced complexity of this patch set should make it easier to evaluate for initial inclusion into the Linux kernel. Future updates will provide policy support, which will entail per-file passphrase and per-file public key support.

Thanks,
Phillip

#1 lore raw

PATCH 1/12 Makefile and Kconfig

These patches modify fs/Makefile and fs/Kconfig to provide build support for eCryptfs.

---

 Kconfig  |   10 ++++++++++
 Makefile |    1 +
 2 files changed, 11 insertions(+)
fs/Makefile +1 -0
@@ -67,6 +67,7 @@
 obj-$(CONFIG_ISO9660_FS)	+= isofs/
 obj-$(CONFIG_DEVFS_FS)		+= devfs/
 obj-$(CONFIG_HFSPLUS_FS)	+= hfsplus/ # Before hfs to find wrapped HFS+
+obj-$(CONFIG_ECRYPTFS)		+= ecryptfs/
 obj-$(CONFIG_HFS_FS)		+= hfs/
 obj-$(CONFIG_VXFS_FS)		+= freevxfs/
 obj-$(CONFIG_NFS_FS)		+= nfs/
fs/Kconfig +10 -0
@@ -989,6 +989,16 @@
 
 	  If unsure, say N.
 
+config ECRYPTFS
+	tristate "eCrypt filesystem layer support (EXPERIMENTAL)"
+	depends on EXPERIMENTAL && KEYS && CRYPTO
+	help
+	  Encrypted filesystem that operates on the VFS layer.  See
+	  Documentation/ecryptfs.txt to learn more about eCryptfs.
+
+	  To compile this file system support as a module, choose M here: the
+	  module will be called ecryptfs.
+
 config HFS_FS
 	tristate "Apple Macintosh file system support (EXPERIMENTAL)"
 	depends on EXPERIMENTAL
#2 lore raw

PATCH 2/12 Documentation

In reply to Phillip Hellewell

This patch provides documentation for using eCryptfs.

---

 ecryptfs.txt |   81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 81 insertions(+)
Documentation/ecryptfs.txt +81 -0
@@ -0,0 +1,81 @@
+eCryptfs: A stacked cryptographic filesystem for Linux
+Maintainer: Phillip Hellewell
+Lead developer: Michael A. Halcrow <[email protected]>
+Developers: Michael C. Thompson
+            Kent Yoder
+Current Release Version: 0.1
+
+This software is currently undergoing development. Make sure to
+maintain a backup copy of any data you write into eCryptfs.
+
+eCryptfs requires the userspace tools downloadable from the
+SourceForge site:
+
+http://sourceforge.net/projects/ecryptfs/
+
+Requirements include:
+ - Kernel version 2.6.15-rc1-mm1 or higher
+  - eCryptfs will work with 2.6.14, but you will need the
+    export_user_type.patch applied
+ - David Howells' userspace keyring headers and libraries, obtainable
+   from http://people.redhat.com/~dhowells/keyutils/
+  - (You will need to apply the keyutil_h_fix.diff patch to version 0.3)
+ - GnuPG Made Easy (GPGME)
+ - Building the kernel with:
+  - Cryptographic API
+  - Blowfish cipher
+  - Key retention support
+  - Filesystems->Miscellaneous filesystems->eCryptfs
+
+
+BUILD AND INSTALL INSTRUCTIONS
+
+If you are installing from the patch set:
+1) Apply export_user_type.patch to the kernel if you are running
+   kernel version 2.6.14
+2) Apply all patches in the patches/ directory to the kernel
+
+Once eCryptfs is already in the kernel:
+3) Select build options (see Requirements) and build kernel
+4) Apply keyutil_h_fix.diff to the keyutils if you are running version
+   0.3
+5) Run make and make install from the request-key/ directory.
+
+
+MOUNT-WIDE PASSPHRASE
+
+Create a new directory into which eCryptfs will write its encrypted
+files (i.e., /root/crypt).  Then, create the mount point directory
+(i.e., /mnt/crypt).  Now it's time to mount eCryptfs:
+
+mount -t ecryptfs /root/crypt /mnt/crypt
+
+You should be prompted for a passphrase and a salt (the salt may be
+blank).
+
+Try writing a new file:
+
+echo "Hello, World" > /mnt/crypt/hello.txt
+
+The operation will complete.  Notice that there is a new file in
+/root/crypt that is 3 pages (12288 bytes) in size.  This is the
+encrypted underlying file for what you just wrote.  To test reading,
+from start to finish, you need to clear the user session keyring:
+
+keyctl clear @u
+
+Then umount /mnt/crypt and mount again per the instructions given
+above.
+
+cat /mnt/crypt/hello.txt
+
+
+NOTES
+
+eCryptfs should only be mounted on (1) empty directories or (2)
+directories containing files only created by eCryptfs. If you mount a
+directory that has pre-existing files not created by eCryptfs, then
+behavior is undefined.
+
+Mike Halcrow
[email protected]
#3 lore raw

PATCH 3/12 Makefile

In reply to Phillip Hellewell

Makefile for eCryptfs.

---

 Makefile |    7 +++++++
 1 files changed, 7 insertions(+)
fs/ecryptfs/Makefile +7 -0
@@ -0,0 +1,7 @@
+#
+# Makefile for the Linux 2.6 eCryptfs
+#
+
+obj-$(CONFIG_ECRYPTFS) += ecryptfs.o
+
+ecryptfs-objs := dentry.o file.o inode.o main.o super.o mmap.o crypto.o keystore.o
#4 lore raw

PATCH 4/12 Main module functions

In reply to Phillip Hellewell

Provides functions to initialize the eCryptfs module and eCryptfs mounts. Allocates and deallocates kmem_cache regions.

---

 main.c |  734 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 734 insertions(+)
fs/ecryptfs/main.c +734 -0
@@ -0,0 +1,734 @@
+/**
+ * eCryptfs: Linux filesystem encryption layer
+ *
+ * Copyright (c) 1997-2003 Erez Zadok
+ * Copyright (c) 2001-2003 Stony Brook University
+ * Copyright (c) 2005 International Business Machines Corp.
+ *   Author(s): Michael A. Halcrow <[email protected]>
+ *              Michael C. Thompson <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <linux/dcache.h>
+#include <linux/file.h>
+#include <linux/module.h>
+#include <linux/namei.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/mount.h>
+#include <linux/dcache.h>
+#include <linux/pagemap.h>
+#include <linux/key.h>
+#include <linux/parser.h>
+#include <keys/user-type.h>
+#include "ecryptfs_kernel.h"
+
+/**
+ * Module parameter that defines the ecryptfs_verbosity level.
+ */
+int ecryptfs_verbosity = 1;
+
+module_param(ecryptfs_verbosity, int, 0);
+MODULE_PARM_DESC(ecryptfs_verbosity,
+		 "Initial verbosity level (0 or 1; defaults to "
+		 "0, which is Quiet)");
+
+void __ecryptfs_printk(int verb, const char *fmt, ...)
+{
+	va_list args;
+	va_start(args, fmt);
+	if ((ecryptfs_verbosity >= verb) && printk_ratelimit())
+		vprintk(fmt, args);
+	va_end(args);
+}
+
+/**
+ * Interposes upper and lower dentries.
+ * This function will call an ecryptfs_inode into existance through the call to
+ * iget(sb, lower_inode->i_ino).
+ * 
+ * @param lower_dentry	existing dentry in the lower filesystem
+ * @param dentry	ecryptfs' dentry
+ * @param sb		eCryptfs's super_block
+ * @param flag		If set to true, then d_add is called, else d_instantiate
+ * 			is called.
+ * @return		Zero on success; non-zero otherwise
+ */
+int ecryptfs_interpose(struct dentry *lower_dentry, struct dentry *dentry,
+		       struct super_block *sb, int flag)
+{
+	struct inode *lower_inode;
+	int err = 0;
+	struct inode *inode;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; lower_dentry = [%p], "
+			"lower_dentry->d_name.name = [%s], dentry = "
+			"[%p], dentry->d_name.name = [%s], sb = [%p]; "
+			"flag = [%.4x]; lower_dentry->d_count "
+			"= [%d]; dentry->d_count = [%d]\n", lower_dentry,
+			lower_dentry->d_name.name, dentry, dentry->d_name.name,
+			sb, flag, atomic_read(&lower_dentry->d_count),
+			atomic_read(&dentry->d_count));
+	lower_inode = lower_dentry->d_inode;
+	if (lower_inode->i_sb != ECRYPTFS_SUPERBLOCK_TO_LOWER(sb)) {
+		err = -EXDEV;
+		goto out;
+	}
+	inode = iget(sb, lower_inode->i_ino);
+	if (!inode) {
+		err = -EACCES;
+		goto out;
+	}
+	/* This check is required here because if we failed to allocated the
+	 * required space for an inode_info_cache struct, then the only way
+	 * we know we failed, is by the pointer being NULL */
+	if (!ECRYPTFS_INODE_TO_PRIVATE(inode)) {
+		ecryptfs_printk(1, KERN_ERR, "Out of memory. Failure to "
+				"allocate memory in ecryptfs_read_inode.\n");
+		err = -ENOMEM;
+		BUG();
+		goto out;
+	}
+
+	if (NULL == ECRYPTFS_INODE_TO_LOWER(inode)) {
+		ECRYPTFS_INODE_TO_LOWER(inode) = igrab(lower_inode);
+		/* If we are still NULL at this point, igrab failed.
+		 * We are _NOT_ supposed to be failing here */
+		if (NULL == ECRYPTFS_INODE_TO_LOWER(inode)) {
+			BUG();
+			err = -EINVAL;
+			goto out;
+		}
+	}
+	if (S_ISLNK(lower_inode->i_mode))
+		inode->i_op = &ecryptfs_symlink_iops;
+	else if (S_ISDIR(lower_inode->i_mode))
+		inode->i_op = &ecryptfs_dir_iops;
+	if (S_ISDIR(lower_inode->i_mode))
+		inode->i_fop = &ecryptfs_dir_fops;
+	/* TODO: Is there a better way to identify if the inode is
+	 * special? */
+	if (S_ISBLK(lower_inode->i_mode) || S_ISCHR(lower_inode->i_mode) ||
+	    S_ISFIFO(lower_inode->i_mode) || S_ISSOCK(lower_inode->i_mode))
+		init_special_inode(inode, lower_inode->i_mode,
+				   lower_inode->i_rdev);
+	dentry->d_op = &ecryptfs_dops;
+	if (flag)
+		d_add(dentry, inode);
+	else
+		d_instantiate(dentry, inode);
+	ecryptfs_copy_attr_all(inode, lower_inode);
+	/* This size will be overwritten for real files w/ headers and
+	 * other metadata */
+	ecryptfs_copy_inode_size(inode, lower_inode);
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; err = %d\n", err);
+	return err;
+}
+
+/**
+ * For rvalue references; better to just use the macro.
+ */
+inline struct dentry *ecryptfs_lower_dentry(struct dentry *dentry)
+{
+	ASSERT(dentry);
+	return ECRYPTFS_DENTRY_TO_LOWER(dentry);
+}
+
+enum { ecryptfs_opt_sig, ecryptfs_opt_debug, ecryptfs_opt_cipher,
+	ecryptfs_opt_err
+};
+
+static match_table_t tokens = {
+	{ecryptfs_opt_sig, "sig=%s"},
+	{ecryptfs_opt_debug, "debug=%u"},
+	{ecryptfs_opt_cipher, "cipher=%s"},
+	{ecryptfs_opt_err, NULL}
+};
+
+/**
+ * Parse mount options:
+ * debug=N 	   - ecryptfs_verbosity level for debug output
+ * sig=XXX	   - description(signature) of the key to use
+ * 
+ * Returns the dentry object of the lower-level (lower/interposed)
+ * directory; We want to mount our stackable file system on top of
+ * that lower directory.
+ *
+ * N.B. The signature of the key to use must be the description of a key
+ * 	already in the keyring. Mounting will fail if the key can not be
+ * 	found.
+ *
+ * @param sb
+ * @param options
+ * @return Zero on success; non-zero on error
+ */
+static int ecryptfs_parse_options(struct super_block *sb, char *options)
+{
+	char *p;
+	int rc = 0;
+	int sig_set = 0;
+	int cipher_name_set = 0;
+	struct key *auth_tok_key = NULL;
+	struct ecryptfs_auth_tok *auth_tok = NULL;
+	struct ecryptfs_mount_crypt_stats *mount_crypt_stats =
+		&(ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb)->mount_crypt_stats);
+	substring_t args[MAX_OPT_ARGS];
+	int token;
+	char *sig_src;
+	char *sig_dst;
+	char *debug_src;
+	char *cipher_name_dst;
+	char *cipher_name_src;
+	int cipher_name_len;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; options = [%s]\n", options);
+	if (!options) {
+		rc = -EINVAL;
+		goto out;
+	}
+	while ((p = strsep(&options, ",")) != NULL) {
+		if (!*p)
+			continue;
+		token = match_token(p, tokens, args);
+		switch (token) {
+		case ecryptfs_opt_sig:
+			sig_src = args[0].from;
+			sig_dst =
+				mount_crypt_stats->global_auth_tok_sig;
+			memcpy(sig_dst, sig_src, ECRYPTFS_SIG_SIZE_HEX);
+			sig_dst[ECRYPTFS_SIG_SIZE_HEX] = '\0';
+			ecryptfs_printk(1, KERN_NOTICE,
+					"The mount_crypt_stats "
+					"global_auth_tok_sig set to: "
+					"[%s]\n", sig_dst);
+			sig_set = 1;
+			break;
+		case ecryptfs_opt_debug:
+			debug_src = args[0].from;
+			ecryptfs_verbosity =
+				(int)simple_strtol(debug_src, &debug_src,
+						   0);
+			ecryptfs_printk(1, KERN_NOTICE,
+					"Verbosity set to [%d]" "\n",
+					ecryptfs_verbosity);
+			break;
+		case ecryptfs_opt_cipher:
+			cipher_name_src = args[0].from;
+			cipher_name_dst =
+				mount_crypt_stats->
+				global_default_cipher_name;
+			strncpy(cipher_name_dst, cipher_name_src,
+				ECRYPTFS_MAX_CIPHER_NAME_SIZE);
+			ecryptfs_printk(1, KERN_NOTICE,
+					"The mount_crypt_stats "
+					"global_default_cipher_name set to: "
+					"[%s]\n", cipher_name_dst);
+			cipher_name_set = 1;
+			break;
+		case ecryptfs_opt_err:
+		default:
+			ecryptfs_printk(1, KERN_WARNING,
+					"eCryptfs: unrecognized option '%s'\n",
+					options);
+		}
+	}
+	/* Do not support lack of mount-wide signature in 0.1
+	 * release */
+	if (!sig_set) {
+		rc = -EINVAL;
+		ecryptfs_printk(0, KERN_ERR, "You must supply a valid "
+				"passphrase auth tok signature as a mount "
+				"parameter; see the eCryptfs README\n");
+		goto out;
+	}
+	if (!cipher_name_set) {
+		cipher_name_len = strlen(ECRYPTFS_DEFAULT_CIPHER);
+		if (unlikely(cipher_name_len 
+			     >= ECRYPTFS_MAX_CIPHER_NAME_SIZE)) {
+			rc = -EINVAL;
+			BUG();
+			goto out;
+		}
+		memcpy(mount_crypt_stats->global_default_cipher_name,
+		       ECRYPTFS_DEFAULT_CIPHER, cipher_name_len);
+		mount_crypt_stats->global_default_cipher_name[cipher_name_len]
+		    = '\0';
+	}
+	ecryptfs_printk(1, KERN_NOTICE, "Requesting the key with description: "
+			"[%s]\n", mount_crypt_stats->global_auth_tok_sig);
+	/* N.B. The reference to this key is held until umount is done
+	 * The call to key_put is done in ecryptfs_put_super() */
+	auth_tok_key = request_key(&key_type_user,
+				   mount_crypt_stats->global_auth_tok_sig,
+				   NULL);
+	if (!auth_tok_key || IS_ERR(auth_tok_key)) {
+		ecryptfs_printk(0, KERN_ERR, "Could not find key with "
+				"description: [%s]\n",
+				mount_crypt_stats->global_auth_tok_sig);
+		process_request_key_err(PTR_ERR(auth_tok_key));
+		rc = -EINVAL;
+		goto out;
+	}
+	auth_tok = (struct ecryptfs_auth_tok *)KEY_PAYLOAD_DATA(auth_tok_key);
+	if (auth_tok->instanceof != ECRYPTFS_PASSWORD) {
+		ecryptfs_printk(0, KERN_ERR, "Invalid auth_tok structure "
+				"returned from key");
+		rc = -EINVAL;
+		goto out;
+	}
+	mount_crypt_stats->global_auth_tok_key = auth_tok_key;
+	mount_crypt_stats->global_auth_tok = auth_tok;
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
+
+kmem_cache_t *ecryptfs_sb_info_cache;
+
+/**
+ * Preform the cleanup for ecryptfs_read_super()
+ */
+static inline void ecryptfs_cleanup_read_super(struct super_block *sb)
+{
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; sb = [%p], sb->s_root = [%p] "
+			"ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb) = [%p] "
+			"sb->s_root.d_name->name = [%s]\n", sb,
+			sb->s_root, ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb),
+			sb->s_root->d_name.name);
+	up_write(&sb->s_umount);
+	deactivate_super(sb);
+	ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+}
+
+/**
+ * Sets up what we can of the sb, rest is done in ecryptfs_read_super
+ *
+ * @param sb		The ecryptfs super block
+ * @param raw_data	The options passed to mount
+ * @param silent	Not used but required by function prototype
+ * @return 		Zero on success; non-zero otherwise
+ */
+static int
+ecryptfs_fill_super(struct super_block *sb, void *raw_data, int silent)
+{
+	int err = 0;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; sb = [%p] raw_data = [%s] "
+			"silent = [%d]\n", sb, (char *)raw_data, silent);
+	/* Released in ecryptfs_put_super() */
+	ECRYPTFS_SUPERBLOCK_TO_PRIVATE_SM(sb) =
+		kmem_cache_alloc(ecryptfs_sb_info_cache, SLAB_KERNEL);
+	if (!ECRYPTFS_SUPERBLOCK_TO_PRIVATE_SM(sb)) {
+		ecryptfs_printk(0, KERN_WARNING, "Out of memory\n");
+		err = -ENOMEM;
+		goto out;
+	}
+	memset(ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb), 0, 
+	       sizeof(struct ecryptfs_sb_info));
+	sb->s_op = &ecryptfs_sops;
+	/* Released through deactivate_super(sb) from get_sb_nodev */
+	sb->s_root = d_alloc(NULL, &(const struct qstr) {
+			     .hash = 0,.name = "/",.len = 1});
+	if (!sb->s_root) {
+		ecryptfs_printk(0, KERN_ERR, "d_alloc failed\n");
+		err = -ENOMEM;
+		goto out;
+	}
+	sb->s_root->d_op = &ecryptfs_dops;
+	sb->s_root->d_sb = sb;
+	sb->s_root->d_parent = sb->s_root;
+	/* Released in d_release when dput(sb->s_root) is called */
+	/* through deactivate_super(sb) from get_sb_nodev() */
+	ECRYPTFS_DENTRY_TO_PRIVATE_SM(sb->s_root) =
+		(struct ecryptfs_dentry_info *)
+		kmem_cache_alloc(ecryptfs_dentry_info_cache, SLAB_KERNEL);
+	if (!ECRYPTFS_DENTRY_TO_PRIVATE_SM(sb->s_root)) {
+		ecryptfs_printk(0, KERN_ERR,
+				"dentry_info_cache alloc failed\n");
+		err = -ENOMEM;
+		goto out;
+	}
+	memset(ECRYPTFS_DENTRY_TO_PRIVATE(sb->s_root), 0,
+	       sizeof(struct ecryptfs_dentry_info));
+	err = 0;
+out:
+	/* Should be able to rely on deactive_super called from get_sb_nodev */
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n", err);
+	return err;
+}
+
+/**
+ * Read the super block of the lower filesystem, and use ecryptfs_interpose
+ * to create our initial inode and super block struct
+ */
+static int ecryptfs_read_super(struct super_block *sb, const char *dev_name)
+{
+	int err;
+	struct nameidata nd;
+	struct dentry *lower_root;
+
+	memset(&nd, 0, sizeof(struct nameidata));
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; sb = [%p], dev_name = [%s]\n",
+			sb, dev_name);
+	err = path_lookup(dev_name, LOOKUP_FOLLOW, &nd);
+	if (err) {
+		ecryptfs_printk(0, KERN_WARNING, "path_lookup() failed\n");
+		goto out_free;
+	}
+	lower_root = nd.dentry;
+	ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb)->lower_mnt = nd.mnt;
+	if (!lower_root->d_inode) {
+		ecryptfs_printk(0, KERN_WARNING,
+				"No directory to interpose on\n");
+		err = -ENOENT;
+		goto out_free;
+	}
+	ECRYPTFS_SUPERBLOCK_TO_LOWER(sb) = lower_root->d_sb;
+	sb->s_maxbytes = lower_root->d_sb->s_maxbytes;
+	ECRYPTFS_DENTRY_TO_LOWER(sb->s_root) = lower_root;
+	if ((err = ecryptfs_interpose(lower_root, sb->s_root, sb, 0)))
+		goto out_free;
+	err = 0;
+	goto out;
+out_free:
+	path_release(&nd);
+	ecryptfs_cleanup_read_super(sb);
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n", err);
+	return err;
+}
+
+/**
+ * The whole ecryptfs_get_sb process is broken into 4 functions:
+ * ecryptfs_parse_options(): handle options passed to ecryptfs, if any
+ * ecryptfs_fill_super(): used by get_sb_nodev, fills out the super_block
+ *                        with as much information as it can before needing
+ *                        the lower filesystem.
+ * ecryptfs_read_super(): this accesses the lower filesystem and uses
+ *                        ecryptfs_interpolate to perform most of the linking
+ * ecryptfs_interpolate(): links the lower filesystem into ecryptfs 
+ */
+static struct super_block *ecryptfs_get_sb(struct file_system_type *fs_type,
+					   int flags, const char *dev_name,
+					   void *raw_data)
+{
+	int err;
+	struct super_block *sb = NULL;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; fs_type = [%p], flags = [%d],"
+			" dev_name = [%s], raw_data = [%s]\n",
+			fs_type, flags, dev_name, (char *)raw_data);
+	sb = get_sb_nodev(fs_type, flags, raw_data, ecryptfs_fill_super);
+	if (IS_ERR(sb)) {
+		ecryptfs_printk(0, KERN_ERR, "Getting sb failed. "
+				"sb = [%p]\n", sb);
+		goto out;
+	}
+	err = ecryptfs_parse_options(sb, raw_data);
+	if (err) {
+		sb = ERR_PTR(err);
+		goto out;
+	}
+	err = ecryptfs_read_super(sb, dev_name);
+	if (err) {
+		sb = ERR_PTR(err);
+		ecryptfs_printk(0, KERN_ERR, "Reading sb failed. "
+				"sb = [%p]\n", sb);
+	}
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%p]\n", sb);
+	return sb;
+}
+
+/**
+ * Used to bring the superblock down and free the private data.
+ * Private data is free'd in ecryptfs_put_super()
+ */
+static void ecryptfs_kill_block_super(struct super_block *sb)
+{
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; sb = [%p], sb->s_root = [%p] "
+			"ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb) = [%p]\n", sb,
+			sb->s_root, ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb));
+	generic_shutdown_super(sb);
+	ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+}
+
+static struct file_system_type ecryptfs_fs_type = {
+	.owner = THIS_MODULE,
+	.name = "ecryptfs",
+	.get_sb = ecryptfs_get_sb,
+	.kill_sb = ecryptfs_kill_block_super,
+	.fs_flags = 0
+};
+
+/**
+ * Initializes the ecryptfs_inode_info_cache when it is created
+ */
+static void
+inode_info_init_once(void *vptr, kmem_cache_t * cachep, unsigned long flags)
+{
+	struct ecryptfs_inode_info *ei = (struct ecryptfs_inode_info *)vptr;
+
+	if ((flags & (SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR)) ==
+	    SLAB_CTOR_CONSTRUCTOR)
+		inode_init_once(&ei->vfs_inode);
+}
+
+/* This provides a means of backing out cache creations out of the kernel
+ * so that we can elegantly fail should we run out of memory.
+ */
+#define ECRYPTFS_AUTH_TOK_LIST_ITEM_CACHE 	0x0001
+#define ECRYPTFS_AUTH_TOK_PKT_SET_CACHE         0x0002
+#define ECRYPTFS_AUTH_TOK_REQUEST_CACHE         0x0004
+#define ECRYPTFS_AUTH_TOK_REQUEST_BLOB_CACHE    0x0008
+#define ECRYPTFS_FILE_INFO_CACHE 		0x0010
+#define ECRYPTFS_DENTRY_INFO_CACHE 		0x0020
+#define ECRYPTFS_INODE_INFO_CACHE 		0x0040
+#define ECRYPTFS_SB_INFO_CACHE 			0x0080
+#define ECRYPTFS_HEADER_CACHE_0 		0x0100
+#define ECRYPTFS_HEADER_CACHE_1 		0x0200
+#define ECRYPTFS_HEADER_CACHE_2 		0x0400
+#define ECRYPTFS_LOWER_PAGE_CACHE 		0x0800
+#define ECRYPTFS_CACHE_CREATION_SUCCESS		0x0FF1
+
+static short ecryptfs_allocated_caches;
+
+/**
+ * @return Zero on success; non-zero otherwise
+ *
+ * Sets ecryptfs_allocated_caches with flags so that we can
+ * free created caches should we run out of memory during
+ * creation period.
+ *
+ * The overhead for doing this is offset by the fact that we
+ * only do this once, and that should there be insufficient
+ * memory, then we can elegantly fail, and not leave extra
+ * caches around, or worse, panic the kernel trying to free
+ * something that's not there.
+ */
+static int ecryptfs_init_kmem_caches(void)
+{
+	int rc = 0;
+
+	ecryptfs_auth_tok_list_item_cache =
+	    kmem_cache_create("ecryptfs_auth_tok_list_item",
+			      sizeof(struct ecryptfs_auth_tok_list_item),
+			      0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+	if (ecryptfs_auth_tok_list_item_cache)
+		rc |= ECRYPTFS_AUTH_TOK_LIST_ITEM_CACHE;
+	else
+		ecryptfs_printk(0, KERN_WARNING, "ecryptfs_auth_tok_list_item "
+				"kmem_cache_create failed\n");
+
+	ecryptfs_file_info_cache =
+	    kmem_cache_create("ecryptfs_file_cache",
+			      sizeof(struct ecryptfs_file_info),
+			      0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+	if (ecryptfs_file_info_cache)
+		rc |= ECRYPTFS_FILE_INFO_CACHE;
+	else
+		ecryptfs_printk(0, KERN_WARNING, "ecryptfs_file_cache "
+				"kmem_cache_create failed\n");
+
+	ecryptfs_dentry_info_cache =
+	    kmem_cache_create("ecryptfs_dentry_cache",
+			      sizeof(struct ecryptfs_dentry_info),
+			      0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+	if (ecryptfs_dentry_info_cache)
+		rc |= ECRYPTFS_DENTRY_INFO_CACHE;
+	else
+		ecryptfs_printk(0, KERN_WARNING, "ecryptfs_dentry_cache "
+				"kmem_cache_create failed\n");
+
+	ecryptfs_inode_info_cache =
+	    kmem_cache_create("ecryptfs_inode_cache",
+			      sizeof(struct ecryptfs_inode_info), 0,
+			      SLAB_HWCACHE_ALIGN, inode_info_init_once, NULL);
+	if (ecryptfs_inode_info_cache)
+		rc |= ECRYPTFS_INODE_INFO_CACHE;
+	else
+		ecryptfs_printk(0, KERN_WARNING, "ecryptfs_inode_cache "
+				"kmem_cache_create failed\n");
+
+	ecryptfs_sb_info_cache =
+	    kmem_cache_create("ecryptfs_sb_cache",
+			      sizeof(struct ecryptfs_sb_info),
+			      0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+	if (ecryptfs_sb_info_cache)
+		rc |= ECRYPTFS_SB_INFO_CACHE;
+	else
+		ecryptfs_printk(0, KERN_WARNING, "ecryptfs_sb_cache "
+				"kmem_cache_create failed\n");
+
+	ecryptfs_header_cache_0 =
+	    kmem_cache_create("ecryptfs_headers_0", PAGE_CACHE_SIZE,
+			      0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+	if (ecryptfs_header_cache_0)
+		rc |= ECRYPTFS_HEADER_CACHE_0;
+	else
+		ecryptfs_printk(0, KERN_WARNING, "ecryptfs_headers_0 "
+				"kmem_cache_create failed\n");
+
+	ecryptfs_header_cache_1 =
+	    kmem_cache_create("ecryptfs_headers_1", PAGE_CACHE_SIZE,
+			      0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+	if (ecryptfs_header_cache_1)
+		rc |= ECRYPTFS_HEADER_CACHE_1;
+	else
+		ecryptfs_printk(0, KERN_WARNING, "ecryptfs_headers_1 "
+				"kmem_cache_create failed\n");
+
+	ecryptfs_header_cache_2 =
+	    kmem_cache_create("ecryptfs_headers_2", PAGE_CACHE_SIZE,
+			      0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+	if (ecryptfs_header_cache_2)
+		rc |= ECRYPTFS_HEADER_CACHE_2;
+	else
+		ecryptfs_printk(0, KERN_WARNING, "ecryptfs_headers_2 "
+				"kmem_cache_create failed\n");
+
+	ecryptfs_lower_page_cache =
+	    kmem_cache_create("ecryptfs_lower_page_cache", PAGE_CACHE_SIZE,
+			      0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+	if (ecryptfs_lower_page_cache)
+		rc |= ECRYPTFS_LOWER_PAGE_CACHE;
+	else
+		ecryptfs_printk(0, KERN_WARNING, "ecryptfs_lower_page_cache "
+				"kmem_cache_create failed\n");
+
+	ecryptfs_allocated_caches = rc;
+	rc = ECRYPTFS_CACHE_CREATION_SUCCESS ^ rc;
+	return rc;
+}
+
+/**
+ * @return Zero on success; non-zero otherwise
+ */
+static int ecryptfs_free_kmem_caches(void)
+{
+	int rc = 0;
+	int err;
+
+	if (ecryptfs_allocated_caches & ECRYPTFS_AUTH_TOK_LIST_ITEM_CACHE) {
+		rc = kmem_cache_destroy(ecryptfs_auth_tok_list_item_cache);
+		if (rc)
+			ecryptfs_printk(0, KERN_WARNING,
+					"Not all ecryptfs_auth_tok_"
+					"list_item_cache structures were "
+					"freed\n");
+	}
+	if (ecryptfs_allocated_caches & ECRYPTFS_FILE_INFO_CACHE) {
+		err = kmem_cache_destroy(ecryptfs_file_info_cache);
+		if (err)
+			ecryptfs_printk(0, KERN_WARNING,
+					"Not all ecryptfs_file_info_"
+					"cache regions were freed\n");
+		rc |= err;
+	}
+	if (ecryptfs_allocated_caches & ECRYPTFS_DENTRY_INFO_CACHE) {
+		err = kmem_cache_destroy(ecryptfs_dentry_info_cache);
+		if (err)
+			ecryptfs_printk(0, KERN_WARNING,
+					"Not all ecryptfs_dentry_info_"
+					"cache regions were freed\n");
+		rc |= err;
+	}
+	if (ecryptfs_allocated_caches & ECRYPTFS_INODE_INFO_CACHE) {
+		err = kmem_cache_destroy(ecryptfs_inode_info_cache);
+		if (err)
+			ecryptfs_printk(0, KERN_WARNING,
+					"Not all ecryptfs_inode_info_"
+					"cache regions were freed\n");
+		rc |= err;
+	}
+	if (ecryptfs_allocated_caches & ECRYPTFS_SB_INFO_CACHE) {
+		err = kmem_cache_destroy(ecryptfs_sb_info_cache);
+		if (err)
+			ecryptfs_printk(0, KERN_WARNING,
+					"Not all ecryptfs_sb_info_"
+					"cache regions were freed\n");
+		rc |= err;
+	}
+	if (ecryptfs_allocated_caches & ECRYPTFS_HEADER_CACHE_0) {
+		err = kmem_cache_destroy(ecryptfs_header_cache_0);
+		if (err)
+			ecryptfs_printk(0, KERN_WARNING, "Not all ecryptfs_"
+					"header_cache_0 regions were freed\n");
+		rc |= err;
+	}
+	if (ecryptfs_allocated_caches & ECRYPTFS_HEADER_CACHE_1) {
+		err = kmem_cache_destroy(ecryptfs_header_cache_1);
+		if (err)
+			ecryptfs_printk(0, KERN_WARNING, "Not all ecryptfs_"
+					"header_cache_1 regions were freed\n");
+		rc |= err;
+	}
+	if (ecryptfs_allocated_caches & ECRYPTFS_HEADER_CACHE_2) {
+		err = kmem_cache_destroy(ecryptfs_header_cache_2);
+		if (err)
+			ecryptfs_printk(0, KERN_WARNING, "Not all ecryptfs_"
+					"header_cache_2 regions were freed\n");
+		rc |= err;
+	}
+	if (ecryptfs_allocated_caches & ECRYPTFS_LOWER_PAGE_CACHE) {
+		err = kmem_cache_destroy(ecryptfs_lower_page_cache);
+		if (err)
+			ecryptfs_printk(0, KERN_WARNING, "Not all ecryptfs_"
+					"lower_page_cache regions were "
+					"freed\n");
+		rc |= err;
+	}
+	return rc;
+}
+
+static int __init init_ecryptfs_fs(void)
+{
+	int rc;
+
+	rc = ecryptfs_init_kmem_caches();
+	if (rc) {
+		ecryptfs_printk(0, KERN_EMERG, "Failure occured while "
+				"attempting to create caches [CREATED: %x]."
+				"Now freeing caches.\n",
+				ecryptfs_allocated_caches);
+		ecryptfs_free_kmem_caches();
+		return -ENOMEM;
+	}
+	ecryptfs_printk(1, KERN_NOTICE, "Registering eCryptfs\n");
+	return register_filesystem(&ecryptfs_fs_type);
+}
+
+static void __exit exit_ecryptfs_fs(void)
+{
+	int rc;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Unregistering eCryptfs\n");
+	unregister_filesystem(&ecryptfs_fs_type);
+	rc = ecryptfs_free_kmem_caches();
+	if (rc)
+		ecryptfs_printk(0, KERN_EMERG, "Failure occured while "
+				"attempting to free caches: [%d]\n", rc);
+}
+
+MODULE_AUTHOR("Michael A. Halcrow <[email protected]>");
+MODULE_DESCRIPTION("eCryptfs");
+
+MODULE_LICENSE("GPL");
+
+module_init(init_ecryptfs_fs)
+module_exit(exit_ecryptfs_fs)
#5 lore raw

Re: [PATCH 4/12: eCryptfs] Main module functions

Quoted 16 lines · Phillip Hellewell
fs/ecryptfs/main.c +16 -0
+int ecryptfs_verbosity = 1;
+
+module_param(ecryptfs_verbosity, int, 0);
+MODULE_PARM_DESC(ecryptfs_verbosity,
+                "Initial verbosity level (0 or 1; defaults to "
+                "0, which is Quiet)");
+
+void __ecryptfs_printk(int verb, const char *fmt, ...)
+{
+       va_list args;
+       va_start(args, fmt);
+       if ((ecryptfs_verbosity >= verb) && printk_ratelimit())
+               vprintk(fmt, args);
+       va_end(args);
+}
+

[snip]

Quoted 5 lines
fs/ecryptfs/main.c +5 -0
+int ecryptfs_interpose(struct dentry *lower_dentry, struct dentry *dentry,
+                      struct super_block *sb, int flag)
+{
+       struct inode *lower_inode;
+       int err = 0;

fs/ecryptfs/main.c line 85

Quoted 10 lines
fs/ecryptfs/main.c +10 -0
+       struct inode *inode;
+
+       ecryptfs_printk(1, KERN_NOTICE, "Enter; lower_dentry = [%p], "
+                       "lower_dentry->d_name.name = [%s], dentry = "
+                       "[%p], dentry->d_name.name = [%s], sb = [%p]; "
+                       "flag = [%.4x]; lower_dentry->d_count "
+                       "= [%d]; dentry->d_count = [%d]\n", lower_dentry,
+                       lower_dentry->d_name.name, dentry, dentry->d_name.name,
+                       sb, flag, atomic_read(&lower_dentry->d_count),
+                       atomic_read(&dentry->d_count));

Could you use KERN_DEBUG instead and drop ecryptfs_printk()?

fs/ecryptfs/main.c line 111

Quoted 5 lines
fs/ecryptfs/main.c +5 -0
+       if (NULL == ECRYPTFS_INODE_TO_LOWER(inode)) {
+               ECRYPTFS_INODE_TO_LOWER(inode) = igrab(lower_inode);
+               /* If we are still NULL at this point, igrab failed.
+                * We are _NOT_ supposed to be failing here */
+               if (NULL == ECRYPTFS_INODE_TO_LOWER(inode)) {

If you're worried about assignment, why not use the normal idiom:

   if (!p) {
      ...
   }

fs/ecryptfs/main.c line 114

Quoted 3 lines
fs/ecryptfs/main.c +3 -0
+                       BUG();
+                       err = -EINVAL;
+                       goto out;

Why do you want to BUG() and then handle the situation?

Quoted 1 line

+kmem_cache_t *ecryptfs_sb_info_cache;

We have struct kmem_cache now so please consider using it instead.

Quoted 6 lines
fs/ecryptfs/main.c +6 -0
+/* This provides a means of backing out cache creations out of the kernel
+ * so that we can elegantly fail should we run out of memory.
+ */
+#define ECRYPTFS_AUTH_TOK_LIST_ITEM_CACHE      0x0001
+#define ECRYPTFS_AUTH_TOK_PKT_SET_CACHE         0x0002
+#define ECRYPTFS_AUTH_TOK_REQUEST_CACHE         0x0004

fs/ecryptfs/main.c line 507

Quoted 10 lines
fs/ecryptfs/main.c +10 -0
+#define ECRYPTFS_AUTH_TOK_REQUEST_BLOB_CACHE    0x0008
+#define ECRYPTFS_FILE_INFO_CACHE               0x0010
+#define ECRYPTFS_DENTRY_INFO_CACHE             0x0020
+#define ECRYPTFS_INODE_INFO_CACHE              0x0040
+#define ECRYPTFS_SB_INFO_CACHE                         0x0080
+#define ECRYPTFS_HEADER_CACHE_0                0x0100
+#define ECRYPTFS_HEADER_CACHE_1                0x0200
+#define ECRYPTFS_HEADER_CACHE_2                0x0400
+#define ECRYPTFS_LOWER_PAGE_CACHE              0x0800
+#define ECRYPTFS_CACHE_CREATION_SUCCESS                0x0FF1

A better way would be to either (1) make ecryptfs_init_kmem_caches() clean up after itself on error using gotos or (2) keep caches NULL before initialization and check for that in ecryptfs_free_kmem_caches().

Quoted 196 lines
fs/ecryptfs/main.c +196 -0
+
+static short ecryptfs_allocated_caches;
+
+/**
+ * @return Zero on success; non-zero otherwise
+ *
+ * Sets ecryptfs_allocated_caches with flags so that we can
+ * free created caches should we run out of memory during
+ * creation period.
+ *
+ * The overhead for doing this is offset by the fact that we
+ * only do this once, and that should there be insufficient
+ * memory, then we can elegantly fail, and not leave extra
+ * caches around, or worse, panic the kernel trying to free
+ * something that's not there.
+ */
+static int ecryptfs_init_kmem_caches(void)
+{
+       int rc = 0;
+
+       ecryptfs_auth_tok_list_item_cache =
+           kmem_cache_create("ecryptfs_auth_tok_list_item",
+                             sizeof(struct ecryptfs_auth_tok_list_item),
+                             0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+       if (ecryptfs_auth_tok_list_item_cache)
+               rc |= ECRYPTFS_AUTH_TOK_LIST_ITEM_CACHE;
+       else
+               ecryptfs_printk(0, KERN_WARNING, "ecryptfs_auth_tok_list_item "
+                               "kmem_cache_create failed\n");
+
+       ecryptfs_file_info_cache =
+           kmem_cache_create("ecryptfs_file_cache",
+                             sizeof(struct ecryptfs_file_info),
+                             0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+       if (ecryptfs_file_info_cache)
+               rc |= ECRYPTFS_FILE_INFO_CACHE;
+       else
+               ecryptfs_printk(0, KERN_WARNING, "ecryptfs_file_cache "
+                               "kmem_cache_create failed\n");
+
+       ecryptfs_dentry_info_cache =
+           kmem_cache_create("ecryptfs_dentry_cache",
+                             sizeof(struct ecryptfs_dentry_info),
+                             0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+       if (ecryptfs_dentry_info_cache)
+               rc |= ECRYPTFS_DENTRY_INFO_CACHE;
+       else
+               ecryptfs_printk(0, KERN_WARNING, "ecryptfs_dentry_cache "
+                               "kmem_cache_create failed\n");
+
+       ecryptfs_inode_info_cache =
+           kmem_cache_create("ecryptfs_inode_cache",
+                             sizeof(struct ecryptfs_inode_info), 0,
+                             SLAB_HWCACHE_ALIGN, inode_info_init_once, NULL);
+       if (ecryptfs_inode_info_cache)
+               rc |= ECRYPTFS_INODE_INFO_CACHE;
+       else
+               ecryptfs_printk(0, KERN_WARNING, "ecryptfs_inode_cache "
+                               "kmem_cache_create failed\n");
+
+       ecryptfs_sb_info_cache =
+           kmem_cache_create("ecryptfs_sb_cache",
+                             sizeof(struct ecryptfs_sb_info),
+                             0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+       if (ecryptfs_sb_info_cache)
+               rc |= ECRYPTFS_SB_INFO_CACHE;
+       else
+               ecryptfs_printk(0, KERN_WARNING, "ecryptfs_sb_cache "
+                               "kmem_cache_create failed\n");
+
+       ecryptfs_header_cache_0 =
+           kmem_cache_create("ecryptfs_headers_0", PAGE_CACHE_SIZE,
+                             0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+       if (ecryptfs_header_cache_0)
+               rc |= ECRYPTFS_HEADER_CACHE_0;
+       else
+               ecryptfs_printk(0, KERN_WARNING, "ecryptfs_headers_0 "
+                               "kmem_cache_create failed\n");
+
+       ecryptfs_header_cache_1 =
+           kmem_cache_create("ecryptfs_headers_1", PAGE_CACHE_SIZE,
+                             0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+       if (ecryptfs_header_cache_1)
+               rc |= ECRYPTFS_HEADER_CACHE_1;
+       else
+               ecryptfs_printk(0, KERN_WARNING, "ecryptfs_headers_1 "
+                               "kmem_cache_create failed\n");
+
+       ecryptfs_header_cache_2 =
+           kmem_cache_create("ecryptfs_headers_2", PAGE_CACHE_SIZE,
+                             0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+       if (ecryptfs_header_cache_2)
+               rc |= ECRYPTFS_HEADER_CACHE_2;
+       else
+               ecryptfs_printk(0, KERN_WARNING, "ecryptfs_headers_2 "
+                               "kmem_cache_create failed\n");
+
+       ecryptfs_lower_page_cache =
+           kmem_cache_create("ecryptfs_lower_page_cache", PAGE_CACHE_SIZE,
+                             0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+       if (ecryptfs_lower_page_cache)
+               rc |= ECRYPTFS_LOWER_PAGE_CACHE;
+       else
+               ecryptfs_printk(0, KERN_WARNING, "ecryptfs_lower_page_cache "
+                               "kmem_cache_create failed\n");
+
+       ecryptfs_allocated_caches = rc;
+       rc = ECRYPTFS_CACHE_CREATION_SUCCESS ^ rc;
+       return rc;
+}
+
+/**
+ * @return Zero on success; non-zero otherwise
+ */
+static int ecryptfs_free_kmem_caches(void)
+{
+       int rc = 0;
+       int err;
+
+       if (ecryptfs_allocated_caches & ECRYPTFS_AUTH_TOK_LIST_ITEM_CACHE) {
+               rc = kmem_cache_destroy(ecryptfs_auth_tok_list_item_cache);
+               if (rc)
+                       ecryptfs_printk(0, KERN_WARNING,
+                                       "Not all ecryptfs_auth_tok_"
+                                       "list_item_cache structures were "
+                                       "freed\n");
+       }
+       if (ecryptfs_allocated_caches & ECRYPTFS_FILE_INFO_CACHE) {
+               err = kmem_cache_destroy(ecryptfs_file_info_cache);
+               if (err)
+                       ecryptfs_printk(0, KERN_WARNING,
+                                       "Not all ecryptfs_file_info_"
+                                       "cache regions were freed\n");
+               rc |= err;
+       }
+       if (ecryptfs_allocated_caches & ECRYPTFS_DENTRY_INFO_CACHE) {
+               err = kmem_cache_destroy(ecryptfs_dentry_info_cache);
+               if (err)
+                       ecryptfs_printk(0, KERN_WARNING,
+                                       "Not all ecryptfs_dentry_info_"
+                                       "cache regions were freed\n");
+               rc |= err;
+       }
+       if (ecryptfs_allocated_caches & ECRYPTFS_INODE_INFO_CACHE) {
+               err = kmem_cache_destroy(ecryptfs_inode_info_cache);
+               if (err)
+                       ecryptfs_printk(0, KERN_WARNING,
+                                       "Not all ecryptfs_inode_info_"
+                                       "cache regions were freed\n");
+               rc |= err;
+       }
+       if (ecryptfs_allocated_caches & ECRYPTFS_SB_INFO_CACHE) {
+               err = kmem_cache_destroy(ecryptfs_sb_info_cache);
+               if (err)
+                       ecryptfs_printk(0, KERN_WARNING,
+                                       "Not all ecryptfs_sb_info_"
+                                       "cache regions were freed\n");
+               rc |= err;
+       }
+       if (ecryptfs_allocated_caches & ECRYPTFS_HEADER_CACHE_0) {
+               err = kmem_cache_destroy(ecryptfs_header_cache_0);
+               if (err)
+                       ecryptfs_printk(0, KERN_WARNING, "Not all ecryptfs_"
+                                       "header_cache_0 regions were freed\n");
+               rc |= err;
+       }
+       if (ecryptfs_allocated_caches & ECRYPTFS_HEADER_CACHE_1) {
+               err = kmem_cache_destroy(ecryptfs_header_cache_1);
+               if (err)
+                       ecryptfs_printk(0, KERN_WARNING, "Not all ecryptfs_"
+                                       "header_cache_1 regions were freed\n");
+               rc |= err;
+       }
+       if (ecryptfs_allocated_caches & ECRYPTFS_HEADER_CACHE_2) {
+               err = kmem_cache_destroy(ecryptfs_header_cache_2);
+               if (err)
+                       ecryptfs_printk(0, KERN_WARNING, "Not all ecryptfs_"
+                                       "header_cache_2 regions were freed\n");
+               rc |= err;
+       }
+       if (ecryptfs_allocated_caches & ECRYPTFS_LOWER_PAGE_CACHE) {
+               err = kmem_cache_destroy(ecryptfs_lower_page_cache);
+               if (err)
+                       ecryptfs_printk(0, KERN_WARNING, "Not all ecryptfs_"
+                                       "lower_page_cache regions were "
+                                       "freed\n");
+               rc |= err;
+       }
+       return rc;
+}
+
+static int __init init_ecryptfs_fs(void)
+{
+       int rc;
+
+       rc = ecryptfs_init_kmem_caches();

fs/ecryptfs/main.c line 713

Quoted 10 lines
fs/ecryptfs/main.c +10 -0
+       if (rc) {
+               ecryptfs_printk(0, KERN_EMERG, "Failure occured while "
+                               "attempting to create caches [CREATED: %x]."
+                               "Now freeing caches.\n",
+                               ecryptfs_allocated_caches);
+               ecryptfs_free_kmem_caches();
+               return -ENOMEM;
+       }
+       ecryptfs_printk(1, KERN_NOTICE, "Registering eCryptfs\n");
+       return register_filesystem(&ecryptfs_fs_type);

register_filesystem() can fail in which case youre leaking all the slab caches here.

Pekka

#6 lore raw

Re: [PATCH 4/12: eCryptfs] Main module functions

Quoted 6 lines · Pekka Enberg

fs/ecryptfs/main.c line 114

Quoted 3 lines · Phillip Hellewell
fs/ecryptfs/main.c +3 -0
+                       BUG();
+                       err = -EINVAL;
+                       goto out;

Why do you want to BUG() and then handle the situation?

Because you can define BUG() to nothing (on embedded builds for example) and then you would be screwed if you don't handle the error gracefully. You should never assume something does not return, except perhaps a panic() although someone might even get rid of that one day...

Best regards,

Anton

Signature · 4 lines
Anton Altaparmakov <aia21 at cam.ac.uk> (replace at with @)
Unix Support, Computing Service, University of Cambridge, CB2 3QH, UK
Linux NTFS maintainer / IRC: #ntfs on irc.freenode.net
WWW: http://linux-ntfs.sf.net/ & http://www-stu.christs.cam.ac.uk/~aia21/
#7 lore raw

Re: [PATCH 4/12: eCryptfs] Main module functions

Hi Anton,

Quoted 3 lines · Phillip Hellewell
Quoted 3 lines
Quoted 3 lines
fs/ecryptfs/main.c +3 -0
+                       BUG();
+                       err = -EINVAL;
+                       goto out;
Quoted 1 line · Pekka Enberg
Quoted 1 line

Why do you want to BUG() and then handle the situation?

Quoted 4 lines · Anton Altaparmakov

Because you can define BUG() to nothing (on embedded builds for example) and then you would be screwed if you don't handle the error gracefully. You should never assume something does not return, except perhaps a panic() although someone might even get rid of that one day...

You have a point but in this case, I don't understand why they don't just handle it gracefully since they clearly can do so. Also, I was under the impression that people who disable BUG() are knowingly taking the risk...

Pekka

#8 lore raw

Re: [PATCH 4/12: eCryptfs] Main module functions

In reply to Pekka Enberg
Quoted 29 lines · Pekka Enberg
Quoted 16 lines · Phillip Hellewell
fs/ecryptfs/main.c +16 -0
+int ecryptfs_verbosity = 1;
+
+module_param(ecryptfs_verbosity, int, 0);
+MODULE_PARM_DESC(ecryptfs_verbosity,
+                "Initial verbosity level (0 or 1; defaults to "
+                "0, which is Quiet)");
+
+void __ecryptfs_printk(int verb, const char *fmt, ...)
+{
+       va_list args;
+       va_start(args, fmt);
+       if ((ecryptfs_verbosity >= verb) && printk_ratelimit())
+               vprintk(fmt, args);
+       va_end(args);
+}
+

[snip]

Quoted 9 lines
fs/ecryptfs/main.c +9 -0
+int ecryptfs_interpose(struct dentry *lower_dentry, struct dentry *dentry,
+                      struct super_block *sb, int flag)
+{
+       struct inode *lower_inode;
+       int err = 0;
+       struct inode *inode;
+
+       ecryptfs_printk(1, KERN_NOTICE, "Enter; lower_dentry = [%p], "
+                       "lower_dentry->d_name.name = [%s], dentry = "
Quoted 8 lines

fs/ecryptfs/main.c line 85

Quoted 6 lines
fs/ecryptfs/main.c +6 -0
+                       "[%p], dentry->d_name.name = [%s], sb = [%p]; "
+                       "flag = [%.4x]; lower_dentry->d_count "
+                       "= [%d]; dentry->d_count = [%d]\n", lower_dentry,
+                       lower_dentry->d_name.name, dentry, dentry->d_name.name,
+                       sb, flag, atomic_read(&lower_dentry->d_count),
+                       atomic_read(&dentry->d_count));

Could you use KERN_DEBUG instead and drop ecryptfs_printk()?

I don't follow with what you mean. We use ecryptfs_printk for 2 reasons:
1) Debugging. There is (or atleast at one time was) a large amount of ecryptfs_printk's scattered around during the development process. Obviously, much has been removed because we don't need them much anymore. However, the normal printk functionality doesn't, afaik, provide function & line number by default. ecryptfs_printk inserts it automatically for easy of use.

2) Verbosity switch. The intent, eventually atleast, is to be able to toggle the value of the verbosity level of ecryptfs at run time (this isn't implemented at the moment)... or atleast that's my plan :P Its not my project so I have to get "approval" etc. The 0th argument is the verbosity in which this message applies (1 being just informative, 0 being critical).

Quoted 7 lines

fs/ecryptfs/main.c line 111

Quoted 6 lines
fs/ecryptfs/main.c +5 -0
+       if (NULL == ECRYPTFS_INODE_TO_LOWER(inode)) {
+               ECRYPTFS_INODE_TO_LOWER(inode) = igrab(lower_inode);
+               /* If we are still NULL at this point, igrab failed.
+                * We are _NOT_ supposed to be failing here */
+               if (NULL == ECRYPTFS_INODE_TO_LOWER(inode)) {
Quoted 5 lines

If you're worried about assignment, why not use the normal idiom:

   if (!p) {
      ...
   }

No good reason. Is there a reason to change this? (other than consistancy, which I agree with, so I'll change it)

Quoted 6 lines

fs/ecryptfs/main.c line 114

Quoted 4 lines
fs/ecryptfs/main.c +3 -0
+                       BUG();
+                       err = -EINVAL;
+                       goto out;

Why do you want to BUG() and then handle the situation?

Simply, we don't. Atleast, not in production code. This was in there to be a clear catch if we were ever to fall into this case. A remnant of development I am afraid.

Quoted 4 lines
Quoted 2 lines

+kmem_cache_t *ecryptfs_sb_info_cache;

We have struct kmem_cache now so please consider using it instead.

Noted. Changed.

Quoted 14 lines
Quoted 14 lines
fs/ecryptfs/main.c +13 -0
+/* This provides a means of backing out cache creations out of the kernel
+ * so that we can elegantly fail should we run out of memory.
+ */
+#define ECRYPTFS_AUTH_TOK_LIST_ITEM_CACHE      0x0001
+#define ECRYPTFS_AUTH_TOK_PKT_SET_CACHE         0x0002
+#define ECRYPTFS_AUTH_TOK_REQUEST_CACHE         0x0004
+#define ECRYPTFS_AUTH_TOK_REQUEST_BLOB_CACHE    0x0008
+#define ECRYPTFS_FILE_INFO_CACHE               0x0010
+#define ECRYPTFS_DENTRY_INFO_CACHE             0x0020
+#define ECRYPTFS_INODE_INFO_CACHE              0x0040
+#define ECRYPTFS_SB_INFO_CACHE                         0x0080
+#define ECRYPTFS_HEADER_CACHE_0                0x0100
+#define ECRYPTFS_HEADER_CACHE_1                0x0200
Quoted 8 lines

fs/ecryptfs/main.c line 507

Quoted 3 lines
fs/ecryptfs/main.c +3 -0
+#define ECRYPTFS_HEADER_CACHE_2                0x0400
+#define ECRYPTFS_LOWER_PAGE_CACHE              0x0800
+#define ECRYPTFS_CACHE_CREATION_SUCCESS                0x0FF1

A better way would be to either (1) make ecryptfs_init_kmem_caches() clean up after itself on error using gotos or (2) keep caches NULL before initialization and check for that in ecryptfs_free_kmem_caches().

Only problem with cleanp gotos is it would make the function needlessly larger than it already is. I do like the idea of the NULL thing better, will have a go at it.

Quoted 202 lines
Quoted 202 lines
fs/ecryptfs/main.c +201 -0
+
+static short ecryptfs_allocated_caches;
+
+/**
+ * @return Zero on success; non-zero otherwise
+ *
+ * Sets ecryptfs_allocated_caches with flags so that we can
+ * free created caches should we run out of memory during
+ * creation period.
+ *
+ * The overhead for doing this is offset by the fact that we
+ * only do this once, and that should there be insufficient
+ * memory, then we can elegantly fail, and not leave extra
+ * caches around, or worse, panic the kernel trying to free
+ * something that's not there.
+ */
+static int ecryptfs_init_kmem_caches(void)
+{
+       int rc = 0;
+
+       ecryptfs_auth_tok_list_item_cache =
+           kmem_cache_create("ecryptfs_auth_tok_list_item",
+                             sizeof(struct ecryptfs_auth_tok_list_item),
+                             0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+       if (ecryptfs_auth_tok_list_item_cache)
+               rc |= ECRYPTFS_AUTH_TOK_LIST_ITEM_CACHE;
+       else
+               ecryptfs_printk(0, KERN_WARNING, "ecryptfs_auth_tok_list_item "
+                               "kmem_cache_create failed\n");
+
+       ecryptfs_file_info_cache =
+           kmem_cache_create("ecryptfs_file_cache",
+                             sizeof(struct ecryptfs_file_info),
+                             0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+       if (ecryptfs_file_info_cache)
+               rc |= ECRYPTFS_FILE_INFO_CACHE;
+       else
+               ecryptfs_printk(0, KERN_WARNING, "ecryptfs_file_cache "
+                               "kmem_cache_create failed\n");
+
+       ecryptfs_dentry_info_cache =
+           kmem_cache_create("ecryptfs_dentry_cache",
+                             sizeof(struct ecryptfs_dentry_info),
+                             0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+       if (ecryptfs_dentry_info_cache)
+               rc |= ECRYPTFS_DENTRY_INFO_CACHE;
+       else
+               ecryptfs_printk(0, KERN_WARNING, "ecryptfs_dentry_cache "
+                               "kmem_cache_create failed\n");
+
+       ecryptfs_inode_info_cache =
+           kmem_cache_create("ecryptfs_inode_cache",
+                             sizeof(struct ecryptfs_inode_info), 0,
+                             SLAB_HWCACHE_ALIGN, inode_info_init_once, NULL);
+       if (ecryptfs_inode_info_cache)
+               rc |= ECRYPTFS_INODE_INFO_CACHE;
+       else
+               ecryptfs_printk(0, KERN_WARNING, "ecryptfs_inode_cache "
+                               "kmem_cache_create failed\n");
+
+       ecryptfs_sb_info_cache =
+           kmem_cache_create("ecryptfs_sb_cache",
+                             sizeof(struct ecryptfs_sb_info),
+                             0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+       if (ecryptfs_sb_info_cache)
+               rc |= ECRYPTFS_SB_INFO_CACHE;
+       else
+               ecryptfs_printk(0, KERN_WARNING, "ecryptfs_sb_cache "
+                               "kmem_cache_create failed\n");
+
+       ecryptfs_header_cache_0 =
+           kmem_cache_create("ecryptfs_headers_0", PAGE_CACHE_SIZE,
+                             0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+       if (ecryptfs_header_cache_0)
+               rc |= ECRYPTFS_HEADER_CACHE_0;
+       else
+               ecryptfs_printk(0, KERN_WARNING, "ecryptfs_headers_0 "
+                               "kmem_cache_create failed\n");
+
+       ecryptfs_header_cache_1 =
+           kmem_cache_create("ecryptfs_headers_1", PAGE_CACHE_SIZE,
+                             0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+       if (ecryptfs_header_cache_1)
+               rc |= ECRYPTFS_HEADER_CACHE_1;
+       else
+               ecryptfs_printk(0, KERN_WARNING, "ecryptfs_headers_1 "
+                               "kmem_cache_create failed\n");
+
+       ecryptfs_header_cache_2 =
+           kmem_cache_create("ecryptfs_headers_2", PAGE_CACHE_SIZE,
+                             0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+       if (ecryptfs_header_cache_2)
+               rc |= ECRYPTFS_HEADER_CACHE_2;
+       else
+               ecryptfs_printk(0, KERN_WARNING, "ecryptfs_headers_2 "
+                               "kmem_cache_create failed\n");
+
+       ecryptfs_lower_page_cache =
+           kmem_cache_create("ecryptfs_lower_page_cache", PAGE_CACHE_SIZE,
+                             0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+       if (ecryptfs_lower_page_cache)
+               rc |= ECRYPTFS_LOWER_PAGE_CACHE;
+       else
+               ecryptfs_printk(0, KERN_WARNING, "ecryptfs_lower_page_cache "
+                               "kmem_cache_create failed\n");
+
+       ecryptfs_allocated_caches = rc;
+       rc = ECRYPTFS_CACHE_CREATION_SUCCESS ^ rc;
+       return rc;
+}
+
+/**
+ * @return Zero on success; non-zero otherwise
+ */
+static int ecryptfs_free_kmem_caches(void)
+{
+       int rc = 0;
+       int err;
+
+       if (ecryptfs_allocated_caches & ECRYPTFS_AUTH_TOK_LIST_ITEM_CACHE) {
+               rc = kmem_cache_destroy(ecryptfs_auth_tok_list_item_cache);
+               if (rc)
+                       ecryptfs_printk(0, KERN_WARNING,
+                                       "Not all ecryptfs_auth_tok_"
+                                       "list_item_cache structures were "
+                                       "freed\n");
+       }
+       if (ecryptfs_allocated_caches & ECRYPTFS_FILE_INFO_CACHE) {
+               err = kmem_cache_destroy(ecryptfs_file_info_cache);
+               if (err)
+                       ecryptfs_printk(0, KERN_WARNING,
+                                       "Not all ecryptfs_file_info_"
+                                       "cache regions were freed\n");
+               rc |= err;
+       }
+       if (ecryptfs_allocated_caches & ECRYPTFS_DENTRY_INFO_CACHE) {
+               err = kmem_cache_destroy(ecryptfs_dentry_info_cache);
+               if (err)
+                       ecryptfs_printk(0, KERN_WARNING,
+                                       "Not all ecryptfs_dentry_info_"
+                                       "cache regions were freed\n");
+               rc |= err;
+       }
+       if (ecryptfs_allocated_caches & ECRYPTFS_INODE_INFO_CACHE) {
+               err = kmem_cache_destroy(ecryptfs_inode_info_cache);
+               if (err)
+                       ecryptfs_printk(0, KERN_WARNING,
+                                       "Not all ecryptfs_inode_info_"
+                                       "cache regions were freed\n");
+               rc |= err;
+       }
+       if (ecryptfs_allocated_caches & ECRYPTFS_SB_INFO_CACHE) {
+               err = kmem_cache_destroy(ecryptfs_sb_info_cache);
+               if (err)
+                       ecryptfs_printk(0, KERN_WARNING,
+                                       "Not all ecryptfs_sb_info_"
+                                       "cache regions were freed\n");
+               rc |= err;
+       }
+       if (ecryptfs_allocated_caches & ECRYPTFS_HEADER_CACHE_0) {
+               err = kmem_cache_destroy(ecryptfs_header_cache_0);
+               if (err)
+                       ecryptfs_printk(0, KERN_WARNING, "Not all ecryptfs_"
+                                       "header_cache_0 regions were freed\n");
+               rc |= err;
+       }
+       if (ecryptfs_allocated_caches & ECRYPTFS_HEADER_CACHE_1) {
+               err = kmem_cache_destroy(ecryptfs_header_cache_1);
+               if (err)
+                       ecryptfs_printk(0, KERN_WARNING, "Not all ecryptfs_"
+                                       "header_cache_1 regions were freed\n");
+               rc |= err;
+       }
+       if (ecryptfs_allocated_caches & ECRYPTFS_HEADER_CACHE_2) {
+               err = kmem_cache_destroy(ecryptfs_header_cache_2);
+               if (err)
+                       ecryptfs_printk(0, KERN_WARNING, "Not all ecryptfs_"
+                                       "header_cache_2 regions were freed\n");
+               rc |= err;
+       }
+       if (ecryptfs_allocated_caches & ECRYPTFS_LOWER_PAGE_CACHE) {
+               err = kmem_cache_destroy(ecryptfs_lower_page_cache);
+               if (err)
+                       ecryptfs_printk(0, KERN_WARNING, "Not all ecryptfs_"
+                                       "lower_page_cache regions were "
+                                       "freed\n");
+               rc |= err;
+       }
+       return rc;
+}
+
+static int __init init_ecryptfs_fs(void)
+{
+       int rc;
+
+       rc = ecryptfs_init_kmem_caches();
+       if (rc) {
+               ecryptfs_printk(0, KERN_EMERG, "Failure occured while "
+                               "attempting to create caches [CREATED: %x]."
+                               "Now freeing caches.\n",
+                               ecryptfs_allocated_caches);
Quoted 8 lines

fs/ecryptfs/main.c line 713

Quoted 5 lines
fs/ecryptfs/main.c +5 -0
+               ecryptfs_free_kmem_caches();
+               return -ENOMEM;
+       }
+       ecryptfs_printk(1, KERN_NOTICE, "Registering eCryptfs\n");
+       return register_filesystem(&ecryptfs_fs_type);

register_filesystem() can fail in which case youre leaking all the slab caches here.

I wasn't aware it _could_ fail, thanks for that.

Quoted 7 lines

Pekka
-
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

--
Michael C. Thompson <[email protected]>
Software-Engineer, IBM LTC Security

#9 lore raw

Re: [PATCH 4/12: eCryptfs] Main module functions

Quoted 8 lines · Michael Thompson
Quoted 5 lines · Pekka Enberg
Quoted 1 line · Phillip Hellewell

+ return register_filesystem(&ecryptfs_fs_type);

register_filesystem() can fail in which case youre leaking all the slab caches here.

I wasn't aware it _could_ fail, thanks for that.

Well, let me rephrase: I never bothered to check. Its obvious it can fail, I'm just embarresed that I never bothered to find out... again, thanks for catching that.

Quoted 10 lines
Quoted 8 lines

Pekka
-
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

Quoted 4 lines

--
Michael C. Thompson <[email protected]>
Software-Engineer, IBM LTC Security

--
Michael C. Thompson <[email protected]>
Software-Engineer, IBM LTC Security

#10 lore raw

Re: [PATCH 4/12: eCryptfs] Main module functions

In reply to Michael Thompson

Hi,

Quoted 10 lines · Pekka Enberg
Quoted 10 lines

fs/ecryptfs/main.c line 85

Quoted 8 lines
fs/ecryptfs/main.c +8 -0
+       ecryptfs_printk(1, KERN_NOTICE, "Enter; lower_dentry = [%p], "
+                       "lower_dentry->d_name.name = [%s], dentry = "
+                       "[%p], dentry->d_name.name = [%s], sb = [%p]; "
+                       "flag = [%.4x]; lower_dentry->d_count "
+                       "= [%d]; dentry->d_count = [%d]\n", lower_dentry,
+                       lower_dentry->d_name.name, dentry, dentry->d_name.name,
+                       sb, flag, atomic_read(&lower_dentry->d_count),
+                       atomic_read(&dentry->d_count));

Could you use KERN_DEBUG instead and drop ecryptfs_printk()?

Quoted 1 line · Michael Thompson

I don't follow with what you mean. We use ecryptfs_printk for 2 reasons:

Okay, let me spell it out for you: I think it is damn ugly :-)

Quoted 6 lines

1) Debugging. There is (or atleast at one time was) a large amount of ecryptfs_printk's scattered around during the development process. Obviously, much has been removed because we don't need them much anymore. However, the normal printk functionality doesn't, afaik, provide function & line number by default. ecryptfs_printk inserts it automatically for easy of use.

Like you said, not all debugging aids should be merged. I don't think function and line number is enough an argument to justify putting your own printk() in. That's my thinking anyway.

Quoted 6 lines

2) Verbosity switch. The intent, eventually atleast, is to be able to toggle the value of the verbosity level of ecryptfs at run time (this isn't implemented at the moment)... or atleast that's my plan :P Its not my project so I have to get "approval" etc. The 0th argument is the verbosity in which this message applies (1 being just informative, 0 being critical).

Do you really need it? Wouldn't it be better if you figured out which debug printk() statements make sense and leave those in with KERN_DEBUG?

Pekka

#11 lore raw

PATCH 5/12 Header declarations

In reply to Phillip Hellewell

This header contains declarations for various structs used in eCryptfs. Some of these are accessed both in userspace and in the kernel, so it is important to make sure that the struct declarations match in both places.

---

 ecryptfs_kernel.h |  430 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 430 insertions(+)
fs/ecryptfs/ecryptfs_kernel.h +430 -0
@@ -0,0 +1,430 @@
+/**
+ * eCryptfs: Linux filesystem encryption layer
+ * Kernel declarations.
+ *
+ * Copyright (c) 1997-2003 Erez Zadok
+ * Copyright (c) 2001-2003 Stony Brook University
+ * Copyright (c) 2005 International Business Machines Corp.
+ *   Author(s): Michael A. Halcrow <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef ECRYPTFS_KERNEL_H
+#define ECRYPTFS_KERNEL_H
+
+#include <linux/fs.h>
+#include <asm/semaphore.h>
+#include <asm/scatterlist.h>
+
+#define ECRYPTFS_MAX_PASSWORD_LENGTH 64
+#define ECRYPTFS_MAX_PASSPHRASE_BYTES ECRYPTFS_MAX_PASSWORD_LENGTH
+#define ECRYPTFS_SALT_SIZE 8
+#define ECRYPTFS_SALT_SIZE_HEX (ECRYPTFS_SALT_SIZE*2)
+/* The original signature size is only for what is stored on disk; all
+ * in-memory representations are expanded hex, so it better adapted to
+ * be passed around or referenced on the command line */
+#define ECRYPTFS_SIG_SIZE 8
+#define ECRYPTFS_SIG_SIZE_HEX (ECRYPTFS_SIG_SIZE*2)
+#define ECRYPTFS_PASSWORD_SIG_SIZE ECRYPTFS_SIG_SIZE_HEX
+#define ECRYPTFS_MAX_KEY_BYTES 16
+#define ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES 512
+#define ECRYPTFS_DEFAULT_IV_BYTES 8
+#define ECRYPTFS_FILE_VERSION 0x01
+
+/**
+ * For convenience, we may need to pass around the encrypted session
+ * key between kernel and userspace because the authentication token
+ * may not be extractable.  For example, the TPM may not release the
+ * private key, instead requiring the encrypted data and returning the
+ * decrypted data.
+ */
+struct ecryptfs_session_key {
+#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT 0x01
+#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT 0x02
+#define ECRYPTFS_CONTAINS_DECRYPTED_KEY 0x04
+#define ECRYPTFS_CONTAINS_ENCRYPTED_KEY 0x08
+	int32_t flags;
+	int32_t encrypted_key_size;
+	int32_t decrypted_key_size;
+	uint8_t decrypted_key[ECRYPTFS_MAX_KEY_BYTES];
+	uint8_t encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES];
+};
+
+/**
+ * States for this struct are:
+ *  - Uninstantiated: no password, but the salt, encrypted session
+ *    key, etc. are filled in
+ *  - Uninstantiated: no password, salt, etc. are filled in; at this
+ *    time, there is no reason for the auth_tok to exist and be in
+ *    this state
+ *  - Instantiated: password, but no salt, and hence no encrypted
+ *    session key, etc.  eCryptfs will generate those items when the
+ *    time come to write out the headers to disk
+ *  - Instantiated: password, salt, encrypted session key, etc.  This
+ *    is typically used to actually obtain the session key for the
+ *    file
+ */
+struct ecryptfs_password {
+	int32_t saltless;       /* If set, this is the ``seed'' token
+				 * from which other salted tokens are
+				 * derived. Note that this is _not_
+				 * the same as a token that just has
+				 * not received its salt yet. */
+	int32_t password_size;
+	int32_t hash_algo;
+	int32_t hash_iterations;
+	int32_t session_key_encryption_key_size;	/* In bytes */
+	int32_t session_key_encryption_key_set;
+	uint8_t salt[ECRYPTFS_SALT_SIZE];
+	/* Always in expanded hex */
+	uint8_t signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
+	uint8_t password[ECRYPTFS_MAX_PASSWORD_LENGTH];
+	/* Iterated-hash concatenation of salt and passphrase */
+	uint8_t session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES];
+};
+
+/* May be a password or a private key */
+struct ecryptfs_auth_tok {
+	int32_t instantiated;	/* When not instantiated, this struct only
+				 * contains enough information to construct
+				 * the file headers, which contain token
+				 * descriptors */
+#define ECRYPTFS_PASSWORD 0
+#define ECRYPTFS_PRIVATE_KEY 1
+	int32_t instanceof;
+	int32_t expired;
+	uid_t uid;
+	int64_t creation_time;
+	int64_t expiration_time;
+	/* This is in case we want userspace to extract the session
+	 * key */
+	struct ecryptfs_session_key session_key;
+	union {
+		struct ecryptfs_password password;
+		/* Private key is in future eCryptfs releases */
+	} token;
+};
+
+void dump_auth_tok(struct ecryptfs_auth_tok *auth_tok);
+extern void ecryptfs_to_hex(char *dst, char *src, int src_size);
+extern void ecryptfs_from_hex(char *dst, char *src, int dst_size);
+
+/* For ecryptfs_auth_tok_packet_set.packet_set_type */
+#define ECRYPTFS_PACKET_SET_TYPE_PASSWORD 0
+
+#ifndef ECRYPTFS_DEBUG
+#define ECRYPTFS_DEBUG 1
+#endif
+
+/* See RFC 2440 */
+struct ecryptfs_key_record {
+	unsigned char type;
+	unsigned char sig[ECRYPTFS_SIG_SIZE];
+	u16 enc_key_size_bits;
+	unsigned char enc_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES];
+};
+#define KEY_REC_SIZE(key_rec) \
+        ( sizeof(struct ecryptfs_key_record) - ECRYPTFS_MAX_KEY_BYTES \
+          + key_rec.enc_key_size_bits/8 )
+
+/* TODO: kref */
+struct ecryptfs_auth_tok_list {
+	struct ecryptfs_auth_tok *auth_tok;
+	struct list_head list;
+};
+
+/* Structure prototypes */
+struct ecryptfs_crypt_stats;
+struct ecryptfs_mount_crypt_stats;
+
+#define KEY_PAYLOAD_DATA(key) \
+	( ((struct user_key_payload*)key->payload.data)->data)
+#define KEY_PAYLOAD_LEN(key) \
+	( ((struct user_key_payload*)key->payload.data)->datalen)
+
+#define ECRYPTFS_SUPER_MAGIC 0xf15f
+#define ECRYPTFS_MAX_KEYSET_SIZE 1024
+#define ECRYPTFS_MAX_CIPHER_NAME_SIZE 32
+#define ECRYPTFS_MAX_NUM_ENC_KEYS 64
+#define ECRYPTFS_MAX_NUM_KEYSIGS 64	/* TODO: make it a list */
+#define ECRYPTFS_MAX_IV_BYTES 8	/* 64 bits */
+#define ECRYPTFS_SALT_BYTES 2
+#define MAGIC_ECRYPTFS_MARKER 0x3c81b7f5
+#define MAGIC_ECRYPTFS_MARKER_SIZE_BYTES 8	/* 4*2 */
+#define ECRYPTFS_FILE_SIZE_BYTES 8
+#define ECRYPTFS_DEFAULT_CIPHER "blowfish"
+#define ECRYPTFS_DEFAULT_KEY_BYTES 16
+
+/**
+ * IV_SIZE (i.e., 8 bytes)
+ * HMAC_SIZE (i.e., 20 bytes)
+ * IVS_PER_PAGE (i.e., 512)
+ * IVS_PER_PAGE * HMAC_SIZE = TOTAL_SIZE_PER_EXTENT; 512 * 20 = 10240
+ * TOTAL_SIZE_PER_EXTENT / PAGE_SIZE = NUM_PAGES_FOR_HMACS = 2.5
+ * PIIHHHHHDx1024IIHHHHHDx1024...
+ * PIDx512IDx512...
+ * Or: [Ix146+Hx146] (wastes 8 bytes per page)
+ */
+
+/**
+ * This is the primary struct associated with each encrypted file.
+ *
+ * TODO: cache align/pack?
+ */
+struct ecryptfs_crypt_stats {
+	int struct_initialized;
+	int policy_applied;
+	int new_file;
+	int encrypted;
+	int security_warning;	/* This flag is set if something happens
+				 * that could weaken the security of the
+				 * file */
+	int iv_bytes;		/* Set to 0 if encryption not enabled */
+	int records_per_page;	/* extent_size / (iv_bytes + hmac_bytes) */
+#define ECRYPTFS_IV_ROTATE_NO 0
+#define ECRYPTFS_IV_ROTATE_INCREMENT 1
+#define ECRYPTFS_IV_ROTATE_PERMUTATE 2
+#define ECRYPTFS_IV_ROTATE_RANDOM 3
+	int rotate_iv;		/* Whether or not to rotate the IV on each
+				 * write (performance vs. security tradeoff) */
+	int encrypt_iv_pages;	/* To attempt to hide sparse regions? */
+	int num_keysigs;
+	int num_header_pages;	/* Number of pages that comprise the
+				 * header */
+	int extent_size;
+	int key_size_bits;
+	int key_valid;
+	struct crypto_tfm *tfm;
+	unsigned char iv[ECRYPTFS_MAX_IV_BYTES];
+	unsigned char key[ECRYPTFS_MAX_KEY_BYTES];
+	char cipher[ECRYPTFS_MAX_CIPHER_NAME_SIZE];
+	struct semaphore iv_sem;
+	char keysigs[ECRYPTFS_MAX_NUM_KEYSIGS][ECRYPTFS_SIG_SIZE_HEX];
+};
+
+/* inode private data. */
+struct ecryptfs_inode_info {
+	struct inode *wii_inode;
+	struct ecryptfs_crypt_stats crypt_stats;
+	struct inode vfs_inode;
+};
+
+/* dentry private data. */
+struct ecryptfs_dentry_info {
+	struct dentry *wdi_dentry;
+	struct ecryptfs_crypt_stats *crypt_stats;
+};
+
+/**
+ * This struct is to enable a mount-wide passphrase/salt combo. This
+ * is more or less a stopgap to provide similar functionality to other
+ * crypto filesystems like EncFS or CFS until full policy support is
+ * implemented in eCryptfs.
+ */
+struct ecryptfs_mount_crypt_stats {
+	/* N.B. Pointers to memory we do not own, do not free these */
+	struct ecryptfs_auth_tok *global_auth_tok;
+	struct key *global_auth_tok_key;
+	char global_auth_tok_sig[ECRYPTFS_SIG_SIZE_HEX + 1];
+	char global_default_cipher_name[ECRYPTFS_MAX_CIPHER_NAME_SIZE + 1];
+};
+
+/* super block private data. */
+struct ecryptfs_sb_info {
+	struct super_block *wsi_sb;
+	struct vfsmount *lower_mnt;
+	struct ecryptfs_mount_crypt_stats mount_crypt_stats;
+};
+
+/* file private data. */
+struct ecryptfs_file_info {
+	struct file *wfi_file;
+	struct ecryptfs_crypt_stats *crypt_stats;
+};
+
+/* auth_tok <=> encrypted_session_key mappings */
+struct ecryptfs_auth_tok_list_item {
+	struct list_head list;
+	struct ecryptfs_auth_tok auth_tok;
+	char encrypted_session_key[ECRYPTFS_MAX_KEY_BYTES];
+};
+
+extern inline pgoff_t 
+ecryptfs_pg_idx_to_lwr_pg_idx(struct ecryptfs_crypt_stats *crypt_stats,
+			      pgoff_t idx)
+{
+	return ((idx / crypt_stats->records_per_page) + idx
+		+ crypt_stats->num_header_pages + 1);
+}
+
+#ifndef DEFAULT_POLLMASK
+#define DEFAULT_POLLMASK (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)
+#endif				/* ndef DEFAULT_POLLMASK */
+
+#define OBSERVE_ASSERTS 1
+#ifdef OBSERVE_ASSERTS
+#define ASSERT(EX)	                                                      \
+do {	                                                                      \
+        if (unlikely(!(EX))) {                                                \
+	        printk(KERN_CRIT "ASSERTION FAILED: %s at %s:%d (%s)\n", #EX, \
+	               __FILE__, __LINE__, __FUNCTION__);	              \
+                BUG();                                                        \
+        }	                                                              \
+} while (0)
+#else
+#define ASSERT(EX) ;
+#endif				/* OBSERVE_ASSERTS */
+
+/**
+ * Halcrow: What does the kernel VFS do to ensure that there is no
+ * contention for file->private_data?
+ */
+#define ECRYPTFS_FILE_TO_PRIVATE(file) ((struct ecryptfs_file_info *) \
+                                        ((file)->private_data))
+#define ECRYPTFS_FILE_TO_PRIVATE_SM(file) ((file)->private_data)
+#define ECRYPTFS_FILE_TO_LOWER(file) \
+        ((ECRYPTFS_FILE_TO_PRIVATE(file))->wfi_file)
+#define ECRYPTFS_INODE_TO_PRIVATE(ino) ((struct ecryptfs_inode_info *) \
+                                        (ino)->u.generic_ip)
+#define ECRYPTFS_INODE_TO_PRIVATE_SM(ino) ((ino)->u.generic_ip)
+#define ECRYPTFS_INODE_TO_LOWER(ino) (ECRYPTFS_INODE_TO_PRIVATE(ino)->wii_inode)
+#define ECRYPTFS_SUPERBLOCK_TO_PRIVATE(super) ((struct ecryptfs_sb_info *) \
+                                               (super)->s_fs_info)
+#define ECRYPTFS_SUPERBLOCK_TO_PRIVATE_SM(super) ((super)->s_fs_info)
+#define ECRYPTFS_SUPERBLOCK_TO_LOWER(super) \
+        (ECRYPTFS_SUPERBLOCK_TO_PRIVATE(super)->wsi_sb)
+#define ECRYPTFS_DENTRY_TO_PRIVATE_SM(dentry) ((dentry)->d_fsdata)
+#define ECRYPTFS_DENTRY_TO_PRIVATE(dentry) ((struct ecryptfs_dentry_info *) \
+                                            (dentry)->d_fsdata)
+#define ECRYPTFS_DENTRY_TO_LOWER(dentry) \
+        (ECRYPTFS_DENTRY_TO_PRIVATE(dentry)->wdi_dentry)
+
+/*
+ * Flags for ecryptfs_{en,de}code_filename
+ * DO_DOTS means the special entries . and .. should be encoded (for symlink)
+ * SKIP_DOTS means they should be preserved intact
+ */
+#define ECRYPTFS_DO_DOTS   0
+#define ECRYPTFS_SKIP_DOTS 1
+
+/**
+ * EXTERNALS:
+ */
+extern struct file_operations ecryptfs_main_fops;
+extern struct file_operations ecryptfs_dir_fops;
+extern struct inode_operations ecryptfs_main_iops;
+extern struct inode_operations ecryptfs_dir_iops;
+extern struct inode_operations ecryptfs_symlink_iops;
+extern struct super_operations ecryptfs_sops;
+extern struct dentry_operations ecryptfs_dops;
+extern struct address_space_operations ecryptfs_aops;
+struct ecryptfs_key_record;
+struct ecryptfs_auth_tok;
+
+/**
+ * TODO: Make eCryptfs's memory usage as lean as possible
+ */
+extern kmem_cache_t *ecryptfs_auth_tok_list_item_cache;
+extern kmem_cache_t *ecryptfs_file_info_cache;
+extern kmem_cache_t *ecryptfs_dentry_info_cache;
+extern kmem_cache_t *ecryptfs_inode_info_cache;
+extern kmem_cache_t *ecryptfs_sb_info_cache;
+extern kmem_cache_t *ecryptfs_header_cache_0;
+extern kmem_cache_t *ecryptfs_header_cache_1;
+extern kmem_cache_t *ecryptfs_header_cache_2;
+extern kmem_cache_t *ecryptfs_lower_page_cache;
+
+int ecryptfs_interpose(struct dentry *hidden_dentry,
+		       struct dentry *this_dentry, struct super_block *sb,
+		       int flag);
+int ecryptfs_fill_zeros(struct file *file, loff_t new_length);
+int ecryptfs_decode_filename(const char *name, int length,
+			     char **decrypted_name, int skip_dots,
+			     struct ecryptfs_crypt_stats *crypt_stats);
+int ecryptfs_encode_filename(const char *name, int length,
+			     char **encoded_name, int skip_dots,
+			     struct ecryptfs_crypt_stats *crypt_stats);
+struct dentry *ecryptfs_lower_dentry(struct dentry *this_dentry);
+
+void ecryptfs_copy_attr_times(struct inode *dest, const struct inode *src);
+void ecryptfs_copy_attr_atime(struct inode *dest, const struct inode *src);
+void ecryptfs_copy_attr_all(struct inode *dest, const struct inode *src);
+void ecryptfs_copy_inode_size(struct inode *dst, const struct inode *src);
+
+#define ecryptfs_printk(verb, type, fmt, arg...) \
+        __ecryptfs_printk((verb), type "%s: " fmt, __FUNCTION__, ## arg);
+void __ecryptfs_printk(int verb, const char *fmt, ...);
+
+extern int ecryptfs_verbosity;
+
+/* crypto */
+void ecryptfs_dump_hex(char *data, int bytes);
+int virt_to_scatterlist(const void *addr, int size, struct scatterlist *sg,
+			int sg_size);
+void ecryptfs_rotate_iv(unsigned char *iv);
+void ecryptfs_init_crypt_stats(struct ecryptfs_crypt_stats *crypt_stats);
+void ecryptfs_destruct_crypt_stats(struct ecryptfs_crypt_stats *crypt_stats);
+int ecryptfs_init_crypt_ctx(struct ecryptfs_crypt_stats *crypt_stats);
+int ecryptfs_write_inode_size_to_header(struct file *lower_file,
+					struct inode *lower_inode,
+					struct inode *inode);
+int do_encrypt_page(struct ecryptfs_crypt_stats *crypt_stats,
+		    struct page *dest_page, struct page *src_page,
+		    unsigned char *iv);
+int do_encrypt_page_offset(struct ecryptfs_crypt_stats *crypt_stats,
+			   struct page *dest_page, int dest_offset,
+			   struct page *src_page, int src_offset, int size,
+			   unsigned char *iv);
+int do_decrypt_page(struct ecryptfs_crypt_stats *crypt_stats,
+		    struct page *dest_page, struct page *src_page,
+		    unsigned char *iv);
+int do_decrypt_page_offset(struct ecryptfs_crypt_stats *crypt_stats,
+			   struct page *dest_page, int dest_offset,
+			   struct page *src_page, int src_offset, int size,
+			   unsigned char *iv);
+int do_encrypt_virt(struct ecryptfs_crypt_stats *crypt_stats,
+		    char *dest_virt_addr, const char *src_virt_addr,
+		    int size, unsigned char *iv);
+int do_decrypt_virt(struct ecryptfs_crypt_stats *crypt_stats,
+		    char *dest_virt_addr, const char *src_virt_addr,
+		    int size, unsigned char *iv);
+int ecryptfs_write_headers(struct dentry *ecryptfs_dentry,
+			   struct file *lower_file);
+int ecryptfs_write_headers_virt(char *page_virt, 
+				struct ecryptfs_crypt_stats *crypt_stats,
+				struct dentry *ecryptfs_dentry, int version);
+int ecryptfs_read_headers(struct dentry *ecryptfs_dentry,
+			  struct file *lower_file);
+int ecryptfs_new_file_context(struct dentry *ecryptfs_dentry);
+int contains_ecryptfs_marker(char *data);
+int ecryptfs_read_header_region(char *data, struct dentry *dentry,
+				struct nameidata *nd);
+u16 ecryptfs_code_for_cipher_string(char *str);
+int ecryptfs_cipher_code_to_string(char *str, u16 cipher_code);
+void ecryptfs_set_default_sizes(struct ecryptfs_crypt_stats *crypt_stats);
+int ecryptfs_generate_key_packet_set(char *dest_base,
+				     struct ecryptfs_crypt_stats *crypt_stats,
+				     struct dentry *ecryptfs_dentry, int *len);
+int process_request_key_err(long err_code);
+int ecryptfs_parse_packet_set(unsigned char *dest,
+			      struct ecryptfs_crypt_stats *crypt_stats,
+			      struct dentry *ecryptfs_dentry, int version);
+
+/* inode.c */
+int ecryptfs_truncate(struct dentry *dentry, loff_t new_length);
+
+#endif				/* ndef ECRYPTFS_KERNEL_H */
#12 lore raw

Re: [PATCH 5/12: eCryptfs] Header declarations

Hi Phillip,

fs/ecryptfs/ecryptfs_kernel.h line 64

Quoted 10 lines · Phillip Hellewell
fs/ecryptfs/ecryptfs_kernel.h +10 -0
+struct ecryptfs_session_key {
+#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT 0x01
+#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT 0x02
+#define ECRYPTFS_CONTAINS_DECRYPTED_KEY 0x04
+#define ECRYPTFS_CONTAINS_ENCRYPTED_KEY 0x08
+       int32_t flags;
+       int32_t encrypted_key_size;
+       int32_t decrypted_key_size;
+       uint8_t decrypted_key[ECRYPTFS_MAX_KEY_BYTES];
+       uint8_t encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES];

s32 and u8 are preferred in the kernel.

fs/ecryptfs/ecryptfs_kernel.h line 290

Quoted 13 lines
fs/ecryptfs/ecryptfs_kernel.h +13 -0
+#define OBSERVE_ASSERTS 1
+#ifdef OBSERVE_ASSERTS
+#define ASSERT(EX)                                                           \
+do {                                                                         \
+        if (unlikely(!(EX))) {                                                \
+               printk(KERN_CRIT "ASSERTION FAILED: %s at %s:%d (%s)\n", #EX, \
+                      __FILE__, __LINE__, __FUNCTION__);                     \
+                BUG();                                                        \
+        }                                                                    \
+} while (0)
+#else
+#define ASSERT(EX) ;
+#endif                         /* OBSERVE_ASSERTS */

Any reason why you can't just use BUG and BUG_ON()?

fs/ecryptfs/ecryptfs_kernel.h line 295

Quoted 5 lines
fs/ecryptfs/ecryptfs_kernel.h +5 -0
+
+/**
+ * Halcrow: What does the kernel VFS do to ensure that there is no
+ * contention for file->private_data?
+ */

Please elaborate?

Quoted 9 lines
fs/ecryptfs/ecryptfs_kernel.h +9 -0
+#define ECRYPTFS_FILE_TO_PRIVATE(file) ((struct ecryptfs_file_info *) \
+                                        ((file)->private_data))
+#define ECRYPTFS_FILE_TO_PRIVATE_SM(file) ((file)->private_data)
+#define ECRYPTFS_FILE_TO_LOWER(file) \
+        ((ECRYPTFS_FILE_TO_PRIVATE(file))->wfi_file)
+#define ECRYPTFS_INODE_TO_PRIVATE(ino) ((struct ecryptfs_inode_info *) \
+                                        (ino)->u.generic_ip)
+#define ECRYPTFS_INODE_TO_PRIVATE_SM(ino) ((ino)->u.generic_ip)
+#define ECRYPTFS_INODE_TO_LOWER(ino) (ECRYPTFS_INODE_TO_PRIVATE(ino)->wii_inode)

fs/ecryptfs/ecryptfs_kernel.h line 314

Quoted 10 lines
fs/ecryptfs/ecryptfs_kernel.h +10 -0
+#define ECRYPTFS_SUPERBLOCK_TO_PRIVATE(super) ((struct ecryptfs_sb_info *) \
+                                               (super)->s_fs_info)
+#define ECRYPTFS_SUPERBLOCK_TO_PRIVATE_SM(super) ((super)->s_fs_info)
+#define ECRYPTFS_SUPERBLOCK_TO_LOWER(super) \
+        (ECRYPTFS_SUPERBLOCK_TO_PRIVATE(super)->wsi_sb)
+#define ECRYPTFS_DENTRY_TO_PRIVATE_SM(dentry) ((dentry)->d_fsdata)
+#define ECRYPTFS_DENTRY_TO_PRIVATE(dentry) ((struct ecryptfs_dentry_info *) \
+                                            (dentry)->d_fsdata)
+#define ECRYPTFS_DENTRY_TO_LOWER(dentry) \
+        (ECRYPTFS_DENTRY_TO_PRIVATE(dentry)->wdi_dentry)

These wrappers seem rather pointless and obfuscating...

fs/ecryptfs/ecryptfs_kernel.h line 377

Quoted 2 lines
fs/ecryptfs/ecryptfs_kernel.h +2 -0
+int virt_to_scatterlist(const void *addr, int size, struct scatterlist *sg,
+                       int sg_size);

Doesn't seem ecryptfs specific, why is it here?

Pekka

#13 lore raw

Re: [PATCH 5/12: eCryptfs] Header declarations

Quoted 15 lines · Pekka Enberg

Hi Phillip,

fs/ecryptfs/ecryptfs_kernel.h line 64

Quoted 10 lines · Phillip Hellewell
fs/ecryptfs/ecryptfs_kernel.h +10 -0
+struct ecryptfs_session_key {
+#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT 0x01
+#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT 0x02
+#define ECRYPTFS_CONTAINS_DECRYPTED_KEY 0x04
+#define ECRYPTFS_CONTAINS_ENCRYPTED_KEY 0x08
+       int32_t flags;
+       int32_t encrypted_key_size;
+       int32_t decrypted_key_size;
+       uint8_t decrypted_key[ECRYPTFS_MAX_KEY_BYTES];
+       uint8_t encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES];

s32 and u8 are preferred in the kernel.

Thanks for that, lots of little things like this were undoubtably missed, we use u/s# in other areas. Changed and will be resent after all comments are in.

Quoted 16 lines
Quoted 4 lines
fs/ecryptfs/ecryptfs_kernel.h +3 -0
+#define OBSERVE_ASSERTS 1
+#ifdef OBSERVE_ASSERTS
+#define ASSERT(EX)                                                           \

fs/ecryptfs/ecryptfs_kernel.h line 290

Quoted 10 lines
fs/ecryptfs/ecryptfs_kernel.h +10 -0
+do {                                                                         \
+        if (unlikely(!(EX))) {                                                \
+               printk(KERN_CRIT "ASSERTION FAILED: %s at %s:%d (%s)\n", #EX, \
+                      __FILE__, __LINE__, __FUNCTION__);                     \
+                BUG();                                                        \
+        }                                                                    \
+} while (0)
+#else
+#define ASSERT(EX) ;
+#endif                         /* OBSERVE_ASSERTS */

Any reason why you can't just use BUG and BUG_ON()?

No reason? I think we've had this comment before, not sure if we have had a decent reason though... let me see if I can find out why we do it this way.

Quoted 7 lines

fs/ecryptfs/ecryptfs_kernel.h line 295

Quoted 5 lines
fs/ecryptfs/ecryptfs_kernel.h +5 -0
+
+/**
+ * Halcrow: What does the kernel VFS do to ensure that there is no
+ * contention for file->private_data?
+ */

Please elaborate?

I believe this is an old and lingering comment. While I don't know who originated, it sounds like a question to Michael Halcrow regarding locking of a struct file's private data... It will be removed for next submission.

Quoted 14 lines
Quoted 14 lines
fs/ecryptfs/ecryptfs_kernel.h +13 -0
+#define ECRYPTFS_FILE_TO_PRIVATE(file) ((struct ecryptfs_file_info *) \
+                                        ((file)->private_data))
+#define ECRYPTFS_FILE_TO_PRIVATE_SM(file) ((file)->private_data)
+#define ECRYPTFS_FILE_TO_LOWER(file) \
+        ((ECRYPTFS_FILE_TO_PRIVATE(file))->wfi_file)
+#define ECRYPTFS_INODE_TO_PRIVATE(ino) ((struct ecryptfs_inode_info *) \
+                                        (ino)->u.generic_ip)
+#define ECRYPTFS_INODE_TO_PRIVATE_SM(ino) ((ino)->u.generic_ip)
+#define ECRYPTFS_INODE_TO_LOWER(ino) (ECRYPTFS_INODE_TO_PRIVATE(ino)->wii_inode)
+#define ECRYPTFS_SUPERBLOCK_TO_PRIVATE(super) ((struct ecryptfs_sb_info *) \
+                                               (super)->s_fs_info)
+#define ECRYPTFS_SUPERBLOCK_TO_PRIVATE_SM(super) ((super)->s_fs_info)
+#define ECRYPTFS_SUPERBLOCK_TO_LOWER(super) \
Quoted 8 lines

fs/ecryptfs/ecryptfs_kernel.h line 314

Quoted 6 lines
fs/ecryptfs/ecryptfs_kernel.h +6 -0
+        (ECRYPTFS_SUPERBLOCK_TO_PRIVATE(super)->wsi_sb)
+#define ECRYPTFS_DENTRY_TO_PRIVATE_SM(dentry) ((dentry)->d_fsdata)
+#define ECRYPTFS_DENTRY_TO_PRIVATE(dentry) ((struct ecryptfs_dentry_info *) \
+                                            (dentry)->d_fsdata)
+#define ECRYPTFS_DENTRY_TO_LOWER(dentry) \
+        (ECRYPTFS_DENTRY_TO_PRIVATE(dentry)->wdi_dentry)

These wrappers seem rather pointless and obfuscating...

I find them make the code a bit more clear. Then again, I understand what they do and use them a lot, so I am clearly jaded. You are the first person to comment on these wrappers (all previous comments where regarding some debug wrappers we had in when we first submitted, I like those ones too, but I guess my opinion doesn't carry enough weight here ;P) There is no functional reason why these can't be removed, but like I said, I think they make the code a lot easier to read once you know all they do is sugar-coat a potentially long, and potentially confusing, chain of refences, and turn that into some phrase-like macro.

Quoted 5 lines

fs/ecryptfs/ecryptfs_kernel.h line 377

Quoted 3 lines
fs/ecryptfs/ecryptfs_kernel.h +2 -0
+int virt_to_scatterlist(const void *addr, int size, struct scatterlist *sg,
+                       int sg_size);

Doesn't seem ecryptfs specific, why is it here?

Ah ha, that should read ecryptfs_virt_to_scatterlist. Again, will be changed for next submission. While reviewing the code, there seem to be a few more like this. I'll make sure they get updated.

Quoted 7 lines

Pekka
-
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

--
Michael C. Thompson <[email protected]>
Software-Engineer, IBM LTC Security

#14 lore raw

PATCH 6/12 Superblock operations

In reply to Phillip Hellewell

eCryptfs superblock operations and inode
allocation/deallocation/initialization functions.

---

 super.c |  250 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 250 insertions(+)
fs/ecryptfs/super.c +250 -0
@@ -0,0 +1,250 @@
+/**
+ * eCryptfs: Linux filesystem encryption layer
+ *
+ * Copyright (c) 1997-2003 Erez Zadok
+ * Copyright (c) 2001-2003 Stony Brook University
+ * Copyright (c) 2005 International Business Machines Corp.
+ *   Author(s): Michael A. Halcrow <[email protected]>
+ *              Michael C. Thompson <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/key.h>
+#include "ecryptfs_kernel.h"
+
+kmem_cache_t *ecryptfs_inode_info_cache;
+
+/**
+ * Called to bring an inode into existence.
+ *
+ * Note that setting the self referencing pointer doesn't work here:
+ * 	i.e. ECRYPTFS_INODE_TO_PRIVATE_SM(inode)=ei;
+ *
+ * Only handle allocation, setting up structures should be done in
+ * ecryptfs_read_inode. This is because the kernel, between now and
+ * then, will 0 out the private data pointer.
+ *
+ * @param sb	Pointer to the super block of the filesystem
+ * @return	Pointer to a newly allocated inode, NULL otherwise
+ */
+static struct inode *ecryptfs_alloc_inode(struct super_block *sb) {
+	struct ecryptfs_inode_info *ecryptfs_inode = NULL;
+	struct inode *inode = NULL;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; sb = [%p]\n", sb);
+	ecryptfs_inode = kmem_cache_alloc(ecryptfs_inode_info_cache,
+					  SLAB_KERNEL);
+	if (unlikely(!ecryptfs_inode)) {
+		ecryptfs_printk(1, KERN_WARNING,
+				"Failed to allocate new inode\n");
+		goto out;
+	}
+	ecryptfs_init_crypt_stats(&(ecryptfs_inode->crypt_stats));
+	inode = &(ecryptfs_inode->vfs_inode);
+ out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; inode = [%p]\n", inode);
+	return inode;
+}
+
+/**
+ * This is used during the final destruction of the inode.
+ * All allocation of memory related to the inode, including allocated
+ * memory in the crypt_stats struct, will be released here.
+ * There should be no chance that this deallocation will be missed.
+ */
+static void ecryptfs_destroy_inode(struct inode *inode) {
+	struct ecryptfs_crypt_stats *crypt_stats;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; inode = [%p]\n", inode);
+	crypt_stats = &(ECRYPTFS_INODE_TO_PRIVATE(inode))->crypt_stats;
+	ecryptfs_destruct_crypt_stats(crypt_stats);
+	kmem_cache_free(ecryptfs_inode_info_cache,
+			ECRYPTFS_INODE_TO_PRIVATE(inode));
+	ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+}
+
+/**
+ * Return a pointer to our ecryptfs_inode_info struct.
+ * This is specified by the kernel documentation for the alloc_inode &
+ * destroy_inode change. We use this function to get a handle to our
+ * ecryptfs specific data.
+ *
+ * @param inode	The inode component of the ecryptfs_inode_info we want to find
+ * @return	Handle to our ecryptfs_inode_info
+ */
+static inline struct ecryptfs_inode_info *ECRYPTFS_I(struct inode *inode)
+{
+	return list_entry(inode, struct ecryptfs_inode_info, vfs_inode);
+}
+
+/**
+ * Set up the ecryptfs inode.
+ */
+static void ecryptfs_read_inode(struct inode *inode)
+{
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; inode = [%p]\n", inode);
+	/* This is where we setup the self-reference in the vfs_inode's
+	 * u.generic_ip. That way we don't have to walk the list again. */
+	ECRYPTFS_INODE_TO_PRIVATE_SM(inode) = ECRYPTFS_I(inode);
+	ECRYPTFS_INODE_TO_LOWER(inode) = NULL;
+	inode->i_version++;
+	inode->i_op = &ecryptfs_main_iops;
+	inode->i_fop = &ecryptfs_main_fops;
+	inode->i_mapping->a_ops = &ecryptfs_aops;
+	ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+}
+
+
+/**
+ * This is called through iput_final().
+ * This is function will replace generic_drop_inode. The end result of which
+ * is we are skipping the check in inode->i_nlink, which we do not use.
+ */
+static void ecryptfs_drop_inode(struct inode *inode) {
+	generic_delete_inode(inode);
+}
+
+/**
+ * Final actions when unmounting a file system.
+ * This will handle deallocation and release of our private data.
+ */
+static void ecryptfs_put_super(struct super_block *sb)
+{
+	struct ecryptfs_sb_info *sb_info = ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb);
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	mntput(sb_info->lower_mnt);
+	key_put(sb_info->mount_crypt_stats.global_auth_tok_key);
+	kmem_cache_free(ecryptfs_sb_info_cache, sb_info);
+	ECRYPTFS_SUPERBLOCK_TO_PRIVATE_SM(sb) = NULL;
+	ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+}
+
+/**
+ * Get the filesystem statistics. Currently, we let this pass right through
+ * to the lower filesystem and take no action ourselves
+ * 
+ * TODO: Any stats need to be transposed?
+ */
+static int ecryptfs_statfs(struct super_block *sb, struct kstatfs *buf)
+{
+	int err = 0;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	err = vfs_statfs(ECRYPTFS_SUPERBLOCK_TO_LOWER(sb), buf);
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n",err);
+	return err;
+}
+
+/**
+ * TODO: Not implemented.
+ * Called to ask filesystem to change mount options.
+ */
+static int ecryptfs_remount_fs(struct super_block *sb, int *flags, char *data)
+{
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+	return -ENOSYS;
+}
+
+/**
+ * Called by iput() when the inode reference count reached zero
+ * and the inode is not hashed anywhere.  Used to clear anything
+ * that needs to be, before the inode is completely destroyed and put
+ * on the inode free list. We use this to drop out reference to the
+ * lower inode.
+ *
+ * TODO: Why do we just not drop the reference to the lower inode in
+ *      ecryptfs_destroy_inode?
+ */
+static void ecryptfs_clear_inode(struct inode *inode)
+{
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; inode = [%p]; i_ino = [%lu]\n",
+			inode, inode->i_ino);
+	iput(ECRYPTFS_INODE_TO_LOWER(inode));
+	ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+}
+
+/**
+ * Called in do_umount() if the MNT_FORCE flag was used and this
+ * function is defined.  See comment in linux/fs/super.c:do_umount().
+ * Used only in nfs, to kill any pending RPC tasks, so that subsequent
+ * code can actually succeed and won't leave tasks that need handling.
+ *
+ * PS. I wonder if this is somehow useful to undo damage that was
+ * left in the kernel after a user level file server (such as amd)
+ * dies.
+ */
+static void ecryptfs_umount_begin(struct super_block *sb)
+{
+	struct super_block *lower_sb;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	/* TODO: Explain why we are umounting the lower superblock */
+	lower_sb = ECRYPTFS_SUPERBLOCK_TO_LOWER(sb);
+	if (lower_sb->s_op->umount_begin)
+		lower_sb->s_op->umount_begin(lower_sb);
+	ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+}
+
+/* TODO: Where is this normally declared? */
+int seq_printf(struct seq_file *m, const char *f, ...);
+
+/**
+ * Prints the directory we are currently mounted over
+ * 
+ * @return Zero on success; non-zero otherwise
+ */
+static int ecryptfs_show_options(struct seq_file *m, struct vfsmount *mnt)
+{
+	struct super_block *sb = mnt->mnt_sb;
+	int ret = 0;
+	char *tmp = NULL;
+	char *path;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	tmp = (char *)__get_free_page(GFP_KERNEL);
+	if (!tmp) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	/* TODO: wrap? */
+	path = d_path(ECRYPTFS_DENTRY_TO_LOWER(sb->s_root),
+		      ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb)->lower_mnt, tmp,
+		      PAGE_SIZE);
+	seq_printf(m, ",dir=%s", path);
+	free_page((unsigned long)tmp);
+ out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; ret = [%d]\n",ret);
+	return ret;
+}
+
+/* TODO: Provide operations? (quota stuff, dirty, sync) */
+struct super_operations ecryptfs_sops = {
+	.alloc_inode = ecryptfs_alloc_inode,
+	.destroy_inode = ecryptfs_destroy_inode,
+	.read_inode = ecryptfs_read_inode,
+	.drop_inode = ecryptfs_drop_inode,
+	.put_super = ecryptfs_put_super,
+	.statfs = ecryptfs_statfs,
+	.remount_fs = ecryptfs_remount_fs,
+	.clear_inode = ecryptfs_clear_inode,
+	.umount_begin = ecryptfs_umount_begin,
+	.show_options = ecryptfs_show_options
+};
#15 lore raw

Re: [PATCH 6/12: eCryptfs] Superblock operations

fs/ecryptfs/super.c line 121

Quoted 8 lines · Phillip Hellewell
fs/ecryptfs/super.c +8 -0
+/**
+ * This is called through iput_final().
+ * This is function will replace generic_drop_inode. The end result of which
+ * is we are skipping the check in inode->i_nlink, which we do not use.
+ */
+static void ecryptfs_drop_inode(struct inode *inode) {
+       generic_delete_inode(inode);
+}

Please drop this useless wrapper and introduce it when it actually does something.

Pekka

#16 lore raw

Re: [PATCH 6/12: eCryptfs] Superblock operations

Quoted 12 lines · Pekka Enberg

fs/ecryptfs/super.c line 121

Quoted 8 lines · Phillip Hellewell
fs/ecryptfs/super.c +8 -0
+/**
+ * This is called through iput_final().
+ * This is function will replace generic_drop_inode. The end result of which
+ * is we are skipping the check in inode->i_nlink, which we do not use.
+ */
+static void ecryptfs_drop_inode(struct inode *inode) {
+       generic_delete_inode(inode);
+}

Please drop this useless wrapper and introduce it when it actually does something.

It does do something. By providing this function, we over-ride the default flow of execution.

If we did not provide this function, the flow would be the following:

iput_final -> generic_drop_inode -> generic_delete_inode (or generic_forget_inode).

However, since we do not care about the i_nlink value, which generic_drop_inode checks in order to call generic_delete_inode, we simply circumvent the check by redirecting the flow thusly:

iput_final -> ecryptfs_drop_inode -> generic_delete_inode

I don't see a problem with doing that, but perhaps there is? Please elaborate if so.

Quoted 7 lines

Pekka
-
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

--
Michael C. Thompson <[email protected]>
Software-Engineer, IBM LTC Security

#17 lore raw

Re: [PATCH 6/12: eCryptfs] Superblock operations

Hi,

Quoted 11 lines · Pekka Enberg
Quoted 11 lines

fs/ecryptfs/super.c line 121

Quoted 8 lines
fs/ecryptfs/super.c +8 -0
+/**
+ * This is called through iput_final().
+ * This is function will replace generic_drop_inode. The end result of which
+ * is we are skipping the check in inode->i_nlink, which we do not use.
+ */
+static void ecryptfs_drop_inode(struct inode *inode) {
+       generic_delete_inode(inode);
+}

Please drop this useless wrapper and introduce it when it actually does something.

Quoted 2 lines · Michael Thompson

I don't see a problem with doing that, but perhaps there is? Please elaborate if so.

You can set ecryptfs_sops->drop_inode to generic_delete_inode directly, no?

Pekka

#18 lore raw

Re: [PATCH 6/12: eCryptfs] Superblock operations

Quoted 15 lines · Pekka Enberg

Hi,

Quoted 11 lines · Pekka Enberg
Quoted 11 lines

fs/ecryptfs/super.c line 121

Quoted 8 lines
fs/ecryptfs/super.c +8 -0
+/**
+ * This is called through iput_final().
+ * This is function will replace generic_drop_inode. The end result of which
+ * is we are skipping the check in inode->i_nlink, which we do not use.
+ */
+static void ecryptfs_drop_inode(struct inode *inode) {
+       generic_delete_inode(inode);
+}

Please drop this useless wrapper and introduce it when it actually does something.

Quoted 6 lines
Quoted 2 lines · Michael Thompson

I don't see a problem with doing that, but perhaps there is? Please elaborate if so.

You can set ecryptfs_sops->drop_inode to generic_delete_inode directly, no?

Yes, I do believe I could do that and save a function call. My mind is wobbely today.

Quoted 4 lines

Pekka

--
Michael C. Thompson <[email protected]>
Software-Engineer, IBM LTC Security

#19 lore raw

Re: [PATCH 6/12: eCryptfs] Superblock operations

Quoted 17 lines · Michael Thompson
Quoted 16 lines · Pekka Enberg

Hi,

Quoted 11 lines · Pekka Enberg
Quoted 11 lines

fs/ecryptfs/super.c line 121

Quoted 8 lines
fs/ecryptfs/super.c +8 -0
+/**
+ * This is called through iput_final().
+ * This is function will replace generic_drop_inode. The end result of which
+ * is we are skipping the check in inode->i_nlink, which we do not use.
+ */
+static void ecryptfs_drop_inode(struct inode *inode) {
+       generic_delete_inode(inode);
+}

Please drop this useless wrapper and introduce it when it actually does something.

On Mon, 2005-11-21 at 09:57 -0600, Michael Thompson wrote:

Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

I don't see a problem with doing that, but perhaps there is? Please elaborate if so.

You can set ecryptfs_sops->drop_inode to generic_delete_inode directly, no?

Yes, I do believe I could do that and save a function call. My mind is wobbely today.

Very wobbley, can't even spell right. Is this an acceptable solution? I didn't even bother to ask that ;)

Quoted 11 lines
Quoted 5 lines

Pekka

--
Michael C. Thompson <[email protected]>
Software-Engineer, IBM LTC Security

--
Michael C. Thompson <[email protected]>
Software-Engineer, IBM LTC Security

#20 lore raw

Re: [PATCH 6/12: eCryptfs] Superblock operations

Hi Michael,

Quoted 2 lines · Pekka Enberg
Quoted 2 lines
Quoted 2 lines

You can set ecryptfs_sops->drop_inode to generic_delete_inode directly, no?

Quoted 5 lines · Michael Thompson
Quoted 2 lines

Yes, I do believe I could do that and save a function call. My mind is wobbely today.

Very wobbley, can't even spell right. Is this an acceptable solution? I didn't even bother to ask that ;)

Yes, that's what I was suggesting.

Pekka

#21 lore raw

PATCH 7/12 File operations

In reply to Phillip Hellewell

eCryptfs file operations. Includes code to read header information from the underyling file when needed.

---

 file.c |  710 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 710 insertions(+)
fs/ecryptfs/file.c +710 -0
@@ -0,0 +1,710 @@
+/**
+ * eCryptfs: Linux filesystem encryption layer
+ *
+ * Copyright (c) 1997-2004 Erez Zadok
+ * Copyright (c) 2001-2004 Stony Brook University
+ * Copyright (c) 2005 International Business Machines Corp.
+ *   Author(s): Michael A. Halcrow <[email protected]>
+ *   		Michael C. Thompson <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <linux/file.h>
+#include <linux/poll.h>
+#include <linux/mount.h>
+#include <linux/pagemap.h>
+#include <linux/security.h>
+#include <linux/smp_lock.h>
+#include <linux/compat.h>
+#include "ecryptfs_kernel.h"
+
+/**
+ * @param file		File we are seeking in
+ * @param offset	The offset to seek to
+ * @param origin	Where to start seek from
+ * 			(0=beginning,1=cur pos,2=end of file)
+ * @return		The position we have seeked to, or negative on error
+ */
+static loff_t ecryptfs_llseek(struct file *file, loff_t offset, int origin)
+{
+	loff_t ret;
+	int rc;
+	struct file *lower_file = NULL;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; offset = [%lld] origin = [%d]",
+			"\n", offset, origin);
+	if (NULL != ECRYPTFS_FILE_TO_PRIVATE(file))
+		lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+	/* Intent: If our offset is past the end of our file, we're going to
+	 * need to grow it so we have a valid length of 0's */
+	if (offset > i_size_read(file->f_dentry->d_inode)) {
+		rc = ecryptfs_truncate(file->f_dentry, offset);
+		if (rc) {
+			ret = rc;
+			ecryptfs_printk(0, KERN_ERR, "Error on attempt to "
+					"truncate to (higher) offset; rc = "
+					"[%d]\n", rc);
+			goto out;
+		}
+	}
+	ret = generic_file_llseek(file, offset, origin);
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; ret = [%lld]\n", ret);
+	return ret;
+}
+
+/**
+ * generic_file_read updates the atime of upper layer inode.  But, it
+ * doesn't give us a chance to update the atime of the lower layer
+ * inode.  This function is a wrapper to generic_file_read.  It
+ * updates the atime of the lower level inode if generic_file_read
+ * returns without any errors. This is to be used only for file reads.
+ * The function to be used for directory reads is ecryptfs_read.
+ */
+static ssize_t ecryptfs_read_update_atime(struct file *file, char __user * buf,
+					  size_t count, loff_t * ppos)
+{
+	int err = 0;
+	struct dentry *lower_dentry;
+	struct vfsmount *lower_vfsmount;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	err = generic_file_read(file, buf, count, ppos);
+	if (err >= 0) {
+		lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(file->f_dentry);
+		lower_vfsmount = ECRYPTFS_SUPERBLOCK_TO_PRIVATE(
+			file->f_dentry->d_inode->i_sb)->lower_mnt;
+		touch_atime(lower_vfsmount, lower_dentry);
+	}
+	ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+	return err;
+}
+
+static ssize_t
+ecryptfs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+	int rc = -EINVAL;
+	struct file *lower_file = NULL;
+	loff_t pos = *ppos;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; file = [%p]\n", file);
+	if (!lower_file->f_op || !lower_file->f_op->read)
+		goto out;
+	rc = lower_file->f_op->read(lower_file, buf, count, &pos);
+	if (rc >= 0)
+		/* atime should also be updated for reads of size zero
+		 * or more */
+		ecryptfs_copy_attr_atime(file->f_dentry->d_inode,
+					 lower_file->f_dentry->d_inode);
+	lower_file->f_pos = *ppos = pos;
+	memcpy(&(file->f_ra), &(lower_file->f_ra),
+	       sizeof(struct file_ra_state));
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
+
+/**
+ * Directory write operation.
+ *
+ * TODO: Encrypt the directory pages also if policy calls for it
+ */
+static ssize_t ecryptfs_dir_write(struct file *file, const char __user *buf,
+				  size_t count, loff_t *ppos)
+{
+	int rc = -EINVAL;
+	struct file *lower_file = NULL;
+	struct inode *inode;
+	struct inode *lower_inode;
+	loff_t pos = *ppos;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; file = [%p]\n", file);
+	inode = file->f_dentry->d_inode;
+	lower_inode = ECRYPTFS_INODE_TO_LOWER(inode);
+	if (!lower_file->f_op || !lower_file->f_op->write)
+		goto out;
+	/* adjust for append -- seek to the end of the file */
+	if ((file->f_flags & O_APPEND) && (count != 0))
+		pos = i_size_read(inode);
+	if (count != 0)
+		rc = lower_file->f_op->write(lower_file, buf, count, &pos);
+	else
+		rc = 0;
+	/* copy ctime and mtime from lower layer attributes
+	 * atime is unchanged for both layers */
+	if (rc >= 0)
+		ecryptfs_copy_attr_times(inode, lower_inode);
+	/* because pwrite() does not have any way to tell us that it
+	 * is our caller, then we don't know for sure if we have to
+	 * update the file positions.  This hack relies on write()
+	 * having passed us the "real" pointer of its struct file's
+	 * f_pos field. */
+	lower_file->f_pos = *ppos = pos;
+	/* update this inode's size */
+	if (pos > i_size_read(inode))
+		i_size_write(inode, pos);
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
+
+struct ecryptfs_getdents_callback {
+	void *dirent;
+	struct dentry *dentry;
+	filldir_t filldir;
+	int err;
+	int filldir_called;
+	int entries_written;
+};
+
+/* copied from generic filldir in fs/readir.c */
+static int
+ecryptfs_filldir(void *dirent, const char *name, int namelen, loff_t offset,
+		 ino_t ino, unsigned int d_type)
+{
+	struct ecryptfs_crypt_stats *crypt_stats;
+	struct ecryptfs_getdents_callback *buf =
+	    (struct ecryptfs_getdents_callback *)dirent;
+	int rc;
+	char *decoded_name;
+	int decoded_length;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter w/ name = [%.*s]\n", namelen,
+			name);
+	/* Get the crypto stats for this file */
+	/* TODO: If filldir oopses, look here ... */
+	crypt_stats = ECRYPTFS_DENTRY_TO_PRIVATE(buf->dentry)->crypt_stats;
+	buf->filldir_called++;
+	/* TODO: Halcrow: Check the headers of the file to determine
+	 * if the filename needs decryption (or hiding, or
+	 * obfuscation, etc.) */
+	decoded_length = ecryptfs_decode_filename(name, namelen, &decoded_name,
+						  ECRYPTFS_SKIP_DOTS,
+						  crypt_stats);
+	if (decoded_length < 0)
+		return 0;
+	rc = buf->filldir(buf->dirent, decoded_name, decoded_length, offset,
+			  ino, d_type);
+	kfree(decoded_name);
+	if (rc >= 0)
+		buf->entries_written++;
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
+
+/**
+ * @param file The ecryptfs file struct
+ * @param filldir The filldir callback function
+ */
+static int ecryptfs_readdir(struct file *file, void *dirent, filldir_t filldir)
+{
+	int rc = -ENOTDIR;
+	struct file *lower_file = NULL;
+	struct inode *inode;
+	struct ecryptfs_getdents_callback buf;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; file = [%p]\n", file);
+	lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+	inode = file->f_dentry->d_inode;
+	buf.dirent = dirent;
+	buf.dentry = file->f_dentry;
+	buf.filldir = filldir;
+retry:
+	buf.filldir_called = 0;
+	buf.entries_written = 0;
+	buf.err = 0;
+	rc = vfs_readdir(lower_file, ecryptfs_filldir, (void *)&buf);
+	if (buf.err)
+		rc = buf.err;
+	if (buf.filldir_called && !buf.entries_written)
+		goto retry;
+	file->f_pos = lower_file->f_pos;
+	if (rc >= 0)
+		ecryptfs_copy_attr_atime(inode, lower_file->f_dentry->d_inode);
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
+
+static unsigned int ecryptfs_poll(struct file *file, poll_table * wait)
+{
+	unsigned int mask = DEFAULT_POLLMASK;
+	struct file *lower_file = NULL;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; file = [%p]\n", file);
+	if (!lower_file->f_op || !lower_file->f_op->poll)
+		goto out;
+	mask = lower_file->f_op->poll(lower_file, wait);
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; mask = [%x]\n", mask);
+	return mask;
+}
+
+/**
+ * @return Zero on success; non-zero otherwise
+ */
+static int
+read_inode_size_from_header(struct file *lower_file,
+			    struct inode *lower_inode, struct inode *inode)
+{
+	int rc = 0;
+	struct page *header_page;
+	unsigned char *header_virt;
+	u64 data_size;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter w/ lower_inode = [%p]; inode = "
+			"[%p]\n", lower_inode, inode);
+	header_page = grab_cache_page(lower_inode->i_mapping, 0);
+	if (!header_page) {
+		rc = -EINVAL;
+		ecryptfs_printk(0, KERN_ERR, "grab_cache_page for header page "
+				"failed\n");
+		goto out;
+	}
+	header_virt = kmap(header_page);
+	rc = lower_inode->i_mapping->a_ops->readpage(lower_file, header_page);
+	if (rc) {
+		ecryptfs_printk(0, KERN_ERR, "Error reading header page\n");
+		goto out_unmap;
+	}
+	memcpy(&data_size, header_virt, sizeof(data_size));
+	i_size_write(inode, (loff_t) data_size);
+	ecryptfs_printk(1, KERN_NOTICE, "Read inode size from header: [%llu]\n",
+			i_size_read(inode));
+out_unmap:
+	kunmap(header_page);
+	page_cache_release(header_page);
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
+
+kmem_cache_t *ecryptfs_file_info_cache;
+
+/**
+ * Opens the file specified by inode.
+ *
+ * @param inode	inode speciying file to open
+ * @param file	Structure to return filled in
+ * @return Zero on success; non-zero otherwise
+ */
+static int ecryptfs_open(struct inode *inode, struct file *file)
+{
+	int rc = 0;
+	struct ecryptfs_crypt_stats *crypt_stats = NULL;
+	struct dentry *ecryptfs_dentry = file->f_dentry;
+	struct dentry *lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(ecryptfs_dentry);
+	struct inode *lower_inode = NULL;
+	struct file *lower_file = NULL;
+	struct vfsmount *lower_mnt;
+	int lower_flags;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; i_ino = [%lu] inode = [%p] "
+			"inode->i_size = [%lld] inode->i_count = [%d] file->"
+			"f_dentry = [%p] file->f_dentry->d_name.name = [%s] "
+			"file->f_dentry->d_name.len = [%d]\n", inode->i_ino,
+			inode, atomic_read(&inode->i_count), i_size_read(inode),
+			ecryptfs_dentry, ecryptfs_dentry->d_name.name,
+			ecryptfs_dentry->d_name.len);
+
+	/* ECRYPTFS_DENTRY_TO_PRIVATE(ecryptfs_dentry) Allocated in
+	 * ecryptfs_lookup() */
+	/* Released in ecryptfs_release or end of function if failure */
+	ECRYPTFS_FILE_TO_PRIVATE_SM(file) =
+		kmem_cache_alloc(ecryptfs_file_info_cache, SLAB_KERNEL);
+	if (!ECRYPTFS_FILE_TO_PRIVATE_SM(file)) {
+		ecryptfs_printk(0, KERN_ERR,
+				"Error attempting to allocate memory\n");
+		rc = -ENOMEM;
+		goto out;
+	}
+	lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(ecryptfs_dentry);
+	crypt_stats = &(ECRYPTFS_INODE_TO_PRIVATE(inode)->crypt_stats);
+	if (!crypt_stats->policy_applied) {
+		ecryptfs_printk(1, KERN_NOTICE, "Setting flags for stats...\n");
+		/* Policy code enabled in future release */
+		crypt_stats->policy_applied = 1;
+		crypt_stats->encrypted = 1;
+	}
+	/* This mntget & dget is undone via fput when the file is released */
+	dget(lower_dentry);
+	lower_flags = file->f_flags;
+	if ((lower_flags & O_ACCMODE) == O_WRONLY)
+		lower_flags = (lower_flags & O_ACCMODE) | O_RDWR;
+	if (file->f_flags & O_APPEND)
+		lower_flags &= ~O_APPEND;
+	lower_mnt = ECRYPTFS_SUPERBLOCK_TO_PRIVATE(inode->i_sb)->lower_mnt;
+	mntget(lower_mnt);
+	/* Corresponding fput() in ecryptfs_release() */
+	lower_file = dentry_open(lower_dentry, lower_mnt, lower_flags);
+	if (IS_ERR(lower_file)) {
+		rc = PTR_ERR(lower_file);
+		ecryptfs_printk(0, KERN_ERR, "Error opening lower file\n");
+		goto out_puts;
+	}
+	ECRYPTFS_FILE_TO_LOWER(file) = lower_file;
+	/* Isn't this check the same as the one in lookup? */
+	lower_inode = lower_dentry->d_inode;
+	if (S_ISDIR(ecryptfs_dentry->d_inode->i_mode)) {
+		ecryptfs_printk(1, KERN_NOTICE, "This is a directory\n");
+		crypt_stats->encrypted = 0;
+		rc = 0;
+		goto out;
+	}
+	ecryptfs_printk(1, KERN_NOTICE, "(file->f_flags & O_CREAT) = [%d] "
+			"lower_inode->i_size = [%llu] crypt_stats->struct_"
+			"initialized = [%d] crypt_stats->key_valid = [%d]\n",
+			(file->f_flags & O_CREAT),
+			(unsigned long long)lower_inode->i_size,
+			crypt_stats->struct_initialized,
+			crypt_stats->key_valid);
+	/* TODO: This was originally used to determine if it was a new file,
+	 * but if we are allowing pass-through mode, then we couldbe opening
+	 * a 0-length normal file.. in which case, this can't cause an error
+	 * So.... what do we want to do? */
+	if (lower_inode->i_size == 0) {
+		ecryptfs_printk(0, KERN_EMERG, "Zero-length lower file; "
+				"ecryptfs_create() had a problem?\n");
+		rc = -ENOENT;
+		goto out_puts;
+	} else if (!crypt_stats->policy_applied || !crypt_stats->key_valid) {
+		/* crypto.c */
+		rc = ecryptfs_read_headers(ecryptfs_dentry, lower_file);
+		if (rc) {
+			ecryptfs_printk(1, KERN_NOTICE,
+					"Valid headers not found\n");
+			crypt_stats->encrypted = 0;
+		} else
+			read_inode_size_from_header(lower_file, lower_inode,
+						    inode);
+	} else
+		ecryptfs_printk(1, KERN_NOTICE, "crypt_stats->struct_"
+				"initialized = [%d]; crypt_stats->key_valid = "
+				"[%d]\n", crypt_stats->struct_initialized,
+				crypt_stats->key_valid);
+	ECRYPTFS_FILE_TO_LOWER(file) = lower_file;
+	goto out;
+out_puts:
+	mntput(lower_mnt);
+	dput(lower_dentry);
+	kmem_cache_free(ecryptfs_file_info_cache,
+			ECRYPTFS_FILE_TO_PRIVATE(file));
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
+
+static int ecryptfs_flush(struct file *file)
+{
+	int rc = 0;
+	struct file *lower_file = NULL;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; file = [%p]\n", file);
+	lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+	if (lower_file->f_op && lower_file->f_op->flush)
+		rc = lower_file->f_op->flush(lower_file);
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
+
+static int ecryptfs_release(struct inode *ecryptfs_inode, struct file *file)
+{
+	int rc = 0;
+	struct file *lower_file = NULL;
+	struct inode *lower_inode;
+
+	ecryptfs_printk(1, KERN_NOTICE,
+			"Enter; ecryptfs_inode->i_count = [%d]\n",
+			ecryptfs_inode->i_count);
+	lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+	kmem_cache_free(ecryptfs_file_info_cache,
+			ECRYPTFS_FILE_TO_PRIVATE(file));
+	lower_inode = ECRYPTFS_INODE_TO_LOWER(ecryptfs_inode);
+	fput(lower_file);
+	ecryptfs_inode->i_blocks = lower_inode->i_blocks;
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
+
+static int
+ecryptfs_fsync(struct file *file, struct dentry *dentry, int datasync)
+{
+	int rc = -EINVAL;
+	struct file *lower_file = NULL;
+	struct dentry *lower_dentry;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	if (NULL == file) {
+		lower_dentry = ecryptfs_lower_dentry(dentry);
+		if (lower_dentry->d_inode->i_fop
+		    && lower_dentry->d_inode->i_fop->fsync) {
+			down(&lower_dentry->d_inode->i_sem);
+			rc = lower_dentry->d_inode->i_fop->fsync(lower_file,
+								 lower_dentry,
+								 datasync);
+			up(&lower_dentry->d_inode->i_sem);
+		}
+	} else {
+		if (NULL == ECRYPTFS_FILE_TO_PRIVATE(file)) {
+			rc = -EINVAL;
+			ecryptfs_printk(0, KERN_ERR, "ECRYPTFS_FILE_TO_PRIVATE"
+					"(file=[%p]) == NULL\n", file);
+			goto out;
+		}
+		lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+		lower_dentry = ecryptfs_lower_dentry(dentry);
+		if (lower_file->f_op && lower_file->f_op->fsync) {
+			down(&lower_dentry->d_inode->i_sem);
+			rc = lower_file->f_op->fsync(lower_file, lower_dentry,
+						     datasync);
+			up(&lower_dentry->d_inode->i_sem);
+		}
+	}
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
+
+static void locks_delete_block(struct file_lock *waiter)
+{
+	lock_kernel();
+	list_del_init(&waiter->fl_block);
+	list_del_init(&waiter->fl_link);
+	waiter->fl_next = NULL;
+	unlock_kernel();
+}
+
+static int ecryptfs_posix_lock(struct file *file, struct file_lock *fl, int cmd)
+{
+	int rc;
+
+lock_file:
+	rc = posix_lock_file(file, fl);
+	if ((rc != -EAGAIN) || (cmd == F_SETLK))
+		goto out;
+	rc = wait_event_interruptible(fl->fl_wait, !fl->fl_next);
+	if (!rc)
+		goto lock_file;
+	locks_delete_block(fl);
+out:
+	return rc;
+}
+
+static int ecryptfs_setlk(struct file *file, int cmd, struct file_lock *fl)
+{
+	int rc = -EINVAL;
+	struct inode *inode, *lower_inode;
+	struct file *lower_file = NULL;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+	inode = file->f_dentry->d_inode;
+	lower_inode = lower_file->f_dentry->d_inode;
+	/* Don't allow mandatory locks on files that may be memory mapped
+	 * and shared. */
+	if (IS_MANDLOCK(lower_inode) &&
+	    (lower_inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID &&
+	    mapping_writably_mapped(lower_file->f_mapping)) {
+		rc = -EAGAIN;
+		goto out;
+	}
+	if (cmd == F_SETLKW)
+		fl->fl_flags |= FL_SLEEP;
+	rc = -EBADF;
+	switch (fl->fl_type) {
+	case F_RDLCK:
+		if (!(lower_file->f_mode & FMODE_READ))
+			goto out;
+		break;
+	case F_WRLCK:
+		if (!(lower_file->f_mode & FMODE_WRITE))
+			goto out;
+		break;
+	case F_UNLCK:
+		break;
+	default:
+		rc = -EINVAL;
+		goto out;
+	}
+	fl->fl_file = lower_file;
+	rc = security_file_lock(lower_file, fl->fl_type);
+	if (rc)
+		goto out;
+	if (lower_file->f_op && lower_file->f_op->lock != NULL) {
+		rc = lower_file->f_op->lock(lower_file, cmd, fl);
+		if (rc)
+			goto out;
+		goto upper_lock;
+	}
+	rc = ecryptfs_posix_lock(lower_file, fl, cmd);
+	if (rc)
+		goto out;
+upper_lock:
+	fl->fl_file = file;
+	rc = ecryptfs_posix_lock(file, fl, cmd);
+	if (rc) {
+		fl->fl_type = F_UNLCK;
+		fl->fl_file = lower_file;
+		ecryptfs_posix_lock(lower_file, fl, cmd);
+	}
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
+
+static int ecryptfs_getlk(struct file *file, struct file_lock *fl)
+{
+	int rc = 0;
+	struct file_lock *tempfl = NULL;
+
+	if (file->f_op && file->f_op->lock) {
+		rc = file->f_op->lock(file, F_GETLK, fl);
+		if (rc < 0)
+			goto out;
+	} else
+		tempfl = posix_test_lock(file, fl);
+	if (!tempfl)
+		fl->fl_type = F_UNLCK;
+	else
+		memcpy(fl, tempfl, sizeof(struct file_lock));
+out:
+	return rc;
+}
+
+static int ecryptfs_fasync(int fd, struct file *file, int flag)
+{
+	int rc = 0;
+	struct file *lower_file = NULL;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	if (NULL != ECRYPTFS_FILE_TO_PRIVATE(file))
+		lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+	else {
+		rc = -EINVAL;
+		goto out;
+	}
+	if (lower_file->f_op && lower_file->f_op->fasync)
+		rc = lower_file->f_op->fasync(fd, lower_file, flag);
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+	return rc;
+}
+
+static int ecryptfs_lock(struct file *file, int cmd, struct file_lock *fl)
+{
+	int rc = 0;
+	struct file *lower_file = NULL;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	if (ECRYPTFS_FILE_TO_PRIVATE(file) != NULL)
+		lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+	ASSERT(lower_file != NULL);
+	rc = -EINVAL;
+	if (!fl)
+		goto out;
+	fl->fl_file = lower_file;
+	switch (cmd) {
+	case F_GETLK:
+	case F_GETLK64:
+		rc = ecryptfs_getlk(lower_file, fl);
+		break;
+	case F_SETLK:
+	case F_SETLKW:
+	case F_SETLK64:
+	case F_SETLKW64:
+		fl->fl_file = file;
+		rc = ecryptfs_setlk(file, cmd, fl);
+		break;
+	default:
+		rc = -EINVAL;
+	}
+	fl->fl_file = file;
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+	return rc;
+}
+
+static ssize_t ecryptfs_sendfile(struct file *file, loff_t * ppos,
+				 size_t count, read_actor_t actor, void *target)
+{
+	struct file *lower_file = NULL;
+	int rc = -EINVAL;
+
+	if (ECRYPTFS_FILE_TO_PRIVATE(file) != NULL)
+		lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+	ASSERT(lower_file != NULL);
+	/* TODO: Is this a superfluous check for f_op? */
+	if (lower_file->f_op && lower_file->f_op->sendfile)
+		rc = lower_file->f_op->sendfile(lower_file, ppos, count,
+						actor, target);
+
+	return rc;
+}
+
+static int ecryptfs_ioctl(struct inode *inode, struct file *file,
+			  unsigned int cmd, unsigned long arg);
+
+struct file_operations ecryptfs_dir_fops = {
+	.read = ecryptfs_read,
+	.write = ecryptfs_dir_write,
+	.readdir = ecryptfs_readdir,
+	.poll = ecryptfs_poll,
+	.ioctl = ecryptfs_ioctl,
+	.mmap = generic_file_mmap,
+	.open = ecryptfs_open,
+	.flush = ecryptfs_flush,
+	.release = ecryptfs_release,
+	.fsync = ecryptfs_fsync,
+	.fasync = ecryptfs_fasync,
+	.lock = ecryptfs_lock,
+	.sendfile = ecryptfs_sendfile,
+};
+
+struct file_operations ecryptfs_main_fops = {
+	.llseek = ecryptfs_llseek,
+	.read = ecryptfs_read_update_atime,
+	.write = generic_file_write,
+	.readdir = ecryptfs_readdir,
+	.poll = ecryptfs_poll,
+	.ioctl = ecryptfs_ioctl,
+	.mmap = generic_file_mmap,
+	.open = ecryptfs_open,
+	.flush = ecryptfs_flush,
+	.release = ecryptfs_release,
+	.fsync = ecryptfs_fsync,
+	.fasync = ecryptfs_fasync,
+	.lock = ecryptfs_lock,
+	.sendfile = ecryptfs_sendfile,
+};
+
+/**
+ * TODO: sysfs
+ */
+static int
+ecryptfs_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+	       unsigned long arg)
+{
+	int rc = 0;
+	struct file *lower_file = NULL;
+	if (ECRYPTFS_FILE_TO_PRIVATE(file) != NULL)
+		lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+	if (lower_file && lower_file->f_op && lower_file->f_op->ioctl)
+		rc = lower_file->f_op->ioctl(ECRYPTFS_INODE_TO_LOWER(inode),
+					     lower_file, cmd, arg);
+	else
+		rc = -ENOTTY;
+	return rc;
+}
#22 lore raw

Re: [PATCH 7/12: eCryptfs] File operations

Quoted 7 lines · Phillip Hellewell

+/**
+ * Opens the file specified by inode.
+ *
+ * @param inode inode speciying file to open
+ * @param file Structure to return filled in
+ * @return Zero on success; non-zero otherwise
+ */

General comment: please use the kerneldoc format properly or don't use kerneldoc at all.

Pekka

#23 lore raw

Re: [PATCH 7/12: eCryptfs] File operations

Quoted 11 lines · Pekka Enberg
Quoted 7 lines · Phillip Hellewell

+/**
+ * Opens the file specified by inode.
+ *
+ * @param inode inode speciying file to open
+ * @param file Structure to return filled in
+ * @return Zero on success; non-zero otherwise
+ */

General comment: please use the kerneldoc format properly or don't use kerneldoc at all.

OK, this will be fixed for the next submission.

Quoted 7 lines

Pekka
-
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

--
Michael C. Thompson <[email protected]>
Software-Engineer, IBM LTC Security

#24 lore raw

PATCH 8/12 Dentry operations

In reply to Phillip Hellewell

eCryptfs dentry operations.

---

 dentry.c |  105 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 105 insertions(+)
fs/ecryptfs/dentry.c +105 -0
@@ -0,0 +1,105 @@
+/**
+ * eCryptfs: Linux filesystem encryption layer
+ *
+ * Copyright (c) 1997-2003 Erez Zadok
+ * Copyright (c) 2001-2003 Stony Brook University
+ * Copyright (c) 2005 International Business Machines Corp.
+ *   Author(s): Michael A. Halcrow <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <linux/dcache.h>
+#include <linux/namei.h>
+#include "ecryptfs_kernel.h"
+
+/**
+ * called when the VFS needs to revalidate a dentry. This
+ * is called whenever a name lookup finds a dentry in the
+ * dcache. Most filesystems leave this as NULL, because all their
+ * dentries in the dcache are valid.
+ *
+ *
+ * @param dentry	ecryptfs dentry
+ * @param nd		
+ * @return 		1 if valid, 0 otherwise
+ */
+static int ecryptfs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
+{
+	int err = 1;
+	struct dentry *lower_dentry;
+	struct dentry *saved_dentry;
+	struct vfsmount *saved_vfsmount;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; dentry->d_name.name = [%s]\n",
+			dentry->d_name.name);
+	lower_dentry = ecryptfs_lower_dentry(dentry);
+	if (!lower_dentry) {
+		err = 0;
+		goto out;
+	}
+	if (!lower_dentry->d_op || !lower_dentry->d_op->d_revalidate)
+		goto out;
+	/* Call the lower dentry's d_revalidate (assuming it has one) */
+	saved_dentry = nd->dentry;
+	saved_vfsmount = nd->mnt;
+	nd->dentry = lower_dentry;
+	nd->mnt = ECRYPTFS_SUPERBLOCK_TO_PRIVATE(dentry->d_sb)->lower_mnt;
+	err = lower_dentry->d_op->d_revalidate(lower_dentry, nd);
+	nd->dentry = saved_dentry;
+	nd->mnt = saved_vfsmount;
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n", err);
+	return err;
+}
+
+kmem_cache_t *ecryptfs_dentry_info_cache;
+
+/* Notes:
+ * Called when a dentry is really deallocated
+ * Sanity check? wrapper around ecryptfs_dput()
+ */
+static void ecryptfs_d_release(struct dentry *dentry)
+{
+	struct dentry *lower_dentry;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; dentry->d_name->name = [%s]\n",
+			dentry->d_name.name);
+	if (!dentry) {
+		ecryptfs_printk(0, KERN_ERR, "NULL dentry\n");
+		goto out;
+	}
+	if (!ECRYPTFS_DENTRY_TO_PRIVATE(dentry)) {
+		ecryptfs_printk(1, KERN_ERR, "dentry without private data: "
+				"[%*s]\n", dentry->d_name.len,
+				dentry->d_name.name);
+		goto out;
+	}
+	lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(dentry);
+	if (ECRYPTFS_DENTRY_TO_PRIVATE(dentry))
+		kmem_cache_free(ecryptfs_dentry_info_cache,
+				ECRYPTFS_DENTRY_TO_PRIVATE(dentry));
+	if (lower_dentry)
+		dput(lower_dentry);
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+	return;
+}
+
+struct dentry_operations ecryptfs_dops = {
+	.d_revalidate = ecryptfs_d_revalidate,
+	.d_release = ecryptfs_d_release,
+};
#25 lore raw

PATCH 9/12 Inode operations

In reply to Phillip Hellewell

eCryptfs inode operations. Includes functions to support inode interpolation between upper and lower inodes.

---

 inode.c | 1084 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 1084 insertions(+)
fs/ecryptfs/inode.c +1084 -0
@@ -0,0 +1,1084 @@
+/**
+ * eCryptfs: Linux filesystem encryption layer
+ *
+ * Copyright (c) 1997-2004 Erez Zadok
+ * Copyright (c) 2001-2004 Stony Brook University
+ * Copyright (c) 2005 International Business Machines Corp.
+ *   Author(s): Michael A. Halcrow <[email protected]>
+ *              Michael C. Thompsion <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <linux/file.h>
+#include <linux/vmalloc.h>
+#include <linux/pagemap.h>
+#include <linux/dcache.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+#include <linux/crypto.h>
+#include "ecryptfs_kernel.h"
+
+static inline struct dentry *lock_parent(struct dentry *dentry)
+{
+	struct dentry *dir;
+
+	dir = dget(dentry->d_parent);
+	down(&(dir->d_inode->i_sem));
+	return dir;
+}
+
+static inline void unlock_parent(struct dentry *dentry)
+{
+	up(&(dentry->d_parent->d_inode->i_sem));
+	dput(dentry->d_parent);
+}
+
+static inline void unlock_dir(struct dentry *dir)
+{
+	up(&dir->d_inode->i_sem);
+	dput(dir);
+}
+
+void ecryptfs_copy_inode_size(struct inode *dst, const struct inode *src)
+{
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	ecryptfs_printk(1, KERN_NOTICE, "src->i_size = [%lld]\n", src->i_size);
+	ecryptfs_printk(1, KERN_NOTICE, "src->i_blocks = [%lu]\n",
+			src->i_blocks);
+	i_size_write(dst, i_size_read((struct inode *)src));
+	dst->i_blocks = src->i_blocks;
+	ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+}
+
+void ecryptfs_copy_attr_atime(struct inode *dest, const struct inode *src)
+{
+	ASSERT(dest != NULL);
+	ASSERT(src != NULL);
+	dest->i_atime = src->i_atime;
+}
+
+void ecryptfs_copy_attr_times(struct inode *dest, const struct inode *src)
+{
+	ASSERT(dest != NULL);
+	ASSERT(src != NULL);
+	dest->i_atime = src->i_atime;
+	dest->i_mtime = src->i_mtime;
+	dest->i_ctime = src->i_ctime;
+}
+
+static void ecryptfs_copy_attr_timesizes(struct inode *dest,
+					 const struct inode *src)
+{
+	ASSERT(dest != NULL);
+	ASSERT(src != NULL);
+	dest->i_atime = src->i_atime;
+	dest->i_mtime = src->i_mtime;
+	dest->i_ctime = src->i_ctime;
+	ecryptfs_copy_inode_size(dest, src);
+}
+
+void ecryptfs_copy_attr_all(struct inode *dest, const struct inode *src)
+{
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	ASSERT(dest != NULL);
+	ASSERT(src != NULL);
+	dest->i_mode = src->i_mode;
+	dest->i_nlink = src->i_nlink;
+	dest->i_uid = src->i_uid;
+	dest->i_gid = src->i_gid;
+	dest->i_rdev = src->i_rdev;
+	dest->i_atime = src->i_atime;
+	dest->i_mtime = src->i_mtime;
+	dest->i_ctime = src->i_ctime;
+	dest->i_blksize = src->i_blksize;
+	ecryptfs_printk(1, KERN_NOTICE, "src->i_blksize = [%lu]\n",
+			src->i_blksize);
+	dest->i_blkbits = src->i_blkbits;
+	dest->i_flags = src->i_flags;
+	ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+}
+
+/**
+ * Creates our file in the lower file system
+ *
+ * @param lower_dir_inode   inode of the parent in the lower fs of the new file
+ * @param lower_dentry	    New file's dentry in the lower fs
+ * @param ecryptfs_dentry   New file's dentry in ecryptfs
+ * @param mode		    The mode of the new file
+ * @param nd		    nameidata of ecryptfs' parent's dentry & vfsmnt
+ * @return		    0 on success; non-zero on error condition
+ */
+static int
+ecryptfs_create_underlying_file(struct inode *lower_dir_inode,
+				struct dentry *lower_dentry,
+				struct dentry *ecryptfs_dentry, int mode,
+				struct nameidata *nd)
+{
+	int rc;
+	struct dentry *saved_dentry = NULL;
+	struct vfsmount *saved_vfsmount = NULL;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	saved_dentry = nd->dentry;
+	saved_vfsmount = nd->mnt;
+	nd->dentry = lower_dentry;
+	nd->mnt = ECRYPTFS_SUPERBLOCK_TO_PRIVATE(
+		ecryptfs_dentry->d_sb)->lower_mnt;
+	rc = vfs_create(lower_dir_inode, lower_dentry, mode, nd);
+	nd->dentry = saved_dentry;
+	nd->mnt = saved_vfsmount;
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
+
+/**
+ * Creates the underlying file and the eCryptfs inode which will link to
+ * it. It will also update the eCryptfs directory inode to mimic the
+ * stats of the lower directory inode
+ *
+ * @param directory_inode   inode of the new file's dentry's parent in ecryptfs
+ * @param ecryptfs_dentry   New file's dentry in ecryptfs
+ * @param mode		    The mode of the new file
+ * @param nd		    nameidata of ecryptfs' parent's dentry & vfsmnt
+ * @return		    0 on success; non-zero on error condition
+ */
+static int
+ecryptfs_do_create(struct inode *directory_inode,
+		   struct dentry *ecryptfs_dentry, int mode,
+		   struct nameidata *nd)
+{
+	int rc;
+	struct dentry *lower_dentry;
+	struct dentry *lower_dir_dentry;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	lower_dentry = ecryptfs_lower_dentry(ecryptfs_dentry);
+	if (IS_ERR(lower_dentry)) {
+		ecryptfs_printk(0, KERN_ERR, "ecryptfs dentry doesn't know"
+				"about its lower counterpart\n");
+		rc = PTR_ERR(lower_dentry);
+		goto out;
+	}
+	lower_dir_dentry = lock_parent(lower_dentry);
+	if (unlikely(IS_ERR(lower_dir_dentry))) {
+		ecryptfs_printk(0, KERN_ERR, "Error locking directory of "
+				"dentry\n");
+		rc = PTR_ERR(lower_dir_dentry);
+		goto out;
+	}
+	rc = ecryptfs_create_underlying_file(lower_dir_dentry->d_inode,
+					     lower_dentry, ecryptfs_dentry,
+					     mode, nd);
+	if (unlikely(rc)) {
+		ecryptfs_printk(0, KERN_ERR,
+				"Failure to create underlying file\n");
+		goto out_lock;
+	}
+	rc = ecryptfs_interpose(lower_dentry, ecryptfs_dentry,
+				directory_inode->i_sb, 0);
+	if (rc) {
+		ecryptfs_printk(0, KERN_ERR, "Failure in ecryptfs_interpose\n");
+		goto out_lock;
+	}
+	ecryptfs_copy_attr_timesizes(directory_inode,
+				     lower_dir_dentry->d_inode);
+out_lock:
+	unlock_dir(lower_dir_dentry);
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
+
+/**
+ * This is the code which will grow the file to be 8192 or
+ * 12288, depending on whether the file derived-IV or
+ * written-IV formatted.
+ */
+static int grow_file(struct dentry *ecryptfs_dentry, struct file *lower_file,
+		     struct inode *inode, struct inode *lower_inode)
+{
+	int rc = 0;
+	struct file fake_file;
+
+	memset(&fake_file, 0, sizeof(fake_file));
+	fake_file.f_dentry = ecryptfs_dentry;
+	ECRYPTFS_FILE_TO_PRIVATE_SM(&fake_file) =
+		kmem_cache_alloc(ecryptfs_file_info_cache, SLAB_KERNEL);
+	if (!(ECRYPTFS_FILE_TO_PRIVATE_SM(&fake_file))) {
+		rc = -ENOMEM;
+		goto out;
+	}
+	ECRYPTFS_FILE_TO_LOWER(&fake_file) = lower_file;
+	ecryptfs_fill_zeros(&fake_file, 1);
+	kmem_cache_free(ecryptfs_file_info_cache,
+			ECRYPTFS_FILE_TO_PRIVATE(&fake_file));
+	i_size_write(inode, 0);
+	ecryptfs_write_inode_size_to_header(lower_file, lower_inode, inode);
+	ECRYPTFS_INODE_TO_PRIVATE(inode)->crypt_stats.new_file = 1;
+out:
+	return rc;
+}
+
+/**
+ * Force the file to be changed from a basic empty file to an ecryptfs file
+ * with a header, 1st IV page & 1st data page
+ *
+ * @return Zero on success
+ */
+static int ecryptfs_initialize_file(struct dentry *ecryptfs_dentry)
+{
+	int rc = 0;
+	int lower_flags;
+	struct ecryptfs_crypt_stats *crypt_stats;
+	struct dentry *lower_dentry;
+	struct dentry *tlower_dentry = NULL;
+	struct file *lower_file;
+	struct inode *inode, *lower_inode;
+	struct vfsmount *lower_mnt;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; dentry->d_name.name = [%s]\n",
+			ecryptfs_dentry->d_name.name);
+
+	lower_dentry = ecryptfs_lower_dentry(ecryptfs_dentry);
+	if (IS_ERR(lower_dentry)) {
+		ecryptfs_printk(0, KERN_ERR, "ecryptfs dentry doesn't know"
+				"about its lower counterpart\n");
+		rc = PTR_ERR(lower_dentry);
+		goto out;
+	}
+
+	ecryptfs_printk(1, KERN_NOTICE, "lower_dentry->d_name.name = [%s]\n",
+			lower_dentry->d_name.name);
+
+	/* This code is migrated from ecryptfs_open... 
+	 * there is no reason that this shouldn't just "work"
+	 * The flags and mode for the file passed into ecryptfs_open is:
+	 * f->f_flags = flags;
+	 * f->f_mode = ((flags+1) & O_ACCMODE) 
+	 *                      | FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE;
+	 * 
+	 * Where flags = O_CREAT | O_WRONLY | O_TRUNC &
+	 *      ( #if BITS_PER_LONG != 32 ; flags |= O_LARGEFILE; #endif )
+	 *
+	 * Code to initialize the file to be a valid ecryptfs file
+	 */
+	inode = ecryptfs_dentry->d_inode;
+	crypt_stats = &(ECRYPTFS_INODE_TO_PRIVATE(inode)->crypt_stats);
+	/* TODO: Initialization is done in alloc_inode, BUG if its not done */
+	if (!crypt_stats->struct_initialized)
+		BUG();
+	tlower_dentry = dget(lower_dentry);
+	if (!tlower_dentry) {
+		rc = -ENOMEM;
+		ecryptfs_printk(0, KERN_ERR, "Error dget'ing lower_dentry\n");
+		goto out;
+	}
+	lower_flags = ((O_CREAT | O_WRONLY | O_TRUNC) & O_ACCMODE) | O_RDWR;
+#if BITS_PER_LONG != 32
+	lower_flags |= O_LARGEFILE;
+#endif
+	lower_mnt = ECRYPTFS_SUPERBLOCK_TO_PRIVATE(inode->i_sb)->lower_mnt;
+	mntget(lower_mnt);
+	/* Corresponding fput() at end of func */
+	lower_file = dentry_open(tlower_dentry, lower_mnt, lower_flags);
+	if (IS_ERR(lower_file)) {
+		rc = PTR_ERR(lower_file);
+		ecryptfs_printk(0, KERN_ERR,
+				"Error opening dentry; rc = [%i]\n", rc);
+		goto out;
+	}
+	/* fput(lower_file) should handle the puts if we do this */
+	lower_file->f_dentry = tlower_dentry;
+	lower_file->f_vfsmnt = lower_mnt;
+	lower_inode = tlower_dentry->d_inode;
+	if (S_ISDIR(ecryptfs_dentry->d_inode->i_mode)) {
+		ecryptfs_printk(1, KERN_NOTICE, "This is a directory\n");
+		crypt_stats->encrypted = 0;
+		goto out_fput;
+	}
+	crypt_stats->new_file = 1;
+	ecryptfs_printk(1, KERN_NOTICE, "Initializing crypto context\n");
+	rc = ecryptfs_new_file_context(ecryptfs_dentry);	/* crypto.c */
+	if (rc) {
+		ecryptfs_printk(1, KERN_NOTICE, "Error creating new file "
+				"context\n");
+		goto out_fput;
+	}
+	rc = ecryptfs_write_headers(ecryptfs_dentry, lower_file);
+	if (rc) {
+		ecryptfs_printk(1, KERN_NOTICE, "Error writing headers\n");
+		goto out_fput;
+	}
+	rc = grow_file(ecryptfs_dentry, lower_file, inode, lower_inode);
+out_fput:
+	fput(lower_file);
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
+
+/**
+ * Creates a new file.
+ *
+ * @param dir The inode of the directory in which to create the file.
+ * @param dentry The eCryptfs dentry
+ * @param mode The mode of the new file.
+ * @param nd nameidata
+ * @return 0 on success; non-zero on error condition
+ */
+static int
+ecryptfs_create(struct inode *directory_inode, struct dentry *ecryptfs_dentry,
+		int mode, struct nameidata *nd)
+{
+	int rc;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; ecryptfs_dentry->d_name.name = "
+			"[%s], directory_inode=[%p], ecryptfs_dentry->d_parent"
+			"->d_inode=[%p], nd->dentry=[%p], nd->last.name=[%s], "
+			"ecryptfs_dentry=[%p]\n", ecryptfs_dentry->d_name.name,
+			directory_inode, ecryptfs_dentry->d_parent->d_inode,
+			nd->dentry, nd->last.name, ecryptfs_dentry);
+
+	rc = ecryptfs_do_create(directory_inode, ecryptfs_dentry, mode, nd);
+	if (unlikely(rc)) {
+		ecryptfs_printk(0, KERN_WARNING, "Failed to create file in"
+				"lower filesystem\n");
+		goto out;
+	}
+	/* At this point, a file exists on "disk", we need to make sure
+	 * that this on disk file is prepared to be an ecryptfs file */
+	rc = ecryptfs_initialize_file(ecryptfs_dentry);
+      out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
+
+/**
+ * Find a file on disk. If the file does not exist, then we'll add it to the
+ * dentry cache and continue on to read it from the disk.
+ *
+ * @param dir	 inode
+ * @param dentry dentry
+ * @param nd	 nameidata; may be NULL
+ */
+static struct dentry *ecryptfs_lookup(struct inode *dir, struct dentry *dentry,
+				      struct nameidata *nd)
+{
+	int err = 0;
+	struct dentry *lower_dir_dentry;
+	struct dentry *lower_dentry;
+	struct dentry *tlower_dentry = NULL;
+	char *encoded_name;
+	unsigned int encoded_namelen;
+	struct ecryptfs_crypt_stats *crypt_stats = NULL;
+	char *page_virt = NULL;
+	struct inode *lower_inode;
+	unsigned long long file_size;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; dir = [%p], dentry->d_name.nam"
+			"e = [%s], nd = [%p]\n", dir, dentry->d_name.name, nd);
+
+	lower_dir_dentry = ecryptfs_lower_dentry(dentry->d_parent);
+	dentry->d_op = &ecryptfs_dops;
+
+	/* Sanity Check: Make sure we can operate on the file */
+	if ((dentry->d_name.len == 1 && !strcmp(dentry->d_name.name, "."))
+	    || (dentry->d_name.len == 2 && !strcmp(dentry->d_name.name, "..")))
+		goto out_drop;
+
+	encoded_namelen = ecryptfs_encode_filename(dentry->d_name.name,
+						   dentry->d_name.len,
+						   &encoded_name,
+						   ECRYPTFS_SKIP_DOTS,
+						   crypt_stats);
+	if (encoded_namelen < 0) {
+		err = encoded_namelen;
+		goto out_drop;
+	}
+	/* TODO: pretty sure we need to do a dput(lower_dentry) to
+	 * counter */
+	ecryptfs_printk(1, KERN_NOTICE, "encoded_name = [%s]; encoded_namelen "
+			"= [%d]\n", encoded_name, encoded_namelen);
+	/* TODO: BUG: What to do on symlink to directory? */
+	lower_dentry = lookup_one_len(encoded_name, lower_dir_dentry,
+				      encoded_namelen - 1);
+	kfree(encoded_name);
+	if (IS_ERR(lower_dentry)) {
+		ecryptfs_printk(0, KERN_ERR, "ERR from lower_dentry\n");
+		err = PTR_ERR(lower_dentry);
+		goto out_drop;
+	}
+	ecryptfs_printk(1, KERN_NOTICE, "lower_dentry = [%p]; lower_dentry->"
+       		"d_name.name = [%s]\n", lower_dentry,
+		lower_dentry->d_name.name);
+	lower_inode = lower_dentry->d_inode;
+	ecryptfs_copy_attr_atime(dir, lower_dir_dentry->d_inode);
+
+	/* Sanity check */
+	ASSERT(atomic_read(&lower_dentry->d_count));
+
+	/* Private allocation */
+	ECRYPTFS_DENTRY_TO_PRIVATE_SM(dentry) =
+		kmem_cache_alloc(ecryptfs_dentry_info_cache, SLAB_KERNEL);
+	if (!ECRYPTFS_DENTRY_TO_PRIVATE_SM(dentry)) {
+		err = -ENOMEM;
+		ecryptfs_printk(0, KERN_ERR, "Out of memory whilst attempting "
+				"to allocate ecryptfs_dentry_info struct\n");
+		goto out_dput;
+	}
+
+	ECRYPTFS_DENTRY_TO_LOWER(dentry) = lower_dentry;
+	if (!lower_dentry->d_inode) {
+		/* We want to add because we couldn't find in lower */
+		d_add(dentry, NULL);
+		goto out;
+	}
+	err = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 1);
+	if (err) {
+		ecryptfs_printk(0, KERN_ERR, "Error interposing\n");
+		goto out_dput;
+	}
+	/* Do we want to just get a handle to the directory and its lower
+	 * and not abort with puts?
+	 */
+	if (S_ISDIR(lower_inode->i_mode)) {
+		ecryptfs_printk(1, KERN_NOTICE, "Is a directory; returning\n");
+		goto out;
+	}
+	if (S_ISLNK(lower_inode->i_mode)) {
+		ecryptfs_printk(1, KERN_NOTICE, "Is a symlink; returning\n");
+		goto out;
+	}
+	/* We have a NULL dentry, can we just skip over the read here? */
+	if (!nd) {
+		ecryptfs_printk(1, KERN_NOTICE, "We have a NULL nd, just leave"
+				"as we *think* we are about to unlink\n");
+		goto out;
+	}
+	tlower_dentry = dget(lower_dentry);
+	if (!tlower_dentry || IS_ERR(tlower_dentry)) {
+		err = -ENOMEM;
+		ecryptfs_printk(0, KERN_ERR, "Cannot dget lower_dentry\n");
+		goto out_dput;
+	}
+	/* Released in this function */
+	page_virt =
+	    (char *)kmem_cache_alloc(ecryptfs_header_cache_2,
+				     SLAB_USER);
+	if (!page_virt) {
+		err = -ENOMEM;
+		ecryptfs_printk(0, KERN_ERR,
+				"Cannot ecryptfs_kmalloc a page\n");
+		goto out_dput;
+	}
+
+	/* Use the file's "header" to determine if its an ecryptfs file */
+	err = ecryptfs_read_header_region(page_virt, tlower_dentry, nd);
+	/* Force default values if we haven't parsed the header */
+	crypt_stats = 
+		&(ECRYPTFS_INODE_TO_PRIVATE(dentry->d_inode)->crypt_stats);
+	if (!crypt_stats->policy_applied)
+		ecryptfs_set_default_sizes(crypt_stats);
+
+	if (err) {
+		err = 0;
+		ecryptfs_printk(1, KERN_WARNING, "Error reading header region;"
+				" assuming unencrypted\n");
+	} else {
+		if (!contains_ecryptfs_marker(page_virt
+					      + ECRYPTFS_FILE_SIZE_BYTES)) {
+			ecryptfs_printk(0, KERN_WARNING, "Underlying file "
+					"lacks recognizable eCryptfs marker\n");
+		}
+		memcpy(&file_size, page_virt, sizeof(file_size));
+		dentry->d_inode->i_size = file_size;
+	}
+	kmem_cache_free(ecryptfs_header_cache_2, page_virt);
+	goto out;
+
+out_dput:
+	dput(lower_dentry);
+	if (tlower_dentry)
+		dput(tlower_dentry);
+out_drop:
+	d_drop(dentry);
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n", err);
+	return ERR_PTR(err);
+}
+
+static int ecryptfs_link(struct dentry *old_dentry, struct inode *dir,
+			 struct dentry *new_dentry)
+{
+	int err;
+	struct dentry *lower_old_dentry;
+	struct dentry *lower_new_dentry;
+	struct dentry *lower_dir_dentry;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	lower_old_dentry = ecryptfs_lower_dentry(old_dentry);
+	lower_new_dentry = ecryptfs_lower_dentry(new_dentry);
+	dget(lower_old_dentry);
+	dget(lower_new_dentry);
+	lower_dir_dentry = lock_parent(lower_new_dentry);
+	err = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode,
+		       lower_new_dentry);
+	if (err || !lower_new_dentry->d_inode)
+		goto out_lock;
+	err = ecryptfs_interpose(lower_new_dentry, new_dentry, dir->i_sb, 0);
+	if (err)
+		goto out_lock;
+	ecryptfs_copy_attr_timesizes(dir, lower_new_dentry->d_inode);
+	old_dentry->d_inode->i_nlink =
+	    ECRYPTFS_INODE_TO_LOWER(old_dentry->d_inode)->i_nlink;
+out_lock:
+	unlock_dir(lower_dir_dentry);
+	dput(lower_new_dentry);
+	dput(lower_old_dentry);
+	if (!new_dentry->d_inode)
+		d_drop(new_dentry);
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n", err);
+	return err;
+}
+
+static int ecryptfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+	int rc = 0;
+	struct dentry *lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(dentry);
+	struct inode *lower_dir_inode = ECRYPTFS_INODE_TO_LOWER(dir);
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; dentry->d_name = [%s]\n",
+			dentry->d_name.name);
+	lock_parent(lower_dentry);
+	rc = vfs_unlink(lower_dir_inode, lower_dentry);
+	if (rc) {
+		ecryptfs_printk(1, KERN_ERR, "Error in vfs_unlink\n");
+		goto out_unlock;
+	}
+	ecryptfs_copy_attr_times(dir, lower_dir_inode);
+	dentry->d_inode->i_nlink = 
+		ECRYPTFS_INODE_TO_LOWER(dentry->d_inode)->i_nlink;
+        dentry->d_inode->i_ctime = dir->i_ctime;	
+out_unlock:	
+	unlock_parent(lower_dentry);
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]",rc);
+	return rc;
+}
+
+static int ecryptfs_symlink(struct inode *dir, struct dentry *dentry,
+			    const char *symname)
+{
+	int err;
+	struct dentry *lower_dentry;
+	struct dentry *lower_dir_dentry;
+	umode_t mode;
+	char *encoded_symname;
+	unsigned int encoded_symlen;
+	struct ecryptfs_crypt_stats *crypt_stats = NULL;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	lower_dentry = ecryptfs_lower_dentry(dentry);
+	dget(lower_dentry);
+	lower_dir_dentry = lock_parent(lower_dentry);
+	mode = S_IALLUGO;
+	encoded_symlen = ecryptfs_encode_filename(symname, strlen(symname),
+						  &encoded_symname,
+						  ECRYPTFS_DO_DOTS,
+						  crypt_stats);
+	if (encoded_symlen < 0) {
+		err = encoded_symlen;
+		goto out_lock;
+	}
+	err = vfs_symlink(lower_dir_dentry->d_inode, lower_dentry,
+			  encoded_symname, mode);
+	kfree(encoded_symname);
+	if (err || !lower_dentry->d_inode)
+		goto out_lock;
+	err = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0);
+	if (err)
+		goto out_lock;
+	ecryptfs_copy_attr_timesizes(dir, lower_dir_dentry->d_inode);
+out_lock:
+	unlock_dir(lower_dir_dentry);
+	dput(lower_dentry);
+	if (!dentry->d_inode)
+		d_drop(dentry);
+	return err;
+}
+
+static int ecryptfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+	int err;
+	struct dentry *lower_dentry;
+	struct dentry *lower_dir_dentry;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	lower_dentry = ecryptfs_lower_dentry(dentry);
+	lower_dir_dentry = lock_parent(lower_dentry);
+	err = vfs_mkdir(lower_dir_dentry->d_inode, lower_dentry, mode);
+	if (err || !lower_dentry->d_inode)
+		goto out;
+	err = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0);
+	if (err)
+		goto out;
+	ecryptfs_copy_attr_timesizes(dir, lower_dir_dentry->d_inode);
+	dir->i_nlink = lower_dir_dentry->d_inode->i_nlink;
+out:
+	unlock_dir(lower_dir_dentry);
+	if (!dentry->d_inode)
+		d_drop(dentry);
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n", err);
+	return err;
+}
+
+static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+	int err = 0;
+	struct dentry *tdentry = NULL;
+	struct dentry *lower_dentry;
+	struct dentry *tlower_dentry = NULL;
+	struct dentry *lower_dir_dentry;
+	struct dentry *tlower_dir_dentry = NULL;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	lower_dentry = ecryptfs_lower_dentry(dentry);
+	if (!(tdentry = dget(dentry))) {
+		err = -EINVAL;
+		ecryptfs_printk(0, KERN_ERR, "Error dget'ing dentry [%p]\n",
+				dentry);
+		goto out;
+	}
+	lower_dir_dentry = lock_parent(lower_dentry);
+	if (!(tlower_dentry = dget(lower_dentry))) {
+		err = -EINVAL;
+		ecryptfs_printk(0, KERN_ERR, "Error dget'ing lower_dentry "
+				"[%p]\n", lower_dentry);
+		goto out;
+	}
+	err = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry);
+	if (!err) {
+		d_delete(tlower_dentry);
+		tlower_dentry = NULL;
+	}
+	ecryptfs_copy_attr_times(dir, lower_dir_dentry->d_inode);
+	dir->i_nlink = lower_dir_dentry->d_inode->i_nlink;
+	unlock_dir(lower_dir_dentry);
+	if (!err)
+		d_drop(dentry);
+out:
+	if (tdentry)
+		dput(tdentry);
+	if (tlower_dentry)
+		dput(tlower_dentry);
+	if (tlower_dir_dentry)
+		dput(tlower_dir_dentry);
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n", err);
+	return err;
+}
+
+static int
+ecryptfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
+{
+	int err;
+	struct dentry *lower_dentry;
+	struct dentry *lower_dir_dentry;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	lower_dentry = ecryptfs_lower_dentry(dentry);
+	lower_dir_dentry = lock_parent(lower_dentry);
+	err = vfs_mknod(lower_dir_dentry->d_inode, lower_dentry, mode, dev);
+	if (err || !lower_dentry->d_inode)
+		goto out;
+	err = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0);
+	if (err)
+		goto out;
+	ecryptfs_copy_attr_timesizes(dir, lower_dir_dentry->d_inode);
+out:
+	unlock_dir(lower_dir_dentry);
+	if (!dentry->d_inode)
+		d_drop(dentry);
+	return err;
+}
+
+static int
+ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+		struct inode *new_dir, struct dentry *new_dentry)
+{
+	int err;
+	struct dentry *lower_old_dentry;
+	struct dentry *lower_new_dentry;
+	struct dentry *lower_old_dir_dentry;
+	struct dentry *lower_new_dir_dentry;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	lower_old_dentry = ecryptfs_lower_dentry(old_dentry);
+	lower_new_dentry = ecryptfs_lower_dentry(new_dentry);
+	dget(lower_old_dentry);
+	dget(lower_new_dentry);
+	lower_old_dir_dentry = dget_parent(lower_old_dentry);
+	lower_new_dir_dentry = dget_parent(lower_new_dentry);
+	lock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
+	err = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry,
+			 lower_new_dir_dentry->d_inode, lower_new_dentry);
+	if (err)
+		goto out_lock;
+	ecryptfs_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode);
+	if (new_dir != old_dir)
+		ecryptfs_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode);
+out_lock:
+	unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
+	dput(lower_new_dentry);
+	dput(lower_old_dentry);
+	return err;
+}
+
+static int
+ecryptfs_readlink(struct dentry *dentry, char __user * buf, int bufsiz)
+{
+	int err;
+	struct dentry *lower_dentry;
+	char *decoded_name;
+	char *lower_buf;
+	mm_segment_t old_fs;
+	struct ecryptfs_crypt_stats *crypt_stats;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	lower_dentry = ecryptfs_lower_dentry(dentry);
+	if (!lower_dentry->d_inode->i_op ||
+	    !lower_dentry->d_inode->i_op->readlink) {
+		err = -EINVAL;
+		goto out;
+	}
+	/* Released in this function */
+	lower_buf = kmalloc(bufsiz, GFP_KERNEL);
+	if (lower_buf == NULL) {
+		ecryptfs_printk(0, KERN_ERR, "Out of memory\n");
+		err = -ENOMEM;
+		goto out;
+	}
+	old_fs = get_fs();
+	set_fs(get_ds());
+	ecryptfs_printk(1, KERN_NOTICE, "Calling readlink w/ "
+			"lower_dentry->d_name.name = [%s]\n",
+			lower_dentry->d_name.name);
+	err = lower_dentry->d_inode->i_op->readlink(lower_dentry, 
+						    (char __user *)lower_buf,
+						    bufsiz);
+	set_fs(old_fs);
+	if (err >= 0) {
+		crypt_stats = NULL;
+		err = ecryptfs_decode_filename(lower_buf, err,
+					       &decoded_name,
+					       ECRYPTFS_DO_DOTS, crypt_stats);
+		if (err == -ENOMEM)
+			goto out_free_lower_buf;
+		if (err > 0) {
+			ecryptfs_printk(1, KERN_NOTICE, "Copying [%d] bytes "
+					"to userspace: [%*s]\n", err, err,
+					decoded_name);
+			if (copy_to_user(buf, decoded_name, err))
+				err = -EFAULT;
+		}
+		kfree(decoded_name);
+		ecryptfs_copy_attr_atime(dentry->d_inode,
+					 lower_dentry->d_inode);
+	}
+out_free_lower_buf:
+	kfree(lower_buf);
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n", err);
+	return err;
+}
+
+static void *ecryptfs_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+	char *buf;
+	int len = PAGE_SIZE, rc;
+	mm_segment_t old_fs;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; dentry->d_name.name = [%s]\n",
+			dentry->d_name.name);
+	/* Released in ecryptfs_put_link(); only release here on error */
+	buf = kmalloc(len, GFP_KERNEL);
+	if (!buf) {
+		rc = -ENOMEM;
+		goto out;
+	}
+	old_fs = get_fs();
+	set_fs(get_ds());
+	ecryptfs_printk(1, KERN_NOTICE, "Calling readlink w/ "
+			"dentry->d_name.name = [%s]\n", dentry->d_name.name);
+	rc = dentry->d_inode->i_op->readlink(dentry, (char __user *)buf, len);
+	buf[rc] = '\0';
+	set_fs(old_fs);
+	if (rc < 0)
+		goto out_free;
+	rc = 0;
+	nd_set_link(nd, buf);
+	goto out;
+out_free:
+	kfree(buf);
+out:
+	return ERR_PTR(rc);
+}
+
+static inline void
+ecryptfs_put_link(struct dentry *dentry, struct nameidata *nd, void *ptr)
+{
+	/* Free the char* */
+	kfree(nd_get_link(nd));
+}
+
+/**
+ * Calculate the requried size of the lower file based on the specified size
+ * of the upper file. This calculation is based on the number of headers in
+ * the underlying file, the number of records (IV/HMAC/IV+HMAC) per page
+ * that can be stored, and the extent size of the underlying file (page size).
+ *
+ * @param crypt_stats crypt_stats associated with file
+ * @param upper_size size of the upper file
+ * @return calculated size of the lower file.
+ */
+static loff_t
+upper_size_to_lower_size(struct ecryptfs_crypt_stats *crypt_stats,
+			 loff_t upper_size)
+{
+	int upper_idx = 0;
+	int lower_idx;
+	loff_t lower_size = upper_size;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; upper_size = [%lld]\n",
+			upper_size);
+	/* TODO: Does this need to be based on the extent size? */
+	if (likely(upper_size > 0))
+		upper_idx = (upper_size - 1) >> PAGE_CACHE_SHIFT;
+	else if (!upper_size)
+		upper_idx = 0;
+	else /* Sanity check, size shouldn't be negative */
+		BUG();
+	lower_idx = ecryptfs_pg_idx_to_lwr_pg_idx(crypt_stats, upper_idx);
+	lower_size = ((lower_idx + 1) << PAGE_CACHE_SHIFT);
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; lower_size = [%lld]\n",
+			lower_size);
+	return lower_size;
+}
+
+/**
+ * Function to handle truncations modifying the size of the file. Note
+ * that the file sizes are interpolated. When expanding, we are simply
+ * writing strings of 0's out. When truncating, we need to modify the
+ * underlying file size accordingly to the page index interpolations.
+ *
+ * N.B. No claims are made about integrety of the encrypted data when
+ * 	shrinking a file. (But its probably going to be lost)
+ *
+ * TODO: Support holes. Should we also be supporting preservation of
+ * 	 the encrypted data?
+ *
+ * @param dentry	The ecryptfs layer dentry
+ * @param new_length	The length to expand the file to
+ * @return		Zero on success; non-zero otherwise
+ */
+int ecryptfs_truncate(struct dentry *dentry, loff_t new_length)
+{
+	int rc = 0;
+	struct inode *inode = dentry->d_inode;
+	struct dentry *lower_dentry;
+	struct file fake_ecryptfs_file, *lower_file = NULL;
+	struct ecryptfs_crypt_stats *crypt_stats;
+	loff_t i_size = i_size_read(inode);
+	loff_t lower_size_before_truncate;
+	loff_t lower_size_after_truncate;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; dentry = [%p], new_length = "
+			"[%lld], i_size_read(inode) = [%lld]\n",
+			dentry, new_length, i_size);
+	/* Sanity checks */
+	if (unlikely((new_length == i_size)))
+		goto out;
+	crypt_stats = &ECRYPTFS_INODE_TO_PRIVATE(dentry->d_inode)->crypt_stats;
+	if (unlikely(!crypt_stats)) {
+		ecryptfs_printk(0, KERN_ERR, "NULL crypt_stats on dentry with "
+				"d_name.name = [%s]\n", dentry->d_name.name);
+		rc = -EINVAL;
+		goto out;
+	}
+
+	/* Set up a fake ecryptfs file, this is used to interface with the file
+	 * in the underlying filesystem so that the truncation has an effect
+	 * there as well. */
+	memset(&fake_ecryptfs_file, 0, sizeof(struct file));
+	fake_ecryptfs_file.f_dentry = dentry;
+	/* Released at out_free: */
+	ECRYPTFS_FILE_TO_PRIVATE_SM(&fake_ecryptfs_file) =
+		kmem_cache_alloc(ecryptfs_file_info_cache, SLAB_KERNEL);
+	if (unlikely(!ECRYPTFS_FILE_TO_PRIVATE(&fake_ecryptfs_file))) {
+		rc = -ENOMEM;
+		goto out;
+	}
+	lower_dentry = ecryptfs_lower_dentry(dentry);
+	/* This dget & mntget is released through fput at out_fput: */
+	dget(lower_dentry);
+	mntget(ECRYPTFS_SUPERBLOCK_TO_PRIVATE(inode->i_sb)->lower_mnt);
+	lower_file = dentry_open(
+		lower_dentry,
+		ECRYPTFS_SUPERBLOCK_TO_PRIVATE(inode->i_sb)->lower_mnt, O_RDWR);
+	if (unlikely(IS_ERR(lower_file))) {
+		rc = PTR_ERR(lower_file);
+		goto out_free;
+	}
+	ECRYPTFS_FILE_TO_LOWER(&fake_ecryptfs_file) = lower_file;
+
+	/* Switch on growing or shrinking file */
+	if (new_length > i_size) {
+		rc = ecryptfs_fill_zeros(&fake_ecryptfs_file, new_length);
+		if (rc) {
+			ecryptfs_printk(0, KERN_ERR,
+					"Problem with fill_zeros\n");
+			goto out_fput;
+		}
+		i_size_write(inode, new_length);
+		rc = ecryptfs_write_inode_size_to_header(lower_file,
+							 lower_dentry->d_inode,
+							 inode);
+		if (rc) {
+			ecryptfs_printk(0, KERN_ERR,
+					"Problem with ecryptfs_write"
+					"_inode_size\n");
+			goto out_fput;
+		}
+	} else {		/* new_length < i_size_read(inode) */
+		vmtruncate(inode, new_length);
+		ecryptfs_write_inode_size_to_header(lower_file,
+						    lower_dentry->d_inode,
+						    inode);
+		/* We are reducing the size of the ecryptfs file, and need to
+		 * know if we need to reduce the size of the lower file. */
+		lower_size_before_truncate =
+		    upper_size_to_lower_size(crypt_stats, i_size);
+		lower_size_after_truncate =
+		    upper_size_to_lower_size(crypt_stats, new_length);
+		if (lower_size_after_truncate < lower_size_before_truncate)
+			vmtruncate(lower_dentry->d_inode,
+				   lower_size_after_truncate);
+	}
+	/* Update the access times */
+	lower_dentry->d_inode->i_mtime = CURRENT_TIME;
+	lower_dentry->d_inode->i_ctime = CURRENT_TIME;
+	mark_inode_dirty_sync(inode);
+out_fput:
+	/* TODO: Determine which of these need to really be called.
+	 * filp_close() will call fput(), but do we need to do the extra work
+	 * that filp_close() provides?
+	 if (lower_file)
+	 filp_close(lower_file, NULL);
+	 */
+	fput(lower_file);
+out_free:
+	if (ECRYPTFS_FILE_TO_PRIVATE(&fake_ecryptfs_file))
+		kmem_cache_free(ecryptfs_file_info_cache,
+				ECRYPTFS_FILE_TO_PRIVATE(&fake_ecryptfs_file));
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
+
+static int
+ecryptfs_permission(struct inode *inode, int mask, struct nameidata *nd)
+{
+	struct inode *lower_inode;
+	int rc = 0;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; inode = [%p], mask=[%d], nd ="
+			"[%p]\n", inode, mask, nd);
+	lower_inode = ECRYPTFS_INODE_TO_LOWER(inode);
+	if (nd)
+		ecryptfs_printk(1, KERN_NOTICE, "nd->dentry = [%p]\n",
+				nd->dentry);
+	rc = permission(lower_inode, mask, nd);
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
+
+/**
+ * Updates the metadata of an inode. If the update is to the size
+ * i.e. truncation, then ecryptfs_truncate will handle the size modification
+ * of both the ecryptfs inode and the lower inode.
+ * 
+ * All other metadata changes will be passed right to the lower filesystem,
+ * and we will just update our inode to look like the lower.
+ *
+ * @param dentry	dentry handle to the inode to modify
+ * @param ia		structure with flags of what to change and values
+ */
+static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia)
+{
+	int err = 0;
+	struct dentry *lower_dentry;
+	struct inode *inode;
+	struct inode *lower_inode;
+	struct ecryptfs_crypt_stats *crypt_stats;
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; dentry->d_name.name = [%s]\n",
+			dentry->d_name.name);
+	crypt_stats = &ECRYPTFS_INODE_TO_PRIVATE(dentry->d_inode)->crypt_stats;
+	lower_dentry = ecryptfs_lower_dentry(dentry);
+	inode = dentry->d_inode;
+	lower_inode = ECRYPTFS_INODE_TO_LOWER(inode);
+	if (ia->ia_valid & ATTR_SIZE) {
+		ecryptfs_printk(1, KERN_NOTICE,
+				"ia->ia_valid = [0x%x] ATTR_SIZE" " = [0x%x]\n",
+				ia->ia_valid, ATTR_SIZE);
+		err = ecryptfs_truncate(dentry, ia->ia_size);
+		/* ecryptfs_truncate handles resizing of the lower file */
+		ia->ia_valid &= ~ATTR_SIZE;
+		ecryptfs_printk(1, KERN_NOTICE, "ia->ia_valid = [%x]\n",
+				ia->ia_valid);
+		if (err < 0)
+			goto out;
+	}
+	err = notify_change(lower_dentry, ia);
+out:
+	ecryptfs_copy_attr_all(inode, lower_inode);
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n");
+	return err;
+}
+
+struct inode_operations ecryptfs_symlink_iops = {
+	.readlink = ecryptfs_readlink,
+	.follow_link = ecryptfs_follow_link,
+	.put_link = ecryptfs_put_link,
+	.permission = ecryptfs_permission,
+	.setattr = ecryptfs_setattr,
+};
+
+struct inode_operations ecryptfs_dir_iops = {
+	.create = ecryptfs_create,
+	.lookup = ecryptfs_lookup,
+	.link = ecryptfs_link,
+	.unlink = ecryptfs_unlink,
+	.symlink = ecryptfs_symlink,
+	.mkdir = ecryptfs_mkdir,
+	.rmdir = ecryptfs_rmdir,
+	.mknod = ecryptfs_mknod,
+	.rename = ecryptfs_rename,
+	.permission = ecryptfs_permission,
+	.setattr = ecryptfs_setattr,
+};
+
+struct inode_operations ecryptfs_main_iops = {
+	.permission = ecryptfs_permission,
+	.setattr = ecryptfs_setattr,
+};
#26 lore raw

PATCH 10/12 Mmap operations

In reply to Phillip Hellewell

eCryptfs mmap operations. The bulk encryption and decryption takes place here.

---

 mmap.c | 1153 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 1153 insertions(+)
fs/ecryptfs/mmap.c +1153 -0
@@ -0,0 +1,1153 @@
+/**
+ * eCryptfs: Linux filesystem encryption layer
+ * This is where eCryptfs handles the bulk encryption and decryption,
+ * with upper-lower file index interpolations.
+ *
+ * Copyright (c) 1997-2003 Erez Zadok
+ * Copyright (c) 2001-2003 Stony Brook University
+ * Copyright (c) 2005 International Business Machines Corp.
+ *   Author(s): Michael A. Halcrow <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <linux/pagemap.h>
+#include <linux/writeback.h>
+#include <linux/page-flags.h>
+#include <linux/mount.h>
+#include <linux/file.h>
+#include <linux/crypto.h>
+#include <asm/scatterlist.h>
+#include "ecryptfs_kernel.h"
+
+static int ecryptfs_commit_write(struct file *file, struct page *page,
+				 unsigned from, unsigned to);
+static int ecryptfs_prepare_write(struct file *file, struct page *page,
+				  unsigned from, unsigned to);
+
+kmem_cache_t *ecryptfs_lower_page_cache;
+
+static inline pgoff_t
+record_idx(struct ecryptfs_crypt_stats *crypt_stats, pgoff_t idx)
+{
+	return (idx % crypt_stats->records_per_page);
+}
+
+static inline pgoff_t
+last_records_page_idx(struct ecryptfs_crypt_stats *crypt_stats, pgoff_t idx)
+{
+	pgoff_t lower_data_idx;
+	pgoff_t lower_record_idx;
+
+	lower_data_idx = ecryptfs_pg_idx_to_lwr_pg_idx(crypt_stats, idx);
+	lower_record_idx = record_idx(crypt_stats, idx);
+	return (lower_data_idx - lower_record_idx - 1);
+}
+
+/**
+ * Get one page from cache or lower f/s, return error otherwise.
+ *
+ * @return Unlocked and up-to-date page (if ok), with increased
+ *         refcnt.
+ */
+static struct page *ecryptfs_get1page(struct file *file, int index)
+{
+	struct page *page;
+	struct dentry *dentry;
+	struct inode *inode;
+	struct address_space *mapping;
+	int rc;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	dentry = file->f_dentry;
+	inode = dentry->d_inode;
+	mapping = inode->i_mapping;
+	if (index < 0) {
+		ecryptfs_printk(1, KERN_ERR, "BUG: index=%d\n", index);
+		page = ERR_PTR(-EIO);
+		goto out;
+	}
+	page = read_cache_page(mapping, index,
+			       (filler_t *) mapping->a_ops->readpage,
+			       (void *)file);
+	if (IS_ERR(page))
+		goto out;
+	wait_on_page_locked(page);
+	if (!PageUptodate(page)) {
+		lock_page(page);
+		rc = mapping->a_ops->readpage(file, page);
+		if (rc) {
+			page = ERR_PTR(rc);
+			goto out;
+		}
+		wait_on_page_locked(page);
+		if (!PageUptodate(page)) {
+			page = ERR_PTR(-EIO);
+			goto out;
+		}
+	}
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+	return page;
+}
+
+/**
+ * Write a specified number of zero's to a page.
+ * 
+ * N.B. (start + num_zeros) _must_ be less than or equal to PAGE_CACHE_SIZE
+ *
+ * @param file		The ecryptfs file
+ * @param index		The index in which we are writing
+ * @param start		The position after the last block of data
+ * @param num_zeros	The number of zeros to write
+ */
+static
+int write_zeros(struct file *file, pgoff_t index, int start, int num_zeros)
+{
+	int rc = 0;
+	struct page *tmp_page;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; index = [%lu], start offset = "
+			"[%d] num_zeros = [%d]\n", index, start, num_zeros);
+	tmp_page = ecryptfs_get1page(file, index);
+	if (IS_ERR(tmp_page)) {
+		ecryptfs_printk(0, KERN_ERR, "Error getting page at index "
+				"[%lu]\n", index);
+		rc = PTR_ERR(tmp_page);
+		goto out;
+	}
+	kmap(tmp_page);
+	memset(((char *)page_address(tmp_page) + start), 0, num_zeros);
+	rc = ecryptfs_prepare_write(file, tmp_page, start, start + num_zeros);
+	if (rc) {
+		ecryptfs_printk(0, KERN_ERR, "Error preparing to write zero's "
+				"to remainder of page at index [%lu]\n", index);
+		kunmap(tmp_page);
+		page_cache_release(tmp_page);
+		goto out;
+	}
+	rc = ecryptfs_commit_write(file, tmp_page, start, start + num_zeros);
+	if (rc < 0) {
+		ecryptfs_printk(0, KERN_ERR, "Error attempting to write zero's "
+				"to remainder of page at index [%lu]\n", index);
+		kunmap(tmp_page);
+		page_cache_release(tmp_page);
+		goto out;
+	}
+	rc = 0;
+	kunmap(tmp_page);
+	page_cache_release(tmp_page);
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+	return rc;
+}
+
+/**
+ * Function for handling creation of holes when lseek-ing past the end
+ * of the file and then writing some data.
+ *
+ * N.B.
+ * This function does NOT support shrinking, only growing a file.
+ * The code _will_ BUG() if this requirement is not met.
+ * The new_length _myust_ be greater than the current length.
+ *
+ * @param file	     The ecryptfs file
+ * @param new_length The new length of the data in the underlying file;
+ * 		     everything between the prior end of the file and the new
+ * 		     end of the file will be filled with zero's.
+ * 		     N.B. new_length must be greater than current length
+ * @return	     Zero on success; non-zero otherwise 
+ */
+int ecryptfs_fill_zeros(struct file *file, loff_t new_length)
+{
+	int rc = 0;
+	struct dentry *dentry = file->f_dentry;
+	struct inode *inode = dentry->d_inode;
+	pgoff_t old_end_page_index = 0;
+	pgoff_t index = old_end_page_index;
+	int old_end_pos_in_page = -1;
+	pgoff_t new_end_page_index;
+	int new_end_pos_in_page;
+	loff_t cur_length = i_size_read(inode);
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; inode->i_size = [%llu]; "
+			"new_length = [%llu]\n", cur_length, new_length);
+	/* Sanity check */
+	if (cur_length >= new_length) {
+		ecryptfs_printk(0, KERN_ERR, "Called with new_length less than "
+				"or equal to the current length, this is "
+				"against function restrictions!\n");
+		BUG();
+	}
+	/* N.B. Notes on index calculations
+	 * It is important to note that the size of the file will always be 1
+	 * greater than the last block of data's position added to the data's
+	 * index shifted by the page cache size.
+	 *      size = index * PAGE_CACHE_SIZE + pos + 1
+	 *
+	 * Example, a file with 1 block of data in the 0th index at the 0th
+	 * position will be a file of size 1. Likewise, a file of 100 blocks
+	 * of data will have it's last block in the 99th position.
+	 *
+	 * Therefore, a file of size page with PAGE_CACHE_SIZE blocks of data
+	 * will have its last block of data in the (PAGE_CACHE_SIZE-1)th 
+	 * position, in the 0th index. A file with (PAGE_CACHE_SIZE + 1) blocks
+	 * of data will have its last block of data in the 0th position of the
+	 * 1st index. This calculation can be shown as:
+	 *      position = (size - 1) & ~PAGE_CACHE_MASK 
+	 *      index    =  (size - 1) >> PAGE_CACHE_SHIFT
+	 * 
+	 * Example:
+	 * PAGE_CACHE_SHIFT = 12
+	 * PAGE_CACHE_SIZE = (1UL << PAGE_CACHE_SHIFT) [or 4096]
+	 * PAGE_CACHE_MASK = (~(PAGE_CACHE_SIZE-1)) [or ~(4096-1)]
+	 *
+	 * A size of 4095 yields:
+	 *      position = (4095 - 1) & ~PAGE_CACHE_MASK => 4094
+	 *      index    = (4095 - 1) >> 12              => 0
+	 *
+	 * A size of 4096 yields:
+	 *      position = (4096 - 1) & ~PAGE_CACHE_MASK => 4095
+	 *      index    = (4096 - 1) >> 12              => 0
+	 *
+	 * A size of 4097 yields:
+	 *      position = (4097 - 1) & ~PAGE_CACHE_MASK => 0
+	 *      index    = (4097 - 1) >> 12              => 1
+	 *      
+	 * From these examples, you can see that the logic that a
+	 * file's last block of data is at the (size - 1)th position
+	 * follows.
+	 *
+	 * A 0-length file is by this logic, undefined if positions
+	 * and indicies are required to be positive integers. However,
+	 * this is intuative, as a 0-length file has no data which to
+	 * give a position and index to.  However, to make our
+	 * calculations work, we will use the default value of -1 if
+	 * the size is 0. The following will apply:
+	 *      old size = 0
+	 *      old position = -1
+	 *      old index = 0
+	 *      new size = 1
+	 *      new position = 0
+	 *      new index = 0
+	 *
+	 *      write_zeros(file,0,(-1+1) => 0, ((size-1) - (-1)) => size)
+	 *
+	 * Therefore, if the current size of the file is 0, then
+	 * setting our index and position to -1 will result in valid
+	 * calculations for the new index and position for the new
+	 * size.
+	 */
+	if (cur_length != 0) {
+		index = old_end_page_index =
+		    ((cur_length - 1) >> PAGE_CACHE_SHIFT);
+		old_end_pos_in_page = ((cur_length - 1) & ~PAGE_CACHE_MASK);
+	}
+	new_end_page_index = ((new_length - 1) >> PAGE_CACHE_SHIFT);
+	new_end_pos_in_page = ((new_length - 1) & ~PAGE_CACHE_MASK);
+
+	ecryptfs_printk(1, KERN_NOTICE, "old_end_page_index = [%lu]; "
+			"old_end_pos_in_page = [%d]; "
+			"new_end_page_index = [%lu]; "
+			"new_end_pos_in_page = [%d]\n",
+			old_end_page_index, old_end_pos_in_page,
+			new_end_page_index, new_end_pos_in_page);
+	if (old_end_page_index == new_end_page_index) {
+		/* Start and end are in the same page; we just need to
+		 * set a portion of the existing page to zero's */
+		rc = write_zeros(file, index, (old_end_pos_in_page + 1),
+				 (new_end_pos_in_page - old_end_pos_in_page));
+		if (rc)
+			ecryptfs_printk(0, KERN_ERR, "write_zeros(file=[%p], "
+					"index=[%lu], old_end_pos_in_page=[d], "
+					"(PAGE_CACHE_SIZE - new_end_pos_in_page"
+					"=[%d]"
+					")=[d]) returned [%d]\n", file, index,
+					old_end_pos_in_page,
+					new_end_pos_in_page,
+					(PAGE_CACHE_SIZE - new_end_pos_in_page),
+					rc);
+		goto out;
+	}
+	/* Else, new position is outside of the current index.
+	 * Fill remainder of current page with zeros.
+	 * For each page after that page, will entire page with zeros.
+	 * Upon reaching new last page, write as many zeros as required
+	 * to fullfil the new size. */
+	/* Fill the remainder of the previous last page with zeros */
+	rc = write_zeros(file, index, (old_end_pos_in_page + 1),
+			 ((PAGE_CACHE_SIZE - 1) - old_end_pos_in_page));
+	if (rc) {
+		ecryptfs_printk(0, KERN_ERR, "write_zeros(file=[%p], "
+				"index=[%lu], old_end_pos_in_page=[d], "
+				"(PAGE_CACHE_SIZE - old_end_pos_in_page)=[d]) "
+				"returned [%d]\n", file, index,
+				old_end_pos_in_page,
+				(PAGE_CACHE_SIZE - old_end_pos_in_page), rc);
+		goto out;
+	}
+	index++;
+	while (index < new_end_page_index) {
+		/* Fill all intermediate pages with zeros */
+		rc = write_zeros(file, index, 0, PAGE_CACHE_SIZE);
+		if (rc) {
+			ecryptfs_printk(0, KERN_ERR, "write_zeros(file=[%p], "
+					"index=[%lu], old_end_pos_in_page=[d], "
+					"(PAGE_CACHE_SIZE - new_end_pos_in_page"
+					"=[%d]"
+					")=[d]) returned [%d]\n", file, index,
+					old_end_pos_in_page,
+					new_end_pos_in_page,
+					(PAGE_CACHE_SIZE - new_end_pos_in_page),
+					rc);
+			goto out;
+		}
+		index++;
+	}
+	/* Fill the portion at the beginning of the last new page with
+	 * zero's */
+	rc = write_zeros(file, index, 0, (new_end_pos_in_page + 1));
+	if (rc) {
+		ecryptfs_printk(0, KERN_ERR, "write_zeros(file="
+				"[%p], index=[%lu], 0, "
+				"new_end_pos_in_page=[%d]"
+				"returned [%d]\n", file, index,
+				new_end_pos_in_page, rc);
+		goto out;
+	}
+out:
+	ecryptfs_printk((rc == 0 ? 1 : 0), KERN_NOTICE, "Exit; rc = [%d]\n",
+			rc);
+	return rc;
+}
+
+/**
+ * @param lower_file Can be NULL
+ * @return Zero on success
+ */
+static int 
+ecryptfs_read_rotate_write_iv(char *iv, struct inode *inode,
+			      int lower_iv_idx, struct file *lower_file, 
+			      struct page *page)
+{
+	int rc = 0;
+	pgoff_t records_page_index;
+	struct ecryptfs_crypt_stats *crypt_stats;
+	struct page *records_page;
+	char *records_virt;
+	int lower_file_needs_fput = 0;
+	struct address_space_operations *lower_a_ops;
+	struct inode *lower_inode;
+
+	crypt_stats = &(ECRYPTFS_INODE_TO_PRIVATE(inode)->crypt_stats);
+	lower_inode = ECRYPTFS_INODE_TO_LOWER(inode);
+ 	lower_a_ops = lower_inode->i_mapping->a_ops;
+	records_page_index = last_records_page_idx(crypt_stats, page->index);
+	ecryptfs_printk(1, KERN_NOTICE, "records_page_index = [%lu]\n",
+			records_page_index);
+	records_page = grab_cache_page(lower_inode->i_mapping,
+				       records_page_index);
+	if (!records_page) {
+                        ecryptfs_printk(0, KERN_ERR, "records_page == NULL "
+                                        "after grab_cache_page at index [%lu]"
+                                        "\n", records_page_index);
+                        rc = -EIO;
+                        goto out;
+	}
+	/* TODO: Assume encrypted only for version 0.1 */
+	ecryptfs_printk(1, KERN_NOTICE, "lower_iv_idx = [%d]\n", lower_iv_idx);
+	records_virt = kmap(records_page);
+	if (!records_virt) {
+		rc = -ENOMEM;
+		ecryptfs_printk(1, KERN_NOTICE, "Error in kmap\n");
+		goto out_unlock_and_release;
+	}
+	if (!lower_file) {
+		struct dentry *lower_dentry;
+		struct vfsmount *lower_mnt;
+
+		if (!lower_inode->i_dentry.next) {
+			rc = -EINVAL;
+			ecryptfs_printk(1, KERN_NOTICE, "No dentry for "
+					"lower_inode\n");
+			goto out_unmap;
+		}
+		lower_dentry = list_entry(lower_inode->i_dentry.next, 
+					  struct dentry, d_alias);
+		mntget(ECRYPTFS_SUPERBLOCK_TO_PRIVATE(inode->i_sb)->lower_mnt);
+		lower_mnt =
+			ECRYPTFS_SUPERBLOCK_TO_PRIVATE(inode->i_sb)->lower_mnt;
+		lower_file = dentry_open(lower_dentry, lower_mnt, FMODE_READ);
+		if (IS_ERR(lower_file)) {
+			rc = PTR_ERR(lower_file);
+			ecryptfs_printk(0, KERN_ERR,
+					"Error opening dentry; rc = [%i]\n",
+					rc);
+			mntput(ECRYPTFS_SUPERBLOCK_TO_PRIVATE(inode->i_sb)->lower_mnt);
+			goto out_unmap;
+		}
+		lower_file_needs_fput = 1;
+	}
+	rc = lower_a_ops->prepare_write(lower_file, records_page, lower_iv_idx,
+					(lower_iv_idx + crypt_stats->iv_bytes));
+	if (rc) {
+		ecryptfs_printk(0, KERN_ERR, "Error in lower prepare_write() "
+				"call: rc = [%d]\n", rc);
+		goto out_unmap;
+	}
+	down(&crypt_stats->iv_sem);
+	ecryptfs_rotate_iv(crypt_stats->iv);
+	memcpy(iv, crypt_stats->iv, crypt_stats->iv_bytes);
+	up(&crypt_stats->iv_sem);
+	memcpy(records_virt + lower_iv_idx, crypt_stats->iv,
+	       crypt_stats->iv_bytes);
+	rc = lower_a_ops->commit_write(lower_file, records_page, lower_iv_idx, 
+				       (lower_iv_idx + crypt_stats->iv_bytes));
+	if (rc) {
+		ecryptfs_printk(0, KERN_ERR, "Error in lower commit_write() "
+				"call: rc = [%d]\n", rc);
+		goto out_unmap;
+	}
+	lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME;
+	ecryptfs_printk(1, KERN_NOTICE, "Unlocking page with index = "
+			"[%lu]\n", records_page->index);
+out_unmap:
+	kunmap(records_page);
+out_unlock_and_release:
+	unlock_page(records_page);
+	page_cache_release(records_page);
+	ecryptfs_printk(1, KERN_NOTICE, "After committing IV write, "
+			"lower_inode->i_blocks = [%lu]\n",
+			lower_inode->i_blocks);	
+out:
+	if (lower_file_needs_fput)
+		fput(lower_file);
+	return rc;
+}
+
+/**
+ * @return Zero on success; negative on error
+ */
+static int encrypt_page(struct ecryptfs_crypt_stats *crypt_stats,
+			struct page *page, struct page *lower_page, char *iv)
+{
+	int rc = 0;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Calling do_encrypt_page()\n");
+	ecryptfs_printk(1, KERN_NOTICE, "Encrypting page with iv:\n");
+	if (unlikely(ecryptfs_verbosity > 0))
+		ecryptfs_dump_hex(iv, crypt_stats->iv_bytes);
+	ecryptfs_printk(1, KERN_NOTICE, "First 8 bytes before "
+			"encryption:\n");
+	if (ecryptfs_verbosity > 0)
+		ecryptfs_dump_hex((char *)page_address(page), 8);
+	rc = do_encrypt_page_offset(crypt_stats, lower_page, 0,
+				    page, 0, crypt_stats->extent_size, iv);
+	ecryptfs_printk(1, KERN_NOTICE, "Encrypted [%d] bytes\n", rc);
+	ecryptfs_printk(1, KERN_NOTICE, "First 8 bytes after " "encryption:\n");
+	if (ecryptfs_verbosity > 0)
+		ecryptfs_dump_hex((char *)page_address(lower_page), 8);
+	if (rc > 0)
+		rc = 0;
+	return rc;
+}
+
+/**
+ * @param page Page that is locked before this call is made
+ */
+static int ecryptfs_writepage(struct page *page, struct writeback_control *wbc)
+{
+	int rc = -EIO;
+	int err = 0;
+	unsigned long lower_index;
+	struct inode *inode;
+	struct inode *lower_inode;
+	struct page *lower_page;
+	char *kaddr, *lower_kaddr;
+	struct ecryptfs_crypt_stats *crypt_stats;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; page->index = [%ld]; "
+			"page->mapping->host = [%p]\n", page->index,
+			page->mapping->host);
+	inode = page->mapping->host;
+	crypt_stats = &(ECRYPTFS_INODE_TO_PRIVATE(inode)->crypt_stats);
+	lower_inode = ECRYPTFS_INODE_TO_LOWER(inode);
+	lower_index = ecryptfs_pg_idx_to_lwr_pg_idx(crypt_stats, page->index);
+	ecryptfs_printk(1, KERN_NOTICE, "Grab lower_idx = [%ld]\n",
+			lower_index);
+	lower_page = grab_cache_page(lower_inode->i_mapping, lower_index);
+	if (!lower_page) {
+		rc = -ENOMEM;
+		goto out;
+	}
+	kaddr = (char *)kmap(page);
+	if (!kaddr) {
+		rc = -ENOMEM;
+		goto out;
+	}
+	lower_kaddr = (char *)kmap(lower_page);
+	if (!lower_kaddr) {
+		rc = -ENOMEM;
+		goto out;
+	}
+	if (crypt_stats->encrypted) {
+		char iv[ECRYPTFS_MAX_IV_BYTES];
+		int record_byte_offset;
+		/* TODO: HMAC: Include HMAC bytes in the record size */
+		record_byte_offset = (record_idx(crypt_stats, page->index)
+				      * crypt_stats->iv_bytes);
+		rc = ecryptfs_read_rotate_write_iv(iv, inode,
+						   record_byte_offset, NULL,
+						   page);
+		if (rc) {
+			ecryptfs_printk(0, KERN_ERR, "Error rotating "
+					"IV; write failure. Assuming "
+					"IV page corruption; writing "
+					"0's to associated data extent"
+					".\n");
+			memset(lower_kaddr, 0, crypt_stats->extent_size);
+			err = -EIO;
+			goto do_lower_write;
+		}
+		ecryptfs_printk(1, KERN_NOTICE, "Encrypting page with iv:\n");
+		if (unlikely(ecryptfs_verbosity > 0))
+			ecryptfs_dump_hex(iv, crypt_stats->iv_bytes);
+		err = encrypt_page(crypt_stats, page, lower_page, iv);
+		if (err)
+			ecryptfs_printk(0, KERN_WARNING, "Error encrypting "
+					"page (upper index [%llu])\n", 
+					page->index);
+	} else
+		memcpy(lower_kaddr, kaddr, crypt_stats->extent_size);
+do_lower_write:
+	kunmap(page);
+	kunmap(lower_page);
+	rc = lower_inode->i_mapping->a_ops->writepage(lower_page, wbc);
+	if (rc)
+		ecryptfs_printk(0, KERN_ERR, "Error calling lower writepage(); "
+				"rc = [%d]\n", rc);
+	lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME;
+	page_cache_release(lower_page);
+	if (rc)
+		ClearPageUptodate(page);
+	else
+		SetPageUptodate(page);
+	unlock_page(page);
+out:
+	if (err)
+		rc = err;
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
+
+/**
+ * Reads the data from the lower file file at index lower_page_index
+ * and copies that data into page.
+ *
+ * @param page	Page to fill
+ * @param lower_page_index Index of the page in the lower file to get
+ */
+static int ecryptfs_do_readpage(struct file *file, struct page *page,
+				pgoff_t lower_page_index)
+{
+	int rc = -EIO;
+	struct dentry *dentry;
+	struct file *lower_file;
+	struct dentry *lower_dentry;
+	struct inode *inode;
+	struct inode *lower_inode;
+	char *page_data;
+	struct page *lower_page = NULL;
+	char *lower_page_data;
+	struct address_space_operations *lower_a_ops;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; lower_page_index = [%lu]\n",
+			lower_page_index);
+	dentry = file->f_dentry;
+	if (NULL == ECRYPTFS_FILE_TO_PRIVATE_SM(file)) {
+		rc = -ENOENT;
+		ecryptfs_printk(0, KERN_ERR, "No lower file info\n");
+		goto out;
+	}
+	lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+	lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(dentry);
+	inode = dentry->d_inode;
+	lower_inode = ECRYPTFS_INODE_TO_LOWER(inode);
+	lower_a_ops = lower_inode->i_mapping->a_ops;
+	lower_page = read_cache_page(lower_inode->i_mapping, lower_page_index,
+				     (filler_t *) lower_a_ops->readpage,
+				     (void *)lower_file);
+	if (IS_ERR(lower_page)) {
+		rc = PTR_ERR(lower_page);
+		lower_page = NULL;
+		ecryptfs_printk(0, KERN_ERR, "Error reading from page cache\n");
+		goto out;
+	}
+	wait_on_page_locked(lower_page);
+	if (!PageUptodate(lower_page)) {
+		lock_page(lower_page);
+		rc = lower_a_ops->readpage(lower_file, lower_page);
+		if (rc) {
+			lower_page = NULL;
+			rc = -EIO;
+			ecryptfs_printk(0, KERN_ERR, "Error reading lower "
+					"page at index=[%lu]\n",
+					lower_page_index);
+			goto out;
+		}
+		wait_on_page_locked(lower_page);
+		if (!PageUptodate(lower_page)) {
+			rc = -EIO;
+			ecryptfs_printk(0, KERN_ERR, "Error reading lower "
+					"page at index=[%lu]\n",
+					lower_page_index);
+			goto out;
+		}
+	}
+	page_data = (char *)kmap(page);
+	if (!page_data) {
+		rc = -ENOMEM;
+		ecryptfs_printk(0, KERN_ERR, "Error mapping page\n");
+		goto out;
+	}
+	lower_page_data = (char *)kmap(lower_page);
+	if (!lower_page_data) {
+		rc = -ENOMEM;
+		ecryptfs_printk(0, KERN_ERR, "Error mapping page\n");
+		kunmap(page);
+		goto out;
+	}
+	/* TODO: Copy sub-page amount of data? */
+	memcpy(page_data, lower_page_data, PAGE_CACHE_SIZE);
+	kunmap(lower_page);
+	kunmap(page);
+	rc = 0;
+      out:
+	if (likely(lower_page))
+		page_cache_release(lower_page);
+	if (rc == 0)
+		SetPageUptodate(page);
+	else
+		ClearPageUptodate(page);
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
+
+/* Action descriptor for decrypt_page() */
+#define ECRYPTFS_ACTION_COPY 0
+#define ECRYPTFS_ACTION_DECRYPT 1
+
+/**
+ * Decrypt a page of data
+ *
+ * @return Zero on success
+ */
+static int decrypt_page(struct ecryptfs_crypt_stats *crypt_stats,
+			struct file *file, char *iv, struct page *page,
+			pgoff_t lower_page_index, int decrypt)
+{
+	int rc = 0;
+	char *lower_page_encrypted_virt;
+	struct page *lower_page_encrypted;
+	int decrypt_to;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; lower_page_index = [%d]\n",
+			lower_page_index);
+	lower_page_encrypted_virt =
+	    kmem_cache_alloc(ecryptfs_lower_page_cache, SLAB_KERNEL);
+	if (!lower_page_encrypted_virt) {
+		rc = -ENOMEM;
+		ecryptfs_printk(0, KERN_ERR, "Error getting page for "
+				"encrypted lower page\n");
+		ClearPageUptodate(page);
+		goto out;
+	}
+	lower_page_encrypted = virt_to_page(lower_page_encrypted_virt);
+	rc = ecryptfs_do_readpage(file, lower_page_encrypted, lower_page_index);
+	if (rc) {
+		ecryptfs_printk(0, KERN_ERR, "Error reading lower encrypted "
+				"page\n");
+		ClearPageUptodate(page);
+		goto out;
+	}
+	ecryptfs_printk(1, KERN_NOTICE, "Decrypting page with iv:\n");
+	if (ecryptfs_verbosity > 0)
+		ecryptfs_dump_hex(iv, crypt_stats->iv_bytes);
+	ecryptfs_printk(1, KERN_NOTICE, "Using session key encryption key:\n");
+	if (ecryptfs_verbosity > 0)
+		ecryptfs_dump_hex(crypt_stats->key,
+				  crypt_stats->key_size_bits / 8);
+	decrypt_to = crypt_stats->extent_size;
+	ecryptfs_printk(1, KERN_NOTICE, "Decrypting to: [%d]\n", decrypt_to);
+	ecryptfs_printk(1, KERN_NOTICE, "First 8 bytes before decryption:\n");
+	if (ecryptfs_verbosity > 0)
+		ecryptfs_dump_hex((char *)page_address(lower_page_encrypted),
+				  8);
+	if (decrypt)
+		do_decrypt_page_offset(crypt_stats, page,
+				       0, lower_page_encrypted,
+				       0, decrypt_to, iv);
+	else
+		memcpy(page, lower_page_encrypted_virt, decrypt_to);
+	ecryptfs_printk(1, KERN_NOTICE, "First 8 bytes after decryption:\n");
+	if (ecryptfs_verbosity > 0)
+		ecryptfs_dump_hex((char *)page_address(page), 8);
+	kmem_cache_free(ecryptfs_lower_page_cache, lower_page_encrypted_virt);
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
+
+/**
+ * Read in a page
+ *
+ * @param file	This is an ecryptfs file
+ * @param page	ecryptfs associated page to stick the read data into
+ * @return Zero on success; non-zero on error
+ */
+static int ecryptfs_readpage(struct file *file, struct page *page)
+{
+	int rc = 0;
+	struct ecryptfs_crypt_stats *crypt_stats = NULL;
+	char iv[ECRYPTFS_MAX_IV_BYTES];
+	int iv_is_nonzero = 0;
+	char *records_virt;
+	struct page *records_page;
+	int record_byte_offset;
+	int iv_byte_offset;
+	int i;
+	pgoff_t records_page_index;
+	pgoff_t lower_page_index;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; page->index = [%ld]\n",
+			page->index);
+	ASSERT(file && file->f_dentry && file->f_dentry->d_inode);
+	crypt_stats = 
+		&ECRYPTFS_INODE_TO_PRIVATE(file->f_dentry->d_inode)->crypt_stats;
+	/* If the file is neither encrypted nor HMAC-verified, then we
+	 * have passthrough mode. */
+	if (!crypt_stats || !crypt_stats->encrypted) {
+		ecryptfs_printk(1, KERN_NOTICE,
+				"Passing through unencrypted page\n");
+		rc = ecryptfs_do_readpage(file, page, page->index);
+		goto out;
+	}
+	/* The file is encrypted, hmac verified, or both. */
+	ecryptfs_printk(1, KERN_NOTICE,
+			"crypt_stats->iv_bytes = [%d]\n",
+			crypt_stats->iv_bytes);
+	ecryptfs_printk(1, KERN_NOTICE,
+			"crypt_stats->records_per_page = [%d]\n",
+			crypt_stats->records_per_page);
+
+	/* Get the relevant IV/HMAC page */
+	records_page_index = last_records_page_idx(crypt_stats, page->index);
+	ecryptfs_printk(1, KERN_NOTICE, "records_page_index = [%lu]\n",
+			records_page_index);
+	records_virt = (char *)__get_free_page(GFP_KERNEL);
+	if (!records_virt) {
+		ecryptfs_printk(0, KERN_ERR, "Error getting free page");
+		rc = -ENOMEM;
+		ClearPageUptodate(page);
+		goto out;
+	}
+	records_page = virt_to_page(records_virt);
+	rc = ecryptfs_do_readpage(file, records_page, records_page_index);
+	if (rc) {
+		ecryptfs_printk(0, KERN_ERR, "Error reading IV/HMAC page");
+		ClearPageUptodate(page);
+		goto out;
+	}
+	/* TODO: HMAC: Include HMAC bytes in the record size */
+	record_byte_offset = (record_idx(crypt_stats, page->index) 
+			      * crypt_stats->iv_bytes);
+	iv_byte_offset = -1;
+	if (crypt_stats->encrypted) {
+		iv_byte_offset = record_byte_offset;
+		iv_is_nonzero = 0;
+		for (i = 0; i < crypt_stats->iv_bytes; i++)
+			iv_is_nonzero |= (records_virt + iv_byte_offset)[i];
+		memcpy(iv, (records_virt + iv_byte_offset),
+		       crypt_stats->iv_bytes);
+	}
+	ecryptfs_printk(1, KERN_NOTICE, "record_byte_offset = [%d]\n",
+			record_byte_offset);
+	ecryptfs_printk(1, KERN_NOTICE, "iv_byte_offset = [%d]\n",
+			iv_byte_offset);
+	free_page((unsigned long)records_virt);
+	lower_page_index = ecryptfs_pg_idx_to_lwr_pg_idx(crypt_stats,
+							 page->index);
+	ecryptfs_printk(1, KERN_NOTICE, "lower_page_index = [%lu]\n",
+			lower_page_index);
+	ecryptfs_printk(1, KERN_NOTICE, "iv_is_nonzero = [%d]\n",
+			iv_is_nonzero);
+	if (crypt_stats->encrypted && iv_is_nonzero) {
+		rc = decrypt_page(crypt_stats, file, iv, page,
+				  lower_page_index, ECRYPTFS_ACTION_DECRYPT);
+		if (rc) {
+			ecryptfs_printk(0, KERN_ERR, "Error decrypting "
+					"page; rc = [%d]\n", rc);
+			goto out;
+		}
+	} else {
+		ecryptfs_printk(1, KERN_NOTICE,
+				"Passing through unencrypted page\n");
+		rc = ecryptfs_do_readpage(file, page, lower_page_index);
+	}
+	SetPageUptodate(page);
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Unlocking page with index = [%lu]\n",
+			page->index);
+	unlock_page(page);
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
+
+static void fill_zeros_to_end_of_page(struct page *page, unsigned to)
+{
+	struct inode *inode = page->mapping->host;
+	int end_byte_in_page;
+	char *page_virt;
+
+	if ((i_size_read(inode) / PAGE_CACHE_SIZE) == page->index) {
+		end_byte_in_page = i_size_read(inode) % PAGE_CACHE_SIZE;
+		if (to > end_byte_in_page)
+			end_byte_in_page = to;
+		page_virt = kmap(page);
+		if (!page_virt) {
+			goto out;
+		}
+		memset((page_virt + end_byte_in_page), 0,
+		       (PAGE_CACHE_SIZE - end_byte_in_page));
+		kunmap(page);
+	}
+out:
+	return;
+}
+
+static int ecryptfs_prepare_write(struct file *file, struct page *page,
+				  unsigned from, unsigned to)
+{
+	int err = 0;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; page->index = [%lu]; from = "
+			"[%d]; to = [%d]\n", page->index, from, to);
+	kmap(page);
+	if (from == 0 && to == PAGE_CACHE_SIZE)
+		goto out;	/* If we are writing a full page, it will be
+				   up to date. */
+	if (!PageUptodate(page))
+		err = ecryptfs_do_readpage(file, page, page->index);
+out:
+	return err;
+}
+
+/**
+ * @return Zero on success
+ */
+int
+ecryptfs_write_inode_size_to_header(struct file *lower_file,
+				    struct inode *lower_inode,
+				    struct inode *inode)
+{
+	int rc = 0;
+	struct page *header_page;
+	char *header_virt;
+	struct address_space_operations *lower_a_ops;
+	unsigned long long file_size;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	header_page = grab_cache_page(lower_inode->i_mapping, 0);
+	if (!header_page) {
+		rc = -EINVAL;
+		ecryptfs_printk(0, KERN_ERR, "grab_cache_page for header page "
+				"failed\n");
+		goto out;
+	}
+	header_virt = kmap(header_page);
+	lower_a_ops = lower_inode->i_mapping->a_ops;
+	rc = lower_a_ops->prepare_write(lower_file, header_page, 0, 8);
+	file_size = (unsigned long long)i_size_read(inode);
+	memcpy(header_virt, &file_size, 8);
+	rc = lower_a_ops->commit_write(lower_file, header_page, 0, 8);
+	if (rc < 0)
+		ecryptfs_printk(0, KERN_ERR, "Error commiting header page "
+				"write\n");
+	kunmap(header_page);
+	ecryptfs_printk(1, KERN_NOTICE, "Unlocking page with index = [%lu]\n",
+			header_page->index);
+	unlock_page(header_page);
+	page_cache_release(header_page);
+	lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME;
+	mark_inode_dirty_sync(inode);
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
+
+/**
+ * This is where we encrypt the data and pass the encrypted data to
+ * the lower filesystem.  In OpenPGP-compatible mode, we operate on
+ * entire underlying packets.
+ *
+ * @param file The eCryptfs file object
+ * @param page The eCryptfs page
+ * @param from Ignored (we rotate the page IV on each write)
+ * @param to Ignored
+ * @return 
+ */
+static int ecryptfs_commit_write(struct file *file, struct page *page,
+				 unsigned from, unsigned to)
+{
+	int rc = -ENOMEM;
+	struct inode *inode;
+	struct inode *lower_inode;
+	struct page *lower_page = NULL;
+	struct file *lower_file = NULL;
+	loff_t pos;
+	unsigned bytes = to - from;
+	struct ecryptfs_crypt_stats *crypt_stats;
+	pgoff_t lower_page_index;
+	struct address_space_operations *lower_a_ops;
+	struct dentry *ecryptfs_dentry;
+
+	ecryptfs_printk(1, KERN_NOTICE,
+			"Enter; page->index = [%lu]; from = [%d]; to = [%d]\n",
+			page->index, from, to);
+	ecryptfs_dentry = file->f_dentry;
+	inode = page->mapping->host;
+	lower_inode = ECRYPTFS_INODE_TO_LOWER(inode);
+	down(&lower_inode->i_sem);
+	if (!(lower_inode->i_mapping
+	      && lower_inode->i_mapping->a_ops
+	      && lower_inode->i_mapping->a_ops->prepare_write
+	      && lower_inode->i_mapping->a_ops->commit_write)) {
+		ecryptfs_printk(0, KERN_ERR,
+				"a_ops of lower inode not valid\n");
+		rc = -EINVAL;
+		goto out;
+	}
+	lower_a_ops = lower_inode->i_mapping->a_ops;
+	ASSERT(file && file->f_dentry && file->f_dentry->d_inode);
+	crypt_stats = 
+		&ECRYPTFS_INODE_TO_PRIVATE(file->f_dentry->d_inode)->crypt_stats;
+	if (NULL != ECRYPTFS_FILE_TO_PRIVATE(file))
+		lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+	if (!crypt_stats) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	rc = ecryptfs_init_crypt_ctx(crypt_stats);
+	if (rc)
+		ecryptfs_printk(1, KERN_NOTICE, "Problem with "
+				"initializing crypto context\n");
+	if (crypt_stats->new_file) {
+		struct page *header_page;
+		char *header_virt;
+
+		crypt_stats->new_file = 0;
+		header_page = grab_cache_page(lower_inode->i_mapping, 0);
+		if (!header_page) {
+			rc = -EINVAL;
+			ecryptfs_printk(0, KERN_ERR, "grab_cache_page for "
+					"header page failed\n");
+			goto out;
+		}
+		header_virt = kmap(header_page);
+		rc = lower_a_ops->prepare_write(lower_file, header_page,
+						0, crypt_stats->extent_size);
+		rc = ecryptfs_write_headers_virt(header_virt, crypt_stats,
+						 ecryptfs_dentry, 
+						 ECRYPTFS_FILE_VERSION);
+		if (rc) {
+			ecryptfs_printk(0, KERN_WARNING, "Error generating "
+					"header; rc = [%d]\n", rc);
+			rc = -EIO;
+			goto out;
+		}
+		memset(header_virt, 0, 8);
+		rc = lower_a_ops->commit_write(lower_file, header_page,
+					       0, crypt_stats->extent_size);
+		if (rc < 0)
+			ecryptfs_printk(0, KERN_ERR, "Error commiting header "
+					"page write\n");
+		kunmap(header_page);
+		ecryptfs_printk(1, KERN_NOTICE,
+				"Unlocking header page with index = " "[%lu]\n",
+				header_page->index);
+		unlock_page(header_page);
+		page_cache_release(header_page);
+		if (rc < 0)
+			goto out;
+		rc = 0;
+		ecryptfs_printk(1, KERN_NOTICE, "lower_inode->i_blocks = "
+				"[%lu]\n", lower_inode->i_blocks);
+		i_size_write(inode, 0);
+		lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME;
+		mark_inode_dirty_sync(inode);
+	} else
+		ecryptfs_printk(1, KERN_NOTICE, "Not a new file\n");
+	/* Translate the page index */
+	lower_page_index = ecryptfs_pg_idx_to_lwr_pg_idx(crypt_stats,
+							 page->index);
+	ecryptfs_printk(1, KERN_NOTICE, "lower_page_index = [%lu]\n",
+			lower_page_index);
+	lower_page = grab_cache_page(lower_inode->i_mapping, lower_page_index);
+	if (!lower_page) {
+		ecryptfs_printk(0, KERN_ERR, "grab_cache_page for "
+				"lower_page_index=[%lu] failed\n",
+				lower_page_index);
+		goto out;
+	}
+	kmap(lower_page);
+	rc = lower_a_ops->prepare_write(lower_file, lower_page,
+					0, crypt_stats->extent_size);
+	if (rc)
+		goto out_unlock_lower;
+	fill_zeros_to_end_of_page(page, to);
+	if (!crypt_stats->encrypted) {
+		/* TODO: aops */
+		memcpy((char *)page_address(lower_page),
+		       (char *)page_address(page), crypt_stats->extent_size);
+	} else {
+		/* The file is either encrypted or HMAC'd */
+		char iv[ECRYPTFS_MAX_IV_BYTES];
+		int record_byte_offset;
+		ecryptfs_printk(1, KERN_NOTICE,
+				"crypt_stats->iv_bytes = [%d]\n",
+				crypt_stats->iv_bytes);
+		/* TODO: HMAC: Include HMAC bytes in the record size */	
+		record_byte_offset = (record_idx(crypt_stats, page->index) 
+				      * crypt_stats->iv_bytes);
+		rc = ecryptfs_read_rotate_write_iv(iv, inode, 
+						   record_byte_offset, 
+						   lower_file, page);
+		if (rc) {
+			ecryptfs_printk(0, KERN_ERR, "Error rotating IV\n");
+			goto out_unlock_lower;
+		}
+		ecryptfs_printk(1, KERN_NOTICE, "Encrypting page with iv:\n");
+		if (unlikely(ecryptfs_verbosity > 0))
+			ecryptfs_dump_hex(iv, crypt_stats->iv_bytes);
+		rc = encrypt_page(crypt_stats, page, lower_page, iv);
+		if (rc) {
+			ecryptfs_printk(0, KERN_WARNING, "Error encrypting "
+					"page (upper index [%llu])\n", 
+					page->index);
+			goto out;
+		}
+	}
+	rc = lower_a_ops->commit_write(lower_file, lower_page, 0,
+				       crypt_stats->extent_size);
+	if (rc < 0) {
+		ecryptfs_printk(0, KERN_ERR,
+				"Error committing write; rc = [%d]\n", rc);
+		goto out_unlock_lower;
+	}
+	rc = bytes;
+	inode->i_blocks = lower_inode->i_blocks;
+	pos = (page->index << PAGE_CACHE_SHIFT) + to;
+	if (pos > i_size_read(inode)) {
+		i_size_write(inode, pos);
+		ecryptfs_printk(1, KERN_NOTICE, "Expanded file size to "
+				"[%lld]\n", i_size_read(inode));
+	}
+	ecryptfs_write_inode_size_to_header(lower_file, lower_inode, inode);
+	lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME;
+	mark_inode_dirty_sync(inode);
+	
+out_unlock_lower:
+	kunmap(lower_page);
+	ecryptfs_printk(1, KERN_NOTICE,
+			"Unlocking lower page with index = [%lu]\n",
+			lower_page->index);
+	unlock_page(lower_page);
+	page_cache_release(lower_page);
+	kunmap(page);		/* mapped in prepare_write */
+out:
+	if (rc < 0)
+		ClearPageUptodate(page);
+	else
+		SetPageUptodate(page);
+	up(&lower_inode->i_sem);
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
+
+static sector_t ecryptfs_bmap(struct address_space *mapping, sector_t block)
+{
+	int rc = 0;
+	struct inode *inode;
+	struct inode *lower_inode;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	inode = (struct inode *)mapping->host;
+	lower_inode = ECRYPTFS_INODE_TO_LOWER(inode);
+	if (lower_inode->i_mapping->a_ops->bmap)
+		rc = lower_inode->i_mapping->a_ops->bmap(lower_inode->i_mapping,
+							 block);
+	return rc;
+}
+
+/**
+ * This function is copied verbatim from mm/filemap.c.  It should be
+ * simply moved to some header file instead.
+ */
+static int sync_page(struct page *page)
+{
+	struct address_space *mapping = page->mapping;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	if (mapping && mapping->a_ops && mapping->a_ops->sync_page)
+		return mapping->a_ops->sync_page(page);
+	return 0;
+}
+
+static int ecryptfs_sync_page(struct page *page)
+{
+	int rc = 0;
+	struct inode *inode;
+	struct inode *lower_inode;
+	struct page *lower_page;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	inode = page->mapping->host;
+	lower_inode = ECRYPTFS_INODE_TO_LOWER(inode);
+	lower_page = grab_cache_page(lower_inode->i_mapping, page->index);
+	if (!lower_page) {
+		rc = -ENOMEM;
+		ecryptfs_printk(0, KERN_ERR, "Error from grab_cache_page "
+				"(no mem?)\n");
+		goto out;
+	}
+	rc = sync_page(lower_page);
+	ecryptfs_printk(1, KERN_NOTICE, "Unlocking page with index = [%lu]\n",
+			lower_page->index);
+	unlock_page(lower_page);
+	page_cache_release(lower_page);
+out:
+	return rc;
+}
+
+struct address_space_operations ecryptfs_aops = {
+	.writepage = ecryptfs_writepage,
+	.readpage = ecryptfs_readpage,
+	.prepare_write = ecryptfs_prepare_write,
+	.commit_write = ecryptfs_commit_write,
+	.bmap = ecryptfs_bmap,
+	.sync_page = ecryptfs_sync_page,
+};
#27 lore raw

PATCH 11/12 Keystore

In reply to Phillip Hellewell

eCryptfs keystore. Packet generation and parsing code. Authentication token management code. This file has been trimmed down considerably to support only mount-wide passphrases in the 0.1 release.

---

 keystore.c |  820 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 820 insertions(+)
fs/ecryptfs/keystore.c +820 -0
@@ -0,0 +1,820 @@
+/**
+ * eCryptfs: Linux filesystem encryption layer
+ * In-kernel key management code.  Includes functions to parse and
+ * write authentication token-related packets with the underlying
+ * file.
+ *
+ * Copyright (c) 2005 International Business Machines Corp.
+ *   Author(s): Michael A. Halcrow <[email protected]>
+ *              Michael C. Thompson <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/syscalls.h>
+#include <linux/pagemap.h>
+#include <linux/key.h>
+#include <linux/random.h>
+#include <linux/crypto.h>
+#include <asm/scatterlist.h>
+#include "ecryptfs_kernel.h"
+
+/**
+ * request_key returned an error instead of a valid key address;
+ * determine the type of error, make appropriate log entries, and
+ * return an error code.
+ */
+int process_request_key_err(long err_code)
+{
+	int rc = 0;
+
+	switch (err_code) {
+	case ENOKEY:
+		ecryptfs_printk(0, KERN_WARNING, "No key\n");
+		rc = -ENOENT;
+		break;
+	case EKEYEXPIRED:
+		ecryptfs_printk(0, KERN_WARNING, "Key expired\n");
+		rc = -ETIME;
+		break;
+	case EKEYREVOKED:
+		ecryptfs_printk(0, KERN_WARNING, "Key revoked\n");
+		rc = -EINVAL;
+		break;
+	default:
+		ecryptfs_printk(0, KERN_WARNING, "Unknown error code: "
+				"[%lu]\n", err_code);
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+static void ecryptfs_dump_auth_tok(struct ecryptfs_auth_tok *auth_tok)
+{
+	char salt[ECRYPTFS_SALT_SIZE * 2 + 1];
+	char sig[ECRYPTFS_SIG_SIZE_HEX + 1];
+	ecryptfs_printk(1, KERN_NOTICE, "Auth tok at mem loc [%p]:\n",
+			auth_tok);
+	ecryptfs_printk(1, KERN_NOTICE, " * instanceof = [%d]\n",
+			auth_tok->instanceof);
+	ecryptfs_printk(1, KERN_NOTICE, " * instantiated = [%d]\n",
+			auth_tok->instantiated);
+	switch (auth_tok->instanceof) {
+	case ECRYPTFS_PASSWORD:
+		ecryptfs_printk(1, KERN_NOTICE, " * password = [%s]\n",
+				auth_tok->token.password.password);
+		ecryptfs_printk(1, KERN_NOTICE, " * password_size = [%d]\n",
+				auth_tok->token.password.password_size);
+		ecryptfs_to_hex(salt, auth_tok->token.password.salt,
+				ECRYPTFS_SALT_SIZE);
+		salt[ECRYPTFS_SALT_SIZE * 2] = '\0';
+		ecryptfs_printk(1, KERN_NOTICE, " * salt = [%s]\n", salt);
+		ecryptfs_printk(1, KERN_NOTICE, " * saltless = [%d]\n",
+				auth_tok->token.password.saltless);
+		memcpy(sig, auth_tok->token.password.signature,
+		       ECRYPTFS_SIG_SIZE_HEX);
+		sig[ECRYPTFS_SIG_SIZE_HEX] = '\0';
+		ecryptfs_printk(1, KERN_NOTICE, " * signature = [%s]\n", sig);
+		break;
+	case ECRYPTFS_PRIVATE_KEY:
+		ecryptfs_printk(1, KERN_NOTICE, " * (NO PRIVATE KEY SUPPORT "
+				"IN ECRYPTFS VERSION 0.1)\n");
+		break;
+	default:
+		ecryptfs_printk(1, KERN_NOTICE, " * Unrecognized instanceof\n");
+	}
+	ecryptfs_printk(1, KERN_NOTICE, " * session_key.flags = [0x%x]\n",
+			auth_tok->session_key.flags);
+	if (auth_tok->session_key.flags
+	    & ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT)
+		ecryptfs_printk(1, KERN_NOTICE,
+				" * Userspace decrypt request set\n");
+	if (auth_tok->session_key.flags
+	    & ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT)
+		ecryptfs_printk(1, KERN_NOTICE,
+				" * Userspace encrypt request set\n");
+	if (auth_tok->session_key.flags & ECRYPTFS_CONTAINS_DECRYPTED_KEY) {
+		ecryptfs_printk(1, KERN_NOTICE, " * Contains decrypted key\n");
+		ecryptfs_printk(1, KERN_NOTICE,
+				" * session_key.decrypted_key_size = [0x%x]\n",
+				auth_tok->session_key.decrypted_key_size);
+		ecryptfs_printk(1, KERN_NOTICE, " * Decrypted session key "
+				"dump:\n");
+		if (ecryptfs_verbosity > 0)
+			ecryptfs_dump_hex(auth_tok->session_key.decrypted_key,
+					  ECRYPTFS_DEFAULT_KEY_BYTES);
+	}
+	if (auth_tok->session_key.flags & ECRYPTFS_CONTAINS_ENCRYPTED_KEY) {
+		ecryptfs_printk(1, KERN_NOTICE, " * Contains encrypted key\n");
+		ecryptfs_printk(1, KERN_NOTICE,
+				" * session_key.encrypted_key_size = [0x%x]\n",
+				auth_tok->session_key.encrypted_key_size);
+		ecryptfs_printk(1, KERN_NOTICE, " * Encrypted session key "
+				"dump:\n");
+		if (ecryptfs_verbosity > 0)
+			ecryptfs_dump_hex(auth_tok->session_key.encrypted_key,
+					  auth_tok->session_key.
+					  encrypted_key_size);
+	}
+}
+
+static void wipe_auth_tok_list(struct list_head *auth_tok_list_head)
+{
+	struct list_head *walker;
+	struct ecryptfs_auth_tok_list_item *auth_tok_list_item;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	walker = auth_tok_list_head->next;
+	while (walker != auth_tok_list_head) {
+		auth_tok_list_item =
+		    list_entry(walker, struct ecryptfs_auth_tok_list_item,
+			       list);
+		walker = auth_tok_list_item->list.next;
+		kmem_cache_free(ecryptfs_auth_tok_list_item_cache,
+				auth_tok_list_item);
+	}
+	ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+}
+
+kmem_cache_t *ecryptfs_auth_tok_list_item_cache;
+
+/**
+ * @return Zero on success
+ */
+static int parse_packet_length(unsigned char *data, int *offset, int *size)
+{
+	int rc = 0;
+
+	if (data[(*offset)] < 192) {
+		/* One-byte length */
+		(*size) = data[(*offset)++];
+	} else if (data[(*offset)] < 224) {
+		/* Two-byte length */
+		(*size) = ((data[(*offset)++] - 192) * 256);
+		(*size) += (data[(*offset)++] + 192);
+	} else if (data[(*offset)] == 255) {
+		/* Three-byte length; we're not supposed to see this */
+		ecryptfs_printk(0, KERN_ERR, "Three-byte packet length not "
+				"supported\n");
+		rc = -EINVAL;
+		goto out;
+	} else {
+		ecryptfs_printk(0, KERN_ERR, "Error parsing packet length\n");
+		rc = -EINVAL;
+		goto out;
+	}
+out:
+	return rc;
+}
+
+static int write_packet_length(char *dest, int *dest_offset, int size)
+{
+	int rc = 0;
+
+	if (size < 192) {
+		dest[(*dest_offset)] = size;
+		(*dest_offset)++;
+	} else if (size < 65536) {
+		dest[(*dest_offset)] = (((size - 192) / 256) + 192);
+		(*dest_offset)++;
+		dest[(*dest_offset)] = ((size - 192) % 256);
+		(*dest_offset)++;
+	} else {
+		rc = -EINVAL;
+		ecryptfs_printk(0, KERN_WARNING,
+				"Unsupported packet size: [%d]\n", size);
+	}
+	return rc;
+}
+
+/**
+ * Passphrase packet.
+ *
+ * @return New offset in the packet set page; error value on error.
+ */
+static int
+parse_tag_3_packet(struct ecryptfs_crypt_stats *crypt_stats,
+		   unsigned char *data, struct list_head *auth_tok_list)
+{
+	int i = 0;
+	int rc = 0;
+	int body_size = 0;
+	struct ecryptfs_auth_tok *auth_tok;
+	struct ecryptfs_auth_tok_list_item *auth_tok_list_item;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	if (data[i++] != 0x8c) {
+		ecryptfs_printk(0, KERN_ERR, "Enter w/ first byte != 0x8c\n");
+		rc = -EINVAL;
+		goto out_no_mem;
+	}
+	/* Released: wipe_auth_tok_list called in ecryptfs_parse_packet_set or
+	 * at end of function upon failure */
+	auth_tok_list_item =
+	    kmem_cache_alloc(ecryptfs_auth_tok_list_item_cache,
+			     SLAB_KERNEL);
+	if (!auth_tok_list_item) {
+		ecryptfs_printk(0, KERN_ERR, "Unable to allocate memory\n");
+		rc = -ENOMEM;
+		goto out_no_mem;
+	}
+	auth_tok = &auth_tok_list_item->auth_tok;
+	memset(auth_tok, 0, sizeof(struct ecryptfs_auth_tok));
+	/* TODO: Make *sure* it's encrypted; do this w/ policy tokens.
+	 * Actually, this is probably not the right place to set this
+	 * flag. If the eCryptfs marker matches, then we probably have
+	 * a real tag 3 packet, and hence the file is most likely
+	 * encrypted. */
+	rc = parse_packet_length(data, &i, &body_size);
+	if (rc)
+		goto out;
+	auth_tok->session_key.encrypted_key_size =
+	    body_size - (0x05 + ECRYPTFS_SALT_SIZE);
+	ecryptfs_printk(1, KERN_NOTICE, "Encrypted key size = [%d]\n",
+			auth_tok->session_key.encrypted_key_size);
+	if (data[i++] != 0x04) {
+		ecryptfs_printk(1, KERN_NOTICE, "Unknown version number "
+				"[%d]\n", data[i - 1]);
+		rc = -EINVAL;
+		goto out;
+	}
+	ecryptfs_cipher_code_to_string(crypt_stats->cipher, (u16)data[i]);
+	switch(data[i++]) {
+	case 0x07:
+		crypt_stats->key_size_bits = 128;
+		break;
+	case 0x08:
+		crypt_stats->key_size_bits = 192;
+		break;
+	case 0x09:
+		crypt_stats->key_size_bits = 256;
+		break;
+	}
+	ecryptfs_init_crypt_ctx(crypt_stats);
+	if (data[i++] != 0x03) {
+		ecryptfs_printk(0, KERN_ERR, "Only S2K ID 3 is currently "
+				"supported\n");
+		rc = -ENOSYS;
+		goto out;
+	}
+	/* TODO: finish the hash mapping */
+	switch (data[i++]) {
+	case 0x01:
+		/* Choose MD5 */
+		memcpy(auth_tok->token.password.salt, &data[i],
+		       ECRYPTFS_SALT_SIZE);
+		i += ECRYPTFS_SALT_SIZE;
+		auth_tok->token.password.hash_iterations =
+		    ((u32) 16 + (data[i] & 15)) << ((data[i] >> 4) + 6);
+		i++;
+		memcpy(auth_tok->session_key.encrypted_key, &data[i],
+		       auth_tok->session_key.encrypted_key_size);
+		i += auth_tok->session_key.encrypted_key_size;
+		auth_tok->session_key.flags &= ~ECRYPTFS_CONTAINS_DECRYPTED_KEY;
+		auth_tok->session_key.flags |= ECRYPTFS_CONTAINS_ENCRYPTED_KEY;
+		auth_tok->token.password.hash_algo = 0x01;
+		break;
+	default:
+		ecryptfs_printk(0, KERN_ERR, "Unsupported hash algorithm: "
+				"[%d]\n", data[i - 1]);
+		rc = -ENOSYS;
+		goto out;
+	}
+	/* TODO: Use the keyring */
+	auth_tok->uid = current->uid;
+	auth_tok->instanceof = ECRYPTFS_PASSWORD;
+	if (data[i++] != 0xed) {
+		ecryptfs_printk(0, KERN_ERR, "No (ecryptfs-specific) literal "
+				"packet containing authentication token "
+				"signature found after tag 3 packet\n");
+		rc = -EINVAL;
+		goto out;
+	}
+	if (data[i++] != (ECRYPTFS_SIG_SIZE + 13)) {
+		ecryptfs_printk(0, KERN_ERR, "Unrecognizable packet\n");
+		rc = -EINVAL;
+		goto out;
+	}
+	if (data[i++] != 0x62) {
+		ecryptfs_printk(0, KERN_ERR, "Unrecognizable packet\n");
+		rc = -EINVAL;
+		goto out;
+	}
+	if (data[i++] != 0x08) {
+		ecryptfs_printk(0, KERN_ERR, "Unrecognizable packet\n");
+		rc = -EINVAL;
+		goto out;
+	}
+	i += 12;		/* We don't care about the filename or the timestamp */
+	/* TODO: Parametarize; we might actually want userspace to
+	 * decrypt the session key. */
+	auth_tok->session_key.flags &=
+	    ~ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT;
+	auth_tok->session_key.flags &=
+	    ~ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT;
+	ecryptfs_to_hex(auth_tok->token.password.signature, &data[i],
+			ECRYPTFS_SIG_SIZE);
+	auth_tok->token.password.signature[ECRYPTFS_PASSWORD_SIG_SIZE] = '\0';
+	rc = i;
+	list_add(&auth_tok_list_item->list, auth_tok_list);
+	crypt_stats->encrypted = 1;
+	goto out_success;
+out:
+	kmem_cache_free(ecryptfs_auth_tok_list_item_cache,
+			auth_tok_list_item);
+out_no_mem:
+out_success:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
+
+/**
+ * Decrypt the session key with the given auth_tok.
+ *
+ * TODO: Performance: This is a good candidate for optimization.
+ *
+ * @param auth_tok
+ * @return 0 on success; non-zero error otherwise
+ */
+static int decrypt_session_key(struct ecryptfs_auth_tok *auth_tok,
+			       struct ecryptfs_crypt_stats *crypt_stats)
+{
+	int rc = 0;
+	struct ecryptfs_password *password_s_ptr;
+	struct crypto_tfm *tfm = NULL;
+	struct scatterlist src_sg[2], dst_sg[2];
+	/* TODO: Use virt_to_scatterlist for these */
+	char *encrypted_session_key;
+	char *session_key;
+
+	password_s_ptr = &auth_tok->token.password;
+	if (password_s_ptr->session_key_encryption_key_set)
+		ecryptfs_printk(1, KERN_NOTICE, "Session key encryption key "
+				"set; skipping key generation\n");
+	ecryptfs_printk(1, KERN_NOTICE, "Session key encryption key (size [%d])"
+			":\n", password_s_ptr->session_key_encryption_key_size);
+	if (ecryptfs_verbosity > 0)
+		ecryptfs_dump_hex(password_s_ptr->session_key_encryption_key,
+				  password_s_ptr->
+				  session_key_encryption_key_size);
+	tfm = crypto_alloc_tfm(crypt_stats->cipher, 0);
+	if (!tfm) {
+		ecryptfs_printk(0, KERN_ERR, "Error allocating crypto "
+				"context\n");
+		rc = -ENOMEM;
+		goto out;
+	}
+	crypto_cipher_setkey(tfm, password_s_ptr->session_key_encryption_key,
+			     password_s_ptr->session_key_encryption_key_size);
+	/* TODO: virt_to_scatterlist */
+	encrypted_session_key = (char *)__get_free_page(GFP_KERNEL);
+	if (!encrypted_session_key) {
+		ecryptfs_printk(0, KERN_ERR, "Out of memory\n");
+		rc = -ENOMEM;
+		goto out;
+	}
+	session_key = (char *)__get_free_page(GFP_KERNEL);
+	if (!session_key) {
+		kfree(encrypted_session_key);
+		ecryptfs_printk(0, KERN_ERR, "Out of memory\n");
+		rc = -ENOMEM;
+		goto out;
+	}
+	memcpy(encrypted_session_key, auth_tok->session_key.encrypted_key,
+	       auth_tok->session_key.encrypted_key_size);
+	src_sg[0].page = virt_to_page(encrypted_session_key);
+	src_sg[0].offset = 0;
+	/* TODO: key_size < PAGE_CACHE_SIZE */
+	src_sg[0].length = auth_tok->session_key.encrypted_key_size;
+	dst_sg[0].page = virt_to_page(session_key);
+	dst_sg[0].offset = 0;
+	auth_tok->session_key.decrypted_key_size =
+	    auth_tok->session_key.encrypted_key_size;
+	dst_sg[0].length = auth_tok->session_key.encrypted_key_size;
+	/* TODO: Handle error condition */
+	crypto_cipher_decrypt(tfm, dst_sg, src_sg,
+			      auth_tok->session_key.encrypted_key_size);
+	auth_tok->session_key.decrypted_key_size =
+	    auth_tok->session_key.encrypted_key_size;
+	memcpy(auth_tok->session_key.decrypted_key, session_key,
+	       auth_tok->session_key.decrypted_key_size);
+	auth_tok->session_key.flags |= ECRYPTFS_CONTAINS_DECRYPTED_KEY;
+	memcpy(crypt_stats->key, auth_tok->session_key.decrypted_key,
+	       auth_tok->session_key.decrypted_key_size);
+	crypt_stats->key_valid = 1;
+	crypt_stats->key_size_bits =
+	    auth_tok->session_key.decrypted_key_size * 8;
+	ecryptfs_printk(1, KERN_NOTICE, "Decrypted session key:\n");
+	if (ecryptfs_verbosity > 0)
+		ecryptfs_dump_hex(crypt_stats->key,
+				  crypt_stats->key_size_bits / 8);
+	free_page((unsigned long)encrypted_session_key);
+	free_page((unsigned long)session_key);
+out:
+	if (tfm)
+		crypto_free_tfm(tfm);
+	return rc;
+}
+
+/**
+ * N.B. This comment is applicable to 0.2 release (and later) only.
+ * Extract the authentication token signatures.  eCryptfs expects this
+ * function to recover the symmetric key of the crypt_stats structure
+ * if at all possible, given the current packet set.  Authentication
+ * tokens are composed of the tokens themselves and their descriptors.
+ * It is possible to have an authentication token object in the
+ * keyring that only has a descriptor and not a token component.  Each
+ * eCryptfs file header contains a concatenation of descriptors.  For
+ * each descriptor, this function assures that an authentication token
+ * object is instantiated in the keyring.  This instantiation takes
+ * place in the request_key callout application.  Thus, the callout
+ * application requires the complete descriptor.  The crypt_stats
+ * object contains a set of descriptors that apply to this file.  When
+ * the headers are written out, they are re-constructed from the set
+ * of authentication token descriptors.
+ *
+ * If at any point we have a problem parsing the packets, we will -EIO and
+ * just bail out.
+ *
+ * GOAL: Get crypt_stats to have the file's session key.
+ * 
+ * @param dest	The header page in memory
+ * @param version Version of file format, to guide parsing behavior
+ * @return	0 if a valid authentication token was retrieved and processed;
+ * 		negative value for file not encrypted or for error conditions
+ */
+int ecryptfs_parse_packet_set(unsigned char *dest,
+			      struct ecryptfs_crypt_stats *crypt_stats,
+			      struct dentry *ecryptfs_dentry, int version)
+{
+	int i = 0;
+	int rc = 0;
+	int found_auth_tok = 0;
+	int next_packet_is_auth_tok_packet;
+	char sig[ECRYPTFS_SIG_SIZE_HEX];
+	struct list_head auth_tok_list;
+	struct list_head *walker;
+	struct ecryptfs_auth_tok *chosen_auth_tok = NULL;
+	struct ecryptfs_mount_crypt_stats *mount_crypt_stats =
+	    &(ECRYPTFS_SUPERBLOCK_TO_PRIVATE(
+		      ecryptfs_dentry->d_sb)->mount_crypt_stats);
+	struct ecryptfs_auth_tok *candidate_auth_tok = NULL;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	ecryptfs_set_default_sizes(crypt_stats);
+	INIT_LIST_HEAD(&auth_tok_list);
+
+	/* Parse the header to find as many packets as we can, these will be
+	 * added the our &auth_tok_list */
+	next_packet_is_auth_tok_packet = 1;
+	while (next_packet_is_auth_tok_packet)
+		switch (dest[i]) {
+		case 0x8c:	/* tag 3 packet; s2k */
+			rc = parse_tag_3_packet(crypt_stats,
+						(unsigned char *)&dest[i],
+						&auth_tok_list);
+			if (rc != -EINVAL && rc != -ENOMEM) {
+				i += rc;
+				rc = 0;
+			} else {
+				ecryptfs_printk(0, KERN_ERR, "Error parsing "
+						"tag 3 packet\n");
+				rc = -EIO;
+				goto out_wipe_list;
+			}
+			break;
+		default:
+			ecryptfs_printk(1, KERN_NOTICE, "No packet at offset "
+					"[%d] of the file header; hex value of "
+					"character is [0x%.2x]\n", i, dest[i]);
+			next_packet_is_auth_tok_packet = 0;
+		}
+	if (list_empty(&auth_tok_list)) {
+		rc = -EINVAL;	/* Do not support non-encrypted files */
+		goto out;
+	}
+
+	/* If we have a global auth tok, then use it should be tried */
+	if (mount_crypt_stats->global_auth_tok) {
+		memcpy(sig, mount_crypt_stats->global_auth_tok_sig,
+		       ECRYPTFS_SIG_SIZE_HEX);
+		chosen_auth_tok = mount_crypt_stats->global_auth_tok;
+	} else
+		BUG();		/* This should not be the case in 0.1 release */
+	/* Scan list to see if our chosen_auth_tok works */
+	list_for_each(walker, &auth_tok_list) {
+		struct ecryptfs_auth_tok_list_item *auth_tok_list_item;
+		auth_tok_list_item =
+		    list_entry(walker, struct ecryptfs_auth_tok_list_item,
+			       list);
+		candidate_auth_tok = &auth_tok_list_item->auth_tok;
+		if (unlikely(ecryptfs_verbosity > 0)) {
+			ecryptfs_printk(1, KERN_NOTICE,
+					"Considering cadidate auth tok:\n");
+			ecryptfs_dump_auth_tok(candidate_auth_tok);
+		}
+		/* TODO: Replace ECRYPTFS_SIG_SIZE_HEX w/ dynamic value */
+		if ((candidate_auth_tok->instanceof == ECRYPTFS_PASSWORD) &&
+		    !strncmp(candidate_auth_tok->token.password.signature,
+			     sig, ECRYPTFS_SIG_SIZE_HEX)) {
+			found_auth_tok = 1;
+			goto leave_list;
+			/* TODO: Transfer the common salt into the
+			 * crypt_stats salt */
+		}
+	}
+leave_list:
+	if (!found_auth_tok) {
+		ecryptfs_printk(0, KERN_ERR, "Could not find authentication "
+				"token on temporary list for sig [%.*s]\n",
+				ECRYPTFS_SIG_SIZE_HEX, sig);
+		goto out_wipe_list;
+	} else {
+		memcpy(&(candidate_auth_tok->token.password),
+		       &(chosen_auth_tok->token.password),
+		       sizeof(struct ecryptfs_password));
+		decrypt_session_key(candidate_auth_tok, crypt_stats);
+	}
+
+out_wipe_list:
+	wipe_auth_tok_list(&auth_tok_list);
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
+
+/**
+ * Write passphrase packet
+ *
+ * @param dest Buffer into which to write the packet
+ * @param max Maximum number of bytes that can be writtn
+ * @return Number of bytes written; 0 on error
+ */
+static int
+write_tag_3_packet(char *dest, int dest_offset, int max,
+		   struct ecryptfs_auth_tok *auth_tok,
+		   struct ecryptfs_crypt_stats *crypt_stats)
+{
+	int rc = 0;
+	struct ecryptfs_key_record key_rec;
+	int i;
+	int signature_is_valid = 0;
+	int encrypted_session_key_valid = 0;
+	char session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES];
+	struct scatterlist dest_sg[2];
+	struct scatterlist src_sg[2];
+	struct crypto_tfm *tfm = NULL;
+	int key_rec_size;
+	int offset_save = 0;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; dest_offset = [%d]\n",
+			dest_offset);
+	/* Check for a valid signature on the auth_tok */
+	for (i = 0; i < ECRYPTFS_SIG_SIZE_HEX; i++)
+		signature_is_valid |= auth_tok->token.password.signature[i];
+	if (!signature_is_valid)
+		BUG();
+	ecryptfs_from_hex(key_rec.sig, auth_tok->token.password.signature,
+			  ECRYPTFS_SIG_SIZE);
+	key_rec.enc_key_size_bits = crypt_stats->key_size_bits;
+	key_rec.type = ECRYPTFS_PACKET_SET_TYPE_PASSWORD;
+
+	encrypted_session_key_valid = 0;
+	if (auth_tok->session_key.encrypted_key_size == 0)
+		auth_tok->session_key.encrypted_key_size =
+		    ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES;
+	for (i = 0; i < auth_tok->session_key.encrypted_key_size; i++)
+		encrypted_session_key_valid |=
+		    auth_tok->session_key.encrypted_key[i];
+	if (auth_tok->session_key.encrypted_key_size == 0) {
+		ecryptfs_printk(0, KERN_WARNING, "auth_tok->session_key."
+				"encrypted_key_size == 0");
+		auth_tok->session_key.encrypted_key_size =
+		    ECRYPTFS_DEFAULT_KEY_BYTES;
+	}
+	if (encrypted_session_key_valid) {
+		memcpy(key_rec.enc_key,
+		       auth_tok->session_key.encrypted_key,
+		       auth_tok->session_key.encrypted_key_size);
+		goto encrypted_session_key_set;
+	}
+	if (auth_tok->token.password.session_key_encryption_key_set) {
+		ecryptfs_printk(1, KERN_NOTICE, "Using previously generated "
+				"session key encryption key of size [%d]\n",
+				auth_tok->token.password.
+				session_key_encryption_key_size);
+		memcpy(session_key_encryption_key,
+		       auth_tok->token.password.session_key_encryption_key,
+		       auth_tok->token.password.
+		       session_key_encryption_key_size);
+		ecryptfs_printk(1, KERN_NOTICE,
+				"Cached session key " "encryption key: \n");
+		if (ecryptfs_verbosity > 0)
+			ecryptfs_dump_hex(session_key_encryption_key, 16);
+		goto session_key_encryption_key_set;
+	}
+      session_key_encryption_key_set:
+	if (unlikely(ecryptfs_verbosity > 0)) {
+		ecryptfs_printk(1, KERN_NOTICE, "Session key encryption key:"
+				"\n");
+		ecryptfs_dump_hex(session_key_encryption_key, 16);
+	}
+	/* Encrypt the key with the key encryption key */
+	/* Set up the scatterlists */
+	rc = virt_to_scatterlist(crypt_stats->key,
+				 crypt_stats->key_size_bits / 8, src_sg, 2);
+	if (!rc) {
+		ecryptfs_printk(0, KERN_ERR, "Error generating scatterlist "
+				"for crypt_stats session key\n");
+		rc = -ENOMEM;
+		goto out;
+	}
+	rc = virt_to_scatterlist(key_rec.enc_key,
+				 key_rec.enc_key_size_bits / 8, dest_sg, 2);
+	if (!rc) {
+		ecryptfs_printk(0, KERN_ERR, "Error generating scatterlist "
+				"for crypt_stats encrypted session key\n");
+		rc = -ENOMEM;
+		goto out;
+	}
+	/* Initialize the key encryption context */
+	ASSERT(crypt_stats->cipher);
+	if ((tfm = crypto_alloc_tfm(crypt_stats->cipher, 0)) == NULL) {
+		ecryptfs_printk(0, KERN_ERR, "Could not initialize crypto "
+				"context for cipher [%s]\n",
+				crypt_stats->cipher);
+		rc = 0;
+		goto out;
+	}
+	/* Set the key encryption key */
+	rc = crypto_cipher_setkey(tfm, session_key_encryption_key,
+				  ECRYPTFS_DEFAULT_KEY_BYTES);
+	if (rc < 0) {
+		ecryptfs_printk(0, KERN_ERR, "Error setting key for crypto "
+				"context\n");
+		rc = 0;
+		goto out;
+	}
+	ecryptfs_printk(1, KERN_NOTICE, "Encrypting [%d] bytes of the key\n",
+			crypt_stats->key_size_bits / 8);
+	crypto_cipher_encrypt(tfm, dest_sg, src_sg,
+			      crypt_stats->key_size_bits / 8);
+
+	ecryptfs_printk(1, KERN_NOTICE, "This should be the encrypted key:\n");
+	if (ecryptfs_verbosity > 0)
+		ecryptfs_dump_hex(key_rec.enc_key, 
+				  key_rec.enc_key_size_bits / 8);
+encrypted_session_key_set:
+	/* Now we have a valid key_rec.  Append it to the
+	 * key_rec set. */
+	key_rec_size = KEY_REC_SIZE(key_rec);
+	if ((dest_offset + key_rec_size) >= ECRYPTFS_MAX_KEYSET_SIZE) {
+		ecryptfs_printk(0, KERN_ERR, "Keyset too large\n");
+		rc = 0;
+		goto out;
+	}
+	offset_save = dest_offset;
+	if ((dest_offset + 0x05 + ECRYPTFS_SALT_SIZE
+	     + (key_rec.enc_key_size_bits / 8)) >= PAGE_CACHE_SIZE) {
+		ecryptfs_printk(0, KERN_ERR, "Too many authentication tokens; "
+				"cryptfs does not yet support this many\n");
+		rc = 0;
+		goto out;
+	}
+	/* This format is inspired by OpenPGP; see RFC 2440
+	 * packet tag 3 */
+	*(dest + (dest_offset++)) = 0x8c;	/* tag 3 */
+	/* ver+cipher+s2k+hash+salt+iter+enc_key */
+	rc = write_packet_length(dest, &dest_offset,
+				 (0x05 + ECRYPTFS_SALT_SIZE
+				  + (key_rec.enc_key_size_bits / 8)));
+	*(dest + (dest_offset++)) = 0x04;	/* version 4 */
+	
+	rc = ecryptfs_code_for_cipher_string(crypt_stats->cipher);
+	if (rc == 0) {
+		ecryptfs_printk(0, KERN_WARNING, "Unable to generate code for "
+				"cipher [%s]\n", crypt_stats->cipher);
+		goto out;
+	}
+	if (rc == 0x07) {
+		switch (crypt_stats->key_size_bits) {
+		case 128:
+			break;
+		case 192:
+			rc = 0x08;	/* AES-192 */
+			break;
+		case 256:
+			rc = 0x09;	/* AES-256 */
+			break;
+		default:
+			rc = -EINVAL;
+			ecryptfs_printk(0, KERN_WARNING, "Unsupported AES key "
+					"size: [%d]\n", 
+					crypt_stats->key_size_bits);
+			goto out;
+		}		
+	}
+	*(dest + (dest_offset++)) = rc;
+	rc = 0;
+	*(dest + (dest_offset++)) = 0x03;	/* S2K */
+	*(dest + (dest_offset++)) = 0x01;	/* MD5 (TODO) */
+	memcpy((dest + dest_offset), auth_tok->token.password.salt,
+	       ECRYPTFS_SALT_SIZE);
+	dest_offset += ECRYPTFS_SALT_SIZE;	/* salt */
+	*(dest + (dest_offset++)) = 0x60;	/* hash iterations */
+	memcpy((dest + dest_offset), key_rec.enc_key,
+	       key_rec.enc_key_size_bits / 8);
+	dest_offset += (key_rec.enc_key_size_bits / 8);
+	/* Write auth tok signature packet */
+	*(dest + (dest_offset++)) = 0xed;	/* tag 11 */
+	*(dest + (dest_offset++)) = ECRYPTFS_SIG_SIZE + 13;	/* packet
+								 * length */
+	*(dest + (dest_offset++)) = 0x62;	/* binary type */
+	*(dest + (dest_offset++)) = 0x08;	/* filename length */
+	strncpy((dest + dest_offset), "_CONSOLE", 0x08);
+	dest_offset += 0x08;
+	memset((dest + dest_offset), 0, 4);
+	dest_offset += 4;
+	memcpy((dest + dest_offset), key_rec.sig, ECRYPTFS_SIG_SIZE);
+	dest_offset += ECRYPTFS_SIG_SIZE;
+	*(dest + (dest_offset)) = 0x00;	/* NULL terminator */
+	rc = dest_offset;
+out:
+	if (tfm)
+		crypto_free_tfm(tfm);
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
+
+/**
+ * Generates a key packet set and writes it to the virtual address
+ * passed in.
+ *
+ * @param dest		Page to which to write the key record set
+ * @param crypt_stats	The cryptographic context from which the authentication
+ * 			tokens will be retrieved
+ * @param len           The amount written
+ * @return		Zero on success
+ */
+int
+ecryptfs_generate_key_packet_set(char *dest_base,
+				 struct ecryptfs_crypt_stats *crypt_stats,
+				 struct dentry *ecryptfs_dentry, int *len)
+{
+	int rc = 0;
+	struct ecryptfs_auth_tok *auth_tok;
+	int dest_offset = 0;
+	struct ecryptfs_mount_crypt_stats *mount_crypt_stats =
+	    &(ECRYPTFS_SUPERBLOCK_TO_PRIVATE(
+		      ecryptfs_dentry->d_sb)->mount_crypt_stats);
+	int written;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	(*len) = 0;
+	if (mount_crypt_stats->global_auth_tok) {
+		auth_tok = mount_crypt_stats->global_auth_tok;
+		switch (auth_tok->instanceof) {
+		case ECRYPTFS_PASSWORD:
+			written = write_tag_3_packet(dest_base, dest_offset,
+						     PAGE_CACHE_SIZE, auth_tok,
+						     crypt_stats);
+			break;
+		default:
+			ecryptfs_printk(0, KERN_WARNING, "Unknown "
+					"authentication token type [%d]\n",
+					auth_tok->instanceof);
+			rc = -EINVAL;
+			goto out;
+		}
+		if (written == 0) {
+			ecryptfs_printk(1, KERN_WARNING, "Error writing "
+					"authentication token packet with sig "
+					"= [%s]\n",
+					mount_crypt_stats->global_auth_tok_sig);
+			rc = -EIO;
+			goto out;
+		} else {
+			dest_offset += written;
+			(*len) += written;
+		}
+	} else
+		BUG();
+      out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
#28 lore raw

PATCH 12/12 Crypto functions

In reply to Phillip Hellewell

eCryptfs crypto functions. Scatterlist abstraction functions. Page encryption/decryption functions. Inode cryptographic context initialization functions. Header region manipulation functions. Functions in which filename and xattr encoding/decoding can be easily implemented.

---

 crypto.c |  955 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 955 insertions(+)
fs/ecryptfs/crypto.c +955 -0
@@ -0,0 +1,955 @@
+/**
+ * eCryptfs: Linux filesystem encryption layer
+ *
+ * Copyright (c) 1997-2004 Erez Zadok
+ * Copyright (c) 2001-2004 Stony Brook University
+ * Copyright (c) 2005 International Business Machines Corp.
+ *   Author(s): Michael A. Halcrow <[email protected]>
+ *   		Michael C. Thompson <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/pagemap.h>
+#include <linux/random.h>
+#include <linux/compiler.h>
+#include <linux/key.h>
+#include <linux/namei.h>
+#include <linux/crypto.h>
+#include <linux/file.h>
+#include <asm/scatterlist.h>
+#include "ecryptfs_kernel.h"
+
+/**
+ * Requirement:
+ *	Size of dst buffer needs to be atleast src_size * 2
+ */
+inline void ecryptfs_to_hex(char *dst, char *src, int src_size)
+{
+	int x;
+
+	for (x = 0; x < src_size; x++)
+		sprintf(&dst[x * 2], "%.2x", (unsigned char)src[x]);
+}
+
+/**
+ * Requirement:
+ * 	Size of src buffer needs to be atleast twice that of dst_size
+ */
+inline void ecryptfs_from_hex(char *dst, char *src, int dst_size)
+{
+	int x;
+	char tmp[3] = { 0, };
+
+	for (x = 0; x < dst_size; x++) {
+		tmp[0] = src[x * 2];
+		tmp[1] = src[x * 2 + 1];
+		dst[x] = (unsigned char)simple_strtol(tmp, NULL, 16);
+	}
+}
+
+static int iv_mixer;
+
+/**
+ * Rotate the initialization vector for an extent.  This stirs things
+ * up to help protect against linear cryptanalysis when an attacker
+ * may have access to several encryptions based on the same IV.
+ */
+void ecryptfs_rotate_iv(unsigned char *iv)
+{
+	int i = (ECRYPTFS_MAX_IV_BYTES - sizeof(iv_mixer));
+	int zero_test = 0;
+
+	while ((i -= sizeof(iv_mixer)) >= 0)
+		zero_test |= ((*((int *)(iv + i))) ^=
+			      (iv_mixer *= (*(int *)(iv + i))));
+	while (unlikely(!zero_test)) {
+		get_random_bytes(iv, ECRYPTFS_MAX_IV_BYTES);
+		zero_test = 0;
+		i = ECRYPTFS_MAX_IV_BYTES / sizeof(int);
+		while (i--)
+			zero_test |= *((int *)(iv + i));
+	}
+}
+
+/**
+ * Initialize the crypt_stats structure.  This involves setting an
+ * initial IV, indicating how many header pages we have on the file by
+ * default, initializing the list of raw authentication token packets
+ * (TODO: deprecated/replaced w/ auth_tok sigs pointing to keyring
+ * structures), setting the extent size (TODO: this is the page size;
+ * as it now stands, everything falls apart if the page size is
+ * anything but 4096), and finally setting a flag to indicate that the
+ * structure is initialized.
+ *
+ * @param crypt_stats Pointer to the crypt_stats struct to
+ *                    initialize.
+ */
+void ecryptfs_init_crypt_stats(struct ecryptfs_crypt_stats *crypt_stats)
+{
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	memset((void *)crypt_stats, 0, sizeof(struct ecryptfs_crypt_stats));
+	init_MUTEX(&crypt_stats->iv_sem);
+	down(&crypt_stats->iv_sem);
+	get_random_bytes(&crypt_stats->iv, ECRYPTFS_MAX_IV_BYTES);
+	up(&crypt_stats->iv_sem);
+	get_random_bytes(&iv_mixer, sizeof(iv_mixer));
+	crypt_stats->num_header_pages = 1;	/* TODO: Remove with policy */
+	crypt_stats->struct_initialized = 1;
+	ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+}
+
+/**
+ * Releases all memory associated with a crypt_stats struct.
+ */
+void ecryptfs_destruct_crypt_stats(struct ecryptfs_crypt_stats *crypt_stats)
+{
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	if (crypt_stats->tfm) {
+		crypto_free_tfm(crypt_stats->tfm);
+		crypt_stats->tfm = NULL;
+	}
+	ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+}
+
+/**
+ * Dump hexadecimal representation of char array
+ *
+ * @param data
+ * @param bytes
+ */
+void ecryptfs_dump_hex(char *data, int bytes)
+{
+	int i = 0;
+	int pretty_print = 1;
+
+	if (bytes != 0) {
+		printk(KERN_NOTICE "0x%.2x.", (unsigned char)data[i]);
+		i++;
+	}
+	while (i < bytes) {
+		printk("0x%.2x.", (unsigned char)data[i]);
+		i++;
+		if (i % 16 == 0) {
+			printk("\n");
+			pretty_print = 0;
+		} else
+			pretty_print = 1;
+	}
+	if (pretty_print)
+		printk("\n");
+}
+
+/**
+ * Fills in a scatterlist array with page references for a passed
+ * virtual address: James Morris
+ *
+ * @param addr Virtual address
+ * @param size Size of data; should be an even multiple of the block
+ *             size
+ * @param sg Pointer to scatterlist array
+ * @param sg_size Max array size
+ * @return Number of scatterlist structs in array used
+ */
+int virt_to_scatterlist(const void *addr, int size, struct scatterlist *sg,
+			int sg_size)
+{
+	int i = 0;
+	struct page *pg;
+	int offset;
+	int remainder_of_page;
+
+	while (size > 0 && i < sg_size) {
+		pg = virt_to_page(addr);
+		offset = offset_in_page(addr);
+		sg[i].page = pg;
+		sg[i].offset = offset;
+		remainder_of_page = PAGE_CACHE_SIZE - offset;
+		if (size >= remainder_of_page) {
+			sg[i].length = remainder_of_page;
+			addr += remainder_of_page;
+			size -= remainder_of_page;
+		} else {
+			sg[i].length = size;
+			addr += size;
+			size = 0;
+		}
+		i++;
+	}
+	if (size > 0)
+		return -ENOMEM;
+	return i;
+}
+
+/**
+ * @return Number of bytes encrypted; negative value on error
+ */
+static int do_encrypt_scatterlist(struct ecryptfs_crypt_stats *crypt_stats,
+				  struct scatterlist *dest_sg,
+				  struct scatterlist *src_sg, int size,
+				  unsigned char *iv)
+{
+	int rc = 0;
+
+	if (!crypt_stats || !crypt_stats->tfm
+	    || !crypt_stats->struct_initialized) {
+		ecryptfs_printk(0, KERN_ERR,
+				"Called w/ invalid crypt_stats state\n");
+		rc = -EINVAL;
+		goto out;
+	}
+	ecryptfs_printk(1, KERN_NOTICE, "Key size [%d]; key:\n",
+			crypt_stats->key_size_bits / 8);
+	if (ecryptfs_verbosity > 0)
+		ecryptfs_dump_hex(crypt_stats->key,
+				  crypt_stats->key_size_bits / 8);
+	rc = crypto_cipher_setkey(crypt_stats->tfm, crypt_stats->key,
+				  crypt_stats->key_size_bits / 8);
+	if (rc) {
+		ecryptfs_printk(0, KERN_ERR, "Error setting key; rc = [%d]\n",
+				rc);
+		rc = -EINVAL;
+		goto out;
+	}
+	ecryptfs_printk(1, KERN_NOTICE, "Encrypting [%d] bytes.\n", size);
+	if (crypt_stats->tfm->crt_cipher.cit_mode == CRYPTO_TFM_MODE_ECB) {
+		crypt_stats->security_warning = 1;
+		crypto_cipher_encrypt(crypt_stats->tfm, dest_sg, src_sg, size);
+	} else if (crypt_stats->tfm->crt_cipher.cit_mode
+		   == CRYPTO_TFM_MODE_CFB
+		   || crypt_stats->tfm->crt_cipher.cit_mode
+		   == CRYPTO_TFM_MODE_CBC)
+		crypto_cipher_encrypt_iv(crypt_stats->tfm, dest_sg, src_sg,
+					 size, iv);
+	else {
+		ecryptfs_printk(0, KERN_ERR,
+				"Unsupported block cipher mode: [%d]\n",
+				crypt_stats->tfm->crt_cipher.cit_mode);
+		rc = -ENOSYS;
+		goto out;
+	}
+	rc = size;
+	/* TODO: crypt_stats->iv size must be equal to the block size;
+	 * verify this*/
+out:
+	return rc;
+}
+
+int do_encrypt_page_offset(struct ecryptfs_crypt_stats *crypt_stats,
+			   struct page *dest_page, int dest_offset,
+			   struct page *src_page, int src_offset, int size,
+			   unsigned char *iv)
+{
+	struct scatterlist src_sg[2], dest_sg[2];
+
+	ecryptfs_printk(1, KERN_NOTICE, "Called with dest_page->index = [%lu], "
+			"src_page->index = [%lu], dest_offset = [%d], "
+			"src_offset = [%d]\n", dest_page->index,
+			src_page->index, dest_offset, src_offset);
+	src_sg[0].page = src_page;
+	src_sg[0].offset = src_offset;
+	src_sg[0].length = size;
+	dest_sg[0].page = dest_page;
+	dest_sg[0].offset = dest_offset;
+	dest_sg[0].length = size;
+	return do_encrypt_scatterlist(crypt_stats, dest_sg, src_sg, size, iv);;
+}
+
+int
+do_encrypt_page(struct ecryptfs_crypt_stats *crypt_stats,
+		struct page *dest_page, struct page *src_page,
+		unsigned char *iv)
+{
+	ecryptfs_printk(1, KERN_NOTICE, "Called with dest_page->index = [%lu] "
+			"and src_page->index = [%lu]\n", dest_page->index,
+			src_page->index);
+	return do_encrypt_page_offset(crypt_stats, dest_page, 0, src_page, 0,
+				      PAGE_CACHE_SIZE, iv);
+}
+
+/**
+ * Encrypt from a virtual address to a virtual address.
+ * 
+ * @return 
+ */
+int do_encrypt_virt(struct ecryptfs_crypt_stats *crypt_stats,
+		    char *dest_virt_addr, const char *src_virt_addr,
+		    int size, unsigned char *iv)
+{
+	/* TODO: 32 is a magic number */
+	struct scatterlist src_sg[32];
+	struct scatterlist dest_sg[32];
+	int rc;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Source:\n");
+	if (unlikely(ecryptfs_verbosity > 0))
+		ecryptfs_dump_hex((char *)src_virt_addr, size);
+	rc = virt_to_scatterlist(src_virt_addr, size, src_sg, 32);
+	if (rc == -ENOMEM) {
+		ecryptfs_printk(0, KERN_ERR, "do_encrypt_virt: No memory for "
+				"this operation\n");
+		goto out;
+
+	}
+	rc = virt_to_scatterlist(dest_virt_addr, size, dest_sg, 32);
+	if (rc == -ENOMEM) {
+		ecryptfs_printk(0, KERN_ERR, "do_encrypt_virt: No memory for "
+				"this operation\n");
+		goto out;
+	}
+	rc = do_encrypt_scatterlist(crypt_stats, dest_sg, src_sg, size, iv);
+	ecryptfs_printk(1, KERN_NOTICE, "Destination:\n");
+	if (unlikely(ecryptfs_verbosity > 0))
+		ecryptfs_dump_hex((char *)dest_virt_addr, size);
+out:
+	return rc;
+}
+
+/**
+ * @return Number of bytes decrypted; negative value on error
+ */
+static int do_decrypt_scatterlist(struct ecryptfs_crypt_stats *crypt_stats,
+				  struct scatterlist *dest_sg,
+				  struct scatterlist *src_sg, int size,
+				  unsigned char *iv)
+{
+	int rc = 0;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	/* TODO: This should be done when the file is opened */
+	ecryptfs_printk(1, KERN_NOTICE, "Key size [%d]; key:\n",
+			crypt_stats->key_size_bits / 8);
+	if (unlikely(ecryptfs_verbosity > 0))
+		ecryptfs_dump_hex(crypt_stats->key,
+				  crypt_stats->key_size_bits / 8);
+	rc = crypto_cipher_setkey(crypt_stats->tfm, crypt_stats->key,
+				  crypt_stats->key_size_bits / 8);
+	if (rc) {
+		ecryptfs_printk(0, KERN_ERR, "Error setting key; rc = [%d]\n",
+				rc);
+		rc = -EINVAL;
+		goto out;
+	}	
+	ecryptfs_printk(1, KERN_NOTICE, "Decrypting [%d] bytes.\n", size);
+	if (crypt_stats->tfm->crt_cipher.cit_mode == CRYPTO_TFM_MODE_ECB) {
+		rc = crypto_cipher_decrypt(crypt_stats->tfm, dest_sg, src_sg, 
+					   size);
+		
+	} else if (crypt_stats->tfm->crt_cipher.cit_mode
+		   == CRYPTO_TFM_MODE_CFB
+		   || crypt_stats->tfm->crt_cipher.cit_mode
+		   == CRYPTO_TFM_MODE_CBC) {
+		rc = crypto_cipher_decrypt_iv(crypt_stats->tfm, dest_sg, src_sg,
+					      size, iv);
+		if (rc) {
+			ecryptfs_printk(0, KERN_ERR, "Error decrypting; rc = "
+					"[%d]\n", rc);
+			goto out;
+		}
+	} else {
+		ecryptfs_printk(0, KERN_ERR,
+				"Unsupported block cipher mode: [%d]\n",
+				crypt_stats->tfm->crt_cipher.cit_mode);
+		rc = -ENOSYS;
+		goto out;
+	}
+	rc = size;
+ out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
+
+/**
+ * @return Number of bytes decrypted
+ */
+int do_decrypt_page_offset(struct ecryptfs_crypt_stats *crypt_stats,
+			   struct page *dest_page, int dest_offset,
+			   struct page *src_page, int src_offset, int size,
+			   unsigned char *iv)
+{
+	struct scatterlist src_sg[2], dest_sg[2];
+
+	src_sg[0].page = src_page;
+	src_sg[0].offset = src_offset;
+	src_sg[0].length = size;
+	dest_sg[0].page = dest_page;
+	dest_sg[0].offset = dest_offset;
+	dest_sg[0].length = size;
+	return do_decrypt_scatterlist(crypt_stats, dest_sg, src_sg, size, iv);;
+}
+
+int
+do_decrypt_page(struct ecryptfs_crypt_stats *crypt_stats,
+		struct page *dest_page, struct page *src_page,
+		unsigned char *iv)
+{
+	return do_decrypt_page_offset(crypt_stats, dest_page, 0, src_page, 0,
+				      PAGE_CACHE_SIZE, iv);
+}
+
+#define ECRYPTFS_MAX_SCATTERLIST_LEN 4
+
+int do_decrypt_virt(struct ecryptfs_crypt_stats *crypt_stats,
+		    char *dest_virt_addr, const char *src_virt_addr,
+		    int size, unsigned char *iv)
+{
+	struct scatterlist src_sg[ECRYPTFS_MAX_SCATTERLIST_LEN];
+	struct scatterlist dest_sg[ECRYPTFS_MAX_SCATTERLIST_LEN];
+	int rc;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Source:\n");
+	if (ecryptfs_verbosity > 0)
+		ecryptfs_dump_hex((char *)src_virt_addr, size);
+	rc = virt_to_scatterlist(src_virt_addr, size, src_sg,
+				 ECRYPTFS_MAX_SCATTERLIST_LEN);
+	if (rc == -ENOMEM) {
+		ecryptfs_printk(0, KERN_ERR, "do_decrypt_virt: No memory for "
+				"this operation\n");
+		goto out;
+
+	}
+	rc = virt_to_scatterlist(dest_virt_addr, size, dest_sg,
+				 ECRYPTFS_MAX_SCATTERLIST_LEN);
+	if (rc == -ENOMEM) {
+		ecryptfs_printk(0, KERN_ERR, "do_decrypt_virt: No memory for "
+				"this operation\n");
+		goto out;
+	}
+	rc = do_decrypt_scatterlist(crypt_stats, dest_sg, src_sg, size, iv);
+	ecryptfs_printk(1, KERN_NOTICE, "Destination:\n");
+	if (unlikely(ecryptfs_verbosity > 0))
+		ecryptfs_dump_hex((char *)dest_virt_addr, size);
+out:
+	return rc;
+}
+
+/**
+ * Initialize the crypto context
+ *
+ * TODO: Performance: Keep a cache of initialized cipher contexts;
+ * only init if needed
+ */
+int ecryptfs_init_crypt_ctx(struct ecryptfs_crypt_stats *crypt_stats)
+{
+	int rc = -EINVAL;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	if (crypt_stats->cipher == NULL) {
+		ecryptfs_printk(1, KERN_NOTICE, "No cipher specified\n");
+		goto out;
+	}
+	ecryptfs_printk(1, KERN_NOTICE,
+			"Initializing cipher [%s]; strlen = [%d]\n",
+			crypt_stats->cipher, (int)strlen(crypt_stats->cipher));
+	if (crypt_stats->tfm != NULL) {
+		ecryptfs_printk(1, KERN_WARNING, "Crypto context already "
+				"initialized\n");
+		goto out;
+	}
+	crypt_stats->tfm = crypto_alloc_tfm(crypt_stats->cipher,
+					    CRYPTO_TFM_MODE_CBC);
+	if (crypt_stats->tfm == NULL) {
+		ecryptfs_printk(0, KERN_ERR, "cryptfs: init_crypt_ctx(): Error "
+				"initializing cipher [%s]\n",
+				crypt_stats->cipher);
+		goto out;
+	}
+	rc = 0;
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
+
+static inline pgoff_t records_per_page(struct ecryptfs_crypt_stats *crypt_stats)
+{
+	return (crypt_stats->extent_size / crypt_stats->iv_bytes);
+}
+
+void ecryptfs_set_default_sizes(struct ecryptfs_crypt_stats *crypt_stats)
+{
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	/* Default values; may be overwritten as we are parsing the
+	 * packets. */
+	crypt_stats->extent_size = PAGE_SIZE;
+	crypt_stats->iv_bytes = ECRYPTFS_DEFAULT_IV_BYTES;
+	crypt_stats->records_per_page = records_per_page(crypt_stats);
+	ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+}
+
+/**
+ * Default values in the event that policy does not override them.
+ */
+static void
+ecryptfs_set_default_crypt_stats_vals(struct ecryptfs_crypt_stats *crypt_stats)
+{
+	int key_size_bits = ECRYPTFS_DEFAULT_KEY_BYTES * 8;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	strcpy(crypt_stats->cipher, ECRYPTFS_DEFAULT_CIPHER);
+	get_random_bytes(crypt_stats->key, key_size_bits / 8);
+	crypt_stats->key_size_bits = key_size_bits;
+	crypt_stats->key_valid = 1;
+	ecryptfs_printk(1, KERN_NOTICE, "Generated new session key:\n");
+	if (unlikely(ecryptfs_verbosity > 0))
+		ecryptfs_dump_hex(crypt_stats->key,
+				  crypt_stats->key_size_bits / 8);
+	ecryptfs_set_default_sizes(crypt_stats);
+	ecryptfs_printk(1, KERN_NOTICE, "Exit\n");
+}
+
+/**
+ * If the crypto context for the file has not yet been established,
+ * this is where we do that.  Establishing a new crypto context
+ * involves the following decisions:
+ *  - What cipher to use?
+ *  - What set of authentication tokens to use?
+ * Here we just worry about getting enough information into the
+ * authentication tokens so that we know that they are available.
+ * We associate the available authentication tokens with the new file
+ * via the set of signatures in the crypt_stats struct.  Later, when
+ * the headers are actually written out, we may again defer to
+ * userspace to perform the encryption of the session key; for the
+ * foreseeable future, this will be the case with public key packets.
+ *
+ * @param ecryptfs_dentry
+ * @return Zero on success; non-zero otherwise
+ */
+/* Associate an authentication token(s) with the file */
+int ecryptfs_new_file_context(struct dentry *ecryptfs_dentry)
+{
+	int rc = 0;
+	struct ecryptfs_crypt_stats *crypt_stats =
+	    &ECRYPTFS_INODE_TO_PRIVATE(ecryptfs_dentry->d_inode)->crypt_stats;
+	struct ecryptfs_mount_crypt_stats *mount_crypt_stats =
+	    &(ECRYPTFS_SUPERBLOCK_TO_PRIVATE(
+		      ecryptfs_dentry->d_sb)->mount_crypt_stats);
+	int cipher_name_len;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	ecryptfs_set_default_crypt_stats_vals(crypt_stats);
+	/* See if there are mount crypt options */
+	if (mount_crypt_stats->global_auth_tok) {
+		ecryptfs_printk(1, KERN_NOTICE, "Initializing context for new "
+				"file using mount_crypt_stats\n");
+		crypt_stats->encrypted = 1;
+		crypt_stats->key_valid = 1;
+		memcpy(crypt_stats->keysigs[crypt_stats->num_keysigs++],
+		       mount_crypt_stats->global_auth_tok_sig,
+		       ECRYPTFS_SIG_SIZE_HEX);
+		cipher_name_len =
+		    strlen(mount_crypt_stats->global_default_cipher_name);
+		memcpy(crypt_stats->cipher,
+		       mount_crypt_stats->global_default_cipher_name,
+		       cipher_name_len);
+		crypt_stats->cipher[cipher_name_len] = '\0';
+	} else
+		/* We should not encounter this scenario since we
+		 * should detect lack of global_auth_tok at mount time
+		 * TODO: Applies to 0.1 release only; remember to
+		 * remove in future release */
+		BUG();
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
+
+/**
+ * @return One if marker found; zero if not found
+ */
+int contains_ecryptfs_marker(char *data)
+{
+	u32 m_1, m_2, ver;
+	
+	memcpy(&m_1, data, 4);
+	memcpy(&m_2, (data + 4), 4);
+	ver = (m_2 ^ (m_1 ^ MAGIC_ECRYPTFS_MARKER));
+	/* There is a 2^(32-4) chance for each file that a
+	 * non-eCryptfs file will be start to be mistaken for an
+	 * eCryptfs file */
+	if (ver > 15)
+		ver = 0;
+	return (int)ver;
+}
+
+/**
+ * Marker = 0x3c81b7f5
+ */
+static int write_ecryptfs_marker(char *page_virt)
+{
+	u32 m_1, m_2, ver;
+	int version = 1;
+
+	ver = (u32)version;
+	get_random_bytes(&m_1, (MAGIC_ECRYPTFS_MARKER_SIZE_BYTES / 2));
+	memcpy(page_virt, &m_1, (MAGIC_ECRYPTFS_MARKER_SIZE_BYTES / 2));
+	m_2 = ((m_1 ^ ver) ^ MAGIC_ECRYPTFS_MARKER);
+	memcpy(page_virt + (MAGIC_ECRYPTFS_MARKER_SIZE_BYTES / 2), &m_2,
+	       (MAGIC_ECRYPTFS_MARKER_SIZE_BYTES / 2));
+	return MAGIC_ECRYPTFS_MARKER_SIZE_BYTES;
+}
+
+/**
+ * @return Zero on no match
+ */
+u16 ecryptfs_code_for_cipher_string(char *str)
+{
+	u16 rc = 0;
+	if (strcmp(str, "des3_ede") == 0) {
+		rc = 0x02;
+	} else if (strcmp(str, "cast5") == 0) {
+		rc = 0x03;
+	} else if (strcmp(str, "blowfish") == 0) {
+		rc = 0x04;
+	} else if (strcmp(str, "aes") == 0) {
+		rc = 0x07;
+	}
+	return rc;
+}
+
+/**
+ * @return Zero on success
+ */
+int ecryptfs_cipher_code_to_string(char *str, u16 cipher_code)
+{
+	int rc = 0;
+	switch(cipher_code) {
+	case 0x01:
+		/* IDEA not supported */
+		str[0] = '\0';
+		ecryptfs_printk(0, KERN_WARNING, "Cipher code not supported: "
+				"[%d]\n", cipher_code);
+		rc = -ENOSYS;
+		break;
+	case 0x02:
+		/* Choose Triple-DES */
+		strcpy(str, "des3_ede");
+		break;
+	case 0x03:
+		/* Choose CAST5 */
+		strcpy(str, "cast5");
+		break;
+	case 0x04:
+		/* Choose blowfish */
+		strcpy(str, "blowfish");
+		break;
+	case 0x07:
+		/* Choose AES-128 */
+		strcpy(str, "aes");
+		break;
+	case 0x08:
+		/* Choose AES-192 */
+		strcpy(str, "aes");
+		break;
+	case 0x09:
+		/* Choose AES-256 */
+		strcpy(str, "aes");
+		break;
+	default:
+		str[0] = '\0';
+		ecryptfs_printk(0, KERN_WARNING, "Cipher code not recognized: "
+				"[%d]\n", cipher_code);		
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+/**
+ * @return Zero on success; non-zero otherwise
+ */
+int ecryptfs_read_header_region(char *data, struct dentry *dentry,
+				struct nameidata *nd)
+{
+	int rc = 0;
+	struct vfsmount *mnt = NULL;
+	struct file *file = NULL;
+	mm_segment_t oldfs;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	mnt = mntget(nd->mnt);
+	file = dentry_open(dentry, mnt, O_RDONLY);
+	if (IS_ERR(file)) {
+		ecryptfs_printk(1, KERN_NOTICE, "Error opening file to "
+				"determine interpolated filesize\n");
+		mntput(mnt);
+		rc = PTR_ERR(file);
+		goto out;
+	}
+	if (!file || !file->f_op || !file->f_op->read) {
+		ecryptfs_printk(1, KERN_NOTICE, "File has no read op\n");
+		rc = -EINVAL;
+		goto out;
+	}
+	file->f_pos = 0;
+	oldfs = get_fs();
+	set_fs(get_ds());
+	rc = file->f_op->read(file, (char __user *)data,
+			      PAGE_CACHE_SIZE, &file->f_pos);
+	set_fs(oldfs);
+	fput(file);
+	rc = 0;
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n",rc);
+	return rc;
+}
+
+kmem_cache_t *ecryptfs_header_cache_0;
+kmem_cache_t *ecryptfs_header_cache_1;
+kmem_cache_t *ecryptfs_header_cache_2;
+
+/**
+ * The file size is written along a different execution path than the
+ * rest of the headers; we treat it as a special case in eCryptfs.
+ *
+ * Proposed header format is in flux; this is the current proposal for
+ * later versions of eCryptfs, for compatibility:
+ *
+ * BYTE RANGE: DESCRIPTION
+ * -----------------------
+ * 00 - 07: File size
+ * 08 - 11: Random value (M_1)
+ * 12 - 15: M_1 ^ VER ^ MARKER
+ * 16 - 16: Hash identifier
+ * 17 - 18: Cipher identifier
+ * 19 - 19: Number of header pages
+ * 20 - 63: Reserved
+ * 64 - ..: RFC2440 packet set
+ *
+ * @return Zero on success
+ */
+int ecryptfs_write_headers_virt(char *page_virt, 
+				struct ecryptfs_crypt_stats *crypt_stats,
+				struct dentry *ecryptfs_dentry, int version)
+{
+	int ecryptfs_marker_len;
+	int rc = 0;
+	int written;
+	int offset;
+
+	offset = ECRYPTFS_FILE_SIZE_BYTES;
+	ecryptfs_marker_len = write_ecryptfs_marker(page_virt + offset);
+	offset += ecryptfs_marker_len;
+	rc = ecryptfs_generate_key_packet_set((page_virt + offset), crypt_stats,
+					      ecryptfs_dentry, &written);
+	if (rc)
+		ecryptfs_printk(0, KERN_WARNING, "Error generating key packet "
+				"set; rc = [%d]\n", rc);
+	return rc;
+}
+
+/**
+ * Write the file headers out.  This will likely involve a userspace
+ * callout, in which the session key is encrypted with one or more
+ * public keys and/or the passphrase necessary to do the encryption is
+ * retrieved via a prompt.  Exactly what happens at this point should
+ * be policy-dependent.
+ *
+ * @param lower_file The lower file struct, which was returned from
+ * dentry_open
+ * @return Zero on success; non-zero on error
+ */
+int ecryptfs_write_headers(struct dentry *ecryptfs_dentry,
+			   struct file *lower_file)
+{
+	int rc = 0;
+	char *page_virt;
+	struct ecryptfs_crypt_stats *crypt_stats;
+	mm_segment_t oldfs;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	crypt_stats = &ECRYPTFS_INODE_TO_PRIVATE(
+		ecryptfs_dentry->d_inode)->crypt_stats;
+	if (likely(1 == crypt_stats->encrypted)) {
+		if (!crypt_stats->key_valid) {
+			ecryptfs_printk(1, KERN_NOTICE, "Key is "
+					"invalid; bailing out\n");
+			rc = -EINVAL;
+			goto out;
+		}
+	} else {
+		rc = -EINVAL;
+		ecryptfs_printk(0, KERN_WARNING,
+				"Called with crypt_stats->encrypted == 0\n");
+		goto out;
+	}
+	/* Released in this function */
+	page_virt = kmem_cache_alloc(ecryptfs_header_cache_0, SLAB_USER);
+	if (!page_virt) {
+		ecryptfs_printk(0, KERN_ERR, "Out of memory\n");
+		return -ENOMEM;
+	}
+	rc = ecryptfs_write_headers_virt(page_virt, crypt_stats, 
+					 ecryptfs_dentry, 
+					 ECRYPTFS_FILE_VERSION);
+	if (unlikely(rc != 0)) {
+		ecryptfs_printk(0, KERN_ERR, "Error whilst writing headers\n");
+		goto out_free;
+	}
+	rc = 0;
+	ecryptfs_printk(1, KERN_NOTICE,
+			"Writing key packet set to underlying file\n");
+	lower_file->f_pos = 0;
+	oldfs = get_fs();
+	set_fs(get_ds());
+	lower_file->f_op->write(lower_file, (char __user *)page_virt,
+				PAGE_CACHE_SIZE, &lower_file->f_pos);
+	set_fs(oldfs);
+	ecryptfs_printk(1, KERN_NOTICE,
+			"Done writing key packet set to underlying file.\n");
+out_free:
+	kmem_cache_free(ecryptfs_header_cache_0, page_virt);
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
+
+/**
+ * @return Zero on success
+ */
+int ecryptfs_read_headers_virt(char *page_virt,
+			       struct ecryptfs_crypt_stats* crypt_stats,
+			       struct dentry *ecryptfs_dentry)
+{
+	int rc = 0;
+	int offset;
+	int version;
+
+	offset = ECRYPTFS_FILE_SIZE_BYTES;
+	version = contains_ecryptfs_marker(page_virt + offset);
+	if (version == 0)
+		ecryptfs_printk(0, KERN_WARNING, "Valid eCryptfs marker not "
+				"found\n");
+	offset += MAGIC_ECRYPTFS_MARKER_SIZE_BYTES;
+	rc = ecryptfs_parse_packet_set((page_virt + offset), crypt_stats,
+				       ecryptfs_dentry, version);
+	return rc;
+}
+
+/**
+ * @return	Zero if valid headers found and parsed; non-zero otherwise
+ */
+int ecryptfs_read_headers(struct dentry *ecryptfs_dentry,
+			  struct file *lower_file)
+{
+	int rc = 0;
+	char *page_virt;
+	mm_segment_t oldfs;
+	ssize_t bytes_read;
+	struct ecryptfs_crypt_stats *crypt_stats =
+	    &ECRYPTFS_INODE_TO_PRIVATE(ecryptfs_dentry->d_inode)->crypt_stats;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+	/* Read the first page from the underlying file */
+	page_virt = kmem_cache_alloc(ecryptfs_header_cache_1, SLAB_USER);
+	if (!page_virt) {
+		rc = -ENOMEM;
+		ecryptfs_printk(0, KERN_ERR, "Unable to allocate page_virt\n");
+		goto out;
+	}
+	lower_file->f_pos = 0;
+	oldfs = get_fs();
+	set_fs(get_ds());
+	bytes_read =
+	    lower_file->f_op->read(lower_file, (char __user *)page_virt,
+				   PAGE_CACHE_SIZE, &lower_file->f_pos);
+	set_fs(oldfs);
+	if (bytes_read != PAGE_CACHE_SIZE) {
+		rc = -EINVAL;
+		ecryptfs_printk(0, KERN_ERR, "Expected size of header not read."
+				"Instead [%d] bytes were read\n", bytes_read);
+		goto out;
+	}
+	rc = ecryptfs_read_headers_virt(page_virt, crypt_stats,
+					ecryptfs_dentry);
+	kmem_cache_free(ecryptfs_header_cache_1, page_virt);
+	if (rc) {
+		ecryptfs_printk(1, KERN_NOTICE, "File not encrypted\n");
+		rc = -EINVAL;
+		goto out;
+	}
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+	return rc;
+}
+
+/**
+ * N.B. The concept of encoded filenames does not apply for 0.1 release
+ *
+ * Encrypts and encodes a filename into something that constitutes a
+ * valid filename for a filesystem, with printable characters.
+ *
+ * We assume that we have a properly initialized crypto context,
+ * pointed to by crypt_stats->tfm.
+ *
+ * TODO: Implement filename encryption and encoding here, in place of
+ * memcpy.
+ *
+ * @return	Length of encoded filename; negative if error
+ */
+int
+ecryptfs_encode_filename(const char *name, int length, char **encoded_name,
+			 int skip_dots,
+			 struct ecryptfs_crypt_stats *crypt_stats)
+{
+	int error = 0;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; length = [%d]\n", length);
+	(*encoded_name) = kmalloc(length + 2, GFP_KERNEL);
+	if (!(*encoded_name)) {
+		error = -ENOMEM;
+		goto out;
+	}
+	memcpy((void *)(*encoded_name), (void *)name, length);
+	(*encoded_name)[length] = '\0';
+	error = length + 1;
+      out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; error = [%d]\n", error);
+	return error;
+}
+
+/**
+ * N.B. The concept of encoded filenames does not apply for 0.1 release
+ *
+ * Decrypts and decodes the filename
+ *
+ * TODO: Implement filename decoding and decryption here, in place of
+ * memcpy.
+ *
+ * @return	Length of decoded filename; negative if error
+ */
+int
+ecryptfs_decode_filename(const char *name, int length, char **decrypted_name,
+			 int skip_dots,
+			 struct ecryptfs_crypt_stats *crypt_stats)
+{
+	int error = 0;
+
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; length = [%d]\n", length);
+	(*decrypted_name) = kmalloc(length + 2, GFP_KERNEL);
+	if (!(*decrypted_name)) {
+		error = -ENOMEM;
+		goto out;
+	}
+	memcpy((void *)(*decrypted_name), (void *)name, length);
+	(*decrypted_name)[length + 1] = '\0';	/* Only for convenience
+						 * in printing out the
+						 * string in debug
+						 * messages */
+	error = length;
+out:
+	ecryptfs_printk(1, KERN_NOTICE, "Exit; error = [%d]\n", error);
+	return error;
+}
#29 lore raw
In reply to Phillip Hellewell
Quoted 3 lines · Phillip Hellewell

eCryptfs

If Linux is going to offer a feature like this then people have to be able to trust it to be quite secure. What we don't want to happen is to distribute it for six months and then be buried in reports of vulnerabilites from cryptography specialists. Even worse if those reports lead to exploits.

So I guess what I'm asking is: has this code been reviewed by crypto experts? Bearing in mind that it'll be world-class crypto people who will try to poke holes in it.

#30 lore raw
Quoted 5 lines · Andrew Morton

If Linux is going to offer a feature like this then people have to be able to trust it to be quite secure. What we don't want to happen is to distribute it for six months and then be buried in reports of vulnerabilites from cryptography specialists. Even worse if those reports lead to exploits.

I think you brought up two categories of potential security vulnerabilities. The first has to do with the theoretical security of the algorithms -- do the encrypted files really have the attribute such that decrypting the files without the proper key is computationally infeasible? This is the job for the cryptographers to confront.

The other category has to do with ``exploits''; I assume you are talking about -- for instance -- malicious files that are able to circumvent the intended behavior of the code. Such vulnerabilities may coerce the filesystem to dump the secret key out to an insecure location. This is an extension of the general ``correctness'' problem that can be an issue with any code. I would say that this is the job of the engineers to help prevent. It basically involves verification that eCryptfs is handling all of its memory correctly (i.e., via data and control flow analysis).

Quoted 3 lines

So I guess what I'm asking is: has this code been reviewed by crypto experts? Bearing in mind that it'll be world-class crypto people who will try to poke holes in it.

Let's start with the first issue: the theoretical algorithmic security of eCryptfs. The good news is that there is no new crypto in the filesystem; we use only existing algorithms. Of course, these algorithms need to be used correctly in order for them to be of any use at all (key management typically being the weak point, in my opinion, which is why the roadmap for eCryptfs incorporates robust policy-based key management). I give a list of assumptions with regard to these algorithms further down.

I have thought long and hard about how I am putting it all together and have presented the design (and my justifications) to several audiences over the last two years, including crypto researchers, and nobody has complained (thus far). For those just tuning in, my paper starts on page 209 of the first half of the OLS 2005 proceedings:

http://www.linuxsymposium.org/2005/linuxsymposium_procv1.pdf

I am actually trying to publicize the code as much as possible at this point for that exact reason -- so more security and cryptography experts can analyze our current eCryptfs implementation to criticize it. I am interested in knowing (a) how well my design holds up and (b) how well my implementation reflects my design.

The code correctness problem necessitates a significant engineering effort, given that eCryptfs occupies several thousand lines of new code. We have been practicing due diligence in having frequent code reviews over the last year, and we have used various static source code analysis tools to help out in pinpointing potential problems. We have a structured internal process of peer review before committing any changes to the code base. We are making every effort to use our development resources to ferret out correctness flaws, but community involvement would help quicken the pace. By submitting our code for inclusion in the kernel, we are now asking for many eyes to help make any remaining bugs in eCryptfs shallow.

Keep in mind that eCryptfs is one piece that takes part in confronting a very large and complex general security problem. Up to this point, I have been focusing my efforts on the scenario wherein the physical media is separated from its trusted host environment; my primary goal is that the sensitive data on the storage media, when separated from the control of the trusted host, will be unreadable without the proper key to decrypt it.

There are many other things that should be done to ensure access control-type security on the trusted host system itself; that sort of security must be coordinated with the distro's to provide all the desirable attributes of a comprehensively secure deployment. Basically, I am stating that, assuming perfect correctness, the problem of an access control violation on the host causing weakly- or non-encrypted data to be written to disk, or causing the key to be compromised, is outside the scope of eCryptfs's specific function, which focuses on data-at-rest outside the control of the host. It is the ``assuming perfect correctness'' part that needs verification at this point. :-)

The express job of eCryptfs is to make sure that if the media is separated from the trusted host environment, then the confidentiality of the data on the media is enforced cryptographically. In later updates to eCryptfs, we will also include integrity verification to make sure that unauthorized modifications to the underlying data were not made outside the control of the trusted host. This is easily accomplished with another standard cryptographic construct; HMAC values can simply be written side-by-side with IV's on a per-extent basis.

That said, I am certainly not opposed to exploring how eCryptfs can coordinate with other access control mechanisms (i.e., SE Linux) to help prevent access control violations on the host. Another example that springs to mind, in terms of cooperation with MAC, is an SE Linux-eCryptfs policy that states, ``Any file object written to location ABC must be encrypted with crypto context XYZ.''

Now, by way of plugging together existing crypto constructs, I suppose it is time to state my assumptions:

1) The kernel get_random_bytes() call always provides a random number suitable for use as a symmetric Session Key.

2) The symmetric ciphers in the kernel are cryptographically secure according to current industry norms and expectations in relation to key length (if not, then eCryptfs will need to include a ``known good'' cipher list to compare the requested cipher name against).

3) It is still the case that the Initialization Vector does not need to be secret and that Cipher Block Chaining (CBC) mode is secure, when used over 4096-byte chunks of data. The one thing I would like feedback on is my IV permutation code; I am wondering if it will be necessary to enforce a Feistel network-like property in each permutation of the IV as we walk down the extents, or if a simple increment (or other naive transformation) of the IV is sufficient for each successive extent. Comments?

4) The header file format in RFC2440 (OpenPGP) is secure for the purpose of encrypting and storing the encrypted Session Key.

5) Knowledge of the number of bytes in the decrypted version of the file is not significantly helpful in cryptanalysis.

6) Users will select cryptographically strong passphrases (note that later versions of eCryptfs will use public key and TPM technology).

This list of assumptions will likely grow as eCryptfs undergoes more scrutiny. Of these assumptions, I would say that assumption 6 is the weakest; in version 0.1 of eCryptfs, we do allow for a salt to be specified by the user, but that reduces to basically adding more characters to the passphrase (we could have hacked it and included a randomly-generated salt in a mount-associated structure, but that would violate the per-file crypto context strategy of the filesystem in general). Future versions of eCryptfs, which will have per-file passphrase application rather than mount-wide, will use more intelligent salting to help mitigate the weak passphrase problem. Ultimately, though, the ``right'' solution is to depend on more than just a passphrase for protection, and that is part of the roadmap for eCryptfs.

There are, of course, some things that users could do that might make their files easier to attack by way of cryptanalysis. Layering encryption is one such thing (i.e., copying an already encrypted file with cipher XYZ into an eCryptfs mount point that is using that same cipher (and key)). This is a general problem w/ all crypto applications though.

Basically, I am asserting that, as long as my assumptions are valid, eCryptfs accomplishes its goal of cryptographically enforcing confidentiality of data at rest on secondary storage. There are plenty of things that users can (and, unfortunately, will) do to compromise the cryptographic security afforded by any application, and so that problem deserves at least as much attention as the cryptographic properties of the filesystem.

That is one of the reasons why I have been emphasizing making the key management process as transparent to the user as possible, so that good practices can be enacted while being as unobtrusive to the user as possible, so that the crypto is not circumvented or disabled by the user in frustration (this is likely the biggest security risk in any crypto app deployment). Once policy support is in place, nothing is stopping the user from doing something silly like selecting DES with a key size of 56 bits, and eCryptfs will need to have a way of warning the user when the security of the filesystem may be in question (sanity-checking policies against ``best practices'' standards).

That said, there are things that the eCryptfs userspace mount wrapper could do to help the user out in the version 0.1 release, like do a standard weak-passphrase check against the passphrase given to the mount wrapper (actually, now that I think about it, I see no reason why the eCryptfs mount wrapper could not use PAM). My recommendation right now is that the user generate a cryptographically random string of printable bytes for the passphrase, having a sufficient length so as to be, given the base of valid passphrase characters, sufficient in size given the keyspace for the cipher being used. In other words, the problem of brute-forcing the Session Key and brute-forcing the passphrase should be computationally equivalent.

In later versions of eCryptfs, there will be public key and TPM support. Then, once policy support is in place and eCryptfs is being shipped as part of the distro (making proper use of a TPM), the whole package will be even more secure and transparent to the user experience. One step at a time, though. I would like to get the kinks worked out of the basic passphrase-based modes of operation (especially correct and stable VFS-related operation) before adding more complexity to eCryptfs.

Thanks,
Mike

#31 lore raw
Quoted 9 lines · Michael Halcrow

I think you brought up two categories of potential security vulnerabilities.

The first has to do with the theoretical security of
the algorithms -- do the encrypted files really have the attribute such that decrypting the files without the proper key is computationally infeasible? This is the job for the cryptographers to confront.

Quoted 9 lines

The other category has to do with ``exploits''; I assume you are talking about -- for instance -- malicious files that are able to circumvent the intended behavior of the code. Such vulnerabilities may coerce the filesystem to dump the secret key out to an insecure location. This is an extension of the general ``correctness'' problem that can be an issue with any code. I would say that this is the job of the engineers to help prevent. It basically involves verification that eCryptfs is handling all of its memory correctly (i.e., via data and control flow analysis).

There's a third important category: the design of the _system_.

(Which you end up discussing somewhat further in the email).

It would be great to have a document which describes the design of the system and includes a comprehensive security analysis.

- James

Signature · 2 lines
James Morris
<[email protected]>
#32 lore raw
Quoted 21 lines · James Morris
Quoted 9 lines · Michael Halcrow

I think you brought up two categories of potential security vulnerabilities.

The first has to do with the theoretical security of
the algorithms -- do the encrypted files really have the attribute such that decrypting the files without the proper key is computationally infeasible? This is the job for the cryptographers to confront.

Quoted 9 lines

The other category has to do with ``exploits''; I assume you are talking about -- for instance -- malicious files that are able to circumvent the intended behavior of the code. Such vulnerabilities may coerce the filesystem to dump the secret key out to an insecure location. This is an extension of the general ``correctness'' problem that can be an issue with any code. I would say that this is the job of the engineers to help prevent. It basically involves verification that eCryptfs is handling all of its memory correctly (i.e., via data and control flow analysis).

Quoted 6 lines

There's a third important category: the design of the _system_.

(Which you end up discussing somewhat further in the email).

It would be great to have a document which describes the design of the system and includes a comprehensive security analysis.

Kernel programmers making documentation? You must be joking! (Side joke... someone somewhere, which I have now forgetten, made a similar comment).

For documentation, nothing formal exists, and while we were planning on having some, it sounds like it might be a good thing to start sooner than later.

I (or someone else) will get back to you when we figure out how we want to approach this.

Quoted 11 lines

- James
--
James Morris
<[email protected]>
-
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

--
Michael C. Thompson <[email protected]>
Software-Engineer, IBM LTC Security