eCryptfs: delay writing 0's after llseek until write
Delay writing 0's out in eCryptfs after a seek past the end of the file until data is actually written. http://www.opengroup.org/onlinepubs/009695399/functions/lseek.html ``The lseek() function shall not, by itself, extend the size of a file.'' Without this fix, applications that lseek() past the end of the file without writing will experience unexpected behavior. 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>
This commit is contained in:
Родитель
4acb3e2f97
Коммит
53a2731f93
|
@ -32,63 +32,6 @@
|
||||||
#include <linux/fs_stack.h>
|
#include <linux/fs_stack.h>
|
||||||
#include "ecryptfs_kernel.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_path.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", new_end_pos, rc);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rv = generic_file_llseek(file, offset, origin);
|
|
||||||
out:
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ecryptfs_read_update_atime
|
* ecryptfs_read_update_atime
|
||||||
*
|
*
|
||||||
|
@ -425,7 +368,7 @@ const struct file_operations ecryptfs_dir_fops = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const struct file_operations ecryptfs_main_fops = {
|
const struct file_operations ecryptfs_main_fops = {
|
||||||
.llseek = ecryptfs_llseek,
|
.llseek = generic_file_llseek,
|
||||||
.read = do_sync_read,
|
.read = do_sync_read,
|
||||||
.aio_read = ecryptfs_read_update_atime,
|
.aio_read = ecryptfs_read_update_atime,
|
||||||
.write = do_sync_write,
|
.write = do_sync_write,
|
||||||
|
|
|
@ -376,8 +376,15 @@ out:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ecryptfs_prepare_write(struct file *file, struct page *page,
|
/**
|
||||||
unsigned from, unsigned to)
|
* eCryptfs does not currently support holes. When writing after a
|
||||||
|
* seek past the end of the file, eCryptfs fills in 0's through to the
|
||||||
|
* current location. The code to fill in the 0's to all the
|
||||||
|
* intermediate pages calls ecryptfs_prepare_write_no_truncate().
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
ecryptfs_prepare_write_no_truncate(struct file *file, struct page *page,
|
||||||
|
unsigned from, unsigned to)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
|
@ -390,6 +397,31 @@ out:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ecryptfs_prepare_write(struct file *file, struct page *page,
|
||||||
|
unsigned from, unsigned to)
|
||||||
|
{
|
||||||
|
loff_t pos;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
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);
|
||||||
|
pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to;
|
||||||
|
if (pos > i_size_read(page->mapping->host)) {
|
||||||
|
rc = ecryptfs_truncate(file->f_path.dentry, pos);
|
||||||
|
if (rc) {
|
||||||
|
printk(KERN_ERR "Error on attempt to "
|
||||||
|
"truncate to (higher) offset [%lld];"
|
||||||
|
" rc = [%d]\n", pos, rc);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
int ecryptfs_writepage_and_release_lower_page(struct page *lower_page,
|
int ecryptfs_writepage_and_release_lower_page(struct page *lower_page,
|
||||||
struct inode *lower_inode,
|
struct inode *lower_inode,
|
||||||
struct writeback_control *wbc)
|
struct writeback_control *wbc)
|
||||||
|
@ -744,10 +776,10 @@ int write_zeros(struct file *file, pgoff_t index, int start, int num_zeros)
|
||||||
rc = PTR_ERR(tmp_page);
|
rc = PTR_ERR(tmp_page);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
rc = ecryptfs_prepare_write(file, tmp_page, start, start + num_zeros);
|
if ((rc = ecryptfs_prepare_write_no_truncate(file, tmp_page, start,
|
||||||
if (rc) {
|
(start + num_zeros)))) {
|
||||||
ecryptfs_printk(KERN_ERR, "Error preparing to write zero's "
|
ecryptfs_printk(KERN_ERR, "Error preparing to write zero's "
|
||||||
"to remainder of page at index [0x%.16x]\n",
|
"to page at index [0x%.16x]\n",
|
||||||
index);
|
index);
|
||||||
page_cache_release(tmp_page);
|
page_cache_release(tmp_page);
|
||||||
goto out;
|
goto out;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче