2006-05-04 03:17:57

by Phillip Hellewell

[permalink] [raw]
Subject: [PATCH 0/12: eCryptfs] eCryptfs version 0.1.6

This patch set constitutes the 0.1.6 release of the eCryptfs
cryptographic filesystem:

http://ecryptfs.sourceforge.net/

eCryptfs is a kernel-native stacked cryptographic filesystem for
Linux. It is derived from Erez Zadok's Cryptfs, implemented through
the FiST framework for generating stacked filesystems. eCryptfs
extends Cryptfs to provide a framework for advanced key management and
policy features. The initial release includes support for mount-wide
passphrase only. eCryptfs stores cryptographic metadata in the header
of each file written, so that encrypted files can be copied between
the lower filesystems of hosts; the file will be decryptable through
eCryptfs with the proper key, and there is no need to keep track of
any additional information aside from what is already in the encrypted
file itself. We think of eCryptfs as a sort of ``pgpfs.''

This patch set implements the design reflected in the document sent to
the LKML on March 24th (subject ``eCryptfs Design Document''), with
two modifications per responses to that document. The first
modification is that extents are fixed to 4096-byte regions rather
than whatever the page size of the host happens to be. In cases where
the page size is larger than 4096 bytes and where the pages are not
aligned, eCryptfs crosses page boundaries in the lower file while
processing the 4096-byte extents. The second modification is that the
header region occupies either 8192 bytes or the page size of the host
on which the file is created, whichever is larger. This maximizes the
probability that pages will be aligned between the unencrypted and
encrypted data, which is not a requirement, but it helps with
performance.

This patch set was produced and tested against the 2.6.17-rc3-mm1
release of the kernel.

Thanks,
Phillip


2006-05-04 03:27:45

by Phillip Hellewell

[permalink] [raw]
Subject: [PATCH 1/13: eCryptfs] fs/Makefile and fs/Kconfig

This is the 1st patch in a series of 13 constituting the kernel
components of the eCryptfs cryptographic filesystem.

This patch modifies the fs/Kconfig and fs/Makefile files to
incorporate eCryptfs into the kernel build.

Signed-off-by: Phillip Hellewell <[email protected]>
Signed-off-by: Michael Halcrow <[email protected]>

---

Kconfig | 18 ++++++++++++++++++
Makefile | 1 +
2 files changed, 19 insertions(+)

Index: linux-2.6.17-rc3-mm1-ecryptfs/fs/Kconfig
===================================================================
--- linux-2.6.17-rc3-mm1-ecryptfs.orig/fs/Kconfig 2006-05-02 18:05:37.000000000 -0600
+++ linux-2.6.17-rc3-mm1-ecryptfs/fs/Kconfig 2006-05-02 19:35:57.000000000 -0600
@@ -935,6 +935,24 @@
To compile this file system support as a module, choose M here: the
module will be called affs. If unsure, say N.

+config ECRYPT_FS
+ 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 ECRYPT_DEBUG
+ bool "Enable eCryptfs debug mode"
+ depends on ECRYPT_FS
+ help
+ Turn on debugging code in eCryptfs.
+
+ If unsure, say N.
+
config HFS_FS
tristate "Apple Macintosh file system support (EXPERIMENTAL)"
depends on EXPERIMENTAL
Index: linux-2.6.17-rc3-mm1-ecryptfs/fs/Makefile
===================================================================
--- linux-2.6.17-rc3-mm1-ecryptfs.orig/fs/Makefile 2006-05-02 18:05:37.000000000 -0600
+++ linux-2.6.17-rc3-mm1-ecryptfs/fs/Makefile 2006-05-02 19:35:57.000000000 -0600
@@ -68,6 +68,7 @@
obj-$(CONFIG_ISO9660_FS) += isofs/
obj-$(CONFIG_HFSPLUS_FS) += hfsplus/ # Before hfs to find wrapped HFS+
obj-$(CONFIG_HFS_FS) += hfs/
+obj-$(CONFIG_ECRYPT_FS) += ecryptfs/
obj-$(CONFIG_VXFS_FS) += freevxfs/
obj-$(CONFIG_NFS_FS) += nfs/
obj-$(CONFIG_EXPORTFS) += exportfs/

2006-05-04 03:35:34

by Phillip Hellewell

[permalink] [raw]
Subject: [PATCH 2/13: eCryptfs] Documentation

This is the 2nd patch in a series of 13 constituting the kernel
components of the eCryptfs cryptographic filesystem.

This patch provides documentation for using eCryptfs.

Signed-off-by: Phillip Hellewell <[email protected]>
Signed-off-by: Michael Halcrow <[email protected]>

---

ecryptfs.txt | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 76 insertions(+)

Index: linux-2.6.17-rc3-mm1-ecryptfs/Documentation/ecryptfs.txt
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.17-rc3-mm1-ecryptfs/Documentation/ecryptfs.txt 2006-05-02 19:36:04.000000000 -0600
@@ -0,0 +1,76 @@
+eCryptfs: A stacked cryptographic filesystem for Linux
+
+eCryptfs is free software. Please see the file COPYING for details.
+For documentation, please see the files in the doc/ subdirectory. For
+building and installation instructions please see the INSTALL file.
+
+Maintainer: Phillip Hellewell
+Lead developer: Michael A. Halcrow <[email protected]>
+Developers: Michael C. Thompson
+ Kent Yoder
+Web Site: http://ecryptfs.sf.net
+
+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/
+
+Userspace requirements include:
+ - David Howells' userspace keyring headers and libraries (version
+ 1.0 or higher), obtainable from
+ http://people.redhat.com/~dhowells/keyutils/
+ - Libgcrypt
+
+
+NOTES
+
+In the beta/experimental releases of eCryptfs, when you upgrade
+eCryptfs, you should copy the files to an unencrypted location and
+then copy the files back into the new eCryptfs mount to migrate the
+files.
+
+
+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 2 pages (8192 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 version 0.1 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. Do not run eCryptfs in higher verbosity
+levels unless you are doing so for the sole purpose of debugging or
+development, since secret values will be written out to the system log
+in that case.
+
+
+Mike Halcrow
[email protected]

2006-05-04 03:36:20

by Phillip Hellewell

[permalink] [raw]
Subject: [PATCH 3/13: eCryptfs] Makefile

This is the 3rd patch in a series of 13 constituting the kernel
components of the eCryptfs cryptographic filesystem.

Makefile for eCryptfs.

Signed-off-by: Phillip Hellewell <[email protected]>
Signed-off-by: Michael Halcrow <[email protected]>

---

Makefile | 7 +++++++
1 files changed, 7 insertions(+)

Index: linux-2.6.17-rc3-mm1-ecryptfs/fs/ecryptfs/Makefile
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.17-rc3-mm1-ecryptfs/fs/ecryptfs/Makefile 2006-05-02 19:35:58.000000000 -0600
@@ -0,0 +1,7 @@
+#
+# Makefile for the Linux 2.6 eCryptfs
+#
+
+obj-$(CONFIG_ECRYPT_FS) += ecryptfs.o
+
+ecryptfs-objs := dentry.o file.o inode.o main.o super.o mmap.o crypto.o keystore.o debug.o

2006-05-04 03:37:06

by Phillip Hellewell

[permalink] [raw]
Subject: [PATCH 4/13: eCryptfs] Main module functions

This is the 4th patch in a series of 13 constituting the kernel
components of the eCryptfs cryptographic filesystem.

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

Signed-off-by: Phillip Hellewell <[email protected]>
Signed-off-by: Michael Halcrow <[email protected]>

---

main.c | 769 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 769 insertions(+)

Index: linux-2.6.17-rc3-mm1-ecryptfs/fs/ecryptfs/main.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.17-rc3-mm1-ecryptfs/fs/ecryptfs/main.c 2006-05-02 19:36:03.000000000 -0600
@@ -0,0 +1,769 @@
+/**
+ * eCryptfs: Linux filesystem encryption layer
+ *
+ * Copyright (C) 1997-2003 Erez Zadok
+ * Copyright (C) 2001-2003 Stony Brook University
+ * Copyright (C) 2004-2006 International Business Machines Corp.
+ * Author(s): Michael A. Halcrow <[email protected]>
+ * Michael C. Thompson <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <linux/dcache.h>
+#include <linux/file.h>
+#include <linux/module.h>
+#include <linux/namei.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/mount.h>
+#include <linux/dcache.h>
+#include <linux/pagemap.h>
+#include <linux/key.h>
+#include <linux/parser.h>
+#include <keys/user-type.h>
+#include "ecryptfs_kernel.h"
+
+/**
+ * Module parameter that defines the ecryptfs_verbosity level.
+ */
+int ecryptfs_verbosity = 0;
+
+module_param(ecryptfs_verbosity, int, 0);
+MODULE_PARM_DESC(ecryptfs_verbosity,
+ "Initial verbosity level (0 or 1; defaults to "
+ "0, which is Quiet)");
+
+void __ecryptfs_printk(const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ if (fmt[1] == '7') { /* KERN_DEBUG */
+ if (ecryptfs_verbosity >= 1)
+ vprintk(fmt, args);
+ } else
+ vprintk(fmt, args);
+ va_end(args);
+}
+
+/**
+ * Interposes upper and lower dentries.
+ *
+ * This function will allocate an ecryptfs_inode through the call to
+ * iget(sb, lower_inode->i_ino).
+ *
+ * @param lower_dentry existing dentry in the lower filesystem
+ * @param dentry ecryptfs' dentry
+ * @param sb eCryptfs's super_block
+ * @param flag If set to true, then d_add is called, else
+ * d_instantiate is called.
+ * @return Zero on success; non-zero otherwise
+ */
+int ecryptfs_interpose(struct dentry *lower_dentry, struct dentry *dentry,
+ struct super_block *sb, int flag)
+{
+ struct inode *lower_inode;
+ int rc = 0;
+ struct inode *inode;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; lower_dentry = [%p], "
+ "lower_dentry->d_name.name = [%s], dentry = "
+ "[%p], dentry->d_name.name = [%s], sb = [%p]; "
+ "flag = [%.4x]; lower_dentry->d_count "
+ "= [%d]; dentry->d_count = [%d]\n", lower_dentry,
+ lower_dentry->d_name.name, dentry, dentry->d_name.name,
+ sb, flag, atomic_read(&lower_dentry->d_count),
+ atomic_read(&dentry->d_count));
+ lower_inode = lower_dentry->d_inode;
+ if (lower_inode->i_sb != ECRYPTFS_SUPERBLOCK_TO_LOWER(sb)) {
+ rc = -EXDEV;
+ goto out;
+ }
+ inode = iget(sb, lower_inode->i_ino);
+ if (!inode) {
+ rc = -EACCES;
+ goto out;
+ }
+ /* This check is required here because if we failed to allocated the
+ * required space for an inode_info_cache struct, then the only way
+ * we know we failed, is by the pointer being NULL */
+ if (!ECRYPTFS_INODE_TO_PRIVATE(inode)) {
+ ecryptfs_printk(KERN_ERR, "Out of memory. Failure to "
+ "allocate memory in ecryptfs_read_inode.\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+ if (NULL == ECRYPTFS_INODE_TO_LOWER(inode))
+ ECRYPTFS_INODE_TO_LOWER(inode) = igrab(lower_inode);
+ if (S_ISLNK(lower_inode->i_mode))
+ inode->i_op = &ecryptfs_symlink_iops;
+ else if (S_ISDIR(lower_inode->i_mode))
+ inode->i_op = &ecryptfs_dir_iops;
+ if (S_ISDIR(lower_inode->i_mode))
+ inode->i_fop = &ecryptfs_dir_fops;
+ /* TODO: Is there a better way to identify if the inode is
+ * special? */
+ if (S_ISBLK(lower_inode->i_mode) || S_ISCHR(lower_inode->i_mode) ||
+ S_ISFIFO(lower_inode->i_mode) || S_ISSOCK(lower_inode->i_mode))
+ init_special_inode(inode, lower_inode->i_mode,
+ lower_inode->i_rdev);
+ dentry->d_op = &ecryptfs_dops;
+ if (flag)
+ d_add(dentry, inode);
+ else
+ d_instantiate(dentry, inode);
+ ecryptfs_copy_attr_all(inode, lower_inode);
+ /* This size will be overwritten for real files w/ headers and
+ * other metadata */
+ ecryptfs_copy_inode_size(inode, lower_inode);
+out:
+ ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+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}
+};
+
+/**
+ * @return Zero on good version; non-zero otherwise
+ */
+int ecryptfs_verify_version(uint16_t version)
+{
+ int rc = 0;
+ unsigned char major;
+ unsigned char minor;
+
+ major = ((version >> 8) & 0xFF);
+ minor = (version & 0xFF);
+ if (major != ECRYPTFS_VERSION_MAJOR) {
+ ecryptfs_printk(KERN_ERR, "Major version number mismatch. "
+ "Expected [%d]; got [%d]\n",
+ ECRYPTFS_VERSION_MAJOR, major);
+ rc = -EINVAL;
+ goto out;
+ }
+ if (minor != ECRYPTFS_VERSION_MINOR) {
+ ecryptfs_printk(KERN_ERR, "Minor version number mismatch. "
+ "Expected [%d]; got [%d]\n",
+ ECRYPTFS_VERSION_MINOR, minor);
+ rc = -EINVAL;
+ goto out;
+ }
+out:
+ return rc;
+}
+
+/**
+ * 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_stat *mount_crypt_stat =
+ &(ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb)->mount_crypt_stat);
+ substring_t args[MAX_OPT_ARGS];
+ int token;
+ char *sig_src;
+ char *sig_dst;
+ char *debug_src;
+ char *cipher_name_dst;
+ char *cipher_name_src;
+ int cipher_name_len;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; options = [%s]\n", options);
+ if (!options) {
+ rc = -EINVAL;
+ goto out;
+ }
+ while ((p = strsep(&options, ",")) != NULL) {
+ if (!*p)
+ continue;
+ token = match_token(p, tokens, args);
+ switch (token) {
+ case ecryptfs_opt_sig:
+ sig_src = args[0].from;
+ sig_dst =
+ mount_crypt_stat->global_auth_tok_sig;
+ memcpy(sig_dst, sig_src, ECRYPTFS_SIG_SIZE_HEX);
+ sig_dst[ECRYPTFS_SIG_SIZE_HEX] = '\0';
+ ecryptfs_printk(KERN_DEBUG,
+ "The mount_crypt_stat "
+ "global_auth_tok_sig set to: "
+ "[%s]\n", sig_dst);
+ sig_set = 1;
+ break;
+ case ecryptfs_opt_debug:
+ debug_src = args[0].from;
+ ecryptfs_verbosity =
+ (int)simple_strtol(debug_src, &debug_src,
+ 0);
+ ecryptfs_printk(KERN_DEBUG,
+ "Verbosity set to [%d]" "\n",
+ ecryptfs_verbosity);
+ break;
+ case ecryptfs_opt_cipher:
+ cipher_name_src = args[0].from;
+ cipher_name_dst =
+ mount_crypt_stat->
+ global_default_cipher_name;
+ strncpy(cipher_name_dst, cipher_name_src,
+ ECRYPTFS_MAX_CIPHER_NAME_SIZE);
+ ecryptfs_printk(KERN_DEBUG,
+ "The mount_crypt_stat "
+ "global_default_cipher_name set to: "
+ "[%s]\n", cipher_name_dst);
+ cipher_name_set = 1;
+ break;
+ case ecryptfs_opt_err:
+ default:
+ ecryptfs_printk(KERN_WARNING,
+ "eCryptfs: unrecognized option '%s'\n",
+ options);
+ }
+ }
+ /* Do not support lack of mount-wide signature in 0.1
+ * release */
+ if (!sig_set) {
+ rc = -EINVAL;
+ ecryptfs_printk(KERN_ERR, "You must supply a valid "
+ "passphrase auth tok signature as a mount "
+ "parameter; see the eCryptfs README\n");
+ goto out;
+ }
+ if (!cipher_name_set) {
+ cipher_name_len = strlen(ECRYPTFS_DEFAULT_CIPHER);
+ if (unlikely(cipher_name_len
+ >= ECRYPTFS_MAX_CIPHER_NAME_SIZE)) {
+ rc = -EINVAL;
+ BUG();
+ goto out;
+ }
+ memcpy(mount_crypt_stat->global_default_cipher_name,
+ ECRYPTFS_DEFAULT_CIPHER, cipher_name_len);
+ mount_crypt_stat->global_default_cipher_name[cipher_name_len]
+ = '\0';
+ }
+ ecryptfs_printk(KERN_DEBUG, "Requesting the key with description: "
+ "[%s]\n", mount_crypt_stat->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_stat->global_auth_tok_sig,
+ NULL);
+ if (!auth_tok_key || IS_ERR(auth_tok_key)) {
+ ecryptfs_printk(KERN_ERR, "Could not find key with "
+ "description: [%s]\n",
+ mount_crypt_stat->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 (ecryptfs_verify_version(auth_tok->version)) {
+ ecryptfs_printk(KERN_ERR, "Data structure version mismatch. "
+ "Userspace tools must match eCryptfs kernel "
+ "module with major version [%d] and minor "
+ "version [%d]\n", ECRYPTFS_VERSION_MAJOR,
+ ECRYPTFS_VERSION_MINOR);
+ rc = -EINVAL;
+ goto out;
+ }
+ if (!ECRYPTFS_CHECK_FLAG(auth_tok->flags, ECRYPTFS_PASSWORD)) {
+ ecryptfs_printk(KERN_ERR, "Invalid auth_tok structure "
+ "returned from key\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ mount_crypt_stat->global_auth_tok_key = auth_tok_key;
+ mount_crypt_stat->global_auth_tok = auth_tok;
+out:
+ ecryptfs_printk(KERN_DEBUG, "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(KERN_DEBUG, "Enter; sb = [%p], sb->s_root = [%p] "
+ "ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb) = [%p] "
+ "sb->s_root.d_name->name = [%s]\n", sb,
+ sb->s_root, ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb),
+ sb->s_root->d_name.name);
+ up_write(&sb->s_umount);
+ deactivate_super(sb);
+ ecryptfs_printk(KERN_DEBUG, "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 rc = 0;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; sb = [%p] raw_data = [%s] "
+ "silent = [%d]\n", sb, (char *)raw_data, silent);
+ /* Released in ecryptfs_put_super() */
+ ECRYPTFS_SUPERBLOCK_TO_PRIVATE_SM(sb) =
+ kmem_cache_alloc(ecryptfs_sb_info_cache, SLAB_KERNEL);
+ if (!ECRYPTFS_SUPERBLOCK_TO_PRIVATE_SM(sb)) {
+ ecryptfs_printk(KERN_WARNING, "Out of memory\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+ memset(ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb), 0,
+ sizeof(struct ecryptfs_sb_info));
+ sb->s_op = &ecryptfs_sops;
+ /* Released through deactivate_super(sb) from get_sb_nodev */
+ sb->s_root = d_alloc(NULL, &(const struct qstr) {
+ .hash = 0,.name = "/",.len = 1});
+ if (!sb->s_root) {
+ ecryptfs_printk(KERN_ERR, "d_alloc failed\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+ sb->s_root->d_op = &ecryptfs_dops;
+ sb->s_root->d_sb = sb;
+ sb->s_root->d_parent = sb->s_root;
+ /* Released in d_release when dput(sb->s_root) is called */
+ /* through deactivate_super(sb) from get_sb_nodev() */
+ ECRYPTFS_DENTRY_TO_PRIVATE_SM(sb->s_root) =
+ (struct ecryptfs_dentry_info *)
+ kmem_cache_alloc(ecryptfs_dentry_info_cache, SLAB_KERNEL);
+ if (!ECRYPTFS_DENTRY_TO_PRIVATE_SM(sb->s_root)) {
+ ecryptfs_printk(KERN_ERR,
+ "dentry_info_cache alloc failed\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+ memset(ECRYPTFS_DENTRY_TO_PRIVATE(sb->s_root), 0,
+ sizeof(struct ecryptfs_dentry_info));
+ rc = 0;
+out:
+ /* Should be able to rely on deactive_super called from
+ * get_sb_nodev */
+ ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * 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 rc;
+ struct nameidata nd;
+ struct dentry *lower_root;
+
+ memset(&nd, 0, sizeof(struct nameidata));
+ ecryptfs_printk(KERN_DEBUG, "Enter; sb = [%p], dev_name = [%s]\n",
+ sb, dev_name);
+ rc = path_lookup(dev_name, LOOKUP_FOLLOW, &nd);
+ if (rc) {
+ ecryptfs_printk(KERN_WARNING, "path_lookup() failed\n");
+ goto out_free;
+ }
+ lower_root = nd.dentry;
+ ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb)->lower_mnt = nd.mnt;
+ if (!lower_root->d_inode) {
+ ecryptfs_printk(KERN_WARNING,
+ "No directory to interpose on\n");
+ rc = -ENOENT;
+ goto out_free;
+ }
+ ECRYPTFS_SUPERBLOCK_TO_LOWER(sb) = lower_root->d_sb;
+ sb->s_maxbytes = lower_root->d_sb->s_maxbytes;
+ ECRYPTFS_DENTRY_TO_LOWER(sb->s_root) = lower_root;
+ if ((rc = ecryptfs_interpose(lower_root, sb->s_root, sb, 0)))
+ goto out_free;
+ rc = 0;
+ goto out;
+out_free:
+ path_release(&nd);
+ ecryptfs_cleanup_read_super(sb);
+out:
+ ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * 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 rc;
+ struct super_block *sb = NULL;
+
+ ecryptfs_printk(KERN_DEBUG, "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(KERN_ERR, "Getting sb failed. "
+ "sb = [%p]\n", sb);
+ goto out;
+ }
+ rc = ecryptfs_parse_options(sb, raw_data);
+ if (rc) {
+ sb = ERR_PTR(rc);
+ goto out;
+ }
+ rc = ecryptfs_read_super(sb, dev_name);
+ if (rc) {
+ sb = ERR_PTR(rc);
+ ecryptfs_printk(KERN_ERR, "Reading sb failed. "
+ "sb = [%p]\n", sb);
+ }
+out:
+ ecryptfs_printk(KERN_DEBUG, "Exit; sb = [%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)
+{
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat =
+ &(ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb)->mount_crypt_stat);
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; sb = [%p], sb->s_root = [%p] "
+ "ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb) = [%p]\n", sb,
+ sb->s_root, ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb));
+ memset(mount_crypt_stat, 0, sizeof(struct ecryptfs_mount_crypt_stat));
+ generic_shutdown_super(sb);
+ ecryptfs_printk(KERN_DEBUG, "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;
+
+/**
+ * Sets ecryptfs_allocated_caches with flags so that we can
+ * free created caches should we run out of memory during
+ * creation period.
+ *
+ * @return Zero on success; non-zero otherwise
+ */
+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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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;
+
+ if (ECRYPTFS_DEFAULT_EXTENT_SIZE > PAGE_CACHE_SIZE) {
+ rc = -EINVAL;
+ ecryptfs_printk(KERN_ERR, "The eCryptfs extent size is "
+ "larger than the host's page size, and so "
+ "eCryptfs cannot run on this system. The "
+ "default eCryptfs extent size is [%d] bytes; "
+ "the page size is [%d] bytes.\n",
+ ECRYPTFS_DEFAULT_EXTENT_SIZE, PAGE_CACHE_SIZE);
+ goto out;
+ }
+ rc = ecryptfs_init_kmem_caches();
+ if (rc) {
+ ecryptfs_printk(KERN_EMERG, "Failure occured while "
+ "attempting to create caches [Mask of created "
+ "caches: 0x%x]. Now freeing caches.\n",
+ ecryptfs_allocated_caches);
+ ecryptfs_free_kmem_caches();
+ rc = -ENOMEM;
+ goto out;
+ }
+ ecryptfs_printk(KERN_DEBUG, "Registering eCryptfs\n");
+ rc = register_filesystem(&ecryptfs_fs_type);
+out:
+ return rc;
+}
+
+static void __exit exit_ecryptfs_fs(void)
+{
+ int rc;
+
+ ecryptfs_printk(KERN_DEBUG, "Unregistering eCryptfs\n");
+ unregister_filesystem(&ecryptfs_fs_type);
+ rc = ecryptfs_free_kmem_caches();
+ if (rc)
+ ecryptfs_printk(KERN_EMERG, "Failure occured while "
+ "attempting to free caches: [%d]\n", rc);
+}
+
+MODULE_AUTHOR("Michael A. Halcrow <[email protected]>");
+MODULE_DESCRIPTION("eCryptfs");
+
+MODULE_LICENSE("GPL");
+
+module_init(init_ecryptfs_fs)
+module_exit(exit_ecryptfs_fs)

2006-05-04 03:37:50

by Phillip Hellewell

[permalink] [raw]
Subject: [PATCH 5/13: eCryptfs] Header declarations

This is the 5th patch in a series of 13 constituting the kernel
components of the eCryptfs cryptographic filesystem.

This header contains declarations for various structs used in
eCryptfs.

Signed-off-by: Phillip Hellewell <[email protected]>
Signed-off-by: Michael Halcrow <[email protected]>

---

ecryptfs_kernel.h | 400 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 400 insertions(+)

Index: linux-2.6.17-rc3-mm1-ecryptfs/fs/ecryptfs/ecryptfs_kernel.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.17-rc3-mm1-ecryptfs/fs/ecryptfs/ecryptfs_kernel.h 2006-05-02 19:36:00.000000000 -0600
@@ -0,0 +1,400 @@
+/**
+ * eCryptfs: Linux filesystem encryption layer
+ * Kernel declarations.
+ *
+ * Copyright (C) 1997-2003 Erez Zadok
+ * Copyright (C) 2001-2003 Stony Brook University
+ * Copyright (C) 2004-2006 International Business Machines Corp.
+ * Author(s): Michael A. Halcrow <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef ECRYPTFS_KERNEL_H
+#define ECRYPTFS_KERNEL_H
+
+#ifdef CONFIG_ECRYPT_DEBUG
+#define OBSERVE_ASSERTS 1
+#endif
+
+#include <linux/fs.h>
+#include <asm/semaphore.h>
+#include <asm/scatterlist.h>
+
+/* Version verification for shared data structures w/ userspace */
+#ifndef ECRYPTFS_VERSION_MAJOR
+#define ECRYPTFS_VERSION_MAJOR 0x00
+#endif
+#ifndef ECRYPTFS_VERSION_MINOR
+#define ECRYPTFS_VERSION_MINOR 0x01
+#endif
+
+#ifndef ECRYPTFS_SUPPORTED_FILE_VERSION
+#define ECRYPTFS_SUPPORTED_FILE_VERSION 0x01
+#endif
+
+#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 16
+#define ECRYPTFS_FILE_VERSION 0x01
+#define ECRYPTFS_DEFAULT_HEADER_EXTENT_SIZE 8192
+#define ECRYPTFS_DEFAULT_EXTENT_SIZE 4096
+#define ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE 8192
+
+#define ECRYPTFS_SET_FLAG(flag_bit_vector, flag) (flag_bit_vector |= (flag))
+#define ECRYPTFS_CLEAR_FLAG(flag_bit_vector, flag) (flag_bit_vector &= ~(flag))
+#define ECRYPTFS_CHECK_FLAG(flag_bit_vector, flag) (flag_bit_vector & (flag))
+
+/**
+ * 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 0x00000001
+#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT 0x00000002
+#define ECRYPTFS_CONTAINS_DECRYPTED_KEY 0x00000004
+#define ECRYPTFS_CONTAINS_ENCRYPTED_KEY 0x00000008
+ int32_t flags;
+ int32_t encrypted_key_size;
+ int32_t decrypted_key_size;
+ uint8_t encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES];
+ uint8_t decrypted_key[ECRYPTFS_MAX_KEY_BYTES];
+};
+
+struct ecryptfs_password {
+ int32_t password_bytes;
+ int32_t hash_algo;
+ int32_t hash_iterations;
+ int32_t session_key_encryption_key_bytes;
+#define ECRYPTFS_PERSISTENT_PASSWORD 0x01
+#define ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET 0x02
+ uint32_t flags;
+ /* Iterated-hash concatenation of salt and passphrase */
+ uint8_t session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES];
+ uint8_t signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
+ /* Always in expanded hex */
+ uint8_t salt[ECRYPTFS_SALT_SIZE];
+};
+
+/* May be a password or a private key */
+struct ecryptfs_auth_tok {
+ uint16_t version; /* 8-bit major and 8-bit minor */
+#define ECRYPTFS_PASSWORD 0x00000001
+#define ECRYPTFS_PRIVATE_KEY 0x00000002
+#define ECRYPTFS_CONTAINS_SECRET 0x00000004
+#define ECRYPTFS_EXPIRED 0x00000008
+ uint32_t flags;
+ uid_t uid;
+ int64_t creation_time;
+ int64_t expiration_time;
+ union {
+ struct ecryptfs_password password;
+ /* Private key is in future eCryptfs releases */
+ } token;
+ struct ecryptfs_session_key session_key;
+};
+
+void ecryptfs_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);
+
+struct ecryptfs_key_record {
+ u16 enc_key_size_bits;
+ unsigned char type;
+ unsigned char sig[ECRYPTFS_SIG_SIZE];
+ unsigned char enc_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES];
+};
+
+struct ecryptfs_auth_tok_list {
+ struct ecryptfs_auth_tok *auth_tok;
+ struct list_head list;
+};
+
+struct ecryptfs_crypt_stat;
+struct ecryptfs_mount_crypt_stat;
+
+struct ecryptfs_page_crypt_context {
+ struct page *page;
+#define ECRYPTFS_PREPARE_COMMIT_MODE 0
+#define ECRYPTFS_WRITEPAGE_MODE 1
+ int mode;
+ union {
+ struct file *lower_file;
+ struct writeback_control *wbc;
+ } param;
+};
+
+#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 2 /* TODO: Make this a linked list */
+#define ECRYPTFS_MAX_IV_BYTES 16 /* 128 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 "aes"
+#define ECRYPTFS_DEFAULT_KEY_BYTES 16
+#define ECRYPTFS_DEFAULT_CHAINING_MODE CRYPTO_TFM_MODE_CBC
+#define ECRYPTFS_TAG_3_PACKET_TYPE 0x8C
+#define ECRYPTFS_TAG_11_PACKET_TYPE 0xED
+#ifndef MD5_DIGEST_SIZE
+#define MD5_DIGEST_SIZE 16
+#endif
+
+/**
+ * This is the primary struct associated with each encrypted file.
+ *
+ * TODO: cache align/pack?
+ */
+struct ecryptfs_crypt_stat {
+#define ECRYPTFS_STRUCT_INITIALIZED 0x00000001
+#define ECRYPTFS_POLICY_APPLIED 0x00000002
+#define ECRYPTFS_NEW_FILE 0x00000004
+#define ECRYPTFS_ENCRYPTED 0x00000008
+#define ECRYPTFS_SECURITY_WARNING 0x00000010
+#define ECRYPTFS_ENABLE_HMAC 0x00000020
+#define ECRYPTFS_ENCRYPT_IV_PAGES 0x00000040
+#define ECRYPTFS_KEY_VALID 0x00000080
+ u32 flags;
+ int file_version;
+ int iv_bytes;
+ int num_keysigs;
+ int header_extent_size;
+ int num_header_extents_at_front; /* Number of header extents
+ * at the front of the file */
+ int extent_size; /* Data extent size; default is 4096 */
+ int key_size_bits;
+ unsigned int extent_shift;
+ unsigned int extent_mask;
+ struct crypto_tfm *tfm;
+ struct crypto_tfm *md5_tfm; /* Crypto context for generating
+ * the initialization vectors */
+ char cipher[ECRYPTFS_MAX_CIPHER_NAME_SIZE];
+ unsigned char key[ECRYPTFS_MAX_KEY_BYTES];
+ unsigned char root_iv[ECRYPTFS_MAX_IV_BYTES];
+ char keysigs[ECRYPTFS_MAX_NUM_KEYSIGS][ECRYPTFS_SIG_SIZE_HEX];
+ struct mutex cs_mutex;
+};
+
+/* inode private data. */
+struct ecryptfs_inode_info {
+ struct inode *wii_inode;
+ struct inode vfs_inode;
+ struct ecryptfs_crypt_stat crypt_stat;
+};
+
+/* dentry private data. */
+struct ecryptfs_dentry_info {
+ struct dentry *wdi_dentry;
+ struct ecryptfs_crypt_stat *crypt_stat;
+};
+
+/**
+ * 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_stat {
+ /* Pointers to memory we do not own, do not free these */
+ struct ecryptfs_auth_tok *global_auth_tok;
+ struct key *global_auth_tok_key;
+ char global_default_cipher_name[ECRYPTFS_MAX_CIPHER_NAME_SIZE + 1];
+ char global_auth_tok_sig[ECRYPTFS_SIG_SIZE_HEX + 1];
+};
+
+/* superblock private data. */
+struct ecryptfs_sb_info {
+ struct super_block *wsi_sb;
+ struct vfsmount *lower_mnt;
+ struct ecryptfs_mount_crypt_stat mount_crypt_stat;
+};
+
+/* file private data. */
+struct ecryptfs_file_info {
+ struct file *wfi_file;
+ struct ecryptfs_crypt_stat *crypt_stat;
+};
+
+/* auth_tok <=> encrypted_session_key mappings */
+struct ecryptfs_auth_tok_list_item {
+ char encrypted_session_key[ECRYPTFS_MAX_KEY_BYTES];
+ struct list_head list;
+ struct ecryptfs_auth_tok auth_tok;
+};
+
+#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 */
+
+#define ECRYPTFS_FILE_TO_PRIVATE(file) ((struct ecryptfs_file_info *) \
+ ((file)->private_data))
+#define ECRYPTFS_FILE_TO_PRIVATE_SM(file) ((file)->private_data)
+#define ECRYPTFS_FILE_TO_LOWER(file) \
+ ((ECRYPTFS_FILE_TO_PRIVATE(file))->wfi_file)
+#define ECRYPTFS_INODE_TO_PRIVATE(ino) ((struct ecryptfs_inode_info *) \
+ (ino)->u.generic_ip)
+#define ECRYPTFS_INODE_TO_PRIVATE_SM(ino) ((ino)->u.generic_ip)
+#define ECRYPTFS_INODE_TO_LOWER(ino) (ECRYPTFS_INODE_TO_PRIVATE(ino)->wii_inode)
+#define ECRYPTFS_SUPERBLOCK_TO_PRIVATE(super) ((struct ecryptfs_sb_info *) \
+ (super)->s_fs_info)
+#define ECRYPTFS_SUPERBLOCK_TO_PRIVATE_SM(super) ((super)->s_fs_info)
+#define ECRYPTFS_SUPERBLOCK_TO_LOWER(super) \
+ (ECRYPTFS_SUPERBLOCK_TO_PRIVATE(super)->wsi_sb)
+#define ECRYPTFS_DENTRY_TO_PRIVATE_SM(dentry) ((dentry)->d_fsdata)
+#define ECRYPTFS_DENTRY_TO_PRIVATE(dentry) ((struct ecryptfs_dentry_info *) \
+ (dentry)->d_fsdata)
+#define ECRYPTFS_DENTRY_TO_LOWER(dentry) \
+ (ECRYPTFS_DENTRY_TO_PRIVATE(dentry)->wdi_dentry)
+
+#define ecryptfs_printk(type, fmt, arg...) \
+ __ecryptfs_printk(type "%s: " fmt, __FUNCTION__, ## arg);
+void __ecryptfs_printk(const char *fmt, ...);
+
+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;
+extern int ecryptfs_verbosity;
+
+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(struct ecryptfs_crypt_stat *crypt_stat,
+ const char *name, int length,
+ char **decrypted_name);
+int ecryptfs_encode_filename(struct ecryptfs_crypt_stat *crypt_stat,
+ const char *name, int length,
+ char **encoded_name);
+struct dentry *ecryptfs_lower_dentry(struct dentry *this_dentry);
+void ecryptfs_copy_attr_times(struct inode *dest, const struct inode *src);
+void ecryptfs_copy_attr_atime(struct inode *dest, const struct inode *src);
+void ecryptfs_copy_attr_all(struct inode *dest, const struct inode *src);
+void ecryptfs_copy_inode_size(struct inode *dst, const struct inode *src);
+void ecryptfs_dump_hex(char *data, int bytes);
+int virt_to_scatterlist(const void *addr, int size, struct scatterlist *sg,
+ int sg_size);
+int ecryptfs_calculate_md5(char *dst, struct ecryptfs_crypt_stat *crypt_stat,
+ char *src, int len);
+int ecryptfs_derive_iv(char *iv, struct ecryptfs_crypt_stat *crypt_stat,
+ pgoff_t offset);
+int ecryptfs_compute_root_iv(struct ecryptfs_crypt_stat *crypt_stat);
+void ecryptfs_rotate_iv(unsigned char *iv);
+void ecryptfs_init_crypt_stat(struct ecryptfs_crypt_stat *crypt_stat);
+void ecryptfs_destruct_crypt_stat(struct ecryptfs_crypt_stat *crypt_stat);
+int ecryptfs_init_crypt_ctx(struct ecryptfs_crypt_stat *crypt_stat);
+int ecryptfs_write_inode_size_to_header(struct file *lower_file,
+ struct inode *lower_inode,
+ struct inode *inode);
+int ecryptfs_get_lower_page(struct page **lower_page, struct inode *lower_inode,
+ struct file *lower_file,
+ unsigned long lower_page_index, int byte_offset,
+ int region_bytes);
+int
+ecryptfs_commit_lower_page(struct page *lower_page, struct inode *lower_inode,
+ struct file *lower_file, int byte_offset,
+ int region_size);
+int ecryptfs_copy_page_to_lower(struct page *page, struct inode *lower_inode,
+ struct file *lower_file);
+int ecryptfs_do_readpage(struct file *file, struct page *page,
+ pgoff_t lower_page_index);
+int ecryptfs_grab_and_map_lower_page(struct page **lower_page,
+ char **lower_virt,
+ struct inode *lower_inode,
+ unsigned long lower_page_index);
+int ecryptfs_writepage_and_release_lower_page(struct page *lower_page,
+ struct inode *lower_inode,
+ struct writeback_control *wbc);
+int ecryptfs_encrypt_page(struct ecryptfs_page_crypt_context *ctx);
+int
+ecryptfs_encrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat,
+ struct page *dst_page, int dst_offset,
+ struct page *src_page, int src_offset, int size,
+ unsigned char *iv);
+int ecryptfs_decrypt_page(struct file *file, struct page *page);
+int
+ecryptfs_decrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat,
+ struct page *dst_page, int dst_offset,
+ struct page *src_page, int src_offset, int size,
+ unsigned char *iv);
+int ecryptfs_write_headers(struct dentry *ecryptfs_dentry,
+ struct file *lower_file);
+int ecryptfs_write_headers_virt(char *page_virt,
+ struct ecryptfs_crypt_stat *crypt_stat,
+ struct dentry *ecryptfs_dentry);
+int ecryptfs_read_headers(struct dentry *ecryptfs_dentry,
+ struct file *lower_file);
+int ecryptfs_new_file_context(struct dentry *ecryptfs_dentry);
+int contains_ecryptfs_marker(char *data);
+int ecryptfs_read_header_region(char *data, struct dentry *dentry,
+ struct nameidata *nd);
+u16 ecryptfs_code_for_cipher_string(char *str);
+int ecryptfs_cipher_code_to_string(char *str, u16 cipher_code);
+void ecryptfs_set_default_sizes(struct ecryptfs_crypt_stat *crypt_stat);
+int ecryptfs_generate_key_packet_set(char *dest_base,
+ struct ecryptfs_crypt_stat *crypt_stat,
+ struct dentry *ecryptfs_dentry, int *len);
+int process_request_key_err(long err_code);
+int
+ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat,
+ unsigned char *src, struct dentry *ecryptfs_dentry);
+int ecryptfs_truncate(struct dentry *dentry, loff_t new_length);
+
+#endif /* #ifndef ECRYPTFS_KERNEL_H */

2006-05-04 03:38:28

by Phillip Hellewell

[permalink] [raw]
Subject: [PATCH 6/13: eCryptfs] Superblock operations

This is the 6th patch in a series of 13 constituting the kernel
components of the eCryptfs cryptographic filesystem.

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

Signed-off-by: Phillip Hellewell <[email protected]>
Signed-off-by: Michael Halcrow <[email protected]>

---

super.c | 225 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 225 insertions(+)

Index: linux-2.6.17-rc3-mm1-ecryptfs/fs/ecryptfs/super.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.17-rc3-mm1-ecryptfs/fs/ecryptfs/super.c 2006-05-02 19:36:04.000000000 -0600
@@ -0,0 +1,225 @@
+/**
+ * eCryptfs: Linux filesystem encryption layer
+ *
+ * Copyright (C) 1997-2003 Erez Zadok
+ * Copyright (C) 2001-2003 Stony Brook University
+ * Copyright (C) 2004-2006 International Business Machines Corp.
+ * Author(s): Michael A. Halcrow <[email protected]>
+ * Michael C. Thompson <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/key.h>
+#include <linux/seq_file.h>
+#include "ecryptfs_kernel.h"
+
+kmem_cache_t *ecryptfs_inode_info_cache;
+
+/**
+ * Called to bring an inode into existence.
+ *
+ * Note that setting the self referencing pointer doesn't work here:
+ * i.e. ECRYPTFS_INODE_TO_PRIVATE_SM(inode) = ei;
+ *
+ * Only handle allocation, setting up structures should be done in
+ * ecryptfs_read_inode. This is because the kernel, between now and
+ * then, will 0 out the private data pointer.
+ *
+ * @param sb Pointer to the super block of the filesystem
+ * @return Pointer to a newly allocated inode, NULL otherwise
+ */
+static struct inode *ecryptfs_alloc_inode(struct super_block *sb) {
+ struct ecryptfs_inode_info *ecryptfs_inode = NULL;
+ struct inode *inode = NULL;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; sb = [%p]\n", sb);
+ ecryptfs_inode = kmem_cache_alloc(ecryptfs_inode_info_cache,
+ SLAB_KERNEL);
+ if (unlikely(!ecryptfs_inode)) {
+ ecryptfs_printk(KERN_WARNING,
+ "Failed to allocate new inode\n");
+ goto out;
+ }
+ ecryptfs_init_crypt_stat(&(ecryptfs_inode->crypt_stat));
+ inode = &(ecryptfs_inode->vfs_inode);
+out:
+ ecryptfs_printk(KERN_DEBUG, "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_stat 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_stat *crypt_stat;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; inode = [%p]\n", inode);
+ crypt_stat = &(ECRYPTFS_INODE_TO_PRIVATE(inode))->crypt_stat;
+ ecryptfs_destruct_crypt_stat(crypt_stat);
+ kmem_cache_free(ecryptfs_inode_info_cache,
+ ECRYPTFS_INODE_TO_PRIVATE(inode));
+ ecryptfs_printk(KERN_DEBUG, "Exit\n");
+}
+
+/**
+ * Set up the ecryptfs inode.
+ */
+static void ecryptfs_read_inode(struct inode *inode)
+{
+ ecryptfs_printk(KERN_DEBUG, "Enter; inode = [%p]\n", inode);
+ /* This is where we setup the self-reference in the vfs_inode's
+ * u.generic_ip. That way we don't have to walk the list again. */
+ ECRYPTFS_INODE_TO_PRIVATE_SM(inode) =
+ list_entry(inode, struct ecryptfs_inode_info, vfs_inode);
+ ECRYPTFS_INODE_TO_LOWER(inode) = NULL;
+ inode->i_version++;
+ inode->i_op = &ecryptfs_main_iops;
+ inode->i_fop = &ecryptfs_main_fops;
+ inode->i_mapping->a_ops = &ecryptfs_aops;
+ ecryptfs_printk(KERN_DEBUG, "Exit\n");
+}
+
+
+/**
+ * This is called through iput_final().
+ * This is function will replace generic_drop_inode. The end result of which
+ * is we are skipping the check in inode->i_nlink, which we do not use.
+ */
+static void ecryptfs_drop_inode(struct inode *inode) {
+ generic_delete_inode(inode);
+}
+
+/**
+ * Final actions when unmounting a file system.
+ * This will handle deallocation and release of our private data.
+ */
+static void ecryptfs_put_super(struct super_block *sb)
+{
+ struct ecryptfs_sb_info *sb_info = ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb);
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ mntput(sb_info->lower_mnt);
+ key_put(sb_info->mount_crypt_stat.global_auth_tok_key);
+ kmem_cache_free(ecryptfs_sb_info_cache, sb_info);
+ ECRYPTFS_SUPERBLOCK_TO_PRIVATE_SM(sb) = NULL;
+ ecryptfs_printk(KERN_DEBUG, "Exit\n");
+}
+
+/**
+ * Get the filesystem statistics. Currently, we let this pass right through
+ * to the lower filesystem and take no action ourselves.
+ */
+static int ecryptfs_statfs(struct super_block *sb, struct kstatfs *buf)
+{
+ int rc = 0;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ rc = vfs_statfs(ECRYPTFS_SUPERBLOCK_TO_LOWER(sb), buf);
+ ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * Called to ask filesystem to change mount options. Not implemented;
+ * returns -ENOSYS every time.
+ */
+static int ecryptfs_remount_fs(struct super_block *sb, int *flags, char *data)
+{
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ ecryptfs_printk(KERN_DEBUG, "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.
+ */
+static void ecryptfs_clear_inode(struct inode *inode)
+{
+ ecryptfs_printk(KERN_DEBUG, "Enter; inode = [%p]; i_ino = [0x%.16x]\n",
+ inode, inode->i_ino);
+ iput(ECRYPTFS_INODE_TO_LOWER(inode));
+ ecryptfs_printk(KERN_DEBUG, "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.
+ */
+static void ecryptfs_umount_begin(struct vfsmount *vfsmnt, int flags)
+{
+ struct vfsmount *lower_mnt;
+ struct super_block *lower_sb;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ lower_mnt = ECRYPTFS_SUPERBLOCK_TO_PRIVATE(vfsmnt->mnt_sb)->lower_mnt;
+ lower_sb = lower_mnt->mnt_sb;
+ if (lower_sb->s_op->umount_begin)
+ lower_sb->s_op->umount_begin(lower_mnt, flags);
+ ecryptfs_printk(KERN_DEBUG, "Exit\n");
+}
+
+/**
+ * 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 rc = 0;
+ char *tmp = NULL;
+ char *path;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ tmp = (char *)__get_free_page(GFP_KERNEL);
+ if (!tmp) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ path = d_path(ECRYPTFS_DENTRY_TO_LOWER(sb->s_root),
+ ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb)->lower_mnt, tmp,
+ PAGE_SIZE);
+ seq_printf(m, ",dir=%s", path);
+ free_page((unsigned long)tmp);
+out:
+ ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+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
+};

2006-05-04 03:39:11

by Phillip Hellewell

[permalink] [raw]
Subject: [PATCH 7/13: eCryptfs] Dentry operations

This is the 7th patch in a series of 13 constituting the kernel
components of the eCryptfs cryptographic filesystem.

eCryptfs dentry operations.

Signed-off-by: Phillip Hellewell <[email protected]>
Signed-off-by: Michael Halcrow <[email protected]>

---

dentry.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 91 insertions(+)

Index: linux-2.6.17-rc3-mm1-ecryptfs/fs/ecryptfs/dentry.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.17-rc3-mm1-ecryptfs/fs/ecryptfs/dentry.c 2006-05-02 19:36:00.000000000 -0600
@@ -0,0 +1,91 @@
+/**
+ * eCryptfs: Linux filesystem encryption layer
+ *
+ * Copyright (C) 1997-2003 Erez Zadok
+ * Copyright (C) 2001-2003 Stony Brook University
+ * Copyright (C) 2004-2006 International Business Machines Corp.
+ * Author(s): Michael A. Halcrow <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <linux/dcache.h>
+#include <linux/namei.h>
+#include "ecryptfs_kernel.h"
+
+/**
+ * Called when the VFS needs to revalidate a dentry. This
+ * is called whenever a name lookup finds a dentry in the
+ * dcache. Most filesystems leave this as NULL, because all their
+ * dentries in the dcache are valid.
+ *
+ * @param dentry ecryptfs dentry
+ * @param nd
+ * @return 1 if valid, 0 otherwise
+ */
+static int ecryptfs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
+{
+ int err = 1;
+ struct dentry *lower_dentry;
+ struct dentry *saved_dentry;
+ struct vfsmount *saved_vfsmount;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; dentry->d_name.name = [%s]\n",
+ dentry->d_name.name);
+ lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(dentry);
+ if (!lower_dentry) {
+ err = 0;
+ goto out;
+ }
+ if (!lower_dentry->d_op || !lower_dentry->d_op->d_revalidate)
+ goto out;
+ saved_dentry = nd->dentry;
+ saved_vfsmount = nd->mnt;
+ nd->dentry = lower_dentry;
+ nd->mnt = ECRYPTFS_SUPERBLOCK_TO_PRIVATE(dentry->d_sb)->lower_mnt;
+ err = lower_dentry->d_op->d_revalidate(lower_dentry, nd);
+ nd->dentry = saved_dentry;
+ nd->mnt = saved_vfsmount;
+out:
+ ecryptfs_printk(KERN_DEBUG, "Exit; err = [%d]\n", err);
+ return err;
+}
+
+kmem_cache_t *ecryptfs_dentry_info_cache;
+
+/**
+ * Called when a dentry is really deallocated.
+ */
+static void ecryptfs_d_release(struct dentry *dentry)
+{
+ struct dentry *lower_dentry;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; dentry->d_name->name = [%s]\n",
+ dentry->d_name.name);
+ lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(dentry);
+ if (ECRYPTFS_DENTRY_TO_PRIVATE(dentry))
+ kmem_cache_free(ecryptfs_dentry_info_cache,
+ ECRYPTFS_DENTRY_TO_PRIVATE(dentry));
+ if (lower_dentry)
+ dput(lower_dentry);
+ ecryptfs_printk(KERN_DEBUG, "Exit\n");
+ return;
+}
+
+struct dentry_operations ecryptfs_dops = {
+ .d_revalidate = ecryptfs_d_revalidate,
+ .d_release = ecryptfs_d_release,
+};

2006-05-04 03:39:50

by Phillip Hellewell

[permalink] [raw]
Subject: [PATCH 8/13: eCryptfs] File operations

This is the 8th patch in a series of 13 constituting the kernel
components of the eCryptfs cryptographic filesystem.

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

Signed-off-by: Phillip Hellewell <[email protected]>
Signed-off-by: Michael Halcrow <[email protected]>

---

file.c | 642 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 642 insertions(+)

Index: linux-2.6.17-rc3-mm1-ecryptfs/fs/ecryptfs/file.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.17-rc3-mm1-ecryptfs/fs/ecryptfs/file.c 2006-05-02 19:36:01.000000000 -0600
@@ -0,0 +1,642 @@
+/**
+ * eCryptfs: Linux filesystem encryption layer
+ *
+ * Copyright (C) 1997-2004 Erez Zadok
+ * Copyright (C) 2001-2004 Stony Brook University
+ * Copyright (C) 2004-2006 International Business Machines Corp.
+ * Author(s): Michael A. Halcrow <[email protected]>
+ * Michael C. Thompson <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <linux/file.h>
+#include <linux/poll.h>
+#include <linux/mount.h>
+#include <linux/pagemap.h>
+#include <linux/security.h>
+#include <linux/smp_lock.h>
+#include <linux/compat.h>
+#include "ecryptfs_kernel.h"
+
+/**
+ * @param file File we are seeking in
+ * @param offset The offset to seek to
+ * @param origin 2: offset from i_size; 1: offset from f_pos
+ * @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 rv;
+ loff_t new_end_pos;
+ int rc;
+ int expanding_file = 0;
+ struct inode *inode = file->f_mapping->host;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; offset = [0x%.16x]\n", offset);
+ ecryptfs_printk(KERN_DEBUG, "origin = [%d]\n", origin);
+ ecryptfs_printk(KERN_DEBUG, "inode w/ addr = [0x%p], i_ino = [0x%.16x] "
+ "size: [0x%.16x]\n", inode, inode->i_ino,
+ i_size_read(inode));
+ /* If our offset is past the end of our file, we're going to
+ * need to grow it so we have a valid length of 0's */
+ new_end_pos = offset;
+ switch (origin) {
+ case 2:
+ new_end_pos += i_size_read(inode);
+ expanding_file = 1;
+ break;
+ case 1:
+ new_end_pos += file->f_pos;
+ if (new_end_pos > i_size_read(inode)) {
+ ecryptfs_printk(KERN_DEBUG, "new_end_pos(=[0x%.16x]) "
+ "> i_size_read(inode)(=[0x%.16x])\n",
+ new_end_pos, i_size_read(inode));
+ expanding_file = 1;
+ }
+ break;
+ default:
+ if (new_end_pos > i_size_read(inode)) {
+ ecryptfs_printk(KERN_DEBUG, "new_end_pos(=[0x%.16x]) "
+ "> i_size_read(inode)(=[0x%.16x])\n",
+ new_end_pos, i_size_read(inode));
+ expanding_file = 1;
+ }
+ }
+ ecryptfs_printk(KERN_DEBUG, "new_end_pos = [0x%.16x]\n", new_end_pos);
+ if (expanding_file) {
+ rc = ecryptfs_truncate(file->f_dentry, new_end_pos);
+ if (rc) {
+ rv = rc;
+ ecryptfs_printk(KERN_ERR, "Error on attempt to "
+ "truncate to (higher) offset [0x%.16x];"
+ " rc = [%d]\n", rc, new_end_pos);
+ goto out;
+ }
+ }
+ rv = generic_file_llseek(file, offset, origin);
+out:
+ ecryptfs_printk(KERN_DEBUG, "Exit; rv = [0x%.16x]\n", rv);
+ return rv;
+}
+
+/**
+ * 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 rc = 0;
+ struct dentry *lower_dentry;
+ struct vfsmount *lower_vfsmount;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ rc = generic_file_read(file, buf, count, ppos);
+ if (rc >= 0) {
+ lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(file->f_dentry);
+ lower_vfsmount = ECRYPTFS_SUPERBLOCK_TO_PRIVATE(
+ file->f_dentry->d_inode->i_sb)->lower_mnt;
+ touch_atime(lower_vfsmount, lower_dentry);
+ }
+ ecryptfs_printk(KERN_DEBUG, "Exit\n");
+ return rc;
+}
+
+struct ecryptfs_getdents_callback {
+ void *dirent;
+ struct dentry *dentry;
+ filldir_t filldir;
+ int err;
+ int filldir_called;
+ int entries_written;
+};
+
+/* Inspired by 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_stat *crypt_stat;
+ struct ecryptfs_getdents_callback *buf =
+ (struct ecryptfs_getdents_callback *)dirent;
+ int rc;
+ char *decoded_name;
+ int decoded_length;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter w/ name = [%.*s]\n", namelen,
+ name);
+ crypt_stat = ECRYPTFS_DENTRY_TO_PRIVATE(buf->dentry)->crypt_stat;
+ buf->filldir_called++;
+ decoded_length = ecryptfs_decode_filename(crypt_stat, name, namelen,
+ &decoded_name);
+ if (decoded_length < 0)
+ return 0;
+ rc = buf->filldir(buf->dirent, decoded_name, decoded_length, offset,
+ ino, d_type);
+ kfree(decoded_name);
+ if (rc >= 0)
+ buf->entries_written++;
+ ecryptfs_printk(KERN_DEBUG, "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(KERN_DEBUG, "Enter; file = [%p]\n", file);
+ lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+ inode = file->f_dentry->d_inode;
+ memset(&buf, 0, sizeof(buf));
+ 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(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * @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(KERN_DEBUG, "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(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(KERN_ERR, "Error reading header page\n");
+ goto out_unmap;
+ }
+ memcpy(&data_size, header_virt, sizeof(data_size));
+ data_size = be64_to_cpu(data_size);
+ i_size_write(inode, (loff_t)data_size);
+ ecryptfs_printk(KERN_DEBUG, "inode w/ addr = [0x%p], i_ino = [0x%.16x] "
+ "size: [0x%.16x]\n", inode, inode->i_ino,
+ i_size_read(inode));
+out_unmap:
+ kunmap(header_page);
+ page_cache_release(header_page);
+out:
+ ecryptfs_printk(KERN_DEBUG, "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_stat *crypt_stat = NULL;
+ struct dentry *ecryptfs_dentry = file->f_dentry;
+ struct dentry *lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(ecryptfs_dentry);
+ struct inode *lower_inode = NULL;
+ struct file *lower_file = NULL;
+ struct vfsmount *lower_mnt;
+ int lower_flags;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ ecryptfs_printk(KERN_DEBUG, "inode w/ addr = [0x%p], i_ino = [0x%.16x] "
+ "size: [0x%.16x]\n", inode, inode->i_ino,
+ i_size_read(inode));
+ ecryptfs_printk(KERN_DEBUG, "file->f_dentry = [%p], "
+ "file->f_dentry->d_name.name = [%s], "
+ "file->f_dentry->d_name.len = [%d]\n",
+ ecryptfs_dentry, ecryptfs_dentry->d_name.name,
+ ecryptfs_dentry->d_name.len);
+ /* ECRYPTFS_DENTRY_TO_PRIVATE(ecryptfs_dentry) Allocated in
+ * ecryptfs_lookup() */
+ /* Released in ecryptfs_release or end of function if failure */
+ ECRYPTFS_FILE_TO_PRIVATE_SM(file) =
+ kmem_cache_alloc(ecryptfs_file_info_cache, SLAB_KERNEL);
+ if (!ECRYPTFS_FILE_TO_PRIVATE_SM(file)) {
+ ecryptfs_printk(KERN_ERR,
+ "Error attempting to allocate memory\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+ lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(ecryptfs_dentry);
+ crypt_stat = &(ECRYPTFS_INODE_TO_PRIVATE(inode)->crypt_stat);
+ if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_POLICY_APPLIED)) {
+ ecryptfs_printk(KERN_DEBUG, "Setting flags for stat...\n");
+ /* Policy code enabled in future release */
+ ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_POLICY_APPLIED);
+ ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED);
+ }
+ /* This mntget & dget is undone via fput when the file is released */
+ dget(lower_dentry);
+ lower_flags = file->f_flags;
+ if ((lower_flags & O_ACCMODE) == O_WRONLY)
+ lower_flags = (lower_flags & O_ACCMODE) | O_RDWR;
+ if (file->f_flags & O_APPEND)
+ lower_flags &= ~O_APPEND;
+ lower_mnt = ECRYPTFS_SUPERBLOCK_TO_PRIVATE(inode->i_sb)->lower_mnt;
+ mntget(lower_mnt);
+ /* Corresponding fput() in ecryptfs_release() */
+ lower_file = dentry_open(lower_dentry, lower_mnt, lower_flags);
+ if (IS_ERR(lower_file)) {
+ rc = PTR_ERR(lower_file);
+ ecryptfs_printk(KERN_ERR, "Error opening lower file\n");
+ goto out_puts;
+ }
+ ECRYPTFS_FILE_TO_LOWER(file) = lower_file;
+ /* Isn't this check the same as the one in lookup? */
+ lower_inode = lower_dentry->d_inode;
+ if (S_ISDIR(ecryptfs_dentry->d_inode->i_mode)) {
+ ecryptfs_printk(KERN_DEBUG, "This is a directory\n");
+ ECRYPTFS_CLEAR_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED);
+ rc = 0;
+ goto out;
+ }
+ if (i_size_read(lower_inode) == 0) {
+ ecryptfs_printk(KERN_EMERG, "Zero-length lower file; "
+ "ecryptfs_create() had a problem?\n");
+ rc = -ENOENT;
+ goto out_puts;
+ } else if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags,
+ ECRYPTFS_POLICY_APPLIED)
+ || !ECRYPTFS_CHECK_FLAG(crypt_stat->flags,
+ ECRYPTFS_KEY_VALID)) {
+ rc = ecryptfs_read_headers(ecryptfs_dentry, lower_file);
+ if (rc) {
+ ecryptfs_printk(KERN_DEBUG,
+ "Valid headers not found\n");
+ ECRYPTFS_CLEAR_FLAG(crypt_stat->flags,
+ ECRYPTFS_ENCRYPTED);
+ /* At this point, we could just move on and
+ * have the encrypted data passed through
+ * as-is to userspace. For release 0.1, we are
+ * going to default to -EIO. */
+ rc = -EIO;
+ goto out_puts;
+ } else
+ read_inode_size_from_header(lower_file, lower_inode,
+ inode);
+ }
+ ecryptfs_printk(KERN_DEBUG, "inode w/ addr = [0x%p], i_ino = [0x%.16x] "
+ "size: [0x%.16x]\n", inode, inode->i_ino,
+ i_size_read(inode));
+ ECRYPTFS_FILE_TO_LOWER(file) = lower_file;
+ goto out;
+out_puts:
+ mntput(lower_mnt);
+ dput(lower_dentry);
+ kmem_cache_free(ecryptfs_file_info_cache,
+ ECRYPTFS_FILE_TO_PRIVATE(file));
+out:
+ ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+static int ecryptfs_flush(struct file *file, fl_owner_t td)
+{
+ int rc = 0;
+ struct file *lower_file = NULL;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; file = [%p]\n", file);
+ lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+ if (lower_file->f_op && lower_file->f_op->flush)
+ rc = lower_file->f_op->flush(lower_file, td);
+ ecryptfs_printk(KERN_DEBUG, "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(KERN_DEBUG,
+ "Enter; ecryptfs_inode->i_count = [%d]\n",
+ ecryptfs_inode->i_count);
+ lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+ kmem_cache_free(ecryptfs_file_info_cache,
+ ECRYPTFS_FILE_TO_PRIVATE(file));
+ lower_inode = ECRYPTFS_INODE_TO_LOWER(ecryptfs_inode);
+ fput(lower_file);
+ ecryptfs_inode->i_blocks = lower_inode->i_blocks;
+ ecryptfs_printk(KERN_DEBUG, "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(KERN_DEBUG, "Enter\n");
+ if (NULL == file) {
+ lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(dentry);
+ if (lower_dentry->d_inode->i_fop
+ && lower_dentry->d_inode->i_fop->fsync) {
+ mutex_lock(&lower_dentry->d_inode->i_mutex);
+ rc = lower_dentry->d_inode->i_fop->fsync(lower_file,
+ lower_dentry,
+ datasync);
+ mutex_unlock(&lower_dentry->d_inode->i_mutex);
+ }
+ } else {
+ if (NULL == ECRYPTFS_FILE_TO_PRIVATE(file)) {
+ rc = -EINVAL;
+ ecryptfs_printk(KERN_ERR, "ECRYPTFS_FILE_TO_PRIVATE"
+ "(file=[%p]) == NULL\n", file);
+ goto out;
+ }
+ lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+ lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(dentry);
+ if (lower_file->f_op && lower_file->f_op->fsync) {
+ mutex_lock(&lower_dentry->d_inode->i_mutex);
+ rc = lower_file->f_op->fsync(lower_file, lower_dentry,
+ datasync);
+ mutex_unlock(&lower_dentry->d_inode->i_mutex);
+ }
+ }
+out:
+ ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+static void locks_delete_block(struct file_lock *waiter)
+{
+ lock_kernel();
+ list_del_init(&waiter->fl_block);
+ list_del_init(&waiter->fl_link);
+ waiter->fl_next = NULL;
+ unlock_kernel();
+}
+
+static int ecryptfs_posix_lock(struct file *file, struct file_lock *fl, int cmd)
+{
+ int rc;
+
+lock_file:
+ rc = posix_lock_file(file, fl);
+ if ((rc != -EAGAIN) || (cmd == F_SETLK))
+ goto out;
+ rc = wait_event_interruptible(fl->fl_wait, !fl->fl_next);
+ if (!rc)
+ goto lock_file;
+ locks_delete_block(fl);
+out:
+ return rc;
+}
+
+static int ecryptfs_setlk(struct file *file, int cmd, struct file_lock *fl)
+{
+ int rc = -EINVAL;
+ struct inode *inode, *lower_inode;
+ struct file *lower_file = NULL;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+ inode = file->f_dentry->d_inode;
+ lower_inode = lower_file->f_dentry->d_inode;
+ /* Don't allow mandatory locks on files that may be memory mapped
+ * and shared. */
+ if (IS_MANDLOCK(lower_inode) &&
+ (lower_inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID &&
+ mapping_writably_mapped(lower_file->f_mapping)) {
+ rc = -EAGAIN;
+ goto out;
+ }
+ if (cmd == F_SETLKW)
+ fl->fl_flags |= FL_SLEEP;
+ rc = -EBADF;
+ switch (fl->fl_type) {
+ case F_RDLCK:
+ if (!(lower_file->f_mode & FMODE_READ))
+ goto out;
+ break;
+ case F_WRLCK:
+ if (!(lower_file->f_mode & FMODE_WRITE))
+ goto out;
+ break;
+ case F_UNLCK:
+ break;
+ default:
+ rc = -EINVAL;
+ goto out;
+ }
+ fl->fl_file = lower_file;
+ rc = security_file_lock(lower_file, fl->fl_type);
+ if (rc)
+ goto out;
+ if (lower_file->f_op && lower_file->f_op->lock != NULL) {
+ rc = lower_file->f_op->lock(lower_file, cmd, fl);
+ if (rc)
+ goto out;
+ goto upper_lock;
+ }
+ rc = ecryptfs_posix_lock(lower_file, fl, cmd);
+ if (rc)
+ goto out;
+upper_lock:
+ fl->fl_file = file;
+ rc = ecryptfs_posix_lock(file, fl, cmd);
+ if (rc) {
+ fl->fl_type = F_UNLCK;
+ fl->fl_file = lower_file;
+ ecryptfs_posix_lock(lower_file, fl, cmd);
+ }
+out:
+ ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+static int ecryptfs_getlk(struct file *file, struct file_lock *fl)
+{
+ struct file_lock cfl;
+ struct file_lock *tempfl = NULL;
+ int rc = 0;
+
+ 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, &cfl) ? &cfl : NULL);
+ 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(KERN_DEBUG, "Enter\n");
+ if (NULL != ECRYPTFS_FILE_TO_PRIVATE(file))
+ lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+ else {
+ rc = -EINVAL;
+ goto out;
+ }
+ if (lower_file->f_op && lower_file->f_op->fasync)
+ rc = lower_file->f_op->fasync(fd, lower_file, flag);
+out:
+ ecryptfs_printk(KERN_DEBUG, "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(KERN_DEBUG, "Enter\n");
+ if (ECRYPTFS_FILE_TO_PRIVATE(file) != NULL)
+ lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+ ASSERT(lower_file != NULL);
+ rc = -EINVAL;
+ if (!fl)
+ goto out;
+ fl->fl_file = lower_file;
+ switch (cmd) {
+ case F_GETLK:
+ case F_GETLK64:
+ rc = ecryptfs_getlk(lower_file, fl);
+ break;
+ case F_SETLK:
+ case F_SETLKW:
+ case F_SETLK64:
+ case F_SETLKW64:
+ fl->fl_file = file;
+ rc = ecryptfs_setlk(file, cmd, fl);
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ fl->fl_file = file;
+out:
+ ecryptfs_printk(KERN_DEBUG, "Exit\n");
+ return rc;
+}
+
+static ssize_t ecryptfs_sendfile(struct file *file, loff_t * ppos,
+ size_t count, read_actor_t actor, void *target)
+{
+ struct file *lower_file = NULL;
+ int rc = -EINVAL;
+
+ if (ECRYPTFS_FILE_TO_PRIVATE(file) != NULL)
+ lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+ ASSERT(lower_file != NULL);
+ 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 = {
+ .readdir = ecryptfs_readdir,
+ .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,
+ .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,
+};
+
+static int
+ecryptfs_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int rc = 0;
+ struct file *lower_file = NULL;
+ if (ECRYPTFS_FILE_TO_PRIVATE(file) != NULL)
+ lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+ if (lower_file && lower_file->f_op && lower_file->f_op->ioctl)
+ rc = lower_file->f_op->ioctl(ECRYPTFS_INODE_TO_LOWER(inode),
+ lower_file, cmd, arg);
+ else
+ rc = -ENOTTY;
+ return rc;
+}

2006-05-04 03:40:52

by Phillip Hellewell

[permalink] [raw]
Subject: [PATCH 9/13: eCryptfs] Inode operations

This is the 9th patch in a series of 13 constituting the kernel
components of the eCryptfs cryptographic filesystem.

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

Signed-off-by: Phillip Hellewell <[email protected]>
Signed-off-by: Michael Halcrow <[email protected]>

---

inode.c | 1121 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 1121 insertions(+)

Index: linux-2.6.17-rc3-mm1-ecryptfs/fs/ecryptfs/inode.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.17-rc3-mm1-ecryptfs/fs/ecryptfs/inode.c 2006-05-02 19:36:02.000000000 -0600
@@ -0,0 +1,1121 @@
+/**
+ * eCryptfs: Linux filesystem encryption layer
+ *
+ * Copyright (C) 1997-2004 Erez Zadok
+ * Copyright (C) 2001-2004 Stony Brook University
+ * Copyright (C) 2004-2006 International Business Machines Corp.
+ * Author(s): Michael A. Halcrow <[email protected]>
+ * Michael C. Thompsion <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <linux/file.h>
+#include <linux/vmalloc.h>
+#include <linux/pagemap.h>
+#include <linux/dcache.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+#include <linux/crypto.h>
+#include "ecryptfs_kernel.h"
+
+static inline struct dentry *lock_parent(struct dentry *dentry)
+{
+ struct dentry *dir;
+
+ dir = dget(dentry->d_parent);
+ mutex_lock(&(dir->d_inode->i_mutex));
+ return dir;
+}
+
+static inline void unlock_parent(struct dentry *dentry)
+{
+ mutex_unlock(&(dentry->d_parent->d_inode->i_mutex));
+ dput(dentry->d_parent);
+}
+
+static inline void unlock_dir(struct dentry *dir)
+{
+ mutex_unlock(&dir->d_inode->i_mutex);
+ dput(dir);
+}
+
+void ecryptfs_copy_inode_size(struct inode *dst, const struct inode *src)
+{
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ ecryptfs_printk(KERN_DEBUG, "src->i_size = [0x%.16x]\n",
+ i_size_read((struct inode *)src));
+ ecryptfs_printk(KERN_DEBUG, "src->i_blocks = [0x%.16x]\n",
+ src->i_blocks);
+ i_size_write(dst, i_size_read((struct inode *)src));
+ dst->i_blocks = src->i_blocks;
+ ecryptfs_printk(KERN_DEBUG, "Exit\n");
+}
+
+void ecryptfs_copy_attr_atime(struct inode *dest, const struct inode *src)
+{
+ ASSERT(dest != NULL);
+ ASSERT(src != NULL);
+ dest->i_atime = src->i_atime;
+}
+
+void ecryptfs_copy_attr_times(struct inode *dest, const struct inode *src)
+{
+ ASSERT(dest != NULL);
+ ASSERT(src != NULL);
+ dest->i_atime = src->i_atime;
+ dest->i_mtime = src->i_mtime;
+ dest->i_ctime = src->i_ctime;
+}
+
+static void ecryptfs_copy_attr_timesizes(struct inode *dest,
+ const struct inode *src)
+{
+ ASSERT(dest != NULL);
+ ASSERT(src != NULL);
+ dest->i_atime = src->i_atime;
+ dest->i_mtime = src->i_mtime;
+ dest->i_ctime = src->i_ctime;
+ ecryptfs_copy_inode_size(dest, src);
+}
+
+void ecryptfs_copy_attr_all(struct inode *dest, const struct inode *src)
+{
+ ecryptfs_printk(KERN_DEBUG, "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(KERN_DEBUG, "src->i_blksize = [0x%.16x]\n",
+ src->i_blksize);
+ dest->i_blkbits = src->i_blkbits;
+ dest->i_flags = src->i_flags;
+ ecryptfs_printk(KERN_DEBUG, "Exit\n");
+}
+
+/**
+ * Creates the 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 Zero 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(KERN_DEBUG, "Enter\n");
+ saved_dentry = nd->dentry;
+ saved_vfsmount = nd->mnt;
+ nd->dentry = lower_dentry;
+ nd->mnt = ECRYPTFS_SUPERBLOCK_TO_PRIVATE(
+ ecryptfs_dentry->d_sb)->lower_mnt;
+ rc = vfs_create(lower_dir_inode, lower_dentry, mode, nd);
+ nd->dentry = saved_dentry;
+ nd->mnt = saved_vfsmount;
+ ecryptfs_printk(KERN_DEBUG, "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
+ * stat 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 Zero 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(KERN_DEBUG, "Enter\n");
+ lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(ecryptfs_dentry);
+ if (IS_ERR(lower_dentry)) {
+ ecryptfs_printk(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(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(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(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(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * This is the code which will grow the file to its correct size.
+ */
+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;
+ struct ecryptfs_file_info tmp_file_info;
+
+ memset(&fake_file, 0, sizeof(fake_file));
+ fake_file.f_dentry = ecryptfs_dentry;
+ memset(&tmp_file_info, 0, sizeof(tmp_file_info));
+ ECRYPTFS_FILE_TO_PRIVATE_SM(&fake_file) = &tmp_file_info;
+ ECRYPTFS_FILE_TO_LOWER(&fake_file) = lower_file;
+ rc = ecryptfs_fill_zeros(&fake_file, 1);
+ if (rc) {
+ ECRYPTFS_SET_FLAG(
+ ECRYPTFS_INODE_TO_PRIVATE(inode)->crypt_stat.flags,
+ ECRYPTFS_SECURITY_WARNING);
+ ecryptfs_printk(KERN_WARNING, "Error attempting to fill zeros "
+ "in file; rc = [%d]\n", rc);
+ goto out;
+ }
+ i_size_write(inode, 0);
+ ecryptfs_write_inode_size_to_header(lower_file, lower_inode, inode);
+ ECRYPTFS_SET_FLAG(ECRYPTFS_INODE_TO_PRIVATE(inode)->crypt_stat.flags,
+ ECRYPTFS_NEW_FILE);
+out:
+ return rc;
+}
+
+/**
+ * Cause the file to be changed from a basic empty file to an ecryptfs
+ * file with a header and first data page.
+ *
+ * @return Zero on success
+ */
+static int ecryptfs_initialize_file(struct dentry *ecryptfs_dentry)
+{
+ int rc = 0;
+ int lower_flags;
+ struct ecryptfs_crypt_stat *crypt_stat;
+ struct dentry *lower_dentry;
+ struct dentry *tlower_dentry = NULL;
+ struct file *lower_file;
+ struct inode *inode, *lower_inode;
+ struct vfsmount *lower_mnt;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; dentry->d_name.name = [%s]\n",
+ ecryptfs_dentry->d_name.name);
+ lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(ecryptfs_dentry);
+ if (IS_ERR(lower_dentry)) {
+ ecryptfs_printk(KERN_ERR, "ecryptfs dentry doesn't know"
+ "about its lower counterpart\n");
+ rc = PTR_ERR(lower_dentry);
+ goto out;
+ }
+ ecryptfs_printk(KERN_DEBUG, "lower_dentry->d_name.name = [%s]\n",
+ lower_dentry->d_name.name);
+ inode = ecryptfs_dentry->d_inode;
+ crypt_stat = &(ECRYPTFS_INODE_TO_PRIVATE(inode)->crypt_stat);
+ tlower_dentry = dget(lower_dentry);
+ if (!tlower_dentry) {
+ rc = -ENOMEM;
+ ecryptfs_printk(KERN_ERR, "Error dget'ing lower_dentry\n");
+ goto out;
+ }
+ lower_flags = ((O_CREAT | O_WRONLY | O_TRUNC) & O_ACCMODE) | O_RDWR;
+#if BITS_PER_LONG != 32
+ lower_flags |= O_LARGEFILE;
+#endif
+ lower_mnt = ECRYPTFS_SUPERBLOCK_TO_PRIVATE(inode->i_sb)->lower_mnt;
+ mntget(lower_mnt);
+ /* Corresponding fput() at end of this function */
+ lower_file = dentry_open(tlower_dentry, lower_mnt, lower_flags);
+ if (IS_ERR(lower_file)) {
+ rc = PTR_ERR(lower_file);
+ ecryptfs_printk(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(KERN_DEBUG, "This is a directory\n");
+ ECRYPTFS_CLEAR_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED);
+ goto out_fput;
+ }
+ ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_NEW_FILE);
+ ecryptfs_printk(KERN_DEBUG, "Initializing crypto context\n");
+ rc = ecryptfs_new_file_context(ecryptfs_dentry);
+ if (rc) {
+ ecryptfs_printk(KERN_DEBUG, "Error creating new file "
+ "context\n");
+ goto out_fput;
+ }
+ rc = ecryptfs_write_headers(ecryptfs_dentry, lower_file);
+ if (rc) {
+ ecryptfs_printk(KERN_DEBUG, "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(KERN_DEBUG, "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 Zero 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(KERN_DEBUG, "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(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(KERN_DEBUG, "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 rc = 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_stat *crypt_stat = NULL;
+ char *page_virt = NULL;
+ struct inode *lower_inode;
+ u64 file_size;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; dir = [%p], dentry->d_name.nam"
+ "e = [%s], nd = [%p]\n", dir, dentry->d_name.name, nd);
+ lower_dir_dentry = ECRYPTFS_DENTRY_TO_LOWER(dentry->d_parent);
+ dentry->d_op = &ecryptfs_dops;
+ 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(crypt_stat,
+ dentry->d_name.name,
+ dentry->d_name.len,
+ &encoded_name);
+ if (encoded_namelen < 0) {
+ rc = encoded_namelen;
+ goto out_drop;
+ }
+ ecryptfs_printk(KERN_DEBUG, "encoded_name = [%s]; encoded_namelen "
+ "= [%d]\n", encoded_name, encoded_namelen);
+ lower_dentry = lookup_one_len(encoded_name, lower_dir_dentry,
+ encoded_namelen - 1);
+ kfree(encoded_name);
+ if (IS_ERR(lower_dentry)) {
+ ecryptfs_printk(KERN_ERR, "ERR from lower_dentry\n");
+ rc = PTR_ERR(lower_dentry);
+ goto out_drop;
+ }
+ ecryptfs_printk(KERN_DEBUG, "lower_dentry = [%p]; lower_dentry->"
+ "d_name.name = [%s]\n", lower_dentry,
+ lower_dentry->d_name.name);
+ lower_inode = lower_dentry->d_inode;
+ ecryptfs_copy_attr_atime(dir, lower_dir_dentry->d_inode);
+ ASSERT(atomic_read(&lower_dentry->d_count));
+ ECRYPTFS_DENTRY_TO_PRIVATE_SM(dentry) =
+ kmem_cache_alloc(ecryptfs_dentry_info_cache, SLAB_KERNEL);
+ if (!ECRYPTFS_DENTRY_TO_PRIVATE_SM(dentry)) {
+ rc = -ENOMEM;
+ ecryptfs_printk(KERN_ERR, "Out of memory whilst attempting "
+ "to allocate ecryptfs_dentry_info struct\n");
+ goto out_dput;
+ }
+ ECRYPTFS_DENTRY_TO_LOWER(dentry) = lower_dentry;
+ if (!lower_dentry->d_inode) {
+ /* We want to add because we couldn't find in lower */
+ d_add(dentry, NULL);
+ goto out;
+ }
+ rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 1);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error interposing\n");
+ goto out_dput;
+ }
+ if (S_ISDIR(lower_inode->i_mode)) {
+ ecryptfs_printk(KERN_DEBUG, "Is a directory; returning\n");
+ goto out;
+ }
+ if (S_ISLNK(lower_inode->i_mode)) {
+ ecryptfs_printk(KERN_DEBUG, "Is a symlink; returning\n");
+ goto out;
+ }
+ if (!nd) {
+ ecryptfs_printk(KERN_DEBUG, "We have a NULL nd, just leave"
+ "as we *think* we are about to unlink\n");
+ goto out;
+ }
+ tlower_dentry = dget(lower_dentry);
+ if (!tlower_dentry || IS_ERR(tlower_dentry)) {
+ rc = -ENOMEM;
+ ecryptfs_printk(KERN_ERR, "Cannot dget lower_dentry\n");
+ goto out_dput;
+ }
+ /* Released in this function */
+ page_virt =
+ (char *)kmem_cache_alloc(ecryptfs_header_cache_2,
+ SLAB_USER);
+ if (!page_virt) {
+ rc = -ENOMEM;
+ ecryptfs_printk(KERN_ERR,
+ "Cannot ecryptfs_kmalloc a page\n");
+ goto out_dput;
+ }
+ memset(page_virt, 0, PAGE_CACHE_SIZE);
+ rc = ecryptfs_read_header_region(page_virt, tlower_dentry, nd);
+ crypt_stat =
+ &(ECRYPTFS_INODE_TO_PRIVATE(dentry->d_inode)->crypt_stat);
+ if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_POLICY_APPLIED))
+ ecryptfs_set_default_sizes(crypt_stat);
+ if (rc) {
+ rc = 0;
+ ecryptfs_printk(KERN_WARNING, "Error reading header region;"
+ " assuming unencrypted\n");
+ } else {
+ if (!contains_ecryptfs_marker(page_virt
+ + ECRYPTFS_FILE_SIZE_BYTES)) {
+ ecryptfs_printk(KERN_WARNING, "Underlying file "
+ "lacks recognizable eCryptfs marker\n");
+ }
+ memcpy(&file_size, page_virt, sizeof(file_size));
+ file_size = be64_to_cpu(file_size);
+ i_size_write(dentry->d_inode, (loff_t)file_size);
+ }
+ kmem_cache_free(ecryptfs_header_cache_2, page_virt);
+ goto out;
+
+out_dput:
+ dput(lower_dentry);
+ if (tlower_dentry)
+ dput(tlower_dentry);
+out_drop:
+ d_drop(dentry);
+out:
+ ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
+ return ERR_PTR(rc);
+}
+
+static int ecryptfs_link(struct dentry *old_dentry, struct inode *dir,
+ struct dentry *new_dentry)
+{
+ int rc;
+ struct dentry *lower_old_dentry;
+ struct dentry *lower_new_dentry;
+ struct dentry *lower_dir_dentry;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ lower_old_dentry = ECRYPTFS_DENTRY_TO_LOWER(old_dentry);
+ lower_new_dentry = ECRYPTFS_DENTRY_TO_LOWER(new_dentry);
+ dget(lower_old_dentry);
+ dget(lower_new_dentry);
+ lower_dir_dentry = lock_parent(lower_new_dentry);
+ rc = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode,
+ lower_new_dentry);
+ if (rc || !lower_new_dentry->d_inode)
+ goto out_lock;
+ rc = ecryptfs_interpose(lower_new_dentry, new_dentry, dir->i_sb, 0);
+ if (rc)
+ goto out_lock;
+ ecryptfs_copy_attr_timesizes(dir, lower_new_dentry->d_inode);
+ old_dentry->d_inode->i_nlink =
+ ECRYPTFS_INODE_TO_LOWER(old_dentry->d_inode)->i_nlink;
+out_lock:
+ unlock_dir(lower_dir_dentry);
+ dput(lower_new_dentry);
+ dput(lower_old_dentry);
+ if (!new_dentry->d_inode)
+ d_drop(new_dentry);
+ ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+static int ecryptfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+ int rc = 0;
+ struct dentry *lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(dentry);
+ struct inode *lower_dir_inode = ECRYPTFS_INODE_TO_LOWER(dir);
+ ecryptfs_printk(KERN_DEBUG, "Enter; dentry->d_name = [%s]\n",
+ dentry->d_name.name);
+ lock_parent(lower_dentry);
+ rc = vfs_unlink(lower_dir_inode, lower_dentry);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error in vfs_unlink\n");
+ goto out_unlock;
+ }
+ ecryptfs_copy_attr_times(dir, lower_dir_inode);
+ dentry->d_inode->i_nlink =
+ ECRYPTFS_INODE_TO_LOWER(dentry->d_inode)->i_nlink;
+ dentry->d_inode->i_ctime = dir->i_ctime;
+out_unlock:
+ unlock_parent(lower_dentry);
+ ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]",rc);
+ return rc;
+}
+
+static int ecryptfs_symlink(struct inode *dir, struct dentry *dentry,
+ const char *symname)
+{
+ int rc;
+ struct dentry *lower_dentry;
+ struct dentry *lower_dir_dentry;
+ umode_t mode;
+ char *encoded_symname;
+ unsigned int encoded_symlen;
+ struct ecryptfs_crypt_stat *crypt_stat = NULL;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(dentry);
+ dget(lower_dentry);
+ lower_dir_dentry = lock_parent(lower_dentry);
+ mode = S_IALLUGO;
+ encoded_symlen = ecryptfs_encode_filename(crypt_stat, symname,
+ strlen(symname),
+ &encoded_symname);
+ if (encoded_symlen < 0) {
+ rc = encoded_symlen;
+ goto out_lock;
+ }
+ rc = vfs_symlink(lower_dir_dentry->d_inode, lower_dentry,
+ encoded_symname, mode);
+ kfree(encoded_symname);
+ if (rc || !lower_dentry->d_inode)
+ goto out_lock;
+ rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0);
+ if (rc)
+ goto out_lock;
+ ecryptfs_copy_attr_timesizes(dir, lower_dir_dentry->d_inode);
+out_lock:
+ unlock_dir(lower_dir_dentry);
+ dput(lower_dentry);
+ if (!dentry->d_inode)
+ d_drop(dentry);
+ return rc;
+}
+
+static int ecryptfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+ int rc;
+ struct dentry *lower_dentry;
+ struct dentry *lower_dir_dentry;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(dentry);
+ lower_dir_dentry = lock_parent(lower_dentry);
+ rc = vfs_mkdir(lower_dir_dentry->d_inode, lower_dentry, mode);
+ if (rc || !lower_dentry->d_inode)
+ goto out;
+ rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0);
+ if (rc)
+ goto out;
+ ecryptfs_copy_attr_timesizes(dir, lower_dir_dentry->d_inode);
+ dir->i_nlink = lower_dir_dentry->d_inode->i_nlink;
+out:
+ unlock_dir(lower_dir_dentry);
+ if (!dentry->d_inode)
+ d_drop(dentry);
+ ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ int rc = 0;
+ struct dentry *tdentry = NULL;
+ struct dentry *lower_dentry;
+ struct dentry *tlower_dentry = NULL;
+ struct dentry *lower_dir_dentry;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(dentry);
+ if (!(tdentry = dget(dentry))) {
+ rc = -EINVAL;
+ ecryptfs_printk(KERN_ERR, "Error dget'ing dentry [%p]\n",
+ dentry);
+ goto out;
+ }
+ lower_dir_dentry = lock_parent(lower_dentry);
+ if (!(tlower_dentry = dget(lower_dentry))) {
+ rc = -EINVAL;
+ ecryptfs_printk(KERN_ERR, "Error dget'ing lower_dentry "
+ "[%p]\n", lower_dentry);
+ goto out;
+ }
+ rc = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry);
+ if (!rc) {
+ d_delete(tlower_dentry);
+ tlower_dentry = NULL;
+ }
+ ecryptfs_copy_attr_times(dir, lower_dir_dentry->d_inode);
+ dir->i_nlink = lower_dir_dentry->d_inode->i_nlink;
+ unlock_dir(lower_dir_dentry);
+ if (!rc)
+ d_drop(dentry);
+out:
+ if (tdentry)
+ dput(tdentry);
+ if (tlower_dentry)
+ dput(tlower_dentry);
+ ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+static int
+ecryptfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
+{
+ int rc;
+ struct dentry *lower_dentry;
+ struct dentry *lower_dir_dentry;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(dentry);
+ lower_dir_dentry = lock_parent(lower_dentry);
+ rc = vfs_mknod(lower_dir_dentry->d_inode, lower_dentry, mode, dev);
+ if (rc || !lower_dentry->d_inode)
+ goto out;
+ rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0);
+ if (rc)
+ 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 rc;
+}
+
+static int
+ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ int rc;
+ struct dentry *lower_old_dentry;
+ struct dentry *lower_new_dentry;
+ struct dentry *lower_old_dir_dentry;
+ struct dentry *lower_new_dir_dentry;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ lower_old_dentry = ECRYPTFS_DENTRY_TO_LOWER(old_dentry);
+ lower_new_dentry = ECRYPTFS_DENTRY_TO_LOWER(new_dentry);
+ dget(lower_old_dentry);
+ dget(lower_new_dentry);
+ lower_old_dir_dentry = dget_parent(lower_old_dentry);
+ lower_new_dir_dentry = dget_parent(lower_new_dentry);
+ lock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
+ rc = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry,
+ lower_new_dir_dentry->d_inode, lower_new_dentry);
+ if (rc)
+ goto out_lock;
+ ecryptfs_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode);
+ if (new_dir != old_dir)
+ ecryptfs_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode);
+out_lock:
+ unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
+ dput(lower_new_dentry);
+ dput(lower_old_dentry);
+ return rc;
+}
+
+static int
+ecryptfs_readlink(struct dentry *dentry, char __user * buf, int bufsiz)
+{
+ int rc;
+ struct dentry *lower_dentry;
+ char *decoded_name;
+ char *lower_buf;
+ mm_segment_t old_fs;
+ struct ecryptfs_crypt_stat *crypt_stat;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(dentry);
+ if (!lower_dentry->d_inode->i_op ||
+ !lower_dentry->d_inode->i_op->readlink) {
+ rc = -EINVAL;
+ goto out;
+ }
+ /* Released in this function */
+ lower_buf = kmalloc(bufsiz, GFP_KERNEL);
+ if (lower_buf == NULL) {
+ ecryptfs_printk(KERN_ERR, "Out of memory\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+ old_fs = get_fs();
+ set_fs(get_ds());
+ ecryptfs_printk(KERN_DEBUG, "Calling readlink w/ "
+ "lower_dentry->d_name.name = [%s]\n",
+ lower_dentry->d_name.name);
+ rc = lower_dentry->d_inode->i_op->readlink(lower_dentry,
+ (char __user *)lower_buf,
+ bufsiz);
+ set_fs(old_fs);
+ if (rc >= 0) {
+ crypt_stat = NULL;
+ rc = ecryptfs_decode_filename(crypt_stat, lower_buf, rc,
+ &decoded_name);
+ if (rc == -ENOMEM)
+ goto out_free_lower_buf;
+ if (rc > 0) {
+ ecryptfs_printk(KERN_DEBUG, "Copying [%d] bytes "
+ "to userspace: [%*s]\n", rc,
+ decoded_name);
+ if (copy_to_user(buf, decoded_name, rc))
+ rc = -EFAULT;
+ }
+ kfree(decoded_name);
+ ecryptfs_copy_attr_atime(dentry->d_inode,
+ lower_dentry->d_inode);
+ }
+out_free_lower_buf:
+ kfree(lower_buf);
+out:
+ ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+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(KERN_DEBUG, "Enter; dentry->d_name.name = [%s]\n",
+ dentry->d_name.name);
+ /* Released in ecryptfs_put_link(); only release here on error */
+ buf = kmalloc(len, GFP_KERNEL);
+ if (!buf) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ old_fs = get_fs();
+ set_fs(get_ds());
+ ecryptfs_printk(KERN_DEBUG, "Calling readlink w/ "
+ "dentry->d_name.name = [%s]\n", dentry->d_name.name);
+ rc = dentry->d_inode->i_op->readlink(dentry, (char __user *)buf, len);
+ buf[rc] = '\0';
+ set_fs(old_fs);
+ if (rc < 0)
+ goto out_free;
+ rc = 0;
+ nd_set_link(nd, buf);
+ goto out;
+out_free:
+ kfree(buf);
+out:
+ return ERR_PTR(rc);
+}
+
+static inline void
+ecryptfs_put_link(struct dentry *dentry, struct nameidata *nd, void *ptr)
+{
+ /* Free the char* */
+ kfree(nd_get_link(nd));
+}
+
+/**
+ * Calculate the requried size of the lower file based on the
+ * specified size of the upper file. This calculation is based on the
+ * number of headers in the underlying file and the extent size.
+ *
+ * @param crypt_stat Crypt_stat 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_stat *crypt_stat,
+ loff_t upper_size)
+{
+ loff_t lower_size;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; upper_size = [0x%.16x]\n",
+ upper_size);
+ lower_size = ( crypt_stat->header_extent_size
+ * crypt_stat->num_header_extents_at_front );
+ if (upper_size != 0) {
+ loff_t num_extents;
+
+ num_extents = upper_size >> crypt_stat->extent_shift;
+ if (upper_size & ~crypt_stat->extent_mask)
+ num_extents++;
+ lower_size += (num_extents * crypt_stat->extent_size);
+ }
+ ecryptfs_printk(KERN_DEBUG, "Exit; lower_size = [0x%.16x]\n");
+ 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 according to the page index interpolations.
+ *
+ * @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_stat *crypt_stat;
+ loff_t i_size = i_size_read(inode);
+ loff_t lower_size_before_truncate;
+ loff_t lower_size_after_truncate;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; dentry = [%p], new_length = "
+ "[0x%.16x], i_size_read(inode) = [0x%.16x]\n",
+ dentry, new_length, i_size);
+ if (unlikely((new_length == i_size)))
+ goto out;
+ crypt_stat = &ECRYPTFS_INODE_TO_PRIVATE(dentry->d_inode)->crypt_stat;
+ if (unlikely(!crypt_stat)) {
+ ecryptfs_printk(KERN_ERR, "NULL crypt_stat 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(fake_ecryptfs_file));
+ fake_ecryptfs_file.f_dentry = dentry;
+ /* Released at out_free: label */
+ ECRYPTFS_FILE_TO_PRIVATE_SM(&fake_ecryptfs_file) =
+ kmem_cache_alloc(ecryptfs_file_info_cache, SLAB_KERNEL);
+ if (unlikely(!ECRYPTFS_FILE_TO_PRIVATE(&fake_ecryptfs_file))) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(dentry);
+ /* This dget & mntget is released through fput at out_fput: */
+ dget(lower_dentry);
+ mntget(ECRYPTFS_SUPERBLOCK_TO_PRIVATE(inode->i_sb)->lower_mnt);
+ lower_file = dentry_open(
+ lower_dentry,
+ ECRYPTFS_SUPERBLOCK_TO_PRIVATE(inode->i_sb)->lower_mnt, O_RDWR);
+ if (unlikely(IS_ERR(lower_file))) {
+ rc = PTR_ERR(lower_file);
+ goto out_free;
+ }
+ ECRYPTFS_FILE_TO_LOWER(&fake_ecryptfs_file) = lower_file;
+ /* Switch on growing or shrinking file */
+ if (new_length > i_size) {
+ rc = ecryptfs_fill_zeros(&fake_ecryptfs_file, new_length);
+ if (rc) {
+ ecryptfs_printk(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(KERN_ERR,
+ "Problem with ecryptfs_write"
+ "_inode_size\n");
+ goto out_fput;
+ }
+ } else { /* new_length < i_size_read(inode) */
+ vmtruncate(inode, new_length);
+ ecryptfs_write_inode_size_to_header(lower_file,
+ lower_dentry->d_inode,
+ inode);
+ /* We are reducing the size of the ecryptfs file, and need to
+ * know if we need to reduce the size of the lower file. */
+ lower_size_before_truncate =
+ upper_size_to_lower_size(crypt_stat, i_size);
+ lower_size_after_truncate =
+ upper_size_to_lower_size(crypt_stat, 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 = lower_dentry->d_inode->i_ctime
+ = CURRENT_TIME;
+ mark_inode_dirty_sync(inode);
+out_fput:
+ fput(lower_file);
+out_free:
+ if (ECRYPTFS_FILE_TO_PRIVATE(&fake_ecryptfs_file))
+ kmem_cache_free(ecryptfs_file_info_cache,
+ ECRYPTFS_FILE_TO_PRIVATE(&fake_ecryptfs_file));
+out:
+ ecryptfs_printk(KERN_DEBUG, "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(KERN_DEBUG, "Enter; inode = [%p], mask=[%d], nd ="
+ "[%p]\n", inode, mask, nd);
+ lower_inode = ECRYPTFS_INODE_TO_LOWER(inode);
+ if (nd)
+ ecryptfs_printk(KERN_DEBUG, "nd->dentry = [%p]\n",
+ nd->dentry);
+ rc = permission(lower_inode, mask, nd);
+ ecryptfs_printk(KERN_DEBUG, "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 rc = 0;
+ struct dentry *lower_dentry;
+ struct inode *inode;
+ struct inode *lower_inode;
+ struct ecryptfs_crypt_stat *crypt_stat;
+ ecryptfs_printk(KERN_DEBUG, "Enter; dentry->d_name.name = [%s]\n",
+ dentry->d_name.name);
+ crypt_stat = &ECRYPTFS_INODE_TO_PRIVATE(dentry->d_inode)->crypt_stat;
+ lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(dentry);
+ inode = dentry->d_inode;
+ lower_inode = ECRYPTFS_INODE_TO_LOWER(inode);
+ if (ia->ia_valid & ATTR_SIZE) {
+ ecryptfs_printk(KERN_DEBUG,
+ "ia->ia_valid = [0x%x] ATTR_SIZE" " = [0x%x]\n",
+ ia->ia_valid, ATTR_SIZE);
+ rc = ecryptfs_truncate(dentry, ia->ia_size);
+ /* ecryptfs_truncate handles resizing of the lower file */
+ ia->ia_valid &= ~ATTR_SIZE;
+ ecryptfs_printk(KERN_DEBUG, "ia->ia_valid = [%x]\n",
+ ia->ia_valid);
+ if (rc < 0)
+ goto out;
+ }
+ rc = notify_change(lower_dentry, ia);
+out:
+ ecryptfs_copy_attr_all(inode, lower_inode);
+ ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+static int
+ecryptfs_setxattr(struct dentry *dentry, const char *name, const void *value,
+ size_t size, int flags)
+{
+ int rc = 0;
+ struct dentry *lower_dentry;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(dentry);
+ if (!lower_dentry->d_inode->i_op->setxattr) {
+ rc = -ENOSYS;
+ goto out;
+ }
+ mutex_lock(&lower_dentry->d_inode->i_mutex);
+ rc = lower_dentry->d_inode->i_op->setxattr(lower_dentry, name, value,
+ size, flags);
+ mutex_unlock(&lower_dentry->d_inode->i_mutex);
+out:
+ ecryptfs_printk(KERN_DEBUG, "Exit\n");
+ return rc;
+}
+
+static ssize_t
+ecryptfs_getxattr(struct dentry *dentry, const char *name, void *value,
+ size_t size)
+{
+ int rc = 0;
+ struct dentry *lower_dentry;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(dentry);
+ if (!lower_dentry->d_inode->i_op->getxattr) {
+ rc = -ENOSYS;
+ goto out;
+ }
+ mutex_lock(&lower_dentry->d_inode->i_mutex);
+ rc = lower_dentry->d_inode->i_op->getxattr(lower_dentry, name, value,
+ size);
+ mutex_unlock(&lower_dentry->d_inode->i_mutex);
+out:
+ ecryptfs_printk(KERN_DEBUG, "Exit\n");
+ return rc;
+}
+
+static ssize_t
+ecryptfs_listxattr(struct dentry *dentry, char *list, size_t size)
+{
+ int rc = 0;
+ struct dentry *lower_dentry;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(dentry);
+ if (!lower_dentry->d_inode->i_op->listxattr) {
+ rc = -ENOSYS;
+ goto out;
+ }
+ mutex_lock(&lower_dentry->d_inode->i_mutex);
+ rc = lower_dentry->d_inode->i_op->listxattr(lower_dentry, list, size);
+ mutex_unlock(&lower_dentry->d_inode->i_mutex);
+out:
+ ecryptfs_printk(KERN_DEBUG, "Exit\n");
+ return rc;
+}
+
+static int ecryptfs_removexattr(struct dentry *dentry, const char *name)
+{
+ int rc = 0;
+ struct dentry *lower_dentry;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(dentry);
+ if (!lower_dentry->d_inode->i_op->removexattr) {
+ rc = -ENOSYS;
+ goto out;
+ }
+ mutex_lock(&lower_dentry->d_inode->i_mutex);
+ rc = lower_dentry->d_inode->i_op->removexattr(lower_dentry, name);
+ mutex_unlock(&lower_dentry->d_inode->i_mutex);
+out:
+ ecryptfs_printk(KERN_DEBUG, "Exit\n");
+ return rc;
+}
+
+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
+};

2006-05-04 03:41:29

by Phillip Hellewell

[permalink] [raw]
Subject: [PATCH 10/13: eCryptfs] Mmap operations

This is the 10th patch in a series of 13 constituting the kernel
components of the eCryptfs cryptographic filesystem.

eCryptfs mmap operations. Page read/write code works closely with
encryption/decryption routines in crypto.c.

Signed-off-by: Phillip Hellewell <[email protected]>
Signed-off-by: Michael Halcrow <[email protected]>

---

mmap.c | 869 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 869 insertions(+)

Index: linux-2.6.17-rc3-mm1-ecryptfs/fs/ecryptfs/mmap.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.17-rc3-mm1-ecryptfs/fs/ecryptfs/mmap.c 2006-05-02 19:36:03.000000000 -0600
@@ -0,0 +1,869 @@
+/**
+ * eCryptfs: Linux filesystem encryption layer
+ * This is where eCryptfs coordinates the symmetric encryption and
+ * decryption of the file data as it passes between the lower
+ * encrypted file and the upper decrypted file.
+ *
+ * Copyright (C) 1997-2003 Erez Zadok
+ * Copyright (C) 2001-2003 Stony Brook University
+ * Copyright (C) 2004-2006 International Business Machines Corp.
+ * Author(s): Michael A. Halcrow <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <linux/pagemap.h>
+#include <linux/writeback.h>
+#include <linux/page-flags.h>
+#include <linux/mount.h>
+#include <linux/file.h>
+#include <linux/crypto.h>
+#include <asm/scatterlist.h>
+#include "ecryptfs_kernel.h"
+
+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(KERN_DEBUG, "Enter\n");
+ dentry = file->f_dentry;
+ inode = dentry->d_inode;
+ mapping = inode->i_mapping;
+ 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(KERN_DEBUG, "Exit\n");
+ return page;
+}
+
+static
+int write_zeros(struct file *file, pgoff_t index, int start, int num_zeros);
+
+/**
+ * Function for handling lseek-ing past the end of the file.
+ *
+ * This function does not support shrinking, only growing a file.
+ *
+ * @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.
+ * 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(KERN_DEBUG, "Enter; inode->i_size = [0x%.16x]; "
+ "new_length = [0x%.16x]\n", cur_length, new_length);
+ 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(KERN_DEBUG, "old_end_page_index = [0x%.16x]; "
+ "old_end_pos_in_page = [%d]; "
+ "new_end_page_index = [0x%.16x]; "
+ "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(KERN_ERR, "write_zeros(file=[%p], "
+ "index=[0x%.16x], "
+ "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;
+ }
+ /* 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(KERN_ERR, "write_zeros(file=[%p], "
+ "index=[0x%.16x], 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(KERN_ERR, "write_zeros(file=[%p], "
+ "index=[0x%.16x], "
+ "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(KERN_ERR, "write_zeros(file="
+ "[%p], index=[0x%.16x], 0, "
+ "new_end_pos_in_page=[%d]"
+ "returned [%d]\n", file, index,
+ new_end_pos_in_page, rc);
+ goto out;
+ }
+out:
+ ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * @param page Page that is locked before this call is made
+ * @return Zero on success
+ */
+static int ecryptfs_writepage(struct page *page, struct writeback_control *wbc)
+{
+ struct ecryptfs_page_crypt_context ctx;
+ int rc = 0;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; page->index = [0x%.16x]; "
+ "page->mapping->host = [%p]\n", page->index,
+ page->mapping->host);
+ ctx.page = page;
+ ctx.mode = ECRYPTFS_WRITEPAGE_MODE;
+ ctx.param.wbc = wbc;
+ rc = ecryptfs_encrypt_page(&ctx);
+ if (rc) {
+ ecryptfs_printk(KERN_WARNING, "Error encrypting "
+ "page (upper index [0x%.16x])\n", page->index);
+ ClearPageUptodate(page);
+ goto out;
+ }
+ SetPageUptodate(page);
+ unlock_page(page);
+out:
+ ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * Reads the data from the lower file file at index lower_page_index
+ * and copies that data into page.
+ *
+ * @param page Page to fill
+ * @param lower_page_index Index of the page in the lower file to get
+ */
+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(KERN_DEBUG, "Enter; lower_page_index = [0x%.16x]\n",
+ lower_page_index);
+ dentry = file->f_dentry;
+ if (NULL == ECRYPTFS_FILE_TO_PRIVATE_SM(file)) {
+ rc = -ENOENT;
+ ecryptfs_printk(KERN_ERR, "No lower file info\n");
+ goto out;
+ }
+ lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+ lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(dentry);
+ inode = dentry->d_inode;
+ lower_inode = ECRYPTFS_INODE_TO_LOWER(inode);
+ lower_a_ops = lower_inode->i_mapping->a_ops;
+ lower_page = read_cache_page(lower_inode->i_mapping, lower_page_index,
+ (filler_t *)lower_a_ops->readpage,
+ (void *)lower_file);
+ if (IS_ERR(lower_page)) {
+ rc = PTR_ERR(lower_page);
+ lower_page = NULL;
+ ecryptfs_printk(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(KERN_ERR, "Error reading lower "
+ "page at index=[0x%.16x]\n",
+ lower_page_index);
+ goto out;
+ }
+ wait_on_page_locked(lower_page);
+ if (!PageUptodate(lower_page)) {
+ rc = -EIO;
+ ecryptfs_printk(KERN_ERR, "Error reading lower "
+ "page at index=[0x%.16x]\n",
+ lower_page_index);
+ goto out;
+ }
+ }
+ page_data = (char *)kmap(page);
+ if (!page_data) {
+ rc = -ENOMEM;
+ ecryptfs_printk(KERN_ERR, "Error mapping page\n");
+ goto out;
+ }
+ lower_page_data = (char *)kmap(lower_page);
+ if (!lower_page_data) {
+ rc = -ENOMEM;
+ ecryptfs_printk(KERN_ERR, "Error mapping page\n");
+ kunmap(page);
+ goto out;
+ }
+ 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(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * Read in a page, decrypting if necessary.
+ *
+ * @param file This is an ecryptfs file
+ * @param page ecryptfs associated page to stick the read data into
+ * @return Zero on success; non-zero on error
+ */
+static int ecryptfs_readpage(struct file *file, struct page *page)
+{
+ int rc = 0;
+ struct ecryptfs_crypt_stat *crypt_stat;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; page->index = [%ld]\n",
+ page->index);
+ ASSERT(file && file->f_dentry && file->f_dentry->d_inode);
+ crypt_stat =
+ &ECRYPTFS_INODE_TO_PRIVATE(file->f_dentry->d_inode)->crypt_stat;
+ if (!crypt_stat
+ || !ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED)
+ || ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_NEW_FILE)) {
+ ecryptfs_printk(KERN_DEBUG,
+ "Passing through unencrypted page\n");
+ rc = ecryptfs_do_readpage(file, page, page->index);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error reading page; rc = "
+ "[%d]\n", rc);
+ goto out;
+ }
+ } else {
+ rc = ecryptfs_decrypt_page(file, page);
+ if (rc) {
+
+ ecryptfs_printk(KERN_ERR, "Error decrypting page; "
+ "rc = [%d]\n", rc);
+ goto out;
+ }
+ }
+ SetPageUptodate(page);
+out:
+ if (rc)
+ ClearPageUptodate(page);
+ ecryptfs_printk(KERN_DEBUG, "Unlocking page with index = [0x%.16x]\n",
+ page->index);
+ unlock_page(page);
+ ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+static int fill_zeros_to_end_of_page(struct page *page, unsigned int to)
+{
+ struct inode *inode = page->mapping->host;
+ int end_byte_in_page;
+ int rc = 0;
+ char *page_virt;
+
+ ecryptfs_printk(KERN_DEBUG, "Called on page w/ index [0x%.16x] and "
+ "to = [%d]\n", page->index, to);
+ if ((i_size_read(inode) / PAGE_CACHE_SIZE) == page->index) {
+ end_byte_in_page = i_size_read(inode) % PAGE_CACHE_SIZE;
+ if (to > end_byte_in_page)
+ end_byte_in_page = to;
+ page_virt = kmap(page);
+ if (!page_virt) {
+ rc = -ENOMEM;
+ ecryptfs_printk(KERN_WARNING,
+ "Could not map page\n");
+ goto out;
+ }
+ memset((page_virt + end_byte_in_page), 0,
+ (PAGE_CACHE_SIZE - end_byte_in_page));
+ kunmap(page);
+ }
+out:
+ return rc;
+}
+
+static int ecryptfs_prepare_write(struct file *file, struct page *page,
+ unsigned from, unsigned to)
+{
+ int rc = 0;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; page->index = [0x%.16x]; 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))
+ rc = ecryptfs_do_readpage(file, page, page->index);
+out:
+ return rc;
+}
+
+int ecryptfs_grab_and_map_lower_page(struct page **lower_page,
+ char **lower_virt,
+ struct inode *lower_inode,
+ unsigned long lower_page_index)
+{
+ int rc = 0;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; lower_page_index = [0x%.16x]\n",
+ lower_page_index);
+ (*lower_page) = grab_cache_page(lower_inode->i_mapping,
+ lower_page_index);
+ if (!(*lower_page)) {
+ ecryptfs_printk(KERN_ERR, "grab_cache_page for "
+ "lower_page_index = [0x%.16x] failed\n",
+ lower_page_index);
+ rc = -EINVAL;
+ goto out;
+ }
+ if (lower_virt)
+ (*lower_virt) = kmap((*lower_page));
+ else
+ kmap((*lower_page));
+out:
+ return rc;
+}
+
+int ecryptfs_writepage_and_release_lower_page(struct page *lower_page,
+ struct inode *lower_inode,
+ struct writeback_control *wbc)
+{
+ int rc = 0;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; lower_page->index = [0x%.16x]\n",
+ lower_page->index);
+ rc = lower_inode->i_mapping->a_ops->writepage(lower_page, wbc);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error calling lower writepage(); "
+ "rc = [%d]\n", rc);
+ goto out;
+ }
+ lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME;
+ page_cache_release(lower_page);
+out:
+ return rc;
+}
+
+void ecryptfs_unmap_and_release_lower_page(struct page *lower_page)
+{
+ ecryptfs_printk(KERN_DEBUG, "Enter; lower_page->index = [0x%.16x]\n",
+ lower_page->index);
+ kunmap(lower_page);
+ ecryptfs_printk(KERN_DEBUG, "Unlocking lower page with index = "
+ "[0x%.16x]\n", lower_page->index);
+ unlock_page(lower_page);
+ page_cache_release(lower_page);
+}
+
+/**
+ * Writes the lower file size to the first 8 bytes of the header.
+ *
+ * @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;
+ u64 file_size;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ rc = ecryptfs_grab_and_map_lower_page(&header_page, &header_virt,
+ lower_inode, 0);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "grab_cache_page for header page "
+ "failed\n");
+ goto out;
+ }
+ lower_a_ops = lower_inode->i_mapping->a_ops;
+ rc = lower_a_ops->prepare_write(lower_file, header_page, 0, 8);
+ file_size = (u64)i_size_read(inode);
+ ecryptfs_printk(KERN_DEBUG, "Writing size: [0x%.16x]\n", file_size);
+ file_size = cpu_to_be64(file_size);
+ memcpy(header_virt, &file_size, sizeof(u64));
+ rc = lower_a_ops->commit_write(lower_file, header_page, 0, 8);
+ if (rc < 0)
+ ecryptfs_printk(KERN_ERR, "Error commiting header page "
+ "write\n");
+ ecryptfs_unmap_and_release_lower_page(header_page);
+ lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty_sync(inode);
+out:
+ ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+int ecryptfs_get_lower_page(struct page **lower_page, struct inode *lower_inode,
+ struct file *lower_file,
+ unsigned long lower_page_index, int byte_offset,
+ int region_bytes)
+{
+ int rc = 0;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; lower_page_index = [0x%.16x], "
+ "byte_offset = [%d], region_bytes = [%d]\n",
+ lower_page_index, byte_offset, region_bytes);
+ rc = ecryptfs_grab_and_map_lower_page(lower_page, NULL, lower_inode,
+ lower_page_index);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error attempting to grab and map "
+ "lower page with index [0x%.16x]\n",
+ lower_page_index);
+ goto out;
+ }
+ rc = lower_inode->i_mapping->a_ops->prepare_write(lower_file,
+ (*lower_page),
+ byte_offset,
+ region_bytes);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "prepare_write for "
+ "lower_page_index = [0x%.16x] failed; rc = "
+ "[%d]\n", lower_page_index, rc);
+ }
+out:
+ if (rc && (*lower_page)) {
+ ecryptfs_unmap_and_release_lower_page(*lower_page);
+ (*lower_page) = NULL;
+ }
+ return rc;
+}
+
+/**
+ * @return Zero on success
+ */
+int
+ecryptfs_commit_lower_page(struct page *lower_page, struct inode *lower_inode,
+ struct file *lower_file, int byte_offset,
+ int region_size)
+{
+ int rc = 0;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; lower_page->index = [0x%.16x], "
+ "byte_offset = [%d], region_size = [%d]\n",
+ lower_page->index, byte_offset, region_size);
+ rc = lower_inode->i_mapping->a_ops->commit_write(
+ lower_file, lower_page, byte_offset, region_size);
+ if (rc < 0) {
+ ecryptfs_printk(KERN_ERR,
+ "Error committing write; rc = [%d]\n", rc);
+ } else
+ rc = 0;
+ ecryptfs_unmap_and_release_lower_page(lower_page);
+ return rc;
+}
+
+/**
+ * Used for plaintext pass-through; no page index interpolation
+ * required.
+ */
+int ecryptfs_copy_page_to_lower(struct page *page, struct inode *lower_inode,
+ struct file *lower_file)
+{
+ int rc = 0;
+ struct page *lower_page;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; page->index = [0x%.16x]\n",
+ page->index);
+ rc = ecryptfs_get_lower_page(&lower_page, lower_inode, lower_file,
+ page->index, 0, PAGE_CACHE_SIZE);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error attempting to get page "
+ "at index [0x%.16x]\n", page->index);
+ goto out;
+ }
+ /* TODO: aops */
+ memcpy((char *)page_address(lower_page), page_address(page),
+ PAGE_CACHE_SIZE);
+ rc = ecryptfs_commit_lower_page(lower_page, lower_inode, lower_file,
+ 0, PAGE_CACHE_SIZE);
+ if (rc)
+ ecryptfs_printk(KERN_ERR, "Error attempting to commit page "
+ "at index [0x%.16x]\n", page->index);
+out:
+ return rc;
+}
+
+static int
+process_new_file(struct ecryptfs_crypt_stat *crypt_stat,
+ struct file *file, struct inode *inode)
+{
+ struct page *header_page;
+ struct address_space_operations *lower_a_ops;
+ struct inode *lower_inode;
+ struct file *lower_file;
+ char *header_virt;
+ int rc = 0;
+ int current_header_page = 0;
+ int header_pages;
+ int more_header_data_to_be_written = 1;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ lower_inode = ECRYPTFS_INODE_TO_LOWER(inode);
+ lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+ lower_a_ops = lower_inode->i_mapping->a_ops;
+ header_pages = ((crypt_stat->header_extent_size
+ * crypt_stat->num_header_extents_at_front)
+ / PAGE_CACHE_SIZE);
+ ASSERT(header_pages >= 1);
+ while (current_header_page < header_pages) {
+ rc = ecryptfs_grab_and_map_lower_page(&header_page,
+ &header_virt,
+ lower_inode,
+ current_header_page);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "grab_cache_page for "
+ "header page [%d] failed; rc = [%d]\n",
+ current_header_page, rc);
+ goto out;
+ }
+ rc = lower_a_ops->prepare_write(lower_file, header_page, 0,
+ PAGE_CACHE_SIZE);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error preparing to write "
+ "header page out; rc = [%d]\n", rc);
+ goto out;
+ }
+ memset(header_virt, 0, PAGE_CACHE_SIZE);
+ if (more_header_data_to_be_written) {
+ rc = ecryptfs_write_headers_virt(header_virt,
+ crypt_stat,
+ file->f_dentry);
+ if (rc) {
+ ecryptfs_printk(KERN_WARNING, "Error "
+ "generating header; rc = "
+ "[%d]\n", rc);
+ rc = -EIO;
+ memset(header_virt, 0, PAGE_CACHE_SIZE);
+ ecryptfs_unmap_and_release_lower_page(
+ header_page);
+ goto out;
+ }
+ if (current_header_page == 0)
+ memset(header_virt, 0, 8);
+ more_header_data_to_be_written = 0;
+ }
+ rc = lower_a_ops->commit_write(lower_file, header_page, 0,
+ PAGE_CACHE_SIZE);
+ ecryptfs_unmap_and_release_lower_page(header_page);
+ if (rc < 0) {
+ ecryptfs_printk(KERN_ERR,
+ "Error commiting header page write; "
+ "rc = [%d]\n", rc);
+ break;
+ }
+ current_header_page++;
+ }
+ if (rc >= 0) {
+ rc = 0;
+ ecryptfs_printk(KERN_DEBUG, "lower_inode->i_blocks = "
+ "[0x%.16x]\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);
+ }
+ ecryptfs_printk(KERN_DEBUG, "Clearing ECRYPTFS_NEW_FILE flag in "
+ "crypt_stat at memory location [%p]\n", crypt_stat);
+ ECRYPTFS_CLEAR_FLAG(crypt_stat->flags, ECRYPTFS_NEW_FILE);
+out:
+ 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)
+{
+ struct ecryptfs_page_crypt_context ctx;
+ loff_t pos;
+ unsigned bytes = to - from;
+ struct inode *inode;
+ struct inode *lower_inode;
+ struct file *lower_file;
+ struct ecryptfs_crypt_stat *crypt_stat;
+ int rc = -ENOMEM;
+
+ ecryptfs_printk(KERN_DEBUG,
+ "Enter; page->index = [0x%.16x]; from = [%d]; to = "
+ "[%d]\n", page->index, from, to);
+ inode = page->mapping->host;
+ lower_inode = ECRYPTFS_INODE_TO_LOWER(inode);
+ lower_file = ECRYPTFS_FILE_TO_LOWER(file);
+ mutex_lock(&lower_inode->i_mutex);
+ crypt_stat =
+ &ECRYPTFS_INODE_TO_PRIVATE(file->f_dentry->d_inode)->crypt_stat;
+ ASSERT(crypt_stat);
+ ASSERT(lower_file);
+ if (ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_NEW_FILE)) {
+ ecryptfs_printk(KERN_DEBUG, "ECRYPTFS_NEW_FILE flag set in "
+ "crypt_stat at memory location [%p]\n", crypt_stat);
+ rc = process_new_file(crypt_stat, file, inode);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error processing new "
+ "file; rc = [%d]\n", rc);
+ goto out;
+ }
+ } else
+ ecryptfs_printk(KERN_DEBUG, "Not a new file\n");
+ ecryptfs_printk(KERN_DEBUG, "Calling fill_zeros_to_end_of_page"
+ "(page w/ index = [0x%.16x], to = [%d])\n", page->index,
+ to);
+ rc = fill_zeros_to_end_of_page(page, to);
+ if (rc) {
+ ecryptfs_printk(KERN_WARNING, "Error attempting to fill "
+ "zeros in page with index = [0x%.16x]\n",
+ page->index);
+ goto out;
+ }
+ ctx.page = page;
+ ctx.mode = ECRYPTFS_PREPARE_COMMIT_MODE;
+ ctx.param.lower_file = lower_file;
+ rc = ecryptfs_encrypt_page(&ctx);
+ if (rc) {
+ ecryptfs_printk(KERN_WARNING, "Error encrypting page (upper "
+ "index [0x%.16x])\n", page->index);
+ goto out;
+ }
+ 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(KERN_DEBUG, "Expanded file size to "
+ "[0x%.16x]\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:
+ kunmap(page); /* mapped in prior call (prepare_write) */
+ if (rc < 0)
+ ClearPageUptodate(page);
+ else
+ SetPageUptodate(page);
+ mutex_unlock(&lower_inode->i_mutex);
+ ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * Write a specified number of zero's to a page.
+ *
+ * (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(KERN_DEBUG, "Enter; index = [0x%.16x], start offset = "
+ "[%d] num_zeros = [%d]\n", index, start, num_zeros);
+ tmp_page = ecryptfs_get1page(file, index);
+ if (IS_ERR(tmp_page)) {
+ ecryptfs_printk(KERN_ERR, "Error getting page at index "
+ "[0x%.16x]\n", index);
+ rc = PTR_ERR(tmp_page);
+ goto out;
+ }
+ kmap(tmp_page);
+ rc = ecryptfs_prepare_write(file, tmp_page, start, start + num_zeros);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error preparing to write zero's "
+ "to remainder of page at index [0x%.16x]\n",
+ index);
+ kunmap(tmp_page);
+ page_cache_release(tmp_page);
+ goto out;
+ }
+ memset(((char *)page_address(tmp_page) + start), 0, num_zeros);
+ rc = ecryptfs_commit_write(file, tmp_page, start, start + num_zeros);
+ if (rc < 0) {
+ ecryptfs_printk(KERN_ERR, "Error attempting to write zero's "
+ "to remainder of page at index [0x%.16x]\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(KERN_DEBUG, "Exit\n");
+ 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(KERN_DEBUG, "Enter\n");
+ inode = (struct inode *)mapping->host;
+ lower_inode = ECRYPTFS_INODE_TO_LOWER(inode);
+ if (lower_inode->i_mapping->a_ops->bmap)
+ rc = lower_inode->i_mapping->a_ops->bmap(lower_inode->i_mapping,
+ block);
+ return rc;
+}
+
+static void sync_page(struct page *page)
+{
+ struct address_space *mapping = page->mapping;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ mapping->a_ops->sync_page(page);
+ ecryptfs_printk(KERN_DEBUG, "Exit\n");
+}
+
+static void ecryptfs_sync_page(struct page *page)
+{
+ struct inode *inode;
+ struct inode *lower_inode;
+ struct page *lower_page;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ inode = page->mapping->host;
+ lower_inode = ECRYPTFS_INODE_TO_LOWER(inode);
+ /* NOTE: Recently swapped with grab_cache_page(), since
+ * sync_page() just makes sure that pending I/O gets done. */
+ lower_page = find_get_page(lower_inode->i_mapping, page->index);
+ if (!lower_page) {
+ ecryptfs_printk(KERN_DEBUG, "find_get_page failed\n");
+ return;
+ }
+ sync_page(lower_page);
+ ecryptfs_printk(KERN_DEBUG, "Unlocking page with index = [0x%.16x]\n",
+ lower_page->index);
+ unlock_page(lower_page);
+ page_cache_release(lower_page);
+}
+
+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,
+};

