[PATCH 0/12: eCryptfs] eCryptfs version 0.1

4 views
Skip to first unread message

Phillip Hellewell

unread,
Nov 2, 2005, 10:30:13 PM11/2/05
to
This set of patches constitutes eCryptfs version 0.1. We are
presenting it to be reviewed and considered for inclusion into the
kernel.

eCryptfs is a stackable filesystem that is based off of the Cryptfs
that is generated by the FiST stackable filesystem framework written
by Erez Zadok:

http://filesystems.org/

eCryptfs stores cryptographic metadata in the headers of each file;
the headers contain OpenPGP-like packets (see RFC 2440). This allows
the encrypted underlying files to be copied between hosts, and all of
the information necessary to decrypt the files stays with the files
themselves. eCryptfs aims to make the encryption and the decryption of
each individual file completely transparent to userspace applications,
so long as the recipient has the requisite key or passphrase to access
the file available.

Michael Halcrow presented eCryptfs at the 2004 and the 2005 Ottawa
Linux Symposiums; the high-level overview from this year's symposium
starts on page 209 of the first half of the symposium proceedings:

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

Note that this set of patches contains a considerably trimmed-down
version of eCryptfs than what was sent to the LKML earlier this
year. Release 0.1 includes mount-wide passphrase support only; this
will make eCryptfs easier to analyze and debug before the more
advanced policy and public key features are merged in.

eCryptfs performs well under a variety of tests, including FSX and
Connectathon (Basic and General functional). There is a bug that crops
up on a kernel compile. We would appreciate any insight that the VFS
guru's could give us in tracking down and fixing any extant bugs.

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 (ecryptfs-v0_1.tar.bz2):

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

Future releases will have policy support, which will entail per-file
passphrase and per-file public key support. Those who are interested
in looking at that code are welcome to obtain it from the eCryptfs CVS
repository on SourceForge:

cvs -d:pserver:anon...@cvs.sourceforge.net:/cvsroot/ecryptfs login
cvs -z3 -d:pserver:anon...@cvs.sourceforge.net:/cvsroot/ecryptfs \
co -P ecryptfs

Phillip
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majo...@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

Phillip Hellewell

unread,
Nov 2, 2005, 10:40:10 PM11/2/05
to

Phillip Hellewell

unread,
Nov 2, 2005, 10:50:05 PM11/2/05
to
This patch provides documentation for using eCryptfs.

Signed off by: Phillip Hellewell <phi...@hellewell.homeip.net>
Signed off by: Michael Halcrow <mhal...@us.ibm.com>
Signed off by: Michael Thompson <mmct...@us.ibm.com>
Signed off by: Kent Yoder <yod...@us.ibm.com>

ecryptfs.txt | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 55 insertions(+)
--- linux-2.6.14-rc5-mm1/Documentation/ecryptfs.txt 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.14-rc5-mm1-ecryptfs/Documentation/ecryptfs.txt 2005-11-01 16:47:17.000000000 -0600
@@ -0,0 +1,55 @@
+eCryptfs: A stacked cryptographic filesystem for Linux
+Maintainer: Phillip Hellewell
+Lead developer: Michael A. Halcrow <mhal...@us.ibm.com>
+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/
+
+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
+mhal...@us.ibm.com

Phillip Hellewell

unread,
Nov 2, 2005, 10:50:07 PM11/2/05
to
These patches modify fs/Makefile and fs/Kconfig to provide build
support for eCryptfs.

Signed off by: Phillip Hellewell <phi...@hellewell.homeip.net>
Signed off by: Michael Halcrow <mhal...@us.ibm.com>
Signed off by: Michael Thompson <mmct...@us.ibm.com>
Signed off by: Kent Yoder <yod...@us.ibm.com>

Kconfig | 10 ++++++++++
Makefile | 1 +
2 files changed, 11 insertions(+)
--- linux-2.6.14-rc5-mm1/fs/Makefile 2005-11-01 10:19:25.000000000 -0600
+++ linux-2.6.14-rc5-mm1-ecryptfs//fs/Makefile 2005-11-01 10:36:50.000000000 -0600
@@ -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/
--- linux-2.6.14-rc5-mm1/fs/Kconfig 2005-11-01 10:19:25.000000000 -0600
+++ linux-2.6.14-rc5-mm1-ecryptfs//fs/Kconfig 2005-11-01 10:36:50.000000000 -0600
@@ -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

Phillip Hellewell

unread,
Nov 2, 2005, 10:50:08 PM11/2/05
to
Makefile for eCryptfs.

Signed off by: Phillip Hellewell <phi...@hellewell.homeip.net>
Signed off by: Michael Halcrow <mhal...@us.ibm.com>
Signed off by: Michael Thompson <mmct...@us.ibm.com>
Signed off by: Kent Yoder <yod...@us.ibm.com>

Makefile | 7 +++++++
1 files changed, 7 insertions(+)
--- linux-2.6.14-rc5-mm1/fs/ecryptfs/Makefile 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.14-rc5-mm1-ecryptfs/fs/ecryptfs/Makefile 2005-11-01 13:48:26.000000000 -0600
@@ -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

Phillip Hellewell

unread,
Nov 2, 2005, 10:50:09 PM11/2/05
to
Provides functions to initialize the eCryptfs module and eCryptfs
mounts. Allocates and deallocates kmem_cache regions.

Signed off by: Phillip Hellewell <phi...@hellewell.homeip.net>
Signed off by: Michael Halcrow <mhal...@us.ibm.com>
Signed off by: Michael Thompson <mmct...@us.ibm.com>
Signed off by: Kent Yoder <yod...@us.ibm.com>

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 <maha...@us.ibm.com>
+ * Michael C. Thompson <mcth...@us.ibm.com>
+ *
+ * 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 <mhal...@us.ibm.com>");
+MODULE_DESCRIPTION("eCryptfs");
+
+MODULE_LICENSE("GPL");
+
+module_init(init_ecryptfs_fs)
+module_exit(exit_ecryptfs_fs)

Phillip Hellewell

unread,
Nov 2, 2005, 11:00:08 PM11/2/05
to
This header contains declarations for various structs used in
eCryptfs.

Signed off by: Phillip Hellewell <phi...@hellewell.homeip.net>
Signed off by: Michael Halcrow <mhal...@us.ibm.com>
Signed off by: Michael Thompson <mmct...@us.ibm.com>
Signed off by: Kent Yoder <yod...@us.ibm.com>

ecryptfs_kernel.h | 523 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 523 insertions(+)
--- linux-2.6.14-rc5-mm1/fs/ecryptfs/ecryptfs_kernel.h 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.14-rc5-mm1-ecryptfs/fs/ecryptfs/ecryptfs_kernel.h 2005-11-01 14:40:09.000000000 -0600
@@ -0,0 +1,523 @@


+/**
+ * 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 <maha...@us.ibm.com>
+ *

+ * 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
+
+/**
+ * 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
+ int flags;
+ char encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES];
+ int encrypted_key_size;
+ char decrypted_key[ECRYPTFS_MAX_KEY_BYTES];
+ int decrypted_key_size;
+};
+
+/**
+ * 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 {
+ char password[ECRYPTFS_MAX_PASSWORD_LENGTH];
+ int password_size;
+ unsigned char salt[ECRYPTFS_SALT_SIZE];
+ int 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. */
+ int hash_algo;
+ int hash_iterations;
+ /* Iterated-hash concatenation of salt and passphrase */
+ unsigned char session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES];
+ int session_key_encryption_key_size; /* In bytes */
+ int session_key_encryption_key_set;
+ /* Always in expanded hex */
+ unsigned char signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
+};
+
+/* May be a password or a private key */
+struct ecryptfs_auth_tok {
+ int instantiated; /* When not instantiated, this struct only
+ * contains enough information to construct
+ * the file headers, which contain token
+ * descriptors */
+ uid_t uid;
+ long creation_time;
+ long expiration_time;
+ int expired;
+#define ECRYPTFS_PASSWORD 0
+#define ECRYPTFS_PRIVATE_KEY 1
+ int instanceof;
+ /* 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
+
+#define RECORDS_PER_PAGE(crypt_stats) (crypt_stats->extent_size / \
+ (crypt_stats->iv_bytes))
+#define PG_IDX_TO_LWR_PG_IDX(crypt_stats, idx) \
+ ((idx / crypt_stats->records_per_page) + idx \
+ + crypt_stats->num_header_pages + 1)
+#define RECORD_IDX(crypt_stats, idx) (idx % crypt_stats->records_per_page)
+#define RECORD_OFFSET(crypt_stats, idx) \
+ (RECORD_IDX(crypt_stats, idx) * (crypt_stats->iv_bytes \
+ + crypt_stats->hmac_bytes))
+#define LAST_RECORDS_PAGE_IDX(crypt_stats, idx) \
+ (PG_IDX_TO_LWR_PG_IDX(crypt_stats, idx) \
+ - RECORD_IDX(crypt_stats,idx) - 1)
+
+/**
+ * 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 */
+ char cipher[ECRYPTFS_MAX_CIPHER_NAME_SIZE];
+ struct semaphore iv_sem;
+ unsigned char iv[ECRYPTFS_MAX_IV_BYTES];
+ 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? */
+ unsigned char key[ECRYPTFS_MAX_KEY_BYTES];
+ int key_size_bits;
+ int key_valid;
+ struct crypto_tfm *tfm;
+ char keysigs[ECRYPTFS_MAX_NUM_KEYSIGS][ECRYPTFS_SIG_SIZE_HEX];
+ int num_keysigs;
+ int num_header_pages; /* Number of pages that comprise the
+ * header */
+ int extent_size;
+};
+
+/* 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 {
+ char global_auth_tok_sig[ECRYPTFS_SIG_SIZE_HEX + 1];
+ char global_default_cipher_name[ECRYPTFS_MAX_CIPHER_NAME_SIZE + 1];
+ /* 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;
+};
+
+/* 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];
+};
+
+#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 FILE_TO_PRIVATE(file) ((struct ecryptfs_file_info *)((file)->private_data))
+#define FILE_TO_PRIVATE_SM(file) ((file)->private_data)
+#define FILE_TO_LOWER(file) ((FILE_TO_PRIVATE(file))->wfi_file)
+#define INODE_TO_PRIVATE(ino) ((struct ecryptfs_inode_info *)(ino)->u.generic_ip)
+#define INODE_TO_PRIVATE_SM(ino) ((ino)->u.generic_ip)
+#define INODE_TO_LOWER(ino) (INODE_TO_PRIVATE(ino)->wii_inode)
+#define SUPERBLOCK_TO_PRIVATE(super) ((struct ecryptfs_sb_info *)(super)->s_fs_info)
+#define SUPERBLOCK_TO_PRIVATE_SM(super) ((super)->s_fs_info)
+#define SUPERBLOCK_TO_LOWER(super) (SUPERBLOCK_TO_PRIVATE(super)->wsi_sb)
+#define DENTRY_TO_PRIVATE_SM(dentry) ((dentry)->d_fsdata)
+#define DENTRY_TO_PRIVATE(dentry) ((struct ecryptfs_dentry_info *)(dentry)->d_fsdata)
+#define DENTRY_TO_LOWER(dentry) (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);
+
+#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_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);
+void ecryptfs_set_default_sizes(struct ecryptfs_crypt_stats *crypt_stats);
+int ecryptfs_generate_key_packet_set(char *dest_base, int start_offset,
+ struct ecryptfs_crypt_stats *crypt_stats,
+ struct dentry *ecryptfs_dentry);
+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);
+
+/* inode.c */
+int ecryptfs_truncate(struct dentry *dentry, loff_t new_length);
+
+/* For debugging purposes only */
+#ifdef ECRYPTFS_DEBUG
+#define ECRYPTFS_ENABLE_MEMORY_TRACING 1
+#endif
+#ifdef ECRYPTFS_ENABLE_MEMORY_TRACING
+void __ecryptfs_kfree(void *ptr, const char *fun, int line);
+#define ecryptfs_kfree(ptr) \
+ __ecryptfs_kfree(ptr, __FUNCTION__, __LINE__);
+#define ecryptfs_kmalloc(size, flags) \
+ __ecryptfs_kmalloc(size, flags, __FUNCTION__, __LINE__);
+#define ecryptfs_kmem_cache_alloc(kmem_cache, slab_type) \
+ __ecryptfs_kmem_cache_alloc(kmem_cache, slab_type, __FUNCTION__, \
+ __LINE__)
+#define ecryptfs_kmem_cache_free(kmem_cache, ptr) \
+ __ecryptfs_kmem_cache_free(kmem_cache, ptr, __FUNCTION__, __LINE__)


