Apply Arnd's patches to extend superblock times beyond 2038
[ext4-patch-queue.git] / fix-inline-updates-with-csums-enabled
blob5aedf7a1eaf37ca673f3d3e013d24a3b8eda46b6
1 ext4: fix inline data updates with checksums enabled
3 The inline data code was updating the raw inode directly; this is
4 problematic since if metadata checksums are enabled,
5 ext4_mark_inode_dirty() must be called to update the inode's checksum.
6 In addition, the jbd2 layer requires that get_write_access() be called
7 before the metadata buffer is modified.  Fix both of these problems.
9 https://bugzilla.kernel.org/show_bug.cgi?id=200443
11 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
12 Cc: stable@vger.kernel.org
13 ---
14  fs/ext4/inline.c | 19 +++++++++++--------
15  fs/ext4/inode.c  | 16 +++++++---------
16  2 files changed, 18 insertions(+), 17 deletions(-)
18 diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
19 index e55a8bc870bd..3543fe80a3c4 100644
20 --- a/fs/ext4/inline.c
21 +++ b/fs/ext4/inline.c
22 @@ -682,6 +682,10 @@ int ext4_try_to_write_inline_data(struct address_space *mapping,
23                 goto convert;
24         }
26 +       ret = ext4_journal_get_write_access(handle, iloc.bh);
27 +       if (ret)
28 +               goto out;
30         flags |= AOP_FLAG_NOFS;
32         page = grab_cache_page_write_begin(mapping, 0, flags);
33 @@ -710,7 +714,7 @@ int ext4_try_to_write_inline_data(struct address_space *mapping,
34  out_up_read:
35         up_read(&EXT4_I(inode)->xattr_sem);
36  out:
37 -       if (handle)
38 +       if (handle && (ret != 1))
39                 ext4_journal_stop(handle);
40         brelse(iloc.bh);
41         return ret;
42 @@ -752,6 +756,7 @@ int ext4_write_inline_data_end(struct inode *inode, loff_t pos, unsigned len,
44         ext4_write_unlock_xattr(inode, &no_expand);
45         brelse(iloc.bh);
46 +       mark_inode_dirty(inode);
47  out:
48         return copied;
49  }
50 @@ -898,7 +903,6 @@ int ext4_da_write_inline_data_begin(struct address_space *mapping,
51                 goto out;
52         }
55         page = grab_cache_page_write_begin(mapping, 0, flags);
56         if (!page) {
57                 ret = -ENOMEM;
58 @@ -916,6 +920,9 @@ int ext4_da_write_inline_data_begin(struct address_space *mapping,
59                 if (ret < 0)
60                         goto out_release_page;
61         }
62 +       ret = ext4_journal_get_write_access(handle, iloc.bh);
63 +       if (ret)
64 +               goto out_release_page;
66         up_read(&EXT4_I(inode)->xattr_sem);
67         *pagep = page;
68 @@ -936,7 +943,6 @@ int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos,
69                                   unsigned len, unsigned copied,
70                                   struct page *page)
71  {
72 -       int i_size_changed = 0;
73         int ret;
75         ret = ext4_write_inline_data_end(inode, pos, len, copied, page);
76 @@ -954,10 +960,8 @@ int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos,
77          * But it's important to update i_size while still holding page lock:
78          * page writeout could otherwise come in and zero beyond i_size.
79          */
80 -       if (pos+copied > inode->i_size) {
81 +       if (pos+copied > inode->i_size)
82                 i_size_write(inode, pos+copied);
83 -               i_size_changed = 1;
84 -       }
85         unlock_page(page);
86         put_page(page);
88 @@ -967,8 +971,7 @@ int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos,
89          * ordering of page lock and transaction start for journaling
90          * filesystems.
91          */
92 -       if (i_size_changed)
93 -               mark_inode_dirty(inode);
94 +       mark_inode_dirty(inode);
96         return copied;
97  }
98 diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
99 index 7d6c10017bdf..4efe77286ecd 100644
100 --- a/fs/ext4/inode.c
101 +++ b/fs/ext4/inode.c
102 @@ -1389,9 +1389,10 @@ static int ext4_write_end(struct file *file,
103         loff_t old_size = inode->i_size;
104         int ret = 0, ret2;
105         int i_size_changed = 0;
106 +       int inline_data = ext4_has_inline_data(inode);
108         trace_ext4_write_end(inode, pos, len, copied);
109 -       if (ext4_has_inline_data(inode)) {
110 +       if (inline_data) {
111                 ret = ext4_write_inline_data_end(inode, pos, len,
112                                                  copied, page);
113                 if (ret < 0) {
114 @@ -1419,7 +1420,7 @@ static int ext4_write_end(struct file *file,
115          * ordering of page lock and transaction start for journaling
116          * filesystems.
117          */
118 -       if (i_size_changed)
119 +       if (i_size_changed || inline_data)
120                 ext4_mark_inode_dirty(handle, inode);
122         if (pos + len > inode->i_size && ext4_can_truncate(inode))
123 @@ -1493,6 +1494,7 @@ static int ext4_journalled_write_end(struct file *file,
124         int partial = 0;
125         unsigned from, to;
126         int size_changed = 0;
127 +       int inline_data = ext4_has_inline_data(inode);
129         trace_ext4_journalled_write_end(inode, pos, len, copied);
130         from = pos & (PAGE_SIZE - 1);
131 @@ -1500,7 +1502,7 @@ static int ext4_journalled_write_end(struct file *file,
133         BUG_ON(!ext4_handle_valid(handle));
135 -       if (ext4_has_inline_data(inode)) {
136 +       if (inline_data) {
137                 ret = ext4_write_inline_data_end(inode, pos, len,
138                                                  copied, page);
139                 if (ret < 0) {
140 @@ -1531,7 +1533,7 @@ static int ext4_journalled_write_end(struct file *file,
141         if (old_size < pos)
142                 pagecache_isize_extended(inode, old_size, pos);
144 -       if (size_changed) {
145 +       if (size_changed || inline_data) {
146                 ret2 = ext4_mark_inode_dirty(handle, inode);
147                 if (!ret)
148                         ret = ret2;
149 @@ -2028,11 +2030,7 @@ static int __ext4_journalled_writepage(struct page *page,
150         }
152         if (inline_data) {
153 -               BUFFER_TRACE(inode_bh, "get write access");
154 -               ret = ext4_journal_get_write_access(handle, inode_bh);
156 -               err = ext4_handle_dirty_metadata(handle, inode, inode_bh);
158 +               ret = ext4_mark_inode_dirty(handle, inode);
159         } else {
160                 ret = ext4_walk_page_buffers(handle, page_bufs, 0, len, NULL,
161                                              do_journal_get_write_access);