Whamcloud - gitweb
LU-9816 kernel: kernel upgrade RHEL7.4 [3.10.0-693.el7]
[fs/lustre-release.git] / ldiskfs / kernel_patches / patches / rhel7.4 / ext4-fix-xattr-shifting-when-expanding-inodes.patch
1 From e3014d14a81edde488d9a6758eea8afc41752d2d Mon Sep 17 00:00:00 2001
2 From: Jan Kara <jack@suse.cz>
3 Date: Mon, 29 Aug 2016 15:38:11 -0400
4 Subject: [PATCH] ext4: fixup free space calculations when expanding inodes
5
6 Conditions checking whether there is enough free space in an xattr block
7 and when xattr is large enough to make enough space in the inode forgot
8 to account for the fact that inode need not be completely filled up with
9 xattrs. Thus we could move unnecessarily many xattrs out of inode or
10 even falsely claim there is not enough space to expand the inode. We
11 also forgot to update the amount of free space in xattr block when moving
12 more xattrs and thus could decide to move too big xattr resulting in
13 unexpected failure.
14
15 Fix these problems by properly updating free space in the inode and
16 xattr block as we move xattrs. To simplify the math, avoid shifting
17 xattrs after removing each one xattr and instead just shift xattrs only
18 once there is enough free space in the inode.
19
20 Signed-off-by: Jan Kara <jack@suse.cz>
21 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
22 ---
23  fs/ext4/xattr.c | 58 ++++++++++++++++++++++++---------------------------------
24  1 file changed, 24 insertions(+), 34 deletions(-)
25
26 diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
27 index 2eb935c..22d2ebc 100644
28 --- a/fs/ext4/xattr.c
29 +++ b/fs/ext4/xattr.c
30 @@ -1350,7 +1350,8 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
31         struct ext4_xattr_ibody_find *is = NULL;
32         struct ext4_xattr_block_find *bs = NULL;
33         char *buffer = NULL, *b_entry_name = NULL;
34 -       size_t min_offs, free;
35 +       size_t min_offs;
36 +       size_t ifree, bfree;
37         int total_ino;
38         void *base, *start, *end;
39         int error = 0, tried_min_extra_isize = 0;
40 @@ -1385,17 +1386,9 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
41         if (error)
42                 goto cleanup;
43  
44 -       free = ext4_xattr_free_space(last, &min_offs, base, &total_ino);
45 -       if (free >= isize_diff) {
46 -               entry = IFIRST(header);
47 -               ext4_xattr_shift_entries(entry, EXT4_I(inode)->i_extra_isize
48 -                               - new_extra_isize, (void *)raw_inode +
49 -                               EXT4_GOOD_OLD_INODE_SIZE + new_extra_isize,
50 -                               (void *)header, total_ino,
51 -                               inode->i_sb->s_blocksize);
52 -               EXT4_I(inode)->i_extra_isize = new_extra_isize;
53 -               goto out;
54 -       }
55 +       ifree = ext4_xattr_free_space(last, &min_offs, base, &total_ino);
56 +       if (ifree >= isize_diff)
57 +               goto shift;
58  
59         /*
60          * Enough free space isn't available in the inode, check if
61 @@ -1416,8 +1409,8 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
62                 first = BFIRST(bh);
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 < isize_diff) {
67 +               bfree = ext4_xattr_free_space(first, &min_offs, base, NULL);
68 +               if (bfree + ifree < isize_diff) {
69                         if (!tried_min_extra_isize && s_min_extra_isize) {
70                                 tried_min_extra_isize++;
71                                 new_extra_isize = s_min_extra_isize;
72 @@ -1428,10 +1421,10 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
73                         goto cleanup;
74                 }
75         } else {
76 -               free = inode->i_sb->s_blocksize;
77 +               bfree = inode->i_sb->s_blocksize;
78         }
79  
80 -       while (isize_diff > 0) {
81 +       while (isize_diff > ifree) {
82                 size_t offs, size, entry_size;
83                 struct ext4_xattr_entry *small_entry = NULL;
84                 struct ext4_xattr_info i = {
85 @@ -1439,7 +1432,6 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
86                         .value_len = 0,
87                 };
88                 unsigned int total_size;  /* EA entry size + value size */
89 -               unsigned int shift_bytes; /* No. of bytes to shift EAs by? */
90                 unsigned int min_total_size = ~0U;
91  
92                 is = kzalloc(sizeof(struct ext4_xattr_ibody_find), GFP_NOFS);
93 @@ -1461,8 +1453,9 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
94                         total_size =
95                         EXT4_XATTR_SIZE(le32_to_cpu(last->e_value_size)) +
96                                         EXT4_XATTR_LEN(last->e_name_len);
97 -                       if (total_size <= free && total_size < min_total_size) {
98 -                               if (total_size < isize_diff) {
99 +                       if (total_size <= bfree &&
100 +                           total_size < min_total_size) {
101 +                               if (total_size + ifree < isize_diff) {
102                                         small_entry = last;
103                                 } else {
104                                         entry = last;
105 @@ -1491,6 +1484,7 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
106                 offs = le16_to_cpu(entry->e_value_offs);
107                 size = le32_to_cpu(entry->e_value_size);
108                 entry_size = EXT4_XATTR_LEN(entry->e_name_len);
109 +               total_size = entry_size + EXT4_XATTR_SIZE(size);
110                 i.name_index = entry->e_name_index,
111                 buffer = kmalloc(EXT4_XATTR_SIZE(size), GFP_NOFS);
112                 b_entry_name = kmalloc(entry->e_name_len + 1, GFP_NOFS);
113 @@ -1518,21 +1512,8 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
114                 if (error)
115                         goto cleanup;
116                 total_ino -= entry_size;
117 -
118 -               entry = IFIRST(header);
119 -               if (entry_size + EXT4_XATTR_SIZE(size) >= isize_diff)
120 -                       shift_bytes = isize_diff;
121 -               else
122 -                       shift_bytes = entry_size + EXT4_XATTR_SIZE(size);
123 -               /* Adjust the offsets and shift the remaining entries ahead */
124 -               ext4_xattr_shift_entries(entry, -shift_bytes,
125 -                       (void *)raw_inode + EXT4_GOOD_OLD_INODE_SIZE +
126 -                       EXT4_I(inode)->i_extra_isize + shift_bytes,
127 -                       (void *)header, total_ino, inode->i_sb->s_blocksize);
128 -
129 -               isize_diff -= shift_bytes;
130 -               EXT4_I(inode)->i_extra_isize += shift_bytes;
131 -               header = IHDR(inode, raw_inode);
132 +               ifree += total_size;
133 +               bfree -= total_size;
134  
135                 i.name = b_entry_name;
136                 i.value = buffer;
137 @@ -1553,6 +1534,15 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
138                 kfree(is);
139                 kfree(bs);
140         }
141 +
142 +shift:
143 +       /* Adjust the offsets and shift the remaining entries ahead */
144 +       entry = IFIRST(header);
145 +       ext4_xattr_shift_entries(entry, EXT4_I(inode)->i_extra_isize
146 +                       - new_extra_isize, (void *)raw_inode +
147 +                       EXT4_GOOD_OLD_INODE_SIZE + new_extra_isize,
148 +                       (void *)header, total_ino, inode->i_sb->s_blocksize);
149 +       EXT4_I(inode)->i_extra_isize = new_extra_isize;
150         brelse(bh);
151  out:
152         ext4_clear_inode_state(inode, EXT4_STATE_NO_EXPAND);
153 -- 
154 2.9.3
155
156 From 94405713889d4a9d341b4ad92956e4e2ec8ec2c2 Mon Sep 17 00:00:00 2001
157 From: Jan Kara <jack@suse.cz>
158 Date: Mon, 29 Aug 2016 15:41:11 -0400
159 Subject: [PATCH] ext4: replace bogus assertion in ext4_xattr_shift_entries()
160
161 We were checking whether computed offsets do not exceed end of block in
162 ext4_xattr_shift_entries(). However this does not make sense since we
163 always only decrease offsets. So replace that assertion with a check
164 whether we really decrease xattrs value offsets.
165
166 Signed-off-by: Jan Kara <jack@suse.cz>
167 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
168 ---
169  fs/ext4/xattr.c | 9 +++++----
170  1 file changed, 5 insertions(+), 4 deletions(-)
171
172 diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
173 index 1447860..82b025c 100644
174 --- a/fs/ext4/xattr.c
175 +++ b/fs/ext4/xattr.c
176 @@ -1319,18 +1319,19 @@ ext4_xattr_set(struct inode *inode, int name_index, const char *name,
177   */
178  static void ext4_xattr_shift_entries(struct ext4_xattr_entry *entry,
179                                      int value_offs_shift, void *to,
180 -                                    void *from, size_t n, int blocksize)
181 +                                    void *from, size_t n)
182  {
183         struct ext4_xattr_entry *last = entry;
184         int new_offs;
185  
186 +       /* We always shift xattr headers further thus offsets get lower */
187 +       BUG_ON(value_offs_shift > 0);
188 +
189         /* Adjust the value offsets of the entries */
190         for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) {
191                 if (!last->e_value_inum && last->e_value_size) {
192                         new_offs = le16_to_cpu(last->e_value_offs) +
193                                                         value_offs_shift;
194 -                       BUG_ON(new_offs + le32_to_cpu(last->e_value_size)
195 -                                > blocksize);
196                         last->e_value_offs = cpu_to_le16(new_offs);
197                 }
198         }
199 @@ -1542,7 +1543,7 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
200         ext4_xattr_shift_entries(entry, EXT4_I(inode)->i_extra_isize
201                         - new_extra_isize, (void *)raw_inode +
202                         EXT4_GOOD_OLD_INODE_SIZE + new_extra_isize,
203 -                       (void *)header, total_ino, inode->i_sb->s_blocksize);
204 +                       (void *)header, total_ino);
205         EXT4_I(inode)->i_extra_isize = new_extra_isize;
206         brelse(bh);
207  out:
208 -- 
209 2.9.3
210
211 From 887a9730614727c4fff7cb756711b190593fc1df Mon Sep 17 00:00:00 2001
212 From: Konstantin Khlebnikov <khlebnikov@yandex-team.ru>
213 Date: Sun, 21 May 2017 22:36:23 -0400
214 Subject: [PATCH] ext4: keep existing extra fields when inode expands
215
216 ext4_expand_extra_isize() should clear only space between old and new
217 size.
218
219 Fixes: 6dd4ee7cab7e # v2.6.23
220 Cc: stable@vger.kernel.org
221 Signed-off-by: Konstantin Khlebnikov <khlebnikov@yandex-team.ru>
222 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
223 ---
224  fs/ext4/inode.c | 5 +++--
225  1 file changed, 3 insertions(+), 2 deletions(-)
226
227 diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
228 index 1bd0bfa..7cd99de 100644
229 --- a/fs/ext4/inode.c
230 +++ b/fs/ext4/inode.c
231 @@ -5637,8 +5637,9 @@ static int ext4_expand_extra_isize(struct inode *inode,
232         /* No extended attributes present */
233         if (!ext4_test_inode_state(inode, EXT4_STATE_XATTR) ||
234             header->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC)) {
235 -               memset((void *)raw_inode + EXT4_GOOD_OLD_INODE_SIZE, 0,
236 -                       new_extra_isize);
237 +               memset((void *)raw_inode + EXT4_GOOD_OLD_INODE_SIZE +
238 +                      EXT4_I(inode)->i_extra_isize, 0,
239 +                      new_extra_isize - EXT4_I(inode)->i_extra_isize);
240                 EXT4_I(inode)->i_extra_isize = new_extra_isize;
241                 return 0;
242         }
243 -- 
244 2.9.3
245