2006-05-04 03:42:23

by Phillip Hellewell

[permalink] [raw]
Subject: [PATCH 11/13: eCryptfs] Keystore

This is the 11th patch in a series of 13 constituting the kernel
components of the eCryptfs cryptographic filesystem.

eCryptfs keystore. Packet generation and parsing code. Authentication
token management code.

Signed-off-by: Phillip Hellewell <[email protected]>
Signed-off-by: Michael Halcrow <[email protected]>

---

keystore.c | 1055 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 1055 insertions(+)

Index: linux-2.6.17-rc3-mm1-ecryptfs/fs/ecryptfs/keystore.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.17-rc3-mm1-ecryptfs/fs/ecryptfs/keystore.c 2006-05-02 19:36:02.000000000 -0600
@@ -0,0 +1,1055 @@
+/**
+ * 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) 2004-2006 International Business Machines Corp.
+ * Author(s): Michael A. Halcrow <[email protected]>
+ * Michael C. Thompson <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/syscalls.h>
+#include <linux/pagemap.h>
+#include <linux/key.h>
+#include <linux/random.h>
+#include <linux/crypto.h>
+#include <asm/scatterlist.h>
+#include "ecryptfs_kernel.h"
+
+/**
+ * request_key returned an error instead of a valid key address;
+ * determine the type of error, make appropriate log entries, and
+ * return an error code.
+ */
+int process_request_key_err(long err_code)
+{
+ int rc = 0;
+
+ switch (err_code) {
+ case ENOKEY:
+ ecryptfs_printk(KERN_WARNING, "No key\n");
+ rc = -ENOENT;
+ break;
+ case EKEYEXPIRED:
+ ecryptfs_printk(KERN_WARNING, "Key expired\n");
+ rc = -ETIME;
+ break;
+ case EKEYREVOKED:
+ ecryptfs_printk(KERN_WARNING, "Key revoked\n");
+ rc = -EINVAL;
+ break;
+ default:
+ ecryptfs_printk(KERN_WARNING, "Unknown error code: "
+ "[0x%.16x]\n", err_code);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+static void wipe_auth_tok_list(struct list_head *auth_tok_list_head)
+{
+ struct list_head *walker;
+ struct ecryptfs_auth_tok_list_item *auth_tok_list_item;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ walker = auth_tok_list_head->next;
+ while (walker != auth_tok_list_head) {
+ auth_tok_list_item =
+ list_entry(walker, struct ecryptfs_auth_tok_list_item,
+ list);
+ walker = auth_tok_list_item->list.next;
+ memset(auth_tok_list_item, 0,
+ sizeof(struct ecryptfs_auth_tok_list_item));
+ kmem_cache_free(ecryptfs_auth_tok_list_item_cache,
+ auth_tok_list_item);
+ }
+ ecryptfs_printk(KERN_DEBUG, "Exit\n");
+}
+
+kmem_cache_t *ecryptfs_auth_tok_list_item_cache;
+
+/**
+ * @param data Pointer to memory containing length at offset
+ * @param size This function writes the decoded size to this memory
+ * address; zero on error
+ * @param length_size The number of bytes occupied by the encoded
+ * length
+ * @return Zero on success
+ */
+static int parse_packet_length(unsigned char *data, int *size, int *length_size)
+{
+ int rc = 0;
+
+ (*length_size) = 0;
+ (*size) = 0;
+ if (data[0] < 192) {
+ /* One-byte length */
+ (*size) = data[0];
+ (*length_size) = 1;
+ } else if (data[0] < 224) {
+ /* Two-byte length */
+ (*size) = ((data[0] - 192) * 256);
+ (*size) += (data[1] + 192);
+ (*length_size) = 2;
+ } else if (data[0] == 255) {
+ /* Five-byte length; we're not supposed to see this */
+ ecryptfs_printk(KERN_ERR, "Five-byte packet length not "
+ "supported\n");
+ rc = -EINVAL;
+ goto out;
+ } else {
+ ecryptfs_printk(KERN_ERR, "Error parsing packet length\n");
+ rc = -EINVAL;
+ goto out;
+ }
+out:
+ return rc;
+}
+
+/**
+ * @param dest The byte array target into which to write the
+ * length. Must have at least 5 bytes allocated.
+ * @param size The length to write
+ * @param packet_size_length The number of bytes used to encode the
+ * packet length is written to this address
+ * @return Zero on success; non-zero on error
+ */
+static int write_packet_length(char *dest, int size, int *packet_size_length)
+{
+ int rc = 0;
+
+ if (size < 192) {
+ dest[0] = size;
+ (*packet_size_length) = 1;
+ } else if (size < 65536) {
+ dest[0] = (((size - 192) / 256) + 192);
+ dest[1] = ((size - 192) % 256);
+ (*packet_size_length) = 2;
+ } else {
+ rc = -EINVAL;
+ ecryptfs_printk(KERN_WARNING,
+ "Unsupported packet size: [%d]\n", size);
+ }
+ return rc;
+}
+
+/**
+ * Parse a tag 3 (passphrase) packet.
+ *
+ * @param crypt_stat The cryptographic context to modify based on
+ * packet contents
+ * @param data The raw bytes of the packet
+ * @param auth_tok_list eCryptfs parses packets into authentication
+ * tokens; a new authentication token will be
+ * placed at the end of this list for this packet
+ * @param new_auth_tok Pointer to a pointer to memory that this
+ * function allocates; sets the memory address of
+ * the pointer to NULL on error. This object is
+ * added to the auth_tok_list.
+ * @param packet_size This function writes the size of the parsed
+ * packet into this memory location; zero on error
+ * @return Zero on success; non-zero on error
+ */
+static int
+parse_tag_3_packet(struct ecryptfs_crypt_stat *crypt_stat,
+ unsigned char *data, struct list_head *auth_tok_list,
+ struct ecryptfs_auth_tok **new_auth_tok,
+ int *packet_size, int max_packet_size)
+{
+ int rc = 0;
+ int body_size;
+ struct ecryptfs_auth_tok_list_item *auth_tok_list_item;
+ int length_size;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ (*packet_size) = 0;
+ (*new_auth_tok) = NULL;
+ if (data[(*packet_size)++] != ECRYPTFS_TAG_3_PACKET_TYPE) {
+ ecryptfs_printk(KERN_ERR, "Enter w/ first byte != 0x%.2x\n",
+ ECRYPTFS_TAG_3_PACKET_TYPE);
+ rc = -EINVAL;
+ goto out;
+ }
+ if (unlikely((*packet_size) > max_packet_size)) {
+ ecryptfs_printk(KERN_ERR, "Packet size exceeds max\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ /* Released: wipe_auth_tok_list called in ecryptfs_parse_packet_set or
+ * at end of function upon failure */
+ auth_tok_list_item =
+ kmem_cache_alloc(ecryptfs_auth_tok_list_item_cache, SLAB_KERNEL);
+ if (!auth_tok_list_item) {
+ ecryptfs_printk(KERN_ERR, "Unable to allocate memory\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+ memset(auth_tok_list_item, 0,
+ sizeof(struct ecryptfs_auth_tok_list_item));
+ (*new_auth_tok) = &auth_tok_list_item->auth_tok;
+ rc = parse_packet_length(&data[(*packet_size)], &body_size,
+ &length_size);
+ if (rc) {
+ ecryptfs_printk(KERN_WARNING, "Error parsing packet length; "
+ "rc = [%d]\n", rc);
+ goto out_free;
+ }
+ if (body_size < (0x05 + ECRYPTFS_SALT_SIZE)) {
+ ecryptfs_printk(KERN_WARNING, "Invalid body size ([%d])\n",
+ body_size);
+ rc = -EINVAL;
+ goto out_free;
+ }
+ (*packet_size) += length_size;
+ if (unlikely((*packet_size) > max_packet_size)) {
+ ecryptfs_printk(KERN_ERR, "Packet size exceeds max\n");
+ rc = -EINVAL;
+ goto out_free;
+ }
+ /* There are 5 characters of additional information in the
+ * packet */
+ (*new_auth_tok)->session_key.encrypted_key_size =
+ body_size - (0x05 + ECRYPTFS_SALT_SIZE);
+ ecryptfs_printk(KERN_DEBUG, "Encrypted key size = [%d]\n",
+ (*new_auth_tok)->session_key.encrypted_key_size);
+ /* Version 4 (from RFC2440) */
+ if (data[(*packet_size)++] != 0x04) {
+ ecryptfs_printk(KERN_DEBUG, "Unknown version number "
+ "[%d]\n", data[(*packet_size) - 1]);
+ rc = -EINVAL;
+ goto out_free;
+ }
+ if (unlikely((*packet_size) > max_packet_size)) {
+ ecryptfs_printk(KERN_ERR, "Packet size exceeds max\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ ecryptfs_cipher_code_to_string(crypt_stat->cipher,
+ (u16)data[(*packet_size)]);
+ /* A little extra work to differentiate among the AES key
+ * sizes; see RFC2440 */
+ switch(data[(*packet_size)++]) {
+ case 0x07:
+ crypt_stat->key_size_bits = 128;
+ break;
+ case 0x08:
+ crypt_stat->key_size_bits = 192;
+ break;
+ case 0x09:
+ crypt_stat->key_size_bits = 256;
+ break;
+ }
+ if (unlikely((*packet_size) > max_packet_size)) {
+ ecryptfs_printk(KERN_ERR, "Packet size exceeds max\n");
+ rc = -EINVAL;
+ goto out_free;
+ }
+ ecryptfs_init_crypt_ctx(crypt_stat);
+ /* S2K identifier 3 (from RFC2440) */
+ if (data[(*packet_size)++] != 0x03) {
+ ecryptfs_printk(KERN_ERR, "Only S2K ID 3 is currently "
+ "supported\n");
+ rc = -ENOSYS;
+ goto out_free;
+ }
+ if (unlikely((*packet_size) > max_packet_size)) {
+ ecryptfs_printk(KERN_ERR, "Packet size exceeds max\n");
+ rc = -EINVAL;
+ goto out_free;
+ }
+ /* TODO: finish the hash mapping */
+ switch (data[(*packet_size)++]) {
+ case 0x01: /* See RFC2440 for these numbers and their mappings */
+ /* Choose MD5 */
+ memcpy((*new_auth_tok)->token.password.salt,
+ &data[(*packet_size)], ECRYPTFS_SALT_SIZE);
+ (*packet_size) += ECRYPTFS_SALT_SIZE;
+ if (unlikely((*packet_size) > max_packet_size)) {
+ ecryptfs_printk(KERN_ERR,
+ "Packet size exceeds max\n");
+ rc = -EINVAL;
+ goto out_free;
+ }
+ /* This conversion was taken straight from RFC2440 */
+ (*new_auth_tok)->token.password.hash_iterations =
+ ((u32) 16 + (data[(*packet_size)] & 15))
+ << ((data[(*packet_size)] >> 4) + 6);
+ (*packet_size)++;
+ if (unlikely((*packet_size) > max_packet_size)) {
+ ecryptfs_printk(KERN_ERR,
+ "Packet size exceeds max\n");
+ rc = -EINVAL;
+ goto out_free;
+ }
+ memcpy((*new_auth_tok)->session_key.encrypted_key,
+ &data[(*packet_size)],
+ (*new_auth_tok)->session_key.encrypted_key_size);
+ (*packet_size) +=
+ (*new_auth_tok)->session_key.encrypted_key_size;
+ if (unlikely((*packet_size) > max_packet_size)) {
+ ecryptfs_printk(KERN_ERR,
+ "Packet size exceeds max\n");
+ rc = -EINVAL;
+ goto out_free;
+ }
+ (*new_auth_tok)->session_key.flags &=
+ ~ECRYPTFS_CONTAINS_DECRYPTED_KEY;
+ (*new_auth_tok)->session_key.flags |=
+ ECRYPTFS_CONTAINS_ENCRYPTED_KEY;
+ (*new_auth_tok)->token.password.hash_algo = 0x01;
+ break;
+ default:
+ ecryptfs_printk(KERN_ERR, "Unsupported hash algorithm: "
+ "[%d]\n", data[(*packet_size) - 1]);
+ rc = -ENOSYS;
+ goto out_free;
+ }
+ if (unlikely((*packet_size) > max_packet_size)) {
+ ecryptfs_printk(KERN_ERR, "Packet size exceeds max\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ /* TODO: Use the keyring */
+ (*new_auth_tok)->uid = current->uid;
+ ECRYPTFS_SET_FLAG((*new_auth_tok)->flags, ECRYPTFS_PASSWORD);
+ /* TODO: Parametarize; we might actually want userspace to
+ * decrypt the session key. */
+ ECRYPTFS_CLEAR_FLAG((*new_auth_tok)->session_key.flags,
+ ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT);
+ ECRYPTFS_CLEAR_FLAG((*new_auth_tok)->session_key.flags,
+ ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT);
+ list_add(&auth_tok_list_item->list, auth_tok_list);
+ goto out;
+out_free:
+ (*new_auth_tok) = NULL;
+ memset(auth_tok_list_item, 0,
+ sizeof(struct ecryptfs_auth_tok_list_item));
+ kmem_cache_free(ecryptfs_auth_tok_list_item_cache,
+ auth_tok_list_item);
+out:
+ if (rc)
+ (*packet_size) = 0;
+ ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * Parse a tag 11 (literal) packet.
+ *
+ * @param data The raw bytes of the packet
+ * @param contents This function writes the data contents of the
+ * literal packet into this memory location
+ * @param max_contents_bytes The maximum number of bytes that this
+ * function is allowed to write into
+ * contents
+ * @param tag_11_contents_size This function writes the size of the
+ * parsed contents into this memory
+ * location; zero on error
+ * @param packet_size This function writes the size of the parsed
+ * packet into this memory location; zero on error
+ * @return Zero on success; non-zero on error
+ */
+static int
+parse_tag_11_packet(unsigned char *data, unsigned char *contents,
+ int max_contents_bytes, int *tag_11_contents_size,
+ int *packet_size, int max_packet_size)
+{
+ int rc = 0;
+ int body_size;
+ int length_size;
+
+ (*packet_size) = 0;
+ (*tag_11_contents_size) = 0;
+ if (data[(*packet_size)++] != ECRYPTFS_TAG_11_PACKET_TYPE) {
+ ecryptfs_printk(KERN_WARNING,
+ "Invalid tag 11 packet format\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ if (unlikely((*packet_size) > max_packet_size)) {
+ ecryptfs_printk(KERN_ERR, "Packet size exceeds max\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ rc = parse_packet_length(&data[(*packet_size)], &body_size,
+ &length_size);
+ if (rc) {
+ ecryptfs_printk(KERN_WARNING,
+ "Invalid tag 11 packet format\n");
+ goto out;
+ }
+ (*packet_size) += length_size;
+ if (unlikely((*packet_size) > max_packet_size)) {
+ ecryptfs_printk(KERN_ERR, "Packet size exceeds max\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ /* We have 13 bytes of surrounding packet values */
+ (*tag_11_contents_size) = (body_size - 13);
+ if ((*tag_11_contents_size) > max_contents_bytes) {
+ rc = -ENOMEM;
+ ecryptfs_printk(KERN_WARNING, "Not enough space allocated "
+ "in contents to copy entire contents of tag 11 "
+ "packet\n");
+ goto out;
+ }
+ if (data[(*packet_size)++] != 0x62) {
+ ecryptfs_printk(KERN_WARNING, "Unrecognizable packet\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ if (unlikely((*packet_size) > max_packet_size)) {
+ ecryptfs_printk(KERN_ERR, "Packet size exceeds max\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ if (data[(*packet_size)++] != 0x08) {
+ ecryptfs_printk(KERN_WARNING, "Unrecognizable packet\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ if (unlikely((*packet_size) > max_packet_size)) {
+ ecryptfs_printk(KERN_ERR, "Packet size exceeds max\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ (*packet_size) += 12; /* We don't care about the filename or
+ * the timestamp */
+ if (unlikely((*packet_size) > max_packet_size)) {
+ ecryptfs_printk(KERN_ERR, "Packet size exceeds max\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ memcpy(contents, &data[(*packet_size)], (*tag_11_contents_size));
+ (*packet_size) += (*tag_11_contents_size);
+ if (unlikely((*packet_size) > max_packet_size)) {
+ ecryptfs_printk(KERN_ERR, "Packet size exceeds max\n");
+ rc = -EINVAL;
+ goto out;
+ }
+out:
+ if (rc) {
+ (*packet_size) = 0;
+ (*tag_11_contents_size) = 0;
+ }
+ 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_stat *crypt_stat)
+{
+ 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 (ECRYPTFS_CHECK_FLAG(password_s_ptr->flags,
+ ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET))
+ ecryptfs_printk(KERN_DEBUG, "Session key encryption key "
+ "set; skipping key generation\n");
+ ecryptfs_printk(KERN_DEBUG, "Session key encryption key (size [%d])"
+ ":\n",
+ password_s_ptr->session_key_encryption_key_bytes);
+ if (ecryptfs_verbosity > 0)
+ ecryptfs_dump_hex(password_s_ptr->session_key_encryption_key,
+ password_s_ptr->
+ session_key_encryption_key_bytes);
+ tfm = crypto_alloc_tfm(crypt_stat->cipher, 0);
+ if (!tfm) {
+ ecryptfs_printk(KERN_ERR, "Error allocating crypto "
+ "context\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+ crypto_cipher_setkey(tfm, password_s_ptr->session_key_encryption_key,
+ password_s_ptr->session_key_encryption_key_bytes);
+ /* TODO: virt_to_scatterlist */
+ encrypted_session_key = (char *)__get_free_page(GFP_KERNEL);
+ if (!encrypted_session_key) {
+ ecryptfs_printk(KERN_ERR, "Out of memory\n");
+ rc = -ENOMEM;
+ goto out_free_tfm;
+ }
+ session_key = (char *)__get_free_page(GFP_KERNEL);
+ if (!session_key) {
+ kfree(encrypted_session_key);
+ ecryptfs_printk(KERN_ERR, "Out of memory\n");
+ rc = -ENOMEM;
+ goto out_free_tfm;
+ }
+ 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;
+ ASSERT(auth_tok->session_key.encrypted_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_stat->key, auth_tok->session_key.decrypted_key,
+ auth_tok->session_key.decrypted_key_size);
+ ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_KEY_VALID);
+ crypt_stat->key_size_bits =
+ auth_tok->session_key.decrypted_key_size * 8;
+ ecryptfs_printk(KERN_DEBUG, "Decrypted session key:\n");
+ if (ecryptfs_verbosity > 0)
+ ecryptfs_dump_hex(crypt_stat->key,
+ crypt_stat->key_size_bits / 8);
+ memset(encrypted_session_key, 0, PAGE_CACHE_SIZE);
+ free_page((unsigned long)encrypted_session_key);
+ memset(session_key, 0, PAGE_CACHE_SIZE);
+ free_page((unsigned long)session_key);
+out_free_tfm:
+ crypto_free_tfm(tfm);
+out:
+ return rc;
+}
+
+/**
+ * Get crypt_stat to have the file's session key if the requisite key
+ * is available to decrypt the session key.
+ *
+ * @param dest The header page in memory
+ * @param version Version of file format, to guide parsing behavior
+ * @return Zero if a valid authentication token was retrieved and processed;
+ * negative value for file not encrypted or for error conditions
+ */
+int ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat,
+ unsigned char *src,
+ struct dentry *ecryptfs_dentry)
+{
+ int i = 0;
+ int rc = 0;
+ int found_auth_tok = 0;
+ int next_packet_is_auth_tok_packet;
+ char sig[ECRYPTFS_SIG_SIZE_HEX];
+ struct list_head auth_tok_list;
+ struct list_head *walker;
+ struct ecryptfs_auth_tok *chosen_auth_tok = NULL;
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat =
+ &(ECRYPTFS_SUPERBLOCK_TO_PRIVATE(
+ ecryptfs_dentry->d_sb)->mount_crypt_stat);
+ struct ecryptfs_auth_tok *candidate_auth_tok = NULL;
+ int packet_size;
+ struct ecryptfs_auth_tok *new_auth_tok;
+ unsigned char sig_tmp_space[ECRYPTFS_SIG_SIZE];
+ int tag_11_contents_size;
+ int tag_11_packet_size;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ 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) {
+ int max_packet_size;
+
+ max_packet_size = ((PAGE_CACHE_SIZE - 8) - i);
+ switch (src[i]) {
+ case ECRYPTFS_TAG_3_PACKET_TYPE:
+ rc = parse_tag_3_packet(crypt_stat,
+ (unsigned char *)&src[i],
+ &auth_tok_list, &new_auth_tok,
+ &packet_size, max_packet_size);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error parsing "
+ "tag 3 packet\n");
+ rc = -EIO;
+ goto out_wipe_list;
+ }
+ i += packet_size;
+ rc = parse_tag_11_packet((unsigned char *)&src[i],
+ sig_tmp_space,
+ ECRYPTFS_SIG_SIZE,
+ &tag_11_contents_size,
+ &tag_11_packet_size,
+ max_packet_size);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "No valid "
+ "(ecryptfs-specific) literal "
+ "packet containing "
+ "authentication token "
+ "signature found after "
+ "tag 3 packet\n");
+ rc = -EIO;
+ goto out_wipe_list;
+ }
+ i += tag_11_packet_size;
+ if (ECRYPTFS_SIG_SIZE != tag_11_contents_size) {
+ ecryptfs_printk(KERN_ERR, "Expected "
+ "signature of size [%d]; "
+ "read size [%d]\n",
+ ECRYPTFS_SIG_SIZE,
+ tag_11_contents_size);
+ rc = -EIO;
+ goto out_wipe_list;
+ }
+ ecryptfs_to_hex(new_auth_tok->token.password.signature,
+ sig_tmp_space, tag_11_contents_size);
+ new_auth_tok->token.password.signature[
+ ECRYPTFS_PASSWORD_SIG_SIZE] = '\0';
+ ECRYPTFS_SET_FLAG(crypt_stat->flags,
+ ECRYPTFS_ENCRYPTED);
+ break;
+ case ECRYPTFS_TAG_11_PACKET_TYPE:
+ ecryptfs_printk(KERN_WARNING, "Invalid packet set "
+ "(Tag 11 not allowed by itself)\n");
+ rc = -EIO;
+ goto out_wipe_list;
+ break;
+ default:
+ ecryptfs_printk(KERN_DEBUG, "No packet at offset "
+ "[%d] of the file header; hex value of "
+ "character is [0x%.2x]\n", i, src[i]);
+ next_packet_is_auth_tok_packet = 0;
+ }
+ }
+ if (list_empty(&auth_tok_list)) {
+ rc = -EINVAL; /* Do not support non-encrypted files in
+ * the 0.1 release */
+ goto out;
+ }
+ /* If we have a global auth tok, then we should try to use
+ * it */
+ if (mount_crypt_stat->global_auth_tok) {
+ memcpy(sig, mount_crypt_stat->global_auth_tok_sig,
+ ECRYPTFS_SIG_SIZE_HEX);
+ chosen_auth_tok = mount_crypt_stat->global_auth_tok;
+ } else
+ BUG(); /* We should always have a global auth tok in
+ * the 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(KERN_DEBUG,
+ "Considering cadidate auth tok:\n");
+ ecryptfs_dump_auth_tok(candidate_auth_tok);
+ }
+ /* TODO: Replace ECRYPTFS_SIG_SIZE_HEX w/ dynamic value */
+ if ((ECRYPTFS_CHECK_FLAG(candidate_auth_tok->flags,
+ 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_stat salt */
+ }
+ }
+leave_list:
+ if (!found_auth_tok) {
+ ecryptfs_printk(KERN_ERR, "Could not find authentication "
+ "token on temporary list for sig [%.*s]\n",
+ ECRYPTFS_SIG_SIZE_HEX, sig);
+ rc = -EIO;
+ goto out_wipe_list;
+ } else {
+ memcpy(&(candidate_auth_tok->token.password),
+ &(chosen_auth_tok->token.password),
+ sizeof(struct ecryptfs_password));
+ rc = decrypt_session_key(candidate_auth_tok, crypt_stat);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error decrypting the "
+ "session key\n");
+ goto out_wipe_list;
+ }
+ rc = ecryptfs_compute_root_iv(crypt_stat);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error computing "
+ "the root IV\n");
+ goto out_wipe_list;
+ }
+ }
+ rc = ecryptfs_init_crypt_ctx(crypt_stat);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error initializing crypto "
+ "context for cipher [%s]; rc = [%d]\n",
+ crypt_stat->cipher, rc);
+ }
+out_wipe_list:
+ wipe_auth_tok_list(&auth_tok_list);
+out:
+ ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * Generate literal (tag 11) data packet.
+ *
+ * @param dest Target into which Tag 11 packet is to be written
+ * @param max Maximum packet length
+ * @param contents Byte array of contents to copy in
+ * @param contents_length Number of bytes in contents
+ * @param packet_length Length of the Tag 11 packet written; zero on
+ * error
+ * @return Zero on success; non-zero on error
+ */
+int
+write_tag_11_packet(char *dest, int max, char *contents, int contents_length,
+ int *packet_length)
+{
+ int rc = 0;
+ int packet_size_length;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; contents_length = [%d]\n",
+ contents_length);
+ (*packet_length) = 0;
+ if ((13 + contents_length) > max) {
+ rc = -EINVAL;
+ ecryptfs_printk(KERN_ERR, "Packet length larger than "
+ "maximum allowable\n");
+ goto out;
+ }
+ /* General packet header */
+ /* Packet tag */
+ dest[(*packet_length)++] = ECRYPTFS_TAG_11_PACKET_TYPE;
+ /* Packet length */
+ rc = write_packet_length(&dest[(*packet_length)],
+ (13 + contents_length), &packet_size_length);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error generating tag 11 packet "
+ "header; cannot generate packet length\n");
+ goto out;
+ }
+ (*packet_length) += packet_size_length;
+ /* Tag 11 specific */
+ /* One-octet field that describes how the data is formatted */
+ dest[(*packet_length)++] = 0x62; /* binary data */
+ /* One-octet filename length followed by filename */
+ dest[(*packet_length)++] = 8;
+ memcpy(&dest[(*packet_length)], "_CONSOLE", 8);
+ (*packet_length) += 8;
+ /* Four-octet number indicating modification date */
+ memset(&dest[(*packet_length)], 0x00, 4);
+ (*packet_length) += 4;
+ /* Remainder is literal data */
+ memcpy(&dest[(*packet_length)], contents, contents_length);
+ (*packet_length) += contents_length;
+ out:
+ if (rc)
+ (*packet_length) = 0;
+ ecryptfs_printk(KERN_DEBUG, "Exit\n");
+ return rc;
+}
+
+/**
+ * Generate passphrase (tag 3) packet.
+ *
+ * @param dest Buffer into which to write the packet
+ * @param max Maximum number of bytes that can be writtn
+ * @param packet_size This function will write the number of bytes
+ * that end up constituting the packet; set to zero
+ * on error
+ * @return Zero on success; non-zero on error
+ */
+static int
+write_tag_3_packet(char *dest, int max, struct ecryptfs_auth_tok *auth_tok,
+ struct ecryptfs_crypt_stat *crypt_stat,
+ struct ecryptfs_key_record *key_rec, int *packet_size)
+{
+ int rc = 0;
+
+ 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 packet_size_length;
+ int cipher_code;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ (*packet_size) = 0;
+ /* 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_stat->key_size_bits;
+ 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(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 (ECRYPTFS_CHECK_FLAG(auth_tok->token.password.flags,
+ ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET)) {
+ ecryptfs_printk(KERN_DEBUG, "Using previously generated "
+ "session key encryption key of size [%d]\n",
+ auth_tok->token.password.
+ session_key_encryption_key_bytes);
+ memcpy(session_key_encryption_key,
+ auth_tok->token.password.session_key_encryption_key,
+ auth_tok->token.password.
+ session_key_encryption_key_bytes);
+ ecryptfs_printk(KERN_DEBUG,
+ "Cached session key " "encryption key: \n");
+ if (ecryptfs_verbosity > 0)
+ ecryptfs_dump_hex(session_key_encryption_key, 16);
+ }
+ if (unlikely(ecryptfs_verbosity > 0)) {
+ ecryptfs_printk(KERN_DEBUG, "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_stat->key,
+ crypt_stat->key_size_bits / 8, src_sg, 2);
+ if (!rc) {
+ ecryptfs_printk(KERN_ERR, "Error generating scatterlist "
+ "for crypt_stat 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(KERN_ERR, "Error generating scatterlist "
+ "for crypt_stat encrypted session key\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+ if ((tfm = crypto_alloc_tfm(crypt_stat->cipher, 0)) == NULL) {
+ ecryptfs_printk(KERN_ERR, "Could not initialize crypto "
+ "context for cipher [%s]\n",
+ crypt_stat->cipher);
+ rc = -EINVAL;
+ goto out;
+ }
+ rc = crypto_cipher_setkey(tfm, session_key_encryption_key,
+ ECRYPTFS_DEFAULT_KEY_BYTES);
+ if (rc < 0) {
+ ecryptfs_printk(KERN_ERR, "Error setting key for crypto "
+ "context\n");
+ goto out;
+ }
+ rc = 0;
+ ecryptfs_printk(KERN_DEBUG, "Encrypting [%d] bytes of the key\n",
+ crypt_stat->key_size_bits / 8);
+ crypto_cipher_encrypt(tfm, dest_sg, src_sg,
+ crypt_stat->key_size_bits / 8);
+ ecryptfs_printk(KERN_DEBUG, "This should be the encrypted key:\n");
+ if (ecryptfs_verbosity > 0)
+ ecryptfs_dump_hex((*key_rec).enc_key,
+ (*key_rec).enc_key_size_bits / 8);
+encrypted_session_key_set:
+ /* Now we have a valid key_rec. Append it to the
+ * key_rec set. */
+ key_rec_size = (sizeof(struct ecryptfs_key_record)
+ - ECRYPTFS_MAX_KEY_BYTES
+ + ((*key_rec).enc_key_size_bits / 8) );
+ /* TODO: Include a packet size limit as a parameter to this
+ * function once we have multi-packet headers (for versions
+ * later than 0.1 */
+ if (key_rec_size >= ECRYPTFS_MAX_KEYSET_SIZE) {
+ ecryptfs_printk(KERN_ERR, "Keyset too large\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ /* TODO: Packet size limit */
+ /* We have 5 bytes of surrounding packet data */
+ if ((0x05 + ECRYPTFS_SALT_SIZE
+ + ((*key_rec).enc_key_size_bits / 8)) >= PAGE_CACHE_SIZE) {
+ ecryptfs_printk(KERN_ERR, "Authentication token is too "
+ "large\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ /* This format is inspired by OpenPGP; see RFC 2440
+ * packet tag 3 */
+ dest[(*packet_size)++] = ECRYPTFS_TAG_3_PACKET_TYPE;
+ /* ver+cipher+s2k+hash+salt+iter+enc_key */
+ rc = write_packet_length(&dest[(*packet_size)],
+ (0x05 + ECRYPTFS_SALT_SIZE
+ + ((*key_rec).enc_key_size_bits / 8)),
+ &packet_size_length);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error generating tag 3 packet "
+ "header; cannot generate packet length\n");
+ goto out;
+ }
+ (*packet_size) += packet_size_length;
+ dest[(*packet_size)++] = 0x04; /* version 4 */
+ cipher_code = ecryptfs_code_for_cipher_string(crypt_stat->cipher);
+ if (cipher_code == 0) {
+ ecryptfs_printk(KERN_WARNING, "Unable to generate code for "
+ "cipher [%s]\n", crypt_stat->cipher);
+ rc = -EINVAL;
+ goto out;
+ }
+ /* If it is AES, we need to get more specific. */
+ if (cipher_code == 0x07) {
+ switch (crypt_stat->key_size_bits) {
+ case 128:
+ break;
+ case 192:
+ cipher_code = 0x08; /* AES-192 */
+ break;
+ case 256:
+ cipher_code = 0x09; /* AES-256 */
+ break;
+ default:
+ rc = -EINVAL;
+ ecryptfs_printk(KERN_WARNING, "Unsupported AES key "
+ "size: [%d]\n",
+ crypt_stat->key_size_bits);
+ goto out;
+ }
+ }
+ dest[(*packet_size)++] = cipher_code;
+ dest[(*packet_size)++] = 0x03; /* S2K */
+ dest[(*packet_size)++] = 0x01; /* MD5 (TODO: parameterize) */
+ memcpy(&dest[(*packet_size)], auth_tok->token.password.salt,
+ ECRYPTFS_SALT_SIZE);
+ (*packet_size) += ECRYPTFS_SALT_SIZE; /* salt */
+ dest[(*packet_size)++] = 0x60; /* hash iterations (65536) */
+ memcpy(&dest[(*packet_size)], (*key_rec).enc_key,
+ (*key_rec).enc_key_size_bits / 8);
+ (*packet_size) += ((*key_rec).enc_key_size_bits / 8);
+out:
+ if (tfm)
+ crypto_free_tfm(tfm);
+ if (rc)
+ (*packet_size) = 0;
+ ecryptfs_printk(KERN_DEBUG, "Exit; (*packet_size) = [%d], rc = "
+ "[%d]\n", (*packet_size), rc);
+ return rc;
+}
+
+/**
+ * Generates a key packet set and writes it to the virtual address
+ * passed in.
+ *
+ * @param dest Virtual address from which to write the key record set
+ * @param crypt_stat The cryptographic context from which the
+ * authentication tokens will be retrieved
+ * @param ecryptfs_dentry The dentry, used to retrieve the mount crypt
+ * stat for the global parameters
+ * @param len The amount written
+ * @return Zero on success
+ */
+int
+ecryptfs_generate_key_packet_set(char *dest_base,
+ struct ecryptfs_crypt_stat *crypt_stat,
+ struct dentry *ecryptfs_dentry, int *len)
+{
+ int rc = 0;
+ struct ecryptfs_auth_tok *auth_tok;
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat =
+ &(ECRYPTFS_SUPERBLOCK_TO_PRIVATE(
+ ecryptfs_dentry->d_sb)->mount_crypt_stat);
+ int written;
+ struct ecryptfs_key_record key_rec;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ (*len) = 0;
+ if (mount_crypt_stat->global_auth_tok) {
+ auth_tok = mount_crypt_stat->global_auth_tok;
+ if (ECRYPTFS_CHECK_FLAG(auth_tok->flags, ECRYPTFS_PASSWORD)) {
+ rc = write_tag_3_packet((dest_base + (*len)),
+ PAGE_CACHE_SIZE, auth_tok,
+ crypt_stat, &key_rec,
+ &written);
+ if (rc) {
+ ecryptfs_printk(KERN_WARNING, "Error "
+ "writing tag 3 packet\n");
+ goto out;
+ }
+ (*len) += written;
+ /* Write auth tok signature packet */
+ rc = write_tag_11_packet(
+ (dest_base + (*len)),
+ (PAGE_CACHE_SIZE - (*len)),
+ key_rec.sig, ECRYPTFS_SIG_SIZE, &written);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error writing "
+ "auth tok signature packet\n");
+ goto out;
+ }
+ (*len) += written;
+ } else {
+ ecryptfs_printk(KERN_WARNING, "Unsupported "
+ "authentication token type\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ if (rc) {
+ ecryptfs_printk(KERN_WARNING, "Error writing "
+ "authentication token packet with sig "
+ "= [%s]\n",
+ mount_crypt_stat->global_auth_tok_sig);
+ rc = -EIO;
+ goto out;
+ }
+ } else
+ BUG();
+ dest_base[(*len)] = 0x00;
+out:
+ if (rc)
+ (*len) = 0;
+ ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
+ return rc;
+}

2006-05-04 03:43:32

by Phillip Hellewell

[permalink] [raw]
Subject: [PATCH 13/13: eCryptfs] Debug functions

This is the 13th patch in a series of 13 constituting the kernel
components of the eCryptfs cryptographic filesystem.

Functions used only for debugging purposes; typically called when the
verbosity is turned higher than 0.

Signed-off-by: Phillip Hellewell <[email protected]>
Signed-off-by: Michael Halcrow <[email protected]>

---

debug.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 122 insertions(+)

Index: linux-2.6.17-rc3-mm1-ecryptfs/fs/ecryptfs/debug.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.17-rc3-mm1-ecryptfs/fs/ecryptfs/debug.c 2006-05-02 19:35:59.000000000 -0600
@@ -0,0 +1,122 @@
+/**
+ * eCryptfs: Linux filesystem encryption layer
+ * Functions only useful for debugging.
+ *
+ * Copyright (C) 2006 International Business Machines Corp.
+ * Author(s): Michael A. Halcrow <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "ecryptfs_kernel.h"
+
+void ecryptfs_dump_auth_tok(struct ecryptfs_auth_tok *auth_tok)
+{
+ char salt[ECRYPTFS_SALT_SIZE * 2 + 1];
+ char sig[ECRYPTFS_SIG_SIZE_HEX + 1];
+
+ ecryptfs_printk(KERN_DEBUG, "Auth tok at mem loc [%p]:\n",
+ auth_tok);
+ if (ECRYPTFS_CHECK_FLAG(auth_tok->flags, ECRYPTFS_PRIVATE_KEY)) {
+ ecryptfs_printk(KERN_DEBUG, " * private key type\n");
+ ecryptfs_printk(KERN_DEBUG, " * (NO PRIVATE KEY SUPPORT "
+ "IN ECRYPTFS VERSION 0.1)\n");
+ } else {
+ ecryptfs_printk(KERN_DEBUG, " * passphrase type\n");
+ ecryptfs_to_hex(salt, auth_tok->token.password.salt,
+ ECRYPTFS_SALT_SIZE);
+ salt[ECRYPTFS_SALT_SIZE * 2] = '\0';
+ ecryptfs_printk(KERN_DEBUG, " * salt = [%s]\n", salt);
+ if (ECRYPTFS_CHECK_FLAG(auth_tok->token.password.flags,
+ ECRYPTFS_PERSISTENT_PASSWORD)) {
+ ecryptfs_printk(KERN_DEBUG, " * persistent\n");
+ }
+ memcpy(sig, auth_tok->token.password.signature,
+ ECRYPTFS_SIG_SIZE_HEX);
+ sig[ECRYPTFS_SIG_SIZE_HEX] = '\0';
+ ecryptfs_printk(KERN_DEBUG, " * signature = [%s]\n", sig);
+ }
+ if (ECRYPTFS_CHECK_FLAG(auth_tok->flags, ECRYPTFS_CONTAINS_SECRET)) {
+ ecryptfs_printk(KERN_DEBUG, " * contains secret value\n");
+ } else {
+ ecryptfs_printk(KERN_DEBUG, " * lacks secret value\n");
+ }
+ if (ECRYPTFS_CHECK_FLAG(auth_tok->flags, ECRYPTFS_EXPIRED))
+ ecryptfs_printk(KERN_DEBUG, " * expired\n");
+ ecryptfs_printk(KERN_DEBUG, " * 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(KERN_DEBUG,
+ " * Userspace decrypt request set\n");
+ if (auth_tok->session_key.flags
+ & ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT)
+ ecryptfs_printk(KERN_DEBUG,
+ " * Userspace encrypt request set\n");
+ if (auth_tok->session_key.flags & ECRYPTFS_CONTAINS_DECRYPTED_KEY) {
+ ecryptfs_printk(KERN_DEBUG, " * Contains decrypted key\n");
+ ecryptfs_printk(KERN_DEBUG,
+ " * session_key.decrypted_key_size = [0x%x]\n",
+ auth_tok->session_key.decrypted_key_size);
+ ecryptfs_printk(KERN_DEBUG, " * 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(KERN_DEBUG, " * Contains encrypted key\n");
+ ecryptfs_printk(KERN_DEBUG,
+ " * session_key.encrypted_key_size = [0x%x]\n",
+ auth_tok->session_key.encrypted_key_size);
+ ecryptfs_printk(KERN_DEBUG, " * 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);
+ }
+}
+
+/**
+ * Dump hexadecimal representation of char array
+ *
+ * @param data
+ * @param bytes
+ */
+void ecryptfs_dump_hex(char *data, int bytes)
+{
+ int i = 0;
+ int add_newline = 1;
+
+ if (ecryptfs_verbosity < 1)
+ return;
+ if (bytes != 0) {
+ printk(KERN_DEBUG "0x%.2x.", (unsigned char)data[i]);
+ i++;
+ }
+ while (i < bytes) {
+ printk("0x%.2x.", (unsigned char)data[i]);
+ i++;
+ if (i % 16 == 0) {
+ printk("\n");
+ add_newline = 0;
+ } else
+ add_newline = 1;
+ }
+ if (add_newline)
+ printk("\n");
+}

2006-05-04 03:43:00

by Phillip Hellewell

[permalink] [raw]
Subject: [PATCH 12/13: eCryptfs] Crypto functions

This is the 12th patch in a series of 13 constituting the kernel
components of the eCryptfs cryptographic filesystem.

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

Signed-off-by: Phillip Hellewell <[email protected]>
Signed-off-by: Michael Halcrow <[email protected]>

---

crypto.c | 1467 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 1467 insertions(+)

Index: linux-2.6.17-rc3-mm1-ecryptfs/fs/ecryptfs/crypto.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.17-rc3-mm1-ecryptfs/fs/ecryptfs/crypto.c 2006-05-02 19:35:59.000000000 -0600
@@ -0,0 +1,1467 @@
+/**
+ * eCryptfs: Linux filesystem encryption layer
+ *
+ * Copyright (C) 1997-2004 Erez Zadok
+ * Copyright (C) 2001-2004 Stony Brook University
+ * Copyright (C) 2004-2006 International Business Machines Corp.
+ * Author(s): Michael A. Halcrow <[email protected]>
+ * Michael C. Thompson <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/pagemap.h>
+#include <linux/random.h>
+#include <linux/compiler.h>
+#include <linux/key.h>
+#include <linux/namei.h>
+#include <linux/crypto.h>
+#include <linux/file.h>
+#include <linux/scatterlist.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);
+ }
+}
+
+/**
+ * Uses the allocated crypto context that crypt_stat references to
+ * generate the MD5 sum of the contents of src.
+ *
+ * @param dst Pointer to 16 bytes of allocated memory
+ */
+int ecryptfs_calculate_md5(char *dst, struct ecryptfs_crypt_stat *crypt_stat,
+ char *src, int len)
+{
+ int rc = 0;
+ struct scatterlist sg;
+
+ sg_init_one(&sg, (u8 *)src, len);
+ if (!crypt_stat->md5_tfm) {
+ crypt_stat->md5_tfm =
+ crypto_alloc_tfm("md5", CRYPTO_TFM_REQ_MAY_SLEEP);
+ if (!crypt_stat->md5_tfm) {
+ rc = -ENOMEM;
+ ecryptfs_printk(KERN_ERR, "Error attempting to "
+ "allocate crypto context\n");
+ goto out;
+ }
+ }
+ crypto_digest_init(crypt_stat->md5_tfm);
+ crypto_digest_update(crypt_stat->md5_tfm, &sg, 1);
+ crypto_digest_final(crypt_stat->md5_tfm, dst);
+out:
+ return rc;
+}
+
+/**
+ * Generate the initialization vector from the given root IV and page
+ * offset.
+ *
+ * @param iv
+ * @param crypt_stat Pointer to crypt_stat struct for the current
+ * inode
+ * @param offset
+ * @return Zero on success; non-zero on error
+ */
+int ecryptfs_derive_iv(char *iv, struct ecryptfs_crypt_stat *crypt_stat,
+ pgoff_t offset)
+{
+ int rc = 0;
+ char dst[MD5_DIGEST_SIZE];
+ char src[ECRYPTFS_MAX_IV_BYTES + 16];
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; offset = [0x%.16x]\n", offset);
+ if (unlikely(ecryptfs_verbosity > 0)) {
+ ecryptfs_printk(KERN_DEBUG, "root iv:\n");
+ ecryptfs_dump_hex(crypt_stat->root_iv, crypt_stat->iv_bytes);
+ }
+ /* TODO: It is probably secure to just cast the least
+ * significant bits of the root IV into an unsigned long and
+ * add the offset to that rather than go through all this
+ * hashing business. -Halcrow */
+ memcpy(src, crypt_stat->root_iv, crypt_stat->iv_bytes);
+ memset((src + crypt_stat->iv_bytes), 0, 16);
+ snprintf((src + crypt_stat->iv_bytes), 16, "%ld", offset);
+ if (unlikely(ecryptfs_verbosity > 0)) {
+ ecryptfs_printk(KERN_DEBUG, "source:\n");
+ ecryptfs_dump_hex(src, (crypt_stat->iv_bytes + 16));
+ }
+ rc = ecryptfs_calculate_md5(dst, crypt_stat, src,
+ (crypt_stat->iv_bytes + 16));
+ if (rc) {
+ ecryptfs_printk(KERN_WARNING, "Error attempting to compute "
+ "MD5 while generating IV for a page\n");
+ goto out;
+ }
+ memcpy(iv, dst, crypt_stat->iv_bytes);
+ if (unlikely(ecryptfs_verbosity > 0)) {
+ ecryptfs_printk(KERN_DEBUG, "derived iv:\n");
+ ecryptfs_dump_hex(iv, crypt_stat->iv_bytes);
+ }
+out:
+ return rc;
+}
+
+/**
+ * Initialize the crypt_stat structure.
+ *
+ * @param crypt_stat Pointer to the crypt_stat struct to
+ * initialize.
+ */
+void ecryptfs_init_crypt_stat(struct ecryptfs_crypt_stat *crypt_stat)
+{
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ memset((void *)crypt_stat, 0, sizeof(struct ecryptfs_crypt_stat));
+ mutex_init(&crypt_stat->cs_mutex);
+ ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_STRUCT_INITIALIZED);
+ ecryptfs_printk(KERN_DEBUG, "Exit\n");
+}
+
+/**
+ * Releases all memory associated with a crypt_stat struct.
+ */
+void ecryptfs_destruct_crypt_stat(struct ecryptfs_crypt_stat *crypt_stat)
+{
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ if (crypt_stat->tfm)
+ crypto_free_tfm(crypt_stat->tfm);
+ if (crypt_stat->md5_tfm)
+ crypto_free_tfm(crypt_stat->md5_tfm);
+ memset(crypt_stat, 0, sizeof(struct ecryptfs_crypt_stat));
+ ecryptfs_printk(KERN_DEBUG, "Exit\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; set to NULL to obtain only
+ * the number of scatterlist structs required in 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);
+ if (sg) {
+ sg[i].page = pg;
+ sg[i].offset = offset;
+ }
+ remainder_of_page = PAGE_CACHE_SIZE - offset;
+ if (size >= remainder_of_page) {
+ if (sg)
+ sg[i].length = remainder_of_page;
+ addr += remainder_of_page;
+ size -= remainder_of_page;
+ } else {
+ if (sg)
+ sg[i].length = size;
+ addr += size;
+ size = 0;
+ }
+ i++;
+ }
+ if (size > 0)
+ return -ENOMEM;
+ return i;
+}
+
+/**
+ * @return Number of bytes encrypted; negative value on error
+ */
+static int encrypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat,
+ struct scatterlist *dest_sg,
+ struct scatterlist *src_sg, int size,
+ unsigned char *iv)
+{
+ int rc = 0;
+
+ ASSERT(crypt_stat && crypt_stat->tfm
+ && ECRYPTFS_CHECK_FLAG(crypt_stat->flags,
+ ECRYPTFS_STRUCT_INITIALIZED));
+ if (unlikely(ecryptfs_verbosity > 0)) {
+ ecryptfs_printk(KERN_DEBUG, "Key size [%d]; key:\n",
+ crypt_stat->key_size_bits / 8);
+ ecryptfs_dump_hex(crypt_stat->key,
+ crypt_stat->key_size_bits / 8);
+ }
+ /* Consider doing this once, when the file is opened */
+ rc = crypto_cipher_setkey(crypt_stat->tfm, crypt_stat->key,
+ crypt_stat->key_size_bits / 8);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error setting key; rc = [%d]\n",
+ rc);
+ rc = -EINVAL;
+ goto out;
+ }
+ ecryptfs_printk(KERN_DEBUG, "Encrypting [%d] bytes.\n", size);
+ crypto_cipher_encrypt_iv(crypt_stat->tfm, dest_sg, src_sg, size, iv);
+out:
+ return rc;
+}
+
+void
+ecryptfs_extent_to_lwr_pg_idx_and_offset(unsigned long *lower_page_idx,
+ int *byte_offset,
+ struct ecryptfs_crypt_stat *crypt_stat,
+ unsigned long extent_num)
+{
+ unsigned long lower_extent_num;
+ int extents_occupied_by_headers_at_front;
+ int bytes_occupied_by_headers_at_front;
+ int extent_offset;
+ int extents_per_page;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; extent_num = [0x%.16x]\n",
+ extent_num);
+ bytes_occupied_by_headers_at_front =
+ ( crypt_stat->header_extent_size
+ * crypt_stat->num_header_extents_at_front );
+ extents_occupied_by_headers_at_front =
+ ( bytes_occupied_by_headers_at_front
+ / crypt_stat->extent_size );
+ lower_extent_num = extents_occupied_by_headers_at_front + extent_num;
+ extents_per_page = PAGE_CACHE_SIZE / crypt_stat->extent_size;
+ (*lower_page_idx) = lower_extent_num / extents_per_page;
+ extent_offset = lower_extent_num % extents_per_page;
+ (*byte_offset) = extent_offset * crypt_stat->extent_size;
+ ecryptfs_printk(KERN_DEBUG, " * crypt_stat->header_extent_size = "
+ "[%d]\n", crypt_stat->header_extent_size);
+ ecryptfs_printk(KERN_DEBUG, " * crypt_stat->"
+ "num_header_extents_at_front = [%d]\n",
+ crypt_stat->num_header_extents_at_front);
+ ecryptfs_printk(KERN_DEBUG, " * extents_occupied_by_headers_at_"
+ "front = [%d]\n", extents_occupied_by_headers_at_front);
+ ecryptfs_printk(KERN_DEBUG, " * lower_extent_num = [0x%.16x]\n",
+ lower_extent_num);
+ ecryptfs_printk(KERN_DEBUG, " * extents_per_page = [%d]\n",
+ extents_per_page);
+ ecryptfs_printk(KERN_DEBUG, " * (*lower_page_idx) = [0x%.16x]\n",
+ (*lower_page_idx));
+ ecryptfs_printk(KERN_DEBUG, " * extent_offset = [%d]\n",
+ extent_offset);
+ ecryptfs_printk(KERN_DEBUG, " * (*byte_offset) = [%d]\n",
+ (*byte_offset));
+}
+
+int ecryptfs_write_out_page(struct ecryptfs_page_crypt_context *ctx,
+ struct page *lower_page, struct inode *lower_inode,
+ int byte_offset_in_page, int bytes_to_write)
+{
+ int rc = 0;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; ctx->mode = [%d], "
+ "lower_page->index = [0x%.16x], byte_offset_in_page = "
+ "[%d], bytes_to_write = [%d]\n", ctx->mode,
+ lower_page->index, byte_offset_in_page, bytes_to_write);
+ if (ctx->mode == ECRYPTFS_PREPARE_COMMIT_MODE) {
+ rc = ecryptfs_commit_lower_page(lower_page, lower_inode,
+ ctx->param.lower_file,
+ byte_offset_in_page,
+ bytes_to_write);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error calling lower "
+ "commit; rc = [%d]\n", rc);
+ goto out;
+ }
+ } else {
+ rc = ecryptfs_writepage_and_release_lower_page(lower_page,
+ lower_inode,
+ ctx->param.wbc);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error calling lower "
+ "writepage(); rc = [%d]\n", rc);
+ goto out;
+ }
+ }
+out:
+ return rc;
+}
+
+int ecryptfs_read_in_page(struct ecryptfs_page_crypt_context *ctx,
+ struct page **lower_page, struct inode *lower_inode,
+ unsigned long lower_page_idx, int byte_offset_in_page)
+{
+ int rc = 0;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; ctx->mode = [%d], "
+ "lower_page_idx = [0x%.16x], byte_offset_in_page = "
+ "[%d]\n", ctx->mode, lower_page_idx,
+ byte_offset_in_page);
+ if (ctx->mode == ECRYPTFS_PREPARE_COMMIT_MODE) {
+ /* TODO: Limit this to only the data extents that are
+ * needed */
+ rc = ecryptfs_get_lower_page(lower_page, lower_inode,
+ ctx->param.lower_file,
+ lower_page_idx,
+ byte_offset_in_page,
+ (PAGE_CACHE_SIZE
+ - byte_offset_in_page));
+ if (rc) {
+ ecryptfs_printk(
+ KERN_ERR, "Error attempting to grab, map, "
+ "and prepare_write lower page with index "
+ "[0x%.16x]; rc = [%d]\n", lower_page_idx, rc);
+ goto out;
+ }
+ } else {
+ rc = ecryptfs_grab_and_map_lower_page(lower_page, NULL,
+ lower_inode,
+ lower_page_idx);
+ if (rc) {
+ ecryptfs_printk(
+ KERN_ERR, "Error attempting to grab and map "
+ "lower page with index [0x%.16x]; rc = [%d]\n",
+ lower_page_idx, rc);
+ goto out;
+ }
+ }
+out:
+ return rc;
+}
+
+/**
+ * Encrypt an eCryptfs page. This is done on a per-extent basis. Note
+ * that eCryptfs pages may straddle the lower pages -- for instance,
+ * if the file was created on a machine with an 8K page size
+ * (resulting in an 8K header), and then the file is copied onto a
+ * host with a 32K page size, then when reading page 0 of the eCryptfs
+ * file, 24K of page 0 of the lower file will be read and decrypted,
+ * and then 8K of page 1 of the lower file will be read and decrypted.
+ *
+ * The actual operations performed on each page depends on the
+ * contents of the ecryptfs_page_crypt_context struct.
+ *
+ * @return Zero on success; negative on error
+ */
+int ecryptfs_encrypt_page(struct ecryptfs_page_crypt_context *ctx)
+{
+ char extent_iv[ECRYPTFS_MAX_IV_BYTES];
+ unsigned long base_extent;
+ unsigned long extent_offset = 0;
+ unsigned long lower_page_idx = 0;
+ unsigned long prior_lower_page_idx = 0;
+ struct page *lower_page;
+ struct inode *lower_inode;
+ struct ecryptfs_crypt_stat *crypt_stat;
+ int rc = 0;
+ int lower_byte_offset = 0;
+ int orig_byte_offset = 0;
+ int num_extents_per_page;
+#define ECRYPTFS_PAGE_STATE_UNREAD 0
+#define ECRYPTFS_PAGE_STATE_READ 1
+#define ECRYPTFS_PAGE_STATE_MODIFIED 2
+#define ECRYPTFS_PAGE_STATE_WRITTEN 3
+ int page_state;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; ctx->page->index = [0x%.16x]\n",
+ ctx->page->index);
+ crypt_stat = &(ECRYPTFS_INODE_TO_PRIVATE(
+ ctx->page->mapping->host)->crypt_stat);
+ lower_inode = ECRYPTFS_INODE_TO_LOWER(ctx->page->mapping->host);
+ if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED)) {
+ rc = ecryptfs_copy_page_to_lower(ctx->page, lower_inode,
+ ctx->param.lower_file);
+ if (rc)
+ ecryptfs_printk(KERN_ERR, "Error attempting to copy "
+ "page at index [0x%.16x]\n",
+ ctx->page->index);
+ goto out;
+ }
+ num_extents_per_page = PAGE_CACHE_SIZE / crypt_stat->extent_size;
+ base_extent = (ctx->page->index * num_extents_per_page);
+ page_state = ECRYPTFS_PAGE_STATE_UNREAD;
+ while (extent_offset < num_extents_per_page) {
+ ecryptfs_extent_to_lwr_pg_idx_and_offset(
+ &lower_page_idx, &lower_byte_offset, crypt_stat,
+ (base_extent + extent_offset));
+ if (prior_lower_page_idx != lower_page_idx
+ && page_state == ECRYPTFS_PAGE_STATE_MODIFIED) {
+ rc = ecryptfs_write_out_page(ctx, lower_page,
+ lower_inode,
+ orig_byte_offset,
+ (PAGE_CACHE_SIZE
+ - orig_byte_offset));
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error attempting "
+ "to write out page; rc = [%d]"
+ "\n", rc);
+ goto out;
+ }
+ page_state = ECRYPTFS_PAGE_STATE_WRITTEN;
+ }
+ if (page_state == ECRYPTFS_PAGE_STATE_UNREAD
+ || page_state == ECRYPTFS_PAGE_STATE_WRITTEN) {
+ rc = ecryptfs_read_in_page(ctx, &lower_page,
+ lower_inode, lower_page_idx,
+ lower_byte_offset);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error attempting "
+ "to read in lower page with "
+ "index [0x%.16x]; rc = [%d]\n",
+ lower_page_idx, rc);
+ goto out;
+ }
+ orig_byte_offset = lower_byte_offset;
+ prior_lower_page_idx = lower_page_idx;
+ page_state = ECRYPTFS_PAGE_STATE_READ;
+ }
+ ASSERT(page_state == ECRYPTFS_PAGE_STATE_MODIFIED
+ || page_state == ECRYPTFS_PAGE_STATE_READ);
+ rc = ecryptfs_derive_iv(extent_iv, crypt_stat,
+ (base_extent + extent_offset));
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error attempting to "
+ "derive IV for extent [0x%.16x]; "
+ "rc = [%d]\n",
+ (base_extent + extent_offset), rc);
+ goto out;
+ }
+ if (unlikely(ecryptfs_verbosity > 0)) {
+ ecryptfs_printk(KERN_DEBUG, "Encrypting extent "
+ "with iv:\n");
+ ecryptfs_dump_hex(extent_iv, crypt_stat->iv_bytes);
+ ecryptfs_printk(KERN_DEBUG, "First 8 bytes before "
+ "encryption:\n");
+ ecryptfs_dump_hex((char *)
+ (page_address(ctx->page)
+ + (extent_offset
+ * crypt_stat->extent_size)), 8);
+ }
+ rc = ecryptfs_encrypt_page_offset(
+ crypt_stat, lower_page, lower_byte_offset, ctx->page,
+ (extent_offset * crypt_stat->extent_size),
+ crypt_stat->extent_size, extent_iv);
+ ecryptfs_printk(KERN_DEBUG, "Encrypt extent [0x%.16x]; "
+ "rc = [%d]\n",
+ (base_extent + extent_offset), rc);
+ if (unlikely(ecryptfs_verbosity > 0)) {
+ ecryptfs_printk(KERN_DEBUG, "First 8 bytes after "
+ "encryption:\n");
+ ecryptfs_dump_hex((char *)(page_address(lower_page)
+ + lower_byte_offset), 8);
+ }
+ page_state = ECRYPTFS_PAGE_STATE_MODIFIED;
+ extent_offset++;
+ }
+ ASSERT(orig_byte_offset == 0);
+ rc = ecryptfs_write_out_page(ctx, lower_page, lower_inode, 0,
+ (lower_byte_offset
+ + crypt_stat->extent_size));
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error attempting to write out "
+ "page; rc = [%d]\n", rc);
+ goto out;
+ }
+out:
+ return rc;
+}
+
+/**
+ * Decrypt an eCryptfs page. This is done on a per-extent basis. Note
+ * that eCryptfs pages may straddle the lower pages -- for instance,
+ * if the file was created on a machine with an 8K page size
+ * (resulting in an 8K header), and then the file is copied onto a
+ * host with a 32K page size, then when reading page 0 of the eCryptfs
+ * file, 24K of page 0 of the lower file will be read and decrypted,
+ * and then 8K of page 1 of the lower file will be read and decrypted.
+ *
+ * @return Zero on success; negative on error
+ */
+int ecryptfs_decrypt_page(struct file *file, struct page *page)
+{
+ char extent_iv[ECRYPTFS_MAX_IV_BYTES];
+ unsigned long base_extent;
+ unsigned long extent_offset = 0;
+ unsigned long lower_page_idx = 0;
+ unsigned long prior_lower_page_idx = 0;
+ struct page *lower_page;
+ char *lower_page_virt = NULL;
+ struct inode *lower_inode;
+ struct ecryptfs_crypt_stat *crypt_stat;
+ int rc = 0;
+ int byte_offset;
+ int num_extents_per_page;
+ int page_state;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; page->index = [0x%.16x]\n",
+ page->index);
+ crypt_stat = &(ECRYPTFS_INODE_TO_PRIVATE(
+ page->mapping->host)->crypt_stat);
+ lower_inode = ECRYPTFS_INODE_TO_LOWER(page->mapping->host);
+ if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED)) {
+ rc = ecryptfs_do_readpage(file, page, page->index);
+ if (rc)
+ ecryptfs_printk(KERN_ERR, "Error attempting to copy "
+ "page at index [0x%.16x]\n",
+ page->index);
+ goto out;
+ }
+ num_extents_per_page = PAGE_CACHE_SIZE / crypt_stat->extent_size;
+ base_extent = (page->index * num_extents_per_page);
+ lower_page_virt = kmem_cache_alloc(ecryptfs_lower_page_cache,
+ SLAB_KERNEL);
+ if (!lower_page_virt) {
+ rc = -ENOMEM;
+ ecryptfs_printk(KERN_ERR, "Error getting page for encrypted "
+ "lower page(s)\n");
+ goto out;
+ }
+ lower_page = virt_to_page(lower_page_virt);
+ page_state = ECRYPTFS_PAGE_STATE_UNREAD;
+ while (extent_offset < num_extents_per_page) {
+ ecryptfs_extent_to_lwr_pg_idx_and_offset(
+ &lower_page_idx, &byte_offset, crypt_stat,
+ (base_extent + extent_offset));
+ if (prior_lower_page_idx != lower_page_idx
+ || page_state == ECRYPTFS_PAGE_STATE_UNREAD) {
+ rc = ecryptfs_do_readpage(file, lower_page,
+ lower_page_idx);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error reading "
+ "lower encrypted page; rc = "
+ "[%d]\n", rc);
+ goto out;
+ }
+ prior_lower_page_idx = lower_page_idx;
+ page_state = ECRYPTFS_PAGE_STATE_READ;
+ }
+ rc = ecryptfs_derive_iv(extent_iv, crypt_stat,
+ (base_extent + extent_offset));
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error attempting to "
+ "derive IV for extent [0x%.16x]; rc = "
+ "[%d]\n",
+ (base_extent + extent_offset), rc);
+ goto out;
+ }
+ if (unlikely(ecryptfs_verbosity > 0)) {
+ ecryptfs_printk(KERN_DEBUG, "Decrypting extent "
+ "with iv:\n");
+ ecryptfs_dump_hex(extent_iv, crypt_stat->iv_bytes);
+ ecryptfs_printk(KERN_DEBUG, "First 8 bytes before "
+ "decryption:\n");
+ ecryptfs_dump_hex((lower_page_virt + byte_offset), 8);
+ }
+ rc = ecryptfs_decrypt_page_offset(crypt_stat, page,
+ (extent_offset
+ * crypt_stat->extent_size),
+ lower_page, byte_offset,
+ crypt_stat->extent_size,
+ extent_iv);
+ if (rc != crypt_stat->extent_size) {
+ ecryptfs_printk(KERN_ERR, "Error attempting to "
+ "decrypt extent [0x%.16x]\n",
+ (base_extent + extent_offset));
+ goto out;
+ }
+ rc = 0;
+ if (unlikely(ecryptfs_verbosity > 0)) {
+ ecryptfs_printk(KERN_DEBUG, "First 8 bytes after "
+ "decryption:\n");
+ ecryptfs_dump_hex((char *)(page_address(page)
+ + byte_offset), 8);
+ }
+ extent_offset++;
+ }
+out:
+ if (lower_page_virt)
+ kmem_cache_free(ecryptfs_lower_page_cache, lower_page_virt);
+ return rc;
+}
+
+/**
+ * @return Number of bytes decrypted; negative value on error
+ */
+static int decrypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat,
+ struct scatterlist *dest_sg,
+ struct scatterlist *src_sg, int size,
+ unsigned char *iv)
+{
+ int rc = 0;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ ecryptfs_printk(KERN_DEBUG, "Key size [%d]; key:\n",
+ crypt_stat->key_size_bits / 8);
+ if (unlikely(ecryptfs_verbosity > 0))
+ ecryptfs_dump_hex(crypt_stat->key,
+ crypt_stat->key_size_bits / 8);
+ /* Consider doing this once, when the file is opened */
+ rc = crypto_cipher_setkey(crypt_stat->tfm, crypt_stat->key,
+ crypt_stat->key_size_bits / 8);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error setting key; rc = [%d]\n",
+ rc);
+ rc = -EINVAL;
+ goto out;
+ }
+ ecryptfs_printk(KERN_DEBUG, "Decrypting [%d] bytes.\n", size);
+ rc = crypto_cipher_decrypt_iv(crypt_stat->tfm, dest_sg, src_sg, size,
+ iv);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error decrypting; rc = [%d]\n",
+ rc);
+ goto out;
+ }
+ rc = size;
+out:
+ ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * @return Number of bytes encrypted
+ */
+int
+ecryptfs_encrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat,
+ struct page *dst_page, int dst_offset,
+ struct page *src_page, int src_offset, int size,
+ unsigned char *iv)
+{
+ struct scatterlist src_sg, dst_sg;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; dst_page->index = [0x%.16x], "
+ "dst_offset = [%d], src_page->index = [0x%.16x], "
+ "src_offset = [%d]\n", dst_page->index, dst_offset,
+ src_page->index, src_offset);
+ src_sg.page = src_page;
+ src_sg.offset = src_offset;
+ src_sg.length = size;
+ dst_sg.page = dst_page;
+ dst_sg.offset = dst_offset;
+ dst_sg.length = size;
+ return encrypt_scatterlist(crypt_stat, &dst_sg, &src_sg, size, iv);
+}
+
+/**
+ * @return Number of bytes decrypted
+ */
+int
+ecryptfs_decrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat,
+ struct page *dst_page, int dst_offset,
+ struct page *src_page, int src_offset, int size,
+ unsigned char *iv)
+{
+ struct scatterlist src_sg, dst_sg;
+
+ ecryptfs_printk(KERN_DEBUG, "Called with dest_page->index = [0x%.16x], "
+ "dst_offset = [%d], src_page->index = [0x%.16x], "
+ "src_offset = [%d]\n", dst_page->index, dst_offset,
+ src_page->index, src_offset);
+ src_sg.page = src_page;
+ src_sg.offset = src_offset;
+ src_sg.length = size;
+ dst_sg.page = dst_page;
+ dst_sg.offset = dst_offset;
+ dst_sg.length = size;
+ return decrypt_scatterlist(crypt_stat, &dst_sg, &src_sg, size, iv);
+}
+
+#define ECRYPTFS_MAX_SCATTERLIST_LEN 4
+
+/**
+ * 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_stat *crypt_stat)
+{
+ int rc = -EINVAL;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ if (crypt_stat->cipher == NULL) {
+ ecryptfs_printk(KERN_ERR, "No cipher specified\n");
+ goto out;
+ }
+ ecryptfs_printk(KERN_DEBUG,
+ "Initializing cipher [%s]; strlen = [%d]\n",
+ crypt_stat->cipher, (int)strlen(crypt_stat->cipher));
+ if (crypt_stat->tfm) {
+ rc = 0;
+ goto out;
+ }
+ crypt_stat->tfm = crypto_alloc_tfm(crypt_stat->cipher,
+ ECRYPTFS_DEFAULT_CHAINING_MODE);
+ if (crypt_stat->tfm == NULL) {
+ ecryptfs_printk(KERN_ERR, "cryptfs: init_crypt_ctx(): Error "
+ "initializing cipher [%s]\n",
+ crypt_stat->cipher);
+ goto out;
+ }
+ rc = 0;
+out:
+ ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+static void set_extent_mask_and_shift(struct ecryptfs_crypt_stat *crypt_stat)
+{
+ int extent_size_tmp;
+
+ crypt_stat->extent_mask = 0xFFFFFFFF;
+ crypt_stat->extent_shift = 0;
+ if (crypt_stat->extent_size == 0)
+ return;
+ extent_size_tmp = crypt_stat->extent_size;
+ while ((extent_size_tmp & 0x01) == 0) {
+ extent_size_tmp >>= 1;
+ crypt_stat->extent_mask <<= 1;
+ crypt_stat->extent_shift++;
+ }
+}
+
+void ecryptfs_set_default_sizes(struct ecryptfs_crypt_stat *crypt_stat)
+{
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ /* Default values; may be overwritten as we are parsing the
+ * packets. */
+ crypt_stat->extent_size = ECRYPTFS_DEFAULT_EXTENT_SIZE;
+ set_extent_mask_and_shift(crypt_stat);
+ crypt_stat->iv_bytes = ECRYPTFS_DEFAULT_IV_BYTES;
+ if (PAGE_CACHE_SIZE <= ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE) {
+ crypt_stat->header_extent_size =
+ ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE;
+ } else
+ crypt_stat->header_extent_size = PAGE_CACHE_SIZE;
+ crypt_stat->num_header_extents_at_front = 1;
+ ecryptfs_printk(KERN_DEBUG, "Exit\n");
+}
+
+/**
+ * On error, sets the root IV to all 0's.
+ */
+int ecryptfs_compute_root_iv(struct ecryptfs_crypt_stat *crypt_stat)
+{
+ int rc = 0;
+ char dst[MD5_DIGEST_SIZE];
+
+ ASSERT(crypt_stat->iv_bytes <= MD5_DIGEST_SIZE);
+ ASSERT(crypt_stat->iv_bytes > 0);
+ if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_KEY_VALID)) {
+ rc = -EINVAL;
+ ecryptfs_printk(KERN_WARNING, "Session key not valid; "
+ "cannot generate root IV\n");
+ goto out;
+ }
+ rc = ecryptfs_calculate_md5(dst, crypt_stat, crypt_stat->key,
+ (crypt_stat->key_size_bits / 8));
+ if (rc) {
+ ecryptfs_printk(KERN_WARNING, "Error attempting to compute "
+ "MD5 while generating root IV\n");
+ goto out;
+ }
+ memcpy(crypt_stat->root_iv, dst, crypt_stat->iv_bytes);
+out:
+ if (rc) {
+ memset(crypt_stat->root_iv, 0, crypt_stat->iv_bytes);
+ ECRYPTFS_SET_FLAG(crypt_stat->flags,
+ ECRYPTFS_SECURITY_WARNING);
+ }
+ return rc;
+}
+
+/**
+ * Default values in the event that policy does not override them.
+ */
+static void
+ecryptfs_set_default_crypt_stat_vals(struct ecryptfs_crypt_stat *crypt_stat)
+{
+ int key_size_bits = ECRYPTFS_DEFAULT_KEY_BYTES * 8;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ ecryptfs_set_default_sizes(crypt_stat);
+ strcpy(crypt_stat->cipher, ECRYPTFS_DEFAULT_CIPHER);
+ crypt_stat->key_size_bits = key_size_bits;
+ get_random_bytes(crypt_stat->key, key_size_bits / 8);
+ ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_KEY_VALID);
+ ecryptfs_compute_root_iv(crypt_stat);
+ if (unlikely(ecryptfs_verbosity > 0)) {
+ ecryptfs_printk(KERN_DEBUG, "Generated new session key:\n");
+ ecryptfs_dump_hex(crypt_stat->key,
+ crypt_stat->key_size_bits / 8);
+ }
+ crypt_stat->file_version = ECRYPTFS_FILE_VERSION;
+ ecryptfs_printk(KERN_DEBUG, "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_stat 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_stat *crypt_stat =
+ &ECRYPTFS_INODE_TO_PRIVATE(ecryptfs_dentry->d_inode)->crypt_stat;
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat =
+ &(ECRYPTFS_SUPERBLOCK_TO_PRIVATE(
+ ecryptfs_dentry->d_sb)->mount_crypt_stat);
+ int cipher_name_len;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ ecryptfs_set_default_crypt_stat_vals(crypt_stat);
+ /* See if there are mount crypt options */
+ if (mount_crypt_stat->global_auth_tok) {
+ ecryptfs_printk(KERN_DEBUG, "Initializing context for new "
+ "file using mount_crypt_stat\n");
+ ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED);
+ ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_KEY_VALID);
+ memcpy(crypt_stat->keysigs[crypt_stat->num_keysigs++],
+ mount_crypt_stat->global_auth_tok_sig,
+ ECRYPTFS_SIG_SIZE_HEX);
+ cipher_name_len =
+ strlen(mount_crypt_stat->global_default_cipher_name);
+ memcpy(crypt_stat->cipher,
+ mount_crypt_stat->global_default_cipher_name,
+ cipher_name_len);
+ crypt_stat->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; remove in future
+ * release */
+ BUG();
+ rc = ecryptfs_init_crypt_ctx(crypt_stat);
+ if (rc)
+ ecryptfs_printk(KERN_ERR, "Error initializing cryptographic "
+ "context for cipher [%s]: rc = [%d]\n",
+ crypt_stat->cipher, rc);
+ ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * @return One if marker found; zero if not found
+ */
+int contains_ecryptfs_marker(char *data)
+{
+ u32 m_1, m_2;
+
+ memcpy(&m_1, data, 4);
+ m_1 = be32_to_cpu(m_1);
+ memcpy(&m_2, (data + 4), 4);
+ m_2 = be32_to_cpu(m_2);
+ if ((m_1 ^ MAGIC_ECRYPTFS_MARKER) == m_2)
+ return 1;
+ ecryptfs_printk(KERN_DEBUG, "m_1 = [0x%.8x]; m_2 = [0x%.8x]; "
+ "MAGIC_ECRYPTFS_MARKER = [0x%.8x]\n", m_1, m_2,
+ MAGIC_ECRYPTFS_MARKER);
+ ecryptfs_printk(KERN_DEBUG, "(m_1 ^ MAGIC_ECRYPTFS_MARKER) = "
+ "[0x%.8x]\n", (m_1 ^ MAGIC_ECRYPTFS_MARKER));
+ return 0;
+}
+
+struct ecryptfs_flag_map_elem {
+ u32 file_flag;
+ u32 local_flag;
+};
+
+/* Add support for additional flags by adding elements here. */
+static struct ecryptfs_flag_map_elem ecryptfs_flag_map[] = {
+ {0x00000001, ECRYPTFS_ENABLE_HMAC},
+ {0x00000002, ECRYPTFS_ENCRYPTED}
+};
+
+/**
+ * @return Zero on success; non-zero if the flag set is invalid
+ */
+static int ecryptfs_process_flags(struct ecryptfs_crypt_stat *crypt_stat,
+ char *page_virt, int *bytes_read)
+{
+ int rc = 0;
+ int i;
+ u32 flags;
+
+ memcpy(&flags, page_virt, 4);
+ flags = be32_to_cpu(flags);
+ for (i = 0; i < ((sizeof(ecryptfs_flag_map)
+ / sizeof(struct ecryptfs_flag_map_elem))); i++)
+ if (flags & ecryptfs_flag_map[i].file_flag) {
+ ECRYPTFS_SET_FLAG(crypt_stat->flags,
+ ecryptfs_flag_map[i].local_flag);
+ } else
+ ECRYPTFS_CLEAR_FLAG(crypt_stat->flags,
+ ecryptfs_flag_map[i].local_flag);
+ /* Version is in top 8 bits of the 32-bit flag vector */
+ crypt_stat->file_version = ((flags >> 24) & 0xFF);
+ (*bytes_read) = 4;
+ return rc;
+}
+
+/**
+ * Marker = 0x3c81b7f5
+ */
+static void write_ecryptfs_marker(char *page_virt, int *written)
+{
+ u32 m_1, m_2;
+
+ get_random_bytes(&m_1, (MAGIC_ECRYPTFS_MARKER_SIZE_BYTES / 2));
+ m_2 = (m_1 ^ MAGIC_ECRYPTFS_MARKER);
+ m_1 = cpu_to_be32(m_1);
+ memcpy(page_virt, &m_1, (MAGIC_ECRYPTFS_MARKER_SIZE_BYTES / 2));
+ m_2 = cpu_to_be32(m_2);
+ memcpy(page_virt + (MAGIC_ECRYPTFS_MARKER_SIZE_BYTES / 2), &m_2,
+ (MAGIC_ECRYPTFS_MARKER_SIZE_BYTES / 2));
+ (*written) = MAGIC_ECRYPTFS_MARKER_SIZE_BYTES;
+}
+
+static void
+write_ecryptfs_flags(char *page_virt, struct ecryptfs_crypt_stat *crypt_stat,
+ int *written)
+{
+ u32 flags = 0;
+ int i;
+
+ for (i = 0; i < ((sizeof(ecryptfs_flag_map)
+ / sizeof(struct ecryptfs_flag_map_elem))); i++)
+ if (ECRYPTFS_CHECK_FLAG(crypt_stat->flags,
+ ecryptfs_flag_map[i].local_flag))
+ flags |= ecryptfs_flag_map[i].file_flag;
+ /* Version is in top 8 bits of the 32-bit flag vector */
+ flags |= ((((u8)crypt_stat->file_version) << 24) & 0xFF000000);
+ flags = cpu_to_be32(flags);
+ memcpy(page_virt, &flags, 4);
+ (*written) = 4;
+}
+
+struct ecryptfs_cipher_code_str_map_elem {
+ char cipher_str[16];
+ u16 cipher_code;
+};
+
+/* Add support for additional ciphers by adding elements here. The
+ * cipher_code is whatever OpenPGP applicatoins use to identify the
+ * ciphers. */
+/* List in order of probability. */
+static struct ecryptfs_cipher_code_str_map_elem
+ecryptfs_cipher_code_str_map[] = {
+ {"aes", 0x07},
+ {"blowfish", 0x04},
+ {"des3_ede", 0x02},
+ {"cast5", 0x03}
+};
+
+/**
+ * @return Zero on no match
+ */
+u16 ecryptfs_code_for_cipher_string(char *str)
+{
+ int i;
+
+ for (i = 0; i < (sizeof(ecryptfs_cipher_code_str_map)
+ / sizeof(struct ecryptfs_cipher_code_str_map_elem));
+ i++)
+ if (strcmp(str, ecryptfs_cipher_code_str_map[i].cipher_str)==0)
+ return ecryptfs_cipher_code_str_map[i].cipher_code;
+ return 0;
+}
+
+/**
+ * @return Zero on success
+ */
+int ecryptfs_cipher_code_to_string(char *str, u16 cipher_code)
+{
+ int rc = 0;
+ int i;
+
+ str[0] = '\0';
+ for (i = 0; i < (sizeof(ecryptfs_cipher_code_str_map)
+ / sizeof(struct ecryptfs_cipher_code_str_map_elem));
+ i++)
+ if (cipher_code == ecryptfs_cipher_code_str_map[i].cipher_code)
+ strcpy(str, ecryptfs_cipher_code_str_map[i].cipher_str);
+ if (str[0] == '\0') {
+ ecryptfs_printk(KERN_WARNING, "Cipher code not recognized: "
+ "[%d]\n", cipher_code);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+/**
+ * @return Zero on success; non-zero otherwise
+ */
+int ecryptfs_read_header_region(char *data, struct dentry *dentry,
+ struct nameidata *nd)
+{
+ int rc = 0;
+ struct vfsmount *mnt = NULL;
+ struct file *file = NULL;
+ mm_segment_t oldfs;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ mnt = mntget(nd->mnt);
+ file = dentry_open(dentry, mnt, O_RDONLY);
+ if (IS_ERR(file)) {
+ ecryptfs_printk(KERN_DEBUG, "Error opening file to "
+ "read header region\n");
+ mntput(mnt);
+ rc = PTR_ERR(file);
+ goto out;
+ }
+ file->f_pos = 0;
+ oldfs = get_fs();
+ set_fs(get_ds());
+ /* For releases 0.1 and 0.2, all of the header information
+ * fits in the first data extent-sized region. */
+ rc = file->f_op->read(file, (char __user *)data,
+ ECRYPTFS_DEFAULT_EXTENT_SIZE, &file->f_pos);
+ set_fs(oldfs);
+ fput(file);
+ rc = 0;
+out:
+ ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n",rc);
+ return rc;
+}
+
+static void
+write_header_metadata(char *virt, struct ecryptfs_crypt_stat *crypt_stat,
+ int *written)
+{
+ u32 header_extent_size;
+ u16 num_header_extents_at_front;
+
+ header_extent_size = (u32)crypt_stat->header_extent_size;
+ num_header_extents_at_front =
+ (u16)crypt_stat->num_header_extents_at_front;
+ header_extent_size = cpu_to_be32(header_extent_size);
+ memcpy(virt, &header_extent_size, 4);
+ virt += 4;
+ num_header_extents_at_front = cpu_to_be16(num_header_extents_at_front);
+ memcpy(virt, &num_header_extents_at_front, 2);
+ (*written) = 6;
+}
+
+kmem_cache_t *ecryptfs_header_cache_0;
+kmem_cache_t *ecryptfs_header_cache_1;
+kmem_cache_t *ecryptfs_header_cache_2;
+
+/**
+ * Format version: 1
+ *
+ * Header Extent:
+ * Octets 0-7: Unencrypted file size (big-endian)
+ * Octets 8-15: eCryptfs special marker
+ * Octets 16-19: Flags
+ * Octet 16: File format version number (between 0 and 255)
+ * Octets 17-18: Reserved
+ * Octet 19: Bit 1 (lsb): Reserved
+ * Bit 2: Encrypted?
+ * Bits 3-8: Reserved
+ * Octets 20-23: Header extent size (big-endian)
+ * Octets 24-25: Number of header extents at front of file
+ * (big-endian)
+ * Octet 26: Begin RFC 2440 authentication token packet set
+ * Data Extent 0:
+ * Lower data (CBC encrypted)
+ * Data Extent 1:
+ * Lower data (CBC encrypted)
+ * ...
+ *
+ * @return Zero on success
+ */
+int ecryptfs_write_headers_virt(char *page_virt,
+ struct ecryptfs_crypt_stat *crypt_stat,
+ struct dentry *ecryptfs_dentry)
+{
+ int rc;
+ int written;
+ int offset;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ offset = ECRYPTFS_FILE_SIZE_BYTES;
+ write_ecryptfs_marker((page_virt + offset), &written);
+ offset += written;
+ write_ecryptfs_flags((page_virt + offset), crypt_stat, &written);
+ offset += written;
+ write_header_metadata((page_virt + offset), crypt_stat, &written);
+ offset += written;
+ rc = ecryptfs_generate_key_packet_set((page_virt + offset), crypt_stat,
+ ecryptfs_dentry, &written);
+ if (rc)
+ ecryptfs_printk(KERN_WARNING, "Error generating key packet "
+ "set; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * Write the file headers out. This will likely involve a userspace
+ * callout, in which the session key is encrypted with one or more
+ * public keys and/or the passphrase necessary to do the encryption is
+ * retrieved via a prompt. Exactly what happens at this point should
+ * be policy-dependent.
+ *
+ * @param lower_file The lower file struct, which was returned from
+ * dentry_open
+ * @return Zero on success; non-zero on error
+ */
+int ecryptfs_write_headers(struct dentry *ecryptfs_dentry,
+ struct file *lower_file)
+{
+ mm_segment_t oldfs;
+ struct ecryptfs_crypt_stat *crypt_stat;
+ char *page_virt;
+ int current_header_page;
+ int header_pages;
+ int rc = 0;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ crypt_stat = &ECRYPTFS_INODE_TO_PRIVATE(
+ ecryptfs_dentry->d_inode)->crypt_stat;
+ if (likely(ECRYPTFS_CHECK_FLAG(crypt_stat->flags,
+ ECRYPTFS_ENCRYPTED))) {
+ if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags,
+ ECRYPTFS_KEY_VALID)) {
+ ecryptfs_printk(KERN_DEBUG, "Key is "
+ "invalid; bailing out\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ } else {
+ rc = -EINVAL;
+ ecryptfs_printk(KERN_WARNING,
+ "Called with crypt_stat->encrypted == 0\n");
+ goto out;
+ }
+ /* Released in this function */
+ page_virt = kmem_cache_alloc(ecryptfs_header_cache_0, SLAB_USER);
+ if (!page_virt) {
+ ecryptfs_printk(KERN_ERR, "Out of memory\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+ memset(page_virt, 0, PAGE_CACHE_SIZE);
+ rc = ecryptfs_write_headers_virt(page_virt, crypt_stat,
+ ecryptfs_dentry);
+ if (unlikely(rc)) {
+ ecryptfs_printk(KERN_ERR, "Error whilst writing headers\n");
+ memset(page_virt, 0, PAGE_CACHE_SIZE);
+ goto out_free;
+ }
+ ecryptfs_printk(KERN_DEBUG,
+ "Writing key packet set to underlying file\n");
+ lower_file->f_pos = 0;
+ oldfs = get_fs();
+ set_fs(get_ds());
+ ecryptfs_printk(KERN_DEBUG, "Calling lower_file->f_op->"
+ "write() w/ header page; lower_file->f_pos = "
+ "[0x%.16x]\n", lower_file->f_pos);
+ lower_file->f_op->write(lower_file, (char __user *)page_virt,
+ PAGE_CACHE_SIZE, &lower_file->f_pos);
+ header_pages = ((crypt_stat->header_extent_size
+ * crypt_stat->num_header_extents_at_front)
+ / PAGE_CACHE_SIZE);
+ memset(page_virt, 0, PAGE_CACHE_SIZE);
+ current_header_page = 1;
+ while (current_header_page < header_pages) {
+ ecryptfs_printk(KERN_DEBUG, "Calling lower_file->f_op->"
+ "write() w/ zero'd page; lower_file->f_pos = "
+ "[0x%.16x]\n", lower_file->f_pos);
+ lower_file->f_op->write(lower_file, (char __user *)page_virt,
+ PAGE_CACHE_SIZE, &lower_file->f_pos);
+ current_header_page++;
+ }
+ set_fs(oldfs);
+ ecryptfs_printk(KERN_DEBUG,
+ "Done writing key packet set to underlying file.\n");
+out_free:
+ kmem_cache_free(ecryptfs_header_cache_0, page_virt);
+out:
+ ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+static int parse_header_metadata(struct ecryptfs_crypt_stat *crypt_stat,
+ char *virt, int *bytes_read)
+{
+ int rc = 0;
+ u32 header_extent_size;
+ u16 num_header_extents_at_front;
+
+ memcpy(&header_extent_size, virt, 4);
+ header_extent_size = be32_to_cpu(header_extent_size);
+ virt += 4;
+ memcpy(&num_header_extents_at_front, virt, 2);
+ num_header_extents_at_front = be16_to_cpu(num_header_extents_at_front);
+ crypt_stat->header_extent_size = (int)header_extent_size;
+ crypt_stat->num_header_extents_at_front =
+ (int)num_header_extents_at_front;
+ (*bytes_read) = 6;
+ if (crypt_stat->header_extent_size
+ < ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE) {
+ rc = -EINVAL;
+ ecryptfs_printk(KERN_WARNING, "Invalid header extent size: "
+ "[%d]\n", crypt_stat->header_extent_size);
+ }
+ return rc;
+}
+
+/**
+ * For version 0 file format; this function is only for backwards
+ * compatibility for files created with the prior versions of
+ * eCryptfs.
+ */
+inline void set_default_header_data(struct ecryptfs_crypt_stat *crypt_stat)
+{
+ crypt_stat->header_extent_size = 4096;
+ crypt_stat->num_header_extents_at_front = 1;
+}
+
+/**
+ * Read/parse the header data. The header format is detailed in the
+ * comment block for the ecryptfs_write_headers_virt() function.
+ *
+ * @return Zero on success
+ */
+int ecryptfs_read_headers_virt(char *page_virt,
+ struct ecryptfs_crypt_stat *crypt_stat,
+ struct dentry *ecryptfs_dentry)
+{
+ int rc = 0;
+ int offset;
+ int bytes_read;
+
+ ecryptfs_printk(KERN_DEBUG, "Called\n");
+ ecryptfs_set_default_sizes(crypt_stat);
+ offset = ECRYPTFS_FILE_SIZE_BYTES;
+ rc = contains_ecryptfs_marker(page_virt + offset);
+ if (rc == 0) {
+ ecryptfs_printk(KERN_WARNING, "Valid eCryptfs marker not "
+ "found\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ offset += MAGIC_ECRYPTFS_MARKER_SIZE_BYTES;
+ rc = ecryptfs_process_flags(crypt_stat, (page_virt + offset),
+ &bytes_read);
+ if (rc) {
+ ecryptfs_printk(KERN_WARNING, "Error processing flags\n");
+ goto out;
+ }
+ if (crypt_stat->file_version > ECRYPTFS_SUPPORTED_FILE_VERSION) {
+ ecryptfs_printk(KERN_WARNING, "File version is [%d]; only "
+ "file version [%d] is supported by this "
+ "version of eCryptfs\n",
+ crypt_stat->file_version,
+ ECRYPTFS_SUPPORTED_FILE_VERSION);
+ rc = -EINVAL;
+ goto out;
+ }
+ offset += bytes_read;
+ if (crypt_stat->file_version >= 1) {
+ rc = parse_header_metadata(crypt_stat, (page_virt + offset),
+ &bytes_read);
+ if (rc) {
+ ecryptfs_printk(KERN_WARNING, "Error reading header "
+ "metadata; rc = [%d]\n", rc);
+ }
+ offset += bytes_read;
+ } else
+ set_default_header_data(crypt_stat);
+ rc = ecryptfs_parse_packet_set(crypt_stat, (page_virt + offset),
+ ecryptfs_dentry);
+out:
+ 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 = NULL;
+ mm_segment_t oldfs;
+ ssize_t bytes_read;
+ struct ecryptfs_crypt_stat *crypt_stat =
+ &ECRYPTFS_INODE_TO_PRIVATE(ecryptfs_dentry->d_inode)->crypt_stat;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter\n");
+ /* Read the first page from the underlying file */
+ page_virt = kmem_cache_alloc(ecryptfs_header_cache_1, SLAB_USER);
+ if (!page_virt) {
+ rc = -ENOMEM;
+ ecryptfs_printk(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,
+ ECRYPTFS_DEFAULT_EXTENT_SIZE,
+ &lower_file->f_pos);
+ set_fs(oldfs);
+ if (bytes_read != ECRYPTFS_DEFAULT_EXTENT_SIZE) {
+ ecryptfs_printk(KERN_ERR, "Expected size of header not read."
+ "Instead [%d] bytes were read\n", bytes_read);
+ rc = -EINVAL;
+ goto out;
+ }
+ rc = ecryptfs_read_headers_virt(page_virt, crypt_stat,
+ ecryptfs_dentry);
+ if (rc) {
+ ecryptfs_printk(KERN_DEBUG, "Valid eCryptfs headers not "
+ "found\n");
+ rc = -EINVAL;
+ }
+out:
+ if (page_virt) {
+ memset(page_virt, 0, PAGE_CACHE_SIZE);
+ kmem_cache_free(ecryptfs_header_cache_1, page_virt);
+ }
+ ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
+ return rc;
+}
+
+/**
+ * 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_stat->tfm.
+ *
+ * TODO: Implement filename decoding and decryption here, in place of
+ * memcpy. We are keeping the framework around for now to (1)
+ * facilitate testing of the components needed to implement filename
+ * encryption and (2) to provide a code base from which other
+ * developers in the community can easily implement this feature.
+ *
+ * @return Length of encoded filename; negative if error
+ */
+int
+ecryptfs_encode_filename(struct ecryptfs_crypt_stat *crypt_stat,
+ const char *name, int length, char **encoded_name)
+{
+ int error = 0;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; length = [%d]\n", length);
+ (*encoded_name) = kmalloc(length + 2, GFP_KERNEL);
+ if (!(*encoded_name)) {
+ error = -ENOMEM;
+ goto out;
+ }
+ /* TODO: Filename encryption is a scheduled feature for a
+ * future version of eCryptfs. This function is here only for
+ * the purpose of providing a framework for other developers
+ * to easily implement filename encryption. Hint: Replace this
+ * memcpy() with a call to encrypt and encode the
+ * filename, the set the length accordingly. */
+ memcpy((void *)(*encoded_name), (void *)name, length);
+ (*encoded_name)[length] = '\0';
+ error = length + 1;
+out:
+ ecryptfs_printk(KERN_DEBUG, "Exit; error = [%d]\n", error);
+ return error;
+}
+
+/**
+ * Decodes and decrypts the filename.
+ *
+ * We assume that we have a properly initialized crypto context,
+ * pointed to by crypt_stat->tfm.
+ *
+ * TODO: Implement filename decoding and decryption here, in place of
+ * memcpy. We are keeping the framework around for now to (1)
+ * facilitate testing of the components needed to implement filename
+ * encryption and (2) to provide a code base from which other
+ * developers in the community can easily implement this feature.
+ *
+ * @return Length of decoded filename; negative if error
+ */
+int
+ecryptfs_decode_filename(struct ecryptfs_crypt_stat *crypt_stat,
+ const char *name, int length, char **decrypted_name)
+{
+ int error = 0;
+
+ ecryptfs_printk(KERN_DEBUG, "Enter; length = [%d]\n", length);
+ (*decrypted_name) = kmalloc(length + 2, GFP_KERNEL);
+ if (!(*decrypted_name)) {
+ error = -ENOMEM;
+ goto out;
+ }
+ /* TODO: Filename encryption is a scheduled feature for a
+ * future version of eCryptfs. This function is here only for
+ * the purpose of providing a framework for other developers
+ * to easily implement filename encryption. Hint: Replace this
+ * memcpy() with a call to decode and decrypt the
+ * filename, the set the length accordingly. */
+ 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(KERN_DEBUG, "Exit; error = [%d]\n", error);
+ return error;
+}

2006-05-04 04:06:22

by Eric Dumazet

[permalink] [raw]
Subject: Re: [PATCH 8/13: eCryptfs] File operations

On Thursday 04 May 2006 05:39, Phillip Hellewell wrote:
> +struct file_operations ecryptfs_dir_fops = {
> + .readdir = ecryptfs_readdir,
> + .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,
> + .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,
> +};

Current kernel has support for const file_operations, so new file systems
should use a 'const' attribute for 'file_operation' declarations.
It helps avoiding false sharing on SMP.

Eric

2006-05-04 09:38:20

by Pavel Machek

[permalink] [raw]
Subject: Re: [PATCH 2/13: eCryptfs] Documentation

Hi!

> Index: linux-2.6.17-rc3-mm1-ecryptfs/Documentation/ecryptfs.txt
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ linux-2.6.17-rc3-mm1-ecryptfs/Documentation/ecryptfs.txt 2006-05-02 19:36:04.000000000 -0600
> @@ -0,0 +1,76 @@
> +
> +This software is currently undergoing development. Make sure to
> +maintain a backup copy of any data you write into eCryptfs.

Hehe, and encrypt backup copy with rot13? ;-).

> +The operation will complete. Notice that there is a new file in
> +/root/crypt that is 2 pages (8192 bytes) in size. This is the

Not 12K and or 8K+strlen('hello world')?

Pavel
--
Thanks, Sharp!

2006-05-04 09:38:44

by Pavel Machek

[permalink] [raw]
Subject: Re: [PATCH 0/12: eCryptfs] eCryptfs version 0.1.6

Hi!

> processing the 4096-byte extents. The second modification is that the
> header region occupies either 8192 bytes or the page size of the host
> on which the file is created, whichever is larger. This maximizes the
> probability that pages will be aligned between the unencrypted and
> encrypted data, which is not a requirement, but it helps with
> performance.

Does that mean that 10-bytes file now occupies 12KB disk space in
common case of 4K filesystem?
Pavel
--
Thanks, Sharp!

2006-05-04 09:56:12

by Pavel Machek

[permalink] [raw]
Subject: Re: [PATCH 6/13: eCryptfs] Superblock operations

HI!

> +/**
> + * Get the filesystem statistics. Currently, we let this pass right through
> + * to the lower filesystem and take no action ourselves.
> + */
> +static int ecryptfs_statfs(struct super_block *sb, struct kstatfs *buf)
> +{
> + int rc = 0;
> +
> + ecryptfs_printk(KERN_DEBUG, "Enter\n");
> + rc = vfs_statfs(ECRYPTFS_SUPERBLOCK_TO_LOWER(sb), buf);
> + ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
> + return rc;
> +}

This is ugly. Could you remove the debugging, or at least use dprintk?


--
Thanks, Sharp!

2006-05-04 12:13:42

by Michael Halcrow

[permalink] [raw]
Subject: Re: [PATCH 0/12: eCryptfs] eCryptfs version 0.1.6

On Thu, May 04, 2006 at 07:28:49AM +0000, Pavel Machek wrote:
> > processing the 4096-byte extents. The second modification is that
> > the header region occupies either 8192 bytes or the page size of
> > the host on which the file is created, whichever is larger. This
> > maximizes the probability that pages will be aligned between the
> > unencrypted and encrypted data, which is not a requirement, but it
> > helps with performance.
>
> Does that mean that 10-bytes file now occupies 12KB disk space in
> common case of 4K filesystem?

Yes.

Mike

2006-05-04 12:17:21

by Michael Halcrow

[permalink] [raw]
Subject: Re: [PATCH 2/13: eCryptfs] Documentation

On Thu, May 04, 2006 at 07:32:22AM +0000, Pavel Machek wrote:
> > +The operation will complete. Notice that there is a new file in
> > +/root/crypt that is 2 pages (8192 bytes) in size. This is the
>
> Not 12K and or 8K+strlen('hello world')?

We missed this update in the documentation when we made the recent
change to the header; it should read:

---
The operation will complete. Notice that there is a new file in
/root/crypt that is at least 12288 bytes in size (depending on your
host page 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:
---

Mike

2006-05-04 14:02:51

by Michael Thompson

[permalink] [raw]
Subject: Re: [PATCH 6/13: eCryptfs] Superblock operations

On 5/4/06, Pavel Machek <[email protected]> wrote:
> HI!
>
> > +/**
> > + * Get the filesystem statistics. Currently, we let this pass right through
> > + * to the lower filesystem and take no action ourselves.
> > + */
> > +static int ecryptfs_statfs(struct super_block *sb, struct kstatfs *buf)
> > +{
> > + int rc = 0;
> > +
> > + ecryptfs_printk(KERN_DEBUG, "Enter\n");
> > + rc = vfs_statfs(ECRYPTFS_SUPERBLOCK_TO_LOWER(sb), buf);
> > + ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
> > + return rc;
> > +}
>
> This is ugly. Could you remove the debugging, or at least use dprintk?

How would dprintk differ from the current approach with ecryptfs_printk?

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

2006-05-04 14:26:45

by Pekka Enberg

[permalink] [raw]
Subject: Re: [PATCH 6/13: eCryptfs] Superblock operations

> On 5/4/06, Pavel Machek <[email protected]> wrote:
> > > +static int ecryptfs_statfs(struct super_block *sb, struct kstatfs *buf)
> > > +{
> > > + int rc = 0;
> > > +
> > > + ecryptfs_printk(KERN_DEBUG, "Enter\n");
> > > + rc = vfs_statfs(ECRYPTFS_SUPERBLOCK_TO_LOWER(sb), buf);
> > > + ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
> > > + return rc;
> > > +}
> >
> > This is ugly. Could you remove the debugging, or at least use dprintk?

On 5/4/06, Michael Thompson <[email protected]> wrote:
> How would dprintk differ from the current approach with ecryptfs_printk?

Not much, so please just kill the function tracing stuffs. Thanks.

Pekka

2006-05-04 14:38:05

by Pekka Enberg

[permalink] [raw]
Subject: Re: [PATCH 6/13: eCryptfs] Superblock operations

Hi Phillip,

Some comments below.

On 5/4/06, Phillip Hellewell <[email protected]> wrote:
> +kmem_cache_t *ecryptfs_inode_info_cache;

Please use struct kmem_cache instead of the typedef.

> +static struct inode *ecryptfs_alloc_inode(struct super_block *sb) {

Formatting

> + struct ecryptfs_inode_info *ecryptfs_inode = NULL;

Redundant initialization

> + struct inode *inode = NULL;
> +
> + ecryptfs_printk(KERN_DEBUG, "Enter; sb = [%p]\n", sb);
> + ecryptfs_inode = kmem_cache_alloc(ecryptfs_inode_info_cache,
> + SLAB_KERNEL);
> + if (unlikely(!ecryptfs_inode)) {
> + ecryptfs_printk(KERN_WARNING,
> + "Failed to allocate new inode\n");
> + goto out;

No need to log it, just return NULL here

> + }
> + ecryptfs_init_crypt_stat(&(ecryptfs_inode->crypt_stat));
> + inode = &(ecryptfs_inode->vfs_inode);

Bogus parenthesis, twice. Inode is a redundant local variable, btw.

> +out:
> + ecryptfs_printk(KERN_DEBUG, "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_stat struct, will be released here.
> + * There should be no chance that this deallocation will be missed.
> + */
> +static void ecryptfs_destroy_inode(struct inode *inode) {

Formatting

> + struct ecryptfs_crypt_stat *crypt_stat;
> +
> + ecryptfs_printk(KERN_DEBUG, "Enter; inode = [%p]\n", inode);
> + crypt_stat = &(ECRYPTFS_INODE_TO_PRIVATE(inode))->crypt_stat;
> + ecryptfs_destruct_crypt_stat(crypt_stat);
> + kmem_cache_free(ecryptfs_inode_info_cache,
> + ECRYPTFS_INODE_TO_PRIVATE(inode));

Better to introduce a local variable for CRYPTFS_INODE_TO_PRIVATE.
More readable and smaller kernel text that way.

> + ecryptfs_printk(KERN_DEBUG, "Exit\n");
> +}
> +
> +/**
> + * Set up the ecryptfs inode.
> + */
> +static void ecryptfs_read_inode(struct inode *inode)
> +{
> + ecryptfs_printk(KERN_DEBUG, "Enter; inode = [%p]\n", inode);
> + /* This is where we setup the self-reference in the vfs_inode's
> + * u.generic_ip. That way we don't have to walk the list again. */
> + ECRYPTFS_INODE_TO_PRIVATE_SM(inode) =
> + list_entry(inode, struct ecryptfs_inode_info, vfs_inode);
> + ECRYPTFS_INODE_TO_LOWER(inode) = NULL;

Hmm, ugly, please make the setters explicit instead.

> + inode->i_version++;
> + inode->i_op = &ecryptfs_main_iops;
> + inode->i_fop = &ecryptfs_main_fops;
> + inode->i_mapping->a_ops = &ecryptfs_aops;
> + ecryptfs_printk(KERN_DEBUG, "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) {

Formatting

> + generic_delete_inode(inode);
> +}
> +
> +/**
> + * Final actions when unmounting a file system.
> + * This will handle deallocation and release of our private data.
> + */
> +static void ecryptfs_put_super(struct super_block *sb)
> +{
> + struct ecryptfs_sb_info *sb_info = ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb);

You probably want to rename ECRYPTFS_SUPERBLOCK_TO_PRIVATE to
ecryptfs_sb or similar.

> +
> + ecryptfs_printk(KERN_DEBUG, "Enter\n");
> + mntput(sb_info->lower_mnt);
> + key_put(sb_info->mount_crypt_stat.global_auth_tok_key);
> + kmem_cache_free(ecryptfs_sb_info_cache, sb_info);
> + ECRYPTFS_SUPERBLOCK_TO_PRIVATE_SM(sb) = NULL;
> + ecryptfs_printk(KERN_DEBUG, "Exit\n");
> +}
> +
> +/**
> + * Get the filesystem statistics. Currently, we let this pass right through
> + * to the lower filesystem and take no action ourselves.
> + */
> +static int ecryptfs_statfs(struct super_block *sb, struct kstatfs *buf)
> +{
> + int rc = 0;

Redundant initialization

> +
> + ecryptfs_printk(KERN_DEBUG, "Enter\n");
> + rc = vfs_statfs(ECRYPTFS_SUPERBLOCK_TO_LOWER(sb), buf);
> + ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
> + return rc;
> +}
> +
> +/**
> + * Called to ask filesystem to change mount options. Not implemented;
> + * returns -ENOSYS every time.
> + */
> +static int ecryptfs_remount_fs(struct super_block *sb, int *flags, char *data)
> +{
> + ecryptfs_printk(KERN_DEBUG, "Enter\n");
> + ecryptfs_printk(KERN_DEBUG, "Exit\n");
> + return -ENOSYS;
> +}

Any reason you want to keep this around?

> +
> +/**
> + * 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.
> + */
> +static void ecryptfs_clear_inode(struct inode *inode)
> +{
> + ecryptfs_printk(KERN_DEBUG, "Enter; inode = [%p]; i_ino = [0x%.16x]\n",
> + inode, inode->i_ino);
> + iput(ECRYPTFS_INODE_TO_LOWER(inode));
> + ecryptfs_printk(KERN_DEBUG, "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.
> + */
> +static void ecryptfs_umount_begin(struct vfsmount *vfsmnt, int flags)
> +{
> + struct vfsmount *lower_mnt;
> + struct super_block *lower_sb;
> +
> + ecryptfs_printk(KERN_DEBUG, "Enter\n");
> + lower_mnt = ECRYPTFS_SUPERBLOCK_TO_PRIVATE(vfsmnt->mnt_sb)->lower_mnt;
> + lower_sb = lower_mnt->mnt_sb;
> + if (lower_sb->s_op->umount_begin)
> + lower_sb->s_op->umount_begin(lower_mnt, flags);
> + ecryptfs_printk(KERN_DEBUG, "Exit\n");
> +}
> +
> +/**
> + * 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 rc = 0;
> + char *tmp = NULL;
> + char *path;
> +
> + ecryptfs_printk(KERN_DEBUG, "Enter\n");
> + tmp = (char *)__get_free_page(GFP_KERNEL);
> + if (!tmp) {
> + rc = -ENOMEM;
> + goto out;
> + }
> + path = d_path(ECRYPTFS_DENTRY_TO_LOWER(sb->s_root),
> + ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb)->lower_mnt, tmp,
> + PAGE_SIZE);

Use of local variables probably would clean that up

> + seq_printf(m, ",dir=%s", path);
> + free_page((unsigned long)tmp);
> +out:
> + ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
> + return rc;
> +}
> +
> +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
> +};
> -
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
>

2006-05-04 14:51:27

by Pekka Enberg

[permalink] [raw]
Subject: Re: [PATCH 5/13: eCryptfs] Header declarations

On 5/4/06, Phillip Hellewell <[email protected]> wrote:
> +#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 */

So, what's wrong with BUG_ON?

> +
> +#define ECRYPTFS_FILE_TO_PRIVATE(file) ((struct ecryptfs_file_info *) \
> + ((file)->private_data))
> +#define ECRYPTFS_FILE_TO_PRIVATE_SM(file) ((file)->private_data)
> +#define ECRYPTFS_FILE_TO_LOWER(file) \
> + ((ECRYPTFS_FILE_TO_PRIVATE(file))->wfi_file)
> +#define ECRYPTFS_INODE_TO_PRIVATE(ino) ((struct ecryptfs_inode_info *) \
> + (ino)->u.generic_ip)
> +#define ECRYPTFS_INODE_TO_PRIVATE_SM(ino) ((ino)->u.generic_ip)
> +#define ECRYPTFS_INODE_TO_LOWER(ino) (ECRYPTFS_INODE_TO_PRIVATE(ino)->wii_inode)
> +#define ECRYPTFS_SUPERBLOCK_TO_PRIVATE(super) ((struct ecryptfs_sb_info *) \
> + (super)->s_fs_info)
> +#define ECRYPTFS_SUPERBLOCK_TO_PRIVATE_SM(super) ((super)->s_fs_info)
> +#define ECRYPTFS_SUPERBLOCK_TO_LOWER(super) \
> + (ECRYPTFS_SUPERBLOCK_TO_PRIVATE(super)->wsi_sb)
> +#define ECRYPTFS_DENTRY_TO_PRIVATE_SM(dentry) ((dentry)->d_fsdata)
> +#define ECRYPTFS_DENTRY_TO_PRIVATE(dentry) ((struct ecryptfs_dentry_info *) \
> + (dentry)->d_fsdata)
> +#define ECRYPTFS_DENTRY_TO_LOWER(dentry) \
> + (ECRYPTFS_DENTRY_TO_PRIVATE(dentry)->wdi_dentry)

Static inline functions, please.

> +
> +#define ecryptfs_printk(type, fmt, arg...) \
> + __ecryptfs_printk(type "%s: " fmt, __FUNCTION__, ## arg);
> +void __ecryptfs_printk(const char *fmt, ...);

Why not plain printk?

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

Please use struct kmem_cache instead.

2006-05-04 14:59:00

by Artem B. Bityutskiy

[permalink] [raw]
Subject: Re: [PATCH 5/13: eCryptfs] Header declarations

Pekka Enberg wrote:
>> +#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 */
>
> So, what's wrong with BUG_ON?
>
I guess because this may be compiled off when no debugging/extra
checking is needed.


--
Best regards, Artem B. Bityutskiy
Oktet Labs (St. Petersburg), Software Engineer.
+7 812 4286709 (office) +7 911 2449030 (mobile)
E-mail: [email protected], Web: http://www.oktetlabs.ru

2006-05-04 15:00:47

by Michael Thompson

[permalink] [raw]
Subject: Re: [PATCH 6/13: eCryptfs] Superblock operations

On 5/4/06, Pekka Enberg <[email protected]> wrote:
> Hi Phillip,
>
> Some comments below.
>
> On 5/4/06, Phillip Hellewell <[email protected]> wrote:
> > +kmem_cache_t *ecryptfs_inode_info_cache;
>
> Please use struct kmem_cache instead of the typedef.

Please explain why. Looking at the source shows that kmem_cache_t is
more widely used, and therefore seems to be the prefered way.

> > + ecryptfs_printk(KERN_DEBUG, "Exit\n");
> > +}
> > +
> > +/**
> > + * Set up the ecryptfs inode.
> > + */
> > +static void ecryptfs_read_inode(struct inode *inode)
> > +{
> > + ecryptfs_printk(KERN_DEBUG, "Enter; inode = [%p]\n", inode);
> > + /* This is where we setup the self-reference in the vfs_inode's
> > + * u.generic_ip. That way we don't have to walk the list again. */
> > + ECRYPTFS_INODE_TO_PRIVATE_SM(inode) =
> > + list_entry(inode, struct ecryptfs_inode_info, vfs_inode);
> > + ECRYPTFS_INODE_TO_LOWER(inode) = NULL;
>
> Hmm, ugly, please make the setters explicit instead.

Curious, what exactly do you mean by this? I'm not sure what you mean
by "setters".

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

2006-05-04 15:08:07

by Michael Thompson

[permalink] [raw]
Subject: Re: [PATCH 5/13: eCryptfs] Header declarations

On 5/4/06, Pekka Enberg <[email protected]> wrote:
> On 5/4/06, Phillip Hellewell <[email protected]> wrote:
> > +
> > +#define ecryptfs_printk(type, fmt, arg...) \
> > + __ecryptfs_printk(type "%s: " fmt, __FUNCTION__, ## arg);
> > +void __ecryptfs_printk(const char *fmt, ...);
>
> Why not plain printk?

Originally, ecryptfs_printk was using a verbosity level, which is why
there was a custom call wrapping printk. This is no longer the case,
and the eCryptfs developers are currently looking at this, and are
planning to remove the bulk of printks.

Thanks,
Mike

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

2006-05-04 15:13:17

by Pekka Enberg

[permalink] [raw]
Subject: Re: [PATCH 10/13: eCryptfs] Mmap operations

On 5/4/06, Phillip Hellewell <[email protected]> wrote:
> +/**
> + * 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(KERN_DEBUG, "Enter\n");
> + dentry = file->f_dentry;
> + inode = dentry->d_inode;
> + mapping = inode->i_mapping;
> + 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);

What's the purpose of this second read?

> + 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(KERN_DEBUG, "Exit\n");
> + return page;
> +}

[snip]

> +/**
> + * 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
> + */
> +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(KERN_DEBUG, "Enter; lower_page_index = [0x%.16x]\n",
> + lower_page_index);
> + dentry = file->f_dentry;
> + if (NULL == ECRYPTFS_FILE_TO_PRIVATE_SM(file)) {
> + rc = -ENOENT;
> + ecryptfs_printk(KERN_ERR, "No lower file info\n");
> + goto out;
> + }
> + lower_file = ECRYPTFS_FILE_TO_LOWER(file);
> + lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(dentry);
> + inode = dentry->d_inode;
> + lower_inode = ECRYPTFS_INODE_TO_LOWER(inode);
> + lower_a_ops = lower_inode->i_mapping->a_ops;
> + lower_page = read_cache_page(lower_inode->i_mapping, lower_page_index,
> + (filler_t *)lower_a_ops->readpage,
> + (void *)lower_file);
> + if (IS_ERR(lower_page)) {
> + rc = PTR_ERR(lower_page);
> + lower_page = NULL;
> + ecryptfs_printk(KERN_ERR, "Error reading from page cache\n");
> + goto out;
> + }
> + wait_on_page_locked(lower_page);
> + if (!PageUptodate(lower_page)) {

And here?

> + lock_page(lower_page);
> + rc = lower_a_ops->readpage(lower_file, lower_page);
> + if (rc) {
> + lower_page = NULL;
> + rc = -EIO;
> + ecryptfs_printk(KERN_ERR, "Error reading lower "
> + "page at index=[0x%.16x]\n",
> + lower_page_index);
> + goto out;
> + }
> + wait_on_page_locked(lower_page);
> + if (!PageUptodate(lower_page)) {
> + rc = -EIO;
> + ecryptfs_printk(KERN_ERR, "Error reading lower "
> + "page at index=[0x%.16x]\n",
> + lower_page_index);
> + goto out;
> + }
> + }

2006-05-04 15:12:41

by Pekka Enberg

[permalink] [raw]
Subject: Re: [PATCH 6/13: eCryptfs] Superblock operations

On 5/4/06, Phillip Hellewell <[email protected]> wrote:
> > > +kmem_cache_t *ecryptfs_inode_info_cache;
> >
> > Please use struct kmem_cache instead of the typedef.

On Thu, 2006-05-04 at 10:00 -0500, Michael Thompson wrote:
> Please explain why. Looking at the source shows that kmem_cache_t is
> more widely used, and therefore seems to be the prefered way.

That's because the typedef is deprecated. See commit
2109a2d1b175dfcffbfdac693bdbe4c4ab62f11f "[PATCH] mm: rename
kmem_cache_s to kmem_cache".

> > > +/**
> > > + * Set up the ecryptfs inode.
> > > + */
> > > +static void ecryptfs_read_inode(struct inode *inode)
> > > +{
> > > + ecryptfs_printk(KERN_DEBUG, "Enter; inode = [%p]\n", inode);
> > > + /* This is where we setup the self-reference in the vfs_inode's
> > > + * u.generic_ip. That way we don't have to walk the list again. */
> > > + ECRYPTFS_INODE_TO_PRIVATE_SM(inode) =
> > > + list_entry(inode, struct ecryptfs_inode_info, vfs_inode);
> > > + ECRYPTFS_INODE_TO_LOWER(inode) = NULL;
> >
> > Hmm, ugly, please make the setters explicit instead.
>
> Curious, what exactly do you mean by this? I'm not sure what you mean
> by "setters".

That

+ ECRYPTFS_INODE_TO_LOWER(inode) = NULL;

should be either

ecryptfs_set_lower_inode(inode, NULL);

or

inode->lower = NULL;

2006-05-04 15:23:04

by Pekka Enberg

[permalink] [raw]
Subject: Re: [PATCH 5/13: eCryptfs] Header declarations

Pekka Enberg wrote:
> > So, what's wrong with BUG_ON?

On Thu, 2006-05-04 at 18:58 +0400, Artem B. Bityutskiy wrote:
> I guess because this may be compiled off when no debugging/extra
> checking is needed.

But you shouldn't write assertions that you don't really need anyway.

Pekka

2006-05-04 15:29:11

by Artem B. Bityutskiy

[permalink] [raw]
Subject: Re: [PATCH 5/13: eCryptfs] Header declarations

Pekka Enberg wrote:
> Pekka Enberg wrote:
>
>>>So, what's wrong with BUG_ON?
>
>>I guess because this may be compiled off when no debugging/extra
>>checking is needed.
>
> But you shouldn't write assertions that you don't really need anyway.
>
I wouldn't be so strict. Normally, you don't need them and disable as
they slow you down. But when you are hunting a problem you enable them
as they may catch it on an early stage. I don't see anything bad here...

--
Best regards, Artem B. Bityutskiy
Oktet Labs (St. Petersburg), Software Engineer.
+7 812 4286709 (office) +7 911 2449030 (mobile)
E-mail: [email protected], Web: http://www.oktetlabs.ru

2006-05-04 20:28:07

by Randy Dunlap

[permalink] [raw]
Subject: Re: [PATCH 13/13: eCryptfs] Debug functions

On Wed, 3 May 2006 21:43:34 -0600 Phillip Hellewell wrote:

> ---
> debug.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 files changed, 122 insertions(+)
>
> Index: linux-2.6.17-rc3-mm1-ecryptfs/fs/ecryptfs/debug.c
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ linux-2.6.17-rc3-mm1-ecryptfs/fs/ecryptfs/debug.c 2006-05-02 19:35:59.000000000 -0600
> @@ -0,0 +1,122 @@
> +
> +/**
> + * Dump hexadecimal representation of char array
> + *
> + * @param data
> + * @param bytes
> + */
> +void ecryptfs_dump_hex(char *data, int bytes)
> +{

Use proper kernel-doc notation/format, please.

(did someone already say this?)

---
~Randy

2006-05-04 21:41:32

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 6/13: eCryptfs] Superblock operations

Pekka Enberg <[email protected]> wrote:

> > + ecryptfs_printk(KERN_DEBUG, "Enter; inode = [%p]\n", inode);
> > + crypt_stat = &(ECRYPTFS_INODE_TO_PRIVATE(inode))->crypt_stat;
> > + ecryptfs_destruct_crypt_stat(crypt_stat);
> > + kmem_cache_free(ecryptfs_inode_info_cache,
> > + ECRYPTFS_INODE_TO_PRIVATE(inode));
>
> Better to introduce a local variable for CRYPTFS_INODE_TO_PRIVATE.
> More readable and smaller kernel text that way.

But it may use more stack, which is a much more limited resource, so what you
suggest is not necessarily the best thing to do.

David

2006-05-04 21:43:42

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 10/13: eCryptfs] Mmap operations

Pekka Enberg <[email protected]> wrote:

> > + rc = mapping->a_ops->readpage(file, page);
>
> What's the purpose of this second read?

When writing CacheFiles, I noticed that ext3 would occasionally unlock a page
that had neither PG_uptodate nor PG_error set, and so I had to force another
readpage() on it.

David

2006-05-05 09:03:17

by Alon Bar-Lev

[permalink] [raw]
Subject: Re: [PATCH 0/12: eCryptfs] eCryptfs version 0.1.6

Phillip Hellewell wrote:
> This patch set constitutes the 0.1.6 release of the eCryptfs
> cryptographic filesystem:
>

Great work!
I just want to make some notes about the user mode interface.

I think that current security and encryption solutions that
come out these days without a proper smart card support
create a false sense of security for users. So solutions
should first be designed so that a smart card can be used,
and only then provide a passphrase alternative.

This is the first time I've looked on the user mode
keyutils, and it looks quite good. I think that the eCryptfs
should use it more ?correctly? so that keys stored on smart
card may be used.

For example, you can publish a text format of the key data
so that any application can easily and independently
construct this.

A simple example of key content is:
eCryptfs:version:cipher:key
or:
eCryptfs:1:AES256:123DD12ED12312....

You can modify the mount options:
mount -o keytype=type,keydesc=desc,keydata=data

So that any specific key may be provided, and not only keys
derived from a passphrase. And a proper key may be
instantiate from the kernel.

User may already have this key available using keyctl. The
key may be available from any place.

But if the key is not available, the following
request-key.conf this may be written:

---
create user eCryptfs:p11-data:*
|/usr/bin/eCryptfs-key-p11-data /etc/eCryptfs-key-p11-data.conf

create user eCryptfs:pass:* |/usr/bin/eCryptfs-key-pass
---

This will construct the key from the data if the key is not
available.

For PKCS#11 the providers listed in the conf file, key may
be stored as a data object, and data may be:
cipher:slot-type:slot:application:label:PIN
Where:
slot-type
slot - slot id
name - slot name
label - token label

?Calling without a PIN may allow prompt for dialog
via a named pipe to a running daemon by uid?

For passphrase the data may be the cipher:passphrase.

Another issue is a multi user access without a shared
secret. This may be done by encrypting the symmetric key in
several asymmetric ones. Again this can be done in user
mode... But there is a need to support reencrypting the
whole filesystem with a new symmetric key so that a user may
be removed from the list.

In order to support this mode using smart cards when key is
unavailable, the following request-key.conf may be written:

---
create user eCryptfs:p11-multi:*
|/usr/bin/eCryptfs-key-p11-multi
/etc/eCryptfs-key-p11-multi.conf
---

Now the data may be:
cipher:slot-type:slot:id-type:id:PIN
Where:
slot-type
slot - slot id
name - slot name
label - token label
id-type
id - CKA_ID
label - CKA_LABEL
subject - certificate subject


I hope I understood correctly the various components.

If you like, I will be happy to help you with the user mode
stuff, and implement the smart card access.

Best Regards,
Alon Bar-Lev

2006-05-05 13:12:24

by Dave Kleikamp

[permalink] [raw]
Subject: Re: [PATCH 6/13: eCryptfs] Superblock operations

On Thu, 2006-05-04 at 22:40 +0100, David Howells wrote:
> Pekka Enberg <[email protected]> wrote:
>
> > > + ecryptfs_printk(KERN_DEBUG, "Enter; inode = [%p]\n", inode);
> > > + crypt_stat = &(ECRYPTFS_INODE_TO_PRIVATE(inode))->crypt_stat;
> > > + ecryptfs_destruct_crypt_stat(crypt_stat);
> > > + kmem_cache_free(ecryptfs_inode_info_cache,
> > > + ECRYPTFS_INODE_TO_PRIVATE(inode));
> >
> > Better to introduce a local variable for CRYPTFS_INODE_TO_PRIVATE.
> > More readable and smaller kernel text that way.
>
> But it may use more stack, which is a much more limited resource, so what you
> suggest is not necessarily the best thing to do.

I think either way it's coded, the compiler will probably store the
result in a register. I would recommend the most readable approach
(which I believe would be using a local variable) and leave the
optimization to the compiler.

> David

Shaggy
--
David Kleikamp
IBM Linux Technology Center

2006-05-05 14:04:50

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 6/13: eCryptfs] Superblock operations

Dave Kleikamp <[email protected]> wrote:

> > But it may use more stack, which is a much more limited resource, so what
> > you suggest is not necessarily the best thing to do.
>
> I think either way it's coded, the compiler will probably store the
> result in a register.

There's an apparent function call between the two usages of the value. So
even if the value is placed in a register, that register must either be saved
on the stack around the function call (if it's callee-clobbered), or the
register must be saved on the stack before the value is placed in it (if it's
callee-saved).

Either way, it will use more stack; the mere fact that whilst it's using the
value, the compiler may stash it in a register is irrelevant.

> I would recommend the most readable approach (which I believe would be using
> a local variable) and leave the optimization to the compiler.

Whilst it may be more readable, it doesn't mean it's more optimal. You're
just trading stack usage for code size. The function call in the middle
limits the optimisation the compiler can do.

Of course, if the thing in the middle is not actually a function call, or if
it can be inlined, then this _might_ not apply. It may even be possible that
the compiler will discard the variable and fetch it again from memory if it
considers the value in memory to be unchanging for the duration.

David

2006-05-05 14:35:01

by Dave Kleikamp

[permalink] [raw]
Subject: Re: [PATCH 6/13: eCryptfs] Superblock operations

On Fri, 2006-05-05 at 15:03 +0100, David Howells wrote:
> Dave Kleikamp <[email protected]> wrote:
>
> > > But it may use more stack, which is a much more limited resource, so what
> > > you suggest is not necessarily the best thing to do.
> >
> > I think either way it's coded, the compiler will probably store the
> > result in a register.
>
> There's an apparent function call between the two usages of the value. So
> even if the value is placed in a register, that register must either be saved
> on the stack around the function call (if it's callee-clobbered), or the
> register must be saved on the stack before the value is placed in it (if it's
> callee-saved).

Probably true unless it can reuse a callee-saved register.

> Either way, it will use more stack; the mere fact that whilst it's using the
> value, the compiler may stash it in a register is irrelevant.

Is the stack usage very close to exceeding 4 KB? Could saving one more
pointer on the stack cause a problem? Anyway, it's not that big of a
deal. The code may look a little cleaner with a local variable, but
it's not that bad as it is.

> > I would recommend the most readable approach (which I believe would be using
> > a local variable) and leave the optimization to the compiler.
>
> Whilst it may be more readable, it doesn't mean it's more optimal. You're
> just trading stack usage for code size. The function call in the middle
> limits the optimisation the compiler can do.
>
> Of course, if the thing in the middle is not actually a function call, or if
> it can be inlined, then this _might_ not apply. It may even be possible that
> the compiler will discard the variable and fetch it again from memory if it
> considers the value in memory to be unchanging for the duration.

It looks like a real function call.

Shaggy
--
David Kleikamp
IBM Linux Technology Center

2006-05-05 14:53:13

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 6/13: eCryptfs] Superblock operations

Dave Kleikamp <[email protected]> wrote:

> > Either way, it will use more stack; the mere fact that whilst it's using the
> > value, the compiler may stash it in a register is irrelevant.
>
> Is the stack usage very close to exceeding 4 KB? Could saving one more
> pointer on the stack cause a problem?

I suspect you don't know since it's a stacked filesystem.

David

2006-05-05 15:22:50

by Dave Kleikamp

[permalink] [raw]
Subject: Re: [PATCH 10/13: eCryptfs] Mmap operations

On Thu, 2006-05-04 at 22:43 +0100, David Howells wrote:
> Pekka Enberg <[email protected]> wrote:
>
> > > + rc = mapping->a_ops->readpage(file, page);
> >
> > What's the purpose of this second read?
>
> When writing CacheFiles, I noticed that ext3 would occasionally unlock a page
> that had neither PG_uptodate nor PG_error set, and so I had to force another
> readpage() on it.

I understand this comes from the FiST package. In that code, there is a
comment in one of these functions explaining the second read. It would
be nice to have that comment in here too:

/*
* call readpage() again if we returned from wait_on_page with a
* page that's not up-to-date; that can happen when a partial
* page has a few buffers which are ok, but not the whole
* page.
*/

I'm a bit surprised that this could happen.

Shaggy
--
David Kleikamp
IBM Linux Technology Center

2006-05-05 15:38:51

by Pekka Enberg

[permalink] [raw]
Subject: Re: [PATCH 10/13: eCryptfs] Mmap operations

On Thu, 2006-05-04 at 22:43 +0100, David Howells wrote:
> > When writing CacheFiles, I noticed that ext3 would occasionally unlock a page
> > that had neither PG_uptodate nor PG_error set, and so I had to force another
> > readpage() on it.

On Fri, 2006-05-05 at 10:22 -0500, Dave Kleikamp wrote:
> I understand this comes from the FiST package. In that code, there is a
> comment in one of these functions explaining the second read. It would
> be nice to have that comment in here too:
>
> /*
> * call readpage() again if we returned from wait_on_page with a
> * page that's not up-to-date; that can happen when a partial
> * page has a few buffers which are ok, but not the whole
> * page.
> */
>
> I'm a bit surprised that this could happen.

Me too. How do we know we don't end up the same way for the second read?

Pekka

2006-05-05 16:08:08

by Michael Halcrow

[permalink] [raw]
Subject: Re: [PATCH 0/12: eCryptfs] eCryptfs version 0.1.6

On Fri, May 05, 2006 at 12:05:37PM +0300, Alon Bar-Lev wrote:
> I think that current security and encryption solutions that come out
> these days without a proper smart card support create a false sense
> of security for users. So solutions should first be designed so that
> a smart card can be used, and only then provide a passphrase
> alternative.

As detailed in my OLS papers, the whole purpose of eCryptfs, in the
long run, is to provide a means for users to get away from having to
use passphrases alone to protect their data.

> This is the first time I've looked on the user mode keyutils, and it
> looks quite good. I think that the eCryptfs should use it more
> ?correctly? so that keys stored on smart card may be used.

The 0.1 release of eCryptfs provides passphrase-only support in order
to minimize the code complexity for the initial adoption into the
Linux kernel. As you have noticed, the framework is easily extensible
to provide more advanced key management features. We are nearly
finished with the 0.2 release design document, and we have a team
scheduled to work on the more advanced key management features this
summer.

> For PKCS#11 the providers listed in the conf file, key may
> be stored as a data object, and data may be:
> cipher:slot-type:slot:application:label:PIN
> Where:
> slot-type
> slot - slot id
> name - slot name
> label - token label
>
> ?Calling without a PIN may allow prompt for dialog
> via a named pipe to a running daemon by uid?

I have been the maintainer of the openCryptoki package (an Open Source
PKCS#11 implementation) for the last year; utilization of openCryptoki
is one of the things we have in mind for eCryptfs. Our design for the
0.2 release includes an interface for allowing keys to be managed via
various services, implemented by plugins. For instance, a one plugin
could link against libopencryptoki.so and use PKCS#11 tokens to manage
the keys. Another plugin could link against libgpgme.so and use GnuPG
to manage the keys. Another plugin could link against libssl.so and
use direct OpenSSL crypto calls to manage the keys. Another plugin
could link against libtspi.so and use a TPM module to manage the keys.

Packets in the header of each file will be processed with specific
modules (identifiers for these modules and the keys in the modules
will be in the header). We have plans to implement userspace
components to accomplish all of this at file open:

- Kernel eCryptfs module reads the encrypted file encryption key and
the module/key identifier from the header.

- Kernel module sends a message to a userspace daemon (question: is
netlink, relayfs, or some other mechanism best for this task?).

- The daemon links against libecryptfs, which did a dlopen on the
plugins in a place like /usr/lib/ecryptfs/ when it launched.

- Then we defer the request to decrypt the file encryption key to the
module specified in the message that was originally sent to the
daemon.

- The daemon relays the decrypted file encryption key back to
eCryptfs in the kernel, and eCryptfs associates that key with the
crypt_stat for the inode, continuing with the page read/write
operations that follow.

> Another issue is a multi user access without a shared secret. This
> may be done by encrypting the symmetric key in several asymmetric
> ones.

This is exactly the sort of functionality that eCryptfs is designed to
support.

> Again this can be done in user mode... But there is a need to
> support reencrypting the whole filesystem with a new symmetric key
> so that a user may be removed from the list.

Yes, key revocation will necessarily require the re-encryption of all
of the data, which is of limited use if the user whose key is revoked
had access to all of the encrypted data in the first place before the
revocation (he could have simply made a copy of it all). Given the
stated goal of eCryptfs to protect data-at-rest from compromise in the
event of the loss or theft of the storage device, I cannot think of
many use case scenarios where key revocation will be a requirement. I
would expect that a key revocation event would entail revocation of
access to the encrypted data too.

> If you like, I will be happy to help you with the user mode stuff,
> and implement the smart card access.

Thanks for your suggestions; it seems that we are not thinking too
differently in terms of what needs to happen to provide more advanced
key management features. In the meantime, we would like to get the
simple passphrase-only version of eCryptfs upstream, and then we will
be working on implementing these more advanced features this summer. I
will let you know when we have completed the 0.2 design document.

In the meantime, if you have a desire to implement some of the
features (in userspace) you describe, please join the ecryptfs-devel
list and discuss the plans with our team, so that there is no wasted
or duplicated effort:

https://lists.sourceforge.net/lists/listinfo/ecryptfs-devel

Thanks,
Mike Halcrow


Attachments:
(No filename) (4.91 kB)
signature.asc (481.00 B)
Digital signature
Download all attachments

2006-05-05 16:15:50

by Timothy R. Chavez

[permalink] [raw]
Subject: Re: [PATCH 6/13: eCryptfs] Superblock operations

On Wed, 2006-05-03 at 21:38 -0600, Phillip Hellewell wrote:
> This is the 6th patch in a series of 13 constituting the kernel
> components of the eCryptfs cryptographic filesystem.
>
> eCryptfs superblock operations and inode allocation, deallocation, and
> initialization functions.
>
> Signed-off-by: Phillip Hellewell <[email protected]>
> Signed-off-by: Michael Halcrow <[email protected]>

Hello,

I looked over this code and had just a few nits; all pretty trivial.
Comments below.

-tim
> ---
>
> super.c | 225 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 files changed, 225 insertions(+)
>
> Index: linux-2.6.17-rc3-mm1-ecryptfs/fs/ecryptfs/super.c
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ linux-2.6.17-rc3-mm1-ecryptfs/fs/ecryptfs/super.c 2006-05-02 19:36:04.000000000 -0600
> @@ -0,0 +1,225 @@
> +/**
> + * eCryptfs: Linux filesystem encryption layer
> + *
> + * Copyright (C) 1997-2003 Erez Zadok
> + * Copyright (C) 2001-2003 Stony Brook University
> + * Copyright (C) 2004-2006 International Business Machines Corp.
> + * Author(s): Michael A. Halcrow <[email protected]>
> + * Michael C. Thompson <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of the
> + * License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
> + * 02111-1307, USA.
> + */
> +
> +#include <linux/fs.h>
> +#include <linux/mount.h>
> +#include <linux/key.h>
> +#include <linux/seq_file.h>
> +#include "ecryptfs_kernel.h"
> +
> +kmem_cache_t *ecryptfs_inode_info_cache;

Can this be declared static?

[..]
> +
> +/**
> + * Called to bring an inode into existence.
> + *
> + * Note that setting the self referencing pointer doesn't work here:
> + * i.e. ECRYPTFS_INODE_TO_PRIVATE_SM(inode) = ei;
> + *
> + * Only handle allocation, setting up structures should be done in
> + * ecryptfs_read_inode. This is because the kernel, between now and
> + * then, will 0 out the private data pointer.
> + *
> + * @param sb Pointer to the super block of the filesystem
> + * @return Pointer to a newly allocated inode, NULL otherwise
> + */
> +static struct inode *ecryptfs_alloc_inode(struct super_block *sb) {
> + struct ecryptfs_inode_info *ecryptfs_inode = NULL;
> + struct inode *inode = NULL;
> +
> + ecryptfs_printk(KERN_DEBUG, "Enter; sb = [%p]\n", sb);
> + ecryptfs_inode = kmem_cache_alloc(ecryptfs_inode_info_cache,
> + SLAB_KERNEL);
> + if (unlikely(!ecryptfs_inode)) {
> + ecryptfs_printk(KERN_WARNING,
> + "Failed to allocate new inode\n");
> + goto out;
> + }
> + ecryptfs_init_crypt_stat(&(ecryptfs_inode->crypt_stat));
> + inode = &(ecryptfs_inode->vfs_inode);
> +out:
> + ecryptfs_printk(KERN_DEBUG, "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_stat 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_stat *crypt_stat;
> +
> + ecryptfs_printk(KERN_DEBUG, "Enter; inode = [%p]\n", inode);
> + crypt_stat = &(ECRYPTFS_INODE_TO_PRIVATE(inode))->crypt_stat;
> + ecryptfs_destruct_crypt_stat(crypt_stat);
> + kmem_cache_free(ecryptfs_inode_info_cache,
> + ECRYPTFS_INODE_TO_PRIVATE(inode));
> + ecryptfs_printk(KERN_DEBUG, "Exit\n");
> +}
> +
> +/**
> + * Set up the ecryptfs inode.
> + */
> +static void ecryptfs_read_inode(struct inode *inode)
> +{
> + ecryptfs_printk(KERN_DEBUG, "Enter; inode = [%p]\n", inode);
> + /* This is where we setup the self-reference in the vfs_inode's
> + * u.generic_ip. That way we don't have to walk the list again. */
> + ECRYPTFS_INODE_TO_PRIVATE_SM(inode) =
> + list_entry(inode, struct ecryptfs_inode_info, vfs_inode);
> + ECRYPTFS_INODE_TO_LOWER(inode) = NULL;
> + inode->i_version++;
> + inode->i_op = &ecryptfs_main_iops;
> + inode->i_fop = &ecryptfs_main_fops;
> + inode->i_mapping->a_ops = &ecryptfs_aops;
> + ecryptfs_printk(KERN_DEBUG, "Exit\n");
> +}
> +
> +
> +/**
> + * This is called through iput_final().
> + * This is function will replace generic_drop_inode. The end result of which

This is function? :)

[..]
> + * 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);
> +}
> +

Might you static inline this function?

[..]
> +/**
> + * Final actions when unmounting a file system.
> + * This will handle deallocation and release of our private data.
> + */
> +static void ecryptfs_put_super(struct super_block *sb)
> +{
> + struct ecryptfs_sb_info *sb_info = ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb);
> +
> + ecryptfs_printk(KERN_DEBUG, "Enter\n");
> + mntput(sb_info->lower_mnt);
> + key_put(sb_info->mount_crypt_stat.global_auth_tok_key);
> + kmem_cache_free(ecryptfs_sb_info_cache, sb_info);
> + ECRYPTFS_SUPERBLOCK_TO_PRIVATE_SM(sb) = NULL;
> + ecryptfs_printk(KERN_DEBUG, "Exit\n");
> +}
> +
> +/**
> + * Get the filesystem statistics. Currently, we let this pass right through
> + * to the lower filesystem and take no action ourselves.
> + */
> +static int ecryptfs_statfs(struct super_block *sb, struct kstatfs *buf)
> +{
> + int rc = 0;

Trivial, but this initialization really isn't necessary.

[..]
> +
> + ecryptfs_printk(KERN_DEBUG, "Enter\n");
> + rc = vfs_statfs(ECRYPTFS_SUPERBLOCK_TO_LOWER(sb), buf);
> + ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
> + return rc;
> +}
> +
> +/**
> + * Called to ask filesystem to change mount options. Not implemented;
> + * returns -ENOSYS every time.
> + */
> +static int ecryptfs_remount_fs(struct super_block *sb, int *flags, char *data)
> +{
> + ecryptfs_printk(KERN_DEBUG, "Enter\n");
> + ecryptfs_printk(KERN_DEBUG, "Exit\n");
> + return -ENOSYS;
> +}

This may also be trivial, but why have place holder code when you could
simply set ecryptfs_ops.remount_fs function ptr to NULL? Besides...
wouldn't -ENOTSUPP be a better error code here? Maybe not.

[..]
> +
> +/**
> + * 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.
> + */
> +static void ecryptfs_clear_inode(struct inode *inode)
> +{
> + ecryptfs_printk(KERN_DEBUG, "Enter; inode = [%p]; i_ino = [0x%.16x]\n",
> + inode, inode->i_ino);
> + iput(ECRYPTFS_INODE_TO_LOWER(inode));
> + ecryptfs_printk(KERN_DEBUG, "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.
> + */
> +static void ecryptfs_umount_begin(struct vfsmount *vfsmnt, int flags)
> +{
> + struct vfsmount *lower_mnt;
> + struct super_block *lower_sb;
> +
> + ecryptfs_printk(KERN_DEBUG, "Enter\n");
> + lower_mnt = ECRYPTFS_SUPERBLOCK_TO_PRIVATE(vfsmnt->mnt_sb)->lower_mnt;
> + lower_sb = lower_mnt->mnt_sb;
> + if (lower_sb->s_op->umount_begin)
> + lower_sb->s_op->umount_begin(lower_mnt, flags);
> + ecryptfs_printk(KERN_DEBUG, "Exit\n");
> +}
> +
> +/**
> + * 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 rc = 0;
> + char *tmp = NULL;
> + char *path;
> +
> + ecryptfs_printk(KERN_DEBUG, "Enter\n");
> + tmp = (char *)__get_free_page(GFP_KERNEL);
> + if (!tmp) {
> + rc = -ENOMEM;
> + goto out;
> + }
> + path = d_path(ECRYPTFS_DENTRY_TO_LOWER(sb->s_root),
> + ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb)->lower_mnt, tmp,
> + PAGE_SIZE);

Might you check IS_ERR on path?

> + seq_printf(m, ",dir=%s", path);
> + free_page((unsigned long)tmp);
> +out:
> + ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
> + return rc;
> +}
> +
> +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
> +};

2006-05-05 16:46:48

by Timothy R. Chavez

[permalink] [raw]
Subject: Re: [PATCH 7/13: eCryptfs] Dentry operations

On Wed, 2006-05-03 at 21:39 -0600, Phillip Hellewell wrote:
> This is the 7th patch in a series of 13 constituting the kernel
> components of the eCryptfs cryptographic filesystem.
>
> eCryptfs dentry operations.
>
> Signed-off-by: Phillip Hellewell <[email protected]>
> Signed-off-by: Michael Halcrow <[email protected]>
>

Hello,

Just two really really trivial comments for you all.

-tim

> ---
>
> dentry.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 files changed, 91 insertions(+)
>
> Index: linux-2.6.17-rc3-mm1-ecryptfs/fs/ecryptfs/dentry.c
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ linux-2.6.17-rc3-mm1-ecryptfs/fs/ecryptfs/dentry.c 2006-05-02 19:36:00.000000000 -0600
> @@ -0,0 +1,91 @@
> +/**
> + * eCryptfs: Linux filesystem encryption layer
> + *
> + * Copyright (C) 1997-2003 Erez Zadok
> + * Copyright (C) 2001-2003 Stony Brook University
> + * Copyright (C) 2004-2006 International Business Machines Corp.
> + * Author(s): Michael A. Halcrow <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of the
> + * License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
> + * 02111-1307, USA.
> + */
> +
> +#include <linux/dcache.h>
> +#include <linux/namei.h>
> +#include "ecryptfs_kernel.h"
> +
> +/**
> + * Called when the VFS needs to revalidate a dentry. This
> + * is called whenever a name lookup finds a dentry in the
> + * dcache. Most filesystems leave this as NULL, because all their
> + * dentries in the dcache are valid.
> + *
> + * @param dentry ecryptfs dentry
> + * @param nd
> + * @return 1 if valid, 0 otherwise
> + */
> +static int ecryptfs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
> +{
> + int err = 1;
> + struct dentry *lower_dentry;
> + struct dentry *saved_dentry;
> + struct vfsmount *saved_vfsmount;
> +
> + ecryptfs_printk(KERN_DEBUG, "Enter; dentry->d_name.name = [%s]\n",
> + dentry->d_name.name);
> + lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(dentry);
> + if (!lower_dentry) {
> + err = 0;
> + goto out;
> + }
> + if (!lower_dentry->d_op || !lower_dentry->d_op->d_revalidate)
> + goto out;
> + saved_dentry = nd->dentry;
> + saved_vfsmount = nd->mnt;
> + nd->dentry = lower_dentry;
> + nd->mnt = ECRYPTFS_SUPERBLOCK_TO_PRIVATE(dentry->d_sb)->lower_mnt;
> + err = lower_dentry->d_op->d_revalidate(lower_dentry, nd);
> + nd->dentry = saved_dentry;
> + nd->mnt = saved_vfsmount;
> +out:
> + ecryptfs_printk(KERN_DEBUG, "Exit; err = [%d]\n", err);
> + return err;
> +}
> +
> +kmem_cache_t *ecryptfs_dentry_info_cache;

Can this be made static?

[..]
> +
> +/**
> + * Called when a dentry is really deallocated.
> + */
> +static void ecryptfs_d_release(struct dentry *dentry)
> +{
> + struct dentry *lower_dentry;
> +
> + ecryptfs_printk(KERN_DEBUG, "Enter; dentry->d_name->name = [%s]\n",
> + dentry->d_name.name);

Such a stupid thing to point out, I know, but ecryptfs_printk reports
dentry->d_name->name, when it should be dentry->d_name.name *cough* :)

> + lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(dentry);
> + if (ECRYPTFS_DENTRY_TO_PRIVATE(dentry))
> + kmem_cache_free(ecryptfs_dentry_info_cache,
> + ECRYPTFS_DENTRY_TO_PRIVATE(dentry));
> + if (lower_dentry)
> + dput(lower_dentry);
> + ecryptfs_printk(KERN_DEBUG, "Exit\n");
> + return;
> +}
> +
> +struct dentry_operations ecryptfs_dops = {
> + .d_revalidate = ecryptfs_d_revalidate,
> + .d_release = ecryptfs_d_release,
> +};
> -
> To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2006-05-05 18:55:06

by Timothy R. Chavez

[permalink] [raw]
Subject: Re: [PATCH 8/13: eCryptfs] File operations

On Wed, 2006-05-03 at 21:39 -0600, Phillip Hellewell wrote:
> This is the 8th patch in a series of 13 constituting the kernel
> components of the eCryptfs cryptographic filesystem.
>
> eCryptfs file operations. Includes code to read header information
> from the underyling file when needed.
>
> Signed-off-by: Phillip Hellewell <[email protected]>
> Signed-off-by: Michael Halcrow <[email protected]>
>

Hello,

Just a few more little comments.

-tim

> ---
>
> file.c | 642 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 files changed, 642 insertions(+)
>
> Index: linux-2.6.17-rc3-mm1-ecryptfs/fs/ecryptfs/file.c
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ linux-2.6.17-rc3-mm1-ecryptfs/fs/ecryptfs/file.c 2006-05-02 19:36:01.000000000 -0600
> @@ -0,0 +1,642 @@
> +/**
> + * eCryptfs: Linux filesystem encryption layer
> + *
> + * Copyright (C) 1997-2004 Erez Zadok
> + * Copyright (C) 2001-2004 Stony Brook University
> + * Copyright (C) 2004-2006 International Business Machines Corp.
> + * Author(s): Michael A. Halcrow <[email protected]>
> + * Michael C. Thompson <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of the
> + * License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
> + * 02111-1307, USA.
> + */
> +
> +#include <linux/file.h>
> +#include <linux/poll.h>
> +#include <linux/mount.h>
> +#include <linux/pagemap.h>
> +#include <linux/security.h>
> +#include <linux/smp_lock.h>
> +#include <linux/compat.h>
> +#include "ecryptfs_kernel.h"
> +
> +/**
> + * @param file File we are seeking in
> + * @param offset The offset to seek to
> + * @param origin 2: offset from i_size; 1: offset from f_pos
> + * @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 rv;
> + loff_t new_end_pos;
> + int rc;
> + int expanding_file = 0;
> + struct inode *inode = file->f_mapping->host;
> +
> + ecryptfs_printk(KERN_DEBUG, "Enter; offset = [0x%.16x]\n", offset);
> + ecryptfs_printk(KERN_DEBUG, "origin = [%d]\n", origin);
> + ecryptfs_printk(KERN_DEBUG, "inode w/ addr = [0x%p], i_ino = [0x%.16x] "
> + "size: [0x%.16x]\n", inode, inode->i_ino,
> + i_size_read(inode));
> + /* If our offset is past the end of our file, we're going to
> + * need to grow it so we have a valid length of 0's */
> + new_end_pos = offset;
> + switch (origin) {
> + case 2:
> + new_end_pos += i_size_read(inode);
> + expanding_file = 1;
> + break;
> + case 1:
> + new_end_pos += file->f_pos;
> + if (new_end_pos > i_size_read(inode)) {
> + ecryptfs_printk(KERN_DEBUG, "new_end_pos(=[0x%.16x]) "
> + "> i_size_read(inode)(=[0x%.16x])\n",
> + new_end_pos, i_size_read(inode));
> + expanding_file = 1;
> + }
> + break;
> + default:
> + if (new_end_pos > i_size_read(inode)) {
> + ecryptfs_printk(KERN_DEBUG, "new_end_pos(=[0x%.16x]) "
> + "> i_size_read(inode)(=[0x%.16x])\n",
> + new_end_pos, i_size_read(inode));
> + expanding_file = 1;
> + }
> + }
> + ecryptfs_printk(KERN_DEBUG, "new_end_pos = [0x%.16x]\n", new_end_pos);
> + if (expanding_file) {
> + rc = ecryptfs_truncate(file->f_dentry, new_end_pos);
> + if (rc) {
> + rv = rc;
> + ecryptfs_printk(KERN_ERR, "Error on attempt to "
> + "truncate to (higher) offset [0x%.16x];"
> + " rc = [%d]\n", rc, new_end_pos);
> + goto out;
> + }
> + }
> + rv = generic_file_llseek(file, offset, origin);
> +out:
> + ecryptfs_printk(KERN_DEBUG, "Exit; rv = [0x%.16x]\n", rv);
> + return rv;
> +}

Maybe get rid of 'rc' and rename 'rv' to 'rc'? Makes things look a bit
more consistent...

[..]
> +
> +/**
> + * 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 rc = 0;

Initialization not needed.

[..]
> + struct dentry *lower_dentry;
> + struct vfsmount *lower_vfsmount;
> +
> + ecryptfs_printk(KERN_DEBUG, "Enter\n");
> + rc = generic_file_read(file, buf, count, ppos);
> + if (rc >= 0) {
> + lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(file->f_dentry);
> + lower_vfsmount = ECRYPTFS_SUPERBLOCK_TO_PRIVATE(
> + file->f_dentry->d_inode->i_sb)->lower_mnt;
> + touch_atime(lower_vfsmount, lower_dentry);
> + }
> + ecryptfs_printk(KERN_DEBUG, "Exit\n");
> + return rc;
> +}
> +
> +struct ecryptfs_getdents_callback {
> + void *dirent;
> + struct dentry *dentry;
> + filldir_t filldir;
> + int err;
> + int filldir_called;
> + int entries_written;
> +};
> +
> +/* Inspired by 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_stat *crypt_stat;
> + struct ecryptfs_getdents_callback *buf =
> + (struct ecryptfs_getdents_callback *)dirent;
> + int rc;
> + char *decoded_name;
> + int decoded_length;
> +
> + ecryptfs_printk(KERN_DEBUG, "Enter w/ name = [%.*s]\n", namelen,
> + name);
> + crypt_stat = ECRYPTFS_DENTRY_TO_PRIVATE(buf->dentry)->crypt_stat;
> + buf->filldir_called++;
> + decoded_length = ecryptfs_decode_filename(crypt_stat, name, namelen,
> + &decoded_name);
> + if (decoded_length < 0)
> + return 0;

Is it wise to potentially toss away the -ENOMEM being returned from
ecryptfs_decode_filename... Might consider propagating that error??

[..]
>
> + rc = buf->filldir(buf->dirent, decoded_name, decoded_length, offset,
> + ino, d_type);
> + kfree(decoded_name);
> + if (rc >= 0)
> + buf->entries_written++;
> + ecryptfs_printk(KERN_DEBUG, "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;

Neither initialization not needed.

[..]
> + struct inode *inode;
> + struct ecryptfs_getdents_callback buf;
> +
> + ecryptfs_printk(KERN_DEBUG, "Enter; file = [%p]\n", file);
> + lower_file = ECRYPTFS_FILE_TO_LOWER(file);
> + inode = file->f_dentry->d_inode;
> + memset(&buf, 0, sizeof(buf));
> + 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(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
> + return rc;
> +}
> +
> +/**
> + * @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(KERN_DEBUG, "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(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(KERN_ERR, "Error reading header page\n");
> + goto out_unmap;
> + }
> + memcpy(&data_size, header_virt, sizeof(data_size));
> + data_size = be64_to_cpu(data_size);
> + i_size_write(inode, (loff_t)data_size);
> + ecryptfs_printk(KERN_DEBUG, "inode w/ addr = [0x%p], i_ino = [0x%.16x] "
> + "size: [0x%.16x]\n", inode, inode->i_ino,
> + i_size_read(inode));
> +out_unmap:
> + kunmap(header_page);
> + page_cache_release(header_page);
> +out:
> + ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
> + return rc;
> +}
> +
> +kmem_cache_t *ecryptfs_file_info_cache;

Static?

[..]
> +
> +/**
> + * 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_stat *crypt_stat = NULL;
> + struct dentry *ecryptfs_dentry = file->f_dentry;
> + struct dentry *lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(ecryptfs_dentry);
> + struct inode *lower_inode = NULL;
> + struct file *lower_file = NULL;
> + struct vfsmount *lower_mnt;
> + int lower_flags;
> +
> + ecryptfs_printk(KERN_DEBUG, "Enter\n");
> + ecryptfs_printk(KERN_DEBUG, "inode w/ addr = [0x%p], i_ino = [0x%.16x] "
> + "size: [0x%.16x]\n", inode, inode->i_ino,
> + i_size_read(inode));
> + ecryptfs_printk(KERN_DEBUG, "file->f_dentry = [%p], "
> + "file->f_dentry->d_name.name = [%s], "
> + "file->f_dentry->d_name.len = [%d]\n",
> + ecryptfs_dentry, ecryptfs_dentry->d_name.name,
> + ecryptfs_dentry->d_name.len);
> + /* ECRYPTFS_DENTRY_TO_PRIVATE(ecryptfs_dentry) Allocated in
> + * ecryptfs_lookup() */
> + /* Released in ecryptfs_release or end of function if failure */
> + ECRYPTFS_FILE_TO_PRIVATE_SM(file) =
> + kmem_cache_alloc(ecryptfs_file_info_cache, SLAB_KERNEL);
> + if (!ECRYPTFS_FILE_TO_PRIVATE_SM(file)) {
> + ecryptfs_printk(KERN_ERR,
> + "Error attempting to allocate memory\n");
> + rc = -ENOMEM;
> + goto out;
> + }
> + lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(ecryptfs_dentry);
> + crypt_stat = &(ECRYPTFS_INODE_TO_PRIVATE(inode)->crypt_stat);
> + if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_POLICY_APPLIED)) {
> + ecryptfs_printk(KERN_DEBUG, "Setting flags for stat...\n");
> + /* Policy code enabled in future release */
> + ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_POLICY_APPLIED);
> + ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED);
> + }
> + /* This mntget & dget is undone via fput when the file is released */
> + dget(lower_dentry);
> + lower_flags = file->f_flags;
> + if ((lower_flags & O_ACCMODE) == O_WRONLY)
> + lower_flags = (lower_flags & O_ACCMODE) | O_RDWR;
> + if (file->f_flags & O_APPEND)
> + lower_flags &= ~O_APPEND;
> + lower_mnt = ECRYPTFS_SUPERBLOCK_TO_PRIVATE(inode->i_sb)->lower_mnt;
> + mntget(lower_mnt);
> + /* Corresponding fput() in ecryptfs_release() */
> + lower_file = dentry_open(lower_dentry, lower_mnt, lower_flags);
> + if (IS_ERR(lower_file)) {
> + rc = PTR_ERR(lower_file);
> + ecryptfs_printk(KERN_ERR, "Error opening lower file\n");
> + goto out_puts;
> + }
> + ECRYPTFS_FILE_TO_LOWER(file) = lower_file;
> + /* Isn't this check the same as the one in lookup? */
> + lower_inode = lower_dentry->d_inode;
> + if (S_ISDIR(ecryptfs_dentry->d_inode->i_mode)) {
> + ecryptfs_printk(KERN_DEBUG, "This is a directory\n");
> + ECRYPTFS_CLEAR_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED);
> + rc = 0;
> + goto out;
> + }
> + if (i_size_read(lower_inode) == 0) {
> + ecryptfs_printk(KERN_EMERG, "Zero-length lower file; "
> + "ecryptfs_create() had a problem?\n");
> + rc = -ENOENT;
> + goto out_puts;
> + } else if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags,
> + ECRYPTFS_POLICY_APPLIED)
> + || !ECRYPTFS_CHECK_FLAG(crypt_stat->flags,
> + ECRYPTFS_KEY_VALID)) {
> + rc = ecryptfs_read_headers(ecryptfs_dentry, lower_file);
> + if (rc) {
> + ecryptfs_printk(KERN_DEBUG,
> + "Valid headers not found\n");
> + ECRYPTFS_CLEAR_FLAG(crypt_stat->flags,
> + ECRYPTFS_ENCRYPTED);
> + /* At this point, we could just move on and
> + * have the encrypted data passed through
> + * as-is to userspace. For release 0.1, we are
> + * going to default to -EIO. */
> + rc = -EIO;
> + goto out_puts;
> + } else
> + read_inode_size_from_header(lower_file, lower_inode,
> + inode);
> + }
> + ecryptfs_printk(KERN_DEBUG, "inode w/ addr = [0x%p], i_ino = [0x%.16x] "
> + "size: [0x%.16x]\n", inode, inode->i_ino,
> + i_size_read(inode));
> + ECRYPTFS_FILE_TO_LOWER(file) = lower_file;
> + goto out;
> +out_puts:
> + mntput(lower_mnt);
> + dput(lower_dentry);
> + kmem_cache_free(ecryptfs_file_info_cache,
> + ECRYPTFS_FILE_TO_PRIVATE(file));
> +out:
> + ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
> + return rc;
> +}
> +
> +static int ecryptfs_flush(struct file *file, fl_owner_t td)
> +{
> + int rc = 0;
> + struct file *lower_file = NULL;
> +
> + ecryptfs_printk(KERN_DEBUG, "Enter; file = [%p]\n", file);
> + lower_file = ECRYPTFS_FILE_TO_LOWER(file);
> + if (lower_file->f_op && lower_file->f_op->flush)
> + rc = lower_file->f_op->flush(lower_file, td);
> + ecryptfs_printk(KERN_DEBUG, "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(KERN_DEBUG,
> + "Enter; ecryptfs_inode->i_count = [%d]\n",
> + ecryptfs_inode->i_count);
> + lower_file = ECRYPTFS_FILE_TO_LOWER(file);
> + kmem_cache_free(ecryptfs_file_info_cache,
> + ECRYPTFS_FILE_TO_PRIVATE(file));
> + lower_inode = ECRYPTFS_INODE_TO_LOWER(ecryptfs_inode);
> + fput(lower_file);
> + ecryptfs_inode->i_blocks = lower_inode->i_blocks;
> + ecryptfs_printk(KERN_DEBUG, "Exit; rc = [%d]\n", rc);
> + return rc;

What about just "return 0;" here. There's no need for 'rc'.

> +}
> +

<snip>

2006-05-06 02:22:39

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH 10/13: eCryptfs] Mmap operations

On Fri, 05 May 2006 18:38:48 +0300
Pekka Enberg <[email protected]> wrote:

> On Thu, 2006-05-04 at 22:43 +0100, David Howells wrote:
> > > When writing CacheFiles, I noticed that ext3 would occasionally unlock a page
> > > that had neither PG_uptodate nor PG_error set, and so I had to force another
> > > readpage() on it.
>
> On Fri, 2006-05-05 at 10:22 -0500, Dave Kleikamp wrote:
> > I understand this comes from the FiST package. In that code, there is a
> > comment in one of these functions explaining the second read. It would
> > be nice to have that comment in here too:
> >
> > /*
> > * call readpage() again if we returned from wait_on_page with a
> > * page that's not up-to-date; that can happen when a partial
> > * page has a few buffers which are ok, but not the whole
> > * page.
> > */
> >
> > I'm a bit surprised that this could happen.
>
> Me too. How do we know we don't end up the same way for the second read?
>

And why doesn't it cause do_generic_mapping_read() and page_cache_read() to
fail?

This is all raher fishy.

2006-05-06 16:05:54

by Michael Halcrow

[permalink] [raw]
Subject: Re: [PATCH 10/13: eCryptfs] Mmap operations

On Fri, May 05, 2006 at 07:21:48PM -0700, Andrew Morton wrote:
> > On Fri, 2006-05-05 at 10:22 -0500, Dave Kleikamp wrote:
> > > I understand this comes from the FiST package. In that code,
> > > there is a comment in one of these functions explaining the
> > > second read. It would be nice to have that comment in here too:
> > >
> > > /*
> > > * call readpage() again if we returned from wait_on_page with a
> > > * page that's not up-to-date; that can happen when a partial
> > > * page has a few buffers which are ok, but not the whole
> > > * page.
> > > */
...
> And why doesn't it cause do_generic_mapping_read() and
> page_cache_read() to fail?
>
> This is all raher fishy.

I asked Erez about this; I will try to accurately summarize his
response. He indicated that, about 5 or so years ago, when ext2/3's
block size was set to 1K or 2K, but the page size was 4K, they found
that it was possible to get a page which had some of the blocks in the
bufcache, while other blocks were not.

Their solution at the time was to just read again, and that seemed to
fix the problem for them. It was not obvious as to why things were
happening that way, and it may be the case that the second read is no
longer necessary in the current kernel.

Mike

2006-05-06 16:43:47

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH 10/13: eCryptfs] Mmap operations

On Sat, 6 May 2006 11:00:44 -0500
Michael Halcrow <[email protected]> wrote:

> On Fri, May 05, 2006 at 07:21:48PM -0700, Andrew Morton wrote:
> > > On Fri, 2006-05-05 at 10:22 -0500, Dave Kleikamp wrote:
> > > > I understand this comes from the FiST package. In that code,
> > > > there is a comment in one of these functions explaining the
> > > > second read. It would be nice to have that comment in here too:
> > > >
> > > > /*
> > > > * call readpage() again if we returned from wait_on_page with a
> > > > * page that's not up-to-date; that can happen when a partial
> > > > * page has a few buffers which are ok, but not the whole
> > > > * page.
> > > > */
> ...
> > And why doesn't it cause do_generic_mapping_read() and
> > page_cache_read() to fail?
> >
> > This is all raher fishy.
>
> I asked Erez about this; I will try to accurately summarize his
> response. He indicated that, about 5 or so years ago, when ext2/3's
> block size was set to 1K or 2K, but the page size was 4K, they found
> that it was possible to get a page which had some of the blocks in the
> bufcache, while other blocks were not.

hm, OK, I'm not sure that Linux buffered file contents in that manner in
that timeframe. Maybe it did, before my time..

> Their solution at the time was to just read again, and that seemed to
> fix the problem for them. It was not obvious as to why things were
> happening that way, and it may be the case that the second read is no
> longer necessary in the current kernel.
>

Yeah, it shouldn't be needed now. It'd be a howler of a bug if it is
needed.

Note that the pagefault handlers do still do a second readpage(). The
comment implies that this is an open-coded attempt to recover from an I/O
error. I do recall that a year or so ago we discussed taking out that
second readpage attempt, but Linus had good-sounding reasons for keeping
it. But I forget what they were. Perhaps he can remind me?

2006-05-06 16:58:06

by Linus Torvalds

[permalink] [raw]
Subject: Re: [PATCH 10/13: eCryptfs] Mmap operations



On Sat, 6 May 2006, Andrew Morton wrote:
>
> Note that the pagefault handlers do still do a second readpage(). The
> comment implies that this is an open-coded attempt to recover from an I/O
> error. I do recall that a year or so ago we discussed taking out that
> second readpage attempt, but Linus had good-sounding reasons for keeping
> it. But I forget what they were. Perhaps he can remind me?

All the non-readahead read paths - not just page faults, but certainly
things like the generic file read routines - will do (at least they
_should_ do) a "readpage()" if they find a page that is not up-to-date
after they've gotten the lock.

That's basically just what the PG_uptodate flag means. If that flag isn't
set, you need to ->readpage() the contents, whether you allocated the page
yourself or not.

But nobody should ever do two readpages on their OWN. If readpage() fails,
you should return -EIO (or whatever), and the page will be left
not-up-to-date. But you do need to be able to accept the fact that a
_previous_ read-page failed.

And yes, it happens in practice. Networked filesystems, for example. The
previous person to try to read might have gotten a permission error. The
same is true of any kind of security scheme where the "read()" checks may
not match the "open()" security.

It's also true of read errors. You don't want to have the kernel re-try
them forever, but on the other hand, you do NOT want to keep the page as
an "error" forever without trying again. The read error could have been
because the user had removed the media (or not closed the door properly),
or anything else that the user could actually fix manually, and re-do the
operation, and it would work.

Not all read errors are final. So we shouldn't consider them final.

Linus