summaryrefslogtreecommitdiffstats
path: root/releases/2.6.32.58/ecryptfs-use-notify_change-for-truncating-lower-inodes.patch
diff options
context:
space:
mode:
Diffstat (limited to 'releases/2.6.32.58/ecryptfs-use-notify_change-for-truncating-lower-inodes.patch')
-rw-r--r--releases/2.6.32.58/ecryptfs-use-notify_change-for-truncating-lower-inodes.patch235
1 files changed, 235 insertions, 0 deletions
diff --git a/releases/2.6.32.58/ecryptfs-use-notify_change-for-truncating-lower-inodes.patch b/releases/2.6.32.58/ecryptfs-use-notify_change-for-truncating-lower-inodes.patch
new file mode 100644
index 0000000..a40074d
--- /dev/null
+++ b/releases/2.6.32.58/ecryptfs-use-notify_change-for-truncating-lower-inodes.patch
@@ -0,0 +1,235 @@
+From 5f3ef64f4da1c587cdcfaaac72311225b7df094c Mon Sep 17 00:00:00 2001
+From: Tyler Hicks <tyhicks@linux.vnet.ibm.com>
+Date: Wed, 14 Oct 2009 16:18:27 -0500
+Subject: eCryptfs: Use notify_change for truncating lower inodes
+
+From: Tyler Hicks <tyhicks@linux.vnet.ibm.com>
+
+commit 5f3ef64f4da1c587cdcfaaac72311225b7df094c upstream.
+
+When truncating inodes in the lower filesystem, eCryptfs directly
+invoked vmtruncate(). As Christoph Hellwig pointed out, vmtruncate() is
+a filesystem helper function, but filesystems may need to do more than
+just a call to vmtruncate().
+
+This patch moves the lower inode truncation out of ecryptfs_truncate()
+and renames the function to truncate_upper(). truncate_upper() updates
+an iattr for the lower inode to indicate if the lower inode needs to be
+truncated upon return. ecryptfs_setattr() then calls notify_change(),
+using the updated iattr for the lower inode, to complete the truncation.
+
+For eCryptfs functions needing to truncate, ecryptfs_truncate() is
+reintroduced as a simple way to truncate the upper inode to a specified
+size and then truncate the lower inode accordingly.
+
+https://bugs.launchpad.net/bugs/451368
+
+Reported-by: Christoph Hellwig <hch@lst.de>
+Acked-by: Dustin Kirkland <kirkland@canonical.com>
+Cc: ecryptfs-devel@lists.launchpad.net
+Cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Tyler Hicks <tyhicks@linux.vnet.ibm.com>
+Signed-off-by: Colin Ian King <colin.king@canonical.com>
+Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+
+---
+ fs/ecryptfs/inode.c | 99 +++++++++++++++++++++++++++++++++++-----------------
+ 1 file changed, 67 insertions(+), 32 deletions(-)
+
+--- a/fs/ecryptfs/inode.c
++++ b/fs/ecryptfs/inode.c
+@@ -758,18 +758,23 @@ upper_size_to_lower_size(struct ecryptfs
+ }
+
+ /**
+- * ecryptfs_truncate
++ * truncate_upper
+ * @dentry: The ecryptfs layer dentry
+- * @new_length: The length to expand the file to
++ * @ia: Address of the ecryptfs inode's attributes
++ * @lower_ia: Address of the lower inode's attributes
+ *
+ * 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.
++ * writing strings of 0's out. When truncating, we truncate the upper
++ * inode and update the lower_ia according to the page index
++ * interpolations. If ATTR_SIZE is set in lower_ia->ia_valid upon return,
++ * the caller must use lower_ia in a call to notify_change() to perform
++ * the truncation of the lower inode.
+ *
+ * Returns zero on success; non-zero otherwise
+ */
+-int ecryptfs_truncate(struct dentry *dentry, loff_t new_length)
++static int truncate_upper(struct dentry *dentry, struct iattr *ia,
++ struct iattr *lower_ia)
+ {
+ int rc = 0;
+ struct inode *inode = dentry->d_inode;
+@@ -780,8 +785,10 @@ int ecryptfs_truncate(struct dentry *den
+ loff_t lower_size_before_truncate;
+ loff_t lower_size_after_truncate;
+
+- if (unlikely((new_length == i_size)))
++ if (unlikely((ia->ia_size == i_size))) {
++ lower_ia->ia_valid &= ~ATTR_SIZE;
+ goto out;
++ }
+ crypt_stat = &ecryptfs_inode_to_private(dentry->d_inode)->crypt_stat;
+ /* Set up a fake ecryptfs file, this is used to interface with
+ * the file in the underlying filesystem so that the
+@@ -801,28 +808,30 @@ int ecryptfs_truncate(struct dentry *den
+ &fake_ecryptfs_file,
+ ecryptfs_inode_to_private(dentry->d_inode)->lower_file);
+ /* Switch on growing or shrinking file */
+- if (new_length > i_size) {
++ if (ia->ia_size > i_size) {
+ char zero[] = { 0x00 };
+
++ lower_ia->ia_valid &= ~ATTR_SIZE;
+ /* Write a single 0 at the last position of the file;
+ * this triggers code that will fill in 0's throughout
+ * the intermediate portion of the previous end of the
+ * file and the new and of the file */
+ rc = ecryptfs_write(&fake_ecryptfs_file, zero,
+- (new_length - 1), 1);
+- } else { /* new_length < i_size_read(inode) */
+- /* We're chopping off all the pages down do the page
+- * in which new_length is located. Fill in the end of
+- * that page from (new_length & ~PAGE_CACHE_MASK) to
++ (ia->ia_size - 1), 1);
++ } else { /* ia->ia_size < i_size_read(inode) */
++ /* We're chopping off all the pages down to the page
++ * in which ia->ia_size is located. Fill in the end of
++ * that page from (ia->ia_size & ~PAGE_CACHE_MASK) to
+ * PAGE_CACHE_SIZE with zeros. */
+ size_t num_zeros = (PAGE_CACHE_SIZE
+- - (new_length & ~PAGE_CACHE_MASK));
++ - (ia->ia_size & ~PAGE_CACHE_MASK));
+
+ if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) {
+- rc = vmtruncate(inode, new_length);
++ rc = vmtruncate(inode, ia->ia_size);
+ if (rc)
+ goto out_free;
+- rc = vmtruncate(lower_dentry->d_inode, new_length);
++ lower_ia->ia_size = ia->ia_size;
++ lower_ia->ia_valid |= ATTR_SIZE;
+ goto out_free;
+ }
+ if (num_zeros) {
+@@ -834,7 +843,7 @@ int ecryptfs_truncate(struct dentry *den
+ goto out_free;
+ }
+ rc = ecryptfs_write(&fake_ecryptfs_file, zeros_virt,
+- new_length, num_zeros);
++ ia->ia_size, num_zeros);
+ kfree(zeros_virt);
+ if (rc) {
+ printk(KERN_ERR "Error attempting to zero out "
+@@ -843,7 +852,7 @@ int ecryptfs_truncate(struct dentry *den
+ goto out_free;
+ }
+ }
+- vmtruncate(inode, new_length);
++ vmtruncate(inode, ia->ia_size);
+ rc = ecryptfs_write_inode_size_to_metadata(inode);
+ if (rc) {
+ printk(KERN_ERR "Problem with "
+@@ -856,10 +865,12 @@ int ecryptfs_truncate(struct dentry *den
+ 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);
++ upper_size_to_lower_size(crypt_stat, ia->ia_size);
++ if (lower_size_after_truncate < lower_size_before_truncate) {
++ lower_ia->ia_size = lower_size_after_truncate;
++ lower_ia->ia_valid |= ATTR_SIZE;
++ } else
++ lower_ia->ia_valid &= ~ATTR_SIZE;
+ }
+ out_free:
+ if (ecryptfs_file_to_private(&fake_ecryptfs_file))
+@@ -869,6 +880,33 @@ out:
+ return rc;
+ }
+
++/**
++ * ecryptfs_truncate
++ * @dentry: The ecryptfs layer dentry
++ * @new_length: The length to expand the file to
++ *
++ * Simple function that handles the truncation of an eCryptfs inode and
++ * its corresponding lower inode.
++ *
++ * Returns zero on success; non-zero otherwise
++ */
++int ecryptfs_truncate(struct dentry *dentry, loff_t new_length)
++{
++ struct iattr ia = { .ia_valid = ATTR_SIZE, .ia_size = new_length };
++ struct iattr lower_ia = { .ia_valid = 0 };
++ int rc;
++
++ rc = truncate_upper(dentry, &ia, &lower_ia);
++ if (!rc && lower_ia.ia_valid & ATTR_SIZE) {
++ struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
++
++ mutex_lock(&lower_dentry->d_inode->i_mutex);
++ rc = notify_change(lower_dentry, &lower_ia);
++ mutex_unlock(&lower_dentry->d_inode->i_mutex);
++ }
++ return rc;
++}
++
+ static int
+ ecryptfs_permission(struct inode *inode, int mask)
+ {
+@@ -891,6 +929,7 @@ static int ecryptfs_setattr(struct dentr
+ {
+ int rc = 0;
+ struct dentry *lower_dentry;
++ struct iattr lower_ia;
+ struct inode *inode;
+ struct inode *lower_inode;
+ struct ecryptfs_crypt_stat *crypt_stat;
+@@ -929,15 +968,11 @@ static int ecryptfs_setattr(struct dentr
+ }
+ }
+ mutex_unlock(&crypt_stat->cs_mutex);
++ memcpy(&lower_ia, ia, sizeof(lower_ia));
++ if (ia->ia_valid & ATTR_FILE)
++ lower_ia.ia_file = ecryptfs_file_to_lower(ia->ia_file);
+ 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);
++ rc = truncate_upper(dentry, ia, &lower_ia);
+ if (rc < 0)
+ goto out;
+ }
+@@ -946,11 +981,11 @@ static int ecryptfs_setattr(struct dentr
+ * mode change is for clearing setuid/setgid bits. Allow lower fs
+ * to interpret this in its own way.
+ */
+- if (ia->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID))
+- ia->ia_valid &= ~ATTR_MODE;
++ if (lower_ia.ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID))
++ lower_ia.ia_valid &= ~ATTR_MODE;
+
+ mutex_lock(&lower_dentry->d_inode->i_mutex);
+- rc = notify_change(lower_dentry, ia);
++ rc = notify_change(lower_dentry, &lower_ia);
+ mutex_unlock(&lower_dentry->d_inode->i_mutex);
+ out:
+ fsstack_copy_attr_all(inode, lower_inode, NULL);