+void *__ecryptfs_kmalloc(size_t size, unsigned int flags,

+ const char *fun, int line);


+void *__ecryptfs_kmem_cache_alloc(kmem_cache_t * kmem_cache,
+ unsigned int slab_type, const char *fun,

+ int line);
+void __ecryptfs_kmem_cache_free(kmem_cache_t * kmem_cache, void *ptr,
+ const char *fun, int line);
+#else
+#define ecryptfs_kfree(ptr) \
+ kfree(ptr)
+#define ecryptfs_kmalloc(size, flags) \
+ kmalloc(size, flags)
+#define ecryptfs_kmem_cache_alloc(kmem_cache, slab_type) \
+ kmem_cache_alloc(kmem_cache, slab_type)
+#define ecryptfs_kmem_cache_free(kmem_cache, ptr) \
+ kmem_cache_free(kmem_cache, ptr)


+#endif /* ECRYPTFS_ENABLE_MEMORY_TRACING */
+

+/**
+ * For debugging purposes; replace with direct calls when done
+ * debugging. Wouldn't it be neat to put something like this in
+ * mainline?
+ */
+#ifdef ECRYPTFS_DEBUG
+#define ECRYPTFS_WRAP_VFS_CALLS 1
+#endif
+#ifdef ECRYPTFS_WRAP_VFS_CALLS
+#define ecryptfs_iget(sb, inode) \
+ __ecryptfs_iget(sb, inode, __FUNCTION__, __LINE__)
+#define ecryptfs_iput(inode) \
+ __ecryptfs_iput(inode, __FUNCTION__, __LINE__)
+#define ecryptfs_fput(file) \
+ __ecryptfs_fput(file, __FUNCTION__, __LINE__)
+#define ecryptfs_d_add(dentry, inode) \
+ __ecryptfs_d_add(dentry, inode, __FUNCTION__, __LINE__)
+#define ecryptfs_d_instantiate(dentry, inode) \
+ __ecryptfs_d_instantiate(dentry, inode, __FUNCTION__, __LINE__)
+#define ecryptfs_d_alloc(parent, name) \
+ __ecryptfs_d_alloc(parent, name, __FUNCTION__, __LINE__)
+#define ecryptfs_dput(dentry) \
+ __ecryptfs_dput(dentry, __FUNCTION__, __LINE__)
+#define ecryptfs_dget(dentry) \
+ __ecryptfs_dget(dentry, __FUNCTION__, __LINE__)
+#define ecryptfs_d_drop(dentry) \
+ __ecryptfs_d_drop(dentry, __FUNCTION__, __LINE__)
+#define ecryptfs_dentry_open(dentry, mnt, flags) \
+ __ecryptfs_dentry_open(dentry, mnt, flags, __FUNCTION__, __LINE__)
+#define ecryptfs_dentry_delete(dentry) \
+ __ecryptfs_d_delete(dentry, __FUNCTION__, __LINE__)
+#define ecryptfs_igrab(inode) \
+ __ecryptfs_igrab(inode, __FUNCTION__, __LINE__)
+/* Wrappers for various VFS calls, for debugging purposes. */
+void __ecryptfs_d_drop(struct dentry *dentry, const char *fun, int line);


+struct dentry *__ecryptfs_dget(struct dentry *dentry, const char *fun,

+ int line);
+void __ecryptfs_dput(struct dentry *dentry, const char *fun, int line);


+struct dentry *__ecryptfs_d_alloc(struct dentry *parent,
+ const struct qstr *name, const char *fun,

+ int line);


+void __ecryptfs_d_instantiate(struct dentry *dentry, struct inode *inode,

+ const char *fun, int line);


+void __ecryptfs_d_add(struct dentry *dentry, struct inode *inode,

+ const char *fun, int line);
+void __ecryptfs_iput(struct inode *inode, const char *fun, int line);
+void __ecryptfs_fput(struct file *file, const char *fun, int line);


+struct inode *__ecryptfs_iget(struct super_block *sb, unsigned long ino,

+ const char *fun, int line);


+struct file *__ecryptfs_dentry_open(struct dentry *dentry, struct vfsmount *mnt,

+ int flags, const char *fun, int line);
+void __ecryptfs_d_delete(struct dentry *dentry, const char *fun, int line);


+struct inode *__ecryptfs_igrab(struct inode *inode, const char *fun, int line);
+

+#else
+#define ecryptfs_iget(sb, inode) iget(sb, inode)
+#define ecryptfs_iput(inode) iput(inode)
+#define ecryptfs_fput(file) fput(file)
+#define ecryptfs_d_add(dentry, inode) d_add(dentry, inode)
+#define ecryptfs_d_instantiate(dentry, inode) d_instantiate(dentry, inode)
+#define ecryptfs_d_alloc(parent, name) d_alloc(parent, name)
+#define ecryptfs_dput(dentry) dput(dentry)
+#define ecryptfs_dget(dentry) dget(dentry)
+#define ecryptfs_d_drop(dentry) d_drop(dentry)
+#define ecryptfs_dentry_open(dentry, mnt, flags) dentry_open(dentry, mnt, flags)
+#define ecryptfs_d_delete(dentry) d_delete(dentry)
+#define ecryptfs_dentry_delete(dentry) d_delete(dentry)
+#define ecryptfs_igrab(inode) igrab(inode)
+#endif /* ECRYPTFS_WRAP_VFS_CALLS */
+
+#endif /* ndef ECRYPTFS_KERNEL_H */

Phillip Hellewell

unread,
Nov 2, 2005, 11:00:12 PM11/2/05
to
eCryptfs dentry operations.

Signed off by: Phillip Hellewell <phi...@hellewell.homeip.net>
Signed off by: Michael Halcrow <mhal...@us.ibm.com>
Signed off by: Michael Thompson <mmct...@us.ibm.com>
Signed off by: Kent Yoder <yod...@us.ibm.com>

dentry.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 110 insertions(+)
--- linux-2.6.14-rc5-mm1/fs/ecryptfs/dentry.c 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.14-rc5-mm1-ecryptfs/fs/ecryptfs/dentry.c 2005-11-01 14:40:09.000000000 -0600
@@ -0,0 +1,110 @@


+/**
+ * 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 <maha...@us.ibm.com>
+ *
+ * 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 <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 = 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 (!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 = DENTRY_TO_LOWER(dentry);
+ /* TODO: Why do we look for an inode here? We should be
+ * freeing regardless */
+ /* OLD: if (lower_dentry && lower_dentry->d_inode) { */
+ if (DENTRY_TO_PRIVATE(dentry)) {
+ ecryptfs_kmem_cache_free(ecryptfs_dentry_info_cache,
+ DENTRY_TO_PRIVATE(dentry));
+ }
+ if (lower_dentry)
+ ecryptfs_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,
+};

Phillip Hellewell

unread,
Nov 2, 2005, 11:00:14 PM11/2/05
to
eCryptfs inode operations. Includes functions to support inode
interpolation between upper and lower inodes. The ecryptfs_lookup()
function is one in particular where we would like to solicit feedback.

Signed off by: Phillip Hellewell <phi...@hellewell.homeip.net>
Signed off by: Michael Halcrow <mhal...@us.ibm.com>
Signed off by: Michael Thompson <mmct...@us.ibm.com>
Signed off by: Kent Yoder <yod...@us.ibm.com>

inode.c | 1492 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 1492 insertions(+)
--- linux-2.6.14-rc5-mm1/fs/ecryptfs/inode.c 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.14-rc5-mm1-ecryptfs/fs/ecryptfs/inode.c 2005-11-01 16:42:33.000000000 -0600
@@ -0,0 +1,1492 @@


+/**
+ * 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 <maha...@us.ibm.com>

+ * Michael C. Thompsion <mcth...@us.ibm.com>


+ *
+ * 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 <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 = ecryptfs_dget(dentry->d_parent);
+ down(&dir->d_inode->i_sem);
+ return dir;
+}
+
+static inline void unlock_dir(struct dentry *dir)
+{
+ up(&dir->d_inode->i_sem);
+ ecryptfs_dput(dir);
+}
+
+static 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;
+}
+
+static void ecryptfs_copy_attr_ctime(struct inode *dest,
+ const struct inode *src)
+{
+ ASSERT(dest != NULL);
+ ASSERT(src != NULL);
+ dest->i_ctime = src->i_ctime;
+}


+
+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 = 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;
+ FILE_TO_PRIVATE_SM(&fake_file) =
+ ecryptfs_kmem_cache_alloc(ecryptfs_file_info_cache,
+ SLAB_KERNEL);
+ if (!(FILE_TO_PRIVATE_SM(&fake_file))) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ FILE_TO_LOWER(&fake_file) = lower_file;
+ ecryptfs_fill_zeros(&fake_file, 1);
+ ecryptfs_kmem_cache_free(ecryptfs_file_info_cache,
+ FILE_TO_PRIVATE(&fake_file));
+ i_size_write(inode, 0);
+ ecryptfs_write_inode_size_to_header(lower_file, lower_inode, inode);
+ 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 = &(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 = ecryptfs_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 = SUPERBLOCK_TO_PRIVATE(inode->i_sb)->lower_mnt;
+ mntget(lower_mnt);
+ /* Corresponding fput() at end of func */
+ lower_file = ecryptfs_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);
+ lower_dentry = lookup_one_len(encoded_name, lower_dir_dentry,
+ encoded_namelen - 1);
+ ecryptfs_kfree(encoded_name);
+ ecryptfs_printk(1, KERN_NOTICE, "lower_dentry = [%p]; lower_dentry->"
+ "d_name.name = [%s]\n", lower_dentry,
+ lower_dentry->d_name.name);
+ if (IS_ERR(lower_dentry)) {
+ ecryptfs_printk(0, KERN_ERR, "ERR from lower_dentry\n");
+ err = PTR_ERR(lower_dentry);
+ goto out_drop;
+ }


