diff options
author | Tao Ma <boyu.mt@taobao.com> | 2012-12-10 14:04:46 -0500 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2012-12-10 14:04:46 -0500 |
commit | 67cf5b09a46f72e048501b84996f2f77bc42e947 (patch) | |
tree | a03218e2d47fc2ea9875fbd245e5b7ec3985e25e /fs/ext4 | |
parent | 879b38257bf2b6fa8406693a3b5b5a0649e7c594 (diff) |
ext4: add the basic function for inline data support
Implement inline data with xattr.
Now we use "system.data" to store xattr, and the xattr will
be extended if the i_size is increased while we don't release
the space during truncate.
Signed-off-by: Tao Ma <boyu.mt@taobao.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs/ext4')
-rw-r--r-- | fs/ext4/Makefile | 2 | ||||
-rw-r--r-- | fs/ext4/ext4.h | 10 | ||||
-rw-r--r-- | fs/ext4/inline.c | 466 | ||||
-rw-r--r-- | fs/ext4/inode.c | 5 | ||||
-rw-r--r-- | fs/ext4/xattr.h | 54 |
5 files changed, 534 insertions, 3 deletions
diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile index 41f22be2ffa4..3d96d5698538 100644 --- a/fs/ext4/Makefile +++ b/fs/ext4/Makefile | |||
@@ -9,6 +9,6 @@ ext4-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o page-io.o \ | |||
9 | ext4_jbd2.o migrate.o mballoc.o block_validity.o move_extent.o \ | 9 | ext4_jbd2.o migrate.o mballoc.o block_validity.o move_extent.o \ |
10 | mmp.o indirect.o extents_status.o | 10 | mmp.o indirect.o extents_status.o |
11 | 11 | ||
12 | ext4-$(CONFIG_EXT4_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o | 12 | ext4-$(CONFIG_EXT4_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o inline.o |
13 | ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o | 13 | ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o |
14 | ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o | 14 | ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o |
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 2e9ffa9100bb..c827e47d556c 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h | |||
@@ -402,6 +402,7 @@ struct flex_groups { | |||
402 | #define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */ | 402 | #define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */ |
403 | #define EXT4_EA_INODE_FL 0x00200000 /* Inode used for large EA */ | 403 | #define EXT4_EA_INODE_FL 0x00200000 /* Inode used for large EA */ |
404 | #define EXT4_EOFBLOCKS_FL 0x00400000 /* Blocks allocated beyond EOF */ | 404 | #define EXT4_EOFBLOCKS_FL 0x00400000 /* Blocks allocated beyond EOF */ |
405 | #define EXT4_INLINE_DATA_FL 0x10000000 /* Inode has inline data. */ | ||
405 | #define EXT4_RESERVED_FL 0x80000000 /* reserved for ext4 lib */ | 406 | #define EXT4_RESERVED_FL 0x80000000 /* reserved for ext4 lib */ |
406 | 407 | ||
407 | #define EXT4_FL_USER_VISIBLE 0x004BDFFF /* User visible flags */ | 408 | #define EXT4_FL_USER_VISIBLE 0x004BDFFF /* User visible flags */ |
@@ -458,6 +459,7 @@ enum { | |||
458 | EXT4_INODE_EXTENTS = 19, /* Inode uses extents */ | 459 | EXT4_INODE_EXTENTS = 19, /* Inode uses extents */ |
459 | EXT4_INODE_EA_INODE = 21, /* Inode used for large EA */ | 460 | EXT4_INODE_EA_INODE = 21, /* Inode used for large EA */ |
460 | EXT4_INODE_EOFBLOCKS = 22, /* Blocks allocated beyond EOF */ | 461 | EXT4_INODE_EOFBLOCKS = 22, /* Blocks allocated beyond EOF */ |
462 | EXT4_INODE_INLINE_DATA = 28, /* Data in inode. */ | ||
461 | EXT4_INODE_RESERVED = 31, /* reserved for ext4 lib */ | 463 | EXT4_INODE_RESERVED = 31, /* reserved for ext4 lib */ |
462 | }; | 464 | }; |
463 | 465 | ||
@@ -504,6 +506,7 @@ static inline void ext4_check_flag_values(void) | |||
504 | CHECK_FLAG_VALUE(EXTENTS); | 506 | CHECK_FLAG_VALUE(EXTENTS); |
505 | CHECK_FLAG_VALUE(EA_INODE); | 507 | CHECK_FLAG_VALUE(EA_INODE); |
506 | CHECK_FLAG_VALUE(EOFBLOCKS); | 508 | CHECK_FLAG_VALUE(EOFBLOCKS); |
509 | CHECK_FLAG_VALUE(INLINE_DATA); | ||
507 | CHECK_FLAG_VALUE(RESERVED); | 510 | CHECK_FLAG_VALUE(RESERVED); |
508 | } | 511 | } |
509 | 512 | ||
@@ -918,6 +921,10 @@ struct ext4_inode_info { | |||
918 | /* on-disk additional length */ | 921 | /* on-disk additional length */ |
919 | __u16 i_extra_isize; | 922 | __u16 i_extra_isize; |
920 | 923 | ||
924 | /* Indicate the inline data space. */ | ||
925 | u16 i_inline_off; | ||
926 | u16 i_inline_size; | ||
927 | |||
921 | #ifdef CONFIG_QUOTA | 928 | #ifdef CONFIG_QUOTA |
922 | /* quota space reservation, managed internally by quota code */ | 929 | /* quota space reservation, managed internally by quota code */ |
923 | qsize_t i_reserved_quota; | 930 | qsize_t i_reserved_quota; |
@@ -1376,6 +1383,7 @@ enum { | |||
1376 | EXT4_STATE_DELALLOC_RESERVED, /* blks already reserved for delalloc */ | 1383 | EXT4_STATE_DELALLOC_RESERVED, /* blks already reserved for delalloc */ |
1377 | EXT4_STATE_DIOREAD_LOCK, /* Disable support for dio read | 1384 | EXT4_STATE_DIOREAD_LOCK, /* Disable support for dio read |
1378 | nolocking */ | 1385 | nolocking */ |
1386 | EXT4_STATE_MAY_INLINE_DATA, /* may have in-inode data */ | ||
1379 | }; | 1387 | }; |
1380 | 1388 | ||
1381 | #define EXT4_INODE_BIT_FNS(name, field, offset) \ | 1389 | #define EXT4_INODE_BIT_FNS(name, field, offset) \ |
@@ -1497,7 +1505,7 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei) | |||
1497 | #define EXT4_FEATURE_INCOMPAT_DIRDATA 0x1000 /* data in dirent */ | 1505 | #define EXT4_FEATURE_INCOMPAT_DIRDATA 0x1000 /* data in dirent */ |
1498 | #define EXT4_FEATURE_INCOMPAT_BG_USE_META_CSUM 0x2000 /* use crc32c for bg */ | 1506 | #define EXT4_FEATURE_INCOMPAT_BG_USE_META_CSUM 0x2000 /* use crc32c for bg */ |
1499 | #define EXT4_FEATURE_INCOMPAT_LARGEDIR 0x4000 /* >2GB or 3-lvl htree */ | 1507 | #define EXT4_FEATURE_INCOMPAT_LARGEDIR 0x4000 /* >2GB or 3-lvl htree */ |
1500 | #define EXT4_FEATURE_INCOMPAT_INLINEDATA 0x8000 /* data in inode */ | 1508 | #define EXT4_FEATURE_INCOMPAT_INLINE_DATA 0x8000 /* data in inode */ |
1501 | 1509 | ||
1502 | #define EXT2_FEATURE_COMPAT_SUPP EXT4_FEATURE_COMPAT_EXT_ATTR | 1510 | #define EXT2_FEATURE_COMPAT_SUPP EXT4_FEATURE_COMPAT_EXT_ATTR |
1503 | #define EXT2_FEATURE_INCOMPAT_SUPP (EXT4_FEATURE_INCOMPAT_FILETYPE| \ | 1511 | #define EXT2_FEATURE_INCOMPAT_SUPP (EXT4_FEATURE_INCOMPAT_FILETYPE| \ |
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c new file mode 100644 index 000000000000..bec68b364832 --- /dev/null +++ b/fs/ext4/inline.c | |||
@@ -0,0 +1,466 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2012 Taobao. | ||
3 | * Written by Tao Ma <boyu.mt@taobao.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of version 2.1 of the GNU Lesser General Public License | ||
7 | * as published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | */ | ||
14 | #include "ext4_jbd2.h" | ||
15 | #include "ext4.h" | ||
16 | #include "xattr.h" | ||
17 | |||
18 | #define EXT4_XATTR_SYSTEM_DATA "data" | ||
19 | #define EXT4_MIN_INLINE_DATA_SIZE ((sizeof(__le32) * EXT4_N_BLOCKS)) | ||
20 | |||
21 | int ext4_get_inline_size(struct inode *inode) | ||
22 | { | ||
23 | if (EXT4_I(inode)->i_inline_off) | ||
24 | return EXT4_I(inode)->i_inline_size; | ||
25 | |||
26 | return 0; | ||
27 | } | ||
28 | |||
29 | static int get_max_inline_xattr_value_size(struct inode *inode, | ||
30 | struct ext4_iloc *iloc) | ||
31 | { | ||
32 | struct ext4_xattr_ibody_header *header; | ||
33 | struct ext4_xattr_entry *entry; | ||
34 | struct ext4_inode *raw_inode; | ||
35 | int free, min_offs; | ||
36 | |||
37 | min_offs = EXT4_SB(inode->i_sb)->s_inode_size - | ||
38 | EXT4_GOOD_OLD_INODE_SIZE - | ||
39 | EXT4_I(inode)->i_extra_isize - | ||
40 | sizeof(struct ext4_xattr_ibody_header); | ||
41 | |||
42 | /* | ||
43 | * We need to subtract another sizeof(__u32) since an in-inode xattr | ||
44 | * needs an empty 4 bytes to indicate the gap between the xattr entry | ||
45 | * and the name/value pair. | ||
46 | */ | ||
47 | if (!ext4_test_inode_state(inode, EXT4_STATE_XATTR)) | ||
48 | return EXT4_XATTR_SIZE(min_offs - | ||
49 | EXT4_XATTR_LEN(strlen(EXT4_XATTR_SYSTEM_DATA)) - | ||
50 | EXT4_XATTR_ROUND - sizeof(__u32)); | ||
51 | |||
52 | raw_inode = ext4_raw_inode(iloc); | ||
53 | header = IHDR(inode, raw_inode); | ||
54 | entry = IFIRST(header); | ||
55 | |||
56 | /* Compute min_offs. */ | ||
57 | for (; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) { | ||
58 | if (!entry->e_value_block && entry->e_value_size) { | ||
59 | size_t offs = le16_to_cpu(entry->e_value_offs); | ||
60 | if (offs < min_offs) | ||
61 | min_offs = offs; | ||
62 | } | ||
63 | } | ||
64 | free = min_offs - | ||
65 | ((void *)entry - (void *)IFIRST(header)) - sizeof(__u32); | ||
66 | |||
67 | if (EXT4_I(inode)->i_inline_off) { | ||
68 | entry = (struct ext4_xattr_entry *) | ||
69 | ((void *)raw_inode + EXT4_I(inode)->i_inline_off); | ||
70 | |||
71 | free += le32_to_cpu(entry->e_value_size); | ||
72 | goto out; | ||
73 | } | ||
74 | |||
75 | free -= EXT4_XATTR_LEN(strlen(EXT4_XATTR_SYSTEM_DATA)); | ||
76 | |||
77 | if (free > EXT4_XATTR_ROUND) | ||
78 | free = EXT4_XATTR_SIZE(free - EXT4_XATTR_ROUND); | ||
79 | else | ||
80 | free = 0; | ||
81 | |||
82 | out: | ||
83 | return free; | ||
84 | } | ||
85 | |||
86 | /* | ||
87 | * Get the maximum size we now can store in an inode. | ||
88 | * If we can't find the space for a xattr entry, don't use the space | ||
89 | * of the extents since we have no space to indicate the inline data. | ||
90 | */ | ||
91 | int ext4_get_max_inline_size(struct inode *inode) | ||
92 | { | ||
93 | int error, max_inline_size; | ||
94 | struct ext4_iloc iloc; | ||
95 | |||
96 | if (EXT4_I(inode)->i_extra_isize == 0) | ||
97 | return 0; | ||
98 | |||
99 | error = ext4_get_inode_loc(inode, &iloc); | ||
100 | if (error) { | ||
101 | ext4_error_inode(inode, __func__, __LINE__, 0, | ||
102 | "can't get inode location %lu", | ||
103 | inode->i_ino); | ||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | down_read(&EXT4_I(inode)->xattr_sem); | ||
108 | max_inline_size = get_max_inline_xattr_value_size(inode, &iloc); | ||
109 | up_read(&EXT4_I(inode)->xattr_sem); | ||
110 | |||
111 | brelse(iloc.bh); | ||
112 | |||
113 | if (!max_inline_size) | ||
114 | return 0; | ||
115 | |||
116 | return max_inline_size + EXT4_MIN_INLINE_DATA_SIZE; | ||
117 | } | ||
118 | |||
119 | int ext4_has_inline_data(struct inode *inode) | ||
120 | { | ||
121 | return ext4_test_inode_flag(inode, EXT4_INODE_INLINE_DATA) && | ||
122 | EXT4_I(inode)->i_inline_off; | ||
123 | } | ||
124 | |||
125 | /* | ||
126 | * this function does not take xattr_sem, which is OK because it is | ||
127 | * currently only used in a code path coming form ext4_iget, before | ||
128 | * the new inode has been unlocked | ||
129 | */ | ||
130 | int ext4_find_inline_data_nolock(struct inode *inode) | ||
131 | { | ||
132 | struct ext4_xattr_ibody_find is = { | ||
133 | .s = { .not_found = -ENODATA, }, | ||
134 | }; | ||
135 | struct ext4_xattr_info i = { | ||
136 | .name_index = EXT4_XATTR_INDEX_SYSTEM, | ||
137 | .name = EXT4_XATTR_SYSTEM_DATA, | ||
138 | }; | ||
139 | int error; | ||
140 | |||
141 | if (EXT4_I(inode)->i_extra_isize == 0) | ||
142 | return 0; | ||
143 | |||
144 | error = ext4_get_inode_loc(inode, &is.iloc); | ||
145 | if (error) | ||
146 | return error; | ||
147 | |||
148 | error = ext4_xattr_ibody_find(inode, &i, &is); | ||
149 | if (error) | ||
150 | goto out; | ||
151 | |||
152 | if (!is.s.not_found) { | ||
153 | EXT4_I(inode)->i_inline_off = (u16)((void *)is.s.here - | ||
154 | (void *)ext4_raw_inode(&is.iloc)); | ||
155 | EXT4_I(inode)->i_inline_size = EXT4_MIN_INLINE_DATA_SIZE + | ||
156 | le32_to_cpu(is.s.here->e_value_size); | ||
157 | ext4_set_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA); | ||
158 | } | ||
159 | out: | ||
160 | brelse(is.iloc.bh); | ||
161 | return error; | ||
162 | } | ||
163 | |||
164 | static int ext4_read_inline_data(struct inode *inode, void *buffer, | ||
165 | unsigned int len, | ||
166 | struct ext4_iloc *iloc) | ||
167 | { | ||
168 | struct ext4_xattr_entry *entry; | ||
169 | struct ext4_xattr_ibody_header *header; | ||
170 | int cp_len = 0; | ||
171 | struct ext4_inode *raw_inode; | ||
172 | |||
173 | if (!len) | ||
174 | return 0; | ||
175 | |||
176 | BUG_ON(len > EXT4_I(inode)->i_inline_size); | ||
177 | |||
178 | cp_len = len < EXT4_MIN_INLINE_DATA_SIZE ? | ||
179 | len : EXT4_MIN_INLINE_DATA_SIZE; | ||
180 | |||
181 | raw_inode = ext4_raw_inode(iloc); | ||
182 | memcpy(buffer, (void *)(raw_inode->i_block), cp_len); | ||
183 | |||
184 | len -= cp_len; | ||
185 | buffer += cp_len; | ||
186 | |||
187 | if (!len) | ||
188 | goto out; | ||
189 | |||
190 | header = IHDR(inode, raw_inode); | ||
191 | entry = (struct ext4_xattr_entry *)((void *)raw_inode + | ||
192 | EXT4_I(inode)->i_inline_off); | ||
193 | len = min_t(unsigned int, len, | ||
194 | (unsigned int)le32_to_cpu(entry->e_value_size)); | ||
195 | |||
196 | memcpy(buffer, | ||
197 | (void *)IFIRST(header) + le16_to_cpu(entry->e_value_offs), len); | ||
198 | cp_len += len; | ||
199 | |||
200 | out: | ||
201 | return cp_len; | ||
202 | } | ||
203 | |||
204 | /* | ||
205 | * write the buffer to the inline inode. | ||
206 | * If 'create' is set, we don't need to do the extra copy in the xattr | ||
207 | * value since it is already handled by ext4_xattr_ibody_set. That saves | ||
208 | * us one memcpy. | ||
209 | */ | ||
210 | void ext4_write_inline_data(struct inode *inode, struct ext4_iloc *iloc, | ||
211 | void *buffer, loff_t pos, unsigned int len) | ||
212 | { | ||
213 | struct ext4_xattr_entry *entry; | ||
214 | struct ext4_xattr_ibody_header *header; | ||
215 | struct ext4_inode *raw_inode; | ||
216 | int cp_len = 0; | ||
217 | |||
218 | BUG_ON(!EXT4_I(inode)->i_inline_off); | ||
219 | BUG_ON(pos + len > EXT4_I(inode)->i_inline_size); | ||
220 | |||
221 | raw_inode = ext4_raw_inode(iloc); | ||
222 | buffer += pos; | ||
223 | |||
224 | if (pos < EXT4_MIN_INLINE_DATA_SIZE) { | ||
225 | cp_len = pos + len > EXT4_MIN_INLINE_DATA_SIZE ? | ||
226 | EXT4_MIN_INLINE_DATA_SIZE - pos : len; | ||
227 | memcpy((void *)raw_inode->i_block + pos, buffer, cp_len); | ||
228 | |||
229 | len -= cp_len; | ||
230 | buffer += cp_len; | ||
231 | pos += cp_len; | ||
232 | } | ||
233 | |||
234 | if (!len) | ||
235 | return; | ||
236 | |||
237 | pos -= EXT4_MIN_INLINE_DATA_SIZE; | ||
238 | header = IHDR(inode, raw_inode); | ||
239 | entry = (struct ext4_xattr_entry *)((void *)raw_inode + | ||
240 | EXT4_I(inode)->i_inline_off); | ||
241 | |||
242 | memcpy((void *)IFIRST(header) + le16_to_cpu(entry->e_value_offs) + pos, | ||
243 | buffer, len); | ||
244 | } | ||
245 | |||
246 | static int ext4_create_inline_data(handle_t *handle, | ||
247 | struct inode *inode, unsigned len) | ||
248 | { | ||
249 | int error; | ||
250 | void *value = NULL; | ||
251 | struct ext4_xattr_ibody_find is = { | ||
252 | .s = { .not_found = -ENODATA, }, | ||
253 | }; | ||
254 | struct ext4_xattr_info i = { | ||
255 | .name_index = EXT4_XATTR_INDEX_SYSTEM, | ||
256 | .name = EXT4_XATTR_SYSTEM_DATA, | ||
257 | }; | ||
258 | |||
259 | error = ext4_get_inode_loc(inode, &is.iloc); | ||
260 | if (error) | ||
261 | return error; | ||
262 | |||
263 | error = ext4_journal_get_write_access(handle, is.iloc.bh); | ||
264 | if (error) | ||
265 | goto out; | ||
266 | |||
267 | if (len > EXT4_MIN_INLINE_DATA_SIZE) { | ||
268 | value = (void *)empty_zero_page; | ||
269 | len -= EXT4_MIN_INLINE_DATA_SIZE; | ||
270 | } else { | ||
271 | value = ""; | ||
272 | len = 0; | ||
273 | } | ||
274 | |||
275 | /* Insert the the xttr entry. */ | ||
276 | i.value = value; | ||
277 | i.value_len = len; | ||
278 | |||
279 | error = ext4_xattr_ibody_find(inode, &i, &is); | ||
280 | if (error) | ||
281 | goto out; | ||
282 | |||
283 | BUG_ON(!is.s.not_found); | ||
284 | |||
285 | error = ext4_xattr_ibody_set(handle, inode, &i, &is); | ||
286 | if (error) { | ||
287 | if (error == -ENOSPC) | ||
288 | ext4_clear_inode_state(inode, | ||
289 | EXT4_STATE_MAY_INLINE_DATA); | ||
290 | goto out; | ||
291 | } | ||
292 | |||
293 | memset((void *)ext4_raw_inode(&is.iloc)->i_block, | ||
294 | 0, EXT4_MIN_INLINE_DATA_SIZE); | ||
295 | |||
296 | EXT4_I(inode)->i_inline_off = (u16)((void *)is.s.here - | ||
297 | (void *)ext4_raw_inode(&is.iloc)); | ||
298 | EXT4_I(inode)->i_inline_size = len + EXT4_MIN_INLINE_DATA_SIZE; | ||
299 | ext4_clear_inode_flag(inode, EXT4_INODE_EXTENTS); | ||
300 | ext4_set_inode_flag(inode, EXT4_INODE_INLINE_DATA); | ||
301 | get_bh(is.iloc.bh); | ||
302 | error = ext4_mark_iloc_dirty(handle, inode, &is.iloc); | ||
303 | |||
304 | out: | ||
305 | brelse(is.iloc.bh); | ||
306 | return error; | ||
307 | } | ||
308 | |||
309 | static int ext4_update_inline_data(handle_t *handle, struct inode *inode, | ||
310 | unsigned int len) | ||
311 | { | ||
312 | int error; | ||
313 | void *value = NULL; | ||
314 | struct ext4_xattr_ibody_find is = { | ||
315 | .s = { .not_found = -ENODATA, }, | ||
316 | }; | ||
317 | struct ext4_xattr_info i = { | ||
318 | .name_index = EXT4_XATTR_INDEX_SYSTEM, | ||
319 | .name = EXT4_XATTR_SYSTEM_DATA, | ||
320 | }; | ||
321 | |||
322 | /* If the old space is ok, write the data directly. */ | ||
323 | if (len <= EXT4_I(inode)->i_inline_size) | ||
324 | return 0; | ||
325 | |||
326 | error = ext4_get_inode_loc(inode, &is.iloc); | ||
327 | if (error) | ||
328 | return error; | ||
329 | |||
330 | error = ext4_xattr_ibody_find(inode, &i, &is); | ||
331 | if (error) | ||
332 | goto out; | ||
333 | |||
334 | BUG_ON(is.s.not_found); | ||
335 | |||
336 | len -= EXT4_MIN_INLINE_DATA_SIZE; | ||
337 | value = kzalloc(len, GFP_NOFS); | ||
338 | if (!value) | ||
339 | goto out; | ||
340 | |||
341 | error = ext4_xattr_ibody_get(inode, i.name_index, i.name, | ||
342 | value, len); | ||
343 | if (error == -ENODATA) | ||
344 | goto out; | ||
345 | |||
346 | error = ext4_journal_get_write_access(handle, is.iloc.bh); | ||
347 | if (error) | ||
348 | goto out; | ||
349 | |||
350 | /* Update the xttr entry. */ | ||
351 | i.value = value; | ||
352 | i.value_len = len; | ||
353 | |||
354 | error = ext4_xattr_ibody_set(handle, inode, &i, &is); | ||
355 | if (error) | ||
356 | goto out; | ||
357 | |||
358 | EXT4_I(inode)->i_inline_off = (u16)((void *)is.s.here - | ||
359 | (void *)ext4_raw_inode(&is.iloc)); | ||
360 | EXT4_I(inode)->i_inline_size = EXT4_MIN_INLINE_DATA_SIZE + | ||
361 | le32_to_cpu(is.s.here->e_value_size); | ||
362 | ext4_set_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA); | ||
363 | get_bh(is.iloc.bh); | ||
364 | error = ext4_mark_iloc_dirty(handle, inode, &is.iloc); | ||
365 | |||
366 | out: | ||
367 | kfree(value); | ||
368 | brelse(is.iloc.bh); | ||
369 | return error; | ||
370 | } | ||
371 | |||
372 | int ext4_prepare_inline_data(handle_t *handle, struct inode *inode, | ||
373 | unsigned int len) | ||
374 | { | ||
375 | int ret, size; | ||
376 | struct ext4_inode_info *ei = EXT4_I(inode); | ||
377 | |||
378 | if (!ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA)) | ||
379 | return -ENOSPC; | ||
380 | |||
381 | size = ext4_get_max_inline_size(inode); | ||
382 | if (size < len) | ||
383 | return -ENOSPC; | ||
384 | |||
385 | down_write(&EXT4_I(inode)->xattr_sem); | ||
386 | |||
387 | if (ei->i_inline_off) | ||
388 | ret = ext4_update_inline_data(handle, inode, len); | ||
389 | else | ||
390 | ret = ext4_create_inline_data(handle, inode, len); | ||
391 | |||
392 | up_write(&EXT4_I(inode)->xattr_sem); | ||
393 | |||
394 | return ret; | ||
395 | } | ||
396 | |||
397 | static int ext4_destroy_inline_data_nolock(handle_t *handle, | ||
398 | struct inode *inode) | ||
399 | { | ||
400 | struct ext4_inode_info *ei = EXT4_I(inode); | ||
401 | struct ext4_xattr_ibody_find is = { | ||
402 | .s = { .not_found = 0, }, | ||
403 | }; | ||
404 | struct ext4_xattr_info i = { | ||
405 | .name_index = EXT4_XATTR_INDEX_SYSTEM, | ||
406 | .name = EXT4_XATTR_SYSTEM_DATA, | ||
407 | .value = NULL, | ||
408 | .value_len = 0, | ||
409 | }; | ||
410 | int error; | ||
411 | |||
412 | if (!ei->i_inline_off) | ||
413 | return 0; | ||
414 | |||
415 | error = ext4_get_inode_loc(inode, &is.iloc); | ||
416 | if (error) | ||
417 | return error; | ||
418 | |||
419 | error = ext4_xattr_ibody_find(inode, &i, &is); | ||
420 | if (error) | ||
421 | goto out; | ||
422 | |||
423 | error = ext4_journal_get_write_access(handle, is.iloc.bh); | ||
424 | if (error) | ||
425 | goto out; | ||
426 | |||
427 | error = ext4_xattr_ibody_set(handle, inode, &i, &is); | ||
428 | if (error) | ||
429 | goto out; | ||
430 | |||
431 | memset((void *)ext4_raw_inode(&is.iloc)->i_block, | ||
432 | 0, EXT4_MIN_INLINE_DATA_SIZE); | ||
433 | |||
434 | if (EXT4_HAS_INCOMPAT_FEATURE(inode->i_sb, | ||
435 | EXT4_FEATURE_INCOMPAT_EXTENTS)) { | ||
436 | if (S_ISDIR(inode->i_mode) || | ||
437 | S_ISREG(inode->i_mode) || S_ISLNK(inode->i_mode)) { | ||
438 | ext4_set_inode_flag(inode, EXT4_INODE_EXTENTS); | ||
439 | ext4_ext_tree_init(handle, inode); | ||
440 | } | ||
441 | } | ||
442 | ext4_clear_inode_flag(inode, EXT4_INODE_INLINE_DATA); | ||
443 | |||
444 | get_bh(is.iloc.bh); | ||
445 | error = ext4_mark_iloc_dirty(handle, inode, &is.iloc); | ||
446 | |||
447 | EXT4_I(inode)->i_inline_off = 0; | ||
448 | EXT4_I(inode)->i_inline_size = 0; | ||
449 | ext4_clear_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA); | ||
450 | out: | ||
451 | brelse(is.iloc.bh); | ||
452 | if (error == -ENODATA) | ||
453 | error = 0; | ||
454 | return error; | ||
455 | } | ||
456 | |||
457 | int ext4_destroy_inline_data(handle_t *handle, struct inode *inode) | ||
458 | { | ||
459 | int ret; | ||
460 | |||
461 | down_write(&EXT4_I(inode)->xattr_sem); | ||
462 | ret = ext4_destroy_inline_data_nolock(handle, inode); | ||
463 | up_write(&EXT4_I(inode)->xattr_sem); | ||
464 | |||
465 | return ret; | ||
466 | } | ||
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index befa005711a1..e23f114e2cfe 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
@@ -3706,8 +3706,10 @@ static inline void ext4_iget_extra_inode(struct inode *inode, | |||
3706 | { | 3706 | { |
3707 | __le32 *magic = (void *)raw_inode + | 3707 | __le32 *magic = (void *)raw_inode + |
3708 | EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize; | 3708 | EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize; |
3709 | if (*magic == cpu_to_le32(EXT4_XATTR_MAGIC)) | 3709 | if (*magic == cpu_to_le32(EXT4_XATTR_MAGIC)) { |
3710 | ext4_set_inode_state(inode, EXT4_STATE_XATTR); | 3710 | ext4_set_inode_state(inode, EXT4_STATE_XATTR); |
3711 | ext4_find_inline_data_nolock(inode); | ||
3712 | } | ||
3711 | } | 3713 | } |
3712 | 3714 | ||
3713 | struct inode *ext4_iget(struct super_block *sb, unsigned long ino) | 3715 | struct inode *ext4_iget(struct super_block *sb, unsigned long ino) |
@@ -3780,6 +3782,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino) | |||
3780 | set_nlink(inode, le16_to_cpu(raw_inode->i_links_count)); | 3782 | set_nlink(inode, le16_to_cpu(raw_inode->i_links_count)); |
3781 | 3783 | ||
3782 | ext4_clear_state_flags(ei); /* Only relevant on 32-bit archs */ | 3784 | ext4_clear_state_flags(ei); /* Only relevant on 32-bit archs */ |
3785 | ei->i_inline_off = 0; | ||
3783 | ei->i_dir_start_lookup = 0; | 3786 | ei->i_dir_start_lookup = 0; |
3784 | ei->i_dtime = le32_to_cpu(raw_inode->i_dtime); | 3787 | ei->i_dtime = le32_to_cpu(raw_inode->i_dtime); |
3785 | /* We now have enough fields to check if the inode was active or not. | 3788 | /* We now have enough fields to check if the inode was active or not. |
diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h index 40ca7a6f5eec..7ae0d05156e3 100644 --- a/fs/ext4/xattr.h +++ b/fs/ext4/xattr.h | |||
@@ -21,6 +21,7 @@ | |||
21 | #define EXT4_XATTR_INDEX_TRUSTED 4 | 21 | #define EXT4_XATTR_INDEX_TRUSTED 4 |
22 | #define EXT4_XATTR_INDEX_LUSTRE 5 | 22 | #define EXT4_XATTR_INDEX_LUSTRE 5 |
23 | #define EXT4_XATTR_INDEX_SECURITY 6 | 23 | #define EXT4_XATTR_INDEX_SECURITY 6 |
24 | #define EXT4_XATTR_INDEX_SYSTEM 7 | ||
24 | 25 | ||
25 | struct ext4_xattr_header { | 26 | struct ext4_xattr_header { |
26 | __le32 h_magic; /* magic number for identification */ | 27 | __le32 h_magic; /* magic number for identification */ |
@@ -125,6 +126,19 @@ extern int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode, | |||
125 | struct ext4_xattr_info *i, | 126 | struct ext4_xattr_info *i, |
126 | struct ext4_xattr_ibody_find *is); | 127 | struct ext4_xattr_ibody_find *is); |
127 | 128 | ||
129 | extern int ext4_has_inline_data(struct inode *inode); | ||
130 | extern int ext4_get_inline_size(struct inode *inode); | ||
131 | extern int ext4_get_max_inline_size(struct inode *inode); | ||
132 | extern int ext4_find_inline_data_nolock(struct inode *inode); | ||
133 | extern void ext4_write_inline_data(struct inode *inode, | ||
134 | struct ext4_iloc *iloc, | ||
135 | void *buffer, loff_t pos, | ||
136 | unsigned int len); | ||
137 | extern int ext4_prepare_inline_data(handle_t *handle, struct inode *inode, | ||
138 | unsigned int len); | ||
139 | extern int ext4_init_inline_data(handle_t *handle, struct inode *inode, | ||
140 | unsigned int len); | ||
141 | extern int ext4_destroy_inline_data(handle_t *handle, struct inode *inode); | ||
128 | # else /* CONFIG_EXT4_FS_XATTR */ | 142 | # else /* CONFIG_EXT4_FS_XATTR */ |
129 | 143 | ||
130 | static inline int | 144 | static inline int |
@@ -201,6 +215,46 @@ ext4_xattr_ibody_get(struct inode *inode, int name_index, | |||
201 | return -EOPNOTSUPP; | 215 | return -EOPNOTSUPP; |
202 | } | 216 | } |
203 | 217 | ||
218 | static inline int ext4_find_inline_data_nolock(struct inode *inode) | ||
219 | { | ||
220 | return 0; | ||
221 | } | ||
222 | |||
223 | static inline int ext4_has_inline_data(struct inode *inode) | ||
224 | { | ||
225 | return 0; | ||
226 | } | ||
227 | |||
228 | static inline int ext4_get_inline_size(struct inode *inode) | ||
229 | { | ||
230 | return 0; | ||
231 | } | ||
232 | |||
233 | static inline int ext4_get_max_inline_size(struct inode *inode) | ||
234 | { | ||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | static inline void ext4_write_inline_data(struct inode *inode, | ||
239 | struct ext4_iloc *iloc, | ||
240 | void *buffer, loff_t pos, | ||
241 | unsigned int len) | ||
242 | { | ||
243 | return; | ||
244 | } | ||
245 | |||
246 | static inline int ext4_init_inline_data(handle_t *handle, | ||
247 | struct inode *inode, | ||
248 | unsigned int len) | ||
249 | { | ||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | static inline int ext4_destroy_inline_data(handle_t *handle, | ||
254 | struct inode *inode) | ||
255 | { | ||
256 | return 0; | ||
257 | } | ||
204 | # endif /* CONFIG_EXT4_FS_XATTR */ | 258 | # endif /* CONFIG_EXT4_FS_XATTR */ |
205 | 259 | ||
206 | #ifdef CONFIG_EXT4_FS_SECURITY | 260 | #ifdef CONFIG_EXT4_FS_SECURITY |