diff options
author | Michael Halcrow <mhalcrow@us.ibm.com> | 2007-06-27 17:09:44 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-06-28 14:34:53 -0400 |
commit | 240e2df5c740d73fc08cac9989872212deb2d20e (patch) | |
tree | 538dc70281c5401f3843ac8c752b2092f79ba9ee /fs | |
parent | b75ae8603568ae18f270213693758c78fb8a29ff (diff) |
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>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ecryptfs/ecryptfs_kernel.h | 2 | ||||
-rw-r--r-- | fs/ecryptfs/inode.c | 19 | ||||
-rw-r--r-- | fs/ecryptfs/mmap.c | 53 |
3 files changed, 50 insertions, 24 deletions
diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h index 403e3bad1455..1b9dd9a96f19 100644 --- a/fs/ecryptfs/ecryptfs_kernel.h +++ b/fs/ecryptfs/ecryptfs_kernel.h | |||
@@ -580,5 +580,7 @@ void | |||
580 | ecryptfs_write_header_metadata(char *virt, | 580 | ecryptfs_write_header_metadata(char *virt, |
581 | struct ecryptfs_crypt_stat *crypt_stat, | 581 | struct ecryptfs_crypt_stat *crypt_stat, |
582 | size_t *written); | 582 | size_t *written); |
583 | int ecryptfs_write_zeros(struct file *file, pgoff_t index, int start, | ||
584 | int num_zeros); | ||
583 | 585 | ||
584 | #endif /* #ifndef ECRYPTFS_KERNEL_H */ | 586 | #endif /* #ifndef ECRYPTFS_KERNEL_H */ |
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 1548be26b5e6..0981ae35ea16 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c | |||
@@ -800,6 +800,25 @@ int ecryptfs_truncate(struct dentry *dentry, loff_t new_length) | |||
800 | goto out_fput; | 800 | goto out_fput; |
801 | } | 801 | } |
802 | } else { /* new_length < i_size_read(inode) */ | 802 | } else { /* new_length < i_size_read(inode) */ |
803 | pgoff_t index = 0; | ||
804 | int end_pos_in_page = -1; | ||
805 | |||
806 | if (new_length != 0) { | ||
807 | index = ((new_length - 1) >> PAGE_CACHE_SHIFT); | ||
808 | end_pos_in_page = ((new_length - 1) & ~PAGE_CACHE_MASK); | ||
809 | } | ||
810 | if (end_pos_in_page != (PAGE_CACHE_SIZE - 1)) { | ||
811 | if ((rc = ecryptfs_write_zeros(&fake_ecryptfs_file, | ||
812 | index, | ||
813 | (end_pos_in_page + 1), | ||
814 | ((PAGE_CACHE_SIZE - 1) | ||
815 | - end_pos_in_page)))) { | ||
816 | printk(KERN_ERR "Error attempting to zero out " | ||
817 | "the remainder of the end page on " | ||
818 | "reducing truncate; rc = [%d]\n", rc); | ||
819 | goto out_fput; | ||
820 | } | ||
821 | } | ||
803 | vmtruncate(inode, new_length); | 822 | vmtruncate(inode, new_length); |
804 | rc = ecryptfs_write_inode_size_to_metadata( | 823 | rc = ecryptfs_write_inode_size_to_metadata( |
805 | lower_file, lower_dentry->d_inode, inode, dentry, | 824 | lower_file, lower_dentry->d_inode, inode, dentry, |
diff --git a/fs/ecryptfs/mmap.c b/fs/ecryptfs/mmap.c index 55cec98a84e7..6df410c77264 100644 --- a/fs/ecryptfs/mmap.c +++ b/fs/ecryptfs/mmap.c | |||
@@ -56,9 +56,6 @@ static struct page *ecryptfs_get1page(struct file *file, int index) | |||
56 | return read_mapping_page(mapping, index, (void *)file); | 56 | return read_mapping_page(mapping, index, (void *)file); |
57 | } | 57 | } |
58 | 58 | ||
59 | static | ||
60 | int write_zeros(struct file *file, pgoff_t index, int start, int num_zeros); | ||
61 | |||
62 | /** | 59 | /** |
63 | * ecryptfs_fill_zeros | 60 | * ecryptfs_fill_zeros |
64 | * @file: The ecryptfs file | 61 | * @file: The ecryptfs file |
@@ -101,10 +98,13 @@ int ecryptfs_fill_zeros(struct file *file, loff_t new_length) | |||
101 | if (old_end_page_index == new_end_page_index) { | 98 | if (old_end_page_index == new_end_page_index) { |
102 | /* Start and end are in the same page; we just need to | 99 | /* Start and end are in the same page; we just need to |
103 | * set a portion of the existing page to zero's */ | 100 | * set a portion of the existing page to zero's */ |
104 | rc = write_zeros(file, index, (old_end_pos_in_page + 1), | 101 | rc = ecryptfs_write_zeros(file, index, |
105 | (new_end_pos_in_page - old_end_pos_in_page)); | 102 | (old_end_pos_in_page + 1), |
103 | (new_end_pos_in_page | ||
104 | - old_end_pos_in_page)); | ||
106 | if (rc) | 105 | if (rc) |
107 | ecryptfs_printk(KERN_ERR, "write_zeros(file=[%p], " | 106 | ecryptfs_printk(KERN_ERR, "ecryptfs_write_zeros(" |
107 | "file=[%p], " | ||
108 | "index=[0x%.16x], " | 108 | "index=[0x%.16x], " |
109 | "old_end_pos_in_page=[d], " | 109 | "old_end_pos_in_page=[d], " |
110 | "(PAGE_CACHE_SIZE - new_end_pos_in_page" | 110 | "(PAGE_CACHE_SIZE - new_end_pos_in_page" |
@@ -117,10 +117,10 @@ int ecryptfs_fill_zeros(struct file *file, loff_t new_length) | |||
117 | goto out; | 117 | goto out; |
118 | } | 118 | } |
119 | /* Fill the remainder of the previous last page with zeros */ | 119 | /* Fill the remainder of the previous last page with zeros */ |
120 | rc = write_zeros(file, index, (old_end_pos_in_page + 1), | 120 | rc = ecryptfs_write_zeros(file, index, (old_end_pos_in_page + 1), |
121 | ((PAGE_CACHE_SIZE - 1) - old_end_pos_in_page)); | 121 | ((PAGE_CACHE_SIZE - 1) - old_end_pos_in_page)); |
122 | if (rc) { | 122 | if (rc) { |
123 | ecryptfs_printk(KERN_ERR, "write_zeros(file=[%p], " | 123 | ecryptfs_printk(KERN_ERR, "ecryptfs_write_zeros(file=[%p], " |
124 | "index=[0x%.16x], old_end_pos_in_page=[d], " | 124 | "index=[0x%.16x], old_end_pos_in_page=[d], " |
125 | "(PAGE_CACHE_SIZE - old_end_pos_in_page)=[d]) " | 125 | "(PAGE_CACHE_SIZE - old_end_pos_in_page)=[d]) " |
126 | "returned [%d]\n", file, index, | 126 | "returned [%d]\n", file, index, |
@@ -131,9 +131,10 @@ int ecryptfs_fill_zeros(struct file *file, loff_t new_length) | |||
131 | index++; | 131 | index++; |
132 | while (index < new_end_page_index) { | 132 | while (index < new_end_page_index) { |
133 | /* Fill all intermediate pages with zeros */ | 133 | /* Fill all intermediate pages with zeros */ |
134 | rc = write_zeros(file, index, 0, PAGE_CACHE_SIZE); | 134 | rc = ecryptfs_write_zeros(file, index, 0, PAGE_CACHE_SIZE); |
135 | if (rc) { | 135 | if (rc) { |
136 | ecryptfs_printk(KERN_ERR, "write_zeros(file=[%p], " | 136 | ecryptfs_printk(KERN_ERR, "ecryptfs_write_zeros(" |
137 | "file=[%p], " | ||
137 | "index=[0x%.16x], " | 138 | "index=[0x%.16x], " |
138 | "old_end_pos_in_page=[d], " | 139 | "old_end_pos_in_page=[d], " |
139 | "(PAGE_CACHE_SIZE - new_end_pos_in_page" | 140 | "(PAGE_CACHE_SIZE - new_end_pos_in_page" |
@@ -149,9 +150,9 @@ int ecryptfs_fill_zeros(struct file *file, loff_t new_length) | |||
149 | } | 150 | } |
150 | /* Fill the portion at the beginning of the last new page with | 151 | /* Fill the portion at the beginning of the last new page with |
151 | * zero's */ | 152 | * zero's */ |
152 | rc = write_zeros(file, index, 0, (new_end_pos_in_page + 1)); | 153 | rc = ecryptfs_write_zeros(file, index, 0, (new_end_pos_in_page + 1)); |
153 | if (rc) { | 154 | if (rc) { |
154 | ecryptfs_printk(KERN_ERR, "write_zeros(file=" | 155 | ecryptfs_printk(KERN_ERR, "ecryptfs_write_zeros(file=" |
155 | "[%p], index=[0x%.16x], 0, " | 156 | "[%p], index=[0x%.16x], 0, " |
156 | "new_end_pos_in_page=[%d]" | 157 | "new_end_pos_in_page=[%d]" |
157 | "returned [%d]\n", file, index, | 158 | "returned [%d]\n", file, index, |
@@ -400,7 +401,6 @@ out: | |||
400 | static int ecryptfs_prepare_write(struct file *file, struct page *page, | 401 | static int ecryptfs_prepare_write(struct file *file, struct page *page, |
401 | unsigned from, unsigned to) | 402 | unsigned from, unsigned to) |
402 | { | 403 | { |
403 | loff_t pos; | ||
404 | int rc = 0; | 404 | int rc = 0; |
405 | 405 | ||
406 | if (from == 0 && to == PAGE_CACHE_SIZE) | 406 | if (from == 0 && to == PAGE_CACHE_SIZE) |
@@ -408,14 +408,19 @@ static int ecryptfs_prepare_write(struct file *file, struct page *page, | |||
408 | up to date. */ | 408 | up to date. */ |
409 | if (!PageUptodate(page)) | 409 | if (!PageUptodate(page)) |
410 | rc = ecryptfs_do_readpage(file, page, page->index); | 410 | rc = ecryptfs_do_readpage(file, page, page->index); |
411 | pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to; | 411 | if (page->index != 0) { |
412 | if (pos > i_size_read(page->mapping->host)) { | 412 | loff_t end_of_prev_pg_pos = |
413 | rc = ecryptfs_truncate(file->f_path.dentry, pos); | 413 | (((loff_t)page->index << PAGE_CACHE_SHIFT) - 1); |
414 | if (rc) { | 414 | |
415 | printk(KERN_ERR "Error on attempt to " | 415 | if (end_of_prev_pg_pos > i_size_read(page->mapping->host)) { |
416 | "truncate to (higher) offset [%lld];" | 416 | rc = ecryptfs_truncate(file->f_path.dentry, |
417 | " rc = [%d]\n", pos, rc); | 417 | end_of_prev_pg_pos); |
418 | goto out; | 418 | if (rc) { |
419 | printk(KERN_ERR "Error on attempt to " | ||
420 | "truncate to (higher) offset [%lld];" | ||
421 | " rc = [%d]\n", end_of_prev_pg_pos, rc); | ||
422 | goto out; | ||
423 | } | ||
419 | } | 424 | } |
420 | } | 425 | } |
421 | out: | 426 | out: |
@@ -753,7 +758,7 @@ out: | |||
753 | } | 758 | } |
754 | 759 | ||
755 | /** | 760 | /** |
756 | * write_zeros | 761 | * ecryptfs_write_zeros |
757 | * @file: The ecryptfs file | 762 | * @file: The ecryptfs file |
758 | * @index: The index in which we are writing | 763 | * @index: The index in which we are writing |
759 | * @start: The position after the last block of data | 764 | * @start: The position after the last block of data |
@@ -763,8 +768,8 @@ out: | |||
763 | * | 768 | * |
764 | * (start + num_zeros) must be less than or equal to PAGE_CACHE_SIZE | 769 | * (start + num_zeros) must be less than or equal to PAGE_CACHE_SIZE |
765 | */ | 770 | */ |
766 | static | 771 | int |
767 | int write_zeros(struct file *file, pgoff_t index, int start, int num_zeros) | 772 | ecryptfs_write_zeros(struct file *file, pgoff_t index, int start, int num_zeros) |
768 | { | 773 | { |
769 | int rc = 0; | 774 | int rc = 0; |
770 | struct page *tmp_page; | 775 | struct page *tmp_page; |