+ 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 */
+ DENTRY_TO_PRIVATE_SM(dentry) =
+ ecryptfs_kmem_cache_alloc(ecryptfs_dentry_info_cache, SLAB_KERNEL);
+ if (!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;
+ }
+
+ DENTRY_TO_LOWER(dentry) = lower_dentry;
+ if (!lower_dentry->d_inode) {
+ /* We want to add because we couldn't find in lower */
+ ecryptfs_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;
+ }
+

+ /* 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 = ecryptfs_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 *)ecryptfs_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 = &(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)) {
+ memcpy(&file_size, page_virt, sizeof(file_size));
+ dentry->d_inode->i_size = file_size;
+ } else {
+ ecryptfs_printk(1, KERN_NOTICE, "Underlying file does "
+ "not look like it's encrypted; not "
+ "copying file size\n");
+ }
+ }
+ ecryptfs_kmem_cache_free(ecryptfs_header_cache_2, page_virt);
+ goto out;
+
+out_dput:
+ ecryptfs_dput(lower_dentry);
+ if (tlower_dentry) {
+ ecryptfs_dput(tlower_dentry);
+ }
+out_drop:
+ ecryptfs_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);
+ ecryptfs_dget(lower_old_dentry);
+ ecryptfs_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 =
+ INODE_TO_LOWER(old_dentry->d_inode)->i_nlink;
+out_lock:
+ unlock_dir(lower_dir_dentry);
+ ecryptfs_dput(lower_new_dentry);
+ ecryptfs_dput(lower_old_dentry);
+ if (!new_dentry->d_inode)
+ ecryptfs_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 err = 0;
+ struct dentry *tdentry = NULL;
+ struct inode *lower_dir;
+ 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_dir = INODE_TO_LOWER(dir);
+ lower_dentry = ecryptfs_lower_dentry(dentry);
+ ecryptfs_printk(1, KERN_NOTICE, "dentry = [%p]\n", dentry);
+ ecryptfs_printk(1, KERN_NOTICE, "lower_dentry = [%p]\n", lower_dentry);
+ if (!(tdentry = ecryptfs_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);
+ ecryptfs_printk(1, KERN_NOTICE, "lower_dir_dentry = [%p]\n",
+ lower_dir_dentry);
+ if (!(tlower_dentry = ecryptfs_dget(lower_dentry))) {
+ err = -EINVAL;
+ ecryptfs_printk(0, KERN_ERR, "Error dget'ing lower_dentry "
+ "[%p]\n", lower_dentry);
+ goto out;
+ }
+ spin_lock(&dcache_lock);
+ err = vfs_unlink(lower_dir, lower_dentry);
+ spin_unlock(&dcache_lock);
+ if (!err) {
+ ecryptfs_dentry_delete(tlower_dentry);
+ tlower_dentry = NULL;
+ }
+ ecryptfs_copy_attr_times(dir, lower_dir);
+ dentry->d_inode->i_nlink = INODE_TO_LOWER(dentry->d_inode)->i_nlink;
+ ecryptfs_copy_attr_ctime(dentry->d_inode, dir);
+ unlock_dir(lower_dir_dentry);
+ if (!err || err == -ENOENT) {
+ ecryptfs_d_drop(tdentry);
+ tdentry = NULL;
+ }
+out:
+ if (tdentry)
+ ecryptfs_dput(tdentry);
+ if (tlower_dentry)
+ ecryptfs_dput(tlower_dentry);
+ if (tlower_dir_dentry)
+ ecryptfs_dput(tlower_dir_dentry);


+ return err;
+}
+

+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);
+ ecryptfs_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);
+ ecryptfs_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);
+ ecryptfs_dput(lower_dentry);
+ if (!dentry->d_inode)
+ ecryptfs_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)
+ ecryptfs_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 = ecryptfs_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 = ecryptfs_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) {
+ ecryptfs_dentry_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)
+ ecryptfs_d_drop(dentry);
+out:
+ if (tdentry)
+ ecryptfs_dput(tdentry);
+ if (tlower_dentry)
+ ecryptfs_dput(tlower_dentry);
+ if (tlower_dir_dentry)
+ ecryptfs_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);
+ ecryptfs_dget(lower_old_dentry);
+ ecryptfs_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);
+ ecryptfs_dput(lower_new_dentry);
+ ecryptfs_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 = ecryptfs_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());
+ 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;
+ }
+ if (err > 0) {
+ ecryptfs_printk(1, KERN_NOTICE, "Copying [%d] bytes "
+ "to userspace: [%s]\n", err,
+ decoded_name);
+ if (copy_to_user(buf, decoded_name, err))
+ err = -EFAULT;
+ }
+ ecryptfs_kfree(decoded_name);
+ ecryptfs_copy_attr_atime(dentry->d_inode,
+ lower_dentry->d_inode);
+ }
+ ecryptfs_kfree(lower_buf);
+ out:


+ 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\n");
+ /* Released in this function */
+ buf = ecryptfs_kmalloc(len, GFP_KERNEL);
+ if (!buf) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ old_fs = get_fs();
+ set_fs(get_ds());
+ 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);
+out_free:
+ ecryptfs_kfree(buf);
+out:
+ return NULL;
+}
+
+static inline void
+ecryptfs_put_link(struct dentry *dentry, struct nameidata *nd, void *ptr)
+{
+ /* Free the char* */
+ ecryptfs_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 = 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);
+
+ 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 = &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: */
+ FILE_TO_PRIVATE_SM(&fake_ecryptfs_file) =
+ ecryptfs_kmem_cache_alloc(ecryptfs_file_info_cache, SLAB_KERNEL);
+ if (unlikely(!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(SUPERBLOCK_TO_PRIVATE(inode->i_sb)->lower_mnt);
+ lower_file =
+ ecryptfs_dentry_open(lower_dentry,
+ SUPERBLOCK_TO_PRIVATE(inode->i_sb)->lower_mnt,
+ O_RDWR);
+ if (unlikely(IS_ERR(lower_file))) {
+ rc = PTR_ERR(lower_file);
+ goto out_free;
+ }
+ 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) */
+ loff_t lower_size_before_truncate;
+ loff_t lower_size_after_truncate;
+
+ 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 (FILE_TO_PRIVATE(&fake_ecryptfs_file)) {
+ ecryptfs_kmem_cache_free(ecryptfs_file_info_cache,
+ 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 = 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 = &INODE_TO_PRIVATE(dentry->d_inode)->crypt_stats;
+ lower_dentry = ecryptfs_lower_dentry(dentry);
+ inode = dentry->d_inode;
+ lower_inode = 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;
+}
+

+static void *xattr_alloc(size_t size, size_t limit)
+{
+ void *ptr;
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ if (size > limit)
+ return ERR_PTR(-E2BIG);
+ if (!size)
+ return NULL;
+ else if (size <= PAGE_SIZE) {
+ /* Released in xattr_free() */
+ ptr = ecryptfs_kmalloc((unsigned long)size, GFP_KERNEL);
+ } else {
+ /* Released in xattr_free() */
+ ptr = vmalloc((unsigned long)size);
+ }
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);


+ return ptr;
+}
+

+static void xattr_free(void *ptr, size_t size)
+{
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ if (!size)
+ return;
+ else if (size <= PAGE_SIZE) {
+ /* Allocated in xallr_alloc() */
+ ecryptfs_kfree(ptr);
+ } else {
+ /* Allocated in xallr_alloc() */
+ vfree(ptr);
+ }
+}
+
+static ssize_t ecryptfs_getxattr(struct dentry *dentry, const char *name,
+ void *value, size_t size)
+{
+ struct dentry *lower_dentry = NULL;
+ int err = -ENOTSUPP;
+ char *encoded_suffix = NULL;
+ int namelen = 0;
+ int sufflen = 0;
+ int prelen = 0;
+ char *encoded_name = NULL;
+ char *encoded_value = NULL;
+ struct ecryptfs_crypt_stats *crypt_stats;
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ lower_dentry = DENTRY_TO_LOWER(dentry);
+ if (lower_dentry->d_inode->i_op->getxattr) {
+ const char *suffix;
+ namelen = strlen(name);
+ suffix = strchr(name, '.');
+ if (suffix) {
+ suffix++;
+ prelen = (suffix - name) - 1;
+ sufflen = namelen - (prelen + 1);
+ } else {
+ prelen = 0;
+ sufflen = namelen;
+ suffix = name;
+ }
+ crypt_stats = NULL;
+ sufflen = ecryptfs_encode_filename(suffix, sufflen,
+ &encoded_suffix,
+ ECRYPTFS_DO_DOTS,
+ crypt_stats) - 1;
+ encoded_name = xattr_alloc(prelen + sufflen + 2,
+ XATTR_SIZE_MAX);
+ if (IS_ERR(encoded_name)) {
+ err = PTR_ERR(encoded_name);
+ encoded_name = NULL;
+ ecryptfs_kfree(encoded_suffix);
+ goto out;
+ }
+ if (prelen) {
+ memcpy(encoded_name, name, prelen);
+ encoded_name[prelen] = '.';
+ memcpy(encoded_name + prelen + 1, encoded_suffix,
+ sufflen);
+ encoded_name[prelen + 1 + sufflen] = '\0';
+ } else {
+ memcpy(encoded_name + prelen, encoded_suffix, sufflen);
+ encoded_name[sufflen] = '\0';
+ }
+ ecryptfs_kfree(encoded_suffix);
+ encoded_value = xattr_alloc(size, XATTR_SIZE_MAX);
+ if (IS_ERR(encoded_value)) {
+ err = PTR_ERR(encoded_value);
+ encoded_name = NULL;
+ goto out;
+ }
+ down(&lower_dentry->d_inode->i_sem);
+ err = lower_dentry->d_inode->i_op->getxattr(lower_dentry,
+ encoded_name,
+ encoded_value,
+ size);
+ up(&lower_dentry->d_inode->i_sem);
+ if ((err > 0) && size) {
+ int ret = 0;
+ unsigned long bsize;
+ struct ecryptfs_crypt_stats *crypt_stats;
+ unsigned char zero_iv[ECRYPTFS_MAX_IV_BYTES];
+ memset(zero_iv, 0, ECRYPTFS_MAX_IV_BYTES);
+ crypt_stats = &(INODE_TO_PRIVATE
+ (dentry->d_inode))->crypt_stats;
+ ecryptfs_printk(0, KERN_WARNING,
+ "Unfinished function called!\n");
+ bsize = crypto_tfm_alg_blocksize(crypt_stats->tfm);
+ if ((err % bsize) != 0) {
+ int adjusted_size = err
+ + (bsize - (err % bsize));
+
+ err = do_decrypt_virt(crypt_stats, value,
+ encoded_value,
+ adjusted_size, zero_iv);
+ } else {
+ err = do_decrypt_virt(crypt_stats, value,
+ encoded_value, err,
+ zero_iv);
+ }
+ if (ret < 0) {
+ err = ret;


+ goto out;
+ }
+ }

+ }
+
+out:
+ if (encoded_name) {
+ xattr_free(encoded_name, namelen + 1);
+ }
+ if (encoded_value) {
+ xattr_free(encoded_value, size);
+ }


+ return err;
+}
+

+static int ecryptfs_setxattr(struct dentry *dentry, const char *name,
+ const void *value, size_t size, int flags)
+{
+ struct dentry *lower_dentry = NULL;
+ int err = -ENOTSUPP;
+ char *encoded_suffix = NULL;
+ int namelen = 0;
+ int sufflen = 0;
+ int prelen = 0;
+ char *encoded_value = NULL;
+ char *encoded_name = NULL;
+ struct ecryptfs_crypt_stats *crypt_stats;
+ unsigned int bsize;
+ unsigned char zero_iv[ECRYPTFS_MAX_IV_BYTES];
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ memset(zero_iv, 0, ECRYPTFS_MAX_IV_BYTES);
+ lower_dentry = DENTRY_TO_LOWER(dentry);
+ crypt_stats = &(INODE_TO_PRIVATE(dentry->d_inode)->crypt_stats);
+ ecryptfs_printk(0, KERN_WARNING, "Unfinished function called!\n");
+ if (lower_dentry->d_inode->i_op->setxattr) {
+ const char *suffix;
+ namelen = strlen(name);
+ suffix = strchr(name, '.');
+ if (suffix) {
+ suffix++;
+ prelen = (suffix - name) - 1;
+ sufflen = namelen - (prelen + 1);
+ } else {
+ prelen = 0;
+ sufflen = namelen;
+ suffix = name;
+ }
+ sufflen = ecryptfs_encode_filename(suffix, sufflen,
+ &encoded_suffix,
+ ECRYPTFS_DO_DOTS,
+ crypt_stats) - 1;
+ encoded_name = xattr_alloc(prelen + sufflen + 2,
+ XATTR_SIZE_MAX);
+ if (IS_ERR(encoded_name)) {
+ err = PTR_ERR(encoded_name);
+ encoded_name = NULL;
+ goto out;
+ }
+ if (prelen) {
+ memcpy(encoded_name, name, prelen);
+ encoded_name[prelen] = '.';
+ memcpy(encoded_name + prelen + 1, encoded_suffix,
+ sufflen);
+ encoded_name[prelen + 1 + sufflen] = '\0';
+ } else {
+ memcpy(encoded_name + prelen, encoded_suffix, sufflen);
+ encoded_name[sufflen] = '\0';
+ }
+ encoded_value = xattr_alloc(size, XATTR_SIZE_MAX);
+ if (IS_ERR(encoded_value)) {
+ err = PTR_ERR(encoded_value);
+ encoded_value = NULL;
+ goto out;
+ }
+ bsize = crypto_tfm_alg_blocksize(crypt_stats->tfm);
+ if ((size % bsize) != 0) {
+ size_t remainder = (bsize - (size % bsize));
+ /* TODO: halcrow: Is this memory
+ * allocated? */
+ memset((void *)(value + size), 0, remainder);
+ size = size + remainder;
+ }
+ err = do_encrypt_virt(crypt_stats, encoded_value,
+ value, size, zero_iv);
+ if (err < 0) {
+ ecryptfs_printk(1, KERN_WARNING, "err<0!\n");
+ goto out;
+ }
+ down(&lower_dentry->d_inode->i_sem);
+ err = lower_dentry->d_inode->i_op->setxattr(lower_dentry,
+ encoded_name,
+ encoded_value,
+ size, flags);
+ up(&lower_dentry->d_inode->i_sem);
+ }
+
+out:
+ if (encoded_name)
+ xattr_free(encoded_name, namelen + 1);
+ if (encoded_suffix)
+ ecryptfs_kfree(encoded_suffix);
+ if (encoded_value)
+ xattr_free(encoded_value, size);


+ return err;
+}
+

+static int ecryptfs_removexattr(struct dentry *dentry, const char *name)
+{
+ struct dentry *lower_dentry = NULL;
+ int err = -ENOTSUPP;
+ char *encoded_name;
+ char *encoded_suffix = NULL;
+ int namelen = 0;
+ int sufflen = 0;
+ int prelen = 0;
+ struct ecryptfs_crypt_stats *crypt_stats;
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ lower_dentry = DENTRY_TO_LOWER(dentry);
+ if (lower_dentry->d_inode->i_op->removexattr) {
+ const char *suffix;
+ namelen = strlen(name);
+ suffix = strchr(name, '.');
+ if (suffix) {
+ suffix++;
+ prelen = (suffix - name) - 1;
+ sufflen = namelen - (prelen + 1);
+ } else {
+ prelen = 0;
+ sufflen = namelen;
+ suffix = name;
+ }
+ crypt_stats = NULL;
+ sufflen = ecryptfs_encode_filename(suffix, sufflen,
+ &encoded_suffix,
+ ECRYPTFS_DO_DOTS,
+ crypt_stats) - 1;
+ encoded_name = xattr_alloc(prelen + sufflen + 2,
+ XATTR_SIZE_MAX);
+ if (IS_ERR(encoded_name)) {
+ err = PTR_ERR(encoded_name);
+ encoded_name = NULL;
+ goto out;
+ }
+ if (prelen) {
+ memcpy(encoded_name, name, prelen);
+ encoded_name[prelen] = '.';
+ memcpy(encoded_name + prelen + 1, encoded_suffix,
+ sufflen);
+ encoded_name[prelen + 1 + sufflen] = '\0';
+ } else {
+ memcpy(encoded_name + prelen, encoded_suffix, sufflen);
+ encoded_name[sufflen] = '\0';
+ }
+ down(&lower_dentry->d_inode->i_sem);
+ err = lower_dentry->d_inode->i_op->removexattr(lower_dentry,
+ encoded_name);
+ up(&lower_dentry->d_inode->i_sem);
+ }
+out:
+ if (encoded_suffix)
+ ecryptfs_kfree(encoded_suffix);


+ return err;
+}
+

+/**
+ *
+ * Returns negative on error or positive on success
+ */
+static ssize_t
+ecryptfs_listxattr(struct dentry *dentry, char *list, size_t size)
+{
+ int err = -ENOTSUPP;
+ int newsize = 0;
+ char *encoded_suffix = NULL;
+ char *encoded_list = NULL;
+ char *cur = NULL;
+ struct dentry *lower_dentry = NULL;
+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");
+ lower_dentry = DENTRY_TO_LOWER(dentry);
+ if (!lower_dentry->d_inode->i_op->listxattr) {
+ goto out_no_xattr;
+ }
+
+ encoded_list = xattr_alloc(size, XATTR_LIST_MAX);
+ if (IS_ERR(encoded_list)) {
+ err = PTR_ERR(encoded_list);
+ goto out_no_mem;
+ }
+
+ down(&lower_dentry->d_inode->i_sem);
+ err = lower_dentry->d_inode->i_op->listxattr(lower_dentry,
+ encoded_list, size);
+ up(&lower_dentry->d_inode->i_sem);
+ if (err < 0) {


+ goto out;
+ }
+

+ cur = encoded_list;
+ while (cur && (cur < (encoded_list + err))) {
+ const char *suffix;
+ int namelen, sufflen, prelen;
+ namelen = strlen(cur);
+ suffix = strchr(cur, '.');
+ if (suffix) {
+ suffix++;
+ prelen = (suffix - cur) - 1;
+ sufflen = namelen - (prelen + 1);
+ } else {
+ prelen = 0;
+ sufflen = namelen;
+ suffix = cur;
+ }
+ sufflen = ecryptfs_decode_filename(suffix, sufflen,
+ &encoded_suffix,
+ ECRYPTFS_DO_DOTS, NULL);
+ if (sufflen < 0) {
+ err = -EINVAL;
+ goto out;
+ }
+ if (prelen) {
+ if (newsize + sufflen + prelen + 2 < size) {
+ memcpy(list + newsize, cur, prelen);
+ list[newsize + prelen] = '.';
+ memcpy(list + newsize + prelen + 1,
+ encoded_suffix, sufflen);
+ list[newsize + prelen + 1 + sufflen] = '\0';
+ }
+ newsize += sufflen + prelen + 2;
+ } else {
+ if (newsize + sufflen + 1 < size) {
+ memcpy(list + newsize, encoded_suffix, sufflen);
+ list[newsize + sufflen] = '\0';
+ }
+ newsize += sufflen + 1;
+ }
+ cur += (namelen + 1);
+ ecryptfs_kfree(encoded_suffix);
+ }
+ err = newsize;
+out:
+ xattr_free(encoded_list, size);
+out_no_mem:
+out_no_xattr:


+ ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n", err);
+ 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,
+ .setxattr = ecryptfs_setxattr,
+ .getxattr = ecryptfs_getxattr,
+ .listxattr = ecryptfs_listxattr,
+ .removexattr = ecryptfs_removexattr
+};
+
+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,
+ .setxattr = ecryptfs_setxattr,
+ .getxattr = ecryptfs_getxattr,
+ .listxattr = ecryptfs_listxattr,
+ .removexattr = ecryptfs_removexattr
+};
+
+struct inode_operations ecryptfs_main_iops = {
+ .permission = ecryptfs_permission,
+ .setattr = ecryptfs_setattr,
+ .setxattr = ecryptfs_setxattr,
+ .getxattr = ecryptfs_getxattr,
+ .listxattr = ecryptfs_listxattr,
+ .removexattr = ecryptfs_removexattr
+};

