All of lore.kernel.org
 help / color / mirror / Atom feed
* Re: [ANNOUNCE] libata EH/NCQ/hotplug/PM git tree
From: Tejun Heo @ 2006-05-13  3:59 UTC (permalink / raw)
  To: Jeff Garzik
  Cc: Alan Cox, Albert Lee, Jens Axboe, Edward Falk, Carlos Pardo,
	Raymond Liu, linux-ide@vger.kernel.org
In-Reply-To: <44655496.9030402@pobox.com>

Jeff Garzik wrote:
> Tejun Heo wrote:
>> Jeff Garzik wrote:
>>> Tejun Heo wrote:
>>>> tested.  I implemented AHCI PM support half-way but then realized I 
>>>> don't have any AHCI controller which supports PM (I have ICH7R).  Is 
>>>> there any AHCI controller which reports PM support?
>>>
>>> You can always use SCR-based PM support...
>>>
>>
>> Can you elaborate a bit?
> 
> Sections A.1 and 6.1 of the PM spec.  You set the PMP field of the 
> SControl register.  This has obvious parallelism limitations, which is 
> why "hardware context switching support" aka native PM support is 
> greatly preferred.
> 

That cmd-switching PM support is what AHCI SPM bit indicates.  As of 
v1.1, AHCI doesn't define FIS-switching PM support yet.

Supports Port Multiplier (SPM): Indicates whether the HBA can support a 
Port Multiplier. When set, a Port Multiplier using command-based 
switching is supported. When cleared to ‘0’, a Port Multiplier is not 
supported, and a Port Multiplier may not be attached to this HBA.

And. the SControl section says...

19:16 RO 0h  Port Multiplier Port (PMP): This field is not used by AHCI.

Which is understandable considering AHCI doesn't construct FISes itself. 
  I don't think PM requires more hardware support than being capable for 
setting PMP in the FISes, which AHCI certainly can (standing down on 
collision for SDB notify aside).  I can give it a shot but it clearly 
violates the spec, and even if it works, we would need to print big fat 
warning message while attaching PM.

-- 
tejun

^ permalink raw reply

* [PATCH 13/13: eCryptfs] Debug functions
From: Phillip Hellewell @ 2006-05-13  3:49 UTC (permalink / raw)
  To: Andrew Morton
  Cc: linux-kernel, linux-fsdevel, viro, mike, mhalcrow, mcthomps, toml,
	yoder1, James Morris, Stephen C. Tweedie, Erez Zadok,
	David Howells
In-Reply-To: <20060513033742.GA18598@hellewell.homeip.net>

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 <phillip@hellewell.homeip.net>
Signed-off-by: Michael Halcrow <mhalcrow@us.ibm.com>

---

 debug.c |  130 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 130 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-12 20:00:26.000000000 -0600
@@ -0,0 +1,130 @@
+/**
+ * eCryptfs: Linux filesystem encryption layer
+ * Functions only useful for debugging.
+ *
+ * Copyright (C) 2006 International Business Machines Corp.
+ *   Author(s): Michael A. Halcrow <mahalcro@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "ecryptfs_kernel.h"
+
+/**
+ * ecryptfs_dump_auth_tok - debug function to print auth toks
+ * 
+ * This function will print the contents of an ecryptfs authentication
+ * token.
+ */
+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);
+	}
+}
+
+/**
+ * ecryptfs_dump_hex - debug hex printer
+ * @data: string of bytes to be printed
+ * @bytes: number of bytes to print
+ * 
+ * Dump hexadecimal representation of char array
+ */
+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");
+}
+

^ permalink raw reply

* [PATCH 12/13: eCryptfs] Crypto functions
From: Phillip Hellewell @ 2006-05-13  3:48 UTC (permalink / raw)
  To: Andrew Morton
  Cc: linux-kernel, linux-fsdevel, viro, mike, mhalcrow, mcthomps, toml,
	yoder1, James Morris, Stephen C. Tweedie, Erez Zadok,
	David Howells
In-Reply-To: <20060513033742.GA18598@hellewell.homeip.net>

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 <phillip@hellewell.homeip.net>
Signed-off-by: Michael Halcrow <mhalcrow@us.ibm.com>

---

 crypto.c | 1508 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 1508 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-12 20:00:25.000000000 -0600
