1 ext4: fix xattr shifting when expanding inodes
3 From: Jan Kara <jack@suse.cz>
5 The code in ext4_expand_extra_isize_ea() treated new_extra_isize
6 argument sometimes as the desired target i_extra_isize and sometimes as
7 the amount by which we need to grow current i_extra_isize. These happen
8 to coincide when i_extra_isize is 0 which used to be the common case and
9 so nobody noticed this until recently when we added i_projid to the
10 inode and so i_extra_isize now needs to grow from 28 to 32 bytes.
12 The result of these bugs was that we sometimes unnecessarily decided to
13 move xattrs out of inode even if there was enough space and we often
14 ended up corrupting in-inode xattrs because arguments to
15 ext4_xattr_shift_entries() were just wrong. This could demonstrate
16 itself as BUG_ON in ext4_xattr_shift_entries() triggering.
18 Fix the problem by introducing new isize_diff variable and use it where
21 CC: stable@vger.kernel.org # 4.4.x
22 Reported-by: Dave Chinner <david@fromorbit.com>
23 Signed-off-by: Jan Kara <jack@suse.cz>
24 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
26 fs/ext4/xattr.c | 27 ++++++++++++++-------------
27 1 file changed, 14 insertions(+), 13 deletions(-)
29 diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
30 index 39e9cfb1b371..cb1d7b4482de 100644
33 @@ -1353,11 +1353,13 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
34 size_t min_offs, free;
36 void *base, *start, *end;
37 - int extra_isize = 0, error = 0, tried_min_extra_isize = 0;
38 + int error = 0, tried_min_extra_isize = 0;
39 int s_min_extra_isize = le16_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_min_extra_isize);
40 + int isize_diff; /* How much do we need to grow i_extra_isize */
42 down_write(&EXT4_I(inode)->xattr_sem);
44 + isize_diff = new_extra_isize - EXT4_I(inode)->i_extra_isize;
45 if (EXT4_I(inode)->i_extra_isize >= new_extra_isize) {
46 up_write(&EXT4_I(inode)->xattr_sem);
48 @@ -1382,7 +1384,7 @@ retry:
51 free = ext4_xattr_free_space(last, &min_offs, base, &total_ino);
52 - if (free >= new_extra_isize) {
53 + if (free >= isize_diff) {
54 entry = IFIRST(header);
55 ext4_xattr_shift_entries(entry, EXT4_I(inode)->i_extra_isize
56 - new_extra_isize, (void *)raw_inode +
57 @@ -1414,7 +1416,7 @@ retry:
58 end = bh->b_data + bh->b_size;
59 min_offs = end - base;
60 free = ext4_xattr_free_space(first, &min_offs, base, NULL);
61 - if (free < new_extra_isize) {
62 + if (free < isize_diff) {
63 if (!tried_min_extra_isize && s_min_extra_isize) {
64 tried_min_extra_isize++;
65 new_extra_isize = s_min_extra_isize;
66 @@ -1428,7 +1430,7 @@ retry:
67 free = inode->i_sb->s_blocksize;
70 - while (new_extra_isize > 0) {
71 + while (isize_diff > 0) {
72 size_t offs, size, entry_size;
73 struct ext4_xattr_entry *small_entry = NULL;
74 struct ext4_xattr_info i = {
75 @@ -1459,7 +1461,7 @@ retry:
76 EXT4_XATTR_SIZE(le32_to_cpu(last->e_value_size)) +
77 EXT4_XATTR_LEN(last->e_name_len);
78 if (total_size <= free && total_size < min_total_size) {
79 - if (total_size < new_extra_isize) {
80 + if (total_size < isize_diff) {
84 @@ -1516,20 +1518,19 @@ retry:
87 entry = IFIRST(header);
88 - if (entry_size + EXT4_XATTR_SIZE(size) >= new_extra_isize)
89 - shift_bytes = new_extra_isize;
90 + if (entry_size + EXT4_XATTR_SIZE(size) >= isize_diff)
91 + shift_bytes = isize_diff;
93 shift_bytes = entry_size + size;
94 /* Adjust the offsets and shift the remaining entries ahead */
95 - ext4_xattr_shift_entries(entry, EXT4_I(inode)->i_extra_isize -
96 - shift_bytes, (void *)raw_inode +
97 - EXT4_GOOD_OLD_INODE_SIZE + extra_isize + shift_bytes,
98 + ext4_xattr_shift_entries(entry, -shift_bytes,
99 + (void *)raw_inode + EXT4_GOOD_OLD_INODE_SIZE +
100 + EXT4_I(inode)->i_extra_isize + shift_bytes,
101 (void *)header, total_ino - entry_size,
102 inode->i_sb->s_blocksize);
104 - extra_isize += shift_bytes;
105 - new_extra_isize -= shift_bytes;
106 - EXT4_I(inode)->i_extra_isize = extra_isize;
107 + isize_diff -= shift_bytes;
108 + EXT4_I(inode)->i_extra_isize += shift_bytes;
110 i.name = b_entry_name;
116 To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
117 the body of a message to majordomo@vger.kernel.org
118 More majordomo info at http://vger.kernel.org/majordomo-info.html