Commit e4465fda authored by Michael Halcrow's avatar Michael Halcrow Committed by Linus Torvalds

eCryptfs: make ecryptfs_prepare_write decrypt the page

When the page is not up to date, ecryptfs_prepare_write() should be
acting much like ecryptfs_readpage(). This includes the painfully
obvious step of actually decrypting the page contents read from the
lower encrypted file.

Note that this patch resolves a bug in eCryptfs in 2.6.24 that one can
produce with these steps:

# mount -t ecryptfs /secret /secret
# echo "abc" > /secret/file.txt
# umount /secret
# mount -t ecryptfs /secret /secret
# echo "def" >> /secret/file.txt
# cat /secret/file.txt

Without this patch, the resulting data returned from cat is likely to
be something other than "abc\ndef\n".

(Thanks to Benedikt Driessen for reporting this.)
Signed-off-by: default avatarMichael Halcrow <mhalcrow@us.ibm.com>
Cc: Benedikt Driessen <bdriessen@escrypt.com>
Cc: <stable@kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 87ffbe67
...@@ -263,52 +263,102 @@ out: ...@@ -263,52 +263,102 @@ out:
return 0; return 0;
} }
/* This function must zero any hole we create */ /**
* ecryptfs_prepare_write
* @file: The eCryptfs file
* @page: The eCryptfs page
* @from: The start byte from which we will write
* @to: The end byte to which we will write
*
* This function must zero any hole we create
*
* Returns zero on success; non-zero otherwise
*/
static int ecryptfs_prepare_write(struct file *file, struct page *page, static int ecryptfs_prepare_write(struct file *file, struct page *page,
unsigned from, unsigned to) unsigned from, unsigned to)
{ {
int rc = 0;
loff_t prev_page_end_size; loff_t prev_page_end_size;
int rc = 0;
if (!PageUptodate(page)) { if (!PageUptodate(page)) {
rc = ecryptfs_read_lower_page_segment(page, page->index, 0, struct ecryptfs_crypt_stat *crypt_stat =
PAGE_CACHE_SIZE, &ecryptfs_inode_to_private(
page->mapping->host); file->f_path.dentry->d_inode)->crypt_stat;
if (rc) {
printk(KERN_ERR "%s: Error attemping to read lower " if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)
"page segment; rc = [%d]\n", __FUNCTION__, rc); || (crypt_stat->flags & ECRYPTFS_NEW_FILE)) {
ClearPageUptodate(page); rc = ecryptfs_read_lower_page_segment(
goto out; page, page->index, 0, PAGE_CACHE_SIZE,
} else page->mapping->host);
if (rc) {
printk(KERN_ERR "%s: Error attemping to read "
"lower page segment; rc = [%d]\n",
__FUNCTION__, rc);
ClearPageUptodate(page);
goto out;
} else
SetPageUptodate(page);
} else if (crypt_stat->flags & ECRYPTFS_VIEW_AS_ENCRYPTED) {
if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR) {
rc = ecryptfs_copy_up_encrypted_with_header(
page, crypt_stat);
if (rc) {
printk(KERN_ERR "%s: Error attempting "
"to copy the encrypted content "
"from the lower file whilst "
"inserting the metadata from "
"the xattr into the header; rc "
"= [%d]\n", __FUNCTION__, rc);
ClearPageUptodate(page);
goto out;
}
SetPageUptodate(page);
} else {
rc = ecryptfs_read_lower_page_segment(
page, page->index, 0, PAGE_CACHE_SIZE,
page->mapping->host);
if (rc) {
printk(KERN_ERR "%s: Error reading "
"page; rc = [%d]\n",
__FUNCTION__, rc);
ClearPageUptodate(page);
goto out;
}
SetPageUptodate(page);
}
} else {
rc = ecryptfs_decrypt_page(page);
if (rc) {
printk(KERN_ERR "%s: Error decrypting page "
"at index [%ld]; rc = [%d]\n",
__FUNCTION__, page->index, rc);
ClearPageUptodate(page);
goto out;
}
SetPageUptodate(page); SetPageUptodate(page);
}
} }
prev_page_end_size = ((loff_t)page->index << PAGE_CACHE_SHIFT); prev_page_end_size = ((loff_t)page->index << PAGE_CACHE_SHIFT);
/* If creating a page or more of holes, zero them out via truncate.
/* * Note, this will increase i_size. */
* If creating a page or more of holes, zero them out via truncate.
* Note, this will increase i_size.
*/
if (page->index != 0) { if (page->index != 0) {
if (prev_page_end_size > i_size_read(page->mapping->host)) { if (prev_page_end_size > i_size_read(page->mapping->host)) {
rc = ecryptfs_truncate(file->f_path.dentry, rc = ecryptfs_truncate(file->f_path.dentry,
prev_page_end_size); prev_page_end_size);
if (rc) { if (rc) {
printk(KERN_ERR "Error on attempt to " printk(KERN_ERR "%s: Error on attempt to "
"truncate to (higher) offset [%lld];" "truncate to (higher) offset [%lld];"
" rc = [%d]\n", prev_page_end_size, rc); " rc = [%d]\n", __FUNCTION__,
prev_page_end_size, rc);
goto out; goto out;
} }
} }
} }
/* /* Writing to a new page, and creating a small hole from start
* Writing to a new page, and creating a small hole from start of page? * of page? Zero it out. */
* Zero it out. if ((i_size_read(page->mapping->host) == prev_page_end_size)
*/ && (from != 0))
if ((i_size_read(page->mapping->host) == prev_page_end_size) &&
(from != 0)) {
zero_user(page, 0, PAGE_CACHE_SIZE); zero_user(page, 0, PAGE_CACHE_SIZE);
}
out: out:
return rc; return rc;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment