add patch rename-GOINGDOWN-to-SHUTDOWN
[ext4-patch-queue.git] / fix-data-corruption-in-data_journal-mode
blob7777a268d17a3298a799ead966815cee1b396d64
1 ext4: fix data corruption in data=journal mode
3 From: Jan Kara <jack@suse.cz>
5 ext4_journalled_write_end() did not propely handle all the cases when
6 generic_perform_write() did not copy all the data into the target page
7 and could mark buffers with uninitialized contents as uptodate and dirty
8 leading to possible data corruption (which would be quickly fixed by
9 generic_perform_write() retrying the write but still). Fix the problem
10 by carefully handling the case when the page that is written to is not
11 uptodate.
13 CC: stable@vger.kernel.org
14 Reported-by: Al Viro <viro@ZenIV.linux.org.uk>
15 Signed-off-by: Jan Kara <jack@suse.cz>
16 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
17 ---
18  fs/ext4/inode.c | 23 +++++++++++++----------
19  1 file changed, 13 insertions(+), 10 deletions(-)
21 diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
22 index 88d57af1b516..4bb9136b0221 100644
23 --- a/fs/ext4/inode.c
24 +++ b/fs/ext4/inode.c
25 @@ -1385,7 +1385,9 @@ static int ext4_write_end(struct file *file,
26   * set the buffer to be dirty, since in data=journalled mode we need
27   * to call ext4_handle_dirty_metadata() instead.
28   */
29 -static void zero_new_buffers(struct page *page, unsigned from, unsigned to)
30 +static void ext4_journalled_zero_new_buffers(handle_t *handle,
31 +                                           struct page *page,
32 +                                           unsigned from, unsigned to)
33  {
34         unsigned int block_start = 0, block_end;
35         struct buffer_head *head, *bh;
36 @@ -1402,7 +1404,7 @@ static void zero_new_buffers(struct page *page, unsigned from, unsigned to)
37                                         size = min(to, block_end) - start;
39                                         zero_user(page, start, size);
40 -                                       set_buffer_uptodate(bh);
41 +                                       write_end_fn(handle, bh);
42                                 }
43                                 clear_buffer_new(bh);
44                         }
45 @@ -1434,15 +1436,16 @@ static int ext4_journalled_write_end(struct file *file,
46         if (ext4_has_inline_data(inode))
47                 copied = ext4_write_inline_data_end(inode, pos, len,
48                                                     copied, page);
49 -       else {
50 -               if (copied < len) {
51 -                       if (!PageUptodate(page))
52 -                               copied = 0;
53 -                       zero_new_buffers(page, from+copied, to);
54 -               }
56 +       else if (unlikely(copied < len) && !PageUptodate(page)) {
57 +               copied = 0;
58 +               ext4_journalled_zero_new_buffers(handle, page, from, to);
59 +       } else {
60 +               if (unlikely(copied < len))
61 +                       ext4_journalled_zero_new_buffers(handle, page,
62 +                                                        from + copied, to);
63                 ret = ext4_walk_page_buffers(handle, page_buffers(page), from,
64 -                                            to, &partial, write_end_fn);
65 +                                            from + copied, &partial,
66 +                                            write_end_fn);
67                 if (!partial)
68                         SetPageUptodate(page);
69         }
70 -- 
71 2.10.2