Phillip Hellewell

unread,
Nov 2, 2005, 11:00:17 PM11/2/05
to
eCryptfs crypto functions. Scatterlist abstraction functions. Page
encryption/decryption functions. Inode cryptographic context
initialization functions. Header region manipulation
functions. Functions in which filename encoding/decoding can be
implemented.

Signed off by: Phillip Hellewell <phi...@hellewell.homeip.net>
Signed off by: Michael Halcrow <mhal...@us.ibm.com>
Signed off by: Michael Thompson <mmct...@us.ibm.com>
Signed off by: Kent Yoder <yod...@us.ibm.com>

crypto.c | 822 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 822 insertions(+)
--- linux-2.6.14-rc5-mm1/fs/ecryptfs/crypto.c 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.14-rc5-mm1-ecryptfs/fs/ecryptfs/crypto.c 2005-11-01 15:55:57.000000000 -0600
@@ -0,0 +1,822 @@


+/**
+ * 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 <maha...@us.ibm.com>

+ * Michael C. Thompson <mcth...@us.ibm.com>


+ *
+ * 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 <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 (ecryptfs_verbosity < 1) {
+ return;
+ }
+ 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
+ */
+static int do_encrypt_scatterlist(struct ecryptfs_crypt_stats *crypt_stats,
+ struct scatterlist *dest_sg,
+ struct scatterlist *src_sg, int size,
+ unsigned char *iv)
+{
+ if (!crypt_stats || !crypt_stats->tfm
+ || !crypt_stats->struct_initialized) {
+ ecryptfs_printk(0, KERN_ERR,
+ "Called w/ invalid crypt_stats state\n");
+ size = -EINVAL;
+ BUG();
+ 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);
+ }
+ crypto_cipher_setkey(crypt_stats->tfm, crypt_stats->key,
+ crypt_stats->key_size_bits / 8);
+ 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);
+ size = -ENOSYS;
+ }
+ /* TODO: crypt_stats->iv size must be equal to the block size */
+out:
+ return size;
+}
+


+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 rc;
+ 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;
+ rc = do_encrypt_scatterlist(crypt_stats, dest_sg, src_sg, size, iv);


+ return rc;
+}
+

+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 (ecryptfs_verbosity > 0) {
+ ecryptfs_dump_hex((char *)src_virt_addr, size);
+ }
+ if ((rc = virt_to_scatterlist(src_virt_addr, size, src_sg, 32))
+ == -ENOMEM) {
+ ecryptfs_printk(0, KERN_ERR, "do_encrypt_virt: No memory for "
+ "this operation\n");


+ goto out;
+
+ }

+ if ((rc = virt_to_scatterlist(dest_virt_addr, size, dest_sg, 32))
+ == -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 (ecryptfs_verbosity > 0) {
+ ecryptfs_dump_hex((char *)dest_virt_addr, size);
+ }


+out:
+ return rc;
+}
+
+/**

+ * @return Number of bytes decrypted
+ */
+static int do_decrypt_scatterlist(struct ecryptfs_crypt_stats *crypt_stats,
+ struct scatterlist *dest_sg,
+ struct scatterlist *src_sg, int size,
+ unsigned char *iv)


+{
+ 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 (ecryptfs_verbosity > 0) {
+ ecryptfs_dump_hex(crypt_stats->key,
+ crypt_stats->key_size_bits / 8);
+ }
+ crypto_cipher_setkey(crypt_stats->tfm, crypt_stats->key,
+ crypt_stats->key_size_bits / 8);
+ ecryptfs_printk(1, KERN_NOTICE, "Decrypting [%d] bytes.\n", size);
+ if (crypt_stats->tfm->crt_cipher.cit_mode == CRYPTO_TFM_MODE_ECB) {
+ 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) {
+ crypto_cipher_decrypt_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);
+ size = -ENOSYS;
+ }
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; size = [%d]\n", size);
+ return size;
+}
+
+/**
+ * @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)

+{
+ int rc;
+ 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;
+ rc = do_decrypt_scatterlist(crypt_stats, dest_sg, src_sg, size, iv);


+ return rc;
+}
+

+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);
+}
+


+int do_decrypt_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; minimize stack usage here */
+ struct scatterlist src_sg[32];
+ struct scatterlist dest_sg[32];
+ int rc;
+ ecryptfs_printk(1, KERN_NOTICE, "Source:\n");
+ if (ecryptfs_verbosity > 0) {
+ ecryptfs_dump_hex((char *)src_virt_addr, size);
+ }
+ if ((rc = virt_to_scatterlist(src_virt_addr, size, src_sg, 32))
+ == -ENOMEM) {
+ ecryptfs_printk(0, KERN_ERR, "do_decrypt_virt: No memory for "
+ "this operation\n");


+ goto out;
+
+ }

+ if ((rc = virt_to_scatterlist(dest_virt_addr, size, dest_sg, 32))
+ == -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 (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 void generate_random_key(unsigned char *key, int num_bytes)
+{
+ get_random_bytes(key, num_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);
+ generate_random_key(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 (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 =
+ &INODE_TO_PRIVATE(ecryptfs_dentry->d_inode)->crypt_stats;


+ struct ecryptfs_mount_crypt_stats *mount_crypt_stats =

+ &(SUPERBLOCK_TO_PRIVATE(ecryptfs_dentry->d_sb)->mount_crypt_stats);


+
+ 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) {
+ int cipher_name_len;
+ 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 num;
+ u32 marker;
+ memcpy(&num, (data + ECRYPTFS_FILE_SIZE_BYTES), 4);
+ num ^= MAGIC_ECRYPTFS_MARKER;
+ memcpy(&marker, (data + ECRYPTFS_FILE_SIZE_BYTES + 4), 4);
+ return (num == marker);
+}
+
+static int write_ecryptfs_marker(char *page_virt, int start_offset)
+{
+ u32 marker;
+ /* Generate the eCryptfs marker, which is a 4-byte random
+ * value, followed by another 4-byte random value that is
+ * formed by xor'ing the first value by 0x3c81b7f5. This could
+ * also be just the 0x3c81b7f5 value, but that is more likely
+ * to cause a misread; especially, for example, with the
+ * crypto.o object file, which has that number in its text
+ * area. */
+ get_random_bytes(&marker, (MAGIC_ECRYPTFS_MARKER_SIZE_BYTES / 2));
+ memcpy(page_virt + start_offset, &marker,
+ (MAGIC_ECRYPTFS_MARKER_SIZE_BYTES / 2));
+ ecryptfs_printk(1, KERN_NOTICE, "Wrote first val:\n");
+ if (ecryptfs_verbosity > 0) {
+ ecryptfs_dump_hex(page_virt + start_offset,
+ (MAGIC_ECRYPTFS_MARKER_SIZE_BYTES / 2));
+ }
+ marker ^= MAGIC_ECRYPTFS_MARKER;
+ memcpy(page_virt + start_offset +
+ (MAGIC_ECRYPTFS_MARKER_SIZE_BYTES / 2), &marker,
+ (MAGIC_ECRYPTFS_MARKER_SIZE_BYTES / 2));
+ ecryptfs_printk(1, KERN_NOTICE, "Wrote second val:\n");
+ if (ecryptfs_verbosity > 0) {
+ ecryptfs_dump_hex(page_virt + start_offset
+ + (MAGIC_ECRYPTFS_MARKER_SIZE_BYTES / 2),
+ (MAGIC_ECRYPTFS_MARKER_SIZE_BYTES / 2));
+ }
+ return MAGIC_ECRYPTFS_MARKER_SIZE_BYTES;
+}
+
+/**


+ * @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");

+ /* TODO: Do we really need to grab the mount here? */
+ 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;
+
+/**
+ * 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;
+ int ecryptfs_marker_len;


+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");

+ crypt_stats = &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 =

+ ecryptfs_kmem_cache_alloc(ecryptfs_header_cache_0, SLAB_USER);
+ if (!page_virt) {


+ ecryptfs_printk(0, KERN_ERR, "Out of memory\n");

+ return -ENOMEM;
+ }
+ ecryptfs_marker_len = write_ecryptfs_marker(page_virt,
+ ECRYPTFS_FILE_SIZE_BYTES);
+ rc = ecryptfs_generate_key_packet_set(page_virt,
+ (ECRYPTFS_FILE_SIZE_BYTES
+ + ecryptfs_marker_len),
+ crypt_stats, ecryptfs_dentry);
+ if (unlikely(rc == 0)) {
+ rc = -EIO;
+ ecryptfs_printk(0, KERN_ERR, "Error whilst generating the key "
+ "packet set; writing zero's\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_fput(lower_file);
+ ecryptfs_printk(1, KERN_NOTICE,
+ "Done writing key packet set to underlying file.\n");
+out_free:
+ ecryptfs_kmem_cache_free(ecryptfs_header_cache_0, page_virt);


+out:
+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ 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 =

+ &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 =
+ ecryptfs_kmem_cache_alloc(ecryptfs_header_cache_1, SLAB_USER);
+ if (IS_ERR(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_parse_packet_set(page_virt, crypt_stats, ecryptfs_dentry);
+ ecryptfs_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) = ecryptfs_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);
+ /* Make sure we are called correctly */
+ BUG_ON(length < 0);
+ (*decrypted_name) = ecryptfs_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;
+}

Phillip Hellewell

unread,
Nov 2, 2005, 11:00:16 PM11/2/05
to
eCryptfs mmap operations. The bulk encryption and decryption takes
place here.

We are aware of one possibility of data inconsistency in the current
scheme (when an IV is written and the associated page is not). We have
a patch currently being tested that avoids this problem by deriving
the initialization vector from a root IV combined with the page
index. Another patch in plan is to go to three writes -- one to write
all 0's to the IV, then to write the page data, then to write the IV;
a failure after any of these writes will be detectable in that case.

Signed off by: Phillip Hellewell <phi...@hellewell.homeip.net>
Signed off by: Michael Halcrow <mhal...@us.ibm.com>
Signed off by: Michael Thompson <mmct...@us.ibm.com>
Signed off by: Kent Yoder <yod...@us.ibm.com>

mmap.c | 1045 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 1045 insertions(+)
--- linux-2.6.14-rc5-mm1/fs/ecryptfs/mmap.c 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.14-rc5-mm1-ecryptfs/fs/ecryptfs/mmap.c 2005-11-01 14:40:17.000000000 -0600
@@ -0,0 +1,1045 @@


+/**
+ * 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 <maha...@us.ibm.com>
+ *

+ * 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 <linux/pagemap.h>
+#include <linux/writeback.h>
+#include <linux/page-flags.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;
+
+/**
+ * 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 page Page that is locked before this call is made
+ */
+static int ecryptfs_writepage(struct page *page, struct writeback_control *wbc)
+{
+ int err = -EIO;
+ 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);
+
+ /* Sanity checkers */
+ if (1) {
+ struct address_space *mapping = page->mapping;
+ loff_t offset = (loff_t) page->index << PAGE_CACHE_SHIFT;
+ if (wbc->sync_mode != WB_SYNC_NONE) {
+ ecryptfs_printk(1, KERN_NOTICE,
+ "wbc->sync_mode != WB_SYNC_NONE\n");
+ } else {
+ ecryptfs_printk(1, KERN_NOTICE,
+ "wbc->sync_mode == WB_SYNC_NONE\n");
+ }
+ /* racing with truncate? */
+ if (offset > mapping->host->i_size) {
+ ecryptfs_printk(1, KERN_NOTICE, "Racing truncate\n");
+ }
+ BUG_ON(PageWriteback(page));
+ }
+
+ /* End sanity */
+
+ inode = page->mapping->host;


+ crypt_stats = &(INODE_TO_PRIVATE(inode)->crypt_stats);

+ lower_inode = INODE_TO_LOWER(inode);
+ /* The 2nd argument here should be the index of the lower file, which
+ * should be the calculated result of our index interpolation */
+ lower_index = 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) {
+ err = -ENOMEM;
+ goto out;
+ }
+ if (PageWriteback(lower_page)) {
+ ecryptfs_printk(1, KERN_NOTICE,
+ "lower_page is under writeback\n");
+ } else {
+ ecryptfs_printk(1, KERN_NOTICE,
+ "lower_page is avail for writeback\n");
+ }
+ if (!PageLocked(page)) {
+ ecryptfs_printk(1, KERN_NOTICE, "Page not locked\n");
+ } else {
+ ecryptfs_printk(1, KERN_NOTICE, "Page is already locked\n");
+ }
+ kaddr = (char *)kmap(page);
+ lower_kaddr = (char *)kmap(lower_page);
+ ecryptfs_printk(1, KERN_NOTICE, "Copying page\n");
+ memcpy(lower_kaddr, kaddr, crypt_stats->extent_size);
+ kunmap(page);
+ kunmap(lower_page);
+ if (PageWriteback(lower_page)) {
+ ecryptfs_printk(1, KERN_NOTICE,
+ "lower_page is under writeback\n");
+ } else {
+ ecryptfs_printk(1, KERN_NOTICE,
+ "lower_page is avail for writeback\n");
+ }
+ err = lower_inode->i_mapping->a_ops->writepage(lower_page, wbc);
+ lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME;
+ page_cache_release(lower_page);
+ if (err)
+ ClearPageUptodate(page);
+ else
+ SetPageUptodate(page);
+ unlock_page(page);
+out:


+ ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n", err);
+ return err;

+}
+
+/**
+ * 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 == FILE_TO_PRIVATE_SM(file)) {
+ rc = -ENOENT;
+ ecryptfs_printk(0, KERN_ERR, "No lower file info\n");
+ goto out;
+ }
+ lower_file = FILE_TO_LOWER(file);
+ lower_dentry = DENTRY_TO_LOWER(dentry);


+ inode = dentry->d_inode;
+ lower_inode = 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 =
+ ecryptfs_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");
+ ecryptfs_dump_hex(iv, crypt_stats->iv_bytes);
+ ecryptfs_printk(1, KERN_NOTICE, "Using session key encryption key:\n");
+ 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");
+ 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");
+ ecryptfs_dump_hex((char *)page_address(page), 8);
+ ecryptfs_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
+ */
+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;
+ int record_size;
+ 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 = &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;
+ }
+ record_size = crypt_stats->iv_bytes;
+ /* 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;
+ }
+

+ record_byte_offset = RECORD_IDX(crypt_stats, page->index) * record_size;
+ 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 = 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 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;
+}
+
+/**

+ * @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");
+ if (iv) {
+ ecryptfs_printk(1, KERN_NOTICE, "Encrypting page with iv:\n");
+ if (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;
+}
+
+/**

+ * 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 page *records_page;
+ 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 = 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 = &INODE_TO_PRIVATE(file->f_dentry->d_inode)->crypt_stats;
+ if (NULL != FILE_TO_PRIVATE(file))
+ lower_file = 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_generate_key_packet_set(
+ header_virt, (ECRYPTFS_FILE_SIZE_BYTES
+ + MAGIC_ECRYPTFS_MARKER_SIZE_BYTES),
+ crypt_stats, ecryptfs_dentry);
+ 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 = 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;
+ }
+ 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 */
+ pgoff_t records_page_index;
+ int record_byte_offset = -1;
+ int iv_byte_offset = -1;
+ char *records_virt;
+ char iv[ECRYPTFS_MAX_IV_BYTES];
+ int record_size;
+ 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);
+ record_size = crypt_stats->iv_bytes;
+ 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_unlock_lower;
+ }
+ iv_byte_offset = -1;
+ record_byte_offset =
+ RECORD_IDX(crypt_stats, page->index) * record_size;
+ if (crypt_stats->encrypted) {
+ iv_byte_offset = record_byte_offset;
+ }
+ ecryptfs_printk(1, KERN_NOTICE, "iv_byte_offset = [%d]\n",
+ iv_byte_offset);
+ records_virt = kmap(records_page);
+ rc = lower_a_ops->prepare_write(lower_file, records_page,
+ iv_byte_offset,
+ iv_byte_offset
+ + crypt_stats->iv_bytes);
+ 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 + iv_byte_offset, crypt_stats->iv,
+ crypt_stats->iv_bytes);
+ rc = lower_a_ops->commit_write(lower_file, records_page,
+ iv_byte_offset,
+ iv_byte_offset +
+ crypt_stats->iv_bytes);
+ kunmap(records_page);
+ lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty_sync(inode);
+ ecryptfs_printk(1, KERN_NOTICE, "Unlocking page with index = "
+ "[%lu]\n", records_page->index);
+ 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);
+ ecryptfs_printk(1, KERN_NOTICE, "Encrypting page with iv:\n");
+ if (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\n");


+ 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 = 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 = 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,
+};

