[PATCH 4/12: eCryptfs] Main module functions

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

Signed off by: Phillip Hellewell <[email protected]>
Signed off by: Michael Halcrow <[email protected]>
Signed off by: Michael Thompson <[email protected]>
Signed off by: Kent Yoder <[email protected]>

 main.c |  973 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 973 insertions(+)
--- linux-2.6.14-rc5-mm1/fs/ecryptfs/main.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.14-rc5-mm1-ecryptfs/fs/ecryptfs/main.c	2005-11-01 14:41:36.000000000 -0600
@@ -0,0 +1,973 @@
+/**
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif				/* HAVE_CONFIG_H */
+#include <net/sock.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.
+ */
+#define VERBOSE_DUMP 9
+#ifdef DEBUG
+int ecryptfs_verbosity = VERBOSE_DUMP;
+#else
+int ecryptfs_verbosity = 1;
+#endif
+module_param(ecryptfs_verbosity, int, 1);
+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 (unlikely((ecryptfs_verbosity == VERBOSE_DUMP))) {
+		vprintk(fmt, args);
+		goto out;
+	}
+	if ((ecryptfs_verbosity >= verb) && printk_ratelimit()) {
+		vprintk(fmt, args);
+	}
+out:
+	va_end(args);
+}
+
+#ifdef ECRYPTFS_ENABLE_MEMORY_TRACING
+static void ecryptfs_printk_release(void *ptr, const char *fun, int line)
+{
+	ecryptfs_printk(1, KERN_NOTICE, "%s:%d: Releasing memory at location "
+			"[%p]\n", fun, line, ptr);
+}
+
+static void ecryptfs_printk_alloc(void *ptr, size_t size, const char *fun,
+				  int line)
+{
+	ecryptfs_printk(1, KERN_NOTICE, "%s:%d: Allocated memory at location "
+			"[%p] ([%d] bytes)\n", fun, line, ptr, size);
+}
+
+void __ecryptfs_kfree(void *ptr, const char *fun, int line)
+{
+	if (unlikely(ECRYPTFS_ENABLE_MEMORY_TRACING))
+		ecryptfs_printk_release(ptr, fun, line);
+	kfree(ptr);
+}
+
+void *__ecryptfs_kmalloc(size_t size, unsigned int flags, const char *fun,
+			 int line)
+{
+	void *ptr;
+	ptr = kmalloc(size, flags);
+	if (unlikely(ECRYPTFS_ENABLE_MEMORY_TRACING))
+		ecryptfs_printk_alloc(ptr, size, fun, line);
+	return ptr;
+}
+
+#ifndef ECRYPTFS_KMEM_CACHE_DEBUG_VERBOSITY
+#define ECRYPTFS_KMEM_CACHE_DEBUG_VERBOSITY 1
+#endif
+
+void __ecryptfs_kmem_cache_free(struct kmem_cache_s *kmem_cache, void *ptr,
+				const char *fun, int line)
+{
+	ecryptfs_printk(ECRYPTFS_KMEM_CACHE_DEBUG_VERBOSITY,
+			KERN_NOTICE, "%s:%d: calling kmem_cache_free() on "
+			"kmem_cache = [%p]; ptr = [%p]\n", fun, line,
+			kmem_cache, ptr);
+	kmem_cache_free(kmem_cache, ptr);
+}
+
+void *__ecryptfs_kmem_cache_alloc(kmem_cache_t * kmem_cache,
+				  unsigned int slab_type, const char *fun,
+				  int line)
+{
+	void *ptr;
+	ecryptfs_printk(ECRYPTFS_KMEM_CACHE_DEBUG_VERBOSITY,
+			KERN_NOTICE, "%s:%d: calling kmem_cache_alloc() on "
+			"kmem_cache = [%p]\n", fun, line, kmem_cache);
+	ptr = kmem_cache_alloc(kmem_cache, slab_type);
+	ecryptfs_printk(ECRYPTFS_KMEM_CACHE_DEBUG_VERBOSITY,
+			KERN_NOTICE, "%s:%d: called kmem_cache_alloc(); ptr "
+			"= [%p]\n", fun, line, ptr);
+	return ptr;
+}
+#endif				/* ECRYPTFS_ENABLE_MEMORY_TRACING */
+
+#ifdef ECRYPTFS_WRAP_VFS_CALLS
+struct inode *__ecryptfs_iget(struct super_block *sb, unsigned long ino,
+			      const char *fun, int line)
+{
+	int count;
+	struct inode *inode;
+	ecryptfs_printk(1, KERN_NOTICE, "%s:%d: calling iget(); ino = [%lu]\n",
+			fun, line, ino);
+	inode = iget(sb, ino);
+	count = atomic_read(&inode->i_count);
+	ecryptfs_printk(1, KERN_NOTICE, "%s:%d: called iget(): after call, "
+			"inode = [%p]; inode->i_count = [%d]\n", fun, line,
+			inode, count);
+	return inode;
+}
+
+void __ecryptfs_iput(struct inode *inode, const char *fun, int line)
+{
+	int count;
+	count = atomic_read(&inode->i_count);
+	ecryptfs_printk(1, KERN_NOTICE, "%s:%d: calling iput(); before call, "
+			"inode = [%p]; inode->i_count = [%d]\n", fun, line,
+			inode, count);
+	iput(inode);
+	count = atomic_read(&inode->i_count);
+	ecryptfs_printk(1, KERN_NOTICE, "%s:%d: called iput(); after call, "
+			"inode->i_count = [%d]\n", fun, line, count);
+}
+
+void __ecryptfs_fput(struct file *file, const char *fun, int line)
+{
+	int count;
+	count = atomic_read(&file->f_count);
+	ecryptfs_printk(1, KERN_NOTICE, "%s:%d: calling fput(); before call, "
+			"file = [%p]; file->f_count = [%d]\n", fun, line,
+			file, count);
+	fput(file);
+	count = atomic_read(&file->f_count);
+	ecryptfs_printk(1, KERN_NOTICE, "%s:%d: called fput(); after call, "
+			"file->f_count = [%d]\n", fun, line, count);
+}
+
+void __ecryptfs_d_add(struct dentry *dentry, struct inode *inode,
+		      const char *fun, int line)
+{
+	int count;
+	count = atomic_read(&dentry->d_count);
+	ecryptfs_printk(1, KERN_NOTICE, "%s:%d: calling d_add(); before call, "
+			"dentry->d_count = [%d]\n", fun, line, count);
+	d_add(dentry, inode);
+	count = atomic_read(&dentry->d_count);
+	ecryptfs_printk(1, KERN_NOTICE, "%s:%d: called iget(): after call, "
+			"dentry->d_count = [%d]\n", fun, line, count);
+}
+
+void __ecryptfs_d_instantiate(struct dentry *dentry, struct inode *inode,
+			      const char *fun, int line)
+{
+	int count;
+	count = atomic_read(&dentry->d_count);
+	ecryptfs_printk(1, KERN_NOTICE, "%s:%d: calling d_instantiate(); "
+			"before call, dentry->d_count = [%d]\n", fun, line,
+			count);
+	d_instantiate(dentry, inode);
+	count = atomic_read(&dentry->d_count);
+	ecryptfs_printk(1, KERN_NOTICE, "%s:%d: called d_instantiate(); "
+			"dentry->d_count = [%d]\n", fun, line, count);
+}
+
+struct dentry *__ecryptfs_d_alloc(struct dentry *parent,
+				  const struct qstr *name, const char *fun,
+				  int line)
+{
+	struct dentry *dentry;
+	dentry = d_alloc(parent, name);
+	ecryptfs_printk(1, KERN_NOTICE, "%s:%d: Allocated a new dentry at "
+			"location [%p] for name [%s]\n", fun, line, dentry,
+			name);
+	return dentry;
+}
+
+void __ecryptfs_d_drop(struct dentry *dentry, const char *fun, int line)
+{
+	int count;
+	count = atomic_read(&dentry->d_count);
+	ecryptfs_printk(1, KERN_NOTICE, "%s:%d: calling d_drop() w/ "
+			"dentry = [%p]; dentry->d_count = [%d]\n", fun, line,
+			dentry, count);
+	d_drop(dentry);
+	count = atomic_read(&dentry->d_count);
+	ecryptfs_printk(1, KERN_NOTICE, "%s:%d: called d_drop(), "
+			"dentry->d_count = [%d]\n", fun, line, count);
+}
+
+void __ecryptfs_dput(struct dentry *dentry, const char *fun, int line)
+{
+	int count;
+	count = atomic_read(&dentry->d_count);
+	ecryptfs_printk(1, KERN_NOTICE, "%s:%d: calling dput() on dentry with "
+			"dentry = [%p]; dentry->d_name.name = [%s]; "
+			"dentry->d_count = [%d]\n", fun, line,
+			dentry, dentry->d_name.name, count);
+	dput(dentry);
+	count = atomic_read(&dentry->d_count);
+	ecryptfs_printk(1, KERN_NOTICE, "%s:%d: called dput() on dentry; now "
+			"dentry->d_count = [%d]\n", fun, line, count);
+}
+
+struct dentry *__ecryptfs_dget(struct dentry *dentry, const char *fun, int line)
+{
+	int count;
+	struct dentry *ret_dentry;
+	count = atomic_read(&dentry->d_count);
+	ecryptfs_printk(1, KERN_NOTICE, "%s:%d: calling dget() on dentry with "
+			"dentry = [%p]; dentry->d_name.name = [%s]; "
+			"dentry->d_count = [%d]\n", fun, line,
+			dentry, dentry->d_name.name, count);
+	if (count == 0) {
+		ecryptfs_printk(0, KERN_ERR, "%s:%d: The dentry has a count "
+				"of 0, which is illegal!\n");
+	}
+	ret_dentry = dget(dentry);
+	count = atomic_read(&dentry->d_count);
+	ecryptfs_printk(1, KERN_NOTICE, "%s:%d: called dget() on dentry; now "
+			"dentry->d_count = [%d]\n", fun, line, count);
+	return ret_dentry;
+}
+
+struct file *__ecryptfs_dentry_open(struct dentry *dentry, struct vfsmount *mnt,
+				    int flags, const char *fun, int line)
+{
+	struct file *file;
+	ecryptfs_printk(1, KERN_NOTICE, "%s:%d: calling dentry_open(); "
+			"dentry=[%p], mnt=[%p], flags=[0x%.8x]); "
+			"dentry->d_count = [%d]\n", fun, line, dentry, mnt,
+			flags, atomic_read(&dentry->d_count));
+	file = dentry_open(dentry, mnt, flags);
+	ecryptfs_printk(1, KERN_NOTICE, "%s:%d: called dentry_open()\n",
+			fun, line);
+	return file;
+}
+
+void __ecryptfs_d_delete(struct dentry *dentry, const char *fun, int line)
+{
+	ecryptfs_printk(1, KERN_NOTICE, "%s:%d: calling d_delete(); "
+			"dentry = [%p], dentry->d_name.name = [%s], "
+			"dentry->d_count = [%d]\n", fun, line, dentry,
+			dentry->d_name.name, atomic_read(&dentry->d_count));
+	d_delete(dentry);
+	ecryptfs_printk(1, KERN_NOTICE, "%s:%d: called d_delete(); "
+			"dentry->d_count = [%d]\n", fun, line,
+			atomic_read(&dentry->d_count));
+}
+
+struct inode *__ecryptfs_igrab(struct inode *inode, const char *fun, int line)
+{
+	struct inode *ret_inode;
+	int count;
+	count = atomic_read(&inode->i_count);
+	ecryptfs_printk(1, KERN_NOTICE, "%s:%d: calling igrab(); before call, "
+			"inode = [%p]; inode->i_count = [%d]\n", fun, line,
+			inode, count);
+	ret_inode = igrab(inode);
+	count = atomic_read(&inode->i_count);
+	ecryptfs_printk(1, KERN_NOTICE, "%s:%d: called igrab(); after call, "
+			"inode->i_count = [%d]\n", fun, line, count);
+	return ret_inode;
+}
+
+#endif				/* #ifdef ECRYPTFS_WRAP_VFS_CALLS */
+
+/**
+ * Interposes upper and lower dentries.
+ * This function will call an ecryptfs_inode into existance through the call to
+ * ecryptfs_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 != SUPERBLOCK_TO_LOWER(sb)) {
+		err = -EXDEV;
+		goto out;
+	}
+	inode = ecryptfs_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 (!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 == INODE_TO_LOWER(inode)) {
+		INODE_TO_LOWER(inode) = ecryptfs_igrab(lower_inode);
+		/* If we are still NULL at this point, igrab failed.
+		 * We are _NOT_ supposed to be failing here */
+		if (NULL == 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) {
+		ecryptfs_d_add(dentry, inode);
+	} else {
+		ecryptfs_d_instantiate(dentry, inode);
+	}
+	ecryptfs_copy_attr_all(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 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 =
+		&(SUPERBLOCK_TO_PRIVATE(sb)->mount_crypt_stats);
+	ecryptfs_printk(1, KERN_NOTICE, "Enter; options = [%s]\n", options);
+	if (!options) {
+		rc = -EINVAL;
+		goto out;
+	}
+	while ((p = strsep(&options, ",")) != NULL) {
+		substring_t args[MAX_OPT_ARGS];
+		int token;
+		if (!*p)
+			continue;
+		token = match_token(p, tokens, args);
+		switch (token) {
+		case ecryptfs_opt_sig:
+			{
+				char *sig_src;
+				char *sig_dst;
+				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:
+			{
+				char *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:
+			{
+				char *cipher_name_dst;
+				char *cipher_name_src;
+				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 */
+	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) {
+		int 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] "
+			"SUPERBLOCK_TO_PRIVATE(sb) = [%p] sb->s_root.d_name->"
+			"name = [%s]\n", sb,
+			sb->s_root, 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() */
+	SUPERBLOCK_TO_PRIVATE_SM(sb) =
+	    ecryptfs_kmem_cache_alloc(ecryptfs_sb_info_cache, SLAB_KERNEL);
+	if (!SUPERBLOCK_TO_PRIVATE_SM(sb)) {
+		ecryptfs_printk(0, KERN_WARNING, "Out of memory\n");
+		err = -ENOMEM;
+		goto out;
+	}
+	memset(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 ecryptfs_d_release when dput(sb->s_root) is called */
+	/* through deactivate_super(sb) from get_sb_nodev() */
+	DENTRY_TO_PRIVATE_SM(sb->s_root) = (struct ecryptfs_dentry_info *)
+	    ecryptfs_kmem_cache_alloc(ecryptfs_dentry_info_cache, SLAB_KERNEL);
+	if (!DENTRY_TO_PRIVATE_SM(sb->s_root)) {
+		ecryptfs_printk(0, KERN_ERR,
+				"dentry_info_cache alloc failed\n");
+		err = -ENOMEM;
+		goto out;
+	}
+	memset(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;
+	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;
+	}
+	SUPERBLOCK_TO_LOWER(sb) = lower_root->d_sb;
+	sb->s_maxbytes = lower_root->d_sb->s_maxbytes;
+	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] "
+			"SUPERBLOCK_TO_PRIVATE(sb) = [%p]\n", sb, sb->s_root,
+			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)
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux