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>

authored by Eric Sandeen and committed by Linus Torvalds 7a3f595c 8998979c

+41 -17
+20 -11
fs/ecryptfs/mmap.c
··· 263 263 return 0; 264 264 } 265 265 266 + /* This function must zero any hole we create */ 266 267 static int ecryptfs_prepare_write(struct file *file, struct page *page, 267 268 unsigned from, unsigned to) 268 269 { 269 270 int rc = 0; 271 + loff_t prev_page_end_size; 270 272 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. */ 274 273 if (!PageUptodate(page)) { 275 274 rc = ecryptfs_read_lower_page_segment(page, page->index, 0, 276 275 PAGE_CACHE_SIZE, ··· 282 283 } else 283 284 SetPageUptodate(page); 284 285 } 285 - if (page->index != 0) { 286 - loff_t end_of_prev_pg_pos = 287 - (((loff_t)page->index << PAGE_CACHE_SHIFT) - 1); 288 286 289 - if (end_of_prev_pg_pos > i_size_read(page->mapping->host)) { 287 + prev_page_end_size = ((loff_t)page->index << PAGE_CACHE_SHIFT); 288 + 289 + /* 290 + * If creating a page or more of holes, zero them out via truncate. 291 + * Note, this will increase i_size. 292 + */ 293 + if (page->index != 0) { 294 + if (prev_page_end_size > i_size_read(page->mapping->host)) { 290 295 rc = ecryptfs_truncate(file->f_path.dentry, 291 - end_of_prev_pg_pos); 296 + prev_page_end_size); 292 297 if (rc) { 293 298 printk(KERN_ERR "Error on attempt to " 294 299 "truncate to (higher) offset [%lld];" 295 - " rc = [%d]\n", end_of_prev_pg_pos, rc); 300 + " rc = [%d]\n", prev_page_end_size, rc); 296 301 goto out; 297 302 } 298 303 } 299 - if (end_of_prev_pg_pos + 1 > i_size_read(page->mapping->host)) 300 - zero_user_page(page, 0, PAGE_CACHE_SIZE, KM_USER0); 304 + } 305 + /* 306 + * Writing to a new page, and creating a small hole from start of page? 307 + * Zero it out. 308 + */ 309 + if ((i_size_read(page->mapping->host) == prev_page_end_size) && 310 + (from != 0)) { 311 + zero_user_page(page, 0, PAGE_CACHE_SIZE, KM_USER0); 301 312 } 302 313 out: 303 314 return rc;
+21 -6
fs/ecryptfs/read_write.c
··· 124 124 loff_t pos; 125 125 int rc = 0; 126 126 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 + */ 127 131 if (offset > ecryptfs_file_size) 128 132 pos = ecryptfs_file_size; 129 133 else ··· 141 137 if (num_bytes > total_remaining_bytes) 142 138 num_bytes = total_remaining_bytes; 143 139 if (pos < offset) { 140 + /* remaining zeros to write, up to destination offset */ 144 141 size_t total_remaining_zeros = (offset - pos); 145 142 146 143 if (num_bytes > total_remaining_zeros) ··· 172 167 } 173 168 } 174 169 ecryptfs_page_virt = kmap_atomic(ecryptfs_page, KM_USER0); 170 + 171 + /* 172 + * pos: where we're now writing, offset: where the request was 173 + * If current pos is before request, we are filling zeros 174 + * If we are at or beyond request, we are writing the *data* 175 + * If we're in a fresh page beyond eof, zero it in either case 176 + */ 177 + if (pos < offset || !start_offset_in_page) { 178 + /* We are extending past the previous end of the file. 179 + * Fill in zero values to the end of the page */ 180 + memset(((char *)ecryptfs_page_virt 181 + + start_offset_in_page), 0, 182 + PAGE_CACHE_SIZE - start_offset_in_page); 183 + } 184 + 185 + /* pos >= offset, we are now writing the data request */ 175 186 if (pos >= offset) { 176 187 memcpy(((char *)ecryptfs_page_virt 177 188 + start_offset_in_page), 178 189 (data + data_offset), num_bytes); 179 190 data_offset += num_bytes; 180 - } else { 181 - /* We are extending past the previous end of the file. 182 - * Fill in zero values up to the start of where we 183 - * will be writing data. */ 184 - memset(((char *)ecryptfs_page_virt 185 - + start_offset_in_page), 0, num_bytes); 186 191 } 187 192 kunmap_atomic(ecryptfs_page_virt, KM_USER0); 188 193 flush_dcache_page(ecryptfs_page);