@@ -0,0 +1,1508 @@
+/**
+ * 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 <mahalcro@us.ibm.com>
+ *   		Michael C. Thompson <mcthomps@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <linux/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"
+
+/**
+ * ecryptfs_to_hex
+ * @dst: Buffer to take hex character representation of contents of
+ *       src; must be at least of size (src_size * 2)
+ * @src: Buffer to be converted to a hex string respresentation
+ * @src_size: number of bytes to convert
+ */
+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]);
+}
+
+/**
+ * ecryptfs_from_hex
+ * @dst: Buffer to take the bytes from src hex; must be at least of
+ *       size (src_size / 2)
+ * @src: Buffer to be converted from a hex string respresentation to raw value
+ * @dst_size: size of dst buffer, or number of hex characters pairs to convert
+ */
+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);
+	}
+}
+
+/**
+ * ecryptfs_calculate_md5 - calculates the md5 of @src
+ * @dst: Pointer to 16 bytes of allocated memory
+ * @crypt_stat: Pointer to crypt_stat struct for the current inode
+ * @src: Data to be md5'd
+ * @len: Length of @src
+ * 
+ * Uses the allocated crypto context that crypt_stat references to
+ * generate the MD5 sum of the contents of src.
+ */
+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;
+}
+
+/**
+ * ecryptfs_derive_iv
+ * @iv: destination for the derived iv vale
+ * @crypt_stat: Pointer to crypt_stat struct for the current inode
+ * @offset: Offset of the page whose's iv we are to derive
+ * 
+ * Generate the initialization vector from the given root IV and page
+ * offset.
+ * 
+ * Returns 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];
+
+	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;
+}
+
+/**
+ * ecryptfs_init_crypt_stat
+ * @crypt_stat: Pointer to the crypt_stat struct to initialize.
+ *
+ * Initialize the crypt_stat structure.
+ */
+void ecryptfs_init_crypt_stat(struct ecryptfs_crypt_stat *crypt_stat)
+{
+	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_destruct_crypt_stat
+ * @crypt_stat: Pointer to the crypt_stat struct to initialize.
+ *
+ * Releases all memory associated with a crypt_stat struct.
+ */
+void ecryptfs_destruct_crypt_stat(struct ecryptfs_crypt_stat *crypt_stat)
+{
+	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));
+}
+
+/**
+ * virt_to_scatterlist
+ * @addr: Virtual address
+ * @size: Size of data; should be an even multiple of the block size
+ * @sg: Pointer to scatterlist array; set to NULL to obtain only
+ *      the number of scatterlist structs required in array
+ * @sg_size: Max array size
+ *
+ * Fills in a scatterlist array with page references for a passed
+ * virtual address.
+ *
+ * Returns the 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;
+}
+
+/**
+ * encrypt_scatterlist
+ * @crypt_stat: Pointer to the crypt_stat struct to initialize.
+ * @dest_sg: Destination of encrypted data
+ * @src_sg: Data to be encrypted
+ * @size: Length of data to be encrypted
+ * @iv: iv to use during encryption
+ * 
+ * Returns the 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;
+
+	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;
+
+	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;
+
+	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;
+}
+
+/**
+ * ecryptfs_encrypt_page
+ * @ctx: The context of the page
+ * 
+ * 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.
+ *
+ * Returns 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_inode_info *inode_info;
+	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;
+
+	lower_inode = ecryptfs_inode_to_lower(ctx->page->mapping->host);
+	inode_info = ecryptfs_inode_to_private(ctx->page->mapping->host);
+	crypt_stat = &inode_info->crypt_stat;
+	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;
+}
+
+/**
+ * ecryptfs_decrypt_page
+ * @file: The ecryptfs file
+ * @page: The page in ecryptfs to decrypt
+ * 
+ * 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.
+ *
+ * Returns 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;
+
+	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;
+}
+
+/**
+ * decrypt_scatterlist
+ * 
+ * Returns the 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;
+
+	/* 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:
+	return rc;
+}
+
+/**
+ * ecryptfs_encrypt_page_offset
+ * 
+ * Returns the 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;
+
+	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);
+}
+
+/**
+ * ecryptfs_decrypt_page_offset
+ * 
+ * Returns the 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;
+
+	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
+
+/**
+ * ecryptfs_init_crypt_ctx
+ * @crypt_stat: Uninitilized crypt stats structure
+ * 
+ * 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;
+
+	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:
+	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)
+{
+	/* 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_compute_root_iv
+ * @crypt_stats
+ *
+ * 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;
+}
+
+/**
+ * ecryptfs_set_default_crypt_stat_vals
+ * @crypt_stat
+ *
+ * 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_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_new_file_context
+ * @ecryptfs_dentry
+ * 
+ * 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.
+ *
+ * Returns 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_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);
+	return rc;
+}
+
+/**
+ * contains_ecryptfs_marker - check for the ecryptfs marker
+ * @data: The data block in which to check
+ * 
+ * Returns 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}
+};
+
+/**
+ * ecryptfs_process_flags
+ * @crypt_stat
+ * @page_virt: Source data to be parsed
+ * @bytes_read: Updated with the number of bytes read
+ *
+ * Returns 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;
+}
+
+/**
+ * write_ecryptfs_marker
+ * @page_virt: The pointer to in a page to begin writing the marker
+ * @written: Number of bytes written
+ *
+ * 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}
+};
+
+/**
+ * ecryptfs_code_for_cipher_string
+ * @str: The string representing the cipher name
+ * 
+ * Returns zero on no match, or the cipher code on 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;
+}
+
+/**
+ * ecryptfs_cipher_code_to_string
+ * @str: Destination to write out the cipher name
+ * @cipher_code: The code to convert to cipher name string
+ * 
+ * Returns 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;
+}
+
+/**
+ * ecryptfs_read_header_region
+ * @data
+ * @dentry
+ * @nd
+ * 
+ * Returns 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;
+
+	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:
+	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;
+}
+
+struct kmem_cache *ecryptfs_header_cache_0;
+struct kmem_cache *ecryptfs_header_cache_1;
+struct kmem_cache *ecryptfs_header_cache_2;
+
+/**
+ * ecryptfs_write_headers_virt
+ * @page_virt
+ * @crypt_stat
+ * @ecryptfs_dentry
+ *
+ * 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)
+ *   ...
+ *
+ * Returns 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;
+
+	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;
+}
+
+/**
+ * ecryptfs_write_headers
+ * @lower_file: The lower file struct, which was returned from dentry_open
+ * 
+ * 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.
+ *
+ * Returns 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;
+
+	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:
+	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;
+}
+
+/**
+ * set_default_header_data
+ *
+ * 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;
+}
+
+/**
+ * ecryptfs_read_headers_virt
+ *
+ * Read/parse the header data. The header format is detailed in the
+ * comment block for the ecryptfs_write_headers_virt() function.
+ *
+ * Returns 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_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;
+}
+
+/**
+ * ecryptfs_read_headers
+ * 
+ * Returns 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;
+
+	/* 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);
+	}
+	return rc;
+}
+
+/**
+ * ecryptfs_encode_filename - converts a plaintext file name to cipher text
+ * @crypt_stat: The crypt_stat struct associated with the file anem to encode
+ * @name: The plaintext name
+ * @length: The length of the plaintext
+ * @encoded_name: The encypted name
+ * 
+ * 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.
+ *
+ * Returns the 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;
+
+	(*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:
+	return error;
+}
+
+/**
+ * ecryptfs_decode_filename - converts the cipher text name to plaintext
+ * @crypt_stat: The crypt_stat struct associated with the file
+ * @name: The filename in cipher text
+ * @length: The length of the cipher text name
+ * @decrypted_name: The plaintext name
+ * 
+ * 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.
+ *
+ * Returns the 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;
+
+	(*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:
+	return error;
+}

^ permalink raw reply

* [PATCH 11/13: eCryptfs] Keystore
From: Phillip Hellewell @ 2006-05-13  3:47 UTC (permalink / raw)
  To: Andrew Morton
  Cc: linux-kernel, linux-fsdevel, viro, mike, mhalcrow, mcthomps, toml,
	yoder1, James Morris, Stephen C. Tweedie, Erez Zadok,
	David Howells
In-Reply-To: <20060513033742.GA18598@hellewell.homeip.net>

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 <phillip@hellewell.homeip.net>
Signed-off-by: Michael Halcrow <mhalcrow@us.ibm.com>

---

 keystore.c | 1043 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 1043 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-12 20:00:28.000000000 -0600
@@ -0,0 +1,1043 @@
+/**
+ * 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 <mhalcrow@us.ibm.com>
+ *              Michael C. Thompson <mcthomps@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/syscalls.h>
+#include <linux/pagemap.h>
+#include <linux/key.h>
+#include <linux/random.h>
+#include <linux/crypto.h>
+#include <asm/scatterlist.h>
+#include "ecryptfs_kernel.h"
+
+/**
+ * request_key returned an error instead of a valid key address;
+ * determine the type of error, make appropriate log entries, and
+ * return an error code.
+ */
+int process_request_key_err(long err_code)
+{
+	int rc = 0;
+
+	switch (err_code) {
+	case ENOKEY:
+		ecryptfs_printk(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;
+
+	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);
+	}
+}
+
+struct kmem_cache *ecryptfs_auth_tok_list_item_cache;
+
+/**
+ * parse_packet_length
+ * @data: Pointer to memory containing length at offset
+ * @size: This function writes the decoded size to this memory
+ *        address; zero on error
+ * @length_size: The number of bytes occupied by the encoded length
+ *
+ * Returns 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;
+}
+
+/**
+ * write_packet_length
+ * @dest: The byte array target into which to write the
+ *       length. Must have at least 5 bytes allocated.
+ * @size: The length to write.
+ * @packet_size_length: The number of bytes used to encode the
+ *                      packet length is written to this address.
+ *
+ * Returns 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_tag_3_packet
+ * @crypt_stat: The cryptographic context to modify based on packet
+ *              contents.
+ * @data: The raw bytes of the packet.
+ * @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.
+ * @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.
+ * @packet_size: This function writes the size of the parsed packet
+ *               into this memory location; zero on error.
+ *
+ * Returns 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;
+
+	(*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;
+	return rc;
+}
+
+/**
+ * parse_tag_11_packet
+ * @data: The raw bytes of the packet
+ * @contents: This function writes the data contents of the literal
+ *            packet into this memory location
+ * @max_contents_bytes: The maximum number of bytes that this function
+ *                      is allowed to write into contents
+ * @tag_11_contents_size: This function writes the size of the parsed
+ *                        contents into this memory location; zero on
+ *                        error
+ * @packet_size: This function writes the size of the parsed packet
+ *               into this memory location; zero on error
+ *
+ * Returns 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_session_key - Decrypt the session key with the given auth_tok.
+ *
+ * Returns Zero 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;
+}
+
+/**
+ * ecryptfs_parse_packet_set
+ * @dest: The header page in memory
+ * @version: Version of file format, to guide parsing behavior
+ *
+ * Get crypt_stat to have the file's session key if the requisite key
+ * is available to decrypt the session key.
+ *
+ * Returns 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;
+
+	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:
+	return rc;
+}
+
+/**
+ * write_tag_11_packet
+ * @dest: Target into which Tag 11 packet is to be written
+ * @max: Maximum packet length
+ * @contents: Byte array of contents to copy in
+ * @contents_length: Number of bytes in contents
+ * @packet_length: Length of the Tag 11 packet written; zero on error
+ *
+ * Returns 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;
+
+	(*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;
+	return rc;
+}
+
+/**
+ * write_tag_3_packet
+ * @dest: Buffer into which to write the packet
+ * @max: Maximum number of bytes that can be writtn
+ * @packet_size: This function will write the number of bytes that end
+ *               up constituting the packet; set to zero on error
+ *
+ * Returns 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;
+
+	(*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;
+	return rc;
+}
+
+/**
+ * ecryptfs_generate_key_packet_set
+ * @dest: Virtual address from which to write the key record set
+ * @crypt_stat: The cryptographic context from which the
+ *              authentication tokens will be retrieved
+ * @ecryptfs_dentry: The dentry, used to retrieve the mount crypt stat
+ *                   for the global parameters
+ * @len: The amount written
+ *
+ * Generates a key packet set and writes it to the virtual address
+ * passed in.
+ *
+ * Returns zero on success; non-zero on error.
+ */
+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;
+
+	(*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;
+	return rc;
+}

^ permalink raw reply

* [PATCH 10/13: eCryptfs] Mmap operations
From: Phillip Hellewell @ 2006-05-13  3:47 UTC (permalink / raw)
  To: Andrew Morton
  Cc: linux-kernel, linux-fsdevel, viro, mike, mhalcrow, mcthomps, toml,
	yoder1, James Morris, Stephen C. Tweedie, Erez Zadok,
	David Howells
In-Reply-To: <20060513033742.GA18598@hellewell.homeip.net>

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 <phillip@hellewell.homeip.net>
Signed-off-by: Michael Halcrow <mhalcrow@us.ibm.com>

---

 mmap.c |  796 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 796 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-12 20:00:29.000000000 -0600