Phillip Hellewell

unread,
Nov 2, 2005, 11:00:18 PM11/2/05
to
eCryptfs superblock operations and inode
allocation/deallocation/initialization functions.

Signed off by: Phillip Hellewell <phi...@hellewell.homeip.net>
Signed off by: Michael Halcrow <mhal...@us.ibm.com>
Signed off by: Michael Thompson <mmct...@us.ibm.com>
Signed off by: Kent Yoder <yod...@us.ibm.com>

super.c | 246 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 246 insertions(+)
--- linux-2.6.14-rc5-mm1/fs/ecryptfs/super.c 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.14-rc5-mm1-ecryptfs/fs/ecryptfs/super.c 2005-11-01 14:40:17.000000000 -0600
@@ -0,0 +1,246 @@


+/**
+ * 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 <maha...@us.ibm.com>

+ * Michael C. Thompson <mcth...@us.ibm.com>


+ *
+ * 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 <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. 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 = ecryptfs_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 = &(INODE_TO_PRIVATE(inode))->crypt_stats;
+ ecryptfs_destruct_crypt_stats(crypt_stats);
+ ecryptfs_kmem_cache_free(ecryptfs_inode_info_cache,
+ 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. */
+ INODE_TO_PRIVATE_SM(inode) = ECRYPTFS_I(inode);
+ 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 = 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);
+ ecryptfs_kmem_cache_free(ecryptfs_sb_info_cache, sb_info);
+ 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(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);
+ ecryptfs_iput(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 = 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(DENTRY_TO_LOWER(sb->s_root),
+ 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
+};

Phillip Hellewell

unread,
Nov 2, 2005, 11:00:18 PM11/2/05
to
eCryptfs file operations. Includes code to read header information
from the underyling file when needed.

Signed off by: Phillip Hellewell <phi...@hellewell.homeip.net>
Signed off by: Michael Halcrow <mhal...@us.ibm.com>
Signed off by: Michael Thompson <mmct...@us.ibm.com>
Signed off by: Kent Yoder <yod...@us.ibm.com>

file.c | 708 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 708 insertions(+)
--- linux-2.6.14-rc5-mm1/fs/ecryptfs/file.c 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.14-rc5-mm1-ecryptfs/fs/ecryptfs/file.c 2005-11-01 14:40:10.000000000 -0600
@@ -0,0 +1,708 @@


+/**
+ * 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 <mhal...@us.ibm.com>


+ * Michael C. Thompson <mcth...@us.ibm.com>
+ *
+ * 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 <linux/file.h>
+#include <linux/poll.h>
+#include <linux/mount.h>
+#include <linux/pagemap.h>
+#include <linux/security.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 != FILE_TO_PRIVATE(file)) {
+ lower_file = 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 0s
+ */
+ if (offset > i_size_read(file->f_dentry->d_inode)) {
+ rc = ecryptfs_truncate(file->f_dentry, offset);
+ if (rc) {
+ ret = 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;
+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");

+ err = generic_file_read(file, buf, count, ppos);


+ if (err >= 0)

+ update_atime(INODE_TO_LOWER(file->f_dentry->d_inode));


+ 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);
+ ASSERT(lower_file = FILE_TO_LOWER(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);
+ ASSERT(lower_file = FILE_TO_LOWER(file));
+ inode = file->f_dentry->d_inode;
+ lower_inode = 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 = 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);
+ ecryptfs_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);
+ ASSERT(lower_file = 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);
+ ASSERT(lower_file = FILE_TO_LOWER(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 = 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);
+
+ /* DENTRY_TO_PRIVATE(ecryptfs_dentry) Allocated in
+ * ecryptfs_lookup() */
+ ASSERT(DENTRY_TO_PRIVATE(ecryptfs_dentry));
+ /* Released in ecryptfs_release or end of function if failure */
+ FILE_TO_PRIVATE_SM(file) =
+ ecryptfs_kmem_cache_alloc(ecryptfs_file_info_cache, SLAB_KERNEL);
+ if (!FILE_TO_PRIVATE_SM(file)) {
+ ecryptfs_printk(0, KERN_ERR,
+ "Error attempting to allocate memory\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+ lower_dentry = DENTRY_TO_LOWER(ecryptfs_dentry);
+ ASSERT(lower_dentry);


+ crypt_stats = &(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 */
+ ecryptfs_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 = SUPERBLOCK_TO_PRIVATE(inode->i_sb)->lower_mnt;
+ mntget(lower_mnt);

+ /* Corresponding fput() in ecryptfs_release() */
+ lower_file = ecryptfs_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;
+ }
+ 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);
+ }
+ FILE_TO_LOWER(file) = lower_file;
+ goto out;
+out_puts:
+ mntput(lower_mnt);
+ ecryptfs_dput(lower_dentry);
+ ecryptfs_kmem_cache_free(ecryptfs_file_info_cache,
+ 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);
+ ASSERT(lower_file = 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);
+ if (FILE_TO_PRIVATE(file) != NULL)
+ lower_file = FILE_TO_LOWER(file);
+ ASSERT(lower_file);
+ ecryptfs_kmem_cache_free(ecryptfs_file_info_cache,
+ FILE_TO_PRIVATE(file));
+ lower_inode = 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 == FILE_TO_PRIVATE(file)) {
+ rc = -EINVAL;
+ ecryptfs_printk(0, KERN_ERR, "FILE_TO_PRIVATE(file="
+ "[%p]) == NULL\n", file);
+ goto out;
+ }
+ lower_file = 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\n");

+ 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;

+ if (FILE_TO_PRIVATE(file) != NULL)
+ lower_file = FILE_TO_LOWER(file);
+ ASSERT(lower_file != NULL);
+ 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:
+ 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 != FILE_TO_PRIVATE(file))
+ lower_file = 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 (FILE_TO_PRIVATE(file) != NULL)
+ lower_file = 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 (FILE_TO_PRIVATE(file) != NULL)
+ lower_file = 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 (FILE_TO_PRIVATE(file) != NULL) {


+ lower_file = FILE_TO_LOWER(file);
+ }

+ if (lower_file && lower_file->f_op && lower_file->f_op->ioctl) {
+ rc = lower_file->f_op->ioctl(INODE_TO_LOWER(inode),
+ lower_file, cmd, arg);
+ } else {
+ rc = -ENOTTY;


+ }
+ return rc;
+}

Phillip Hellewell

unread,
Nov 2, 2005, 11:00:19 PM11/2/05
to
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.

Signed off by: Phillip Hellewell <phi...@hellewell.homeip.net>
Signed off by: Michael Halcrow <mhal...@us.ibm.com>
Signed off by: Michael Thompson <mmct...@us.ibm.com>
Signed off by: Kent Yoder <yod...@us.ibm.com>

keystore.c | 860 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 860 insertions(+)
--- linux-2.6.14-rc5-mm1/fs/ecryptfs/keystore.c 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.14-rc5-mm1-ecryptfs/fs/ecryptfs/keystore.c 2005-11-01 15:51:59.000000000 -0600
@@ -0,0 +1,860 @@


+/**
+ * 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 <mhal...@us.ibm.com>
+ * Michael C. Thompson <mcth...@us.ibm.com>
+ *
+ * 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)
+{
+ 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) {
+ char salt[ECRYPTFS_SALT_SIZE * 2 + 1];
+ char sig[ECRYPTFS_SIG_SIZE_HEX + 1];
+ 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;


+ ecryptfs_printk(1, KERN_NOTICE, "Enter\n");

+ walker = auth_tok_list_head->next;
+ while (walker != auth_tok_list_head) {
+ struct ecryptfs_auth_tok_list_item *auth_tok_list_item;
+ auth_tok_list_item =
+ list_entry(walker, struct ecryptfs_auth_tok_list_item,
+ list);
+ walker = auth_tok_list_item->list.next;
+ ecryptfs_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)++] - 0x05 - ECRYPTFS_SALT_SIZE; */
+ (*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\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 {
+ /**
+ *p++ = 255;
+ *p++ = size >> 24;
+ *p++ = size >> 16;
+ *p++ = size >> 8;
+ *p++ = size;
+ */
+ 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 =
+ ecryptfs_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;
+ }

+ /* TODO: finish the cipher mapping */
+ switch (data[i++]) {
+ case 0x01:
+ /* IDEA not supported */
+ crypt_stats->cipher[0] = '\0';
+ /* TODO: Error msg */
+ rc = -ENOSYS;
+ goto out;
+ case 0x02:
+ /* Choose Triple-DES */
+ strcpy(crypt_stats->cipher, "des3_ede");
+ break;
+ case 0x03:
+ /* Choose CAST5 */
+ strcpy(crypt_stats->cipher, "cast5");
+ break;
+ case 0x04:
+ /* Choose blowfish */
+ strcpy(crypt_stats->cipher, "blowfish");
+ break;
+ case 0x07:
+ /* Choose AES-128 */
+ strcpy(crypt_stats->cipher, "aes");
+ crypt_stats->key_size_bits = 128;
+ break;
+ case 0x08:
+ /* Choose AES-192 */
+ strcpy(crypt_stats->cipher, "aes");
+ crypt_stats->key_size_bits = 192;
+ break;
+ case 0x09:
+ /* Choose AES-256 */
+ strcpy(crypt_stats->cipher, "aes");
+ crypt_stats->key_size_bits = 256;
+ break;
+ default:
+ crypt_stats->cipher[0] = '\0';
+ /* TODO: Error msg */


+ rc = -EINVAL;
+ goto out;
+ }

+ 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:
+ ecryptfs_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");
+ goto session_key_encryption_key_set;
+ }
+ session_key_encryption_key_set:
+ 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);
+ 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) {
+ ecryptfs_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
+ * @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 i = (ECRYPTFS_FILE_SIZE_BYTES + MAGIC_ECRYPTFS_MARKER_SIZE_BYTES);


+ 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 =
+ &(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");

+ if (!contains_ecryptfs_marker(dest)) {
+ ecryptfs_printk(0, KERN_NOTICE, "eCryptfs marker not found\n");
+ rc = -EINVAL; /* TODO: Make this a #define */
+ goto out;
+ }
+ 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");
+ 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");
+ 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 */
+ if (strcmp(crypt_stats->cipher, "des3_ede") == 0) {
+ *(dest + (dest_offset++)) = 0x02; /* Triple-DES */
+ } else if (strcmp(crypt_stats->cipher, "cast5") == 0) {
+ *(dest + (dest_offset++)) = 0x03; /* CAST5 */
+ } else if (strcmp(crypt_stats->cipher, "blowfish") == 0) {
+ *(dest + (dest_offset++)) = 0x04; /* BLOWFISH */
+ } else if (strcmp(crypt_stats->cipher, "aes") == 0) {
+ switch (crypt_stats->key_size_bits) {
+ case 128:
+ *(dest + (dest_offset++)) = 0x07; /* AES-128 */
+ break;
+ case 192:
+ *(dest + (dest_offset++)) = 0x08; /* AES-192 */
+ break;
+ case 256:
+ *(dest + (dest_offset++)) = 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;
+ }


+ } else {
+ rc = -EINVAL;

+ ecryptfs_printk(0, KERN_WARNING, "Unsupported cipher: [%s]\n",
+ crypt_stats->cipher);
+
+ goto out;
+ }
+ *(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
+ * @return Size of the data written; 0 if there was nothing to
+ * write or if a problem was encountered
+ */
+int
+ecryptfs_generate_key_packet_set(char *dest_base, int start_offset,


+ struct ecryptfs_crypt_stats *crypt_stats,
+ struct dentry *ecryptfs_dentry)

+{
+ int rc = 0;

+ struct ecryptfs_auth_tok *auth_tok;
+ int dest_offset = start_offset;


+ struct ecryptfs_mount_crypt_stats *mount_crypt_stats =
+ &(SUPERBLOCK_TO_PRIVATE(ecryptfs_dentry->d_sb)->mount_crypt_stats);

+
+ ecryptfs_printk(1, KERN_NOTICE, "Enter; start_offset = [%d]\n",
+ start_offset);
+ if (mount_crypt_stats->global_auth_tok) {
+ auth_tok = mount_crypt_stats->global_auth_tok;
+ switch (auth_tok->instanceof) {
+ case ECRYPTFS_PASSWORD:
+ rc = 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 = 0;
+ }

+ if (rc == 0) {

+ ecryptfs_printk(1, KERN_WARNING, "Error writing "
+ "authentication token packet with sig "
+ "= [%s]\n",
+ mount_crypt_stats->global_auth_tok_sig);


+ goto out;
+ } else {

+ dest_offset += rc;
+ }
+ } else {
+ BUG();
+ }
+ out:


+ ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
+ return rc;
+}

Michael Halcrow

unread,
Nov 3, 2005, 12:40:06 AM11/3/05
to
On Wed, Nov 02, 2005 at 08:55:30PM -0700, Phillip Hellewell wrote:
> +static int ecryptfs_writepage(struct page *page, struct writeback_control *wbc)
> +{
> ...

> + ecryptfs_printk(1, KERN_NOTICE, "Copying page\n");
> + memcpy(lower_kaddr, kaddr, crypt_stats->extent_size);

Note that this serves only to reduce the complexity of the execution
path while we try to pin down the kernel oops that shows up on certain
sets of concurrent gcc jobs. When actually encrypting, we are doing
extra page reads and writes with the underlying file to juggle the
initialization vectors. This part will, of course, need to be replaced
with the same crypto operations being done in ecryptfs_commit_write()
in order to encrypt the data on its way out via ecryptfs_writepage().
I will test the patch to do this and offer it as a follow-up once I am
satisfied that it does not regress any of our testcases; this patch
will also reduce the size of the ecryptfs_commit_write() function to
be closer to what it should be.

Thanks,
Mike

Greg KH

unread,
Nov 3, 2005, 1:10:11 AM11/3/05
to
On Wed, Nov 02, 2005 at 08:49:29PM -0700, Phillip Hellewell wrote:
> +#ifdef HAVE_CONFIG_H
> +# include <config.h>
> +#endif /* HAVE_CONFIG_H */

What is this here for?

> +#include <net/sock.h>
> +#include <linux/file.h>

net/ after linux/ please. Why do you need sock.h anyway?

> +/**
> + * 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);

I don't think you want a "1" here, do you? Hint, it's not doing what
you think it is doing...

> +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)

<snip>

Don't have wrappers for all of the common kernel functions, just call
them directly.

thanks,

greg k-h

Michael Thompson

unread,
Nov 3, 2005, 10:10:17 AM11/3/05
to
On 11/3/05, Greg KH <gr...@kroah.com> wrote:
> On Wed, Nov 02, 2005 at 08:49:29PM -0700, Phillip Hellewell wrote:
> > +#include <net/sock.h>
> > +#include <linux/file.h>
>
> net/ after linux/ please. Why do you need sock.h anyway?
We don't, thanks for pointing that out. It will be removed.

> > +/**
> > + * 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);
>
> I don't think you want a "1" here, do you? Hint, it's not doing what
> you think it is doing...

Would you care to explain, providing its short, what it does? I don't
mind admitting I don't know everything, especially when it comes to
kernel code. If I am to RTFM, please point me to the right M. :)

> > +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)
>
> <snip>
>
> Don't have wrappers for all of the common kernel functions, just call
> them directly.

We do call them directly as long as DEBUG is not defined. This code
exists for development purposes (or debug, if you will) so that we can
track memory allocations and other various things in the logs. All
wrappers for the common kernel functions are #defined in
ecryptfs_kernel.h so via preprocessing, it all becomes pretty.

Of course, these can be removed, assuming that this is not an
acceptable approach.

Mike

Michael Thompson

unread,
Nov 3, 2005, 10:30:18 AM11/3/05
to
On 11/2/05, Phillip Hellewell <phi...@hellewell.homeip.net> wrote:
> These patches modify fs/Makefile and fs/Kconfig to provide build
> support for eCryptfs.
>
> Signed off by: Phillip Hellewell <phi...@hellewell.homeip.net>
> Signed off by: Michael Halcrow <mhal...@us.ibm.com>
> Signed off by: Michael Thompson <mmct...@us.ibm.com>

That should read:
Signed off by: Michael Thompson <mcth...@us.ibm.com>

(Extra m), ah well :)

Alexey Dobriyan

unread,
Nov 3, 2005, 10:40:15 AM11/3/05
to
On Thu, Nov 03, 2005 at 09:09:24AM -0600, Michael Thompson wrote:
> On 11/3/05, Greg KH <gr...@kroah.com> wrote:
> > > +/**
> > > + * 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);
> >
> > I don't think you want a "1" here, do you? Hint, it's not doing what
> > you think it is doing...
> Would you care to explain, providing its short, what it does? I don't
> mind admitting I don't know everything, especially when it comes to
> kernel code. If I am to RTFM, please point me to the right M. :)

include/linux/moduleparam.h:#define module_param(name, type, perm)
^^^^

Michael Thompson

unread,
Nov 3, 2005, 10:50:27 AM11/3/05
to
On 11/3/05, Alexey Dobriyan <adob...@gmail.com> wrote:
> On Thu, Nov 03, 2005 at 09:09:24AM -0600, Michael Thompson wrote:
> > On 11/3/05, Greg KH <gr...@kroah.com> wrote:
> > > > +/**
> > > > + * 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);
> > >
> > > I don't think you want a "1" here, do you? Hint, it's not doing what
> > > you think it is doing...
> > Would you care to explain, providing its short, what it does? I don't
> > mind admitting I don't know everything, especially when it comes to
> > kernel code. If I am to RTFM, please point me to the right M. :)
>
> include/linux/moduleparam.h:#define module_param(name, type, perm)
> ^^^^
Thanks, my ability to read has improved. This will now be used properly :)

Michael Thompson

unread,
Nov 3, 2005, 4:40:21 PM11/3/05
to
On 11/3/05, Greg KH <gr...@kroah.com> wrote:
> On Wed, Nov 02, 2005 at 08:49:29PM -0700, Phillip Hellewell wrote:
> > +#include <net/sock.h>
> > +#include <linux/file.h>
>
> net/ after linux/ please. Why do you need sock.h anyway?

Here is the followup patch to remove the sock and include dcache.h.

Other patches will follow to resolve other comments.

Thanks,
Mike

-----

Signed off by: Michael Thompson <mmct...@us.ibm.com>

--- linux-2.6.14-rc2/fs/ecryptfs/main.c 2005-11-03 15:25:19.000000000 -0600
+++ linux-ecryptfs-updated/fs/ecryptfs/main.c 2005-11-03
15:26:56.000000000 -0600
@@ -26,7 +26,7 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif /* HAVE_CONFIG_H */
-#include <net/sock.h>
+#include <linux/dcache.h>
#include <linux/file.h>
#include <linux/module.h>
#include <linux/namei.h>

Dave Kleikamp

unread,
Nov 3, 2005, 5:10:19 PM11/3/05
to

Why the call to ecryptfs_fput() here? The caller does it's own fput on
lower_file.

> + ecryptfs_printk(1, KERN_NOTICE,
> + "Done writing key packet set to underlying
> file.\n");
> +out_free:
> + ecryptfs_kmem_cache_free(ecryptfs_header_cache_0, page_virt);
> +out:
> + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc);
> + return rc;
> +}

--
David Kleikamp
IBM Linux Technology Center

Michael Thompson

unread,
Nov 3, 2005, 5:30:24 PM11/3/05
to
On 11/3/05, Dave Kleikamp <sha...@austin.ibm.com> wrote:
> On Wed, 2005-11-02 at 20:56 -0700, Phillip Hellewell wrote:
> > + ecryptfs_fput(lower_file);
>
> Why the call to ecryptfs_fput() here? The caller does it's own fput on
> lower_file.

Hmm, good catch. That slipped through us - and to be hoenst, I have no
explination other than, it's wrong. ecryptfs_write_headers should not
be responsible for put'ing that which it did not get.

I'm wondering if I should be sending 1 patch per tiny fix like this,
or if I should be waiting for a few more changes, so as to not flood
the threads with minor patches?

Thanks,
Mike

Dave Kleikamp

unread,
Nov 3, 2005, 5:40:09 PM11/3/05
to
On Thu, 2005-11-03 at 16:25 -0600, Michael Thompson wrote:
> On 11/3/05, Dave Kleikamp <sha...@austin.ibm.com> wrote:
> > On Wed, 2005-11-02 at 20:56 -0700, Phillip Hellewell wrote:
> > > + ecryptfs_fput(lower_file);
> >
> > Why the call to ecryptfs_fput() here? The caller does it's own fput on
> > lower_file.
>
> Hmm, good catch. That slipped through us - and to be hoenst, I have no
> explination other than, it's wrong. ecryptfs_write_headers should not
> be responsible for put'ing that which it did not get.
>
> I'm wondering if I should be sending 1 patch per tiny fix like this,
> or if I should be waiting for a few more changes, so as to not flood
> the threads with minor patches?

Well, I found it trying to look for the cause of bug 1228303, but I
haven't actually run anything to verify it. It may be worth checking if
it fixes that problem, and if it does, it would bump up its importance.

> Thanks,
> Mike


>
--
David Kleikamp
IBM Linux Technology Center

-

Greg KH

unread,
Nov 3, 2005, 6:30:13 PM11/3/05
to
On Thu, Nov 03, 2005 at 09:21:16AM -0600, Michael Thompson wrote:
> On 11/2/05, Phillip Hellewell <phi...@hellewell.homeip.net> wrote:
> > These patches modify fs/Makefile and fs/Kconfig to provide build
> > support for eCryptfs.
> >
> > Signed off by: Phillip Hellewell <phi...@hellewell.homeip.net>
> > Signed off by: Michael Halcrow <mhal...@us.ibm.com>
> > Signed off by: Michael Thompson <mmct...@us.ibm.com>
>
> That should read:
> Signed off by: Michael Thompson <mcth...@us.ibm.com>

No, that's not how it is documented on how to do this. Please try
again.

thanks,

greg k-h

James Morris

unread,
Nov 3, 2005, 6:40:13 PM11/3/05
to
On Wed, 2 Nov 2005, Phillip Hellewell wrote:

> +struct ecryptfs_password {
> + char password[ECRYPTFS_MAX_PASSWORD_LENGTH];
> + int password_size;
> + unsigned char salt[ECRYPTFS_SALT_SIZE];
> + int 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. */
> + int hash_algo;
> + int hash_iterations;
> + /* Iterated-hash concatenation of salt and passphrase */
> + unsigned char session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES];
> + int session_key_encryption_key_size; /* In bytes */
> + int session_key_encryption_key_set;
> + /* Always in expanded hex */
> + unsigned char signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
> +};

Generally, you should put the smallest components of a data structure at
the front, so the compiler can optimize alignment.

> +#define RECORDS_PER_PAGE(crypt_stats) (crypt_stats->extent_size / \
> + (crypt_stats->iv_bytes))
> +#define PG_IDX_TO_LWR_PG_IDX(crypt_stats, idx) \
> + ((idx / crypt_stats->records_per_page) + idx \
> + + crypt_stats->num_header_pages + 1)
> +#define RECORD_IDX(crypt_stats, idx) (idx % crypt_stats->records_per_page)
> +#define RECORD_OFFSET(crypt_stats, idx) \
> + (RECORD_IDX(crypt_stats, idx) * (crypt_stats->iv_bytes \
> + + crypt_stats->hmac_bytes))
> +#define LAST_RECORDS_PAGE_IDX(crypt_stats, idx) \
> + (PG_IDX_TO_LWR_PG_IDX(crypt_stats, idx) \
> + - RECORD_IDX(crypt_stats,idx) - 1)

These should all be static inline (and any other similar).

> + */
> +#define FILE_TO_PRIVATE(file) ((struct ecryptfs_file_info *)((file)->private_data))
> +#define FILE_TO_PRIVATE_SM(file) ((file)->private_data)
> +#define FILE_TO_LOWER(file) ((FILE_TO_PRIVATE(file))->wfi_file)
> +#define INODE_TO_PRIVATE(ino) ((struct ecryptfs_inode_info *)(ino)->u.generic_ip)
> +#define INODE_TO_PRIVATE_SM(ino) ((ino)->u.generic_ip)
> +#define INODE_TO_LOWER(ino) (INODE_TO_PRIVATE(ino)->wii_inode)
> +#define SUPERBLOCK_TO_PRIVATE(super) ((struct ecryptfs_sb_info *)(super)->s_fs_info)
> +#define SUPERBLOCK_TO_PRIVATE_SM(super) ((super)->s_fs_info)
> +#define SUPERBLOCK_TO_LOWER(super) (SUPERBLOCK_TO_PRIVATE(super)->wsi_sb)
> +#define DENTRY_TO_PRIVATE_SM(dentry) ((dentry)->d_fsdata)
> +#define DENTRY_TO_PRIVATE(dentry) ((struct ecryptfs_dentry_info *)(dentry)->d_fsdata)
> +#define DENTRY_TO_LOWER(dentry) (DENTRY_TO_PRIVATE(dentry)->wdi_dentry)

These macro names are too generic.


- James
--
James Morris
<jmo...@namei.org>

James Morris

unread,
Nov 3, 2005, 6:50:13 PM11/3/05
to
On Wed, 2 Nov 2005, Phillip Hellewell wrote:

> +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 = ecryptfs_kmem_cache_alloc(ecryptfs_inode_info_cache,
> + SLAB_KERNEL);

Most of the kernel code separates variable declarations from code. Please
do this with your code.

For debugging and tracing, have a look at the ocfs2 masklog code. (It'd
be nice to see something like that as a general kernel feature).


- James
--
James Morris
<jmo...@namei.org>

James Morris

unread,
Nov 3, 2005, 7:00:07 PM11/3/05
to
On Wed, 2 Nov 2005, Phillip Hellewell wrote:

> + rc = wait_event_interruptible(fl->fl_wait, !fl->fl_next);
> + if (!rc) {
> + goto lock_file;
> + }

> + } else {


> + tempfl = posix_test_lock(file, fl);
> + }
> + if (!tempfl) {
> + fl->fl_type = F_UNLCK;
> + } else {
> + memcpy(fl, tempfl, sizeof(struct file_lock));
> + }

