Skip to content

Commit 7a3f595

Browse files
Eric SandeenLinus Torvalds
authored andcommitted
ecryptfs: fix fsx data corruption problems
ecryptfs in 2.6.24-rc3 wasn't surviving fsx for me at all, dying after 4 ops. Generally, encountering problems with stale data and improperly zeroed pages. An extending truncate + write for example would expose stale data. With the changes below I got to a million ops and beyond with all mmap ops disabled - mmap still needs work. (A version of this patch on a RHEL5 kernel ran for over 110 million fsx ops) I added a few comments as well, to the best of my understanding as I read through the code. Signed-off-by: Eric Sandeen <sandeen@redhat.com> Acked-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>
1 parent 8998979 commit 7a3f595

File tree

2 files changed

+41
-17
lines changed

2 files changed

+41
-17
lines changed

fs/ecryptfs/mmap.c

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -263,14 +263,13 @@ static int fill_zeros_to_end_of_page(struct page *page, unsigned int to)
263263
return 0;
264264
}
265265

266+
/* This function must zero any hole we create */
266267
static int ecryptfs_prepare_write(struct file *file, struct page *page,
267268
unsigned from, unsigned to)
268269
{
269270
int rc = 0;
271+
loff_t prev_page_end_size;
270272

271-
if (from == 0 && to == PAGE_CACHE_SIZE)
272-
goto out; /* If we are writing a full page, it will be
273-
up to date. */
274273
if (!PageUptodate(page)) {
275274
rc = ecryptfs_read_lower_page_segment(page, page->index, 0,
276275
PAGE_CACHE_SIZE,
@@ -283,22 +282,32 @@ static int ecryptfs_prepare_write(struct file *file, struct page *page,
283282
} else
284283
SetPageUptodate(page);
285284
}
286-
if (page->index != 0) {
287-
loff_t end_of_prev_pg_pos =
288-
(((loff_t)page->index << PAGE_CACHE_SHIFT) - 1);
289285

290-
if (end_of_prev_pg_pos > i_size_read(page->mapping->host)) {
286+
prev_page_end_size = ((loff_t)page->index << PAGE_CACHE_SHIFT);
287+
288+
/*
289+
* If creating a page or more of holes, zero them out via truncate.
290+
* Note, this will increase i_size.
291+
*/
292+
if (page->index != 0) {
293+
if (prev_page_end_size > i_size_read(page->mapping->host)) {
291294
rc = ecryptfs_truncate(file->f_path.dentry,
292-
end_of_prev_pg_pos);
295+
prev_page_end_size);
293296
if (rc) {
294297
printk(KERN_ERR "Error on attempt to "
295298
"truncate to (higher) offset [%lld];"
296-
" rc = [%d]\n", end_of_prev_pg_pos, rc);
299+
" rc = [%d]\n", prev_page_end_size, rc);
297300
goto out;
298301
}
299302
}
300-
if (end_of_prev_pg_pos + 1 > i_size_read(page->mapping->host))
301-
zero_user_page(page, 0, PAGE_CACHE_SIZE, KM_USER0);
303+
}
304+
/*
305+
* Writing to a new page, and creating a small hole from start of page?
306+
* Zero it out.
307+
*/
308+
if ((i_size_read(page->mapping->host) == prev_page_end_size) &&
309+
(from != 0)) {
310+
zero_user_page(page, 0, PAGE_CACHE_SIZE, KM_USER0);
302311
}
303312
out:
304313
return rc;

fs/ecryptfs/read_write.c

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@ int ecryptfs_write(struct file *ecryptfs_file, char *data, loff_t offset,
124124
loff_t pos;
125125
int rc = 0;
126126

127+
/*
128+
* if we are writing beyond current size, then start pos
129+
* at the current size - we'll fill in zeros from there.
130+
*/
127131
if (offset > ecryptfs_file_size)
128132
pos = ecryptfs_file_size;
129133
else
@@ -137,6 +141,7 @@ int ecryptfs_write(struct file *ecryptfs_file, char *data, loff_t offset,
137141
if (num_bytes > total_remaining_bytes)
138142
num_bytes = total_remaining_bytes;
139143
if (pos < offset) {
144+
/* remaining zeros to write, up to destination offset */
140145
size_t total_remaining_zeros = (offset - pos);
141146

142147
if (num_bytes > total_remaining_zeros)
@@ -167,17 +172,27 @@ int ecryptfs_write(struct file *ecryptfs_file, char *data, loff_t offset,
167172
}
168173
}
169174
ecryptfs_page_virt = kmap_atomic(ecryptfs_page, KM_USER0);
175+
176+
/*
177+
* pos: where we're now writing, offset: where the request was
178+
* If current pos is before request, we are filling zeros
179+
* If we are at or beyond request, we are writing the *data*
180+
* If we're in a fresh page beyond eof, zero it in either case
181+
*/
182+
if (pos < offset || !start_offset_in_page) {
183+
/* We are extending past the previous end of the file.
184+
* Fill in zero values to the end of the page */
185+
memset(((char *)ecryptfs_page_virt
186+
+ start_offset_in_page), 0,
187+
PAGE_CACHE_SIZE - start_offset_in_page);
188+
}
189+
190+
/* pos >= offset, we are now writing the data request */
170191
if (pos >= offset) {
171192
memcpy(((char *)ecryptfs_page_virt
172193
+ start_offset_in_page),
173194
(data + data_offset), num_bytes);
174195
data_offset += num_bytes;
175-
} else {
176-
/* We are extending past the previous end of the file.
177-
* Fill in zero values up to the start of where we
178-
* will be writing data. */
179-
memset(((char *)ecryptfs_page_virt
180-
+ start_offset_in_page), 0, num_bytes);
181196
}
182197
kunmap_atomic(ecryptfs_page_virt, KM_USER0);
183198
flush_dcache_page(ecryptfs_page);

0 commit comments

Comments
 (0)