@@ -0,0 +1,796 @@
+/**
+ * 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 <mahalcro@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <linux/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"
+
+struct kmem_cache *ecryptfs_lower_page_cache;
+
+/**
+ * ecryptfs_get1page
+ *
+ * Get one page from cache or lower f/s, return error otherwise.
+ *
+ * Returns 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;
+
+	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);
+out:
+	return page;
+}
+
+static
+int write_zeros(struct file *file, pgoff_t index, int start, int num_zeros);
+
+/**
+ * ecryptfs_fill_zeros
+ * @file: The ecryptfs file
+ * @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
+ *
+ * Function for handling lseek-ing past the end of the file.
+ *
+ * This function does not support shrinking, only growing a file.
+ *
+ * Returns 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);
+
+	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:
+	return rc;
+}
+
+/**
+ * ecryptfs_writepage
+ * @page: Page that is locked before this call is made
+ *
+ * Returns zero on success; non-zero otherwise
+ */
+static int ecryptfs_writepage(struct page *page, struct writeback_control *wbc)
+{
+	struct ecryptfs_page_crypt_context ctx;
+	int rc;
+
+	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:
+	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;
+	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;
+
+	dentry = file->f_dentry;
+	if (!ecryptfs_file_to_private(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);
+	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);
+	return rc;
+}
+
+/**
+ * ecryptfs_readpage
+ * @file: This is an ecryptfs file
+ * @page: ecryptfs associated page to stick the read data into
+ *
+ * Read in a page, decrypting if necessary.
+ *
+ * Returns 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;
+
+	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);
+	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;
+
+	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;
+
+	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;
+
+	(*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;
+
+	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)
+{
+	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);
+}
+
+/**
+ * ecryptfs_write_inode_size_to_header
+ *
+ * Writes the lower file size to the first 8 bytes of the header.
+ *
+ * Returns zero on success; non-zero on error.
+ */
+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;
+
+	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:
+	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;
+
+	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;
+}
+
+/**
+ * ecryptfs_commit_lower_page
+ *
+ * Returns zero on success; non-zero on error
+ */
+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;
+
+	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;
+}
+
+/**
+ * ecryptfs_copy_page_to_lower
+ *
+ * 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;
+
+	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;
+
+	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;
+}
+
+/**
+ * ecryptfs_commit_write
+ * @file: The eCryptfs file object
+ * @page: The eCryptfs page
+ * @from: Ignored (we rotate the page IV on each write)
+ * @to: Ignored
+ *
+ * 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.
+ */
+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;
+
+	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);
+	return rc;
+}
+
+/**
+ * write_zeros
+ * @file: The ecryptfs file
+ * @index: The index in which we are writing
+ * @start: The position after the last block of data
+ * @num_zeros: The number of zeros to write
+ *
+ * Write a specified number of zero's to a page.
+ * 
+ * (start + num_zeros) must be less than or equal to PAGE_CACHE_SIZE
+ */
+static
+int write_zeros(struct file *file, pgoff_t index, int start, int num_zeros)
+{
+	int rc = 0;
+	struct page *tmp_page;
+
+	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:
+	return rc;
+}
+
+static sector_t ecryptfs_bmap(struct address_space *mapping, sector_t block)
+{
+	int rc = 0;
+	struct inode *inode;
+	struct inode *lower_inode;
+
+	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 ecryptfs_sync_page(struct page *page)
+{
+	struct inode *inode;
+	struct inode *lower_inode;
+	struct page *lower_page;
+
+	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;
+	}
+	lower_page->mapping->a_ops->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,
+};

^ permalink raw reply

* [PATCH 9/13: eCryptfs] Inode operations
From: Phillip Hellewell @ 2006-05-13  3:46 UTC (permalink / raw)
  To: Andrew Morton
  Cc: linux-kernel, linux-fsdevel, viro, mike, mhalcrow, mcthomps, toml,
	yoder1, James Morris, Stephen C. Tweedie, Erez Zadok,
	David Howells
In-Reply-To: <20060513033742.GA18598@hellewell.homeip.net>

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 <phillip@hellewell.homeip.net>
Signed-off-by: Michael Halcrow <mhalcrow@us.ibm.com>

---

 inode.c | 1080 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 1080 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-12 20:00:28.000000000 -0600
@@ -0,0 +1,1080 @@
+/**
+ * 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 <mahalcro@us.ibm.com>
+ *              Michael C. Thompsion <mcthomps@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <linux/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)
+{
+	i_size_write(dst, i_size_read((struct inode *)src));
+	dst->i_blocks = src->i_blocks;
+}
+
+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)
+{
+	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;
+	dest->i_blkbits = src->i_blkbits;
+	dest->i_flags = src->i_flags;
+}
+
+/**
+ * ecryptfs_create_underlying_file
+ * @lower_dir_inode: inode of the parent in the lower fs of the new file
+ * @lower_dentry: New file's dentry in the lower fs
+ * @ecryptfs_dentry: New file's dentry in ecryptfs
+ * @mode: The mode of the new file
+ * @nd: nameidata of ecryptfs' parent's dentry & vfsmnt
+ * 
+ * Creates the file in the lower file system.
+ *
+ * Returns 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;
+
+	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;
+	return rc;
+}
+
+/**
+ * ecryptfs_do_create
+ * @directory_inode: inode of the new file's dentry's parent in ecryptfs
+ * @ecryptfs_dentry: New file's dentry in ecryptfs
+ * @mode: The mode of the new file
+ * @nd: nameidata of ecryptfs' parent's dentry & vfsmnt
+ * 
+ * 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.
+ *
+ * Returns 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;
+
+	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:
+	return rc;
+}
+
+/**
+ * grow_file
+ * @ecryptfs_dentry: the ecryptfs dentry
+ * @lower_file: The lower file
+ * @inode: The ecryptfs inode
+ * @lower_inode: The lower inode
+ * 
+ * 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_set_file_private(&fake_file, &tmp_file_info);
+	ecryptfs_set_file_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;
+}
+
+/**
+ * ecryptfs_initialize_file
+ * 
+ * Cause the file to be changed from a basic empty file to an ecryptfs
+ * file with a header and first data page.
+ *
+ * Returns 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;
+
+	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:
+	return rc;
+}
+
+/**
+ * ecryptfs_create
+ * @dir: The inode of the directory in which to create the file.
+ * @dentry: The eCryptfs dentry
+ * @mode: The mode of the new file.
+ * @nd: nameidata
+ * 
+ * Creates a new file.
+ *
+ * Returns 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;
+
+	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:
+	return rc;
+}
+
+/**
+ * ecryptfs_lookup
+ * @dir: inode
+ * @dentry: The dentry
+ * @nd: nameidata, may be NULL
+ *
+ * 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.
+ */
+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;
+
+	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_set_dentry_private(dentry,
+				    kmem_cache_alloc(ecryptfs_dentry_info_cache,
+						     SLAB_KERNEL));
+	if (!ecryptfs_dentry_to_private(dentry)) {
+		rc = -ENOMEM;
+		ecryptfs_printk(KERN_ERR, "Out of memory whilst attempting "
+				"to allocate ecryptfs_dentry_info struct\n");
+		goto out_dput;
+	}
+	ecryptfs_set_dentry_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:
+	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;
+
+	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);
+	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);
+
+	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);
+	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;
+
+	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;
+
+	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);
+	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;
+
+	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);
+	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;
+
+	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;
+
+	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;
+
+	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:
+	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;
+
+	/* 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));
+}
+
+/**
+ * upper_size_to_lower_size
+ * @crypt_stat: Crypt_stat associated with file
+ * @upper_size: Size of the upper file
+ *
+ * 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.
+ *
+ * Returns 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;
+
+	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);
+	}
+	return lower_size;
+}
+
+/**
+ * ecryptfs_truncate
+ * @dentry: The ecryptfs layer dentry
+ * @new_length: The length to expand the file to
+ * 
+ * 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.
+ *
+ * Returns 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;
+
+	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_set_file_private(&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_set_file_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:
+	return rc;
+}
+
+static int
+ecryptfs_permission(struct inode *inode, int mask, struct nameidata *nd)
+{
+	struct inode *lower_inode;
+	int rc = 0;
+
+	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);
+	return rc;
+}
+
+/**
+ * ecryptfs_setattr
+ * @dentry: dentry handle to the inode to modify
+ * @ia: Structure with flags of what to change and values
+ * 
+ * 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.
+ */
+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;
+
+	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);
+	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;
+
+	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:
+	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;
+
+	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:
+	return rc;
+}
+
+static ssize_t
+ecryptfs_listxattr(struct dentry *dentry, char *list, size_t size)
+{
+	int rc = 0;
+	struct dentry *lower_dentry;
+
+	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:
+	return rc;
+}
+
+static int ecryptfs_removexattr(struct dentry *dentry, const char *name)
+{
+	int rc = 0;
+	struct dentry *lower_dentry;
+
+	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:
+	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
+};

^ permalink raw reply

* Re: [PATCH 008 of 8] md/bitmap: Change md/bitmap file handling to use bmap to file blocks.
From: Neil Brown @ 2006-05-13  3:46 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-raid, linux-kernel, paul.clements
In-Reply-To: <20060512104750.0f5cb10a.akpm@osdl.org>

On Friday May 12, akpm@osdl.org wrote:
> NeilBrown <neilb@suse.de> wrote:
> >
> > If md is asked to store a bitmap in a file, it tries to hold onto the
> > page cache pages for that file, manipulate them directly, and call a
> > cocktail of operations to write the file out.  I don't believe this is
> > a supportable approach.
> 
> erk.  I think it's better than...
> 
> > This patch changes the approach to use the same approach as swap files.
> > i.e. bmap is used to enumerate all the block address of parts of the file
> > and we write directly to those blocks of the device.
> 
> That's going in at a much lower level.  Even swapfiles don't assume
> buffer_heads.

I'm not "assuming" buffer_heads.  I'm creating buffer heads and using
them for my own purposes.  These are my pages and my buffer heads.
None of them belong to the filesystem.
The buffer_heads are simply a convenient data-structure to record the
several block addresses for each page.  I could have equally created
an array storing all the addresses, and built the required bios by
hand at write time.  But buffer_heads did most of the work for me, so
I used them.

Yes, it is a lower level, but
 1/ I am certain that there will be no kmalloc problems and
 2/ Because it is exactly the level used by swapfile, I know that it
    is sufficiently 'well defined' that no-one is going to break it.

> 
> When playing with bmap() one needs to be careful that nobody has truncated
> the file on you, else you end up writing to disk blocks which aren't part
> of the file any more.

Well we currently play games with i_write_count to ensure that no-one
else has the file open for write.  And if no-one else can get write
access, then it cannot be truncated.
I did think about adding the S_SWAPFILE flag, but decided to leave
that for a separate patch and review different approaches to
preventing write access first (e.g. can I use a lease?).

> 
> All this (and a set_fs(KERNEL_DS), ug) looks like a step backwards to me. 
> Operating at the pagecache a_ops level looked better, and more
> filesystem-independent.

If you really want filesystem independence, you need to use vfs_read
and vfs_write to read/write the file.  I have a patch which did that,
but decided that the possibility of kmalloc failure at awkward times
would make that not suitable.
So I now use vfs_read to read in the file (just like knfsd) and
bmap/submit_bh to write out the file (just like swapfile).

I don't think a_ops really provides an interface that I can use, partly
because, as I said in a previous email, it isn't really a public
interface to a filesystem.

> 
> I haven't looked at this patch at all closely yet.  Do I really need to?

I assume you are asking that because you hope I will retract the
patch.  While I'm always open to being educated, I am not yet
convinced that there is any better way, or even any other usable way,
to do what needs to be done, so I am not inclined to retract the
patch.

I'd like to say that you don't need to read it because it is perfect,
but unfortunately history suggests that is unlikely to be true.

Whether you look more closely is of course up to you, but I'm convinced
that patch is in the right direction, and your review and comments are
always very valuable.

NeilBrown

^ permalink raw reply

* [PATCH 8/13: eCryptfs] File operations
From: Phillip Hellewell @ 2006-05-13  3:45 UTC (permalink / raw)
  To: Andrew Morton
  Cc: linux-kernel, linux-fsdevel, viro, mike, mhalcrow, mcthomps, toml,
	yoder1, James Morris, Stephen C. Tweedie, Erez Zadok,
	David Howells
In-Reply-To: <20060513033742.GA18598@hellewell.homeip.net>

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 <phillip@hellewell.homeip.net>
Signed-off-by: Michael Halcrow <mhalcrow@us.ibm.com>

---

 file.c |  620 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 620 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-12 20:00:27.000000000 -0600
@@ -0,0 +1,620 @@
+/**
+ * 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 <mhalcrow@us.ibm.com>
+ *   		Michael C. Thompson <mcthomps@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <linux/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"
+
+/**
+ * ecryptfs_llseek
+ * @file: File we are seeking in
+ * @offset: The offset to seek to
+ * @origin: 2 - offset from i_size; 1 - offset from f_pos
+ * 
+ * Returns 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;
+
+	/* 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:
+	return rv;
+}
+
+/**
+ * ecryptfs_read_update_atime
+ *
+ * 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;
+	struct dentry *lower_dentry;
+	struct vfsmount *lower_vfsmount;
+
+	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);
+	}
+	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;
+	int decoded_length;
+	char *decoded_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) {
+		rc = decoded_length;
+		goto out;
+	}
+	rc = buf->filldir(buf->dirent, decoded_name, decoded_length, offset,
+			  ino, d_type);
+	kfree(decoded_name);
+	if (rc >= 0)
+		buf->entries_written++;
+out:
+	return rc;
+}
+
+/**
+ * ecryptfs_readdir
+ * @file: The ecryptfs file struct
+ * @dirent: Directory entry
+ * @filldir: The filldir callback function
+ */
+static int ecryptfs_readdir(struct file *file, void *dirent, filldir_t filldir)
+{
+	int rc;
+	struct file *lower_file;
+	struct inode *inode;
+	struct ecryptfs_getdents_callback buf;
+
+	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);
+	return rc;
+}
+
+/**
+ * read_inode_size_from_header
+ * @lower_file: The lower file struct
+ * @lower_inode: The lower inode
+ * @inode: The ecryptfs inode
+ * 
+ * Returns 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;
+	struct page *header_page;
+	unsigned char *header_virt;
+	u64 data_size;
+
+	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:
+	return rc;
+}
+
+struct kmem_cache *ecryptfs_file_info_cache;
+
+/**
+ * ecryptfs_open
+ * @inode: inode speciying file to open
+ * @file: Structure to return filled in
+ *
+ * Opens the file specified by inode.
+ * 
+ * Returns 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;
+	/* Private value of ecryptfs_dentry allocated in
+	 * ecryptfs_lookup() */
+	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;
+
+	/* Released in ecryptfs_release or end of function if failure */
+	ecryptfs_set_file_private(file,
+				  kmem_cache_alloc(ecryptfs_file_info_cache,
+						   SLAB_KERNEL));
+	if (!ecryptfs_file_to_private(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_set_file_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_set_file_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:
+	return rc;
+}
+
+static int ecryptfs_flush(struct file *file, fl_owner_t td)
+{
+	int rc = 0;
+	struct file *lower_file = NULL;
+
+	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);
+	return rc;
+}
+
+static int ecryptfs_release(struct inode *ecryptfs_inode, struct file *file)
+{
+	struct file *lower_file;
+	struct ecryptfs_file_info *file_info;
+	struct inode *lower_inode;
+
+	file_info = ecryptfs_file_to_private(file);
+	kmem_cache_free(ecryptfs_file_info_cache, file_info);
+	lower_file = ecryptfs_file_to_lower(file);
+	fput(lower_file);
+	lower_inode = ecryptfs_inode_to_lower(ecryptfs_inode);
+	ecryptfs_inode->i_blocks = lower_inode->i_blocks;
+	return 0;
+}
+
+static int
+ecryptfs_fsync(struct file *file, struct dentry *dentry, int datasync)
+{
+	int rc = -EINVAL;
+	struct file *lower_file = NULL;
+	struct dentry *lower_dentry;
+
+	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 (!ecryptfs_file_to_private(file)) {
+			rc = -EINVAL;
+			ecryptfs_printk(KERN_ERR, "ecryptfs_file_to_private"
+					"(file=[%p]) is 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:
+	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;
+
+	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) {
+		rc = lower_file->f_op->lock(lower_file, cmd, fl);
+		if (rc)
+			goto out;
+		goto upper_lock;
+	}
+	rc = ecryptfs_posix_lock(lower_file, fl, cmd);
+	if (rc)
+		goto out;
+upper_lock:
+	fl->fl_file = file;
+	rc = ecryptfs_posix_lock(file, fl, cmd);
+	if (rc) {
+		fl->fl_type = F_UNLCK;
+		fl->fl_file = lower_file;
+		ecryptfs_posix_lock(lower_file, fl, cmd);
+	}
+out:
+	return rc;
+}
+
+static int ecryptfs_getlk(struct file *file, struct file_lock *fl)
+{
+	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;
+
+	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:
+	return rc;
+}
+
+static int ecryptfs_lock(struct file *file, int cmd, struct file_lock *fl)
+{
+	int rc = 0;
+	struct file *lower_file = NULL;
+
+	if (ecryptfs_file_to_private(file))
+		lower_file = ecryptfs_file_to_lower(file);
+	ASSERT(lower_file);
+	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:
+	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))
+		lower_file = ecryptfs_file_to_lower(file);
+	ASSERT(lower_file);
+	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);
+
+const 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,
+};
+
+const 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))
+		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;
+}

^ permalink raw reply

* [PATCH 7/13: eCryptfs] Dentry operations
From: Phillip Hellewell @ 2006-05-13  3:45 UTC (permalink / raw)
  To: Andrew Morton
  Cc: linux-kernel, linux-fsdevel, viro, mike, mhalcrow, mcthomps, toml,
	yoder1, James Morris, Stephen C. Tweedie, Erez Zadok,
	David Howells
In-Reply-To: <20060513033742.GA18598@hellewell.homeip.net>

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 <phillip@hellewell.homeip.net>
Signed-off-by: Michael Halcrow <mhalcrow@us.ibm.com>

---

 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-12 20:00:26.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 <mahalcro@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <linux/dcache.h>
+#include <linux/namei.h>
+#include "ecryptfs_kernel.h"
+
+/**
+ * ecryptfs_d_revalidate - revalidate an ecryptfs dentry
+ * @dentry: The ecryptfs dentry
+ * @nd: The associated nameidata
+ *
+ * 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.
+ * 
+ * Returns 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;
+
+	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:
+	return err;
+}
+
+struct kmem_cache *ecryptfs_dentry_info_cache;
+
+/**
+ * ecryptfs_d_release
+ * @dentry: The ecryptfs dentry
+ *
+ * Called when a dentry is really deallocated.
+ */
+static void ecryptfs_d_release(struct dentry *dentry)
+{
+	struct dentry *lower_dentry;
+
+	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);
+	return;
+}
+
+struct dentry_operations ecryptfs_dops = {
+	.d_revalidate = ecryptfs_d_revalidate,
+	.d_release = ecryptfs_d_release,
+};

^ permalink raw reply

* [PATCH 6/13: eCryptfs] Superblock operations
From: Phillip Hellewell @ 2006-05-13  3:44 UTC (permalink / raw)
  To: Andrew Morton
  Cc: linux-kernel, linux-fsdevel, viro, mike, mhalcrow, mcthomps, toml,
	yoder1, James Morris, Stephen C. Tweedie, Erez Zadok,
	David Howells
