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
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
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.
20 Signed-off-by: Jan Kara <jack@suse.cz>
21 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
23 fs/ext4/xattr.c | 58 ++++++++++++++++++++++++---------------------------------
24 1 file changed, 24 insertions(+), 34 deletions(-)
26 diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
27 index 2eb935c..22d2ebc 100644
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;
36 + size_t ifree, bfree;
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,
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;
55 + ifree = ext4_xattr_free_space(last, &min_offs, base, &total_ino);
56 + if (ifree >= isize_diff)
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,
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,
76 - free = inode->i_sb->s_blocksize;
77 + bfree = inode->i_sb->s_blocksize;
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,
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;
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,
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) {
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,
116 total_ino -= entry_size;
118 - entry = IFIRST(header);
119 - if (entry_size + EXT4_XATTR_SIZE(size) >= isize_diff)
120 - shift_bytes = isize_diff;
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);
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;
135 i.name = b_entry_name;
137 @@ -1553,6 +1534,15 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
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;
152 ext4_clear_inode_state(inode, EXT4_STATE_NO_EXPAND);
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()
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.
166 Signed-off-by: Jan Kara <jack@suse.cz>
167 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
169 fs/ext4/xattr.c | 9 +++++----
170 1 file changed, 5 insertions(+), 4 deletions(-)
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,
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)
183 struct ext4_xattr_entry *last = entry;
186 + /* We always shift xattr headers further thus offsets get lower */
187 + BUG_ON(value_offs_shift > 0);
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) +
194 - BUG_ON(new_offs + le32_to_cpu(last->e_value_size)
196 last->e_value_offs = cpu_to_le16(new_offs);
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;
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
216 ext4_expand_extra_isize() should clear only space between old and new
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>
224 fs/ext4/inode.c | 5 +++--
225 1 file changed, 3 insertions(+), 2 deletions(-)
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,
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;