Pointless braces (all over the place).


- James
--
James Morris
<jmo...@namei.org>

James Morris

unread,
Nov 3, 2005, 7:00:15 PM11/3/05
to
On Wed, 2 Nov 2005, Phillip Hellewell wrote:

> +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));


You don't need these initializations, bss is always initialized to zero
in this environment.


- James
--
James Morris
<jmo...@namei.org>

James Morris

unread,
Nov 3, 2005, 7:10:07 PM11/3/05
to
On Wed, 2 Nov 2005, Phillip Hellewell wrote:

> + 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");
> + goto session_key_encryption_key_set;
> + }
> + session_key_encryption_key_set:
> + ecryptfs_printk(1, KERN_NOTICE, "Session key encryption key (size [%d])"
> + ":\n", password_s_ptr->session_key_encryption_key_size);

Spurious goto?

> +out:
> + if (tfm)
> + crypto_free_tfm(tfm);
> + return rc;

Just call crypto_free_tfm() unconditionally.


- James
--
James Morris
<jmo...@namei.org>

James Morris

unread,
Nov 3, 2005, 7:10:08 PM11/3/05
to
On Wed, 2 Nov 2005, Phillip Hellewell wrote:

> + crypto_cipher_setkey(crypt_stats->tfm, crypt_stats->key,
> + crypt_stats->key_size_bits / 8);

Check return value.

> +static void generate_random_key(unsigned char *key, int num_bytes)
> +{
> + get_random_bytes(key, num_bytes);
> +}

Call get_random_bytes() directly.

> + 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;
> + }

What's going on here? Is (crypt_stats->encrypted != 1) a kernel bug?


- James
--
James Morris
<jmo...@namei.org>

Michael Halcrow

unread,
Nov 3, 2005, 7:20:05 PM11/3/05
to
On Thu, Nov 03, 2005 at 07:08:46PM -0500, James Morris wrote:
> > + 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;
> > + }
>
> What's going on here? Is (crypt_stats->encrypted != 1) a kernel
> bug?

If ecryptfs_write_headers() is ever called on an unencrypted file,
then that is a programming error in eCryptfs. This will need to be
replaced with a BUG_ON().

Mike

Michael Halcrow

unread,
Nov 3, 2005, 7:20:11 PM11/3/05