In-Reply-To: <20060513033742.GA18598@hellewell.homeip.net>

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 <phillip@hellewell.homeip.net>
Signed-off-by: Michael Halcrow <mhalcrow@us.ibm.com>

---

 super.c |  212 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 212 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-12 20:00:30.000000000 -0600
@@ -0,0 +1,212 @@
+/**
+ * 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 <mahalcro@us.ibm.com>
+ *              Michael C. Thompson <mcthomps@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/key.h>
+#include <linux/seq_file.h>
+#include "ecryptfs_kernel.h"
+
+struct kmem_cache *ecryptfs_inode_info_cache;
+
+/**
+ * ecryptfs_alloc_inode - allocate an ecryptfs inode
+ * @sb: Pointer to the ecryptfs super block
+ * 
+ * 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.
+ * 
+ * Returns a pointer to a newly allocated inode, NULL otherwise
+ */
+static struct inode *ecryptfs_alloc_inode(struct super_block *sb)
+{
+	struct ecryptfs_inode_info *ecryptfs_inode;
+	struct inode *inode = NULL;
+
+	ecryptfs_inode = kmem_cache_alloc(ecryptfs_inode_info_cache,
+					  SLAB_KERNEL);
+	if (unlikely(!ecryptfs_inode))
+		goto out;
+	ecryptfs_init_crypt_stat(&ecryptfs_inode->crypt_stat);
+	inode = &ecryptfs_inode->vfs_inode;
+out:
+	return inode;
+}
+
+/**
+ * ecryptfs_destroy_inode
+ * @inode: The ecryptfs 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_inode_info *inode_info;
+
+	inode_info = ecryptfs_inode_to_private(inode);
+	ecryptfs_destruct_crypt_stat(&inode_info->crypt_stat);
+	kmem_cache_free(ecryptfs_inode_info_cache, inode_info);
+}
+
+/**
+ * ecryptfs_read_inode
+ * @inode: The ecryptfs inode
+ *
+ * Set up the ecryptfs inode.
+ */
+static void ecryptfs_read_inode(struct inode *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_set_inode_private(inode,
+				   list_entry(inode, struct ecryptfs_inode_info,
+					      vfs_inode));
+	ecryptfs_set_inode_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_put_super
+ * @sb: Pointer to the ecryptfs super block
+ * 
+ * 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);
+
+	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_set_superblock_private(sb, NULL);
+}
+
+/**
+ * ecryptfs_statfs
+ * @sb: The ecryptfs super block
+ * @buf: The struct kstatfs to fill in with stats
+ * 
+ * Get the filesystem statistics. Currently, we let this pass right through
+ * to the lower filesystem and take no action ourselves.
+ */
+static inline int ecryptfs_statfs(struct super_block *sb, struct kstatfs *buf)
+{
+	return vfs_statfs(ecryptfs_superblock_to_lower(sb), buf);
+}
+
+/**
+ * ecryptfs_clear_inode
+ * @inode - The ecryptfs inode
+ *
+ * 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 inline void ecryptfs_clear_inode(struct inode *inode)
+{
+	iput(ecryptfs_inode_to_lower(inode));
+}
+
+/**
+ * ecryptfs_umount_begin
+ *
+ * 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;
+
+	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_show_options
+ * 
+ * Prints the directory we are currently mounted over.
+ * Returns 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;
+	struct dentry *lower_root_dentry;
+	struct ecryptfs_sb_info *sb_info;
+	struct vfsmount *lower_mount;
+	int rc = 0;
+	char *tmp_page = NULL;
+	char *path;
+
+	tmp_page = (char *)__get_free_page(GFP_KERNEL);
+	if (!tmp_page) {
+		rc = -ENOMEM;
+		goto out;
+	}
+	lower_root_dentry = ecryptfs_dentry_to_lower(sb->s_root);
+	sb_info = ecryptfs_superblock_to_private(sb);
+	lower_mount = sb_info->lower_mnt;
+	path = d_path(lower_root_dentry, lower_mount, tmp_page, PAGE_SIZE);
+	if (IS_ERR(path)) {
+		rc = PTR_ERR(path);
+		goto out;
+	}
+	seq_printf(m, ",dir=%s", path);
+	free_page((unsigned long)tmp_page);
+out:
+	return rc;
+}
+
+struct super_operations ecryptfs_sops = {
+	.alloc_inode = ecryptfs_alloc_inode,
+	.destroy_inode = ecryptfs_destroy_inode,
+	.read_inode = ecryptfs_read_inode,
+	.drop_inode = generic_delete_inode,
+	.put_super = ecryptfs_put_super,
+	.statfs = ecryptfs_statfs,
+	.remount_fs = NULL,
+	.clear_inode = ecryptfs_clear_inode,
+	.umount_begin = ecryptfs_umount_begin,
+	.show_options = ecryptfs_show_options
+};

^ permalink raw reply

* [PATCH 5/13: eCryptfs] Header declarations
From: Phillip Hellewell @ 2006-05-13  3:43 UTC (permalink / raw)
  To: Andrew Morton
  Cc: linux-kernel, linux-fsdevel, viro, mike, mhalcrow, mcthomps, toml,
	yoder1, James Morris, Stephen C. Tweedie, Erez Zadok,
	David Howells
In-Reply-To: <20060513033742.GA18598@hellewell.homeip.net>

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 <phillip@hellewell.homeip.net>
Signed-off-by: Michael Halcrow <mhalcrow@us.ibm.com>

---

 ecryptfs_kernel.h |  486 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 486 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-12 20:00:27.000000000 -0600
@@ -0,0 +1,486 @@
+/**
+ * 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 <mahalcro@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef ECRYPTFS_KERNEL_H
+#define ECRYPTFS_KERNEL_H
+
+#ifdef CONFIG_ECRYPT_DEBUG
+#define OBSERVE_ASSERTS 1
+#endif
+
+#include <keys/user-type.h>
+#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
+	s32 flags;
+	s32 encrypted_key_size;
+	s32 decrypted_key_size;
+	u8 encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES];
+	u8 decrypted_key[ECRYPTFS_MAX_KEY_BYTES];
+};
+
+struct ecryptfs_password {
+	s32 password_bytes;
+	s32 hash_algo;
+	s32 hash_iterations;
+	s32 session_key_encryption_key_bytes;
+#define ECRYPTFS_PERSISTENT_PASSWORD 0x01
+#define ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET 0x02
+	u32 flags;
+	/* Iterated-hash concatenation of salt and passphrase */
+	u8 session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES];
+	u8 signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
+	/* Always in expanded hex */
+	u8 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
+	u32 flags;
+	uid_t uid;
+	s64 creation_time;
+	s64 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;
+};
+
+static inline struct ecryptfs_auth_tok *
+ecryptfs_get_key_payload_data(struct key *key)
+{
+	return (struct ecryptfs_auth_tok *)
+		(((struct user_key_payload*)key->payload.data)->data);
+}
+
+#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) do { /* nothing */ } while (0)
+#endif /* OBSERVE_ASSERTS */
+
+static inline struct ecryptfs_file_info *
+ecryptfs_file_to_private(struct file *file)
+{
+	return (struct ecryptfs_file_info *)file->private_data;
+}
+
+static inline void
+ecryptfs_set_file_private(struct file *file,
+			  struct ecryptfs_file_info *file_info)
+{
+	file->private_data = file_info;
+}
+
+static inline struct file *ecryptfs_file_to_lower(struct file *file)
+{
+	return ((struct ecryptfs_file_info *)file->private_data)->wfi_file;
+}
+
+static inline void
+ecryptfs_set_file_lower(struct file *file, struct file *lower_file)
+{
+	((struct ecryptfs_file_info *)file->private_data)->wfi_file =
+		lower_file;
+}
+
+static inline struct ecryptfs_inode_info *
+ecryptfs_inode_to_private(struct inode *inode)
+{
+	return (struct ecryptfs_inode_info *)inode->u.generic_ip;
+}
+
+static inline void
+ecryptfs_set_inode_private(struct inode *inode,
+			   struct ecryptfs_inode_info *inode_info)
+{
+	inode->u.generic_ip = inode_info;
+}
+
+static inline struct inode *ecryptfs_inode_to_lower(struct inode *inode)
+{
+	return ((struct ecryptfs_inode_info *)inode->u.generic_ip)->wii_inode;
+}
+
+static inline void
+ecryptfs_set_inode_lower(struct inode *inode, struct inode *lower_inode)
+{
+	((struct ecryptfs_inode_info *)inode->u.generic_ip)->wii_inode =
+		lower_inode;
+}
+
+static inline struct ecryptfs_sb_info *
+ecryptfs_superblock_to_private(struct super_block *sb)
+{
+	return (struct ecryptfs_sb_info *)sb->s_fs_info;
+}
+
+static inline void
+ecryptfs_set_superblock_private(struct super_block *sb,
+				struct ecryptfs_sb_info *sb_info)
+{
+	sb->s_fs_info = sb_info;
+}
+
+static inline struct super_block *
+ecryptfs_superblock_to_lower(struct super_block *sb)
+{
+	return ((struct ecryptfs_sb_info *)sb->s_fs_info)->wsi_sb;
+}
+
+static inline void
+ecryptfs_set_superblock_lower(struct super_block *sb,
+			      struct super_block *lower_sb)
+{
+	((struct ecryptfs_sb_info *)sb->s_fs_info)->wsi_sb = lower_sb;
+}
+
+static inline struct ecryptfs_dentry_info *
+ecryptfs_dentry_to_private(struct dentry *dentry)
+{
+	return (struct ecryptfs_dentry_info *)dentry->d_fsdata;
+}
+
+static inline void
+ecryptfs_set_dentry_private(struct dentry *dentry,
+			    struct ecryptfs_dentry_info *dentry_info)
+{
+	dentry->d_fsdata = dentry_info;
+}
+
+static inline struct dentry *
+ecryptfs_dentry_to_lower(struct dentry *dentry)
+{
+	return ((struct ecryptfs_dentry_info *)dentry->d_fsdata)->wdi_dentry;
+}
+
+static inline void
+ecryptfs_set_dentry_lower(struct dentry *dentry, struct dentry *lower_dentry)
+{
+	((struct ecryptfs_dentry_info *)dentry->d_fsdata)->wdi_dentry =
+		lower_dentry;
+}
+
+
+#define ecryptfs_printk(type, fmt, arg...) \
+        __ecryptfs_printk(type "%s: " fmt, __FUNCTION__, ## arg);
+void __ecryptfs_printk(const char *fmt, ...);
+
+extern const struct file_operations ecryptfs_main_fops;
+extern const 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 struct kmem_cache *ecryptfs_auth_tok_list_item_cache;
+extern struct kmem_cache *ecryptfs_file_info_cache;
+extern struct kmem_cache *ecryptfs_dentry_info_cache;
+extern struct kmem_cache *ecryptfs_inode_info_cache;
+extern struct kmem_cache *ecryptfs_sb_info_cache;
+extern struct kmem_cache *ecryptfs_header_cache_0;
+extern struct kmem_cache *ecryptfs_header_cache_1;
+extern struct kmem_cache *ecryptfs_header_cache_2;
+extern struct kmem_cache *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 */

^ permalink raw reply

* [PATCH 4/13: eCryptfs] Main module functions
From: Phillip Hellewell @ 2006-05-13  3:42 UTC (permalink / raw)
  To: Andrew Morton
  Cc: linux-kernel, linux-fsdevel, viro, mike, mhalcrow, mcthomps, toml,
	yoder1, James Morris, Stephen C. Tweedie, Erez Zadok,
	David Howells
In-Reply-To: <20060513033742.GA18598@hellewell.homeip.net>

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 <phillip@hellewell.homeip.net>
Signed-off-by: Michael Halcrow <mhalcrow@us.ibm.com>

---

 main.c |  767 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 767 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-12 20:00:29.000000000 -0600
@@ -0,0 +1,767 @@
+/**
+ * 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 <mahalcro@us.ibm.com>
+ *              Michael C. Thompson <mcthomps@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <linux/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 "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);
+}
+
+/**
+ * ecryptfs_interpose
+ * @lower_dentry: Existing dentry in the lower filesystem
+ * @dentry: ecryptfs' dentry
+ * @sb: ecryptfs's super_block
+ * @flag: If set to true, then d_add is called, else d_instantiate is called
+ * 
+ * Interposes upper and lower dentries.
+ *
+ * This function will allocate an ecryptfs_inode through the call to
+ * iget(sb, lower_inode->i_ino).
+ * 
+ * Returns 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;
+
+	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_set_inode_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:
+	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}
+};
+
+/**
+ * ecryptfs_verify_version
+ * @version: The version number to confirm
+ * 
+ * Returns 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;
+}
+
+/**
+ * ecryptfs_parse_options
+ * @sb: The ecryptfs super block
+ * @options: The options pased to the kernel
+ * 
+ * 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.
+ *
+ * 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.
+ *
+ * Returns 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;
+
+	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);
+	/* 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 = ecryptfs_get_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:
+	return rc;
+}
+
+struct kmem_cache *ecryptfs_sb_info_cache;
+
+/**
+ * ecryptfs_cleanup_read_super
+ * @sb: The ecryptfs super block
+ * 
+ * Preform the cleanup for ecryptfs_read_super()
+ */
+static inline void ecryptfs_cleanup_read_super(struct super_block *sb)
+{
+	up_write(&sb->s_umount);
+	deactivate_super(sb);
+}
+
+/**
+ * ecryptfs_fill_super
+ * @sb: The ecryptfs super block
+ * @raw_data: The options passed to mount
+ * @silent: Not used but required by function prototype
+ * 
+ * Sets up what we can of the sb, rest is done in ecryptfs_read_super
+ * 
+ * Returns zero on success; non-zero otherwise
+ */
+static int
+ecryptfs_fill_super(struct super_block *sb, void *raw_data, int silent)
+{
+	int rc = 0;
+
+	/* Released in ecryptfs_put_super() */
+	ecryptfs_set_superblock_private(sb,
+					kmem_cache_alloc(ecryptfs_sb_info_cache,
+							 SLAB_KERNEL));
+	if (!ecryptfs_superblock_to_private(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_set_dentry_private(sb->s_root,
+				    kmem_cache_alloc(ecryptfs_dentry_info_cache,
+						     SLAB_KERNEL));
+	if (!ecryptfs_dentry_to_private(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 */
+	return rc;
+}
+
+/**
+ * ecryptfs_read_super
+ * @sb: The ecryptfs super block
+ * @dev_name: The path to mount over
+ * 
+ * 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));
+	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_set_superblock_lower(sb, lower_root->d_sb);
+	sb->s_maxbytes = lower_root->d_sb->s_maxbytes;
+	ecryptfs_set_dentry_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:
+	return rc;
+}
+
+/**
+ * ecryptfs_get_sb
+ * @fs_type
+ * @flags
+ * @dev_name: The path to mount over
+ * @raw_data: The options passed into the kernel
+ * 
+ * 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;
+
+	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:
+	return sb;
+}
+
+/**
+ * ecryptfs_kill_block_super
+ * @sb: The ecryptfs super block
+ * 
+ * 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);
+
+	memset(mount_crypt_stat, 0, sizeof(struct ecryptfs_mount_crypt_stat));
+	generic_shutdown_super(sb);
+}
+
+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
+};
+
+/**
+ * inode_info_init_once
+ * 
+ * Initializes the ecryptfs_inode_info_cache when it is created
+ */
+static void
+inode_info_init_once(void *vptr, struct kmem_cache *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;
+
+/**
+ * ecryptfs_init_kmem_caches
+ * 
+ * Sets ecryptfs_allocated_caches with flags so that we can
+ * free created caches should we run out of memory during
+ * creation period.
+ *
+ * Returns 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;
+}
+
+/**
+ * ecryptfs_free_kmem_caches
+ * 
+ * Returns 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;
+
+	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 <mhalcrow@us.ibm.com>");
+MODULE_DESCRIPTION("eCryptfs");
+
+MODULE_LICENSE("GPL");
+
+module_init(init_ecryptfs_fs)
+module_exit(exit_ecryptfs_fs)

^ permalink raw reply

* Re: ata_piix failure on ich6m
From: Jeff Garzik @ 2006-05-13  3:42 UTC (permalink / raw)
  To: Tejun Heo; +Cc: Matthew Garrett, linux-ide, linux-kernel
In-Reply-To: <44629E68.3020302@gmail.com>

Tejun Heo wrote:
> Matthew Garrett wrote:
>> Hi,
>>
>> We've got an ich6m system (a Toshiba Portege S100). ata_piix attempts 
>> to drive the chipset, but fails - however, it doesn't bail out. As a 
>> result it remains bound to the device and ahci isn't loaded.
>>
>> I've attached the lspci output for the chipset. A few things to note are:
>>
>> 1) The AHCI BAR is set
>> 2) The SCC register identifies it as an AHCI controller
>> 3) Bits 2 and 0 of the PCS are set, which the spec claims indicates 
>> that the port is to be controlled as an ahci device.
>>
>> So, my question is effectively: why does ata_piix attempt to disable 
>> ahci rather than simply letting the ahci driver bind? Points (1) and 
>> (2) seem to be checked by the code, but I'm guessing that in the case 
>> of (3) it should just return ENODEV and let ahci be run instead. If 
>> so, should I code up a patch?
>>
> 
> I'm not very sure but it might be historical.  ahci got implemented 
> after ata_piix and in the meantime ata_piix must have handled all it 
> could.  Can you verify whether modifying the code to return -ENODEV work 
> for your machine?  If so, that could be the correct solution but I'm a 
> bit worried because it could change probing order or fail to enable 
> devices it used to.  Maybe we need a hack to return -ENODEV iff ahci is 
> there to handle the device.

It's definitely historical.  I'm pretty frazzled now so I don't 
remember.  It may be that on ICH6, AHCI mode does not cause the PCI IDs 
to change, so driver load order winds up dictating what gets used.

	Jeff




^ permalink raw reply

* [PATCH 3/13: eCryptfs] Makefile
From: Phillip Hellewell @ 2006-05-13  3:42 UTC (permalink / raw)
  To: Andrew Morton
  Cc: linux-kernel, linux-fsdevel, viro, mike, mhalcrow, mcthomps, toml,
	yoder1, James Morris, Stephen C. Tweedie, Erez Zadok,
	David Howells
In-Reply-To: <20060513033742.GA18598@hellewell.homeip.net>

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 <phillip@hellewell.homeip.net>
Signed-off-by: Michael Halcrow <mhalcrow@us.ibm.com>

---

 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-12 20:00:25.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

^ permalink raw reply

* [PATCH 2/13: eCryptfs] Documentation
From: Phillip Hellewell @ 2006-05-13  3:41 UTC (permalink / raw)
  To: Andrew Morton
  Cc: linux-kernel, linux-fsdevel, viro, mike, mhalcrow, mcthomps, toml,
	yoder1, James Morris, Stephen C. Tweedie, Erez Zadok,
	David Howells
In-Reply-To: <20060513033742.GA18598@hellewell.homeip.net>

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 <phillip@hellewell.homeip.net>
Signed-off-by: Michael Halcrow <mhalcrow@us.ibm.com>

---

 ecryptfs.txt |   77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 77 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-12 20:00:30.000000000 -0600
@@ -0,0 +1,77 @@
+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 <mhalcrow@us.ibm.com>
+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 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:
+
+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
+mhalcrow@us.ibm.com

^ permalink raw reply

* Re: Tracking branch history
From: Shawn Pearce @ 2006-05-13  3:40 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Daniel Barkalow, git
In-Reply-To: <Pine.LNX.4.64.0605121640210.3866@g5.osdl.org>

Linus Torvalds <torvalds@osdl.org> wrote:
> 
> On Fri, 12 May 2006, Daniel Barkalow wrote:
> >
> > One feature that might make git more intuitive to people is if we were to 
> > additionally track the history of what commit was the head of each branch 
> > over time. This is only vaguely related to the history of the content, but 
> > it's well-defined and sometimes significant.
> > 
> > E.g., if you know that two weeks ago, what you had worked, but it doesn't 
> > work now, you can use git-bisect to figure out what happened, but first 
> > you have to figure out what commit it was that you were using two weeks 
> > ago. Two weeks ago, we had that information, but we didn't keep it.
> 
> Note that this is possible, but it must be done literally as a separate 
> history from the commit history. 
> 
> IOW, a good (?) way to do it is to literally have a commit hook that 
> basically just does
> 
> 	echo $new >> .git/$branch-commit-history
> 
> possibly together with a datestamp thing (ie it could be something like
> "echo $new "$USER" $(date)" rather than just the commit SHA1).

Why not intergrate this into git-update-ref.  Almost every tool which
deals with a GIT repository (aside from my pure-Java Eclipse plugin
which is still a major work-in-process) performs ref changes through
git-udpate-ref.  So just have it append the ref's history to a file:

	.git/log/refs/heads/$branch

where the history records are stored as:

	40 byte commit-ish SHA1
	<SP>
	<committer>
	<LF>

e.g.:

	cbb6d91d95e523c2b6a6b52577c4be28d18ace83 Shawn O. Pearce <spearce@spearce.org> 1137039378 -0500
	ae8c74e96a1e02bbfb7f1a9669b77d6f8ee6c3cf Shawn O. Pearce <spearce@spearce.org> 1136921470 -0500

Of course a major issue here is locking the log file during the ref
update, but it looks like it might just be safe to append the entry
to the log file right after the re_verify and before the rename.

I wouldn't have git-update-ref create the log file. I'd would only
have it append if the log already exists.

Hmm, this actually looks like it would be really easy.  Maybe I'll
hack up an RFC patch this evening after dinner.

-- 
Shawn.

^ permalink raw reply

* [PATCH 1/13: eCryptfs] fs/Makefile and fs/Kconfig
From: Phillip Hellewell @ 2006-05-13  3:40 UTC (permalink / raw)
  To: Andrew Morton
  Cc: linux-kernel, linux-fsdevel, viro, mike, mhalcrow, mcthomps, toml,
	yoder1, James Morris, Stephen C. Tweedie, Erez Zadok,
	David Howells
In-Reply-To: <20060513033742.GA18598@hellewell.homeip.net>

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 <phillip@hellewell.homeip.net>
Signed-off-by: Michael Halcrow <mhalcrow@us.ibm.com>

---

 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-12 20:00:24.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-12 20:00:24.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/

^ permalink raw reply

* Re: [ANNOUNCE] libata EH/NCQ/hotplug/PM git tree
From: Jeff Garzik @ 2006-05-13  3:37 UTC (permalink / raw)
  To: Tejun Heo
  Cc: Alan Cox, Albert Lee, Jens Axboe, Edward Falk, Carlos Pardo,
	Raymond Liu, linux-ide@vger.kernel.org
In-Reply-To: <44653807.3030101@gmail.com>

Tejun Heo wrote:
> Jeff Garzik wrote:
>> Tejun Heo wrote:
>>> tested.  I implemented AHCI PM support half-way but then realized I 
>>> don't have any AHCI controller which supports PM (I have ICH7R).  Is 
>>> there any AHCI controller which reports PM support?
>>
>> You can always use SCR-based PM support...
>>
> 
> Can you elaborate a bit?

Sections A.1 and 6.1 of the PM spec.  You set the PMP field of the 
SControl register.  This has obvious parallelism limitations, which is 
why "hardware context switching support" aka native PM support is 
greatly preferred.


> @ welcome back.

Thanks!  :)

	Jeff, learning to type with a ring on the finger



^ permalink raw reply

* [PATCH 0/13: eCryptfs] eCryptfs Patch Set
From: Phillip Hellewell @ 2006-05-13  3:37 UTC (permalink / raw)
  To: Andrew Morton
  Cc: linux-kernel, linux-fsdevel, viro, mike, mhalcrow, mcthomps, toml,
	yoder1, James Morris, Stephen C. Tweedie, Phillip Hellewell,
	Erez Zadok, David Howells

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

http://ecryptfs.sourceforge.net/

It includes numerous updates based on comments on the 0.1.6 submission
made on May 4th. The only functional change worth noting is the
removal of the unnecessary second read in ecryptfs_get1page() and
ecryptfs_do_readpage().

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

Thanks,
Phillip

^ permalink raw reply

* [ALSA - driver 0002023]: midi/joystick port disabled and no way to enable it
From: bugtrack @ 2006-05-13  3:19 UTC (permalink / raw)
  To: alsa-devel


A NOTE has been added to this issue.
======================================================================
<https://bugtrack.alsa-project.org/alsa-bug/view.php?id=2023> 
======================================================================
Reported By:                zanonmark
Assigned To:                
======================================================================
Project:                    ALSA - driver
Issue ID:                   2023
Category:                   PCI - ens1371
Reproducibility:            always
Severity:                   major
Priority:                   normal
Status:                     new
Distribution:               Ubuntu 5.10
Kernel Version:             2.6.12-10
======================================================================
Date Submitted:             04-16-2006 12:18 CEST
Last Modified:              05-13-2006 05:19 CEST
======================================================================
Summary:                    midi/joystick port disabled and no way to enable it
Description: 
I have a MIDI keyboard and I'd like to connect it to the midi/joystick port
of the soundcard.

Unfortunately, while PCM works fine and /dev/midi is created, I see no
MIDI communication through /dev/midi.
/proc/asound/AudioPCI/audiopci reports that the joystick port is
disabled.

It is probably a bug on the Ubuntu side (did they forget to compile the
driver correctly?), but I'd like to see a parameter for enabling the
joystick port "on the fly": other drivers have one (e.g.: "joystick=1"),
but snd_ens1371 has not, so:
1) it is not possible to reload (rmmod and modprobe) the driver with that
parameter enabling the midi/joystick port manually,
2) it is not possible to save (alsactl store), modify and reload (alsactl
restore) the soundcard settings, because I see no such parameter to
modify.

So, will you please include such a parameter in the next version of the
driver?

Thanks a lot,

Marco.

======================================================================

----------------------------------------------------------------------
 zanonmark - 05-12-06 19:44 
----------------------------------------------------------------------
of course I could disable it, but my intention is to try to make it usable
someday :) I'd just like to buy a midi replicator when I have some time...
:)

as per the card model, lspci -vvn reports:

0000:00:0a.0 0401: 1274:1371 (rev 02)
        Subsystem: 1274:1371
        Control: I/O+ Mem- BusMaster+ SpecCycle- MemWINV- VGASnoop-
ParErr- Stepping- SERR- FastB2B-
        Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=slow >TAbort-
<TAbort+ <MAbort+ >SERR- <PERR-
        Latency: 32 (3000ns min, 32000ns max)
        Interrupt: pin A routed to IRQ 18
        Region 0: I/O ports at d400 [size=64]
        Capabilities: [dc] Power Management version 1
                Flags: PMEClk- DSI+ D1- D2+ AuxCurrent=0mA
PME(D0+,D1-,D2+,D3hot+,D3cold-)
                Status: D0 PME-Enable- DSel=0 DScale=0 PME-

while lspci -v reports:

0000:00:0a.0 Multimedia audio controller: Ensoniq ES1371 [AudioPCI-97]
(rev 02)
        Subsystem: Ensoniq Creative Sound Blaster AudioPCI64V,
AudioPCI128
        Flags: bus master, slow devsel, latency 32, IRQ 18
        I/O ports at d400 [size=64]
        Capabilities: [dc] Power Management version 1

thanks,

MZ

----------------------------------------------------------------------
 Raymond - 05-13-06 05:19 
----------------------------------------------------------------------
amidi -d -p hw:0,0 should see the keyboard and receives messages from it

http://ubuntuforums.org/showthread.php?t=113933

Issue History
Date Modified  Username       Field                    Change              
======================================================================
04-16-06 12:18 zanonmark      New Issue                                    
04-16-06 12:18 zanonmark      Distribution              => Ubuntu 5.10     
04-16-06 12:18 zanonmark      Kernel Version            => 2.6.12-10       
04-17-06 06:56 rlrevell       Note Added: 0009301                          
04-17-06 07:19 Raymond        Note Added: 0009303                          
04-17-06 19:59 zanonmark      Note Added: 0009319                          
04-18-06 10:54 Raymond        Note Added: 0009328                          
04-18-06 13:18 Raymond        Note Edited: 0009328                         
04-18-06 16:28 Clemens LadischNote Added: 0009338                          
04-19-06 20:05 zanonmark      Note Added: 0009354                          
04-19-06 21:49 rlrevell       Note Added: 0009356                          
04-20-06 03:39 Raymond        Note Added: 0009363                          
04-22-06 13:36 zanonmark      Note Added: 0009433                          
04-24-06 15:47 Raymond        Note Added: 0009452                          
04-24-06 15:57 zanonmark      Note Added: 0009460                          
04-25-06 03:25 Raymond        Note Added: 0009484                          
04-25-06 10:55 zanonmark      Note Added: 0009486                          
04-26-06 10:54 Raymond        Note Added: 0009505                          
04-26-06 19:51 zanonmark      Note Added: 0009522                          
05-01-06 10:38 Raymond        Note Added: 0009553                          
05-07-06 13:33 zanonmark      Note Added: 0009641                          
05-12-06 03:58 Raymond        Note Added: 0009771                          
05-12-06 19:44 zanonmark      Note Added: 0009788                          
05-13-06 05:19 Raymond        Note Added: 0009794                          
======================================================================




-------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642

^ permalink raw reply

* Re: [PATCH 002 of 8] md/bitmap: Remove bitmap writeback daemon.
From: Neil Brown @ 2006-05-13  3:14 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-raid, linux-kernel, paul.clements
In-Reply-To: <20060512104035.5fee4e66.akpm@osdl.org>

On Friday May 12, akpm@osdl.org wrote:
> NeilBrown <neilb@suse.de> wrote:
> >
> >  ./drivers/md/bitmap.c         |  115 ++----------------------------------------
> 
> hmm.  I hope we're not doing any of that filesystem I/O within the context
> of submit_bio() or kblockd or anything like that.  Looks OK from a quick
> scan.

No.  We do all the I/O from the context of the per-array thread.
However some IO requests cannot complete until the filesystem I/O
completes, so we need to be sure that the filesystem I/O won't block
waiting for memory, or fail with -ENOMEM.

> 
> a_ops->commit_write() already ran set_page_dirty(), so you don't need that
> in there.

Is that documented somewhere..... but yes, that seems to be right. Thanks.

> 
> I assume this always works in units of a complete page?  It's strange to do
> prepare_write() followed immediately by commit_write().  Normally
> prepare_write() will do some prereading, but it's smart enough to not do
> that if the caller is preparing to write the whole page.
> 

Yes, it is strange.  That was one of the things that made me want to
review this code and figure out how to do it "properly".

As far as I can see, much of 'address_space' is really an internal
interface to support routines used by the filesystem.  A filesystem
may choose to use address spaces, and has a fair degree of freedom
when it comes to which bits to make use of and exactly what they
mean.
About the only thing that *has* to be supported is ->writepages --
which has a fair degree of latitude in exactly what it does -- and
->writepage -- which can only be called after locking a page and
rechecking the ->mapping.

bitmap.c is currently trying to do something every different.
It uses ->readpage to get pages in the page cache (even though some
address spaces don't define ->readpage) and then holds onto those
pages without holding the page lock, and then calls ->writepage to
flush them out from time to time.
Before calling writepage it gets the pagelock, but doesn't re-check
that ->mapping is correct (there is nothing much it can do if it isn't
correct..).

I noticed this is particularly a problem with tmpfs.  When you call
writepage on a tmpfs page, the page is swizzled into the swap cache,
and ->mapping becomes NULL - not the behaviour that bitmap is
expecting.

Now I agree that tmpfs is an unusual case, and that storing a bitmap
in tmpfs doesn't make a lot of sense (though it can make some...) but
the point is that if a filesystem is allowed to move pages around like
that, then bitmap cannot hold on to pages in the page cache like it
wants to.  It simply isn't a well defined thing to do.


> We normally use PAGE_CACHE_SIZE for these things, not PAGE_SIZE.  Same diff.
> 

Yeah.... why is that?  Why have two names for exactly the same value?
How does a poor develop know when to use one and when the other?  More
particularly, how does one remember.
I would argue that the "same diff" should be no difference - not even
in spelling....

> If you have a page and you want to write the whole thing out then there's
> really no need to run prepare_write or commit_write at all.  Just
> initialise the whole page, run set_page_dirty() then write_one_page().
> 

I see that now.  But only after locking the page, and rechecking that
->mapping is correct, and if it isn't .... well, more work is involved
that bitmap is in a position to do.

> Perhaps it should check that the backing filesystem actually implements
> commit_write(), prepare_write(), readpage(), etc.  Some might not, and the
> user will get taught not to do that via an oops.
> 

Might help, but as I think you've gathered, I really want a whole
different approach to writing to the file.  One that I can justify as
being a "correct" use of interfaces, and also that I can be certain
will not block or fail on a kmalloc or similar.  Hence the bmap thing
later.


Thanks for your feedback.

NeilBrown

^ permalink raw reply

* Re: GRUB 2 and eltorito boot (no emul)
From: Yoshinori K. Okuji @ 2006-05-13  3:12 UTC (permalink / raw)
  To: The development of GRUB 2
In-Reply-To: <44648523.8080905@corest.com>

On Friday 12 May 2006 14:52, Gerardo Richarte wrote:
>     Suppose I wanted to implement eltorito boot (as for mkiso -b
> iso/boot/eltorito -no-emul-boot -boot-load-size 4 -boot-info-table) for
> GRUB 2. What do you think would be the best approach? I took a look at
> GRUB Legacy's eltorito_start and it doesn't think neither too
> complicated nor too different to boot.S from GRUB 2, give or take a table.

For now, the easiest way is to use the hard drive emulation mode. This should 
work transparently. But I don't know how many BIOSes correctly implement the 
emulation.

If you really want to use the no emulation mode, it would be the easiest to 
port the feature from GRUB Legacy, as you guess.

FYI, a student might work on this issue in the Summer of Code.

Okuji



^ permalink raw reply

* Possible alignment issue in kernel's sha256 implementation
From: Jurij Smakov @ 2006-05-13  2:59 UTC (permalink / raw)
  To: sparclinux

Hi,

Yesterday a bug was discovered in Debian's application, which uses the 
sha256 implementation virtually identical to the one available in kernel 
[0]. An unaligned access in sha256 code was causing a bus error on sparc. 
The offending code also present in the 2.6.17-rc3 crypto/sha256.c in the 
following form:

60 static inline void LOAD_OP(int I, u32 *W, const u8 *input)
61 {
62         W[I] = __be32_to_cpu( ((__be32*)(input))[I] );
63 }

This function is only called from sha256_transform(), which is called only 
from sha256_update(), which, in turn, is exposed to the kernel's 
cryptographic API layer. I believe that if a u8 array not aligned on 
4-byte boundary is passed as a second argument ('data') to that function, 
it will cause an unaligned memory access in LOAD_OP.

I cannot really determine how dangerous this is. It might be that the 
cryptographic layer takes care of alignment, or the kernel will just 
handle the unaligned access gracefully. In any case, I think it's worth 
checking out by someone with more knowledge on the matter than myself.

[0] http://bugs.debian.org/366717

Best regards,

Jurij Smakov                                        jurij@wooyd.org
Key: http://www.wooyd.org/pgpkey/                   KeyID: C99E03CC

^ permalink raw reply

* legal pad promenade
From: Penny Simon @ 2006-05-13  2:52 UTC (permalink / raw)
  To: alsa-devel


[-- Attachment #1.1: Type: text/plain, Size: 0 bytes --]



[-- Attachment #1.2: Type: text/html, Size: 416 bytes --]

[-- Attachment #2: rooftop.gif --]
[-- Type: image/gif, Size: 37508 bytes --]

^ permalink raw reply

* Elks tarball
From: Tom McCabe @ 2006-05-13  2:44 UTC (permalink / raw)
  To: linux-8086

A new elks tarball seems to be out at
http://compsolutions.ath.cx:88/elks/, dated May 2nd
even though it incorporates a patch I sent on the 7th.

__________________________________________________
Do You Yahoo!?
Tired of spam?  Yahoo! Mail has the best spam protection around 
http://mail.yahoo.com 

^ permalink raw reply


This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.