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: Michael Halcrow <mhalcrow@us.ibm.com> Cc: Benedikt Driessen <bdriessen@escrypt.com> Cc: <stable@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
committed by
Linus Torvalds
parent
87ffbe679e
commit
e4465fdaeb
@@ -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(
|
||||||
|
file->f_path.dentry->d_inode)->crypt_stat;
|
||||||
|
|
||||||
|
if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)
|
||||||
|
|| (crypt_stat->flags & ECRYPTFS_NEW_FILE)) {
|
||||||
|
rc = ecryptfs_read_lower_page_segment(
|
||||||
|
page, page->index, 0, PAGE_CACHE_SIZE,
|
||||||
page->mapping->host);
|
page->mapping->host);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
printk(KERN_ERR "%s: Error attemping to read lower "
|
printk(KERN_ERR "%s: Error attemping to read "
|
||||||
"page segment; rc = [%d]\n", __FUNCTION__, rc);
|
"lower page segment; rc = [%d]\n",
|
||||||
|
__FUNCTION__, rc);
|
||||||
ClearPageUptodate(page);
|
ClearPageUptodate(page);
|
||||||
goto out;
|
goto out;
|
||||||
} else
|
} else
|
||||||
SetPageUptodate(page);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user