Whamcloud - gitweb
LU-9384 ldiskfs: extra patch for changing extra isize
[fs/lustre-release.git] / ldiskfs / kernel_patches / patches / rhel7 / ext4-fix-xattr-shifting-when-expanding-inodes.patch
1 From d0141191a20289f8955c1e03dad08e42e6f71ca9 Mon Sep 17 00:00:00 2001
2 From: Jan Kara <jack@suse.cz>
3 Date: Thu, 11 Aug 2016 11:50:30 -0400
4 Subject: [PATCH] ext4: fix xattr shifting when expanding inodes
5
6 The code in ext4_expand_extra_isize_ea() treated new_extra_isize
7 argument sometimes as the desired target i_extra_isize and sometimes as
8 the amount by which we need to grow current i_extra_isize. These happen
9 to coincide when i_extra_isize is 0 which used to be the common case and
10 so nobody noticed this until recently when we added i_projid to the
11 inode and so i_extra_isize now needs to grow from 28 to 32 bytes.
12
13 The result of these bugs was that we sometimes unnecessarily decided to
14 move xattrs out of inode even if there was enough space and we often
15 ended up corrupting in-inode xattrs because arguments to
16 ext4_xattr_shift_entries() were just wrong. This could demonstrate
17 itself as BUG_ON in ext4_xattr_shift_entries() triggering.
18
19 Fix the problem by introducing new isize_diff variable and use it where
20 appropriate.
21
22 CC: stable@vger.kernel.org   # 4.4.x
23 Reported-by: Dave Chinner <david@fromorbit.com>
24 Signed-off-by: Jan Kara <jack@suse.cz>
25 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
26 ---
27  fs/ext4/xattr.c | 27 ++++++++++++++-------------
28  1 file changed, 14 insertions(+), 13 deletions(-)
29
30 diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
31 index 39e9cfb..cb1d7b4 100644
32 --- a/fs/ext4/xattr.c
33 +++ b/fs/ext4/xattr.c
34 @@ -1353,15 +1353,17 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
35         size_t min_offs, free;
36         int total_ino;
37         void *base, *start, *end;
38 -       int extra_isize = 0, error = 0, tried_min_extra_isize = 0;
39 +       int error = 0, tried_min_extra_isize = 0;
40         int s_min_extra_isize = le16_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_min_extra_isize);
41 +       int isize_diff; /* How much do we need to grow i_extra_isize */
42  
43         down_write(&EXT4_I(inode)->xattr_sem);
44         /*
45          * Set EXT4_STATE_NO_EXPAND to avoid recursion when marking inode dirty
46          */
47         ext4_set_inode_state(inode, EXT4_STATE_NO_EXPAND);
48  retry:
49 +       isize_diff = new_extra_isize - EXT4_I(inode)->i_extra_isize;
50         if (EXT4_I(inode)->i_extra_isize >= new_extra_isize)
51                 goto out;
52
53 @@ -1382,7 +1384,7 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
54                 goto cleanup;
55  
56         free = ext4_xattr_free_space(last, &min_offs, base, &total_ino);
57 -       if (free >= new_extra_isize) {
58 +       if (free >= isize_diff) {
59                 entry = IFIRST(header);
60                 ext4_xattr_shift_entries(entry, EXT4_I(inode)->i_extra_isize
61                                 - new_extra_isize, (void *)raw_inode +
62 @@ -1414,7 +1416,7 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
63                 end = bh->b_data + bh->b_size;
64                 min_offs = end - base;
65                 free = ext4_xattr_free_space(first, &min_offs, base, NULL);
66 -               if (free < new_extra_isize) {
67 +               if (free < isize_diff) {
68                         if (!tried_min_extra_isize && s_min_extra_isize) {
69                                 tried_min_extra_isize++;
70                                 new_extra_isize = s_min_extra_isize;
71 @@ -1428,7 +1430,7 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
72                 free = inode->i_sb->s_blocksize;
73         }
74  
75 -       while (new_extra_isize > 0) {
76 +       while (isize_diff > 0) {
77                 size_t offs, size, entry_size;
78                 struct ext4_xattr_entry *small_entry = NULL;
79                 struct ext4_xattr_info i = {
80 @@ -1459,7 +1461,7 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
81                         EXT4_XATTR_SIZE(le32_to_cpu(last->e_value_size)) +
82                                         EXT4_XATTR_LEN(last->e_name_len);
83                         if (total_size <= free && total_size < min_total_size) {
84 -                               if (total_size < new_extra_isize) {
85 +                               if (total_size < isize_diff) {
86                                         small_entry = last;
87                                 } else {
88                                         entry = last;
89 @@ -1516,20 +1518,19 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
90                         goto cleanup;
91  
92                 entry = IFIRST(header);
93 -               if (entry_size + EXT4_XATTR_SIZE(size) >= new_extra_isize)
94 -                       shift_bytes = new_extra_isize;
95 +               if (entry_size + EXT4_XATTR_SIZE(size) >= isize_diff)
96 +                       shift_bytes = isize_diff;
97                 else
98                         shift_bytes = entry_size + size;
99                 /* Adjust the offsets and shift the remaining entries ahead */
100 -               ext4_xattr_shift_entries(entry, EXT4_I(inode)->i_extra_isize -
101 -                       shift_bytes, (void *)raw_inode +
102 -                       EXT4_GOOD_OLD_INODE_SIZE + extra_isize + shift_bytes,
103 +               ext4_xattr_shift_entries(entry, -shift_bytes,
104 +                       (void *)raw_inode + EXT4_GOOD_OLD_INODE_SIZE +
105 +                       EXT4_I(inode)->i_extra_isize + shift_bytes,
106                         (void *)header, total_ino - entry_size,
107                         inode->i_sb->s_blocksize);
108  
109 -               extra_isize += shift_bytes;
110 -               new_extra_isize -= shift_bytes;
111 -               EXT4_I(inode)->i_extra_isize = extra_isize;
112 +               isize_diff -= shift_bytes;
113 +               EXT4_I(inode)->i_extra_isize += shift_bytes;
114  
115                 i.name = b_entry_name;
116                 i.value = buffer;
117 -- 
118 2.9.3
119
120 From 418c12d08dc64a45107c467ec1ba29b5e69b0715 Mon Sep 17 00:00:00 2001
121 From: Jan Kara <jack@suse.cz>
122 Date: Thu, 11 Aug 2016 11:58:32 -0400
123 Subject: [PATCH] ext4: fix xattr shifting when expanding inodes part 2
124
125 When multiple xattrs need to be moved out of inode, we did not properly
126 recompute total size of xattr headers in the inode and the new header
127 position. Thus when moving the second and further xattr we asked
128 ext4_xattr_shift_entries() to move too much and from the wrong place,
129 resulting in possible xattr value corruption or general memory
130 corruption.
131
132 CC: stable@vger.kernel.org  # 4.4.x
133 Signed-off-by: Jan Kara <jack@suse.cz>
134 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
135 ---
136  fs/ext4/xattr.c | 5 +++--
137  1 file changed, 3 insertions(+), 2 deletions(-)
138
139 diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
140 index cb1d7b4..b18b1ff 100644
141 --- a/fs/ext4/xattr.c
142 +++ b/fs/ext4/xattr.c
143 @@ -1516,6 +1516,7 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
144                 error = ext4_xattr_ibody_set(handle, inode, &i, is);
145                 if (error)
146                         goto cleanup;
147 +               total_ino -= entry_size;
148  
149                 entry = IFIRST(header);
150                 if (entry_size + EXT4_XATTR_SIZE(size) >= isize_diff)
151 @@ -1526,11 +1527,11 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
152                 ext4_xattr_shift_entries(entry, -shift_bytes,
153                         (void *)raw_inode + EXT4_GOOD_OLD_INODE_SIZE +
154                         EXT4_I(inode)->i_extra_isize + shift_bytes,
155 -                       (void *)header, total_ino - entry_size,
156 -                       inode->i_sb->s_blocksize);
157 +                       (void *)header, total_ino, inode->i_sb->s_blocksize);
158  
159                 isize_diff -= shift_bytes;
160                 EXT4_I(inode)->i_extra_isize += shift_bytes;
161 +               header = IHDR(inode, raw_inode);
162  
163                 i.name = b_entry_name;
164                 i.value = buffer;
165 -- 
166 2.9.3
167
168 From 443a8c41cd49de66a3fda45b32b9860ea0292b84 Mon Sep 17 00:00:00 2001
169 From: Jan Kara <jack@suse.cz>
170 Date: Thu, 11 Aug 2016 12:00:01 -0400
171 Subject: [PATCH] ext4: properly align shifted xattrs when expanding inodes
172
173 We did not count with the padding of xattr value when computing desired
174 shift of xattrs in the inode when expanding i_extra_isize. As a result
175 we could create unaligned start of inline xattrs. Account for alignment
176 properly.
177
178 CC: stable@vger.kernel.org  # 4.4.x-
179 Signed-off-by: Jan Kara <jack@suse.cz>
180 ---
181  fs/ext4/xattr.c | 2 +-
182  1 file changed, 1 insertion(+), 1 deletion(-)
183
184 diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
185 index b18b1ff..c893f00 100644
186 --- a/fs/ext4/xattr.c
187 +++ b/fs/ext4/xattr.c
188 @@ -1522,7 +1522,7 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
189                 if (entry_size + EXT4_XATTR_SIZE(size) >= isize_diff)
190                         shift_bytes = isize_diff;
191                 else
192 -                       shift_bytes = entry_size + size;
193 +                       shift_bytes = entry_size + EXT4_XATTR_SIZE(size);
194                 /* Adjust the offsets and shift the remaining entries ahead */
195                 ext4_xattr_shift_entries(entry, -shift_bytes,
196                         (void *)raw_inode + EXT4_GOOD_OLD_INODE_SIZE +
197 -- 
198 2.9.3
199
200 From e3014d14a81edde488d9a6758eea8afc41752d2d Mon Sep 17 00:00:00 2001
201 From: Jan Kara <jack@suse.cz>
202 Date: Mon, 29 Aug 2016 15:38:11 -0400
203 Subject: [PATCH] ext4: fixup free space calculations when expanding inodes
204
205 Conditions checking whether there is enough free space in an xattr block
206 and when xattr is large enough to make enough space in the inode forgot
207 to account for the fact that inode need not be completely filled up with
208 xattrs. Thus we could move unnecessarily many xattrs out of inode or
209 even falsely claim there is not enough space to expand the inode. We
210 also forgot to update the amount of free space in xattr block when moving
211 more xattrs and thus could decide to move too big xattr resulting in
212 unexpected failure.
213
214 Fix these problems by properly updating free space in the inode and
215 xattr block as we move xattrs. To simplify the math, avoid shifting
216 xattrs after removing each one xattr and instead just shift xattrs only
217 once there is enough free space in the inode.
218
219 Signed-off-by: Jan Kara <jack@suse.cz>
220 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
221 ---
222  fs/ext4/xattr.c | 58 ++++++++++++++++++++++++---------------------------------
223  1 file changed, 24 insertions(+), 34 deletions(-)
224
225 diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
226 index 2eb935c..22d2ebc 100644
227 --- a/fs/ext4/xattr.c
228 +++ b/fs/ext4/xattr.c
229 @@ -1350,7 +1350,8 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
230         struct ext4_xattr_ibody_find *is = NULL;
231         struct ext4_xattr_block_find *bs = NULL;
232         char *buffer = NULL, *b_entry_name = NULL;
233 -       size_t min_offs, free;
234 +       size_t min_offs;
235 +       size_t ifree, bfree;
236         int total_ino;
237         void *base, *start, *end;
238         int error = 0, tried_min_extra_isize = 0;
239 @@ -1385,17 +1386,9 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
240         if (error)
241                 goto cleanup;
242  
243 -       free = ext4_xattr_free_space(last, &min_offs, base, &total_ino);
244 -       if (free >= isize_diff) {
245 -               entry = IFIRST(header);
246 -               ext4_xattr_shift_entries(entry, EXT4_I(inode)->i_extra_isize
247 -                               - new_extra_isize, (void *)raw_inode +
248 -                               EXT4_GOOD_OLD_INODE_SIZE + new_extra_isize,
249 -                               (void *)header, total_ino,
250 -                               inode->i_sb->s_blocksize);
251 -               EXT4_I(inode)->i_extra_isize = new_extra_isize;
252 -               goto out;
253 -       }
254 +       ifree = ext4_xattr_free_space(last, &min_offs, base, &total_ino);
255 +       if (ifree >= isize_diff)
256 +               goto shift;
257  
258         /*
259          * Enough free space isn't available in the inode, check if
260 @@ -1416,8 +1409,8 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
261                 first = BFIRST(bh);
262                 end = bh->b_data + bh->b_size;
263                 min_offs = end - base;
264 -               free = ext4_xattr_free_space(first, &min_offs, base, NULL);
265 -               if (free < isize_diff) {
266 +               bfree = ext4_xattr_free_space(first, &min_offs, base, NULL);
267 +               if (bfree + ifree < isize_diff) {
268                         if (!tried_min_extra_isize && s_min_extra_isize) {
269                                 tried_min_extra_isize++;
270                                 new_extra_isize = s_min_extra_isize;
271 @@ -1428,10 +1421,10 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
272                         goto cleanup;
273                 }
274         } else {
275 -               free = inode->i_sb->s_blocksize;
276 +               bfree = inode->i_sb->s_blocksize;
277         }
278  
279 -       while (isize_diff > 0) {
280 +       while (isize_diff > ifree) {
281                 size_t offs, size, entry_size;
282                 struct ext4_xattr_entry *small_entry = NULL;
283                 struct ext4_xattr_info i = {
284 @@ -1439,7 +1432,6 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
285                         .value_len = 0,
286                 };
287                 unsigned int total_size;  /* EA entry size + value size */
288 -               unsigned int shift_bytes; /* No. of bytes to shift EAs by? */
289                 unsigned int min_total_size = ~0U;
290  
291                 is = kzalloc(sizeof(struct ext4_xattr_ibody_find), GFP_NOFS);
292 @@ -1461,8 +1453,9 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
293                         total_size =
294                         EXT4_XATTR_SIZE(le32_to_cpu(last->e_value_size)) +
295                                         EXT4_XATTR_LEN(last->e_name_len);
296 -                       if (total_size <= free && total_size < min_total_size) {
297 -                               if (total_size < isize_diff) {
298 +                       if (total_size <= bfree &&
299 +                           total_size < min_total_size) {
300 +                               if (total_size + ifree < isize_diff) {
301                                         small_entry = last;
302                                 } else {
303                                         entry = last;
304 @@ -1491,6 +1484,7 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
305                 offs = le16_to_cpu(entry->e_value_offs);
306                 size = le32_to_cpu(entry->e_value_size);
307                 entry_size = EXT4_XATTR_LEN(entry->e_name_len);
308 +               total_size = entry_size + EXT4_XATTR_SIZE(size);
309                 i.name_index = entry->e_name_index,
310                 buffer = kmalloc(EXT4_XATTR_SIZE(size), GFP_NOFS);
311                 b_entry_name = kmalloc(entry->e_name_len + 1, GFP_NOFS);
312 @@ -1518,21 +1512,8 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
313                 if (error)
314                         goto cleanup;
315                 total_ino -= entry_size;
316 -
317 -               entry = IFIRST(header);
318 -               if (entry_size + EXT4_XATTR_SIZE(size) >= isize_diff)
319 -                       shift_bytes = isize_diff;
320 -               else
321 -                       shift_bytes = entry_size + EXT4_XATTR_SIZE(size);
322 -               /* Adjust the offsets and shift the remaining entries ahead */
323 -               ext4_xattr_shift_entries(entry, -shift_bytes,
324 -                       (void *)raw_inode + EXT4_GOOD_OLD_INODE_SIZE +
325 -                       EXT4_I(inode)->i_extra_isize + shift_bytes,
326 -                       (void *)header, total_ino, inode->i_sb->s_blocksize);
327 -
328 -               isize_diff -= shift_bytes;
329 -               EXT4_I(inode)->i_extra_isize += shift_bytes;
330 -               header = IHDR(inode, raw_inode);
331 +               ifree += total_size;
332 +               bfree -= total_size;
333  
334                 i.name = b_entry_name;
335                 i.value = buffer;
336 @@ -1553,6 +1534,15 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
337                 kfree(is);
338                 kfree(bs);
339         }
340 +
341 +shift:
342 +       /* Adjust the offsets and shift the remaining entries ahead */
343 +       entry = IFIRST(header);
344 +       ext4_xattr_shift_entries(entry, EXT4_I(inode)->i_extra_isize
345 +                       - new_extra_isize, (void *)raw_inode +
346 +                       EXT4_GOOD_OLD_INODE_SIZE + new_extra_isize,
347 +                       (void *)header, total_ino, inode->i_sb->s_blocksize);
348 +       EXT4_I(inode)->i_extra_isize = new_extra_isize;
349         brelse(bh);
350  out:
351         ext4_clear_inode_state(inode, EXT4_STATE_NO_EXPAND);
352 -- 
353 2.9.3
354
355 From 94405713889d4a9d341b4ad92956e4e2ec8ec2c2 Mon Sep 17 00:00:00 2001
356 From: Jan Kara <jack@suse.cz>
357 Date: Mon, 29 Aug 2016 15:41:11 -0400
358 Subject: [PATCH] ext4: replace bogus assertion in ext4_xattr_shift_entries()
359
360 We were checking whether computed offsets do not exceed end of block in
361 ext4_xattr_shift_entries(). However this does not make sense since we
362 always only decrease offsets. So replace that assertion with a check
363 whether we really decrease xattrs value offsets.
364
365 Signed-off-by: Jan Kara <jack@suse.cz>
366 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
367 ---
368  fs/ext4/xattr.c | 9 +++++----
369  1 file changed, 5 insertions(+), 4 deletions(-)
370
371 diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
372 index 1447860..82b025c 100644
373 --- a/fs/ext4/xattr.c
374 +++ b/fs/ext4/xattr.c
375 @@ -1319,18 +1319,19 @@ ext4_xattr_set(struct inode *inode, int name_index, const char *name,
376   */
377  static void ext4_xattr_shift_entries(struct ext4_xattr_entry *entry,
378                                      int value_offs_shift, void *to,
379 -                                    void *from, size_t n, int blocksize)
380 +                                    void *from, size_t n)
381  {
382         struct ext4_xattr_entry *last = entry;
383         int new_offs;
384  
385 +       /* We always shift xattr headers further thus offsets get lower */
386 +       BUG_ON(value_offs_shift > 0);
387 +
388         /* Adjust the value offsets of the entries */
389         for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) {
390                 if (!last->e_value_inum && last->e_value_size) {
391                         new_offs = le16_to_cpu(last->e_value_offs) +
392                                                         value_offs_shift;
393 -                       BUG_ON(new_offs + le32_to_cpu(last->e_value_size)
394 -                                > blocksize);
395                         last->e_value_offs = cpu_to_le16(new_offs);
396                 }
397         }
398 @@ -1542,7 +1543,7 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
399         ext4_xattr_shift_entries(entry, EXT4_I(inode)->i_extra_isize
400                         - new_extra_isize, (void *)raw_inode +
401                         EXT4_GOOD_OLD_INODE_SIZE + new_extra_isize,
402 -                       (void *)header, total_ino, inode->i_sb->s_blocksize);
403 +                       (void *)header, total_ino);
404         EXT4_I(inode)->i_extra_isize = new_extra_isize;
405         brelse(bh);
406  out:
407 -- 
408 2.9.3
409
410 From 887a9730614727c4fff7cb756711b190593fc1df Mon Sep 17 00:00:00 2001
411 From: Konstantin Khlebnikov <khlebnikov@yandex-team.ru>
412 Date: Sun, 21 May 2017 22:36:23 -0400
413 Subject: [PATCH] ext4: keep existing extra fields when inode expands
414
415 ext4_expand_extra_isize() should clear only space between old and new
416 size.
417
418 Fixes: 6dd4ee7cab7e # v2.6.23
419 Cc: stable@vger.kernel.org
420 Signed-off-by: Konstantin Khlebnikov <khlebnikov@yandex-team.ru>
421 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
422 ---
423  fs/ext4/inode.c | 5 +++--
424  1 file changed, 3 insertions(+), 2 deletions(-)
425
426 diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
427 index 1bd0bfa..7cd99de 100644
428 --- a/fs/ext4/inode.c
429 +++ b/fs/ext4/inode.c
430 @@ -5637,8 +5637,9 @@ static int ext4_expand_extra_isize(struct inode *inode,
431         /* No extended attributes present */
432         if (!ext4_test_inode_state(inode, EXT4_STATE_XATTR) ||
433             header->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC)) {
434 -               memset((void *)raw_inode + EXT4_GOOD_OLD_INODE_SIZE, 0,
435 -                       new_extra_isize);
436 +               memset((void *)raw_inode + EXT4_GOOD_OLD_INODE_SIZE +
437 +                      EXT4_I(inode)->i_extra_isize, 0,
438 +                      new_extra_isize - EXT4_I(inode)->i_extra_isize);
439                 EXT4_I(inode)->i_extra_isize = new_extra_isize;
440                 return 0;
441         }
442 -- 
443 2.9.3
444