eCryptfs: fix write zeros behavior

This patch fixes the processes involved in wiping regions of the data during
truncate and write events, fixing a kernel hang in 2.6.22-rc4 while assuring
that zero values are written out to the appropriate locations during events in
which the i_size will change.

The range passed to ecryptfs_truncate() from ecryptfs_prepare_write() includes
the page that is the object of ecryptfs_prepare_write().  This leads to a
kernel hang as read_cache_page() is executed on the same page in the
ecryptfs_truncate() execution path.  This patch remedies this by limiting the
range passed to ecryptfs_truncate() so as to exclude the page that is the
object of ecryptfs_prepare_write(); it also adds code to
ecryptfs_prepare_write() to zero out the region of its own page when writing
past the i_size position.  This patch also modifies ecryptfs_truncate() so
that when a file is truncated to a smaller size, eCryptfs will zero out the
contents of the new last page from the new size through to the end of the last
page.

Signed-off-by: Michael Halcrow <mhalcrow@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index 1548be2..0981ae3 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -800,6 +800,25 @@
 			goto out_fput;
 		}
 	} else { /* new_length < i_size_read(inode) */
+		pgoff_t index = 0;
+		int end_pos_in_page = -1;
+
+		if (new_length != 0) {
+			index = ((new_length - 1) >> PAGE_CACHE_SHIFT);
+			end_pos_in_page = ((new_length - 1) & ~PAGE_CACHE_MASK);
+		}
+		if (end_pos_in_page != (PAGE_CACHE_SIZE - 1)) {
+			if ((rc = ecryptfs_write_zeros(&fake_ecryptfs_file,
+						       index,
+						       (end_pos_in_page + 1),
+						       ((PAGE_CACHE_SIZE - 1)
+							- end_pos_in_page)))) {
+				printk(KERN_ERR "Error attempting to zero out "
+				       "the remainder of the end page on "
+				       "reducing truncate; rc = [%d]\n", rc);
+				goto out_fput;
+			}
+		}
 		vmtruncate(inode, new_length);
 		rc = ecryptfs_write_inode_size_to_metadata(
 			lower_file, lower_dentry->d_inode, inode, dentry,